switchroom 0.19.38 → 0.19.39
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 +3 -3
- package/dist/cli/switchroom.js +618 -378
- package/dist/host-control/main.js +4 -4
- package/dist/vault/approvals/kernel-server.js +3 -3
- package/dist/vault/broker/server.js +3 -3
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +140 -19
- package/telegram-plugin/gateway/handback-preturn-signal.ts +16 -0
- package/telegram-plugin/gateway/stream-render.ts +238 -17
- package/telegram-plugin/hooks/tool-label-pretool.mjs +119 -1
- package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +19 -12
- package/telegram-plugin/tests/fixtures/pretool-main-2.1.219.json +15 -0
- package/telegram-plugin/tests/fixtures/pretool-subagent-2.1.219.json +16 -0
- package/telegram-plugin/tests/queued-card-surface.test.ts +262 -0
- package/telegram-plugin/tests/sidechain-label-filter-pretool.test.ts +272 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +7 -4
- package/vendor/hindsight-memory/scripts/lib/config.py +151 -0
- package/vendor/hindsight-memory/scripts/retain.py +19 -15
- package/vendor/hindsight-memory/scripts/tests/test_backfill.py +14 -1
- package/vendor/hindsight-memory/scripts/tests/test_observation_scopes.py +192 -3
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +18 -2
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +57 -4
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.19.
|
|
2123
|
+
var VERSION = "0.19.39", COMMIT_SHA = "4aff2bdd";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -15830,7 +15830,7 @@ function hindsightPgEnv(overrides = new Map) {
|
|
|
15830
15830
|
}
|
|
15831
15831
|
return out;
|
|
15832
15832
|
}
|
|
15833
|
-
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION, HINDSIGHT_PG_APP_ANON_MIB = 2560, HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048, HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB, HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB =
|
|
15833
|
+
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION, HINDSIGHT_PG_APP_ANON_MIB = 2560, HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048, HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB, HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 6144, HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288, HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE", HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS", HINDSIGHT_PG_FSYNC_ENV = "SWITCHROOM_HINDSIGHT_PG_FSYNC", HINDSIGHT_PG_DEFAULT_FSYNC = "on", HINDSIGHT_PG_DEFAULTS, HINDSIGHT_PG_ENV_KEYS;
|
|
15834
15834
|
var init_hindsight_pg_defaults = __esm(() => {
|
|
15835
15835
|
HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
|
|
15836
15836
|
HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
@@ -15908,7 +15908,7 @@ function hindsightPerfEnv(caps, overrides = new Map) {
|
|
|
15908
15908
|
function findUnmanagedHindsightEnvKeys(configEnv) {
|
|
15909
15909
|
return Object.keys(configEnv ?? {}).filter((key) => !HINDSIGHT_PERF_ENV_KEYS.has(key) && !HINDSIGHT_PG_ENV_KEYS.has(key)).sort();
|
|
15910
15910
|
}
|
|
15911
|
-
var HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION = 150, HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE, HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT = 50, HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2, HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low", HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT, HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true", HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128, HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true", HINDSIGHT_DEFAULT_LLM_MAX_RETRIES = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND =
|
|
15911
|
+
var HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION = 150, HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE, HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT = 50, HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2, HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low", HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT, HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true", HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128, HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true", HINDSIGHT_DEFAULT_LLM_MAX_RETRIES = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250, HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential", HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30, HINDSIGHT_PERF_DEFAULTS_UNGATED, HINDSIGHT_PERF_DEFAULTS_GPU, HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM, HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS, HINDSIGHT_PERF_ENV_KEYS;
|
|
15912
15912
|
var init_hindsight_perf_defaults = __esm(() => {
|
|
15913
15913
|
init_hindsight_pg_defaults();
|
|
15914
15914
|
HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE = Math.ceil(HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION * 0.4);
|
|
@@ -45987,6 +45987,38 @@ function installCron(opts) {
|
|
|
45987
45987
|
renameSync15(tmp, path6);
|
|
45988
45988
|
return { status: "installed", path: path6, content };
|
|
45989
45989
|
}
|
|
45990
|
+
function parseCronUser(content) {
|
|
45991
|
+
for (const line of content.split(`
|
|
45992
|
+
`)) {
|
|
45993
|
+
const trimmed = line.trim();
|
|
45994
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
45995
|
+
continue;
|
|
45996
|
+
if (!trimmed.includes("hindsight-watch"))
|
|
45997
|
+
continue;
|
|
45998
|
+
const fields = trimmed.split(/\s+/);
|
|
45999
|
+
if (fields.length >= 7)
|
|
46000
|
+
return fields[5];
|
|
46001
|
+
}
|
|
46002
|
+
return null;
|
|
46003
|
+
}
|
|
46004
|
+
function reconcileCron(opts) {
|
|
46005
|
+
const path6 = opts.path ?? CRON_PATH;
|
|
46006
|
+
if (!existsSync63(path6))
|
|
46007
|
+
return { status: "absent", path: path6 };
|
|
46008
|
+
let user = opts.fallbackUser;
|
|
46009
|
+
try {
|
|
46010
|
+
user = parseCronUser(readFileSync60(path6, "utf8")) ?? opts.fallbackUser;
|
|
46011
|
+
} catch {}
|
|
46012
|
+
if (!user) {
|
|
46013
|
+
throw new Error(`cannot reconcile ${path6}: no cron-user parseable from the existing fragment and no fallback user available`);
|
|
46014
|
+
}
|
|
46015
|
+
const r = installCron({ user, binary: opts.binary, path: path6 });
|
|
46016
|
+
return {
|
|
46017
|
+
status: r.status === "installed" ? "reconciled" : "unchanged",
|
|
46018
|
+
path: path6,
|
|
46019
|
+
content: r.content
|
|
46020
|
+
};
|
|
46021
|
+
}
|
|
45990
46022
|
var CRON_PATH = "/etc/cron.d/hindsight-watch", CRON_SCHEDULE = "*/15 * * * *", CRON_LOG_PATH = "/var/log/hindsight-watch.log", CRON_LOCK_PATH = "/run/lock/hindsight-watch.lock";
|
|
45991
46023
|
var init_install_cron = () => {};
|
|
45992
46024
|
|
|
@@ -51689,6 +51721,9 @@ import { spawnSync as spawnSync12 } from "node:child_process";
|
|
|
51689
51721
|
function fixFor(c, target) {
|
|
51690
51722
|
return remediationFor(classifyComponent(c), target);
|
|
51691
51723
|
}
|
|
51724
|
+
function hostCliBehindStatus(c) {
|
|
51725
|
+
return c.kind === "cli" ? "fail" : "warn";
|
|
51726
|
+
}
|
|
51692
51727
|
function runComponentVersionChecks(config, opts = {}) {
|
|
51693
51728
|
const exec = opts.exec ?? defaultExec2;
|
|
51694
51729
|
const components = collectComponents(opts.cliVersion ?? SWITCHROOM_VERSION, exec);
|
|
@@ -51712,10 +51747,11 @@ function runComponentVersionChecks(config, opts = {}) {
|
|
|
51712
51747
|
});
|
|
51713
51748
|
}
|
|
51714
51749
|
for (const c of report.behind) {
|
|
51750
|
+
const status = hostCliBehindStatus(c);
|
|
51715
51751
|
results.push({
|
|
51716
51752
|
name: `component behind: ${c.name}`,
|
|
51717
|
-
status
|
|
51718
|
-
detail: `on ${c.version}, expected ${report.target} (${src})`,
|
|
51753
|
+
status,
|
|
51754
|
+
detail: status === "fail" ? `on ${c.version}, expected ${report.target} (${src}) \u2014 the host CLI must never trail the fleet; it self-heals in-band via \`switchroom update\`` : `on ${c.version}, expected ${report.target} (${src})`,
|
|
51719
51755
|
fix: fixFor(c, report.target)
|
|
51720
51756
|
});
|
|
51721
51757
|
}
|
|
@@ -68980,9 +69016,9 @@ __export(exports_server, {
|
|
|
68980
69016
|
dispatchTool: () => dispatchTool,
|
|
68981
69017
|
TOOLS: () => TOOLS
|
|
68982
69018
|
});
|
|
68983
|
-
import { spawnSync as
|
|
69019
|
+
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
68984
69020
|
function execCli(args, stdin) {
|
|
68985
|
-
const r =
|
|
69021
|
+
const r = spawnSync23(CLI_BIN, args, {
|
|
68986
69022
|
encoding: "utf-8",
|
|
68987
69023
|
env: process.env,
|
|
68988
69024
|
timeout: 15000,
|
|
@@ -69491,7 +69527,7 @@ __export(exports_server2, {
|
|
|
69491
69527
|
TOOLS: () => TOOLS2
|
|
69492
69528
|
});
|
|
69493
69529
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
69494
|
-
import { existsSync as
|
|
69530
|
+
import { existsSync as existsSync106 } from "node:fs";
|
|
69495
69531
|
function selfSocketPath() {
|
|
69496
69532
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
69497
69533
|
}
|
|
@@ -69518,7 +69554,7 @@ async function dispatchTool2(name, args) {
|
|
|
69518
69554
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
69519
69555
|
}
|
|
69520
69556
|
const sockPath = selfSocketPath();
|
|
69521
|
-
if (!
|
|
69557
|
+
if (!existsSync106(sockPath)) {
|
|
69522
69558
|
return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
|
|
69523
69559
|
}
|
|
69524
69560
|
let req;
|
|
@@ -69769,13 +69805,13 @@ function resolveAuditLogPath() {
|
|
|
69769
69805
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
69770
69806
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
69771
69807
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
69772
|
-
if (
|
|
69808
|
+
if (existsSync106(bindMounted))
|
|
69773
69809
|
return bindMounted;
|
|
69774
69810
|
return defaultAuditLogPath2();
|
|
69775
69811
|
}
|
|
69776
69812
|
function getLastUpdateApplyStatus() {
|
|
69777
69813
|
const path10 = resolveAuditLogPath();
|
|
69778
|
-
if (!
|
|
69814
|
+
if (!existsSync106(path10)) {
|
|
69779
69815
|
return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
|
|
69780
69816
|
}
|
|
69781
69817
|
let raw;
|
|
@@ -70494,11 +70530,11 @@ var init_test_agents = __esm(() => {
|
|
|
70494
70530
|
});
|
|
70495
70531
|
|
|
70496
70532
|
// src/litellm/header-passthrough-guard.ts
|
|
70497
|
-
import { existsSync as
|
|
70533
|
+
import { existsSync as existsSync109, readdirSync as readdirSync41 } from "node:fs";
|
|
70498
70534
|
function discoverLiveLitellmConfigPath(opts) {
|
|
70499
70535
|
const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
|
|
70500
70536
|
const readdir2 = opts?.readdirFn ?? ((p) => readdirSync41(p));
|
|
70501
|
-
const exists = opts?.existsFn ??
|
|
70537
|
+
const exists = opts?.existsFn ?? existsSync109;
|
|
70502
70538
|
let entries;
|
|
70503
70539
|
try {
|
|
70504
70540
|
entries = readdir2(servicesDir);
|
|
@@ -70588,7 +70624,7 @@ var init_header_passthrough_guard = __esm(() => {
|
|
|
70588
70624
|
});
|
|
70589
70625
|
|
|
70590
70626
|
// src/fleet-health/litellm-config-sensor.ts
|
|
70591
|
-
import { readFileSync as
|
|
70627
|
+
import { readFileSync as readFileSync98, existsSync as existsSync110 } from "node:fs";
|
|
70592
70628
|
function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitellmConfigPath()) {
|
|
70593
70629
|
if (explicit)
|
|
70594
70630
|
return explicit;
|
|
@@ -70599,8 +70635,8 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
|
|
|
70599
70635
|
}
|
|
70600
70636
|
function scanLitellmConfig(opts = {}) {
|
|
70601
70637
|
const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
|
|
70602
|
-
const exists = opts.existsFn ??
|
|
70603
|
-
const read = opts.readFn ?? ((p) =>
|
|
70638
|
+
const exists = opts.existsFn ?? existsSync110;
|
|
70639
|
+
const read = opts.readFn ?? ((p) => readFileSync98(p, "utf-8"));
|
|
70604
70640
|
const log = opts.log ?? (() => {});
|
|
70605
70641
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
70606
70642
|
if (path10 === null) {
|
|
@@ -70675,20 +70711,20 @@ __export(exports_scan, {
|
|
|
70675
70711
|
ledgerPathForBase: () => ledgerPathForBase
|
|
70676
70712
|
});
|
|
70677
70713
|
import {
|
|
70678
|
-
readFileSync as
|
|
70714
|
+
readFileSync as readFileSync99,
|
|
70679
70715
|
readdirSync as readdirSync42,
|
|
70680
|
-
existsSync as
|
|
70681
|
-
mkdirSync as
|
|
70682
|
-
writeFileSync as
|
|
70716
|
+
existsSync as existsSync111,
|
|
70717
|
+
mkdirSync as mkdirSync64,
|
|
70718
|
+
writeFileSync as writeFileSync47
|
|
70683
70719
|
} from "node:fs";
|
|
70684
|
-
import { resolve as resolve61, dirname as
|
|
70720
|
+
import { resolve as resolve61, dirname as dirname43 } from "node:path";
|
|
70685
70721
|
import { homedir as homedir61 } from "node:os";
|
|
70686
70722
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir61()) {
|
|
70687
70723
|
return resolve61(home2, ".switchroom");
|
|
70688
70724
|
}
|
|
70689
70725
|
function listAgents(base) {
|
|
70690
70726
|
const dir = resolve61(base, "agents");
|
|
70691
|
-
if (!
|
|
70727
|
+
if (!existsSync111(dir))
|
|
70692
70728
|
return [];
|
|
70693
70729
|
try {
|
|
70694
70730
|
return readdirSync42(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
@@ -70717,16 +70753,16 @@ function runScan(opts = {}) {
|
|
|
70717
70753
|
let gwText = "";
|
|
70718
70754
|
let sawArtifact = false;
|
|
70719
70755
|
try {
|
|
70720
|
-
if (
|
|
70721
|
-
turnsText =
|
|
70756
|
+
if (existsSync111(turnsPath)) {
|
|
70757
|
+
turnsText = readFileSync99(turnsPath, "utf-8");
|
|
70722
70758
|
sawArtifact = true;
|
|
70723
70759
|
}
|
|
70724
70760
|
} catch (e) {
|
|
70725
70761
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
70726
70762
|
}
|
|
70727
70763
|
try {
|
|
70728
|
-
if (
|
|
70729
|
-
gwText =
|
|
70764
|
+
if (existsSync111(gwPath)) {
|
|
70765
|
+
gwText = readFileSync99(gwPath, "utf-8");
|
|
70730
70766
|
sawArtifact = true;
|
|
70731
70767
|
}
|
|
70732
70768
|
} catch (e) {
|
|
@@ -70768,20 +70804,20 @@ function runScan(opts = {}) {
|
|
|
70768
70804
|
function readLedgerIfPresent(base) {
|
|
70769
70805
|
const path10 = ledgerPathForBase(base);
|
|
70770
70806
|
try {
|
|
70771
|
-
if (!
|
|
70807
|
+
if (!existsSync111(path10))
|
|
70772
70808
|
return null;
|
|
70773
|
-
return JSON.parse(
|
|
70809
|
+
return JSON.parse(readFileSync99(path10, "utf-8"));
|
|
70774
70810
|
} catch {
|
|
70775
70811
|
return null;
|
|
70776
70812
|
}
|
|
70777
70813
|
}
|
|
70778
70814
|
function ledgerPathForBase(base) {
|
|
70779
|
-
return fleetHealthLedgerPath(
|
|
70815
|
+
return fleetHealthLedgerPath(dirname43(base));
|
|
70780
70816
|
}
|
|
70781
70817
|
function writeLedger(base, ledger) {
|
|
70782
70818
|
const path10 = ledgerPathForBase(base);
|
|
70783
|
-
|
|
70784
|
-
|
|
70819
|
+
mkdirSync64(dirname43(path10), { recursive: true });
|
|
70820
|
+
writeFileSync47(path10, JSON.stringify(ledger, null, 2) + `
|
|
70785
70821
|
`, "utf-8");
|
|
70786
70822
|
return path10;
|
|
70787
70823
|
}
|
|
@@ -92108,9 +92144,9 @@ init_source();
|
|
|
92108
92144
|
init_loader();
|
|
92109
92145
|
init_lifecycle();
|
|
92110
92146
|
init_compose_env();
|
|
92111
|
-
import { existsSync as
|
|
92112
|
-
import { spawnSync as
|
|
92113
|
-
import { join as join84, dirname as
|
|
92147
|
+
import { existsSync as existsSync81, mkdirSync as mkdirSync46, readFileSync as readFileSync75, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
|
|
92148
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
92149
|
+
import { join as join84, dirname as dirname34, resolve as resolve45 } from "node:path";
|
|
92114
92150
|
import { homedir as homedir44 } from "node:os";
|
|
92115
92151
|
|
|
92116
92152
|
// src/cli/release-yaml.ts
|
|
@@ -92639,10 +92675,151 @@ function defaultSelfUpdateIO() {
|
|
|
92639
92675
|
// src/cli/update.ts
|
|
92640
92676
|
init_self_invoke();
|
|
92641
92677
|
init_component_versions();
|
|
92678
|
+
init_install_cron();
|
|
92679
|
+
|
|
92680
|
+
// src/host-cli-self-heal/install-timer.ts
|
|
92681
|
+
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
92682
|
+
import { existsSync as existsSync80, mkdirSync as mkdirSync45, readFileSync as readFileSync74, renameSync as renameSync20, writeFileSync as writeFileSync31 } from "node:fs";
|
|
92683
|
+
import { dirname as dirname33 } from "node:path";
|
|
92684
|
+
var SERVICE_PATH = "/etc/systemd/system/switchroom-self-heal.service";
|
|
92685
|
+
var TIMER_PATH = "/etc/systemd/system/switchroom-self-heal.timer";
|
|
92686
|
+
var TIMER_UNIT = "switchroom-self-heal.timer";
|
|
92687
|
+
var SYSTEMD_MARKER = "/run/systemd/system";
|
|
92688
|
+
var DOC_URL = "https://github.com/switchroom/switchroom/blob/main/docs/operators/host-cli-self-heal.md";
|
|
92689
|
+
function renderService(opts) {
|
|
92690
|
+
const homeLine = opts.home ? `Environment=HOME=${opts.home}
|
|
92691
|
+
` : "";
|
|
92692
|
+
return `# switchroom host CLI self-heal \u2014 keeps /usr/local/bin/switchroom from silently
|
|
92693
|
+
` + `# trailing the fleet. Managed by \`switchroom update\`; edits are overwritten.
|
|
92694
|
+
` + `[Unit]
|
|
92695
|
+
` + `Description=Switchroom host CLI self-heal (re-run \`switchroom update\` so the host binary can't silently trail the fleet)
|
|
92696
|
+
` + `Documentation=${DOC_URL}
|
|
92697
|
+
` + `After=network-online.target docker.service
|
|
92698
|
+
` + `Wants=network-online.target
|
|
92699
|
+
` + `
|
|
92700
|
+
` + `[Service]
|
|
92701
|
+
` + `Type=oneshot
|
|
92702
|
+
` + `# Run as the operator whose ~/.switchroom holds the fleet config \u2014 NOT root.
|
|
92703
|
+
` + `User=${opts.user}
|
|
92704
|
+
` + homeLine + `# --skip-images keeps each tick cheap: self-update-cli (the host binary swap)
|
|
92705
|
+
` + `# still runs; GHCR pulls and container recreation do not.
|
|
92706
|
+
` + `ExecStart=${opts.binary} update --skip-images
|
|
92707
|
+
` + `# Self-heal must never wedge the host: bound the run and run at low priority.
|
|
92708
|
+
` + `TimeoutStartSec=600
|
|
92709
|
+
` + `Nice=10
|
|
92710
|
+
`;
|
|
92711
|
+
}
|
|
92712
|
+
function renderTimer() {
|
|
92713
|
+
return `# switchroom host CLI self-heal timer. Managed by \`switchroom update\`.
|
|
92714
|
+
` + `[Unit]
|
|
92715
|
+
` + `Description=Run the Switchroom host CLI self-heal every 30 minutes
|
|
92716
|
+
` + `Documentation=${DOC_URL}
|
|
92717
|
+
` + `
|
|
92718
|
+
` + `[Timer]
|
|
92719
|
+
` + `OnBootSec=5min
|
|
92720
|
+
` + `OnUnitActiveSec=30min
|
|
92721
|
+
` + `RandomizedDelaySec=2min
|
|
92722
|
+
` + `Persistent=true
|
|
92723
|
+
` + `
|
|
92724
|
+
` + `[Install]
|
|
92725
|
+
` + `WantedBy=timers.target
|
|
92726
|
+
`;
|
|
92727
|
+
}
|
|
92728
|
+
function writeUnitIdempotent(path7, content) {
|
|
92729
|
+
if (existsSync80(path7)) {
|
|
92730
|
+
try {
|
|
92731
|
+
if (readFileSync74(path7, "utf8") === content)
|
|
92732
|
+
return "unchanged";
|
|
92733
|
+
} catch {}
|
|
92734
|
+
}
|
|
92735
|
+
mkdirSync45(dirname33(path7), { recursive: true });
|
|
92736
|
+
const tmp = `${path7}.${process.pid}.tmp`;
|
|
92737
|
+
writeFileSync31(tmp, content, { mode: 420 });
|
|
92738
|
+
renameSync20(tmp, path7);
|
|
92739
|
+
return "written";
|
|
92740
|
+
}
|
|
92741
|
+
function homeFromPasswd(user) {
|
|
92742
|
+
try {
|
|
92743
|
+
for (const line of readFileSync74("/etc/passwd", "utf8").split(`
|
|
92744
|
+
`)) {
|
|
92745
|
+
const f = line.split(":");
|
|
92746
|
+
if (f[0] === user && f[5])
|
|
92747
|
+
return f[5];
|
|
92748
|
+
}
|
|
92749
|
+
} catch {}
|
|
92750
|
+
return;
|
|
92751
|
+
}
|
|
92752
|
+
function resolveOperatorUser(env2) {
|
|
92753
|
+
const user = env2.SUDO_USER ?? env2.USER ?? env2.LOGNAME;
|
|
92754
|
+
if (!user || user === "root")
|
|
92755
|
+
return null;
|
|
92756
|
+
return user;
|
|
92757
|
+
}
|
|
92758
|
+
function installSelfHealTimer(deps = {}) {
|
|
92759
|
+
const env2 = deps.env ?? process.env;
|
|
92760
|
+
const systemdBooted = deps.systemdBooted ?? (() => existsSync80(SYSTEMD_MARKER));
|
|
92761
|
+
const geteuid = deps.geteuid ?? (() => typeof process.geteuid === "function" ? process.geteuid() : undefined);
|
|
92762
|
+
const homeForUser = deps.homeForUser ?? homeFromPasswd;
|
|
92763
|
+
const servicePath = deps.servicePath ?? SERVICE_PATH;
|
|
92764
|
+
const timerPath = deps.timerPath ?? TIMER_PATH;
|
|
92765
|
+
const binary = env2.SWITCHROOM_BINARY ?? "/usr/local/bin/switchroom";
|
|
92766
|
+
const user = resolveOperatorUser(env2);
|
|
92767
|
+
const renderUser = user ?? "OPERATOR";
|
|
92768
|
+
const home2 = user ? homeForUser(user) : undefined;
|
|
92769
|
+
const serviceContent = renderService({ binary, user: renderUser, ...home2 ? { home: home2 } : {} });
|
|
92770
|
+
const timerContent = renderTimer();
|
|
92771
|
+
const manualCommands = [
|
|
92772
|
+
"sudo systemctl daemon-reload",
|
|
92773
|
+
`sudo systemctl enable --now ${TIMER_UNIT}`
|
|
92774
|
+
];
|
|
92775
|
+
const base = { servicePath, timerPath, serviceContent, timerContent, manualCommands };
|
|
92776
|
+
if (!binary.startsWith("/")) {
|
|
92777
|
+
return {
|
|
92778
|
+
status: "skipped",
|
|
92779
|
+
reason: `SWITCHROOM_BINARY must be an absolute path (got ${binary})`,
|
|
92780
|
+
...base
|
|
92781
|
+
};
|
|
92782
|
+
}
|
|
92783
|
+
if (!systemdBooted()) {
|
|
92784
|
+
return {
|
|
92785
|
+
status: "skipped",
|
|
92786
|
+
reason: "host is not systemd-booted (/run/systemd/system absent)",
|
|
92787
|
+
...base
|
|
92788
|
+
};
|
|
92789
|
+
}
|
|
92790
|
+
if (geteuid() !== 0) {
|
|
92791
|
+
return { status: "skipped", reason: "not privileged (euid != 0)", ...base };
|
|
92792
|
+
}
|
|
92793
|
+
if (!user) {
|
|
92794
|
+
return {
|
|
92795
|
+
status: "skipped",
|
|
92796
|
+
reason: "no non-root operator user resolvable (SUDO_USER / USER / LOGNAME all unset or root)",
|
|
92797
|
+
...base
|
|
92798
|
+
};
|
|
92799
|
+
}
|
|
92800
|
+
const svc = writeUnitIdempotent(servicePath, serviceContent);
|
|
92801
|
+
const tim = writeUnitIdempotent(timerPath, timerContent);
|
|
92802
|
+
const changed = svc === "written" || tim === "written";
|
|
92803
|
+
const runner = deps.runner ?? ((cmd, args) => {
|
|
92804
|
+
const r = spawnSync16(cmd, args, { stdio: "inherit" });
|
|
92805
|
+
return { status: r.status ?? 1 };
|
|
92806
|
+
});
|
|
92807
|
+
const reload = runner("systemctl", ["daemon-reload"]);
|
|
92808
|
+
if (reload.status !== 0) {
|
|
92809
|
+
throw new Error(`systemctl daemon-reload failed (exit ${reload.status})`);
|
|
92810
|
+
}
|
|
92811
|
+
const enable = runner("systemctl", ["enable", "--now", TIMER_UNIT]);
|
|
92812
|
+
if (enable.status !== 0) {
|
|
92813
|
+
throw new Error(`systemctl enable --now ${TIMER_UNIT} failed (exit ${enable.status})`);
|
|
92814
|
+
}
|
|
92815
|
+
return { status: changed ? "installed" : "unchanged", ...base };
|
|
92816
|
+
}
|
|
92817
|
+
|
|
92818
|
+
// src/cli/update.ts
|
|
92642
92819
|
function defaultPersistPin(configPath) {
|
|
92643
92820
|
return (pin) => {
|
|
92644
92821
|
const path7 = configPath ?? findConfigFile();
|
|
92645
|
-
const before =
|
|
92822
|
+
const before = readFileSync75(path7, "utf8");
|
|
92646
92823
|
const after = setReleasePinInConfig(before, pin);
|
|
92647
92824
|
if (after === before)
|
|
92648
92825
|
return;
|
|
@@ -92658,16 +92835,16 @@ function defaultPersistPin(configPath) {
|
|
|
92658
92835
|
}
|
|
92659
92836
|
var DEFAULT_COMPOSE_PATH2 = join84(homedir44(), ".switchroom", "compose", "docker-compose.yml");
|
|
92660
92837
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
92661
|
-
let dir =
|
|
92838
|
+
let dir = dirname34(scriptPath);
|
|
92662
92839
|
for (let i = 0;i < 12; i++) {
|
|
92663
|
-
if (
|
|
92840
|
+
if (existsSync81(join84(dir, ".git"))) {
|
|
92664
92841
|
try {
|
|
92665
|
-
const pkg = JSON.parse(
|
|
92842
|
+
const pkg = JSON.parse(readFileSync75(join84(dir, "package.json"), "utf-8"));
|
|
92666
92843
|
if (pkg.name === "switchroom")
|
|
92667
92844
|
return true;
|
|
92668
92845
|
} catch {}
|
|
92669
92846
|
}
|
|
92670
|
-
const parent =
|
|
92847
|
+
const parent = dirname34(dir);
|
|
92671
92848
|
if (parent === dir)
|
|
92672
92849
|
break;
|
|
92673
92850
|
dir = parent;
|
|
@@ -92728,7 +92905,7 @@ function planUpdate(opts) {
|
|
|
92728
92905
|
bundleDir: import.meta.dirname,
|
|
92729
92906
|
execPath: process.execPath,
|
|
92730
92907
|
scriptPath,
|
|
92731
|
-
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" ||
|
|
92908
|
+
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync81("/.dockerenv")
|
|
92732
92909
|
});
|
|
92733
92910
|
if (detection.kind !== "static-binary") {
|
|
92734
92911
|
say(source_default.gray(` host CLI not self-updated: ${detection.reason}
|
|
@@ -92801,7 +92978,7 @@ function planUpdate(opts) {
|
|
|
92801
92978
|
steps.push({
|
|
92802
92979
|
name: "pull-images",
|
|
92803
92980
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
92804
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
92981
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync81(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
92805
92982
|
run: () => {
|
|
92806
92983
|
const r = runner("docker", [
|
|
92807
92984
|
"compose",
|
|
@@ -92846,6 +93023,69 @@ function planUpdate(opts) {
|
|
|
92846
93023
|
throw new Error("switchroom apply failed");
|
|
92847
93024
|
}
|
|
92848
93025
|
});
|
|
93026
|
+
steps.push({
|
|
93027
|
+
name: "reconcile-hindsight-watch-cron",
|
|
93028
|
+
description: "Reconcile /etc/cron.d/hindsight-watch (binary path / schedule / flags) on already-armed hosts \u2014 idempotent; no-op when unarmed or already current",
|
|
93029
|
+
skipReason: hostdContext ? "deferred (hostd-context): /etc/cron.d is on the host, not in the hostd container \u2014 finish host-side with `switchroom hindsight-watch --install-cron`" : undefined,
|
|
93030
|
+
isHostdDeferred: hostdContext,
|
|
93031
|
+
run: () => {
|
|
93032
|
+
const say = opts.stdout ?? ((t) => process.stdout.write(t));
|
|
93033
|
+
const binary = process.env.SWITCHROOM_BINARY ?? "/usr/local/bin/switchroom";
|
|
93034
|
+
const fallbackUser = process.env.SUDO_USER ?? process.env.USER ?? process.env.LOGNAME;
|
|
93035
|
+
let res;
|
|
93036
|
+
try {
|
|
93037
|
+
res = reconcileCron({
|
|
93038
|
+
binary,
|
|
93039
|
+
fallbackUser,
|
|
93040
|
+
...opts.watchCronPath ? { path: opts.watchCronPath } : {}
|
|
93041
|
+
});
|
|
93042
|
+
} catch (e) {
|
|
93043
|
+
say(source_default.yellow(` hindsight-watch cron not reconciled: ${e.message}
|
|
93044
|
+
`));
|
|
93045
|
+
return;
|
|
93046
|
+
}
|
|
93047
|
+
if (res.status === "absent") {
|
|
93048
|
+
say(source_default.gray(" hindsight-watch cron not armed \u2014 skipping reconcile " + "(arm with `switchroom hindsight-watch --install-cron`; doctor FAILs while unarmed)\n"));
|
|
93049
|
+
} else if (res.status === "reconciled") {
|
|
93050
|
+
say(source_default.green(` hindsight-watch cron reconciled at ${res.path}
|
|
93051
|
+
`));
|
|
93052
|
+
} else {
|
|
93053
|
+
say(source_default.gray(` hindsight-watch cron already current at ${res.path}
|
|
93054
|
+
`));
|
|
93055
|
+
}
|
|
93056
|
+
}
|
|
93057
|
+
});
|
|
93058
|
+
steps.push({
|
|
93059
|
+
name: "install-self-heal-timer",
|
|
93060
|
+
description: "Install + enable the switchroom-self-heal systemd timer so a shipped release converges the host binary automatically \u2014 graceful no-op without systemd/privilege",
|
|
93061
|
+
skipReason: hostdContext ? "deferred (hostd-context): /etc/systemd/system is on the host, not in the hostd container \u2014 finish host-side by re-running `switchroom update` on the host" : undefined,
|
|
93062
|
+
isHostdDeferred: hostdContext,
|
|
93063
|
+
run: () => {
|
|
93064
|
+
const say = opts.stdout ?? ((t) => process.stdout.write(t));
|
|
93065
|
+
let res;
|
|
93066
|
+
try {
|
|
93067
|
+
res = installSelfHealTimer(opts.selfHealTimerDeps ?? {});
|
|
93068
|
+
} catch (e) {
|
|
93069
|
+
say(source_default.yellow(` self-heal timer not installed: ${e.message}
|
|
93070
|
+
`));
|
|
93071
|
+
return;
|
|
93072
|
+
}
|
|
93073
|
+
if (res.status === "installed") {
|
|
93074
|
+
say(source_default.green(` self-heal timer installed + enabled (${res.servicePath}, ${res.timerPath})
|
|
93075
|
+
`));
|
|
93076
|
+
} else if (res.status === "unchanged") {
|
|
93077
|
+
say(source_default.gray(` self-heal timer already current + enabled at ${res.timerPath}
|
|
93078
|
+
`));
|
|
93079
|
+
} else {
|
|
93080
|
+
say(source_default.gray(` self-heal timer not installed automatically: ${res.reason}
|
|
93081
|
+
`) + source_default.gray(` Install it by hand:
|
|
93082
|
+
`) + source_default.gray(` # ${res.servicePath}
|
|
93083
|
+
`) + res.serviceContent.replace(/^/gm, source_default.gray(" ")) + source_default.gray(` # ${res.timerPath}
|
|
93084
|
+
`) + res.timerContent.replace(/^/gm, source_default.gray(" ")) + res.manualCommands.map((c) => source_default.gray(` ${c}
|
|
93085
|
+
`)).join(""));
|
|
93086
|
+
}
|
|
93087
|
+
}
|
|
93088
|
+
});
|
|
92849
93089
|
let hostControlEnabled;
|
|
92850
93090
|
if (typeof opts.hostControlEnabled === "boolean") {
|
|
92851
93091
|
hostControlEnabled = opts.hostControlEnabled;
|
|
@@ -92922,13 +93162,13 @@ function planUpdate(opts) {
|
|
|
92922
93162
|
}
|
|
92923
93163
|
const source = resolve45(import.meta.dirname, "../../skills");
|
|
92924
93164
|
const dest = join84(homedir44(), ".switchroom", "skills", "_bundled");
|
|
92925
|
-
if (!
|
|
93165
|
+
if (!existsSync81(source)) {
|
|
92926
93166
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
92927
93167
|
`);
|
|
92928
93168
|
return;
|
|
92929
93169
|
}
|
|
92930
93170
|
try {
|
|
92931
|
-
|
|
93171
|
+
mkdirSync46(dirname34(dest), { recursive: true });
|
|
92932
93172
|
const r = syncBundledSkills({
|
|
92933
93173
|
source,
|
|
92934
93174
|
dest,
|
|
@@ -92954,10 +93194,10 @@ function planUpdate(opts) {
|
|
|
92954
93194
|
if (opts.syncBundledSkillsFn)
|
|
92955
93195
|
return;
|
|
92956
93196
|
const dest = join84(homedir44(), ".switchroom", "skills", "_bundled");
|
|
92957
|
-
if (!
|
|
93197
|
+
if (!existsSync81(dest)) {
|
|
92958
93198
|
return;
|
|
92959
93199
|
}
|
|
92960
|
-
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !
|
|
93200
|
+
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync81(join84(dest, key)));
|
|
92961
93201
|
if (missing.length > 0) {
|
|
92962
93202
|
throw new Error(`verify-bundled-skills: builtin default skill(s) missing from the pool after sync: ` + `${missing.join(", ")}. These ship in the CLI package and must exist in ${dest}. ` + `This is a broken sync or a packaging regression \u2014 the pool is not converged.`);
|
|
92963
93203
|
}
|
|
@@ -92992,7 +93232,7 @@ function planUpdate(opts) {
|
|
|
92992
93232
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
92993
93233
|
run: () => {
|
|
92994
93234
|
try {
|
|
92995
|
-
const composeText =
|
|
93235
|
+
const composeText = readFileSync75(composePath, "utf8");
|
|
92996
93236
|
const pf = validateBindSources(composeText);
|
|
92997
93237
|
if (!pf.ok)
|
|
92998
93238
|
throw new Error(formatPreflightError(pf));
|
|
@@ -93026,7 +93266,7 @@ function planUpdate(opts) {
|
|
|
93026
93266
|
return steps;
|
|
93027
93267
|
}
|
|
93028
93268
|
function defaultRunner2(cmd, args) {
|
|
93029
|
-
const r =
|
|
93269
|
+
const r = spawnSync17(cmd, args, { stdio: "inherit" });
|
|
93030
93270
|
return { status: r.status ?? 1 };
|
|
93031
93271
|
}
|
|
93032
93272
|
function writeMarkerInPreferredLocation(agent, reason, runner) {
|
|
@@ -93069,12 +93309,12 @@ function defaultStatusProbe(composePath) {
|
|
|
93069
93309
|
try {
|
|
93070
93310
|
cliBuiltAt = new Date(statSync45(scriptPath).mtimeMs).toISOString();
|
|
93071
93311
|
} catch {}
|
|
93072
|
-
let dir =
|
|
93312
|
+
let dir = dirname34(scriptPath);
|
|
93073
93313
|
for (let i = 0;i < 8; i++) {
|
|
93074
93314
|
const pkgPath = join84(dir, "package.json");
|
|
93075
|
-
if (
|
|
93315
|
+
if (existsSync81(pkgPath)) {
|
|
93076
93316
|
try {
|
|
93077
|
-
const pkg = JSON.parse(
|
|
93317
|
+
const pkg = JSON.parse(readFileSync75(pkgPath, "utf-8"));
|
|
93078
93318
|
if (typeof pkg.version === "string")
|
|
93079
93319
|
cliVersion = pkg.version;
|
|
93080
93320
|
} catch (err) {
|
|
@@ -93082,7 +93322,7 @@ function defaultStatusProbe(composePath) {
|
|
|
93082
93322
|
}
|
|
93083
93323
|
break;
|
|
93084
93324
|
}
|
|
93085
|
-
const parent =
|
|
93325
|
+
const parent = dirname34(dir);
|
|
93086
93326
|
if (parent === dir)
|
|
93087
93327
|
break;
|
|
93088
93328
|
dir = parent;
|
|
@@ -93095,13 +93335,13 @@ function defaultStatusProbe(composePath) {
|
|
|
93095
93335
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
93096
93336
|
}
|
|
93097
93337
|
const services = [];
|
|
93098
|
-
if (!
|
|
93338
|
+
if (!existsSync81(composePath)) {
|
|
93099
93339
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
93100
93340
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
93101
93341
|
}
|
|
93102
93342
|
let serviceList = [];
|
|
93103
93343
|
try {
|
|
93104
|
-
const r =
|
|
93344
|
+
const r = spawnSync17("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
|
|
93105
93345
|
if (r.status !== 0) {
|
|
93106
93346
|
warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
|
|
93107
93347
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
@@ -93118,7 +93358,7 @@ function defaultStatusProbe(composePath) {
|
|
|
93118
93358
|
let containerCreatedAt = null;
|
|
93119
93359
|
let status = "<unknown>";
|
|
93120
93360
|
try {
|
|
93121
|
-
const r =
|
|
93361
|
+
const r = spawnSync17("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
|
|
93122
93362
|
if (r.status === 0) {
|
|
93123
93363
|
const [img, created, st] = r.stdout.trim().split("|");
|
|
93124
93364
|
image = img ?? null;
|
|
@@ -93134,7 +93374,7 @@ function defaultStatusProbe(composePath) {
|
|
|
93134
93374
|
let imagePulledAt = null;
|
|
93135
93375
|
if (image) {
|
|
93136
93376
|
try {
|
|
93137
|
-
const r =
|
|
93377
|
+
const r = spawnSync17("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
|
|
93138
93378
|
if (r.status === 0) {
|
|
93139
93379
|
const [id, created, lastTag] = r.stdout.trim().split("|");
|
|
93140
93380
|
imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
|
|
@@ -93209,7 +93449,7 @@ function buildReexecArgs(opts) {
|
|
|
93209
93449
|
return args;
|
|
93210
93450
|
}
|
|
93211
93451
|
function defaultReexec(binaryPath, args) {
|
|
93212
|
-
const r =
|
|
93452
|
+
const r = spawnSync17(binaryPath, args, {
|
|
93213
93453
|
stdio: "inherit",
|
|
93214
93454
|
env: { ...process.env, [SELF_UPDATE_ENV_SENTINEL]: "1" }
|
|
93215
93455
|
});
|
|
@@ -93218,7 +93458,7 @@ function defaultReexec(binaryPath, args) {
|
|
|
93218
93458
|
function renderDriftPreamble(opts) {
|
|
93219
93459
|
try {
|
|
93220
93460
|
const exec = opts.componentExec ?? ((cmd, args) => {
|
|
93221
|
-
const r =
|
|
93461
|
+
const r = spawnSync17(cmd, args, { encoding: "utf-8", timeout: 1e4 });
|
|
93222
93462
|
return { status: r.status ?? 1, stdout: r.stdout ?? "" };
|
|
93223
93463
|
});
|
|
93224
93464
|
let pin;
|
|
@@ -93342,23 +93582,23 @@ function registerUpdateCommand(program3) {
|
|
|
93342
93582
|
|
|
93343
93583
|
// src/cli/rollout.ts
|
|
93344
93584
|
init_helpers();
|
|
93345
|
-
import { spawnSync as
|
|
93346
|
-
import { readFileSync as
|
|
93585
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
93586
|
+
import { readFileSync as readFileSync77, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
|
|
93347
93587
|
import { homedir as homedir46 } from "node:os";
|
|
93348
93588
|
|
|
93349
93589
|
// src/cli/rollout-pin-journal.ts
|
|
93350
93590
|
import {
|
|
93351
|
-
existsSync as
|
|
93352
|
-
readFileSync as
|
|
93353
|
-
writeFileSync as
|
|
93354
|
-
renameSync as
|
|
93591
|
+
existsSync as existsSync82,
|
|
93592
|
+
readFileSync as readFileSync76,
|
|
93593
|
+
writeFileSync as writeFileSync32,
|
|
93594
|
+
renameSync as renameSync21,
|
|
93355
93595
|
unlinkSync as unlinkSync15,
|
|
93356
93596
|
statSync as statSync46,
|
|
93357
|
-
mkdirSync as
|
|
93597
|
+
mkdirSync as mkdirSync47
|
|
93358
93598
|
} from "node:fs";
|
|
93359
93599
|
import { homedir as homedir45 } from "node:os";
|
|
93360
93600
|
import { createHash as createHash17 } from "node:crypto";
|
|
93361
|
-
import { join as join85, basename as basename9, resolve as resolve46, dirname as
|
|
93601
|
+
import { join as join85, basename as basename9, resolve as resolve46, dirname as dirname35 } from "node:path";
|
|
93362
93602
|
init_flock();
|
|
93363
93603
|
var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
|
|
93364
93604
|
var STATE_DIR_NAME = ".switchroom";
|
|
@@ -93367,7 +93607,7 @@ function pinJournalDir(configPath) {
|
|
|
93367
93607
|
if (override && override.trim().length > 0)
|
|
93368
93608
|
return override.trim();
|
|
93369
93609
|
if (configPath) {
|
|
93370
|
-
const dir =
|
|
93610
|
+
const dir = dirname35(resolve46(configPath));
|
|
93371
93611
|
if (basename9(dir) === STATE_DIR_NAME)
|
|
93372
93612
|
return dir;
|
|
93373
93613
|
}
|
|
@@ -93410,9 +93650,9 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
|
|
|
93410
93650
|
const p = pinJournalPath(configPath);
|
|
93411
93651
|
let raw;
|
|
93412
93652
|
try {
|
|
93413
|
-
raw =
|
|
93653
|
+
raw = readFileSync76(p, "utf8");
|
|
93414
93654
|
} catch (e) {
|
|
93415
|
-
if (
|
|
93655
|
+
if (existsSync82(p)) {
|
|
93416
93656
|
warn(`\u26a0\ufe0f rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted \u2014 verify it host-side before the next reconcile.
|
|
93417
93657
|
`);
|
|
93418
93658
|
}
|
|
@@ -93463,7 +93703,7 @@ function beginPinPersist(configPath, pin, opts = {}) {
|
|
|
93463
93703
|
warn(`\u26a0\ufe0f rollout pin journal: overwriting an ABANDONED journal at ${p} ` + `(pid ${existing.pid} recorded ${existing.at}, provisional pin ` + `${existing.pin}). Its roll never committed or reverted; \`release.pin\` ` + `in ${configPath} may still name that unproven build \u2014 verify it.
|
|
93464
93704
|
`);
|
|
93465
93705
|
}
|
|
93466
|
-
const priorPin = getReleasePinFromConfig(
|
|
93706
|
+
const priorPin = getReleasePinFromConfig(readFileSync76(configPath, "utf8"));
|
|
93467
93707
|
const journal = {
|
|
93468
93708
|
v: 1,
|
|
93469
93709
|
configPath,
|
|
@@ -93472,10 +93712,10 @@ function beginPinPersist(configPath, pin, opts = {}) {
|
|
|
93472
93712
|
pid: process.pid,
|
|
93473
93713
|
at: new Date().toISOString()
|
|
93474
93714
|
};
|
|
93475
|
-
|
|
93715
|
+
mkdirSync47(dirname35(p), { recursive: true });
|
|
93476
93716
|
const tmp = `${p}.${process.pid}.tmp`;
|
|
93477
|
-
|
|
93478
|
-
|
|
93717
|
+
writeFileSync32(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
|
|
93718
|
+
renameSync21(tmp, p);
|
|
93479
93719
|
return journal;
|
|
93480
93720
|
}
|
|
93481
93721
|
function commitPinPersist(configPath) {
|
|
@@ -93484,7 +93724,7 @@ function commitPinPersist(configPath) {
|
|
|
93484
93724
|
unlinkSync15(p);
|
|
93485
93725
|
return null;
|
|
93486
93726
|
} catch (e) {
|
|
93487
|
-
if (!
|
|
93727
|
+
if (!existsSync82(p))
|
|
93488
93728
|
return null;
|
|
93489
93729
|
return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side \u2014 while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
|
|
93490
93730
|
}
|
|
@@ -93506,7 +93746,7 @@ function rollbackPinPersist(configPath, opts = {}) {
|
|
|
93506
93746
|
}
|
|
93507
93747
|
}
|
|
93508
93748
|
try {
|
|
93509
|
-
const current =
|
|
93749
|
+
const current = readFileSync76(configPath, "utf8");
|
|
93510
93750
|
const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
|
|
93511
93751
|
let mode = 384;
|
|
93512
93752
|
try {
|
|
@@ -93515,7 +93755,7 @@ function rollbackPinPersist(configPath, opts = {}) {
|
|
|
93515
93755
|
if (opts.writeConfig) {
|
|
93516
93756
|
opts.writeConfig(configPath, next, mode);
|
|
93517
93757
|
} else {
|
|
93518
|
-
|
|
93758
|
+
writeFileSync32(configPath, next, { encoding: "utf8", mode });
|
|
93519
93759
|
}
|
|
93520
93760
|
} catch (e) {
|
|
93521
93761
|
return {
|
|
@@ -93563,10 +93803,10 @@ init_audit_reader();
|
|
|
93563
93803
|
init_hindsight();
|
|
93564
93804
|
|
|
93565
93805
|
// src/cli/deploy-version-guard.ts
|
|
93566
|
-
import { spawnSync as
|
|
93806
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
93567
93807
|
var DOCKER_INSPECT_TIMEOUT_MS = 60 * 1000;
|
|
93568
93808
|
var defaultRunner3 = (args) => {
|
|
93569
|
-
const r =
|
|
93809
|
+
const r = spawnSync18("docker", args, {
|
|
93570
93810
|
encoding: "utf8",
|
|
93571
93811
|
timeout: DOCKER_INSPECT_TIMEOUT_MS,
|
|
93572
93812
|
killSignal: "SIGKILL"
|
|
@@ -93990,7 +94230,7 @@ function isSpawnTimeout(r, killSignal) {
|
|
|
93990
94230
|
var ROLLOUT_KILL_SIGNAL = "SIGKILL";
|
|
93991
94231
|
function createRolloutDeps(params) {
|
|
93992
94232
|
const { configPath, scriptPath, hostdCtx } = params;
|
|
93993
|
-
const spawn6 = params.spawn ??
|
|
94233
|
+
const spawn6 = params.spawn ?? spawnSync19;
|
|
93994
94234
|
const warn = params.warn ?? ((line) => process.stderr.write(line));
|
|
93995
94235
|
const self = resolveSelfInvocation({
|
|
93996
94236
|
execPath: params.execPath ?? process.execPath,
|
|
@@ -94075,7 +94315,7 @@ function createRolloutDeps(params) {
|
|
|
94075
94315
|
return { status: r.ok ? 0 : 1, stdout: r.stdout };
|
|
94076
94316
|
}),
|
|
94077
94317
|
persistPin: (pin) => {
|
|
94078
|
-
const before =
|
|
94318
|
+
const before = readFileSync77(configPath, "utf8");
|
|
94079
94319
|
const after = setReleasePinInConfig(before, pin);
|
|
94080
94320
|
if (after === before)
|
|
94081
94321
|
return false;
|
|
@@ -94263,8 +94503,8 @@ init_helpers();
|
|
|
94263
94503
|
init_lifecycle();
|
|
94264
94504
|
init_resolve_version();
|
|
94265
94505
|
import { execSync as execSync3 } from "node:child_process";
|
|
94266
|
-
import { existsSync as
|
|
94267
|
-
import { dirname as
|
|
94506
|
+
import { existsSync as existsSync83, readFileSync as readFileSync78 } from "node:fs";
|
|
94507
|
+
import { dirname as dirname36, join as join86 } from "node:path";
|
|
94268
94508
|
function getClaudeCodeVersion() {
|
|
94269
94509
|
try {
|
|
94270
94510
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -94315,15 +94555,15 @@ function locateSwitchroomInstallDir() {
|
|
|
94315
94555
|
let dir = import.meta.dirname;
|
|
94316
94556
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
94317
94557
|
const pkgPath = join86(dir, "package.json");
|
|
94318
|
-
if (
|
|
94558
|
+
if (existsSync83(pkgPath)) {
|
|
94319
94559
|
try {
|
|
94320
|
-
const pkg = JSON.parse(
|
|
94321
|
-
if (pkg.name === "switchroom" &&
|
|
94560
|
+
const pkg = JSON.parse(readFileSync78(pkgPath, "utf-8"));
|
|
94561
|
+
if (pkg.name === "switchroom" && existsSync83(join86(dir, ".git"))) {
|
|
94322
94562
|
return dir;
|
|
94323
94563
|
}
|
|
94324
94564
|
} catch {}
|
|
94325
94565
|
}
|
|
94326
|
-
dir =
|
|
94566
|
+
dir = dirname36(dir);
|
|
94327
94567
|
}
|
|
94328
94568
|
return null;
|
|
94329
94569
|
}
|
|
@@ -94497,7 +94737,7 @@ init_merge();
|
|
|
94497
94737
|
|
|
94498
94738
|
// src/agents/session-retention.ts
|
|
94499
94739
|
import {
|
|
94500
|
-
existsSync as
|
|
94740
|
+
existsSync as existsSync84,
|
|
94501
94741
|
readdirSync as readdirSync28,
|
|
94502
94742
|
statSync as statSync48,
|
|
94503
94743
|
unlinkSync as unlinkSync16
|
|
@@ -94508,7 +94748,7 @@ var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
|
94508
94748
|
var MIN_KEEP = 2;
|
|
94509
94749
|
function collectSessionJsonl(claudeConfigDir) {
|
|
94510
94750
|
const projects = join87(claudeConfigDir, "projects");
|
|
94511
|
-
if (!
|
|
94751
|
+
if (!existsSync84(projects))
|
|
94512
94752
|
return [];
|
|
94513
94753
|
const found = [];
|
|
94514
94754
|
const walk2 = (dir) => {
|
|
@@ -94644,15 +94884,15 @@ function registerHandoffCommand(program3) {
|
|
|
94644
94884
|
// src/issues/store.ts
|
|
94645
94885
|
import {
|
|
94646
94886
|
closeSync as closeSync16,
|
|
94647
|
-
existsSync as
|
|
94648
|
-
mkdirSync as
|
|
94887
|
+
existsSync as existsSync85,
|
|
94888
|
+
mkdirSync as mkdirSync48,
|
|
94649
94889
|
openSync as openSync16,
|
|
94650
94890
|
readdirSync as readdirSync29,
|
|
94651
|
-
readFileSync as
|
|
94652
|
-
renameSync as
|
|
94891
|
+
readFileSync as readFileSync79,
|
|
94892
|
+
renameSync as renameSync22,
|
|
94653
94893
|
statSync as statSync49,
|
|
94654
94894
|
unlinkSync as unlinkSync17,
|
|
94655
|
-
writeFileSync as
|
|
94895
|
+
writeFileSync as writeFileSync33,
|
|
94656
94896
|
writeSync as writeSync9
|
|
94657
94897
|
} from "node:fs";
|
|
94658
94898
|
import { join as join88 } from "node:path";
|
|
@@ -94683,11 +94923,11 @@ var ISSUES_FILE = "issues.jsonl";
|
|
|
94683
94923
|
var ISSUES_LOCK = "issues.lock";
|
|
94684
94924
|
function readAll(stateDir) {
|
|
94685
94925
|
const path7 = join88(stateDir, ISSUES_FILE);
|
|
94686
|
-
if (!
|
|
94926
|
+
if (!existsSync85(path7))
|
|
94687
94927
|
return [];
|
|
94688
94928
|
let raw;
|
|
94689
94929
|
try {
|
|
94690
|
-
raw =
|
|
94930
|
+
raw = readFileSync79(path7, "utf-8");
|
|
94691
94931
|
} catch {
|
|
94692
94932
|
return [];
|
|
94693
94933
|
}
|
|
@@ -94760,7 +95000,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
94760
95000
|
});
|
|
94761
95001
|
}
|
|
94762
95002
|
function resolve49(stateDir, fingerprint, nowFn = Date.now) {
|
|
94763
|
-
if (!
|
|
95003
|
+
if (!existsSync85(join88(stateDir, ISSUES_FILE)))
|
|
94764
95004
|
return 0;
|
|
94765
95005
|
return withLock(stateDir, () => {
|
|
94766
95006
|
const all = readAll(stateDir);
|
|
@@ -94778,7 +95018,7 @@ function resolve49(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
94778
95018
|
});
|
|
94779
95019
|
}
|
|
94780
95020
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
94781
|
-
if (!
|
|
95021
|
+
if (!existsSync85(join88(stateDir, ISSUES_FILE)))
|
|
94782
95022
|
return 0;
|
|
94783
95023
|
return withLock(stateDir, () => {
|
|
94784
95024
|
const all = readAll(stateDir);
|
|
@@ -94796,7 +95036,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
94796
95036
|
});
|
|
94797
95037
|
}
|
|
94798
95038
|
function prune(stateDir, opts = {}) {
|
|
94799
|
-
if (!
|
|
95039
|
+
if (!existsSync85(join88(stateDir, ISSUES_FILE)))
|
|
94800
95040
|
return 0;
|
|
94801
95041
|
return withLock(stateDir, () => {
|
|
94802
95042
|
const all = readAll(stateDir);
|
|
@@ -94826,7 +95066,7 @@ function prune(stateDir, opts = {}) {
|
|
|
94826
95066
|
});
|
|
94827
95067
|
}
|
|
94828
95068
|
function ensureDir(stateDir) {
|
|
94829
|
-
|
|
95069
|
+
mkdirSync48(stateDir, { recursive: true });
|
|
94830
95070
|
}
|
|
94831
95071
|
function writeAll(stateDir, events) {
|
|
94832
95072
|
const path7 = join88(stateDir, ISSUES_FILE);
|
|
@@ -94835,8 +95075,8 @@ function writeAll(stateDir, events) {
|
|
|
94835
95075
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
94836
95076
|
`) + `
|
|
94837
95077
|
`;
|
|
94838
|
-
|
|
94839
|
-
|
|
95078
|
+
writeFileSync33(tmp, body, "utf-8");
|
|
95079
|
+
renameSync22(tmp, path7);
|
|
94840
95080
|
}
|
|
94841
95081
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
94842
95082
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -94898,7 +95138,7 @@ function withLock(stateDir, fn) {
|
|
|
94898
95138
|
function tryStealStaleLock(lockPath) {
|
|
94899
95139
|
let pidStr;
|
|
94900
95140
|
try {
|
|
94901
|
-
pidStr =
|
|
95141
|
+
pidStr = readFileSync79(lockPath, "utf-8").trim();
|
|
94902
95142
|
} catch {
|
|
94903
95143
|
return true;
|
|
94904
95144
|
}
|
|
@@ -95146,20 +95386,20 @@ function relTime(deltaMs) {
|
|
|
95146
95386
|
|
|
95147
95387
|
// src/cli/deps.ts
|
|
95148
95388
|
init_source();
|
|
95149
|
-
import { existsSync as
|
|
95389
|
+
import { existsSync as existsSync88 } from "node:fs";
|
|
95150
95390
|
import { homedir as homedir49 } from "node:os";
|
|
95151
95391
|
import { join as join91, resolve as resolve50 } from "node:path";
|
|
95152
95392
|
|
|
95153
95393
|
// src/deps/python.ts
|
|
95154
95394
|
import { createHash as createHash18 } from "node:crypto";
|
|
95155
95395
|
import {
|
|
95156
|
-
existsSync as
|
|
95157
|
-
mkdirSync as
|
|
95158
|
-
readFileSync as
|
|
95396
|
+
existsSync as existsSync86,
|
|
95397
|
+
mkdirSync as mkdirSync49,
|
|
95398
|
+
readFileSync as readFileSync80,
|
|
95159
95399
|
rmSync as rmSync15,
|
|
95160
|
-
writeFileSync as
|
|
95400
|
+
writeFileSync as writeFileSync34
|
|
95161
95401
|
} from "node:fs";
|
|
95162
|
-
import { dirname as
|
|
95402
|
+
import { dirname as dirname37, join as join89 } from "node:path";
|
|
95163
95403
|
import { homedir as homedir47 } from "node:os";
|
|
95164
95404
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
95165
95405
|
|
|
@@ -95175,13 +95415,13 @@ function defaultPythonCacheRoot() {
|
|
|
95175
95415
|
return join89(homedir47(), ".switchroom", "deps", "python");
|
|
95176
95416
|
}
|
|
95177
95417
|
function hashFile(path7) {
|
|
95178
|
-
return createHash18("sha256").update(
|
|
95418
|
+
return createHash18("sha256").update(readFileSync80(path7)).digest("hex");
|
|
95179
95419
|
}
|
|
95180
95420
|
function ensurePythonEnv(opts) {
|
|
95181
95421
|
const { skillName, requirementsPath, force = false } = opts;
|
|
95182
95422
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
95183
95423
|
const hostPython = opts.pythonBin ?? "python3";
|
|
95184
|
-
if (!
|
|
95424
|
+
if (!existsSync86(requirementsPath)) {
|
|
95185
95425
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
95186
95426
|
}
|
|
95187
95427
|
const venvDir = join89(cacheRoot, skillName);
|
|
@@ -95190,8 +95430,8 @@ function ensurePythonEnv(opts) {
|
|
|
95190
95430
|
const pythonBin = join89(binDir, "python");
|
|
95191
95431
|
const pipBin = join89(binDir, "pip");
|
|
95192
95432
|
const targetHash = hashFile(requirementsPath);
|
|
95193
|
-
if (!force &&
|
|
95194
|
-
const existingHash =
|
|
95433
|
+
if (!force && existsSync86(stampPath) && existsSync86(pythonBin)) {
|
|
95434
|
+
const existingHash = readFileSync80(stampPath, "utf8").trim();
|
|
95195
95435
|
if (existingHash === targetHash) {
|
|
95196
95436
|
return {
|
|
95197
95437
|
skillName,
|
|
@@ -95203,10 +95443,10 @@ function ensurePythonEnv(opts) {
|
|
|
95203
95443
|
};
|
|
95204
95444
|
}
|
|
95205
95445
|
}
|
|
95206
|
-
if (
|
|
95446
|
+
if (existsSync86(venvDir)) {
|
|
95207
95447
|
rmSync15(venvDir, { recursive: true, force: true });
|
|
95208
95448
|
}
|
|
95209
|
-
|
|
95449
|
+
mkdirSync49(dirname37(venvDir), { recursive: true });
|
|
95210
95450
|
try {
|
|
95211
95451
|
execFileSync24(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
95212
95452
|
} catch (err) {
|
|
@@ -95225,7 +95465,7 @@ function ensurePythonEnv(opts) {
|
|
|
95225
95465
|
const e = err;
|
|
95226
95466
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
95227
95467
|
}
|
|
95228
|
-
|
|
95468
|
+
writeFileSync34(stampPath, targetHash + `
|
|
95229
95469
|
`);
|
|
95230
95470
|
return {
|
|
95231
95471
|
skillName,
|
|
@@ -95241,13 +95481,13 @@ function ensurePythonEnv(opts) {
|
|
|
95241
95481
|
import { createHash as createHash19 } from "node:crypto";
|
|
95242
95482
|
import {
|
|
95243
95483
|
copyFileSync as copyFileSync12,
|
|
95244
|
-
existsSync as
|
|
95245
|
-
mkdirSync as
|
|
95246
|
-
readFileSync as
|
|
95484
|
+
existsSync as existsSync87,
|
|
95485
|
+
mkdirSync as mkdirSync50,
|
|
95486
|
+
readFileSync as readFileSync81,
|
|
95247
95487
|
rmSync as rmSync16,
|
|
95248
|
-
writeFileSync as
|
|
95488
|
+
writeFileSync as writeFileSync35
|
|
95249
95489
|
} from "node:fs";
|
|
95250
|
-
import { dirname as
|
|
95490
|
+
import { dirname as dirname38, join as join90 } from "node:path";
|
|
95251
95491
|
import { homedir as homedir48 } from "node:os";
|
|
95252
95492
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
95253
95493
|
|
|
@@ -95274,20 +95514,20 @@ function defaultNodeCacheRoot() {
|
|
|
95274
95514
|
return join90(homedir48(), ".switchroom", "deps", "node");
|
|
95275
95515
|
}
|
|
95276
95516
|
function hashDepInputs(packageJsonPath) {
|
|
95277
|
-
const sourceDir =
|
|
95517
|
+
const sourceDir = dirname38(packageJsonPath);
|
|
95278
95518
|
const hasher = createHash19("sha256");
|
|
95279
95519
|
hasher.update(`package.json
|
|
95280
95520
|
`);
|
|
95281
|
-
hasher.update(
|
|
95521
|
+
hasher.update(readFileSync81(packageJsonPath));
|
|
95282
95522
|
for (const lockName of ALL_LOCKFILES) {
|
|
95283
95523
|
const lockPath = join90(sourceDir, lockName);
|
|
95284
|
-
if (
|
|
95524
|
+
if (existsSync87(lockPath)) {
|
|
95285
95525
|
hasher.update(`
|
|
95286
95526
|
`);
|
|
95287
95527
|
hasher.update(lockName);
|
|
95288
95528
|
hasher.update(`
|
|
95289
95529
|
`);
|
|
95290
|
-
hasher.update(
|
|
95530
|
+
hasher.update(readFileSync81(lockPath));
|
|
95291
95531
|
}
|
|
95292
95532
|
}
|
|
95293
95533
|
return hasher.digest("hex");
|
|
@@ -95296,17 +95536,17 @@ function ensureNodeEnv(opts) {
|
|
|
95296
95536
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
95297
95537
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
95298
95538
|
const installer = opts.installer ?? "bun";
|
|
95299
|
-
if (!
|
|
95539
|
+
if (!existsSync87(packageJsonPath)) {
|
|
95300
95540
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
95301
95541
|
}
|
|
95302
|
-
const sourceDir =
|
|
95542
|
+
const sourceDir = dirname38(packageJsonPath);
|
|
95303
95543
|
const envDir = join90(cacheRoot, skillName);
|
|
95304
95544
|
const stampPath = join90(envDir, ".package.sha256");
|
|
95305
95545
|
const nodeModulesDir = join90(envDir, "node_modules");
|
|
95306
95546
|
const binDir = join90(nodeModulesDir, ".bin");
|
|
95307
95547
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
95308
|
-
if (!force &&
|
|
95309
|
-
const existingHash =
|
|
95548
|
+
if (!force && existsSync87(stampPath) && existsSync87(nodeModulesDir)) {
|
|
95549
|
+
const existingHash = readFileSync81(stampPath, "utf8").trim();
|
|
95310
95550
|
if (existingHash === targetHash) {
|
|
95311
95551
|
return {
|
|
95312
95552
|
skillName,
|
|
@@ -95317,15 +95557,15 @@ function ensureNodeEnv(opts) {
|
|
|
95317
95557
|
};
|
|
95318
95558
|
}
|
|
95319
95559
|
}
|
|
95320
|
-
if (
|
|
95560
|
+
if (existsSync87(envDir)) {
|
|
95321
95561
|
rmSync16(envDir, { recursive: true, force: true });
|
|
95322
95562
|
}
|
|
95323
|
-
|
|
95563
|
+
mkdirSync50(envDir, { recursive: true });
|
|
95324
95564
|
copyFileSync12(packageJsonPath, join90(envDir, "package.json"));
|
|
95325
95565
|
let copiedLockfile = false;
|
|
95326
95566
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
95327
95567
|
const lockPath = join90(sourceDir, lockName);
|
|
95328
|
-
if (
|
|
95568
|
+
if (existsSync87(lockPath)) {
|
|
95329
95569
|
copyFileSync12(lockPath, join90(envDir, lockName));
|
|
95330
95570
|
copiedLockfile = true;
|
|
95331
95571
|
}
|
|
@@ -95342,7 +95582,7 @@ function ensureNodeEnv(opts) {
|
|
|
95342
95582
|
const e = err;
|
|
95343
95583
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
95344
95584
|
}
|
|
95345
|
-
|
|
95585
|
+
writeFileSync35(stampPath, targetHash + `
|
|
95346
95586
|
`);
|
|
95347
95587
|
return {
|
|
95348
95588
|
skillName,
|
|
@@ -95361,22 +95601,22 @@ function registerDepsCommand(program3) {
|
|
|
95361
95601
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
95362
95602
|
deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
|
|
95363
95603
|
const skillsRoot = builtinSkillsRoot();
|
|
95364
|
-
if (!
|
|
95604
|
+
if (!existsSync88(skillsRoot)) {
|
|
95365
95605
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
95366
95606
|
process.exit(1);
|
|
95367
95607
|
}
|
|
95368
95608
|
const skillDir = join91(skillsRoot, skill);
|
|
95369
|
-
if (!
|
|
95609
|
+
if (!existsSync88(skillDir)) {
|
|
95370
95610
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
95371
95611
|
process.exit(1);
|
|
95372
95612
|
}
|
|
95373
95613
|
const requirementsPath = join91(skillDir, "requirements.txt");
|
|
95374
95614
|
const packageJsonPath = join91(skillDir, "package.json");
|
|
95375
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
95376
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
95615
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync88(requirementsPath));
|
|
95616
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync88(packageJsonPath));
|
|
95377
95617
|
let did = 0;
|
|
95378
95618
|
if (wantPython) {
|
|
95379
|
-
if (!
|
|
95619
|
+
if (!existsSync88(requirementsPath)) {
|
|
95380
95620
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
95381
95621
|
process.exit(1);
|
|
95382
95622
|
}
|
|
@@ -95400,7 +95640,7 @@ function registerDepsCommand(program3) {
|
|
|
95400
95640
|
}
|
|
95401
95641
|
}
|
|
95402
95642
|
if (wantNode) {
|
|
95403
|
-
if (!
|
|
95643
|
+
if (!existsSync88(packageJsonPath)) {
|
|
95404
95644
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
95405
95645
|
process.exit(1);
|
|
95406
95646
|
}
|
|
@@ -95433,9 +95673,9 @@ function registerDepsCommand(program3) {
|
|
|
95433
95673
|
// src/cli/workspace.ts
|
|
95434
95674
|
init_helpers();
|
|
95435
95675
|
init_loader();
|
|
95436
|
-
import { existsSync as
|
|
95676
|
+
import { existsSync as existsSync89 } from "node:fs";
|
|
95437
95677
|
import { resolve as resolve51, sep as sep4 } from "node:path";
|
|
95438
|
-
import { spawnSync as
|
|
95678
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
95439
95679
|
|
|
95440
95680
|
// src/agents/workspace.ts
|
|
95441
95681
|
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
@@ -96148,7 +96388,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
96148
96388
|
process.exit(1);
|
|
96149
96389
|
}
|
|
96150
96390
|
const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
96151
|
-
const child =
|
|
96391
|
+
const child = spawnSync20(editor, [target], { stdio: "inherit" });
|
|
96152
96392
|
if (child.status !== 0 && child.status !== null) {
|
|
96153
96393
|
process.exit(child.status);
|
|
96154
96394
|
}
|
|
@@ -96210,12 +96450,12 @@ function registerWorkspaceCommand(program3) {
|
|
|
96210
96450
|
if (!dir)
|
|
96211
96451
|
return;
|
|
96212
96452
|
const gitDir = resolve51(dir, ".git");
|
|
96213
|
-
if (!
|
|
96453
|
+
if (!existsSync89(gitDir)) {
|
|
96214
96454
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
96215
96455
|
`);
|
|
96216
96456
|
return;
|
|
96217
96457
|
}
|
|
96218
|
-
const statusResult =
|
|
96458
|
+
const statusResult = spawnSync20("git", ["status", "--short"], {
|
|
96219
96459
|
cwd: dir,
|
|
96220
96460
|
encoding: "utf-8"
|
|
96221
96461
|
});
|
|
@@ -96230,7 +96470,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
96230
96470
|
return;
|
|
96231
96471
|
}
|
|
96232
96472
|
const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
|
|
96233
|
-
const addResult =
|
|
96473
|
+
const addResult = spawnSync20("git", ["add", "-A"], {
|
|
96234
96474
|
cwd: dir,
|
|
96235
96475
|
encoding: "utf-8"
|
|
96236
96476
|
});
|
|
@@ -96239,7 +96479,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
96239
96479
|
`);
|
|
96240
96480
|
process.exit(1);
|
|
96241
96481
|
}
|
|
96242
|
-
const commitResult =
|
|
96482
|
+
const commitResult = spawnSync20("git", ["commit", "-m", message], {
|
|
96243
96483
|
cwd: dir,
|
|
96244
96484
|
encoding: "utf-8"
|
|
96245
96485
|
});
|
|
@@ -96248,7 +96488,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
96248
96488
|
`);
|
|
96249
96489
|
process.exit(1);
|
|
96250
96490
|
}
|
|
96251
|
-
const shaResult =
|
|
96491
|
+
const shaResult = spawnSync20("git", ["rev-parse", "--short", "HEAD"], {
|
|
96252
96492
|
cwd: dir,
|
|
96253
96493
|
encoding: "utf-8"
|
|
96254
96494
|
});
|
|
@@ -96264,12 +96504,12 @@ function registerWorkspaceCommand(program3) {
|
|
|
96264
96504
|
if (!dir)
|
|
96265
96505
|
return;
|
|
96266
96506
|
const gitDir = resolve51(dir, ".git");
|
|
96267
|
-
if (!
|
|
96507
|
+
if (!existsSync89(gitDir)) {
|
|
96268
96508
|
process.stdout.write(`Workspace is not a git repository.
|
|
96269
96509
|
`);
|
|
96270
96510
|
return;
|
|
96271
96511
|
}
|
|
96272
|
-
const child =
|
|
96512
|
+
const child = spawnSync20("git", ["status", "--short"], {
|
|
96273
96513
|
cwd: dir,
|
|
96274
96514
|
stdio: "inherit"
|
|
96275
96515
|
});
|
|
@@ -96289,7 +96529,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
96289
96529
|
const agentsDir = resolveAgentsDir(config);
|
|
96290
96530
|
const agentDir = resolve51(agentsDir, agentName);
|
|
96291
96531
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
96292
|
-
if (!
|
|
96532
|
+
if (!existsSync89(dir)) {
|
|
96293
96533
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
96294
96534
|
`);
|
|
96295
96535
|
return;
|
|
@@ -96325,7 +96565,7 @@ function safeParseInt(value, fallback) {
|
|
|
96325
96565
|
init_helpers();
|
|
96326
96566
|
init_loader();
|
|
96327
96567
|
init_merge();
|
|
96328
|
-
import { copyFileSync as copyFileSync13, existsSync as
|
|
96568
|
+
import { copyFileSync as copyFileSync13, existsSync as existsSync90, readFileSync as readFileSync82, writeFileSync as writeFileSync36 } from "node:fs";
|
|
96329
96569
|
import { join as join92, resolve as resolve52 } from "node:path";
|
|
96330
96570
|
init_scaffold();
|
|
96331
96571
|
init_profiles();
|
|
@@ -96343,7 +96583,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
96343
96583
|
const agentsDir = resolveAgentsDir(config);
|
|
96344
96584
|
const agentDir = resolve52(agentsDir, agentName);
|
|
96345
96585
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
96346
|
-
if (!
|
|
96586
|
+
if (!existsSync90(workspaceDir)) {
|
|
96347
96587
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
96348
96588
|
process.exit(1);
|
|
96349
96589
|
}
|
|
@@ -96369,11 +96609,11 @@ function registerSoulCommand(program3) {
|
|
|
96369
96609
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
96370
96610
|
if (!t)
|
|
96371
96611
|
return;
|
|
96372
|
-
if (!
|
|
96612
|
+
if (!existsSync90(t.soulPath)) {
|
|
96373
96613
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
96374
96614
|
process.exit(1);
|
|
96375
96615
|
}
|
|
96376
|
-
process.stdout.write(
|
|
96616
|
+
process.stdout.write(readFileSync82(t.soulPath, "utf-8"));
|
|
96377
96617
|
}));
|
|
96378
96618
|
cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
|
|
96379
96619
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -96384,7 +96624,7 @@ function registerSoulCommand(program3) {
|
|
|
96384
96624
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
96385
96625
|
process.exit(1);
|
|
96386
96626
|
}
|
|
96387
|
-
const exists =
|
|
96627
|
+
const exists = existsSync90(t.soulPath);
|
|
96388
96628
|
if (exists && !opts.yes) {
|
|
96389
96629
|
if (!isInteractive()) {
|
|
96390
96630
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -96399,12 +96639,12 @@ function registerSoulCommand(program3) {
|
|
|
96399
96639
|
let backupPath;
|
|
96400
96640
|
if (exists) {
|
|
96401
96641
|
backupPath = `${t.soulPath}.bak`;
|
|
96402
|
-
if (
|
|
96642
|
+
if (existsSync90(backupPath)) {
|
|
96403
96643
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
96404
96644
|
}
|
|
96405
96645
|
copyFileSync13(t.soulPath, backupPath);
|
|
96406
96646
|
}
|
|
96407
|
-
|
|
96647
|
+
writeFileSync36(t.soulPath, content, "utf-8");
|
|
96408
96648
|
if (backupPath) {
|
|
96409
96649
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
96410
96650
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -96418,7 +96658,7 @@ function registerSoulCommand(program3) {
|
|
|
96418
96658
|
// src/cli/debug.ts
|
|
96419
96659
|
init_helpers();
|
|
96420
96660
|
init_loader();
|
|
96421
|
-
import { existsSync as
|
|
96661
|
+
import { existsSync as existsSync91, readFileSync as readFileSync83, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
|
|
96422
96662
|
import { resolve as resolve53, join as join93 } from "node:path";
|
|
96423
96663
|
import { createHash as createHash20 } from "node:crypto";
|
|
96424
96664
|
init_merge();
|
|
@@ -96431,10 +96671,10 @@ function estimateTokens(bytes) {
|
|
|
96431
96671
|
}
|
|
96432
96672
|
function readMcpServerNames(agentDir) {
|
|
96433
96673
|
const mcpPath = join93(agentDir, ".mcp.json");
|
|
96434
|
-
if (!
|
|
96674
|
+
if (!existsSync91(mcpPath))
|
|
96435
96675
|
return [];
|
|
96436
96676
|
try {
|
|
96437
|
-
const parsed = JSON.parse(
|
|
96677
|
+
const parsed = JSON.parse(readFileSync83(mcpPath, "utf-8"));
|
|
96438
96678
|
return Object.keys(parsed.mcpServers ?? {});
|
|
96439
96679
|
} catch {
|
|
96440
96680
|
return null;
|
|
@@ -96445,7 +96685,7 @@ function sha256(content) {
|
|
|
96445
96685
|
}
|
|
96446
96686
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
96447
96687
|
const projectsDir = join93(claudeConfigDir, "projects");
|
|
96448
|
-
if (!
|
|
96688
|
+
if (!existsSync91(projectsDir))
|
|
96449
96689
|
return;
|
|
96450
96690
|
try {
|
|
96451
96691
|
const entries = readdirSync30(projectsDir, { withFileTypes: true });
|
|
@@ -96455,7 +96695,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
96455
96695
|
continue;
|
|
96456
96696
|
const projectPath = join93(projectsDir, entry.name);
|
|
96457
96697
|
const transcriptPath = join93(projectPath, "transcript.jsonl");
|
|
96458
|
-
if (!
|
|
96698
|
+
if (!existsSync91(transcriptPath))
|
|
96459
96699
|
continue;
|
|
96460
96700
|
const stat3 = statSync50(transcriptPath);
|
|
96461
96701
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -96469,7 +96709,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
96469
96709
|
}
|
|
96470
96710
|
function extractLatestUserMessage(transcriptPath) {
|
|
96471
96711
|
try {
|
|
96472
|
-
const content =
|
|
96712
|
+
const content = readFileSync83(transcriptPath, "utf-8");
|
|
96473
96713
|
const lines = content.trim().split(`
|
|
96474
96714
|
`).filter(Boolean);
|
|
96475
96715
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -96518,7 +96758,7 @@ function registerDebugCommand(program3) {
|
|
|
96518
96758
|
}
|
|
96519
96759
|
const agentsDir = resolveAgentsDir(config);
|
|
96520
96760
|
const agentDir = resolve53(agentsDir, agentName);
|
|
96521
|
-
if (!
|
|
96761
|
+
if (!existsSync91(agentDir)) {
|
|
96522
96762
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
96523
96763
|
process.exit(1);
|
|
96524
96764
|
}
|
|
@@ -96573,7 +96813,7 @@ function registerDebugCommand(program3) {
|
|
|
96573
96813
|
}
|
|
96574
96814
|
console.log(`=== Append System Prompt (per-session) ===
|
|
96575
96815
|
`);
|
|
96576
|
-
const handoffContent =
|
|
96816
|
+
const handoffContent = existsSync91(handoffPath) ? readFileSync83(handoffPath, "utf-8") : "";
|
|
96577
96817
|
if (handoffContent.trim().length > 0) {
|
|
96578
96818
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
96579
96819
|
console.log(handoffContent);
|
|
@@ -96584,7 +96824,7 @@ function registerDebugCommand(program3) {
|
|
|
96584
96824
|
}
|
|
96585
96825
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
96586
96826
|
`);
|
|
96587
|
-
const claudeMdContent =
|
|
96827
|
+
const claudeMdContent = existsSync91(claudeMdPath) ? readFileSync83(claudeMdPath, "utf-8") : "";
|
|
96588
96828
|
if (claudeMdContent.trim().length > 0) {
|
|
96589
96829
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
96590
96830
|
console.log(claudeMdContent);
|
|
@@ -96595,7 +96835,7 @@ function registerDebugCommand(program3) {
|
|
|
96595
96835
|
}
|
|
96596
96836
|
console.log(`=== Persona (SOUL.md) ===
|
|
96597
96837
|
`);
|
|
96598
|
-
const soulMdContent =
|
|
96838
|
+
const soulMdContent = existsSync91(soulMdPath) ? readFileSync83(soulMdPath, "utf-8") : existsSync91(workspaceSoulMdPath) ? readFileSync83(workspaceSoulMdPath, "utf-8") : "";
|
|
96599
96839
|
if (soulMdContent.trim().length > 0) {
|
|
96600
96840
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
96601
96841
|
console.log(soulMdContent);
|
|
@@ -96659,8 +96899,8 @@ function registerDebugCommand(program3) {
|
|
|
96659
96899
|
const fleetDir = join93(agentsDir, "..", "fleet");
|
|
96660
96900
|
const fleetInvPath = join93(fleetDir, "switchroom-invariants.md");
|
|
96661
96901
|
const fleetClaudePath = join93(fleetDir, "CLAUDE.md");
|
|
96662
|
-
const fleetInvBytes =
|
|
96663
|
-
const fleetClaudeBytes =
|
|
96902
|
+
const fleetInvBytes = existsSync91(fleetInvPath) ? readFileSync83(fleetInvPath, "utf-8").length : 0;
|
|
96903
|
+
const fleetClaudeBytes = existsSync91(fleetClaudePath) ? readFileSync83(fleetClaudePath, "utf-8").length : 0;
|
|
96664
96904
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
96665
96905
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
96666
96906
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -96693,20 +96933,20 @@ init_source();
|
|
|
96693
96933
|
|
|
96694
96934
|
// src/worktree/claim.ts
|
|
96695
96935
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
96696
|
-
import { closeSync as closeSync17, mkdirSync as
|
|
96936
|
+
import { closeSync as closeSync17, mkdirSync as mkdirSync52, openSync as openSync17, existsSync as existsSync93, unlinkSync as unlinkSync19 } from "node:fs";
|
|
96697
96937
|
import { join as join95, resolve as resolve55 } from "node:path";
|
|
96698
96938
|
import { homedir as homedir51 } from "node:os";
|
|
96699
96939
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
96700
96940
|
|
|
96701
96941
|
// src/worktree/registry.ts
|
|
96702
96942
|
import {
|
|
96703
|
-
mkdirSync as
|
|
96704
|
-
writeFileSync as
|
|
96705
|
-
readFileSync as
|
|
96943
|
+
mkdirSync as mkdirSync51,
|
|
96944
|
+
writeFileSync as writeFileSync37,
|
|
96945
|
+
readFileSync as readFileSync84,
|
|
96706
96946
|
readdirSync as readdirSync31,
|
|
96707
96947
|
unlinkSync as unlinkSync18,
|
|
96708
|
-
existsSync as
|
|
96709
|
-
renameSync as
|
|
96948
|
+
existsSync as existsSync92,
|
|
96949
|
+
renameSync as renameSync23
|
|
96710
96950
|
} from "node:fs";
|
|
96711
96951
|
import { join as join94, resolve as resolve54 } from "node:path";
|
|
96712
96952
|
import { homedir as homedir50 } from "node:os";
|
|
@@ -96717,20 +96957,20 @@ function recordPath(id) {
|
|
|
96717
96957
|
return join94(registryDir(), `${id}.json`);
|
|
96718
96958
|
}
|
|
96719
96959
|
function ensureDir2() {
|
|
96720
|
-
|
|
96960
|
+
mkdirSync51(registryDir(), { recursive: true });
|
|
96721
96961
|
}
|
|
96722
96962
|
function writeRecord(record2) {
|
|
96723
96963
|
ensureDir2();
|
|
96724
96964
|
const target = recordPath(record2.id);
|
|
96725
96965
|
const tmp = `${target}.tmp${process.pid}`;
|
|
96726
|
-
|
|
96966
|
+
writeFileSync37(tmp, JSON.stringify(record2, null, 2) + `
|
|
96727
96967
|
`, { mode: 384 });
|
|
96728
|
-
|
|
96968
|
+
renameSync23(tmp, target);
|
|
96729
96969
|
}
|
|
96730
96970
|
function readRecord(id) {
|
|
96731
96971
|
const path10 = recordPath(id);
|
|
96732
96972
|
try {
|
|
96733
|
-
const raw =
|
|
96973
|
+
const raw = readFileSync84(path10, "utf8");
|
|
96734
96974
|
return JSON.parse(raw);
|
|
96735
96975
|
} catch {
|
|
96736
96976
|
return null;
|
|
@@ -96763,7 +97003,7 @@ function countByRepo(repoPath) {
|
|
|
96763
97003
|
// src/worktree/claim.ts
|
|
96764
97004
|
function acquireRepoLock(repoPath) {
|
|
96765
97005
|
const lockDir = registryDir();
|
|
96766
|
-
|
|
97006
|
+
mkdirSync52(lockDir, { recursive: true });
|
|
96767
97007
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
96768
97008
|
const lockPath = join95(lockDir, `.lock-${lockName}`);
|
|
96769
97009
|
const deadline = Date.now() + 5000;
|
|
@@ -96819,7 +97059,7 @@ function expandHome(p) {
|
|
|
96819
97059
|
}
|
|
96820
97060
|
async function claimWorktree(input, codeRepos) {
|
|
96821
97061
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
96822
|
-
if (!
|
|
97062
|
+
if (!existsSync93(repoPath)) {
|
|
96823
97063
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
96824
97064
|
}
|
|
96825
97065
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -96841,7 +97081,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
96841
97081
|
const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
|
|
96842
97082
|
branch = `task/${taskSuffix}-${id}`;
|
|
96843
97083
|
const baseDir = worktreesBaseDir();
|
|
96844
|
-
|
|
97084
|
+
mkdirSync52(baseDir, { recursive: true });
|
|
96845
97085
|
worktreePath = join95(baseDir, `${id}-${taskSuffix}`);
|
|
96846
97086
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
96847
97087
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
@@ -96875,7 +97115,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
96875
97115
|
|
|
96876
97116
|
// src/worktree/release.ts
|
|
96877
97117
|
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
96878
|
-
import { existsSync as
|
|
97118
|
+
import { existsSync as existsSync94 } from "node:fs";
|
|
96879
97119
|
function releaseWorktree(input) {
|
|
96880
97120
|
const { id } = input;
|
|
96881
97121
|
const record2 = readRecord(id);
|
|
@@ -96883,7 +97123,7 @@ function releaseWorktree(input) {
|
|
|
96883
97123
|
return { released: true };
|
|
96884
97124
|
}
|
|
96885
97125
|
let gitSuccess = true;
|
|
96886
|
-
if (
|
|
97126
|
+
if (existsSync94(record2.path)) {
|
|
96887
97127
|
try {
|
|
96888
97128
|
execFileSync27("git", ["worktree", "remove", "--force", record2.path], {
|
|
96889
97129
|
cwd: record2.repo,
|
|
@@ -96922,7 +97162,7 @@ function listWorktrees() {
|
|
|
96922
97162
|
|
|
96923
97163
|
// src/worktree/reaper.ts
|
|
96924
97164
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
96925
|
-
import { existsSync as
|
|
97165
|
+
import { existsSync as existsSync95 } from "node:fs";
|
|
96926
97166
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
96927
97167
|
function reapSkipReasonText(action) {
|
|
96928
97168
|
switch (action) {
|
|
@@ -96983,7 +97223,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
96983
97223
|
const plan = [];
|
|
96984
97224
|
for (const record2 of listRecords()) {
|
|
96985
97225
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
96986
|
-
const worktreeExists =
|
|
97226
|
+
const worktreeExists = existsSync95(record2.path);
|
|
96987
97227
|
if (!worktreeExists) {
|
|
96988
97228
|
plan.push({
|
|
96989
97229
|
record: record2,
|
|
@@ -97064,12 +97304,12 @@ function runReaper(nowMs, deps = {}) {
|
|
|
97064
97304
|
// src/worktree/gc.ts
|
|
97065
97305
|
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
97066
97306
|
import {
|
|
97067
|
-
existsSync as
|
|
97068
|
-
readFileSync as
|
|
97307
|
+
existsSync as existsSync96,
|
|
97308
|
+
readFileSync as readFileSync85,
|
|
97069
97309
|
readdirSync as readdirSync32,
|
|
97070
97310
|
statSync as statSync51,
|
|
97071
|
-
renameSync as
|
|
97072
|
-
mkdirSync as
|
|
97311
|
+
renameSync as renameSync24,
|
|
97312
|
+
mkdirSync as mkdirSync53,
|
|
97073
97313
|
rmSync as rmSync17
|
|
97074
97314
|
} from "node:fs";
|
|
97075
97315
|
import { homedir as homedir52 } from "node:os";
|
|
@@ -97191,9 +97431,9 @@ function trashRoot() {
|
|
|
97191
97431
|
return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join96(homedir52(), ".switchroom", "worktree-gc-trash"));
|
|
97192
97432
|
}
|
|
97193
97433
|
function planGc(roots, deps = {}) {
|
|
97194
|
-
const exists = deps.existsSync ??
|
|
97434
|
+
const exists = deps.existsSync ?? existsSync96;
|
|
97195
97435
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
97196
|
-
const readFile4 = deps.readFile ?? ((p) =>
|
|
97436
|
+
const readFile4 = deps.readFile ?? ((p) => readFileSync85(p, "utf8"));
|
|
97197
97437
|
const stat3 = deps.stat ?? ((p) => statSync51(p));
|
|
97198
97438
|
const exec = deps.exec ?? defaultExec3;
|
|
97199
97439
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
@@ -97321,10 +97561,10 @@ function planGc(roots, deps = {}) {
|
|
|
97321
97561
|
}
|
|
97322
97562
|
function applyGc(plan, deps = {}) {
|
|
97323
97563
|
const exec = deps.exec ?? defaultExec3;
|
|
97324
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
97564
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync53(p, { recursive: true }));
|
|
97325
97565
|
const move = deps.move ?? ((src, dest) => {
|
|
97326
97566
|
try {
|
|
97327
|
-
|
|
97567
|
+
renameSync24(src, dest);
|
|
97328
97568
|
} catch {
|
|
97329
97569
|
exec("mv", [src, dest]);
|
|
97330
97570
|
}
|
|
@@ -97370,7 +97610,7 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
97370
97610
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
97371
97611
|
}
|
|
97372
97612
|
function listTrashEntries(nowMs, deps = {}) {
|
|
97373
|
-
const exists = deps.existsSync ??
|
|
97613
|
+
const exists = deps.existsSync ?? existsSync96;
|
|
97374
97614
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
97375
97615
|
const root = trashRoot();
|
|
97376
97616
|
if (!exists(root))
|
|
@@ -97632,10 +97872,10 @@ init_drive();
|
|
|
97632
97872
|
init_scaffold_integration();
|
|
97633
97873
|
import {
|
|
97634
97874
|
chmodSync as chmodSync16,
|
|
97635
|
-
mkdirSync as
|
|
97875
|
+
mkdirSync as mkdirSync54,
|
|
97636
97876
|
readdirSync as readdirSync33,
|
|
97637
97877
|
rmSync as rmSync18,
|
|
97638
|
-
writeFileSync as
|
|
97878
|
+
writeFileSync as writeFileSync38
|
|
97639
97879
|
} from "node:fs";
|
|
97640
97880
|
import { join as join97 } from "node:path";
|
|
97641
97881
|
function encodeCredentialsFilename(email) {
|
|
@@ -97830,14 +98070,14 @@ function resolveCredentialsDir(env2) {
|
|
|
97830
98070
|
return join97(stateBase, "google-workspace-mcp", "credentials");
|
|
97831
98071
|
}
|
|
97832
98072
|
function writeSeedFile(dir, email, seed) {
|
|
97833
|
-
|
|
98073
|
+
mkdirSync54(dir, { recursive: true, mode: 448 });
|
|
97834
98074
|
chmodSync16(dir, 448);
|
|
97835
98075
|
for (const name of readdirSync33(dir)) {
|
|
97836
98076
|
rmSync18(join97(dir, name), { force: true, recursive: true });
|
|
97837
98077
|
}
|
|
97838
98078
|
const filename = encodeCredentialsFilename(email);
|
|
97839
98079
|
const filePath = join97(dir, filename);
|
|
97840
|
-
|
|
98080
|
+
writeFileSync38(filePath, JSON.stringify(seed), { mode: 384 });
|
|
97841
98081
|
chmodSync16(filePath, 384);
|
|
97842
98082
|
return filePath;
|
|
97843
98083
|
}
|
|
@@ -97995,8 +98235,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
97995
98235
|
// src/cli/m365-mcp-launcher.ts
|
|
97996
98236
|
init_scaffold_integration();
|
|
97997
98237
|
import { spawn as spawn6 } from "node:child_process";
|
|
97998
|
-
import { writeFileSync as
|
|
97999
|
-
import { dirname as
|
|
98238
|
+
import { writeFileSync as writeFileSync39, mkdirSync as mkdirSync55 } from "node:fs";
|
|
98239
|
+
import { dirname as dirname39, join as join98 } from "node:path";
|
|
98000
98240
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
98001
98241
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
98002
98242
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -98032,8 +98272,8 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
98032
98272
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
98033
98273
|
const path10 = heartbeatPath(agentName, account);
|
|
98034
98274
|
try {
|
|
98035
|
-
|
|
98036
|
-
|
|
98275
|
+
mkdirSync55(dirname39(path10), { recursive: true });
|
|
98276
|
+
writeFileSync39(path10, JSON.stringify(data, null, 2), { mode: 420 });
|
|
98037
98277
|
} catch {}
|
|
98038
98278
|
}
|
|
98039
98279
|
function heartbeatPath(agentName, account) {
|
|
@@ -98273,8 +98513,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
98273
98513
|
// src/cli/notion-mcp-launcher.ts
|
|
98274
98514
|
init_scaffold_integration();
|
|
98275
98515
|
import { spawn as spawn7 } from "node:child_process";
|
|
98276
|
-
import { existsSync as
|
|
98277
|
-
import { dirname as
|
|
98516
|
+
import { existsSync as existsSync97, mkdirSync as mkdirSync56, writeFileSync as writeFileSync40 } from "node:fs";
|
|
98517
|
+
import { dirname as dirname40 } from "node:path";
|
|
98278
98518
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
98279
98519
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
98280
98520
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -98284,10 +98524,10 @@ function buildNotionMcpArgs(opts) {
|
|
|
98284
98524
|
}
|
|
98285
98525
|
function defaultWriteHeartbeat(path10, contents) {
|
|
98286
98526
|
try {
|
|
98287
|
-
const dir =
|
|
98288
|
-
if (!
|
|
98289
|
-
|
|
98290
|
-
|
|
98527
|
+
const dir = dirname40(path10);
|
|
98528
|
+
if (!existsSync97(dir))
|
|
98529
|
+
mkdirSync56(dir, { recursive: true });
|
|
98530
|
+
writeFileSync40(path10, contents);
|
|
98291
98531
|
} catch {}
|
|
98292
98532
|
}
|
|
98293
98533
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -98396,7 +98636,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
98396
98636
|
}
|
|
98397
98637
|
|
|
98398
98638
|
// src/cli/hindsight-mcp-shim.ts
|
|
98399
|
-
import { mkdirSync as
|
|
98639
|
+
import { mkdirSync as mkdirSync57, readFileSync as readFileSync86, renameSync as renameSync25, writeFileSync as writeFileSync41 } from "node:fs";
|
|
98400
98640
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
98401
98641
|
import { join as join99 } from "node:path";
|
|
98402
98642
|
import { createInterface as createInterface6 } from "node:readline";
|
|
@@ -98848,18 +99088,18 @@ class HindsightShim {
|
|
|
98848
99088
|
}
|
|
98849
99089
|
writeCache(result) {
|
|
98850
99090
|
try {
|
|
98851
|
-
|
|
99091
|
+
mkdirSync57(this.opts.cacheDir, { recursive: true });
|
|
98852
99092
|
const tmp = join99(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
98853
|
-
|
|
99093
|
+
writeFileSync41(tmp, JSON.stringify(result, null, 2) + `
|
|
98854
99094
|
`);
|
|
98855
|
-
|
|
99095
|
+
renameSync25(tmp, this.cachePath);
|
|
98856
99096
|
} catch (err) {
|
|
98857
99097
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
98858
99098
|
}
|
|
98859
99099
|
}
|
|
98860
99100
|
readCache() {
|
|
98861
99101
|
try {
|
|
98862
|
-
const parsed = JSON.parse(
|
|
99102
|
+
const parsed = JSON.parse(readFileSync86(this.cachePath, "utf-8"));
|
|
98863
99103
|
if (Array.isArray(parsed.tools))
|
|
98864
99104
|
return parsed;
|
|
98865
99105
|
return null;
|
|
@@ -99092,7 +99332,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
99092
99332
|
|
|
99093
99333
|
// src/cli/deliver-file.ts
|
|
99094
99334
|
init_client2();
|
|
99095
|
-
import { readFileSync as
|
|
99335
|
+
import { readFileSync as readFileSync87, statSync as statSync52 } from "node:fs";
|
|
99096
99336
|
import { basename as basename13 } from "node:path";
|
|
99097
99337
|
|
|
99098
99338
|
// src/delivery/onedrive.ts
|
|
@@ -99432,7 +99672,7 @@ async function defaultResolveProvider() {
|
|
|
99432
99672
|
async function runDeliverFile(localPath, deps = {}) {
|
|
99433
99673
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
99434
99674
|
const sizeOf = deps.fileSize ?? ((p) => statSync52(p).size);
|
|
99435
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
99675
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync87(p)));
|
|
99436
99676
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
99437
99677
|
let size;
|
|
99438
99678
|
try {
|
|
@@ -99753,7 +99993,7 @@ function runRedactStdin() {
|
|
|
99753
99993
|
}
|
|
99754
99994
|
|
|
99755
99995
|
// src/cli/status-ask.ts
|
|
99756
|
-
import { readFileSync as
|
|
99996
|
+
import { readFileSync as readFileSync88, existsSync as existsSync98, readdirSync as readdirSync34 } from "node:fs";
|
|
99757
99997
|
import { join as join100 } from "node:path";
|
|
99758
99998
|
import { homedir as homedir53 } from "node:os";
|
|
99759
99999
|
|
|
@@ -100029,7 +100269,7 @@ function runReport(opts) {
|
|
|
100029
100269
|
for (const src of sources) {
|
|
100030
100270
|
let content;
|
|
100031
100271
|
try {
|
|
100032
|
-
content =
|
|
100272
|
+
content = readFileSync88(src.path, "utf-8");
|
|
100033
100273
|
} catch (err) {
|
|
100034
100274
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
100035
100275
|
`);
|
|
@@ -100076,7 +100316,7 @@ function runReport(opts) {
|
|
|
100076
100316
|
function resolveSources(explicitPath) {
|
|
100077
100317
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
100078
100318
|
const trimmed = explicitPath.trim();
|
|
100079
|
-
if (!
|
|
100319
|
+
if (!existsSync98(trimmed)) {
|
|
100080
100320
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
100081
100321
|
`);
|
|
100082
100322
|
process.exit(1);
|
|
@@ -100092,7 +100332,7 @@ function resolveSources(explicitPath) {
|
|
|
100092
100332
|
} catch {
|
|
100093
100333
|
agentsDir = join100(homedir53(), ".switchroom", "agents");
|
|
100094
100334
|
}
|
|
100095
|
-
if (!
|
|
100335
|
+
if (!existsSync98(agentsDir))
|
|
100096
100336
|
return [];
|
|
100097
100337
|
const sources = [];
|
|
100098
100338
|
let entries;
|
|
@@ -100103,7 +100343,7 @@ function resolveSources(explicitPath) {
|
|
|
100103
100343
|
}
|
|
100104
100344
|
for (const name of entries) {
|
|
100105
100345
|
const path10 = join100(agentsDir, name, "runtime-metrics.jsonl");
|
|
100106
|
-
if (
|
|
100346
|
+
if (existsSync98(path10)) {
|
|
100107
100347
|
sources.push({ path: path10, agent: name });
|
|
100108
100348
|
}
|
|
100109
100349
|
}
|
|
@@ -100133,13 +100373,13 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
100133
100373
|
init_paths();
|
|
100134
100374
|
import {
|
|
100135
100375
|
closeSync as closeSync18,
|
|
100136
|
-
existsSync as
|
|
100376
|
+
existsSync as existsSync99,
|
|
100137
100377
|
fsyncSync as fsyncSync8,
|
|
100138
|
-
mkdirSync as
|
|
100378
|
+
mkdirSync as mkdirSync58,
|
|
100139
100379
|
openSync as openSync18,
|
|
100140
100380
|
readdirSync as readdirSync35,
|
|
100141
|
-
readFileSync as
|
|
100142
|
-
renameSync as
|
|
100381
|
+
readFileSync as readFileSync89,
|
|
100382
|
+
renameSync as renameSync26,
|
|
100143
100383
|
statSync as statSync53,
|
|
100144
100384
|
unlinkSync as unlinkSync20,
|
|
100145
100385
|
writeSync as writeSync10
|
|
@@ -100163,15 +100403,15 @@ function overlayPathsFor(agent, opts = {}) {
|
|
|
100163
100403
|
};
|
|
100164
100404
|
}
|
|
100165
100405
|
function ensureDirs(paths) {
|
|
100166
|
-
|
|
100167
|
-
|
|
100406
|
+
mkdirSync58(paths.scheduleDir, { recursive: true });
|
|
100407
|
+
mkdirSync58(paths.scheduleStagingDir, { recursive: true });
|
|
100168
100408
|
}
|
|
100169
100409
|
function ensureSkillsDirs(paths) {
|
|
100170
|
-
|
|
100171
|
-
|
|
100410
|
+
mkdirSync58(paths.skillsDir, { recursive: true });
|
|
100411
|
+
mkdirSync58(paths.skillsStagingDir, { recursive: true });
|
|
100172
100412
|
}
|
|
100173
100413
|
function withAgentLock(paths, fn) {
|
|
100174
|
-
|
|
100414
|
+
mkdirSync58(paths.agentRoot, { recursive: true });
|
|
100175
100415
|
const start = Date.now();
|
|
100176
100416
|
const TIMEOUT_MS = 5000;
|
|
100177
100417
|
let fd = null;
|
|
@@ -100221,7 +100461,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
100221
100461
|
} finally {
|
|
100222
100462
|
closeSync18(fd);
|
|
100223
100463
|
}
|
|
100224
|
-
|
|
100464
|
+
renameSync26(stagingPath, finalPath);
|
|
100225
100465
|
return finalPath;
|
|
100226
100466
|
});
|
|
100227
100467
|
}
|
|
@@ -100238,7 +100478,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
100238
100478
|
} finally {
|
|
100239
100479
|
closeSync18(fd);
|
|
100240
100480
|
}
|
|
100241
|
-
|
|
100481
|
+
renameSync26(stagingPath, finalPath);
|
|
100242
100482
|
return finalPath;
|
|
100243
100483
|
});
|
|
100244
100484
|
}
|
|
@@ -100246,7 +100486,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
100246
100486
|
const paths = overlayPathsFor(agent, opts);
|
|
100247
100487
|
return withAgentLock(paths, () => {
|
|
100248
100488
|
const finalPath = join101(paths.skillsDir, `${slug}.yaml`);
|
|
100249
|
-
if (!
|
|
100489
|
+
if (!existsSync99(finalPath))
|
|
100250
100490
|
return false;
|
|
100251
100491
|
unlinkSync20(finalPath);
|
|
100252
100492
|
return true;
|
|
@@ -100254,7 +100494,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
100254
100494
|
}
|
|
100255
100495
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
100256
100496
|
const paths = overlayPathsFor(agent, opts);
|
|
100257
|
-
if (!
|
|
100497
|
+
if (!existsSync99(paths.skillsDir))
|
|
100258
100498
|
return [];
|
|
100259
100499
|
const out = [];
|
|
100260
100500
|
for (const name of readdirSync35(paths.skillsDir)) {
|
|
@@ -100262,7 +100502,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
100262
100502
|
continue;
|
|
100263
100503
|
const full = join101(paths.skillsDir, name);
|
|
100264
100504
|
try {
|
|
100265
|
-
const raw =
|
|
100505
|
+
const raw = readFileSync89(full, "utf-8");
|
|
100266
100506
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
100267
100507
|
out.push({ slug, path: full, raw });
|
|
100268
100508
|
} catch {}
|
|
@@ -100273,7 +100513,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
100273
100513
|
const paths = overlayPathsFor(agent, opts);
|
|
100274
100514
|
return withAgentLock(paths, () => {
|
|
100275
100515
|
const finalPath = join101(paths.scheduleDir, `${slug}.yaml`);
|
|
100276
|
-
if (!
|
|
100516
|
+
if (!existsSync99(finalPath))
|
|
100277
100517
|
return false;
|
|
100278
100518
|
unlinkSync20(finalPath);
|
|
100279
100519
|
return true;
|
|
@@ -100281,7 +100521,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
100281
100521
|
}
|
|
100282
100522
|
function listOverlayEntries(agent, opts = {}) {
|
|
100283
100523
|
const paths = overlayPathsFor(agent, opts);
|
|
100284
|
-
if (!
|
|
100524
|
+
if (!existsSync99(paths.scheduleDir))
|
|
100285
100525
|
return [];
|
|
100286
100526
|
const out = [];
|
|
100287
100527
|
for (const name of readdirSync35(paths.scheduleDir)) {
|
|
@@ -100289,7 +100529,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
100289
100529
|
continue;
|
|
100290
100530
|
const full = join101(paths.scheduleDir, name);
|
|
100291
100531
|
try {
|
|
100292
|
-
const raw =
|
|
100532
|
+
const raw = readFileSync89(full, "utf-8");
|
|
100293
100533
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
100294
100534
|
out.push({ slug, path: full, raw });
|
|
100295
100535
|
} catch {}
|
|
@@ -100509,15 +100749,15 @@ function reconcileAgentCronOnly(agent) {
|
|
|
100509
100749
|
// src/cli/agent-config-pending.ts
|
|
100510
100750
|
import {
|
|
100511
100751
|
closeSync as closeSync19,
|
|
100512
|
-
existsSync as
|
|
100752
|
+
existsSync as existsSync100,
|
|
100513
100753
|
fsyncSync as fsyncSync9,
|
|
100514
|
-
mkdirSync as
|
|
100754
|
+
mkdirSync as mkdirSync59,
|
|
100515
100755
|
openSync as openSync19,
|
|
100516
100756
|
readdirSync as readdirSync36,
|
|
100517
|
-
readFileSync as
|
|
100518
|
-
renameSync as
|
|
100757
|
+
readFileSync as readFileSync90,
|
|
100758
|
+
renameSync as renameSync27,
|
|
100519
100759
|
unlinkSync as unlinkSync21,
|
|
100520
|
-
writeFileSync as
|
|
100760
|
+
writeFileSync as writeFileSync42,
|
|
100521
100761
|
writeSync as writeSync11
|
|
100522
100762
|
} from "node:fs";
|
|
100523
100763
|
import { join as join102 } from "node:path";
|
|
@@ -100529,7 +100769,7 @@ function pendingDir(agent, opts = {}) {
|
|
|
100529
100769
|
}
|
|
100530
100770
|
function ensurePendingDir(agent, opts = {}) {
|
|
100531
100771
|
const dir = pendingDir(agent, opts);
|
|
100532
|
-
|
|
100772
|
+
mkdirSync59(dir, { recursive: true });
|
|
100533
100773
|
return dir;
|
|
100534
100774
|
}
|
|
100535
100775
|
function newStageId() {
|
|
@@ -100558,15 +100798,15 @@ function stagePendingScheduleEntry(opts) {
|
|
|
100558
100798
|
} finally {
|
|
100559
100799
|
closeSync19(fd);
|
|
100560
100800
|
}
|
|
100561
|
-
|
|
100801
|
+
renameSync27(yamlTmp, yamlPath);
|
|
100562
100802
|
}
|
|
100563
|
-
|
|
100803
|
+
writeFileSync42(metaPath, JSON.stringify(meta, null, 2) + `
|
|
100564
100804
|
`, { mode: 384 });
|
|
100565
100805
|
return { stageId, yamlPath, metaPath };
|
|
100566
100806
|
}
|
|
100567
100807
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
100568
100808
|
const dir = pendingDir(agent, opts);
|
|
100569
|
-
if (!
|
|
100809
|
+
if (!existsSync100(dir))
|
|
100570
100810
|
return [];
|
|
100571
100811
|
const out = [];
|
|
100572
100812
|
for (const name of readdirSync36(dir).sort()) {
|
|
@@ -100575,10 +100815,10 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
100575
100815
|
const stageId = name.slice(0, -".meta.json".length);
|
|
100576
100816
|
const metaPath = join102(dir, name);
|
|
100577
100817
|
const yamlPath = join102(dir, `${stageId}.yaml`);
|
|
100578
|
-
if (!
|
|
100818
|
+
if (!existsSync100(yamlPath))
|
|
100579
100819
|
continue;
|
|
100580
100820
|
try {
|
|
100581
|
-
const meta = JSON.parse(
|
|
100821
|
+
const meta = JSON.parse(readFileSync90(metaPath, "utf-8"));
|
|
100582
100822
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
100583
100823
|
continue;
|
|
100584
100824
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -100594,10 +100834,10 @@ function commitPendingScheduleEntry(opts) {
|
|
|
100594
100834
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
100595
100835
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
100596
100836
|
const finalPath = join102(paths.scheduleDir, `${slug}.yaml`);
|
|
100597
|
-
if (
|
|
100837
|
+
if (existsSync100(finalPath)) {
|
|
100598
100838
|
return { committed: false, reason: "slug_collision" };
|
|
100599
100839
|
}
|
|
100600
|
-
|
|
100840
|
+
renameSync27(match.yamlPath, finalPath);
|
|
100601
100841
|
unlinkSync21(match.metaPath);
|
|
100602
100842
|
return { committed: true, path: finalPath, slug };
|
|
100603
100843
|
}
|
|
@@ -100616,7 +100856,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
100616
100856
|
}
|
|
100617
100857
|
|
|
100618
100858
|
// src/cli/agent-config-write.ts
|
|
100619
|
-
import { existsSync as
|
|
100859
|
+
import { existsSync as existsSync101, readFileSync as readFileSync91 } from "node:fs";
|
|
100620
100860
|
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
100621
100861
|
|
|
100622
100862
|
// src/scheduler/schedule-report.ts
|
|
@@ -101006,8 +101246,8 @@ function scheduleRemove(opts) {
|
|
|
101006
101246
|
}
|
|
101007
101247
|
let priorContent = null;
|
|
101008
101248
|
try {
|
|
101009
|
-
if (
|
|
101010
|
-
priorContent =
|
|
101249
|
+
if (existsSync101(match.path))
|
|
101250
|
+
priorContent = readFileSync91(match.path, "utf-8");
|
|
101011
101251
|
} catch {}
|
|
101012
101252
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
101013
101253
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -101210,7 +101450,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101210
101450
|
}
|
|
101211
101451
|
let blob;
|
|
101212
101452
|
if (opts.jsonl) {
|
|
101213
|
-
blob =
|
|
101453
|
+
blob = existsSync101(opts.jsonl) ? readFileSync91(opts.jsonl, "utf-8") : "";
|
|
101214
101454
|
} else {
|
|
101215
101455
|
try {
|
|
101216
101456
|
blob = execFileSync30("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -101242,7 +101482,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
101242
101482
|
|
|
101243
101483
|
// src/cli/agent-config-skill-write.ts
|
|
101244
101484
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
101245
|
-
import { existsSync as
|
|
101485
|
+
import { existsSync as existsSync102 } from "node:fs";
|
|
101246
101486
|
init_reconcile_default_skills();
|
|
101247
101487
|
init_agent_config();
|
|
101248
101488
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
@@ -101322,7 +101562,7 @@ function skillInstall(opts) {
|
|
|
101322
101562
|
}
|
|
101323
101563
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
101324
101564
|
const skillPath = join103(poolDir, skillName);
|
|
101325
|
-
if (!
|
|
101565
|
+
if (!existsSync102(skillPath)) {
|
|
101326
101566
|
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.`);
|
|
101327
101567
|
}
|
|
101328
101568
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -101486,22 +101726,22 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
101486
101726
|
// src/cli/skill.ts
|
|
101487
101727
|
import {
|
|
101488
101728
|
closeSync as closeSync20,
|
|
101489
|
-
existsSync as
|
|
101729
|
+
existsSync as existsSync103,
|
|
101490
101730
|
lstatSync as lstatSync12,
|
|
101491
|
-
mkdirSync as
|
|
101731
|
+
mkdirSync as mkdirSync60,
|
|
101492
101732
|
mkdtempSync as mkdtempSync5,
|
|
101493
101733
|
openSync as openSync20,
|
|
101494
|
-
readFileSync as
|
|
101734
|
+
readFileSync as readFileSync92,
|
|
101495
101735
|
readdirSync as readdirSync37,
|
|
101496
101736
|
realpathSync as realpathSync7,
|
|
101497
|
-
renameSync as
|
|
101737
|
+
renameSync as renameSync28,
|
|
101498
101738
|
rmSync as rmSync19,
|
|
101499
101739
|
statSync as statSync54,
|
|
101500
|
-
writeFileSync as
|
|
101740
|
+
writeFileSync as writeFileSync43
|
|
101501
101741
|
} from "node:fs";
|
|
101502
101742
|
import { tmpdir as tmpdir6, homedir as homedir54 } from "node:os";
|
|
101503
|
-
import { dirname as
|
|
101504
|
-
import { spawnSync as
|
|
101743
|
+
import { dirname as dirname41, join as join104, relative as relative4, resolve as resolve58 } from "node:path";
|
|
101744
|
+
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
101505
101745
|
|
|
101506
101746
|
// src/cli/skill-common.ts
|
|
101507
101747
|
var import_yaml24 = __toESM(require_dist(), 1);
|
|
@@ -101784,7 +102024,7 @@ function loadFromDir(dir) {
|
|
|
101784
102024
|
continue;
|
|
101785
102025
|
}
|
|
101786
102026
|
if (ent.isFile()) {
|
|
101787
|
-
const buf =
|
|
102027
|
+
const buf = readFileSync92(full);
|
|
101788
102028
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
101789
102029
|
}
|
|
101790
102030
|
}
|
|
@@ -101795,7 +102035,7 @@ function loadFromDir(dir) {
|
|
|
101795
102035
|
function loadFromTarball(tarPath) {
|
|
101796
102036
|
const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
|
|
101797
102037
|
const listFlags = isGz ? ["-tzf"] : ["-tf"];
|
|
101798
|
-
const list2 =
|
|
102038
|
+
const list2 = spawnSync21("tar", [...listFlags, tarPath], {
|
|
101799
102039
|
encoding: "utf-8",
|
|
101800
102040
|
stdio: ["ignore", "pipe", "pipe"]
|
|
101801
102041
|
});
|
|
@@ -101812,7 +102052,7 @@ function loadFromTarball(tarPath) {
|
|
|
101812
102052
|
const staging = mkdtempSync5(join104(tmpdir6(), "skill-apply-extract-"));
|
|
101813
102053
|
try {
|
|
101814
102054
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
101815
|
-
const r =
|
|
102055
|
+
const r = spawnSync21("tar", [
|
|
101816
102056
|
...flags,
|
|
101817
102057
|
tarPath,
|
|
101818
102058
|
"-C",
|
|
@@ -101833,7 +102073,7 @@ function loadFromTarball(tarPath) {
|
|
|
101833
102073
|
}
|
|
101834
102074
|
}
|
|
101835
102075
|
function loadSingleFile(filePath) {
|
|
101836
|
-
const content =
|
|
102076
|
+
const content = readFileSync92(filePath, "utf-8");
|
|
101837
102077
|
return { "SKILL.md": content };
|
|
101838
102078
|
}
|
|
101839
102079
|
function loadFromStdin() {
|
|
@@ -101887,7 +102127,7 @@ function validatePayload(name, files) {
|
|
|
101887
102127
|
if (errors2.length === 0) {
|
|
101888
102128
|
for (const [path10, content] of Object.entries(files)) {
|
|
101889
102129
|
if (SH_SCRIPT_RE2.test(path10)) {
|
|
101890
|
-
const r =
|
|
102130
|
+
const r = spawnSync21("bash", ["-n"], {
|
|
101891
102131
|
input: content,
|
|
101892
102132
|
encoding: "utf-8"
|
|
101893
102133
|
});
|
|
@@ -101898,8 +102138,8 @@ function validatePayload(name, files) {
|
|
|
101898
102138
|
const tmp = mkdtempSync5(join104(tmpdir6(), "skill-apply-py-"));
|
|
101899
102139
|
const tmpPy = join104(tmp, "check.py");
|
|
101900
102140
|
try {
|
|
101901
|
-
|
|
101902
|
-
const r =
|
|
102141
|
+
writeFileSync43(tmpPy, content);
|
|
102142
|
+
const r = spawnSync21("python3", ["-m", "py_compile", tmpPy], {
|
|
101903
102143
|
encoding: "utf-8"
|
|
101904
102144
|
});
|
|
101905
102145
|
if (r.status !== 0) {
|
|
@@ -101916,7 +102156,7 @@ function validatePayload(name, files) {
|
|
|
101916
102156
|
function diffSummary(currentDir, files) {
|
|
101917
102157
|
const lines = [];
|
|
101918
102158
|
const currentFiles = {};
|
|
101919
|
-
if (
|
|
102159
|
+
if (existsSync103(currentDir)) {
|
|
101920
102160
|
const walk2 = (sub) => {
|
|
101921
102161
|
for (const ent of readdirSync37(sub, { withFileTypes: true })) {
|
|
101922
102162
|
const full = join104(sub, ent.name);
|
|
@@ -101924,7 +102164,7 @@ function diffSummary(currentDir, files) {
|
|
|
101924
102164
|
if (ent.isDirectory()) {
|
|
101925
102165
|
walk2(full);
|
|
101926
102166
|
} else if (ent.isFile()) {
|
|
101927
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
102167
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync92(full, "utf-8");
|
|
101928
102168
|
}
|
|
101929
102169
|
}
|
|
101930
102170
|
};
|
|
@@ -101952,8 +102192,8 @@ function diffSummary(currentDir, files) {
|
|
|
101952
102192
|
`);
|
|
101953
102193
|
}
|
|
101954
102194
|
function writePayload(poolDir, name, files) {
|
|
101955
|
-
if (!
|
|
101956
|
-
|
|
102195
|
+
if (!existsSync103(poolDir)) {
|
|
102196
|
+
mkdirSync60(poolDir, { recursive: true, mode: 493 });
|
|
101957
102197
|
}
|
|
101958
102198
|
const target = join104(poolDir, name);
|
|
101959
102199
|
let targetIsSymlink = false;
|
|
@@ -101971,10 +102211,10 @@ function writePayload(poolDir, name, files) {
|
|
|
101971
102211
|
try {
|
|
101972
102212
|
for (const [path10, content] of Object.entries(files)) {
|
|
101973
102213
|
const full = join104(staging, path10);
|
|
101974
|
-
|
|
102214
|
+
mkdirSync60(dirname41(full), { recursive: true, mode: 493 });
|
|
101975
102215
|
const fd = openSync20(full, "wx");
|
|
101976
102216
|
try {
|
|
101977
|
-
|
|
102217
|
+
writeFileSync43(fd, content);
|
|
101978
102218
|
} finally {
|
|
101979
102219
|
closeSync20(fd);
|
|
101980
102220
|
}
|
|
@@ -101990,9 +102230,9 @@ function writePayload(poolDir, name, files) {
|
|
|
101990
102230
|
} catch {}
|
|
101991
102231
|
if (targetExists) {
|
|
101992
102232
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
101993
|
-
|
|
102233
|
+
renameSync28(target, oldRename);
|
|
101994
102234
|
}
|
|
101995
|
-
|
|
102235
|
+
renameSync28(staging, target);
|
|
101996
102236
|
if (oldRename) {
|
|
101997
102237
|
rmSync19(oldRename, { recursive: true, force: true });
|
|
101998
102238
|
oldRename = null;
|
|
@@ -102001,12 +102241,12 @@ function writePayload(poolDir, name, files) {
|
|
|
102001
102241
|
try {
|
|
102002
102242
|
rmSync19(staging, { recursive: true, force: true });
|
|
102003
102243
|
} catch {}
|
|
102004
|
-
if (oldRename &&
|
|
102244
|
+
if (oldRename && existsSync103(oldRename)) {
|
|
102005
102245
|
try {
|
|
102006
|
-
if (
|
|
102246
|
+
if (existsSync103(target)) {
|
|
102007
102247
|
rmSync19(target, { recursive: true, force: true });
|
|
102008
102248
|
}
|
|
102009
|
-
|
|
102249
|
+
renameSync28(oldRename, target);
|
|
102010
102250
|
} catch {}
|
|
102011
102251
|
}
|
|
102012
102252
|
throw err2;
|
|
@@ -102024,7 +102264,7 @@ function registerSkillCommand(program3) {
|
|
|
102024
102264
|
files = loadFromStdin();
|
|
102025
102265
|
} else {
|
|
102026
102266
|
const fromPath = resolve58(opts.from);
|
|
102027
|
-
if (!
|
|
102267
|
+
if (!existsSync103(fromPath)) {
|
|
102028
102268
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
102029
102269
|
}
|
|
102030
102270
|
const st = statSync54(fromPath);
|
|
@@ -102062,7 +102302,7 @@ function registerSkillCommand(program3) {
|
|
|
102062
102302
|
\u2713 Wrote ${name} to ${currentDir}`));
|
|
102063
102303
|
const self = selfInvokeArgv(["apply", "--non-interactive"]);
|
|
102064
102304
|
console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
|
|
102065
|
-
const r =
|
|
102305
|
+
const r = spawnSync21(self.command, self.argv, { stdio: "inherit" });
|
|
102066
102306
|
if (r.status !== 0) {
|
|
102067
102307
|
console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
|
|
102068
102308
|
}
|
|
@@ -102080,22 +102320,22 @@ function sumBytes(files) {
|
|
|
102080
102320
|
init_esm();
|
|
102081
102321
|
import {
|
|
102082
102322
|
closeSync as closeSync21,
|
|
102083
|
-
existsSync as
|
|
102323
|
+
existsSync as existsSync104,
|
|
102084
102324
|
lstatSync as lstatSync13,
|
|
102085
|
-
mkdirSync as
|
|
102325
|
+
mkdirSync as mkdirSync61,
|
|
102086
102326
|
mkdtempSync as mkdtempSync6,
|
|
102087
102327
|
openSync as openSync21,
|
|
102088
|
-
readFileSync as
|
|
102328
|
+
readFileSync as readFileSync93,
|
|
102089
102329
|
readdirSync as readdirSync38,
|
|
102090
|
-
renameSync as
|
|
102330
|
+
renameSync as renameSync29,
|
|
102091
102331
|
rmSync as rmSync20,
|
|
102092
102332
|
statSync as statSync55,
|
|
102093
102333
|
utimesSync,
|
|
102094
|
-
writeFileSync as
|
|
102334
|
+
writeFileSync as writeFileSync44
|
|
102095
102335
|
} from "node:fs";
|
|
102096
|
-
import { dirname as
|
|
102336
|
+
import { dirname as dirname42, join as join105, relative as relative5, resolve as resolve59 } from "node:path";
|
|
102097
102337
|
import { homedir as homedir55, tmpdir as tmpdir7 } from "node:os";
|
|
102098
|
-
import { spawnSync as
|
|
102338
|
+
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
102099
102339
|
init_helpers();
|
|
102100
102340
|
init_agent_config();
|
|
102101
102341
|
init_source();
|
|
@@ -102106,14 +102346,14 @@ var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
|
102106
102346
|
function resolveConfigSkillsDir(agent) {
|
|
102107
102347
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
102108
102348
|
const candidate = override ? resolve59(override) : join105(homedir55(), ".switchroom-config");
|
|
102109
|
-
if (!
|
|
102349
|
+
if (!existsSync104(candidate))
|
|
102110
102350
|
return null;
|
|
102111
102351
|
return join105(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
102112
102352
|
}
|
|
102113
102353
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
102114
102354
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
102115
102355
|
try {
|
|
102116
|
-
if (!
|
|
102356
|
+
if (!existsSync104(configSkillsRoot))
|
|
102117
102357
|
return;
|
|
102118
102358
|
const now = Date.now();
|
|
102119
102359
|
for (const ent of readdirSync38(configSkillsRoot)) {
|
|
@@ -102149,17 +102389,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
102149
102389
|
}
|
|
102150
102390
|
if (liveSkillDir === null) {
|
|
102151
102391
|
sweepMirrorPriors(configSkillsRoot);
|
|
102152
|
-
if (
|
|
102392
|
+
if (existsSync104(dest)) {
|
|
102153
102393
|
const trash = join105(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
102154
|
-
|
|
102394
|
+
renameSync29(dest, trash);
|
|
102155
102395
|
}
|
|
102156
102396
|
return;
|
|
102157
102397
|
}
|
|
102158
|
-
|
|
102398
|
+
mkdirSync61(configSkillsRoot, { recursive: true, mode: 493 });
|
|
102159
102399
|
sweepMirrorPriors(configSkillsRoot);
|
|
102160
102400
|
const staging = mkdtempSync6(join105(configSkillsRoot, `.${name}-staging-`));
|
|
102161
102401
|
const walk2 = (src, dst) => {
|
|
102162
|
-
|
|
102402
|
+
mkdirSync61(dst, { recursive: true, mode: 493 });
|
|
102163
102403
|
for (const ent of readdirSync38(src, { withFileTypes: true })) {
|
|
102164
102404
|
const s = join105(src, ent.name);
|
|
102165
102405
|
const d = join105(dst, ent.name);
|
|
@@ -102168,16 +102408,16 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
102168
102408
|
if (ent.isDirectory())
|
|
102169
102409
|
walk2(s, d);
|
|
102170
102410
|
else if (ent.isFile()) {
|
|
102171
|
-
|
|
102411
|
+
writeFileSync44(d, readFileSync93(s));
|
|
102172
102412
|
}
|
|
102173
102413
|
}
|
|
102174
102414
|
};
|
|
102175
102415
|
walk2(liveSkillDir, staging);
|
|
102176
|
-
if (
|
|
102416
|
+
if (existsSync104(dest)) {
|
|
102177
102417
|
const prior = join105(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
102178
|
-
|
|
102418
|
+
renameSync29(dest, prior);
|
|
102179
102419
|
}
|
|
102180
|
-
|
|
102420
|
+
renameSync29(staging, dest);
|
|
102181
102421
|
} catch (err2) {
|
|
102182
102422
|
process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
|
|
102183
102423
|
`));
|
|
@@ -102214,7 +102454,7 @@ function trashDir(agentsRoot, agent) {
|
|
|
102214
102454
|
}
|
|
102215
102455
|
function countPersonalSkills(agentsRoot, agent) {
|
|
102216
102456
|
const skillsDir = join105(agentsRoot, agent, ".claude", "skills");
|
|
102217
|
-
if (!
|
|
102457
|
+
if (!existsSync104(skillsDir))
|
|
102218
102458
|
return 0;
|
|
102219
102459
|
let n = 0;
|
|
102220
102460
|
for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
|
|
@@ -102261,7 +102501,7 @@ function loadFromDir2(dir) {
|
|
|
102261
102501
|
}
|
|
102262
102502
|
if (ent.isFile()) {
|
|
102263
102503
|
const rel = relative5(abs, full).replace(/\\/g, "/");
|
|
102264
|
-
files[rel] =
|
|
102504
|
+
files[rel] = readFileSync93(full, "utf-8");
|
|
102265
102505
|
}
|
|
102266
102506
|
}
|
|
102267
102507
|
};
|
|
@@ -102299,7 +102539,7 @@ function behavioralValidate(files) {
|
|
|
102299
102539
|
const errors2 = [];
|
|
102300
102540
|
for (const [path10, content] of Object.entries(files)) {
|
|
102301
102541
|
if (SH_SCRIPT_RE.test(path10)) {
|
|
102302
|
-
const r =
|
|
102542
|
+
const r = spawnSync22("bash", ["-n"], { input: content, encoding: "utf-8" });
|
|
102303
102543
|
if (r.status !== 0) {
|
|
102304
102544
|
errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
102305
102545
|
}
|
|
@@ -102307,8 +102547,8 @@ function behavioralValidate(files) {
|
|
|
102307
102547
|
const tmp = mkdtempSync6(join105(tmpdir7(), "skill-personal-py-"));
|
|
102308
102548
|
const tmpPy = join105(tmp, "check.py");
|
|
102309
102549
|
try {
|
|
102310
|
-
|
|
102311
|
-
const r =
|
|
102550
|
+
writeFileSync44(tmpPy, content);
|
|
102551
|
+
const r = spawnSync22("python3", ["-m", "py_compile", tmpPy], {
|
|
102312
102552
|
encoding: "utf-8"
|
|
102313
102553
|
});
|
|
102314
102554
|
if (r.status !== 0) {
|
|
@@ -102323,7 +102563,7 @@ function behavioralValidate(files) {
|
|
|
102323
102563
|
}
|
|
102324
102564
|
function sweepTrash(agentsRoot, agent) {
|
|
102325
102565
|
const trash = trashDir(agentsRoot, agent);
|
|
102326
|
-
if (!
|
|
102566
|
+
if (!existsSync104(trash))
|
|
102327
102567
|
return;
|
|
102328
102568
|
const now = Date.now();
|
|
102329
102569
|
for (const ent of readdirSync38(trash, { withFileTypes: true })) {
|
|
@@ -102349,16 +102589,16 @@ function writePersonalSkill(targetDir, files) {
|
|
|
102349
102589
|
if (targetIsSymlink) {
|
|
102350
102590
|
fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
102351
102591
|
}
|
|
102352
|
-
|
|
102353
|
-
const staging = mkdtempSync6(join105(
|
|
102592
|
+
mkdirSync61(dirname42(targetDir), { recursive: true, mode: 493 });
|
|
102593
|
+
const staging = mkdtempSync6(join105(dirname42(targetDir), `.skill-personal-stage-`));
|
|
102354
102594
|
let oldRename = null;
|
|
102355
102595
|
try {
|
|
102356
102596
|
for (const [path10, content] of Object.entries(files)) {
|
|
102357
102597
|
const full = join105(staging, path10);
|
|
102358
|
-
|
|
102598
|
+
mkdirSync61(dirname42(full), { recursive: true, mode: 493 });
|
|
102359
102599
|
const fd = openSync21(full, "wx");
|
|
102360
102600
|
try {
|
|
102361
|
-
|
|
102601
|
+
writeFileSync44(fd, content);
|
|
102362
102602
|
} finally {
|
|
102363
102603
|
closeSync21(fd);
|
|
102364
102604
|
}
|
|
@@ -102374,9 +102614,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
102374
102614
|
} catch {}
|
|
102375
102615
|
if (targetExists) {
|
|
102376
102616
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
102377
|
-
|
|
102617
|
+
renameSync29(targetDir, oldRename);
|
|
102378
102618
|
}
|
|
102379
|
-
|
|
102619
|
+
renameSync29(staging, targetDir);
|
|
102380
102620
|
if (oldRename) {
|
|
102381
102621
|
rmSync20(oldRename, { recursive: true, force: true });
|
|
102382
102622
|
oldRename = null;
|
|
@@ -102385,12 +102625,12 @@ function writePersonalSkill(targetDir, files) {
|
|
|
102385
102625
|
try {
|
|
102386
102626
|
rmSync20(staging, { recursive: true, force: true });
|
|
102387
102627
|
} catch {}
|
|
102388
|
-
if (oldRename &&
|
|
102628
|
+
if (oldRename && existsSync104(oldRename)) {
|
|
102389
102629
|
try {
|
|
102390
|
-
if (
|
|
102630
|
+
if (existsSync104(targetDir)) {
|
|
102391
102631
|
rmSync20(targetDir, { recursive: true, force: true });
|
|
102392
102632
|
}
|
|
102393
|
-
|
|
102633
|
+
renameSync29(oldRename, targetDir);
|
|
102394
102634
|
} catch {}
|
|
102395
102635
|
}
|
|
102396
102636
|
throw err2;
|
|
@@ -102453,7 +102693,7 @@ function loadFiles(opts) {
|
|
|
102453
102693
|
return loadFromStdin2();
|
|
102454
102694
|
}
|
|
102455
102695
|
const p = resolve59(opts.from);
|
|
102456
|
-
if (!
|
|
102696
|
+
if (!existsSync104(p)) {
|
|
102457
102697
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
102458
102698
|
}
|
|
102459
102699
|
const st = statSync55(p);
|
|
@@ -102461,7 +102701,7 @@ function loadFiles(opts) {
|
|
|
102461
102701
|
return loadFromDir2(p);
|
|
102462
102702
|
}
|
|
102463
102703
|
if (p.endsWith(".md")) {
|
|
102464
|
-
return { "SKILL.md":
|
|
102704
|
+
return { "SKILL.md": readFileSync93(p, "utf-8") };
|
|
102465
102705
|
}
|
|
102466
102706
|
fail5(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
102467
102707
|
}
|
|
@@ -102515,7 +102755,7 @@ function resolveCloneSource(source, opts) {
|
|
|
102515
102755
|
const slug = m[2];
|
|
102516
102756
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
102517
102757
|
const dir = join105(root, slug);
|
|
102518
|
-
if (!
|
|
102758
|
+
if (!existsSync104(dir)) {
|
|
102519
102759
|
fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
102520
102760
|
}
|
|
102521
102761
|
const st = lstatSync13(dir);
|
|
@@ -102550,7 +102790,7 @@ function readSourceFiles(dir) {
|
|
|
102550
102790
|
fail5(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
102551
102791
|
}
|
|
102552
102792
|
} catch {}
|
|
102553
|
-
files[rel] =
|
|
102793
|
+
files[rel] = readFileSync93(full, "utf-8");
|
|
102554
102794
|
}
|
|
102555
102795
|
}
|
|
102556
102796
|
};
|
|
@@ -102639,10 +102879,10 @@ function removePersonalAction(name, opts) {
|
|
|
102639
102879
|
throw err2;
|
|
102640
102880
|
}
|
|
102641
102881
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
102642
|
-
|
|
102882
|
+
mkdirSync61(trashRoot2, { recursive: true, mode: 493 });
|
|
102643
102883
|
const ts = Date.now();
|
|
102644
102884
|
const trashTarget = join105(trashRoot2, `${name}-${ts}`);
|
|
102645
|
-
|
|
102885
|
+
renameSync29(target, trashTarget);
|
|
102646
102886
|
const now = new Date(ts);
|
|
102647
102887
|
utimesSync(trashTarget, now, now);
|
|
102648
102888
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -102662,7 +102902,7 @@ function listPersonalAction(opts) {
|
|
|
102662
102902
|
sweepTrash(agentsRoot, agent);
|
|
102663
102903
|
const skillsDir = join105(agentsRoot, agent, ".claude", "skills");
|
|
102664
102904
|
const personal = [];
|
|
102665
|
-
if (
|
|
102905
|
+
if (existsSync104(skillsDir)) {
|
|
102666
102906
|
for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
|
|
102667
102907
|
if (!ent.isDirectory())
|
|
102668
102908
|
continue;
|
|
@@ -102720,7 +102960,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
102720
102960
|
import { createConnection as createConnection4 } from "node:net";
|
|
102721
102961
|
import { homedir as homedir56 } from "node:os";
|
|
102722
102962
|
import { join as join106 } from "node:path";
|
|
102723
|
-
import { readFileSync as
|
|
102963
|
+
import { readFileSync as readFileSync94 } from "node:fs";
|
|
102724
102964
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
102725
102965
|
function gatewaySocketPath() {
|
|
102726
102966
|
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join106(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join106(homedir56(), ".claude", "channels", "telegram", "gateway.sock"));
|
|
@@ -102758,7 +102998,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
102758
102998
|
fail6("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
|
|
102759
102999
|
let draft;
|
|
102760
103000
|
try {
|
|
102761
|
-
draft = JSON.parse(
|
|
103001
|
+
draft = JSON.parse(readFileSync94(opts.draft, "utf-8"));
|
|
102762
103002
|
} catch (e) {
|
|
102763
103003
|
fail6(`failed to read/parse --draft: ${e.message}`);
|
|
102764
103004
|
}
|
|
@@ -102795,7 +103035,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
102795
103035
|
init_esm();
|
|
102796
103036
|
init_helpers();
|
|
102797
103037
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
102798
|
-
import { existsSync as
|
|
103038
|
+
import { existsSync as existsSync105, readdirSync as readdirSync39, readFileSync as readFileSync95, statSync as statSync56 } from "node:fs";
|
|
102799
103039
|
import { homedir as homedir57 } from "node:os";
|
|
102800
103040
|
import { join as join107, resolve as resolve60 } from "node:path";
|
|
102801
103041
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -102812,11 +103052,11 @@ function defaultBundledRoot2() {
|
|
|
102812
103052
|
}
|
|
102813
103053
|
function readSkillFrontmatter(skillDir) {
|
|
102814
103054
|
const mdPath = join107(skillDir, "SKILL.md");
|
|
102815
|
-
if (!
|
|
103055
|
+
if (!existsSync105(mdPath))
|
|
102816
103056
|
return null;
|
|
102817
103057
|
let content;
|
|
102818
103058
|
try {
|
|
102819
|
-
content =
|
|
103059
|
+
content = readFileSync95(mdPath, "utf-8");
|
|
102820
103060
|
} catch {
|
|
102821
103061
|
return null;
|
|
102822
103062
|
}
|
|
@@ -102856,7 +103096,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
102856
103096
|
if (!AGENT_NAME_RE3.test(agent))
|
|
102857
103097
|
return [];
|
|
102858
103098
|
const skillsDir = join107(agentsRoot, agent, ".claude/skills");
|
|
102859
|
-
if (!
|
|
103099
|
+
if (!existsSync105(skillsDir))
|
|
102860
103100
|
return [];
|
|
102861
103101
|
const out = [];
|
|
102862
103102
|
let entries;
|
|
@@ -102894,7 +103134,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
102894
103134
|
return out;
|
|
102895
103135
|
}
|
|
102896
103136
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
102897
|
-
if (!
|
|
103137
|
+
if (!existsSync105(sharedRoot))
|
|
102898
103138
|
return [];
|
|
102899
103139
|
const out = [];
|
|
102900
103140
|
let entries;
|
|
@@ -102932,7 +103172,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
102932
103172
|
return out;
|
|
102933
103173
|
}
|
|
102934
103174
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
102935
|
-
if (!
|
|
103175
|
+
if (!existsSync105(bundledRoot))
|
|
102936
103176
|
return [];
|
|
102937
103177
|
const out = [];
|
|
102938
103178
|
let entries;
|
|
@@ -103090,10 +103330,10 @@ init_source();
|
|
|
103090
103330
|
init_helpers();
|
|
103091
103331
|
init_operator_uid();
|
|
103092
103332
|
import {
|
|
103093
|
-
existsSync as
|
|
103094
|
-
mkdirSync as
|
|
103333
|
+
existsSync as existsSync107,
|
|
103334
|
+
mkdirSync as mkdirSync62,
|
|
103095
103335
|
readdirSync as readdirSync40,
|
|
103096
|
-
writeFileSync as
|
|
103336
|
+
writeFileSync as writeFileSync45,
|
|
103097
103337
|
statSync as statSync57,
|
|
103098
103338
|
lstatSync as lstatSync14,
|
|
103099
103339
|
realpathSync as realpathSync8,
|
|
@@ -103101,13 +103341,13 @@ import {
|
|
|
103101
103341
|
} from "node:fs";
|
|
103102
103342
|
import { homedir as homedir58 } from "node:os";
|
|
103103
103343
|
import { join as join108 } from "node:path";
|
|
103104
|
-
import { spawnSync as
|
|
103344
|
+
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
103105
103345
|
|
|
103106
103346
|
// src/cli/singleton-stale-cleanup.ts
|
|
103107
|
-
import { spawnSync as
|
|
103347
|
+
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
103108
103348
|
function makeDockerRunner() {
|
|
103109
103349
|
return (args) => {
|
|
103110
|
-
const r =
|
|
103350
|
+
const r = spawnSync24("docker", args, { encoding: "utf8" });
|
|
103111
103351
|
return {
|
|
103112
103352
|
ok: r.status === 0,
|
|
103113
103353
|
stdout: r.stdout ?? "",
|
|
@@ -103466,7 +103706,7 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
103466
103706
|
console.warn(`switchroom hostd install: ~/.switchroom/skills is a symlink whose target ` + `does not resolve (dangling) \u2014 skipping the skills bind mount. Bundled ` + `skills will be unavailable to rollout/update until the symlink is fixed.`);
|
|
103467
103707
|
return;
|
|
103468
103708
|
}
|
|
103469
|
-
if (!
|
|
103709
|
+
if (!existsSync107(target)) {
|
|
103470
103710
|
console.warn(`switchroom hostd install: ~/.switchroom/skills resolves to "${target}", ` + `which does not exist \u2014 skipping the skills bind mount. Bundled skills ` + `will be unavailable to rollout/update until the symlink target exists.`);
|
|
103471
103711
|
return;
|
|
103472
103712
|
}
|
|
@@ -103480,7 +103720,7 @@ function hostdComposePath() {
|
|
|
103480
103720
|
}
|
|
103481
103721
|
function backupExistingCompose() {
|
|
103482
103722
|
const p = hostdComposePath();
|
|
103483
|
-
if (!
|
|
103723
|
+
if (!existsSync107(p))
|
|
103484
103724
|
return null;
|
|
103485
103725
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
103486
103726
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -103488,7 +103728,7 @@ function backupExistingCompose() {
|
|
|
103488
103728
|
return bak;
|
|
103489
103729
|
}
|
|
103490
103730
|
function runDocker(args) {
|
|
103491
|
-
const r =
|
|
103731
|
+
const r = spawnSync25("docker", args, { encoding: "utf8" });
|
|
103492
103732
|
return {
|
|
103493
103733
|
ok: r.status === 0,
|
|
103494
103734
|
stdout: r.stdout ?? "",
|
|
@@ -103513,7 +103753,7 @@ async function doInstall(opts, program3) {
|
|
|
103513
103753
|
}
|
|
103514
103754
|
const dir = hostdDir();
|
|
103515
103755
|
const composePath = hostdComposePath();
|
|
103516
|
-
|
|
103756
|
+
mkdirSync62(dir, { recursive: true });
|
|
103517
103757
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
103518
103758
|
const guard = checkDowngrade({
|
|
103519
103759
|
container: "switchroom-hostd",
|
|
@@ -103546,7 +103786,7 @@ async function doInstall(opts, program3) {
|
|
|
103546
103786
|
const bak = backupExistingCompose();
|
|
103547
103787
|
if (bak)
|
|
103548
103788
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
103549
|
-
|
|
103789
|
+
writeFileSync45(composePath, yaml, "utf8");
|
|
103550
103790
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
103551
103791
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
103552
103792
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -103578,7 +103818,7 @@ function doStatus() {
|
|
|
103578
103818
|
const composeYml = hostdComposePath();
|
|
103579
103819
|
console.log(source_default.bold("switchroom-hostd"));
|
|
103580
103820
|
console.log("");
|
|
103581
|
-
if (!
|
|
103821
|
+
if (!existsSync107(composeYml)) {
|
|
103582
103822
|
console.log(source_default.yellow(" compose: not installed"));
|
|
103583
103823
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
103584
103824
|
return;
|
|
@@ -103599,14 +103839,14 @@ function doStatus() {
|
|
|
103599
103839
|
} else {
|
|
103600
103840
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
103601
103841
|
}
|
|
103602
|
-
if (
|
|
103842
|
+
if (existsSync107(dir)) {
|
|
103603
103843
|
const entries = [];
|
|
103604
103844
|
try {
|
|
103605
103845
|
for (const name of readdirSync40(dir)) {
|
|
103606
103846
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
103607
103847
|
continue;
|
|
103608
103848
|
const sockPath = join108(dir, name, "sock");
|
|
103609
|
-
if (
|
|
103849
|
+
if (existsSync107(sockPath)) {
|
|
103610
103850
|
const st = statSync57(sockPath);
|
|
103611
103851
|
if ((st.mode & 61440) === 49152) {
|
|
103612
103852
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -103625,7 +103865,7 @@ function doStatus() {
|
|
|
103625
103865
|
}
|
|
103626
103866
|
function doUninstall() {
|
|
103627
103867
|
const composeYml = hostdComposePath();
|
|
103628
|
-
if (!
|
|
103868
|
+
if (!existsSync107(composeYml)) {
|
|
103629
103869
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
103630
103870
|
return;
|
|
103631
103871
|
}
|
|
@@ -103649,7 +103889,7 @@ function registerHostdCommand(program3) {
|
|
|
103649
103889
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
103650
103890
|
hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
|
|
103651
103891
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
103652
|
-
if (!
|
|
103892
|
+
if (!existsSync107(logPath)) {
|
|
103653
103893
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
103654
103894
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
103655
103895
|
return;
|
|
@@ -103698,10 +103938,10 @@ init_source();
|
|
|
103698
103938
|
init_helpers();
|
|
103699
103939
|
init_operator_uid();
|
|
103700
103940
|
init_compose();
|
|
103701
|
-
import { chownSync as chownSync10, existsSync as
|
|
103941
|
+
import { chownSync as chownSync10, existsSync as existsSync108, mkdirSync as mkdirSync63, writeFileSync as writeFileSync46, copyFileSync as copyFileSync15 } from "node:fs";
|
|
103702
103942
|
import { homedir as homedir59 } from "node:os";
|
|
103703
103943
|
import { join as join109 } from "node:path";
|
|
103704
|
-
import { spawnSync as
|
|
103944
|
+
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
103705
103945
|
function resolveWebImageTag(explicitTag, release) {
|
|
103706
103946
|
if (explicitTag)
|
|
103707
103947
|
return explicitTag;
|
|
@@ -103809,7 +104049,7 @@ function webdComposePath() {
|
|
|
103809
104049
|
}
|
|
103810
104050
|
function backupExistingCompose2() {
|
|
103811
104051
|
const p = webdComposePath();
|
|
103812
|
-
if (!
|
|
104052
|
+
if (!existsSync108(p))
|
|
103813
104053
|
return null;
|
|
103814
104054
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
103815
104055
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -103817,7 +104057,7 @@ function backupExistingCompose2() {
|
|
|
103817
104057
|
return bak;
|
|
103818
104058
|
}
|
|
103819
104059
|
function runDocker2(args) {
|
|
103820
|
-
const r =
|
|
104060
|
+
const r = spawnSync26("docker", args, { encoding: "utf8" });
|
|
103821
104061
|
return {
|
|
103822
104062
|
ok: r.status === 0,
|
|
103823
104063
|
stdout: r.stdout ?? "",
|
|
@@ -103840,7 +104080,7 @@ async function doInstall2(opts, program3) {
|
|
|
103840
104080
|
}
|
|
103841
104081
|
const dir = webdDir();
|
|
103842
104082
|
const composePath = webdComposePath();
|
|
103843
|
-
|
|
104083
|
+
mkdirSync63(dir, { recursive: true });
|
|
103844
104084
|
const cfg = getConfig(program3);
|
|
103845
104085
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
103846
104086
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -103875,7 +104115,7 @@ async function doInstall2(opts, program3) {
|
|
|
103875
104115
|
const bak = backupExistingCompose2();
|
|
103876
104116
|
if (bak)
|
|
103877
104117
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
103878
|
-
|
|
104118
|
+
writeFileSync46(composePath, yaml, "utf8");
|
|
103879
104119
|
try {
|
|
103880
104120
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
103881
104121
|
chownSync10(dir, operatorUid, operatorUid);
|
|
@@ -103917,7 +104157,7 @@ function doStatus2() {
|
|
|
103917
104157
|
const composeYml = webdComposePath();
|
|
103918
104158
|
console.log(source_default.bold("switchroom-web"));
|
|
103919
104159
|
console.log("");
|
|
103920
|
-
if (!
|
|
104160
|
+
if (!existsSync108(composeYml)) {
|
|
103921
104161
|
console.log(source_default.yellow(" compose: not installed"));
|
|
103922
104162
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
103923
104163
|
return;
|
|
@@ -103941,7 +104181,7 @@ function doStatus2() {
|
|
|
103941
104181
|
}
|
|
103942
104182
|
function doUninstall2() {
|
|
103943
104183
|
const composeYml = webdComposePath();
|
|
103944
|
-
if (!
|
|
104184
|
+
if (!existsSync108(composeYml)) {
|
|
103945
104185
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
103946
104186
|
return;
|
|
103947
104187
|
}
|
|
@@ -104216,13 +104456,13 @@ init_helpers();
|
|
|
104216
104456
|
init_scan();
|
|
104217
104457
|
|
|
104218
104458
|
// src/fleet-health/gh-sync.ts
|
|
104219
|
-
import { spawnSync as
|
|
104459
|
+
import { spawnSync as spawnSync27 } from "node:child_process";
|
|
104220
104460
|
var LABEL = "fleet-health";
|
|
104221
104461
|
function defaultGhDeps(log) {
|
|
104222
104462
|
return {
|
|
104223
104463
|
log,
|
|
104224
104464
|
run: (args) => {
|
|
104225
|
-
const r =
|
|
104465
|
+
const r = spawnSync27("gh", args, { encoding: "utf-8" });
|
|
104226
104466
|
return {
|
|
104227
104467
|
ok: r.status === 0,
|
|
104228
104468
|
stdout: (r.stdout ?? "").trim(),
|
|
@@ -104427,7 +104667,7 @@ function printDeepDiveBrief(targets) {
|
|
|
104427
104667
|
|
|
104428
104668
|
// src/cli/hindsight-watch.ts
|
|
104429
104669
|
init_source();
|
|
104430
|
-
import { existsSync as
|
|
104670
|
+
import { existsSync as existsSync113, readdirSync as readdirSync45 } from "node:fs";
|
|
104431
104671
|
import { homedir as homedir63 } from "node:os";
|
|
104432
104672
|
import { join as join112, resolve as resolve63 } from "node:path";
|
|
104433
104673
|
|
|
@@ -104497,8 +104737,8 @@ function postOperatorNoticeViaGateways(candidates, text, log, connectTimeoutMs =
|
|
|
104497
104737
|
init_install_cron();
|
|
104498
104738
|
|
|
104499
104739
|
// src/hindsight-watch/probe.ts
|
|
104500
|
-
import { spawnSync as
|
|
104501
|
-
import { readFileSync as
|
|
104740
|
+
import { spawnSync as spawnSync28 } from "node:child_process";
|
|
104741
|
+
import { readFileSync as readFileSync100, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
|
|
104502
104742
|
import { homedir as homedir62 } from "node:os";
|
|
104503
104743
|
import { resolve as resolve62 } from "node:path";
|
|
104504
104744
|
|
|
@@ -104626,7 +104866,7 @@ function readLlmSignals(series) {
|
|
|
104626
104866
|
}
|
|
104627
104867
|
|
|
104628
104868
|
// src/hindsight-watch/recall-log.ts
|
|
104629
|
-
import { closeSync as closeSync22, existsSync as
|
|
104869
|
+
import { closeSync as closeSync22, existsSync as existsSync112, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as statSync58 } from "node:fs";
|
|
104630
104870
|
import { join as join111 } from "node:path";
|
|
104631
104871
|
var RECALL_WINDOW_ROWS = 200;
|
|
104632
104872
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
@@ -104635,7 +104875,7 @@ function recallLogPath2(agentDir) {
|
|
|
104635
104875
|
return join111(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
104636
104876
|
}
|
|
104637
104877
|
function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
104638
|
-
if (!
|
|
104878
|
+
if (!existsSync112(path10))
|
|
104639
104879
|
return [];
|
|
104640
104880
|
let text;
|
|
104641
104881
|
let truncatedHead = false;
|
|
@@ -104847,7 +105087,7 @@ async function probeMetrics(url = DEFAULT_METRICS_URL, fetchImpl = fetch) {
|
|
|
104847
105087
|
}
|
|
104848
105088
|
}
|
|
104849
105089
|
var defaultRunner4 = (cmd, args) => {
|
|
104850
|
-
const r =
|
|
105090
|
+
const r = spawnSync28(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
|
|
104851
105091
|
if (r.error)
|
|
104852
105092
|
return { status: null, stdout: "", stderr: r.error.message };
|
|
104853
105093
|
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
@@ -104875,7 +105115,7 @@ function readDropCount(hindsightDir) {
|
|
|
104875
105115
|
try {
|
|
104876
105116
|
if (statSync59(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
104877
105117
|
return 0;
|
|
104878
|
-
parsed = JSON.parse(
|
|
105118
|
+
parsed = JSON.parse(readFileSync100(path10, "utf8"));
|
|
104879
105119
|
} catch {
|
|
104880
105120
|
return 0;
|
|
104881
105121
|
}
|
|
@@ -105923,7 +106163,7 @@ function collectRelayCandidates(agentsDir) {
|
|
|
105923
106163
|
const candidates = [];
|
|
105924
106164
|
for (const name of readdirSync45(agentsDir).sort()) {
|
|
105925
106165
|
const sock = resolve63(agentsDir, name, "telegram", "gateway.sock");
|
|
105926
|
-
if (
|
|
106166
|
+
if (existsSync113(sock))
|
|
105927
106167
|
candidates.push({ agent: name, sock });
|
|
105928
106168
|
}
|
|
105929
106169
|
return orderRelayCandidates(candidates);
|
|
@@ -105945,13 +106185,13 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
105945
106185
|
|
|
105946
106186
|
// src/cli/openrouter-watch.ts
|
|
105947
106187
|
init_source();
|
|
105948
|
-
import { existsSync as
|
|
106188
|
+
import { existsSync as existsSync115, readdirSync as readdirSync46 } from "node:fs";
|
|
105949
106189
|
import { homedir as homedir65 } from "node:os";
|
|
105950
106190
|
import { join as join113, resolve as resolve65 } from "node:path";
|
|
105951
106191
|
|
|
105952
106192
|
// src/openrouter/install-cron.ts
|
|
105953
|
-
import { existsSync as
|
|
105954
|
-
import { dirname as
|
|
106193
|
+
import { existsSync as existsSync114, mkdirSync as mkdirSync65, readFileSync as readFileSync101, renameSync as renameSync30, writeFileSync as writeFileSync48 } from "node:fs";
|
|
106194
|
+
import { dirname as dirname44 } from "node:path";
|
|
105955
106195
|
var CRON_PATH2 = "/etc/cron.d/openrouter-watch";
|
|
105956
106196
|
var CRON_SCHEDULE2 = "17 * * * *";
|
|
105957
106197
|
var CRON_LOG_PATH2 = "/var/log/openrouter-watch.log";
|
|
@@ -105968,30 +106208,30 @@ function renderCron2(opts) {
|
|
|
105968
106208
|
function installCron2(opts) {
|
|
105969
106209
|
const path10 = opts.path ?? CRON_PATH2;
|
|
105970
106210
|
const content = renderCron2(opts);
|
|
105971
|
-
if (
|
|
106211
|
+
if (existsSync114(path10)) {
|
|
105972
106212
|
try {
|
|
105973
|
-
if (
|
|
106213
|
+
if (readFileSync101(path10, "utf8") === content) {
|
|
105974
106214
|
return { status: "unchanged", path: path10, content };
|
|
105975
106215
|
}
|
|
105976
106216
|
} catch {}
|
|
105977
106217
|
}
|
|
105978
|
-
|
|
106218
|
+
mkdirSync65(dirname44(path10), { recursive: true });
|
|
105979
106219
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
105980
|
-
|
|
105981
|
-
|
|
106220
|
+
writeFileSync48(tmp, content, { mode: 420 });
|
|
106221
|
+
renameSync30(tmp, path10);
|
|
105982
106222
|
return { status: "installed", path: path10, content };
|
|
105983
106223
|
}
|
|
105984
106224
|
// src/openrouter/state.ts
|
|
105985
|
-
import { mkdirSync as
|
|
106225
|
+
import { mkdirSync as mkdirSync66, readFileSync as readFileSync102, renameSync as renameSync31, writeFileSync as writeFileSync49 } from "node:fs";
|
|
105986
106226
|
import { homedir as homedir64 } from "node:os";
|
|
105987
|
-
import { dirname as
|
|
106227
|
+
import { dirname as dirname45, resolve as resolve64 } from "node:path";
|
|
105988
106228
|
function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir64()) {
|
|
105989
106229
|
return resolve64(home2, ".switchroom", "openrouter-watch", "state.json");
|
|
105990
106230
|
}
|
|
105991
106231
|
function loadState2(path10) {
|
|
105992
106232
|
let parsed;
|
|
105993
106233
|
try {
|
|
105994
|
-
parsed = JSON.parse(
|
|
106234
|
+
parsed = JSON.parse(readFileSync102(path10, "utf8"));
|
|
105995
106235
|
} catch {
|
|
105996
106236
|
return {};
|
|
105997
106237
|
}
|
|
@@ -106011,12 +106251,12 @@ function loadState2(path10) {
|
|
|
106011
106251
|
return out;
|
|
106012
106252
|
}
|
|
106013
106253
|
function saveState2(path10, state) {
|
|
106014
|
-
|
|
106254
|
+
mkdirSync66(dirname45(path10), { recursive: true, mode: 448 });
|
|
106015
106255
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
106016
106256
|
const payload = { v: 1, ...state };
|
|
106017
|
-
|
|
106257
|
+
writeFileSync49(tmp, JSON.stringify(payload, null, 2) + `
|
|
106018
106258
|
`, { mode: 384 });
|
|
106019
|
-
|
|
106259
|
+
renameSync31(tmp, path10);
|
|
106020
106260
|
}
|
|
106021
106261
|
|
|
106022
106262
|
// src/openrouter/watch.ts
|
|
@@ -106201,7 +106441,7 @@ async function notifyOperator2(agentsDir, text, log) {
|
|
|
106201
106441
|
try {
|
|
106202
106442
|
for (const name of readdirSync46(agentsDir).sort()) {
|
|
106203
106443
|
const sock = resolve65(agentsDir, name, "telegram", "gateway.sock");
|
|
106204
|
-
if (
|
|
106444
|
+
if (existsSync115(sock))
|
|
106205
106445
|
candidates.push({ agent: name, sock });
|
|
106206
106446
|
}
|
|
106207
106447
|
} catch (e) {
|