cmdr-mcp 0.1.1 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +19 -16
- package/docs/README.zh-CN.md +16 -11
- package/docs/agent-integration.md +13 -3
- package/docs/cmdr-design-v1.md +2 -0
- package/docs/implementation.md +51 -6
- package/docs/long-running-collaboration.md +138 -0
- package/docs/publishing.md +23 -8
- package/docs/troubleshooting.md +34 -3
- package/docs/zcode-desktop-session-visibility.md +172 -0
- package/marketplace.json +1 -1
- package/package.json +2 -1
- package/plugins/cmdr/.claude-plugin/plugin.json +1 -1
- package/plugins/cmdr/.codex-plugin/plugin.json +1 -1
- package/plugins/cmdr/.zcode-plugin/plugin.json +1 -1
- package/plugins/cmdr/README.md +4 -4
- package/plugins/cmdr/bin/cmdr-node +2 -2
- package/plugins/cmdr/commands/cmdr.md +4 -6
- package/plugins/cmdr/dist/cli.mjs +322 -36
- package/plugins/cmdr/dist/daemon.mjs +1478 -155
- package/plugins/cmdr/dist/hook.mjs +25 -8
- package/plugins/cmdr/dist/integrity.json +13 -13
- package/plugins/cmdr/dist/mcp.mjs +44 -17
- package/plugins/cmdr/skills/cmdr-commander/SKILL.md +10 -8
- package/plugins/cmdr/skills/cmdr-executor/SKILL.md +9 -7
- package/plugins/cmdr/skills/using-cmdr/SKILL.md +7 -9
|
@@ -6,8 +6,8 @@ var __export = (target, all) => {
|
|
|
6
6
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
// src/cli/
|
|
10
|
-
import {
|
|
9
|
+
// src/cli/watch.ts
|
|
10
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11
11
|
|
|
12
12
|
// src/shared/diagnostics.ts
|
|
13
13
|
import { mkdirSync as mkdirSync2, readFileSync, writeFileSync, renameSync, rmSync } from "node:fs";
|
|
@@ -115,7 +115,18 @@ import { connect } from "node:net";
|
|
|
115
115
|
import { spawn } from "node:child_process";
|
|
116
116
|
import { dirname, join as join3 } from "node:path";
|
|
117
117
|
import { fileURLToPath } from "node:url";
|
|
118
|
-
import { mkdirSync as mkdirSync3, rmSync as rmSync2, statSync } from "node:fs";
|
|
118
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, rmSync as rmSync2, statSync } from "node:fs";
|
|
119
|
+
|
|
120
|
+
// src/daemon/lock.ts
|
|
121
|
+
function alive(pid) {
|
|
122
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
123
|
+
try {
|
|
124
|
+
process.kill(pid, 0);
|
|
125
|
+
return true;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return e.code === "EPERM";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
119
130
|
|
|
120
131
|
// src/shared/rpc.ts
|
|
121
132
|
import { EventEmitter } from "node:events";
|
|
@@ -260,7 +271,7 @@ var Rpc = class extends EventEmitter {
|
|
|
260
271
|
};
|
|
261
272
|
|
|
262
273
|
// src/shared/version.ts
|
|
263
|
-
var VERSION = true ? "0.
|
|
274
|
+
var VERSION = true ? "0.3.0" : MIN_CLIENT_VERSION;
|
|
264
275
|
var PROTOCOL = 1;
|
|
265
276
|
function newer(a, b) {
|
|
266
277
|
const x = a.split(".").map(Number), y = b.split(".").map(Number);
|
|
@@ -306,15 +317,16 @@ async function daemonConnection(options = {}) {
|
|
|
306
317
|
{ from: hello.version, to: VERSION, protocol: hello.protocol },
|
|
307
318
|
options.home
|
|
308
319
|
);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
320
|
+
throw new CmdrError(
|
|
321
|
+
"UPGRADE_REQUIRED",
|
|
322
|
+
`Daemon ${hello.version} is older than client ${VERSION}. Run cmdr daemon restart from this installation; automatic replacement is disabled to protect live sessions.`
|
|
323
|
+
);
|
|
313
324
|
}
|
|
314
325
|
return rpc;
|
|
315
326
|
} catch (e) {
|
|
316
327
|
rpc?.close();
|
|
317
|
-
if (e.code === "PROTOCOL_MISMATCH" || !options.start)
|
|
328
|
+
if (e.code === "PROTOCOL_MISMATCH" || e.code === "UPGRADE_REQUIRED" || !options.start)
|
|
329
|
+
throw e;
|
|
318
330
|
}
|
|
319
331
|
if (!owner) {
|
|
320
332
|
try {
|
|
@@ -328,7 +340,12 @@ async function daemonConnection(options = {}) {
|
|
|
328
340
|
}
|
|
329
341
|
}
|
|
330
342
|
}
|
|
331
|
-
|
|
343
|
+
let daemonOwnsLock = false;
|
|
344
|
+
try {
|
|
345
|
+
daemonOwnsLock = alive(Number(readFileSync2(p.lock, "utf8")));
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
if (owner && !daemonOwnsLock) {
|
|
332
349
|
if (attempt === 0 || !spawned) {
|
|
333
350
|
const child = spawn(
|
|
334
351
|
process.execPath,
|
|
@@ -439,6 +456,173 @@ var DaemonClient = class {
|
|
|
439
456
|
}
|
|
440
457
|
};
|
|
441
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
|
+
|
|
550
|
+
// src/cli/tail.ts
|
|
551
|
+
async function tail(options) {
|
|
552
|
+
let after = options.after, stopped = false;
|
|
553
|
+
let connection;
|
|
554
|
+
const stop = () => {
|
|
555
|
+
stopped = true;
|
|
556
|
+
connection?.close();
|
|
557
|
+
};
|
|
558
|
+
process.on("SIGINT", stop);
|
|
559
|
+
process.on("SIGTERM", stop);
|
|
560
|
+
const show = (event) => {
|
|
561
|
+
if (typeof after === "number" && event.event_seq <= after) return;
|
|
562
|
+
process.stdout.write(
|
|
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, " ")}` : ""}
|
|
565
|
+
`
|
|
566
|
+
);
|
|
567
|
+
after = event.event_seq;
|
|
568
|
+
};
|
|
569
|
+
try {
|
|
570
|
+
do {
|
|
571
|
+
try {
|
|
572
|
+
const rpc = connection = await daemonConnection();
|
|
573
|
+
const closed = new Promise((resolve3) => rpc.once("close", resolve3));
|
|
574
|
+
await rpc.request("session.register", { kind: "cli" });
|
|
575
|
+
let replaying = true;
|
|
576
|
+
const buffer = [];
|
|
577
|
+
rpc.on("notification", (method, value) => {
|
|
578
|
+
if (method === "lifecycle.event") replaying ? buffer.push(value) : show(value);
|
|
579
|
+
});
|
|
580
|
+
let result = await rpc.request(options.follow ? "admin.tail" : "admin.events", {
|
|
581
|
+
...options,
|
|
582
|
+
after
|
|
583
|
+
});
|
|
584
|
+
if (result.gap) {
|
|
585
|
+
const gap = { kind: "retention.gap", after, retained_after: result.retained_after };
|
|
586
|
+
if (options.json) process.stdout.write(JSON.stringify(gap) + "\n");
|
|
587
|
+
else
|
|
588
|
+
process.stderr.write(
|
|
589
|
+
`cmdr: event retention gap; events through ${result.retained_after} expired
|
|
590
|
+
`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
for (; ; ) {
|
|
594
|
+
for (const event of result.events) show(event);
|
|
595
|
+
after = Math.max(typeof after === "number" ? after : 0, result.next);
|
|
596
|
+
if (result.next >= result.high) break;
|
|
597
|
+
result = await rpc.request("admin.events", { ...options, after });
|
|
598
|
+
}
|
|
599
|
+
replaying = false;
|
|
600
|
+
for (const event of buffer.sort((a, b) => a.event_seq - b.event_seq)) show(event);
|
|
601
|
+
if (!options.follow) rpc.close();
|
|
602
|
+
else await closed;
|
|
603
|
+
} catch (e) {
|
|
604
|
+
if (!options.follow || stopped) {
|
|
605
|
+
if (!stopped) throw e;
|
|
606
|
+
} else
|
|
607
|
+
process.stderr.write(
|
|
608
|
+
`cmdr tail: ${String(e)}; reconnecting after ${after ?? "latest"}
|
|
609
|
+
`
|
|
610
|
+
);
|
|
611
|
+
} finally {
|
|
612
|
+
connection?.close();
|
|
613
|
+
connection = void 0;
|
|
614
|
+
}
|
|
615
|
+
if (options.follow && !stopped) await new Promise((r) => setTimeout(r, 1e3));
|
|
616
|
+
} while (options.follow && !stopped);
|
|
617
|
+
} finally {
|
|
618
|
+
process.off("SIGINT", stop);
|
|
619
|
+
process.off("SIGTERM", stop);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/cli/session.ts
|
|
624
|
+
import { parseArgs } from "node:util";
|
|
625
|
+
|
|
442
626
|
// node_modules/zod/v3/external.js
|
|
443
627
|
var external_exports = {};
|
|
444
628
|
__export(external_exports, {
|
|
@@ -4492,16 +4676,20 @@ var schemas = {
|
|
|
4492
4676
|
squad: external_exports.string().optional(),
|
|
4493
4677
|
name: external_exports.string().trim().min(1).max(64).optional(),
|
|
4494
4678
|
note: text.optional(),
|
|
4495
|
-
squad_name: external_exports.string().trim().min(1).max(64).optional()
|
|
4679
|
+
squad_name: external_exports.string().trim().min(1).max(64).optional(),
|
|
4680
|
+
takeover: external_exports.boolean().default(false),
|
|
4681
|
+
standby: external_exports.enum(["auto", "manual"]).optional(),
|
|
4682
|
+
rebind: external_exports.string().optional()
|
|
4496
4683
|
}).strict(),
|
|
4497
4684
|
list: external_exports.object({
|
|
4498
4685
|
...identity,
|
|
4686
|
+
full: external_exports.boolean().default(false),
|
|
4499
4687
|
scope: external_exports.enum(["squad", "all"]).optional(),
|
|
4500
4688
|
squad: external_exports.string().optional()
|
|
4501
4689
|
}).strict(),
|
|
4502
4690
|
report: external_exports.object({
|
|
4503
4691
|
...identity,
|
|
4504
|
-
status: external_exports.enum(["ready", "working", "blocked", "done", "failed"]),
|
|
4692
|
+
status: external_exports.enum(["ready", "working", "blocked", "done", "failed", "cancelled"]),
|
|
4505
4693
|
message: text,
|
|
4506
4694
|
reply_to: external_exports.string().optional(),
|
|
4507
4695
|
data
|
|
@@ -4511,7 +4699,10 @@ var schemas = {
|
|
|
4511
4699
|
...identity,
|
|
4512
4700
|
to: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1).max(1e3)]),
|
|
4513
4701
|
message: text,
|
|
4514
|
-
type: external_exports.enum(["command", "answer", "info"]).default("command"),
|
|
4702
|
+
type: external_exports.enum(["command", "cancel", "answer", "info"]).default("command"),
|
|
4703
|
+
task_key: external_exports.string().min(1).max(128).optional(),
|
|
4704
|
+
reassign: external_exports.string().optional(),
|
|
4705
|
+
attention: external_exports.boolean().optional(),
|
|
4515
4706
|
priority: external_exports.enum(["high", "normal", "low"]).optional(),
|
|
4516
4707
|
reply_to: external_exports.string().optional(),
|
|
4517
4708
|
data
|
|
@@ -4522,7 +4713,10 @@ var schemas = {
|
|
|
4522
4713
|
limit: external_exports.number().int().min(1).max(100).default(20),
|
|
4523
4714
|
peek: external_exports.boolean().default(false),
|
|
4524
4715
|
history: external_exports.boolean().default(false),
|
|
4525
|
-
since: external_exports.string().optional()
|
|
4716
|
+
since: external_exports.string().optional(),
|
|
4717
|
+
id: external_exports.string().optional(),
|
|
4718
|
+
recover: external_exports.boolean().default(false),
|
|
4719
|
+
full: external_exports.boolean().default(false)
|
|
4526
4720
|
}).strict(),
|
|
4527
4721
|
leave: external_exports.object({ ...identity, dissolve: external_exports.boolean().default(false), message: text.optional() }).strict()
|
|
4528
4722
|
};
|
|
@@ -4591,6 +4785,16 @@ async function runSession(argv) {
|
|
|
4591
4785
|
to: { type: "string" },
|
|
4592
4786
|
type: { type: "string" },
|
|
4593
4787
|
"reply-to": { type: "string" },
|
|
4788
|
+
priority: { type: "string" },
|
|
4789
|
+
"task-key": { type: "string" },
|
|
4790
|
+
reassign: { type: "string" },
|
|
4791
|
+
rebind: { type: "string" },
|
|
4792
|
+
takeover: { type: "boolean" },
|
|
4793
|
+
standby: { type: "string" },
|
|
4794
|
+
full: { type: "boolean" },
|
|
4795
|
+
recover: { type: "boolean" },
|
|
4796
|
+
id: { type: "string" },
|
|
4797
|
+
limit: { type: "string" },
|
|
4594
4798
|
peek: { type: "boolean" },
|
|
4595
4799
|
history: { type: "boolean" },
|
|
4596
4800
|
dissolve: { type: "boolean" },
|
|
@@ -4600,7 +4804,7 @@ async function runSession(argv) {
|
|
|
4600
4804
|
});
|
|
4601
4805
|
if (v.help) {
|
|
4602
4806
|
console.log(
|
|
4603
|
-
"cmdr session join|list|send|report|ask|read|leave --agent HOST --native-id ID [--input JSON] [--timeout SECONDS]\nUse --squad-name for join; --status and text for report; --to and text for send; text for ask. read supports --wait/--peek/--history. IDs must match the host session;
|
|
4807
|
+
"cmdr session join|list|send|report|ask|read|leave --agent HOST --native-id ID [--input JSON] [--timeout SECONDS]\nUse --squad-name for join; --status and text for report; --to and text for send; text for ask. read supports --wait/--peek/--history. IDs must match the host session; read also supports --recover/--id/--full/--limit. Configure automatic wake with cmdr standby start --session SID; unsupported hosts remain manual."
|
|
4604
4808
|
);
|
|
4605
4809
|
return;
|
|
4606
4810
|
}
|
|
@@ -4631,12 +4835,22 @@ async function runSession(argv) {
|
|
|
4631
4835
|
to: "to",
|
|
4632
4836
|
type: "type",
|
|
4633
4837
|
"reply-to": "reply_to",
|
|
4838
|
+
priority: "priority",
|
|
4839
|
+
"task-key": "task_key",
|
|
4840
|
+
reassign: "reassign",
|
|
4841
|
+
rebind: "rebind",
|
|
4842
|
+
takeover: "takeover",
|
|
4843
|
+
standby: "standby",
|
|
4844
|
+
full: "full",
|
|
4845
|
+
recover: "recover",
|
|
4846
|
+
id: "id",
|
|
4634
4847
|
peek: "peek",
|
|
4635
4848
|
history: "history",
|
|
4636
4849
|
dissolve: "dissolve"
|
|
4637
4850
|
};
|
|
4638
4851
|
for (const [flag, field] of Object.entries(mapping))
|
|
4639
4852
|
if (v[flag] !== void 0) input[field] = v[flag];
|
|
4853
|
+
if (v.limit !== void 0) input.limit = Number(v.limit);
|
|
4640
4854
|
if (v.all) input.scope = "all";
|
|
4641
4855
|
if (v.wait !== void 0) input.wait = Number(v.wait);
|
|
4642
4856
|
if (args.length > 1) input[action === "ask" ? "question" : "message"] = args.slice(1).join(" ");
|
|
@@ -4646,6 +4860,7 @@ async function runSession(argv) {
|
|
|
4646
4860
|
throw new Error("--timeout must be >0 and <=3600 seconds");
|
|
4647
4861
|
client = new DaemonClient({
|
|
4648
4862
|
kind: "mcp",
|
|
4863
|
+
transport: "cli",
|
|
4649
4864
|
agent,
|
|
4650
4865
|
native_id: native,
|
|
4651
4866
|
cwd: process.cwd(),
|
|
@@ -4794,7 +5009,7 @@ async function probeMcp(root) {
|
|
|
4794
5009
|
}
|
|
4795
5010
|
|
|
4796
5011
|
// src/cli/main.ts
|
|
4797
|
-
import { readFileSync as
|
|
5012
|
+
import { readFileSync as readFileSync3, existsSync } from "node:fs";
|
|
4798
5013
|
import { dirname as dirname2, join as join5, resolve as resolve2 } from "node:path";
|
|
4799
5014
|
import { homedir as homedir2 } from "node:os";
|
|
4800
5015
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -4814,6 +5029,23 @@ if (process.argv[2] === "session") {
|
|
|
4814
5029
|
to: { type: "string" },
|
|
4815
5030
|
type: { type: "string" },
|
|
4816
5031
|
"reply-to": { type: "string" },
|
|
5032
|
+
priority: { type: "string" },
|
|
5033
|
+
"task-key": { type: "string" },
|
|
5034
|
+
reassign: { type: "string" },
|
|
5035
|
+
after: { type: "string" },
|
|
5036
|
+
for: { type: "string" },
|
|
5037
|
+
adapter: { type: "string" },
|
|
5038
|
+
transport: { type: "string" },
|
|
5039
|
+
actionable: { type: "boolean" },
|
|
5040
|
+
once: { type: "boolean" },
|
|
5041
|
+
format: { type: "string" },
|
|
5042
|
+
executable: { type: "string" },
|
|
5043
|
+
socket: { type: "string" },
|
|
5044
|
+
resolve: { type: "string" },
|
|
5045
|
+
recover: { type: "boolean" },
|
|
5046
|
+
history: { type: "boolean" },
|
|
5047
|
+
id: { type: "string" },
|
|
5048
|
+
limit: { type: "string" },
|
|
4817
5049
|
session: { type: "string" },
|
|
4818
5050
|
peek: { type: "boolean" },
|
|
4819
5051
|
follow: { type: "boolean" },
|
|
@@ -4864,7 +5096,7 @@ if (process.argv[2] === "session") {
|
|
|
4864
5096
|
checks.daemon = "not running";
|
|
4865
5097
|
}
|
|
4866
5098
|
try {
|
|
4867
|
-
const conf =
|
|
5099
|
+
const conf = readFileSync3(
|
|
4868
5100
|
join5(process.env.CODEX_HOME || join5(homedir2(), ".codex"), "config.toml"),
|
|
4869
5101
|
"utf8"
|
|
4870
5102
|
);
|
|
@@ -4899,7 +5131,7 @@ if (process.argv[2] === "session") {
|
|
|
4899
5131
|
} else
|
|
4900
5132
|
checks.zcode = "Desktop app not found in /Applications; generic MCP configuration is available.";
|
|
4901
5133
|
try {
|
|
4902
|
-
const zconfig = JSON.parse(
|
|
5134
|
+
const zconfig = JSON.parse(readFileSync3(join5(homedir2(), ".zcode/cli/config.json"), "utf8"));
|
|
4903
5135
|
checks.zcode_user_hooks = zconfig.hooks?.enabled === true;
|
|
4904
5136
|
} catch {
|
|
4905
5137
|
checks.zcode_user_hooks = "No user hook config; installed plugin hooks follow plugin enablement.";
|
|
@@ -4909,7 +5141,7 @@ if (process.argv[2] === "session") {
|
|
|
4909
5141
|
try {
|
|
4910
5142
|
if (v.help)
|
|
4911
5143
|
process.stdout.write(
|
|
4912
|
-
"cmdr status | list [--all] [--squad ID] | tail [--follow] [--full] | send --squad ID [--to MEMBER] [--type command|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"
|
|
4913
5145
|
);
|
|
4914
5146
|
else if (cmd === "config") {
|
|
4915
5147
|
const agent = v.agent || "generic";
|
|
@@ -4933,7 +5165,8 @@ if (process.argv[2] === "session") {
|
|
|
4933
5165
|
else if (cmd === "list") {
|
|
4934
5166
|
const result = await call("session.list", {
|
|
4935
5167
|
scope: v.all ? "all" : void 0,
|
|
4936
|
-
squad: v.squad
|
|
5168
|
+
squad: v.squad,
|
|
5169
|
+
full: !!v.full
|
|
4937
5170
|
});
|
|
4938
5171
|
if (v.json) print(result);
|
|
4939
5172
|
else {
|
|
@@ -4945,11 +5178,16 @@ if (process.argv[2] === "session") {
|
|
|
4945
5178
|
title: s.title,
|
|
4946
5179
|
role: s.role,
|
|
4947
5180
|
squad: s.squad,
|
|
4948
|
-
|
|
5181
|
+
work: s.commands.map((m) => `${m.id}:${m.state}${m.cancel_requested_at ? ":cancelling" : ""}`).join(", "),
|
|
5182
|
+
unacked: s.unacked,
|
|
5183
|
+
in_progress: s.in_progress,
|
|
4949
5184
|
activity: s.activity,
|
|
5185
|
+
progress_seconds_ago: s.last_progress_at ? Math.floor((Date.now() - s.last_progress_at) / 1e3) : "unknown",
|
|
5186
|
+
connection: s.presence,
|
|
4950
5187
|
pending: s.pending,
|
|
5188
|
+
wake: `${s.listener.wake_mode}/${s.listener.health}`,
|
|
4951
5189
|
identity: s.native_id ? "confirmed" : "provisional",
|
|
4952
|
-
|
|
5190
|
+
seen_seconds_ago: Math.floor((Date.now() - s.last_seen) / 1e3),
|
|
4953
5191
|
cwd: s.cwd,
|
|
4954
5192
|
terminal: s.terminal?.program
|
|
4955
5193
|
}))
|
|
@@ -4972,6 +5210,9 @@ if (process.argv[2] === "session") {
|
|
|
4972
5210
|
to: v.to || "all",
|
|
4973
5211
|
type: v.type || "command",
|
|
4974
5212
|
reply_to: v["reply-to"],
|
|
5213
|
+
priority: v.priority,
|
|
5214
|
+
reassign: v.reassign,
|
|
5215
|
+
task_key: v["task-key"],
|
|
4975
5216
|
message: args.slice(1).join(" ")
|
|
4976
5217
|
},
|
|
4977
5218
|
true
|
|
@@ -4979,26 +5220,71 @@ if (process.argv[2] === "session") {
|
|
|
4979
5220
|
);
|
|
4980
5221
|
else if (cmd === "read") {
|
|
4981
5222
|
if (!v.session) throw new Error("--session SID is required");
|
|
4982
|
-
print(
|
|
5223
|
+
print(
|
|
5224
|
+
await call("admin.read", {
|
|
5225
|
+
sid: v.session,
|
|
5226
|
+
options: {
|
|
5227
|
+
peek: !!v.peek,
|
|
5228
|
+
history: !!v.history,
|
|
5229
|
+
full: !!v.full,
|
|
5230
|
+
recover: !!v.recover,
|
|
5231
|
+
id: v.id,
|
|
5232
|
+
limit: v.limit ? Number(v.limit) : void 0
|
|
5233
|
+
}
|
|
5234
|
+
})
|
|
5235
|
+
);
|
|
4983
5236
|
} else if (cmd === "purge") print(await call("admin.purge", { all: !!v.all }));
|
|
4984
|
-
else if (cmd === "
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5237
|
+
else if (cmd === "standby") {
|
|
5238
|
+
if (!v.session) throw new Error("--session SID is required");
|
|
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
|
+
);
|
|
5256
|
+
} else if (cmd === "tail") {
|
|
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");
|
|
5262
|
+
await tail({
|
|
5263
|
+
squad: v.squad,
|
|
5264
|
+
for: v.for,
|
|
5265
|
+
after,
|
|
5266
|
+
full: !!v.full,
|
|
5267
|
+
json: !!v.json || v.format === "json",
|
|
5268
|
+
line: v.format === "line",
|
|
5269
|
+
actionable: !!v.actionable,
|
|
5270
|
+
follow: !!v.follow
|
|
5271
|
+
});
|
|
4996
5272
|
} else if (cmd === "daemon") {
|
|
4997
5273
|
const action = args[1] || "status";
|
|
4998
5274
|
if (action === "logs")
|
|
4999
|
-
process.stdout.write(existsSync(p.log) ?
|
|
5275
|
+
process.stdout.write(existsSync(p.log) ? readFileSync3(p.log, "utf8") : "No daemon logs.\n");
|
|
5000
5276
|
else if (action === "status") print(await call("admin.status"));
|
|
5001
5277
|
else if (action === "stop" || action === "restart") {
|
|
5278
|
+
if (action === "restart")
|
|
5279
|
+
execFileSync(
|
|
5280
|
+
process.execPath,
|
|
5281
|
+
[
|
|
5282
|
+
"--experimental-sqlite",
|
|
5283
|
+
join5(dirname2(fileURLToPath3(import.meta.url)), "daemon.mjs"),
|
|
5284
|
+
"--preflight"
|
|
5285
|
+
],
|
|
5286
|
+
{ timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] }
|
|
5287
|
+
);
|
|
5002
5288
|
try {
|
|
5003
5289
|
print(await call("admin.shutdown", { reason: action }));
|
|
5004
5290
|
} catch (e) {
|