squadrant 0.14.2 → 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 +757 -328
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +627 -131
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/captain-ops/SKILL.md +10 -7
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
|
}
|
|
@@ -2186,15 +2208,16 @@ async function dispatchToSibling(opts) {
|
|
|
2186
2208
|
if (!toCfg) {
|
|
2187
2209
|
throw new Error(`target project '${opts.toProject}' not found in config`);
|
|
2188
2210
|
}
|
|
2189
|
-
|
|
2190
|
-
throw new Error(`cannot dispatch: '${opts.toProject}' (group: ${toCfg.group ?? "none"}) is not in the same group as '${opts.fromProject}' (group: ${fromCfg.group ?? "none"})`);
|
|
2191
|
-
}
|
|
2211
|
+
const sameGroup = !!fromCfg?.group && !!toCfg.group && fromCfg.group === toCfg.group;
|
|
2192
2212
|
if (toCfg.acceptDelegations === false) {
|
|
2193
2213
|
throw new Error(`cannot dispatch to '${opts.toProject}': project has acceptDelegations set to false`);
|
|
2194
2214
|
}
|
|
2195
2215
|
const sockPath = opts.sockPath ?? DEFAULT_SOCK_PATH2;
|
|
2196
2216
|
const alive = await isCaptainAlive(opts.toProject, sockPath);
|
|
2197
2217
|
if (!alive) {
|
|
2218
|
+
if (!sameGroup) {
|
|
2219
|
+
throw new Error(`cannot dispatch to '${opts.toProject}': captain is not running and cross-group dispatch does not auto-boot it. Use 'squadrant ping ${opts.toProject} "<msg>"' or start it manually with 'squadrant launch ${opts.toProject}', then retry.`);
|
|
2220
|
+
}
|
|
2198
2221
|
if (opts.bootCaptain) {
|
|
2199
2222
|
await opts.bootCaptain(opts.toProject);
|
|
2200
2223
|
}
|
|
@@ -2786,7 +2809,7 @@ var init_dist2 = __esm({
|
|
|
2786
2809
|
init_mailbox();
|
|
2787
2810
|
init_protocol();
|
|
2788
2811
|
init_state_machine();
|
|
2789
|
-
|
|
2812
|
+
init_liveness2();
|
|
2790
2813
|
init_watchdog();
|
|
2791
2814
|
init_store();
|
|
2792
2815
|
init_snapshot();
|
|
@@ -3660,11 +3683,81 @@ var init_events_bridge = __esm({
|
|
|
3660
3683
|
}
|
|
3661
3684
|
});
|
|
3662
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
|
+
|
|
3663
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";
|
|
3664
3755
|
var DaemonCmux;
|
|
3665
3756
|
var init_daemon_cmux = __esm({
|
|
3666
3757
|
"packages/workspaces/dist/cmux-daemon/daemon-cmux.js"() {
|
|
3667
3758
|
init_dist2();
|
|
3759
|
+
init_dist();
|
|
3760
|
+
init_store_fingerprint();
|
|
3668
3761
|
DaemonCmux = class {
|
|
3669
3762
|
driver;
|
|
3670
3763
|
constructor(driver) {
|
|
@@ -3715,14 +3808,30 @@ var init_daemon_cmux = __esm({
|
|
|
3715
3808
|
return false;
|
|
3716
3809
|
}
|
|
3717
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
|
+
}
|
|
3718
3827
|
};
|
|
3719
3828
|
}
|
|
3720
3829
|
});
|
|
3721
3830
|
|
|
3722
3831
|
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
3723
|
-
import { join as
|
|
3724
|
-
import { homedir as
|
|
3725
|
-
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";
|
|
3726
3835
|
function parseLifecycleState(s) {
|
|
3727
3836
|
if (s === "running" || s === "idle" || s === "needsInput" || s === "unknown") {
|
|
3728
3837
|
return s;
|
|
@@ -3739,14 +3848,14 @@ function defaultIsPidAlive(pid) {
|
|
|
3739
3848
|
}
|
|
3740
3849
|
function defaultListFiles(dir) {
|
|
3741
3850
|
try {
|
|
3742
|
-
return
|
|
3851
|
+
return readdirSync4(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
3743
3852
|
} catch {
|
|
3744
3853
|
return [];
|
|
3745
3854
|
}
|
|
3746
3855
|
}
|
|
3747
3856
|
function defaultReadFile(path29) {
|
|
3748
3857
|
try {
|
|
3749
|
-
return
|
|
3858
|
+
return readFileSync10(path29, "utf-8");
|
|
3750
3859
|
} catch {
|
|
3751
3860
|
return void 0;
|
|
3752
3861
|
}
|
|
@@ -3782,7 +3891,7 @@ var init_cmux_store_source = __esm({
|
|
|
3782
3891
|
active = false;
|
|
3783
3892
|
lastError = null;
|
|
3784
3893
|
constructor(opts = {}) {
|
|
3785
|
-
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");
|
|
3786
3895
|
this.debounceMs = opts.debounceMs ?? 50;
|
|
3787
3896
|
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
3788
3897
|
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
@@ -3844,7 +3953,7 @@ var init_cmux_store_source = __esm({
|
|
|
3844
3953
|
}
|
|
3845
3954
|
scanFile(filename) {
|
|
3846
3955
|
const deps = this.deps;
|
|
3847
|
-
const filePath =
|
|
3956
|
+
const filePath = join14(this.stateDir, filename);
|
|
3848
3957
|
const lockPath = `${filePath}.lock`;
|
|
3849
3958
|
if (this.fileExists(lockPath)) {
|
|
3850
3959
|
this.log(`cmux-store: skipping ${filename} (locked)`);
|
|
@@ -3898,11 +4007,11 @@ var init_cmux_store_source = __esm({
|
|
|
3898
4007
|
});
|
|
3899
4008
|
|
|
3900
4009
|
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
3901
|
-
import { join as
|
|
3902
|
-
import { homedir as
|
|
3903
|
-
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";
|
|
3904
4013
|
function installClaudeHooks(opts = {}) {
|
|
3905
|
-
const settingsPath = opts.settingsPath ??
|
|
4014
|
+
const settingsPath = opts.settingsPath ?? join15(homedir10(), ".claude", "settings.json");
|
|
3906
4015
|
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
3907
4016
|
const readFile6 = opts.readFile ?? defaultReadFile2;
|
|
3908
4017
|
const writeFile5 = opts.writeFile ?? defaultWriteFile;
|
|
@@ -3976,14 +4085,14 @@ function extractDetail(sub, payload) {
|
|
|
3976
4085
|
}
|
|
3977
4086
|
function defaultReadFile2(path29) {
|
|
3978
4087
|
try {
|
|
3979
|
-
return
|
|
4088
|
+
return readFileSync11(path29, "utf-8");
|
|
3980
4089
|
} catch {
|
|
3981
4090
|
return void 0;
|
|
3982
4091
|
}
|
|
3983
4092
|
}
|
|
3984
4093
|
function defaultWriteFile(path29, content) {
|
|
3985
4094
|
mkdirSync6(path29.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
3986
|
-
|
|
4095
|
+
writeFileSync8(path29, content, "utf-8");
|
|
3987
4096
|
}
|
|
3988
4097
|
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, NativeHookSource;
|
|
3989
4098
|
var init_native_hook_source = __esm({
|
|
@@ -4152,6 +4261,21 @@ async function confirmedSendToPane(runtime, pane, message) {
|
|
|
4152
4261
|
}
|
|
4153
4262
|
return { delivered: false };
|
|
4154
4263
|
}
|
|
4264
|
+
async function resendCrewFirstTurn(runtime, captainName, project, name, message) {
|
|
4265
|
+
const captain = await runtime.status(captainName);
|
|
4266
|
+
if (!captain)
|
|
4267
|
+
return { delivered: false };
|
|
4268
|
+
const surfaces = await runtime.listSurfaces(captain.id);
|
|
4269
|
+
const want = titleFor(project, name);
|
|
4270
|
+
const pane = surfaces.find((s) => s.title === want);
|
|
4271
|
+
if (!pane)
|
|
4272
|
+
return { delivered: false };
|
|
4273
|
+
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
4274
|
+
if (!hasCCInputBox(screen) || classifyStartupSurface(screen) !== "idle") {
|
|
4275
|
+
return { delivered: false };
|
|
4276
|
+
}
|
|
4277
|
+
return confirmedSendToPane(runtime, pane, message);
|
|
4278
|
+
}
|
|
4155
4279
|
async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acceptanceConfig) {
|
|
4156
4280
|
await new Promise((r) => setTimeout(r, SEND_FIRST_TURN_FLOOR_MS));
|
|
4157
4281
|
const maxPolls = Math.floor((SEND_FIRST_TURN_TIMEOUT_MS - SEND_FIRST_TURN_FLOOR_MS) / POLL_INTERVAL_MS);
|
|
@@ -4260,6 +4384,7 @@ __export(dist_exports, {
|
|
|
4260
4384
|
isInsideCmux: () => isInsideCmux,
|
|
4261
4385
|
listProjectCrews: () => listProjectCrews,
|
|
4262
4386
|
mapSubToLifecycle: () => mapSubToLifecycle,
|
|
4387
|
+
resendCrewFirstTurn: () => resendCrewFirstTurn,
|
|
4263
4388
|
resolveCaptainWorkspace: () => resolveCaptainWorkspace,
|
|
4264
4389
|
sendFirstTurnWhenReady: () => sendFirstTurnWhenReady
|
|
4265
4390
|
});
|
|
@@ -5296,11 +5421,11 @@ var init_codex_app_server_source = __esm({
|
|
|
5296
5421
|
|
|
5297
5422
|
// packages/agents/dist/codex/config.js
|
|
5298
5423
|
import { readFile as readFile5 } from "fs/promises";
|
|
5299
|
-
import { homedir as
|
|
5300
|
-
import { join as
|
|
5424
|
+
import { homedir as homedir12 } from "os";
|
|
5425
|
+
import { join as join17 } from "path";
|
|
5301
5426
|
async function resolveCodexModel() {
|
|
5302
|
-
const home = process.env["CODEX_HOME"] ??
|
|
5303
|
-
const configPath =
|
|
5427
|
+
const home = process.env["CODEX_HOME"] ?? join17(homedir12(), ".codex");
|
|
5428
|
+
const configPath = join17(home, "config.toml");
|
|
5304
5429
|
let text;
|
|
5305
5430
|
try {
|
|
5306
5431
|
text = await readFile5(configPath, "utf8");
|
|
@@ -5722,7 +5847,7 @@ var init_sse_bridge = __esm({
|
|
|
5722
5847
|
const fetchImpl = this.deps.fetchImpl ?? fetch;
|
|
5723
5848
|
const sleep2 = this.deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5724
5849
|
const reconnectMs = this.deps.reconnectMs ?? 500;
|
|
5725
|
-
const maxBoot = this.deps.maxBootAttempts ??
|
|
5850
|
+
const maxBoot = this.deps.maxBootAttempts ?? 240;
|
|
5726
5851
|
const url = `http://127.0.0.1:${port}/event`;
|
|
5727
5852
|
let booted = false;
|
|
5728
5853
|
let bootAttempts = 0;
|
|
@@ -5821,9 +5946,9 @@ var init_sse_bridge = __esm({
|
|
|
5821
5946
|
|
|
5822
5947
|
// packages/agents/dist/interactive/claude.js
|
|
5823
5948
|
import { execSync as execSync7 } from "child_process";
|
|
5824
|
-
import { readFileSync as
|
|
5825
|
-
import { homedir as
|
|
5826
|
-
import { join as
|
|
5949
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
5950
|
+
import { homedir as homedir13 } from "os";
|
|
5951
|
+
import { join as join18 } from "path";
|
|
5827
5952
|
function probeClaudeSettingsFlag() {
|
|
5828
5953
|
try {
|
|
5829
5954
|
const help = execSync7("claude --help", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -5874,11 +5999,11 @@ function deriveTranscriptPath(sessionId, cwd) {
|
|
|
5874
5999
|
if (!sessionId || !cwd)
|
|
5875
6000
|
return null;
|
|
5876
6001
|
const escaped = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5877
|
-
return
|
|
6002
|
+
return join18(homedir13(), ".claude", "projects", escaped, `${sessionId}.jsonl`);
|
|
5878
6003
|
}
|
|
5879
6004
|
function readLastAssistantText(transcriptPath) {
|
|
5880
6005
|
try {
|
|
5881
|
-
const raw =
|
|
6006
|
+
const raw = readFileSync12(transcriptPath, "utf-8");
|
|
5882
6007
|
const lines = raw.split(/\r?\n/);
|
|
5883
6008
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5884
6009
|
const line = lines[i].trim();
|
|
@@ -6344,11 +6469,11 @@ var init_dist4 = __esm({
|
|
|
6344
6469
|
// packages/cli/src/index.ts
|
|
6345
6470
|
init_dist();
|
|
6346
6471
|
init_dist2();
|
|
6347
|
-
import { Command as
|
|
6348
|
-
import { readFileSync as
|
|
6472
|
+
import { Command as Command31 } from "commander";
|
|
6473
|
+
import { readFileSync as readFileSync15, existsSync as existsSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
6349
6474
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
6350
|
-
import { dirname as
|
|
6351
|
-
import { homedir as
|
|
6475
|
+
import { dirname as dirname8, join as join27 } from "path";
|
|
6476
|
+
import { homedir as homedir20 } from "os";
|
|
6352
6477
|
|
|
6353
6478
|
// packages/cli/src/commands/doctor.ts
|
|
6354
6479
|
init_dist();
|
|
@@ -6365,10 +6490,10 @@ import chalk3 from "chalk";
|
|
|
6365
6490
|
// packages/cli/src/commands/health-view.ts
|
|
6366
6491
|
init_dist2();
|
|
6367
6492
|
init_dist2();
|
|
6368
|
-
import { homedir as
|
|
6369
|
-
import { join as
|
|
6493
|
+
import { homedir as homedir11 } from "os";
|
|
6494
|
+
import { join as join16 } from "path";
|
|
6370
6495
|
import chalk2 from "chalk";
|
|
6371
|
-
var SOCK =
|
|
6496
|
+
var SOCK = join16(homedir11(), ".config", "squadrant", "squadrant.sock");
|
|
6372
6497
|
async function queryHealth(project) {
|
|
6373
6498
|
try {
|
|
6374
6499
|
const reply = await sendRequest(SOCK, { kind: "health", project });
|
|
@@ -6698,13 +6823,161 @@ ${passed === total ? chalk3.green("All checks passed") : chalk3.yellow(`${passed
|
|
|
6698
6823
|
init_dist();
|
|
6699
6824
|
init_dist3();
|
|
6700
6825
|
init_dist();
|
|
6701
|
-
init_dist4();
|
|
6702
6826
|
import { Command as Command2 } from "commander";
|
|
6703
6827
|
import fs16 from "fs";
|
|
6704
6828
|
import path18 from "path";
|
|
6705
6829
|
import os9 from "os";
|
|
6706
6830
|
import readline from "readline";
|
|
6707
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();
|
|
6708
6981
|
function findPackageRoot() {
|
|
6709
6982
|
let dir = path18.dirname(new URL(import.meta.url).pathname);
|
|
6710
6983
|
while (dir !== "/") {
|
|
@@ -6754,6 +7027,9 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
6754
7027
|
console.log(chalk4.cyan(" /plugin marketplace add superpowers"));
|
|
6755
7028
|
console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
|
|
6756
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)`));
|
|
6757
7033
|
console.log(chalk4.bold("\n 4/5 Register first project"));
|
|
6758
7034
|
console.log(chalk4.cyan(" squadrant projects add <name> <path>"));
|
|
6759
7035
|
console.log(chalk4.bold("\n 5/5 Telegram (optional)"));
|
|
@@ -6849,6 +7125,15 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
6849
7125
|
console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
|
|
6850
7126
|
console.log(chalk4.cyan(" /plugin marketplace add context7\n"));
|
|
6851
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."));
|
|
6852
7137
|
stepHeader(4, 5, "Register first project");
|
|
6853
7138
|
const projectPath = await promptLine(
|
|
6854
7139
|
chalk4.cyan(" Absolute path to your first project (Enter to skip): ")
|
|
@@ -7203,9 +7488,9 @@ init_dist4();
|
|
|
7203
7488
|
import { Command as Command8 } from "commander";
|
|
7204
7489
|
import { createConnection as createConnection3 } from "net";
|
|
7205
7490
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7206
|
-
import { homedir as
|
|
7207
|
-
import { join as
|
|
7208
|
-
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";
|
|
7209
7494
|
|
|
7210
7495
|
// packages/cli/src/commands/crew-output.ts
|
|
7211
7496
|
function tailLines(text, maxLines = 40, maxBytes = 4096) {
|
|
@@ -7263,11 +7548,11 @@ init_dist2();
|
|
|
7263
7548
|
import { Command as Command6 } from "commander";
|
|
7264
7549
|
import chalk8 from "chalk";
|
|
7265
7550
|
import { createConnection as createConnection2 } from "net";
|
|
7266
|
-
import { homedir as
|
|
7267
|
-
import { join as
|
|
7551
|
+
import { homedir as homedir15 } from "os";
|
|
7552
|
+
import { join as join20 } from "path";
|
|
7268
7553
|
import { createInterface } from "readline";
|
|
7269
7554
|
function socketPath() {
|
|
7270
|
-
return process.env.SQUADRANTD_SOCK ??
|
|
7555
|
+
return process.env.SQUADRANTD_SOCK ?? join20(homedir15(), ".config", "squadrant", "squadrant.sock");
|
|
7271
7556
|
}
|
|
7272
7557
|
function rule(width, ch = "\u2500") {
|
|
7273
7558
|
return ch.repeat(Math.max(0, width));
|
|
@@ -7503,7 +7788,7 @@ var crewChatCommand = new Command7("chat").description("[DEPRECATED] alias for `
|
|
|
7503
7788
|
});
|
|
7504
7789
|
|
|
7505
7790
|
// packages/cli/src/commands/crew-control.ts
|
|
7506
|
-
var SOCK2 =
|
|
7791
|
+
var SOCK2 = join21(homedir16(), ".config", "squadrant", "squadrant.sock");
|
|
7507
7792
|
var CODEX_FIRST_TURN_DELAY_MS = 1500;
|
|
7508
7793
|
async function sendCodexFirstTurn(taskId, text) {
|
|
7509
7794
|
await new Promise((r) => setTimeout(r, CODEX_FIRST_TURN_DELAY_MS));
|
|
@@ -7608,10 +7893,10 @@ function buildSignalRequest(signal, o) {
|
|
|
7608
7893
|
return { kind: "event", project, event };
|
|
7609
7894
|
}
|
|
7610
7895
|
function defaultWriteResult(id, payload) {
|
|
7611
|
-
const dir =
|
|
7612
|
-
|
|
7613
|
-
const file =
|
|
7614
|
-
|
|
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);
|
|
7615
7900
|
return file;
|
|
7616
7901
|
}
|
|
7617
7902
|
function addControlPlaneCrewCommands(crew) {
|
|
@@ -7717,136 +8002,6 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
7717
8002
|
var crewControlCommand = new Command8("crew").description("Dispatch and track crew via the squadrant control plane");
|
|
7718
8003
|
addControlPlaneCrewCommands(crewControlCommand);
|
|
7719
8004
|
|
|
7720
|
-
// packages/cli/src/lib/per-crew-settings.ts
|
|
7721
|
-
init_dist4();
|
|
7722
|
-
import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
7723
|
-
import { join as join20 } from "path";
|
|
7724
|
-
var CREW_PERMISSION_ALLOWLIST = [
|
|
7725
|
-
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
7726
|
-
"Bash(git status:*)",
|
|
7727
|
-
"Bash(git add:*)",
|
|
7728
|
-
"Bash(git commit:*)",
|
|
7729
|
-
"Bash(git push:*)",
|
|
7730
|
-
"Bash(git pull:*)",
|
|
7731
|
-
"Bash(git fetch:*)",
|
|
7732
|
-
"Bash(git checkout:*)",
|
|
7733
|
-
"Bash(git branch:*)",
|
|
7734
|
-
"Bash(git diff:*)",
|
|
7735
|
-
"Bash(git log:*)",
|
|
7736
|
-
"Bash(git stash:*)",
|
|
7737
|
-
"Bash(git restore:*)",
|
|
7738
|
-
// git — read-only queries (no-mutation subcommands)
|
|
7739
|
-
"Bash(git show:*)",
|
|
7740
|
-
"Bash(git rev-parse:*)",
|
|
7741
|
-
"Bash(git remote -v:*)",
|
|
7742
|
-
"Bash(git stash list:*)",
|
|
7743
|
-
// npm / npx — installs + script/test/build runners
|
|
7744
|
-
"Bash(npm install:*)",
|
|
7745
|
-
"Bash(npm ci:*)",
|
|
7746
|
-
"Bash(npm run:*)",
|
|
7747
|
-
"Bash(npm test:*)",
|
|
7748
|
-
"Bash(npm ls:*)",
|
|
7749
|
-
"Bash(npm view:*)",
|
|
7750
|
-
"Bash(npx vitest:*)",
|
|
7751
|
-
"Bash(npx tsc:*)",
|
|
7752
|
-
// direct runners
|
|
7753
|
-
"Bash(node:*)",
|
|
7754
|
-
"Bash(node --version:*)",
|
|
7755
|
-
"Bash(vitest:*)",
|
|
7756
|
-
"Bash(tsc:*)",
|
|
7757
|
-
// pnpm
|
|
7758
|
-
"Bash(pnpm ls:*)",
|
|
7759
|
-
// process / system read — non-interactive, read-only
|
|
7760
|
-
"Bash(ps:*)",
|
|
7761
|
-
"Bash(pgrep:*)",
|
|
7762
|
-
"Bash(lsof:*)",
|
|
7763
|
-
"Bash(top -l:*)",
|
|
7764
|
-
"Bash(uptime:*)",
|
|
7765
|
-
"Bash(whoami:*)",
|
|
7766
|
-
"Bash(uname:*)",
|
|
7767
|
-
"Bash(date:*)",
|
|
7768
|
-
"Bash(env:*)",
|
|
7769
|
-
"Bash(sysctl:*)",
|
|
7770
|
-
// file read / navigation
|
|
7771
|
-
"Bash(ls:*)",
|
|
7772
|
-
"Bash(cat:*)",
|
|
7773
|
-
"Bash(head:*)",
|
|
7774
|
-
"Bash(tail:*)",
|
|
7775
|
-
"Bash(wc:*)",
|
|
7776
|
-
"Bash(grep:*)",
|
|
7777
|
-
"Bash(rg:*)",
|
|
7778
|
-
"Bash(find:*)",
|
|
7779
|
-
"Bash(pwd:*)",
|
|
7780
|
-
"Bash(stat:*)",
|
|
7781
|
-
"Bash(file:*)",
|
|
7782
|
-
"Bash(realpath:*)",
|
|
7783
|
-
"Bash(basename:*)",
|
|
7784
|
-
"Bash(dirname:*)",
|
|
7785
|
-
"Bash(du:*)",
|
|
7786
|
-
"Bash(df:*)",
|
|
7787
|
-
"Bash(tree:*)",
|
|
7788
|
-
// read-only text processing
|
|
7789
|
-
"Bash(sort:*)",
|
|
7790
|
-
"Bash(uniq:*)",
|
|
7791
|
-
"Bash(cut:*)",
|
|
7792
|
-
"Bash(tr:*)",
|
|
7793
|
-
"Bash(jq:*)",
|
|
7794
|
-
"Bash(diff:*)",
|
|
7795
|
-
"Bash(column:*)",
|
|
7796
|
-
// harmless utilities
|
|
7797
|
-
"Bash(echo:*)",
|
|
7798
|
-
"Bash(which:*)",
|
|
7799
|
-
"Bash(true:*)",
|
|
7800
|
-
"Bash(sleep:*)",
|
|
7801
|
-
"Bash(printf:*)"
|
|
7802
|
-
];
|
|
7803
|
-
function healStaleCockpitRefs(raw) {
|
|
7804
|
-
return raw.replaceAll("cockpit crew _hook", "squadrant crew _hook").replaceAll(".config/cockpit", ".config/squadrant").replaceAll("cockpit-hub", "squadrant-hub");
|
|
7805
|
-
}
|
|
7806
|
-
function mergeCrewPermissions(settings) {
|
|
7807
|
-
const next = structuredClone(settings ?? {});
|
|
7808
|
-
const permissions = next.permissions ??= {};
|
|
7809
|
-
const existing = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
7810
|
-
permissions.allow = [.../* @__PURE__ */ new Set([...existing, ...CREW_PERMISSION_ALLOWLIST])];
|
|
7811
|
-
return next;
|
|
7812
|
-
}
|
|
7813
|
-
function writePerCrewSettingsLocal(o) {
|
|
7814
|
-
const dir = join20(o.projectCwd, ".claude");
|
|
7815
|
-
mkdirSync8(dir, { recursive: true });
|
|
7816
|
-
const file = join20(dir, "settings.local.json");
|
|
7817
|
-
let existing = {};
|
|
7818
|
-
try {
|
|
7819
|
-
const raw = healStaleCockpitRefs(readFileSync11(file, "utf-8"));
|
|
7820
|
-
existing = JSON.parse(raw);
|
|
7821
|
-
} catch {
|
|
7822
|
-
}
|
|
7823
|
-
const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
|
|
7824
|
-
const merged = mergeCrewPermissions(withHooks);
|
|
7825
|
-
writeFileSync9(file, JSON.stringify(merged, null, 2));
|
|
7826
|
-
return file;
|
|
7827
|
-
}
|
|
7828
|
-
function writePerCrewOpencodeConfig(o) {
|
|
7829
|
-
const dir = join20(o.stateRoot, o.project, o.taskId);
|
|
7830
|
-
mkdirSync8(dir, { recursive: true });
|
|
7831
|
-
const file = join20(dir, "opencode.json");
|
|
7832
|
-
const config = {
|
|
7833
|
-
permission: {
|
|
7834
|
-
read: "allow",
|
|
7835
|
-
edit: "allow",
|
|
7836
|
-
glob: "allow",
|
|
7837
|
-
grep: "allow",
|
|
7838
|
-
bash: o.gateBash ? "ask" : "allow",
|
|
7839
|
-
webfetch: "allow",
|
|
7840
|
-
websearch: "allow",
|
|
7841
|
-
task: "allow",
|
|
7842
|
-
lsp: "allow",
|
|
7843
|
-
external_directory: { "**": "allow" }
|
|
7844
|
-
}
|
|
7845
|
-
};
|
|
7846
|
-
writeFileSync9(file, JSON.stringify(config, null, 2));
|
|
7847
|
-
return file;
|
|
7848
|
-
}
|
|
7849
|
-
|
|
7850
8005
|
// packages/cli/src/commands/crew.ts
|
|
7851
8006
|
async function runCrewSpawn2(input) {
|
|
7852
8007
|
const config = loadConfig();
|
|
@@ -8143,8 +8298,8 @@ init_dist();
|
|
|
8143
8298
|
init_dist3();
|
|
8144
8299
|
import { Command as Command11 } from "commander";
|
|
8145
8300
|
import { execSync as execSync10 } from "child_process";
|
|
8146
|
-
import { homedir as
|
|
8147
|
-
import { join as
|
|
8301
|
+
import { homedir as homedir18 } from "os";
|
|
8302
|
+
import { join as join23 } from "path";
|
|
8148
8303
|
import chalk12 from "chalk";
|
|
8149
8304
|
|
|
8150
8305
|
// packages/web/dist/read-status.js
|
|
@@ -8157,6 +8312,11 @@ function deriveState(tasks) {
|
|
|
8157
8312
|
return "busy";
|
|
8158
8313
|
return "idle";
|
|
8159
8314
|
}
|
|
8315
|
+
function deriveRowState(tasks, captainState) {
|
|
8316
|
+
if (captainState === "gone" || captainState === "stopped" || captainState === "unknown")
|
|
8317
|
+
return "offline";
|
|
8318
|
+
return deriveState(tasks);
|
|
8319
|
+
}
|
|
8160
8320
|
function buildExcerpt(tasks) {
|
|
8161
8321
|
const working = tasks.filter((t) => t.state === "working").length;
|
|
8162
8322
|
const blocked = tasks.filter((t) => t.state === "blocked" || t.state === "awaiting-input").length;
|
|
@@ -8180,13 +8340,25 @@ async function readAllStatuses(deps) {
|
|
|
8180
8340
|
const result = await deps.call({ kind: "list", project });
|
|
8181
8341
|
return result;
|
|
8182
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
|
+
}
|
|
8183
8354
|
const rows = [];
|
|
8184
8355
|
for (const [name, project] of Object.entries(deps.config.projects)) {
|
|
8185
8356
|
try {
|
|
8186
8357
|
const tasks = await listTasks(name);
|
|
8358
|
+
const captainState = captainStateByProject.get(name) ?? "unknown";
|
|
8187
8359
|
rows.push({
|
|
8188
8360
|
project: name,
|
|
8189
|
-
state:
|
|
8361
|
+
state: deriveRowState(tasks, captainState),
|
|
8190
8362
|
lastChecked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8191
8363
|
captainWorkspace: project.captainName,
|
|
8192
8364
|
excerpt: buildExcerpt(tasks)
|
|
@@ -8340,9 +8512,9 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
8340
8512
|
// packages/web/dist/probes.js
|
|
8341
8513
|
init_dist();
|
|
8342
8514
|
init_dist();
|
|
8343
|
-
import { join as
|
|
8344
|
-
import { homedir as
|
|
8345
|
-
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";
|
|
8346
8518
|
import { execFile as execFile3 } from "child_process";
|
|
8347
8519
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
8348
8520
|
var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
|
|
@@ -8378,7 +8550,7 @@ function vaultProbe(run, dir) {
|
|
|
8378
8550
|
return { state: "unknown", detail: "no vault configured" };
|
|
8379
8551
|
if (!run.pathExists(dir))
|
|
8380
8552
|
return { state: "gone", detail: "vault directory missing" };
|
|
8381
|
-
if (!run.pathExists(
|
|
8553
|
+
if (!run.pathExists(join22(dir, ".obsidian")))
|
|
8382
8554
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
8383
8555
|
return { state: "alive" };
|
|
8384
8556
|
} catch {
|
|
@@ -8446,13 +8618,13 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
8446
8618
|
const sessions = probeSessions(run);
|
|
8447
8619
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
8448
8620
|
}
|
|
8449
|
-
var SESSIONS_PATH =
|
|
8621
|
+
var SESSIONS_PATH = join22(homedir17(), ".config", "squadrant", "sessions.json");
|
|
8450
8622
|
function onPath(cli) {
|
|
8451
8623
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
8452
|
-
return dirs.some((d) => existsSync11(
|
|
8624
|
+
return dirs.some((d) => existsSync11(join22(d, cli)));
|
|
8453
8625
|
}
|
|
8454
8626
|
function readSessionsHashes() {
|
|
8455
|
-
const raw = JSON.parse(
|
|
8627
|
+
const raw = JSON.parse(readFileSync14(SESSIONS_PATH, "utf-8"));
|
|
8456
8628
|
const hashes = Object.values(raw.workspaces ?? {}).map((w) => w.templateHash).filter((h) => typeof h === "string" && h.length > 0);
|
|
8457
8629
|
return [...new Set(hashes)];
|
|
8458
8630
|
}
|
|
@@ -8661,6 +8833,16 @@ function renderOverview(snap, col) {
|
|
|
8661
8833
|
out.push(`</section>`);
|
|
8662
8834
|
return out.join("");
|
|
8663
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
|
+
}
|
|
8664
8846
|
function componentRow(c, now) {
|
|
8665
8847
|
const detail = c.detail ? esc(c.detail) : "";
|
|
8666
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>`;
|
|
@@ -8685,6 +8867,7 @@ function renderProjects(snap, now) {
|
|
|
8685
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>`);
|
|
8686
8868
|
return out.join("");
|
|
8687
8869
|
}
|
|
8870
|
+
out.push(filterBar());
|
|
8688
8871
|
const d = snap.daemon;
|
|
8689
8872
|
const projects = /* @__PURE__ */ new Set([...d.tier1.map((c) => c.project), ...d.tier2.projects.map((p) => p.project)]);
|
|
8690
8873
|
if (projects.size === 0)
|
|
@@ -8701,8 +8884,10 @@ function renderProjects(snap, now) {
|
|
|
8701
8884
|
deferralState
|
|
8702
8885
|
];
|
|
8703
8886
|
const rollup = worst(rollupStates);
|
|
8887
|
+
const captainComp = comps.find((c) => c.kind === "captain");
|
|
8888
|
+
const captainState = captainComp?.state ?? "unknown";
|
|
8704
8889
|
out.push(`<article class="card" data-rollup="${rollup}">`);
|
|
8705
|
-
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>`);
|
|
8706
8891
|
if (comps.length) {
|
|
8707
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>`);
|
|
8708
8893
|
} else {
|
|
@@ -8783,7 +8968,131 @@ function renderEnv(ext) {
|
|
|
8783
8968
|
out.push(`</section>`);
|
|
8784
8969
|
return out.join("");
|
|
8785
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
|
+
}
|
|
8786
9094
|
var TABS = [
|
|
9095
|
+
["live", "Live"],
|
|
8787
9096
|
["overview", "Overview"],
|
|
8788
9097
|
["projects", "Projects"],
|
|
8789
9098
|
["daemon", "Daemon"],
|
|
@@ -8831,6 +9140,7 @@ function renderContent(snap) {
|
|
|
8831
9140
|
}
|
|
8832
9141
|
out.push(tabNav());
|
|
8833
9142
|
out.push(`<div class="panels">`);
|
|
9143
|
+
out.push(renderLiveGrid(snap, col.now));
|
|
8834
9144
|
out.push(renderOverview(snap, col));
|
|
8835
9145
|
out.push(renderProjects(snap, col.now));
|
|
8836
9146
|
out.push(renderDaemon(snap));
|
|
@@ -8955,6 +9265,77 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
8955
9265
|
.spark-area{fill:var(--hud);opacity:.1}
|
|
8956
9266
|
.spark-dot{fill:var(--hud)}
|
|
8957
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
|
+
|
|
8958
9339
|
/* Cards / tables */
|
|
8959
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)}
|
|
8960
9341
|
.card[data-rollup="gone"]{border-left-color:var(--crit)}
|
|
@@ -9017,7 +9398,7 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
9017
9398
|
`.trim();
|
|
9018
9399
|
var CLIENT_JS = `
|
|
9019
9400
|
(function(){
|
|
9020
|
-
var activeTab='
|
|
9401
|
+
var activeTab='live';var hist={};var prev={};var currentFilter='all';var currentSort='attention';var sortAsc=true;var currentSearch='';var expandedProjects={};
|
|
9021
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();}
|
|
9022
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);}
|
|
9023
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"/>';}
|
|
@@ -9025,9 +9406,15 @@ var CLIENT_JS = `
|
|
|
9025
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;}}
|
|
9026
9407
|
function ease(k){return k<.5?2*k*k:1-Math.pow(-2*k+2,2)/2;}
|
|
9027
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]);}}
|
|
9028
|
-
function
|
|
9029
|
-
document.
|
|
9030
|
-
|
|
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();}});
|
|
9031
9418
|
var led=document.getElementById('led');var conn=document.getElementById('conn');var updated=document.getElementById('updated');
|
|
9032
9419
|
function tickAge(){if(!generatedAt)return;var s=Math.max(0,Math.round((Date.now()-generatedAt)/1000));updated.textContent='updated '+s+'s ago';}
|
|
9033
9420
|
setInterval(tickAge,1000);
|
|
@@ -9142,7 +9529,7 @@ async function startWebServer(opts) {
|
|
|
9142
9529
|
|
|
9143
9530
|
// packages/cli/src/commands/dashboard.ts
|
|
9144
9531
|
init_dist();
|
|
9145
|
-
var SOCK3 =
|
|
9532
|
+
var SOCK3 = join23(homedir18(), ".config", "squadrant", "squadrant.sock");
|
|
9146
9533
|
function detectCurrentWorkspace2() {
|
|
9147
9534
|
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
9148
9535
|
const match = out.match(/workspace:\d+/);
|
|
@@ -9310,8 +9697,8 @@ init_dist2();
|
|
|
9310
9697
|
var CMUX_APP = "/Applications/cmux.app";
|
|
9311
9698
|
var TEMPLATES_DIR4 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
|
|
9312
9699
|
var SESSIONS_PATH2 = path23.join(os12.homedir(), ".config", "squadrant", "sessions.json");
|
|
9313
|
-
function ensureCmuxReady() {
|
|
9314
|
-
if (isInsideCmux()) return;
|
|
9700
|
+
function ensureCmuxReady(headless) {
|
|
9701
|
+
if (headless || isInsideCmux()) return;
|
|
9315
9702
|
console.log(chalk13.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
9316
9703
|
execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
|
|
9317
9704
|
console.log(chalk13.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
|
|
@@ -9319,8 +9706,9 @@ function ensureCmuxReady() {
|
|
|
9319
9706
|
}
|
|
9320
9707
|
var launchCommand = new Command12("launch").description(
|
|
9321
9708
|
"Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
|
|
9322
|
-
).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) => {
|
|
9323
9710
|
const config = loadConfig();
|
|
9711
|
+
let hadFailure = false;
|
|
9324
9712
|
const drivers = {
|
|
9325
9713
|
claude: createClaudeDriver(),
|
|
9326
9714
|
codex: createCodexDriver(),
|
|
@@ -9330,7 +9718,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9330
9718
|
const registry = new CapabilityRegistry(drivers);
|
|
9331
9719
|
const runtimes = new RuntimeRegistry({ cmux: createCmuxDriver() });
|
|
9332
9720
|
async function launchOne(workspaceName, role, cwd, permissionMode, navigate, pinToTop = false, projectName) {
|
|
9333
|
-
ensureCmuxReady();
|
|
9721
|
+
ensureCmuxReady(!!opts.headless);
|
|
9334
9722
|
const roleConfig = config.defaults.roles?.[role];
|
|
9335
9723
|
const agentName = roleConfig?.agent || "claude";
|
|
9336
9724
|
const model = roleConfig?.model || config.defaults.models?.[role];
|
|
@@ -9370,6 +9758,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9370
9758
|
});
|
|
9371
9759
|
} catch (err) {
|
|
9372
9760
|
console.error(chalk13.red(` \u2718 Failed: ${err.message}`));
|
|
9761
|
+
hadFailure = true;
|
|
9373
9762
|
}
|
|
9374
9763
|
}
|
|
9375
9764
|
if (opts.all) {
|
|
@@ -9398,7 +9787,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
9398
9787
|
);
|
|
9399
9788
|
process.exit(1);
|
|
9400
9789
|
}
|
|
9401
|
-
ensureCmuxReady();
|
|
9790
|
+
ensureCmuxReady(!!opts.headless);
|
|
9402
9791
|
const sessions = loadSessions(SESSIONS_PATH2);
|
|
9403
9792
|
const entries = Object.entries(config.projects).map(([name, proj]) => ({
|
|
9404
9793
|
projectName: name,
|
|
@@ -9455,6 +9844,7 @@ Launching captain workspace for '${project}' (${proj.captainName})
|
|
|
9455
9844
|
);
|
|
9456
9845
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, project);
|
|
9457
9846
|
}
|
|
9847
|
+
if (hadFailure) process.exitCode = 1;
|
|
9458
9848
|
});
|
|
9459
9849
|
|
|
9460
9850
|
// packages/cli/src/commands/shutdown.ts
|
|
@@ -10423,7 +10813,7 @@ init_dist2();
|
|
|
10423
10813
|
import { Command as Command22 } from "commander";
|
|
10424
10814
|
import fs25 from "fs";
|
|
10425
10815
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10426
|
-
import { dirname as
|
|
10816
|
+
import { dirname as dirname6, join as join24 } from "path";
|
|
10427
10817
|
import chalk22 from "chalk";
|
|
10428
10818
|
function runConfigCheck(opts) {
|
|
10429
10819
|
const raw = JSON.parse(fs25.readFileSync(opts.configPath, "utf-8"));
|
|
@@ -10559,7 +10949,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
10559
10949
|
}
|
|
10560
10950
|
});
|
|
10561
10951
|
function readPkgVersion2() {
|
|
10562
|
-
const pkgPath =
|
|
10952
|
+
const pkgPath = join24(dirname6(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
10563
10953
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
10564
10954
|
}
|
|
10565
10955
|
|
|
@@ -10656,49 +11046,85 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
|
|
|
10656
11046
|
);
|
|
10657
11047
|
|
|
10658
11048
|
// packages/cli/src/commands/group.ts
|
|
11049
|
+
import { Command as Command25 } from "commander";
|
|
11050
|
+
import chalk25 from "chalk";
|
|
11051
|
+
|
|
11052
|
+
// packages/cli/src/commands/dispatch.ts
|
|
10659
11053
|
init_dist();
|
|
10660
11054
|
init_dist2();
|
|
10661
|
-
init_dist2();
|
|
10662
11055
|
import { Command as Command24 } from "commander";
|
|
10663
11056
|
import { execSync as execSync13 } from "child_process";
|
|
10664
11057
|
import chalk24 from "chalk";
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
} catch {
|
|
10684
|
-
throw new Error(`failed to launch captain for '${project}' \u2014 is squadrant installed?`);
|
|
10685
|
-
}
|
|
10686
|
-
}
|
|
10687
|
-
});
|
|
10688
|
-
console.log(chalk24.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
|
|
10689
|
-
console.log(chalk24.dim(` originProject: ${result.originProject ?? "none"}`));
|
|
10690
|
-
console.log(chalk24.dim(" You will be notified when the task settles (done/blocked/failed)."));
|
|
10691
|
-
} catch (e) {
|
|
10692
|
-
console.error(chalk24.red(`\u2718 ${e.message}`));
|
|
10693
|
-
process.exit(1);
|
|
11058
|
+
async function runDispatch(toProject, task, opts) {
|
|
11059
|
+
const fromProject = resolveCurrentProject(loadConfig());
|
|
11060
|
+
if (!fromProject) {
|
|
11061
|
+
throw new Error("Could not determine current project from cwd. Run from inside a registered project directory.");
|
|
11062
|
+
}
|
|
11063
|
+
return dispatchToSibling({
|
|
11064
|
+
fromProject,
|
|
11065
|
+
toProject,
|
|
11066
|
+
task,
|
|
11067
|
+
provider: opts.provider,
|
|
11068
|
+
mode: opts.mode,
|
|
11069
|
+
warmupTimeoutMs: opts.warmupTimeout,
|
|
11070
|
+
bootCaptain: async (project) => {
|
|
11071
|
+
try {
|
|
11072
|
+
execSync13(`squadrant launch ${project}`, { stdio: "ignore", timeout: 15e3 });
|
|
11073
|
+
} catch {
|
|
11074
|
+
throw new Error(`failed to launch captain for '${project}' \u2014 is squadrant installed?`);
|
|
11075
|
+
}
|
|
10694
11076
|
}
|
|
11077
|
+
});
|
|
11078
|
+
}
|
|
11079
|
+
async function dispatchAction(toProject, task, opts) {
|
|
11080
|
+
try {
|
|
11081
|
+
const result = await runDispatch(toProject, task, opts);
|
|
11082
|
+
console.log(chalk24.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
|
|
11083
|
+
console.log(chalk24.dim(` originProject: ${result.originProject ?? "none"}`));
|
|
11084
|
+
console.log(chalk24.dim(" You will be notified when the task settles (done/blocked/failed)."));
|
|
11085
|
+
} catch (e) {
|
|
11086
|
+
console.error(chalk24.red(`\u2718 ${e.message}`));
|
|
11087
|
+
process.exit(1);
|
|
11088
|
+
}
|
|
11089
|
+
}
|
|
11090
|
+
var dispatchCommand = new Command24("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v) => parseInt(v, 10) * 1e3).action(dispatchAction);
|
|
11091
|
+
|
|
11092
|
+
// packages/cli/src/commands/group.ts
|
|
11093
|
+
init_dist2();
|
|
11094
|
+
var groupCommand = new Command25("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
|
|
11095
|
+
new Command25("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v) => parseInt(v, 10) * 1e3).action(async (toProject, task, opts) => {
|
|
11096
|
+
console.error(chalk25.yellow(
|
|
11097
|
+
`\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
|
|
11098
|
+
));
|
|
11099
|
+
await dispatchAction(toProject, task, opts);
|
|
10695
11100
|
})
|
|
10696
11101
|
);
|
|
10697
11102
|
|
|
11103
|
+
// packages/cli/src/commands/ping.ts
|
|
11104
|
+
init_dist();
|
|
11105
|
+
import { Command as Command26 } from "commander";
|
|
11106
|
+
import chalk26 from "chalk";
|
|
11107
|
+
async function runPing(project, message) {
|
|
11108
|
+
const config = loadConfig();
|
|
11109
|
+
const registry = buildRegistry();
|
|
11110
|
+
const resolved = resolveTarget(registry, config, project, false);
|
|
11111
|
+
const ref = await needRef(resolved);
|
|
11112
|
+
await resolved.driver.send(ref, message);
|
|
11113
|
+
}
|
|
11114
|
+
var pingCommand = new Command26("ping").description("Fire-and-forget: deliver a message into a registered project's captain pane (no tracked task, no report-back)").argument("<project>", "Target project name (must be registered)").argument("<message>", "Message to deliver").action(async (project, message) => {
|
|
11115
|
+
try {
|
|
11116
|
+
await runPing(project, message);
|
|
11117
|
+
console.log(chalk26.green(`\u2714 Pinged '${project}'`));
|
|
11118
|
+
} catch (err) {
|
|
11119
|
+
console.error(chalk26.red(err.message));
|
|
11120
|
+
process.exit(1);
|
|
11121
|
+
}
|
|
11122
|
+
});
|
|
11123
|
+
|
|
10698
11124
|
// packages/cli/src/commands/cmux.ts
|
|
10699
11125
|
init_dist();
|
|
10700
|
-
import { Command as
|
|
10701
|
-
import
|
|
11126
|
+
import { Command as Command27 } from "commander";
|
|
11127
|
+
import chalk27 from "chalk";
|
|
10702
11128
|
async function runCmuxAutoconfig(opts) {
|
|
10703
11129
|
const { json, stdout, stderr } = opts;
|
|
10704
11130
|
let r;
|
|
@@ -10717,19 +11143,19 @@ async function runCmuxAutoconfig(opts) {
|
|
|
10717
11143
|
stdout.write(`wrote cmux automation config \u2192 ${r.configPath}
|
|
10718
11144
|
`);
|
|
10719
11145
|
} else {
|
|
10720
|
-
stdout.write(
|
|
11146
|
+
stdout.write(chalk27.dim(`cmux automation config already in place (${r.configPath})
|
|
10721
11147
|
`));
|
|
10722
11148
|
}
|
|
10723
11149
|
if (r.verdict === "reachable") {
|
|
10724
|
-
stdout.write(
|
|
11150
|
+
stdout.write(chalk27.green("\u2714 daemon-direct delivery is reachable \u2014 cmux control socket accepts the daemon\n"));
|
|
10725
11151
|
return 0;
|
|
10726
11152
|
}
|
|
10727
11153
|
if (r.needsRestart) {
|
|
10728
11154
|
stdout.write(
|
|
10729
|
-
|
|
11155
|
+
chalk27.yellow("\u26A0 cmux is still on the old socket mode \u2014 restart cmux to enable daemon-direct delivery.\n")
|
|
10730
11156
|
);
|
|
10731
11157
|
if (r.promptedThisRun) {
|
|
10732
|
-
stdout.write(
|
|
11158
|
+
stdout.write(chalk27.dim(" (one-time prompt \u2014 you won't be nagged again)\n"));
|
|
10733
11159
|
}
|
|
10734
11160
|
return 2;
|
|
10735
11161
|
}
|
|
@@ -10738,8 +11164,8 @@ async function runCmuxAutoconfig(opts) {
|
|
|
10738
11164
|
);
|
|
10739
11165
|
return 1;
|
|
10740
11166
|
}
|
|
10741
|
-
var cmuxCommand = new
|
|
10742
|
-
new
|
|
11167
|
+
var cmuxCommand = new Command27("cmux").description("cmux integration helpers").addCommand(
|
|
11168
|
+
new Command27("autoconfig").description(
|
|
10743
11169
|
"Write the cmux automation socket config and probe whether daemon-direct\ndelivery is reachable. Idempotent; prompts once if a cmux restart is needed."
|
|
10744
11170
|
).option("--json", "output machine-readable JSON (exit 0=reachable, 1=unknown, 2=restart-needed)").action(async (opts) => {
|
|
10745
11171
|
const code = await runCmuxAutoconfig({
|
|
@@ -10755,17 +11181,17 @@ var cmuxCommand = new Command25("cmux").description("cmux integration helpers").
|
|
|
10755
11181
|
init_dist();
|
|
10756
11182
|
import fs26 from "fs";
|
|
10757
11183
|
import path28 from "path";
|
|
10758
|
-
import { Command as
|
|
10759
|
-
import
|
|
11184
|
+
import { Command as Command28 } from "commander";
|
|
11185
|
+
import chalk28 from "chalk";
|
|
10760
11186
|
var VALID_EFFORTS = ["max", "balance", "low"];
|
|
10761
11187
|
var EFFORT_MEANING = {
|
|
10762
11188
|
max: "tokens are plentiful \u2014 bias crew spawns toward claude/opus",
|
|
10763
11189
|
balance: "normal routing \u2014 use default crew routing rules unchanged",
|
|
10764
11190
|
low: "conserve tokens \u2014 bias crew spawns toward opencode/sonnet; reserve opus for work that genuinely needs it"
|
|
10765
11191
|
};
|
|
10766
|
-
function runEffortGet(configPath = DEFAULT_CONFIG_PATH) {
|
|
11192
|
+
function runEffortGet(configPath = DEFAULT_CONFIG_PATH, projectName) {
|
|
10767
11193
|
const config = loadConfig(configPath);
|
|
10768
|
-
const effort = resolveEffort(config);
|
|
11194
|
+
const effort = resolveEffort(config, projectName);
|
|
10769
11195
|
const description = `${effort}: ${EFFORT_MEANING[effort]}`;
|
|
10770
11196
|
return { effort, description };
|
|
10771
11197
|
}
|
|
@@ -10800,22 +11226,23 @@ async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(
|
|
|
10800
11226
|
}
|
|
10801
11227
|
}
|
|
10802
11228
|
}
|
|
10803
|
-
var effortCommand = new
|
|
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) => {
|
|
10804
11230
|
if (value === void 0) {
|
|
10805
|
-
const { effort: effort2, description } = runEffortGet();
|
|
10806
|
-
|
|
10807
|
-
console.log(
|
|
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));
|
|
11234
|
+
console.log(chalk28.dim(EFFORT_MEANING[effort2]));
|
|
10808
11235
|
return;
|
|
10809
11236
|
}
|
|
10810
11237
|
try {
|
|
10811
11238
|
runEffortSet(value);
|
|
10812
11239
|
} catch (err) {
|
|
10813
|
-
console.error(
|
|
11240
|
+
console.error(chalk28.red(err.message));
|
|
10814
11241
|
process.exit(1);
|
|
10815
11242
|
}
|
|
10816
11243
|
const effort = value;
|
|
10817
|
-
console.log(
|
|
10818
|
-
console.log(
|
|
11244
|
+
console.log(chalk28.green(`\u2714 effort \u2192 ${effort}`));
|
|
11245
|
+
console.log(chalk28.dim(EFFORT_MEANING[effort]));
|
|
10819
11246
|
try {
|
|
10820
11247
|
const { createCmuxDriver: createCmuxDriver2, RuntimeRegistry: RuntimeRegistry2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports));
|
|
10821
11248
|
const config = loadConfig();
|
|
@@ -10823,19 +11250,19 @@ var effortCommand = new Command26("effort").description("Get or set the global c
|
|
|
10823
11250
|
const driver = registry.global(config);
|
|
10824
11251
|
await notifyCaptainsOfEffort(effort, config, driver);
|
|
10825
11252
|
} catch {
|
|
10826
|
-
console.log(
|
|
11253
|
+
console.log(chalk28.dim("(no running captain detected \u2014 change applies on next launch)"));
|
|
10827
11254
|
}
|
|
10828
11255
|
});
|
|
10829
11256
|
|
|
10830
11257
|
// packages/cli/src/commands/telegram.ts
|
|
10831
11258
|
init_dist();
|
|
10832
11259
|
init_dist2();
|
|
10833
|
-
import { join as
|
|
11260
|
+
import { join as join25, dirname as dirname7 } from "path";
|
|
10834
11261
|
import { emitKeypressEvents } from "readline";
|
|
10835
|
-
import { Command as
|
|
10836
|
-
import
|
|
11262
|
+
import { Command as Command29 } from "commander";
|
|
11263
|
+
import chalk29 from "chalk";
|
|
10837
11264
|
function defaultStateRoot() {
|
|
10838
|
-
return
|
|
11265
|
+
return join25(dirname7(DEFAULT_CONFIG_PATH), "state");
|
|
10839
11266
|
}
|
|
10840
11267
|
async function questionMasked() {
|
|
10841
11268
|
return new Promise((resolve3) => {
|
|
@@ -10880,11 +11307,11 @@ async function questionYesNo(prompt) {
|
|
|
10880
11307
|
});
|
|
10881
11308
|
});
|
|
10882
11309
|
}
|
|
10883
|
-
var telegramCommand = new
|
|
11310
|
+
var telegramCommand = new Command29("telegram").description("Two-way Telegram integration: push crew events to a topic and reply into the captain pane");
|
|
10884
11311
|
telegramCommand.command("status").description("Show Telegram config and linked projects").action(() => {
|
|
10885
11312
|
const { tokenSet, supergroupId, links } = runTelegramStatus({ config: loadConfig(), stateRoot: defaultStateRoot() });
|
|
10886
|
-
console.log(`token: ${tokenSet ?
|
|
10887
|
-
console.log(`supergroup: ${supergroupId ??
|
|
11313
|
+
console.log(`token: ${tokenSet ? chalk29.green("set") : chalk29.yellow("unset")}`);
|
|
11314
|
+
console.log(`supergroup: ${supergroupId ?? chalk29.yellow("unset")}`);
|
|
10888
11315
|
if (links.length === 0) {
|
|
10889
11316
|
console.log("no projects linked");
|
|
10890
11317
|
return;
|
|
@@ -10894,32 +11321,32 @@ telegramCommand.command("status").description("Show Telegram config and linked p
|
|
|
10894
11321
|
telegramCommand.command("link").argument("<project>", "project to bind to a Telegram topic").description("Create (or reuse) a forum topic for a project and bind it").action(async (project) => {
|
|
10895
11322
|
const cfg = loadConfig().telegram;
|
|
10896
11323
|
if (!cfg) {
|
|
10897
|
-
console.error(
|
|
11324
|
+
console.error(chalk29.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
|
|
10898
11325
|
process.exit(1);
|
|
10899
11326
|
}
|
|
10900
11327
|
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
10901
11328
|
if (!token) {
|
|
10902
|
-
console.error(
|
|
11329
|
+
console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
|
|
10903
11330
|
process.exit(1);
|
|
10904
11331
|
}
|
|
10905
11332
|
const client = createTelegramClient({ token });
|
|
10906
11333
|
const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
|
|
10907
|
-
console.log(
|
|
11334
|
+
console.log(chalk29.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
|
|
10908
11335
|
});
|
|
10909
11336
|
telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
10910
11337
|
if (!process.stdin.isTTY) {
|
|
10911
|
-
console.error(
|
|
11338
|
+
console.error(chalk29.red("setup requires a TTY \u2014 pipe input is not supported"));
|
|
10912
11339
|
process.exit(1);
|
|
10913
11340
|
}
|
|
10914
11341
|
console.log();
|
|
10915
|
-
console.log(
|
|
11342
|
+
console.log(chalk29.bold("Telegram setup") + " \u2014 connect squadrant to a Telegram bot for notifications + remote control");
|
|
10916
11343
|
console.log();
|
|
10917
11344
|
console.log("Before you start you need:");
|
|
10918
11345
|
console.log(" 1. A bot token from @BotFather (send /newbot)");
|
|
10919
11346
|
console.log(" 2. A forum supergroup with the bot added as an admin (Topics enabled)");
|
|
10920
11347
|
console.log(" 3. Bot privacy mode set to OFF (@BotFather \u2192 /setprivacy \u2192 Disable)");
|
|
10921
11348
|
console.log();
|
|
10922
|
-
console.log(
|
|
11349
|
+
console.log(chalk29.bold("Step 1/3 \u2014 Bot token"));
|
|
10923
11350
|
const existingCfg = loadConfig().telegram;
|
|
10924
11351
|
const existingToken = existingCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
10925
11352
|
const decision = resolveSetupToken(existingToken, { resetToken: opts.resetToken ?? false });
|
|
@@ -10931,67 +11358,67 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
|
|
|
10931
11358
|
try {
|
|
10932
11359
|
botUser = await client.getMe();
|
|
10933
11360
|
token = existingToken;
|
|
10934
|
-
console.log(
|
|
11361
|
+
console.log(chalk29.green(`Using existing bot token (@${botUser.username})`));
|
|
10935
11362
|
console.log();
|
|
10936
11363
|
} catch {
|
|
10937
|
-
console.log(
|
|
11364
|
+
console.log(chalk29.yellow("Existing token is invalid \u2014 please enter a new one."));
|
|
10938
11365
|
console.log("Paste your bot token then press Enter (input is hidden):");
|
|
10939
11366
|
token = await questionMasked();
|
|
10940
11367
|
if (!token) {
|
|
10941
|
-
console.error(
|
|
11368
|
+
console.error(chalk29.red("token required"));
|
|
10942
11369
|
process.exit(1);
|
|
10943
11370
|
}
|
|
10944
11371
|
client = createTelegramClient({ token });
|
|
10945
11372
|
try {
|
|
10946
11373
|
botUser = await client.getMe();
|
|
10947
11374
|
} catch (e) {
|
|
10948
|
-
console.error(
|
|
11375
|
+
console.error(chalk29.red(`token rejected: ${e.message}`));
|
|
10949
11376
|
process.exit(1);
|
|
10950
11377
|
}
|
|
10951
|
-
console.log(
|
|
11378
|
+
console.log(chalk29.green(`Connected as @${botUser.username}`));
|
|
10952
11379
|
console.log();
|
|
10953
11380
|
}
|
|
10954
11381
|
} else {
|
|
10955
11382
|
console.log("Paste your bot token then press Enter (input is hidden):");
|
|
10956
11383
|
token = await questionMasked();
|
|
10957
11384
|
if (!token) {
|
|
10958
|
-
console.error(
|
|
11385
|
+
console.error(chalk29.red("token required"));
|
|
10959
11386
|
process.exit(1);
|
|
10960
11387
|
}
|
|
10961
11388
|
client = createTelegramClient({ token });
|
|
10962
11389
|
try {
|
|
10963
11390
|
botUser = await client.getMe();
|
|
10964
11391
|
} catch (e) {
|
|
10965
|
-
console.error(
|
|
11392
|
+
console.error(chalk29.red(`token rejected: ${e.message}`));
|
|
10966
11393
|
process.exit(1);
|
|
10967
11394
|
}
|
|
10968
|
-
console.log(
|
|
11395
|
+
console.log(chalk29.green(`Connected as @${botUser.username}`));
|
|
10969
11396
|
console.log();
|
|
10970
11397
|
}
|
|
10971
|
-
console.log(
|
|
11398
|
+
console.log(chalk29.bold("Step 2/3 \u2014 Supergroup"));
|
|
10972
11399
|
const groupDecision = resolveSetupGroup(existingCfg?.supergroupId, { redetect: opts.redetect ?? false });
|
|
10973
11400
|
let supergroupId;
|
|
10974
11401
|
let detectedUserId;
|
|
10975
11402
|
if (groupDecision === "reuse") {
|
|
10976
11403
|
supergroupId = existingCfg.supergroupId;
|
|
10977
|
-
console.log(
|
|
11404
|
+
console.log(chalk29.green(`Using existing group: ${supergroupId}`));
|
|
10978
11405
|
console.log();
|
|
10979
11406
|
} else {
|
|
10980
11407
|
console.log("Add the bot to your forum supergroup, then send any message in it.");
|
|
10981
|
-
console.log(
|
|
11408
|
+
console.log(chalk29.dim("Waiting for a message (up to 60s)\u2026"));
|
|
10982
11409
|
try {
|
|
10983
11410
|
({ supergroupId, userId: detectedUserId } = await detectGroupAndUser(client, { timeoutMs: 6e4 }));
|
|
10984
11411
|
} catch {
|
|
10985
|
-
console.error(
|
|
10986
|
-
console.error(
|
|
11412
|
+
console.error(chalk29.red("Timed out \u2014 no supergroup message received within 60s."));
|
|
11413
|
+
console.error(chalk29.yellow("Check: bot is an admin in the group \xB7 privacy mode is OFF \xB7 Topics enabled"));
|
|
10987
11414
|
process.exit(1);
|
|
10988
11415
|
}
|
|
10989
|
-
console.log(
|
|
11416
|
+
console.log(chalk29.green(`Found group: ${supergroupId}`));
|
|
10990
11417
|
console.log();
|
|
10991
11418
|
}
|
|
10992
|
-
console.log(
|
|
10993
|
-
console.log(
|
|
10994
|
-
console.log(
|
|
11419
|
+
console.log(chalk29.bold("Step 3/3 \u2014 Remote control + Save"));
|
|
11420
|
+
console.log(chalk29.dim("Remote control enables auto-launching captains and the General command channel"));
|
|
11421
|
+
console.log(chalk29.dim("from your phone \u2014 gated to your Telegram user-id only (fail-closed)."));
|
|
10995
11422
|
const finalUserId = resolveSetupUserId(opts.userId, detectedUserId, defaultStateRoot());
|
|
10996
11423
|
let users;
|
|
10997
11424
|
let remoteControl;
|
|
@@ -11005,32 +11432,32 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
|
|
|
11005
11432
|
remoteControl = true;
|
|
11006
11433
|
}
|
|
11007
11434
|
} else if (groupDecision === "detect") {
|
|
11008
|
-
console.log(
|
|
11009
|
-
console.log(
|
|
11435
|
+
console.log(chalk29.yellow("Could not read your user-id from that message \u2014 skipping remote control."));
|
|
11436
|
+
console.log(chalk29.yellow("Re-run with --user-id <id> to enable, or edit telegram.users in config manually."));
|
|
11010
11437
|
printedRemoteControlState = true;
|
|
11011
11438
|
} else {
|
|
11012
11439
|
const existingUsers = existingCfg?.users;
|
|
11013
11440
|
if (existingUsers && existingUsers.length > 0) {
|
|
11014
|
-
console.log(
|
|
11441
|
+
console.log(chalk29.dim(`Remote control: already configured (user-id ${existingUsers[0]}). Use --user-id to update.`));
|
|
11015
11442
|
} else {
|
|
11016
|
-
console.log(
|
|
11443
|
+
console.log(chalk29.dim("Remote control: off. Re-run with --user-id <id> to enable."));
|
|
11017
11444
|
}
|
|
11018
11445
|
printedRemoteControlState = true;
|
|
11019
11446
|
}
|
|
11020
11447
|
writeTelegramConfig(DEFAULT_CONFIG_PATH, { token, supergroupId, users, remoteControl });
|
|
11021
|
-
console.log(
|
|
11448
|
+
console.log(chalk29.green(`Wrote telegram config \u2014 token: ${maskToken(token)} group: ${supergroupId}`));
|
|
11022
11449
|
if (!printedRemoteControlState) {
|
|
11023
11450
|
if (remoteControl) {
|
|
11024
|
-
console.log(
|
|
11451
|
+
console.log(chalk29.green(`Remote control: ON (allowlisted user-id ${users[0]})`));
|
|
11025
11452
|
} else {
|
|
11026
|
-
console.log(
|
|
11453
|
+
console.log(chalk29.dim("Remote control: off (default). Re-run with --user-id <id> to enable."));
|
|
11027
11454
|
}
|
|
11028
11455
|
}
|
|
11029
11456
|
try {
|
|
11030
11457
|
await runRegisterCommands({ client });
|
|
11031
|
-
console.log(
|
|
11458
|
+
console.log(chalk29.dim("Registered the /command menu."));
|
|
11032
11459
|
} catch (e) {
|
|
11033
|
-
console.log(
|
|
11460
|
+
console.log(chalk29.yellow(`command-menu registration skipped: ${e.message}`));
|
|
11034
11461
|
}
|
|
11035
11462
|
const topics = loadState(defaultStateRoot()).topics;
|
|
11036
11463
|
const topicEntries = Object.entries(topics);
|
|
@@ -11039,28 +11466,28 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
|
|
|
11039
11466
|
const project = key.slice(0, key.indexOf("::"));
|
|
11040
11467
|
return `${project}\u2192${id}`;
|
|
11041
11468
|
}).join(", ");
|
|
11042
|
-
console.log(
|
|
11469
|
+
console.log(chalk29.dim(`Existing topics: ${summary} (already created \u2014 not recreated)`));
|
|
11043
11470
|
} else {
|
|
11044
|
-
console.log(
|
|
11471
|
+
console.log(chalk29.dim("No project topics yet \u2014 they're created on first delivery or via: squadrant telegram link <project>"));
|
|
11045
11472
|
}
|
|
11046
11473
|
runTelegramPostSetup({});
|
|
11047
11474
|
console.log();
|
|
11048
|
-
console.log(`Next: ${
|
|
11475
|
+
console.log(`Next: ${chalk29.cyan("squadrant telegram link <project>")}`);
|
|
11049
11476
|
});
|
|
11050
11477
|
telegramCommand.command("register-commands").description("Register (or re-register) the bot's / command menu with Telegram").action(async () => {
|
|
11051
11478
|
const cfg = loadConfig().telegram;
|
|
11052
11479
|
if (!cfg) {
|
|
11053
|
-
console.error(
|
|
11480
|
+
console.error(chalk29.red("telegram config absent \u2014 run: squadrant telegram setup"));
|
|
11054
11481
|
process.exit(1);
|
|
11055
11482
|
}
|
|
11056
11483
|
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
11057
11484
|
if (!token) {
|
|
11058
|
-
console.error(
|
|
11485
|
+
console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
|
|
11059
11486
|
process.exit(1);
|
|
11060
11487
|
}
|
|
11061
11488
|
const client = createTelegramClient({ token });
|
|
11062
11489
|
await runRegisterCommands({ client });
|
|
11063
|
-
console.log(
|
|
11490
|
+
console.log(chalk29.green(`registered ${BOT_COMMANDS.length} bot commands`));
|
|
11064
11491
|
});
|
|
11065
11492
|
telegramCommand.command("notify").argument("[project]", "project to toggle").argument("[state]", "on | off | crew | cap").argument("[value]", "tier for crew (all|alert_only|done_only|none) or on|off for cap").option("--status", "list notification state for all projects").description("Live on|off (state), or crew <tier> / cap <on|off> preference (per-project config)").action(async (project, state, value, opts) => {
|
|
11066
11493
|
const stateRoot = defaultStateRoot();
|
|
@@ -11071,7 +11498,7 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
|
|
|
11071
11498
|
return;
|
|
11072
11499
|
}
|
|
11073
11500
|
for (const r of rows) {
|
|
11074
|
-
console.log(` ${r.project}: ${r.active ?
|
|
11501
|
+
console.log(` ${r.project}: ${r.active ? chalk29.green("on") : chalk29.dim("off (muted)")}`);
|
|
11075
11502
|
}
|
|
11076
11503
|
return;
|
|
11077
11504
|
}
|
|
@@ -11080,53 +11507,53 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
|
|
|
11080
11507
|
const token = tgCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
11081
11508
|
if (state === "crew" || state === "cap") {
|
|
11082
11509
|
if (value === void 0) {
|
|
11083
|
-
console.error(
|
|
11510
|
+
console.error(chalk29.red(`usage: squadrant telegram notify <project> ${state} <value>`));
|
|
11084
11511
|
process.exit(1);
|
|
11085
11512
|
}
|
|
11086
11513
|
const resolved2 = resolveNotify(globalNotify, loadProjectOverride(project));
|
|
11087
11514
|
const before2 = { ...resolved2, active: isNotifyActive(stateRoot, project) };
|
|
11088
11515
|
const res = runTelegramNotifyPref({ project, dimension: state, value });
|
|
11089
11516
|
if (!res.ok) {
|
|
11090
|
-
console.error(
|
|
11517
|
+
console.error(chalk29.red(res.message));
|
|
11091
11518
|
process.exit(1);
|
|
11092
11519
|
}
|
|
11093
|
-
console.log(
|
|
11520
|
+
console.log(chalk29.green(`${project} ${state} = ${value}`));
|
|
11094
11521
|
const after2 = state === "crew" ? { ...before2, crew: value } : { ...before2, cap: value === "on" };
|
|
11095
11522
|
if (tgCfg && token) {
|
|
11096
11523
|
const client = createTelegramClient({ token });
|
|
11097
11524
|
const sent = await runNotifyConfirmation({ project, before: before2, after: after2, cfg: tgCfg, client, stateRoot });
|
|
11098
|
-
if (sent) console.log(
|
|
11525
|
+
if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
|
|
11099
11526
|
}
|
|
11100
11527
|
return;
|
|
11101
11528
|
}
|
|
11102
11529
|
if (state !== "on" && state !== "off") {
|
|
11103
|
-
console.error(
|
|
11530
|
+
console.error(chalk29.red("usage: squadrant telegram notify <project> <on|off|crew <tier>|cap <on|off>>"));
|
|
11104
11531
|
process.exit(1);
|
|
11105
11532
|
}
|
|
11106
11533
|
const resolved = resolveNotify(globalNotify, loadProjectOverride(project));
|
|
11107
11534
|
const before = { ...resolved, active: isNotifyActive(stateRoot, project) };
|
|
11108
11535
|
const after = { ...before, active: state === "on" };
|
|
11109
11536
|
runTelegramNotifySet({ project, active: state === "on", stateRoot });
|
|
11110
|
-
console.log(
|
|
11537
|
+
console.log(chalk29.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
|
|
11111
11538
|
if (tgCfg && token) {
|
|
11112
11539
|
const client = createTelegramClient({ token });
|
|
11113
11540
|
const sent = await runNotifyConfirmation({ project, before, after, cfg: tgCfg, client, stateRoot });
|
|
11114
|
-
if (sent) console.log(
|
|
11541
|
+
if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
|
|
11115
11542
|
}
|
|
11116
11543
|
});
|
|
11117
11544
|
telegramCommand.command("send").argument("<project>", "project whose topic receives the message").argument("[message...]", "message text (omit to read from stdin)").description("Send a message to a project's linked Telegram topic").action(async (project, messageParts) => {
|
|
11118
11545
|
const cfg = loadConfig().telegram;
|
|
11119
11546
|
if (!cfg) {
|
|
11120
|
-
console.error(
|
|
11547
|
+
console.error(chalk29.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
|
|
11121
11548
|
process.exit(1);
|
|
11122
11549
|
}
|
|
11123
11550
|
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
11124
11551
|
if (!token) {
|
|
11125
|
-
console.error(
|
|
11552
|
+
console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
|
|
11126
11553
|
process.exit(1);
|
|
11127
11554
|
}
|
|
11128
11555
|
if (!capAllowed(project, cfg.notify)) {
|
|
11129
|
-
console.log(
|
|
11556
|
+
console.log(chalk29.dim(`${project}: captain messages muted (cap=off) \u2014 not sent`));
|
|
11130
11557
|
return;
|
|
11131
11558
|
}
|
|
11132
11559
|
let message;
|
|
@@ -11139,19 +11566,19 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
11139
11566
|
for await (const line of rl) lines.push(line);
|
|
11140
11567
|
message = lines.join("\n").trimEnd();
|
|
11141
11568
|
if (!message) {
|
|
11142
|
-
console.error(
|
|
11569
|
+
console.error(chalk29.red("no message provided (stdin was empty)"));
|
|
11143
11570
|
process.exit(1);
|
|
11144
11571
|
}
|
|
11145
11572
|
} else {
|
|
11146
|
-
console.error(
|
|
11573
|
+
console.error(chalk29.red("message required \u2014 pass as argument or pipe via stdin"));
|
|
11147
11574
|
process.exit(1);
|
|
11148
11575
|
}
|
|
11149
11576
|
const client = createTelegramClient({ token });
|
|
11150
11577
|
try {
|
|
11151
11578
|
const { chatId, topicId } = await runTelegramSend({ project, message, cfg, client, stateRoot: defaultStateRoot() });
|
|
11152
|
-
console.log(
|
|
11579
|
+
console.log(chalk29.green(`sent to group ${chatId} topic ${topicId}`));
|
|
11153
11580
|
} catch (e) {
|
|
11154
|
-
console.error(
|
|
11581
|
+
console.error(chalk29.red(e.message));
|
|
11155
11582
|
process.exit(1);
|
|
11156
11583
|
}
|
|
11157
11584
|
});
|
|
@@ -11159,10 +11586,10 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
11159
11586
|
// packages/cli/src/commands/hooks.ts
|
|
11160
11587
|
init_dist2();
|
|
11161
11588
|
init_dist4();
|
|
11162
|
-
import { Command as
|
|
11163
|
-
import { join as
|
|
11164
|
-
import { homedir as
|
|
11165
|
-
var SOCK4 =
|
|
11589
|
+
import { Command as Command30 } from "commander";
|
|
11590
|
+
import { join as join26 } from "path";
|
|
11591
|
+
import { homedir as homedir19 } from "os";
|
|
11592
|
+
var SOCK4 = join26(homedir19(), ".config", "squadrant", "squadrant.sock");
|
|
11166
11593
|
async function sendToSock(req) {
|
|
11167
11594
|
await sendRequest(SOCK4, req);
|
|
11168
11595
|
}
|
|
@@ -11188,7 +11615,7 @@ function mapHookSub(sub, payload, taskId) {
|
|
|
11188
11615
|
}
|
|
11189
11616
|
}
|
|
11190
11617
|
function hooksCommand() {
|
|
11191
|
-
const hooks = new
|
|
11618
|
+
const hooks = new Command30("hooks").description("(internal) receive lifecycle hook events from agent processes");
|
|
11192
11619
|
hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
|
|
11193
11620
|
const taskId = process.env.SQUADRANT_CREW_TASK_ID;
|
|
11194
11621
|
const project = process.env.SQUADRANT_CREW_PROJECT;
|
|
@@ -11224,21 +11651,21 @@ function hooksCommand() {
|
|
|
11224
11651
|
init_dist();
|
|
11225
11652
|
init_dist();
|
|
11226
11653
|
init_dist();
|
|
11227
|
-
var __dirname =
|
|
11228
|
-
var pkg = JSON.parse(
|
|
11654
|
+
var __dirname = dirname8(fileURLToPath6(import.meta.url));
|
|
11655
|
+
var pkg = JSON.parse(readFileSync15(join27(__dirname, "..", "package.json"), "utf-8"));
|
|
11229
11656
|
ensureRuntimeSynced({
|
|
11230
|
-
sourceRoot:
|
|
11231
|
-
runtimeRoot:
|
|
11657
|
+
sourceRoot: join27(__dirname, ".."),
|
|
11658
|
+
runtimeRoot: join27(homedir20(), ".config", "squadrant")
|
|
11232
11659
|
});
|
|
11233
11660
|
if (process.argv[2] !== "config") {
|
|
11234
11661
|
try {
|
|
11235
|
-
const cfgPath =
|
|
11662
|
+
const cfgPath = join27(homedir20(), ".config", "squadrant", "config.json");
|
|
11236
11663
|
if (existsSync12(cfgPath)) {
|
|
11237
|
-
const cfg = JSON.parse(
|
|
11664
|
+
const cfg = JSON.parse(readFileSync15(cfgPath, "utf-8"));
|
|
11238
11665
|
if (needsCheck(cfg, pkg.version)) {
|
|
11239
11666
|
const items = detectDrift(cfg, getDefaultConfig());
|
|
11240
11667
|
if (items.length === 0) {
|
|
11241
|
-
|
|
11668
|
+
writeFileSync11(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
|
|
11242
11669
|
} else {
|
|
11243
11670
|
const from = cfg._squadrantVersion ?? "an earlier version";
|
|
11244
11671
|
process.stderr.write(
|
|
@@ -11257,7 +11684,7 @@ if (process.argv[2] !== "config") {
|
|
|
11257
11684
|
if (!process.env.SQUADRANT_DAEMON_SKIP) {
|
|
11258
11685
|
ensureDaemon();
|
|
11259
11686
|
}
|
|
11260
|
-
var program = new
|
|
11687
|
+
var program = new Command31();
|
|
11261
11688
|
program.name("squadrant").description("Multi-project orchestration for your coding agents (Claude, Codex, opencode, Gemini)").version(pkg.version);
|
|
11262
11689
|
program.addCommand(doctorCommand);
|
|
11263
11690
|
program.addCommand(initCommand);
|
|
@@ -11281,6 +11708,8 @@ program.addCommand(codexChatSmokeCommand);
|
|
|
11281
11708
|
program.addCommand(configCommand);
|
|
11282
11709
|
program.addCommand(healCommand);
|
|
11283
11710
|
program.addCommand(groupCommand);
|
|
11711
|
+
program.addCommand(pingCommand);
|
|
11712
|
+
program.addCommand(dispatchCommand);
|
|
11284
11713
|
program.addCommand(cmuxCommand);
|
|
11285
11714
|
program.addCommand(effortCommand);
|
|
11286
11715
|
program.addCommand(telegramCommand);
|