squadrant 0.28.1 → 0.29.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/index.js +1590 -1278
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +934 -807
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/captain-ops/SKILL.md +9 -5
- package/plugin/skills/command-ops/SKILL.md +9 -7
- package/plugin/skills/daily-log/SKILL.md +4 -4
- package/plugin/skills/side-session/SKILL.md +1 -1
- package/plugin/skills/where-i-am/SKILL.md +3 -3
- package/scripts/read-handoff.sh +20 -5
- package/scripts/record-side-handoff.sh +5 -3
- package/scripts/spawn-workspace.sh +9 -5
- package/scripts/write-handoff.sh +6 -3
- package/templates/captain.claude.md +2 -2
- package/templates/captain.generic.md +1 -1
- package/templates/side.debug.claude.md +2 -2
- package/templates/side.research.claude.md +2 -2
- package/obsidian/spoke/meta/vault-changelog.md +0 -6
- package/obsidian/spoke/status.md +0 -19
- package/obsidian/spoke/templates/crew-session.md +0 -21
- package/obsidian/spoke/templates/daily-log.md +0 -16
- package/obsidian/spoke/wiki/index.md +0 -14
- package/obsidian/spoke/wiki/log.md +0 -10
- package/obsidian/spoke/wiki/pages/.gitkeep +0 -0
package/dist/squadrantd.js
CHANGED
|
@@ -417,6 +417,18 @@ var init_knowledge = __esm({
|
|
|
417
417
|
}
|
|
418
418
|
});
|
|
419
419
|
|
|
420
|
+
// packages/shared/dist/workspace.js
|
|
421
|
+
import fs2 from "fs";
|
|
422
|
+
import os3 from "os";
|
|
423
|
+
import path5 from "path";
|
|
424
|
+
var workspaceRoot, workspacePath;
|
|
425
|
+
var init_workspace = __esm({
|
|
426
|
+
"packages/shared/dist/workspace.js"() {
|
|
427
|
+
workspaceRoot = () => process.env.SQUADRANT_WORKSPACE_DIR || path5.join(os3.homedir(), "squadrant", "workspace");
|
|
428
|
+
workspacePath = (project) => path5.join(workspaceRoot(), project);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
420
432
|
// packages/shared/dist/router-model.js
|
|
421
433
|
function resolveRouterModel(model, agentName, router) {
|
|
422
434
|
if (!model)
|
|
@@ -484,22 +496,22 @@ function defaultCmuxConfigPath() {
|
|
|
484
496
|
return join(homedir(), ".config", "cmux", "cmux.json");
|
|
485
497
|
}
|
|
486
498
|
function ensureSocketAutomation(opts = {}) {
|
|
487
|
-
const
|
|
488
|
-
if (!existsSync(
|
|
489
|
-
mkdirSync(dirname(
|
|
490
|
-
writeFileSync(
|
|
491
|
-
return { path:
|
|
499
|
+
const path44 = opts.path ?? defaultCmuxConfigPath();
|
|
500
|
+
if (!existsSync(path44)) {
|
|
501
|
+
mkdirSync(dirname(path44), { recursive: true });
|
|
502
|
+
writeFileSync(path44, MINIMAL_TEMPLATE);
|
|
503
|
+
return { path: path44, changed: true, alreadySet: false };
|
|
492
504
|
}
|
|
493
|
-
const text = readFileSync(
|
|
505
|
+
const text = readFileSync(path44, "utf-8");
|
|
494
506
|
const current = parse(text)?.automation?.socketControlMode;
|
|
495
507
|
if (current === AUTOMATION_MODE) {
|
|
496
|
-
return { path:
|
|
508
|
+
return { path: path44, changed: false, alreadySet: true };
|
|
497
509
|
}
|
|
498
510
|
const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
|
|
499
511
|
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
500
512
|
});
|
|
501
|
-
writeFileSync(
|
|
502
|
-
return { path:
|
|
513
|
+
writeFileSync(path44, applyEdits(text, edits));
|
|
514
|
+
return { path: path44, changed: true, alreadySet: false };
|
|
503
515
|
}
|
|
504
516
|
var SOCKET_CONTROL_MODE_PATH, AUTOMATION_MODE, MINIMAL_TEMPLATE;
|
|
505
517
|
var init_cmux_config = __esm({
|
|
@@ -642,16 +654,75 @@ writeFileSync(resultFile, JSON.stringify(result));
|
|
|
642
654
|
}
|
|
643
655
|
});
|
|
644
656
|
|
|
645
|
-
// packages/shared/dist/lib/
|
|
646
|
-
import { existsSync as existsSync4,
|
|
657
|
+
// packages/shared/dist/lib/state-paths.js
|
|
658
|
+
import { cpSync, existsSync as existsSync4, readdirSync, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
647
659
|
import { homedir as homedir3 } from "os";
|
|
648
|
-
import { join as join4 } from "path";
|
|
660
|
+
import { dirname as dirname2, join as join4, resolve } from "path";
|
|
661
|
+
function isolatedConfigDir() {
|
|
662
|
+
const cfg = process.env.SQUADRANT_CONFIG;
|
|
663
|
+
if (!cfg || resolve(cfg) === join4(defaultConfigDir(), "config.json"))
|
|
664
|
+
return null;
|
|
665
|
+
return dirname2(resolve(cfg));
|
|
666
|
+
}
|
|
667
|
+
function stateRoot() {
|
|
668
|
+
if (process.env.SQUADRANT_STATE_DIR)
|
|
669
|
+
return process.env.SQUADRANT_STATE_DIR;
|
|
670
|
+
const isolated = isolatedConfigDir();
|
|
671
|
+
if (isolated)
|
|
672
|
+
return join4(isolated, "state");
|
|
673
|
+
return join4(process.env.XDG_STATE_HOME || join4(homedir3(), ".local", "state"), "squadrant");
|
|
674
|
+
}
|
|
675
|
+
function logsRoot() {
|
|
676
|
+
return join4(stateRoot(), "logs");
|
|
677
|
+
}
|
|
678
|
+
function daemonLogPath() {
|
|
679
|
+
return join4(logsRoot(), "squadrantd.log");
|
|
680
|
+
}
|
|
681
|
+
function legacyStateDir() {
|
|
682
|
+
return join4(isolatedConfigDir() ?? defaultConfigDir(), "state");
|
|
683
|
+
}
|
|
684
|
+
function legacyDaemonLogPath() {
|
|
685
|
+
return join4(isolatedConfigDir() ?? defaultConfigDir(), "squadrantd.log");
|
|
686
|
+
}
|
|
687
|
+
function stateSeedMarkerPath(root = stateRoot()) {
|
|
688
|
+
return join4(root, ".seeded-from-legacy");
|
|
689
|
+
}
|
|
690
|
+
function resolveReadPath(newPath, legacyPath) {
|
|
691
|
+
if (existsSync4(newPath) || !existsSync4(legacyPath))
|
|
692
|
+
return newPath;
|
|
693
|
+
return legacyPath;
|
|
694
|
+
}
|
|
695
|
+
function seedStateFromLegacy(opts = {}) {
|
|
696
|
+
const root = opts.root ?? stateRoot();
|
|
697
|
+
const legacy = opts.legacy ?? legacyStateDir();
|
|
698
|
+
if (resolve(root) === resolve(legacy))
|
|
699
|
+
return { seeded: false, reason: "same-dir" };
|
|
700
|
+
const marker = stateSeedMarkerPath(root);
|
|
701
|
+
if (existsSync4(marker))
|
|
702
|
+
return { seeded: false, reason: "already-seeded" };
|
|
703
|
+
if (!existsSync4(legacy) || readdirSync(legacy).length === 0)
|
|
704
|
+
return { seeded: false, reason: "no-legacy-data" };
|
|
705
|
+
mkdirSync2(root, { recursive: true });
|
|
706
|
+
cpSync(legacy, root, { recursive: true, force: false, errorOnExist: false });
|
|
707
|
+
writeFileSync3(marker, JSON.stringify({ from: legacy, at: (/* @__PURE__ */ new Date()).toISOString() }) + "\n");
|
|
708
|
+
return { seeded: true, reason: "seeded" };
|
|
709
|
+
}
|
|
710
|
+
var defaultConfigDir;
|
|
711
|
+
var init_state_paths = __esm({
|
|
712
|
+
"packages/shared/dist/lib/state-paths.js"() {
|
|
713
|
+
defaultConfigDir = () => join4(homedir3(), ".config", "squadrant");
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// packages/shared/dist/lib/cmux-autoconfig.js
|
|
718
|
+
import { existsSync as existsSync5, rmSync as rmSync2 } from "fs";
|
|
719
|
+
import { join as join5 } from "path";
|
|
649
720
|
function defaultStatePath() {
|
|
650
|
-
return
|
|
721
|
+
return join5(stateRoot(), "cmux-autoconfig.json");
|
|
651
722
|
}
|
|
652
|
-
function readState(
|
|
723
|
+
function readState(path44) {
|
|
653
724
|
try {
|
|
654
|
-
return JSON.parse(readConfigFileSync(
|
|
725
|
+
return JSON.parse(readConfigFileSync(path44));
|
|
655
726
|
} catch {
|
|
656
727
|
return {};
|
|
657
728
|
}
|
|
@@ -671,7 +742,7 @@ async function ensureCmuxAutoConfig(opts = {}) {
|
|
|
671
742
|
promptedThisRun = true;
|
|
672
743
|
}
|
|
673
744
|
} else if (verdict === "reachable") {
|
|
674
|
-
if (
|
|
745
|
+
if (existsSync5(statePath3))
|
|
675
746
|
rmSync2(statePath3, { force: true });
|
|
676
747
|
}
|
|
677
748
|
return {
|
|
@@ -688,6 +759,7 @@ var init_cmux_autoconfig = __esm({
|
|
|
688
759
|
init_cmux_config();
|
|
689
760
|
init_cmux_probe();
|
|
690
761
|
init_config_io();
|
|
762
|
+
init_state_paths();
|
|
691
763
|
}
|
|
692
764
|
});
|
|
693
765
|
|
|
@@ -723,14 +795,14 @@ var init_config_version = __esm({
|
|
|
723
795
|
});
|
|
724
796
|
|
|
725
797
|
// packages/shared/dist/lib/update-check.js
|
|
726
|
-
import
|
|
727
|
-
import
|
|
728
|
-
import
|
|
798
|
+
import fs3 from "fs";
|
|
799
|
+
import path6 from "path";
|
|
800
|
+
import os4 from "os";
|
|
729
801
|
import https from "https";
|
|
730
802
|
var UPDATE_CHECK_STATE_PATH, CHECK_INTERVAL_MS, FAILURE_RETRY_MS;
|
|
731
803
|
var init_update_check = __esm({
|
|
732
804
|
"packages/shared/dist/lib/update-check.js"() {
|
|
733
|
-
UPDATE_CHECK_STATE_PATH =
|
|
805
|
+
UPDATE_CHECK_STATE_PATH = path6.join(os4.homedir(), ".config", "squadrant", "update-check.json");
|
|
734
806
|
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
735
807
|
FAILURE_RETRY_MS = 60 * 60 * 1e3;
|
|
736
808
|
}
|
|
@@ -738,10 +810,10 @@ var init_update_check = __esm({
|
|
|
738
810
|
|
|
739
811
|
// packages/shared/dist/lib/git-worktree.js
|
|
740
812
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
741
|
-
import
|
|
742
|
-
import
|
|
813
|
+
import fs4 from "fs";
|
|
814
|
+
import path7 from "path";
|
|
743
815
|
function worktreePath(repoRoot, worktreeDir, project, name) {
|
|
744
|
-
return
|
|
816
|
+
return path7.resolve(repoRoot, worktreeDir, `${project}-${name}`);
|
|
745
817
|
}
|
|
746
818
|
function crewBranch(name) {
|
|
747
819
|
return `crew/${name}`;
|
|
@@ -750,11 +822,11 @@ function ensureSpotlightExcluded(repoRoot, worktreeDir) {
|
|
|
750
822
|
if (process.platform !== "darwin")
|
|
751
823
|
return;
|
|
752
824
|
try {
|
|
753
|
-
const dir =
|
|
754
|
-
|
|
755
|
-
const marker =
|
|
756
|
-
if (!
|
|
757
|
-
|
|
825
|
+
const dir = path7.resolve(repoRoot, worktreeDir);
|
|
826
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
827
|
+
const marker = path7.join(dir, ".metadata_never_index");
|
|
828
|
+
if (!fs4.existsSync(marker))
|
|
829
|
+
fs4.writeFileSync(marker, "");
|
|
758
830
|
} catch {
|
|
759
831
|
}
|
|
760
832
|
}
|
|
@@ -816,15 +888,15 @@ function addWorktree(spec) {
|
|
|
816
888
|
return wt;
|
|
817
889
|
}
|
|
818
890
|
function installWorktreeDependencies(wt) {
|
|
819
|
-
if (!
|
|
891
|
+
if (!fs4.existsSync(path7.join(wt, "package.json")))
|
|
820
892
|
return;
|
|
821
|
-
if (
|
|
893
|
+
if (fs4.existsSync(path7.join(wt, "pnpm-lock.yaml"))) {
|
|
822
894
|
execFileSync2("pnpm", ["-C", wt, "install", "--frozen-lockfile"], { stdio: "pipe" });
|
|
823
|
-
} else if (
|
|
895
|
+
} else if (fs4.existsSync(path7.join(wt, "yarn.lock"))) {
|
|
824
896
|
execFileSync2("yarn", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
|
|
825
|
-
} else if (
|
|
897
|
+
} else if (fs4.existsSync(path7.join(wt, "package-lock.json"))) {
|
|
826
898
|
execFileSync2("npm", ["ci"], { cwd: wt, stdio: "pipe" });
|
|
827
|
-
} else if (
|
|
899
|
+
} else if (fs4.existsSync(path7.join(wt, "bun.lockb"))) {
|
|
828
900
|
execFileSync2("bun", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
|
|
829
901
|
} else {
|
|
830
902
|
process.stderr.write(`worktree ${wt}: package.json present but no lockfile \u2014 dependencies not installed; local typechecks/tests may resolve against the main checkout instead of this worktree.
|
|
@@ -843,7 +915,7 @@ function worktreeDirtyFiles(wtPath) {
|
|
|
843
915
|
}
|
|
844
916
|
function removeGeneratedWorktreeFiles(wtPath) {
|
|
845
917
|
for (const [, file] of worktreeStatus(wtPath).filter(isGeneratedUntracked)) {
|
|
846
|
-
|
|
918
|
+
fs4.rmSync(path7.join(wtPath, file), { force: true });
|
|
847
919
|
}
|
|
848
920
|
}
|
|
849
921
|
function removeWorktree(repoRoot, wtPath, opts) {
|
|
@@ -859,15 +931,15 @@ var init_git_worktree = __esm({
|
|
|
859
931
|
});
|
|
860
932
|
|
|
861
933
|
// packages/shared/dist/lib/resolve-text-input.js
|
|
862
|
-
import
|
|
934
|
+
import fs5 from "fs";
|
|
863
935
|
var init_resolve_text_input = __esm({
|
|
864
936
|
"packages/shared/dist/lib/resolve-text-input.js"() {
|
|
865
937
|
}
|
|
866
938
|
});
|
|
867
939
|
|
|
868
940
|
// packages/shared/dist/lib/runtime-sync.js
|
|
869
|
-
import
|
|
870
|
-
import
|
|
941
|
+
import fs6 from "fs";
|
|
942
|
+
import path8 from "path";
|
|
871
943
|
var init_runtime_sync = __esm({
|
|
872
944
|
"packages/shared/dist/lib/runtime-sync.js"() {
|
|
873
945
|
}
|
|
@@ -909,8 +981,8 @@ var init_tool_compat = __esm({
|
|
|
909
981
|
});
|
|
910
982
|
|
|
911
983
|
// packages/shared/dist/lib/canonical-source.js
|
|
912
|
-
import
|
|
913
|
-
import
|
|
984
|
+
import fs7 from "fs";
|
|
985
|
+
import path9 from "path";
|
|
914
986
|
var init_canonical_source = __esm({
|
|
915
987
|
"packages/shared/dist/lib/canonical-source.js"() {
|
|
916
988
|
}
|
|
@@ -918,8 +990,8 @@ var init_canonical_source = __esm({
|
|
|
918
990
|
|
|
919
991
|
// packages/shared/dist/lib/daily-logs.js
|
|
920
992
|
import { execSync } from "child_process";
|
|
921
|
-
import
|
|
922
|
-
import
|
|
993
|
+
import fs8 from "fs";
|
|
994
|
+
import path10 from "path";
|
|
923
995
|
import matter from "gray-matter";
|
|
924
996
|
var init_daily_logs = __esm({
|
|
925
997
|
"packages/shared/dist/lib/daily-logs.js"() {
|
|
@@ -942,12 +1014,6 @@ var init_session_name = __esm({
|
|
|
942
1014
|
}
|
|
943
1015
|
});
|
|
944
1016
|
|
|
945
|
-
// packages/shared/dist/lib/vault-layout.js
|
|
946
|
-
var init_vault_layout = __esm({
|
|
947
|
-
"packages/shared/dist/lib/vault-layout.js"() {
|
|
948
|
-
}
|
|
949
|
-
});
|
|
950
|
-
|
|
951
1017
|
// packages/shared/dist/daemon-keys.js
|
|
952
1018
|
var init_daemon_keys = __esm({
|
|
953
1019
|
"packages/shared/dist/daemon-keys.js"() {
|
|
@@ -962,6 +1028,7 @@ var init_dist = __esm({
|
|
|
962
1028
|
init_project_config();
|
|
963
1029
|
init_effort();
|
|
964
1030
|
init_knowledge();
|
|
1031
|
+
init_workspace();
|
|
965
1032
|
init_router_model();
|
|
966
1033
|
init_runtime();
|
|
967
1034
|
init_liveness();
|
|
@@ -985,7 +1052,7 @@ var init_dist = __esm({
|
|
|
985
1052
|
init_canonical_source();
|
|
986
1053
|
init_daily_logs();
|
|
987
1054
|
init_session_name();
|
|
988
|
-
|
|
1055
|
+
init_state_paths();
|
|
989
1056
|
init_daemon_keys();
|
|
990
1057
|
}
|
|
991
1058
|
});
|
|
@@ -1670,33 +1737,33 @@ var init_reduce = __esm({
|
|
|
1670
1737
|
});
|
|
1671
1738
|
|
|
1672
1739
|
// packages/core/dist/mailbox.js
|
|
1673
|
-
import { promises as
|
|
1674
|
-
import { join as
|
|
1740
|
+
import { promises as fs9 } from "fs";
|
|
1741
|
+
import { join as join6 } from "path";
|
|
1675
1742
|
import { randomUUID } from "crypto";
|
|
1676
|
-
function inboxDir(
|
|
1677
|
-
return
|
|
1743
|
+
function inboxDir(stateRoot2) {
|
|
1744
|
+
return join6(stateRoot2, "inbox");
|
|
1678
1745
|
}
|
|
1679
|
-
function logPath(
|
|
1680
|
-
return
|
|
1746
|
+
function logPath(stateRoot2, project) {
|
|
1747
|
+
return join6(inboxDir(stateRoot2), `${project}.log`);
|
|
1681
1748
|
}
|
|
1682
1749
|
function extractPayload(event) {
|
|
1683
1750
|
const { type: _type, id: _id, ...payload } = event;
|
|
1684
1751
|
return payload;
|
|
1685
1752
|
}
|
|
1686
|
-
async function listRotatedOldestFirst(
|
|
1687
|
-
const dir = inboxDir(
|
|
1753
|
+
async function listRotatedOldestFirst(stateRoot2, project) {
|
|
1754
|
+
const dir = inboxDir(stateRoot2);
|
|
1688
1755
|
let entries;
|
|
1689
1756
|
try {
|
|
1690
|
-
entries = await
|
|
1757
|
+
entries = await fs9.readdir(dir);
|
|
1691
1758
|
} catch {
|
|
1692
1759
|
return [];
|
|
1693
1760
|
}
|
|
1694
1761
|
const prefix = `${project}.log.`;
|
|
1695
|
-
return entries.filter((e) => e.startsWith(prefix) && /^\d+$/.test(e.slice(prefix.length))).map((e) => ({ name: e, n: Number(e.slice(prefix.length)) })).sort((a, b) => b.n - a.n).map((e) =>
|
|
1762
|
+
return entries.filter((e) => e.startsWith(prefix) && /^\d+$/.test(e.slice(prefix.length))).map((e) => ({ name: e, n: Number(e.slice(prefix.length)) })).sort((a, b) => b.n - a.n).map((e) => join6(dir, e.name));
|
|
1696
1763
|
}
|
|
1697
1764
|
async function readMaxSeqFromFile(file) {
|
|
1698
1765
|
try {
|
|
1699
|
-
const buf = await
|
|
1766
|
+
const buf = await fs9.readFile(file, "utf-8");
|
|
1700
1767
|
if (!buf.trim())
|
|
1701
1768
|
return 0;
|
|
1702
1769
|
const lines = buf.trim().split("\n");
|
|
@@ -1715,11 +1782,11 @@ async function readMaxSeqFromFile(file) {
|
|
|
1715
1782
|
throw e;
|
|
1716
1783
|
}
|
|
1717
1784
|
}
|
|
1718
|
-
async function readMaxSeq(
|
|
1785
|
+
async function readMaxSeq(stateRoot2, project) {
|
|
1719
1786
|
let max = 0;
|
|
1720
1787
|
const files = [
|
|
1721
|
-
logPath(
|
|
1722
|
-
...await listRotatedOldestFirst(
|
|
1788
|
+
logPath(stateRoot2, project),
|
|
1789
|
+
...await listRotatedOldestFirst(stateRoot2, project)
|
|
1723
1790
|
];
|
|
1724
1791
|
for (const file of files) {
|
|
1725
1792
|
const seq2 = await readMaxSeqFromFile(file);
|
|
@@ -1734,15 +1801,15 @@ function withProjectLock(project, fn) {
|
|
|
1734
1801
|
projectLocks.set(project, next.catch(() => void 0));
|
|
1735
1802
|
return next;
|
|
1736
1803
|
}
|
|
1737
|
-
function appendEntry(
|
|
1804
|
+
function appendEntry(stateRoot2, project, build) {
|
|
1738
1805
|
return withProjectLock(project, async () => {
|
|
1739
|
-
const dir = inboxDir(
|
|
1740
|
-
await
|
|
1741
|
-
const file = logPath(
|
|
1742
|
-
const lastSeq = await readMaxSeq(
|
|
1806
|
+
const dir = inboxDir(stateRoot2);
|
|
1807
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
1808
|
+
const file = logPath(stateRoot2, project);
|
|
1809
|
+
const lastSeq = await readMaxSeq(stateRoot2, project);
|
|
1743
1810
|
const seq2 = lastSeq + 1;
|
|
1744
1811
|
const entry = build(seq2);
|
|
1745
|
-
await
|
|
1812
|
+
await fs9.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
|
|
1746
1813
|
return seq2;
|
|
1747
1814
|
});
|
|
1748
1815
|
}
|
|
@@ -1767,13 +1834,13 @@ async function appendCaptainMessage(opts) {
|
|
|
1767
1834
|
message: opts.text
|
|
1768
1835
|
}));
|
|
1769
1836
|
}
|
|
1770
|
-
function cursorPath(
|
|
1771
|
-
return
|
|
1837
|
+
function cursorPath(stateRoot2, project, subscriber) {
|
|
1838
|
+
return join6(inboxDir(stateRoot2), `${project}.${subscriber}.cursor`);
|
|
1772
1839
|
}
|
|
1773
1840
|
async function readCursor(opts) {
|
|
1774
1841
|
let buf;
|
|
1775
1842
|
try {
|
|
1776
|
-
buf = await
|
|
1843
|
+
buf = await fs9.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
|
|
1777
1844
|
} catch (e) {
|
|
1778
1845
|
if (e.code === "ENOENT")
|
|
1779
1846
|
return null;
|
|
@@ -1800,7 +1867,7 @@ async function waitForCaptainDelivery(opts) {
|
|
|
1800
1867
|
}
|
|
1801
1868
|
}
|
|
1802
1869
|
async function writeCursor(opts) {
|
|
1803
|
-
await
|
|
1870
|
+
await fs9.mkdir(inboxDir(opts.stateRoot), { recursive: true });
|
|
1804
1871
|
const dest = cursorPath(opts.stateRoot, opts.project, opts.subscriber);
|
|
1805
1872
|
const tmp = `${dest}.${process.pid}.${randomUUID()}.tmp`;
|
|
1806
1873
|
const data = {
|
|
@@ -1808,7 +1875,7 @@ async function writeCursor(opts) {
|
|
|
1808
1875
|
subscriber: opts.subscriber,
|
|
1809
1876
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1810
1877
|
};
|
|
1811
|
-
const handle = await
|
|
1878
|
+
const handle = await fs9.open(tmp, "w");
|
|
1812
1879
|
try {
|
|
1813
1880
|
await handle.writeFile(JSON.stringify(data), { encoding: "utf-8" });
|
|
1814
1881
|
await handle.sync();
|
|
@@ -1816,9 +1883,9 @@ async function writeCursor(opts) {
|
|
|
1816
1883
|
await handle.close();
|
|
1817
1884
|
}
|
|
1818
1885
|
try {
|
|
1819
|
-
await
|
|
1886
|
+
await fs9.rename(tmp, dest);
|
|
1820
1887
|
} catch (e) {
|
|
1821
|
-
await
|
|
1888
|
+
await fs9.unlink(tmp).catch(() => {
|
|
1822
1889
|
});
|
|
1823
1890
|
throw e;
|
|
1824
1891
|
}
|
|
@@ -1829,7 +1896,7 @@ async function* readFromCursor(opts) {
|
|
|
1829
1896
|
for (const file of files) {
|
|
1830
1897
|
let buf;
|
|
1831
1898
|
try {
|
|
1832
|
-
buf = await
|
|
1899
|
+
buf = await fs9.readFile(file, "utf-8");
|
|
1833
1900
|
} catch (e) {
|
|
1834
1901
|
if (e.code === "ENOENT")
|
|
1835
1902
|
continue;
|
|
@@ -1849,13 +1916,13 @@ async function* readFromCursor(opts) {
|
|
|
1849
1916
|
}
|
|
1850
1917
|
}
|
|
1851
1918
|
}
|
|
1852
|
-
async function mailboxStats(
|
|
1853
|
-
const file = logPath(
|
|
1854
|
-
const rotated = await listRotatedOldestFirst(
|
|
1919
|
+
async function mailboxStats(stateRoot2, project) {
|
|
1920
|
+
const file = logPath(stateRoot2, project);
|
|
1921
|
+
const rotated = await listRotatedOldestFirst(stateRoot2, project);
|
|
1855
1922
|
let sizeBytes = 0;
|
|
1856
1923
|
for (const f of [file, ...rotated]) {
|
|
1857
1924
|
try {
|
|
1858
|
-
sizeBytes += (await
|
|
1925
|
+
sizeBytes += (await fs9.stat(f)).size;
|
|
1859
1926
|
} catch (e) {
|
|
1860
1927
|
if (e.code !== "ENOENT")
|
|
1861
1928
|
throw e;
|
|
@@ -1863,7 +1930,7 @@ async function mailboxStats(stateRoot, project) {
|
|
|
1863
1930
|
}
|
|
1864
1931
|
const oldestFile = rotated[0] ?? file;
|
|
1865
1932
|
return {
|
|
1866
|
-
maxSeq: await readMaxSeq(
|
|
1933
|
+
maxSeq: await readMaxSeq(stateRoot2, project),
|
|
1867
1934
|
sizeBytes,
|
|
1868
1935
|
oldestEntryAgeMs: await oldestEntryAgeMs(oldestFile),
|
|
1869
1936
|
rotationCount: rotated.length
|
|
@@ -1871,7 +1938,7 @@ async function mailboxStats(stateRoot, project) {
|
|
|
1871
1938
|
}
|
|
1872
1939
|
async function oldestEntryAgeMs(file) {
|
|
1873
1940
|
try {
|
|
1874
|
-
const buf = await
|
|
1941
|
+
const buf = await fs9.readFile(file, "utf-8");
|
|
1875
1942
|
const firstLine = buf.split("\n").find((l) => l.trim());
|
|
1876
1943
|
if (!firstLine)
|
|
1877
1944
|
return 0;
|
|
@@ -1886,7 +1953,7 @@ async function rotateIfNeeded(opts) {
|
|
|
1886
1953
|
const file = logPath(opts.stateRoot, opts.project);
|
|
1887
1954
|
let size = 0;
|
|
1888
1955
|
try {
|
|
1889
|
-
size = (await
|
|
1956
|
+
size = (await fs9.stat(file)).size;
|
|
1890
1957
|
} catch (e) {
|
|
1891
1958
|
if (e.code === "ENOENT")
|
|
1892
1959
|
return { rotated: false };
|
|
@@ -1902,22 +1969,22 @@ async function rotateIfNeeded(opts) {
|
|
|
1902
1969
|
const dst = `${file}.${n + 1}`;
|
|
1903
1970
|
if (n + 1 > opts.keepCount) {
|
|
1904
1971
|
try {
|
|
1905
|
-
await
|
|
1972
|
+
await fs9.unlink(src);
|
|
1906
1973
|
} catch (e) {
|
|
1907
1974
|
if (e.code !== "ENOENT")
|
|
1908
1975
|
throw e;
|
|
1909
1976
|
}
|
|
1910
1977
|
} else {
|
|
1911
1978
|
try {
|
|
1912
|
-
await
|
|
1979
|
+
await fs9.rename(src, dst);
|
|
1913
1980
|
} catch (e) {
|
|
1914
1981
|
if (e.code !== "ENOENT")
|
|
1915
1982
|
throw e;
|
|
1916
1983
|
}
|
|
1917
1984
|
}
|
|
1918
1985
|
}
|
|
1919
|
-
await
|
|
1920
|
-
await
|
|
1986
|
+
await fs9.rename(file, `${file}.1`);
|
|
1987
|
+
await fs9.writeFile(file, "", { encoding: "utf-8" });
|
|
1921
1988
|
return { rotated: true, from: file, to: `${file}.1` };
|
|
1922
1989
|
});
|
|
1923
1990
|
}
|
|
@@ -1930,7 +1997,7 @@ var init_mailbox = __esm({
|
|
|
1930
1997
|
|
|
1931
1998
|
// packages/core/dist/protocol.js
|
|
1932
1999
|
import { createServer, createConnection } from "net";
|
|
1933
|
-
import { existsSync as
|
|
2000
|
+
import { existsSync as existsSync6, unlinkSync, chmodSync } from "fs";
|
|
1934
2001
|
function encodeMsg(obj) {
|
|
1935
2002
|
return JSON.stringify(obj) + "\n";
|
|
1936
2003
|
}
|
|
@@ -1975,7 +2042,7 @@ function startServer(sockPath, handlerOrCallbacks, onListenError = defaultListen
|
|
|
1975
2042
|
const { handler, onAttach, onAttachInbound, onAttachClose } = callbacks;
|
|
1976
2043
|
const setIntervalFn = deps.setInterval ?? setInterval;
|
|
1977
2044
|
const clearIntervalFn = deps.clearInterval ?? clearInterval;
|
|
1978
|
-
if (
|
|
2045
|
+
if (existsSync6(sockPath)) {
|
|
1979
2046
|
try {
|
|
1980
2047
|
unlinkSync(sockPath);
|
|
1981
2048
|
} catch {
|
|
@@ -2053,9 +2120,9 @@ function startServer(sockPath, handlerOrCallbacks, onListenError = defaultListen
|
|
|
2053
2120
|
return server;
|
|
2054
2121
|
}
|
|
2055
2122
|
function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
2056
|
-
return new Promise((
|
|
2057
|
-
if (!
|
|
2058
|
-
|
|
2123
|
+
return new Promise((resolve5) => {
|
|
2124
|
+
if (!existsSync6(sockPath)) {
|
|
2125
|
+
resolve5(false);
|
|
2059
2126
|
return;
|
|
2060
2127
|
}
|
|
2061
2128
|
const conn = createConnection(sockPath);
|
|
@@ -2064,7 +2131,7 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
2064
2131
|
conn.destroy();
|
|
2065
2132
|
} catch {
|
|
2066
2133
|
}
|
|
2067
|
-
|
|
2134
|
+
resolve5(v2);
|
|
2068
2135
|
};
|
|
2069
2136
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
2070
2137
|
conn.on("connect", () => {
|
|
@@ -2078,7 +2145,7 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
2078
2145
|
});
|
|
2079
2146
|
}
|
|
2080
2147
|
function sendRequest(sockPath, msg, timeoutMs = 5e3) {
|
|
2081
|
-
return new Promise((
|
|
2148
|
+
return new Promise((resolve5, reject) => {
|
|
2082
2149
|
const conn = createConnection(sockPath);
|
|
2083
2150
|
const dec = createDecoder();
|
|
2084
2151
|
const timer = setTimeout(() => {
|
|
@@ -2094,7 +2161,7 @@ function sendRequest(sockPath, msg, timeoutMs = 5e3) {
|
|
|
2094
2161
|
if (m._v !== void 0 && m._v !== PROTOCOL_VERSION) {
|
|
2095
2162
|
reject(new Error(`squadrantd protocol v${m._v}, this client expects v${PROTOCOL_VERSION} \u2014 upgrade squadrantd or this CLI`));
|
|
2096
2163
|
} else if (m.ok) {
|
|
2097
|
-
|
|
2164
|
+
resolve5(m.reply);
|
|
2098
2165
|
} else {
|
|
2099
2166
|
reject(new Error(m.error));
|
|
2100
2167
|
}
|
|
@@ -2243,8 +2310,8 @@ var init_liveness2 = __esm({
|
|
|
2243
2310
|
});
|
|
2244
2311
|
|
|
2245
2312
|
// packages/core/dist/store.js
|
|
2246
|
-
import { mkdirSync as
|
|
2247
|
-
import { join as
|
|
2313
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, renameSync, writeFileSync as writeFileSync4, existsSync as existsSync7, rmSync as rmSync3, statSync } from "fs";
|
|
2314
|
+
import { join as join7, resolve as resolve2, sep } from "path";
|
|
2248
2315
|
function safeSegment(kind, s) {
|
|
2249
2316
|
if (typeof s !== "string" || s.length === 0) {
|
|
2250
2317
|
throw new Error(`invalid ${kind}: must be a non-empty string`);
|
|
@@ -2263,19 +2330,19 @@ function isTaskRecord(r) {
|
|
|
2263
2330
|
return typeof rec.id === "string" && rec.id.length > 0 && typeof rec.project === "string" && rec.project.length > 0;
|
|
2264
2331
|
}
|
|
2265
2332
|
function createStore(root) {
|
|
2266
|
-
const rootResolved =
|
|
2333
|
+
const rootResolved = resolve2(root);
|
|
2267
2334
|
const assertUnderRoot = (target) => {
|
|
2268
|
-
const r =
|
|
2335
|
+
const r = resolve2(target);
|
|
2269
2336
|
if (r !== rootResolved && !r.startsWith(rootResolved + sep)) {
|
|
2270
2337
|
throw new Error(`path escapes state root: ${target}`);
|
|
2271
2338
|
}
|
|
2272
2339
|
return target;
|
|
2273
2340
|
};
|
|
2274
|
-
const projDir = (p) => assertUnderRoot(
|
|
2275
|
-
const taskFile = (p, id) => assertUnderRoot(
|
|
2341
|
+
const projDir = (p) => assertUnderRoot(join7(root, safeSegment("project", p)));
|
|
2342
|
+
const taskFile = (p, id) => assertUnderRoot(join7(projDir(p), `${safeSegment("id", id)}.json`));
|
|
2276
2343
|
const readRecord = (project, id) => {
|
|
2277
2344
|
const f = taskFile(project, id);
|
|
2278
|
-
if (!
|
|
2345
|
+
if (!existsSync7(f))
|
|
2279
2346
|
return void 0;
|
|
2280
2347
|
try {
|
|
2281
2348
|
return JSON.parse(readFileSync4(f, "utf-8"));
|
|
@@ -2290,10 +2357,10 @@ function createStore(root) {
|
|
|
2290
2357
|
console.error(`[squadrant] REJECTED terminal\u2192terminal overwrite of ${rec.project}/${rec.id}: already ${existing.state}/${existing.lastEvent} \u2014 refusing ${rec.state}/${rec.lastEvent}; original terminal record preserved (#595)`);
|
|
2291
2358
|
return;
|
|
2292
2359
|
}
|
|
2293
|
-
|
|
2360
|
+
mkdirSync3(projDir(rec.project), { recursive: true });
|
|
2294
2361
|
const dest = taskFile(rec.project, rec.id);
|
|
2295
2362
|
const tmp = `${dest}.tmp`;
|
|
2296
|
-
|
|
2363
|
+
writeFileSync4(tmp, JSON.stringify(rec, null, 2));
|
|
2297
2364
|
renameSync(tmp, dest);
|
|
2298
2365
|
},
|
|
2299
2366
|
get(project, id) {
|
|
@@ -2301,22 +2368,22 @@ function createStore(root) {
|
|
|
2301
2368
|
},
|
|
2302
2369
|
list(project) {
|
|
2303
2370
|
const d = projDir(project);
|
|
2304
|
-
if (!
|
|
2371
|
+
if (!existsSync7(d))
|
|
2305
2372
|
return [];
|
|
2306
|
-
return
|
|
2373
|
+
return readdirSync2(d).filter((n) => n.endsWith(".json")).map((n) => {
|
|
2307
2374
|
try {
|
|
2308
|
-
return JSON.parse(readFileSync4(
|
|
2375
|
+
return JSON.parse(readFileSync4(join7(d, n), "utf-8"));
|
|
2309
2376
|
} catch {
|
|
2310
2377
|
return void 0;
|
|
2311
2378
|
}
|
|
2312
2379
|
}).filter(isTaskRecord);
|
|
2313
2380
|
},
|
|
2314
2381
|
listAll() {
|
|
2315
|
-
if (!
|
|
2382
|
+
if (!existsSync7(root))
|
|
2316
2383
|
return [];
|
|
2317
|
-
return
|
|
2384
|
+
return readdirSync2(root).filter((p) => {
|
|
2318
2385
|
try {
|
|
2319
|
-
return statSync(
|
|
2386
|
+
return statSync(join7(root, p)).isDirectory();
|
|
2320
2387
|
} catch {
|
|
2321
2388
|
return false;
|
|
2322
2389
|
}
|
|
@@ -2324,12 +2391,12 @@ function createStore(root) {
|
|
|
2324
2391
|
},
|
|
2325
2392
|
quarantine(project, id) {
|
|
2326
2393
|
const f = taskFile(project, id);
|
|
2327
|
-
if (
|
|
2394
|
+
if (existsSync7(f))
|
|
2328
2395
|
renameSync(f, `${f}.corrupt.${Date.now()}`);
|
|
2329
2396
|
},
|
|
2330
2397
|
delete(project, id) {
|
|
2331
2398
|
const f = taskFile(project, id);
|
|
2332
|
-
if (
|
|
2399
|
+
if (existsSync7(f))
|
|
2333
2400
|
rmSync3(f);
|
|
2334
2401
|
}
|
|
2335
2402
|
};
|
|
@@ -2342,11 +2409,11 @@ var init_store = __esm({
|
|
|
2342
2409
|
|
|
2343
2410
|
// packages/core/dist/work-store.js
|
|
2344
2411
|
import { homedir as homedir4 } from "os";
|
|
2345
|
-
import { join as
|
|
2412
|
+
import { join as join8, resolve as resolve3, sep as sep2 } from "path";
|
|
2346
2413
|
import { randomBytes } from "crypto";
|
|
2347
|
-
import { mkdirSync as
|
|
2414
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync3, renameSync as renameSync2, writeFileSync as writeFileSync5, existsSync as existsSync8, rmSync as rmSync4, statSync as statSync2 } from "fs";
|
|
2348
2415
|
function defaultWorkRoot() {
|
|
2349
|
-
return
|
|
2416
|
+
return join8(homedir4(), ".config", "squadrant", "work");
|
|
2350
2417
|
}
|
|
2351
2418
|
function safeSegment2(kind, s) {
|
|
2352
2419
|
if (typeof s !== "string" || s.length === 0) {
|
|
@@ -2360,27 +2427,27 @@ function safeSegment2(kind, s) {
|
|
|
2360
2427
|
return s;
|
|
2361
2428
|
}
|
|
2362
2429
|
function createWorkStore(root = defaultWorkRoot()) {
|
|
2363
|
-
const rootResolved =
|
|
2430
|
+
const rootResolved = resolve3(root);
|
|
2364
2431
|
const assertUnderRoot = (target) => {
|
|
2365
|
-
const r =
|
|
2432
|
+
const r = resolve3(target);
|
|
2366
2433
|
if (r !== rootResolved && !r.startsWith(rootResolved + sep2)) {
|
|
2367
2434
|
throw new Error(`path escapes state root: ${target}`);
|
|
2368
2435
|
}
|
|
2369
2436
|
return target;
|
|
2370
2437
|
};
|
|
2371
|
-
const projDir = (p) => assertUnderRoot(
|
|
2372
|
-
const itemFile = (p, id) => assertUnderRoot(
|
|
2438
|
+
const projDir = (p) => assertUnderRoot(join8(root, safeSegment2("project", p)));
|
|
2439
|
+
const itemFile = (p, id) => assertUnderRoot(join8(projDir(p), `${safeSegment2("id", id)}.json`));
|
|
2373
2440
|
return {
|
|
2374
2441
|
put(item) {
|
|
2375
|
-
|
|
2442
|
+
mkdirSync4(projDir(item.project), { recursive: true });
|
|
2376
2443
|
const dest = itemFile(item.project, item.id);
|
|
2377
2444
|
const tmp = `${dest}.tmp`;
|
|
2378
|
-
|
|
2445
|
+
writeFileSync5(tmp, JSON.stringify(item, null, 2));
|
|
2379
2446
|
renameSync2(tmp, dest);
|
|
2380
2447
|
},
|
|
2381
2448
|
get(project, id) {
|
|
2382
2449
|
const f = itemFile(project, id);
|
|
2383
|
-
if (!
|
|
2450
|
+
if (!existsSync8(f))
|
|
2384
2451
|
return void 0;
|
|
2385
2452
|
try {
|
|
2386
2453
|
return JSON.parse(readFileSync5(f, "utf-8"));
|
|
@@ -2390,22 +2457,22 @@ function createWorkStore(root = defaultWorkRoot()) {
|
|
|
2390
2457
|
},
|
|
2391
2458
|
list(project) {
|
|
2392
2459
|
const d = projDir(project);
|
|
2393
|
-
if (!
|
|
2460
|
+
if (!existsSync8(d))
|
|
2394
2461
|
return [];
|
|
2395
|
-
return
|
|
2462
|
+
return readdirSync3(d).filter((n) => n.endsWith(".json") && !n.endsWith(".json.tmp")).map((n) => {
|
|
2396
2463
|
try {
|
|
2397
|
-
return JSON.parse(readFileSync5(
|
|
2464
|
+
return JSON.parse(readFileSync5(join8(d, n), "utf-8"));
|
|
2398
2465
|
} catch {
|
|
2399
2466
|
return void 0;
|
|
2400
2467
|
}
|
|
2401
2468
|
}).filter((r) => r !== void 0);
|
|
2402
2469
|
},
|
|
2403
2470
|
listAll() {
|
|
2404
|
-
if (!
|
|
2471
|
+
if (!existsSync8(root))
|
|
2405
2472
|
return [];
|
|
2406
|
-
return
|
|
2473
|
+
return readdirSync3(root).filter((p) => {
|
|
2407
2474
|
try {
|
|
2408
|
-
return statSync2(
|
|
2475
|
+
return statSync2(join8(root, p)).isDirectory();
|
|
2409
2476
|
} catch {
|
|
2410
2477
|
return false;
|
|
2411
2478
|
}
|
|
@@ -2413,7 +2480,7 @@ function createWorkStore(root = defaultWorkRoot()) {
|
|
|
2413
2480
|
},
|
|
2414
2481
|
delete(project, id) {
|
|
2415
2482
|
const f = itemFile(project, id);
|
|
2416
|
-
if (
|
|
2483
|
+
if (existsSync8(f))
|
|
2417
2484
|
rmSync4(f);
|
|
2418
2485
|
}
|
|
2419
2486
|
};
|
|
@@ -2539,16 +2606,16 @@ var init_snapshot = __esm({
|
|
|
2539
2606
|
|
|
2540
2607
|
// packages/core/dist/launchd.js
|
|
2541
2608
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2542
|
-
import { mkdirSync as
|
|
2609
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, readFileSync as readFileSync6, existsSync as existsSync9, openSync, writeSync, closeSync, unlinkSync as unlinkSync2, constants } from "fs";
|
|
2543
2610
|
import { homedir as homedir5 } from "os";
|
|
2544
|
-
import { dirname as
|
|
2611
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
2545
2612
|
import { fileURLToPath } from "url";
|
|
2546
2613
|
function plistPath() {
|
|
2547
|
-
return
|
|
2614
|
+
return join9(homedir5(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
2548
2615
|
}
|
|
2549
2616
|
function daemonEntryPath() {
|
|
2550
|
-
const p =
|
|
2551
|
-
if (!
|
|
2617
|
+
const p = join9(dirname3(fileURLToPath(import.meta.url)), "squadrantd.js");
|
|
2618
|
+
if (!existsSync9(p)) {
|
|
2552
2619
|
throw new Error(`daemonEntryPath: compiled entry not found at '${p}'; run 'npm run build' \u2014 a src-tree or missing path in the launchd plist causes a MODULE_NOT_FOUND crash-loop (#259)`);
|
|
2553
2620
|
}
|
|
2554
2621
|
return p;
|
|
@@ -2598,10 +2665,10 @@ function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
|
|
|
2598
2665
|
isUpgrade: isVersionUpgrade(parsed.daemonEntry, thisEntry)
|
|
2599
2666
|
};
|
|
2600
2667
|
}
|
|
2601
|
-
function sanitizePathForPlist(
|
|
2668
|
+
function sanitizePathForPlist(path44) {
|
|
2602
2669
|
const seen = /* @__PURE__ */ new Set();
|
|
2603
2670
|
const stable = [];
|
|
2604
|
-
for (const p of
|
|
2671
|
+
for (const p of path44.split(":")) {
|
|
2605
2672
|
if (!p)
|
|
2606
2673
|
continue;
|
|
2607
2674
|
if (p.includes("/.claude/plugins/"))
|
|
@@ -2624,7 +2691,7 @@ function resolveAgentBinDirs() {
|
|
|
2624
2691
|
const out = execFileSync3("which", [bin], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
2625
2692
|
const resolved = out.trim();
|
|
2626
2693
|
if (resolved)
|
|
2627
|
-
dirs.push(
|
|
2694
|
+
dirs.push(dirname3(resolved));
|
|
2628
2695
|
} catch {
|
|
2629
2696
|
}
|
|
2630
2697
|
}
|
|
@@ -2640,7 +2707,7 @@ function buildDaemonPath(shellPath) {
|
|
|
2640
2707
|
return sanitizePathForPlist([...resolveAgentBinDirs(), shellPath].join(":"));
|
|
2641
2708
|
}
|
|
2642
2709
|
function renderPlist(nodeBin, daemonEntry, pathEnv = "") {
|
|
2643
|
-
const logPath2 =
|
|
2710
|
+
const logPath2 = daemonLogPath();
|
|
2644
2711
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2645
2712
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2646
2713
|
<plist version="1.0">
|
|
@@ -2669,11 +2736,11 @@ function _resetRestartInFlightForTest() {
|
|
|
2669
2736
|
restartInFlight = false;
|
|
2670
2737
|
}
|
|
2671
2738
|
function daemonLockPath() {
|
|
2672
|
-
return
|
|
2739
|
+
return join9(homedir5(), ".config", "squadrant", "daemon.lock");
|
|
2673
2740
|
}
|
|
2674
2741
|
function tryAcquireDaemonLock() {
|
|
2675
2742
|
const lp = daemonLockPath();
|
|
2676
|
-
if (
|
|
2743
|
+
if (existsSync9(lp)) {
|
|
2677
2744
|
try {
|
|
2678
2745
|
const pid = parseInt(readFileSync6(lp, "utf-8").trim(), 10);
|
|
2679
2746
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
@@ -2712,19 +2779,20 @@ function computeDaemonDrift(nodeBin) {
|
|
|
2712
2779
|
const p = plistPath();
|
|
2713
2780
|
const entry = daemonEntryPath();
|
|
2714
2781
|
const desired = renderPlist(nodeBin, entry, buildDaemonPath(process.env.PATH ?? ""));
|
|
2715
|
-
const current =
|
|
2782
|
+
const current = existsSync9(p) ? readFileSync6(p, "utf-8") : null;
|
|
2716
2783
|
const uid = process.getuid?.() ?? 0;
|
|
2717
2784
|
const target = `gui/${uid}/${LABEL}`;
|
|
2718
2785
|
const changed = current !== desired;
|
|
2719
2786
|
const programChanged = current !== null && changed && !current.includes(programArgsBlock(nodeBin, entry));
|
|
2720
2787
|
const parsedCurrent = current !== null ? parseProgramArgs(current) : null;
|
|
2721
|
-
const foreignInstall = detectForeignInstall(parsedCurrent, entry, parsedCurrent !== null &&
|
|
2788
|
+
const foreignInstall = detectForeignInstall(parsedCurrent, entry, parsedCurrent !== null && existsSync9(parsedCurrent.daemonEntry));
|
|
2722
2789
|
return { plistPath: p, target, desired, current, changed, programChanged, foreignInstall };
|
|
2723
2790
|
}
|
|
2724
2791
|
function reconcilePlistAndService(drift) {
|
|
2725
2792
|
if (drift.changed) {
|
|
2726
|
-
|
|
2727
|
-
|
|
2793
|
+
mkdirSync5(dirname3(drift.plistPath), { recursive: true });
|
|
2794
|
+
mkdirSync5(logsRoot(), { recursive: true });
|
|
2795
|
+
writeFileSync6(drift.plistPath, drift.desired);
|
|
2728
2796
|
}
|
|
2729
2797
|
if (drift.programChanged) {
|
|
2730
2798
|
try {
|
|
@@ -2742,6 +2810,17 @@ function applyDaemonDrift(drift) {
|
|
|
2742
2810
|
reconcilePlistAndService(drift);
|
|
2743
2811
|
execFileSync3("launchctl", ["kickstart", drift.target], { stdio: "ignore" });
|
|
2744
2812
|
}
|
|
2813
|
+
function parseLaunchctlStderrPath(printOutput) {
|
|
2814
|
+
const m = printOutput.match(/^\s*stderr path = (.+)$/m);
|
|
2815
|
+
return m ? m[1].trim() : null;
|
|
2816
|
+
}
|
|
2817
|
+
function loadedDaemonLogPath(target) {
|
|
2818
|
+
try {
|
|
2819
|
+
return parseLaunchctlStderrPath(execFileSync3("launchctl", ["print", target], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }));
|
|
2820
|
+
} catch {
|
|
2821
|
+
return null;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2745
2824
|
function getDaemonPid(target) {
|
|
2746
2825
|
try {
|
|
2747
2826
|
const out = execFileSync3("launchctl", ["print", target], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -2856,7 +2935,9 @@ function reregisterDaemon(nodeBin = process.execPath, kickstartOpts = {}) {
|
|
|
2856
2935
|
try {
|
|
2857
2936
|
const drift = computeDaemonDrift(nodeBin);
|
|
2858
2937
|
const pidBeforeReconcile = getDaemonPid(drift.target);
|
|
2859
|
-
|
|
2938
|
+
const loadedLog = loadedDaemonLogPath(drift.target);
|
|
2939
|
+
const logMoved = loadedLog !== null && loadedLog !== daemonLogPath();
|
|
2940
|
+
reconcilePlistAndService({ ...drift, programChanged: drift.programChanged || logMoved });
|
|
2860
2941
|
const result = forceKickstartAndVerify(drift.target, { ...kickstartOpts, pidBefore: pidBeforeReconcile });
|
|
2861
2942
|
if (!result.restarted) {
|
|
2862
2943
|
throw new Error(`daemon did not restart (pid before=${result.pidBefore ?? "none"}, after=${result.pidAfter ?? "none"}) \u2014 \`launchctl kickstart -k ${drift.target}\` ran but the pid never changed`);
|
|
@@ -2869,6 +2950,7 @@ function reregisterDaemon(nodeBin = process.execPath, kickstartOpts = {}) {
|
|
|
2869
2950
|
var LABEL, AGENT_BINS, restartInFlight, OPERATOR_INITIATED_COMMANDS, READ_ONLY_CREW_SUBCOMMANDS, READ_ONLY_TOP_LEVEL_COMMANDS;
|
|
2870
2951
|
var init_launchd = __esm({
|
|
2871
2952
|
"packages/core/dist/launchd.js"() {
|
|
2953
|
+
init_dist();
|
|
2872
2954
|
LABEL = "com.squadrant.daemon";
|
|
2873
2955
|
AGENT_BINS = ["cmux", "claude", "opencode", "codex", "gemini", "node"];
|
|
2874
2956
|
restartInFlight = false;
|
|
@@ -3087,7 +3169,7 @@ var init_gate = __esm({
|
|
|
3087
3169
|
});
|
|
3088
3170
|
|
|
3089
3171
|
// packages/core/dist/daemon/liveness-registry.js
|
|
3090
|
-
import { writeFileSync as
|
|
3172
|
+
import { writeFileSync as writeFileSync7, readFileSync as readFileSync7, renameSync as renameSync3 } from "fs";
|
|
3091
3173
|
var LivenessRegistry;
|
|
3092
3174
|
var init_liveness_registry = __esm({
|
|
3093
3175
|
"packages/core/dist/daemon/liveness-registry.js"() {
|
|
@@ -3107,7 +3189,7 @@ var init_liveness_registry = __esm({
|
|
|
3107
3189
|
}
|
|
3108
3190
|
});
|
|
3109
3191
|
this.writeFile = opts.writeFile ?? ((p, c) => {
|
|
3110
|
-
|
|
3192
|
+
writeFileSync7(`${p}.tmp`, c);
|
|
3111
3193
|
renameSync3(`${p}.tmp`, p);
|
|
3112
3194
|
});
|
|
3113
3195
|
}
|
|
@@ -3157,10 +3239,9 @@ var init_liveness_registry = __esm({
|
|
|
3157
3239
|
});
|
|
3158
3240
|
|
|
3159
3241
|
// packages/core/dist/daemon/context.js
|
|
3160
|
-
import {
|
|
3161
|
-
import { join as join9 } from "path";
|
|
3242
|
+
import { join as join10 } from "path";
|
|
3162
3243
|
import { spawn as realSpawn } from "child_process";
|
|
3163
|
-
import { writeFileSync as
|
|
3244
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
3164
3245
|
function defaultIsPidAlive(pid) {
|
|
3165
3246
|
try {
|
|
3166
3247
|
process.kill(pid, 0);
|
|
@@ -3170,27 +3251,27 @@ function defaultIsPidAlive(pid) {
|
|
|
3170
3251
|
}
|
|
3171
3252
|
}
|
|
3172
3253
|
function buildContext(opts) {
|
|
3173
|
-
const
|
|
3254
|
+
const stateRoot2 = opts.stateRoot ?? stateRoot();
|
|
3174
3255
|
const sockPath = opts.sockPath ?? DAEMON_SOCK_PATH;
|
|
3175
|
-
const store = createStore(
|
|
3256
|
+
const store = createStore(stateRoot2);
|
|
3176
3257
|
const bootedAt = Date.now();
|
|
3177
3258
|
const config = loadConfig();
|
|
3178
3259
|
const taskTimeoutMs = config.defaults.taskTimeoutMs;
|
|
3179
3260
|
const takeoverNudgeHours = config.defaults.takeoverNudgeHours;
|
|
3180
3261
|
const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
3181
3262
|
const spawn2 = opts.spawn ?? realSpawn;
|
|
3182
|
-
const resultsDir =
|
|
3183
|
-
|
|
3263
|
+
const resultsDir = join10(stateRoot2, "_results");
|
|
3264
|
+
mkdirSync6(resultsDir, { recursive: true });
|
|
3184
3265
|
const writeResult = (id, payload) => {
|
|
3185
|
-
const p =
|
|
3186
|
-
|
|
3266
|
+
const p = join10(resultsDir, `${id}.txt`);
|
|
3267
|
+
writeFileSync8(p, payload);
|
|
3187
3268
|
return p;
|
|
3188
3269
|
};
|
|
3189
3270
|
const log = (m) => process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${m}
|
|
3190
3271
|
`);
|
|
3191
3272
|
return {
|
|
3192
3273
|
opts,
|
|
3193
|
-
stateRoot,
|
|
3274
|
+
stateRoot: stateRoot2,
|
|
3194
3275
|
sockPath,
|
|
3195
3276
|
store,
|
|
3196
3277
|
bootedAt,
|
|
@@ -3206,7 +3287,7 @@ function buildContext(opts) {
|
|
|
3206
3287
|
inFlightHeadlessIds: /* @__PURE__ */ new Set(),
|
|
3207
3288
|
activeHeadlessKills: /* @__PURE__ */ new Set(),
|
|
3208
3289
|
livenessRegistry: (() => {
|
|
3209
|
-
const r = new LivenessRegistry({ path:
|
|
3290
|
+
const r = new LivenessRegistry({ path: join10(stateRoot2, "liveness.json") });
|
|
3210
3291
|
r.load();
|
|
3211
3292
|
return r;
|
|
3212
3293
|
})(),
|
|
@@ -3874,8 +3955,8 @@ var init_crew_routing = __esm({
|
|
|
3874
3955
|
|
|
3875
3956
|
// packages/core/dist/permission-gate.js
|
|
3876
3957
|
import { createHash } from "crypto";
|
|
3877
|
-
import { mkdirSync as
|
|
3878
|
-
import { dirname as
|
|
3958
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
|
|
3959
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
3879
3960
|
function isGateSession(env) {
|
|
3880
3961
|
return Boolean(env.SQUADRANT_CREW_TASK_ID) || env[SIDE_SESSION_ENV] === "1" || env.SQUADRANT_ROLE === "captain";
|
|
3881
3962
|
}
|
|
@@ -4097,7 +4178,7 @@ async function callClassifier(o) {
|
|
|
4097
4178
|
}
|
|
4098
4179
|
}
|
|
4099
4180
|
function defaultGateCachePath() {
|
|
4100
|
-
return
|
|
4181
|
+
return join11(CONFIG_DIR, "state", "gate-cache.json");
|
|
4101
4182
|
}
|
|
4102
4183
|
function gateCacheKey(toolName, cwd, toolPayload) {
|
|
4103
4184
|
return createHash("sha256").update(`${toolName}
|
|
@@ -4261,8 +4342,8 @@ var init_permission_gate = __esm({
|
|
|
4261
4342
|
const next = Object.fromEntries(entries);
|
|
4262
4343
|
const tmp = `${this.path}.${process.pid}.${Date.now()}.tmp`;
|
|
4263
4344
|
try {
|
|
4264
|
-
|
|
4265
|
-
|
|
4345
|
+
mkdirSync7(dirname4(this.path), { recursive: true });
|
|
4346
|
+
writeFileSync9(tmp, JSON.stringify(next), "utf-8");
|
|
4266
4347
|
renameSync4(tmp, this.path);
|
|
4267
4348
|
} catch {
|
|
4268
4349
|
try {
|
|
@@ -4356,14 +4437,14 @@ var init_auto_gate = __esm({
|
|
|
4356
4437
|
});
|
|
4357
4438
|
|
|
4358
4439
|
// packages/core/dist/first-turn-spill.js
|
|
4359
|
-
import
|
|
4360
|
-
import
|
|
4361
|
-
import
|
|
4440
|
+
import fs10 from "fs";
|
|
4441
|
+
import os5 from "os";
|
|
4442
|
+
import path11 from "path";
|
|
4362
4443
|
function spillOversizedFirstTurn(text, id) {
|
|
4363
4444
|
if (Buffer.byteLength(text, "utf8") <= FIRST_TURN_INLINE_MAX_BYTES)
|
|
4364
4445
|
return text;
|
|
4365
|
-
const spillFile =
|
|
4366
|
-
|
|
4446
|
+
const spillFile = path11.join(os5.tmpdir(), `squadrant-task-${id}.md`);
|
|
4447
|
+
fs10.writeFileSync(spillFile, text, "utf8");
|
|
4367
4448
|
return `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
4368
4449
|
}
|
|
4369
4450
|
var FIRST_TURN_INLINE_MAX_BYTES;
|
|
@@ -4435,8 +4516,8 @@ import { exec as nodeExec } from "child_process";
|
|
|
4435
4516
|
async function reapCrewChildren(taskId, graceMs = 2e3, execFn = nodeExec) {
|
|
4436
4517
|
const marker = `SQUADRANT_CREW_TASK_ID=${taskId}`;
|
|
4437
4518
|
try {
|
|
4438
|
-
const stdout = await new Promise((
|
|
4439
|
-
execFn("ps auxE", { maxBuffer: 64 * 1024 * 1024 }, (err, out) => err ? reject(err) :
|
|
4519
|
+
const stdout = await new Promise((resolve5, reject) => {
|
|
4520
|
+
execFn("ps auxE", { maxBuffer: 64 * 1024 * 1024 }, (err, out) => err ? reject(err) : resolve5(out));
|
|
4440
4521
|
});
|
|
4441
4522
|
const pids = [];
|
|
4442
4523
|
for (const line2 of stdout.split("\n").slice(1)) {
|
|
@@ -4470,14 +4551,14 @@ var init_crew_lifecycle = __esm({
|
|
|
4470
4551
|
});
|
|
4471
4552
|
|
|
4472
4553
|
// packages/core/dist/crew-spawn.js
|
|
4473
|
-
import
|
|
4474
|
-
import
|
|
4475
|
-
import
|
|
4554
|
+
import fs11 from "fs";
|
|
4555
|
+
import os6 from "os";
|
|
4556
|
+
import path12 from "path";
|
|
4476
4557
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4477
4558
|
function ensureSocksDir(dir = CC_SOCKS_DIR) {
|
|
4478
|
-
|
|
4479
|
-
if ((
|
|
4480
|
-
|
|
4559
|
+
fs11.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4560
|
+
if ((fs11.statSync(dir).mode & 511) !== 448)
|
|
4561
|
+
fs11.chmodSync(dir, 448);
|
|
4481
4562
|
}
|
|
4482
4563
|
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id, scrapeSettled, cancel) {
|
|
4483
4564
|
let scrapeSettledAt;
|
|
@@ -4499,14 +4580,14 @@ async function pollFirstTurnConfirmedAt(getTaskRecord, project, id, scrapeSettle
|
|
|
4499
4580
|
}
|
|
4500
4581
|
}
|
|
4501
4582
|
function firstTrueOrBothFalse(a, b, deadlineMs) {
|
|
4502
|
-
return new Promise((
|
|
4583
|
+
return new Promise((resolve5) => {
|
|
4503
4584
|
let done = false;
|
|
4504
4585
|
const finish = (ok2) => {
|
|
4505
4586
|
if (done)
|
|
4506
4587
|
return;
|
|
4507
4588
|
done = true;
|
|
4508
4589
|
clearTimeout(timer);
|
|
4509
|
-
|
|
4590
|
+
resolve5(ok2);
|
|
4510
4591
|
};
|
|
4511
4592
|
const timer = setTimeout(() => finish(false), deadlineMs);
|
|
4512
4593
|
let settledFalseCount = 0;
|
|
@@ -4610,9 +4691,9 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4610
4691
|
}) : proj.path;
|
|
4611
4692
|
let firstTurnTask = input.task;
|
|
4612
4693
|
if (input.taskFile && input.taskFile !== "-" && !input.shared) {
|
|
4613
|
-
const absTaskFile =
|
|
4614
|
-
const basename =
|
|
4615
|
-
|
|
4694
|
+
const absTaskFile = path12.resolve(input.taskFile);
|
|
4695
|
+
const basename = path12.basename(absTaskFile);
|
|
4696
|
+
fs11.copyFileSync(absTaskFile, path12.join(spawnCwd, basename));
|
|
4616
4697
|
firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
|
|
4617
4698
|
}
|
|
4618
4699
|
const agentName = requestedAgent;
|
|
@@ -4626,8 +4707,8 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4626
4707
|
assertBackendUsable({ backend, agent: agent.name, router: config.defaults.router });
|
|
4627
4708
|
deps.onBackendResolved?.({ backend });
|
|
4628
4709
|
if (agentName === "codex") {
|
|
4629
|
-
const codexRoleFile =
|
|
4630
|
-
const roleInstructions =
|
|
4710
|
+
const codexRoleFile = path12.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
|
|
4711
|
+
const roleInstructions = fs11.existsSync(codexRoleFile) ? fs11.readFileSync(codexRoleFile, "utf8") : void 0;
|
|
4631
4712
|
return runCodexInteractiveSpawn({
|
|
4632
4713
|
project: input.project,
|
|
4633
4714
|
task: input.task,
|
|
@@ -4643,7 +4724,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4643
4724
|
sendCodexFirstTurn: deps.sendCodexFirstTurn
|
|
4644
4725
|
});
|
|
4645
4726
|
}
|
|
4646
|
-
const promptFile =
|
|
4727
|
+
const promptFile = path12.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
|
|
4647
4728
|
const interactive = agent.name === "claude" || agent.name === "opencode";
|
|
4648
4729
|
const crewRole = config.defaults.roles?.crew;
|
|
4649
4730
|
const configModel = crewRole && crewRole.agent === agent.name ? crewRole.model : void 0;
|
|
@@ -4669,7 +4750,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4669
4750
|
}
|
|
4670
4751
|
}
|
|
4671
4752
|
ensureSocksDir();
|
|
4672
|
-
const messagingSocketPath =
|
|
4753
|
+
const messagingSocketPath = path12.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
|
|
4673
4754
|
const rec = await deps.dispatchCrew({
|
|
4674
4755
|
provider: "claude",
|
|
4675
4756
|
mode: "interactive",
|
|
@@ -4680,7 +4761,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4680
4761
|
messagingSocketPath
|
|
4681
4762
|
});
|
|
4682
4763
|
const routerSettingsPath = Object.keys(routerEnv).length > 0 ? deps.writeRouterSettings({
|
|
4683
|
-
stateRoot:
|
|
4764
|
+
stateRoot: stateRoot(),
|
|
4684
4765
|
project: input.project,
|
|
4685
4766
|
taskId: rec.id,
|
|
4686
4767
|
env: mergeClaudeEnvRouterSettings(routerEnv, config.defaults.claudeEnv)
|
|
@@ -4777,7 +4858,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen) : Promise.r
|
|
|
4777
4858
|
`);
|
|
4778
4859
|
}
|
|
4779
4860
|
const opencodeConfigPath = deps.writeOpencodeConfig({
|
|
4780
|
-
stateRoot:
|
|
4861
|
+
stateRoot: stateRoot(),
|
|
4781
4862
|
project: input.project,
|
|
4782
4863
|
taskId: rec.id,
|
|
4783
4864
|
// CP3 opt-in: --approval gates bash so the captain approves shell commands.
|
|
@@ -4947,7 +5028,7 @@ function buildRecoveryHint(sessId, provider, worktreeCwd) {
|
|
|
4947
5028
|
return "";
|
|
4948
5029
|
if (provider === "claude") {
|
|
4949
5030
|
const escaped = worktreeCwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4950
|
-
const transcriptPath =
|
|
5031
|
+
const transcriptPath = path12.join(os6.homedir(), ".claude", "projects", escaped, `${sessId}.jsonl`);
|
|
4951
5032
|
return `
|
|
4952
5033
|
transcript: ${transcriptPath}
|
|
4953
5034
|
resume: claude --resume ${sessId} (run from the worktree path above)
|
|
@@ -5042,7 +5123,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
5042
5123
|
surfaceId: c.surfaceId
|
|
5043
5124
|
}));
|
|
5044
5125
|
}
|
|
5045
|
-
var CC_SOCKS_DIR, TEMPLATES_DIR,
|
|
5126
|
+
var CC_SOCKS_DIR, TEMPLATES_DIR, FIRST_TURN_HOOK_CONFIRM_GRACE_MS, FIRST_TURN_HOOK_CONFIRM_MAX_MS, FIRST_TURN_HOOK_POLL_INTERVAL_MS, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
5046
5127
|
var init_crew_spawn = __esm({
|
|
5047
5128
|
"packages/core/dist/crew-spawn.js"() {
|
|
5048
5129
|
init_control_channel();
|
|
@@ -5056,8 +5137,7 @@ var init_crew_spawn = __esm({
|
|
|
5056
5137
|
init_crew_protocol();
|
|
5057
5138
|
init_crew_lifecycle();
|
|
5058
5139
|
CC_SOCKS_DIR = "/tmp/cc-socks";
|
|
5059
|
-
TEMPLATES_DIR =
|
|
5060
|
-
STATE_ROOT = path11.join(os5.homedir(), ".config", "squadrant", "state");
|
|
5140
|
+
TEMPLATES_DIR = path12.join(os6.homedir(), ".config", "squadrant", "templates");
|
|
5061
5141
|
FIRST_TURN_HOOK_CONFIRM_GRACE_MS = 15e3;
|
|
5062
5142
|
FIRST_TURN_HOOK_CONFIRM_MAX_MS = 18e4;
|
|
5063
5143
|
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
@@ -5067,12 +5147,12 @@ var init_crew_spawn = __esm({
|
|
|
5067
5147
|
});
|
|
5068
5148
|
|
|
5069
5149
|
// packages/core/dist/captain-channel.js
|
|
5070
|
-
import { join as
|
|
5150
|
+
import { join as join12 } from "path";
|
|
5071
5151
|
function captainSocketPath(project, socksDir = CC_SOCKS_DIR) {
|
|
5072
5152
|
if (!/^[A-Za-z0-9._-]+$/.test(project)) {
|
|
5073
5153
|
throw new Error(`captain-channel: project name '${project}' is not a safe socket filename`);
|
|
5074
5154
|
}
|
|
5075
|
-
return
|
|
5155
|
+
return join12(socksDir, `squadrant-captain-${project}.sock`);
|
|
5076
5156
|
}
|
|
5077
5157
|
async function deliverToCaptain(project, text, deps) {
|
|
5078
5158
|
if (deps.mode === "off")
|
|
@@ -5102,23 +5182,23 @@ var init_captain_channel = __esm({
|
|
|
5102
5182
|
});
|
|
5103
5183
|
|
|
5104
5184
|
// packages/core/dist/captain-record.js
|
|
5105
|
-
import { mkdirSync as
|
|
5106
|
-
import { dirname as
|
|
5107
|
-
function captainRecordPath(
|
|
5108
|
-
return
|
|
5185
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync9, realpathSync, writeFileSync as writeFileSync10 } from "fs";
|
|
5186
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
5187
|
+
function captainRecordPath(stateRoot2, project) {
|
|
5188
|
+
return join13(stateRoot2, project, "captain.json");
|
|
5109
5189
|
}
|
|
5110
|
-
function readCaptainAddress(
|
|
5190
|
+
function readCaptainAddress(stateRoot2, project) {
|
|
5111
5191
|
try {
|
|
5112
|
-
const parsed = JSON.parse(readFileSync9(captainRecordPath(
|
|
5192
|
+
const parsed = JSON.parse(readFileSync9(captainRecordPath(stateRoot2, project), "utf-8"));
|
|
5113
5193
|
return parsed && typeof parsed.agent === "string" ? parsed : null;
|
|
5114
5194
|
} catch {
|
|
5115
5195
|
return null;
|
|
5116
5196
|
}
|
|
5117
5197
|
}
|
|
5118
|
-
function writeCaptainAddress(
|
|
5119
|
-
const p = captainRecordPath(
|
|
5120
|
-
|
|
5121
|
-
|
|
5198
|
+
function writeCaptainAddress(stateRoot2, project, addr) {
|
|
5199
|
+
const p = captainRecordPath(stateRoot2, project);
|
|
5200
|
+
mkdirSync8(dirname5(p), { recursive: true });
|
|
5201
|
+
writeFileSync10(p, JSON.stringify(addr, null, 2) + "\n");
|
|
5122
5202
|
}
|
|
5123
5203
|
function realpathOrSelf(p) {
|
|
5124
5204
|
try {
|
|
@@ -5141,11 +5221,11 @@ var init_captain_record = __esm({
|
|
|
5141
5221
|
// packages/core/dist/opencode-session.js
|
|
5142
5222
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5143
5223
|
async function listSessions(port, fetchImpl = fetch, timeoutMs = 5e3) {
|
|
5144
|
-
for (const
|
|
5224
|
+
for (const path44 of ["/session", "/api/session"]) {
|
|
5145
5225
|
const ac = new AbortController();
|
|
5146
5226
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
5147
5227
|
try {
|
|
5148
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
5228
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path44}`, { signal: ac.signal });
|
|
5149
5229
|
if (!res.ok)
|
|
5150
5230
|
continue;
|
|
5151
5231
|
const rows = await res.json();
|
|
@@ -5276,19 +5356,19 @@ async function reresolveOpencodeAddress(rec) {
|
|
|
5276
5356
|
return null;
|
|
5277
5357
|
return { port, sessionId };
|
|
5278
5358
|
}
|
|
5279
|
-
async function healStaleOpencodeAddress(
|
|
5280
|
-
const rec = readCaptainAddress(
|
|
5359
|
+
async function healStaleOpencodeAddress(stateRoot2, project, log, logPrefix) {
|
|
5360
|
+
const rec = readCaptainAddress(stateRoot2, project);
|
|
5281
5361
|
if (!rec)
|
|
5282
5362
|
return false;
|
|
5283
5363
|
const resolved = await reresolveOpencodeAddress(rec);
|
|
5284
5364
|
if (!resolved)
|
|
5285
5365
|
return false;
|
|
5286
|
-
const current = readCaptainAddress(
|
|
5366
|
+
const current = readCaptainAddress(stateRoot2, project);
|
|
5287
5367
|
if (!current || current.launchedAt !== rec.launchedAt) {
|
|
5288
5368
|
log(`captain-channel ${project}: skipped stale-address heal \u2014 a newer launch record won the race`);
|
|
5289
5369
|
return false;
|
|
5290
5370
|
}
|
|
5291
|
-
writeCaptainAddress(
|
|
5371
|
+
writeCaptainAddress(stateRoot2, project, { ...current, port: resolved.port, sessionId: resolved.sessionId });
|
|
5292
5372
|
log(`captain-channel ${project}: ${logPrefix} \u2192 port ${resolved.port}`);
|
|
5293
5373
|
return true;
|
|
5294
5374
|
}
|
|
@@ -5384,7 +5464,7 @@ async function runLivenessTick(deps) {
|
|
|
5384
5464
|
}
|
|
5385
5465
|
}
|
|
5386
5466
|
function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
5387
|
-
const { stateRoot, store, log, livenessRegistry, isPidAlive, opts, telegramBridge } = ctx;
|
|
5467
|
+
const { stateRoot: stateRoot2, store, log, livenessRegistry, isPidAlive, opts, telegramBridge } = ctx;
|
|
5388
5468
|
const surfaceProbe = isSurfaceAlive ?? (async () => "unknown");
|
|
5389
5469
|
const notifyFault = ctx.notifyFault ?? (() => {
|
|
5390
5470
|
});
|
|
@@ -5395,7 +5475,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5395
5475
|
}
|
|
5396
5476
|
try {
|
|
5397
5477
|
await appendToMailbox({
|
|
5398
|
-
stateRoot,
|
|
5478
|
+
stateRoot: stateRoot2,
|
|
5399
5479
|
project: args.project,
|
|
5400
5480
|
taskRecord: args.record,
|
|
5401
5481
|
event: args.event,
|
|
@@ -5474,7 +5554,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5474
5554
|
surface = await resolveCaptainSurface();
|
|
5475
5555
|
if (!surface)
|
|
5476
5556
|
continue;
|
|
5477
|
-
const cursor = await readCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER });
|
|
5557
|
+
const cursor = await readCursor({ stateRoot: stateRoot2, project, subscriber: CURSOR_SUBSCRIBER });
|
|
5478
5558
|
const lastAcked = cursor?.lastAckedSeq ?? 0;
|
|
5479
5559
|
let d = deliveries.get(project);
|
|
5480
5560
|
if (!d) {
|
|
@@ -5485,7 +5565,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5485
5565
|
deliveries.set(project, d);
|
|
5486
5566
|
}
|
|
5487
5567
|
const pending = [];
|
|
5488
|
-
for await (const entry of readFromCursor({ stateRoot, project, fromSeq: lastAcked + 1 }))
|
|
5568
|
+
for await (const entry of readFromCursor({ stateRoot: stateRoot2, project, fromSeq: lastAcked + 1 }))
|
|
5489
5569
|
pending.push(entry);
|
|
5490
5570
|
const decisions = coalesceBacklog(pending, {
|
|
5491
5571
|
now: Date.now(),
|
|
@@ -5498,7 +5578,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5498
5578
|
const isExemptMessage = entry.kind === "captain.message" && entry.payload?.source !== "daemon";
|
|
5499
5579
|
if (!isExemptMessage) {
|
|
5500
5580
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-skipped`);
|
|
5501
|
-
await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5581
|
+
await writeCursor({ stateRoot: stateRoot2, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5502
5582
|
continue;
|
|
5503
5583
|
}
|
|
5504
5584
|
if (logStale)
|
|
@@ -5511,7 +5591,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5511
5591
|
const decision = decisions[i];
|
|
5512
5592
|
if (decision.action === "skip") {
|
|
5513
5593
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=coalesced reason=${decision.reason}`);
|
|
5514
|
-
await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5594
|
+
await writeCursor({ stateRoot: stateRoot2, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5515
5595
|
continue;
|
|
5516
5596
|
}
|
|
5517
5597
|
const result = await d.deliver(decision.entry, async (text, sendOpts) => {
|
|
@@ -5527,7 +5607,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5527
5607
|
log(`captain-channel ${project}: threw, falling back to pane \u2014 ${e.message}`);
|
|
5528
5608
|
}
|
|
5529
5609
|
if (agent === "opencode" && mode !== "off") {
|
|
5530
|
-
const healed = await healStaleOpencodeAddress(
|
|
5610
|
+
const healed = await healStaleOpencodeAddress(stateRoot2, project, log, "re-resolved opencode address");
|
|
5531
5611
|
if (healed) {
|
|
5532
5612
|
try {
|
|
5533
5613
|
const retry = await deliverToCaptain(project, text, { channel: picked, mode, log });
|
|
@@ -5564,7 +5644,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5564
5644
|
});
|
|
5565
5645
|
if ("delivered" in result) {
|
|
5566
5646
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=delivered`);
|
|
5567
|
-
await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5647
|
+
await writeCursor({ stateRoot: stateRoot2, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
5568
5648
|
lastDeferred.delete(project);
|
|
5569
5649
|
projectBackoff.delete(project);
|
|
5570
5650
|
} else {
|
|
@@ -5591,7 +5671,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5591
5671
|
log(`delivery stuck project=${project} deferCount=${maxDeferCount} reason=${reason ?? "unknown"}`);
|
|
5592
5672
|
const text = STUCK_ALERT_TEXT[reason ?? "unknown"](maxDeferCount);
|
|
5593
5673
|
try {
|
|
5594
|
-
await appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
|
|
5674
|
+
await appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" });
|
|
5595
5675
|
} catch (e) {
|
|
5596
5676
|
log(`delivery stuck alert failed project=${project}: ${e.message}`);
|
|
5597
5677
|
}
|
|
@@ -5766,21 +5846,21 @@ var init_server = __esm({
|
|
|
5766
5846
|
});
|
|
5767
5847
|
|
|
5768
5848
|
// packages/core/dist/daemon/exit-marker.js
|
|
5769
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
5770
|
-
import { join as
|
|
5771
|
-
function exitMarkerPath(
|
|
5772
|
-
return
|
|
5849
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync11, unlinkSync as unlinkSync4, existsSync as existsSync10 } from "fs";
|
|
5850
|
+
import { join as join14 } from "path";
|
|
5851
|
+
function exitMarkerPath(stateRoot2) {
|
|
5852
|
+
return join14(stateRoot2, "exit-marker.json");
|
|
5773
5853
|
}
|
|
5774
|
-
function writeExitMarker(
|
|
5854
|
+
function writeExitMarker(stateRoot2, marker, log) {
|
|
5775
5855
|
try {
|
|
5776
|
-
|
|
5856
|
+
writeFileSync11(exitMarkerPath(stateRoot2), JSON.stringify(marker));
|
|
5777
5857
|
} catch (e) {
|
|
5778
5858
|
log(`exit marker write failed: ${e.message}`);
|
|
5779
5859
|
}
|
|
5780
5860
|
}
|
|
5781
|
-
function consumeExitMarker(
|
|
5782
|
-
const p = exitMarkerPath(
|
|
5783
|
-
if (!
|
|
5861
|
+
function consumeExitMarker(stateRoot2, now = Date.now) {
|
|
5862
|
+
const p = exitMarkerPath(stateRoot2);
|
|
5863
|
+
if (!existsSync10(p))
|
|
5784
5864
|
return { marker: null };
|
|
5785
5865
|
let marker = null;
|
|
5786
5866
|
try {
|
|
@@ -5797,26 +5877,26 @@ function consumeExitMarker(stateRoot, now = Date.now) {
|
|
|
5797
5877
|
const gapMs = Math.max(0, now() - new Date(marker.ts).getTime());
|
|
5798
5878
|
return { marker, gapMs };
|
|
5799
5879
|
}
|
|
5800
|
-
function runningMarkerPath(
|
|
5801
|
-
return
|
|
5880
|
+
function runningMarkerPath(stateRoot2) {
|
|
5881
|
+
return join14(stateRoot2, "running-marker.json");
|
|
5802
5882
|
}
|
|
5803
|
-
function writeRunningMarker(
|
|
5883
|
+
function writeRunningMarker(stateRoot2, marker, log) {
|
|
5804
5884
|
try {
|
|
5805
|
-
|
|
5885
|
+
writeFileSync11(runningMarkerPath(stateRoot2), JSON.stringify(marker));
|
|
5806
5886
|
} catch (e) {
|
|
5807
5887
|
log(`running marker write failed: ${e.message}`);
|
|
5808
5888
|
}
|
|
5809
5889
|
}
|
|
5810
|
-
function readRunningMarker(
|
|
5890
|
+
function readRunningMarker(stateRoot2) {
|
|
5811
5891
|
try {
|
|
5812
|
-
return JSON.parse(readFileSync10(runningMarkerPath(
|
|
5892
|
+
return JSON.parse(readFileSync10(runningMarkerPath(stateRoot2), "utf-8"));
|
|
5813
5893
|
} catch {
|
|
5814
5894
|
return null;
|
|
5815
5895
|
}
|
|
5816
5896
|
}
|
|
5817
|
-
function removeRunningMarker(
|
|
5897
|
+
function removeRunningMarker(stateRoot2, log) {
|
|
5818
5898
|
try {
|
|
5819
|
-
unlinkSync4(runningMarkerPath(
|
|
5899
|
+
unlinkSync4(runningMarkerPath(stateRoot2));
|
|
5820
5900
|
} catch (e) {
|
|
5821
5901
|
if (e.code !== "ENOENT")
|
|
5822
5902
|
log(`running marker remove failed: ${e.message}`);
|
|
@@ -5829,8 +5909,8 @@ var init_exit_marker = __esm({
|
|
|
5829
5909
|
|
|
5830
5910
|
// packages/core/dist/daemon/snapshot-gather.js
|
|
5831
5911
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5832
|
-
import { join as
|
|
5833
|
-
import { statSync as statSync3, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as
|
|
5912
|
+
import { join as join15 } from "path";
|
|
5913
|
+
import { statSync as statSync3, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
5834
5914
|
function distBuiltAt() {
|
|
5835
5915
|
try {
|
|
5836
5916
|
return statSync3(SELF_PATH).mtimeMs;
|
|
@@ -5838,10 +5918,10 @@ function distBuiltAt() {
|
|
|
5838
5918
|
return 0;
|
|
5839
5919
|
}
|
|
5840
5920
|
}
|
|
5841
|
-
function gatherLogStats(
|
|
5921
|
+
function gatherLogStats(path44, now, windowMs) {
|
|
5842
5922
|
let sizeBytes = 0;
|
|
5843
5923
|
try {
|
|
5844
|
-
sizeBytes = statSync3(
|
|
5924
|
+
sizeBytes = statSync3(path44).size;
|
|
5845
5925
|
} catch {
|
|
5846
5926
|
return { errorCount: 0, sizeBytes: 0, windowMs };
|
|
5847
5927
|
}
|
|
@@ -5852,7 +5932,7 @@ function gatherLogStats(path43, now, windowMs) {
|
|
|
5852
5932
|
const len = sizeBytes - start;
|
|
5853
5933
|
let text = "";
|
|
5854
5934
|
try {
|
|
5855
|
-
const fd = openSync2(
|
|
5935
|
+
const fd = openSync2(path44, "r");
|
|
5856
5936
|
try {
|
|
5857
5937
|
const buf = Buffer.alloc(len);
|
|
5858
5938
|
readSync(fd, buf, 0, len, start);
|
|
@@ -5878,14 +5958,14 @@ function gatherLogStats(path43, now, windowMs) {
|
|
|
5878
5958
|
}
|
|
5879
5959
|
return { errorCount, sizeBytes, windowMs };
|
|
5880
5960
|
}
|
|
5881
|
-
function gatherStoreStats(store,
|
|
5961
|
+
function gatherStoreStats(store, stateRoot2, project) {
|
|
5882
5962
|
const byState = {};
|
|
5883
5963
|
for (const r of store.list(project))
|
|
5884
5964
|
byState[r.state] = (byState[r.state] ?? 0) + 1;
|
|
5885
5965
|
let corruptCount = 0;
|
|
5886
|
-
const dir =
|
|
5966
|
+
const dir = join15(stateRoot2, project);
|
|
5887
5967
|
try {
|
|
5888
|
-
for (const n of
|
|
5968
|
+
for (const n of readdirSync4(dir)) {
|
|
5889
5969
|
if (n.includes(".corrupt.")) {
|
|
5890
5970
|
corruptCount++;
|
|
5891
5971
|
continue;
|
|
@@ -5893,7 +5973,7 @@ function gatherStoreStats(store, stateRoot, project) {
|
|
|
5893
5973
|
if (!n.endsWith(".json"))
|
|
5894
5974
|
continue;
|
|
5895
5975
|
try {
|
|
5896
|
-
JSON.parse(readFileSync11(
|
|
5976
|
+
JSON.parse(readFileSync11(join15(dir, n), "utf-8"));
|
|
5897
5977
|
} catch {
|
|
5898
5978
|
corruptCount++;
|
|
5899
5979
|
}
|
|
@@ -5906,9 +5986,9 @@ function gatherResults(resultsDir) {
|
|
|
5906
5986
|
let fileCount = 0;
|
|
5907
5987
|
let totalBytes = 0;
|
|
5908
5988
|
try {
|
|
5909
|
-
for (const n of
|
|
5989
|
+
for (const n of readdirSync4(resultsDir)) {
|
|
5910
5990
|
try {
|
|
5911
|
-
const s = statSync3(
|
|
5991
|
+
const s = statSync3(join15(resultsDir, n));
|
|
5912
5992
|
if (s.isFile()) {
|
|
5913
5993
|
fileCount++;
|
|
5914
5994
|
totalBytes += s.size;
|
|
@@ -5928,10 +6008,10 @@ var init_snapshot_gather = __esm({
|
|
|
5928
6008
|
});
|
|
5929
6009
|
|
|
5930
6010
|
// packages/core/dist/daemon/start.js
|
|
5931
|
-
import { join as
|
|
6011
|
+
import { join as join16 } from "path";
|
|
5932
6012
|
import { readdir } from "fs/promises";
|
|
5933
6013
|
function startDaemon(ctx, opts, pkgVersion) {
|
|
5934
|
-
const { stateRoot, store, log, isPidAlive, resultsDir, taskTimeoutMs, inFlightHeadlessIds, activeHeadlessKills, broadcast, cancelPromotionsFor } = ctx;
|
|
6014
|
+
const { stateRoot: stateRoot2, store, log, isPidAlive, resultsDir, taskTimeoutMs, inFlightHeadlessIds, activeHeadlessKills, broadcast, cancelPromotionsFor } = ctx;
|
|
5935
6015
|
const { daemonCmux } = ctx;
|
|
5936
6016
|
const probes = createProbes(ctx);
|
|
5937
6017
|
const surfaceProbe = buildSurfaceProbe(ctx, probes, daemonCmux);
|
|
@@ -6005,14 +6085,14 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6005
6085
|
return out;
|
|
6006
6086
|
}
|
|
6007
6087
|
async function gatherSnapshotInputs(now) {
|
|
6008
|
-
const logPath2 =
|
|
6088
|
+
const logPath2 = stateRoot2 === stateRoot() ? resolveReadPath(daemonLogPath(), legacyDaemonLogPath()) : join16(stateRoot2, "logs", "squadrantd.log");
|
|
6009
6089
|
const tier2Projects = opts.registeredProjects ?? Object.keys(loadConfig().projects);
|
|
6010
6090
|
const projects = await Promise.all(tier2Projects.map(async (project) => {
|
|
6011
|
-
const cursor = await readCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER2 });
|
|
6012
|
-
const storeStats = gatherStoreStats(store,
|
|
6091
|
+
const cursor = await readCursor({ stateRoot: stateRoot2, project, subscriber: CURSOR_SUBSCRIBER2 });
|
|
6092
|
+
const storeStats = gatherStoreStats(store, stateRoot2, project);
|
|
6013
6093
|
return {
|
|
6014
6094
|
project,
|
|
6015
|
-
mailbox: await mailboxStats(
|
|
6095
|
+
mailbox: await mailboxStats(stateRoot2, project),
|
|
6016
6096
|
lastAckedSeq: cursor?.lastAckedSeq ?? 0,
|
|
6017
6097
|
storeByState: storeStats.byState,
|
|
6018
6098
|
corruptCount: storeStats.corruptCount,
|
|
@@ -6101,18 +6181,18 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6101
6181
|
}
|
|
6102
6182
|
})();
|
|
6103
6183
|
const server = createServer2(ctx, { buildHealth, gatherSnapshotInputs, cancelPromotionsFor, broadcast });
|
|
6104
|
-
log(`boot pid=${process.pid} version=${pkgVersion} socket=${ctx.sockPath} stateRoot=${
|
|
6184
|
+
log(`boot pid=${process.pid} version=${pkgVersion} socket=${ctx.sockPath} stateRoot=${stateRoot2}`);
|
|
6105
6185
|
const bootTs = (/* @__PURE__ */ new Date()).toISOString();
|
|
6106
6186
|
{
|
|
6107
6187
|
const sendDownAlert = (startTs, endTs, minutes, reasonText) => {
|
|
6108
6188
|
const text = formatDownAlertText({ startTs, endTs, minutes, reasonText });
|
|
6109
6189
|
const alertProjects = opts.registeredProjects ?? Object.keys(loadConfig().projects);
|
|
6110
6190
|
for (const project of alertProjects) {
|
|
6111
|
-
appendCaptainMessage({ stateRoot, project, text, source: "daemon" }).catch((e) => log(`boot-gap alert failed project=${project}: ${e.message}`));
|
|
6191
|
+
appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" }).catch((e) => log(`boot-gap alert failed project=${project}: ${e.message}`));
|
|
6112
6192
|
}
|
|
6113
6193
|
};
|
|
6114
|
-
const { marker, gapMs } = consumeExitMarker(
|
|
6115
|
-
const prevRunning = readRunningMarker(
|
|
6194
|
+
const { marker, gapMs } = consumeExitMarker(stateRoot2);
|
|
6195
|
+
const prevRunning = readRunningMarker(stateRoot2);
|
|
6116
6196
|
if (marker) {
|
|
6117
6197
|
log(`previous exit ts=${marker.ts} reason=${marker.reason} gap=${((gapMs ?? 0) / 1e3).toFixed(1)}s`);
|
|
6118
6198
|
if ((gapMs ?? 0) > 6e4)
|
|
@@ -6127,7 +6207,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6127
6207
|
} else {
|
|
6128
6208
|
log("previous exit: none (clean or first boot)");
|
|
6129
6209
|
}
|
|
6130
|
-
writeRunningMarker(
|
|
6210
|
+
writeRunningMarker(stateRoot2, { pid: process.pid, bootTs, lastHeartbeatTs: bootTs }, log);
|
|
6131
6211
|
}
|
|
6132
6212
|
let deliveryTick = initialDeliveryTick;
|
|
6133
6213
|
let probeTick;
|
|
@@ -6171,7 +6251,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6171
6251
|
let rotationTimer;
|
|
6172
6252
|
let rotationTick;
|
|
6173
6253
|
if (rotationInterval > 0) {
|
|
6174
|
-
const inboxPath =
|
|
6254
|
+
const inboxPath = join16(stateRoot2, "inbox");
|
|
6175
6255
|
rotationTick = async () => {
|
|
6176
6256
|
try {
|
|
6177
6257
|
let entries;
|
|
@@ -6182,12 +6262,12 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6182
6262
|
}
|
|
6183
6263
|
const projects = new Set(entries.filter((e) => e.endsWith(".log")).map((e) => e.slice(0, -".log".length)));
|
|
6184
6264
|
for (const project of projects)
|
|
6185
|
-
await rotateIfNeeded({ stateRoot, project, ...mboxCfg });
|
|
6265
|
+
await rotateIfNeeded({ stateRoot: stateRoot2, project, ...mboxCfg });
|
|
6186
6266
|
await opts.knowledgeTick?.();
|
|
6187
6267
|
} catch (e) {
|
|
6188
6268
|
log(`rotation timer error: ${e.message}`);
|
|
6189
6269
|
} finally {
|
|
6190
|
-
writeRunningMarker(
|
|
6270
|
+
writeRunningMarker(stateRoot2, { pid: process.pid, bootTs, lastHeartbeatTs: (/* @__PURE__ */ new Date()).toISOString() }, log);
|
|
6191
6271
|
}
|
|
6192
6272
|
};
|
|
6193
6273
|
rotationTimer = setInterval(() => {
|
|
@@ -6201,8 +6281,8 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6201
6281
|
const uptimeMs = Math.round(process.uptime() * 1e3);
|
|
6202
6282
|
const inFlight = inFlightDelivery();
|
|
6203
6283
|
log(`exit pid=${process.pid} reason=${reason} ppid=${ppid} launchd=${ppid === 1} uptimeMs=${uptimeMs} inFlightDelivery=${inFlight ? `${inFlight.project}#${inFlight.seq}(defers=${inFlight.deferCount})` : "none"}`);
|
|
6204
|
-
writeExitMarker(
|
|
6205
|
-
removeRunningMarker(
|
|
6284
|
+
writeExitMarker(stateRoot2, { ts: (/* @__PURE__ */ new Date()).toISOString(), pid: process.pid, reason, ppid, uptimeMs, inFlightDelivery: inFlight }, log);
|
|
6285
|
+
removeRunningMarker(stateRoot2, log);
|
|
6206
6286
|
if (deliveryTimer)
|
|
6207
6287
|
clearInterval(deliveryTimer);
|
|
6208
6288
|
if (probeTimer)
|
|
@@ -6229,9 +6309,9 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6229
6309
|
await ctx.routerService?.stop();
|
|
6230
6310
|
} catch {
|
|
6231
6311
|
}
|
|
6232
|
-
return new Promise((
|
|
6312
|
+
return new Promise((resolve5) => server.close(() => {
|
|
6233
6313
|
log(`exit-complete pid=${process.pid}`);
|
|
6234
|
-
|
|
6314
|
+
resolve5();
|
|
6235
6315
|
}));
|
|
6236
6316
|
},
|
|
6237
6317
|
tickDelivery: deliveryTick,
|
|
@@ -6260,11 +6340,12 @@ var init_start = __esm({
|
|
|
6260
6340
|
|
|
6261
6341
|
// packages/core/dist/session-freshness.js
|
|
6262
6342
|
import crypto from "crypto";
|
|
6263
|
-
import
|
|
6264
|
-
import
|
|
6265
|
-
function loadSessions(sessionsPath) {
|
|
6343
|
+
import fs12 from "fs";
|
|
6344
|
+
import path13 from "path";
|
|
6345
|
+
function loadSessions(sessionsPath, legacyPath) {
|
|
6266
6346
|
try {
|
|
6267
|
-
|
|
6347
|
+
const readPath = legacyPath ? resolveReadPath(sessionsPath, legacyPath) : sessionsPath;
|
|
6348
|
+
return JSON.parse(readConfigFileSync(readPath));
|
|
6268
6349
|
} catch {
|
|
6269
6350
|
return { workspaces: {} };
|
|
6270
6351
|
}
|
|
@@ -6274,26 +6355,26 @@ function saveSessions(sessionsPath, sessions) {
|
|
|
6274
6355
|
}
|
|
6275
6356
|
function computeTemplateHash(role, templatesDir) {
|
|
6276
6357
|
const hash = crypto.createHash("sha256");
|
|
6277
|
-
const roleFile =
|
|
6278
|
-
const legacyRoleFile =
|
|
6279
|
-
if (
|
|
6280
|
-
hash.update(
|
|
6281
|
-
} else if (
|
|
6282
|
-
hash.update(
|
|
6358
|
+
const roleFile = path13.join(templatesDir, `${role}.claude.md`);
|
|
6359
|
+
const legacyRoleFile = path13.join(templatesDir, `${role}.CLAUDE.md`);
|
|
6360
|
+
if (fs12.existsSync(roleFile)) {
|
|
6361
|
+
hash.update(fs12.readFileSync(roleFile, "utf-8"));
|
|
6362
|
+
} else if (fs12.existsSync(legacyRoleFile)) {
|
|
6363
|
+
hash.update(fs12.readFileSync(legacyRoleFile, "utf-8"));
|
|
6283
6364
|
}
|
|
6284
|
-
const pluginSkillsDir =
|
|
6285
|
-
if (
|
|
6286
|
-
for (const skill of
|
|
6287
|
-
const skillFile =
|
|
6288
|
-
if (
|
|
6289
|
-
hash.update(
|
|
6365
|
+
const pluginSkillsDir = path13.join(templatesDir, "..", "plugin", "skills");
|
|
6366
|
+
if (fs12.existsSync(pluginSkillsDir)) {
|
|
6367
|
+
for (const skill of fs12.readdirSync(pluginSkillsDir).sort()) {
|
|
6368
|
+
const skillFile = path13.join(pluginSkillsDir, skill, "SKILL.md");
|
|
6369
|
+
if (fs12.existsSync(skillFile)) {
|
|
6370
|
+
hash.update(fs12.readFileSync(skillFile, "utf-8"));
|
|
6290
6371
|
}
|
|
6291
6372
|
}
|
|
6292
6373
|
}
|
|
6293
6374
|
return hash.digest("hex").slice(0, 16);
|
|
6294
6375
|
}
|
|
6295
6376
|
function shouldStartFresh(workspaceName, role, opts) {
|
|
6296
|
-
const sessions = loadSessions(opts.sessionsPath);
|
|
6377
|
+
const sessions = loadSessions(opts.sessionsPath, opts.legacySessionsPath);
|
|
6297
6378
|
const record = sessions.workspaces[workspaceName];
|
|
6298
6379
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6299
6380
|
const currentHash = computeTemplateHash(role, opts.templatesDir);
|
|
@@ -6309,7 +6390,7 @@ function shouldStartFresh(workspaceName, role, opts) {
|
|
|
6309
6390
|
return { fresh: false };
|
|
6310
6391
|
}
|
|
6311
6392
|
function recordSession(workspaceName, role, opts) {
|
|
6312
|
-
const sessions = loadSessions(opts.sessionsPath);
|
|
6393
|
+
const sessions = loadSessions(opts.sessionsPath, opts.legacySessionsPath);
|
|
6313
6394
|
sessions.workspaces[workspaceName] = {
|
|
6314
6395
|
lastLaunched: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
6315
6396
|
templateHash: computeTemplateHash(role, opts.templatesDir)
|
|
@@ -6523,7 +6604,7 @@ function createIsCaptainAlive(sock) {
|
|
|
6523
6604
|
};
|
|
6524
6605
|
}
|
|
6525
6606
|
function createLaunch(cliBin, log) {
|
|
6526
|
-
return (project) => new Promise((
|
|
6607
|
+
return (project) => new Promise((resolve5, reject) => {
|
|
6527
6608
|
execFile(process.execPath, [cliBin, "launch", project, "--headless"], { timeout: 3e4 }, (err, stdout, stderr) => {
|
|
6528
6609
|
const output = capOutput(stdout ?? "", stderr ?? "");
|
|
6529
6610
|
if (err) {
|
|
@@ -6533,7 +6614,7 @@ function createLaunch(cliBin, log) {
|
|
|
6533
6614
|
}
|
|
6534
6615
|
if (output !== "(no output)")
|
|
6535
6616
|
log?.(`launch ${project}: ${output}`);
|
|
6536
|
-
|
|
6617
|
+
resolve5();
|
|
6537
6618
|
});
|
|
6538
6619
|
});
|
|
6539
6620
|
}
|
|
@@ -6670,16 +6751,16 @@ var init_format = __esm({
|
|
|
6670
6751
|
});
|
|
6671
6752
|
|
|
6672
6753
|
// packages/core/dist/telegram/state.js
|
|
6673
|
-
import
|
|
6674
|
-
function statePath(
|
|
6675
|
-
return
|
|
6754
|
+
import path14 from "path";
|
|
6755
|
+
function statePath(stateRoot2) {
|
|
6756
|
+
return path14.join(stateRoot2, "telegram-state.json");
|
|
6676
6757
|
}
|
|
6677
6758
|
function topicKey(project, scope = "project") {
|
|
6678
6759
|
return `${project}::${scope}`;
|
|
6679
6760
|
}
|
|
6680
|
-
function loadState(
|
|
6761
|
+
function loadState(stateRoot2) {
|
|
6681
6762
|
try {
|
|
6682
|
-
const raw = readConfigFileSync(statePath(
|
|
6763
|
+
const raw = readConfigFileSync(statePath(stateRoot2));
|
|
6683
6764
|
const data = JSON.parse(raw);
|
|
6684
6765
|
const result = {
|
|
6685
6766
|
offset: typeof data.offset === "number" ? data.offset : 0,
|
|
@@ -6695,16 +6776,16 @@ function loadState(stateRoot) {
|
|
|
6695
6776
|
return { offset: 0, topics: {}, notify: {} };
|
|
6696
6777
|
}
|
|
6697
6778
|
}
|
|
6698
|
-
function saveState(
|
|
6699
|
-
writeConfigFileSync(statePath(
|
|
6779
|
+
function saveState(stateRoot2, s) {
|
|
6780
|
+
writeConfigFileSync(statePath(stateRoot2), JSON.stringify(s, null, 2) + "\n");
|
|
6700
6781
|
}
|
|
6701
|
-
function setTopic(
|
|
6702
|
-
const s = loadState(
|
|
6782
|
+
function setTopic(stateRoot2, project, topicId, scope = "project") {
|
|
6783
|
+
const s = loadState(stateRoot2);
|
|
6703
6784
|
s.topics[topicKey(project, scope)] = topicId;
|
|
6704
|
-
saveState(
|
|
6785
|
+
saveState(stateRoot2, s);
|
|
6705
6786
|
}
|
|
6706
|
-
function pruneTopics(
|
|
6707
|
-
const s = loadState(
|
|
6787
|
+
function pruneTopics(stateRoot2, keep) {
|
|
6788
|
+
const s = loadState(stateRoot2);
|
|
6708
6789
|
const removed = Object.keys(s.topics).filter((key) => {
|
|
6709
6790
|
const sep3 = key.indexOf("::");
|
|
6710
6791
|
return !keep(sep3 === -1 ? key : key.slice(0, sep3));
|
|
@@ -6713,60 +6794,60 @@ function pruneTopics(stateRoot, keep) {
|
|
|
6713
6794
|
return [];
|
|
6714
6795
|
for (const key of removed)
|
|
6715
6796
|
delete s.topics[key];
|
|
6716
|
-
saveState(
|
|
6797
|
+
saveState(stateRoot2, s);
|
|
6717
6798
|
return removed;
|
|
6718
6799
|
}
|
|
6719
|
-
function isNotifyActive(
|
|
6720
|
-
return loadState(
|
|
6800
|
+
function isNotifyActive(stateRoot2, project) {
|
|
6801
|
+
return loadState(stateRoot2).notify[project] === true;
|
|
6721
6802
|
}
|
|
6722
|
-
function setLastUserId(
|
|
6723
|
-
const s = loadState(
|
|
6803
|
+
function setLastUserId(stateRoot2, id) {
|
|
6804
|
+
const s = loadState(stateRoot2);
|
|
6724
6805
|
s.lastUserId = id;
|
|
6725
|
-
saveState(
|
|
6806
|
+
saveState(stateRoot2, s);
|
|
6726
6807
|
}
|
|
6727
|
-
function setNotify(
|
|
6728
|
-
const s = loadState(
|
|
6808
|
+
function setNotify(stateRoot2, project, active) {
|
|
6809
|
+
const s = loadState(stateRoot2);
|
|
6729
6810
|
s.notify[project] = active;
|
|
6730
|
-
saveState(
|
|
6811
|
+
saveState(stateRoot2, s);
|
|
6731
6812
|
}
|
|
6732
|
-
function loadPending(
|
|
6733
|
-
return loadState(
|
|
6813
|
+
function loadPending(stateRoot2) {
|
|
6814
|
+
return loadState(stateRoot2).pending ?? {};
|
|
6734
6815
|
}
|
|
6735
|
-
function setPending(
|
|
6736
|
-
const s = loadState(
|
|
6816
|
+
function setPending(stateRoot2, project, p) {
|
|
6817
|
+
const s = loadState(stateRoot2);
|
|
6737
6818
|
s.pending = { ...s.pending ?? {}, [project]: p };
|
|
6738
|
-
saveState(
|
|
6819
|
+
saveState(stateRoot2, s);
|
|
6739
6820
|
}
|
|
6740
|
-
function clearPending(
|
|
6741
|
-
const s = loadState(
|
|
6821
|
+
function clearPending(stateRoot2, project) {
|
|
6822
|
+
const s = loadState(stateRoot2);
|
|
6742
6823
|
if (!s.pending || !(project in s.pending))
|
|
6743
6824
|
return;
|
|
6744
6825
|
delete s.pending[project];
|
|
6745
6826
|
if (Object.keys(s.pending).length === 0)
|
|
6746
6827
|
delete s.pending;
|
|
6747
|
-
saveState(
|
|
6828
|
+
saveState(stateRoot2, s);
|
|
6748
6829
|
}
|
|
6749
|
-
function markPendingWarned(
|
|
6750
|
-
const s = loadState(
|
|
6830
|
+
function markPendingWarned(stateRoot2, project, at) {
|
|
6831
|
+
const s = loadState(stateRoot2);
|
|
6751
6832
|
const p = s.pending?.[project];
|
|
6752
6833
|
if (!p)
|
|
6753
6834
|
return;
|
|
6754
6835
|
s.pending[project] = { ...p, warnedAt: at };
|
|
6755
|
-
saveState(
|
|
6836
|
+
saveState(stateRoot2, s);
|
|
6756
6837
|
}
|
|
6757
|
-
function notePaneScreen(
|
|
6758
|
-
const s = loadState(
|
|
6838
|
+
function notePaneScreen(stateRoot2, project, hash, at) {
|
|
6839
|
+
const s = loadState(stateRoot2);
|
|
6759
6840
|
const p = s.pending?.[project];
|
|
6760
6841
|
if (!p)
|
|
6761
6842
|
return void 0;
|
|
6762
6843
|
if (p.paneHash === hash && p.paneChangedAt !== void 0)
|
|
6763
6844
|
return at - p.paneChangedAt;
|
|
6764
6845
|
s.pending[project] = { ...p, paneHash: hash, paneChangedAt: at };
|
|
6765
|
-
saveState(
|
|
6846
|
+
saveState(stateRoot2, s);
|
|
6766
6847
|
return void 0;
|
|
6767
6848
|
}
|
|
6768
|
-
function findProjectByThread(
|
|
6769
|
-
const s = loadState(
|
|
6849
|
+
function findProjectByThread(stateRoot2, threadId) {
|
|
6850
|
+
const s = loadState(stateRoot2);
|
|
6770
6851
|
for (const [key, id] of Object.entries(s.topics)) {
|
|
6771
6852
|
if (id !== threadId)
|
|
6772
6853
|
continue;
|
|
@@ -6888,7 +6969,7 @@ function formatWatchdogWarning(project, read, minutes) {
|
|
|
6888
6969
|
}
|
|
6889
6970
|
}
|
|
6890
6971
|
function createInboundLifecycle(opts) {
|
|
6891
|
-
const { stateRoot, cfg, sendChatAction, sendReply, readPane, log } = opts;
|
|
6972
|
+
const { stateRoot: stateRoot2, cfg, sendChatAction, sendReply, readPane, log } = opts;
|
|
6892
6973
|
const now = opts.now ?? Date.now;
|
|
6893
6974
|
const schedule = opts.schedule ?? defaultSchedule;
|
|
6894
6975
|
const typingMs = opts.typingMs ?? TYPING_KEEPALIVE_MS;
|
|
@@ -6904,7 +6985,7 @@ function createInboundLifecycle(opts) {
|
|
|
6904
6985
|
const tick = () => {
|
|
6905
6986
|
if (!running || !active.has(project))
|
|
6906
6987
|
return;
|
|
6907
|
-
const p = loadPending(
|
|
6988
|
+
const p = loadPending(stateRoot2)[project];
|
|
6908
6989
|
if (!p) {
|
|
6909
6990
|
active.delete(project);
|
|
6910
6991
|
return;
|
|
@@ -6918,7 +6999,7 @@ function createInboundLifecycle(opts) {
|
|
|
6918
6999
|
schedule(tick, typingMs);
|
|
6919
7000
|
}
|
|
6920
7001
|
async function warn(project, threadId) {
|
|
6921
|
-
markPendingWarned(
|
|
7002
|
+
markPendingWarned(stateRoot2, project, now());
|
|
6922
7003
|
let read = { status: "unreadable" };
|
|
6923
7004
|
try {
|
|
6924
7005
|
if (readPane)
|
|
@@ -6935,7 +7016,7 @@ function createInboundLifecycle(opts) {
|
|
|
6935
7016
|
return {
|
|
6936
7017
|
start() {
|
|
6937
7018
|
running = true;
|
|
6938
|
-
for (const [project, p] of Object.entries(loadPending(
|
|
7019
|
+
for (const [project, p] of Object.entries(loadPending(stateRoot2))) {
|
|
6939
7020
|
void (async () => {
|
|
6940
7021
|
let read = { status: "unreadable" };
|
|
6941
7022
|
try {
|
|
@@ -6944,11 +7025,11 @@ function createInboundLifecycle(opts) {
|
|
|
6944
7025
|
} catch (e) {
|
|
6945
7026
|
log(`telegram resume pane read failed project=${project}: ${e.message}`);
|
|
6946
7027
|
}
|
|
6947
|
-
const still = loadPending(
|
|
7028
|
+
const still = loadPending(stateRoot2)[project];
|
|
6948
7029
|
if (!still || still.startedAt !== p.startedAt)
|
|
6949
7030
|
return;
|
|
6950
7031
|
if (read.status === "no-session") {
|
|
6951
|
-
clearPending(
|
|
7032
|
+
clearPending(stateRoot2, project);
|
|
6952
7033
|
log(`telegram typing lifecycle cleared for ${project}: captain session gone on boot`);
|
|
6953
7034
|
return;
|
|
6954
7035
|
}
|
|
@@ -6963,13 +7044,13 @@ function createInboundLifecycle(opts) {
|
|
|
6963
7044
|
active.clear();
|
|
6964
7045
|
},
|
|
6965
7046
|
begin(project, threadId) {
|
|
6966
|
-
setPending(
|
|
7047
|
+
setPending(stateRoot2, project, { threadId, startedAt: now() });
|
|
6967
7048
|
issue(project, threadId);
|
|
6968
7049
|
if (running && !active.has(project))
|
|
6969
7050
|
drain(project);
|
|
6970
7051
|
},
|
|
6971
7052
|
pending() {
|
|
6972
|
-
return loadPending(
|
|
7053
|
+
return loadPending(stateRoot2);
|
|
6973
7054
|
}
|
|
6974
7055
|
};
|
|
6975
7056
|
}
|
|
@@ -7164,9 +7245,9 @@ var init_tiers = __esm({
|
|
|
7164
7245
|
});
|
|
7165
7246
|
|
|
7166
7247
|
// packages/core/dist/telegram/bridge.js
|
|
7167
|
-
import
|
|
7168
|
-
import
|
|
7169
|
-
import
|
|
7248
|
+
import fs13 from "fs";
|
|
7249
|
+
import os7 from "os";
|
|
7250
|
+
import path15 from "path";
|
|
7170
7251
|
import crypto2 from "crypto";
|
|
7171
7252
|
function formatInboundReceipt(project, outcome) {
|
|
7172
7253
|
if (!outcome)
|
|
@@ -7185,7 +7266,7 @@ function formatInboundReceipt(project, outcome) {
|
|
|
7185
7266
|
}
|
|
7186
7267
|
function telegramPollLockPath(token) {
|
|
7187
7268
|
const hash = crypto2.createHash("sha256").update(token).digest("hex").slice(0, 16);
|
|
7188
|
-
return
|
|
7269
|
+
return path15.join(os7.tmpdir(), `squadrant-telegram-poll-${hash}.lock`);
|
|
7189
7270
|
}
|
|
7190
7271
|
function pidAlive(pid) {
|
|
7191
7272
|
try {
|
|
@@ -7199,9 +7280,9 @@ function lockIsHeld(file) {
|
|
|
7199
7280
|
let pid;
|
|
7200
7281
|
let ageMs;
|
|
7201
7282
|
try {
|
|
7202
|
-
const parsed = Number.parseInt(
|
|
7283
|
+
const parsed = Number.parseInt(fs13.readFileSync(file, "utf8").trim(), 10);
|
|
7203
7284
|
pid = Number.isFinite(parsed) ? parsed : void 0;
|
|
7204
|
-
ageMs = Date.now() -
|
|
7285
|
+
ageMs = Date.now() - fs13.statSync(file).mtimeMs;
|
|
7205
7286
|
} catch {
|
|
7206
7287
|
return false;
|
|
7207
7288
|
}
|
|
@@ -7213,12 +7294,12 @@ function acquirePollLock(token) {
|
|
|
7213
7294
|
const file = telegramPollLockPath(token);
|
|
7214
7295
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
7215
7296
|
try {
|
|
7216
|
-
const fd =
|
|
7217
|
-
|
|
7218
|
-
|
|
7297
|
+
const fd = fs13.openSync(file, "wx");
|
|
7298
|
+
fs13.writeSync(fd, String(process.pid));
|
|
7299
|
+
fs13.closeSync(fd);
|
|
7219
7300
|
return () => {
|
|
7220
7301
|
try {
|
|
7221
|
-
|
|
7302
|
+
fs13.unlinkSync(file);
|
|
7222
7303
|
} catch {
|
|
7223
7304
|
}
|
|
7224
7305
|
};
|
|
@@ -7227,7 +7308,7 @@ function acquirePollLock(token) {
|
|
|
7227
7308
|
return null;
|
|
7228
7309
|
if (attempt === 0 && !lockIsHeld(file)) {
|
|
7229
7310
|
try {
|
|
7230
|
-
|
|
7311
|
+
fs13.unlinkSync(file);
|
|
7231
7312
|
} catch {
|
|
7232
7313
|
}
|
|
7233
7314
|
continue;
|
|
@@ -7246,7 +7327,7 @@ function foreignPollLockHolder(token, selfPid = process.pid) {
|
|
|
7246
7327
|
const file = telegramPollLockPath(token);
|
|
7247
7328
|
let pid;
|
|
7248
7329
|
try {
|
|
7249
|
-
const parsed = Number.parseInt(
|
|
7330
|
+
const parsed = Number.parseInt(fs13.readFileSync(file, "utf8").trim(), 10);
|
|
7250
7331
|
if (!Number.isFinite(parsed))
|
|
7251
7332
|
return null;
|
|
7252
7333
|
pid = parsed;
|
|
@@ -7287,9 +7368,9 @@ function notifyToggle(text) {
|
|
|
7287
7368
|
return null;
|
|
7288
7369
|
}
|
|
7289
7370
|
function createTelegramBridge(opts) {
|
|
7290
|
-
const { cfg, stateRoot, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
|
|
7371
|
+
const { cfg, stateRoot: stateRoot2, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
|
|
7291
7372
|
const lifecycle = opts.lifecycle;
|
|
7292
|
-
const configRoot = opts.configRoot ??
|
|
7373
|
+
const configRoot = opts.configRoot ?? path15.join(os7.homedir(), ".config", "squadrant");
|
|
7293
7374
|
const pollMs = cfg.pollMs ?? 1e3;
|
|
7294
7375
|
const schedule = opts.schedule ?? defaultSchedule2;
|
|
7295
7376
|
const lockToken = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
@@ -7303,17 +7384,17 @@ function createTelegramBridge(opts) {
|
|
|
7303
7384
|
let lastError = null;
|
|
7304
7385
|
let lastErrorAt = null;
|
|
7305
7386
|
function persistOffset(next) {
|
|
7306
|
-
const s = loadState(
|
|
7387
|
+
const s = loadState(stateRoot2);
|
|
7307
7388
|
s.offset = next;
|
|
7308
|
-
saveState(
|
|
7389
|
+
saveState(stateRoot2, s);
|
|
7309
7390
|
}
|
|
7310
7391
|
async function sendToTopic(project, text) {
|
|
7311
|
-
const { topicId } = await runTelegramLink({ project, cfg, client, stateRoot });
|
|
7392
|
+
const { topicId } = await runTelegramLink({ project, cfg, client, stateRoot: stateRoot2 });
|
|
7312
7393
|
await client.sendMessage(cfg.supergroupId, topicId, text);
|
|
7313
7394
|
}
|
|
7314
7395
|
async function deliverOutbound(project, ev2) {
|
|
7315
7396
|
const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
|
|
7316
|
-
const live = loadState(
|
|
7397
|
+
const live = loadState(stateRoot2).notify[project];
|
|
7317
7398
|
const active = live ?? resolved.active;
|
|
7318
7399
|
if (!active)
|
|
7319
7400
|
return;
|
|
@@ -7333,7 +7414,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7333
7414
|
}
|
|
7334
7415
|
function resolveLiveNotify(project) {
|
|
7335
7416
|
const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
|
|
7336
|
-
const live = loadState(
|
|
7417
|
+
const live = loadState(stateRoot2).notify[project];
|
|
7337
7418
|
return { ...resolved, active: live ?? resolved.active };
|
|
7338
7419
|
}
|
|
7339
7420
|
async function editMarkup(chatId, messageId, markup) {
|
|
@@ -7363,14 +7444,14 @@ ${formatUsageLine(usage2)}`;
|
|
|
7363
7444
|
const chatId = cq.message.chat.id;
|
|
7364
7445
|
const messageId = cq.message.message_id;
|
|
7365
7446
|
if (action.t === "notify") {
|
|
7366
|
-
const resolved = findProjectByThread(
|
|
7447
|
+
const resolved = findProjectByThread(stateRoot2, cq.message.message_thread_id ?? -1);
|
|
7367
7448
|
if (!resolved) {
|
|
7368
7449
|
await client.answerCallbackQuery(cq.id, "no project for this topic");
|
|
7369
7450
|
return;
|
|
7370
7451
|
}
|
|
7371
7452
|
const project2 = resolved.project;
|
|
7372
7453
|
if (action.dim === "active") {
|
|
7373
|
-
setNotify(
|
|
7454
|
+
setNotify(stateRoot2, project2, action.val === "on");
|
|
7374
7455
|
} else if (action.dim === "cap") {
|
|
7375
7456
|
saveProjectOverride(project2, { telegram: { notify: { cap: action.val === "on" } } }, configRoot);
|
|
7376
7457
|
} else {
|
|
@@ -7402,10 +7483,10 @@ ${formatUsageLine(usage2)}`;
|
|
|
7402
7483
|
await runCommand(["launch", project]);
|
|
7403
7484
|
await client.answerCallbackQuery(cq.id, `launching ${project}`);
|
|
7404
7485
|
} else if (act === "mu") {
|
|
7405
|
-
setNotify(
|
|
7486
|
+
setNotify(stateRoot2, project, false);
|
|
7406
7487
|
await client.answerCallbackQuery(cq.id, `\u{1F515} muted ${project}`);
|
|
7407
7488
|
} else {
|
|
7408
|
-
setNotify(
|
|
7489
|
+
setNotify(stateRoot2, project, true);
|
|
7409
7490
|
await client.answerCallbackQuery(cq.id, `\u{1F514} unmuted ${project}`);
|
|
7410
7491
|
}
|
|
7411
7492
|
} catch (e) {
|
|
@@ -7430,14 +7511,14 @@ ${formatUsageLine(usage2)}`;
|
|
|
7430
7511
|
}
|
|
7431
7512
|
function currentEffort() {
|
|
7432
7513
|
try {
|
|
7433
|
-
return loadConfig(
|
|
7514
|
+
return loadConfig(path15.join(configRoot, "config.json")).defaults.effort ?? "balance";
|
|
7434
7515
|
} catch {
|
|
7435
7516
|
return "balance";
|
|
7436
7517
|
}
|
|
7437
7518
|
}
|
|
7438
7519
|
function projectNames() {
|
|
7439
7520
|
try {
|
|
7440
|
-
return Object.keys(loadConfig(
|
|
7521
|
+
return Object.keys(loadConfig(path15.join(configRoot, "config.json")).projects);
|
|
7441
7522
|
} catch {
|
|
7442
7523
|
return [];
|
|
7443
7524
|
}
|
|
@@ -7497,7 +7578,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7497
7578
|
await runChannelCommand(text, fromId, void 0);
|
|
7498
7579
|
}
|
|
7499
7580
|
async function handleProjectTopic(text, threadId, fromId, messageId, media) {
|
|
7500
|
-
const resolved = findProjectByThread(
|
|
7581
|
+
const resolved = findProjectByThread(stateRoot2, threadId);
|
|
7501
7582
|
if (!resolved) {
|
|
7502
7583
|
log(`telegram inbound in unbound topic thread=${threadId} \u2014 no project linked, message dropped`);
|
|
7503
7584
|
await reply(threadId, "\u26A0\uFE0F this topic isn't linked to a project \u2014 run: squadrant telegram link <project>");
|
|
@@ -7517,7 +7598,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7517
7598
|
await reply(threadId, "\u26D4 not authorized");
|
|
7518
7599
|
return;
|
|
7519
7600
|
}
|
|
7520
|
-
setNotify(
|
|
7601
|
+
setNotify(stateRoot2, resolved.project, toggle);
|
|
7521
7602
|
await reply(threadId, toggle ? `\u{1F514} ${resolved.project} notifications ON` : `\u{1F515} ${resolved.project} notifications OFF`);
|
|
7522
7603
|
return;
|
|
7523
7604
|
}
|
|
@@ -7555,7 +7636,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7555
7636
|
if (messageId !== void 0) {
|
|
7556
7637
|
void Promise.resolve(client.setMessageReaction?.(cfg.supergroupId, messageId, "\u{1F44D}")).catch((e) => log(`telegram stage-1 reaction failed project=${resolved.project}: ${e.message}`));
|
|
7557
7638
|
}
|
|
7558
|
-
setNotify(
|
|
7639
|
+
setNotify(stateRoot2, resolved.project, true);
|
|
7559
7640
|
if (ensureCaptainAlive && isControlEnabled(cfg) && isAuthorized(fromId, cfg)) {
|
|
7560
7641
|
void ensureCaptainAlive(resolved.project).then((r) => {
|
|
7561
7642
|
if (r === "timeout") {
|
|
@@ -7572,7 +7653,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7572
7653
|
}
|
|
7573
7654
|
const deliveryHeld = outcome?.status === "held";
|
|
7574
7655
|
if (!handled) {
|
|
7575
|
-
await appendCaptainMessage2({ stateRoot, project: resolved.project, text: formatInbound(text), source: "telegram" });
|
|
7656
|
+
await appendCaptainMessage2({ stateRoot: stateRoot2, project: resolved.project, text: formatInbound(text), source: "telegram" });
|
|
7576
7657
|
outcome = void 0;
|
|
7577
7658
|
}
|
|
7578
7659
|
const receipt = formatInboundReceipt(resolved.project, outcome);
|
|
@@ -7601,8 +7682,8 @@ ${formatUsageLine(usage2)}`;
|
|
|
7601
7682
|
return;
|
|
7602
7683
|
if (!cfg.chats.includes(m.chat.id))
|
|
7603
7684
|
return;
|
|
7604
|
-
if (m.from?.id !== void 0 && loadState(
|
|
7605
|
-
setLastUserId(
|
|
7685
|
+
if (m.from?.id !== void 0 && loadState(stateRoot2).lastUserId !== m.from.id) {
|
|
7686
|
+
setLastUserId(stateRoot2, m.from.id);
|
|
7606
7687
|
}
|
|
7607
7688
|
const spawnProject = parseSpawnPrompt(m.reply_to_message?.text);
|
|
7608
7689
|
if (spawnProject) {
|
|
@@ -7661,7 +7742,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7661
7742
|
pollAbort = ac;
|
|
7662
7743
|
let waitMs = pollMs;
|
|
7663
7744
|
try {
|
|
7664
|
-
const offset = loadState(
|
|
7745
|
+
const offset = loadState(stateRoot2).offset;
|
|
7665
7746
|
const updates = await client.getUpdates(offset, LONG_POLL_SEC, ac.signal);
|
|
7666
7747
|
conflictCount = 0;
|
|
7667
7748
|
for (const u of updates) {
|
|
@@ -7709,7 +7790,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7709
7790
|
const known = opts.registeredProjects?.();
|
|
7710
7791
|
if (known !== void 0) {
|
|
7711
7792
|
try {
|
|
7712
|
-
const removed = pruneTopics(
|
|
7793
|
+
const removed = pruneTopics(stateRoot2, (p) => known.includes(p));
|
|
7713
7794
|
if (removed.length > 0)
|
|
7714
7795
|
log(`telegram registry pruned ${removed.length} stale topic link(s): ${removed.join(", ")}`);
|
|
7715
7796
|
} catch (e) {
|
|
@@ -7779,9 +7860,9 @@ var init_bridge = __esm({
|
|
|
7779
7860
|
|
|
7780
7861
|
// packages/core/dist/restart-daemon.js
|
|
7781
7862
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
7782
|
-
import { existsSync as
|
|
7863
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7783
7864
|
function defaultIsRunning() {
|
|
7784
|
-
return
|
|
7865
|
+
return existsSync11(DAEMON_SOCK_PATH);
|
|
7785
7866
|
}
|
|
7786
7867
|
function defaultRunKickstart() {
|
|
7787
7868
|
const uid = process.getuid?.() ?? 0;
|
|
@@ -7815,7 +7896,7 @@ var init_restart_daemon = __esm({
|
|
|
7815
7896
|
});
|
|
7816
7897
|
|
|
7817
7898
|
// packages/core/dist/telegram/setup.js
|
|
7818
|
-
import
|
|
7899
|
+
import fs14 from "fs";
|
|
7819
7900
|
function resolveSetupGroup(existingSupergroupId, opts) {
|
|
7820
7901
|
if (existingSupergroupId !== void 0 && !opts.redetect)
|
|
7821
7902
|
return "reuse";
|
|
@@ -7847,8 +7928,8 @@ function resolveSetupToken(existingToken, opts) {
|
|
|
7847
7928
|
return "prompt";
|
|
7848
7929
|
return "try-reuse";
|
|
7849
7930
|
}
|
|
7850
|
-
function resolveSetupUserId(flagUserId, detectedUserId,
|
|
7851
|
-
return flagUserId ?? detectedUserId ?? loadState(
|
|
7931
|
+
function resolveSetupUserId(flagUserId, detectedUserId, stateRoot2) {
|
|
7932
|
+
return flagUserId ?? detectedUserId ?? loadState(stateRoot2).lastUserId;
|
|
7852
7933
|
}
|
|
7853
7934
|
async function runRegisterCommands(opts) {
|
|
7854
7935
|
await opts.client.setMyCommands(BOT_COMMANDS);
|
|
@@ -7866,7 +7947,7 @@ function writeTelegramConfig(configPath, opts) {
|
|
|
7866
7947
|
let config;
|
|
7867
7948
|
let raw = null;
|
|
7868
7949
|
try {
|
|
7869
|
-
raw =
|
|
7950
|
+
raw = fs14.readFileSync(configPath, "utf-8");
|
|
7870
7951
|
} catch (err) {
|
|
7871
7952
|
if (err.code !== "ENOENT") {
|
|
7872
7953
|
throw new Error(`refusing to overwrite unreadable config at ${configPath}: ${String(err)}`);
|
|
@@ -8098,8 +8179,8 @@ var init_stream = __esm({
|
|
|
8098
8179
|
// packages/core/dist/router/shim.js
|
|
8099
8180
|
import { createServer as createServer3 } from "http";
|
|
8100
8181
|
import { Readable } from "stream";
|
|
8101
|
-
function joinUrl(base,
|
|
8102
|
-
return base.replace(/\/+$/, "") +
|
|
8182
|
+
function joinUrl(base, path44) {
|
|
8183
|
+
return base.replace(/\/+$/, "") + path44;
|
|
8103
8184
|
}
|
|
8104
8185
|
async function readBody(req) {
|
|
8105
8186
|
const chunks = [];
|
|
@@ -8275,8 +8356,8 @@ function createRouterShim(opts) {
|
|
|
8275
8356
|
writeJson(res, 200, { input_tokens: Math.max(1, Math.ceil(text.length / 4)) });
|
|
8276
8357
|
}
|
|
8277
8358
|
function handler(req, res) {
|
|
8278
|
-
const
|
|
8279
|
-
if (req.method === "POST" &&
|
|
8359
|
+
const path44 = pathnameOf(req.url);
|
|
8360
|
+
if (req.method === "POST" && path44 === "/v1/messages") {
|
|
8280
8361
|
void handleMessages(req, res).catch((err) => {
|
|
8281
8362
|
log(`router internal error: ${err instanceof Error ? err.message : String(err)}`);
|
|
8282
8363
|
if (!res.headersSent) {
|
|
@@ -8288,18 +8369,18 @@ function createRouterShim(opts) {
|
|
|
8288
8369
|
});
|
|
8289
8370
|
return;
|
|
8290
8371
|
}
|
|
8291
|
-
if (req.method === "POST" &&
|
|
8372
|
+
if (req.method === "POST" && path44 === "/v1/messages/count_tokens") {
|
|
8292
8373
|
void handleCountTokens(req, res).catch(() => {
|
|
8293
8374
|
if (!res.headersSent)
|
|
8294
8375
|
writeJson(res, 502, anthropicError(502, "api_error", "router internal error").body);
|
|
8295
8376
|
});
|
|
8296
8377
|
return;
|
|
8297
8378
|
}
|
|
8298
|
-
if (req.method === "GET" &&
|
|
8379
|
+
if (req.method === "GET" && path44 === "/v1/models") {
|
|
8299
8380
|
writeJson(res, 200, modelsList([...advertisedModels]));
|
|
8300
8381
|
return;
|
|
8301
8382
|
}
|
|
8302
|
-
if (req.method === "GET" &&
|
|
8383
|
+
if (req.method === "GET" && path44 === "/healthz") {
|
|
8303
8384
|
writeJson(res, 200, { ready: server?.listening === true, upstreamReachable, lastError });
|
|
8304
8385
|
return;
|
|
8305
8386
|
}
|
|
@@ -8311,7 +8392,7 @@ function createRouterShim(opts) {
|
|
|
8311
8392
|
return;
|
|
8312
8393
|
if (starting)
|
|
8313
8394
|
return starting;
|
|
8314
|
-
starting = new Promise((
|
|
8395
|
+
starting = new Promise((resolve5, reject) => {
|
|
8315
8396
|
const s = createServer3(handler);
|
|
8316
8397
|
const onStartupError = (err) => {
|
|
8317
8398
|
s.off("error", onStartupError);
|
|
@@ -8327,7 +8408,7 @@ function createRouterShim(opts) {
|
|
|
8327
8408
|
server = s;
|
|
8328
8409
|
const addr = s.address();
|
|
8329
8410
|
boundPort = typeof addr === "object" && addr ? addr.port : 0;
|
|
8330
|
-
|
|
8411
|
+
resolve5();
|
|
8331
8412
|
});
|
|
8332
8413
|
});
|
|
8333
8414
|
try {
|
|
@@ -8347,7 +8428,7 @@ function createRouterShim(opts) {
|
|
|
8347
8428
|
server = void 0;
|
|
8348
8429
|
boundPort = 0;
|
|
8349
8430
|
s.closeAllConnections?.();
|
|
8350
|
-
await new Promise((
|
|
8431
|
+
await new Promise((resolve5) => s.close(() => resolve5()));
|
|
8351
8432
|
},
|
|
8352
8433
|
url() {
|
|
8353
8434
|
return `http://${opts.host ?? "127.0.0.1"}:${boundPort}`;
|
|
@@ -8541,7 +8622,6 @@ var init_captain_router = __esm({
|
|
|
8541
8622
|
// packages/core/dist/group-dispatch.js
|
|
8542
8623
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8543
8624
|
import { createConnection as createConnection2 } from "net";
|
|
8544
|
-
import { join as join16 } from "path";
|
|
8545
8625
|
function resolveCurrentProject(config) {
|
|
8546
8626
|
const cwd = process.cwd();
|
|
8547
8627
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
@@ -8552,8 +8632,8 @@ function resolveCurrentProject(config) {
|
|
|
8552
8632
|
return null;
|
|
8553
8633
|
}
|
|
8554
8634
|
async function probeCaptainChannel(project, opts = {}) {
|
|
8555
|
-
const
|
|
8556
|
-
const addr = readCaptainAddress(
|
|
8635
|
+
const stateRoot2 = opts.stateRoot ?? stateRoot();
|
|
8636
|
+
const addr = readCaptainAddress(stateRoot2, project);
|
|
8557
8637
|
if (!addr)
|
|
8558
8638
|
return false;
|
|
8559
8639
|
if (addr.agent === "opencode") {
|
|
@@ -8568,11 +8648,11 @@ async function probeCaptainChannel(project, opts = {}) {
|
|
|
8568
8648
|
}
|
|
8569
8649
|
}
|
|
8570
8650
|
async function httpReachable(port, fetchImpl, timeoutMs) {
|
|
8571
|
-
for (const
|
|
8651
|
+
for (const path44 of ["/session", "/api/session"]) {
|
|
8572
8652
|
const ac = new AbortController();
|
|
8573
8653
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
8574
8654
|
try {
|
|
8575
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
8655
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path44}`, { signal: ac.signal });
|
|
8576
8656
|
if (res.ok)
|
|
8577
8657
|
return true;
|
|
8578
8658
|
} catch {
|
|
@@ -8583,7 +8663,7 @@ async function httpReachable(port, fetchImpl, timeoutMs) {
|
|
|
8583
8663
|
return false;
|
|
8584
8664
|
}
|
|
8585
8665
|
function socketAccepts(socketPath, timeoutMs = 2e3) {
|
|
8586
|
-
return new Promise((
|
|
8666
|
+
return new Promise((resolve5) => {
|
|
8587
8667
|
let settled = false;
|
|
8588
8668
|
const sock = createConnection2(socketPath);
|
|
8589
8669
|
const done = (v2) => {
|
|
@@ -8591,7 +8671,7 @@ function socketAccepts(socketPath, timeoutMs = 2e3) {
|
|
|
8591
8671
|
return;
|
|
8592
8672
|
settled = true;
|
|
8593
8673
|
sock.destroy();
|
|
8594
|
-
|
|
8674
|
+
resolve5(v2);
|
|
8595
8675
|
};
|
|
8596
8676
|
sock.setTimeout(timeoutMs, () => done(false));
|
|
8597
8677
|
sock.once("connect", () => done(true));
|
|
@@ -8751,6 +8831,7 @@ async function launchOneWorkspace(opts) {
|
|
|
8751
8831
|
if (!forceFresh) {
|
|
8752
8832
|
const auto = shouldStartFresh(opts.workspaceName, opts.role, {
|
|
8753
8833
|
sessionsPath: opts.sessionsPath,
|
|
8834
|
+
legacySessionsPath: opts.legacySessionsPath,
|
|
8754
8835
|
templatesDir: opts.templatesDir
|
|
8755
8836
|
});
|
|
8756
8837
|
if (auto.fresh && opts.keepOverride && auto.reason !== "first launch") {
|
|
@@ -8764,6 +8845,7 @@ async function launchOneWorkspace(opts) {
|
|
|
8764
8845
|
const agentCmd = opts.role === "captain" ? `SQUADRANT_ROLE=captain ${builtCmd}` : builtCmd;
|
|
8765
8846
|
recordSession(opts.workspaceName, opts.role, {
|
|
8766
8847
|
sessionsPath: opts.sessionsPath,
|
|
8848
|
+
legacySessionsPath: opts.legacySessionsPath,
|
|
8767
8849
|
templatesDir: opts.templatesDir
|
|
8768
8850
|
});
|
|
8769
8851
|
await bootWorkspace({
|
|
@@ -8790,7 +8872,7 @@ var init_launch_workspace = __esm({
|
|
|
8790
8872
|
});
|
|
8791
8873
|
|
|
8792
8874
|
// packages/core/dist/side-session.js
|
|
8793
|
-
import
|
|
8875
|
+
import fs15 from "fs";
|
|
8794
8876
|
function sideTitleFor(project, name) {
|
|
8795
8877
|
return `\u{1F5D2} ${project}:${name}`;
|
|
8796
8878
|
}
|
|
@@ -8812,7 +8894,7 @@ function sideNextAutoName(existingTitles, project) {
|
|
|
8812
8894
|
i++;
|
|
8813
8895
|
return `side-${i}`;
|
|
8814
8896
|
}
|
|
8815
|
-
function buildSideFirstTurn(topic, project, role,
|
|
8897
|
+
function buildSideFirstTurn(topic, project, role, workspace, scratchWorktree) {
|
|
8816
8898
|
const lines = [
|
|
8817
8899
|
topic,
|
|
8818
8900
|
"",
|
|
@@ -8820,7 +8902,7 @@ function buildSideFirstTurn(topic, project, role, spokeVault, scratchWorktree) {
|
|
|
8820
8902
|
"Side-session context (for handoff use):",
|
|
8821
8903
|
`Project: ${project}`,
|
|
8822
8904
|
`Role: ${role}`,
|
|
8823
|
-
`
|
|
8905
|
+
`Workspace: ${workspace}`
|
|
8824
8906
|
];
|
|
8825
8907
|
if (scratchWorktree) {
|
|
8826
8908
|
lines.push(`Scratch worktree: ${scratchWorktree}`);
|
|
@@ -8862,7 +8944,7 @@ async function runSideSpawn(input, config, deps) {
|
|
|
8862
8944
|
const pane = await runtime.newPane({ workspaceId: captain.id, direction, title });
|
|
8863
8945
|
await runtime.sendToPane(pane, `cd ${shellQuote(spawnCwd)} && ${SIDE_SESSION_ENV}=1 ${agentCmd}`);
|
|
8864
8946
|
const preLaunchScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
8865
|
-
const firstTurn = buildSideFirstTurn(input.topic, input.project, input.role,
|
|
8947
|
+
const firstTurn = buildSideFirstTurn(input.topic, input.project, input.role, workspacePath(input.project), input.role === "debug" ? spawnCwd : void 0);
|
|
8866
8948
|
await deps.sendFirstTurn(pane, spillOversizedFirstTurn(firstTurn, `side-${input.project}-${name}`), preLaunchScreen);
|
|
8867
8949
|
return { ...pane, title };
|
|
8868
8950
|
}
|
|
@@ -8897,7 +8979,7 @@ async function runSideClose(runtime, workspaceId, project, name, projPath, workt
|
|
|
8897
8979
|
}
|
|
8898
8980
|
if (projPath) {
|
|
8899
8981
|
const wtPath = worktreePath(projPath, worktreeDir, project, name);
|
|
8900
|
-
if (
|
|
8982
|
+
if (fs15.existsSync(wtPath)) {
|
|
8901
8983
|
try {
|
|
8902
8984
|
removeWorktree(projPath, wtPath);
|
|
8903
8985
|
} catch (e) {
|
|
@@ -9411,15 +9493,15 @@ var init_peer_first_turn = __esm({
|
|
|
9411
9493
|
});
|
|
9412
9494
|
|
|
9413
9495
|
// packages/core/dist/knowledge/paths.js
|
|
9414
|
-
import
|
|
9496
|
+
import path16 from "path";
|
|
9415
9497
|
function kbDir(cfg, kb) {
|
|
9416
9498
|
return kbPath(cfg, kb);
|
|
9417
9499
|
}
|
|
9418
9500
|
function kbRulesDir(cfg, kb) {
|
|
9419
|
-
return
|
|
9501
|
+
return path16.join(kbDir(cfg, kb), "shared", "rules");
|
|
9420
9502
|
}
|
|
9421
9503
|
function legacyKbDir(hubVault, kb) {
|
|
9422
|
-
return
|
|
9504
|
+
return path16.join(hubVault, "knowledge", kb);
|
|
9423
9505
|
}
|
|
9424
9506
|
var PROPOSED_DIR;
|
|
9425
9507
|
var init_paths = __esm({
|
|
@@ -9431,28 +9513,28 @@ var init_paths = __esm({
|
|
|
9431
9513
|
|
|
9432
9514
|
// packages/core/dist/knowledge/ingest.js
|
|
9433
9515
|
import crypto3 from "crypto";
|
|
9434
|
-
import
|
|
9435
|
-
import
|
|
9436
|
-
import
|
|
9516
|
+
import fs16 from "fs";
|
|
9517
|
+
import os8 from "os";
|
|
9518
|
+
import path17 from "path";
|
|
9437
9519
|
import { execFile as execFile2 } from "child_process";
|
|
9438
9520
|
import { promisify as promisify2 } from "util";
|
|
9439
9521
|
function refFor(kbRoot, file) {
|
|
9440
|
-
const rel =
|
|
9441
|
-
if (rel && !rel.startsWith("..") && !
|
|
9442
|
-
return rel.split(
|
|
9443
|
-
const home =
|
|
9444
|
-
return file.startsWith(home +
|
|
9522
|
+
const rel = path17.relative(kbRoot, file);
|
|
9523
|
+
if (rel && !rel.startsWith("..") && !path17.isAbsolute(rel))
|
|
9524
|
+
return rel.split(path17.sep).join("/");
|
|
9525
|
+
const home = os8.homedir();
|
|
9526
|
+
return file.startsWith(home + path17.sep) ? `~/${path17.relative(home, file).split(path17.sep).join("/")}` : file;
|
|
9445
9527
|
}
|
|
9446
9528
|
function readState2(kbRoot, ref) {
|
|
9447
9529
|
try {
|
|
9448
|
-
return JSON.parse(
|
|
9530
|
+
return JSON.parse(fs16.readFileSync(stampFile(kbRoot, ref), "utf8"));
|
|
9449
9531
|
} catch {
|
|
9450
9532
|
return null;
|
|
9451
9533
|
}
|
|
9452
9534
|
}
|
|
9453
9535
|
function writeState(kbRoot, st) {
|
|
9454
|
-
|
|
9455
|
-
|
|
9536
|
+
fs16.mkdirSync(path17.join(kbRoot, CONVERTED_DIR), { recursive: true });
|
|
9537
|
+
fs16.writeFileSync(stampFile(kbRoot, st.ref), JSON.stringify(st) + "\n");
|
|
9456
9538
|
}
|
|
9457
9539
|
function globToRegExp(glob) {
|
|
9458
9540
|
let re = "";
|
|
@@ -9472,12 +9554,12 @@ function globToRegExp(glob) {
|
|
|
9472
9554
|
return new RegExp(`^${re}$`);
|
|
9473
9555
|
}
|
|
9474
9556
|
function walkFiles(dir, out = []) {
|
|
9475
|
-
if (!
|
|
9557
|
+
if (!fs16.existsSync(dir))
|
|
9476
9558
|
return out;
|
|
9477
|
-
for (const ent of
|
|
9559
|
+
for (const ent of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
9478
9560
|
if (ent.name === ".git" || ent.name === CONVERTED_DIR || ent.name === "node_modules")
|
|
9479
9561
|
continue;
|
|
9480
|
-
const full =
|
|
9562
|
+
const full = path17.join(dir, ent.name);
|
|
9481
9563
|
if (ent.isDirectory())
|
|
9482
9564
|
walkFiles(full, out);
|
|
9483
9565
|
else if (ent.isFile())
|
|
@@ -9486,27 +9568,27 @@ function walkFiles(dir, out = []) {
|
|
|
9486
9568
|
return out;
|
|
9487
9569
|
}
|
|
9488
9570
|
function expandSource(kbRoot, entry) {
|
|
9489
|
-
const abs =
|
|
9571
|
+
const abs = path17.resolve(kbRoot, expandHome(entry.path));
|
|
9490
9572
|
if (!/[*?]/.test(entry.path))
|
|
9491
|
-
return
|
|
9573
|
+
return fs16.existsSync(abs) && fs16.statSync(abs).isFile() ? [abs] : [];
|
|
9492
9574
|
const base = abs.split(/[*?]/)[0];
|
|
9493
|
-
const root = base.endsWith(
|
|
9494
|
-
const re = globToRegExp(abs.split(
|
|
9495
|
-
return walkFiles(root).filter((f) => re.test(f.split(
|
|
9575
|
+
const root = base.endsWith(path17.sep) ? base : path17.dirname(base);
|
|
9576
|
+
const re = globToRegExp(abs.split(path17.sep).join("/"));
|
|
9577
|
+
return walkFiles(root).filter((f) => re.test(f.split(path17.sep).join("/"))).sort();
|
|
9496
9578
|
}
|
|
9497
9579
|
function refCandidates(kbRoot, sources, ref) {
|
|
9498
|
-
const asIs =
|
|
9499
|
-
if (
|
|
9580
|
+
const asIs = path17.resolve(kbRoot, expandHome(ref));
|
|
9581
|
+
if (path17.isAbsolute(expandHome(ref)))
|
|
9500
9582
|
return [asIs];
|
|
9501
9583
|
const bases = /* @__PURE__ */ new Set([kbRoot]);
|
|
9502
9584
|
for (const s of sources) {
|
|
9503
|
-
let d =
|
|
9504
|
-
for (let i = 0; i < 6 && d !==
|
|
9505
|
-
d =
|
|
9585
|
+
let d = path17.resolve(kbRoot, expandHome(s.path.split(/[*?[{]/)[0]));
|
|
9586
|
+
for (let i = 0; i < 6 && d !== path17.dirname(d); i++) {
|
|
9587
|
+
d = path17.dirname(d);
|
|
9506
9588
|
bases.add(d);
|
|
9507
9589
|
}
|
|
9508
9590
|
}
|
|
9509
|
-
return [.../* @__PURE__ */ new Set([asIs, ...[...bases].map((b) =>
|
|
9591
|
+
return [.../* @__PURE__ */ new Set([asIs, ...[...bases].map((b) => path17.join(b, ref))])];
|
|
9510
9592
|
}
|
|
9511
9593
|
function refPath(kbRoot, sources, ref) {
|
|
9512
9594
|
const cands = refCandidates(kbRoot, sources, ref);
|
|
@@ -9525,7 +9607,7 @@ function refCanon(kbRoot, sources) {
|
|
|
9525
9607
|
}
|
|
9526
9608
|
function detectChange(kbRoot, file) {
|
|
9527
9609
|
const ref = refFor(kbRoot, file);
|
|
9528
|
-
const st =
|
|
9610
|
+
const st = fs16.statSync(file);
|
|
9529
9611
|
const prior = readState2(kbRoot, ref);
|
|
9530
9612
|
if (prior && prior.size === st.size && prior.mtimeMs === st.mtimeMs) {
|
|
9531
9613
|
return { ref, changed: false, stamp: { sha: prior.sha, size: st.size, mtimeMs: st.mtimeMs }, prior };
|
|
@@ -9582,13 +9664,13 @@ async function planIngest(kbRoot, opts) {
|
|
|
9582
9664
|
let text;
|
|
9583
9665
|
const conv = convertedFile(kbRoot, ref);
|
|
9584
9666
|
try {
|
|
9585
|
-
if (!changed &&
|
|
9586
|
-
text =
|
|
9667
|
+
if (!changed && fs16.existsSync(conv))
|
|
9668
|
+
text = fs16.readFileSync(conv, "utf8");
|
|
9587
9669
|
else {
|
|
9588
|
-
text = PASS_THROUGH.has(
|
|
9670
|
+
text = PASS_THROUGH.has(path17.extname(file).toLowerCase()) ? fs16.readFileSync(file, "utf8") : await converter(file);
|
|
9589
9671
|
if (!opts.dryRun) {
|
|
9590
|
-
|
|
9591
|
-
|
|
9672
|
+
fs16.mkdirSync(path17.dirname(conv), { recursive: true });
|
|
9673
|
+
fs16.writeFileSync(conv, text);
|
|
9592
9674
|
}
|
|
9593
9675
|
}
|
|
9594
9676
|
} catch (e) {
|
|
@@ -9632,19 +9714,19 @@ var init_ingest = __esm({
|
|
|
9632
9714
|
const { stdout } = await execFileAsync("markitdown", [file], { maxBuffer: 64 * 1024 * 1024 });
|
|
9633
9715
|
return stdout;
|
|
9634
9716
|
};
|
|
9635
|
-
expandHome = (p) => p === "~" || p.startsWith("~/") ?
|
|
9717
|
+
expandHome = (p) => p === "~" || p.startsWith("~/") ? path17.join(os8.homedir(), p.slice(1)) : p;
|
|
9636
9718
|
cacheKey = (ref) => `${ref.replace(/[^A-Za-z0-9._-]+/g, "_")}.${crypto3.createHash("sha1").update(ref).digest("hex").slice(0, 6)}`;
|
|
9637
|
-
convertedFile = (kbRoot, ref) =>
|
|
9638
|
-
stampFile = (kbRoot, ref) =>
|
|
9639
|
-
isFile = (f) =>
|
|
9719
|
+
convertedFile = (kbRoot, ref) => path17.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.md`);
|
|
9720
|
+
stampFile = (kbRoot, ref) => path17.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.sha`);
|
|
9721
|
+
isFile = (f) => fs16.existsSync(f) && fs16.statSync(f).isFile();
|
|
9640
9722
|
resolveSourceRef = (kbRoot, sources, ref) => refCandidates(kbRoot, sources, ref).find(isFile);
|
|
9641
9723
|
entryCovers = (kbRoot, entry, file) => {
|
|
9642
|
-
const abs =
|
|
9643
|
-
return /[*?]/.test(entry.path) ? globToRegExp(abs.split(
|
|
9724
|
+
const abs = path17.resolve(kbRoot, expandHome(entry.path));
|
|
9725
|
+
return /[*?]/.test(entry.path) ? globToRegExp(abs.split(path17.sep).join("/")).test(file.split(path17.sep).join("/")) : abs === file;
|
|
9644
9726
|
};
|
|
9645
9727
|
sourceCovers = (kbRoot, sources, entry, ref) => entryCovers(kbRoot, entry, refPath(kbRoot, sources, ref));
|
|
9646
9728
|
isGatedRef = (kbRoot, sources, ref) => sources.some((e) => e.review === "required" && sourceCovers(kbRoot, sources, e, ref));
|
|
9647
|
-
sha256 = (file) => crypto3.createHash("sha256").update(
|
|
9729
|
+
sha256 = (file) => crypto3.createHash("sha256").update(fs16.readFileSync(file)).digest("hex");
|
|
9648
9730
|
}
|
|
9649
9731
|
});
|
|
9650
9732
|
|
|
@@ -9725,14 +9807,14 @@ var init_verify = __esm({
|
|
|
9725
9807
|
});
|
|
9726
9808
|
|
|
9727
9809
|
// packages/core/dist/knowledge/store.js
|
|
9728
|
-
import
|
|
9729
|
-
import
|
|
9810
|
+
import fs17 from "fs";
|
|
9811
|
+
import path18 from "path";
|
|
9730
9812
|
import matter2 from "gray-matter";
|
|
9731
9813
|
function duplicateIdProblem(id, ignored, used) {
|
|
9732
9814
|
return `duplicate id ${id}: ${ignored} is ignored, ${used} is used (live files win over _proposed, then path order). Fix: rename the id in one file, or delete or move one of them.`;
|
|
9733
9815
|
}
|
|
9734
9816
|
function isPendingProposalDup(e, rulesDir) {
|
|
9735
|
-
return e.problems.every((p) => p.startsWith("duplicate id")) &&
|
|
9817
|
+
return e.problems.every((p) => p.startsWith("duplicate id")) && path18.relative(rulesDir, e.file).split(path18.sep).includes(PROPOSED_DIR);
|
|
9736
9818
|
}
|
|
9737
9819
|
function splitStatement(body) {
|
|
9738
9820
|
const first = body.trim().split(/\n\s*\n/)[0] ?? "";
|
|
@@ -9741,8 +9823,8 @@ function splitStatement(body) {
|
|
|
9741
9823
|
function listMarkdown(dir, includeProposed) {
|
|
9742
9824
|
const out = [];
|
|
9743
9825
|
const walk = (d) => {
|
|
9744
|
-
for (const ent of
|
|
9745
|
-
const full =
|
|
9826
|
+
for (const ent of fs17.readdirSync(d, { withFileTypes: true })) {
|
|
9827
|
+
const full = path18.join(d, ent.name);
|
|
9746
9828
|
if (ent.isDirectory()) {
|
|
9747
9829
|
if (ent.name === PROPOSED_DIR && !includeProposed)
|
|
9748
9830
|
continue;
|
|
@@ -9753,11 +9835,11 @@ function listMarkdown(dir, includeProposed) {
|
|
|
9753
9835
|
}
|
|
9754
9836
|
};
|
|
9755
9837
|
walk(dir);
|
|
9756
|
-
const isProposed = (f) =>
|
|
9838
|
+
const isProposed = (f) => path18.relative(dir, f).split(path18.sep).includes(PROPOSED_DIR);
|
|
9757
9839
|
return out.sort((a, b) => Number(isProposed(a)) - Number(isProposed(b)) || (a < b ? -1 : a > b ? 1 : 0));
|
|
9758
9840
|
}
|
|
9759
9841
|
function loadRulesDir(dir, layer, opts = {}) {
|
|
9760
|
-
if (!
|
|
9842
|
+
if (!fs17.existsSync(dir))
|
|
9761
9843
|
return { rules: [], errors: [] };
|
|
9762
9844
|
const rules = [];
|
|
9763
9845
|
const errors = [];
|
|
@@ -9765,7 +9847,7 @@ function loadRulesDir(dir, layer, opts = {}) {
|
|
|
9765
9847
|
for (const file of listMarkdown(dir, opts.includeProposed ?? false)) {
|
|
9766
9848
|
let parsed;
|
|
9767
9849
|
try {
|
|
9768
|
-
parsed = matter2(
|
|
9850
|
+
parsed = matter2(fs17.readFileSync(file, "utf8"), NO_JS_FRONTMATTER);
|
|
9769
9851
|
} catch (e) {
|
|
9770
9852
|
errors.push({ file, problems: [`frontmatter parse error: ${e.message}`] });
|
|
9771
9853
|
continue;
|
|
@@ -9792,7 +9874,7 @@ function validateKb(opts) {
|
|
|
9792
9874
|
const dups = errors.filter((e) => e.problems.some((p) => p.startsWith("duplicate id")) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9793
9875
|
const pendingDups = errors.filter((e) => isPendingProposalDup(e, opts.rulesDir)).length;
|
|
9794
9876
|
const invalid = errors.filter((e) => !dups.includes(e) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9795
|
-
const proposed = rules.filter((r) =>
|
|
9877
|
+
const proposed = rules.filter((r) => path18.relative(opts.rulesDir, r.file).split(path18.sep).includes(PROPOSED_DIR)).length;
|
|
9796
9878
|
checks.push({ level: "ok", message: `${rules.length + errors.length} rule files read (${rules.length - proposed} live, ${proposed + pendingDups} proposed parsed OK)` });
|
|
9797
9879
|
if (invalid.length)
|
|
9798
9880
|
for (const e of invalid)
|
|
@@ -9817,17 +9899,17 @@ function validateKb(opts) {
|
|
|
9817
9899
|
const key = `${src.ref}\0${file}`;
|
|
9818
9900
|
if (!cache.has(key)) {
|
|
9819
9901
|
const conv = readState2(opts.kbRoot, canon(src.ref)) ? convertedFile(opts.kbRoot, canon(src.ref)) : null;
|
|
9820
|
-
const cached = !!conv &&
|
|
9902
|
+
const cached = !!conv && fs17.existsSync(conv);
|
|
9821
9903
|
cache.set(key, {
|
|
9822
|
-
hay: normalizedViews(
|
|
9823
|
-
convertible: !cached && !PASS_THROUGH.has(
|
|
9904
|
+
hay: normalizedViews(fs17.readFileSync(cached ? conv : file, "utf8")),
|
|
9905
|
+
convertible: !cached && !PASS_THROUGH.has(path18.extname(file).toLowerCase())
|
|
9824
9906
|
});
|
|
9825
9907
|
}
|
|
9826
9908
|
const { hay, convertible } = cache.get(key);
|
|
9827
9909
|
const span = findQuote(hay, src.quote);
|
|
9828
9910
|
if (span) {
|
|
9829
9911
|
verified++;
|
|
9830
|
-
const active = r.status === "active" && !
|
|
9912
|
+
const active = r.status === "active" && !path18.relative(opts.rulesDir, r.file).split(path18.sep).includes(PROPOSED_DIR);
|
|
9831
9913
|
const err = active ? (opts.errata ?? []).find((e) => canon(e.ref) === canon(src.ref) && ((o) => !!o && spansOverlap(o, span))(findQuote(hay, e.quote))) : void 0;
|
|
9832
9914
|
if (err)
|
|
9833
9915
|
checks.push({ level: "warn", message: `${r.id}: grounded in erratum ${err.id} (${err.reason}); re-check or retire it (${r.file})` });
|
|
@@ -9867,7 +9949,7 @@ var init_store2 = __esm({
|
|
|
9867
9949
|
});
|
|
9868
9950
|
|
|
9869
9951
|
// packages/core/dist/knowledge/layers.js
|
|
9870
|
-
import
|
|
9952
|
+
import fs18 from "fs";
|
|
9871
9953
|
function loadKbRules(cfg, kb, opts = {}) {
|
|
9872
9954
|
return loadRulesDir(kbRulesDir(cfg, kb), `kb:${kb}`, opts);
|
|
9873
9955
|
}
|
|
@@ -9879,7 +9961,7 @@ function resolveProjectRules(cfg, project, opts = {}) {
|
|
|
9879
9961
|
const errors = [];
|
|
9880
9962
|
const warnings = [];
|
|
9881
9963
|
for (const kb of subscribedKbs(cfg, project)) {
|
|
9882
|
-
if (!
|
|
9964
|
+
if (!fs18.existsSync(kbDir(cfg, kb))) {
|
|
9883
9965
|
warnings.push(`knowledge base '${kb}' not found (run: squadrant knowledge init ${kb})`);
|
|
9884
9966
|
continue;
|
|
9885
9967
|
}
|
|
@@ -9917,8 +9999,8 @@ var init_layers = __esm({
|
|
|
9917
9999
|
});
|
|
9918
10000
|
|
|
9919
10001
|
// packages/core/dist/knowledge/index-file.js
|
|
9920
|
-
import
|
|
9921
|
-
import
|
|
10002
|
+
import fs19 from "fs";
|
|
10003
|
+
import path19 from "path";
|
|
9922
10004
|
function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
9923
10005
|
return {
|
|
9924
10006
|
kb,
|
|
@@ -9927,18 +10009,18 @@ function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
|
9927
10009
|
};
|
|
9928
10010
|
}
|
|
9929
10011
|
function writeIndex(kbRoot, index) {
|
|
9930
|
-
const file =
|
|
10012
|
+
const file = path19.join(kbRoot, "index.json");
|
|
9931
10013
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
9932
|
-
|
|
9933
|
-
|
|
10014
|
+
fs19.writeFileSync(tmp, JSON.stringify(index, null, 2) + "\n");
|
|
10015
|
+
fs19.renameSync(tmp, file);
|
|
9934
10016
|
return file;
|
|
9935
10017
|
}
|
|
9936
10018
|
function readIndex(kbRoot, kb) {
|
|
9937
|
-
const file =
|
|
9938
|
-
if (!
|
|
10019
|
+
const file = path19.join(kbRoot, "index.json");
|
|
10020
|
+
if (!fs19.existsSync(file))
|
|
9939
10021
|
return { index: null, problem: `index.json missing for kb '${kb}'` };
|
|
9940
10022
|
try {
|
|
9941
|
-
const index = JSON.parse(
|
|
10023
|
+
const index = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
9942
10024
|
if (!Array.isArray(index.rules))
|
|
9943
10025
|
return { index: null, problem: `index.json corrupt for kb '${kb}' (no rules array)` };
|
|
9944
10026
|
return { index };
|
|
@@ -13033,16 +13115,16 @@ var init_js_yaml = __esm({
|
|
|
13033
13115
|
});
|
|
13034
13116
|
|
|
13035
13117
|
// packages/core/dist/knowledge/sources.js
|
|
13036
|
-
import
|
|
13037
|
-
import
|
|
13118
|
+
import fs20 from "fs";
|
|
13119
|
+
import path20 from "path";
|
|
13038
13120
|
import matter3 from "gray-matter";
|
|
13039
13121
|
function loadSources(kbRoot) {
|
|
13040
|
-
const file =
|
|
13041
|
-
if (!
|
|
13122
|
+
const file = path20.join(kbRoot, "sources.yaml");
|
|
13123
|
+
if (!fs20.existsSync(file))
|
|
13042
13124
|
return { sources: [], errors: [] };
|
|
13043
13125
|
let data;
|
|
13044
13126
|
try {
|
|
13045
|
-
data = yaml.load(
|
|
13127
|
+
data = yaml.load(fs20.readFileSync(file, "utf8"));
|
|
13046
13128
|
} catch (e) {
|
|
13047
13129
|
return { sources: [], errors: [`YAML parse error: ${e.message.split("\n")[0]}`] };
|
|
13048
13130
|
}
|
|
@@ -13074,12 +13156,12 @@ function dropEntryBlock(text, entryPath) {
|
|
|
13074
13156
|
return [...lines.slice(0, i), ...lines.slice(j)].join("\n");
|
|
13075
13157
|
}
|
|
13076
13158
|
function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
|
|
13077
|
-
const file =
|
|
13159
|
+
const file = path20.join(kbRoot, "sources.yaml");
|
|
13078
13160
|
const { sources } = loadSources(kbRoot);
|
|
13079
13161
|
const entry = sources.find((s) => s.path === entryPath);
|
|
13080
13162
|
if (!entry)
|
|
13081
13163
|
throw new Error(`no sources.yaml entry with path '${entryPath}' (give it exactly as listed by \`knowledge sources\`)`);
|
|
13082
|
-
const edited = dropEntryBlock(
|
|
13164
|
+
const edited = dropEntryBlock(fs20.readFileSync(file, "utf8"), entryPath);
|
|
13083
13165
|
if (edited === null)
|
|
13084
13166
|
throw new Error(`could not edit sources.yaml for '${entryPath}' (not a plain block entry); remove it by hand, then rerun to strip the refs`);
|
|
13085
13167
|
const res = { dryRun: !!opts.dryRun, entryRemoved: true, stripped: [], stale: [] };
|
|
@@ -13089,7 +13171,7 @@ function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
|
|
|
13089
13171
|
if (kept.length === rule.sources.length)
|
|
13090
13172
|
continue;
|
|
13091
13173
|
res.stripped.push(rule.id);
|
|
13092
|
-
const parsed = matter3(
|
|
13174
|
+
const parsed = matter3(fs20.readFileSync(rule.file, "utf8"), { engines: { javascript: () => {
|
|
13093
13175
|
throw new Error("only YAML front matter is allowed");
|
|
13094
13176
|
} } });
|
|
13095
13177
|
const fm = { ...parsed.data, sources: kept };
|
|
@@ -13105,9 +13187,9 @@ function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
|
|
|
13105
13187
|
if (opts.dryRun)
|
|
13106
13188
|
return res;
|
|
13107
13189
|
for (const w of writes)
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
const name = opts.kbName ??
|
|
13190
|
+
fs20.writeFileSync(w.file, w.text);
|
|
13191
|
+
fs20.writeFileSync(file, edited);
|
|
13192
|
+
const name = opts.kbName ?? path20.basename(kbRoot);
|
|
13111
13193
|
writeIndex(kbRoot, compileIndex(name, loadRulesDir(rulesDir, `kb:${name}`).rules));
|
|
13112
13194
|
return res;
|
|
13113
13195
|
}
|
|
@@ -13138,25 +13220,25 @@ var init_sources = __esm({
|
|
|
13138
13220
|
});
|
|
13139
13221
|
|
|
13140
13222
|
// packages/core/dist/knowledge/match.js
|
|
13141
|
-
import
|
|
13223
|
+
import path21 from "path";
|
|
13142
13224
|
function globMatches(glob, relPath) {
|
|
13143
13225
|
const g = glob.replace(/^\.\//, "");
|
|
13144
13226
|
if (globToRegExp(g).test(relPath))
|
|
13145
13227
|
return true;
|
|
13146
|
-
return !g.includes("/") && globToRegExp(g).test(
|
|
13228
|
+
return !g.includes("/") && globToRegExp(g).test(path21.posix.basename(relPath));
|
|
13147
13229
|
}
|
|
13148
13230
|
function repoRelativePaths(file, cwd, projectRoot) {
|
|
13149
13231
|
const out = /* @__PURE__ */ new Set();
|
|
13150
13232
|
const add = (base) => {
|
|
13151
|
-
const rel =
|
|
13152
|
-
if (!rel || rel.startsWith("..") ||
|
|
13233
|
+
const rel = path21.relative(base, file).split(path21.sep).join("/");
|
|
13234
|
+
if (!rel || rel.startsWith("..") || path21.isAbsolute(rel))
|
|
13153
13235
|
return;
|
|
13154
13236
|
out.add(rel);
|
|
13155
13237
|
const wt = /^\.worktrees\/[^/]+\/(.+)$/.exec(rel);
|
|
13156
13238
|
if (wt)
|
|
13157
13239
|
out.add(wt[1]);
|
|
13158
13240
|
};
|
|
13159
|
-
if (
|
|
13241
|
+
if (path21.isAbsolute(file)) {
|
|
13160
13242
|
add(cwd);
|
|
13161
13243
|
add(projectRoot);
|
|
13162
13244
|
} else
|
|
@@ -13205,25 +13287,27 @@ var init_match = __esm({
|
|
|
13205
13287
|
});
|
|
13206
13288
|
|
|
13207
13289
|
// packages/core/dist/knowledge/audit.js
|
|
13208
|
-
import
|
|
13209
|
-
import
|
|
13210
|
-
import
|
|
13290
|
+
import fs21 from "fs";
|
|
13291
|
+
import os9 from "os";
|
|
13292
|
+
import path22 from "path";
|
|
13211
13293
|
function appendAudit(events, sink = {}) {
|
|
13212
13294
|
if (!events.length)
|
|
13213
13295
|
return;
|
|
13214
13296
|
try {
|
|
13215
13297
|
const ts = (sink.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
13298
|
+
const task = (sink.env ?? process.env).SQUADRANT_CREW_TASK_ID;
|
|
13216
13299
|
const dir = sink.dir ?? defaultAuditDir();
|
|
13217
|
-
const file =
|
|
13218
|
-
|
|
13219
|
-
const fd =
|
|
13300
|
+
const file = path22.join(dir, `${ts.slice(0, 7)}.${sink.machineId ?? auditMachineId()}.jsonl`);
|
|
13301
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
13302
|
+
const fd = fs21.openSync(file, "a");
|
|
13220
13303
|
try {
|
|
13221
13304
|
for (const e of events) {
|
|
13222
13305
|
const q = e.query === void 0 ? {} : { query: e.query.slice(0, AUDIT_QUERY_CHARS) };
|
|
13223
|
-
|
|
13306
|
+
const t = task && e.task === void 0 && DELIVERY_EVENTS.has(e.event) ? { task } : {};
|
|
13307
|
+
fs21.writeSync(fd, JSON.stringify({ ts, ...e, ...q, ...t }) + "\n");
|
|
13224
13308
|
}
|
|
13225
13309
|
} finally {
|
|
13226
|
-
|
|
13310
|
+
fs21.closeSync(fd);
|
|
13227
13311
|
}
|
|
13228
13312
|
} catch (err) {
|
|
13229
13313
|
console.error(`squadrant audit: log write failed: ${err.message}`);
|
|
@@ -13232,18 +13316,22 @@ function appendAudit(events, sink = {}) {
|
|
|
13232
13316
|
function ruleAuditScope(rule) {
|
|
13233
13317
|
return rule.layer.startsWith("kb:") ? { kb: rule.layer.slice(3), level: "group" } : { kb: "", level: "project" };
|
|
13234
13318
|
}
|
|
13235
|
-
var AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
|
|
13319
|
+
var OUTCOME_SOURCES, outcomeSource, RATED_SOURCES, DELIVERY_EVENTS, AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
|
|
13236
13320
|
var init_audit = __esm({
|
|
13237
13321
|
"packages/core/dist/knowledge/audit.js"() {
|
|
13322
|
+
OUTCOME_SOURCES = ["operator", "review", "crew", "reconcile", "judge", "check"];
|
|
13323
|
+
outcomeSource = (e) => typeof e.source === "string" && e.source ? e.source : "operator";
|
|
13324
|
+
RATED_SOURCES = /* @__PURE__ */ new Set(["operator", "review", "judge", "check"]);
|
|
13325
|
+
DELIVERY_EVENTS = /* @__PURE__ */ new Set(["item.surfaced", "item.shown", "item.searched"]);
|
|
13238
13326
|
AUDIT_QUERY_CHARS = 200;
|
|
13239
|
-
defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR ||
|
|
13240
|
-
auditMachineId = () =>
|
|
13327
|
+
defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR || path22.join(os9.homedir(), ".local", "state", "squadrant", "audit");
|
|
13328
|
+
auditMachineId = () => os9.hostname().replace(/[^A-Za-z0-9_-]/g, "_") || "host";
|
|
13241
13329
|
}
|
|
13242
13330
|
});
|
|
13243
13331
|
|
|
13244
13332
|
// packages/core/dist/knowledge/inject.js
|
|
13245
|
-
import
|
|
13246
|
-
import
|
|
13333
|
+
import fs22 from "fs";
|
|
13334
|
+
import path23 from "path";
|
|
13247
13335
|
function sessionOrder(a, b) {
|
|
13248
13336
|
const proj = (r) => r.layer.startsWith("project:") ? 0 : 1;
|
|
13249
13337
|
const mod = (r) => r.modality === "must-not" ? 0 : 1;
|
|
@@ -13317,7 +13405,7 @@ function promptText(prompt) {
|
|
|
13317
13405
|
if (!m)
|
|
13318
13406
|
return prompt;
|
|
13319
13407
|
try {
|
|
13320
|
-
return
|
|
13408
|
+
return fs22.readFileSync(m[1], "utf8").slice(0, MAX_PROMPT_CHARS * 2);
|
|
13321
13409
|
} catch {
|
|
13322
13410
|
return prompt;
|
|
13323
13411
|
}
|
|
@@ -13361,39 +13449,39 @@ function rulesFallbackPointer(kbs) {
|
|
|
13361
13449
|
"Before changing code, look up the rules that apply with `squadrant rules search <terms>`, then `squadrant rules show <id>`."
|
|
13362
13450
|
].join("\n");
|
|
13363
13451
|
}
|
|
13364
|
-
function injectStateDir(
|
|
13365
|
-
return
|
|
13452
|
+
function injectStateDir(stateRoot2) {
|
|
13453
|
+
return path23.join(stateRoot2, "rules-inject");
|
|
13366
13454
|
}
|
|
13367
|
-
function readSeenRules(
|
|
13455
|
+
function readSeenRules(stateRoot2, sessionId) {
|
|
13368
13456
|
try {
|
|
13369
|
-
const ids = JSON.parse(
|
|
13457
|
+
const ids = JSON.parse(fs22.readFileSync(sessionFile(stateRoot2, sessionId), "utf8"));
|
|
13370
13458
|
return new Set(Array.isArray(ids) ? ids.filter((x) => typeof x === "string") : []);
|
|
13371
13459
|
} catch {
|
|
13372
13460
|
return /* @__PURE__ */ new Set();
|
|
13373
13461
|
}
|
|
13374
13462
|
}
|
|
13375
|
-
function recordSeenRules(
|
|
13463
|
+
function recordSeenRules(stateRoot2, sessionId, ids) {
|
|
13376
13464
|
if (!ids.length)
|
|
13377
13465
|
return;
|
|
13378
|
-
const seen = readSeenRules(
|
|
13466
|
+
const seen = readSeenRules(stateRoot2, sessionId);
|
|
13379
13467
|
for (const id of ids)
|
|
13380
13468
|
seen.add(id);
|
|
13381
|
-
|
|
13382
|
-
|
|
13469
|
+
fs22.mkdirSync(injectStateDir(stateRoot2), { recursive: true });
|
|
13470
|
+
fs22.writeFileSync(sessionFile(stateRoot2, sessionId), JSON.stringify([...seen]));
|
|
13383
13471
|
}
|
|
13384
|
-
function pruneSeenRules(
|
|
13385
|
-
const dir = injectStateDir(
|
|
13472
|
+
function pruneSeenRules(stateRoot2, now = Date.now()) {
|
|
13473
|
+
const dir = injectStateDir(stateRoot2);
|
|
13386
13474
|
let names;
|
|
13387
13475
|
try {
|
|
13388
|
-
names =
|
|
13476
|
+
names = fs22.readdirSync(dir);
|
|
13389
13477
|
} catch {
|
|
13390
13478
|
return;
|
|
13391
13479
|
}
|
|
13392
13480
|
for (const n of names) {
|
|
13393
|
-
const f =
|
|
13481
|
+
const f = path23.join(dir, n);
|
|
13394
13482
|
try {
|
|
13395
|
-
if (now -
|
|
13396
|
-
|
|
13483
|
+
if (now - fs22.statSync(f).mtimeMs > SEEN_TTL_MS)
|
|
13484
|
+
fs22.unlinkSync(f);
|
|
13397
13485
|
} catch {
|
|
13398
13486
|
}
|
|
13399
13487
|
}
|
|
@@ -13402,8 +13490,8 @@ async function withBudget(fn, budgetMs = INJECT_BUDGET_MS) {
|
|
|
13402
13490
|
const start = Date.now();
|
|
13403
13491
|
let timer;
|
|
13404
13492
|
try {
|
|
13405
|
-
const timeout = new Promise((
|
|
13406
|
-
timer = setTimeout(() =>
|
|
13493
|
+
const timeout = new Promise((resolve5) => {
|
|
13494
|
+
timer = setTimeout(() => resolve5(null), budgetMs);
|
|
13407
13495
|
});
|
|
13408
13496
|
const out = await Promise.race([Promise.resolve().then(fn), timeout]);
|
|
13409
13497
|
return Date.now() - start > budgetMs ? null : out;
|
|
@@ -13421,7 +13509,7 @@ function resolveInjectProject(cfg, cwd, env, project) {
|
|
|
13421
13509
|
let bestLen = -1;
|
|
13422
13510
|
for (const [name, pc] of Object.entries(cfg.projects)) {
|
|
13423
13511
|
const root = resolveHome(pc.path);
|
|
13424
|
-
if ((cwd === root || cwd.startsWith(root +
|
|
13512
|
+
if ((cwd === root || cwd.startsWith(root + path23.sep)) && root.length > bestLen) {
|
|
13425
13513
|
best = name;
|
|
13426
13514
|
bestLen = root.length;
|
|
13427
13515
|
}
|
|
@@ -13440,7 +13528,7 @@ function audit(i, project, session, trigger, items) {
|
|
|
13440
13528
|
chars: line(rule).length,
|
|
13441
13529
|
agent: i.agent ?? "claude",
|
|
13442
13530
|
...session ? { session } : {}
|
|
13443
|
-
})), i.audit);
|
|
13531
|
+
})), { env: i.env, ...i.audit });
|
|
13444
13532
|
}
|
|
13445
13533
|
function computeRulesInjection(i) {
|
|
13446
13534
|
if (i.env.SQUADRANT_RULES_INJECT === "0")
|
|
@@ -13529,23 +13617,23 @@ var init_inject = __esm({
|
|
|
13529
13617
|
READ_TOOLS = /* @__PURE__ */ new Set(["read", "read_file", "readfile", "view"]);
|
|
13530
13618
|
SHELL_TOOLS = /* @__PURE__ */ new Set(["bash", "shell", "run_shell_command", "local_shell", "exec_command"]);
|
|
13531
13619
|
SHELL_META = /[|;&<>$`()]/;
|
|
13532
|
-
sessionFile = (
|
|
13620
|
+
sessionFile = (stateRoot2, sessionId) => path23.join(injectStateDir(stateRoot2), `${sessionId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
13533
13621
|
MAX_SUPPRESSED_LOGGED = 10;
|
|
13534
13622
|
}
|
|
13535
13623
|
});
|
|
13536
13624
|
|
|
13537
13625
|
// packages/core/dist/knowledge/migrate.js
|
|
13538
|
-
import
|
|
13539
|
-
import
|
|
13626
|
+
import fs23 from "fs";
|
|
13627
|
+
import path24 from "path";
|
|
13540
13628
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
13541
13629
|
function findLegacyKbs(cfg) {
|
|
13542
13630
|
const root = legacyRoot(cfg);
|
|
13543
|
-
if (!
|
|
13631
|
+
if (!fs23.existsSync(root))
|
|
13544
13632
|
return [];
|
|
13545
|
-
return
|
|
13633
|
+
return fs23.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && KB_NAME_RE.test(e.name)).map((e) => e.name).sort();
|
|
13546
13634
|
}
|
|
13547
13635
|
function ensureGitRepo(dir) {
|
|
13548
|
-
if (
|
|
13636
|
+
if (fs23.existsSync(path24.join(dir, ".git")))
|
|
13549
13637
|
return false;
|
|
13550
13638
|
try {
|
|
13551
13639
|
execFileSync6("git", ["init", "-q", dir], { stdio: "ignore" });
|
|
@@ -13555,14 +13643,14 @@ function ensureGitRepo(dir) {
|
|
|
13555
13643
|
}
|
|
13556
13644
|
}
|
|
13557
13645
|
function ensureKbGitignore(dir) {
|
|
13558
|
-
const file =
|
|
13559
|
-
if (
|
|
13646
|
+
const file = path24.join(dir, ".gitignore");
|
|
13647
|
+
if (fs23.existsSync(file))
|
|
13560
13648
|
return false;
|
|
13561
|
-
|
|
13649
|
+
fs23.writeFileSync(file, KB_GITIGNORE);
|
|
13562
13650
|
return true;
|
|
13563
13651
|
}
|
|
13564
13652
|
function findLegacyOverlays(cfg) {
|
|
13565
|
-
return Object.keys(cfg.projects).filter((p) =>
|
|
13653
|
+
return Object.keys(cfg.projects).filter((p) => fs23.existsSync(legacyOverlay(cfg, p))).sort();
|
|
13566
13654
|
}
|
|
13567
13655
|
function moveLegacyOverlays(cfg, opts = {}) {
|
|
13568
13656
|
return findLegacyOverlays(cfg).map((project) => {
|
|
@@ -13571,18 +13659,18 @@ function moveLegacyOverlays(cfg, opts = {}) {
|
|
|
13571
13659
|
const kb = `overlay:${project}`;
|
|
13572
13660
|
if (!to)
|
|
13573
13661
|
return { kb, from, to: "", action: "skipped", note: "no group KB or knowledgeHome for this project; set one, then re-run" };
|
|
13574
|
-
if (
|
|
13662
|
+
if (fs23.existsSync(to))
|
|
13575
13663
|
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13576
13664
|
if (opts.dryRun)
|
|
13577
13665
|
return { kb, from, to, action: "would-move" };
|
|
13578
|
-
|
|
13666
|
+
fs23.mkdirSync(path24.dirname(to), { recursive: true });
|
|
13579
13667
|
try {
|
|
13580
|
-
|
|
13668
|
+
fs23.renameSync(from, to);
|
|
13581
13669
|
return { kb, from, to, action: "moved" };
|
|
13582
13670
|
} catch (e) {
|
|
13583
13671
|
if (e.code !== "EXDEV")
|
|
13584
13672
|
throw e;
|
|
13585
|
-
|
|
13673
|
+
fs23.cpSync(from, to, { recursive: true });
|
|
13586
13674
|
return { kb, from, to, action: "copied", note: "cross-device: original left in place, delete it yourself once verified" };
|
|
13587
13675
|
}
|
|
13588
13676
|
});
|
|
@@ -13592,32 +13680,32 @@ function moveLegacyKbs(cfg, opts = {}) {
|
|
|
13592
13680
|
return findLegacyKbs(cfg).map((kb) => {
|
|
13593
13681
|
const from = legacyKbDir(hub, kb);
|
|
13594
13682
|
const to = kbDir(cfg, kb);
|
|
13595
|
-
if (
|
|
13683
|
+
if (fs23.existsSync(to))
|
|
13596
13684
|
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13597
13685
|
if (opts.dryRun)
|
|
13598
13686
|
return { kb, from, to, action: "would-move" };
|
|
13599
|
-
|
|
13687
|
+
fs23.mkdirSync(path24.dirname(to), { recursive: true });
|
|
13600
13688
|
let action = "moved";
|
|
13601
13689
|
try {
|
|
13602
|
-
|
|
13690
|
+
fs23.renameSync(from, to);
|
|
13603
13691
|
} catch (e) {
|
|
13604
13692
|
if (e.code !== "EXDEV")
|
|
13605
13693
|
throw e;
|
|
13606
|
-
|
|
13694
|
+
fs23.cpSync(from, to, { recursive: true });
|
|
13607
13695
|
action = "copied";
|
|
13608
13696
|
}
|
|
13609
13697
|
const notes = [];
|
|
13610
|
-
const oldRules =
|
|
13611
|
-
if (
|
|
13612
|
-
|
|
13613
|
-
|
|
13698
|
+
const oldRules = path24.join(to, "rules"), newRules = path24.join(to, "shared", "rules");
|
|
13699
|
+
if (fs23.existsSync(oldRules) && !fs23.existsSync(newRules)) {
|
|
13700
|
+
fs23.mkdirSync(path24.dirname(newRules), { recursive: true });
|
|
13701
|
+
fs23.renameSync(oldRules, newRules);
|
|
13614
13702
|
notes.push("rules/ -> shared/rules/");
|
|
13615
13703
|
}
|
|
13616
13704
|
if (ensureGitRepo(to))
|
|
13617
13705
|
notes.push("git init");
|
|
13618
13706
|
if (ensureKbGitignore(to))
|
|
13619
13707
|
notes.push("wrote .gitignore (raw/, .converted/)");
|
|
13620
|
-
if (
|
|
13708
|
+
if (fs23.existsSync(path24.join(to, "index.json")))
|
|
13621
13709
|
notes.push(`index.json holds old paths: run squadrant knowledge reindex ${kb}`);
|
|
13622
13710
|
if (action === "copied")
|
|
13623
13711
|
notes.push("cross-device: original left in place, delete it yourself once verified");
|
|
@@ -13629,22 +13717,22 @@ var init_migrate = __esm({
|
|
|
13629
13717
|
"packages/core/dist/knowledge/migrate.js"() {
|
|
13630
13718
|
init_dist();
|
|
13631
13719
|
init_paths();
|
|
13632
|
-
legacyRoot = (cfg) =>
|
|
13720
|
+
legacyRoot = (cfg) => path24.join(resolveHome(cfg.hubVault), "knowledge");
|
|
13633
13721
|
KB_GITIGNORE = "# squadrant KB defaults: source documents stay local\nraw/\n.converted/\n";
|
|
13634
|
-
legacyOverlay = (cfg, project) =>
|
|
13722
|
+
legacyOverlay = (cfg, project) => path24.join(resolveHome(cfg.projects[project].spokeVault), "knowledge", "rules");
|
|
13635
13723
|
}
|
|
13636
13724
|
});
|
|
13637
13725
|
|
|
13638
13726
|
// packages/core/dist/knowledge/errata.js
|
|
13639
|
-
import
|
|
13640
|
-
import
|
|
13727
|
+
import fs24 from "fs";
|
|
13728
|
+
import path25 from "path";
|
|
13641
13729
|
function loadErrata(kbRoot) {
|
|
13642
|
-
const file =
|
|
13643
|
-
if (!
|
|
13730
|
+
const file = path25.join(kbRoot, "errata.yaml");
|
|
13731
|
+
if (!fs24.existsSync(file))
|
|
13644
13732
|
return { errata: [], errors: [] };
|
|
13645
13733
|
let data;
|
|
13646
13734
|
try {
|
|
13647
|
-
data = yaml.load(
|
|
13735
|
+
data = yaml.load(fs24.readFileSync(file, "utf8"), { schema: yaml.JSON_SCHEMA });
|
|
13648
13736
|
} catch (e) {
|
|
13649
13737
|
return { errata: [], errors: [`YAML parse error: ${e.message.split("\n")[0]}`] };
|
|
13650
13738
|
}
|
|
@@ -13674,7 +13762,7 @@ function orphanedErrata(kbRoot, sources, errata, refs) {
|
|
|
13674
13762
|
continue;
|
|
13675
13763
|
if (!hays.has(ref)) {
|
|
13676
13764
|
const conv = readState2(kbRoot, ref) ? convertedFile(kbRoot, ref) : null;
|
|
13677
|
-
hays.set(ref, conv &&
|
|
13765
|
+
hays.set(ref, conv && fs24.existsSync(conv) ? normalizedViews(fs24.readFileSync(conv, "utf8")) : null);
|
|
13678
13766
|
}
|
|
13679
13767
|
const hay = hays.get(ref);
|
|
13680
13768
|
if (hay && !findQuote(hay, e.quote))
|
|
@@ -13692,9 +13780,9 @@ var init_errata = __esm({
|
|
|
13692
13780
|
});
|
|
13693
13781
|
|
|
13694
13782
|
// packages/core/dist/knowledge/anchor.js
|
|
13695
|
-
import
|
|
13783
|
+
import fs25 from "fs";
|
|
13696
13784
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
13697
|
-
import
|
|
13785
|
+
import path26 from "path";
|
|
13698
13786
|
function gitFiles(root) {
|
|
13699
13787
|
try {
|
|
13700
13788
|
const out = execFileSync7("git", ["ls-files", "-co", "--exclude-standard", "-z"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024 });
|
|
@@ -13708,13 +13796,13 @@ function* repoFiles(root, maxFiles) {
|
|
|
13708
13796
|
const listed = gitFiles(root);
|
|
13709
13797
|
if (listed) {
|
|
13710
13798
|
for (const rel of listed) {
|
|
13711
|
-
if (!CODE_EXT.has(
|
|
13799
|
+
if (!CODE_EXT.has(path26.extname(rel)) || rel.split("/").slice(0, -1).some((d) => SKIP_DIRS.has(d)))
|
|
13712
13800
|
continue;
|
|
13713
13801
|
if (++count > maxFiles) {
|
|
13714
13802
|
console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
|
|
13715
13803
|
return;
|
|
13716
13804
|
}
|
|
13717
|
-
yield
|
|
13805
|
+
yield path26.join(root, rel);
|
|
13718
13806
|
}
|
|
13719
13807
|
return;
|
|
13720
13808
|
}
|
|
@@ -13723,20 +13811,20 @@ function* repoFiles(root, maxFiles) {
|
|
|
13723
13811
|
const dir = stack.pop();
|
|
13724
13812
|
let ents;
|
|
13725
13813
|
try {
|
|
13726
|
-
ents =
|
|
13814
|
+
ents = fs25.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? 1 : -1);
|
|
13727
13815
|
} catch {
|
|
13728
13816
|
continue;
|
|
13729
13817
|
}
|
|
13730
13818
|
for (const ent of ents) {
|
|
13731
13819
|
if (ent.isDirectory()) {
|
|
13732
13820
|
if (!SKIP_DIRS.has(ent.name))
|
|
13733
|
-
stack.push(
|
|
13734
|
-
} else if (ent.isFile() && CODE_EXT.has(
|
|
13821
|
+
stack.push(path26.join(dir, ent.name));
|
|
13822
|
+
} else if (ent.isFile() && CODE_EXT.has(path26.extname(ent.name))) {
|
|
13735
13823
|
if (++count > maxFiles) {
|
|
13736
13824
|
console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
|
|
13737
13825
|
return;
|
|
13738
13826
|
}
|
|
13739
|
-
yield
|
|
13827
|
+
yield path26.join(dir, ent.name);
|
|
13740
13828
|
}
|
|
13741
13829
|
}
|
|
13742
13830
|
}
|
|
@@ -13745,11 +13833,11 @@ function scanRepo(root, maxFiles) {
|
|
|
13745
13833
|
const out = [];
|
|
13746
13834
|
for (const file of repoFiles(root, maxFiles)) {
|
|
13747
13835
|
try {
|
|
13748
|
-
if (
|
|
13836
|
+
if (fs25.statSync(file).size > MAX_FILE_BYTES)
|
|
13749
13837
|
continue;
|
|
13750
|
-
const text =
|
|
13751
|
-
const rel =
|
|
13752
|
-
out.push({ rel, text, hay: `${
|
|
13838
|
+
const text = fs25.readFileSync(file, "utf8");
|
|
13839
|
+
const rel = path26.relative(root, file).split(path26.sep).join("/");
|
|
13840
|
+
out.push({ rel, text, hay: `${path26.relative(root, file)}
|
|
13753
13841
|
${text}`.toLowerCase() });
|
|
13754
13842
|
} catch {
|
|
13755
13843
|
continue;
|
|
@@ -13759,7 +13847,7 @@ ${text}`.toLowerCase() });
|
|
|
13759
13847
|
}
|
|
13760
13848
|
function computeAnchor(repoRoot, triggers, cache, maxFiles = MAX_FILES) {
|
|
13761
13849
|
const terms2 = anchorTerms(triggers);
|
|
13762
|
-
if (!terms2.length || !
|
|
13850
|
+
if (!terms2.length || !fs25.existsSync(repoRoot))
|
|
13763
13851
|
return null;
|
|
13764
13852
|
let files = cache?.get(repoRoot);
|
|
13765
13853
|
if (!files) {
|
|
@@ -13795,8 +13883,8 @@ var init_anchor = __esm({
|
|
|
13795
13883
|
});
|
|
13796
13884
|
|
|
13797
13885
|
// packages/core/dist/knowledge/extract.js
|
|
13798
|
-
import
|
|
13799
|
-
import
|
|
13886
|
+
import fs26 from "fs";
|
|
13887
|
+
import path27 from "path";
|
|
13800
13888
|
import matter4 from "gray-matter";
|
|
13801
13889
|
function serialize(fm, body) {
|
|
13802
13890
|
return matter4.stringify(body.trim() + "\n", clean(fm));
|
|
@@ -13864,13 +13952,13 @@ function applyCandidates(input, opts) {
|
|
|
13864
13952
|
}
|
|
13865
13953
|
const st = readState2(kbRoot, cand.source);
|
|
13866
13954
|
const convFile = st ? convertedFile(kbRoot, cand.source) : null;
|
|
13867
|
-
if (!st || !
|
|
13955
|
+
if (!st || !fs26.existsSync(convFile)) {
|
|
13868
13956
|
res.rejected.push({ id: cand.id, source: cand.source, reason: "unknown source (not ingested)" });
|
|
13869
13957
|
continue;
|
|
13870
13958
|
}
|
|
13871
13959
|
let hay = norm.get(cand.source);
|
|
13872
13960
|
if (!hay) {
|
|
13873
|
-
hay = normalizedViews(
|
|
13961
|
+
hay = normalizedViews(fs26.readFileSync(convFile, "utf8"));
|
|
13874
13962
|
norm.set(cand.source, hay);
|
|
13875
13963
|
}
|
|
13876
13964
|
const tot = totals.get(cand.source) ?? { total: 0, dropped: 0 };
|
|
@@ -13932,7 +14020,7 @@ ${cand.rationale.trim()}` : "");
|
|
|
13932
14020
|
supersedes: [],
|
|
13933
14021
|
conflictsWith: []
|
|
13934
14022
|
};
|
|
13935
|
-
work.set(id, { fm: fm2, body: bodyOf(), file:
|
|
14023
|
+
work.set(id, { fm: fm2, body: bodyOf(), file: path27.join(rulesDir, cand.domain, `${id}.md`), dirty: true, isNew: true });
|
|
13936
14024
|
res.created.push(id);
|
|
13937
14025
|
ev2("item.applied", id);
|
|
13938
14026
|
continue;
|
|
@@ -13949,24 +14037,24 @@ ${cand.rationale.trim()}` : "");
|
|
|
13949
14037
|
supersedes: [],
|
|
13950
14038
|
conflictsWith: contradicted
|
|
13951
14039
|
};
|
|
13952
|
-
const dir =
|
|
13953
|
-
const file =
|
|
14040
|
+
const dir = path27.join(rulesDir, PROPOSED_DIR);
|
|
14041
|
+
const file = path27.join(dir, `${id}@${st.sha.slice(0, SHA_LEN)}.md`);
|
|
13954
14042
|
const text = serialize(fm, bodyOf());
|
|
13955
|
-
if (
|
|
14043
|
+
if (fs26.existsSync(file) && fs26.readFileSync(file, "utf8") === text)
|
|
13956
14044
|
continue;
|
|
13957
|
-
|
|
13958
|
-
for (const other of
|
|
13959
|
-
if (!other.startsWith(`${id}@`) || !other.endsWith(".md") ||
|
|
14045
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
14046
|
+
for (const other of fs26.readdirSync(dir)) {
|
|
14047
|
+
if (!other.startsWith(`${id}@`) || !other.endsWith(".md") || path27.join(dir, other) === file)
|
|
13960
14048
|
continue;
|
|
13961
|
-
const old = matter4(
|
|
14049
|
+
const old = matter4(fs26.readFileSync(path27.join(dir, other), "utf8"));
|
|
13962
14050
|
if (!old.data.sources?.some((s) => canon(s.ref) === canon(cand.source)))
|
|
13963
14051
|
continue;
|
|
13964
|
-
|
|
13965
|
-
|
|
14052
|
+
fs26.writeFileSync(path27.join(dir, other.replace(/\.md$/, ".superseded")), matter4.stringify(old.content, { ...old.data, supersededBy: path27.basename(file) }));
|
|
14053
|
+
fs26.rmSync(path27.join(dir, other));
|
|
13966
14054
|
res.superseded.push(other.replace(/\.md$/, ""));
|
|
13967
14055
|
ev2("item.superseded", id);
|
|
13968
14056
|
}
|
|
13969
|
-
|
|
14057
|
+
fs26.writeFileSync(file, text);
|
|
13970
14058
|
res.proposed.push(`${id}@${st.sha.slice(0, SHA_LEN)}`);
|
|
13971
14059
|
ev2("item.proposed", id);
|
|
13972
14060
|
}
|
|
@@ -13995,10 +14083,10 @@ ${cand.rationale.trim()}` : "");
|
|
|
13995
14083
|
for (const w of work.values()) {
|
|
13996
14084
|
if (!w.dirty)
|
|
13997
14085
|
continue;
|
|
13998
|
-
|
|
14086
|
+
fs26.mkdirSync(path27.dirname(w.file), { recursive: true });
|
|
13999
14087
|
const text = serialize(w.fm, w.body);
|
|
14000
|
-
if (!
|
|
14001
|
-
|
|
14088
|
+
if (!fs26.existsSync(w.file) || fs26.readFileSync(w.file, "utf8") !== text)
|
|
14089
|
+
fs26.writeFileSync(w.file, text);
|
|
14002
14090
|
}
|
|
14003
14091
|
res.orphanedErrata = orphanedErrata(kbRoot, opts.sources ?? [], opts.errata ?? [], totals.keys());
|
|
14004
14092
|
for (const [ref, t] of totals)
|
|
@@ -14034,20 +14122,20 @@ var init_extract = __esm({
|
|
|
14034
14122
|
});
|
|
14035
14123
|
|
|
14036
14124
|
// packages/core/dist/knowledge/schedule.js
|
|
14037
|
-
import
|
|
14038
|
-
import
|
|
14039
|
-
function readSchedule(
|
|
14125
|
+
import fs27 from "fs";
|
|
14126
|
+
import path28 from "path";
|
|
14127
|
+
function readSchedule(stateRoot2) {
|
|
14040
14128
|
try {
|
|
14041
|
-
return JSON.parse(
|
|
14129
|
+
return JSON.parse(fs27.readFileSync(scheduleFile(stateRoot2), "utf8"));
|
|
14042
14130
|
} catch {
|
|
14043
14131
|
return {};
|
|
14044
14132
|
}
|
|
14045
14133
|
}
|
|
14046
|
-
function writeSchedule(
|
|
14047
|
-
|
|
14048
|
-
const tmp = `${scheduleFile(
|
|
14049
|
-
|
|
14050
|
-
|
|
14134
|
+
function writeSchedule(stateRoot2, s) {
|
|
14135
|
+
fs27.mkdirSync(stateRoot2, { recursive: true });
|
|
14136
|
+
const tmp = `${scheduleFile(stateRoot2)}.${process.pid}.tmp`;
|
|
14137
|
+
fs27.writeFileSync(tmp, JSON.stringify(s, null, 2) + "\n");
|
|
14138
|
+
fs27.renameSync(tmp, scheduleFile(stateRoot2));
|
|
14051
14139
|
}
|
|
14052
14140
|
function snapshotSources(kbRoot, sources) {
|
|
14053
14141
|
const out = {};
|
|
@@ -14127,7 +14215,7 @@ async function tickKnowledgeSchedule(d) {
|
|
|
14127
14215
|
continue;
|
|
14128
14216
|
try {
|
|
14129
14217
|
const root = kbDir(d.cfg, kb);
|
|
14130
|
-
if (!
|
|
14218
|
+
if (!fs27.existsSync(root))
|
|
14131
14219
|
continue;
|
|
14132
14220
|
const { sources } = loadSources(root);
|
|
14133
14221
|
if (!sources.length)
|
|
@@ -14192,7 +14280,7 @@ var init_schedule = __esm({
|
|
|
14192
14280
|
init_sources();
|
|
14193
14281
|
RUNNING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
14194
14282
|
DAY_MS = 24 * 60 * 60 * 1e3;
|
|
14195
|
-
scheduleFile = (
|
|
14283
|
+
scheduleFile = (stateRoot2) => path28.join(stateRoot2, "knowledge-schedule.json");
|
|
14196
14284
|
reconcileConfig = (cfg, kb) => ({ ...DEFAULT_RECONCILE, ...kbConfigs(cfg)[kb]?.reconcile });
|
|
14197
14285
|
}
|
|
14198
14286
|
});
|
|
@@ -14200,19 +14288,19 @@ var init_schedule = __esm({
|
|
|
14200
14288
|
// packages/core/dist/knowledge/review.js
|
|
14201
14289
|
import crypto4 from "crypto";
|
|
14202
14290
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
14203
|
-
import
|
|
14204
|
-
import
|
|
14291
|
+
import fs28 from "fs";
|
|
14292
|
+
import path29 from "path";
|
|
14205
14293
|
import matter5 from "gray-matter";
|
|
14206
14294
|
function readEscalations(kbRoot) {
|
|
14207
14295
|
try {
|
|
14208
|
-
return JSON.parse(
|
|
14296
|
+
return JSON.parse(fs28.readFileSync(escalationsFile(kbRoot), "utf8")).items;
|
|
14209
14297
|
} catch {
|
|
14210
14298
|
return [];
|
|
14211
14299
|
}
|
|
14212
14300
|
}
|
|
14213
14301
|
function writeEscalations(kbRoot, items) {
|
|
14214
|
-
|
|
14215
|
-
|
|
14302
|
+
fs28.mkdirSync(kbRoot, { recursive: true });
|
|
14303
|
+
fs28.writeFileSync(escalationsFile(kbRoot), JSON.stringify({ items }, null, 2) + "\n");
|
|
14216
14304
|
}
|
|
14217
14305
|
function addEscalation(kbRoot, e) {
|
|
14218
14306
|
const items = readEscalations(kbRoot);
|
|
@@ -14222,8 +14310,8 @@ function addEscalation(kbRoot, e) {
|
|
|
14222
14310
|
return true;
|
|
14223
14311
|
}
|
|
14224
14312
|
function appendVerdict(kbRoot, v2, now) {
|
|
14225
|
-
|
|
14226
|
-
|
|
14313
|
+
fs28.mkdirSync(kbRoot, { recursive: true });
|
|
14314
|
+
fs28.appendFileSync(verdictsFile(kbRoot), JSON.stringify({ ts: now.toISOString(), ...v2 }) + "\n");
|
|
14227
14315
|
}
|
|
14228
14316
|
function priorityOfRef(c, ref) {
|
|
14229
14317
|
if (ref.startsWith("agent:"))
|
|
@@ -14240,20 +14328,20 @@ function loadProposals(c) {
|
|
|
14240
14328
|
const byId = new Map(live.map((r) => [r.id, r]));
|
|
14241
14329
|
const proposals = [];
|
|
14242
14330
|
const pdirs = /* @__PURE__ */ new Set();
|
|
14243
|
-
for (const f of
|
|
14244
|
-
if (
|
|
14331
|
+
for (const f of fs28.existsSync(dir) ? walkDirs(dir) : [])
|
|
14332
|
+
if (path29.basename(f) === PROPOSED_DIR)
|
|
14245
14333
|
pdirs.add(f);
|
|
14246
14334
|
for (const pd of pdirs) {
|
|
14247
14335
|
for (const rule of loadRulesDir(pd, `kb:${c.kb}`, { includeProposed: true }).rules) {
|
|
14248
|
-
proposals.push({ key:
|
|
14336
|
+
proposals.push({ key: path29.basename(rule.file).replace(/\.md$/, ""), rule, live: byId.get(rule.id) });
|
|
14249
14337
|
}
|
|
14250
14338
|
}
|
|
14251
14339
|
return { proposals, live };
|
|
14252
14340
|
}
|
|
14253
14341
|
function walkDirs(dir, out = []) {
|
|
14254
|
-
for (const e of
|
|
14342
|
+
for (const e of fs28.readdirSync(dir, { withFileTypes: true }))
|
|
14255
14343
|
if (e.isDirectory()) {
|
|
14256
|
-
const f =
|
|
14344
|
+
const f = path29.join(dir, e.name);
|
|
14257
14345
|
out.push(f);
|
|
14258
14346
|
walkDirs(f, out);
|
|
14259
14347
|
}
|
|
@@ -14282,7 +14370,7 @@ function promoteProposal(c, p, by, justification, events) {
|
|
|
14282
14370
|
continue;
|
|
14283
14371
|
const o = readFm(other.file);
|
|
14284
14372
|
o.fm.status = "retired";
|
|
14285
|
-
|
|
14373
|
+
fs28.writeFileSync(other.file, fileText(o.fm, o.body));
|
|
14286
14374
|
retired.push(id);
|
|
14287
14375
|
events.push(ev(c, "item.superseded", id));
|
|
14288
14376
|
}
|
|
@@ -14292,16 +14380,16 @@ function promoteProposal(c, p, by, justification, events) {
|
|
|
14292
14380
|
const anchors = anchorsFor(c, { ...next, anchors: p.live?.anchors ?? fm.anchors });
|
|
14293
14381
|
if (anchors)
|
|
14294
14382
|
next.anchors = anchors;
|
|
14295
|
-
const target = p.live?.file ??
|
|
14296
|
-
|
|
14297
|
-
|
|
14298
|
-
|
|
14383
|
+
const target = p.live?.file ?? path29.join(dir, next.domain, `${next.id}.md`);
|
|
14384
|
+
fs28.mkdirSync(path29.dirname(target), { recursive: true });
|
|
14385
|
+
fs28.writeFileSync(target, fileText(next, body));
|
|
14386
|
+
fs28.rmSync(p.rule.file);
|
|
14299
14387
|
events.push(ev(c, "item.approved", next.id), ev(c, "item.applied", next.id));
|
|
14300
14388
|
if (p.live)
|
|
14301
14389
|
events.push(ev(c, "item.superseded", next.id));
|
|
14302
14390
|
}
|
|
14303
14391
|
function dropProposal(c, p, events) {
|
|
14304
|
-
|
|
14392
|
+
fs28.rmSync(p.rule.file);
|
|
14305
14393
|
events.push(ev(c, "item.rejected", p.rule.id, { reason: "reviewer" }));
|
|
14306
14394
|
}
|
|
14307
14395
|
function mergeInto(c, p, targetId, live, events) {
|
|
@@ -14313,8 +14401,8 @@ function mergeInto(c, p, targetId, live, events) {
|
|
|
14313
14401
|
for (const s of p.rule.sources)
|
|
14314
14402
|
if (!tf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
|
|
14315
14403
|
tf.fm.sources.push(s);
|
|
14316
|
-
|
|
14317
|
-
|
|
14404
|
+
fs28.writeFileSync(t.file, fileText(tf.fm, tf.body));
|
|
14405
|
+
fs28.rmSync(p.rule.file);
|
|
14318
14406
|
events.push(ev(c, "item.superseded", p.rule.id), ev(c, "item.applied", targetId));
|
|
14319
14407
|
return true;
|
|
14320
14408
|
}
|
|
@@ -14374,6 +14462,7 @@ function applyDecisions(file, c) {
|
|
|
14374
14462
|
if (v2.action === "flag-violation") {
|
|
14375
14463
|
escalate({ key: `violation:${target.id}`, kind: "code-violation", ruleId: target.id, reasons: ["doc newer than code"], explanation: v2.explanation, needsYou: false });
|
|
14376
14464
|
res.violations.push(target.id);
|
|
14465
|
+
events.push(ev(c, "item.outcome", target.id, { outcome: "violated", source: "reconcile", project: d.codeConflict.project }));
|
|
14377
14466
|
} else if (v2.action === "escalate") {
|
|
14378
14467
|
escalate({ key: `code-vs-doc:${target.id}`, kind: "code-vs-doc", ruleId: target.id, reasons: ["code-vs-doc"], explanation: v2.explanation, evidence: v2.evidence, confidence: conf });
|
|
14379
14468
|
verdict(d, "escalated", { reasons: ["code-vs-doc"], confidence: conf });
|
|
@@ -14392,7 +14481,7 @@ function applyDecisions(file, c) {
|
|
|
14392
14481
|
f.fm.sources[0].loc = d.loc;
|
|
14393
14482
|
const noSource = !rule.sources.length;
|
|
14394
14483
|
if (conf >= threshold && !d.flag && !reasons.length && !noSource) {
|
|
14395
|
-
|
|
14484
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14396
14485
|
res.applied.push(d.item);
|
|
14397
14486
|
events.push(ev(c, "item.applied", rule.id));
|
|
14398
14487
|
verdict(d, "applied", { confidence: conf });
|
|
@@ -14409,7 +14498,7 @@ function applyDecisions(file, c) {
|
|
|
14409
14498
|
if (!reasons.length) {
|
|
14410
14499
|
const f = readFm(rule.file);
|
|
14411
14500
|
f.fm.status = "retired";
|
|
14412
|
-
|
|
14501
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14413
14502
|
res.applied.push(d.item);
|
|
14414
14503
|
events.push(ev(c, "item.archived", rule.id));
|
|
14415
14504
|
verdict(d, "applied", { confidence: conf });
|
|
@@ -14489,7 +14578,7 @@ function applyPair(pd, c, res, threshold, events, kbRoot, now) {
|
|
|
14489
14578
|
for (const [x, y] of [[a, b], [b, a]]) {
|
|
14490
14579
|
const f = readFm(x.file);
|
|
14491
14580
|
f.fm.conflictsWith = [.../* @__PURE__ */ new Set([...f.fm.conflictsWith ?? [], y.id])];
|
|
14492
|
-
|
|
14581
|
+
fs28.writeFileSync(x.file, fileText(f.fm, f.body));
|
|
14493
14582
|
}
|
|
14494
14583
|
return;
|
|
14495
14584
|
}
|
|
@@ -14505,10 +14594,10 @@ function applyPair(pd, c, res, threshold, events, kbRoot, now) {
|
|
|
14505
14594
|
if (!kf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
|
|
14506
14595
|
kf.fm.sources.push(s);
|
|
14507
14596
|
kf.fm.supersedes = [.../* @__PURE__ */ new Set([...kf.fm.supersedes ?? [], drop.id])];
|
|
14508
|
-
|
|
14597
|
+
fs28.writeFileSync(keep.file, fileText(kf.fm, kf.body));
|
|
14509
14598
|
const df = readFm(drop.file);
|
|
14510
14599
|
df.fm.status = "retired";
|
|
14511
|
-
|
|
14600
|
+
fs28.writeFileSync(drop.file, fileText(df.fm, df.body));
|
|
14512
14601
|
events.push(ev(c, "item.superseded", drop.id), ev(c, "item.applied", keep.id));
|
|
14513
14602
|
res.applied.push(key);
|
|
14514
14603
|
}
|
|
@@ -14541,7 +14630,7 @@ function resolveEscalation(c, key, r, by, reason) {
|
|
|
14541
14630
|
f.fm.status = "retired";
|
|
14542
14631
|
events.push(ev(c, "item.superseded", rule.id));
|
|
14543
14632
|
}
|
|
14544
|
-
|
|
14633
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14545
14634
|
return done(`${rule.id}: ${r.verdict}`);
|
|
14546
14635
|
}
|
|
14547
14636
|
if (esc.kind === "proposal") {
|
|
@@ -14564,20 +14653,20 @@ function resolveEscalation(c, key, r, by, reason) {
|
|
|
14564
14653
|
if (rule) {
|
|
14565
14654
|
const f = readFm(rule.file);
|
|
14566
14655
|
f.fm.status = r.kind === "approve" ? "retired" : "active";
|
|
14567
|
-
|
|
14656
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14568
14657
|
events.push(ev(c, r.kind === "approve" ? "item.archived" : "item.applied", rule.id));
|
|
14569
14658
|
}
|
|
14570
14659
|
return done(`${esc.ruleId}: ${r.kind === "approve" ? "retired" : "kept"}`);
|
|
14571
14660
|
}
|
|
14572
14661
|
if (esc.kind === "override-base-changed") {
|
|
14573
|
-
const ov = esc.item &&
|
|
14662
|
+
const ov = esc.item && fs28.existsSync(esc.item) ? esc.item : void 0;
|
|
14574
14663
|
if (ov) {
|
|
14575
14664
|
if (r.kind === "reject")
|
|
14576
|
-
|
|
14665
|
+
fs28.rmSync(ov);
|
|
14577
14666
|
else {
|
|
14578
14667
|
const f = readFm(ov);
|
|
14579
14668
|
f.fm.overridesBase = esc.evidence?.docRev;
|
|
14580
|
-
|
|
14669
|
+
fs28.writeFileSync(ov, fileText(f.fm, f.body));
|
|
14581
14670
|
}
|
|
14582
14671
|
}
|
|
14583
14672
|
return done(`${esc.ruleId}: override ${r.kind === "reject" ? "dropped" : "kept"}`);
|
|
@@ -14601,10 +14690,10 @@ ${o.evidence}`).digest("hex");
|
|
|
14601
14690
|
supersedes: [],
|
|
14602
14691
|
conflictsWith: []
|
|
14603
14692
|
};
|
|
14604
|
-
const dir =
|
|
14605
|
-
|
|
14606
|
-
const file =
|
|
14607
|
-
|
|
14693
|
+
const dir = path29.join(kbRulesDir(c.cfg, c.kb), PROPOSED_DIR);
|
|
14694
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
14695
|
+
const file = path29.join(dir, `${id}@${sha.slice(0, SHA_LEN2)}.md`);
|
|
14696
|
+
fs28.writeFileSync(file, fileText(fm, o.statement));
|
|
14608
14697
|
appendAudit([ev(c, "item.proposed", id)], c.audit);
|
|
14609
14698
|
return file;
|
|
14610
14699
|
}
|
|
@@ -14645,8 +14734,8 @@ var init_review = __esm({
|
|
|
14645
14734
|
PRIORITY_RANK = { company: 3, project: 2, agent: 1 };
|
|
14646
14735
|
SHA_LEN2 = 12;
|
|
14647
14736
|
strong = (m) => m === "must" || m === "must-not";
|
|
14648
|
-
escalationsFile = (kbRoot) =>
|
|
14649
|
-
verdictsFile = (kbRoot) =>
|
|
14737
|
+
escalationsFile = (kbRoot) => path29.join(kbRoot, "escalations.json");
|
|
14738
|
+
verdictsFile = (kbRoot) => path29.join(kbRoot, "verdicts.jsonl");
|
|
14650
14739
|
needsYouCount = (kbRoot) => readEscalations(kbRoot).filter((e) => e.needsYou).length;
|
|
14651
14740
|
git = (root, args) => execFileSync8("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
14652
14741
|
realGit = {
|
|
@@ -14667,13 +14756,13 @@ var init_review = __esm({
|
|
|
14667
14756
|
},
|
|
14668
14757
|
docDate(file) {
|
|
14669
14758
|
try {
|
|
14670
|
-
const d = git(
|
|
14759
|
+
const d = git(path29.dirname(file), ["log", "-1", "--format=%cI", "--", path29.basename(file)]);
|
|
14671
14760
|
if (d)
|
|
14672
14761
|
return d;
|
|
14673
14762
|
} catch {
|
|
14674
14763
|
}
|
|
14675
14764
|
try {
|
|
14676
|
-
return
|
|
14765
|
+
return fs28.statSync(file).mtime.toISOString();
|
|
14677
14766
|
} catch {
|
|
14678
14767
|
return null;
|
|
14679
14768
|
}
|
|
@@ -14682,7 +14771,7 @@ var init_review = __esm({
|
|
|
14682
14771
|
nowOf = (c) => c.now?.() ?? /* @__PURE__ */ new Date();
|
|
14683
14772
|
fileText = (fm, body) => matter5.stringify(body.trim() + "\n", JSON.parse(JSON.stringify(fm)));
|
|
14684
14773
|
readFm = (file) => {
|
|
14685
|
-
const m = matter5(
|
|
14774
|
+
const m = matter5(fs28.readFileSync(file, "utf8"));
|
|
14686
14775
|
return { fm: m.data, body: m.content };
|
|
14687
14776
|
};
|
|
14688
14777
|
ev = (c, event, itemId, extra = {}) => ({ kb: c.kb, level: "group", project: "", domain: "rules", itemId, event, ...extra });
|
|
@@ -14692,8 +14781,8 @@ var init_review = __esm({
|
|
|
14692
14781
|
|
|
14693
14782
|
// packages/core/dist/knowledge/fullpass.js
|
|
14694
14783
|
import crypto5 from "crypto";
|
|
14695
|
-
import
|
|
14696
|
-
import
|
|
14784
|
+
import fs29 from "fs";
|
|
14785
|
+
import path30 from "path";
|
|
14697
14786
|
import matter6 from "gray-matter";
|
|
14698
14787
|
function dedupPairs(rules) {
|
|
14699
14788
|
const active = rules.filter((r) => r.status === "active").sort((x, y) => x.id.localeCompare(y.id));
|
|
@@ -14710,12 +14799,12 @@ function dedupPairs(rules) {
|
|
|
14710
14799
|
return out.slice(0, MAX_DEDUP_PAIRS);
|
|
14711
14800
|
}
|
|
14712
14801
|
function readAuditRecords(kb, since, dir = defaultAuditDir()) {
|
|
14713
|
-
if (!
|
|
14802
|
+
if (!fs29.existsSync(dir))
|
|
14714
14803
|
return [];
|
|
14715
14804
|
const sinceMonth = since.toISOString().slice(0, 7);
|
|
14716
14805
|
const out = [];
|
|
14717
|
-
for (const f of
|
|
14718
|
-
for (const line2 of
|
|
14806
|
+
for (const f of fs29.readdirSync(dir).filter((n) => n.endsWith(".jsonl") && n.slice(0, 7) >= sinceMonth).sort()) {
|
|
14807
|
+
for (const line2 of fs29.readFileSync(path30.join(dir, f), "utf8").split("\n")) {
|
|
14719
14808
|
if (!line2.trim())
|
|
14720
14809
|
continue;
|
|
14721
14810
|
try {
|
|
@@ -14737,7 +14826,7 @@ function usageReview(rules, events) {
|
|
|
14737
14826
|
continue;
|
|
14738
14827
|
if (e.event === "item.surfaced")
|
|
14739
14828
|
surfaced[e.itemId] = (surfaced[e.itemId] ?? 0) + 1;
|
|
14740
|
-
if (e.event === "item.outcome") {
|
|
14829
|
+
if (e.event === "item.outcome" && RATED_SOURCES.has(outcomeSource(e))) {
|
|
14741
14830
|
if (e.outcome === "noise")
|
|
14742
14831
|
noise[e.itemId] = (noise[e.itemId] ?? 0) + 1;
|
|
14743
14832
|
if (e.outcome === "violated")
|
|
@@ -14778,8 +14867,8 @@ function checkOverrides(cfg, kb) {
|
|
|
14778
14867
|
continue;
|
|
14779
14868
|
const h = baseHash(b);
|
|
14780
14869
|
if (!o.overridesBase) {
|
|
14781
|
-
const m = matter6(
|
|
14782
|
-
|
|
14870
|
+
const m = matter6(fs29.readFileSync(o.file, "utf8"));
|
|
14871
|
+
fs29.writeFileSync(o.file, matter6.stringify(m.content, { ...m.data, overridesBase: h }));
|
|
14783
14872
|
continue;
|
|
14784
14873
|
}
|
|
14785
14874
|
out.push({ project, ruleId: o.id, file: o.file, baseHash: h, changed: o.overridesBase !== h });
|
|
@@ -14822,8 +14911,8 @@ var init_fullpass = __esm({
|
|
|
14822
14911
|
});
|
|
14823
14912
|
|
|
14824
14913
|
// packages/core/dist/knowledge/scores.js
|
|
14825
|
-
import
|
|
14826
|
-
import
|
|
14914
|
+
import fs30 from "fs";
|
|
14915
|
+
import path31 from "path";
|
|
14827
14916
|
function tally(records, into = emptyTally()) {
|
|
14828
14917
|
for (const e of records) {
|
|
14829
14918
|
const domain = typeof e.domain === "string" && e.domain ? e.domain : "rules";
|
|
@@ -14859,17 +14948,25 @@ function tally(records, into = emptyTally()) {
|
|
|
14859
14948
|
c.cited++;
|
|
14860
14949
|
c.lastUsed = later(c.lastUsed, e.ts);
|
|
14861
14950
|
break;
|
|
14862
|
-
case "item.outcome":
|
|
14863
|
-
if (OUTCOMES.has(String(e.outcome)))
|
|
14864
|
-
|
|
14951
|
+
case "item.outcome": {
|
|
14952
|
+
if (!OUTCOMES.has(String(e.outcome)))
|
|
14953
|
+
break;
|
|
14954
|
+
const src = outcomeSource(e), o = e.outcome;
|
|
14955
|
+
const bySrc = into.sources[src] ??= {};
|
|
14956
|
+
bySrc[o] = (bySrc[o] ?? 0) + 1;
|
|
14957
|
+
if (src === "reconcile" && o === "violated")
|
|
14958
|
+
c.codeViolations++;
|
|
14959
|
+
else if (RATED_SOURCES.has(src))
|
|
14960
|
+
c[o]++;
|
|
14865
14961
|
break;
|
|
14962
|
+
}
|
|
14866
14963
|
}
|
|
14867
14964
|
}
|
|
14868
14965
|
return into;
|
|
14869
14966
|
}
|
|
14870
14967
|
function parseLines(file) {
|
|
14871
14968
|
const out = [];
|
|
14872
|
-
for (const line2 of
|
|
14969
|
+
for (const line2 of fs30.readFileSync(file, "utf8").split("\n")) {
|
|
14873
14970
|
if (!line2.trim())
|
|
14874
14971
|
continue;
|
|
14875
14972
|
try {
|
|
@@ -14881,37 +14978,37 @@ function parseLines(file) {
|
|
|
14881
14978
|
}
|
|
14882
14979
|
function compactAudit(o) {
|
|
14883
14980
|
const dir = o.dir ?? defaultAuditDir();
|
|
14884
|
-
if (!
|
|
14981
|
+
if (!fs30.existsSync(dir))
|
|
14885
14982
|
return [];
|
|
14886
14983
|
const cutoffMonth = new Date(o.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3).toISOString().slice(0, 7);
|
|
14887
14984
|
const done = [];
|
|
14888
|
-
for (const f of
|
|
14985
|
+
for (const f of fs30.readdirSync(dir).sort()) {
|
|
14889
14986
|
const m = MONTH_FILE_RE.exec(f);
|
|
14890
14987
|
if (!m || m[1] >= cutoffMonth)
|
|
14891
14988
|
continue;
|
|
14892
14989
|
const [, month, machine] = m;
|
|
14893
14990
|
const byKb = /* @__PURE__ */ new Map();
|
|
14894
|
-
for (const r of parseLines(
|
|
14991
|
+
for (const r of parseLines(path31.join(dir, f)))
|
|
14895
14992
|
byKb.set(r.kb ?? "", [...byKb.get(r.kb ?? "") ?? [], r]);
|
|
14896
|
-
const statsPath =
|
|
14897
|
-
const prior =
|
|
14993
|
+
const statsPath = path31.join(dir, statsFileName(month, machine));
|
|
14994
|
+
const prior = fs30.existsSync(statsPath) ? JSON.parse(fs30.readFileSync(statsPath, "utf8")) : null;
|
|
14898
14995
|
const kbs = prior?.kbs ?? {};
|
|
14899
14996
|
for (const [kb, recs] of byKb)
|
|
14900
14997
|
kbs[kb] = tally(recs, kbs[kb]);
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14998
|
+
fs30.writeFileSync(`${statsPath}.tmp`, JSON.stringify({ month, machine, kbs }));
|
|
14999
|
+
fs30.renameSync(`${statsPath}.tmp`, statsPath);
|
|
15000
|
+
fs30.unlinkSync(path31.join(dir, f));
|
|
14904
15001
|
done.push(month);
|
|
14905
15002
|
}
|
|
14906
15003
|
return done;
|
|
14907
15004
|
}
|
|
14908
15005
|
function readCompacted(kb, dir) {
|
|
14909
15006
|
const t = emptyTally();
|
|
14910
|
-
if (!
|
|
15007
|
+
if (!fs30.existsSync(dir))
|
|
14911
15008
|
return t;
|
|
14912
|
-
for (const f of
|
|
15009
|
+
for (const f of fs30.readdirSync(dir).filter((n) => n.endsWith(".stats.json"))) {
|
|
14913
15010
|
try {
|
|
14914
|
-
const k = JSON.parse(
|
|
15011
|
+
const k = JSON.parse(fs30.readFileSync(path31.join(dir, f), "utf8")).kbs[kb];
|
|
14915
15012
|
if (k)
|
|
14916
15013
|
mergeTally(t, k);
|
|
14917
15014
|
} catch {
|
|
@@ -14937,6 +15034,12 @@ function mergeTally(into, from) {
|
|
|
14937
15034
|
into.agents[k] = (into.agents[k] ?? 0) + v2;
|
|
14938
15035
|
for (const [k, v2] of Object.entries(from.tiers))
|
|
14939
15036
|
into.tiers[k] = (into.tiers[k] ?? 0) + v2;
|
|
15037
|
+
into.sources ??= {};
|
|
15038
|
+
for (const [src, os18] of Object.entries(from.sources ?? {})) {
|
|
15039
|
+
const t = into.sources[src] ??= {};
|
|
15040
|
+
for (const [o, n] of Object.entries(os18))
|
|
15041
|
+
t[o] = (t[o] ?? 0) + n;
|
|
15042
|
+
}
|
|
14940
15043
|
}
|
|
14941
15044
|
function computeScores(i) {
|
|
14942
15045
|
const since = i.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3;
|
|
@@ -14960,11 +15063,14 @@ function computeScores(i) {
|
|
|
14960
15063
|
violated: w.violated,
|
|
14961
15064
|
noise: w.noise,
|
|
14962
15065
|
wrong: w.wrong,
|
|
15066
|
+
codeViolations: w.codeViolations,
|
|
15067
|
+
evaluated: w.followed + w.violated,
|
|
14963
15068
|
fireRate: w.surfaced,
|
|
14964
15069
|
pullRate: w.shown + w.searched,
|
|
14965
15070
|
useRate: ratio(w.cited, w.surfaced),
|
|
14966
15071
|
noiseRate: ratio(w.noise, w.surfaced),
|
|
14967
15072
|
violationRate: ratio(w.violated, w.surfaced),
|
|
15073
|
+
complianceRate: w.followed + w.violated >= COMPLIANCE_MIN_EVALUATED ? ratio(w.followed, w.followed + w.violated) : null,
|
|
14968
15074
|
lastSurfaced: l?.lastSurfaced,
|
|
14969
15075
|
lastUsed: l?.lastUsed,
|
|
14970
15076
|
neverFired30d: w.surfaced === 0 && (knownActive.has(key) || (l?.surfaced ?? 0) > 0),
|
|
@@ -14997,53 +15103,58 @@ function computeScores(i) {
|
|
|
14997
15103
|
domains,
|
|
14998
15104
|
perAgentCoverage: win.agents,
|
|
14999
15105
|
tierContribution: win.tiers,
|
|
15000
|
-
reviewerAgreement: { resolved: operator.length, approved, rate: ratio(approved, operator.length) }
|
|
15106
|
+
reviewerAgreement: { resolved: operator.length, approved, rate: ratio(approved, operator.length) },
|
|
15107
|
+
bySource: win.sources
|
|
15001
15108
|
};
|
|
15002
15109
|
}
|
|
15003
15110
|
function scoreKb(o) {
|
|
15004
15111
|
const dir = o.auditDir ?? defaultAuditDir();
|
|
15005
15112
|
const records = [];
|
|
15006
|
-
if (
|
|
15007
|
-
for (const f of
|
|
15008
|
-
records.push(...parseLines(
|
|
15113
|
+
if (fs30.existsSync(dir)) {
|
|
15114
|
+
for (const f of fs30.readdirSync(dir).filter((n) => MONTH_FILE_RE.test(n)).sort()) {
|
|
15115
|
+
records.push(...parseLines(path31.join(dir, f)).filter((r) => r.kb === o.kb));
|
|
15009
15116
|
}
|
|
15010
15117
|
}
|
|
15011
15118
|
let verdicts = [];
|
|
15012
15119
|
try {
|
|
15013
|
-
verdicts =
|
|
15120
|
+
verdicts = fs30.readFileSync(path31.join(o.kbRoot, "verdicts.jsonl"), "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
15014
15121
|
} catch {
|
|
15015
15122
|
}
|
|
15016
15123
|
return computeScores({ kb: o.kb, records, now: o.now, known: o.known, compacted: readCompacted(o.kb, dir), verdicts });
|
|
15017
15124
|
}
|
|
15018
15125
|
function writeScores(kbRoot, s) {
|
|
15019
|
-
|
|
15020
|
-
|
|
15126
|
+
fs30.mkdirSync(kbRoot, { recursive: true });
|
|
15127
|
+
fs30.writeFileSync(scoresFile(kbRoot), JSON.stringify(s, null, 2) + "\n");
|
|
15021
15128
|
}
|
|
15022
15129
|
function renderScoresSection(s) {
|
|
15023
15130
|
const pct = (x) => x === null ? "n/a" : `${Math.round(x * 100)}%`;
|
|
15024
15131
|
const used = s.items.filter((x) => x.surfaced).sort((a, b) => b.surfaced - a.surfaced || a.itemId.localeCompare(b.itemId)).slice(0, 10);
|
|
15025
15132
|
const never = s.items.filter((x) => x.neverFired30d).map((x) => x.itemId);
|
|
15133
|
+
const code = s.items.filter((x) => x.codeViolations).map((x) => `${x.itemId} ${x.codeViolations}`);
|
|
15134
|
+
const sources = Object.keys(s.bySource).sort().map((src) => `${src} ${Object.entries(s.bySource[src]).map(([o, n]) => `${o} ${n}`).join(", ")}`);
|
|
15026
15135
|
return [
|
|
15027
15136
|
"",
|
|
15028
15137
|
`## Scores (${s.windowDays} days)`,
|
|
15029
15138
|
"",
|
|
15030
15139
|
...used.length ? [
|
|
15031
|
-
"| Item | Surfaced | Pulled | Cited | Use | Noise | Violated |",
|
|
15032
|
-
"
|
|
15033
|
-
...used.map((x) => `| ${x.itemId} | ${x.surfaced} | ${x.pullRate} | ${x.cited} | ${pct(x.useRate)} | ${pct(x.noiseRate)} | ${pct(x.violationRate)} |`)
|
|
15140
|
+
"| Item | Surfaced | Pulled | Cited | Use | Noise | Violated | Compliance |",
|
|
15141
|
+
"|---|---|---|---|---|---|---|---|",
|
|
15142
|
+
...used.map((x) => `| ${x.itemId} | ${x.surfaced} | ${x.pullRate} | ${x.cited} | ${pct(x.useRate)} | ${pct(x.noiseRate)} | ${pct(x.violationRate)} | ${pct(x.complianceRate)} |`)
|
|
15034
15143
|
] : ["No items surfaced."],
|
|
15035
15144
|
"",
|
|
15036
15145
|
`- Never fired (${never.length}): ${never.slice(0, 20).join(", ") || "none"}${never.length > 20 ? ", \u2026" : ""}`,
|
|
15037
15146
|
`- Tier contribution: ${Object.entries(s.tierContribution).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
15038
15147
|
`- Per-agent coverage (surfaced): ${Object.entries(s.perAgentCoverage).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
15039
15148
|
`- Reviewer agreement: ${pct(s.reviewerAgreement.rate)} (${s.reviewerAgreement.approved}/${s.reviewerAgreement.resolved} operator resolutions approved)`,
|
|
15149
|
+
`- Outcomes by source: ${sources.join("; ") || "none"} (crew and reconcile are not in the rates)`,
|
|
15150
|
+
`- Code violations (reconcile, not in rates): ${code.join(", ") || "none"}`,
|
|
15040
15151
|
"",
|
|
15041
15152
|
"| Domain | Writes/mo | Reads/mo | Chars per citation | Alarm |",
|
|
15042
15153
|
"|---|---|---|---|---|",
|
|
15043
15154
|
...s.domains.map((d) => `| ${d.domain} | ${d.writesPerMonth} | ${d.readsPerMonth} | ${d.charsPerCitation ?? "n/a"} | ${d.deadDomain ? "DEAD (no activity 30d)" : ""} |`)
|
|
15044
15155
|
];
|
|
15045
15156
|
}
|
|
15046
|
-
var DAY_MS3, SCORE_WINDOW_DAYS, WRITE_EVENTS, READ_EVENTS, OUTCOMES, zero, later, emptyTally, MONTH_FILE_RE, statsFileName, ratio, scoresFile;
|
|
15157
|
+
var DAY_MS3, SCORE_WINDOW_DAYS, WRITE_EVENTS, READ_EVENTS, OUTCOMES, zero, later, emptyTally, MONTH_FILE_RE, statsFileName, ratio, COMPLIANCE_MIN_EVALUATED, scoresFile;
|
|
15047
15158
|
var init_scores = __esm({
|
|
15048
15159
|
"packages/core/dist/knowledge/scores.js"() {
|
|
15049
15160
|
init_audit();
|
|
@@ -15052,19 +15163,20 @@ var init_scores = __esm({
|
|
|
15052
15163
|
WRITE_EVENTS = /* @__PURE__ */ new Set(["item.proposed", "item.approved", "item.rejected", "item.applied", "item.promoted", "item.superseded", "item.archived"]);
|
|
15053
15164
|
READ_EVENTS = /* @__PURE__ */ new Set(["item.surfaced", "item.shown", "item.searched"]);
|
|
15054
15165
|
OUTCOMES = /* @__PURE__ */ new Set(["followed", "violated", "noise", "wrong"]);
|
|
15055
|
-
zero = () => ({ surfaced: 0, suppressed: 0, searched: 0, shown: 0, cited: 0, followed: 0, violated: 0, noise: 0, wrong: 0, chars: 0 });
|
|
15166
|
+
zero = () => ({ surfaced: 0, suppressed: 0, searched: 0, shown: 0, cited: 0, followed: 0, violated: 0, noise: 0, wrong: 0, codeViolations: 0, chars: 0 });
|
|
15056
15167
|
later = (a, b) => a && b ? a > b ? a : b : a ?? b;
|
|
15057
|
-
emptyTally = () => ({ items: {}, domains: {}, agents: {}, tiers: {} });
|
|
15168
|
+
emptyTally = () => ({ items: {}, domains: {}, agents: {}, tiers: {}, sources: {} });
|
|
15058
15169
|
MONTH_FILE_RE = /^(\d{4}-\d{2})\.(.+)\.jsonl$/;
|
|
15059
15170
|
statsFileName = (month, machine) => `${month}.${machine}.stats.json`;
|
|
15060
15171
|
ratio = (n, d) => d ? Math.round(n / d * 1e3) / 1e3 : null;
|
|
15061
|
-
|
|
15172
|
+
COMPLIANCE_MIN_EVALUATED = 3;
|
|
15173
|
+
scoresFile = (kbRoot) => path31.join(kbRoot, "scores.json");
|
|
15062
15174
|
}
|
|
15063
15175
|
});
|
|
15064
15176
|
|
|
15065
15177
|
// packages/core/dist/knowledge/report.js
|
|
15066
|
-
import
|
|
15067
|
-
import
|
|
15178
|
+
import fs31 from "fs";
|
|
15179
|
+
import path32 from "path";
|
|
15068
15180
|
function renderReport(cfg, kb, o) {
|
|
15069
15181
|
const { rules } = loadKbRules(cfg, kb, { includeProposed: true });
|
|
15070
15182
|
const count = (s) => rules.filter((r) => r.status === s).length;
|
|
@@ -15093,15 +15205,15 @@ function renderReport(cfg, kb, o) {
|
|
|
15093
15205
|
}
|
|
15094
15206
|
if (o.full) {
|
|
15095
15207
|
const { usage: usage2, caps } = o.full;
|
|
15096
|
-
L.push("", "## Usage (30 days)", "", `- Surfaced: ${Object.values(usage2.surfaced).reduce((a, b) => a + b, 0)} times across ${Object.keys(usage2.surfaced).length} rules.`, `- Never surfaced (re-check triggers): ${usage2.neverSurfaced.join(", ") || "none"}`, `- Noisy (narrow triggers): ${usage2.noisy.join(", ") || "none"}`, `-
|
|
15208
|
+
L.push("", "## Usage (30 days)", "", `- Surfaced: ${Object.values(usage2.surfaced).reduce((a, b) => a + b, 0)} times across ${Object.keys(usage2.surfaced).length} rules.`, `- Never surfaced (re-check triggers): ${usage2.neverSurfaced.join(", ") || "none"}`, `- Noisy (narrow triggers): ${usage2.noisy.join(", ") || "none"}`, `- Violated in graded feedback (reword): ${usage2.violated.join(", ") || "none"}`, `- Domains over cap: ${caps.map((c) => `${c.domain} (${c.count}/${c.cap})`).join(", ") || "none"}`);
|
|
15097
15209
|
}
|
|
15098
15210
|
if (o.scores)
|
|
15099
15211
|
L.push(...renderScoresSection(o.scores));
|
|
15100
15212
|
return L.join("\n") + "\n";
|
|
15101
15213
|
}
|
|
15102
15214
|
function writeReport(cfg, kb, o) {
|
|
15103
|
-
const file =
|
|
15104
|
-
|
|
15215
|
+
const file = path32.join(kbDir(cfg, kb), "REPORT.md");
|
|
15216
|
+
fs31.writeFileSync(file, renderReport(cfg, kb, o));
|
|
15105
15217
|
return file;
|
|
15106
15218
|
}
|
|
15107
15219
|
var init_report = __esm({
|
|
@@ -15261,6 +15373,7 @@ __export(dist_exports, {
|
|
|
15261
15373
|
CITE_LOOKBACK_DAYS: () => CITE_LOOKBACK_DAYS,
|
|
15262
15374
|
CITE_NOTE: () => CITE_NOTE,
|
|
15263
15375
|
CMUX_PRESERVE_CLAUDE_AUTH_ENV: () => CMUX_PRESERVE_CLAUDE_AUTH_ENV,
|
|
15376
|
+
COMPLIANCE_MIN_EVALUATED: () => COMPLIANCE_MIN_EVALUATED,
|
|
15264
15377
|
CONFLICT_409_SELF_HEAL_MS: () => CONFLICT_409_SELF_HEAL_MS,
|
|
15265
15378
|
CONVERTED_DIR: () => CONVERTED_DIR,
|
|
15266
15379
|
CREW_GONE_MS: () => CREW_GONE_MS,
|
|
@@ -15274,6 +15387,7 @@ __export(dist_exports, {
|
|
|
15274
15387
|
DEFAULT_MAX_SECTIONS_PER_PASS: () => DEFAULT_MAX_SECTIONS_PER_PASS,
|
|
15275
15388
|
DEFAULT_SEARCH_STATUSES: () => DEFAULT_SEARCH_STATUSES,
|
|
15276
15389
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
15390
|
+
DELIVERY_EVENTS: () => DELIVERY_EVENTS,
|
|
15277
15391
|
DOCLING_DROP_THRESHOLD: () => DOCLING_DROP_THRESHOLD,
|
|
15278
15392
|
DeferDelivery: () => DeferDelivery,
|
|
15279
15393
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
@@ -15293,6 +15407,7 @@ __export(dist_exports, {
|
|
|
15293
15407
|
NOISY_MIN_SURFACED: () => NOISY_MIN_SURFACED,
|
|
15294
15408
|
NOISY_RATE: () => NOISY_RATE,
|
|
15295
15409
|
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
15410
|
+
OUTCOME_SOURCES: () => OUTCOME_SOURCES,
|
|
15296
15411
|
PANE_IDLE_AFTER_MS: () => PANE_IDLE_AFTER_MS,
|
|
15297
15412
|
PASS_THROUGH: () => PASS_THROUGH,
|
|
15298
15413
|
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
@@ -15301,6 +15416,7 @@ __export(dist_exports, {
|
|
|
15301
15416
|
PROMPT_SINGLE_TERM_MIN_SCORE: () => PROMPT_SINGLE_TERM_MIN_SCORE,
|
|
15302
15417
|
PROPOSED_DIR: () => PROPOSED_DIR,
|
|
15303
15418
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
15419
|
+
RATED_SOURCES: () => RATED_SOURCES,
|
|
15304
15420
|
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
15305
15421
|
READ_ONLY_TOP_LEVEL_COMMANDS: () => READ_ONLY_TOP_LEVEL_COMMANDS,
|
|
15306
15422
|
ROUTER_READY_ATTEMPTS: () => ROUTER_READY_ATTEMPTS,
|
|
@@ -15510,6 +15626,7 @@ __export(dist_exports, {
|
|
|
15510
15626
|
loadSessions: () => loadSessions,
|
|
15511
15627
|
loadSources: () => loadSources,
|
|
15512
15628
|
loadState: () => loadState,
|
|
15629
|
+
loadedDaemonLogPath: () => loadedDaemonLogPath,
|
|
15513
15630
|
mailboxStats: () => mailboxStats,
|
|
15514
15631
|
makeGate: () => makeGate,
|
|
15515
15632
|
markPendingWarned: () => markPendingWarned,
|
|
@@ -15538,9 +15655,11 @@ __export(dist_exports, {
|
|
|
15538
15655
|
notePaneScreen: () => notePaneScreen,
|
|
15539
15656
|
notifyToggle: () => notifyToggle,
|
|
15540
15657
|
orphanedErrata: () => orphanedErrata,
|
|
15658
|
+
outcomeSource: () => outcomeSource,
|
|
15541
15659
|
parseBearer: () => parseBearer,
|
|
15542
15660
|
parseClassifierVerdict: () => parseClassifierVerdict,
|
|
15543
15661
|
parseCommand: () => parseCommand,
|
|
15662
|
+
parseLaunchctlStderrPath: () => parseLaunchctlStderrPath,
|
|
15544
15663
|
parseLiveOpencodeServers: () => parseLiveOpencodeServers,
|
|
15545
15664
|
parseNotifyPref: () => parseNotifyPref,
|
|
15546
15665
|
parseProgramArgs: () => parseProgramArgs,
|
|
@@ -15799,23 +15918,22 @@ init_dist2();
|
|
|
15799
15918
|
init_dist2();
|
|
15800
15919
|
init_dist2();
|
|
15801
15920
|
init_dist2();
|
|
15802
|
-
import { join as join27, dirname as dirname6, resolve as
|
|
15803
|
-
import { homedir as homedir14 } from "os";
|
|
15921
|
+
import { join as join27, dirname as dirname6, resolve as resolve4 } from "path";
|
|
15804
15922
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15805
|
-
import { readFileSync as readFileSync19, statSync as statSync4, existsSync as
|
|
15923
|
+
import { readFileSync as readFileSync19, statSync as statSync4, existsSync as existsSync14 } from "fs";
|
|
15806
15924
|
|
|
15807
15925
|
// packages/agents/dist/drivers/claude.js
|
|
15808
15926
|
import { execSync as execSync2 } from "child_process";
|
|
15809
15927
|
|
|
15810
15928
|
// packages/agents/dist/sessions/claude-sessions.js
|
|
15811
|
-
import { readdirSync as
|
|
15929
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
|
|
15812
15930
|
import { join as join18 } from "path";
|
|
15813
15931
|
|
|
15814
15932
|
// packages/agents/dist/claude/registry.js
|
|
15815
|
-
import
|
|
15933
|
+
import fs32 from "fs";
|
|
15816
15934
|
import { join as join17 } from "path";
|
|
15817
|
-
import { homedir as
|
|
15818
|
-
var CLAUDE_SESSIONS_DIR = join17(
|
|
15935
|
+
import { homedir as homedir6 } from "os";
|
|
15936
|
+
var CLAUDE_SESSIONS_DIR = join17(homedir6(), ".claude", "sessions");
|
|
15819
15937
|
var PID_JSON = /^(\d+)\.json$/;
|
|
15820
15938
|
function parseRegistryDir(files, readFile7) {
|
|
15821
15939
|
const out = [];
|
|
@@ -15857,11 +15975,11 @@ function toLifecycleSnapshot(entry, taskId, alive, now) {
|
|
|
15857
15975
|
function readClaudeStatusBySocketPath(socketPath) {
|
|
15858
15976
|
let files;
|
|
15859
15977
|
try {
|
|
15860
|
-
files =
|
|
15978
|
+
files = fs32.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
15861
15979
|
} catch {
|
|
15862
15980
|
return void 0;
|
|
15863
15981
|
}
|
|
15864
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
15982
|
+
const entries = parseRegistryDir(files, (name) => fs32.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
15865
15983
|
const entry = entries.find((e) => e.messagingSocketPath === socketPath);
|
|
15866
15984
|
if (!entry)
|
|
15867
15985
|
return void 0;
|
|
@@ -15878,44 +15996,43 @@ import { execSync as execSync4 } from "child_process";
|
|
|
15878
15996
|
import { execSync as execSync5 } from "child_process";
|
|
15879
15997
|
|
|
15880
15998
|
// packages/agents/dist/sessions/opencode-sessions.js
|
|
15881
|
-
|
|
15999
|
+
init_dist();
|
|
16000
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
|
|
15882
16001
|
import { join as join19 } from "path";
|
|
15883
|
-
import { homedir as homedir8 } from "os";
|
|
15884
|
-
var SQUADRANT_STATE_DIR = join19(homedir8(), ".config", "squadrant", "state");
|
|
15885
16002
|
|
|
15886
16003
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
15887
|
-
import
|
|
15888
|
-
import
|
|
16004
|
+
import fs33 from "fs";
|
|
16005
|
+
import path33 from "path";
|
|
15889
16006
|
|
|
15890
16007
|
// packages/agents/dist/projection/cursor.js
|
|
15891
16008
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
15892
|
-
import
|
|
15893
|
-
import
|
|
16009
|
+
import path34 from "path";
|
|
16010
|
+
import os10 from "os";
|
|
15894
16011
|
|
|
15895
16012
|
// packages/agents/dist/projection/codex.js
|
|
15896
16013
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
15897
|
-
import
|
|
15898
|
-
import
|
|
16014
|
+
import path35 from "path";
|
|
16015
|
+
import os11 from "os";
|
|
15899
16016
|
|
|
15900
16017
|
// packages/agents/dist/projection/gemini.js
|
|
15901
16018
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
15902
|
-
import
|
|
15903
|
-
import
|
|
16019
|
+
import path36 from "path";
|
|
16020
|
+
import os12 from "os";
|
|
15904
16021
|
|
|
15905
16022
|
// packages/agents/dist/projection/opencode.js
|
|
15906
16023
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
15907
|
-
import
|
|
15908
|
-
import
|
|
16024
|
+
import path37 from "path";
|
|
16025
|
+
import os13 from "os";
|
|
15909
16026
|
|
|
15910
16027
|
// packages/agents/dist/projection/opencode-skills.js
|
|
15911
|
-
import
|
|
15912
|
-
import
|
|
15913
|
-
import
|
|
16028
|
+
import fs34 from "fs";
|
|
16029
|
+
import os14 from "os";
|
|
16030
|
+
import path38 from "path";
|
|
15914
16031
|
var OPENCODE_SKILL_MARKER = ".squadrant-managed";
|
|
15915
16032
|
var MARKER_BODY = "squadrant\n";
|
|
15916
16033
|
function defaultOpencodeSkillsRoot(env = process.env) {
|
|
15917
|
-
const configHome = env.XDG_CONFIG_HOME ||
|
|
15918
|
-
return
|
|
16034
|
+
const configHome = env.XDG_CONFIG_HOME || path38.join(os14.homedir(), ".config");
|
|
16035
|
+
return path38.join(configHome, "opencode", "skills");
|
|
15919
16036
|
}
|
|
15920
16037
|
var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
15921
16038
|
function parseSkillName(raw) {
|
|
@@ -15930,62 +16047,62 @@ function parseSkillName(raw) {
|
|
|
15930
16047
|
}
|
|
15931
16048
|
function listSourceSkills(sourceSkillsDir) {
|
|
15932
16049
|
const out = /* @__PURE__ */ new Map();
|
|
15933
|
-
if (!
|
|
16050
|
+
if (!fs34.existsSync(sourceSkillsDir))
|
|
15934
16051
|
return out;
|
|
15935
|
-
for (const entry of
|
|
16052
|
+
for (const entry of fs34.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
|
|
15936
16053
|
if (!entry.isDirectory())
|
|
15937
16054
|
continue;
|
|
15938
|
-
const skillPath =
|
|
15939
|
-
if (!
|
|
16055
|
+
const skillPath = path38.join(sourceSkillsDir, entry.name, "SKILL.md");
|
|
16056
|
+
if (!fs34.existsSync(skillPath))
|
|
15940
16057
|
continue;
|
|
15941
|
-
const name = parseSkillName(
|
|
16058
|
+
const name = parseSkillName(fs34.readFileSync(skillPath, "utf-8"));
|
|
15942
16059
|
if (name && SAFE_NAME.test(name))
|
|
15943
16060
|
out.set(name, skillPath);
|
|
15944
16061
|
}
|
|
15945
16062
|
return out;
|
|
15946
16063
|
}
|
|
15947
16064
|
function hasMarker(dir) {
|
|
15948
|
-
return
|
|
16065
|
+
return fs34.existsSync(path38.join(dir, OPENCODE_SKILL_MARKER));
|
|
15949
16066
|
}
|
|
15950
16067
|
function syncOpencodeSkills(opts) {
|
|
15951
16068
|
const result = { written: [], unchanged: [], removed: [], skipped: [] };
|
|
15952
16069
|
const wanted = listSourceSkills(opts.sourceSkillsDir);
|
|
15953
|
-
if (wanted.size === 0 && !
|
|
16070
|
+
if (wanted.size === 0 && !fs34.existsSync(opts.sourceSkillsDir))
|
|
15954
16071
|
return result;
|
|
15955
16072
|
if (!opts.dryRun)
|
|
15956
|
-
|
|
16073
|
+
fs34.mkdirSync(opts.skillsRoot, { recursive: true });
|
|
15957
16074
|
for (const [name, srcPath] of wanted) {
|
|
15958
|
-
const targetDir =
|
|
15959
|
-
const targetSkill =
|
|
16075
|
+
const targetDir = path38.join(opts.skillsRoot, name);
|
|
16076
|
+
const targetSkill = path38.join(targetDir, "SKILL.md");
|
|
15960
16077
|
const managed = hasMarker(targetDir);
|
|
15961
|
-
if (
|
|
16078
|
+
if (fs34.existsSync(targetSkill) && !managed) {
|
|
15962
16079
|
result.skipped.push({ name, reason: "foreign-collision" });
|
|
15963
16080
|
continue;
|
|
15964
16081
|
}
|
|
15965
|
-
const current =
|
|
15966
|
-
if (current && current.equals(
|
|
16082
|
+
const current = fs34.existsSync(targetSkill) ? fs34.readFileSync(targetSkill) : null;
|
|
16083
|
+
if (current && current.equals(fs34.readFileSync(srcPath))) {
|
|
15967
16084
|
if (!managed && !opts.dryRun) {
|
|
15968
|
-
|
|
16085
|
+
fs34.writeFileSync(path38.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
15969
16086
|
}
|
|
15970
16087
|
result.unchanged.push(name);
|
|
15971
16088
|
continue;
|
|
15972
16089
|
}
|
|
15973
16090
|
if (!opts.dryRun) {
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
16091
|
+
fs34.mkdirSync(targetDir, { recursive: true });
|
|
16092
|
+
fs34.copyFileSync(srcPath, targetSkill);
|
|
16093
|
+
fs34.writeFileSync(path38.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
15977
16094
|
}
|
|
15978
16095
|
result.written.push(name);
|
|
15979
16096
|
}
|
|
15980
|
-
if (
|
|
15981
|
-
for (const entry of
|
|
16097
|
+
if (fs34.existsSync(opts.skillsRoot)) {
|
|
16098
|
+
for (const entry of fs34.readdirSync(opts.skillsRoot, { withFileTypes: true })) {
|
|
15982
16099
|
if (!entry.isDirectory() || wanted.has(entry.name))
|
|
15983
16100
|
continue;
|
|
15984
|
-
const dir =
|
|
16101
|
+
const dir = path38.join(opts.skillsRoot, entry.name);
|
|
15985
16102
|
if (!hasMarker(dir))
|
|
15986
16103
|
continue;
|
|
15987
16104
|
if (!opts.dryRun)
|
|
15988
|
-
|
|
16105
|
+
fs34.rmSync(dir, { recursive: true, force: true });
|
|
15989
16106
|
result.removed.push(entry.name);
|
|
15990
16107
|
}
|
|
15991
16108
|
}
|
|
@@ -15993,7 +16110,7 @@ function syncOpencodeSkills(opts) {
|
|
|
15993
16110
|
}
|
|
15994
16111
|
function syncShippedOpencodeSkills(opts) {
|
|
15995
16112
|
return syncOpencodeSkills({
|
|
15996
|
-
sourceSkillsDir:
|
|
16113
|
+
sourceSkillsDir: path38.join(opts.pkgRoot, "plugin", "skills"),
|
|
15997
16114
|
skillsRoot: opts.skillsRoot ?? defaultOpencodeSkillsRoot(),
|
|
15998
16115
|
dryRun: opts.dryRun
|
|
15999
16116
|
});
|
|
@@ -16001,8 +16118,8 @@ function syncShippedOpencodeSkills(opts) {
|
|
|
16001
16118
|
|
|
16002
16119
|
// packages/agents/dist/projection/claude.js
|
|
16003
16120
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
16004
|
-
import
|
|
16005
|
-
import
|
|
16121
|
+
import path39 from "path";
|
|
16122
|
+
import os15 from "os";
|
|
16006
16123
|
|
|
16007
16124
|
// packages/agents/dist/codex/app-server-client.js
|
|
16008
16125
|
import { EventEmitter } from "events";
|
|
@@ -16056,8 +16173,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
16056
16173
|
const info = this.opts.clientInfo ?? { name: "squadrant", version: "0" };
|
|
16057
16174
|
const id = this.nextId++;
|
|
16058
16175
|
const env = { jsonrpc: "2.0", id, method: "initialize", params: { clientInfo: info } };
|
|
16059
|
-
const res = await new Promise((
|
|
16060
|
-
this.pending.set(id, { resolve:
|
|
16176
|
+
const res = await new Promise((resolve5, reject) => {
|
|
16177
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
16061
16178
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
16062
16179
|
});
|
|
16063
16180
|
this.proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "initialized" }) + "\n");
|
|
@@ -16090,13 +16207,13 @@ var AppServerClient = class extends EventEmitter {
|
|
|
16090
16207
|
const turnId = ack?.turn?.id;
|
|
16091
16208
|
if (typeof turnId !== "string")
|
|
16092
16209
|
throw new Error(`turn/start: unexpected ack shape (no turn.id): ${JSON.stringify(ack).slice(0, 200)}`);
|
|
16093
|
-
return new Promise((
|
|
16210
|
+
return new Promise((resolve5, reject) => {
|
|
16094
16211
|
const onNote = (n) => {
|
|
16095
16212
|
if (n.params?.turn?.id !== turnId)
|
|
16096
16213
|
return;
|
|
16097
16214
|
if (n.method === "turn/completed") {
|
|
16098
16215
|
cleanup();
|
|
16099
|
-
|
|
16216
|
+
resolve5({ turnId });
|
|
16100
16217
|
}
|
|
16101
16218
|
if (n.method === "turn/failed") {
|
|
16102
16219
|
cleanup();
|
|
@@ -16147,8 +16264,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
16147
16264
|
throw new Error("AppServerClient not started");
|
|
16148
16265
|
const id = this.nextId++;
|
|
16149
16266
|
const env = { jsonrpc: "2.0", id, method, params: params ?? {} };
|
|
16150
|
-
return new Promise((
|
|
16151
|
-
this.pending.set(id, { resolve:
|
|
16267
|
+
return new Promise((resolve5, reject) => {
|
|
16268
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
16152
16269
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
16153
16270
|
});
|
|
16154
16271
|
}
|
|
@@ -16276,10 +16393,10 @@ function toSnapshot(ev2) {
|
|
|
16276
16393
|
|
|
16277
16394
|
// packages/agents/dist/codex/config.js
|
|
16278
16395
|
import { readFile as readFile6 } from "fs/promises";
|
|
16279
|
-
import { homedir as
|
|
16396
|
+
import { homedir as homedir7 } from "os";
|
|
16280
16397
|
import { join as join20 } from "path";
|
|
16281
16398
|
async function resolveCodexModel() {
|
|
16282
|
-
const home = process.env["CODEX_HOME"] ?? join20(
|
|
16399
|
+
const home = process.env["CODEX_HOME"] ?? join20(homedir7(), ".codex");
|
|
16283
16400
|
const configPath = join20(home, "config.toml");
|
|
16284
16401
|
let text;
|
|
16285
16402
|
try {
|
|
@@ -16594,11 +16711,11 @@ function buildCodexDeveloperInstructions(rec) {
|
|
|
16594
16711
|
${directive}` : directive;
|
|
16595
16712
|
}
|
|
16596
16713
|
function withTimeout(p, ms, msg) {
|
|
16597
|
-
return new Promise((
|
|
16714
|
+
return new Promise((resolve5, reject) => {
|
|
16598
16715
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
16599
16716
|
p.then((v2) => {
|
|
16600
16717
|
clearTimeout(t);
|
|
16601
|
-
|
|
16718
|
+
resolve5(v2);
|
|
16602
16719
|
}, (e) => {
|
|
16603
16720
|
clearTimeout(t);
|
|
16604
16721
|
reject(e);
|
|
@@ -16839,7 +16956,7 @@ var OpencodeSseBridge = class {
|
|
|
16839
16956
|
// packages/agents/dist/interactive/claude.js
|
|
16840
16957
|
import { execSync as execSync6 } from "child_process";
|
|
16841
16958
|
import { readFileSync as readFileSync14 } from "fs";
|
|
16842
|
-
import { homedir as
|
|
16959
|
+
import { homedir as homedir8 } from "os";
|
|
16843
16960
|
import { join as join21 } from "path";
|
|
16844
16961
|
var nextHookRequestId = Date.now();
|
|
16845
16962
|
|
|
@@ -16973,14 +17090,14 @@ function runHeadless(opts) {
|
|
|
16973
17090
|
if (err.length > ERR_CAP)
|
|
16974
17091
|
err = err.slice(err.length - ERR_CAP);
|
|
16975
17092
|
});
|
|
16976
|
-
const result = new Promise((
|
|
17093
|
+
const result = new Promise((resolve5) => {
|
|
16977
17094
|
child.once("error", (e) => {
|
|
16978
17095
|
if (debounceTimer) {
|
|
16979
17096
|
clearTimeout(debounceTimer);
|
|
16980
17097
|
debounceTimer = null;
|
|
16981
17098
|
}
|
|
16982
17099
|
opts.emit({ type: "task.failed", id: opts.id, error: `spawn error: ${e.message}`, exitCode: void 0 });
|
|
16983
|
-
|
|
17100
|
+
resolve5();
|
|
16984
17101
|
});
|
|
16985
17102
|
child.on("close", (code) => {
|
|
16986
17103
|
if (chunksSinceProgress > 0)
|
|
@@ -16997,14 +17114,14 @@ function runHeadless(opts) {
|
|
|
16997
17114
|
const ref = opts.writeResult ? opts.writeResult(opts.id, res.payload ?? "") : "";
|
|
16998
17115
|
opts.emit({ type: "task.done", id: opts.id, resultRef: ref, parseWarning: res.parseWarning });
|
|
16999
17116
|
}
|
|
17000
|
-
|
|
17117
|
+
resolve5();
|
|
17001
17118
|
});
|
|
17002
17119
|
});
|
|
17003
17120
|
return { result, kill: () => child.kill("SIGTERM") };
|
|
17004
17121
|
}
|
|
17005
17122
|
|
|
17006
17123
|
// packages/agents/dist/claude/peer-registry-source.js
|
|
17007
|
-
import { readdirSync as
|
|
17124
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
17008
17125
|
import { join as join22 } from "path";
|
|
17009
17126
|
function defaultIsAlive(pid) {
|
|
17010
17127
|
try {
|
|
@@ -17029,7 +17146,7 @@ var ClaudePeerRegistrySource = class {
|
|
|
17029
17146
|
pollMs;
|
|
17030
17147
|
log;
|
|
17031
17148
|
constructor(o = {}) {
|
|
17032
|
-
this.readdir = o.readdir ?? (() =>
|
|
17149
|
+
this.readdir = o.readdir ?? (() => readdirSync7(CLAUDE_SESSIONS_DIR));
|
|
17033
17150
|
this.readFile = o.readFile ?? ((n) => readFileSync15(join22(CLAUDE_SESSIONS_DIR, n), "utf8"));
|
|
17034
17151
|
this.isAlive = o.isAlive ?? defaultIsAlive;
|
|
17035
17152
|
this.now = o.now ?? Date.now;
|
|
@@ -17128,12 +17245,12 @@ function writeLine(socketPath, envelope, deps) {
|
|
|
17128
17245
|
error: `line exceeds the receiver's 1 MiB buffer cap (${Buffer.byteLength(line2, "utf8")} bytes)`
|
|
17129
17246
|
});
|
|
17130
17247
|
}
|
|
17131
|
-
return new Promise((
|
|
17248
|
+
return new Promise((resolve5) => {
|
|
17132
17249
|
let settled = false;
|
|
17133
17250
|
const done = (r) => {
|
|
17134
17251
|
if (!settled) {
|
|
17135
17252
|
settled = true;
|
|
17136
|
-
|
|
17253
|
+
resolve5(r);
|
|
17137
17254
|
}
|
|
17138
17255
|
};
|
|
17139
17256
|
const sock = deps.connect(socketPath);
|
|
@@ -17255,13 +17372,13 @@ var ClaudeReceiptListener = class {
|
|
|
17255
17372
|
* target is a live socket and the subsequent listen fails loudly instead.
|
|
17256
17373
|
*/
|
|
17257
17374
|
start() {
|
|
17258
|
-
return new Promise((
|
|
17375
|
+
return new Promise((resolve5, reject) => {
|
|
17259
17376
|
this.deps.unlinkStale?.(this.deps.socketPath);
|
|
17260
17377
|
this.server = this.deps.createServer((conn) => this.handle(conn));
|
|
17261
17378
|
this.server.once("error", (e) => reject(e));
|
|
17262
17379
|
this.server.listen(this.deps.socketPath, () => {
|
|
17263
17380
|
this.server.unref?.();
|
|
17264
|
-
|
|
17381
|
+
resolve5();
|
|
17265
17382
|
});
|
|
17266
17383
|
});
|
|
17267
17384
|
}
|
|
@@ -17287,15 +17404,15 @@ var ClaudeReceiptListener = class {
|
|
|
17287
17404
|
* is silent by design, so "no receipt" is the common, healthy case.
|
|
17288
17405
|
*/
|
|
17289
17406
|
waitFor(origMsgId, timeoutMs) {
|
|
17290
|
-
return new Promise((
|
|
17407
|
+
return new Promise((resolve5) => {
|
|
17291
17408
|
const timer = setTimeout(() => {
|
|
17292
17409
|
this.waiters.delete(origMsgId);
|
|
17293
|
-
|
|
17410
|
+
resolve5(void 0);
|
|
17294
17411
|
}, timeoutMs);
|
|
17295
17412
|
this.waiters.set(origMsgId, (r) => {
|
|
17296
17413
|
clearTimeout(timer);
|
|
17297
17414
|
this.waiters.delete(origMsgId);
|
|
17298
|
-
|
|
17415
|
+
resolve5(r);
|
|
17299
17416
|
});
|
|
17300
17417
|
});
|
|
17301
17418
|
}
|
|
@@ -17346,9 +17463,9 @@ var ClaudeReceiptListener = class {
|
|
|
17346
17463
|
};
|
|
17347
17464
|
|
|
17348
17465
|
// packages/agents/dist/claude/api-key-approval.js
|
|
17349
|
-
import
|
|
17350
|
-
import
|
|
17351
|
-
import
|
|
17466
|
+
import fs35 from "fs";
|
|
17467
|
+
import os16 from "os";
|
|
17468
|
+
import path40 from "path";
|
|
17352
17469
|
|
|
17353
17470
|
// packages/agents/dist/opencode/http-channel.js
|
|
17354
17471
|
init_dist2();
|
|
@@ -17440,10 +17557,10 @@ var OpencodeHttpChannel = class {
|
|
|
17440
17557
|
return cached;
|
|
17441
17558
|
const directory = this.directoryFor?.(taskId);
|
|
17442
17559
|
const createdAfterMs = this.createdAfterFor?.(taskId);
|
|
17443
|
-
for (const
|
|
17560
|
+
for (const path44 of ["/session?", "/api/session?"]) {
|
|
17444
17561
|
let res;
|
|
17445
17562
|
try {
|
|
17446
|
-
res = await this.request(`http://127.0.0.1:${port}${
|
|
17563
|
+
res = await this.request(`http://127.0.0.1:${port}${path44}`, { method: "GET" });
|
|
17447
17564
|
} catch {
|
|
17448
17565
|
return void 0;
|
|
17449
17566
|
}
|
|
@@ -17517,9 +17634,9 @@ function createOpencodeFactAdapter(deps) {
|
|
|
17517
17634
|
}
|
|
17518
17635
|
|
|
17519
17636
|
// packages/agents/dist/rules-hooks/install.js
|
|
17520
|
-
import
|
|
17521
|
-
import
|
|
17522
|
-
import
|
|
17637
|
+
import fs36 from "fs";
|
|
17638
|
+
import os17 from "os";
|
|
17639
|
+
import path41 from "path";
|
|
17523
17640
|
|
|
17524
17641
|
// packages/workspaces/dist/runtimes/cmux.js
|
|
17525
17642
|
init_dist();
|
|
@@ -17536,7 +17653,7 @@ var CmuxTimeoutError = class extends Error {
|
|
|
17536
17653
|
}
|
|
17537
17654
|
};
|
|
17538
17655
|
function cmux(args) {
|
|
17539
|
-
return new Promise((
|
|
17656
|
+
return new Promise((resolve5, reject) => {
|
|
17540
17657
|
execFile3(
|
|
17541
17658
|
resolveCmuxBin(),
|
|
17542
17659
|
args,
|
|
@@ -17550,19 +17667,19 @@ function cmux(args) {
|
|
|
17550
17667
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
17551
17668
|
return;
|
|
17552
17669
|
}
|
|
17553
|
-
|
|
17670
|
+
resolve5(stdout.trim());
|
|
17554
17671
|
}
|
|
17555
17672
|
);
|
|
17556
17673
|
});
|
|
17557
17674
|
}
|
|
17558
17675
|
function cmuxStdin(args, input) {
|
|
17559
|
-
return new Promise((
|
|
17676
|
+
return new Promise((resolve5, reject) => {
|
|
17560
17677
|
const child = execFile3(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
|
|
17561
17678
|
if (err) {
|
|
17562
17679
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
17563
17680
|
return;
|
|
17564
17681
|
}
|
|
17565
|
-
|
|
17682
|
+
resolve5(stdout.trim());
|
|
17566
17683
|
});
|
|
17567
17684
|
child.stdin.end(input);
|
|
17568
17685
|
});
|
|
@@ -18257,9 +18374,9 @@ var NotifierRegistry = class {
|
|
|
18257
18374
|
};
|
|
18258
18375
|
|
|
18259
18376
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
18260
|
-
import
|
|
18261
|
-
import { existsSync as
|
|
18262
|
-
import
|
|
18377
|
+
import fs37 from "fs/promises";
|
|
18378
|
+
import { existsSync as existsSync12 } from "fs";
|
|
18379
|
+
import path42 from "path";
|
|
18263
18380
|
|
|
18264
18381
|
// packages/workspaces/dist/workspaces/registry.js
|
|
18265
18382
|
init_dist();
|
|
@@ -18331,12 +18448,12 @@ var CmuxEventsBridge = class {
|
|
|
18331
18448
|
continue;
|
|
18332
18449
|
}
|
|
18333
18450
|
this.child = child;
|
|
18334
|
-
await new Promise((
|
|
18451
|
+
await new Promise((resolve5) => {
|
|
18335
18452
|
let settled = false;
|
|
18336
18453
|
const done = () => {
|
|
18337
18454
|
if (!settled) {
|
|
18338
18455
|
settled = true;
|
|
18339
|
-
|
|
18456
|
+
resolve5();
|
|
18340
18457
|
}
|
|
18341
18458
|
};
|
|
18342
18459
|
child.stdout?.on("data", (b) => this.onData(b));
|
|
@@ -18412,9 +18529,9 @@ var CmuxEventsBridge = class {
|
|
|
18412
18529
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
18413
18530
|
init_dist2();
|
|
18414
18531
|
init_dist();
|
|
18415
|
-
import { readdirSync as
|
|
18532
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
18416
18533
|
import { join as join23 } from "path";
|
|
18417
|
-
import { homedir as
|
|
18534
|
+
import { homedir as homedir9 } from "os";
|
|
18418
18535
|
|
|
18419
18536
|
// packages/workspaces/dist/cmux-daemon/store-fingerprint.js
|
|
18420
18537
|
init_dist();
|
|
@@ -18511,11 +18628,11 @@ function readLivenessSnapshot(files, readFile7, projects, argvRecovery = {}) {
|
|
|
18511
18628
|
|
|
18512
18629
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
18513
18630
|
async function readCmuxLiveness() {
|
|
18514
|
-
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join23(
|
|
18631
|
+
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join23(homedir9(), ".cmuxterm");
|
|
18515
18632
|
const projects = loadConfig().projects;
|
|
18516
18633
|
let files;
|
|
18517
18634
|
try {
|
|
18518
|
-
files =
|
|
18635
|
+
files = readdirSync8(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
18519
18636
|
} catch (e) {
|
|
18520
18637
|
throw new Error(`liveness: could not read cmux state dir ${dir}: ${e.message}`);
|
|
18521
18638
|
}
|
|
@@ -18583,8 +18700,8 @@ var DaemonCmux = class {
|
|
|
18583
18700
|
|
|
18584
18701
|
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
18585
18702
|
import { join as join24 } from "path";
|
|
18586
|
-
import { homedir as
|
|
18587
|
-
import { watch, readdirSync as
|
|
18703
|
+
import { homedir as homedir10 } from "os";
|
|
18704
|
+
import { watch, readdirSync as readdirSync9, readFileSync as readFileSync17, existsSync as existsSync13 } from "fs";
|
|
18588
18705
|
var CmuxStoreSource = class {
|
|
18589
18706
|
name = "cmux-store";
|
|
18590
18707
|
stateDir;
|
|
@@ -18613,7 +18730,7 @@ var CmuxStoreSource = class {
|
|
|
18613
18730
|
active = false;
|
|
18614
18731
|
lastError = null;
|
|
18615
18732
|
constructor(opts = {}) {
|
|
18616
|
-
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join24(
|
|
18733
|
+
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join24(homedir10(), ".cmuxterm");
|
|
18617
18734
|
this.debounceMs = opts.debounceMs ?? 50;
|
|
18618
18735
|
this.lockRetryMs = opts.lockRetryMs ?? 50;
|
|
18619
18736
|
this.maxLockRetries = opts.maxLockRetries ?? 3;
|
|
@@ -18621,7 +18738,7 @@ var CmuxStoreSource = class {
|
|
|
18621
18738
|
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive2;
|
|
18622
18739
|
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
18623
18740
|
this.readFile = opts.readFile ?? defaultReadFile;
|
|
18624
|
-
this.fileExists = opts.fileExists ??
|
|
18741
|
+
this.fileExists = opts.fileExists ?? existsSync13;
|
|
18625
18742
|
this.watchDir = opts.watchDir ?? defaultWatchDir;
|
|
18626
18743
|
this.scheduleTimer = opts.scheduleTimer ?? setTimeout;
|
|
18627
18744
|
this.cancelTimer = opts.cancelTimer ?? clearTimeout;
|
|
@@ -18804,14 +18921,14 @@ function defaultIsPidAlive2(pid) {
|
|
|
18804
18921
|
}
|
|
18805
18922
|
function defaultListFiles(dir) {
|
|
18806
18923
|
try {
|
|
18807
|
-
return
|
|
18924
|
+
return readdirSync9(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
18808
18925
|
} catch {
|
|
18809
18926
|
return [];
|
|
18810
18927
|
}
|
|
18811
18928
|
}
|
|
18812
|
-
function defaultReadFile(
|
|
18929
|
+
function defaultReadFile(path44) {
|
|
18813
18930
|
try {
|
|
18814
|
-
return readFileSync17(
|
|
18931
|
+
return readFileSync17(path44, "utf-8");
|
|
18815
18932
|
} catch {
|
|
18816
18933
|
return void 0;
|
|
18817
18934
|
}
|
|
@@ -18828,8 +18945,8 @@ function defaultWatchDir(dir, cb, onError) {
|
|
|
18828
18945
|
|
|
18829
18946
|
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
18830
18947
|
import { join as join25 } from "path";
|
|
18831
|
-
import { homedir as
|
|
18832
|
-
import { mkdirSync as
|
|
18948
|
+
import { homedir as homedir11 } from "os";
|
|
18949
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
|
|
18833
18950
|
var CLAUDE_HOOK_EVENTS = [
|
|
18834
18951
|
["SessionStart", "session-start"],
|
|
18835
18952
|
["UserPromptSubmit", "prompt-submit"],
|
|
@@ -18936,7 +19053,7 @@ function removeForeignGateHandlers(entries) {
|
|
|
18936
19053
|
return removed;
|
|
18937
19054
|
}
|
|
18938
19055
|
function installClaudeHooks(opts = {}) {
|
|
18939
|
-
const settingsPath = opts.settingsPath ?? join25(
|
|
19056
|
+
const settingsPath = opts.settingsPath ?? join25(homedir11(), ".claude", "settings.json");
|
|
18940
19057
|
const cliCmd = opts.cliCmd ?? DEFAULT_CLI_CMD;
|
|
18941
19058
|
const hookCmd = opts.hookCmd ?? `${cliCmd} hooks`;
|
|
18942
19059
|
const gateCmd = `${cliCmd} gate`;
|
|
@@ -19191,16 +19308,16 @@ function extractDetail(sub, payload) {
|
|
|
19191
19308
|
}
|
|
19192
19309
|
return void 0;
|
|
19193
19310
|
}
|
|
19194
|
-
function defaultReadFile2(
|
|
19311
|
+
function defaultReadFile2(path44) {
|
|
19195
19312
|
try {
|
|
19196
|
-
return readFileSync18(
|
|
19313
|
+
return readFileSync18(path44, "utf-8");
|
|
19197
19314
|
} catch {
|
|
19198
19315
|
return void 0;
|
|
19199
19316
|
}
|
|
19200
19317
|
}
|
|
19201
|
-
function defaultWriteFile(
|
|
19202
|
-
|
|
19203
|
-
|
|
19318
|
+
function defaultWriteFile(path44, content) {
|
|
19319
|
+
mkdirSync9(path44.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
19320
|
+
writeFileSync12(path44, content, "utf-8");
|
|
19204
19321
|
}
|
|
19205
19322
|
|
|
19206
19323
|
// packages/workspaces/dist/crew-pane.js
|
|
@@ -19285,24 +19402,24 @@ init_dist();
|
|
|
19285
19402
|
|
|
19286
19403
|
// packages/cli/src/lib/daemon-restart-broadcast.ts
|
|
19287
19404
|
init_dist();
|
|
19288
|
-
import
|
|
19289
|
-
function statePath2(
|
|
19290
|
-
return
|
|
19405
|
+
import path43 from "path";
|
|
19406
|
+
function statePath2(stateRoot2) {
|
|
19407
|
+
return path43.join(stateRoot2, "daemon-restart-state.json");
|
|
19291
19408
|
}
|
|
19292
19409
|
function computeRestartSignature(version, buildMtimeMs) {
|
|
19293
19410
|
return `${version}::${buildMtimeMs}`;
|
|
19294
19411
|
}
|
|
19295
|
-
function readPersistedRestartSignature(
|
|
19412
|
+
function readPersistedRestartSignature(stateRoot2) {
|
|
19296
19413
|
try {
|
|
19297
|
-
const raw = readConfigFileSync(statePath2(
|
|
19414
|
+
const raw = readConfigFileSync(statePath2(stateRoot2));
|
|
19298
19415
|
const data = JSON.parse(raw);
|
|
19299
19416
|
return typeof data.signature === "string" ? data.signature : null;
|
|
19300
19417
|
} catch {
|
|
19301
19418
|
return null;
|
|
19302
19419
|
}
|
|
19303
19420
|
}
|
|
19304
|
-
function writePersistedRestartSignature(
|
|
19305
|
-
writeConfigFileSync(statePath2(
|
|
19421
|
+
function writePersistedRestartSignature(stateRoot2, signature) {
|
|
19422
|
+
writeConfigFileSync(statePath2(stateRoot2), JSON.stringify({ signature }, null, 2) + "\n");
|
|
19306
19423
|
}
|
|
19307
19424
|
function restartNotice(version, isDevRebuild) {
|
|
19308
19425
|
const suffix = isDevRebuild ? " (dev build)" : "";
|
|
@@ -19323,20 +19440,20 @@ async function notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebui
|
|
|
19323
19440
|
}
|
|
19324
19441
|
async function maybeBroadcastDaemonRestart(opts) {
|
|
19325
19442
|
try {
|
|
19326
|
-
const { version, buildMtimeMs, stateRoot, config, driver, appendCaptainMessage: appendCaptainMessage2 } = opts;
|
|
19443
|
+
const { version, buildMtimeMs, stateRoot: stateRoot2, config, driver, appendCaptainMessage: appendCaptainMessage2 } = opts;
|
|
19327
19444
|
const signature = computeRestartSignature(version, buildMtimeMs);
|
|
19328
|
-
const previous = readPersistedRestartSignature(
|
|
19445
|
+
const previous = readPersistedRestartSignature(stateRoot2);
|
|
19329
19446
|
if (previous === signature) return;
|
|
19330
19447
|
const isDevRebuild = previous !== null && previous.split("::")[0] === version;
|
|
19331
19448
|
await notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild, appendCaptainMessage2);
|
|
19332
|
-
writePersistedRestartSignature(
|
|
19449
|
+
writePersistedRestartSignature(stateRoot2, signature);
|
|
19333
19450
|
} catch {
|
|
19334
19451
|
}
|
|
19335
19452
|
}
|
|
19336
19453
|
|
|
19337
19454
|
// packages/cli/src/lib/captain-channel-factory.ts
|
|
19338
19455
|
import { createServer as createServer4, connect as netConnect } from "net";
|
|
19339
|
-
import
|
|
19456
|
+
import fs38 from "fs";
|
|
19340
19457
|
import { join as join26 } from "path";
|
|
19341
19458
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
19342
19459
|
import chalk2 from "chalk";
|
|
@@ -19345,15 +19462,15 @@ var shared;
|
|
|
19345
19462
|
var registryEntryPath = () => join26(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
19346
19463
|
function unregisterSenderIdentity() {
|
|
19347
19464
|
try {
|
|
19348
|
-
|
|
19465
|
+
fs38.unlinkSync(registryEntryPath());
|
|
19349
19466
|
} catch {
|
|
19350
19467
|
}
|
|
19351
19468
|
}
|
|
19352
19469
|
function registerSenderIdentity(socketPath) {
|
|
19353
19470
|
try {
|
|
19354
19471
|
unregisterSenderIdentity();
|
|
19355
|
-
|
|
19356
|
-
|
|
19472
|
+
fs38.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
19473
|
+
fs38.writeFileSync(
|
|
19357
19474
|
registryEntryPath(),
|
|
19358
19475
|
JSON.stringify({
|
|
19359
19476
|
pid: process.pid,
|
|
@@ -19379,7 +19496,7 @@ async function sharedReceiptListener() {
|
|
|
19379
19496
|
// must never be the reason we refuse to start.
|
|
19380
19497
|
unlinkStale: (p) => {
|
|
19381
19498
|
try {
|
|
19382
|
-
|
|
19499
|
+
fs38.unlinkSync(p);
|
|
19383
19500
|
} catch {
|
|
19384
19501
|
}
|
|
19385
19502
|
},
|
|
@@ -19404,7 +19521,7 @@ async function buildCaptainChannel() {
|
|
|
19404
19521
|
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
19405
19522
|
// missed and every captain ping reported accepted-unconfirmed.
|
|
19406
19523
|
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
19407
|
-
wire: (p, e) => writeLine(p, e, { connect: (
|
|
19524
|
+
wire: (p, e) => writeLine(p, e, { connect: (path44) => netConnect(path44) }),
|
|
19408
19525
|
receipts,
|
|
19409
19526
|
newMsgId: () => randomUUID5(),
|
|
19410
19527
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
@@ -19422,8 +19539,8 @@ async function buildCaptainChannels(opts) {
|
|
|
19422
19539
|
agentFor: (project) => resolveCaptainAgent(opts.stateRoot, project, opts.configAgent)
|
|
19423
19540
|
};
|
|
19424
19541
|
}
|
|
19425
|
-
function resolveCaptainAgent(
|
|
19426
|
-
return readCaptainAddress(
|
|
19542
|
+
function resolveCaptainAgent(stateRoot2, project, configAgent) {
|
|
19543
|
+
return readCaptainAddress(stateRoot2, project)?.agent ?? configAgent;
|
|
19427
19544
|
}
|
|
19428
19545
|
async function retryForever(build, opts = {}) {
|
|
19429
19546
|
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => {
|
|
@@ -19463,7 +19580,7 @@ function readPkgVersion() {
|
|
|
19463
19580
|
}
|
|
19464
19581
|
}
|
|
19465
19582
|
var PKG_VERSION = readPkgVersion();
|
|
19466
|
-
function buildTelegramBridge(cfg,
|
|
19583
|
+
function buildTelegramBridge(cfg, stateRoot2, log, deliverInbound, usageFor) {
|
|
19467
19584
|
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
19468
19585
|
if (!token) {
|
|
19469
19586
|
log("telegram: config present but no botToken / TELEGRAM_BOT_TOKEN set \u2014 bridge disabled");
|
|
@@ -19477,7 +19594,7 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19477
19594
|
const runCommand = createRunCommand(CLI_BIN);
|
|
19478
19595
|
const sendReply = (threadId, text, replyMarkup) => client.sendMessage(cfg.supergroupId, threadId, text, replyMarkup);
|
|
19479
19596
|
const lifecycle = createInboundLifecycle({
|
|
19480
|
-
stateRoot,
|
|
19597
|
+
stateRoot: stateRoot2,
|
|
19481
19598
|
cfg: { supergroupId: cfg.supergroupId },
|
|
19482
19599
|
sendChatAction: (chatId, threadId, action) => client.sendChatAction(chatId, threadId, action),
|
|
19483
19600
|
sendReply: async (threadId, text) => {
|
|
@@ -19498,15 +19615,15 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19498
19615
|
// timestamp, so the reader hashes the screen and asks the persisted
|
|
19499
19616
|
// pending store how long that exact screen has been up. Persisted, so a
|
|
19500
19617
|
// daemon restart cannot hand a dead captain a fresh 15 minutes.
|
|
19501
|
-
noteScreen: (project, hash) => notePaneScreen(
|
|
19618
|
+
noteScreen: (project, hash) => notePaneScreen(stateRoot2, project, hash, Date.now())
|
|
19502
19619
|
}
|
|
19503
19620
|
),
|
|
19504
19621
|
log
|
|
19505
19622
|
});
|
|
19506
19623
|
return createTelegramBridge({
|
|
19507
19624
|
cfg,
|
|
19508
|
-
stateRoot,
|
|
19509
|
-
configRoot:
|
|
19625
|
+
stateRoot: stateRoot2,
|
|
19626
|
+
configRoot: CONFIG_DIR,
|
|
19510
19627
|
client,
|
|
19511
19628
|
appendCaptainMessage,
|
|
19512
19629
|
log,
|
|
@@ -19522,7 +19639,7 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19522
19639
|
// every link on the machine. `undefined` correctly means "don't prune".
|
|
19523
19640
|
registeredProjects: () => {
|
|
19524
19641
|
try {
|
|
19525
|
-
const parsed = JSON.parse(readFileSync19(
|
|
19642
|
+
const parsed = JSON.parse(readFileSync19(DEFAULT_CONFIG_PATH, "utf8"));
|
|
19526
19643
|
return Object.keys(parsed.projects ?? {});
|
|
19527
19644
|
} catch {
|
|
19528
19645
|
return void 0;
|
|
@@ -19562,8 +19679,18 @@ function startSquadrantd(opts = {}) {
|
|
|
19562
19679
|
`
|
|
19563
19680
|
);
|
|
19564
19681
|
}
|
|
19682
|
+
if (isDefaultSocket && !opts.stateRoot) {
|
|
19683
|
+
try {
|
|
19684
|
+
const seed = seedStateFromLegacy();
|
|
19685
|
+
if (seed.seeded) process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} state seeded from legacy ${legacyStateDir()} \u2192 ${stateRoot()}
|
|
19686
|
+
`);
|
|
19687
|
+
} catch (err) {
|
|
19688
|
+
process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} legacy state seed failed: ${err instanceof Error ? err.message : String(err)}
|
|
19689
|
+
`);
|
|
19690
|
+
}
|
|
19691
|
+
}
|
|
19565
19692
|
const ctx = buildContext(opts);
|
|
19566
|
-
const { stateRoot, store, log, spawn: spawn2, writeResult, inFlightHeadlessIds, activeHeadlessKills } = ctx;
|
|
19693
|
+
const { stateRoot: stateRoot2, store, log, spawn: spawn2, writeResult, inFlightHeadlessIds, activeHeadlessKills } = ctx;
|
|
19567
19694
|
const { broadcast, schedulePromotion, cancelPromotionsFor } = createAttach(ctx);
|
|
19568
19695
|
ctx.broadcast = broadcast;
|
|
19569
19696
|
ctx.schedulePromotion = schedulePromotion;
|
|
@@ -19639,7 +19766,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19639
19766
|
const rec = store.listAll().find((r) => r.id === taskId);
|
|
19640
19767
|
if (!rec) return false;
|
|
19641
19768
|
try {
|
|
19642
|
-
const cfg2 = JSON.parse(readFileSync19(join27(
|
|
19769
|
+
const cfg2 = JSON.parse(readFileSync19(join27(stateRoot2, rec.project, taskId, "opencode.json"), "utf8"));
|
|
19643
19770
|
return cfg2?.permission?.bash === "ask" && cfg2?.permission?.edit === "ask";
|
|
19644
19771
|
} catch {
|
|
19645
19772
|
return false;
|
|
@@ -19659,7 +19786,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19659
19786
|
(r) => r.mode === "interactive" && !TERMINAL_STATES.has(r.state) && r.cwd === hook.cwd
|
|
19660
19787
|
);
|
|
19661
19788
|
},
|
|
19662
|
-
cursorFile: join27(
|
|
19789
|
+
cursorFile: join27(stateRoot2, "cmux-events.seq"),
|
|
19663
19790
|
log
|
|
19664
19791
|
});
|
|
19665
19792
|
const cmuxStoreSource = new CmuxStoreSource({ log });
|
|
@@ -19689,7 +19816,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19689
19816
|
const tgCfg = loadConfig().telegram;
|
|
19690
19817
|
ctx.telegramBridge = opts.telegramBridge ?? (tgCfg && !process.env.VITEST ? buildTelegramBridge(
|
|
19691
19818
|
tgCfg,
|
|
19692
|
-
|
|
19819
|
+
stateRoot2,
|
|
19693
19820
|
log,
|
|
19694
19821
|
(project, text) => Promise.resolve().then(() => (init_dist2(), dist_exports)).then((core2) => core2.deliverToCaptain(project, text, {
|
|
19695
19822
|
channel: ctx.captainChannel,
|
|
@@ -19708,7 +19835,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19708
19835
|
ctx.captainChannelMode = () => resolveCaptainChannelMode(loadConfig().defaults);
|
|
19709
19836
|
if (ctx.captainChannelMode() !== "off") {
|
|
19710
19837
|
void buildCaptainChannelsWithRetry({
|
|
19711
|
-
stateRoot:
|
|
19838
|
+
stateRoot: ctx.stateRoot,
|
|
19712
19839
|
configAgent: loadConfig().defaults.roles?.captain?.agent,
|
|
19713
19840
|
log
|
|
19714
19841
|
}).then(({ channels, agentFor }) => {
|
|
@@ -19923,10 +20050,10 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
19923
20050
|
void maybeBroadcastDaemonRestart({
|
|
19924
20051
|
version: PKG_VERSION,
|
|
19925
20052
|
buildMtimeMs,
|
|
19926
|
-
stateRoot,
|
|
20053
|
+
stateRoot: stateRoot2,
|
|
19927
20054
|
config: restartConfig,
|
|
19928
20055
|
driver: registry.global(restartConfig),
|
|
19929
|
-
appendCaptainMessage: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
|
|
20056
|
+
appendCaptainMessage: (project, text) => appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" })
|
|
19930
20057
|
});
|
|
19931
20058
|
} catch (e) {
|
|
19932
20059
|
log(`daemon-restart broadcast setup failed: ${e.message}`);
|
|
@@ -19962,8 +20089,8 @@ function logCrashMarker(kind, err) {
|
|
|
19962
20089
|
const message = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
19963
20090
|
process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${kind} pid=${process.pid} error=${message}
|
|
19964
20091
|
`);
|
|
19965
|
-
const
|
|
19966
|
-
writeExitMarker(
|
|
20092
|
+
const stateRoot2 = stateRoot();
|
|
20093
|
+
writeExitMarker(stateRoot2, {
|
|
19967
20094
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19968
20095
|
pid: process.pid,
|
|
19969
20096
|
reason: kind,
|
|
@@ -19974,8 +20101,8 @@ function logCrashMarker(kind, err) {
|
|
|
19974
20101
|
}, (m) => process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${m}
|
|
19975
20102
|
`));
|
|
19976
20103
|
}
|
|
19977
|
-
function isMonorepoCheckout(scriptPath, dirExists =
|
|
19978
|
-
return dirExists(join27(dirname6(
|
|
20104
|
+
function isMonorepoCheckout(scriptPath, dirExists = existsSync14) {
|
|
20105
|
+
return dirExists(join27(dirname6(resolve4(scriptPath)), "..", "packages"));
|
|
19979
20106
|
}
|
|
19980
20107
|
function isLinkedWorktree(scriptPath, statFile = (p) => {
|
|
19981
20108
|
try {
|
|
@@ -19984,7 +20111,7 @@ function isLinkedWorktree(scriptPath, statFile = (p) => {
|
|
|
19984
20111
|
return void 0;
|
|
19985
20112
|
}
|
|
19986
20113
|
}) {
|
|
19987
|
-
const dotGit = join27(dirname6(
|
|
20114
|
+
const dotGit = join27(dirname6(resolve4(scriptPath)), "..", ".git");
|
|
19988
20115
|
return statFile(dotGit)?.isFile === true;
|
|
19989
20116
|
}
|
|
19990
20117
|
if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
@@ -20011,13 +20138,13 @@ if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
|
20011
20138
|
sweepMs: 3e4,
|
|
20012
20139
|
// #898: KB reconcile scheduler on the 60s tick. Enqueues to the home captain's mailbox; never spawns.
|
|
20013
20140
|
knowledgeTick: async () => {
|
|
20014
|
-
const
|
|
20141
|
+
const stateRoot2 = stateRoot();
|
|
20015
20142
|
await tickKnowledgeSchedule({
|
|
20016
20143
|
cfg: loadConfig(),
|
|
20017
|
-
stateRoot,
|
|
20144
|
+
stateRoot: stateRoot2,
|
|
20018
20145
|
log: (m) => process.stderr.write(`[squadrantd] ${m}
|
|
20019
20146
|
`),
|
|
20020
|
-
enqueue: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
|
|
20147
|
+
enqueue: (project, text) => appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" })
|
|
20021
20148
|
});
|
|
20022
20149
|
}
|
|
20023
20150
|
});
|