aibroker 0.7.13 → 0.8.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.
@@ -0,0 +1,597 @@
1
+ /**
2
+ * daemon/sessions.ts — `aibroker sessions <cmd>`.
3
+ *
4
+ * Snapshot / restore / checkpoint the set of open Claude Code sessions so they
5
+ * survive a reboot. A reboot kills sessions without an `/exit`, so:
6
+ * 1. `checkpoint` (before a planned reboot) tells each session to persist state,
7
+ * 2. `snapshot` (auto, every 5 min) records each open session's name + directory,
8
+ * 3. `restore` (after login) reopens each in its own iTerm2 tab and runs `go`.
9
+ *
10
+ * THE MANIFEST IS A REGISTRY, NOT A MIRROR.
11
+ * An earlier version rewrote the manifest with exactly the sessions that were open
12
+ * at snapshot time. That destroys itself at the worst possible moment: shutting
13
+ * down is precisely when the live set drains to zero, so the 5-minute agent would
14
+ * happily persist `[]` over a good restore list. (Observed: five consecutive
15
+ * "Snapshotted 0 session(s)" while the user was /exit-ing tabs by hand.)
16
+ *
17
+ * So: snapshot MERGES by cwd and stamps `lastSeen`. A session that disappears
18
+ * stays in the manifest — that's the whole point of a restore list. Entries leave
19
+ * only by explicit `forget` or by `prune` on age. Every write keeps a `.bak`.
20
+ *
21
+ * Subcommands:
22
+ * snapshot merge open sessions -> manifest
23
+ * restore [--dry-run] [--only NAME] reopen every session in a new tab
24
+ * checkpoint [--message M] [--only NAME] [--dry-run] [--timeout S]
25
+ * ask each to save state, VERIFIED
26
+ * list [--verbose] show the manifest
27
+ * forget NAME drop an entry
28
+ * prune [--older-than DAYS] [--dry-run] drop entries not seen in a while
29
+ * install | uninstall manage the 5-min snapshot LaunchAgent
30
+ */
31
+ import { execFileSync, execSync } from "node:child_process";
32
+ import { writeFileSync, readFileSync, existsSync, mkdirSync, copyFileSync } from "node:fs";
33
+ import { homedir } from "node:os";
34
+ import { join, basename, dirname } from "node:path";
35
+ import { createHash } from "node:crypto";
36
+ import { WatcherClient } from "../ipc/client.js";
37
+ import { captureSession, typeIntoSession } from "../transport/sync-facade.js";
38
+ import { DAEMON_SOCKET_PATH } from "./index.js";
39
+ const HOME = homedir();
40
+ const AIBROKER_DIR = join(HOME, ".aibroker");
41
+ const MANIFEST = join(AIBROKER_DIR, "session-restore.json");
42
+ const MANIFEST_BAK = `${MANIFEST}.bak`;
43
+ const AGENT_LABEL = "com.aibroker.sessions-snapshot";
44
+ const AGENT_PLIST = join(HOME, "Library", "LaunchAgents", `${AGENT_LABEL}.plist`);
45
+ /** Entries unseen this long are prune candidates. Never auto-pruned. */
46
+ const DEFAULT_PRUNE_DAYS = 30;
47
+ // ── manifest I/O ────────────────────────────────────────────────────────────
48
+ function readManifest() {
49
+ if (!existsSync(MANIFEST))
50
+ return { version: 2, entries: [] };
51
+ let raw;
52
+ try {
53
+ raw = JSON.parse(readFileSync(MANIFEST, "utf-8"));
54
+ }
55
+ catch (err) {
56
+ // A corrupt manifest must never look like an empty one — that would let the
57
+ // next snapshot "merge" into nothing and silently discard the restore list.
58
+ throw new Error(`Manifest at ${MANIFEST} is not valid JSON (${err.message}). ` +
59
+ `Fix it or restore from ${MANIFEST_BAK}; refusing to continue.`);
60
+ }
61
+ if (Array.isArray(raw))
62
+ return { version: 2, entries: raw }; // v1
63
+ const m = raw;
64
+ return { version: 2, entries: Array.isArray(m.entries) ? m.entries : [] };
65
+ }
66
+ /** Write with a .bak of the previous content. The only path that touches the file. */
67
+ function writeManifest(entries) {
68
+ mkdirSync(AIBROKER_DIR, { recursive: true });
69
+ if (existsSync(MANIFEST)) {
70
+ try {
71
+ copyFileSync(MANIFEST, MANIFEST_BAK);
72
+ }
73
+ catch { /* best effort */ }
74
+ }
75
+ const payload = { version: 2, entries };
76
+ writeFileSync(MANIFEST, JSON.stringify(payload, null, 2) + "\n");
77
+ }
78
+ async function liveSessions() {
79
+ // null (not []) on failure — the caller MUST distinguish "daemon unreachable"
80
+ // from "genuinely nothing open", or an outage looks like a clean desk.
81
+ try {
82
+ const res = await new WatcherClient(DAEMON_SOCKET_PATH).call_raw("sessions", {});
83
+ const list = res.sessions;
84
+ return Array.isArray(list) ? list : null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ /** id -> tty for every open iTerm2 session (";"-delimited to dodge newline issues). */
91
+ function ttyMap() {
92
+ const script = `tell application "iTerm2"
93
+ set out to ""
94
+ repeat with w in windows
95
+ repeat with t in tabs of w
96
+ repeat with s in sessions of t
97
+ set out to out & (id of s) & "|" & (tty of s) & ";"
98
+ end repeat
99
+ end repeat
100
+ end repeat
101
+ return out
102
+ end tell`;
103
+ const raw = execFileSync("osascript", ["-e", script], { encoding: "utf-8" });
104
+ const m = {};
105
+ for (const pair of raw.split(";")) {
106
+ const [id, tty] = pair.split("|");
107
+ if (id && tty)
108
+ m[id.trim()] = tty.trim();
109
+ }
110
+ return m;
111
+ }
112
+ /** tty -> cwd of the `claude` process (authoritative; $HOME is valid). */
113
+ function cwdForTty(tty) {
114
+ const base = tty.replace("/dev/", "");
115
+ let out;
116
+ try {
117
+ out = execSync(`ps -t ${base} -o pid=,comm=`, { encoding: "utf-8" });
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ const procs = [];
123
+ for (const line of out.split("\n")) {
124
+ const m = line.trim().match(/^(\d+)\s+(.*)$/);
125
+ if (m)
126
+ procs.push({ pid: m[1], comm: m[2] });
127
+ }
128
+ const getCwd = (pid) => {
129
+ try {
130
+ return execSync(`lsof -a -d cwd -p ${pid} -Fn 2>/dev/null`, { encoding: "utf-8" })
131
+ .split("\n").find((l) => l.startsWith("n"))?.slice(1) || null;
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ };
137
+ const claudeProc = procs.find((p) => /(^|\/)claude$/.test(p.comm));
138
+ if (claudeProc) {
139
+ const cwd = getCwd(claudeProc.pid);
140
+ if (cwd && cwd.startsWith("/"))
141
+ return cwd;
142
+ }
143
+ for (const p of procs) {
144
+ const cwd = getCwd(p.pid);
145
+ if (cwd && cwd !== HOME && cwd.startsWith("/") && !cwd.startsWith(join(HOME, ".claude")))
146
+ return cwd;
147
+ }
148
+ return null;
149
+ }
150
+ /** PAI-style launch: --name label + advance-entered "/Name <name>\ngo". */
151
+ function launchCmd(cwd, name) {
152
+ const sq = (s) => `'${s.replace(/'/g, "'\\''")}'`;
153
+ const ansiC = name.replace(/'/g, "");
154
+ const prompt = `$'/Name ${ansiC}\\\\ngo'`; // \\n survives AppleScript literal -> \n -> zsh newline
155
+ return `cd ${sq(cwd)} && claude --name ${sq(name)} --dangerously-skip-permissions ${prompt}`;
156
+ }
157
+ function osascript(osa) { execFileSync("osascript", ["-e", osa]); }
158
+ function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
159
+ function hash(s) { return createHash("sha1").update(s).digest("hex"); }
160
+ /**
161
+ * Merge identity for an entry.
162
+ *
163
+ * NOT cwd alone. Several distinct sessions legitimately share a directory —
164
+ * "Home" and "Solar" both live in $HOME, "Jobs Matthias" has numbered siblings —
165
+ * and keying on cwd silently collapses them, so restore reopens one and drops
166
+ * the rest. Losing a session is worse than the cost of this choice: renaming a
167
+ * session leaves its old name behind as a twin, which `prune` ages out and
168
+ * `forget` removes on demand.
169
+ */
170
+ function keyOf(name, cwd) { return `${name}\u0000${cwd}`; }
171
+ /**
172
+ * Union `fresh` (what is open right now) into `existing` (the restore list).
173
+ *
174
+ * Pure and exported so the property that actually matters is testable: this
175
+ * never returns fewer entries than `existing`. Every historical way of losing
176
+ * the manifest — /exit-ing tabs one by one, a daemon outage, an iTerm restart —
177
+ * reaches this function as a small or empty `fresh`, and must be a no-op rather
178
+ * than a truncation.
179
+ */
180
+ export function mergeEntries(existing, fresh, now) {
181
+ const pending = new Map();
182
+ for (const e of fresh)
183
+ pending.set(keyOf(e.name, e.cwd), e);
184
+ const merged = existing.map((e) => {
185
+ const k = keyOf(e.name, e.cwd);
186
+ if (!pending.has(k))
187
+ return e; // not open now — keep it; that IS the point
188
+ pending.delete(k);
189
+ return { ...e, lastSeen: now };
190
+ });
191
+ for (const e of pending.values())
192
+ merged.push(e);
193
+ return merged;
194
+ }
195
+ function ago(iso) {
196
+ if (!iso)
197
+ return "never";
198
+ const days = (Date.now() - Date.parse(iso)) / 86_400_000;
199
+ if (!Number.isFinite(days))
200
+ return "never";
201
+ if (days < 1)
202
+ return "today";
203
+ if (days < 2)
204
+ return "1d ago";
205
+ return `${Math.floor(days)}d ago`;
206
+ }
207
+ // ── subcommands ─────────────────────────────────────────────────────────────
208
+ /**
209
+ * Merge the currently-open sessions into the manifest. Never removes anything.
210
+ * Returns null when the live set could not be determined (nothing was written).
211
+ */
212
+ async function doSnapshot() {
213
+ const sessions = await liveSessions();
214
+ if (sessions === null) {
215
+ console.error("Daemon unreachable — manifest left untouched.");
216
+ return null;
217
+ }
218
+ const ttys = ttyMap();
219
+ const now = new Date().toISOString();
220
+ const fresh = [];
221
+ for (const s of sessions) {
222
+ if (s.kind !== "claude")
223
+ continue;
224
+ const tty = ttys[s.sessionId];
225
+ if (!tty)
226
+ continue;
227
+ const cwd = cwdForTty(tty);
228
+ if (!cwd)
229
+ continue;
230
+ fresh.push({ name: s.paiName || basename(cwd), cwd, lastSeen: now, addedAt: now });
231
+ }
232
+ const merged = mergeEntries(readManifest().entries, fresh, now);
233
+ writeManifest(merged);
234
+ return { merged, seen: sessions.filter((s) => s.kind === "claude").length };
235
+ }
236
+ async function doRestore(opts) {
237
+ if (!existsSync(MANIFEST)) {
238
+ console.error(`No manifest at ${MANIFEST} — run 'aibroker sessions snapshot' first.`);
239
+ process.exit(1);
240
+ }
241
+ let entries = readManifest().entries;
242
+ if (opts.only)
243
+ entries = entries.filter((e) => e.name.toLowerCase().includes(opts.only.toLowerCase()));
244
+ let n = 0;
245
+ for (const e of entries) {
246
+ if (!e?.cwd || !e?.name)
247
+ continue;
248
+ if (!existsSync(e.cwd)) {
249
+ console.error(` skip (missing dir): ${e.cwd}`);
250
+ continue;
251
+ }
252
+ if (opts.dryRun) {
253
+ console.log(` would open ${e.name.padEnd(24)} ${e.cwd}`);
254
+ n++;
255
+ continue;
256
+ }
257
+ const esc = launchCmd(e.cwd, e.name).replace(/"/g, '\\"');
258
+ osascript(`tell application "iTerm2"
259
+ activate
260
+ if (count of windows) = 0 then create window with default profile
261
+ tell current window
262
+ set newTab to (create tab with default profile)
263
+ tell current session of newTab to write text "${esc}"
264
+ end tell
265
+ end tell`);
266
+ console.log(` reopened ${e.name.padEnd(24)} ${e.cwd}`);
267
+ n++;
268
+ execSync("sleep 1"); // stagger so iTerm/claude don't stampede
269
+ }
270
+ console.log(opts.dryRun ? `Would reopen ${n} session(s).` : `Reopened ${n} session(s).`);
271
+ }
272
+ export const POLL_MS = 500;
273
+ /** Consecutive identical frames that mean "Claude stopped writing". */
274
+ export const SETTLE_TICKS = 4;
275
+ /** Distinct frames required before we believe the prompt was actually SUBMITTED. */
276
+ export const ACK_TRANSITIONS = 2;
277
+ /**
278
+ * Send `message` to one session and prove it was consumed.
279
+ *
280
+ * A successful `write text` only means keystrokes reached the terminal — not
281
+ * that Claude accepted the prompt. That gap is exactly what leaves you walking
282
+ * to every tab by hand after a hiccup. So we watch the screen instead.
283
+ *
284
+ * The subtlety: "the screen changed" is NOT proof of submission. Text typed into
285
+ * Claude's input box changes the screen too, so a send whose Enter was swallowed
286
+ * looks identical to a successful one for one frame — and then freezes. We
287
+ * therefore require ACK_TRANSITIONS *distinct* frames: a working Claude animates
288
+ * (spinner, streaming tokens) and trivially clears the bar, whereas text sitting
289
+ * unsubmitted in the box produces exactly one change and then stillness, so it
290
+ * correctly fails and retries.
291
+ *
292
+ * 1. send, then poll for ACK_TRANSITIONS distinct frames -> genuinely submitted
293
+ * 2. poll until SETTLE_TICKS identical frames -> Claude finished
294
+ *
295
+ * Frame-diffing is deliberately content-agnostic: it never parses what `pause
296
+ * session` prints, so it keeps working when that output changes.
297
+ */
298
+ export async function sendVerified(sessionId, message, opts, probe) {
299
+ const before = probe.capture(sessionId);
300
+ if (before === null)
301
+ return "unreadable";
302
+ let acked = false;
303
+ for (let attempt = 1; attempt <= opts.retries && !acked; attempt++) {
304
+ probe.send(sessionId, message);
305
+ const seen = new Set([hash(before)]);
306
+ const ackDeadline = probe.now() + Math.min(8000, opts.timeoutMs);
307
+ while (probe.now() < ackDeadline) {
308
+ await probe.sleep(POLL_MS);
309
+ const frame = probe.capture(sessionId);
310
+ if (frame !== null)
311
+ seen.add(hash(frame));
312
+ // seen includes the pre-send frame, so N transitions == N+1 members.
313
+ if (seen.size > ACK_TRANSITIONS) {
314
+ acked = true;
315
+ break;
316
+ }
317
+ }
318
+ if (!acked && attempt < opts.retries)
319
+ probe.onRetry?.(attempt + 1, opts.retries);
320
+ }
321
+ if (!acked)
322
+ return "no-ack";
323
+ // Wait for the reply to finish rather than for any particular string.
324
+ const settleDeadline = probe.now() + opts.timeoutMs;
325
+ let last = hash(probe.capture(sessionId) ?? "");
326
+ let still = 0;
327
+ while (probe.now() < settleDeadline) {
328
+ await probe.sleep(POLL_MS);
329
+ const cur = hash(probe.capture(sessionId) ?? "");
330
+ if (cur === last) {
331
+ if (++still >= SETTLE_TICKS)
332
+ return "ok";
333
+ }
334
+ else {
335
+ still = 0;
336
+ last = cur;
337
+ }
338
+ }
339
+ return "no-settle";
340
+ }
341
+ /** The real terminal, wired to whichever transport owns the session. */
342
+ const liveProbe = (log) => ({
343
+ capture: (id) => captureSession(id, 60),
344
+ send: (id, text) => { typeIntoSession(id, text); },
345
+ sleep,
346
+ now: () => Date.now(),
347
+ onRetry: (attempt, of) => log(` no response, retry ${attempt}/${of}…`),
348
+ });
349
+ async function doCheckpoint(opts) {
350
+ // Capture BEFORE asking anyone to pause. This is the authoritative record of
351
+ // the working set, taken while every session is still alive — never leave it
352
+ // to the periodic agent, which may not tick again before you shut down.
353
+ if (!opts.dryRun) {
354
+ const snap = await doSnapshot();
355
+ if (snap)
356
+ console.log(`Manifest updated: ${snap.merged.length} entr(ies) on record.\n`);
357
+ else
358
+ console.log("Warning: could not refresh the manifest (daemon unreachable).\n");
359
+ }
360
+ const self = (process.env.ITERM_SESSION_ID || "").split(":").pop() || "";
361
+ const sessions = await liveSessions();
362
+ if (sessions === null) {
363
+ console.error("Daemon unreachable — cannot checkpoint.");
364
+ process.exit(1);
365
+ }
366
+ const targets = sessions.filter((s) => {
367
+ if (s.kind !== "claude")
368
+ return false;
369
+ if (s.sessionId === self)
370
+ return false; // don't checkpoint the terminal we're in
371
+ const label = s.paiName || s.name || s.sessionId;
372
+ return !opts.only || label.toLowerCase().includes(opts.only.toLowerCase());
373
+ });
374
+ if (opts.dryRun) {
375
+ for (const s of targets)
376
+ console.log(` would checkpoint ${s.paiName || s.name || s.sessionId}`);
377
+ console.log(`Would checkpoint ${targets.length} session(s).`);
378
+ return;
379
+ }
380
+ const failed = [];
381
+ let ok = 0;
382
+ for (const s of targets) {
383
+ const label = s.paiName || s.name || s.sessionId;
384
+ process.stdout.write(` ${label.padEnd(24)} → "${opts.message}" … `);
385
+ const res = await sendVerified(s.sessionId, opts.message, { timeoutMs: opts.timeoutMs, retries: 3 }, liveProbe((m) => console.log(m)));
386
+ if (res === "ok") {
387
+ console.log("saved");
388
+ ok++;
389
+ }
390
+ else {
391
+ const why = res === "no-ack" ? "NO RESPONSE" : res === "no-settle" ? "STILL BUSY" : "UNREADABLE";
392
+ console.log(why);
393
+ failed.push({ label, why: res });
394
+ }
395
+ }
396
+ console.log(`\n${ok}/${targets.length} session(s) checkpointed.`);
397
+ if (failed.length) {
398
+ console.log(`\n${failed.length} need a look before you reboot:`);
399
+ for (const f of failed) {
400
+ const hint = f.why === "no-ack" ? "never reacted — check the tab is at a prompt"
401
+ : f.why === "no-settle" ? "still working — give it longer, or --timeout"
402
+ : "could not read the tab";
403
+ console.log(` ${f.label.padEnd(24)} ${hint}`);
404
+ }
405
+ console.log(`\nRetry just those: aibroker sessions checkpoint --only "<name>"`);
406
+ process.exitCode = 1;
407
+ }
408
+ else {
409
+ console.log("All sessions saved. Safe to reboot.");
410
+ }
411
+ }
412
+ function doForget(name) {
413
+ const entries = readManifest().entries;
414
+ const lower = name.toLowerCase();
415
+ const keep = entries.filter((e) => e.name.toLowerCase() !== lower && e.cwd !== name);
416
+ if (keep.length === entries.length) {
417
+ console.error(`No manifest entry matching "${name}".`);
418
+ process.exit(1);
419
+ }
420
+ writeManifest(keep);
421
+ console.log(`Forgot ${entries.length - keep.length} entr(ies); ${keep.length} remain.`);
422
+ }
423
+ function doPrune(opts) {
424
+ const entries = readManifest().entries;
425
+ const cutoff = Date.now() - opts.days * 86_400_000;
426
+ const stale = entries.filter((e) => e.lastSeen && Date.parse(e.lastSeen) < cutoff);
427
+ if (!stale.length) {
428
+ console.log(`Nothing unseen for ${opts.days}+ days.`);
429
+ return;
430
+ }
431
+ for (const e of stale)
432
+ console.log(` ${opts.dryRun ? "would drop" : "dropped"} ${e.name.padEnd(24)} ${ago(e.lastSeen)}`);
433
+ if (opts.dryRun) {
434
+ console.log(`Would drop ${stale.length} entr(ies).`);
435
+ return;
436
+ }
437
+ const keep = entries.filter((e) => !stale.includes(e));
438
+ writeManifest(keep);
439
+ console.log(`Dropped ${stale.length}; ${keep.length} remain.`);
440
+ }
441
+ function installAgent() {
442
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
443
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
444
+ <plist version="1.0">
445
+ <dict>
446
+ <key>Label</key><string>${AGENT_LABEL}</string>
447
+ <key>ProgramArguments</key>
448
+ <array>
449
+ <string>${process.execPath}</string>
450
+ <string>/usr/local/bin/aibroker</string>
451
+ <string>sessions</string>
452
+ <string>snapshot</string>
453
+ </array>
454
+ <key>EnvironmentVariables</key>
455
+ <dict><key>PATH</key><string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string></dict>
456
+ <key>StartInterval</key><integer>300</integer>
457
+ <key>RunAtLoad</key><true/>
458
+ <key>StandardOutPath</key><string>/tmp/aibroker-sessions-snapshot.log</string>
459
+ <key>StandardErrorPath</key><string>/tmp/aibroker-sessions-snapshot.log</string>
460
+ </dict>
461
+ </plist>
462
+ `;
463
+ mkdirSync(dirname(AGENT_PLIST), { recursive: true });
464
+ writeFileSync(AGENT_PLIST, plist);
465
+ const uid = process.getuid?.() ?? 0;
466
+ try {
467
+ execSync(`launchctl bootout gui/${uid}/${AGENT_LABEL} 2>/dev/null`);
468
+ }
469
+ catch { /* not loaded */ }
470
+ execSync(`launchctl bootstrap gui/${uid} ${AGENT_PLIST}`);
471
+ console.log(`Installed ${AGENT_LABEL} (snapshot every 5 min) -> ${AGENT_PLIST}`);
472
+ }
473
+ function uninstallAgent() {
474
+ const uid = process.getuid?.() ?? 0;
475
+ try {
476
+ execSync(`launchctl bootout gui/${uid}/${AGENT_LABEL} 2>/dev/null`);
477
+ }
478
+ catch { /* not loaded */ }
479
+ try {
480
+ execSync(`rm -f "${AGENT_PLIST}"`);
481
+ }
482
+ catch { /* gone */ }
483
+ console.log(`Removed ${AGENT_LABEL}.`);
484
+ }
485
+ function usage() {
486
+ console.log(`aibroker sessions — snapshot/restore/checkpoint open Claude sessions
487
+
488
+ snapshot merge open sessions (name + dir) into the manifest
489
+ restore [--dry-run] [--only NAME] reopen every session in its own iTerm2 tab
490
+ checkpoint [--message M] [--only NAME] [--dry-run] [--timeout SECONDS]
491
+ ask every open session to persist state and WAIT
492
+ for each to finish (default message: "pause session",
493
+ default timeout: 120s per session)
494
+ list [--verbose] show the current manifest
495
+ forget NAME drop an entry from the manifest
496
+ prune [--older-than DAYS] [--dry-run] drop entries not seen in DAYS (default ${DEFAULT_PRUNE_DAYS})
497
+ install | uninstall manage the 5-min auto-snapshot LaunchAgent (${AGENT_LABEL})
498
+
499
+ The manifest is a registry, not a mirror: closing a session does NOT remove it.
500
+ Entries leave only via 'forget' or 'prune'. Every write keeps a .bak.
501
+
502
+ Reboot flow: aibroker sessions checkpoint → reboot → aibroker sessions restore`);
503
+ }
504
+ // ── dispatcher ──────────────────────────────────────────────────────────────
505
+ export async function runSessions(args) {
506
+ try {
507
+ await dispatch(args);
508
+ }
509
+ catch (err) {
510
+ // A stack trace here is noise: the failures that reach this point (an
511
+ // unparseable manifest, an unwritable ~/.aibroker) are things the user has
512
+ // to go fix by hand, so print the sentence that tells them how.
513
+ console.error(err instanceof Error ? err.message : String(err));
514
+ process.exitCode = 1;
515
+ }
516
+ }
517
+ async function dispatch(args) {
518
+ const [sub, ...rest] = args;
519
+ const has = (f) => rest.includes(f);
520
+ const val = (f) => { const i = rest.indexOf(f); return i >= 0 ? rest[i + 1] : undefined; };
521
+ /** `Number(x) || fallback` would turn a deliberate 0 into the default. */
522
+ const num = (f, fallback) => {
523
+ const raw = val(f);
524
+ if (raw === undefined)
525
+ return fallback;
526
+ const n = Number(raw);
527
+ if (!Number.isFinite(n) || n < 0)
528
+ throw new Error(`${f} expects a non-negative number, got "${raw}"`);
529
+ return n;
530
+ };
531
+ switch (sub) {
532
+ case "snapshot": {
533
+ const snap = await doSnapshot();
534
+ if (!snap) {
535
+ process.exitCode = 1;
536
+ break;
537
+ }
538
+ console.log(`Snapshotted ${snap.seen} open session(s); ${snap.merged.length} on record -> ${MANIFEST}`);
539
+ for (const e of snap.merged)
540
+ console.log(` ${e.name.padEnd(24)} ${ago(e.lastSeen).padEnd(9)} ${e.cwd}`);
541
+ break;
542
+ }
543
+ case "restore":
544
+ await doRestore({ dryRun: has("--dry-run"), only: val("--only") });
545
+ break;
546
+ case "checkpoint":
547
+ await doCheckpoint({
548
+ message: val("--message") ?? "pause session",
549
+ only: val("--only"),
550
+ dryRun: has("--dry-run"),
551
+ timeoutMs: num("--timeout", 120) * 1000,
552
+ });
553
+ break;
554
+ case "list": {
555
+ if (!existsSync(MANIFEST)) {
556
+ console.log("(no manifest yet — run 'aibroker sessions snapshot')");
557
+ break;
558
+ }
559
+ const entries = readManifest().entries;
560
+ console.log(`${entries.length} session(s) in ${MANIFEST}:`);
561
+ for (const e of entries) {
562
+ const seen = has("--verbose") ? `${ago(e.lastSeen).padEnd(9)} ` : "";
563
+ console.log(` ${e.name.padEnd(24)} ${seen}${e.cwd}`);
564
+ }
565
+ break;
566
+ }
567
+ case "forget": {
568
+ const name = rest.find((a) => !a.startsWith("--"));
569
+ if (!name) {
570
+ console.error("Usage: aibroker sessions forget NAME");
571
+ process.exit(1);
572
+ }
573
+ doForget(name);
574
+ break;
575
+ }
576
+ case "prune":
577
+ doPrune({ days: num("--older-than", DEFAULT_PRUNE_DAYS), dryRun: has("--dry-run") });
578
+ break;
579
+ case "install":
580
+ installAgent();
581
+ break;
582
+ case "uninstall":
583
+ uninstallAgent();
584
+ break;
585
+ case "help":
586
+ case "--help":
587
+ case "-h":
588
+ case undefined:
589
+ usage();
590
+ break;
591
+ default:
592
+ console.error(`Unknown: aibroker sessions ${sub}`);
593
+ usage();
594
+ process.exit(1);
595
+ }
596
+ }
597
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/daemon/sessions.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -159,6 +159,7 @@ const server = new McpServer({ name: "aibroker", version: "0.7.0" }, {
159
159
  "| `[PAILot]` | Text from PAILot app | `pailot_send` (text reply) |",
160
160
  "| `[PAILot:voice]` | Voice from PAILot app | `pailot_tts` (VOICE reply — NEVER pailot_send) |",
161
161
  "| `[Session:NAME]` | Text from another Claude Code session | `aibroker_send_to_session` (reply to NAME) |",
162
+ "| `[Task]` | A dispatched work order from the task bus | **Do NOT reply** — act on it, then close it on the tracker |",
162
163
  "| _no prefix_ | User typing at the terminal | Terminal only — do NOT send to any channel |",
163
164
  "",
164
165
  "### Rules",
@@ -170,6 +171,7 @@ const server = new McpServer({ name: "aibroker", version: "0.7.0" }, {
170
171
  "- **Voice formatting**: NEVER use asterisks or any markdown in _tts messages. TTS reads them literally as 'asterisk'. Write plain conversational text only.",
171
172
  "- **Acknowledge long tasks**: if a prefixed task will take more than a few seconds, immediately send a brief ack via the matching _tts or _send tool (match the medium!) BEFORE starting work. Never leave the client silent.",
172
173
  "- **Session messages**: `[Session:NAME]` messages come from another Claude Code session. Process the request, then send your full response back via `aibroker_send_to_session(target=NAME, message=YOUR_RESPONSE)`. Do NOT just answer in the terminal — the sender is waiting via `aibroker_receive`. IMPORTANT: When calling `aibroker_send_to_session`, do NOT include a `[Session:...]` prefix in your message — the hub auto-prepends it.",
174
+ "- **Task messages**: `[Task]` is a dispatched work order, not a question. Act on it. Do NOT reply — the process that sent it has already exited, so there is no reply channel and the usual 'respond in kind' rule does not apply. Report by closing it on the tracker (`pai task done <id>`); if you are blocked or disagree, file that back with `pai task add` rather than replying. The message body restates this, and the body wins if they ever disagree.",
173
175
  "- **Per-message toggle**: this switches automatically with every message. No manual on/off needed.",
174
176
  "",
175
177
  "### Companion App Message Detection",