cmdr-mcp 0.2.0 → 0.3.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.
@@ -6,6 +6,9 @@ var __export = (target, all) => {
6
6
  __defProp(target, name, { get: all[name], enumerable: true });
7
7
  };
8
8
 
9
+ // src/cli/watch.ts
10
+ import { randomUUID as randomUUID2 } from "node:crypto";
11
+
9
12
  // src/shared/diagnostics.ts
10
13
  import { mkdirSync as mkdirSync2, readFileSync, writeFileSync, renameSync, rmSync } from "node:fs";
11
14
  import { join as join2 } from "node:path";
@@ -268,7 +271,7 @@ var Rpc = class extends EventEmitter {
268
271
  };
269
272
 
270
273
  // src/shared/version.ts
271
- var VERSION = true ? "0.2.0" : MIN_CLIENT_VERSION;
274
+ var VERSION = true ? "0.3.0" : MIN_CLIENT_VERSION;
272
275
  var PROTOCOL = 1;
273
276
  function newer(a, b) {
274
277
  const x = a.split(".").map(Number), y = b.split(".").map(Number);
@@ -453,6 +456,97 @@ var DaemonClient = class {
453
456
  }
454
457
  };
455
458
 
459
+ // src/cli/watch.ts
460
+ async function watch(sid, once = false) {
461
+ const rpc = await daemonConnection({ start: true, upgrade: true });
462
+ const token = randomUUID2();
463
+ let timer;
464
+ let stopped = false;
465
+ let checking = false;
466
+ let dirty = false;
467
+ let error;
468
+ const seen = /* @__PURE__ */ new Set();
469
+ const stop = () => {
470
+ stopped = true;
471
+ rpc.close();
472
+ };
473
+ process.on("SIGINT", stop);
474
+ process.on("SIGTERM", stop);
475
+ const closed = new Promise(
476
+ (resolve3) => rpc.once("close", () => {
477
+ if (!stopped) error ||= new Error("cmdr daemon disconnected; re-arm the host watcher");
478
+ resolve3();
479
+ })
480
+ );
481
+ const show = (result, initial = false) => {
482
+ const current = /* @__PURE__ */ new Set();
483
+ const fresh = result.messages.filter((m) => {
484
+ const key = JSON.stringify([m.id, m.cancel_requested_at]);
485
+ current.add(key);
486
+ const alreadyAccepted = initial && m.type === "command" && m.work_state === "accepted" && !m.cancel_requested_at;
487
+ return !alreadyAccepted && !seen.has(key);
488
+ });
489
+ seen.clear();
490
+ for (const key of current) seen.add(key);
491
+ if (!fresh.length) return;
492
+ process.stdout.write(
493
+ JSON.stringify({
494
+ kind: "cmdr.wake",
495
+ sid,
496
+ messages: fresh,
497
+ instruction: "Call read and read(recover=true); handle cancellation first and report with reply_to."
498
+ }) + "\n"
499
+ );
500
+ if (once || result.wake_mode === "zcode") stop();
501
+ };
502
+ const check = async () => {
503
+ if (stopped) return;
504
+ if (checking) {
505
+ dirty = true;
506
+ return;
507
+ }
508
+ checking = true;
509
+ try {
510
+ do {
511
+ dirty = false;
512
+ show(await rpc.request("admin.watch", { sid, token, action: "pulse" }));
513
+ } while (dirty && !stopped);
514
+ } catch (e) {
515
+ if (!stopped) {
516
+ error = e;
517
+ stop();
518
+ }
519
+ } finally {
520
+ checking = false;
521
+ }
522
+ };
523
+ try {
524
+ await rpc.request("session.register", { kind: "cli" });
525
+ rpc.on("notification", (method) => {
526
+ if (method === "lifecycle.event") {
527
+ dirty = true;
528
+ if (timer) void check();
529
+ }
530
+ });
531
+ await rpc.request("admin.tail", { for: sid, after: "now" });
532
+ show(await rpc.request("admin.watch", { sid, token, action: "attach" }), true);
533
+ if (!stopped) {
534
+ timer = setInterval(() => {
535
+ void check();
536
+ }, 3e4);
537
+ if (dirty) await check();
538
+ }
539
+ await closed;
540
+ if (error) throw error;
541
+ } finally {
542
+ clearInterval(timer);
543
+ stopped = true;
544
+ rpc.close();
545
+ process.off("SIGINT", stop);
546
+ process.off("SIGTERM", stop);
547
+ }
548
+ }
549
+
456
550
  // src/cli/tail.ts
457
551
  async function tail(options) {
458
552
  let after = options.after, stopped = false;
@@ -464,9 +558,10 @@ async function tail(options) {
464
558
  process.on("SIGINT", stop);
465
559
  process.on("SIGTERM", stop);
466
560
  const show = (event) => {
467
- if (after !== void 0 && event.event_seq <= after) return;
561
+ if (typeof after === "number" && event.event_seq <= after) return;
468
562
  process.stdout.write(
469
- options.json ? JSON.stringify(event) + "\n" : `${event.event_seq} ${new Date(event.at).toISOString()} ${event.channel || "-"} ${event.kind} ${event.from_sid || "-"} \u2192 ${event.to_sid || "-"} ${event.message_id || ""}${event.reply_to ? ` reply_to=${event.reply_to}` : ""}${event.reason ? ` ${event.reason}` : ""}${event.message ? ` ${event.message.body.replace(/\s+/g, " ")}` : ""}
563
+ options.json ? JSON.stringify(event) + "\n" : options.line ? `[cmdr] ${event.message?.type || event.kind} id=${event.message_id || "-"} from=${event.from_sid || "-"}${event.reply_to ? ` reply_to=${event.reply_to}` : ""}; call read and read(recover=true)
564
+ ` : `${event.event_seq} ${new Date(event.at).toISOString()} ${event.channel || "-"} ${event.kind} ${event.from_sid || "-"} \u2192 ${event.to_sid || "-"} ${event.message_id || ""}${event.reply_to ? ` reply_to=${event.reply_to}` : ""}${event.reason ? ` ${event.reason}` : ""}${event.message ? ` ${event.message.body.replace(/\s+/g, " ")}` : ""}
470
565
  `
471
566
  );
472
567
  after = event.event_seq;
@@ -497,7 +592,7 @@ async function tail(options) {
497
592
  }
498
593
  for (; ; ) {
499
594
  for (const event of result.events) show(event);
500
- after = Math.max(after || 0, result.next);
595
+ after = Math.max(typeof after === "number" ? after : 0, result.next);
501
596
  if (result.next >= result.high) break;
502
597
  result = await rpc.request("admin.events", { ...options, after });
503
598
  }
@@ -4940,6 +5035,10 @@ if (process.argv[2] === "session") {
4940
5035
  after: { type: "string" },
4941
5036
  for: { type: "string" },
4942
5037
  adapter: { type: "string" },
5038
+ transport: { type: "string" },
5039
+ actionable: { type: "boolean" },
5040
+ once: { type: "boolean" },
5041
+ format: { type: "string" },
4943
5042
  executable: { type: "string" },
4944
5043
  socket: { type: "string" },
4945
5044
  resolve: { type: "string" },
@@ -5042,7 +5141,7 @@ if (process.argv[2] === "session") {
5042
5141
  try {
5043
5142
  if (v.help)
5044
5143
  process.stdout.write(
5045
- "cmdr status | list [--all] [--squad ID] | tail [--follow] [--full] [--json] [--after EVENT_SEQ] [--for SID] | standby start|status|stop|resume --session SID [--adapter codex|manual] | send --squad ID [--to MEMBER] [--type command|cancel|info|answer] TEXT | read --session SID [--peek] | daemon start|stop|restart|status|logs | config [--agent HOST] [--session ID] | doctor [--plugin-root PATH] [--deep] | session --help | purge [--all]\n"
5144
+ "cmdr status | list [--all] [--squad ID] | tail [--follow] [--full] [--json] [--after EVENT_SEQ|now] [--actionable] [--format line|json] [--for SID] | standby start|status|stop|resume|watch --session SID [--adapter codex|claude|zcode|manual] [--transport auto|proxy|queue] [--once] | send --squad ID [--to MEMBER] [--type command|cancel|info|answer] TEXT | read --session SID [--peek] | daemon start|stop|restart|status|logs | config [--agent HOST] [--session ID] | doctor [--plugin-root PATH] [--deep] | session --help | purge [--all]\n"
5046
5145
  );
5047
5146
  else if (cmd === "config") {
5048
5147
  const agent = v.agent || "generic";
@@ -5137,30 +5236,37 @@ if (process.argv[2] === "session") {
5137
5236
  } else if (cmd === "purge") print(await call("admin.purge", { all: !!v.all }));
5138
5237
  else if (cmd === "standby") {
5139
5238
  if (!v.session) throw new Error("--session SID is required");
5140
- print(
5141
- await call(
5142
- "admin.standby",
5143
- {
5144
- sid: v.session,
5145
- action: args[1] || "status",
5146
- adapter: v.adapter,
5147
- executable: v.executable,
5148
- socket: v.socket,
5149
- resolve: v.resolve
5150
- },
5151
- true
5152
- )
5153
- );
5239
+ if (args[1] === "watch") await watch(v.session, !!v.once);
5240
+ else
5241
+ print(
5242
+ await call(
5243
+ "admin.standby",
5244
+ {
5245
+ sid: v.session,
5246
+ action: args[1] || "status",
5247
+ adapter: v.adapter,
5248
+ transport: v.transport,
5249
+ executable: v.executable,
5250
+ socket: v.socket,
5251
+ resolve: v.resolve
5252
+ },
5253
+ true
5254
+ )
5255
+ );
5154
5256
  } else if (cmd === "tail") {
5155
- const after = v.after === void 0 ? void 0 : Number(v.after);
5156
- if (after !== void 0 && (!Number.isSafeInteger(after) || after < 0))
5157
- throw new Error("--after requires a nonnegative event_seq");
5257
+ const after = v.after === void 0 ? void 0 : v.after === "now" ? "now" : Number(v.after);
5258
+ if (after !== void 0 && after !== "now" && (!Number.isSafeInteger(after) || after < 0))
5259
+ throw new Error("--after requires now or a nonnegative event_seq");
5260
+ if (v.format && !["line", "json"].includes(v.format))
5261
+ throw new Error("--format must be line or json");
5158
5262
  await tail({
5159
5263
  squad: v.squad,
5160
5264
  for: v.for,
5161
5265
  after,
5162
5266
  full: !!v.full,
5163
- json: !!v.json,
5267
+ json: !!v.json || v.format === "json",
5268
+ line: v.format === "line",
5269
+ actionable: !!v.actionable,
5164
5270
  follow: !!v.follow
5165
5271
  });
5166
5272
  } else if (cmd === "daemon") {