@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/dist/index.js
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Earzbook ↔ OpenClaw sidecar entry.
|
|
3
|
+
//
|
|
4
|
+
// Invoked by launchd/systemd as `earzbook-openclaw --config <path>`.
|
|
5
|
+
//
|
|
6
|
+
// Flow:
|
|
7
|
+
// 1. Parse argv → resolve config path.
|
|
8
|
+
// 2. Acquire single-instance lock (exit cleanly if held).
|
|
9
|
+
// 3. Load + validate config.
|
|
10
|
+
// 4. Run a startup self-update check (force-update path may exit(0) so
|
|
11
|
+
// the supervisor relaunches with the new binary).
|
|
12
|
+
// 5. Start the WS adapter against the relay; on every inbound user_
|
|
13
|
+
// message, enqueue → spawn `openclaw agent` → emit agent_reply.
|
|
14
|
+
// 6. On SIGTERM/SIGINT, close cleanly. On fatal protocol errors, exit
|
|
15
|
+
// with code 75 (EX_TEMPFAIL) so supervisors throttle restarts.
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as os from "node:os";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { createAdapter } from "./adapter.js";
|
|
21
|
+
import { ConfigError, DEFAULT_CONFIG_PATH, loadConfig } from "./config.js";
|
|
22
|
+
import { dispatchToAgent } from "./dispatch.js";
|
|
23
|
+
import { acquireLock, LockHeldError } from "./lockfile.js";
|
|
24
|
+
import { log, warn, errLog, LOG_PATH } from "./logging.js";
|
|
25
|
+
import { createSelfUpdateRunner } from "./self-update.js";
|
|
26
|
+
/**
|
|
27
|
+
* OpenClaw stores sessions on disk like this (verified on real install):
|
|
28
|
+
*
|
|
29
|
+
* ~/.openclaw/agents/<agentId>/sessions/
|
|
30
|
+
* ├─ sessions.json ← manifest (THIS is the index)
|
|
31
|
+
* ├─ <uuid>.jsonl ← user-message log
|
|
32
|
+
* ├─ <uuid>.trajectory.jsonl ← full agent trajectory
|
|
33
|
+
* └─ <uuid>.trajectory-path.json ← pointer metadata
|
|
34
|
+
*
|
|
35
|
+
* `sessions.json` is a dict keyed by `agent:<agentId>:<sessionKey>` (the
|
|
36
|
+
* exact string we pass to `--session-key`). Each value carries the
|
|
37
|
+
* `sessionFile` absolute path which points to the `<uuid>.jsonl`. Both
|
|
38
|
+
* `.trajectory.jsonl` and `.trajectory-path.json` share the same `<uuid>`
|
|
39
|
+
* basename and live in the same directory.
|
|
40
|
+
*
|
|
41
|
+
* Earzbook sessions live under the namespace `earzbook-app:<sessionKey>`
|
|
42
|
+
* — when invoked with `--session-key earzbook-app:c_abc123` the manifest
|
|
43
|
+
* key becomes `agent:<agentId>:earzbook-app:c_abc123`. The helpers below
|
|
44
|
+
* filter to that namespace so we never touch the user's other OpenClaw
|
|
45
|
+
* sessions (e.g. `agent:main:main` from non-Earzbook use of the CLI).
|
|
46
|
+
*/
|
|
47
|
+
const EARZBOOK_SESSION_KEY_PREFIX = "earzbook-app:";
|
|
48
|
+
function openClawSessionsDir(agentId) {
|
|
49
|
+
return path.join(os.homedir(), ".openclaw", "agents", agentId, "sessions");
|
|
50
|
+
}
|
|
51
|
+
function openClawManifestPath(agentId) {
|
|
52
|
+
return path.join(openClawSessionsDir(agentId), "sessions.json");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read OpenClaw's sessions.json manifest. Returns an empty dict when
|
|
56
|
+
* the file is missing (fresh install) or unreadable (treat as "no
|
|
57
|
+
* sessions" — the most useful default).
|
|
58
|
+
*/
|
|
59
|
+
async function readOpenClawManifest(agentId) {
|
|
60
|
+
const filePath = openClawManifestPath(agentId);
|
|
61
|
+
try {
|
|
62
|
+
const raw = await fs.promises.readFile(filePath, "utf8");
|
|
63
|
+
const parsed = JSON.parse(raw);
|
|
64
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
65
|
+
return parsed;
|
|
66
|
+
}
|
|
67
|
+
warn(TAG, `manifest at ${filePath} is not an object — treating as empty`);
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
const errno = e?.code;
|
|
72
|
+
if (errno === "ENOENT") {
|
|
73
|
+
log(TAG, `manifest ${filePath} not present (fresh install) — empty list`);
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
errLog(TAG, `readOpenClawManifest failed (${filePath}):`, e);
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* List the session-keys currently on disk for our `earzbook-app:` namespace.
|
|
82
|
+
* Returns the BARE keys (prefix stripped) so the app can compare directly
|
|
83
|
+
* to its Watermelon `openclaw_session_key` column.
|
|
84
|
+
*/
|
|
85
|
+
async function listOpenClawSessions(agentId) {
|
|
86
|
+
const manifest = await readOpenClawManifest(agentId);
|
|
87
|
+
const expectedKeyPrefix = `agent:${agentId}:${EARZBOOK_SESSION_KEY_PREFIX}`;
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const manifestKey of Object.keys(manifest)) {
|
|
90
|
+
if (!manifestKey.startsWith(expectedKeyPrefix))
|
|
91
|
+
continue;
|
|
92
|
+
const bare = manifestKey.slice(expectedKeyPrefix.length);
|
|
93
|
+
if (bare.length === 0)
|
|
94
|
+
continue; // skip the legacy bare `earzbook-app` entry
|
|
95
|
+
out.push(bare);
|
|
96
|
+
}
|
|
97
|
+
log(TAG, `listOpenClawSessions agentId=${agentId} count=${out.length}`);
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Remove all on-disk artifacts for one session-key:
|
|
102
|
+
* 1. Resolve the manifest entry → get the file basename
|
|
103
|
+
* 2. Unlink <uuid>.jsonl, <uuid>.trajectory.jsonl, <uuid>.trajectory-path.json
|
|
104
|
+
* 3. Scrub the entry from sessions.json + write back
|
|
105
|
+
*
|
|
106
|
+
* Idempotent: missing files are treated as success (user already deleted
|
|
107
|
+
* them, or OpenClaw never created some). Returns success: true even
|
|
108
|
+
* when the manifest entry was absent — the desired end state ("nothing
|
|
109
|
+
* on disk for this key") is satisfied.
|
|
110
|
+
*/
|
|
111
|
+
async function removeOpenClawSession(opts) {
|
|
112
|
+
const manifestKey = `agent:${opts.agentId}:${EARZBOOK_SESSION_KEY_PREFIX}${opts.sessionKey}`;
|
|
113
|
+
const dir = openClawSessionsDir(opts.agentId);
|
|
114
|
+
const manifestPath = openClawManifestPath(opts.agentId);
|
|
115
|
+
const manifest = await readOpenClawManifest(opts.agentId);
|
|
116
|
+
const entry = manifest[manifestKey];
|
|
117
|
+
if (!entry) {
|
|
118
|
+
log(TAG, `removeOpenClawSession: no manifest entry for ${manifestKey} — treating as success`);
|
|
119
|
+
return { success: true };
|
|
120
|
+
}
|
|
121
|
+
// Determine the file basename. Prefer the manifest's sessionFile path
|
|
122
|
+
// (the absolute path to <uuid>.jsonl). Fall back to sessionId if path
|
|
123
|
+
// is missing (defensive — older manifest formats might omit it).
|
|
124
|
+
let basename = null;
|
|
125
|
+
if (typeof entry.sessionFile === "string" && entry.sessionFile) {
|
|
126
|
+
const fileName = path.basename(entry.sessionFile);
|
|
127
|
+
basename = fileName.replace(/\.jsonl$/, "");
|
|
128
|
+
}
|
|
129
|
+
else if (typeof entry.sessionId === "string" && entry.sessionId) {
|
|
130
|
+
basename = entry.sessionId;
|
|
131
|
+
}
|
|
132
|
+
let filesUnlinked = 0;
|
|
133
|
+
if (basename) {
|
|
134
|
+
const candidates = [
|
|
135
|
+
`${basename}.jsonl`,
|
|
136
|
+
`${basename}.trajectory.jsonl`,
|
|
137
|
+
`${basename}.trajectory-path.json`,
|
|
138
|
+
];
|
|
139
|
+
for (const fileName of candidates) {
|
|
140
|
+
const filePath = path.join(dir, fileName);
|
|
141
|
+
try {
|
|
142
|
+
await fs.promises.unlink(filePath);
|
|
143
|
+
filesUnlinked++;
|
|
144
|
+
log(TAG, `unlinked ${filePath}`);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
const errno = e?.code;
|
|
148
|
+
if (errno === "ENOENT") {
|
|
149
|
+
log(TAG, `file already gone ${filePath}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
153
|
+
errLog(TAG, `unlink failed ${filePath}: ${msg}`);
|
|
154
|
+
// Continue best-effort; manifest scrub below is still useful.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
warn(TAG, `removeOpenClawSession: no basename derivable for ${manifestKey} — skipping file unlink, will still scrub manifest`);
|
|
161
|
+
}
|
|
162
|
+
// Scrub the manifest entry + write back. Best-effort: any error here
|
|
163
|
+
// we surface as failure so the app can warn the user.
|
|
164
|
+
delete manifest[manifestKey];
|
|
165
|
+
try {
|
|
166
|
+
const tmp = `${manifestPath}.tmp`;
|
|
167
|
+
await fs.promises.writeFile(tmp, JSON.stringify(manifest, null, 2), "utf8");
|
|
168
|
+
await fs.promises.rename(tmp, manifestPath);
|
|
169
|
+
log(TAG, `removeOpenClawSession ok sessionKey=${opts.sessionKey} files=${filesUnlinked} manifestScrubbed=true`);
|
|
170
|
+
return { success: true };
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
174
|
+
errLog(TAG, `manifest write failed: ${msg}`);
|
|
175
|
+
return { success: false, error: `manifest update failed: ${msg}` };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Cap on how many messages we restore per session. Keeps the `history`
|
|
180
|
+
* wire frame bounded (a runaway trajectory can't blow up the WS payload)
|
|
181
|
+
* and the app-side batch write reasonable. We keep the MOST RECENT N.
|
|
182
|
+
*/
|
|
183
|
+
const HISTORY_MAX_MESSAGES = 500;
|
|
184
|
+
/**
|
|
185
|
+
* Read + parse one session's on-disk trajectory into displayable messages.
|
|
186
|
+
*
|
|
187
|
+
* Restores chats after the user cleared app data / reinstalled / re-paired:
|
|
188
|
+
* OpenClaw chat is local-only (no server copy), so this `.jsonl` is the
|
|
189
|
+
* only surviving record. We surface ONLY user + assistant visible text —
|
|
190
|
+
* thinking blocks, tool_use blocks, and toolResult turns are dropped so the
|
|
191
|
+
* restored conversation matches what the user originally saw in-app.
|
|
192
|
+
*
|
|
193
|
+
* Path resolution mirrors removeOpenClawSession(): manifest key
|
|
194
|
+
* `agent:<agentId>:earzbook-app:<sessionKey>` → entry.sessionFile.
|
|
195
|
+
*
|
|
196
|
+
* Returns {messages:[]} (no error) when the manifest references a file that
|
|
197
|
+
* has since been deleted — a stale entry, not a failure.
|
|
198
|
+
*/
|
|
199
|
+
async function readOpenClawSessionHistory(opts) {
|
|
200
|
+
const maxMessages = opts.maxMessages ?? HISTORY_MAX_MESSAGES;
|
|
201
|
+
const manifestKey = `agent:${opts.agentId}:${EARZBOOK_SESSION_KEY_PREFIX}${opts.sessionKey}`;
|
|
202
|
+
const dir = openClawSessionsDir(opts.agentId);
|
|
203
|
+
const manifest = await readOpenClawManifest(opts.agentId);
|
|
204
|
+
const entry = manifest[manifestKey];
|
|
205
|
+
if (!entry) {
|
|
206
|
+
log(TAG, `readOpenClawSessionHistory: no manifest entry for ${manifestKey}`);
|
|
207
|
+
return { messages: [], error: "no manifest entry for session" };
|
|
208
|
+
}
|
|
209
|
+
// Resolve the trajectory .jsonl path (same precedence as removeOpenClawSession).
|
|
210
|
+
let filePath = null;
|
|
211
|
+
if (typeof entry.sessionFile === "string" && entry.sessionFile) {
|
|
212
|
+
filePath = entry.sessionFile;
|
|
213
|
+
}
|
|
214
|
+
else if (typeof entry.sessionId === "string" && entry.sessionId) {
|
|
215
|
+
filePath = path.join(dir, `${entry.sessionId}.jsonl`);
|
|
216
|
+
}
|
|
217
|
+
if (!filePath) {
|
|
218
|
+
warn(TAG, `readOpenClawSessionHistory: no sessionFile for ${manifestKey}`);
|
|
219
|
+
return { messages: [], error: "session file path unresolved" };
|
|
220
|
+
}
|
|
221
|
+
let raw;
|
|
222
|
+
try {
|
|
223
|
+
raw = await fs.promises.readFile(filePath, "utf8");
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
const errno = e?.code;
|
|
227
|
+
if (errno === "ENOENT") {
|
|
228
|
+
log(TAG, `readOpenClawSessionHistory: file gone ${filePath} — empty (stale manifest)`);
|
|
229
|
+
return { messages: [] };
|
|
230
|
+
}
|
|
231
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
232
|
+
errLog(TAG, `readOpenClawSessionHistory read failed ${filePath}: ${msg}`);
|
|
233
|
+
return { messages: [], error: `read failed: ${msg}` };
|
|
234
|
+
}
|
|
235
|
+
// Parse newline-delimited JSON. Preserve on-disk (chronological) order —
|
|
236
|
+
// do NOT re-sort by timestamp (timestamps can be absent or duplicated).
|
|
237
|
+
const lines = raw.split("\n");
|
|
238
|
+
let rawLines = 0;
|
|
239
|
+
const messages = [];
|
|
240
|
+
for (const line of lines) {
|
|
241
|
+
const trimmed = line.trim();
|
|
242
|
+
if (trimmed.length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
rawLines++;
|
|
245
|
+
let obj;
|
|
246
|
+
try {
|
|
247
|
+
obj = JSON.parse(trimmed);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
continue; // skip malformed lines (partial writes etc.)
|
|
251
|
+
}
|
|
252
|
+
if (obj.type !== "message")
|
|
253
|
+
continue; // skip session/model_change/thinking/custom
|
|
254
|
+
const message = obj.message;
|
|
255
|
+
if (!message || typeof message !== "object")
|
|
256
|
+
continue;
|
|
257
|
+
const role = message.role;
|
|
258
|
+
if (role !== "user" && role !== "assistant")
|
|
259
|
+
continue; // drop toolResult etc.
|
|
260
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
261
|
+
let text;
|
|
262
|
+
if (role === "user") {
|
|
263
|
+
// Every user content block is visible text.
|
|
264
|
+
text = content
|
|
265
|
+
.map(b => (b && typeof b === "object" ? b.text : undefined))
|
|
266
|
+
.filter((t) => typeof t === "string")
|
|
267
|
+
.join("");
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// Assistant: keep ONLY type:"text" blocks (drop thinking / tool_use).
|
|
271
|
+
text = content
|
|
272
|
+
.filter(b => b !== null &&
|
|
273
|
+
typeof b === "object" &&
|
|
274
|
+
b.type === "text" &&
|
|
275
|
+
typeof b.text === "string")
|
|
276
|
+
.map(b => b.text)
|
|
277
|
+
.join("");
|
|
278
|
+
}
|
|
279
|
+
if (text.trim().length === 0)
|
|
280
|
+
continue; // skip empty / pure-tool turns
|
|
281
|
+
const at = Number(message.timestamp);
|
|
282
|
+
messages.push({ role, text, at: Number.isFinite(at) ? at : 0 });
|
|
283
|
+
}
|
|
284
|
+
const capped = messages.length > maxMessages;
|
|
285
|
+
const out = capped ? messages.slice(-maxMessages) : messages;
|
|
286
|
+
log(TAG, `readOpenClawSessionHistory sessionKey=${opts.sessionKey} rawLines=${rawLines} kept=${out.length} capped=${capped}`);
|
|
287
|
+
return { messages: out };
|
|
288
|
+
}
|
|
289
|
+
const TAG = "[earzbook-sidecar]";
|
|
290
|
+
// Read our own version from package.json (resolved relative to dist/).
|
|
291
|
+
const require = createRequire(import.meta.url);
|
|
292
|
+
const pkg = require("../package.json");
|
|
293
|
+
const SIDECAR_VERSION = pkg.version;
|
|
294
|
+
function parseArgs(argv) {
|
|
295
|
+
const out = {
|
|
296
|
+
configPath: DEFAULT_CONFIG_PATH,
|
|
297
|
+
showHelp: false,
|
|
298
|
+
showVersion: false,
|
|
299
|
+
};
|
|
300
|
+
for (let i = 2; i < argv.length; i++) {
|
|
301
|
+
const a = argv[i];
|
|
302
|
+
if (a === "--config" || a === "-c") {
|
|
303
|
+
const next = argv[++i];
|
|
304
|
+
if (!next)
|
|
305
|
+
throw new Error("--config requires a path argument");
|
|
306
|
+
out.configPath = next;
|
|
307
|
+
}
|
|
308
|
+
else if (a === "--help" || a === "-h") {
|
|
309
|
+
out.showHelp = true;
|
|
310
|
+
}
|
|
311
|
+
else if (a === "--version" || a === "-v") {
|
|
312
|
+
out.showVersion = true;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
throw new Error(`unknown argument: ${a}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
function printHelp() {
|
|
321
|
+
process.stdout.write(`earzbook-openclaw v${SIDECAR_VERSION}\n\n` +
|
|
322
|
+
`Earzbook OpenClaw daemon. Bridges the Earzbook mobile app to a\n` +
|
|
323
|
+
`locally-running OpenClaw agent via a WebSocket relay.\n\n` +
|
|
324
|
+
`Usage:\n` +
|
|
325
|
+
` earzbook-openclaw --config <path>\n\n` +
|
|
326
|
+
`Options:\n` +
|
|
327
|
+
` -c, --config <path> Path to config JSON (default: ${DEFAULT_CONFIG_PATH})\n` +
|
|
328
|
+
` -v, --version Print version and exit\n` +
|
|
329
|
+
` -h, --help Show this help and exit\n\n` +
|
|
330
|
+
`Logs: ${LOG_PATH}\n` +
|
|
331
|
+
`Set EARZBOOK_SIDECAR_DEBUG=1 for verbose logs.\n`);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Sequential inbound dispatcher. Inbound messages are enqueued in arrival
|
|
335
|
+
* order; the worker drains the queue one at a time, spawning the agent
|
|
336
|
+
* per item. This protects OpenClaw's single-session model from concurrent
|
|
337
|
+
* `openclaw agent` invocations.
|
|
338
|
+
*/
|
|
339
|
+
function createDispatchQueue(opts) {
|
|
340
|
+
const queue = [];
|
|
341
|
+
let running = false;
|
|
342
|
+
async function pump() {
|
|
343
|
+
if (running)
|
|
344
|
+
return;
|
|
345
|
+
running = true;
|
|
346
|
+
try {
|
|
347
|
+
while (queue.length > 0) {
|
|
348
|
+
const item = queue.shift();
|
|
349
|
+
const result = await dispatchToAgent({
|
|
350
|
+
agentId: opts.cfg.agentId,
|
|
351
|
+
text: item.text,
|
|
352
|
+
// v1 multi-session: per-frame sessionKey isolates each chat into
|
|
353
|
+
// its own OpenClaw trajectory file. When absent (legacy app
|
|
354
|
+
// versions), dispatch falls back to the bare `earzbook-app` key.
|
|
355
|
+
sessionKey: item.sessionKey,
|
|
356
|
+
timeoutMs: opts.cfg.agentTimeoutMs,
|
|
357
|
+
});
|
|
358
|
+
const replyText = result.ok ? result.replyText : `⚠️ ${result.userVisibleError}`;
|
|
359
|
+
try {
|
|
360
|
+
await opts.adapter().sendAgentReply({
|
|
361
|
+
conversationId: item.conversationId,
|
|
362
|
+
chunkSeq: 0,
|
|
363
|
+
text: replyText,
|
|
364
|
+
isFinal: true,
|
|
365
|
+
inReplyTo: item.clientMessageId,
|
|
366
|
+
// Echo sessionKey so the app can persist this reply under
|
|
367
|
+
// the correct chat-scoped Conversation row (v2 flat sessions).
|
|
368
|
+
sessionKey: item.sessionKey,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
errLog(TAG, "sendAgentReply threw:", e);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
running = false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return (envelope) => {
|
|
381
|
+
queue.push(envelope);
|
|
382
|
+
void pump();
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Validate the bare sessionKey portion (after the `earzbook-app:`
|
|
387
|
+
* namespace prefix) before it touches the filesystem layer. Only
|
|
388
|
+
* UUID-ish characters allowed — prevents path traversal and matches
|
|
389
|
+
* what the app generates via generateClientMessageId().
|
|
390
|
+
*/
|
|
391
|
+
const SESSION_KEY_REGEX = /^[a-zA-Z0-9_-]+$/;
|
|
392
|
+
/**
|
|
393
|
+
* Handle a delete_session request from the app — wipe all on-disk
|
|
394
|
+
* artifacts for the chat (sessions.json entry + 3 file unlinks) using
|
|
395
|
+
* the OpenClaw-native manifest format we discovered.
|
|
396
|
+
*/
|
|
397
|
+
async function handleDeleteSession(opts) {
|
|
398
|
+
if (!SESSION_KEY_REGEX.test(opts.sessionKey)) {
|
|
399
|
+
warn(TAG, `delete_session rejected — sessionKey failed regex: ${opts.sessionKey}`);
|
|
400
|
+
return { success: false, error: "invalid sessionKey format" };
|
|
401
|
+
}
|
|
402
|
+
log(TAG, `delete_session sessionKey=${opts.sessionKey}`);
|
|
403
|
+
return removeOpenClawSession({
|
|
404
|
+
agentId: opts.agentId,
|
|
405
|
+
sessionKey: opts.sessionKey,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Handle a fetch_history request from the app — read the chat's on-disk
|
|
410
|
+
* trajectory and return its user+assistant messages so the app can restore
|
|
411
|
+
* a conversation it lost (cleared app data / reinstall / re-pair). Same
|
|
412
|
+
* path-traversal guard as delete_session before any filesystem access.
|
|
413
|
+
*/
|
|
414
|
+
async function handleFetchHistory(opts) {
|
|
415
|
+
if (!SESSION_KEY_REGEX.test(opts.sessionKey)) {
|
|
416
|
+
warn(TAG, `fetch_history rejected — sessionKey failed regex: ${opts.sessionKey}`);
|
|
417
|
+
return { messages: [], error: "invalid sessionKey format" };
|
|
418
|
+
}
|
|
419
|
+
log(TAG, `fetch_history sessionKey=${opts.sessionKey}`);
|
|
420
|
+
return readOpenClawSessionHistory({
|
|
421
|
+
agentId: opts.agentId,
|
|
422
|
+
sessionKey: opts.sessionKey,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async function main() {
|
|
426
|
+
// 1. argv parse — done before we touch the filesystem so --help / --version
|
|
427
|
+
// work even if the config is missing.
|
|
428
|
+
let args;
|
|
429
|
+
try {
|
|
430
|
+
args = parseArgs(process.argv);
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
434
|
+
process.stderr.write(`earzbook-openclaw: ${msg}\nRun with --help for usage.\n`);
|
|
435
|
+
process.exit(64); // EX_USAGE
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (args.showHelp) {
|
|
439
|
+
printHelp();
|
|
440
|
+
process.exit(0);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (args.showVersion) {
|
|
444
|
+
process.stdout.write(`${SIDECAR_VERSION}\n`);
|
|
445
|
+
process.exit(0);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
log(TAG, `starting earzbook-openclaw v${SIDECAR_VERSION} pid=${process.pid}`);
|
|
449
|
+
// 2. Lock — prevent duplicate sidecars.
|
|
450
|
+
let lock;
|
|
451
|
+
try {
|
|
452
|
+
lock = acquireLock();
|
|
453
|
+
}
|
|
454
|
+
catch (e) {
|
|
455
|
+
if (e instanceof LockHeldError) {
|
|
456
|
+
errLog(TAG, e.message);
|
|
457
|
+
process.exit(0); // not an error — another sidecar is running. Exit cleanly.
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
errLog(TAG, "lock acquire failed:", e);
|
|
461
|
+
process.exit(75); // EX_TEMPFAIL
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
// 3. Config.
|
|
465
|
+
let cfg;
|
|
466
|
+
try {
|
|
467
|
+
cfg = loadConfig(args.configPath);
|
|
468
|
+
}
|
|
469
|
+
catch (e) {
|
|
470
|
+
if (e instanceof ConfigError) {
|
|
471
|
+
errLog(TAG, e.message);
|
|
472
|
+
// Bad config is operator error — exit with a stable code so the
|
|
473
|
+
// supervisor doesn't tight-loop trying to start us.
|
|
474
|
+
lock.release();
|
|
475
|
+
process.exit(78); // EX_CONFIG
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
throw e;
|
|
479
|
+
}
|
|
480
|
+
log(TAG, `config loaded relayUrl=${cfg.relayUrl} userId=${cfg.userId} agentId=${cfg.agentId}`);
|
|
481
|
+
// 4. Startup self-update check.
|
|
482
|
+
const updater = createSelfUpdateRunner({
|
|
483
|
+
versionEndpoint: cfg.versionEndpoint,
|
|
484
|
+
installedVersion: SIDECAR_VERSION,
|
|
485
|
+
});
|
|
486
|
+
try {
|
|
487
|
+
const result = await updater.checkNow();
|
|
488
|
+
if (result.action === "force-update") {
|
|
489
|
+
log(TAG, `force-update to ${result.targetVersion} complete — exiting for supervisor relaunch`);
|
|
490
|
+
lock.release();
|
|
491
|
+
process.exit(0);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (result.action === "soft-update") {
|
|
495
|
+
log(TAG, `soft-updated to ${result.targetVersion} — continuing on current process until next restart`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (e) {
|
|
499
|
+
warn(TAG, "startup update check failed (non-fatal):", e);
|
|
500
|
+
}
|
|
501
|
+
updater.startDailyTimer();
|
|
502
|
+
// 5. Adapter + dispatch queue.
|
|
503
|
+
let adapter = null;
|
|
504
|
+
const adapterGetter = () => {
|
|
505
|
+
if (!adapter)
|
|
506
|
+
throw new Error("adapter not yet created");
|
|
507
|
+
return adapter;
|
|
508
|
+
};
|
|
509
|
+
const enqueue = createDispatchQueue({ cfg, adapter: adapterGetter });
|
|
510
|
+
adapter = createAdapter({
|
|
511
|
+
relayUrl: cfg.relayUrl,
|
|
512
|
+
deviceToken: cfg.deviceToken,
|
|
513
|
+
sidecarVersion: SIDECAR_VERSION,
|
|
514
|
+
pingIntervalMs: cfg.pingIntervalMs,
|
|
515
|
+
reconnectBaseMs: cfg.reconnectBaseMs,
|
|
516
|
+
reconnectMaxMs: cfg.reconnectMaxMs,
|
|
517
|
+
}, {
|
|
518
|
+
onMessage: enqueue,
|
|
519
|
+
onConnected(welcome) {
|
|
520
|
+
log(TAG, `relay welcome userId=${welcome.userId} serverTime=${welcome.serverTime}`);
|
|
521
|
+
},
|
|
522
|
+
onDisconnected(info) {
|
|
523
|
+
log(TAG, `relay disconnected fatal=${info.fatal} reason=${info.reason}`);
|
|
524
|
+
if (info.fatal) {
|
|
525
|
+
// Server told us our token is bad / device revoked. Exit so the
|
|
526
|
+
// supervisor's throttling kicks in instead of tight-reconnecting.
|
|
527
|
+
errLog(TAG, "fatal protocol error — exiting for backoff restart");
|
|
528
|
+
updater.stop();
|
|
529
|
+
lock.release();
|
|
530
|
+
process.exit(75); // EX_TEMPFAIL — restartable but slow down
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
onListSessions: () => listOpenClawSessions(cfg.agentId),
|
|
534
|
+
onDeleteSession: frame => handleDeleteSession({
|
|
535
|
+
sessionKey: frame.sessionKey,
|
|
536
|
+
agentId: cfg.agentId,
|
|
537
|
+
}),
|
|
538
|
+
onFetchHistory: frame => handleFetchHistory({
|
|
539
|
+
sessionKey: frame.sessionKey,
|
|
540
|
+
agentId: cfg.agentId,
|
|
541
|
+
}),
|
|
542
|
+
});
|
|
543
|
+
// 6. Signal handling.
|
|
544
|
+
const shutdown = (signal) => {
|
|
545
|
+
log(TAG, `received ${signal} — shutting down`);
|
|
546
|
+
updater.stop();
|
|
547
|
+
if (adapter) {
|
|
548
|
+
void adapter.close(`signal:${signal}`).then(() => {
|
|
549
|
+
lock.release();
|
|
550
|
+
process.exit(0);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
lock.release();
|
|
555
|
+
process.exit(0);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
process.once("SIGTERM", () => shutdown("SIGTERM"));
|
|
559
|
+
process.once("SIGINT", () => shutdown("SIGINT"));
|
|
560
|
+
// 7. Hold the process alive until a signal. Adapter and timers keep
|
|
561
|
+
// Node's event loop spinning; this empty interval is belt-and-suspenders.
|
|
562
|
+
setInterval(() => {
|
|
563
|
+
/* keep-alive */
|
|
564
|
+
}, 60_000).unref();
|
|
565
|
+
log(TAG, "ready");
|
|
566
|
+
}
|
|
567
|
+
main().catch(e => {
|
|
568
|
+
errLog(TAG, "fatal:", e);
|
|
569
|
+
process.exit(70); // EX_SOFTWARE
|
|
570
|
+
});
|
|
571
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class LockHeldError extends Error {
|
|
2
|
+
constructor(path: string);
|
|
3
|
+
}
|
|
4
|
+
interface LockHandle {
|
|
5
|
+
fd: number;
|
|
6
|
+
release(): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Acquire an exclusive lock on the sidecar lockfile. Throws LockHeldError
|
|
10
|
+
* if another process already holds it.
|
|
11
|
+
*
|
|
12
|
+
* We try to use proper-lockfile semantics via flock if available, but Node
|
|
13
|
+
* lacks a stable flock binding in the stdlib. The fallback is O_CREAT |
|
|
14
|
+
* O_EXCL: if the file exists, we treat that as held UNLESS the recorded
|
|
15
|
+
* pid is no longer running (stale lock → take it over).
|
|
16
|
+
*/
|
|
17
|
+
export declare function acquireLock(): LockHandle;
|
|
18
|
+
export declare const LOCK_FILE_PATH: string;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=lockfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../src/lockfile.ts"],"names":[],"mappings":"AA2BA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,IAAI,EAAE,MAAM;CAIzB;AAED,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAsCxC;AAkCD,eAAO,MAAM,cAAc,QAAY,CAAC"}
|