squadrant 0.14.3 → 0.15.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 +591 -217
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +425 -91
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,11 +168,16 @@ var init_project_config = __esm({
|
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
// packages/shared/dist/effort.js
|
|
171
|
-
function resolveEffort(config) {
|
|
171
|
+
function resolveEffort(config, projectName, projectConfigRoot) {
|
|
172
|
+
if (projectName) {
|
|
173
|
+
const override = loadProjectOverride(projectName, projectConfigRoot);
|
|
174
|
+
return override.effort ?? config.defaults.effort ?? "balance";
|
|
175
|
+
}
|
|
172
176
|
return config.defaults.effort ?? "balance";
|
|
173
177
|
}
|
|
174
178
|
var init_effort = __esm({
|
|
175
179
|
"packages/shared/dist/effort.js"() {
|
|
180
|
+
init_project_config();
|
|
176
181
|
}
|
|
177
182
|
});
|
|
178
183
|
|
|
@@ -182,6 +187,12 @@ var init_runtime = __esm({
|
|
|
182
187
|
}
|
|
183
188
|
});
|
|
184
189
|
|
|
190
|
+
// packages/shared/dist/types/liveness.js
|
|
191
|
+
var init_liveness = __esm({
|
|
192
|
+
"packages/shared/dist/types/liveness.js"() {
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
185
196
|
// packages/shared/dist/types/control.js
|
|
186
197
|
var TERMINAL_STATES;
|
|
187
198
|
var init_control = __esm({
|
|
@@ -1040,6 +1051,7 @@ var init_dist = __esm({
|
|
|
1040
1051
|
init_project_config();
|
|
1041
1052
|
init_effort();
|
|
1042
1053
|
init_runtime();
|
|
1054
|
+
init_liveness();
|
|
1043
1055
|
init_control();
|
|
1044
1056
|
init_projection();
|
|
1045
1057
|
init_workspaces();
|
|
@@ -1202,7 +1214,7 @@ function healCmdFor(c) {
|
|
|
1202
1214
|
return null;
|
|
1203
1215
|
}
|
|
1204
1216
|
var CREW_STALE_MS, CREW_GONE_MS;
|
|
1205
|
-
var
|
|
1217
|
+
var init_liveness2 = __esm({
|
|
1206
1218
|
"packages/core/dist/liveness.js"() {
|
|
1207
1219
|
CREW_STALE_MS = 5 * 6e4;
|
|
1208
1220
|
CREW_GONE_MS = 30 * 6e4;
|
|
@@ -1422,15 +1434,24 @@ var init_gate = __esm({
|
|
|
1422
1434
|
}
|
|
1423
1435
|
});
|
|
1424
1436
|
|
|
1437
|
+
// packages/core/dist/daemon/liveness-registry.js
|
|
1438
|
+
import { writeFileSync as writeFileSync6, readFileSync as readFileSync7, renameSync as renameSync2 } from "fs";
|
|
1439
|
+
var init_liveness_registry = __esm({
|
|
1440
|
+
"packages/core/dist/daemon/liveness-registry.js"() {
|
|
1441
|
+
init_liveness2();
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1425
1445
|
// packages/core/dist/daemon/context.js
|
|
1426
1446
|
import { homedir as homedir5 } from "os";
|
|
1427
1447
|
import { join as join8 } from "path";
|
|
1428
1448
|
import { spawn as realSpawn } from "child_process";
|
|
1429
|
-
import { writeFileSync as
|
|
1449
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
1430
1450
|
var init_context = __esm({
|
|
1431
1451
|
"packages/core/dist/daemon/context.js"() {
|
|
1432
1452
|
init_store();
|
|
1433
1453
|
init_dist();
|
|
1454
|
+
init_liveness_registry();
|
|
1434
1455
|
}
|
|
1435
1456
|
});
|
|
1436
1457
|
|
|
@@ -1489,6 +1510,7 @@ var init_delivery_loop = __esm({
|
|
|
1489
1510
|
init_captain_delivery();
|
|
1490
1511
|
init_dist();
|
|
1491
1512
|
init_interactive_probe();
|
|
1513
|
+
init_liveness2();
|
|
1492
1514
|
}
|
|
1493
1515
|
});
|
|
1494
1516
|
|
|
@@ -1508,7 +1530,7 @@ var init_server = __esm({
|
|
|
1508
1530
|
// packages/core/dist/daemon/snapshot-gather.js
|
|
1509
1531
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1510
1532
|
import { join as join9 } from "path";
|
|
1511
|
-
import { statSync as statSync2, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync2, readFileSync as
|
|
1533
|
+
import { statSync as statSync2, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
1512
1534
|
var SELF_PATH;
|
|
1513
1535
|
var init_snapshot_gather = __esm({
|
|
1514
1536
|
"packages/core/dist/daemon/snapshot-gather.js"() {
|
|
@@ -1528,7 +1550,7 @@ var init_start = __esm({
|
|
|
1528
1550
|
init_gates();
|
|
1529
1551
|
init_server();
|
|
1530
1552
|
init_mailbox();
|
|
1531
|
-
|
|
1553
|
+
init_liveness2();
|
|
1532
1554
|
init_dist();
|
|
1533
1555
|
init_snapshot_gather();
|
|
1534
1556
|
SNAPSHOT_LOG_WINDOW_MS = 60 * 60 * 1e3;
|
|
@@ -2165,7 +2187,7 @@ async function isCaptainAlive(project, sockPath = DEFAULT_SOCK_PATH2) {
|
|
|
2165
2187
|
try {
|
|
2166
2188
|
const health = await sendRequest(sockPath, { kind: "health", project }, 5e3);
|
|
2167
2189
|
const captain = health?.find((h) => h.kind === "captain" && h.project === project);
|
|
2168
|
-
return captain
|
|
2190
|
+
return captain?.state === "alive";
|
|
2169
2191
|
} catch {
|
|
2170
2192
|
return false;
|
|
2171
2193
|
}
|
|
@@ -2787,7 +2809,7 @@ var init_dist2 = __esm({
|
|
|
2787
2809
|
init_mailbox();
|
|
2788
2810
|
init_protocol();
|
|
2789
2811
|
init_state_machine();
|
|
2790
|
-
|
|
2812
|
+
init_liveness2();
|
|
2791
2813
|
init_watchdog();
|
|
2792
2814
|
init_store();
|
|
2793
2815
|
init_snapshot();
|
|
@@ -3661,11 +3683,81 @@ var init_events_bridge = __esm({
|
|
|
3661
3683
|
}
|
|
3662
3684
|
});
|
|
3663
3685
|
|
|
3686
|
+
// packages/workspaces/dist/cmux-daemon/store-fingerprint.js
|
|
3687
|
+
function roleFromTemplate(args) {
|
|
3688
|
+
const i = args?.indexOf("--append-system-prompt-file") ?? -1;
|
|
3689
|
+
const tmpl = i >= 0 && args ? (args[i + 1] ?? "").split("/").pop() ?? "" : "";
|
|
3690
|
+
if (tmpl.startsWith("captain"))
|
|
3691
|
+
return "captain";
|
|
3692
|
+
if (tmpl.startsWith("crew"))
|
|
3693
|
+
return "crew";
|
|
3694
|
+
if (tmpl.startsWith("command"))
|
|
3695
|
+
return "command";
|
|
3696
|
+
return "unknown";
|
|
3697
|
+
}
|
|
3698
|
+
function projectFromCwd(cwd, projects) {
|
|
3699
|
+
for (const [name, p] of Object.entries(projects)) {
|
|
3700
|
+
const projPath = resolveHome(p.path);
|
|
3701
|
+
if (cwd === projPath || cwd.startsWith(`${projPath}/`))
|
|
3702
|
+
return name;
|
|
3703
|
+
}
|
|
3704
|
+
return void 0;
|
|
3705
|
+
}
|
|
3706
|
+
function parseStoreRecords(fileContent, projects) {
|
|
3707
|
+
let parsed;
|
|
3708
|
+
try {
|
|
3709
|
+
parsed = JSON.parse(fileContent);
|
|
3710
|
+
} catch (e) {
|
|
3711
|
+
throw new Error(`parseStoreRecords: invalid JSON: ${e.message}`);
|
|
3712
|
+
}
|
|
3713
|
+
const out = [];
|
|
3714
|
+
for (const s of Object.values(parsed.sessions ?? {})) {
|
|
3715
|
+
const cwd = s.cwd ?? s.launchCommand?.workingDirectory ?? "";
|
|
3716
|
+
const project = projectFromCwd(cwd, projects);
|
|
3717
|
+
if (!project || !s.sessionId)
|
|
3718
|
+
continue;
|
|
3719
|
+
out.push({
|
|
3720
|
+
role: roleFromTemplate(s.launchCommand?.arguments),
|
|
3721
|
+
project,
|
|
3722
|
+
pid: typeof s.pid === "number" ? s.pid : null,
|
|
3723
|
+
sessionId: s.sessionId,
|
|
3724
|
+
present: true,
|
|
3725
|
+
isRestorable: s.isRestorable
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
return out;
|
|
3729
|
+
}
|
|
3730
|
+
function readLivenessSnapshot(files, readFile6, projects) {
|
|
3731
|
+
const out = [];
|
|
3732
|
+
let successes = 0;
|
|
3733
|
+
for (const f of files) {
|
|
3734
|
+
try {
|
|
3735
|
+
out.push(...parseStoreRecords(readFile6(f), projects));
|
|
3736
|
+
successes++;
|
|
3737
|
+
} catch {
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
if (files.length > 0 && successes === 0) {
|
|
3741
|
+
throw new Error(`readLivenessSnapshot: all ${files.length} store file(s) unreadable/corrupt this tick`);
|
|
3742
|
+
}
|
|
3743
|
+
return out;
|
|
3744
|
+
}
|
|
3745
|
+
var init_store_fingerprint = __esm({
|
|
3746
|
+
"packages/workspaces/dist/cmux-daemon/store-fingerprint.js"() {
|
|
3747
|
+
init_dist();
|
|
3748
|
+
}
|
|
3749
|
+
});
|
|
3750
|
+
|
|
3664
3751
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
3752
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
3753
|
+
import { join as join13 } from "path";
|
|
3754
|
+
import { homedir as homedir8 } from "os";
|
|
3665
3755
|
var DaemonCmux;
|
|
3666
3756
|
var init_daemon_cmux = __esm({
|
|
3667
3757
|
"packages/workspaces/dist/cmux-daemon/daemon-cmux.js"() {
|
|
3668
3758
|
init_dist2();
|
|
3759
|
+
init_dist();
|
|
3760
|
+
init_store_fingerprint();
|
|
3669
3761
|
DaemonCmux = class {
|
|
3670
3762
|
driver;
|
|
3671
3763
|
constructor(driver) {
|
|
@@ -3716,14 +3808,30 @@ var init_daemon_cmux = __esm({
|
|
|
3716
3808
|
return false;
|
|
3717
3809
|
}
|
|
3718
3810
|
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Ground-truth liveness from cmux's own hook-sessions store (§5.4).
|
|
3813
|
+
* THROWS (does not return []) when the dir can't be listed, or every store
|
|
3814
|
+
* file failed to read/parse — see the class doc above.
|
|
3815
|
+
*/
|
|
3816
|
+
async liveness() {
|
|
3817
|
+
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join13(homedir8(), ".cmuxterm");
|
|
3818
|
+
const projects = loadConfig().projects;
|
|
3819
|
+
let files;
|
|
3820
|
+
try {
|
|
3821
|
+
files = readdirSync3(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
3822
|
+
} catch (e) {
|
|
3823
|
+
throw new Error(`liveness: could not read cmux state dir ${dir}: ${e.message}`);
|
|
3824
|
+
}
|
|
3825
|
+
return readLivenessSnapshot(files, (f) => readFileSync9(join13(dir, f), "utf-8"), projects);
|
|
3826
|
+
}
|
|
3719
3827
|
};
|
|
3720
3828
|
}
|
|
3721
3829
|
});
|
|
3722
3830
|
|
|
3723
3831
|
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
3724
|
-
import { join as
|
|
3725
|
-
import { homedir as
|
|
3726
|
-
import { watch, readdirSync as
|
|
3832
|
+
import { join as join14 } from "path";
|
|
3833
|
+
import { homedir as homedir9 } from "os";
|
|
3834
|
+
import { watch, readdirSync as readdirSync4, readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
3727
3835
|
function parseLifecycleState(s) {
|
|
3728
3836
|
if (s === "running" || s === "idle" || s === "needsInput" || s === "unknown") {
|
|
3729
3837
|
return s;
|
|
@@ -3740,14 +3848,14 @@ function defaultIsPidAlive(pid) {
|
|
|
3740
3848
|
}
|
|
3741
3849
|
function defaultListFiles(dir) {
|
|
3742
3850
|
try {
|
|
3743
|
-
return
|
|
3851
|
+
return readdirSync4(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
3744
3852
|
} catch {
|
|
3745
3853
|
return [];
|
|
3746
3854
|
}
|
|
3747
3855
|
}
|
|
3748
3856
|
function defaultReadFile(path29) {
|
|
3749
3857
|
try {
|
|
3750
|
-
return
|
|
3858
|
+
return readFileSync10(path29, "utf-8");
|
|
3751
3859
|
} catch {
|
|
3752
3860
|
return void 0;
|
|
3753
3861
|
}
|
|
@@ -3783,7 +3891,7 @@ var init_cmux_store_source = __esm({
|
|
|
3783
3891
|
active = false;
|
|
3784
3892
|
lastError = null;
|
|
3785
3893
|
constructor(opts = {}) {
|
|
3786
|
-
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ??
|
|
3894
|
+
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join14(homedir9(), ".cmuxterm");
|
|
3787
3895
|
this.debounceMs = opts.debounceMs ?? 50;
|
|
3788
3896
|
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
3789
3897
|
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
@@ -3845,7 +3953,7 @@ var init_cmux_store_source = __esm({
|
|
|
3845
3953
|
}
|
|
3846
3954
|
scanFile(filename) {
|
|
3847
3955
|
const deps = this.deps;
|
|
3848
|
-
const filePath =
|
|
3956
|
+
const filePath = join14(this.stateDir, filename);
|
|
3849
3957
|
const lockPath = `${filePath}.lock`;
|
|
3850
3958
|
if (this.fileExists(lockPath)) {
|
|
3851
3959
|
this.log(`cmux-store: skipping ${filename} (locked)`);
|
|
@@ -3899,11 +4007,11 @@ var init_cmux_store_source = __esm({
|
|
|
3899
4007
|
});
|
|
3900
4008
|
|
|
3901
4009
|
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
3902
|
-
import { join as
|
|
3903
|
-
import { homedir as
|
|
3904
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
4010
|
+
import { join as join15 } from "path";
|
|
4011
|
+
import { homedir as homedir10 } from "os";
|
|
4012
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
3905
4013
|
function installClaudeHooks(opts = {}) {
|
|
3906
|
-
const settingsPath = opts.settingsPath ??
|
|
4014
|
+
const settingsPath = opts.settingsPath ?? join15(homedir10(), ".claude", "settings.json");
|
|
3907
4015
|
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
3908
4016
|
const readFile6 = opts.readFile ?? defaultReadFile2;
|
|
3909
4017
|
const writeFile5 = opts.writeFile ?? defaultWriteFile;
|
|
@@ -3977,14 +4085,14 @@ function extractDetail(sub, payload) {
|
|
|
3977
4085
|
}
|
|
3978
4086
|
function defaultReadFile2(path29) {
|
|
3979
4087
|
try {
|
|
3980
|
-
return
|
|
4088
|
+
return readFileSync11(path29, "utf-8");
|
|
3981
4089
|
} catch {
|
|
3982
4090
|
return void 0;
|
|
3983
4091
|
}
|
|
3984
4092
|
}
|
|
3985
4093
|
function defaultWriteFile(path29, content) {
|
|
3986
4094
|
mkdirSync6(path29.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
3987
|
-
|
|
4095
|
+
writeFileSync8(path29, content, "utf-8");
|
|
3988
4096
|
}
|
|
3989
4097
|
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, NativeHookSource;
|
|
3990
4098
|
var init_native_hook_source = __esm({
|
|
@@ -5313,11 +5421,11 @@ var init_codex_app_server_source = __esm({
|
|
|
5313
5421
|
|
|
5314
5422
|
// packages/agents/dist/codex/config.js
|
|
5315
5423
|
import { readFile as readFile5 } from "fs/promises";
|
|
5316
|
-
import { homedir as
|
|
5317
|
-
import { join as
|
|
5424
|
+
import { homedir as homedir12 } from "os";
|
|
5425
|
+
import { join as join17 } from "path";
|
|
5318
5426
|
async function resolveCodexModel() {
|
|
5319
|
-
const home = process.env["CODEX_HOME"] ??
|
|
5320
|
-
const configPath =
|
|
5427
|
+
const home = process.env["CODEX_HOME"] ?? join17(homedir12(), ".codex");
|
|
5428
|
+
const configPath = join17(home, "config.toml");
|
|
5321
5429
|
let text;
|
|
5322
5430
|
try {
|
|
5323
5431
|
text = await readFile5(configPath, "utf8");
|
|
@@ -5838,9 +5946,9 @@ var init_sse_bridge = __esm({
|
|
|
5838
5946
|
|
|
5839
5947
|
// packages/agents/dist/interactive/claude.js
|
|
5840
5948
|
import { execSync as execSync7 } from "child_process";
|
|
5841
|
-
import { readFileSync as
|
|
5842
|
-
import { homedir as
|
|
5843
|
-
import { join as
|
|
5949
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
5950
|
+
import { homedir as homedir13 } from "os";
|
|
5951
|
+
import { join as join18 } from "path";
|
|
5844
5952
|
function probeClaudeSettingsFlag() {
|
|
5845
5953
|
try {
|
|
5846
5954
|
const help = execSync7("claude --help", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -5891,11 +5999,11 @@ function deriveTranscriptPath(sessionId, cwd) {
|
|
|
5891
5999
|
if (!sessionId || !cwd)
|
|
5892
6000
|
return null;
|
|
5893
6001
|
const escaped = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5894
|
-
return
|
|
6002
|
+
return join18(homedir13(), ".claude", "projects", escaped, `${sessionId}.jsonl`);
|
|
5895
6003
|
}
|
|
5896
6004
|
function readLastAssistantText(transcriptPath) {
|
|
5897
6005
|
try {
|
|
5898
|
-
const raw =
|
|
6006
|
+
const raw = readFileSync12(transcriptPath, "utf-8");
|
|
5899
6007
|
const lines = raw.split(/\r?\n/);
|
|
5900
6008
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5901
6009
|
const line = lines[i].trim();
|
|
@@ -6362,10 +6470,10 @@ var init_dist4 = __esm({
|
|
|
6362
6470
|
init_dist();
|
|
6363
6471
|
init_dist2();
|
|
6364
6472
|
import { Command as Command31 } from "commander";
|
|
6365
|
-
import { readFileSync as
|
|
6473
|
+
import { readFileSync as readFileSync15, existsSync as existsSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
6366
6474
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
6367
|
-
import { dirname as
|
|
6368
|
-
import { homedir as
|
|
6475
|
+
import { dirname as dirname8, join as join27 } from "path";
|
|
6476
|
+
import { homedir as homedir20 } from "os";
|
|
6369
6477
|
|
|
6370
6478
|
// packages/cli/src/commands/doctor.ts
|
|
6371
6479
|
init_dist();
|
|
@@ -6382,10 +6490,10 @@ import chalk3 from "chalk";
|
|
|
6382
6490
|
// packages/cli/src/commands/health-view.ts
|
|
6383
6491
|
init_dist2();
|
|
6384
6492
|
init_dist2();
|
|
6385
|
-
import { homedir as
|
|
6386
|
-
import { join as
|
|
6493
|
+
import { homedir as homedir11 } from "os";
|
|
6494
|
+
import { join as join16 } from "path";
|
|
6387
6495
|
import chalk2 from "chalk";
|
|
6388
|
-
var SOCK =
|
|
6496
|
+
var SOCK = join16(homedir11(), ".config", "squadrant", "squadrant.sock");
|
|
6389
6497
|
async function queryHealth(project) {
|
|
6390
6498
|
try {
|
|
6391
6499
|
const reply = await sendRequest(SOCK, { kind: "health", project });
|
|
@@ -6715,13 +6823,161 @@ ${passed === total ? chalk3.green("All checks passed") : chalk3.yellow(`${passed
|
|
|
6715
6823
|
init_dist();
|
|
6716
6824
|
init_dist3();
|
|
6717
6825
|
init_dist();
|
|
6718
|
-
init_dist4();
|
|
6719
6826
|
import { Command as Command2 } from "commander";
|
|
6720
6827
|
import fs16 from "fs";
|
|
6721
6828
|
import path18 from "path";
|
|
6722
6829
|
import os9 from "os";
|
|
6723
6830
|
import readline from "readline";
|
|
6724
6831
|
import chalk4 from "chalk";
|
|
6832
|
+
|
|
6833
|
+
// packages/cli/src/lib/per-crew-settings.ts
|
|
6834
|
+
init_dist4();
|
|
6835
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
6836
|
+
import { dirname as dirname5, join as join19 } from "path";
|
|
6837
|
+
import { homedir as homedir14 } from "os";
|
|
6838
|
+
var CREW_PERMISSION_ALLOWLIST = [
|
|
6839
|
+
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
6840
|
+
"Bash(git status:*)",
|
|
6841
|
+
"Bash(git add:*)",
|
|
6842
|
+
"Bash(git commit:*)",
|
|
6843
|
+
"Bash(git push:*)",
|
|
6844
|
+
"Bash(git pull:*)",
|
|
6845
|
+
"Bash(git fetch:*)",
|
|
6846
|
+
"Bash(git checkout:*)",
|
|
6847
|
+
"Bash(git branch:*)",
|
|
6848
|
+
"Bash(git diff:*)",
|
|
6849
|
+
"Bash(git log:*)",
|
|
6850
|
+
"Bash(git stash:*)",
|
|
6851
|
+
"Bash(git restore:*)",
|
|
6852
|
+
// git — read-only queries (no-mutation subcommands)
|
|
6853
|
+
"Bash(git show:*)",
|
|
6854
|
+
"Bash(git rev-parse:*)",
|
|
6855
|
+
"Bash(git remote -v:*)",
|
|
6856
|
+
"Bash(git stash list:*)",
|
|
6857
|
+
// npm / npx — installs + script/test/build runners
|
|
6858
|
+
"Bash(npm install:*)",
|
|
6859
|
+
"Bash(npm ci:*)",
|
|
6860
|
+
"Bash(npm run:*)",
|
|
6861
|
+
"Bash(npm test:*)",
|
|
6862
|
+
"Bash(npm ls:*)",
|
|
6863
|
+
"Bash(npm view:*)",
|
|
6864
|
+
"Bash(npx vitest:*)",
|
|
6865
|
+
"Bash(npx tsc:*)",
|
|
6866
|
+
// direct runners
|
|
6867
|
+
"Bash(node:*)",
|
|
6868
|
+
"Bash(node --version:*)",
|
|
6869
|
+
"Bash(vitest:*)",
|
|
6870
|
+
"Bash(tsc:*)",
|
|
6871
|
+
// pnpm
|
|
6872
|
+
"Bash(pnpm ls:*)",
|
|
6873
|
+
// process / system read — non-interactive, read-only
|
|
6874
|
+
"Bash(ps:*)",
|
|
6875
|
+
"Bash(pgrep:*)",
|
|
6876
|
+
"Bash(lsof:*)",
|
|
6877
|
+
"Bash(top -l:*)",
|
|
6878
|
+
"Bash(uptime:*)",
|
|
6879
|
+
"Bash(whoami:*)",
|
|
6880
|
+
"Bash(uname:*)",
|
|
6881
|
+
"Bash(date:*)",
|
|
6882
|
+
"Bash(env:*)",
|
|
6883
|
+
"Bash(sysctl:*)",
|
|
6884
|
+
// file read / navigation
|
|
6885
|
+
"Bash(ls:*)",
|
|
6886
|
+
"Bash(cat:*)",
|
|
6887
|
+
"Bash(head:*)",
|
|
6888
|
+
"Bash(tail:*)",
|
|
6889
|
+
"Bash(wc:*)",
|
|
6890
|
+
"Bash(grep:*)",
|
|
6891
|
+
"Bash(rg:*)",
|
|
6892
|
+
"Bash(find:*)",
|
|
6893
|
+
"Bash(pwd:*)",
|
|
6894
|
+
"Bash(stat:*)",
|
|
6895
|
+
"Bash(file:*)",
|
|
6896
|
+
"Bash(realpath:*)",
|
|
6897
|
+
"Bash(basename:*)",
|
|
6898
|
+
"Bash(dirname:*)",
|
|
6899
|
+
"Bash(du:*)",
|
|
6900
|
+
"Bash(df:*)",
|
|
6901
|
+
"Bash(tree:*)",
|
|
6902
|
+
// read-only text processing
|
|
6903
|
+
"Bash(sort:*)",
|
|
6904
|
+
"Bash(uniq:*)",
|
|
6905
|
+
"Bash(cut:*)",
|
|
6906
|
+
"Bash(tr:*)",
|
|
6907
|
+
"Bash(jq:*)",
|
|
6908
|
+
"Bash(diff:*)",
|
|
6909
|
+
"Bash(column:*)",
|
|
6910
|
+
// harmless utilities
|
|
6911
|
+
"Bash(echo:*)",
|
|
6912
|
+
"Bash(which:*)",
|
|
6913
|
+
"Bash(true:*)",
|
|
6914
|
+
"Bash(sleep:*)",
|
|
6915
|
+
"Bash(printf:*)"
|
|
6916
|
+
];
|
|
6917
|
+
function healStaleCockpitRefs(raw) {
|
|
6918
|
+
return raw.replaceAll("cockpit crew _hook", "squadrant crew _hook").replaceAll(".config/cockpit", ".config/squadrant").replaceAll("cockpit-hub", "squadrant-hub");
|
|
6919
|
+
}
|
|
6920
|
+
function mergeCrewPermissions(settings) {
|
|
6921
|
+
const next = structuredClone(settings ?? {});
|
|
6922
|
+
const permissions = next.permissions ??= {};
|
|
6923
|
+
const existing = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
6924
|
+
permissions.allow = [.../* @__PURE__ */ new Set([...existing, ...CREW_PERMISSION_ALLOWLIST])];
|
|
6925
|
+
return next;
|
|
6926
|
+
}
|
|
6927
|
+
function writePerCrewSettingsLocal(o) {
|
|
6928
|
+
const dir = join19(o.projectCwd, ".claude");
|
|
6929
|
+
mkdirSync7(dir, { recursive: true });
|
|
6930
|
+
const file = join19(dir, "settings.local.json");
|
|
6931
|
+
let existing = {};
|
|
6932
|
+
try {
|
|
6933
|
+
const raw = healStaleCockpitRefs(readFileSync13(file, "utf-8"));
|
|
6934
|
+
existing = JSON.parse(raw);
|
|
6935
|
+
} catch {
|
|
6936
|
+
}
|
|
6937
|
+
const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
|
|
6938
|
+
const merged = mergeCrewPermissions(withHooks);
|
|
6939
|
+
writeFileSync9(file, JSON.stringify(merged, null, 2));
|
|
6940
|
+
return file;
|
|
6941
|
+
}
|
|
6942
|
+
var DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH = join19(homedir14(), ".config", "opencode", "opencode.json");
|
|
6943
|
+
function ensureGlobalOpencodeConfig(configPath = DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH) {
|
|
6944
|
+
mkdirSync7(dirname5(configPath), { recursive: true });
|
|
6945
|
+
const defaultConfig = {
|
|
6946
|
+
$schema: "https://opencode.ai/config.json",
|
|
6947
|
+
model: "anthropic/claude-sonnet-4-5"
|
|
6948
|
+
};
|
|
6949
|
+
try {
|
|
6950
|
+
writeFileSync9(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", { flag: "wx" });
|
|
6951
|
+
return configPath;
|
|
6952
|
+
} catch (err) {
|
|
6953
|
+
if (err.code === "EEXIST") return null;
|
|
6954
|
+
throw err;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
function writePerCrewOpencodeConfig(o) {
|
|
6958
|
+
const dir = join19(o.stateRoot, o.project, o.taskId);
|
|
6959
|
+
mkdirSync7(dir, { recursive: true });
|
|
6960
|
+
const file = join19(dir, "opencode.json");
|
|
6961
|
+
const config = {
|
|
6962
|
+
permission: {
|
|
6963
|
+
read: "allow",
|
|
6964
|
+
edit: "allow",
|
|
6965
|
+
glob: "allow",
|
|
6966
|
+
grep: "allow",
|
|
6967
|
+
bash: o.gateBash ? "ask" : "allow",
|
|
6968
|
+
webfetch: "allow",
|
|
6969
|
+
websearch: "allow",
|
|
6970
|
+
task: "allow",
|
|
6971
|
+
lsp: "allow",
|
|
6972
|
+
external_directory: { "**": "allow" }
|
|
6973
|
+
}
|
|
6974
|
+
};
|
|
6975
|
+
writeFileSync9(file, JSON.stringify(config, null, 2));
|
|
6976
|
+
return file;
|
|
6977
|
+
}
|
|
6978
|
+
|
|
6979
|
+
// packages/cli/src/commands/init.ts
|
|
6980
|
+
init_dist4();
|
|
6725
6981
|
function findPackageRoot() {
|
|
6726
6982
|
let dir = path18.dirname(new URL(import.meta.url).pathname);
|
|
6727
6983
|
while (dir !== "/") {
|
|
@@ -6771,6 +7027,9 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
6771
7027
|
console.log(chalk4.cyan(" /plugin marketplace add superpowers"));
|
|
6772
7028
|
console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
|
|
6773
7029
|
console.log(chalk4.cyan(" /plugin marketplace add context7"));
|
|
7030
|
+
console.log(chalk4.dim("\n Using opencode crews? Install the CLI:"));
|
|
7031
|
+
console.log(chalk4.cyan(" npm install -g opencode-ai"));
|
|
7032
|
+
console.log(chalk4.dim(` (squadrant provisions a default model in ${DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH} if it's missing)`));
|
|
6774
7033
|
console.log(chalk4.bold("\n 4/5 Register first project"));
|
|
6775
7034
|
console.log(chalk4.cyan(" squadrant projects add <name> <path>"));
|
|
6776
7035
|
console.log(chalk4.bold("\n 5/5 Telegram (optional)"));
|
|
@@ -6866,6 +7125,15 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
6866
7125
|
console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
|
|
6867
7126
|
console.log(chalk4.cyan(" /plugin marketplace add context7\n"));
|
|
6868
7127
|
console.log(chalk4.dim(" Squadrant never auto-installs plugins \u2014 open Claude Code and run the commands above."));
|
|
7128
|
+
console.log(chalk4.bold("\n Using opencode crews?"));
|
|
7129
|
+
console.log(chalk4.cyan(" npm install -g opencode-ai"));
|
|
7130
|
+
const opencodeConfigWritten = ensureGlobalOpencodeConfig();
|
|
7131
|
+
if (opencodeConfigWritten) {
|
|
7132
|
+
console.log(chalk4.green(` \u2714 Default model config created at ${opencodeConfigWritten}`));
|
|
7133
|
+
} else {
|
|
7134
|
+
console.log(chalk4.dim(` - ${DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH} already exists (unchanged)`));
|
|
7135
|
+
}
|
|
7136
|
+
console.log(chalk4.dim(" Per-crew config deep-merges with this file \u2014 edit it to change opencode's default model/plugins/mcp."));
|
|
6869
7137
|
stepHeader(4, 5, "Register first project");
|
|
6870
7138
|
const projectPath = await promptLine(
|
|
6871
7139
|
chalk4.cyan(" Absolute path to your first project (Enter to skip): ")
|
|
@@ -7220,9 +7488,9 @@ init_dist4();
|
|
|
7220
7488
|
import { Command as Command8 } from "commander";
|
|
7221
7489
|
import { createConnection as createConnection3 } from "net";
|
|
7222
7490
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7223
|
-
import { homedir as
|
|
7224
|
-
import { join as
|
|
7225
|
-
import { mkdirSync as
|
|
7491
|
+
import { homedir as homedir16 } from "os";
|
|
7492
|
+
import { join as join21 } from "path";
|
|
7493
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "fs";
|
|
7226
7494
|
|
|
7227
7495
|
// packages/cli/src/commands/crew-output.ts
|
|
7228
7496
|
function tailLines(text, maxLines = 40, maxBytes = 4096) {
|
|
@@ -7280,11 +7548,11 @@ init_dist2();
|
|
|
7280
7548
|
import { Command as Command6 } from "commander";
|
|
7281
7549
|
import chalk8 from "chalk";
|
|
7282
7550
|
import { createConnection as createConnection2 } from "net";
|
|
7283
|
-
import { homedir as
|
|
7284
|
-
import { join as
|
|
7551
|
+
import { homedir as homedir15 } from "os";
|
|
7552
|
+
import { join as join20 } from "path";
|
|
7285
7553
|
import { createInterface } from "readline";
|
|
7286
7554
|
function socketPath() {
|
|
7287
|
-
return process.env.SQUADRANTD_SOCK ??
|
|
7555
|
+
return process.env.SQUADRANTD_SOCK ?? join20(homedir15(), ".config", "squadrant", "squadrant.sock");
|
|
7288
7556
|
}
|
|
7289
7557
|
function rule(width, ch = "\u2500") {
|
|
7290
7558
|
return ch.repeat(Math.max(0, width));
|
|
@@ -7520,7 +7788,7 @@ var crewChatCommand = new Command7("chat").description("[DEPRECATED] alias for `
|
|
|
7520
7788
|
});
|
|
7521
7789
|
|
|
7522
7790
|
// packages/cli/src/commands/crew-control.ts
|
|
7523
|
-
var SOCK2 =
|
|
7791
|
+
var SOCK2 = join21(homedir16(), ".config", "squadrant", "squadrant.sock");
|
|
7524
7792
|
var CODEX_FIRST_TURN_DELAY_MS = 1500;
|
|
7525
7793
|
async function sendCodexFirstTurn(taskId, text) {
|
|
7526
7794
|
await new Promise((r) => setTimeout(r, CODEX_FIRST_TURN_DELAY_MS));
|
|
@@ -7625,10 +7893,10 @@ function buildSignalRequest(signal, o) {
|
|
|
7625
7893
|
return { kind: "event", project, event };
|
|
7626
7894
|
}
|
|
7627
7895
|
function defaultWriteResult(id, payload) {
|
|
7628
|
-
const dir =
|
|
7629
|
-
|
|
7630
|
-
const file =
|
|
7631
|
-
|
|
7896
|
+
const dir = join21(homedir16(), ".config", "squadrant", "state", "_results");
|
|
7897
|
+
mkdirSync8(dir, { recursive: true });
|
|
7898
|
+
const file = join21(dir, `${id}.txt`);
|
|
7899
|
+
writeFileSync10(file, payload);
|
|
7632
7900
|
return file;
|
|
7633
7901
|
}
|
|
7634
7902
|
function addControlPlaneCrewCommands(crew) {
|
|
@@ -7734,136 +8002,6 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
7734
8002
|
var crewControlCommand = new Command8("crew").description("Dispatch and track crew via the squadrant control plane");
|
|
7735
8003
|
addControlPlaneCrewCommands(crewControlCommand);
|
|
7736
8004
|
|
|
7737
|
-
// packages/cli/src/lib/per-crew-settings.ts
|
|
7738
|
-
init_dist4();
|
|
7739
|
-
import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
7740
|
-
import { join as join20 } from "path";
|
|
7741
|
-
var CREW_PERMISSION_ALLOWLIST = [
|
|
7742
|
-
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
7743
|
-
"Bash(git status:*)",
|
|
7744
|
-
"Bash(git add:*)",
|
|
7745
|
-
"Bash(git commit:*)",
|
|
7746
|
-
"Bash(git push:*)",
|
|
7747
|
-
"Bash(git pull:*)",
|
|
7748
|
-
"Bash(git fetch:*)",
|
|
7749
|
-
"Bash(git checkout:*)",
|
|
7750
|
-
"Bash(git branch:*)",
|
|
7751
|
-
"Bash(git diff:*)",
|
|
7752
|
-
"Bash(git log:*)",
|
|
7753
|
-
"Bash(git stash:*)",
|
|
7754
|
-
"Bash(git restore:*)",
|
|
7755
|
-
// git — read-only queries (no-mutation subcommands)
|
|
7756
|
-
"Bash(git show:*)",
|
|
7757
|
-
"Bash(git rev-parse:*)",
|
|
7758
|
-
"Bash(git remote -v:*)",
|
|
7759
|
-
"Bash(git stash list:*)",
|
|
7760
|
-
// npm / npx — installs + script/test/build runners
|
|
7761
|
-
"Bash(npm install:*)",
|
|
7762
|
-
"Bash(npm ci:*)",
|
|
7763
|
-
"Bash(npm run:*)",
|
|
7764
|
-
"Bash(npm test:*)",
|
|
7765
|
-
"Bash(npm ls:*)",
|
|
7766
|
-
"Bash(npm view:*)",
|
|
7767
|
-
"Bash(npx vitest:*)",
|
|
7768
|
-
"Bash(npx tsc:*)",
|
|
7769
|
-
// direct runners
|
|
7770
|
-
"Bash(node:*)",
|
|
7771
|
-
"Bash(node --version:*)",
|
|
7772
|
-
"Bash(vitest:*)",
|
|
7773
|
-
"Bash(tsc:*)",
|
|
7774
|
-
// pnpm
|
|
7775
|
-
"Bash(pnpm ls:*)",
|
|
7776
|
-
// process / system read — non-interactive, read-only
|
|
7777
|
-
"Bash(ps:*)",
|
|
7778
|
-
"Bash(pgrep:*)",
|
|
7779
|
-
"Bash(lsof:*)",
|
|
7780
|
-
"Bash(top -l:*)",
|
|
7781
|
-
"Bash(uptime:*)",
|
|
7782
|
-
"Bash(whoami:*)",
|
|
7783
|
-
"Bash(uname:*)",
|
|
7784
|
-
"Bash(date:*)",
|
|
7785
|
-
"Bash(env:*)",
|
|
7786
|
-
"Bash(sysctl:*)",
|
|
7787
|
-
// file read / navigation
|
|
7788
|
-
"Bash(ls:*)",
|
|
7789
|
-
"Bash(cat:*)",
|
|
7790
|
-
"Bash(head:*)",
|
|
7791
|
-
"Bash(tail:*)",
|
|
7792
|
-
"Bash(wc:*)",
|
|
7793
|
-
"Bash(grep:*)",
|
|
7794
|
-
"Bash(rg:*)",
|
|
7795
|
-
"Bash(find:*)",
|
|
7796
|
-
"Bash(pwd:*)",
|
|
7797
|
-
"Bash(stat:*)",
|
|
7798
|
-
"Bash(file:*)",
|
|
7799
|
-
"Bash(realpath:*)",
|
|
7800
|
-
"Bash(basename:*)",
|
|
7801
|
-
"Bash(dirname:*)",
|
|
7802
|
-
"Bash(du:*)",
|
|
7803
|
-
"Bash(df:*)",
|
|
7804
|
-
"Bash(tree:*)",
|
|
7805
|
-
// read-only text processing
|
|
7806
|
-
"Bash(sort:*)",
|
|
7807
|
-
"Bash(uniq:*)",
|
|
7808
|
-
"Bash(cut:*)",
|
|
7809
|
-
"Bash(tr:*)",
|
|
7810
|
-
"Bash(jq:*)",
|
|
7811
|
-
"Bash(diff:*)",
|
|
7812
|
-
"Bash(column:*)",
|
|
7813
|
-
// harmless utilities
|
|
7814
|
-
"Bash(echo:*)",
|
|
7815
|
-
"Bash(which:*)",
|
|
7816
|
-
"Bash(true:*)",
|
|
7817
|
-
"Bash(sleep:*)",
|
|
7818
|
-
"Bash(printf:*)"
|
|
7819
|
-
];
|
|
7820
|
-
function healStaleCockpitRefs(raw) {
|
|
7821
|
-
return raw.replaceAll("cockpit crew _hook", "squadrant crew _hook").replaceAll(".config/cockpit", ".config/squadrant").replaceAll("cockpit-hub", "squadrant-hub");
|
|
7822
|
-
}
|
|
7823
|
-
function mergeCrewPermissions(settings) {
|
|
7824
|
-
const next = structuredClone(settings ?? {});
|
|
7825
|
-
const permissions = next.permissions ??= {};
|
|
7826
|
-
const existing = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
7827
|
-
permissions.allow = [.../* @__PURE__ */ new Set([...existing, ...CREW_PERMISSION_ALLOWLIST])];
|
|
7828
|
-
return next;
|
|
7829
|
-
}
|
|
7830
|
-
function writePerCrewSettingsLocal(o) {
|
|
7831
|
-
const dir = join20(o.projectCwd, ".claude");
|
|
7832
|
-
mkdirSync8(dir, { recursive: true });
|
|
7833
|
-
const file = join20(dir, "settings.local.json");
|
|
7834
|
-
let existing = {};
|
|
7835
|
-
try {
|
|
7836
|
-
const raw = healStaleCockpitRefs(readFileSync11(file, "utf-8"));
|
|
7837
|
-
existing = JSON.parse(raw);
|
|
7838
|
-
} catch {
|
|
7839
|
-
}
|
|
7840
|
-
const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
|
|
7841
|
-
const merged = mergeCrewPermissions(withHooks);
|
|
7842
|
-
writeFileSync9(file, JSON.stringify(merged, null, 2));
|
|
7843
|
-
return file;
|
|
7844
|
-
}
|
|
7845
|
-
function writePerCrewOpencodeConfig(o) {
|
|
7846
|
-
const dir = join20(o.stateRoot, o.project, o.taskId);
|
|
7847
|
-
mkdirSync8(dir, { recursive: true });
|
|
7848
|
-
const file = join20(dir, "opencode.json");
|
|
7849
|
-
const config = {
|
|
7850
|
-
permission: {
|
|
7851
|
-
read: "allow",
|
|
7852
|
-
edit: "allow",
|
|
7853
|
-
glob: "allow",
|
|
7854
|
-
grep: "allow",
|
|
7855
|
-
bash: o.gateBash ? "ask" : "allow",
|
|
7856
|
-
webfetch: "allow",
|
|
7857
|
-
websearch: "allow",
|
|
7858
|
-
task: "allow",
|
|
7859
|
-
lsp: "allow",
|
|
7860
|
-
external_directory: { "**": "allow" }
|
|
7861
|
-
}
|
|
7862
|
-
};
|
|
7863
|
-
writeFileSync9(file, JSON.stringify(config, null, 2));
|
|
7864
|
-
return file;
|
|
7865
|
-
}
|
|
7866
|
-
|
|
7867
8005
|
// packages/cli/src/commands/crew.ts
|
|
7868
8006
|
async function runCrewSpawn2(input) {
|
|
7869
8007
|
const config = loadConfig();
|
|
@@ -8160,8 +8298,8 @@ init_dist();
|
|
|
8160
8298
|
init_dist3();
|
|
8161
8299
|
import { Command as Command11 } from "commander";
|
|
8162
8300
|
import { execSync as execSync10 } from "child_process";
|
|
8163
|
-
import { homedir as
|
|
8164
|
-
import { join as
|
|
8301
|
+
import { homedir as homedir18 } from "os";
|
|
8302
|
+
import { join as join23 } from "path";
|
|
8165
8303
|
import chalk12 from "chalk";
|
|
8166
8304
|
|
|
8167
8305
|
// packages/web/dist/read-status.js
|
|
@@ -8174,6 +8312,11 @@ function deriveState(tasks) {
|
|
|
8174
8312
|
return "busy";
|
|
8175
8313
|
return "idle";
|
|
8176
8314
|
}
|
|
8315
|
+
function deriveRowState(tasks, captainState) {
|
|
8316
|
+
if (captainState === "gone" || captainState === "stopped" || captainState === "unknown")
|
|
8317
|
+
return "offline";
|
|
8318
|
+
return deriveState(tasks);
|
|
8319
|
+
}
|
|
8177
8320
|
function buildExcerpt(tasks) {
|
|
8178
8321
|
const working = tasks.filter((t) => t.state === "working").length;
|
|
8179
8322
|
const blocked = tasks.filter((t) => t.state === "blocked" || t.state === "awaiting-input").length;
|
|
@@ -8197,13 +8340,25 @@ async function readAllStatuses(deps) {
|
|
|
8197
8340
|
const result = await deps.call({ kind: "list", project });
|
|
8198
8341
|
return result;
|
|
8199
8342
|
});
|
|
8343
|
+
const captainStateByProject = /* @__PURE__ */ new Map();
|
|
8344
|
+
if (deps.call) {
|
|
8345
|
+
try {
|
|
8346
|
+
const health = await deps.call({ kind: "health" });
|
|
8347
|
+
for (const h of health ?? []) {
|
|
8348
|
+
if (h.kind === "captain")
|
|
8349
|
+
captainStateByProject.set(h.project, h.state);
|
|
8350
|
+
}
|
|
8351
|
+
} catch {
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8200
8354
|
const rows = [];
|
|
8201
8355
|
for (const [name, project] of Object.entries(deps.config.projects)) {
|
|
8202
8356
|
try {
|
|
8203
8357
|
const tasks = await listTasks(name);
|
|
8358
|
+
const captainState = captainStateByProject.get(name) ?? "unknown";
|
|
8204
8359
|
rows.push({
|
|
8205
8360
|
project: name,
|
|
8206
|
-
state:
|
|
8361
|
+
state: deriveRowState(tasks, captainState),
|
|
8207
8362
|
lastChecked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8208
8363
|
captainWorkspace: project.captainName,
|
|
8209
8364
|
excerpt: buildExcerpt(tasks)
|
|
@@ -8357,9 +8512,9 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
8357
8512
|
// packages/web/dist/probes.js
|
|
8358
8513
|
init_dist();
|
|
8359
8514
|
init_dist();
|
|
8360
|
-
import { join as
|
|
8361
|
-
import { homedir as
|
|
8362
|
-
import { existsSync as existsSync11, readFileSync as
|
|
8515
|
+
import { join as join22 } from "path";
|
|
8516
|
+
import { homedir as homedir17 } from "os";
|
|
8517
|
+
import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
|
|
8363
8518
|
import { execFile as execFile3 } from "child_process";
|
|
8364
8519
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
8365
8520
|
var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
|
|
@@ -8395,7 +8550,7 @@ function vaultProbe(run, dir) {
|
|
|
8395
8550
|
return { state: "unknown", detail: "no vault configured" };
|
|
8396
8551
|
if (!run.pathExists(dir))
|
|
8397
8552
|
return { state: "gone", detail: "vault directory missing" };
|
|
8398
|
-
if (!run.pathExists(
|
|
8553
|
+
if (!run.pathExists(join22(dir, ".obsidian")))
|
|
8399
8554
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
8400
8555
|
return { state: "alive" };
|
|
8401
8556
|
} catch {
|
|
@@ -8463,13 +8618,13 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
8463
8618
|
const sessions = probeSessions(run);
|
|
8464
8619
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
8465
8620
|
}
|
|
8466
|
-
var SESSIONS_PATH =
|
|
8621
|
+
var SESSIONS_PATH = join22(homedir17(), ".config", "squadrant", "sessions.json");
|
|
8467
8622
|
function onPath(cli) {
|
|
8468
8623
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
8469
|
-
return dirs.some((d) => existsSync11(
|
|
8624
|
+
return dirs.some((d) => existsSync11(join22(d, cli)));
|
|
8470
8625
|
}
|
|
8471
8626
|
function readSessionsHashes() {
|
|
8472
|
-
const raw = JSON.parse(
|
|
8627
|
+
const raw = JSON.parse(readFileSync14(SESSIONS_PATH, "utf-8"));
|
|
8473
8628
|
const hashes = Object.values(raw.workspaces ?? {}).map((w) => w.templateHash).filter((h) => typeof h === "string" && h.length > 0);
|
|
8474
8629
|
return [...new Set(hashes)];
|
|
8475
8630
|
}
|
|
@@ -8678,6 +8833,16 @@ function renderOverview(snap, col) {
|
|
|
8678
8833
|
out.push(`</section>`);
|
|
8679
8834
|
return out.join("");
|
|
8680
8835
|
}
|
|
8836
|
+
function filterBar() {
|
|
8837
|
+
return [
|
|
8838
|
+
`<div class="filter-bar" role="group" aria-label="Filter projects by status">`,
|
|
8839
|
+
`<button class="filter-btn on" data-filter="all" aria-pressed="true">All</button>`,
|
|
8840
|
+
`<button class="filter-btn" data-filter="alive" aria-pressed="false">Nominal</button>`,
|
|
8841
|
+
`<button class="filter-btn" data-filter="caution" aria-pressed="false">Caution</button>`,
|
|
8842
|
+
`<button class="filter-btn" data-filter="offline" aria-pressed="false">Offline</button>`,
|
|
8843
|
+
`</div>`
|
|
8844
|
+
].join("");
|
|
8845
|
+
}
|
|
8681
8846
|
function componentRow(c, now) {
|
|
8682
8847
|
const detail = c.detail ? esc(c.detail) : "";
|
|
8683
8848
|
const row = `<tr><td>${statePill(c.state)}</td><td class="mono">${esc(c.kind)}</td><td class="mono">${esc(c.ref)}</td><td class="mono dim">${esc(ageText(c.lastSeenMs, now))}</td><td class="dim">${detail}</td></tr>`;
|
|
@@ -8702,6 +8867,7 @@ function renderProjects(snap, now) {
|
|
|
8702
8867
|
out.push(`<div class="empty">Telemetry link lost \u2014 per-project data is served by the daemon. Start it to restore: <code>squadrant heal daemon</code></div></section>`);
|
|
8703
8868
|
return out.join("");
|
|
8704
8869
|
}
|
|
8870
|
+
out.push(filterBar());
|
|
8705
8871
|
const d = snap.daemon;
|
|
8706
8872
|
const projects = /* @__PURE__ */ new Set([...d.tier1.map((c) => c.project), ...d.tier2.projects.map((p) => p.project)]);
|
|
8707
8873
|
if (projects.size === 0)
|
|
@@ -8718,8 +8884,10 @@ function renderProjects(snap, now) {
|
|
|
8718
8884
|
deferralState
|
|
8719
8885
|
];
|
|
8720
8886
|
const rollup = worst(rollupStates);
|
|
8887
|
+
const captainComp = comps.find((c) => c.kind === "captain");
|
|
8888
|
+
const captainState = captainComp?.state ?? "unknown";
|
|
8721
8889
|
out.push(`<article class="card" data-rollup="${rollup}">`);
|
|
8722
|
-
out.push(`<header class="card-head"><span class="card-title">${esc(project)}</span>${statePill(rollup)}</header>`);
|
|
8890
|
+
out.push(`<header class="card-head"><span class="card-title">${esc(project)}</span><div class="card-badges"><span class="captain-badge">captain ${statePill(captainState)}</span>${statePill(rollup)}</div></header>`);
|
|
8723
8891
|
if (comps.length) {
|
|
8724
8892
|
out.push(`<table class="grid"><thead><tr><th>status</th><th>kind</th><th>ref</th><th>last seen</th><th>detail</th></tr></thead><tbody>`, ...comps.map((c) => componentRow(c, now)), `</tbody></table>`);
|
|
8725
8893
|
} else {
|
|
@@ -8800,7 +8968,131 @@ function renderEnv(ext) {
|
|
|
8800
8968
|
out.push(`</section>`);
|
|
8801
8969
|
return out.join("");
|
|
8802
8970
|
}
|
|
8971
|
+
var ATTN_ORDER = { stale: 0, gone: 1, alive: 2, unknown: 3, stopped: 4 };
|
|
8972
|
+
var STATE_LABEL = { alive: "running", stale: "blocked", gone: "errored", unknown: "idle", stopped: "offline" };
|
|
8973
|
+
var STATE_CLS = { alive: "s-alive", stale: "s-stale", gone: "s-gone", unknown: "s-unknown", stopped: "s-stopped" };
|
|
8974
|
+
var STATE_ORDER = ["alive", "stale", "gone", "unknown", "stopped"];
|
|
8975
|
+
var TASK_ICON = { done: "\u2713", working: "\u25B6", blocked: "\u25CF", failed: "\u2715", cancelled: "\u2299" };
|
|
8976
|
+
function renderLiveGrid(snap, now) {
|
|
8977
|
+
const out = [`<section class="panel" data-panel="live" role="tabpanel" aria-label="Live">`];
|
|
8978
|
+
if (snap.daemon === "unreachable") {
|
|
8979
|
+
out.push(`<div class="empty">Telemetry link lost \u2014 per-project data is served by the daemon. Start it to restore: <code>squadrant heal daemon</code></div></section>`);
|
|
8980
|
+
return out.join("");
|
|
8981
|
+
}
|
|
8982
|
+
const d = snap.daemon;
|
|
8983
|
+
const projects = /* @__PURE__ */ new Set([...d.tier1.map((c) => c.project), ...d.tier2.projects.map((p) => p.project)]);
|
|
8984
|
+
if (projects.size === 0) {
|
|
8985
|
+
out.push(`<div class="empty">No projects registered yet.</div>`);
|
|
8986
|
+
} else {
|
|
8987
|
+
const rows = [];
|
|
8988
|
+
const stateCounts = {};
|
|
8989
|
+
for (const s of STATE_ORDER)
|
|
8990
|
+
stateCounts[s] = 0;
|
|
8991
|
+
for (const project of projects) {
|
|
8992
|
+
const comps = d.tier1.filter((c) => c.project === project);
|
|
8993
|
+
const dp = d.tier2.projects.find((p) => p.project === project);
|
|
8994
|
+
const captainStopped = comps.some((c) => c.kind === "captain" && c.state === "stopped");
|
|
8995
|
+
const deferralState = !captainStopped && dp && dp.deferral.stuck ? "gone" : !captainStopped && dp && dp.deferral.maxDeferCount > 0 ? "stale" : "alive";
|
|
8996
|
+
const rollupStates = [
|
|
8997
|
+
...comps.map((c) => c.state),
|
|
8998
|
+
!captainStopped && dp && dp.delivery.behind > 0 ? "stale" : "alive",
|
|
8999
|
+
dp && dp.store.corruptCount > 0 ? "gone" : "alive",
|
|
9000
|
+
deferralState
|
|
9001
|
+
];
|
|
9002
|
+
const rollup = worst(rollupStates);
|
|
9003
|
+
const captainComp = comps.find((c) => c.kind === "captain");
|
|
9004
|
+
const captainState = captainComp?.state ?? "unknown";
|
|
9005
|
+
const crews = comps.filter((c) => c.kind === "crew");
|
|
9006
|
+
let detail = "";
|
|
9007
|
+
if (crews.length > 0) {
|
|
9008
|
+
const alive = crews.filter((c) => c.state === "alive");
|
|
9009
|
+
const other = crews.filter((c) => c.state !== "alive");
|
|
9010
|
+
const parts = [];
|
|
9011
|
+
if (alive.length > 0)
|
|
9012
|
+
parts.push(`${alive.length} working \u2014 ${alive.map((c) => c.ref).join("; ")}`);
|
|
9013
|
+
for (const c of other)
|
|
9014
|
+
parts.push(`${c.state} \u2014 ${c.ref}`);
|
|
9015
|
+
detail = parts.join(", ");
|
|
9016
|
+
} else if (captainState === "alive") {
|
|
9017
|
+
detail = "captain idle";
|
|
9018
|
+
}
|
|
9019
|
+
rows.push({ name: project, rollup, captainState, detail, crews, tasks: dp?.store.byState ?? {} });
|
|
9020
|
+
stateCounts[rollup]++;
|
|
9021
|
+
}
|
|
9022
|
+
const headerLabels = STATE_ORDER.map((s) => `${STATE_LABEL[s]}`);
|
|
9023
|
+
out.push(`<div class="live-header" data-live-header="">`);
|
|
9024
|
+
out.push(headerLabels.map((l) => {
|
|
9025
|
+
const raw = Object.entries(STATE_LABEL).find(([, v]) => v === l)[0];
|
|
9026
|
+
const c = stateCounts[raw];
|
|
9027
|
+
const cls = c === 0 ? "zero" : "";
|
|
9028
|
+
return `<span class="live-stat ${cls}"><span class="pdot ${STATE_CLS[raw]}"></span>${c} ${l}</span>`;
|
|
9029
|
+
}).join(` <span class="live-sep">\xB7</span> `));
|
|
9030
|
+
out.push(`</div>`);
|
|
9031
|
+
rows.sort((a, b) => {
|
|
9032
|
+
const d2 = (ATTN_ORDER[a.rollup] ?? 99) - (ATTN_ORDER[b.rollup] ?? 99);
|
|
9033
|
+
return d2 !== 0 ? d2 : a.name.localeCompare(b.name);
|
|
9034
|
+
});
|
|
9035
|
+
out.push(`<div class="live-controls">`);
|
|
9036
|
+
out.push(`<input type="text" class="live-search" placeholder="Search projects or crews\u2026" data-live-search="">`);
|
|
9037
|
+
out.push(`<div class="filter-bar" role="group" aria-label="Filter live rows by status">`);
|
|
9038
|
+
for (const fb of [["all", "All"], ["alive", "Nominal"], ["caution", "Caution"], ["offline", "Offline"]]) {
|
|
9039
|
+
out.push(`<button class="filter-btn${fb[0] === "all" ? " on" : ""}" data-filter="${fb[0]}" data-live-filter="" aria-pressed="${fb[0] === "all" ? "true" : "false"}">${fb[1]}</button>`);
|
|
9040
|
+
}
|
|
9041
|
+
out.push(`</div></div>`);
|
|
9042
|
+
out.push(`<table class="live-grid" data-live-table=""><thead><tr class="lh-row">`);
|
|
9043
|
+
out.push(`<th class="lh-dot"></th><th class="lh-name" data-sort="name">Project</th><th class="lh-state" data-sort="attention">Status</th><th class="lh-detail">Activity</th><th class="lh-lastseen" data-sort="activity">Last Seen</th><th class="lh-tasks">Tasks</th>`);
|
|
9044
|
+
out.push(`</tr></thead><tbody>`);
|
|
9045
|
+
for (const row of rows) {
|
|
9046
|
+
let maxAge = -1;
|
|
9047
|
+
let maxSeenMs = null;
|
|
9048
|
+
for (const c of row.crews) {
|
|
9049
|
+
if (c.lastSeenMs != null) {
|
|
9050
|
+
const age = now - c.lastSeenMs;
|
|
9051
|
+
if (age > maxAge) {
|
|
9052
|
+
maxAge = age;
|
|
9053
|
+
maxSeenMs = c.lastSeenMs;
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
9056
|
+
}
|
|
9057
|
+
const crewAgeStr = maxSeenMs == null ? "\u2014" : ageText(maxSeenMs, now);
|
|
9058
|
+
const crewAgeQuiet = maxSeenMs != null && maxAge > CREW_STALE_MS;
|
|
9059
|
+
const tParts = [];
|
|
9060
|
+
for (const [st, n] of Object.entries(row.tasks)) {
|
|
9061
|
+
const icon = TASK_ICON[st] ?? st[0];
|
|
9062
|
+
tParts.push(`<span class="tk">${n}${esc(icon)}</span>`);
|
|
9063
|
+
}
|
|
9064
|
+
out.push(`<tr class="live-row" data-rollup="${row.rollup}" data-project="${esc(row.name)}" data-max-age="${maxAge}">`);
|
|
9065
|
+
out.push(`<td class="live-dot"><span class="pdot s-${row.rollup}"></span></td>`);
|
|
9066
|
+
out.push(`<td class="live-name">${esc(row.name)}</td>`);
|
|
9067
|
+
out.push(`<td class="live-state">${statePill(row.captainState)}</td>`);
|
|
9068
|
+
out.push(`<td class="live-detail dim">${esc(row.detail)}</td>`);
|
|
9069
|
+
out.push(`<td class="live-lastseen${crewAgeQuiet ? " quiet" : ""}">${crewAgeStr}</td>`);
|
|
9070
|
+
out.push(`<td class="live-tasks">${tParts.join("") || '<span class="dim">\u2014</span>'}</td>`);
|
|
9071
|
+
out.push(`</tr>`);
|
|
9072
|
+
out.push(`<tr class="live-expand" data-project="${esc(row.name)}" hidden>`);
|
|
9073
|
+
out.push(`<td colspan="6"><div class="live-crew-list">`);
|
|
9074
|
+
out.push(`<div class="live-crew-head"><span class="lc-name">Crew</span><span class="lc-state">Status</span><span class="lc-lastseen">Last Seen</span><span class="lc-detail">Detail</span></div>`);
|
|
9075
|
+
for (const c of row.crews) {
|
|
9076
|
+
const cs = ageText(c.lastSeenMs, now);
|
|
9077
|
+
out.push(`<div class="live-crew-row"><span class="lc-name">${esc(c.ref)}</span><span class="lc-state">${statePill(c.state)}</span><span class="lc-lastseen">${cs}</span><span class="lc-detail dim">${c.detail ? esc(c.detail) : ""}</span></div>`);
|
|
9078
|
+
}
|
|
9079
|
+
if (row.crews.length === 0) {
|
|
9080
|
+
out.push(`<div class="live-crew-row dim"><span class="lc-name" colspan="4">no live crews</span></div>`);
|
|
9081
|
+
}
|
|
9082
|
+
out.push(`</div><div class="live-crew-meta">`);
|
|
9083
|
+
out.push(`<span class="lc-meta-item">pid: \u2014 (incoming)</span>`);
|
|
9084
|
+
out.push(`<span class="lc-meta-item">uptime: \u2014 (incoming)</span>`);
|
|
9085
|
+
out.push(`<span class="lc-meta-item">agent: \u2014 (incoming)</span>`);
|
|
9086
|
+
out.push(`<span class="lc-meta-item">model: \u2014 (incoming)</span>`);
|
|
9087
|
+
out.push(`</div></td></tr>`);
|
|
9088
|
+
}
|
|
9089
|
+
out.push(`</tbody></table>`);
|
|
9090
|
+
}
|
|
9091
|
+
out.push(`</section>`);
|
|
9092
|
+
return out.join("");
|
|
9093
|
+
}
|
|
8803
9094
|
var TABS = [
|
|
9095
|
+
["live", "Live"],
|
|
8804
9096
|
["overview", "Overview"],
|
|
8805
9097
|
["projects", "Projects"],
|
|
8806
9098
|
["daemon", "Daemon"],
|
|
@@ -8848,6 +9140,7 @@ function renderContent(snap) {
|
|
|
8848
9140
|
}
|
|
8849
9141
|
out.push(tabNav());
|
|
8850
9142
|
out.push(`<div class="panels">`);
|
|
9143
|
+
out.push(renderLiveGrid(snap, col.now));
|
|
8851
9144
|
out.push(renderOverview(snap, col));
|
|
8852
9145
|
out.push(renderProjects(snap, col.now));
|
|
8853
9146
|
out.push(renderDaemon(snap));
|
|
@@ -8972,6 +9265,77 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
8972
9265
|
.spark-area{fill:var(--hud);opacity:.1}
|
|
8973
9266
|
.spark-dot{fill:var(--hud)}
|
|
8974
9267
|
|
|
9268
|
+
/* Live controls (search + filter bar) */
|
|
9269
|
+
.live-controls{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:4px 0 10px}
|
|
9270
|
+
.live-search{flex:1;min-width:140px;max-width:300px;padding:5px 10px;border:1px solid var(--bezel);border-radius:8px;background:var(--panel);color:var(--ink);font:12px/1.4 ui-monospace,monospace;outline:none;transition:border-color .15s ease}
|
|
9271
|
+
.live-search:focus{border-color:var(--hud);box-shadow:0 0 0 2px rgba(11,111,194,.15)}
|
|
9272
|
+
.live-search::placeholder{color:var(--ink-dim);opacity:.7}
|
|
9273
|
+
|
|
9274
|
+
/* Live grid (mission control) */
|
|
9275
|
+
.live-grid{width:100%;border-collapse:collapse;font-size:12px;table-layout:fixed}
|
|
9276
|
+
.live-grid td,.live-grid th{padding:5px 6px;vertical-align:middle;white-space:nowrap}
|
|
9277
|
+
.live-grid th{padding:6px 6px 8px;text-align:left;font-family:system-ui,sans-serif;text-transform:uppercase;letter-spacing:.1em;font-size:9px;color:var(--ink-dim);border-bottom:1px solid var(--bezel);font-weight:600;cursor:default}
|
|
9278
|
+
.live-grid th:hover{color:var(--ink)}
|
|
9279
|
+
.live-row td{border-bottom:1px solid var(--track)}
|
|
9280
|
+
.live-row:last-child td{border-bottom:0}
|
|
9281
|
+
.lh-dot{width:22px;text-align:center}
|
|
9282
|
+
.lh-name{width:auto;min-width:14%}
|
|
9283
|
+
.lh-state{width:88px;text-align:right;padding-right:12px!important}
|
|
9284
|
+
.lh-detail{width:auto}
|
|
9285
|
+
.lh-lastseen{width:105px}
|
|
9286
|
+
.lh-tasks{width:90px;text-align:right}
|
|
9287
|
+
.live-dot{width:22px;text-align:center;padding-right:0!important}
|
|
9288
|
+
.live-dot .pdot{display:inline-block;vertical-align:middle}
|
|
9289
|
+
.live-name{font-weight:600}
|
|
9290
|
+
.live-state{width:88px;text-align:right;padding-right:12px!important}
|
|
9291
|
+
.live-state .pill{font-size:10px;padding:1px 7px}
|
|
9292
|
+
.live-detail{overflow:hidden;text-overflow:ellipsis;color:var(--ink-dim)}
|
|
9293
|
+
.live-lastseen{width:105px;font-size:11px;color:var(--ink-dim)}
|
|
9294
|
+
.live-lastseen.quiet{color:var(--warn);font-weight:600}
|
|
9295
|
+
.live-tasks{width:90px;text-align:right;font-size:11px}
|
|
9296
|
+
.live-tasks .tk{padding:0 3px}
|
|
9297
|
+
.live-tasks .tk:first-child{padding-left:0}
|
|
9298
|
+
.live-row.expanded td{border-bottom-color:transparent}
|
|
9299
|
+
.live-expand td{padding:0 6px 10px;background:var(--panel-2);border-bottom:1px solid var(--track)}
|
|
9300
|
+
.live-expand td:first-child{border-radius:0 0 0 8px}
|
|
9301
|
+
.live-expand td:last-child{border-radius:0 0 8px 0}
|
|
9302
|
+
.live-crew-list{display:table;width:100%;border-collapse:collapse;font-size:12px}
|
|
9303
|
+
.live-crew-head,.live-crew-row{display:table-row}
|
|
9304
|
+
.live-crew-head>span,.live-crew-row>span{display:table-cell;padding:4px 8px;white-space:nowrap;vertical-align:middle}
|
|
9305
|
+
.live-crew-head>span{font-family:system-ui,sans-serif;text-transform:uppercase;letter-spacing:.1em;font-size:9px;color:var(--ink-dim);font-weight:600;border-bottom:1px solid var(--bezel)}
|
|
9306
|
+
.live-crew-row>span{border-bottom:1px solid var(--track)}
|
|
9307
|
+
.live-crew-row:last-child>span{border-bottom:0}
|
|
9308
|
+
.lc-name{font-weight:600}
|
|
9309
|
+
.lc-state{width:80px}
|
|
9310
|
+
.lc-state .pill{font-size:10px;padding:1px 7px}
|
|
9311
|
+
.lc-lastseen{width:90px;color:var(--ink-dim);font-size:11px}
|
|
9312
|
+
.lc-detail{color:var(--ink-dim);font-size:11px}
|
|
9313
|
+
.live-crew-meta{display:flex;gap:14px;padding:6px 8px 0;font-size:11px;color:var(--ink-dim);border-top:1px solid var(--track);margin-top:4px;padding-top:6px}
|
|
9314
|
+
.lc-meta-item{white-space:nowrap}
|
|
9315
|
+
.live-row.expanded+.live-expand td{background:var(--panel-2)}
|
|
9316
|
+
.live-row[data-rollup="gone"] td.live-dot .pdot{background:var(--crit);box-shadow:0 0 6px var(--crit)}
|
|
9317
|
+
.live-row[data-rollup="stale"] td.live-dot .pdot{background:var(--warn);box-shadow:0 0 6px var(--warn)}
|
|
9318
|
+
.live-row[data-rollup="stopped"] td.live-dot .pdot{background:var(--stopped)}
|
|
9319
|
+
.live-row[data-rollup="alive"] td.live-dot .pdot{background:var(--ok);box-shadow:0 0 6px var(--ok)}
|
|
9320
|
+
.live-row[data-rollup="unknown"] td.live-dot .pdot{background:var(--unk)}
|
|
9321
|
+
.live-row:hover{background:var(--panel-2)}
|
|
9322
|
+
.live-header{display:flex;gap:8px 16px;flex-wrap:wrap;padding:6px 0 10px;font-size:12px;cursor:default;user-select:none}
|
|
9323
|
+
.live-stat{display:flex;align-items:center;gap:5px;font-weight:600}
|
|
9324
|
+
.live-stat .pdot{width:6px;height:6px}
|
|
9325
|
+
.live-stat.zero{opacity:.35}
|
|
9326
|
+
.live-sep{color:var(--ink-dim);opacity:.4}
|
|
9327
|
+
|
|
9328
|
+
/* Filter bar */
|
|
9329
|
+
.filter-bar{display:flex;gap:6px;margin:0 0 14px;flex-wrap:wrap}
|
|
9330
|
+
.filter-btn{appearance:none;background:var(--panel-2);border:1px solid var(--bezel);border-radius:999px;
|
|
9331
|
+
padding:4px 13px;cursor:pointer;font-family:system-ui,sans-serif;text-transform:uppercase;letter-spacing:.1em;
|
|
9332
|
+
font-size:10px;font-weight:600;color:var(--ink-dim);transition:all .15s ease}
|
|
9333
|
+
.filter-btn:hover{background:var(--panel);border-color:var(--ink-dim);color:var(--ink)}
|
|
9334
|
+
.filter-btn.on{background:var(--hud);border-color:var(--hud);color:#fff;box-shadow:0 1px 3px rgba(11,111,194,.3)}
|
|
9335
|
+
.card-badges{display:flex;align-items:center;gap:8px;flex:none}
|
|
9336
|
+
.captain-badge{display:flex;align-items:center;gap:4px;font-size:10px;color:var(--ink-dim);letter-spacing:.04em}
|
|
9337
|
+
.captain-badge .pill{font-size:10px;padding:1px 7px}
|
|
9338
|
+
|
|
8975
9339
|
/* Cards / tables */
|
|
8976
9340
|
.card{border:1px solid var(--bezel);border-radius:12px;background:var(--panel);box-shadow:var(--shadow);padding:14px 16px;margin-bottom:14px;border-left:3px solid var(--bezel)}
|
|
8977
9341
|
.card[data-rollup="gone"]{border-left-color:var(--crit)}
|
|
@@ -9034,7 +9398,7 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
9034
9398
|
`.trim();
|
|
9035
9399
|
var CLIENT_JS = `
|
|
9036
9400
|
(function(){
|
|
9037
|
-
var activeTab='
|
|
9401
|
+
var activeTab='live';var hist={};var prev={};var currentFilter='all';var currentSort='attention';var sortAsc=true;var currentSearch='';var expandedProjects={};
|
|
9038
9402
|
function pushHist(k,v,t){var a=hist[k]||(hist[k]=[]);if(a.length&&a[a.length-1].t===t)return;a.push({t:t,v:v});if(a.length>48)a.shift();}
|
|
9039
9403
|
function ingest(){var el=document.getElementById('squadrant-metrics');if(!el)return;var m;try{m=JSON.parse(el.textContent);}catch(e){return;}pushHist('errors',m.errors,m.t);pushHist('behind',m.behind,m.t);pushHist('crewAge',Math.round(m.crewAgeMs/1000),m.t);pushHist('defers',m.maxDeferCount,m.t);}
|
|
9040
9404
|
function sparkSVG(a){var W=100,H=28,p=2;if(!a.length)return'';var vs=a.map(function(x){return x.v;});var mx=Math.max.apply(null,vs),mn=Math.min.apply(null,vs);if(mx===mn)mx=mn+1;var n=a.length;var pts=a.map(function(x,i){var px=n>1?p+i/(n-1)*(W-2*p):W/2;var py=H-p-(x.v-mn)/(mx-mn)*(H-2*p);return[px,py];});var d=pts.map(function(pt,i){return(i?'L':'M')+pt[0].toFixed(1)+' '+pt[1].toFixed(1);}).join(' ');var last=pts[pts.length-1];var area=d+' L'+last[0].toFixed(1)+' '+H+' L'+pts[0][0].toFixed(1)+' '+H+' Z';return'<path class="spark-area" d="'+area+'"/><path class="spark-line" d="'+d+'"/><circle class="spark-dot" cx="'+last[0].toFixed(1)+'" cy="'+last[1].toFixed(1)+'" r="2"/>';}
|
|
@@ -9042,9 +9406,15 @@ var CLIENT_JS = `
|
|
|
9042
9406
|
function applyTab(){var ts=document.querySelectorAll('[data-tab]');for(var i=0;i<ts.length;i++){var on=ts[i].getAttribute('data-tab')===activeTab;ts[i].setAttribute('aria-selected',on?'true':'false');ts[i].classList.toggle('on',on);}var ps=document.querySelectorAll('[data-panel]');for(var j=0;j<ps.length;j++){var on2=ps[j].getAttribute('data-panel')===activeTab;ps[j].hidden=!on2;}}
|
|
9043
9407
|
function ease(k){return k<.5?2*k*k:1-Math.pow(-2*k+2,2)/2;}
|
|
9044
9408
|
function countUps(){var ns=document.querySelectorAll('[data-countup]');for(var i=0;i<ns.length;i++){(function(el){var key=el.getAttribute('data-countup');var to=parseFloat(el.getAttribute('data-value'))||0;var from=prev[key]!=null?prev[key]:0;prev[key]=to;if(from===to){el.textContent=to;return;}var s=null;function step(ts){if(s===null)s=ts;var k=Math.min(1,(ts-s)/600);el.textContent=Math.round(from+(to-from)*ease(k));if(k<1)requestAnimationFrame(step);}requestAnimationFrame(step);})(ns[i]);}}
|
|
9045
|
-
function
|
|
9046
|
-
document.
|
|
9047
|
-
|
|
9409
|
+
function hideByFilter(r,filter){if(filter==='all')return false;if(filter==='alive')return r!=='alive';if(filter==='caution')return r!=='stale';if(filter==='offline')return r!=='gone'&&r!=='stopped'&&r!=='unknown';return false;}
|
|
9410
|
+
function applyFilter(){document.querySelectorAll('[data-panel="projects"] .card').forEach(function(c){c.hidden=hideByFilter(c.getAttribute('data-rollup'),currentFilter);});document.querySelectorAll('[data-panel="live"] .live-row').forEach(function(c){c.hidden=hideByFilter(c.getAttribute('data-rollup'),currentFilter);});document.querySelectorAll('.filter-btn').forEach(function(x){var on=x.getAttribute('data-filter')===currentFilter;x.classList.toggle('on',on);x.setAttribute('aria-pressed',on?'true':'false');});}
|
|
9411
|
+
function applySearch(){var q=currentSearch.toLowerCase();document.querySelectorAll('[data-panel="live"] .live-row').forEach(function(r){if(!q){if(!hideByFilter(r.getAttribute('data-rollup'),currentFilter))r.hidden=false;return;}var pn=(r.getAttribute('data-project')||'').toLowerCase();var dd=((r.querySelector('.live-detail')||{}).textContent||'').toLowerCase();var rl=r.getAttribute('data-rollup');if(hideByFilter(rl,currentFilter)){r.hidden=true;return;}r.hidden=pn.indexOf(q)===-1&&dd.indexOf(q)===-1;});}
|
|
9412
|
+
function applySort(){var table=document.querySelector('[data-panel="live"] [data-live-table]');if(!table)return;var tbody=table.querySelector('tbody');if(!tbody)return;var rows=Array.prototype.slice.call(tbody.querySelectorAll('.live-row'));var dir=sortAsc?1:-1;rows.sort(function(a,b){var va,vb;if(currentSort==='name'){va=(a.getAttribute('data-project')||'').toLowerCase();vb=(b.getAttribute('data-project')||'').toLowerCase();}else if(currentSort==='attention'){var order={gone:0,stale:1,alive:2,unknown:3,stopped:4};va=order[a.getAttribute('data-rollup')]??99;vb=order[b.getAttribute('data-rollup')]??99;if(va===vb){va=(a.getAttribute('data-project')||'').toLowerCase();vb=(b.getAttribute('data-project')||'').toLowerCase();return va<vb?-1:va>vb?1:0;}}else if(currentSort==='activity'){va=parseInt(a.getAttribute('data-max-age'))||0;vb=parseInt(b.getAttribute('data-max-age'))||0;}return va<vb?-dir:va>vb?dir:0;});rows.forEach(function(r){tbody.appendChild(r);});}
|
|
9413
|
+
function applyExpand(){document.querySelectorAll('[data-panel="live"] .live-row').forEach(function(r){var pn=r.getAttribute('data-project');var er=document.querySelector('[data-panel="live"] .live-expand[data-project="'+pn+'"]');if(er){var show=!!expandedProjects[pn];er.hidden=!show;r.classList.toggle('expanded',show);}});}
|
|
9414
|
+
function refresh(){ingest();applyTab();drawSparks();countUps();applyFilter();applySearch();applySort();applyExpand();}
|
|
9415
|
+
document.addEventListener('click',function(e){var t=e.target&&e.target.closest?e.target.closest('[data-tab]'):null;if(t){activeTab=t.getAttribute('data-tab');applyTab();var c=document.getElementById('content');if(c){c.classList.remove('swap');void c.offsetWidth;c.classList.add('swap');}}var fb=e.target&&e.target.closest?e.target.closest('[data-filter]'):null;if(fb){currentFilter=fb.getAttribute('data-filter');applyFilter();applySearch();}var sh=e.target&&e.target.closest?e.target.closest('[data-sort]'):null;if(sh){var key=sh.getAttribute('data-sort');if(key===currentSort)sortAsc=!sortAsc;else{currentSort=key;sortAsc=true;}applySort();}var lr=e.target&&e.target.closest?e.target.closest('[data-panel="live"] .live-row'):null;if(lr&&!e.target.closest('[data-sort]')){var pn=lr.getAttribute('data-project');var er=document.querySelector('[data-panel="live"] .live-expand[data-project="'+pn+'"]');if(er){var show=er.hidden;er.hidden=!show;lr.classList.toggle('expanded',!!show);expandedProjects[pn]=!!show;}}});
|
|
9416
|
+
document.addEventListener('keydown',function(e){var ae=document.activeElement;if((e.key==='ArrowRight'||e.key==='ArrowLeft')&&ae&&ae.getAttribute&&ae.getAttribute('data-tab')){var o=['live','overview','projects','daemon','environment'];var i=o.indexOf(activeTab);i=(i+(e.key==='ArrowRight'?1:o.length-1))%o.length;activeTab=o[i];applyTab();var nt=document.querySelector('[data-tab="'+activeTab+'"]');if(nt)nt.focus();e.preventDefault();}});
|
|
9417
|
+
document.addEventListener('input',function(e){var sb=e.target&&e.target.closest?e.target.closest('[data-live-search]'):null;if(sb){currentSearch=sb.value;applySearch();}});
|
|
9048
9418
|
var led=document.getElementById('led');var conn=document.getElementById('conn');var updated=document.getElementById('updated');
|
|
9049
9419
|
function tickAge(){if(!generatedAt)return;var s=Math.max(0,Math.round((Date.now()-generatedAt)/1000));updated.textContent='updated '+s+'s ago';}
|
|
9050
9420
|
setInterval(tickAge,1000);
|
|
@@ -9159,7 +9529,7 @@ async function startWebServer(opts) {
|
|
|
9159
9529
|
|
|
9160
9530
|
// packages/cli/src/commands/dashboard.ts
|
|
9161
9531
|
init_dist();
|
|
9162
|
-
var SOCK3 =
|
|
9532
|
+
var SOCK3 = join23(homedir18(), ".config", "squadrant", "squadrant.sock");
|
|
9163
9533
|
function detectCurrentWorkspace2() {
|
|
9164
9534
|
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
9165
9535
|
const match = out.match(/workspace:\d+/);
|
|
@@ -9327,8 +9697,8 @@ init_dist2();
|
|
|
9327
9697
|
var CMUX_APP = "/Applications/cmux.app";
|
|
9328
9698
|
var TEMPLATES_DIR4 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
|
|
9329
9699
|
var SESSIONS_PATH2 = path23.join(os12.homedir(), ".config", "squadrant", "sessions.json");
|
|
9330
|
-
function ensureCmuxReady() {
|
|
9331
|
-
if (isInsideCmux()) return;
|
|
9700
|
+
function ensureCmuxReady(headless) {
|
|
9701
|
+
if (headless || isInsideCmux()) return;
|
|
9332
9702
|
console.log(chalk13.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
9333
9703
|
execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
|
|
9334
9704
|
console.log(chalk13.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
|
|
@@ -9336,8 +9706,9 @@ function ensureCmuxReady() {
|
|
|
9336
9706
|
}
|
|
9337
9707
|
var launchCommand = new Command12("launch").description(
|
|
9338
9708
|
"Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
|
|
9339
|
-
).argument("[project]", "Project name to launch captain for").option("--fresh", "Start a new session instead of resuming the last one").option("--all", "Launch all captain workspaces").action(async (project, opts) => {
|
|
9709
|
+
).argument("[project]", "Project name to launch captain for").option("--fresh", "Start a new session instead of resuming the last one").option("--all", "Launch all captain workspaces").option("--headless", "Skip the interactive cmux-app requirement (used by the daemon to boot captains without a terminal)").action(async (project, opts) => {
|
|
9340
9710
|
const config = loadConfig();
|
|
9711
|
+
let hadFailure = false;
|
|
9341
9712
|
const drivers = {
|
|
9342
9713
|
claude: createClaudeDriver(),
|
|
9343
9714
|
codex: createCodexDriver(),
|
|
@@ -9347,7 +9718,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9347
9718
|
const registry = new CapabilityRegistry(drivers);
|
|
9348
9719
|
const runtimes = new RuntimeRegistry({ cmux: createCmuxDriver() });
|
|
9349
9720
|
async function launchOne(workspaceName, role, cwd, permissionMode, navigate, pinToTop = false, projectName) {
|
|
9350
|
-
ensureCmuxReady();
|
|
9721
|
+
ensureCmuxReady(!!opts.headless);
|
|
9351
9722
|
const roleConfig = config.defaults.roles?.[role];
|
|
9352
9723
|
const agentName = roleConfig?.agent || "claude";
|
|
9353
9724
|
const model = roleConfig?.model || config.defaults.models?.[role];
|
|
@@ -9387,6 +9758,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9387
9758
|
});
|
|
9388
9759
|
} catch (err) {
|
|
9389
9760
|
console.error(chalk13.red(` \u2718 Failed: ${err.message}`));
|
|
9761
|
+
hadFailure = true;
|
|
9390
9762
|
}
|
|
9391
9763
|
}
|
|
9392
9764
|
if (opts.all) {
|
|
@@ -9415,7 +9787,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9415
9787
|
);
|
|
9416
9788
|
process.exit(1);
|
|
9417
9789
|
}
|
|
9418
|
-
ensureCmuxReady();
|
|
9790
|
+
ensureCmuxReady(!!opts.headless);
|
|
9419
9791
|
const sessions = loadSessions(SESSIONS_PATH2);
|
|
9420
9792
|
const entries = Object.entries(config.projects).map(([name, proj]) => ({
|
|
9421
9793
|
projectName: name,
|
|
@@ -9472,6 +9844,7 @@ Launching captain workspace for '${project}' (${proj.captainName})
|
|
|
9472
9844
|
);
|
|
9473
9845
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, project);
|
|
9474
9846
|
}
|
|
9847
|
+
if (hadFailure) process.exitCode = 1;
|
|
9475
9848
|
});
|
|
9476
9849
|
|
|
9477
9850
|
// packages/cli/src/commands/shutdown.ts
|
|
@@ -10440,7 +10813,7 @@ init_dist2();
|
|
|
10440
10813
|
import { Command as Command22 } from "commander";
|
|
10441
10814
|
import fs25 from "fs";
|
|
10442
10815
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10443
|
-
import { dirname as
|
|
10816
|
+
import { dirname as dirname6, join as join24 } from "path";
|
|
10444
10817
|
import chalk22 from "chalk";
|
|
10445
10818
|
function runConfigCheck(opts) {
|
|
10446
10819
|
const raw = JSON.parse(fs25.readFileSync(opts.configPath, "utf-8"));
|
|
@@ -10576,7 +10949,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
10576
10949
|
}
|
|
10577
10950
|
});
|
|
10578
10951
|
function readPkgVersion2() {
|
|
10579
|
-
const pkgPath =
|
|
10952
|
+
const pkgPath = join24(dirname6(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
10580
10953
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
10581
10954
|
}
|
|
10582
10955
|
|
|
@@ -10816,9 +11189,9 @@ var EFFORT_MEANING = {
|
|
|
10816
11189
|
balance: "normal routing \u2014 use default crew routing rules unchanged",
|
|
10817
11190
|
low: "conserve tokens \u2014 bias crew spawns toward opencode/sonnet; reserve opus for work that genuinely needs it"
|
|
10818
11191
|
};
|
|
10819
|
-
function runEffortGet(configPath = DEFAULT_CONFIG_PATH) {
|
|
11192
|
+
function runEffortGet(configPath = DEFAULT_CONFIG_PATH, projectName) {
|
|
10820
11193
|
const config = loadConfig(configPath);
|
|
10821
|
-
const effort = resolveEffort(config);
|
|
11194
|
+
const effort = resolveEffort(config, projectName);
|
|
10822
11195
|
const description = `${effort}: ${EFFORT_MEANING[effort]}`;
|
|
10823
11196
|
return { effort, description };
|
|
10824
11197
|
}
|
|
@@ -10853,10 +11226,11 @@ async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(
|
|
|
10853
11226
|
}
|
|
10854
11227
|
}
|
|
10855
11228
|
}
|
|
10856
|
-
var effortCommand = new Command28("effort").description("Get or set the global crew tokenomics dial (max | balance | low)").argument("[value]", "effort level to set: max | balance | low").action(async (value) => {
|
|
11229
|
+
var effortCommand = new Command28("effort").description("Get or set the global crew tokenomics dial (max | balance | low)").argument("[value]", "effort level to set: max | balance | low").option("--project <name>", "show resolved effort for a specific project").action(async (value, options) => {
|
|
10857
11230
|
if (value === void 0) {
|
|
10858
|
-
const { effort: effort2, description } = runEffortGet();
|
|
10859
|
-
|
|
11231
|
+
const { effort: effort2, description } = runEffortGet(void 0, options.project);
|
|
11232
|
+
const label = options.project ? `${options.project} project` : "global";
|
|
11233
|
+
console.log(chalk28.bold(`Current effort (${label}):`), chalk28.cyan(effort2));
|
|
10860
11234
|
console.log(chalk28.dim(EFFORT_MEANING[effort2]));
|
|
10861
11235
|
return;
|
|
10862
11236
|
}
|
|
@@ -10883,12 +11257,12 @@ var effortCommand = new Command28("effort").description("Get or set the global c
|
|
|
10883
11257
|
// packages/cli/src/commands/telegram.ts
|
|
10884
11258
|
init_dist();
|
|
10885
11259
|
init_dist2();
|
|
10886
|
-
import { join as
|
|
11260
|
+
import { join as join25, dirname as dirname7 } from "path";
|
|
10887
11261
|
import { emitKeypressEvents } from "readline";
|
|
10888
11262
|
import { Command as Command29 } from "commander";
|
|
10889
11263
|
import chalk29 from "chalk";
|
|
10890
11264
|
function defaultStateRoot() {
|
|
10891
|
-
return
|
|
11265
|
+
return join25(dirname7(DEFAULT_CONFIG_PATH), "state");
|
|
10892
11266
|
}
|
|
10893
11267
|
async function questionMasked() {
|
|
10894
11268
|
return new Promise((resolve3) => {
|
|
@@ -11213,9 +11587,9 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
11213
11587
|
init_dist2();
|
|
11214
11588
|
init_dist4();
|
|
11215
11589
|
import { Command as Command30 } from "commander";
|
|
11216
|
-
import { join as
|
|
11217
|
-
import { homedir as
|
|
11218
|
-
var SOCK4 =
|
|
11590
|
+
import { join as join26 } from "path";
|
|
11591
|
+
import { homedir as homedir19 } from "os";
|
|
11592
|
+
var SOCK4 = join26(homedir19(), ".config", "squadrant", "squadrant.sock");
|
|
11219
11593
|
async function sendToSock(req) {
|
|
11220
11594
|
await sendRequest(SOCK4, req);
|
|
11221
11595
|
}
|
|
@@ -11277,21 +11651,21 @@ function hooksCommand() {
|
|
|
11277
11651
|
init_dist();
|
|
11278
11652
|
init_dist();
|
|
11279
11653
|
init_dist();
|
|
11280
|
-
var __dirname =
|
|
11281
|
-
var pkg = JSON.parse(
|
|
11654
|
+
var __dirname = dirname8(fileURLToPath6(import.meta.url));
|
|
11655
|
+
var pkg = JSON.parse(readFileSync15(join27(__dirname, "..", "package.json"), "utf-8"));
|
|
11282
11656
|
ensureRuntimeSynced({
|
|
11283
|
-
sourceRoot:
|
|
11284
|
-
runtimeRoot:
|
|
11657
|
+
sourceRoot: join27(__dirname, ".."),
|
|
11658
|
+
runtimeRoot: join27(homedir20(), ".config", "squadrant")
|
|
11285
11659
|
});
|
|
11286
11660
|
if (process.argv[2] !== "config") {
|
|
11287
11661
|
try {
|
|
11288
|
-
const cfgPath =
|
|
11662
|
+
const cfgPath = join27(homedir20(), ".config", "squadrant", "config.json");
|
|
11289
11663
|
if (existsSync12(cfgPath)) {
|
|
11290
|
-
const cfg = JSON.parse(
|
|
11664
|
+
const cfg = JSON.parse(readFileSync15(cfgPath, "utf-8"));
|
|
11291
11665
|
if (needsCheck(cfg, pkg.version)) {
|
|
11292
11666
|
const items = detectDrift(cfg, getDefaultConfig());
|
|
11293
11667
|
if (items.length === 0) {
|
|
11294
|
-
|
|
11668
|
+
writeFileSync11(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
|
|
11295
11669
|
} else {
|
|
11296
11670
|
const from = cfg._squadrantVersion ?? "an earlier version";
|
|
11297
11671
|
process.stderr.write(
|