@earzbook/openclaw 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/README.md +46 -0
- package/dist/adapter.d.ts +29 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +368 -0
- package/dist/adapter.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +84 -0
- package/dist/config.js.map +1 -0
- package/dist/dispatch.d.ts +48 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +261 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +571 -0
- package/dist/index.js.map +1 -0
- package/dist/lockfile.d.ts +20 -0
- package/dist/lockfile.d.ts.map +1 -0
- package/dist/lockfile.js +113 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/logging.d.ts +5 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +98 -0
- package/dist/logging.js.map +1 -0
- package/dist/platform/launchd.d.ts +12 -0
- package/dist/platform/launchd.d.ts.map +1 -0
- package/dist/platform/launchd.js +37 -0
- package/dist/platform/launchd.js.map +1 -0
- package/dist/platform/systemd.d.ts +10 -0
- package/dist/platform/systemd.d.ts.map +1 -0
- package/dist/platform/systemd.js +18 -0
- package/dist/platform/systemd.js.map +1 -0
- package/dist/self-update.d.ts +36 -0
- package/dist/self-update.d.ts.map +1 -0
- package/dist/self-update.js +151 -0
- package/dist/self-update.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @earzbook/openclaw
|
|
2
|
+
|
|
3
|
+
Sidecar daemon that bridges the Earzbook mobile app to a locally-running OpenClaw agent via a WebSocket relay.
|
|
4
|
+
|
|
5
|
+
Long-running Node process, installed once on the user's machine by the Earzbook pair flow. Registered as a per-user service (launchd LaunchAgent on macOS, systemd `--user` unit on Linux) so it auto-starts on login and survives crashes.
|
|
6
|
+
|
|
7
|
+
## Install (user-facing — runs from the Earzbook pair script)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @earzbook/openclaw
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The Earzbook app's "Connect OpenClaw" flow prints a `curl … | bash` one-liner that runs the npm install above, writes `~/.earzbook/sidecar.json`, drops the launchd plist / systemd unit, and starts the service. No manual setup required.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
earzbook-openclaw --config ~/.earzbook/sidecar.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Config file shape:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"relayUrl": "wss://api.earzbook.app/openclaw/relay",
|
|
26
|
+
"deviceToken": "<JWT issued by Earzbook backend at pair time>",
|
|
27
|
+
"userId": "<Earzbook user id>",
|
|
28
|
+
"agentId": "main",
|
|
29
|
+
"versionEndpoint": "https://api.earzbook.app/openclaw/sidecar/version"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
1. Opens a WebSocket to the Earzbook relay with `role: "plugin"` and authenticates with the `deviceToken` JWT.
|
|
36
|
+
2. On each inbound `user_message`, spawns `openclaw agent --agent <agentId> --message <text> --json` to get the local OpenClaw agent's reply.
|
|
37
|
+
3. Sends the reply back as an `agent_reply` frame.
|
|
38
|
+
4. Pings every 30s, reconnects with exponential backoff (3s → 5min cap), checks for updates on startup + every 24h.
|
|
39
|
+
|
|
40
|
+
See the architecture plan in `/Users/earzbook/.claude/plans/users-earzbook-documents-github-recall-rippling-sphinx.md` for the full design.
|
|
41
|
+
|
|
42
|
+
## Why not the OpenClaw channel plugin?
|
|
43
|
+
|
|
44
|
+
We started with a channel plugin at [`../earzbook-plugin/`](../earzbook-plugin/). OpenClaw 2026.5.22 doesn't import external channel plugin entry modules in its gateway daemon — the plugin's `lifecycle.start` and `registerFull` hooks only fire in CLI invocations. The plugin code stays in the repo for when OpenClaw fixes that, or when we fork them.
|
|
45
|
+
|
|
46
|
+
The sidecar lives alongside as a self-contained workaround. Same wire protocol as the plugin would have used.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import type { AdapterCallbacks, AgentReplyFrame } from "./types.js";
|
|
3
|
+
export interface AdapterOptions {
|
|
4
|
+
relayUrl: string;
|
|
5
|
+
deviceToken: string;
|
|
6
|
+
sidecarVersion: string;
|
|
7
|
+
pingIntervalMs?: number;
|
|
8
|
+
reconnectBaseMs?: number;
|
|
9
|
+
reconnectMaxMs?: number;
|
|
10
|
+
/** Override for tests — defaults to global WebSocket constructor. */
|
|
11
|
+
socketFactory?: (url: string) => WebSocket;
|
|
12
|
+
}
|
|
13
|
+
export interface AdapterHandle {
|
|
14
|
+
/** Send an agent_reply frame. Resolves once buffered to the socket. */
|
|
15
|
+
sendAgentReply(frame: Omit<AgentReplyFrame, "type">): Promise<void>;
|
|
16
|
+
/** Current connection state — useful for status surfaces. */
|
|
17
|
+
state(): AdapterState;
|
|
18
|
+
/** Stop reconnecting and close the active socket. */
|
|
19
|
+
close(reason?: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export type AdapterState = "idle" | "connecting" | "connected" | "reconnecting" | "closed";
|
|
22
|
+
/**
|
|
23
|
+
* Create and start the Earzbook relay adapter. The adapter immediately
|
|
24
|
+
* attempts to connect; callbacks are invoked on connection / message /
|
|
25
|
+
* disconnect events. Reconnects are automatic with exponential backoff.
|
|
26
|
+
* Caller owns lifetime via the returned handle.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createAdapter(options: AdapterOptions, callbacks: AdapterCallbacks): AdapterHandle;
|
|
29
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAYA,OAAO,SAA2B,MAAM,IAAI,CAAC;AAC7C,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAQhB,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,6DAA6D;IAC7D,KAAK,IAAI,YAAY,CAAC;IACtB,qDAAqD;IACrD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,YAAY,GACZ,WAAW,GACX,cAAc,GACd,QAAQ,CAAC;AASb;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,gBAAgB,GAC1B,aAAa,CA6Vf"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// WebSocket lifecycle adapter for the Earzbook sidecar.
|
|
2
|
+
//
|
|
3
|
+
// Owns: connect → hello/welcome handshake, inbound user_message dispatch,
|
|
4
|
+
// outbound agent_reply send, ack tracking, periodic ping keepalive, and
|
|
5
|
+
// exponential-backoff reconnect with resume(lastAckSeq) on every reattempt.
|
|
6
|
+
//
|
|
7
|
+
// This is a copy-adapt of openclaw/earzbook-plugin/src/adapter.ts. We keep
|
|
8
|
+
// them as separate files so the plugin and sidecar can evolve independently
|
|
9
|
+
// (e.g. the sidecar may want different reconnect behavior, different log
|
|
10
|
+
// tagging, or extra hooks for the daemon's process lifecycle). Wire
|
|
11
|
+
// protocol stays identical so the relay treats both paths interchangeably.
|
|
12
|
+
import WebSocket from "ws";
|
|
13
|
+
import { PROTOCOL_VERSION } from "./types.js";
|
|
14
|
+
import { log, warn, errLog } from "./logging.js";
|
|
15
|
+
const DEFAULT_PING_INTERVAL_MS = 30_000;
|
|
16
|
+
const DEFAULT_RECONNECT_BASE_MS = 3_000;
|
|
17
|
+
const DEFAULT_RECONNECT_MAX_MS = 300_000;
|
|
18
|
+
const RECONNECT_MULTIPLIER = 2;
|
|
19
|
+
const TAG = "[earzbook-sidecar:adapter]";
|
|
20
|
+
/**
|
|
21
|
+
* Create and start the Earzbook relay adapter. The adapter immediately
|
|
22
|
+
* attempts to connect; callbacks are invoked on connection / message /
|
|
23
|
+
* disconnect events. Reconnects are automatic with exponential backoff.
|
|
24
|
+
* Caller owns lifetime via the returned handle.
|
|
25
|
+
*/
|
|
26
|
+
export function createAdapter(options, callbacks) {
|
|
27
|
+
const pingIntervalMs = options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
28
|
+
const reconnectBaseMs = options.reconnectBaseMs ?? DEFAULT_RECONNECT_BASE_MS;
|
|
29
|
+
const reconnectMaxMs = options.reconnectMaxMs ?? DEFAULT_RECONNECT_MAX_MS;
|
|
30
|
+
const socketFactory = options.socketFactory ?? ((url) => new WebSocket(url));
|
|
31
|
+
let socket = null;
|
|
32
|
+
let state = "idle";
|
|
33
|
+
let lastAckSeq = 0;
|
|
34
|
+
let reconnectAttempt = 0;
|
|
35
|
+
let pingTimer = null;
|
|
36
|
+
let reconnectTimer = null;
|
|
37
|
+
let stopped = false;
|
|
38
|
+
const outboundQueue = [];
|
|
39
|
+
function transition(next) {
|
|
40
|
+
state = next;
|
|
41
|
+
log(TAG, "state =", next);
|
|
42
|
+
}
|
|
43
|
+
function clearTimers() {
|
|
44
|
+
if (pingTimer) {
|
|
45
|
+
clearInterval(pingTimer);
|
|
46
|
+
pingTimer = null;
|
|
47
|
+
}
|
|
48
|
+
if (reconnectTimer) {
|
|
49
|
+
clearTimeout(reconnectTimer);
|
|
50
|
+
reconnectTimer = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function nextReconnectDelay() {
|
|
54
|
+
// Exponential backoff with ±12.5% jitter to avoid thundering herd.
|
|
55
|
+
const exp = reconnectBaseMs * Math.pow(RECONNECT_MULTIPLIER, reconnectAttempt);
|
|
56
|
+
const capped = Math.min(exp, reconnectMaxMs);
|
|
57
|
+
const jitter = Math.random() * 0.25 * capped;
|
|
58
|
+
return Math.floor(capped - capped * 0.125 + jitter);
|
|
59
|
+
}
|
|
60
|
+
function scheduleReconnect(reason) {
|
|
61
|
+
if (stopped)
|
|
62
|
+
return;
|
|
63
|
+
transition("reconnecting");
|
|
64
|
+
const delay = nextReconnectDelay();
|
|
65
|
+
reconnectAttempt += 1;
|
|
66
|
+
log(TAG, `reconnect attempt ${reconnectAttempt} backoff=${Math.round(delay / 1000)}s reason=${reason}`);
|
|
67
|
+
reconnectTimer = setTimeout(connect, delay);
|
|
68
|
+
}
|
|
69
|
+
function sendRaw(frame) {
|
|
70
|
+
if (!socket || socket.readyState !== WebSocket.OPEN)
|
|
71
|
+
return false;
|
|
72
|
+
try {
|
|
73
|
+
socket.send(JSON.stringify(frame));
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
warn(TAG, "send failed:", e);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function flushOutbound() {
|
|
82
|
+
if (!socket || socket.readyState !== WebSocket.OPEN)
|
|
83
|
+
return;
|
|
84
|
+
while (outboundQueue.length > 0) {
|
|
85
|
+
const frame = outboundQueue[0];
|
|
86
|
+
if (!sendRaw(frame))
|
|
87
|
+
break;
|
|
88
|
+
outboundQueue.shift();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function handleServerFrame(frame) {
|
|
92
|
+
switch (frame.type) {
|
|
93
|
+
case "welcome":
|
|
94
|
+
handleWelcome(frame);
|
|
95
|
+
break;
|
|
96
|
+
case "user_message":
|
|
97
|
+
void handleUserMessage(frame);
|
|
98
|
+
break;
|
|
99
|
+
case "list_sessions":
|
|
100
|
+
void handleListSessions(frame);
|
|
101
|
+
break;
|
|
102
|
+
case "delete_session":
|
|
103
|
+
void handleDeleteSession(frame);
|
|
104
|
+
break;
|
|
105
|
+
case "fetch_history":
|
|
106
|
+
void handleFetchHistory(frame);
|
|
107
|
+
break;
|
|
108
|
+
case "pong":
|
|
109
|
+
// No-op — we use ping for keepalive only, not RTT measurement here.
|
|
110
|
+
break;
|
|
111
|
+
case "error":
|
|
112
|
+
errLog(TAG, `server error code=${frame.code} fatal=${frame.fatal} msg=${frame.message}`);
|
|
113
|
+
if (frame.fatal) {
|
|
114
|
+
stopped = true;
|
|
115
|
+
clearTimers();
|
|
116
|
+
socket?.close();
|
|
117
|
+
callbacks.onDisconnected({ fatal: true, reason: `server:${frame.code}` });
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function handleWelcome(frame) {
|
|
123
|
+
log(TAG, `welcome ok userId=${frame.userId} lastAckSeq=${frame.lastAckSeq}`);
|
|
124
|
+
transition("connected");
|
|
125
|
+
reconnectAttempt = 0;
|
|
126
|
+
// Reconcile our local lastAckSeq with the server's authoritative number.
|
|
127
|
+
lastAckSeq = frame.lastAckSeq;
|
|
128
|
+
flushOutbound();
|
|
129
|
+
startPing();
|
|
130
|
+
callbacks.onConnected(frame);
|
|
131
|
+
}
|
|
132
|
+
async function handleUserMessage(frame) {
|
|
133
|
+
log(TAG, `inbound seq=${frame.seq} conv=${frame.conversationId} chars=${frame.text.length} sessionKey=${frame.sessionKey ?? "(none)"}`);
|
|
134
|
+
try {
|
|
135
|
+
await callbacks.onMessage({
|
|
136
|
+
seq: frame.seq,
|
|
137
|
+
clientMessageId: frame.clientMessageId,
|
|
138
|
+
conversationId: frame.conversationId,
|
|
139
|
+
text: frame.text,
|
|
140
|
+
sentAt: frame.sentAt,
|
|
141
|
+
sessionKey: frame.sessionKey,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
errLog(TAG, "onMessage handler threw:", e);
|
|
146
|
+
// Still ack — handler errors should not block the inbound queue.
|
|
147
|
+
}
|
|
148
|
+
lastAckSeq = frame.seq;
|
|
149
|
+
sendRaw({ type: "ack", seq: frame.seq });
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Inbound list_sessions — app asks "which trajectory files do you
|
|
153
|
+
* have on disk?". We dispatch to the handler, wrap the result in a
|
|
154
|
+
* `sessions_list` frame, send it back. If no handler is registered
|
|
155
|
+
* we return an empty list (forgiving — old sidecar versions just
|
|
156
|
+
* report nothing alive).
|
|
157
|
+
*/
|
|
158
|
+
async function handleListSessions(_frame) {
|
|
159
|
+
log(TAG, "inbound list_sessions");
|
|
160
|
+
let sessionKeys = [];
|
|
161
|
+
if (callbacks.onListSessions) {
|
|
162
|
+
try {
|
|
163
|
+
sessionKeys = await callbacks.onListSessions();
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
errLog(TAG, "onListSessions handler threw:", e);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
warn(TAG, "list_sessions received but no onListSessions handler registered");
|
|
171
|
+
}
|
|
172
|
+
log(TAG, `responding sessions_list count=${sessionKeys.length}`);
|
|
173
|
+
sendRaw({ type: "sessions_list", sessionKeys });
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Inbound delete_session — app asked us to remove a chat's trajectory
|
|
177
|
+
* file from disk. We always send an ack frame back so the app can flip
|
|
178
|
+
* its UI state. If no handler is registered we ack with a generic
|
|
179
|
+
* "not supported" error rather than silently dropping.
|
|
180
|
+
*/
|
|
181
|
+
async function handleDeleteSession(frame) {
|
|
182
|
+
log(TAG, `inbound delete_session sessionKey=${frame.sessionKey}`);
|
|
183
|
+
if (!callbacks.onDeleteSession) {
|
|
184
|
+
warn(TAG, "delete_session received but no onDeleteSession handler registered");
|
|
185
|
+
sendRaw({
|
|
186
|
+
type: "delete_session_ack",
|
|
187
|
+
sessionKey: frame.sessionKey,
|
|
188
|
+
success: false,
|
|
189
|
+
error: "sidecar has no delete handler",
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
let result;
|
|
194
|
+
try {
|
|
195
|
+
result = await callbacks.onDeleteSession({ sessionKey: frame.sessionKey });
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
199
|
+
errLog(TAG, "onDeleteSession handler threw:", msg);
|
|
200
|
+
sendRaw({
|
|
201
|
+
type: "delete_session_ack",
|
|
202
|
+
sessionKey: frame.sessionKey,
|
|
203
|
+
success: false,
|
|
204
|
+
error: `handler threw: ${msg}`,
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
sendRaw({
|
|
209
|
+
type: "delete_session_ack",
|
|
210
|
+
sessionKey: frame.sessionKey,
|
|
211
|
+
success: result.success,
|
|
212
|
+
error: result.error,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Inbound fetch_history — app asks us to restore a chat's past messages
|
|
217
|
+
* from disk (after it cleared app data / re-paired). We read+parse the
|
|
218
|
+
* trajectory and reply with a `history` frame. We always reply (even on
|
|
219
|
+
* error / no handler) so the app's backfill pump can advance.
|
|
220
|
+
*/
|
|
221
|
+
async function handleFetchHistory(frame) {
|
|
222
|
+
log(TAG, `inbound fetch_history sessionKey=${frame.sessionKey}`);
|
|
223
|
+
if (!callbacks.onFetchHistory) {
|
|
224
|
+
warn(TAG, "fetch_history received but no onFetchHistory handler registered");
|
|
225
|
+
sendRaw({
|
|
226
|
+
type: "history",
|
|
227
|
+
sessionKey: frame.sessionKey,
|
|
228
|
+
messages: [],
|
|
229
|
+
error: "sidecar has no history handler",
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
let result;
|
|
234
|
+
try {
|
|
235
|
+
result = await callbacks.onFetchHistory({ sessionKey: frame.sessionKey });
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
239
|
+
errLog(TAG, "onFetchHistory handler threw:", msg);
|
|
240
|
+
sendRaw({
|
|
241
|
+
type: "history",
|
|
242
|
+
sessionKey: frame.sessionKey,
|
|
243
|
+
messages: [],
|
|
244
|
+
error: `handler threw: ${msg}`,
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
log(TAG, `responding history sessionKey=${frame.sessionKey} count=${result.messages.length} error=${result.error ?? "(none)"}`);
|
|
249
|
+
sendRaw({
|
|
250
|
+
type: "history",
|
|
251
|
+
sessionKey: frame.sessionKey,
|
|
252
|
+
messages: result.messages,
|
|
253
|
+
error: result.error,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function startPing() {
|
|
257
|
+
if (pingTimer)
|
|
258
|
+
return;
|
|
259
|
+
pingTimer = setInterval(() => {
|
|
260
|
+
sendRaw({ type: "ping", t: Date.now() });
|
|
261
|
+
}, pingIntervalMs);
|
|
262
|
+
}
|
|
263
|
+
function connect() {
|
|
264
|
+
if (stopped)
|
|
265
|
+
return;
|
|
266
|
+
transition("connecting");
|
|
267
|
+
log(TAG, `connecting to ${redact(options.relayUrl)}`);
|
|
268
|
+
let ws;
|
|
269
|
+
try {
|
|
270
|
+
ws = socketFactory(options.relayUrl);
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
errLog(TAG, "socket factory threw:", e);
|
|
274
|
+
scheduleReconnect("factory-throw");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
socket = ws;
|
|
278
|
+
ws.on("open", () => {
|
|
279
|
+
log(TAG, "socket open — sending hello");
|
|
280
|
+
sendRaw({
|
|
281
|
+
type: "hello",
|
|
282
|
+
version: PROTOCOL_VERSION,
|
|
283
|
+
role: "plugin",
|
|
284
|
+
deviceToken: options.deviceToken,
|
|
285
|
+
lastAckSeq,
|
|
286
|
+
client: {
|
|
287
|
+
sidecarVersion: options.sidecarVersion,
|
|
288
|
+
nodeVersion: process.version,
|
|
289
|
+
platform: process.platform,
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
ws.on("message", (data) => {
|
|
294
|
+
let parsed;
|
|
295
|
+
try {
|
|
296
|
+
parsed = JSON.parse(data.toString());
|
|
297
|
+
}
|
|
298
|
+
catch (e) {
|
|
299
|
+
warn(TAG, "bad inbound JSON:", e);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
handleServerFrame(parsed);
|
|
303
|
+
});
|
|
304
|
+
ws.on("close", (code, reason) => {
|
|
305
|
+
const reasonStr = reason.toString() || `code=${code}`;
|
|
306
|
+
log(TAG, `socket closed code=${code} reason=${reasonStr}`);
|
|
307
|
+
clearTimers();
|
|
308
|
+
socket = null;
|
|
309
|
+
if (stopped) {
|
|
310
|
+
transition("closed");
|
|
311
|
+
callbacks.onDisconnected({ fatal: false, reason: "stopped" });
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
callbacks.onDisconnected({ fatal: false, reason: reasonStr });
|
|
315
|
+
scheduleReconnect(reasonStr);
|
|
316
|
+
});
|
|
317
|
+
ws.on("error", (e) => {
|
|
318
|
+
warn(TAG, "socket error:", e.message);
|
|
319
|
+
// 'close' will follow; reconnect happens there.
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// Kick off initial connection.
|
|
323
|
+
connect();
|
|
324
|
+
return {
|
|
325
|
+
async sendAgentReply(partial) {
|
|
326
|
+
const frame = { type: "agent_reply", ...partial };
|
|
327
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
328
|
+
if (!sendRaw(frame))
|
|
329
|
+
outboundQueue.push(frame);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// Buffer until reconnect — keeps streaming chunks ordered.
|
|
333
|
+
outboundQueue.push(frame);
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
state() {
|
|
337
|
+
return state;
|
|
338
|
+
},
|
|
339
|
+
async close(reason = "explicit") {
|
|
340
|
+
log(TAG, "close requested reason=", reason);
|
|
341
|
+
stopped = true;
|
|
342
|
+
clearTimers();
|
|
343
|
+
if (socket) {
|
|
344
|
+
try {
|
|
345
|
+
socket.close(1000, reason);
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
/* ignore */
|
|
349
|
+
}
|
|
350
|
+
socket = null;
|
|
351
|
+
}
|
|
352
|
+
transition("closed");
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/** Redact a URL's userinfo+query for logging. */
|
|
357
|
+
function redact(url) {
|
|
358
|
+
try {
|
|
359
|
+
const u = new URL(url);
|
|
360
|
+
u.password = "";
|
|
361
|
+
u.username = "";
|
|
362
|
+
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
return url.split("?")[0] ?? url;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SidecarConfig } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_CONFIG_PATH: string;
|
|
3
|
+
export declare class ConfigError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function loadConfig(path?: string): SidecarConfig;
|
|
7
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,mBAAmB,QAAgD,CAAC;AAEjF,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAA4B,GAAG,aAAa,CAqB5E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Sidecar configuration loader.
|
|
2
|
+
//
|
|
3
|
+
// Reads ~/.earzbook/sidecar.json (or a path supplied via --config) and
|
|
4
|
+
// validates the shape before the adapter starts. Validation is strict
|
|
5
|
+
// because a corrupted config could cause the sidecar to dial a wrong
|
|
6
|
+
// relay, or worse, leak the deviceToken JWT to the wrong host.
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
export const DEFAULT_CONFIG_PATH = join(homedir(), ".earzbook", "openclaw.json");
|
|
11
|
+
export class ConfigError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "ConfigError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function loadConfig(path = DEFAULT_CONFIG_PATH) {
|
|
18
|
+
if (!existsSync(path)) {
|
|
19
|
+
throw new ConfigError(`config file not found at ${path} — run the Earzbook pair flow to provision`);
|
|
20
|
+
}
|
|
21
|
+
let raw;
|
|
22
|
+
try {
|
|
23
|
+
raw = readFileSync(path, "utf8");
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
27
|
+
throw new ConfigError(`failed to read config ${path}: ${msg}`);
|
|
28
|
+
}
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
35
|
+
throw new ConfigError(`config ${path} is not valid JSON: ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
return validate(parsed, path);
|
|
38
|
+
}
|
|
39
|
+
function validate(parsed, path) {
|
|
40
|
+
if (!parsed || typeof parsed !== "object") {
|
|
41
|
+
throw new ConfigError(`config ${path}: top-level must be an object`);
|
|
42
|
+
}
|
|
43
|
+
const o = parsed;
|
|
44
|
+
requireString(o, "relayUrl", path);
|
|
45
|
+
requireString(o, "deviceToken", path);
|
|
46
|
+
requireString(o, "userId", path);
|
|
47
|
+
requireString(o, "agentId", path);
|
|
48
|
+
requireString(o, "versionEndpoint", path);
|
|
49
|
+
const relayUrl = o.relayUrl;
|
|
50
|
+
if (!/^wss?:\/\//.test(relayUrl)) {
|
|
51
|
+
throw new ConfigError(`config ${path}: relayUrl must start with ws:// or wss:// (got ${relayUrl})`);
|
|
52
|
+
}
|
|
53
|
+
const versionEndpoint = o.versionEndpoint;
|
|
54
|
+
if (!/^https?:\/\//.test(versionEndpoint)) {
|
|
55
|
+
throw new ConfigError(`config ${path}: versionEndpoint must start with http:// or https://`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
relayUrl,
|
|
59
|
+
deviceToken: o.deviceToken,
|
|
60
|
+
userId: o.userId,
|
|
61
|
+
agentId: o.agentId,
|
|
62
|
+
versionEndpoint,
|
|
63
|
+
pingIntervalMs: optionalNumber(o, "pingIntervalMs"),
|
|
64
|
+
reconnectBaseMs: optionalNumber(o, "reconnectBaseMs"),
|
|
65
|
+
reconnectMaxMs: optionalNumber(o, "reconnectMaxMs"),
|
|
66
|
+
agentTimeoutMs: optionalNumber(o, "agentTimeoutMs"),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function requireString(o, key, path) {
|
|
70
|
+
const v = o[key];
|
|
71
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
72
|
+
throw new ConfigError(`config ${path}: missing or empty required field "${key}"`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function optionalNumber(o, key) {
|
|
76
|
+
const v = o[key];
|
|
77
|
+
if (v === undefined || v === null)
|
|
78
|
+
return undefined;
|
|
79
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0) {
|
|
80
|
+
throw new ConfigError(`config field "${key}" must be a non-negative number`);
|
|
81
|
+
}
|
|
82
|
+
return v;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,uEAAuE;AACvE,sEAAsE;AACtE,qEAAqE;AACrE,+DAA+D;AAE/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;AAEjF,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,mBAAmB;IAC3D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,WAAW,CACnB,4BAA4B,IAAI,4CAA4C,CAC7E,CAAC;IACJ,CAAC;IACD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,WAAW,CAAC,yBAAyB,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,WAAW,CAAC,UAAU,IAAI,uBAAuB,GAAG,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,QAAQ,CAAC,MAAe,EAAE,IAAY;IAC7C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,WAAW,CAAC,UAAU,IAAI,+BAA+B,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,CAAC,GAAG,MAAiC,CAAC;IAE5C,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACnC,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IACtC,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACjC,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAClC,aAAa,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAkB,CAAC;IACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,WAAW,CACnB,UAAU,IAAI,mDAAmD,QAAQ,GAAG,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,eAAe,GAAG,CAAC,CAAC,eAAyB,CAAC;IACpD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,WAAW,CACnB,UAAU,IAAI,uDAAuD,CACtE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,WAAW,EAAE,CAAC,CAAC,WAAqB;QACpC,MAAM,EAAE,CAAC,CAAC,MAAgB;QAC1B,OAAO,EAAE,CAAC,CAAC,OAAiB;QAC5B,eAAe;QACf,cAAc,EAAE,cAAc,CAAC,CAAC,EAAE,gBAAgB,CAAC;QACnD,eAAe,EAAE,cAAc,CAAC,CAAC,EAAE,iBAAiB,CAAC;QACrD,cAAc,EAAE,cAAc,CAAC,CAAC,EAAE,gBAAgB,CAAC;QACnD,cAAc,EAAE,cAAc,CAAC,CAAC,EAAE,gBAAgB,CAAC;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,CAA0B,EAAE,GAAW,EAAE,IAAY;IAC1E,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,UAAU,IAAI,sCAAsC,GAAG,GAAG,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACrB,CAA0B,EAC1B,GAAW;IAEX,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACjB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,WAAW,CAAC,iBAAiB,GAAG,iCAAiC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Build the final --session-key argv value. Pure function; safe to test.
|
|
4
|
+
* Returns the legacy constant when no per-frame sessionKey is given.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveSessionKey(perFrameSessionKey?: string): string;
|
|
7
|
+
export interface DispatchInput {
|
|
8
|
+
agentId: string;
|
|
9
|
+
text: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional per-message session key (v1 multi-session). When provided,
|
|
12
|
+
* the resolved key becomes `earzbook-app:<sessionKey>`. When omitted,
|
|
13
|
+
* the bare `earzbook-app` constant is used (legacy single-session).
|
|
14
|
+
*/
|
|
15
|
+
sessionKey?: string;
|
|
16
|
+
/** Timeout in ms. Hard-kill at this point. Default 120_000. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
/** Override for tests — defaults to the global child_process.spawn. */
|
|
19
|
+
spawnFn?: typeof spawn;
|
|
20
|
+
}
|
|
21
|
+
export type DispatchResult = {
|
|
22
|
+
ok: true;
|
|
23
|
+
replyText: string;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
userVisibleError: string;
|
|
28
|
+
durationMs: number;
|
|
29
|
+
};
|
|
30
|
+
/** Run one agent turn and return the reply text. */
|
|
31
|
+
export declare function dispatchToAgent(input: DispatchInput): Promise<DispatchResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Pull the reply text out of `openclaw agent --json` stdout.
|
|
34
|
+
*
|
|
35
|
+
* OpenClaw 2026.5.22 emits this shape on success (verified against a real
|
|
36
|
+
* run):
|
|
37
|
+
* {
|
|
38
|
+
* "payloads": [{"text": "...", "mediaUrl": null}],
|
|
39
|
+
* "meta": { ..., "finalAssistantVisibleText": "...", ... }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* `payloads[0].text` is the canonical reply text. `finalAssistantVisibleText`
|
|
43
|
+
* is a meta mirror — usually identical, but we treat it as a fallback.
|
|
44
|
+
* Older candidates (output/reply/text at top level) stay in the list so we
|
|
45
|
+
* don't break if OpenClaw changes the shape.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractReplyText(stdout: string): string;
|
|
48
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AA+B9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAoH7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA8DvD"}
|