bazilion 0.10.0 → 0.12.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/dist/cli.js +1666 -266
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +4303 -522
- package/dist/daemon.js.map +1 -1
- package/dist/migrations/0001_init.sql +101 -0
- package/dist/web/client/assets/AgentTabs-R6gSjIj3.js +1 -0
- package/dist/web/client/assets/{ProductionTeamPolicyEditor-CiQ-VVDo.js → ProductionTeamPolicyEditor-CSL3W7XM.js} +2 -2
- package/dist/web/client/assets/{ProfileCommunicationEditor-D84F5yfr.js → ProfileCommunicationEditor-CwU0QkmE.js} +1 -1
- package/dist/web/client/assets/{TeamTabs-Btoe_FAE.js → TeamTabs-C1Hakkff.js} +1 -1
- package/dist/web/client/assets/{TemplatesTabs-CAYY03F5.js → TemplatesTabs-klEydJ8-.js} +1 -1
- package/dist/web/client/assets/{_id-BqU2BodK.js → _id-BOC7ioJu.js} +1 -1
- package/dist/web/client/assets/{_id-CKtoio3t.js → _id-CnVq-rED.js} +1 -1
- package/dist/web/client/assets/{_id-BeauECmk.js → _id-e4u7dmBb.js} +1 -1
- package/dist/web/client/assets/{_id-Q98Of6k9.js → _id-wkjjWLz0.js} +1 -1
- package/dist/web/client/assets/{activity-DXN_N85w.js → activity-uqhwzmiy.js} +1 -1
- package/dist/web/client/assets/{agents-C5Xsdge1.js → agents-B2usE-L_.js} +1 -1
- package/dist/web/client/assets/{agents-Cgxo4H5G.js → agents-FaQtkAWs.js} +1 -1
- package/dist/web/client/assets/{approvals-BnG7fbw-.js → approvals-DqLE6ttw.js} +1 -1
- package/dist/web/client/assets/attention-CkGozYnJ.js +1 -0
- package/dist/web/client/assets/attention-Gavt2yEp.js +1 -0
- package/dist/web/client/assets/chevron-down-CJk0zGNT.js +1 -0
- package/dist/web/client/assets/{config-xw4NyrXF.js → config-xrXoDEWM.js} +1 -1
- package/dist/web/client/assets/{context-D2V1Oi4z.js → context-ZWqx3uiC.js} +1 -1
- package/dist/web/client/assets/inbox-BoIvl9ZF.js +1 -0
- package/dist/web/client/assets/{index-VdURSoM4.js → index-nb2MV8X1.js} +3 -3
- package/dist/web/client/assets/learning-6_oYn1l7.js +1 -0
- package/dist/web/client/assets/{login-CvptUoMR.js → login-DM1ytxQL.js} +1 -1
- package/dist/web/client/assets/{mcp-BDFM_sW-.js → mcp-CetZbyUh.js} +1 -1
- package/dist/web/client/assets/{members-BJvph_TR.js → members-DdqWtwZd.js} +1 -1
- package/dist/web/client/assets/{memory-DRytqZCD.js → memory-FNrxFHmC.js} +1 -1
- package/dist/web/client/assets/plus-C3GroITC.js +1 -0
- package/dist/web/client/assets/{policy-CGP50OtQ.js → policy-BKPejYRI.js} +1 -1
- package/dist/web/client/assets/{routes-DJnWYvC9.js → routes-CH1ZTTo-.js} +1 -1
- package/dist/web/client/assets/{services-DHheiv7L.js → services-zk9SqN06.js} +1 -1
- package/dist/web/client/assets/{skills-DmsitluU.js → skills-tXDpomHW.js} +1 -1
- package/dist/web/client/assets/styles-DMltY3su.css +2 -0
- package/dist/web/client/assets/{teams-BdC4gthq.js → teams-BZYjekBN.js} +1 -1
- package/dist/web/client/assets/{teams-CbxSaorm.js → teams-D1c8zlxs.js} +1 -1
- package/dist/web/client/assets/telegram-DCe1CWMt.js +1 -0
- package/dist/web/client/assets/{tokens-CjRplDrJ.js → tokens-DAPjRS0N.js} +1 -1
- package/dist/web/client/assets/triggers-D8R-5guA.js +1 -0
- package/dist/web/client/assets/{welcome-wBfX9dAd.js → welcome-CuK0shDJ.js} +1 -1
- package/dist/web/client/assets/wire-constants-BhVKOCQa.js +74 -0
- package/dist/web/server/assets/{AgentTabs-DoMDJBXH.js → AgentTabs-aXdsiiMp.js} +5 -0
- package/dist/web/server/assets/{ChatPane-eUY_dga3.js → ChatPane-Ba5wTsVk.js} +325 -36
- package/dist/web/server/assets/{_id-ClN9hY2x.js → _id-BHYLL8M-.js} +3 -3
- package/dist/web/server/assets/{_id-DkYodSkG.js → _id-FcHdSocl.js} +1 -1
- package/dist/web/server/assets/{_tanstack-start-manifest_v-D09Xu_6u.js → _tanstack-start-manifest_v-fLsYERQ-.js} +77 -54
- package/dist/web/server/assets/attention-BW_6ddkj.js +11 -0
- package/dist/web/server/assets/attention-BxkTf337.js +14 -0
- package/dist/web/server/assets/attention-CM-Ks1N9.js +19 -0
- package/dist/web/server/assets/attention-w21GQIGH.js +264 -0
- package/dist/web/server/assets/{inbox-CUIyf9MT.js → inbox-BLUzA5sQ.js} +40 -3
- package/dist/web/server/assets/{inbox-B3-TRsNI.js → inbox-CzlKT9Wg.js} +5 -3
- package/dist/web/server/assets/{inbox-BardsGyy.js → inbox-ZvDNAjaM.js} +1 -1
- package/dist/web/server/assets/learning-8zav4pIT.js +33 -0
- package/dist/web/server/assets/learning-B7PBkvER.js +16 -0
- package/dist/web/server/assets/learning-Cq7K2heb.js +312 -0
- package/dist/web/server/assets/{router-DIdp8ZNt.js → router-CQuj7553.js} +81 -48
- package/dist/web/server/assets/{routes-Jb0zjKav.js → routes-B6cMLIPk.js} +3 -3
- package/dist/web/server/assets/{routes-CXz4hTAr.js → routes-BrA-U_Hm.js} +1 -1
- package/dist/web/server/assets/{routes-lIclH9Br.js → routes-C19CMLZt.js} +1 -1
- package/dist/web/server/assets/{telegram-7ub--6xz.js → telegram-CBF8SJzB.js} +1 -24
- package/dist/web/server/assets/{telegram-Bazhmpr4.js → telegram-XwyKSEtj.js} +1 -1
- package/dist/web/server/assets/{triggers-Ca57_qym.js → triggers-BZPl5CHs.js} +1 -1
- package/dist/web/server/assets/{triggers-BMpHsGIr.js → triggers-CSNNzRIi.js} +28 -4
- package/dist/web/server/assets/{triggers-C_0y6QV9.js → triggers-D79NoSXk.js} +3 -2
- package/dist/web/server/server.js +13 -5
- package/dist/worker.js +1752 -148
- package/dist/worker.js.map +1 -1
- package/package.json +4 -3
- package/dist/web/client/assets/AgentTabs-B3hq5a3e.js +0 -1
- package/dist/web/client/assets/chevron-down-BhcBspw3.js +0 -1
- package/dist/web/client/assets/inbox-DK6hWCMp.js +0 -1
- package/dist/web/client/assets/plus-C9ee3SIO.js +0 -1
- package/dist/web/client/assets/styles-B5eQAwLP.css +0 -2
- package/dist/web/client/assets/telegram-D61G_alO.js +0 -1
- package/dist/web/client/assets/triggers-MLzTm_DM.js +0 -1
- package/dist/web/client/assets/wire-constants-BxfyUMXB.js +0 -74
- package/dist/web/server/assets/{Sidebar-O_CH1-JD.js → Sidebar-DsRVCszF.js} +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { defineCommand as
|
|
4
|
+
import { defineCommand as defineCommand26, runCommand, showUsage } from "citty";
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "bazilion",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.12.0",
|
|
10
10
|
description: "Multi-agent runtime CLI \u2014 spawn LLM agents, manage profiles/teams/skills, and run the local daemon.",
|
|
11
11
|
license: "MIT",
|
|
12
12
|
repository: {
|
|
@@ -68,6 +68,7 @@ var package_default = {
|
|
|
68
68
|
"qrcode-terminal": "^0.12.0",
|
|
69
69
|
react: "^19.2.7",
|
|
70
70
|
"react-dom": "^19.2.7",
|
|
71
|
+
tar: "^7.5.16",
|
|
71
72
|
typebox: "^1.3.0",
|
|
72
73
|
undici: "^8.5.0",
|
|
73
74
|
yaml: "^2.9.0"
|
|
@@ -372,12 +373,12 @@ var listCmd = defineCommand({
|
|
|
372
373
|
async run({ args }) {
|
|
373
374
|
const client = createClient2();
|
|
374
375
|
const q = args.all ? "?includeArchived=true" : "";
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
376
|
+
const list5 = await client.get(`/api/agents${q}`);
|
|
377
|
+
if (list5.length === 0) {
|
|
377
378
|
console.log("(no agents)");
|
|
378
379
|
return;
|
|
379
380
|
}
|
|
380
|
-
const rows =
|
|
381
|
+
const rows = list5.map(
|
|
381
382
|
(a) => args.long ? [a.id, a.status, a.name, a.profileId] : (
|
|
382
383
|
// Short UUID prefix — all CLI commands accept 4+ char prefixes now.
|
|
383
384
|
[a.id.slice(0, 8), a.status, a.name]
|
|
@@ -448,6 +449,74 @@ var deleteCmd = defineCommand({
|
|
|
448
449
|
console.log(`deleted agent ${args.id}`);
|
|
449
450
|
}
|
|
450
451
|
});
|
|
452
|
+
function bashApprovalModeForTty(inputIsTty, outputIsTty) {
|
|
453
|
+
return inputIsTty && outputIsTty ? "interactive" : "auto_deny";
|
|
454
|
+
}
|
|
455
|
+
function buildChatRequest(message, attachments, bashApprovalMode) {
|
|
456
|
+
return {
|
|
457
|
+
message,
|
|
458
|
+
bashApprovalMode,
|
|
459
|
+
...attachments && attachments.length > 0 ? { attachments } : {}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function terminalSafe(value) {
|
|
463
|
+
let safe = "";
|
|
464
|
+
for (const character of value) {
|
|
465
|
+
const code = character.charCodeAt(0);
|
|
466
|
+
const isControl = code <= 9 || code >= 11 && code <= 31 || code >= 127 && code <= 159;
|
|
467
|
+
safe += isControl ? `\\x${code.toString(16).padStart(2, "0")}` : character;
|
|
468
|
+
}
|
|
469
|
+
return safe;
|
|
470
|
+
}
|
|
471
|
+
async function promptForCommandApproval(approval, prompt) {
|
|
472
|
+
prompt.write(" [shell command approval required]");
|
|
473
|
+
prompt.write(" command:");
|
|
474
|
+
for (const line of approval.command.split("\n")) prompt.write(` ${terminalSafe(line)}`);
|
|
475
|
+
prompt.write(" risks:");
|
|
476
|
+
for (const risk of approval.risks) {
|
|
477
|
+
prompt.write(
|
|
478
|
+
` - ${terminalSafe(risk.code)} (${terminalSafe(risk.severity)}): ${terminalSafe(risk.message)}`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
const answer = (await prompt.question(" Allow this command once? [y/N] ")).trim().toLowerCase();
|
|
483
|
+
return answer === "y" || answer === "yes" ? "allow" : "deny";
|
|
484
|
+
} catch {
|
|
485
|
+
prompt.write(" [approval input unavailable; denying]");
|
|
486
|
+
return "deny";
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async function respondToCommandApproval(client, approval, prompt) {
|
|
490
|
+
const decision2 = prompt ? await promptForCommandApproval(approval, prompt) : "deny";
|
|
491
|
+
await client.post(`/api/shell-approvals/${encodeURIComponent(approval.id)}`, { decision: decision2 });
|
|
492
|
+
return decision2;
|
|
493
|
+
}
|
|
494
|
+
function closeDeltaLine(state) {
|
|
495
|
+
if (!state.inDeltaStream) return;
|
|
496
|
+
process.stdout.write("\n");
|
|
497
|
+
state.inDeltaStream = false;
|
|
498
|
+
}
|
|
499
|
+
function printCommandApprovalStatus(approval) {
|
|
500
|
+
switch (approval.status) {
|
|
501
|
+
case "allowed":
|
|
502
|
+
console.log(" [shell command allowed]");
|
|
503
|
+
break;
|
|
504
|
+
case "denied":
|
|
505
|
+
console.log(" [shell command denied]");
|
|
506
|
+
break;
|
|
507
|
+
case "auto_denied":
|
|
508
|
+
console.log(" [shell command auto-denied: non-interactive turn]");
|
|
509
|
+
break;
|
|
510
|
+
case "expired":
|
|
511
|
+
console.log(" [shell command approval expired]");
|
|
512
|
+
break;
|
|
513
|
+
case "cancelled":
|
|
514
|
+
console.log(" [shell command approval cancelled]");
|
|
515
|
+
break;
|
|
516
|
+
case "pending":
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
451
520
|
function printEvent(e, state) {
|
|
452
521
|
switch (e.type) {
|
|
453
522
|
case "assistant_delta":
|
|
@@ -494,16 +563,32 @@ function printEvent(e, state) {
|
|
|
494
563
|
break;
|
|
495
564
|
}
|
|
496
565
|
}
|
|
497
|
-
async function streamTurn(client, agentId, message, attachments) {
|
|
566
|
+
async function streamTurn(client, agentId, message, attachments, options = { bashApprovalMode: "auto_deny" }) {
|
|
498
567
|
const state = { inDeltaStream: false };
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
568
|
+
const respondedApprovalIds = /* @__PURE__ */ new Set();
|
|
569
|
+
for await (const frame of client.stream(
|
|
570
|
+
"POST",
|
|
571
|
+
`/api/agents/${agentId}/chat`,
|
|
572
|
+
buildChatRequest(message, attachments, options.bashApprovalMode)
|
|
573
|
+
)) {
|
|
503
574
|
if (frame.kind === "event") {
|
|
504
|
-
|
|
575
|
+
const event = frame.event;
|
|
576
|
+
if (event.type === "command_approval") {
|
|
577
|
+
closeDeltaLine(state);
|
|
578
|
+
const approval = event.approval;
|
|
579
|
+
if (approval.status === "pending") {
|
|
580
|
+
if (!respondedApprovalIds.has(approval.id)) {
|
|
581
|
+
respondedApprovalIds.add(approval.id);
|
|
582
|
+
await respondToCommandApproval(client, approval, options.approvalPrompt);
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
printCommandApprovalStatus(approval);
|
|
586
|
+
}
|
|
587
|
+
} else if (event.type !== "user_message") {
|
|
588
|
+
printEvent(event, state);
|
|
589
|
+
}
|
|
505
590
|
} else if (frame.kind === "fatal") {
|
|
506
|
-
|
|
591
|
+
closeDeltaLine(state);
|
|
507
592
|
throw new Error(frame.error);
|
|
508
593
|
}
|
|
509
594
|
}
|
|
@@ -528,34 +613,78 @@ var chatCmd = defineCommand({
|
|
|
528
613
|
async run({ args }) {
|
|
529
614
|
const client = createClient2();
|
|
530
615
|
const resolved = await client.get(`/api/agents/${args.id}`);
|
|
616
|
+
const bashApprovalMode = bashApprovalModeForTty(stdin.isTTY, stdout.isTTY);
|
|
531
617
|
const attachments = [
|
|
532
618
|
...loadImages(asPaths(args.image)),
|
|
533
619
|
...loadFiles(asPaths(args.file))
|
|
534
620
|
];
|
|
535
621
|
if (args.message || attachments.length > 0) {
|
|
536
|
-
|
|
622
|
+
const rl2 = bashApprovalMode === "interactive" ? createInterface({ input: stdin, output: stdout }) : null;
|
|
623
|
+
try {
|
|
624
|
+
await streamTurn(client, resolved.agent.id, args.message ?? "", attachments, {
|
|
625
|
+
bashApprovalMode,
|
|
626
|
+
...rl2 ? {
|
|
627
|
+
approvalPrompt: {
|
|
628
|
+
question: (question) => rl2.question(question),
|
|
629
|
+
write: (line) => console.log(line)
|
|
630
|
+
}
|
|
631
|
+
} : {}
|
|
632
|
+
});
|
|
633
|
+
} finally {
|
|
634
|
+
rl2?.close();
|
|
635
|
+
}
|
|
537
636
|
return;
|
|
538
637
|
}
|
|
539
638
|
console.log(`chatting with ${resolved.agent.name} (${resolved.model})`);
|
|
540
639
|
console.log("(type /exit to quit)");
|
|
541
640
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
641
|
+
try {
|
|
642
|
+
if (bashApprovalMode === "interactive") {
|
|
643
|
+
while (true) {
|
|
644
|
+
let line;
|
|
645
|
+
try {
|
|
646
|
+
line = await rl.question("> ");
|
|
647
|
+
} catch {
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
const trimmed = line.trim();
|
|
651
|
+
if (trimmed === "/exit" || trimmed === "/quit") break;
|
|
652
|
+
if (!trimmed) continue;
|
|
653
|
+
try {
|
|
654
|
+
await streamTurn(client, resolved.agent.id, trimmed, void 0, {
|
|
655
|
+
bashApprovalMode,
|
|
656
|
+
approvalPrompt: {
|
|
657
|
+
question: (question) => rl.question(question),
|
|
658
|
+
write: (output) => console.log(output)
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
} catch (err) {
|
|
662
|
+
console.error(`error: ${err.message}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
rl.setPrompt("> ");
|
|
548
667
|
rl.prompt();
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
668
|
+
for await (const line of rl) {
|
|
669
|
+
const trimmed = line.trim();
|
|
670
|
+
if (trimmed === "/exit" || trimmed === "/quit") break;
|
|
671
|
+
if (!trimmed) {
|
|
672
|
+
rl.prompt();
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
await streamTurn(client, resolved.agent.id, trimmed, void 0, {
|
|
677
|
+
bashApprovalMode
|
|
678
|
+
});
|
|
679
|
+
} catch (err) {
|
|
680
|
+
console.error(`error: ${err.message}`);
|
|
681
|
+
}
|
|
682
|
+
rl.prompt();
|
|
683
|
+
}
|
|
555
684
|
}
|
|
556
|
-
|
|
685
|
+
} finally {
|
|
686
|
+
rl.close();
|
|
557
687
|
}
|
|
558
|
-
rl.close();
|
|
559
688
|
}
|
|
560
689
|
});
|
|
561
690
|
var chatResetCmd = defineCommand({
|
|
@@ -567,8 +696,8 @@ var chatResetCmd = defineCommand({
|
|
|
567
696
|
async run({ args }) {
|
|
568
697
|
if (!args.force) {
|
|
569
698
|
process.stdout.write(`reset all chat history for ${args.id}? [y/N] `);
|
|
570
|
-
const answer = await new Promise((
|
|
571
|
-
process.stdin.once("data", (d) =>
|
|
699
|
+
const answer = await new Promise((resolve4) => {
|
|
700
|
+
process.stdin.once("data", (d) => resolve4(String(d).trim().toLowerCase()));
|
|
572
701
|
});
|
|
573
702
|
if (answer !== "y" && answer !== "yes") {
|
|
574
703
|
console.log("aborted");
|
|
@@ -716,8 +845,8 @@ var chatCompactCmd = defineCommand({
|
|
|
716
845
|
process.stdout.write(
|
|
717
846
|
`compact chat history for ${args.id}? head turns become a summary; tail is preserved. [y/N] `
|
|
718
847
|
);
|
|
719
|
-
const answer = await new Promise((
|
|
720
|
-
process.stdin.once("data", (d) =>
|
|
848
|
+
const answer = await new Promise((resolve4) => {
|
|
849
|
+
process.stdin.once("data", (d) => resolve4(String(d).trim().toLowerCase()));
|
|
721
850
|
});
|
|
722
851
|
if (answer !== "y" && answer !== "yes") {
|
|
723
852
|
console.log("aborted");
|
|
@@ -856,6 +985,159 @@ var cancelCmd = defineCommand({
|
|
|
856
985
|
console.log(`cancelled active turn for ${args.id}`);
|
|
857
986
|
}
|
|
858
987
|
});
|
|
988
|
+
var reviewConfigCmd = defineCommand({
|
|
989
|
+
meta: { name: "review-config", description: "Inspect or configure reviewed learning" },
|
|
990
|
+
args: {
|
|
991
|
+
id: { type: "positional", required: true, description: "Agent id or prefix" },
|
|
992
|
+
enable: { type: "boolean", description: "Enable periodic reviews" },
|
|
993
|
+
disable: { type: "boolean", description: "Disable periodic reviews" },
|
|
994
|
+
every: { type: "string", description: "Successful user turns between reviews (1-100)" },
|
|
995
|
+
model: { type: "string", description: "Review model, or 'agent' to use the Agent model" },
|
|
996
|
+
reasoning: { type: "string", description: "Review reasoning level" }
|
|
997
|
+
},
|
|
998
|
+
async run({ args }) {
|
|
999
|
+
if (args.enable && args.disable) throw new Error("choose either --enable or --disable");
|
|
1000
|
+
const client = createClient2();
|
|
1001
|
+
const body = {};
|
|
1002
|
+
if (args.enable || args.disable) body.enabled = Boolean(args.enable);
|
|
1003
|
+
if (args.every !== void 0) {
|
|
1004
|
+
const value = Number(args.every);
|
|
1005
|
+
if (!Number.isInteger(value) || value < 1 || value > 100) {
|
|
1006
|
+
throw new Error("--every must be an integer from 1 to 100");
|
|
1007
|
+
}
|
|
1008
|
+
body.everyNTurns = value;
|
|
1009
|
+
}
|
|
1010
|
+
if (args.model !== void 0) body.model = args.model === "agent" ? null : args.model;
|
|
1011
|
+
if (args.reasoning !== void 0) {
|
|
1012
|
+
body.reasoningLevel = args.reasoning;
|
|
1013
|
+
}
|
|
1014
|
+
const hasChanges = Object.keys(body).length > 0;
|
|
1015
|
+
const config = hasChanges ? await client.patch(`/api/agents/${args.id}/review-config`, body) : await client.get(`/api/agents/${args.id}/review-config`);
|
|
1016
|
+
console.log(`review: ${config.enabled ? "enabled" : "disabled"}`);
|
|
1017
|
+
console.log(`cadence: every ${config.everyNTurns} successful user turns`);
|
|
1018
|
+
console.log(`model: ${config.model ?? "agent model"}`);
|
|
1019
|
+
console.log(`reasoning: ${config.reasoningLevel}`);
|
|
1020
|
+
console.log(`turns since last review: ${config.turnsSinceLast}`);
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
var reviewCmd = defineCommand({
|
|
1024
|
+
meta: { name: "review", description: "Enqueue a reviewed-learning pass" },
|
|
1025
|
+
args: { id: { type: "positional", required: true, description: "Agent id or prefix" } },
|
|
1026
|
+
async run({ args }) {
|
|
1027
|
+
const response = await createClient2().post(
|
|
1028
|
+
`/api/agents/${args.id}/reviews`
|
|
1029
|
+
);
|
|
1030
|
+
console.log(`review ${response.review.id} ${response.review.status}`);
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
var reviewsCmd = defineCommand({
|
|
1034
|
+
meta: { name: "reviews", description: "List reviewed-learning history" },
|
|
1035
|
+
args: {
|
|
1036
|
+
id: { type: "positional", required: true, description: "Agent id or prefix" },
|
|
1037
|
+
status: { type: "string", description: "pending|running|retrying|completed|failed|cancelled" }
|
|
1038
|
+
},
|
|
1039
|
+
async run({ args }) {
|
|
1040
|
+
const response = await createClient2().get(
|
|
1041
|
+
`/api/agents/${args.id}/reviews`
|
|
1042
|
+
);
|
|
1043
|
+
const reviews = args.status ? response.reviews.filter((review) => review.status === args.status) : response.reviews;
|
|
1044
|
+
if (reviews.length === 0) return console.log("(no reviews)");
|
|
1045
|
+
for (const line of columnize(
|
|
1046
|
+
reviews.map((review) => [
|
|
1047
|
+
review.id,
|
|
1048
|
+
review.status,
|
|
1049
|
+
review.trigger,
|
|
1050
|
+
`${review.proposalCount} proposal(s)`,
|
|
1051
|
+
new Date(review.createdAt).toISOString()
|
|
1052
|
+
])
|
|
1053
|
+
)) {
|
|
1054
|
+
console.log(line);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
var lessonsCmd = defineCommand({
|
|
1059
|
+
meta: { name: "lessons", description: "List reviewed lesson proposals" },
|
|
1060
|
+
args: {
|
|
1061
|
+
id: { type: "positional", required: true, description: "Agent id or prefix" },
|
|
1062
|
+
status: { type: "string", description: "pending|approved|rejected|revoked" }
|
|
1063
|
+
},
|
|
1064
|
+
async run({ args }) {
|
|
1065
|
+
const status = args.status;
|
|
1066
|
+
const query = status ? `?status=${encodeURIComponent(status)}` : "";
|
|
1067
|
+
const response = await createClient2().get(
|
|
1068
|
+
`/api/agents/${args.id}/lesson-proposals${query}`
|
|
1069
|
+
);
|
|
1070
|
+
if (response.proposals.length === 0) return console.log("(no lesson proposals)");
|
|
1071
|
+
for (const line of columnize(
|
|
1072
|
+
response.proposals.map((proposal) => [
|
|
1073
|
+
proposal.id,
|
|
1074
|
+
proposal.status,
|
|
1075
|
+
proposal.scope,
|
|
1076
|
+
proposal.text
|
|
1077
|
+
])
|
|
1078
|
+
)) {
|
|
1079
|
+
console.log(line);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
async function locateProposal(id) {
|
|
1084
|
+
const client = createClient2();
|
|
1085
|
+
const agents = await client.get("/api/agents?includeArchived=true");
|
|
1086
|
+
for (const agent of agents) {
|
|
1087
|
+
const response = await client.get(
|
|
1088
|
+
`/api/agents/${agent.id}/lesson-proposals`
|
|
1089
|
+
);
|
|
1090
|
+
const proposal = response.proposals.find((item) => item.id === id || item.id.startsWith(id));
|
|
1091
|
+
if (proposal) return { agent, proposal };
|
|
1092
|
+
}
|
|
1093
|
+
throw new Error(`lesson proposal not found: ${id}`);
|
|
1094
|
+
}
|
|
1095
|
+
function lessonDecisionCmd(decision2) {
|
|
1096
|
+
return defineCommand({
|
|
1097
|
+
meta: { name: decision2, description: `${decision2} a reviewed lesson proposal` },
|
|
1098
|
+
args: {
|
|
1099
|
+
id: { type: "positional", required: true, description: "Proposal id or prefix" },
|
|
1100
|
+
yes: { type: "boolean", description: `Confirm ${decision2}` }
|
|
1101
|
+
},
|
|
1102
|
+
async run({ args }) {
|
|
1103
|
+
if (!args.yes) throw new Error(`${decision2} requires --yes`);
|
|
1104
|
+
const { agent, proposal } = await locateProposal(args.id);
|
|
1105
|
+
const response = await createClient2().post(
|
|
1106
|
+
`/api/agents/${agent.id}/lesson-proposals/${proposal.id}/${decision2}`,
|
|
1107
|
+
{ version: proposal.version }
|
|
1108
|
+
);
|
|
1109
|
+
console.log(`${response.proposal.status} ${response.proposal.id}`);
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
var lessonEditCmd = defineCommand({
|
|
1114
|
+
meta: { name: "edit", description: "Edit a pending reviewed lesson proposal" },
|
|
1115
|
+
args: {
|
|
1116
|
+
id: { type: "positional", required: true, description: "Proposal id or prefix" },
|
|
1117
|
+
text: { type: "string", description: "Replacement lesson text" },
|
|
1118
|
+
scope: { type: "string", description: "private|shared" }
|
|
1119
|
+
},
|
|
1120
|
+
async run({ args }) {
|
|
1121
|
+
if (args.text === void 0 && args.scope === void 0) {
|
|
1122
|
+
throw new Error("lesson edit requires --text or --scope");
|
|
1123
|
+
}
|
|
1124
|
+
const { agent, proposal } = await locateProposal(args.id);
|
|
1125
|
+
const response = await createClient2().patch(
|
|
1126
|
+
`/api/agents/${agent.id}/lesson-proposals/${proposal.id}`,
|
|
1127
|
+
{ version: proposal.version, text: args.text, scope: args.scope }
|
|
1128
|
+
);
|
|
1129
|
+
console.log(`updated ${response.proposal.id} v${response.proposal.version}`);
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
var lessonCmd = defineCommand({
|
|
1133
|
+
meta: { name: "lesson", description: "Edit or decide a reviewed lesson proposal" },
|
|
1134
|
+
subCommands: {
|
|
1135
|
+
edit: lessonEditCmd,
|
|
1136
|
+
approve: lessonDecisionCmd("approve"),
|
|
1137
|
+
reject: lessonDecisionCmd("reject"),
|
|
1138
|
+
revoke: lessonDecisionCmd("revoke")
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
859
1141
|
var agentCommand = defineCommand({
|
|
860
1142
|
meta: { name: "agent", description: "Manage agent instances" },
|
|
861
1143
|
subCommands: {
|
|
@@ -872,6 +1154,11 @@ var agentCommand = defineCommand({
|
|
|
872
1154
|
"chat-context": chatContextCmd,
|
|
873
1155
|
"chat-compact": chatCompactCmd,
|
|
874
1156
|
cancel: cancelCmd,
|
|
1157
|
+
"review-config": reviewConfigCmd,
|
|
1158
|
+
review: reviewCmd,
|
|
1159
|
+
reviews: reviewsCmd,
|
|
1160
|
+
lessons: lessonsCmd,
|
|
1161
|
+
lesson: lessonCmd,
|
|
875
1162
|
skill: skillCmd,
|
|
876
1163
|
move: moveCmd,
|
|
877
1164
|
"session-head": sessionHeadCmd
|
|
@@ -965,15 +1252,101 @@ var approvalCommand = defineCommand2({
|
|
|
965
1252
|
}
|
|
966
1253
|
});
|
|
967
1254
|
|
|
1255
|
+
// src/commands/attention.ts
|
|
1256
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
1257
|
+
var list2 = defineCommand3({
|
|
1258
|
+
meta: { name: "list", description: "List operator attention items" },
|
|
1259
|
+
args: {
|
|
1260
|
+
state: { type: "string", default: "open" },
|
|
1261
|
+
kind: { type: "string" },
|
|
1262
|
+
json: { type: "boolean" }
|
|
1263
|
+
},
|
|
1264
|
+
async run({ args }) {
|
|
1265
|
+
const query = new URLSearchParams({ state: args.state, limit: "200" });
|
|
1266
|
+
if (args.kind) query.set("kind", args.kind);
|
|
1267
|
+
const body = await createClient2().get(`/api/attention?${query}`);
|
|
1268
|
+
if (args.json) return console.log(JSON.stringify(body, null, 2));
|
|
1269
|
+
if (!body.items.length) console.log("(nothing needs attention)");
|
|
1270
|
+
else
|
|
1271
|
+
for (const line of columnize([
|
|
1272
|
+
["severity", "kind", "context", "when", "key"],
|
|
1273
|
+
...body.items.map((item) => [
|
|
1274
|
+
item.severity,
|
|
1275
|
+
item.kind,
|
|
1276
|
+
context(item),
|
|
1277
|
+
new Date(item.occurredAt).toISOString(),
|
|
1278
|
+
item.key
|
|
1279
|
+
])
|
|
1280
|
+
]))
|
|
1281
|
+
console.log(line);
|
|
1282
|
+
for (const source of body.degraded)
|
|
1283
|
+
console.error(`warning: ${source.kind} unavailable: ${source.error}`);
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
var summary = defineCommand3({
|
|
1287
|
+
meta: { name: "summary", description: "Summarize open attention items" },
|
|
1288
|
+
args: { json: { type: "boolean" } },
|
|
1289
|
+
async run({ args }) {
|
|
1290
|
+
const body = await createClient2().get("/api/attention/summary");
|
|
1291
|
+
if (args.json) console.log(JSON.stringify(body, null, 2));
|
|
1292
|
+
else
|
|
1293
|
+
console.log(
|
|
1294
|
+
`${body.openTotal} open \xB7 ${body.bySeverity.action_required} action required \xB7 ${body.bySeverity.error} errors \xB7 ${body.bySeverity.warning} warnings`
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
});
|
|
1298
|
+
function mutation(name) {
|
|
1299
|
+
return defineCommand3({
|
|
1300
|
+
meta: { name, description: `${name} one informational attention item` },
|
|
1301
|
+
args: {
|
|
1302
|
+
key: { type: "positional", required: true },
|
|
1303
|
+
yes: { type: "boolean", description: "Required confirmation" },
|
|
1304
|
+
json: { type: "boolean" }
|
|
1305
|
+
},
|
|
1306
|
+
async run({ args }) {
|
|
1307
|
+
if (!args.yes) throw new Error(`refusing mutation: pass --yes to ${name}`);
|
|
1308
|
+
const path = `/api/attention/${encodeURIComponent(args.key)}/${name === "acknowledge" ? "acknowledge" : "acknowledgement"}`;
|
|
1309
|
+
const body = name === "acknowledge" ? await createClient2().post(path) : await createClient2().del(path);
|
|
1310
|
+
if (args.json) console.log(JSON.stringify(body, null, 2));
|
|
1311
|
+
else console.log(`${body.item.key}: ${body.item.acknowledgedAt ? "acknowledged" : "open"}`);
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
var acknowledgeAll = defineCommand3({
|
|
1316
|
+
meta: { name: "acknowledge-all", description: "Acknowledge all open informational items" },
|
|
1317
|
+
args: { yes: { type: "boolean" }, json: { type: "boolean" } },
|
|
1318
|
+
async run({ args }) {
|
|
1319
|
+
if (!args.yes) throw new Error("refusing mutation: pass --yes to acknowledge-all");
|
|
1320
|
+
const body = await createClient2().post(
|
|
1321
|
+
"/api/attention/acknowledge-all"
|
|
1322
|
+
);
|
|
1323
|
+
if (args.json) console.log(JSON.stringify(body, null, 2));
|
|
1324
|
+
else console.log(`acknowledged ${body.acknowledged} informational item(s)`);
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
function context(item) {
|
|
1328
|
+
return [item.agentName, item.teamName].filter(Boolean).join(" / ") || "system";
|
|
1329
|
+
}
|
|
1330
|
+
var attentionCommand = defineCommand3({
|
|
1331
|
+
meta: { name: "attention", description: "Inspect the operator attention center" },
|
|
1332
|
+
subCommands: {
|
|
1333
|
+
list: list2,
|
|
1334
|
+
summary,
|
|
1335
|
+
acknowledge: mutation("acknowledge"),
|
|
1336
|
+
unacknowledge: mutation("unacknowledge"),
|
|
1337
|
+
"acknowledge-all": acknowledgeAll
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
|
|
968
1341
|
// src/commands/auth.ts
|
|
969
1342
|
import { loginOpenAICodex } from "@earendil-works/pi-ai/oauth";
|
|
970
|
-
import { defineCommand as
|
|
1343
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
971
1344
|
function formatExpiry(ms) {
|
|
972
1345
|
if (!ms) return "(unknown)";
|
|
973
1346
|
const d = new Date(ms);
|
|
974
1347
|
return d.toISOString().replace(/\.\d+Z$/, "Z");
|
|
975
1348
|
}
|
|
976
|
-
var openaiLoginCmd =
|
|
1349
|
+
var openaiLoginCmd = defineCommand4({
|
|
977
1350
|
meta: {
|
|
978
1351
|
name: "login",
|
|
979
1352
|
description: "Connect your ChatGPT account via OAuth (browser sign-in)"
|
|
@@ -990,8 +1363,8 @@ var openaiLoginCmd = defineCommand3({
|
|
|
990
1363
|
},
|
|
991
1364
|
onPrompt: async (prompt) => {
|
|
992
1365
|
process.stdout.write(`${prompt.message}: `);
|
|
993
|
-
return new Promise((
|
|
994
|
-
process.stdin.once("data", (d) =>
|
|
1366
|
+
return new Promise((resolve4) => {
|
|
1367
|
+
process.stdin.once("data", (d) => resolve4(String(d).trim()));
|
|
995
1368
|
});
|
|
996
1369
|
},
|
|
997
1370
|
onProgress: (msg) => {
|
|
@@ -1015,7 +1388,7 @@ var openaiLoginCmd = defineCommand3({
|
|
|
1015
1388
|
console.log(" bazilion provider models-set openai-codex gpt-5.3-codex-spark");
|
|
1016
1389
|
}
|
|
1017
1390
|
});
|
|
1018
|
-
var openaiLogoutCmd =
|
|
1391
|
+
var openaiLogoutCmd = defineCommand4({
|
|
1019
1392
|
meta: {
|
|
1020
1393
|
name: "logout",
|
|
1021
1394
|
description: "Forget the stored ChatGPT OAuth credentials"
|
|
@@ -1026,7 +1399,7 @@ var openaiLogoutCmd = defineCommand3({
|
|
|
1026
1399
|
console.log("disconnected ChatGPT OAuth credentials");
|
|
1027
1400
|
}
|
|
1028
1401
|
});
|
|
1029
|
-
var openaiStatusCmd =
|
|
1402
|
+
var openaiStatusCmd = defineCommand4({
|
|
1030
1403
|
meta: {
|
|
1031
1404
|
name: "status",
|
|
1032
1405
|
description: "Show ChatGPT OAuth connection status"
|
|
@@ -1043,7 +1416,7 @@ var openaiStatusCmd = defineCommand3({
|
|
|
1043
1416
|
console.log(`access token expires: ${formatExpiry(status.expiresAt)}`);
|
|
1044
1417
|
}
|
|
1045
1418
|
});
|
|
1046
|
-
var openaiCmd =
|
|
1419
|
+
var openaiCmd = defineCommand4({
|
|
1047
1420
|
meta: {
|
|
1048
1421
|
name: "openai",
|
|
1049
1422
|
description: "Manage ChatGPT (OAuth) authentication"
|
|
@@ -1054,7 +1427,7 @@ var openaiCmd = defineCommand3({
|
|
|
1054
1427
|
status: openaiStatusCmd
|
|
1055
1428
|
}
|
|
1056
1429
|
});
|
|
1057
|
-
var authCommand =
|
|
1430
|
+
var authCommand = defineCommand4({
|
|
1058
1431
|
meta: {
|
|
1059
1432
|
name: "auth",
|
|
1060
1433
|
description: "OAuth-based provider authentication (ChatGPT account, etc.)"
|
|
@@ -1065,13 +1438,448 @@ var authCommand = defineCommand3({
|
|
|
1065
1438
|
});
|
|
1066
1439
|
|
|
1067
1440
|
// src/commands/backup.ts
|
|
1068
|
-
import {
|
|
1069
|
-
import {
|
|
1070
|
-
|
|
1441
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
1442
|
+
import {
|
|
1443
|
+
chmodSync as chmodSync2,
|
|
1444
|
+
closeSync as closeSync2,
|
|
1445
|
+
copyFileSync,
|
|
1446
|
+
createWriteStream,
|
|
1447
|
+
existsSync as existsSync2,
|
|
1448
|
+
fsyncSync as fsyncSync2,
|
|
1449
|
+
lstatSync as lstatSync2,
|
|
1450
|
+
mkdirSync as mkdirSync2,
|
|
1451
|
+
mkdtempSync,
|
|
1452
|
+
openSync as openSync2,
|
|
1453
|
+
readdirSync,
|
|
1454
|
+
readFileSync as readFileSync4,
|
|
1455
|
+
renameSync as renameSync2,
|
|
1456
|
+
rmSync as rmSync2
|
|
1457
|
+
} from "fs";
|
|
1458
|
+
import { tmpdir } from "os";
|
|
1459
|
+
import { basename as basename3, dirname as dirname2, isAbsolute, parse, posix, relative, resolve as resolve2 } from "path";
|
|
1460
|
+
import { DatabaseSync } from "node:sqlite";
|
|
1071
1461
|
import { Readable } from "stream";
|
|
1072
1462
|
import { pipeline } from "stream/promises";
|
|
1073
|
-
import { defineCommand as
|
|
1074
|
-
|
|
1463
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
1464
|
+
import { extract, list as list3 } from "tar";
|
|
1465
|
+
|
|
1466
|
+
// src/backup-schema.ts
|
|
1467
|
+
import { createHash } from "crypto";
|
|
1468
|
+
var CANONICAL_MIGRATION = "0001_init";
|
|
1469
|
+
var CANONICAL_SCHEMA_HASH = "7c0867f97bd6a7d9c8a89f43f6ae7e5259c3976d40cd81a2061c43c30b7590a1";
|
|
1470
|
+
var CANONICAL_OBJECTS = [
|
|
1471
|
+
["index", "agent_triggers_agent"],
|
|
1472
|
+
["index", "agent_triggers_enabled"],
|
|
1473
|
+
["index", "agent_loop_break_events_agent_time"],
|
|
1474
|
+
["index", "agent_loop_break_events_team_time"],
|
|
1475
|
+
["index", "agent_lesson_proposals_agent_status_time"],
|
|
1476
|
+
["index", "agent_lesson_proposals_review"],
|
|
1477
|
+
["index", "agent_reviews_agent_time"],
|
|
1478
|
+
["index", "agent_reviews_claimable"],
|
|
1479
|
+
["index", "agent_reviews_one_open_per_agent"],
|
|
1480
|
+
["index", "communication_approval_events_attempt"],
|
|
1481
|
+
["index", "communication_approvals_queue"],
|
|
1482
|
+
["index", "communication_approvals_teams"],
|
|
1483
|
+
["index", "idx_agents_telegram_topic_id"],
|
|
1484
|
+
["index", "idx_provider_models_provider"],
|
|
1485
|
+
["index", "messages_policy_delivery_queue"],
|
|
1486
|
+
["index", "messages_causal_chain"],
|
|
1487
|
+
["index", "messages_to_unread"],
|
|
1488
|
+
["index", "team_policy_baseline_owner"],
|
|
1489
|
+
["index", "team_policy_blocks_team_time"],
|
|
1490
|
+
["index", "team_template_active_position"],
|
|
1491
|
+
["index", "trigger_dispatches_claimable"],
|
|
1492
|
+
["index", "trigger_dispatches_trigger_time"],
|
|
1493
|
+
["index", "web_tokens_active"],
|
|
1494
|
+
["table", "agent_skills"],
|
|
1495
|
+
["table", "agent_loop_break_events"],
|
|
1496
|
+
["table", "agent_lesson_proposals"],
|
|
1497
|
+
["table", "agent_reviews"],
|
|
1498
|
+
["table", "agent_triggers"],
|
|
1499
|
+
["table", "agents"],
|
|
1500
|
+
["table", "attention_acknowledgements"],
|
|
1501
|
+
["table", "communication_approval_events"],
|
|
1502
|
+
["table", "communication_approval_message_grants"],
|
|
1503
|
+
["table", "communication_approvals"],
|
|
1504
|
+
["table", "config"],
|
|
1505
|
+
["table", "mcp_servers"],
|
|
1506
|
+
["table", "messages"],
|
|
1507
|
+
["table", "profile_communication_defaults"],
|
|
1508
|
+
["table", "profile_default_skills"],
|
|
1509
|
+
["table", "profiles"],
|
|
1510
|
+
["table", "provider_models"],
|
|
1511
|
+
["table", "provider_state"],
|
|
1512
|
+
["table", "schema_migrations"],
|
|
1513
|
+
["table", "secrets"],
|
|
1514
|
+
["table", "skill_meta"],
|
|
1515
|
+
["table", "source_slot_bindings"],
|
|
1516
|
+
["table", "team_agent_state"],
|
|
1517
|
+
["table", "team_policies"],
|
|
1518
|
+
["table", "team_policy_block_events"],
|
|
1519
|
+
["table", "team_policy_edges"],
|
|
1520
|
+
["table", "team_template_edges"],
|
|
1521
|
+
["table", "team_template_revision_edges"],
|
|
1522
|
+
["table", "team_template_revision_slots"],
|
|
1523
|
+
["table", "team_template_revisions"],
|
|
1524
|
+
["table", "team_template_slots"],
|
|
1525
|
+
["table", "team_templates"],
|
|
1526
|
+
["table", "teams"],
|
|
1527
|
+
["table", "telegram_allowed_users"],
|
|
1528
|
+
["table", "template_instantiations"],
|
|
1529
|
+
["table", "trigger_dispatches"],
|
|
1530
|
+
["table", "web_tokens"],
|
|
1531
|
+
["trigger", "create_team_policy"],
|
|
1532
|
+
["trigger", "prevent_detached_team_policy_delete"],
|
|
1533
|
+
["trigger", "validate_source_binding_insert"],
|
|
1534
|
+
["trigger", "validate_team_agent_state_insert"],
|
|
1535
|
+
["trigger", "validate_team_policy_baseline_update"],
|
|
1536
|
+
["trigger", "validate_team_policy_edge_insert"],
|
|
1537
|
+
["trigger", "validate_team_template_edge_insert"]
|
|
1538
|
+
];
|
|
1539
|
+
function normalizeSql(sql) {
|
|
1540
|
+
return sql.replace(/\s+/g, " ").trim();
|
|
1541
|
+
}
|
|
1542
|
+
function assertCanonicalBackupSchema(db) {
|
|
1543
|
+
const migrations = db.prepare("SELECT version FROM schema_migrations ORDER BY version").all();
|
|
1544
|
+
if (migrations.length !== 1 || migrations[0]?.version !== CANONICAL_MIGRATION) {
|
|
1545
|
+
throw new Error(
|
|
1546
|
+
`schema_migrations must contain exactly ${CANONICAL_MIGRATION}; found ` + (migrations.map((row) => row.version).join(", ") || "none")
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
const rows = db.prepare(
|
|
1550
|
+
`SELECT type, name, tbl_name, sql
|
|
1551
|
+
FROM sqlite_schema
|
|
1552
|
+
WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'`
|
|
1553
|
+
).all();
|
|
1554
|
+
const byKey = new Map(rows.map((row) => [`${row.type}\0${row.name}`, row]));
|
|
1555
|
+
const canonicalKeys = new Set(CANONICAL_OBJECTS.map(([type, name]) => `${type}\0${name}`));
|
|
1556
|
+
for (const row of rows) {
|
|
1557
|
+
if (!canonicalKeys.has(`${row.type}\0${row.name}`)) {
|
|
1558
|
+
throw new Error(`unexpected schema ${row.type} is not canonical: ${row.name}`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
const canonicalRows = [];
|
|
1562
|
+
for (const [type, name] of CANONICAL_OBJECTS) {
|
|
1563
|
+
const row = byKey.get(`${type}\0${name}`);
|
|
1564
|
+
if (!row) throw new Error(`required canonical schema ${type} is missing: ${name}`);
|
|
1565
|
+
canonicalRows.push(row);
|
|
1566
|
+
}
|
|
1567
|
+
const payload = canonicalRows.map((row) => [row.type, row.name, row.tbl_name, normalizeSql(row.sql)].join("\0")).join("\n");
|
|
1568
|
+
const actualHash = createHash("sha256").update(payload).digest("hex");
|
|
1569
|
+
if (actualHash !== CANONICAL_SCHEMA_HASH) {
|
|
1570
|
+
throw new Error(
|
|
1571
|
+
"canonical schema fingerprint does not match this Bazilion release; restore a backup created from the current clean-install schema"
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// src/daemon-liveness.ts
|
|
1577
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
1578
|
+
import {
|
|
1579
|
+
chmodSync,
|
|
1580
|
+
closeSync,
|
|
1581
|
+
fstatSync,
|
|
1582
|
+
fsyncSync,
|
|
1583
|
+
ftruncateSync,
|
|
1584
|
+
futimesSync,
|
|
1585
|
+
lstatSync,
|
|
1586
|
+
mkdirSync,
|
|
1587
|
+
openSync,
|
|
1588
|
+
readFileSync as readFileSync3,
|
|
1589
|
+
realpathSync,
|
|
1590
|
+
renameSync,
|
|
1591
|
+
rmSync,
|
|
1592
|
+
writeSync
|
|
1593
|
+
} from "fs";
|
|
1594
|
+
import { basename as basename2, dirname, join as join2, resolve } from "path";
|
|
1595
|
+
var DAEMON_LIVENESS_FILENAME = "daemon-runtime.json";
|
|
1596
|
+
var STALE_AFTER_MS = 6e4;
|
|
1597
|
+
var PROBE_TIMEOUT_MS = 750;
|
|
1598
|
+
function canonicalHomeIdentity(home) {
|
|
1599
|
+
const absolute = resolve(home);
|
|
1600
|
+
try {
|
|
1601
|
+
return realpathSync.native(absolute);
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
1604
|
+
const parent = dirname(absolute);
|
|
1605
|
+
if (parent === absolute) throw error;
|
|
1606
|
+
return join2(canonicalHomeIdentity(parent), basename2(absolute));
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
function daemonLivenessPath(home) {
|
|
1610
|
+
const canonicalHome = canonicalHomeIdentity(home);
|
|
1611
|
+
const identity = createHash2("sha256").update(canonicalHome).digest("hex").slice(0, 24);
|
|
1612
|
+
return join2(dirname(canonicalHome), `.bazilion-runtime-${identity}.json`);
|
|
1613
|
+
}
|
|
1614
|
+
function errorCode(error) {
|
|
1615
|
+
return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
1616
|
+
}
|
|
1617
|
+
function parseRecord(raw) {
|
|
1618
|
+
try {
|
|
1619
|
+
const value = JSON.parse(raw);
|
|
1620
|
+
if (value.version !== 1 || typeof value.instanceId !== "string" || !value.instanceId || !Number.isSafeInteger(value.pid) || (value.pid ?? 0) <= 0 || !["daemon", "restore"].includes(value.owner ?? "") || ![
|
|
1621
|
+
"starting",
|
|
1622
|
+
"listening",
|
|
1623
|
+
"restoring",
|
|
1624
|
+
"swapping",
|
|
1625
|
+
"installed",
|
|
1626
|
+
"recovery-required"
|
|
1627
|
+
].includes(value.state ?? "") || !Number.isFinite(value.startedAt)) {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
if (value.owner === "daemon" && !["starting", "listening"].includes(value.state ?? "") || value.owner === "restore" && !["restoring", "swapping", "installed", "recovery-required"].includes(value.state ?? "")) {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
if (value.owner === "restore" && ["swapping", "recovery-required"].includes(value.state ?? "") && (typeof value.recoveryPath !== "string" || !value.recoveryPath || typeof value.hadPrevious !== "boolean")) {
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
if (value.owner === "daemon" && value.state === "listening" && (typeof value.host !== "string" || !value.host || !Number.isSafeInteger(value.port) || (value.port ?? 0) < 1 || (value.port ?? 0) > 65535)) {
|
|
1637
|
+
return null;
|
|
1638
|
+
}
|
|
1639
|
+
return value;
|
|
1640
|
+
} catch {
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function readRecord(path) {
|
|
1645
|
+
try {
|
|
1646
|
+
const stat = lstatSync(path);
|
|
1647
|
+
const raw = stat.isFile() ? readFileSync3(path, "utf8") : "";
|
|
1648
|
+
return { raw, stat, record: stat.isFile() ? parseRecord(raw) : null };
|
|
1649
|
+
} catch (error) {
|
|
1650
|
+
if (errorCode(error) === "ENOENT") return null;
|
|
1651
|
+
throw error;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
function pidIsAlive(pid) {
|
|
1655
|
+
try {
|
|
1656
|
+
process.kill(pid, 0);
|
|
1657
|
+
return true;
|
|
1658
|
+
} catch (error) {
|
|
1659
|
+
return errorCode(error) !== "ESRCH";
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function endpointFor(record) {
|
|
1663
|
+
if (record.state !== "listening" || !record.host || !record.port) return null;
|
|
1664
|
+
const normalized = record.host.toLowerCase();
|
|
1665
|
+
if (normalized === "0.0.0.0") return `http://127.0.0.1:${record.port}`;
|
|
1666
|
+
if (normalized === "::" || normalized === "0:0:0:0:0:0:0:0") {
|
|
1667
|
+
return `http://[::1]:${record.port}`;
|
|
1668
|
+
}
|
|
1669
|
+
if (normalized === "localhost" || normalized === "127.0.0.1") {
|
|
1670
|
+
return `http://${normalized}:${record.port}`;
|
|
1671
|
+
}
|
|
1672
|
+
if (normalized === "::1" || normalized === "[::1]") {
|
|
1673
|
+
return `http://[::1]:${record.port}`;
|
|
1674
|
+
}
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
async function endpointHasOwner(endpoint3, instanceId) {
|
|
1678
|
+
const controller = new AbortController();
|
|
1679
|
+
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
1680
|
+
try {
|
|
1681
|
+
const response = await fetch(new URL("/api/health", endpoint3), {
|
|
1682
|
+
signal: controller.signal
|
|
1683
|
+
});
|
|
1684
|
+
return response.headers.get("x-bazilion-daemon-instance") === instanceId;
|
|
1685
|
+
} catch {
|
|
1686
|
+
return false;
|
|
1687
|
+
} finally {
|
|
1688
|
+
clearTimeout(timer);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
function sameFile(left, right) {
|
|
1692
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
1693
|
+
}
|
|
1694
|
+
function writeRecord(fd, record) {
|
|
1695
|
+
const body = `${JSON.stringify(record, null, 2)}
|
|
1696
|
+
`;
|
|
1697
|
+
ftruncateSync(fd, 0);
|
|
1698
|
+
writeSync(fd, body, 0, "utf8");
|
|
1699
|
+
fsyncSync(fd);
|
|
1700
|
+
}
|
|
1701
|
+
function fsyncParentDirectory(path) {
|
|
1702
|
+
let directoryFd = null;
|
|
1703
|
+
try {
|
|
1704
|
+
directoryFd = openSync(dirname(path), "r");
|
|
1705
|
+
fsyncSync(directoryFd);
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
if (process.platform !== "win32") throw error;
|
|
1708
|
+
} finally {
|
|
1709
|
+
if (directoryFd !== null) closeSync(directoryFd);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
function reclaimRecord(path, existing) {
|
|
1713
|
+
const identity = createHash2("sha256").update(existing.record?.instanceId ?? existing.raw).update(`\0${existing.stat.dev}\0${existing.stat.ino}`).digest("hex").slice(0, 24);
|
|
1714
|
+
const claim = `${path}.reclaim-${identity}`;
|
|
1715
|
+
try {
|
|
1716
|
+
mkdirSync(claim, { mode: 448 });
|
|
1717
|
+
} catch (error) {
|
|
1718
|
+
if (errorCode(error) !== "EEXIST") throw error;
|
|
1719
|
+
return null;
|
|
1720
|
+
}
|
|
1721
|
+
const current = readRecord(path);
|
|
1722
|
+
if (!current || !sameFile(current.stat, existing.stat) || current.raw !== existing.raw) {
|
|
1723
|
+
return null;
|
|
1724
|
+
}
|
|
1725
|
+
try {
|
|
1726
|
+
renameSync(path, join2(claim, "stale-record.json"));
|
|
1727
|
+
return claim;
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
if (errorCode(error) === "ENOENT") return null;
|
|
1730
|
+
throw error;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
async function acquireHomeRestoreLock(home) {
|
|
1734
|
+
const targetHome = resolve(home);
|
|
1735
|
+
mkdirSync(dirname(targetHome), { recursive: true });
|
|
1736
|
+
const path = daemonLivenessPath(targetHome);
|
|
1737
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1738
|
+
const instanceId = randomUUID();
|
|
1739
|
+
let fd = null;
|
|
1740
|
+
let reclaimClaim = null;
|
|
1741
|
+
for (let attempt = 0; attempt < 8 && fd === null; attempt++) {
|
|
1742
|
+
try {
|
|
1743
|
+
fd = openSync(path, "wx", 384);
|
|
1744
|
+
break;
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
if (errorCode(error) !== "EEXIST") throw error;
|
|
1747
|
+
}
|
|
1748
|
+
const existing = readRecord(path);
|
|
1749
|
+
if (!existing) continue;
|
|
1750
|
+
const endpoint3 = existing.record ? endpointFor(existing.record) : null;
|
|
1751
|
+
if (endpoint3 && existing.record && await endpointHasOwner(endpoint3, existing.record.instanceId)) {
|
|
1752
|
+
throw new Error(
|
|
1753
|
+
`bazilion daemon is running at ${endpoint3} for ${targetHome} \u2014 stop it before restoring`
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
if (!existing.record) {
|
|
1757
|
+
throw new Error(
|
|
1758
|
+
`the daemon ownership record for ${targetHome} is invalid; verify no daemon is running before removing it manually`
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
const livePid = pidIsAlive(existing.record.pid);
|
|
1762
|
+
if (existing.record.owner === "restore" && ["swapping", "recovery-required"].includes(existing.record.state)) {
|
|
1763
|
+
if (livePid) {
|
|
1764
|
+
throw new Error(
|
|
1765
|
+
`bazilion restore process ${existing.record.pid} is ${existing.record.state} ${targetHome} \u2014 wait for it to finish`
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
const phase = existing.record.state.replace("-", " ");
|
|
1769
|
+
throw new Error(
|
|
1770
|
+
`Bazilion restore ${phase} for ${targetHome}; recovery data is at ${existing.record.recoveryPath}. Recover the previous home, then remove ${path} manually.`
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
if (livePid) {
|
|
1774
|
+
const ageMs = Math.max(0, Date.now() - existing.stat.mtimeMs);
|
|
1775
|
+
const staleDetail = ageMs > STALE_AFTER_MS ? " (its heartbeat is stale)" : "";
|
|
1776
|
+
const owner = existing.record.owner === "daemon" ? "daemon" : "restore";
|
|
1777
|
+
throw new Error(
|
|
1778
|
+
`bazilion ${owner} process ${existing.record.pid} owns ${targetHome}${staleDetail} \u2014 stop it before restoring`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
const claim = reclaimRecord(path, existing);
|
|
1782
|
+
if (claim) {
|
|
1783
|
+
reclaimClaim = claim;
|
|
1784
|
+
console.warn(`removed stale Bazilion daemon ownership record for ${targetHome}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
if (fd === null) {
|
|
1788
|
+
throw new Error(
|
|
1789
|
+
`could not safely acquire exclusive restore ownership for ${targetHome}; try again`
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
const ownedStat = fstatSync(fd);
|
|
1793
|
+
const record = {
|
|
1794
|
+
version: 1,
|
|
1795
|
+
instanceId,
|
|
1796
|
+
pid: process.pid,
|
|
1797
|
+
owner: "restore",
|
|
1798
|
+
state: "restoring",
|
|
1799
|
+
startedAt: Date.now()
|
|
1800
|
+
};
|
|
1801
|
+
try {
|
|
1802
|
+
writeRecord(fd, record);
|
|
1803
|
+
fsyncParentDirectory(path);
|
|
1804
|
+
if (reclaimClaim) rmSync(reclaimClaim, { recursive: true, force: true });
|
|
1805
|
+
try {
|
|
1806
|
+
chmodSync(path, 384);
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
closeSync(fd);
|
|
1811
|
+
fd = null;
|
|
1812
|
+
try {
|
|
1813
|
+
if (sameFile(lstatSync(path), ownedStat)) rmSync(path, { force: true });
|
|
1814
|
+
} catch {
|
|
1815
|
+
}
|
|
1816
|
+
throw error;
|
|
1817
|
+
}
|
|
1818
|
+
let released = false;
|
|
1819
|
+
const stillOwnsPath = () => {
|
|
1820
|
+
try {
|
|
1821
|
+
return sameFile(lstatSync(path), ownedStat);
|
|
1822
|
+
} catch {
|
|
1823
|
+
return false;
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
const heartbeat = setInterval(() => {
|
|
1827
|
+
if (released) return;
|
|
1828
|
+
if (!stillOwnsPath()) {
|
|
1829
|
+
console.error("restore ownership was lost; exiting to protect the Bazilion database");
|
|
1830
|
+
process.exit(1);
|
|
1831
|
+
}
|
|
1832
|
+
try {
|
|
1833
|
+
const now = /* @__PURE__ */ new Date();
|
|
1834
|
+
futimesSync(fd, now, now);
|
|
1835
|
+
} catch (error) {
|
|
1836
|
+
console.error(
|
|
1837
|
+
"restore ownership heartbeat failed:",
|
|
1838
|
+
error instanceof Error ? error.message : error
|
|
1839
|
+
);
|
|
1840
|
+
process.exit(1);
|
|
1841
|
+
}
|
|
1842
|
+
}, 2e3);
|
|
1843
|
+
heartbeat.unref();
|
|
1844
|
+
return {
|
|
1845
|
+
path,
|
|
1846
|
+
markSwapping(recoveryPath, hadPrevious) {
|
|
1847
|
+
if (released || !stillOwnsPath()) throw new Error("restore ownership was lost");
|
|
1848
|
+
record.state = "swapping";
|
|
1849
|
+
record.recoveryPath = resolve(recoveryPath);
|
|
1850
|
+
record.hadPrevious = hadPrevious;
|
|
1851
|
+
writeRecord(fd, record);
|
|
1852
|
+
fsyncParentDirectory(path);
|
|
1853
|
+
},
|
|
1854
|
+
markInstalled() {
|
|
1855
|
+
if (released || !stillOwnsPath()) throw new Error("restore ownership was lost");
|
|
1856
|
+
record.state = "installed";
|
|
1857
|
+
delete record.recoveryPath;
|
|
1858
|
+
delete record.hadPrevious;
|
|
1859
|
+
writeRecord(fd, record);
|
|
1860
|
+
},
|
|
1861
|
+
markRecoveryRequired(recoveryPath, hadPrevious) {
|
|
1862
|
+
if (released || !stillOwnsPath()) throw new Error("restore ownership was lost");
|
|
1863
|
+
record.state = "recovery-required";
|
|
1864
|
+
record.recoveryPath = resolve(recoveryPath);
|
|
1865
|
+
record.hadPrevious = hadPrevious;
|
|
1866
|
+
writeRecord(fd, record);
|
|
1867
|
+
},
|
|
1868
|
+
release() {
|
|
1869
|
+
if (released) return;
|
|
1870
|
+
released = true;
|
|
1871
|
+
clearInterval(heartbeat);
|
|
1872
|
+
const ownsPath = stillOwnsPath();
|
|
1873
|
+
const retainForRecovery = ["swapping", "recovery-required"].includes(record.state);
|
|
1874
|
+
closeSync(fd);
|
|
1875
|
+
fd = null;
|
|
1876
|
+
if (ownsPath && !retainForRecovery) rmSync(path, { force: true });
|
|
1877
|
+
}
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// src/commands/backup.ts
|
|
1882
|
+
var createCmd = defineCommand5({
|
|
1075
1883
|
meta: { name: "create", description: "Download a tar.gz backup of ~/.bazilion from the server" },
|
|
1076
1884
|
args: {
|
|
1077
1885
|
output: {
|
|
@@ -1083,7 +1891,14 @@ var createCmd = defineCommand4({
|
|
|
1083
1891
|
async run({ args }) {
|
|
1084
1892
|
const cfg = loadClientConfig();
|
|
1085
1893
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1086
|
-
const outAbs =
|
|
1894
|
+
const outAbs = resolve2(args.output ?? `bazilion-backup-${date}.tar.gz`);
|
|
1895
|
+
const configuredHome = resolve2(resolveCliPaths().home);
|
|
1896
|
+
const outputFromHome = relative(configuredHome, outAbs);
|
|
1897
|
+
if (!outputFromHome || !outputFromHome.startsWith("..") && !isAbsolute(outputFromHome)) {
|
|
1898
|
+
throw new Error(
|
|
1899
|
+
`backup output must be outside BAZILION_HOME (${configuredHome}) to avoid nesting backups`
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1087
1902
|
console.log(`downloading backup \u2192 ${outAbs}`);
|
|
1088
1903
|
const res = await fetch(`${cfg.serverUrl}/api/backup`, {
|
|
1089
1904
|
headers: { authorization: `Bearer ${cfg.token}`, origin: cfg.serverUrl }
|
|
@@ -1097,22 +1912,463 @@ var createCmd = defineCommand4({
|
|
|
1097
1912
|
}
|
|
1098
1913
|
throw new Error(`backup failed: ${err}`);
|
|
1099
1914
|
}
|
|
1100
|
-
|
|
1915
|
+
const downloadDir = mkdtempSync(resolve2(tmpdir(), "bazilion-download-"));
|
|
1916
|
+
const partial = resolve2(downloadDir, "backup.tar.gz");
|
|
1917
|
+
try {
|
|
1918
|
+
await pipeline(
|
|
1919
|
+
Readable.fromWeb(res.body),
|
|
1920
|
+
createWriteStream(partial, { flags: "wx", mode: 384 })
|
|
1921
|
+
);
|
|
1922
|
+
await validateArchive(partial);
|
|
1923
|
+
try {
|
|
1924
|
+
renameSync2(partial, outAbs);
|
|
1925
|
+
} catch (error) {
|
|
1926
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "EXDEV") throw error;
|
|
1927
|
+
const installPartial = resolve2(
|
|
1928
|
+
dirname2(outAbs),
|
|
1929
|
+
`.${basename3(outAbs)}.${randomUUID2()}.installing`
|
|
1930
|
+
);
|
|
1931
|
+
try {
|
|
1932
|
+
copyFileSync(partial, installPartial);
|
|
1933
|
+
chmodSync2(installPartial, 384);
|
|
1934
|
+
renameSync2(installPartial, outAbs);
|
|
1935
|
+
} finally {
|
|
1936
|
+
rmSync2(installPartial, { force: true });
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
} finally {
|
|
1940
|
+
rmSync2(downloadDir, { recursive: true, force: true });
|
|
1941
|
+
}
|
|
1101
1942
|
console.log("done");
|
|
1102
1943
|
}
|
|
1103
1944
|
});
|
|
1104
|
-
async function probeServerRunning(
|
|
1945
|
+
async function probeServerRunning(serverUrl) {
|
|
1105
1946
|
try {
|
|
1106
1947
|
const ctrl = new AbortController();
|
|
1107
1948
|
const timer = setTimeout(() => ctrl.abort(), 500);
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1949
|
+
try {
|
|
1950
|
+
const res = await fetch(new URL("/api/health", serverUrl), { signal: ctrl.signal });
|
|
1951
|
+
return res.status >= 200 && res.status < 600;
|
|
1952
|
+
} finally {
|
|
1953
|
+
clearTimeout(timer);
|
|
1954
|
+
}
|
|
1111
1955
|
} catch {
|
|
1112
1956
|
return false;
|
|
1113
1957
|
}
|
|
1114
1958
|
}
|
|
1115
|
-
|
|
1959
|
+
function loopbackServerUrl(raw) {
|
|
1960
|
+
if (!raw) return null;
|
|
1961
|
+
try {
|
|
1962
|
+
const url = new URL(raw);
|
|
1963
|
+
if (!["http:", "https:"].includes(url.protocol)) return null;
|
|
1964
|
+
const hostname = url.hostname.toLowerCase();
|
|
1965
|
+
if (!["127.0.0.1", "localhost", "[::1]", "::1"].includes(hostname)) return null;
|
|
1966
|
+
return url.origin;
|
|
1967
|
+
} catch {
|
|
1968
|
+
return null;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
function configuredLoopbackUrls() {
|
|
1972
|
+
const urls = /* @__PURE__ */ new Set(["http://127.0.0.1:4321"]);
|
|
1973
|
+
const envUrl = loopbackServerUrl(process.env.BAZILION_SERVER);
|
|
1974
|
+
if (envUrl) urls.add(envUrl);
|
|
1975
|
+
try {
|
|
1976
|
+
const clientUrl = loopbackServerUrl(loadClientConfig().serverUrl);
|
|
1977
|
+
if (clientUrl) urls.add(clientUrl);
|
|
1978
|
+
} catch {
|
|
1979
|
+
}
|
|
1980
|
+
return [...urls];
|
|
1981
|
+
}
|
|
1982
|
+
var EXTRACTABLE_ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
1983
|
+
"File",
|
|
1984
|
+
"OldFile",
|
|
1985
|
+
"Directory",
|
|
1986
|
+
"SymbolicLink"
|
|
1987
|
+
]);
|
|
1988
|
+
function normalizeArchivePath(rawPath) {
|
|
1989
|
+
if (!rawPath || rawPath.includes("\0")) throw new Error("backup contains an invalid empty path");
|
|
1990
|
+
if (rawPath.includes("\\")) {
|
|
1991
|
+
throw new Error(`backup contains a non-portable path: ${JSON.stringify(rawPath)}`);
|
|
1992
|
+
}
|
|
1993
|
+
if (rawPath.startsWith("/") || /^[A-Za-z]:/.test(rawPath)) {
|
|
1994
|
+
throw new Error(`backup contains an absolute path: ${JSON.stringify(rawPath)}`);
|
|
1995
|
+
}
|
|
1996
|
+
const segments = [];
|
|
1997
|
+
for (const segment of rawPath.split("/")) {
|
|
1998
|
+
if (!segment || segment === ".") continue;
|
|
1999
|
+
if (segment === "..") {
|
|
2000
|
+
throw new Error(`backup path escapes the restore directory: ${JSON.stringify(rawPath)}`);
|
|
2001
|
+
}
|
|
2002
|
+
segments.push(segment);
|
|
2003
|
+
}
|
|
2004
|
+
return segments.join("/");
|
|
2005
|
+
}
|
|
2006
|
+
function validateArchiveSymbolicLink(path, rawTarget) {
|
|
2007
|
+
if (!rawTarget || rawTarget.includes("\0")) {
|
|
2008
|
+
throw new Error(`backup contains an invalid symbolic link target: ${path}`);
|
|
2009
|
+
}
|
|
2010
|
+
const segments = path.split("/");
|
|
2011
|
+
const isTeamSlot = segments.length === 2 && segments[0] === "teams" && /^[a-z0-9][a-z0-9-]*$/.test(segments[1] ?? "");
|
|
2012
|
+
const portableTarget = rawTarget.replaceAll("\\", "/");
|
|
2013
|
+
const absoluteOnAnyPlatform = isAbsolute(rawTarget) || posix.isAbsolute(portableTarget) || /^[A-Za-z]:\//.test(portableTarget);
|
|
2014
|
+
if (absoluteOnAnyPlatform) {
|
|
2015
|
+
if (!isTeamSlot) {
|
|
2016
|
+
throw new Error(`backup absolute symbolic links are only allowed for Team slots: ${path}`);
|
|
2017
|
+
}
|
|
2018
|
+
if (!isAbsolute(rawTarget)) {
|
|
2019
|
+
throw new Error(`backup contains an invalid Team link target: ${path}`);
|
|
2020
|
+
}
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (isTeamSlot) {
|
|
2024
|
+
throw new Error(`backup contains an invalid Team link target: ${path}`);
|
|
2025
|
+
}
|
|
2026
|
+
if (/^[A-Za-z]:/.test(portableTarget)) {
|
|
2027
|
+
throw new Error(`backup contains a non-portable symbolic link target: ${path}`);
|
|
2028
|
+
}
|
|
2029
|
+
const target = posix.normalize(posix.join(posix.dirname(path), portableTarget));
|
|
2030
|
+
if (target === ".." || target.startsWith("../") || posix.isAbsolute(target)) {
|
|
2031
|
+
throw new Error(`backup symbolic link target escapes the restore directory: ${path}`);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
async function validateArchive(file) {
|
|
2035
|
+
const entries = [];
|
|
2036
|
+
await list3({
|
|
2037
|
+
file,
|
|
2038
|
+
strict: true,
|
|
2039
|
+
onentry(entry2) {
|
|
2040
|
+
if (entry2.meta) return;
|
|
2041
|
+
const path = normalizeArchivePath(entry2.path);
|
|
2042
|
+
if (!path) return;
|
|
2043
|
+
if (!EXTRACTABLE_ENTRY_TYPES.has(entry2.type)) {
|
|
2044
|
+
throw new Error(`backup contains unsupported ${entry2.type} entry: ${path}`);
|
|
2045
|
+
}
|
|
2046
|
+
if (entry2.type === "SymbolicLink") {
|
|
2047
|
+
validateArchiveSymbolicLink(path, entry2.linkpath);
|
|
2048
|
+
}
|
|
2049
|
+
entries.push({ path, type: entry2.type, linkpath: entry2.linkpath });
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
2053
|
+
for (const entry2 of entries) {
|
|
2054
|
+
if (byPath.has(entry2.path)) throw new Error(`backup contains a duplicate path: ${entry2.path}`);
|
|
2055
|
+
byPath.set(entry2.path, entry2);
|
|
2056
|
+
}
|
|
2057
|
+
const symlinks = new Set(
|
|
2058
|
+
entries.filter((entry2) => entry2.type === "SymbolicLink").map((entry2) => entry2.path)
|
|
2059
|
+
);
|
|
2060
|
+
for (const entry2 of entries) {
|
|
2061
|
+
const segments = entry2.path.split("/");
|
|
2062
|
+
for (let i = 1; i < segments.length; i++) {
|
|
2063
|
+
const ancestor = segments.slice(0, i).join("/");
|
|
2064
|
+
if (symlinks.has(ancestor)) {
|
|
2065
|
+
throw new Error(`backup entry traverses symbolic link ${ancestor}: ${entry2.path}`);
|
|
2066
|
+
}
|
|
2067
|
+
const ancestorEntry = byPath.get(ancestor);
|
|
2068
|
+
if (ancestorEntry && ancestorEntry.type !== "Directory") {
|
|
2069
|
+
throw new Error(`backup entry traverses non-directory ${ancestor}: ${entry2.path}`);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
for (const required of ["bazilion.db", "auth.json"]) {
|
|
2074
|
+
const entry2 = byPath.get(required);
|
|
2075
|
+
if (!entry2 || entry2.type !== "File" && entry2.type !== "OldFile") {
|
|
2076
|
+
throw new Error(`backup is missing required regular file: ${required}`);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
for (const transient of [
|
|
2080
|
+
"bazilion.db-wal",
|
|
2081
|
+
"bazilion.db-shm",
|
|
2082
|
+
"bazilion.db-journal",
|
|
2083
|
+
DAEMON_LIVENESS_FILENAME
|
|
2084
|
+
]) {
|
|
2085
|
+
if (byPath.has(transient)) {
|
|
2086
|
+
throw new Error(`backup contains unsafe transient SQLite file: ${transient}`);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
return entries;
|
|
2090
|
+
}
|
|
2091
|
+
function validateAuthFile(path) {
|
|
2092
|
+
let parsed;
|
|
2093
|
+
try {
|
|
2094
|
+
parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
2095
|
+
} catch (error) {
|
|
2096
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2097
|
+
throw new Error(`backup auth.json is invalid: ${detail}`);
|
|
2098
|
+
}
|
|
2099
|
+
if (typeof parsed !== "object" || parsed === null || !("token" in parsed) || typeof parsed.token !== "string" || !parsed.token.trim()) {
|
|
2100
|
+
throw new Error("backup auth.json is invalid: token is missing");
|
|
2101
|
+
}
|
|
2102
|
+
return parsed.token;
|
|
2103
|
+
}
|
|
2104
|
+
function validateDatabase(path, authToken) {
|
|
2105
|
+
let db = null;
|
|
2106
|
+
try {
|
|
2107
|
+
db = new DatabaseSync(path, { readOnly: true });
|
|
2108
|
+
const integrity = db.prepare("PRAGMA integrity_check").all();
|
|
2109
|
+
if (integrity.length !== 1 || integrity[0]?.integrity_check !== "ok") {
|
|
2110
|
+
const detail = integrity.map((row) => row.integrity_check).join("; ") || "no result";
|
|
2111
|
+
throw new Error(`integrity_check failed: ${detail}`);
|
|
2112
|
+
}
|
|
2113
|
+
const foreignKeyErrors = db.prepare("PRAGMA foreign_key_check").all();
|
|
2114
|
+
if (foreignKeyErrors.length > 0) {
|
|
2115
|
+
throw new Error(`foreign_key_check failed with ${foreignKeyErrors.length} violation(s)`);
|
|
2116
|
+
}
|
|
2117
|
+
assertCanonicalBackupSchema(db);
|
|
2118
|
+
const tokenHash = createHash3("sha256").update(authToken).digest("hex");
|
|
2119
|
+
const activeBootstrap = db.prepare(
|
|
2120
|
+
`SELECT 1 FROM web_tokens
|
|
2121
|
+
WHERE token_hash = ? AND label = 'bootstrap' AND revoked_at IS NULL
|
|
2122
|
+
LIMIT 1`
|
|
2123
|
+
).get(tokenHash);
|
|
2124
|
+
if (!activeBootstrap) {
|
|
2125
|
+
throw new Error("auth.json token does not match an active web_tokens row");
|
|
2126
|
+
}
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
if (error instanceof Error && error.message.startsWith("backup database is invalid:")) {
|
|
2129
|
+
throw error;
|
|
2130
|
+
}
|
|
2131
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2132
|
+
throw new Error(`backup database is invalid: ${detail}`);
|
|
2133
|
+
} finally {
|
|
2134
|
+
db?.close();
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
var SLUG_ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
2138
|
+
var AGENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
2139
|
+
function validateEntityId(id, pattern, entity) {
|
|
2140
|
+
if (typeof id !== "string" || !pattern.test(id)) {
|
|
2141
|
+
throw new Error(`${entity} has an invalid canonical id: ${JSON.stringify(id)}`);
|
|
2142
|
+
}
|
|
2143
|
+
return id;
|
|
2144
|
+
}
|
|
2145
|
+
function containedChildDirectory(root, id, rowLabel) {
|
|
2146
|
+
const child = resolve2(root, id);
|
|
2147
|
+
const fromRoot = relative(root, child);
|
|
2148
|
+
if (!fromRoot || fromRoot.startsWith("..") || isAbsolute(fromRoot)) {
|
|
2149
|
+
throw new Error(`${rowLabel} directory escapes its canonical root: ${JSON.stringify(id)}`);
|
|
2150
|
+
}
|
|
2151
|
+
return child;
|
|
2152
|
+
}
|
|
2153
|
+
function requireExtractedDirectory(path, rowLabel) {
|
|
2154
|
+
try {
|
|
2155
|
+
if (lstatSync2(path).isDirectory()) return;
|
|
2156
|
+
} catch {
|
|
2157
|
+
}
|
|
2158
|
+
throw new Error(`${rowLabel} is missing its canonical directory in the backup`);
|
|
2159
|
+
}
|
|
2160
|
+
function requireExtractedTeamSlot(path, rowLabel) {
|
|
2161
|
+
try {
|
|
2162
|
+
const entry2 = lstatSync2(path);
|
|
2163
|
+
if (entry2.isDirectory() || entry2.isSymbolicLink()) return;
|
|
2164
|
+
} catch {
|
|
2165
|
+
}
|
|
2166
|
+
throw new Error(`${rowLabel} is missing its canonical Team slot in the backup`);
|
|
2167
|
+
}
|
|
2168
|
+
function rebaseRestoredHomeDirectories(database, payload, targetHome) {
|
|
2169
|
+
let db = null;
|
|
2170
|
+
try {
|
|
2171
|
+
db = new DatabaseSync(database);
|
|
2172
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
2173
|
+
const profiles = db.prepare("SELECT id FROM profiles ORDER BY id").all();
|
|
2174
|
+
const agents = db.prepare("SELECT id FROM agents ORDER BY id").all();
|
|
2175
|
+
const teams = db.prepare("SELECT id FROM teams ORDER BY id").all();
|
|
2176
|
+
const rewrites = [];
|
|
2177
|
+
for (const [table, rows, idPattern] of [
|
|
2178
|
+
["profiles", profiles, SLUG_ID],
|
|
2179
|
+
["agents", agents, AGENT_ID]
|
|
2180
|
+
]) {
|
|
2181
|
+
const collection = table;
|
|
2182
|
+
const stagedRoot = resolve2(payload, collection);
|
|
2183
|
+
const targetRoot = resolve2(targetHome, collection);
|
|
2184
|
+
for (const row of rows) {
|
|
2185
|
+
const id = validateEntityId(row.id, idPattern, `${table} row`);
|
|
2186
|
+
const rowLabel = `${table} row ${JSON.stringify(id)}`;
|
|
2187
|
+
const stagedDir = containedChildDirectory(stagedRoot, id, rowLabel);
|
|
2188
|
+
requireExtractedDirectory(stagedDir, rowLabel);
|
|
2189
|
+
rewrites.push({
|
|
2190
|
+
table,
|
|
2191
|
+
id,
|
|
2192
|
+
dir: containedChildDirectory(targetRoot, id, rowLabel)
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
const stagedTeamsRoot = resolve2(payload, "teams");
|
|
2197
|
+
for (const row of teams) {
|
|
2198
|
+
const id = validateEntityId(row.id, SLUG_ID, "teams row");
|
|
2199
|
+
const rowLabel = `teams row ${JSON.stringify(id)}`;
|
|
2200
|
+
requireExtractedTeamSlot(containedChildDirectory(stagedTeamsRoot, id, rowLabel), rowLabel);
|
|
2201
|
+
}
|
|
2202
|
+
db.exec("PRAGMA journal_mode = DELETE");
|
|
2203
|
+
db.exec("PRAGMA synchronous = FULL");
|
|
2204
|
+
let transactionOpen = false;
|
|
2205
|
+
try {
|
|
2206
|
+
db.exec("BEGIN IMMEDIATE");
|
|
2207
|
+
transactionOpen = true;
|
|
2208
|
+
const updateProfile = db.prepare("UPDATE profiles SET dir = ? WHERE id = ?");
|
|
2209
|
+
const updateAgent = db.prepare("UPDATE agents SET dir = ? WHERE id = ?");
|
|
2210
|
+
for (const rewrite of rewrites) {
|
|
2211
|
+
const result = rewrite.table === "profiles" ? updateProfile.run(rewrite.dir, rewrite.id) : updateAgent.run(rewrite.dir, rewrite.id);
|
|
2212
|
+
if (result.changes !== 1) {
|
|
2213
|
+
throw new Error(`could not rebase ${rewrite.table} row ${JSON.stringify(rewrite.id)}`);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
const readProfile = db.prepare("SELECT dir FROM profiles WHERE id = ?");
|
|
2217
|
+
const readAgent = db.prepare("SELECT dir FROM agents WHERE id = ?");
|
|
2218
|
+
for (const rewrite of rewrites) {
|
|
2219
|
+
const row = rewrite.table === "profiles" ? readProfile.get(rewrite.id) : readAgent.get(rewrite.id);
|
|
2220
|
+
if (row?.dir !== rewrite.dir) {
|
|
2221
|
+
throw new Error(
|
|
2222
|
+
`could not verify rebased ${rewrite.table} row ${JSON.stringify(rewrite.id)}`
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
db.exec("COMMIT");
|
|
2227
|
+
transactionOpen = false;
|
|
2228
|
+
} catch (error) {
|
|
2229
|
+
if (transactionOpen) db.exec("ROLLBACK");
|
|
2230
|
+
throw error;
|
|
2231
|
+
}
|
|
2232
|
+
} catch (error) {
|
|
2233
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2234
|
+
throw new Error(`backup database is invalid: ${detail}`);
|
|
2235
|
+
} finally {
|
|
2236
|
+
db?.close();
|
|
2237
|
+
}
|
|
2238
|
+
for (const suffix of ["-wal", "-shm", "-journal"]) {
|
|
2239
|
+
if (existsSync2(`${database}${suffix}`)) {
|
|
2240
|
+
throw new Error(`backup database is invalid: path rebasing left transient ${suffix} state`);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
function removeRebuildableQmdIndexes(payload) {
|
|
2245
|
+
const teamsDir = resolve2(payload, "teams");
|
|
2246
|
+
if (!existsSync2(teamsDir) || !lstatSync2(teamsDir).isDirectory()) return;
|
|
2247
|
+
for (const team of readdirSync(teamsDir, { withFileTypes: true })) {
|
|
2248
|
+
if (!team.isDirectory() || team.isSymbolicLink()) continue;
|
|
2249
|
+
const memoryDir = resolve2(teamsDir, team.name, "memory");
|
|
2250
|
+
if (!existsSync2(memoryDir) || !lstatSync2(memoryDir).isDirectory()) continue;
|
|
2251
|
+
for (const entry2 of readdirSync(memoryDir, { withFileTypes: true })) {
|
|
2252
|
+
if (entry2.isFile() && entry2.name.startsWith(".qmd-index.sqlite")) {
|
|
2253
|
+
rmSync2(resolve2(memoryDir, entry2.name), { force: true });
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
function installValidatedPayload(payload, targetHome, staging, lifecycle, operations = { rename: renameSync2 }) {
|
|
2259
|
+
const previous = resolve2(staging, "previous-home");
|
|
2260
|
+
const hadPrevious = existsSync2(targetHome);
|
|
2261
|
+
const targetParent = dirname2(targetHome);
|
|
2262
|
+
const syncDirectory = operations.fsyncDirectory ?? fsyncDirectory;
|
|
2263
|
+
const recoveryError = (message, recoveryPath, cause) => {
|
|
2264
|
+
let markerError;
|
|
2265
|
+
try {
|
|
2266
|
+
lifecycle?.markRecoveryRequired(recoveryPath, hadPrevious);
|
|
2267
|
+
} catch (error) {
|
|
2268
|
+
markerError = error;
|
|
2269
|
+
}
|
|
2270
|
+
const causeDetail = cause instanceof Error ? cause.message : String(cause);
|
|
2271
|
+
const markerDetail = markerError ? `; recovery marker update failed: ${markerError instanceof Error ? markerError.message : markerError}` : "";
|
|
2272
|
+
return new RestoreRecoveryRequiredError(
|
|
2273
|
+
`${message} (${causeDetail}); recovery data is at ${recoveryPath}${markerDetail}`,
|
|
2274
|
+
recoveryPath
|
|
2275
|
+
);
|
|
2276
|
+
};
|
|
2277
|
+
lifecycle?.markSwapping(previous, hadPrevious);
|
|
2278
|
+
if (hadPrevious) {
|
|
2279
|
+
try {
|
|
2280
|
+
operations.rename(targetHome, previous);
|
|
2281
|
+
} catch (moveError) {
|
|
2282
|
+
lifecycle?.markInstalled();
|
|
2283
|
+
throw moveError;
|
|
2284
|
+
}
|
|
2285
|
+
try {
|
|
2286
|
+
syncDirectory(targetParent);
|
|
2287
|
+
operations.afterPreviousMoved?.();
|
|
2288
|
+
} catch (postMoveError) {
|
|
2289
|
+
throw recoveryError(
|
|
2290
|
+
"the previous home moved but the swap could not continue safely",
|
|
2291
|
+
previous,
|
|
2292
|
+
postMoveError
|
|
2293
|
+
);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
try {
|
|
2297
|
+
operations.rename(payload, targetHome);
|
|
2298
|
+
} catch (installError) {
|
|
2299
|
+
if (hadPrevious) {
|
|
2300
|
+
try {
|
|
2301
|
+
operations.rename(previous, targetHome);
|
|
2302
|
+
} catch (rollbackError) {
|
|
2303
|
+
const installDetail = installError instanceof Error ? installError.message : String(installError);
|
|
2304
|
+
throw recoveryError(
|
|
2305
|
+
`restore install failed (${installDetail}) and rollback failed`,
|
|
2306
|
+
previous,
|
|
2307
|
+
rollbackError
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
try {
|
|
2311
|
+
syncDirectory(targetParent);
|
|
2312
|
+
} catch (rollbackSyncError) {
|
|
2313
|
+
throw recoveryError(
|
|
2314
|
+
"the previous home was restored but its directory entry could not be made durable",
|
|
2315
|
+
targetHome,
|
|
2316
|
+
rollbackSyncError
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
try {
|
|
2320
|
+
lifecycle?.markInstalled();
|
|
2321
|
+
} catch (phaseError) {
|
|
2322
|
+
throw recoveryError(
|
|
2323
|
+
"the previous home was restored but could not be marked safe",
|
|
2324
|
+
targetHome,
|
|
2325
|
+
phaseError
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
} else {
|
|
2329
|
+
lifecycle?.markInstalled();
|
|
2330
|
+
}
|
|
2331
|
+
throw installError;
|
|
2332
|
+
}
|
|
2333
|
+
try {
|
|
2334
|
+
syncDirectory(targetParent);
|
|
2335
|
+
} catch (installSyncError) {
|
|
2336
|
+
throw recoveryError(
|
|
2337
|
+
"the restored home was installed but its directory entry could not be made durable",
|
|
2338
|
+
hadPrevious ? previous : targetHome,
|
|
2339
|
+
installSyncError
|
|
2340
|
+
);
|
|
2341
|
+
}
|
|
2342
|
+
try {
|
|
2343
|
+
lifecycle?.markInstalled();
|
|
2344
|
+
} catch (phaseError) {
|
|
2345
|
+
throw recoveryError(
|
|
2346
|
+
"the restored home was installed but could not be marked safe",
|
|
2347
|
+
hadPrevious ? previous : targetHome,
|
|
2348
|
+
phaseError
|
|
2349
|
+
);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
var RestoreRecoveryRequiredError = class extends Error {
|
|
2353
|
+
recoveryPath;
|
|
2354
|
+
constructor(message, recoveryPath) {
|
|
2355
|
+
super(message);
|
|
2356
|
+
this.name = "RestoreRecoveryRequiredError";
|
|
2357
|
+
this.recoveryPath = recoveryPath;
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
2360
|
+
function fsyncDirectory(path) {
|
|
2361
|
+
let fd = null;
|
|
2362
|
+
try {
|
|
2363
|
+
fd = openSync2(path, "r");
|
|
2364
|
+
fsyncSync2(fd);
|
|
2365
|
+
} catch (error) {
|
|
2366
|
+
if (process.platform !== "win32") throw error;
|
|
2367
|
+
} finally {
|
|
2368
|
+
if (fd !== null) closeSync2(fd);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
var restoreCmd = defineCommand5({
|
|
1116
2372
|
meta: {
|
|
1117
2373
|
name: "restore",
|
|
1118
2374
|
description: "Extract a backup tar.gz into ~/.bazilion (offline; server must be stopped)"
|
|
@@ -1130,42 +2386,96 @@ var restoreCmd = defineCommand4({
|
|
|
1130
2386
|
}
|
|
1131
2387
|
},
|
|
1132
2388
|
async run({ args }) {
|
|
1133
|
-
const file =
|
|
2389
|
+
const file = resolve2(args.file);
|
|
1134
2390
|
if (!existsSync2(file)) throw new Error(`backup file not found: ${file}`);
|
|
1135
2391
|
const paths = resolveCliPaths(args.home);
|
|
1136
|
-
const targetHome = paths.home;
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
throw new Error(
|
|
1140
|
-
"bazilion server appears to be running on :4321 \u2014 stop it first (DB corruption risk), or pass --force to override."
|
|
1141
|
-
);
|
|
2392
|
+
const targetHome = resolve2(paths.home);
|
|
2393
|
+
if (targetHome === parse(targetHome).root) {
|
|
2394
|
+
throw new Error(`refusing to restore over filesystem root: ${targetHome}`);
|
|
1142
2395
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
2396
|
+
const targetParent = dirname2(targetHome);
|
|
2397
|
+
mkdirSync2(targetParent, { recursive: true });
|
|
2398
|
+
const restoreLock = await acquireHomeRestoreLock(targetHome);
|
|
2399
|
+
try {
|
|
2400
|
+
const targetsConfiguredHome = targetHome === resolve2(resolveCliPaths().home);
|
|
2401
|
+
if (targetsConfiguredHome) {
|
|
2402
|
+
for (const serverUrl of configuredLoopbackUrls()) {
|
|
2403
|
+
if (await probeServerRunning(serverUrl)) {
|
|
2404
|
+
throw new Error(
|
|
2405
|
+
`bazilion server appears to be running at ${serverUrl} \u2014 stop it before restoring.`
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
if (existsSync2(targetHome)) {
|
|
2411
|
+
if (!lstatSync2(targetHome).isDirectory()) {
|
|
2412
|
+
throw new Error(`${targetHome} exists and is not a directory`);
|
|
2413
|
+
}
|
|
2414
|
+
const entries = readdirSync(targetHome);
|
|
2415
|
+
if (entries.length > 0 && !args.force) {
|
|
1147
2416
|
throw new Error(
|
|
1148
2417
|
`${targetHome} is not empty. Pass --force to overwrite (destroys existing data).`
|
|
1149
2418
|
);
|
|
1150
2419
|
}
|
|
1151
|
-
rmSync(targetHome, { recursive: true, force: true });
|
|
1152
2420
|
}
|
|
2421
|
+
const staging = mkdtempSync(resolve2(targetParent, ".bazilion-restore-"));
|
|
2422
|
+
const stagedArchive = resolve2(staging, "backup.tar.gz");
|
|
2423
|
+
const payload = resolve2(staging, "payload");
|
|
2424
|
+
let preserveStaging = false;
|
|
2425
|
+
try {
|
|
2426
|
+
copyFileSync(file, stagedArchive);
|
|
2427
|
+
await validateArchive(stagedArchive);
|
|
2428
|
+
mkdirSync2(payload);
|
|
2429
|
+
await extract({
|
|
2430
|
+
file: stagedArchive,
|
|
2431
|
+
cwd: payload,
|
|
2432
|
+
strict: true,
|
|
2433
|
+
preserveOwner: false,
|
|
2434
|
+
// The private copy has already passed complete entry-path and link
|
|
2435
|
+
// validation above. Preserve paths here so an intentionally absolute
|
|
2436
|
+
// external linked-Team target is not stripped into a different,
|
|
2437
|
+
// payload-relative path by npm-tar.
|
|
2438
|
+
preservePaths: true
|
|
2439
|
+
});
|
|
2440
|
+
const database = resolve2(payload, "bazilion.db");
|
|
2441
|
+
const authFile = resolve2(payload, "auth.json");
|
|
2442
|
+
if (!lstatSync2(database).isFile() || !lstatSync2(authFile).isFile()) {
|
|
2443
|
+
throw new Error("backup required files did not extract as regular files");
|
|
2444
|
+
}
|
|
2445
|
+
const authToken = validateAuthFile(authFile);
|
|
2446
|
+
validateDatabase(database, authToken);
|
|
2447
|
+
rebaseRestoredHomeDirectories(database, payload, targetHome);
|
|
2448
|
+
validateDatabase(database, authToken);
|
|
2449
|
+
removeRebuildableQmdIndexes(payload);
|
|
2450
|
+
console.log(`installing validated backup ${file} \u2192 ${targetHome}`);
|
|
2451
|
+
try {
|
|
2452
|
+
installValidatedPayload(payload, targetHome, staging, restoreLock);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
if (error instanceof RestoreRecoveryRequiredError) {
|
|
2455
|
+
preserveStaging = true;
|
|
2456
|
+
}
|
|
2457
|
+
throw error;
|
|
2458
|
+
}
|
|
2459
|
+
} finally {
|
|
2460
|
+
if (!preserveStaging) {
|
|
2461
|
+
try {
|
|
2462
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
2463
|
+
} catch (error) {
|
|
2464
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2465
|
+
console.warn(
|
|
2466
|
+
`warning: could not remove restore staging directory ${staging}: ${detail}`
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
} finally {
|
|
2472
|
+
restoreLock.release();
|
|
1153
2473
|
}
|
|
1154
|
-
mkdirSync(targetHome, { recursive: true });
|
|
1155
|
-
console.log(`extracting ${file} \u2192 ${targetHome}`);
|
|
1156
|
-
await new Promise((res, rej) => {
|
|
1157
|
-
const proc = spawn("tar", ["-xzf", file, "-C", targetHome], { stdio: "inherit" });
|
|
1158
|
-
proc.on("error", rej);
|
|
1159
|
-
proc.on("exit", (code) => {
|
|
1160
|
-
if (code === 0) res();
|
|
1161
|
-
else rej(new Error(`tar exited with code ${code}`));
|
|
1162
|
-
});
|
|
1163
|
-
});
|
|
1164
2474
|
console.log(`restored to ${targetHome}`);
|
|
1165
2475
|
console.log("start the server with: bazilion serve (migrations apply automatically)");
|
|
1166
2476
|
}
|
|
1167
2477
|
});
|
|
1168
|
-
var backupCommand =
|
|
2478
|
+
var backupCommand = defineCommand5({
|
|
1169
2479
|
meta: { name: "backup", description: "Create or restore a ~/.bazilion tar.gz backup" },
|
|
1170
2480
|
subCommands: {
|
|
1171
2481
|
create: createCmd,
|
|
@@ -1179,7 +2489,7 @@ var backupCommand = defineCommand4({
|
|
|
1179
2489
|
});
|
|
1180
2490
|
|
|
1181
2491
|
// src/commands/completion.ts
|
|
1182
|
-
import { defineCommand as
|
|
2492
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
1183
2493
|
async function walk(cmd, path, out) {
|
|
1184
2494
|
const subsRaw = cmd.subCommands ?? {};
|
|
1185
2495
|
const subNames = Object.keys(subsRaw);
|
|
@@ -1284,7 +2594,7 @@ function fishScript(flat) {
|
|
|
1284
2594
|
`;
|
|
1285
2595
|
}
|
|
1286
2596
|
function completionCommand(main2) {
|
|
1287
|
-
return
|
|
2597
|
+
return defineCommand6({
|
|
1288
2598
|
meta: {
|
|
1289
2599
|
name: "completion",
|
|
1290
2600
|
description: "Print a shell completion script (bash, zsh, or fish)"
|
|
@@ -1309,13 +2619,13 @@ function completionCommand(main2) {
|
|
|
1309
2619
|
}
|
|
1310
2620
|
|
|
1311
2621
|
// src/commands/config.ts
|
|
1312
|
-
import { defineCommand as
|
|
2622
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
1313
2623
|
function displayValue(f) {
|
|
1314
2624
|
if (!f.set) return "(unset)";
|
|
1315
2625
|
if (f.kind === "secret") return f.preview ?? "(set)";
|
|
1316
2626
|
return f.value ?? "(set)";
|
|
1317
2627
|
}
|
|
1318
|
-
var listCmd2 =
|
|
2628
|
+
var listCmd2 = defineCommand7({
|
|
1319
2629
|
meta: {
|
|
1320
2630
|
name: "list",
|
|
1321
2631
|
description: "List every configurable field (credentials + URLs) across all services"
|
|
@@ -1338,7 +2648,7 @@ var listCmd2 = defineCommand6({
|
|
|
1338
2648
|
for (const line of columnize(rows)) console.log(line);
|
|
1339
2649
|
}
|
|
1340
2650
|
});
|
|
1341
|
-
var setCmd =
|
|
2651
|
+
var setCmd = defineCommand7({
|
|
1342
2652
|
meta: {
|
|
1343
2653
|
name: "set",
|
|
1344
2654
|
description: "Set a configurable field (auto-routes to secrets or plaintext store)"
|
|
@@ -1365,7 +2675,7 @@ var setCmd = defineCommand6({
|
|
|
1365
2675
|
console.log(`${state.envVar} (${state.kind}): ${displayValue(state)}`);
|
|
1366
2676
|
}
|
|
1367
2677
|
});
|
|
1368
|
-
var rmCmd =
|
|
2678
|
+
var rmCmd = defineCommand7({
|
|
1369
2679
|
meta: { name: "rm", description: "Remove a configurable field value" },
|
|
1370
2680
|
args: {
|
|
1371
2681
|
envVar: { type: "positional", required: true, description: "Env var name to clear" }
|
|
@@ -1376,7 +2686,7 @@ var rmCmd = defineCommand6({
|
|
|
1376
2686
|
console.log(`removed ${args.envVar}`);
|
|
1377
2687
|
}
|
|
1378
2688
|
});
|
|
1379
|
-
var configCommand =
|
|
2689
|
+
var configCommand = defineCommand7({
|
|
1380
2690
|
meta: {
|
|
1381
2691
|
name: "config",
|
|
1382
2692
|
description: "Manage service configuration \u2014 credentials (encrypted) and URLs/IDs (plaintext), unified under one CLI"
|
|
@@ -1389,19 +2699,19 @@ var configCommand = defineCommand6({
|
|
|
1389
2699
|
});
|
|
1390
2700
|
|
|
1391
2701
|
// src/commands/dashboard.ts
|
|
1392
|
-
import { spawn
|
|
2702
|
+
import { spawn } from "child_process";
|
|
1393
2703
|
import { existsSync as existsSync3 } from "fs";
|
|
1394
2704
|
import { connect } from "net";
|
|
1395
|
-
import { join as
|
|
1396
|
-
import { defineCommand as
|
|
1397
|
-
var bundledDaemonEntry =
|
|
1398
|
-
var sourceDaemonEntry =
|
|
2705
|
+
import { join as join3 } from "path";
|
|
2706
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
2707
|
+
var bundledDaemonEntry = join3(import.meta.dirname, "daemon.js");
|
|
2708
|
+
var sourceDaemonEntry = join3(import.meta.dirname, "..", "..", "..", "daemon", "src", "index.ts");
|
|
1399
2709
|
var daemonEntry = existsSync3(bundledDaemonEntry) ? bundledDaemonEntry : sourceDaemonEntry;
|
|
1400
|
-
var bundledWebServerEntry =
|
|
1401
|
-
var sourceWebServerEntry =
|
|
2710
|
+
var bundledWebServerEntry = join3(import.meta.dirname, "web-server.js");
|
|
2711
|
+
var sourceWebServerEntry = join3(import.meta.dirname, "..", "web-server.ts");
|
|
1402
2712
|
var webServerEntry = existsSync3(bundledWebServerEntry) ? bundledWebServerEntry : sourceWebServerEntry;
|
|
1403
|
-
var bundledWebDist =
|
|
1404
|
-
var sourceWebDist =
|
|
2713
|
+
var bundledWebDist = join3(import.meta.dirname, "web");
|
|
2714
|
+
var sourceWebDist = join3(import.meta.dirname, "..", "..", "..", "web", "dist");
|
|
1405
2715
|
var webDist = existsSync3(bundledWebDist) ? bundledWebDist : sourceWebDist;
|
|
1406
2716
|
function nodeArgs(entry2) {
|
|
1407
2717
|
return entry2.endsWith(".ts") ? ["--import", "tsx/esm", entry2] : [entry2];
|
|
@@ -1420,12 +2730,12 @@ function probeHost(host) {
|
|
|
1420
2730
|
return host;
|
|
1421
2731
|
}
|
|
1422
2732
|
function isTcpPortOpen(host, port) {
|
|
1423
|
-
return new Promise((
|
|
2733
|
+
return new Promise((resolve4) => {
|
|
1424
2734
|
const socket = connect({ host: probeHost(host), port: Number(port) });
|
|
1425
2735
|
const done = (open) => {
|
|
1426
2736
|
socket.removeAllListeners();
|
|
1427
2737
|
socket.destroy();
|
|
1428
|
-
|
|
2738
|
+
resolve4(open);
|
|
1429
2739
|
};
|
|
1430
2740
|
socket.setTimeout(600);
|
|
1431
2741
|
socket.once("connect", () => done(true));
|
|
@@ -1475,19 +2785,19 @@ function openBrowser(url) {
|
|
|
1475
2785
|
const platform = process.platform;
|
|
1476
2786
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : process.env.BROWSER || "xdg-open";
|
|
1477
2787
|
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1478
|
-
const proc =
|
|
2788
|
+
const proc = spawn(cmd, args, { detached: true, stdio: "ignore" });
|
|
1479
2789
|
proc.unref();
|
|
1480
2790
|
}
|
|
1481
2791
|
function spawnProcess(label, entry2, env) {
|
|
1482
2792
|
if (!existsSync3(entry2)) {
|
|
1483
2793
|
throw new Error(`${label} entry not found at ${entry2}`);
|
|
1484
2794
|
}
|
|
1485
|
-
return
|
|
2795
|
+
return spawn(process.execPath, nodeArgs(entry2), {
|
|
1486
2796
|
env,
|
|
1487
2797
|
stdio: "inherit"
|
|
1488
2798
|
});
|
|
1489
2799
|
}
|
|
1490
|
-
var dashboardCommand =
|
|
2800
|
+
var dashboardCommand = defineCommand8({
|
|
1491
2801
|
meta: {
|
|
1492
2802
|
name: "dashboard",
|
|
1493
2803
|
description: "Start the bazilion daemon and bundled web UI"
|
|
@@ -1564,7 +2874,7 @@ var dashboardCommand = defineCommand7({
|
|
|
1564
2874
|
`web UI port ${webPort} is already in use. Re-run with --port <free-port>.`
|
|
1565
2875
|
);
|
|
1566
2876
|
}
|
|
1567
|
-
if (!existsSync3(
|
|
2877
|
+
if (!existsSync3(join3(webDist, "server", "server.js"))) {
|
|
1568
2878
|
throw new Error(
|
|
1569
2879
|
`bazilion web UI bundle not found at ${webDist}. Run "pnpm --filter @bazilion/web build" from a source checkout, or reinstall the published package.`
|
|
1570
2880
|
);
|
|
@@ -1591,13 +2901,13 @@ var dashboardCommand = defineCommand7({
|
|
|
1591
2901
|
if (children.length === 0) return;
|
|
1592
2902
|
process.on("SIGINT", terminateChildren);
|
|
1593
2903
|
process.on("SIGTERM", terminateChildren);
|
|
1594
|
-
await new Promise((
|
|
2904
|
+
await new Promise((resolve4) => {
|
|
1595
2905
|
let remaining = children.length;
|
|
1596
2906
|
for (const child of children) {
|
|
1597
2907
|
child.on("close", (code) => {
|
|
1598
2908
|
remaining--;
|
|
1599
2909
|
if (!signalled && code && code !== 0) process.exitCode = code;
|
|
1600
|
-
if (remaining === 0)
|
|
2910
|
+
if (remaining === 0) resolve4();
|
|
1601
2911
|
});
|
|
1602
2912
|
}
|
|
1603
2913
|
});
|
|
@@ -1606,8 +2916,8 @@ var dashboardCommand = defineCommand7({
|
|
|
1606
2916
|
});
|
|
1607
2917
|
|
|
1608
2918
|
// src/commands/doctor.ts
|
|
1609
|
-
import { defineCommand as
|
|
1610
|
-
var doctorCommand =
|
|
2919
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
2920
|
+
var doctorCommand = defineCommand9({
|
|
1611
2921
|
meta: { name: "doctor", description: "Diagnose your bazilion install" },
|
|
1612
2922
|
async run() {
|
|
1613
2923
|
const client = createClient2();
|
|
@@ -1688,6 +2998,36 @@ var doctorCommand = defineCommand8({
|
|
|
1688
2998
|
` ${r.scheduler.enabled ? "\u2713" : "-"} scheduler${r.scheduler.enabled ? ` (tick ${r.scheduler.tickMs}ms)` : " (BAZILION_SCHEDULER=off)"}`
|
|
1689
2999
|
);
|
|
1690
3000
|
console.log();
|
|
3001
|
+
console.log("agent shell security");
|
|
3002
|
+
if (!r.shellSecurity.ok) {
|
|
3003
|
+
check("valid shell-security configuration", false, r.shellSecurity.error);
|
|
3004
|
+
} else {
|
|
3005
|
+
if (r.shellSecurity.approvalMode === "dangerous") {
|
|
3006
|
+
console.log(" - dangerous-command approval enabled");
|
|
3007
|
+
console.log(
|
|
3008
|
+
" web and TTY CLI turns can allow once; background and non-TTY turns auto-deny"
|
|
3009
|
+
);
|
|
3010
|
+
} else {
|
|
3011
|
+
console.log(" - dangerous-command approval off");
|
|
3012
|
+
console.log(" set BAZILION_BASH_APPROVAL=dangerous to opt in");
|
|
3013
|
+
}
|
|
3014
|
+
if (r.shellSecurity.sandboxMode === "docker") {
|
|
3015
|
+
console.log(` - Docker sandbox configured (${r.shellSecurity.sandboxImage})`);
|
|
3016
|
+
console.log(
|
|
3017
|
+
" workspace-only writable mount \xB7 bounded inputs/skills/memory read-only \xB7 network disabled"
|
|
3018
|
+
);
|
|
3019
|
+
console.log(" host coding tools hidden");
|
|
3020
|
+
console.log(
|
|
3021
|
+
" policy syntax is valid; Docker and image availability are checked on execution"
|
|
3022
|
+
);
|
|
3023
|
+
console.log(" commands fail closed if either is unavailable");
|
|
3024
|
+
} else {
|
|
3025
|
+
console.log(" - sandbox off (agent shell and coding tools run on the host)");
|
|
3026
|
+
console.log(" approval is a host-execution tripwire, not a filesystem sandbox");
|
|
3027
|
+
console.log(" set BAZILION_BASH_SANDBOX=docker to opt in");
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
console.log();
|
|
1691
3031
|
console.log("operational counts");
|
|
1692
3032
|
console.log(` ${r.triggers.active} active trigger(s), ${r.triggers.disabled} disabled`);
|
|
1693
3033
|
console.log(` ${r.tokens.active} active web token(s)`);
|
|
@@ -1719,7 +3059,7 @@ var doctorCommand = defineCommand8({
|
|
|
1719
3059
|
});
|
|
1720
3060
|
|
|
1721
3061
|
// src/commands/inbox.ts
|
|
1722
|
-
import { defineCommand as
|
|
3062
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
1723
3063
|
function decodeText(payload) {
|
|
1724
3064
|
try {
|
|
1725
3065
|
const obj = JSON.parse(payload);
|
|
@@ -1735,7 +3075,7 @@ function messageRow(m) {
|
|
|
1735
3075
|
const preview = text2.length > 60 ? `${text2.slice(0, 60)}\u2026` : text2;
|
|
1736
3076
|
return [m.id, state, when, `from:${m.fromAgentId.slice(0, 8)}`, `"${preview}"`];
|
|
1737
3077
|
}
|
|
1738
|
-
var listCmd3 =
|
|
3078
|
+
var listCmd3 = defineCommand10({
|
|
1739
3079
|
meta: { name: "list", description: "List messages in an agent inbox" },
|
|
1740
3080
|
args: {
|
|
1741
3081
|
agent: { type: "positional", required: true, description: "Recipient agent id" },
|
|
@@ -1754,7 +3094,7 @@ var listCmd3 = defineCommand9({
|
|
|
1754
3094
|
for (const line of columnize(messages.map(messageRow))) console.log(line);
|
|
1755
3095
|
}
|
|
1756
3096
|
});
|
|
1757
|
-
var showCmd2 =
|
|
3097
|
+
var showCmd2 = defineCommand10({
|
|
1758
3098
|
meta: { name: "show", description: "Show a single message in full" },
|
|
1759
3099
|
args: {
|
|
1760
3100
|
id: { type: "positional", required: true, description: "Message id" }
|
|
@@ -1774,7 +3114,7 @@ var showCmd2 = defineCommand9({
|
|
|
1774
3114
|
console.log(decodeText(m.payload));
|
|
1775
3115
|
}
|
|
1776
3116
|
});
|
|
1777
|
-
var readCmd =
|
|
3117
|
+
var readCmd = defineCommand10({
|
|
1778
3118
|
meta: { name: "read", description: "Mark a message as read" },
|
|
1779
3119
|
args: {
|
|
1780
3120
|
id: { type: "positional", required: true, description: "Message id" }
|
|
@@ -1785,18 +3125,47 @@ var readCmd = defineCommand9({
|
|
|
1785
3125
|
console.log(`marked read: ${args.id}`);
|
|
1786
3126
|
}
|
|
1787
3127
|
});
|
|
1788
|
-
var
|
|
3128
|
+
var loopBreaksCmd = defineCommand10({
|
|
3129
|
+
meta: { name: "loop-breaks", description: "List stopped agent-message chains" },
|
|
3130
|
+
args: {
|
|
3131
|
+
agent: { type: "positional", required: true, description: "Agent id" },
|
|
3132
|
+
limit: { type: "string", description: "Maximum events to show (default 25)" }
|
|
3133
|
+
},
|
|
3134
|
+
async run({ args }) {
|
|
3135
|
+
const client = createClient2();
|
|
3136
|
+
const limit = args.limit ? `?limit=${encodeURIComponent(args.limit)}` : "";
|
|
3137
|
+
const { events } = await client.get(
|
|
3138
|
+
`/api/agents/${args.agent}/loop-breaks${limit}`
|
|
3139
|
+
);
|
|
3140
|
+
if (events.length === 0) {
|
|
3141
|
+
console.log("(no agent-message chains stopped)");
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
for (const line of columnize(
|
|
3145
|
+
events.map((event) => [
|
|
3146
|
+
new Date(event.createdAt).toISOString(),
|
|
3147
|
+
`chain:${event.causalChainId.slice(0, 8)}`,
|
|
3148
|
+
`${event.fromAgentId.slice(0, 8)}\u2192${event.toAgentId.slice(0, 8)}`,
|
|
3149
|
+
`hop:${event.attemptedHop}/${event.maxHops}`,
|
|
3150
|
+
event.reason
|
|
3151
|
+
])
|
|
3152
|
+
))
|
|
3153
|
+
console.log(line);
|
|
3154
|
+
}
|
|
3155
|
+
});
|
|
3156
|
+
var inboxCommand = defineCommand10({
|
|
1789
3157
|
meta: { name: "inbox", description: "Inspect agent inboxes" },
|
|
1790
3158
|
subCommands: {
|
|
1791
3159
|
list: listCmd3,
|
|
1792
3160
|
show: showCmd2,
|
|
1793
|
-
read: readCmd
|
|
3161
|
+
read: readCmd,
|
|
3162
|
+
"loop-breaks": loopBreaksCmd
|
|
1794
3163
|
}
|
|
1795
3164
|
});
|
|
1796
3165
|
|
|
1797
3166
|
// src/commands/login.ts
|
|
1798
|
-
import { existsSync as existsSync4, readFileSync as
|
|
1799
|
-
import { defineCommand as
|
|
3167
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
3168
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
1800
3169
|
function normalizeServer(raw) {
|
|
1801
3170
|
let url;
|
|
1802
3171
|
try {
|
|
@@ -1816,7 +3185,7 @@ async function verifyToken(server, token) {
|
|
|
1816
3185
|
if (res.status === 401) throw new Error("server rejected token (401)");
|
|
1817
3186
|
if (!res.ok) throw new Error(`server returned ${res.status} from /api/health`);
|
|
1818
3187
|
}
|
|
1819
|
-
var loginCommand =
|
|
3188
|
+
var loginCommand = defineCommand11({
|
|
1820
3189
|
meta: {
|
|
1821
3190
|
name: "login",
|
|
1822
3191
|
description: "Save a remote bazilion server + token pair to auth.json"
|
|
@@ -1834,7 +3203,7 @@ var loginCommand = defineCommand10({
|
|
|
1834
3203
|
`${paths.authFile} not found. Start the daemon with "bazilion serve" first \u2014 login stores the remote there.`
|
|
1835
3204
|
);
|
|
1836
3205
|
}
|
|
1837
|
-
const auth = JSON.parse(
|
|
3206
|
+
const auth = JSON.parse(readFileSync5(paths.authFile, "utf8"));
|
|
1838
3207
|
if (args.clear) {
|
|
1839
3208
|
if (!auth.remote) {
|
|
1840
3209
|
console.log("no remote stored \u2014 nothing to clear");
|
|
@@ -1864,7 +3233,7 @@ var loginCommand = defineCommand10({
|
|
|
1864
3233
|
});
|
|
1865
3234
|
|
|
1866
3235
|
// src/commands/mcp.ts
|
|
1867
|
-
import { defineCommand as
|
|
3236
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
1868
3237
|
function serverRow(s) {
|
|
1869
3238
|
const where = s.transport === "stdio" ? `${s.command} ${s.args.join(" ")}`.trim() : s.url ?? "";
|
|
1870
3239
|
return [
|
|
@@ -1876,7 +3245,7 @@ function serverRow(s) {
|
|
|
1876
3245
|
where
|
|
1877
3246
|
];
|
|
1878
3247
|
}
|
|
1879
|
-
var addCmd =
|
|
3248
|
+
var addCmd = defineCommand12({
|
|
1880
3249
|
meta: { name: "add", description: "Register an MCP server" },
|
|
1881
3250
|
args: {
|
|
1882
3251
|
name: { type: "positional", required: true, description: "Unique name ([a-zA-Z0-9_])" },
|
|
@@ -1903,7 +3272,7 @@ var addCmd = defineCommand11({
|
|
|
1903
3272
|
console.log(`${server.id} ${server.name} ${server.transport}`);
|
|
1904
3273
|
}
|
|
1905
3274
|
});
|
|
1906
|
-
var listCmd4 =
|
|
3275
|
+
var listCmd4 = defineCommand12({
|
|
1907
3276
|
meta: { name: "list", description: "List MCP servers" },
|
|
1908
3277
|
async run() {
|
|
1909
3278
|
const client = createClient2();
|
|
@@ -1915,7 +3284,7 @@ var listCmd4 = defineCommand11({
|
|
|
1915
3284
|
for (const line of columnize(servers.map(serverRow))) console.log(line);
|
|
1916
3285
|
}
|
|
1917
3286
|
});
|
|
1918
|
-
var showCmd3 =
|
|
3287
|
+
var showCmd3 = defineCommand12({
|
|
1919
3288
|
meta: { name: "show", description: "Show one MCP server" },
|
|
1920
3289
|
args: { id: { type: "positional", required: true } },
|
|
1921
3290
|
async run({ args }) {
|
|
@@ -1924,7 +3293,7 @@ var showCmd3 = defineCommand11({
|
|
|
1924
3293
|
console.log(JSON.stringify(server, null, 2));
|
|
1925
3294
|
}
|
|
1926
3295
|
});
|
|
1927
|
-
var rmCmd2 =
|
|
3296
|
+
var rmCmd2 = defineCommand12({
|
|
1928
3297
|
meta: { name: "rm", description: "Delete an MCP server" },
|
|
1929
3298
|
args: { id: { type: "positional", required: true } },
|
|
1930
3299
|
async run({ args }) {
|
|
@@ -1933,7 +3302,7 @@ var rmCmd2 = defineCommand11({
|
|
|
1933
3302
|
console.log(`removed MCP server ${args.id}`);
|
|
1934
3303
|
}
|
|
1935
3304
|
});
|
|
1936
|
-
var enableCmd =
|
|
3305
|
+
var enableCmd = defineCommand12({
|
|
1937
3306
|
meta: { name: "enable", description: "Enable an MCP server" },
|
|
1938
3307
|
args: { id: { type: "positional", required: true } },
|
|
1939
3308
|
async run({ args }) {
|
|
@@ -1942,7 +3311,7 @@ var enableCmd = defineCommand11({
|
|
|
1942
3311
|
console.log(`enabled MCP server ${args.id}`);
|
|
1943
3312
|
}
|
|
1944
3313
|
});
|
|
1945
|
-
var disableCmd =
|
|
3314
|
+
var disableCmd = defineCommand12({
|
|
1946
3315
|
meta: { name: "disable", description: "Disable an MCP server" },
|
|
1947
3316
|
args: { id: { type: "positional", required: true } },
|
|
1948
3317
|
async run({ args }) {
|
|
@@ -1951,7 +3320,7 @@ var disableCmd = defineCommand11({
|
|
|
1951
3320
|
console.log(`disabled MCP server ${args.id}`);
|
|
1952
3321
|
}
|
|
1953
3322
|
});
|
|
1954
|
-
var testCmd =
|
|
3323
|
+
var testCmd = defineCommand12({
|
|
1955
3324
|
meta: { name: "test", description: "Connect to a server and list its tools" },
|
|
1956
3325
|
args: { id: { type: "positional", required: true } },
|
|
1957
3326
|
async run({ args }) {
|
|
@@ -1969,7 +3338,7 @@ var testCmd = defineCommand11({
|
|
|
1969
3338
|
for (const t of tools) console.log(` ${t.name} ${t.description}`);
|
|
1970
3339
|
}
|
|
1971
3340
|
});
|
|
1972
|
-
var mcpCommand =
|
|
3341
|
+
var mcpCommand = defineCommand12({
|
|
1973
3342
|
meta: { name: "mcp", description: "Manage MCP servers" },
|
|
1974
3343
|
subCommands: {
|
|
1975
3344
|
add: addCmd,
|
|
@@ -1983,11 +3352,11 @@ var mcpCommand = defineCommand11({
|
|
|
1983
3352
|
});
|
|
1984
3353
|
|
|
1985
3354
|
// src/commands/memory.ts
|
|
1986
|
-
import { defineCommand as
|
|
3355
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
1987
3356
|
function encodeKey(key) {
|
|
1988
3357
|
return key.split("/").map(encodeURIComponent).join("/");
|
|
1989
3358
|
}
|
|
1990
|
-
var writeCmd =
|
|
3359
|
+
var writeCmd = defineCommand13({
|
|
1991
3360
|
meta: { name: "write", description: "Write an entry into a team's shared memory" },
|
|
1992
3361
|
args: {
|
|
1993
3362
|
team: { type: "positional", required: true, description: "Team slug" },
|
|
@@ -2003,7 +3372,7 @@ var writeCmd = defineCommand12({
|
|
|
2003
3372
|
console.log(`wrote ${entry2.key} (${entry2.content.length} bytes)`);
|
|
2004
3373
|
}
|
|
2005
3374
|
});
|
|
2006
|
-
var readCmd2 =
|
|
3375
|
+
var readCmd2 = defineCommand13({
|
|
2007
3376
|
meta: { name: "read", description: "Read an entry from a team's shared memory" },
|
|
2008
3377
|
args: {
|
|
2009
3378
|
team: { type: "positional", required: true, description: "Team slug" },
|
|
@@ -2017,7 +3386,7 @@ var readCmd2 = defineCommand12({
|
|
|
2017
3386
|
console.log(entry2.content);
|
|
2018
3387
|
}
|
|
2019
3388
|
});
|
|
2020
|
-
var searchCmd =
|
|
3389
|
+
var searchCmd = defineCommand13({
|
|
2021
3390
|
meta: { name: "search", description: "Search a team's shared memory" },
|
|
2022
3391
|
args: {
|
|
2023
3392
|
team: { type: "positional", required: true, description: "Team slug" },
|
|
@@ -2038,7 +3407,7 @@ var searchCmd = defineCommand12({
|
|
|
2038
3407
|
}
|
|
2039
3408
|
}
|
|
2040
3409
|
});
|
|
2041
|
-
var listCmd5 =
|
|
3410
|
+
var listCmd5 = defineCommand13({
|
|
2042
3411
|
meta: { name: "list", description: "List all entries in a team's shared memory" },
|
|
2043
3412
|
args: {
|
|
2044
3413
|
team: { type: "positional", required: true, description: "Team slug" }
|
|
@@ -2055,7 +3424,7 @@ var listCmd5 = defineCommand12({
|
|
|
2055
3424
|
}
|
|
2056
3425
|
}
|
|
2057
3426
|
});
|
|
2058
|
-
var rmCmd3 =
|
|
3427
|
+
var rmCmd3 = defineCommand13({
|
|
2059
3428
|
meta: { name: "rm", description: "Remove an entry from a team's shared memory" },
|
|
2060
3429
|
args: {
|
|
2061
3430
|
team: { type: "positional", required: true, description: "Team slug" },
|
|
@@ -2067,7 +3436,7 @@ var rmCmd3 = defineCommand12({
|
|
|
2067
3436
|
console.log(`removed ${args.key}`);
|
|
2068
3437
|
}
|
|
2069
3438
|
});
|
|
2070
|
-
var memoryCommand =
|
|
3439
|
+
var memoryCommand = defineCommand13({
|
|
2071
3440
|
meta: {
|
|
2072
3441
|
name: "memory",
|
|
2073
3442
|
description: "Manage a team's shared memory (BM25-indexed markdown notes)"
|
|
@@ -2082,11 +3451,11 @@ var memoryCommand = defineCommand12({
|
|
|
2082
3451
|
});
|
|
2083
3452
|
|
|
2084
3453
|
// src/commands/profile.ts
|
|
2085
|
-
import { spawn as
|
|
3454
|
+
import { spawn as spawn2 } from "child_process";
|
|
2086
3455
|
import { randomBytes } from "crypto";
|
|
2087
|
-
import { readFileSync as
|
|
2088
|
-
import { tmpdir } from "os";
|
|
2089
|
-
import { join as
|
|
3456
|
+
import { readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3457
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
3458
|
+
import { join as join4 } from "path";
|
|
2090
3459
|
|
|
2091
3460
|
// ../../packages/api-types/src/index.ts
|
|
2092
3461
|
var PROFILE_FILES = [
|
|
@@ -2099,7 +3468,7 @@ var PROFILE_FILES = [
|
|
|
2099
3468
|
];
|
|
2100
3469
|
|
|
2101
3470
|
// src/commands/profile.ts
|
|
2102
|
-
import { defineCommand as
|
|
3471
|
+
import { defineCommand as defineCommand14 } from "citty";
|
|
2103
3472
|
function splitCsv(v) {
|
|
2104
3473
|
if (!v) return void 0;
|
|
2105
3474
|
const out = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -2110,7 +3479,7 @@ function parseSkillsMode(v) {
|
|
|
2110
3479
|
if (v === "all" || v === "selected") return v;
|
|
2111
3480
|
throw new Error(`--skills-mode must be 'all' or 'selected', got '${v}'`);
|
|
2112
3481
|
}
|
|
2113
|
-
var createCmd2 =
|
|
3482
|
+
var createCmd2 = defineCommand14({
|
|
2114
3483
|
meta: { name: "create", description: "Create a new profile" },
|
|
2115
3484
|
args: {
|
|
2116
3485
|
id: { type: "positional", required: true, description: "Profile slug" },
|
|
@@ -2155,7 +3524,7 @@ var createCmd2 = defineCommand13({
|
|
|
2155
3524
|
},
|
|
2156
3525
|
async run({ args }) {
|
|
2157
3526
|
const skillsMode = parseSkillsMode(args["skills-mode"]);
|
|
2158
|
-
const readTemplate = (path) => path ?
|
|
3527
|
+
const readTemplate = (path) => path ? readFileSync6(path, "utf8") : void 0;
|
|
2159
3528
|
const body = {
|
|
2160
3529
|
id: args.id,
|
|
2161
3530
|
name: args.name,
|
|
@@ -2173,20 +3542,20 @@ var createCmd2 = defineCommand13({
|
|
|
2173
3542
|
console.log(`created profile ${profile.id} at ${profile.dir}`);
|
|
2174
3543
|
}
|
|
2175
3544
|
});
|
|
2176
|
-
var listCmd6 =
|
|
3545
|
+
var listCmd6 = defineCommand14({
|
|
2177
3546
|
meta: { name: "list", description: "List profiles" },
|
|
2178
3547
|
async run() {
|
|
2179
3548
|
const client = createClient2();
|
|
2180
|
-
const
|
|
2181
|
-
if (
|
|
3549
|
+
const list5 = await client.get("/api/profiles");
|
|
3550
|
+
if (list5.length === 0) {
|
|
2182
3551
|
console.log("(no profiles)");
|
|
2183
3552
|
return;
|
|
2184
3553
|
}
|
|
2185
|
-
const rows =
|
|
3554
|
+
const rows = list5.map((p) => [p.id, p.defaultModel, p.name]);
|
|
2186
3555
|
for (const line of columnize(rows)) console.log(line);
|
|
2187
3556
|
}
|
|
2188
3557
|
});
|
|
2189
|
-
var showCmd4 =
|
|
3558
|
+
var showCmd4 = defineCommand14({
|
|
2190
3559
|
meta: { name: "show", description: "Show profile details" },
|
|
2191
3560
|
args: {
|
|
2192
3561
|
id: { type: "positional", required: true }
|
|
@@ -2211,7 +3580,7 @@ var showCmd4 = defineCommand13({
|
|
|
2211
3580
|
}
|
|
2212
3581
|
}
|
|
2213
3582
|
});
|
|
2214
|
-
var editCmd2 =
|
|
3583
|
+
var editCmd2 = defineCommand14({
|
|
2215
3584
|
meta: { name: "edit", description: "Open a profile file in $EDITOR" },
|
|
2216
3585
|
args: {
|
|
2217
3586
|
id: { type: "positional", required: true },
|
|
@@ -2230,17 +3599,17 @@ var editCmd2 = defineCommand13({
|
|
|
2230
3599
|
const { content } = await client.get(
|
|
2231
3600
|
`/api/profiles/${args.id}/files/${file}`
|
|
2232
3601
|
);
|
|
2233
|
-
const tmpPath =
|
|
3602
|
+
const tmpPath = join4(tmpdir2(), `bazilion-${args.id}-${file}-${randomBytes(4).toString("hex")}`);
|
|
2234
3603
|
writeFileSync3(tmpPath, content);
|
|
2235
3604
|
try {
|
|
2236
3605
|
const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
2237
|
-
const code = await new Promise((
|
|
2238
|
-
const child =
|
|
3606
|
+
const code = await new Promise((resolve4, reject) => {
|
|
3607
|
+
const child = spawn2(editor, [tmpPath], { stdio: "inherit" });
|
|
2239
3608
|
child.on("error", reject);
|
|
2240
|
-
child.on("close", (c) =>
|
|
3609
|
+
child.on("close", (c) => resolve4(c ?? 0));
|
|
2241
3610
|
});
|
|
2242
3611
|
if (code !== 0) throw new Error(`editor exited with code ${code}`);
|
|
2243
|
-
const updated =
|
|
3612
|
+
const updated = readFileSync6(tmpPath, "utf8");
|
|
2244
3613
|
if (updated === content) {
|
|
2245
3614
|
console.log("(no changes)");
|
|
2246
3615
|
return;
|
|
@@ -2250,13 +3619,13 @@ var editCmd2 = defineCommand13({
|
|
|
2250
3619
|
console.log(`saved ${file}`);
|
|
2251
3620
|
} finally {
|
|
2252
3621
|
try {
|
|
2253
|
-
|
|
3622
|
+
rmSync3(tmpPath, { force: true });
|
|
2254
3623
|
} catch {
|
|
2255
3624
|
}
|
|
2256
3625
|
}
|
|
2257
3626
|
}
|
|
2258
3627
|
});
|
|
2259
|
-
var updateCmd =
|
|
3628
|
+
var updateCmd = defineCommand14({
|
|
2260
3629
|
meta: {
|
|
2261
3630
|
name: "update",
|
|
2262
3631
|
description: "Update profile settings (name, model, skills-mode, skills)"
|
|
@@ -2290,7 +3659,7 @@ var updateCmd = defineCommand13({
|
|
|
2290
3659
|
console.log(`updated profile ${profile.id}`);
|
|
2291
3660
|
}
|
|
2292
3661
|
});
|
|
2293
|
-
var deleteCmd2 =
|
|
3662
|
+
var deleteCmd2 = defineCommand14({
|
|
2294
3663
|
meta: { name: "delete", description: "Permanently delete a profile and its files" },
|
|
2295
3664
|
args: {
|
|
2296
3665
|
id: { type: "positional", required: true }
|
|
@@ -2301,7 +3670,7 @@ var deleteCmd2 = defineCommand13({
|
|
|
2301
3670
|
console.log(`deleted profile ${args.id}`);
|
|
2302
3671
|
}
|
|
2303
3672
|
});
|
|
2304
|
-
var profileCommand =
|
|
3673
|
+
var profileCommand = defineCommand14({
|
|
2305
3674
|
meta: { name: "profile", description: "Manage profiles" },
|
|
2306
3675
|
subCommands: {
|
|
2307
3676
|
create: createCmd2,
|
|
@@ -2314,8 +3683,8 @@ var profileCommand = defineCommand13({
|
|
|
2314
3683
|
});
|
|
2315
3684
|
|
|
2316
3685
|
// src/commands/provider.ts
|
|
2317
|
-
import { defineCommand as
|
|
2318
|
-
var listCmd7 =
|
|
3686
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
3687
|
+
var listCmd7 = defineCommand15({
|
|
2319
3688
|
meta: {
|
|
2320
3689
|
name: "list",
|
|
2321
3690
|
description: "List all providers with enabled/disabled state and curated model counts"
|
|
@@ -2341,7 +3710,7 @@ var listCmd7 = defineCommand14({
|
|
|
2341
3710
|
for (const line of columnize(rows)) console.log(line);
|
|
2342
3711
|
}
|
|
2343
3712
|
});
|
|
2344
|
-
var modelsCmd =
|
|
3713
|
+
var modelsCmd = defineCommand15({
|
|
2345
3714
|
meta: {
|
|
2346
3715
|
name: "models",
|
|
2347
3716
|
description: "Show curated + catalog + live models for one provider"
|
|
@@ -2373,7 +3742,7 @@ var modelsCmd = defineCommand14({
|
|
|
2373
3742
|
}
|
|
2374
3743
|
}
|
|
2375
3744
|
});
|
|
2376
|
-
var modelsSetCmd =
|
|
3745
|
+
var modelsSetCmd = defineCommand15({
|
|
2377
3746
|
meta: {
|
|
2378
3747
|
name: "models-set",
|
|
2379
3748
|
description: "Replace the curated model list for a provider (comma-separated)"
|
|
@@ -2398,7 +3767,7 @@ var modelsSetCmd = defineCommand14({
|
|
|
2398
3767
|
for (const m of saved.models) console.log(` ${m}`);
|
|
2399
3768
|
}
|
|
2400
3769
|
});
|
|
2401
|
-
var enableCmd2 =
|
|
3770
|
+
var enableCmd2 = defineCommand15({
|
|
2402
3771
|
meta: { name: "enable", description: "Toggle a provider on so agents can use it" },
|
|
2403
3772
|
args: {
|
|
2404
3773
|
name: { type: "positional", required: true, description: "Provider id (e.g. anthropic)" }
|
|
@@ -2412,7 +3781,7 @@ var enableCmd2 = defineCommand14({
|
|
|
2412
3781
|
console.log(`${res.name}: enabled`);
|
|
2413
3782
|
}
|
|
2414
3783
|
});
|
|
2415
|
-
var disableCmd2 =
|
|
3784
|
+
var disableCmd2 = defineCommand15({
|
|
2416
3785
|
meta: {
|
|
2417
3786
|
name: "disable",
|
|
2418
3787
|
description: "Toggle a provider off \u2014 agents will refuse its model strings"
|
|
@@ -2429,7 +3798,7 @@ var disableCmd2 = defineCommand14({
|
|
|
2429
3798
|
console.log(`${res.name}: disabled`);
|
|
2430
3799
|
}
|
|
2431
3800
|
});
|
|
2432
|
-
var testCmd2 =
|
|
3801
|
+
var testCmd2 = defineCommand15({
|
|
2433
3802
|
meta: { name: "test", description: "Send a single chat to a model and print the reply" },
|
|
2434
3803
|
args: {
|
|
2435
3804
|
model: {
|
|
@@ -2450,7 +3819,7 @@ var testCmd2 = defineCommand14({
|
|
|
2450
3819
|
}
|
|
2451
3820
|
}
|
|
2452
3821
|
});
|
|
2453
|
-
var providerCommand =
|
|
3822
|
+
var providerCommand = defineCommand15({
|
|
2454
3823
|
meta: { name: "provider", description: "Manage and test LLM providers" },
|
|
2455
3824
|
subCommands: {
|
|
2456
3825
|
list: listCmd7,
|
|
@@ -2463,8 +3832,8 @@ var providerCommand = defineCommand14({
|
|
|
2463
3832
|
});
|
|
2464
3833
|
|
|
2465
3834
|
// src/commands/send.ts
|
|
2466
|
-
import { defineCommand as
|
|
2467
|
-
var sendCommand =
|
|
3835
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
3836
|
+
var sendCommand = defineCommand16({
|
|
2468
3837
|
meta: {
|
|
2469
3838
|
name: "send",
|
|
2470
3839
|
description: "Send a message from one agent to another"
|
|
@@ -2495,14 +3864,14 @@ var sendCommand = defineCommand15({
|
|
|
2495
3864
|
});
|
|
2496
3865
|
|
|
2497
3866
|
// src/commands/serve.ts
|
|
2498
|
-
import { spawn as
|
|
3867
|
+
import { spawn as spawn3 } from "child_process";
|
|
2499
3868
|
import { existsSync as existsSync5 } from "fs";
|
|
2500
|
-
import { join as
|
|
2501
|
-
import { defineCommand as
|
|
2502
|
-
var bundledDaemonEntry2 =
|
|
2503
|
-
var sourceDaemonEntry2 =
|
|
3869
|
+
import { join as join5 } from "path";
|
|
3870
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
3871
|
+
var bundledDaemonEntry2 = join5(import.meta.dirname, "daemon.js");
|
|
3872
|
+
var sourceDaemonEntry2 = join5(import.meta.dirname, "..", "..", "..", "daemon", "src", "index.ts");
|
|
2504
3873
|
var daemonEntry2 = existsSync5(bundledDaemonEntry2) ? bundledDaemonEntry2 : sourceDaemonEntry2;
|
|
2505
|
-
var serveCommand =
|
|
3874
|
+
var serveCommand = defineCommand17({
|
|
2506
3875
|
meta: {
|
|
2507
3876
|
name: "serve",
|
|
2508
3877
|
description: "Start the bazilion daemon (HTTP API)"
|
|
@@ -2535,7 +3904,7 @@ var serveCommand = defineCommand16({
|
|
|
2535
3904
|
console.log(`starting bazilion daemon at http://${host}:${port}`);
|
|
2536
3905
|
console.log("(web UI runs separately: cd apps/web && pnpm dev)");
|
|
2537
3906
|
const nodeArgs2 = daemonEntry2.endsWith(".ts") ? ["--import", "tsx/esm", daemonEntry2] : [daemonEntry2];
|
|
2538
|
-
const proc =
|
|
3907
|
+
const proc = spawn3("node", nodeArgs2, {
|
|
2539
3908
|
env,
|
|
2540
3909
|
stdio: "inherit"
|
|
2541
3910
|
});
|
|
@@ -2551,17 +3920,17 @@ var serveCommand = defineCommand16({
|
|
|
2551
3920
|
signalled = true;
|
|
2552
3921
|
});
|
|
2553
3922
|
process.on("SIGTERM", onSigterm);
|
|
2554
|
-
await new Promise((
|
|
3923
|
+
await new Promise((resolve4, reject) => {
|
|
2555
3924
|
proc.on("error", (err) => reject(err));
|
|
2556
3925
|
proc.on("close", (code) => {
|
|
2557
3926
|
if (signalled) {
|
|
2558
3927
|
process.exitCode = 0;
|
|
2559
|
-
|
|
3928
|
+
resolve4();
|
|
2560
3929
|
} else if (code === 0 || code === null) {
|
|
2561
|
-
|
|
3930
|
+
resolve4();
|
|
2562
3931
|
} else {
|
|
2563
3932
|
process.exitCode = code;
|
|
2564
|
-
|
|
3933
|
+
resolve4();
|
|
2565
3934
|
}
|
|
2566
3935
|
});
|
|
2567
3936
|
});
|
|
@@ -2569,16 +3938,16 @@ var serveCommand = defineCommand16({
|
|
|
2569
3938
|
});
|
|
2570
3939
|
|
|
2571
3940
|
// src/commands/skill.ts
|
|
2572
|
-
import { existsSync as existsSync6, readFileSync as
|
|
2573
|
-
import { basename as
|
|
2574
|
-
import { defineCommand as
|
|
3941
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, statSync } from "fs";
|
|
3942
|
+
import { basename as basename4, resolve as resolve3 } from "path";
|
|
3943
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
2575
3944
|
function printFindings(findings, prefix = " ") {
|
|
2576
3945
|
for (const f of findings) {
|
|
2577
3946
|
const line = f.line ? ` line ${f.line}` : "";
|
|
2578
3947
|
console.log(`${prefix}${f.severity}: ${f.code}${line} - ${f.message}`);
|
|
2579
3948
|
}
|
|
2580
3949
|
}
|
|
2581
|
-
var listCmd8 =
|
|
3950
|
+
var listCmd8 = defineCommand18({
|
|
2582
3951
|
meta: { name: "list", description: "List installed skills (or those attached to an agent)" },
|
|
2583
3952
|
args: {
|
|
2584
3953
|
agent: { type: "string", description: "Filter to skills attached to this agent" }
|
|
@@ -2619,7 +3988,7 @@ var listCmd8 = defineCommand17({
|
|
|
2619
3988
|
}
|
|
2620
3989
|
}
|
|
2621
3990
|
});
|
|
2622
|
-
var importCmd =
|
|
3991
|
+
var importCmd = defineCommand18({
|
|
2623
3992
|
meta: {
|
|
2624
3993
|
name: "import",
|
|
2625
3994
|
description: "Import skills from openclaw, a directory, or a local .zip archive"
|
|
@@ -2634,13 +4003,13 @@ var importCmd = defineCommand17({
|
|
|
2634
4003
|
},
|
|
2635
4004
|
async run({ args }) {
|
|
2636
4005
|
const client = createClient2();
|
|
2637
|
-
const absFrom =
|
|
4006
|
+
const absFrom = resolve3(args.from);
|
|
2638
4007
|
const isLocalZip = args.from.toLowerCase().endsWith(".zip") && existsSync6(absFrom) && statSync(absFrom).isFile();
|
|
2639
4008
|
let result;
|
|
2640
4009
|
try {
|
|
2641
4010
|
if (isLocalZip) {
|
|
2642
|
-
const bytes =
|
|
2643
|
-
const file = new File([bytes],
|
|
4011
|
+
const bytes = readFileSync7(absFrom);
|
|
4012
|
+
const file = new File([bytes], basename4(absFrom), { type: "application/zip" });
|
|
2644
4013
|
const fd = new FormData();
|
|
2645
4014
|
fd.set("file", file);
|
|
2646
4015
|
if (args.force) fd.set("force", "true");
|
|
@@ -2688,7 +4057,7 @@ var importCmd = defineCommand17({
|
|
|
2688
4057
|
}
|
|
2689
4058
|
}
|
|
2690
4059
|
});
|
|
2691
|
-
var rmCmd4 =
|
|
4060
|
+
var rmCmd4 = defineCommand18({
|
|
2692
4061
|
meta: { name: "rm", description: "Remove an installed skill" },
|
|
2693
4062
|
args: {
|
|
2694
4063
|
name: { type: "positional", required: true }
|
|
@@ -2699,7 +4068,7 @@ var rmCmd4 = defineCommand17({
|
|
|
2699
4068
|
console.log(`removed skill ${args.name}`);
|
|
2700
4069
|
}
|
|
2701
4070
|
});
|
|
2702
|
-
var skillCommand =
|
|
4071
|
+
var skillCommand = defineCommand18({
|
|
2703
4072
|
meta: { name: "skill", description: "Manage the skill library" },
|
|
2704
4073
|
subCommands: {
|
|
2705
4074
|
list: listCmd8,
|
|
@@ -2709,13 +4078,13 @@ var skillCommand = defineCommand17({
|
|
|
2709
4078
|
});
|
|
2710
4079
|
|
|
2711
4080
|
// src/commands/team.ts
|
|
2712
|
-
import { readFileSync as
|
|
4081
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
2713
4082
|
import { stdin as stdin2 } from "process";
|
|
2714
|
-
import { defineCommand as
|
|
4083
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
2715
4084
|
|
|
2716
4085
|
// src/commands/team-policy.ts
|
|
2717
|
-
import { readFileSync as
|
|
2718
|
-
import { defineCommand as
|
|
4086
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
4087
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
2719
4088
|
|
|
2720
4089
|
// src/team-interchange.ts
|
|
2721
4090
|
var TEAM_POLICY_INTERCHANGE_VERSION = 1;
|
|
@@ -2931,7 +4300,7 @@ function edgeDiff(before, after) {
|
|
|
2931
4300
|
// src/commands/team-policy.ts
|
|
2932
4301
|
function readJson(path) {
|
|
2933
4302
|
try {
|
|
2934
|
-
return JSON.parse(
|
|
4303
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
2935
4304
|
} catch (error) {
|
|
2936
4305
|
throw new Error(`invalid JSON: ${error.message}`);
|
|
2937
4306
|
}
|
|
@@ -2963,7 +4332,7 @@ function printPolicy(detail) {
|
|
|
2963
4332
|
console.log(line);
|
|
2964
4333
|
}
|
|
2965
4334
|
}
|
|
2966
|
-
var show2 =
|
|
4335
|
+
var show2 = defineCommand19({
|
|
2967
4336
|
meta: { name: "show", description: "Show the one effective live policy for a Team" },
|
|
2968
4337
|
args: {
|
|
2969
4338
|
team: { type: "positional", required: true },
|
|
@@ -2977,7 +4346,7 @@ var show2 = defineCommand18({
|
|
|
2977
4346
|
else printPolicy(detail);
|
|
2978
4347
|
}
|
|
2979
4348
|
});
|
|
2980
|
-
var exportCommand =
|
|
4349
|
+
var exportCommand = defineCommand19({
|
|
2981
4350
|
meta: { name: "export", description: "Export a revisioned Team policy document" },
|
|
2982
4351
|
args: { team: { type: "positional", required: true } },
|
|
2983
4352
|
async run({ args }) {
|
|
@@ -3001,7 +4370,7 @@ function printEdgeDiff(current, edges) {
|
|
|
3001
4370
|
for (const value of diff2.added) console.log(` + ${value}`);
|
|
3002
4371
|
for (const value of diff2.removed) console.log(` - ${value}`);
|
|
3003
4372
|
}
|
|
3004
|
-
var importCommand =
|
|
4373
|
+
var importCommand = defineCommand19({
|
|
3005
4374
|
meta: {
|
|
3006
4375
|
name: "import",
|
|
3007
4376
|
description: "Validate, diff, and explicitly replace a Team policy"
|
|
@@ -3056,7 +4425,7 @@ var importCommand = defineCommand18({
|
|
|
3056
4425
|
console.log(`updated Team ${args.team} policy to revision ${updated.teamPolicy.revision}`);
|
|
3057
4426
|
}
|
|
3058
4427
|
});
|
|
3059
|
-
var diff =
|
|
4428
|
+
var diff = defineCommand19({
|
|
3060
4429
|
meta: { name: "diff", description: "Compare live policy with its retained baseline source" },
|
|
3061
4430
|
args: {
|
|
3062
4431
|
team: { type: "positional", required: true },
|
|
@@ -3080,7 +4449,7 @@ var diff = defineCommand18({
|
|
|
3080
4449
|
);
|
|
3081
4450
|
}
|
|
3082
4451
|
});
|
|
3083
|
-
var evaluate =
|
|
4452
|
+
var evaluate = defineCommand19({
|
|
3084
4453
|
meta: {
|
|
3085
4454
|
name: "evaluate",
|
|
3086
4455
|
description: "Evaluate a communication path without sending or recording a block"
|
|
@@ -3111,7 +4480,7 @@ var evaluate = defineCommand18({
|
|
|
3111
4480
|
);
|
|
3112
4481
|
}
|
|
3113
4482
|
});
|
|
3114
|
-
var blocks =
|
|
4483
|
+
var blocks = defineCommand19({
|
|
3115
4484
|
meta: { name: "blocks", description: "List paginated durable communication denials" },
|
|
3116
4485
|
args: {
|
|
3117
4486
|
team: { type: "positional", required: true },
|
|
@@ -3160,7 +4529,7 @@ var blocks = defineCommand18({
|
|
|
3160
4529
|
if (page.nextCursor) console.log(`next cursor: ${page.nextCursor}`);
|
|
3161
4530
|
}
|
|
3162
4531
|
});
|
|
3163
|
-
var teamPolicyCommand =
|
|
4532
|
+
var teamPolicyCommand = defineCommand19({
|
|
3164
4533
|
meta: { name: "policy", description: "Inspect and manage the Team-owned live policy" },
|
|
3165
4534
|
subCommands: {
|
|
3166
4535
|
show: show2,
|
|
@@ -3173,7 +4542,7 @@ var teamPolicyCommand = defineCommand18({
|
|
|
3173
4542
|
});
|
|
3174
4543
|
|
|
3175
4544
|
// src/commands/team.ts
|
|
3176
|
-
var addCmd2 =
|
|
4545
|
+
var addCmd2 = defineCommand20({
|
|
3177
4546
|
meta: {
|
|
3178
4547
|
name: "add",
|
|
3179
4548
|
description: "Register a team at ~/.bazilion/teams/<slug>/ (use --link to point at an existing tree)"
|
|
@@ -3197,20 +4566,20 @@ var addCmd2 = defineCommand19({
|
|
|
3197
4566
|
console.log(`registered team ${g.id} at ${g.path}`);
|
|
3198
4567
|
}
|
|
3199
4568
|
});
|
|
3200
|
-
var listCmd9 =
|
|
4569
|
+
var listCmd9 = defineCommand20({
|
|
3201
4570
|
meta: { name: "list", description: "List teams" },
|
|
3202
4571
|
async run() {
|
|
3203
4572
|
const client = createClient2();
|
|
3204
|
-
const
|
|
3205
|
-
if (
|
|
4573
|
+
const list5 = await client.get("/api/teams");
|
|
4574
|
+
if (list5.length === 0) {
|
|
3206
4575
|
console.log("(no teams)");
|
|
3207
4576
|
return;
|
|
3208
4577
|
}
|
|
3209
|
-
const rows =
|
|
4578
|
+
const rows = list5.map((g) => [g.id, g.path]);
|
|
3210
4579
|
for (const line of columnize(rows)) console.log(line);
|
|
3211
4580
|
}
|
|
3212
4581
|
});
|
|
3213
|
-
var rmCmd5 =
|
|
4582
|
+
var rmCmd5 = defineCommand20({
|
|
3214
4583
|
meta: { name: "rm", description: "Remove a team registration" },
|
|
3215
4584
|
args: {
|
|
3216
4585
|
id: { type: "positional", required: true }
|
|
@@ -3231,7 +4600,7 @@ async function readStdin() {
|
|
|
3231
4600
|
for await (const chunk of stdin2) chunks.push(chunk);
|
|
3232
4601
|
return Buffer.concat(chunks).toString("utf8");
|
|
3233
4602
|
}
|
|
3234
|
-
var userMdShowCmd =
|
|
4603
|
+
var userMdShowCmd = defineCommand20({
|
|
3235
4604
|
meta: { name: "show", description: "Print the team's USER.md" },
|
|
3236
4605
|
args: { id: { type: "positional", required: true } },
|
|
3237
4606
|
async run({ args }) {
|
|
@@ -3241,7 +4610,7 @@ var userMdShowCmd = defineCommand19({
|
|
|
3241
4610
|
if (g.userMd && !g.userMd.endsWith("\n")) process.stdout.write("\n");
|
|
3242
4611
|
}
|
|
3243
4612
|
});
|
|
3244
|
-
var userMdSetCmd =
|
|
4613
|
+
var userMdSetCmd = defineCommand20({
|
|
3245
4614
|
meta: {
|
|
3246
4615
|
name: "set",
|
|
3247
4616
|
description: "Replace the team's USER.md (from --file, --from-stdin, or inline text)"
|
|
@@ -3255,7 +4624,7 @@ var userMdSetCmd = defineCommand19({
|
|
|
3255
4624
|
async run({ args }) {
|
|
3256
4625
|
let content;
|
|
3257
4626
|
if (args["from-stdin"]) content = await readStdin();
|
|
3258
|
-
else if (args.file) content =
|
|
4627
|
+
else if (args.file) content = readFileSync9(args.file, "utf8");
|
|
3259
4628
|
else if (args.text !== void 0) content = args.text;
|
|
3260
4629
|
else {
|
|
3261
4630
|
console.error("team user-md set: provide --file, --from-stdin, or --text");
|
|
@@ -3267,7 +4636,7 @@ var userMdSetCmd = defineCommand19({
|
|
|
3267
4636
|
console.log(`updated USER.md for team ${args.id} (${content.length} bytes)`);
|
|
3268
4637
|
}
|
|
3269
4638
|
});
|
|
3270
|
-
var userMdClearCmd =
|
|
4639
|
+
var userMdClearCmd = defineCommand20({
|
|
3271
4640
|
meta: { name: "clear", description: "Clear the team's USER.md to empty" },
|
|
3272
4641
|
args: { id: { type: "positional", required: true } },
|
|
3273
4642
|
async run({ args }) {
|
|
@@ -3277,7 +4646,7 @@ var userMdClearCmd = defineCommand19({
|
|
|
3277
4646
|
console.log(`cleared USER.md for team ${args.id}`);
|
|
3278
4647
|
}
|
|
3279
4648
|
});
|
|
3280
|
-
var userMdCmd =
|
|
4649
|
+
var userMdCmd = defineCommand20({
|
|
3281
4650
|
meta: { name: "user-md", description: "View or edit a team's USER.md" },
|
|
3282
4651
|
subCommands: {
|
|
3283
4652
|
show: userMdShowCmd,
|
|
@@ -3285,7 +4654,7 @@ var userMdCmd = defineCommand19({
|
|
|
3285
4654
|
clear: userMdClearCmd
|
|
3286
4655
|
}
|
|
3287
4656
|
});
|
|
3288
|
-
var topicFormatShowCmd =
|
|
4657
|
+
var topicFormatShowCmd = defineCommand20({
|
|
3289
4658
|
meta: { name: "show", description: "Print the team's Telegram topic-name template" },
|
|
3290
4659
|
args: { id: { type: "positional", required: true } },
|
|
3291
4660
|
async run({ args }) {
|
|
@@ -3294,7 +4663,7 @@ var topicFormatShowCmd = defineCommand19({
|
|
|
3294
4663
|
console.log(g.telegramTopicNameFormat ?? "(default naming)");
|
|
3295
4664
|
}
|
|
3296
4665
|
});
|
|
3297
|
-
var topicFormatSetCmd =
|
|
4666
|
+
var topicFormatSetCmd = defineCommand20({
|
|
3298
4667
|
meta: {
|
|
3299
4668
|
name: "set",
|
|
3300
4669
|
description: "Set the topic-name template. Tokens: {agent.name} {team.name} {team.slug} (must include {agent.name})"
|
|
@@ -3314,7 +4683,7 @@ var topicFormatSetCmd = defineCommand19({
|
|
|
3314
4683
|
console.log(`set topic-name template for team ${args.id}: ${g.telegramTopicNameFormat}`);
|
|
3315
4684
|
}
|
|
3316
4685
|
});
|
|
3317
|
-
var topicFormatClearCmd =
|
|
4686
|
+
var topicFormatClearCmd = defineCommand20({
|
|
3318
4687
|
meta: { name: "clear", description: "Clear the template (revert to built-in naming)" },
|
|
3319
4688
|
args: { id: { type: "positional", required: true } },
|
|
3320
4689
|
async run({ args }) {
|
|
@@ -3324,7 +4693,7 @@ var topicFormatClearCmd = defineCommand19({
|
|
|
3324
4693
|
console.log(`cleared topic-name template for team ${args.id}`);
|
|
3325
4694
|
}
|
|
3326
4695
|
});
|
|
3327
|
-
var topicFormatCmd =
|
|
4696
|
+
var topicFormatCmd = defineCommand20({
|
|
3328
4697
|
meta: {
|
|
3329
4698
|
name: "topic-format",
|
|
3330
4699
|
description: "View or edit a team's Telegram forum-topic name template"
|
|
@@ -3335,7 +4704,7 @@ var topicFormatCmd = defineCommand19({
|
|
|
3335
4704
|
clear: topicFormatClearCmd
|
|
3336
4705
|
}
|
|
3337
4706
|
});
|
|
3338
|
-
var teamCommand =
|
|
4707
|
+
var teamCommand = defineCommand20({
|
|
3339
4708
|
meta: { name: "team", description: "Manage teams (collaboration contexts)" },
|
|
3340
4709
|
subCommands: {
|
|
3341
4710
|
add: addCmd2,
|
|
@@ -3348,11 +4717,11 @@ var teamCommand = defineCommand19({
|
|
|
3348
4717
|
});
|
|
3349
4718
|
|
|
3350
4719
|
// src/commands/team-template.ts
|
|
3351
|
-
import { readFileSync as
|
|
3352
|
-
import { defineCommand as
|
|
4720
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4721
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
3353
4722
|
function jsonFile(path) {
|
|
3354
4723
|
try {
|
|
3355
|
-
return JSON.parse(
|
|
4724
|
+
return JSON.parse(readFileSync10(path, "utf8"));
|
|
3356
4725
|
} catch (error) {
|
|
3357
4726
|
throw new Error(`invalid JSON: ${error.message}`);
|
|
3358
4727
|
}
|
|
@@ -3377,7 +4746,7 @@ function printTeam(detail) {
|
|
|
3377
4746
|
console.log(line);
|
|
3378
4747
|
}
|
|
3379
4748
|
}
|
|
3380
|
-
var
|
|
4749
|
+
var list4 = defineCommand21({
|
|
3381
4750
|
meta: { name: "list", description: "List canonical Team templates" },
|
|
3382
4751
|
args: { json: { type: "boolean", description: "Emit stable JSON" } },
|
|
3383
4752
|
async run({ args }) {
|
|
@@ -3391,7 +4760,7 @@ var list2 = defineCommand20({
|
|
|
3391
4760
|
console.log(line);
|
|
3392
4761
|
}
|
|
3393
4762
|
});
|
|
3394
|
-
var show3 =
|
|
4763
|
+
var show3 = defineCommand21({
|
|
3395
4764
|
meta: { name: "show", description: "Show a canonical Team template" },
|
|
3396
4765
|
args: {
|
|
3397
4766
|
id: { type: "positional", required: true },
|
|
@@ -3405,7 +4774,7 @@ var show3 = defineCommand20({
|
|
|
3405
4774
|
else printTeam(detail);
|
|
3406
4775
|
}
|
|
3407
4776
|
});
|
|
3408
|
-
var exportCommand2 =
|
|
4777
|
+
var exportCommand2 = defineCommand21({
|
|
3409
4778
|
meta: {
|
|
3410
4779
|
name: "export",
|
|
3411
4780
|
description: "Export a portable versioned Team-template document to stdout"
|
|
@@ -3445,7 +4814,7 @@ function printImportDiff(current, document) {
|
|
|
3445
4814
|
for (const value of diff2.added) console.log(` + ${value}`);
|
|
3446
4815
|
for (const value of diff2.removed) console.log(` - ${value}`);
|
|
3447
4816
|
}
|
|
3448
|
-
var importCommand2 =
|
|
4817
|
+
var importCommand2 = defineCommand21({
|
|
3449
4818
|
meta: {
|
|
3450
4819
|
name: "import",
|
|
3451
4820
|
description: "Validate, diff, and explicitly apply a portable Team-template document"
|
|
@@ -3526,17 +4895,17 @@ var importCommand2 = defineCommand20({
|
|
|
3526
4895
|
);
|
|
3527
4896
|
}
|
|
3528
4897
|
});
|
|
3529
|
-
var teamTemplateCommand =
|
|
4898
|
+
var teamTemplateCommand = defineCommand21({
|
|
3530
4899
|
meta: {
|
|
3531
4900
|
name: "team-template",
|
|
3532
4901
|
description: "Inspect and exchange canonical Team templates"
|
|
3533
4902
|
},
|
|
3534
|
-
subCommands: { list:
|
|
4903
|
+
subCommands: { list: list4, show: show3, export: exportCommand2, import: importCommand2 }
|
|
3535
4904
|
});
|
|
3536
4905
|
|
|
3537
4906
|
// src/commands/telegram.ts
|
|
3538
|
-
import { defineCommand as
|
|
3539
|
-
var setCmd2 =
|
|
4907
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
4908
|
+
var setCmd2 = defineCommand22({
|
|
3540
4909
|
meta: {
|
|
3541
4910
|
name: "set",
|
|
3542
4911
|
description: "Save bot token + supergroup chat ID (paired write)"
|
|
@@ -3554,7 +4923,7 @@ var setCmd2 = defineCommand21({
|
|
|
3554
4923
|
console.log(`saved \xB7 token ${state.botTokenPreview} \xB7 chat ${state.chatId}`);
|
|
3555
4924
|
}
|
|
3556
4925
|
});
|
|
3557
|
-
var clearCmd =
|
|
4926
|
+
var clearCmd = defineCommand22({
|
|
3558
4927
|
meta: {
|
|
3559
4928
|
name: "clear",
|
|
3560
4929
|
description: "Remove stored bot token + chat ID"
|
|
@@ -3565,7 +4934,7 @@ var clearCmd = defineCommand21({
|
|
|
3565
4934
|
console.log("cleared");
|
|
3566
4935
|
}
|
|
3567
4936
|
});
|
|
3568
|
-
var showCmd5 =
|
|
4937
|
+
var showCmd5 = defineCommand22({
|
|
3569
4938
|
meta: {
|
|
3570
4939
|
name: "show",
|
|
3571
4940
|
description: "Show what credentials are stored (token is masked)"
|
|
@@ -3586,7 +4955,7 @@ var showCmd5 = defineCommand21({
|
|
|
3586
4955
|
}
|
|
3587
4956
|
}
|
|
3588
4957
|
});
|
|
3589
|
-
var reconnectCmd =
|
|
4958
|
+
var reconnectCmd = defineCommand22({
|
|
3590
4959
|
meta: {
|
|
3591
4960
|
name: "reconnect",
|
|
3592
4961
|
description: "Apply a pending supergroup chat-id migration + re-activate the bot"
|
|
@@ -3597,7 +4966,7 @@ var reconnectCmd = defineCommand21({
|
|
|
3597
4966
|
console.log(`reconnected \xB7 chat ${state.chatId}`);
|
|
3598
4967
|
}
|
|
3599
4968
|
});
|
|
3600
|
-
var configCmd =
|
|
4969
|
+
var configCmd = defineCommand22({
|
|
3601
4970
|
meta: {
|
|
3602
4971
|
name: "config",
|
|
3603
4972
|
description: "Manage Telegram credentials (bot token + chat ID)"
|
|
@@ -3608,7 +4977,7 @@ var configCmd = defineCommand21({
|
|
|
3608
4977
|
show: showCmd5
|
|
3609
4978
|
}
|
|
3610
4979
|
});
|
|
3611
|
-
var healthCmd =
|
|
4980
|
+
var healthCmd = defineCommand22({
|
|
3612
4981
|
meta: {
|
|
3613
4982
|
name: "health",
|
|
3614
4983
|
description: "Run the four-step preflight against the Telegram Bot API"
|
|
@@ -3642,7 +5011,7 @@ var healthCmd = defineCommand21({
|
|
|
3642
5011
|
if (!allOk) process.exitCode = 1;
|
|
3643
5012
|
}
|
|
3644
5013
|
});
|
|
3645
|
-
var botStatusCmd =
|
|
5014
|
+
var botStatusCmd = defineCommand22({
|
|
3646
5015
|
meta: {
|
|
3647
5016
|
name: "status",
|
|
3648
5017
|
description: "Show polling state of the live bot"
|
|
@@ -3667,7 +5036,7 @@ var botStatusCmd = defineCommand21({
|
|
|
3667
5036
|
if (!p.running) process.exitCode = 1;
|
|
3668
5037
|
}
|
|
3669
5038
|
});
|
|
3670
|
-
var botRestartCmd =
|
|
5039
|
+
var botRestartCmd = defineCommand22({
|
|
3671
5040
|
meta: {
|
|
3672
5041
|
name: "restart",
|
|
3673
5042
|
description: "Force-restart the bot (tears down + brings up from current creds)"
|
|
@@ -3678,7 +5047,7 @@ var botRestartCmd = defineCommand21({
|
|
|
3678
5047
|
console.log("restart requested");
|
|
3679
5048
|
}
|
|
3680
5049
|
});
|
|
3681
|
-
var botCmd =
|
|
5050
|
+
var botCmd = defineCommand22({
|
|
3682
5051
|
meta: {
|
|
3683
5052
|
name: "bot",
|
|
3684
5053
|
description: "Inspect / control the live polling bot"
|
|
@@ -3688,7 +5057,7 @@ var botCmd = defineCommand21({
|
|
|
3688
5057
|
restart: botRestartCmd
|
|
3689
5058
|
}
|
|
3690
5059
|
});
|
|
3691
|
-
var bindCmd =
|
|
5060
|
+
var bindCmd = defineCommand22({
|
|
3692
5061
|
meta: {
|
|
3693
5062
|
name: "bind",
|
|
3694
5063
|
description: "Create a Telegram topic for an agent (manual fallback for /talk)"
|
|
@@ -3706,7 +5075,7 @@ var bindCmd = defineCommand21({
|
|
|
3706
5075
|
console.log(` ${result.deepLink}`);
|
|
3707
5076
|
}
|
|
3708
5077
|
});
|
|
3709
|
-
var unbindCmd =
|
|
5078
|
+
var unbindCmd = defineCommand22({
|
|
3710
5079
|
meta: {
|
|
3711
5080
|
name: "unbind",
|
|
3712
5081
|
description: "Clear an agent's Telegram topic binding (topic stays in Telegram as orphan)"
|
|
@@ -3720,7 +5089,7 @@ var unbindCmd = defineCommand21({
|
|
|
3720
5089
|
console.log(`unbound agent ${args.agent}`);
|
|
3721
5090
|
}
|
|
3722
5091
|
});
|
|
3723
|
-
var listBindingsCmd =
|
|
5092
|
+
var listBindingsCmd = defineCommand22({
|
|
3724
5093
|
meta: {
|
|
3725
5094
|
name: "list",
|
|
3726
5095
|
description: "List agents and their Telegram topic bindings"
|
|
@@ -3748,7 +5117,7 @@ var listBindingsCmd = defineCommand21({
|
|
|
3748
5117
|
}
|
|
3749
5118
|
}
|
|
3750
5119
|
});
|
|
3751
|
-
var allowCmd =
|
|
5120
|
+
var allowCmd = defineCommand22({
|
|
3752
5121
|
meta: { name: "allow", description: "Add a Telegram user id to the allowlist" },
|
|
3753
5122
|
args: {
|
|
3754
5123
|
userId: { type: "positional", required: true, description: "Numeric Telegram user id" },
|
|
@@ -3765,7 +5134,7 @@ var allowCmd = defineCommand21({
|
|
|
3765
5134
|
console.log(`allowed ${u.userId} (${u.role})`);
|
|
3766
5135
|
}
|
|
3767
5136
|
});
|
|
3768
|
-
var denyCmd =
|
|
5137
|
+
var denyCmd = defineCommand22({
|
|
3769
5138
|
meta: { name: "deny", description: "Remove a Telegram user id from the allowlist" },
|
|
3770
5139
|
args: { userId: { type: "positional", required: true } },
|
|
3771
5140
|
async run({ args }) {
|
|
@@ -3774,7 +5143,7 @@ var denyCmd = defineCommand21({
|
|
|
3774
5143
|
console.log(`removed ${args.userId}`);
|
|
3775
5144
|
}
|
|
3776
5145
|
});
|
|
3777
|
-
var allowedCmd =
|
|
5146
|
+
var allowedCmd = defineCommand22({
|
|
3778
5147
|
meta: { name: "allowed", description: "List allowlisted Telegram users" },
|
|
3779
5148
|
async run() {
|
|
3780
5149
|
const client = createClient2();
|
|
@@ -3789,7 +5158,7 @@ var allowedCmd = defineCommand21({
|
|
|
3789
5158
|
}
|
|
3790
5159
|
}
|
|
3791
5160
|
});
|
|
3792
|
-
var telegramCommand =
|
|
5161
|
+
var telegramCommand = defineCommand22({
|
|
3793
5162
|
meta: {
|
|
3794
5163
|
name: "telegram",
|
|
3795
5164
|
description: "Telegram integration: credentials, health, bot lifecycle, bindings, access"
|
|
@@ -3810,7 +5179,7 @@ var telegramCommand = defineCommand21({
|
|
|
3810
5179
|
|
|
3811
5180
|
// src/commands/token.ts
|
|
3812
5181
|
import { networkInterfaces } from "os";
|
|
3813
|
-
import { defineCommand as
|
|
5182
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
3814
5183
|
import qrcode from "qrcode-terminal";
|
|
3815
5184
|
function tokenRow(t) {
|
|
3816
5185
|
const last = t.lastUsedAt ? new Date(t.lastUsedAt).toISOString() : "(never)";
|
|
@@ -3844,7 +5213,7 @@ function resolveQrServer(override) {
|
|
|
3844
5213
|
if (detected.warning) console.warn(`\u26A0 ${detected.warning}`);
|
|
3845
5214
|
return detected.origin;
|
|
3846
5215
|
}
|
|
3847
|
-
var createCmd3 =
|
|
5216
|
+
var createCmd3 = defineCommand23({
|
|
3848
5217
|
meta: { name: "create", description: "Mint a new web token (shown once)" },
|
|
3849
5218
|
args: {
|
|
3850
5219
|
label: { type: "positional", required: true, description: "Human-readable label" },
|
|
@@ -3875,7 +5244,7 @@ var createCmd3 = defineCommand22({
|
|
|
3875
5244
|
qrcode.generate(pairUrl, { small: true }, (qr) => console.log(qr));
|
|
3876
5245
|
}
|
|
3877
5246
|
});
|
|
3878
|
-
var listCmd10 =
|
|
5247
|
+
var listCmd10 = defineCommand23({
|
|
3879
5248
|
meta: { name: "list", description: "List web tokens" },
|
|
3880
5249
|
args: {
|
|
3881
5250
|
all: { type: "boolean", description: "Include revoked tokens" }
|
|
@@ -3891,7 +5260,7 @@ var listCmd10 = defineCommand22({
|
|
|
3891
5260
|
for (const line of columnize(tokens.map(tokenRow))) console.log(line);
|
|
3892
5261
|
}
|
|
3893
5262
|
});
|
|
3894
|
-
var showLocalCmd =
|
|
5263
|
+
var showLocalCmd = defineCommand23({
|
|
3895
5264
|
meta: {
|
|
3896
5265
|
name: "show-local",
|
|
3897
5266
|
description: "Print the bootstrap web token stored in ~/.bazilion/auth.json"
|
|
@@ -3901,7 +5270,7 @@ var showLocalCmd = defineCommand22({
|
|
|
3901
5270
|
console.log(readAuthFile(paths.authFile).token);
|
|
3902
5271
|
}
|
|
3903
5272
|
});
|
|
3904
|
-
var revokeCmd =
|
|
5273
|
+
var revokeCmd = defineCommand23({
|
|
3905
5274
|
meta: { name: "revoke", description: "Revoke a web token" },
|
|
3906
5275
|
args: {
|
|
3907
5276
|
id: { type: "positional", required: true }
|
|
@@ -3912,7 +5281,7 @@ var revokeCmd = defineCommand22({
|
|
|
3912
5281
|
console.log(`revoked token ${args.id}`);
|
|
3913
5282
|
}
|
|
3914
5283
|
});
|
|
3915
|
-
var tokenCommand =
|
|
5284
|
+
var tokenCommand = defineCommand23({
|
|
3916
5285
|
meta: { name: "token", description: "Manage web tokens for API/CLI clients" },
|
|
3917
5286
|
subCommands: {
|
|
3918
5287
|
create: createCmd3,
|
|
@@ -3923,8 +5292,8 @@ var tokenCommand = defineCommand22({
|
|
|
3923
5292
|
});
|
|
3924
5293
|
|
|
3925
5294
|
// src/commands/trigger.ts
|
|
3926
|
-
import { defineCommand as
|
|
3927
|
-
var addCmd3 =
|
|
5295
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
5296
|
+
var addCmd3 = defineCommand24({
|
|
3928
5297
|
meta: { name: "add", description: "Add an interval / cron trigger to an agent" },
|
|
3929
5298
|
args: {
|
|
3930
5299
|
agent: { type: "positional", required: true },
|
|
@@ -3972,7 +5341,7 @@ function triggerRow(t) {
|
|
|
3972
5341
|
const msgPreview = t.message.length > 60 ? `${t.message.slice(0, 60)}\u2026` : t.message;
|
|
3973
5342
|
return [t.id, state, spec, `last: ${last}`, `"${msgPreview}"`];
|
|
3974
5343
|
}
|
|
3975
|
-
var listCmd11 =
|
|
5344
|
+
var listCmd11 = defineCommand24({
|
|
3976
5345
|
meta: { name: "list", description: "List triggers for an agent" },
|
|
3977
5346
|
args: {
|
|
3978
5347
|
agent: { type: "positional", required: true }
|
|
@@ -3989,7 +5358,7 @@ var listCmd11 = defineCommand23({
|
|
|
3989
5358
|
for (const line of columnize(triggers.map(triggerRow))) console.log(line);
|
|
3990
5359
|
}
|
|
3991
5360
|
});
|
|
3992
|
-
var rmCmd6 =
|
|
5361
|
+
var rmCmd6 = defineCommand24({
|
|
3993
5362
|
meta: { name: "rm", description: "Delete a trigger" },
|
|
3994
5363
|
args: {
|
|
3995
5364
|
id: { type: "positional", required: true }
|
|
@@ -4000,7 +5369,7 @@ var rmCmd6 = defineCommand23({
|
|
|
4000
5369
|
console.log(`removed trigger ${args.id}`);
|
|
4001
5370
|
}
|
|
4002
5371
|
});
|
|
4003
|
-
var enableCmd3 =
|
|
5372
|
+
var enableCmd3 = defineCommand24({
|
|
4004
5373
|
meta: { name: "enable", description: "Enable a trigger" },
|
|
4005
5374
|
args: {
|
|
4006
5375
|
id: { type: "positional", required: true }
|
|
@@ -4012,7 +5381,7 @@ var enableCmd3 = defineCommand23({
|
|
|
4012
5381
|
console.log(`enabled trigger ${args.id}`);
|
|
4013
5382
|
}
|
|
4014
5383
|
});
|
|
4015
|
-
var disableCmd3 =
|
|
5384
|
+
var disableCmd3 = defineCommand24({
|
|
4016
5385
|
meta: { name: "disable", description: "Disable a trigger" },
|
|
4017
5386
|
args: {
|
|
4018
5387
|
id: { type: "positional", required: true }
|
|
@@ -4024,21 +5393,50 @@ var disableCmd3 = defineCommand23({
|
|
|
4024
5393
|
console.log(`disabled trigger ${args.id}`);
|
|
4025
5394
|
}
|
|
4026
5395
|
});
|
|
4027
|
-
var
|
|
5396
|
+
var historyCmd = defineCommand24({
|
|
5397
|
+
meta: { name: "history", description: "Show recent dispatches for a trigger" },
|
|
5398
|
+
args: {
|
|
5399
|
+
id: { type: "positional", required: true },
|
|
5400
|
+
limit: { type: "string", description: "Maximum rows (default 20)" }
|
|
5401
|
+
},
|
|
5402
|
+
async run({ args }) {
|
|
5403
|
+
const limit = args.limit ? Number(args.limit) : 20;
|
|
5404
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
5405
|
+
throw new Error("--limit must be an integer from 1 to 100");
|
|
5406
|
+
}
|
|
5407
|
+
const client = createClient2();
|
|
5408
|
+
const { dispatches } = await client.get(
|
|
5409
|
+
`/api/triggers/${args.id}/dispatches?limit=${limit}`
|
|
5410
|
+
);
|
|
5411
|
+
if (dispatches.length === 0) {
|
|
5412
|
+
console.log("(no dispatches)");
|
|
5413
|
+
return;
|
|
5414
|
+
}
|
|
5415
|
+
const rows = dispatches.map((dispatch) => [
|
|
5416
|
+
new Date(dispatch.scheduledAt).toISOString(),
|
|
5417
|
+
dispatch.status,
|
|
5418
|
+
`attempts: ${dispatch.attemptCount}`,
|
|
5419
|
+
dispatch.lastError ?? ""
|
|
5420
|
+
]);
|
|
5421
|
+
for (const line of columnize(rows)) console.log(line);
|
|
5422
|
+
}
|
|
5423
|
+
});
|
|
5424
|
+
var triggerCommand = defineCommand24({
|
|
4028
5425
|
meta: { name: "trigger", description: "Manage scheduled agent triggers" },
|
|
4029
5426
|
subCommands: {
|
|
4030
5427
|
add: addCmd3,
|
|
4031
5428
|
list: listCmd11,
|
|
4032
5429
|
rm: rmCmd6,
|
|
4033
5430
|
enable: enableCmd3,
|
|
4034
|
-
disable: disableCmd3
|
|
5431
|
+
disable: disableCmd3,
|
|
5432
|
+
history: historyCmd
|
|
4035
5433
|
}
|
|
4036
5434
|
});
|
|
4037
5435
|
|
|
4038
5436
|
// src/commands/uninstall.ts
|
|
4039
|
-
import { existsSync as existsSync7, readdirSync as readdirSync2, rmSync as
|
|
4040
|
-
import { join as
|
|
4041
|
-
import { defineCommand as
|
|
5437
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, rmSync as rmSync4 } from "fs";
|
|
5438
|
+
import { join as join6 } from "path";
|
|
5439
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
4042
5440
|
function makeLineReader() {
|
|
4043
5441
|
let buffer = "";
|
|
4044
5442
|
let ended = false;
|
|
@@ -4048,8 +5446,8 @@ function makeLineReader() {
|
|
|
4048
5446
|
const nlIdx = buffer.indexOf("\n");
|
|
4049
5447
|
if (nlIdx === -1) {
|
|
4050
5448
|
if (ended) {
|
|
4051
|
-
const
|
|
4052
|
-
|
|
5449
|
+
const resolve4 = queue.shift();
|
|
5450
|
+
resolve4?.(buffer);
|
|
4053
5451
|
buffer = "";
|
|
4054
5452
|
} else {
|
|
4055
5453
|
return;
|
|
@@ -4057,8 +5455,8 @@ function makeLineReader() {
|
|
|
4057
5455
|
} else {
|
|
4058
5456
|
const line = buffer.slice(0, nlIdx);
|
|
4059
5457
|
buffer = buffer.slice(nlIdx + 1);
|
|
4060
|
-
const
|
|
4061
|
-
|
|
5458
|
+
const resolve4 = queue.shift();
|
|
5459
|
+
resolve4?.(line);
|
|
4062
5460
|
}
|
|
4063
5461
|
}
|
|
4064
5462
|
};
|
|
@@ -4071,8 +5469,8 @@ function makeLineReader() {
|
|
|
4071
5469
|
ended = true;
|
|
4072
5470
|
tryDeliver();
|
|
4073
5471
|
});
|
|
4074
|
-
return () => new Promise((
|
|
4075
|
-
queue.push(
|
|
5472
|
+
return () => new Promise((resolve4) => {
|
|
5473
|
+
queue.push(resolve4);
|
|
4076
5474
|
tryDeliver();
|
|
4077
5475
|
});
|
|
4078
5476
|
}
|
|
@@ -4083,10 +5481,10 @@ async function askYes(readLine, prompt) {
|
|
|
4083
5481
|
}
|
|
4084
5482
|
function removePath(p) {
|
|
4085
5483
|
if (!existsSync7(p)) return false;
|
|
4086
|
-
|
|
5484
|
+
rmSync4(p, { recursive: true, force: true });
|
|
4087
5485
|
return true;
|
|
4088
5486
|
}
|
|
4089
|
-
var uninstallCommand =
|
|
5487
|
+
var uninstallCommand = defineCommand25({
|
|
4090
5488
|
meta: {
|
|
4091
5489
|
name: "uninstall",
|
|
4092
5490
|
description: "Wipe bazilion state from ~/.bazilion (or BAZILION_HOME)"
|
|
@@ -4104,12 +5502,12 @@ var uninstallCommand = defineCommand24({
|
|
|
4104
5502
|
},
|
|
4105
5503
|
async run({ args }) {
|
|
4106
5504
|
const paths = resolveCliPaths(args.home);
|
|
4107
|
-
const dbFile =
|
|
4108
|
-
const profilesDir =
|
|
4109
|
-
const agentsDir =
|
|
4110
|
-
const teamsDir =
|
|
4111
|
-
const skillsDir =
|
|
4112
|
-
const logsDir =
|
|
5505
|
+
const dbFile = join6(paths.home, "bazilion.db");
|
|
5506
|
+
const profilesDir = join6(paths.home, "profiles");
|
|
5507
|
+
const agentsDir = join6(paths.home, "agents");
|
|
5508
|
+
const teamsDir = join6(paths.home, "teams");
|
|
5509
|
+
const skillsDir = join6(paths.home, "skills");
|
|
5510
|
+
const logsDir = join6(paths.home, "logs");
|
|
4113
5511
|
if (!existsSync7(paths.home)) {
|
|
4114
5512
|
console.log(`nothing to remove: ${paths.home} does not exist`);
|
|
4115
5513
|
return;
|
|
@@ -4142,7 +5540,7 @@ var uninstallCommand = defineCommand24({
|
|
|
4142
5540
|
if (removePath(p)) console.log(`removed ${p}`);
|
|
4143
5541
|
}
|
|
4144
5542
|
if (readdirSync2(paths.home).length === 0) {
|
|
4145
|
-
|
|
5543
|
+
rmSync4(paths.home, { recursive: true, force: true });
|
|
4146
5544
|
console.log(`removed ${paths.home}`);
|
|
4147
5545
|
} else {
|
|
4148
5546
|
console.log(`${paths.home} still has unmanaged files; left in place`);
|
|
@@ -4157,7 +5555,7 @@ var uninstallCommand = defineCommand24({
|
|
|
4157
5555
|
|
|
4158
5556
|
// src/index.ts
|
|
4159
5557
|
var VERSION = package_default.version;
|
|
4160
|
-
var main =
|
|
5558
|
+
var main = defineCommand26({
|
|
4161
5559
|
meta: {
|
|
4162
5560
|
name: "bazilion",
|
|
4163
5561
|
version: VERSION,
|
|
@@ -4170,6 +5568,7 @@ var main = defineCommand25({
|
|
|
4170
5568
|
"team-template": teamTemplateCommand,
|
|
4171
5569
|
agent: agentCommand,
|
|
4172
5570
|
approval: approvalCommand,
|
|
5571
|
+
attention: attentionCommand,
|
|
4173
5572
|
skill: skillCommand,
|
|
4174
5573
|
memory: memoryCommand,
|
|
4175
5574
|
mcp: mcpCommand,
|
|
@@ -4265,6 +5664,7 @@ function printTopLevelHelp() {
|
|
|
4265
5664
|
title: "agents",
|
|
4266
5665
|
items: [
|
|
4267
5666
|
["approval", "Review and decide communication approval attempts"],
|
|
5667
|
+
["attention", "See everything that needs operator attention"],
|
|
4268
5668
|
["agent", "Spawn, list, chat, archive agents"],
|
|
4269
5669
|
["memory", "Read/write a team's shared memory"],
|
|
4270
5670
|
["send", "Send a message from one agent to another"],
|