ha-opencode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Stephen Golub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,315 @@
1
+ # ha-opencode
2
+
3
+ OpenCode plugin for Home Assistant integration via MQTT Discovery.
4
+
5
+ Exposes OpenCode session state as Home Assistant entities and allows bidirectional control (responding to permission requests, sending prompts, viewing history).
6
+
7
+ ## Features
8
+
9
+ - **Session-Based Identity**: Each OpenCode session gets its own HA device and entities
10
+ - **Real-time State Tracking**: Session state, model, current tool, tokens, and cost
11
+ - **Permission Handling**: Approve/reject permission requests from HA or mobile notifications
12
+ - **Send Prompts**: Send prompts to OpenCode sessions via MQTT
13
+ - **Session History**: Retrieve conversation history on demand
14
+ - **Agent Tracking**: Track primary agent and sub-agents being used
15
+ - **Hostname Display**: Identify which machine the session is running on
16
+ - **Automatic Cleanup**: Stale sessions (7+ days inactive) are automatically removed
17
+
18
+ ## Installation
19
+
20
+ ### 1. Install the plugin
21
+
22
+ ```bash
23
+ npm install --prefix ~/.config/opencode /path/to/ha-opencode
24
+ ```
25
+
26
+ ### 2. Add to OpenCode config
27
+
28
+ Add `"ha-opencode"` to the `plugins` array in `~/.config/opencode/opencode.json`:
29
+
30
+ ```json
31
+ {
32
+ "plugins": ["ha-opencode"]
33
+ }
34
+ ```
35
+
36
+ ### 3. Configure MQTT connection
37
+
38
+ Via environment variables:
39
+
40
+ ```bash
41
+ export MQTT_HOST=your-mqtt-broker.local
42
+ export MQTT_PORT=1883
43
+ export MQTT_USERNAME=optional-username
44
+ export MQTT_PASSWORD=optional-password
45
+ export HA_DISCOVERY_PREFIX=homeassistant # default
46
+ ```
47
+
48
+ Or via `opencode.json`:
49
+
50
+ ```json
51
+ {
52
+ "plugins": ["ha-opencode"],
53
+ "ha-opencode": {
54
+ "mqtt": {
55
+ "host": "your-mqtt-broker.local",
56
+ "port": 1883,
57
+ "username": "optional-username",
58
+ "password": "optional-password"
59
+ },
60
+ "ha": {
61
+ "discoveryPrefix": "homeassistant"
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ Environment variables take precedence over JSON config.
68
+
69
+ ## Session-Based Identity
70
+
71
+ Each OpenCode session creates a unique Home Assistant device:
72
+
73
+ - **Session ID**: `ses_46b09b89bffevq6HeMNIkuvk4B` (generated by OpenCode)
74
+ - **Device ID**: `opencode_46b09b89bffevq6HeMNIkuvk4B` (strips `ses_` prefix)
75
+ - **Initial Name**: `OpenCode - {projectName} - Untitled`
76
+ - **After Title**: `OpenCode - {projectName} - {sessionTitle}`
77
+
78
+ This allows running multiple concurrent OpenCode sessions in the same directory, each with their own entities.
79
+
80
+ ## Entities Created
81
+
82
+ Per OpenCode session (device), the following entities are created:
83
+
84
+ | Entity | Type | Description |
85
+ |--------|------|-------------|
86
+ | `sensor.opencode_*_state` | Sensor | Session state (idle, working, waiting_permission, error) |
87
+ | `sensor.opencode_*_session_title` | Sensor | Current session/conversation title |
88
+ | `sensor.opencode_*_model` | Sensor | AI model being used (provider/model) |
89
+ | `sensor.opencode_*_current_tool` | Sensor | Currently executing tool |
90
+ | `sensor.opencode_*_tokens_input` | Sensor | Input token count |
91
+ | `sensor.opencode_*_tokens_output` | Sensor | Output token count |
92
+ | `sensor.opencode_*_cost` | Sensor | Session cost in USD |
93
+ | `sensor.opencode_*_last_activity` | Sensor | Timestamp of last activity |
94
+ | `sensor.opencode_*_permission` | Sensor | Permission request status |
95
+ | `sensor.opencode_*_device_id` | Sensor | Device identifier with command/response topics |
96
+
97
+ **Note**: The `*` in entity IDs represents the session ID (e.g., `sensor.opencode_46b09b89bffevq6HeMNIkuvk4B_state`).
98
+
99
+ ### State Entity Attributes
100
+
101
+ The `state` entity includes these attributes:
102
+
103
+ - `previous_state`: The state before the current one (for automation conditions)
104
+ - `agent`: Primary agent selected by the user
105
+ - `current_agent`: Sub-agent currently being used (if any)
106
+ - `hostname`: Machine hostname where OpenCode is running
107
+ - `error_message`: Error details when in error state
108
+
109
+ ### Device ID Entity Attributes
110
+
111
+ The `device_id` entity includes:
112
+
113
+ - `command_topic`: MQTT topic for sending commands
114
+ - `response_topic`: MQTT topic for receiving responses
115
+ - `state_topic_base`: Base topic for all state updates
116
+ - `device_name`: Friendly device name
117
+ - `session_id`: Full session ID (e.g., `ses_46b09b89bffevq6HeMNIkuvk4B`)
118
+ - `project_name`: Project directory name
119
+
120
+ ## MQTT Topics
121
+
122
+ For a session with ID `ses_46b09b89bffevq6HeMNIkuvk4B`:
123
+
124
+ ```
125
+ # State topics
126
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/state
127
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/state/attributes
128
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/session_title
129
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/model
130
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/availability
131
+
132
+ # Commands & responses
133
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/command
134
+ opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/response
135
+
136
+ # HA Discovery
137
+ homeassistant/sensor/opencode_46b09b89bffevq6HeMNIkuvk4B/state/config
138
+
139
+ # Global cleanup response
140
+ opencode/cleanup/response
141
+ ```
142
+
143
+ ## MQTT Commands
144
+
145
+ Send commands to the `command_topic` (e.g., `opencode/opencode_46b09b89bffevq6HeMNIkuvk4B/command`):
146
+
147
+ ### Permission Response
148
+
149
+ ```json
150
+ {
151
+ "command": "permission_response",
152
+ "permission_id": "perm-123",
153
+ "response": "once"
154
+ }
155
+ ```
156
+
157
+ Response options: `"once"`, `"always"`, or `"reject"`
158
+
159
+ ### Send Prompt
160
+
161
+ ```json
162
+ {
163
+ "command": "prompt",
164
+ "text": "Your prompt text here",
165
+ "session_id": "optional-session-id"
166
+ }
167
+ ```
168
+
169
+ ### Get History
170
+
171
+ ```json
172
+ {
173
+ "command": "get_history",
174
+ "session_id": "optional-session-id",
175
+ "request_id": "optional-correlation-id"
176
+ }
177
+ ```
178
+
179
+ ### Get History Since
180
+
181
+ ```json
182
+ {
183
+ "command": "get_history_since",
184
+ "since": "2024-01-01T00:00:00Z",
185
+ "session_id": "optional-session-id",
186
+ "request_id": "optional-correlation-id"
187
+ }
188
+ ```
189
+
190
+ ### Cleanup Stale Sessions
191
+
192
+ ```json
193
+ {
194
+ "command": "cleanup_stale_sessions",
195
+ "max_age_days": 7
196
+ }
197
+ ```
198
+
199
+ Response published to `opencode/cleanup/response`:
200
+
201
+ ```json
202
+ {
203
+ "type": "cleanup_result",
204
+ "sessions_removed": 3,
205
+ "session_ids": ["opencode_abc123...", "opencode_def456..."],
206
+ "max_age_days": 7,
207
+ "timestamp": "2025-01-07T12:00:00Z"
208
+ }
209
+ ```
210
+
211
+ ## Stale Session Cleanup
212
+
213
+ Sessions that haven't been active for 7 days are automatically cleaned up:
214
+
215
+ - **Automatic**: Runs on every plugin startup (async, non-blocking)
216
+ - **Manual**: Send `cleanup_stale_sessions` command via MQTT
217
+
218
+ Cleanup removes all HA entities for stale sessions by publishing empty configs to the discovery topics.
219
+
220
+ ## Home Assistant Blueprints
221
+
222
+ Pre-built automation blueprints are available in the `blueprints/` directory:
223
+
224
+ | Blueprint | Description |
225
+ |-----------|-------------|
226
+ | `opencode_state_notifications.yaml` | Mobile notifications for task complete, permission required, and errors |
227
+ | `opencode_permission_response.yaml` | Handle approve/reject button taps from notifications |
228
+
229
+ ### Installing Blueprints
230
+
231
+ 1. Copy blueprints to your HA config:
232
+ ```bash
233
+ mkdir -p /path/to/ha/config/blueprints/automation/opencode
234
+ cp blueprints/*.yaml /path/to/ha/config/blueprints/automation/opencode/
235
+ ```
236
+
237
+ 2. Reload automations in HA (Developer Tools > YAML > Reload Automations)
238
+
239
+ 3. Create automations from blueprints:
240
+ - Settings > Automations > Create Automation > Use Blueprint
241
+ - Select the OpenCode blueprint
242
+ - Configure your mobile device and preferences
243
+
244
+ See [ha-card/README.md](ha-card/README.md) for detailed blueprint configuration and automation examples.
245
+
246
+ ## Home Assistant Card
247
+
248
+ A custom Lovelace card is included for displaying OpenCode sessions in Home Assistant.
249
+
250
+ See [ha-card/README.md](ha-card/README.md) for installation and usage instructions.
251
+
252
+ > **Note**: The card will be moved to a separate repository ([opencode-card](https://gitlab.com/opencode-home-assistant/opencode-card)) for HACS compatibility. The `ha-card/` directory is structured for easy extraction when that happens.
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ # Install dependencies
258
+ npm install
259
+
260
+ # Build TypeScript
261
+ npm run build
262
+
263
+ # Watch mode
264
+ npm run dev
265
+
266
+ # Run tests
267
+ npm test
268
+
269
+ # Run tests with coverage
270
+ npm run test:coverage
271
+ ```
272
+
273
+ ### Project Structure
274
+
275
+ ```
276
+ src/
277
+ index.ts Plugin entry point, defers setup until first session event
278
+ config.ts Configuration loading (env vars + JSON)
279
+ mqtt.ts MQTT client wrapper with wildcard support
280
+ discovery.ts Session-based HA MQTT Discovery
281
+ state.ts State tracking and publishing
282
+ commands.ts MQTT command handler
283
+ cleanup.ts Stale session cleanup
284
+ notify.ts Terminal notification utilities (Kitty OSC 99)
285
+
286
+ blueprints/
287
+ opencode_state_notifications.yaml Mobile notification blueprint
288
+ opencode_permission_response.yaml Permission action handler
289
+
290
+ ha-card/
291
+ src/ Custom Lovelace card source
292
+ dist/ Built card JS
293
+ ```
294
+
295
+ ## Troubleshooting
296
+
297
+ ### Too many entities in HA
298
+
299
+ Each session creates a new set of entities. Use the cleanup command or rely on automatic cleanup to remove old sessions. Sessions inactive for 7+ days are automatically removed on plugin startup.
300
+
301
+ ### Entity naming shows "Untitled"
302
+
303
+ This is the initial name before session title is available. Once the session gets a proper title, the device name updates automatically.
304
+
305
+ ### Automation not triggering
306
+
307
+ Check that `previous_state` attribute is available when state changes. Attributes are published BEFORE the state value to ensure they're available when automations trigger.
308
+
309
+ ### Kitty notifications not working
310
+
311
+ Ensure your terminal supports OSC 99 (Kitty, iTerm2) and that stdout is a TTY.
312
+
313
+ ## License
314
+
315
+ MIT
@@ -0,0 +1,81 @@
1
+ blueprint:
2
+ name: OpenCode Permission Response Handler
3
+ description: >
4
+ Handles approve/reject actions from OpenCode permission notifications.
5
+
6
+ This blueprint listens for notification actions (button taps) and sends
7
+ the appropriate permission response via MQTT.
8
+
9
+ Works with both iOS and Android via the Home Assistant Companion app.
10
+
11
+ Use this together with the OpenCode State Notifications blueprint.
12
+ domain: automation
13
+ author: ha-opencode
14
+ source_url: https://github.com/your-repo/ha-opencode/blob/main/blueprints/opencode_permission_response.yaml
15
+ input: {}
16
+
17
+ mode: parallel
18
+ max_exceeded: silent
19
+
20
+ trigger:
21
+ - platform: event
22
+ event_type: mobile_app_notification_action
23
+
24
+ variables:
25
+ # Get the action string
26
+ action: "{{ trigger.event.data.action | default('') }}"
27
+
28
+ # Check if this is an OpenCode action
29
+ is_opencode_action: >
30
+ {{ action.startswith('OPENCODE_APPROVE_') or action.startswith('OPENCODE_REJECT_') }}
31
+
32
+ is_approve: "{{ action.startswith('OPENCODE_APPROVE_') }}"
33
+ response: "{{ 'once' if is_approve else 'reject' }}"
34
+
35
+ # Get data passed from the notification
36
+ # iOS uses action_data, Android might use data directly
37
+ action_data: >
38
+ {% if trigger.event.data.action_data is defined %}
39
+ {{ trigger.event.data.action_data }}
40
+ {% elif trigger.event.data.data is defined %}
41
+ {{ trigger.event.data.data }}
42
+ {% else %}
43
+ {}
44
+ {% endif %}
45
+
46
+ command_topic: "{{ action_data.command_topic | default('') }}"
47
+ permission_id: "{{ action_data.permission_id | default('') }}"
48
+ device_id: "{{ action_data.device_id | default('') }}"
49
+
50
+ condition:
51
+ - condition: template
52
+ value_template: "{{ is_opencode_action }}"
53
+
54
+ action:
55
+ # Log for debugging
56
+ - service: system_log.write
57
+ data:
58
+ message: >
59
+ OpenCode permission response: action={{ action }}, is_approve={{ is_approve }},
60
+ response={{ response }}, command_topic={{ command_topic }},
61
+ permission_id={{ permission_id }}, device_id={{ device_id }},
62
+ action_data={{ action_data }}
63
+ level: info
64
+ logger: ha-opencode.permission
65
+
66
+ # Check we have required data before publishing
67
+ - condition: template
68
+ value_template: "{{ command_topic != '' and permission_id != '' }}"
69
+
70
+ - service: mqtt.publish
71
+ data:
72
+ topic: "{{ command_topic }}"
73
+ payload: >
74
+ {"command": "permission_response", "permission_id": "{{ permission_id }}", "response": "{{ response }}"}
75
+
76
+ # Log success
77
+ - service: system_log.write
78
+ data:
79
+ message: "OpenCode permission {{ response }} sent for {{ permission_id }}"
80
+ level: info
81
+ logger: ha-opencode.permission
@@ -0,0 +1,191 @@
1
+ blueprint:
2
+ name: OpenCode State Notifications
3
+ description: >
4
+ Send notifications to your mobile device when OpenCode sessions need attention.
5
+ Notifies when:
6
+ - A task completes (idle after working)
7
+ - Permission is required (with approve/reject buttons)
8
+ - An error occurs
9
+
10
+ Works with both iOS and Android via the Home Assistant Companion app.
11
+
12
+ Requires the Permission Response Handler blueprint to handle approve/reject actions.
13
+ domain: automation
14
+ author: ha-opencode
15
+ source_url: https://github.com/your-repo/ha-opencode/blob/main/blueprints/opencode_state_notifications.yaml
16
+ input:
17
+ notify_service:
18
+ name: Notification Service
19
+ description: >
20
+ The notification service to use (e.g., notify.mobile_app_your_phone).
21
+ selector:
22
+ text:
23
+ default: "notify.mobile_app_phone"
24
+
25
+ notify_on_complete:
26
+ name: Notify on Task Complete
27
+ description: Send a notification when a task completes (idle after working).
28
+ default: true
29
+ selector:
30
+ boolean:
31
+
32
+ notify_on_permission:
33
+ name: Notify on Permission Required
34
+ description: Send a notification with approve/reject buttons when permission is needed.
35
+ default: true
36
+ selector:
37
+ boolean:
38
+
39
+ notify_on_error:
40
+ name: Notify on Error
41
+ description: Send a notification when an error occurs.
42
+ default: true
43
+ selector:
44
+ boolean:
45
+
46
+ notification_channel:
47
+ name: Notification Channel (Android)
48
+ description: >
49
+ Android notification channel name.
50
+ You can create channels in the Companion app settings.
51
+ default: "OpenCode"
52
+ selector:
53
+ text:
54
+
55
+ dashboard_path:
56
+ name: Dashboard Path
57
+ description: >
58
+ Path to your OpenCode dashboard for click action (e.g., /lovelace/opencode).
59
+ Leave empty to use default behavior.
60
+ default: "/lovelace/opencode"
61
+ selector:
62
+ text:
63
+
64
+ mode: parallel
65
+ max_exceeded: silent
66
+
67
+ trigger:
68
+ - platform: mqtt
69
+ topic: "opencode/+/state"
70
+ id: state_change
71
+
72
+ variables:
73
+ # The new state from the MQTT payload
74
+ new_state: "{{ trigger.payload }}"
75
+
76
+ # Extract device_id from topic: opencode/{device_id}/state
77
+ device_id: "{{ trigger.topic.split('/')[1] }}"
78
+
79
+ # Build the state_topic_base for entity lookup
80
+ state_topic_base: "opencode/{{ device_id }}"
81
+
82
+ # Find the device_id entity that has this state_topic_base in its attributes
83
+ device_id_entity: >
84
+ {% set ns = namespace(found='') %}
85
+ {% for entity in states.sensor %}
86
+ {% if entity.attributes.get('state_topic_base', '') == state_topic_base %}
87
+ {% set ns.found = entity.entity_id %}
88
+ {% endif %}
89
+ {% endfor %}
90
+ {{ ns.found }}
91
+
92
+ # Check if we found the entity
93
+ entity_found: "{{ device_id_entity != '' }}"
94
+
95
+ # Derive other entity IDs from the device_id entity (they share the same base)
96
+ entity_base: "{{ device_id_entity | replace('_device_id', '') if entity_found else '' }}"
97
+ state_entity: "{{ (entity_base ~ '_state') if entity_found else '' }}"
98
+ session_entity: "{{ (entity_base ~ '_session_title') if entity_found else '' }}"
99
+
100
+ # Get values from entities (with defaults)
101
+ previous_state: "{{ state_attr(state_entity, 'previous_state') | default('unknown') if entity_found else 'unknown' }}"
102
+ project_name: "{{ state_attr(device_id_entity, 'device_name') | default('OpenCode') if entity_found else 'OpenCode' }}"
103
+ command_topic: "{{ state_attr(device_id_entity, 'command_topic') | default('') if entity_found else '' }}"
104
+ permission_id: "{{ state_attr(state_entity, 'permission_id') | default('') if entity_found else '' }}"
105
+ permission_title: "{{ state_attr(state_entity, 'permission_title') | default('Permission needed') if entity_found else 'Permission needed' }}"
106
+ error_message: "{{ state_attr(state_entity, 'error_message') | default('An error occurred') if entity_found else 'An error occurred' }}"
107
+ session_title: "{{ states(session_entity) | default('Task finished') if entity_found else 'Task finished' }}"
108
+
109
+ # Determine if this is a transition we care about
110
+ was_working: "{{ previous_state == 'working' }}"
111
+
112
+ action:
113
+ # Log for debugging (can be removed later)
114
+ - service: system_log.write
115
+ data:
116
+ message: >
117
+ OpenCode notification trigger: new_state={{ new_state }}, previous_state={{ previous_state }},
118
+ device_id={{ device_id }}, entity_found={{ entity_found }}, was_working={{ was_working }}
119
+ level: info
120
+ logger: ha-opencode.blueprint
121
+
122
+ - choose:
123
+ # Permission Required (always notify, regardless of previous state)
124
+ - conditions:
125
+ - condition: template
126
+ value_template: "{{ new_state == 'waiting_permission' }}"
127
+ - condition: template
128
+ value_template: !input notify_on_permission
129
+ sequence:
130
+ # Small delay to let permission attributes arrive
131
+ - delay:
132
+ milliseconds: 500
133
+ - service: !input notify_service
134
+ data:
135
+ title: "OpenCode: Permission Required"
136
+ message: "{{ project_name }}: {{ permission_title }}"
137
+ data:
138
+ tag: "opencode-permission-{{ device_id }}"
139
+ channel: !input notification_channel
140
+ importance: high
141
+ priority: high
142
+ ttl: 0
143
+ clickAction: !input dashboard_path
144
+ url: !input dashboard_path
145
+ actions:
146
+ - action: "OPENCODE_APPROVE_{{ device_id }}"
147
+ title: "Approve"
148
+ - action: "OPENCODE_REJECT_{{ device_id }}"
149
+ title: "Reject"
150
+ # Pass data needed for permission response
151
+ action_data:
152
+ device_id: "{{ device_id }}"
153
+ permission_id: "{{ permission_id }}"
154
+ command_topic: "{{ command_topic }}"
155
+
156
+ # Error Occurred (only after working)
157
+ - conditions:
158
+ - condition: template
159
+ value_template: "{{ new_state == 'error' and was_working }}"
160
+ - condition: template
161
+ value_template: !input notify_on_error
162
+ sequence:
163
+ - service: !input notify_service
164
+ data:
165
+ title: "OpenCode: Error"
166
+ message: "{{ project_name }}: {{ error_message }}"
167
+ data:
168
+ tag: "opencode-error-{{ device_id }}"
169
+ channel: !input notification_channel
170
+ importance: high
171
+ priority: high
172
+ ttl: 0
173
+ clickAction: !input dashboard_path
174
+ url: !input dashboard_path
175
+
176
+ # Work Complete (idle after working)
177
+ - conditions:
178
+ - condition: template
179
+ value_template: "{{ new_state == 'idle' and was_working }}"
180
+ - condition: template
181
+ value_template: !input notify_on_complete
182
+ sequence:
183
+ - service: !input notify_service
184
+ data:
185
+ title: "OpenCode: Task Complete"
186
+ message: "{{ project_name }}: {{ session_title }}"
187
+ data:
188
+ tag: "opencode-complete-{{ device_id }}"
189
+ channel: !input notification_channel
190
+ clickAction: !input dashboard_path
191
+ url: !input dashboard_path
@@ -0,0 +1,30 @@
1
+ import type { MqttWrapper } from "./mqtt.js";
2
+ import type { HaConfig } from "./config.js";
3
+ export interface CleanupConfig {
4
+ maxAgeDays: number;
5
+ haConfig: HaConfig;
6
+ }
7
+ export interface CleanupResult {
8
+ sessionsRemoved: number;
9
+ sessionIds: string[];
10
+ }
11
+ /**
12
+ * Clean up stale OpenCode sessions from Home Assistant.
13
+ * Removes entities for sessions that haven't been active in `maxAgeDays` days.
14
+ *
15
+ * @param mqtt - MQTT client wrapper
16
+ * @param config - Cleanup configuration
17
+ * @returns Result with count and IDs of removed sessions
18
+ */
19
+ export declare function cleanupStaleSessions(mqtt: MqttWrapper, config: CleanupConfig): Promise<CleanupResult>;
20
+ /**
21
+ * Manually trigger cleanup and publish results to MQTT.
22
+ * Called via the cleanup_stale_sessions command.
23
+ */
24
+ export declare function cleanupStaleSessionsManual(mqtt: MqttWrapper, config: CleanupConfig): Promise<void>;
25
+ /**
26
+ * Run cleanup in the background (non-blocking).
27
+ * Logs results but doesn't throw on error.
28
+ */
29
+ export declare function runCleanupInBackground(mqtt: MqttWrapper, config: CleanupConfig): void;
30
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AA+ED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CA8BxB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,IAAI,CAYN"}