Class: RosettAi::Desktop::DbusClient
- Inherits:
-
Object
- Object
- RosettAi::Desktop::DbusClient
- Defined in:
- lib/rosett_ai/desktop/dbus_client.rb
Overview
D-Bus client for communicating with be.neatnerds.rosettai service.
SAFETY: This client is designed to NEVER raise exceptions to callers. All methods return safe default values on failure. This is critical for GUI stability - a D-Bus failure should never crash the app.
All GTK4/Qt6 desktop apps use this client instead of calling Ruby Rosett-AI modules directly. This ensures the D-Bus service is the single source of truth.
Constant Summary collapse
- BUS_NAME =
'be.neatnerds.rosettai'- OBJECT_PATH =
'/be/neatnerds/rosett-ai'- MANAGER_IFACE =
'be.neatnerds.rosettai.Manager'- FOCUS_IFACE =
'be.neatnerds.rosettai.FocusMonitor'- SAFE_STATUS =
Default safe return values
{ 'error' => 'Service not available', 'version' => 'unknown' }.freeze
- SAFE_FOCUS =
['', ''].freeze
- SAFE_COMPILE_RESULT =
'Error: D-Bus service not available'
Instance Attribute Summary collapse
-
#bus ⇒ Object
readonly
Returns the value of attribute bus.
-
#last_error ⇒ Object
readonly
Returns the value of attribute last_error.
Instance Method Summary collapse
-
#compile ⇒ String
Run compilation via D-Bus.
-
#connect ⇒ Boolean
Connect to the D-Bus session bus and get service proxy.
-
#connected? ⇒ Boolean
Check if connected to D-Bus service.
-
#current_focus ⇒ Array<String>
Get current focus from FocusMonitor interface.
-
#disconnect ⇒ Object
Disconnect from D-Bus.
-
#initialize ⇒ DbusClient
constructor
A new instance of DbusClient.
-
#list_engines ⇒ Array<Hash>
List available engines from D-Bus service.
-
#on_context_changed {|context_name| ... } ⇒ Boolean
Subscribe to ContextChanged signal.
-
#on_focus_changed {|app_id, title| ... } ⇒ Boolean
Subscribe to FocusChanged signal.
-
#service_available? ⇒ Boolean
Check if the D-Bus service is available.
-
#set_config(key, value) ⇒ Boolean
Update a configuration key via D-Bus.
-
#status ⇒ Hash
Get service status from Manager interface.
-
#switch_context(context_name) ⇒ Boolean
Switch AI context.
Constructor Details
#initialize ⇒ DbusClient
Returns a new instance of DbusClient.
33 34 35 36 37 38 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 33 def initialize @bus = nil @object = nil @connected = false @last_error = nil end |
Instance Attribute Details
#bus ⇒ Object (readonly)
Returns the value of attribute bus.
31 32 33 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 31 def bus @bus end |
#last_error ⇒ Object (readonly)
Returns the value of attribute last_error.
31 32 33 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 31 def last_error @last_error end |
Instance Method Details
#compile ⇒ String
Run compilation via D-Bus. SAFETY: Never raises, returns error string on failure.
106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 106 def compile GuiLogger.dbus_call(:compile, interface: MANAGER_IFACE) return SAFE_COMPILE_RESULT unless safe_connect @object.default_iface = MANAGER_IFACE result = @object.Compile output = result.is_a?(Array) ? result.first : result.to_s GuiLogger.dbus_call(:compile, status: :success, result_length: output.length) output rescue StandardError => e log_error('Compile failed', e) "Error: #{e.}" end |
#connect ⇒ Boolean
Connect to the D-Bus session bus and get service proxy.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 43 def connect return true if @connected GuiLogger.dbus_call(:connect, bus_name: BUS_NAME) @last_error = nil @bus = ::DBus::SessionBus.instance service = @bus.service(BUS_NAME) @object = service.object(OBJECT_PATH) @object.introspect @connected = true GuiLogger.dbus_call(:connect, status: :success) true rescue ::DBus::Error => e @last_error = e. log_error('D-Bus connection failed', e) @connected = false false rescue StandardError => e @last_error = e. log_error('Unexpected error during D-Bus connection', e) @connected = false false end |
#connected? ⇒ Boolean
Check if connected to D-Bus service.
70 71 72 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 70 def connected? @connected end |
#current_focus ⇒ Array<String>
Get current focus from FocusMonitor interface. SAFETY: Never raises, returns empty array on failure.
140 141 142 143 144 145 146 147 148 149 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 140 def current_focus return SAFE_FOCUS.dup unless safe_connect @object.default_iface = FOCUS_IFACE result = @object.GetCurrentFocus result.is_a?(Array) && result.size >= 2 ? result : SAFE_FOCUS.dup rescue StandardError => e log_error('GetCurrentFocus failed', e) SAFE_FOCUS.dup end |
#disconnect ⇒ Object
Disconnect from D-Bus. SAFETY: Never raises.
221 222 223 224 225 226 227 228 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 221 def disconnect @connected = false @object = nil @bus = nil @last_error = nil rescue StandardError => e log_error('Disconnect failed', e) end |
#list_engines ⇒ Array<Hash>
List available engines from D-Bus service. SAFETY: Never raises, returns empty array on failure.
189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 189 def list_engines return [] unless safe_connect @object.default_iface = MANAGER_IFACE result = @object.ListEngines tuples = result.is_a?(Array) ? result.first : [] (tuples || []).map { |name, display| { name: name, display_name: display } } rescue StandardError => e log_error('ListEngines failed', e) [] end |
#on_context_changed {|context_name| ... } ⇒ Boolean
Subscribe to ContextChanged signal. SAFETY: Never raises, silently fails if service unavailable.
173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 173 def on_context_changed(&block) return false unless safe_connect return false unless block @object.default_iface = MANAGER_IFACE @object.on_signal('ContextChanged', &block) true rescue StandardError => e log_error('ContextChanged subscription failed', e) false end |
#on_focus_changed {|app_id, title| ... } ⇒ Boolean
Subscribe to FocusChanged signal. SAFETY: Never raises, silently fails if service unavailable.
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 156 def on_focus_changed(&block) return false unless safe_connect return false unless block @object.default_iface = FOCUS_IFACE @object.on_signal('FocusChanged', &block) true rescue StandardError => e log_error('FocusChanged subscription failed', e) false end |
#service_available? ⇒ Boolean
Check if the D-Bus service is available. SAFETY: Never raises, returns false on any error.
78 79 80 81 82 83 84 85 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 78 def service_available? return false unless connect @bus.service(BUS_NAME).exists? rescue StandardError => e log_error('Service availability check failed', e) false end |
#set_config(key, value) ⇒ Boolean
Update a configuration key via D-Bus. SAFETY: Never raises, returns false on failure.
207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 207 def set_config(key, value) return false unless safe_connect @object.default_iface = MANAGER_IFACE result = @object.SetConfig(key, value.to_s) output = result.is_a?(Array) ? result.first : result.to_s !output.start_with?('Error:') rescue StandardError => e log_error('SetConfig failed', e) false end |
#status ⇒ Hash
Get service status from Manager interface. SAFETY: Never raises, returns safe default hash on error.
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 91 def status return SAFE_STATUS.dup unless safe_connect @object.default_iface = MANAGER_IFACE result = @object.GetStatus result.is_a?(Array) ? result.first : SAFE_STATUS.dup rescue StandardError => e log_error('GetStatus failed', e) SAFE_STATUS.merge('error' => e.) end |
#switch_context(context_name) ⇒ Boolean
Switch AI context. SAFETY: Never raises, returns false on failure.
125 126 127 128 129 130 131 132 133 134 |
# File 'lib/rosett_ai/desktop/dbus_client.rb', line 125 def switch_context(context_name) return false unless safe_connect @object.default_iface = MANAGER_IFACE @object.SwitchContext(context_name) true rescue StandardError => e log_error('SwitchContext failed', e) false end |