switchroom 0.16.7 → 0.16.9
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/cli/switchroom.js +1039 -608
- package/dist/host-control/main.js +5 -5
- package/package.json +2 -2
- package/profiles/_base/start.sh.hbs +35 -0
- package/telegram-plugin/dist/gateway/gateway.js +91 -8
- package/telegram-plugin/gateway/gateway.ts +92 -3
- package/telegram-plugin/gateway/model-command.ts +73 -0
- package/telegram-plugin/permission-title.ts +5 -0
- package/telegram-plugin/tests/model-command.test.ts +124 -1
- package/telegram-plugin/uat/scenarios/jtbd-model-litellm-sr-dm.test.ts +24 -10
package/dist/cli/switchroom.js
CHANGED
|
@@ -29955,17 +29955,17 @@ var init_thinking_effort_risk = __esm(() => {
|
|
|
29955
29955
|
|
|
29956
29956
|
// src/manifest.ts
|
|
29957
29957
|
import {
|
|
29958
|
-
existsSync as
|
|
29959
|
-
readFileSync as
|
|
29958
|
+
existsSync as existsSync54,
|
|
29959
|
+
readFileSync as readFileSync50,
|
|
29960
29960
|
readdirSync as readdirSync19
|
|
29961
29961
|
} from "node:fs";
|
|
29962
|
-
import { dirname as dirname14, join as
|
|
29962
|
+
import { dirname as dirname14, join as join50 } from "node:path";
|
|
29963
29963
|
import { execSync as execSync2 } from "node:child_process";
|
|
29964
29964
|
function locateManifestPath() {
|
|
29965
29965
|
let dir = import.meta.dirname;
|
|
29966
29966
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
29967
|
-
const candidate =
|
|
29968
|
-
if (
|
|
29967
|
+
const candidate = join50(dir, "dependencies.json");
|
|
29968
|
+
if (existsSync54(candidate))
|
|
29969
29969
|
return candidate;
|
|
29970
29970
|
dir = dirname14(dir);
|
|
29971
29971
|
}
|
|
@@ -29978,7 +29978,7 @@ function loadManifest(manifestPath) {
|
|
|
29978
29978
|
}
|
|
29979
29979
|
let raw;
|
|
29980
29980
|
try {
|
|
29981
|
-
raw =
|
|
29981
|
+
raw = readFileSync50(path4, "utf-8");
|
|
29982
29982
|
} catch (err) {
|
|
29983
29983
|
throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
|
|
29984
29984
|
}
|
|
@@ -30034,16 +30034,16 @@ function probeClaudeVersion() {
|
|
|
30034
30034
|
}
|
|
30035
30035
|
function probePlaywrightMcpVersion() {
|
|
30036
30036
|
const home2 = process.env.HOME ?? "";
|
|
30037
|
-
const npxCache =
|
|
30038
|
-
if (!
|
|
30037
|
+
const npxCache = join50(home2, ".npm/_npx");
|
|
30038
|
+
if (!existsSync54(npxCache))
|
|
30039
30039
|
return null;
|
|
30040
30040
|
try {
|
|
30041
30041
|
const entries = readdirSync19(npxCache);
|
|
30042
30042
|
for (const entry of entries) {
|
|
30043
|
-
const pkgPath =
|
|
30044
|
-
if (
|
|
30043
|
+
const pkgPath = join50(npxCache, entry, "node_modules/@playwright/mcp/package.json");
|
|
30044
|
+
if (existsSync54(pkgPath)) {
|
|
30045
30045
|
try {
|
|
30046
|
-
const pkg = JSON.parse(
|
|
30046
|
+
const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
|
|
30047
30047
|
if (pkg.version)
|
|
30048
30048
|
return pkg.version;
|
|
30049
30049
|
} catch {}
|
|
@@ -30278,7 +30278,7 @@ var init_doctor_memory = __esm(() => {
|
|
|
30278
30278
|
});
|
|
30279
30279
|
|
|
30280
30280
|
// src/cli/doctor-docker.ts
|
|
30281
|
-
import { readFileSync as
|
|
30281
|
+
import { readFileSync as readFileSync51 } from "node:fs";
|
|
30282
30282
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
30283
30283
|
function imageTagOf(ref) {
|
|
30284
30284
|
if (!ref)
|
|
@@ -30294,7 +30294,7 @@ function isDockerMode(opts) {
|
|
|
30294
30294
|
return true;
|
|
30295
30295
|
if (opts?.composePath) {
|
|
30296
30296
|
try {
|
|
30297
|
-
|
|
30297
|
+
readFileSync51(opts.composePath, "utf8");
|
|
30298
30298
|
return true;
|
|
30299
30299
|
} catch {}
|
|
30300
30300
|
}
|
|
@@ -30576,11 +30576,11 @@ var init_doctor_docker = __esm(() => {
|
|
|
30576
30576
|
});
|
|
30577
30577
|
|
|
30578
30578
|
// src/cli/doctor-auth-broker.ts
|
|
30579
|
-
import { existsSync as
|
|
30580
|
-
import { createHash as
|
|
30579
|
+
import { existsSync as existsSync55, readFileSync as readFileSync52 } from "node:fs";
|
|
30580
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
30581
30581
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
30582
30582
|
import { homedir as homedir27 } from "node:os";
|
|
30583
|
-
import { join as
|
|
30583
|
+
import { join as join51 } from "node:path";
|
|
30584
30584
|
function defaultDockerInspect(container, format) {
|
|
30585
30585
|
try {
|
|
30586
30586
|
const r = spawnSync8("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
|
|
@@ -30603,7 +30603,7 @@ function resolveStateDir(deps) {
|
|
|
30603
30603
|
return deps.stateDir ?? resolveStatePath("state/auth-broker");
|
|
30604
30604
|
}
|
|
30605
30605
|
function sha256Hex(content) {
|
|
30606
|
-
return
|
|
30606
|
+
return createHash11("sha256").update(content).digest("hex");
|
|
30607
30607
|
}
|
|
30608
30608
|
function checkAuthBrokerServiceHealth(deps = {}) {
|
|
30609
30609
|
const inspect = deps.dockerInspect ?? defaultDockerInspect;
|
|
@@ -30680,8 +30680,8 @@ function checkAuthBrokerPerAgentSockets(config, deps = {}) {
|
|
|
30680
30680
|
}
|
|
30681
30681
|
function checkAuthBrokerDrift(deps = {}) {
|
|
30682
30682
|
const stateDir = resolveStateDir(deps);
|
|
30683
|
-
const indexPath =
|
|
30684
|
-
if (!
|
|
30683
|
+
const indexPath = join51(stateDir, "sha-index.json");
|
|
30684
|
+
if (!existsSync55(indexPath)) {
|
|
30685
30685
|
return {
|
|
30686
30686
|
name: "auth-broker: drift",
|
|
30687
30687
|
status: "ok",
|
|
@@ -30690,7 +30690,7 @@ function checkAuthBrokerDrift(deps = {}) {
|
|
|
30690
30690
|
}
|
|
30691
30691
|
let index;
|
|
30692
30692
|
try {
|
|
30693
|
-
index = JSON.parse(
|
|
30693
|
+
index = JSON.parse(readFileSync52(indexPath, "utf-8"));
|
|
30694
30694
|
} catch (err) {
|
|
30695
30695
|
return {
|
|
30696
30696
|
name: "auth-broker: drift",
|
|
@@ -30704,13 +30704,13 @@ function checkAuthBrokerDrift(deps = {}) {
|
|
|
30704
30704
|
const missingOnDisk = [];
|
|
30705
30705
|
for (const [label, expected] of Object.entries(index)) {
|
|
30706
30706
|
const credsPath = accountCredentialsPath(label, home2);
|
|
30707
|
-
if (!
|
|
30707
|
+
if (!existsSync55(credsPath)) {
|
|
30708
30708
|
missingOnDisk.push(label);
|
|
30709
30709
|
continue;
|
|
30710
30710
|
}
|
|
30711
30711
|
let got;
|
|
30712
30712
|
try {
|
|
30713
|
-
got = sha256Hex(
|
|
30713
|
+
got = sha256Hex(readFileSync52(credsPath, "utf-8"));
|
|
30714
30714
|
} catch (err) {
|
|
30715
30715
|
divergent.push(`${label} (read failed: ${err.message})`);
|
|
30716
30716
|
continue;
|
|
@@ -30741,8 +30741,8 @@ function checkAuthBrokerDrift(deps = {}) {
|
|
|
30741
30741
|
}
|
|
30742
30742
|
function checkAuthBrokerThresholdViolations(deps = {}) {
|
|
30743
30743
|
const stateDir = resolveStateDir(deps);
|
|
30744
|
-
const path4 =
|
|
30745
|
-
if (!
|
|
30744
|
+
const path4 = join51(stateDir, "threshold-violations.json");
|
|
30745
|
+
if (!existsSync55(path4)) {
|
|
30746
30746
|
return {
|
|
30747
30747
|
name: "auth-broker: threshold violations",
|
|
30748
30748
|
status: "ok",
|
|
@@ -30751,7 +30751,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
|
|
|
30751
30751
|
}
|
|
30752
30752
|
let violations;
|
|
30753
30753
|
try {
|
|
30754
|
-
violations = JSON.parse(
|
|
30754
|
+
violations = JSON.parse(readFileSync52(path4, "utf-8"));
|
|
30755
30755
|
} catch (err) {
|
|
30756
30756
|
return {
|
|
30757
30757
|
name: "auth-broker: threshold violations",
|
|
@@ -30787,7 +30787,7 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
|
|
|
30787
30787
|
}
|
|
30788
30788
|
const home2 = deps.home ?? homedir27();
|
|
30789
30789
|
const dir = accountDir(active, home2);
|
|
30790
|
-
if (!
|
|
30790
|
+
if (!existsSync55(dir)) {
|
|
30791
30791
|
return {
|
|
30792
30792
|
name: "auth-broker: fleet active account",
|
|
30793
30793
|
status: "fail",
|
|
@@ -30796,7 +30796,7 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
|
|
|
30796
30796
|
};
|
|
30797
30797
|
}
|
|
30798
30798
|
const creds = accountCredentialsPath(active, home2);
|
|
30799
|
-
if (!
|
|
30799
|
+
if (!existsSync55(creds)) {
|
|
30800
30800
|
return {
|
|
30801
30801
|
name: "auth-broker: fleet active account",
|
|
30802
30802
|
status: "fail",
|
|
@@ -30933,17 +30933,17 @@ var init_doctor_hostd = () => {};
|
|
|
30933
30933
|
import {
|
|
30934
30934
|
accessSync,
|
|
30935
30935
|
constants as fsConstants4,
|
|
30936
|
-
existsSync as
|
|
30936
|
+
existsSync as existsSync56,
|
|
30937
30937
|
realpathSync as realpathSync5,
|
|
30938
30938
|
statSync as statSync23
|
|
30939
30939
|
} from "node:fs";
|
|
30940
30940
|
import { userInfo, homedir as homedir28 } from "node:os";
|
|
30941
|
-
import { join as
|
|
30941
|
+
import { join as join52 } from "node:path";
|
|
30942
30942
|
function resolveVaultPath2(config) {
|
|
30943
30943
|
return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
|
|
30944
30944
|
}
|
|
30945
30945
|
function defaultStatVault(path4) {
|
|
30946
|
-
if (!
|
|
30946
|
+
if (!existsSync56(path4)) {
|
|
30947
30947
|
return { exists: false, readable: false, uid: -1, mode: 0, realPath: path4 };
|
|
30948
30948
|
}
|
|
30949
30949
|
let real = path4;
|
|
@@ -31076,7 +31076,7 @@ async function runSecretAccessChecks(config, deps = {}) {
|
|
|
31076
31076
|
};
|
|
31077
31077
|
const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
31078
31078
|
if (!passphrase) {
|
|
31079
|
-
const sock = deps.brokerOperatorSocket ??
|
|
31079
|
+
const sock = deps.brokerOperatorSocket ?? join52(homedir28(), ".switchroom", "broker-operator", "sock");
|
|
31080
31080
|
const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
|
|
31081
31081
|
for (const name of Object.keys(config.agents ?? {})) {
|
|
31082
31082
|
const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
|
|
@@ -31161,7 +31161,7 @@ import {
|
|
|
31161
31161
|
existsSync as realExistsSync,
|
|
31162
31162
|
readFileSync as realReadFileSync
|
|
31163
31163
|
} from "node:fs";
|
|
31164
|
-
import { join as
|
|
31164
|
+
import { join as join53, resolve as resolve32 } from "node:path";
|
|
31165
31165
|
import { homedir as homedir29 } from "node:os";
|
|
31166
31166
|
function resolveDeps(config, deps) {
|
|
31167
31167
|
let agentsDir = deps.agentsDir;
|
|
@@ -31276,7 +31276,7 @@ function checkScaffoldWiring(config, driveAgents, d) {
|
|
|
31276
31276
|
];
|
|
31277
31277
|
}
|
|
31278
31278
|
for (const name of driveAgents) {
|
|
31279
|
-
const agentDir =
|
|
31279
|
+
const agentDir = resolve32(d.agentsDir, name);
|
|
31280
31280
|
if (!d.existsSync(agentDir)) {
|
|
31281
31281
|
results.push({
|
|
31282
31282
|
name: `drive: ${name} scaffold`,
|
|
@@ -31285,8 +31285,8 @@ function checkScaffoldWiring(config, driveAgents, d) {
|
|
|
31285
31285
|
});
|
|
31286
31286
|
continue;
|
|
31287
31287
|
}
|
|
31288
|
-
const mcpPath =
|
|
31289
|
-
const claudeJsonPath =
|
|
31288
|
+
const mcpPath = join53(agentDir, ".mcp.json");
|
|
31289
|
+
const claudeJsonPath = join53(agentDir, ".claude", ".claude.json");
|
|
31290
31290
|
const mcpRead = readJson(d, mcpPath);
|
|
31291
31291
|
const trustRead = readJson(d, claudeJsonPath);
|
|
31292
31292
|
if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
|
|
@@ -31314,7 +31314,7 @@ function checkScaffoldWiring(config, driveAgents, d) {
|
|
|
31314
31314
|
let trustOk = false;
|
|
31315
31315
|
let trustDetail = "no .claude/.claude.json";
|
|
31316
31316
|
if (trustRead.kind === "ok") {
|
|
31317
|
-
const proj = trustRead.data?.projects?.[
|
|
31317
|
+
const proj = trustRead.data?.projects?.[resolve32(agentDir)];
|
|
31318
31318
|
const enabled = proj?.enabledMcpjsonServers;
|
|
31319
31319
|
if (Array.isArray(enabled) && enabled.includes("gdrive")) {
|
|
31320
31320
|
trustOk = true;
|
|
@@ -31399,7 +31399,7 @@ async function runDriveBrokerReachabilityChecks(config, deps = {}) {
|
|
|
31399
31399
|
}
|
|
31400
31400
|
];
|
|
31401
31401
|
}
|
|
31402
|
-
const sock = deps.brokerOperatorSocket ??
|
|
31402
|
+
const sock = deps.brokerOperatorSocket ?? join53(homedir29(), ".switchroom", "broker-operator", "sock");
|
|
31403
31403
|
const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
|
|
31404
31404
|
const results = [];
|
|
31405
31405
|
for (const agent of driveAgents) {
|
|
@@ -31453,7 +31453,7 @@ import {
|
|
|
31453
31453
|
readSync as realReadSync,
|
|
31454
31454
|
closeSync as realCloseSync
|
|
31455
31455
|
} from "node:fs";
|
|
31456
|
-
import { join as
|
|
31456
|
+
import { join as join54 } from "node:path";
|
|
31457
31457
|
import { homedir as homedir30 } from "node:os";
|
|
31458
31458
|
function defaultReadHead(p, n) {
|
|
31459
31459
|
let fd;
|
|
@@ -31511,7 +31511,7 @@ function runWebkiteChecks(config, deps = {}) {
|
|
|
31511
31511
|
return [];
|
|
31512
31512
|
const d = resolveDeps2(config, deps);
|
|
31513
31513
|
const results = [];
|
|
31514
|
-
const binPath =
|
|
31514
|
+
const binPath = join54(d.homeDir, ".switchroom", "bin", "webkite");
|
|
31515
31515
|
if (!d.existsSync(binPath)) {
|
|
31516
31516
|
results.push({
|
|
31517
31517
|
name: "webkite: binary",
|
|
@@ -31541,12 +31541,12 @@ function runWebkiteChecks(config, deps = {}) {
|
|
|
31541
31541
|
});
|
|
31542
31542
|
}
|
|
31543
31543
|
}
|
|
31544
|
-
const cloakDir =
|
|
31544
|
+
const cloakDir = join54(d.homeDir, ".cloakbrowser");
|
|
31545
31545
|
let chromeFound = false;
|
|
31546
31546
|
if (d.existsSync(cloakDir)) {
|
|
31547
31547
|
try {
|
|
31548
31548
|
for (const entry of d.readdirSync(cloakDir)) {
|
|
31549
|
-
if (entry.startsWith("chromium-") && d.existsSync(
|
|
31549
|
+
if (entry.startsWith("chromium-") && d.existsSync(join54(cloakDir, entry, "chrome"))) {
|
|
31550
31550
|
chromeFound = true;
|
|
31551
31551
|
break;
|
|
31552
31552
|
}
|
|
@@ -31572,9 +31572,9 @@ function runWebkiteChecks(config, deps = {}) {
|
|
|
31572
31572
|
return results;
|
|
31573
31573
|
}
|
|
31574
31574
|
for (const agent of enabledAgents) {
|
|
31575
|
-
const agentDir =
|
|
31576
|
-
const settingsPath =
|
|
31577
|
-
const mcpPath =
|
|
31575
|
+
const agentDir = join54(d.agentsDir, agent);
|
|
31576
|
+
const settingsPath = join54(agentDir, ".claude", "settings.json");
|
|
31577
|
+
const mcpPath = join54(agentDir, ".mcp.json");
|
|
31578
31578
|
if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
|
|
31579
31579
|
continue;
|
|
31580
31580
|
}
|
|
@@ -31639,7 +31639,7 @@ var init_doctor_webkite = __esm(() => {
|
|
|
31639
31639
|
|
|
31640
31640
|
// src/cli/doctor-cron-session.ts
|
|
31641
31641
|
import { statSync as realStatSync } from "node:fs";
|
|
31642
|
-
import { resolve as
|
|
31642
|
+
import { resolve as resolve33 } from "node:path";
|
|
31643
31643
|
function agentRunsCronSession(config, agent) {
|
|
31644
31644
|
const raw = config.agents[agent];
|
|
31645
31645
|
if (!raw)
|
|
@@ -31655,7 +31655,7 @@ function runCronSessionChecks(config, deps = defaultDeps2) {
|
|
|
31655
31655
|
if (!agentRunsCronSession(config, agent))
|
|
31656
31656
|
continue;
|
|
31657
31657
|
const name = `cron-session: ${agent}`;
|
|
31658
|
-
const alivePath =
|
|
31658
|
+
const alivePath = resolve33(agentsDir, agent, "telegram", ".bridge-alive-cron");
|
|
31659
31659
|
let mtimeMs;
|
|
31660
31660
|
try {
|
|
31661
31661
|
mtimeMs = deps.statMtimeMs(alivePath);
|
|
@@ -31704,7 +31704,7 @@ var init_doctor_cron_session = __esm(() => {
|
|
|
31704
31704
|
});
|
|
31705
31705
|
|
|
31706
31706
|
// src/cli/doctor-scaffold-wiring.ts
|
|
31707
|
-
import { join as
|
|
31707
|
+
import { join as join55, resolve as resolve34 } from "node:path";
|
|
31708
31708
|
function readJson2(d, path4) {
|
|
31709
31709
|
if (!d.existsSync(path4))
|
|
31710
31710
|
return { kind: "absent" };
|
|
@@ -31738,7 +31738,7 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
31738
31738
|
];
|
|
31739
31739
|
}
|
|
31740
31740
|
for (const name of agents) {
|
|
31741
|
-
const agentDir =
|
|
31741
|
+
const agentDir = resolve34(agentsDir, name);
|
|
31742
31742
|
if (!deps.existsSync(agentDir)) {
|
|
31743
31743
|
results.push({
|
|
31744
31744
|
name: `${label}: ${name} scaffold`,
|
|
@@ -31747,8 +31747,8 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
31747
31747
|
});
|
|
31748
31748
|
continue;
|
|
31749
31749
|
}
|
|
31750
|
-
const mcpPath =
|
|
31751
|
-
const claudeJsonPath =
|
|
31750
|
+
const mcpPath = join55(agentDir, ".mcp.json");
|
|
31751
|
+
const claudeJsonPath = join55(agentDir, ".claude", ".claude.json");
|
|
31752
31752
|
const mcpRead = readJson2(deps, mcpPath);
|
|
31753
31753
|
const trustRead = readJson2(deps, claudeJsonPath);
|
|
31754
31754
|
if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
|
|
@@ -31778,7 +31778,7 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
31778
31778
|
let trustDetail = "no .claude/.claude.json";
|
|
31779
31779
|
if (trustRead.kind === "ok") {
|
|
31780
31780
|
const projects = trustRead.data?.projects ?? {};
|
|
31781
|
-
const proj = projects[
|
|
31781
|
+
const proj = projects[resolve34(agentDir)];
|
|
31782
31782
|
const enabled = proj?.enabledMcpjsonServers;
|
|
31783
31783
|
if (Array.isArray(enabled) && enabled.includes(mcpKey)) {
|
|
31784
31784
|
trustOk = true;
|
|
@@ -31812,14 +31812,14 @@ import {
|
|
|
31812
31812
|
existsSync as realExistsSync3,
|
|
31813
31813
|
readFileSync as realReadFileSync3
|
|
31814
31814
|
} from "node:fs";
|
|
31815
|
-
import { join as
|
|
31815
|
+
import { join as join56 } from "node:path";
|
|
31816
31816
|
import { homedir as homedir31 } from "node:os";
|
|
31817
31817
|
function resolveDeps3(deps) {
|
|
31818
31818
|
const home2 = deps.homeDir?.() ?? homedir31();
|
|
31819
31819
|
return {
|
|
31820
31820
|
existsSync: deps.existsSync ?? realExistsSync3,
|
|
31821
31821
|
readFileSync: deps.readFileSync ?? realReadFileSync3,
|
|
31822
|
-
agentsDir:
|
|
31822
|
+
agentsDir: join56(home2, ".switchroom", "agents"),
|
|
31823
31823
|
now: deps.now ?? Date.now
|
|
31824
31824
|
};
|
|
31825
31825
|
}
|
|
@@ -31902,7 +31902,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
|
|
|
31902
31902
|
];
|
|
31903
31903
|
}
|
|
31904
31904
|
function readHeartbeat(d, agentName) {
|
|
31905
|
-
const path4 =
|
|
31905
|
+
const path4 = join56(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
|
|
31906
31906
|
if (!d.existsSync(path4)) {
|
|
31907
31907
|
return { error: "heartbeat file missing \u2014 launcher has not yet started" };
|
|
31908
31908
|
}
|
|
@@ -31999,7 +31999,7 @@ import {
|
|
|
31999
31999
|
readFileSync as realReadFileSync4,
|
|
32000
32000
|
statSync as realStatSync2
|
|
32001
32001
|
} from "node:fs";
|
|
32002
|
-
import { join as
|
|
32002
|
+
import { join as join57 } from "node:path";
|
|
32003
32003
|
import { homedir as homedir32 } from "node:os";
|
|
32004
32004
|
function resolveDeps4(deps) {
|
|
32005
32005
|
const home2 = deps.homeDir?.() ?? homedir32();
|
|
@@ -32007,7 +32007,7 @@ function resolveDeps4(deps) {
|
|
|
32007
32007
|
existsSync: deps.existsSync ?? realExistsSync4,
|
|
32008
32008
|
readFileSync: deps.readFileSync ?? realReadFileSync4,
|
|
32009
32009
|
statSync: deps.statSync ?? realStatSync2,
|
|
32010
|
-
agentsDir:
|
|
32010
|
+
agentsDir: join57(home2, ".switchroom", "agents"),
|
|
32011
32011
|
now: deps.now ?? Date.now,
|
|
32012
32012
|
vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
|
|
32013
32013
|
};
|
|
@@ -32132,7 +32132,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
|
|
|
32132
32132
|
return [];
|
|
32133
32133
|
const results = [];
|
|
32134
32134
|
for (const name of notionAgents) {
|
|
32135
|
-
const heartbeatPath =
|
|
32135
|
+
const heartbeatPath = join57(d.agentsDir, name, "notion-launcher.heartbeat.json");
|
|
32136
32136
|
if (!d.existsSync(heartbeatPath)) {
|
|
32137
32137
|
results.push({
|
|
32138
32138
|
name: `notion:launcher-heartbeat:${name}`,
|
|
@@ -32354,10 +32354,10 @@ import {
|
|
|
32354
32354
|
statSync as realStatSync3
|
|
32355
32355
|
} from "node:fs";
|
|
32356
32356
|
import { homedir as homedir33 } from "node:os";
|
|
32357
|
-
import { join as
|
|
32357
|
+
import { join as join58 } from "node:path";
|
|
32358
32358
|
function runCredentialsMigrationChecks(config, deps = {}) {
|
|
32359
|
-
const credDir = deps.credentialsDir ??
|
|
32360
|
-
const
|
|
32359
|
+
const credDir = deps.credentialsDir ?? join58(homedir33(), ".switchroom", "credentials");
|
|
32360
|
+
const existsSync57 = deps.existsSync ?? ((p) => realExistsSync5(p));
|
|
32361
32361
|
const readdirSync21 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
|
|
32362
32362
|
const isDirectory = deps.isDirectory ?? ((p) => {
|
|
32363
32363
|
try {
|
|
@@ -32366,7 +32366,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
|
|
|
32366
32366
|
return false;
|
|
32367
32367
|
}
|
|
32368
32368
|
});
|
|
32369
|
-
if (!
|
|
32369
|
+
if (!existsSync57(credDir))
|
|
32370
32370
|
return [];
|
|
32371
32371
|
const agentNames = new Set(Object.keys(config.agents ?? {}));
|
|
32372
32372
|
let entries;
|
|
@@ -32384,7 +32384,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
|
|
|
32384
32384
|
const flat = [];
|
|
32385
32385
|
const perAgentDirs = [];
|
|
32386
32386
|
for (const e of entries) {
|
|
32387
|
-
const full =
|
|
32387
|
+
const full = join58(credDir, e);
|
|
32388
32388
|
if (isDirectory(full) && agentNames.has(e)) {
|
|
32389
32389
|
perAgentDirs.push(e);
|
|
32390
32390
|
} else {
|
|
@@ -32508,13 +32508,13 @@ var init_doctor_inlined_secrets = __esm(() => {
|
|
|
32508
32508
|
// src/cli/doctor-audit-integrity.ts
|
|
32509
32509
|
import { readFileSync as fsReadFileSync2 } from "node:fs";
|
|
32510
32510
|
import { homedir as homedir34 } from "node:os";
|
|
32511
|
-
import { join as
|
|
32511
|
+
import { join as join59 } from "node:path";
|
|
32512
32512
|
function rootWrittenLogs(home2) {
|
|
32513
32513
|
return [
|
|
32514
|
-
{ label: "vault-broker", path:
|
|
32514
|
+
{ label: "vault-broker", path: join59(home2, ".switchroom", "vault-audit.log") },
|
|
32515
32515
|
{
|
|
32516
32516
|
label: "hostd",
|
|
32517
|
-
path:
|
|
32517
|
+
path: join59(home2, ".switchroom", "host-control-audit.log")
|
|
32518
32518
|
}
|
|
32519
32519
|
];
|
|
32520
32520
|
}
|
|
@@ -32576,16 +32576,16 @@ var init_doctor_audit_integrity = __esm(() => {
|
|
|
32576
32576
|
});
|
|
32577
32577
|
|
|
32578
32578
|
// src/cli/doctor-agent-smoke.ts
|
|
32579
|
-
import { existsSync as
|
|
32579
|
+
import { existsSync as existsSync57 } from "node:fs";
|
|
32580
32580
|
import { homedir as homedir35 } from "node:os";
|
|
32581
|
-
import { join as
|
|
32581
|
+
import { join as join60 } from "node:path";
|
|
32582
32582
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
32583
32583
|
async function runAgentSmokeChecks(config, deps = {}) {
|
|
32584
32584
|
if (deps.fast)
|
|
32585
32585
|
return [];
|
|
32586
32586
|
const home2 = deps.homeDir ?? homedir35();
|
|
32587
|
-
const sock = deps.operatorSockPath ??
|
|
32588
|
-
if (!deps.hostdRequestImpl && !
|
|
32587
|
+
const sock = deps.operatorSockPath ?? join60(home2, ".switchroom", "hostd", "operator", "sock");
|
|
32588
|
+
if (!deps.hostdRequestImpl && !existsSync57(sock)) {
|
|
32589
32589
|
return [
|
|
32590
32590
|
{
|
|
32591
32591
|
name: "agent liveness",
|
|
@@ -32663,9 +32663,9 @@ var init_doctor_agent_smoke = __esm(() => {
|
|
|
32663
32663
|
|
|
32664
32664
|
// src/cli/doctor-vault-broker-durability.ts
|
|
32665
32665
|
import { execFileSync as execFileSync18 } from "node:child_process";
|
|
32666
|
-
import { existsSync as
|
|
32666
|
+
import { existsSync as existsSync58, statSync as statSync24 } from "node:fs";
|
|
32667
32667
|
import { homedir as homedir36 } from "node:os";
|
|
32668
|
-
import { join as
|
|
32668
|
+
import { join as join61 } from "node:path";
|
|
32669
32669
|
function probeBindMountInode(hostPath, brokerContainerPath, opts) {
|
|
32670
32670
|
const statHost = opts?.statHost ?? defaultStatHost;
|
|
32671
32671
|
const statBroker = opts?.statBroker ?? defaultStatBroker;
|
|
@@ -32687,7 +32687,7 @@ function probeBindMountInode(hostPath, brokerContainerPath, opts) {
|
|
|
32687
32687
|
};
|
|
32688
32688
|
}
|
|
32689
32689
|
function defaultStatHost(p) {
|
|
32690
|
-
if (!
|
|
32690
|
+
if (!existsSync58(p))
|
|
32691
32691
|
return null;
|
|
32692
32692
|
try {
|
|
32693
32693
|
const s = statSync24(p, { bigint: true });
|
|
@@ -32814,16 +32814,16 @@ function runVaultBrokerDurabilityChecks(_config, opts) {
|
|
|
32814
32814
|
probeBrokerUnlocked(opts?.statusProbe),
|
|
32815
32815
|
probeAutoUnlockBlob(home2),
|
|
32816
32816
|
probeMachineIdMount(),
|
|
32817
|
-
formatBindMountResult("vault-broker: vault.enc bind mount",
|
|
32818
|
-
formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)",
|
|
32819
|
-
formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)",
|
|
32817
|
+
formatBindMountResult("vault-broker: vault.enc bind mount", join61(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join61(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
|
|
32818
|
+
formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join61(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join61(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
|
|
32819
|
+
formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join61(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join61(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
|
|
32820
32820
|
probeKernelDbDurability(home2, {
|
|
32821
32821
|
statBroker: opts?.kernelStatBroker
|
|
32822
32822
|
})
|
|
32823
32823
|
];
|
|
32824
32824
|
}
|
|
32825
32825
|
function probeKernelDbDurability(home2, opts) {
|
|
32826
|
-
const hostDir =
|
|
32826
|
+
const hostDir = join61(home2, ".switchroom", "approvals");
|
|
32827
32827
|
const containerDir = "/state/approvals";
|
|
32828
32828
|
const name = "approval-kernel: approvals bind mount (allow_always durability)";
|
|
32829
32829
|
const kernelStat = opts?.statBroker ?? defaultKernelStatBroker;
|
|
@@ -32891,8 +32891,8 @@ function defaultKernelStatBroker(p) {
|
|
|
32891
32891
|
return { kind: "ok-with-stat", ino: inoStr, size };
|
|
32892
32892
|
}
|
|
32893
32893
|
function probeAutoUnlockBlob(home2) {
|
|
32894
|
-
const blobPath =
|
|
32895
|
-
if (!
|
|
32894
|
+
const blobPath = join61(home2, ".switchroom", "vault-auto-unlock");
|
|
32895
|
+
if (!existsSync58(blobPath)) {
|
|
32896
32896
|
return {
|
|
32897
32897
|
name: "vault-broker: auto-unlock blob",
|
|
32898
32898
|
status: "warn",
|
|
@@ -32916,7 +32916,7 @@ function probeAutoUnlockBlob(home2) {
|
|
|
32916
32916
|
};
|
|
32917
32917
|
}
|
|
32918
32918
|
function probeMachineIdMount() {
|
|
32919
|
-
const hostExists =
|
|
32919
|
+
const hostExists = existsSync58("/etc/machine-id");
|
|
32920
32920
|
if (!hostExists) {
|
|
32921
32921
|
return {
|
|
32922
32922
|
name: "vault-broker: machine-id passthrough",
|
|
@@ -33064,23 +33064,23 @@ import { execSync as execSync3, spawnSync as spawnSync10 } from "node:child_proc
|
|
|
33064
33064
|
import {
|
|
33065
33065
|
accessSync as accessSync2,
|
|
33066
33066
|
constants as fsConstants5,
|
|
33067
|
-
existsSync as
|
|
33067
|
+
existsSync as existsSync59,
|
|
33068
33068
|
lstatSync as lstatSync7,
|
|
33069
33069
|
mkdirSync as mkdirSync31,
|
|
33070
|
-
readFileSync as
|
|
33070
|
+
readFileSync as readFileSync53,
|
|
33071
33071
|
readdirSync as readdirSync21,
|
|
33072
33072
|
statSync as statSync25
|
|
33073
33073
|
} from "node:fs";
|
|
33074
|
-
import { dirname as dirname15, join as
|
|
33074
|
+
import { dirname as dirname15, join as join62, resolve as resolve35 } from "node:path";
|
|
33075
33075
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
33076
33076
|
function findInNvm(bin) {
|
|
33077
|
-
const nvmRoot =
|
|
33078
|
-
if (!
|
|
33077
|
+
const nvmRoot = join62(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
33078
|
+
if (!existsSync59(nvmRoot))
|
|
33079
33079
|
return null;
|
|
33080
33080
|
try {
|
|
33081
33081
|
const versions = readdirSync21(nvmRoot).sort().reverse();
|
|
33082
33082
|
for (const v of versions) {
|
|
33083
|
-
const candidate =
|
|
33083
|
+
const candidate = join62(nvmRoot, v, "bin", bin);
|
|
33084
33084
|
try {
|
|
33085
33085
|
const s = statSync25(candidate);
|
|
33086
33086
|
if (s.isFile() || s.isSymbolicLink()) {
|
|
@@ -33245,21 +33245,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
|
|
|
33245
33245
|
if (envBrowsersPath && envBrowsersPath.length > 0) {
|
|
33246
33246
|
cacheLocations.push(envBrowsersPath);
|
|
33247
33247
|
}
|
|
33248
|
-
cacheLocations.push(
|
|
33248
|
+
cacheLocations.push(join62(homeDir, ".cache", "ms-playwright"));
|
|
33249
33249
|
for (const cacheDir of cacheLocations) {
|
|
33250
|
-
if (!
|
|
33250
|
+
if (!existsSync59(cacheDir))
|
|
33251
33251
|
continue;
|
|
33252
33252
|
try {
|
|
33253
33253
|
const entries = readdirSync21(cacheDir).filter((e) => e.startsWith("chromium"));
|
|
33254
33254
|
for (const entry of entries) {
|
|
33255
33255
|
const candidates2 = [
|
|
33256
|
-
|
|
33257
|
-
|
|
33258
|
-
|
|
33259
|
-
|
|
33256
|
+
join62(cacheDir, entry, "chrome-linux64", "chrome"),
|
|
33257
|
+
join62(cacheDir, entry, "chrome-linux", "chrome"),
|
|
33258
|
+
join62(cacheDir, entry, "chrome-linux64", "headless_shell"),
|
|
33259
|
+
join62(cacheDir, entry, "chrome-linux", "headless_shell")
|
|
33260
33260
|
];
|
|
33261
33261
|
for (const path4 of candidates2) {
|
|
33262
|
-
if (
|
|
33262
|
+
if (existsSync59(path4))
|
|
33263
33263
|
return path4;
|
|
33264
33264
|
}
|
|
33265
33265
|
}
|
|
@@ -33388,7 +33388,7 @@ function checkDeployMounts(opts) {
|
|
|
33388
33388
|
const home2 = opts?.home ?? process.env.HOME ?? "/root";
|
|
33389
33389
|
const { pathKind } = opts?.deps ?? DEFAULT_DEPLOY_MOUNTS_DEPS;
|
|
33390
33390
|
const results = [];
|
|
33391
|
-
const dockerComposePlugin =
|
|
33391
|
+
const dockerComposePlugin = join62(home2, ".docker", "cli-plugins", "docker-compose");
|
|
33392
33392
|
const pluginKind = pathKind(dockerComposePlugin);
|
|
33393
33393
|
if (pluginKind === "dir") {
|
|
33394
33394
|
results.push({
|
|
@@ -33426,8 +33426,8 @@ function checkDeployMounts(opts) {
|
|
|
33426
33426
|
function checkLegacyState() {
|
|
33427
33427
|
const results = [];
|
|
33428
33428
|
const h = process.env.HOME ?? "/root";
|
|
33429
|
-
const clerkDir =
|
|
33430
|
-
const clerkPresent =
|
|
33429
|
+
const clerkDir = join62(h, LEGACY_STATE_DIR);
|
|
33430
|
+
const clerkPresent = existsSync59(clerkDir);
|
|
33431
33431
|
results.push({
|
|
33432
33432
|
name: "legacy ~/.clerk state",
|
|
33433
33433
|
status: clerkPresent ? "warn" : "ok",
|
|
@@ -33436,7 +33436,7 @@ function checkLegacyState() {
|
|
|
33436
33436
|
fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
|
|
33437
33437
|
} : {}
|
|
33438
33438
|
});
|
|
33439
|
-
const legacySock =
|
|
33439
|
+
const legacySock = join62(h, ".switchroom", "vault-broker.sock");
|
|
33440
33440
|
let sockStat = null;
|
|
33441
33441
|
try {
|
|
33442
33442
|
sockStat = lstatSync7(legacySock);
|
|
@@ -33557,7 +33557,7 @@ function checkVault(config) {
|
|
|
33557
33557
|
detail: "Approval auth: passphrase (two-factor)"
|
|
33558
33558
|
};
|
|
33559
33559
|
const pairsResult = checkVaultBrokerSocketPairs(config);
|
|
33560
|
-
if (!
|
|
33560
|
+
if (!existsSync59(vaultPath)) {
|
|
33561
33561
|
return [
|
|
33562
33562
|
postureResult,
|
|
33563
33563
|
{
|
|
@@ -33819,8 +33819,8 @@ async function checkHindsight(config) {
|
|
|
33819
33819
|
}
|
|
33820
33820
|
function checkPendingRetainsQueue(dir) {
|
|
33821
33821
|
const home2 = process.env.HOME ?? "";
|
|
33822
|
-
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ??
|
|
33823
|
-
if (!
|
|
33822
|
+
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join62(home2, ".hindsight", "pending-retains");
|
|
33823
|
+
if (!existsSync59(pendingDir)) {
|
|
33824
33824
|
return {
|
|
33825
33825
|
name: "pending-retains queue",
|
|
33826
33826
|
status: "ok",
|
|
@@ -33879,7 +33879,7 @@ function classifyReadError(err) {
|
|
|
33879
33879
|
}
|
|
33880
33880
|
function tryReadHostFile(path4) {
|
|
33881
33881
|
try {
|
|
33882
|
-
return { kind: "ok", content:
|
|
33882
|
+
return { kind: "ok", content: readFileSync53(path4, "utf-8") };
|
|
33883
33883
|
} catch (err) {
|
|
33884
33884
|
const kind = classifyReadError(err);
|
|
33885
33885
|
const error = err?.message ?? String(err);
|
|
@@ -33891,11 +33891,11 @@ function tryReadHostFile(path4) {
|
|
|
33891
33891
|
}
|
|
33892
33892
|
}
|
|
33893
33893
|
function parseEnvFile(path4) {
|
|
33894
|
-
if (!
|
|
33894
|
+
if (!existsSync59(path4))
|
|
33895
33895
|
return {};
|
|
33896
33896
|
let content;
|
|
33897
33897
|
try {
|
|
33898
|
-
content =
|
|
33898
|
+
content = readFileSync53(path4, "utf-8");
|
|
33899
33899
|
} catch {
|
|
33900
33900
|
return {};
|
|
33901
33901
|
}
|
|
@@ -33950,7 +33950,7 @@ async function checkTelegram(config) {
|
|
|
33950
33950
|
const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
|
|
33951
33951
|
if (plugin !== "switchroom")
|
|
33952
33952
|
continue;
|
|
33953
|
-
const envPath =
|
|
33953
|
+
const envPath = join62(agentsDir, name, "telegram", ".env");
|
|
33954
33954
|
const read = tryReadHostFile(envPath);
|
|
33955
33955
|
if (read.kind === "eacces") {
|
|
33956
33956
|
results.push({
|
|
@@ -34002,7 +34002,7 @@ async function checkTelegram(config) {
|
|
|
34002
34002
|
}
|
|
34003
34003
|
function checkStartShStale(agentName, startShPath) {
|
|
34004
34004
|
const label = `${agentName}: start.sh scheduler block`;
|
|
34005
|
-
if (!
|
|
34005
|
+
if (!existsSync59(startShPath)) {
|
|
34006
34006
|
return {
|
|
34007
34007
|
name: label,
|
|
34008
34008
|
status: "warn",
|
|
@@ -34012,7 +34012,7 @@ function checkStartShStale(agentName, startShPath) {
|
|
|
34012
34012
|
}
|
|
34013
34013
|
let content;
|
|
34014
34014
|
try {
|
|
34015
|
-
content =
|
|
34015
|
+
content = readFileSync53(startShPath, "utf-8");
|
|
34016
34016
|
} catch (err) {
|
|
34017
34017
|
return {
|
|
34018
34018
|
name: label,
|
|
@@ -34033,7 +34033,7 @@ function checkStartShStale(agentName, startShPath) {
|
|
|
34033
34033
|
}
|
|
34034
34034
|
function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
34035
34035
|
const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
|
|
34036
|
-
const path4 =
|
|
34036
|
+
const path4 = join62(agentDir, "home", ".switchroom");
|
|
34037
34037
|
let stats;
|
|
34038
34038
|
try {
|
|
34039
34039
|
stats = lstatSync7(path4);
|
|
@@ -34070,8 +34070,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
|
34070
34070
|
}
|
|
34071
34071
|
function checkRepoHygiene(repoRoot) {
|
|
34072
34072
|
const results = [];
|
|
34073
|
-
const exportDir =
|
|
34074
|
-
if (
|
|
34073
|
+
const exportDir = join62(repoRoot, "clerk-export");
|
|
34074
|
+
if (existsSync59(exportDir)) {
|
|
34075
34075
|
results.push({
|
|
34076
34076
|
name: "repo hygiene: clerk-export/ on disk (#1072)",
|
|
34077
34077
|
status: "warn",
|
|
@@ -34079,8 +34079,8 @@ function checkRepoHygiene(repoRoot) {
|
|
|
34079
34079
|
fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
|
|
34080
34080
|
});
|
|
34081
34081
|
}
|
|
34082
|
-
const knownTarball =
|
|
34083
|
-
if (
|
|
34082
|
+
const knownTarball = join62(repoRoot, "clerk-export-with-secrets.tar.gz");
|
|
34083
|
+
if (existsSync59(knownTarball)) {
|
|
34084
34084
|
results.push({
|
|
34085
34085
|
name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
|
|
34086
34086
|
status: "warn",
|
|
@@ -34097,7 +34097,7 @@ function checkRepoHygiene(repoRoot) {
|
|
|
34097
34097
|
results.push({
|
|
34098
34098
|
name: `repo hygiene: ${name} on disk (#1072)`,
|
|
34099
34099
|
status: "warn",
|
|
34100
|
-
detail: `${
|
|
34100
|
+
detail: `${join62(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
|
|
34101
34101
|
fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
|
|
34102
34102
|
});
|
|
34103
34103
|
}
|
|
@@ -34120,12 +34120,12 @@ function checkRepoHygiene(repoRoot) {
|
|
|
34120
34120
|
}
|
|
34121
34121
|
function isSwitchroomCheckout(dir) {
|
|
34122
34122
|
try {
|
|
34123
|
-
if (!
|
|
34123
|
+
if (!existsSync59(join62(dir, ".git")))
|
|
34124
34124
|
return false;
|
|
34125
|
-
const pkgPath =
|
|
34126
|
-
if (!
|
|
34125
|
+
const pkgPath = join62(dir, "package.json");
|
|
34126
|
+
if (!existsSync59(pkgPath))
|
|
34127
34127
|
return false;
|
|
34128
|
-
const pkg = JSON.parse(
|
|
34128
|
+
const pkg = JSON.parse(readFileSync53(pkgPath, "utf-8"));
|
|
34129
34129
|
return pkg.name === "switchroom";
|
|
34130
34130
|
} catch {
|
|
34131
34131
|
return false;
|
|
@@ -34137,8 +34137,8 @@ function checkAgents(config, configPath) {
|
|
|
34137
34137
|
const statuses = getAllAgentStatuses(config);
|
|
34138
34138
|
const authStatuses = getAllAuthStatuses(config);
|
|
34139
34139
|
for (const [name, agentConfig] of Object.entries(config.agents)) {
|
|
34140
|
-
const agentDir =
|
|
34141
|
-
if (!
|
|
34140
|
+
const agentDir = resolve35(agentsDir, name);
|
|
34141
|
+
if (!existsSync59(agentDir)) {
|
|
34142
34142
|
results.push({
|
|
34143
34143
|
name: `${name}: scaffold`,
|
|
34144
34144
|
status: "fail",
|
|
@@ -34159,7 +34159,7 @@ function checkAgents(config, configPath) {
|
|
|
34159
34159
|
fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
|
|
34160
34160
|
});
|
|
34161
34161
|
}
|
|
34162
|
-
results.push(checkStartShStale(name,
|
|
34162
|
+
results.push(checkStartShStale(name, join62(agentDir, "start.sh")));
|
|
34163
34163
|
results.push(checkLeakedHomeSwitchroom(name, agentDir));
|
|
34164
34164
|
const status = statuses[name];
|
|
34165
34165
|
const active = status?.active ?? "unknown";
|
|
@@ -34236,8 +34236,8 @@ function checkAgents(config, configPath) {
|
|
|
34236
34236
|
}
|
|
34237
34237
|
}
|
|
34238
34238
|
if (agentConfig.channels?.telegram?.plugin === "switchroom") {
|
|
34239
|
-
const mcpJsonPath =
|
|
34240
|
-
if (!
|
|
34239
|
+
const mcpJsonPath = join62(agentDir, ".mcp.json");
|
|
34240
|
+
if (!existsSync59(mcpJsonPath)) {
|
|
34241
34241
|
results.push({
|
|
34242
34242
|
name: `${name}: .mcp.json`,
|
|
34243
34243
|
status: "fail",
|
|
@@ -34246,7 +34246,7 @@ function checkAgents(config, configPath) {
|
|
|
34246
34246
|
});
|
|
34247
34247
|
} else {
|
|
34248
34248
|
try {
|
|
34249
|
-
const mcp = JSON.parse(
|
|
34249
|
+
const mcp = JSON.parse(readFileSync53(mcpJsonPath, "utf-8"));
|
|
34250
34250
|
const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
|
|
34251
34251
|
const memoryEnabled = isHindsightEnabled(config);
|
|
34252
34252
|
const hasHindsight = !!mcp.mcpServers?.hindsight;
|
|
@@ -34320,10 +34320,10 @@ function mffAgentName(config) {
|
|
|
34320
34320
|
function mffEnvPath(config) {
|
|
34321
34321
|
const home2 = process.env.HOME ?? "/root";
|
|
34322
34322
|
const agent = mffAgentName(config);
|
|
34323
|
-
return agent ?
|
|
34323
|
+
return agent ? resolve35(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve35(home2, ".switchroom/credentials/my-family-finance/.env");
|
|
34324
34324
|
}
|
|
34325
34325
|
function mffEnvState(envPath) {
|
|
34326
|
-
if (!
|
|
34326
|
+
if (!existsSync59(envPath))
|
|
34327
34327
|
return "absent";
|
|
34328
34328
|
try {
|
|
34329
34329
|
accessSync2(envPath, fsConstants5.R_OK);
|
|
@@ -34341,7 +34341,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
|
|
|
34341
34341
|
fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
|
|
34342
34342
|
};
|
|
34343
34343
|
}
|
|
34344
|
-
if (!
|
|
34344
|
+
if (!existsSync59(vaultPath)) {
|
|
34345
34345
|
return {
|
|
34346
34346
|
name: "mff: vault key present",
|
|
34347
34347
|
status: "fail",
|
|
@@ -34394,7 +34394,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
|
|
|
34394
34394
|
}
|
|
34395
34395
|
}
|
|
34396
34396
|
function checkMffVaultKeyFormat(passphrase, vaultPath) {
|
|
34397
|
-
if (!passphrase || !
|
|
34397
|
+
if (!passphrase || !existsSync59(vaultPath)) {
|
|
34398
34398
|
return {
|
|
34399
34399
|
name: "mff: vault key format",
|
|
34400
34400
|
status: "warn",
|
|
@@ -34538,8 +34538,8 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
34538
34538
|
};
|
|
34539
34539
|
}
|
|
34540
34540
|
const credDir = dirname15(envPath);
|
|
34541
|
-
const authScript =
|
|
34542
|
-
if (!
|
|
34541
|
+
const authScript = join62(credDir, "claude-auth.py");
|
|
34542
|
+
if (!existsSync59(authScript)) {
|
|
34543
34543
|
return {
|
|
34544
34544
|
name: "mff: auth flow",
|
|
34545
34545
|
status: "warn",
|
|
@@ -34736,16 +34736,16 @@ async function checkManifestDrift(probers) {
|
|
|
34736
34736
|
return results;
|
|
34737
34737
|
}
|
|
34738
34738
|
function runDockerSection(config) {
|
|
34739
|
-
const composePath =
|
|
34739
|
+
const composePath = resolve35(process.env.HOME ?? "", ".switchroom", "compose", "docker-compose.yml");
|
|
34740
34740
|
const active = isDockerMode({ composePath });
|
|
34741
34741
|
let composeYaml;
|
|
34742
34742
|
let dockerfileAgent;
|
|
34743
34743
|
try {
|
|
34744
|
-
composeYaml =
|
|
34744
|
+
composeYaml = readFileSync53(composePath, "utf8");
|
|
34745
34745
|
} catch {}
|
|
34746
|
-
const dockerfilePath =
|
|
34746
|
+
const dockerfilePath = resolve35(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
|
|
34747
34747
|
try {
|
|
34748
|
-
dockerfileAgent =
|
|
34748
|
+
dockerfileAgent = readFileSync53(dockerfilePath, "utf8");
|
|
34749
34749
|
} catch {}
|
|
34750
34750
|
return runDockerChecks({
|
|
34751
34751
|
config,
|
|
@@ -43244,7 +43244,7 @@ class Protocol {
|
|
|
43244
43244
|
return;
|
|
43245
43245
|
}
|
|
43246
43246
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
43247
|
-
await new Promise((
|
|
43247
|
+
await new Promise((resolve53) => setTimeout(resolve53, pollInterval));
|
|
43248
43248
|
options?.signal?.throwIfAborted();
|
|
43249
43249
|
}
|
|
43250
43250
|
} catch (error2) {
|
|
@@ -43256,7 +43256,7 @@ class Protocol {
|
|
|
43256
43256
|
}
|
|
43257
43257
|
request(request, resultSchema, options) {
|
|
43258
43258
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
43259
|
-
return new Promise((
|
|
43259
|
+
return new Promise((resolve53, reject) => {
|
|
43260
43260
|
const earlyReject = (error2) => {
|
|
43261
43261
|
reject(error2);
|
|
43262
43262
|
};
|
|
@@ -43334,7 +43334,7 @@ class Protocol {
|
|
|
43334
43334
|
if (!parseResult.success) {
|
|
43335
43335
|
reject(parseResult.error);
|
|
43336
43336
|
} else {
|
|
43337
|
-
|
|
43337
|
+
resolve53(parseResult.data);
|
|
43338
43338
|
}
|
|
43339
43339
|
} catch (error2) {
|
|
43340
43340
|
reject(error2);
|
|
@@ -43525,12 +43525,12 @@ class Protocol {
|
|
|
43525
43525
|
interval = task.pollInterval;
|
|
43526
43526
|
}
|
|
43527
43527
|
} catch {}
|
|
43528
|
-
return new Promise((
|
|
43528
|
+
return new Promise((resolve53, reject) => {
|
|
43529
43529
|
if (signal.aborted) {
|
|
43530
43530
|
reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
|
|
43531
43531
|
return;
|
|
43532
43532
|
}
|
|
43533
|
-
const timeoutId = setTimeout(
|
|
43533
|
+
const timeoutId = setTimeout(resolve53, interval);
|
|
43534
43534
|
signal.addEventListener("abort", () => {
|
|
43535
43535
|
clearTimeout(timeoutId);
|
|
43536
43536
|
reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
|
|
@@ -46515,7 +46515,7 @@ var require_compile = __commonJS((exports2) => {
|
|
|
46515
46515
|
const schOrFunc = root.refs[ref];
|
|
46516
46516
|
if (schOrFunc)
|
|
46517
46517
|
return schOrFunc;
|
|
46518
|
-
let _sch =
|
|
46518
|
+
let _sch = resolve53.call(this, root, ref);
|
|
46519
46519
|
if (_sch === undefined) {
|
|
46520
46520
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
46521
46521
|
const { schemaId } = this.opts;
|
|
@@ -46542,7 +46542,7 @@ var require_compile = __commonJS((exports2) => {
|
|
|
46542
46542
|
function sameSchemaEnv(s1, s2) {
|
|
46543
46543
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
46544
46544
|
}
|
|
46545
|
-
function
|
|
46545
|
+
function resolve53(root, ref) {
|
|
46546
46546
|
let sch;
|
|
46547
46547
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
46548
46548
|
ref = sch;
|
|
@@ -47072,7 +47072,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
47072
47072
|
}
|
|
47073
47073
|
return uri;
|
|
47074
47074
|
}
|
|
47075
|
-
function
|
|
47075
|
+
function resolve53(baseURI, relativeURI, options) {
|
|
47076
47076
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
47077
47077
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
47078
47078
|
schemelessOptions.skipEscape = true;
|
|
@@ -47300,7 +47300,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
47300
47300
|
var fastUri = {
|
|
47301
47301
|
SCHEMES,
|
|
47302
47302
|
normalize,
|
|
47303
|
-
resolve:
|
|
47303
|
+
resolve: resolve53,
|
|
47304
47304
|
resolveComponent,
|
|
47305
47305
|
equal,
|
|
47306
47306
|
serialize,
|
|
@@ -50683,12 +50683,12 @@ class StdioServerTransport {
|
|
|
50683
50683
|
this.onclose?.();
|
|
50684
50684
|
}
|
|
50685
50685
|
send(message) {
|
|
50686
|
-
return new Promise((
|
|
50686
|
+
return new Promise((resolve53) => {
|
|
50687
50687
|
const json = serializeMessage(message);
|
|
50688
50688
|
if (this._stdout.write(json)) {
|
|
50689
|
-
|
|
50689
|
+
resolve53();
|
|
50690
50690
|
} else {
|
|
50691
|
-
this._stdout.once("drain",
|
|
50691
|
+
this._stdout.once("drain", resolve53);
|
|
50692
50692
|
}
|
|
50693
50693
|
});
|
|
50694
50694
|
}
|
|
@@ -51200,7 +51200,7 @@ __export(exports_server2, {
|
|
|
51200
51200
|
TOOLS: () => TOOLS2
|
|
51201
51201
|
});
|
|
51202
51202
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
51203
|
-
import { existsSync as
|
|
51203
|
+
import { existsSync as existsSync86, readFileSync as readFileSync74 } from "node:fs";
|
|
51204
51204
|
function selfSocketPath() {
|
|
51205
51205
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
51206
51206
|
}
|
|
@@ -51218,7 +51218,7 @@ async function dispatchTool2(name, args) {
|
|
|
51218
51218
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
51219
51219
|
}
|
|
51220
51220
|
const sockPath = selfSocketPath();
|
|
51221
|
-
if (!
|
|
51221
|
+
if (!existsSync86(sockPath)) {
|
|
51222
51222
|
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.`);
|
|
51223
51223
|
}
|
|
51224
51224
|
let req;
|
|
@@ -51393,18 +51393,18 @@ function resolveAuditLogPath() {
|
|
|
51393
51393
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
51394
51394
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
51395
51395
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
51396
|
-
if (
|
|
51396
|
+
if (existsSync86(bindMounted))
|
|
51397
51397
|
return bindMounted;
|
|
51398
51398
|
return defaultAuditLogPath2();
|
|
51399
51399
|
}
|
|
51400
51400
|
function getLastUpdateApplyStatus() {
|
|
51401
51401
|
const path8 = resolveAuditLogPath();
|
|
51402
|
-
if (!
|
|
51402
|
+
if (!existsSync86(path8)) {
|
|
51403
51403
|
return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
|
|
51404
51404
|
}
|
|
51405
51405
|
let raw;
|
|
51406
51406
|
try {
|
|
51407
|
-
raw =
|
|
51407
|
+
raw = readFileSync74(path8, "utf-8");
|
|
51408
51408
|
} catch (err2) {
|
|
51409
51409
|
return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
|
|
51410
51410
|
}
|
|
@@ -51674,8 +51674,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
51674
51674
|
import { dirname, join } from "node:path";
|
|
51675
51675
|
|
|
51676
51676
|
// src/build-info.ts
|
|
51677
|
-
var VERSION = "0.16.
|
|
51678
|
-
var COMMIT_SHA = "
|
|
51677
|
+
var VERSION = "0.16.9";
|
|
51678
|
+
var COMMIT_SHA = "1a926737";
|
|
51679
51679
|
|
|
51680
51680
|
// src/cli/resolve-version.ts
|
|
51681
51681
|
function readPackageVersion() {
|
|
@@ -70449,8 +70449,8 @@ init_merge();
|
|
|
70449
70449
|
init_loader();
|
|
70450
70450
|
init_client();
|
|
70451
70451
|
import {
|
|
70452
|
-
readFileSync as
|
|
70453
|
-
existsSync as
|
|
70452
|
+
readFileSync as readFileSync48,
|
|
70453
|
+
existsSync as existsSync52,
|
|
70454
70454
|
realpathSync as realpathSync4,
|
|
70455
70455
|
mkdirSync as mkdirSync29,
|
|
70456
70456
|
openSync as openSync10,
|
|
@@ -70458,7 +70458,7 @@ import {
|
|
|
70458
70458
|
writeSync as writeSync6,
|
|
70459
70459
|
constants as fsConstants3
|
|
70460
70460
|
} from "node:fs";
|
|
70461
|
-
import { resolve as
|
|
70461
|
+
import { resolve as resolve30, extname, join as join49, relative, dirname as dirname12 } from "node:path";
|
|
70462
70462
|
import { homedir as homedir26 } from "node:os";
|
|
70463
70463
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
70464
70464
|
|
|
@@ -77286,6 +77286,388 @@ async function handleWebhookIngest(args, deps = {}) {
|
|
|
77286
77286
|
return jsonReply(202, { ok: true, recorded: true, ts: now });
|
|
77287
77287
|
}
|
|
77288
77288
|
|
|
77289
|
+
// src/web/hermes-adapter.ts
|
|
77290
|
+
init_loader();
|
|
77291
|
+
import { createConnection as createConnection3 } from "node:net";
|
|
77292
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
77293
|
+
import { join as join48, resolve as resolve29 } from "node:path";
|
|
77294
|
+
import { existsSync as existsSync51, readFileSync as readFileSync47 } from "node:fs";
|
|
77295
|
+
|
|
77296
|
+
// src/agent-scheduler/channel-target.ts
|
|
77297
|
+
init_merge();
|
|
77298
|
+
function resolveChannelTarget(config, agentName) {
|
|
77299
|
+
const agent = config.agents?.[agentName];
|
|
77300
|
+
const tgChannel = agent ? resolveAgentConfig(config.defaults, config.profiles, agent).channels?.telegram : undefined;
|
|
77301
|
+
const supergroupChatId = tgChannel?.chat_id;
|
|
77302
|
+
const supergroupDefaultTopic = tgChannel?.default_topic_id;
|
|
77303
|
+
if (typeof supergroupChatId === "string" && supergroupChatId.length > 0) {
|
|
77304
|
+
return {
|
|
77305
|
+
chatId: supergroupChatId,
|
|
77306
|
+
...typeof supergroupDefaultTopic === "number" ? { threadId: supergroupDefaultTopic } : {},
|
|
77307
|
+
routerConfig: {
|
|
77308
|
+
...typeof supergroupDefaultTopic === "number" ? { default_topic_id: supergroupDefaultTopic } : {},
|
|
77309
|
+
...tgChannel?.topic_aliases ? { topic_aliases: tgChannel.topic_aliases } : {}
|
|
77310
|
+
}
|
|
77311
|
+
};
|
|
77312
|
+
}
|
|
77313
|
+
const forumChatId = config.telegram?.forum_chat_id;
|
|
77314
|
+
if (typeof forumChatId !== "string" || forumChatId.length === 0)
|
|
77315
|
+
return null;
|
|
77316
|
+
const threadId = agent?.topic_id;
|
|
77317
|
+
return {
|
|
77318
|
+
chatId: forumChatId,
|
|
77319
|
+
...typeof threadId === "number" ? { threadId } : {}
|
|
77320
|
+
};
|
|
77321
|
+
}
|
|
77322
|
+
|
|
77323
|
+
// src/web/hermes-adapter.ts
|
|
77324
|
+
function rpcOk(id, result) {
|
|
77325
|
+
return { jsonrpc: "2.0", id, result };
|
|
77326
|
+
}
|
|
77327
|
+
function rpcErr(id, code, message, data) {
|
|
77328
|
+
return { jsonrpc: "2.0", id, error: { code, message, ...data !== undefined ? { data } : {} } };
|
|
77329
|
+
}
|
|
77330
|
+
function hermesEvent(type, sessionId, payload) {
|
|
77331
|
+
return { jsonrpc: "2.0", method: "event", params: { type, session_id: sessionId, payload } };
|
|
77332
|
+
}
|
|
77333
|
+
function agentLiveness(config, agentName) {
|
|
77334
|
+
const agentsDir = resolveAgentsDir(config);
|
|
77335
|
+
if (agentBridgeAlive(agentsDir, agentName))
|
|
77336
|
+
return "active";
|
|
77337
|
+
const alive = join48(agentsDir, agentName, "telegram", ".bridge-alive");
|
|
77338
|
+
if (existsSync51(alive))
|
|
77339
|
+
return "idle";
|
|
77340
|
+
return "offline";
|
|
77341
|
+
}
|
|
77342
|
+
function toHermesSession(agent, liveness) {
|
|
77343
|
+
return {
|
|
77344
|
+
id: agent.name,
|
|
77345
|
+
name: agent.name,
|
|
77346
|
+
status: liveness,
|
|
77347
|
+
model: "claude",
|
|
77348
|
+
created_at: null,
|
|
77349
|
+
updated_at: null,
|
|
77350
|
+
quota: agent.primaryAccount ? { used_pct: null, slot: agent.primaryAccount } : null
|
|
77351
|
+
};
|
|
77352
|
+
}
|
|
77353
|
+
function resolveAgentChat(config, agentName, agentsDir) {
|
|
77354
|
+
const channel = resolveChannelTarget(config, agentName);
|
|
77355
|
+
if (channel)
|
|
77356
|
+
return { chatId: channel.chatId, threadId: channel.threadId };
|
|
77357
|
+
const accessPath = resolve29(agentsDir, agentName, "telegram", "access.json");
|
|
77358
|
+
if (!existsSync51(accessPath))
|
|
77359
|
+
return null;
|
|
77360
|
+
try {
|
|
77361
|
+
const raw = readFileSync47(accessPath, "utf-8");
|
|
77362
|
+
const access = JSON.parse(raw);
|
|
77363
|
+
const chatId = access.allowFrom?.[0];
|
|
77364
|
+
if (!chatId)
|
|
77365
|
+
return null;
|
|
77366
|
+
return { chatId };
|
|
77367
|
+
} catch {
|
|
77368
|
+
return null;
|
|
77369
|
+
}
|
|
77370
|
+
}
|
|
77371
|
+
async function injectInbound(agentsDir, agentName, chatId, threadId, text, promptKey) {
|
|
77372
|
+
const socketPath = resolve29(agentsDir, agentName, "telegram", "gateway.sock");
|
|
77373
|
+
if (!existsSync51(socketPath)) {
|
|
77374
|
+
return { ok: false, error: `agent ${agentName} gateway socket not found \u2014 is it running?` };
|
|
77375
|
+
}
|
|
77376
|
+
const ts = Date.now();
|
|
77377
|
+
const envelope = JSON.stringify({
|
|
77378
|
+
type: "inject_inbound",
|
|
77379
|
+
agentName,
|
|
77380
|
+
inbound: {
|
|
77381
|
+
type: "inbound",
|
|
77382
|
+
chatId,
|
|
77383
|
+
...threadId !== undefined ? { threadId } : {},
|
|
77384
|
+
messageId: ts,
|
|
77385
|
+
user: "operator",
|
|
77386
|
+
userId: 0,
|
|
77387
|
+
ts,
|
|
77388
|
+
text,
|
|
77389
|
+
meta: {
|
|
77390
|
+
source: "operator-console",
|
|
77391
|
+
prompt_key: promptKey
|
|
77392
|
+
}
|
|
77393
|
+
}
|
|
77394
|
+
});
|
|
77395
|
+
return new Promise((res) => {
|
|
77396
|
+
const sock = createConnection3(socketPath);
|
|
77397
|
+
let settled = false;
|
|
77398
|
+
const settle = (result) => {
|
|
77399
|
+
if (!settled) {
|
|
77400
|
+
settled = true;
|
|
77401
|
+
sock.destroy();
|
|
77402
|
+
res(result);
|
|
77403
|
+
}
|
|
77404
|
+
};
|
|
77405
|
+
const timeout = setTimeout(() => settle({ ok: false, error: "gateway socket timeout" }), 5000);
|
|
77406
|
+
sock.once("connect", () => {
|
|
77407
|
+
clearTimeout(timeout);
|
|
77408
|
+
sock.write(envelope + `
|
|
77409
|
+
`, (err) => {
|
|
77410
|
+
settle(err ? { ok: false, error: err.message } : { ok: true });
|
|
77411
|
+
});
|
|
77412
|
+
});
|
|
77413
|
+
sock.once("error", (err) => {
|
|
77414
|
+
clearTimeout(timeout);
|
|
77415
|
+
settle({ ok: false, error: err.message });
|
|
77416
|
+
});
|
|
77417
|
+
});
|
|
77418
|
+
}
|
|
77419
|
+
async function handleHermesRest(method, pathname, config) {
|
|
77420
|
+
if (method === "GET" && pathname === "/api/sessions") {
|
|
77421
|
+
const agents = await handleGetAgents(config);
|
|
77422
|
+
const agentsDir = resolveAgentsDir(config);
|
|
77423
|
+
const sessions = agents.map((a) => toHermesSession(a, agentLiveness(config, a.name)));
|
|
77424
|
+
return { status: 200, body: { sessions } };
|
|
77425
|
+
}
|
|
77426
|
+
const sessionMatch = pathname.match(/^\/api\/sessions\/([^/]+)$/);
|
|
77427
|
+
if (method === "GET" && sessionMatch) {
|
|
77428
|
+
const id = decodeURIComponent(sessionMatch[1]);
|
|
77429
|
+
if (!config.agents?.[id])
|
|
77430
|
+
return { status: 404, body: { error: "Unknown session" } };
|
|
77431
|
+
const agents = await handleGetAgents(config);
|
|
77432
|
+
const agent = agents.find((a) => a.name === id);
|
|
77433
|
+
if (!agent)
|
|
77434
|
+
return { status: 404, body: { error: "Unknown session" } };
|
|
77435
|
+
return { status: 200, body: { session: toHermesSession(agent, agentLiveness(config, id)) } };
|
|
77436
|
+
}
|
|
77437
|
+
const historyMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/history$/);
|
|
77438
|
+
if (method === "GET" && historyMatch) {
|
|
77439
|
+
const id = decodeURIComponent(historyMatch[1]);
|
|
77440
|
+
if (!config.agents?.[id])
|
|
77441
|
+
return { status: 404, body: { error: "Unknown session" } };
|
|
77442
|
+
const result = handleGetTurns(config, id, 50);
|
|
77443
|
+
if (!result.ok)
|
|
77444
|
+
return { status: 500, body: { error: result.error } };
|
|
77445
|
+
return { status: 200, body: { history: result.turns ?? [] } };
|
|
77446
|
+
}
|
|
77447
|
+
if (method === "GET" && pathname === "/api/status") {
|
|
77448
|
+
const agents = await handleGetAgents(config);
|
|
77449
|
+
const fleet = agents.map((a) => ({
|
|
77450
|
+
name: a.name,
|
|
77451
|
+
status: agentLiveness(config, a.name)
|
|
77452
|
+
}));
|
|
77453
|
+
return {
|
|
77454
|
+
status: 200,
|
|
77455
|
+
body: {
|
|
77456
|
+
status: "ok",
|
|
77457
|
+
provider: "switchroom",
|
|
77458
|
+
agents: fleet
|
|
77459
|
+
}
|
|
77460
|
+
};
|
|
77461
|
+
}
|
|
77462
|
+
if (method === "GET" && pathname === "/api/config") {
|
|
77463
|
+
const agentNames = Object.keys(config.agents ?? {});
|
|
77464
|
+
return {
|
|
77465
|
+
status: 200,
|
|
77466
|
+
body: {
|
|
77467
|
+
provider: "switchroom",
|
|
77468
|
+
agents: agentNames
|
|
77469
|
+
}
|
|
77470
|
+
};
|
|
77471
|
+
}
|
|
77472
|
+
return null;
|
|
77473
|
+
}
|
|
77474
|
+
function sendEvent(ctx, type, sessionId, payload) {
|
|
77475
|
+
try {
|
|
77476
|
+
ctx.send(JSON.stringify(hermesEvent(type, sessionId, payload)));
|
|
77477
|
+
} catch {}
|
|
77478
|
+
}
|
|
77479
|
+
function sendResponse(ctx, resp) {
|
|
77480
|
+
try {
|
|
77481
|
+
ctx.send(JSON.stringify(resp));
|
|
77482
|
+
} catch {}
|
|
77483
|
+
}
|
|
77484
|
+
function onHermesOpen(ctx) {
|
|
77485
|
+
sendEvent(ctx, "gateway.ready", null, {
|
|
77486
|
+
provider: "switchroom",
|
|
77487
|
+
version: "1.0"
|
|
77488
|
+
});
|
|
77489
|
+
ctx.pollInterval = setInterval(async () => {
|
|
77490
|
+
const agents = await handleGetAgents(ctx.config);
|
|
77491
|
+
for (const agent of agents) {
|
|
77492
|
+
const liveness = agentLiveness(ctx.config, agent.name);
|
|
77493
|
+
sendEvent(ctx, "status.update", agent.name, { status: liveness });
|
|
77494
|
+
}
|
|
77495
|
+
}, 5000);
|
|
77496
|
+
}
|
|
77497
|
+
function onHermesClose(ctx) {
|
|
77498
|
+
if (ctx.pollInterval) {
|
|
77499
|
+
clearInterval(ctx.pollInterval);
|
|
77500
|
+
ctx.pollInterval = undefined;
|
|
77501
|
+
}
|
|
77502
|
+
}
|
|
77503
|
+
async function onHermesMessage(ctx, raw) {
|
|
77504
|
+
let req;
|
|
77505
|
+
try {
|
|
77506
|
+
req = JSON.parse(raw);
|
|
77507
|
+
} catch {
|
|
77508
|
+
sendResponse(ctx, rpcErr(null, -32700, "Parse error"));
|
|
77509
|
+
return;
|
|
77510
|
+
}
|
|
77511
|
+
if (!req.method || typeof req.method !== "string") {
|
|
77512
|
+
sendResponse(ctx, rpcErr(req.id ?? null, -32600, "Invalid request"));
|
|
77513
|
+
return;
|
|
77514
|
+
}
|
|
77515
|
+
const id = req.id ?? null;
|
|
77516
|
+
const params = req.params ?? {};
|
|
77517
|
+
const config = ctx.config;
|
|
77518
|
+
switch (req.method) {
|
|
77519
|
+
case "session.list": {
|
|
77520
|
+
const agents = await handleGetAgents(config);
|
|
77521
|
+
const sessions = agents.map((a) => toHermesSession(a, agentLiveness(config, a.name)));
|
|
77522
|
+
sendResponse(ctx, rpcOk(id, { sessions }));
|
|
77523
|
+
break;
|
|
77524
|
+
}
|
|
77525
|
+
case "session.most_recent": {
|
|
77526
|
+
const agents = await handleGetAgents(config);
|
|
77527
|
+
if (agents.length === 0) {
|
|
77528
|
+
sendResponse(ctx, rpcOk(id, { session: null }));
|
|
77529
|
+
break;
|
|
77530
|
+
}
|
|
77531
|
+
const a = agents[0];
|
|
77532
|
+
sendResponse(ctx, rpcOk(id, { session: toHermesSession(a, agentLiveness(config, a.name)) }));
|
|
77533
|
+
break;
|
|
77534
|
+
}
|
|
77535
|
+
case "session.status": {
|
|
77536
|
+
const sessionId = String(params.session_id ?? "");
|
|
77537
|
+
if (!config.agents?.[sessionId]) {
|
|
77538
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77539
|
+
break;
|
|
77540
|
+
}
|
|
77541
|
+
const agents = await handleGetAgents(config);
|
|
77542
|
+
const agent = agents.find((a) => a.name === sessionId);
|
|
77543
|
+
if (!agent) {
|
|
77544
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77545
|
+
break;
|
|
77546
|
+
}
|
|
77547
|
+
sendResponse(ctx, rpcOk(id, { session: toHermesSession(agent, agentLiveness(config, sessionId)) }));
|
|
77548
|
+
break;
|
|
77549
|
+
}
|
|
77550
|
+
case "session.create":
|
|
77551
|
+
case "session.resume":
|
|
77552
|
+
case "session.activate": {
|
|
77553
|
+
const sessionId = String(params.session_id ?? params.name ?? "");
|
|
77554
|
+
if (!config.agents?.[sessionId]) {
|
|
77555
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77556
|
+
break;
|
|
77557
|
+
}
|
|
77558
|
+
ctx.activeSessionId = sessionId;
|
|
77559
|
+
const agents = await handleGetAgents(config);
|
|
77560
|
+
const agent = agents.find((a) => a.name === sessionId);
|
|
77561
|
+
if (!agent) {
|
|
77562
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77563
|
+
break;
|
|
77564
|
+
}
|
|
77565
|
+
const session = toHermesSession(agent, agentLiveness(config, sessionId));
|
|
77566
|
+
sendResponse(ctx, rpcOk(id, { session }));
|
|
77567
|
+
sendEvent(ctx, "session.info", sessionId, session);
|
|
77568
|
+
break;
|
|
77569
|
+
}
|
|
77570
|
+
case "session.close": {
|
|
77571
|
+
ctx.activeSessionId = undefined;
|
|
77572
|
+
sendResponse(ctx, rpcOk(id, { ok: true }));
|
|
77573
|
+
break;
|
|
77574
|
+
}
|
|
77575
|
+
case "session.history": {
|
|
77576
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
77577
|
+
if (!config.agents?.[sessionId]) {
|
|
77578
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77579
|
+
break;
|
|
77580
|
+
}
|
|
77581
|
+
const limit = typeof params.limit === "number" ? params.limit : 50;
|
|
77582
|
+
const result = handleGetTurns(config, sessionId, Math.min(limit, 200));
|
|
77583
|
+
if (!result.ok) {
|
|
77584
|
+
sendResponse(ctx, rpcErr(id, -32603, result.error ?? "Failed to read history"));
|
|
77585
|
+
break;
|
|
77586
|
+
}
|
|
77587
|
+
sendResponse(ctx, rpcOk(id, { history: result.turns ?? [] }));
|
|
77588
|
+
break;
|
|
77589
|
+
}
|
|
77590
|
+
case "session.usage": {
|
|
77591
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
77592
|
+
if (!config.agents?.[sessionId]) {
|
|
77593
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77594
|
+
break;
|
|
77595
|
+
}
|
|
77596
|
+
const agents = await handleGetAgents(config);
|
|
77597
|
+
const agent = agents.find((a) => a.name === sessionId);
|
|
77598
|
+
sendResponse(ctx, rpcOk(id, {
|
|
77599
|
+
session_id: sessionId,
|
|
77600
|
+
quota: agent?.primaryAccount ? { slot: agent.primaryAccount, state: agent.auth.subscriptionType ?? null } : null
|
|
77601
|
+
}));
|
|
77602
|
+
break;
|
|
77603
|
+
}
|
|
77604
|
+
case "prompt.submit":
|
|
77605
|
+
case "prompt.background": {
|
|
77606
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
77607
|
+
const content = String(params.content ?? params.text ?? "");
|
|
77608
|
+
if (!sessionId || !config.agents?.[sessionId]) {
|
|
77609
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77610
|
+
break;
|
|
77611
|
+
}
|
|
77612
|
+
if (!content) {
|
|
77613
|
+
sendResponse(ctx, rpcErr(id, -32602, "content is required"));
|
|
77614
|
+
break;
|
|
77615
|
+
}
|
|
77616
|
+
const agentsDir = resolveAgentsDir(config);
|
|
77617
|
+
const chat = resolveAgentChat(config, sessionId, agentsDir);
|
|
77618
|
+
if (!chat) {
|
|
77619
|
+
sendResponse(ctx, rpcErr(id, -32603, `Could not resolve chat for ${sessionId} \u2014 ensure the agent has telegram.forum_chat_id or channels.telegram.chat_id configured.`));
|
|
77620
|
+
break;
|
|
77621
|
+
}
|
|
77622
|
+
const promptKey = createHash10("sha256").update(`${sessionId}:${Date.now()}:${content}`).digest("hex").slice(0, 12);
|
|
77623
|
+
sendEvent(ctx, "message.start", sessionId, { prompt_key: promptKey });
|
|
77624
|
+
const result = await injectInbound(agentsDir, sessionId, chat.chatId, chat.threadId, content, promptKey);
|
|
77625
|
+
if (!result.ok) {
|
|
77626
|
+
sendEvent(ctx, "error", sessionId, { message: result.error, prompt_key: promptKey });
|
|
77627
|
+
sendResponse(ctx, rpcErr(id, -32603, result.error ?? "inject failed"));
|
|
77628
|
+
break;
|
|
77629
|
+
}
|
|
77630
|
+
sendEvent(ctx, "message.complete", sessionId, {
|
|
77631
|
+
prompt_key: promptKey,
|
|
77632
|
+
note: "reply delivered to Telegram thread"
|
|
77633
|
+
});
|
|
77634
|
+
sendResponse(ctx, rpcOk(id, { ok: true, prompt_key: promptKey }));
|
|
77635
|
+
break;
|
|
77636
|
+
}
|
|
77637
|
+
case "session.interrupt": {
|
|
77638
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
77639
|
+
if (!sessionId || !config.agents?.[sessionId]) {
|
|
77640
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
77641
|
+
break;
|
|
77642
|
+
}
|
|
77643
|
+
const agentsDir = resolveAgentsDir(config);
|
|
77644
|
+
const chat = resolveAgentChat(config, sessionId, agentsDir);
|
|
77645
|
+
if (!chat) {
|
|
77646
|
+
sendResponse(ctx, rpcErr(id, -32603, `Cannot resolve chat for ${sessionId}`));
|
|
77647
|
+
break;
|
|
77648
|
+
}
|
|
77649
|
+
const promptKey = `interrupt-${Date.now()}`;
|
|
77650
|
+
const intResult = await injectInbound(agentsDir, sessionId, chat.chatId, chat.threadId, "! ", promptKey);
|
|
77651
|
+
if (!intResult.ok) {
|
|
77652
|
+
sendResponse(ctx, rpcErr(id, -32603, intResult.error ?? "interrupt inject failed"));
|
|
77653
|
+
break;
|
|
77654
|
+
}
|
|
77655
|
+
sendResponse(ctx, rpcOk(id, { ok: true }));
|
|
77656
|
+
break;
|
|
77657
|
+
}
|
|
77658
|
+
case "approval.request":
|
|
77659
|
+
case "approval.respond":
|
|
77660
|
+
case "sudo.request":
|
|
77661
|
+
case "sudo.respond":
|
|
77662
|
+
case "secret.get":
|
|
77663
|
+
case "secret.set":
|
|
77664
|
+
sendResponse(ctx, rpcErr(id, -32601, `Method not implemented: ${req.method}`));
|
|
77665
|
+
break;
|
|
77666
|
+
default:
|
|
77667
|
+
sendResponse(ctx, rpcErr(id, -32601, `Unknown method: ${req.method}`));
|
|
77668
|
+
}
|
|
77669
|
+
}
|
|
77670
|
+
|
|
77289
77671
|
// src/web/server.ts
|
|
77290
77672
|
var LOG_POLL_INTERVAL_MS = 3000;
|
|
77291
77673
|
var LOG_POLL_TAIL_LINES = 400;
|
|
@@ -77337,9 +77719,9 @@ function resolveWebToken() {
|
|
|
77337
77719
|
if (fromEnv && fromEnv.length > 0)
|
|
77338
77720
|
return fromEnv;
|
|
77339
77721
|
const home2 = process.env.HOME ?? homedir26();
|
|
77340
|
-
const tokenPath =
|
|
77341
|
-
if (
|
|
77342
|
-
const existing =
|
|
77722
|
+
const tokenPath = join49(home2, ".switchroom", "web-token");
|
|
77723
|
+
if (existsSync52(tokenPath)) {
|
|
77724
|
+
const existing = readFileSync48(tokenPath, "utf8").trim();
|
|
77343
77725
|
if (existing.length > 0)
|
|
77344
77726
|
return existing;
|
|
77345
77727
|
}
|
|
@@ -77356,7 +77738,7 @@ function resolveWebToken() {
|
|
|
77356
77738
|
return token;
|
|
77357
77739
|
} catch (err) {
|
|
77358
77740
|
if (err.code === "EEXIST") {
|
|
77359
|
-
const existing =
|
|
77741
|
+
const existing = readFileSync48(tokenPath, "utf8").trim();
|
|
77360
77742
|
if (existing.length > 0)
|
|
77361
77743
|
return existing;
|
|
77362
77744
|
}
|
|
@@ -77422,11 +77804,11 @@ function checkWsAuth(req, token, server) {
|
|
|
77422
77804
|
return presented !== null && constantTimeEqual(presented, token);
|
|
77423
77805
|
}
|
|
77424
77806
|
function loadWebhookSecrets() {
|
|
77425
|
-
const path4 =
|
|
77426
|
-
if (!
|
|
77807
|
+
const path4 = join49(homedir26(), ".switchroom", "webhook-secrets.json");
|
|
77808
|
+
if (!existsSync52(path4))
|
|
77427
77809
|
return {};
|
|
77428
77810
|
try {
|
|
77429
|
-
const parsed = JSON.parse(
|
|
77811
|
+
const parsed = JSON.parse(readFileSync48(path4, "utf-8"));
|
|
77430
77812
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
77431
77813
|
} catch (err) {
|
|
77432
77814
|
process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
|
|
@@ -77604,8 +77986,8 @@ function parseRoute(pathname, method) {
|
|
|
77604
77986
|
return null;
|
|
77605
77987
|
}
|
|
77606
77988
|
function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
77607
|
-
const uiDirRaw =
|
|
77608
|
-
const uiDir =
|
|
77989
|
+
const uiDirRaw = resolve30(import.meta.dirname, "ui");
|
|
77990
|
+
const uiDir = existsSync52(uiDirRaw) ? realpathSync4(uiDirRaw) : uiDirRaw;
|
|
77609
77991
|
const token = resolveWebToken();
|
|
77610
77992
|
const freshConfig = () => {
|
|
77611
77993
|
if (!configPath)
|
|
@@ -77653,6 +78035,21 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
77653
78035
|
}
|
|
77654
78036
|
return;
|
|
77655
78037
|
}
|
|
78038
|
+
if (pathname === "/api/ws") {
|
|
78039
|
+
if (!checkWsAuth(req, token, server2)) {
|
|
78040
|
+
return new Response("Unauthorized", { status: 401 });
|
|
78041
|
+
}
|
|
78042
|
+
const wsProto = req.headers.get("Sec-WebSocket-Protocol");
|
|
78043
|
+
const echoBearer = wsProto && wsProto.split(",").map((s) => s.trim()).includes("bearer");
|
|
78044
|
+
const upgraded = server2.upgrade(req, {
|
|
78045
|
+
headers: echoBearer ? { "Sec-WebSocket-Protocol": "bearer" } : undefined,
|
|
78046
|
+
data: { hermesWs: true }
|
|
78047
|
+
});
|
|
78048
|
+
if (!upgraded) {
|
|
78049
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
78050
|
+
}
|
|
78051
|
+
return;
|
|
78052
|
+
}
|
|
77656
78053
|
const route = parseRoute(pathname, req.method);
|
|
77657
78054
|
if (route) {
|
|
77658
78055
|
const authError = checkAuth(req, token, server2);
|
|
@@ -77871,9 +78268,21 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
77871
78268
|
}
|
|
77872
78269
|
}
|
|
77873
78270
|
}
|
|
78271
|
+
if (pathname.startsWith("/api/")) {
|
|
78272
|
+
const authError = checkAuth(req, token, server2);
|
|
78273
|
+
if (authError)
|
|
78274
|
+
return authError;
|
|
78275
|
+
return (async () => {
|
|
78276
|
+
const hermesResult = await handleHermesRest(req.method, pathname, config);
|
|
78277
|
+
if (hermesResult !== null) {
|
|
78278
|
+
return jsonResponse(hermesResult.body, hermesResult.status);
|
|
78279
|
+
}
|
|
78280
|
+
return new Response("Not Found", { status: 404 });
|
|
78281
|
+
})();
|
|
78282
|
+
}
|
|
77874
78283
|
const filePath = resolveDashboardFilePath(pathname);
|
|
77875
|
-
const fullPath =
|
|
77876
|
-
if (!
|
|
78284
|
+
const fullPath = join49(uiDir, filePath);
|
|
78285
|
+
if (!existsSync52(fullPath)) {
|
|
77877
78286
|
return new Response("Not Found", { status: 404 });
|
|
77878
78287
|
}
|
|
77879
78288
|
let realFullPath;
|
|
@@ -77883,12 +78292,12 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
77883
78292
|
return new Response("Not Found", { status: 404 });
|
|
77884
78293
|
}
|
|
77885
78294
|
const rel = relative(uiDir, realFullPath);
|
|
77886
|
-
if (rel.startsWith("..") ||
|
|
78295
|
+
if (rel.startsWith("..") || resolve30(uiDir, rel) !== realFullPath) {
|
|
77887
78296
|
return new Response("Forbidden", { status: 403 });
|
|
77888
78297
|
}
|
|
77889
78298
|
const ext = extname(realFullPath);
|
|
77890
78299
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
77891
|
-
const content =
|
|
78300
|
+
const content = readFileSync48(realFullPath);
|
|
77892
78301
|
const headers = { "Content-Type": contentType };
|
|
77893
78302
|
const cacheControl = dashboardCacheControl(ext);
|
|
77894
78303
|
if (cacheControl)
|
|
@@ -77896,8 +78305,26 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
77896
78305
|
return new Response(content, { headers });
|
|
77897
78306
|
},
|
|
77898
78307
|
websocket: {
|
|
77899
|
-
open(
|
|
78308
|
+
open(ws) {
|
|
78309
|
+
if (ws.data?.hermesWs) {
|
|
78310
|
+
const ctx = {
|
|
78311
|
+
config,
|
|
78312
|
+
send: (msg) => {
|
|
78313
|
+
try {
|
|
78314
|
+
ws.send(msg);
|
|
78315
|
+
} catch {}
|
|
78316
|
+
}
|
|
78317
|
+
};
|
|
78318
|
+
ws._hermesCtx = ctx;
|
|
78319
|
+
onHermesOpen(ctx);
|
|
78320
|
+
}
|
|
78321
|
+
},
|
|
77900
78322
|
close(ws) {
|
|
78323
|
+
if (ws._hermesCtx) {
|
|
78324
|
+
onHermesClose(ws._hermesCtx);
|
|
78325
|
+
delete ws._hermesCtx;
|
|
78326
|
+
return;
|
|
78327
|
+
}
|
|
77901
78328
|
const interval = ws._logInterval;
|
|
77902
78329
|
if (interval) {
|
|
77903
78330
|
clearInterval(interval);
|
|
@@ -77905,6 +78332,10 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
77905
78332
|
}
|
|
77906
78333
|
},
|
|
77907
78334
|
message(ws, message) {
|
|
78335
|
+
if (ws._hermesCtx) {
|
|
78336
|
+
onHermesMessage(ws._hermesCtx, String(message));
|
|
78337
|
+
return;
|
|
78338
|
+
}
|
|
77908
78339
|
let data;
|
|
77909
78340
|
try {
|
|
77910
78341
|
data = JSON.parse(String(message));
|
|
@@ -78039,8 +78470,8 @@ Starting Switchroom dashboard...
|
|
|
78039
78470
|
// src/cli/setup.ts
|
|
78040
78471
|
init_source();
|
|
78041
78472
|
init_loader();
|
|
78042
|
-
import { existsSync as
|
|
78043
|
-
import { resolve as
|
|
78473
|
+
import { existsSync as existsSync53, copyFileSync as copyFileSync8, readFileSync as readFileSync49, writeFileSync as writeFileSync27, mkdirSync as mkdirSync30 } from "node:fs";
|
|
78474
|
+
import { resolve as resolve31, dirname as dirname13 } from "node:path";
|
|
78044
78475
|
init_vault();
|
|
78045
78476
|
init_manager();
|
|
78046
78477
|
init_client();
|
|
@@ -78257,7 +78688,7 @@ async function stepConfigFile(configPath, nonInteractive) {
|
|
|
78257
78688
|
existingConfig = null;
|
|
78258
78689
|
}
|
|
78259
78690
|
}
|
|
78260
|
-
if (existingConfig &&
|
|
78691
|
+
if (existingConfig && existsSync53(existingConfig)) {
|
|
78261
78692
|
if (!nonInteractive) {
|
|
78262
78693
|
const useExisting = await askYesNo(` Found ${source_default.cyan(existingConfig)}. Use it?`, true);
|
|
78263
78694
|
if (!useExisting) {
|
|
@@ -78269,14 +78700,14 @@ async function stepConfigFile(configPath, nonInteractive) {
|
|
|
78269
78700
|
console.log(source_default.green(` ${STEP_DONE} Config loaded`) + source_default.gray(` (${Object.keys(config.agents).length} agents)`));
|
|
78270
78701
|
const hasExplicitTimezone = config.switchroom?.timezone !== undefined || config.defaults?.timezone !== undefined;
|
|
78271
78702
|
if (!hasExplicitTimezone) {
|
|
78272
|
-
await writeDetectedTimezone(
|
|
78703
|
+
await writeDetectedTimezone(resolve31(existingConfig), nonInteractive);
|
|
78273
78704
|
}
|
|
78274
|
-
return { config, configPath:
|
|
78705
|
+
return { config, configPath: resolve31(existingConfig) };
|
|
78275
78706
|
}
|
|
78276
78707
|
return await copyExampleConfig(nonInteractive);
|
|
78277
78708
|
}
|
|
78278
78709
|
async function copyExampleConfig(nonInteractive) {
|
|
78279
|
-
const examplesDir =
|
|
78710
|
+
const examplesDir = resolve31(import.meta.dirname, "../../examples");
|
|
78280
78711
|
let choice;
|
|
78281
78712
|
if (nonInteractive) {
|
|
78282
78713
|
choice = "switchroom";
|
|
@@ -78287,9 +78718,9 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
78287
78718
|
]);
|
|
78288
78719
|
choice = choice.split(" ")[0];
|
|
78289
78720
|
}
|
|
78290
|
-
const srcFile =
|
|
78721
|
+
const srcFile = resolve31(examplesDir, `${choice}.yaml`);
|
|
78291
78722
|
const destFile = resolvePath("~/.switchroom/switchroom.yaml");
|
|
78292
|
-
if (!
|
|
78723
|
+
if (!existsSync53(srcFile)) {
|
|
78293
78724
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
78294
78725
|
}
|
|
78295
78726
|
mkdirSync30(dirname13(destFile), { recursive: true });
|
|
@@ -78299,12 +78730,12 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
78299
78730
|
await writeDetectedTimezone(destFile, nonInteractive);
|
|
78300
78731
|
const config = loadConfig(destFile);
|
|
78301
78732
|
console.log(source_default.green(` ${STEP_DONE} Config loaded`) + source_default.gray(` (${Object.keys(config.agents).length} agents)`));
|
|
78302
|
-
return { config, configPath:
|
|
78733
|
+
return { config, configPath: resolve31(destFile) };
|
|
78303
78734
|
}
|
|
78304
78735
|
async function writeDetectedTimezone(destFile, nonInteractive, detect = detectServerTimezone, prompt = ask) {
|
|
78305
78736
|
const detected = detect();
|
|
78306
78737
|
if (detected !== undefined && detected !== "UTC" && isValidTimezone(detected)) {
|
|
78307
|
-
const before2 =
|
|
78738
|
+
const before2 = readFileSync49(destFile, "utf-8");
|
|
78308
78739
|
const after2 = setSwitchroomTimezone(before2, detected);
|
|
78309
78740
|
if (after2 !== before2)
|
|
78310
78741
|
writeFileSync27(destFile, after2);
|
|
@@ -78325,7 +78756,7 @@ async function writeDetectedTimezone(destFile, nonInteractive, detect = detectSe
|
|
|
78325
78756
|
console.error(source_default.yellow(` \u26a0 "${zone}" is not a valid IANA zone (expected Region/City like ` + '"Australia/Melbourne"). Skipping \u2014 agents will use UTC. ' + "Edit switchroom.timezone in switchroom.yaml by hand."));
|
|
78326
78757
|
return;
|
|
78327
78758
|
}
|
|
78328
|
-
const before =
|
|
78759
|
+
const before = readFileSync49(destFile, "utf-8");
|
|
78329
78760
|
const after = setSwitchroomTimezone(before, zone);
|
|
78330
78761
|
if (after !== before)
|
|
78331
78762
|
writeFileSync27(destFile, after);
|
|
@@ -78404,7 +78835,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
|
|
|
78404
78835
|
try {
|
|
78405
78836
|
const { openVault: openVault2 } = await Promise.resolve().then(() => (init_vault(), exports_vault));
|
|
78406
78837
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
78407
|
-
if (
|
|
78838
|
+
if (existsSync53(vaultPath)) {
|
|
78408
78839
|
const secrets = openVault2(passphrase, vaultPath);
|
|
78409
78840
|
const key = rawToken.replace("vault:", "");
|
|
78410
78841
|
const entry = secrets[key];
|
|
@@ -78432,7 +78863,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
|
|
|
78432
78863
|
async function storeTokenInVault(config, vaultRef, token) {
|
|
78433
78864
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
78434
78865
|
const key = vaultRef.replace("vault:", "");
|
|
78435
|
-
if (!
|
|
78866
|
+
if (!existsSync53(vaultPath)) {
|
|
78436
78867
|
console.log(source_default.gray(" Creating encrypted vault..."));
|
|
78437
78868
|
let passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
78438
78869
|
if (!passphrase) {
|
|
@@ -78614,7 +79045,7 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
|
|
|
78614
79045
|
try {
|
|
78615
79046
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
78616
79047
|
const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
78617
|
-
if (passphrase &&
|
|
79048
|
+
if (passphrase && existsSync53(vaultPath)) {
|
|
78618
79049
|
const existing = getStringSecret(passphrase, vaultPath, "hindsight-api-key");
|
|
78619
79050
|
if (existing) {
|
|
78620
79051
|
console.log(source_default.gray(" Note: legacy 'hindsight-api-key' is in your vault but is no longer used. You can remove it with `switchroom vault rm hindsight-api-key`."));
|
|
@@ -78778,13 +79209,13 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
|
|
|
78778
79209
|
return;
|
|
78779
79210
|
}
|
|
78780
79211
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
78781
|
-
if (!
|
|
79212
|
+
if (!existsSync53(vaultPath)) {
|
|
78782
79213
|
console.log(source_default.gray(" Skipping (vault not created yet)."));
|
|
78783
79214
|
return;
|
|
78784
79215
|
}
|
|
78785
79216
|
const credPathRaw = config.vault?.broker?.autoUnlockCredentialPath ?? "~/.switchroom/vault-auto-unlock";
|
|
78786
79217
|
const credPath = resolvePath(credPathRaw);
|
|
78787
|
-
if (config.vault?.broker?.autoUnlock === true &&
|
|
79218
|
+
if (config.vault?.broker?.autoUnlock === true && existsSync53(credPath)) {
|
|
78788
79219
|
console.log(source_default.green(` ${STEP_DONE} Already configured (${credPath})`));
|
|
78789
79220
|
return;
|
|
78790
79221
|
}
|
|
@@ -78850,9 +79281,9 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
|
|
|
78850
79281
|
const choice = await askChoice(" Approval posture", [PASSPHRASE_CHOICE, TELEGRAM_ID_CHOICE]);
|
|
78851
79282
|
if (choice === TELEGRAM_ID_CHOICE) {
|
|
78852
79283
|
try {
|
|
78853
|
-
const yamlPath =
|
|
78854
|
-
if (
|
|
78855
|
-
const content =
|
|
79284
|
+
const yamlPath = existsSync53(resolve31(process.cwd(), "switchroom.yaml")) ? resolve31(process.cwd(), "switchroom.yaml") : resolve31(process.cwd(), "switchroom.yml");
|
|
79285
|
+
if (existsSync53(yamlPath)) {
|
|
79286
|
+
const content = readFileSync49(yamlPath, "utf-8");
|
|
78856
79287
|
const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
|
|
78857
79288
|
if (result.kind === "rewritten") {
|
|
78858
79289
|
writeFileSync27(yamlPath, result.content, "utf-8");
|
|
@@ -78882,12 +79313,12 @@ async function stepDangerousMode(config, nonInteractive) {
|
|
|
78882
79313
|
}
|
|
78883
79314
|
if (enableDangerous) {
|
|
78884
79315
|
const configPaths = [
|
|
78885
|
-
|
|
78886
|
-
|
|
79316
|
+
resolve31(process.cwd(), "switchroom.yaml"),
|
|
79317
|
+
resolve31(process.cwd(), "switchroom.yml")
|
|
78887
79318
|
];
|
|
78888
79319
|
for (const configPath of configPaths) {
|
|
78889
|
-
if (
|
|
78890
|
-
let content =
|
|
79320
|
+
if (existsSync53(configPath)) {
|
|
79321
|
+
let content = readFileSync49(configPath, "utf-8");
|
|
78891
79322
|
const agentNames = Object.keys(config.agents);
|
|
78892
79323
|
for (const name of agentNames) {
|
|
78893
79324
|
const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
|
|
@@ -78917,7 +79348,7 @@ async function stepOnboardingGuidance(config, nonInteractive) {
|
|
|
78917
79348
|
const agentNames = Object.keys(config.agents);
|
|
78918
79349
|
let allAuthenticated = true;
|
|
78919
79350
|
for (const name of agentNames) {
|
|
78920
|
-
const agentDir =
|
|
79351
|
+
const agentDir = resolve31(agentsDir, name);
|
|
78921
79352
|
const status = getAuthStatus(name, agentDir);
|
|
78922
79353
|
if (status.authenticated) {
|
|
78923
79354
|
console.log(` ${source_default.green("OK")} ${source_default.bold(name)}` + source_default.gray(` - authenticated (expires: ${status.timeUntilExpiry ?? "unknown"})`));
|
|
@@ -79015,9 +79446,9 @@ init_doctor();
|
|
|
79015
79446
|
init_source();
|
|
79016
79447
|
init_loader();
|
|
79017
79448
|
init_lifecycle();
|
|
79018
|
-
import { cpSync as cpSync2, existsSync as
|
|
79449
|
+
import { cpSync as cpSync2, existsSync as existsSync60, mkdirSync as mkdirSync32, readFileSync as readFileSync54, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync27, chownSync as chownSync5 } from "node:fs";
|
|
79019
79450
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
79020
|
-
import { join as
|
|
79451
|
+
import { join as join63, dirname as dirname16, resolve as resolve36 } from "node:path";
|
|
79021
79452
|
import { homedir as homedir37 } from "node:os";
|
|
79022
79453
|
|
|
79023
79454
|
// src/cli/release-yaml.ts
|
|
@@ -79109,7 +79540,7 @@ ${lines.join(`
|
|
|
79109
79540
|
function defaultPersistPin(configPath) {
|
|
79110
79541
|
return (pin) => {
|
|
79111
79542
|
const path4 = configPath ?? findConfigFile();
|
|
79112
|
-
const before =
|
|
79543
|
+
const before = readFileSync54(path4, "utf8");
|
|
79113
79544
|
const after = setReleasePinInConfig(before, pin);
|
|
79114
79545
|
if (after === before)
|
|
79115
79546
|
return;
|
|
@@ -79123,13 +79554,13 @@ function defaultPersistPin(configPath) {
|
|
|
79123
79554
|
} catch {}
|
|
79124
79555
|
};
|
|
79125
79556
|
}
|
|
79126
|
-
var DEFAULT_COMPOSE_PATH =
|
|
79557
|
+
var DEFAULT_COMPOSE_PATH = join63(homedir37(), ".switchroom", "compose", "docker-compose.yml");
|
|
79127
79558
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
79128
79559
|
let dir = dirname16(scriptPath);
|
|
79129
79560
|
for (let i = 0;i < 12; i++) {
|
|
79130
|
-
if (
|
|
79561
|
+
if (existsSync60(join63(dir, ".git"))) {
|
|
79131
79562
|
try {
|
|
79132
|
-
const pkg = JSON.parse(
|
|
79563
|
+
const pkg = JSON.parse(readFileSync54(join63(dir, "package.json"), "utf-8"));
|
|
79133
79564
|
if (pkg.name === "switchroom")
|
|
79134
79565
|
return true;
|
|
79135
79566
|
} catch {}
|
|
@@ -79198,7 +79629,7 @@ function planUpdate(opts) {
|
|
|
79198
79629
|
steps.push({
|
|
79199
79630
|
name: "pull-images",
|
|
79200
79631
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
79201
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
79632
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync60(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
79202
79633
|
run: () => {
|
|
79203
79634
|
const r = runner("docker", [
|
|
79204
79635
|
"compose",
|
|
@@ -79318,15 +79749,15 @@ function planUpdate(opts) {
|
|
|
79318
79749
|
opts.syncBundledSkillsFn();
|
|
79319
79750
|
return;
|
|
79320
79751
|
}
|
|
79321
|
-
const source =
|
|
79322
|
-
const dest =
|
|
79323
|
-
if (!
|
|
79752
|
+
const source = resolve36(import.meta.dirname, "../../skills");
|
|
79753
|
+
const dest = join63(homedir37(), ".switchroom", "skills", "_bundled");
|
|
79754
|
+
if (!existsSync60(source)) {
|
|
79324
79755
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
79325
79756
|
`);
|
|
79326
79757
|
return;
|
|
79327
79758
|
}
|
|
79328
79759
|
try {
|
|
79329
|
-
if (
|
|
79760
|
+
if (existsSync60(dest)) {
|
|
79330
79761
|
rmSync12(dest, { recursive: true, force: true });
|
|
79331
79762
|
}
|
|
79332
79763
|
mkdirSync32(dirname16(dest), { recursive: true });
|
|
@@ -79365,7 +79796,7 @@ function planUpdate(opts) {
|
|
|
79365
79796
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
79366
79797
|
run: () => {
|
|
79367
79798
|
try {
|
|
79368
|
-
const composeText =
|
|
79799
|
+
const composeText = readFileSync54(composePath, "utf8");
|
|
79369
79800
|
const pf = validateBindSources(composeText);
|
|
79370
79801
|
if (!pf.ok)
|
|
79371
79802
|
throw new Error(formatPreflightError(pf));
|
|
@@ -79443,10 +79874,10 @@ function defaultStatusProbe(composePath) {
|
|
|
79443
79874
|
} catch {}
|
|
79444
79875
|
let dir = dirname16(scriptPath);
|
|
79445
79876
|
for (let i = 0;i < 8; i++) {
|
|
79446
|
-
const pkgPath =
|
|
79447
|
-
if (
|
|
79877
|
+
const pkgPath = join63(dir, "package.json");
|
|
79878
|
+
if (existsSync60(pkgPath)) {
|
|
79448
79879
|
try {
|
|
79449
|
-
const pkg = JSON.parse(
|
|
79880
|
+
const pkg = JSON.parse(readFileSync54(pkgPath, "utf-8"));
|
|
79450
79881
|
if (typeof pkg.version === "string")
|
|
79451
79882
|
cliVersion = pkg.version;
|
|
79452
79883
|
} catch (err) {
|
|
@@ -79467,7 +79898,7 @@ function defaultStatusProbe(composePath) {
|
|
|
79467
79898
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
79468
79899
|
}
|
|
79469
79900
|
const services = [];
|
|
79470
|
-
if (!
|
|
79901
|
+
if (!existsSync60(composePath)) {
|
|
79471
79902
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
79472
79903
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
79473
79904
|
}
|
|
@@ -79663,7 +80094,7 @@ function registerUpdateCommand(program3) {
|
|
|
79663
80094
|
// src/cli/rollout.ts
|
|
79664
80095
|
init_helpers();
|
|
79665
80096
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
79666
|
-
import { readFileSync as
|
|
80097
|
+
import { readFileSync as readFileSync55, chownSync as chownSync6, statSync as statSync28 } from "node:fs";
|
|
79667
80098
|
import { homedir as homedir38 } from "node:os";
|
|
79668
80099
|
init_atomic();
|
|
79669
80100
|
init_audit_reader();
|
|
@@ -79847,7 +80278,7 @@ function resolveRollbackTarget(auditLogPath) {
|
|
|
79847
80278
|
const logPath = auditLogPath ?? defaultAuditLogPath2(homedir38());
|
|
79848
80279
|
let raw;
|
|
79849
80280
|
try {
|
|
79850
|
-
raw =
|
|
80281
|
+
raw = readFileSync55(logPath, "utf8");
|
|
79851
80282
|
} catch {
|
|
79852
80283
|
return null;
|
|
79853
80284
|
}
|
|
@@ -79959,7 +80390,7 @@ function registerRolloutCommand(program3) {
|
|
|
79959
80390
|
log: (line) => process.stdout.write(line + `
|
|
79960
80391
|
`),
|
|
79961
80392
|
persistPin: (pin) => {
|
|
79962
|
-
const before =
|
|
80393
|
+
const before = readFileSync55(configPath, "utf8");
|
|
79963
80394
|
const after = setReleasePinInConfig(before, pin);
|
|
79964
80395
|
if (after === before)
|
|
79965
80396
|
return;
|
|
@@ -80024,15 +80455,15 @@ init_source();
|
|
|
80024
80455
|
init_helpers();
|
|
80025
80456
|
init_loader();
|
|
80026
80457
|
init_lifecycle();
|
|
80027
|
-
import { resolve as
|
|
80458
|
+
import { resolve as resolve37 } from "node:path";
|
|
80028
80459
|
|
|
80029
80460
|
// src/cli/version.ts
|
|
80030
80461
|
init_source();
|
|
80031
80462
|
init_helpers();
|
|
80032
80463
|
init_lifecycle();
|
|
80033
80464
|
import { execSync as execSync4 } from "node:child_process";
|
|
80034
|
-
import { existsSync as
|
|
80035
|
-
import { dirname as dirname17, join as
|
|
80465
|
+
import { existsSync as existsSync61, readFileSync as readFileSync56 } from "node:fs";
|
|
80466
|
+
import { dirname as dirname17, join as join64 } from "node:path";
|
|
80036
80467
|
function getClaudeCodeVersion() {
|
|
80037
80468
|
try {
|
|
80038
80469
|
const out = execSync4("claude --version 2>/dev/null", {
|
|
@@ -80082,11 +80513,11 @@ function formatUptime3(timestamp) {
|
|
|
80082
80513
|
function locateSwitchroomInstallDir() {
|
|
80083
80514
|
let dir = import.meta.dirname;
|
|
80084
80515
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
80085
|
-
const pkgPath =
|
|
80086
|
-
if (
|
|
80516
|
+
const pkgPath = join64(dir, "package.json");
|
|
80517
|
+
if (existsSync61(pkgPath)) {
|
|
80087
80518
|
try {
|
|
80088
|
-
const pkg = JSON.parse(
|
|
80089
|
-
if (pkg.name === "switchroom" &&
|
|
80519
|
+
const pkg = JSON.parse(readFileSync56(pkgPath, "utf-8"));
|
|
80520
|
+
if (pkg.name === "switchroom" && existsSync61(join64(dir, ".git"))) {
|
|
80090
80521
|
return dir;
|
|
80091
80522
|
}
|
|
80092
80523
|
} catch {}
|
|
@@ -80179,7 +80610,7 @@ function registerRestartCommand(program3) {
|
|
|
80179
80610
|
}
|
|
80180
80611
|
const didRestart = res.restarted || !graceful;
|
|
80181
80612
|
if (didRestart) {
|
|
80182
|
-
const agentDir =
|
|
80613
|
+
const agentDir = resolve37(agentsDir, name);
|
|
80183
80614
|
const converged = waitForAuthConverge(name, agentDir);
|
|
80184
80615
|
if (!converged) {
|
|
80185
80616
|
console.log(source_default.yellow(` ${name}: agent is up but auth status didn't converge in 30s \u2014 check logs`));
|
|
@@ -80260,7 +80691,7 @@ Dependency manifest`));
|
|
|
80260
80691
|
// src/cli/handoff.ts
|
|
80261
80692
|
init_helpers();
|
|
80262
80693
|
init_loader();
|
|
80263
|
-
import { resolve as
|
|
80694
|
+
import { resolve as resolve38 } from "node:path";
|
|
80264
80695
|
function registerHandoffCommand(program3) {
|
|
80265
80696
|
program3.command("handoff <agent>", { hidden: true }).description("Build the agent's session handoff sidecars \u2014 a transcript-tail " + "briefing (.handoff.md) and topic line (.handoff-topic). " + "[internal \u2014 used by the Stop hook]").option("--max-turns <n>", "Max turns kept in the handoff transcript tail", String(DEFAULT_MAX_TURNS)).action(withConfigError(async (agentName, opts) => {
|
|
80266
80697
|
let agentConfig;
|
|
@@ -80274,7 +80705,7 @@ function registerHandoffCommand(program3) {
|
|
|
80274
80705
|
return;
|
|
80275
80706
|
}
|
|
80276
80707
|
const agentsDir = resolveAgentsDir(config);
|
|
80277
|
-
agentDir =
|
|
80708
|
+
agentDir = resolve38(agentsDir, agentName);
|
|
80278
80709
|
} catch (err) {
|
|
80279
80710
|
if (!(err instanceof ConfigError))
|
|
80280
80711
|
throw err;
|
|
@@ -80289,7 +80720,7 @@ function registerHandoffCommand(program3) {
|
|
|
80289
80720
|
`);
|
|
80290
80721
|
return;
|
|
80291
80722
|
}
|
|
80292
|
-
const claudeConfigDir =
|
|
80723
|
+
const claudeConfigDir = resolve38(agentDir, ".claude");
|
|
80293
80724
|
const jsonl = findLatestSessionJsonl(claudeConfigDir);
|
|
80294
80725
|
if (!jsonl) {
|
|
80295
80726
|
process.stderr.write(`handoff: no session JSONL under ${claudeConfigDir}/projects; skipping
|
|
@@ -80312,18 +80743,18 @@ function registerHandoffCommand(program3) {
|
|
|
80312
80743
|
// src/issues/store.ts
|
|
80313
80744
|
import {
|
|
80314
80745
|
closeSync as closeSync11,
|
|
80315
|
-
existsSync as
|
|
80746
|
+
existsSync as existsSync62,
|
|
80316
80747
|
mkdirSync as mkdirSync33,
|
|
80317
80748
|
openSync as openSync11,
|
|
80318
80749
|
readdirSync as readdirSync22,
|
|
80319
|
-
readFileSync as
|
|
80750
|
+
readFileSync as readFileSync57,
|
|
80320
80751
|
renameSync as renameSync12,
|
|
80321
80752
|
statSync as statSync29,
|
|
80322
80753
|
unlinkSync as unlinkSync11,
|
|
80323
80754
|
writeFileSync as writeFileSync28,
|
|
80324
80755
|
writeSync as writeSync7
|
|
80325
80756
|
} from "node:fs";
|
|
80326
|
-
import { join as
|
|
80757
|
+
import { join as join65 } from "node:path";
|
|
80327
80758
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
80328
80759
|
import { execSync as execSync5 } from "node:child_process";
|
|
80329
80760
|
|
|
@@ -80721,12 +81152,12 @@ function redactedMarker(ruleId) {
|
|
|
80721
81152
|
var ISSUES_FILE = "issues.jsonl";
|
|
80722
81153
|
var ISSUES_LOCK = "issues.lock";
|
|
80723
81154
|
function readAll(stateDir) {
|
|
80724
|
-
const path4 =
|
|
80725
|
-
if (!
|
|
81155
|
+
const path4 = join65(stateDir, ISSUES_FILE);
|
|
81156
|
+
if (!existsSync62(path4))
|
|
80726
81157
|
return [];
|
|
80727
81158
|
let raw;
|
|
80728
81159
|
try {
|
|
80729
|
-
raw =
|
|
81160
|
+
raw = readFileSync57(path4, "utf-8");
|
|
80730
81161
|
} catch {
|
|
80731
81162
|
return [];
|
|
80732
81163
|
}
|
|
@@ -80798,8 +81229,8 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
80798
81229
|
return result;
|
|
80799
81230
|
});
|
|
80800
81231
|
}
|
|
80801
|
-
function
|
|
80802
|
-
if (!
|
|
81232
|
+
function resolve39(stateDir, fingerprint, nowFn = Date.now) {
|
|
81233
|
+
if (!existsSync62(join65(stateDir, ISSUES_FILE)))
|
|
80803
81234
|
return 0;
|
|
80804
81235
|
return withLock(stateDir, () => {
|
|
80805
81236
|
const all = readAll(stateDir);
|
|
@@ -80817,7 +81248,7 @@ function resolve38(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
80817
81248
|
});
|
|
80818
81249
|
}
|
|
80819
81250
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
80820
|
-
if (!
|
|
81251
|
+
if (!existsSync62(join65(stateDir, ISSUES_FILE)))
|
|
80821
81252
|
return 0;
|
|
80822
81253
|
return withLock(stateDir, () => {
|
|
80823
81254
|
const all = readAll(stateDir);
|
|
@@ -80835,7 +81266,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
80835
81266
|
});
|
|
80836
81267
|
}
|
|
80837
81268
|
function prune(stateDir, opts = {}) {
|
|
80838
|
-
if (!
|
|
81269
|
+
if (!existsSync62(join65(stateDir, ISSUES_FILE)))
|
|
80839
81270
|
return 0;
|
|
80840
81271
|
return withLock(stateDir, () => {
|
|
80841
81272
|
const all = readAll(stateDir);
|
|
@@ -80868,7 +81299,7 @@ function ensureDir(stateDir) {
|
|
|
80868
81299
|
mkdirSync33(stateDir, { recursive: true });
|
|
80869
81300
|
}
|
|
80870
81301
|
function writeAll(stateDir, events) {
|
|
80871
|
-
const path4 =
|
|
81302
|
+
const path4 = join65(stateDir, ISSUES_FILE);
|
|
80872
81303
|
sweepOrphanTmpFiles(stateDir);
|
|
80873
81304
|
const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
|
|
80874
81305
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -80890,7 +81321,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
80890
81321
|
for (const entry of entries) {
|
|
80891
81322
|
if (!entry.startsWith(TMP_PREFIX))
|
|
80892
81323
|
continue;
|
|
80893
|
-
const tmpPath =
|
|
81324
|
+
const tmpPath = join65(stateDir, entry);
|
|
80894
81325
|
try {
|
|
80895
81326
|
const stat = statSync29(tmpPath);
|
|
80896
81327
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -80902,7 +81333,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
80902
81333
|
var LOCK_RETRY_MS = 25;
|
|
80903
81334
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
80904
81335
|
function withLock(stateDir, fn) {
|
|
80905
|
-
const lockPath =
|
|
81336
|
+
const lockPath = join65(stateDir, ISSUES_LOCK);
|
|
80906
81337
|
const startedAt = Date.now();
|
|
80907
81338
|
let fd = null;
|
|
80908
81339
|
while (fd === null) {
|
|
@@ -80937,7 +81368,7 @@ function withLock(stateDir, fn) {
|
|
|
80937
81368
|
function tryStealStaleLock(lockPath) {
|
|
80938
81369
|
let pidStr;
|
|
80939
81370
|
try {
|
|
80940
|
-
pidStr =
|
|
81371
|
+
pidStr = readFileSync57(lockPath, "utf-8").trim();
|
|
80941
81372
|
} catch {
|
|
80942
81373
|
return true;
|
|
80943
81374
|
}
|
|
@@ -81086,11 +81517,11 @@ function registerIssuesCommand(program3) {
|
|
|
81086
81517
|
const stateDir = resolveStateDir2(opts);
|
|
81087
81518
|
let flipped;
|
|
81088
81519
|
if (opts.source && opts.code) {
|
|
81089
|
-
flipped =
|
|
81520
|
+
flipped = resolve39(stateDir, computeFingerprint(opts.source, opts.code));
|
|
81090
81521
|
} else if (opts.source && !fingerprint) {
|
|
81091
81522
|
flipped = resolveAllBySource(stateDir, opts.source);
|
|
81092
81523
|
} else if (fingerprint) {
|
|
81093
|
-
flipped =
|
|
81524
|
+
flipped = resolve39(stateDir, fingerprint);
|
|
81094
81525
|
} else {
|
|
81095
81526
|
process.stderr.write(`issues resolve: need either <fingerprint>, --source, or --source + --code
|
|
81096
81527
|
`);
|
|
@@ -81185,20 +81616,20 @@ function relTime(deltaMs) {
|
|
|
81185
81616
|
|
|
81186
81617
|
// src/cli/deps.ts
|
|
81187
81618
|
init_source();
|
|
81188
|
-
import { existsSync as
|
|
81619
|
+
import { existsSync as existsSync65 } from "node:fs";
|
|
81189
81620
|
import { homedir as homedir41 } from "node:os";
|
|
81190
|
-
import { join as
|
|
81621
|
+
import { join as join68, resolve as resolve40 } from "node:path";
|
|
81191
81622
|
|
|
81192
81623
|
// src/deps/python.ts
|
|
81193
|
-
import { createHash as
|
|
81624
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
81194
81625
|
import {
|
|
81195
|
-
existsSync as
|
|
81626
|
+
existsSync as existsSync63,
|
|
81196
81627
|
mkdirSync as mkdirSync34,
|
|
81197
|
-
readFileSync as
|
|
81628
|
+
readFileSync as readFileSync58,
|
|
81198
81629
|
rmSync as rmSync13,
|
|
81199
81630
|
writeFileSync as writeFileSync29
|
|
81200
81631
|
} from "node:fs";
|
|
81201
|
-
import { dirname as dirname18, join as
|
|
81632
|
+
import { dirname as dirname18, join as join66 } from "node:path";
|
|
81202
81633
|
import { homedir as homedir39 } from "node:os";
|
|
81203
81634
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
81204
81635
|
|
|
@@ -81211,26 +81642,26 @@ class PythonEnvError extends Error {
|
|
|
81211
81642
|
}
|
|
81212
81643
|
}
|
|
81213
81644
|
function defaultPythonCacheRoot() {
|
|
81214
|
-
return
|
|
81645
|
+
return join66(homedir39(), ".switchroom", "deps", "python");
|
|
81215
81646
|
}
|
|
81216
81647
|
function hashFile(path4) {
|
|
81217
|
-
return
|
|
81648
|
+
return createHash12("sha256").update(readFileSync58(path4)).digest("hex");
|
|
81218
81649
|
}
|
|
81219
81650
|
function ensurePythonEnv(opts) {
|
|
81220
81651
|
const { skillName, requirementsPath, force = false } = opts;
|
|
81221
81652
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
81222
81653
|
const hostPython = opts.pythonBin ?? "python3";
|
|
81223
|
-
if (!
|
|
81654
|
+
if (!existsSync63(requirementsPath)) {
|
|
81224
81655
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
81225
81656
|
}
|
|
81226
|
-
const venvDir =
|
|
81227
|
-
const stampPath =
|
|
81228
|
-
const binDir =
|
|
81229
|
-
const pythonBin =
|
|
81230
|
-
const pipBin =
|
|
81657
|
+
const venvDir = join66(cacheRoot, skillName);
|
|
81658
|
+
const stampPath = join66(venvDir, ".requirements.sha256");
|
|
81659
|
+
const binDir = join66(venvDir, "bin");
|
|
81660
|
+
const pythonBin = join66(binDir, "python");
|
|
81661
|
+
const pipBin = join66(binDir, "pip");
|
|
81231
81662
|
const targetHash = hashFile(requirementsPath);
|
|
81232
|
-
if (!force &&
|
|
81233
|
-
const existingHash =
|
|
81663
|
+
if (!force && existsSync63(stampPath) && existsSync63(pythonBin)) {
|
|
81664
|
+
const existingHash = readFileSync58(stampPath, "utf8").trim();
|
|
81234
81665
|
if (existingHash === targetHash) {
|
|
81235
81666
|
return {
|
|
81236
81667
|
skillName,
|
|
@@ -81242,7 +81673,7 @@ function ensurePythonEnv(opts) {
|
|
|
81242
81673
|
};
|
|
81243
81674
|
}
|
|
81244
81675
|
}
|
|
81245
|
-
if (
|
|
81676
|
+
if (existsSync63(venvDir)) {
|
|
81246
81677
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
81247
81678
|
}
|
|
81248
81679
|
mkdirSync34(dirname18(venvDir), { recursive: true });
|
|
@@ -81277,16 +81708,16 @@ function ensurePythonEnv(opts) {
|
|
|
81277
81708
|
}
|
|
81278
81709
|
|
|
81279
81710
|
// src/deps/node.ts
|
|
81280
|
-
import { createHash as
|
|
81711
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
81281
81712
|
import {
|
|
81282
81713
|
copyFileSync as copyFileSync9,
|
|
81283
|
-
existsSync as
|
|
81714
|
+
existsSync as existsSync64,
|
|
81284
81715
|
mkdirSync as mkdirSync35,
|
|
81285
|
-
readFileSync as
|
|
81716
|
+
readFileSync as readFileSync59,
|
|
81286
81717
|
rmSync as rmSync14,
|
|
81287
81718
|
writeFileSync as writeFileSync30
|
|
81288
81719
|
} from "node:fs";
|
|
81289
|
-
import { dirname as dirname19, join as
|
|
81720
|
+
import { dirname as dirname19, join as join67 } from "node:path";
|
|
81290
81721
|
import { homedir as homedir40 } from "node:os";
|
|
81291
81722
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
81292
81723
|
|
|
@@ -81310,23 +81741,23 @@ var LOCKFILES_FOR = {
|
|
|
81310
81741
|
npm: ["package-lock.json"]
|
|
81311
81742
|
};
|
|
81312
81743
|
function defaultNodeCacheRoot() {
|
|
81313
|
-
return
|
|
81744
|
+
return join67(homedir40(), ".switchroom", "deps", "node");
|
|
81314
81745
|
}
|
|
81315
81746
|
function hashDepInputs(packageJsonPath) {
|
|
81316
81747
|
const sourceDir = dirname19(packageJsonPath);
|
|
81317
|
-
const hasher =
|
|
81748
|
+
const hasher = createHash13("sha256");
|
|
81318
81749
|
hasher.update(`package.json
|
|
81319
81750
|
`);
|
|
81320
|
-
hasher.update(
|
|
81751
|
+
hasher.update(readFileSync59(packageJsonPath));
|
|
81321
81752
|
for (const lockName of ALL_LOCKFILES) {
|
|
81322
|
-
const lockPath =
|
|
81323
|
-
if (
|
|
81753
|
+
const lockPath = join67(sourceDir, lockName);
|
|
81754
|
+
if (existsSync64(lockPath)) {
|
|
81324
81755
|
hasher.update(`
|
|
81325
81756
|
`);
|
|
81326
81757
|
hasher.update(lockName);
|
|
81327
81758
|
hasher.update(`
|
|
81328
81759
|
`);
|
|
81329
|
-
hasher.update(
|
|
81760
|
+
hasher.update(readFileSync59(lockPath));
|
|
81330
81761
|
}
|
|
81331
81762
|
}
|
|
81332
81763
|
return hasher.digest("hex");
|
|
@@ -81335,17 +81766,17 @@ function ensureNodeEnv(opts) {
|
|
|
81335
81766
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
81336
81767
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
81337
81768
|
const installer = opts.installer ?? "bun";
|
|
81338
|
-
if (!
|
|
81769
|
+
if (!existsSync64(packageJsonPath)) {
|
|
81339
81770
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
81340
81771
|
}
|
|
81341
81772
|
const sourceDir = dirname19(packageJsonPath);
|
|
81342
|
-
const envDir =
|
|
81343
|
-
const stampPath =
|
|
81344
|
-
const nodeModulesDir =
|
|
81345
|
-
const binDir =
|
|
81773
|
+
const envDir = join67(cacheRoot, skillName);
|
|
81774
|
+
const stampPath = join67(envDir, ".package.sha256");
|
|
81775
|
+
const nodeModulesDir = join67(envDir, "node_modules");
|
|
81776
|
+
const binDir = join67(nodeModulesDir, ".bin");
|
|
81346
81777
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
81347
|
-
if (!force &&
|
|
81348
|
-
const existingHash =
|
|
81778
|
+
if (!force && existsSync64(stampPath) && existsSync64(nodeModulesDir)) {
|
|
81779
|
+
const existingHash = readFileSync59(stampPath, "utf8").trim();
|
|
81349
81780
|
if (existingHash === targetHash) {
|
|
81350
81781
|
return {
|
|
81351
81782
|
skillName,
|
|
@@ -81356,16 +81787,16 @@ function ensureNodeEnv(opts) {
|
|
|
81356
81787
|
};
|
|
81357
81788
|
}
|
|
81358
81789
|
}
|
|
81359
|
-
if (
|
|
81790
|
+
if (existsSync64(envDir)) {
|
|
81360
81791
|
rmSync14(envDir, { recursive: true, force: true });
|
|
81361
81792
|
}
|
|
81362
81793
|
mkdirSync35(envDir, { recursive: true });
|
|
81363
|
-
copyFileSync9(packageJsonPath,
|
|
81794
|
+
copyFileSync9(packageJsonPath, join67(envDir, "package.json"));
|
|
81364
81795
|
let copiedLockfile = false;
|
|
81365
81796
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
81366
|
-
const lockPath =
|
|
81367
|
-
if (
|
|
81368
|
-
copyFileSync9(lockPath,
|
|
81797
|
+
const lockPath = join67(sourceDir, lockName);
|
|
81798
|
+
if (existsSync64(lockPath)) {
|
|
81799
|
+
copyFileSync9(lockPath, join67(envDir, lockName));
|
|
81369
81800
|
copiedLockfile = true;
|
|
81370
81801
|
}
|
|
81371
81802
|
}
|
|
@@ -81394,28 +81825,28 @@ function ensureNodeEnv(opts) {
|
|
|
81394
81825
|
|
|
81395
81826
|
// src/cli/deps.ts
|
|
81396
81827
|
function builtinSkillsRoot() {
|
|
81397
|
-
return
|
|
81828
|
+
return resolve40(homedir41(), ".switchroom/skills/_bundled");
|
|
81398
81829
|
}
|
|
81399
81830
|
function registerDepsCommand(program3) {
|
|
81400
81831
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
81401
81832
|
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) => {
|
|
81402
81833
|
const skillsRoot = builtinSkillsRoot();
|
|
81403
|
-
if (!
|
|
81834
|
+
if (!existsSync65(skillsRoot)) {
|
|
81404
81835
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
81405
81836
|
process.exit(1);
|
|
81406
81837
|
}
|
|
81407
|
-
const skillDir =
|
|
81408
|
-
if (!
|
|
81838
|
+
const skillDir = join68(skillsRoot, skill);
|
|
81839
|
+
if (!existsSync65(skillDir)) {
|
|
81409
81840
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
81410
81841
|
process.exit(1);
|
|
81411
81842
|
}
|
|
81412
|
-
const requirementsPath =
|
|
81413
|
-
const packageJsonPath =
|
|
81414
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
81415
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
81843
|
+
const requirementsPath = join68(skillDir, "requirements.txt");
|
|
81844
|
+
const packageJsonPath = join68(skillDir, "package.json");
|
|
81845
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync65(requirementsPath));
|
|
81846
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync65(packageJsonPath));
|
|
81416
81847
|
let did = 0;
|
|
81417
81848
|
if (wantPython) {
|
|
81418
|
-
if (!
|
|
81849
|
+
if (!existsSync65(requirementsPath)) {
|
|
81419
81850
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
81420
81851
|
process.exit(1);
|
|
81421
81852
|
}
|
|
@@ -81439,7 +81870,7 @@ function registerDepsCommand(program3) {
|
|
|
81439
81870
|
}
|
|
81440
81871
|
}
|
|
81441
81872
|
if (wantNode) {
|
|
81442
|
-
if (!
|
|
81873
|
+
if (!existsSync65(packageJsonPath)) {
|
|
81443
81874
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
81444
81875
|
process.exit(1);
|
|
81445
81876
|
}
|
|
@@ -81472,8 +81903,8 @@ function registerDepsCommand(program3) {
|
|
|
81472
81903
|
// src/cli/workspace.ts
|
|
81473
81904
|
init_helpers();
|
|
81474
81905
|
init_loader();
|
|
81475
|
-
import { existsSync as
|
|
81476
|
-
import { resolve as
|
|
81906
|
+
import { existsSync as existsSync66 } from "node:fs";
|
|
81907
|
+
import { resolve as resolve41, sep as sep3 } from "node:path";
|
|
81477
81908
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
81478
81909
|
|
|
81479
81910
|
// src/agents/workspace.ts
|
|
@@ -82179,8 +82610,8 @@ function registerWorkspaceCommand(program3) {
|
|
|
82179
82610
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
82180
82611
|
if (!dir)
|
|
82181
82612
|
return;
|
|
82182
|
-
const resolvedWorkspace =
|
|
82183
|
-
const target =
|
|
82613
|
+
const resolvedWorkspace = resolve41(dir);
|
|
82614
|
+
const target = resolve41(resolvedWorkspace, file ?? "AGENTS.md");
|
|
82184
82615
|
if (!isInsideWorkspace(resolvedWorkspace, target)) {
|
|
82185
82616
|
process.stderr.write(`workspace edit: refusing path traversal outside workspace dir (${target})
|
|
82186
82617
|
`);
|
|
@@ -82248,8 +82679,8 @@ function registerWorkspaceCommand(program3) {
|
|
|
82248
82679
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
82249
82680
|
if (!dir)
|
|
82250
82681
|
return;
|
|
82251
|
-
const gitDir =
|
|
82252
|
-
if (!
|
|
82682
|
+
const gitDir = resolve41(dir, ".git");
|
|
82683
|
+
if (!existsSync66(gitDir)) {
|
|
82253
82684
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
82254
82685
|
`);
|
|
82255
82686
|
return;
|
|
@@ -82302,8 +82733,8 @@ function registerWorkspaceCommand(program3) {
|
|
|
82302
82733
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
82303
82734
|
if (!dir)
|
|
82304
82735
|
return;
|
|
82305
|
-
const gitDir =
|
|
82306
|
-
if (!
|
|
82736
|
+
const gitDir = resolve41(dir, ".git");
|
|
82737
|
+
if (!existsSync66(gitDir)) {
|
|
82307
82738
|
process.stdout.write(`Workspace is not a git repository.
|
|
82308
82739
|
`);
|
|
82309
82740
|
return;
|
|
@@ -82326,9 +82757,9 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
82326
82757
|
return;
|
|
82327
82758
|
}
|
|
82328
82759
|
const agentsDir = resolveAgentsDir(config);
|
|
82329
|
-
const agentDir =
|
|
82760
|
+
const agentDir = resolve41(agentsDir, agentName);
|
|
82330
82761
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
82331
|
-
if (!
|
|
82762
|
+
if (!existsSync66(dir)) {
|
|
82332
82763
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
82333
82764
|
`);
|
|
82334
82765
|
return;
|
|
@@ -82364,8 +82795,8 @@ function safeParseInt(value, fallback) {
|
|
|
82364
82795
|
init_helpers();
|
|
82365
82796
|
init_loader();
|
|
82366
82797
|
init_merge();
|
|
82367
|
-
import { copyFileSync as copyFileSync10, existsSync as
|
|
82368
|
-
import { join as
|
|
82798
|
+
import { copyFileSync as copyFileSync10, existsSync as existsSync67, readFileSync as readFileSync60, writeFileSync as writeFileSync31 } from "node:fs";
|
|
82799
|
+
import { join as join69, resolve as resolve42 } from "node:path";
|
|
82369
82800
|
init_schema();
|
|
82370
82801
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
82371
82802
|
const config = getConfig(program3);
|
|
@@ -82378,9 +82809,9 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
82378
82809
|
const profileName = merged.extends ?? DEFAULT_PROFILE;
|
|
82379
82810
|
const profilePath = getProfilePath(profileName);
|
|
82380
82811
|
const agentsDir = resolveAgentsDir(config);
|
|
82381
|
-
const agentDir =
|
|
82812
|
+
const agentDir = resolve42(agentsDir, agentName);
|
|
82382
82813
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
82383
|
-
if (!
|
|
82814
|
+
if (!existsSync67(workspaceDir)) {
|
|
82384
82815
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
82385
82816
|
process.exit(1);
|
|
82386
82817
|
}
|
|
@@ -82389,7 +82820,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
82389
82820
|
profileName,
|
|
82390
82821
|
profilePath,
|
|
82391
82822
|
workspaceDir,
|
|
82392
|
-
soulPath:
|
|
82823
|
+
soulPath: join69(workspaceDir, "SOUL.md"),
|
|
82393
82824
|
soul: merged.soul
|
|
82394
82825
|
};
|
|
82395
82826
|
}
|
|
@@ -82406,11 +82837,11 @@ function registerSoulCommand(program3) {
|
|
|
82406
82837
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
82407
82838
|
if (!t)
|
|
82408
82839
|
return;
|
|
82409
|
-
if (!
|
|
82840
|
+
if (!existsSync67(t.soulPath)) {
|
|
82410
82841
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
82411
82842
|
process.exit(1);
|
|
82412
82843
|
}
|
|
82413
|
-
process.stdout.write(
|
|
82844
|
+
process.stdout.write(readFileSync60(t.soulPath, "utf-8"));
|
|
82414
82845
|
}));
|
|
82415
82846
|
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) => {
|
|
82416
82847
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -82421,7 +82852,7 @@ function registerSoulCommand(program3) {
|
|
|
82421
82852
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
82422
82853
|
process.exit(1);
|
|
82423
82854
|
}
|
|
82424
|
-
const exists =
|
|
82855
|
+
const exists = existsSync67(t.soulPath);
|
|
82425
82856
|
if (exists && !opts.yes) {
|
|
82426
82857
|
if (!isInteractive()) {
|
|
82427
82858
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -82436,7 +82867,7 @@ function registerSoulCommand(program3) {
|
|
|
82436
82867
|
let backupPath;
|
|
82437
82868
|
if (exists) {
|
|
82438
82869
|
backupPath = `${t.soulPath}.bak`;
|
|
82439
|
-
if (
|
|
82870
|
+
if (existsSync67(backupPath)) {
|
|
82440
82871
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
82441
82872
|
}
|
|
82442
82873
|
copyFileSync10(t.soulPath, backupPath);
|
|
@@ -82455,9 +82886,9 @@ function registerSoulCommand(program3) {
|
|
|
82455
82886
|
// src/cli/debug.ts
|
|
82456
82887
|
init_helpers();
|
|
82457
82888
|
init_loader();
|
|
82458
|
-
import { existsSync as
|
|
82459
|
-
import { resolve as
|
|
82460
|
-
import { createHash as
|
|
82889
|
+
import { existsSync as existsSync68, readFileSync as readFileSync61, readdirSync as readdirSync23, statSync as statSync30 } from "node:fs";
|
|
82890
|
+
import { resolve as resolve43, join as join70 } from "node:path";
|
|
82891
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
82461
82892
|
init_merge();
|
|
82462
82893
|
init_hindsight();
|
|
82463
82894
|
function formatBytes(bytes) {
|
|
@@ -82467,22 +82898,22 @@ function estimateTokens(bytes) {
|
|
|
82467
82898
|
return Math.round(bytes / 3.7);
|
|
82468
82899
|
}
|
|
82469
82900
|
function readMcpServerNames(agentDir) {
|
|
82470
|
-
const mcpPath =
|
|
82471
|
-
if (!
|
|
82901
|
+
const mcpPath = join70(agentDir, ".mcp.json");
|
|
82902
|
+
if (!existsSync68(mcpPath))
|
|
82472
82903
|
return [];
|
|
82473
82904
|
try {
|
|
82474
|
-
const parsed = JSON.parse(
|
|
82905
|
+
const parsed = JSON.parse(readFileSync61(mcpPath, "utf-8"));
|
|
82475
82906
|
return Object.keys(parsed.mcpServers ?? {});
|
|
82476
82907
|
} catch {
|
|
82477
82908
|
return null;
|
|
82478
82909
|
}
|
|
82479
82910
|
}
|
|
82480
82911
|
function sha256(content) {
|
|
82481
|
-
return
|
|
82912
|
+
return createHash14("sha256").update(content).digest("hex").slice(0, 16);
|
|
82482
82913
|
}
|
|
82483
82914
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
82484
|
-
const projectsDir =
|
|
82485
|
-
if (!
|
|
82915
|
+
const projectsDir = join70(claudeConfigDir, "projects");
|
|
82916
|
+
if (!existsSync68(projectsDir))
|
|
82486
82917
|
return;
|
|
82487
82918
|
try {
|
|
82488
82919
|
const entries = readdirSync23(projectsDir, { withFileTypes: true });
|
|
@@ -82490,9 +82921,9 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
82490
82921
|
for (const entry of entries) {
|
|
82491
82922
|
if (!entry.isDirectory())
|
|
82492
82923
|
continue;
|
|
82493
|
-
const projectPath =
|
|
82494
|
-
const transcriptPath =
|
|
82495
|
-
if (!
|
|
82924
|
+
const projectPath = join70(projectsDir, entry.name);
|
|
82925
|
+
const transcriptPath = join70(projectPath, "transcript.jsonl");
|
|
82926
|
+
if (!existsSync68(transcriptPath))
|
|
82496
82927
|
continue;
|
|
82497
82928
|
const stat3 = statSync30(transcriptPath);
|
|
82498
82929
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -82506,7 +82937,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
82506
82937
|
}
|
|
82507
82938
|
function extractLatestUserMessage(transcriptPath) {
|
|
82508
82939
|
try {
|
|
82509
|
-
const content =
|
|
82940
|
+
const content = readFileSync61(transcriptPath, "utf-8");
|
|
82510
82941
|
const lines = content.trim().split(`
|
|
82511
82942
|
`).filter(Boolean);
|
|
82512
82943
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -82554,17 +82985,17 @@ function registerDebugCommand(program3) {
|
|
|
82554
82985
|
process.exit(1);
|
|
82555
82986
|
}
|
|
82556
82987
|
const agentsDir = resolveAgentsDir(config);
|
|
82557
|
-
const agentDir =
|
|
82558
|
-
if (!
|
|
82988
|
+
const agentDir = resolve43(agentsDir, agentName);
|
|
82989
|
+
if (!existsSync68(agentDir)) {
|
|
82559
82990
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
82560
82991
|
process.exit(1);
|
|
82561
82992
|
}
|
|
82562
82993
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
82563
|
-
const claudeConfigDir =
|
|
82564
|
-
const claudeMdPath =
|
|
82565
|
-
const soulMdPath =
|
|
82566
|
-
const workspaceSoulMdPath =
|
|
82567
|
-
const handoffPath =
|
|
82994
|
+
const claudeConfigDir = join70(agentDir, ".claude");
|
|
82995
|
+
const claudeMdPath = join70(agentDir, "CLAUDE.md");
|
|
82996
|
+
const soulMdPath = join70(agentDir, "SOUL.md");
|
|
82997
|
+
const workspaceSoulMdPath = join70(workspaceDir, "SOUL.md");
|
|
82998
|
+
const handoffPath = join70(agentDir, ".handoff.md");
|
|
82568
82999
|
const lastN = parseInt(opts.last, 10);
|
|
82569
83000
|
if (isNaN(lastN) || lastN < 1) {
|
|
82570
83001
|
console.error("--last must be a positive integer");
|
|
@@ -82610,7 +83041,7 @@ function registerDebugCommand(program3) {
|
|
|
82610
83041
|
}
|
|
82611
83042
|
console.log(`=== Append System Prompt (per-session) ===
|
|
82612
83043
|
`);
|
|
82613
|
-
const handoffContent =
|
|
83044
|
+
const handoffContent = existsSync68(handoffPath) ? readFileSync61(handoffPath, "utf-8") : "";
|
|
82614
83045
|
if (handoffContent.trim().length > 0) {
|
|
82615
83046
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
82616
83047
|
console.log(handoffContent);
|
|
@@ -82621,7 +83052,7 @@ function registerDebugCommand(program3) {
|
|
|
82621
83052
|
}
|
|
82622
83053
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
82623
83054
|
`);
|
|
82624
|
-
const claudeMdContent =
|
|
83055
|
+
const claudeMdContent = existsSync68(claudeMdPath) ? readFileSync61(claudeMdPath, "utf-8") : "";
|
|
82625
83056
|
if (claudeMdContent.trim().length > 0) {
|
|
82626
83057
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
82627
83058
|
console.log(claudeMdContent);
|
|
@@ -82632,7 +83063,7 @@ function registerDebugCommand(program3) {
|
|
|
82632
83063
|
}
|
|
82633
83064
|
console.log(`=== Persona (SOUL.md) ===
|
|
82634
83065
|
`);
|
|
82635
|
-
const soulMdContent =
|
|
83066
|
+
const soulMdContent = existsSync68(soulMdPath) ? readFileSync61(soulMdPath, "utf-8") : existsSync68(workspaceSoulMdPath) ? readFileSync61(workspaceSoulMdPath, "utf-8") : "";
|
|
82636
83067
|
if (soulMdContent.trim().length > 0) {
|
|
82637
83068
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
82638
83069
|
console.log(soulMdContent);
|
|
@@ -82693,11 +83124,11 @@ function registerDebugCommand(program3) {
|
|
|
82693
83124
|
const soulMdBytes = soulMdContent.length;
|
|
82694
83125
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
82695
83126
|
const userBytes = userMessage?.text.length ?? 0;
|
|
82696
|
-
const fleetDir =
|
|
82697
|
-
const fleetInvPath =
|
|
82698
|
-
const fleetClaudePath =
|
|
82699
|
-
const fleetInvBytes =
|
|
82700
|
-
const fleetClaudeBytes =
|
|
83127
|
+
const fleetDir = join70(agentsDir, "..", "fleet");
|
|
83128
|
+
const fleetInvPath = join70(fleetDir, "switchroom-invariants.md");
|
|
83129
|
+
const fleetClaudePath = join70(fleetDir, "CLAUDE.md");
|
|
83130
|
+
const fleetInvBytes = existsSync68(fleetInvPath) ? readFileSync61(fleetInvPath, "utf-8").length : 0;
|
|
83131
|
+
const fleetClaudeBytes = existsSync68(fleetClaudePath) ? readFileSync61(fleetClaudePath, "utf-8").length : 0;
|
|
82701
83132
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
82702
83133
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
82703
83134
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -82730,8 +83161,8 @@ init_source();
|
|
|
82730
83161
|
|
|
82731
83162
|
// src/worktree/claim.ts
|
|
82732
83163
|
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
82733
|
-
import { closeSync as closeSync12, mkdirSync as mkdirSync37, openSync as openSync12, existsSync as
|
|
82734
|
-
import { join as
|
|
83164
|
+
import { closeSync as closeSync12, mkdirSync as mkdirSync37, openSync as openSync12, existsSync as existsSync70, unlinkSync as unlinkSync13 } from "node:fs";
|
|
83165
|
+
import { join as join72, resolve as resolve45 } from "node:path";
|
|
82735
83166
|
import { homedir as homedir43 } from "node:os";
|
|
82736
83167
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
82737
83168
|
|
|
@@ -82739,19 +83170,19 @@ import { randomBytes as randomBytes13 } from "node:crypto";
|
|
|
82739
83170
|
import {
|
|
82740
83171
|
mkdirSync as mkdirSync36,
|
|
82741
83172
|
writeFileSync as writeFileSync32,
|
|
82742
|
-
readFileSync as
|
|
83173
|
+
readFileSync as readFileSync62,
|
|
82743
83174
|
readdirSync as readdirSync24,
|
|
82744
83175
|
unlinkSync as unlinkSync12,
|
|
82745
|
-
existsSync as
|
|
83176
|
+
existsSync as existsSync69,
|
|
82746
83177
|
renameSync as renameSync13
|
|
82747
83178
|
} from "node:fs";
|
|
82748
|
-
import { join as
|
|
83179
|
+
import { join as join71, resolve as resolve44 } from "node:path";
|
|
82749
83180
|
import { homedir as homedir42 } from "node:os";
|
|
82750
83181
|
function registryDir() {
|
|
82751
|
-
return
|
|
83182
|
+
return resolve44(process.env.SWITCHROOM_WORKTREE_DIR ?? join71(homedir42(), ".switchroom", "worktrees"));
|
|
82752
83183
|
}
|
|
82753
83184
|
function recordPath(id) {
|
|
82754
|
-
return
|
|
83185
|
+
return join71(registryDir(), `${id}.json`);
|
|
82755
83186
|
}
|
|
82756
83187
|
function ensureDir2() {
|
|
82757
83188
|
mkdirSync36(registryDir(), { recursive: true });
|
|
@@ -82767,7 +83198,7 @@ function writeRecord(record2) {
|
|
|
82767
83198
|
function readRecord(id) {
|
|
82768
83199
|
const path7 = recordPath(id);
|
|
82769
83200
|
try {
|
|
82770
|
-
const raw =
|
|
83201
|
+
const raw = readFileSync62(path7, "utf8");
|
|
82771
83202
|
return JSON.parse(raw);
|
|
82772
83203
|
} catch {
|
|
82773
83204
|
return null;
|
|
@@ -82802,7 +83233,7 @@ function acquireRepoLock(repoPath) {
|
|
|
82802
83233
|
const lockDir = registryDir();
|
|
82803
83234
|
mkdirSync37(lockDir, { recursive: true });
|
|
82804
83235
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
82805
|
-
const lockPath =
|
|
83236
|
+
const lockPath = join72(lockDir, `.lock-${lockName}`);
|
|
82806
83237
|
const deadline = Date.now() + 5000;
|
|
82807
83238
|
let fd = null;
|
|
82808
83239
|
while (fd === null) {
|
|
@@ -82829,7 +83260,7 @@ function acquireRepoLock(repoPath) {
|
|
|
82829
83260
|
}
|
|
82830
83261
|
var DEFAULT_CONCURRENCY = 5;
|
|
82831
83262
|
function worktreesBaseDir() {
|
|
82832
|
-
return
|
|
83263
|
+
return resolve45(process.env.SWITCHROOM_WORKTREE_BASE ?? join72(homedir43(), ".switchroom", "worktree-checkouts"));
|
|
82833
83264
|
}
|
|
82834
83265
|
function shortId() {
|
|
82835
83266
|
return randomBytes13(4).toString("hex");
|
|
@@ -82851,12 +83282,12 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
82851
83282
|
}
|
|
82852
83283
|
function expandHome(p) {
|
|
82853
83284
|
if (p.startsWith("~/"))
|
|
82854
|
-
return
|
|
83285
|
+
return join72(homedir43(), p.slice(2));
|
|
82855
83286
|
return p;
|
|
82856
83287
|
}
|
|
82857
83288
|
async function claimWorktree(input, codeRepos) {
|
|
82858
83289
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
82859
|
-
if (!
|
|
83290
|
+
if (!existsSync70(repoPath)) {
|
|
82860
83291
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
82861
83292
|
}
|
|
82862
83293
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -82879,7 +83310,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
82879
83310
|
branch = `task/${taskSuffix}-${id}`;
|
|
82880
83311
|
const baseDir = worktreesBaseDir();
|
|
82881
83312
|
mkdirSync37(baseDir, { recursive: true });
|
|
82882
|
-
worktreePath =
|
|
83313
|
+
worktreePath = join72(baseDir, `${id}-${taskSuffix}`);
|
|
82883
83314
|
const now = new Date().toISOString();
|
|
82884
83315
|
const record2 = {
|
|
82885
83316
|
id,
|
|
@@ -82910,7 +83341,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
82910
83341
|
|
|
82911
83342
|
// src/worktree/release.ts
|
|
82912
83343
|
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
82913
|
-
import { existsSync as
|
|
83344
|
+
import { existsSync as existsSync71 } from "node:fs";
|
|
82914
83345
|
function releaseWorktree(input) {
|
|
82915
83346
|
const { id } = input;
|
|
82916
83347
|
const record2 = readRecord(id);
|
|
@@ -82918,7 +83349,7 @@ function releaseWorktree(input) {
|
|
|
82918
83349
|
return { released: true };
|
|
82919
83350
|
}
|
|
82920
83351
|
let gitSuccess = true;
|
|
82921
|
-
if (
|
|
83352
|
+
if (existsSync71(record2.path)) {
|
|
82922
83353
|
try {
|
|
82923
83354
|
execFileSync22("git", ["worktree", "remove", "--force", record2.path], {
|
|
82924
83355
|
cwd: record2.repo,
|
|
@@ -82957,7 +83388,7 @@ function listWorktrees() {
|
|
|
82957
83388
|
|
|
82958
83389
|
// src/worktree/reaper.ts
|
|
82959
83390
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
82960
|
-
import { existsSync as
|
|
83391
|
+
import { existsSync as existsSync72 } from "node:fs";
|
|
82961
83392
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
82962
83393
|
function isPathInUse(path7) {
|
|
82963
83394
|
try {
|
|
@@ -82984,7 +83415,7 @@ function hasUncommittedChanges(repoPath, worktreePath) {
|
|
|
82984
83415
|
function reapRecord(record2) {
|
|
82985
83416
|
const { id, path: path7, repo, branch, ownerAgent } = record2;
|
|
82986
83417
|
let warning = null;
|
|
82987
|
-
if (
|
|
83418
|
+
if (existsSync72(path7)) {
|
|
82988
83419
|
if (hasUncommittedChanges(repo, path7)) {
|
|
82989
83420
|
warning = `[worktree-reaper] Reaped worktree with uncommitted changes: ` + `id=${id} branch=${branch} agent=${ownerAgent ?? "unknown"} path=${path7}`;
|
|
82990
83421
|
}
|
|
@@ -83005,7 +83436,7 @@ function runReaper(nowMs) {
|
|
|
83005
83436
|
const warnings = [];
|
|
83006
83437
|
for (const record2 of records) {
|
|
83007
83438
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
83008
|
-
const worktreeExists =
|
|
83439
|
+
const worktreeExists = existsSync72(record2.path);
|
|
83009
83440
|
if (!worktreeExists) {
|
|
83010
83441
|
deleteRecord(record2.id);
|
|
83011
83442
|
reaped.push(record2.id);
|
|
@@ -83025,8 +83456,8 @@ function runReaper(nowMs) {
|
|
|
83025
83456
|
// src/worktree/gc.ts
|
|
83026
83457
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
83027
83458
|
import {
|
|
83028
|
-
existsSync as
|
|
83029
|
-
readFileSync as
|
|
83459
|
+
existsSync as existsSync73,
|
|
83460
|
+
readFileSync as readFileSync63,
|
|
83030
83461
|
readdirSync as readdirSync25,
|
|
83031
83462
|
statSync as statSync31,
|
|
83032
83463
|
renameSync as renameSync14,
|
|
@@ -83034,7 +83465,7 @@ import {
|
|
|
83034
83465
|
rmSync as rmSync15
|
|
83035
83466
|
} from "node:fs";
|
|
83036
83467
|
import { homedir as homedir44 } from "node:os";
|
|
83037
|
-
import { join as
|
|
83468
|
+
import { join as join73, resolve as resolve46 } from "node:path";
|
|
83038
83469
|
function parseGitdirPointer(dotGitFileContents) {
|
|
83039
83470
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
83040
83471
|
return m ? m[1] : null;
|
|
@@ -83109,7 +83540,7 @@ function looksLikeAgentWorktree(path7, branch) {
|
|
|
83109
83540
|
return false;
|
|
83110
83541
|
}
|
|
83111
83542
|
function isEphemeralPath(path7) {
|
|
83112
|
-
const p =
|
|
83543
|
+
const p = resolve46(path7);
|
|
83113
83544
|
for (const root of ["/tmp", "/host", "/state"]) {
|
|
83114
83545
|
if (p === root || p.startsWith(root + "/"))
|
|
83115
83546
|
return true;
|
|
@@ -83149,20 +83580,20 @@ function defaultPrSignal(repo, branch, exec) {
|
|
|
83149
83580
|
}
|
|
83150
83581
|
}
|
|
83151
83582
|
function trashRoot() {
|
|
83152
|
-
return
|
|
83583
|
+
return resolve46(process.env.SWITCHROOM_WORKTREE_TRASH ?? join73(homedir44(), ".switchroom", "worktree-gc-trash"));
|
|
83153
83584
|
}
|
|
83154
83585
|
function planGc(roots, deps = {}) {
|
|
83155
|
-
const exists = deps.existsSync ??
|
|
83586
|
+
const exists = deps.existsSync ?? existsSync73;
|
|
83156
83587
|
const readDir = deps.readDir ?? ((p) => readdirSync25(p));
|
|
83157
|
-
const readFile4 = deps.readFile ?? ((p) =>
|
|
83588
|
+
const readFile4 = deps.readFile ?? ((p) => readFileSync63(p, "utf8"));
|
|
83158
83589
|
const stat3 = deps.stat ?? ((p) => statSync31(p));
|
|
83159
83590
|
const exec = deps.exec ?? defaultExec;
|
|
83160
83591
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
83161
83592
|
const stamp = deps.dateStamp ?? "undated";
|
|
83162
|
-
const trash =
|
|
83593
|
+
const trash = join73(trashRoot(), stamp);
|
|
83163
83594
|
let claimed;
|
|
83164
83595
|
try {
|
|
83165
|
-
claimed = new Set(listRecords().map((r) =>
|
|
83596
|
+
claimed = new Set(listRecords().map((r) => resolve46(r.path)));
|
|
83166
83597
|
} catch {
|
|
83167
83598
|
claimed = new Set;
|
|
83168
83599
|
}
|
|
@@ -83195,10 +83626,10 @@ function planGc(roots, deps = {}) {
|
|
|
83195
83626
|
continue;
|
|
83196
83627
|
}
|
|
83197
83628
|
for (const name of entries) {
|
|
83198
|
-
const dir =
|
|
83629
|
+
const dir = join73(root, name);
|
|
83199
83630
|
if (isEphemeralPath(dir))
|
|
83200
83631
|
continue;
|
|
83201
|
-
const dotGit =
|
|
83632
|
+
const dotGit = join73(dir, ".git");
|
|
83202
83633
|
if (!exists(dotGit))
|
|
83203
83634
|
continue;
|
|
83204
83635
|
let st;
|
|
@@ -83227,7 +83658,7 @@ function planGc(roots, deps = {}) {
|
|
|
83227
83658
|
ownerRepos.add(repoRoot);
|
|
83228
83659
|
if (exists(ptr))
|
|
83229
83660
|
continue;
|
|
83230
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
83661
|
+
orphans.push({ dir, owner: repoRoot, dest: join73(trash, name) });
|
|
83231
83662
|
}
|
|
83232
83663
|
}
|
|
83233
83664
|
const registered = [];
|
|
@@ -83240,8 +83671,8 @@ function planGc(roots, deps = {}) {
|
|
|
83240
83671
|
continue;
|
|
83241
83672
|
}
|
|
83242
83673
|
for (const wt of parseWorktreeList(porcelain)) {
|
|
83243
|
-
const wtPath =
|
|
83244
|
-
const isMain = wtPath ===
|
|
83674
|
+
const wtPath = resolve46(wt.path);
|
|
83675
|
+
const isMain = wtPath === resolve46(repo);
|
|
83245
83676
|
const claimedWt = claimed.has(wtPath);
|
|
83246
83677
|
const agentWt = looksLikeAgentWorktree(wt.path, wt.branch);
|
|
83247
83678
|
const ephemeral = isEphemeralPath(wt.path);
|
|
@@ -83331,14 +83762,14 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
83331
83762
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
83332
83763
|
}
|
|
83333
83764
|
function listTrashEntries(nowMs, deps = {}) {
|
|
83334
|
-
const exists = deps.existsSync ??
|
|
83765
|
+
const exists = deps.existsSync ?? existsSync73;
|
|
83335
83766
|
const readDir = deps.readDir ?? ((p) => readdirSync25(p));
|
|
83336
83767
|
const root = trashRoot();
|
|
83337
83768
|
if (!exists(root))
|
|
83338
83769
|
return [];
|
|
83339
83770
|
const out = [];
|
|
83340
83771
|
for (const stamp of readDir(root)) {
|
|
83341
|
-
const stampDir =
|
|
83772
|
+
const stampDir = join73(root, stamp);
|
|
83342
83773
|
let names;
|
|
83343
83774
|
try {
|
|
83344
83775
|
names = readDir(stampDir);
|
|
@@ -83346,7 +83777,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
83346
83777
|
continue;
|
|
83347
83778
|
}
|
|
83348
83779
|
for (const name of names) {
|
|
83349
|
-
const p =
|
|
83780
|
+
const p = join73(stampDir, name);
|
|
83350
83781
|
let mtimeMs = nowMs;
|
|
83351
83782
|
try {
|
|
83352
83783
|
mtimeMs = statSync31(p).mtimeMs;
|
|
@@ -83370,7 +83801,7 @@ function purgeTrash(paths) {
|
|
|
83370
83801
|
return { deleted, errors: errors2 };
|
|
83371
83802
|
}
|
|
83372
83803
|
function defaultRoots() {
|
|
83373
|
-
return [
|
|
83804
|
+
return [join73(homedir44(), "code")];
|
|
83374
83805
|
}
|
|
83375
83806
|
|
|
83376
83807
|
// src/cli/worktree.ts
|
|
@@ -83564,7 +83995,7 @@ import {
|
|
|
83564
83995
|
rmSync as rmSync16,
|
|
83565
83996
|
writeFileSync as writeFileSync33
|
|
83566
83997
|
} from "node:fs";
|
|
83567
|
-
import { join as
|
|
83998
|
+
import { join as join74 } from "node:path";
|
|
83568
83999
|
function encodeCredentialsFilename(email) {
|
|
83569
84000
|
const SAFE = new Set([
|
|
83570
84001
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -83754,16 +84185,16 @@ function resolveCredentialsDir(env2) {
|
|
|
83754
84185
|
if (explicit && explicit.length > 0)
|
|
83755
84186
|
return explicit;
|
|
83756
84187
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
83757
|
-
return
|
|
84188
|
+
return join74(stateBase, "google-workspace-mcp", "credentials");
|
|
83758
84189
|
}
|
|
83759
84190
|
function writeSeedFile(dir, email, seed) {
|
|
83760
84191
|
mkdirSync39(dir, { recursive: true, mode: 448 });
|
|
83761
84192
|
chmodSync9(dir, 448);
|
|
83762
84193
|
for (const name of readdirSync26(dir)) {
|
|
83763
|
-
rmSync16(
|
|
84194
|
+
rmSync16(join74(dir, name), { force: true, recursive: true });
|
|
83764
84195
|
}
|
|
83765
84196
|
const filename = encodeCredentialsFilename(email);
|
|
83766
|
-
const filePath =
|
|
84197
|
+
const filePath = join74(dir, filename);
|
|
83767
84198
|
writeFileSync33(filePath, JSON.stringify(seed), { mode: 384 });
|
|
83768
84199
|
chmodSync9(filePath, 384);
|
|
83769
84200
|
return filePath;
|
|
@@ -83923,7 +84354,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
83923
84354
|
init_scaffold_integration();
|
|
83924
84355
|
import { spawn as spawn5 } from "node:child_process";
|
|
83925
84356
|
import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync40 } from "node:fs";
|
|
83926
|
-
import { dirname as dirname20, join as
|
|
84357
|
+
import { dirname as dirname20, join as join75 } from "node:path";
|
|
83927
84358
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
83928
84359
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
83929
84360
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -83955,7 +84386,7 @@ function writeRefreshHeartbeat(agentName, data) {
|
|
|
83955
84386
|
function heartbeatPath(agentName) {
|
|
83956
84387
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
83957
84388
|
if (override) {
|
|
83958
|
-
return
|
|
84389
|
+
return join75(override, `m365-launcher-${agentName}.heartbeat.json`);
|
|
83959
84390
|
}
|
|
83960
84391
|
return "/state/agent/m365-launcher.heartbeat.json";
|
|
83961
84392
|
}
|
|
@@ -83979,20 +84410,20 @@ function wireStdio(child) {
|
|
|
83979
84410
|
async function killChild(child, gracefulMs = 3000) {
|
|
83980
84411
|
if (child.exitCode !== null || child.signalCode !== null)
|
|
83981
84412
|
return;
|
|
83982
|
-
return new Promise((
|
|
84413
|
+
return new Promise((resolve47) => {
|
|
83983
84414
|
let killTimer = null;
|
|
83984
84415
|
const onExit = () => {
|
|
83985
84416
|
if (killTimer) {
|
|
83986
84417
|
clearTimeout(killTimer);
|
|
83987
84418
|
killTimer = null;
|
|
83988
84419
|
}
|
|
83989
|
-
|
|
84420
|
+
resolve47();
|
|
83990
84421
|
};
|
|
83991
84422
|
child.once("exit", onExit);
|
|
83992
84423
|
try {
|
|
83993
84424
|
child.kill("SIGTERM");
|
|
83994
84425
|
} catch {
|
|
83995
|
-
|
|
84426
|
+
resolve47();
|
|
83996
84427
|
return;
|
|
83997
84428
|
}
|
|
83998
84429
|
killTimer = setTimeout(() => {
|
|
@@ -84106,8 +84537,8 @@ async function runMs365McpLauncher(opts, rt) {
|
|
|
84106
84537
|
};
|
|
84107
84538
|
process.on("SIGINT", onSignal);
|
|
84108
84539
|
process.on("SIGTERM", onSignal);
|
|
84109
|
-
return new Promise((
|
|
84110
|
-
resolveLauncher =
|
|
84540
|
+
return new Promise((resolve47) => {
|
|
84541
|
+
resolveLauncher = resolve47;
|
|
84111
84542
|
});
|
|
84112
84543
|
}
|
|
84113
84544
|
function registerM365McpLauncherCommand(program3) {
|
|
@@ -84140,7 +84571,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
84140
84571
|
// src/cli/notion-mcp-launcher.ts
|
|
84141
84572
|
init_scaffold_integration();
|
|
84142
84573
|
import { spawn as spawn6 } from "node:child_process";
|
|
84143
|
-
import { existsSync as
|
|
84574
|
+
import { existsSync as existsSync74, mkdirSync as mkdirSync41, writeFileSync as writeFileSync35 } from "node:fs";
|
|
84144
84575
|
import { dirname as dirname21 } from "node:path";
|
|
84145
84576
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
84146
84577
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
@@ -84152,7 +84583,7 @@ function buildNotionMcpArgs(opts) {
|
|
|
84152
84583
|
function defaultWriteHeartbeat(path7, contents) {
|
|
84153
84584
|
try {
|
|
84154
84585
|
const dir = dirname21(path7);
|
|
84155
|
-
if (!
|
|
84586
|
+
if (!existsSync74(dir))
|
|
84156
84587
|
mkdirSync41(dir, { recursive: true });
|
|
84157
84588
|
writeFileSync35(path7, contents);
|
|
84158
84589
|
} catch {}
|
|
@@ -84209,23 +84640,23 @@ async function runNotionMcpLauncher(opts, runtime) {
|
|
|
84209
84640
|
};
|
|
84210
84641
|
process.on("SIGTERM", () => forward("SIGTERM"));
|
|
84211
84642
|
process.on("SIGINT", () => forward("SIGINT"));
|
|
84212
|
-
const exitCode = await new Promise((
|
|
84643
|
+
const exitCode = await new Promise((resolve47) => {
|
|
84213
84644
|
child.once("exit", (code, signal) => {
|
|
84214
84645
|
clearTimer(heartbeatHandle);
|
|
84215
84646
|
if (typeof code === "number") {
|
|
84216
|
-
|
|
84647
|
+
resolve47(code);
|
|
84217
84648
|
} else if (signal) {
|
|
84218
84649
|
const sigCode = { SIGTERM: 15, SIGINT: 2, SIGKILL: 9 }[signal] ?? 1;
|
|
84219
|
-
|
|
84650
|
+
resolve47(128 + sigCode);
|
|
84220
84651
|
} else {
|
|
84221
|
-
|
|
84652
|
+
resolve47(1);
|
|
84222
84653
|
}
|
|
84223
84654
|
});
|
|
84224
84655
|
child.once("error", (err) => {
|
|
84225
84656
|
clearTimer(heartbeatHandle);
|
|
84226
84657
|
process.stderr.write(`notion-mcp-launcher: child spawn error: ${err.message}
|
|
84227
84658
|
`);
|
|
84228
|
-
|
|
84659
|
+
resolve47(1);
|
|
84229
84660
|
});
|
|
84230
84661
|
});
|
|
84231
84662
|
return exitCode;
|
|
@@ -84264,7 +84695,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
84264
84695
|
|
|
84265
84696
|
// src/cli/deliver-file.ts
|
|
84266
84697
|
init_client2();
|
|
84267
|
-
import { readFileSync as
|
|
84698
|
+
import { readFileSync as readFileSync64, statSync as statSync32 } from "node:fs";
|
|
84268
84699
|
import { basename as basename10 } from "node:path";
|
|
84269
84700
|
|
|
84270
84701
|
// src/delivery/onedrive.ts
|
|
@@ -84604,7 +85035,7 @@ async function defaultResolveProvider() {
|
|
|
84604
85035
|
async function runDeliverFile(localPath, deps = {}) {
|
|
84605
85036
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
84606
85037
|
const sizeOf = deps.fileSize ?? ((p) => statSync32(p).size);
|
|
84607
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
85038
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync64(p)));
|
|
84608
85039
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
84609
85040
|
let size;
|
|
84610
85041
|
try {
|
|
@@ -84894,7 +85325,7 @@ async function fetchToken(vaultKey) {
|
|
|
84894
85325
|
|
|
84895
85326
|
// src/cli/apply.ts
|
|
84896
85327
|
init_source();
|
|
84897
|
-
import { accessSync as accessSync3, chownSync as chownSync7, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as
|
|
85328
|
+
import { accessSync as accessSync3, chownSync as chownSync7, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync77, mkdirSync as mkdirSync44, readFileSync as readFileSync66, readdirSync as readdirSync27, renameSync as renameSync15, writeFileSync as writeFileSync38 } from "node:fs";
|
|
84898
85329
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
84899
85330
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
84900
85331
|
import readline from "node:readline";
|
|
@@ -85286,7 +85717,7 @@ agents:
|
|
|
85286
85717
|
// src/cli/apply.ts
|
|
85287
85718
|
init_resolver();
|
|
85288
85719
|
init_merge();
|
|
85289
|
-
import { dirname as dirname24, join as
|
|
85720
|
+
import { dirname as dirname24, join as join79, resolve as resolve48 } from "node:path";
|
|
85290
85721
|
import { homedir as homedir46 } from "node:os";
|
|
85291
85722
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
85292
85723
|
init_vault();
|
|
@@ -85295,7 +85726,7 @@ init_loader();
|
|
|
85295
85726
|
|
|
85296
85727
|
// src/agents/connection-health.ts
|
|
85297
85728
|
import { mkdirSync as mkdirSync42, writeFileSync as writeFileSync36 } from "node:fs";
|
|
85298
|
-
import { join as
|
|
85729
|
+
import { join as join76 } from "node:path";
|
|
85299
85730
|
var CONNECTION_HEALTH_FILENAME = "connection-health.json";
|
|
85300
85731
|
async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
85301
85732
|
const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
|
|
@@ -85353,8 +85784,8 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
|
85353
85784
|
return issues;
|
|
85354
85785
|
}
|
|
85355
85786
|
function writeConnectionHealthFile(agentDir, health, deps) {
|
|
85356
|
-
const dir =
|
|
85357
|
-
const path7 =
|
|
85787
|
+
const dir = join76(agentDir, ".claude");
|
|
85788
|
+
const path7 = join76(dir, CONNECTION_HEALTH_FILENAME);
|
|
85358
85789
|
(deps?.mkdir ?? ((p, o) => mkdirSync42(p, o)))(dir, { recursive: true });
|
|
85359
85790
|
(deps?.writeFile ?? ((p, d) => writeFileSync36(p, d)))(path7, JSON.stringify(health, null, 2) + `
|
|
85360
85791
|
`);
|
|
@@ -85375,12 +85806,12 @@ async function refreshAgentConnectionHealth(config, agentName, agentDir, deps) {
|
|
|
85375
85806
|
}
|
|
85376
85807
|
|
|
85377
85808
|
// src/cli/update-prompt-hook.ts
|
|
85378
|
-
import { existsSync as
|
|
85379
|
-
import { join as
|
|
85809
|
+
import { existsSync as existsSync75, readFileSync as readFileSync65, writeFileSync as writeFileSync37, chmodSync as chmodSync10, mkdirSync as mkdirSync43 } from "node:fs";
|
|
85810
|
+
import { join as join77 } from "node:path";
|
|
85380
85811
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
85381
85812
|
var CONTAINER_AGENT_DIR = "/state/agent";
|
|
85382
85813
|
function containerHookCommand() {
|
|
85383
|
-
return
|
|
85814
|
+
return join77(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
|
|
85384
85815
|
}
|
|
85385
85816
|
function updatePromptHookScript() {
|
|
85386
85817
|
return `#!/bin/bash
|
|
@@ -85446,12 +85877,12 @@ exit 0
|
|
|
85446
85877
|
`;
|
|
85447
85878
|
}
|
|
85448
85879
|
function installUpdatePromptHook(agentDir) {
|
|
85449
|
-
const hooksDir =
|
|
85880
|
+
const hooksDir = join77(agentDir, ".claude", "hooks");
|
|
85450
85881
|
mkdirSync43(hooksDir, { recursive: true });
|
|
85451
|
-
const scriptPath =
|
|
85882
|
+
const scriptPath = join77(hooksDir, HOOK_FILENAME);
|
|
85452
85883
|
const desired = updatePromptHookScript();
|
|
85453
85884
|
let installed = false;
|
|
85454
|
-
const existing =
|
|
85885
|
+
const existing = existsSync75(scriptPath) ? readFileSync65(scriptPath, "utf-8") : "";
|
|
85455
85886
|
if (existing !== desired) {
|
|
85456
85887
|
writeFileSync37(scriptPath, desired, { mode: 493 });
|
|
85457
85888
|
chmodSync10(scriptPath, 493);
|
|
@@ -85461,11 +85892,11 @@ function installUpdatePromptHook(agentDir) {
|
|
|
85461
85892
|
chmodSync10(scriptPath, 493);
|
|
85462
85893
|
} catch {}
|
|
85463
85894
|
}
|
|
85464
|
-
const settingsPath =
|
|
85465
|
-
if (!
|
|
85895
|
+
const settingsPath = join77(agentDir, ".claude", "settings.json");
|
|
85896
|
+
if (!existsSync75(settingsPath)) {
|
|
85466
85897
|
return { scriptPath, settingsPath, installed };
|
|
85467
85898
|
}
|
|
85468
|
-
const raw =
|
|
85899
|
+
const raw = readFileSync65(settingsPath, "utf-8");
|
|
85469
85900
|
let parsed;
|
|
85470
85901
|
try {
|
|
85471
85902
|
parsed = JSON.parse(raw);
|
|
@@ -85575,7 +86006,7 @@ var EMBEDDED_EXAMPLES = {
|
|
|
85575
86006
|
switchroom: switchroom_default,
|
|
85576
86007
|
minimal: minimal_default
|
|
85577
86008
|
};
|
|
85578
|
-
var DEFAULT_COMPOSE_PATH2 =
|
|
86009
|
+
var DEFAULT_COMPOSE_PATH2 = join79(homedir46(), ".switchroom", "compose", "docker-compose.yml");
|
|
85579
86010
|
var COMPOSE_PROJECT2 = "switchroom";
|
|
85580
86011
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
85581
86012
|
return agentResolvedLitellm?.enabled ?? config.litellm?.enabled ?? false;
|
|
@@ -85586,7 +86017,7 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
85586
86017
|
return envPass;
|
|
85587
86018
|
try {
|
|
85588
86019
|
const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
|
|
85589
|
-
const blobPath =
|
|
86020
|
+
const blobPath = join79(home2, ".switchroom", "vault-auto-unlock");
|
|
85590
86021
|
const pass = readAutoUnlockFile2(blobPath);
|
|
85591
86022
|
return pass && pass.length > 0 ? pass : null;
|
|
85592
86023
|
} catch {
|
|
@@ -85633,9 +86064,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
85633
86064
|
const oauthAccount = config.auth?.active;
|
|
85634
86065
|
let pendingConfigEdits = false;
|
|
85635
86066
|
let configText = null;
|
|
85636
|
-
if (switchroomConfigPath &&
|
|
86067
|
+
if (switchroomConfigPath && existsSync77(switchroomConfigPath)) {
|
|
85637
86068
|
try {
|
|
85638
|
-
configText =
|
|
86069
|
+
configText = readFileSync66(switchroomConfigPath, "utf-8");
|
|
85639
86070
|
} catch (err) {
|
|
85640
86071
|
ctx.writeErr(source_default.yellow(` ! litellm: could not read config for ACL grants (${err.message}); keys will be provisioned but agents may lack read-ACL.
|
|
85641
86072
|
`));
|
|
@@ -85798,10 +86229,10 @@ function resolveVaultBindMountDir(homeDir, ctx) {
|
|
|
85798
86229
|
if (isCustomPath && ctx.customVaultPath) {
|
|
85799
86230
|
return dirname24(ctx.customVaultPath);
|
|
85800
86231
|
}
|
|
85801
|
-
return
|
|
86232
|
+
return join79(homeDir, ".switchroom", "vault");
|
|
85802
86233
|
}
|
|
85803
86234
|
function inspectVaultBindMountDir(vaultDir) {
|
|
85804
|
-
if (!
|
|
86235
|
+
if (!existsSync77(vaultDir))
|
|
85805
86236
|
return { kind: "missing" };
|
|
85806
86237
|
const entries = readdirSync27(vaultDir);
|
|
85807
86238
|
const unknown = [];
|
|
@@ -85829,43 +86260,43 @@ function hasVaultRefs(value) {
|
|
|
85829
86260
|
async function ensureHostMountSources(config) {
|
|
85830
86261
|
const home2 = resolveHostHomeForCompose();
|
|
85831
86262
|
const dirs = [
|
|
85832
|
-
|
|
85833
|
-
|
|
85834
|
-
|
|
85835
|
-
|
|
85836
|
-
|
|
86263
|
+
join79(home2, ".switchroom", "approvals"),
|
|
86264
|
+
join79(home2, ".switchroom", "scheduler"),
|
|
86265
|
+
join79(home2, ".switchroom", "logs"),
|
|
86266
|
+
join79(home2, ".switchroom", "compose"),
|
|
86267
|
+
join79(home2, ".switchroom", "broker-operator")
|
|
85837
86268
|
];
|
|
85838
86269
|
for (const name of Object.keys(config.agents)) {
|
|
85839
|
-
dirs.push(
|
|
85840
|
-
dirs.push(
|
|
85841
|
-
dirs.push(
|
|
85842
|
-
dirs.push(
|
|
85843
|
-
if (
|
|
85844
|
-
dirs.push(
|
|
86270
|
+
dirs.push(join79(home2, ".switchroom", "agents", name));
|
|
86271
|
+
dirs.push(join79(home2, ".switchroom", "logs", name));
|
|
86272
|
+
dirs.push(join79(home2, ".claude", "projects", name));
|
|
86273
|
+
dirs.push(join79(home2, ".switchroom", "audit", name));
|
|
86274
|
+
if (existsSync77(join79(home2, ".switchroom-config"))) {
|
|
86275
|
+
dirs.push(join79(home2, ".switchroom-config", "agents", name, "personal-skills"));
|
|
85845
86276
|
}
|
|
85846
86277
|
}
|
|
85847
86278
|
for (const dir of dirs) {
|
|
85848
86279
|
await mkdir2(dir, { recursive: true });
|
|
85849
86280
|
}
|
|
85850
|
-
const autoUnlockPath =
|
|
85851
|
-
if (!
|
|
86281
|
+
const autoUnlockPath = join79(home2, ".switchroom", "vault-auto-unlock");
|
|
86282
|
+
if (!existsSync77(autoUnlockPath)) {
|
|
85852
86283
|
writeFileSync38(autoUnlockPath, "", { mode: 384 });
|
|
85853
86284
|
}
|
|
85854
|
-
const auditLogPath =
|
|
85855
|
-
if (!
|
|
86285
|
+
const auditLogPath = join79(home2, ".switchroom", "vault-audit.log");
|
|
86286
|
+
if (!existsSync77(auditLogPath)) {
|
|
85856
86287
|
writeFileSync38(auditLogPath, "", { mode: 420 });
|
|
85857
86288
|
}
|
|
85858
|
-
const grantsDbPath =
|
|
85859
|
-
if (!
|
|
86289
|
+
const grantsDbPath = join79(home2, ".switchroom", "vault-grants.db");
|
|
86290
|
+
if (!existsSync77(grantsDbPath)) {
|
|
85860
86291
|
writeFileSync38(grantsDbPath, "", { mode: 384 });
|
|
85861
86292
|
}
|
|
85862
|
-
const hostdAuditLogPath =
|
|
85863
|
-
if (!
|
|
86293
|
+
const hostdAuditLogPath = join79(home2, ".switchroom", "host-control-audit.log");
|
|
86294
|
+
if (!existsSync77(hostdAuditLogPath)) {
|
|
85864
86295
|
writeFileSync38(hostdAuditLogPath, "", { mode: 420 });
|
|
85865
86296
|
}
|
|
85866
86297
|
for (const name of Object.keys(config.agents)) {
|
|
85867
|
-
const tokenPath =
|
|
85868
|
-
if (!
|
|
86298
|
+
const tokenPath = join79(home2, ".switchroom", "agents", name, ".vault-token");
|
|
86299
|
+
if (!existsSync77(tokenPath)) {
|
|
85869
86300
|
writeFileSync38(tokenPath, "", { mode: 384 });
|
|
85870
86301
|
}
|
|
85871
86302
|
try {
|
|
@@ -85873,16 +86304,16 @@ async function ensureHostMountSources(config) {
|
|
|
85873
86304
|
chownSync7(tokenPath, uid, uid);
|
|
85874
86305
|
} catch {}
|
|
85875
86306
|
}
|
|
85876
|
-
const fleetDir =
|
|
86307
|
+
const fleetDir = join79(home2, ".switchroom", "fleet");
|
|
85877
86308
|
await mkdir2(fleetDir, { recursive: true });
|
|
85878
|
-
const invariantsPath =
|
|
86309
|
+
const invariantsPath = join79(fleetDir, "switchroom-invariants.md");
|
|
85879
86310
|
const invariantsCanonical = renderFleetInvariants();
|
|
85880
|
-
const invariantsCurrent =
|
|
86311
|
+
const invariantsCurrent = existsSync77(invariantsPath) ? readFileSync66(invariantsPath, "utf-8") : null;
|
|
85881
86312
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
85882
86313
|
writeFileSync38(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
85883
86314
|
}
|
|
85884
|
-
const fleetClaudePath =
|
|
85885
|
-
if (!
|
|
86315
|
+
const fleetClaudePath = join79(fleetDir, "CLAUDE.md");
|
|
86316
|
+
if (!existsSync77(fleetClaudePath)) {
|
|
85886
86317
|
writeFileSync38(fleetClaudePath, [
|
|
85887
86318
|
"# Switchroom fleet defaults",
|
|
85888
86319
|
"",
|
|
@@ -85914,7 +86345,7 @@ ${out.trim()}`;
|
|
|
85914
86345
|
}
|
|
85915
86346
|
function runApplyPreflight(config, opts = {}) {
|
|
85916
86347
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
85917
|
-
if (hasVaultRefs(config) && !
|
|
86348
|
+
if (hasVaultRefs(config) && !existsSync77(vaultPath)) {
|
|
85918
86349
|
throw new Error(`Config references vault keys (vault:<name>) but ${vaultPath} is missing. Run \`switchroom setup\` first to initialise the vault.`);
|
|
85919
86350
|
}
|
|
85920
86351
|
const detect = opts.detectComposeV2 ?? detectComposeV2;
|
|
@@ -85925,7 +86356,7 @@ function runApplyPreflight(config, opts = {}) {
|
|
|
85925
86356
|
detectAndReportLegacyGdriveSlots(vaultPath);
|
|
85926
86357
|
}
|
|
85927
86358
|
function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
85928
|
-
if (!
|
|
86359
|
+
if (!existsSync77(vaultPath))
|
|
85929
86360
|
return;
|
|
85930
86361
|
const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
85931
86362
|
if (!passphrase)
|
|
@@ -85966,8 +86397,8 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
85966
86397
|
}
|
|
85967
86398
|
function writeInstallTypeCache(homeDir = homedir46()) {
|
|
85968
86399
|
const ctx = detectInstallType();
|
|
85969
|
-
const dir =
|
|
85970
|
-
const out =
|
|
86400
|
+
const dir = join79(homeDir, ".switchroom");
|
|
86401
|
+
const out = join79(dir, "install-type.json");
|
|
85971
86402
|
const tmp = `${out}.tmp`;
|
|
85972
86403
|
mkdirSync44(dir, { recursive: true });
|
|
85973
86404
|
const payload = {
|
|
@@ -86034,17 +86465,17 @@ Applying switchroom config...
|
|
|
86034
86465
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
86035
86466
|
`));
|
|
86036
86467
|
try {
|
|
86037
|
-
installUpdatePromptHook(
|
|
86468
|
+
installUpdatePromptHook(join79(agentsDir, name));
|
|
86038
86469
|
} catch (hookErr) {
|
|
86039
86470
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
86040
86471
|
`));
|
|
86041
86472
|
}
|
|
86042
|
-
await refreshAgentConnectionHealth(config, name,
|
|
86473
|
+
await refreshAgentConnectionHealth(config, name, join79(agentsDir, name), {
|
|
86043
86474
|
vaultAclReader: connHealthVaultAclReader
|
|
86044
86475
|
});
|
|
86045
86476
|
try {
|
|
86046
86477
|
const uid = allocateAgentUid(name);
|
|
86047
|
-
alignAgentUid(name,
|
|
86478
|
+
alignAgentUid(name, join79(agentsDir, name), uid, {
|
|
86048
86479
|
confirm: !options.nonInteractive,
|
|
86049
86480
|
writeOut
|
|
86050
86481
|
});
|
|
@@ -86089,7 +86520,7 @@ Applying switchroom config...
|
|
|
86089
86520
|
for (const name of agentNames) {
|
|
86090
86521
|
try {
|
|
86091
86522
|
const uid = allocateAgentUid(name);
|
|
86092
|
-
alignAgentUid(name,
|
|
86523
|
+
alignAgentUid(name, join79(agentsDir, name), uid, {
|
|
86093
86524
|
confirm: !options.nonInteractive,
|
|
86094
86525
|
writeOut
|
|
86095
86526
|
});
|
|
@@ -86230,8 +86661,8 @@ function copyExampleConfig2(name) {
|
|
|
86230
86661
|
if (!/^[a-z0-9_-]+$/.test(name)) {
|
|
86231
86662
|
throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
|
|
86232
86663
|
}
|
|
86233
|
-
const dest =
|
|
86234
|
-
if (
|
|
86664
|
+
const dest = resolve48(process.cwd(), "switchroom.yaml");
|
|
86665
|
+
if (existsSync77(dest)) {
|
|
86235
86666
|
console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
|
|
86236
86667
|
return;
|
|
86237
86668
|
}
|
|
@@ -86241,8 +86672,8 @@ function copyExampleConfig2(name) {
|
|
|
86241
86672
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
86242
86673
|
return;
|
|
86243
86674
|
}
|
|
86244
|
-
const exampleFile =
|
|
86245
|
-
if (!
|
|
86675
|
+
const exampleFile = resolve48(import.meta.dirname, `../../examples/${name}.yaml`);
|
|
86676
|
+
if (!existsSync77(exampleFile)) {
|
|
86246
86677
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
86247
86678
|
}
|
|
86248
86679
|
copyFileSync11(exampleFile, dest);
|
|
@@ -86253,8 +86684,8 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
86253
86684
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
86254
86685
|
const unwritable = [];
|
|
86255
86686
|
for (const name of targets) {
|
|
86256
|
-
const startSh =
|
|
86257
|
-
if (!
|
|
86687
|
+
const startSh = join79(agentsDir, name, "start.sh");
|
|
86688
|
+
if (!existsSync77(startSh))
|
|
86258
86689
|
continue;
|
|
86259
86690
|
try {
|
|
86260
86691
|
accessSync3(startSh, fsConstants6.W_OK);
|
|
@@ -86450,8 +86881,8 @@ function runRedactStdin() {
|
|
|
86450
86881
|
}
|
|
86451
86882
|
|
|
86452
86883
|
// src/cli/status-ask.ts
|
|
86453
|
-
import { readFileSync as
|
|
86454
|
-
import { join as
|
|
86884
|
+
import { readFileSync as readFileSync67, existsSync as existsSync78, readdirSync as readdirSync28 } from "node:fs";
|
|
86885
|
+
import { join as join80 } from "node:path";
|
|
86455
86886
|
import { homedir as homedir47 } from "node:os";
|
|
86456
86887
|
|
|
86457
86888
|
// src/status-ask/report.ts
|
|
@@ -86726,7 +87157,7 @@ function runReport(opts) {
|
|
|
86726
87157
|
for (const src of sources) {
|
|
86727
87158
|
let content;
|
|
86728
87159
|
try {
|
|
86729
|
-
content =
|
|
87160
|
+
content = readFileSync67(src.path, "utf-8");
|
|
86730
87161
|
} catch (err) {
|
|
86731
87162
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
86732
87163
|
`);
|
|
@@ -86773,7 +87204,7 @@ function runReport(opts) {
|
|
|
86773
87204
|
function resolveSources(explicitPath) {
|
|
86774
87205
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
86775
87206
|
const trimmed = explicitPath.trim();
|
|
86776
|
-
if (!
|
|
87207
|
+
if (!existsSync78(trimmed)) {
|
|
86777
87208
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
86778
87209
|
`);
|
|
86779
87210
|
process.exit(1);
|
|
@@ -86787,9 +87218,9 @@ function resolveSources(explicitPath) {
|
|
|
86787
87218
|
const config = loadConfig();
|
|
86788
87219
|
agentsDir = resolveAgentsDir(config);
|
|
86789
87220
|
} catch {
|
|
86790
|
-
agentsDir =
|
|
87221
|
+
agentsDir = join80(homedir47(), ".switchroom", "agents");
|
|
86791
87222
|
}
|
|
86792
|
-
if (!
|
|
87223
|
+
if (!existsSync78(agentsDir))
|
|
86793
87224
|
return [];
|
|
86794
87225
|
const sources = [];
|
|
86795
87226
|
let entries;
|
|
@@ -86799,8 +87230,8 @@ function resolveSources(explicitPath) {
|
|
|
86799
87230
|
return [];
|
|
86800
87231
|
}
|
|
86801
87232
|
for (const name of entries) {
|
|
86802
|
-
const path8 =
|
|
86803
|
-
if (
|
|
87233
|
+
const path8 = join80(agentsDir, name, "runtime-metrics.jsonl");
|
|
87234
|
+
if (existsSync78(path8)) {
|
|
86804
87235
|
sources.push({ path: path8, agent: name });
|
|
86805
87236
|
}
|
|
86806
87237
|
}
|
|
@@ -86826,32 +87257,32 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
86826
87257
|
init_paths();
|
|
86827
87258
|
import {
|
|
86828
87259
|
closeSync as closeSync13,
|
|
86829
|
-
existsSync as
|
|
87260
|
+
existsSync as existsSync79,
|
|
86830
87261
|
fsyncSync as fsyncSync6,
|
|
86831
87262
|
mkdirSync as mkdirSync45,
|
|
86832
87263
|
openSync as openSync13,
|
|
86833
87264
|
readdirSync as readdirSync29,
|
|
86834
|
-
readFileSync as
|
|
87265
|
+
readFileSync as readFileSync68,
|
|
86835
87266
|
renameSync as renameSync16,
|
|
86836
87267
|
statSync as statSync33,
|
|
86837
87268
|
unlinkSync as unlinkSync14,
|
|
86838
87269
|
writeSync as writeSync8
|
|
86839
87270
|
} from "node:fs";
|
|
86840
|
-
import { join as
|
|
87271
|
+
import { join as join81, resolve as resolve49 } from "node:path";
|
|
86841
87272
|
var STAGING_SUBDIR = ".staging";
|
|
86842
87273
|
function overlayPathsFor(agent, opts = {}) {
|
|
86843
|
-
const base = opts.root ?
|
|
86844
|
-
const scheduleDir =
|
|
86845
|
-
const scheduleStagingDir =
|
|
86846
|
-
const skillsDir =
|
|
86847
|
-
const skillsStagingDir =
|
|
87274
|
+
const base = opts.root ? resolve49(opts.root, agent) : resolve49(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
87275
|
+
const scheduleDir = join81(base, "schedule.d");
|
|
87276
|
+
const scheduleStagingDir = join81(scheduleDir, STAGING_SUBDIR);
|
|
87277
|
+
const skillsDir = join81(base, "skills.d");
|
|
87278
|
+
const skillsStagingDir = join81(skillsDir, STAGING_SUBDIR);
|
|
86848
87279
|
return {
|
|
86849
87280
|
agentRoot: base,
|
|
86850
87281
|
scheduleDir,
|
|
86851
87282
|
scheduleStagingDir,
|
|
86852
87283
|
skillsDir,
|
|
86853
87284
|
skillsStagingDir,
|
|
86854
|
-
lockPath:
|
|
87285
|
+
lockPath: join81(base, ".lock"),
|
|
86855
87286
|
stagingDir: scheduleStagingDir
|
|
86856
87287
|
};
|
|
86857
87288
|
}
|
|
@@ -86905,8 +87336,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
86905
87336
|
const paths = overlayPathsFor(agent, opts);
|
|
86906
87337
|
return withAgentLock(paths, () => {
|
|
86907
87338
|
ensureDirs(paths);
|
|
86908
|
-
const stagingPath =
|
|
86909
|
-
const finalPath =
|
|
87339
|
+
const stagingPath = join81(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
87340
|
+
const finalPath = join81(paths.scheduleDir, `${slug}.yaml`);
|
|
86910
87341
|
const fd = openSync13(stagingPath, "w", 384);
|
|
86911
87342
|
try {
|
|
86912
87343
|
writeSync8(fd, yamlText);
|
|
@@ -86922,8 +87353,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
86922
87353
|
const paths = overlayPathsFor(agent, opts);
|
|
86923
87354
|
return withAgentLock(paths, () => {
|
|
86924
87355
|
ensureSkillsDirs(paths);
|
|
86925
|
-
const stagingPath =
|
|
86926
|
-
const finalPath =
|
|
87356
|
+
const stagingPath = join81(paths.skillsStagingDir, `${slug}.yaml`);
|
|
87357
|
+
const finalPath = join81(paths.skillsDir, `${slug}.yaml`);
|
|
86927
87358
|
const fd = openSync13(stagingPath, "w", 384);
|
|
86928
87359
|
try {
|
|
86929
87360
|
writeSync8(fd, yamlText);
|
|
@@ -86938,8 +87369,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
86938
87369
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
86939
87370
|
const paths = overlayPathsFor(agent, opts);
|
|
86940
87371
|
return withAgentLock(paths, () => {
|
|
86941
|
-
const finalPath =
|
|
86942
|
-
if (!
|
|
87372
|
+
const finalPath = join81(paths.skillsDir, `${slug}.yaml`);
|
|
87373
|
+
if (!existsSync79(finalPath))
|
|
86943
87374
|
return false;
|
|
86944
87375
|
unlinkSync14(finalPath);
|
|
86945
87376
|
return true;
|
|
@@ -86947,15 +87378,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
86947
87378
|
}
|
|
86948
87379
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
86949
87380
|
const paths = overlayPathsFor(agent, opts);
|
|
86950
|
-
if (!
|
|
87381
|
+
if (!existsSync79(paths.skillsDir))
|
|
86951
87382
|
return [];
|
|
86952
87383
|
const out = [];
|
|
86953
87384
|
for (const name of readdirSync29(paths.skillsDir)) {
|
|
86954
87385
|
if (!/\.ya?ml$/i.test(name))
|
|
86955
87386
|
continue;
|
|
86956
|
-
const full =
|
|
87387
|
+
const full = join81(paths.skillsDir, name);
|
|
86957
87388
|
try {
|
|
86958
|
-
const raw =
|
|
87389
|
+
const raw = readFileSync68(full, "utf-8");
|
|
86959
87390
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
86960
87391
|
out.push({ slug, path: full, raw });
|
|
86961
87392
|
} catch {}
|
|
@@ -86965,8 +87396,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
86965
87396
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
86966
87397
|
const paths = overlayPathsFor(agent, opts);
|
|
86967
87398
|
return withAgentLock(paths, () => {
|
|
86968
|
-
const finalPath =
|
|
86969
|
-
if (!
|
|
87399
|
+
const finalPath = join81(paths.scheduleDir, `${slug}.yaml`);
|
|
87400
|
+
if (!existsSync79(finalPath))
|
|
86970
87401
|
return false;
|
|
86971
87402
|
unlinkSync14(finalPath);
|
|
86972
87403
|
return true;
|
|
@@ -86974,15 +87405,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
86974
87405
|
}
|
|
86975
87406
|
function listOverlayEntries(agent, opts = {}) {
|
|
86976
87407
|
const paths = overlayPathsFor(agent, opts);
|
|
86977
|
-
if (!
|
|
87408
|
+
if (!existsSync79(paths.scheduleDir))
|
|
86978
87409
|
return [];
|
|
86979
87410
|
const out = [];
|
|
86980
87411
|
for (const name of readdirSync29(paths.scheduleDir)) {
|
|
86981
87412
|
if (!/\.ya?ml$/i.test(name))
|
|
86982
87413
|
continue;
|
|
86983
|
-
const full =
|
|
87414
|
+
const full = join81(paths.scheduleDir, name);
|
|
86984
87415
|
try {
|
|
86985
|
-
const raw =
|
|
87416
|
+
const raw = readFileSync68(full, "utf-8");
|
|
86986
87417
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
86987
87418
|
out.push({ slug, path: full, raw });
|
|
86988
87419
|
} catch {}
|
|
@@ -87125,23 +87556,23 @@ function reconcileAgentCronOnly(agent) {
|
|
|
87125
87556
|
// src/cli/agent-config-pending.ts
|
|
87126
87557
|
import {
|
|
87127
87558
|
closeSync as closeSync14,
|
|
87128
|
-
existsSync as
|
|
87559
|
+
existsSync as existsSync80,
|
|
87129
87560
|
fsyncSync as fsyncSync7,
|
|
87130
87561
|
mkdirSync as mkdirSync46,
|
|
87131
87562
|
openSync as openSync14,
|
|
87132
87563
|
readdirSync as readdirSync30,
|
|
87133
|
-
readFileSync as
|
|
87564
|
+
readFileSync as readFileSync69,
|
|
87134
87565
|
renameSync as renameSync17,
|
|
87135
87566
|
unlinkSync as unlinkSync15,
|
|
87136
87567
|
writeFileSync as writeFileSync39,
|
|
87137
87568
|
writeSync as writeSync9
|
|
87138
87569
|
} from "node:fs";
|
|
87139
|
-
import { join as
|
|
87570
|
+
import { join as join82 } from "node:path";
|
|
87140
87571
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
87141
87572
|
var STAGE_ID_PREFIX = "cap_";
|
|
87142
87573
|
function pendingDir(agent, opts = {}) {
|
|
87143
87574
|
const paths = overlayPathsFor(agent, opts);
|
|
87144
|
-
return
|
|
87575
|
+
return join82(paths.scheduleDir, ".pending");
|
|
87145
87576
|
}
|
|
87146
87577
|
function ensurePendingDir(agent, opts = {}) {
|
|
87147
87578
|
const dir = pendingDir(agent, opts);
|
|
@@ -87154,8 +87585,8 @@ function newStageId() {
|
|
|
87154
87585
|
function stagePendingScheduleEntry(opts) {
|
|
87155
87586
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
87156
87587
|
const stageId = opts.stageId ?? newStageId();
|
|
87157
|
-
const yamlPath =
|
|
87158
|
-
const metaPath =
|
|
87588
|
+
const yamlPath = join82(dir, `${stageId}.yaml`);
|
|
87589
|
+
const metaPath = join82(dir, `${stageId}.meta.json`);
|
|
87159
87590
|
const meta = {
|
|
87160
87591
|
v: 1,
|
|
87161
87592
|
stage_id: stageId,
|
|
@@ -87182,19 +87613,19 @@ function stagePendingScheduleEntry(opts) {
|
|
|
87182
87613
|
}
|
|
87183
87614
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
87184
87615
|
const dir = pendingDir(agent, opts);
|
|
87185
|
-
if (!
|
|
87616
|
+
if (!existsSync80(dir))
|
|
87186
87617
|
return [];
|
|
87187
87618
|
const out = [];
|
|
87188
87619
|
for (const name of readdirSync30(dir).sort()) {
|
|
87189
87620
|
if (!name.endsWith(".meta.json"))
|
|
87190
87621
|
continue;
|
|
87191
87622
|
const stageId = name.slice(0, -".meta.json".length);
|
|
87192
|
-
const metaPath =
|
|
87193
|
-
const yamlPath =
|
|
87194
|
-
if (!
|
|
87623
|
+
const metaPath = join82(dir, name);
|
|
87624
|
+
const yamlPath = join82(dir, `${stageId}.yaml`);
|
|
87625
|
+
if (!existsSync80(yamlPath))
|
|
87195
87626
|
continue;
|
|
87196
87627
|
try {
|
|
87197
|
-
const meta = JSON.parse(
|
|
87628
|
+
const meta = JSON.parse(readFileSync69(metaPath, "utf-8"));
|
|
87198
87629
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
87199
87630
|
continue;
|
|
87200
87631
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -87209,8 +87640,8 @@ function commitPendingScheduleEntry(opts) {
|
|
|
87209
87640
|
return { committed: false, reason: "not_found" };
|
|
87210
87641
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
87211
87642
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
87212
|
-
const finalPath =
|
|
87213
|
-
if (
|
|
87643
|
+
const finalPath = join82(paths.scheduleDir, `${slug}.yaml`);
|
|
87644
|
+
if (existsSync80(finalPath)) {
|
|
87214
87645
|
return { committed: false, reason: "slug_collision" };
|
|
87215
87646
|
}
|
|
87216
87647
|
renameSync17(match.yamlPath, finalPath);
|
|
@@ -87232,7 +87663,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
87232
87663
|
}
|
|
87233
87664
|
|
|
87234
87665
|
// src/cli/agent-config-write.ts
|
|
87235
|
-
import { existsSync as
|
|
87666
|
+
import { existsSync as existsSync81, readFileSync as readFileSync70 } from "node:fs";
|
|
87236
87667
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
87237
87668
|
|
|
87238
87669
|
// src/scheduler/schedule-report.ts
|
|
@@ -87603,8 +88034,8 @@ function scheduleRemove(opts) {
|
|
|
87603
88034
|
}
|
|
87604
88035
|
let priorContent = null;
|
|
87605
88036
|
try {
|
|
87606
|
-
if (
|
|
87607
|
-
priorContent =
|
|
88037
|
+
if (existsSync81(match.path))
|
|
88038
|
+
priorContent = readFileSync70(match.path, "utf-8");
|
|
87608
88039
|
} catch {}
|
|
87609
88040
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
87610
88041
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -87807,7 +88238,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
87807
88238
|
}
|
|
87808
88239
|
let blob;
|
|
87809
88240
|
if (opts.jsonl) {
|
|
87810
|
-
blob =
|
|
88241
|
+
blob = existsSync81(opts.jsonl) ? readFileSync70(opts.jsonl, "utf-8") : "";
|
|
87811
88242
|
} else {
|
|
87812
88243
|
try {
|
|
87813
88244
|
blob = execFileSync26("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -87839,10 +88270,10 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
87839
88270
|
|
|
87840
88271
|
// src/cli/agent-config-skill-write.ts
|
|
87841
88272
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
87842
|
-
import { existsSync as
|
|
88273
|
+
import { existsSync as existsSync82 } from "node:fs";
|
|
87843
88274
|
init_reconcile_default_skills();
|
|
87844
88275
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
87845
|
-
import { join as
|
|
88276
|
+
import { join as join83 } from "node:path";
|
|
87846
88277
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
87847
88278
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
87848
88279
|
function exitCodeFor2(code) {
|
|
@@ -87917,8 +88348,8 @@ function skillInstall(opts) {
|
|
|
87917
88348
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
87918
88349
|
}
|
|
87919
88350
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
87920
|
-
const skillPath =
|
|
87921
|
-
if (!
|
|
88351
|
+
const skillPath = join83(poolDir, skillName);
|
|
88352
|
+
if (!existsSync82(skillPath)) {
|
|
87922
88353
|
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.`);
|
|
87923
88354
|
}
|
|
87924
88355
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -88082,12 +88513,12 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
88082
88513
|
// src/cli/skill.ts
|
|
88083
88514
|
import {
|
|
88084
88515
|
closeSync as closeSync15,
|
|
88085
|
-
existsSync as
|
|
88516
|
+
existsSync as existsSync83,
|
|
88086
88517
|
lstatSync as lstatSync9,
|
|
88087
88518
|
mkdirSync as mkdirSync47,
|
|
88088
88519
|
mkdtempSync as mkdtempSync5,
|
|
88089
88520
|
openSync as openSync15,
|
|
88090
|
-
readFileSync as
|
|
88521
|
+
readFileSync as readFileSync71,
|
|
88091
88522
|
readdirSync as readdirSync31,
|
|
88092
88523
|
realpathSync as realpathSync7,
|
|
88093
88524
|
renameSync as renameSync18,
|
|
@@ -88096,7 +88527,7 @@ import {
|
|
|
88096
88527
|
writeFileSync as writeFileSync40
|
|
88097
88528
|
} from "node:fs";
|
|
88098
88529
|
import { tmpdir as tmpdir5, homedir as homedir48 } from "node:os";
|
|
88099
|
-
import { dirname as dirname25, join as
|
|
88530
|
+
import { dirname as dirname25, join as join84, relative as relative2, resolve as resolve50 } from "node:path";
|
|
88100
88531
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
88101
88532
|
|
|
88102
88533
|
// src/cli/skill-common.ts
|
|
@@ -88290,11 +88721,11 @@ function scanForClaudeP2(content) {
|
|
|
88290
88721
|
function resolveSkillsPoolDir2(override) {
|
|
88291
88722
|
const raw = override ?? "~/.switchroom/skills";
|
|
88292
88723
|
if (raw.startsWith("~/")) {
|
|
88293
|
-
return
|
|
88724
|
+
return join84(homedir48(), raw.slice(2));
|
|
88294
88725
|
}
|
|
88295
88726
|
if (raw === "~")
|
|
88296
88727
|
return homedir48();
|
|
88297
|
-
return
|
|
88728
|
+
return resolve50(raw);
|
|
88298
88729
|
}
|
|
88299
88730
|
function readStdinSync() {
|
|
88300
88731
|
const chunks = [];
|
|
@@ -88329,7 +88760,7 @@ function loadFromDir(dir) {
|
|
|
88329
88760
|
const walk2 = (sub) => {
|
|
88330
88761
|
const entries = readdirSync31(sub, { withFileTypes: true });
|
|
88331
88762
|
for (const ent of entries) {
|
|
88332
|
-
const full =
|
|
88763
|
+
const full = join84(sub, ent.name);
|
|
88333
88764
|
const rel = relative2(abs, full);
|
|
88334
88765
|
if (ent.isSymbolicLink()) {
|
|
88335
88766
|
fail3(`refusing to read symlink inside --from dir: ${rel}`);
|
|
@@ -88339,7 +88770,7 @@ function loadFromDir(dir) {
|
|
|
88339
88770
|
continue;
|
|
88340
88771
|
}
|
|
88341
88772
|
if (ent.isFile()) {
|
|
88342
|
-
const buf =
|
|
88773
|
+
const buf = readFileSync71(full);
|
|
88343
88774
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
88344
88775
|
}
|
|
88345
88776
|
}
|
|
@@ -88364,7 +88795,7 @@ function loadFromTarball(tarPath) {
|
|
|
88364
88795
|
fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
88365
88796
|
}
|
|
88366
88797
|
}
|
|
88367
|
-
const staging = mkdtempSync5(
|
|
88798
|
+
const staging = mkdtempSync5(join84(tmpdir5(), "skill-apply-extract-"));
|
|
88368
88799
|
try {
|
|
88369
88800
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
88370
88801
|
const r = spawnSync14("tar", [
|
|
@@ -88388,7 +88819,7 @@ function loadFromTarball(tarPath) {
|
|
|
88388
88819
|
}
|
|
88389
88820
|
}
|
|
88390
88821
|
function loadSingleFile(filePath) {
|
|
88391
|
-
const content =
|
|
88822
|
+
const content = readFileSync71(filePath, "utf-8");
|
|
88392
88823
|
return { "SKILL.md": content };
|
|
88393
88824
|
}
|
|
88394
88825
|
function loadFromStdin() {
|
|
@@ -88450,8 +88881,8 @@ function validatePayload(name, files) {
|
|
|
88450
88881
|
errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
88451
88882
|
}
|
|
88452
88883
|
} else if (PY_SCRIPT_RE2.test(path8)) {
|
|
88453
|
-
const tmp = mkdtempSync5(
|
|
88454
|
-
const tmpPy =
|
|
88884
|
+
const tmp = mkdtempSync5(join84(tmpdir5(), "skill-apply-py-"));
|
|
88885
|
+
const tmpPy = join84(tmp, "check.py");
|
|
88455
88886
|
try {
|
|
88456
88887
|
writeFileSync40(tmpPy, content);
|
|
88457
88888
|
const r = spawnSync14("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -88471,15 +88902,15 @@ function validatePayload(name, files) {
|
|
|
88471
88902
|
function diffSummary(currentDir, files) {
|
|
88472
88903
|
const lines = [];
|
|
88473
88904
|
const currentFiles = {};
|
|
88474
|
-
if (
|
|
88905
|
+
if (existsSync83(currentDir)) {
|
|
88475
88906
|
const walk2 = (sub) => {
|
|
88476
88907
|
for (const ent of readdirSync31(sub, { withFileTypes: true })) {
|
|
88477
|
-
const full =
|
|
88908
|
+
const full = join84(sub, ent.name);
|
|
88478
88909
|
const rel = relative2(currentDir, full);
|
|
88479
88910
|
if (ent.isDirectory()) {
|
|
88480
88911
|
walk2(full);
|
|
88481
88912
|
} else if (ent.isFile()) {
|
|
88482
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
88913
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync71(full, "utf-8");
|
|
88483
88914
|
}
|
|
88484
88915
|
}
|
|
88485
88916
|
};
|
|
@@ -88507,10 +88938,10 @@ function diffSummary(currentDir, files) {
|
|
|
88507
88938
|
`);
|
|
88508
88939
|
}
|
|
88509
88940
|
function writePayload(poolDir, name, files) {
|
|
88510
|
-
if (!
|
|
88941
|
+
if (!existsSync83(poolDir)) {
|
|
88511
88942
|
mkdirSync47(poolDir, { recursive: true, mode: 493 });
|
|
88512
88943
|
}
|
|
88513
|
-
const target =
|
|
88944
|
+
const target = join84(poolDir, name);
|
|
88514
88945
|
let targetIsSymlink = false;
|
|
88515
88946
|
try {
|
|
88516
88947
|
const st = lstatSync9(target);
|
|
@@ -88521,11 +88952,11 @@ function writePayload(poolDir, name, files) {
|
|
|
88521
88952
|
if (targetIsSymlink) {
|
|
88522
88953
|
fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
88523
88954
|
}
|
|
88524
|
-
const staging = mkdtempSync5(
|
|
88955
|
+
const staging = mkdtempSync5(join84(poolDir, `.skill-apply-stage-${name}-`));
|
|
88525
88956
|
let oldRename = null;
|
|
88526
88957
|
try {
|
|
88527
88958
|
for (const [path8, content] of Object.entries(files)) {
|
|
88528
|
-
const full =
|
|
88959
|
+
const full = join84(staging, path8);
|
|
88529
88960
|
mkdirSync47(dirname25(full), { recursive: true, mode: 493 });
|
|
88530
88961
|
const fd = openSync15(full, "wx");
|
|
88531
88962
|
try {
|
|
@@ -88556,9 +88987,9 @@ function writePayload(poolDir, name, files) {
|
|
|
88556
88987
|
try {
|
|
88557
88988
|
rmSync17(staging, { recursive: true, force: true });
|
|
88558
88989
|
} catch {}
|
|
88559
|
-
if (oldRename &&
|
|
88990
|
+
if (oldRename && existsSync83(oldRename)) {
|
|
88560
88991
|
try {
|
|
88561
|
-
if (
|
|
88992
|
+
if (existsSync83(target)) {
|
|
88562
88993
|
rmSync17(target, { recursive: true, force: true });
|
|
88563
88994
|
}
|
|
88564
88995
|
renameSync18(oldRename, target);
|
|
@@ -88578,8 +89009,8 @@ function registerSkillCommand(program3) {
|
|
|
88578
89009
|
if (opts.from === undefined) {
|
|
88579
89010
|
files = loadFromStdin();
|
|
88580
89011
|
} else {
|
|
88581
|
-
const fromPath =
|
|
88582
|
-
if (!
|
|
89012
|
+
const fromPath = resolve50(opts.from);
|
|
89013
|
+
if (!existsSync83(fromPath)) {
|
|
88583
89014
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
88584
89015
|
}
|
|
88585
89016
|
const st = statSync34(fromPath);
|
|
@@ -88603,7 +89034,7 @@ function registerSkillCommand(program3) {
|
|
|
88603
89034
|
}
|
|
88604
89035
|
const config = loadConfig();
|
|
88605
89036
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
88606
|
-
const currentDir =
|
|
89037
|
+
const currentDir = join84(poolDir, name);
|
|
88607
89038
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
88608
89039
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
88609
89040
|
console.log(diffSummary(currentDir, files));
|
|
@@ -88634,12 +89065,12 @@ function sumBytes(files) {
|
|
|
88634
89065
|
// src/cli/skill-personal.ts
|
|
88635
89066
|
import {
|
|
88636
89067
|
closeSync as closeSync16,
|
|
88637
|
-
existsSync as
|
|
89068
|
+
existsSync as existsSync84,
|
|
88638
89069
|
lstatSync as lstatSync10,
|
|
88639
89070
|
mkdirSync as mkdirSync48,
|
|
88640
89071
|
mkdtempSync as mkdtempSync6,
|
|
88641
89072
|
openSync as openSync16,
|
|
88642
|
-
readFileSync as
|
|
89073
|
+
readFileSync as readFileSync72,
|
|
88643
89074
|
readdirSync as readdirSync32,
|
|
88644
89075
|
renameSync as renameSync19,
|
|
88645
89076
|
rmSync as rmSync18,
|
|
@@ -88647,7 +89078,7 @@ import {
|
|
|
88647
89078
|
utimesSync,
|
|
88648
89079
|
writeFileSync as writeFileSync41
|
|
88649
89080
|
} from "node:fs";
|
|
88650
|
-
import { dirname as dirname26, join as
|
|
89081
|
+
import { dirname as dirname26, join as join85, relative as relative3, resolve as resolve51 } from "node:path";
|
|
88651
89082
|
import { homedir as homedir49, tmpdir as tmpdir6 } from "node:os";
|
|
88652
89083
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
88653
89084
|
init_helpers();
|
|
@@ -88658,15 +89089,15 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
88658
89089
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
88659
89090
|
function resolveConfigSkillsDir(agent) {
|
|
88660
89091
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
88661
|
-
const candidate = override ?
|
|
88662
|
-
if (!
|
|
89092
|
+
const candidate = override ? resolve51(override) : join85(homedir49(), ".switchroom-config");
|
|
89093
|
+
if (!existsSync84(candidate))
|
|
88663
89094
|
return null;
|
|
88664
|
-
return
|
|
89095
|
+
return join85(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
88665
89096
|
}
|
|
88666
89097
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
88667
89098
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
88668
89099
|
try {
|
|
88669
|
-
if (!
|
|
89100
|
+
if (!existsSync84(configSkillsRoot))
|
|
88670
89101
|
return;
|
|
88671
89102
|
const now = Date.now();
|
|
88672
89103
|
for (const ent of readdirSync32(configSkillsRoot)) {
|
|
@@ -88679,7 +89110,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
88679
89110
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
88680
89111
|
continue;
|
|
88681
89112
|
try {
|
|
88682
|
-
rmSync18(
|
|
89113
|
+
rmSync18(join85(configSkillsRoot, ent), { recursive: true, force: true });
|
|
88683
89114
|
} catch {}
|
|
88684
89115
|
}
|
|
88685
89116
|
} catch {}
|
|
@@ -88688,7 +89119,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
88688
89119
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
88689
89120
|
if (!configSkillsRoot)
|
|
88690
89121
|
return;
|
|
88691
|
-
const dest =
|
|
89122
|
+
const dest = join85(configSkillsRoot, name);
|
|
88692
89123
|
try {
|
|
88693
89124
|
if (liveSkillDir !== null) {
|
|
88694
89125
|
try {
|
|
@@ -88702,32 +89133,32 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
88702
89133
|
}
|
|
88703
89134
|
if (liveSkillDir === null) {
|
|
88704
89135
|
sweepMirrorPriors(configSkillsRoot);
|
|
88705
|
-
if (
|
|
88706
|
-
const trash =
|
|
89136
|
+
if (existsSync84(dest)) {
|
|
89137
|
+
const trash = join85(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
88707
89138
|
renameSync19(dest, trash);
|
|
88708
89139
|
}
|
|
88709
89140
|
return;
|
|
88710
89141
|
}
|
|
88711
89142
|
mkdirSync48(configSkillsRoot, { recursive: true, mode: 493 });
|
|
88712
89143
|
sweepMirrorPriors(configSkillsRoot);
|
|
88713
|
-
const staging = mkdtempSync6(
|
|
89144
|
+
const staging = mkdtempSync6(join85(configSkillsRoot, `.${name}-staging-`));
|
|
88714
89145
|
const walk2 = (src, dst) => {
|
|
88715
89146
|
mkdirSync48(dst, { recursive: true, mode: 493 });
|
|
88716
89147
|
for (const ent of readdirSync32(src, { withFileTypes: true })) {
|
|
88717
|
-
const s =
|
|
88718
|
-
const d =
|
|
89148
|
+
const s = join85(src, ent.name);
|
|
89149
|
+
const d = join85(dst, ent.name);
|
|
88719
89150
|
if (ent.isSymbolicLink())
|
|
88720
89151
|
continue;
|
|
88721
89152
|
if (ent.isDirectory())
|
|
88722
89153
|
walk2(s, d);
|
|
88723
89154
|
else if (ent.isFile()) {
|
|
88724
|
-
writeFileSync41(d,
|
|
89155
|
+
writeFileSync41(d, readFileSync72(s));
|
|
88725
89156
|
}
|
|
88726
89157
|
}
|
|
88727
89158
|
};
|
|
88728
89159
|
walk2(liveSkillDir, staging);
|
|
88729
|
-
if (
|
|
88730
|
-
const prior =
|
|
89160
|
+
if (existsSync84(dest)) {
|
|
89161
|
+
const prior = join85(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
88731
89162
|
renameSync19(dest, prior);
|
|
88732
89163
|
}
|
|
88733
89164
|
renameSync19(staging, dest);
|
|
@@ -88753,14 +89184,14 @@ function resolveAgent(opts) {
|
|
|
88753
89184
|
}
|
|
88754
89185
|
function resolveAgentsRoot(opts) {
|
|
88755
89186
|
if (opts.root)
|
|
88756
|
-
return
|
|
88757
|
-
return
|
|
89187
|
+
return resolve51(opts.root);
|
|
89188
|
+
return join85(homedir49(), ".switchroom", "agents");
|
|
88758
89189
|
}
|
|
88759
89190
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
88760
|
-
return
|
|
89191
|
+
return join85(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
88761
89192
|
}
|
|
88762
89193
|
function trashDir(agentsRoot, agent) {
|
|
88763
|
-
return
|
|
89194
|
+
return join85(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
88764
89195
|
}
|
|
88765
89196
|
function readStdinSync2() {
|
|
88766
89197
|
const chunks = [];
|
|
@@ -88783,14 +89214,14 @@ function readStdinSync2() {
|
|
|
88783
89214
|
return Buffer.concat(chunks).toString("utf-8");
|
|
88784
89215
|
}
|
|
88785
89216
|
function loadFromDir2(dir) {
|
|
88786
|
-
const abs =
|
|
89217
|
+
const abs = resolve51(dir);
|
|
88787
89218
|
if (!statSync35(abs).isDirectory()) {
|
|
88788
89219
|
fail4(`--from path is not a directory: ${dir}`);
|
|
88789
89220
|
}
|
|
88790
89221
|
const files = {};
|
|
88791
89222
|
const walk2 = (sub) => {
|
|
88792
89223
|
for (const ent of readdirSync32(sub, { withFileTypes: true })) {
|
|
88793
|
-
const full =
|
|
89224
|
+
const full = join85(sub, ent.name);
|
|
88794
89225
|
if (ent.isSymbolicLink()) {
|
|
88795
89226
|
fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
|
|
88796
89227
|
}
|
|
@@ -88800,7 +89231,7 @@ function loadFromDir2(dir) {
|
|
|
88800
89231
|
}
|
|
88801
89232
|
if (ent.isFile()) {
|
|
88802
89233
|
const rel = relative3(abs, full).replace(/\\/g, "/");
|
|
88803
|
-
files[rel] =
|
|
89234
|
+
files[rel] = readFileSync72(full, "utf-8");
|
|
88804
89235
|
}
|
|
88805
89236
|
}
|
|
88806
89237
|
};
|
|
@@ -88843,8 +89274,8 @@ function behavioralValidate(files) {
|
|
|
88843
89274
|
errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
88844
89275
|
}
|
|
88845
89276
|
} else if (PY_SCRIPT_RE.test(path8)) {
|
|
88846
|
-
const tmp = mkdtempSync6(
|
|
88847
|
-
const tmpPy =
|
|
89277
|
+
const tmp = mkdtempSync6(join85(tmpdir6(), "skill-personal-py-"));
|
|
89278
|
+
const tmpPy = join85(tmp, "check.py");
|
|
88848
89279
|
try {
|
|
88849
89280
|
writeFileSync41(tmpPy, content);
|
|
88850
89281
|
const r = spawnSync15("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -88862,13 +89293,13 @@ function behavioralValidate(files) {
|
|
|
88862
89293
|
}
|
|
88863
89294
|
function sweepTrash(agentsRoot, agent) {
|
|
88864
89295
|
const trash = trashDir(agentsRoot, agent);
|
|
88865
|
-
if (!
|
|
89296
|
+
if (!existsSync84(trash))
|
|
88866
89297
|
return;
|
|
88867
89298
|
const now = Date.now();
|
|
88868
89299
|
for (const ent of readdirSync32(trash, { withFileTypes: true })) {
|
|
88869
89300
|
if (!ent.isDirectory())
|
|
88870
89301
|
continue;
|
|
88871
|
-
const entPath =
|
|
89302
|
+
const entPath = join85(trash, ent.name);
|
|
88872
89303
|
try {
|
|
88873
89304
|
const st = statSync35(entPath);
|
|
88874
89305
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
@@ -88889,11 +89320,11 @@ function writePersonalSkill(targetDir, files) {
|
|
|
88889
89320
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
88890
89321
|
}
|
|
88891
89322
|
mkdirSync48(dirname26(targetDir), { recursive: true, mode: 493 });
|
|
88892
|
-
const staging = mkdtempSync6(
|
|
89323
|
+
const staging = mkdtempSync6(join85(dirname26(targetDir), `.skill-personal-stage-`));
|
|
88893
89324
|
let oldRename = null;
|
|
88894
89325
|
try {
|
|
88895
89326
|
for (const [path8, content] of Object.entries(files)) {
|
|
88896
|
-
const full =
|
|
89327
|
+
const full = join85(staging, path8);
|
|
88897
89328
|
mkdirSync48(dirname26(full), { recursive: true, mode: 493 });
|
|
88898
89329
|
const fd = openSync16(full, "wx");
|
|
88899
89330
|
try {
|
|
@@ -88924,9 +89355,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
88924
89355
|
try {
|
|
88925
89356
|
rmSync18(staging, { recursive: true, force: true });
|
|
88926
89357
|
} catch {}
|
|
88927
|
-
if (oldRename &&
|
|
89358
|
+
if (oldRename && existsSync84(oldRename)) {
|
|
88928
89359
|
try {
|
|
88929
|
-
if (
|
|
89360
|
+
if (existsSync84(targetDir)) {
|
|
88930
89361
|
rmSync18(targetDir, { recursive: true, force: true });
|
|
88931
89362
|
}
|
|
88932
89363
|
renameSync19(oldRename, targetDir);
|
|
@@ -88977,8 +89408,8 @@ function loadFiles(opts) {
|
|
|
88977
89408
|
if (opts.from === undefined) {
|
|
88978
89409
|
return loadFromStdin2();
|
|
88979
89410
|
}
|
|
88980
|
-
const p =
|
|
88981
|
-
if (!
|
|
89411
|
+
const p = resolve51(opts.from);
|
|
89412
|
+
if (!existsSync84(p)) {
|
|
88982
89413
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
88983
89414
|
}
|
|
88984
89415
|
const st = statSync35(p);
|
|
@@ -88986,7 +89417,7 @@ function loadFiles(opts) {
|
|
|
88986
89417
|
return loadFromDir2(p);
|
|
88987
89418
|
}
|
|
88988
89419
|
if (p.endsWith(".md")) {
|
|
88989
|
-
return { "SKILL.md":
|
|
89420
|
+
return { "SKILL.md": readFileSync72(p, "utf-8") };
|
|
88990
89421
|
}
|
|
88991
89422
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
88992
89423
|
}
|
|
@@ -89026,10 +89457,10 @@ function editPersonalAction(name, opts) {
|
|
|
89026
89457
|
}
|
|
89027
89458
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
89028
89459
|
function defaultSharedRoot() {
|
|
89029
|
-
return
|
|
89460
|
+
return join85(homedir49(), ".switchroom", "skills");
|
|
89030
89461
|
}
|
|
89031
89462
|
function defaultBundledRoot() {
|
|
89032
|
-
return
|
|
89463
|
+
return join85(homedir49(), ".switchroom", "skills", "_bundled");
|
|
89033
89464
|
}
|
|
89034
89465
|
function resolveCloneSource(source, opts) {
|
|
89035
89466
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -89039,8 +89470,8 @@ function resolveCloneSource(source, opts) {
|
|
|
89039
89470
|
const tier = m[1];
|
|
89040
89471
|
const slug = m[2];
|
|
89041
89472
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
89042
|
-
const dir =
|
|
89043
|
-
if (!
|
|
89473
|
+
const dir = join85(root, slug);
|
|
89474
|
+
if (!existsSync84(dir)) {
|
|
89044
89475
|
fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
89045
89476
|
}
|
|
89046
89477
|
const st = lstatSync10(dir);
|
|
@@ -89055,7 +89486,7 @@ function readSourceFiles(dir) {
|
|
|
89055
89486
|
const skipped = [];
|
|
89056
89487
|
const walk2 = (sub) => {
|
|
89057
89488
|
for (const ent of readdirSync32(sub, { withFileTypes: true })) {
|
|
89058
|
-
const full =
|
|
89489
|
+
const full = join85(sub, ent.name);
|
|
89059
89490
|
if (ent.isSymbolicLink()) {
|
|
89060
89491
|
continue;
|
|
89061
89492
|
}
|
|
@@ -89075,7 +89506,7 @@ function readSourceFiles(dir) {
|
|
|
89075
89506
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
89076
89507
|
}
|
|
89077
89508
|
} catch {}
|
|
89078
|
-
files[rel] =
|
|
89509
|
+
files[rel] = readFileSync72(full, "utf-8");
|
|
89079
89510
|
}
|
|
89080
89511
|
}
|
|
89081
89512
|
};
|
|
@@ -89166,7 +89597,7 @@ function removePersonalAction(name, opts) {
|
|
|
89166
89597
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
89167
89598
|
mkdirSync48(trashRoot2, { recursive: true, mode: 493 });
|
|
89168
89599
|
const ts = Date.now();
|
|
89169
|
-
const trashTarget =
|
|
89600
|
+
const trashTarget = join85(trashRoot2, `${name}-${ts}`);
|
|
89170
89601
|
renameSync19(target, trashTarget);
|
|
89171
89602
|
const now = new Date(ts);
|
|
89172
89603
|
utimesSync(trashTarget, now, now);
|
|
@@ -89185,16 +89616,16 @@ function listPersonalAction(opts) {
|
|
|
89185
89616
|
const agent = resolveAgent(opts);
|
|
89186
89617
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
89187
89618
|
sweepTrash(agentsRoot, agent);
|
|
89188
|
-
const skillsDir =
|
|
89619
|
+
const skillsDir = join85(agentsRoot, agent, ".claude", "skills");
|
|
89189
89620
|
const personal = [];
|
|
89190
|
-
if (
|
|
89621
|
+
if (existsSync84(skillsDir)) {
|
|
89191
89622
|
for (const ent of readdirSync32(skillsDir, { withFileTypes: true })) {
|
|
89192
89623
|
if (!ent.isDirectory())
|
|
89193
89624
|
continue;
|
|
89194
89625
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
89195
89626
|
continue;
|
|
89196
89627
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
89197
|
-
const skillPath =
|
|
89628
|
+
const skillPath = join85(skillsDir, ent.name);
|
|
89198
89629
|
let fileCount = 0;
|
|
89199
89630
|
let totalBytes = 0;
|
|
89200
89631
|
const walk2 = (sub) => {
|
|
@@ -89202,10 +89633,10 @@ function listPersonalAction(opts) {
|
|
|
89202
89633
|
if (e.isFile()) {
|
|
89203
89634
|
fileCount += 1;
|
|
89204
89635
|
try {
|
|
89205
|
-
totalBytes += statSync35(
|
|
89636
|
+
totalBytes += statSync35(join85(sub, e.name)).size;
|
|
89206
89637
|
} catch {}
|
|
89207
89638
|
} else if (e.isDirectory()) {
|
|
89208
|
-
walk2(
|
|
89639
|
+
walk2(join85(sub, e.name));
|
|
89209
89640
|
}
|
|
89210
89641
|
}
|
|
89211
89642
|
};
|
|
@@ -89244,28 +89675,28 @@ function registerSkillPersonalCommands(program3) {
|
|
|
89244
89675
|
// src/cli/skill-search.ts
|
|
89245
89676
|
init_helpers();
|
|
89246
89677
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
89247
|
-
import { existsSync as
|
|
89678
|
+
import { existsSync as existsSync85, readdirSync as readdirSync33, readFileSync as readFileSync73, statSync as statSync36 } from "node:fs";
|
|
89248
89679
|
import { homedir as homedir50 } from "node:os";
|
|
89249
|
-
import { join as
|
|
89680
|
+
import { join as join86, resolve as resolve52 } from "node:path";
|
|
89250
89681
|
var PERSONAL_PREFIX2 = "personal-";
|
|
89251
89682
|
var BUNDLED_SUBDIR = "_bundled";
|
|
89252
89683
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
89253
89684
|
function defaultAgentsRoot() {
|
|
89254
|
-
return
|
|
89685
|
+
return resolve52(homedir50(), ".switchroom/agents");
|
|
89255
89686
|
}
|
|
89256
89687
|
function defaultSharedRoot2() {
|
|
89257
|
-
return
|
|
89688
|
+
return resolve52(homedir50(), ".switchroom/skills");
|
|
89258
89689
|
}
|
|
89259
89690
|
function defaultBundledRoot2() {
|
|
89260
|
-
return
|
|
89691
|
+
return resolve52(homedir50(), ".switchroom/skills/_bundled");
|
|
89261
89692
|
}
|
|
89262
89693
|
function readSkillFrontmatter(skillDir) {
|
|
89263
|
-
const mdPath =
|
|
89264
|
-
if (!
|
|
89694
|
+
const mdPath = join86(skillDir, "SKILL.md");
|
|
89695
|
+
if (!existsSync85(mdPath))
|
|
89265
89696
|
return null;
|
|
89266
89697
|
let content;
|
|
89267
89698
|
try {
|
|
89268
|
-
content =
|
|
89699
|
+
content = readFileSync73(mdPath, "utf-8");
|
|
89269
89700
|
} catch {
|
|
89270
89701
|
return null;
|
|
89271
89702
|
}
|
|
@@ -89293,7 +89724,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
89293
89724
|
return { fm: parsed };
|
|
89294
89725
|
}
|
|
89295
89726
|
function statSkillMd(skillDir) {
|
|
89296
|
-
const mdPath =
|
|
89727
|
+
const mdPath = join86(skillDir, "SKILL.md");
|
|
89297
89728
|
try {
|
|
89298
89729
|
const st = statSync36(mdPath);
|
|
89299
89730
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
@@ -89304,8 +89735,8 @@ function statSkillMd(skillDir) {
|
|
|
89304
89735
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
89305
89736
|
if (!AGENT_NAME_RE3.test(agent))
|
|
89306
89737
|
return [];
|
|
89307
|
-
const skillsDir =
|
|
89308
|
-
if (!
|
|
89738
|
+
const skillsDir = join86(agentsRoot, agent, ".claude/skills");
|
|
89739
|
+
if (!existsSync85(skillsDir))
|
|
89309
89740
|
return [];
|
|
89310
89741
|
const out = [];
|
|
89311
89742
|
let entries;
|
|
@@ -89317,7 +89748,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
89317
89748
|
for (const ent of entries) {
|
|
89318
89749
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
89319
89750
|
continue;
|
|
89320
|
-
const dirPath =
|
|
89751
|
+
const dirPath = join86(skillsDir, ent);
|
|
89321
89752
|
try {
|
|
89322
89753
|
if (!statSync36(dirPath).isDirectory())
|
|
89323
89754
|
continue;
|
|
@@ -89343,7 +89774,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
89343
89774
|
return out;
|
|
89344
89775
|
}
|
|
89345
89776
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
89346
|
-
if (!
|
|
89777
|
+
if (!existsSync85(sharedRoot))
|
|
89347
89778
|
return [];
|
|
89348
89779
|
const out = [];
|
|
89349
89780
|
let entries;
|
|
@@ -89357,7 +89788,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
89357
89788
|
continue;
|
|
89358
89789
|
if (ent.startsWith("."))
|
|
89359
89790
|
continue;
|
|
89360
|
-
const dirPath =
|
|
89791
|
+
const dirPath = join86(sharedRoot, ent);
|
|
89361
89792
|
try {
|
|
89362
89793
|
if (!statSync36(dirPath).isDirectory())
|
|
89363
89794
|
continue;
|
|
@@ -89381,7 +89812,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
89381
89812
|
return out;
|
|
89382
89813
|
}
|
|
89383
89814
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
89384
|
-
if (!
|
|
89815
|
+
if (!existsSync85(bundledRoot))
|
|
89385
89816
|
return [];
|
|
89386
89817
|
const out = [];
|
|
89387
89818
|
let entries;
|
|
@@ -89393,7 +89824,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
89393
89824
|
for (const ent of entries) {
|
|
89394
89825
|
if (ent.startsWith("."))
|
|
89395
89826
|
continue;
|
|
89396
|
-
const dirPath =
|
|
89827
|
+
const dirPath = join86(bundledRoot, ent);
|
|
89397
89828
|
try {
|
|
89398
89829
|
if (!statSync36(dirPath).isDirectory())
|
|
89399
89830
|
continue;
|
|
@@ -89537,9 +89968,9 @@ function registerHostdMcpCommand(program3) {
|
|
|
89537
89968
|
// src/cli/hostd.ts
|
|
89538
89969
|
init_source();
|
|
89539
89970
|
init_helpers();
|
|
89540
|
-
import { existsSync as
|
|
89971
|
+
import { existsSync as existsSync87, mkdirSync as mkdirSync49, readdirSync as readdirSync34, readFileSync as readFileSync75, writeFileSync as writeFileSync42, statSync as statSync37, copyFileSync as copyFileSync12 } from "node:fs";
|
|
89541
89972
|
import { homedir as homedir51 } from "node:os";
|
|
89542
|
-
import { join as
|
|
89973
|
+
import { join as join87 } from "node:path";
|
|
89543
89974
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
89544
89975
|
|
|
89545
89976
|
// src/cli/deploy-version-guard.ts
|
|
@@ -89753,14 +90184,14 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir51()) {
|
|
|
89753
90184
|
return resolved;
|
|
89754
90185
|
}
|
|
89755
90186
|
function hostdDir() {
|
|
89756
|
-
return
|
|
90187
|
+
return join87(homedir51(), ".switchroom", "hostd");
|
|
89757
90188
|
}
|
|
89758
90189
|
function hostdComposePath() {
|
|
89759
|
-
return
|
|
90190
|
+
return join87(hostdDir(), "docker-compose.yml");
|
|
89760
90191
|
}
|
|
89761
90192
|
function backupExistingCompose() {
|
|
89762
90193
|
const p = hostdComposePath();
|
|
89763
|
-
if (!
|
|
90194
|
+
if (!existsSync87(p))
|
|
89764
90195
|
return null;
|
|
89765
90196
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
89766
90197
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -89856,7 +90287,7 @@ function doStatus() {
|
|
|
89856
90287
|
const composeYml = hostdComposePath();
|
|
89857
90288
|
console.log(source_default.bold("switchroom-hostd"));
|
|
89858
90289
|
console.log("");
|
|
89859
|
-
if (!
|
|
90290
|
+
if (!existsSync87(composeYml)) {
|
|
89860
90291
|
console.log(source_default.yellow(" compose: not installed"));
|
|
89861
90292
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
89862
90293
|
return;
|
|
@@ -89877,14 +90308,14 @@ function doStatus() {
|
|
|
89877
90308
|
} else {
|
|
89878
90309
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
89879
90310
|
}
|
|
89880
|
-
if (
|
|
90311
|
+
if (existsSync87(dir)) {
|
|
89881
90312
|
const entries = [];
|
|
89882
90313
|
try {
|
|
89883
90314
|
for (const name of readdirSync34(dir)) {
|
|
89884
90315
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
89885
90316
|
continue;
|
|
89886
|
-
const sockPath =
|
|
89887
|
-
if (
|
|
90317
|
+
const sockPath = join87(dir, name, "sock");
|
|
90318
|
+
if (existsSync87(sockPath)) {
|
|
89888
90319
|
const st = statSync37(sockPath);
|
|
89889
90320
|
if ((st.mode & 61440) === 49152) {
|
|
89890
90321
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -89903,7 +90334,7 @@ function doStatus() {
|
|
|
89903
90334
|
}
|
|
89904
90335
|
function doUninstall() {
|
|
89905
90336
|
const composeYml = hostdComposePath();
|
|
89906
|
-
if (!
|
|
90337
|
+
if (!existsSync87(composeYml)) {
|
|
89907
90338
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
89908
90339
|
return;
|
|
89909
90340
|
}
|
|
@@ -89927,12 +90358,12 @@ function registerHostdCommand(program3) {
|
|
|
89927
90358
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
89928
90359
|
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) => {
|
|
89929
90360
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
89930
|
-
if (!
|
|
90361
|
+
if (!existsSync87(logPath)) {
|
|
89931
90362
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
89932
90363
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
89933
90364
|
return;
|
|
89934
90365
|
}
|
|
89935
|
-
const raw =
|
|
90366
|
+
const raw = readFileSync75(logPath, "utf-8");
|
|
89936
90367
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
89937
90368
|
const filters = {
|
|
89938
90369
|
agent: opts.agent,
|
|
@@ -89974,9 +90405,9 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
89974
90405
|
// src/cli/webd.ts
|
|
89975
90406
|
init_source();
|
|
89976
90407
|
init_helpers();
|
|
89977
|
-
import { existsSync as
|
|
90408
|
+
import { existsSync as existsSync88, mkdirSync as mkdirSync50, writeFileSync as writeFileSync43, copyFileSync as copyFileSync13 } from "node:fs";
|
|
89978
90409
|
import { homedir as homedir52 } from "node:os";
|
|
89979
|
-
import { join as
|
|
90410
|
+
import { join as join88 } from "node:path";
|
|
89980
90411
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
89981
90412
|
function resolveWebImageTag(explicitTag, release) {
|
|
89982
90413
|
if (explicitTag)
|
|
@@ -90061,14 +90492,14 @@ services:
|
|
|
90061
90492
|
`;
|
|
90062
90493
|
}
|
|
90063
90494
|
function webdDir() {
|
|
90064
|
-
return
|
|
90495
|
+
return join88(homedir52(), ".switchroom", "web");
|
|
90065
90496
|
}
|
|
90066
90497
|
function webdComposePath() {
|
|
90067
|
-
return
|
|
90498
|
+
return join88(webdDir(), "docker-compose.yml");
|
|
90068
90499
|
}
|
|
90069
90500
|
function backupExistingCompose2() {
|
|
90070
90501
|
const p = webdComposePath();
|
|
90071
|
-
if (!
|
|
90502
|
+
if (!existsSync88(p))
|
|
90072
90503
|
return null;
|
|
90073
90504
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
90074
90505
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -90152,7 +90583,7 @@ function doStatus2() {
|
|
|
90152
90583
|
const composeYml = webdComposePath();
|
|
90153
90584
|
console.log(source_default.bold("switchroom-web"));
|
|
90154
90585
|
console.log("");
|
|
90155
|
-
if (!
|
|
90586
|
+
if (!existsSync88(composeYml)) {
|
|
90156
90587
|
console.log(source_default.yellow(" compose: not installed"));
|
|
90157
90588
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
90158
90589
|
return;
|
|
@@ -90176,7 +90607,7 @@ function doStatus2() {
|
|
|
90176
90607
|
}
|
|
90177
90608
|
function doUninstall2() {
|
|
90178
90609
|
const composeYml = webdComposePath();
|
|
90179
|
-
if (!
|
|
90610
|
+
if (!existsSync88(composeYml)) {
|
|
90180
90611
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
90181
90612
|
return;
|
|
90182
90613
|
}
|
|
@@ -90206,9 +90637,9 @@ function registerWebdCommand(program3) {
|
|
|
90206
90637
|
// src/cli/host-repair.ts
|
|
90207
90638
|
init_source();
|
|
90208
90639
|
import { homedir as homedir53 } from "node:os";
|
|
90209
|
-
import { join as
|
|
90640
|
+
import { join as join89 } from "node:path";
|
|
90210
90641
|
var ARTIFACT_ALLOWLIST = {
|
|
90211
|
-
dockerComposePluginDir: (home2) =>
|
|
90642
|
+
dockerComposePluginDir: (home2) => join89(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
90212
90643
|
stateSentinel: "/state"
|
|
90213
90644
|
};
|
|
90214
90645
|
function isStateBogusAutoDir(probe2) {
|