switchroom 0.13.64 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/switchroom.js +487 -285
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +113 -8
- package/telegram-plugin/gateway/gateway.ts +35 -22
- package/telegram-plugin/tests/tool-activity-summary.test.ts +66 -0
- package/telegram-plugin/tool-activity-summary.ts +137 -0
- package/telegram-plugin/uat/scenarios/jtbd-webkite-read-dm.test.ts +115 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -29685,8 +29685,201 @@ var init_doctor_drive = __esm(() => {
|
|
|
29685
29685
|
init_doctor_secret_access();
|
|
29686
29686
|
});
|
|
29687
29687
|
|
|
29688
|
+
// src/cli/doctor-webkite.ts
|
|
29689
|
+
import {
|
|
29690
|
+
existsSync as realExistsSync2,
|
|
29691
|
+
readFileSync as realReadFileSync2,
|
|
29692
|
+
readdirSync as realReaddirSync,
|
|
29693
|
+
openSync as realOpenSync,
|
|
29694
|
+
readSync as realReadSync,
|
|
29695
|
+
closeSync as realCloseSync
|
|
29696
|
+
} from "node:fs";
|
|
29697
|
+
import { join as join45 } from "node:path";
|
|
29698
|
+
import { homedir as homedir25 } from "node:os";
|
|
29699
|
+
function defaultReadHead(p, n) {
|
|
29700
|
+
let fd;
|
|
29701
|
+
try {
|
|
29702
|
+
fd = realOpenSync(p, "r");
|
|
29703
|
+
const buf = Buffer.alloc(n);
|
|
29704
|
+
const read = realReadSync(fd, buf, 0, n, 0);
|
|
29705
|
+
return buf.subarray(0, read);
|
|
29706
|
+
} catch {
|
|
29707
|
+
return null;
|
|
29708
|
+
} finally {
|
|
29709
|
+
if (fd !== undefined) {
|
|
29710
|
+
try {
|
|
29711
|
+
realCloseSync(fd);
|
|
29712
|
+
} catch {}
|
|
29713
|
+
}
|
|
29714
|
+
}
|
|
29715
|
+
}
|
|
29716
|
+
function resolveDeps2(config, deps) {
|
|
29717
|
+
let agentsDir = deps.agentsDir;
|
|
29718
|
+
if (agentsDir === undefined) {
|
|
29719
|
+
try {
|
|
29720
|
+
agentsDir = resolveAgentsDir(config);
|
|
29721
|
+
} catch {
|
|
29722
|
+
agentsDir = undefined;
|
|
29723
|
+
}
|
|
29724
|
+
}
|
|
29725
|
+
return {
|
|
29726
|
+
homeDir: deps.homeDir ?? homedir25(),
|
|
29727
|
+
agentsDir,
|
|
29728
|
+
existsSync: deps.existsSync ?? ((p) => realExistsSync2(p)),
|
|
29729
|
+
readFileSync: deps.readFileSync ?? ((p) => realReadFileSync2(p, "utf-8")),
|
|
29730
|
+
readdirSync: deps.readdirSync ?? ((p) => realReaddirSync(p)),
|
|
29731
|
+
readHead: deps.readHead ?? defaultReadHead
|
|
29732
|
+
};
|
|
29733
|
+
}
|
|
29734
|
+
function classifyArch(d, path4) {
|
|
29735
|
+
const head = d.readHead(path4, 20);
|
|
29736
|
+
if (head === null || head.length < 20)
|
|
29737
|
+
return "unreadable";
|
|
29738
|
+
if (!(head[0] === 127 && head[1] === 69 && head[2] === 76 && head[3] === 70)) {
|
|
29739
|
+
return "not-elf";
|
|
29740
|
+
}
|
|
29741
|
+
const eMachine = head[18] | head[19] << 8;
|
|
29742
|
+
return eMachine === 62 ? "elf-x64" : "elf-other-arch";
|
|
29743
|
+
}
|
|
29744
|
+
function webkiteEnabled(config, agent) {
|
|
29745
|
+
const mcp = config.agents?.[agent]?.mcp_servers;
|
|
29746
|
+
return (mcp ?? {})["webkite"] !== false;
|
|
29747
|
+
}
|
|
29748
|
+
function runWebkiteChecks(config, deps = {}) {
|
|
29749
|
+
const agents = Object.keys(config.agents ?? {});
|
|
29750
|
+
const enabledAgents = agents.filter((a) => webkiteEnabled(config, a));
|
|
29751
|
+
if (enabledAgents.length === 0)
|
|
29752
|
+
return [];
|
|
29753
|
+
const d = resolveDeps2(config, deps);
|
|
29754
|
+
const results = [];
|
|
29755
|
+
const binPath = join45(d.homeDir, ".switchroom", "bin", "webkite");
|
|
29756
|
+
if (!d.existsSync(binPath)) {
|
|
29757
|
+
results.push({
|
|
29758
|
+
name: "webkite: binary",
|
|
29759
|
+
status: "warn",
|
|
29760
|
+
detail: `not staged at ${binPath} \u2014 agents run in degraded "no-webkite" mode (native WebFetch kept by the fail-safe gate)`,
|
|
29761
|
+
fix: "Stage the private-beta binary: cp <webkite> ~/.switchroom/bin/webkite && chmod +x; then `switchroom update`."
|
|
29762
|
+
});
|
|
29763
|
+
} else {
|
|
29764
|
+
const arch = classifyArch(d, binPath);
|
|
29765
|
+
if (arch === "elf-x64") {
|
|
29766
|
+
results.push({ name: "webkite: binary", status: "ok", detail: `staged, linux x86-64 ELF (${binPath})` });
|
|
29767
|
+
} else if (arch === "unreadable") {
|
|
29768
|
+
results.push({ name: "webkite: binary", status: "skip", detail: `present but unreadable by the operator \u2014 re-run doctor without sudo` });
|
|
29769
|
+
} else if (arch === "not-elf") {
|
|
29770
|
+
results.push({
|
|
29771
|
+
name: "webkite: binary",
|
|
29772
|
+
status: "fail",
|
|
29773
|
+
detail: `${binPath} is not a Linux ELF binary (likely a macOS/other-OS build) \u2014 it cannot run in the agent container`,
|
|
29774
|
+
fix: "Replace with a linux x86-64 build (static musl preferred). See the webkite release artifacts."
|
|
29775
|
+
});
|
|
29776
|
+
} else {
|
|
29777
|
+
results.push({
|
|
29778
|
+
name: "webkite: binary",
|
|
29779
|
+
status: "fail",
|
|
29780
|
+
detail: `${binPath} is an ELF binary for the wrong CPU arch (not x86-64) \u2014 it cannot run in the agent container`,
|
|
29781
|
+
fix: "Replace with a linux x86-64 (amd64) build."
|
|
29782
|
+
});
|
|
29783
|
+
}
|
|
29784
|
+
}
|
|
29785
|
+
const cloakDir = join45(d.homeDir, ".cloakbrowser");
|
|
29786
|
+
let chromeFound = false;
|
|
29787
|
+
if (d.existsSync(cloakDir)) {
|
|
29788
|
+
try {
|
|
29789
|
+
for (const entry of d.readdirSync(cloakDir)) {
|
|
29790
|
+
if (entry.startsWith("chromium-") && d.existsSync(join45(cloakDir, entry, "chrome"))) {
|
|
29791
|
+
chromeFound = true;
|
|
29792
|
+
break;
|
|
29793
|
+
}
|
|
29794
|
+
}
|
|
29795
|
+
} catch {}
|
|
29796
|
+
}
|
|
29797
|
+
if (chromeFound) {
|
|
29798
|
+
results.push({ name: "webkite: cloakbrowser", status: "ok", detail: "stealth Chromium present (local render available)" });
|
|
29799
|
+
} else {
|
|
29800
|
+
results.push({
|
|
29801
|
+
name: "webkite: cloakbrowser",
|
|
29802
|
+
status: "warn",
|
|
29803
|
+
detail: "no local stealth Chromium at ~/.cloakbrowser/chromium-*/chrome \u2014 webkite can only cloud-render (Cloudflare/Firecrawl) and will fail bot-gated sites with no local fallback",
|
|
29804
|
+
fix: "Install once on the host: XDG_DATA_HOME=~/.switchroom/webkite-share webkite setup --yes cloakbrowser"
|
|
29805
|
+
});
|
|
29806
|
+
}
|
|
29807
|
+
if (d.agentsDir === undefined) {
|
|
29808
|
+
results.push({
|
|
29809
|
+
name: "webkite: scaffold",
|
|
29810
|
+
status: "warn",
|
|
29811
|
+
detail: "agents_dir unresolved (no switchroom.agents_dir) \u2014 cannot verify per-agent webkite wiring"
|
|
29812
|
+
});
|
|
29813
|
+
return results;
|
|
29814
|
+
}
|
|
29815
|
+
for (const agent of enabledAgents) {
|
|
29816
|
+
const agentDir = join45(d.agentsDir, agent);
|
|
29817
|
+
const settingsPath = join45(agentDir, ".claude", "settings.json");
|
|
29818
|
+
const mcpPath = join45(agentDir, ".mcp.json");
|
|
29819
|
+
if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
|
|
29820
|
+
continue;
|
|
29821
|
+
}
|
|
29822
|
+
let settings = null;
|
|
29823
|
+
let mcp = null;
|
|
29824
|
+
let unreadable = false;
|
|
29825
|
+
for (const [path4, set] of [
|
|
29826
|
+
[settingsPath, (v) => {
|
|
29827
|
+
settings = v;
|
|
29828
|
+
}],
|
|
29829
|
+
[mcpPath, (v) => {
|
|
29830
|
+
mcp = v;
|
|
29831
|
+
}]
|
|
29832
|
+
]) {
|
|
29833
|
+
if (!d.existsSync(path4))
|
|
29834
|
+
continue;
|
|
29835
|
+
try {
|
|
29836
|
+
set(JSON.parse(d.readFileSync(path4)));
|
|
29837
|
+
} catch (err) {
|
|
29838
|
+
const code = err?.code;
|
|
29839
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
29840
|
+
unreadable = true;
|
|
29841
|
+
}
|
|
29842
|
+
}
|
|
29843
|
+
}
|
|
29844
|
+
if (unreadable) {
|
|
29845
|
+
results.push({
|
|
29846
|
+
name: `webkite: ${agent} scaffold`,
|
|
29847
|
+
status: "skip",
|
|
29848
|
+
detail: "scaffold files are agent-UID-owned (0600) \u2014 re-run doctor without sudo to read them"
|
|
29849
|
+
});
|
|
29850
|
+
continue;
|
|
29851
|
+
}
|
|
29852
|
+
const mcpHasWebkite = !!(mcp && mcp.mcpServers?.["webkite"]);
|
|
29853
|
+
const allow = settings?.permissions?.allow ?? [];
|
|
29854
|
+
const deny = settings?.permissions?.deny ?? [];
|
|
29855
|
+
const allowOk = allow.includes("mcp__webkite__*");
|
|
29856
|
+
const denyOk = deny.includes("WebFetch");
|
|
29857
|
+
const problems = [];
|
|
29858
|
+
if (!mcpHasWebkite)
|
|
29859
|
+
problems.push(".mcp.json missing the webkite entry");
|
|
29860
|
+
if (!allowOk)
|
|
29861
|
+
problems.push("permissions.allow missing mcp__webkite__* (first fetch will prompt)");
|
|
29862
|
+
if (!denyOk)
|
|
29863
|
+
problems.push("permissions.deny missing WebFetch (native fetch not disabled)");
|
|
29864
|
+
if (problems.length === 0) {
|
|
29865
|
+
results.push({ name: `webkite: ${agent} scaffold`, status: "ok", detail: "wired + pre-approved + WebFetch denied" });
|
|
29866
|
+
} else {
|
|
29867
|
+
results.push({
|
|
29868
|
+
name: `webkite: ${agent} scaffold`,
|
|
29869
|
+
status: "warn",
|
|
29870
|
+
detail: problems.join("; "),
|
|
29871
|
+
fix: "Run `switchroom apply` (or `switchroom agent restart <agent>`) to re-scaffold the webkite wiring."
|
|
29872
|
+
});
|
|
29873
|
+
}
|
|
29874
|
+
}
|
|
29875
|
+
return results;
|
|
29876
|
+
}
|
|
29877
|
+
var init_doctor_webkite = __esm(() => {
|
|
29878
|
+
init_loader();
|
|
29879
|
+
});
|
|
29880
|
+
|
|
29688
29881
|
// src/cli/doctor-scaffold-wiring.ts
|
|
29689
|
-
import { join as
|
|
29882
|
+
import { join as join46, resolve as resolve30 } from "node:path";
|
|
29690
29883
|
function readJson2(d, path4) {
|
|
29691
29884
|
if (!d.existsSync(path4))
|
|
29692
29885
|
return { kind: "absent" };
|
|
@@ -29729,8 +29922,8 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
29729
29922
|
});
|
|
29730
29923
|
continue;
|
|
29731
29924
|
}
|
|
29732
|
-
const mcpPath =
|
|
29733
|
-
const claudeJsonPath =
|
|
29925
|
+
const mcpPath = join46(agentDir, ".mcp.json");
|
|
29926
|
+
const claudeJsonPath = join46(agentDir, ".claude", ".claude.json");
|
|
29734
29927
|
const mcpRead = readJson2(deps, mcpPath);
|
|
29735
29928
|
const trustRead = readJson2(deps, claudeJsonPath);
|
|
29736
29929
|
if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
|
|
@@ -29791,17 +29984,17 @@ var init_doctor_scaffold_wiring = () => {};
|
|
|
29791
29984
|
|
|
29792
29985
|
// src/cli/doctor-microsoft.ts
|
|
29793
29986
|
import {
|
|
29794
|
-
existsSync as
|
|
29795
|
-
readFileSync as
|
|
29987
|
+
existsSync as realExistsSync3,
|
|
29988
|
+
readFileSync as realReadFileSync3
|
|
29796
29989
|
} from "node:fs";
|
|
29797
|
-
import { join as
|
|
29798
|
-
import { homedir as
|
|
29799
|
-
function
|
|
29800
|
-
const home2 = deps.homeDir?.() ??
|
|
29990
|
+
import { join as join47 } from "node:path";
|
|
29991
|
+
import { homedir as homedir26 } from "node:os";
|
|
29992
|
+
function resolveDeps3(deps) {
|
|
29993
|
+
const home2 = deps.homeDir?.() ?? homedir26();
|
|
29801
29994
|
return {
|
|
29802
|
-
existsSync: deps.existsSync ??
|
|
29803
|
-
readFileSync: deps.readFileSync ??
|
|
29804
|
-
agentsDir:
|
|
29995
|
+
existsSync: deps.existsSync ?? realExistsSync3,
|
|
29996
|
+
readFileSync: deps.readFileSync ?? realReadFileSync3,
|
|
29997
|
+
agentsDir: join47(home2, ".switchroom", "agents"),
|
|
29805
29998
|
now: deps.now ?? Date.now
|
|
29806
29999
|
};
|
|
29807
30000
|
}
|
|
@@ -29895,7 +30088,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
|
|
|
29895
30088
|
];
|
|
29896
30089
|
}
|
|
29897
30090
|
function readHeartbeat(d, agentName) {
|
|
29898
|
-
const path4 =
|
|
30091
|
+
const path4 = join47(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
|
|
29899
30092
|
if (!d.existsSync(path4)) {
|
|
29900
30093
|
return { error: "heartbeat file missing \u2014 launcher has not yet started" };
|
|
29901
30094
|
}
|
|
@@ -29964,7 +30157,7 @@ function runMicrosoftChecks(config, deps = {}) {
|
|
|
29964
30157
|
if (!accountsConfigured && !anyAgentAccount && !config.microsoft_workspace) {
|
|
29965
30158
|
return [];
|
|
29966
30159
|
}
|
|
29967
|
-
const d =
|
|
30160
|
+
const d = resolveDeps3(deps);
|
|
29968
30161
|
const results = [];
|
|
29969
30162
|
results.push(...checkConfigMatrix2(config));
|
|
29970
30163
|
const msAgents = computeMicrosoftEnabledAgents(config);
|
|
@@ -29988,19 +30181,19 @@ var init_doctor_microsoft = __esm(() => {
|
|
|
29988
30181
|
|
|
29989
30182
|
// src/cli/doctor-notion.ts
|
|
29990
30183
|
import {
|
|
29991
|
-
existsSync as
|
|
29992
|
-
readFileSync as
|
|
30184
|
+
existsSync as realExistsSync4,
|
|
30185
|
+
readFileSync as realReadFileSync4,
|
|
29993
30186
|
statSync as realStatSync
|
|
29994
30187
|
} from "node:fs";
|
|
29995
|
-
import { join as
|
|
29996
|
-
import { homedir as
|
|
29997
|
-
function
|
|
29998
|
-
const home2 = deps.homeDir?.() ??
|
|
30188
|
+
import { join as join48 } from "node:path";
|
|
30189
|
+
import { homedir as homedir27 } from "node:os";
|
|
30190
|
+
function resolveDeps4(deps) {
|
|
30191
|
+
const home2 = deps.homeDir?.() ?? homedir27();
|
|
29999
30192
|
return {
|
|
30000
|
-
existsSync: deps.existsSync ??
|
|
30001
|
-
readFileSync: deps.readFileSync ??
|
|
30193
|
+
existsSync: deps.existsSync ?? realExistsSync4,
|
|
30194
|
+
readFileSync: deps.readFileSync ?? realReadFileSync4,
|
|
30002
30195
|
statSync: deps.statSync ?? realStatSync,
|
|
30003
|
-
agentsDir:
|
|
30196
|
+
agentsDir: join48(home2, ".switchroom", "agents"),
|
|
30004
30197
|
now: deps.now ?? Date.now,
|
|
30005
30198
|
vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
|
|
30006
30199
|
};
|
|
@@ -30125,7 +30318,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
|
|
|
30125
30318
|
return [];
|
|
30126
30319
|
const results = [];
|
|
30127
30320
|
for (const name of notionAgents) {
|
|
30128
|
-
const heartbeatPath =
|
|
30321
|
+
const heartbeatPath = join48(d.agentsDir, name, "notion-launcher.heartbeat.json");
|
|
30129
30322
|
if (!d.existsSync(heartbeatPath)) {
|
|
30130
30323
|
results.push({
|
|
30131
30324
|
name: `notion:launcher-heartbeat:${name}`,
|
|
@@ -30169,7 +30362,7 @@ async function runNotionChecks(config, deps = {}) {
|
|
|
30169
30362
|
if (notionAgents.length === 0 && !config.notion_workspace) {
|
|
30170
30363
|
return [];
|
|
30171
30364
|
}
|
|
30172
|
-
const d =
|
|
30365
|
+
const d = resolveDeps4(deps);
|
|
30173
30366
|
const results = [];
|
|
30174
30367
|
results.push(...checkConfigMatrix3(config));
|
|
30175
30368
|
if (notionAgents.length > 0 && config.notion_workspace) {
|
|
@@ -30196,16 +30389,16 @@ var init_doctor_notion = __esm(() => {
|
|
|
30196
30389
|
|
|
30197
30390
|
// src/cli/doctor-credentials-migration.ts
|
|
30198
30391
|
import {
|
|
30199
|
-
existsSync as
|
|
30200
|
-
readdirSync as
|
|
30392
|
+
existsSync as realExistsSync5,
|
|
30393
|
+
readdirSync as realReaddirSync2,
|
|
30201
30394
|
statSync as realStatSync2
|
|
30202
30395
|
} from "node:fs";
|
|
30203
|
-
import { homedir as
|
|
30204
|
-
import { join as
|
|
30396
|
+
import { homedir as homedir28 } from "node:os";
|
|
30397
|
+
import { join as join49 } from "node:path";
|
|
30205
30398
|
function runCredentialsMigrationChecks(config, deps = {}) {
|
|
30206
|
-
const credDir = deps.credentialsDir ??
|
|
30207
|
-
const existsSync49 = deps.existsSync ?? ((p) =>
|
|
30208
|
-
const readdirSync19 = deps.readdirSync ?? ((p) =>
|
|
30399
|
+
const credDir = deps.credentialsDir ?? join49(homedir28(), ".switchroom", "credentials");
|
|
30400
|
+
const existsSync49 = deps.existsSync ?? ((p) => realExistsSync5(p));
|
|
30401
|
+
const readdirSync19 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
|
|
30209
30402
|
const isDirectory = deps.isDirectory ?? ((p) => {
|
|
30210
30403
|
try {
|
|
30211
30404
|
return realStatSync2(p).isDirectory();
|
|
@@ -30231,7 +30424,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
|
|
|
30231
30424
|
const flat = [];
|
|
30232
30425
|
const perAgentDirs = [];
|
|
30233
30426
|
for (const e of entries) {
|
|
30234
|
-
const full =
|
|
30427
|
+
const full = join49(credDir, e);
|
|
30235
30428
|
if (isDirectory(full) && agentNames.has(e)) {
|
|
30236
30429
|
perAgentDirs.push(e);
|
|
30237
30430
|
} else {
|
|
@@ -30354,19 +30547,19 @@ var init_doctor_inlined_secrets = __esm(() => {
|
|
|
30354
30547
|
|
|
30355
30548
|
// src/cli/doctor-audit-integrity.ts
|
|
30356
30549
|
import { readFileSync as fsReadFileSync2 } from "node:fs";
|
|
30357
|
-
import { homedir as
|
|
30358
|
-
import { join as
|
|
30550
|
+
import { homedir as homedir29 } from "node:os";
|
|
30551
|
+
import { join as join50 } from "node:path";
|
|
30359
30552
|
function rootWrittenLogs(home2) {
|
|
30360
30553
|
return [
|
|
30361
|
-
{ label: "vault-broker", path:
|
|
30554
|
+
{ label: "vault-broker", path: join50(home2, ".switchroom", "vault-audit.log") },
|
|
30362
30555
|
{
|
|
30363
30556
|
label: "hostd",
|
|
30364
|
-
path:
|
|
30557
|
+
path: join50(home2, ".switchroom", "host-control-audit.log")
|
|
30365
30558
|
}
|
|
30366
30559
|
];
|
|
30367
30560
|
}
|
|
30368
30561
|
function runAuditIntegrityChecks(deps = {}) {
|
|
30369
|
-
const home2 = deps.homeDir ??
|
|
30562
|
+
const home2 = deps.homeDir ?? homedir29();
|
|
30370
30563
|
const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
|
|
30371
30564
|
const results = [];
|
|
30372
30565
|
for (const { label, path: path4 } of rootWrittenLogs(home2)) {
|
|
@@ -30504,14 +30697,14 @@ var init_client4 = __esm(() => {
|
|
|
30504
30697
|
|
|
30505
30698
|
// src/cli/doctor-agent-smoke.ts
|
|
30506
30699
|
import { existsSync as existsSync49 } from "node:fs";
|
|
30507
|
-
import { homedir as
|
|
30508
|
-
import { join as
|
|
30700
|
+
import { homedir as homedir30 } from "node:os";
|
|
30701
|
+
import { join as join51 } from "node:path";
|
|
30509
30702
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
30510
30703
|
async function runAgentSmokeChecks(config, deps = {}) {
|
|
30511
30704
|
if (deps.fast)
|
|
30512
30705
|
return [];
|
|
30513
|
-
const home2 = deps.homeDir ??
|
|
30514
|
-
const sock = deps.operatorSockPath ??
|
|
30706
|
+
const home2 = deps.homeDir ?? homedir30();
|
|
30707
|
+
const sock = deps.operatorSockPath ?? join51(home2, ".switchroom", "hostd", "operator", "sock");
|
|
30515
30708
|
if (!deps.hostdRequestImpl && !existsSync49(sock)) {
|
|
30516
30709
|
return [
|
|
30517
30710
|
{
|
|
@@ -30591,8 +30784,8 @@ var init_doctor_agent_smoke = __esm(() => {
|
|
|
30591
30784
|
// src/cli/doctor-vault-broker-durability.ts
|
|
30592
30785
|
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
30593
30786
|
import { existsSync as existsSync50, statSync as statSync22 } from "node:fs";
|
|
30594
|
-
import { homedir as
|
|
30595
|
-
import { join as
|
|
30787
|
+
import { homedir as homedir31 } from "node:os";
|
|
30788
|
+
import { join as join52 } from "node:path";
|
|
30596
30789
|
function probeBindMountInode(hostPath, brokerContainerPath, opts) {
|
|
30597
30790
|
const statHost = opts?.statHost ?? defaultStatHost;
|
|
30598
30791
|
const statBroker = opts?.statBroker ?? defaultStatBroker;
|
|
@@ -30732,19 +30925,19 @@ function defaultBrokerStatusProbe() {
|
|
|
30732
30925
|
}
|
|
30733
30926
|
}
|
|
30734
30927
|
function runVaultBrokerDurabilityChecks(_config, opts) {
|
|
30735
|
-
const home2 =
|
|
30928
|
+
const home2 = homedir31();
|
|
30736
30929
|
const probe2 = opts?.inodeProbe ?? probeBindMountInode;
|
|
30737
30930
|
return [
|
|
30738
30931
|
probeBrokerUnlocked(opts?.statusProbe),
|
|
30739
30932
|
probeAutoUnlockBlob(home2),
|
|
30740
30933
|
probeMachineIdMount(),
|
|
30741
|
-
formatBindMountResult("vault-broker: vault.enc bind mount",
|
|
30742
|
-
formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)",
|
|
30743
|
-
formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)",
|
|
30934
|
+
formatBindMountResult("vault-broker: vault.enc bind mount", join52(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join52(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
|
|
30935
|
+
formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join52(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join52(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
|
|
30936
|
+
formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join52(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join52(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log"))
|
|
30744
30937
|
];
|
|
30745
30938
|
}
|
|
30746
30939
|
function probeAutoUnlockBlob(home2) {
|
|
30747
|
-
const blobPath =
|
|
30940
|
+
const blobPath = join52(home2, ".switchroom", "vault-auto-unlock");
|
|
30748
30941
|
if (!existsSync50(blobPath)) {
|
|
30749
30942
|
return {
|
|
30750
30943
|
name: "vault-broker: auto-unlock blob",
|
|
@@ -30855,16 +31048,16 @@ import {
|
|
|
30855
31048
|
readdirSync as readdirSync19,
|
|
30856
31049
|
statSync as statSync23
|
|
30857
31050
|
} from "node:fs";
|
|
30858
|
-
import { dirname as dirname12, join as
|
|
31051
|
+
import { dirname as dirname12, join as join53, resolve as resolve31 } from "node:path";
|
|
30859
31052
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
30860
31053
|
function findInNvm(bin) {
|
|
30861
|
-
const nvmRoot =
|
|
31054
|
+
const nvmRoot = join53(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
30862
31055
|
if (!existsSync51(nvmRoot))
|
|
30863
31056
|
return null;
|
|
30864
31057
|
try {
|
|
30865
31058
|
const versions = readdirSync19(nvmRoot).sort().reverse();
|
|
30866
31059
|
for (const v of versions) {
|
|
30867
|
-
const candidate =
|
|
31060
|
+
const candidate = join53(nvmRoot, v, "bin", bin);
|
|
30868
31061
|
try {
|
|
30869
31062
|
const s = statSync23(candidate);
|
|
30870
31063
|
if (s.isFile() || s.isSymbolicLink()) {
|
|
@@ -31029,7 +31222,7 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
|
|
|
31029
31222
|
if (envBrowsersPath && envBrowsersPath.length > 0) {
|
|
31030
31223
|
cacheLocations.push(envBrowsersPath);
|
|
31031
31224
|
}
|
|
31032
|
-
cacheLocations.push(
|
|
31225
|
+
cacheLocations.push(join53(homeDir, ".cache", "ms-playwright"));
|
|
31033
31226
|
for (const cacheDir of cacheLocations) {
|
|
31034
31227
|
if (!existsSync51(cacheDir))
|
|
31035
31228
|
continue;
|
|
@@ -31037,10 +31230,10 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
|
|
|
31037
31230
|
const entries = readdirSync19(cacheDir).filter((e) => e.startsWith("chromium"));
|
|
31038
31231
|
for (const entry of entries) {
|
|
31039
31232
|
const candidates2 = [
|
|
31040
|
-
|
|
31041
|
-
|
|
31042
|
-
|
|
31043
|
-
|
|
31233
|
+
join53(cacheDir, entry, "chrome-linux64", "chrome"),
|
|
31234
|
+
join53(cacheDir, entry, "chrome-linux", "chrome"),
|
|
31235
|
+
join53(cacheDir, entry, "chrome-linux64", "headless_shell"),
|
|
31236
|
+
join53(cacheDir, entry, "chrome-linux", "headless_shell")
|
|
31044
31237
|
];
|
|
31045
31238
|
for (const path4 of candidates2) {
|
|
31046
31239
|
if (existsSync51(path4))
|
|
@@ -31150,7 +31343,7 @@ function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
|
|
|
31150
31343
|
function checkLegacyState() {
|
|
31151
31344
|
const results = [];
|
|
31152
31345
|
const h = process.env.HOME ?? "/root";
|
|
31153
|
-
const clerkDir =
|
|
31346
|
+
const clerkDir = join53(h, LEGACY_STATE_DIR);
|
|
31154
31347
|
const clerkPresent = existsSync51(clerkDir);
|
|
31155
31348
|
results.push({
|
|
31156
31349
|
name: "legacy ~/.clerk state",
|
|
@@ -31160,7 +31353,7 @@ function checkLegacyState() {
|
|
|
31160
31353
|
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."
|
|
31161
31354
|
} : {}
|
|
31162
31355
|
});
|
|
31163
|
-
const legacySock =
|
|
31356
|
+
const legacySock = join53(h, ".switchroom", "vault-broker.sock");
|
|
31164
31357
|
let sockStat = null;
|
|
31165
31358
|
try {
|
|
31166
31359
|
sockStat = lstatSync5(legacySock);
|
|
@@ -31456,7 +31649,7 @@ async function checkHindsight(config) {
|
|
|
31456
31649
|
}
|
|
31457
31650
|
function checkPendingRetainsQueue(dir) {
|
|
31458
31651
|
const home2 = process.env.HOME ?? "";
|
|
31459
|
-
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ??
|
|
31652
|
+
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join53(home2, ".hindsight", "pending-retains");
|
|
31460
31653
|
if (!existsSync51(pendingDir)) {
|
|
31461
31654
|
return {
|
|
31462
31655
|
name: "pending-retains queue",
|
|
@@ -31587,7 +31780,7 @@ async function checkTelegram(config) {
|
|
|
31587
31780
|
const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
|
|
31588
31781
|
if (plugin !== "switchroom")
|
|
31589
31782
|
continue;
|
|
31590
|
-
const envPath =
|
|
31783
|
+
const envPath = join53(agentsDir, name, "telegram", ".env");
|
|
31591
31784
|
const read = tryReadHostFile(envPath);
|
|
31592
31785
|
if (read.kind === "eacces") {
|
|
31593
31786
|
results.push({
|
|
@@ -31670,7 +31863,7 @@ function checkStartShStale(agentName, startShPath) {
|
|
|
31670
31863
|
}
|
|
31671
31864
|
function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
31672
31865
|
const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
|
|
31673
|
-
const path4 =
|
|
31866
|
+
const path4 = join53(agentDir, "home", ".switchroom");
|
|
31674
31867
|
let stats;
|
|
31675
31868
|
try {
|
|
31676
31869
|
stats = lstatSync5(path4);
|
|
@@ -31707,7 +31900,7 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
|
31707
31900
|
}
|
|
31708
31901
|
function checkRepoHygiene(repoRoot) {
|
|
31709
31902
|
const results = [];
|
|
31710
|
-
const exportDir =
|
|
31903
|
+
const exportDir = join53(repoRoot, "clerk-export");
|
|
31711
31904
|
if (existsSync51(exportDir)) {
|
|
31712
31905
|
results.push({
|
|
31713
31906
|
name: "repo hygiene: clerk-export/ on disk (#1072)",
|
|
@@ -31716,7 +31909,7 @@ function checkRepoHygiene(repoRoot) {
|
|
|
31716
31909
|
fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
|
|
31717
31910
|
});
|
|
31718
31911
|
}
|
|
31719
|
-
const knownTarball =
|
|
31912
|
+
const knownTarball = join53(repoRoot, "clerk-export-with-secrets.tar.gz");
|
|
31720
31913
|
if (existsSync51(knownTarball)) {
|
|
31721
31914
|
results.push({
|
|
31722
31915
|
name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
|
|
@@ -31734,7 +31927,7 @@ function checkRepoHygiene(repoRoot) {
|
|
|
31734
31927
|
results.push({
|
|
31735
31928
|
name: `repo hygiene: ${name} on disk (#1072)`,
|
|
31736
31929
|
status: "warn",
|
|
31737
|
-
detail: `${
|
|
31930
|
+
detail: `${join53(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
|
|
31738
31931
|
fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
|
|
31739
31932
|
});
|
|
31740
31933
|
}
|
|
@@ -31757,9 +31950,9 @@ function checkRepoHygiene(repoRoot) {
|
|
|
31757
31950
|
}
|
|
31758
31951
|
function isSwitchroomCheckout(dir) {
|
|
31759
31952
|
try {
|
|
31760
|
-
if (!existsSync51(
|
|
31953
|
+
if (!existsSync51(join53(dir, ".git")))
|
|
31761
31954
|
return false;
|
|
31762
|
-
const pkgPath =
|
|
31955
|
+
const pkgPath = join53(dir, "package.json");
|
|
31763
31956
|
if (!existsSync51(pkgPath))
|
|
31764
31957
|
return false;
|
|
31765
31958
|
const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
|
|
@@ -31796,7 +31989,7 @@ function checkAgents(config, configPath) {
|
|
|
31796
31989
|
fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
|
|
31797
31990
|
});
|
|
31798
31991
|
}
|
|
31799
|
-
results.push(checkStartShStale(name,
|
|
31992
|
+
results.push(checkStartShStale(name, join53(agentDir, "start.sh")));
|
|
31800
31993
|
results.push(checkLeakedHomeSwitchroom(name, agentDir));
|
|
31801
31994
|
const status = statuses[name];
|
|
31802
31995
|
const active = status?.active ?? "unknown";
|
|
@@ -31873,7 +32066,7 @@ function checkAgents(config, configPath) {
|
|
|
31873
32066
|
}
|
|
31874
32067
|
}
|
|
31875
32068
|
if (agentConfig.channels?.telegram?.plugin === "switchroom") {
|
|
31876
|
-
const mcpJsonPath =
|
|
32069
|
+
const mcpJsonPath = join53(agentDir, ".mcp.json");
|
|
31877
32070
|
if (!existsSync51(mcpJsonPath)) {
|
|
31878
32071
|
results.push({
|
|
31879
32072
|
name: `${name}: .mcp.json`,
|
|
@@ -32175,7 +32368,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
32175
32368
|
};
|
|
32176
32369
|
}
|
|
32177
32370
|
const credDir = dirname12(envPath);
|
|
32178
|
-
const authScript =
|
|
32371
|
+
const authScript = join53(credDir, "claude-auth.py");
|
|
32179
32372
|
if (!existsSync51(authScript)) {
|
|
32180
32373
|
return {
|
|
32181
32374
|
name: "mff: auth flow",
|
|
@@ -32504,7 +32697,8 @@ function registerDoctorCommand(program3) {
|
|
|
32504
32697
|
}
|
|
32505
32698
|
})
|
|
32506
32699
|
},
|
|
32507
|
-
{ title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) }
|
|
32700
|
+
{ title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) },
|
|
32701
|
+
{ title: "Webkite", results: runWebkiteChecks(config) }
|
|
32508
32702
|
];
|
|
32509
32703
|
const cwd = process.cwd();
|
|
32510
32704
|
if (isSwitchroomCheckout(cwd)) {
|
|
@@ -32563,6 +32757,7 @@ var init_doctor = __esm(() => {
|
|
|
32563
32757
|
init_doctor_auth_broker();
|
|
32564
32758
|
init_doctor_hostd();
|
|
32565
32759
|
init_doctor_drive();
|
|
32760
|
+
init_doctor_webkite();
|
|
32566
32761
|
init_doctor_microsoft();
|
|
32567
32762
|
init_doctor_notion();
|
|
32568
32763
|
init_doctor_credentials_migration();
|
|
@@ -49083,8 +49278,8 @@ var {
|
|
|
49083
49278
|
} = import__.default;
|
|
49084
49279
|
|
|
49085
49280
|
// src/build-info.ts
|
|
49086
|
-
var VERSION = "0.
|
|
49087
|
-
var COMMIT_SHA = "
|
|
49281
|
+
var VERSION = "0.14.0";
|
|
49282
|
+
var COMMIT_SHA = "d7cd6faa";
|
|
49088
49283
|
|
|
49089
49284
|
// src/cli/agent.ts
|
|
49090
49285
|
init_source();
|
|
@@ -49940,6 +50135,10 @@ var AGENT_CONFIG_MCP_TOOLS = [
|
|
|
49940
50135
|
var HOSTD_MCP_TOOLS = [
|
|
49941
50136
|
"mcp__hostd__update_check"
|
|
49942
50137
|
];
|
|
50138
|
+
var WEBKITE_MCP_TOOLS = [
|
|
50139
|
+
"mcp__webkite",
|
|
50140
|
+
"mcp__webkite__*"
|
|
50141
|
+
];
|
|
49943
50142
|
var LEGACY_SWITCHROOM_MCP_TOKENS = ["mcp__switchroom", "mcp__switchroom__*"];
|
|
49944
50143
|
var LEGACY_HOSTD_BLANKET_TOKENS = ["mcp__hostd", "mcp__hostd__*"];
|
|
49945
50144
|
var DEFAULT_READ_ONLY_PREAPPROVED_TOOLS = [
|
|
@@ -50755,7 +50954,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
50755
50954
|
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
50756
50955
|
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
50757
50956
|
...AGENT_CONFIG_MCP_TOOLS,
|
|
50758
|
-
...HOSTD_MCP_TOOLS
|
|
50957
|
+
...HOSTD_MCP_TOOLS,
|
|
50958
|
+
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
50759
50959
|
]);
|
|
50760
50960
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
50761
50961
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
@@ -50816,7 +51016,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
50816
51016
|
}
|
|
50817
51017
|
settings.permissions = settings.permissions ?? {};
|
|
50818
51018
|
const allow = (Array.isArray(settings.permissions.allow) ? settings.permissions.allow : []).filter((p) => !LEGACY_HOSTD_BLANKET_TOKENS.includes(p));
|
|
50819
|
-
|
|
51019
|
+
const webkiteAllowTools = agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS;
|
|
51020
|
+
for (const t of [...AGENT_CONFIG_MCP_TOOLS, ...HOSTD_MCP_TOOLS, ...webkiteAllowTools]) {
|
|
50820
51021
|
if (!allow.includes(t))
|
|
50821
51022
|
allow.push(t);
|
|
50822
51023
|
}
|
|
@@ -51539,7 +51740,8 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
|
|
|
51539
51740
|
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
51540
51741
|
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
51541
51742
|
...AGENT_CONFIG_MCP_TOOLS,
|
|
51542
|
-
...HOSTD_MCP_TOOLS
|
|
51743
|
+
...HOSTD_MCP_TOOLS,
|
|
51744
|
+
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
51543
51745
|
]);
|
|
51544
51746
|
const desiredDeny = dedupe2([
|
|
51545
51747
|
...tools.deny ?? [],
|
|
@@ -72402,15 +72604,15 @@ init_loader();
|
|
|
72402
72604
|
init_lifecycle();
|
|
72403
72605
|
import { cpSync as cpSync2, existsSync as existsSync52, mkdirSync as mkdirSync28, readFileSync as readFileSync47, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync24 } from "node:fs";
|
|
72404
72606
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
72405
|
-
import { join as
|
|
72406
|
-
import { homedir as
|
|
72407
|
-
var DEFAULT_COMPOSE_PATH =
|
|
72607
|
+
import { join as join54, dirname as dirname13, resolve as resolve32 } from "node:path";
|
|
72608
|
+
import { homedir as homedir32 } from "node:os";
|
|
72609
|
+
var DEFAULT_COMPOSE_PATH = join54(homedir32(), ".switchroom", "compose", "docker-compose.yml");
|
|
72408
72610
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
72409
72611
|
let dir = dirname13(scriptPath);
|
|
72410
72612
|
for (let i = 0;i < 12; i++) {
|
|
72411
|
-
if (existsSync52(
|
|
72613
|
+
if (existsSync52(join54(dir, ".git"))) {
|
|
72412
72614
|
try {
|
|
72413
|
-
const pkg = JSON.parse(readFileSync47(
|
|
72615
|
+
const pkg = JSON.parse(readFileSync47(join54(dir, "package.json"), "utf-8"));
|
|
72414
72616
|
if (pkg.name === "switchroom")
|
|
72415
72617
|
return true;
|
|
72416
72618
|
} catch {}
|
|
@@ -72541,7 +72743,7 @@ function planUpdate(opts) {
|
|
|
72541
72743
|
return;
|
|
72542
72744
|
}
|
|
72543
72745
|
const source = resolve32(import.meta.dirname, "../../skills");
|
|
72544
|
-
const dest =
|
|
72746
|
+
const dest = join54(homedir32(), ".switchroom", "skills", "_bundled");
|
|
72545
72747
|
if (!existsSync52(source)) {
|
|
72546
72748
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
72547
72749
|
`);
|
|
@@ -72655,7 +72857,7 @@ function defaultStatusProbe(composePath) {
|
|
|
72655
72857
|
} catch {}
|
|
72656
72858
|
let dir = dirname13(scriptPath);
|
|
72657
72859
|
for (let i = 0;i < 8; i++) {
|
|
72658
|
-
const pkgPath =
|
|
72860
|
+
const pkgPath = join54(dir, "package.json");
|
|
72659
72861
|
if (existsSync52(pkgPath)) {
|
|
72660
72862
|
try {
|
|
72661
72863
|
const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
|
|
@@ -72875,7 +73077,7 @@ init_helpers();
|
|
|
72875
73077
|
init_lifecycle();
|
|
72876
73078
|
import { execSync as execSync4 } from "node:child_process";
|
|
72877
73079
|
import { existsSync as existsSync53, readFileSync as readFileSync48 } from "node:fs";
|
|
72878
|
-
import { dirname as dirname14, join as
|
|
73080
|
+
import { dirname as dirname14, join as join55 } from "node:path";
|
|
72879
73081
|
function getClaudeCodeVersion() {
|
|
72880
73082
|
try {
|
|
72881
73083
|
const out = execSync4("claude --version 2>/dev/null", {
|
|
@@ -72925,11 +73127,11 @@ function formatUptime3(timestamp) {
|
|
|
72925
73127
|
function locateSwitchroomInstallDir() {
|
|
72926
73128
|
let dir = import.meta.dirname;
|
|
72927
73129
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
72928
|
-
const pkgPath =
|
|
73130
|
+
const pkgPath = join55(dir, "package.json");
|
|
72929
73131
|
if (existsSync53(pkgPath)) {
|
|
72930
73132
|
try {
|
|
72931
73133
|
const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
|
|
72932
|
-
if (pkg.name === "switchroom" && existsSync53(
|
|
73134
|
+
if (pkg.name === "switchroom" && existsSync53(join55(dir, ".git"))) {
|
|
72933
73135
|
return dir;
|
|
72934
73136
|
}
|
|
72935
73137
|
} catch {}
|
|
@@ -73166,7 +73368,7 @@ import {
|
|
|
73166
73368
|
writeFileSync as writeFileSync26,
|
|
73167
73369
|
writeSync as writeSync7
|
|
73168
73370
|
} from "node:fs";
|
|
73169
|
-
import { join as
|
|
73371
|
+
import { join as join56 } from "node:path";
|
|
73170
73372
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
73171
73373
|
import { execSync as execSync5 } from "node:child_process";
|
|
73172
73374
|
|
|
@@ -73486,7 +73688,7 @@ function redactedMarker(ruleId) {
|
|
|
73486
73688
|
var ISSUES_FILE = "issues.jsonl";
|
|
73487
73689
|
var ISSUES_LOCK = "issues.lock";
|
|
73488
73690
|
function readAll(stateDir) {
|
|
73489
|
-
const path4 =
|
|
73691
|
+
const path4 = join56(stateDir, ISSUES_FILE);
|
|
73490
73692
|
if (!existsSync54(path4))
|
|
73491
73693
|
return [];
|
|
73492
73694
|
let raw;
|
|
@@ -73564,7 +73766,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
73564
73766
|
});
|
|
73565
73767
|
}
|
|
73566
73768
|
function resolve35(stateDir, fingerprint, nowFn = Date.now) {
|
|
73567
|
-
if (!existsSync54(
|
|
73769
|
+
if (!existsSync54(join56(stateDir, ISSUES_FILE)))
|
|
73568
73770
|
return 0;
|
|
73569
73771
|
return withLock(stateDir, () => {
|
|
73570
73772
|
const all = readAll(stateDir);
|
|
@@ -73582,7 +73784,7 @@ function resolve35(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
73582
73784
|
});
|
|
73583
73785
|
}
|
|
73584
73786
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
73585
|
-
if (!existsSync54(
|
|
73787
|
+
if (!existsSync54(join56(stateDir, ISSUES_FILE)))
|
|
73586
73788
|
return 0;
|
|
73587
73789
|
return withLock(stateDir, () => {
|
|
73588
73790
|
const all = readAll(stateDir);
|
|
@@ -73600,7 +73802,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
73600
73802
|
});
|
|
73601
73803
|
}
|
|
73602
73804
|
function prune(stateDir, opts = {}) {
|
|
73603
|
-
if (!existsSync54(
|
|
73805
|
+
if (!existsSync54(join56(stateDir, ISSUES_FILE)))
|
|
73604
73806
|
return 0;
|
|
73605
73807
|
return withLock(stateDir, () => {
|
|
73606
73808
|
const all = readAll(stateDir);
|
|
@@ -73633,7 +73835,7 @@ function ensureDir(stateDir) {
|
|
|
73633
73835
|
mkdirSync29(stateDir, { recursive: true });
|
|
73634
73836
|
}
|
|
73635
73837
|
function writeAll(stateDir, events) {
|
|
73636
|
-
const path4 =
|
|
73838
|
+
const path4 = join56(stateDir, ISSUES_FILE);
|
|
73637
73839
|
sweepOrphanTmpFiles(stateDir);
|
|
73638
73840
|
const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
|
|
73639
73841
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -73655,7 +73857,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
73655
73857
|
for (const entry of entries) {
|
|
73656
73858
|
if (!entry.startsWith(TMP_PREFIX))
|
|
73657
73859
|
continue;
|
|
73658
|
-
const tmpPath =
|
|
73860
|
+
const tmpPath = join56(stateDir, entry);
|
|
73659
73861
|
try {
|
|
73660
73862
|
const stat = statSync25(tmpPath);
|
|
73661
73863
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -73667,7 +73869,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
73667
73869
|
var LOCK_RETRY_MS = 25;
|
|
73668
73870
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
73669
73871
|
function withLock(stateDir, fn) {
|
|
73670
|
-
const lockPath =
|
|
73872
|
+
const lockPath = join56(stateDir, ISSUES_LOCK);
|
|
73671
73873
|
const startedAt = Date.now();
|
|
73672
73874
|
let fd = null;
|
|
73673
73875
|
while (fd === null) {
|
|
@@ -73951,8 +74153,8 @@ function relTime(deltaMs) {
|
|
|
73951
74153
|
// src/cli/deps.ts
|
|
73952
74154
|
init_source();
|
|
73953
74155
|
import { existsSync as existsSync57 } from "node:fs";
|
|
73954
|
-
import { homedir as
|
|
73955
|
-
import { join as
|
|
74156
|
+
import { homedir as homedir35 } from "node:os";
|
|
74157
|
+
import { join as join59, resolve as resolve36 } from "node:path";
|
|
73956
74158
|
|
|
73957
74159
|
// src/deps/python.ts
|
|
73958
74160
|
import { createHash as createHash11 } from "node:crypto";
|
|
@@ -73963,8 +74165,8 @@ import {
|
|
|
73963
74165
|
rmSync as rmSync13,
|
|
73964
74166
|
writeFileSync as writeFileSync27
|
|
73965
74167
|
} from "node:fs";
|
|
73966
|
-
import { dirname as dirname15, join as
|
|
73967
|
-
import { homedir as
|
|
74168
|
+
import { dirname as dirname15, join as join57 } from "node:path";
|
|
74169
|
+
import { homedir as homedir33 } from "node:os";
|
|
73968
74170
|
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
73969
74171
|
|
|
73970
74172
|
class PythonEnvError extends Error {
|
|
@@ -73976,7 +74178,7 @@ class PythonEnvError extends Error {
|
|
|
73976
74178
|
}
|
|
73977
74179
|
}
|
|
73978
74180
|
function defaultPythonCacheRoot() {
|
|
73979
|
-
return
|
|
74181
|
+
return join57(homedir33(), ".switchroom", "deps", "python");
|
|
73980
74182
|
}
|
|
73981
74183
|
function hashFile(path4) {
|
|
73982
74184
|
return createHash11("sha256").update(readFileSync50(path4)).digest("hex");
|
|
@@ -73988,11 +74190,11 @@ function ensurePythonEnv(opts) {
|
|
|
73988
74190
|
if (!existsSync55(requirementsPath)) {
|
|
73989
74191
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
73990
74192
|
}
|
|
73991
|
-
const venvDir =
|
|
73992
|
-
const stampPath =
|
|
73993
|
-
const binDir =
|
|
73994
|
-
const pythonBin =
|
|
73995
|
-
const pipBin =
|
|
74193
|
+
const venvDir = join57(cacheRoot, skillName);
|
|
74194
|
+
const stampPath = join57(venvDir, ".requirements.sha256");
|
|
74195
|
+
const binDir = join57(venvDir, "bin");
|
|
74196
|
+
const pythonBin = join57(binDir, "python");
|
|
74197
|
+
const pipBin = join57(binDir, "pip");
|
|
73996
74198
|
const targetHash = hashFile(requirementsPath);
|
|
73997
74199
|
if (!force && existsSync55(stampPath) && existsSync55(pythonBin)) {
|
|
73998
74200
|
const existingHash = readFileSync50(stampPath, "utf8").trim();
|
|
@@ -74051,8 +74253,8 @@ import {
|
|
|
74051
74253
|
rmSync as rmSync14,
|
|
74052
74254
|
writeFileSync as writeFileSync28
|
|
74053
74255
|
} from "node:fs";
|
|
74054
|
-
import { dirname as dirname16, join as
|
|
74055
|
-
import { homedir as
|
|
74256
|
+
import { dirname as dirname16, join as join58 } from "node:path";
|
|
74257
|
+
import { homedir as homedir34 } from "node:os";
|
|
74056
74258
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
74057
74259
|
|
|
74058
74260
|
class NodeEnvError extends Error {
|
|
@@ -74075,7 +74277,7 @@ var LOCKFILES_FOR = {
|
|
|
74075
74277
|
npm: ["package-lock.json"]
|
|
74076
74278
|
};
|
|
74077
74279
|
function defaultNodeCacheRoot() {
|
|
74078
|
-
return
|
|
74280
|
+
return join58(homedir34(), ".switchroom", "deps", "node");
|
|
74079
74281
|
}
|
|
74080
74282
|
function hashDepInputs(packageJsonPath) {
|
|
74081
74283
|
const sourceDir = dirname16(packageJsonPath);
|
|
@@ -74084,7 +74286,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
74084
74286
|
`);
|
|
74085
74287
|
hasher.update(readFileSync51(packageJsonPath));
|
|
74086
74288
|
for (const lockName of ALL_LOCKFILES) {
|
|
74087
|
-
const lockPath =
|
|
74289
|
+
const lockPath = join58(sourceDir, lockName);
|
|
74088
74290
|
if (existsSync56(lockPath)) {
|
|
74089
74291
|
hasher.update(`
|
|
74090
74292
|
`);
|
|
@@ -74104,10 +74306,10 @@ function ensureNodeEnv(opts) {
|
|
|
74104
74306
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
74105
74307
|
}
|
|
74106
74308
|
const sourceDir = dirname16(packageJsonPath);
|
|
74107
|
-
const envDir =
|
|
74108
|
-
const stampPath =
|
|
74109
|
-
const nodeModulesDir =
|
|
74110
|
-
const binDir =
|
|
74309
|
+
const envDir = join58(cacheRoot, skillName);
|
|
74310
|
+
const stampPath = join58(envDir, ".package.sha256");
|
|
74311
|
+
const nodeModulesDir = join58(envDir, "node_modules");
|
|
74312
|
+
const binDir = join58(nodeModulesDir, ".bin");
|
|
74111
74313
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
74112
74314
|
if (!force && existsSync56(stampPath) && existsSync56(nodeModulesDir)) {
|
|
74113
74315
|
const existingHash = readFileSync51(stampPath, "utf8").trim();
|
|
@@ -74125,12 +74327,12 @@ function ensureNodeEnv(opts) {
|
|
|
74125
74327
|
rmSync14(envDir, { recursive: true, force: true });
|
|
74126
74328
|
}
|
|
74127
74329
|
mkdirSync31(envDir, { recursive: true });
|
|
74128
|
-
copyFileSync9(packageJsonPath,
|
|
74330
|
+
copyFileSync9(packageJsonPath, join58(envDir, "package.json"));
|
|
74129
74331
|
let copiedLockfile = false;
|
|
74130
74332
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
74131
|
-
const lockPath =
|
|
74333
|
+
const lockPath = join58(sourceDir, lockName);
|
|
74132
74334
|
if (existsSync56(lockPath)) {
|
|
74133
|
-
copyFileSync9(lockPath,
|
|
74335
|
+
copyFileSync9(lockPath, join58(envDir, lockName));
|
|
74134
74336
|
copiedLockfile = true;
|
|
74135
74337
|
}
|
|
74136
74338
|
}
|
|
@@ -74159,7 +74361,7 @@ function ensureNodeEnv(opts) {
|
|
|
74159
74361
|
|
|
74160
74362
|
// src/cli/deps.ts
|
|
74161
74363
|
function builtinSkillsRoot() {
|
|
74162
|
-
return resolve36(
|
|
74364
|
+
return resolve36(homedir35(), ".switchroom/skills/_bundled");
|
|
74163
74365
|
}
|
|
74164
74366
|
function registerDepsCommand(program3) {
|
|
74165
74367
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
@@ -74169,13 +74371,13 @@ function registerDepsCommand(program3) {
|
|
|
74169
74371
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
74170
74372
|
process.exit(1);
|
|
74171
74373
|
}
|
|
74172
|
-
const skillDir =
|
|
74374
|
+
const skillDir = join59(skillsRoot, skill);
|
|
74173
74375
|
if (!existsSync57(skillDir)) {
|
|
74174
74376
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
74175
74377
|
process.exit(1);
|
|
74176
74378
|
}
|
|
74177
|
-
const requirementsPath =
|
|
74178
|
-
const packageJsonPath =
|
|
74379
|
+
const requirementsPath = join59(skillDir, "requirements.txt");
|
|
74380
|
+
const packageJsonPath = join59(skillDir, "package.json");
|
|
74179
74381
|
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync57(requirementsPath));
|
|
74180
74382
|
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync57(packageJsonPath));
|
|
74181
74383
|
let did = 0;
|
|
@@ -75130,7 +75332,7 @@ init_helpers();
|
|
|
75130
75332
|
init_loader();
|
|
75131
75333
|
init_merge();
|
|
75132
75334
|
import { copyFileSync as copyFileSync10, existsSync as existsSync59, readFileSync as readFileSync52, writeFileSync as writeFileSync29 } from "node:fs";
|
|
75133
|
-
import { join as
|
|
75335
|
+
import { join as join60, resolve as resolve38 } from "node:path";
|
|
75134
75336
|
init_schema();
|
|
75135
75337
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
75136
75338
|
const config = getConfig(program3);
|
|
@@ -75154,7 +75356,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
75154
75356
|
profileName,
|
|
75155
75357
|
profilePath,
|
|
75156
75358
|
workspaceDir,
|
|
75157
|
-
soulPath:
|
|
75359
|
+
soulPath: join60(workspaceDir, "SOUL.md"),
|
|
75158
75360
|
soul: merged.soul
|
|
75159
75361
|
};
|
|
75160
75362
|
}
|
|
@@ -75221,7 +75423,7 @@ function registerSoulCommand(program3) {
|
|
|
75221
75423
|
init_helpers();
|
|
75222
75424
|
init_loader();
|
|
75223
75425
|
import { existsSync as existsSync60, readFileSync as readFileSync53, readdirSync as readdirSync21, statSync as statSync26 } from "node:fs";
|
|
75224
|
-
import { resolve as resolve39, join as
|
|
75426
|
+
import { resolve as resolve39, join as join61 } from "node:path";
|
|
75225
75427
|
import { createHash as createHash13 } from "node:crypto";
|
|
75226
75428
|
init_merge();
|
|
75227
75429
|
init_hindsight();
|
|
@@ -75235,7 +75437,7 @@ function sha256(content) {
|
|
|
75235
75437
|
return createHash13("sha256").update(content).digest("hex").slice(0, 16);
|
|
75236
75438
|
}
|
|
75237
75439
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
75238
|
-
const projectsDir =
|
|
75440
|
+
const projectsDir = join61(claudeConfigDir, "projects");
|
|
75239
75441
|
if (!existsSync60(projectsDir))
|
|
75240
75442
|
return;
|
|
75241
75443
|
try {
|
|
@@ -75244,8 +75446,8 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
75244
75446
|
for (const entry of entries) {
|
|
75245
75447
|
if (!entry.isDirectory())
|
|
75246
75448
|
continue;
|
|
75247
|
-
const projectPath =
|
|
75248
|
-
const transcriptPath =
|
|
75449
|
+
const projectPath = join61(projectsDir, entry.name);
|
|
75450
|
+
const transcriptPath = join61(projectPath, "transcript.jsonl");
|
|
75249
75451
|
if (!existsSync60(transcriptPath))
|
|
75250
75452
|
continue;
|
|
75251
75453
|
const stat3 = statSync26(transcriptPath);
|
|
@@ -75314,11 +75516,11 @@ function registerDebugCommand(program3) {
|
|
|
75314
75516
|
process.exit(1);
|
|
75315
75517
|
}
|
|
75316
75518
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
75317
|
-
const claudeConfigDir =
|
|
75318
|
-
const claudeMdPath =
|
|
75319
|
-
const soulMdPath =
|
|
75320
|
-
const workspaceSoulMdPath =
|
|
75321
|
-
const handoffPath =
|
|
75519
|
+
const claudeConfigDir = join61(agentDir, ".claude");
|
|
75520
|
+
const claudeMdPath = join61(agentDir, "CLAUDE.md");
|
|
75521
|
+
const soulMdPath = join61(agentDir, "SOUL.md");
|
|
75522
|
+
const workspaceSoulMdPath = join61(workspaceDir, "SOUL.md");
|
|
75523
|
+
const handoffPath = join61(agentDir, ".handoff.md");
|
|
75322
75524
|
const lastN = parseInt(opts.last, 10);
|
|
75323
75525
|
if (isNaN(lastN) || lastN < 1) {
|
|
75324
75526
|
console.error("--last must be a positive integer");
|
|
@@ -75468,8 +75670,8 @@ init_source();
|
|
|
75468
75670
|
// src/worktree/claim.ts
|
|
75469
75671
|
import { execFileSync as execFileSync17 } from "node:child_process";
|
|
75470
75672
|
import { closeSync as closeSync12, mkdirSync as mkdirSync33, openSync as openSync12, existsSync as existsSync62, unlinkSync as unlinkSync13 } from "node:fs";
|
|
75471
|
-
import { join as
|
|
75472
|
-
import { homedir as
|
|
75673
|
+
import { join as join63, resolve as resolve41 } from "node:path";
|
|
75674
|
+
import { homedir as homedir37 } from "node:os";
|
|
75473
75675
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
75474
75676
|
|
|
75475
75677
|
// src/worktree/registry.ts
|
|
@@ -75482,13 +75684,13 @@ import {
|
|
|
75482
75684
|
existsSync as existsSync61,
|
|
75483
75685
|
renameSync as renameSync12
|
|
75484
75686
|
} from "node:fs";
|
|
75485
|
-
import { join as
|
|
75486
|
-
import { homedir as
|
|
75687
|
+
import { join as join62, resolve as resolve40 } from "node:path";
|
|
75688
|
+
import { homedir as homedir36 } from "node:os";
|
|
75487
75689
|
function registryDir() {
|
|
75488
|
-
return resolve40(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
75690
|
+
return resolve40(process.env.SWITCHROOM_WORKTREE_DIR ?? join62(homedir36(), ".switchroom", "worktrees"));
|
|
75489
75691
|
}
|
|
75490
75692
|
function recordPath(id) {
|
|
75491
|
-
return
|
|
75693
|
+
return join62(registryDir(), `${id}.json`);
|
|
75492
75694
|
}
|
|
75493
75695
|
function ensureDir2() {
|
|
75494
75696
|
mkdirSync32(registryDir(), { recursive: true });
|
|
@@ -75539,7 +75741,7 @@ function acquireRepoLock(repoPath) {
|
|
|
75539
75741
|
const lockDir = registryDir();
|
|
75540
75742
|
mkdirSync33(lockDir, { recursive: true });
|
|
75541
75743
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
75542
|
-
const lockPath =
|
|
75744
|
+
const lockPath = join63(lockDir, `.lock-${lockName}`);
|
|
75543
75745
|
const deadline = Date.now() + 5000;
|
|
75544
75746
|
let fd = null;
|
|
75545
75747
|
while (fd === null) {
|
|
@@ -75566,7 +75768,7 @@ function acquireRepoLock(repoPath) {
|
|
|
75566
75768
|
}
|
|
75567
75769
|
var DEFAULT_CONCURRENCY = 5;
|
|
75568
75770
|
function worktreesBaseDir() {
|
|
75569
|
-
return resolve41(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
75771
|
+
return resolve41(process.env.SWITCHROOM_WORKTREE_BASE ?? join63(homedir37(), ".switchroom", "worktree-checkouts"));
|
|
75570
75772
|
}
|
|
75571
75773
|
function shortId() {
|
|
75572
75774
|
return randomBytes13(4).toString("hex");
|
|
@@ -75588,7 +75790,7 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
75588
75790
|
}
|
|
75589
75791
|
function expandHome(p) {
|
|
75590
75792
|
if (p.startsWith("~/"))
|
|
75591
|
-
return
|
|
75793
|
+
return join63(homedir37(), p.slice(2));
|
|
75592
75794
|
return p;
|
|
75593
75795
|
}
|
|
75594
75796
|
async function claimWorktree(input, codeRepos) {
|
|
@@ -75616,7 +75818,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
75616
75818
|
branch = `task/${taskSuffix}-${id}`;
|
|
75617
75819
|
const baseDir = worktreesBaseDir();
|
|
75618
75820
|
mkdirSync33(baseDir, { recursive: true });
|
|
75619
|
-
worktreePath =
|
|
75821
|
+
worktreePath = join63(baseDir, `${id}-${taskSuffix}`);
|
|
75620
75822
|
const now = new Date().toISOString();
|
|
75621
75823
|
const record2 = {
|
|
75622
75824
|
id,
|
|
@@ -75871,7 +76073,7 @@ import {
|
|
|
75871
76073
|
rmSync as rmSync15,
|
|
75872
76074
|
writeFileSync as writeFileSync31
|
|
75873
76075
|
} from "node:fs";
|
|
75874
|
-
import { join as
|
|
76076
|
+
import { join as join64 } from "node:path";
|
|
75875
76077
|
function encodeCredentialsFilename(email) {
|
|
75876
76078
|
const SAFE = new Set([
|
|
75877
76079
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -76061,16 +76263,16 @@ function resolveCredentialsDir(env2) {
|
|
|
76061
76263
|
if (explicit && explicit.length > 0)
|
|
76062
76264
|
return explicit;
|
|
76063
76265
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
76064
|
-
return
|
|
76266
|
+
return join64(stateBase, "google-workspace-mcp", "credentials");
|
|
76065
76267
|
}
|
|
76066
76268
|
function writeSeedFile(dir, email, seed) {
|
|
76067
76269
|
mkdirSync34(dir, { recursive: true, mode: 448 });
|
|
76068
76270
|
chmodSync9(dir, 448);
|
|
76069
76271
|
for (const name of readdirSync23(dir)) {
|
|
76070
|
-
rmSync15(
|
|
76272
|
+
rmSync15(join64(dir, name), { force: true, recursive: true });
|
|
76071
76273
|
}
|
|
76072
76274
|
const filename = encodeCredentialsFilename(email);
|
|
76073
|
-
const filePath =
|
|
76275
|
+
const filePath = join64(dir, filename);
|
|
76074
76276
|
writeFileSync31(filePath, JSON.stringify(seed), { mode: 384 });
|
|
76075
76277
|
chmodSync9(filePath, 384);
|
|
76076
76278
|
return filePath;
|
|
@@ -76230,7 +76432,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
76230
76432
|
init_scaffold_integration();
|
|
76231
76433
|
import { spawn as spawn6 } from "node:child_process";
|
|
76232
76434
|
import { writeFileSync as writeFileSync32, mkdirSync as mkdirSync35 } from "node:fs";
|
|
76233
|
-
import { dirname as dirname17, join as
|
|
76435
|
+
import { dirname as dirname17, join as join65 } from "node:path";
|
|
76234
76436
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
76235
76437
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
76236
76438
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -76262,7 +76464,7 @@ function writeRefreshHeartbeat(agentName, data) {
|
|
|
76262
76464
|
function heartbeatPath(agentName) {
|
|
76263
76465
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
76264
76466
|
if (override) {
|
|
76265
|
-
return
|
|
76467
|
+
return join65(override, `m365-launcher-${agentName}.heartbeat.json`);
|
|
76266
76468
|
}
|
|
76267
76469
|
return "/state/agent/m365-launcher.heartbeat.json";
|
|
76268
76470
|
}
|
|
@@ -77181,8 +77383,8 @@ agents:
|
|
|
77181
77383
|
|
|
77182
77384
|
// src/cli/apply.ts
|
|
77183
77385
|
init_resolver();
|
|
77184
|
-
import { dirname as dirname21, join as
|
|
77185
|
-
import { homedir as
|
|
77386
|
+
import { dirname as dirname21, join as join69, resolve as resolve43 } from "node:path";
|
|
77387
|
+
import { homedir as homedir39 } from "node:os";
|
|
77186
77388
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
77187
77389
|
init_vault();
|
|
77188
77390
|
init_loader();
|
|
@@ -77190,7 +77392,7 @@ init_loader();
|
|
|
77190
77392
|
|
|
77191
77393
|
// src/cli/update-prompt-hook.ts
|
|
77192
77394
|
import { existsSync as existsSync66, readFileSync as readFileSync55, writeFileSync as writeFileSync34, chmodSync as chmodSync10, mkdirSync as mkdirSync37 } from "node:fs";
|
|
77193
|
-
import { join as
|
|
77395
|
+
import { join as join66 } from "node:path";
|
|
77194
77396
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
77195
77397
|
function updatePromptHookScript() {
|
|
77196
77398
|
return `#!/bin/bash
|
|
@@ -77256,9 +77458,9 @@ exit 0
|
|
|
77256
77458
|
`;
|
|
77257
77459
|
}
|
|
77258
77460
|
function installUpdatePromptHook(agentDir) {
|
|
77259
|
-
const hooksDir =
|
|
77461
|
+
const hooksDir = join66(agentDir, ".claude", "hooks");
|
|
77260
77462
|
mkdirSync37(hooksDir, { recursive: true });
|
|
77261
|
-
const scriptPath =
|
|
77463
|
+
const scriptPath = join66(hooksDir, HOOK_FILENAME);
|
|
77262
77464
|
const desired = updatePromptHookScript();
|
|
77263
77465
|
let installed = false;
|
|
77264
77466
|
const existing = existsSync66(scriptPath) ? readFileSync55(scriptPath, "utf-8") : "";
|
|
@@ -77271,7 +77473,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
77271
77473
|
chmodSync10(scriptPath, 493);
|
|
77272
77474
|
} catch {}
|
|
77273
77475
|
}
|
|
77274
|
-
const settingsPath =
|
|
77476
|
+
const settingsPath = join66(agentDir, ".claude", "settings.json");
|
|
77275
77477
|
if (!existsSync66(settingsPath)) {
|
|
77276
77478
|
return { scriptPath, settingsPath, installed };
|
|
77277
77479
|
}
|
|
@@ -77397,7 +77599,7 @@ import {
|
|
|
77397
77599
|
realpathSync as realpathSync6,
|
|
77398
77600
|
statSync as statSync27
|
|
77399
77601
|
} from "node:fs";
|
|
77400
|
-
import { join as
|
|
77602
|
+
import { join as join68 } from "node:path";
|
|
77401
77603
|
function resolveOperatorUid() {
|
|
77402
77604
|
const sudoUid = process.env.SUDO_UID;
|
|
77403
77605
|
if (sudoUid !== undefined) {
|
|
@@ -77413,14 +77615,14 @@ function resolveOperatorUid() {
|
|
|
77413
77615
|
return;
|
|
77414
77616
|
}
|
|
77415
77617
|
function operatorOwnedPaths(home2) {
|
|
77416
|
-
const root =
|
|
77618
|
+
const root = join68(home2, ".switchroom");
|
|
77417
77619
|
return [
|
|
77418
|
-
|
|
77419
|
-
|
|
77420
|
-
|
|
77421
|
-
|
|
77422
|
-
|
|
77423
|
-
|
|
77620
|
+
join68(root, "vault"),
|
|
77621
|
+
join68(root, "vault-auto-unlock"),
|
|
77622
|
+
join68(root, "vault-audit.log"),
|
|
77623
|
+
join68(root, "host-control-audit.log"),
|
|
77624
|
+
join68(root, "accounts"),
|
|
77625
|
+
join68(root, "compose")
|
|
77424
77626
|
];
|
|
77425
77627
|
}
|
|
77426
77628
|
function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
|
|
@@ -77469,7 +77671,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
|
|
|
77469
77671
|
} catch {}
|
|
77470
77672
|
if (isDir(target)) {
|
|
77471
77673
|
for (const entry of readdir2(target)) {
|
|
77472
|
-
visit(
|
|
77674
|
+
visit(join68(target, entry));
|
|
77473
77675
|
}
|
|
77474
77676
|
}
|
|
77475
77677
|
};
|
|
@@ -77483,14 +77685,14 @@ var EMBEDDED_EXAMPLES = {
|
|
|
77483
77685
|
switchroom: switchroom_default,
|
|
77484
77686
|
minimal: minimal_default
|
|
77485
77687
|
};
|
|
77486
|
-
var DEFAULT_COMPOSE_PATH2 =
|
|
77688
|
+
var DEFAULT_COMPOSE_PATH2 = join69(homedir39(), ".switchroom", "compose", "docker-compose.yml");
|
|
77487
77689
|
var COMPOSE_PROJECT2 = "switchroom";
|
|
77488
77690
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
77489
77691
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
77490
77692
|
if (isCustomPath && ctx.customVaultPath) {
|
|
77491
77693
|
return dirname21(ctx.customVaultPath);
|
|
77492
77694
|
}
|
|
77493
|
-
return
|
|
77695
|
+
return join69(homeDir, ".switchroom", "vault");
|
|
77494
77696
|
}
|
|
77495
77697
|
function inspectVaultBindMountDir(vaultDir) {
|
|
77496
77698
|
if (!existsSync69(vaultDir))
|
|
@@ -77519,44 +77721,44 @@ function hasVaultRefs(value) {
|
|
|
77519
77721
|
return false;
|
|
77520
77722
|
}
|
|
77521
77723
|
async function ensureHostMountSources(config) {
|
|
77522
|
-
const home2 =
|
|
77724
|
+
const home2 = homedir39();
|
|
77523
77725
|
const dirs = [
|
|
77524
|
-
|
|
77525
|
-
|
|
77526
|
-
|
|
77527
|
-
|
|
77528
|
-
|
|
77726
|
+
join69(home2, ".switchroom", "approvals"),
|
|
77727
|
+
join69(home2, ".switchroom", "scheduler"),
|
|
77728
|
+
join69(home2, ".switchroom", "logs"),
|
|
77729
|
+
join69(home2, ".switchroom", "compose"),
|
|
77730
|
+
join69(home2, ".switchroom", "broker-operator")
|
|
77529
77731
|
];
|
|
77530
77732
|
for (const name of Object.keys(config.agents)) {
|
|
77531
|
-
dirs.push(
|
|
77532
|
-
dirs.push(
|
|
77533
|
-
dirs.push(
|
|
77534
|
-
dirs.push(
|
|
77535
|
-
if (existsSync69(
|
|
77536
|
-
dirs.push(
|
|
77733
|
+
dirs.push(join69(home2, ".switchroom", "agents", name));
|
|
77734
|
+
dirs.push(join69(home2, ".switchroom", "logs", name));
|
|
77735
|
+
dirs.push(join69(home2, ".claude", "projects", name));
|
|
77736
|
+
dirs.push(join69(home2, ".switchroom", "audit", name));
|
|
77737
|
+
if (existsSync69(join69(home2, ".switchroom-config"))) {
|
|
77738
|
+
dirs.push(join69(home2, ".switchroom-config", "agents", name, "personal-skills"));
|
|
77537
77739
|
}
|
|
77538
77740
|
}
|
|
77539
77741
|
for (const dir of dirs) {
|
|
77540
77742
|
await mkdir(dir, { recursive: true });
|
|
77541
77743
|
}
|
|
77542
|
-
const autoUnlockPath =
|
|
77744
|
+
const autoUnlockPath = join69(home2, ".switchroom", "vault-auto-unlock");
|
|
77543
77745
|
if (!existsSync69(autoUnlockPath)) {
|
|
77544
77746
|
writeFileSync35(autoUnlockPath, "", { mode: 384 });
|
|
77545
77747
|
}
|
|
77546
|
-
const auditLogPath =
|
|
77748
|
+
const auditLogPath = join69(home2, ".switchroom", "vault-audit.log");
|
|
77547
77749
|
if (!existsSync69(auditLogPath)) {
|
|
77548
77750
|
writeFileSync35(auditLogPath, "", { mode: 420 });
|
|
77549
77751
|
}
|
|
77550
|
-
const grantsDbPath =
|
|
77752
|
+
const grantsDbPath = join69(home2, ".switchroom", "vault-grants.db");
|
|
77551
77753
|
if (!existsSync69(grantsDbPath)) {
|
|
77552
77754
|
writeFileSync35(grantsDbPath, "", { mode: 384 });
|
|
77553
77755
|
}
|
|
77554
|
-
const hostdAuditLogPath =
|
|
77756
|
+
const hostdAuditLogPath = join69(home2, ".switchroom", "host-control-audit.log");
|
|
77555
77757
|
if (!existsSync69(hostdAuditLogPath)) {
|
|
77556
77758
|
writeFileSync35(hostdAuditLogPath, "", { mode: 420 });
|
|
77557
77759
|
}
|
|
77558
77760
|
for (const name of Object.keys(config.agents)) {
|
|
77559
|
-
const tokenPath =
|
|
77761
|
+
const tokenPath = join69(home2, ".switchroom", "agents", name, ".vault-token");
|
|
77560
77762
|
if (!existsSync69(tokenPath)) {
|
|
77561
77763
|
writeFileSync35(tokenPath, "", { mode: 384 });
|
|
77562
77764
|
}
|
|
@@ -77565,15 +77767,15 @@ async function ensureHostMountSources(config) {
|
|
|
77565
77767
|
chownSync4(tokenPath, uid, uid);
|
|
77566
77768
|
} catch {}
|
|
77567
77769
|
}
|
|
77568
|
-
const fleetDir =
|
|
77770
|
+
const fleetDir = join69(home2, ".switchroom", "fleet");
|
|
77569
77771
|
await mkdir(fleetDir, { recursive: true });
|
|
77570
|
-
const invariantsPath =
|
|
77772
|
+
const invariantsPath = join69(fleetDir, "switchroom-invariants.md");
|
|
77571
77773
|
const invariantsCanonical = renderFleetInvariants();
|
|
77572
77774
|
const invariantsCurrent = existsSync69(invariantsPath) ? readFileSync56(invariantsPath, "utf-8") : null;
|
|
77573
77775
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
77574
77776
|
writeFileSync35(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
77575
77777
|
}
|
|
77576
|
-
const fleetClaudePath =
|
|
77778
|
+
const fleetClaudePath = join69(fleetDir, "CLAUDE.md");
|
|
77577
77779
|
if (!existsSync69(fleetClaudePath)) {
|
|
77578
77780
|
writeFileSync35(fleetClaudePath, [
|
|
77579
77781
|
"# Switchroom fleet defaults",
|
|
@@ -77656,10 +77858,10 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
77656
77858
|
`));
|
|
77657
77859
|
}
|
|
77658
77860
|
}
|
|
77659
|
-
function writeInstallTypeCache(homeDir =
|
|
77861
|
+
function writeInstallTypeCache(homeDir = homedir39()) {
|
|
77660
77862
|
const ctx = detectInstallType();
|
|
77661
|
-
const dir =
|
|
77662
|
-
const out =
|
|
77863
|
+
const dir = join69(homeDir, ".switchroom");
|
|
77864
|
+
const out = join69(dir, "install-type.json");
|
|
77663
77865
|
const tmp = `${out}.tmp`;
|
|
77664
77866
|
mkdirSync38(dir, { recursive: true });
|
|
77665
77867
|
const payload = {
|
|
@@ -77708,14 +77910,14 @@ Applying switchroom config...
|
|
|
77708
77910
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
77709
77911
|
`));
|
|
77710
77912
|
try {
|
|
77711
|
-
installUpdatePromptHook(
|
|
77913
|
+
installUpdatePromptHook(join69(agentsDir, name));
|
|
77712
77914
|
} catch (hookErr) {
|
|
77713
77915
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
77714
77916
|
`));
|
|
77715
77917
|
}
|
|
77716
77918
|
try {
|
|
77717
77919
|
const uid = allocateAgentUid(name);
|
|
77718
|
-
alignAgentUid(name,
|
|
77920
|
+
alignAgentUid(name, join69(agentsDir, name), uid, {
|
|
77719
77921
|
confirm: !options.nonInteractive,
|
|
77720
77922
|
writeOut
|
|
77721
77923
|
});
|
|
@@ -77752,7 +77954,7 @@ Applying switchroom config...
|
|
|
77752
77954
|
for (const name of agentNames) {
|
|
77753
77955
|
try {
|
|
77754
77956
|
const uid = allocateAgentUid(name);
|
|
77755
|
-
alignAgentUid(name,
|
|
77957
|
+
alignAgentUid(name, join69(agentsDir, name), uid, {
|
|
77756
77958
|
confirm: !options.nonInteractive,
|
|
77757
77959
|
writeOut
|
|
77758
77960
|
});
|
|
@@ -77766,7 +77968,7 @@ Applying switchroom config...
|
|
|
77766
77968
|
}
|
|
77767
77969
|
const vaultPathConfigured = config.vault?.path;
|
|
77768
77970
|
const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
|
|
77769
|
-
const migrationResult = migrateVaultLayout(
|
|
77971
|
+
const migrationResult = migrateVaultLayout(homedir39(), {
|
|
77770
77972
|
customVaultPath
|
|
77771
77973
|
});
|
|
77772
77974
|
switch (migrationResult.kind) {
|
|
@@ -77792,7 +77994,7 @@ Applying switchroom config...
|
|
|
77792
77994
|
writeErr(formatDivergentRecoveryMessage(migrationResult.details));
|
|
77793
77995
|
process.exit(4);
|
|
77794
77996
|
}
|
|
77795
|
-
const postMigrationInspect = inspectVaultLayout(
|
|
77997
|
+
const postMigrationInspect = inspectVaultLayout(homedir39());
|
|
77796
77998
|
const acceptable = [
|
|
77797
77999
|
"no-vault",
|
|
77798
78000
|
"already-migrated",
|
|
@@ -77807,7 +78009,7 @@ Applying switchroom config...
|
|
|
77807
78009
|
`));
|
|
77808
78010
|
process.exit(5);
|
|
77809
78011
|
}
|
|
77810
|
-
const vaultDir = resolveVaultBindMountDir(
|
|
78012
|
+
const vaultDir = resolveVaultBindMountDir(homedir39(), {
|
|
77811
78013
|
migrationKind: migrationResult.kind,
|
|
77812
78014
|
customVaultPath
|
|
77813
78015
|
});
|
|
@@ -77837,7 +78039,7 @@ Applying switchroom config...
|
|
|
77837
78039
|
imageTag: composeImageTag,
|
|
77838
78040
|
buildMode: options.buildLocal ? "local" : "pull",
|
|
77839
78041
|
buildContext: options.buildContext,
|
|
77840
|
-
homeDir:
|
|
78042
|
+
homeDir: homedir39(),
|
|
77841
78043
|
switchroomConfigPath,
|
|
77842
78044
|
operatorUid
|
|
77843
78045
|
});
|
|
@@ -77857,7 +78059,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
|
|
|
77857
78059
|
writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
|
|
77858
78060
|
`));
|
|
77859
78061
|
if (process.geteuid?.() === 0 && operatorUid !== undefined) {
|
|
77860
|
-
const restored = restoreOperatorOwnership(
|
|
78062
|
+
const restored = restoreOperatorOwnership(homedir39(), operatorUid);
|
|
77861
78063
|
if (restored.length > 0) {
|
|
77862
78064
|
writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
|
|
77863
78065
|
`));
|
|
@@ -77927,7 +78129,7 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
77927
78129
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
77928
78130
|
const unwritable = [];
|
|
77929
78131
|
for (const name of targets) {
|
|
77930
|
-
const startSh =
|
|
78132
|
+
const startSh = join69(agentsDir, name, "start.sh");
|
|
77931
78133
|
if (!existsSync69(startSh))
|
|
77932
78134
|
continue;
|
|
77933
78135
|
try {
|
|
@@ -78107,8 +78309,8 @@ function runRedactStdin() {
|
|
|
78107
78309
|
|
|
78108
78310
|
// src/cli/status-ask.ts
|
|
78109
78311
|
import { readFileSync as readFileSync57, existsSync as existsSync70, readdirSync as readdirSync26 } from "node:fs";
|
|
78110
|
-
import { join as
|
|
78111
|
-
import { homedir as
|
|
78312
|
+
import { join as join70 } from "node:path";
|
|
78313
|
+
import { homedir as homedir40 } from "node:os";
|
|
78112
78314
|
|
|
78113
78315
|
// src/status-ask/report.ts
|
|
78114
78316
|
function parseJsonl(content) {
|
|
@@ -78443,7 +78645,7 @@ function resolveSources(explicitPath) {
|
|
|
78443
78645
|
const config = loadConfig();
|
|
78444
78646
|
agentsDir = resolveAgentsDir(config);
|
|
78445
78647
|
} catch {
|
|
78446
|
-
agentsDir =
|
|
78648
|
+
agentsDir = join70(homedir40(), ".switchroom", "agents");
|
|
78447
78649
|
}
|
|
78448
78650
|
if (!existsSync70(agentsDir))
|
|
78449
78651
|
return [];
|
|
@@ -78455,7 +78657,7 @@ function resolveSources(explicitPath) {
|
|
|
78455
78657
|
return [];
|
|
78456
78658
|
}
|
|
78457
78659
|
for (const name of entries) {
|
|
78458
|
-
const path8 =
|
|
78660
|
+
const path8 = join70(agentsDir, name, "runtime-metrics.jsonl");
|
|
78459
78661
|
if (existsSync70(path8)) {
|
|
78460
78662
|
sources.push({ path: path8, agent: name });
|
|
78461
78663
|
}
|
|
@@ -78477,17 +78679,17 @@ function inferAgentFromPath(p) {
|
|
|
78477
78679
|
|
|
78478
78680
|
// src/cli/agent-config.ts
|
|
78479
78681
|
init_helpers();
|
|
78480
|
-
import { join as
|
|
78481
|
-
import { homedir as
|
|
78682
|
+
import { join as join71 } from "node:path";
|
|
78683
|
+
import { homedir as homedir41 } from "node:os";
|
|
78482
78684
|
import {
|
|
78483
78685
|
existsSync as existsSync71,
|
|
78484
78686
|
mkdirSync as mkdirSync39,
|
|
78485
78687
|
appendFileSync as appendFileSync4,
|
|
78486
78688
|
readFileSync as readFileSync58
|
|
78487
78689
|
} from "node:fs";
|
|
78488
|
-
var AUDIT_ROOT =
|
|
78690
|
+
var AUDIT_ROOT = join71(homedir41(), ".switchroom", "audit");
|
|
78489
78691
|
function auditPathFor(agent) {
|
|
78490
|
-
return
|
|
78692
|
+
return join71(AUDIT_ROOT, agent, "agent-config.jsonl");
|
|
78491
78693
|
}
|
|
78492
78694
|
function appendAudit(agent, cmd, args, exit, opts = {}) {
|
|
78493
78695
|
const row = {
|
|
@@ -78745,21 +78947,21 @@ import {
|
|
|
78745
78947
|
unlinkSync as unlinkSync14,
|
|
78746
78948
|
writeSync as writeSync8
|
|
78747
78949
|
} from "node:fs";
|
|
78748
|
-
import { join as
|
|
78950
|
+
import { join as join72, resolve as resolve44 } from "node:path";
|
|
78749
78951
|
var STAGING_SUBDIR = ".staging";
|
|
78750
78952
|
function overlayPathsFor(agent, opts = {}) {
|
|
78751
78953
|
const base = opts.root ? resolve44(opts.root, agent) : resolve44(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
78752
|
-
const scheduleDir =
|
|
78753
|
-
const scheduleStagingDir =
|
|
78754
|
-
const skillsDir =
|
|
78755
|
-
const skillsStagingDir =
|
|
78954
|
+
const scheduleDir = join72(base, "schedule.d");
|
|
78955
|
+
const scheduleStagingDir = join72(scheduleDir, STAGING_SUBDIR);
|
|
78956
|
+
const skillsDir = join72(base, "skills.d");
|
|
78957
|
+
const skillsStagingDir = join72(skillsDir, STAGING_SUBDIR);
|
|
78756
78958
|
return {
|
|
78757
78959
|
agentRoot: base,
|
|
78758
78960
|
scheduleDir,
|
|
78759
78961
|
scheduleStagingDir,
|
|
78760
78962
|
skillsDir,
|
|
78761
78963
|
skillsStagingDir,
|
|
78762
|
-
lockPath:
|
|
78964
|
+
lockPath: join72(base, ".lock"),
|
|
78763
78965
|
stagingDir: scheduleStagingDir
|
|
78764
78966
|
};
|
|
78765
78967
|
}
|
|
@@ -78813,8 +79015,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
78813
79015
|
const paths = overlayPathsFor(agent, opts);
|
|
78814
79016
|
return withAgentLock(paths, () => {
|
|
78815
79017
|
ensureDirs(paths);
|
|
78816
|
-
const stagingPath =
|
|
78817
|
-
const finalPath =
|
|
79018
|
+
const stagingPath = join72(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
79019
|
+
const finalPath = join72(paths.scheduleDir, `${slug}.yaml`);
|
|
78818
79020
|
const fd = openSync13(stagingPath, "w", 384);
|
|
78819
79021
|
try {
|
|
78820
79022
|
writeSync8(fd, yamlText);
|
|
@@ -78830,8 +79032,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
78830
79032
|
const paths = overlayPathsFor(agent, opts);
|
|
78831
79033
|
return withAgentLock(paths, () => {
|
|
78832
79034
|
ensureSkillsDirs(paths);
|
|
78833
|
-
const stagingPath =
|
|
78834
|
-
const finalPath =
|
|
79035
|
+
const stagingPath = join72(paths.skillsStagingDir, `${slug}.yaml`);
|
|
79036
|
+
const finalPath = join72(paths.skillsDir, `${slug}.yaml`);
|
|
78835
79037
|
const fd = openSync13(stagingPath, "w", 384);
|
|
78836
79038
|
try {
|
|
78837
79039
|
writeSync8(fd, yamlText);
|
|
@@ -78846,7 +79048,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
78846
79048
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
78847
79049
|
const paths = overlayPathsFor(agent, opts);
|
|
78848
79050
|
return withAgentLock(paths, () => {
|
|
78849
|
-
const finalPath =
|
|
79051
|
+
const finalPath = join72(paths.skillsDir, `${slug}.yaml`);
|
|
78850
79052
|
if (!existsSync72(finalPath))
|
|
78851
79053
|
return false;
|
|
78852
79054
|
unlinkSync14(finalPath);
|
|
@@ -78861,7 +79063,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
78861
79063
|
for (const name of readdirSync27(paths.skillsDir)) {
|
|
78862
79064
|
if (!/\.ya?ml$/i.test(name))
|
|
78863
79065
|
continue;
|
|
78864
|
-
const full =
|
|
79066
|
+
const full = join72(paths.skillsDir, name);
|
|
78865
79067
|
try {
|
|
78866
79068
|
const raw = readFileSync59(full, "utf-8");
|
|
78867
79069
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -78873,7 +79075,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
78873
79075
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
78874
79076
|
const paths = overlayPathsFor(agent, opts);
|
|
78875
79077
|
return withAgentLock(paths, () => {
|
|
78876
|
-
const finalPath =
|
|
79078
|
+
const finalPath = join72(paths.scheduleDir, `${slug}.yaml`);
|
|
78877
79079
|
if (!existsSync72(finalPath))
|
|
78878
79080
|
return false;
|
|
78879
79081
|
unlinkSync14(finalPath);
|
|
@@ -78888,7 +79090,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
78888
79090
|
for (const name of readdirSync27(paths.scheduleDir)) {
|
|
78889
79091
|
if (!/\.ya?ml$/i.test(name))
|
|
78890
79092
|
continue;
|
|
78891
|
-
const full =
|
|
79093
|
+
const full = join72(paths.scheduleDir, name);
|
|
78892
79094
|
try {
|
|
78893
79095
|
const raw = readFileSync59(full, "utf-8");
|
|
78894
79096
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -79044,12 +79246,12 @@ import {
|
|
|
79044
79246
|
writeFileSync as writeFileSync36,
|
|
79045
79247
|
writeSync as writeSync9
|
|
79046
79248
|
} from "node:fs";
|
|
79047
|
-
import { join as
|
|
79249
|
+
import { join as join73 } from "node:path";
|
|
79048
79250
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
79049
79251
|
var STAGE_ID_PREFIX = "cap_";
|
|
79050
79252
|
function pendingDir(agent, opts = {}) {
|
|
79051
79253
|
const paths = overlayPathsFor(agent, opts);
|
|
79052
|
-
return
|
|
79254
|
+
return join73(paths.scheduleDir, ".pending");
|
|
79053
79255
|
}
|
|
79054
79256
|
function ensurePendingDir(agent, opts = {}) {
|
|
79055
79257
|
const dir = pendingDir(agent, opts);
|
|
@@ -79062,8 +79264,8 @@ function newStageId() {
|
|
|
79062
79264
|
function stagePendingScheduleEntry(opts) {
|
|
79063
79265
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
79064
79266
|
const stageId = opts.stageId ?? newStageId();
|
|
79065
|
-
const yamlPath =
|
|
79066
|
-
const metaPath =
|
|
79267
|
+
const yamlPath = join73(dir, `${stageId}.yaml`);
|
|
79268
|
+
const metaPath = join73(dir, `${stageId}.meta.json`);
|
|
79067
79269
|
const meta = {
|
|
79068
79270
|
v: 1,
|
|
79069
79271
|
stage_id: stageId,
|
|
@@ -79097,8 +79299,8 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
79097
79299
|
if (!name.endsWith(".meta.json"))
|
|
79098
79300
|
continue;
|
|
79099
79301
|
const stageId = name.slice(0, -".meta.json".length);
|
|
79100
|
-
const metaPath =
|
|
79101
|
-
const yamlPath =
|
|
79302
|
+
const metaPath = join73(dir, name);
|
|
79303
|
+
const yamlPath = join73(dir, `${stageId}.yaml`);
|
|
79102
79304
|
if (!existsSync73(yamlPath))
|
|
79103
79305
|
continue;
|
|
79104
79306
|
try {
|
|
@@ -79117,7 +79319,7 @@ function commitPendingScheduleEntry(opts) {
|
|
|
79117
79319
|
return { committed: false, reason: "not_found" };
|
|
79118
79320
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
79119
79321
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
79120
|
-
const finalPath =
|
|
79322
|
+
const finalPath = join73(paths.scheduleDir, `${slug}.yaml`);
|
|
79121
79323
|
if (existsSync73(finalPath)) {
|
|
79122
79324
|
return { committed: false, reason: "slug_collision" };
|
|
79123
79325
|
}
|
|
@@ -79623,7 +79825,7 @@ var import_yaml16 = __toESM(require_dist(), 1);
|
|
|
79623
79825
|
import { existsSync as existsSync75 } from "node:fs";
|
|
79624
79826
|
init_reconcile_default_skills();
|
|
79625
79827
|
var import_yaml17 = __toESM(require_dist(), 1);
|
|
79626
|
-
import { join as
|
|
79828
|
+
import { join as join74 } from "node:path";
|
|
79627
79829
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
79628
79830
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
79629
79831
|
function exitCodeFor2(code) {
|
|
@@ -79698,7 +79900,7 @@ function skillInstall(opts) {
|
|
|
79698
79900
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
79699
79901
|
}
|
|
79700
79902
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
79701
|
-
const skillPath =
|
|
79903
|
+
const skillPath = join74(poolDir, skillName);
|
|
79702
79904
|
if (!existsSync75(skillPath)) {
|
|
79703
79905
|
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.`);
|
|
79704
79906
|
}
|
|
@@ -79876,8 +80078,8 @@ import {
|
|
|
79876
80078
|
statSync as statSync29,
|
|
79877
80079
|
writeFileSync as writeFileSync37
|
|
79878
80080
|
} from "node:fs";
|
|
79879
|
-
import { tmpdir as tmpdir4, homedir as
|
|
79880
|
-
import { dirname as dirname22, join as
|
|
80081
|
+
import { tmpdir as tmpdir4, homedir as homedir42 } from "node:os";
|
|
80082
|
+
import { dirname as dirname22, join as join75, relative as relative2, resolve as resolve45 } from "node:path";
|
|
79881
80083
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
79882
80084
|
|
|
79883
80085
|
// src/cli/skill-common.ts
|
|
@@ -80071,10 +80273,10 @@ function scanForClaudeP2(content) {
|
|
|
80071
80273
|
function resolveSkillsPoolDir2(override) {
|
|
80072
80274
|
const raw = override ?? "~/.switchroom/skills";
|
|
80073
80275
|
if (raw.startsWith("~/")) {
|
|
80074
|
-
return
|
|
80276
|
+
return join75(homedir42(), raw.slice(2));
|
|
80075
80277
|
}
|
|
80076
80278
|
if (raw === "~")
|
|
80077
|
-
return
|
|
80279
|
+
return homedir42();
|
|
80078
80280
|
return resolve45(raw);
|
|
80079
80281
|
}
|
|
80080
80282
|
function readStdinSync() {
|
|
@@ -80110,7 +80312,7 @@ function loadFromDir(dir) {
|
|
|
80110
80312
|
const walk2 = (sub) => {
|
|
80111
80313
|
const entries = readdirSync29(sub, { withFileTypes: true });
|
|
80112
80314
|
for (const ent of entries) {
|
|
80113
|
-
const full =
|
|
80315
|
+
const full = join75(sub, ent.name);
|
|
80114
80316
|
const rel = relative2(abs, full);
|
|
80115
80317
|
if (ent.isSymbolicLink()) {
|
|
80116
80318
|
fail2(`refusing to read symlink inside --from dir: ${rel}`);
|
|
@@ -80145,7 +80347,7 @@ function loadFromTarball(tarPath) {
|
|
|
80145
80347
|
fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
80146
80348
|
}
|
|
80147
80349
|
}
|
|
80148
|
-
const staging = mkdtempSync5(
|
|
80350
|
+
const staging = mkdtempSync5(join75(tmpdir4(), "skill-apply-extract-"));
|
|
80149
80351
|
try {
|
|
80150
80352
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
80151
80353
|
const r = spawnSync10("tar", [
|
|
@@ -80231,8 +80433,8 @@ function validatePayload(name, files) {
|
|
|
80231
80433
|
errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
80232
80434
|
}
|
|
80233
80435
|
} else if (PY_SCRIPT_RE2.test(path8)) {
|
|
80234
|
-
const tmp = mkdtempSync5(
|
|
80235
|
-
const tmpPy =
|
|
80436
|
+
const tmp = mkdtempSync5(join75(tmpdir4(), "skill-apply-py-"));
|
|
80437
|
+
const tmpPy = join75(tmp, "check.py");
|
|
80236
80438
|
try {
|
|
80237
80439
|
writeFileSync37(tmpPy, content);
|
|
80238
80440
|
const r = spawnSync10("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -80255,7 +80457,7 @@ function diffSummary(currentDir, files) {
|
|
|
80255
80457
|
if (existsSync76(currentDir)) {
|
|
80256
80458
|
const walk2 = (sub) => {
|
|
80257
80459
|
for (const ent of readdirSync29(sub, { withFileTypes: true })) {
|
|
80258
|
-
const full =
|
|
80460
|
+
const full = join75(sub, ent.name);
|
|
80259
80461
|
const rel = relative2(currentDir, full);
|
|
80260
80462
|
if (ent.isDirectory()) {
|
|
80261
80463
|
walk2(full);
|
|
@@ -80291,7 +80493,7 @@ function writePayload(poolDir, name, files) {
|
|
|
80291
80493
|
if (!existsSync76(poolDir)) {
|
|
80292
80494
|
mkdirSync42(poolDir, { recursive: true, mode: 493 });
|
|
80293
80495
|
}
|
|
80294
|
-
const target =
|
|
80496
|
+
const target = join75(poolDir, name);
|
|
80295
80497
|
let targetIsSymlink = false;
|
|
80296
80498
|
try {
|
|
80297
80499
|
const st = lstatSync8(target);
|
|
@@ -80302,11 +80504,11 @@ function writePayload(poolDir, name, files) {
|
|
|
80302
80504
|
if (targetIsSymlink) {
|
|
80303
80505
|
fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
80304
80506
|
}
|
|
80305
|
-
const staging = mkdtempSync5(
|
|
80507
|
+
const staging = mkdtempSync5(join75(poolDir, `.skill-apply-stage-${name}-`));
|
|
80306
80508
|
let oldRename = null;
|
|
80307
80509
|
try {
|
|
80308
80510
|
for (const [path8, content] of Object.entries(files)) {
|
|
80309
|
-
const full =
|
|
80511
|
+
const full = join75(staging, path8);
|
|
80310
80512
|
mkdirSync42(dirname22(full), { recursive: true, mode: 493 });
|
|
80311
80513
|
const fd = openSync15(full, "wx");
|
|
80312
80514
|
try {
|
|
@@ -80384,7 +80586,7 @@ function registerSkillCommand(program3) {
|
|
|
80384
80586
|
}
|
|
80385
80587
|
const config = loadConfig();
|
|
80386
80588
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
80387
|
-
const currentDir =
|
|
80589
|
+
const currentDir = join75(poolDir, name);
|
|
80388
80590
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
80389
80591
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
80390
80592
|
console.log(diffSummary(currentDir, files));
|
|
@@ -80428,8 +80630,8 @@ import {
|
|
|
80428
80630
|
utimesSync,
|
|
80429
80631
|
writeFileSync as writeFileSync38
|
|
80430
80632
|
} from "node:fs";
|
|
80431
|
-
import { dirname as dirname23, join as
|
|
80432
|
-
import { homedir as
|
|
80633
|
+
import { dirname as dirname23, join as join76, relative as relative3, resolve as resolve46 } from "node:path";
|
|
80634
|
+
import { homedir as homedir43, tmpdir as tmpdir5 } from "node:os";
|
|
80433
80635
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
80434
80636
|
init_helpers();
|
|
80435
80637
|
init_source();
|
|
@@ -80439,10 +80641,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
80439
80641
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
80440
80642
|
function resolveConfigSkillsDir(agent) {
|
|
80441
80643
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
80442
|
-
const candidate = override ? resolve46(override) :
|
|
80644
|
+
const candidate = override ? resolve46(override) : join76(homedir43(), ".switchroom-config");
|
|
80443
80645
|
if (!existsSync77(candidate))
|
|
80444
80646
|
return null;
|
|
80445
|
-
return
|
|
80647
|
+
return join76(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
80446
80648
|
}
|
|
80447
80649
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
80448
80650
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
@@ -80460,7 +80662,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
80460
80662
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
80461
80663
|
continue;
|
|
80462
80664
|
try {
|
|
80463
|
-
rmSync17(
|
|
80665
|
+
rmSync17(join76(configSkillsRoot, ent), { recursive: true, force: true });
|
|
80464
80666
|
} catch {}
|
|
80465
80667
|
}
|
|
80466
80668
|
} catch {}
|
|
@@ -80469,7 +80671,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
80469
80671
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
80470
80672
|
if (!configSkillsRoot)
|
|
80471
80673
|
return;
|
|
80472
|
-
const dest =
|
|
80674
|
+
const dest = join76(configSkillsRoot, name);
|
|
80473
80675
|
try {
|
|
80474
80676
|
if (liveSkillDir !== null) {
|
|
80475
80677
|
try {
|
|
@@ -80484,19 +80686,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
80484
80686
|
if (liveSkillDir === null) {
|
|
80485
80687
|
sweepMirrorPriors(configSkillsRoot);
|
|
80486
80688
|
if (existsSync77(dest)) {
|
|
80487
|
-
const trash =
|
|
80689
|
+
const trash = join76(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
80488
80690
|
renameSync17(dest, trash);
|
|
80489
80691
|
}
|
|
80490
80692
|
return;
|
|
80491
80693
|
}
|
|
80492
80694
|
mkdirSync43(configSkillsRoot, { recursive: true, mode: 493 });
|
|
80493
80695
|
sweepMirrorPriors(configSkillsRoot);
|
|
80494
|
-
const staging = mkdtempSync6(
|
|
80696
|
+
const staging = mkdtempSync6(join76(configSkillsRoot, `.${name}-staging-`));
|
|
80495
80697
|
const walk2 = (src, dst) => {
|
|
80496
80698
|
mkdirSync43(dst, { recursive: true, mode: 493 });
|
|
80497
80699
|
for (const ent of readdirSync30(src, { withFileTypes: true })) {
|
|
80498
|
-
const s =
|
|
80499
|
-
const d =
|
|
80700
|
+
const s = join76(src, ent.name);
|
|
80701
|
+
const d = join76(dst, ent.name);
|
|
80500
80702
|
if (ent.isSymbolicLink())
|
|
80501
80703
|
continue;
|
|
80502
80704
|
if (ent.isDirectory())
|
|
@@ -80508,7 +80710,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
80508
80710
|
};
|
|
80509
80711
|
walk2(liveSkillDir, staging);
|
|
80510
80712
|
if (existsSync77(dest)) {
|
|
80511
|
-
const prior =
|
|
80713
|
+
const prior = join76(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
80512
80714
|
renameSync17(dest, prior);
|
|
80513
80715
|
}
|
|
80514
80716
|
renameSync17(staging, dest);
|
|
@@ -80535,13 +80737,13 @@ function resolveAgent(opts) {
|
|
|
80535
80737
|
function resolveAgentsRoot(opts) {
|
|
80536
80738
|
if (opts.root)
|
|
80537
80739
|
return resolve46(opts.root);
|
|
80538
|
-
return
|
|
80740
|
+
return join76(homedir43(), ".switchroom", "agents");
|
|
80539
80741
|
}
|
|
80540
80742
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
80541
|
-
return
|
|
80743
|
+
return join76(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
80542
80744
|
}
|
|
80543
80745
|
function trashDir(agentsRoot, agent) {
|
|
80544
|
-
return
|
|
80746
|
+
return join76(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
80545
80747
|
}
|
|
80546
80748
|
function readStdinSync2() {
|
|
80547
80749
|
const chunks = [];
|
|
@@ -80571,7 +80773,7 @@ function loadFromDir2(dir) {
|
|
|
80571
80773
|
const files = {};
|
|
80572
80774
|
const walk2 = (sub) => {
|
|
80573
80775
|
for (const ent of readdirSync30(sub, { withFileTypes: true })) {
|
|
80574
|
-
const full =
|
|
80776
|
+
const full = join76(sub, ent.name);
|
|
80575
80777
|
if (ent.isSymbolicLink()) {
|
|
80576
80778
|
fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
|
|
80577
80779
|
}
|
|
@@ -80624,8 +80826,8 @@ function behavioralValidate(files) {
|
|
|
80624
80826
|
errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
80625
80827
|
}
|
|
80626
80828
|
} else if (PY_SCRIPT_RE.test(path8)) {
|
|
80627
|
-
const tmp = mkdtempSync6(
|
|
80628
|
-
const tmpPy =
|
|
80829
|
+
const tmp = mkdtempSync6(join76(tmpdir5(), "skill-personal-py-"));
|
|
80830
|
+
const tmpPy = join76(tmp, "check.py");
|
|
80629
80831
|
try {
|
|
80630
80832
|
writeFileSync38(tmpPy, content);
|
|
80631
80833
|
const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
|
|
@@ -80649,7 +80851,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
80649
80851
|
for (const ent of readdirSync30(trash, { withFileTypes: true })) {
|
|
80650
80852
|
if (!ent.isDirectory())
|
|
80651
80853
|
continue;
|
|
80652
|
-
const entPath =
|
|
80854
|
+
const entPath = join76(trash, ent.name);
|
|
80653
80855
|
try {
|
|
80654
80856
|
const st = statSync30(entPath);
|
|
80655
80857
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
@@ -80670,11 +80872,11 @@ function writePersonalSkill(targetDir, files) {
|
|
|
80670
80872
|
fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
80671
80873
|
}
|
|
80672
80874
|
mkdirSync43(dirname23(targetDir), { recursive: true, mode: 493 });
|
|
80673
|
-
const staging = mkdtempSync6(
|
|
80875
|
+
const staging = mkdtempSync6(join76(dirname23(targetDir), `.skill-personal-stage-`));
|
|
80674
80876
|
let oldRename = null;
|
|
80675
80877
|
try {
|
|
80676
80878
|
for (const [path8, content] of Object.entries(files)) {
|
|
80677
|
-
const full =
|
|
80879
|
+
const full = join76(staging, path8);
|
|
80678
80880
|
mkdirSync43(dirname23(full), { recursive: true, mode: 493 });
|
|
80679
80881
|
const fd = openSync16(full, "wx");
|
|
80680
80882
|
try {
|
|
@@ -80807,10 +81009,10 @@ function editPersonalAction(name, opts) {
|
|
|
80807
81009
|
}
|
|
80808
81010
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
80809
81011
|
function defaultSharedRoot() {
|
|
80810
|
-
return
|
|
81012
|
+
return join76(homedir43(), ".switchroom", "skills");
|
|
80811
81013
|
}
|
|
80812
81014
|
function defaultBundledRoot() {
|
|
80813
|
-
return
|
|
81015
|
+
return join76(homedir43(), ".switchroom", "skills", "_bundled");
|
|
80814
81016
|
}
|
|
80815
81017
|
function resolveCloneSource(source, opts) {
|
|
80816
81018
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -80820,7 +81022,7 @@ function resolveCloneSource(source, opts) {
|
|
|
80820
81022
|
const tier = m[1];
|
|
80821
81023
|
const slug = m[2];
|
|
80822
81024
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
80823
|
-
const dir =
|
|
81025
|
+
const dir = join76(root, slug);
|
|
80824
81026
|
if (!existsSync77(dir)) {
|
|
80825
81027
|
fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
80826
81028
|
}
|
|
@@ -80836,7 +81038,7 @@ function readSourceFiles(dir) {
|
|
|
80836
81038
|
const skipped = [];
|
|
80837
81039
|
const walk2 = (sub) => {
|
|
80838
81040
|
for (const ent of readdirSync30(sub, { withFileTypes: true })) {
|
|
80839
|
-
const full =
|
|
81041
|
+
const full = join76(sub, ent.name);
|
|
80840
81042
|
if (ent.isSymbolicLink()) {
|
|
80841
81043
|
continue;
|
|
80842
81044
|
}
|
|
@@ -80947,7 +81149,7 @@ function removePersonalAction(name, opts) {
|
|
|
80947
81149
|
const trashRoot = trashDir(agentsRoot, agent);
|
|
80948
81150
|
mkdirSync43(trashRoot, { recursive: true, mode: 493 });
|
|
80949
81151
|
const ts = Date.now();
|
|
80950
|
-
const trashTarget =
|
|
81152
|
+
const trashTarget = join76(trashRoot, `${name}-${ts}`);
|
|
80951
81153
|
renameSync17(target, trashTarget);
|
|
80952
81154
|
const now = new Date(ts);
|
|
80953
81155
|
utimesSync(trashTarget, now, now);
|
|
@@ -80966,7 +81168,7 @@ function listPersonalAction(opts) {
|
|
|
80966
81168
|
const agent = resolveAgent(opts);
|
|
80967
81169
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
80968
81170
|
sweepTrash(agentsRoot, agent);
|
|
80969
|
-
const skillsDir =
|
|
81171
|
+
const skillsDir = join76(agentsRoot, agent, ".claude", "skills");
|
|
80970
81172
|
const personal = [];
|
|
80971
81173
|
if (existsSync77(skillsDir)) {
|
|
80972
81174
|
for (const ent of readdirSync30(skillsDir, { withFileTypes: true })) {
|
|
@@ -80975,7 +81177,7 @@ function listPersonalAction(opts) {
|
|
|
80975
81177
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
80976
81178
|
continue;
|
|
80977
81179
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
80978
|
-
const skillPath =
|
|
81180
|
+
const skillPath = join76(skillsDir, ent.name);
|
|
80979
81181
|
let fileCount = 0;
|
|
80980
81182
|
let totalBytes = 0;
|
|
80981
81183
|
const walk2 = (sub) => {
|
|
@@ -80983,10 +81185,10 @@ function listPersonalAction(opts) {
|
|
|
80983
81185
|
if (e.isFile()) {
|
|
80984
81186
|
fileCount += 1;
|
|
80985
81187
|
try {
|
|
80986
|
-
totalBytes += statSync30(
|
|
81188
|
+
totalBytes += statSync30(join76(sub, e.name)).size;
|
|
80987
81189
|
} catch {}
|
|
80988
81190
|
} else if (e.isDirectory()) {
|
|
80989
|
-
walk2(
|
|
81191
|
+
walk2(join76(sub, e.name));
|
|
80990
81192
|
}
|
|
80991
81193
|
}
|
|
80992
81194
|
};
|
|
@@ -81026,22 +81228,22 @@ function registerSkillPersonalCommands(program3) {
|
|
|
81026
81228
|
init_helpers();
|
|
81027
81229
|
var import_yaml19 = __toESM(require_dist(), 1);
|
|
81028
81230
|
import { existsSync as existsSync78, readdirSync as readdirSync31, readFileSync as readFileSync64, statSync as statSync31 } from "node:fs";
|
|
81029
|
-
import { homedir as
|
|
81030
|
-
import { join as
|
|
81231
|
+
import { homedir as homedir44 } from "node:os";
|
|
81232
|
+
import { join as join77, resolve as resolve47 } from "node:path";
|
|
81031
81233
|
var PERSONAL_PREFIX2 = "personal-";
|
|
81032
81234
|
var BUNDLED_SUBDIR = "_bundled";
|
|
81033
81235
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
81034
81236
|
function defaultAgentsRoot() {
|
|
81035
|
-
return resolve47(
|
|
81237
|
+
return resolve47(homedir44(), ".switchroom/agents");
|
|
81036
81238
|
}
|
|
81037
81239
|
function defaultSharedRoot2() {
|
|
81038
|
-
return resolve47(
|
|
81240
|
+
return resolve47(homedir44(), ".switchroom/skills");
|
|
81039
81241
|
}
|
|
81040
81242
|
function defaultBundledRoot2() {
|
|
81041
|
-
return resolve47(
|
|
81243
|
+
return resolve47(homedir44(), ".switchroom/skills/_bundled");
|
|
81042
81244
|
}
|
|
81043
81245
|
function readSkillFrontmatter(skillDir) {
|
|
81044
|
-
const mdPath =
|
|
81246
|
+
const mdPath = join77(skillDir, "SKILL.md");
|
|
81045
81247
|
if (!existsSync78(mdPath))
|
|
81046
81248
|
return null;
|
|
81047
81249
|
let content;
|
|
@@ -81074,7 +81276,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
81074
81276
|
return { fm: parsed };
|
|
81075
81277
|
}
|
|
81076
81278
|
function statSkillMd(skillDir) {
|
|
81077
|
-
const mdPath =
|
|
81279
|
+
const mdPath = join77(skillDir, "SKILL.md");
|
|
81078
81280
|
try {
|
|
81079
81281
|
const st = statSync31(mdPath);
|
|
81080
81282
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
@@ -81085,7 +81287,7 @@ function statSkillMd(skillDir) {
|
|
|
81085
81287
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
81086
81288
|
if (!AGENT_NAME_RE3.test(agent))
|
|
81087
81289
|
return [];
|
|
81088
|
-
const skillsDir =
|
|
81290
|
+
const skillsDir = join77(agentsRoot, agent, ".claude/skills");
|
|
81089
81291
|
if (!existsSync78(skillsDir))
|
|
81090
81292
|
return [];
|
|
81091
81293
|
const out = [];
|
|
@@ -81098,7 +81300,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
81098
81300
|
for (const ent of entries) {
|
|
81099
81301
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
81100
81302
|
continue;
|
|
81101
|
-
const dirPath =
|
|
81303
|
+
const dirPath = join77(skillsDir, ent);
|
|
81102
81304
|
try {
|
|
81103
81305
|
if (!statSync31(dirPath).isDirectory())
|
|
81104
81306
|
continue;
|
|
@@ -81138,7 +81340,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
81138
81340
|
continue;
|
|
81139
81341
|
if (ent.startsWith("."))
|
|
81140
81342
|
continue;
|
|
81141
|
-
const dirPath =
|
|
81343
|
+
const dirPath = join77(sharedRoot, ent);
|
|
81142
81344
|
try {
|
|
81143
81345
|
if (!statSync31(dirPath).isDirectory())
|
|
81144
81346
|
continue;
|
|
@@ -81174,7 +81376,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
81174
81376
|
for (const ent of entries) {
|
|
81175
81377
|
if (ent.startsWith("."))
|
|
81176
81378
|
continue;
|
|
81177
|
-
const dirPath =
|
|
81379
|
+
const dirPath = join77(bundledRoot, ent);
|
|
81178
81380
|
try {
|
|
81179
81381
|
if (!statSync31(dirPath).isDirectory())
|
|
81180
81382
|
continue;
|
|
@@ -81319,8 +81521,8 @@ function registerHostdMcpCommand(program3) {
|
|
|
81319
81521
|
init_source();
|
|
81320
81522
|
init_helpers();
|
|
81321
81523
|
import { existsSync as existsSync80, mkdirSync as mkdirSync44, readdirSync as readdirSync32, readFileSync as readFileSync66, writeFileSync as writeFileSync39, statSync as statSync32, copyFileSync as copyFileSync12 } from "node:fs";
|
|
81322
|
-
import { homedir as
|
|
81323
|
-
import { join as
|
|
81524
|
+
import { homedir as homedir45 } from "node:os";
|
|
81525
|
+
import { join as join78 } from "node:path";
|
|
81324
81526
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
81325
81527
|
init_audit_reader();
|
|
81326
81528
|
var DEFAULT_IMAGE_TAG = "latest";
|
|
@@ -81411,10 +81613,10 @@ networks:
|
|
|
81411
81613
|
`;
|
|
81412
81614
|
}
|
|
81413
81615
|
function hostdDir() {
|
|
81414
|
-
return
|
|
81616
|
+
return join78(homedir45(), ".switchroom", "hostd");
|
|
81415
81617
|
}
|
|
81416
81618
|
function hostdComposePath() {
|
|
81417
|
-
return
|
|
81619
|
+
return join78(hostdDir(), "docker-compose.yml");
|
|
81418
81620
|
}
|
|
81419
81621
|
function backupExistingCompose() {
|
|
81420
81622
|
const p = hostdComposePath();
|
|
@@ -81453,7 +81655,7 @@ async function doInstall(opts, program3) {
|
|
|
81453
81655
|
const composePath = hostdComposePath();
|
|
81454
81656
|
mkdirSync44(dir, { recursive: true });
|
|
81455
81657
|
const yaml = renderHostdComposeFile({
|
|
81456
|
-
hostHome:
|
|
81658
|
+
hostHome: homedir45(),
|
|
81457
81659
|
imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
|
|
81458
81660
|
operatorUid: resolveOperatorUid()
|
|
81459
81661
|
});
|
|
@@ -81522,7 +81724,7 @@ function doStatus() {
|
|
|
81522
81724
|
for (const name of readdirSync32(dir)) {
|
|
81523
81725
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
81524
81726
|
continue;
|
|
81525
|
-
const sockPath =
|
|
81727
|
+
const sockPath = join78(dir, name, "sock");
|
|
81526
81728
|
if (existsSync80(sockPath)) {
|
|
81527
81729
|
const st = statSync32(sockPath);
|
|
81528
81730
|
if ((st.mode & 61440) === 49152) {
|