switchroom 0.21.1 → 0.21.4
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/bin/handoff-briefing.sh +23 -1
- package/dist/auth-broker/index.js +54 -34
- package/dist/cli/switchroom.js +840 -379
- package/dist/host-control/main.js +8 -1
- package/package.json +3 -2
- package/telegram-plugin/dist/gateway/gateway.js +378 -111
- package/telegram-plugin/external-spend.ts +1 -1
- package/telegram-plugin/gateway/gateway.ts +3 -1
- package/telegram-plugin/gateway/orphaned-db-sweep.ts +315 -0
- package/telegram-plugin/history.ts +328 -62
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +19 -4
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +8 -2
- package/telegram-plugin/tests/orphaned-db-sweep.test.ts +713 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.21.
|
|
2123
|
+
var VERSION = "0.21.4", COMMIT_SHA = "aa63e74c";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -63125,6 +63125,17 @@ function rollbackHint(installDir, previousVersion) {
|
|
|
63125
63125
|
function alreadySelfUpdated(env2) {
|
|
63126
63126
|
return env2[SELF_UPDATE_ENV_SENTINEL] === "1";
|
|
63127
63127
|
}
|
|
63128
|
+
function describeBinaryProbeFailure(opts) {
|
|
63129
|
+
const { probe: probe2, path: path7, subject } = opts;
|
|
63130
|
+
switch (probe2.kind) {
|
|
63131
|
+
case "not-executable":
|
|
63132
|
+
return `could not EXECUTE ${subject} at ${path7} (${probe2.detail}). This is a ` + `property of WHERE it was staged, not of the artifact \u2014 the usual causes ` + `are a staging directory mounted \`noexec\`, a lost execute bit, or an ` + `architecture this kernel cannot run. The download's sha256 already ` + `matched the release's checksums file, so re-downloading will not help.`;
|
|
63133
|
+
case "ran-but-failed":
|
|
63134
|
+
return `${subject} at ${path7} ran but exited non-zero for \`--version\` ` + `(${probe2.detail}) \u2014 the artifact itself is faulty.`;
|
|
63135
|
+
case "no-version":
|
|
63136
|
+
return `${subject} at ${path7} ran and exited 0 but printed no parseable version ` + `(${probe2.detail}) \u2014 the artifact itself is faulty.`;
|
|
63137
|
+
}
|
|
63138
|
+
}
|
|
63128
63139
|
async function fetchLatestReleaseTag(io) {
|
|
63129
63140
|
try {
|
|
63130
63141
|
return parseLatestReleaseTag(await io.httpGetText(GITHUB_LATEST_RELEASE_URL));
|
|
@@ -63278,10 +63289,14 @@ async function performSelfUpdate(opts) {
|
|
|
63278
63289
|
throw new Error(`self-update: SHA256 mismatch for ${assetName} (expected ${expected}, got ${actual}) \u2014 ` + `refusing to install. The installed CLI is unchanged.`);
|
|
63279
63290
|
}
|
|
63280
63291
|
io.chmodExec(tmp);
|
|
63281
|
-
const proved = io.
|
|
63282
|
-
if (!proved) {
|
|
63292
|
+
const proved = io.probeBinary(tmp);
|
|
63293
|
+
if (!proved.ok) {
|
|
63283
63294
|
io.remove(tmp);
|
|
63284
|
-
throw new Error(`self-update:
|
|
63295
|
+
throw new Error(`self-update: ${describeBinaryProbeFailure({
|
|
63296
|
+
probe: proved,
|
|
63297
|
+
path: tmp,
|
|
63298
|
+
subject: `the downloaded ${plan.to} binary`
|
|
63299
|
+
})} Refusing to install it. The installed CLI is unchanged.`);
|
|
63285
63300
|
}
|
|
63286
63301
|
const payload = await installAssetPayload({
|
|
63287
63302
|
tag: plan.to,
|
|
@@ -63459,10 +63474,13 @@ function serializeStamp(stamp) {
|
|
|
63459
63474
|
`;
|
|
63460
63475
|
}
|
|
63461
63476
|
function refreshHostCliStamp(input, io = {}) {
|
|
63477
|
+
const stamp = buildHostCliStamp(input, io);
|
|
63478
|
+
if (!stamp)
|
|
63479
|
+
return { status: "skipped", reason: "running in a container" };
|
|
63480
|
+
return writeHostCliStamp(stamp, io);
|
|
63481
|
+
}
|
|
63482
|
+
function writeHostCliStamp(stamp, io = {}) {
|
|
63462
63483
|
try {
|
|
63463
|
-
const stamp = buildHostCliStamp(input, io);
|
|
63464
|
-
if (!stamp)
|
|
63465
|
-
return { status: "skipped", reason: "running in a container" };
|
|
63466
63484
|
const dir = resolveStampDir(io);
|
|
63467
63485
|
if (!dir)
|
|
63468
63486
|
return { status: "skipped", reason: "no switchroom home found" };
|
|
@@ -64917,6 +64935,7 @@ async function defaultLoadDecisions() {
|
|
|
64917
64935
|
"exec",
|
|
64918
64936
|
"switchroom-vault-broker",
|
|
64919
64937
|
"sqlite3",
|
|
64938
|
+
"-readonly",
|
|
64920
64939
|
"-separator",
|
|
64921
64940
|
SEP2,
|
|
64922
64941
|
GRANTS_DB_CONTAINER_PATH,
|
|
@@ -84374,7 +84393,8 @@ function detectGatewayFindings(agent, logText, logName = `logs/${agent}/gateway-
|
|
|
84374
84393
|
const gw_hits = {
|
|
84375
84394
|
"duplicate-delivery-represent": 0,
|
|
84376
84395
|
"represent-escalation": 0,
|
|
84377
|
-
"reply-delivery-failure": 0
|
|
84396
|
+
"reply-delivery-failure": 0,
|
|
84397
|
+
"orphaned-db-handle": 0
|
|
84378
84398
|
};
|
|
84379
84399
|
const lines = logText.split(`
|
|
84380
84400
|
`);
|
|
@@ -84432,7 +84452,8 @@ var init_detect = __esm(() => {
|
|
|
84432
84452
|
GATEWAY_SIGNATURES = {
|
|
84433
84453
|
"duplicate-delivery-represent": /represent duplicate-send/,
|
|
84434
84454
|
"represent-escalation": /obligation escalation/,
|
|
84435
|
-
"reply-delivery-failure": /tg-post method=sendRichMessage[^\n]*status=err(?![a-z])
|
|
84455
|
+
"reply-delivery-failure": /tg-post method=sendRichMessage[^\n]*status=err(?![a-z])/,
|
|
84456
|
+
"orphaned-db-handle": /orphaned-db-sweep DETECTED \d+ deleted-inode DB handle/
|
|
84436
84457
|
};
|
|
84437
84458
|
});
|
|
84438
84459
|
|
|
@@ -84496,6 +84517,12 @@ var init_mapping = __esm(() => {
|
|
|
84496
84517
|
job_spec: "talk-to-agents-from-anywhere",
|
|
84497
84518
|
signature: "reply-delivery-failure:sendRichMessage-err"
|
|
84498
84519
|
},
|
|
84520
|
+
"orphaned-db-handle": {
|
|
84521
|
+
failure_mode: "success-theater",
|
|
84522
|
+
severity: 3,
|
|
84523
|
+
job_spec: "survive-reboots-and-real-life",
|
|
84524
|
+
signature: "orphaned-db-handle:deleted-inode-writes"
|
|
84525
|
+
},
|
|
84499
84526
|
"hang-long-stalled": {
|
|
84500
84527
|
failure_mode: "partial",
|
|
84501
84528
|
severity: 2,
|
|
@@ -84740,10 +84767,10 @@ var init_test_agents = __esm(() => {
|
|
|
84740
84767
|
});
|
|
84741
84768
|
|
|
84742
84769
|
// src/litellm/header-passthrough-guard.ts
|
|
84743
|
-
import { existsSync as existsSync124, readdirSync as
|
|
84770
|
+
import { existsSync as existsSync124, readdirSync as readdirSync48 } from "node:fs";
|
|
84744
84771
|
function discoverLiveLitellmConfigPath(opts) {
|
|
84745
84772
|
const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
|
|
84746
|
-
const readdir2 = opts?.readdirFn ?? ((p) =>
|
|
84773
|
+
const readdir2 = opts?.readdirFn ?? ((p) => readdirSync48(p));
|
|
84747
84774
|
const exists = opts?.existsFn ?? existsSync124;
|
|
84748
84775
|
let entries;
|
|
84749
84776
|
try {
|
|
@@ -84964,7 +84991,7 @@ var init_passthrough_mount_guard = __esm(() => {
|
|
|
84964
84991
|
// src/fleet-health/litellm-config-sensor.ts
|
|
84965
84992
|
import { execFileSync as execFileSync35 } from "node:child_process";
|
|
84966
84993
|
import { readFileSync as readFileSync111, existsSync as existsSync125 } from "node:fs";
|
|
84967
|
-
import { dirname as
|
|
84994
|
+
import { dirname as dirname52, join as join127 } from "node:path";
|
|
84968
84995
|
function resolveLiveLitellmPythonVersion() {
|
|
84969
84996
|
try {
|
|
84970
84997
|
const ps = execFileSync35("docker", ["ps", "--format", "{{.Names}}\t{{.Image}}"], {
|
|
@@ -85048,7 +85075,7 @@ function scanLitellmConfig(opts = {}) {
|
|
|
85048
85075
|
ts: nowIso
|
|
85049
85076
|
});
|
|
85050
85077
|
}
|
|
85051
|
-
const composePath =
|
|
85078
|
+
const composePath = join127(dirname52(path10), LIVE_COMPOSE_BASENAME);
|
|
85052
85079
|
let composeText = null;
|
|
85053
85080
|
if (exists(composePath)) {
|
|
85054
85081
|
try {
|
|
@@ -85152,12 +85179,12 @@ __export(exports_scan, {
|
|
|
85152
85179
|
});
|
|
85153
85180
|
import {
|
|
85154
85181
|
readFileSync as readFileSync112,
|
|
85155
|
-
readdirSync as
|
|
85182
|
+
readdirSync as readdirSync49,
|
|
85156
85183
|
existsSync as existsSync126,
|
|
85157
85184
|
mkdirSync as mkdirSync71,
|
|
85158
85185
|
writeFileSync as writeFileSync53
|
|
85159
85186
|
} from "node:fs";
|
|
85160
|
-
import { resolve as resolve64, dirname as
|
|
85187
|
+
import { resolve as resolve64, dirname as dirname53 } from "node:path";
|
|
85161
85188
|
import { homedir as homedir68 } from "node:os";
|
|
85162
85189
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir68()) {
|
|
85163
85190
|
return resolve64(home2, ".switchroom");
|
|
@@ -85167,7 +85194,7 @@ function listAgents(base) {
|
|
|
85167
85194
|
if (!existsSync126(dir))
|
|
85168
85195
|
return [];
|
|
85169
85196
|
try {
|
|
85170
|
-
return
|
|
85197
|
+
return readdirSync49(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
85171
85198
|
} catch {
|
|
85172
85199
|
return [];
|
|
85173
85200
|
}
|
|
@@ -85254,11 +85281,11 @@ function readLedgerIfPresent(base) {
|
|
|
85254
85281
|
}
|
|
85255
85282
|
}
|
|
85256
85283
|
function ledgerPathForBase(base) {
|
|
85257
|
-
return fleetHealthLedgerPath(
|
|
85284
|
+
return fleetHealthLedgerPath(dirname53(base));
|
|
85258
85285
|
}
|
|
85259
85286
|
function writeLedger(base, ledger) {
|
|
85260
85287
|
const path10 = ledgerPathForBase(base);
|
|
85261
|
-
mkdirSync71(
|
|
85288
|
+
mkdirSync71(dirname53(path10), { recursive: true });
|
|
85262
85289
|
writeFileSync53(path10, JSON.stringify(ledger, null, 2) + `
|
|
85263
85290
|
`, "utf-8");
|
|
85264
85291
|
return path10;
|
|
@@ -86285,6 +86312,7 @@ function readLastMessages(historyDbPath) {
|
|
|
86285
86312
|
} catch {}
|
|
86286
86313
|
try {
|
|
86287
86314
|
const out = execFileSync11("sqlite3", [
|
|
86315
|
+
"-readonly",
|
|
86288
86316
|
historyDbPath,
|
|
86289
86317
|
"SELECT role, MAX(ts) FROM messages GROUP BY role;"
|
|
86290
86318
|
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -92341,7 +92369,7 @@ function scrubSqlite(path2, values, dryRun) {
|
|
|
92341
92369
|
for (const { value } of values) {
|
|
92342
92370
|
const esc = value.replace(/'/g, "''");
|
|
92343
92371
|
try {
|
|
92344
|
-
const out = execFileSync17("sqlite3", [path2, `SELECT COUNT(*) FROM messages WHERE text LIKE '%${esc}%'`], { encoding: "utf8", timeout: 1e4 });
|
|
92372
|
+
const out = execFileSync17("sqlite3", ["-readonly", path2, `SELECT COUNT(*) FROM messages WHERE text LIKE '%${esc}%'`], { encoding: "utf8", timeout: 1e4 });
|
|
92345
92373
|
const n = parseInt(out.trim(), 10);
|
|
92346
92374
|
if (!Number.isNaN(n))
|
|
92347
92375
|
matches += n;
|
|
@@ -106606,7 +106634,7 @@ function readTopicsFromHistory(dbPath, chatId, limit) {
|
|
|
106606
106634
|
fail2("`switchroom telegram topics` requires the Bun runtime (history DB uses bun:sqlite). " + "Run via `bun run dev` or via the installed `switchroom` CLI (bun-bundled).");
|
|
106607
106635
|
}
|
|
106608
106636
|
const { Database: Database2 } = metaRequire("bun:sqlite");
|
|
106609
|
-
const db = new Database2(dbPath, { create: false });
|
|
106637
|
+
const db = new Database2(dbPath, { create: false, readonly: true });
|
|
106610
106638
|
try {
|
|
106611
106639
|
const aggregateRows = db.prepare(`
|
|
106612
106640
|
SELECT
|
|
@@ -115303,6 +115331,11 @@ import { Readable } from "node:stream";
|
|
|
115303
115331
|
import { pipeline } from "node:stream/promises";
|
|
115304
115332
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
115305
115333
|
var HTTP_TIMEOUT_MS = 60000;
|
|
115334
|
+
function firstLine2(s) {
|
|
115335
|
+
const line = `${s ?? ""}`.split(`
|
|
115336
|
+
`).map((l) => l.trim()).find((l) => l.length > 0) ?? "";
|
|
115337
|
+
return line.length > 200 ? `${line.slice(0, 197)}...` : line;
|
|
115338
|
+
}
|
|
115306
115339
|
function defaultSelfUpdateIO() {
|
|
115307
115340
|
return {
|
|
115308
115341
|
async httpGetText(url) {
|
|
@@ -115342,17 +115375,40 @@ function defaultSelfUpdateIO() {
|
|
|
115342
115375
|
}
|
|
115343
115376
|
return hash2.digest("hex");
|
|
115344
115377
|
},
|
|
115345
|
-
|
|
115346
|
-
const r = spawnSync18(path7, ["version"], {
|
|
115378
|
+
probeBinary(path7) {
|
|
115379
|
+
const r = spawnSync18(path7, ["--version"], {
|
|
115347
115380
|
encoding: "utf-8",
|
|
115348
115381
|
timeout: 30000,
|
|
115349
115382
|
env: { ...process.env, SWITCHROOM_SELF_UPDATED: "" }
|
|
115350
115383
|
});
|
|
115351
|
-
if (r.
|
|
115352
|
-
|
|
115384
|
+
if (r.error) {
|
|
115385
|
+
const code = r.error.code;
|
|
115386
|
+
return {
|
|
115387
|
+
ok: false,
|
|
115388
|
+
kind: "not-executable",
|
|
115389
|
+
detail: code ? `${code}: ${r.error.message}` : r.error.message
|
|
115390
|
+
};
|
|
115391
|
+
}
|
|
115392
|
+
if (r.signal) {
|
|
115393
|
+
return { ok: false, kind: "not-executable", detail: `killed by signal ${r.signal}` };
|
|
115394
|
+
}
|
|
115395
|
+
if (r.status !== 0) {
|
|
115396
|
+
return {
|
|
115397
|
+
ok: false,
|
|
115398
|
+
kind: "ran-but-failed",
|
|
115399
|
+
detail: `exit ${r.status}${firstLine2(r.stderr) ? `: ${firstLine2(r.stderr)}` : ""}`
|
|
115400
|
+
};
|
|
115401
|
+
}
|
|
115353
115402
|
const out = `${r.stdout ?? ""}`.trim();
|
|
115354
115403
|
const m = out.match(/\d+\.\d+\.\d+/);
|
|
115355
|
-
|
|
115404
|
+
if (!m) {
|
|
115405
|
+
return {
|
|
115406
|
+
ok: false,
|
|
115407
|
+
kind: "no-version",
|
|
115408
|
+
detail: out ? `printed ${JSON.stringify(firstLine2(out))}` : "printed nothing"
|
|
115409
|
+
};
|
|
115410
|
+
}
|
|
115411
|
+
return { ok: true, version: m[0] };
|
|
115356
115412
|
},
|
|
115357
115413
|
mkdirp(dir) {
|
|
115358
115414
|
mkdirSync49(dir, { recursive: true });
|
|
@@ -116419,7 +116475,7 @@ function registerUpdateCommand(program3) {
|
|
|
116419
116475
|
// src/cli/rollout.ts
|
|
116420
116476
|
init_helpers();
|
|
116421
116477
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
116422
|
-
import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as
|
|
116478
|
+
import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as statSync54 } from "node:fs";
|
|
116423
116479
|
import { homedir as homedir51 } from "node:os";
|
|
116424
116480
|
|
|
116425
116481
|
// src/cli/rollout-pin-journal.ts
|
|
@@ -116633,6 +116689,363 @@ function recoverPinJournal(configPath, opts = {}) {
|
|
|
116633
116689
|
// src/cli/rollout.ts
|
|
116634
116690
|
init_operator_uid();
|
|
116635
116691
|
init_host_cli_stamp();
|
|
116692
|
+
|
|
116693
|
+
// src/cli/host-cli-upgrade.ts
|
|
116694
|
+
init_self_update();
|
|
116695
|
+
import { lchownSync, lstatSync as lstatSync14, readdirSync as readdirSync32, statSync as statSync53 } from "node:fs";
|
|
116696
|
+
import { basename as basename10, dirname as dirname42, join as join96 } from "node:path";
|
|
116697
|
+
init_shipped_assets();
|
|
116698
|
+
var HOST_CLI_UPGRADE_SENTINEL = "SWITCHROOM_HOST_CLI_UPGRADE:";
|
|
116699
|
+
function encodeHostCliUpgradeResult(r) {
|
|
116700
|
+
return `${HOST_CLI_UPGRADE_SENTINEL}${JSON.stringify(r)}`;
|
|
116701
|
+
}
|
|
116702
|
+
function parseHostCliUpgradeResult(logs) {
|
|
116703
|
+
const lines = logs.split(`
|
|
116704
|
+
`);
|
|
116705
|
+
for (let i2 = lines.length - 1;i2 >= 0; i2--) {
|
|
116706
|
+
const line = lines[i2]?.trim() ?? "";
|
|
116707
|
+
if (!line.startsWith(HOST_CLI_UPGRADE_SENTINEL))
|
|
116708
|
+
continue;
|
|
116709
|
+
try {
|
|
116710
|
+
const o = JSON.parse(line.slice(HOST_CLI_UPGRADE_SENTINEL.length));
|
|
116711
|
+
if (typeof o !== "object" || o === null)
|
|
116712
|
+
return null;
|
|
116713
|
+
if (typeof o.ok !== "boolean")
|
|
116714
|
+
return null;
|
|
116715
|
+
return {
|
|
116716
|
+
ok: o.ok,
|
|
116717
|
+
...typeof o.version === "string" ? { version: o.version } : {},
|
|
116718
|
+
...typeof o.binaryPath === "string" ? { binaryPath: o.binaryPath } : {},
|
|
116719
|
+
...typeof o.error === "string" ? { error: o.error } : {}
|
|
116720
|
+
};
|
|
116721
|
+
} catch {
|
|
116722
|
+
return null;
|
|
116723
|
+
}
|
|
116724
|
+
}
|
|
116725
|
+
return null;
|
|
116726
|
+
}
|
|
116727
|
+
var SEMVER_TAG_RE2 = /^v\d+\.\d+\.\d+$/;
|
|
116728
|
+
function defaultIo() {
|
|
116729
|
+
return {
|
|
116730
|
+
isFile: (p) => {
|
|
116731
|
+
try {
|
|
116732
|
+
return statSync53(p).isFile();
|
|
116733
|
+
} catch {
|
|
116734
|
+
return false;
|
|
116735
|
+
}
|
|
116736
|
+
},
|
|
116737
|
+
kind: (p) => {
|
|
116738
|
+
try {
|
|
116739
|
+
const st = lstatSync14(p);
|
|
116740
|
+
if (st.isSymbolicLink())
|
|
116741
|
+
return "symlink";
|
|
116742
|
+
if (st.isDirectory())
|
|
116743
|
+
return "dir";
|
|
116744
|
+
if (st.isFile())
|
|
116745
|
+
return "file";
|
|
116746
|
+
return "other";
|
|
116747
|
+
} catch {
|
|
116748
|
+
return;
|
|
116749
|
+
}
|
|
116750
|
+
},
|
|
116751
|
+
owner: (p) => {
|
|
116752
|
+
try {
|
|
116753
|
+
const st = lstatSync14(p);
|
|
116754
|
+
return { uid: st.uid, gid: st.gid };
|
|
116755
|
+
} catch {
|
|
116756
|
+
return;
|
|
116757
|
+
}
|
|
116758
|
+
},
|
|
116759
|
+
chown: (p, uid, gid) => lchownSync(p, uid, gid),
|
|
116760
|
+
list: (d) => {
|
|
116761
|
+
try {
|
|
116762
|
+
return readdirSync32(d);
|
|
116763
|
+
} catch {
|
|
116764
|
+
return [];
|
|
116765
|
+
}
|
|
116766
|
+
},
|
|
116767
|
+
selfUpdate: defaultSelfUpdateIO(),
|
|
116768
|
+
platform: process.platform,
|
|
116769
|
+
arch: process.arch,
|
|
116770
|
+
getuid: () => process.getuid?.()
|
|
116771
|
+
};
|
|
116772
|
+
}
|
|
116773
|
+
function handBackOwnership(paths, owner, io) {
|
|
116774
|
+
const failures = [];
|
|
116775
|
+
const seen = new Set;
|
|
116776
|
+
const stack = [...paths];
|
|
116777
|
+
while (stack.length > 0) {
|
|
116778
|
+
const p = stack.pop();
|
|
116779
|
+
if (p === undefined || seen.has(p))
|
|
116780
|
+
continue;
|
|
116781
|
+
seen.add(p);
|
|
116782
|
+
const kind = io.kind(p);
|
|
116783
|
+
if (kind === undefined)
|
|
116784
|
+
continue;
|
|
116785
|
+
try {
|
|
116786
|
+
io.chown(p, owner.uid, owner.gid);
|
|
116787
|
+
} catch (err) {
|
|
116788
|
+
failures.push(`${p}: ${err.message}`);
|
|
116789
|
+
}
|
|
116790
|
+
if (kind !== "dir")
|
|
116791
|
+
continue;
|
|
116792
|
+
for (const child of io.list(p))
|
|
116793
|
+
stack.push(join96(p, child));
|
|
116794
|
+
}
|
|
116795
|
+
return failures;
|
|
116796
|
+
}
|
|
116797
|
+
async function runHostCliUpgrade(opts, io = defaultIo(), log = () => {}) {
|
|
116798
|
+
const { binary, pin, from } = opts;
|
|
116799
|
+
if (!SEMVER_TAG_RE2.test(pin)) {
|
|
116800
|
+
return { ok: false, error: `--pin must be a vX.Y.Z release tag, got "${pin}"` };
|
|
116801
|
+
}
|
|
116802
|
+
if (!binary.startsWith("/")) {
|
|
116803
|
+
return { ok: false, error: `--binary must be an absolute path, got "${binary}"` };
|
|
116804
|
+
}
|
|
116805
|
+
if (basename10(binary) !== "switchroom") {
|
|
116806
|
+
return {
|
|
116807
|
+
ok: false,
|
|
116808
|
+
error: `--binary must name a file called \`switchroom\`, got ` + `"${basename10(binary)}" \u2014 refusing to overwrite an unrelated file.`
|
|
116809
|
+
};
|
|
116810
|
+
}
|
|
116811
|
+
if (!io.isFile(binary)) {
|
|
116812
|
+
return {
|
|
116813
|
+
ok: false,
|
|
116814
|
+
error: `${binary} is not an existing regular file \u2014 the host bindir is either ` + `not mounted or the recorded host CLI path is stale.`
|
|
116815
|
+
};
|
|
116816
|
+
}
|
|
116817
|
+
const asset = releaseAssetName(io.platform, io.arch);
|
|
116818
|
+
if (!asset) {
|
|
116819
|
+
return {
|
|
116820
|
+
ok: false,
|
|
116821
|
+
error: `no published binary for ${io.platform}/${io.arch}`
|
|
116822
|
+
};
|
|
116823
|
+
}
|
|
116824
|
+
const priorOwner = io.owner(binary);
|
|
116825
|
+
const handBack = () => {
|
|
116826
|
+
try {
|
|
116827
|
+
if (!priorOwner || priorOwner.uid === io.getuid())
|
|
116828
|
+
return;
|
|
116829
|
+
const installRoot = payloadInstallRoot(binary);
|
|
116830
|
+
const failures = handBackOwnership([
|
|
116831
|
+
binary,
|
|
116832
|
+
join96(dirname42(binary), ".switchroom-versions"),
|
|
116833
|
+
dirname42(installRoot),
|
|
116834
|
+
installRoot,
|
|
116835
|
+
payloadVersionDir(installRoot, pin)
|
|
116836
|
+
], priorOwner, io);
|
|
116837
|
+
if (failures.length > 0) {
|
|
116838
|
+
log(`warning: could not hand ownership back to uid ${priorOwner.uid}: ` + `${failures.join("; ")}
|
|
116839
|
+
`);
|
|
116840
|
+
}
|
|
116841
|
+
} catch (err) {
|
|
116842
|
+
log(`warning: ownership handoff failed: ${err.message}
|
|
116843
|
+
`);
|
|
116844
|
+
}
|
|
116845
|
+
};
|
|
116846
|
+
try {
|
|
116847
|
+
let result;
|
|
116848
|
+
try {
|
|
116849
|
+
result = await performSelfUpdate({
|
|
116850
|
+
plan: {
|
|
116851
|
+
action: "update",
|
|
116852
|
+
from: `v${from.replace(/^v/, "")}`,
|
|
116853
|
+
to: pin,
|
|
116854
|
+
binaryPath: binary
|
|
116855
|
+
},
|
|
116856
|
+
assetName: asset,
|
|
116857
|
+
io: io.selfUpdate,
|
|
116858
|
+
log
|
|
116859
|
+
});
|
|
116860
|
+
} catch (err) {
|
|
116861
|
+
return { ok: false, error: err.message };
|
|
116862
|
+
}
|
|
116863
|
+
const proven = io.selfUpdate.probeBinary(binary);
|
|
116864
|
+
if (!proven.ok) {
|
|
116865
|
+
return {
|
|
116866
|
+
ok: false,
|
|
116867
|
+
error: `swapped ${binary} but ${describeBinaryProbeFailure({
|
|
116868
|
+
probe: proven,
|
|
116869
|
+
path: binary,
|
|
116870
|
+
subject: "the installed binary"
|
|
116871
|
+
})} ${result.message}`
|
|
116872
|
+
};
|
|
116873
|
+
}
|
|
116874
|
+
if (`v${proven.version.replace(/^v/, "")}` !== pin) {
|
|
116875
|
+
return {
|
|
116876
|
+
ok: false,
|
|
116877
|
+
error: `swapped ${binary} but it reports ${proven.version}, not ${pin} \u2014 ` + `the install did not land. ${result.message}`
|
|
116878
|
+
};
|
|
116879
|
+
}
|
|
116880
|
+
return { ok: true, version: pin, binaryPath: binary };
|
|
116881
|
+
} finally {
|
|
116882
|
+
handBack();
|
|
116883
|
+
}
|
|
116884
|
+
}
|
|
116885
|
+
function registerHostCliUpgradeCommand(program3) {
|
|
116886
|
+
program3.command("host-cli-upgrade").description("INTERNAL (#4585): replace a bind-mounted HOST switchroom binary with a " + "published release, checksum-verified. Spawned by rollout inside a " + "short-lived helper container; not for interactive use.").requiredOption("--binary <path>", "Path of the host binary to replace").requiredOption("--pin <version>", "Target release tag, vX.Y.Z").requiredOption("--from <version>", "Version currently installed").action(async (opts) => {
|
|
116887
|
+
const result = await runHostCliUpgrade(opts, defaultIo(), (s) => process.stderr.write(s.endsWith(`
|
|
116888
|
+
`) ? s : `${s}
|
|
116889
|
+
`));
|
|
116890
|
+
process.stdout.write(`${encodeHostCliUpgradeResult(result)}
|
|
116891
|
+
`);
|
|
116892
|
+
if (!result.ok) {
|
|
116893
|
+
process.stderr.write(`host-cli-upgrade failed: ${result.error}
|
|
116894
|
+
`);
|
|
116895
|
+
process.exitCode = 1;
|
|
116896
|
+
}
|
|
116897
|
+
});
|
|
116898
|
+
}
|
|
116899
|
+
|
|
116900
|
+
// src/cli/host-cli-heal.ts
|
|
116901
|
+
var HEAL_PREFIX_MOUNT = "/hostcli";
|
|
116902
|
+
var HEAL_HELPER_CONTAINER = "switchroom-hostcli-heal";
|
|
116903
|
+
var HOSTD_CONTAINER_NAME = "switchroom-hostd";
|
|
116904
|
+
var HOST_CLI_HEAL_TIMEOUT_MS = (() => {
|
|
116905
|
+
const raw = process.env.SWITCHROOM_HOST_CLI_HEAL_TIMEOUT_MS;
|
|
116906
|
+
const n = raw ? Number(raw) : NaN;
|
|
116907
|
+
return Number.isFinite(n) && n > 0 ? n : 10 * 60 * 1000;
|
|
116908
|
+
})();
|
|
116909
|
+
function planHostCliHeal(opts) {
|
|
116910
|
+
const { stamp, target, hostdCtx } = opts;
|
|
116911
|
+
if (!hostdCtx) {
|
|
116912
|
+
return {
|
|
116913
|
+
action: "skip",
|
|
116914
|
+
reason: "not running in hostd context \u2014 on the host shell the operator can run " + "the install command directly"
|
|
116915
|
+
};
|
|
116916
|
+
}
|
|
116917
|
+
if (!stamp)
|
|
116918
|
+
return { action: "skip", reason: "no host-cli.json stamp to act on" };
|
|
116919
|
+
if (!/^v\d+\.\d+\.\d+$/.test(target)) {
|
|
116920
|
+
return { action: "skip", reason: `target "${target}" is not a vX.Y.Z release tag` };
|
|
116921
|
+
}
|
|
116922
|
+
if (stamp.installKind !== "static-binary") {
|
|
116923
|
+
return {
|
|
116924
|
+
action: "skip",
|
|
116925
|
+
reason: `the host CLI is a ${stamp.installKind} install \u2014 only a static-binary ` + `install can be replaced by swapping one file, so an operator must run ` + `the upgrade host-side`
|
|
116926
|
+
};
|
|
116927
|
+
}
|
|
116928
|
+
const path7 = stamp.path;
|
|
116929
|
+
if (!path7.startsWith("/") || path7.includes("/..")) {
|
|
116930
|
+
return { action: "skip", reason: `recorded host CLI path "${path7}" is not a plain absolute path` };
|
|
116931
|
+
}
|
|
116932
|
+
const installDir = path7.slice(0, path7.lastIndexOf("/"));
|
|
116933
|
+
const name = path7.slice(path7.lastIndexOf("/") + 1);
|
|
116934
|
+
if (name !== "switchroom") {
|
|
116935
|
+
return { action: "skip", reason: `recorded host CLI path "${path7}" is not named \`switchroom\`` };
|
|
116936
|
+
}
|
|
116937
|
+
const prefixHostPath = installDir.slice(0, installDir.lastIndexOf("/"));
|
|
116938
|
+
const dirName = installDir.slice(installDir.lastIndexOf("/") + 1);
|
|
116939
|
+
if (prefixHostPath.length === 0 || dirName.length === 0) {
|
|
116940
|
+
return {
|
|
116941
|
+
action: "skip",
|
|
116942
|
+
reason: `recorded host CLI path "${path7}" has no install prefix to bind \u2014 ` + `refusing to mount the host root`
|
|
116943
|
+
};
|
|
116944
|
+
}
|
|
116945
|
+
return {
|
|
116946
|
+
action: "heal",
|
|
116947
|
+
prefixHostPath,
|
|
116948
|
+
containerBinaryPath: `${HEAL_PREFIX_MOUNT}/${dirName}/switchroom`,
|
|
116949
|
+
from: stamp.version,
|
|
116950
|
+
target
|
|
116951
|
+
};
|
|
116952
|
+
}
|
|
116953
|
+
function healHelperArgs(opts) {
|
|
116954
|
+
const { plan, helperImage } = opts;
|
|
116955
|
+
return [
|
|
116956
|
+
"run",
|
|
116957
|
+
"-d",
|
|
116958
|
+
"--name",
|
|
116959
|
+
opts.containerName ?? HEAL_HELPER_CONTAINER,
|
|
116960
|
+
"--label",
|
|
116961
|
+
"switchroom.hostcli-heal=true",
|
|
116962
|
+
"-v",
|
|
116963
|
+
`${plan.prefixHostPath}:${HEAL_PREFIX_MOUNT}:rw`,
|
|
116964
|
+
helperImage,
|
|
116965
|
+
"switchroom",
|
|
116966
|
+
"host-cli-upgrade",
|
|
116967
|
+
"--binary",
|
|
116968
|
+
plan.containerBinaryPath,
|
|
116969
|
+
"--pin",
|
|
116970
|
+
plan.target,
|
|
116971
|
+
"--from",
|
|
116972
|
+
plan.from
|
|
116973
|
+
];
|
|
116974
|
+
}
|
|
116975
|
+
function resolveHelperImage(docker) {
|
|
116976
|
+
const r = docker(["inspect", "--format", "{{.Config.Image}}", HOSTD_CONTAINER_NAME]);
|
|
116977
|
+
if (!r.ok)
|
|
116978
|
+
return null;
|
|
116979
|
+
const ref = r.stdout.trim().split(`
|
|
116980
|
+
`)[0]?.trim() ?? "";
|
|
116981
|
+
return ref.length > 0 ? ref : null;
|
|
116982
|
+
}
|
|
116983
|
+
function runHostCliHeal(opts) {
|
|
116984
|
+
const { plan, docker } = opts;
|
|
116985
|
+
const name = opts.containerName ?? HEAL_HELPER_CONTAINER;
|
|
116986
|
+
const log = opts.log ?? (() => {});
|
|
116987
|
+
const image = resolveHelperImage(docker);
|
|
116988
|
+
if (!image) {
|
|
116989
|
+
return {
|
|
116990
|
+
ok: false,
|
|
116991
|
+
message: `could not resolve the image of the running \`${HOSTD_CONTAINER_NAME}\` ` + `container, so there is no image to run the upgrade helper from`
|
|
116992
|
+
};
|
|
116993
|
+
}
|
|
116994
|
+
docker(["rm", "-f", name]);
|
|
116995
|
+
log(`\u21bb host CLI ${plan.from} \u2192 ${plan.target}: running the upgrade helper ` + `(${image}, binding ${plan.prefixHostPath})
|
|
116996
|
+
`);
|
|
116997
|
+
const started = docker(healHelperArgs({ plan, helperImage: image, containerName: name }));
|
|
116998
|
+
if (!started.ok) {
|
|
116999
|
+
return {
|
|
117000
|
+
ok: false,
|
|
117001
|
+
message: `could not start the upgrade helper: ${oneLine(started.stderr) || "docker run failed"}`
|
|
117002
|
+
};
|
|
117003
|
+
}
|
|
117004
|
+
try {
|
|
117005
|
+
return waitForHelper(docker, name, plan);
|
|
117006
|
+
} catch (err) {
|
|
117007
|
+
return { ok: false, message: `the upgrade helper could not be waited on: ${err.message}` };
|
|
117008
|
+
} finally {
|
|
117009
|
+
try {
|
|
117010
|
+
docker(["rm", "-f", name]);
|
|
117011
|
+
} catch {}
|
|
117012
|
+
}
|
|
117013
|
+
}
|
|
117014
|
+
function waitForHelper(docker, name, plan) {
|
|
117015
|
+
const waited = docker(["wait", name]);
|
|
117016
|
+
const logs = docker(["logs", name]);
|
|
117017
|
+
const blob = `${logs.stdout}
|
|
117018
|
+
${logs.stderr}`;
|
|
117019
|
+
const parsed = parseHostCliUpgradeResult(blob);
|
|
117020
|
+
if (!waited.ok) {
|
|
117021
|
+
return {
|
|
117022
|
+
ok: false,
|
|
117023
|
+
message: `waiting on the upgrade helper failed (${oneLine(waited.stderr) || "docker wait failed"})` + (parsed?.error ? `; helper reported: ${parsed.error}` : "")
|
|
117024
|
+
};
|
|
117025
|
+
}
|
|
117026
|
+
const code = waited.stdout.trim().split(`
|
|
117027
|
+
`).pop()?.trim() ?? "";
|
|
117028
|
+
if (code !== "0" || !parsed || !parsed.ok) {
|
|
117029
|
+
return {
|
|
117030
|
+
ok: false,
|
|
117031
|
+
message: `the upgrade helper exited ${code || "?"}: ` + (parsed?.error ?? (oneLine(blob) || "no diagnostic on stdout/stderr"))
|
|
117032
|
+
};
|
|
117033
|
+
}
|
|
117034
|
+
if (!parsed.version) {
|
|
117035
|
+
return { ok: false, message: "the upgrade helper reported success without a version" };
|
|
117036
|
+
}
|
|
117037
|
+
return {
|
|
117038
|
+
ok: true,
|
|
117039
|
+
version: parsed.version,
|
|
117040
|
+
message: `host CLI upgraded ${plan.from} \u2192 ${parsed.version} at ${plan.containerBinaryPath.replace(HEAL_PREFIX_MOUNT, plan.prefixHostPath)}`
|
|
117041
|
+
};
|
|
117042
|
+
}
|
|
117043
|
+
function oneLine(s) {
|
|
117044
|
+
const flat = s.replace(/\s+/g, " ").trim();
|
|
117045
|
+
return flat.length > 400 ? `${flat.slice(0, 397)}...` : flat;
|
|
117046
|
+
}
|
|
117047
|
+
|
|
117048
|
+
// src/cli/rollout.ts
|
|
116636
117049
|
init_self_invoke();
|
|
116637
117050
|
init_atomic();
|
|
116638
117051
|
|
|
@@ -117281,7 +117694,7 @@ function createRolloutDeps(params) {
|
|
|
117281
117694
|
if (after === before)
|
|
117282
117695
|
return false;
|
|
117283
117696
|
beginPinPersist(configPath, pin);
|
|
117284
|
-
writeConfigPreservingOwnership(configPath, after,
|
|
117697
|
+
writeConfigPreservingOwnership(configPath, after, statSync54(configPath).mode & 511);
|
|
117285
117698
|
return true;
|
|
117286
117699
|
},
|
|
117287
117700
|
commitPin: () => {
|
|
@@ -117309,8 +117722,21 @@ function createRolloutDeps(params) {
|
|
|
117309
117722
|
} : {}
|
|
117310
117723
|
};
|
|
117311
117724
|
}
|
|
117312
|
-
function registerRolloutCommand(program3) {
|
|
117725
|
+
function registerRolloutCommand(program3, testHooks) {
|
|
117313
117726
|
const hostdCtx = isHostdContext2();
|
|
117727
|
+
const hostCliHealDocker = testHooks?.hostCliHealDocker;
|
|
117728
|
+
const defaultHostCliHealDocker = (args) => {
|
|
117729
|
+
try {
|
|
117730
|
+
const r = spawnSync22("docker", args, {
|
|
117731
|
+
encoding: "utf8",
|
|
117732
|
+
timeout: HOST_CLI_HEAL_TIMEOUT_MS,
|
|
117733
|
+
killSignal: ROLLOUT_KILL_SIGNAL
|
|
117734
|
+
});
|
|
117735
|
+
return { ok: r.status === 0, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
117736
|
+
} catch (err) {
|
|
117737
|
+
return { ok: false, stdout: "", stderr: err.message };
|
|
117738
|
+
}
|
|
117739
|
+
};
|
|
117314
117740
|
program3.command("rollout").description("Deploy a pinned version to the fleet, safely (staggered restart + " + "per-agent version assert + web/hostd refresh)." + (hostdCtx ? "" : " Run with sudo.")).option("--pin <version>", "Version to roll (e.g. v0.15.18). Defaults to release.pin from config.").option("--agents <list>", "Comma-separated subset of agents to roll (default: all configured).").option("--skip-web", "Skip the web refresh (host-shell path: also the hostd + hindsight " + "refresh). On the hostd/agent path only the web refresh is skipped \u2014 " + "hindsight is still recreated and hostd stays deferred regardless.").option("--allow-downgrade", "Permit rolling to an older semver tag (operator-approved rollback path). " + "When set, the downgrade guard is relaxed so --pin may be older than the " + "current release.pin. All other safety rails apply unchanged.").option("--allow-stale-host-cli", "Roll the fleet even though the HOST operator CLI is observably behind " + "the target. Escape hatch for the host-CLI-first gate: the host CLI is " + "then left drifted and every later host-shell command runs old code. " + "HOST-SHELL ONLY: deliberately not exposed as an `mcp__hostd__rollout` " + "parameter, so an agent cannot roll past the gate on its own \u2014 " + "overriding requires the same host shell needed to actually fix it.").option("--dry-run", "Print the plan and exit without changing anything.").action(async (opts) => {
|
|
117315
117741
|
if (!opts.dryRun) {
|
|
117316
117742
|
const note = recoverPinJournal(getConfigPath(program3));
|
|
@@ -117360,29 +117786,6 @@ function registerRolloutCommand(program3) {
|
|
|
117360
117786
|
process.exitCode = 2;
|
|
117361
117787
|
return;
|
|
117362
117788
|
}
|
|
117363
|
-
const hostCliStamp = readHostCliStamp();
|
|
117364
|
-
if (!opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117365
|
-
const observed = hostCliStamp?.version ?? "unknown";
|
|
117366
|
-
const refusal = `rollout refused: the HOST operator CLI is v${observed}, OLDER than ` + `the target ${target}. The host CLI must be upgraded FIRST \u2014 it is ` + `what runs \`switchroom apply\`, \`vault\`, \`doctor\` and the next ` + `roll host-side, and leaving it behind is how it silently drifted ` + `five releases. Run this on the host, then re-run the roll: ` + `${hostCliInstallCommand(hostCliStamp, target)}. ` + `(Observed from ${HOST_CLI_STAMP_FILENAME}, refreshed by every ` + `host-context switchroom command \u2014 if you already upgraded, run any ` + `switchroom command on the host to refresh it, or pass ` + `--allow-stale-host-cli to roll anyway.) Nothing was changed.`;
|
|
117367
|
-
process.stderr.write(refusal + `
|
|
117368
|
-
`);
|
|
117369
|
-
if (hostdCtx) {
|
|
117370
|
-
process.stdout.write(encodeRolloutResultLine({
|
|
117371
|
-
ok: false,
|
|
117372
|
-
rolled: [],
|
|
117373
|
-
failedStep: PREFLIGHT_HOST_CLI_STALE_STEP,
|
|
117374
|
-
got: observed,
|
|
117375
|
-
warnings: [refusal]
|
|
117376
|
-
}) + `
|
|
117377
|
-
`);
|
|
117378
|
-
}
|
|
117379
|
-
process.exitCode = 2;
|
|
117380
|
-
return;
|
|
117381
|
-
}
|
|
117382
|
-
if (opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117383
|
-
process.stderr.write(`\u26a0\ufe0f --allow-stale-host-cli: rolling past a HOST operator CLI on ` + `v${hostCliStamp?.version} (target ${target}). Fix it host-side ` + `with: ${hostCliInstallCommand(hostCliStamp, target)}
|
|
117384
|
-
`);
|
|
117385
|
-
}
|
|
117386
117789
|
if (shouldRefuseDowngrade(hostdCtx, resolvedPin, config.release?.pin, opts.allowDowngrade)) {
|
|
117387
117790
|
const current = config.release?.pin;
|
|
117388
117791
|
process.stderr.write(`rollout (hostd path) refuses a DOWNGRADE: ${resolvedPin} is older ` + `than the current pin ${current}. Pass --allow-downgrade to ` + `authorize an operator-approved rollback to a known-good earlier ` + `tag. Nothing was changed.
|
|
@@ -117410,6 +117813,63 @@ function registerRolloutCommand(program3) {
|
|
|
117410
117813
|
process.exitCode = 2;
|
|
117411
117814
|
return;
|
|
117412
117815
|
}
|
|
117816
|
+
let hostCliStamp = readHostCliStamp();
|
|
117817
|
+
let healNote;
|
|
117818
|
+
let healDeferredByDryRun = false;
|
|
117819
|
+
if (!opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117820
|
+
const plan = planHostCliHeal({ stamp: hostCliStamp, target, hostdCtx });
|
|
117821
|
+
if (plan.action === "skip") {
|
|
117822
|
+
healNote = `host CLI self-heal not attempted: ${plan.reason}.`;
|
|
117823
|
+
} else if (opts.dryRun) {
|
|
117824
|
+
healDeferredByDryRun = true;
|
|
117825
|
+
healNote = `would upgrade the HOST CLI ${plan.from} \u2192 ${plan.target} first, in a ` + `short-lived helper container binding ${plan.prefixHostPath}`;
|
|
117826
|
+
process.stdout.write(`\u21bb dry-run: ${healNote}
|
|
117827
|
+
`);
|
|
117828
|
+
} else {
|
|
117829
|
+
const outcome = runHostCliHeal({
|
|
117830
|
+
plan,
|
|
117831
|
+
docker: hostCliHealDocker ?? defaultHostCliHealDocker,
|
|
117832
|
+
log: (line) => process.stderr.write(line)
|
|
117833
|
+
});
|
|
117834
|
+
if (outcome.ok && outcome.version) {
|
|
117835
|
+
const updated = {
|
|
117836
|
+
...hostCliStamp,
|
|
117837
|
+
version: outcome.version.replace(/^v/, "")
|
|
117838
|
+
};
|
|
117839
|
+
const w = writeHostCliStamp(updated, testHooks?.hostCliStampIo ?? {});
|
|
117840
|
+
hostCliStamp = updated;
|
|
117841
|
+
healNote = `${outcome.message}` + (w.status === "skipped" ? ` (proceeding on the PROVEN version; ${HOST_CLI_STAMP_FILENAME} ` + `could not be refreshed: ${w.reason} \u2014 the next roll will ` + `re-observe the old version and heal again)` : "");
|
|
117842
|
+
process.stderr.write(`\u2705 ${healNote}
|
|
117843
|
+
`);
|
|
117844
|
+
} else {
|
|
117845
|
+
healNote = `host CLI self-heal FAILED: ${outcome.message}.`;
|
|
117846
|
+
process.stderr.write(`\u26a0\ufe0f ${healNote}
|
|
117847
|
+
`);
|
|
117848
|
+
}
|
|
117849
|
+
}
|
|
117850
|
+
}
|
|
117851
|
+
if (!opts.allowStaleHostCli && !healDeferredByDryRun && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117852
|
+
const observed = hostCliStamp?.version ?? "unknown";
|
|
117853
|
+
const refusal = `rollout refused: the HOST operator CLI is v${observed}, OLDER than ` + `the target ${target}. The host CLI must be upgraded FIRST \u2014 it is ` + `what runs \`switchroom apply\`, \`vault\`, \`doctor\` and the next ` + `roll host-side, and leaving it behind is how it silently drifted ` + `five releases. ` + (healNote ? `${healNote} ` : "") + `An operator must run this on the host, then re-run the roll: ` + `${hostCliInstallCommand(hostCliStamp, target)}. ` + `(Observed from ${HOST_CLI_STAMP_FILENAME}, refreshed by every ` + `host-context switchroom command \u2014 if you already upgraded, run any ` + `switchroom command on the host to refresh it, or pass ` + `--allow-stale-host-cli to roll anyway.) Nothing was changed.`;
|
|
117854
|
+
process.stderr.write(refusal + `
|
|
117855
|
+
`);
|
|
117856
|
+
if (hostdCtx) {
|
|
117857
|
+
process.stdout.write(encodeRolloutResultLine({
|
|
117858
|
+
ok: false,
|
|
117859
|
+
rolled: [],
|
|
117860
|
+
failedStep: PREFLIGHT_HOST_CLI_STALE_STEP,
|
|
117861
|
+
got: observed,
|
|
117862
|
+
warnings: [refusal]
|
|
117863
|
+
}) + `
|
|
117864
|
+
`);
|
|
117865
|
+
}
|
|
117866
|
+
process.exitCode = 2;
|
|
117867
|
+
return;
|
|
117868
|
+
}
|
|
117869
|
+
if (opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117870
|
+
process.stderr.write(`\u26a0\ufe0f --allow-stale-host-cli: rolling past a HOST operator CLI on ` + `v${hostCliStamp?.version} (target ${target}). Fix it host-side ` + `with: ${hostCliInstallCommand(hostCliStamp, target)}
|
|
117871
|
+
`);
|
|
117872
|
+
}
|
|
117413
117873
|
const steps = planRollout(requested, {
|
|
117414
117874
|
skipWeb: opts.skipWeb,
|
|
117415
117875
|
pinToPersist: resolvedPin ?? undefined,
|
|
@@ -117429,7 +117889,7 @@ function registerRolloutCommand(program3) {
|
|
|
117429
117889
|
hindsightApiUrl = rawUrl ? rawUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
117430
117890
|
resolveBankId = (agent) => config.agents?.[agent]?.memory?.collection ?? agent;
|
|
117431
117891
|
}
|
|
117432
|
-
const deps = createRolloutDeps({
|
|
117892
|
+
const deps = testHooks?.rolloutDeps ?? createRolloutDeps({
|
|
117433
117893
|
configPath,
|
|
117434
117894
|
scriptPath,
|
|
117435
117895
|
hostdCtx,
|
|
@@ -117519,7 +117979,7 @@ init_lifecycle();
|
|
|
117519
117979
|
init_resolve_version();
|
|
117520
117980
|
import { execSync as execSync3 } from "node:child_process";
|
|
117521
117981
|
import { existsSync as existsSync93, readFileSync as readFileSync86 } from "node:fs";
|
|
117522
|
-
import { dirname as
|
|
117982
|
+
import { dirname as dirname43, join as join97 } from "node:path";
|
|
117523
117983
|
function getClaudeCodeVersion() {
|
|
117524
117984
|
try {
|
|
117525
117985
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -117569,16 +118029,16 @@ function formatUptime3(timestamp) {
|
|
|
117569
118029
|
function locateSwitchroomInstallDir() {
|
|
117570
118030
|
let dir = import.meta.dirname;
|
|
117571
118031
|
for (let i2 = 0;i2 < 10 && dir && dir !== "/"; i2++) {
|
|
117572
|
-
const pkgPath =
|
|
118032
|
+
const pkgPath = join97(dir, "package.json");
|
|
117573
118033
|
if (existsSync93(pkgPath)) {
|
|
117574
118034
|
try {
|
|
117575
118035
|
const pkg = JSON.parse(readFileSync86(pkgPath, "utf-8"));
|
|
117576
|
-
if (pkg.name === "switchroom" && existsSync93(
|
|
118036
|
+
if (pkg.name === "switchroom" && existsSync93(join97(dir, ".git"))) {
|
|
117577
118037
|
return dir;
|
|
117578
118038
|
}
|
|
117579
118039
|
} catch {}
|
|
117580
118040
|
}
|
|
117581
|
-
dir =
|
|
118041
|
+
dir = dirname43(dir);
|
|
117582
118042
|
}
|
|
117583
118043
|
return null;
|
|
117584
118044
|
}
|
|
@@ -117755,31 +118215,31 @@ init_merge();
|
|
|
117755
118215
|
// src/agents/session-retention.ts
|
|
117756
118216
|
import {
|
|
117757
118217
|
existsSync as existsSync94,
|
|
117758
|
-
readdirSync as
|
|
117759
|
-
statSync as
|
|
118218
|
+
readdirSync as readdirSync33,
|
|
118219
|
+
statSync as statSync55,
|
|
117760
118220
|
unlinkSync as unlinkSync18
|
|
117761
118221
|
} from "node:fs";
|
|
117762
|
-
import { join as
|
|
118222
|
+
import { join as join98 } from "node:path";
|
|
117763
118223
|
var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
|
|
117764
118224
|
var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
117765
118225
|
var MIN_KEEP = 2;
|
|
117766
118226
|
function collectSessionJsonl(claudeConfigDir) {
|
|
117767
|
-
const projects =
|
|
118227
|
+
const projects = join98(claudeConfigDir, "projects");
|
|
117768
118228
|
if (!existsSync94(projects))
|
|
117769
118229
|
return [];
|
|
117770
118230
|
const found = [];
|
|
117771
118231
|
const walk2 = (dir) => {
|
|
117772
118232
|
let entries;
|
|
117773
118233
|
try {
|
|
117774
|
-
entries =
|
|
118234
|
+
entries = readdirSync33(dir);
|
|
117775
118235
|
} catch {
|
|
117776
118236
|
return;
|
|
117777
118237
|
}
|
|
117778
118238
|
for (const name of entries) {
|
|
117779
|
-
const full =
|
|
118239
|
+
const full = join98(dir, name);
|
|
117780
118240
|
let st;
|
|
117781
118241
|
try {
|
|
117782
|
-
st =
|
|
118242
|
+
st = statSync55(full);
|
|
117783
118243
|
} catch {
|
|
117784
118244
|
continue;
|
|
117785
118245
|
}
|
|
@@ -117900,15 +118360,15 @@ import {
|
|
|
117900
118360
|
existsSync as existsSync95,
|
|
117901
118361
|
mkdirSync as mkdirSync53,
|
|
117902
118362
|
openSync as openSync17,
|
|
117903
|
-
readdirSync as
|
|
118363
|
+
readdirSync as readdirSync34,
|
|
117904
118364
|
readFileSync as readFileSync87,
|
|
117905
118365
|
renameSync as renameSync25,
|
|
117906
|
-
statSync as
|
|
118366
|
+
statSync as statSync56,
|
|
117907
118367
|
unlinkSync as unlinkSync19,
|
|
117908
118368
|
writeFileSync as writeFileSync38,
|
|
117909
118369
|
writeSync as writeSync9
|
|
117910
118370
|
} from "node:fs";
|
|
117911
|
-
import { join as
|
|
118371
|
+
import { join as join99 } from "node:path";
|
|
117912
118372
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
117913
118373
|
import { execSync as execSync4 } from "node:child_process";
|
|
117914
118374
|
|
|
@@ -117935,7 +118395,7 @@ function computeFingerprint(source, code) {
|
|
|
117935
118395
|
var ISSUES_FILE = "issues.jsonl";
|
|
117936
118396
|
var ISSUES_LOCK = "issues.lock";
|
|
117937
118397
|
function readAll(stateDir) {
|
|
117938
|
-
const path7 =
|
|
118398
|
+
const path7 = join99(stateDir, ISSUES_FILE);
|
|
117939
118399
|
if (!existsSync95(path7))
|
|
117940
118400
|
return [];
|
|
117941
118401
|
let raw;
|
|
@@ -118013,7 +118473,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
118013
118473
|
});
|
|
118014
118474
|
}
|
|
118015
118475
|
function resolve51(stateDir, fingerprint, nowFn = Date.now) {
|
|
118016
|
-
if (!existsSync95(
|
|
118476
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118017
118477
|
return 0;
|
|
118018
118478
|
return withLock(stateDir, () => {
|
|
118019
118479
|
const all = readAll(stateDir);
|
|
@@ -118031,7 +118491,7 @@ function resolve51(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
118031
118491
|
});
|
|
118032
118492
|
}
|
|
118033
118493
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
118034
|
-
if (!existsSync95(
|
|
118494
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118035
118495
|
return 0;
|
|
118036
118496
|
return withLock(stateDir, () => {
|
|
118037
118497
|
const all = readAll(stateDir);
|
|
@@ -118049,7 +118509,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
118049
118509
|
});
|
|
118050
118510
|
}
|
|
118051
118511
|
function prune(stateDir, opts = {}) {
|
|
118052
|
-
if (!existsSync95(
|
|
118512
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118053
118513
|
return 0;
|
|
118054
118514
|
return withLock(stateDir, () => {
|
|
118055
118515
|
const all = readAll(stateDir);
|
|
@@ -118082,7 +118542,7 @@ function ensureDir(stateDir) {
|
|
|
118082
118542
|
mkdirSync53(stateDir, { recursive: true });
|
|
118083
118543
|
}
|
|
118084
118544
|
function writeAll(stateDir, events) {
|
|
118085
|
-
const path7 =
|
|
118545
|
+
const path7 = join99(stateDir, ISSUES_FILE);
|
|
118086
118546
|
sweepOrphanTmpFiles(stateDir);
|
|
118087
118547
|
const tmp = `${path7}.tmp-${process.pid}-${randomBytes15(4).toString("hex")}`;
|
|
118088
118548
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -118096,7 +118556,7 @@ var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
|
118096
118556
|
function sweepOrphanTmpFiles(stateDir) {
|
|
118097
118557
|
let entries;
|
|
118098
118558
|
try {
|
|
118099
|
-
entries =
|
|
118559
|
+
entries = readdirSync34(stateDir);
|
|
118100
118560
|
} catch {
|
|
118101
118561
|
return;
|
|
118102
118562
|
}
|
|
@@ -118104,9 +118564,9 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
118104
118564
|
for (const entry of entries) {
|
|
118105
118565
|
if (!entry.startsWith(TMP_PREFIX))
|
|
118106
118566
|
continue;
|
|
118107
|
-
const tmpPath =
|
|
118567
|
+
const tmpPath = join99(stateDir, entry);
|
|
118108
118568
|
try {
|
|
118109
|
-
const stat =
|
|
118569
|
+
const stat = statSync56(tmpPath);
|
|
118110
118570
|
if (stat.mtimeMs < cutoff) {
|
|
118111
118571
|
unlinkSync19(tmpPath);
|
|
118112
118572
|
}
|
|
@@ -118116,7 +118576,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
118116
118576
|
var LOCK_RETRY_MS = 25;
|
|
118117
118577
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
118118
118578
|
function withLock(stateDir, fn) {
|
|
118119
|
-
const lockPath =
|
|
118579
|
+
const lockPath = join99(stateDir, ISSUES_LOCK);
|
|
118120
118580
|
const startedAt = Date.now();
|
|
118121
118581
|
let fd = null;
|
|
118122
118582
|
while (fd === null) {
|
|
@@ -118401,7 +118861,7 @@ function relTime(deltaMs) {
|
|
|
118401
118861
|
init_source();
|
|
118402
118862
|
import { existsSync as existsSync98 } from "node:fs";
|
|
118403
118863
|
import { homedir as homedir54 } from "node:os";
|
|
118404
|
-
import { join as
|
|
118864
|
+
import { join as join102, resolve as resolve52 } from "node:path";
|
|
118405
118865
|
|
|
118406
118866
|
// src/deps/python.ts
|
|
118407
118867
|
import { createHash as createHash18 } from "node:crypto";
|
|
@@ -118412,7 +118872,7 @@ import {
|
|
|
118412
118872
|
rmSync as rmSync16,
|
|
118413
118873
|
writeFileSync as writeFileSync39
|
|
118414
118874
|
} from "node:fs";
|
|
118415
|
-
import { dirname as
|
|
118875
|
+
import { dirname as dirname44, join as join100 } from "node:path";
|
|
118416
118876
|
import { homedir as homedir52 } from "node:os";
|
|
118417
118877
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
118418
118878
|
|
|
@@ -118425,7 +118885,7 @@ class PythonEnvError extends Error {
|
|
|
118425
118885
|
}
|
|
118426
118886
|
}
|
|
118427
118887
|
function defaultPythonCacheRoot() {
|
|
118428
|
-
return
|
|
118888
|
+
return join100(homedir52(), ".switchroom", "deps", "python");
|
|
118429
118889
|
}
|
|
118430
118890
|
function hashFile(path7) {
|
|
118431
118891
|
return createHash18("sha256").update(readFileSync88(path7)).digest("hex");
|
|
@@ -118437,11 +118897,11 @@ function ensurePythonEnv(opts) {
|
|
|
118437
118897
|
if (!existsSync96(requirementsPath)) {
|
|
118438
118898
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
118439
118899
|
}
|
|
118440
|
-
const venvDir =
|
|
118441
|
-
const stampPath =
|
|
118442
|
-
const binDir =
|
|
118443
|
-
const pythonBin =
|
|
118444
|
-
const pipBin =
|
|
118900
|
+
const venvDir = join100(cacheRoot, skillName);
|
|
118901
|
+
const stampPath = join100(venvDir, ".requirements.sha256");
|
|
118902
|
+
const binDir = join100(venvDir, "bin");
|
|
118903
|
+
const pythonBin = join100(binDir, "python");
|
|
118904
|
+
const pipBin = join100(binDir, "pip");
|
|
118445
118905
|
const targetHash = hashFile(requirementsPath);
|
|
118446
118906
|
if (!force && existsSync96(stampPath) && existsSync96(pythonBin)) {
|
|
118447
118907
|
const existingHash = readFileSync88(stampPath, "utf8").trim();
|
|
@@ -118459,7 +118919,7 @@ function ensurePythonEnv(opts) {
|
|
|
118459
118919
|
if (existsSync96(venvDir)) {
|
|
118460
118920
|
rmSync16(venvDir, { recursive: true, force: true });
|
|
118461
118921
|
}
|
|
118462
|
-
mkdirSync54(
|
|
118922
|
+
mkdirSync54(dirname44(venvDir), { recursive: true });
|
|
118463
118923
|
try {
|
|
118464
118924
|
execFileSync28(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
118465
118925
|
} catch (err) {
|
|
@@ -118500,7 +118960,7 @@ import {
|
|
|
118500
118960
|
rmSync as rmSync17,
|
|
118501
118961
|
writeFileSync as writeFileSync40
|
|
118502
118962
|
} from "node:fs";
|
|
118503
|
-
import { dirname as
|
|
118963
|
+
import { dirname as dirname45, join as join101 } from "node:path";
|
|
118504
118964
|
import { homedir as homedir53 } from "node:os";
|
|
118505
118965
|
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
118506
118966
|
|
|
@@ -118524,16 +118984,16 @@ var LOCKFILES_FOR = {
|
|
|
118524
118984
|
npm: ["package-lock.json"]
|
|
118525
118985
|
};
|
|
118526
118986
|
function defaultNodeCacheRoot() {
|
|
118527
|
-
return
|
|
118987
|
+
return join101(homedir53(), ".switchroom", "deps", "node");
|
|
118528
118988
|
}
|
|
118529
118989
|
function hashDepInputs(packageJsonPath) {
|
|
118530
|
-
const sourceDir =
|
|
118990
|
+
const sourceDir = dirname45(packageJsonPath);
|
|
118531
118991
|
const hasher = createHash19("sha256");
|
|
118532
118992
|
hasher.update(`package.json
|
|
118533
118993
|
`);
|
|
118534
118994
|
hasher.update(readFileSync89(packageJsonPath));
|
|
118535
118995
|
for (const lockName of ALL_LOCKFILES) {
|
|
118536
|
-
const lockPath =
|
|
118996
|
+
const lockPath = join101(sourceDir, lockName);
|
|
118537
118997
|
if (existsSync97(lockPath)) {
|
|
118538
118998
|
hasher.update(`
|
|
118539
118999
|
`);
|
|
@@ -118552,11 +119012,11 @@ function ensureNodeEnv(opts) {
|
|
|
118552
119012
|
if (!existsSync97(packageJsonPath)) {
|
|
118553
119013
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
118554
119014
|
}
|
|
118555
|
-
const sourceDir =
|
|
118556
|
-
const envDir =
|
|
118557
|
-
const stampPath =
|
|
118558
|
-
const nodeModulesDir =
|
|
118559
|
-
const binDir =
|
|
119015
|
+
const sourceDir = dirname45(packageJsonPath);
|
|
119016
|
+
const envDir = join101(cacheRoot, skillName);
|
|
119017
|
+
const stampPath = join101(envDir, ".package.sha256");
|
|
119018
|
+
const nodeModulesDir = join101(envDir, "node_modules");
|
|
119019
|
+
const binDir = join101(nodeModulesDir, ".bin");
|
|
118560
119020
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
118561
119021
|
if (!force && existsSync97(stampPath) && existsSync97(nodeModulesDir)) {
|
|
118562
119022
|
const existingHash = readFileSync89(stampPath, "utf8").trim();
|
|
@@ -118574,12 +119034,12 @@ function ensureNodeEnv(opts) {
|
|
|
118574
119034
|
rmSync17(envDir, { recursive: true, force: true });
|
|
118575
119035
|
}
|
|
118576
119036
|
mkdirSync55(envDir, { recursive: true });
|
|
118577
|
-
copyFileSync13(packageJsonPath,
|
|
119037
|
+
copyFileSync13(packageJsonPath, join101(envDir, "package.json"));
|
|
118578
119038
|
let copiedLockfile = false;
|
|
118579
119039
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
118580
|
-
const lockPath =
|
|
119040
|
+
const lockPath = join101(sourceDir, lockName);
|
|
118581
119041
|
if (existsSync97(lockPath)) {
|
|
118582
|
-
copyFileSync13(lockPath,
|
|
119042
|
+
copyFileSync13(lockPath, join101(envDir, lockName));
|
|
118583
119043
|
copiedLockfile = true;
|
|
118584
119044
|
}
|
|
118585
119045
|
}
|
|
@@ -118618,13 +119078,13 @@ function registerDepsCommand(program3) {
|
|
|
118618
119078
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
118619
119079
|
process.exit(1);
|
|
118620
119080
|
}
|
|
118621
|
-
const skillDir =
|
|
119081
|
+
const skillDir = join102(skillsRoot, skill);
|
|
118622
119082
|
if (!existsSync98(skillDir)) {
|
|
118623
119083
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
118624
119084
|
process.exit(1);
|
|
118625
119085
|
}
|
|
118626
|
-
const requirementsPath =
|
|
118627
|
-
const packageJsonPath =
|
|
119086
|
+
const requirementsPath = join102(skillDir, "requirements.txt");
|
|
119087
|
+
const packageJsonPath = join102(skillDir, "package.json");
|
|
118628
119088
|
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync98(requirementsPath));
|
|
118629
119089
|
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync98(packageJsonPath));
|
|
118630
119090
|
let did = 0;
|
|
@@ -119579,7 +120039,7 @@ init_helpers();
|
|
|
119579
120039
|
init_loader();
|
|
119580
120040
|
init_merge();
|
|
119581
120041
|
import { copyFileSync as copyFileSync14, existsSync as existsSync100, readFileSync as readFileSync90, writeFileSync as writeFileSync41 } from "node:fs";
|
|
119582
|
-
import { join as
|
|
120042
|
+
import { join as join103, resolve as resolve54 } from "node:path";
|
|
119583
120043
|
init_scaffold();
|
|
119584
120044
|
init_profiles();
|
|
119585
120045
|
init_schema();
|
|
@@ -119605,7 +120065,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
119605
120065
|
profileName,
|
|
119606
120066
|
profilePath,
|
|
119607
120067
|
workspaceDir,
|
|
119608
|
-
soulPath:
|
|
120068
|
+
soulPath: join103(workspaceDir, "SOUL.md"),
|
|
119609
120069
|
soul: merged.soul
|
|
119610
120070
|
};
|
|
119611
120071
|
}
|
|
@@ -119671,8 +120131,8 @@ function registerSoulCommand(program3) {
|
|
|
119671
120131
|
// src/cli/debug.ts
|
|
119672
120132
|
init_helpers();
|
|
119673
120133
|
init_loader();
|
|
119674
|
-
import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as
|
|
119675
|
-
import { resolve as resolve55, join as
|
|
120134
|
+
import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as readdirSync35, statSync as statSync57 } from "node:fs";
|
|
120135
|
+
import { resolve as resolve55, join as join104 } from "node:path";
|
|
119676
120136
|
import { createHash as createHash20 } from "node:crypto";
|
|
119677
120137
|
init_merge();
|
|
119678
120138
|
init_hindsight2();
|
|
@@ -119683,7 +120143,7 @@ function estimateTokens(bytes) {
|
|
|
119683
120143
|
return Math.round(bytes / 3.7);
|
|
119684
120144
|
}
|
|
119685
120145
|
function readMcpServerNames(agentDir) {
|
|
119686
|
-
const mcpPath =
|
|
120146
|
+
const mcpPath = join104(agentDir, ".mcp.json");
|
|
119687
120147
|
if (!existsSync101(mcpPath))
|
|
119688
120148
|
return [];
|
|
119689
120149
|
try {
|
|
@@ -119697,20 +120157,20 @@ function sha2562(content) {
|
|
|
119697
120157
|
return createHash20("sha256").update(content).digest("hex").slice(0, 16);
|
|
119698
120158
|
}
|
|
119699
120159
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
119700
|
-
const projectsDir =
|
|
120160
|
+
const projectsDir = join104(claudeConfigDir, "projects");
|
|
119701
120161
|
if (!existsSync101(projectsDir))
|
|
119702
120162
|
return;
|
|
119703
120163
|
try {
|
|
119704
|
-
const entries =
|
|
120164
|
+
const entries = readdirSync35(projectsDir, { withFileTypes: true });
|
|
119705
120165
|
let latest;
|
|
119706
120166
|
for (const entry of entries) {
|
|
119707
120167
|
if (!entry.isDirectory())
|
|
119708
120168
|
continue;
|
|
119709
|
-
const projectPath =
|
|
119710
|
-
const transcriptPath =
|
|
120169
|
+
const projectPath = join104(projectsDir, entry.name);
|
|
120170
|
+
const transcriptPath = join104(projectPath, "transcript.jsonl");
|
|
119711
120171
|
if (!existsSync101(transcriptPath))
|
|
119712
120172
|
continue;
|
|
119713
|
-
const stat3 =
|
|
120173
|
+
const stat3 = statSync57(transcriptPath);
|
|
119714
120174
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
119715
120175
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
119716
120176
|
}
|
|
@@ -119776,11 +120236,11 @@ function registerDebugCommand(program3) {
|
|
|
119776
120236
|
process.exit(1);
|
|
119777
120237
|
}
|
|
119778
120238
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
119779
|
-
const claudeConfigDir =
|
|
119780
|
-
const claudeMdPath =
|
|
119781
|
-
const soulMdPath =
|
|
119782
|
-
const workspaceSoulMdPath =
|
|
119783
|
-
const handoffPath =
|
|
120239
|
+
const claudeConfigDir = join104(agentDir, ".claude");
|
|
120240
|
+
const claudeMdPath = join104(agentDir, "CLAUDE.md");
|
|
120241
|
+
const soulMdPath = join104(agentDir, "SOUL.md");
|
|
120242
|
+
const workspaceSoulMdPath = join104(workspaceDir, "SOUL.md");
|
|
120243
|
+
const handoffPath = join104(agentDir, ".handoff.md");
|
|
119784
120244
|
const lastN = parseInt(opts.last, 10);
|
|
119785
120245
|
if (isNaN(lastN) || lastN < 1) {
|
|
119786
120246
|
console.error("--last must be a positive integer");
|
|
@@ -119909,9 +120369,9 @@ function registerDebugCommand(program3) {
|
|
|
119909
120369
|
const soulMdBytes = soulMdContent.length;
|
|
119910
120370
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
119911
120371
|
const userBytes = userMessage?.text.length ?? 0;
|
|
119912
|
-
const fleetDir =
|
|
119913
|
-
const fleetInvPath =
|
|
119914
|
-
const fleetClaudePath =
|
|
120372
|
+
const fleetDir = join104(agentsDir, "..", "fleet");
|
|
120373
|
+
const fleetInvPath = join104(fleetDir, "switchroom-invariants.md");
|
|
120374
|
+
const fleetClaudePath = join104(fleetDir, "CLAUDE.md");
|
|
119915
120375
|
const fleetInvBytes = existsSync101(fleetInvPath) ? readFileSync91(fleetInvPath, "utf-8").length : 0;
|
|
119916
120376
|
const fleetClaudeBytes = existsSync101(fleetClaudePath) ? readFileSync91(fleetClaudePath, "utf-8").length : 0;
|
|
119917
120377
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
@@ -119947,7 +120407,7 @@ init_source();
|
|
|
119947
120407
|
// src/worktree/claim.ts
|
|
119948
120408
|
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
119949
120409
|
import { closeSync as closeSync18, mkdirSync as mkdirSync57, openSync as openSync18, existsSync as existsSync103, unlinkSync as unlinkSync21 } from "node:fs";
|
|
119950
|
-
import { join as
|
|
120410
|
+
import { join as join106, resolve as resolve57 } from "node:path";
|
|
119951
120411
|
import { homedir as homedir56 } from "node:os";
|
|
119952
120412
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
119953
120413
|
|
|
@@ -119956,18 +120416,18 @@ import {
|
|
|
119956
120416
|
mkdirSync as mkdirSync56,
|
|
119957
120417
|
writeFileSync as writeFileSync42,
|
|
119958
120418
|
readFileSync as readFileSync92,
|
|
119959
|
-
readdirSync as
|
|
120419
|
+
readdirSync as readdirSync36,
|
|
119960
120420
|
unlinkSync as unlinkSync20,
|
|
119961
120421
|
existsSync as existsSync102,
|
|
119962
120422
|
renameSync as renameSync26
|
|
119963
120423
|
} from "node:fs";
|
|
119964
|
-
import { join as
|
|
120424
|
+
import { join as join105, resolve as resolve56 } from "node:path";
|
|
119965
120425
|
import { homedir as homedir55 } from "node:os";
|
|
119966
120426
|
function registryDir() {
|
|
119967
|
-
return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
120427
|
+
return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ?? join105(homedir55(), ".switchroom", "worktrees"));
|
|
119968
120428
|
}
|
|
119969
120429
|
function recordPath(id) {
|
|
119970
|
-
return
|
|
120430
|
+
return join105(registryDir(), `${id}.json`);
|
|
119971
120431
|
}
|
|
119972
120432
|
function ensureDir2() {
|
|
119973
120433
|
mkdirSync56(registryDir(), { recursive: true });
|
|
@@ -119999,7 +120459,7 @@ function listRecords() {
|
|
|
119999
120459
|
ensureDir2();
|
|
120000
120460
|
const dir = registryDir();
|
|
120001
120461
|
const records = [];
|
|
120002
|
-
for (const entry of
|
|
120462
|
+
for (const entry of readdirSync36(dir)) {
|
|
120003
120463
|
if (!entry.endsWith(".json"))
|
|
120004
120464
|
continue;
|
|
120005
120465
|
const id = entry.slice(0, -5);
|
|
@@ -120018,7 +120478,7 @@ function acquireRepoLock(repoPath) {
|
|
|
120018
120478
|
const lockDir = registryDir();
|
|
120019
120479
|
mkdirSync57(lockDir, { recursive: true });
|
|
120020
120480
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
120021
|
-
const lockPath =
|
|
120481
|
+
const lockPath = join106(lockDir, `.lock-${lockName}`);
|
|
120022
120482
|
const deadline = Date.now() + 5000;
|
|
120023
120483
|
let fd = null;
|
|
120024
120484
|
while (fd === null) {
|
|
@@ -120045,7 +120505,7 @@ function acquireRepoLock(repoPath) {
|
|
|
120045
120505
|
}
|
|
120046
120506
|
var DEFAULT_CONCURRENCY = 5;
|
|
120047
120507
|
function worktreesBaseDir() {
|
|
120048
|
-
return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
120508
|
+
return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ?? join106(homedir56(), ".switchroom", "worktree-checkouts"));
|
|
120049
120509
|
}
|
|
120050
120510
|
function shortId() {
|
|
120051
120511
|
return randomBytes16(4).toString("hex");
|
|
@@ -120067,7 +120527,7 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
120067
120527
|
}
|
|
120068
120528
|
function expandHome(p) {
|
|
120069
120529
|
if (p.startsWith("~/"))
|
|
120070
|
-
return
|
|
120530
|
+
return join106(homedir56(), p.slice(2));
|
|
120071
120531
|
return p;
|
|
120072
120532
|
}
|
|
120073
120533
|
async function claimWorktree(input, codeRepos) {
|
|
@@ -120095,7 +120555,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
120095
120555
|
branch = `task/${taskSuffix}-${id}`;
|
|
120096
120556
|
const baseDir = worktreesBaseDir();
|
|
120097
120557
|
mkdirSync57(baseDir, { recursive: true });
|
|
120098
|
-
worktreePath =
|
|
120558
|
+
worktreePath = join106(baseDir, `${id}-${taskSuffix}`);
|
|
120099
120559
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
120100
120560
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
120101
120561
|
const now2 = new Date().toISOString();
|
|
@@ -120319,14 +120779,14 @@ import { execFileSync as execFileSync33 } from "node:child_process";
|
|
|
120319
120779
|
import {
|
|
120320
120780
|
existsSync as existsSync106,
|
|
120321
120781
|
readFileSync as readFileSync93,
|
|
120322
|
-
readdirSync as
|
|
120323
|
-
statSync as
|
|
120782
|
+
readdirSync as readdirSync37,
|
|
120783
|
+
statSync as statSync58,
|
|
120324
120784
|
renameSync as renameSync27,
|
|
120325
120785
|
mkdirSync as mkdirSync58,
|
|
120326
120786
|
rmSync as rmSync18
|
|
120327
120787
|
} from "node:fs";
|
|
120328
120788
|
import { homedir as homedir57 } from "node:os";
|
|
120329
|
-
import { join as
|
|
120789
|
+
import { join as join107, resolve as resolve58 } from "node:path";
|
|
120330
120790
|
function parseGitdirPointer(dotGitFileContents) {
|
|
120331
120791
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
120332
120792
|
return m ? m[1] : null;
|
|
@@ -120485,7 +120945,7 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
|
120485
120945
|
let newest = 0;
|
|
120486
120946
|
for (const f of files) {
|
|
120487
120947
|
try {
|
|
120488
|
-
const m =
|
|
120948
|
+
const m = statSync58(join107(dir, f)).mtimeMs;
|
|
120489
120949
|
if (m > newest)
|
|
120490
120950
|
newest = m;
|
|
120491
120951
|
} catch {}
|
|
@@ -120493,17 +120953,17 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
|
120493
120953
|
return newest || Date.now();
|
|
120494
120954
|
}
|
|
120495
120955
|
function trashRoot() {
|
|
120496
|
-
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ??
|
|
120956
|
+
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join107(homedir57(), ".switchroom", "worktree-gc-trash"));
|
|
120497
120957
|
}
|
|
120498
120958
|
function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
120499
120959
|
const exists = deps.existsSync ?? existsSync106;
|
|
120500
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
120960
|
+
const readDir = deps.readDir ?? ((p) => readdirSync37(p));
|
|
120501
120961
|
const readFile4 = deps.readFile ?? ((p) => readFileSync93(p, "utf8"));
|
|
120502
|
-
const stat3 = deps.stat ?? ((p) =>
|
|
120962
|
+
const stat3 = deps.stat ?? ((p) => statSync58(p));
|
|
120503
120963
|
const exec = deps.exec ?? defaultExec3;
|
|
120504
120964
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
120505
120965
|
const stamp = deps.dateStamp ?? "undated";
|
|
120506
|
-
const trash =
|
|
120966
|
+
const trash = join107(trashRoot(), stamp);
|
|
120507
120967
|
const probeInUse = deps.probeInUse ?? probePathInUse;
|
|
120508
120968
|
const newestMtime = deps.newestTrackedMtimeMs ?? ((dir) => defaultNewestTrackedMtimeMs(dir, exec));
|
|
120509
120969
|
const idleDays = deps.idleDays ?? 14;
|
|
@@ -120544,10 +121004,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120544
121004
|
continue;
|
|
120545
121005
|
}
|
|
120546
121006
|
for (const name of entries) {
|
|
120547
|
-
const dir =
|
|
121007
|
+
const dir = join107(root, name);
|
|
120548
121008
|
if (isEphemeralPath(dir))
|
|
120549
121009
|
continue;
|
|
120550
|
-
const dotGit =
|
|
121010
|
+
const dotGit = join107(dir, ".git");
|
|
120551
121011
|
if (!exists(dotGit))
|
|
120552
121012
|
continue;
|
|
120553
121013
|
let st;
|
|
@@ -120576,7 +121036,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120576
121036
|
ownerRepos.add(repoRoot);
|
|
120577
121037
|
if (exists(ptr))
|
|
120578
121038
|
continue;
|
|
120579
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
121039
|
+
orphans.push({ dir, owner: repoRoot, dest: join107(trash, name) });
|
|
120580
121040
|
}
|
|
120581
121041
|
}
|
|
120582
121042
|
const registered = [];
|
|
@@ -120631,10 +121091,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120631
121091
|
continue;
|
|
120632
121092
|
}
|
|
120633
121093
|
for (const name of entries) {
|
|
120634
|
-
const dir =
|
|
121094
|
+
const dir = join107(root, name);
|
|
120635
121095
|
if (isEphemeralPath(dir))
|
|
120636
121096
|
continue;
|
|
120637
|
-
const dotGit =
|
|
121097
|
+
const dotGit = join107(dir, ".git");
|
|
120638
121098
|
if (!exists(dotGit))
|
|
120639
121099
|
continue;
|
|
120640
121100
|
let shape;
|
|
@@ -120739,7 +121199,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120739
121199
|
verdict,
|
|
120740
121200
|
prSignal: sig,
|
|
120741
121201
|
idle,
|
|
120742
|
-
dest:
|
|
121202
|
+
dest: join107(trash, name),
|
|
120743
121203
|
willAct
|
|
120744
121204
|
});
|
|
120745
121205
|
}
|
|
@@ -120816,13 +121276,13 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
120816
121276
|
}
|
|
120817
121277
|
function listTrashEntries(nowMs, deps = {}) {
|
|
120818
121278
|
const exists = deps.existsSync ?? existsSync106;
|
|
120819
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
121279
|
+
const readDir = deps.readDir ?? ((p) => readdirSync37(p));
|
|
120820
121280
|
const root = trashRoot();
|
|
120821
121281
|
if (!exists(root))
|
|
120822
121282
|
return [];
|
|
120823
121283
|
const out = [];
|
|
120824
121284
|
for (const stamp of readDir(root)) {
|
|
120825
|
-
const stampDir =
|
|
121285
|
+
const stampDir = join107(root, stamp);
|
|
120826
121286
|
let names;
|
|
120827
121287
|
try {
|
|
120828
121288
|
names = readDir(stampDir);
|
|
@@ -120830,10 +121290,10 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
120830
121290
|
continue;
|
|
120831
121291
|
}
|
|
120832
121292
|
for (const name of names) {
|
|
120833
|
-
const p =
|
|
121293
|
+
const p = join107(stampDir, name);
|
|
120834
121294
|
let mtimeMs = nowMs;
|
|
120835
121295
|
try {
|
|
120836
|
-
mtimeMs =
|
|
121296
|
+
mtimeMs = statSync58(p).mtimeMs;
|
|
120837
121297
|
} catch {}
|
|
120838
121298
|
out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
|
|
120839
121299
|
}
|
|
@@ -120854,20 +121314,20 @@ function purgeTrash(paths) {
|
|
|
120854
121314
|
return { deleted, errors: errors2 };
|
|
120855
121315
|
}
|
|
120856
121316
|
function defaultRoots() {
|
|
120857
|
-
return [
|
|
121317
|
+
return [join107(homedir57(), "code")];
|
|
120858
121318
|
}
|
|
120859
121319
|
function defaultTaskTreeRoots() {
|
|
120860
|
-
const agentsDir =
|
|
121320
|
+
const agentsDir = join107(homedir57(), ".switchroom", "agents");
|
|
120861
121321
|
let names;
|
|
120862
121322
|
try {
|
|
120863
|
-
names =
|
|
121323
|
+
names = readdirSync37(agentsDir);
|
|
120864
121324
|
} catch {
|
|
120865
121325
|
return [];
|
|
120866
121326
|
}
|
|
120867
121327
|
const roots = [];
|
|
120868
121328
|
for (const name of names.sort()) {
|
|
120869
121329
|
for (const sub of ["home/work", "home/workspace"]) {
|
|
120870
|
-
const r =
|
|
121330
|
+
const r = join107(agentsDir, name, sub);
|
|
120871
121331
|
if (existsSync106(r))
|
|
120872
121332
|
roots.push(r);
|
|
120873
121333
|
}
|
|
@@ -121128,11 +121588,11 @@ init_scaffold_integration();
|
|
|
121128
121588
|
import {
|
|
121129
121589
|
chmodSync as chmodSync17,
|
|
121130
121590
|
mkdirSync as mkdirSync59,
|
|
121131
|
-
readdirSync as
|
|
121591
|
+
readdirSync as readdirSync38,
|
|
121132
121592
|
rmSync as rmSync19,
|
|
121133
121593
|
writeFileSync as writeFileSync43
|
|
121134
121594
|
} from "node:fs";
|
|
121135
|
-
import { join as
|
|
121595
|
+
import { join as join108 } from "node:path";
|
|
121136
121596
|
function encodeCredentialsFilename(email) {
|
|
121137
121597
|
const SAFE = new Set([
|
|
121138
121598
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -121396,16 +121856,16 @@ function resolveCredentialsDir(env2) {
|
|
|
121396
121856
|
if (explicit && explicit.length > 0)
|
|
121397
121857
|
return explicit;
|
|
121398
121858
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
121399
|
-
return
|
|
121859
|
+
return join108(stateBase, "google-workspace-mcp", "credentials");
|
|
121400
121860
|
}
|
|
121401
121861
|
function writeSeedFile(dir, email, seed) {
|
|
121402
121862
|
mkdirSync59(dir, { recursive: true, mode: 448 });
|
|
121403
121863
|
chmodSync17(dir, 448);
|
|
121404
|
-
for (const name of
|
|
121405
|
-
rmSync19(
|
|
121864
|
+
for (const name of readdirSync38(dir)) {
|
|
121865
|
+
rmSync19(join108(dir, name), { force: true, recursive: true });
|
|
121406
121866
|
}
|
|
121407
121867
|
const filename = encodeCredentialsFilename(email);
|
|
121408
|
-
const filePath =
|
|
121868
|
+
const filePath = join108(dir, filename);
|
|
121409
121869
|
writeFileSync43(filePath, JSON.stringify(seed), { mode: 384 });
|
|
121410
121870
|
chmodSync17(filePath, 384);
|
|
121411
121871
|
return filePath;
|
|
@@ -121602,7 +122062,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
121602
122062
|
init_scaffold_integration();
|
|
121603
122063
|
import { spawn as spawn6 } from "node:child_process";
|
|
121604
122064
|
import { writeFileSync as writeFileSync44, mkdirSync as mkdirSync60 } from "node:fs";
|
|
121605
|
-
import { dirname as
|
|
122065
|
+
import { dirname as dirname46, join as join109 } from "node:path";
|
|
121606
122066
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
121607
122067
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
121608
122068
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -121638,7 +122098,7 @@ function computeRefreshDelayMs(expiresAt, now2, leadMs = DEFAULT_REFRESH_LEAD_MS
|
|
|
121638
122098
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
121639
122099
|
const path10 = heartbeatPath(agentName, account);
|
|
121640
122100
|
try {
|
|
121641
|
-
mkdirSync60(
|
|
122101
|
+
mkdirSync60(dirname46(path10), { recursive: true });
|
|
121642
122102
|
writeFileSync44(path10, JSON.stringify(data, null, 2), { mode: 420 });
|
|
121643
122103
|
} catch {}
|
|
121644
122104
|
}
|
|
@@ -121647,7 +122107,7 @@ function heartbeatPath(agentName, account) {
|
|
|
121647
122107
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
121648
122108
|
if (override) {
|
|
121649
122109
|
const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
|
|
121650
|
-
return
|
|
122110
|
+
return join109(override, `${base}.heartbeat.json`);
|
|
121651
122111
|
}
|
|
121652
122112
|
return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
|
|
121653
122113
|
}
|
|
@@ -121880,7 +122340,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
121880
122340
|
init_scaffold_integration();
|
|
121881
122341
|
import { spawn as spawn7 } from "node:child_process";
|
|
121882
122342
|
import { existsSync as existsSync107, mkdirSync as mkdirSync61, writeFileSync as writeFileSync45 } from "node:fs";
|
|
121883
|
-
import { dirname as
|
|
122343
|
+
import { dirname as dirname47 } from "node:path";
|
|
121884
122344
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
121885
122345
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
121886
122346
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -121890,7 +122350,7 @@ function buildNotionMcpArgs(opts) {
|
|
|
121890
122350
|
}
|
|
121891
122351
|
function defaultWriteHeartbeat(path10, contents) {
|
|
121892
122352
|
try {
|
|
121893
|
-
const dir =
|
|
122353
|
+
const dir = dirname47(path10);
|
|
121894
122354
|
if (!existsSync107(dir))
|
|
121895
122355
|
mkdirSync61(dir, { recursive: true });
|
|
121896
122356
|
writeFileSync45(path10, contents);
|
|
@@ -122005,7 +122465,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
122005
122465
|
init_hindsight_directive_admin();
|
|
122006
122466
|
import { mkdirSync as mkdirSync62, readFileSync as readFileSync94, renameSync as renameSync28, writeFileSync as writeFileSync46 } from "node:fs";
|
|
122007
122467
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
122008
|
-
import { join as
|
|
122468
|
+
import { join as join110 } from "node:path";
|
|
122009
122469
|
import { createInterface as createInterface7 } from "node:readline";
|
|
122010
122470
|
|
|
122011
122471
|
// src/memory/hindsight-knowledge-admin.ts
|
|
@@ -122593,12 +123053,12 @@ class HindsightShim {
|
|
|
122593
123053
|
`));
|
|
122594
123054
|
}
|
|
122595
123055
|
get cachePath() {
|
|
122596
|
-
return
|
|
123056
|
+
return join110(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
|
|
122597
123057
|
}
|
|
122598
123058
|
writeCache(result) {
|
|
122599
123059
|
try {
|
|
122600
123060
|
mkdirSync62(this.opts.cacheDir, { recursive: true });
|
|
122601
|
-
const tmp =
|
|
123061
|
+
const tmp = join110(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
122602
123062
|
writeFileSync46(tmp, JSON.stringify(result, null, 2) + `
|
|
122603
123063
|
`);
|
|
122604
123064
|
renameSync28(tmp, this.cachePath);
|
|
@@ -122882,7 +123342,7 @@ function resolveShimOptionsFromEnv(env2) {
|
|
|
122882
123342
|
return {
|
|
122883
123343
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
122884
123344
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
122885
|
-
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR ||
|
|
123345
|
+
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join110(home2, ".hindsight-shim")
|
|
122886
123346
|
};
|
|
122887
123347
|
}
|
|
122888
123348
|
function registerHindsightMcpShimCommand(program3) {
|
|
@@ -122895,11 +123355,11 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
122895
123355
|
|
|
122896
123356
|
// src/cli/deliver-file.ts
|
|
122897
123357
|
init_client2();
|
|
122898
|
-
import { readFileSync as readFileSync95, statSync as
|
|
122899
|
-
import { basename as
|
|
123358
|
+
import { readFileSync as readFileSync95, statSync as statSync59 } from "node:fs";
|
|
123359
|
+
import { basename as basename14 } from "node:path";
|
|
122900
123360
|
|
|
122901
123361
|
// src/delivery/onedrive.ts
|
|
122902
|
-
import { basename as
|
|
123362
|
+
import { basename as basename12 } from "node:path";
|
|
122903
123363
|
var GRAPH = "https://graph.microsoft.com/v1.0";
|
|
122904
123364
|
var ONEDRIVE_INLINE_MAX_BYTES = 4 * 1024 * 1024;
|
|
122905
123365
|
function authHeaders2(token) {
|
|
@@ -123022,14 +123482,14 @@ async function createShareLink(deps, item, scopes = ["anonymous", "organization"
|
|
|
123022
123482
|
async function deliverToOneDrive(args) {
|
|
123023
123483
|
const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
|
|
123024
123484
|
const folder = await ensureSwitchroomFolder(deps, args.agentName);
|
|
123025
|
-
const filename =
|
|
123485
|
+
const filename = basename12(args.localPath);
|
|
123026
123486
|
const item = await uploadFile(deps, folder.id, filename, args.bytes);
|
|
123027
123487
|
const link = await createShareLink(deps, item, args.linkScopes);
|
|
123028
123488
|
return { itemId: item.id, link, folderPath: `Switchroom/${args.agentName}` };
|
|
123029
123489
|
}
|
|
123030
123490
|
|
|
123031
123491
|
// src/delivery/gdrive.ts
|
|
123032
|
-
import { basename as
|
|
123492
|
+
import { basename as basename13 } from "node:path";
|
|
123033
123493
|
var DRIVE = "https://www.googleapis.com/drive/v3";
|
|
123034
123494
|
var UPLOAD = "https://www.googleapis.com/upload/drive/v3";
|
|
123035
123495
|
var FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
@@ -123177,7 +123637,7 @@ async function createShareLink2(deps, file, scopes = ["anyone"]) {
|
|
|
123177
123637
|
async function deliverToGoogleDrive(args) {
|
|
123178
123638
|
const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
|
|
123179
123639
|
const folder = await ensureSwitchroomFolder2(deps, args.agentName);
|
|
123180
|
-
const filename =
|
|
123640
|
+
const filename = basename13(args.localPath);
|
|
123181
123641
|
const file = await uploadFile2(deps, folder.id, filename, args.bytes);
|
|
123182
123642
|
const link = await createShareLink2(deps, file, args.linkScopes);
|
|
123183
123643
|
return { itemId: file.id, link, folderPath: `Switchroom/${args.agentName}` };
|
|
@@ -123234,7 +123694,7 @@ async function defaultResolveProvider() {
|
|
|
123234
123694
|
}
|
|
123235
123695
|
async function runDeliverFile(localPath, deps = {}) {
|
|
123236
123696
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
123237
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
123697
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync59(p).size);
|
|
123238
123698
|
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync95(p)));
|
|
123239
123699
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
123240
123700
|
let size;
|
|
@@ -123256,7 +123716,7 @@ async function runDeliverFile(localPath, deps = {}) {
|
|
|
123256
123716
|
try {
|
|
123257
123717
|
const bytes = read(localPath);
|
|
123258
123718
|
const out = await provider.deliver({ agentName, localPath, bytes });
|
|
123259
|
-
return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename:
|
|
123719
|
+
return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename14(localPath) };
|
|
123260
123720
|
} catch (err) {
|
|
123261
123721
|
return { ok: false, provider: provider.name, error: `upload failed: ${err.message}` };
|
|
123262
123722
|
}
|
|
@@ -123556,8 +124016,8 @@ function runRedactStdin() {
|
|
|
123556
124016
|
}
|
|
123557
124017
|
|
|
123558
124018
|
// src/cli/status-ask.ts
|
|
123559
|
-
import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as
|
|
123560
|
-
import { join as
|
|
124019
|
+
import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as readdirSync39 } from "node:fs";
|
|
124020
|
+
import { join as join111 } from "node:path";
|
|
123561
124021
|
import { homedir as homedir58 } from "node:os";
|
|
123562
124022
|
|
|
123563
124023
|
// src/status-ask/report.ts
|
|
@@ -123893,19 +124353,19 @@ function resolveSources(explicitPath) {
|
|
|
123893
124353
|
const config = loadConfig();
|
|
123894
124354
|
agentsDir = resolveAgentsDir(config);
|
|
123895
124355
|
} catch {
|
|
123896
|
-
agentsDir =
|
|
124356
|
+
agentsDir = join111(homedir58(), ".switchroom", "agents");
|
|
123897
124357
|
}
|
|
123898
124358
|
if (!existsSync108(agentsDir))
|
|
123899
124359
|
return [];
|
|
123900
124360
|
const sources = [];
|
|
123901
124361
|
let entries;
|
|
123902
124362
|
try {
|
|
123903
|
-
entries =
|
|
124363
|
+
entries = readdirSync39(agentsDir);
|
|
123904
124364
|
} catch {
|
|
123905
124365
|
return [];
|
|
123906
124366
|
}
|
|
123907
124367
|
for (const name of entries) {
|
|
123908
|
-
const path10 =
|
|
124368
|
+
const path10 = join111(agentsDir, name, "runtime-metrics.jsonl");
|
|
123909
124369
|
if (existsSync108(path10)) {
|
|
123910
124370
|
sources.push({ path: path10, agent: name });
|
|
123911
124371
|
}
|
|
@@ -123941,28 +124401,28 @@ import {
|
|
|
123941
124401
|
fsyncSync as fsyncSync8,
|
|
123942
124402
|
mkdirSync as mkdirSync63,
|
|
123943
124403
|
openSync as openSync19,
|
|
123944
|
-
readdirSync as
|
|
124404
|
+
readdirSync as readdirSync40,
|
|
123945
124405
|
readFileSync as readFileSync97,
|
|
123946
124406
|
renameSync as renameSync29,
|
|
123947
|
-
statSync as
|
|
124407
|
+
statSync as statSync60,
|
|
123948
124408
|
unlinkSync as unlinkSync22,
|
|
123949
124409
|
writeSync as writeSync10
|
|
123950
124410
|
} from "node:fs";
|
|
123951
|
-
import { join as
|
|
124411
|
+
import { join as join112, resolve as resolve59 } from "node:path";
|
|
123952
124412
|
var STAGING_SUBDIR = ".staging";
|
|
123953
124413
|
function overlayPathsFor(agent, opts = {}) {
|
|
123954
124414
|
const base = opts.root ? resolve59(opts.root, agent) : resolve59(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
123955
|
-
const scheduleDir =
|
|
123956
|
-
const scheduleStagingDir =
|
|
123957
|
-
const skillsDir =
|
|
123958
|
-
const skillsStagingDir =
|
|
124415
|
+
const scheduleDir = join112(base, "schedule.d");
|
|
124416
|
+
const scheduleStagingDir = join112(scheduleDir, STAGING_SUBDIR);
|
|
124417
|
+
const skillsDir = join112(base, "skills.d");
|
|
124418
|
+
const skillsStagingDir = join112(skillsDir, STAGING_SUBDIR);
|
|
123959
124419
|
return {
|
|
123960
124420
|
agentRoot: base,
|
|
123961
124421
|
scheduleDir,
|
|
123962
124422
|
scheduleStagingDir,
|
|
123963
124423
|
skillsDir,
|
|
123964
124424
|
skillsStagingDir,
|
|
123965
|
-
lockPath:
|
|
124425
|
+
lockPath: join112(base, ".lock"),
|
|
123966
124426
|
stagingDir: scheduleStagingDir
|
|
123967
124427
|
};
|
|
123968
124428
|
}
|
|
@@ -123988,7 +124448,7 @@ function withAgentLock(paths, fn) {
|
|
|
123988
124448
|
if (e.code !== "EEXIST")
|
|
123989
124449
|
throw err;
|
|
123990
124450
|
try {
|
|
123991
|
-
const age = Date.now() -
|
|
124451
|
+
const age = Date.now() - statSync60(paths.lockPath).mtimeMs;
|
|
123992
124452
|
if (age > 30000) {
|
|
123993
124453
|
unlinkSync22(paths.lockPath);
|
|
123994
124454
|
continue;
|
|
@@ -124021,7 +124481,7 @@ function alignStagedOwnerToDir(stagingPath, targetDir) {
|
|
|
124021
124481
|
const euid = overlayWriterRuntime.geteuid();
|
|
124022
124482
|
if (euid === undefined)
|
|
124023
124483
|
return;
|
|
124024
|
-
const dirStat =
|
|
124484
|
+
const dirStat = statSync60(targetDir);
|
|
124025
124485
|
if (dirStat.uid === euid)
|
|
124026
124486
|
return;
|
|
124027
124487
|
overlayWriterRuntime.chown(stagingPath, dirStat.uid, dirStat.gid);
|
|
@@ -124031,8 +124491,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124031
124491
|
const paths = overlayPathsFor(agent, opts);
|
|
124032
124492
|
return withAgentLock(paths, () => {
|
|
124033
124493
|
ensureDirs(paths);
|
|
124034
|
-
const stagingPath =
|
|
124035
|
-
const finalPath =
|
|
124494
|
+
const stagingPath = join112(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
124495
|
+
const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
|
|
124036
124496
|
const fd = openSync19(stagingPath, "w", 384);
|
|
124037
124497
|
try {
|
|
124038
124498
|
writeSync10(fd, yamlText);
|
|
@@ -124049,8 +124509,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124049
124509
|
const paths = overlayPathsFor(agent, opts);
|
|
124050
124510
|
return withAgentLock(paths, () => {
|
|
124051
124511
|
ensureSkillsDirs(paths);
|
|
124052
|
-
const stagingPath =
|
|
124053
|
-
const finalPath =
|
|
124512
|
+
const stagingPath = join112(paths.skillsStagingDir, `${slug}.yaml`);
|
|
124513
|
+
const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
|
|
124054
124514
|
const fd = openSync19(stagingPath, "w", 384);
|
|
124055
124515
|
try {
|
|
124056
124516
|
writeSync10(fd, yamlText);
|
|
@@ -124066,7 +124526,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124066
124526
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
124067
124527
|
const paths = overlayPathsFor(agent, opts);
|
|
124068
124528
|
return withAgentLock(paths, () => {
|
|
124069
|
-
const finalPath =
|
|
124529
|
+
const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
|
|
124070
124530
|
if (!existsSync109(finalPath))
|
|
124071
124531
|
return false;
|
|
124072
124532
|
unlinkSync22(finalPath);
|
|
@@ -124078,10 +124538,10 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
124078
124538
|
if (!existsSync109(paths.skillsDir))
|
|
124079
124539
|
return [];
|
|
124080
124540
|
const out = [];
|
|
124081
|
-
for (const name of
|
|
124541
|
+
for (const name of readdirSync40(paths.skillsDir)) {
|
|
124082
124542
|
if (!/\.ya?ml$/i.test(name))
|
|
124083
124543
|
continue;
|
|
124084
|
-
const full =
|
|
124544
|
+
const full = join112(paths.skillsDir, name);
|
|
124085
124545
|
try {
|
|
124086
124546
|
const raw = readFileSync97(full, "utf-8");
|
|
124087
124547
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -124093,7 +124553,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
124093
124553
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
124094
124554
|
const paths = overlayPathsFor(agent, opts);
|
|
124095
124555
|
return withAgentLock(paths, () => {
|
|
124096
|
-
const finalPath =
|
|
124556
|
+
const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
|
|
124097
124557
|
if (!existsSync109(finalPath))
|
|
124098
124558
|
return false;
|
|
124099
124559
|
unlinkSync22(finalPath);
|
|
@@ -124105,10 +124565,10 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
124105
124565
|
if (!existsSync109(paths.scheduleDir))
|
|
124106
124566
|
return [];
|
|
124107
124567
|
const out = [];
|
|
124108
|
-
for (const name of
|
|
124568
|
+
for (const name of readdirSync40(paths.scheduleDir)) {
|
|
124109
124569
|
if (!/\.ya?ml$/i.test(name))
|
|
124110
124570
|
continue;
|
|
124111
|
-
const full =
|
|
124571
|
+
const full = join112(paths.scheduleDir, name);
|
|
124112
124572
|
try {
|
|
124113
124573
|
const raw = readFileSync97(full, "utf-8");
|
|
124114
124574
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -124334,19 +124794,19 @@ import {
|
|
|
124334
124794
|
fsyncSync as fsyncSync9,
|
|
124335
124795
|
mkdirSync as mkdirSync64,
|
|
124336
124796
|
openSync as openSync20,
|
|
124337
|
-
readdirSync as
|
|
124797
|
+
readdirSync as readdirSync41,
|
|
124338
124798
|
readFileSync as readFileSync98,
|
|
124339
124799
|
renameSync as renameSync30,
|
|
124340
124800
|
unlinkSync as unlinkSync23,
|
|
124341
124801
|
writeFileSync as writeFileSync47,
|
|
124342
124802
|
writeSync as writeSync11
|
|
124343
124803
|
} from "node:fs";
|
|
124344
|
-
import { join as
|
|
124804
|
+
import { join as join113 } from "node:path";
|
|
124345
124805
|
import { randomBytes as randomBytes17 } from "node:crypto";
|
|
124346
124806
|
var STAGE_ID_PREFIX = "cap_";
|
|
124347
124807
|
function pendingDir(agent, opts = {}) {
|
|
124348
124808
|
const paths = overlayPathsFor(agent, opts);
|
|
124349
|
-
return
|
|
124809
|
+
return join113(paths.scheduleDir, ".pending");
|
|
124350
124810
|
}
|
|
124351
124811
|
function ensurePendingDir(agent, opts = {}) {
|
|
124352
124812
|
const dir = pendingDir(agent, opts);
|
|
@@ -124359,8 +124819,8 @@ function newStageId() {
|
|
|
124359
124819
|
function stagePendingScheduleEntry(opts) {
|
|
124360
124820
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
124361
124821
|
const stageId = opts.stageId ?? newStageId();
|
|
124362
|
-
const yamlPath =
|
|
124363
|
-
const metaPath =
|
|
124822
|
+
const yamlPath = join113(dir, `${stageId}.yaml`);
|
|
124823
|
+
const metaPath = join113(dir, `${stageId}.meta.json`);
|
|
124364
124824
|
const meta = {
|
|
124365
124825
|
v: 1,
|
|
124366
124826
|
stage_id: stageId,
|
|
@@ -124390,12 +124850,12 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
124390
124850
|
if (!existsSync110(dir))
|
|
124391
124851
|
return [];
|
|
124392
124852
|
const out = [];
|
|
124393
|
-
for (const name of
|
|
124853
|
+
for (const name of readdirSync41(dir).sort()) {
|
|
124394
124854
|
if (!name.endsWith(".meta.json"))
|
|
124395
124855
|
continue;
|
|
124396
124856
|
const stageId = name.slice(0, -".meta.json".length);
|
|
124397
|
-
const metaPath =
|
|
124398
|
-
const yamlPath =
|
|
124857
|
+
const metaPath = join113(dir, name);
|
|
124858
|
+
const yamlPath = join113(dir, `${stageId}.yaml`);
|
|
124399
124859
|
if (!existsSync110(yamlPath))
|
|
124400
124860
|
continue;
|
|
124401
124861
|
try {
|
|
@@ -124414,7 +124874,7 @@ function commitPendingScheduleEntry(opts) {
|
|
|
124414
124874
|
return { committed: false, reason: "not_found" };
|
|
124415
124875
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
124416
124876
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
124417
|
-
const finalPath =
|
|
124877
|
+
const finalPath = join113(paths.scheduleDir, `${slug}.yaml`);
|
|
124418
124878
|
if (existsSync110(finalPath)) {
|
|
124419
124879
|
return { committed: false, reason: "slug_collision" };
|
|
124420
124880
|
}
|
|
@@ -125067,7 +125527,7 @@ import { existsSync as existsSync112 } from "node:fs";
|
|
|
125067
125527
|
init_reconcile_default_skills();
|
|
125068
125528
|
init_agent_config();
|
|
125069
125529
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
125070
|
-
import { join as
|
|
125530
|
+
import { join as join114 } from "node:path";
|
|
125071
125531
|
var MAX_SKILLS_PER_AGENT2 = 20;
|
|
125072
125532
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
125073
125533
|
function exitCodeFor2(code) {
|
|
@@ -125142,7 +125602,7 @@ function skillInstall(opts) {
|
|
|
125142
125602
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT2})`);
|
|
125143
125603
|
}
|
|
125144
125604
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
125145
|
-
const skillPath =
|
|
125605
|
+
const skillPath = join114(poolDir, skillName);
|
|
125146
125606
|
if (!existsSync112(skillPath)) {
|
|
125147
125607
|
return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
|
|
125148
125608
|
}
|
|
@@ -125314,20 +125774,20 @@ import {
|
|
|
125314
125774
|
chmodSync as chmodSync18,
|
|
125315
125775
|
closeSync as closeSync21,
|
|
125316
125776
|
existsSync as existsSync113,
|
|
125317
|
-
lstatSync as
|
|
125777
|
+
lstatSync as lstatSync15,
|
|
125318
125778
|
mkdirSync as mkdirSync65,
|
|
125319
125779
|
mkdtempSync as mkdtempSync5,
|
|
125320
125780
|
openSync as openSync21,
|
|
125321
125781
|
readFileSync as readFileSync100,
|
|
125322
|
-
readdirSync as
|
|
125782
|
+
readdirSync as readdirSync42,
|
|
125323
125783
|
realpathSync as realpathSync9,
|
|
125324
125784
|
renameSync as renameSync31,
|
|
125325
125785
|
rmSync as rmSync20,
|
|
125326
|
-
statSync as
|
|
125786
|
+
statSync as statSync61,
|
|
125327
125787
|
writeFileSync as writeFileSync48
|
|
125328
125788
|
} from "node:fs";
|
|
125329
125789
|
import { tmpdir as tmpdir6, homedir as homedir59 } from "node:os";
|
|
125330
|
-
import { dirname as
|
|
125790
|
+
import { dirname as dirname48, join as join115, relative as relative6, resolve as resolve60 } from "node:path";
|
|
125331
125791
|
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
125332
125792
|
var SKILL_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
125333
125793
|
var SH_SCRIPT_RE2 = /^scripts\/[A-Za-z0-9_.-]+\.sh$/;
|
|
@@ -125341,7 +125801,7 @@ function scanForClaudeP2(content) {
|
|
|
125341
125801
|
function resolveSkillsPoolDir2(override) {
|
|
125342
125802
|
const raw = override ?? "~/.switchroom/skills";
|
|
125343
125803
|
if (raw.startsWith("~/")) {
|
|
125344
|
-
return
|
|
125804
|
+
return join115(homedir59(), raw.slice(2));
|
|
125345
125805
|
}
|
|
125346
125806
|
if (raw === "~")
|
|
125347
125807
|
return homedir59();
|
|
@@ -125373,14 +125833,14 @@ function isTarballPath(p) {
|
|
|
125373
125833
|
}
|
|
125374
125834
|
function loadFromDir(dir) {
|
|
125375
125835
|
const abs = realpathSync9(dir);
|
|
125376
|
-
if (!
|
|
125836
|
+
if (!statSync61(abs).isDirectory()) {
|
|
125377
125837
|
fail4(`--from path is not a directory: ${dir}`);
|
|
125378
125838
|
}
|
|
125379
125839
|
const files = {};
|
|
125380
125840
|
const walk2 = (sub) => {
|
|
125381
|
-
const entries =
|
|
125841
|
+
const entries = readdirSync42(sub, { withFileTypes: true });
|
|
125382
125842
|
for (const ent of entries) {
|
|
125383
|
-
const full =
|
|
125843
|
+
const full = join115(sub, ent.name);
|
|
125384
125844
|
const rel = relative6(abs, full);
|
|
125385
125845
|
if (ent.isSymbolicLink()) {
|
|
125386
125846
|
fail4(`refusing to read symlink inside --from dir: ${rel}`);
|
|
@@ -125415,7 +125875,7 @@ function loadFromTarball(tarPath) {
|
|
|
125415
125875
|
fail4(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
125416
125876
|
}
|
|
125417
125877
|
}
|
|
125418
|
-
const staging = mkdtempSync5(
|
|
125878
|
+
const staging = mkdtempSync5(join115(tmpdir6(), "skill-apply-extract-"));
|
|
125419
125879
|
try {
|
|
125420
125880
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
125421
125881
|
const r = spawnSync24("tar", [
|
|
@@ -125501,8 +125961,8 @@ function validatePayload(name, files) {
|
|
|
125501
125961
|
errors2.push(`${path10} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
125502
125962
|
}
|
|
125503
125963
|
} else if (PY_SCRIPT_RE2.test(path10)) {
|
|
125504
|
-
const tmp = mkdtempSync5(
|
|
125505
|
-
const tmpPy =
|
|
125964
|
+
const tmp = mkdtempSync5(join115(tmpdir6(), "skill-apply-py-"));
|
|
125965
|
+
const tmpPy = join115(tmp, "check.py");
|
|
125506
125966
|
try {
|
|
125507
125967
|
writeFileSync48(tmpPy, content);
|
|
125508
125968
|
const r = spawnSync24("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -125524,8 +125984,8 @@ function diffSummary(currentDir, files) {
|
|
|
125524
125984
|
const currentFiles = {};
|
|
125525
125985
|
if (existsSync113(currentDir)) {
|
|
125526
125986
|
const walk2 = (sub) => {
|
|
125527
|
-
for (const ent of
|
|
125528
|
-
const full =
|
|
125987
|
+
for (const ent of readdirSync42(sub, { withFileTypes: true })) {
|
|
125988
|
+
const full = join115(sub, ent.name);
|
|
125529
125989
|
const rel = relative6(currentDir, full);
|
|
125530
125990
|
if (ent.isDirectory()) {
|
|
125531
125991
|
walk2(full);
|
|
@@ -125561,10 +126021,10 @@ function writePayload(poolDir, name, files) {
|
|
|
125561
126021
|
if (!existsSync113(poolDir)) {
|
|
125562
126022
|
mkdirSync65(poolDir, { recursive: true, mode: 493 });
|
|
125563
126023
|
}
|
|
125564
|
-
const target =
|
|
126024
|
+
const target = join115(poolDir, name);
|
|
125565
126025
|
let targetIsSymlink = false;
|
|
125566
126026
|
try {
|
|
125567
|
-
const st =
|
|
126027
|
+
const st = lstatSync15(target);
|
|
125568
126028
|
if (st.isSymbolicLink()) {
|
|
125569
126029
|
targetIsSymlink = true;
|
|
125570
126030
|
}
|
|
@@ -125572,13 +126032,13 @@ function writePayload(poolDir, name, files) {
|
|
|
125572
126032
|
if (targetIsSymlink) {
|
|
125573
126033
|
fail4(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
125574
126034
|
}
|
|
125575
|
-
const staging = mkdtempSync5(
|
|
126035
|
+
const staging = mkdtempSync5(join115(poolDir, `.skill-apply-stage-${name}-`));
|
|
125576
126036
|
chmodSync18(staging, 493);
|
|
125577
126037
|
let oldRename = null;
|
|
125578
126038
|
try {
|
|
125579
126039
|
for (const [path10, content] of Object.entries(files)) {
|
|
125580
|
-
const full =
|
|
125581
|
-
mkdirSync65(
|
|
126040
|
+
const full = join115(staging, path10);
|
|
126041
|
+
mkdirSync65(dirname48(full), { recursive: true, mode: 493 });
|
|
125582
126042
|
const fd = openSync21(full, "wx");
|
|
125583
126043
|
try {
|
|
125584
126044
|
writeFileSync48(fd, content);
|
|
@@ -125592,7 +126052,7 @@ function writePayload(poolDir, name, files) {
|
|
|
125592
126052
|
}
|
|
125593
126053
|
let targetExists = false;
|
|
125594
126054
|
try {
|
|
125595
|
-
|
|
126055
|
+
lstatSync15(target);
|
|
125596
126056
|
targetExists = true;
|
|
125597
126057
|
} catch {}
|
|
125598
126058
|
if (targetExists) {
|
|
@@ -125634,7 +126094,7 @@ function registerSkillCommand(program3) {
|
|
|
125634
126094
|
if (!existsSync113(fromPath)) {
|
|
125635
126095
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
125636
126096
|
}
|
|
125637
|
-
const st =
|
|
126097
|
+
const st = statSync61(fromPath);
|
|
125638
126098
|
if (st.isDirectory()) {
|
|
125639
126099
|
files = loadFromDir(fromPath);
|
|
125640
126100
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -125655,7 +126115,7 @@ function registerSkillCommand(program3) {
|
|
|
125655
126115
|
}
|
|
125656
126116
|
const config = loadConfig();
|
|
125657
126117
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
125658
|
-
const currentDir =
|
|
126118
|
+
const currentDir = join115(poolDir, name);
|
|
125659
126119
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
125660
126120
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
125661
126121
|
console.log(diffSummary(currentDir, files));
|
|
@@ -125693,19 +126153,19 @@ import {
|
|
|
125693
126153
|
chmodSync as chmodSync19,
|
|
125694
126154
|
closeSync as closeSync22,
|
|
125695
126155
|
existsSync as existsSync114,
|
|
125696
|
-
lstatSync as
|
|
126156
|
+
lstatSync as lstatSync16,
|
|
125697
126157
|
mkdirSync as mkdirSync66,
|
|
125698
126158
|
mkdtempSync as mkdtempSync6,
|
|
125699
126159
|
openSync as openSync22,
|
|
125700
126160
|
readFileSync as readFileSync101,
|
|
125701
|
-
readdirSync as
|
|
126161
|
+
readdirSync as readdirSync43,
|
|
125702
126162
|
renameSync as renameSync32,
|
|
125703
126163
|
rmSync as rmSync21,
|
|
125704
|
-
statSync as
|
|
126164
|
+
statSync as statSync62,
|
|
125705
126165
|
utimesSync,
|
|
125706
126166
|
writeFileSync as writeFileSync49
|
|
125707
126167
|
} from "node:fs";
|
|
125708
|
-
import { dirname as
|
|
126168
|
+
import { dirname as dirname49, join as join116, relative as relative7, resolve as resolve61 } from "node:path";
|
|
125709
126169
|
import { homedir as homedir60, tmpdir as tmpdir7 } from "node:os";
|
|
125710
126170
|
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
125711
126171
|
var PERSONAL_PREFIX = "personal-";
|
|
@@ -125714,10 +126174,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
125714
126174
|
var PERSONAL_SKILLS_SUBPATH2 = "personal-skills";
|
|
125715
126175
|
function resolveConfigSkillsDir(agent) {
|
|
125716
126176
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
125717
|
-
const candidate = override ? resolve61(override) :
|
|
126177
|
+
const candidate = override ? resolve61(override) : join116(homedir60(), ".switchroom-config");
|
|
125718
126178
|
if (!existsSync114(candidate))
|
|
125719
126179
|
return null;
|
|
125720
|
-
return
|
|
126180
|
+
return join116(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH2);
|
|
125721
126181
|
}
|
|
125722
126182
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
125723
126183
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
@@ -125725,7 +126185,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
125725
126185
|
if (!existsSync114(configSkillsRoot))
|
|
125726
126186
|
return;
|
|
125727
126187
|
const now2 = Date.now();
|
|
125728
|
-
for (const ent of
|
|
126188
|
+
for (const ent of readdirSync43(configSkillsRoot)) {
|
|
125729
126189
|
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
125730
126190
|
if (!m)
|
|
125731
126191
|
continue;
|
|
@@ -125735,7 +126195,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
125735
126195
|
if (now2 - ts < MIRROR_PRIOR_TTL_MS)
|
|
125736
126196
|
continue;
|
|
125737
126197
|
try {
|
|
125738
|
-
rmSync21(
|
|
126198
|
+
rmSync21(join116(configSkillsRoot, ent), { recursive: true, force: true });
|
|
125739
126199
|
} catch {}
|
|
125740
126200
|
}
|
|
125741
126201
|
} catch {}
|
|
@@ -125744,11 +126204,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125744
126204
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
125745
126205
|
if (!configSkillsRoot)
|
|
125746
126206
|
return;
|
|
125747
|
-
const dest =
|
|
126207
|
+
const dest = join116(configSkillsRoot, name);
|
|
125748
126208
|
try {
|
|
125749
126209
|
if (liveSkillDir !== null) {
|
|
125750
126210
|
try {
|
|
125751
|
-
const st =
|
|
126211
|
+
const st = lstatSync16(liveSkillDir);
|
|
125752
126212
|
if (st.isSymbolicLink()) {
|
|
125753
126213
|
process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
|
|
125754
126214
|
`));
|
|
@@ -125759,19 +126219,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125759
126219
|
if (liveSkillDir === null) {
|
|
125760
126220
|
sweepMirrorPriors(configSkillsRoot);
|
|
125761
126221
|
if (existsSync114(dest)) {
|
|
125762
|
-
const trash =
|
|
126222
|
+
const trash = join116(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
125763
126223
|
renameSync32(dest, trash);
|
|
125764
126224
|
}
|
|
125765
126225
|
return;
|
|
125766
126226
|
}
|
|
125767
126227
|
mkdirSync66(configSkillsRoot, { recursive: true, mode: 493 });
|
|
125768
126228
|
sweepMirrorPriors(configSkillsRoot);
|
|
125769
|
-
const staging = mkdtempSync6(
|
|
126229
|
+
const staging = mkdtempSync6(join116(configSkillsRoot, `.${name}-staging-`));
|
|
125770
126230
|
const walk2 = (src, dst) => {
|
|
125771
126231
|
mkdirSync66(dst, { recursive: true, mode: 493 });
|
|
125772
|
-
for (const ent of
|
|
125773
|
-
const s =
|
|
125774
|
-
const d =
|
|
126232
|
+
for (const ent of readdirSync43(src, { withFileTypes: true })) {
|
|
126233
|
+
const s = join116(src, ent.name);
|
|
126234
|
+
const d = join116(dst, ent.name);
|
|
125775
126235
|
if (ent.isSymbolicLink())
|
|
125776
126236
|
continue;
|
|
125777
126237
|
if (ent.isDirectory())
|
|
@@ -125784,7 +126244,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125784
126244
|
walk2(liveSkillDir, staging);
|
|
125785
126245
|
chmodSync19(staging, 493);
|
|
125786
126246
|
if (existsSync114(dest)) {
|
|
125787
|
-
const prior =
|
|
126247
|
+
const prior = join116(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
125788
126248
|
renameSync32(dest, prior);
|
|
125789
126249
|
}
|
|
125790
126250
|
renameSync32(staging, dest);
|
|
@@ -125814,20 +126274,20 @@ function resolveAgent(opts) {
|
|
|
125814
126274
|
function resolveAgentsRoot(opts) {
|
|
125815
126275
|
if (opts.root)
|
|
125816
126276
|
return resolve61(opts.root);
|
|
125817
|
-
return
|
|
126277
|
+
return join116(homedir60(), ".switchroom", "agents");
|
|
125818
126278
|
}
|
|
125819
126279
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
125820
|
-
return
|
|
126280
|
+
return join116(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
125821
126281
|
}
|
|
125822
126282
|
function trashDir(agentsRoot, agent) {
|
|
125823
|
-
return
|
|
126283
|
+
return join116(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
125824
126284
|
}
|
|
125825
126285
|
function countPersonalSkills(agentsRoot, agent) {
|
|
125826
|
-
const skillsDir =
|
|
126286
|
+
const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
|
|
125827
126287
|
if (!existsSync114(skillsDir))
|
|
125828
126288
|
return 0;
|
|
125829
126289
|
let n = 0;
|
|
125830
|
-
for (const ent of
|
|
126290
|
+
for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
|
|
125831
126291
|
if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
|
|
125832
126292
|
n += 1;
|
|
125833
126293
|
}
|
|
@@ -125855,13 +126315,13 @@ function readStdinSync2() {
|
|
|
125855
126315
|
}
|
|
125856
126316
|
function loadFromDir2(dir) {
|
|
125857
126317
|
const abs = resolve61(dir);
|
|
125858
|
-
if (!
|
|
126318
|
+
if (!statSync62(abs).isDirectory()) {
|
|
125859
126319
|
fail5(`--from path is not a directory: ${dir}`);
|
|
125860
126320
|
}
|
|
125861
126321
|
const files = {};
|
|
125862
126322
|
const walk2 = (sub) => {
|
|
125863
|
-
for (const ent of
|
|
125864
|
-
const full =
|
|
126323
|
+
for (const ent of readdirSync43(sub, { withFileTypes: true })) {
|
|
126324
|
+
const full = join116(sub, ent.name);
|
|
125865
126325
|
if (ent.isSymbolicLink()) {
|
|
125866
126326
|
fail5(`refusing to read symlink in --from dir: ${relative7(abs, full)}`);
|
|
125867
126327
|
}
|
|
@@ -125914,8 +126374,8 @@ function behavioralValidate(files) {
|
|
|
125914
126374
|
errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
125915
126375
|
}
|
|
125916
126376
|
} else if (PY_SCRIPT_RE.test(path10)) {
|
|
125917
|
-
const tmp = mkdtempSync6(
|
|
125918
|
-
const tmpPy =
|
|
126377
|
+
const tmp = mkdtempSync6(join116(tmpdir7(), "skill-personal-py-"));
|
|
126378
|
+
const tmpPy = join116(tmp, "check.py");
|
|
125919
126379
|
try {
|
|
125920
126380
|
writeFileSync49(tmpPy, content);
|
|
125921
126381
|
const r = spawnSync25("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -125936,12 +126396,12 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
125936
126396
|
if (!existsSync114(trash))
|
|
125937
126397
|
return;
|
|
125938
126398
|
const now2 = Date.now();
|
|
125939
|
-
for (const ent of
|
|
126399
|
+
for (const ent of readdirSync43(trash, { withFileTypes: true })) {
|
|
125940
126400
|
if (!ent.isDirectory())
|
|
125941
126401
|
continue;
|
|
125942
|
-
const entPath =
|
|
126402
|
+
const entPath = join116(trash, ent.name);
|
|
125943
126403
|
try {
|
|
125944
|
-
const st =
|
|
126404
|
+
const st = statSync62(entPath);
|
|
125945
126405
|
if (now2 - st.mtimeMs > TRASH_TTL_MS) {
|
|
125946
126406
|
rmSync21(entPath, { recursive: true, force: true });
|
|
125947
126407
|
}
|
|
@@ -125951,7 +126411,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
125951
126411
|
function writePersonalSkill(targetDir, files) {
|
|
125952
126412
|
let targetIsSymlink = false;
|
|
125953
126413
|
try {
|
|
125954
|
-
const st =
|
|
126414
|
+
const st = lstatSync16(targetDir);
|
|
125955
126415
|
if (st.isSymbolicLink()) {
|
|
125956
126416
|
targetIsSymlink = true;
|
|
125957
126417
|
}
|
|
@@ -125959,14 +126419,14 @@ function writePersonalSkill(targetDir, files) {
|
|
|
125959
126419
|
if (targetIsSymlink) {
|
|
125960
126420
|
fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
125961
126421
|
}
|
|
125962
|
-
mkdirSync66(
|
|
125963
|
-
const staging = mkdtempSync6(
|
|
126422
|
+
mkdirSync66(dirname49(targetDir), { recursive: true, mode: 493 });
|
|
126423
|
+
const staging = mkdtempSync6(join116(dirname49(targetDir), `.skill-personal-stage-`));
|
|
125964
126424
|
chmodSync19(staging, 493);
|
|
125965
126425
|
let oldRename = null;
|
|
125966
126426
|
try {
|
|
125967
126427
|
for (const [path10, content] of Object.entries(files)) {
|
|
125968
|
-
const full =
|
|
125969
|
-
mkdirSync66(
|
|
126428
|
+
const full = join116(staging, path10);
|
|
126429
|
+
mkdirSync66(dirname49(full), { recursive: true, mode: 493 });
|
|
125970
126430
|
const fd = openSync22(full, "wx");
|
|
125971
126431
|
try {
|
|
125972
126432
|
writeFileSync49(fd, content);
|
|
@@ -125980,7 +126440,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
125980
126440
|
}
|
|
125981
126441
|
let targetExists = false;
|
|
125982
126442
|
try {
|
|
125983
|
-
|
|
126443
|
+
lstatSync16(targetDir);
|
|
125984
126444
|
targetExists = true;
|
|
125985
126445
|
} catch {}
|
|
125986
126446
|
if (targetExists) {
|
|
@@ -126015,7 +126475,7 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
|
126015
126475
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
126016
126476
|
const exists = (() => {
|
|
126017
126477
|
try {
|
|
126018
|
-
|
|
126478
|
+
lstatSync16(target);
|
|
126019
126479
|
return true;
|
|
126020
126480
|
} catch {
|
|
126021
126481
|
return false;
|
|
@@ -126067,7 +126527,7 @@ function loadFiles(opts) {
|
|
|
126067
126527
|
if (!existsSync114(p)) {
|
|
126068
126528
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
126069
126529
|
}
|
|
126070
|
-
const st =
|
|
126530
|
+
const st = statSync62(p);
|
|
126071
126531
|
if (st.isDirectory()) {
|
|
126072
126532
|
return loadFromDir2(p);
|
|
126073
126533
|
}
|
|
@@ -126112,10 +126572,10 @@ function editPersonalAction(name, opts) {
|
|
|
126112
126572
|
}
|
|
126113
126573
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
126114
126574
|
function defaultSharedRoot() {
|
|
126115
|
-
return
|
|
126575
|
+
return join116(homedir60(), ".switchroom", "skills");
|
|
126116
126576
|
}
|
|
126117
126577
|
function defaultBundledRoot() {
|
|
126118
|
-
return
|
|
126578
|
+
return join116(homedir60(), ".switchroom", "skills", "_bundled");
|
|
126119
126579
|
}
|
|
126120
126580
|
function resolveCloneSource(source, opts) {
|
|
126121
126581
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -126125,11 +126585,11 @@ function resolveCloneSource(source, opts) {
|
|
|
126125
126585
|
const tier = m[1];
|
|
126126
126586
|
const slug = m[2];
|
|
126127
126587
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
126128
|
-
const dir =
|
|
126588
|
+
const dir = join116(root, slug);
|
|
126129
126589
|
if (!existsSync114(dir)) {
|
|
126130
126590
|
fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
126131
126591
|
}
|
|
126132
|
-
const st =
|
|
126592
|
+
const st = lstatSync16(dir);
|
|
126133
126593
|
if (st.isSymbolicLink()) {
|
|
126134
126594
|
fail5(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
|
|
126135
126595
|
}
|
|
@@ -126140,8 +126600,8 @@ function readSourceFiles(dir) {
|
|
|
126140
126600
|
const files = {};
|
|
126141
126601
|
const skipped = [];
|
|
126142
126602
|
const walk2 = (sub) => {
|
|
126143
|
-
for (const ent of
|
|
126144
|
-
const full =
|
|
126603
|
+
for (const ent of readdirSync43(sub, { withFileTypes: true })) {
|
|
126604
|
+
const full = join116(sub, ent.name);
|
|
126145
126605
|
if (ent.isSymbolicLink()) {
|
|
126146
126606
|
continue;
|
|
126147
126607
|
}
|
|
@@ -126156,7 +126616,7 @@ function readSourceFiles(dir) {
|
|
|
126156
126616
|
continue;
|
|
126157
126617
|
}
|
|
126158
126618
|
try {
|
|
126159
|
-
const st =
|
|
126619
|
+
const st = lstatSync16(full);
|
|
126160
126620
|
if (st.size > CLONE_MAX_FILE_BYTES) {
|
|
126161
126621
|
fail5(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
126162
126622
|
}
|
|
@@ -126238,7 +126698,7 @@ function removePersonalAction(name, opts) {
|
|
|
126238
126698
|
}
|
|
126239
126699
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
126240
126700
|
try {
|
|
126241
|
-
const st =
|
|
126701
|
+
const st = lstatSync16(target);
|
|
126242
126702
|
if (st.isSymbolicLink()) {
|
|
126243
126703
|
fail5(`refusing to remove symlink at ${target}; investigate manually`);
|
|
126244
126704
|
}
|
|
@@ -126252,7 +126712,7 @@ function removePersonalAction(name, opts) {
|
|
|
126252
126712
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
126253
126713
|
mkdirSync66(trashRoot2, { recursive: true, mode: 493 });
|
|
126254
126714
|
const ts = Date.now();
|
|
126255
|
-
const trashTarget =
|
|
126715
|
+
const trashTarget = join116(trashRoot2, `${name}-${ts}`);
|
|
126256
126716
|
renameSync32(target, trashTarget);
|
|
126257
126717
|
const now2 = new Date(ts);
|
|
126258
126718
|
utimesSync(trashTarget, now2, now2);
|
|
@@ -126271,27 +126731,27 @@ function listPersonalAction(opts) {
|
|
|
126271
126731
|
const agent = resolveAgent(opts);
|
|
126272
126732
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
126273
126733
|
sweepTrash(agentsRoot, agent);
|
|
126274
|
-
const skillsDir =
|
|
126734
|
+
const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
|
|
126275
126735
|
const personal = [];
|
|
126276
126736
|
if (existsSync114(skillsDir)) {
|
|
126277
|
-
for (const ent of
|
|
126737
|
+
for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
|
|
126278
126738
|
if (!ent.isDirectory())
|
|
126279
126739
|
continue;
|
|
126280
126740
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
126281
126741
|
continue;
|
|
126282
126742
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
126283
|
-
const skillPath =
|
|
126743
|
+
const skillPath = join116(skillsDir, ent.name);
|
|
126284
126744
|
let fileCount = 0;
|
|
126285
126745
|
let totalBytes = 0;
|
|
126286
126746
|
const walk2 = (sub) => {
|
|
126287
|
-
for (const e of
|
|
126747
|
+
for (const e of readdirSync43(sub, { withFileTypes: true })) {
|
|
126288
126748
|
if (e.isFile()) {
|
|
126289
126749
|
fileCount += 1;
|
|
126290
126750
|
try {
|
|
126291
|
-
totalBytes +=
|
|
126751
|
+
totalBytes += statSync62(join116(sub, e.name)).size;
|
|
126292
126752
|
} catch {}
|
|
126293
126753
|
} else if (e.isDirectory()) {
|
|
126294
|
-
walk2(
|
|
126754
|
+
walk2(join116(sub, e.name));
|
|
126295
126755
|
}
|
|
126296
126756
|
}
|
|
126297
126757
|
};
|
|
@@ -126330,7 +126790,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
126330
126790
|
// src/cli/self-improve-propose-skill.ts
|
|
126331
126791
|
import { createConnection as createConnection4 } from "node:net";
|
|
126332
126792
|
import { homedir as homedir61 } from "node:os";
|
|
126333
|
-
import { join as
|
|
126793
|
+
import { join as join117 } from "node:path";
|
|
126334
126794
|
import { readFileSync as readFileSync102 } from "node:fs";
|
|
126335
126795
|
|
|
126336
126796
|
// src/self-improve/skill-proposals.ts
|
|
@@ -126378,7 +126838,7 @@ var STOPWORDS = new Set([
|
|
|
126378
126838
|
// src/cli/self-improve-propose-skill.ts
|
|
126379
126839
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
126380
126840
|
function gatewaySocketPath() {
|
|
126381
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ?
|
|
126841
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join117(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join117(homedir61(), ".claude", "channels", "telegram", "gateway.sock"));
|
|
126382
126842
|
}
|
|
126383
126843
|
function fail6(msg, code = 1) {
|
|
126384
126844
|
console.error(msg);
|
|
@@ -126458,11 +126918,11 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
126458
126918
|
// src/cli/self-improve-eval-case.ts
|
|
126459
126919
|
import { createConnection as createConnection5 } from "node:net";
|
|
126460
126920
|
import { homedir as homedir62 } from "node:os";
|
|
126461
|
-
import { join as
|
|
126921
|
+
import { join as join121, resolve as resolve62, sep as sep7 } from "node:path";
|
|
126462
126922
|
import { existsSync as existsSync119, readFileSync as readFileSync107, realpathSync as realpathSync10 } from "node:fs";
|
|
126463
126923
|
|
|
126464
126924
|
// src/self-improve/apply-guard.ts
|
|
126465
|
-
import { existsSync as existsSync117, lstatSync as
|
|
126925
|
+
import { existsSync as existsSync117, lstatSync as lstatSync17, readFileSync as readFileSync105, statSync as statSync64 } from "node:fs";
|
|
126466
126926
|
|
|
126467
126927
|
// src/self-improve/config.ts
|
|
126468
126928
|
function intEnv(name, def) {
|
|
@@ -126483,11 +126943,11 @@ function resolveSelfImproveConfig() {
|
|
|
126483
126943
|
}
|
|
126484
126944
|
|
|
126485
126945
|
// src/self-improve/eval-gate.ts
|
|
126486
|
-
import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as
|
|
126487
|
-
import { join as
|
|
126946
|
+
import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as readdirSync44, statSync as statSync63 } from "node:fs";
|
|
126947
|
+
import { join as join118 } from "node:path";
|
|
126488
126948
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
126489
126949
|
function evalsJsonPath(skillDir) {
|
|
126490
|
-
return
|
|
126950
|
+
return join118(skillDir, "evals", "evals.json");
|
|
126491
126951
|
}
|
|
126492
126952
|
function skillHasEvals(skillDir) {
|
|
126493
126953
|
const p = evalsJsonPath(skillDir);
|
|
@@ -126501,7 +126961,7 @@ function skillHasEvals(skillDir) {
|
|
|
126501
126961
|
}
|
|
126502
126962
|
}
|
|
126503
126963
|
function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWITCHROOM_PYTHON ?? "python3") {
|
|
126504
|
-
const script =
|
|
126964
|
+
const script = join118(repoRoot, "skills", "skill-creator", "scripts", "aggregate_benchmark.py");
|
|
126505
126965
|
if (!existsSync115(script)) {
|
|
126506
126966
|
return { ok: false, error: `aggregate_benchmark.py not found at ${script}` };
|
|
126507
126967
|
}
|
|
@@ -126515,25 +126975,25 @@ function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWIT
|
|
|
126515
126975
|
error: `aggregate_benchmark exited ${r.status}: ${(r.stderr ?? "").trim()}`
|
|
126516
126976
|
};
|
|
126517
126977
|
}
|
|
126518
|
-
return { ok: true, benchmarkJson:
|
|
126978
|
+
return { ok: true, benchmarkJson: join118(benchmarkDir, "benchmark.json") };
|
|
126519
126979
|
}
|
|
126520
126980
|
function isBenchmarkDir(dir) {
|
|
126521
126981
|
try {
|
|
126522
|
-
if (!
|
|
126982
|
+
if (!statSync63(dir).isDirectory())
|
|
126523
126983
|
return false;
|
|
126524
126984
|
} catch {
|
|
126525
126985
|
return false;
|
|
126526
126986
|
}
|
|
126527
126987
|
const hasEvalDirs = (d) => {
|
|
126528
126988
|
try {
|
|
126529
|
-
return
|
|
126989
|
+
return readdirSync44(d, { withFileTypes: true }).some((e) => e.isDirectory() && e.name.startsWith("eval-"));
|
|
126530
126990
|
} catch {
|
|
126531
126991
|
return false;
|
|
126532
126992
|
}
|
|
126533
126993
|
};
|
|
126534
126994
|
if (hasEvalDirs(dir))
|
|
126535
126995
|
return true;
|
|
126536
|
-
const runs =
|
|
126996
|
+
const runs = join118(dir, "runs");
|
|
126537
126997
|
return existsSync115(runs) && hasEvalDirs(runs);
|
|
126538
126998
|
}
|
|
126539
126999
|
function resolveBenchmarkDir(baseDir, explicit) {
|
|
@@ -126544,13 +127004,13 @@ function resolveBenchmarkDir(baseDir, explicit) {
|
|
|
126544
127004
|
return baseDir;
|
|
126545
127005
|
let entries;
|
|
126546
127006
|
try {
|
|
126547
|
-
entries =
|
|
127007
|
+
entries = readdirSync44(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join118(baseDir, e.name)).filter(isBenchmarkDir);
|
|
126548
127008
|
} catch {
|
|
126549
127009
|
return null;
|
|
126550
127010
|
}
|
|
126551
127011
|
if (entries.length === 0)
|
|
126552
127012
|
return null;
|
|
126553
|
-
entries.sort((a, b) =>
|
|
127013
|
+
entries.sort((a, b) => statSync63(b).mtimeMs - statSync63(a).mtimeMs);
|
|
126554
127014
|
return entries[0];
|
|
126555
127015
|
}
|
|
126556
127016
|
var CANDIDATE_CONFIGS = ["with_skill", "candidate", "new_skill", "after"];
|
|
@@ -126617,12 +127077,12 @@ import {
|
|
|
126617
127077
|
mkdirSync as mkdirSync67,
|
|
126618
127078
|
openSync as openSync23,
|
|
126619
127079
|
readFileSync as readFileSync104,
|
|
126620
|
-
readdirSync as
|
|
127080
|
+
readdirSync as readdirSync45,
|
|
126621
127081
|
renameSync as renameSync33,
|
|
126622
127082
|
writeFileSync as writeFileSync50,
|
|
126623
127083
|
writeSync as writeSync12
|
|
126624
127084
|
} from "node:fs";
|
|
126625
|
-
import { dirname as
|
|
127085
|
+
import { dirname as dirname50, join as join119 } from "node:path";
|
|
126626
127086
|
import { createHash as createHash21 } from "node:crypto";
|
|
126627
127087
|
|
|
126628
127088
|
// src/self-improve/pii-scan.ts
|
|
@@ -126773,7 +127233,7 @@ function readEvalsDoc(skillDir) {
|
|
|
126773
127233
|
}
|
|
126774
127234
|
}
|
|
126775
127235
|
function writeAtomic(path10, content) {
|
|
126776
|
-
mkdirSync67(
|
|
127236
|
+
mkdirSync67(dirname50(path10), { recursive: true });
|
|
126777
127237
|
const tmp = `${path10}.tmp-${process.pid}-${Date.now()}`;
|
|
126778
127238
|
writeFileSync50(tmp, content, { encoding: "utf-8", mode: 420 });
|
|
126779
127239
|
renameSync33(tmp, path10);
|
|
@@ -126812,7 +127272,7 @@ function appendEvalCase(skillDir, slug, input) {
|
|
|
126812
127272
|
}
|
|
126813
127273
|
var HELD_OUT_FILE = "self-improve-held-out-evals.jsonl";
|
|
126814
127274
|
function heldOutPath(stateDir) {
|
|
126815
|
-
return
|
|
127275
|
+
return join119(stateDir, HELD_OUT_FILE);
|
|
126816
127276
|
}
|
|
126817
127277
|
function heldOutHasFingerprint(stateDir, slug, fp) {
|
|
126818
127278
|
const path10 = heldOutPath(stateDir);
|
|
@@ -126861,10 +127321,10 @@ function appendHeldOutCase(stateDir, slug, ec, opts = {}) {
|
|
|
126861
127321
|
var EVAL_MANIFEST_FILE = "self-improve-eval-manifest.json";
|
|
126862
127322
|
var EVAL_BASELINE_SUBDIR = "self-improve-eval-baselines";
|
|
126863
127323
|
function manifestPath(stateDir) {
|
|
126864
|
-
return
|
|
127324
|
+
return join119(stateDir, EVAL_MANIFEST_FILE);
|
|
126865
127325
|
}
|
|
126866
127326
|
function baselineEvalsPath(stateDir, slug) {
|
|
126867
|
-
return
|
|
127327
|
+
return join119(stateDir, EVAL_BASELINE_SUBDIR, slug, "evals.json");
|
|
126868
127328
|
}
|
|
126869
127329
|
function evalsSha256(skillDir) {
|
|
126870
127330
|
const p = evalsJsonPath(skillDir);
|
|
@@ -126900,7 +127360,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
|
|
|
126900
127360
|
m[slug] = { sha256: sha, updated_at: new Date(now2()).toISOString(), skill_dir: skillDir };
|
|
126901
127361
|
writeManifest(stateDir, m);
|
|
126902
127362
|
const snap = baselineEvalsPath(stateDir, slug);
|
|
126903
|
-
mkdirSync67(
|
|
127363
|
+
mkdirSync67(dirname50(snap), { recursive: true });
|
|
126904
127364
|
copyFileSync15(evalsJsonPath(skillDir), snap);
|
|
126905
127365
|
}
|
|
126906
127366
|
|
|
@@ -126908,7 +127368,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
|
|
|
126908
127368
|
var BENCHMARK_SUBDIR = "self-improve-benchmarks";
|
|
126909
127369
|
function ownsSkill(skillDir) {
|
|
126910
127370
|
try {
|
|
126911
|
-
const st =
|
|
127371
|
+
const st = lstatSync17(skillDir);
|
|
126912
127372
|
if (st.isSymbolicLink())
|
|
126913
127373
|
return false;
|
|
126914
127374
|
return st.isDirectory();
|
|
@@ -126926,10 +127386,10 @@ import {
|
|
|
126926
127386
|
readFileSync as readFileSync106,
|
|
126927
127387
|
writeSync as writeSync13
|
|
126928
127388
|
} from "node:fs";
|
|
126929
|
-
import { join as
|
|
127389
|
+
import { join as join120 } from "node:path";
|
|
126930
127390
|
var EVAL_CASE_PROPOSALS_FILE = "eval-case-proposals.jsonl";
|
|
126931
127391
|
function proposalsPath(stateDir) {
|
|
126932
|
-
return
|
|
127392
|
+
return join120(stateDir, EVAL_CASE_PROPOSALS_FILE);
|
|
126933
127393
|
}
|
|
126934
127394
|
function isRecord2(v) {
|
|
126935
127395
|
return !!v && typeof v === "object" && typeof v.id === "string" && typeof v.skill_slug === "string" && !!v.case && typeof v.case === "object";
|
|
@@ -126964,16 +127424,16 @@ function getEvalCaseProposal(stateDir, id) {
|
|
|
126964
127424
|
// src/cli/self-improve-eval-case.ts
|
|
126965
127425
|
var IPC_CONNECT_TIMEOUT_MS2 = 5000;
|
|
126966
127426
|
function stateDir() {
|
|
126967
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
127427
|
+
return process.env.TELEGRAM_STATE_DIR ?? join121(homedir62(), ".claude", "channels", "telegram");
|
|
126968
127428
|
}
|
|
126969
127429
|
function gatewaySocketPath2() {
|
|
126970
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
127430
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? join121(stateDir(), "gateway.sock");
|
|
126971
127431
|
}
|
|
126972
127432
|
function skillsRoot() {
|
|
126973
|
-
return
|
|
127433
|
+
return join121(homedir62(), ".claude", "skills");
|
|
126974
127434
|
}
|
|
126975
127435
|
function skillDirFor(slug) {
|
|
126976
|
-
return
|
|
127436
|
+
return join121(skillsRoot(), slug);
|
|
126977
127437
|
}
|
|
126978
127438
|
function authorizeApplyTarget(persistedDir, slug) {
|
|
126979
127439
|
const dir = skillDirFor(slug);
|
|
@@ -127171,20 +127631,20 @@ function registerSelfImproveEvalCaseCommands(program3) {
|
|
|
127171
127631
|
|
|
127172
127632
|
// src/cli/self-improve-bench.ts
|
|
127173
127633
|
import { homedir as homedir63 } from "node:os";
|
|
127174
|
-
import { dirname as
|
|
127634
|
+
import { dirname as dirname51, join as join122 } from "node:path";
|
|
127175
127635
|
init_shipped_assets();
|
|
127176
127636
|
function fail8(msg, code = 1) {
|
|
127177
127637
|
console.error(msg);
|
|
127178
127638
|
process.exit(code);
|
|
127179
127639
|
}
|
|
127180
127640
|
function stateDir2() {
|
|
127181
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
127641
|
+
return process.env.TELEGRAM_STATE_DIR ?? join122(homedir63(), ".claude", "channels", "telegram");
|
|
127182
127642
|
}
|
|
127183
127643
|
function skillDirFor2(slug) {
|
|
127184
|
-
return
|
|
127644
|
+
return join122(homedir63(), ".claude", "skills", slug);
|
|
127185
127645
|
}
|
|
127186
127646
|
function benchmarkBaseDir(slug) {
|
|
127187
|
-
return
|
|
127647
|
+
return join122(stateDir2(), BENCHMARK_SUBDIR, slug);
|
|
127188
127648
|
}
|
|
127189
127649
|
function resolveAggregatorRepoRoot() {
|
|
127190
127650
|
const res = resolveShippedAsset(SKILLS_ASSET, {
|
|
@@ -127196,7 +127656,7 @@ function resolveAggregatorRepoRoot() {
|
|
|
127196
127656
|
`);
|
|
127197
127657
|
return null;
|
|
127198
127658
|
}
|
|
127199
|
-
return
|
|
127659
|
+
return dirname51(res.path);
|
|
127200
127660
|
}
|
|
127201
127661
|
function registerSelfImproveBenchCommand(program3) {
|
|
127202
127662
|
const parent = program3.commands.find((c) => c.name() === "self-improve") ?? program3.command("self-improve").description("Agent self-improvement ops");
|
|
@@ -127246,9 +127706,9 @@ function registerSelfImproveBenchCommand(program3) {
|
|
|
127246
127706
|
init_esm();
|
|
127247
127707
|
init_helpers();
|
|
127248
127708
|
var import_yaml26 = __toESM(require_dist(), 1);
|
|
127249
|
-
import { existsSync as existsSync120, readdirSync as
|
|
127709
|
+
import { existsSync as existsSync120, readdirSync as readdirSync46, readFileSync as readFileSync108, statSync as statSync65 } from "node:fs";
|
|
127250
127710
|
import { homedir as homedir64 } from "node:os";
|
|
127251
|
-
import { join as
|
|
127711
|
+
import { join as join123, resolve as resolve63 } from "node:path";
|
|
127252
127712
|
var PERSONAL_PREFIX2 = "personal-";
|
|
127253
127713
|
var BUNDLED_SUBDIR = "_bundled";
|
|
127254
127714
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
@@ -127262,7 +127722,7 @@ function defaultBundledRoot2() {
|
|
|
127262
127722
|
return resolve63(homedir64(), ".switchroom/skills/_bundled");
|
|
127263
127723
|
}
|
|
127264
127724
|
function readSkillFrontmatter(skillDir) {
|
|
127265
|
-
const mdPath =
|
|
127725
|
+
const mdPath = join123(skillDir, "SKILL.md");
|
|
127266
127726
|
if (!existsSync120(mdPath))
|
|
127267
127727
|
return null;
|
|
127268
127728
|
let content;
|
|
@@ -127295,9 +127755,9 @@ function readSkillFrontmatter(skillDir) {
|
|
|
127295
127755
|
return { fm: parsed };
|
|
127296
127756
|
}
|
|
127297
127757
|
function statSkillMd(skillDir) {
|
|
127298
|
-
const mdPath =
|
|
127758
|
+
const mdPath = join123(skillDir, "SKILL.md");
|
|
127299
127759
|
try {
|
|
127300
|
-
const st =
|
|
127760
|
+
const st = statSync65(mdPath);
|
|
127301
127761
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
127302
127762
|
} catch {
|
|
127303
127763
|
return null;
|
|
@@ -127306,22 +127766,22 @@ function statSkillMd(skillDir) {
|
|
|
127306
127766
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
127307
127767
|
if (!AGENT_NAME_RE3.test(agent))
|
|
127308
127768
|
return [];
|
|
127309
|
-
const skillsDir =
|
|
127769
|
+
const skillsDir = join123(agentsRoot, agent, ".claude/skills");
|
|
127310
127770
|
if (!existsSync120(skillsDir))
|
|
127311
127771
|
return [];
|
|
127312
127772
|
const out = [];
|
|
127313
127773
|
let entries;
|
|
127314
127774
|
try {
|
|
127315
|
-
entries =
|
|
127775
|
+
entries = readdirSync46(skillsDir);
|
|
127316
127776
|
} catch {
|
|
127317
127777
|
return [];
|
|
127318
127778
|
}
|
|
127319
127779
|
for (const ent of entries) {
|
|
127320
127780
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
127321
127781
|
continue;
|
|
127322
|
-
const dirPath =
|
|
127782
|
+
const dirPath = join123(skillsDir, ent);
|
|
127323
127783
|
try {
|
|
127324
|
-
if (!
|
|
127784
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127325
127785
|
continue;
|
|
127326
127786
|
} catch {
|
|
127327
127787
|
continue;
|
|
@@ -127350,7 +127810,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
127350
127810
|
const out = [];
|
|
127351
127811
|
let entries;
|
|
127352
127812
|
try {
|
|
127353
|
-
entries =
|
|
127813
|
+
entries = readdirSync46(sharedRoot);
|
|
127354
127814
|
} catch {
|
|
127355
127815
|
return [];
|
|
127356
127816
|
}
|
|
@@ -127359,9 +127819,9 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
127359
127819
|
continue;
|
|
127360
127820
|
if (ent.startsWith("."))
|
|
127361
127821
|
continue;
|
|
127362
|
-
const dirPath =
|
|
127822
|
+
const dirPath = join123(sharedRoot, ent);
|
|
127363
127823
|
try {
|
|
127364
|
-
if (!
|
|
127824
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127365
127825
|
continue;
|
|
127366
127826
|
} catch {
|
|
127367
127827
|
continue;
|
|
@@ -127388,16 +127848,16 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
127388
127848
|
const out = [];
|
|
127389
127849
|
let entries;
|
|
127390
127850
|
try {
|
|
127391
|
-
entries =
|
|
127851
|
+
entries = readdirSync46(bundledRoot);
|
|
127392
127852
|
} catch {
|
|
127393
127853
|
return [];
|
|
127394
127854
|
}
|
|
127395
127855
|
for (const ent of entries) {
|
|
127396
127856
|
if (ent.startsWith("."))
|
|
127397
127857
|
continue;
|
|
127398
|
-
const dirPath =
|
|
127858
|
+
const dirPath = join123(bundledRoot, ent);
|
|
127399
127859
|
try {
|
|
127400
|
-
if (!
|
|
127860
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127401
127861
|
continue;
|
|
127402
127862
|
} catch {
|
|
127403
127863
|
continue;
|
|
@@ -127544,15 +128004,15 @@ init_operator_uid();
|
|
|
127544
128004
|
import {
|
|
127545
128005
|
existsSync as existsSync122,
|
|
127546
128006
|
mkdirSync as mkdirSync69,
|
|
127547
|
-
readdirSync as
|
|
128007
|
+
readdirSync as readdirSync47,
|
|
127548
128008
|
writeFileSync as writeFileSync51,
|
|
127549
|
-
statSync as
|
|
127550
|
-
lstatSync as
|
|
128009
|
+
statSync as statSync66,
|
|
128010
|
+
lstatSync as lstatSync18,
|
|
127551
128011
|
realpathSync as realpathSync11,
|
|
127552
128012
|
copyFileSync as copyFileSync16
|
|
127553
128013
|
} from "node:fs";
|
|
127554
128014
|
import { homedir as homedir65 } from "node:os";
|
|
127555
|
-
import { join as
|
|
128015
|
+
import { join as join124 } from "node:path";
|
|
127556
128016
|
import { spawnSync as spawnSync29 } from "node:child_process";
|
|
127557
128017
|
|
|
127558
128018
|
// src/cli/singleton-stale-cleanup.ts
|
|
@@ -127908,10 +128368,10 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir65()) {
|
|
|
127908
128368
|
return resolved;
|
|
127909
128369
|
}
|
|
127910
128370
|
function resolveHostdSkillsTarget(hostHome) {
|
|
127911
|
-
const skillsPath =
|
|
128371
|
+
const skillsPath = join124(hostHome, ".switchroom", "skills");
|
|
127912
128372
|
let st;
|
|
127913
128373
|
try {
|
|
127914
|
-
st =
|
|
128374
|
+
st = lstatSync18(skillsPath);
|
|
127915
128375
|
} catch {
|
|
127916
128376
|
return;
|
|
127917
128377
|
}
|
|
@@ -127932,10 +128392,10 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
127932
128392
|
return target;
|
|
127933
128393
|
}
|
|
127934
128394
|
function hostdDir() {
|
|
127935
|
-
return
|
|
128395
|
+
return join124(homedir65(), ".switchroom", "hostd");
|
|
127936
128396
|
}
|
|
127937
128397
|
function hostdComposePath() {
|
|
127938
|
-
return
|
|
128398
|
+
return join124(hostdDir(), "docker-compose.yml");
|
|
127939
128399
|
}
|
|
127940
128400
|
function backupExistingCompose() {
|
|
127941
128401
|
const p = hostdComposePath();
|
|
@@ -128062,12 +128522,12 @@ function doStatus() {
|
|
|
128062
128522
|
if (existsSync122(dir)) {
|
|
128063
128523
|
const entries = [];
|
|
128064
128524
|
try {
|
|
128065
|
-
for (const name of
|
|
128525
|
+
for (const name of readdirSync47(dir)) {
|
|
128066
128526
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
128067
128527
|
continue;
|
|
128068
|
-
const sockPath =
|
|
128528
|
+
const sockPath = join124(dir, name, "sock");
|
|
128069
128529
|
if (existsSync122(sockPath)) {
|
|
128070
|
-
const st =
|
|
128530
|
+
const st = statSync66(sockPath);
|
|
128071
128531
|
if ((st.mode & 61440) === 49152) {
|
|
128072
128532
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
128073
128533
|
}
|
|
@@ -128160,7 +128620,7 @@ init_operator_uid();
|
|
|
128160
128620
|
init_compose();
|
|
128161
128621
|
import { chownSync as chownSync14, existsSync as existsSync123, mkdirSync as mkdirSync70, writeFileSync as writeFileSync52, copyFileSync as copyFileSync17 } from "node:fs";
|
|
128162
128622
|
import { homedir as homedir66 } from "node:os";
|
|
128163
|
-
import { join as
|
|
128623
|
+
import { join as join125 } from "node:path";
|
|
128164
128624
|
import { spawnSync as spawnSync30 } from "node:child_process";
|
|
128165
128625
|
function resolveWebImageTag(explicitTag, release) {
|
|
128166
128626
|
if (explicitTag)
|
|
@@ -128262,10 +128722,10 @@ services:
|
|
|
128262
128722
|
`;
|
|
128263
128723
|
}
|
|
128264
128724
|
function webdDir() {
|
|
128265
|
-
return
|
|
128725
|
+
return join125(homedir66(), ".switchroom", "web");
|
|
128266
128726
|
}
|
|
128267
128727
|
function webdComposePath() {
|
|
128268
|
-
return
|
|
128728
|
+
return join125(webdDir(), "docker-compose.yml");
|
|
128269
128729
|
}
|
|
128270
128730
|
function backupExistingCompose2() {
|
|
128271
128731
|
const p = webdComposePath();
|
|
@@ -128431,9 +128891,9 @@ function registerWebdCommand(program3) {
|
|
|
128431
128891
|
// src/cli/host-repair.ts
|
|
128432
128892
|
init_source();
|
|
128433
128893
|
import { homedir as homedir67 } from "node:os";
|
|
128434
|
-
import { join as
|
|
128894
|
+
import { join as join126 } from "node:path";
|
|
128435
128895
|
var ARTIFACT_ALLOWLIST = {
|
|
128436
|
-
dockerComposePluginDir: (home2) =>
|
|
128896
|
+
dockerComposePluginDir: (home2) => join126(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
128437
128897
|
stateSentinel: "/state"
|
|
128438
128898
|
};
|
|
128439
128899
|
function isStateBogusAutoDir(probe2) {
|
|
@@ -128587,18 +129047,18 @@ function applyMountRepairs(items, deps) {
|
|
|
128587
129047
|
function registerHostCommand(program3) {
|
|
128588
129048
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
128589
129049
|
host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
|
|
128590
|
-
const { rmdirSync: rmdirSync2, rmSync: rmSync22, lstatSync:
|
|
129050
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync22, lstatSync: lstatSync19, readdirSync: readdirSync48 } = await import("node:fs");
|
|
128591
129051
|
const probe2 = {
|
|
128592
129052
|
lstat(path10) {
|
|
128593
129053
|
try {
|
|
128594
|
-
return
|
|
129054
|
+
return lstatSync19(path10);
|
|
128595
129055
|
} catch {
|
|
128596
129056
|
return null;
|
|
128597
129057
|
}
|
|
128598
129058
|
},
|
|
128599
129059
|
readdir(path10) {
|
|
128600
129060
|
try {
|
|
128601
|
-
return
|
|
129061
|
+
return readdirSync48(path10);
|
|
128602
129062
|
} catch {
|
|
128603
129063
|
return null;
|
|
128604
129064
|
}
|
|
@@ -128887,9 +129347,9 @@ function printDeepDiveBrief(targets) {
|
|
|
128887
129347
|
|
|
128888
129348
|
// src/cli/hindsight-watch.ts
|
|
128889
129349
|
init_source();
|
|
128890
|
-
import { existsSync as existsSync128, readdirSync as
|
|
129350
|
+
import { existsSync as existsSync128, readdirSync as readdirSync52 } from "node:fs";
|
|
128891
129351
|
import { homedir as homedir70 } from "node:os";
|
|
128892
|
-
import { join as
|
|
129352
|
+
import { join as join129, resolve as resolve66 } from "node:path";
|
|
128893
129353
|
|
|
128894
129354
|
// src/host-control/config-degraded.ts
|
|
128895
129355
|
import { connect as connect3 } from "node:net";
|
|
@@ -128958,7 +129418,7 @@ init_install_cron();
|
|
|
128958
129418
|
|
|
128959
129419
|
// src/hindsight-watch/probe.ts
|
|
128960
129420
|
import { spawnSync as spawnSync32 } from "node:child_process";
|
|
128961
|
-
import { readFileSync as readFileSync113, readdirSync as
|
|
129421
|
+
import { readFileSync as readFileSync113, readdirSync as readdirSync51, statSync as statSync68 } from "node:fs";
|
|
128962
129422
|
import { homedir as homedir69 } from "node:os";
|
|
128963
129423
|
import { resolve as resolve65 } from "node:path";
|
|
128964
129424
|
|
|
@@ -129086,13 +129546,13 @@ function readLlmSignals(series) {
|
|
|
129086
129546
|
}
|
|
129087
129547
|
|
|
129088
129548
|
// src/hindsight-watch/recall-log.ts
|
|
129089
|
-
import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as
|
|
129090
|
-
import { join as
|
|
129549
|
+
import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as readdirSync50, readSync as readSync6, statSync as statSync67 } from "node:fs";
|
|
129550
|
+
import { join as join128 } from "node:path";
|
|
129091
129551
|
var RECALL_WINDOW_ROWS = 200;
|
|
129092
129552
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
129093
129553
|
var RECALL_MAX_ROW_AGE_MS = 24 * 60 * 60 * 1000;
|
|
129094
129554
|
function recallLogPath2(agentDir) {
|
|
129095
|
-
return
|
|
129555
|
+
return join128(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
129096
129556
|
}
|
|
129097
129557
|
function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
129098
129558
|
if (!existsSync127(path10))
|
|
@@ -129101,7 +129561,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
129101
129561
|
let truncatedHead = false;
|
|
129102
129562
|
let fd;
|
|
129103
129563
|
try {
|
|
129104
|
-
const size =
|
|
129564
|
+
const size = statSync67(path10).size;
|
|
129105
129565
|
const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
|
|
129106
129566
|
truncatedHead = start > 0;
|
|
129107
129567
|
const length = size - start;
|
|
@@ -129236,11 +129696,11 @@ function summarizeRecallRows2(rows) {
|
|
|
129236
129696
|
return out;
|
|
129237
129697
|
}
|
|
129238
129698
|
function probeRecallLogs(agentsDir, now2, windowRows = RECALL_WINDOW_ROWS, maxAgeMs = RECALL_MAX_ROW_AGE_MS) {
|
|
129239
|
-
const names =
|
|
129699
|
+
const names = readdirSync50(agentsDir);
|
|
129240
129700
|
const all = [];
|
|
129241
129701
|
let agents = 0;
|
|
129242
129702
|
for (const name of names) {
|
|
129243
|
-
const rows = withinWindow(readRecallLogTail2(recallLogPath2(
|
|
129703
|
+
const rows = withinWindow(readRecallLogTail2(recallLogPath2(join128(agentsDir, name)), windowRows), now2, maxAgeMs);
|
|
129244
129704
|
if (rows.length > 0)
|
|
129245
129705
|
agents++;
|
|
129246
129706
|
all.push(...rows);
|
|
@@ -129333,7 +129793,7 @@ function readDropCount(hindsightDir) {
|
|
|
129333
129793
|
const path10 = resolve65(hindsightDir, "pending-drops.json");
|
|
129334
129794
|
let parsed;
|
|
129335
129795
|
try {
|
|
129336
|
-
if (
|
|
129796
|
+
if (statSync68(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
129337
129797
|
return 0;
|
|
129338
129798
|
parsed = JSON.parse(readFileSync113(path10, "utf8"));
|
|
129339
129799
|
} catch {
|
|
@@ -129350,7 +129810,7 @@ function resolveAgentsDir2(explicit) {
|
|
|
129350
129810
|
function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
129351
129811
|
let names;
|
|
129352
129812
|
try {
|
|
129353
|
-
names =
|
|
129813
|
+
names = readdirSync51(agentsDir);
|
|
129354
129814
|
} catch (e) {
|
|
129355
129815
|
throw new ProbeError(`reading ${agentsDir}: ${e.message}`, "spool");
|
|
129356
129816
|
}
|
|
@@ -129364,7 +129824,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
129364
129824
|
const hindsightDir = resolve65(agentsDir, name, "home", ".hindsight");
|
|
129365
129825
|
let entries = null;
|
|
129366
129826
|
try {
|
|
129367
|
-
entries =
|
|
129827
|
+
entries = readdirSync51(resolve65(hindsightDir, "pending-retains"));
|
|
129368
129828
|
} catch {}
|
|
129369
129829
|
if (entries !== null) {
|
|
129370
129830
|
agents++;
|
|
@@ -129376,19 +129836,19 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
129376
129836
|
}
|
|
129377
129837
|
}
|
|
129378
129838
|
try {
|
|
129379
|
-
for (const f of
|
|
129839
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-evicted"))) {
|
|
129380
129840
|
if (f.endsWith(".json"))
|
|
129381
129841
|
evicted++;
|
|
129382
129842
|
}
|
|
129383
129843
|
} catch {}
|
|
129384
129844
|
try {
|
|
129385
|
-
for (const f of
|
|
129845
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-dead"))) {
|
|
129386
129846
|
if (f.endsWith(".dead"))
|
|
129387
129847
|
dead++;
|
|
129388
129848
|
}
|
|
129389
129849
|
} catch {}
|
|
129390
129850
|
try {
|
|
129391
|
-
for (const f of
|
|
129851
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-duplicate"))) {
|
|
129392
129852
|
if (f.endsWith(".json"))
|
|
129393
129853
|
duplicates++;
|
|
129394
129854
|
}
|
|
@@ -130298,7 +130758,7 @@ function registerHindsightWatchCommand(program3) {
|
|
|
130298
130758
|
process.exitCode = runInstallCron(opts.cronUser);
|
|
130299
130759
|
return;
|
|
130300
130760
|
}
|
|
130301
|
-
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ??
|
|
130761
|
+
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join129(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir70(), ".switchroom", "agents");
|
|
130302
130762
|
const statePath = opts.state ?? defaultStatePath2();
|
|
130303
130763
|
const log = (m) => {
|
|
130304
130764
|
process.stderr.write(`${m}
|
|
@@ -130412,7 +130872,7 @@ function orderRelayCandidates(candidates, preferred = PREFERRED_RELAY_AGENT) {
|
|
|
130412
130872
|
}
|
|
130413
130873
|
function collectRelayCandidates(agentsDir) {
|
|
130414
130874
|
const candidates = [];
|
|
130415
|
-
for (const name of
|
|
130875
|
+
for (const name of readdirSync52(agentsDir).sort()) {
|
|
130416
130876
|
const sock = resolve66(agentsDir, name, "telegram", "gateway.sock");
|
|
130417
130877
|
if (existsSync128(sock))
|
|
130418
130878
|
candidates.push({ agent: name, sock });
|
|
@@ -130437,7 +130897,7 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
130437
130897
|
// src/cli/hindsight-bench.ts
|
|
130438
130898
|
init_source();
|
|
130439
130899
|
import { mkdirSync as mkdirSync72, readFileSync as readFileSync114, writeFileSync as writeFileSync54 } from "node:fs";
|
|
130440
|
-
import { dirname as
|
|
130900
|
+
import { dirname as dirname54, resolve as resolve67 } from "node:path";
|
|
130441
130901
|
|
|
130442
130902
|
// src/hindsight-bench/anonymise.ts
|
|
130443
130903
|
function pseudonym(n, width) {
|
|
@@ -131565,7 +132025,7 @@ function readResult(path10) {
|
|
|
131565
132025
|
}
|
|
131566
132026
|
function writeOut(path10, body) {
|
|
131567
132027
|
const abs = resolve67(path10);
|
|
131568
|
-
mkdirSync72(
|
|
132028
|
+
mkdirSync72(dirname54(abs), { recursive: true });
|
|
131569
132029
|
writeFileSync54(abs, body);
|
|
131570
132030
|
process.stderr.write(`${source_default.green("\u2713")} wrote ${abs}
|
|
131571
132031
|
`);
|
|
@@ -131770,13 +132230,13 @@ async function runMeasureMode(opts) {
|
|
|
131770
132230
|
|
|
131771
132231
|
// src/cli/openrouter-watch.ts
|
|
131772
132232
|
init_source();
|
|
131773
|
-
import { existsSync as existsSync130, readdirSync as
|
|
132233
|
+
import { existsSync as existsSync130, readdirSync as readdirSync53 } from "node:fs";
|
|
131774
132234
|
import { homedir as homedir72 } from "node:os";
|
|
131775
|
-
import { join as
|
|
132235
|
+
import { join as join130, resolve as resolve69 } from "node:path";
|
|
131776
132236
|
|
|
131777
132237
|
// src/openrouter/install-cron.ts
|
|
131778
132238
|
import { existsSync as existsSync129, mkdirSync as mkdirSync73, readFileSync as readFileSync115, renameSync as renameSync34, writeFileSync as writeFileSync55 } from "node:fs";
|
|
131779
|
-
import { dirname as
|
|
132239
|
+
import { dirname as dirname55 } from "node:path";
|
|
131780
132240
|
var CRON_PATH3 = "/etc/cron.d/openrouter-watch";
|
|
131781
132241
|
var CRON_SCHEDULE2 = "17 * * * *";
|
|
131782
132242
|
var CRON_LOG_PATH3 = "/var/log/openrouter-watch.log";
|
|
@@ -131800,7 +132260,7 @@ function installCron3(opts) {
|
|
|
131800
132260
|
}
|
|
131801
132261
|
} catch {}
|
|
131802
132262
|
}
|
|
131803
|
-
mkdirSync73(
|
|
132263
|
+
mkdirSync73(dirname55(path10), { recursive: true });
|
|
131804
132264
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
131805
132265
|
writeFileSync55(tmp, content, { mode: 420 });
|
|
131806
132266
|
renameSync34(tmp, path10);
|
|
@@ -131809,7 +132269,7 @@ function installCron3(opts) {
|
|
|
131809
132269
|
// src/openrouter/state.ts
|
|
131810
132270
|
import { mkdirSync as mkdirSync74, readFileSync as readFileSync116, renameSync as renameSync35, writeFileSync as writeFileSync56 } from "node:fs";
|
|
131811
132271
|
import { homedir as homedir71 } from "node:os";
|
|
131812
|
-
import { dirname as
|
|
132272
|
+
import { dirname as dirname56, resolve as resolve68 } from "node:path";
|
|
131813
132273
|
function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir71()) {
|
|
131814
132274
|
return resolve68(home2, ".switchroom", "openrouter-watch", "state.json");
|
|
131815
132275
|
}
|
|
@@ -131836,7 +132296,7 @@ function loadState2(path10) {
|
|
|
131836
132296
|
return out;
|
|
131837
132297
|
}
|
|
131838
132298
|
function saveState2(path10, state) {
|
|
131839
|
-
mkdirSync74(
|
|
132299
|
+
mkdirSync74(dirname56(path10), { recursive: true, mode: 448 });
|
|
131840
132300
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
131841
132301
|
const payload = { v: 1, ...state };
|
|
131842
132302
|
writeFileSync56(tmp, JSON.stringify(payload, null, 2) + `
|
|
@@ -131927,7 +132387,7 @@ function registerOpenRouterWatchCommand(program3) {
|
|
|
131927
132387
|
process.exitCode = runInstallCron2(opts.cronUser);
|
|
131928
132388
|
return;
|
|
131929
132389
|
}
|
|
131930
|
-
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ??
|
|
132390
|
+
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join130(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir72(), ".switchroom", "agents");
|
|
131931
132391
|
const statePath = opts.state ?? defaultStatePath3();
|
|
131932
132392
|
const log = (m) => {
|
|
131933
132393
|
process.stderr.write(`${m}
|
|
@@ -132024,7 +132484,7 @@ function runInstallCron2(cronUser) {
|
|
|
132024
132484
|
async function notifyOperator2(agentsDir, text, log) {
|
|
132025
132485
|
const candidates = [];
|
|
132026
132486
|
try {
|
|
132027
|
-
for (const name of
|
|
132487
|
+
for (const name of readdirSync53(agentsDir).sort()) {
|
|
132028
132488
|
const sock = resolve69(agentsDir, name, "telegram", "gateway.sock");
|
|
132029
132489
|
if (existsSync130(sock))
|
|
132030
132490
|
candidates.push({ agent: name, sock });
|
|
@@ -132047,7 +132507,7 @@ init_sync();
|
|
|
132047
132507
|
init_install_cron2();
|
|
132048
132508
|
init_flock();
|
|
132049
132509
|
import { homedir as homedir73 } from "node:os";
|
|
132050
|
-
import { join as
|
|
132510
|
+
import { join as join131 } from "node:path";
|
|
132051
132511
|
function switchroomHome() {
|
|
132052
132512
|
return process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir73();
|
|
132053
132513
|
}
|
|
@@ -132059,9 +132519,9 @@ function resolveSyncOptions(configPath, opts) {
|
|
|
132059
132519
|
cfg = loaded.config_repo;
|
|
132060
132520
|
} catch {}
|
|
132061
132521
|
const home2 = switchroomHome();
|
|
132062
|
-
const repoPath = opts.path ? resolvePath(opts.path) : cfg.path ? resolvePath(cfg.path) :
|
|
132063
|
-
const livePath = process.env.SWITCHROOM_CONFIG ??
|
|
132064
|
-
const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ??
|
|
132522
|
+
const repoPath = opts.path ? resolvePath(opts.path) : cfg.path ? resolvePath(cfg.path) : join131(home2, ".switchroom-config");
|
|
132523
|
+
const livePath = process.env.SWITCHROOM_CONFIG ?? join131(home2, ".switchroom", "switchroom.yaml");
|
|
132524
|
+
const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join131(home2, ".switchroom", "agents");
|
|
132065
132525
|
const push = opts.push === false ? false : cfg.push ?? true;
|
|
132066
132526
|
const remote = opts.remote ?? cfg.remote ?? "origin";
|
|
132067
132527
|
const requirePrivate = opts.requirePrivate ?? cfg.require_private ?? true;
|
|
@@ -132245,6 +132705,7 @@ registerDoctorCommand(program3);
|
|
|
132245
132705
|
registerUpdateCommand(program3);
|
|
132246
132706
|
registerRolloutCommand(program3);
|
|
132247
132707
|
registerRollbackCommand(program3);
|
|
132708
|
+
registerHostCliUpgradeCommand(program3);
|
|
132248
132709
|
registerRestartCommand(program3);
|
|
132249
132710
|
registerVersionCommand(program3);
|
|
132250
132711
|
registerVersionsCommand(program3);
|