switchroom 0.21.0 → 0.21.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-broker/index.js +54 -34
- package/dist/cli/switchroom.js +765 -367
- package/dist/host-control/main.js +8 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +174 -18
- package/telegram-plugin/external-spend.ts +1 -1
- package/telegram-plugin/gateway/gateway.ts +8 -0
- package/telegram-plugin/gateway/system-message-observer.ts +189 -13
- package/telegram-plugin/shared/sent-text-capture.ts +191 -0
- package/telegram-plugin/tests/card-history-lane.test.ts +121 -1
- package/telegram-plugin/tests/sent-text-capture.test.ts +526 -0
- package/telegram-plugin/tests/system-message-observer.test.ts +11 -2
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.3", COMMIT_SHA = "8a74c2f6";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -63459,10 +63459,13 @@ function serializeStamp(stamp) {
|
|
|
63459
63459
|
`;
|
|
63460
63460
|
}
|
|
63461
63461
|
function refreshHostCliStamp(input, io = {}) {
|
|
63462
|
+
const stamp = buildHostCliStamp(input, io);
|
|
63463
|
+
if (!stamp)
|
|
63464
|
+
return { status: "skipped", reason: "running in a container" };
|
|
63465
|
+
return writeHostCliStamp(stamp, io);
|
|
63466
|
+
}
|
|
63467
|
+
function writeHostCliStamp(stamp, io = {}) {
|
|
63462
63468
|
try {
|
|
63463
|
-
const stamp = buildHostCliStamp(input, io);
|
|
63464
|
-
if (!stamp)
|
|
63465
|
-
return { status: "skipped", reason: "running in a container" };
|
|
63466
63469
|
const dir = resolveStampDir(io);
|
|
63467
63470
|
if (!dir)
|
|
63468
63471
|
return { status: "skipped", reason: "no switchroom home found" };
|
|
@@ -84740,10 +84743,10 @@ var init_test_agents = __esm(() => {
|
|
|
84740
84743
|
});
|
|
84741
84744
|
|
|
84742
84745
|
// src/litellm/header-passthrough-guard.ts
|
|
84743
|
-
import { existsSync as existsSync124, readdirSync as
|
|
84746
|
+
import { existsSync as existsSync124, readdirSync as readdirSync48 } from "node:fs";
|
|
84744
84747
|
function discoverLiveLitellmConfigPath(opts) {
|
|
84745
84748
|
const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
|
|
84746
|
-
const readdir2 = opts?.readdirFn ?? ((p) =>
|
|
84749
|
+
const readdir2 = opts?.readdirFn ?? ((p) => readdirSync48(p));
|
|
84747
84750
|
const exists = opts?.existsFn ?? existsSync124;
|
|
84748
84751
|
let entries;
|
|
84749
84752
|
try {
|
|
@@ -84964,7 +84967,7 @@ var init_passthrough_mount_guard = __esm(() => {
|
|
|
84964
84967
|
// src/fleet-health/litellm-config-sensor.ts
|
|
84965
84968
|
import { execFileSync as execFileSync35 } from "node:child_process";
|
|
84966
84969
|
import { readFileSync as readFileSync111, existsSync as existsSync125 } from "node:fs";
|
|
84967
|
-
import { dirname as
|
|
84970
|
+
import { dirname as dirname52, join as join127 } from "node:path";
|
|
84968
84971
|
function resolveLiveLitellmPythonVersion() {
|
|
84969
84972
|
try {
|
|
84970
84973
|
const ps = execFileSync35("docker", ["ps", "--format", "{{.Names}}\t{{.Image}}"], {
|
|
@@ -85048,7 +85051,7 @@ function scanLitellmConfig(opts = {}) {
|
|
|
85048
85051
|
ts: nowIso
|
|
85049
85052
|
});
|
|
85050
85053
|
}
|
|
85051
|
-
const composePath =
|
|
85054
|
+
const composePath = join127(dirname52(path10), LIVE_COMPOSE_BASENAME);
|
|
85052
85055
|
let composeText = null;
|
|
85053
85056
|
if (exists(composePath)) {
|
|
85054
85057
|
try {
|
|
@@ -85152,12 +85155,12 @@ __export(exports_scan, {
|
|
|
85152
85155
|
});
|
|
85153
85156
|
import {
|
|
85154
85157
|
readFileSync as readFileSync112,
|
|
85155
|
-
readdirSync as
|
|
85158
|
+
readdirSync as readdirSync49,
|
|
85156
85159
|
existsSync as existsSync126,
|
|
85157
85160
|
mkdirSync as mkdirSync71,
|
|
85158
85161
|
writeFileSync as writeFileSync53
|
|
85159
85162
|
} from "node:fs";
|
|
85160
|
-
import { resolve as resolve64, dirname as
|
|
85163
|
+
import { resolve as resolve64, dirname as dirname53 } from "node:path";
|
|
85161
85164
|
import { homedir as homedir68 } from "node:os";
|
|
85162
85165
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir68()) {
|
|
85163
85166
|
return resolve64(home2, ".switchroom");
|
|
@@ -85167,7 +85170,7 @@ function listAgents(base) {
|
|
|
85167
85170
|
if (!existsSync126(dir))
|
|
85168
85171
|
return [];
|
|
85169
85172
|
try {
|
|
85170
|
-
return
|
|
85173
|
+
return readdirSync49(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
85171
85174
|
} catch {
|
|
85172
85175
|
return [];
|
|
85173
85176
|
}
|
|
@@ -85254,11 +85257,11 @@ function readLedgerIfPresent(base) {
|
|
|
85254
85257
|
}
|
|
85255
85258
|
}
|
|
85256
85259
|
function ledgerPathForBase(base) {
|
|
85257
|
-
return fleetHealthLedgerPath(
|
|
85260
|
+
return fleetHealthLedgerPath(dirname53(base));
|
|
85258
85261
|
}
|
|
85259
85262
|
function writeLedger(base, ledger) {
|
|
85260
85263
|
const path10 = ledgerPathForBase(base);
|
|
85261
|
-
mkdirSync71(
|
|
85264
|
+
mkdirSync71(dirname53(path10), { recursive: true });
|
|
85262
85265
|
writeFileSync53(path10, JSON.stringify(ledger, null, 2) + `
|
|
85263
85266
|
`, "utf-8");
|
|
85264
85267
|
return path10;
|
|
@@ -116419,7 +116422,7 @@ function registerUpdateCommand(program3) {
|
|
|
116419
116422
|
// src/cli/rollout.ts
|
|
116420
116423
|
init_helpers();
|
|
116421
116424
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
116422
|
-
import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as
|
|
116425
|
+
import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as statSync54 } from "node:fs";
|
|
116423
116426
|
import { homedir as homedir51 } from "node:os";
|
|
116424
116427
|
|
|
116425
116428
|
// src/cli/rollout-pin-journal.ts
|
|
@@ -116633,6 +116636,353 @@ function recoverPinJournal(configPath, opts = {}) {
|
|
|
116633
116636
|
// src/cli/rollout.ts
|
|
116634
116637
|
init_operator_uid();
|
|
116635
116638
|
init_host_cli_stamp();
|
|
116639
|
+
|
|
116640
|
+
// src/cli/host-cli-upgrade.ts
|
|
116641
|
+
init_self_update();
|
|
116642
|
+
import { lchownSync, lstatSync as lstatSync14, readdirSync as readdirSync32, statSync as statSync53 } from "node:fs";
|
|
116643
|
+
import { basename as basename10, dirname as dirname42, join as join96 } from "node:path";
|
|
116644
|
+
init_shipped_assets();
|
|
116645
|
+
var HOST_CLI_UPGRADE_SENTINEL = "SWITCHROOM_HOST_CLI_UPGRADE:";
|
|
116646
|
+
function encodeHostCliUpgradeResult(r) {
|
|
116647
|
+
return `${HOST_CLI_UPGRADE_SENTINEL}${JSON.stringify(r)}`;
|
|
116648
|
+
}
|
|
116649
|
+
function parseHostCliUpgradeResult(logs) {
|
|
116650
|
+
const lines = logs.split(`
|
|
116651
|
+
`);
|
|
116652
|
+
for (let i2 = lines.length - 1;i2 >= 0; i2--) {
|
|
116653
|
+
const line = lines[i2]?.trim() ?? "";
|
|
116654
|
+
if (!line.startsWith(HOST_CLI_UPGRADE_SENTINEL))
|
|
116655
|
+
continue;
|
|
116656
|
+
try {
|
|
116657
|
+
const o = JSON.parse(line.slice(HOST_CLI_UPGRADE_SENTINEL.length));
|
|
116658
|
+
if (typeof o !== "object" || o === null)
|
|
116659
|
+
return null;
|
|
116660
|
+
if (typeof o.ok !== "boolean")
|
|
116661
|
+
return null;
|
|
116662
|
+
return {
|
|
116663
|
+
ok: o.ok,
|
|
116664
|
+
...typeof o.version === "string" ? { version: o.version } : {},
|
|
116665
|
+
...typeof o.binaryPath === "string" ? { binaryPath: o.binaryPath } : {},
|
|
116666
|
+
...typeof o.error === "string" ? { error: o.error } : {}
|
|
116667
|
+
};
|
|
116668
|
+
} catch {
|
|
116669
|
+
return null;
|
|
116670
|
+
}
|
|
116671
|
+
}
|
|
116672
|
+
return null;
|
|
116673
|
+
}
|
|
116674
|
+
var SEMVER_TAG_RE2 = /^v\d+\.\d+\.\d+$/;
|
|
116675
|
+
function defaultIo() {
|
|
116676
|
+
return {
|
|
116677
|
+
isFile: (p) => {
|
|
116678
|
+
try {
|
|
116679
|
+
return statSync53(p).isFile();
|
|
116680
|
+
} catch {
|
|
116681
|
+
return false;
|
|
116682
|
+
}
|
|
116683
|
+
},
|
|
116684
|
+
kind: (p) => {
|
|
116685
|
+
try {
|
|
116686
|
+
const st = lstatSync14(p);
|
|
116687
|
+
if (st.isSymbolicLink())
|
|
116688
|
+
return "symlink";
|
|
116689
|
+
if (st.isDirectory())
|
|
116690
|
+
return "dir";
|
|
116691
|
+
if (st.isFile())
|
|
116692
|
+
return "file";
|
|
116693
|
+
return "other";
|
|
116694
|
+
} catch {
|
|
116695
|
+
return;
|
|
116696
|
+
}
|
|
116697
|
+
},
|
|
116698
|
+
owner: (p) => {
|
|
116699
|
+
try {
|
|
116700
|
+
const st = lstatSync14(p);
|
|
116701
|
+
return { uid: st.uid, gid: st.gid };
|
|
116702
|
+
} catch {
|
|
116703
|
+
return;
|
|
116704
|
+
}
|
|
116705
|
+
},
|
|
116706
|
+
chown: (p, uid, gid) => lchownSync(p, uid, gid),
|
|
116707
|
+
list: (d) => {
|
|
116708
|
+
try {
|
|
116709
|
+
return readdirSync32(d);
|
|
116710
|
+
} catch {
|
|
116711
|
+
return [];
|
|
116712
|
+
}
|
|
116713
|
+
},
|
|
116714
|
+
selfUpdate: defaultSelfUpdateIO(),
|
|
116715
|
+
platform: process.platform,
|
|
116716
|
+
arch: process.arch,
|
|
116717
|
+
getuid: () => process.getuid?.()
|
|
116718
|
+
};
|
|
116719
|
+
}
|
|
116720
|
+
function handBackOwnership(paths, owner, io) {
|
|
116721
|
+
const failures = [];
|
|
116722
|
+
const seen = new Set;
|
|
116723
|
+
const stack = [...paths];
|
|
116724
|
+
while (stack.length > 0) {
|
|
116725
|
+
const p = stack.pop();
|
|
116726
|
+
if (p === undefined || seen.has(p))
|
|
116727
|
+
continue;
|
|
116728
|
+
seen.add(p);
|
|
116729
|
+
const kind = io.kind(p);
|
|
116730
|
+
if (kind === undefined)
|
|
116731
|
+
continue;
|
|
116732
|
+
try {
|
|
116733
|
+
io.chown(p, owner.uid, owner.gid);
|
|
116734
|
+
} catch (err) {
|
|
116735
|
+
failures.push(`${p}: ${err.message}`);
|
|
116736
|
+
}
|
|
116737
|
+
if (kind !== "dir")
|
|
116738
|
+
continue;
|
|
116739
|
+
for (const child of io.list(p))
|
|
116740
|
+
stack.push(join96(p, child));
|
|
116741
|
+
}
|
|
116742
|
+
return failures;
|
|
116743
|
+
}
|
|
116744
|
+
async function runHostCliUpgrade(opts, io = defaultIo(), log = () => {}) {
|
|
116745
|
+
const { binary, pin, from } = opts;
|
|
116746
|
+
if (!SEMVER_TAG_RE2.test(pin)) {
|
|
116747
|
+
return { ok: false, error: `--pin must be a vX.Y.Z release tag, got "${pin}"` };
|
|
116748
|
+
}
|
|
116749
|
+
if (!binary.startsWith("/")) {
|
|
116750
|
+
return { ok: false, error: `--binary must be an absolute path, got "${binary}"` };
|
|
116751
|
+
}
|
|
116752
|
+
if (basename10(binary) !== "switchroom") {
|
|
116753
|
+
return {
|
|
116754
|
+
ok: false,
|
|
116755
|
+
error: `--binary must name a file called \`switchroom\`, got ` + `"${basename10(binary)}" \u2014 refusing to overwrite an unrelated file.`
|
|
116756
|
+
};
|
|
116757
|
+
}
|
|
116758
|
+
if (!io.isFile(binary)) {
|
|
116759
|
+
return {
|
|
116760
|
+
ok: false,
|
|
116761
|
+
error: `${binary} is not an existing regular file \u2014 the host bindir is either ` + `not mounted or the recorded host CLI path is stale.`
|
|
116762
|
+
};
|
|
116763
|
+
}
|
|
116764
|
+
const asset = releaseAssetName(io.platform, io.arch);
|
|
116765
|
+
if (!asset) {
|
|
116766
|
+
return {
|
|
116767
|
+
ok: false,
|
|
116768
|
+
error: `no published binary for ${io.platform}/${io.arch}`
|
|
116769
|
+
};
|
|
116770
|
+
}
|
|
116771
|
+
const priorOwner = io.owner(binary);
|
|
116772
|
+
const handBack = () => {
|
|
116773
|
+
try {
|
|
116774
|
+
if (!priorOwner || priorOwner.uid === io.getuid())
|
|
116775
|
+
return;
|
|
116776
|
+
const installRoot = payloadInstallRoot(binary);
|
|
116777
|
+
const failures = handBackOwnership([
|
|
116778
|
+
binary,
|
|
116779
|
+
join96(dirname42(binary), ".switchroom-versions"),
|
|
116780
|
+
dirname42(installRoot),
|
|
116781
|
+
installRoot,
|
|
116782
|
+
payloadVersionDir(installRoot, pin)
|
|
116783
|
+
], priorOwner, io);
|
|
116784
|
+
if (failures.length > 0) {
|
|
116785
|
+
log(`warning: could not hand ownership back to uid ${priorOwner.uid}: ` + `${failures.join("; ")}
|
|
116786
|
+
`);
|
|
116787
|
+
}
|
|
116788
|
+
} catch (err) {
|
|
116789
|
+
log(`warning: ownership handoff failed: ${err.message}
|
|
116790
|
+
`);
|
|
116791
|
+
}
|
|
116792
|
+
};
|
|
116793
|
+
try {
|
|
116794
|
+
let result;
|
|
116795
|
+
try {
|
|
116796
|
+
result = await performSelfUpdate({
|
|
116797
|
+
plan: {
|
|
116798
|
+
action: "update",
|
|
116799
|
+
from: `v${from.replace(/^v/, "")}`,
|
|
116800
|
+
to: pin,
|
|
116801
|
+
binaryPath: binary
|
|
116802
|
+
},
|
|
116803
|
+
assetName: asset,
|
|
116804
|
+
io: io.selfUpdate,
|
|
116805
|
+
log
|
|
116806
|
+
});
|
|
116807
|
+
} catch (err) {
|
|
116808
|
+
return { ok: false, error: err.message };
|
|
116809
|
+
}
|
|
116810
|
+
const proven = io.selfUpdate.probeBinaryVersion(binary);
|
|
116811
|
+
if (!proven || `v${proven.replace(/^v/, "")}` !== pin) {
|
|
116812
|
+
return {
|
|
116813
|
+
ok: false,
|
|
116814
|
+
error: `swapped ${binary} but it reports ${proven ?? "<nothing>"}, not ${pin} \u2014 ` + `the install did not land. ${result.message}`
|
|
116815
|
+
};
|
|
116816
|
+
}
|
|
116817
|
+
return { ok: true, version: pin, binaryPath: binary };
|
|
116818
|
+
} finally {
|
|
116819
|
+
handBack();
|
|
116820
|
+
}
|
|
116821
|
+
}
|
|
116822
|
+
function registerHostCliUpgradeCommand(program3) {
|
|
116823
|
+
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) => {
|
|
116824
|
+
const result = await runHostCliUpgrade(opts, defaultIo(), (s) => process.stderr.write(s.endsWith(`
|
|
116825
|
+
`) ? s : `${s}
|
|
116826
|
+
`));
|
|
116827
|
+
process.stdout.write(`${encodeHostCliUpgradeResult(result)}
|
|
116828
|
+
`);
|
|
116829
|
+
if (!result.ok) {
|
|
116830
|
+
process.stderr.write(`host-cli-upgrade failed: ${result.error}
|
|
116831
|
+
`);
|
|
116832
|
+
process.exitCode = 1;
|
|
116833
|
+
}
|
|
116834
|
+
});
|
|
116835
|
+
}
|
|
116836
|
+
|
|
116837
|
+
// src/cli/host-cli-heal.ts
|
|
116838
|
+
var HEAL_PREFIX_MOUNT = "/hostcli";
|
|
116839
|
+
var HEAL_HELPER_CONTAINER = "switchroom-hostcli-heal";
|
|
116840
|
+
var HOSTD_CONTAINER_NAME = "switchroom-hostd";
|
|
116841
|
+
var HOST_CLI_HEAL_TIMEOUT_MS = (() => {
|
|
116842
|
+
const raw = process.env.SWITCHROOM_HOST_CLI_HEAL_TIMEOUT_MS;
|
|
116843
|
+
const n = raw ? Number(raw) : NaN;
|
|
116844
|
+
return Number.isFinite(n) && n > 0 ? n : 10 * 60 * 1000;
|
|
116845
|
+
})();
|
|
116846
|
+
function planHostCliHeal(opts) {
|
|
116847
|
+
const { stamp, target, hostdCtx } = opts;
|
|
116848
|
+
if (!hostdCtx) {
|
|
116849
|
+
return {
|
|
116850
|
+
action: "skip",
|
|
116851
|
+
reason: "not running in hostd context \u2014 on the host shell the operator can run " + "the install command directly"
|
|
116852
|
+
};
|
|
116853
|
+
}
|
|
116854
|
+
if (!stamp)
|
|
116855
|
+
return { action: "skip", reason: "no host-cli.json stamp to act on" };
|
|
116856
|
+
if (!/^v\d+\.\d+\.\d+$/.test(target)) {
|
|
116857
|
+
return { action: "skip", reason: `target "${target}" is not a vX.Y.Z release tag` };
|
|
116858
|
+
}
|
|
116859
|
+
if (stamp.installKind !== "static-binary") {
|
|
116860
|
+
return {
|
|
116861
|
+
action: "skip",
|
|
116862
|
+
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`
|
|
116863
|
+
};
|
|
116864
|
+
}
|
|
116865
|
+
const path7 = stamp.path;
|
|
116866
|
+
if (!path7.startsWith("/") || path7.includes("/..")) {
|
|
116867
|
+
return { action: "skip", reason: `recorded host CLI path "${path7}" is not a plain absolute path` };
|
|
116868
|
+
}
|
|
116869
|
+
const installDir = path7.slice(0, path7.lastIndexOf("/"));
|
|
116870
|
+
const name = path7.slice(path7.lastIndexOf("/") + 1);
|
|
116871
|
+
if (name !== "switchroom") {
|
|
116872
|
+
return { action: "skip", reason: `recorded host CLI path "${path7}" is not named \`switchroom\`` };
|
|
116873
|
+
}
|
|
116874
|
+
const prefixHostPath = installDir.slice(0, installDir.lastIndexOf("/"));
|
|
116875
|
+
const dirName = installDir.slice(installDir.lastIndexOf("/") + 1);
|
|
116876
|
+
if (prefixHostPath.length === 0 || dirName.length === 0) {
|
|
116877
|
+
return {
|
|
116878
|
+
action: "skip",
|
|
116879
|
+
reason: `recorded host CLI path "${path7}" has no install prefix to bind \u2014 ` + `refusing to mount the host root`
|
|
116880
|
+
};
|
|
116881
|
+
}
|
|
116882
|
+
return {
|
|
116883
|
+
action: "heal",
|
|
116884
|
+
prefixHostPath,
|
|
116885
|
+
containerBinaryPath: `${HEAL_PREFIX_MOUNT}/${dirName}/switchroom`,
|
|
116886
|
+
from: stamp.version,
|
|
116887
|
+
target
|
|
116888
|
+
};
|
|
116889
|
+
}
|
|
116890
|
+
function healHelperArgs(opts) {
|
|
116891
|
+
const { plan, helperImage } = opts;
|
|
116892
|
+
return [
|
|
116893
|
+
"run",
|
|
116894
|
+
"-d",
|
|
116895
|
+
"--name",
|
|
116896
|
+
opts.containerName ?? HEAL_HELPER_CONTAINER,
|
|
116897
|
+
"--label",
|
|
116898
|
+
"switchroom.hostcli-heal=true",
|
|
116899
|
+
"-v",
|
|
116900
|
+
`${plan.prefixHostPath}:${HEAL_PREFIX_MOUNT}:rw`,
|
|
116901
|
+
helperImage,
|
|
116902
|
+
"switchroom",
|
|
116903
|
+
"host-cli-upgrade",
|
|
116904
|
+
"--binary",
|
|
116905
|
+
plan.containerBinaryPath,
|
|
116906
|
+
"--pin",
|
|
116907
|
+
plan.target,
|
|
116908
|
+
"--from",
|
|
116909
|
+
plan.from
|
|
116910
|
+
];
|
|
116911
|
+
}
|
|
116912
|
+
function resolveHelperImage(docker) {
|
|
116913
|
+
const r = docker(["inspect", "--format", "{{.Config.Image}}", HOSTD_CONTAINER_NAME]);
|
|
116914
|
+
if (!r.ok)
|
|
116915
|
+
return null;
|
|
116916
|
+
const ref = r.stdout.trim().split(`
|
|
116917
|
+
`)[0]?.trim() ?? "";
|
|
116918
|
+
return ref.length > 0 ? ref : null;
|
|
116919
|
+
}
|
|
116920
|
+
function runHostCliHeal(opts) {
|
|
116921
|
+
const { plan, docker } = opts;
|
|
116922
|
+
const name = opts.containerName ?? HEAL_HELPER_CONTAINER;
|
|
116923
|
+
const log = opts.log ?? (() => {});
|
|
116924
|
+
const image = resolveHelperImage(docker);
|
|
116925
|
+
if (!image) {
|
|
116926
|
+
return {
|
|
116927
|
+
ok: false,
|
|
116928
|
+
message: `could not resolve the image of the running \`${HOSTD_CONTAINER_NAME}\` ` + `container, so there is no image to run the upgrade helper from`
|
|
116929
|
+
};
|
|
116930
|
+
}
|
|
116931
|
+
docker(["rm", "-f", name]);
|
|
116932
|
+
log(`\u21bb host CLI ${plan.from} \u2192 ${plan.target}: running the upgrade helper ` + `(${image}, binding ${plan.prefixHostPath})
|
|
116933
|
+
`);
|
|
116934
|
+
const started = docker(healHelperArgs({ plan, helperImage: image, containerName: name }));
|
|
116935
|
+
if (!started.ok) {
|
|
116936
|
+
return {
|
|
116937
|
+
ok: false,
|
|
116938
|
+
message: `could not start the upgrade helper: ${oneLine(started.stderr) || "docker run failed"}`
|
|
116939
|
+
};
|
|
116940
|
+
}
|
|
116941
|
+
try {
|
|
116942
|
+
return waitForHelper(docker, name, plan);
|
|
116943
|
+
} catch (err) {
|
|
116944
|
+
return { ok: false, message: `the upgrade helper could not be waited on: ${err.message}` };
|
|
116945
|
+
} finally {
|
|
116946
|
+
try {
|
|
116947
|
+
docker(["rm", "-f", name]);
|
|
116948
|
+
} catch {}
|
|
116949
|
+
}
|
|
116950
|
+
}
|
|
116951
|
+
function waitForHelper(docker, name, plan) {
|
|
116952
|
+
const waited = docker(["wait", name]);
|
|
116953
|
+
const logs = docker(["logs", name]);
|
|
116954
|
+
const blob = `${logs.stdout}
|
|
116955
|
+
${logs.stderr}`;
|
|
116956
|
+
const parsed = parseHostCliUpgradeResult(blob);
|
|
116957
|
+
if (!waited.ok) {
|
|
116958
|
+
return {
|
|
116959
|
+
ok: false,
|
|
116960
|
+
message: `waiting on the upgrade helper failed (${oneLine(waited.stderr) || "docker wait failed"})` + (parsed?.error ? `; helper reported: ${parsed.error}` : "")
|
|
116961
|
+
};
|
|
116962
|
+
}
|
|
116963
|
+
const code = waited.stdout.trim().split(`
|
|
116964
|
+
`).pop()?.trim() ?? "";
|
|
116965
|
+
if (code !== "0" || !parsed || !parsed.ok) {
|
|
116966
|
+
return {
|
|
116967
|
+
ok: false,
|
|
116968
|
+
message: `the upgrade helper exited ${code || "?"}: ` + (parsed?.error ?? (oneLine(blob) || "no diagnostic on stdout/stderr"))
|
|
116969
|
+
};
|
|
116970
|
+
}
|
|
116971
|
+
if (!parsed.version) {
|
|
116972
|
+
return { ok: false, message: "the upgrade helper reported success without a version" };
|
|
116973
|
+
}
|
|
116974
|
+
return {
|
|
116975
|
+
ok: true,
|
|
116976
|
+
version: parsed.version,
|
|
116977
|
+
message: `host CLI upgraded ${plan.from} \u2192 ${parsed.version} at ${plan.containerBinaryPath.replace(HEAL_PREFIX_MOUNT, plan.prefixHostPath)}`
|
|
116978
|
+
};
|
|
116979
|
+
}
|
|
116980
|
+
function oneLine(s) {
|
|
116981
|
+
const flat = s.replace(/\s+/g, " ").trim();
|
|
116982
|
+
return flat.length > 400 ? `${flat.slice(0, 397)}...` : flat;
|
|
116983
|
+
}
|
|
116984
|
+
|
|
116985
|
+
// src/cli/rollout.ts
|
|
116636
116986
|
init_self_invoke();
|
|
116637
116987
|
init_atomic();
|
|
116638
116988
|
|
|
@@ -117281,7 +117631,7 @@ function createRolloutDeps(params) {
|
|
|
117281
117631
|
if (after === before)
|
|
117282
117632
|
return false;
|
|
117283
117633
|
beginPinPersist(configPath, pin);
|
|
117284
|
-
writeConfigPreservingOwnership(configPath, after,
|
|
117634
|
+
writeConfigPreservingOwnership(configPath, after, statSync54(configPath).mode & 511);
|
|
117285
117635
|
return true;
|
|
117286
117636
|
},
|
|
117287
117637
|
commitPin: () => {
|
|
@@ -117309,8 +117659,21 @@ function createRolloutDeps(params) {
|
|
|
117309
117659
|
} : {}
|
|
117310
117660
|
};
|
|
117311
117661
|
}
|
|
117312
|
-
function registerRolloutCommand(program3) {
|
|
117662
|
+
function registerRolloutCommand(program3, testHooks) {
|
|
117313
117663
|
const hostdCtx = isHostdContext2();
|
|
117664
|
+
const hostCliHealDocker = testHooks?.hostCliHealDocker;
|
|
117665
|
+
const defaultHostCliHealDocker = (args) => {
|
|
117666
|
+
try {
|
|
117667
|
+
const r = spawnSync22("docker", args, {
|
|
117668
|
+
encoding: "utf8",
|
|
117669
|
+
timeout: HOST_CLI_HEAL_TIMEOUT_MS,
|
|
117670
|
+
killSignal: ROLLOUT_KILL_SIGNAL
|
|
117671
|
+
});
|
|
117672
|
+
return { ok: r.status === 0, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
117673
|
+
} catch (err) {
|
|
117674
|
+
return { ok: false, stdout: "", stderr: err.message };
|
|
117675
|
+
}
|
|
117676
|
+
};
|
|
117314
117677
|
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
117678
|
if (!opts.dryRun) {
|
|
117316
117679
|
const note = recoverPinJournal(getConfigPath(program3));
|
|
@@ -117360,29 +117723,6 @@ function registerRolloutCommand(program3) {
|
|
|
117360
117723
|
process.exitCode = 2;
|
|
117361
117724
|
return;
|
|
117362
117725
|
}
|
|
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
117726
|
if (shouldRefuseDowngrade(hostdCtx, resolvedPin, config.release?.pin, opts.allowDowngrade)) {
|
|
117387
117727
|
const current = config.release?.pin;
|
|
117388
117728
|
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 +117750,63 @@ function registerRolloutCommand(program3) {
|
|
|
117410
117750
|
process.exitCode = 2;
|
|
117411
117751
|
return;
|
|
117412
117752
|
}
|
|
117753
|
+
let hostCliStamp = readHostCliStamp();
|
|
117754
|
+
let healNote;
|
|
117755
|
+
let healDeferredByDryRun = false;
|
|
117756
|
+
if (!opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117757
|
+
const plan = planHostCliHeal({ stamp: hostCliStamp, target, hostdCtx });
|
|
117758
|
+
if (plan.action === "skip") {
|
|
117759
|
+
healNote = `host CLI self-heal not attempted: ${plan.reason}.`;
|
|
117760
|
+
} else if (opts.dryRun) {
|
|
117761
|
+
healDeferredByDryRun = true;
|
|
117762
|
+
healNote = `would upgrade the HOST CLI ${plan.from} \u2192 ${plan.target} first, in a ` + `short-lived helper container binding ${plan.prefixHostPath}`;
|
|
117763
|
+
process.stdout.write(`\u21bb dry-run: ${healNote}
|
|
117764
|
+
`);
|
|
117765
|
+
} else {
|
|
117766
|
+
const outcome = runHostCliHeal({
|
|
117767
|
+
plan,
|
|
117768
|
+
docker: hostCliHealDocker ?? defaultHostCliHealDocker,
|
|
117769
|
+
log: (line) => process.stderr.write(line)
|
|
117770
|
+
});
|
|
117771
|
+
if (outcome.ok && outcome.version) {
|
|
117772
|
+
const updated = {
|
|
117773
|
+
...hostCliStamp,
|
|
117774
|
+
version: outcome.version.replace(/^v/, "")
|
|
117775
|
+
};
|
|
117776
|
+
const w = writeHostCliStamp(updated, testHooks?.hostCliStampIo ?? {});
|
|
117777
|
+
hostCliStamp = updated;
|
|
117778
|
+
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)` : "");
|
|
117779
|
+
process.stderr.write(`\u2705 ${healNote}
|
|
117780
|
+
`);
|
|
117781
|
+
} else {
|
|
117782
|
+
healNote = `host CLI self-heal FAILED: ${outcome.message}.`;
|
|
117783
|
+
process.stderr.write(`\u26a0\ufe0f ${healNote}
|
|
117784
|
+
`);
|
|
117785
|
+
}
|
|
117786
|
+
}
|
|
117787
|
+
}
|
|
117788
|
+
if (!opts.allowStaleHostCli && !healDeferredByDryRun && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117789
|
+
const observed = hostCliStamp?.version ?? "unknown";
|
|
117790
|
+
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.`;
|
|
117791
|
+
process.stderr.write(refusal + `
|
|
117792
|
+
`);
|
|
117793
|
+
if (hostdCtx) {
|
|
117794
|
+
process.stdout.write(encodeRolloutResultLine({
|
|
117795
|
+
ok: false,
|
|
117796
|
+
rolled: [],
|
|
117797
|
+
failedStep: PREFLIGHT_HOST_CLI_STALE_STEP,
|
|
117798
|
+
got: observed,
|
|
117799
|
+
warnings: [refusal]
|
|
117800
|
+
}) + `
|
|
117801
|
+
`);
|
|
117802
|
+
}
|
|
117803
|
+
process.exitCode = 2;
|
|
117804
|
+
return;
|
|
117805
|
+
}
|
|
117806
|
+
if (opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
|
|
117807
|
+
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)}
|
|
117808
|
+
`);
|
|
117809
|
+
}
|
|
117413
117810
|
const steps = planRollout(requested, {
|
|
117414
117811
|
skipWeb: opts.skipWeb,
|
|
117415
117812
|
pinToPersist: resolvedPin ?? undefined,
|
|
@@ -117429,7 +117826,7 @@ function registerRolloutCommand(program3) {
|
|
|
117429
117826
|
hindsightApiUrl = rawUrl ? rawUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
117430
117827
|
resolveBankId = (agent) => config.agents?.[agent]?.memory?.collection ?? agent;
|
|
117431
117828
|
}
|
|
117432
|
-
const deps = createRolloutDeps({
|
|
117829
|
+
const deps = testHooks?.rolloutDeps ?? createRolloutDeps({
|
|
117433
117830
|
configPath,
|
|
117434
117831
|
scriptPath,
|
|
117435
117832
|
hostdCtx,
|
|
@@ -117519,7 +117916,7 @@ init_lifecycle();
|
|
|
117519
117916
|
init_resolve_version();
|
|
117520
117917
|
import { execSync as execSync3 } from "node:child_process";
|
|
117521
117918
|
import { existsSync as existsSync93, readFileSync as readFileSync86 } from "node:fs";
|
|
117522
|
-
import { dirname as
|
|
117919
|
+
import { dirname as dirname43, join as join97 } from "node:path";
|
|
117523
117920
|
function getClaudeCodeVersion() {
|
|
117524
117921
|
try {
|
|
117525
117922
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -117569,16 +117966,16 @@ function formatUptime3(timestamp) {
|
|
|
117569
117966
|
function locateSwitchroomInstallDir() {
|
|
117570
117967
|
let dir = import.meta.dirname;
|
|
117571
117968
|
for (let i2 = 0;i2 < 10 && dir && dir !== "/"; i2++) {
|
|
117572
|
-
const pkgPath =
|
|
117969
|
+
const pkgPath = join97(dir, "package.json");
|
|
117573
117970
|
if (existsSync93(pkgPath)) {
|
|
117574
117971
|
try {
|
|
117575
117972
|
const pkg = JSON.parse(readFileSync86(pkgPath, "utf-8"));
|
|
117576
|
-
if (pkg.name === "switchroom" && existsSync93(
|
|
117973
|
+
if (pkg.name === "switchroom" && existsSync93(join97(dir, ".git"))) {
|
|
117577
117974
|
return dir;
|
|
117578
117975
|
}
|
|
117579
117976
|
} catch {}
|
|
117580
117977
|
}
|
|
117581
|
-
dir =
|
|
117978
|
+
dir = dirname43(dir);
|
|
117582
117979
|
}
|
|
117583
117980
|
return null;
|
|
117584
117981
|
}
|
|
@@ -117755,31 +118152,31 @@ init_merge();
|
|
|
117755
118152
|
// src/agents/session-retention.ts
|
|
117756
118153
|
import {
|
|
117757
118154
|
existsSync as existsSync94,
|
|
117758
|
-
readdirSync as
|
|
117759
|
-
statSync as
|
|
118155
|
+
readdirSync as readdirSync33,
|
|
118156
|
+
statSync as statSync55,
|
|
117760
118157
|
unlinkSync as unlinkSync18
|
|
117761
118158
|
} from "node:fs";
|
|
117762
|
-
import { join as
|
|
118159
|
+
import { join as join98 } from "node:path";
|
|
117763
118160
|
var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
|
|
117764
118161
|
var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
117765
118162
|
var MIN_KEEP = 2;
|
|
117766
118163
|
function collectSessionJsonl(claudeConfigDir) {
|
|
117767
|
-
const projects =
|
|
118164
|
+
const projects = join98(claudeConfigDir, "projects");
|
|
117768
118165
|
if (!existsSync94(projects))
|
|
117769
118166
|
return [];
|
|
117770
118167
|
const found = [];
|
|
117771
118168
|
const walk2 = (dir) => {
|
|
117772
118169
|
let entries;
|
|
117773
118170
|
try {
|
|
117774
|
-
entries =
|
|
118171
|
+
entries = readdirSync33(dir);
|
|
117775
118172
|
} catch {
|
|
117776
118173
|
return;
|
|
117777
118174
|
}
|
|
117778
118175
|
for (const name of entries) {
|
|
117779
|
-
const full =
|
|
118176
|
+
const full = join98(dir, name);
|
|
117780
118177
|
let st;
|
|
117781
118178
|
try {
|
|
117782
|
-
st =
|
|
118179
|
+
st = statSync55(full);
|
|
117783
118180
|
} catch {
|
|
117784
118181
|
continue;
|
|
117785
118182
|
}
|
|
@@ -117900,15 +118297,15 @@ import {
|
|
|
117900
118297
|
existsSync as existsSync95,
|
|
117901
118298
|
mkdirSync as mkdirSync53,
|
|
117902
118299
|
openSync as openSync17,
|
|
117903
|
-
readdirSync as
|
|
118300
|
+
readdirSync as readdirSync34,
|
|
117904
118301
|
readFileSync as readFileSync87,
|
|
117905
118302
|
renameSync as renameSync25,
|
|
117906
|
-
statSync as
|
|
118303
|
+
statSync as statSync56,
|
|
117907
118304
|
unlinkSync as unlinkSync19,
|
|
117908
118305
|
writeFileSync as writeFileSync38,
|
|
117909
118306
|
writeSync as writeSync9
|
|
117910
118307
|
} from "node:fs";
|
|
117911
|
-
import { join as
|
|
118308
|
+
import { join as join99 } from "node:path";
|
|
117912
118309
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
117913
118310
|
import { execSync as execSync4 } from "node:child_process";
|
|
117914
118311
|
|
|
@@ -117935,7 +118332,7 @@ function computeFingerprint(source, code) {
|
|
|
117935
118332
|
var ISSUES_FILE = "issues.jsonl";
|
|
117936
118333
|
var ISSUES_LOCK = "issues.lock";
|
|
117937
118334
|
function readAll(stateDir) {
|
|
117938
|
-
const path7 =
|
|
118335
|
+
const path7 = join99(stateDir, ISSUES_FILE);
|
|
117939
118336
|
if (!existsSync95(path7))
|
|
117940
118337
|
return [];
|
|
117941
118338
|
let raw;
|
|
@@ -118013,7 +118410,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
118013
118410
|
});
|
|
118014
118411
|
}
|
|
118015
118412
|
function resolve51(stateDir, fingerprint, nowFn = Date.now) {
|
|
118016
|
-
if (!existsSync95(
|
|
118413
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118017
118414
|
return 0;
|
|
118018
118415
|
return withLock(stateDir, () => {
|
|
118019
118416
|
const all = readAll(stateDir);
|
|
@@ -118031,7 +118428,7 @@ function resolve51(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
118031
118428
|
});
|
|
118032
118429
|
}
|
|
118033
118430
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
118034
|
-
if (!existsSync95(
|
|
118431
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118035
118432
|
return 0;
|
|
118036
118433
|
return withLock(stateDir, () => {
|
|
118037
118434
|
const all = readAll(stateDir);
|
|
@@ -118049,7 +118446,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
118049
118446
|
});
|
|
118050
118447
|
}
|
|
118051
118448
|
function prune(stateDir, opts = {}) {
|
|
118052
|
-
if (!existsSync95(
|
|
118449
|
+
if (!existsSync95(join99(stateDir, ISSUES_FILE)))
|
|
118053
118450
|
return 0;
|
|
118054
118451
|
return withLock(stateDir, () => {
|
|
118055
118452
|
const all = readAll(stateDir);
|
|
@@ -118082,7 +118479,7 @@ function ensureDir(stateDir) {
|
|
|
118082
118479
|
mkdirSync53(stateDir, { recursive: true });
|
|
118083
118480
|
}
|
|
118084
118481
|
function writeAll(stateDir, events) {
|
|
118085
|
-
const path7 =
|
|
118482
|
+
const path7 = join99(stateDir, ISSUES_FILE);
|
|
118086
118483
|
sweepOrphanTmpFiles(stateDir);
|
|
118087
118484
|
const tmp = `${path7}.tmp-${process.pid}-${randomBytes15(4).toString("hex")}`;
|
|
118088
118485
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -118096,7 +118493,7 @@ var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
|
118096
118493
|
function sweepOrphanTmpFiles(stateDir) {
|
|
118097
118494
|
let entries;
|
|
118098
118495
|
try {
|
|
118099
|
-
entries =
|
|
118496
|
+
entries = readdirSync34(stateDir);
|
|
118100
118497
|
} catch {
|
|
118101
118498
|
return;
|
|
118102
118499
|
}
|
|
@@ -118104,9 +118501,9 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
118104
118501
|
for (const entry of entries) {
|
|
118105
118502
|
if (!entry.startsWith(TMP_PREFIX))
|
|
118106
118503
|
continue;
|
|
118107
|
-
const tmpPath =
|
|
118504
|
+
const tmpPath = join99(stateDir, entry);
|
|
118108
118505
|
try {
|
|
118109
|
-
const stat =
|
|
118506
|
+
const stat = statSync56(tmpPath);
|
|
118110
118507
|
if (stat.mtimeMs < cutoff) {
|
|
118111
118508
|
unlinkSync19(tmpPath);
|
|
118112
118509
|
}
|
|
@@ -118116,7 +118513,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
118116
118513
|
var LOCK_RETRY_MS = 25;
|
|
118117
118514
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
118118
118515
|
function withLock(stateDir, fn) {
|
|
118119
|
-
const lockPath =
|
|
118516
|
+
const lockPath = join99(stateDir, ISSUES_LOCK);
|
|
118120
118517
|
const startedAt = Date.now();
|
|
118121
118518
|
let fd = null;
|
|
118122
118519
|
while (fd === null) {
|
|
@@ -118401,7 +118798,7 @@ function relTime(deltaMs) {
|
|
|
118401
118798
|
init_source();
|
|
118402
118799
|
import { existsSync as existsSync98 } from "node:fs";
|
|
118403
118800
|
import { homedir as homedir54 } from "node:os";
|
|
118404
|
-
import { join as
|
|
118801
|
+
import { join as join102, resolve as resolve52 } from "node:path";
|
|
118405
118802
|
|
|
118406
118803
|
// src/deps/python.ts
|
|
118407
118804
|
import { createHash as createHash18 } from "node:crypto";
|
|
@@ -118412,7 +118809,7 @@ import {
|
|
|
118412
118809
|
rmSync as rmSync16,
|
|
118413
118810
|
writeFileSync as writeFileSync39
|
|
118414
118811
|
} from "node:fs";
|
|
118415
|
-
import { dirname as
|
|
118812
|
+
import { dirname as dirname44, join as join100 } from "node:path";
|
|
118416
118813
|
import { homedir as homedir52 } from "node:os";
|
|
118417
118814
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
118418
118815
|
|
|
@@ -118425,7 +118822,7 @@ class PythonEnvError extends Error {
|
|
|
118425
118822
|
}
|
|
118426
118823
|
}
|
|
118427
118824
|
function defaultPythonCacheRoot() {
|
|
118428
|
-
return
|
|
118825
|
+
return join100(homedir52(), ".switchroom", "deps", "python");
|
|
118429
118826
|
}
|
|
118430
118827
|
function hashFile(path7) {
|
|
118431
118828
|
return createHash18("sha256").update(readFileSync88(path7)).digest("hex");
|
|
@@ -118437,11 +118834,11 @@ function ensurePythonEnv(opts) {
|
|
|
118437
118834
|
if (!existsSync96(requirementsPath)) {
|
|
118438
118835
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
118439
118836
|
}
|
|
118440
|
-
const venvDir =
|
|
118441
|
-
const stampPath =
|
|
118442
|
-
const binDir =
|
|
118443
|
-
const pythonBin =
|
|
118444
|
-
const pipBin =
|
|
118837
|
+
const venvDir = join100(cacheRoot, skillName);
|
|
118838
|
+
const stampPath = join100(venvDir, ".requirements.sha256");
|
|
118839
|
+
const binDir = join100(venvDir, "bin");
|
|
118840
|
+
const pythonBin = join100(binDir, "python");
|
|
118841
|
+
const pipBin = join100(binDir, "pip");
|
|
118445
118842
|
const targetHash = hashFile(requirementsPath);
|
|
118446
118843
|
if (!force && existsSync96(stampPath) && existsSync96(pythonBin)) {
|
|
118447
118844
|
const existingHash = readFileSync88(stampPath, "utf8").trim();
|
|
@@ -118459,7 +118856,7 @@ function ensurePythonEnv(opts) {
|
|
|
118459
118856
|
if (existsSync96(venvDir)) {
|
|
118460
118857
|
rmSync16(venvDir, { recursive: true, force: true });
|
|
118461
118858
|
}
|
|
118462
|
-
mkdirSync54(
|
|
118859
|
+
mkdirSync54(dirname44(venvDir), { recursive: true });
|
|
118463
118860
|
try {
|
|
118464
118861
|
execFileSync28(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
118465
118862
|
} catch (err) {
|
|
@@ -118500,7 +118897,7 @@ import {
|
|
|
118500
118897
|
rmSync as rmSync17,
|
|
118501
118898
|
writeFileSync as writeFileSync40
|
|
118502
118899
|
} from "node:fs";
|
|
118503
|
-
import { dirname as
|
|
118900
|
+
import { dirname as dirname45, join as join101 } from "node:path";
|
|
118504
118901
|
import { homedir as homedir53 } from "node:os";
|
|
118505
118902
|
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
118506
118903
|
|
|
@@ -118524,16 +118921,16 @@ var LOCKFILES_FOR = {
|
|
|
118524
118921
|
npm: ["package-lock.json"]
|
|
118525
118922
|
};
|
|
118526
118923
|
function defaultNodeCacheRoot() {
|
|
118527
|
-
return
|
|
118924
|
+
return join101(homedir53(), ".switchroom", "deps", "node");
|
|
118528
118925
|
}
|
|
118529
118926
|
function hashDepInputs(packageJsonPath) {
|
|
118530
|
-
const sourceDir =
|
|
118927
|
+
const sourceDir = dirname45(packageJsonPath);
|
|
118531
118928
|
const hasher = createHash19("sha256");
|
|
118532
118929
|
hasher.update(`package.json
|
|
118533
118930
|
`);
|
|
118534
118931
|
hasher.update(readFileSync89(packageJsonPath));
|
|
118535
118932
|
for (const lockName of ALL_LOCKFILES) {
|
|
118536
|
-
const lockPath =
|
|
118933
|
+
const lockPath = join101(sourceDir, lockName);
|
|
118537
118934
|
if (existsSync97(lockPath)) {
|
|
118538
118935
|
hasher.update(`
|
|
118539
118936
|
`);
|
|
@@ -118552,11 +118949,11 @@ function ensureNodeEnv(opts) {
|
|
|
118552
118949
|
if (!existsSync97(packageJsonPath)) {
|
|
118553
118950
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
118554
118951
|
}
|
|
118555
|
-
const sourceDir =
|
|
118556
|
-
const envDir =
|
|
118557
|
-
const stampPath =
|
|
118558
|
-
const nodeModulesDir =
|
|
118559
|
-
const binDir =
|
|
118952
|
+
const sourceDir = dirname45(packageJsonPath);
|
|
118953
|
+
const envDir = join101(cacheRoot, skillName);
|
|
118954
|
+
const stampPath = join101(envDir, ".package.sha256");
|
|
118955
|
+
const nodeModulesDir = join101(envDir, "node_modules");
|
|
118956
|
+
const binDir = join101(nodeModulesDir, ".bin");
|
|
118560
118957
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
118561
118958
|
if (!force && existsSync97(stampPath) && existsSync97(nodeModulesDir)) {
|
|
118562
118959
|
const existingHash = readFileSync89(stampPath, "utf8").trim();
|
|
@@ -118574,12 +118971,12 @@ function ensureNodeEnv(opts) {
|
|
|
118574
118971
|
rmSync17(envDir, { recursive: true, force: true });
|
|
118575
118972
|
}
|
|
118576
118973
|
mkdirSync55(envDir, { recursive: true });
|
|
118577
|
-
copyFileSync13(packageJsonPath,
|
|
118974
|
+
copyFileSync13(packageJsonPath, join101(envDir, "package.json"));
|
|
118578
118975
|
let copiedLockfile = false;
|
|
118579
118976
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
118580
|
-
const lockPath =
|
|
118977
|
+
const lockPath = join101(sourceDir, lockName);
|
|
118581
118978
|
if (existsSync97(lockPath)) {
|
|
118582
|
-
copyFileSync13(lockPath,
|
|
118979
|
+
copyFileSync13(lockPath, join101(envDir, lockName));
|
|
118583
118980
|
copiedLockfile = true;
|
|
118584
118981
|
}
|
|
118585
118982
|
}
|
|
@@ -118618,13 +119015,13 @@ function registerDepsCommand(program3) {
|
|
|
118618
119015
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
118619
119016
|
process.exit(1);
|
|
118620
119017
|
}
|
|
118621
|
-
const skillDir =
|
|
119018
|
+
const skillDir = join102(skillsRoot, skill);
|
|
118622
119019
|
if (!existsSync98(skillDir)) {
|
|
118623
119020
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
118624
119021
|
process.exit(1);
|
|
118625
119022
|
}
|
|
118626
|
-
const requirementsPath =
|
|
118627
|
-
const packageJsonPath =
|
|
119023
|
+
const requirementsPath = join102(skillDir, "requirements.txt");
|
|
119024
|
+
const packageJsonPath = join102(skillDir, "package.json");
|
|
118628
119025
|
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync98(requirementsPath));
|
|
118629
119026
|
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync98(packageJsonPath));
|
|
118630
119027
|
let did = 0;
|
|
@@ -119579,7 +119976,7 @@ init_helpers();
|
|
|
119579
119976
|
init_loader();
|
|
119580
119977
|
init_merge();
|
|
119581
119978
|
import { copyFileSync as copyFileSync14, existsSync as existsSync100, readFileSync as readFileSync90, writeFileSync as writeFileSync41 } from "node:fs";
|
|
119582
|
-
import { join as
|
|
119979
|
+
import { join as join103, resolve as resolve54 } from "node:path";
|
|
119583
119980
|
init_scaffold();
|
|
119584
119981
|
init_profiles();
|
|
119585
119982
|
init_schema();
|
|
@@ -119605,7 +120002,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
119605
120002
|
profileName,
|
|
119606
120003
|
profilePath,
|
|
119607
120004
|
workspaceDir,
|
|
119608
|
-
soulPath:
|
|
120005
|
+
soulPath: join103(workspaceDir, "SOUL.md"),
|
|
119609
120006
|
soul: merged.soul
|
|
119610
120007
|
};
|
|
119611
120008
|
}
|
|
@@ -119671,8 +120068,8 @@ function registerSoulCommand(program3) {
|
|
|
119671
120068
|
// src/cli/debug.ts
|
|
119672
120069
|
init_helpers();
|
|
119673
120070
|
init_loader();
|
|
119674
|
-
import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as
|
|
119675
|
-
import { resolve as resolve55, join as
|
|
120071
|
+
import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as readdirSync35, statSync as statSync57 } from "node:fs";
|
|
120072
|
+
import { resolve as resolve55, join as join104 } from "node:path";
|
|
119676
120073
|
import { createHash as createHash20 } from "node:crypto";
|
|
119677
120074
|
init_merge();
|
|
119678
120075
|
init_hindsight2();
|
|
@@ -119683,7 +120080,7 @@ function estimateTokens(bytes) {
|
|
|
119683
120080
|
return Math.round(bytes / 3.7);
|
|
119684
120081
|
}
|
|
119685
120082
|
function readMcpServerNames(agentDir) {
|
|
119686
|
-
const mcpPath =
|
|
120083
|
+
const mcpPath = join104(agentDir, ".mcp.json");
|
|
119687
120084
|
if (!existsSync101(mcpPath))
|
|
119688
120085
|
return [];
|
|
119689
120086
|
try {
|
|
@@ -119697,20 +120094,20 @@ function sha2562(content) {
|
|
|
119697
120094
|
return createHash20("sha256").update(content).digest("hex").slice(0, 16);
|
|
119698
120095
|
}
|
|
119699
120096
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
119700
|
-
const projectsDir =
|
|
120097
|
+
const projectsDir = join104(claudeConfigDir, "projects");
|
|
119701
120098
|
if (!existsSync101(projectsDir))
|
|
119702
120099
|
return;
|
|
119703
120100
|
try {
|
|
119704
|
-
const entries =
|
|
120101
|
+
const entries = readdirSync35(projectsDir, { withFileTypes: true });
|
|
119705
120102
|
let latest;
|
|
119706
120103
|
for (const entry of entries) {
|
|
119707
120104
|
if (!entry.isDirectory())
|
|
119708
120105
|
continue;
|
|
119709
|
-
const projectPath =
|
|
119710
|
-
const transcriptPath =
|
|
120106
|
+
const projectPath = join104(projectsDir, entry.name);
|
|
120107
|
+
const transcriptPath = join104(projectPath, "transcript.jsonl");
|
|
119711
120108
|
if (!existsSync101(transcriptPath))
|
|
119712
120109
|
continue;
|
|
119713
|
-
const stat3 =
|
|
120110
|
+
const stat3 = statSync57(transcriptPath);
|
|
119714
120111
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
119715
120112
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
119716
120113
|
}
|
|
@@ -119776,11 +120173,11 @@ function registerDebugCommand(program3) {
|
|
|
119776
120173
|
process.exit(1);
|
|
119777
120174
|
}
|
|
119778
120175
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
119779
|
-
const claudeConfigDir =
|
|
119780
|
-
const claudeMdPath =
|
|
119781
|
-
const soulMdPath =
|
|
119782
|
-
const workspaceSoulMdPath =
|
|
119783
|
-
const handoffPath =
|
|
120176
|
+
const claudeConfigDir = join104(agentDir, ".claude");
|
|
120177
|
+
const claudeMdPath = join104(agentDir, "CLAUDE.md");
|
|
120178
|
+
const soulMdPath = join104(agentDir, "SOUL.md");
|
|
120179
|
+
const workspaceSoulMdPath = join104(workspaceDir, "SOUL.md");
|
|
120180
|
+
const handoffPath = join104(agentDir, ".handoff.md");
|
|
119784
120181
|
const lastN = parseInt(opts.last, 10);
|
|
119785
120182
|
if (isNaN(lastN) || lastN < 1) {
|
|
119786
120183
|
console.error("--last must be a positive integer");
|
|
@@ -119909,9 +120306,9 @@ function registerDebugCommand(program3) {
|
|
|
119909
120306
|
const soulMdBytes = soulMdContent.length;
|
|
119910
120307
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
119911
120308
|
const userBytes = userMessage?.text.length ?? 0;
|
|
119912
|
-
const fleetDir =
|
|
119913
|
-
const fleetInvPath =
|
|
119914
|
-
const fleetClaudePath =
|
|
120309
|
+
const fleetDir = join104(agentsDir, "..", "fleet");
|
|
120310
|
+
const fleetInvPath = join104(fleetDir, "switchroom-invariants.md");
|
|
120311
|
+
const fleetClaudePath = join104(fleetDir, "CLAUDE.md");
|
|
119915
120312
|
const fleetInvBytes = existsSync101(fleetInvPath) ? readFileSync91(fleetInvPath, "utf-8").length : 0;
|
|
119916
120313
|
const fleetClaudeBytes = existsSync101(fleetClaudePath) ? readFileSync91(fleetClaudePath, "utf-8").length : 0;
|
|
119917
120314
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
@@ -119947,7 +120344,7 @@ init_source();
|
|
|
119947
120344
|
// src/worktree/claim.ts
|
|
119948
120345
|
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
119949
120346
|
import { closeSync as closeSync18, mkdirSync as mkdirSync57, openSync as openSync18, existsSync as existsSync103, unlinkSync as unlinkSync21 } from "node:fs";
|
|
119950
|
-
import { join as
|
|
120347
|
+
import { join as join106, resolve as resolve57 } from "node:path";
|
|
119951
120348
|
import { homedir as homedir56 } from "node:os";
|
|
119952
120349
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
119953
120350
|
|
|
@@ -119956,18 +120353,18 @@ import {
|
|
|
119956
120353
|
mkdirSync as mkdirSync56,
|
|
119957
120354
|
writeFileSync as writeFileSync42,
|
|
119958
120355
|
readFileSync as readFileSync92,
|
|
119959
|
-
readdirSync as
|
|
120356
|
+
readdirSync as readdirSync36,
|
|
119960
120357
|
unlinkSync as unlinkSync20,
|
|
119961
120358
|
existsSync as existsSync102,
|
|
119962
120359
|
renameSync as renameSync26
|
|
119963
120360
|
} from "node:fs";
|
|
119964
|
-
import { join as
|
|
120361
|
+
import { join as join105, resolve as resolve56 } from "node:path";
|
|
119965
120362
|
import { homedir as homedir55 } from "node:os";
|
|
119966
120363
|
function registryDir() {
|
|
119967
|
-
return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
120364
|
+
return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ?? join105(homedir55(), ".switchroom", "worktrees"));
|
|
119968
120365
|
}
|
|
119969
120366
|
function recordPath(id) {
|
|
119970
|
-
return
|
|
120367
|
+
return join105(registryDir(), `${id}.json`);
|
|
119971
120368
|
}
|
|
119972
120369
|
function ensureDir2() {
|
|
119973
120370
|
mkdirSync56(registryDir(), { recursive: true });
|
|
@@ -119999,7 +120396,7 @@ function listRecords() {
|
|
|
119999
120396
|
ensureDir2();
|
|
120000
120397
|
const dir = registryDir();
|
|
120001
120398
|
const records = [];
|
|
120002
|
-
for (const entry of
|
|
120399
|
+
for (const entry of readdirSync36(dir)) {
|
|
120003
120400
|
if (!entry.endsWith(".json"))
|
|
120004
120401
|
continue;
|
|
120005
120402
|
const id = entry.slice(0, -5);
|
|
@@ -120018,7 +120415,7 @@ function acquireRepoLock(repoPath) {
|
|
|
120018
120415
|
const lockDir = registryDir();
|
|
120019
120416
|
mkdirSync57(lockDir, { recursive: true });
|
|
120020
120417
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
120021
|
-
const lockPath =
|
|
120418
|
+
const lockPath = join106(lockDir, `.lock-${lockName}`);
|
|
120022
120419
|
const deadline = Date.now() + 5000;
|
|
120023
120420
|
let fd = null;
|
|
120024
120421
|
while (fd === null) {
|
|
@@ -120045,7 +120442,7 @@ function acquireRepoLock(repoPath) {
|
|
|
120045
120442
|
}
|
|
120046
120443
|
var DEFAULT_CONCURRENCY = 5;
|
|
120047
120444
|
function worktreesBaseDir() {
|
|
120048
|
-
return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
120445
|
+
return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ?? join106(homedir56(), ".switchroom", "worktree-checkouts"));
|
|
120049
120446
|
}
|
|
120050
120447
|
function shortId() {
|
|
120051
120448
|
return randomBytes16(4).toString("hex");
|
|
@@ -120067,7 +120464,7 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
120067
120464
|
}
|
|
120068
120465
|
function expandHome(p) {
|
|
120069
120466
|
if (p.startsWith("~/"))
|
|
120070
|
-
return
|
|
120467
|
+
return join106(homedir56(), p.slice(2));
|
|
120071
120468
|
return p;
|
|
120072
120469
|
}
|
|
120073
120470
|
async function claimWorktree(input, codeRepos) {
|
|
@@ -120095,7 +120492,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
120095
120492
|
branch = `task/${taskSuffix}-${id}`;
|
|
120096
120493
|
const baseDir = worktreesBaseDir();
|
|
120097
120494
|
mkdirSync57(baseDir, { recursive: true });
|
|
120098
|
-
worktreePath =
|
|
120495
|
+
worktreePath = join106(baseDir, `${id}-${taskSuffix}`);
|
|
120099
120496
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
120100
120497
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
120101
120498
|
const now2 = new Date().toISOString();
|
|
@@ -120319,14 +120716,14 @@ import { execFileSync as execFileSync33 } from "node:child_process";
|
|
|
120319
120716
|
import {
|
|
120320
120717
|
existsSync as existsSync106,
|
|
120321
120718
|
readFileSync as readFileSync93,
|
|
120322
|
-
readdirSync as
|
|
120323
|
-
statSync as
|
|
120719
|
+
readdirSync as readdirSync37,
|
|
120720
|
+
statSync as statSync58,
|
|
120324
120721
|
renameSync as renameSync27,
|
|
120325
120722
|
mkdirSync as mkdirSync58,
|
|
120326
120723
|
rmSync as rmSync18
|
|
120327
120724
|
} from "node:fs";
|
|
120328
120725
|
import { homedir as homedir57 } from "node:os";
|
|
120329
|
-
import { join as
|
|
120726
|
+
import { join as join107, resolve as resolve58 } from "node:path";
|
|
120330
120727
|
function parseGitdirPointer(dotGitFileContents) {
|
|
120331
120728
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
120332
120729
|
return m ? m[1] : null;
|
|
@@ -120485,7 +120882,7 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
|
120485
120882
|
let newest = 0;
|
|
120486
120883
|
for (const f of files) {
|
|
120487
120884
|
try {
|
|
120488
|
-
const m =
|
|
120885
|
+
const m = statSync58(join107(dir, f)).mtimeMs;
|
|
120489
120886
|
if (m > newest)
|
|
120490
120887
|
newest = m;
|
|
120491
120888
|
} catch {}
|
|
@@ -120493,17 +120890,17 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
|
120493
120890
|
return newest || Date.now();
|
|
120494
120891
|
}
|
|
120495
120892
|
function trashRoot() {
|
|
120496
|
-
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ??
|
|
120893
|
+
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join107(homedir57(), ".switchroom", "worktree-gc-trash"));
|
|
120497
120894
|
}
|
|
120498
120895
|
function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
120499
120896
|
const exists = deps.existsSync ?? existsSync106;
|
|
120500
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
120897
|
+
const readDir = deps.readDir ?? ((p) => readdirSync37(p));
|
|
120501
120898
|
const readFile4 = deps.readFile ?? ((p) => readFileSync93(p, "utf8"));
|
|
120502
|
-
const stat3 = deps.stat ?? ((p) =>
|
|
120899
|
+
const stat3 = deps.stat ?? ((p) => statSync58(p));
|
|
120503
120900
|
const exec = deps.exec ?? defaultExec3;
|
|
120504
120901
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
120505
120902
|
const stamp = deps.dateStamp ?? "undated";
|
|
120506
|
-
const trash =
|
|
120903
|
+
const trash = join107(trashRoot(), stamp);
|
|
120507
120904
|
const probeInUse = deps.probeInUse ?? probePathInUse;
|
|
120508
120905
|
const newestMtime = deps.newestTrackedMtimeMs ?? ((dir) => defaultNewestTrackedMtimeMs(dir, exec));
|
|
120509
120906
|
const idleDays = deps.idleDays ?? 14;
|
|
@@ -120544,10 +120941,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120544
120941
|
continue;
|
|
120545
120942
|
}
|
|
120546
120943
|
for (const name of entries) {
|
|
120547
|
-
const dir =
|
|
120944
|
+
const dir = join107(root, name);
|
|
120548
120945
|
if (isEphemeralPath(dir))
|
|
120549
120946
|
continue;
|
|
120550
|
-
const dotGit =
|
|
120947
|
+
const dotGit = join107(dir, ".git");
|
|
120551
120948
|
if (!exists(dotGit))
|
|
120552
120949
|
continue;
|
|
120553
120950
|
let st;
|
|
@@ -120576,7 +120973,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120576
120973
|
ownerRepos.add(repoRoot);
|
|
120577
120974
|
if (exists(ptr))
|
|
120578
120975
|
continue;
|
|
120579
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
120976
|
+
orphans.push({ dir, owner: repoRoot, dest: join107(trash, name) });
|
|
120580
120977
|
}
|
|
120581
120978
|
}
|
|
120582
120979
|
const registered = [];
|
|
@@ -120631,10 +121028,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120631
121028
|
continue;
|
|
120632
121029
|
}
|
|
120633
121030
|
for (const name of entries) {
|
|
120634
|
-
const dir =
|
|
121031
|
+
const dir = join107(root, name);
|
|
120635
121032
|
if (isEphemeralPath(dir))
|
|
120636
121033
|
continue;
|
|
120637
|
-
const dotGit =
|
|
121034
|
+
const dotGit = join107(dir, ".git");
|
|
120638
121035
|
if (!exists(dotGit))
|
|
120639
121036
|
continue;
|
|
120640
121037
|
let shape;
|
|
@@ -120739,7 +121136,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
|
120739
121136
|
verdict,
|
|
120740
121137
|
prSignal: sig,
|
|
120741
121138
|
idle,
|
|
120742
|
-
dest:
|
|
121139
|
+
dest: join107(trash, name),
|
|
120743
121140
|
willAct
|
|
120744
121141
|
});
|
|
120745
121142
|
}
|
|
@@ -120816,13 +121213,13 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
120816
121213
|
}
|
|
120817
121214
|
function listTrashEntries(nowMs, deps = {}) {
|
|
120818
121215
|
const exists = deps.existsSync ?? existsSync106;
|
|
120819
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
121216
|
+
const readDir = deps.readDir ?? ((p) => readdirSync37(p));
|
|
120820
121217
|
const root = trashRoot();
|
|
120821
121218
|
if (!exists(root))
|
|
120822
121219
|
return [];
|
|
120823
121220
|
const out = [];
|
|
120824
121221
|
for (const stamp of readDir(root)) {
|
|
120825
|
-
const stampDir =
|
|
121222
|
+
const stampDir = join107(root, stamp);
|
|
120826
121223
|
let names;
|
|
120827
121224
|
try {
|
|
120828
121225
|
names = readDir(stampDir);
|
|
@@ -120830,10 +121227,10 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
120830
121227
|
continue;
|
|
120831
121228
|
}
|
|
120832
121229
|
for (const name of names) {
|
|
120833
|
-
const p =
|
|
121230
|
+
const p = join107(stampDir, name);
|
|
120834
121231
|
let mtimeMs = nowMs;
|
|
120835
121232
|
try {
|
|
120836
|
-
mtimeMs =
|
|
121233
|
+
mtimeMs = statSync58(p).mtimeMs;
|
|
120837
121234
|
} catch {}
|
|
120838
121235
|
out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
|
|
120839
121236
|
}
|
|
@@ -120854,20 +121251,20 @@ function purgeTrash(paths) {
|
|
|
120854
121251
|
return { deleted, errors: errors2 };
|
|
120855
121252
|
}
|
|
120856
121253
|
function defaultRoots() {
|
|
120857
|
-
return [
|
|
121254
|
+
return [join107(homedir57(), "code")];
|
|
120858
121255
|
}
|
|
120859
121256
|
function defaultTaskTreeRoots() {
|
|
120860
|
-
const agentsDir =
|
|
121257
|
+
const agentsDir = join107(homedir57(), ".switchroom", "agents");
|
|
120861
121258
|
let names;
|
|
120862
121259
|
try {
|
|
120863
|
-
names =
|
|
121260
|
+
names = readdirSync37(agentsDir);
|
|
120864
121261
|
} catch {
|
|
120865
121262
|
return [];
|
|
120866
121263
|
}
|
|
120867
121264
|
const roots = [];
|
|
120868
121265
|
for (const name of names.sort()) {
|
|
120869
121266
|
for (const sub of ["home/work", "home/workspace"]) {
|
|
120870
|
-
const r =
|
|
121267
|
+
const r = join107(agentsDir, name, sub);
|
|
120871
121268
|
if (existsSync106(r))
|
|
120872
121269
|
roots.push(r);
|
|
120873
121270
|
}
|
|
@@ -121128,11 +121525,11 @@ init_scaffold_integration();
|
|
|
121128
121525
|
import {
|
|
121129
121526
|
chmodSync as chmodSync17,
|
|
121130
121527
|
mkdirSync as mkdirSync59,
|
|
121131
|
-
readdirSync as
|
|
121528
|
+
readdirSync as readdirSync38,
|
|
121132
121529
|
rmSync as rmSync19,
|
|
121133
121530
|
writeFileSync as writeFileSync43
|
|
121134
121531
|
} from "node:fs";
|
|
121135
|
-
import { join as
|
|
121532
|
+
import { join as join108 } from "node:path";
|
|
121136
121533
|
function encodeCredentialsFilename(email) {
|
|
121137
121534
|
const SAFE = new Set([
|
|
121138
121535
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -121396,16 +121793,16 @@ function resolveCredentialsDir(env2) {
|
|
|
121396
121793
|
if (explicit && explicit.length > 0)
|
|
121397
121794
|
return explicit;
|
|
121398
121795
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
121399
|
-
return
|
|
121796
|
+
return join108(stateBase, "google-workspace-mcp", "credentials");
|
|
121400
121797
|
}
|
|
121401
121798
|
function writeSeedFile(dir, email, seed) {
|
|
121402
121799
|
mkdirSync59(dir, { recursive: true, mode: 448 });
|
|
121403
121800
|
chmodSync17(dir, 448);
|
|
121404
|
-
for (const name of
|
|
121405
|
-
rmSync19(
|
|
121801
|
+
for (const name of readdirSync38(dir)) {
|
|
121802
|
+
rmSync19(join108(dir, name), { force: true, recursive: true });
|
|
121406
121803
|
}
|
|
121407
121804
|
const filename = encodeCredentialsFilename(email);
|
|
121408
|
-
const filePath =
|
|
121805
|
+
const filePath = join108(dir, filename);
|
|
121409
121806
|
writeFileSync43(filePath, JSON.stringify(seed), { mode: 384 });
|
|
121410
121807
|
chmodSync17(filePath, 384);
|
|
121411
121808
|
return filePath;
|
|
@@ -121602,7 +121999,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
121602
121999
|
init_scaffold_integration();
|
|
121603
122000
|
import { spawn as spawn6 } from "node:child_process";
|
|
121604
122001
|
import { writeFileSync as writeFileSync44, mkdirSync as mkdirSync60 } from "node:fs";
|
|
121605
|
-
import { dirname as
|
|
122002
|
+
import { dirname as dirname46, join as join109 } from "node:path";
|
|
121606
122003
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
121607
122004
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
121608
122005
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -121638,7 +122035,7 @@ function computeRefreshDelayMs(expiresAt, now2, leadMs = DEFAULT_REFRESH_LEAD_MS
|
|
|
121638
122035
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
121639
122036
|
const path10 = heartbeatPath(agentName, account);
|
|
121640
122037
|
try {
|
|
121641
|
-
mkdirSync60(
|
|
122038
|
+
mkdirSync60(dirname46(path10), { recursive: true });
|
|
121642
122039
|
writeFileSync44(path10, JSON.stringify(data, null, 2), { mode: 420 });
|
|
121643
122040
|
} catch {}
|
|
121644
122041
|
}
|
|
@@ -121647,7 +122044,7 @@ function heartbeatPath(agentName, account) {
|
|
|
121647
122044
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
121648
122045
|
if (override) {
|
|
121649
122046
|
const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
|
|
121650
|
-
return
|
|
122047
|
+
return join109(override, `${base}.heartbeat.json`);
|
|
121651
122048
|
}
|
|
121652
122049
|
return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
|
|
121653
122050
|
}
|
|
@@ -121880,7 +122277,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
121880
122277
|
init_scaffold_integration();
|
|
121881
122278
|
import { spawn as spawn7 } from "node:child_process";
|
|
121882
122279
|
import { existsSync as existsSync107, mkdirSync as mkdirSync61, writeFileSync as writeFileSync45 } from "node:fs";
|
|
121883
|
-
import { dirname as
|
|
122280
|
+
import { dirname as dirname47 } from "node:path";
|
|
121884
122281
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
121885
122282
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
121886
122283
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -121890,7 +122287,7 @@ function buildNotionMcpArgs(opts) {
|
|
|
121890
122287
|
}
|
|
121891
122288
|
function defaultWriteHeartbeat(path10, contents) {
|
|
121892
122289
|
try {
|
|
121893
|
-
const dir =
|
|
122290
|
+
const dir = dirname47(path10);
|
|
121894
122291
|
if (!existsSync107(dir))
|
|
121895
122292
|
mkdirSync61(dir, { recursive: true });
|
|
121896
122293
|
writeFileSync45(path10, contents);
|
|
@@ -122005,7 +122402,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
122005
122402
|
init_hindsight_directive_admin();
|
|
122006
122403
|
import { mkdirSync as mkdirSync62, readFileSync as readFileSync94, renameSync as renameSync28, writeFileSync as writeFileSync46 } from "node:fs";
|
|
122007
122404
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
122008
|
-
import { join as
|
|
122405
|
+
import { join as join110 } from "node:path";
|
|
122009
122406
|
import { createInterface as createInterface7 } from "node:readline";
|
|
122010
122407
|
|
|
122011
122408
|
// src/memory/hindsight-knowledge-admin.ts
|
|
@@ -122593,12 +122990,12 @@ class HindsightShim {
|
|
|
122593
122990
|
`));
|
|
122594
122991
|
}
|
|
122595
122992
|
get cachePath() {
|
|
122596
|
-
return
|
|
122993
|
+
return join110(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
|
|
122597
122994
|
}
|
|
122598
122995
|
writeCache(result) {
|
|
122599
122996
|
try {
|
|
122600
122997
|
mkdirSync62(this.opts.cacheDir, { recursive: true });
|
|
122601
|
-
const tmp =
|
|
122998
|
+
const tmp = join110(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
122602
122999
|
writeFileSync46(tmp, JSON.stringify(result, null, 2) + `
|
|
122603
123000
|
`);
|
|
122604
123001
|
renameSync28(tmp, this.cachePath);
|
|
@@ -122882,7 +123279,7 @@ function resolveShimOptionsFromEnv(env2) {
|
|
|
122882
123279
|
return {
|
|
122883
123280
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
122884
123281
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
122885
|
-
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR ||
|
|
123282
|
+
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join110(home2, ".hindsight-shim")
|
|
122886
123283
|
};
|
|
122887
123284
|
}
|
|
122888
123285
|
function registerHindsightMcpShimCommand(program3) {
|
|
@@ -122895,11 +123292,11 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
122895
123292
|
|
|
122896
123293
|
// src/cli/deliver-file.ts
|
|
122897
123294
|
init_client2();
|
|
122898
|
-
import { readFileSync as readFileSync95, statSync as
|
|
122899
|
-
import { basename as
|
|
123295
|
+
import { readFileSync as readFileSync95, statSync as statSync59 } from "node:fs";
|
|
123296
|
+
import { basename as basename14 } from "node:path";
|
|
122900
123297
|
|
|
122901
123298
|
// src/delivery/onedrive.ts
|
|
122902
|
-
import { basename as
|
|
123299
|
+
import { basename as basename12 } from "node:path";
|
|
122903
123300
|
var GRAPH = "https://graph.microsoft.com/v1.0";
|
|
122904
123301
|
var ONEDRIVE_INLINE_MAX_BYTES = 4 * 1024 * 1024;
|
|
122905
123302
|
function authHeaders2(token) {
|
|
@@ -123022,14 +123419,14 @@ async function createShareLink(deps, item, scopes = ["anonymous", "organization"
|
|
|
123022
123419
|
async function deliverToOneDrive(args) {
|
|
123023
123420
|
const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
|
|
123024
123421
|
const folder = await ensureSwitchroomFolder(deps, args.agentName);
|
|
123025
|
-
const filename =
|
|
123422
|
+
const filename = basename12(args.localPath);
|
|
123026
123423
|
const item = await uploadFile(deps, folder.id, filename, args.bytes);
|
|
123027
123424
|
const link = await createShareLink(deps, item, args.linkScopes);
|
|
123028
123425
|
return { itemId: item.id, link, folderPath: `Switchroom/${args.agentName}` };
|
|
123029
123426
|
}
|
|
123030
123427
|
|
|
123031
123428
|
// src/delivery/gdrive.ts
|
|
123032
|
-
import { basename as
|
|
123429
|
+
import { basename as basename13 } from "node:path";
|
|
123033
123430
|
var DRIVE = "https://www.googleapis.com/drive/v3";
|
|
123034
123431
|
var UPLOAD = "https://www.googleapis.com/upload/drive/v3";
|
|
123035
123432
|
var FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
@@ -123177,7 +123574,7 @@ async function createShareLink2(deps, file, scopes = ["anyone"]) {
|
|
|
123177
123574
|
async function deliverToGoogleDrive(args) {
|
|
123178
123575
|
const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
|
|
123179
123576
|
const folder = await ensureSwitchroomFolder2(deps, args.agentName);
|
|
123180
|
-
const filename =
|
|
123577
|
+
const filename = basename13(args.localPath);
|
|
123181
123578
|
const file = await uploadFile2(deps, folder.id, filename, args.bytes);
|
|
123182
123579
|
const link = await createShareLink2(deps, file, args.linkScopes);
|
|
123183
123580
|
return { itemId: file.id, link, folderPath: `Switchroom/${args.agentName}` };
|
|
@@ -123234,7 +123631,7 @@ async function defaultResolveProvider() {
|
|
|
123234
123631
|
}
|
|
123235
123632
|
async function runDeliverFile(localPath, deps = {}) {
|
|
123236
123633
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
123237
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
123634
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync59(p).size);
|
|
123238
123635
|
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync95(p)));
|
|
123239
123636
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
123240
123637
|
let size;
|
|
@@ -123256,7 +123653,7 @@ async function runDeliverFile(localPath, deps = {}) {
|
|
|
123256
123653
|
try {
|
|
123257
123654
|
const bytes = read(localPath);
|
|
123258
123655
|
const out = await provider.deliver({ agentName, localPath, bytes });
|
|
123259
|
-
return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename:
|
|
123656
|
+
return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename14(localPath) };
|
|
123260
123657
|
} catch (err) {
|
|
123261
123658
|
return { ok: false, provider: provider.name, error: `upload failed: ${err.message}` };
|
|
123262
123659
|
}
|
|
@@ -123556,8 +123953,8 @@ function runRedactStdin() {
|
|
|
123556
123953
|
}
|
|
123557
123954
|
|
|
123558
123955
|
// src/cli/status-ask.ts
|
|
123559
|
-
import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as
|
|
123560
|
-
import { join as
|
|
123956
|
+
import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as readdirSync39 } from "node:fs";
|
|
123957
|
+
import { join as join111 } from "node:path";
|
|
123561
123958
|
import { homedir as homedir58 } from "node:os";
|
|
123562
123959
|
|
|
123563
123960
|
// src/status-ask/report.ts
|
|
@@ -123893,19 +124290,19 @@ function resolveSources(explicitPath) {
|
|
|
123893
124290
|
const config = loadConfig();
|
|
123894
124291
|
agentsDir = resolveAgentsDir(config);
|
|
123895
124292
|
} catch {
|
|
123896
|
-
agentsDir =
|
|
124293
|
+
agentsDir = join111(homedir58(), ".switchroom", "agents");
|
|
123897
124294
|
}
|
|
123898
124295
|
if (!existsSync108(agentsDir))
|
|
123899
124296
|
return [];
|
|
123900
124297
|
const sources = [];
|
|
123901
124298
|
let entries;
|
|
123902
124299
|
try {
|
|
123903
|
-
entries =
|
|
124300
|
+
entries = readdirSync39(agentsDir);
|
|
123904
124301
|
} catch {
|
|
123905
124302
|
return [];
|
|
123906
124303
|
}
|
|
123907
124304
|
for (const name of entries) {
|
|
123908
|
-
const path10 =
|
|
124305
|
+
const path10 = join111(agentsDir, name, "runtime-metrics.jsonl");
|
|
123909
124306
|
if (existsSync108(path10)) {
|
|
123910
124307
|
sources.push({ path: path10, agent: name });
|
|
123911
124308
|
}
|
|
@@ -123941,28 +124338,28 @@ import {
|
|
|
123941
124338
|
fsyncSync as fsyncSync8,
|
|
123942
124339
|
mkdirSync as mkdirSync63,
|
|
123943
124340
|
openSync as openSync19,
|
|
123944
|
-
readdirSync as
|
|
124341
|
+
readdirSync as readdirSync40,
|
|
123945
124342
|
readFileSync as readFileSync97,
|
|
123946
124343
|
renameSync as renameSync29,
|
|
123947
|
-
statSync as
|
|
124344
|
+
statSync as statSync60,
|
|
123948
124345
|
unlinkSync as unlinkSync22,
|
|
123949
124346
|
writeSync as writeSync10
|
|
123950
124347
|
} from "node:fs";
|
|
123951
|
-
import { join as
|
|
124348
|
+
import { join as join112, resolve as resolve59 } from "node:path";
|
|
123952
124349
|
var STAGING_SUBDIR = ".staging";
|
|
123953
124350
|
function overlayPathsFor(agent, opts = {}) {
|
|
123954
124351
|
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 =
|
|
124352
|
+
const scheduleDir = join112(base, "schedule.d");
|
|
124353
|
+
const scheduleStagingDir = join112(scheduleDir, STAGING_SUBDIR);
|
|
124354
|
+
const skillsDir = join112(base, "skills.d");
|
|
124355
|
+
const skillsStagingDir = join112(skillsDir, STAGING_SUBDIR);
|
|
123959
124356
|
return {
|
|
123960
124357
|
agentRoot: base,
|
|
123961
124358
|
scheduleDir,
|
|
123962
124359
|
scheduleStagingDir,
|
|
123963
124360
|
skillsDir,
|
|
123964
124361
|
skillsStagingDir,
|
|
123965
|
-
lockPath:
|
|
124362
|
+
lockPath: join112(base, ".lock"),
|
|
123966
124363
|
stagingDir: scheduleStagingDir
|
|
123967
124364
|
};
|
|
123968
124365
|
}
|
|
@@ -123988,7 +124385,7 @@ function withAgentLock(paths, fn) {
|
|
|
123988
124385
|
if (e.code !== "EEXIST")
|
|
123989
124386
|
throw err;
|
|
123990
124387
|
try {
|
|
123991
|
-
const age = Date.now() -
|
|
124388
|
+
const age = Date.now() - statSync60(paths.lockPath).mtimeMs;
|
|
123992
124389
|
if (age > 30000) {
|
|
123993
124390
|
unlinkSync22(paths.lockPath);
|
|
123994
124391
|
continue;
|
|
@@ -124021,7 +124418,7 @@ function alignStagedOwnerToDir(stagingPath, targetDir) {
|
|
|
124021
124418
|
const euid = overlayWriterRuntime.geteuid();
|
|
124022
124419
|
if (euid === undefined)
|
|
124023
124420
|
return;
|
|
124024
|
-
const dirStat =
|
|
124421
|
+
const dirStat = statSync60(targetDir);
|
|
124025
124422
|
if (dirStat.uid === euid)
|
|
124026
124423
|
return;
|
|
124027
124424
|
overlayWriterRuntime.chown(stagingPath, dirStat.uid, dirStat.gid);
|
|
@@ -124031,8 +124428,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124031
124428
|
const paths = overlayPathsFor(agent, opts);
|
|
124032
124429
|
return withAgentLock(paths, () => {
|
|
124033
124430
|
ensureDirs(paths);
|
|
124034
|
-
const stagingPath =
|
|
124035
|
-
const finalPath =
|
|
124431
|
+
const stagingPath = join112(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
124432
|
+
const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
|
|
124036
124433
|
const fd = openSync19(stagingPath, "w", 384);
|
|
124037
124434
|
try {
|
|
124038
124435
|
writeSync10(fd, yamlText);
|
|
@@ -124049,8 +124446,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124049
124446
|
const paths = overlayPathsFor(agent, opts);
|
|
124050
124447
|
return withAgentLock(paths, () => {
|
|
124051
124448
|
ensureSkillsDirs(paths);
|
|
124052
|
-
const stagingPath =
|
|
124053
|
-
const finalPath =
|
|
124449
|
+
const stagingPath = join112(paths.skillsStagingDir, `${slug}.yaml`);
|
|
124450
|
+
const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
|
|
124054
124451
|
const fd = openSync19(stagingPath, "w", 384);
|
|
124055
124452
|
try {
|
|
124056
124453
|
writeSync10(fd, yamlText);
|
|
@@ -124066,7 +124463,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
124066
124463
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
124067
124464
|
const paths = overlayPathsFor(agent, opts);
|
|
124068
124465
|
return withAgentLock(paths, () => {
|
|
124069
|
-
const finalPath =
|
|
124466
|
+
const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
|
|
124070
124467
|
if (!existsSync109(finalPath))
|
|
124071
124468
|
return false;
|
|
124072
124469
|
unlinkSync22(finalPath);
|
|
@@ -124078,10 +124475,10 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
124078
124475
|
if (!existsSync109(paths.skillsDir))
|
|
124079
124476
|
return [];
|
|
124080
124477
|
const out = [];
|
|
124081
|
-
for (const name of
|
|
124478
|
+
for (const name of readdirSync40(paths.skillsDir)) {
|
|
124082
124479
|
if (!/\.ya?ml$/i.test(name))
|
|
124083
124480
|
continue;
|
|
124084
|
-
const full =
|
|
124481
|
+
const full = join112(paths.skillsDir, name);
|
|
124085
124482
|
try {
|
|
124086
124483
|
const raw = readFileSync97(full, "utf-8");
|
|
124087
124484
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -124093,7 +124490,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
124093
124490
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
124094
124491
|
const paths = overlayPathsFor(agent, opts);
|
|
124095
124492
|
return withAgentLock(paths, () => {
|
|
124096
|
-
const finalPath =
|
|
124493
|
+
const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
|
|
124097
124494
|
if (!existsSync109(finalPath))
|
|
124098
124495
|
return false;
|
|
124099
124496
|
unlinkSync22(finalPath);
|
|
@@ -124105,10 +124502,10 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
124105
124502
|
if (!existsSync109(paths.scheduleDir))
|
|
124106
124503
|
return [];
|
|
124107
124504
|
const out = [];
|
|
124108
|
-
for (const name of
|
|
124505
|
+
for (const name of readdirSync40(paths.scheduleDir)) {
|
|
124109
124506
|
if (!/\.ya?ml$/i.test(name))
|
|
124110
124507
|
continue;
|
|
124111
|
-
const full =
|
|
124508
|
+
const full = join112(paths.scheduleDir, name);
|
|
124112
124509
|
try {
|
|
124113
124510
|
const raw = readFileSync97(full, "utf-8");
|
|
124114
124511
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -124334,19 +124731,19 @@ import {
|
|
|
124334
124731
|
fsyncSync as fsyncSync9,
|
|
124335
124732
|
mkdirSync as mkdirSync64,
|
|
124336
124733
|
openSync as openSync20,
|
|
124337
|
-
readdirSync as
|
|
124734
|
+
readdirSync as readdirSync41,
|
|
124338
124735
|
readFileSync as readFileSync98,
|
|
124339
124736
|
renameSync as renameSync30,
|
|
124340
124737
|
unlinkSync as unlinkSync23,
|
|
124341
124738
|
writeFileSync as writeFileSync47,
|
|
124342
124739
|
writeSync as writeSync11
|
|
124343
124740
|
} from "node:fs";
|
|
124344
|
-
import { join as
|
|
124741
|
+
import { join as join113 } from "node:path";
|
|
124345
124742
|
import { randomBytes as randomBytes17 } from "node:crypto";
|
|
124346
124743
|
var STAGE_ID_PREFIX = "cap_";
|
|
124347
124744
|
function pendingDir(agent, opts = {}) {
|
|
124348
124745
|
const paths = overlayPathsFor(agent, opts);
|
|
124349
|
-
return
|
|
124746
|
+
return join113(paths.scheduleDir, ".pending");
|
|
124350
124747
|
}
|
|
124351
124748
|
function ensurePendingDir(agent, opts = {}) {
|
|
124352
124749
|
const dir = pendingDir(agent, opts);
|
|
@@ -124359,8 +124756,8 @@ function newStageId() {
|
|
|
124359
124756
|
function stagePendingScheduleEntry(opts) {
|
|
124360
124757
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
124361
124758
|
const stageId = opts.stageId ?? newStageId();
|
|
124362
|
-
const yamlPath =
|
|
124363
|
-
const metaPath =
|
|
124759
|
+
const yamlPath = join113(dir, `${stageId}.yaml`);
|
|
124760
|
+
const metaPath = join113(dir, `${stageId}.meta.json`);
|
|
124364
124761
|
const meta = {
|
|
124365
124762
|
v: 1,
|
|
124366
124763
|
stage_id: stageId,
|
|
@@ -124390,12 +124787,12 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
124390
124787
|
if (!existsSync110(dir))
|
|
124391
124788
|
return [];
|
|
124392
124789
|
const out = [];
|
|
124393
|
-
for (const name of
|
|
124790
|
+
for (const name of readdirSync41(dir).sort()) {
|
|
124394
124791
|
if (!name.endsWith(".meta.json"))
|
|
124395
124792
|
continue;
|
|
124396
124793
|
const stageId = name.slice(0, -".meta.json".length);
|
|
124397
|
-
const metaPath =
|
|
124398
|
-
const yamlPath =
|
|
124794
|
+
const metaPath = join113(dir, name);
|
|
124795
|
+
const yamlPath = join113(dir, `${stageId}.yaml`);
|
|
124399
124796
|
if (!existsSync110(yamlPath))
|
|
124400
124797
|
continue;
|
|
124401
124798
|
try {
|
|
@@ -124414,7 +124811,7 @@ function commitPendingScheduleEntry(opts) {
|
|
|
124414
124811
|
return { committed: false, reason: "not_found" };
|
|
124415
124812
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
124416
124813
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
124417
|
-
const finalPath =
|
|
124814
|
+
const finalPath = join113(paths.scheduleDir, `${slug}.yaml`);
|
|
124418
124815
|
if (existsSync110(finalPath)) {
|
|
124419
124816
|
return { committed: false, reason: "slug_collision" };
|
|
124420
124817
|
}
|
|
@@ -125067,7 +125464,7 @@ import { existsSync as existsSync112 } from "node:fs";
|
|
|
125067
125464
|
init_reconcile_default_skills();
|
|
125068
125465
|
init_agent_config();
|
|
125069
125466
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
125070
|
-
import { join as
|
|
125467
|
+
import { join as join114 } from "node:path";
|
|
125071
125468
|
var MAX_SKILLS_PER_AGENT2 = 20;
|
|
125072
125469
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
125073
125470
|
function exitCodeFor2(code) {
|
|
@@ -125142,7 +125539,7 @@ function skillInstall(opts) {
|
|
|
125142
125539
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT2})`);
|
|
125143
125540
|
}
|
|
125144
125541
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
125145
|
-
const skillPath =
|
|
125542
|
+
const skillPath = join114(poolDir, skillName);
|
|
125146
125543
|
if (!existsSync112(skillPath)) {
|
|
125147
125544
|
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
125545
|
}
|
|
@@ -125314,20 +125711,20 @@ import {
|
|
|
125314
125711
|
chmodSync as chmodSync18,
|
|
125315
125712
|
closeSync as closeSync21,
|
|
125316
125713
|
existsSync as existsSync113,
|
|
125317
|
-
lstatSync as
|
|
125714
|
+
lstatSync as lstatSync15,
|
|
125318
125715
|
mkdirSync as mkdirSync65,
|
|
125319
125716
|
mkdtempSync as mkdtempSync5,
|
|
125320
125717
|
openSync as openSync21,
|
|
125321
125718
|
readFileSync as readFileSync100,
|
|
125322
|
-
readdirSync as
|
|
125719
|
+
readdirSync as readdirSync42,
|
|
125323
125720
|
realpathSync as realpathSync9,
|
|
125324
125721
|
renameSync as renameSync31,
|
|
125325
125722
|
rmSync as rmSync20,
|
|
125326
|
-
statSync as
|
|
125723
|
+
statSync as statSync61,
|
|
125327
125724
|
writeFileSync as writeFileSync48
|
|
125328
125725
|
} from "node:fs";
|
|
125329
125726
|
import { tmpdir as tmpdir6, homedir as homedir59 } from "node:os";
|
|
125330
|
-
import { dirname as
|
|
125727
|
+
import { dirname as dirname48, join as join115, relative as relative6, resolve as resolve60 } from "node:path";
|
|
125331
125728
|
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
125332
125729
|
var SKILL_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
125333
125730
|
var SH_SCRIPT_RE2 = /^scripts\/[A-Za-z0-9_.-]+\.sh$/;
|
|
@@ -125341,7 +125738,7 @@ function scanForClaudeP2(content) {
|
|
|
125341
125738
|
function resolveSkillsPoolDir2(override) {
|
|
125342
125739
|
const raw = override ?? "~/.switchroom/skills";
|
|
125343
125740
|
if (raw.startsWith("~/")) {
|
|
125344
|
-
return
|
|
125741
|
+
return join115(homedir59(), raw.slice(2));
|
|
125345
125742
|
}
|
|
125346
125743
|
if (raw === "~")
|
|
125347
125744
|
return homedir59();
|
|
@@ -125373,14 +125770,14 @@ function isTarballPath(p) {
|
|
|
125373
125770
|
}
|
|
125374
125771
|
function loadFromDir(dir) {
|
|
125375
125772
|
const abs = realpathSync9(dir);
|
|
125376
|
-
if (!
|
|
125773
|
+
if (!statSync61(abs).isDirectory()) {
|
|
125377
125774
|
fail4(`--from path is not a directory: ${dir}`);
|
|
125378
125775
|
}
|
|
125379
125776
|
const files = {};
|
|
125380
125777
|
const walk2 = (sub) => {
|
|
125381
|
-
const entries =
|
|
125778
|
+
const entries = readdirSync42(sub, { withFileTypes: true });
|
|
125382
125779
|
for (const ent of entries) {
|
|
125383
|
-
const full =
|
|
125780
|
+
const full = join115(sub, ent.name);
|
|
125384
125781
|
const rel = relative6(abs, full);
|
|
125385
125782
|
if (ent.isSymbolicLink()) {
|
|
125386
125783
|
fail4(`refusing to read symlink inside --from dir: ${rel}`);
|
|
@@ -125415,7 +125812,7 @@ function loadFromTarball(tarPath) {
|
|
|
125415
125812
|
fail4(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
125416
125813
|
}
|
|
125417
125814
|
}
|
|
125418
|
-
const staging = mkdtempSync5(
|
|
125815
|
+
const staging = mkdtempSync5(join115(tmpdir6(), "skill-apply-extract-"));
|
|
125419
125816
|
try {
|
|
125420
125817
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
125421
125818
|
const r = spawnSync24("tar", [
|
|
@@ -125501,8 +125898,8 @@ function validatePayload(name, files) {
|
|
|
125501
125898
|
errors2.push(`${path10} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
125502
125899
|
}
|
|
125503
125900
|
} else if (PY_SCRIPT_RE2.test(path10)) {
|
|
125504
|
-
const tmp = mkdtempSync5(
|
|
125505
|
-
const tmpPy =
|
|
125901
|
+
const tmp = mkdtempSync5(join115(tmpdir6(), "skill-apply-py-"));
|
|
125902
|
+
const tmpPy = join115(tmp, "check.py");
|
|
125506
125903
|
try {
|
|
125507
125904
|
writeFileSync48(tmpPy, content);
|
|
125508
125905
|
const r = spawnSync24("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -125524,8 +125921,8 @@ function diffSummary(currentDir, files) {
|
|
|
125524
125921
|
const currentFiles = {};
|
|
125525
125922
|
if (existsSync113(currentDir)) {
|
|
125526
125923
|
const walk2 = (sub) => {
|
|
125527
|
-
for (const ent of
|
|
125528
|
-
const full =
|
|
125924
|
+
for (const ent of readdirSync42(sub, { withFileTypes: true })) {
|
|
125925
|
+
const full = join115(sub, ent.name);
|
|
125529
125926
|
const rel = relative6(currentDir, full);
|
|
125530
125927
|
if (ent.isDirectory()) {
|
|
125531
125928
|
walk2(full);
|
|
@@ -125561,10 +125958,10 @@ function writePayload(poolDir, name, files) {
|
|
|
125561
125958
|
if (!existsSync113(poolDir)) {
|
|
125562
125959
|
mkdirSync65(poolDir, { recursive: true, mode: 493 });
|
|
125563
125960
|
}
|
|
125564
|
-
const target =
|
|
125961
|
+
const target = join115(poolDir, name);
|
|
125565
125962
|
let targetIsSymlink = false;
|
|
125566
125963
|
try {
|
|
125567
|
-
const st =
|
|
125964
|
+
const st = lstatSync15(target);
|
|
125568
125965
|
if (st.isSymbolicLink()) {
|
|
125569
125966
|
targetIsSymlink = true;
|
|
125570
125967
|
}
|
|
@@ -125572,13 +125969,13 @@ function writePayload(poolDir, name, files) {
|
|
|
125572
125969
|
if (targetIsSymlink) {
|
|
125573
125970
|
fail4(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
125574
125971
|
}
|
|
125575
|
-
const staging = mkdtempSync5(
|
|
125972
|
+
const staging = mkdtempSync5(join115(poolDir, `.skill-apply-stage-${name}-`));
|
|
125576
125973
|
chmodSync18(staging, 493);
|
|
125577
125974
|
let oldRename = null;
|
|
125578
125975
|
try {
|
|
125579
125976
|
for (const [path10, content] of Object.entries(files)) {
|
|
125580
|
-
const full =
|
|
125581
|
-
mkdirSync65(
|
|
125977
|
+
const full = join115(staging, path10);
|
|
125978
|
+
mkdirSync65(dirname48(full), { recursive: true, mode: 493 });
|
|
125582
125979
|
const fd = openSync21(full, "wx");
|
|
125583
125980
|
try {
|
|
125584
125981
|
writeFileSync48(fd, content);
|
|
@@ -125592,7 +125989,7 @@ function writePayload(poolDir, name, files) {
|
|
|
125592
125989
|
}
|
|
125593
125990
|
let targetExists = false;
|
|
125594
125991
|
try {
|
|
125595
|
-
|
|
125992
|
+
lstatSync15(target);
|
|
125596
125993
|
targetExists = true;
|
|
125597
125994
|
} catch {}
|
|
125598
125995
|
if (targetExists) {
|
|
@@ -125634,7 +126031,7 @@ function registerSkillCommand(program3) {
|
|
|
125634
126031
|
if (!existsSync113(fromPath)) {
|
|
125635
126032
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
125636
126033
|
}
|
|
125637
|
-
const st =
|
|
126034
|
+
const st = statSync61(fromPath);
|
|
125638
126035
|
if (st.isDirectory()) {
|
|
125639
126036
|
files = loadFromDir(fromPath);
|
|
125640
126037
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -125655,7 +126052,7 @@ function registerSkillCommand(program3) {
|
|
|
125655
126052
|
}
|
|
125656
126053
|
const config = loadConfig();
|
|
125657
126054
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
125658
|
-
const currentDir =
|
|
126055
|
+
const currentDir = join115(poolDir, name);
|
|
125659
126056
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
125660
126057
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
125661
126058
|
console.log(diffSummary(currentDir, files));
|
|
@@ -125693,19 +126090,19 @@ import {
|
|
|
125693
126090
|
chmodSync as chmodSync19,
|
|
125694
126091
|
closeSync as closeSync22,
|
|
125695
126092
|
existsSync as existsSync114,
|
|
125696
|
-
lstatSync as
|
|
126093
|
+
lstatSync as lstatSync16,
|
|
125697
126094
|
mkdirSync as mkdirSync66,
|
|
125698
126095
|
mkdtempSync as mkdtempSync6,
|
|
125699
126096
|
openSync as openSync22,
|
|
125700
126097
|
readFileSync as readFileSync101,
|
|
125701
|
-
readdirSync as
|
|
126098
|
+
readdirSync as readdirSync43,
|
|
125702
126099
|
renameSync as renameSync32,
|
|
125703
126100
|
rmSync as rmSync21,
|
|
125704
|
-
statSync as
|
|
126101
|
+
statSync as statSync62,
|
|
125705
126102
|
utimesSync,
|
|
125706
126103
|
writeFileSync as writeFileSync49
|
|
125707
126104
|
} from "node:fs";
|
|
125708
|
-
import { dirname as
|
|
126105
|
+
import { dirname as dirname49, join as join116, relative as relative7, resolve as resolve61 } from "node:path";
|
|
125709
126106
|
import { homedir as homedir60, tmpdir as tmpdir7 } from "node:os";
|
|
125710
126107
|
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
125711
126108
|
var PERSONAL_PREFIX = "personal-";
|
|
@@ -125714,10 +126111,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
125714
126111
|
var PERSONAL_SKILLS_SUBPATH2 = "personal-skills";
|
|
125715
126112
|
function resolveConfigSkillsDir(agent) {
|
|
125716
126113
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
125717
|
-
const candidate = override ? resolve61(override) :
|
|
126114
|
+
const candidate = override ? resolve61(override) : join116(homedir60(), ".switchroom-config");
|
|
125718
126115
|
if (!existsSync114(candidate))
|
|
125719
126116
|
return null;
|
|
125720
|
-
return
|
|
126117
|
+
return join116(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH2);
|
|
125721
126118
|
}
|
|
125722
126119
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
125723
126120
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
@@ -125725,7 +126122,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
125725
126122
|
if (!existsSync114(configSkillsRoot))
|
|
125726
126123
|
return;
|
|
125727
126124
|
const now2 = Date.now();
|
|
125728
|
-
for (const ent of
|
|
126125
|
+
for (const ent of readdirSync43(configSkillsRoot)) {
|
|
125729
126126
|
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
125730
126127
|
if (!m)
|
|
125731
126128
|
continue;
|
|
@@ -125735,7 +126132,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
125735
126132
|
if (now2 - ts < MIRROR_PRIOR_TTL_MS)
|
|
125736
126133
|
continue;
|
|
125737
126134
|
try {
|
|
125738
|
-
rmSync21(
|
|
126135
|
+
rmSync21(join116(configSkillsRoot, ent), { recursive: true, force: true });
|
|
125739
126136
|
} catch {}
|
|
125740
126137
|
}
|
|
125741
126138
|
} catch {}
|
|
@@ -125744,11 +126141,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125744
126141
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
125745
126142
|
if (!configSkillsRoot)
|
|
125746
126143
|
return;
|
|
125747
|
-
const dest =
|
|
126144
|
+
const dest = join116(configSkillsRoot, name);
|
|
125748
126145
|
try {
|
|
125749
126146
|
if (liveSkillDir !== null) {
|
|
125750
126147
|
try {
|
|
125751
|
-
const st =
|
|
126148
|
+
const st = lstatSync16(liveSkillDir);
|
|
125752
126149
|
if (st.isSymbolicLink()) {
|
|
125753
126150
|
process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
|
|
125754
126151
|
`));
|
|
@@ -125759,19 +126156,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125759
126156
|
if (liveSkillDir === null) {
|
|
125760
126157
|
sweepMirrorPriors(configSkillsRoot);
|
|
125761
126158
|
if (existsSync114(dest)) {
|
|
125762
|
-
const trash =
|
|
126159
|
+
const trash = join116(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
125763
126160
|
renameSync32(dest, trash);
|
|
125764
126161
|
}
|
|
125765
126162
|
return;
|
|
125766
126163
|
}
|
|
125767
126164
|
mkdirSync66(configSkillsRoot, { recursive: true, mode: 493 });
|
|
125768
126165
|
sweepMirrorPriors(configSkillsRoot);
|
|
125769
|
-
const staging = mkdtempSync6(
|
|
126166
|
+
const staging = mkdtempSync6(join116(configSkillsRoot, `.${name}-staging-`));
|
|
125770
126167
|
const walk2 = (src, dst) => {
|
|
125771
126168
|
mkdirSync66(dst, { recursive: true, mode: 493 });
|
|
125772
|
-
for (const ent of
|
|
125773
|
-
const s =
|
|
125774
|
-
const d =
|
|
126169
|
+
for (const ent of readdirSync43(src, { withFileTypes: true })) {
|
|
126170
|
+
const s = join116(src, ent.name);
|
|
126171
|
+
const d = join116(dst, ent.name);
|
|
125775
126172
|
if (ent.isSymbolicLink())
|
|
125776
126173
|
continue;
|
|
125777
126174
|
if (ent.isDirectory())
|
|
@@ -125784,7 +126181,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
125784
126181
|
walk2(liveSkillDir, staging);
|
|
125785
126182
|
chmodSync19(staging, 493);
|
|
125786
126183
|
if (existsSync114(dest)) {
|
|
125787
|
-
const prior =
|
|
126184
|
+
const prior = join116(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
125788
126185
|
renameSync32(dest, prior);
|
|
125789
126186
|
}
|
|
125790
126187
|
renameSync32(staging, dest);
|
|
@@ -125814,20 +126211,20 @@ function resolveAgent(opts) {
|
|
|
125814
126211
|
function resolveAgentsRoot(opts) {
|
|
125815
126212
|
if (opts.root)
|
|
125816
126213
|
return resolve61(opts.root);
|
|
125817
|
-
return
|
|
126214
|
+
return join116(homedir60(), ".switchroom", "agents");
|
|
125818
126215
|
}
|
|
125819
126216
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
125820
|
-
return
|
|
126217
|
+
return join116(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
125821
126218
|
}
|
|
125822
126219
|
function trashDir(agentsRoot, agent) {
|
|
125823
|
-
return
|
|
126220
|
+
return join116(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
125824
126221
|
}
|
|
125825
126222
|
function countPersonalSkills(agentsRoot, agent) {
|
|
125826
|
-
const skillsDir =
|
|
126223
|
+
const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
|
|
125827
126224
|
if (!existsSync114(skillsDir))
|
|
125828
126225
|
return 0;
|
|
125829
126226
|
let n = 0;
|
|
125830
|
-
for (const ent of
|
|
126227
|
+
for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
|
|
125831
126228
|
if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
|
|
125832
126229
|
n += 1;
|
|
125833
126230
|
}
|
|
@@ -125855,13 +126252,13 @@ function readStdinSync2() {
|
|
|
125855
126252
|
}
|
|
125856
126253
|
function loadFromDir2(dir) {
|
|
125857
126254
|
const abs = resolve61(dir);
|
|
125858
|
-
if (!
|
|
126255
|
+
if (!statSync62(abs).isDirectory()) {
|
|
125859
126256
|
fail5(`--from path is not a directory: ${dir}`);
|
|
125860
126257
|
}
|
|
125861
126258
|
const files = {};
|
|
125862
126259
|
const walk2 = (sub) => {
|
|
125863
|
-
for (const ent of
|
|
125864
|
-
const full =
|
|
126260
|
+
for (const ent of readdirSync43(sub, { withFileTypes: true })) {
|
|
126261
|
+
const full = join116(sub, ent.name);
|
|
125865
126262
|
if (ent.isSymbolicLink()) {
|
|
125866
126263
|
fail5(`refusing to read symlink in --from dir: ${relative7(abs, full)}`);
|
|
125867
126264
|
}
|
|
@@ -125914,8 +126311,8 @@ function behavioralValidate(files) {
|
|
|
125914
126311
|
errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
125915
126312
|
}
|
|
125916
126313
|
} else if (PY_SCRIPT_RE.test(path10)) {
|
|
125917
|
-
const tmp = mkdtempSync6(
|
|
125918
|
-
const tmpPy =
|
|
126314
|
+
const tmp = mkdtempSync6(join116(tmpdir7(), "skill-personal-py-"));
|
|
126315
|
+
const tmpPy = join116(tmp, "check.py");
|
|
125919
126316
|
try {
|
|
125920
126317
|
writeFileSync49(tmpPy, content);
|
|
125921
126318
|
const r = spawnSync25("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -125936,12 +126333,12 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
125936
126333
|
if (!existsSync114(trash))
|
|
125937
126334
|
return;
|
|
125938
126335
|
const now2 = Date.now();
|
|
125939
|
-
for (const ent of
|
|
126336
|
+
for (const ent of readdirSync43(trash, { withFileTypes: true })) {
|
|
125940
126337
|
if (!ent.isDirectory())
|
|
125941
126338
|
continue;
|
|
125942
|
-
const entPath =
|
|
126339
|
+
const entPath = join116(trash, ent.name);
|
|
125943
126340
|
try {
|
|
125944
|
-
const st =
|
|
126341
|
+
const st = statSync62(entPath);
|
|
125945
126342
|
if (now2 - st.mtimeMs > TRASH_TTL_MS) {
|
|
125946
126343
|
rmSync21(entPath, { recursive: true, force: true });
|
|
125947
126344
|
}
|
|
@@ -125951,7 +126348,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
125951
126348
|
function writePersonalSkill(targetDir, files) {
|
|
125952
126349
|
let targetIsSymlink = false;
|
|
125953
126350
|
try {
|
|
125954
|
-
const st =
|
|
126351
|
+
const st = lstatSync16(targetDir);
|
|
125955
126352
|
if (st.isSymbolicLink()) {
|
|
125956
126353
|
targetIsSymlink = true;
|
|
125957
126354
|
}
|
|
@@ -125959,14 +126356,14 @@ function writePersonalSkill(targetDir, files) {
|
|
|
125959
126356
|
if (targetIsSymlink) {
|
|
125960
126357
|
fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
125961
126358
|
}
|
|
125962
|
-
mkdirSync66(
|
|
125963
|
-
const staging = mkdtempSync6(
|
|
126359
|
+
mkdirSync66(dirname49(targetDir), { recursive: true, mode: 493 });
|
|
126360
|
+
const staging = mkdtempSync6(join116(dirname49(targetDir), `.skill-personal-stage-`));
|
|
125964
126361
|
chmodSync19(staging, 493);
|
|
125965
126362
|
let oldRename = null;
|
|
125966
126363
|
try {
|
|
125967
126364
|
for (const [path10, content] of Object.entries(files)) {
|
|
125968
|
-
const full =
|
|
125969
|
-
mkdirSync66(
|
|
126365
|
+
const full = join116(staging, path10);
|
|
126366
|
+
mkdirSync66(dirname49(full), { recursive: true, mode: 493 });
|
|
125970
126367
|
const fd = openSync22(full, "wx");
|
|
125971
126368
|
try {
|
|
125972
126369
|
writeFileSync49(fd, content);
|
|
@@ -125980,7 +126377,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
125980
126377
|
}
|
|
125981
126378
|
let targetExists = false;
|
|
125982
126379
|
try {
|
|
125983
|
-
|
|
126380
|
+
lstatSync16(targetDir);
|
|
125984
126381
|
targetExists = true;
|
|
125985
126382
|
} catch {}
|
|
125986
126383
|
if (targetExists) {
|
|
@@ -126015,7 +126412,7 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
|
126015
126412
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
126016
126413
|
const exists = (() => {
|
|
126017
126414
|
try {
|
|
126018
|
-
|
|
126415
|
+
lstatSync16(target);
|
|
126019
126416
|
return true;
|
|
126020
126417
|
} catch {
|
|
126021
126418
|
return false;
|
|
@@ -126067,7 +126464,7 @@ function loadFiles(opts) {
|
|
|
126067
126464
|
if (!existsSync114(p)) {
|
|
126068
126465
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
126069
126466
|
}
|
|
126070
|
-
const st =
|
|
126467
|
+
const st = statSync62(p);
|
|
126071
126468
|
if (st.isDirectory()) {
|
|
126072
126469
|
return loadFromDir2(p);
|
|
126073
126470
|
}
|
|
@@ -126112,10 +126509,10 @@ function editPersonalAction(name, opts) {
|
|
|
126112
126509
|
}
|
|
126113
126510
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
126114
126511
|
function defaultSharedRoot() {
|
|
126115
|
-
return
|
|
126512
|
+
return join116(homedir60(), ".switchroom", "skills");
|
|
126116
126513
|
}
|
|
126117
126514
|
function defaultBundledRoot() {
|
|
126118
|
-
return
|
|
126515
|
+
return join116(homedir60(), ".switchroom", "skills", "_bundled");
|
|
126119
126516
|
}
|
|
126120
126517
|
function resolveCloneSource(source, opts) {
|
|
126121
126518
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -126125,11 +126522,11 @@ function resolveCloneSource(source, opts) {
|
|
|
126125
126522
|
const tier = m[1];
|
|
126126
126523
|
const slug = m[2];
|
|
126127
126524
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
126128
|
-
const dir =
|
|
126525
|
+
const dir = join116(root, slug);
|
|
126129
126526
|
if (!existsSync114(dir)) {
|
|
126130
126527
|
fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
126131
126528
|
}
|
|
126132
|
-
const st =
|
|
126529
|
+
const st = lstatSync16(dir);
|
|
126133
126530
|
if (st.isSymbolicLink()) {
|
|
126134
126531
|
fail5(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
|
|
126135
126532
|
}
|
|
@@ -126140,8 +126537,8 @@ function readSourceFiles(dir) {
|
|
|
126140
126537
|
const files = {};
|
|
126141
126538
|
const skipped = [];
|
|
126142
126539
|
const walk2 = (sub) => {
|
|
126143
|
-
for (const ent of
|
|
126144
|
-
const full =
|
|
126540
|
+
for (const ent of readdirSync43(sub, { withFileTypes: true })) {
|
|
126541
|
+
const full = join116(sub, ent.name);
|
|
126145
126542
|
if (ent.isSymbolicLink()) {
|
|
126146
126543
|
continue;
|
|
126147
126544
|
}
|
|
@@ -126156,7 +126553,7 @@ function readSourceFiles(dir) {
|
|
|
126156
126553
|
continue;
|
|
126157
126554
|
}
|
|
126158
126555
|
try {
|
|
126159
|
-
const st =
|
|
126556
|
+
const st = lstatSync16(full);
|
|
126160
126557
|
if (st.size > CLONE_MAX_FILE_BYTES) {
|
|
126161
126558
|
fail5(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
126162
126559
|
}
|
|
@@ -126238,7 +126635,7 @@ function removePersonalAction(name, opts) {
|
|
|
126238
126635
|
}
|
|
126239
126636
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
126240
126637
|
try {
|
|
126241
|
-
const st =
|
|
126638
|
+
const st = lstatSync16(target);
|
|
126242
126639
|
if (st.isSymbolicLink()) {
|
|
126243
126640
|
fail5(`refusing to remove symlink at ${target}; investigate manually`);
|
|
126244
126641
|
}
|
|
@@ -126252,7 +126649,7 @@ function removePersonalAction(name, opts) {
|
|
|
126252
126649
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
126253
126650
|
mkdirSync66(trashRoot2, { recursive: true, mode: 493 });
|
|
126254
126651
|
const ts = Date.now();
|
|
126255
|
-
const trashTarget =
|
|
126652
|
+
const trashTarget = join116(trashRoot2, `${name}-${ts}`);
|
|
126256
126653
|
renameSync32(target, trashTarget);
|
|
126257
126654
|
const now2 = new Date(ts);
|
|
126258
126655
|
utimesSync(trashTarget, now2, now2);
|
|
@@ -126271,27 +126668,27 @@ function listPersonalAction(opts) {
|
|
|
126271
126668
|
const agent = resolveAgent(opts);
|
|
126272
126669
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
126273
126670
|
sweepTrash(agentsRoot, agent);
|
|
126274
|
-
const skillsDir =
|
|
126671
|
+
const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
|
|
126275
126672
|
const personal = [];
|
|
126276
126673
|
if (existsSync114(skillsDir)) {
|
|
126277
|
-
for (const ent of
|
|
126674
|
+
for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
|
|
126278
126675
|
if (!ent.isDirectory())
|
|
126279
126676
|
continue;
|
|
126280
126677
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
126281
126678
|
continue;
|
|
126282
126679
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
126283
|
-
const skillPath =
|
|
126680
|
+
const skillPath = join116(skillsDir, ent.name);
|
|
126284
126681
|
let fileCount = 0;
|
|
126285
126682
|
let totalBytes = 0;
|
|
126286
126683
|
const walk2 = (sub) => {
|
|
126287
|
-
for (const e of
|
|
126684
|
+
for (const e of readdirSync43(sub, { withFileTypes: true })) {
|
|
126288
126685
|
if (e.isFile()) {
|
|
126289
126686
|
fileCount += 1;
|
|
126290
126687
|
try {
|
|
126291
|
-
totalBytes +=
|
|
126688
|
+
totalBytes += statSync62(join116(sub, e.name)).size;
|
|
126292
126689
|
} catch {}
|
|
126293
126690
|
} else if (e.isDirectory()) {
|
|
126294
|
-
walk2(
|
|
126691
|
+
walk2(join116(sub, e.name));
|
|
126295
126692
|
}
|
|
126296
126693
|
}
|
|
126297
126694
|
};
|
|
@@ -126330,7 +126727,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
126330
126727
|
// src/cli/self-improve-propose-skill.ts
|
|
126331
126728
|
import { createConnection as createConnection4 } from "node:net";
|
|
126332
126729
|
import { homedir as homedir61 } from "node:os";
|
|
126333
|
-
import { join as
|
|
126730
|
+
import { join as join117 } from "node:path";
|
|
126334
126731
|
import { readFileSync as readFileSync102 } from "node:fs";
|
|
126335
126732
|
|
|
126336
126733
|
// src/self-improve/skill-proposals.ts
|
|
@@ -126378,7 +126775,7 @@ var STOPWORDS = new Set([
|
|
|
126378
126775
|
// src/cli/self-improve-propose-skill.ts
|
|
126379
126776
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
126380
126777
|
function gatewaySocketPath() {
|
|
126381
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ?
|
|
126778
|
+
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
126779
|
}
|
|
126383
126780
|
function fail6(msg, code = 1) {
|
|
126384
126781
|
console.error(msg);
|
|
@@ -126458,11 +126855,11 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
126458
126855
|
// src/cli/self-improve-eval-case.ts
|
|
126459
126856
|
import { createConnection as createConnection5 } from "node:net";
|
|
126460
126857
|
import { homedir as homedir62 } from "node:os";
|
|
126461
|
-
import { join as
|
|
126858
|
+
import { join as join121, resolve as resolve62, sep as sep7 } from "node:path";
|
|
126462
126859
|
import { existsSync as existsSync119, readFileSync as readFileSync107, realpathSync as realpathSync10 } from "node:fs";
|
|
126463
126860
|
|
|
126464
126861
|
// src/self-improve/apply-guard.ts
|
|
126465
|
-
import { existsSync as existsSync117, lstatSync as
|
|
126862
|
+
import { existsSync as existsSync117, lstatSync as lstatSync17, readFileSync as readFileSync105, statSync as statSync64 } from "node:fs";
|
|
126466
126863
|
|
|
126467
126864
|
// src/self-improve/config.ts
|
|
126468
126865
|
function intEnv(name, def) {
|
|
@@ -126483,11 +126880,11 @@ function resolveSelfImproveConfig() {
|
|
|
126483
126880
|
}
|
|
126484
126881
|
|
|
126485
126882
|
// src/self-improve/eval-gate.ts
|
|
126486
|
-
import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as
|
|
126487
|
-
import { join as
|
|
126883
|
+
import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as readdirSync44, statSync as statSync63 } from "node:fs";
|
|
126884
|
+
import { join as join118 } from "node:path";
|
|
126488
126885
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
126489
126886
|
function evalsJsonPath(skillDir) {
|
|
126490
|
-
return
|
|
126887
|
+
return join118(skillDir, "evals", "evals.json");
|
|
126491
126888
|
}
|
|
126492
126889
|
function skillHasEvals(skillDir) {
|
|
126493
126890
|
const p = evalsJsonPath(skillDir);
|
|
@@ -126501,7 +126898,7 @@ function skillHasEvals(skillDir) {
|
|
|
126501
126898
|
}
|
|
126502
126899
|
}
|
|
126503
126900
|
function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWITCHROOM_PYTHON ?? "python3") {
|
|
126504
|
-
const script =
|
|
126901
|
+
const script = join118(repoRoot, "skills", "skill-creator", "scripts", "aggregate_benchmark.py");
|
|
126505
126902
|
if (!existsSync115(script)) {
|
|
126506
126903
|
return { ok: false, error: `aggregate_benchmark.py not found at ${script}` };
|
|
126507
126904
|
}
|
|
@@ -126515,25 +126912,25 @@ function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWIT
|
|
|
126515
126912
|
error: `aggregate_benchmark exited ${r.status}: ${(r.stderr ?? "").trim()}`
|
|
126516
126913
|
};
|
|
126517
126914
|
}
|
|
126518
|
-
return { ok: true, benchmarkJson:
|
|
126915
|
+
return { ok: true, benchmarkJson: join118(benchmarkDir, "benchmark.json") };
|
|
126519
126916
|
}
|
|
126520
126917
|
function isBenchmarkDir(dir) {
|
|
126521
126918
|
try {
|
|
126522
|
-
if (!
|
|
126919
|
+
if (!statSync63(dir).isDirectory())
|
|
126523
126920
|
return false;
|
|
126524
126921
|
} catch {
|
|
126525
126922
|
return false;
|
|
126526
126923
|
}
|
|
126527
126924
|
const hasEvalDirs = (d) => {
|
|
126528
126925
|
try {
|
|
126529
|
-
return
|
|
126926
|
+
return readdirSync44(d, { withFileTypes: true }).some((e) => e.isDirectory() && e.name.startsWith("eval-"));
|
|
126530
126927
|
} catch {
|
|
126531
126928
|
return false;
|
|
126532
126929
|
}
|
|
126533
126930
|
};
|
|
126534
126931
|
if (hasEvalDirs(dir))
|
|
126535
126932
|
return true;
|
|
126536
|
-
const runs =
|
|
126933
|
+
const runs = join118(dir, "runs");
|
|
126537
126934
|
return existsSync115(runs) && hasEvalDirs(runs);
|
|
126538
126935
|
}
|
|
126539
126936
|
function resolveBenchmarkDir(baseDir, explicit) {
|
|
@@ -126544,13 +126941,13 @@ function resolveBenchmarkDir(baseDir, explicit) {
|
|
|
126544
126941
|
return baseDir;
|
|
126545
126942
|
let entries;
|
|
126546
126943
|
try {
|
|
126547
|
-
entries =
|
|
126944
|
+
entries = readdirSync44(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join118(baseDir, e.name)).filter(isBenchmarkDir);
|
|
126548
126945
|
} catch {
|
|
126549
126946
|
return null;
|
|
126550
126947
|
}
|
|
126551
126948
|
if (entries.length === 0)
|
|
126552
126949
|
return null;
|
|
126553
|
-
entries.sort((a, b) =>
|
|
126950
|
+
entries.sort((a, b) => statSync63(b).mtimeMs - statSync63(a).mtimeMs);
|
|
126554
126951
|
return entries[0];
|
|
126555
126952
|
}
|
|
126556
126953
|
var CANDIDATE_CONFIGS = ["with_skill", "candidate", "new_skill", "after"];
|
|
@@ -126617,12 +127014,12 @@ import {
|
|
|
126617
127014
|
mkdirSync as mkdirSync67,
|
|
126618
127015
|
openSync as openSync23,
|
|
126619
127016
|
readFileSync as readFileSync104,
|
|
126620
|
-
readdirSync as
|
|
127017
|
+
readdirSync as readdirSync45,
|
|
126621
127018
|
renameSync as renameSync33,
|
|
126622
127019
|
writeFileSync as writeFileSync50,
|
|
126623
127020
|
writeSync as writeSync12
|
|
126624
127021
|
} from "node:fs";
|
|
126625
|
-
import { dirname as
|
|
127022
|
+
import { dirname as dirname50, join as join119 } from "node:path";
|
|
126626
127023
|
import { createHash as createHash21 } from "node:crypto";
|
|
126627
127024
|
|
|
126628
127025
|
// src/self-improve/pii-scan.ts
|
|
@@ -126773,7 +127170,7 @@ function readEvalsDoc(skillDir) {
|
|
|
126773
127170
|
}
|
|
126774
127171
|
}
|
|
126775
127172
|
function writeAtomic(path10, content) {
|
|
126776
|
-
mkdirSync67(
|
|
127173
|
+
mkdirSync67(dirname50(path10), { recursive: true });
|
|
126777
127174
|
const tmp = `${path10}.tmp-${process.pid}-${Date.now()}`;
|
|
126778
127175
|
writeFileSync50(tmp, content, { encoding: "utf-8", mode: 420 });
|
|
126779
127176
|
renameSync33(tmp, path10);
|
|
@@ -126812,7 +127209,7 @@ function appendEvalCase(skillDir, slug, input) {
|
|
|
126812
127209
|
}
|
|
126813
127210
|
var HELD_OUT_FILE = "self-improve-held-out-evals.jsonl";
|
|
126814
127211
|
function heldOutPath(stateDir) {
|
|
126815
|
-
return
|
|
127212
|
+
return join119(stateDir, HELD_OUT_FILE);
|
|
126816
127213
|
}
|
|
126817
127214
|
function heldOutHasFingerprint(stateDir, slug, fp) {
|
|
126818
127215
|
const path10 = heldOutPath(stateDir);
|
|
@@ -126861,10 +127258,10 @@ function appendHeldOutCase(stateDir, slug, ec, opts = {}) {
|
|
|
126861
127258
|
var EVAL_MANIFEST_FILE = "self-improve-eval-manifest.json";
|
|
126862
127259
|
var EVAL_BASELINE_SUBDIR = "self-improve-eval-baselines";
|
|
126863
127260
|
function manifestPath(stateDir) {
|
|
126864
|
-
return
|
|
127261
|
+
return join119(stateDir, EVAL_MANIFEST_FILE);
|
|
126865
127262
|
}
|
|
126866
127263
|
function baselineEvalsPath(stateDir, slug) {
|
|
126867
|
-
return
|
|
127264
|
+
return join119(stateDir, EVAL_BASELINE_SUBDIR, slug, "evals.json");
|
|
126868
127265
|
}
|
|
126869
127266
|
function evalsSha256(skillDir) {
|
|
126870
127267
|
const p = evalsJsonPath(skillDir);
|
|
@@ -126900,7 +127297,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
|
|
|
126900
127297
|
m[slug] = { sha256: sha, updated_at: new Date(now2()).toISOString(), skill_dir: skillDir };
|
|
126901
127298
|
writeManifest(stateDir, m);
|
|
126902
127299
|
const snap = baselineEvalsPath(stateDir, slug);
|
|
126903
|
-
mkdirSync67(
|
|
127300
|
+
mkdirSync67(dirname50(snap), { recursive: true });
|
|
126904
127301
|
copyFileSync15(evalsJsonPath(skillDir), snap);
|
|
126905
127302
|
}
|
|
126906
127303
|
|
|
@@ -126908,7 +127305,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
|
|
|
126908
127305
|
var BENCHMARK_SUBDIR = "self-improve-benchmarks";
|
|
126909
127306
|
function ownsSkill(skillDir) {
|
|
126910
127307
|
try {
|
|
126911
|
-
const st =
|
|
127308
|
+
const st = lstatSync17(skillDir);
|
|
126912
127309
|
if (st.isSymbolicLink())
|
|
126913
127310
|
return false;
|
|
126914
127311
|
return st.isDirectory();
|
|
@@ -126926,10 +127323,10 @@ import {
|
|
|
126926
127323
|
readFileSync as readFileSync106,
|
|
126927
127324
|
writeSync as writeSync13
|
|
126928
127325
|
} from "node:fs";
|
|
126929
|
-
import { join as
|
|
127326
|
+
import { join as join120 } from "node:path";
|
|
126930
127327
|
var EVAL_CASE_PROPOSALS_FILE = "eval-case-proposals.jsonl";
|
|
126931
127328
|
function proposalsPath(stateDir) {
|
|
126932
|
-
return
|
|
127329
|
+
return join120(stateDir, EVAL_CASE_PROPOSALS_FILE);
|
|
126933
127330
|
}
|
|
126934
127331
|
function isRecord2(v) {
|
|
126935
127332
|
return !!v && typeof v === "object" && typeof v.id === "string" && typeof v.skill_slug === "string" && !!v.case && typeof v.case === "object";
|
|
@@ -126964,16 +127361,16 @@ function getEvalCaseProposal(stateDir, id) {
|
|
|
126964
127361
|
// src/cli/self-improve-eval-case.ts
|
|
126965
127362
|
var IPC_CONNECT_TIMEOUT_MS2 = 5000;
|
|
126966
127363
|
function stateDir() {
|
|
126967
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
127364
|
+
return process.env.TELEGRAM_STATE_DIR ?? join121(homedir62(), ".claude", "channels", "telegram");
|
|
126968
127365
|
}
|
|
126969
127366
|
function gatewaySocketPath2() {
|
|
126970
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
127367
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? join121(stateDir(), "gateway.sock");
|
|
126971
127368
|
}
|
|
126972
127369
|
function skillsRoot() {
|
|
126973
|
-
return
|
|
127370
|
+
return join121(homedir62(), ".claude", "skills");
|
|
126974
127371
|
}
|
|
126975
127372
|
function skillDirFor(slug) {
|
|
126976
|
-
return
|
|
127373
|
+
return join121(skillsRoot(), slug);
|
|
126977
127374
|
}
|
|
126978
127375
|
function authorizeApplyTarget(persistedDir, slug) {
|
|
126979
127376
|
const dir = skillDirFor(slug);
|
|
@@ -127171,20 +127568,20 @@ function registerSelfImproveEvalCaseCommands(program3) {
|
|
|
127171
127568
|
|
|
127172
127569
|
// src/cli/self-improve-bench.ts
|
|
127173
127570
|
import { homedir as homedir63 } from "node:os";
|
|
127174
|
-
import { dirname as
|
|
127571
|
+
import { dirname as dirname51, join as join122 } from "node:path";
|
|
127175
127572
|
init_shipped_assets();
|
|
127176
127573
|
function fail8(msg, code = 1) {
|
|
127177
127574
|
console.error(msg);
|
|
127178
127575
|
process.exit(code);
|
|
127179
127576
|
}
|
|
127180
127577
|
function stateDir2() {
|
|
127181
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
127578
|
+
return process.env.TELEGRAM_STATE_DIR ?? join122(homedir63(), ".claude", "channels", "telegram");
|
|
127182
127579
|
}
|
|
127183
127580
|
function skillDirFor2(slug) {
|
|
127184
|
-
return
|
|
127581
|
+
return join122(homedir63(), ".claude", "skills", slug);
|
|
127185
127582
|
}
|
|
127186
127583
|
function benchmarkBaseDir(slug) {
|
|
127187
|
-
return
|
|
127584
|
+
return join122(stateDir2(), BENCHMARK_SUBDIR, slug);
|
|
127188
127585
|
}
|
|
127189
127586
|
function resolveAggregatorRepoRoot() {
|
|
127190
127587
|
const res = resolveShippedAsset(SKILLS_ASSET, {
|
|
@@ -127196,7 +127593,7 @@ function resolveAggregatorRepoRoot() {
|
|
|
127196
127593
|
`);
|
|
127197
127594
|
return null;
|
|
127198
127595
|
}
|
|
127199
|
-
return
|
|
127596
|
+
return dirname51(res.path);
|
|
127200
127597
|
}
|
|
127201
127598
|
function registerSelfImproveBenchCommand(program3) {
|
|
127202
127599
|
const parent = program3.commands.find((c) => c.name() === "self-improve") ?? program3.command("self-improve").description("Agent self-improvement ops");
|
|
@@ -127246,9 +127643,9 @@ function registerSelfImproveBenchCommand(program3) {
|
|
|
127246
127643
|
init_esm();
|
|
127247
127644
|
init_helpers();
|
|
127248
127645
|
var import_yaml26 = __toESM(require_dist(), 1);
|
|
127249
|
-
import { existsSync as existsSync120, readdirSync as
|
|
127646
|
+
import { existsSync as existsSync120, readdirSync as readdirSync46, readFileSync as readFileSync108, statSync as statSync65 } from "node:fs";
|
|
127250
127647
|
import { homedir as homedir64 } from "node:os";
|
|
127251
|
-
import { join as
|
|
127648
|
+
import { join as join123, resolve as resolve63 } from "node:path";
|
|
127252
127649
|
var PERSONAL_PREFIX2 = "personal-";
|
|
127253
127650
|
var BUNDLED_SUBDIR = "_bundled";
|
|
127254
127651
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
@@ -127262,7 +127659,7 @@ function defaultBundledRoot2() {
|
|
|
127262
127659
|
return resolve63(homedir64(), ".switchroom/skills/_bundled");
|
|
127263
127660
|
}
|
|
127264
127661
|
function readSkillFrontmatter(skillDir) {
|
|
127265
|
-
const mdPath =
|
|
127662
|
+
const mdPath = join123(skillDir, "SKILL.md");
|
|
127266
127663
|
if (!existsSync120(mdPath))
|
|
127267
127664
|
return null;
|
|
127268
127665
|
let content;
|
|
@@ -127295,9 +127692,9 @@ function readSkillFrontmatter(skillDir) {
|
|
|
127295
127692
|
return { fm: parsed };
|
|
127296
127693
|
}
|
|
127297
127694
|
function statSkillMd(skillDir) {
|
|
127298
|
-
const mdPath =
|
|
127695
|
+
const mdPath = join123(skillDir, "SKILL.md");
|
|
127299
127696
|
try {
|
|
127300
|
-
const st =
|
|
127697
|
+
const st = statSync65(mdPath);
|
|
127301
127698
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
127302
127699
|
} catch {
|
|
127303
127700
|
return null;
|
|
@@ -127306,22 +127703,22 @@ function statSkillMd(skillDir) {
|
|
|
127306
127703
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
127307
127704
|
if (!AGENT_NAME_RE3.test(agent))
|
|
127308
127705
|
return [];
|
|
127309
|
-
const skillsDir =
|
|
127706
|
+
const skillsDir = join123(agentsRoot, agent, ".claude/skills");
|
|
127310
127707
|
if (!existsSync120(skillsDir))
|
|
127311
127708
|
return [];
|
|
127312
127709
|
const out = [];
|
|
127313
127710
|
let entries;
|
|
127314
127711
|
try {
|
|
127315
|
-
entries =
|
|
127712
|
+
entries = readdirSync46(skillsDir);
|
|
127316
127713
|
} catch {
|
|
127317
127714
|
return [];
|
|
127318
127715
|
}
|
|
127319
127716
|
for (const ent of entries) {
|
|
127320
127717
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
127321
127718
|
continue;
|
|
127322
|
-
const dirPath =
|
|
127719
|
+
const dirPath = join123(skillsDir, ent);
|
|
127323
127720
|
try {
|
|
127324
|
-
if (!
|
|
127721
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127325
127722
|
continue;
|
|
127326
127723
|
} catch {
|
|
127327
127724
|
continue;
|
|
@@ -127350,7 +127747,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
127350
127747
|
const out = [];
|
|
127351
127748
|
let entries;
|
|
127352
127749
|
try {
|
|
127353
|
-
entries =
|
|
127750
|
+
entries = readdirSync46(sharedRoot);
|
|
127354
127751
|
} catch {
|
|
127355
127752
|
return [];
|
|
127356
127753
|
}
|
|
@@ -127359,9 +127756,9 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
127359
127756
|
continue;
|
|
127360
127757
|
if (ent.startsWith("."))
|
|
127361
127758
|
continue;
|
|
127362
|
-
const dirPath =
|
|
127759
|
+
const dirPath = join123(sharedRoot, ent);
|
|
127363
127760
|
try {
|
|
127364
|
-
if (!
|
|
127761
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127365
127762
|
continue;
|
|
127366
127763
|
} catch {
|
|
127367
127764
|
continue;
|
|
@@ -127388,16 +127785,16 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
127388
127785
|
const out = [];
|
|
127389
127786
|
let entries;
|
|
127390
127787
|
try {
|
|
127391
|
-
entries =
|
|
127788
|
+
entries = readdirSync46(bundledRoot);
|
|
127392
127789
|
} catch {
|
|
127393
127790
|
return [];
|
|
127394
127791
|
}
|
|
127395
127792
|
for (const ent of entries) {
|
|
127396
127793
|
if (ent.startsWith("."))
|
|
127397
127794
|
continue;
|
|
127398
|
-
const dirPath =
|
|
127795
|
+
const dirPath = join123(bundledRoot, ent);
|
|
127399
127796
|
try {
|
|
127400
|
-
if (!
|
|
127797
|
+
if (!statSync65(dirPath).isDirectory())
|
|
127401
127798
|
continue;
|
|
127402
127799
|
} catch {
|
|
127403
127800
|
continue;
|
|
@@ -127544,15 +127941,15 @@ init_operator_uid();
|
|
|
127544
127941
|
import {
|
|
127545
127942
|
existsSync as existsSync122,
|
|
127546
127943
|
mkdirSync as mkdirSync69,
|
|
127547
|
-
readdirSync as
|
|
127944
|
+
readdirSync as readdirSync47,
|
|
127548
127945
|
writeFileSync as writeFileSync51,
|
|
127549
|
-
statSync as
|
|
127550
|
-
lstatSync as
|
|
127946
|
+
statSync as statSync66,
|
|
127947
|
+
lstatSync as lstatSync18,
|
|
127551
127948
|
realpathSync as realpathSync11,
|
|
127552
127949
|
copyFileSync as copyFileSync16
|
|
127553
127950
|
} from "node:fs";
|
|
127554
127951
|
import { homedir as homedir65 } from "node:os";
|
|
127555
|
-
import { join as
|
|
127952
|
+
import { join as join124 } from "node:path";
|
|
127556
127953
|
import { spawnSync as spawnSync29 } from "node:child_process";
|
|
127557
127954
|
|
|
127558
127955
|
// src/cli/singleton-stale-cleanup.ts
|
|
@@ -127908,10 +128305,10 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir65()) {
|
|
|
127908
128305
|
return resolved;
|
|
127909
128306
|
}
|
|
127910
128307
|
function resolveHostdSkillsTarget(hostHome) {
|
|
127911
|
-
const skillsPath =
|
|
128308
|
+
const skillsPath = join124(hostHome, ".switchroom", "skills");
|
|
127912
128309
|
let st;
|
|
127913
128310
|
try {
|
|
127914
|
-
st =
|
|
128311
|
+
st = lstatSync18(skillsPath);
|
|
127915
128312
|
} catch {
|
|
127916
128313
|
return;
|
|
127917
128314
|
}
|
|
@@ -127932,10 +128329,10 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
127932
128329
|
return target;
|
|
127933
128330
|
}
|
|
127934
128331
|
function hostdDir() {
|
|
127935
|
-
return
|
|
128332
|
+
return join124(homedir65(), ".switchroom", "hostd");
|
|
127936
128333
|
}
|
|
127937
128334
|
function hostdComposePath() {
|
|
127938
|
-
return
|
|
128335
|
+
return join124(hostdDir(), "docker-compose.yml");
|
|
127939
128336
|
}
|
|
127940
128337
|
function backupExistingCompose() {
|
|
127941
128338
|
const p = hostdComposePath();
|
|
@@ -128062,12 +128459,12 @@ function doStatus() {
|
|
|
128062
128459
|
if (existsSync122(dir)) {
|
|
128063
128460
|
const entries = [];
|
|
128064
128461
|
try {
|
|
128065
|
-
for (const name of
|
|
128462
|
+
for (const name of readdirSync47(dir)) {
|
|
128066
128463
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
128067
128464
|
continue;
|
|
128068
|
-
const sockPath =
|
|
128465
|
+
const sockPath = join124(dir, name, "sock");
|
|
128069
128466
|
if (existsSync122(sockPath)) {
|
|
128070
|
-
const st =
|
|
128467
|
+
const st = statSync66(sockPath);
|
|
128071
128468
|
if ((st.mode & 61440) === 49152) {
|
|
128072
128469
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
128073
128470
|
}
|
|
@@ -128160,7 +128557,7 @@ init_operator_uid();
|
|
|
128160
128557
|
init_compose();
|
|
128161
128558
|
import { chownSync as chownSync14, existsSync as existsSync123, mkdirSync as mkdirSync70, writeFileSync as writeFileSync52, copyFileSync as copyFileSync17 } from "node:fs";
|
|
128162
128559
|
import { homedir as homedir66 } from "node:os";
|
|
128163
|
-
import { join as
|
|
128560
|
+
import { join as join125 } from "node:path";
|
|
128164
128561
|
import { spawnSync as spawnSync30 } from "node:child_process";
|
|
128165
128562
|
function resolveWebImageTag(explicitTag, release) {
|
|
128166
128563
|
if (explicitTag)
|
|
@@ -128262,10 +128659,10 @@ services:
|
|
|
128262
128659
|
`;
|
|
128263
128660
|
}
|
|
128264
128661
|
function webdDir() {
|
|
128265
|
-
return
|
|
128662
|
+
return join125(homedir66(), ".switchroom", "web");
|
|
128266
128663
|
}
|
|
128267
128664
|
function webdComposePath() {
|
|
128268
|
-
return
|
|
128665
|
+
return join125(webdDir(), "docker-compose.yml");
|
|
128269
128666
|
}
|
|
128270
128667
|
function backupExistingCompose2() {
|
|
128271
128668
|
const p = webdComposePath();
|
|
@@ -128431,9 +128828,9 @@ function registerWebdCommand(program3) {
|
|
|
128431
128828
|
// src/cli/host-repair.ts
|
|
128432
128829
|
init_source();
|
|
128433
128830
|
import { homedir as homedir67 } from "node:os";
|
|
128434
|
-
import { join as
|
|
128831
|
+
import { join as join126 } from "node:path";
|
|
128435
128832
|
var ARTIFACT_ALLOWLIST = {
|
|
128436
|
-
dockerComposePluginDir: (home2) =>
|
|
128833
|
+
dockerComposePluginDir: (home2) => join126(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
128437
128834
|
stateSentinel: "/state"
|
|
128438
128835
|
};
|
|
128439
128836
|
function isStateBogusAutoDir(probe2) {
|
|
@@ -128587,18 +128984,18 @@ function applyMountRepairs(items, deps) {
|
|
|
128587
128984
|
function registerHostCommand(program3) {
|
|
128588
128985
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
128589
128986
|
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:
|
|
128987
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync22, lstatSync: lstatSync19, readdirSync: readdirSync48 } = await import("node:fs");
|
|
128591
128988
|
const probe2 = {
|
|
128592
128989
|
lstat(path10) {
|
|
128593
128990
|
try {
|
|
128594
|
-
return
|
|
128991
|
+
return lstatSync19(path10);
|
|
128595
128992
|
} catch {
|
|
128596
128993
|
return null;
|
|
128597
128994
|
}
|
|
128598
128995
|
},
|
|
128599
128996
|
readdir(path10) {
|
|
128600
128997
|
try {
|
|
128601
|
-
return
|
|
128998
|
+
return readdirSync48(path10);
|
|
128602
128999
|
} catch {
|
|
128603
129000
|
return null;
|
|
128604
129001
|
}
|
|
@@ -128887,9 +129284,9 @@ function printDeepDiveBrief(targets) {
|
|
|
128887
129284
|
|
|
128888
129285
|
// src/cli/hindsight-watch.ts
|
|
128889
129286
|
init_source();
|
|
128890
|
-
import { existsSync as existsSync128, readdirSync as
|
|
129287
|
+
import { existsSync as existsSync128, readdirSync as readdirSync52 } from "node:fs";
|
|
128891
129288
|
import { homedir as homedir70 } from "node:os";
|
|
128892
|
-
import { join as
|
|
129289
|
+
import { join as join129, resolve as resolve66 } from "node:path";
|
|
128893
129290
|
|
|
128894
129291
|
// src/host-control/config-degraded.ts
|
|
128895
129292
|
import { connect as connect3 } from "node:net";
|
|
@@ -128958,7 +129355,7 @@ init_install_cron();
|
|
|
128958
129355
|
|
|
128959
129356
|
// src/hindsight-watch/probe.ts
|
|
128960
129357
|
import { spawnSync as spawnSync32 } from "node:child_process";
|
|
128961
|
-
import { readFileSync as readFileSync113, readdirSync as
|
|
129358
|
+
import { readFileSync as readFileSync113, readdirSync as readdirSync51, statSync as statSync68 } from "node:fs";
|
|
128962
129359
|
import { homedir as homedir69 } from "node:os";
|
|
128963
129360
|
import { resolve as resolve65 } from "node:path";
|
|
128964
129361
|
|
|
@@ -129086,13 +129483,13 @@ function readLlmSignals(series) {
|
|
|
129086
129483
|
}
|
|
129087
129484
|
|
|
129088
129485
|
// src/hindsight-watch/recall-log.ts
|
|
129089
|
-
import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as
|
|
129090
|
-
import { join as
|
|
129486
|
+
import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as readdirSync50, readSync as readSync6, statSync as statSync67 } from "node:fs";
|
|
129487
|
+
import { join as join128 } from "node:path";
|
|
129091
129488
|
var RECALL_WINDOW_ROWS = 200;
|
|
129092
129489
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
129093
129490
|
var RECALL_MAX_ROW_AGE_MS = 24 * 60 * 60 * 1000;
|
|
129094
129491
|
function recallLogPath2(agentDir) {
|
|
129095
|
-
return
|
|
129492
|
+
return join128(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
129096
129493
|
}
|
|
129097
129494
|
function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
129098
129495
|
if (!existsSync127(path10))
|
|
@@ -129101,7 +129498,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
129101
129498
|
let truncatedHead = false;
|
|
129102
129499
|
let fd;
|
|
129103
129500
|
try {
|
|
129104
|
-
const size =
|
|
129501
|
+
const size = statSync67(path10).size;
|
|
129105
129502
|
const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
|
|
129106
129503
|
truncatedHead = start > 0;
|
|
129107
129504
|
const length = size - start;
|
|
@@ -129236,11 +129633,11 @@ function summarizeRecallRows2(rows) {
|
|
|
129236
129633
|
return out;
|
|
129237
129634
|
}
|
|
129238
129635
|
function probeRecallLogs(agentsDir, now2, windowRows = RECALL_WINDOW_ROWS, maxAgeMs = RECALL_MAX_ROW_AGE_MS) {
|
|
129239
|
-
const names =
|
|
129636
|
+
const names = readdirSync50(agentsDir);
|
|
129240
129637
|
const all = [];
|
|
129241
129638
|
let agents = 0;
|
|
129242
129639
|
for (const name of names) {
|
|
129243
|
-
const rows = withinWindow(readRecallLogTail2(recallLogPath2(
|
|
129640
|
+
const rows = withinWindow(readRecallLogTail2(recallLogPath2(join128(agentsDir, name)), windowRows), now2, maxAgeMs);
|
|
129244
129641
|
if (rows.length > 0)
|
|
129245
129642
|
agents++;
|
|
129246
129643
|
all.push(...rows);
|
|
@@ -129333,7 +129730,7 @@ function readDropCount(hindsightDir) {
|
|
|
129333
129730
|
const path10 = resolve65(hindsightDir, "pending-drops.json");
|
|
129334
129731
|
let parsed;
|
|
129335
129732
|
try {
|
|
129336
|
-
if (
|
|
129733
|
+
if (statSync68(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
129337
129734
|
return 0;
|
|
129338
129735
|
parsed = JSON.parse(readFileSync113(path10, "utf8"));
|
|
129339
129736
|
} catch {
|
|
@@ -129350,7 +129747,7 @@ function resolveAgentsDir2(explicit) {
|
|
|
129350
129747
|
function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
129351
129748
|
let names;
|
|
129352
129749
|
try {
|
|
129353
|
-
names =
|
|
129750
|
+
names = readdirSync51(agentsDir);
|
|
129354
129751
|
} catch (e) {
|
|
129355
129752
|
throw new ProbeError(`reading ${agentsDir}: ${e.message}`, "spool");
|
|
129356
129753
|
}
|
|
@@ -129364,7 +129761,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
129364
129761
|
const hindsightDir = resolve65(agentsDir, name, "home", ".hindsight");
|
|
129365
129762
|
let entries = null;
|
|
129366
129763
|
try {
|
|
129367
|
-
entries =
|
|
129764
|
+
entries = readdirSync51(resolve65(hindsightDir, "pending-retains"));
|
|
129368
129765
|
} catch {}
|
|
129369
129766
|
if (entries !== null) {
|
|
129370
129767
|
agents++;
|
|
@@ -129376,19 +129773,19 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
|
|
|
129376
129773
|
}
|
|
129377
129774
|
}
|
|
129378
129775
|
try {
|
|
129379
|
-
for (const f of
|
|
129776
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-evicted"))) {
|
|
129380
129777
|
if (f.endsWith(".json"))
|
|
129381
129778
|
evicted++;
|
|
129382
129779
|
}
|
|
129383
129780
|
} catch {}
|
|
129384
129781
|
try {
|
|
129385
|
-
for (const f of
|
|
129782
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-dead"))) {
|
|
129386
129783
|
if (f.endsWith(".dead"))
|
|
129387
129784
|
dead++;
|
|
129388
129785
|
}
|
|
129389
129786
|
} catch {}
|
|
129390
129787
|
try {
|
|
129391
|
-
for (const f of
|
|
129788
|
+
for (const f of readdirSync51(resolve65(hindsightDir, "pending-duplicate"))) {
|
|
129392
129789
|
if (f.endsWith(".json"))
|
|
129393
129790
|
duplicates++;
|
|
129394
129791
|
}
|
|
@@ -130298,7 +130695,7 @@ function registerHindsightWatchCommand(program3) {
|
|
|
130298
130695
|
process.exitCode = runInstallCron(opts.cronUser);
|
|
130299
130696
|
return;
|
|
130300
130697
|
}
|
|
130301
|
-
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ??
|
|
130698
|
+
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join129(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir70(), ".switchroom", "agents");
|
|
130302
130699
|
const statePath = opts.state ?? defaultStatePath2();
|
|
130303
130700
|
const log = (m) => {
|
|
130304
130701
|
process.stderr.write(`${m}
|
|
@@ -130412,7 +130809,7 @@ function orderRelayCandidates(candidates, preferred = PREFERRED_RELAY_AGENT) {
|
|
|
130412
130809
|
}
|
|
130413
130810
|
function collectRelayCandidates(agentsDir) {
|
|
130414
130811
|
const candidates = [];
|
|
130415
|
-
for (const name of
|
|
130812
|
+
for (const name of readdirSync52(agentsDir).sort()) {
|
|
130416
130813
|
const sock = resolve66(agentsDir, name, "telegram", "gateway.sock");
|
|
130417
130814
|
if (existsSync128(sock))
|
|
130418
130815
|
candidates.push({ agent: name, sock });
|
|
@@ -130437,7 +130834,7 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
130437
130834
|
// src/cli/hindsight-bench.ts
|
|
130438
130835
|
init_source();
|
|
130439
130836
|
import { mkdirSync as mkdirSync72, readFileSync as readFileSync114, writeFileSync as writeFileSync54 } from "node:fs";
|
|
130440
|
-
import { dirname as
|
|
130837
|
+
import { dirname as dirname54, resolve as resolve67 } from "node:path";
|
|
130441
130838
|
|
|
130442
130839
|
// src/hindsight-bench/anonymise.ts
|
|
130443
130840
|
function pseudonym(n, width) {
|
|
@@ -131565,7 +131962,7 @@ function readResult(path10) {
|
|
|
131565
131962
|
}
|
|
131566
131963
|
function writeOut(path10, body) {
|
|
131567
131964
|
const abs = resolve67(path10);
|
|
131568
|
-
mkdirSync72(
|
|
131965
|
+
mkdirSync72(dirname54(abs), { recursive: true });
|
|
131569
131966
|
writeFileSync54(abs, body);
|
|
131570
131967
|
process.stderr.write(`${source_default.green("\u2713")} wrote ${abs}
|
|
131571
131968
|
`);
|
|
@@ -131770,13 +132167,13 @@ async function runMeasureMode(opts) {
|
|
|
131770
132167
|
|
|
131771
132168
|
// src/cli/openrouter-watch.ts
|
|
131772
132169
|
init_source();
|
|
131773
|
-
import { existsSync as existsSync130, readdirSync as
|
|
132170
|
+
import { existsSync as existsSync130, readdirSync as readdirSync53 } from "node:fs";
|
|
131774
132171
|
import { homedir as homedir72 } from "node:os";
|
|
131775
|
-
import { join as
|
|
132172
|
+
import { join as join130, resolve as resolve69 } from "node:path";
|
|
131776
132173
|
|
|
131777
132174
|
// src/openrouter/install-cron.ts
|
|
131778
132175
|
import { existsSync as existsSync129, mkdirSync as mkdirSync73, readFileSync as readFileSync115, renameSync as renameSync34, writeFileSync as writeFileSync55 } from "node:fs";
|
|
131779
|
-
import { dirname as
|
|
132176
|
+
import { dirname as dirname55 } from "node:path";
|
|
131780
132177
|
var CRON_PATH3 = "/etc/cron.d/openrouter-watch";
|
|
131781
132178
|
var CRON_SCHEDULE2 = "17 * * * *";
|
|
131782
132179
|
var CRON_LOG_PATH3 = "/var/log/openrouter-watch.log";
|
|
@@ -131800,7 +132197,7 @@ function installCron3(opts) {
|
|
|
131800
132197
|
}
|
|
131801
132198
|
} catch {}
|
|
131802
132199
|
}
|
|
131803
|
-
mkdirSync73(
|
|
132200
|
+
mkdirSync73(dirname55(path10), { recursive: true });
|
|
131804
132201
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
131805
132202
|
writeFileSync55(tmp, content, { mode: 420 });
|
|
131806
132203
|
renameSync34(tmp, path10);
|
|
@@ -131809,7 +132206,7 @@ function installCron3(opts) {
|
|
|
131809
132206
|
// src/openrouter/state.ts
|
|
131810
132207
|
import { mkdirSync as mkdirSync74, readFileSync as readFileSync116, renameSync as renameSync35, writeFileSync as writeFileSync56 } from "node:fs";
|
|
131811
132208
|
import { homedir as homedir71 } from "node:os";
|
|
131812
|
-
import { dirname as
|
|
132209
|
+
import { dirname as dirname56, resolve as resolve68 } from "node:path";
|
|
131813
132210
|
function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir71()) {
|
|
131814
132211
|
return resolve68(home2, ".switchroom", "openrouter-watch", "state.json");
|
|
131815
132212
|
}
|
|
@@ -131836,7 +132233,7 @@ function loadState2(path10) {
|
|
|
131836
132233
|
return out;
|
|
131837
132234
|
}
|
|
131838
132235
|
function saveState2(path10, state) {
|
|
131839
|
-
mkdirSync74(
|
|
132236
|
+
mkdirSync74(dirname56(path10), { recursive: true, mode: 448 });
|
|
131840
132237
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
131841
132238
|
const payload = { v: 1, ...state };
|
|
131842
132239
|
writeFileSync56(tmp, JSON.stringify(payload, null, 2) + `
|
|
@@ -131927,7 +132324,7 @@ function registerOpenRouterWatchCommand(program3) {
|
|
|
131927
132324
|
process.exitCode = runInstallCron2(opts.cronUser);
|
|
131928
132325
|
return;
|
|
131929
132326
|
}
|
|
131930
|
-
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ??
|
|
132327
|
+
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join130(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir72(), ".switchroom", "agents");
|
|
131931
132328
|
const statePath = opts.state ?? defaultStatePath3();
|
|
131932
132329
|
const log = (m) => {
|
|
131933
132330
|
process.stderr.write(`${m}
|
|
@@ -132024,7 +132421,7 @@ function runInstallCron2(cronUser) {
|
|
|
132024
132421
|
async function notifyOperator2(agentsDir, text, log) {
|
|
132025
132422
|
const candidates = [];
|
|
132026
132423
|
try {
|
|
132027
|
-
for (const name of
|
|
132424
|
+
for (const name of readdirSync53(agentsDir).sort()) {
|
|
132028
132425
|
const sock = resolve69(agentsDir, name, "telegram", "gateway.sock");
|
|
132029
132426
|
if (existsSync130(sock))
|
|
132030
132427
|
candidates.push({ agent: name, sock });
|
|
@@ -132047,7 +132444,7 @@ init_sync();
|
|
|
132047
132444
|
init_install_cron2();
|
|
132048
132445
|
init_flock();
|
|
132049
132446
|
import { homedir as homedir73 } from "node:os";
|
|
132050
|
-
import { join as
|
|
132447
|
+
import { join as join131 } from "node:path";
|
|
132051
132448
|
function switchroomHome() {
|
|
132052
132449
|
return process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir73();
|
|
132053
132450
|
}
|
|
@@ -132059,9 +132456,9 @@ function resolveSyncOptions(configPath, opts) {
|
|
|
132059
132456
|
cfg = loaded.config_repo;
|
|
132060
132457
|
} catch {}
|
|
132061
132458
|
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 ??
|
|
132459
|
+
const repoPath = opts.path ? resolvePath(opts.path) : cfg.path ? resolvePath(cfg.path) : join131(home2, ".switchroom-config");
|
|
132460
|
+
const livePath = process.env.SWITCHROOM_CONFIG ?? join131(home2, ".switchroom", "switchroom.yaml");
|
|
132461
|
+
const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join131(home2, ".switchroom", "agents");
|
|
132065
132462
|
const push = opts.push === false ? false : cfg.push ?? true;
|
|
132066
132463
|
const remote = opts.remote ?? cfg.remote ?? "origin";
|
|
132067
132464
|
const requirePrivate = opts.requirePrivate ?? cfg.require_private ?? true;
|
|
@@ -132245,6 +132642,7 @@ registerDoctorCommand(program3);
|
|
|
132245
132642
|
registerUpdateCommand(program3);
|
|
132246
132643
|
registerRolloutCommand(program3);
|
|
132247
132644
|
registerRollbackCommand(program3);
|
|
132645
|
+
registerHostCliUpgradeCommand(program3);
|
|
132248
132646
|
registerRestartCommand(program3);
|
|
132249
132647
|
registerVersionCommand(program3);
|
|
132250
132648
|
registerVersionsCommand(program3);
|