squadrant 0.21.1 → 0.23.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 +1666 -343
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +1004 -193
- package/dist/squadrantd.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -98,6 +98,9 @@ function isGateMode(v2) {
|
|
|
98
98
|
function isGatePolicy(v2) {
|
|
99
99
|
return GATE_POLICIES.includes(v2);
|
|
100
100
|
}
|
|
101
|
+
function isGateEngine(v2) {
|
|
102
|
+
return GATE_ENGINES.includes(v2);
|
|
103
|
+
}
|
|
101
104
|
function isThinkingLevel(v2) {
|
|
102
105
|
return THINKING_LEVELS.includes(v2);
|
|
103
106
|
}
|
|
@@ -156,12 +159,7 @@ function getDefaultConfig() {
|
|
|
156
159
|
// resolver fallback is the safety net).
|
|
157
160
|
controlChannel: { claude: "on", opencode: "on" },
|
|
158
161
|
crewRouting: {
|
|
159
|
-
rules:
|
|
160
|
-
{ tier: "extreme", match: "redesign|architect|rewrite|from scratch|deep reasoning", agent: "claude", model: "opus" },
|
|
161
|
-
{ tier: "hard", match: "refactor|migrate|implement|feature|daemon|control-plane", agent: "claude", model: "sonnet" },
|
|
162
|
-
{ tier: "mobile", match: "mobile|ios|swift|android|kotlin|react native", agent: "codex" },
|
|
163
|
-
{ tier: "daily", match: "typo|rename|bump|docs|comment|lint|format", agent: "opencode" }
|
|
164
|
-
]
|
|
162
|
+
rules: DEFAULT_CREW_ROUTING_RULES.map((r) => ({ ...r }))
|
|
165
163
|
}
|
|
166
164
|
},
|
|
167
165
|
metrics: {
|
|
@@ -203,18 +201,214 @@ function saveConfig(config, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
203
201
|
function resolveHome(p) {
|
|
204
202
|
return p.startsWith("~") ? p.replace("~", os.homedir()) : p;
|
|
205
203
|
}
|
|
206
|
-
var ROUTER_KINDS, GATE_MODES, GATE_POLICIES, THINKING_LEVELS, CONTROL_CHANNEL_MODES, DEFAULT_CONFIG_PATH, CONFIG_DIR, DAEMON_SOCK_PATH;
|
|
204
|
+
var ROUTER_KINDS, GATE_MODES, GATE_POLICIES, GATE_ENGINES, THINKING_LEVELS, CONTROL_CHANNEL_MODES, DEFAULT_CONFIG_PATH, CONFIG_DIR, DAEMON_SOCK_PATH, DEFAULT_CREW_ROUTING_RULES;
|
|
207
205
|
var init_config = __esm({
|
|
208
206
|
"packages/shared/dist/config.js"() {
|
|
209
207
|
init_config_io();
|
|
210
208
|
ROUTER_KINDS = ["opencode-go", "openrouter", "ccr", "litellm", "custom"];
|
|
211
209
|
GATE_MODES = ["on", "off", "auto"];
|
|
212
210
|
GATE_POLICIES = ["deny-dangerous", "ask-on-doubt"];
|
|
211
|
+
GATE_ENGINES = ["router", "auto-gate"];
|
|
213
212
|
THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
214
213
|
CONTROL_CHANNEL_MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
215
214
|
DEFAULT_CONFIG_PATH = process.env.SQUADRANT_CONFIG || path2.join(os.homedir(), ".config", "squadrant", "config.json");
|
|
216
215
|
CONFIG_DIR = path2.dirname(DEFAULT_CONFIG_PATH);
|
|
217
216
|
DAEMON_SOCK_PATH = path2.join(CONFIG_DIR, "squadrant.sock");
|
|
217
|
+
DEFAULT_CREW_ROUTING_RULES = [
|
|
218
|
+
{ tier: "extreme", match: "redesign|architect|rewrite|from scratch|deep reasoning", agent: "claude", model: "opus" },
|
|
219
|
+
{ tier: "hard", match: "refactor|migrate|implement|feature|daemon|control-plane", agent: "claude", model: "sonnet" },
|
|
220
|
+
{ tier: "mobile", match: "mobile|ios|swift|android|kotlin|react native", agent: "codex" },
|
|
221
|
+
{ tier: "daily", match: "typo|rename|bump|docs|comment|lint|format", agent: "opencode" }
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// packages/shared/dist/provider-preset.js
|
|
227
|
+
function isProviderPresetId(v2) {
|
|
228
|
+
return v2 === "a" || v2 === "b" || v2 === "c" || v2 === "d";
|
|
229
|
+
}
|
|
230
|
+
function presetCrewRouting(id) {
|
|
231
|
+
const base = (tier) => {
|
|
232
|
+
const found = DEFAULT_CREW_ROUTING_RULES.find((r) => r.tier === tier);
|
|
233
|
+
return found ? { ...found } : { tier, match: "", agent: "claude" };
|
|
234
|
+
};
|
|
235
|
+
const codex = (tier) => ({
|
|
236
|
+
tier: base(tier).tier,
|
|
237
|
+
match: base(tier).match,
|
|
238
|
+
agent: "codex"
|
|
239
|
+
});
|
|
240
|
+
switch (id) {
|
|
241
|
+
case "a":
|
|
242
|
+
return DEFAULT_CREW_ROUTING_RULES.map((r) => ({ ...r }));
|
|
243
|
+
case "b":
|
|
244
|
+
return [
|
|
245
|
+
{ ...base("extreme"), agent: "opencode", model: OPENCODE_MODEL },
|
|
246
|
+
{ ...base("hard"), agent: "opencode", model: OPENCODE_MODEL },
|
|
247
|
+
codex("mobile"),
|
|
248
|
+
{ ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
|
|
249
|
+
];
|
|
250
|
+
case "c":
|
|
251
|
+
return [
|
|
252
|
+
{ ...base("extreme"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
|
|
253
|
+
{ ...base("hard"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
|
|
254
|
+
codex("mobile"),
|
|
255
|
+
{ ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
|
|
256
|
+
];
|
|
257
|
+
case "d":
|
|
258
|
+
return ["extreme", "hard", "mobile", "daily"].map(codex);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function providerPresetDefaults(id, router) {
|
|
262
|
+
switch (id) {
|
|
263
|
+
case "a":
|
|
264
|
+
return {
|
|
265
|
+
roles: {
|
|
266
|
+
command: { agent: "claude", model: "opus" },
|
|
267
|
+
captain: { agent: "claude", model: "opus" },
|
|
268
|
+
crew: { agent: "claude", model: "sonnet" },
|
|
269
|
+
exploration: { agent: "claude", model: "haiku" },
|
|
270
|
+
side: { agent: "claude", model: "opus" }
|
|
271
|
+
},
|
|
272
|
+
permissions: { command: "auto", captain: "auto", crew: "auto" },
|
|
273
|
+
crewRouting: { rules: presetCrewRouting("a") }
|
|
274
|
+
};
|
|
275
|
+
case "b": {
|
|
276
|
+
const opencode = { agent: "opencode", model: OPENCODE_MODEL };
|
|
277
|
+
return {
|
|
278
|
+
roles: {
|
|
279
|
+
command: { ...opencode },
|
|
280
|
+
captain: { ...opencode },
|
|
281
|
+
crew: { ...opencode },
|
|
282
|
+
exploration: { ...opencode },
|
|
283
|
+
side: { ...opencode }
|
|
284
|
+
},
|
|
285
|
+
permissions: { command: "auto", captain: "auto", crew: "auto" },
|
|
286
|
+
crewRouting: { rules: presetCrewRouting("b") }
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
case "c": {
|
|
290
|
+
if (!router) {
|
|
291
|
+
throw new Error("preset C (claude harness + router backend) requires a router config \u2014 set defaults.router first");
|
|
292
|
+
}
|
|
293
|
+
const routed = { agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL };
|
|
294
|
+
return {
|
|
295
|
+
roles: {
|
|
296
|
+
command: { ...routed },
|
|
297
|
+
captain: { ...routed },
|
|
298
|
+
crew: { ...routed },
|
|
299
|
+
exploration: { ...routed },
|
|
300
|
+
side: { ...routed }
|
|
301
|
+
},
|
|
302
|
+
// Auto mode's classifier is hardcoded to Sonnet 5 and fails closed on a
|
|
303
|
+
// router upstream — every claude session must use a manual mode so the
|
|
304
|
+
// U7 gate owns the prompt.
|
|
305
|
+
permissions: { command: "default", captain: "default", crew: "default" },
|
|
306
|
+
crewRouting: { rules: presetCrewRouting("c") },
|
|
307
|
+
router,
|
|
308
|
+
gate: { mode: "on" }
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
case "d": {
|
|
312
|
+
const codex = { agent: "codex" };
|
|
313
|
+
return {
|
|
314
|
+
roles: {
|
|
315
|
+
command: { ...codex },
|
|
316
|
+
captain: { ...codex },
|
|
317
|
+
crew: { ...codex },
|
|
318
|
+
exploration: { ...codex },
|
|
319
|
+
side: { ...codex }
|
|
320
|
+
},
|
|
321
|
+
permissions: {},
|
|
322
|
+
crewRouting: { rules: presetCrewRouting("d") }
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function detectProviderPresetId(config) {
|
|
328
|
+
const crew = config.defaults?.roles?.crew;
|
|
329
|
+
if (crew?.agent === "opencode")
|
|
330
|
+
return "b";
|
|
331
|
+
if (crew?.agent === "codex")
|
|
332
|
+
return "d";
|
|
333
|
+
if (crew?.backend && crew.backend !== "native")
|
|
334
|
+
return "c";
|
|
335
|
+
return "a";
|
|
336
|
+
}
|
|
337
|
+
function applyProviderPreset(config, id, opts) {
|
|
338
|
+
const preset = providerPresetDefaults(id, opts.router);
|
|
339
|
+
const next = structuredClone(config);
|
|
340
|
+
const changes = [];
|
|
341
|
+
if (opts.overwrite) {
|
|
342
|
+
next.defaults.roles = { ...preset.roles };
|
|
343
|
+
changes.push("defaults.roles");
|
|
344
|
+
} else if (next.defaults.roles === void 0) {
|
|
345
|
+
next.defaults.roles = { ...preset.roles };
|
|
346
|
+
changes.push("defaults.roles");
|
|
347
|
+
}
|
|
348
|
+
if (opts.overwrite) {
|
|
349
|
+
next.defaults.crewRouting = { rules: preset.crewRouting.rules.map((r) => ({ ...r })) };
|
|
350
|
+
changes.push("defaults.crewRouting");
|
|
351
|
+
} else if (next.defaults.crewRouting === void 0) {
|
|
352
|
+
next.defaults.crewRouting = { rules: preset.crewRouting.rules.map((r) => ({ ...r })) };
|
|
353
|
+
changes.push("defaults.crewRouting");
|
|
354
|
+
}
|
|
355
|
+
const existingPerms = next.defaults.permissions ?? {};
|
|
356
|
+
if (opts.overwrite) {
|
|
357
|
+
next.defaults.permissions = { ...existingPerms, ...preset.permissions };
|
|
358
|
+
if (Object.keys(preset.permissions).length > 0)
|
|
359
|
+
changes.push("defaults.permissions");
|
|
360
|
+
} else if (next.defaults.permissions === void 0 && Object.keys(preset.permissions).length > 0) {
|
|
361
|
+
next.defaults.permissions = { ...preset.permissions };
|
|
362
|
+
changes.push("defaults.permissions");
|
|
363
|
+
}
|
|
364
|
+
if (preset.router) {
|
|
365
|
+
if (opts.overwrite || next.defaults.router === void 0) {
|
|
366
|
+
next.defaults.router = structuredClone(preset.router);
|
|
367
|
+
changes.push("defaults.router");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (preset.gate) {
|
|
371
|
+
if (opts.overwrite || next.defaults.gate === void 0) {
|
|
372
|
+
next.defaults.gate = { ...preset.gate };
|
|
373
|
+
changes.push("defaults.gate");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return { config: next, changes };
|
|
377
|
+
}
|
|
378
|
+
var PROVIDER_PRESETS, ROUTER_PRESET_MODEL, DEFAULT_ROUTER_KIND, DEFAULT_ROUTER_BASE_URL, OPENCODE_MODEL;
|
|
379
|
+
var init_provider_preset = __esm({
|
|
380
|
+
"packages/shared/dist/provider-preset.js"() {
|
|
381
|
+
init_config();
|
|
382
|
+
PROVIDER_PRESETS = [
|
|
383
|
+
{
|
|
384
|
+
id: "a",
|
|
385
|
+
label: "Claude Code (Pro/Max)",
|
|
386
|
+
summary: "Claude harness on your subscription/API key. Real auto mode.",
|
|
387
|
+
requiresAnthropic: true
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "b",
|
|
391
|
+
label: "opencode (no Anthropic subscription)",
|
|
392
|
+
summary: "All roles run on the opencode harness. Auto mode is model-agnostic.",
|
|
393
|
+
requiresAnthropic: false
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: "c",
|
|
397
|
+
label: "Claude harness + router backend (advanced)",
|
|
398
|
+
summary: "Claude harness against a router upstream. The U7 gate replaces auto mode.",
|
|
399
|
+
requiresAnthropic: false
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: "d",
|
|
403
|
+
label: "Codex (ChatGPT Pro)",
|
|
404
|
+
summary: "All roles run on the codex harness.",
|
|
405
|
+
requiresAnthropic: false
|
|
406
|
+
}
|
|
407
|
+
];
|
|
408
|
+
ROUTER_PRESET_MODEL = "deepseek-v4.1-flash";
|
|
409
|
+
DEFAULT_ROUTER_KIND = "opencode-go";
|
|
410
|
+
DEFAULT_ROUTER_BASE_URL = "https://opencode.ai/zen/go";
|
|
411
|
+
OPENCODE_MODEL = "opencode-go/deepseek-v4.1-flash";
|
|
218
412
|
}
|
|
219
413
|
});
|
|
220
414
|
|
|
@@ -1465,11 +1659,17 @@ __export(dist_exports, {
|
|
|
1465
1659
|
CREW_SKILLS: () => CREW_SKILLS,
|
|
1466
1660
|
DAEMON_SOCK_PATH: () => DAEMON_SOCK_PATH,
|
|
1467
1661
|
DEFAULT_CONFIG_PATH: () => DEFAULT_CONFIG_PATH,
|
|
1662
|
+
DEFAULT_CREW_ROUTING_RULES: () => DEFAULT_CREW_ROUTING_RULES,
|
|
1468
1663
|
DEFAULT_NOTIFY: () => DEFAULT_NOTIFY,
|
|
1664
|
+
DEFAULT_ROUTER_BASE_URL: () => DEFAULT_ROUTER_BASE_URL,
|
|
1665
|
+
DEFAULT_ROUTER_KIND: () => DEFAULT_ROUTER_KIND,
|
|
1666
|
+
GATE_ENGINES: () => GATE_ENGINES,
|
|
1469
1667
|
GATE_MODES: () => GATE_MODES,
|
|
1470
1668
|
GATE_POLICIES: () => GATE_POLICIES,
|
|
1471
1669
|
MANAGED_TARGETS: () => MANAGED_TARGETS,
|
|
1670
|
+
PROVIDER_PRESETS: () => PROVIDER_PRESETS,
|
|
1472
1671
|
ROUTER_KINDS: () => ROUTER_KINDS,
|
|
1672
|
+
ROUTER_PRESET_MODEL: () => ROUTER_PRESET_MODEL,
|
|
1473
1673
|
SAFE_KINDS: () => SAFE_KINDS,
|
|
1474
1674
|
SOCKET_CONTROL_MODE_PATH: () => SOCKET_CONTROL_MODE_PATH,
|
|
1475
1675
|
SPOKE_SUBDIRS: () => SPOKE_SUBDIRS,
|
|
@@ -1478,6 +1678,7 @@ __export(dist_exports, {
|
|
|
1478
1678
|
THINKING_LEVELS: () => THINKING_LEVELS,
|
|
1479
1679
|
UPDATE_CHECK_STATE_PATH: () => UPDATE_CHECK_STATE_PATH,
|
|
1480
1680
|
addWorktree: () => addWorktree,
|
|
1681
|
+
applyProviderPreset: () => applyProviderPreset,
|
|
1481
1682
|
applySafeFixes: () => applySafeFixes,
|
|
1482
1683
|
captainSessionName: () => captainSessionName,
|
|
1483
1684
|
checkForUpdate: () => checkForUpdate,
|
|
@@ -1493,6 +1694,7 @@ __export(dist_exports, {
|
|
|
1493
1694
|
defaultStatePath: () => defaultStatePath,
|
|
1494
1695
|
detectDrift: () => detectDrift,
|
|
1495
1696
|
detectInstallManager: () => detectInstallManager,
|
|
1697
|
+
detectProviderPresetId: () => detectProviderPresetId,
|
|
1496
1698
|
ensureCmuxAutoConfig: () => ensureCmuxAutoConfig,
|
|
1497
1699
|
ensureDirSync: () => ensureDirSync,
|
|
1498
1700
|
ensureRuntimeSynced: () => ensureRuntimeSynced,
|
|
@@ -1508,9 +1710,11 @@ __export(dist_exports, {
|
|
|
1508
1710
|
isBackendMode: () => isBackendMode,
|
|
1509
1711
|
isCacheStale: () => isCacheStale,
|
|
1510
1712
|
isDaemonCachedKey: () => isDaemonCachedKey,
|
|
1713
|
+
isGateEngine: () => isGateEngine,
|
|
1511
1714
|
isGateMode: () => isGateMode,
|
|
1512
1715
|
isGatePolicy: () => isGatePolicy,
|
|
1513
1716
|
isNewerVersion: () => isNewerVersion,
|
|
1717
|
+
isProviderPresetId: () => isProviderPresetId,
|
|
1514
1718
|
isQuieter: () => isQuieter,
|
|
1515
1719
|
isRouterKind: () => isRouterKind,
|
|
1516
1720
|
isThinkingLevel: () => isThinkingLevel,
|
|
@@ -1527,6 +1731,7 @@ __export(dist_exports, {
|
|
|
1527
1731
|
parseThinkingLevel: () => parseThinkingLevel,
|
|
1528
1732
|
probeCmuxDaemonDirect: () => probeCmuxDaemonDirect,
|
|
1529
1733
|
projectConfigPath: () => projectConfigPath,
|
|
1734
|
+
providerPresetDefaults: () => providerPresetDefaults,
|
|
1530
1735
|
readConfigFileSync: () => readConfigFileSync,
|
|
1531
1736
|
readDailyLog: () => readDailyLog,
|
|
1532
1737
|
readProjectLevelSource: () => readProjectLevelSource,
|
|
@@ -1556,6 +1761,7 @@ __export(dist_exports, {
|
|
|
1556
1761
|
var init_dist = __esm({
|
|
1557
1762
|
"packages/shared/dist/index.js"() {
|
|
1558
1763
|
init_config();
|
|
1764
|
+
init_provider_preset();
|
|
1559
1765
|
init_project_config();
|
|
1560
1766
|
init_effort();
|
|
1561
1767
|
init_router_model();
|
|
@@ -1827,10 +2033,11 @@ function formatDelegationReport(rec, originProject, targetProject) {
|
|
|
1827
2033
|
return null;
|
|
1828
2034
|
}
|
|
1829
2035
|
}
|
|
1830
|
-
function firePush(deps, project, prev, next, event, lastCaptainTurnAt) {
|
|
2036
|
+
function firePush(deps, project, prev, next, event, lastCaptainTurnAt, prevRec) {
|
|
1831
2037
|
if (!deps.notify)
|
|
1832
2038
|
return;
|
|
1833
|
-
|
|
2039
|
+
const questionChanged = prev === "blocked" && next.state === "blocked" && prevRec != null && (prevRec.question ?? "").trim() !== (next.question ?? "").trim();
|
|
2040
|
+
if (prev === next.state && !questionChanged)
|
|
1834
2041
|
return;
|
|
1835
2042
|
if (!ATTENTION_STATES.has(next.state))
|
|
1836
2043
|
return;
|
|
@@ -1868,6 +2075,7 @@ function createDaemon(deps) {
|
|
|
1868
2075
|
const { store, now } = deps;
|
|
1869
2076
|
const lastCaptainTurnAt = /* @__PURE__ */ new Map();
|
|
1870
2077
|
const quietNotifiedAt = /* @__PURE__ */ new Map();
|
|
2078
|
+
const blockedRenudgedFor = /* @__PURE__ */ new Map();
|
|
1871
2079
|
const resendAttemptedAt = /* @__PURE__ */ new Map();
|
|
1872
2080
|
const firstTurnUndeliveredBudgetMs = deps.firstTurnUndeliveredBudgetMs ?? DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS;
|
|
1873
2081
|
const firstTurnResendCooldownMs = deps.firstTurnResendCooldownMs ?? DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS;
|
|
@@ -1888,7 +2096,7 @@ function createDaemon(deps) {
|
|
|
1888
2096
|
const next = reduce(cur, event, now());
|
|
1889
2097
|
if (next !== cur) {
|
|
1890
2098
|
store.put(next);
|
|
1891
|
-
firePush(deps, project, cur.state, next, event, lastCaptainTurnAt.get(next.id));
|
|
2099
|
+
firePush(deps, project, cur.state, next, event, lastCaptainTurnAt.get(next.id), cur);
|
|
1892
2100
|
}
|
|
1893
2101
|
return next;
|
|
1894
2102
|
}
|
|
@@ -2040,6 +2248,8 @@ function createDaemon(deps) {
|
|
|
2040
2248
|
}
|
|
2041
2249
|
const recoveryPromises = [];
|
|
2042
2250
|
for (const r of store.listAll()) {
|
|
2251
|
+
if (r.state !== "blocked")
|
|
2252
|
+
blockedRenudgedFor.delete(r.id);
|
|
2043
2253
|
if (TERMINAL_STATES.has(r.state) && t - r.lastHeartbeat > TERMINAL_RECORD_TTL_MS) {
|
|
2044
2254
|
store.delete(r.project, r.id);
|
|
2045
2255
|
continue;
|
|
@@ -2102,6 +2312,27 @@ function createDaemon(deps) {
|
|
|
2102
2312
|
continue;
|
|
2103
2313
|
}
|
|
2104
2314
|
}
|
|
2315
|
+
if (r.state === "blocked" && !r.operatorHold) {
|
|
2316
|
+
const threshold = deps.blockedRenudgeMs ?? DEFAULT_BLOCKED_RENUDGE_MS;
|
|
2317
|
+
const blockedFor = t - r.lastHeartbeat;
|
|
2318
|
+
const episodeKey = r.question ?? "";
|
|
2319
|
+
if (blockedFor > threshold && blockedRenudgedFor.get(r.id) !== episodeKey) {
|
|
2320
|
+
blockedRenudgedFor.set(r.id, episodeKey);
|
|
2321
|
+
if (deps.notify) {
|
|
2322
|
+
const tag = crewTag(r);
|
|
2323
|
+
const mins = Math.max(1, Math.round(blockedFor / 6e4));
|
|
2324
|
+
const message = `CREW BLOCKED REMINDER ${tag}: still blocked ~${mins}min (reminder \u2014 same question, not a new one): ${(r.question ?? "(no question)").trim()}`;
|
|
2325
|
+
const synthEvent = { type: "task.blocked", id: r.id, reason: "renudge", question: r.question ?? "" };
|
|
2326
|
+
try {
|
|
2327
|
+
const p = deps.notify({ project: r.project, message, record: r, event: synthEvent });
|
|
2328
|
+
if (p && typeof p.catch === "function")
|
|
2329
|
+
p.catch(() => {
|
|
2330
|
+
});
|
|
2331
|
+
} catch {
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2105
2336
|
if (r.mode === "interactive" && r.state === "submitted" && !r.firstTurnConfirmedAt) {
|
|
2106
2337
|
const undeliveredMs = t - r.createdAt;
|
|
2107
2338
|
if (undeliveredMs > firstTurnUndeliveredBudgetMs) {
|
|
@@ -2191,7 +2422,7 @@ function createDaemon(deps) {
|
|
|
2191
2422
|
}
|
|
2192
2423
|
};
|
|
2193
2424
|
}
|
|
2194
|
-
var DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS, DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS, DEFAULT_TASK_TIMEOUT_MS, TERMINAL_RECORD_TTL_MS, TERMINAL_RECORD_KEEP_PER_PROJECT, ATTENTION_STATES, REAPABLE_SURFACE_STATES, IDLE_DEBOUNCE_MS, KNOWN_EVENT_TYPES;
|
|
2425
|
+
var DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS, DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS, DEFAULT_BLOCKED_RENUDGE_MS, DEFAULT_TASK_TIMEOUT_MS, TERMINAL_RECORD_TTL_MS, TERMINAL_RECORD_KEEP_PER_PROJECT, ATTENTION_STATES, REAPABLE_SURFACE_STATES, IDLE_DEBOUNCE_MS, KNOWN_EVENT_TYPES;
|
|
2195
2426
|
var init_reduce = __esm({
|
|
2196
2427
|
"packages/core/dist/daemon/reduce.js"() {
|
|
2197
2428
|
init_dist();
|
|
@@ -2199,6 +2430,7 @@ var init_reduce = __esm({
|
|
|
2199
2430
|
init_watchdog();
|
|
2200
2431
|
DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS = 12e4;
|
|
2201
2432
|
DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS = 6e4;
|
|
2433
|
+
DEFAULT_BLOCKED_RENUDGE_MS = 30 * 60 * 1e3;
|
|
2202
2434
|
DEFAULT_TASK_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
2203
2435
|
TERMINAL_RECORD_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2204
2436
|
TERMINAL_RECORD_KEEP_PER_PROJECT = 20;
|
|
@@ -2793,8 +3025,8 @@ function reconcileLiveness(prev, next) {
|
|
|
2793
3025
|
if (!prev)
|
|
2794
3026
|
return next;
|
|
2795
3027
|
if (next.source === "scan") {
|
|
2796
|
-
const
|
|
2797
|
-
return { ...prev, pidAlive, lastSeenAt: Math.max(prev.lastSeenAt, next.lastSeenAt) };
|
|
3028
|
+
const pidAlive2 = next.lastSeenAt >= prev.lastSeenAt ? next.pidAlive : prev.pidAlive;
|
|
3029
|
+
return { ...prev, pidAlive: pidAlive2, lastSeenAt: Math.max(prev.lastSeenAt, next.lastSeenAt) };
|
|
2798
3030
|
}
|
|
2799
3031
|
if (next.startedAt >= prev.startedAt || next.lastState === "end")
|
|
2800
3032
|
return next;
|
|
@@ -3540,6 +3772,68 @@ function createDirectSurfaceLivenessProbe(cmux2, getCaptainTitle) {
|
|
|
3540
3772
|
}
|
|
3541
3773
|
};
|
|
3542
3774
|
}
|
|
3775
|
+
function hasModalOptionList(screen) {
|
|
3776
|
+
if (!screen)
|
|
3777
|
+
return false;
|
|
3778
|
+
const lines = screen.split(/\r?\n/);
|
|
3779
|
+
let bottomHR = -1;
|
|
3780
|
+
let topHR = -1;
|
|
3781
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3782
|
+
if (HR_RE.test(lines[i])) {
|
|
3783
|
+
if (bottomHR === -1)
|
|
3784
|
+
bottomHR = i;
|
|
3785
|
+
else {
|
|
3786
|
+
topHR = i;
|
|
3787
|
+
break;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
if (topHR === -1)
|
|
3792
|
+
return false;
|
|
3793
|
+
return lines.slice(topHR + 1, bottomHR).some((l) => /^\s*\d+\.\s/.test(l));
|
|
3794
|
+
}
|
|
3795
|
+
function classifyCaptainPane(screen, opts = {}) {
|
|
3796
|
+
if (screen == null || screen.trim() === "")
|
|
3797
|
+
return { status: "unreadable" };
|
|
3798
|
+
if (hasModalOptionList(screen))
|
|
3799
|
+
return { status: "locked" };
|
|
3800
|
+
if (WORKING_RE.test(screen))
|
|
3801
|
+
return { status: "mid-turn" };
|
|
3802
|
+
const idleAfterMs = opts.idleAfterMs ?? 0;
|
|
3803
|
+
const stale = opts.outputAgoMs !== void 0 && opts.outputAgoMs >= idleAfterMs;
|
|
3804
|
+
return stale ? { status: "no-session" } : { status: "mid-turn" };
|
|
3805
|
+
}
|
|
3806
|
+
function screenHash(screen) {
|
|
3807
|
+
let h = 2166136261;
|
|
3808
|
+
for (let i = 0; i < screen.length; i++) {
|
|
3809
|
+
h ^= screen.charCodeAt(i);
|
|
3810
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
3811
|
+
}
|
|
3812
|
+
return h.toString(16).padStart(8, "0");
|
|
3813
|
+
}
|
|
3814
|
+
function createCaptainPaneReader(cmux2, getCaptainTitle, opts = {}) {
|
|
3815
|
+
const idleAfterMs = opts.idleAfterMs ?? PANE_IDLE_AFTER_MS;
|
|
3816
|
+
return async (project) => {
|
|
3817
|
+
try {
|
|
3818
|
+
const wsId = await cmux2.findWorkspaceId(getCaptainTitle(project));
|
|
3819
|
+
if (!wsId)
|
|
3820
|
+
return { status: "no-session" };
|
|
3821
|
+
const surfaces = await cmux2.listSurfaces(wsId);
|
|
3822
|
+
const want = getCaptainTitle(project);
|
|
3823
|
+
const pane = surfaces.find((s) => s.title === want) ?? surfaces[0];
|
|
3824
|
+
if (!pane)
|
|
3825
|
+
return { status: "no-session" };
|
|
3826
|
+
const screen = await cmux2.readPaneScreen(pane);
|
|
3827
|
+
if (screen == null)
|
|
3828
|
+
return { status: "unreadable" };
|
|
3829
|
+
const outputAgoMs = opts.noteScreen?.(project, screenHash(screen));
|
|
3830
|
+
return classifyCaptainPane(screen, { outputAgoMs, idleAfterMs });
|
|
3831
|
+
} catch (e) {
|
|
3832
|
+
opts.log?.(`captain pane read failed project=${project}: ${e.message}`);
|
|
3833
|
+
return { status: "unreadable" };
|
|
3834
|
+
}
|
|
3835
|
+
};
|
|
3836
|
+
}
|
|
3543
3837
|
function createDirectCrewPaneReader(cmux2, getCaptainTitle) {
|
|
3544
3838
|
return async (rec) => {
|
|
3545
3839
|
try {
|
|
@@ -3562,11 +3856,14 @@ function createDirectCrewPaneReader(cmux2, getCaptainTitle) {
|
|
|
3562
3856
|
}
|
|
3563
3857
|
};
|
|
3564
3858
|
}
|
|
3565
|
-
var TAIL_LINES;
|
|
3859
|
+
var TAIL_LINES, HR_RE, WORKING_RE, PANE_IDLE_AFTER_MS;
|
|
3566
3860
|
var init_crew_pane_reader = __esm({
|
|
3567
3861
|
"packages/core/dist/crew-pane-reader.js"() {
|
|
3568
3862
|
init_dist();
|
|
3569
3863
|
TAIL_LINES = 25;
|
|
3864
|
+
HR_RE = /^\s*─{10,}\s*$/;
|
|
3865
|
+
WORKING_RE = /esc to interrupt|\btokens?\b.*\b(used|left)\b|↓\s*[\d.]+\s*k?\s*tokens?\b|\(\d+m?\s*\d+s\b/i;
|
|
3866
|
+
PANE_IDLE_AFTER_MS = 10 * 6e4;
|
|
3570
3867
|
}
|
|
3571
3868
|
});
|
|
3572
3869
|
|
|
@@ -4264,8 +4561,11 @@ function buildRouterEnv(creds, model) {
|
|
|
4264
4561
|
if (headers)
|
|
4265
4562
|
env.ANTHROPIC_CUSTOM_HEADERS = headers;
|
|
4266
4563
|
}
|
|
4267
|
-
if (model)
|
|
4564
|
+
if (model) {
|
|
4268
4565
|
env.ANTHROPIC_MODEL = model;
|
|
4566
|
+
env.CLAUDE_CODE_SUBAGENT_MODEL = model;
|
|
4567
|
+
env.ANTHROPIC_SMALL_FAST_MODEL = model;
|
|
4568
|
+
}
|
|
4269
4569
|
return env;
|
|
4270
4570
|
}
|
|
4271
4571
|
function mergeClaudeEnvRouterSettings(routerEnv, claudeEnv) {
|
|
@@ -5431,8 +5731,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5431
5731
|
if (mode !== "off")
|
|
5432
5732
|
throw new DeferDelivery(null, "no-channel");
|
|
5433
5733
|
}
|
|
5734
|
+
const paneOpts = { ...sendOpts, agent };
|
|
5434
5735
|
try {
|
|
5435
|
-
return await cmux2.send(surface, text,
|
|
5736
|
+
return await cmux2.send(surface, text, paneOpts);
|
|
5436
5737
|
} catch (e) {
|
|
5437
5738
|
if (!(e instanceof DeferDelivery) || e.reason !== "probe-failed")
|
|
5438
5739
|
throw e;
|
|
@@ -5444,7 +5745,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5444
5745
|
}
|
|
5445
5746
|
log(`delivery project=${project}: probe-failed on ${surface.workspaceId}/${surface.surfaceId} \u2014 re-resolved to ${next.workspaceId}/${next.surfaceId}, retrying`);
|
|
5446
5747
|
surface = next;
|
|
5447
|
-
return cmux2.send(next, text,
|
|
5748
|
+
return cmux2.send(next, text, paneOpts);
|
|
5448
5749
|
}
|
|
5449
5750
|
});
|
|
5450
5751
|
if ("delivered" in result) {
|
|
@@ -6375,16 +6676,26 @@ function createRunCommand(cliBin) {
|
|
|
6375
6676
|
}
|
|
6376
6677
|
};
|
|
6377
6678
|
}
|
|
6679
|
+
function captainAliveStateFromHealth(rows, project) {
|
|
6680
|
+
const row = rows.find((h) => h.kind === "captain" && h.project === project);
|
|
6681
|
+
if (!row)
|
|
6682
|
+
return "unknown";
|
|
6683
|
+
if (row.state === "alive")
|
|
6684
|
+
return "alive";
|
|
6685
|
+
if (row.state === "gone" || row.state === "stopped")
|
|
6686
|
+
return "dead";
|
|
6687
|
+
return "unknown";
|
|
6688
|
+
}
|
|
6378
6689
|
function isCaptainAliveFromHealth(rows, project) {
|
|
6379
|
-
return rows
|
|
6690
|
+
return captainAliveStateFromHealth(rows, project) === "alive";
|
|
6380
6691
|
}
|
|
6381
6692
|
function createIsCaptainAlive(sock) {
|
|
6382
6693
|
return async (project) => {
|
|
6383
6694
|
try {
|
|
6384
6695
|
const health = await sendRequest(sock, { kind: "health", project }, 5e3);
|
|
6385
|
-
return
|
|
6696
|
+
return captainAliveStateFromHealth(health ?? [], project);
|
|
6386
6697
|
} catch {
|
|
6387
|
-
return
|
|
6698
|
+
return "unknown";
|
|
6388
6699
|
}
|
|
6389
6700
|
};
|
|
6390
6701
|
}
|
|
@@ -6421,16 +6732,19 @@ function createEnsureCaptainAlive(deps) {
|
|
|
6421
6732
|
const now = deps.now ?? (() => Date.now());
|
|
6422
6733
|
const inFlight = /* @__PURE__ */ new Map();
|
|
6423
6734
|
async function run(project) {
|
|
6424
|
-
|
|
6735
|
+
const first = await deps.isAlive(project);
|
|
6736
|
+
if (first === "alive")
|
|
6425
6737
|
return "alive";
|
|
6426
6738
|
await deps.launch(project);
|
|
6427
6739
|
const deadline = now() + warmupTimeoutMs;
|
|
6740
|
+
let last = first;
|
|
6428
6741
|
while (now() < deadline) {
|
|
6429
|
-
|
|
6742
|
+
last = await deps.isAlive(project);
|
|
6743
|
+
if (last === "alive")
|
|
6430
6744
|
return "launched";
|
|
6431
6745
|
await sleep3(pollMs);
|
|
6432
6746
|
}
|
|
6433
|
-
return "timeout";
|
|
6747
|
+
return last === "dead" ? "timeout" : "unknown";
|
|
6434
6748
|
}
|
|
6435
6749
|
return function ensure(project) {
|
|
6436
6750
|
const existing = inFlight.get(project);
|
|
@@ -6485,6 +6799,26 @@ ${ev.question}`;
|
|
|
6485
6799
|
function formatInbound(text) {
|
|
6486
6800
|
return `\u{1F4E9} [from Telegram] ${text}`;
|
|
6487
6801
|
}
|
|
6802
|
+
function mediaKind(m) {
|
|
6803
|
+
for (const [field, name] of MEDIA_KINDS) {
|
|
6804
|
+
if (m[field] !== void 0)
|
|
6805
|
+
return name;
|
|
6806
|
+
}
|
|
6807
|
+
return void 0;
|
|
6808
|
+
}
|
|
6809
|
+
function mediaMarker(kind) {
|
|
6810
|
+
return `[${kind} attached - not forwarded]`;
|
|
6811
|
+
}
|
|
6812
|
+
function inboundBody(text, kind) {
|
|
6813
|
+
if (kind === void 0)
|
|
6814
|
+
return text ?? "";
|
|
6815
|
+
const marker = mediaMarker(kind);
|
|
6816
|
+
return text ? `${text}
|
|
6817
|
+
${marker}` : marker;
|
|
6818
|
+
}
|
|
6819
|
+
function formatMediaReceipt(kind, hasCaption) {
|
|
6820
|
+
return hasCaption ? `\u{1F4CE} ${kind} not forwarded \u2014 your caption reached the captain` : `\u{1F4CE} ${kind} not forwarded \u2014 the captain was told it arrived, nothing else was sent`;
|
|
6821
|
+
}
|
|
6488
6822
|
function formatUsageLine(u) {
|
|
6489
6823
|
const reqs = u.requests === 1 ? "1 req" : `${u.requests} reqs`;
|
|
6490
6824
|
const named = Object.keys(u.models).filter((m) => m !== "unknown").sort((a, b) => u.models[b].costUsd - u.models[a].costUsd);
|
|
@@ -6496,8 +6830,19 @@ function maskToken(token) {
|
|
|
6496
6830
|
return token;
|
|
6497
6831
|
return "*".repeat(token.length - 4) + token.slice(-4);
|
|
6498
6832
|
}
|
|
6833
|
+
var MEDIA_KINDS;
|
|
6499
6834
|
var init_format = __esm({
|
|
6500
6835
|
"packages/core/dist/telegram/format.js"() {
|
|
6836
|
+
MEDIA_KINDS = [
|
|
6837
|
+
["photo", "photo"],
|
|
6838
|
+
["voice", "voice message"],
|
|
6839
|
+
["video_note", "video message"],
|
|
6840
|
+
["video", "video"],
|
|
6841
|
+
["audio", "audio file"],
|
|
6842
|
+
["document", "document"],
|
|
6843
|
+
["animation", "animation"],
|
|
6844
|
+
["sticker", "sticker"]
|
|
6845
|
+
];
|
|
6501
6846
|
}
|
|
6502
6847
|
});
|
|
6503
6848
|
|
|
@@ -6520,6 +6865,8 @@ function loadState(stateRoot) {
|
|
|
6520
6865
|
};
|
|
6521
6866
|
if (typeof data.lastUserId === "number")
|
|
6522
6867
|
result.lastUserId = data.lastUserId;
|
|
6868
|
+
if (data.pending && Object.keys(data.pending).length > 0)
|
|
6869
|
+
result.pending = data.pending;
|
|
6523
6870
|
return result;
|
|
6524
6871
|
} catch {
|
|
6525
6872
|
return { offset: 0, topics: {}, notify: {} };
|
|
@@ -6533,6 +6880,19 @@ function setTopic(stateRoot, project, topicId, scope = "project") {
|
|
|
6533
6880
|
s.topics[topicKey(project, scope)] = topicId;
|
|
6534
6881
|
saveState(stateRoot, s);
|
|
6535
6882
|
}
|
|
6883
|
+
function pruneTopics(stateRoot, keep) {
|
|
6884
|
+
const s = loadState(stateRoot);
|
|
6885
|
+
const removed = Object.keys(s.topics).filter((key) => {
|
|
6886
|
+
const sep3 = key.indexOf("::");
|
|
6887
|
+
return !keep(sep3 === -1 ? key : key.slice(0, sep3));
|
|
6888
|
+
});
|
|
6889
|
+
if (removed.length === 0)
|
|
6890
|
+
return [];
|
|
6891
|
+
for (const key of removed)
|
|
6892
|
+
delete s.topics[key];
|
|
6893
|
+
saveState(stateRoot, s);
|
|
6894
|
+
return removed;
|
|
6895
|
+
}
|
|
6536
6896
|
function isNotifyActive(stateRoot, project) {
|
|
6537
6897
|
return loadState(stateRoot).notify[project] === true;
|
|
6538
6898
|
}
|
|
@@ -6546,6 +6906,42 @@ function setNotify(stateRoot, project, active) {
|
|
|
6546
6906
|
s.notify[project] = active;
|
|
6547
6907
|
saveState(stateRoot, s);
|
|
6548
6908
|
}
|
|
6909
|
+
function loadPending(stateRoot) {
|
|
6910
|
+
return loadState(stateRoot).pending ?? {};
|
|
6911
|
+
}
|
|
6912
|
+
function setPending(stateRoot, project, p) {
|
|
6913
|
+
const s = loadState(stateRoot);
|
|
6914
|
+
s.pending = { ...s.pending ?? {}, [project]: p };
|
|
6915
|
+
saveState(stateRoot, s);
|
|
6916
|
+
}
|
|
6917
|
+
function clearPending(stateRoot, project) {
|
|
6918
|
+
const s = loadState(stateRoot);
|
|
6919
|
+
if (!s.pending || !(project in s.pending))
|
|
6920
|
+
return;
|
|
6921
|
+
delete s.pending[project];
|
|
6922
|
+
if (Object.keys(s.pending).length === 0)
|
|
6923
|
+
delete s.pending;
|
|
6924
|
+
saveState(stateRoot, s);
|
|
6925
|
+
}
|
|
6926
|
+
function markPendingWarned(stateRoot, project, at) {
|
|
6927
|
+
const s = loadState(stateRoot);
|
|
6928
|
+
const p = s.pending?.[project];
|
|
6929
|
+
if (!p)
|
|
6930
|
+
return;
|
|
6931
|
+
s.pending[project] = { ...p, warnedAt: at };
|
|
6932
|
+
saveState(stateRoot, s);
|
|
6933
|
+
}
|
|
6934
|
+
function notePaneScreen(stateRoot, project, hash, at) {
|
|
6935
|
+
const s = loadState(stateRoot);
|
|
6936
|
+
const p = s.pending?.[project];
|
|
6937
|
+
if (!p)
|
|
6938
|
+
return void 0;
|
|
6939
|
+
if (p.paneHash === hash && p.paneChangedAt !== void 0)
|
|
6940
|
+
return at - p.paneChangedAt;
|
|
6941
|
+
s.pending[project] = { ...p, paneHash: hash, paneChangedAt: at };
|
|
6942
|
+
saveState(stateRoot, s);
|
|
6943
|
+
return void 0;
|
|
6944
|
+
}
|
|
6549
6945
|
function findProjectByThread(stateRoot, threadId) {
|
|
6550
6946
|
const s = loadState(stateRoot);
|
|
6551
6947
|
for (const [key, id] of Object.entries(s.topics)) {
|
|
@@ -6568,17 +6964,18 @@ var init_state = __esm({
|
|
|
6568
6964
|
function createTelegramClient(opts) {
|
|
6569
6965
|
const fetchImpl = opts.fetch ?? fetch;
|
|
6570
6966
|
const base = `https://api.telegram.org/bot${opts.token}`;
|
|
6571
|
-
async function call(method, body) {
|
|
6967
|
+
async function call(method, body, signal) {
|
|
6572
6968
|
const res = await fetchImpl(`${base}/${method}`, {
|
|
6573
6969
|
method: "POST",
|
|
6574
6970
|
headers: { "content-type": "application/json" },
|
|
6575
|
-
body: JSON.stringify(body)
|
|
6971
|
+
body: JSON.stringify(body),
|
|
6972
|
+
...signal ? { signal } : {}
|
|
6576
6973
|
});
|
|
6577
6974
|
const json = await res.json();
|
|
6578
6975
|
if (!res.ok || !json.ok) {
|
|
6579
6976
|
const code = json.error_code ?? res.status;
|
|
6580
6977
|
const desc = json.description ?? "unknown error";
|
|
6581
|
-
throw new
|
|
6978
|
+
throw new TelegramApiError(code, `telegram ${method} failed (${code}): ${desc}`, json.parameters?.retry_after);
|
|
6582
6979
|
}
|
|
6583
6980
|
return json.result;
|
|
6584
6981
|
}
|
|
@@ -6587,8 +6984,8 @@ function createTelegramClient(opts) {
|
|
|
6587
6984
|
const r = await call("getMe", {});
|
|
6588
6985
|
return { id: r.id, username: r.username };
|
|
6589
6986
|
},
|
|
6590
|
-
getUpdates(offset, timeoutSec = 50) {
|
|
6591
|
-
return call("getUpdates", { offset, timeout: timeoutSec });
|
|
6987
|
+
getUpdates(offset, timeoutSec = 50, signal) {
|
|
6988
|
+
return call("getUpdates", { offset, timeout: timeoutSec }, signal);
|
|
6592
6989
|
},
|
|
6593
6990
|
async sendMessage(chatId, threadId, text, replyMarkup) {
|
|
6594
6991
|
const body = { chat_id: chatId, text };
|
|
@@ -6611,6 +7008,9 @@ function createTelegramClient(opts) {
|
|
|
6611
7008
|
const r = await call("createForumTopic", { chat_id: chatId, name });
|
|
6612
7009
|
return r.message_thread_id;
|
|
6613
7010
|
},
|
|
7011
|
+
async deleteForumTopic(chatId, threadId) {
|
|
7012
|
+
await call("deleteForumTopic", { chat_id: chatId, message_thread_id: threadId });
|
|
7013
|
+
},
|
|
6614
7014
|
async setMyCommands(commands) {
|
|
6615
7015
|
await call("setMyCommands", { commands });
|
|
6616
7016
|
},
|
|
@@ -6619,11 +7019,241 @@ function createTelegramClient(opts) {
|
|
|
6619
7019
|
if (threadId !== void 0)
|
|
6620
7020
|
body.message_thread_id = threadId;
|
|
6621
7021
|
await call("sendChatAction", body);
|
|
7022
|
+
},
|
|
7023
|
+
async setMessageReaction(chatId, messageId, emoji) {
|
|
7024
|
+
await call("setMessageReaction", {
|
|
7025
|
+
chat_id: chatId,
|
|
7026
|
+
message_id: messageId,
|
|
7027
|
+
reaction: [{ type: "emoji", emoji }]
|
|
7028
|
+
});
|
|
6622
7029
|
}
|
|
6623
7030
|
};
|
|
6624
7031
|
}
|
|
7032
|
+
var TelegramApiError;
|
|
6625
7033
|
var init_client = __esm({
|
|
6626
7034
|
"packages/core/dist/telegram/client.js"() {
|
|
7035
|
+
TelegramApiError = class extends Error {
|
|
7036
|
+
code;
|
|
7037
|
+
retryAfterSec;
|
|
7038
|
+
constructor(code, message, retryAfterSec) {
|
|
7039
|
+
super(message);
|
|
7040
|
+
this.code = code;
|
|
7041
|
+
this.retryAfterSec = retryAfterSec;
|
|
7042
|
+
this.name = "TelegramApiError";
|
|
7043
|
+
}
|
|
7044
|
+
};
|
|
7045
|
+
}
|
|
7046
|
+
});
|
|
7047
|
+
|
|
7048
|
+
// packages/core/dist/telegram/inbound-lifecycle.js
|
|
7049
|
+
function defaultSchedule(fn, ms) {
|
|
7050
|
+
const t = setTimeout(fn, ms);
|
|
7051
|
+
t.unref?.();
|
|
7052
|
+
return () => clearTimeout(t);
|
|
7053
|
+
}
|
|
7054
|
+
function formatWatchdogWarning(project, read, minutes) {
|
|
7055
|
+
const head = `\u23F1\uFE0F [${project}] no reply for ${minutes} minutes`;
|
|
7056
|
+
switch (read.status) {
|
|
7057
|
+
case "locked":
|
|
7058
|
+
return `${head} \u2014 the captain is LOCKED on a prompt, waiting for a human. Answer it in the pane (or with \`squadrant crew answer\`) and it will continue.`;
|
|
7059
|
+
case "mid-turn":
|
|
7060
|
+
return `${head} \u2014 the captain is still working (a turn is in flight), just slow. No action needed yet.`;
|
|
7061
|
+
case "no-session":
|
|
7062
|
+
return `${head} \u2014 the captain looks GONE (no live session in its pane). Run \`squadrant launch ${project}\`.`;
|
|
7063
|
+
case "unreadable":
|
|
7064
|
+
return `${head} \u2014 couldn't read the captain's pane to classify it. Check it manually.`;
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
function createInboundLifecycle(opts) {
|
|
7068
|
+
const { stateRoot, cfg, sendChatAction, sendReply, readPane, log } = opts;
|
|
7069
|
+
const now = opts.now ?? Date.now;
|
|
7070
|
+
const schedule = opts.schedule ?? defaultSchedule;
|
|
7071
|
+
const typingMs = opts.typingMs ?? TYPING_KEEPALIVE_MS;
|
|
7072
|
+
const watchdogMs = opts.watchdogMs ?? WATCHDOG_MS;
|
|
7073
|
+
const warnAtMs = watchdogMs + (opts.watchdogMs === void 0 ? WATCHDOG_GRACE_MS : 0);
|
|
7074
|
+
const active = /* @__PURE__ */ new Set();
|
|
7075
|
+
let running = false;
|
|
7076
|
+
function issue(project, threadId) {
|
|
7077
|
+
void Promise.resolve(sendChatAction(cfg.supergroupId, threadId, "typing")).catch((e) => log(`telegram typing keep-alive failed project=${project}: ${e.message}`));
|
|
7078
|
+
}
|
|
7079
|
+
function drain(project) {
|
|
7080
|
+
active.add(project);
|
|
7081
|
+
const tick = () => {
|
|
7082
|
+
if (!running || !active.has(project))
|
|
7083
|
+
return;
|
|
7084
|
+
const p = loadPending(stateRoot)[project];
|
|
7085
|
+
if (!p) {
|
|
7086
|
+
active.delete(project);
|
|
7087
|
+
return;
|
|
7088
|
+
}
|
|
7089
|
+
if (now() - p.startedAt >= warnAtMs && p.warnedAt === void 0) {
|
|
7090
|
+
void warn(project, p.threadId);
|
|
7091
|
+
}
|
|
7092
|
+
issue(project, p.threadId);
|
|
7093
|
+
schedule(tick, typingMs);
|
|
7094
|
+
};
|
|
7095
|
+
schedule(tick, typingMs);
|
|
7096
|
+
}
|
|
7097
|
+
async function warn(project, threadId) {
|
|
7098
|
+
markPendingWarned(stateRoot, project, now());
|
|
7099
|
+
let read = { status: "unreadable" };
|
|
7100
|
+
try {
|
|
7101
|
+
if (readPane)
|
|
7102
|
+
read = await readPane(project);
|
|
7103
|
+
} catch (e) {
|
|
7104
|
+
log(`telegram watchdog pane read failed project=${project}: ${e.message}`);
|
|
7105
|
+
}
|
|
7106
|
+
try {
|
|
7107
|
+
await sendReply(threadId, formatWatchdogWarning(project, read, Math.floor(watchdogMs / 6e4)));
|
|
7108
|
+
} catch (e) {
|
|
7109
|
+
log(`telegram watchdog warning failed project=${project}: ${e.message}`);
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
return {
|
|
7113
|
+
start() {
|
|
7114
|
+
running = true;
|
|
7115
|
+
for (const [project, p] of Object.entries(loadPending(stateRoot))) {
|
|
7116
|
+
void (async () => {
|
|
7117
|
+
let read = { status: "unreadable" };
|
|
7118
|
+
try {
|
|
7119
|
+
if (readPane)
|
|
7120
|
+
read = await readPane(project);
|
|
7121
|
+
} catch (e) {
|
|
7122
|
+
log(`telegram resume pane read failed project=${project}: ${e.message}`);
|
|
7123
|
+
}
|
|
7124
|
+
const still = loadPending(stateRoot)[project];
|
|
7125
|
+
if (!still || still.startedAt !== p.startedAt)
|
|
7126
|
+
return;
|
|
7127
|
+
if (read.status === "no-session") {
|
|
7128
|
+
clearPending(stateRoot, project);
|
|
7129
|
+
log(`telegram typing lifecycle cleared for ${project}: captain session gone on boot`);
|
|
7130
|
+
return;
|
|
7131
|
+
}
|
|
7132
|
+
log(`telegram typing lifecycle resumed for ${project} (thread ${p.threadId})`);
|
|
7133
|
+
issue(project, p.threadId);
|
|
7134
|
+
drain(project);
|
|
7135
|
+
})();
|
|
7136
|
+
}
|
|
7137
|
+
},
|
|
7138
|
+
stop() {
|
|
7139
|
+
running = false;
|
|
7140
|
+
active.clear();
|
|
7141
|
+
},
|
|
7142
|
+
begin(project, threadId) {
|
|
7143
|
+
setPending(stateRoot, project, { threadId, startedAt: now() });
|
|
7144
|
+
issue(project, threadId);
|
|
7145
|
+
if (running && !active.has(project))
|
|
7146
|
+
drain(project);
|
|
7147
|
+
},
|
|
7148
|
+
pending() {
|
|
7149
|
+
return loadPending(stateRoot);
|
|
7150
|
+
}
|
|
7151
|
+
};
|
|
7152
|
+
}
|
|
7153
|
+
var TYPING_KEEPALIVE_MS, WATCHDOG_MS, WATCHDOG_GRACE_MS;
|
|
7154
|
+
var init_inbound_lifecycle = __esm({
|
|
7155
|
+
"packages/core/dist/telegram/inbound-lifecycle.js"() {
|
|
7156
|
+
init_state();
|
|
7157
|
+
TYPING_KEEPALIVE_MS = 4e3;
|
|
7158
|
+
WATCHDOG_MS = 15 * 6e4;
|
|
7159
|
+
WATCHDOG_GRACE_MS = 1;
|
|
7160
|
+
}
|
|
7161
|
+
});
|
|
7162
|
+
|
|
7163
|
+
// packages/core/dist/telegram/notify.js
|
|
7164
|
+
function runTelegramStatus(opts) {
|
|
7165
|
+
const tg = opts.config.telegram;
|
|
7166
|
+
const env = opts.env ?? process.env;
|
|
7167
|
+
const tokenSet = !!(tg?.botToken ?? env.TELEGRAM_BOT_TOKEN);
|
|
7168
|
+
const links = Object.entries(loadState(opts.stateRoot).topics).map(([key, topicId]) => {
|
|
7169
|
+
const sep3 = key.indexOf("::");
|
|
7170
|
+
return { project: key.slice(0, sep3), scope: key.slice(sep3 + 2), topicId };
|
|
7171
|
+
});
|
|
7172
|
+
return { tokenSet, supergroupId: tg?.supergroupId ?? null, links };
|
|
7173
|
+
}
|
|
7174
|
+
function runTelegramNotifySet(opts) {
|
|
7175
|
+
setNotify(opts.stateRoot, opts.project, opts.active);
|
|
7176
|
+
}
|
|
7177
|
+
function runTelegramNotifyPref(args) {
|
|
7178
|
+
const { project, dimension, value, root } = args;
|
|
7179
|
+
if (dimension === "crew") {
|
|
7180
|
+
if (!["all", "alert_only", "done_only", "none"].includes(value))
|
|
7181
|
+
return { ok: false, message: "crew must be all|alert_only|done_only|none" };
|
|
7182
|
+
saveProjectOverride(project, { telegram: { notify: { crew: value } } }, root);
|
|
7183
|
+
return { ok: true };
|
|
7184
|
+
}
|
|
7185
|
+
if (value !== "on" && value !== "off")
|
|
7186
|
+
return { ok: false, message: "cap must be on|off" };
|
|
7187
|
+
saveProjectOverride(project, { telegram: { notify: { cap: value === "on" } } }, root);
|
|
7188
|
+
return { ok: true };
|
|
7189
|
+
}
|
|
7190
|
+
function runTelegramNotifyStatus(opts) {
|
|
7191
|
+
const s = loadState(opts.stateRoot);
|
|
7192
|
+
const projects = /* @__PURE__ */ new Set();
|
|
7193
|
+
for (const key of Object.keys(s.topics)) {
|
|
7194
|
+
const sep3 = key.indexOf("::");
|
|
7195
|
+
projects.add(sep3 === -1 ? key : key.slice(0, sep3));
|
|
7196
|
+
}
|
|
7197
|
+
for (const p of Object.keys(s.notify))
|
|
7198
|
+
projects.add(p);
|
|
7199
|
+
return [...projects].map((project) => ({ project, active: s.notify[project] === true }));
|
|
7200
|
+
}
|
|
7201
|
+
function capAllowed(project, globalNotify, root) {
|
|
7202
|
+
return resolveNotify(globalNotify, loadProjectOverride(project, root)).cap;
|
|
7203
|
+
}
|
|
7204
|
+
function confirmationText(project, before, after, dim) {
|
|
7205
|
+
if (dim === "active")
|
|
7206
|
+
return `\u{1F515} ${project} \u2014 all notifications muted here. Unmute: squadrant telegram notify ${project} on`;
|
|
7207
|
+
if (dim === "cap")
|
|
7208
|
+
return `\u{1F515} ${project} \u2014 captain messages muted here. Re-enable: squadrant telegram notify ${project} cap on`;
|
|
7209
|
+
return `\u{1F515} ${project} \u2014 crew notifications now '${after.crew}' (was '${before.crew}'). Re-enable: squadrant telegram notify ${project} crew ${before.crew}`;
|
|
7210
|
+
}
|
|
7211
|
+
async function runNotifyConfirmation(opts) {
|
|
7212
|
+
const { quieter, dim } = isQuieter(opts.before, opts.after);
|
|
7213
|
+
if (!quieter || dim === null)
|
|
7214
|
+
return false;
|
|
7215
|
+
const topicId = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7216
|
+
if (topicId === void 0)
|
|
7217
|
+
return false;
|
|
7218
|
+
const text = confirmationText(opts.project, opts.before, opts.after, dim);
|
|
7219
|
+
try {
|
|
7220
|
+
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, text);
|
|
7221
|
+
return true;
|
|
7222
|
+
} catch {
|
|
7223
|
+
console.warn(`[squadrant] mute-confirmation send failed for ${opts.project} \u2014 notification preference was still saved`);
|
|
7224
|
+
return false;
|
|
7225
|
+
}
|
|
7226
|
+
}
|
|
7227
|
+
async function runTelegramSend(opts) {
|
|
7228
|
+
const topicId = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7229
|
+
if (topicId === void 0) {
|
|
7230
|
+
throw new Error(`project "${opts.project}" is not linked \u2014 run: squadrant telegram link ${opts.project}`);
|
|
7231
|
+
}
|
|
7232
|
+
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, opts.message);
|
|
7233
|
+
clearPending(opts.stateRoot, opts.project);
|
|
7234
|
+
return { chatId: opts.cfg.supergroupId, topicId };
|
|
7235
|
+
}
|
|
7236
|
+
async function runTelegramLink(opts) {
|
|
7237
|
+
const existing = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7238
|
+
if (existing !== void 0)
|
|
7239
|
+
return { topicId: existing, created: false };
|
|
7240
|
+
const topicId = await opts.client.createForumTopic(opts.cfg.supergroupId, topicName(opts.project));
|
|
7241
|
+
const winner = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7242
|
+
if (winner !== void 0) {
|
|
7243
|
+
try {
|
|
7244
|
+
await opts.client.deleteForumTopic?.(opts.cfg.supergroupId, topicId);
|
|
7245
|
+
} catch {
|
|
7246
|
+
}
|
|
7247
|
+
return { topicId: winner, created: false };
|
|
7248
|
+
}
|
|
7249
|
+
setTopic(opts.stateRoot, opts.project, topicId);
|
|
7250
|
+
return { topicId, created: true };
|
|
7251
|
+
}
|
|
7252
|
+
var init_notify = __esm({
|
|
7253
|
+
"packages/core/dist/telegram/notify.js"() {
|
|
7254
|
+
init_dist();
|
|
7255
|
+
init_state();
|
|
7256
|
+
init_format();
|
|
6627
7257
|
}
|
|
6628
7258
|
});
|
|
6629
7259
|
|
|
@@ -6711,8 +7341,10 @@ var init_tiers = __esm({
|
|
|
6711
7341
|
});
|
|
6712
7342
|
|
|
6713
7343
|
// packages/core/dist/telegram/bridge.js
|
|
7344
|
+
import fs11 from "fs";
|
|
6714
7345
|
import os5 from "os";
|
|
6715
7346
|
import path12 from "path";
|
|
7347
|
+
import crypto2 from "crypto";
|
|
6716
7348
|
function formatInboundReceipt(project, outcome) {
|
|
6717
7349
|
if (!outcome)
|
|
6718
7350
|
return void 0;
|
|
@@ -6728,6 +7360,85 @@ function formatInboundReceipt(project, outcome) {
|
|
|
6728
7360
|
return void 0;
|
|
6729
7361
|
}
|
|
6730
7362
|
}
|
|
7363
|
+
function telegramPollLockPath(token) {
|
|
7364
|
+
const hash = crypto2.createHash("sha256").update(token).digest("hex").slice(0, 16);
|
|
7365
|
+
return path12.join(os5.tmpdir(), `squadrant-telegram-poll-${hash}.lock`);
|
|
7366
|
+
}
|
|
7367
|
+
function pidAlive(pid) {
|
|
7368
|
+
try {
|
|
7369
|
+
process.kill(pid, 0);
|
|
7370
|
+
return true;
|
|
7371
|
+
} catch (e) {
|
|
7372
|
+
return e.code === "EPERM";
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
function lockIsHeld(file) {
|
|
7376
|
+
let pid;
|
|
7377
|
+
let ageMs;
|
|
7378
|
+
try {
|
|
7379
|
+
const parsed = Number.parseInt(fs11.readFileSync(file, "utf8").trim(), 10);
|
|
7380
|
+
pid = Number.isFinite(parsed) ? parsed : void 0;
|
|
7381
|
+
ageMs = Date.now() - fs11.statSync(file).mtimeMs;
|
|
7382
|
+
} catch {
|
|
7383
|
+
return false;
|
|
7384
|
+
}
|
|
7385
|
+
if (pid !== void 0)
|
|
7386
|
+
return pidAlive(pid);
|
|
7387
|
+
return ageMs < POLL_LOCK_STALE_MS;
|
|
7388
|
+
}
|
|
7389
|
+
function acquirePollLock(token) {
|
|
7390
|
+
const file = telegramPollLockPath(token);
|
|
7391
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
7392
|
+
try {
|
|
7393
|
+
const fd = fs11.openSync(file, "wx");
|
|
7394
|
+
fs11.writeSync(fd, String(process.pid));
|
|
7395
|
+
fs11.closeSync(fd);
|
|
7396
|
+
return () => {
|
|
7397
|
+
try {
|
|
7398
|
+
fs11.unlinkSync(file);
|
|
7399
|
+
} catch {
|
|
7400
|
+
}
|
|
7401
|
+
};
|
|
7402
|
+
} catch (e) {
|
|
7403
|
+
if (e.code !== "EEXIST")
|
|
7404
|
+
return null;
|
|
7405
|
+
if (attempt === 0 && !lockIsHeld(file)) {
|
|
7406
|
+
try {
|
|
7407
|
+
fs11.unlinkSync(file);
|
|
7408
|
+
} catch {
|
|
7409
|
+
}
|
|
7410
|
+
continue;
|
|
7411
|
+
}
|
|
7412
|
+
return null;
|
|
7413
|
+
}
|
|
7414
|
+
}
|
|
7415
|
+
return null;
|
|
7416
|
+
}
|
|
7417
|
+
function defaultSchedule2(fn, ms) {
|
|
7418
|
+
const t = setTimeout(fn, ms);
|
|
7419
|
+
t.unref?.();
|
|
7420
|
+
return () => clearTimeout(t);
|
|
7421
|
+
}
|
|
7422
|
+
function foreignPollLockHolder(token, selfPid = process.pid) {
|
|
7423
|
+
const file = telegramPollLockPath(token);
|
|
7424
|
+
let pid;
|
|
7425
|
+
try {
|
|
7426
|
+
const parsed = Number.parseInt(fs11.readFileSync(file, "utf8").trim(), 10);
|
|
7427
|
+
if (!Number.isFinite(parsed))
|
|
7428
|
+
return null;
|
|
7429
|
+
pid = parsed;
|
|
7430
|
+
} catch {
|
|
7431
|
+
return null;
|
|
7432
|
+
}
|
|
7433
|
+
if (pid === selfPid)
|
|
7434
|
+
return null;
|
|
7435
|
+
return pidAlive(pid) ? pid : null;
|
|
7436
|
+
}
|
|
7437
|
+
function pollBackoffMs(err, pollMs) {
|
|
7438
|
+
if (err instanceof TelegramApiError && err.retryAfterSec !== void 0)
|
|
7439
|
+
return err.retryAfterSec * 1e3;
|
|
7440
|
+
return pollMs;
|
|
7441
|
+
}
|
|
6731
7442
|
function parseNotifyPref(text) {
|
|
6732
7443
|
const parts = text.trim().split(/\s+/);
|
|
6733
7444
|
if (stripBotMention(parts[0] ?? "").toLowerCase() !== "/notify")
|
|
@@ -6754,9 +7465,17 @@ function notifyToggle(text) {
|
|
|
6754
7465
|
}
|
|
6755
7466
|
function createTelegramBridge(opts) {
|
|
6756
7467
|
const { cfg, stateRoot, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
|
|
7468
|
+
const lifecycle = opts.lifecycle;
|
|
6757
7469
|
const configRoot = opts.configRoot ?? path12.join(os5.homedir(), ".config", "squadrant");
|
|
6758
7470
|
const pollMs = cfg.pollMs ?? 1e3;
|
|
7471
|
+
const schedule = opts.schedule ?? defaultSchedule2;
|
|
7472
|
+
const lockToken = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
7473
|
+
let releasePollLock = null;
|
|
7474
|
+
let pollAbort = null;
|
|
6759
7475
|
let running = false;
|
|
7476
|
+
let pollStopped = false;
|
|
7477
|
+
let selfHealCancel = null;
|
|
7478
|
+
let conflictCount = 0;
|
|
6760
7479
|
let lastSuccessfulPollAt = null;
|
|
6761
7480
|
let lastError = null;
|
|
6762
7481
|
let lastErrorAt = null;
|
|
@@ -6766,12 +7485,8 @@ function createTelegramBridge(opts) {
|
|
|
6766
7485
|
saveState(stateRoot, s);
|
|
6767
7486
|
}
|
|
6768
7487
|
async function sendToTopic(project, text) {
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
threadId = await client.createForumTopic(cfg.supergroupId, topicName(project));
|
|
6772
|
-
setTopic(stateRoot, project, threadId);
|
|
6773
|
-
}
|
|
6774
|
-
await client.sendMessage(cfg.supergroupId, threadId, text);
|
|
7488
|
+
const { topicId } = await runTelegramLink({ project, cfg, client, stateRoot });
|
|
7489
|
+
await client.sendMessage(cfg.supergroupId, topicId, text);
|
|
6775
7490
|
}
|
|
6776
7491
|
async function deliverOutbound(project, ev) {
|
|
6777
7492
|
const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
|
|
@@ -6958,10 +7673,13 @@ ${formatUsageLine(usage2)}`;
|
|
|
6958
7673
|
}
|
|
6959
7674
|
await runChannelCommand(text, fromId, void 0);
|
|
6960
7675
|
}
|
|
6961
|
-
async function handleProjectTopic(text, threadId, fromId) {
|
|
7676
|
+
async function handleProjectTopic(text, threadId, fromId, messageId, media) {
|
|
6962
7677
|
const resolved = findProjectByThread(stateRoot, threadId);
|
|
6963
|
-
if (!resolved)
|
|
7678
|
+
if (!resolved) {
|
|
7679
|
+
log(`telegram inbound in unbound topic thread=${threadId} \u2014 no project linked, message dropped`);
|
|
7680
|
+
await reply(threadId, "\u26A0\uFE0F this topic isn't linked to a project \u2014 run: squadrant telegram link <project>");
|
|
6964
7681
|
return;
|
|
7682
|
+
}
|
|
6965
7683
|
if (isBareSpawn(text)) {
|
|
6966
7684
|
if (!isControlEnabled(cfg) || !isAuthorized(fromId, cfg)) {
|
|
6967
7685
|
await reply(threadId, "\u26D4 not authorized");
|
|
@@ -7011,21 +7729,16 @@ ${formatUsageLine(usage2)}`;
|
|
|
7011
7729
|
await runChannelCommand(text, fromId, threadId);
|
|
7012
7730
|
return;
|
|
7013
7731
|
}
|
|
7014
|
-
|
|
7015
|
-
log(`telegram
|
|
7016
|
-
}
|
|
7732
|
+
if (messageId !== void 0) {
|
|
7733
|
+
void Promise.resolve(client.setMessageReaction?.(cfg.supergroupId, messageId, "\u{1F44D}")).catch((e) => log(`telegram stage-1 reaction failed project=${resolved.project}: ${e.message}`));
|
|
7734
|
+
}
|
|
7017
7735
|
setNotify(stateRoot, resolved.project, true);
|
|
7018
7736
|
if (ensureCaptainAlive && isControlEnabled(cfg) && isAuthorized(fromId, cfg)) {
|
|
7019
|
-
|
|
7020
|
-
const r = await ensureCaptainAlive(resolved.project);
|
|
7737
|
+
void ensureCaptainAlive(resolved.project).then((r) => {
|
|
7021
7738
|
if (r === "timeout") {
|
|
7022
|
-
|
|
7023
|
-
} else {
|
|
7024
|
-
await reply(threadId, `\u{1F4E8} delivered to ${resolved.project} captain`);
|
|
7739
|
+
log(`telegram auto-launch: warmup timed out project=${resolved.project} \u2014 delivery verdict (not this probe) decides the receipt`);
|
|
7025
7740
|
}
|
|
7026
|
-
}
|
|
7027
|
-
log(`telegram auto-launch failed project=${resolved.project}: ${e.message}`);
|
|
7028
|
-
}
|
|
7741
|
+
}).catch((e) => log(`telegram auto-launch failed project=${resolved.project}: ${e.message}`));
|
|
7029
7742
|
}
|
|
7030
7743
|
let handled = false;
|
|
7031
7744
|
let outcome;
|
|
@@ -7034,13 +7747,22 @@ ${formatUsageLine(usage2)}`;
|
|
|
7034
7747
|
handled = res.handled;
|
|
7035
7748
|
outcome = res.outcome;
|
|
7036
7749
|
}
|
|
7750
|
+
const deliveryHeld = outcome?.status === "held";
|
|
7037
7751
|
if (!handled) {
|
|
7038
7752
|
await appendCaptainMessage2({ stateRoot, project: resolved.project, text: formatInbound(text), source: "telegram" });
|
|
7753
|
+
outcome = void 0;
|
|
7039
7754
|
}
|
|
7040
7755
|
const receipt = formatInboundReceipt(resolved.project, outcome);
|
|
7041
7756
|
if (receipt && sendReply) {
|
|
7042
7757
|
await sendReply(threadId, receipt).catch((e) => log(`telegram receipt failed project=${resolved.project}: ${e.message}`));
|
|
7043
7758
|
}
|
|
7759
|
+
if (media) {
|
|
7760
|
+
await reply(threadId, formatMediaReceipt(media.kind, media.hasCaption));
|
|
7761
|
+
}
|
|
7762
|
+
if (deliveryHeld)
|
|
7763
|
+
return;
|
|
7764
|
+
await reply(threadId, CAPTAIN_RECEIVED_ACK);
|
|
7765
|
+
lifecycle?.begin(resolved.project, threadId);
|
|
7044
7766
|
}
|
|
7045
7767
|
async function handleUpdate(u) {
|
|
7046
7768
|
if (u.callback_query) {
|
|
@@ -7048,7 +7770,11 @@ ${formatUsageLine(usage2)}`;
|
|
|
7048
7770
|
return;
|
|
7049
7771
|
}
|
|
7050
7772
|
const m = u.message;
|
|
7051
|
-
if (!m
|
|
7773
|
+
if (!m)
|
|
7774
|
+
return;
|
|
7775
|
+
const kind = mediaKind(m);
|
|
7776
|
+
const text = m.text ?? m.caption;
|
|
7777
|
+
if (text === void 0 && kind === void 0)
|
|
7052
7778
|
return;
|
|
7053
7779
|
if (!cfg.chats.includes(m.chat.id))
|
|
7054
7780
|
return;
|
|
@@ -7062,7 +7788,7 @@ ${formatUsageLine(usage2)}`;
|
|
|
7062
7788
|
await reply(threadId, "\u26D4 not authorized");
|
|
7063
7789
|
return;
|
|
7064
7790
|
}
|
|
7065
|
-
const task =
|
|
7791
|
+
const task = (text ?? "").trim();
|
|
7066
7792
|
if (!task) {
|
|
7067
7793
|
await reply(threadId, "spawn cancelled \u2014 empty task");
|
|
7068
7794
|
return;
|
|
@@ -7073,39 +7799,122 @@ ${formatUsageLine(usage2)}`;
|
|
|
7073
7799
|
return;
|
|
7074
7800
|
}
|
|
7075
7801
|
if (m.message_thread_id === void 0) {
|
|
7802
|
+
if (m.text === void 0)
|
|
7803
|
+
return;
|
|
7076
7804
|
await handleGeneral(m.text, m.from?.id);
|
|
7077
7805
|
return;
|
|
7078
7806
|
}
|
|
7079
|
-
await handleProjectTopic(
|
|
7807
|
+
await handleProjectTopic(inboundBody(text, kind), m.message_thread_id, m.from?.id, m.message_id, kind === void 0 ? void 0 : { kind, hasCaption: text !== void 0 });
|
|
7808
|
+
}
|
|
7809
|
+
function acquirePollLockOrLog() {
|
|
7810
|
+
if (!lockToken)
|
|
7811
|
+
return true;
|
|
7812
|
+
releasePollLock = acquirePollLock(lockToken);
|
|
7813
|
+
if (releasePollLock)
|
|
7814
|
+
return true;
|
|
7815
|
+
const holder = foreignPollLockHolder(lockToken);
|
|
7816
|
+
log(`telegram inbound poll disabled: another consumer already polls this bot token (single-consumer guard, #830)${holder !== null ? ` \u2014 lock held by pid ${holder}` : ""}`);
|
|
7817
|
+
return false;
|
|
7818
|
+
}
|
|
7819
|
+
function scheduleSelfHeal() {
|
|
7820
|
+
selfHealCancel?.();
|
|
7821
|
+
selfHealCancel = schedule(() => {
|
|
7822
|
+
selfHealCancel = null;
|
|
7823
|
+
if (pollStopped || running)
|
|
7824
|
+
return;
|
|
7825
|
+
if (!acquirePollLockOrLog()) {
|
|
7826
|
+
scheduleSelfHeal();
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7829
|
+
running = true;
|
|
7830
|
+
conflictCount = 0;
|
|
7831
|
+
log("telegram inbound poll re-attempting after a 409 stop (#850)");
|
|
7832
|
+
void pollLoop();
|
|
7833
|
+
}, CONFLICT_409_SELF_HEAL_MS);
|
|
7080
7834
|
}
|
|
7081
7835
|
async function pollLoop() {
|
|
7082
7836
|
while (running) {
|
|
7837
|
+
const ac = new AbortController();
|
|
7838
|
+
pollAbort = ac;
|
|
7839
|
+
let waitMs = pollMs;
|
|
7083
7840
|
try {
|
|
7084
7841
|
const offset = loadState(stateRoot).offset;
|
|
7085
|
-
const updates = await client.getUpdates(offset, LONG_POLL_SEC);
|
|
7842
|
+
const updates = await client.getUpdates(offset, LONG_POLL_SEC, ac.signal);
|
|
7843
|
+
conflictCount = 0;
|
|
7086
7844
|
for (const u of updates) {
|
|
7087
7845
|
await handleUpdate(u);
|
|
7088
7846
|
persistOffset(u.update_id + 1);
|
|
7089
7847
|
}
|
|
7090
7848
|
lastSuccessfulPollAt = Date.now();
|
|
7091
7849
|
} catch (e) {
|
|
7092
|
-
|
|
7850
|
+
if (ac.signal.aborted)
|
|
7851
|
+
break;
|
|
7852
|
+
const msg = e.message;
|
|
7853
|
+
lastError = msg;
|
|
7093
7854
|
lastErrorAt = Date.now();
|
|
7094
|
-
|
|
7855
|
+
if (e instanceof TelegramApiError && e.code === 409) {
|
|
7856
|
+
conflictCount += 1;
|
|
7857
|
+
if (conflictCount >= CONFLICT_409_TERMINAL_AFTER) {
|
|
7858
|
+
log(`telegram inbound poll stopped: repeated 409 conflict (another getUpdates consumer holds this token) \u2014 ${msg}`);
|
|
7859
|
+
break;
|
|
7860
|
+
}
|
|
7861
|
+
} else {
|
|
7862
|
+
log(`telegram inbound poll failed: ${msg}`);
|
|
7863
|
+
}
|
|
7864
|
+
waitMs = pollBackoffMs(e, pollMs);
|
|
7865
|
+
} finally {
|
|
7866
|
+
pollAbort = null;
|
|
7095
7867
|
}
|
|
7096
7868
|
if (running)
|
|
7097
|
-
await sleep2(
|
|
7869
|
+
await sleep2(waitMs);
|
|
7098
7870
|
}
|
|
7871
|
+
running = false;
|
|
7872
|
+
releasePollLock?.();
|
|
7873
|
+
releasePollLock = null;
|
|
7874
|
+
if (!pollStopped)
|
|
7875
|
+
scheduleSelfHeal();
|
|
7099
7876
|
}
|
|
7100
7877
|
return {
|
|
7101
7878
|
start() {
|
|
7102
7879
|
if (running)
|
|
7103
7880
|
return;
|
|
7881
|
+
pollStopped = false;
|
|
7882
|
+
selfHealCancel?.();
|
|
7883
|
+
selfHealCancel = null;
|
|
7884
|
+
if (!acquirePollLockOrLog())
|
|
7885
|
+
return;
|
|
7886
|
+
const known = opts.registeredProjects?.();
|
|
7887
|
+
if (known !== void 0) {
|
|
7888
|
+
try {
|
|
7889
|
+
const removed = pruneTopics(stateRoot, (p) => known.includes(p));
|
|
7890
|
+
if (removed.length > 0)
|
|
7891
|
+
log(`telegram registry pruned ${removed.length} stale topic link(s): ${removed.join(", ")}`);
|
|
7892
|
+
} catch (e) {
|
|
7893
|
+
log(`telegram registry prune failed: ${e.message}`);
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7104
7896
|
running = true;
|
|
7897
|
+
conflictCount = 0;
|
|
7898
|
+
try {
|
|
7899
|
+
lifecycle?.start();
|
|
7900
|
+
} catch (e) {
|
|
7901
|
+
log(`telegram lifecycle start failed: ${e.message}`);
|
|
7902
|
+
}
|
|
7105
7903
|
void pollLoop();
|
|
7106
7904
|
},
|
|
7107
7905
|
stop() {
|
|
7108
7906
|
running = false;
|
|
7907
|
+
pollStopped = true;
|
|
7908
|
+
selfHealCancel?.();
|
|
7909
|
+
selfHealCancel = null;
|
|
7910
|
+
try {
|
|
7911
|
+
lifecycle?.stop();
|
|
7912
|
+
} catch (e) {
|
|
7913
|
+
log(`telegram lifecycle stop failed: ${e.message}`);
|
|
7914
|
+
}
|
|
7915
|
+
pollAbort?.abort();
|
|
7916
|
+
releasePollLock?.();
|
|
7917
|
+
releasePollLock = null;
|
|
7109
7918
|
},
|
|
7110
7919
|
pushLifecycle(project, ev) {
|
|
7111
7920
|
void deliverOutbound(project, ev).catch((e) => {
|
|
@@ -7122,17 +7931,23 @@ ${formatUsageLine(usage2)}`;
|
|
|
7122
7931
|
}
|
|
7123
7932
|
};
|
|
7124
7933
|
}
|
|
7125
|
-
var LONG_POLL_SEC, sleep2, CREW_TIERS, RECOGNIZED_CHANNEL_COMMANDS;
|
|
7934
|
+
var CAPTAIN_RECEIVED_ACK, LONG_POLL_SEC, POLL_LOCK_STALE_MS, CONFLICT_409_TERMINAL_AFTER, CONFLICT_409_SELF_HEAL_MS, sleep2, CREW_TIERS, RECOGNIZED_CHANNEL_COMMANDS;
|
|
7126
7935
|
var init_bridge = __esm({
|
|
7127
7936
|
"packages/core/dist/telegram/bridge.js"() {
|
|
7128
7937
|
init_dist();
|
|
7938
|
+
init_client();
|
|
7129
7939
|
init_auth();
|
|
7130
7940
|
init_commands();
|
|
7131
7941
|
init_format();
|
|
7942
|
+
init_notify();
|
|
7132
7943
|
init_panels();
|
|
7133
7944
|
init_state();
|
|
7134
7945
|
init_tiers();
|
|
7946
|
+
CAPTAIN_RECEIVED_ACK = "\u2705 captain received";
|
|
7135
7947
|
LONG_POLL_SEC = 50;
|
|
7948
|
+
POLL_LOCK_STALE_MS = 2 * 6e4;
|
|
7949
|
+
CONFLICT_409_TERMINAL_AFTER = 3;
|
|
7950
|
+
CONFLICT_409_SELF_HEAL_MS = 5 * 6e4;
|
|
7136
7951
|
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7137
7952
|
CREW_TIERS = ["all", "alert_only", "done_only", "none"];
|
|
7138
7953
|
RECOGNIZED_CHANNEL_COMMANDS = /* @__PURE__ */ new Set(["status", "projects", "crews", "launch", "effort", "spawn"]);
|
|
@@ -7177,7 +7992,7 @@ var init_restart_daemon = __esm({
|
|
|
7177
7992
|
});
|
|
7178
7993
|
|
|
7179
7994
|
// packages/core/dist/telegram/setup.js
|
|
7180
|
-
import
|
|
7995
|
+
import fs12 from "fs";
|
|
7181
7996
|
function resolveSetupGroup(existingSupergroupId, opts) {
|
|
7182
7997
|
if (existingSupergroupId !== void 0 && !opts.redetect)
|
|
7183
7998
|
return "reuse";
|
|
@@ -7228,7 +8043,7 @@ function writeTelegramConfig(configPath, opts) {
|
|
|
7228
8043
|
let config;
|
|
7229
8044
|
let raw = null;
|
|
7230
8045
|
try {
|
|
7231
|
-
raw =
|
|
8046
|
+
raw = fs12.readFileSync(configPath, "utf-8");
|
|
7232
8047
|
} catch (err) {
|
|
7233
8048
|
if (err.code !== "ENOENT") {
|
|
7234
8049
|
throw new Error(`refusing to overwrite unreadable config at ${configPath}: ${String(err)}`);
|
|
@@ -7251,107 +8066,19 @@ function writeTelegramConfig(configPath, opts) {
|
|
|
7251
8066
|
};
|
|
7252
8067
|
const users = opts.users ?? prev.users;
|
|
7253
8068
|
const remoteControl = opts.remoteControl ?? prev.remoteControl;
|
|
7254
|
-
if (users !== void 0)
|
|
7255
|
-
next.users = users;
|
|
7256
|
-
if (remoteControl !== void 0)
|
|
7257
|
-
next.remoteControl = remoteControl;
|
|
7258
|
-
config.telegram = next;
|
|
7259
|
-
writeConfigFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
7260
|
-
}
|
|
7261
|
-
var init_setup = __esm({
|
|
7262
|
-
"packages/core/dist/telegram/setup.js"() {
|
|
7263
|
-
init_dist();
|
|
7264
|
-
init_state();
|
|
7265
|
-
init_bot_commands();
|
|
7266
|
-
init_restart_daemon();
|
|
7267
|
-
}
|
|
7268
|
-
});
|
|
7269
|
-
|
|
7270
|
-
// packages/core/dist/telegram/notify.js
|
|
7271
|
-
function runTelegramStatus(opts) {
|
|
7272
|
-
const tg = opts.config.telegram;
|
|
7273
|
-
const env = opts.env ?? process.env;
|
|
7274
|
-
const tokenSet = !!(tg?.botToken ?? env.TELEGRAM_BOT_TOKEN);
|
|
7275
|
-
const links = Object.entries(loadState(opts.stateRoot).topics).map(([key, topicId]) => {
|
|
7276
|
-
const sep3 = key.indexOf("::");
|
|
7277
|
-
return { project: key.slice(0, sep3), scope: key.slice(sep3 + 2), topicId };
|
|
7278
|
-
});
|
|
7279
|
-
return { tokenSet, supergroupId: tg?.supergroupId ?? null, links };
|
|
7280
|
-
}
|
|
7281
|
-
function runTelegramNotifySet(opts) {
|
|
7282
|
-
setNotify(opts.stateRoot, opts.project, opts.active);
|
|
7283
|
-
}
|
|
7284
|
-
function runTelegramNotifyPref(args) {
|
|
7285
|
-
const { project, dimension, value, root } = args;
|
|
7286
|
-
if (dimension === "crew") {
|
|
7287
|
-
if (!["all", "alert_only", "done_only", "none"].includes(value))
|
|
7288
|
-
return { ok: false, message: "crew must be all|alert_only|done_only|none" };
|
|
7289
|
-
saveProjectOverride(project, { telegram: { notify: { crew: value } } }, root);
|
|
7290
|
-
return { ok: true };
|
|
7291
|
-
}
|
|
7292
|
-
if (value !== "on" && value !== "off")
|
|
7293
|
-
return { ok: false, message: "cap must be on|off" };
|
|
7294
|
-
saveProjectOverride(project, { telegram: { notify: { cap: value === "on" } } }, root);
|
|
7295
|
-
return { ok: true };
|
|
7296
|
-
}
|
|
7297
|
-
function runTelegramNotifyStatus(opts) {
|
|
7298
|
-
const s = loadState(opts.stateRoot);
|
|
7299
|
-
const projects = /* @__PURE__ */ new Set();
|
|
7300
|
-
for (const key of Object.keys(s.topics)) {
|
|
7301
|
-
const sep3 = key.indexOf("::");
|
|
7302
|
-
projects.add(sep3 === -1 ? key : key.slice(0, sep3));
|
|
7303
|
-
}
|
|
7304
|
-
for (const p of Object.keys(s.notify))
|
|
7305
|
-
projects.add(p);
|
|
7306
|
-
return [...projects].map((project) => ({ project, active: s.notify[project] === true }));
|
|
7307
|
-
}
|
|
7308
|
-
function capAllowed(project, globalNotify, root) {
|
|
7309
|
-
return resolveNotify(globalNotify, loadProjectOverride(project, root)).cap;
|
|
7310
|
-
}
|
|
7311
|
-
function confirmationText(project, before, after, dim) {
|
|
7312
|
-
if (dim === "active")
|
|
7313
|
-
return `\u{1F515} ${project} \u2014 all notifications muted here. Unmute: squadrant telegram notify ${project} on`;
|
|
7314
|
-
if (dim === "cap")
|
|
7315
|
-
return `\u{1F515} ${project} \u2014 captain messages muted here. Re-enable: squadrant telegram notify ${project} cap on`;
|
|
7316
|
-
return `\u{1F515} ${project} \u2014 crew notifications now '${after.crew}' (was '${before.crew}'). Re-enable: squadrant telegram notify ${project} crew ${before.crew}`;
|
|
7317
|
-
}
|
|
7318
|
-
async function runNotifyConfirmation(opts) {
|
|
7319
|
-
const { quieter, dim } = isQuieter(opts.before, opts.after);
|
|
7320
|
-
if (!quieter || dim === null)
|
|
7321
|
-
return false;
|
|
7322
|
-
const topicId = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7323
|
-
if (topicId === void 0)
|
|
7324
|
-
return false;
|
|
7325
|
-
const text = confirmationText(opts.project, opts.before, opts.after, dim);
|
|
7326
|
-
try {
|
|
7327
|
-
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, text);
|
|
7328
|
-
return true;
|
|
7329
|
-
} catch {
|
|
7330
|
-
console.warn(`[squadrant] mute-confirmation send failed for ${opts.project} \u2014 notification preference was still saved`);
|
|
7331
|
-
return false;
|
|
7332
|
-
}
|
|
7333
|
-
}
|
|
7334
|
-
async function runTelegramSend(opts) {
|
|
7335
|
-
const topicId = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7336
|
-
if (topicId === void 0) {
|
|
7337
|
-
throw new Error(`project "${opts.project}" is not linked \u2014 run: squadrant telegram link ${opts.project}`);
|
|
7338
|
-
}
|
|
7339
|
-
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, opts.message);
|
|
7340
|
-
return { chatId: opts.cfg.supergroupId, topicId };
|
|
7341
|
-
}
|
|
7342
|
-
async function runTelegramLink(opts) {
|
|
7343
|
-
const existing = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
7344
|
-
if (existing !== void 0)
|
|
7345
|
-
return { topicId: existing, created: false };
|
|
7346
|
-
const topicId = await opts.client.createForumTopic(opts.cfg.supergroupId, topicName(opts.project));
|
|
7347
|
-
setTopic(opts.stateRoot, opts.project, topicId);
|
|
7348
|
-
return { topicId, created: true };
|
|
8069
|
+
if (users !== void 0)
|
|
8070
|
+
next.users = users;
|
|
8071
|
+
if (remoteControl !== void 0)
|
|
8072
|
+
next.remoteControl = remoteControl;
|
|
8073
|
+
config.telegram = next;
|
|
8074
|
+
writeConfigFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
7349
8075
|
}
|
|
7350
|
-
var
|
|
7351
|
-
"packages/core/dist/telegram/
|
|
8076
|
+
var init_setup = __esm({
|
|
8077
|
+
"packages/core/dist/telegram/setup.js"() {
|
|
7352
8078
|
init_dist();
|
|
7353
8079
|
init_state();
|
|
7354
|
-
|
|
8080
|
+
init_bot_commands();
|
|
8081
|
+
init_restart_daemon();
|
|
7355
8082
|
}
|
|
7356
8083
|
});
|
|
7357
8084
|
|
|
@@ -7366,6 +8093,7 @@ var init_telegram = __esm({
|
|
|
7366
8093
|
init_format();
|
|
7367
8094
|
init_state();
|
|
7368
8095
|
init_client();
|
|
8096
|
+
init_inbound_lifecycle();
|
|
7369
8097
|
init_bridge();
|
|
7370
8098
|
init_setup();
|
|
7371
8099
|
init_notify();
|
|
@@ -8000,6 +8728,12 @@ function resolveGateMode(env, gate) {
|
|
|
8000
8728
|
return fromEnv;
|
|
8001
8729
|
return gate?.mode && isGateMode(gate.mode) ? gate.mode : "auto";
|
|
8002
8730
|
}
|
|
8731
|
+
function resolveGateEngine(env, gate) {
|
|
8732
|
+
const fromEnv = env.SQUADRANT_GATE_ENGINE;
|
|
8733
|
+
if (fromEnv !== void 0 && isGateEngine(fromEnv))
|
|
8734
|
+
return fromEnv;
|
|
8735
|
+
return gate?.engine && isGateEngine(gate.engine) ? gate.engine : "router";
|
|
8736
|
+
}
|
|
8003
8737
|
function resolveGatePolicy(env, gate) {
|
|
8004
8738
|
const fromEnv = env.SQUADRANT_GATE_POLICY;
|
|
8005
8739
|
if (fromEnv !== void 0 && isGatePolicy(fromEnv))
|
|
@@ -8384,6 +9118,59 @@ var init_permission_gate = __esm({
|
|
|
8384
9118
|
}
|
|
8385
9119
|
});
|
|
8386
9120
|
|
|
9121
|
+
// packages/core/dist/auto-gate.js
|
|
9122
|
+
import { createAutoGate, projectGateConfig } from "@squadrant-ai/auto-gate";
|
|
9123
|
+
function gateForProjection(config) {
|
|
9124
|
+
const gate = config.defaults.gate;
|
|
9125
|
+
if (!gate)
|
|
9126
|
+
return {};
|
|
9127
|
+
const out = {};
|
|
9128
|
+
if (gate.mode)
|
|
9129
|
+
out.mode = gate.mode;
|
|
9130
|
+
if (gate.tools)
|
|
9131
|
+
out.tools = gate.tools;
|
|
9132
|
+
if (gate.deny)
|
|
9133
|
+
out.deny = gate.deny;
|
|
9134
|
+
return out;
|
|
9135
|
+
}
|
|
9136
|
+
function createSquadrantAutoGate(deps) {
|
|
9137
|
+
const env = deps.env ?? process.env;
|
|
9138
|
+
const taskId = env.SQUADRANT_CREW_TASK_ID;
|
|
9139
|
+
const project = deps.project ?? env.SQUADRANT_CREW_PROJECT;
|
|
9140
|
+
const mode = resolveGateMode(env, deps.config.defaults.gate);
|
|
9141
|
+
const projected = projectGateConfig({ gate: { ...gateForProjection(deps.config), mode } });
|
|
9142
|
+
const enabled = mode === "on";
|
|
9143
|
+
const blockedSignal = async (ctx) => {
|
|
9144
|
+
if (!taskId || !project || !deps.sendBlocked)
|
|
9145
|
+
return;
|
|
9146
|
+
const event = {
|
|
9147
|
+
type: "task.blocked",
|
|
9148
|
+
id: taskId,
|
|
9149
|
+
reason: ctx.reason,
|
|
9150
|
+
question: `crew needs permission to run ${ctx.tool}`
|
|
9151
|
+
};
|
|
9152
|
+
await deps.sendBlocked(project, event);
|
|
9153
|
+
};
|
|
9154
|
+
const host = createAutoGate({
|
|
9155
|
+
config: projected,
|
|
9156
|
+
readIntent: (p) => readUserIntent(p) ?? void 0,
|
|
9157
|
+
blockedSignal,
|
|
9158
|
+
...deps.decide ? { decide: deps.decide } : {},
|
|
9159
|
+
...project ? { project } : {},
|
|
9160
|
+
env,
|
|
9161
|
+
log: deps.log
|
|
9162
|
+
});
|
|
9163
|
+
if (!enabled) {
|
|
9164
|
+
return { config: host.config, decideClaudeHookPayload: async () => void 0 };
|
|
9165
|
+
}
|
|
9166
|
+
return host;
|
|
9167
|
+
}
|
|
9168
|
+
var init_auto_gate = __esm({
|
|
9169
|
+
"packages/core/dist/auto-gate.js"() {
|
|
9170
|
+
init_permission_gate();
|
|
9171
|
+
}
|
|
9172
|
+
});
|
|
9173
|
+
|
|
8387
9174
|
// packages/core/dist/group-dispatch.js
|
|
8388
9175
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8389
9176
|
import { createConnection as createConnection2 } from "net";
|
|
@@ -8631,7 +9418,7 @@ var init_launch_workspace = __esm({
|
|
|
8631
9418
|
});
|
|
8632
9419
|
|
|
8633
9420
|
// packages/core/dist/side-session.js
|
|
8634
|
-
import
|
|
9421
|
+
import fs13 from "fs";
|
|
8635
9422
|
function sideTitleFor(project, name) {
|
|
8636
9423
|
return `\u{1F5D2} ${project}:${name}`;
|
|
8637
9424
|
}
|
|
@@ -8733,7 +9520,7 @@ async function runSideClose(runtime, workspaceId, project, name, projPath, workt
|
|
|
8733
9520
|
await runtime.closePane(pane);
|
|
8734
9521
|
if (projPath) {
|
|
8735
9522
|
const wtPath = worktreePath(projPath, worktreeDir, project, name);
|
|
8736
|
-
if (
|
|
9523
|
+
if (fs13.existsSync(wtPath)) {
|
|
8737
9524
|
try {
|
|
8738
9525
|
removeWorktree(projPath, wtPath);
|
|
8739
9526
|
} catch (e) {
|
|
@@ -8790,10 +9577,11 @@ async function runCrewAnswer(project, name, option, runtime, workspaceId, deps,
|
|
|
8790
9577
|
if (!crew) {
|
|
8791
9578
|
throw new Error(`Crew '${name}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
|
|
8792
9579
|
}
|
|
8793
|
-
const
|
|
8794
|
-
if (!options) {
|
|
9580
|
+
const modal = await deps.readModalOptions(crew);
|
|
9581
|
+
if (!modal || modal.options.length === 0) {
|
|
8795
9582
|
throw new Error(`Crew '${name}' has no interactive option prompt visible right now \u2014 nothing to answer. Read its screen with 'squadrant crew read ${project} ${name}' to check its state.`);
|
|
8796
9583
|
}
|
|
9584
|
+
const options = modal.options;
|
|
8797
9585
|
const target = resolveOption(options, option);
|
|
8798
9586
|
if (opts?.expect && !target.label.toLowerCase().includes(opts.expect.toLowerCase())) {
|
|
8799
9587
|
throw new Error(`Refusing: option ${target.index} is "${target.label}", which does not contain expected text "${opts.expect}". Option order is model-generated and can shift between renders \u2014 re-check with 'squadrant crew read ${project} ${name}'.
|
|
@@ -8805,7 +9593,9 @@ ${describeOptions(options)}`);
|
|
|
8805
9593
|
log(`\u2192 selecting ${target.index}. "${target.label}"`);
|
|
8806
9594
|
const current = options.find((o) => o.highlighted) ?? options[0];
|
|
8807
9595
|
const steps = target.index - current.index;
|
|
8808
|
-
const
|
|
9596
|
+
const forwardKey = modal.axis === "horizontal" ? "Right" : "Down";
|
|
9597
|
+
const backwardKey = modal.axis === "horizontal" ? "Left" : "Up";
|
|
9598
|
+
const key = steps >= 0 ? forwardKey : backwardKey;
|
|
8809
9599
|
for (let i = 0; i < Math.abs(steps); i++) {
|
|
8810
9600
|
await runtime.sendKeyToPane(crew, key);
|
|
8811
9601
|
}
|
|
@@ -9170,11 +9960,14 @@ __export(dist_exports2, {
|
|
|
9170
9960
|
AGENT_BINS: () => AGENT_BINS,
|
|
9171
9961
|
ANTHROPIC_ONLY_REQUEST_FIELDS: () => ANTHROPIC_ONLY_REQUEST_FIELDS,
|
|
9172
9962
|
BOT_COMMANDS: () => BOT_COMMANDS,
|
|
9963
|
+
CAPTAIN_RECEIVED_ACK: () => CAPTAIN_RECEIVED_ACK,
|
|
9173
9964
|
CC_SOCKS_DIR: () => CC_SOCKS_DIR,
|
|
9174
9965
|
CMUX_PRESERVE_CLAUDE_AUTH_ENV: () => CMUX_PRESERVE_CLAUDE_AUTH_ENV,
|
|
9966
|
+
CONFLICT_409_SELF_HEAL_MS: () => CONFLICT_409_SELF_HEAL_MS,
|
|
9175
9967
|
CREW_GONE_MS: () => CREW_GONE_MS,
|
|
9176
9968
|
CREW_STALE_MS: () => CREW_STALE_MS,
|
|
9177
9969
|
CaptainDelivery: () => CaptainDelivery,
|
|
9970
|
+
DEFAULT_BLOCKED_RENUDGE_MS: () => DEFAULT_BLOCKED_RENUDGE_MS,
|
|
9178
9971
|
DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS: () => DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS,
|
|
9179
9972
|
DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS: () => DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS,
|
|
9180
9973
|
DEFAULT_GATE_DENY_RULES: () => DEFAULT_GATE_DENY_RULES,
|
|
@@ -9191,6 +9984,7 @@ __export(dist_exports2, {
|
|
|
9191
9984
|
LivenessRegistry: () => LivenessRegistry,
|
|
9192
9985
|
MONITOR_STALL_BUDGET_MS: () => MONITOR_STALL_BUDGET_MS,
|
|
9193
9986
|
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
9987
|
+
PANE_IDLE_AFTER_MS: () => PANE_IDLE_AFTER_MS,
|
|
9194
9988
|
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
9195
9989
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
9196
9990
|
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
@@ -9203,6 +9997,10 @@ __export(dist_exports2, {
|
|
|
9203
9997
|
TERMINAL_RECORD_KEEP_PER_PROJECT: () => TERMINAL_RECORD_KEEP_PER_PROJECT,
|
|
9204
9998
|
TERMINAL_RECORD_TTL_MS: () => TERMINAL_RECORD_TTL_MS,
|
|
9205
9999
|
TOOL_STALL_BUDGET_MS: () => TOOL_STALL_BUDGET_MS,
|
|
10000
|
+
TYPING_KEEPALIVE_MS: () => TYPING_KEEPALIVE_MS,
|
|
10001
|
+
TelegramApiError: () => TelegramApiError,
|
|
10002
|
+
WATCHDOG_GRACE_MS: () => WATCHDOG_GRACE_MS,
|
|
10003
|
+
WATCHDOG_MS: () => WATCHDOG_MS,
|
|
9206
10004
|
WORK_ITEM_TTL_MS: () => WORK_ITEM_TTL_MS,
|
|
9207
10005
|
WRITABLE_CONFIG_KEYS: () => WRITABLE_CONFIG_KEYS,
|
|
9208
10006
|
_resetRestartInFlightForTest: () => _resetRestartInFlightForTest,
|
|
@@ -9224,15 +10022,19 @@ __export(dist_exports2, {
|
|
|
9224
10022
|
callClassifier: () => callClassifier,
|
|
9225
10023
|
capAllowed: () => capAllowed,
|
|
9226
10024
|
capOutput: () => capOutput,
|
|
10025
|
+
captainAliveStateFromHealth: () => captainAliveStateFromHealth,
|
|
9227
10026
|
captainRecordPath: () => captainRecordPath,
|
|
9228
10027
|
captainSocketPath: () => captainSocketPath,
|
|
9229
10028
|
checkFact: () => checkFact,
|
|
10029
|
+
classifyCaptainPane: () => classifyCaptainPane,
|
|
9230
10030
|
classifyHealth: () => classifyHealth,
|
|
9231
10031
|
claudeEnvShadowsRouter: () => claudeEnvShadowsRouter,
|
|
10032
|
+
clearPending: () => clearPending,
|
|
9232
10033
|
closeWorkItem: () => closeWorkItem,
|
|
9233
10034
|
computeTemplateHash: () => computeTemplateHash,
|
|
9234
10035
|
consumeExitMarker: () => consumeExitMarker,
|
|
9235
10036
|
createAttach: () => createAttach,
|
|
10037
|
+
createCaptainPaneReader: () => createCaptainPaneReader,
|
|
9236
10038
|
createCrewPaneReader: () => createCrewPaneReader,
|
|
9237
10039
|
createDaemon: () => createDaemon,
|
|
9238
10040
|
createDecoder: () => createDecoder,
|
|
@@ -9241,12 +10043,14 @@ __export(dist_exports2, {
|
|
|
9241
10043
|
createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
|
|
9242
10044
|
createEnsureCaptainAlive: () => createEnsureCaptainAlive,
|
|
9243
10045
|
createEventsSource: () => createEventsSource,
|
|
10046
|
+
createInboundLifecycle: () => createInboundLifecycle,
|
|
9244
10047
|
createInteractiveProbe: () => createInteractiveProbe,
|
|
9245
10048
|
createIsCaptainAlive: () => createIsCaptainAlive,
|
|
9246
10049
|
createLaunch: () => createLaunch,
|
|
9247
10050
|
createRouterService: () => createRouterService,
|
|
9248
10051
|
createRouterShim: () => createRouterShim,
|
|
9249
10052
|
createRunCommand: () => createRunCommand,
|
|
10053
|
+
createSquadrantAutoGate: () => createSquadrantAutoGate,
|
|
9250
10054
|
createStore: () => createStore,
|
|
9251
10055
|
createSurfaceLivenessProbe: () => createSurfaceLivenessProbe,
|
|
9252
10056
|
createTelegramBridge: () => createTelegramBridge,
|
|
@@ -9296,12 +10100,16 @@ __export(dist_exports2, {
|
|
|
9296
10100
|
formatInbound: () => formatInbound,
|
|
9297
10101
|
formatInboundReceipt: () => formatInboundReceipt,
|
|
9298
10102
|
formatLifecycle: () => formatLifecycle,
|
|
10103
|
+
formatMediaReceipt: () => formatMediaReceipt,
|
|
9299
10104
|
formatPermissionDecision: () => formatPermissionDecision,
|
|
9300
10105
|
formatUsageLine: () => formatUsageLine,
|
|
10106
|
+
formatWatchdogWarning: () => formatWatchdogWarning,
|
|
9301
10107
|
freshTrace: () => freshTrace,
|
|
9302
10108
|
gateCacheKey: () => gateCacheKey,
|
|
9303
10109
|
getDaemonPid: () => getDaemonPid,
|
|
10110
|
+
hasModalOptionList: () => hasModalOptionList,
|
|
9304
10111
|
healCmdFor: () => healCmdFor,
|
|
10112
|
+
inboundBody: () => inboundBody,
|
|
9305
10113
|
isAuthorized: () => isAuthorized,
|
|
9306
10114
|
isBareSpawn: () => isBareSpawn,
|
|
9307
10115
|
isCaptainAlive: () => isCaptainAlive,
|
|
@@ -9323,18 +10131,23 @@ __export(dist_exports2, {
|
|
|
9323
10131
|
kickstartArgv: () => kickstartArgv,
|
|
9324
10132
|
launchOneWorkspace: () => launchOneWorkspace,
|
|
9325
10133
|
listSessions: () => listSessions,
|
|
10134
|
+
loadPending: () => loadPending,
|
|
9326
10135
|
loadSessions: () => loadSessions,
|
|
9327
10136
|
loadState: () => loadState,
|
|
9328
10137
|
mailboxStats: () => mailboxStats,
|
|
9329
10138
|
makeGate: () => makeGate,
|
|
10139
|
+
markPendingWarned: () => markPendingWarned,
|
|
9330
10140
|
maskToken: () => maskToken,
|
|
9331
10141
|
matchTier1Deny: () => matchTier1Deny,
|
|
10142
|
+
mediaKind: () => mediaKind,
|
|
10143
|
+
mediaMarker: () => mediaMarker,
|
|
9332
10144
|
mergeClaudeEnvRouterSettings: () => mergeClaudeEnvRouterSettings,
|
|
9333
10145
|
mintToken: () => mintToken,
|
|
9334
10146
|
nameFromTitle: () => nameFromTitle,
|
|
9335
10147
|
newestSessionInDirectory: () => newestSessionInDirectory,
|
|
9336
10148
|
nextAutoName: () => nextAutoName,
|
|
9337
10149
|
niceCrewCommand: () => niceCrewCommand,
|
|
10150
|
+
notePaneScreen: () => notePaneScreen,
|
|
9338
10151
|
notifyToggle: () => notifyToggle,
|
|
9339
10152
|
parseBearer: () => parseBearer,
|
|
9340
10153
|
parseClassifierVerdict: () => parseClassifierVerdict,
|
|
@@ -9343,6 +10156,7 @@ __export(dist_exports2, {
|
|
|
9343
10156
|
parseNotifyPref: () => parseNotifyPref,
|
|
9344
10157
|
parseProgramArgs: () => parseProgramArgs,
|
|
9345
10158
|
plistPath: () => plistPath,
|
|
10159
|
+
pollBackoffMs: () => pollBackoffMs,
|
|
9346
10160
|
pollNewestSessionInDirectory: () => pollNewestSessionInDirectory,
|
|
9347
10161
|
prepareCaptainRoute: () => prepareCaptainRoute,
|
|
9348
10162
|
printForeignInstallError: () => printForeignInstallError,
|
|
@@ -9350,6 +10164,7 @@ __export(dist_exports2, {
|
|
|
9350
10164
|
probeCaptainChannel: () => probeCaptainChannel,
|
|
9351
10165
|
programArgsBlock: () => programArgsBlock,
|
|
9352
10166
|
projectHealth: () => projectHealth,
|
|
10167
|
+
pruneTopics: () => pruneTopics,
|
|
9353
10168
|
purgeExpiredWorkItems: () => purgeExpiredWorkItems,
|
|
9354
10169
|
readCaptainAddress: () => readCaptainAddress,
|
|
9355
10170
|
readCursor: () => readCursor,
|
|
@@ -9377,6 +10192,7 @@ __export(dist_exports2, {
|
|
|
9377
10192
|
resolveCurrentProject: () => resolveCurrentProject,
|
|
9378
10193
|
resolveGate: () => resolveGate,
|
|
9379
10194
|
resolveGateDenyRules: () => resolveGateDenyRules,
|
|
10195
|
+
resolveGateEngine: () => resolveGateEngine,
|
|
9380
10196
|
resolveGateMode: () => resolveGateMode,
|
|
9381
10197
|
resolveGatePolicy: () => resolveGatePolicy,
|
|
9382
10198
|
resolveGateTools: () => resolveGateTools,
|
|
@@ -9416,9 +10232,11 @@ __export(dist_exports2, {
|
|
|
9416
10232
|
saveSessions: () => saveSessions,
|
|
9417
10233
|
saveState: () => saveState,
|
|
9418
10234
|
screenHasSplashMarker: () => screenHasSplashMarker,
|
|
10235
|
+
screenHash: () => screenHash,
|
|
9419
10236
|
sendRequest: () => sendRequest,
|
|
9420
10237
|
setLastUserId: () => setLastUserId,
|
|
9421
10238
|
setNotify: () => setNotify,
|
|
10239
|
+
setPending: () => setPending,
|
|
9422
10240
|
setTopic: () => setTopic,
|
|
9423
10241
|
shellQuote: () => shellQuote,
|
|
9424
10242
|
shouldBuildRouterService: () => shouldBuildRouterService,
|
|
@@ -9431,6 +10249,7 @@ __export(dist_exports2, {
|
|
|
9431
10249
|
startServer: () => startServer,
|
|
9432
10250
|
stripBotMention: () => stripBotMention,
|
|
9433
10251
|
surfaceVerdict: () => surfaceVerdict,
|
|
10252
|
+
telegramPollLockPath: () => telegramPollLockPath,
|
|
9434
10253
|
timeoutGate: () => timeoutGate,
|
|
9435
10254
|
titleFor: () => titleFor,
|
|
9436
10255
|
toControlEvent: () => toControlEvent,
|
|
@@ -9479,6 +10298,7 @@ var init_dist2 = __esm({
|
|
|
9479
10298
|
init_router_resolution();
|
|
9480
10299
|
init_captain_router();
|
|
9481
10300
|
init_permission_gate();
|
|
10301
|
+
init_auto_gate();
|
|
9482
10302
|
init_restart_daemon();
|
|
9483
10303
|
init_group_dispatch();
|
|
9484
10304
|
init_launch_workspace();
|
|
@@ -9500,7 +10320,7 @@ var init_dist2 = __esm({
|
|
|
9500
10320
|
});
|
|
9501
10321
|
|
|
9502
10322
|
// packages/agents/dist/claude/registry.js
|
|
9503
|
-
import
|
|
10323
|
+
import fs14 from "fs";
|
|
9504
10324
|
import { join as join17 } from "path";
|
|
9505
10325
|
import { homedir as homedir7 } from "os";
|
|
9506
10326
|
function parseRegistryDir(files, readFile7) {
|
|
@@ -9545,11 +10365,11 @@ function readClaudeStatus(task) {
|
|
|
9545
10365
|
return void 0;
|
|
9546
10366
|
let files;
|
|
9547
10367
|
try {
|
|
9548
|
-
files =
|
|
10368
|
+
files = fs14.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
9549
10369
|
} catch {
|
|
9550
10370
|
return void 0;
|
|
9551
10371
|
}
|
|
9552
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
10372
|
+
const entries = parseRegistryDir(files, (name) => fs14.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
9553
10373
|
let entry;
|
|
9554
10374
|
if (task.messagingSocketPath) {
|
|
9555
10375
|
entry = entries.find((e) => e.messagingSocketPath === task.messagingSocketPath);
|
|
@@ -9564,11 +10384,11 @@ function readClaudeStatus(task) {
|
|
|
9564
10384
|
function readClaudeStatusByCwd(cwd) {
|
|
9565
10385
|
let files;
|
|
9566
10386
|
try {
|
|
9567
|
-
files =
|
|
10387
|
+
files = fs14.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
9568
10388
|
} catch {
|
|
9569
10389
|
return void 0;
|
|
9570
10390
|
}
|
|
9571
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
10391
|
+
const entries = parseRegistryDir(files, (name) => fs14.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
9572
10392
|
const entry = entries.find((e) => e.cwd === cwd);
|
|
9573
10393
|
if (!entry)
|
|
9574
10394
|
return void 0;
|
|
@@ -9577,11 +10397,11 @@ function readClaudeStatusByCwd(cwd) {
|
|
|
9577
10397
|
function readClaudeStatusBySocketPath(socketPath2) {
|
|
9578
10398
|
let files;
|
|
9579
10399
|
try {
|
|
9580
|
-
files =
|
|
10400
|
+
files = fs14.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
9581
10401
|
} catch {
|
|
9582
10402
|
return void 0;
|
|
9583
10403
|
}
|
|
9584
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
10404
|
+
const entries = parseRegistryDir(files, (name) => fs14.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
9585
10405
|
const entry = entries.find((e) => e.messagingSocketPath === socketPath2);
|
|
9586
10406
|
if (!entry)
|
|
9587
10407
|
return void 0;
|
|
@@ -10004,7 +10824,7 @@ var init_registry2 = __esm({
|
|
|
10004
10824
|
});
|
|
10005
10825
|
|
|
10006
10826
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
10007
|
-
import
|
|
10827
|
+
import fs15 from "fs";
|
|
10008
10828
|
import path13 from "path";
|
|
10009
10829
|
function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model, templatesDir, messagingSocketPath, sessionName, thinking, captainBoot, settingsPath) {
|
|
10010
10830
|
const driver = registry.getDriver(agentName);
|
|
@@ -10034,12 +10854,12 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
|
|
|
10034
10854
|
if (templatesDir) {
|
|
10035
10855
|
const roleFile2 = path13.join(templatesDir, `${role}.claude.md`);
|
|
10036
10856
|
const legacyRoleFile = path13.join(templatesDir, `${role}.CLAUDE.md`);
|
|
10037
|
-
const actualRoleFile =
|
|
10857
|
+
const actualRoleFile = fs15.existsSync(roleFile2) ? roleFile2 : fs15.existsSync(legacyRoleFile) ? legacyRoleFile : null;
|
|
10038
10858
|
if (actualRoleFile) {
|
|
10039
10859
|
cmd += ` --append-system-prompt-file ${actualRoleFile}`;
|
|
10040
10860
|
}
|
|
10041
10861
|
const pluginDir = path13.join(templatesDir, "..", "plugin");
|
|
10042
|
-
if (
|
|
10862
|
+
if (fs15.existsSync(pluginDir)) {
|
|
10043
10863
|
cmd += ` --plugin-dir ${pluginDir}`;
|
|
10044
10864
|
}
|
|
10045
10865
|
}
|
|
@@ -10055,7 +10875,7 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
|
|
|
10055
10875
|
role,
|
|
10056
10876
|
model,
|
|
10057
10877
|
autoApprove: true,
|
|
10058
|
-
promptFile: roleFile &&
|
|
10878
|
+
promptFile: roleFile && fs15.existsSync(roleFile) ? roleFile : void 0,
|
|
10059
10879
|
...captainBoot && role === "captain" ? {
|
|
10060
10880
|
interactive: true,
|
|
10061
10881
|
...captainBoot.port ? { port: captainBoot.port } : {},
|
|
@@ -10427,7 +11247,7 @@ var init_opencode2 = __esm({
|
|
|
10427
11247
|
});
|
|
10428
11248
|
|
|
10429
11249
|
// packages/agents/dist/projection/opencode-skills.js
|
|
10430
|
-
import
|
|
11250
|
+
import fs16 from "fs";
|
|
10431
11251
|
import os10 from "os";
|
|
10432
11252
|
import path18 from "path";
|
|
10433
11253
|
function defaultOpencodeSkillsRoot(env = process.env) {
|
|
@@ -10446,62 +11266,62 @@ function parseSkillName(raw) {
|
|
|
10446
11266
|
}
|
|
10447
11267
|
function listSourceSkills(sourceSkillsDir) {
|
|
10448
11268
|
const out = /* @__PURE__ */ new Map();
|
|
10449
|
-
if (!
|
|
11269
|
+
if (!fs16.existsSync(sourceSkillsDir))
|
|
10450
11270
|
return out;
|
|
10451
|
-
for (const entry of
|
|
11271
|
+
for (const entry of fs16.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
|
|
10452
11272
|
if (!entry.isDirectory())
|
|
10453
11273
|
continue;
|
|
10454
11274
|
const skillPath = path18.join(sourceSkillsDir, entry.name, "SKILL.md");
|
|
10455
|
-
if (!
|
|
11275
|
+
if (!fs16.existsSync(skillPath))
|
|
10456
11276
|
continue;
|
|
10457
|
-
const name = parseSkillName(
|
|
11277
|
+
const name = parseSkillName(fs16.readFileSync(skillPath, "utf-8"));
|
|
10458
11278
|
if (name && SAFE_NAME.test(name))
|
|
10459
11279
|
out.set(name, skillPath);
|
|
10460
11280
|
}
|
|
10461
11281
|
return out;
|
|
10462
11282
|
}
|
|
10463
11283
|
function hasMarker(dir) {
|
|
10464
|
-
return
|
|
11284
|
+
return fs16.existsSync(path18.join(dir, OPENCODE_SKILL_MARKER));
|
|
10465
11285
|
}
|
|
10466
11286
|
function syncOpencodeSkills(opts) {
|
|
10467
11287
|
const result = { written: [], unchanged: [], removed: [], skipped: [] };
|
|
10468
11288
|
const wanted = listSourceSkills(opts.sourceSkillsDir);
|
|
10469
|
-
if (wanted.size === 0 && !
|
|
11289
|
+
if (wanted.size === 0 && !fs16.existsSync(opts.sourceSkillsDir))
|
|
10470
11290
|
return result;
|
|
10471
11291
|
if (!opts.dryRun)
|
|
10472
|
-
|
|
11292
|
+
fs16.mkdirSync(opts.skillsRoot, { recursive: true });
|
|
10473
11293
|
for (const [name, srcPath] of wanted) {
|
|
10474
11294
|
const targetDir = path18.join(opts.skillsRoot, name);
|
|
10475
11295
|
const targetSkill = path18.join(targetDir, "SKILL.md");
|
|
10476
11296
|
const managed = hasMarker(targetDir);
|
|
10477
|
-
if (
|
|
11297
|
+
if (fs16.existsSync(targetSkill) && !managed) {
|
|
10478
11298
|
result.skipped.push({ name, reason: "foreign-collision" });
|
|
10479
11299
|
continue;
|
|
10480
11300
|
}
|
|
10481
|
-
const current =
|
|
10482
|
-
if (current && current.equals(
|
|
11301
|
+
const current = fs16.existsSync(targetSkill) ? fs16.readFileSync(targetSkill) : null;
|
|
11302
|
+
if (current && current.equals(fs16.readFileSync(srcPath))) {
|
|
10483
11303
|
if (!managed && !opts.dryRun) {
|
|
10484
|
-
|
|
11304
|
+
fs16.writeFileSync(path18.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
10485
11305
|
}
|
|
10486
11306
|
result.unchanged.push(name);
|
|
10487
11307
|
continue;
|
|
10488
11308
|
}
|
|
10489
11309
|
if (!opts.dryRun) {
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
11310
|
+
fs16.mkdirSync(targetDir, { recursive: true });
|
|
11311
|
+
fs16.copyFileSync(srcPath, targetSkill);
|
|
11312
|
+
fs16.writeFileSync(path18.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
10493
11313
|
}
|
|
10494
11314
|
result.written.push(name);
|
|
10495
11315
|
}
|
|
10496
|
-
if (
|
|
10497
|
-
for (const entry of
|
|
11316
|
+
if (fs16.existsSync(opts.skillsRoot)) {
|
|
11317
|
+
for (const entry of fs16.readdirSync(opts.skillsRoot, { withFileTypes: true })) {
|
|
10498
11318
|
if (!entry.isDirectory() || wanted.has(entry.name))
|
|
10499
11319
|
continue;
|
|
10500
11320
|
const dir = path18.join(opts.skillsRoot, entry.name);
|
|
10501
11321
|
if (!hasMarker(dir))
|
|
10502
11322
|
continue;
|
|
10503
11323
|
if (!opts.dryRun)
|
|
10504
|
-
|
|
11324
|
+
fs16.rmSync(dir, { recursive: true, force: true });
|
|
10505
11325
|
result.removed.push(entry.name);
|
|
10506
11326
|
}
|
|
10507
11327
|
}
|
|
@@ -12310,6 +13130,9 @@ var init_peer_channel = __esm({
|
|
|
12310
13130
|
return { status: "accepted", via: this.name, confirmed: true };
|
|
12311
13131
|
}
|
|
12312
13132
|
}
|
|
13133
|
+
if (statusBefore === "busy" || statusBefore === "shell") {
|
|
13134
|
+
return { status: "queued", via: this.name };
|
|
13135
|
+
}
|
|
12313
13136
|
if (statusBefore === "idle") {
|
|
12314
13137
|
await this.deps.sleep(window);
|
|
12315
13138
|
const afterInfo = this.deps.statusFor(taskId);
|
|
@@ -12455,7 +13278,7 @@ var init_receipt_listener = __esm({
|
|
|
12455
13278
|
});
|
|
12456
13279
|
|
|
12457
13280
|
// packages/agents/dist/claude/api-key-approval.js
|
|
12458
|
-
import
|
|
13281
|
+
import fs17 from "fs";
|
|
12459
13282
|
import os12 from "os";
|
|
12460
13283
|
import path20 from "path";
|
|
12461
13284
|
function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
|
|
@@ -12465,12 +13288,12 @@ function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
|
|
|
12465
13288
|
const file = opts.claudeJsonPath ?? path20.join(os12.homedir(), ".claude.json");
|
|
12466
13289
|
const log = opts.log ?? (() => {
|
|
12467
13290
|
});
|
|
12468
|
-
if (!
|
|
13291
|
+
if (!fs17.existsSync(file)) {
|
|
12469
13292
|
return { changed: false, reason: "no ~/.claude.json yet \u2014 skipping routed-key pre-approval" };
|
|
12470
13293
|
}
|
|
12471
13294
|
let doc;
|
|
12472
13295
|
try {
|
|
12473
|
-
doc = JSON.parse(
|
|
13296
|
+
doc = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
12474
13297
|
} catch {
|
|
12475
13298
|
return {
|
|
12476
13299
|
changed: false,
|
|
@@ -12492,11 +13315,11 @@ function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
|
|
|
12492
13315
|
};
|
|
12493
13316
|
const tmp = `${file}.squadrant-tmp`;
|
|
12494
13317
|
try {
|
|
12495
|
-
|
|
12496
|
-
|
|
13318
|
+
fs17.writeFileSync(tmp, JSON.stringify(doc, null, 2) + "\n", { mode: fs17.statSync(file).mode & 511 });
|
|
13319
|
+
fs17.renameSync(tmp, file);
|
|
12497
13320
|
} catch (e) {
|
|
12498
13321
|
try {
|
|
12499
|
-
|
|
13322
|
+
fs17.unlinkSync(tmp);
|
|
12500
13323
|
} catch {
|
|
12501
13324
|
}
|
|
12502
13325
|
return {
|
|
@@ -12852,11 +13675,11 @@ function parseDraftFromScreen(screen) {
|
|
|
12852
13675
|
if (!screen)
|
|
12853
13676
|
return null;
|
|
12854
13677
|
const lines = screen.split(/\r?\n/);
|
|
12855
|
-
const
|
|
13678
|
+
const HR_RE2 = /^\s*─{10,}\s*$/;
|
|
12856
13679
|
let bottomHR = -1;
|
|
12857
13680
|
let topHR = -1;
|
|
12858
13681
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12859
|
-
if (
|
|
13682
|
+
if (HR_RE2.test(lines[i])) {
|
|
12860
13683
|
if (bottomHR === -1) {
|
|
12861
13684
|
bottomHR = i;
|
|
12862
13685
|
} else {
|
|
@@ -12894,11 +13717,11 @@ function hasCCInputBox(screen) {
|
|
|
12894
13717
|
if (!screen)
|
|
12895
13718
|
return false;
|
|
12896
13719
|
const lines = screen.split(/\r?\n/);
|
|
12897
|
-
const
|
|
13720
|
+
const HR_RE2 = /^\s*─{10,}\s*$/;
|
|
12898
13721
|
let bottomHR = -1;
|
|
12899
13722
|
let topHR = -1;
|
|
12900
13723
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12901
|
-
if (
|
|
13724
|
+
if (HR_RE2.test(lines[i])) {
|
|
12902
13725
|
if (bottomHR === -1)
|
|
12903
13726
|
bottomHR = i;
|
|
12904
13727
|
else {
|
|
@@ -12911,15 +13734,15 @@ function hasCCInputBox(screen) {
|
|
|
12911
13734
|
return false;
|
|
12912
13735
|
return lines.slice(topHR + 1, bottomHR).some((l) => /[>❯]/.test(l));
|
|
12913
13736
|
}
|
|
12914
|
-
function
|
|
13737
|
+
function hasModalOptionList2(screen) {
|
|
12915
13738
|
if (!screen)
|
|
12916
13739
|
return false;
|
|
12917
13740
|
const lines = screen.split(/\r?\n/);
|
|
12918
|
-
const
|
|
13741
|
+
const HR_RE2 = /^\s*─{10,}\s*$/;
|
|
12919
13742
|
let bottomHR = -1;
|
|
12920
13743
|
let topHR = -1;
|
|
12921
13744
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12922
|
-
if (
|
|
13745
|
+
if (HR_RE2.test(lines[i])) {
|
|
12923
13746
|
if (bottomHR === -1)
|
|
12924
13747
|
bottomHR = i;
|
|
12925
13748
|
else {
|
|
@@ -12933,14 +13756,14 @@ function hasModalOptionList(screen) {
|
|
|
12933
13756
|
return lines.slice(topHR + 1, bottomHR).some((l) => /^\s*\d+\.\s/.test(l));
|
|
12934
13757
|
}
|
|
12935
13758
|
function parseModalOptions(screen) {
|
|
12936
|
-
if (!
|
|
13759
|
+
if (!hasModalOptionList2(screen))
|
|
12937
13760
|
return null;
|
|
12938
13761
|
const lines = screen.split(/\r?\n/);
|
|
12939
|
-
const
|
|
13762
|
+
const HR_RE2 = /^\s*─{10,}\s*$/;
|
|
12940
13763
|
let bottomHR = -1;
|
|
12941
13764
|
let topHR = -1;
|
|
12942
13765
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12943
|
-
if (
|
|
13766
|
+
if (HR_RE2.test(lines[i])) {
|
|
12944
13767
|
if (bottomHR === -1)
|
|
12945
13768
|
bottomHR = i;
|
|
12946
13769
|
else {
|
|
@@ -12961,15 +13784,46 @@ function parseModalOptions(screen) {
|
|
|
12961
13784
|
}
|
|
12962
13785
|
return options.length > 0 ? options : null;
|
|
12963
13786
|
}
|
|
13787
|
+
function parseOpencodePermissionOptions(screen) {
|
|
13788
|
+
if (!screen)
|
|
13789
|
+
return null;
|
|
13790
|
+
if (!/△\s*Permission required/.test(screen))
|
|
13791
|
+
return null;
|
|
13792
|
+
const lines = screen.split(/\r?\n/);
|
|
13793
|
+
const bar = lines.find((l) => /⇆\s*select/.test(l));
|
|
13794
|
+
if (!bar)
|
|
13795
|
+
return null;
|
|
13796
|
+
let region = bar.split("\u21C6")[0];
|
|
13797
|
+
const fullscreenIdx = region.toLowerCase().lastIndexOf("fullscreen");
|
|
13798
|
+
if (fullscreenIdx !== -1) {
|
|
13799
|
+
region = region.slice(0, fullscreenIdx).replace(/\s+\S+\s*$/, "");
|
|
13800
|
+
}
|
|
13801
|
+
region = region.replace(/^[\s┃│╭╮╰╯|]+/, "").trim();
|
|
13802
|
+
if (!region)
|
|
13803
|
+
return null;
|
|
13804
|
+
const labels = region.split(/\s{2,}/).map((s) => s.trim()).filter(Boolean);
|
|
13805
|
+
if (labels.length === 0)
|
|
13806
|
+
return null;
|
|
13807
|
+
return labels.map((label, i) => ({ index: i + 1, label, highlighted: i === 0 }));
|
|
13808
|
+
}
|
|
13809
|
+
function parseModal(screen) {
|
|
13810
|
+
const claude = parseModalOptions(screen);
|
|
13811
|
+
if (claude)
|
|
13812
|
+
return { options: claude, axis: "vertical" };
|
|
13813
|
+
const opencode = parseOpencodePermissionOptions(screen);
|
|
13814
|
+
if (opencode)
|
|
13815
|
+
return { options: opencode, axis: "horizontal" };
|
|
13816
|
+
return null;
|
|
13817
|
+
}
|
|
12964
13818
|
function readInputBoxRaw(screen, opts) {
|
|
12965
13819
|
if (!screen)
|
|
12966
13820
|
return null;
|
|
12967
13821
|
const lines = screen.split(/\r?\n/);
|
|
12968
|
-
const
|
|
13822
|
+
const HR_RE2 = /^\s*─{10,}\s*$/;
|
|
12969
13823
|
let bottomHR = -1;
|
|
12970
13824
|
let topHR = -1;
|
|
12971
13825
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12972
|
-
if (
|
|
13826
|
+
if (HR_RE2.test(lines[i])) {
|
|
12973
13827
|
if (bottomHR === -1)
|
|
12974
13828
|
bottomHR = i;
|
|
12975
13829
|
else {
|
|
@@ -13002,6 +13856,30 @@ function classifyOpencodeStartupSurface(screen) {
|
|
|
13002
13856
|
return "loading";
|
|
13003
13857
|
return OC_IDLE_MARKERS.some((m) => screenHasSplashMarker(screen, m)) ? "idle" : "loading";
|
|
13004
13858
|
}
|
|
13859
|
+
function parseOpencodeDraftFromScreen(screen) {
|
|
13860
|
+
if (!screen)
|
|
13861
|
+
return null;
|
|
13862
|
+
if (OC_BUSY_RE.test(screen))
|
|
13863
|
+
return null;
|
|
13864
|
+
const lines = screen.split(/\r?\n/);
|
|
13865
|
+
let bottom = -1;
|
|
13866
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
13867
|
+
if (OC_BOX_BOTTOM_RE.test(lines[i])) {
|
|
13868
|
+
bottom = i;
|
|
13869
|
+
break;
|
|
13870
|
+
}
|
|
13871
|
+
}
|
|
13872
|
+
if (bottom === -1)
|
|
13873
|
+
return null;
|
|
13874
|
+
const content = [];
|
|
13875
|
+
for (let i = bottom - 1; i >= 0 && OC_BOX_SIDE_RE.test(lines[i]); i--) {
|
|
13876
|
+
content.push(lines[i].replace(/^\s*┃\s*/, "").replace(/[\s┃│]+$/, ""));
|
|
13877
|
+
}
|
|
13878
|
+
if (content.length === 0)
|
|
13879
|
+
return null;
|
|
13880
|
+
const hasDraft = content.some((l) => l !== "" && !OC_BOX_STATUS_RE.test(l) && !screenHasSplashMarker(l, "Ask anything"));
|
|
13881
|
+
return hasDraft ? null : "";
|
|
13882
|
+
}
|
|
13005
13883
|
function sendDebugEnabled() {
|
|
13006
13884
|
return !!process.env.SQUADRANT_DEBUG_SEND;
|
|
13007
13885
|
}
|
|
@@ -13217,10 +14095,10 @@ function createCmuxDriver() {
|
|
|
13217
14095
|
`);
|
|
13218
14096
|
throw new DeferDelivery(null, "probe-failed");
|
|
13219
14097
|
}
|
|
13220
|
-
const draft = parseDraftFromScreen(screen);
|
|
14098
|
+
const draft = opts?.agent === "opencode" ? parseOpencodeDraftFromScreen(screen) : parseDraftFromScreen(screen);
|
|
13221
14099
|
if (draft === null)
|
|
13222
14100
|
throw new DeferDelivery(null, "no-box");
|
|
13223
|
-
if (
|
|
14101
|
+
if (hasModalOptionList2(screen))
|
|
13224
14102
|
throw new DeferDelivery(null, "modal");
|
|
13225
14103
|
if (draft === "") {
|
|
13226
14104
|
await deliver();
|
|
@@ -13324,7 +14202,7 @@ function createCmuxDriver() {
|
|
|
13324
14202
|
}
|
|
13325
14203
|
};
|
|
13326
14204
|
}
|
|
13327
|
-
var CMUX_TIMEOUT, CmuxTimeoutError, CC_INITIALIZED_RE, CC_WORKING_RE, OC_IDLE_MARKERS;
|
|
14205
|
+
var CMUX_TIMEOUT, CmuxTimeoutError, CC_INITIALIZED_RE, CC_WORKING_RE, OC_IDLE_MARKERS, OC_BOX_BOTTOM_RE, OC_BOX_SIDE_RE, OC_BOX_STATUS_RE, OC_BUSY_RE;
|
|
13328
14206
|
var init_cmux = __esm({
|
|
13329
14207
|
"packages/workspaces/dist/runtimes/cmux.js"() {
|
|
13330
14208
|
init_dist();
|
|
@@ -13342,6 +14220,10 @@ var init_cmux = __esm({
|
|
|
13342
14220
|
CC_INITIALIZED_RE = /⏵⏵|Ctx Used|for shortcuts|accept edits/i;
|
|
13343
14221
|
CC_WORKING_RE = /↓\s*[\d.]+\s*k?\s*tokens?\b|esc to interrupt|\bshell still running\b|·\s*\d+\s*shell\b|\(\d+m?\s*\d*s\b/i;
|
|
13344
14222
|
OC_IDLE_MARKERS = ["Ask anything", "ctrl+p commands"];
|
|
14223
|
+
OC_BOX_BOTTOM_RE = /^\s*╹[▀]{6,}\s*$/;
|
|
14224
|
+
OC_BOX_SIDE_RE = /^\s*┃/;
|
|
14225
|
+
OC_BOX_STATUS_RE = /^Build\s*·/i;
|
|
14226
|
+
OC_BUSY_RE = /esc\s+interrupt/i;
|
|
13345
14227
|
}
|
|
13346
14228
|
});
|
|
13347
14229
|
|
|
@@ -13466,7 +14348,7 @@ var init_notifiers = __esm({
|
|
|
13466
14348
|
});
|
|
13467
14349
|
|
|
13468
14350
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
13469
|
-
import
|
|
14351
|
+
import fs18 from "fs/promises";
|
|
13470
14352
|
import { existsSync as existsSync11 } from "fs";
|
|
13471
14353
|
import path21 from "path";
|
|
13472
14354
|
function resolveInRoot(root, relative) {
|
|
@@ -13491,16 +14373,16 @@ function createObsidianDriver(scope) {
|
|
|
13491
14373
|
};
|
|
13492
14374
|
},
|
|
13493
14375
|
async read(rel) {
|
|
13494
|
-
return
|
|
14376
|
+
return fs18.readFile(resolveInRoot(root, rel), "utf-8");
|
|
13495
14377
|
},
|
|
13496
14378
|
async write(rel, content) {
|
|
13497
14379
|
const abs = resolveInRoot(root, rel);
|
|
13498
|
-
await
|
|
13499
|
-
await
|
|
14380
|
+
await fs18.mkdir(path21.dirname(abs), { recursive: true });
|
|
14381
|
+
await fs18.writeFile(abs, content);
|
|
13500
14382
|
},
|
|
13501
14383
|
async exists(rel) {
|
|
13502
14384
|
try {
|
|
13503
|
-
await
|
|
14385
|
+
await fs18.access(resolveInRoot(root, rel));
|
|
13504
14386
|
return true;
|
|
13505
14387
|
} catch {
|
|
13506
14388
|
return false;
|
|
@@ -13508,13 +14390,13 @@ function createObsidianDriver(scope) {
|
|
|
13508
14390
|
},
|
|
13509
14391
|
async list(rel) {
|
|
13510
14392
|
try {
|
|
13511
|
-
return await
|
|
14393
|
+
return await fs18.readdir(resolveInRoot(root, rel));
|
|
13512
14394
|
} catch {
|
|
13513
14395
|
return [];
|
|
13514
14396
|
}
|
|
13515
14397
|
},
|
|
13516
14398
|
async mkdir(rel) {
|
|
13517
|
-
await
|
|
14399
|
+
await fs18.mkdir(resolveInRoot(root, rel), { recursive: true });
|
|
13518
14400
|
}
|
|
13519
14401
|
};
|
|
13520
14402
|
}
|
|
@@ -14142,6 +15024,47 @@ function removeCommandHandlers(entries, command) {
|
|
|
14142
15024
|
}
|
|
14143
15025
|
return removed;
|
|
14144
15026
|
}
|
|
15027
|
+
function isForeignAutoGateCommand(command) {
|
|
15028
|
+
if (typeof command !== "string")
|
|
15029
|
+
return false;
|
|
15030
|
+
if (command.includes(FOREIGN_AUTO_GATE_MARKER))
|
|
15031
|
+
return true;
|
|
15032
|
+
const first = command.trim().split(/\s+/)[0] ?? "";
|
|
15033
|
+
return /(^|[/\\])auto-gate$/.test(first);
|
|
15034
|
+
}
|
|
15035
|
+
function findForeignGateHandlers(entries) {
|
|
15036
|
+
const found = [];
|
|
15037
|
+
for (const entry of entries) {
|
|
15038
|
+
const hooks = entry?.hooks;
|
|
15039
|
+
if (!Array.isArray(hooks))
|
|
15040
|
+
continue;
|
|
15041
|
+
for (const h of hooks) {
|
|
15042
|
+
const cmd = h?.command;
|
|
15043
|
+
if (isForeignAutoGateCommand(cmd))
|
|
15044
|
+
found.push(cmd);
|
|
15045
|
+
}
|
|
15046
|
+
}
|
|
15047
|
+
return found;
|
|
15048
|
+
}
|
|
15049
|
+
function removeForeignGateHandlers(entries) {
|
|
15050
|
+
const removed = [];
|
|
15051
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
15052
|
+
const entry = entries[i];
|
|
15053
|
+
if (!Array.isArray(entry?.hooks))
|
|
15054
|
+
continue;
|
|
15055
|
+
entry.hooks = entry.hooks.filter((h) => {
|
|
15056
|
+
const cmd = h?.command;
|
|
15057
|
+
if (isForeignAutoGateCommand(cmd)) {
|
|
15058
|
+
removed.push(cmd);
|
|
15059
|
+
return false;
|
|
15060
|
+
}
|
|
15061
|
+
return true;
|
|
15062
|
+
});
|
|
15063
|
+
if (entry.hooks.length === 0)
|
|
15064
|
+
entries.splice(i, 1);
|
|
15065
|
+
}
|
|
15066
|
+
return removed;
|
|
15067
|
+
}
|
|
14145
15068
|
function installClaudeHooks(opts = {}) {
|
|
14146
15069
|
const settingsPath = opts.settingsPath ?? join25(homedir13(), ".claude", "settings.json");
|
|
14147
15070
|
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
@@ -14179,6 +15102,23 @@ function installClaudeHooks(opts = {}) {
|
|
|
14179
15102
|
changed = true;
|
|
14180
15103
|
migrated.push(`${eventName}/${sub}`);
|
|
14181
15104
|
}
|
|
15105
|
+
const foreign = findForeignGateHandlers(entries);
|
|
15106
|
+
if (foreign.length > 0) {
|
|
15107
|
+
if (opts.gateMode === "on") {
|
|
15108
|
+
const removed = removeForeignGateHandlers(entries);
|
|
15109
|
+
if (removed.length > 0)
|
|
15110
|
+
changed = true;
|
|
15111
|
+
for (const command2 of removed) {
|
|
15112
|
+
log(`native-hook: removed foreign auto-gate PermissionRequest handler in ${settingsPath} [#P6 \xA715#5]: ${command2}`);
|
|
15113
|
+
opts.recordGateAction?.({ action: "removed-foreign-gate", command: command2 });
|
|
15114
|
+
}
|
|
15115
|
+
} else {
|
|
15116
|
+
for (const command2 of foreign) {
|
|
15117
|
+
log(`native-hook: foreign auto-gate PermissionRequest handler present in ${settingsPath} but squadrant's gate is '${opts.gateMode ?? "auto"}' (not 'on') \u2014 foreign entry left untouched [#P6 \xA715#5]: ${command2}`);
|
|
15118
|
+
opts.recordGateAction?.({ action: "left-foreign-gate", command: command2 });
|
|
15119
|
+
}
|
|
15120
|
+
}
|
|
15121
|
+
}
|
|
14182
15122
|
}
|
|
14183
15123
|
const alreadyPresent = entries.some((m) => Array.isArray(m.hooks) && m.hooks.some((h) => typeof h.command === "string" && h.command === command));
|
|
14184
15124
|
if (!alreadyPresent) {
|
|
@@ -14209,11 +15149,55 @@ function installClaudeHooks(opts = {}) {
|
|
|
14209
15149
|
changed = true;
|
|
14210
15150
|
}
|
|
14211
15151
|
}
|
|
15152
|
+
if (typeof settings.env === "object" && settings.env !== null && !Array.isArray(settings.env)) {
|
|
15153
|
+
if (reconcileSubagentModels(settings.env, log))
|
|
15154
|
+
changed = true;
|
|
15155
|
+
}
|
|
14212
15156
|
if (changed) {
|
|
14213
15157
|
writeFile6(settingsPath, JSON.stringify(settings, null, 2));
|
|
14214
15158
|
}
|
|
14215
15159
|
return settingsPath;
|
|
14216
15160
|
}
|
|
15161
|
+
function isCustomUpstream(baseUrl) {
|
|
15162
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0)
|
|
15163
|
+
return false;
|
|
15164
|
+
try {
|
|
15165
|
+
return new URL(baseUrl).hostname.toLowerCase() !== "api.anthropic.com";
|
|
15166
|
+
} catch {
|
|
15167
|
+
return false;
|
|
15168
|
+
}
|
|
15169
|
+
}
|
|
15170
|
+
function isAnthropicModelId(value) {
|
|
15171
|
+
if (typeof value !== "string")
|
|
15172
|
+
return false;
|
|
15173
|
+
const v2 = value.trim().toLowerCase();
|
|
15174
|
+
return v2 === "sonnet" || v2 === "opus" || v2 === "haiku" || v2.startsWith("claude-");
|
|
15175
|
+
}
|
|
15176
|
+
function reconcileSubagentModels(env, log) {
|
|
15177
|
+
if (!isCustomUpstream(env.ANTHROPIC_BASE_URL))
|
|
15178
|
+
return false;
|
|
15179
|
+
const model = env.ANTHROPIC_MODEL;
|
|
15180
|
+
if (typeof model !== "string" || model.length === 0)
|
|
15181
|
+
return false;
|
|
15182
|
+
let changed = false;
|
|
15183
|
+
for (const key of [SUBAGENT_MODEL_KEY, SMALL_FAST_MODEL_KEY]) {
|
|
15184
|
+
const current = env[key];
|
|
15185
|
+
if (current === void 0) {
|
|
15186
|
+
env[key] = model;
|
|
15187
|
+
changed = true;
|
|
15188
|
+
log(`native-hook: set ${key}='${model}' to match ANTHROPIC_MODEL on a custom upstream`);
|
|
15189
|
+
} else if (isAnthropicModelId(current)) {
|
|
15190
|
+
if (current !== model) {
|
|
15191
|
+
env[key] = model;
|
|
15192
|
+
changed = true;
|
|
15193
|
+
log(`native-hook: aligned ${key} '${String(current)}' \u2192 '${model}' for a custom upstream`);
|
|
15194
|
+
}
|
|
15195
|
+
} else {
|
|
15196
|
+
log(`native-hook: ${key} '${String(current)}' is a custom model id \u2014 left untouched`);
|
|
15197
|
+
}
|
|
15198
|
+
}
|
|
15199
|
+
return changed;
|
|
15200
|
+
}
|
|
14217
15201
|
function mapSubToLifecycle(sub) {
|
|
14218
15202
|
switch (sub) {
|
|
14219
15203
|
case "session-start":
|
|
@@ -14263,7 +15247,7 @@ function defaultWriteFile(path39, content) {
|
|
|
14263
15247
|
mkdirSync8(path39.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
14264
15248
|
writeFileSync11(path39, content, "utf-8");
|
|
14265
15249
|
}
|
|
14266
|
-
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, GATE_HOOK_CMD, NativeHookSource;
|
|
15250
|
+
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, GATE_HOOK_CMD, FOREIGN_AUTO_GATE_MARKER, SUBAGENT_MODEL_KEY, SMALL_FAST_MODEL_KEY, NativeHookSource;
|
|
14267
15251
|
var init_native_hook_source = __esm({
|
|
14268
15252
|
"packages/workspaces/dist/native-hooks/native-hook-source.js"() {
|
|
14269
15253
|
CLAUDE_HOOK_EVENTS = [
|
|
@@ -14283,6 +15267,9 @@ var init_native_hook_source = __esm({
|
|
|
14283
15267
|
];
|
|
14284
15268
|
DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
14285
15269
|
GATE_HOOK_CMD = "squadrant gate";
|
|
15270
|
+
FOREIGN_AUTO_GATE_MARKER = "auto-gate-managed";
|
|
15271
|
+
SUBAGENT_MODEL_KEY = "CLAUDE_CODE_SUBAGENT_MODEL";
|
|
15272
|
+
SMALL_FAST_MODEL_KEY = "ANTHROPIC_SMALL_FAST_MODEL";
|
|
14286
15273
|
NativeHookSource = class {
|
|
14287
15274
|
name = "native-hook";
|
|
14288
15275
|
hookInstall;
|
|
@@ -14412,15 +15399,15 @@ async function resolveCaptainWorkspace(project) {
|
|
|
14412
15399
|
}
|
|
14413
15400
|
async function paneHasOpenModal(runtime, pane) {
|
|
14414
15401
|
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
14415
|
-
return
|
|
15402
|
+
return hasModalOptionList2(screen);
|
|
14416
15403
|
}
|
|
14417
15404
|
async function readModalOptions(runtime, pane) {
|
|
14418
15405
|
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
14419
|
-
return
|
|
15406
|
+
return parseModal(screen);
|
|
14420
15407
|
}
|
|
14421
15408
|
async function confirmedSendToPane(runtime, pane, message) {
|
|
14422
15409
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
14423
|
-
if (
|
|
15410
|
+
if (hasModalOptionList2(preSendScreen)) {
|
|
14424
15411
|
return { delivered: false, blockedByModal: true };
|
|
14425
15412
|
}
|
|
14426
15413
|
await runtime.pasteToPane(pane, message);
|
|
@@ -14673,9 +15660,9 @@ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
|
|
|
14673
15660
|
const resolved = resolveTarget(registry, config, target, !!opts.command);
|
|
14674
15661
|
await needRef(resolved);
|
|
14675
15662
|
const finalProject = opts.command ? config.commandName : target;
|
|
14676
|
-
const { join:
|
|
15663
|
+
const { join: join36, dirname: dirname12 } = await import("path");
|
|
14677
15664
|
const { DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
14678
|
-
const stateRoot =
|
|
15665
|
+
const stateRoot = join36(dirname12(DEFAULT_CONFIG_PATH2), "state");
|
|
14679
15666
|
const seq = await appendCaptainMessage2({
|
|
14680
15667
|
stateRoot,
|
|
14681
15668
|
project: finalProject,
|
|
@@ -14784,7 +15771,7 @@ init_dist3();
|
|
|
14784
15771
|
import { Command as Command38 } from "commander";
|
|
14785
15772
|
import { existsSync as existsSync14, readFileSync as readFileSync21 } from "fs";
|
|
14786
15773
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
14787
|
-
import { dirname as
|
|
15774
|
+
import { dirname as dirname11, join as join35 } from "path";
|
|
14788
15775
|
import { homedir as homedir20 } from "os";
|
|
14789
15776
|
|
|
14790
15777
|
// packages/cli/src/commands/doctor.ts
|
|
@@ -14794,7 +15781,7 @@ init_dist();
|
|
|
14794
15781
|
init_dist4();
|
|
14795
15782
|
import { Command } from "commander";
|
|
14796
15783
|
import { execSync as execSync8 } from "child_process";
|
|
14797
|
-
import
|
|
15784
|
+
import fs19 from "fs";
|
|
14798
15785
|
import { stat } from "fs/promises";
|
|
14799
15786
|
import path22 from "path";
|
|
14800
15787
|
import chalk3 from "chalk";
|
|
@@ -14898,7 +15885,7 @@ function settingsHaveAgentTeams() {
|
|
|
14898
15885
|
try {
|
|
14899
15886
|
const home = process.env.HOME || "";
|
|
14900
15887
|
const settings = JSON.parse(
|
|
14901
|
-
|
|
15888
|
+
fs19.readFileSync(`${home}/.claude/settings.json`, "utf-8")
|
|
14902
15889
|
);
|
|
14903
15890
|
return settings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1";
|
|
14904
15891
|
} catch {
|
|
@@ -14909,7 +15896,7 @@ function pluginInstalled(pluginKey) {
|
|
|
14909
15896
|
try {
|
|
14910
15897
|
const home = process.env.HOME || "";
|
|
14911
15898
|
const plugins = JSON.parse(
|
|
14912
|
-
|
|
15899
|
+
fs19.readFileSync(
|
|
14913
15900
|
`${home}/.claude/plugins/installed_plugins.json`,
|
|
14914
15901
|
"utf-8"
|
|
14915
15902
|
)
|
|
@@ -14955,7 +15942,7 @@ ${lines.join("\n")}
|
|
|
14955
15942
|
}
|
|
14956
15943
|
function readPackageVersion(packageJsonPath) {
|
|
14957
15944
|
try {
|
|
14958
|
-
const pkg2 = JSON.parse(
|
|
15945
|
+
const pkg2 = JSON.parse(fs19.readFileSync(packageJsonPath, "utf-8"));
|
|
14959
15946
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
14960
15947
|
} catch {
|
|
14961
15948
|
return null;
|
|
@@ -14991,7 +15978,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
14991
15978
|
));
|
|
14992
15979
|
results.push(check(
|
|
14993
15980
|
"Obsidian installed",
|
|
14994
|
-
commandExists("obsidian") ||
|
|
15981
|
+
commandExists("obsidian") || fs19.existsSync("/Applications/Obsidian.app"),
|
|
14995
15982
|
"Install from: https://obsidian.md"
|
|
14996
15983
|
));
|
|
14997
15984
|
results.push(check(
|
|
@@ -15099,7 +16086,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
15099
16086
|
results.push(
|
|
15100
16087
|
check(
|
|
15101
16088
|
"Squadrant config exists",
|
|
15102
|
-
|
|
16089
|
+
fs19.existsSync(
|
|
15103
16090
|
process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
|
|
15104
16091
|
),
|
|
15105
16092
|
"Run: squadrant init"
|
|
@@ -15183,7 +16170,7 @@ init_dist();
|
|
|
15183
16170
|
init_dist4();
|
|
15184
16171
|
init_dist();
|
|
15185
16172
|
import { Command as Command2 } from "commander";
|
|
15186
|
-
import
|
|
16173
|
+
import fs20 from "fs";
|
|
15187
16174
|
import path23 from "path";
|
|
15188
16175
|
import os13 from "os";
|
|
15189
16176
|
import readline from "readline";
|
|
@@ -15400,25 +16387,57 @@ function writePerCrewOpencodeConfig(o) {
|
|
|
15400
16387
|
return file;
|
|
15401
16388
|
}
|
|
15402
16389
|
|
|
16390
|
+
// packages/cli/src/lib/claude-auth.ts
|
|
16391
|
+
import { execSync as execSync9 } from "child_process";
|
|
16392
|
+
function parseClaudeAuthStatus(raw) {
|
|
16393
|
+
let parsed;
|
|
16394
|
+
try {
|
|
16395
|
+
parsed = JSON.parse(raw);
|
|
16396
|
+
} catch {
|
|
16397
|
+
return null;
|
|
16398
|
+
}
|
|
16399
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
16400
|
+
const obj = parsed;
|
|
16401
|
+
if (typeof obj.loggedIn !== "boolean") return null;
|
|
16402
|
+
return {
|
|
16403
|
+
authenticated: obj.loggedIn,
|
|
16404
|
+
method: typeof obj.authMethod === "string" ? obj.authMethod : void 0,
|
|
16405
|
+
provider: typeof obj.apiProvider === "string" ? obj.apiProvider : void 0
|
|
16406
|
+
};
|
|
16407
|
+
}
|
|
16408
|
+
function detectClaudeAuth(probe = {}) {
|
|
16409
|
+
const run = probe.run ?? (() => execSync9("claude auth status", {
|
|
16410
|
+
encoding: "utf8",
|
|
16411
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
16412
|
+
timeout: 1e4
|
|
16413
|
+
}));
|
|
16414
|
+
try {
|
|
16415
|
+
const parsed = parseClaudeAuthStatus(run());
|
|
16416
|
+
return parsed ?? { authenticated: false, reason: "unreadable `claude auth status` output" };
|
|
16417
|
+
} catch {
|
|
16418
|
+
return { authenticated: false, reason: "`claude auth status` unavailable" };
|
|
16419
|
+
}
|
|
16420
|
+
}
|
|
16421
|
+
|
|
15403
16422
|
// packages/cli/src/commands/init.ts
|
|
15404
16423
|
init_dist3();
|
|
15405
16424
|
function findPackageRoot() {
|
|
15406
16425
|
let dir = path23.dirname(new URL(import.meta.url).pathname);
|
|
15407
16426
|
while (dir !== "/") {
|
|
15408
|
-
if (
|
|
16427
|
+
if (fs20.existsSync(path23.join(dir, "package.json"))) return dir;
|
|
15409
16428
|
dir = path23.dirname(dir);
|
|
15410
16429
|
}
|
|
15411
16430
|
return process.cwd();
|
|
15412
16431
|
}
|
|
15413
16432
|
function copyDirRecursive(src, dest) {
|
|
15414
|
-
|
|
15415
|
-
for (const entry of
|
|
16433
|
+
fs20.mkdirSync(dest, { recursive: true });
|
|
16434
|
+
for (const entry of fs20.readdirSync(src, { withFileTypes: true })) {
|
|
15416
16435
|
const srcPath = path23.join(src, entry.name);
|
|
15417
16436
|
const destPath = path23.join(dest, entry.name);
|
|
15418
16437
|
if (entry.isDirectory()) {
|
|
15419
16438
|
copyDirRecursive(srcPath, destPath);
|
|
15420
16439
|
} else {
|
|
15421
|
-
|
|
16440
|
+
fs20.copyFileSync(srcPath, destPath);
|
|
15422
16441
|
}
|
|
15423
16442
|
}
|
|
15424
16443
|
}
|
|
@@ -15435,16 +16454,70 @@ function promptLine(question) {
|
|
|
15435
16454
|
});
|
|
15436
16455
|
});
|
|
15437
16456
|
}
|
|
15438
|
-
|
|
16457
|
+
async function resolveRouter(config, opts, isTTY) {
|
|
16458
|
+
if (config.defaults?.router) return config.defaults.router;
|
|
16459
|
+
let kind = opts.routerKind;
|
|
16460
|
+
let baseUrl = opts.routerBaseUrl;
|
|
16461
|
+
let apiKeyEnv = opts.routerApiKeyEnv;
|
|
16462
|
+
if (isTTY && (!kind || !baseUrl)) {
|
|
16463
|
+
const k = await promptLine(chalk4.cyan(` Router kind [${DEFAULT_ROUTER_KIND}]: `));
|
|
16464
|
+
kind = kind ?? (k || DEFAULT_ROUTER_KIND);
|
|
16465
|
+
const b = await promptLine(chalk4.cyan(` Router base URL [${DEFAULT_ROUTER_BASE_URL}]: `));
|
|
16466
|
+
baseUrl = baseUrl ?? (b || DEFAULT_ROUTER_BASE_URL);
|
|
16467
|
+
const e = await promptLine(chalk4.cyan(" Env var holding the API key (optional): "));
|
|
16468
|
+
apiKeyEnv = apiKeyEnv ?? (e || void 0);
|
|
16469
|
+
}
|
|
16470
|
+
const resolvedKind = kind && isRouterKind(kind) ? kind : DEFAULT_ROUTER_KIND;
|
|
16471
|
+
const resolvedBase = baseUrl || DEFAULT_ROUTER_BASE_URL;
|
|
16472
|
+
if (!isTTY && !opts.routerBaseUrl) {
|
|
16473
|
+
console.log(chalk4.dim(` - no router flags given; defaulting to ${resolvedKind} (${resolvedBase})`));
|
|
16474
|
+
console.log(chalk4.dim(" set a credential: squadrant config set defaults.router.apiKeyEnv <VAR>"));
|
|
16475
|
+
}
|
|
16476
|
+
return { kind: resolvedKind, baseUrl: resolvedBase, ...apiKeyEnv ? { apiKeyEnv } : {} };
|
|
16477
|
+
}
|
|
16478
|
+
async function askProvider(current, auth) {
|
|
16479
|
+
console.log(chalk4.bold("\n Choose your provider:"));
|
|
16480
|
+
for (const p of PROVIDER_PRESETS) {
|
|
16481
|
+
const currentTag = p.id === current ? chalk4.dim(" (current)") : "";
|
|
16482
|
+
console.log(` ${chalk4.cyan(`${p.id})`)} ${p.label}${currentTag}`);
|
|
16483
|
+
console.log(chalk4.dim(` ${p.summary}`));
|
|
16484
|
+
}
|
|
16485
|
+
if (!auth.authenticated) {
|
|
16486
|
+
console.log(chalk4.yellow("\n \u26A0 No Anthropic credential detected \u2014 preset a will not work; consider b or c."));
|
|
16487
|
+
}
|
|
16488
|
+
const answer = (await promptLine(chalk4.cyan(`
|
|
16489
|
+
Provider [a/b/c/d] (default ${current}): `))).toLowerCase();
|
|
16490
|
+
if (!answer) return current;
|
|
16491
|
+
if (isProviderPresetId(answer)) return answer;
|
|
16492
|
+
console.log(chalk4.yellow(` \u26A0 Unknown choice '${answer}' \u2014 keeping '${current}'.`));
|
|
16493
|
+
return current;
|
|
16494
|
+
}
|
|
16495
|
+
function warnIfNoAnthropic(preset, auth) {
|
|
16496
|
+
if (preset !== "a" || auth.authenticated) return;
|
|
16497
|
+
console.log(chalk4.yellow(" \u26A0 No Anthropic credential detected (`claude auth status`)."));
|
|
16498
|
+
console.log(chalk4.dim(" Preset A (Claude Pro/Max or an API key) may not work \u2014 consider preset b or c."));
|
|
16499
|
+
}
|
|
16500
|
+
var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c|d (non-interactive)").option("--router-kind <kind>", "router kind for --preset c (opencode-go|openrouter|ccr|litellm|custom)").option("--router-base-url <url>", "router base URL for --preset c").option("--router-api-key-env <env>", "env var holding the router credential for --preset c").action(async (opts) => {
|
|
15439
16501
|
const hubPath = resolveHome(opts.hub);
|
|
15440
16502
|
const pkgRoot = findPackageRoot();
|
|
15441
16503
|
const configDir = path23.join(os13.homedir(), ".config", "squadrant");
|
|
15442
16504
|
const isTTY = process.stdin.isTTY === true;
|
|
15443
16505
|
console.log(chalk4.bold("\nSquadrant Init\n"));
|
|
15444
|
-
|
|
16506
|
+
let explicitPreset;
|
|
16507
|
+
if (opts.preset !== void 0) {
|
|
16508
|
+
const candidate = opts.preset.toLowerCase();
|
|
16509
|
+
if (!isProviderPresetId(candidate)) {
|
|
16510
|
+
console.error(chalk4.red(` \u2718 Unknown --preset '${opts.preset}'. Valid values: a, b, c, d`));
|
|
16511
|
+
process.exitCode = 1;
|
|
16512
|
+
return;
|
|
16513
|
+
}
|
|
16514
|
+
explicitPreset = candidate;
|
|
16515
|
+
}
|
|
16516
|
+
if (!isTTY && !explicitPreset) {
|
|
15445
16517
|
console.log(" Run these steps to get started:\n");
|
|
15446
|
-
console.log(chalk4.bold(" 1/5 Hub vault"));
|
|
16518
|
+
console.log(chalk4.bold(" 1/5 Hub vault + provider"));
|
|
15447
16519
|
console.log(chalk4.cyan(` squadrant init --hub ${opts.hub}`));
|
|
16520
|
+
console.log(chalk4.dim(" Choose a provider: squadrant init --preset a|b|c|d"));
|
|
15448
16521
|
console.log(chalk4.bold("\n 2/5 Agent + projection setup"));
|
|
15449
16522
|
console.log(" (handled automatically by: " + chalk4.cyan("squadrant init") + ")");
|
|
15450
16523
|
console.log(chalk4.bold("\n 3/5 Plugins \u2014 open Claude Code and run:"));
|
|
@@ -15464,8 +16537,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
15464
16537
|
}
|
|
15465
16538
|
const wsRegistry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
15466
16539
|
try {
|
|
15467
|
-
if (
|
|
15468
|
-
const existing = JSON.parse(
|
|
16540
|
+
if (fs20.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
16541
|
+
const existing = JSON.parse(fs20.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
|
|
15469
16542
|
wsRegistry.get(existing.workspace ?? "obsidian");
|
|
15470
16543
|
}
|
|
15471
16544
|
} catch (err) {
|
|
@@ -15473,45 +16546,76 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
15473
16546
|
return;
|
|
15474
16547
|
}
|
|
15475
16548
|
stepHeader(1, 5, "Hub vault");
|
|
15476
|
-
|
|
16549
|
+
const configExists = fs20.existsSync(DEFAULT_CONFIG_PATH);
|
|
16550
|
+
let config;
|
|
16551
|
+
if (configExists) {
|
|
15477
16552
|
console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
|
|
16553
|
+
config = loadConfig();
|
|
15478
16554
|
} else {
|
|
15479
|
-
|
|
16555
|
+
config = getDefaultConfig();
|
|
15480
16556
|
config.hubVault = hubPath;
|
|
16557
|
+
}
|
|
16558
|
+
const auth = detectClaudeAuth();
|
|
16559
|
+
let chosenPreset = explicitPreset;
|
|
16560
|
+
if (!chosenPreset && isTTY) {
|
|
16561
|
+
chosenPreset = await askProvider(detectProviderPresetId(config), auth);
|
|
16562
|
+
}
|
|
16563
|
+
let appliedChanges = [];
|
|
16564
|
+
if (chosenPreset) {
|
|
16565
|
+
const label = PROVIDER_PRESETS.find((p) => p.id === chosenPreset)?.label ?? chosenPreset;
|
|
16566
|
+
const router = chosenPreset === "c" ? await resolveRouter(config, opts, isTTY) : void 0;
|
|
16567
|
+
const overwrite = !configExists || explicitPreset !== void 0;
|
|
16568
|
+
const result = applyProviderPreset(config, chosenPreset, { router, overwrite });
|
|
16569
|
+
appliedChanges = result.changes;
|
|
16570
|
+
config = result.config;
|
|
16571
|
+
console.log(chalk4.bold(`
|
|
16572
|
+
Provider: ${chosenPreset.toUpperCase()} \u2014 ${label}`));
|
|
16573
|
+
if (appliedChanges.length) {
|
|
16574
|
+
console.log(" Will change:");
|
|
16575
|
+
for (const change of appliedChanges) console.log(chalk4.dim(` - ${change}`));
|
|
16576
|
+
} else {
|
|
16577
|
+
console.log(chalk4.dim(" - provider config already set (unchanged)"));
|
|
16578
|
+
}
|
|
16579
|
+
if (!isTTY) warnIfNoAnthropic(chosenPreset, auth);
|
|
16580
|
+
}
|
|
16581
|
+
if (!configExists) {
|
|
15481
16582
|
saveConfig(config);
|
|
15482
16583
|
console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
|
|
16584
|
+
} else if (appliedChanges.length) {
|
|
16585
|
+
saveConfig(config);
|
|
16586
|
+
console.log(chalk4.green(` \u2714 Config updated at ${DEFAULT_CONFIG_PATH}`));
|
|
15483
16587
|
}
|
|
15484
16588
|
const hubTemplate = path23.join(pkgRoot, "obsidian", "hub");
|
|
15485
|
-
if (
|
|
16589
|
+
if (fs20.existsSync(hubPath)) {
|
|
15486
16590
|
console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}`));
|
|
15487
|
-
} else if (
|
|
16591
|
+
} else if (fs20.existsSync(hubTemplate)) {
|
|
15488
16592
|
copyDirRecursive(hubTemplate, hubPath);
|
|
15489
16593
|
console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
|
|
15490
16594
|
} else {
|
|
15491
|
-
|
|
16595
|
+
fs20.mkdirSync(hubPath, { recursive: true });
|
|
15492
16596
|
console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty directory at ${hubPath}`));
|
|
15493
16597
|
}
|
|
15494
16598
|
const hubDashboardSrc = path23.join(pkgRoot, "obsidian", "hub", "dashboard.md");
|
|
15495
16599
|
const hubDashboardDest = path23.join(hubPath, "dashboard.md");
|
|
15496
|
-
if (
|
|
15497
|
-
|
|
16600
|
+
if (fs20.existsSync(hubDashboardSrc)) {
|
|
16601
|
+
fs20.copyFileSync(hubDashboardSrc, hubDashboardDest);
|
|
15498
16602
|
console.log(chalk4.green(` \u2714 Dashboard refreshed`));
|
|
15499
16603
|
}
|
|
15500
|
-
|
|
16604
|
+
fs20.mkdirSync(path23.join(hubPath, "projects"), { recursive: true });
|
|
15501
16605
|
ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
|
|
15502
16606
|
console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
|
|
15503
16607
|
stepHeader(2, 5, "Agent + projection setup");
|
|
15504
16608
|
const settingsPath = path23.join(os13.homedir(), ".claude", "settings.json");
|
|
15505
16609
|
try {
|
|
15506
16610
|
let settings = {};
|
|
15507
|
-
if (
|
|
15508
|
-
settings = JSON.parse(
|
|
16611
|
+
if (fs20.existsSync(settingsPath)) {
|
|
16612
|
+
settings = JSON.parse(fs20.readFileSync(settingsPath, "utf-8"));
|
|
15509
16613
|
}
|
|
15510
16614
|
const env = settings.env || {};
|
|
15511
16615
|
if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
|
|
15512
16616
|
settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
|
|
15513
|
-
|
|
15514
|
-
|
|
16617
|
+
fs20.mkdirSync(path23.dirname(settingsPath), { recursive: true });
|
|
16618
|
+
fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
15515
16619
|
console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
|
|
15516
16620
|
} else {
|
|
15517
16621
|
console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
|
|
@@ -15591,7 +16695,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
|
|
|
15591
16695
|
init_dist();
|
|
15592
16696
|
init_dist2();
|
|
15593
16697
|
import { Command as Command3 } from "commander";
|
|
15594
|
-
import
|
|
16698
|
+
import fs21 from "fs";
|
|
15595
16699
|
import path24 from "path";
|
|
15596
16700
|
import chalk5 from "chalk";
|
|
15597
16701
|
function restartAfterProjectsAdd(opts) {
|
|
@@ -15606,20 +16710,20 @@ function restartAfterProjectsAdd(opts) {
|
|
|
15606
16710
|
function findPackageRoot2() {
|
|
15607
16711
|
let dir = path24.dirname(new URL(import.meta.url).pathname);
|
|
15608
16712
|
while (dir !== "/") {
|
|
15609
|
-
if (
|
|
16713
|
+
if (fs21.existsSync(path24.join(dir, "package.json"))) return dir;
|
|
15610
16714
|
dir = path24.dirname(dir);
|
|
15611
16715
|
}
|
|
15612
16716
|
return process.cwd();
|
|
15613
16717
|
}
|
|
15614
16718
|
function copyDirRecursive2(src, dest) {
|
|
15615
|
-
|
|
15616
|
-
for (const entry of
|
|
16719
|
+
fs21.mkdirSync(dest, { recursive: true });
|
|
16720
|
+
for (const entry of fs21.readdirSync(src, { withFileTypes: true })) {
|
|
15617
16721
|
const srcPath = path24.join(src, entry.name);
|
|
15618
16722
|
const destPath = path24.join(dest, entry.name);
|
|
15619
16723
|
if (entry.isDirectory()) {
|
|
15620
16724
|
copyDirRecursive2(srcPath, destPath);
|
|
15621
16725
|
} else {
|
|
15622
|
-
|
|
16726
|
+
fs21.copyFileSync(srcPath, destPath);
|
|
15623
16727
|
}
|
|
15624
16728
|
}
|
|
15625
16729
|
}
|
|
@@ -15655,7 +16759,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
15655
16759
|
process.exit(1);
|
|
15656
16760
|
}
|
|
15657
16761
|
const resolvedPath = resolveHome(projectPath);
|
|
15658
|
-
if (!
|
|
16762
|
+
if (!fs21.existsSync(path24.join(resolvedPath, ".git"))) {
|
|
15659
16763
|
console.log(chalk5.yellow(`
|
|
15660
16764
|
\u26A0 No .git found at ${resolvedPath}. Make sure this is the project root, not a parent directory.
|
|
15661
16765
|
`));
|
|
@@ -15724,19 +16828,19 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
15724
16828
|
restartAfterProjectsAdd({ noRestart: opts.restart === false });
|
|
15725
16829
|
const pkgRoot = findPackageRoot2();
|
|
15726
16830
|
const spokeTemplate = path24.join(pkgRoot, "obsidian", "spoke");
|
|
15727
|
-
if (
|
|
16831
|
+
if (fs21.existsSync(spokeVault)) {
|
|
15728
16832
|
console.log(chalk5.yellow(` \u26A0 Spoke vault already exists at ${spokeVault}, skipping scaffold`));
|
|
15729
|
-
} else if (
|
|
16833
|
+
} else if (fs21.existsSync(spokeTemplate)) {
|
|
15730
16834
|
copyDirRecursive2(spokeTemplate, spokeVault);
|
|
15731
16835
|
const statusPath = path24.join(spokeVault, "status.md");
|
|
15732
|
-
if (
|
|
15733
|
-
const content =
|
|
16836
|
+
if (fs21.existsSync(statusPath)) {
|
|
16837
|
+
const content = fs21.readFileSync(statusPath, "utf-8");
|
|
15734
16838
|
const updated = content.replace(/^project: unnamed/m, `project: ${name}`);
|
|
15735
|
-
|
|
16839
|
+
fs21.writeFileSync(statusPath, updated);
|
|
15736
16840
|
}
|
|
15737
16841
|
console.log(chalk5.green(` \u2714 Spoke vault scaffolded at ${spokeVault}`));
|
|
15738
16842
|
} else {
|
|
15739
|
-
|
|
16843
|
+
fs21.mkdirSync(spokeVault, { recursive: true });
|
|
15740
16844
|
console.log(chalk5.yellow(` \u26A0 Spoke template not found; created empty dir at ${spokeVault}`));
|
|
15741
16845
|
}
|
|
15742
16846
|
console.log("");
|
|
@@ -15804,7 +16908,7 @@ init_dist4();
|
|
|
15804
16908
|
init_dist3();
|
|
15805
16909
|
init_dist();
|
|
15806
16910
|
import { Command as Command5 } from "commander";
|
|
15807
|
-
import { execSync as
|
|
16911
|
+
import { execSync as execSync10 } from "child_process";
|
|
15808
16912
|
import path25 from "path";
|
|
15809
16913
|
import os14 from "os";
|
|
15810
16914
|
import chalk7 from "chalk";
|
|
@@ -15815,7 +16919,7 @@ var TASK_PROMPTS = {
|
|
|
15815
16919
|
"wiki-aggregate": "Run a wiki aggregation pass using the squadrant:command-ops skill. Scan each spoke wiki index, identify shared knowledge worth promoting, write hub wiki pages, and exit when done."
|
|
15816
16920
|
};
|
|
15817
16921
|
function detectCurrentWorkspace() {
|
|
15818
|
-
const out =
|
|
16922
|
+
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
15819
16923
|
const match = out.match(/workspace:\d+/);
|
|
15820
16924
|
if (!match) {
|
|
15821
16925
|
throw new Error("Could not detect current cmux workspace. Run `squadrant command` from inside a cmux workspace.");
|
|
@@ -17133,7 +18237,7 @@ init_dist4();
|
|
|
17133
18237
|
init_dist();
|
|
17134
18238
|
init_dist2();
|
|
17135
18239
|
import { Command as Command12 } from "commander";
|
|
17136
|
-
import
|
|
18240
|
+
import fs22 from "fs";
|
|
17137
18241
|
import path26 from "path";
|
|
17138
18242
|
import os15 from "os";
|
|
17139
18243
|
import chalk12 from "chalk";
|
|
@@ -17170,7 +18274,7 @@ async function runSideSpawn2(input) {
|
|
|
17170
18274
|
prompt: input.topic,
|
|
17171
18275
|
workdir: spawnCwd,
|
|
17172
18276
|
role: "side",
|
|
17173
|
-
promptFile:
|
|
18277
|
+
promptFile: fs22.existsSync(promptFile) ? promptFile : void 0,
|
|
17174
18278
|
interactive: true,
|
|
17175
18279
|
permissionMode: config.defaults.permissions?.crew ?? "auto",
|
|
17176
18280
|
...sideModel ? { model: sideModel } : {},
|
|
@@ -17277,7 +18381,7 @@ sideCommand.command("close").description("Close a side-session (closes its tab)"
|
|
|
17277
18381
|
init_dist();
|
|
17278
18382
|
init_dist4();
|
|
17279
18383
|
import { Command as Command13 } from "commander";
|
|
17280
|
-
import { execSync as
|
|
18384
|
+
import { execSync as execSync11 } from "child_process";
|
|
17281
18385
|
import chalk14 from "chalk";
|
|
17282
18386
|
|
|
17283
18387
|
// packages/web/dist/read-status.js
|
|
@@ -17433,7 +18537,7 @@ function renderDashboard(rows, opts) {
|
|
|
17433
18537
|
|
|
17434
18538
|
// packages/web/dist/sync-hub.js
|
|
17435
18539
|
init_dist();
|
|
17436
|
-
import
|
|
18540
|
+
import fs23 from "fs";
|
|
17437
18541
|
import path27 from "path";
|
|
17438
18542
|
function buildMirrorMarkdown(s) {
|
|
17439
18543
|
const fenced = "```";
|
|
@@ -17460,8 +18564,8 @@ function buildMirrorMarkdown(s) {
|
|
|
17460
18564
|
function syncHub(deps) {
|
|
17461
18565
|
if (!deps.config.hubVault)
|
|
17462
18566
|
return [];
|
|
17463
|
-
const writeFile6 = deps.writeFile ?? ((p, c) =>
|
|
17464
|
-
const mkdir6 = deps.mkdir ?? ((p) =>
|
|
18567
|
+
const writeFile6 = deps.writeFile ?? ((p, c) => fs23.writeFileSync(p, c));
|
|
18568
|
+
const mkdir6 = deps.mkdir ?? ((p) => fs23.mkdirSync(p, { recursive: true }));
|
|
17465
18569
|
const projectsDir = path27.join(resolveHome(deps.config.hubVault), "projects");
|
|
17466
18570
|
mkdir6(projectsDir);
|
|
17467
18571
|
const out = [];
|
|
@@ -17481,6 +18585,7 @@ function syncHub(deps) {
|
|
|
17481
18585
|
// packages/web/dist/web-server.js
|
|
17482
18586
|
init_dist2();
|
|
17483
18587
|
import { createServer as createServer5 } from "http";
|
|
18588
|
+
import { dirname as dirname7, join as join29 } from "path";
|
|
17484
18589
|
|
|
17485
18590
|
// packages/web/dist/snapshot-merge.js
|
|
17486
18591
|
function mergeSnapshot(daemon, external, now) {
|
|
@@ -17946,6 +19051,24 @@ function renderEnv(ext) {
|
|
|
17946
19051
|
out.push(`</section>`);
|
|
17947
19052
|
return out.join("");
|
|
17948
19053
|
}
|
|
19054
|
+
function renderLogs() {
|
|
19055
|
+
return [
|
|
19056
|
+
`<section class="panel" data-panel="logs" role="tabpanel" aria-label="Logs">`,
|
|
19057
|
+
sectionHead("Daemon log", "Live tail of squadrantd.log \u2014 lifecycle events, delivery attempts, auto-launches and errors as the daemon writes them."),
|
|
19058
|
+
`<div class="log-controls">`,
|
|
19059
|
+
`<div class="filter-bar" role="group" aria-label="Filter log lines by severity">`,
|
|
19060
|
+
`<button class="filter-btn on" data-log-sev="all" aria-pressed="true">All</button>`,
|
|
19061
|
+
`<button class="filter-btn" data-log-sev="warn" aria-pressed="false">Warn+</button>`,
|
|
19062
|
+
`<button class="filter-btn" data-log-sev="error" aria-pressed="false">Errors</button>`,
|
|
19063
|
+
`</div>`,
|
|
19064
|
+
`<button class="filter-btn" data-log-pause aria-pressed="false">Pause</button>`,
|
|
19065
|
+
`<button class="filter-btn" data-log-copy>Copy</button>`,
|
|
19066
|
+
`<span class="log-status" data-log-status>connecting\u2026</span>`,
|
|
19067
|
+
`</div>`,
|
|
19068
|
+
`<div class="log-view mono" data-log-view aria-live="polite" aria-label="Daemon log lines"></div>`,
|
|
19069
|
+
`</section>`
|
|
19070
|
+
].join("");
|
|
19071
|
+
}
|
|
17949
19072
|
var ATTN_ORDER = { stale: 0, gone: 1, alive: 2, unknown: 3, stopped: 4 };
|
|
17950
19073
|
var STATE_LABEL = { alive: "running", stale: "blocked", gone: "errored", unknown: "idle", stopped: "offline" };
|
|
17951
19074
|
var STATE_CLS = { alive: "s-alive", stale: "s-stale", gone: "s-gone", unknown: "s-unknown", stopped: "s-stopped" };
|
|
@@ -18074,7 +19197,8 @@ var TABS = [
|
|
|
18074
19197
|
["overview", "Overview"],
|
|
18075
19198
|
["projects", "Projects"],
|
|
18076
19199
|
["daemon", "Daemon"],
|
|
18077
|
-
["environment", "Environment"]
|
|
19200
|
+
["environment", "Environment"],
|
|
19201
|
+
["logs", "Logs"]
|
|
18078
19202
|
];
|
|
18079
19203
|
function tabNav() {
|
|
18080
19204
|
const btns = TABS.map(([id, label], i) => `<button class="tab${i === 0 ? " on" : ""}" data-tab="${id}" role="tab" aria-selected="${i === 0 ? "true" : "false"}">${esc(label)}</button>`).join("");
|
|
@@ -18123,6 +19247,7 @@ function renderContent(snap) {
|
|
|
18123
19247
|
out.push(renderProjects(snap, col.now));
|
|
18124
19248
|
out.push(renderDaemon(snap));
|
|
18125
19249
|
out.push(renderEnv(snap.external));
|
|
19250
|
+
out.push(renderLogs());
|
|
18126
19251
|
out.push(`</div>`);
|
|
18127
19252
|
out.push(metricsBlob(col, linkLost));
|
|
18128
19253
|
return out.join("\n");
|
|
@@ -18364,6 +19489,17 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
18364
19489
|
|
|
18365
19490
|
.empty{border:1px dashed var(--bezel);border-radius:12px;padding:24px;color:var(--ink-dim);text-align:center;background:var(--panel)}
|
|
18366
19491
|
|
|
19492
|
+
/* Logs (live daemon-log tail) */
|
|
19493
|
+
.log-controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:0 0 12px}
|
|
19494
|
+
.log-controls .filter-bar{margin:0}
|
|
19495
|
+
.log-status{margin-left:auto;color:var(--ink-dim);font-size:11px}
|
|
19496
|
+
.log-view{height:min(58vh,520px);overflow:auto;background:#0b1220;border:1px solid var(--bezel);border-radius:12px;
|
|
19497
|
+
padding:12px 14px;box-shadow:var(--shadow);font-size:12px;line-height:1.55}
|
|
19498
|
+
.log-line{white-space:pre-wrap;word-break:break-word;color:#c7d2e0}
|
|
19499
|
+
.log-line.l-warn{color:#f0c674}
|
|
19500
|
+
.log-line.l-error{color:#ff8a80}
|
|
19501
|
+
.log-view .empty-log{color:#6b7a90}
|
|
19502
|
+
|
|
18367
19503
|
:focus-visible{outline:2px solid var(--hud);outline-offset:2px;border-radius:4px}
|
|
18368
19504
|
|
|
18369
19505
|
@keyframes beat{0%,100%{box-shadow:0 0 0 0 rgba(58,210,159,.45)}50%{box-shadow:0 0 0 6px rgba(58,210,159,0)}}
|
|
@@ -18377,6 +19513,7 @@ body{margin:0;min-height:100vh;background:radial-gradient(1200px 760px at 50% -2
|
|
|
18377
19513
|
var CLIENT_JS = `
|
|
18378
19514
|
(function(){
|
|
18379
19515
|
var activeTab='live';var hist={};var prev={};var currentFilter='all';var currentSort='attention';var sortAsc=true;var currentSearch='';var expandedProjects={};
|
|
19516
|
+
var logLines=[];var logSev='all';var logPaused=false;var logAutoscroll=true;var logConnected=false;var logScrollSave=0;var LOG_MAX=1000;
|
|
18380
19517
|
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();}
|
|
18381
19518
|
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);}
|
|
18382
19519
|
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"/>';}
|
|
@@ -18389,9 +19526,19 @@ var CLIENT_JS = `
|
|
|
18389
19526
|
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;});}
|
|
18390
19527
|
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);});}
|
|
18391
19528
|
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);}});}
|
|
18392
|
-
function
|
|
18393
|
-
|
|
18394
|
-
|
|
19529
|
+
function logSevOf(s){return /error|failed|fatal/i.test(s)?'error':/warn/i.test(s)?'warn':'info';}
|
|
19530
|
+
function logMatch(l){if(logSev==='all')return true;if(logSev==='error')return logSevOf(l)==='error';if(logSev==='warn')return logSevOf(l)!=='info';return true;}
|
|
19531
|
+
function escHtml(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
|
|
19532
|
+
function logVisible(){return logLines.filter(logMatch);}
|
|
19533
|
+
function renderLogs(){var v=document.querySelector('[data-log-view]');if(!v)return;var vis=logVisible();var html='';for(var i=0;i<vis.length;i++){html+='<div class="log-line l-'+logSevOf(vis[i])+'">'+escHtml(vis[i])+'</div>';}v.innerHTML=html||'<div class="empty-log">waiting for log output\u2026</div>';if(logAutoscroll)v.scrollTop=v.scrollHeight;else v.scrollTop=logScrollSave;
|
|
19534
|
+
var sb=document.querySelectorAll('[data-log-sev]');for(var j=0;j<sb.length;j++){var on=sb[j].getAttribute('data-log-sev')===logSev;sb[j].classList.toggle('on',on);sb[j].setAttribute('aria-pressed',on?'true':'false');}
|
|
19535
|
+
var pb=document.querySelector('[data-log-pause]');if(pb){pb.textContent=logPaused?'Resume':'Pause';pb.classList.toggle('on',logPaused);pb.setAttribute('aria-pressed',logPaused?'true':'false');}
|
|
19536
|
+
var st=document.querySelector('[data-log-status]');if(st){if(!logConnected)st.textContent='log stream reconnecting\u2026';else st.textContent=(logPaused?'paused \xB7 ':'')+vis.length+(vis.length===logLines.length?'':'/'+logLines.length)+' lines';}
|
|
19537
|
+
}
|
|
19538
|
+
function ingestLog(lines){if(!lines||!lines.length)return;logLines=logLines.concat(lines);if(logLines.length>LOG_MAX)logLines=logLines.slice(logLines.length-LOG_MAX);if(activeTab==='logs'&&!logPaused)renderLogs();}
|
|
19539
|
+
function refresh(){ingest();applyTab();drawSparks();countUps();applyFilter();applySearch();applySort();applyExpand();if(activeTab==='logs')renderLogs();}
|
|
19540
|
+
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');}if(activeTab==='logs')renderLogs();}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 sv=e.target&&e.target.closest?e.target.closest('[data-log-sev]'):null;if(sv){logSev=sv.getAttribute('data-log-sev');renderLogs();}var lp=e.target&&e.target.closest?e.target.closest('[data-log-pause]'):null;if(lp){logPaused=!logPaused;renderLogs();}var cp=e.target&&e.target.closest?e.target.closest('[data-log-copy]'):null;if(cp&&navigator.clipboard){navigator.clipboard.writeText(logVisible().join('\\n'));}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;}}});
|
|
19541
|
+
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','logs'];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();}});
|
|
18395
19542
|
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();}});
|
|
18396
19543
|
var led=document.getElementById('led');var conn=document.getElementById('conn');var updated=document.getElementById('updated');
|
|
18397
19544
|
function tickAge(){if(!generatedAt)return;var s=Math.max(0,Math.round((Date.now()-generatedAt)/1000));updated.textContent='updated '+s+'s ago';}
|
|
@@ -18400,6 +19547,11 @@ var CLIENT_JS = `
|
|
|
18400
19547
|
es.onopen=function(){conn.textContent='live';conn.className='conn live';led.className='led live';};
|
|
18401
19548
|
es.onerror=function(){conn.textContent='reconnecting';conn.className='conn down';led.className='led down';};
|
|
18402
19549
|
es.onmessage=function(e){try{var d=JSON.parse(e.data);document.getElementById('content').innerHTML=d.contentHtml;generatedAt=d.generatedAt;tickAge();refresh();}catch(_){}};
|
|
19550
|
+
document.addEventListener('scroll',function(e){var v=e.target;if(v&&v.getAttribute&&v.getAttribute('data-log-view')!==null){logAutoscroll=v.scrollTop+v.clientHeight>=v.scrollHeight-6;logScrollSave=v.scrollTop;}},true);
|
|
19551
|
+
var les=new EventSource('/logs');
|
|
19552
|
+
les.onopen=function(){logConnected=true;renderLogs();};
|
|
19553
|
+
les.onerror=function(){logConnected=false;renderLogs();};
|
|
19554
|
+
les.onmessage=function(e){try{var d=JSON.parse(e.data);if(!logPaused)ingestLog(d&&d.lines);}catch(_){}};
|
|
18403
19555
|
refresh();tickAge();
|
|
18404
19556
|
})();
|
|
18405
19557
|
`.trim();
|
|
@@ -18422,7 +19574,100 @@ function renderHtml(snap, opts = {}) {
|
|
|
18422
19574
|
].join("");
|
|
18423
19575
|
}
|
|
18424
19576
|
|
|
19577
|
+
// packages/web/dist/log-tail.js
|
|
19578
|
+
import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync4 } from "fs";
|
|
19579
|
+
var LOG_TAIL_BYTES = 256 * 1024;
|
|
19580
|
+
var LOG_RING_SIZE = 1e3;
|
|
19581
|
+
function defaultSize(path39) {
|
|
19582
|
+
try {
|
|
19583
|
+
return statSync4(path39).size;
|
|
19584
|
+
} catch {
|
|
19585
|
+
return 0;
|
|
19586
|
+
}
|
|
19587
|
+
}
|
|
19588
|
+
function defaultRead(path39, start, len) {
|
|
19589
|
+
if (len <= 0)
|
|
19590
|
+
return "";
|
|
19591
|
+
try {
|
|
19592
|
+
const fd = openSync3(path39, "r");
|
|
19593
|
+
try {
|
|
19594
|
+
const buf = Buffer.alloc(len);
|
|
19595
|
+
const n = readSync2(fd, buf, 0, len, start);
|
|
19596
|
+
return buf.subarray(0, n).toString("utf-8");
|
|
19597
|
+
} finally {
|
|
19598
|
+
closeSync3(fd);
|
|
19599
|
+
}
|
|
19600
|
+
} catch {
|
|
19601
|
+
return "";
|
|
19602
|
+
}
|
|
19603
|
+
}
|
|
19604
|
+
var LogTailer = class {
|
|
19605
|
+
path;
|
|
19606
|
+
ringSize;
|
|
19607
|
+
offset = 0;
|
|
19608
|
+
pending = "";
|
|
19609
|
+
ring = [];
|
|
19610
|
+
size;
|
|
19611
|
+
read;
|
|
19612
|
+
constructor(path39, ringSize = LOG_RING_SIZE, deps = {}) {
|
|
19613
|
+
this.path = path39;
|
|
19614
|
+
this.ringSize = ringSize;
|
|
19615
|
+
this.size = deps.size ?? defaultSize;
|
|
19616
|
+
this.read = deps.read ?? defaultRead;
|
|
19617
|
+
}
|
|
19618
|
+
/** Seed history from the last `maxBytes` and position the cursor at EOF.
|
|
19619
|
+
* Idempotent; call once at startup. A trailing partial line stays pending
|
|
19620
|
+
* so the next append completes it without loss or duplication. */
|
|
19621
|
+
init(maxBytes = LOG_TAIL_BYTES) {
|
|
19622
|
+
const size = this.size(this.path);
|
|
19623
|
+
this.pending = "";
|
|
19624
|
+
this.ring = [];
|
|
19625
|
+
if (size <= 0) {
|
|
19626
|
+
this.offset = size;
|
|
19627
|
+
return;
|
|
19628
|
+
}
|
|
19629
|
+
const start = Math.max(0, size - maxBytes);
|
|
19630
|
+
const parts = this.read(this.path, start, size - start).split("\n");
|
|
19631
|
+
const rest = parts.pop() ?? "";
|
|
19632
|
+
if (start > 0 && parts.length)
|
|
19633
|
+
parts.shift();
|
|
19634
|
+
this.push(parts);
|
|
19635
|
+
this.pending = rest;
|
|
19636
|
+
this.offset = size;
|
|
19637
|
+
}
|
|
19638
|
+
/** Complete lines appended since the previous poll. */
|
|
19639
|
+
poll() {
|
|
19640
|
+
const size = this.size(this.path);
|
|
19641
|
+
if (size < this.offset) {
|
|
19642
|
+
this.offset = 0;
|
|
19643
|
+
this.pending = "";
|
|
19644
|
+
}
|
|
19645
|
+
if (size <= this.offset)
|
|
19646
|
+
return [];
|
|
19647
|
+
const text = this.read(this.path, this.offset, size - this.offset);
|
|
19648
|
+
if (!text)
|
|
19649
|
+
return [];
|
|
19650
|
+
this.offset += Buffer.byteLength(text);
|
|
19651
|
+
const parts = (this.pending + text).split("\n");
|
|
19652
|
+
this.pending = parts.pop() ?? "";
|
|
19653
|
+
this.push(parts);
|
|
19654
|
+
return parts;
|
|
19655
|
+
}
|
|
19656
|
+
/** The buffered backlog (oldest first) replayed to a newly-connected client. */
|
|
19657
|
+
backlog() {
|
|
19658
|
+
return this.ring.slice();
|
|
19659
|
+
}
|
|
19660
|
+
push(lines) {
|
|
19661
|
+
for (const l of lines)
|
|
19662
|
+
this.ring.push(l);
|
|
19663
|
+
const over = this.ring.length - this.ringSize;
|
|
19664
|
+
if (over > 0)
|
|
19665
|
+
this.ring.splice(0, over);
|
|
19666
|
+
}
|
|
19667
|
+
};
|
|
19668
|
+
|
|
18425
19669
|
// packages/web/dist/web-server.js
|
|
19670
|
+
var LOG_POLL_MS = 1e3;
|
|
18426
19671
|
async function querySnapshot(sockPath) {
|
|
18427
19672
|
try {
|
|
18428
19673
|
const reply = await sendRequest(sockPath, { kind: "snapshot" });
|
|
@@ -18435,6 +19680,9 @@ async function startWebServer(opts) {
|
|
|
18435
19680
|
const host = opts.host ?? "127.0.0.1";
|
|
18436
19681
|
const now = opts.now ?? (() => Date.now());
|
|
18437
19682
|
const clients = /* @__PURE__ */ new Set();
|
|
19683
|
+
const logClients = /* @__PURE__ */ new Set();
|
|
19684
|
+
const logPath2 = opts.logPath ?? join29(dirname7(opts.sockPath), "squadrantd.log");
|
|
19685
|
+
const tailer = new LogTailer(logPath2);
|
|
18438
19686
|
let latest = null;
|
|
18439
19687
|
async function tick() {
|
|
18440
19688
|
const daemon = await querySnapshot(opts.sockPath);
|
|
@@ -18450,6 +19698,19 @@ async function startWebServer(opts) {
|
|
|
18450
19698
|
}
|
|
18451
19699
|
}
|
|
18452
19700
|
}
|
|
19701
|
+
function broadcastLog(lines) {
|
|
19702
|
+
if (lines.length === 0)
|
|
19703
|
+
return;
|
|
19704
|
+
const payload = `data: ${JSON.stringify({ lines })}
|
|
19705
|
+
|
|
19706
|
+
`;
|
|
19707
|
+
for (const res of logClients) {
|
|
19708
|
+
try {
|
|
19709
|
+
res.write(payload);
|
|
19710
|
+
} catch {
|
|
19711
|
+
}
|
|
19712
|
+
}
|
|
19713
|
+
}
|
|
18453
19714
|
const server = createServer5((req, res) => {
|
|
18454
19715
|
if (req.method !== "GET") {
|
|
18455
19716
|
res.writeHead(405).end();
|
|
@@ -18475,6 +19736,22 @@ async function startWebServer(opts) {
|
|
|
18475
19736
|
req.on("close", () => clients.delete(res));
|
|
18476
19737
|
return;
|
|
18477
19738
|
}
|
|
19739
|
+
if (req.url === "/logs") {
|
|
19740
|
+
res.writeHead(200, {
|
|
19741
|
+
"Content-Type": "text/event-stream",
|
|
19742
|
+
"Cache-Control": "no-cache",
|
|
19743
|
+
Connection: "keep-alive"
|
|
19744
|
+
});
|
|
19745
|
+
res.write("retry: 3000\n\n");
|
|
19746
|
+
const backlog = tailer.backlog();
|
|
19747
|
+
if (backlog.length)
|
|
19748
|
+
res.write(`data: ${JSON.stringify({ lines: backlog })}
|
|
19749
|
+
|
|
19750
|
+
`);
|
|
19751
|
+
logClients.add(res);
|
|
19752
|
+
req.on("close", () => logClients.delete(res));
|
|
19753
|
+
return;
|
|
19754
|
+
}
|
|
18478
19755
|
res.writeHead(404).end();
|
|
18479
19756
|
});
|
|
18480
19757
|
await new Promise((resolve4, reject) => {
|
|
@@ -18489,17 +19766,33 @@ async function startWebServer(opts) {
|
|
|
18489
19766
|
});
|
|
18490
19767
|
}, opts.intervalMs);
|
|
18491
19768
|
timer.unref?.();
|
|
19769
|
+
tailer.init();
|
|
19770
|
+
const logTimer = setInterval(() => {
|
|
19771
|
+
try {
|
|
19772
|
+
broadcastLog(tailer.poll());
|
|
19773
|
+
} catch {
|
|
19774
|
+
}
|
|
19775
|
+
}, LOG_POLL_MS);
|
|
19776
|
+
logTimer.unref?.();
|
|
18492
19777
|
return {
|
|
18493
19778
|
port: boundPort,
|
|
18494
19779
|
close: () => new Promise((resolve4) => {
|
|
18495
19780
|
clearInterval(timer);
|
|
19781
|
+
clearInterval(logTimer);
|
|
18496
19782
|
for (const res of clients) {
|
|
18497
19783
|
try {
|
|
18498
19784
|
res.end();
|
|
18499
19785
|
} catch {
|
|
18500
19786
|
}
|
|
18501
19787
|
}
|
|
19788
|
+
for (const res of logClients) {
|
|
19789
|
+
try {
|
|
19790
|
+
res.end();
|
|
19791
|
+
} catch {
|
|
19792
|
+
}
|
|
19793
|
+
}
|
|
18502
19794
|
clients.clear();
|
|
19795
|
+
logClients.clear();
|
|
18503
19796
|
server.close(() => resolve4());
|
|
18504
19797
|
})
|
|
18505
19798
|
};
|
|
@@ -18509,7 +19802,7 @@ async function startWebServer(opts) {
|
|
|
18509
19802
|
init_dist();
|
|
18510
19803
|
var SOCK3 = DAEMON_SOCK_PATH;
|
|
18511
19804
|
function detectCurrentWorkspace2() {
|
|
18512
|
-
const out =
|
|
19805
|
+
const out = execSync11(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
18513
19806
|
const match = out.match(/workspace:\d+/);
|
|
18514
19807
|
if (!match) {
|
|
18515
19808
|
throw new Error("Could not detect current cmux workspace. Run `squadrant dashboard --pane` from inside a cmux workspace.");
|
|
@@ -18592,8 +19885,8 @@ init_dist3();
|
|
|
18592
19885
|
init_dist4();
|
|
18593
19886
|
init_dist2();
|
|
18594
19887
|
import { Command as Command14 } from "commander";
|
|
18595
|
-
import { execSync as
|
|
18596
|
-
import
|
|
19888
|
+
import { execSync as execSync12, execFileSync as execFileSync10 } from "child_process";
|
|
19889
|
+
import fs24 from "fs";
|
|
18597
19890
|
import path28 from "path";
|
|
18598
19891
|
import os16 from "os";
|
|
18599
19892
|
import chalk15 from "chalk";
|
|
@@ -18724,7 +20017,7 @@ function resolveAnthropicRefusal(role, workspaceName, agentName, model, readOpen
|
|
|
18724
20017
|
function ensureCmuxReady(headless) {
|
|
18725
20018
|
if (headless || isInsideCmux()) return;
|
|
18726
20019
|
console.log(chalk15.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
18727
|
-
|
|
20020
|
+
execSync12(`open "${CMUX_APP}"`, { stdio: "inherit" });
|
|
18728
20021
|
console.log(chalk15.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
|
|
18729
20022
|
process.exit(0);
|
|
18730
20023
|
}
|
|
@@ -18921,12 +20214,12 @@ var launchCommand = new Command14("launch").description(
|
|
|
18921
20214
|
}
|
|
18922
20215
|
if (opts.all) {
|
|
18923
20216
|
const hubPath = resolveHome(config.hubVault);
|
|
18924
|
-
|
|
20217
|
+
fs24.mkdirSync(hubPath, { recursive: true });
|
|
18925
20218
|
console.log(chalk15.bold("\nLaunching all captain workspaces\n"));
|
|
18926
20219
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
18927
20220
|
const projPath = resolveHome(proj.path);
|
|
18928
20221
|
const spokePath = resolveHome(proj.spokeVault);
|
|
18929
|
-
if (!
|
|
20222
|
+
if (!fs24.existsSync(spokePath)) {
|
|
18930
20223
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
18931
20224
|
await ensureSpokeLayout(spokeDriver);
|
|
18932
20225
|
console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
@@ -18964,7 +20257,7 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
18964
20257
|
const proj = config.projects[name];
|
|
18965
20258
|
const projPath = resolveHome(proj.path);
|
|
18966
20259
|
const spokePath = resolveHome(proj.spokeVault);
|
|
18967
|
-
if (!
|
|
20260
|
+
if (!fs24.existsSync(spokePath)) {
|
|
18968
20261
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
18969
20262
|
await ensureSpokeLayout(spokeDriver);
|
|
18970
20263
|
console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
@@ -18988,7 +20281,7 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
18988
20281
|
const proj = config.projects[project];
|
|
18989
20282
|
const projPath = resolveHome(proj.path);
|
|
18990
20283
|
const spokePath = resolveHome(proj.spokeVault);
|
|
18991
|
-
if (!
|
|
20284
|
+
if (!fs24.existsSync(spokePath)) {
|
|
18992
20285
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
|
|
18993
20286
|
await ensureSpokeLayout(spokeDriver);
|
|
18994
20287
|
console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
@@ -19124,24 +20417,24 @@ Shutting down captain workspace for '${project}'...
|
|
|
19124
20417
|
// packages/cli/src/commands/feedback.ts
|
|
19125
20418
|
init_dist();
|
|
19126
20419
|
import { Command as Command16 } from "commander";
|
|
19127
|
-
import
|
|
20420
|
+
import fs25 from "fs";
|
|
19128
20421
|
import os17 from "os";
|
|
19129
20422
|
import path29 from "path";
|
|
19130
20423
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
19131
|
-
import { execSync as
|
|
20424
|
+
import { execSync as execSync13 } from "child_process";
|
|
19132
20425
|
import chalk17 from "chalk";
|
|
19133
20426
|
var REPO_URL = "https://github.com/tu11aa/squadrant";
|
|
19134
20427
|
function readPkgVersion() {
|
|
19135
20428
|
try {
|
|
19136
20429
|
const pkgPath = path29.join(path29.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
|
|
19137
|
-
return JSON.parse(
|
|
20430
|
+
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
19138
20431
|
} catch {
|
|
19139
20432
|
return "unknown";
|
|
19140
20433
|
}
|
|
19141
20434
|
}
|
|
19142
20435
|
function readMetrics(metricsPath) {
|
|
19143
20436
|
try {
|
|
19144
|
-
return JSON.parse(
|
|
20437
|
+
return JSON.parse(fs25.readFileSync(metricsPath, "utf-8"));
|
|
19145
20438
|
} catch {
|
|
19146
20439
|
return {};
|
|
19147
20440
|
}
|
|
@@ -19187,7 +20480,7 @@ var feedbackCommand = new Command16("feedback").description("Open a pre-filled G
|
|
|
19187
20480
|
console.log(chalk17.dim(` URL: ${issueUrl.substring(0, 80)}...
|
|
19188
20481
|
`));
|
|
19189
20482
|
try {
|
|
19190
|
-
|
|
20483
|
+
execSync13(`open "${issueUrl}"`, { stdio: "ignore" });
|
|
19191
20484
|
console.log(chalk17.green(" \u2714 Browser opened\n"));
|
|
19192
20485
|
} catch {
|
|
19193
20486
|
console.log(chalk17.yellow(" \u26A0 Could not open browser automatically."));
|
|
@@ -19589,7 +20882,7 @@ init_dist4();
|
|
|
19589
20882
|
init_dist();
|
|
19590
20883
|
import { Command as Command21 } from "commander";
|
|
19591
20884
|
import chalk22 from "chalk";
|
|
19592
|
-
import
|
|
20885
|
+
import fs26 from "fs";
|
|
19593
20886
|
import path30 from "path";
|
|
19594
20887
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
19595
20888
|
function parseScope(v2) {
|
|
@@ -19601,7 +20894,7 @@ function parseScope(v2) {
|
|
|
19601
20894
|
function findPackageRoot3() {
|
|
19602
20895
|
let dir = path30.dirname(fileURLToPath4(import.meta.url));
|
|
19603
20896
|
while (dir !== "/" && dir !== "") {
|
|
19604
|
-
if (
|
|
20897
|
+
if (fs26.existsSync(path30.join(dir, "package.json"))) return dir;
|
|
19605
20898
|
dir = path30.dirname(dir);
|
|
19606
20899
|
}
|
|
19607
20900
|
return process.cwd();
|
|
@@ -19798,12 +21091,12 @@ init_dist();
|
|
|
19798
21091
|
init_dist();
|
|
19799
21092
|
init_dist2();
|
|
19800
21093
|
import { Command as Command23 } from "commander";
|
|
19801
|
-
import
|
|
21094
|
+
import fs27 from "fs";
|
|
19802
21095
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
19803
|
-
import { dirname as
|
|
21096
|
+
import { dirname as dirname8, join as join30 } from "path";
|
|
19804
21097
|
import chalk23 from "chalk";
|
|
19805
21098
|
function runConfigCheck(opts) {
|
|
19806
|
-
const raw = JSON.parse(
|
|
21099
|
+
const raw = JSON.parse(fs27.readFileSync(opts.configPath, "utf-8"));
|
|
19807
21100
|
const def = getDefaultConfig();
|
|
19808
21101
|
const items = detectDrift(raw, def);
|
|
19809
21102
|
let working = raw;
|
|
@@ -19891,7 +21184,7 @@ function printItems(items) {
|
|
|
19891
21184
|
var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
|
|
19892
21185
|
configCommand.command("check").description("Detect config drift vs the current default schema").option("--fix", "Apply the safe tier (add missing, remove deprecated)", false).option("--accept", "Stamp the current version without changing config (dismiss advisories)", false).option("--json", "Output drift items as JSON", false).action((opts) => {
|
|
19893
21186
|
const pkgVersion = readPkgVersion2();
|
|
19894
|
-
if (!
|
|
21187
|
+
if (!fs27.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
19895
21188
|
console.log(chalk23.yellow("No config found \u2014 run `squadrant init` first."));
|
|
19896
21189
|
return;
|
|
19897
21190
|
}
|
|
@@ -19936,14 +21229,14 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
19936
21229
|
}
|
|
19937
21230
|
});
|
|
19938
21231
|
function readPkgVersion2() {
|
|
19939
|
-
const pkgPath =
|
|
19940
|
-
return JSON.parse(
|
|
21232
|
+
const pkgPath = join30(dirname8(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
21233
|
+
return JSON.parse(fs27.readFileSync(pkgPath, "utf-8")).version;
|
|
19941
21234
|
}
|
|
19942
21235
|
|
|
19943
21236
|
// packages/cli/src/commands/heal.ts
|
|
19944
21237
|
init_dist();
|
|
19945
21238
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
19946
|
-
import { join as
|
|
21239
|
+
import { join as join31 } from "path";
|
|
19947
21240
|
import { homedir as homedir17 } from "os";
|
|
19948
21241
|
import { Command as Command24 } from "commander";
|
|
19949
21242
|
import chalk24 from "chalk";
|
|
@@ -20183,8 +21476,8 @@ var healCommand = new Command24("heal").description("Targeted, idempotent remedi
|
|
|
20183
21476
|
}
|
|
20184
21477
|
const config = loadConfig();
|
|
20185
21478
|
const projects = opts.all ? Object.keys(config.projects) : [project];
|
|
20186
|
-
const stateRoot =
|
|
20187
|
-
const registry = new LivenessRegistry({ path:
|
|
21479
|
+
const stateRoot = join31(homedir17(), ".config", "squadrant", "state");
|
|
21480
|
+
const registry = new LivenessRegistry({ path: join31(stateRoot, "liveness.json") });
|
|
20188
21481
|
registry.load();
|
|
20189
21482
|
const code = await runHealCaptain(projects, {
|
|
20190
21483
|
liveness: readCmuxLiveness,
|
|
@@ -20216,7 +21509,7 @@ import chalk26 from "chalk";
|
|
|
20216
21509
|
init_dist();
|
|
20217
21510
|
init_dist2();
|
|
20218
21511
|
import { Command as Command25 } from "commander";
|
|
20219
|
-
import { execSync as
|
|
21512
|
+
import { execSync as execSync14 } from "child_process";
|
|
20220
21513
|
import chalk25 from "chalk";
|
|
20221
21514
|
async function runDispatch(toProject, task, opts) {
|
|
20222
21515
|
const fromProject = resolveCurrentProject(loadConfig());
|
|
@@ -20232,7 +21525,7 @@ async function runDispatch(toProject, task, opts) {
|
|
|
20232
21525
|
warmupTimeoutMs: opts.warmupTimeout,
|
|
20233
21526
|
bootCaptain: async (project) => {
|
|
20234
21527
|
try {
|
|
20235
|
-
|
|
21528
|
+
execSync14(`squadrant launch ${project}`, { stdio: "ignore", timeout: 15e3 });
|
|
20236
21529
|
} catch {
|
|
20237
21530
|
throw new Error(`failed to launch captain for '${project}' \u2014 is squadrant installed?`);
|
|
20238
21531
|
}
|
|
@@ -20268,7 +21561,7 @@ init_dist();
|
|
|
20268
21561
|
init_dist2();
|
|
20269
21562
|
init_runtime2();
|
|
20270
21563
|
init_require_daemon();
|
|
20271
|
-
import { join as
|
|
21564
|
+
import { join as join33, dirname as dirname9 } from "path";
|
|
20272
21565
|
import { homedir as homedir18 } from "os";
|
|
20273
21566
|
import { Command as Command27 } from "commander";
|
|
20274
21567
|
import chalk28 from "chalk";
|
|
@@ -20277,23 +21570,23 @@ import chalk28 from "chalk";
|
|
|
20277
21570
|
init_dist3();
|
|
20278
21571
|
init_dist2();
|
|
20279
21572
|
import { createServer as createServer6, connect as netConnect2 } from "net";
|
|
20280
|
-
import
|
|
20281
|
-
import { join as
|
|
21573
|
+
import fs28 from "fs";
|
|
21574
|
+
import { join as join32 } from "path";
|
|
20282
21575
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
20283
21576
|
import chalk27 from "chalk";
|
|
20284
21577
|
var shared;
|
|
20285
|
-
var registryEntryPath = () =>
|
|
21578
|
+
var registryEntryPath = () => join32(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
20286
21579
|
function unregisterSenderIdentity() {
|
|
20287
21580
|
try {
|
|
20288
|
-
|
|
21581
|
+
fs28.unlinkSync(registryEntryPath());
|
|
20289
21582
|
} catch {
|
|
20290
21583
|
}
|
|
20291
21584
|
}
|
|
20292
21585
|
function registerSenderIdentity(socketPath2) {
|
|
20293
21586
|
try {
|
|
20294
21587
|
unregisterSenderIdentity();
|
|
20295
|
-
|
|
20296
|
-
|
|
21588
|
+
fs28.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
21589
|
+
fs28.writeFileSync(
|
|
20297
21590
|
registryEntryPath(),
|
|
20298
21591
|
JSON.stringify({
|
|
20299
21592
|
pid: process.pid,
|
|
@@ -20319,7 +21612,7 @@ async function sharedReceiptListener() {
|
|
|
20319
21612
|
// must never be the reason we refuse to start.
|
|
20320
21613
|
unlinkStale: (p) => {
|
|
20321
21614
|
try {
|
|
20322
|
-
|
|
21615
|
+
fs28.unlinkSync(p);
|
|
20323
21616
|
} catch {
|
|
20324
21617
|
}
|
|
20325
21618
|
},
|
|
@@ -20389,7 +21682,7 @@ async function runPing(project, message) {
|
|
|
20389
21682
|
await requireDaemon();
|
|
20390
21683
|
await needRef(resolved);
|
|
20391
21684
|
const mode = resolveCaptainChannelMode(config.defaults);
|
|
20392
|
-
const stateRoot =
|
|
21685
|
+
const stateRoot = join33(homedir18(), ".config", "squadrant", "state");
|
|
20393
21686
|
const { channels, agentFor } = mode === "off" ? { channels: {}, agentFor: () => void 0 } : await buildCaptainChannels({ stateRoot, configAgent: config.defaults.roles?.captain?.agent });
|
|
20394
21687
|
const channel = channels[agentFor(project) ?? ""];
|
|
20395
21688
|
const { handled, outcome } = await deliverToCaptain(project, message, {
|
|
@@ -20398,7 +21691,7 @@ async function runPing(project, message) {
|
|
|
20398
21691
|
log: (m) => console.error(chalk28.dim(m))
|
|
20399
21692
|
});
|
|
20400
21693
|
if (!handled) {
|
|
20401
|
-
const stateRoot2 =
|
|
21694
|
+
const stateRoot2 = join33(dirname9(DEFAULT_CONFIG_PATH), "state");
|
|
20402
21695
|
await appendCaptainMessage({
|
|
20403
21696
|
stateRoot: stateRoot2,
|
|
20404
21697
|
project,
|
|
@@ -20477,7 +21770,7 @@ var cmuxCommand = new Command28("cmux").description("cmux integration helpers").
|
|
|
20477
21770
|
// packages/cli/src/commands/effort.ts
|
|
20478
21771
|
init_dist();
|
|
20479
21772
|
init_dist2();
|
|
20480
|
-
import
|
|
21773
|
+
import fs29 from "fs";
|
|
20481
21774
|
import path31 from "path";
|
|
20482
21775
|
import { Command as Command29 } from "commander";
|
|
20483
21776
|
import chalk30 from "chalk";
|
|
@@ -20521,7 +21814,7 @@ function effortScopeLabel(projectName) {
|
|
|
20521
21814
|
}
|
|
20522
21815
|
function canonical(p) {
|
|
20523
21816
|
try {
|
|
20524
|
-
return
|
|
21817
|
+
return fs29.realpathSync(p);
|
|
20525
21818
|
} catch {
|
|
20526
21819
|
return path31.resolve(p);
|
|
20527
21820
|
}
|
|
@@ -20583,7 +21876,7 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
|
|
|
20583
21876
|
|
|
20584
21877
|
// packages/cli/src/commands/tokens.ts
|
|
20585
21878
|
init_dist();
|
|
20586
|
-
import
|
|
21879
|
+
import fs30 from "fs";
|
|
20587
21880
|
import path32 from "path";
|
|
20588
21881
|
import os18 from "os";
|
|
20589
21882
|
import readline3 from "readline";
|
|
@@ -20639,7 +21932,7 @@ function foldTranscriptLine(agg, rawLine, state) {
|
|
|
20639
21932
|
async function aggregateTranscriptFile(filePath) {
|
|
20640
21933
|
const agg = emptySessionAggregate();
|
|
20641
21934
|
const state = { lastCacheRead: null };
|
|
20642
|
-
const rl = readline3.createInterface({ input:
|
|
21935
|
+
const rl = readline3.createInterface({ input: fs30.createReadStream(filePath), crlfDelay: Infinity });
|
|
20643
21936
|
for await (const line of rl) {
|
|
20644
21937
|
foldTranscriptLine(agg, line, state);
|
|
20645
21938
|
}
|
|
@@ -20707,7 +22000,7 @@ function buildRoleReport(role, sessions) {
|
|
|
20707
22000
|
}
|
|
20708
22001
|
async function readdirSafe(dir) {
|
|
20709
22002
|
try {
|
|
20710
|
-
return await
|
|
22003
|
+
return await fs30.promises.readdir(dir);
|
|
20711
22004
|
} catch {
|
|
20712
22005
|
return [];
|
|
20713
22006
|
}
|
|
@@ -20874,12 +22167,12 @@ var tokensCommand = new Command30("tokens").description(
|
|
|
20874
22167
|
// packages/cli/src/commands/telegram.ts
|
|
20875
22168
|
init_dist();
|
|
20876
22169
|
init_dist2();
|
|
20877
|
-
import { join as
|
|
22170
|
+
import { join as join34, dirname as dirname10 } from "path";
|
|
20878
22171
|
import { emitKeypressEvents } from "readline";
|
|
20879
22172
|
import { Command as Command31 } from "commander";
|
|
20880
22173
|
import chalk32 from "chalk";
|
|
20881
22174
|
function defaultStateRoot() {
|
|
20882
|
-
return
|
|
22175
|
+
return join34(dirname10(DEFAULT_CONFIG_PATH), "state");
|
|
20883
22176
|
}
|
|
20884
22177
|
async function questionMasked() {
|
|
20885
22178
|
return new Promise((resolve4) => {
|
|
@@ -21208,7 +22501,7 @@ import { Command as Command33 } from "commander";
|
|
|
21208
22501
|
import { homedir as homedir19 } from "os";
|
|
21209
22502
|
|
|
21210
22503
|
// packages/cli/src/lib/captain-session-registry.ts
|
|
21211
|
-
import
|
|
22504
|
+
import fs31 from "fs";
|
|
21212
22505
|
import path33 from "path";
|
|
21213
22506
|
|
|
21214
22507
|
// packages/cli/src/lib/handoff-facts.ts
|
|
@@ -21260,15 +22553,15 @@ function assembleHandoffFacts(live, claudeMem, gapSessions, checkpoint, now, ext
|
|
|
21260
22553
|
// packages/cli/src/lib/captain-session-registry.ts
|
|
21261
22554
|
var CAPTAIN_SESSION_REGISTRY_FILE = "captain-sessions.jsonl";
|
|
21262
22555
|
function appendCaptainSession(spokeVault, record) {
|
|
21263
|
-
|
|
22556
|
+
fs31.mkdirSync(spokeVault, { recursive: true });
|
|
21264
22557
|
const file = path33.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
|
|
21265
|
-
|
|
22558
|
+
fs31.appendFileSync(file, JSON.stringify(record) + "\n");
|
|
21266
22559
|
}
|
|
21267
22560
|
function readCaptainSessionRegistry(spokeVault) {
|
|
21268
22561
|
const file = path33.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
|
|
21269
|
-
if (!
|
|
22562
|
+
if (!fs31.existsSync(file)) return [];
|
|
21270
22563
|
const records = [];
|
|
21271
|
-
for (const line of
|
|
22564
|
+
for (const line of fs31.readFileSync(file, "utf-8").split("\n")) {
|
|
21272
22565
|
if (!line.trim()) continue;
|
|
21273
22566
|
try {
|
|
21274
22567
|
records.push(JSON.parse(line));
|
|
@@ -21293,6 +22586,30 @@ init_dist();
|
|
|
21293
22586
|
init_dist3();
|
|
21294
22587
|
import { Command as Command32 } from "commander";
|
|
21295
22588
|
var SOCK4 = DAEMON_SOCK_PATH;
|
|
22589
|
+
async function runAutoGateClaudeHook(o) {
|
|
22590
|
+
const make = o.deps.createAutoGate ?? createSquadrantAutoGate;
|
|
22591
|
+
const autoGate = make({
|
|
22592
|
+
config: o.config,
|
|
22593
|
+
env: o.env,
|
|
22594
|
+
...o.project ? { project: o.project } : {},
|
|
22595
|
+
...o.deps.sendEvent ? { sendBlocked: o.deps.sendEvent } : {},
|
|
22596
|
+
log: o.log
|
|
22597
|
+
});
|
|
22598
|
+
const out = await autoGate.decideClaudeHookPayload(o.deps.rawPayload ?? "");
|
|
22599
|
+
if (out === void 0) {
|
|
22600
|
+
o.log("gate auto-gate: ask/yield \u2014 normal dialog");
|
|
22601
|
+
return { decision: "ask", tier: 2, reason: "auto-gate: ask/yield" };
|
|
22602
|
+
}
|
|
22603
|
+
o.stdout(out);
|
|
22604
|
+
let decision = "allow";
|
|
22605
|
+
try {
|
|
22606
|
+
const parsed = JSON.parse(out);
|
|
22607
|
+
if (parsed?.hookSpecificOutput?.decision?.behavior === "deny") decision = "deny";
|
|
22608
|
+
} catch {
|
|
22609
|
+
}
|
|
22610
|
+
o.log(`gate auto-gate ${decision}`);
|
|
22611
|
+
return { decision, tier: 2, reason: "auto-gate" };
|
|
22612
|
+
}
|
|
21296
22613
|
async function runGatePermissionRequest(deps) {
|
|
21297
22614
|
const env = deps.env ?? process.env;
|
|
21298
22615
|
const payload = deps.payload ?? {};
|
|
@@ -21307,6 +22624,10 @@ async function runGatePermissionRequest(deps) {
|
|
|
21307
22624
|
const log = deps.log ?? ((m) => process.stderr.write(`[squadrant] ${m}
|
|
21308
22625
|
`));
|
|
21309
22626
|
const config = deps.config ?? (deps.loadConfigFn ?? loadConfig)();
|
|
22627
|
+
const gate = config.defaults.gate;
|
|
22628
|
+
if (resolveGateMode(env, gate) === "on" && resolveGateEngine(env, gate) === "auto-gate") {
|
|
22629
|
+
return runAutoGateClaudeHook({ deps, env, config, project, stdout, log });
|
|
22630
|
+
}
|
|
21310
22631
|
const cwd = deps.cwd ?? (typeof p.cwd === "string" && p.cwd ? p.cwd : env.SQUADRANT_CREW_CWD ?? process.cwd());
|
|
21311
22632
|
const transcriptPath = typeof p.transcript_path === "string" && p.transcript_path ? p.transcript_path : deriveTranscriptPath(
|
|
21312
22633
|
typeof p.session_id === "string" ? p.session_id : "",
|
|
@@ -21370,6 +22691,7 @@ function gateCommand() {
|
|
|
21370
22691
|
}
|
|
21371
22692
|
await runGatePermissionRequest({
|
|
21372
22693
|
payload,
|
|
22694
|
+
rawPayload: stdin,
|
|
21373
22695
|
env: process.env,
|
|
21374
22696
|
stdout: (s) => process.stdout.write(s),
|
|
21375
22697
|
sendEvent: async (project, ev) => {
|
|
@@ -21452,6 +22774,7 @@ function hooksCommand() {
|
|
|
21452
22774
|
if (sub === "permission-request") {
|
|
21453
22775
|
await runGatePermissionRequest({
|
|
21454
22776
|
payload,
|
|
22777
|
+
rawPayload: stdin,
|
|
21455
22778
|
env: process.env,
|
|
21456
22779
|
stdout: (s) => process.stdout.write(s),
|
|
21457
22780
|
sendEvent: async (project2, ev2) => {
|
|
@@ -21645,7 +22968,7 @@ import os19 from "os";
|
|
|
21645
22968
|
// packages/cli/src/lib/handoff-live-repo.ts
|
|
21646
22969
|
init_dist();
|
|
21647
22970
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
21648
|
-
import
|
|
22971
|
+
import fs32 from "fs";
|
|
21649
22972
|
import path35 from "path";
|
|
21650
22973
|
|
|
21651
22974
|
// packages/cli/src/lib/handoff-branch-state.ts
|
|
@@ -21785,7 +23108,7 @@ function localAheadOfBase(runner, projectPath, base) {
|
|
|
21785
23108
|
}
|
|
21786
23109
|
function readFetchAgeMs(projectPath, now) {
|
|
21787
23110
|
try {
|
|
21788
|
-
const stat2 =
|
|
23111
|
+
const stat2 = fs32.statSync(path35.join(projectPath, ".git", "FETCH_HEAD"));
|
|
21789
23112
|
return Math.max(0, now - stat2.mtime.getTime());
|
|
21790
23113
|
} catch {
|
|
21791
23114
|
return null;
|
|
@@ -21856,7 +23179,7 @@ function gatherLiveRepoState(projectPath, fallbackBaseBranch, tasks, runner = de
|
|
|
21856
23179
|
|
|
21857
23180
|
// packages/cli/src/lib/handoff-claude-mem.ts
|
|
21858
23181
|
import { createRequire } from "module";
|
|
21859
|
-
import
|
|
23182
|
+
import fs33 from "fs";
|
|
21860
23183
|
var { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
|
|
21861
23184
|
var CLAUDE_MEM_RECENCY_LIMIT = 20;
|
|
21862
23185
|
function decisionText(row) {
|
|
@@ -21870,7 +23193,7 @@ function decisionText(row) {
|
|
|
21870
23193
|
return row.narrative ?? "";
|
|
21871
23194
|
}
|
|
21872
23195
|
function queryClaudeMem(dbPath, project) {
|
|
21873
|
-
if (!
|
|
23196
|
+
if (!fs33.existsSync(dbPath)) return null;
|
|
21874
23197
|
let db;
|
|
21875
23198
|
try {
|
|
21876
23199
|
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
@@ -21913,7 +23236,7 @@ function queryClaudeMem(dbPath, project) {
|
|
|
21913
23236
|
}
|
|
21914
23237
|
|
|
21915
23238
|
// packages/cli/src/lib/handoff-transcript.ts
|
|
21916
|
-
import
|
|
23239
|
+
import fs34 from "fs";
|
|
21917
23240
|
var TRANSCRIPT_BYTE_CAP = 2e5;
|
|
21918
23241
|
var DIGEST_BYTE_CAP = 8 * 1024;
|
|
21919
23242
|
var PROMPT_TRUNCATE_CHARS = 300;
|
|
@@ -22022,27 +23345,27 @@ function extractMessages(tailText) {
|
|
|
22022
23345
|
return { lastUserMessage, lastAssistantText, digest };
|
|
22023
23346
|
}
|
|
22024
23347
|
function extractTranscriptTail(transcriptPath, byteCap = TRANSCRIPT_BYTE_CAP) {
|
|
22025
|
-
if (!
|
|
22026
|
-
const content =
|
|
23348
|
+
if (!fs34.existsSync(transcriptPath)) return null;
|
|
23349
|
+
const content = fs34.readFileSync(transcriptPath, "utf-8");
|
|
22027
23350
|
const { lastUserMessage, lastAssistantText, digest } = extractMessages(tailOf(content, byteCap));
|
|
22028
|
-
const mtimeIso =
|
|
23351
|
+
const mtimeIso = fs34.statSync(transcriptPath).mtime.toISOString();
|
|
22029
23352
|
return { path: transcriptPath, mtimeIso, lastUserMessage, lastAssistantText, digest };
|
|
22030
23353
|
}
|
|
22031
23354
|
|
|
22032
23355
|
// packages/cli/src/lib/handoff-archive.ts
|
|
22033
|
-
import
|
|
23356
|
+
import fs35 from "fs";
|
|
22034
23357
|
import path36 from "path";
|
|
22035
23358
|
function readNewestArchivedHandoff(spokeVault, now) {
|
|
22036
23359
|
const dir = path36.join(spokeVault, "handoffs");
|
|
22037
|
-
if (!
|
|
22038
|
-
const candidates =
|
|
23360
|
+
if (!fs35.existsSync(dir)) return null;
|
|
23361
|
+
const candidates = fs35.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
|
|
22039
23362
|
const full = path36.join(dir, e.name);
|
|
22040
|
-
return { name: e.name, full, mtime:
|
|
23363
|
+
return { name: e.name, full, mtime: fs35.statSync(full).mtime };
|
|
22041
23364
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
22042
23365
|
for (const candidate of candidates) {
|
|
22043
23366
|
let content;
|
|
22044
23367
|
try {
|
|
22045
|
-
content = JSON.parse(
|
|
23368
|
+
content = JSON.parse(fs35.readFileSync(candidate.full, "utf-8"));
|
|
22046
23369
|
} catch {
|
|
22047
23370
|
continue;
|
|
22048
23371
|
}
|
|
@@ -22385,15 +23708,15 @@ init_dist();
|
|
|
22385
23708
|
init_dist();
|
|
22386
23709
|
init_dist();
|
|
22387
23710
|
init_dist();
|
|
22388
|
-
var __dirname =
|
|
22389
|
-
var pkg = JSON.parse(readFileSync21(
|
|
23711
|
+
var __dirname = dirname11(fileURLToPath6(import.meta.url));
|
|
23712
|
+
var pkg = JSON.parse(readFileSync21(join35(__dirname, "..", "package.json"), "utf-8"));
|
|
22390
23713
|
ensureRuntimeSynced({
|
|
22391
|
-
sourceRoot:
|
|
22392
|
-
runtimeRoot:
|
|
23714
|
+
sourceRoot: join35(__dirname, ".."),
|
|
23715
|
+
runtimeRoot: join35(homedir20(), ".config", "squadrant")
|
|
22393
23716
|
});
|
|
22394
23717
|
if (!process.env.VITEST) {
|
|
22395
23718
|
try {
|
|
22396
|
-
syncShippedOpencodeSkills({ pkgRoot:
|
|
23719
|
+
syncShippedOpencodeSkills({ pkgRoot: join35(__dirname, "..") });
|
|
22397
23720
|
} catch (err) {
|
|
22398
23721
|
process.stderr.write(
|
|
22399
23722
|
`squadrant: opencode skills sync skipped: ${err.message}
|
|
@@ -22403,7 +23726,7 @@ if (!process.env.VITEST) {
|
|
|
22403
23726
|
}
|
|
22404
23727
|
if (process.argv[2] !== "config") {
|
|
22405
23728
|
try {
|
|
22406
|
-
const cfgPath =
|
|
23729
|
+
const cfgPath = join35(homedir20(), ".config", "squadrant", "config.json");
|
|
22407
23730
|
if (existsSync14(cfgPath)) {
|
|
22408
23731
|
const cfg = JSON.parse(readConfigFileSync(cfgPath));
|
|
22409
23732
|
if (needsCheck(cfg, pkg.version)) {
|