squadrant 0.28.0 → 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 +1712 -1342
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +1044 -859
- 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,22 +9568,46 @@ 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();
|
|
9578
|
+
}
|
|
9579
|
+
function refCandidates(kbRoot, sources, ref) {
|
|
9580
|
+
const asIs = path17.resolve(kbRoot, expandHome(ref));
|
|
9581
|
+
if (path17.isAbsolute(expandHome(ref)))
|
|
9582
|
+
return [asIs];
|
|
9583
|
+
const bases = /* @__PURE__ */ new Set([kbRoot]);
|
|
9584
|
+
for (const s of sources) {
|
|
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);
|
|
9588
|
+
bases.add(d);
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
return [.../* @__PURE__ */ new Set([asIs, ...[...bases].map((b) => path17.join(b, ref))])];
|
|
9496
9592
|
}
|
|
9497
|
-
function
|
|
9498
|
-
const
|
|
9499
|
-
|
|
9500
|
-
|
|
9593
|
+
function refPath(kbRoot, sources, ref) {
|
|
9594
|
+
const cands = refCandidates(kbRoot, sources, ref);
|
|
9595
|
+
return cands.find(isFile) ?? cands.find((f) => sources.some((e) => entryCovers(kbRoot, e, f))) ?? cands[0];
|
|
9596
|
+
}
|
|
9597
|
+
function refCanon(kbRoot, sources) {
|
|
9598
|
+
const memo = /* @__PURE__ */ new Map();
|
|
9599
|
+
return (ref) => {
|
|
9600
|
+
let c = memo.get(ref);
|
|
9601
|
+
if (c === void 0) {
|
|
9602
|
+
c = refFor(kbRoot, refPath(kbRoot, sources, ref));
|
|
9603
|
+
memo.set(ref, c);
|
|
9604
|
+
}
|
|
9605
|
+
return c;
|
|
9606
|
+
};
|
|
9501
9607
|
}
|
|
9502
9608
|
function detectChange(kbRoot, file) {
|
|
9503
9609
|
const ref = refFor(kbRoot, file);
|
|
9504
|
-
const st =
|
|
9610
|
+
const st = fs16.statSync(file);
|
|
9505
9611
|
const prior = readState2(kbRoot, ref);
|
|
9506
9612
|
if (prior && prior.size === st.size && prior.mtimeMs === st.mtimeMs) {
|
|
9507
9613
|
return { ref, changed: false, stamp: { sha: prior.sha, size: st.size, mtimeMs: st.mtimeMs }, prior };
|
|
@@ -9558,13 +9664,13 @@ async function planIngest(kbRoot, opts) {
|
|
|
9558
9664
|
let text;
|
|
9559
9665
|
const conv = convertedFile(kbRoot, ref);
|
|
9560
9666
|
try {
|
|
9561
|
-
if (!changed &&
|
|
9562
|
-
text =
|
|
9667
|
+
if (!changed && fs16.existsSync(conv))
|
|
9668
|
+
text = fs16.readFileSync(conv, "utf8");
|
|
9563
9669
|
else {
|
|
9564
|
-
text = PASS_THROUGH.has(
|
|
9670
|
+
text = PASS_THROUGH.has(path17.extname(file).toLowerCase()) ? fs16.readFileSync(file, "utf8") : await converter(file);
|
|
9565
9671
|
if (!opts.dryRun) {
|
|
9566
|
-
|
|
9567
|
-
|
|
9672
|
+
fs16.mkdirSync(path17.dirname(conv), { recursive: true });
|
|
9673
|
+
fs16.writeFileSync(conv, text);
|
|
9568
9674
|
}
|
|
9569
9675
|
}
|
|
9570
9676
|
} catch (e) {
|
|
@@ -9595,7 +9701,7 @@ async function planIngest(kbRoot, opts) {
|
|
|
9595
9701
|
}
|
|
9596
9702
|
return plan;
|
|
9597
9703
|
}
|
|
9598
|
-
var execFileAsync, CONVERTED_DIR, DEFAULT_MAX_SECTIONS_PER_PASS, CHARS_PER_TOKEN, PASS_THROUGH, SECTION_TARGET_CHARS, markitdownConverter, expandHome,
|
|
9704
|
+
var execFileAsync, CONVERTED_DIR, DEFAULT_MAX_SECTIONS_PER_PASS, CHARS_PER_TOKEN, PASS_THROUGH, SECTION_TARGET_CHARS, markitdownConverter, expandHome, cacheKey, convertedFile, stampFile, isFile, resolveSourceRef, entryCovers, sourceCovers, isGatedRef, sha256;
|
|
9599
9705
|
var init_ingest = __esm({
|
|
9600
9706
|
"packages/core/dist/knowledge/ingest.js"() {
|
|
9601
9707
|
execFileAsync = promisify2(execFile2);
|
|
@@ -9608,13 +9714,19 @@ var init_ingest = __esm({
|
|
|
9608
9714
|
const { stdout } = await execFileAsync("markitdown", [file], { maxBuffer: 64 * 1024 * 1024 });
|
|
9609
9715
|
return stdout;
|
|
9610
9716
|
};
|
|
9611
|
-
expandHome = (p) => p === "~" || p.startsWith("~/") ?
|
|
9612
|
-
fileForRef = (kbRoot, ref) => path16.resolve(kbRoot, expandHome(ref));
|
|
9717
|
+
expandHome = (p) => p === "~" || p.startsWith("~/") ? path17.join(os8.homedir(), p.slice(1)) : p;
|
|
9613
9718
|
cacheKey = (ref) => `${ref.replace(/[^A-Za-z0-9._-]+/g, "_")}.${crypto3.createHash("sha1").update(ref).digest("hex").slice(0, 6)}`;
|
|
9614
|
-
convertedFile = (kbRoot, ref) =>
|
|
9615
|
-
stampFile = (kbRoot, ref) =>
|
|
9616
|
-
|
|
9617
|
-
|
|
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();
|
|
9722
|
+
resolveSourceRef = (kbRoot, sources, ref) => refCandidates(kbRoot, sources, ref).find(isFile);
|
|
9723
|
+
entryCovers = (kbRoot, entry, file) => {
|
|
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;
|
|
9726
|
+
};
|
|
9727
|
+
sourceCovers = (kbRoot, sources, entry, ref) => entryCovers(kbRoot, entry, refPath(kbRoot, sources, ref));
|
|
9728
|
+
isGatedRef = (kbRoot, sources, ref) => sources.some((e) => e.review === "required" && sourceCovers(kbRoot, sources, e, ref));
|
|
9729
|
+
sha256 = (file) => crypto3.createHash("sha256").update(fs16.readFileSync(file)).digest("hex");
|
|
9618
9730
|
}
|
|
9619
9731
|
});
|
|
9620
9732
|
|
|
@@ -9695,15 +9807,14 @@ var init_verify = __esm({
|
|
|
9695
9807
|
});
|
|
9696
9808
|
|
|
9697
9809
|
// packages/core/dist/knowledge/store.js
|
|
9698
|
-
import
|
|
9699
|
-
import
|
|
9700
|
-
import path17 from "path";
|
|
9810
|
+
import fs17 from "fs";
|
|
9811
|
+
import path18 from "path";
|
|
9701
9812
|
import matter2 from "gray-matter";
|
|
9702
9813
|
function duplicateIdProblem(id, ignored, used) {
|
|
9703
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.`;
|
|
9704
9815
|
}
|
|
9705
9816
|
function isPendingProposalDup(e, rulesDir) {
|
|
9706
|
-
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);
|
|
9707
9818
|
}
|
|
9708
9819
|
function splitStatement(body) {
|
|
9709
9820
|
const first = body.trim().split(/\n\s*\n/)[0] ?? "";
|
|
@@ -9712,8 +9823,8 @@ function splitStatement(body) {
|
|
|
9712
9823
|
function listMarkdown(dir, includeProposed) {
|
|
9713
9824
|
const out = [];
|
|
9714
9825
|
const walk = (d) => {
|
|
9715
|
-
for (const ent of
|
|
9716
|
-
const full =
|
|
9826
|
+
for (const ent of fs17.readdirSync(d, { withFileTypes: true })) {
|
|
9827
|
+
const full = path18.join(d, ent.name);
|
|
9717
9828
|
if (ent.isDirectory()) {
|
|
9718
9829
|
if (ent.name === PROPOSED_DIR && !includeProposed)
|
|
9719
9830
|
continue;
|
|
@@ -9724,11 +9835,11 @@ function listMarkdown(dir, includeProposed) {
|
|
|
9724
9835
|
}
|
|
9725
9836
|
};
|
|
9726
9837
|
walk(dir);
|
|
9727
|
-
const isProposed = (f) =>
|
|
9838
|
+
const isProposed = (f) => path18.relative(dir, f).split(path18.sep).includes(PROPOSED_DIR);
|
|
9728
9839
|
return out.sort((a, b) => Number(isProposed(a)) - Number(isProposed(b)) || (a < b ? -1 : a > b ? 1 : 0));
|
|
9729
9840
|
}
|
|
9730
9841
|
function loadRulesDir(dir, layer, opts = {}) {
|
|
9731
|
-
if (!
|
|
9842
|
+
if (!fs17.existsSync(dir))
|
|
9732
9843
|
return { rules: [], errors: [] };
|
|
9733
9844
|
const rules = [];
|
|
9734
9845
|
const errors = [];
|
|
@@ -9736,7 +9847,7 @@ function loadRulesDir(dir, layer, opts = {}) {
|
|
|
9736
9847
|
for (const file of listMarkdown(dir, opts.includeProposed ?? false)) {
|
|
9737
9848
|
let parsed;
|
|
9738
9849
|
try {
|
|
9739
|
-
parsed = matter2(
|
|
9850
|
+
parsed = matter2(fs17.readFileSync(file, "utf8"), NO_JS_FRONTMATTER);
|
|
9740
9851
|
} catch (e) {
|
|
9741
9852
|
errors.push({ file, problems: [`frontmatter parse error: ${e.message}`] });
|
|
9742
9853
|
continue;
|
|
@@ -9757,25 +9868,13 @@ function loadRulesDir(dir, layer, opts = {}) {
|
|
|
9757
9868
|
}
|
|
9758
9869
|
return { rules, errors };
|
|
9759
9870
|
}
|
|
9760
|
-
function resolveSourceRef(ref, kbRoot, sources) {
|
|
9761
|
-
const bases = /* @__PURE__ */ new Set([kbRoot]);
|
|
9762
|
-
for (const s of sources) {
|
|
9763
|
-
let d = path17.resolve(kbRoot, expandHome2(s.path.split(/[*?[{]/)[0]));
|
|
9764
|
-
for (let i = 0; i < 6 && d !== path17.dirname(d); i++) {
|
|
9765
|
-
d = path17.dirname(d);
|
|
9766
|
-
bases.add(d);
|
|
9767
|
-
}
|
|
9768
|
-
}
|
|
9769
|
-
const cands = [path17.resolve(kbRoot, expandHome2(ref)), ...[...bases].map((b) => path17.join(b, ref))];
|
|
9770
|
-
return cands.find((c) => fs16.existsSync(c) && fs16.statSync(c).isFile());
|
|
9771
|
-
}
|
|
9772
9871
|
function validateKb(opts) {
|
|
9773
9872
|
const { rules, errors } = loadRulesDir(opts.rulesDir, "kb", { includeProposed: true });
|
|
9774
9873
|
const checks = [];
|
|
9775
9874
|
const dups = errors.filter((e) => e.problems.some((p) => p.startsWith("duplicate id")) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9776
9875
|
const pendingDups = errors.filter((e) => isPendingProposalDup(e, opts.rulesDir)).length;
|
|
9777
9876
|
const invalid = errors.filter((e) => !dups.includes(e) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9778
|
-
const proposed = rules.filter((r) =>
|
|
9877
|
+
const proposed = rules.filter((r) => path18.relative(opts.rulesDir, r.file).split(path18.sep).includes(PROPOSED_DIR)).length;
|
|
9779
9878
|
checks.push({ level: "ok", message: `${rules.length + errors.length} rule files read (${rules.length - proposed} live, ${proposed + pendingDups} proposed parsed OK)` });
|
|
9780
9879
|
if (invalid.length)
|
|
9781
9880
|
for (const e of invalid)
|
|
@@ -9788,29 +9887,30 @@ function validateKb(opts) {
|
|
|
9788
9887
|
else
|
|
9789
9888
|
checks.push({ level: "ok", message: "no duplicate ids" });
|
|
9790
9889
|
const cache = /* @__PURE__ */ new Map();
|
|
9890
|
+
const canon = refCanon(opts.kbRoot, opts.sources);
|
|
9791
9891
|
let verified = 0, unresolved = 0, notIngested = 0;
|
|
9792
9892
|
for (const r of rules) {
|
|
9793
9893
|
r.sources.forEach((src, i) => {
|
|
9794
|
-
const file = resolveSourceRef(
|
|
9894
|
+
const file = resolveSourceRef(opts.kbRoot, opts.sources, src.ref);
|
|
9795
9895
|
if (!file) {
|
|
9796
9896
|
unresolved++;
|
|
9797
9897
|
return;
|
|
9798
9898
|
}
|
|
9799
9899
|
const key = `${src.ref}\0${file}`;
|
|
9800
9900
|
if (!cache.has(key)) {
|
|
9801
|
-
const conv = readState2(opts.kbRoot, src.ref) ? convertedFile(opts.kbRoot, src.ref) : null;
|
|
9802
|
-
const cached = !!conv &&
|
|
9901
|
+
const conv = readState2(opts.kbRoot, canon(src.ref)) ? convertedFile(opts.kbRoot, canon(src.ref)) : null;
|
|
9902
|
+
const cached = !!conv && fs17.existsSync(conv);
|
|
9803
9903
|
cache.set(key, {
|
|
9804
|
-
hay: normalizedViews(
|
|
9805
|
-
convertible: !cached && !PASS_THROUGH.has(
|
|
9904
|
+
hay: normalizedViews(fs17.readFileSync(cached ? conv : file, "utf8")),
|
|
9905
|
+
convertible: !cached && !PASS_THROUGH.has(path18.extname(file).toLowerCase())
|
|
9806
9906
|
});
|
|
9807
9907
|
}
|
|
9808
9908
|
const { hay, convertible } = cache.get(key);
|
|
9809
9909
|
const span = findQuote(hay, src.quote);
|
|
9810
9910
|
if (span) {
|
|
9811
9911
|
verified++;
|
|
9812
|
-
const active = r.status === "active" && !
|
|
9813
|
-
const err = active ? (opts.errata ?? []).find((e) => e.ref === src.ref && ((o) => !!o && spansOverlap(o, span))(findQuote(hay, e.quote))) : void 0;
|
|
9912
|
+
const active = r.status === "active" && !path18.relative(opts.rulesDir, r.file).split(path18.sep).includes(PROPOSED_DIR);
|
|
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;
|
|
9814
9914
|
if (err)
|
|
9815
9915
|
checks.push({ level: "warn", message: `${r.id}: grounded in erratum ${err.id} (${err.reason}); re-check or retire it (${r.file})` });
|
|
9816
9916
|
} else if (convertible)
|
|
@@ -9833,7 +9933,7 @@ function validateKb(opts) {
|
|
|
9833
9933
|
}
|
|
9834
9934
|
return { checks, ok: !checks.some((c) => c.level === "fail") };
|
|
9835
9935
|
}
|
|
9836
|
-
var NO_JS_FRONTMATTER
|
|
9936
|
+
var NO_JS_FRONTMATTER;
|
|
9837
9937
|
var init_store2 = __esm({
|
|
9838
9938
|
"packages/core/dist/knowledge/store.js"() {
|
|
9839
9939
|
init_dist();
|
|
@@ -9845,12 +9945,11 @@ var init_store2 = __esm({
|
|
|
9845
9945
|
throw new Error("only YAML front matter is allowed");
|
|
9846
9946
|
} } }
|
|
9847
9947
|
};
|
|
9848
|
-
expandHome2 = (p) => p === "~" || p.startsWith("~/") ? path17.join(os8.homedir(), p.slice(1)) : p;
|
|
9849
9948
|
}
|
|
9850
9949
|
});
|
|
9851
9950
|
|
|
9852
9951
|
// packages/core/dist/knowledge/layers.js
|
|
9853
|
-
import
|
|
9952
|
+
import fs18 from "fs";
|
|
9854
9953
|
function loadKbRules(cfg, kb, opts = {}) {
|
|
9855
9954
|
return loadRulesDir(kbRulesDir(cfg, kb), `kb:${kb}`, opts);
|
|
9856
9955
|
}
|
|
@@ -9862,7 +9961,7 @@ function resolveProjectRules(cfg, project, opts = {}) {
|
|
|
9862
9961
|
const errors = [];
|
|
9863
9962
|
const warnings = [];
|
|
9864
9963
|
for (const kb of subscribedKbs(cfg, project)) {
|
|
9865
|
-
if (!
|
|
9964
|
+
if (!fs18.existsSync(kbDir(cfg, kb))) {
|
|
9866
9965
|
warnings.push(`knowledge base '${kb}' not found (run: squadrant knowledge init ${kb})`);
|
|
9867
9966
|
continue;
|
|
9868
9967
|
}
|
|
@@ -9900,8 +9999,8 @@ var init_layers = __esm({
|
|
|
9900
9999
|
});
|
|
9901
10000
|
|
|
9902
10001
|
// packages/core/dist/knowledge/index-file.js
|
|
9903
|
-
import
|
|
9904
|
-
import
|
|
10002
|
+
import fs19 from "fs";
|
|
10003
|
+
import path19 from "path";
|
|
9905
10004
|
function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
9906
10005
|
return {
|
|
9907
10006
|
kb,
|
|
@@ -9910,18 +10009,18 @@ function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
|
9910
10009
|
};
|
|
9911
10010
|
}
|
|
9912
10011
|
function writeIndex(kbRoot, index) {
|
|
9913
|
-
const file =
|
|
10012
|
+
const file = path19.join(kbRoot, "index.json");
|
|
9914
10013
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
9915
|
-
|
|
9916
|
-
|
|
10014
|
+
fs19.writeFileSync(tmp, JSON.stringify(index, null, 2) + "\n");
|
|
10015
|
+
fs19.renameSync(tmp, file);
|
|
9917
10016
|
return file;
|
|
9918
10017
|
}
|
|
9919
10018
|
function readIndex(kbRoot, kb) {
|
|
9920
|
-
const file =
|
|
9921
|
-
if (!
|
|
10019
|
+
const file = path19.join(kbRoot, "index.json");
|
|
10020
|
+
if (!fs19.existsSync(file))
|
|
9922
10021
|
return { index: null, problem: `index.json missing for kb '${kb}'` };
|
|
9923
10022
|
try {
|
|
9924
|
-
const index = JSON.parse(
|
|
10023
|
+
const index = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
9925
10024
|
if (!Array.isArray(index.rules))
|
|
9926
10025
|
return { index: null, problem: `index.json corrupt for kb '${kb}' (no rules array)` };
|
|
9927
10026
|
return { index };
|
|
@@ -13016,16 +13115,16 @@ var init_js_yaml = __esm({
|
|
|
13016
13115
|
});
|
|
13017
13116
|
|
|
13018
13117
|
// packages/core/dist/knowledge/sources.js
|
|
13019
|
-
import
|
|
13020
|
-
import
|
|
13118
|
+
import fs20 from "fs";
|
|
13119
|
+
import path20 from "path";
|
|
13021
13120
|
import matter3 from "gray-matter";
|
|
13022
13121
|
function loadSources(kbRoot) {
|
|
13023
|
-
const file =
|
|
13024
|
-
if (!
|
|
13122
|
+
const file = path20.join(kbRoot, "sources.yaml");
|
|
13123
|
+
if (!fs20.existsSync(file))
|
|
13025
13124
|
return { sources: [], errors: [] };
|
|
13026
13125
|
let data;
|
|
13027
13126
|
try {
|
|
13028
|
-
data = yaml.load(
|
|
13127
|
+
data = yaml.load(fs20.readFileSync(file, "utf8"));
|
|
13029
13128
|
} catch (e) {
|
|
13030
13129
|
return { sources: [], errors: [`YAML parse error: ${e.message.split("\n")[0]}`] };
|
|
13031
13130
|
}
|
|
@@ -13057,22 +13156,22 @@ function dropEntryBlock(text, entryPath) {
|
|
|
13057
13156
|
return [...lines.slice(0, i), ...lines.slice(j)].join("\n");
|
|
13058
13157
|
}
|
|
13059
13158
|
function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
|
|
13060
|
-
const file =
|
|
13159
|
+
const file = path20.join(kbRoot, "sources.yaml");
|
|
13061
13160
|
const { sources } = loadSources(kbRoot);
|
|
13062
13161
|
const entry = sources.find((s) => s.path === entryPath);
|
|
13063
13162
|
if (!entry)
|
|
13064
13163
|
throw new Error(`no sources.yaml entry with path '${entryPath}' (give it exactly as listed by \`knowledge sources\`)`);
|
|
13065
|
-
const edited = dropEntryBlock(
|
|
13164
|
+
const edited = dropEntryBlock(fs20.readFileSync(file, "utf8"), entryPath);
|
|
13066
13165
|
if (edited === null)
|
|
13067
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`);
|
|
13068
13167
|
const res = { dryRun: !!opts.dryRun, entryRemoved: true, stripped: [], stale: [] };
|
|
13069
13168
|
const writes = [];
|
|
13070
13169
|
for (const rule of loadRulesDir(rulesDir, "kb").rules) {
|
|
13071
|
-
const kept = rule.sources.filter((s) => !sourceCovers(kbRoot, entry, s.ref));
|
|
13170
|
+
const kept = rule.sources.filter((s) => !sourceCovers(kbRoot, sources, entry, s.ref));
|
|
13072
13171
|
if (kept.length === rule.sources.length)
|
|
13073
13172
|
continue;
|
|
13074
13173
|
res.stripped.push(rule.id);
|
|
13075
|
-
const parsed = matter3(
|
|
13174
|
+
const parsed = matter3(fs20.readFileSync(rule.file, "utf8"), { engines: { javascript: () => {
|
|
13076
13175
|
throw new Error("only YAML front matter is allowed");
|
|
13077
13176
|
} } });
|
|
13078
13177
|
const fm = { ...parsed.data, sources: kept };
|
|
@@ -13088,9 +13187,9 @@ function removeSource(kbRoot, rulesDir, entryPath, opts = {}) {
|
|
|
13088
13187
|
if (opts.dryRun)
|
|
13089
13188
|
return res;
|
|
13090
13189
|
for (const w of writes)
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
const name = opts.kbName ??
|
|
13190
|
+
fs20.writeFileSync(w.file, w.text);
|
|
13191
|
+
fs20.writeFileSync(file, edited);
|
|
13192
|
+
const name = opts.kbName ?? path20.basename(kbRoot);
|
|
13094
13193
|
writeIndex(kbRoot, compileIndex(name, loadRulesDir(rulesDir, `kb:${name}`).rules));
|
|
13095
13194
|
return res;
|
|
13096
13195
|
}
|
|
@@ -13121,25 +13220,25 @@ var init_sources = __esm({
|
|
|
13121
13220
|
});
|
|
13122
13221
|
|
|
13123
13222
|
// packages/core/dist/knowledge/match.js
|
|
13124
|
-
import
|
|
13223
|
+
import path21 from "path";
|
|
13125
13224
|
function globMatches(glob, relPath) {
|
|
13126
13225
|
const g = glob.replace(/^\.\//, "");
|
|
13127
13226
|
if (globToRegExp(g).test(relPath))
|
|
13128
13227
|
return true;
|
|
13129
|
-
return !g.includes("/") && globToRegExp(g).test(
|
|
13228
|
+
return !g.includes("/") && globToRegExp(g).test(path21.posix.basename(relPath));
|
|
13130
13229
|
}
|
|
13131
13230
|
function repoRelativePaths(file, cwd, projectRoot) {
|
|
13132
13231
|
const out = /* @__PURE__ */ new Set();
|
|
13133
13232
|
const add = (base) => {
|
|
13134
|
-
const rel =
|
|
13135
|
-
if (!rel || rel.startsWith("..") ||
|
|
13233
|
+
const rel = path21.relative(base, file).split(path21.sep).join("/");
|
|
13234
|
+
if (!rel || rel.startsWith("..") || path21.isAbsolute(rel))
|
|
13136
13235
|
return;
|
|
13137
13236
|
out.add(rel);
|
|
13138
13237
|
const wt = /^\.worktrees\/[^/]+\/(.+)$/.exec(rel);
|
|
13139
13238
|
if (wt)
|
|
13140
13239
|
out.add(wt[1]);
|
|
13141
13240
|
};
|
|
13142
|
-
if (
|
|
13241
|
+
if (path21.isAbsolute(file)) {
|
|
13143
13242
|
add(cwd);
|
|
13144
13243
|
add(projectRoot);
|
|
13145
13244
|
} else
|
|
@@ -13188,25 +13287,27 @@ var init_match = __esm({
|
|
|
13188
13287
|
});
|
|
13189
13288
|
|
|
13190
13289
|
// packages/core/dist/knowledge/audit.js
|
|
13191
|
-
import
|
|
13290
|
+
import fs21 from "fs";
|
|
13192
13291
|
import os9 from "os";
|
|
13193
|
-
import
|
|
13292
|
+
import path22 from "path";
|
|
13194
13293
|
function appendAudit(events, sink = {}) {
|
|
13195
13294
|
if (!events.length)
|
|
13196
13295
|
return;
|
|
13197
13296
|
try {
|
|
13198
13297
|
const ts = (sink.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
13298
|
+
const task = (sink.env ?? process.env).SQUADRANT_CREW_TASK_ID;
|
|
13199
13299
|
const dir = sink.dir ?? defaultAuditDir();
|
|
13200
|
-
const file =
|
|
13201
|
-
|
|
13202
|
-
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");
|
|
13203
13303
|
try {
|
|
13204
13304
|
for (const e of events) {
|
|
13205
13305
|
const q = e.query === void 0 ? {} : { query: e.query.slice(0, AUDIT_QUERY_CHARS) };
|
|
13206
|
-
|
|
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");
|
|
13207
13308
|
}
|
|
13208
13309
|
} finally {
|
|
13209
|
-
|
|
13310
|
+
fs21.closeSync(fd);
|
|
13210
13311
|
}
|
|
13211
13312
|
} catch (err) {
|
|
13212
13313
|
console.error(`squadrant audit: log write failed: ${err.message}`);
|
|
@@ -13215,18 +13316,22 @@ function appendAudit(events, sink = {}) {
|
|
|
13215
13316
|
function ruleAuditScope(rule) {
|
|
13216
13317
|
return rule.layer.startsWith("kb:") ? { kb: rule.layer.slice(3), level: "group" } : { kb: "", level: "project" };
|
|
13217
13318
|
}
|
|
13218
|
-
var AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
|
|
13319
|
+
var OUTCOME_SOURCES, outcomeSource, RATED_SOURCES, DELIVERY_EVENTS, AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
|
|
13219
13320
|
var init_audit = __esm({
|
|
13220
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"]);
|
|
13221
13326
|
AUDIT_QUERY_CHARS = 200;
|
|
13222
|
-
defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR ||
|
|
13327
|
+
defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR || path22.join(os9.homedir(), ".local", "state", "squadrant", "audit");
|
|
13223
13328
|
auditMachineId = () => os9.hostname().replace(/[^A-Za-z0-9_-]/g, "_") || "host";
|
|
13224
13329
|
}
|
|
13225
13330
|
});
|
|
13226
13331
|
|
|
13227
13332
|
// packages/core/dist/knowledge/inject.js
|
|
13228
|
-
import
|
|
13229
|
-
import
|
|
13333
|
+
import fs22 from "fs";
|
|
13334
|
+
import path23 from "path";
|
|
13230
13335
|
function sessionOrder(a, b) {
|
|
13231
13336
|
const proj = (r) => r.layer.startsWith("project:") ? 0 : 1;
|
|
13232
13337
|
const mod = (r) => r.modality === "must-not" ? 0 : 1;
|
|
@@ -13300,7 +13405,7 @@ function promptText(prompt) {
|
|
|
13300
13405
|
if (!m)
|
|
13301
13406
|
return prompt;
|
|
13302
13407
|
try {
|
|
13303
|
-
return
|
|
13408
|
+
return fs22.readFileSync(m[1], "utf8").slice(0, MAX_PROMPT_CHARS * 2);
|
|
13304
13409
|
} catch {
|
|
13305
13410
|
return prompt;
|
|
13306
13411
|
}
|
|
@@ -13344,39 +13449,39 @@ function rulesFallbackPointer(kbs) {
|
|
|
13344
13449
|
"Before changing code, look up the rules that apply with `squadrant rules search <terms>`, then `squadrant rules show <id>`."
|
|
13345
13450
|
].join("\n");
|
|
13346
13451
|
}
|
|
13347
|
-
function injectStateDir(
|
|
13348
|
-
return
|
|
13452
|
+
function injectStateDir(stateRoot2) {
|
|
13453
|
+
return path23.join(stateRoot2, "rules-inject");
|
|
13349
13454
|
}
|
|
13350
|
-
function readSeenRules(
|
|
13455
|
+
function readSeenRules(stateRoot2, sessionId) {
|
|
13351
13456
|
try {
|
|
13352
|
-
const ids = JSON.parse(
|
|
13457
|
+
const ids = JSON.parse(fs22.readFileSync(sessionFile(stateRoot2, sessionId), "utf8"));
|
|
13353
13458
|
return new Set(Array.isArray(ids) ? ids.filter((x) => typeof x === "string") : []);
|
|
13354
13459
|
} catch {
|
|
13355
13460
|
return /* @__PURE__ */ new Set();
|
|
13356
13461
|
}
|
|
13357
13462
|
}
|
|
13358
|
-
function recordSeenRules(
|
|
13463
|
+
function recordSeenRules(stateRoot2, sessionId, ids) {
|
|
13359
13464
|
if (!ids.length)
|
|
13360
13465
|
return;
|
|
13361
|
-
const seen = readSeenRules(
|
|
13466
|
+
const seen = readSeenRules(stateRoot2, sessionId);
|
|
13362
13467
|
for (const id of ids)
|
|
13363
13468
|
seen.add(id);
|
|
13364
|
-
|
|
13365
|
-
|
|
13469
|
+
fs22.mkdirSync(injectStateDir(stateRoot2), { recursive: true });
|
|
13470
|
+
fs22.writeFileSync(sessionFile(stateRoot2, sessionId), JSON.stringify([...seen]));
|
|
13366
13471
|
}
|
|
13367
|
-
function pruneSeenRules(
|
|
13368
|
-
const dir = injectStateDir(
|
|
13472
|
+
function pruneSeenRules(stateRoot2, now = Date.now()) {
|
|
13473
|
+
const dir = injectStateDir(stateRoot2);
|
|
13369
13474
|
let names;
|
|
13370
13475
|
try {
|
|
13371
|
-
names =
|
|
13476
|
+
names = fs22.readdirSync(dir);
|
|
13372
13477
|
} catch {
|
|
13373
13478
|
return;
|
|
13374
13479
|
}
|
|
13375
13480
|
for (const n of names) {
|
|
13376
|
-
const f =
|
|
13481
|
+
const f = path23.join(dir, n);
|
|
13377
13482
|
try {
|
|
13378
|
-
if (now -
|
|
13379
|
-
|
|
13483
|
+
if (now - fs22.statSync(f).mtimeMs > SEEN_TTL_MS)
|
|
13484
|
+
fs22.unlinkSync(f);
|
|
13380
13485
|
} catch {
|
|
13381
13486
|
}
|
|
13382
13487
|
}
|
|
@@ -13385,8 +13490,8 @@ async function withBudget(fn, budgetMs = INJECT_BUDGET_MS) {
|
|
|
13385
13490
|
const start = Date.now();
|
|
13386
13491
|
let timer;
|
|
13387
13492
|
try {
|
|
13388
|
-
const timeout = new Promise((
|
|
13389
|
-
timer = setTimeout(() =>
|
|
13493
|
+
const timeout = new Promise((resolve5) => {
|
|
13494
|
+
timer = setTimeout(() => resolve5(null), budgetMs);
|
|
13390
13495
|
});
|
|
13391
13496
|
const out = await Promise.race([Promise.resolve().then(fn), timeout]);
|
|
13392
13497
|
return Date.now() - start > budgetMs ? null : out;
|
|
@@ -13404,7 +13509,7 @@ function resolveInjectProject(cfg, cwd, env, project) {
|
|
|
13404
13509
|
let bestLen = -1;
|
|
13405
13510
|
for (const [name, pc] of Object.entries(cfg.projects)) {
|
|
13406
13511
|
const root = resolveHome(pc.path);
|
|
13407
|
-
if ((cwd === root || cwd.startsWith(root +
|
|
13512
|
+
if ((cwd === root || cwd.startsWith(root + path23.sep)) && root.length > bestLen) {
|
|
13408
13513
|
best = name;
|
|
13409
13514
|
bestLen = root.length;
|
|
13410
13515
|
}
|
|
@@ -13423,7 +13528,7 @@ function audit(i, project, session, trigger, items) {
|
|
|
13423
13528
|
chars: line(rule).length,
|
|
13424
13529
|
agent: i.agent ?? "claude",
|
|
13425
13530
|
...session ? { session } : {}
|
|
13426
|
-
})), i.audit);
|
|
13531
|
+
})), { env: i.env, ...i.audit });
|
|
13427
13532
|
}
|
|
13428
13533
|
function computeRulesInjection(i) {
|
|
13429
13534
|
if (i.env.SQUADRANT_RULES_INJECT === "0")
|
|
@@ -13512,23 +13617,23 @@ var init_inject = __esm({
|
|
|
13512
13617
|
READ_TOOLS = /* @__PURE__ */ new Set(["read", "read_file", "readfile", "view"]);
|
|
13513
13618
|
SHELL_TOOLS = /* @__PURE__ */ new Set(["bash", "shell", "run_shell_command", "local_shell", "exec_command"]);
|
|
13514
13619
|
SHELL_META = /[|;&<>$`()]/;
|
|
13515
|
-
sessionFile = (
|
|
13620
|
+
sessionFile = (stateRoot2, sessionId) => path23.join(injectStateDir(stateRoot2), `${sessionId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
13516
13621
|
MAX_SUPPRESSED_LOGGED = 10;
|
|
13517
13622
|
}
|
|
13518
13623
|
});
|
|
13519
13624
|
|
|
13520
13625
|
// packages/core/dist/knowledge/migrate.js
|
|
13521
|
-
import
|
|
13522
|
-
import
|
|
13626
|
+
import fs23 from "fs";
|
|
13627
|
+
import path24 from "path";
|
|
13523
13628
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
13524
13629
|
function findLegacyKbs(cfg) {
|
|
13525
13630
|
const root = legacyRoot(cfg);
|
|
13526
|
-
if (!
|
|
13631
|
+
if (!fs23.existsSync(root))
|
|
13527
13632
|
return [];
|
|
13528
|
-
return
|
|
13633
|
+
return fs23.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && KB_NAME_RE.test(e.name)).map((e) => e.name).sort();
|
|
13529
13634
|
}
|
|
13530
13635
|
function ensureGitRepo(dir) {
|
|
13531
|
-
if (
|
|
13636
|
+
if (fs23.existsSync(path24.join(dir, ".git")))
|
|
13532
13637
|
return false;
|
|
13533
13638
|
try {
|
|
13534
13639
|
execFileSync6("git", ["init", "-q", dir], { stdio: "ignore" });
|
|
@@ -13538,14 +13643,14 @@ function ensureGitRepo(dir) {
|
|
|
13538
13643
|
}
|
|
13539
13644
|
}
|
|
13540
13645
|
function ensureKbGitignore(dir) {
|
|
13541
|
-
const file =
|
|
13542
|
-
if (
|
|
13646
|
+
const file = path24.join(dir, ".gitignore");
|
|
13647
|
+
if (fs23.existsSync(file))
|
|
13543
13648
|
return false;
|
|
13544
|
-
|
|
13649
|
+
fs23.writeFileSync(file, KB_GITIGNORE);
|
|
13545
13650
|
return true;
|
|
13546
13651
|
}
|
|
13547
13652
|
function findLegacyOverlays(cfg) {
|
|
13548
|
-
return Object.keys(cfg.projects).filter((p) =>
|
|
13653
|
+
return Object.keys(cfg.projects).filter((p) => fs23.existsSync(legacyOverlay(cfg, p))).sort();
|
|
13549
13654
|
}
|
|
13550
13655
|
function moveLegacyOverlays(cfg, opts = {}) {
|
|
13551
13656
|
return findLegacyOverlays(cfg).map((project) => {
|
|
@@ -13554,18 +13659,18 @@ function moveLegacyOverlays(cfg, opts = {}) {
|
|
|
13554
13659
|
const kb = `overlay:${project}`;
|
|
13555
13660
|
if (!to)
|
|
13556
13661
|
return { kb, from, to: "", action: "skipped", note: "no group KB or knowledgeHome for this project; set one, then re-run" };
|
|
13557
|
-
if (
|
|
13662
|
+
if (fs23.existsSync(to))
|
|
13558
13663
|
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13559
13664
|
if (opts.dryRun)
|
|
13560
13665
|
return { kb, from, to, action: "would-move" };
|
|
13561
|
-
|
|
13666
|
+
fs23.mkdirSync(path24.dirname(to), { recursive: true });
|
|
13562
13667
|
try {
|
|
13563
|
-
|
|
13668
|
+
fs23.renameSync(from, to);
|
|
13564
13669
|
return { kb, from, to, action: "moved" };
|
|
13565
13670
|
} catch (e) {
|
|
13566
13671
|
if (e.code !== "EXDEV")
|
|
13567
13672
|
throw e;
|
|
13568
|
-
|
|
13673
|
+
fs23.cpSync(from, to, { recursive: true });
|
|
13569
13674
|
return { kb, from, to, action: "copied", note: "cross-device: original left in place, delete it yourself once verified" };
|
|
13570
13675
|
}
|
|
13571
13676
|
});
|
|
@@ -13575,32 +13680,32 @@ function moveLegacyKbs(cfg, opts = {}) {
|
|
|
13575
13680
|
return findLegacyKbs(cfg).map((kb) => {
|
|
13576
13681
|
const from = legacyKbDir(hub, kb);
|
|
13577
13682
|
const to = kbDir(cfg, kb);
|
|
13578
|
-
if (
|
|
13683
|
+
if (fs23.existsSync(to))
|
|
13579
13684
|
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13580
13685
|
if (opts.dryRun)
|
|
13581
13686
|
return { kb, from, to, action: "would-move" };
|
|
13582
|
-
|
|
13687
|
+
fs23.mkdirSync(path24.dirname(to), { recursive: true });
|
|
13583
13688
|
let action = "moved";
|
|
13584
13689
|
try {
|
|
13585
|
-
|
|
13690
|
+
fs23.renameSync(from, to);
|
|
13586
13691
|
} catch (e) {
|
|
13587
13692
|
if (e.code !== "EXDEV")
|
|
13588
13693
|
throw e;
|
|
13589
|
-
|
|
13694
|
+
fs23.cpSync(from, to, { recursive: true });
|
|
13590
13695
|
action = "copied";
|
|
13591
13696
|
}
|
|
13592
13697
|
const notes = [];
|
|
13593
|
-
const oldRules =
|
|
13594
|
-
if (
|
|
13595
|
-
|
|
13596
|
-
|
|
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);
|
|
13597
13702
|
notes.push("rules/ -> shared/rules/");
|
|
13598
13703
|
}
|
|
13599
13704
|
if (ensureGitRepo(to))
|
|
13600
13705
|
notes.push("git init");
|
|
13601
13706
|
if (ensureKbGitignore(to))
|
|
13602
13707
|
notes.push("wrote .gitignore (raw/, .converted/)");
|
|
13603
|
-
if (
|
|
13708
|
+
if (fs23.existsSync(path24.join(to, "index.json")))
|
|
13604
13709
|
notes.push(`index.json holds old paths: run squadrant knowledge reindex ${kb}`);
|
|
13605
13710
|
if (action === "copied")
|
|
13606
13711
|
notes.push("cross-device: original left in place, delete it yourself once verified");
|
|
@@ -13612,22 +13717,22 @@ var init_migrate = __esm({
|
|
|
13612
13717
|
"packages/core/dist/knowledge/migrate.js"() {
|
|
13613
13718
|
init_dist();
|
|
13614
13719
|
init_paths();
|
|
13615
|
-
legacyRoot = (cfg) =>
|
|
13720
|
+
legacyRoot = (cfg) => path24.join(resolveHome(cfg.hubVault), "knowledge");
|
|
13616
13721
|
KB_GITIGNORE = "# squadrant KB defaults: source documents stay local\nraw/\n.converted/\n";
|
|
13617
|
-
legacyOverlay = (cfg, project) =>
|
|
13722
|
+
legacyOverlay = (cfg, project) => path24.join(resolveHome(cfg.projects[project].spokeVault), "knowledge", "rules");
|
|
13618
13723
|
}
|
|
13619
13724
|
});
|
|
13620
13725
|
|
|
13621
13726
|
// packages/core/dist/knowledge/errata.js
|
|
13622
|
-
import
|
|
13623
|
-
import
|
|
13727
|
+
import fs24 from "fs";
|
|
13728
|
+
import path25 from "path";
|
|
13624
13729
|
function loadErrata(kbRoot) {
|
|
13625
|
-
const file =
|
|
13626
|
-
if (!
|
|
13730
|
+
const file = path25.join(kbRoot, "errata.yaml");
|
|
13731
|
+
if (!fs24.existsSync(file))
|
|
13627
13732
|
return { errata: [], errors: [] };
|
|
13628
13733
|
let data;
|
|
13629
13734
|
try {
|
|
13630
|
-
data = yaml.load(
|
|
13735
|
+
data = yaml.load(fs24.readFileSync(file, "utf8"), { schema: yaml.JSON_SCHEMA });
|
|
13631
13736
|
} catch (e) {
|
|
13632
13737
|
return { errata: [], errors: [`YAML parse error: ${e.message.split("\n")[0]}`] };
|
|
13633
13738
|
}
|
|
@@ -13646,18 +13751,20 @@ function loadErrata(kbRoot) {
|
|
|
13646
13751
|
});
|
|
13647
13752
|
return { errata, errors };
|
|
13648
13753
|
}
|
|
13649
|
-
function orphanedErrata(kbRoot, errata, refs) {
|
|
13650
|
-
const
|
|
13754
|
+
function orphanedErrata(kbRoot, sources, errata, refs) {
|
|
13755
|
+
const canon = refCanon(kbRoot, sources);
|
|
13756
|
+
const only = refs ? new Set([...refs].map(canon)) : void 0;
|
|
13651
13757
|
const hays = /* @__PURE__ */ new Map();
|
|
13652
13758
|
const out = [];
|
|
13653
13759
|
for (const e of errata) {
|
|
13654
|
-
|
|
13760
|
+
const ref = canon(e.ref);
|
|
13761
|
+
if (only && !only.has(ref))
|
|
13655
13762
|
continue;
|
|
13656
|
-
if (!hays.has(
|
|
13657
|
-
const conv = readState2(kbRoot,
|
|
13658
|
-
hays.set(
|
|
13763
|
+
if (!hays.has(ref)) {
|
|
13764
|
+
const conv = readState2(kbRoot, ref) ? convertedFile(kbRoot, ref) : null;
|
|
13765
|
+
hays.set(ref, conv && fs24.existsSync(conv) ? normalizedViews(fs24.readFileSync(conv, "utf8")) : null);
|
|
13659
13766
|
}
|
|
13660
|
-
const hay = hays.get(
|
|
13767
|
+
const hay = hays.get(ref);
|
|
13661
13768
|
if (hay && !findQuote(hay, e.quote))
|
|
13662
13769
|
out.push({ id: e.id, ref: e.ref });
|
|
13663
13770
|
}
|
|
@@ -13673,66 +13780,100 @@ var init_errata = __esm({
|
|
|
13673
13780
|
});
|
|
13674
13781
|
|
|
13675
13782
|
// packages/core/dist/knowledge/anchor.js
|
|
13676
|
-
import
|
|
13677
|
-
import
|
|
13678
|
-
|
|
13783
|
+
import fs25 from "fs";
|
|
13784
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
13785
|
+
import path26 from "path";
|
|
13786
|
+
function gitFiles(root) {
|
|
13787
|
+
try {
|
|
13788
|
+
const out = execFileSync7("git", ["ls-files", "-co", "--exclude-standard", "-z"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024 });
|
|
13789
|
+
return out.split("\0").filter(Boolean).sort();
|
|
13790
|
+
} catch {
|
|
13791
|
+
return null;
|
|
13792
|
+
}
|
|
13793
|
+
}
|
|
13794
|
+
function* repoFiles(root, maxFiles) {
|
|
13679
13795
|
let count = 0;
|
|
13796
|
+
const listed = gitFiles(root);
|
|
13797
|
+
if (listed) {
|
|
13798
|
+
for (const rel of listed) {
|
|
13799
|
+
if (!CODE_EXT.has(path26.extname(rel)) || rel.split("/").slice(0, -1).some((d) => SKIP_DIRS.has(d)))
|
|
13800
|
+
continue;
|
|
13801
|
+
if (++count > maxFiles) {
|
|
13802
|
+
console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
|
|
13803
|
+
return;
|
|
13804
|
+
}
|
|
13805
|
+
yield path26.join(root, rel);
|
|
13806
|
+
}
|
|
13807
|
+
return;
|
|
13808
|
+
}
|
|
13680
13809
|
const stack = [root];
|
|
13681
13810
|
while (stack.length) {
|
|
13682
13811
|
const dir = stack.pop();
|
|
13683
13812
|
let ents;
|
|
13684
13813
|
try {
|
|
13685
|
-
ents =
|
|
13814
|
+
ents = fs25.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? 1 : -1);
|
|
13686
13815
|
} catch {
|
|
13687
13816
|
continue;
|
|
13688
13817
|
}
|
|
13689
13818
|
for (const ent of ents) {
|
|
13690
13819
|
if (ent.isDirectory()) {
|
|
13691
13820
|
if (!SKIP_DIRS.has(ent.name))
|
|
13692
|
-
stack.push(
|
|
13693
|
-
} else if (ent.isFile() && CODE_EXT.has(
|
|
13694
|
-
if (++count >
|
|
13821
|
+
stack.push(path26.join(dir, ent.name));
|
|
13822
|
+
} else if (ent.isFile() && CODE_EXT.has(path26.extname(ent.name))) {
|
|
13823
|
+
if (++count > maxFiles) {
|
|
13824
|
+
console.warn(`anchor: ${root} has more than ${maxFiles} code files; anchors cover only the first ${maxFiles}`);
|
|
13695
13825
|
return;
|
|
13696
|
-
|
|
13826
|
+
}
|
|
13827
|
+
yield path26.join(dir, ent.name);
|
|
13697
13828
|
}
|
|
13698
13829
|
}
|
|
13699
13830
|
}
|
|
13700
13831
|
}
|
|
13701
|
-
function
|
|
13702
|
-
const
|
|
13703
|
-
|
|
13704
|
-
return null;
|
|
13705
|
-
const hits = [];
|
|
13706
|
-
for (const file of repoFiles(repoRoot)) {
|
|
13707
|
-
let text;
|
|
13832
|
+
function scanRepo(root, maxFiles) {
|
|
13833
|
+
const out = [];
|
|
13834
|
+
for (const file of repoFiles(root, maxFiles)) {
|
|
13708
13835
|
try {
|
|
13709
|
-
if (
|
|
13836
|
+
if (fs25.statSync(file).size > MAX_FILE_BYTES)
|
|
13710
13837
|
continue;
|
|
13711
|
-
text =
|
|
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)}
|
|
13841
|
+
${text}`.toLowerCase() });
|
|
13712
13842
|
} catch {
|
|
13713
13843
|
continue;
|
|
13714
13844
|
}
|
|
13715
|
-
|
|
13716
|
-
|
|
13845
|
+
}
|
|
13846
|
+
return out;
|
|
13847
|
+
}
|
|
13848
|
+
function computeAnchor(repoRoot, triggers, cache, maxFiles = MAX_FILES) {
|
|
13849
|
+
const terms2 = anchorTerms(triggers);
|
|
13850
|
+
if (!terms2.length || !fs25.existsSync(repoRoot))
|
|
13851
|
+
return null;
|
|
13852
|
+
let files = cache?.get(repoRoot);
|
|
13853
|
+
if (!files) {
|
|
13854
|
+
files = scanRepo(repoRoot, maxFiles);
|
|
13855
|
+
cache?.set(repoRoot, files);
|
|
13856
|
+
}
|
|
13857
|
+
const hits = [];
|
|
13858
|
+
for (const { rel, text, hay } of files) {
|
|
13717
13859
|
const score = terms2.filter((t) => hay.includes(t)).length;
|
|
13718
13860
|
if (!score)
|
|
13719
13861
|
continue;
|
|
13720
|
-
|
|
13721
|
-
hits.push({ rel: path25.relative(repoRoot, file).split(path25.sep).join("/"), score, symbols: symbols2 });
|
|
13862
|
+
hits.push({ rel, score, text });
|
|
13722
13863
|
}
|
|
13723
13864
|
if (!hits.length)
|
|
13724
13865
|
return null;
|
|
13725
13866
|
hits.sort((a, b) => b.score - a.score || (a.rel < b.rel ? -1 : 1));
|
|
13726
13867
|
const top = hits.slice(0, MAX_PATHS);
|
|
13727
|
-
const symbols = [...new Set(top.flatMap((h) => h.
|
|
13868
|
+
const symbols = [...new Set(top.flatMap((h) => [...h.text.matchAll(SYMBOL_RE)].map((m) => m[1]).filter((s) => terms2.some((t) => s.toLowerCase().includes(t)))))].sort().slice(0, MAX_SYMBOLS);
|
|
13728
13869
|
return { paths: top.map((h) => h.rel).sort(), ...symbols.length ? { symbols } : {} };
|
|
13729
13870
|
}
|
|
13730
13871
|
var SKIP_DIRS, CODE_EXT, MAX_FILES, MAX_FILE_BYTES, MAX_PATHS, MAX_SYMBOLS, SYMBOL_RE, anchorTerms;
|
|
13731
13872
|
var init_anchor = __esm({
|
|
13732
13873
|
"packages/core/dist/knowledge/anchor.js"() {
|
|
13733
|
-
SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", ".worktrees", ".planning", ".converted"]);
|
|
13874
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", ".worktrees", ".planning", ".converted", ".venv", "venv", "__pycache__"]);
|
|
13734
13875
|
CODE_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".kt", ".swift", ".rb", ".php", ".cs", ".sol", ".sql", ".md"]);
|
|
13735
|
-
MAX_FILES =
|
|
13876
|
+
MAX_FILES = 2e4;
|
|
13736
13877
|
MAX_FILE_BYTES = 256 * 1024;
|
|
13737
13878
|
MAX_PATHS = 5;
|
|
13738
13879
|
MAX_SYMBOLS = 10;
|
|
@@ -13742,8 +13883,8 @@ var init_anchor = __esm({
|
|
|
13742
13883
|
});
|
|
13743
13884
|
|
|
13744
13885
|
// packages/core/dist/knowledge/extract.js
|
|
13745
|
-
import
|
|
13746
|
-
import
|
|
13886
|
+
import fs26 from "fs";
|
|
13887
|
+
import path27 from "path";
|
|
13747
13888
|
import matter4 from "gray-matter";
|
|
13748
13889
|
function serialize(fm, body) {
|
|
13749
13890
|
return matter4.stringify(body.trim() + "\n", clean(fm));
|
|
@@ -13776,8 +13917,8 @@ function validateCandidate(c) {
|
|
|
13776
13917
|
}
|
|
13777
13918
|
return p;
|
|
13778
13919
|
}
|
|
13779
|
-
function upsertSource(fm, src) {
|
|
13780
|
-
const i = fm.sources.findIndex((s) => s.ref === src.ref);
|
|
13920
|
+
function upsertSource(fm, src, canon) {
|
|
13921
|
+
const i = fm.sources.findIndex((s) => canon(s.ref) === canon(src.ref));
|
|
13781
13922
|
if (i < 0) {
|
|
13782
13923
|
fm.sources.push(src);
|
|
13783
13924
|
return true;
|
|
@@ -13801,7 +13942,8 @@ function applyCandidates(input, opts) {
|
|
|
13801
13942
|
const norm = /* @__PURE__ */ new Map();
|
|
13802
13943
|
const totals = /* @__PURE__ */ new Map();
|
|
13803
13944
|
const touched = /* @__PURE__ */ new Set();
|
|
13804
|
-
const
|
|
13945
|
+
const canon = refCanon(kbRoot, opts.sources ?? []);
|
|
13946
|
+
const complete = new Set((input.complete ?? []).map(canon));
|
|
13805
13947
|
for (const cand of input.candidates) {
|
|
13806
13948
|
const problems = validateCandidate(cand);
|
|
13807
13949
|
if (problems.length) {
|
|
@@ -13810,13 +13952,13 @@ function applyCandidates(input, opts) {
|
|
|
13810
13952
|
}
|
|
13811
13953
|
const st = readState2(kbRoot, cand.source);
|
|
13812
13954
|
const convFile = st ? convertedFile(kbRoot, cand.source) : null;
|
|
13813
|
-
if (!st || !
|
|
13955
|
+
if (!st || !fs26.existsSync(convFile)) {
|
|
13814
13956
|
res.rejected.push({ id: cand.id, source: cand.source, reason: "unknown source (not ingested)" });
|
|
13815
13957
|
continue;
|
|
13816
13958
|
}
|
|
13817
13959
|
let hay = norm.get(cand.source);
|
|
13818
13960
|
if (!hay) {
|
|
13819
|
-
hay = normalizedViews(
|
|
13961
|
+
hay = normalizedViews(fs26.readFileSync(convFile, "utf8"));
|
|
13820
13962
|
norm.set(cand.source, hay);
|
|
13821
13963
|
}
|
|
13822
13964
|
const tot = totals.get(cand.source) ?? { total: 0, dropped: 0 };
|
|
@@ -13829,7 +13971,7 @@ function applyCandidates(input, opts) {
|
|
|
13829
13971
|
ev2("item.rejected", cand.id, { reason: "ungrounded" });
|
|
13830
13972
|
continue;
|
|
13831
13973
|
}
|
|
13832
|
-
const hit = (opts.errata ?? []).find((e) => e.ref === cand.source && ((o) => !!o && spansOverlap(o, offset))(findQuote(hay, e.quote)));
|
|
13974
|
+
const hit = (opts.errata ?? []).find((e) => canon(e.ref) === canon(cand.source) && ((o) => !!o && spansOverlap(o, offset))(findQuote(hay, e.quote)));
|
|
13833
13975
|
if (hit) {
|
|
13834
13976
|
tot.total--;
|
|
13835
13977
|
res.rejected.push({ id: cand.id, source: cand.source, reason: `errata:${hit.id}` });
|
|
@@ -13839,7 +13981,7 @@ function applyCandidates(input, opts) {
|
|
|
13839
13981
|
let id = cand.id;
|
|
13840
13982
|
for (const [wid, w] of work) {
|
|
13841
13983
|
const span = (s) => s.sha === st.sha ? s.offset : findQuote(hay, s.quote) ?? s.offset;
|
|
13842
|
-
if (w.fm.sources.some((s) => s.ref === cand.source && s.offset && ((o) => !!o && spansOverlap(o, offset))(span(s)))) {
|
|
13984
|
+
if (w.fm.sources.some((s) => canon(s.ref) === canon(cand.source) && s.offset && ((o) => !!o && spansOverlap(o, offset))(span(s)))) {
|
|
13843
13985
|
id = wid;
|
|
13844
13986
|
break;
|
|
13845
13987
|
}
|
|
@@ -13854,7 +13996,7 @@ function applyCandidates(input, opts) {
|
|
|
13854
13996
|
|
|
13855
13997
|
${cand.rationale.trim()}` : "");
|
|
13856
13998
|
if (target && contradicted.length === 0 && sameMeaning(target, cand)) {
|
|
13857
|
-
let changed = upsertSource(target.fm, src);
|
|
13999
|
+
let changed = upsertSource(target.fm, src, canon);
|
|
13858
14000
|
if (target.fm.status === "stale") {
|
|
13859
14001
|
target.fm.status = "active";
|
|
13860
14002
|
changed = true;
|
|
@@ -13878,7 +14020,7 @@ ${cand.rationale.trim()}` : "");
|
|
|
13878
14020
|
supersedes: [],
|
|
13879
14021
|
conflictsWith: []
|
|
13880
14022
|
};
|
|
13881
|
-
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 });
|
|
13882
14024
|
res.created.push(id);
|
|
13883
14025
|
ev2("item.applied", id);
|
|
13884
14026
|
continue;
|
|
@@ -13895,42 +14037,43 @@ ${cand.rationale.trim()}` : "");
|
|
|
13895
14037
|
supersedes: [],
|
|
13896
14038
|
conflictsWith: contradicted
|
|
13897
14039
|
};
|
|
13898
|
-
const dir =
|
|
13899
|
-
const file =
|
|
14040
|
+
const dir = path27.join(rulesDir, PROPOSED_DIR);
|
|
14041
|
+
const file = path27.join(dir, `${id}@${st.sha.slice(0, SHA_LEN)}.md`);
|
|
13900
14042
|
const text = serialize(fm, bodyOf());
|
|
13901
|
-
if (
|
|
14043
|
+
if (fs26.existsSync(file) && fs26.readFileSync(file, "utf8") === text)
|
|
13902
14044
|
continue;
|
|
13903
|
-
|
|
13904
|
-
for (const other of
|
|
13905
|
-
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)
|
|
13906
14048
|
continue;
|
|
13907
|
-
const old = matter4(
|
|
13908
|
-
if (!old.data.sources?.some((s) => s.ref === cand.source))
|
|
14049
|
+
const old = matter4(fs26.readFileSync(path27.join(dir, other), "utf8"));
|
|
14050
|
+
if (!old.data.sources?.some((s) => canon(s.ref) === canon(cand.source)))
|
|
13909
14051
|
continue;
|
|
13910
|
-
|
|
13911
|
-
|
|
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));
|
|
13912
14054
|
res.superseded.push(other.replace(/\.md$/, ""));
|
|
13913
14055
|
ev2("item.superseded", id);
|
|
13914
14056
|
}
|
|
13915
|
-
|
|
14057
|
+
fs26.writeFileSync(file, text);
|
|
13916
14058
|
res.proposed.push(`${id}@${st.sha.slice(0, SHA_LEN)}`);
|
|
13917
14059
|
ev2("item.proposed", id);
|
|
13918
14060
|
}
|
|
13919
14061
|
for (const [id, w] of work) {
|
|
13920
14062
|
if (touched.has(id) || w.fm.status !== "active" || w.isNew)
|
|
13921
14063
|
continue;
|
|
13922
|
-
if (w.fm.sources.length && w.fm.sources.every((s) => complete.has(s.ref))) {
|
|
14064
|
+
if (w.fm.sources.length && w.fm.sources.every((s) => complete.has(canon(s.ref)))) {
|
|
13923
14065
|
w.fm.status = "stale";
|
|
13924
14066
|
w.dirty = true;
|
|
13925
14067
|
res.stale.push(id);
|
|
13926
14068
|
}
|
|
13927
14069
|
}
|
|
14070
|
+
const anchorCache = /* @__PURE__ */ new Map();
|
|
13928
14071
|
for (const w of work.values()) {
|
|
13929
14072
|
if (!w.dirty || !opts.projects?.length)
|
|
13930
14073
|
continue;
|
|
13931
14074
|
const anchors = { ...w.fm.anchors ?? {} };
|
|
13932
14075
|
for (const p of opts.projects) {
|
|
13933
|
-
const a = computeAnchor(p.root, w.fm.triggers);
|
|
14076
|
+
const a = computeAnchor(p.root, w.fm.triggers, anchorCache);
|
|
13934
14077
|
if (a)
|
|
13935
14078
|
anchors[p.name] = a;
|
|
13936
14079
|
}
|
|
@@ -13940,12 +14083,12 @@ ${cand.rationale.trim()}` : "");
|
|
|
13940
14083
|
for (const w of work.values()) {
|
|
13941
14084
|
if (!w.dirty)
|
|
13942
14085
|
continue;
|
|
13943
|
-
|
|
14086
|
+
fs26.mkdirSync(path27.dirname(w.file), { recursive: true });
|
|
13944
14087
|
const text = serialize(w.fm, w.body);
|
|
13945
|
-
if (!
|
|
13946
|
-
|
|
14088
|
+
if (!fs26.existsSync(w.file) || fs26.readFileSync(w.file, "utf8") !== text)
|
|
14089
|
+
fs26.writeFileSync(w.file, text);
|
|
13947
14090
|
}
|
|
13948
|
-
res.orphanedErrata = orphanedErrata(kbRoot, opts.errata ?? [], totals.keys());
|
|
14091
|
+
res.orphanedErrata = orphanedErrata(kbRoot, opts.sources ?? [], opts.errata ?? [], totals.keys());
|
|
13949
14092
|
for (const [ref, t] of totals)
|
|
13950
14093
|
if (needsDocling(t.dropped, t.total))
|
|
13951
14094
|
res.doclingFlags.push({ ref, ...t });
|
|
@@ -13979,20 +14122,20 @@ var init_extract = __esm({
|
|
|
13979
14122
|
});
|
|
13980
14123
|
|
|
13981
14124
|
// packages/core/dist/knowledge/schedule.js
|
|
13982
|
-
import
|
|
13983
|
-
import
|
|
13984
|
-
function readSchedule(
|
|
14125
|
+
import fs27 from "fs";
|
|
14126
|
+
import path28 from "path";
|
|
14127
|
+
function readSchedule(stateRoot2) {
|
|
13985
14128
|
try {
|
|
13986
|
-
return JSON.parse(
|
|
14129
|
+
return JSON.parse(fs27.readFileSync(scheduleFile(stateRoot2), "utf8"));
|
|
13987
14130
|
} catch {
|
|
13988
14131
|
return {};
|
|
13989
14132
|
}
|
|
13990
14133
|
}
|
|
13991
|
-
function writeSchedule(
|
|
13992
|
-
|
|
13993
|
-
const tmp = `${scheduleFile(
|
|
13994
|
-
|
|
13995
|
-
|
|
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));
|
|
13996
14139
|
}
|
|
13997
14140
|
function snapshotSources(kbRoot, sources) {
|
|
13998
14141
|
const out = {};
|
|
@@ -14072,7 +14215,7 @@ async function tickKnowledgeSchedule(d) {
|
|
|
14072
14215
|
continue;
|
|
14073
14216
|
try {
|
|
14074
14217
|
const root = kbDir(d.cfg, kb);
|
|
14075
|
-
if (!
|
|
14218
|
+
if (!fs27.existsSync(root))
|
|
14076
14219
|
continue;
|
|
14077
14220
|
const { sources } = loadSources(root);
|
|
14078
14221
|
if (!sources.length)
|
|
@@ -14137,27 +14280,27 @@ var init_schedule = __esm({
|
|
|
14137
14280
|
init_sources();
|
|
14138
14281
|
RUNNING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
14139
14282
|
DAY_MS = 24 * 60 * 60 * 1e3;
|
|
14140
|
-
scheduleFile = (
|
|
14283
|
+
scheduleFile = (stateRoot2) => path28.join(stateRoot2, "knowledge-schedule.json");
|
|
14141
14284
|
reconcileConfig = (cfg, kb) => ({ ...DEFAULT_RECONCILE, ...kbConfigs(cfg)[kb]?.reconcile });
|
|
14142
14285
|
}
|
|
14143
14286
|
});
|
|
14144
14287
|
|
|
14145
14288
|
// packages/core/dist/knowledge/review.js
|
|
14146
14289
|
import crypto4 from "crypto";
|
|
14147
|
-
import { execFileSync as
|
|
14148
|
-
import
|
|
14149
|
-
import
|
|
14290
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
14291
|
+
import fs28 from "fs";
|
|
14292
|
+
import path29 from "path";
|
|
14150
14293
|
import matter5 from "gray-matter";
|
|
14151
14294
|
function readEscalations(kbRoot) {
|
|
14152
14295
|
try {
|
|
14153
|
-
return JSON.parse(
|
|
14296
|
+
return JSON.parse(fs28.readFileSync(escalationsFile(kbRoot), "utf8")).items;
|
|
14154
14297
|
} catch {
|
|
14155
14298
|
return [];
|
|
14156
14299
|
}
|
|
14157
14300
|
}
|
|
14158
14301
|
function writeEscalations(kbRoot, items) {
|
|
14159
|
-
|
|
14160
|
-
|
|
14302
|
+
fs28.mkdirSync(kbRoot, { recursive: true });
|
|
14303
|
+
fs28.writeFileSync(escalationsFile(kbRoot), JSON.stringify({ items }, null, 2) + "\n");
|
|
14161
14304
|
}
|
|
14162
14305
|
function addEscalation(kbRoot, e) {
|
|
14163
14306
|
const items = readEscalations(kbRoot);
|
|
@@ -14167,18 +14310,14 @@ function addEscalation(kbRoot, e) {
|
|
|
14167
14310
|
return true;
|
|
14168
14311
|
}
|
|
14169
14312
|
function appendVerdict(kbRoot, v2, now) {
|
|
14170
|
-
|
|
14171
|
-
|
|
14313
|
+
fs28.mkdirSync(kbRoot, { recursive: true });
|
|
14314
|
+
fs28.appendFileSync(verdictsFile(kbRoot), JSON.stringify({ ts: now.toISOString(), ...v2 }) + "\n");
|
|
14172
14315
|
}
|
|
14173
14316
|
function priorityOfRef(c, ref) {
|
|
14174
14317
|
if (ref.startsWith("agent:"))
|
|
14175
14318
|
return "agent";
|
|
14176
14319
|
const root = kbDir(c.cfg, c.kb);
|
|
14177
|
-
|
|
14178
|
-
for (const f of expandSource(root, e))
|
|
14179
|
-
if (refFor(root, f) === ref)
|
|
14180
|
-
return e.priority;
|
|
14181
|
-
return "project";
|
|
14320
|
+
return c.sources.find((e) => sourceCovers(root, c.sources, e, ref))?.priority ?? "project";
|
|
14182
14321
|
}
|
|
14183
14322
|
function priorityOfRule(c, r) {
|
|
14184
14323
|
return r.sources.map((s) => priorityOfRef(c, s.ref)).sort((a, b) => PRIORITY_RANK[b] - PRIORITY_RANK[a])[0] ?? "project";
|
|
@@ -14189,20 +14328,20 @@ function loadProposals(c) {
|
|
|
14189
14328
|
const byId = new Map(live.map((r) => [r.id, r]));
|
|
14190
14329
|
const proposals = [];
|
|
14191
14330
|
const pdirs = /* @__PURE__ */ new Set();
|
|
14192
|
-
for (const f of
|
|
14193
|
-
if (
|
|
14331
|
+
for (const f of fs28.existsSync(dir) ? walkDirs(dir) : [])
|
|
14332
|
+
if (path29.basename(f) === PROPOSED_DIR)
|
|
14194
14333
|
pdirs.add(f);
|
|
14195
14334
|
for (const pd of pdirs) {
|
|
14196
14335
|
for (const rule of loadRulesDir(pd, `kb:${c.kb}`, { includeProposed: true }).rules) {
|
|
14197
|
-
proposals.push({ key:
|
|
14336
|
+
proposals.push({ key: path29.basename(rule.file).replace(/\.md$/, ""), rule, live: byId.get(rule.id) });
|
|
14198
14337
|
}
|
|
14199
14338
|
}
|
|
14200
14339
|
return { proposals, live };
|
|
14201
14340
|
}
|
|
14202
14341
|
function walkDirs(dir, out = []) {
|
|
14203
|
-
for (const e of
|
|
14342
|
+
for (const e of fs28.readdirSync(dir, { withFileTypes: true }))
|
|
14204
14343
|
if (e.isDirectory()) {
|
|
14205
|
-
const f =
|
|
14344
|
+
const f = path29.join(dir, e.name);
|
|
14206
14345
|
out.push(f);
|
|
14207
14346
|
walkDirs(f, out);
|
|
14208
14347
|
}
|
|
@@ -14215,7 +14354,7 @@ function reindex(c) {
|
|
|
14215
14354
|
function anchorsFor(c, fm) {
|
|
14216
14355
|
const anchors = { ...fm.anchors ?? {} };
|
|
14217
14356
|
for (const p of c.projects ?? []) {
|
|
14218
|
-
const a = computeAnchor(p.root, fm.triggers);
|
|
14357
|
+
const a = computeAnchor(p.root, fm.triggers, c.anchorCache);
|
|
14219
14358
|
if (a)
|
|
14220
14359
|
anchors[p.name] = a;
|
|
14221
14360
|
}
|
|
@@ -14231,7 +14370,7 @@ function promoteProposal(c, p, by, justification, events) {
|
|
|
14231
14370
|
continue;
|
|
14232
14371
|
const o = readFm(other.file);
|
|
14233
14372
|
o.fm.status = "retired";
|
|
14234
|
-
|
|
14373
|
+
fs28.writeFileSync(other.file, fileText(o.fm, o.body));
|
|
14235
14374
|
retired.push(id);
|
|
14236
14375
|
events.push(ev(c, "item.superseded", id));
|
|
14237
14376
|
}
|
|
@@ -14241,16 +14380,16 @@ function promoteProposal(c, p, by, justification, events) {
|
|
|
14241
14380
|
const anchors = anchorsFor(c, { ...next, anchors: p.live?.anchors ?? fm.anchors });
|
|
14242
14381
|
if (anchors)
|
|
14243
14382
|
next.anchors = anchors;
|
|
14244
|
-
const target = p.live?.file ??
|
|
14245
|
-
|
|
14246
|
-
|
|
14247
|
-
|
|
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);
|
|
14248
14387
|
events.push(ev(c, "item.approved", next.id), ev(c, "item.applied", next.id));
|
|
14249
14388
|
if (p.live)
|
|
14250
14389
|
events.push(ev(c, "item.superseded", next.id));
|
|
14251
14390
|
}
|
|
14252
14391
|
function dropProposal(c, p, events) {
|
|
14253
|
-
|
|
14392
|
+
fs28.rmSync(p.rule.file);
|
|
14254
14393
|
events.push(ev(c, "item.rejected", p.rule.id, { reason: "reviewer" }));
|
|
14255
14394
|
}
|
|
14256
14395
|
function mergeInto(c, p, targetId, live, events) {
|
|
@@ -14258,11 +14397,12 @@ function mergeInto(c, p, targetId, live, events) {
|
|
|
14258
14397
|
if (!t)
|
|
14259
14398
|
return false;
|
|
14260
14399
|
const tf = readFm(t.file);
|
|
14400
|
+
const canon = refCanon(kbDir(c.cfg, c.kb), c.sources);
|
|
14261
14401
|
for (const s of p.rule.sources)
|
|
14262
|
-
if (!tf.fm.sources.some((x) => x.ref === s.ref))
|
|
14402
|
+
if (!tf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
|
|
14263
14403
|
tf.fm.sources.push(s);
|
|
14264
|
-
|
|
14265
|
-
|
|
14404
|
+
fs28.writeFileSync(t.file, fileText(tf.fm, tf.body));
|
|
14405
|
+
fs28.rmSync(p.rule.file);
|
|
14266
14406
|
events.push(ev(c, "item.superseded", p.rule.id), ev(c, "item.applied", targetId));
|
|
14267
14407
|
return true;
|
|
14268
14408
|
}
|
|
@@ -14272,7 +14412,7 @@ function evaluateCodeConflict(c, rule, cc) {
|
|
|
14272
14412
|
const root = proj?.root;
|
|
14273
14413
|
const code = root ? g.lastCommit(root, cc.files) : null;
|
|
14274
14414
|
const kbRoot = kbDir(c.cfg, c.kb);
|
|
14275
|
-
const docDates = rule.sources.map((s) => g.docDate(
|
|
14415
|
+
const docDates = rule.sources.map((s) => g.docDate(refPath(kbRoot, c.sources, s.ref))).filter((d2) => !!d2).sort();
|
|
14276
14416
|
const docDate = docDates[docDates.length - 1];
|
|
14277
14417
|
const docRev = docRevOf(rule);
|
|
14278
14418
|
const evidence = { docRev, codeRev: code?.rev ?? "unknown", files: cc.files, project: cc.project };
|
|
@@ -14294,6 +14434,7 @@ function evaluateCodeConflict(c, rule, cc) {
|
|
|
14294
14434
|
};
|
|
14295
14435
|
}
|
|
14296
14436
|
function applyDecisions(file, c) {
|
|
14437
|
+
c = { ...c, anchorCache: /* @__PURE__ */ new Map() };
|
|
14297
14438
|
const now = nowOf(c);
|
|
14298
14439
|
const kbRoot = kbDir(c.cfg, c.kb);
|
|
14299
14440
|
const threshold = reconcileConfig(c.cfg, c.kb).autoApproveConfidence;
|
|
@@ -14321,6 +14462,7 @@ function applyDecisions(file, c) {
|
|
|
14321
14462
|
if (v2.action === "flag-violation") {
|
|
14322
14463
|
escalate({ key: `violation:${target.id}`, kind: "code-violation", ruleId: target.id, reasons: ["doc newer than code"], explanation: v2.explanation, needsYou: false });
|
|
14323
14464
|
res.violations.push(target.id);
|
|
14465
|
+
events.push(ev(c, "item.outcome", target.id, { outcome: "violated", source: "reconcile", project: d.codeConflict.project }));
|
|
14324
14466
|
} else if (v2.action === "escalate") {
|
|
14325
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 });
|
|
14326
14468
|
verdict(d, "escalated", { reasons: ["code-vs-doc"], confidence: conf });
|
|
@@ -14339,7 +14481,7 @@ function applyDecisions(file, c) {
|
|
|
14339
14481
|
f.fm.sources[0].loc = d.loc;
|
|
14340
14482
|
const noSource = !rule.sources.length;
|
|
14341
14483
|
if (conf >= threshold && !d.flag && !reasons.length && !noSource) {
|
|
14342
|
-
|
|
14484
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14343
14485
|
res.applied.push(d.item);
|
|
14344
14486
|
events.push(ev(c, "item.applied", rule.id));
|
|
14345
14487
|
verdict(d, "applied", { confidence: conf });
|
|
@@ -14356,7 +14498,7 @@ function applyDecisions(file, c) {
|
|
|
14356
14498
|
if (!reasons.length) {
|
|
14357
14499
|
const f = readFm(rule.file);
|
|
14358
14500
|
f.fm.status = "retired";
|
|
14359
|
-
|
|
14501
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14360
14502
|
res.applied.push(d.item);
|
|
14361
14503
|
events.push(ev(c, "item.archived", rule.id));
|
|
14362
14504
|
verdict(d, "applied", { confidence: conf });
|
|
@@ -14436,7 +14578,7 @@ function applyPair(pd, c, res, threshold, events, kbRoot, now) {
|
|
|
14436
14578
|
for (const [x, y] of [[a, b], [b, a]]) {
|
|
14437
14579
|
const f = readFm(x.file);
|
|
14438
14580
|
f.fm.conflictsWith = [.../* @__PURE__ */ new Set([...f.fm.conflictsWith ?? [], y.id])];
|
|
14439
|
-
|
|
14581
|
+
fs28.writeFileSync(x.file, fileText(f.fm, f.body));
|
|
14440
14582
|
}
|
|
14441
14583
|
return;
|
|
14442
14584
|
}
|
|
@@ -14447,14 +14589,15 @@ function applyPair(pd, c, res, threshold, events, kbRoot, now) {
|
|
|
14447
14589
|
return;
|
|
14448
14590
|
}
|
|
14449
14591
|
const kf = readFm(keep.file);
|
|
14592
|
+
const canon = refCanon(kbRoot, c.sources);
|
|
14450
14593
|
for (const s of drop.sources)
|
|
14451
|
-
if (!kf.fm.sources.some((x) => x.ref === s.ref))
|
|
14594
|
+
if (!kf.fm.sources.some((x) => canon(x.ref) === canon(s.ref)))
|
|
14452
14595
|
kf.fm.sources.push(s);
|
|
14453
14596
|
kf.fm.supersedes = [.../* @__PURE__ */ new Set([...kf.fm.supersedes ?? [], drop.id])];
|
|
14454
|
-
|
|
14597
|
+
fs28.writeFileSync(keep.file, fileText(kf.fm, kf.body));
|
|
14455
14598
|
const df = readFm(drop.file);
|
|
14456
14599
|
df.fm.status = "retired";
|
|
14457
|
-
|
|
14600
|
+
fs28.writeFileSync(drop.file, fileText(df.fm, df.body));
|
|
14458
14601
|
events.push(ev(c, "item.superseded", drop.id), ev(c, "item.applied", keep.id));
|
|
14459
14602
|
res.applied.push(key);
|
|
14460
14603
|
}
|
|
@@ -14487,7 +14630,7 @@ function resolveEscalation(c, key, r, by, reason) {
|
|
|
14487
14630
|
f.fm.status = "retired";
|
|
14488
14631
|
events.push(ev(c, "item.superseded", rule.id));
|
|
14489
14632
|
}
|
|
14490
|
-
|
|
14633
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14491
14634
|
return done(`${rule.id}: ${r.verdict}`);
|
|
14492
14635
|
}
|
|
14493
14636
|
if (esc.kind === "proposal") {
|
|
@@ -14510,20 +14653,20 @@ function resolveEscalation(c, key, r, by, reason) {
|
|
|
14510
14653
|
if (rule) {
|
|
14511
14654
|
const f = readFm(rule.file);
|
|
14512
14655
|
f.fm.status = r.kind === "approve" ? "retired" : "active";
|
|
14513
|
-
|
|
14656
|
+
fs28.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14514
14657
|
events.push(ev(c, r.kind === "approve" ? "item.archived" : "item.applied", rule.id));
|
|
14515
14658
|
}
|
|
14516
14659
|
return done(`${esc.ruleId}: ${r.kind === "approve" ? "retired" : "kept"}`);
|
|
14517
14660
|
}
|
|
14518
14661
|
if (esc.kind === "override-base-changed") {
|
|
14519
|
-
const ov = esc.item &&
|
|
14662
|
+
const ov = esc.item && fs28.existsSync(esc.item) ? esc.item : void 0;
|
|
14520
14663
|
if (ov) {
|
|
14521
14664
|
if (r.kind === "reject")
|
|
14522
|
-
|
|
14665
|
+
fs28.rmSync(ov);
|
|
14523
14666
|
else {
|
|
14524
14667
|
const f = readFm(ov);
|
|
14525
14668
|
f.fm.overridesBase = esc.evidence?.docRev;
|
|
14526
|
-
|
|
14669
|
+
fs28.writeFileSync(ov, fileText(f.fm, f.body));
|
|
14527
14670
|
}
|
|
14528
14671
|
}
|
|
14529
14672
|
return done(`${esc.ruleId}: override ${r.kind === "reject" ? "dropped" : "kept"}`);
|
|
@@ -14547,10 +14690,10 @@ ${o.evidence}`).digest("hex");
|
|
|
14547
14690
|
supersedes: [],
|
|
14548
14691
|
conflictsWith: []
|
|
14549
14692
|
};
|
|
14550
|
-
const dir =
|
|
14551
|
-
|
|
14552
|
-
const file =
|
|
14553
|
-
|
|
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));
|
|
14554
14697
|
appendAudit([ev(c, "item.proposed", id)], c.audit);
|
|
14555
14698
|
return file;
|
|
14556
14699
|
}
|
|
@@ -14591,10 +14734,10 @@ var init_review = __esm({
|
|
|
14591
14734
|
PRIORITY_RANK = { company: 3, project: 2, agent: 1 };
|
|
14592
14735
|
SHA_LEN2 = 12;
|
|
14593
14736
|
strong = (m) => m === "must" || m === "must-not";
|
|
14594
|
-
escalationsFile = (kbRoot) =>
|
|
14595
|
-
verdictsFile = (kbRoot) =>
|
|
14737
|
+
escalationsFile = (kbRoot) => path29.join(kbRoot, "escalations.json");
|
|
14738
|
+
verdictsFile = (kbRoot) => path29.join(kbRoot, "verdicts.jsonl");
|
|
14596
14739
|
needsYouCount = (kbRoot) => readEscalations(kbRoot).filter((e) => e.needsYou).length;
|
|
14597
|
-
git = (root, args) =>
|
|
14740
|
+
git = (root, args) => execFileSync8("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
14598
14741
|
realGit = {
|
|
14599
14742
|
lastCommit(root, files) {
|
|
14600
14743
|
try {
|
|
@@ -14613,13 +14756,13 @@ var init_review = __esm({
|
|
|
14613
14756
|
},
|
|
14614
14757
|
docDate(file) {
|
|
14615
14758
|
try {
|
|
14616
|
-
const d = git(
|
|
14759
|
+
const d = git(path29.dirname(file), ["log", "-1", "--format=%cI", "--", path29.basename(file)]);
|
|
14617
14760
|
if (d)
|
|
14618
14761
|
return d;
|
|
14619
14762
|
} catch {
|
|
14620
14763
|
}
|
|
14621
14764
|
try {
|
|
14622
|
-
return
|
|
14765
|
+
return fs28.statSync(file).mtime.toISOString();
|
|
14623
14766
|
} catch {
|
|
14624
14767
|
return null;
|
|
14625
14768
|
}
|
|
@@ -14628,7 +14771,7 @@ var init_review = __esm({
|
|
|
14628
14771
|
nowOf = (c) => c.now?.() ?? /* @__PURE__ */ new Date();
|
|
14629
14772
|
fileText = (fm, body) => matter5.stringify(body.trim() + "\n", JSON.parse(JSON.stringify(fm)));
|
|
14630
14773
|
readFm = (file) => {
|
|
14631
|
-
const m = matter5(
|
|
14774
|
+
const m = matter5(fs28.readFileSync(file, "utf8"));
|
|
14632
14775
|
return { fm: m.data, body: m.content };
|
|
14633
14776
|
};
|
|
14634
14777
|
ev = (c, event, itemId, extra = {}) => ({ kb: c.kb, level: "group", project: "", domain: "rules", itemId, event, ...extra });
|
|
@@ -14638,8 +14781,8 @@ var init_review = __esm({
|
|
|
14638
14781
|
|
|
14639
14782
|
// packages/core/dist/knowledge/fullpass.js
|
|
14640
14783
|
import crypto5 from "crypto";
|
|
14641
|
-
import
|
|
14642
|
-
import
|
|
14784
|
+
import fs29 from "fs";
|
|
14785
|
+
import path30 from "path";
|
|
14643
14786
|
import matter6 from "gray-matter";
|
|
14644
14787
|
function dedupPairs(rules) {
|
|
14645
14788
|
const active = rules.filter((r) => r.status === "active").sort((x, y) => x.id.localeCompare(y.id));
|
|
@@ -14656,12 +14799,12 @@ function dedupPairs(rules) {
|
|
|
14656
14799
|
return out.slice(0, MAX_DEDUP_PAIRS);
|
|
14657
14800
|
}
|
|
14658
14801
|
function readAuditRecords(kb, since, dir = defaultAuditDir()) {
|
|
14659
|
-
if (!
|
|
14802
|
+
if (!fs29.existsSync(dir))
|
|
14660
14803
|
return [];
|
|
14661
14804
|
const sinceMonth = since.toISOString().slice(0, 7);
|
|
14662
14805
|
const out = [];
|
|
14663
|
-
for (const f of
|
|
14664
|
-
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")) {
|
|
14665
14808
|
if (!line2.trim())
|
|
14666
14809
|
continue;
|
|
14667
14810
|
try {
|
|
@@ -14683,7 +14826,7 @@ function usageReview(rules, events) {
|
|
|
14683
14826
|
continue;
|
|
14684
14827
|
if (e.event === "item.surfaced")
|
|
14685
14828
|
surfaced[e.itemId] = (surfaced[e.itemId] ?? 0) + 1;
|
|
14686
|
-
if (e.event === "item.outcome") {
|
|
14829
|
+
if (e.event === "item.outcome" && RATED_SOURCES.has(outcomeSource(e))) {
|
|
14687
14830
|
if (e.outcome === "noise")
|
|
14688
14831
|
noise[e.itemId] = (noise[e.itemId] ?? 0) + 1;
|
|
14689
14832
|
if (e.outcome === "violated")
|
|
@@ -14724,8 +14867,8 @@ function checkOverrides(cfg, kb) {
|
|
|
14724
14867
|
continue;
|
|
14725
14868
|
const h = baseHash(b);
|
|
14726
14869
|
if (!o.overridesBase) {
|
|
14727
|
-
const m = matter6(
|
|
14728
|
-
|
|
14870
|
+
const m = matter6(fs29.readFileSync(o.file, "utf8"));
|
|
14871
|
+
fs29.writeFileSync(o.file, matter6.stringify(m.content, { ...m.data, overridesBase: h }));
|
|
14729
14872
|
continue;
|
|
14730
14873
|
}
|
|
14731
14874
|
out.push({ project, ruleId: o.id, file: o.file, baseHash: h, changed: o.overridesBase !== h });
|
|
@@ -14768,8 +14911,8 @@ var init_fullpass = __esm({
|
|
|
14768
14911
|
});
|
|
14769
14912
|
|
|
14770
14913
|
// packages/core/dist/knowledge/scores.js
|
|
14771
|
-
import
|
|
14772
|
-
import
|
|
14914
|
+
import fs30 from "fs";
|
|
14915
|
+
import path31 from "path";
|
|
14773
14916
|
function tally(records, into = emptyTally()) {
|
|
14774
14917
|
for (const e of records) {
|
|
14775
14918
|
const domain = typeof e.domain === "string" && e.domain ? e.domain : "rules";
|
|
@@ -14805,17 +14948,25 @@ function tally(records, into = emptyTally()) {
|
|
|
14805
14948
|
c.cited++;
|
|
14806
14949
|
c.lastUsed = later(c.lastUsed, e.ts);
|
|
14807
14950
|
break;
|
|
14808
|
-
case "item.outcome":
|
|
14809
|
-
if (OUTCOMES.has(String(e.outcome)))
|
|
14810
|
-
|
|
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]++;
|
|
14811
14961
|
break;
|
|
14962
|
+
}
|
|
14812
14963
|
}
|
|
14813
14964
|
}
|
|
14814
14965
|
return into;
|
|
14815
14966
|
}
|
|
14816
14967
|
function parseLines(file) {
|
|
14817
14968
|
const out = [];
|
|
14818
|
-
for (const line2 of
|
|
14969
|
+
for (const line2 of fs30.readFileSync(file, "utf8").split("\n")) {
|
|
14819
14970
|
if (!line2.trim())
|
|
14820
14971
|
continue;
|
|
14821
14972
|
try {
|
|
@@ -14827,37 +14978,37 @@ function parseLines(file) {
|
|
|
14827
14978
|
}
|
|
14828
14979
|
function compactAudit(o) {
|
|
14829
14980
|
const dir = o.dir ?? defaultAuditDir();
|
|
14830
|
-
if (!
|
|
14981
|
+
if (!fs30.existsSync(dir))
|
|
14831
14982
|
return [];
|
|
14832
14983
|
const cutoffMonth = new Date(o.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3).toISOString().slice(0, 7);
|
|
14833
14984
|
const done = [];
|
|
14834
|
-
for (const f of
|
|
14985
|
+
for (const f of fs30.readdirSync(dir).sort()) {
|
|
14835
14986
|
const m = MONTH_FILE_RE.exec(f);
|
|
14836
14987
|
if (!m || m[1] >= cutoffMonth)
|
|
14837
14988
|
continue;
|
|
14838
14989
|
const [, month, machine] = m;
|
|
14839
14990
|
const byKb = /* @__PURE__ */ new Map();
|
|
14840
|
-
for (const r of parseLines(
|
|
14991
|
+
for (const r of parseLines(path31.join(dir, f)))
|
|
14841
14992
|
byKb.set(r.kb ?? "", [...byKb.get(r.kb ?? "") ?? [], r]);
|
|
14842
|
-
const statsPath =
|
|
14843
|
-
const prior =
|
|
14993
|
+
const statsPath = path31.join(dir, statsFileName(month, machine));
|
|
14994
|
+
const prior = fs30.existsSync(statsPath) ? JSON.parse(fs30.readFileSync(statsPath, "utf8")) : null;
|
|
14844
14995
|
const kbs = prior?.kbs ?? {};
|
|
14845
14996
|
for (const [kb, recs] of byKb)
|
|
14846
14997
|
kbs[kb] = tally(recs, kbs[kb]);
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
|
|
14998
|
+
fs30.writeFileSync(`${statsPath}.tmp`, JSON.stringify({ month, machine, kbs }));
|
|
14999
|
+
fs30.renameSync(`${statsPath}.tmp`, statsPath);
|
|
15000
|
+
fs30.unlinkSync(path31.join(dir, f));
|
|
14850
15001
|
done.push(month);
|
|
14851
15002
|
}
|
|
14852
15003
|
return done;
|
|
14853
15004
|
}
|
|
14854
15005
|
function readCompacted(kb, dir) {
|
|
14855
15006
|
const t = emptyTally();
|
|
14856
|
-
if (!
|
|
15007
|
+
if (!fs30.existsSync(dir))
|
|
14857
15008
|
return t;
|
|
14858
|
-
for (const f of
|
|
15009
|
+
for (const f of fs30.readdirSync(dir).filter((n) => n.endsWith(".stats.json"))) {
|
|
14859
15010
|
try {
|
|
14860
|
-
const k = JSON.parse(
|
|
15011
|
+
const k = JSON.parse(fs30.readFileSync(path31.join(dir, f), "utf8")).kbs[kb];
|
|
14861
15012
|
if (k)
|
|
14862
15013
|
mergeTally(t, k);
|
|
14863
15014
|
} catch {
|
|
@@ -14883,6 +15034,12 @@ function mergeTally(into, from) {
|
|
|
14883
15034
|
into.agents[k] = (into.agents[k] ?? 0) + v2;
|
|
14884
15035
|
for (const [k, v2] of Object.entries(from.tiers))
|
|
14885
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
|
+
}
|
|
14886
15043
|
}
|
|
14887
15044
|
function computeScores(i) {
|
|
14888
15045
|
const since = i.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3;
|
|
@@ -14906,11 +15063,14 @@ function computeScores(i) {
|
|
|
14906
15063
|
violated: w.violated,
|
|
14907
15064
|
noise: w.noise,
|
|
14908
15065
|
wrong: w.wrong,
|
|
15066
|
+
codeViolations: w.codeViolations,
|
|
15067
|
+
evaluated: w.followed + w.violated,
|
|
14909
15068
|
fireRate: w.surfaced,
|
|
14910
15069
|
pullRate: w.shown + w.searched,
|
|
14911
15070
|
useRate: ratio(w.cited, w.surfaced),
|
|
14912
15071
|
noiseRate: ratio(w.noise, w.surfaced),
|
|
14913
15072
|
violationRate: ratio(w.violated, w.surfaced),
|
|
15073
|
+
complianceRate: w.followed + w.violated >= COMPLIANCE_MIN_EVALUATED ? ratio(w.followed, w.followed + w.violated) : null,
|
|
14914
15074
|
lastSurfaced: l?.lastSurfaced,
|
|
14915
15075
|
lastUsed: l?.lastUsed,
|
|
14916
15076
|
neverFired30d: w.surfaced === 0 && (knownActive.has(key) || (l?.surfaced ?? 0) > 0),
|
|
@@ -14943,53 +15103,58 @@ function computeScores(i) {
|
|
|
14943
15103
|
domains,
|
|
14944
15104
|
perAgentCoverage: win.agents,
|
|
14945
15105
|
tierContribution: win.tiers,
|
|
14946
|
-
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
|
|
14947
15108
|
};
|
|
14948
15109
|
}
|
|
14949
15110
|
function scoreKb(o) {
|
|
14950
15111
|
const dir = o.auditDir ?? defaultAuditDir();
|
|
14951
15112
|
const records = [];
|
|
14952
|
-
if (
|
|
14953
|
-
for (const f of
|
|
14954
|
-
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));
|
|
14955
15116
|
}
|
|
14956
15117
|
}
|
|
14957
15118
|
let verdicts = [];
|
|
14958
15119
|
try {
|
|
14959
|
-
verdicts =
|
|
15120
|
+
verdicts = fs30.readFileSync(path31.join(o.kbRoot, "verdicts.jsonl"), "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
14960
15121
|
} catch {
|
|
14961
15122
|
}
|
|
14962
15123
|
return computeScores({ kb: o.kb, records, now: o.now, known: o.known, compacted: readCompacted(o.kb, dir), verdicts });
|
|
14963
15124
|
}
|
|
14964
15125
|
function writeScores(kbRoot, s) {
|
|
14965
|
-
|
|
14966
|
-
|
|
15126
|
+
fs30.mkdirSync(kbRoot, { recursive: true });
|
|
15127
|
+
fs30.writeFileSync(scoresFile(kbRoot), JSON.stringify(s, null, 2) + "\n");
|
|
14967
15128
|
}
|
|
14968
15129
|
function renderScoresSection(s) {
|
|
14969
15130
|
const pct = (x) => x === null ? "n/a" : `${Math.round(x * 100)}%`;
|
|
14970
15131
|
const used = s.items.filter((x) => x.surfaced).sort((a, b) => b.surfaced - a.surfaced || a.itemId.localeCompare(b.itemId)).slice(0, 10);
|
|
14971
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(", ")}`);
|
|
14972
15135
|
return [
|
|
14973
15136
|
"",
|
|
14974
15137
|
`## Scores (${s.windowDays} days)`,
|
|
14975
15138
|
"",
|
|
14976
15139
|
...used.length ? [
|
|
14977
|
-
"| Item | Surfaced | Pulled | Cited | Use | Noise | Violated |",
|
|
14978
|
-
"
|
|
14979
|
-
...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)} |`)
|
|
14980
15143
|
] : ["No items surfaced."],
|
|
14981
15144
|
"",
|
|
14982
15145
|
`- Never fired (${never.length}): ${never.slice(0, 20).join(", ") || "none"}${never.length > 20 ? ", \u2026" : ""}`,
|
|
14983
15146
|
`- Tier contribution: ${Object.entries(s.tierContribution).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
14984
15147
|
`- Per-agent coverage (surfaced): ${Object.entries(s.perAgentCoverage).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
14985
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"}`,
|
|
14986
15151
|
"",
|
|
14987
15152
|
"| Domain | Writes/mo | Reads/mo | Chars per citation | Alarm |",
|
|
14988
15153
|
"|---|---|---|---|---|",
|
|
14989
15154
|
...s.domains.map((d) => `| ${d.domain} | ${d.writesPerMonth} | ${d.readsPerMonth} | ${d.charsPerCitation ?? "n/a"} | ${d.deadDomain ? "DEAD (no activity 30d)" : ""} |`)
|
|
14990
15155
|
];
|
|
14991
15156
|
}
|
|
14992
|
-
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;
|
|
14993
15158
|
var init_scores = __esm({
|
|
14994
15159
|
"packages/core/dist/knowledge/scores.js"() {
|
|
14995
15160
|
init_audit();
|
|
@@ -14998,19 +15163,20 @@ var init_scores = __esm({
|
|
|
14998
15163
|
WRITE_EVENTS = /* @__PURE__ */ new Set(["item.proposed", "item.approved", "item.rejected", "item.applied", "item.promoted", "item.superseded", "item.archived"]);
|
|
14999
15164
|
READ_EVENTS = /* @__PURE__ */ new Set(["item.surfaced", "item.shown", "item.searched"]);
|
|
15000
15165
|
OUTCOMES = /* @__PURE__ */ new Set(["followed", "violated", "noise", "wrong"]);
|
|
15001
|
-
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 });
|
|
15002
15167
|
later = (a, b) => a && b ? a > b ? a : b : a ?? b;
|
|
15003
|
-
emptyTally = () => ({ items: {}, domains: {}, agents: {}, tiers: {} });
|
|
15168
|
+
emptyTally = () => ({ items: {}, domains: {}, agents: {}, tiers: {}, sources: {} });
|
|
15004
15169
|
MONTH_FILE_RE = /^(\d{4}-\d{2})\.(.+)\.jsonl$/;
|
|
15005
15170
|
statsFileName = (month, machine) => `${month}.${machine}.stats.json`;
|
|
15006
15171
|
ratio = (n, d) => d ? Math.round(n / d * 1e3) / 1e3 : null;
|
|
15007
|
-
|
|
15172
|
+
COMPLIANCE_MIN_EVALUATED = 3;
|
|
15173
|
+
scoresFile = (kbRoot) => path31.join(kbRoot, "scores.json");
|
|
15008
15174
|
}
|
|
15009
15175
|
});
|
|
15010
15176
|
|
|
15011
15177
|
// packages/core/dist/knowledge/report.js
|
|
15012
|
-
import
|
|
15013
|
-
import
|
|
15178
|
+
import fs31 from "fs";
|
|
15179
|
+
import path32 from "path";
|
|
15014
15180
|
function renderReport(cfg, kb, o) {
|
|
15015
15181
|
const { rules } = loadKbRules(cfg, kb, { includeProposed: true });
|
|
15016
15182
|
const count = (s) => rules.filter((r) => r.status === s).length;
|
|
@@ -15033,21 +15199,21 @@ function renderReport(cfg, kb, o) {
|
|
|
15033
15199
|
if (info.length)
|
|
15034
15200
|
L.push("", `## For information (${info.length})`, "", ...info.map((e) => `- **${e.key}**: ${e.explanation}`));
|
|
15035
15201
|
const { errata, errors: errataErrors } = loadErrata(kbDir(cfg, kb));
|
|
15036
|
-
const orphans = orphanedErrata(kbDir(cfg, kb), errata);
|
|
15202
|
+
const orphans = orphanedErrata(kbDir(cfg, kb), loadSources(kbDir(cfg, kb)).sources, errata);
|
|
15037
15203
|
if (orphans.length || errataErrors.length) {
|
|
15038
15204
|
L.push("", `## Errata to re-check (${orphans.length + errataErrors.length})`, "", ...orphans.map((e) => `- **${e.id}** (${e.ref}): quote no longer found in the source; the raw file changed. Re-check it in errata.yaml.`), ...errataErrors.map((e) => `- errata.yaml: ${e}`));
|
|
15039
15205
|
}
|
|
15040
15206
|
if (o.full) {
|
|
15041
15207
|
const { usage: usage2, caps } = o.full;
|
|
15042
|
-
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"}`);
|
|
15043
15209
|
}
|
|
15044
15210
|
if (o.scores)
|
|
15045
15211
|
L.push(...renderScoresSection(o.scores));
|
|
15046
15212
|
return L.join("\n") + "\n";
|
|
15047
15213
|
}
|
|
15048
15214
|
function writeReport(cfg, kb, o) {
|
|
15049
|
-
const file =
|
|
15050
|
-
|
|
15215
|
+
const file = path32.join(kbDir(cfg, kb), "REPORT.md");
|
|
15216
|
+
fs31.writeFileSync(file, renderReport(cfg, kb, o));
|
|
15051
15217
|
return file;
|
|
15052
15218
|
}
|
|
15053
15219
|
var init_report = __esm({
|
|
@@ -15055,6 +15221,7 @@ var init_report = __esm({
|
|
|
15055
15221
|
init_errata();
|
|
15056
15222
|
init_layers();
|
|
15057
15223
|
init_paths();
|
|
15224
|
+
init_sources();
|
|
15058
15225
|
init_review();
|
|
15059
15226
|
init_scores();
|
|
15060
15227
|
}
|
|
@@ -15206,6 +15373,7 @@ __export(dist_exports, {
|
|
|
15206
15373
|
CITE_LOOKBACK_DAYS: () => CITE_LOOKBACK_DAYS,
|
|
15207
15374
|
CITE_NOTE: () => CITE_NOTE,
|
|
15208
15375
|
CMUX_PRESERVE_CLAUDE_AUTH_ENV: () => CMUX_PRESERVE_CLAUDE_AUTH_ENV,
|
|
15376
|
+
COMPLIANCE_MIN_EVALUATED: () => COMPLIANCE_MIN_EVALUATED,
|
|
15209
15377
|
CONFLICT_409_SELF_HEAL_MS: () => CONFLICT_409_SELF_HEAL_MS,
|
|
15210
15378
|
CONVERTED_DIR: () => CONVERTED_DIR,
|
|
15211
15379
|
CREW_GONE_MS: () => CREW_GONE_MS,
|
|
@@ -15219,6 +15387,7 @@ __export(dist_exports, {
|
|
|
15219
15387
|
DEFAULT_MAX_SECTIONS_PER_PASS: () => DEFAULT_MAX_SECTIONS_PER_PASS,
|
|
15220
15388
|
DEFAULT_SEARCH_STATUSES: () => DEFAULT_SEARCH_STATUSES,
|
|
15221
15389
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
15390
|
+
DELIVERY_EVENTS: () => DELIVERY_EVENTS,
|
|
15222
15391
|
DOCLING_DROP_THRESHOLD: () => DOCLING_DROP_THRESHOLD,
|
|
15223
15392
|
DeferDelivery: () => DeferDelivery,
|
|
15224
15393
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
@@ -15238,6 +15407,7 @@ __export(dist_exports, {
|
|
|
15238
15407
|
NOISY_MIN_SURFACED: () => NOISY_MIN_SURFACED,
|
|
15239
15408
|
NOISY_RATE: () => NOISY_RATE,
|
|
15240
15409
|
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
15410
|
+
OUTCOME_SOURCES: () => OUTCOME_SOURCES,
|
|
15241
15411
|
PANE_IDLE_AFTER_MS: () => PANE_IDLE_AFTER_MS,
|
|
15242
15412
|
PASS_THROUGH: () => PASS_THROUGH,
|
|
15243
15413
|
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
@@ -15246,6 +15416,7 @@ __export(dist_exports, {
|
|
|
15246
15416
|
PROMPT_SINGLE_TERM_MIN_SCORE: () => PROMPT_SINGLE_TERM_MIN_SCORE,
|
|
15247
15417
|
PROPOSED_DIR: () => PROPOSED_DIR,
|
|
15248
15418
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
15419
|
+
RATED_SOURCES: () => RATED_SOURCES,
|
|
15249
15420
|
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
15250
15421
|
READ_ONLY_TOP_LEVEL_COMMANDS: () => READ_ONLY_TOP_LEVEL_COMMANDS,
|
|
15251
15422
|
ROUTER_READY_ATTEMPTS: () => ROUTER_READY_ATTEMPTS,
|
|
@@ -15390,7 +15561,6 @@ __export(dist_exports, {
|
|
|
15390
15561
|
extractToolPayload: () => extractToolPayload,
|
|
15391
15562
|
extractUserIntentFromTranscript: () => extractUserIntentFromTranscript,
|
|
15392
15563
|
fallsBackToPane: () => fallsBackToPane,
|
|
15393
|
-
fileForRef: () => fileForRef,
|
|
15394
15564
|
findCrewPane: () => findCrewPane,
|
|
15395
15565
|
findLegacyKbs: () => findLegacyKbs,
|
|
15396
15566
|
findLegacyOverlays: () => findLegacyOverlays,
|
|
@@ -15456,6 +15626,7 @@ __export(dist_exports, {
|
|
|
15456
15626
|
loadSessions: () => loadSessions,
|
|
15457
15627
|
loadSources: () => loadSources,
|
|
15458
15628
|
loadState: () => loadState,
|
|
15629
|
+
loadedDaemonLogPath: () => loadedDaemonLogPath,
|
|
15459
15630
|
mailboxStats: () => mailboxStats,
|
|
15460
15631
|
makeGate: () => makeGate,
|
|
15461
15632
|
markPendingWarned: () => markPendingWarned,
|
|
@@ -15484,9 +15655,11 @@ __export(dist_exports, {
|
|
|
15484
15655
|
notePaneScreen: () => notePaneScreen,
|
|
15485
15656
|
notifyToggle: () => notifyToggle,
|
|
15486
15657
|
orphanedErrata: () => orphanedErrata,
|
|
15658
|
+
outcomeSource: () => outcomeSource,
|
|
15487
15659
|
parseBearer: () => parseBearer,
|
|
15488
15660
|
parseClassifierVerdict: () => parseClassifierVerdict,
|
|
15489
15661
|
parseCommand: () => parseCommand,
|
|
15662
|
+
parseLaunchctlStderrPath: () => parseLaunchctlStderrPath,
|
|
15490
15663
|
parseLiveOpencodeServers: () => parseLiveOpencodeServers,
|
|
15491
15664
|
parseNotifyPref: () => parseNotifyPref,
|
|
15492
15665
|
parseProgramArgs: () => parseProgramArgs,
|
|
@@ -15532,7 +15705,10 @@ __export(dist_exports, {
|
|
|
15532
15705
|
recoverStall: () => recoverStall,
|
|
15533
15706
|
reduce: () => reduce,
|
|
15534
15707
|
reduceLifecycle: () => reduceLifecycle,
|
|
15708
|
+
refCandidates: () => refCandidates,
|
|
15709
|
+
refCanon: () => refCanon,
|
|
15535
15710
|
refFor: () => refFor,
|
|
15711
|
+
refPath: () => refPath,
|
|
15536
15712
|
releaseDaemonLock: () => releaseDaemonLock,
|
|
15537
15713
|
removeRunningMarker: () => removeRunningMarker,
|
|
15538
15714
|
removeSource: () => removeSource,
|
|
@@ -15565,6 +15741,7 @@ __export(dist_exports, {
|
|
|
15565
15741
|
resolveSetupGroup: () => resolveSetupGroup,
|
|
15566
15742
|
resolveSetupToken: () => resolveSetupToken,
|
|
15567
15743
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
15744
|
+
resolveSourceRef: () => resolveSourceRef,
|
|
15568
15745
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
15569
15746
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
15570
15747
|
ruleAuditScope: () => ruleAuditScope,
|
|
@@ -15741,23 +15918,22 @@ init_dist2();
|
|
|
15741
15918
|
init_dist2();
|
|
15742
15919
|
init_dist2();
|
|
15743
15920
|
init_dist2();
|
|
15744
|
-
import { join as join27, dirname as dirname6, resolve as
|
|
15745
|
-
import { homedir as homedir14 } from "os";
|
|
15921
|
+
import { join as join27, dirname as dirname6, resolve as resolve4 } from "path";
|
|
15746
15922
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15747
|
-
import { readFileSync as readFileSync19, statSync as statSync4, existsSync as
|
|
15923
|
+
import { readFileSync as readFileSync19, statSync as statSync4, existsSync as existsSync14 } from "fs";
|
|
15748
15924
|
|
|
15749
15925
|
// packages/agents/dist/drivers/claude.js
|
|
15750
15926
|
import { execSync as execSync2 } from "child_process";
|
|
15751
15927
|
|
|
15752
15928
|
// packages/agents/dist/sessions/claude-sessions.js
|
|
15753
|
-
import { readdirSync as
|
|
15929
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
|
|
15754
15930
|
import { join as join18 } from "path";
|
|
15755
15931
|
|
|
15756
15932
|
// packages/agents/dist/claude/registry.js
|
|
15757
|
-
import
|
|
15933
|
+
import fs32 from "fs";
|
|
15758
15934
|
import { join as join17 } from "path";
|
|
15759
|
-
import { homedir as
|
|
15760
|
-
var CLAUDE_SESSIONS_DIR = join17(
|
|
15935
|
+
import { homedir as homedir6 } from "os";
|
|
15936
|
+
var CLAUDE_SESSIONS_DIR = join17(homedir6(), ".claude", "sessions");
|
|
15761
15937
|
var PID_JSON = /^(\d+)\.json$/;
|
|
15762
15938
|
function parseRegistryDir(files, readFile7) {
|
|
15763
15939
|
const out = [];
|
|
@@ -15799,11 +15975,11 @@ function toLifecycleSnapshot(entry, taskId, alive, now) {
|
|
|
15799
15975
|
function readClaudeStatusBySocketPath(socketPath) {
|
|
15800
15976
|
let files;
|
|
15801
15977
|
try {
|
|
15802
|
-
files =
|
|
15978
|
+
files = fs32.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
15803
15979
|
} catch {
|
|
15804
15980
|
return void 0;
|
|
15805
15981
|
}
|
|
15806
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
15982
|
+
const entries = parseRegistryDir(files, (name) => fs32.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
15807
15983
|
const entry = entries.find((e) => e.messagingSocketPath === socketPath);
|
|
15808
15984
|
if (!entry)
|
|
15809
15985
|
return void 0;
|
|
@@ -15820,44 +15996,43 @@ import { execSync as execSync4 } from "child_process";
|
|
|
15820
15996
|
import { execSync as execSync5 } from "child_process";
|
|
15821
15997
|
|
|
15822
15998
|
// packages/agents/dist/sessions/opencode-sessions.js
|
|
15823
|
-
|
|
15999
|
+
init_dist();
|
|
16000
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
|
|
15824
16001
|
import { join as join19 } from "path";
|
|
15825
|
-
import { homedir as homedir8 } from "os";
|
|
15826
|
-
var SQUADRANT_STATE_DIR = join19(homedir8(), ".config", "squadrant", "state");
|
|
15827
16002
|
|
|
15828
16003
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
15829
|
-
import
|
|
15830
|
-
import
|
|
16004
|
+
import fs33 from "fs";
|
|
16005
|
+
import path33 from "path";
|
|
15831
16006
|
|
|
15832
16007
|
// packages/agents/dist/projection/cursor.js
|
|
15833
16008
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
15834
|
-
import
|
|
16009
|
+
import path34 from "path";
|
|
15835
16010
|
import os10 from "os";
|
|
15836
16011
|
|
|
15837
16012
|
// packages/agents/dist/projection/codex.js
|
|
15838
16013
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
15839
|
-
import
|
|
16014
|
+
import path35 from "path";
|
|
15840
16015
|
import os11 from "os";
|
|
15841
16016
|
|
|
15842
16017
|
// packages/agents/dist/projection/gemini.js
|
|
15843
16018
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
15844
|
-
import
|
|
16019
|
+
import path36 from "path";
|
|
15845
16020
|
import os12 from "os";
|
|
15846
16021
|
|
|
15847
16022
|
// packages/agents/dist/projection/opencode.js
|
|
15848
16023
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
15849
|
-
import
|
|
16024
|
+
import path37 from "path";
|
|
15850
16025
|
import os13 from "os";
|
|
15851
16026
|
|
|
15852
16027
|
// packages/agents/dist/projection/opencode-skills.js
|
|
15853
|
-
import
|
|
16028
|
+
import fs34 from "fs";
|
|
15854
16029
|
import os14 from "os";
|
|
15855
|
-
import
|
|
16030
|
+
import path38 from "path";
|
|
15856
16031
|
var OPENCODE_SKILL_MARKER = ".squadrant-managed";
|
|
15857
16032
|
var MARKER_BODY = "squadrant\n";
|
|
15858
16033
|
function defaultOpencodeSkillsRoot(env = process.env) {
|
|
15859
|
-
const configHome = env.XDG_CONFIG_HOME ||
|
|
15860
|
-
return
|
|
16034
|
+
const configHome = env.XDG_CONFIG_HOME || path38.join(os14.homedir(), ".config");
|
|
16035
|
+
return path38.join(configHome, "opencode", "skills");
|
|
15861
16036
|
}
|
|
15862
16037
|
var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
15863
16038
|
function parseSkillName(raw) {
|
|
@@ -15872,62 +16047,62 @@ function parseSkillName(raw) {
|
|
|
15872
16047
|
}
|
|
15873
16048
|
function listSourceSkills(sourceSkillsDir) {
|
|
15874
16049
|
const out = /* @__PURE__ */ new Map();
|
|
15875
|
-
if (!
|
|
16050
|
+
if (!fs34.existsSync(sourceSkillsDir))
|
|
15876
16051
|
return out;
|
|
15877
|
-
for (const entry of
|
|
16052
|
+
for (const entry of fs34.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
|
|
15878
16053
|
if (!entry.isDirectory())
|
|
15879
16054
|
continue;
|
|
15880
|
-
const skillPath =
|
|
15881
|
-
if (!
|
|
16055
|
+
const skillPath = path38.join(sourceSkillsDir, entry.name, "SKILL.md");
|
|
16056
|
+
if (!fs34.existsSync(skillPath))
|
|
15882
16057
|
continue;
|
|
15883
|
-
const name = parseSkillName(
|
|
16058
|
+
const name = parseSkillName(fs34.readFileSync(skillPath, "utf-8"));
|
|
15884
16059
|
if (name && SAFE_NAME.test(name))
|
|
15885
16060
|
out.set(name, skillPath);
|
|
15886
16061
|
}
|
|
15887
16062
|
return out;
|
|
15888
16063
|
}
|
|
15889
16064
|
function hasMarker(dir) {
|
|
15890
|
-
return
|
|
16065
|
+
return fs34.existsSync(path38.join(dir, OPENCODE_SKILL_MARKER));
|
|
15891
16066
|
}
|
|
15892
16067
|
function syncOpencodeSkills(opts) {
|
|
15893
16068
|
const result = { written: [], unchanged: [], removed: [], skipped: [] };
|
|
15894
16069
|
const wanted = listSourceSkills(opts.sourceSkillsDir);
|
|
15895
|
-
if (wanted.size === 0 && !
|
|
16070
|
+
if (wanted.size === 0 && !fs34.existsSync(opts.sourceSkillsDir))
|
|
15896
16071
|
return result;
|
|
15897
16072
|
if (!opts.dryRun)
|
|
15898
|
-
|
|
16073
|
+
fs34.mkdirSync(opts.skillsRoot, { recursive: true });
|
|
15899
16074
|
for (const [name, srcPath] of wanted) {
|
|
15900
|
-
const targetDir =
|
|
15901
|
-
const targetSkill =
|
|
16075
|
+
const targetDir = path38.join(opts.skillsRoot, name);
|
|
16076
|
+
const targetSkill = path38.join(targetDir, "SKILL.md");
|
|
15902
16077
|
const managed = hasMarker(targetDir);
|
|
15903
|
-
if (
|
|
16078
|
+
if (fs34.existsSync(targetSkill) && !managed) {
|
|
15904
16079
|
result.skipped.push({ name, reason: "foreign-collision" });
|
|
15905
16080
|
continue;
|
|
15906
16081
|
}
|
|
15907
|
-
const current =
|
|
15908
|
-
if (current && current.equals(
|
|
16082
|
+
const current = fs34.existsSync(targetSkill) ? fs34.readFileSync(targetSkill) : null;
|
|
16083
|
+
if (current && current.equals(fs34.readFileSync(srcPath))) {
|
|
15909
16084
|
if (!managed && !opts.dryRun) {
|
|
15910
|
-
|
|
16085
|
+
fs34.writeFileSync(path38.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
15911
16086
|
}
|
|
15912
16087
|
result.unchanged.push(name);
|
|
15913
16088
|
continue;
|
|
15914
16089
|
}
|
|
15915
16090
|
if (!opts.dryRun) {
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
16091
|
+
fs34.mkdirSync(targetDir, { recursive: true });
|
|
16092
|
+
fs34.copyFileSync(srcPath, targetSkill);
|
|
16093
|
+
fs34.writeFileSync(path38.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
15919
16094
|
}
|
|
15920
16095
|
result.written.push(name);
|
|
15921
16096
|
}
|
|
15922
|
-
if (
|
|
15923
|
-
for (const entry of
|
|
16097
|
+
if (fs34.existsSync(opts.skillsRoot)) {
|
|
16098
|
+
for (const entry of fs34.readdirSync(opts.skillsRoot, { withFileTypes: true })) {
|
|
15924
16099
|
if (!entry.isDirectory() || wanted.has(entry.name))
|
|
15925
16100
|
continue;
|
|
15926
|
-
const dir =
|
|
16101
|
+
const dir = path38.join(opts.skillsRoot, entry.name);
|
|
15927
16102
|
if (!hasMarker(dir))
|
|
15928
16103
|
continue;
|
|
15929
16104
|
if (!opts.dryRun)
|
|
15930
|
-
|
|
16105
|
+
fs34.rmSync(dir, { recursive: true, force: true });
|
|
15931
16106
|
result.removed.push(entry.name);
|
|
15932
16107
|
}
|
|
15933
16108
|
}
|
|
@@ -15935,7 +16110,7 @@ function syncOpencodeSkills(opts) {
|
|
|
15935
16110
|
}
|
|
15936
16111
|
function syncShippedOpencodeSkills(opts) {
|
|
15937
16112
|
return syncOpencodeSkills({
|
|
15938
|
-
sourceSkillsDir:
|
|
16113
|
+
sourceSkillsDir: path38.join(opts.pkgRoot, "plugin", "skills"),
|
|
15939
16114
|
skillsRoot: opts.skillsRoot ?? defaultOpencodeSkillsRoot(),
|
|
15940
16115
|
dryRun: opts.dryRun
|
|
15941
16116
|
});
|
|
@@ -15943,7 +16118,7 @@ function syncShippedOpencodeSkills(opts) {
|
|
|
15943
16118
|
|
|
15944
16119
|
// packages/agents/dist/projection/claude.js
|
|
15945
16120
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
15946
|
-
import
|
|
16121
|
+
import path39 from "path";
|
|
15947
16122
|
import os15 from "os";
|
|
15948
16123
|
|
|
15949
16124
|
// packages/agents/dist/codex/app-server-client.js
|
|
@@ -15998,8 +16173,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
15998
16173
|
const info = this.opts.clientInfo ?? { name: "squadrant", version: "0" };
|
|
15999
16174
|
const id = this.nextId++;
|
|
16000
16175
|
const env = { jsonrpc: "2.0", id, method: "initialize", params: { clientInfo: info } };
|
|
16001
|
-
const res = await new Promise((
|
|
16002
|
-
this.pending.set(id, { resolve:
|
|
16176
|
+
const res = await new Promise((resolve5, reject) => {
|
|
16177
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
16003
16178
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
16004
16179
|
});
|
|
16005
16180
|
this.proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "initialized" }) + "\n");
|
|
@@ -16032,13 +16207,13 @@ var AppServerClient = class extends EventEmitter {
|
|
|
16032
16207
|
const turnId = ack?.turn?.id;
|
|
16033
16208
|
if (typeof turnId !== "string")
|
|
16034
16209
|
throw new Error(`turn/start: unexpected ack shape (no turn.id): ${JSON.stringify(ack).slice(0, 200)}`);
|
|
16035
|
-
return new Promise((
|
|
16210
|
+
return new Promise((resolve5, reject) => {
|
|
16036
16211
|
const onNote = (n) => {
|
|
16037
16212
|
if (n.params?.turn?.id !== turnId)
|
|
16038
16213
|
return;
|
|
16039
16214
|
if (n.method === "turn/completed") {
|
|
16040
16215
|
cleanup();
|
|
16041
|
-
|
|
16216
|
+
resolve5({ turnId });
|
|
16042
16217
|
}
|
|
16043
16218
|
if (n.method === "turn/failed") {
|
|
16044
16219
|
cleanup();
|
|
@@ -16089,8 +16264,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
16089
16264
|
throw new Error("AppServerClient not started");
|
|
16090
16265
|
const id = this.nextId++;
|
|
16091
16266
|
const env = { jsonrpc: "2.0", id, method, params: params ?? {} };
|
|
16092
|
-
return new Promise((
|
|
16093
|
-
this.pending.set(id, { resolve:
|
|
16267
|
+
return new Promise((resolve5, reject) => {
|
|
16268
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
16094
16269
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
16095
16270
|
});
|
|
16096
16271
|
}
|
|
@@ -16218,10 +16393,10 @@ function toSnapshot(ev2) {
|
|
|
16218
16393
|
|
|
16219
16394
|
// packages/agents/dist/codex/config.js
|
|
16220
16395
|
import { readFile as readFile6 } from "fs/promises";
|
|
16221
|
-
import { homedir as
|
|
16396
|
+
import { homedir as homedir7 } from "os";
|
|
16222
16397
|
import { join as join20 } from "path";
|
|
16223
16398
|
async function resolveCodexModel() {
|
|
16224
|
-
const home = process.env["CODEX_HOME"] ?? join20(
|
|
16399
|
+
const home = process.env["CODEX_HOME"] ?? join20(homedir7(), ".codex");
|
|
16225
16400
|
const configPath = join20(home, "config.toml");
|
|
16226
16401
|
let text;
|
|
16227
16402
|
try {
|
|
@@ -16536,11 +16711,11 @@ function buildCodexDeveloperInstructions(rec) {
|
|
|
16536
16711
|
${directive}` : directive;
|
|
16537
16712
|
}
|
|
16538
16713
|
function withTimeout(p, ms, msg) {
|
|
16539
|
-
return new Promise((
|
|
16714
|
+
return new Promise((resolve5, reject) => {
|
|
16540
16715
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
16541
16716
|
p.then((v2) => {
|
|
16542
16717
|
clearTimeout(t);
|
|
16543
|
-
|
|
16718
|
+
resolve5(v2);
|
|
16544
16719
|
}, (e) => {
|
|
16545
16720
|
clearTimeout(t);
|
|
16546
16721
|
reject(e);
|
|
@@ -16781,7 +16956,7 @@ var OpencodeSseBridge = class {
|
|
|
16781
16956
|
// packages/agents/dist/interactive/claude.js
|
|
16782
16957
|
import { execSync as execSync6 } from "child_process";
|
|
16783
16958
|
import { readFileSync as readFileSync14 } from "fs";
|
|
16784
|
-
import { homedir as
|
|
16959
|
+
import { homedir as homedir8 } from "os";
|
|
16785
16960
|
import { join as join21 } from "path";
|
|
16786
16961
|
var nextHookRequestId = Date.now();
|
|
16787
16962
|
|
|
@@ -16915,14 +17090,14 @@ function runHeadless(opts) {
|
|
|
16915
17090
|
if (err.length > ERR_CAP)
|
|
16916
17091
|
err = err.slice(err.length - ERR_CAP);
|
|
16917
17092
|
});
|
|
16918
|
-
const result = new Promise((
|
|
17093
|
+
const result = new Promise((resolve5) => {
|
|
16919
17094
|
child.once("error", (e) => {
|
|
16920
17095
|
if (debounceTimer) {
|
|
16921
17096
|
clearTimeout(debounceTimer);
|
|
16922
17097
|
debounceTimer = null;
|
|
16923
17098
|
}
|
|
16924
17099
|
opts.emit({ type: "task.failed", id: opts.id, error: `spawn error: ${e.message}`, exitCode: void 0 });
|
|
16925
|
-
|
|
17100
|
+
resolve5();
|
|
16926
17101
|
});
|
|
16927
17102
|
child.on("close", (code) => {
|
|
16928
17103
|
if (chunksSinceProgress > 0)
|
|
@@ -16939,14 +17114,14 @@ function runHeadless(opts) {
|
|
|
16939
17114
|
const ref = opts.writeResult ? opts.writeResult(opts.id, res.payload ?? "") : "";
|
|
16940
17115
|
opts.emit({ type: "task.done", id: opts.id, resultRef: ref, parseWarning: res.parseWarning });
|
|
16941
17116
|
}
|
|
16942
|
-
|
|
17117
|
+
resolve5();
|
|
16943
17118
|
});
|
|
16944
17119
|
});
|
|
16945
17120
|
return { result, kill: () => child.kill("SIGTERM") };
|
|
16946
17121
|
}
|
|
16947
17122
|
|
|
16948
17123
|
// packages/agents/dist/claude/peer-registry-source.js
|
|
16949
|
-
import { readdirSync as
|
|
17124
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
16950
17125
|
import { join as join22 } from "path";
|
|
16951
17126
|
function defaultIsAlive(pid) {
|
|
16952
17127
|
try {
|
|
@@ -16971,7 +17146,7 @@ var ClaudePeerRegistrySource = class {
|
|
|
16971
17146
|
pollMs;
|
|
16972
17147
|
log;
|
|
16973
17148
|
constructor(o = {}) {
|
|
16974
|
-
this.readdir = o.readdir ?? (() =>
|
|
17149
|
+
this.readdir = o.readdir ?? (() => readdirSync7(CLAUDE_SESSIONS_DIR));
|
|
16975
17150
|
this.readFile = o.readFile ?? ((n) => readFileSync15(join22(CLAUDE_SESSIONS_DIR, n), "utf8"));
|
|
16976
17151
|
this.isAlive = o.isAlive ?? defaultIsAlive;
|
|
16977
17152
|
this.now = o.now ?? Date.now;
|
|
@@ -17070,12 +17245,12 @@ function writeLine(socketPath, envelope, deps) {
|
|
|
17070
17245
|
error: `line exceeds the receiver's 1 MiB buffer cap (${Buffer.byteLength(line2, "utf8")} bytes)`
|
|
17071
17246
|
});
|
|
17072
17247
|
}
|
|
17073
|
-
return new Promise((
|
|
17248
|
+
return new Promise((resolve5) => {
|
|
17074
17249
|
let settled = false;
|
|
17075
17250
|
const done = (r) => {
|
|
17076
17251
|
if (!settled) {
|
|
17077
17252
|
settled = true;
|
|
17078
|
-
|
|
17253
|
+
resolve5(r);
|
|
17079
17254
|
}
|
|
17080
17255
|
};
|
|
17081
17256
|
const sock = deps.connect(socketPath);
|
|
@@ -17197,13 +17372,13 @@ var ClaudeReceiptListener = class {
|
|
|
17197
17372
|
* target is a live socket and the subsequent listen fails loudly instead.
|
|
17198
17373
|
*/
|
|
17199
17374
|
start() {
|
|
17200
|
-
return new Promise((
|
|
17375
|
+
return new Promise((resolve5, reject) => {
|
|
17201
17376
|
this.deps.unlinkStale?.(this.deps.socketPath);
|
|
17202
17377
|
this.server = this.deps.createServer((conn) => this.handle(conn));
|
|
17203
17378
|
this.server.once("error", (e) => reject(e));
|
|
17204
17379
|
this.server.listen(this.deps.socketPath, () => {
|
|
17205
17380
|
this.server.unref?.();
|
|
17206
|
-
|
|
17381
|
+
resolve5();
|
|
17207
17382
|
});
|
|
17208
17383
|
});
|
|
17209
17384
|
}
|
|
@@ -17229,15 +17404,15 @@ var ClaudeReceiptListener = class {
|
|
|
17229
17404
|
* is silent by design, so "no receipt" is the common, healthy case.
|
|
17230
17405
|
*/
|
|
17231
17406
|
waitFor(origMsgId, timeoutMs) {
|
|
17232
|
-
return new Promise((
|
|
17407
|
+
return new Promise((resolve5) => {
|
|
17233
17408
|
const timer = setTimeout(() => {
|
|
17234
17409
|
this.waiters.delete(origMsgId);
|
|
17235
|
-
|
|
17410
|
+
resolve5(void 0);
|
|
17236
17411
|
}, timeoutMs);
|
|
17237
17412
|
this.waiters.set(origMsgId, (r) => {
|
|
17238
17413
|
clearTimeout(timer);
|
|
17239
17414
|
this.waiters.delete(origMsgId);
|
|
17240
|
-
|
|
17415
|
+
resolve5(r);
|
|
17241
17416
|
});
|
|
17242
17417
|
});
|
|
17243
17418
|
}
|
|
@@ -17288,9 +17463,9 @@ var ClaudeReceiptListener = class {
|
|
|
17288
17463
|
};
|
|
17289
17464
|
|
|
17290
17465
|
// packages/agents/dist/claude/api-key-approval.js
|
|
17291
|
-
import
|
|
17466
|
+
import fs35 from "fs";
|
|
17292
17467
|
import os16 from "os";
|
|
17293
|
-
import
|
|
17468
|
+
import path40 from "path";
|
|
17294
17469
|
|
|
17295
17470
|
// packages/agents/dist/opencode/http-channel.js
|
|
17296
17471
|
init_dist2();
|
|
@@ -17382,10 +17557,10 @@ var OpencodeHttpChannel = class {
|
|
|
17382
17557
|
return cached;
|
|
17383
17558
|
const directory = this.directoryFor?.(taskId);
|
|
17384
17559
|
const createdAfterMs = this.createdAfterFor?.(taskId);
|
|
17385
|
-
for (const
|
|
17560
|
+
for (const path44 of ["/session?", "/api/session?"]) {
|
|
17386
17561
|
let res;
|
|
17387
17562
|
try {
|
|
17388
|
-
res = await this.request(`http://127.0.0.1:${port}${
|
|
17563
|
+
res = await this.request(`http://127.0.0.1:${port}${path44}`, { method: "GET" });
|
|
17389
17564
|
} catch {
|
|
17390
17565
|
return void 0;
|
|
17391
17566
|
}
|
|
@@ -17459,9 +17634,9 @@ function createOpencodeFactAdapter(deps) {
|
|
|
17459
17634
|
}
|
|
17460
17635
|
|
|
17461
17636
|
// packages/agents/dist/rules-hooks/install.js
|
|
17462
|
-
import
|
|
17637
|
+
import fs36 from "fs";
|
|
17463
17638
|
import os17 from "os";
|
|
17464
|
-
import
|
|
17639
|
+
import path41 from "path";
|
|
17465
17640
|
|
|
17466
17641
|
// packages/workspaces/dist/runtimes/cmux.js
|
|
17467
17642
|
init_dist();
|
|
@@ -17469,7 +17644,7 @@ init_dist();
|
|
|
17469
17644
|
init_dist();
|
|
17470
17645
|
init_dist2();
|
|
17471
17646
|
init_dist2();
|
|
17472
|
-
import { execFile as execFile3, execFileSync as
|
|
17647
|
+
import { execFile as execFile3, execFileSync as execFileSync9 } from "child_process";
|
|
17473
17648
|
var CMUX_TIMEOUT = 15e3;
|
|
17474
17649
|
var CmuxTimeoutError = class extends Error {
|
|
17475
17650
|
constructor(cmd) {
|
|
@@ -17478,7 +17653,7 @@ var CmuxTimeoutError = class extends Error {
|
|
|
17478
17653
|
}
|
|
17479
17654
|
};
|
|
17480
17655
|
function cmux(args) {
|
|
17481
|
-
return new Promise((
|
|
17656
|
+
return new Promise((resolve5, reject) => {
|
|
17482
17657
|
execFile3(
|
|
17483
17658
|
resolveCmuxBin(),
|
|
17484
17659
|
args,
|
|
@@ -17492,19 +17667,19 @@ function cmux(args) {
|
|
|
17492
17667
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
17493
17668
|
return;
|
|
17494
17669
|
}
|
|
17495
|
-
|
|
17670
|
+
resolve5(stdout.trim());
|
|
17496
17671
|
}
|
|
17497
17672
|
);
|
|
17498
17673
|
});
|
|
17499
17674
|
}
|
|
17500
17675
|
function cmuxStdin(args, input) {
|
|
17501
|
-
return new Promise((
|
|
17676
|
+
return new Promise((resolve5, reject) => {
|
|
17502
17677
|
const child = execFile3(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
|
|
17503
17678
|
if (err) {
|
|
17504
17679
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
17505
17680
|
return;
|
|
17506
17681
|
}
|
|
17507
|
-
|
|
17682
|
+
resolve5(stdout.trim());
|
|
17508
17683
|
});
|
|
17509
17684
|
child.stdin.end(input);
|
|
17510
17685
|
});
|
|
@@ -18143,7 +18318,7 @@ var RuntimeRegistry = class {
|
|
|
18143
18318
|
|
|
18144
18319
|
// packages/workspaces/dist/notifiers/cmux.js
|
|
18145
18320
|
init_dist();
|
|
18146
|
-
import { execFile as execFileCb, execFileSync as
|
|
18321
|
+
import { execFile as execFileCb, execFileSync as execFileSync10 } from "child_process";
|
|
18147
18322
|
import { promisify as promisify3 } from "util";
|
|
18148
18323
|
var execFile4 = promisify3(execFileCb);
|
|
18149
18324
|
function createCmuxNotifier(_scope) {
|
|
@@ -18151,7 +18326,7 @@ function createCmuxNotifier(_scope) {
|
|
|
18151
18326
|
name: "cmux",
|
|
18152
18327
|
async probe() {
|
|
18153
18328
|
try {
|
|
18154
|
-
|
|
18329
|
+
execFileSync10(resolveCmuxBin(), ["capabilities"], { encoding: "utf-8", stdio: "pipe", timeout: CMUX_TIMEOUT });
|
|
18155
18330
|
return { installed: true, reachable: true };
|
|
18156
18331
|
} catch (err) {
|
|
18157
18332
|
const code = err.code;
|
|
@@ -18199,9 +18374,9 @@ var NotifierRegistry = class {
|
|
|
18199
18374
|
};
|
|
18200
18375
|
|
|
18201
18376
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
18202
|
-
import
|
|
18203
|
-
import { existsSync as
|
|
18204
|
-
import
|
|
18377
|
+
import fs37 from "fs/promises";
|
|
18378
|
+
import { existsSync as existsSync12 } from "fs";
|
|
18379
|
+
import path42 from "path";
|
|
18205
18380
|
|
|
18206
18381
|
// packages/workspaces/dist/workspaces/registry.js
|
|
18207
18382
|
init_dist();
|
|
@@ -18273,12 +18448,12 @@ var CmuxEventsBridge = class {
|
|
|
18273
18448
|
continue;
|
|
18274
18449
|
}
|
|
18275
18450
|
this.child = child;
|
|
18276
|
-
await new Promise((
|
|
18451
|
+
await new Promise((resolve5) => {
|
|
18277
18452
|
let settled = false;
|
|
18278
18453
|
const done = () => {
|
|
18279
18454
|
if (!settled) {
|
|
18280
18455
|
settled = true;
|
|
18281
|
-
|
|
18456
|
+
resolve5();
|
|
18282
18457
|
}
|
|
18283
18458
|
};
|
|
18284
18459
|
child.stdout?.on("data", (b) => this.onData(b));
|
|
@@ -18354,16 +18529,16 @@ var CmuxEventsBridge = class {
|
|
|
18354
18529
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
18355
18530
|
init_dist2();
|
|
18356
18531
|
init_dist();
|
|
18357
|
-
import { readdirSync as
|
|
18532
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
18358
18533
|
import { join as join23 } from "path";
|
|
18359
|
-
import { homedir as
|
|
18534
|
+
import { homedir as homedir9 } from "os";
|
|
18360
18535
|
|
|
18361
18536
|
// packages/workspaces/dist/cmux-daemon/store-fingerprint.js
|
|
18362
18537
|
init_dist();
|
|
18363
18538
|
init_dist2();
|
|
18364
|
-
import { execFileSync as
|
|
18539
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
18365
18540
|
function defaultExecPs(pid) {
|
|
18366
|
-
return
|
|
18541
|
+
return execFileSync11("ps", ["-o", "command=", "-p", String(pid)], {
|
|
18367
18542
|
encoding: "utf-8",
|
|
18368
18543
|
stdio: ["ignore", "pipe", "ignore"]
|
|
18369
18544
|
});
|
|
@@ -18453,11 +18628,11 @@ function readLivenessSnapshot(files, readFile7, projects, argvRecovery = {}) {
|
|
|
18453
18628
|
|
|
18454
18629
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
18455
18630
|
async function readCmuxLiveness() {
|
|
18456
|
-
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join23(
|
|
18631
|
+
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join23(homedir9(), ".cmuxterm");
|
|
18457
18632
|
const projects = loadConfig().projects;
|
|
18458
18633
|
let files;
|
|
18459
18634
|
try {
|
|
18460
|
-
files =
|
|
18635
|
+
files = readdirSync8(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
18461
18636
|
} catch (e) {
|
|
18462
18637
|
throw new Error(`liveness: could not read cmux state dir ${dir}: ${e.message}`);
|
|
18463
18638
|
}
|
|
@@ -18525,8 +18700,8 @@ var DaemonCmux = class {
|
|
|
18525
18700
|
|
|
18526
18701
|
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
18527
18702
|
import { join as join24 } from "path";
|
|
18528
|
-
import { homedir as
|
|
18529
|
-
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";
|
|
18530
18705
|
var CmuxStoreSource = class {
|
|
18531
18706
|
name = "cmux-store";
|
|
18532
18707
|
stateDir;
|
|
@@ -18555,7 +18730,7 @@ var CmuxStoreSource = class {
|
|
|
18555
18730
|
active = false;
|
|
18556
18731
|
lastError = null;
|
|
18557
18732
|
constructor(opts = {}) {
|
|
18558
|
-
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");
|
|
18559
18734
|
this.debounceMs = opts.debounceMs ?? 50;
|
|
18560
18735
|
this.lockRetryMs = opts.lockRetryMs ?? 50;
|
|
18561
18736
|
this.maxLockRetries = opts.maxLockRetries ?? 3;
|
|
@@ -18563,7 +18738,7 @@ var CmuxStoreSource = class {
|
|
|
18563
18738
|
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive2;
|
|
18564
18739
|
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
18565
18740
|
this.readFile = opts.readFile ?? defaultReadFile;
|
|
18566
|
-
this.fileExists = opts.fileExists ??
|
|
18741
|
+
this.fileExists = opts.fileExists ?? existsSync13;
|
|
18567
18742
|
this.watchDir = opts.watchDir ?? defaultWatchDir;
|
|
18568
18743
|
this.scheduleTimer = opts.scheduleTimer ?? setTimeout;
|
|
18569
18744
|
this.cancelTimer = opts.cancelTimer ?? clearTimeout;
|
|
@@ -18746,14 +18921,14 @@ function defaultIsPidAlive2(pid) {
|
|
|
18746
18921
|
}
|
|
18747
18922
|
function defaultListFiles(dir) {
|
|
18748
18923
|
try {
|
|
18749
|
-
return
|
|
18924
|
+
return readdirSync9(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
18750
18925
|
} catch {
|
|
18751
18926
|
return [];
|
|
18752
18927
|
}
|
|
18753
18928
|
}
|
|
18754
|
-
function defaultReadFile(
|
|
18929
|
+
function defaultReadFile(path44) {
|
|
18755
18930
|
try {
|
|
18756
|
-
return readFileSync17(
|
|
18931
|
+
return readFileSync17(path44, "utf-8");
|
|
18757
18932
|
} catch {
|
|
18758
18933
|
return void 0;
|
|
18759
18934
|
}
|
|
@@ -18770,8 +18945,8 @@ function defaultWatchDir(dir, cb, onError) {
|
|
|
18770
18945
|
|
|
18771
18946
|
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
18772
18947
|
import { join as join25 } from "path";
|
|
18773
|
-
import { homedir as
|
|
18774
|
-
import { mkdirSync as
|
|
18948
|
+
import { homedir as homedir11 } from "os";
|
|
18949
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
|
|
18775
18950
|
var CLAUDE_HOOK_EVENTS = [
|
|
18776
18951
|
["SessionStart", "session-start"],
|
|
18777
18952
|
["UserPromptSubmit", "prompt-submit"],
|
|
@@ -18878,7 +19053,7 @@ function removeForeignGateHandlers(entries) {
|
|
|
18878
19053
|
return removed;
|
|
18879
19054
|
}
|
|
18880
19055
|
function installClaudeHooks(opts = {}) {
|
|
18881
|
-
const settingsPath = opts.settingsPath ?? join25(
|
|
19056
|
+
const settingsPath = opts.settingsPath ?? join25(homedir11(), ".claude", "settings.json");
|
|
18882
19057
|
const cliCmd = opts.cliCmd ?? DEFAULT_CLI_CMD;
|
|
18883
19058
|
const hookCmd = opts.hookCmd ?? `${cliCmd} hooks`;
|
|
18884
19059
|
const gateCmd = `${cliCmd} gate`;
|
|
@@ -19133,16 +19308,16 @@ function extractDetail(sub, payload) {
|
|
|
19133
19308
|
}
|
|
19134
19309
|
return void 0;
|
|
19135
19310
|
}
|
|
19136
|
-
function defaultReadFile2(
|
|
19311
|
+
function defaultReadFile2(path44) {
|
|
19137
19312
|
try {
|
|
19138
|
-
return readFileSync18(
|
|
19313
|
+
return readFileSync18(path44, "utf-8");
|
|
19139
19314
|
} catch {
|
|
19140
19315
|
return void 0;
|
|
19141
19316
|
}
|
|
19142
19317
|
}
|
|
19143
|
-
function defaultWriteFile(
|
|
19144
|
-
|
|
19145
|
-
|
|
19318
|
+
function defaultWriteFile(path44, content) {
|
|
19319
|
+
mkdirSync9(path44.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
19320
|
+
writeFileSync12(path44, content, "utf-8");
|
|
19146
19321
|
}
|
|
19147
19322
|
|
|
19148
19323
|
// packages/workspaces/dist/crew-pane.js
|
|
@@ -19227,24 +19402,24 @@ init_dist();
|
|
|
19227
19402
|
|
|
19228
19403
|
// packages/cli/src/lib/daemon-restart-broadcast.ts
|
|
19229
19404
|
init_dist();
|
|
19230
|
-
import
|
|
19231
|
-
function statePath2(
|
|
19232
|
-
return
|
|
19405
|
+
import path43 from "path";
|
|
19406
|
+
function statePath2(stateRoot2) {
|
|
19407
|
+
return path43.join(stateRoot2, "daemon-restart-state.json");
|
|
19233
19408
|
}
|
|
19234
19409
|
function computeRestartSignature(version, buildMtimeMs) {
|
|
19235
19410
|
return `${version}::${buildMtimeMs}`;
|
|
19236
19411
|
}
|
|
19237
|
-
function readPersistedRestartSignature(
|
|
19412
|
+
function readPersistedRestartSignature(stateRoot2) {
|
|
19238
19413
|
try {
|
|
19239
|
-
const raw = readConfigFileSync(statePath2(
|
|
19414
|
+
const raw = readConfigFileSync(statePath2(stateRoot2));
|
|
19240
19415
|
const data = JSON.parse(raw);
|
|
19241
19416
|
return typeof data.signature === "string" ? data.signature : null;
|
|
19242
19417
|
} catch {
|
|
19243
19418
|
return null;
|
|
19244
19419
|
}
|
|
19245
19420
|
}
|
|
19246
|
-
function writePersistedRestartSignature(
|
|
19247
|
-
writeConfigFileSync(statePath2(
|
|
19421
|
+
function writePersistedRestartSignature(stateRoot2, signature) {
|
|
19422
|
+
writeConfigFileSync(statePath2(stateRoot2), JSON.stringify({ signature }, null, 2) + "\n");
|
|
19248
19423
|
}
|
|
19249
19424
|
function restartNotice(version, isDevRebuild) {
|
|
19250
19425
|
const suffix = isDevRebuild ? " (dev build)" : "";
|
|
@@ -19265,20 +19440,20 @@ async function notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebui
|
|
|
19265
19440
|
}
|
|
19266
19441
|
async function maybeBroadcastDaemonRestart(opts) {
|
|
19267
19442
|
try {
|
|
19268
|
-
const { version, buildMtimeMs, stateRoot, config, driver, appendCaptainMessage: appendCaptainMessage2 } = opts;
|
|
19443
|
+
const { version, buildMtimeMs, stateRoot: stateRoot2, config, driver, appendCaptainMessage: appendCaptainMessage2 } = opts;
|
|
19269
19444
|
const signature = computeRestartSignature(version, buildMtimeMs);
|
|
19270
|
-
const previous = readPersistedRestartSignature(
|
|
19445
|
+
const previous = readPersistedRestartSignature(stateRoot2);
|
|
19271
19446
|
if (previous === signature) return;
|
|
19272
19447
|
const isDevRebuild = previous !== null && previous.split("::")[0] === version;
|
|
19273
19448
|
await notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild, appendCaptainMessage2);
|
|
19274
|
-
writePersistedRestartSignature(
|
|
19449
|
+
writePersistedRestartSignature(stateRoot2, signature);
|
|
19275
19450
|
} catch {
|
|
19276
19451
|
}
|
|
19277
19452
|
}
|
|
19278
19453
|
|
|
19279
19454
|
// packages/cli/src/lib/captain-channel-factory.ts
|
|
19280
19455
|
import { createServer as createServer4, connect as netConnect } from "net";
|
|
19281
|
-
import
|
|
19456
|
+
import fs38 from "fs";
|
|
19282
19457
|
import { join as join26 } from "path";
|
|
19283
19458
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
19284
19459
|
import chalk2 from "chalk";
|
|
@@ -19287,15 +19462,15 @@ var shared;
|
|
|
19287
19462
|
var registryEntryPath = () => join26(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
19288
19463
|
function unregisterSenderIdentity() {
|
|
19289
19464
|
try {
|
|
19290
|
-
|
|
19465
|
+
fs38.unlinkSync(registryEntryPath());
|
|
19291
19466
|
} catch {
|
|
19292
19467
|
}
|
|
19293
19468
|
}
|
|
19294
19469
|
function registerSenderIdentity(socketPath) {
|
|
19295
19470
|
try {
|
|
19296
19471
|
unregisterSenderIdentity();
|
|
19297
|
-
|
|
19298
|
-
|
|
19472
|
+
fs38.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
19473
|
+
fs38.writeFileSync(
|
|
19299
19474
|
registryEntryPath(),
|
|
19300
19475
|
JSON.stringify({
|
|
19301
19476
|
pid: process.pid,
|
|
@@ -19321,7 +19496,7 @@ async function sharedReceiptListener() {
|
|
|
19321
19496
|
// must never be the reason we refuse to start.
|
|
19322
19497
|
unlinkStale: (p) => {
|
|
19323
19498
|
try {
|
|
19324
|
-
|
|
19499
|
+
fs38.unlinkSync(p);
|
|
19325
19500
|
} catch {
|
|
19326
19501
|
}
|
|
19327
19502
|
},
|
|
@@ -19346,7 +19521,7 @@ async function buildCaptainChannel() {
|
|
|
19346
19521
|
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
19347
19522
|
// missed and every captain ping reported accepted-unconfirmed.
|
|
19348
19523
|
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
19349
|
-
wire: (p, e) => writeLine(p, e, { connect: (
|
|
19524
|
+
wire: (p, e) => writeLine(p, e, { connect: (path44) => netConnect(path44) }),
|
|
19350
19525
|
receipts,
|
|
19351
19526
|
newMsgId: () => randomUUID5(),
|
|
19352
19527
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
@@ -19364,8 +19539,8 @@ async function buildCaptainChannels(opts) {
|
|
|
19364
19539
|
agentFor: (project) => resolveCaptainAgent(opts.stateRoot, project, opts.configAgent)
|
|
19365
19540
|
};
|
|
19366
19541
|
}
|
|
19367
|
-
function resolveCaptainAgent(
|
|
19368
|
-
return readCaptainAddress(
|
|
19542
|
+
function resolveCaptainAgent(stateRoot2, project, configAgent) {
|
|
19543
|
+
return readCaptainAddress(stateRoot2, project)?.agent ?? configAgent;
|
|
19369
19544
|
}
|
|
19370
19545
|
async function retryForever(build, opts = {}) {
|
|
19371
19546
|
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => {
|
|
@@ -19405,7 +19580,7 @@ function readPkgVersion() {
|
|
|
19405
19580
|
}
|
|
19406
19581
|
}
|
|
19407
19582
|
var PKG_VERSION = readPkgVersion();
|
|
19408
|
-
function buildTelegramBridge(cfg,
|
|
19583
|
+
function buildTelegramBridge(cfg, stateRoot2, log, deliverInbound, usageFor) {
|
|
19409
19584
|
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
19410
19585
|
if (!token) {
|
|
19411
19586
|
log("telegram: config present but no botToken / TELEGRAM_BOT_TOKEN set \u2014 bridge disabled");
|
|
@@ -19419,7 +19594,7 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19419
19594
|
const runCommand = createRunCommand(CLI_BIN);
|
|
19420
19595
|
const sendReply = (threadId, text, replyMarkup) => client.sendMessage(cfg.supergroupId, threadId, text, replyMarkup);
|
|
19421
19596
|
const lifecycle = createInboundLifecycle({
|
|
19422
|
-
stateRoot,
|
|
19597
|
+
stateRoot: stateRoot2,
|
|
19423
19598
|
cfg: { supergroupId: cfg.supergroupId },
|
|
19424
19599
|
sendChatAction: (chatId, threadId, action) => client.sendChatAction(chatId, threadId, action),
|
|
19425
19600
|
sendReply: async (threadId, text) => {
|
|
@@ -19440,15 +19615,15 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19440
19615
|
// timestamp, so the reader hashes the screen and asks the persisted
|
|
19441
19616
|
// pending store how long that exact screen has been up. Persisted, so a
|
|
19442
19617
|
// daemon restart cannot hand a dead captain a fresh 15 minutes.
|
|
19443
|
-
noteScreen: (project, hash) => notePaneScreen(
|
|
19618
|
+
noteScreen: (project, hash) => notePaneScreen(stateRoot2, project, hash, Date.now())
|
|
19444
19619
|
}
|
|
19445
19620
|
),
|
|
19446
19621
|
log
|
|
19447
19622
|
});
|
|
19448
19623
|
return createTelegramBridge({
|
|
19449
19624
|
cfg,
|
|
19450
|
-
stateRoot,
|
|
19451
|
-
configRoot:
|
|
19625
|
+
stateRoot: stateRoot2,
|
|
19626
|
+
configRoot: CONFIG_DIR,
|
|
19452
19627
|
client,
|
|
19453
19628
|
appendCaptainMessage,
|
|
19454
19629
|
log,
|
|
@@ -19464,7 +19639,7 @@ function buildTelegramBridge(cfg, stateRoot, log, deliverInbound, usageFor) {
|
|
|
19464
19639
|
// every link on the machine. `undefined` correctly means "don't prune".
|
|
19465
19640
|
registeredProjects: () => {
|
|
19466
19641
|
try {
|
|
19467
|
-
const parsed = JSON.parse(readFileSync19(
|
|
19642
|
+
const parsed = JSON.parse(readFileSync19(DEFAULT_CONFIG_PATH, "utf8"));
|
|
19468
19643
|
return Object.keys(parsed.projects ?? {});
|
|
19469
19644
|
} catch {
|
|
19470
19645
|
return void 0;
|
|
@@ -19504,8 +19679,18 @@ function startSquadrantd(opts = {}) {
|
|
|
19504
19679
|
`
|
|
19505
19680
|
);
|
|
19506
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
|
+
}
|
|
19507
19692
|
const ctx = buildContext(opts);
|
|
19508
|
-
const { stateRoot, store, log, spawn: spawn2, writeResult, inFlightHeadlessIds, activeHeadlessKills } = ctx;
|
|
19693
|
+
const { stateRoot: stateRoot2, store, log, spawn: spawn2, writeResult, inFlightHeadlessIds, activeHeadlessKills } = ctx;
|
|
19509
19694
|
const { broadcast, schedulePromotion, cancelPromotionsFor } = createAttach(ctx);
|
|
19510
19695
|
ctx.broadcast = broadcast;
|
|
19511
19696
|
ctx.schedulePromotion = schedulePromotion;
|
|
@@ -19581,7 +19766,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19581
19766
|
const rec = store.listAll().find((r) => r.id === taskId);
|
|
19582
19767
|
if (!rec) return false;
|
|
19583
19768
|
try {
|
|
19584
|
-
const cfg2 = JSON.parse(readFileSync19(join27(
|
|
19769
|
+
const cfg2 = JSON.parse(readFileSync19(join27(stateRoot2, rec.project, taskId, "opencode.json"), "utf8"));
|
|
19585
19770
|
return cfg2?.permission?.bash === "ask" && cfg2?.permission?.edit === "ask";
|
|
19586
19771
|
} catch {
|
|
19587
19772
|
return false;
|
|
@@ -19601,7 +19786,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19601
19786
|
(r) => r.mode === "interactive" && !TERMINAL_STATES.has(r.state) && r.cwd === hook.cwd
|
|
19602
19787
|
);
|
|
19603
19788
|
},
|
|
19604
|
-
cursorFile: join27(
|
|
19789
|
+
cursorFile: join27(stateRoot2, "cmux-events.seq"),
|
|
19605
19790
|
log
|
|
19606
19791
|
});
|
|
19607
19792
|
const cmuxStoreSource = new CmuxStoreSource({ log });
|
|
@@ -19631,7 +19816,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19631
19816
|
const tgCfg = loadConfig().telegram;
|
|
19632
19817
|
ctx.telegramBridge = opts.telegramBridge ?? (tgCfg && !process.env.VITEST ? buildTelegramBridge(
|
|
19633
19818
|
tgCfg,
|
|
19634
|
-
|
|
19819
|
+
stateRoot2,
|
|
19635
19820
|
log,
|
|
19636
19821
|
(project, text) => Promise.resolve().then(() => (init_dist2(), dist_exports)).then((core2) => core2.deliverToCaptain(project, text, {
|
|
19637
19822
|
channel: ctx.captainChannel,
|
|
@@ -19650,7 +19835,7 @@ function startSquadrantd(opts = {}) {
|
|
|
19650
19835
|
ctx.captainChannelMode = () => resolveCaptainChannelMode(loadConfig().defaults);
|
|
19651
19836
|
if (ctx.captainChannelMode() !== "off") {
|
|
19652
19837
|
void buildCaptainChannelsWithRetry({
|
|
19653
|
-
stateRoot:
|
|
19838
|
+
stateRoot: ctx.stateRoot,
|
|
19654
19839
|
configAgent: loadConfig().defaults.roles?.captain?.agent,
|
|
19655
19840
|
log
|
|
19656
19841
|
}).then(({ channels, agentFor }) => {
|
|
@@ -19865,10 +20050,10 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
19865
20050
|
void maybeBroadcastDaemonRestart({
|
|
19866
20051
|
version: PKG_VERSION,
|
|
19867
20052
|
buildMtimeMs,
|
|
19868
|
-
stateRoot,
|
|
20053
|
+
stateRoot: stateRoot2,
|
|
19869
20054
|
config: restartConfig,
|
|
19870
20055
|
driver: registry.global(restartConfig),
|
|
19871
|
-
appendCaptainMessage: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
|
|
20056
|
+
appendCaptainMessage: (project, text) => appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" })
|
|
19872
20057
|
});
|
|
19873
20058
|
} catch (e) {
|
|
19874
20059
|
log(`daemon-restart broadcast setup failed: ${e.message}`);
|
|
@@ -19904,8 +20089,8 @@ function logCrashMarker(kind, err) {
|
|
|
19904
20089
|
const message = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
19905
20090
|
process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${kind} pid=${process.pid} error=${message}
|
|
19906
20091
|
`);
|
|
19907
|
-
const
|
|
19908
|
-
writeExitMarker(
|
|
20092
|
+
const stateRoot2 = stateRoot();
|
|
20093
|
+
writeExitMarker(stateRoot2, {
|
|
19909
20094
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19910
20095
|
pid: process.pid,
|
|
19911
20096
|
reason: kind,
|
|
@@ -19916,8 +20101,8 @@ function logCrashMarker(kind, err) {
|
|
|
19916
20101
|
}, (m) => process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${m}
|
|
19917
20102
|
`));
|
|
19918
20103
|
}
|
|
19919
|
-
function isMonorepoCheckout(scriptPath, dirExists =
|
|
19920
|
-
return dirExists(join27(dirname6(
|
|
20104
|
+
function isMonorepoCheckout(scriptPath, dirExists = existsSync14) {
|
|
20105
|
+
return dirExists(join27(dirname6(resolve4(scriptPath)), "..", "packages"));
|
|
19921
20106
|
}
|
|
19922
20107
|
function isLinkedWorktree(scriptPath, statFile = (p) => {
|
|
19923
20108
|
try {
|
|
@@ -19926,7 +20111,7 @@ function isLinkedWorktree(scriptPath, statFile = (p) => {
|
|
|
19926
20111
|
return void 0;
|
|
19927
20112
|
}
|
|
19928
20113
|
}) {
|
|
19929
|
-
const dotGit = join27(dirname6(
|
|
20114
|
+
const dotGit = join27(dirname6(resolve4(scriptPath)), "..", ".git");
|
|
19930
20115
|
return statFile(dotGit)?.isFile === true;
|
|
19931
20116
|
}
|
|
19932
20117
|
if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
@@ -19953,13 +20138,13 @@ if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
|
19953
20138
|
sweepMs: 3e4,
|
|
19954
20139
|
// #898: KB reconcile scheduler on the 60s tick. Enqueues to the home captain's mailbox; never spawns.
|
|
19955
20140
|
knowledgeTick: async () => {
|
|
19956
|
-
const
|
|
20141
|
+
const stateRoot2 = stateRoot();
|
|
19957
20142
|
await tickKnowledgeSchedule({
|
|
19958
20143
|
cfg: loadConfig(),
|
|
19959
|
-
stateRoot,
|
|
20144
|
+
stateRoot: stateRoot2,
|
|
19960
20145
|
log: (m) => process.stderr.write(`[squadrantd] ${m}
|
|
19961
20146
|
`),
|
|
19962
|
-
enqueue: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
|
|
20147
|
+
enqueue: (project, text) => appendCaptainMessage({ stateRoot: stateRoot2, project, text, source: "daemon" })
|
|
19963
20148
|
});
|
|
19964
20149
|
}
|
|
19965
20150
|
});
|