@vauxr/openclaw 2026.5.24-2 → 2026.5.31
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/dist/index.js +13 -8
- package/dist/src/bridge.js +33 -7
- package/dist/src/channel.js +25 -4
- package/openclaw.plugin.json +1 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -28,15 +28,20 @@ const entry = defineChannelPluginEntry({
|
|
|
28
28
|
}
|
|
29
29
|
const client = new VauxrAPIClient(httpBase, config.token ?? "");
|
|
30
30
|
registerTools(api, client);
|
|
31
|
-
// WS bridge
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
31
|
+
// Construct the WS bridge but DO NOT start it here. `registerFull` is
|
|
32
|
+
// invoked from introspection paths too (e.g. `openclaw doctor`), and
|
|
33
|
+
// starting the bridge here would open a live WebSocket to vauxr during
|
|
34
|
+
// diagnostics. The bridge is started later by `gateway.startAccount`
|
|
35
|
+
// (see channel.ts), which only fires when the gateway is actually
|
|
36
|
+
// bringing the channel up for runtime use. The globalThis stash bridges
|
|
37
|
+
// the two scopes because `startAccount` doesn't have access to `api`.
|
|
38
|
+
//
|
|
39
|
+
// The single-bridge guard remains here so multiple `registerFull`
|
|
40
|
+
// invocations in the same process don't reconstruct the bridge — they'd
|
|
41
|
+
// contend for the single active channel slot in vauxr otherwise.
|
|
35
42
|
const g = globalThis;
|
|
36
|
-
if (!g.
|
|
37
|
-
g.
|
|
38
|
-
const bridge = new VauxrBridge(api, config);
|
|
39
|
-
bridge.start();
|
|
43
|
+
if (!g.__vauxrBridge) {
|
|
44
|
+
g.__vauxrBridge = new VauxrBridge(api, config);
|
|
40
45
|
}
|
|
41
46
|
// Voice system prompt injection for vauxr sessions. Match both the bare
|
|
42
47
|
// form (`vauxr:<deviceId>`) used by the old subagent.run path and the
|
package/dist/src/bridge.js
CHANGED
|
@@ -8,6 +8,7 @@ export class VauxrBridge {
|
|
|
8
8
|
reconnectMs = INITIAL_RECONNECT_MS;
|
|
9
9
|
reconnectTimer = null;
|
|
10
10
|
unsubscribeEvents = null;
|
|
11
|
+
started = false;
|
|
11
12
|
// Inflight turns keyed by deviceId. channel.turn.run doesn't surface an SDK
|
|
12
13
|
// runId to the caller (unlike the old subagent.run path), so dispatch-time
|
|
13
14
|
// bookkeeping is keyed by deviceId; we then latch onto the SDK runId on the
|
|
@@ -33,10 +34,16 @@ export class VauxrBridge {
|
|
|
33
34
|
this.wsUrl = base.replace(/^http/, "ws") + "/channel";
|
|
34
35
|
}
|
|
35
36
|
start() {
|
|
37
|
+
if (this.started)
|
|
38
|
+
return;
|
|
39
|
+
this.started = true;
|
|
36
40
|
this.connect();
|
|
37
41
|
this.subscribeAgentEvents();
|
|
38
42
|
}
|
|
39
43
|
stop() {
|
|
44
|
+
if (!this.started)
|
|
45
|
+
return;
|
|
46
|
+
this.started = false;
|
|
40
47
|
if (this.reconnectTimer) {
|
|
41
48
|
clearTimeout(this.reconnectTimer);
|
|
42
49
|
this.reconnectTimer = null;
|
|
@@ -49,13 +56,18 @@ export class VauxrBridge {
|
|
|
49
56
|
this.ws.close();
|
|
50
57
|
this.ws = null;
|
|
51
58
|
}
|
|
59
|
+
// Reset the backoff so a subsequent stop/start cycle (e.g. channel
|
|
60
|
+
// aborts then restarts) begins reconnecting at INITIAL_RECONNECT_MS
|
|
61
|
+
// instead of inheriting whatever escalated delay the previous run
|
|
62
|
+
// had accumulated.
|
|
63
|
+
this.reconnectMs = INITIAL_RECONNECT_MS;
|
|
52
64
|
}
|
|
53
65
|
connect() {
|
|
54
|
-
this.api.logger.
|
|
66
|
+
this.api.logger.debug?.(`[vauxr-bridge] Connecting to vauxr: ${this.wsUrl}`);
|
|
55
67
|
const ws = new WebSocket(this.wsUrl);
|
|
56
68
|
this.ws = ws;
|
|
57
69
|
ws.on("open", () => {
|
|
58
|
-
this.api.logger.
|
|
70
|
+
this.api.logger.debug?.("[vauxr-bridge] Connected to vauxr");
|
|
59
71
|
this.reconnectMs = INITIAL_RECONNECT_MS;
|
|
60
72
|
// Authenticate with channel token
|
|
61
73
|
if (this.config.token) {
|
|
@@ -72,9 +84,17 @@ export class VauxrBridge {
|
|
|
72
84
|
}
|
|
73
85
|
});
|
|
74
86
|
ws.on("close", () => {
|
|
75
|
-
this.api.logger.
|
|
87
|
+
this.api.logger.debug?.("[vauxr-bridge] Disconnected from vauxr");
|
|
88
|
+
// Identity check: a stop() during the close-event async delay can be
|
|
89
|
+
// followed by another start() that opens a fresh ws. If we cleared
|
|
90
|
+
// `this.ws` blindly here we'd wipe the new socket's reference and
|
|
91
|
+
// also fire a duplicate reconnect. Only act if we're still the
|
|
92
|
+
// bridge's current ws.
|
|
93
|
+
if (this.ws !== ws)
|
|
94
|
+
return;
|
|
76
95
|
this.ws = null;
|
|
77
|
-
this.
|
|
96
|
+
if (this.started)
|
|
97
|
+
this.scheduleReconnect();
|
|
78
98
|
});
|
|
79
99
|
ws.on("error", (err) => {
|
|
80
100
|
this.api.logger.warn(`[vauxr-bridge] WS error: ${String(err)}`);
|
|
@@ -84,7 +104,7 @@ export class VauxrBridge {
|
|
|
84
104
|
scheduleReconnect() {
|
|
85
105
|
if (this.reconnectTimer)
|
|
86
106
|
return;
|
|
87
|
-
this.api.logger.
|
|
107
|
+
this.api.logger.debug?.(`[vauxr-bridge] Reconnecting in ${this.reconnectMs}ms`);
|
|
88
108
|
this.reconnectTimer = setTimeout(() => {
|
|
89
109
|
this.reconnectTimer = null;
|
|
90
110
|
this.connect();
|
|
@@ -102,7 +122,7 @@ export class VauxrBridge {
|
|
|
102
122
|
this.api.logger.info(`[vauxr-bridge] Device ${frame.deviceId ?? "unknown"}: ${frame.state ?? "unknown"}`);
|
|
103
123
|
break;
|
|
104
124
|
case "channel.ready":
|
|
105
|
-
this.api.logger.
|
|
125
|
+
this.api.logger.debug?.("[vauxr-bridge] Channel authenticated");
|
|
106
126
|
break;
|
|
107
127
|
case "error":
|
|
108
128
|
this.api.logger.warn(`[vauxr-bridge] Error from vauxr: ${frame.code ?? "UNKNOWN"} — ${frame.message ?? "no details"}`);
|
|
@@ -144,7 +164,13 @@ export class VauxrBridge {
|
|
|
144
164
|
Timestamp: Date.now(),
|
|
145
165
|
};
|
|
146
166
|
try {
|
|
147
|
-
|
|
167
|
+
// OpenClaw 2026.5.28 renamed `runtime.channel.turn` to
|
|
168
|
+
// `runtime.channel.inbound` (pure rename — same signature, same
|
|
169
|
+
// ChannelInboundEventRunnerParams shape as the prior
|
|
170
|
+
// RunChannelTurnParams). Earlier vauxr-openclaw releases that
|
|
171
|
+
// referenced `.turn.run` will throw `Cannot read properties of
|
|
172
|
+
// undefined (reading 'run')` on gateways 2026.5.28+.
|
|
173
|
+
await this.api.runtime.channel.inbound.run({
|
|
148
174
|
channel: "vauxr",
|
|
149
175
|
raw: { deviceId, text },
|
|
150
176
|
adapter: {
|
package/dist/src/channel.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createTopLevelChannelConfigBase } from "openclaw/plugin-sdk/channel-con
|
|
|
7
7
|
export const vauxrPlugin = createChatChannelPlugin({
|
|
8
8
|
base: createChannelPluginBase({
|
|
9
9
|
id: "vauxr",
|
|
10
|
-
meta: { label: "Vauxr" },
|
|
10
|
+
meta: { label: "Vauxr", selectionLabel: "Vauxr (voice devices)", docsPath: "/channels/vauxr", blurb: "Speak through and control Vauxr voice devices on your LAN." },
|
|
11
11
|
capabilities: {
|
|
12
12
|
chatTypes: ["direct"],
|
|
13
13
|
},
|
|
@@ -75,9 +75,30 @@ vauxrPlugin.config.isConfigured = (_account, cfg) => {
|
|
|
75
75
|
};
|
|
76
76
|
vauxrPlugin.gateway = {
|
|
77
77
|
startAccount: async (ctx) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// If the channel was aborted before startAccount even ran, there's no
|
|
79
|
+
// bridge to start and nothing to clean up — short-circuit.
|
|
80
|
+
if (ctx.abortSignal.aborted)
|
|
81
|
+
return;
|
|
82
|
+
const g = globalThis;
|
|
83
|
+
g.__vauxrBridge?.start();
|
|
84
|
+
try {
|
|
85
|
+
await new Promise((resolve) => {
|
|
86
|
+
// Guard against the race where the signal aborts between the
|
|
87
|
+
// top-of-function check and the listener attach below. In that
|
|
88
|
+
// window `addEventListener("abort", ...)` would never fire,
|
|
89
|
+
// hanging startAccount forever.
|
|
90
|
+
if (ctx.abortSignal.aborted) {
|
|
91
|
+
resolve();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
// Always stop the bridge on the way out — covers normal abort,
|
|
99
|
+
// already-aborted-after-attach, and any thrown error in between.
|
|
100
|
+
g.__vauxrBridge?.stop();
|
|
101
|
+
}
|
|
81
102
|
},
|
|
82
103
|
};
|
|
83
104
|
function resolveSection(cfg) {
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vauxr/openclaw",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"./dist/setup-entry.js"
|
|
17
17
|
],
|
|
18
18
|
"compat": {
|
|
19
|
-
"pluginApi": ">=2026.5.
|
|
20
|
-
"minGatewayVersion": "2026.5.
|
|
19
|
+
"pluginApi": ">=2026.5.28",
|
|
20
|
+
"minGatewayVersion": "2026.5.28"
|
|
21
21
|
},
|
|
22
22
|
"build": {
|
|
23
|
-
"openclawVersion": "2026.5.
|
|
24
|
-
"pluginSdkVersion": "2026.5.
|
|
23
|
+
"openclawVersion": "2026.5.28",
|
|
24
|
+
"pluginSdkVersion": "2026.5.28"
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/ws": "^8.5.0",
|
|
33
|
-
"openclaw": "^2026.5.
|
|
33
|
+
"openclaw": "^2026.5.28",
|
|
34
34
|
"typescript": "^5.0.0"
|
|
35
35
|
},
|
|
36
36
|
"description": "An OpenClaw plugin that gives your agents the ability to speak through and control Vauxr voice assistant devices.",
|