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 +21 -0
- package/README.md +315 -0
- package/blueprints/opencode_permission_response.yaml +81 -0
- package/blueprints/opencode_state_notifications.yaml +191 -0
- package/dist/cleanup.d.ts +30 -0
- package/dist/cleanup.d.ts.map +1 -0
- package/dist/cleanup.js +125 -0
- package/dist/cleanup.js.map +1 -0
- package/dist/commands.d.ts +24 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +309 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +129 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +257 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/mqtt.d.ts +16 -0
- package/dist/mqtt.d.ts.map +1 -0
- package/dist/mqtt.js +141 -0
- package/dist/mqtt.js.map +1 -0
- package/dist/notify.d.ts +11 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +20 -0
- package/dist/notify.js.map +1 -0
- package/dist/state.d.ts +42 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +282 -0
- package/dist/state.js.map +1 -0
- package/package.json +53 -0
package/dist/mqtt.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import mqtt from "mqtt";
|
|
2
|
+
export async function connectMqtt(config, will) {
|
|
3
|
+
const options = {
|
|
4
|
+
clientId: config.clientId,
|
|
5
|
+
clean: true,
|
|
6
|
+
connectTimeout: 10000,
|
|
7
|
+
reconnectPeriod: 5000,
|
|
8
|
+
};
|
|
9
|
+
if (config.username) {
|
|
10
|
+
options.username = config.username;
|
|
11
|
+
}
|
|
12
|
+
if (config.password) {
|
|
13
|
+
options.password = config.password;
|
|
14
|
+
}
|
|
15
|
+
// Configure Last Will and Testament (LWT) for availability tracking
|
|
16
|
+
if (will) {
|
|
17
|
+
options.will = {
|
|
18
|
+
topic: will.topic,
|
|
19
|
+
payload: Buffer.from(will.payload),
|
|
20
|
+
qos: 1,
|
|
21
|
+
retain: will.retain,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const url = `mqtt://${config.host}:${config.port}`;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const client = mqtt.connect(url, options);
|
|
27
|
+
let connected = false;
|
|
28
|
+
const timeout = setTimeout(() => {
|
|
29
|
+
if (!connected) {
|
|
30
|
+
client.end(true);
|
|
31
|
+
reject(new Error(`MQTT connection timeout to ${url}`));
|
|
32
|
+
}
|
|
33
|
+
}, options.connectTimeout);
|
|
34
|
+
client.on("connect", () => {
|
|
35
|
+
connected = true;
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
resolve(createWrapper(client));
|
|
38
|
+
});
|
|
39
|
+
client.on("error", (err) => {
|
|
40
|
+
if (!connected) {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
client.end(true); // Stop reconnection attempts
|
|
43
|
+
reject(err);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a topic matches a pattern with MQTT wildcards.
|
|
50
|
+
* + matches a single level, # matches multiple levels.
|
|
51
|
+
*/
|
|
52
|
+
function topicMatchesPattern(topic, pattern) {
|
|
53
|
+
const topicParts = topic.split("/");
|
|
54
|
+
const patternParts = pattern.split("/");
|
|
55
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
56
|
+
const patternPart = patternParts[i];
|
|
57
|
+
if (patternPart === "#") {
|
|
58
|
+
// # matches everything from this point
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (i >= topicParts.length) {
|
|
62
|
+
// Topic is shorter than pattern
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (patternPart !== "+" && patternPart !== topicParts[i]) {
|
|
66
|
+
// Not a wildcard and doesn't match
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Pattern and topic must be same length (unless pattern ends with #)
|
|
71
|
+
return topicParts.length === patternParts.length;
|
|
72
|
+
}
|
|
73
|
+
function createWrapper(client) {
|
|
74
|
+
const handlers = new Map();
|
|
75
|
+
client.on("message", (topic, payload) => {
|
|
76
|
+
// Check all registered patterns for a match
|
|
77
|
+
for (const [pattern, handler] of handlers) {
|
|
78
|
+
if (topicMatchesPattern(topic, pattern)) {
|
|
79
|
+
try {
|
|
80
|
+
handler(topic, payload.toString());
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error(`[ha-opencode] Error handling message on ${topic}:`, err);
|
|
84
|
+
}
|
|
85
|
+
break; // Only call first matching handler
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
publish(topic, payload, retain = false) {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const message = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
93
|
+
client.publish(topic, message, { qos: 1, retain }, (err) => {
|
|
94
|
+
if (err) {
|
|
95
|
+
reject(err);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
subscribe(topic, handler) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
client.subscribe(topic, { qos: 1 }, (err) => {
|
|
106
|
+
if (err) {
|
|
107
|
+
reject(err);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
handlers.set(topic, handler);
|
|
111
|
+
resolve();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
unsubscribe(topic) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
client.unsubscribe(topic, (err) => {
|
|
119
|
+
if (err) {
|
|
120
|
+
reject(err);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
handlers.delete(topic);
|
|
124
|
+
resolve();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
isConnected() {
|
|
130
|
+
return client.connected;
|
|
131
|
+
},
|
|
132
|
+
close() {
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
client.end(false, {}, () => {
|
|
135
|
+
resolve();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=mqtt.js.map
|
package/dist/mqtt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mqtt.js","sourceRoot":"","sources":["../src/mqtt.ts"],"names":[],"mappings":"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"}
|
package/dist/notify.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform terminal notification utilities.
|
|
3
|
+
* Uses Kitty's OSC 99 protocol with BEL fallback.
|
|
4
|
+
* @see https://sw.kovidgoyal.net/kitty/desktop-notifications/
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Trigger a terminal notification.
|
|
8
|
+
* Uses OSC 99 (Kitty's native notification protocol) with BEL fallback.
|
|
9
|
+
*/
|
|
10
|
+
export declare function notify(title: string, message: string): void;
|
|
11
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAW3D"}
|
package/dist/notify.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform terminal notification utilities.
|
|
3
|
+
* Uses Kitty's OSC 99 protocol with BEL fallback.
|
|
4
|
+
* @see https://sw.kovidgoyal.net/kitty/desktop-notifications/
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Trigger a terminal notification.
|
|
8
|
+
* Uses OSC 99 (Kitty's native notification protocol) with BEL fallback.
|
|
9
|
+
*/
|
|
10
|
+
export function notify(title, message) {
|
|
11
|
+
// OSC 99 - Kitty desktop notification protocol
|
|
12
|
+
// Format: ESC ] 99 ; i=<id>:d=0:p=title ; <body> BEL
|
|
13
|
+
const id = Date.now().toString();
|
|
14
|
+
const osc99 = `\x1b]99;i=${id}:d=0:p=title;${title}\x07\x1b]99;i=${id}:d=1:p=body;${message}\x07`;
|
|
15
|
+
// Write OSC 99 notification
|
|
16
|
+
process.stdout.write(osc99);
|
|
17
|
+
// Also trigger terminal bell (BEL) for audible alert
|
|
18
|
+
process.stdout.write("\x07");
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,OAAe;IACnD,+CAA+C;IAC/C,qDAAqD;IACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,aAAa,EAAE,gBAAgB,KAAK,iBAAiB,EAAE,eAAe,OAAO,MAAM,CAAC;IAElG,4BAA4B;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE5B,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Event } from "@opencode-ai/sdk";
|
|
2
|
+
import type { Discovery, PermissionInfo } from "./discovery.js";
|
|
3
|
+
export declare class StateTracker {
|
|
4
|
+
private readonly discovery;
|
|
5
|
+
private state;
|
|
6
|
+
private pendingPermission;
|
|
7
|
+
private currentSessionId;
|
|
8
|
+
private deviceNameUpdated;
|
|
9
|
+
constructor(discovery: Discovery);
|
|
10
|
+
getCurrentSessionId(): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Check if we have a valid session title for updating the device friendly name.
|
|
13
|
+
*/
|
|
14
|
+
private hasValidTitle;
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the state tracker.
|
|
17
|
+
* Registers the device with Home Assistant immediately since we have the session ID.
|
|
18
|
+
*/
|
|
19
|
+
initialize(): Promise<void>;
|
|
20
|
+
getPendingPermission(): PermissionInfo | null;
|
|
21
|
+
handleEvent(event: Event): Promise<void>;
|
|
22
|
+
private onSessionCreated;
|
|
23
|
+
private onSessionUpdated;
|
|
24
|
+
/**
|
|
25
|
+
* Update the device friendly name in Home Assistant if we have a valid title
|
|
26
|
+
* and haven't done so yet.
|
|
27
|
+
*/
|
|
28
|
+
private maybeUpdateDeviceName;
|
|
29
|
+
private onSessionIdle;
|
|
30
|
+
private onSessionError;
|
|
31
|
+
private onMessageUpdated;
|
|
32
|
+
private onMessagePartUpdated;
|
|
33
|
+
private onPermissionUpdated;
|
|
34
|
+
private onPermissionReplied;
|
|
35
|
+
clearPermission(): Promise<void>;
|
|
36
|
+
private updateActivity;
|
|
37
|
+
private updateState;
|
|
38
|
+
private publishStateAttributes;
|
|
39
|
+
private publish;
|
|
40
|
+
private publishAll;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAa,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAmB3E,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,SAAS,EAAE,SAAS;IAkBhC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC,oBAAoB,IAAI,cAAc,GAAG,IAAI;IAIvC,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;YAyChC,gBAAgB;YAqBhB,gBAAgB;IAoB9B;;;OAGG;YACW,qBAAqB;YASrB,aAAa;YAWb,cAAc;YAmBd,gBAAgB;YAiChB,oBAAoB;YAoCpB,mBAAmB;YAuBnB,mBAAmB;IAW3B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtC,OAAO,CAAC,cAAc;YAIR,WAAW;YAiBX,sBAAsB;YAUtB,OAAO;YAWP,UAAU;CAUzB"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { hostname } from "os";
|
|
2
|
+
export class StateTracker {
|
|
3
|
+
discovery;
|
|
4
|
+
state;
|
|
5
|
+
pendingPermission = null;
|
|
6
|
+
currentSessionId = null;
|
|
7
|
+
deviceNameUpdated = false;
|
|
8
|
+
constructor(discovery) {
|
|
9
|
+
this.discovery = discovery;
|
|
10
|
+
this.state = {
|
|
11
|
+
state: "idle",
|
|
12
|
+
previousState: null,
|
|
13
|
+
sessionTitle: "Untitled",
|
|
14
|
+
model: "unknown",
|
|
15
|
+
currentTool: "none",
|
|
16
|
+
tokensInput: 0,
|
|
17
|
+
tokensOutput: 0,
|
|
18
|
+
cost: 0,
|
|
19
|
+
lastActivity: new Date().toISOString(),
|
|
20
|
+
agent: null,
|
|
21
|
+
currentAgent: null,
|
|
22
|
+
errorMessage: null,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
getCurrentSessionId() {
|
|
26
|
+
return this.currentSessionId;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if we have a valid session title for updating the device friendly name.
|
|
30
|
+
*/
|
|
31
|
+
hasValidTitle() {
|
|
32
|
+
const title = this.state.sessionTitle;
|
|
33
|
+
return !!(title &&
|
|
34
|
+
title !== "No active session" &&
|
|
35
|
+
title !== "Untitled" &&
|
|
36
|
+
title.toLowerCase() !== "unknown");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Initialize the state tracker.
|
|
40
|
+
* Registers the device with Home Assistant immediately since we have the session ID.
|
|
41
|
+
*/
|
|
42
|
+
async initialize() {
|
|
43
|
+
await this.discovery.registerDevice();
|
|
44
|
+
await this.discovery.publishDeviceInfo();
|
|
45
|
+
await this.discovery.publishAvailable();
|
|
46
|
+
await this.publishAll();
|
|
47
|
+
await this.publishStateAttributes();
|
|
48
|
+
await this.discovery.publishPermission(null);
|
|
49
|
+
}
|
|
50
|
+
getPendingPermission() {
|
|
51
|
+
return this.pendingPermission;
|
|
52
|
+
}
|
|
53
|
+
async handleEvent(event) {
|
|
54
|
+
try {
|
|
55
|
+
switch (event.type) {
|
|
56
|
+
case "session.created":
|
|
57
|
+
await this.onSessionCreated(event);
|
|
58
|
+
break;
|
|
59
|
+
case "session.updated":
|
|
60
|
+
await this.onSessionUpdated(event);
|
|
61
|
+
break;
|
|
62
|
+
case "session.idle":
|
|
63
|
+
await this.onSessionIdle(event);
|
|
64
|
+
break;
|
|
65
|
+
case "session.error":
|
|
66
|
+
await this.onSessionError(event);
|
|
67
|
+
break;
|
|
68
|
+
case "message.updated":
|
|
69
|
+
await this.onMessageUpdated(event);
|
|
70
|
+
break;
|
|
71
|
+
case "message.part.updated":
|
|
72
|
+
await this.onMessagePartUpdated(event);
|
|
73
|
+
break;
|
|
74
|
+
case "permission.updated":
|
|
75
|
+
await this.onPermissionUpdated(event);
|
|
76
|
+
break;
|
|
77
|
+
case "permission.replied":
|
|
78
|
+
await this.onPermissionReplied(event);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error(`[ha-opencode] Error in handleEvent for '${event.type}':`, err);
|
|
84
|
+
console.error(`[ha-opencode] Event properties:`, JSON.stringify(event.properties, null, 2));
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async onSessionCreated(event) {
|
|
89
|
+
this.currentSessionId = event.properties.info.id;
|
|
90
|
+
this.state.sessionTitle = event.properties.info.title || "Untitled";
|
|
91
|
+
this.state.tokensInput = 0;
|
|
92
|
+
this.state.tokensOutput = 0;
|
|
93
|
+
this.state.cost = 0;
|
|
94
|
+
this.updateActivity();
|
|
95
|
+
// Check if we got a valid title on creation
|
|
96
|
+
await this.maybeUpdateDeviceName();
|
|
97
|
+
await this.publish("session_title", this.state.sessionTitle);
|
|
98
|
+
await this.updateState("idle");
|
|
99
|
+
await this.publish("tokens_input", this.state.tokensInput);
|
|
100
|
+
await this.publish("tokens_output", this.state.tokensOutput);
|
|
101
|
+
await this.publish("cost", this.state.cost);
|
|
102
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
103
|
+
}
|
|
104
|
+
async onSessionUpdated(event) {
|
|
105
|
+
const title = event.properties.info.title;
|
|
106
|
+
const titleChanged = title && title !== this.state.sessionTitle;
|
|
107
|
+
if (titleChanged) {
|
|
108
|
+
this.state.sessionTitle = title;
|
|
109
|
+
}
|
|
110
|
+
this.updateActivity();
|
|
111
|
+
// Update device friendly name when we get a valid title
|
|
112
|
+
await this.maybeUpdateDeviceName();
|
|
113
|
+
if (titleChanged) {
|
|
114
|
+
await this.publish("session_title", this.state.sessionTitle);
|
|
115
|
+
}
|
|
116
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Update the device friendly name in Home Assistant if we have a valid title
|
|
120
|
+
* and haven't done so yet.
|
|
121
|
+
*/
|
|
122
|
+
async maybeUpdateDeviceName() {
|
|
123
|
+
if (!this.deviceNameUpdated && this.hasValidTitle()) {
|
|
124
|
+
await this.discovery.updateDeviceName(this.state.sessionTitle);
|
|
125
|
+
// Also update device_id attributes with new device name
|
|
126
|
+
await this.discovery.publishDeviceInfo();
|
|
127
|
+
this.deviceNameUpdated = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async onSessionIdle(_event) {
|
|
131
|
+
this.state.currentTool = "none";
|
|
132
|
+
this.updateActivity();
|
|
133
|
+
await this.updateState("idle");
|
|
134
|
+
await this.publish("current_tool", this.state.currentTool);
|
|
135
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
136
|
+
}
|
|
137
|
+
async onSessionError(event) {
|
|
138
|
+
this.updateActivity();
|
|
139
|
+
// Capture error message if available
|
|
140
|
+
const error = event.properties.error;
|
|
141
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
142
|
+
this.state.errorMessage = String(error.message);
|
|
143
|
+
}
|
|
144
|
+
else if (error && typeof error === "object" && "name" in error) {
|
|
145
|
+
this.state.errorMessage = String(error.name);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.state.errorMessage = "Unknown error";
|
|
149
|
+
}
|
|
150
|
+
await this.updateState("error");
|
|
151
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
152
|
+
}
|
|
153
|
+
async onMessageUpdated(event) {
|
|
154
|
+
const message = event.properties.info;
|
|
155
|
+
if (message.role === "user") {
|
|
156
|
+
// Track primary agent from user message
|
|
157
|
+
if (message.agent && message.agent !== this.state.agent) {
|
|
158
|
+
this.state.agent = message.agent;
|
|
159
|
+
// Reset current agent when new user message arrives
|
|
160
|
+
this.state.currentAgent = null;
|
|
161
|
+
await this.publishStateAttributes();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (message.role === "assistant") {
|
|
165
|
+
// Update model info
|
|
166
|
+
this.state.model = `${message.providerID}/${message.modelID}`;
|
|
167
|
+
await this.publish("model", this.state.model);
|
|
168
|
+
// Update token counts and cost
|
|
169
|
+
// Note: SDK may return Decimal objects, so convert to primitives
|
|
170
|
+
this.state.tokensInput = Number(message.tokens.input);
|
|
171
|
+
this.state.tokensOutput = Number(message.tokens.output);
|
|
172
|
+
this.state.cost = Number(message.cost);
|
|
173
|
+
await this.publish("tokens_input", this.state.tokensInput);
|
|
174
|
+
await this.publish("tokens_output", this.state.tokensOutput);
|
|
175
|
+
await this.publish("cost", this.state.cost);
|
|
176
|
+
}
|
|
177
|
+
this.updateActivity();
|
|
178
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
179
|
+
}
|
|
180
|
+
async onMessagePartUpdated(event) {
|
|
181
|
+
const part = event.properties.part;
|
|
182
|
+
// When receiving text parts with deltas, the model is actively working
|
|
183
|
+
if (part.type === "text" && event.properties.delta) {
|
|
184
|
+
await this.updateState("working");
|
|
185
|
+
}
|
|
186
|
+
// Track agent/sub-agent switching
|
|
187
|
+
if (part.type === "agent") {
|
|
188
|
+
if (part.name !== this.state.currentAgent) {
|
|
189
|
+
this.state.currentAgent = part.name;
|
|
190
|
+
await this.publishStateAttributes();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Track tool execution
|
|
194
|
+
if (part.type === "tool") {
|
|
195
|
+
const toolState = part.state;
|
|
196
|
+
if (toolState.status === "running") {
|
|
197
|
+
this.state.currentTool = part.tool;
|
|
198
|
+
await this.updateState("working");
|
|
199
|
+
await this.publish("current_tool", this.state.currentTool);
|
|
200
|
+
}
|
|
201
|
+
else if (toolState.status === "completed" || toolState.status === "error") {
|
|
202
|
+
this.state.currentTool = "none";
|
|
203
|
+
await this.publish("current_tool", this.state.currentTool);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
this.updateActivity();
|
|
207
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
208
|
+
}
|
|
209
|
+
async onPermissionUpdated(event) {
|
|
210
|
+
const permission = event.properties;
|
|
211
|
+
this.pendingPermission = {
|
|
212
|
+
id: permission.id,
|
|
213
|
+
type: permission.type,
|
|
214
|
+
title: permission.title,
|
|
215
|
+
sessionID: permission.sessionID,
|
|
216
|
+
messageID: permission.messageID,
|
|
217
|
+
callID: permission.callID,
|
|
218
|
+
pattern: permission.pattern,
|
|
219
|
+
metadata: permission.metadata,
|
|
220
|
+
};
|
|
221
|
+
this.updateActivity();
|
|
222
|
+
await this.updateState("waiting_permission");
|
|
223
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
224
|
+
await this.discovery.publishPermission(this.pendingPermission);
|
|
225
|
+
}
|
|
226
|
+
async onPermissionReplied(_event) {
|
|
227
|
+
this.pendingPermission = null;
|
|
228
|
+
this.updateActivity();
|
|
229
|
+
await this.updateState("working");
|
|
230
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
231
|
+
await this.discovery.publishPermission(null);
|
|
232
|
+
}
|
|
233
|
+
async clearPermission() {
|
|
234
|
+
this.pendingPermission = null;
|
|
235
|
+
await this.discovery.publishPermission(null);
|
|
236
|
+
}
|
|
237
|
+
updateActivity() {
|
|
238
|
+
this.state.lastActivity = new Date().toISOString();
|
|
239
|
+
}
|
|
240
|
+
async updateState(newState) {
|
|
241
|
+
if (this.state.state !== newState) {
|
|
242
|
+
this.state.previousState = this.state.state;
|
|
243
|
+
this.state.state = newState;
|
|
244
|
+
// Clear error message when transitioning away from error state
|
|
245
|
+
if (newState !== "error") {
|
|
246
|
+
this.state.errorMessage = null;
|
|
247
|
+
}
|
|
248
|
+
// IMPORTANT: Publish attributes BEFORE state so that when HA automation
|
|
249
|
+
// triggers on state topic, the previous_state attribute is already available
|
|
250
|
+
await this.publishStateAttributes();
|
|
251
|
+
await this.publish("state", this.state.state);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async publishStateAttributes() {
|
|
255
|
+
await this.discovery.publishAttributes("state", {
|
|
256
|
+
previous_state: this.state.previousState,
|
|
257
|
+
agent: this.state.agent,
|
|
258
|
+
current_agent: this.state.currentAgent,
|
|
259
|
+
hostname: hostname(),
|
|
260
|
+
error_message: this.state.errorMessage,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async publish(key, value) {
|
|
264
|
+
try {
|
|
265
|
+
await this.discovery.publishState(key, value);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
console.error(`[ha-opencode] Failed to publish ${key}:`, err);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async publishAll() {
|
|
272
|
+
await this.publish("state", this.state.state);
|
|
273
|
+
await this.publish("session_title", this.state.sessionTitle);
|
|
274
|
+
await this.publish("model", this.state.model);
|
|
275
|
+
await this.publish("current_tool", this.state.currentTool);
|
|
276
|
+
await this.publish("tokens_input", this.state.tokensInput);
|
|
277
|
+
await this.publish("tokens_output", this.state.tokensOutput);
|
|
278
|
+
await this.publish("cost", this.state.cost);
|
|
279
|
+
await this.publish("last_activity", this.state.lastActivity);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ha-opencode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenCode plugin for Home Assistant integration via MQTT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"blueprints",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://gitlab.com/opencode-home-assistant/opencode-plugin.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://gitlab.com/opencode-home-assistant/opencode-plugin#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://gitlab.com/opencode-home-assistant/opencode-plugin/-/issues"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"prepublishOnly": "npm run build && npm test"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"opencode",
|
|
32
|
+
"home-assistant",
|
|
33
|
+
"mqtt",
|
|
34
|
+
"plugin"
|
|
35
|
+
],
|
|
36
|
+
"author": "Stephen Golub",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"mqtt": "^5.10.0",
|
|
40
|
+
"source-map-support": "^0.5.21"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@opencode-ai/plugin": "^1.0.23",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"@types/source-map-support": "^0.5.10",
|
|
46
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
47
|
+
"typescript": "^5.7.0",
|
|
48
|
+
"vitest": "^4.0.16"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|