switchroom 0.19.8 → 0.19.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.19.
|
|
2123
|
+
var VERSION = "0.19.9", COMMIT_SHA = "9d791e63";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -23939,7 +23939,7 @@ function bindingsForAgent(agentName, mw, microsoftAccounts) {
|
|
|
23939
23939
|
// src/agents/reconcile-default-skills.ts
|
|
23940
23940
|
import { existsSync as existsSync8, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readlinkSync as readlinkSync3, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
23941
23941
|
import { homedir as homedir3 } from "node:os";
|
|
23942
|
-
import { join as join6, resolve as resolve5 } from "node:path";
|
|
23942
|
+
import { dirname as dirname2, isAbsolute, join as join6, relative, resolve as resolve5 } from "node:path";
|
|
23943
23943
|
function warnMissingPoolDir(poolDir) {
|
|
23944
23944
|
if (warnedMissingPool.has(poolDir))
|
|
23945
23945
|
return;
|
|
@@ -23947,6 +23947,14 @@ function warnMissingPoolDir(poolDir) {
|
|
|
23947
23947
|
process.stderr.write(`switchroom: bundled skills pool dir not found at ${poolDir} \u2014 run \`switchroom update\` to install it.
|
|
23948
23948
|
`);
|
|
23949
23949
|
}
|
|
23950
|
+
function warnMissingBuiltinDefault(poolDir, key) {
|
|
23951
|
+
const marker = `${poolDir} ${key}`;
|
|
23952
|
+
if (warnedMissingDefault.has(marker))
|
|
23953
|
+
return;
|
|
23954
|
+
warnedMissingDefault.add(marker);
|
|
23955
|
+
process.stderr.write(`switchroom: ERROR \u2014 builtin default skill "${key}" is missing from the bundled pool ` + `(${poolDir}). It ships in the CLI package but was not synced. ` + `Re-run \`switchroom update\` to repair the pool; if it persists this is a packaging bug.
|
|
23956
|
+
`);
|
|
23957
|
+
}
|
|
23950
23958
|
function getBundledSkillsPoolDir() {
|
|
23951
23959
|
return resolve5(homedir3(), ".switchroom/skills/_bundled");
|
|
23952
23960
|
}
|
|
@@ -23961,6 +23969,27 @@ function isOwnedStaleLink(target, poolDir) {
|
|
|
23961
23969
|
return true;
|
|
23962
23970
|
return false;
|
|
23963
23971
|
}
|
|
23972
|
+
function absoluteLinkTarget(linkPath, storedTarget) {
|
|
23973
|
+
return isAbsolute(storedTarget) ? storedTarget : resolve5(dirname2(linkPath), storedTarget);
|
|
23974
|
+
}
|
|
23975
|
+
function linkTargetFor(dest, src) {
|
|
23976
|
+
return relative(dirname2(dest), src);
|
|
23977
|
+
}
|
|
23978
|
+
function isOwnedBundledLink(dest, poolDir) {
|
|
23979
|
+
let stored = null;
|
|
23980
|
+
try {
|
|
23981
|
+
if (!lstatSync2(dest).isSymbolicLink())
|
|
23982
|
+
return false;
|
|
23983
|
+
stored = readlinkSync3(dest);
|
|
23984
|
+
} catch {
|
|
23985
|
+
return false;
|
|
23986
|
+
}
|
|
23987
|
+
if (!stored)
|
|
23988
|
+
return false;
|
|
23989
|
+
const resolved = isAbsolute(stored) ? stored : resolve5(dirname2(dest), stored);
|
|
23990
|
+
const poolPrefix = poolDir.endsWith("/") ? poolDir : poolDir + "/";
|
|
23991
|
+
return resolved === poolDir || resolved.startsWith(poolPrefix);
|
|
23992
|
+
}
|
|
23964
23993
|
function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuiltinDefaultSkillEntries(), poolDir = getBundledSkillsPoolDir()) {
|
|
23965
23994
|
const name = agentDir.split("/").pop() ?? agentDir;
|
|
23966
23995
|
const result = {
|
|
@@ -23969,6 +23998,8 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23969
23998
|
alreadyPresent: [],
|
|
23970
23999
|
optedOut: [],
|
|
23971
24000
|
conflicts: [],
|
|
24001
|
+
missingFromPool: [],
|
|
24002
|
+
pruned: [],
|
|
23972
24003
|
changed: false
|
|
23973
24004
|
};
|
|
23974
24005
|
const claudeDir = join6(agentDir, ".claude");
|
|
@@ -23982,15 +24013,32 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23982
24013
|
return result;
|
|
23983
24014
|
}
|
|
23984
24015
|
for (const entry of defaults) {
|
|
24016
|
+
const dest = join6(targetDir, entry.key);
|
|
23985
24017
|
if (optOuts[entry.optOutKey] === false) {
|
|
23986
24018
|
result.optedOut.push(entry.key);
|
|
24019
|
+
if (isOwnedBundledLink(dest, poolDir)) {
|
|
24020
|
+
try {
|
|
24021
|
+
rmSync2(dest, { force: true });
|
|
24022
|
+
result.pruned.push(entry.key);
|
|
24023
|
+
result.changed = true;
|
|
24024
|
+
} catch {}
|
|
24025
|
+
}
|
|
23987
24026
|
continue;
|
|
23988
24027
|
}
|
|
23989
24028
|
const src = join6(poolDir, entry.key);
|
|
23990
24029
|
if (!existsSync8(src)) {
|
|
24030
|
+
result.missingFromPool.push(entry.key);
|
|
24031
|
+
warnMissingBuiltinDefault(poolDir, entry.key);
|
|
24032
|
+
if (isOwnedBundledLink(dest, poolDir)) {
|
|
24033
|
+
try {
|
|
24034
|
+
rmSync2(dest, { force: true });
|
|
24035
|
+
result.pruned.push(entry.key);
|
|
24036
|
+
result.changed = true;
|
|
24037
|
+
} catch {}
|
|
24038
|
+
}
|
|
23991
24039
|
continue;
|
|
23992
24040
|
}
|
|
23993
|
-
const
|
|
24041
|
+
const relTarget = linkTargetFor(dest, src);
|
|
23994
24042
|
let existing;
|
|
23995
24043
|
try {
|
|
23996
24044
|
existing = lstatSync2(dest);
|
|
@@ -24003,11 +24051,12 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24003
24051
|
try {
|
|
24004
24052
|
currentTarget = readlinkSync3(dest);
|
|
24005
24053
|
} catch {}
|
|
24006
|
-
if (currentTarget ===
|
|
24054
|
+
if (currentTarget === relTarget) {
|
|
24007
24055
|
result.alreadyPresent.push(entry.key);
|
|
24008
24056
|
continue;
|
|
24009
24057
|
}
|
|
24010
|
-
|
|
24058
|
+
const resolvedTarget = currentTarget ? absoluteLinkTarget(dest, currentTarget) : null;
|
|
24059
|
+
if (resolvedTarget && isOwnedStaleLink(resolvedTarget, poolDir)) {
|
|
24011
24060
|
try {
|
|
24012
24061
|
rmSync2(dest, { force: true });
|
|
24013
24062
|
} catch {}
|
|
@@ -24021,7 +24070,7 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24021
24070
|
}
|
|
24022
24071
|
}
|
|
24023
24072
|
try {
|
|
24024
|
-
symlinkSync(
|
|
24073
|
+
symlinkSync(relTarget, dest);
|
|
24025
24074
|
result.added.push(entry.key);
|
|
24026
24075
|
result.changed = true;
|
|
24027
24076
|
} catch (err) {
|
|
@@ -24030,10 +24079,11 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24030
24079
|
}
|
|
24031
24080
|
return result;
|
|
24032
24081
|
}
|
|
24033
|
-
var warnedMissingPool;
|
|
24082
|
+
var warnedMissingPool, warnedMissingDefault;
|
|
24034
24083
|
var init_reconcile_default_skills = __esm(() => {
|
|
24035
24084
|
init_scaffold_integration();
|
|
24036
24085
|
warnedMissingPool = new Set;
|
|
24086
|
+
warnedMissingDefault = new Set;
|
|
24037
24087
|
});
|
|
24038
24088
|
|
|
24039
24089
|
// src/agents/sub-agent-telegram-prompt.ts
|
|
@@ -24089,7 +24139,7 @@ import {
|
|
|
24089
24139
|
copyFileSync as copyFileSync2,
|
|
24090
24140
|
unlinkSync
|
|
24091
24141
|
} from "node:fs";
|
|
24092
|
-
import { basename as basename2, dirname as
|
|
24142
|
+
import { basename as basename2, dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
24093
24143
|
function defaultStatePath() {
|
|
24094
24144
|
return resolveStatePath("topics.json");
|
|
24095
24145
|
}
|
|
@@ -24116,7 +24166,7 @@ function loadTopicState(statePath) {
|
|
|
24116
24166
|
}
|
|
24117
24167
|
function saveTopicState(state, statePath) {
|
|
24118
24168
|
const path = statePath ?? defaultStatePath();
|
|
24119
|
-
const dir =
|
|
24169
|
+
const dir = dirname3(path);
|
|
24120
24170
|
if (!existsSync9(dir)) {
|
|
24121
24171
|
mkdirSync4(dir, { recursive: true });
|
|
24122
24172
|
}
|
|
@@ -24388,7 +24438,7 @@ import {
|
|
|
24388
24438
|
lstatSync as lstatSync3,
|
|
24389
24439
|
realpathSync as realpathSync2
|
|
24390
24440
|
} from "node:fs";
|
|
24391
|
-
import { dirname as
|
|
24441
|
+
import { dirname as dirname4, basename as basename3, resolve as resolve7 } from "node:path";
|
|
24392
24442
|
function atomicWriteFileSync2(path, data, mode) {
|
|
24393
24443
|
let effectivePath = path;
|
|
24394
24444
|
try {
|
|
@@ -24396,7 +24446,7 @@ function atomicWriteFileSync2(path, data, mode) {
|
|
|
24396
24446
|
effectivePath = realpathSync2(path);
|
|
24397
24447
|
}
|
|
24398
24448
|
} catch {}
|
|
24399
|
-
const dir =
|
|
24449
|
+
const dir = dirname4(resolve7(effectivePath));
|
|
24400
24450
|
const tmp = resolve7(dir, `.${basename3(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
24401
24451
|
try {
|
|
24402
24452
|
const fd = openSync4(tmp, "wx", mode);
|
|
@@ -24533,7 +24583,7 @@ function createVault(passphrase, vaultPath) {
|
|
|
24533
24583
|
if (existsSync11(vaultPath)) {
|
|
24534
24584
|
throw new VaultError(`Vault file already exists: ${vaultPath}`);
|
|
24535
24585
|
}
|
|
24536
|
-
const dir =
|
|
24586
|
+
const dir = dirname4(vaultPath);
|
|
24537
24587
|
if (!existsSync11(dir)) {
|
|
24538
24588
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
24539
24589
|
}
|
|
@@ -26141,7 +26191,7 @@ import {
|
|
|
26141
26191
|
} from "node:fs";
|
|
26142
26192
|
import { homedir as homedir5 } from "node:os";
|
|
26143
26193
|
import { execSync, execFileSync as execFileSync6 } from "node:child_process";
|
|
26144
|
-
import { join as join11, resolve as resolve11 } from "node:path";
|
|
26194
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join11, relative as relative2, resolve as resolve11 } from "node:path";
|
|
26145
26195
|
import { createHash as createHash3 } from "node:crypto";
|
|
26146
26196
|
function prependReplyDiscipline(rendered, context) {
|
|
26147
26197
|
const replyDiscipline = renderReplyDisciplineFragment(context);
|
|
@@ -26527,7 +26577,8 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
|
|
|
26527
26577
|
} catch {
|
|
26528
26578
|
continue;
|
|
26529
26579
|
}
|
|
26530
|
-
|
|
26580
|
+
const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(entryPath), target) : null;
|
|
26581
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26531
26582
|
try {
|
|
26532
26583
|
rmSync4(entryPath, { force: true });
|
|
26533
26584
|
} catch {}
|
|
@@ -26550,6 +26601,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26550
26601
|
console.warn(` WARNING: skill "${name}" not found in pool (${skillsPool}) \u2014 skipping`);
|
|
26551
26602
|
continue;
|
|
26552
26603
|
}
|
|
26604
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26553
26605
|
let linkStat;
|
|
26554
26606
|
try {
|
|
26555
26607
|
linkStat = lstatSync4(dest);
|
|
@@ -26562,7 +26614,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26562
26614
|
try {
|
|
26563
26615
|
target = readlinkSync4(dest);
|
|
26564
26616
|
} catch {}
|
|
26565
|
-
if (target
|
|
26617
|
+
if (target === relTarget) {
|
|
26618
|
+
continue;
|
|
26619
|
+
}
|
|
26620
|
+
const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(dest), target) : null;
|
|
26621
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26566
26622
|
try {
|
|
26567
26623
|
rmSync4(dest, { force: true });
|
|
26568
26624
|
} catch {}
|
|
@@ -26574,7 +26630,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26574
26630
|
}
|
|
26575
26631
|
}
|
|
26576
26632
|
try {
|
|
26577
|
-
symlinkSync2(
|
|
26633
|
+
symlinkSync2(relTarget, dest);
|
|
26578
26634
|
} catch (err) {
|
|
26579
26635
|
console.warn(` WARNING: failed to symlink skill "${name}": ${err.message}`);
|
|
26580
26636
|
}
|
|
@@ -26590,10 +26646,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26590
26646
|
} catch {
|
|
26591
26647
|
continue;
|
|
26592
26648
|
}
|
|
26593
|
-
|
|
26649
|
+
const resolved = linkTarget ? isAbsolute2(linkTarget) ? linkTarget : resolve11(dirname5(entryPath), linkTarget) : null;
|
|
26650
|
+
if (resolved && resolved.includes("/.switchroom/skills/_bundled/")) {
|
|
26594
26651
|
continue;
|
|
26595
26652
|
}
|
|
26596
|
-
if (
|
|
26653
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26597
26654
|
rmSync4(entryPath, { force: true });
|
|
26598
26655
|
}
|
|
26599
26656
|
}
|
|
@@ -26646,7 +26703,10 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26646
26703
|
try {
|
|
26647
26704
|
currentTarget = readlinkSync4(dest);
|
|
26648
26705
|
} catch {}
|
|
26649
|
-
if (currentTarget
|
|
26706
|
+
if (!currentTarget)
|
|
26707
|
+
continue;
|
|
26708
|
+
const resolvedTarget = isAbsolute2(currentTarget) ? currentTarget : resolve11(dirname5(dest), currentTarget);
|
|
26709
|
+
if (resolvedTarget !== join11(builtinSkillsDir, name))
|
|
26650
26710
|
continue;
|
|
26651
26711
|
try {
|
|
26652
26712
|
rmSync4(dest, { force: true });
|
|
@@ -26657,6 +26717,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26657
26717
|
for (const name of switchroomSkillNames) {
|
|
26658
26718
|
const src = join11(builtinSkillsDir, name);
|
|
26659
26719
|
const dest = join11(targetDir, name);
|
|
26720
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26660
26721
|
let existing;
|
|
26661
26722
|
try {
|
|
26662
26723
|
existing = lstatSync4(dest);
|
|
@@ -26669,7 +26730,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26669
26730
|
try {
|
|
26670
26731
|
currentTarget = readlinkSync4(dest);
|
|
26671
26732
|
} catch {}
|
|
26672
|
-
if (currentTarget ===
|
|
26733
|
+
if (currentTarget === relTarget)
|
|
26673
26734
|
continue;
|
|
26674
26735
|
try {
|
|
26675
26736
|
rmSync4(dest, { force: true });
|
|
@@ -26679,7 +26740,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26679
26740
|
}
|
|
26680
26741
|
}
|
|
26681
26742
|
try {
|
|
26682
|
-
symlinkSync2(
|
|
26743
|
+
symlinkSync2(relTarget, dest);
|
|
26683
26744
|
} catch (err) {
|
|
26684
26745
|
console.warn(` WARNING: failed to symlink switchroom skill "${name}": ${err.message}`);
|
|
26685
26746
|
}
|
|
@@ -27381,6 +27442,24 @@ function resolveWebkiteMcpEntry(_agentName, agentConfig, _switchroomConfig) {
|
|
|
27381
27442
|
}
|
|
27382
27443
|
};
|
|
27383
27444
|
}
|
|
27445
|
+
function computeDesiredPermissionAllow(agentConfig, hindsightEnabled) {
|
|
27446
|
+
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
27447
|
+
const rawAllow = tools.allow ?? [];
|
|
27448
|
+
const hasAllWildcard = rawAllow.includes("all");
|
|
27449
|
+
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
27450
|
+
const dangerousMode = agentConfig.dangerous_mode === true;
|
|
27451
|
+
const hadExplicitAllow = rawAllow.length > 0;
|
|
27452
|
+
const readOnlyDefaults = !dangerousMode && !hadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
27453
|
+
return dedupe2([
|
|
27454
|
+
...baseAllow,
|
|
27455
|
+
...readOnlyDefaults,
|
|
27456
|
+
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
27457
|
+
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
27458
|
+
...AGENT_CONFIG_MCP_TOOLS,
|
|
27459
|
+
...HOSTD_MCP_TOOLS,
|
|
27460
|
+
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
27461
|
+
]);
|
|
27462
|
+
}
|
|
27384
27463
|
function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchroomConfig, userIdOverride, switchroomConfigPath) {
|
|
27385
27464
|
const agentConfig = resolveAgentConfig(switchroomConfig?.defaults, switchroomConfig?.profiles, agentConfigRaw);
|
|
27386
27465
|
const agentDir = resolve11(agentsDir, name);
|
|
@@ -27403,20 +27482,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
27403
27482
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
27404
27483
|
const rawAllow = tools.allow ?? [];
|
|
27405
27484
|
const hasAllWildcard = rawAllow.includes("all");
|
|
27406
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
27407
|
-
const dangerousMode = agentConfig.dangerous_mode === true;
|
|
27408
|
-
const hadExplicitAllow = rawAllow.length > 0;
|
|
27409
|
-
const readOnlyDefaults = !dangerousMode && !hadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
27410
27485
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
27411
|
-
const permissionAllow =
|
|
27412
|
-
...baseAllow,
|
|
27413
|
-
...readOnlyDefaults,
|
|
27414
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
27415
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
27416
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
27417
|
-
...HOSTD_MCP_TOOLS,
|
|
27418
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
27419
|
-
]);
|
|
27486
|
+
const permissionAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
27420
27487
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
27421
27488
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
27422
27489
|
const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
@@ -28332,23 +28399,11 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
28332
28399
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
28333
28400
|
const rawAllow = tools.allow ?? [];
|
|
28334
28401
|
const hasAllWildcard = rawAllow.includes("all");
|
|
28335
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
28336
|
-
const reconcileDangerousMode = agentConfig.dangerous_mode === true;
|
|
28337
|
-
const reconcileHadExplicitAllow = rawAllow.length > 0;
|
|
28338
|
-
const reconcileReadOnlyDefaults = !reconcileDangerousMode && !reconcileHadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
28339
28402
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
28403
|
+
const desiredAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
28340
28404
|
if (Array.isArray(tools.allow)) {
|
|
28341
28405
|
tools.allow = tools.allow.filter((p) => !LEGACY_SWITCHROOM_MCP_TOKENS.includes(p) && !LEGACY_HOSTD_BLANKET_TOKENS.includes(p));
|
|
28342
28406
|
}
|
|
28343
|
-
const desiredAllow = dedupe2([
|
|
28344
|
-
...baseAllow,
|
|
28345
|
-
...reconcileReadOnlyDefaults,
|
|
28346
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
28347
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
28348
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
28349
|
-
...HOSTD_MCP_TOOLS,
|
|
28350
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
28351
|
-
]);
|
|
28352
28407
|
const desiredDeny = dedupe2([
|
|
28353
28408
|
...tools.deny ?? [],
|
|
28354
28409
|
...webkiteDenyForAgent(agentConfig),
|
|
@@ -29606,7 +29661,7 @@ var init_scaffold = __esm(() => {
|
|
|
29606
29661
|
|
|
29607
29662
|
// src/setup/host-capabilities.ts
|
|
29608
29663
|
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
|
|
29609
|
-
import { dirname as
|
|
29664
|
+
import { dirname as dirname6 } from "node:path";
|
|
29610
29665
|
function hostCapabilitiesPath() {
|
|
29611
29666
|
return resolveStatePath("host-capabilities.json");
|
|
29612
29667
|
}
|
|
@@ -29621,7 +29676,7 @@ function saveVoiceCapability(caps, now = () => new Date) {
|
|
|
29621
29676
|
}
|
|
29622
29677
|
};
|
|
29623
29678
|
const path = hostCapabilitiesPath();
|
|
29624
|
-
mkdirSync11(
|
|
29679
|
+
mkdirSync11(dirname6(path), { recursive: true });
|
|
29625
29680
|
writeFileSync5(path, JSON.stringify(doc, null, 2) + `
|
|
29626
29681
|
`, {
|
|
29627
29682
|
encoding: "utf-8",
|
|
@@ -29728,11 +29783,11 @@ var init_grants_db_path = __esm(() => {
|
|
|
29728
29783
|
|
|
29729
29784
|
// src/agents/compose.ts
|
|
29730
29785
|
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync15, lstatSync as lstatSync5, readlinkSync as readlinkSync5, chmodSync as chmodSync4 } from "node:fs";
|
|
29731
|
-
import { join as join13, isAbsolute, dirname as
|
|
29786
|
+
import { join as join13, isAbsolute as isAbsolute3, dirname as dirname8, resolve as resolve13 } from "node:path";
|
|
29732
29787
|
function assertPlausibleHostHome(homePrefix) {
|
|
29733
29788
|
if (homePrefix === "${HOME}")
|
|
29734
29789
|
return;
|
|
29735
|
-
const bad = !
|
|
29790
|
+
const bad = !isAbsolute3(homePrefix) || CONTAINER_ROOT_PREFIXES.some((p) => homePrefix === p || homePrefix.startsWith(p + "/"));
|
|
29736
29791
|
if (!bad)
|
|
29737
29792
|
return;
|
|
29738
29793
|
throw new Error(`compose: refusing to generate \u2014 the host-home prefix resolved to "${homePrefix}", ` + `which is not a real host path (it looks like an in-container root). Emitting it as a ` + `bind-mount source would make docker auto-create empty dirs on the host and crash the ` + `fleet (start.sh missing \u2192 exec 127; broker EISDIR / SQLite "unable to open").
|
|
@@ -29976,8 +30031,8 @@ function conditionalMountPresent(probePath, hostHome, probeHome) {
|
|
|
29976
30031
|
if (!lstatSync5(probePath).isSymbolicLink())
|
|
29977
30032
|
return false;
|
|
29978
30033
|
let target = readlinkSync5(probePath);
|
|
29979
|
-
if (!
|
|
29980
|
-
target = resolve13(
|
|
30034
|
+
if (!isAbsolute3(target))
|
|
30035
|
+
target = resolve13(dirname8(probePath), target);
|
|
29981
30036
|
if (hostHome && probeHome && hostHome !== probeHome) {
|
|
29982
30037
|
if (target.startsWith(hostHome + "/")) {
|
|
29983
30038
|
return true;
|
|
@@ -30026,7 +30081,7 @@ function generateCompose(opts) {
|
|
|
30026
30081
|
const lines = [];
|
|
30027
30082
|
lines.push("# generated by switchroom \u2014 do not edit by hand.");
|
|
30028
30083
|
lines.push("# Manual edits will be overwritten on the next `switchroom agent add`");
|
|
30029
|
-
lines.push("#
|
|
30084
|
+
lines.push("# or `switchroom apply`. To customise an agent, edit");
|
|
30030
30085
|
lines.push("# switchroom.yaml and re-run the regenerating command.");
|
|
30031
30086
|
lines.push("");
|
|
30032
30087
|
lines.push(`# image tag: ${imageTag}`);
|
|
@@ -30698,7 +30753,7 @@ var init_operator_uid = () => {};
|
|
|
30698
30753
|
import { chownSync as chownSync2 } from "node:fs";
|
|
30699
30754
|
import { mkdir, readFile, writeFile, rename, copyFile } from "node:fs/promises";
|
|
30700
30755
|
import { homedir as homedir7 } from "node:os";
|
|
30701
|
-
import { basename as basename4, dirname as
|
|
30756
|
+
import { basename as basename4, dirname as dirname9, join as join15 } from "node:path";
|
|
30702
30757
|
function agentHadLiteLLMRouting(composeContent, agentName) {
|
|
30703
30758
|
const lines = composeContent.split(`
|
|
30704
30759
|
`);
|
|
@@ -30839,7 +30894,7 @@ async function computeComposeContent(opts) {
|
|
|
30839
30894
|
async function writeComposeFile(opts) {
|
|
30840
30895
|
const { content, imageTag, previous, previousImageTag } = await computeComposeContent(opts);
|
|
30841
30896
|
const operatorUid = resolveOperatorUid();
|
|
30842
|
-
await mkdir(
|
|
30897
|
+
await mkdir(dirname9(opts.composePath), { recursive: true });
|
|
30843
30898
|
if (previous !== null) {
|
|
30844
30899
|
try {
|
|
30845
30900
|
await copyFile(opts.composePath, opts.composePath + ".bak");
|
|
@@ -30908,9 +30963,9 @@ var init_tmux = __esm(() => {
|
|
|
30908
30963
|
|
|
30909
30964
|
// src/agents/compose-env.ts
|
|
30910
30965
|
import { existsSync as existsSync21 } from "node:fs";
|
|
30911
|
-
import { dirname as
|
|
30966
|
+
import { dirname as dirname10 } from "node:path";
|
|
30912
30967
|
function composeEnvPath(composePath) {
|
|
30913
|
-
return
|
|
30968
|
+
return dirname10(composePath) + "/.env";
|
|
30914
30969
|
}
|
|
30915
30970
|
function composeEnvFileArgs(composePath) {
|
|
30916
30971
|
const envPath = composeEnvPath(composePath);
|
|
@@ -35578,7 +35633,7 @@ import {
|
|
|
35578
35633
|
unlinkSync as unlinkSync7
|
|
35579
35634
|
} from "node:fs";
|
|
35580
35635
|
import { createHash as createHash6 } from "node:crypto";
|
|
35581
|
-
import { basename as basename5, dirname as
|
|
35636
|
+
import { basename as basename5, dirname as dirname11, join as join31 } from "node:path";
|
|
35582
35637
|
function vaultLayoutPaths(home2) {
|
|
35583
35638
|
const switchroomRoot = join31(home2, ".switchroom");
|
|
35584
35639
|
return {
|
|
@@ -35731,7 +35786,7 @@ function sha256File(path2) {
|
|
|
35731
35786
|
return createHash6("sha256").update(data).digest("hex");
|
|
35732
35787
|
}
|
|
35733
35788
|
function atomicReplaceWithSymlink(target, linkTarget) {
|
|
35734
|
-
const tmp = join31(
|
|
35789
|
+
const tmp = join31(dirname11(target), `.${basename5(target)}.symlink-tmp`);
|
|
35735
35790
|
if (existsSync39(tmp)) {
|
|
35736
35791
|
try {
|
|
35737
35792
|
unlinkSync7(tmp);
|
|
@@ -35785,7 +35840,7 @@ import {
|
|
|
35785
35840
|
unlinkSync as unlinkSync8,
|
|
35786
35841
|
writeSync as writeSync5
|
|
35787
35842
|
} from "node:fs";
|
|
35788
|
-
import { basename as basename6, dirname as
|
|
35843
|
+
import { basename as basename6, dirname as dirname12, resolve as resolve27 } from "node:path";
|
|
35789
35844
|
function readMachineId() {
|
|
35790
35845
|
const vitestVal = process.env.VITEST;
|
|
35791
35846
|
const isTestEnv = vitestVal !== undefined && vitestVal.length > 0;
|
|
@@ -35850,7 +35905,7 @@ function decryptAutoUnlock(blob, machineId) {
|
|
|
35850
35905
|
}
|
|
35851
35906
|
function writeAutoUnlockFile(passphrase, filePath) {
|
|
35852
35907
|
const blob = encryptAutoUnlock(passphrase);
|
|
35853
|
-
const dir =
|
|
35908
|
+
const dir = dirname12(filePath);
|
|
35854
35909
|
mkdirSync23(dir, { recursive: true, mode: 448 });
|
|
35855
35910
|
const tmp = resolve27(dir, `.${basename6(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
35856
35911
|
try {
|
|
@@ -36957,7 +37012,7 @@ function formatForCli(entries, opts = {}) {
|
|
|
36957
37012
|
var init_audit_reader = () => {};
|
|
36958
37013
|
|
|
36959
37014
|
// node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
36960
|
-
import { dirname as
|
|
37015
|
+
import { dirname as dirname16, posix, sep as sep2 } from "path";
|
|
36961
37016
|
function createModulerModifier() {
|
|
36962
37017
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
36963
37018
|
return async (frames) => {
|
|
@@ -36966,7 +37021,7 @@ function createModulerModifier() {
|
|
|
36966
37021
|
return frames;
|
|
36967
37022
|
};
|
|
36968
37023
|
}
|
|
36969
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
37024
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname16(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
36970
37025
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
36971
37026
|
return (filename) => {
|
|
36972
37027
|
if (!filename)
|
|
@@ -41582,7 +41637,7 @@ import {
|
|
|
41582
41637
|
readFileSync as readFileSync45,
|
|
41583
41638
|
writeFileSync as writeFileSync15
|
|
41584
41639
|
} from "node:fs";
|
|
41585
|
-
import { dirname as
|
|
41640
|
+
import { dirname as dirname17 } from "node:path";
|
|
41586
41641
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
41587
41642
|
function telemetryDisabled() {
|
|
41588
41643
|
const v = process.env.SWITCHROOM_TELEMETRY_DISABLED;
|
|
@@ -41604,7 +41659,7 @@ function getDistinctId() {
|
|
|
41604
41659
|
const id = randomUUID3();
|
|
41605
41660
|
cachedDistinctId = id;
|
|
41606
41661
|
try {
|
|
41607
|
-
mkdirSync27(
|
|
41662
|
+
mkdirSync27(dirname17(path5), { recursive: true });
|
|
41608
41663
|
writeFileSync15(path5, id, "utf-8");
|
|
41609
41664
|
} catch {}
|
|
41610
41665
|
return id;
|
|
@@ -41851,7 +41906,7 @@ import {
|
|
|
41851
41906
|
readFileSync as readFileSync55,
|
|
41852
41907
|
readdirSync as readdirSync21
|
|
41853
41908
|
} from "node:fs";
|
|
41854
|
-
import { dirname as
|
|
41909
|
+
import { dirname as dirname21, join as join53 } from "node:path";
|
|
41855
41910
|
import { execSync as execSync2 } from "node:child_process";
|
|
41856
41911
|
function locateManifestPath() {
|
|
41857
41912
|
let dir = import.meta.dirname;
|
|
@@ -41859,7 +41914,7 @@ function locateManifestPath() {
|
|
|
41859
41914
|
const candidate = join53(dir, "dependencies.json");
|
|
41860
41915
|
if (existsSync60(candidate))
|
|
41861
41916
|
return candidate;
|
|
41862
|
-
dir =
|
|
41917
|
+
dir = dirname21(dir);
|
|
41863
41918
|
}
|
|
41864
41919
|
return null;
|
|
41865
41920
|
}
|
|
@@ -42713,7 +42768,7 @@ function checkAgentSocketMounts(composeYaml) {
|
|
|
42713
42768
|
name: "agent socket-volume isolation",
|
|
42714
42769
|
status: "fail",
|
|
42715
42770
|
detail: `Cross-mounted socket volumes: ${violations.join("; ")}`,
|
|
42716
|
-
fix: "Re-run `switchroom
|
|
42771
|
+
fix: "Re-run `switchroom apply` to regenerate the compose from cascade. Hand-edits violating per-agent socket isolation are the load-bearing security invariant."
|
|
42717
42772
|
};
|
|
42718
42773
|
}
|
|
42719
42774
|
function checkAgentCaps(config) {
|
|
@@ -42911,7 +42966,7 @@ function runDockerChecks(args) {
|
|
|
42911
42966
|
name: "compose file present",
|
|
42912
42967
|
status: "warn",
|
|
42913
42968
|
detail: "Docker mode active but no docker-compose.yml found at ~/.switchroom/compose/docker-compose.yml",
|
|
42914
|
-
fix: "Run `switchroom
|
|
42969
|
+
fix: "Run `switchroom apply` to generate it."
|
|
42915
42970
|
});
|
|
42916
42971
|
}
|
|
42917
42972
|
out.push(...checkContainerRuntimeHealth(args.config, args.dockerPsDeps));
|
|
@@ -45933,7 +45988,7 @@ import {
|
|
|
45933
45988
|
readdirSync as readdirSync23,
|
|
45934
45989
|
statSync as statSync38
|
|
45935
45990
|
} from "node:fs";
|
|
45936
|
-
import { dirname as
|
|
45991
|
+
import { dirname as dirname22, join as join68, resolve as resolve39 } from "node:path";
|
|
45937
45992
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
45938
45993
|
function findInNvm(bin) {
|
|
45939
45994
|
const nvmRoot = join68(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -47530,7 +47585,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
47530
47585
|
detail: "skipped (MFF_API_URL not set)"
|
|
47531
47586
|
};
|
|
47532
47587
|
}
|
|
47533
|
-
const credDir =
|
|
47588
|
+
const credDir = dirname22(envPath);
|
|
47534
47589
|
const authScript = join68(credDir, "claude-auth.py");
|
|
47535
47590
|
if (!existsSync66(authScript)) {
|
|
47536
47591
|
return {
|
|
@@ -48590,8 +48645,8 @@ var init_fleet_defaults = __esm(() => {
|
|
|
48590
48645
|
});
|
|
48591
48646
|
|
|
48592
48647
|
// src/agents/connection-health.ts
|
|
48593
|
-
import { mkdirSync as
|
|
48594
|
-
import { join as
|
|
48648
|
+
import { mkdirSync as mkdirSync47, writeFileSync as writeFileSync29 } from "node:fs";
|
|
48649
|
+
import { join as join85 } from "node:path";
|
|
48595
48650
|
async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
48596
48651
|
const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
|
|
48597
48652
|
if (reqs.length === 0)
|
|
@@ -48648,10 +48703,10 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
|
48648
48703
|
return issues;
|
|
48649
48704
|
}
|
|
48650
48705
|
function writeConnectionHealthFile(agentDir, health, deps) {
|
|
48651
|
-
const dir =
|
|
48652
|
-
const path8 =
|
|
48653
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
48654
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
48706
|
+
const dir = join85(agentDir, ".claude");
|
|
48707
|
+
const path8 = join85(dir, CONNECTION_HEALTH_FILENAME);
|
|
48708
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync47(p, o)))(dir, { recursive: true });
|
|
48709
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync29(p, d)))(path8, JSON.stringify(health, null, 2) + `
|
|
48655
48710
|
`);
|
|
48656
48711
|
}
|
|
48657
48712
|
async function refreshAgentConnectionHealth(config, agentName, agentDir, deps) {
|
|
@@ -48672,10 +48727,10 @@ var CONNECTION_HEALTH_FILENAME = "connection-health.json";
|
|
|
48672
48727
|
var init_connection_health = () => {};
|
|
48673
48728
|
|
|
48674
48729
|
// src/cli/update-prompt-hook.ts
|
|
48675
|
-
import { existsSync as
|
|
48676
|
-
import { join as
|
|
48730
|
+
import { existsSync as existsSync84, readFileSync as readFileSync73, writeFileSync as writeFileSync30, chmodSync as chmodSync12, mkdirSync as mkdirSync48 } from "node:fs";
|
|
48731
|
+
import { join as join86 } from "node:path";
|
|
48677
48732
|
function containerHookCommand() {
|
|
48678
|
-
return
|
|
48733
|
+
return join86(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
|
|
48679
48734
|
}
|
|
48680
48735
|
function updatePromptHookScript() {
|
|
48681
48736
|
return `#!/bin/bash
|
|
@@ -48741,14 +48796,14 @@ exit 0
|
|
|
48741
48796
|
`;
|
|
48742
48797
|
}
|
|
48743
48798
|
function installUpdatePromptHook(agentDir) {
|
|
48744
|
-
const hooksDir =
|
|
48745
|
-
|
|
48746
|
-
const scriptPath =
|
|
48799
|
+
const hooksDir = join86(agentDir, ".claude", "hooks");
|
|
48800
|
+
mkdirSync48(hooksDir, { recursive: true });
|
|
48801
|
+
const scriptPath = join86(hooksDir, HOOK_FILENAME);
|
|
48747
48802
|
const desired = updatePromptHookScript();
|
|
48748
48803
|
let installed = false;
|
|
48749
|
-
const existing =
|
|
48804
|
+
const existing = existsSync84(scriptPath) ? readFileSync73(scriptPath, "utf-8") : "";
|
|
48750
48805
|
if (existing !== desired) {
|
|
48751
|
-
|
|
48806
|
+
writeFileSync30(scriptPath, desired, { mode: 493 });
|
|
48752
48807
|
chmodSync12(scriptPath, 493);
|
|
48753
48808
|
installed = true;
|
|
48754
48809
|
} else {
|
|
@@ -48756,11 +48811,11 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48756
48811
|
chmodSync12(scriptPath, 493);
|
|
48757
48812
|
} catch {}
|
|
48758
48813
|
}
|
|
48759
|
-
const settingsPath =
|
|
48760
|
-
if (!
|
|
48814
|
+
const settingsPath = join86(agentDir, ".claude", "settings.json");
|
|
48815
|
+
if (!existsSync84(settingsPath)) {
|
|
48761
48816
|
return { scriptPath, settingsPath, installed };
|
|
48762
48817
|
}
|
|
48763
|
-
const raw =
|
|
48818
|
+
const raw = readFileSync73(settingsPath, "utf-8");
|
|
48764
48819
|
let parsed;
|
|
48765
48820
|
try {
|
|
48766
48821
|
parsed = JSON.parse(raw);
|
|
@@ -48796,7 +48851,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48796
48851
|
if (mutated) {
|
|
48797
48852
|
hooks.UserPromptSubmit = list2;
|
|
48798
48853
|
parsed.hooks = hooks;
|
|
48799
|
-
|
|
48854
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48800
48855
|
`, { mode: 384 });
|
|
48801
48856
|
installed = true;
|
|
48802
48857
|
} else if (!alreadyCorrect) {
|
|
@@ -48805,7 +48860,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48805
48860
|
});
|
|
48806
48861
|
hooks.UserPromptSubmit = list2;
|
|
48807
48862
|
parsed.hooks = hooks;
|
|
48808
|
-
|
|
48863
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48809
48864
|
`, { mode: 384 });
|
|
48810
48865
|
installed = true;
|
|
48811
48866
|
}
|
|
@@ -49189,8 +49244,8 @@ __export(exports_voice_sidecar_token, {
|
|
|
49189
49244
|
VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
|
|
49190
49245
|
});
|
|
49191
49246
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
49192
|
-
import { chmodSync as chmodSync13, chownSync as chownSync7, existsSync as
|
|
49193
|
-
import { dirname as
|
|
49247
|
+
import { chmodSync as chmodSync13, chownSync as chownSync7, existsSync as existsSync86, mkdirSync as mkdirSync49, readFileSync as readFileSync74, rmSync as rmSync17, writeFileSync as writeFileSync31 } from "node:fs";
|
|
49248
|
+
import { dirname as dirname31 } from "node:path";
|
|
49194
49249
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
49195
49250
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
49196
49251
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
@@ -49224,8 +49279,8 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49224
49279
|
const envPath = composeEnvPath(composePath);
|
|
49225
49280
|
if (engine !== "local") {
|
|
49226
49281
|
try {
|
|
49227
|
-
if (
|
|
49228
|
-
const body =
|
|
49282
|
+
if (existsSync86(envPath)) {
|
|
49283
|
+
const body = readFileSync74(envPath, "utf-8");
|
|
49229
49284
|
if (body.includes(`${VOICE_SIDECAR_TOKEN_ENV}=`))
|
|
49230
49285
|
rmSync17(envPath);
|
|
49231
49286
|
}
|
|
@@ -49244,11 +49299,11 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49244
49299
|
if (!token)
|
|
49245
49300
|
return;
|
|
49246
49301
|
try {
|
|
49247
|
-
|
|
49302
|
+
mkdirSync49(dirname31(envPath), { recursive: true });
|
|
49248
49303
|
let body = "";
|
|
49249
49304
|
try {
|
|
49250
|
-
if (
|
|
49251
|
-
body =
|
|
49305
|
+
if (existsSync86(envPath))
|
|
49306
|
+
body = readFileSync74(envPath, "utf-8");
|
|
49252
49307
|
} catch {}
|
|
49253
49308
|
const line = `${VOICE_SIDECAR_TOKEN_ENV}=${token}`;
|
|
49254
49309
|
const keyRe = new RegExp(`^${VOICE_SIDECAR_TOKEN_ENV}=.*$`, "m");
|
|
@@ -49256,7 +49311,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49256
49311
|
`) ? body + `
|
|
49257
49312
|
` : body) + line + `
|
|
49258
49313
|
`;
|
|
49259
|
-
|
|
49314
|
+
writeFileSync31(envPath, next, {
|
|
49260
49315
|
encoding: "utf-8",
|
|
49261
49316
|
mode: 384
|
|
49262
49317
|
});
|
|
@@ -49308,11 +49363,11 @@ __export(exports_apply, {
|
|
|
49308
49363
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
|
|
49309
49364
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
49310
49365
|
});
|
|
49311
|
-
import { accessSync as accessSync3, chmodSync as chmodSync14, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as
|
|
49366
|
+
import { accessSync as accessSync3, chmodSync as chmodSync14, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync87, mkdirSync as mkdirSync50, readFileSync as readFileSync75, readdirSync as readdirSync31, renameSync as renameSync21, statSync as statSync47, writeFileSync as writeFileSync32 } from "node:fs";
|
|
49312
49367
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
49313
49368
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
49314
49369
|
import readline from "node:readline";
|
|
49315
|
-
import { dirname as
|
|
49370
|
+
import { dirname as dirname32, join as join88, resolve as resolve52 } from "node:path";
|
|
49316
49371
|
import { homedir as homedir49 } from "node:os";
|
|
49317
49372
|
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
49318
49373
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
@@ -49324,7 +49379,7 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49324
49379
|
return envPass;
|
|
49325
49380
|
try {
|
|
49326
49381
|
const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
|
|
49327
|
-
const blobPath =
|
|
49382
|
+
const blobPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49328
49383
|
const pass = readAutoUnlockFile2(blobPath);
|
|
49329
49384
|
return pass && pass.length > 0 ? pass : null;
|
|
49330
49385
|
} catch {
|
|
@@ -49333,10 +49388,10 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49333
49388
|
}
|
|
49334
49389
|
function materializeLitellmMasterKeyForBroker(masterKey, home2 = process.env.HOME ?? "/root") {
|
|
49335
49390
|
try {
|
|
49336
|
-
const stateDir =
|
|
49337
|
-
|
|
49338
|
-
const path9 =
|
|
49339
|
-
|
|
49391
|
+
const stateDir = join88(home2, ".switchroom", "state", "auth-broker");
|
|
49392
|
+
mkdirSync50(stateDir, { recursive: true, mode: 448 });
|
|
49393
|
+
const path9 = join88(stateDir, LITELLM_MASTER_KEY_STATE_BASENAME);
|
|
49394
|
+
writeFileSync32(path9, masterKey.trim() + `
|
|
49340
49395
|
`, { mode: 384 });
|
|
49341
49396
|
try {
|
|
49342
49397
|
chmodSync14(path9, 384);
|
|
@@ -49428,9 +49483,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49428
49483
|
const oauthAccount = config.auth?.active;
|
|
49429
49484
|
let pendingConfigEdits = false;
|
|
49430
49485
|
let configText = null;
|
|
49431
|
-
if (switchroomConfigPath &&
|
|
49486
|
+
if (switchroomConfigPath && existsSync87(switchroomConfigPath)) {
|
|
49432
49487
|
try {
|
|
49433
|
-
configText =
|
|
49488
|
+
configText = readFileSync75(switchroomConfigPath, "utf-8");
|
|
49434
49489
|
} catch (err) {
|
|
49435
49490
|
ctx.writeErr(source_default.yellow(` ! litellm: could not read config for ACL grants (${err.message}); keys will be provisioned but agents may lack read-ACL.
|
|
49436
49491
|
`));
|
|
@@ -49659,14 +49714,14 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49659
49714
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
49660
49715
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
49661
49716
|
if (isCustomPath && ctx.customVaultPath) {
|
|
49662
|
-
return
|
|
49717
|
+
return dirname32(ctx.customVaultPath);
|
|
49663
49718
|
}
|
|
49664
|
-
return
|
|
49719
|
+
return join88(homeDir, ".switchroom", "vault");
|
|
49665
49720
|
}
|
|
49666
49721
|
function inspectVaultBindMountDir(vaultDir) {
|
|
49667
|
-
if (!
|
|
49722
|
+
if (!existsSync87(vaultDir))
|
|
49668
49723
|
return { kind: "missing" };
|
|
49669
|
-
const entries =
|
|
49724
|
+
const entries = readdirSync31(vaultDir);
|
|
49670
49725
|
const unknown = [];
|
|
49671
49726
|
for (const name of entries) {
|
|
49672
49727
|
if (KNOWN_VAULT_ARTIFACT_NAMES.has(name))
|
|
@@ -49692,60 +49747,60 @@ function hasVaultRefs(value) {
|
|
|
49692
49747
|
async function ensureHostMountSources(config) {
|
|
49693
49748
|
const home2 = resolveHostHomeForCompose();
|
|
49694
49749
|
const dirs = [
|
|
49695
|
-
|
|
49696
|
-
|
|
49697
|
-
|
|
49698
|
-
|
|
49699
|
-
|
|
49750
|
+
join88(home2, ".switchroom", "approvals"),
|
|
49751
|
+
join88(home2, ".switchroom", "scheduler"),
|
|
49752
|
+
join88(home2, ".switchroom", "logs"),
|
|
49753
|
+
join88(home2, ".switchroom", "compose"),
|
|
49754
|
+
join88(home2, ".switchroom", "broker-operator")
|
|
49700
49755
|
];
|
|
49701
49756
|
for (const name of Object.keys(config.agents)) {
|
|
49702
|
-
dirs.push(
|
|
49703
|
-
dirs.push(
|
|
49704
|
-
dirs.push(
|
|
49705
|
-
dirs.push(
|
|
49706
|
-
if (
|
|
49707
|
-
dirs.push(
|
|
49757
|
+
dirs.push(join88(home2, ".switchroom", "agents", name));
|
|
49758
|
+
dirs.push(join88(home2, ".switchroom", "logs", name));
|
|
49759
|
+
dirs.push(join88(home2, ".claude", "projects", name));
|
|
49760
|
+
dirs.push(join88(home2, ".switchroom", "audit", name));
|
|
49761
|
+
if (existsSync87(join88(home2, ".switchroom-config"))) {
|
|
49762
|
+
dirs.push(join88(home2, ".switchroom-config", "agents", name, "personal-skills"));
|
|
49708
49763
|
}
|
|
49709
49764
|
}
|
|
49710
49765
|
for (const dir of dirs) {
|
|
49711
49766
|
await mkdir2(dir, { recursive: true });
|
|
49712
49767
|
}
|
|
49713
|
-
const autoUnlockPath =
|
|
49714
|
-
if (!
|
|
49715
|
-
|
|
49768
|
+
const autoUnlockPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49769
|
+
if (!existsSync87(autoUnlockPath)) {
|
|
49770
|
+
writeFileSync32(autoUnlockPath, "", { mode: 384 });
|
|
49716
49771
|
}
|
|
49717
|
-
const auditLogPath =
|
|
49718
|
-
if (!
|
|
49719
|
-
|
|
49772
|
+
const auditLogPath = join88(home2, ".switchroom", "vault-audit.log");
|
|
49773
|
+
if (!existsSync87(auditLogPath)) {
|
|
49774
|
+
writeFileSync32(auditLogPath, "", { mode: 420 });
|
|
49720
49775
|
}
|
|
49721
49776
|
const grantsDbDir = getGrantsDbDir(home2);
|
|
49722
|
-
|
|
49777
|
+
mkdirSync50(grantsDbDir, { recursive: true, mode: 448 });
|
|
49723
49778
|
migrateLegacyGrantsDbLocation(getGrantsDbPath(home2));
|
|
49724
|
-
const hostdAuditLogPath =
|
|
49725
|
-
if (!
|
|
49726
|
-
|
|
49779
|
+
const hostdAuditLogPath = join88(home2, ".switchroom", "host-control-audit.log");
|
|
49780
|
+
if (!existsSync87(hostdAuditLogPath)) {
|
|
49781
|
+
writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
|
|
49727
49782
|
}
|
|
49728
49783
|
for (const name of Object.keys(config.agents)) {
|
|
49729
|
-
const tokenPath =
|
|
49730
|
-
if (!
|
|
49731
|
-
|
|
49784
|
+
const tokenPath = join88(home2, ".switchroom", "agents", name, ".vault-token");
|
|
49785
|
+
if (!existsSync87(tokenPath)) {
|
|
49786
|
+
writeFileSync32(tokenPath, "", { mode: 384 });
|
|
49732
49787
|
}
|
|
49733
49788
|
try {
|
|
49734
49789
|
const uid = allocateAgentUid(name);
|
|
49735
49790
|
chownSync8(tokenPath, uid, uid);
|
|
49736
49791
|
} catch {}
|
|
49737
49792
|
}
|
|
49738
|
-
const fleetDir =
|
|
49793
|
+
const fleetDir = join88(home2, ".switchroom", "fleet");
|
|
49739
49794
|
await mkdir2(fleetDir, { recursive: true });
|
|
49740
|
-
const invariantsPath =
|
|
49795
|
+
const invariantsPath = join88(fleetDir, "switchroom-invariants.md");
|
|
49741
49796
|
const invariantsCanonical = renderFleetInvariants();
|
|
49742
|
-
const invariantsCurrent =
|
|
49797
|
+
const invariantsCurrent = existsSync87(invariantsPath) ? readFileSync75(invariantsPath, "utf-8") : null;
|
|
49743
49798
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
49744
|
-
|
|
49799
|
+
writeFileSync32(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
49745
49800
|
}
|
|
49746
|
-
const fleetClaudePath =
|
|
49747
|
-
if (!
|
|
49748
|
-
|
|
49801
|
+
const fleetClaudePath = join88(fleetDir, "CLAUDE.md");
|
|
49802
|
+
if (!existsSync87(fleetClaudePath)) {
|
|
49803
|
+
writeFileSync32(fleetClaudePath, renderFleetDefaultsClaudeMd(), {
|
|
49749
49804
|
mode: 420
|
|
49750
49805
|
});
|
|
49751
49806
|
}
|
|
@@ -49774,7 +49829,7 @@ function isInAgentContainer(vaultPresent, composeV2Present, env2 = process.env)
|
|
|
49774
49829
|
function runApplyPreflight(config, opts = {}) {
|
|
49775
49830
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
49776
49831
|
const detect = opts.detectComposeV2 ?? detectComposeV2;
|
|
49777
|
-
const vaultMissing = hasVaultRefs(config) && !
|
|
49832
|
+
const vaultMissing = hasVaultRefs(config) && !existsSync87(vaultPath);
|
|
49778
49833
|
const composeErr = detect();
|
|
49779
49834
|
if ((vaultMissing || composeErr) && isInAgentContainer(!vaultMissing, composeErr === null)) {
|
|
49780
49835
|
throw new Error(IN_AGENT_CONTAINER_APPLY_MSG);
|
|
@@ -49788,7 +49843,7 @@ function runApplyPreflight(config, opts = {}) {
|
|
|
49788
49843
|
detectAndReportLegacyGdriveSlots(vaultPath);
|
|
49789
49844
|
}
|
|
49790
49845
|
function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
49791
|
-
if (!
|
|
49846
|
+
if (!existsSync87(vaultPath))
|
|
49792
49847
|
return;
|
|
49793
49848
|
const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
49794
49849
|
if (!passphrase)
|
|
@@ -49829,17 +49884,17 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
49829
49884
|
}
|
|
49830
49885
|
function writeInstallTypeCache(homeDir = homedir49()) {
|
|
49831
49886
|
const ctx = detectInstallType();
|
|
49832
|
-
const dir =
|
|
49833
|
-
const out =
|
|
49887
|
+
const dir = join88(homeDir, ".switchroom");
|
|
49888
|
+
const out = join88(dir, "install-type.json");
|
|
49834
49889
|
const tmp = `${out}.tmp`;
|
|
49835
|
-
|
|
49890
|
+
mkdirSync50(dir, { recursive: true });
|
|
49836
49891
|
const payload = {
|
|
49837
49892
|
install_type: ctx.install_type,
|
|
49838
49893
|
detected_at: new Date().toISOString(),
|
|
49839
49894
|
source_paths: ctx.source_paths
|
|
49840
49895
|
};
|
|
49841
|
-
|
|
49842
|
-
|
|
49896
|
+
writeFileSync32(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
|
|
49897
|
+
renameSync21(tmp, out);
|
|
49843
49898
|
return out;
|
|
49844
49899
|
}
|
|
49845
49900
|
async function runApply(config, options, deps = {}, switchroomConfigPath) {
|
|
@@ -49897,17 +49952,17 @@ Applying switchroom config...
|
|
|
49897
49952
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
49898
49953
|
`));
|
|
49899
49954
|
try {
|
|
49900
|
-
installUpdatePromptHook(
|
|
49955
|
+
installUpdatePromptHook(join88(agentsDir, name));
|
|
49901
49956
|
} catch (hookErr) {
|
|
49902
49957
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
49903
49958
|
`));
|
|
49904
49959
|
}
|
|
49905
|
-
await refreshAgentConnectionHealth(config, name,
|
|
49960
|
+
await refreshAgentConnectionHealth(config, name, join88(agentsDir, name), {
|
|
49906
49961
|
vaultAclReader: connHealthVaultAclReader
|
|
49907
49962
|
});
|
|
49908
49963
|
try {
|
|
49909
49964
|
const uid = allocateAgentUid(name);
|
|
49910
|
-
alignAgentUid(name,
|
|
49965
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49911
49966
|
confirm: !options.nonInteractive,
|
|
49912
49967
|
writeOut
|
|
49913
49968
|
});
|
|
@@ -49952,7 +50007,7 @@ Applying switchroom config...
|
|
|
49952
50007
|
for (const name of agentNames) {
|
|
49953
50008
|
try {
|
|
49954
50009
|
const uid = allocateAgentUid(name);
|
|
49955
|
-
alignAgentUid(name,
|
|
50010
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49956
50011
|
confirm: !options.nonInteractive,
|
|
49957
50012
|
writeOut
|
|
49958
50013
|
});
|
|
@@ -50290,18 +50345,18 @@ function copyExampleConfig2(name) {
|
|
|
50290
50345
|
throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
|
|
50291
50346
|
}
|
|
50292
50347
|
const dest = resolve52(process.cwd(), "switchroom.yaml");
|
|
50293
|
-
if (
|
|
50348
|
+
if (existsSync87(dest)) {
|
|
50294
50349
|
console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
|
|
50295
50350
|
return;
|
|
50296
50351
|
}
|
|
50297
50352
|
const embedded = EMBEDDED_EXAMPLES[name];
|
|
50298
50353
|
if (embedded !== undefined) {
|
|
50299
|
-
|
|
50354
|
+
writeFileSync32(dest, embedded, { encoding: "utf8" });
|
|
50300
50355
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
50301
50356
|
return;
|
|
50302
50357
|
}
|
|
50303
50358
|
const exampleFile = resolve52(import.meta.dirname, `../../examples/${name}.yaml`);
|
|
50304
|
-
if (!
|
|
50359
|
+
if (!existsSync87(exampleFile)) {
|
|
50305
50360
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
50306
50361
|
}
|
|
50307
50362
|
copyFileSync12(exampleFile, dest);
|
|
@@ -50312,8 +50367,8 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
50312
50367
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
50313
50368
|
const unwritable = [];
|
|
50314
50369
|
for (const name of targets) {
|
|
50315
|
-
const startSh =
|
|
50316
|
-
if (!
|
|
50370
|
+
const startSh = join88(agentsDir, name, "start.sh");
|
|
50371
|
+
if (!existsSync87(startSh))
|
|
50317
50372
|
continue;
|
|
50318
50373
|
try {
|
|
50319
50374
|
accessSync3(startSh, fsConstants6.W_OK);
|
|
@@ -50518,7 +50573,7 @@ var init_apply = __esm(() => {
|
|
|
50518
50573
|
switchroom: switchroom_default,
|
|
50519
50574
|
minimal: minimal_default
|
|
50520
50575
|
};
|
|
50521
|
-
DEFAULT_COMPOSE_PATH2 =
|
|
50576
|
+
DEFAULT_COMPOSE_PATH2 = join88(homedir49(), ".switchroom", "compose", "docker-compose.yml");
|
|
50522
50577
|
IN_AGENT_CONTAINER_APPLY_MSG = "`switchroom apply`'s full per-agent scaffold cannot run from inside an " + "agent container \u2014 this is a host/hostd operation by construction " + "(no vault at the container HOME, and no `docker compose` v2 plugin here).\nTo roll the fleet to a new version, drive the hostd rollout (`mcp__hostd__rollout`): it runs a `--compose-only` apply plus a per-agent restart-reconcile, and each agent refreshes its own templates " + `on restart \u2014 the roll completes without any agent running a full apply.
|
|
50523
50578
|
` + "A full host-side `sudo switchroom apply` is only needed for structural changes (compose regeneration / new-agent scaffolding), and is run by the operator on the host, never from inside an agent.";
|
|
50524
50579
|
SELF_ELEVATE_PRESERVED_ENV = [
|
|
@@ -59829,49 +59884,49 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59829
59884
|
schemelessOptions.skipEscape = true;
|
|
59830
59885
|
return serialize(resolved, schemelessOptions);
|
|
59831
59886
|
}
|
|
59832
|
-
function resolveComponent(base,
|
|
59887
|
+
function resolveComponent(base, relative6, options, skipNormalization) {
|
|
59833
59888
|
const target = {};
|
|
59834
59889
|
if (!skipNormalization) {
|
|
59835
59890
|
base = parse6(serialize(base, options), options);
|
|
59836
|
-
|
|
59891
|
+
relative6 = parse6(serialize(relative6, options), options);
|
|
59837
59892
|
}
|
|
59838
59893
|
options = options || {};
|
|
59839
|
-
if (!options.tolerant &&
|
|
59840
|
-
target.scheme =
|
|
59841
|
-
target.userinfo =
|
|
59842
|
-
target.host =
|
|
59843
|
-
target.port =
|
|
59844
|
-
target.path = removeDotSegments(
|
|
59845
|
-
target.query =
|
|
59894
|
+
if (!options.tolerant && relative6.scheme) {
|
|
59895
|
+
target.scheme = relative6.scheme;
|
|
59896
|
+
target.userinfo = relative6.userinfo;
|
|
59897
|
+
target.host = relative6.host;
|
|
59898
|
+
target.port = relative6.port;
|
|
59899
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
59900
|
+
target.query = relative6.query;
|
|
59846
59901
|
} else {
|
|
59847
|
-
if (
|
|
59848
|
-
target.userinfo =
|
|
59849
|
-
target.host =
|
|
59850
|
-
target.port =
|
|
59851
|
-
target.path = removeDotSegments(
|
|
59852
|
-
target.query =
|
|
59902
|
+
if (relative6.userinfo !== undefined || relative6.host !== undefined || relative6.port !== undefined) {
|
|
59903
|
+
target.userinfo = relative6.userinfo;
|
|
59904
|
+
target.host = relative6.host;
|
|
59905
|
+
target.port = relative6.port;
|
|
59906
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
59907
|
+
target.query = relative6.query;
|
|
59853
59908
|
} else {
|
|
59854
|
-
if (!
|
|
59909
|
+
if (!relative6.path) {
|
|
59855
59910
|
target.path = base.path;
|
|
59856
|
-
if (
|
|
59857
|
-
target.query =
|
|
59911
|
+
if (relative6.query !== undefined) {
|
|
59912
|
+
target.query = relative6.query;
|
|
59858
59913
|
} else {
|
|
59859
59914
|
target.query = base.query;
|
|
59860
59915
|
}
|
|
59861
59916
|
} else {
|
|
59862
|
-
if (
|
|
59863
|
-
target.path = removeDotSegments(
|
|
59917
|
+
if (relative6.path[0] === "/") {
|
|
59918
|
+
target.path = removeDotSegments(relative6.path);
|
|
59864
59919
|
} else {
|
|
59865
59920
|
if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
|
|
59866
|
-
target.path = "/" +
|
|
59921
|
+
target.path = "/" + relative6.path;
|
|
59867
59922
|
} else if (!base.path) {
|
|
59868
|
-
target.path =
|
|
59923
|
+
target.path = relative6.path;
|
|
59869
59924
|
} else {
|
|
59870
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
59925
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
|
|
59871
59926
|
}
|
|
59872
59927
|
target.path = removeDotSegments(target.path);
|
|
59873
59928
|
}
|
|
59874
|
-
target.query =
|
|
59929
|
+
target.query = relative6.query;
|
|
59875
59930
|
}
|
|
59876
59931
|
target.userinfo = base.userinfo;
|
|
59877
59932
|
target.host = base.host;
|
|
@@ -59879,7 +59934,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59879
59934
|
}
|
|
59880
59935
|
target.scheme = base.scheme;
|
|
59881
59936
|
}
|
|
59882
|
-
target.fragment =
|
|
59937
|
+
target.fragment = relative6.fragment;
|
|
59883
59938
|
return target;
|
|
59884
59939
|
}
|
|
59885
59940
|
function equal(uriA, uriB, options) {
|
|
@@ -63967,7 +64022,7 @@ __export(exports_server2, {
|
|
|
63967
64022
|
TOOLS: () => TOOLS2
|
|
63968
64023
|
});
|
|
63969
64024
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
63970
|
-
import { existsSync as
|
|
64025
|
+
import { existsSync as existsSync96, readFileSync as readFileSync84 } from "node:fs";
|
|
63971
64026
|
function selfSocketPath() {
|
|
63972
64027
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
63973
64028
|
}
|
|
@@ -63994,7 +64049,7 @@ async function dispatchTool2(name, args) {
|
|
|
63994
64049
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
63995
64050
|
}
|
|
63996
64051
|
const sockPath = selfSocketPath();
|
|
63997
|
-
if (!
|
|
64052
|
+
if (!existsSync96(sockPath)) {
|
|
63998
64053
|
return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
|
|
63999
64054
|
}
|
|
64000
64055
|
let req;
|
|
@@ -64245,18 +64300,18 @@ function resolveAuditLogPath() {
|
|
|
64245
64300
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
64246
64301
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
64247
64302
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
64248
|
-
if (
|
|
64303
|
+
if (existsSync96(bindMounted))
|
|
64249
64304
|
return bindMounted;
|
|
64250
64305
|
return defaultAuditLogPath2();
|
|
64251
64306
|
}
|
|
64252
64307
|
function getLastUpdateApplyStatus() {
|
|
64253
64308
|
const path9 = resolveAuditLogPath();
|
|
64254
|
-
if (!
|
|
64309
|
+
if (!existsSync96(path9)) {
|
|
64255
64310
|
return errorText2(`get_status: audit log not found at ${path9}. No update_apply has run yet?`);
|
|
64256
64311
|
}
|
|
64257
64312
|
let raw;
|
|
64258
64313
|
try {
|
|
64259
|
-
raw =
|
|
64314
|
+
raw = readFileSync84(path9, "utf-8");
|
|
64260
64315
|
} catch (err2) {
|
|
64261
64316
|
return errorText2(`get_status: failed to read audit log at ${path9}: ${err2.message}`);
|
|
64262
64317
|
}
|
|
@@ -65006,14 +65061,14 @@ var init_header_passthrough_guard = __esm(() => {
|
|
|
65006
65061
|
});
|
|
65007
65062
|
|
|
65008
65063
|
// src/fleet-health/litellm-config-sensor.ts
|
|
65009
|
-
import { readFileSync as
|
|
65064
|
+
import { readFileSync as readFileSync86, existsSync as existsSync99 } from "node:fs";
|
|
65010
65065
|
function resolveLitellmConfigPath(explicit) {
|
|
65011
65066
|
return explicit ?? process.env.LITELLM_CONFIG_PATH ?? DEFAULT_LITELLM_CONFIG_PATH;
|
|
65012
65067
|
}
|
|
65013
65068
|
function scanLitellmConfig(opts = {}) {
|
|
65014
65069
|
const path9 = resolveLitellmConfigPath(opts.path);
|
|
65015
|
-
const exists = opts.existsFn ??
|
|
65016
|
-
const read = opts.readFn ?? ((p) =>
|
|
65070
|
+
const exists = opts.existsFn ?? existsSync99;
|
|
65071
|
+
const read = opts.readFn ?? ((p) => readFileSync86(p, "utf-8"));
|
|
65017
65072
|
const log = opts.log ?? (() => {});
|
|
65018
65073
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
65019
65074
|
if (!exists(path9)) {
|
|
@@ -65068,23 +65123,23 @@ __export(exports_scan, {
|
|
|
65068
65123
|
ledgerPathForBase: () => ledgerPathForBase
|
|
65069
65124
|
});
|
|
65070
65125
|
import {
|
|
65071
|
-
readFileSync as
|
|
65072
|
-
readdirSync as
|
|
65073
|
-
existsSync as
|
|
65074
|
-
mkdirSync as
|
|
65075
|
-
writeFileSync as
|
|
65126
|
+
readFileSync as readFileSync87,
|
|
65127
|
+
readdirSync as readdirSync39,
|
|
65128
|
+
existsSync as existsSync100,
|
|
65129
|
+
mkdirSync as mkdirSync57,
|
|
65130
|
+
writeFileSync as writeFileSync38
|
|
65076
65131
|
} from "node:fs";
|
|
65077
|
-
import { resolve as resolve57, dirname as
|
|
65132
|
+
import { resolve as resolve57, dirname as dirname35 } from "node:path";
|
|
65078
65133
|
import { homedir as homedir58 } from "node:os";
|
|
65079
65134
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir58()) {
|
|
65080
65135
|
return resolve57(home2, ".switchroom");
|
|
65081
65136
|
}
|
|
65082
65137
|
function listAgents(base) {
|
|
65083
65138
|
const dir = resolve57(base, "agents");
|
|
65084
|
-
if (!
|
|
65139
|
+
if (!existsSync100(dir))
|
|
65085
65140
|
return [];
|
|
65086
65141
|
try {
|
|
65087
|
-
return
|
|
65142
|
+
return readdirSync39(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
65088
65143
|
} catch {
|
|
65089
65144
|
return [];
|
|
65090
65145
|
}
|
|
@@ -65110,16 +65165,16 @@ function runScan(opts = {}) {
|
|
|
65110
65165
|
let gwText = "";
|
|
65111
65166
|
let sawArtifact = false;
|
|
65112
65167
|
try {
|
|
65113
|
-
if (
|
|
65114
|
-
turnsText =
|
|
65168
|
+
if (existsSync100(turnsPath)) {
|
|
65169
|
+
turnsText = readFileSync87(turnsPath, "utf-8");
|
|
65115
65170
|
sawArtifact = true;
|
|
65116
65171
|
}
|
|
65117
65172
|
} catch (e) {
|
|
65118
65173
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
65119
65174
|
}
|
|
65120
65175
|
try {
|
|
65121
|
-
if (
|
|
65122
|
-
gwText =
|
|
65176
|
+
if (existsSync100(gwPath)) {
|
|
65177
|
+
gwText = readFileSync87(gwPath, "utf-8");
|
|
65123
65178
|
sawArtifact = true;
|
|
65124
65179
|
}
|
|
65125
65180
|
} catch (e) {
|
|
@@ -65161,20 +65216,20 @@ function runScan(opts = {}) {
|
|
|
65161
65216
|
function readLedgerIfPresent(base) {
|
|
65162
65217
|
const path9 = ledgerPathForBase(base);
|
|
65163
65218
|
try {
|
|
65164
|
-
if (!
|
|
65219
|
+
if (!existsSync100(path9))
|
|
65165
65220
|
return null;
|
|
65166
|
-
return JSON.parse(
|
|
65221
|
+
return JSON.parse(readFileSync87(path9, "utf-8"));
|
|
65167
65222
|
} catch {
|
|
65168
65223
|
return null;
|
|
65169
65224
|
}
|
|
65170
65225
|
}
|
|
65171
65226
|
function ledgerPathForBase(base) {
|
|
65172
|
-
return fleetHealthLedgerPath(
|
|
65227
|
+
return fleetHealthLedgerPath(dirname35(base));
|
|
65173
65228
|
}
|
|
65174
65229
|
function writeLedger(base, ledger) {
|
|
65175
65230
|
const path9 = ledgerPathForBase(base);
|
|
65176
|
-
|
|
65177
|
-
|
|
65231
|
+
mkdirSync57(dirname35(path9), { recursive: true });
|
|
65232
|
+
writeFileSync38(path9, JSON.stringify(ledger, null, 2) + `
|
|
65178
65233
|
`, "utf-8");
|
|
65179
65234
|
return path9;
|
|
65180
65235
|
}
|
|
@@ -72180,7 +72235,7 @@ init_audit_log();
|
|
|
72180
72235
|
init_test_isolation_guard();
|
|
72181
72236
|
import * as net3 from "node:net";
|
|
72182
72237
|
import { mkdirSync as mkdirSync25, chmodSync as chmodSync9, chownSync as chownSync4, existsSync as existsSync43, readFileSync as readFileSync35, readdirSync as readdirSync18, statSync as statSync26, unlinkSync as unlinkSync10, writeFileSync as writeFileSync14, renameSync as renameSync14 } from "node:fs";
|
|
72183
|
-
import { dirname as
|
|
72238
|
+
import { dirname as dirname15, resolve as resolve29, basename as basename7 } from "node:path";
|
|
72184
72239
|
import * as os4 from "node:os";
|
|
72185
72240
|
import * as path4 from "node:path";
|
|
72186
72241
|
|
|
@@ -74556,7 +74611,7 @@ class VaultBroker {
|
|
|
74556
74611
|
this.passphrase = this.testOpts._testPassphrase;
|
|
74557
74612
|
}
|
|
74558
74613
|
process.umask(63);
|
|
74559
|
-
const parentDir =
|
|
74614
|
+
const parentDir = dirname15(this.socketPath);
|
|
74560
74615
|
mkdirSync25(parentDir, { recursive: true, mode: 448 });
|
|
74561
74616
|
try {
|
|
74562
74617
|
chmodSync9(parentDir, 448);
|
|
@@ -76101,15 +76156,15 @@ class VaultBroker {
|
|
|
76101
76156
|
}
|
|
76102
76157
|
}
|
|
76103
76158
|
function detectVaultLayoutDrift(vaultPath) {
|
|
76104
|
-
const dir =
|
|
76159
|
+
const dir = dirname15(vaultPath);
|
|
76105
76160
|
if (basename7(dir) !== "vault")
|
|
76106
76161
|
return;
|
|
76107
76162
|
if (basename7(vaultPath) !== "vault.enc")
|
|
76108
76163
|
return;
|
|
76109
|
-
const switchroomDir =
|
|
76164
|
+
const switchroomDir = dirname15(dir);
|
|
76110
76165
|
if (basename7(switchroomDir) !== ".switchroom")
|
|
76111
76166
|
return;
|
|
76112
|
-
const home2 =
|
|
76167
|
+
const home2 = dirname15(switchroomDir);
|
|
76113
76168
|
const result = inspectVaultLayout(home2);
|
|
76114
76169
|
if (result.kind === "divergent") {
|
|
76115
76170
|
throw new VaultError(`Vault layout divergence detected at boot: ${result.details.oldPath} and ${result.details.newPath} are both regular files with different content. An older switchroom CLI may have written to the legacy path after migration ran. Run \`switchroom apply\` from the host to surface the recovery recipe (state E refusal with literal \`mv\` commands). See docs/operators/state-e-recovery.md.`);
|
|
@@ -79590,7 +79645,7 @@ import {
|
|
|
79590
79645
|
writeSync as writeSync8,
|
|
79591
79646
|
constants as fsConstants3
|
|
79592
79647
|
} from "node:fs";
|
|
79593
|
-
import { resolve as resolve34, extname, join as join51, relative, dirname as
|
|
79648
|
+
import { resolve as resolve34, extname, join as join51, relative as relative3, dirname as dirname18 } from "node:path";
|
|
79594
79649
|
import { homedir as homedir28 } from "node:os";
|
|
79595
79650
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
79596
79651
|
|
|
@@ -83051,7 +83106,7 @@ function resolveWebToken() {
|
|
|
83051
83106
|
return existing;
|
|
83052
83107
|
}
|
|
83053
83108
|
const token = randomBytes11(32).toString("hex");
|
|
83054
|
-
mkdirSync31(
|
|
83109
|
+
mkdirSync31(dirname18(tokenPath), { recursive: true, mode: 448 });
|
|
83055
83110
|
try {
|
|
83056
83111
|
const fd = openSync11(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
|
|
83057
83112
|
try {
|
|
@@ -83638,7 +83693,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
83638
83693
|
} catch {
|
|
83639
83694
|
return new Response("Not Found", { status: 404 });
|
|
83640
83695
|
}
|
|
83641
|
-
const rel =
|
|
83696
|
+
const rel = relative3(uiDir, realFullPath);
|
|
83642
83697
|
if (rel.startsWith("..") || resolve34(uiDir, rel) !== realFullPath) {
|
|
83643
83698
|
return new Response("Forbidden", { status: 403 });
|
|
83644
83699
|
}
|
|
@@ -83768,7 +83823,7 @@ init_loader();
|
|
|
83768
83823
|
|
|
83769
83824
|
// src/web/startup-guard.ts
|
|
83770
83825
|
import { existsSync as existsSync58, readFileSync as readFileSync53, writeFileSync as writeFileSync18, mkdirSync as mkdirSync32, statSync as statSync33, unlinkSync as unlinkSync13 } from "node:fs";
|
|
83771
|
-
import { dirname as
|
|
83826
|
+
import { dirname as dirname19 } from "node:path";
|
|
83772
83827
|
function detectConfigMountFault(configPath, deps = {}) {
|
|
83773
83828
|
const stat = deps.stat ?? ((p) => statSync33(p));
|
|
83774
83829
|
let st;
|
|
@@ -83815,7 +83870,7 @@ function readCrashState(statePath) {
|
|
|
83815
83870
|
}
|
|
83816
83871
|
function writeCrashState(statePath, state) {
|
|
83817
83872
|
try {
|
|
83818
|
-
mkdirSync32(
|
|
83873
|
+
mkdirSync32(dirname19(statePath), { recursive: true });
|
|
83819
83874
|
writeFileSync18(statePath, JSON.stringify(state), { mode: 384 });
|
|
83820
83875
|
} catch {}
|
|
83821
83876
|
}
|
|
@@ -83911,7 +83966,7 @@ init_atomic();
|
|
|
83911
83966
|
init_loader();
|
|
83912
83967
|
init_scaffold();
|
|
83913
83968
|
import { existsSync as existsSync59, copyFileSync as copyFileSync9, readFileSync as readFileSync54, mkdirSync as mkdirSync33, statSync as statSync34 } from "node:fs";
|
|
83914
|
-
import { resolve as resolve35, dirname as
|
|
83969
|
+
import { resolve as resolve35, dirname as dirname20 } from "node:path";
|
|
83915
83970
|
init_state();
|
|
83916
83971
|
init_vault();
|
|
83917
83972
|
init_manager();
|
|
@@ -84223,7 +84278,7 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
84223
84278
|
if (!existsSync59(srcFile)) {
|
|
84224
84279
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
84225
84280
|
}
|
|
84226
|
-
mkdirSync33(
|
|
84281
|
+
mkdirSync33(dirname20(destFile), { recursive: true });
|
|
84227
84282
|
copyFileSync9(srcFile, destFile);
|
|
84228
84283
|
console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
|
|
84229
84284
|
console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
|
|
@@ -85003,9 +85058,9 @@ init_source();
|
|
|
85003
85058
|
init_loader();
|
|
85004
85059
|
init_lifecycle();
|
|
85005
85060
|
init_compose_env();
|
|
85006
|
-
import {
|
|
85061
|
+
import { existsSync as existsSync68, mkdirSync as mkdirSync36, readFileSync as readFileSync61, realpathSync as realpathSync6, statSync as statSync40, chownSync as chownSync5 } from "node:fs";
|
|
85007
85062
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
85008
|
-
import { join as
|
|
85063
|
+
import { join as join70, dirname as dirname23, resolve as resolve40 } from "node:path";
|
|
85009
85064
|
import { homedir as homedir40 } from "node:os";
|
|
85010
85065
|
|
|
85011
85066
|
// src/cli/release-yaml.ts
|
|
@@ -85096,10 +85151,132 @@ ${lines.join(`
|
|
|
85096
85151
|
|
|
85097
85152
|
// src/cli/update.ts
|
|
85098
85153
|
init_hindsight();
|
|
85154
|
+
init_scaffold_integration();
|
|
85155
|
+
|
|
85156
|
+
// src/cli/sync-bundled-skills.ts
|
|
85157
|
+
import {
|
|
85158
|
+
cpSync as cpSync2,
|
|
85159
|
+
existsSync as existsSync67,
|
|
85160
|
+
mkdirSync as mkdirSync35,
|
|
85161
|
+
readFileSync as readFileSync60,
|
|
85162
|
+
readdirSync as readdirSync24,
|
|
85163
|
+
renameSync as renameSync16,
|
|
85164
|
+
rmSync as rmSync12,
|
|
85165
|
+
writeFileSync as writeFileSync19
|
|
85166
|
+
} from "node:fs";
|
|
85167
|
+
import { join as join69 } from "node:path";
|
|
85168
|
+
var BUNDLED_SKILL_MANIFEST_NAME = ".switchroom-manifest.json";
|
|
85169
|
+
function listSkillDirs(dir) {
|
|
85170
|
+
if (!existsSync67(dir))
|
|
85171
|
+
return [];
|
|
85172
|
+
return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
85173
|
+
}
|
|
85174
|
+
function readBundledSkillManifest(poolDir) {
|
|
85175
|
+
const path5 = join69(poolDir, BUNDLED_SKILL_MANIFEST_NAME);
|
|
85176
|
+
if (!existsSync67(path5))
|
|
85177
|
+
return { firstRun: true };
|
|
85178
|
+
try {
|
|
85179
|
+
const parsed = JSON.parse(readFileSync60(path5, "utf8"));
|
|
85180
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.skills) || !parsed.skills.every((s) => typeof s === "string")) {
|
|
85181
|
+
return { corrupt: true };
|
|
85182
|
+
}
|
|
85183
|
+
const m = parsed;
|
|
85184
|
+
return { manifest: { version: String(m.version ?? ""), skills: m.skills, updatedAt: String(m.updatedAt ?? "") } };
|
|
85185
|
+
} catch {
|
|
85186
|
+
return { corrupt: true };
|
|
85187
|
+
}
|
|
85188
|
+
}
|
|
85189
|
+
function stageAndSwap(srcSkill, destSkill, poolDir, name) {
|
|
85190
|
+
const staging = join69(poolDir, `.tmp-${name}-${process.pid}-${Date.now()}`);
|
|
85191
|
+
try {
|
|
85192
|
+
rmSync12(staging, { recursive: true, force: true });
|
|
85193
|
+
cpSync2(srcSkill, staging, { recursive: true, dereference: false });
|
|
85194
|
+
rmSync12(destSkill, { recursive: true, force: true });
|
|
85195
|
+
renameSync16(staging, destSkill);
|
|
85196
|
+
} finally {
|
|
85197
|
+
rmSync12(staging, { recursive: true, force: true });
|
|
85198
|
+
}
|
|
85199
|
+
}
|
|
85200
|
+
function syncBundledSkills(opts) {
|
|
85201
|
+
const { source, dest, version: version2 } = opts;
|
|
85202
|
+
const result = {
|
|
85203
|
+
added: [],
|
|
85204
|
+
updated: [],
|
|
85205
|
+
removed: [],
|
|
85206
|
+
preserved: [],
|
|
85207
|
+
ownershipTransferred: [],
|
|
85208
|
+
firstRun: false,
|
|
85209
|
+
manifestCorrupt: false
|
|
85210
|
+
};
|
|
85211
|
+
mkdirSync35(dest, { recursive: true });
|
|
85212
|
+
const prior = readBundledSkillManifest(dest);
|
|
85213
|
+
const priorSkills = new Set("manifest" in prior ? prior.manifest.skills : []);
|
|
85214
|
+
result.firstRun = "firstRun" in prior;
|
|
85215
|
+
result.manifestCorrupt = "corrupt" in prior;
|
|
85216
|
+
const shipped = listSkillDirs(source).sort();
|
|
85217
|
+
const shippedSet = new Set(shipped);
|
|
85218
|
+
for (const name of shipped) {
|
|
85219
|
+
const destSkill = join69(dest, name);
|
|
85220
|
+
const existed = existsSync67(destSkill);
|
|
85221
|
+
let transferred = false;
|
|
85222
|
+
if (existed && !priorSkills.has(name) && !result.firstRun) {
|
|
85223
|
+
const backup = join69(dest, `${name}.operator-backup-${process.pid}-${Date.now()}`);
|
|
85224
|
+
try {
|
|
85225
|
+
renameSync16(destSkill, backup);
|
|
85226
|
+
transferred = true;
|
|
85227
|
+
result.ownershipTransferred.push(name);
|
|
85228
|
+
process.stderr.write(`switchroom: WARNING \u2014 a shipped skill "${name}" collides with an ` + `operator-added pool dir of the same name. Preserved the existing ` + `content as "${backup}" and installed the shipped skill under "${name}". ` + `If you meant to keep your version, rename it to a distinct skill name.
|
|
85229
|
+
`);
|
|
85230
|
+
} catch {
|
|
85231
|
+
result.ownershipTransferred.push(name);
|
|
85232
|
+
process.stderr.write(`switchroom: WARNING \u2014 shipped skill "${name}" collides with an ` + `operator-added pool dir and the preservation backup failed; left the ` + `existing dir in place and did NOT install the shipped skill. Resolve ` + `the name collision manually.
|
|
85233
|
+
`);
|
|
85234
|
+
continue;
|
|
85235
|
+
}
|
|
85236
|
+
}
|
|
85237
|
+
stageAndSwap(join69(source, name), destSkill, dest, name);
|
|
85238
|
+
if (!transferred && (priorSkills.has(name) || existed))
|
|
85239
|
+
result.updated.push(name);
|
|
85240
|
+
else
|
|
85241
|
+
result.added.push(name);
|
|
85242
|
+
}
|
|
85243
|
+
if (!result.firstRun && !result.manifestCorrupt) {
|
|
85244
|
+
for (const name of priorSkills) {
|
|
85245
|
+
if (shippedSet.has(name))
|
|
85246
|
+
continue;
|
|
85247
|
+
const target = join69(dest, name);
|
|
85248
|
+
if (existsSync67(target)) {
|
|
85249
|
+
rmSync12(target, { recursive: true, force: true });
|
|
85250
|
+
result.removed.push(name);
|
|
85251
|
+
}
|
|
85252
|
+
}
|
|
85253
|
+
}
|
|
85254
|
+
for (const name of listSkillDirs(dest)) {
|
|
85255
|
+
if (!shippedSet.has(name) && !priorSkills.has(name)) {
|
|
85256
|
+
result.preserved.push(name);
|
|
85257
|
+
}
|
|
85258
|
+
}
|
|
85259
|
+
result.removed.sort();
|
|
85260
|
+
result.preserved.sort();
|
|
85261
|
+
const manifest = {
|
|
85262
|
+
version: version2,
|
|
85263
|
+
skills: shipped,
|
|
85264
|
+
updatedAt: new Date().toISOString()
|
|
85265
|
+
};
|
|
85266
|
+
const manifestPath = join69(dest, BUNDLED_SKILL_MANIFEST_NAME);
|
|
85267
|
+
const manifestTmp = join69(dest, `${BUNDLED_SKILL_MANIFEST_NAME}.tmp-${process.pid}-${Date.now()}`);
|
|
85268
|
+
writeFileSync19(manifestTmp, JSON.stringify(manifest, null, 2) + `
|
|
85269
|
+
`, "utf8");
|
|
85270
|
+
renameSync16(manifestTmp, manifestPath);
|
|
85271
|
+
return result;
|
|
85272
|
+
}
|
|
85273
|
+
|
|
85274
|
+
// src/cli/update.ts
|
|
85275
|
+
init_resolve_version();
|
|
85099
85276
|
function defaultPersistPin(configPath) {
|
|
85100
85277
|
return (pin) => {
|
|
85101
85278
|
const path5 = configPath ?? findConfigFile();
|
|
85102
|
-
const before =
|
|
85279
|
+
const before = readFileSync61(path5, "utf8");
|
|
85103
85280
|
const after = setReleasePinInConfig(before, pin);
|
|
85104
85281
|
if (after === before)
|
|
85105
85282
|
return;
|
|
@@ -85113,18 +85290,18 @@ function defaultPersistPin(configPath) {
|
|
|
85113
85290
|
} catch {}
|
|
85114
85291
|
};
|
|
85115
85292
|
}
|
|
85116
|
-
var DEFAULT_COMPOSE_PATH =
|
|
85293
|
+
var DEFAULT_COMPOSE_PATH = join70(homedir40(), ".switchroom", "compose", "docker-compose.yml");
|
|
85117
85294
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
85118
|
-
let dir =
|
|
85295
|
+
let dir = dirname23(scriptPath);
|
|
85119
85296
|
for (let i = 0;i < 12; i++) {
|
|
85120
|
-
if (
|
|
85297
|
+
if (existsSync68(join70(dir, ".git"))) {
|
|
85121
85298
|
try {
|
|
85122
|
-
const pkg = JSON.parse(
|
|
85299
|
+
const pkg = JSON.parse(readFileSync61(join70(dir, "package.json"), "utf-8"));
|
|
85123
85300
|
if (pkg.name === "switchroom")
|
|
85124
85301
|
return true;
|
|
85125
85302
|
} catch {}
|
|
85126
85303
|
}
|
|
85127
|
-
const parent =
|
|
85304
|
+
const parent = dirname23(dir);
|
|
85128
85305
|
if (parent === dir)
|
|
85129
85306
|
break;
|
|
85130
85307
|
dir = parent;
|
|
@@ -85204,7 +85381,7 @@ function planUpdate(opts) {
|
|
|
85204
85381
|
steps.push({
|
|
85205
85382
|
name: "pull-images",
|
|
85206
85383
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
85207
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
85384
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync68(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
85208
85385
|
run: () => {
|
|
85209
85386
|
const r = runner("docker", [
|
|
85210
85387
|
"compose",
|
|
@@ -85330,23 +85507,48 @@ function planUpdate(opts) {
|
|
|
85330
85507
|
return;
|
|
85331
85508
|
}
|
|
85332
85509
|
const source = resolve40(import.meta.dirname, "../../skills");
|
|
85333
|
-
const dest =
|
|
85334
|
-
if (!
|
|
85510
|
+
const dest = join70(homedir40(), ".switchroom", "skills", "_bundled");
|
|
85511
|
+
if (!existsSync68(source)) {
|
|
85335
85512
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
85336
85513
|
`);
|
|
85337
85514
|
return;
|
|
85338
85515
|
}
|
|
85339
85516
|
try {
|
|
85340
|
-
|
|
85341
|
-
|
|
85517
|
+
mkdirSync36(dirname23(dest), { recursive: true });
|
|
85518
|
+
const r = syncBundledSkills({
|
|
85519
|
+
source,
|
|
85520
|
+
dest,
|
|
85521
|
+
version: SWITCHROOM_VERSION
|
|
85522
|
+
});
|
|
85523
|
+
if (r.manifestCorrupt) {
|
|
85524
|
+
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 pool manifest was unreadable; ` + `deleted nothing (fail-closed) and rewrote a clean manifest.
|
|
85525
|
+
`);
|
|
85526
|
+
}
|
|
85527
|
+
if (r.removed.length > 0) {
|
|
85528
|
+
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 removed ${r.removed.length} retired ` + `bundled skill(s): ${r.removed.join(", ")}.
|
|
85529
|
+
`);
|
|
85342
85530
|
}
|
|
85343
|
-
mkdirSync35(dirname21(dest), { recursive: true });
|
|
85344
|
-
cpSync2(source, dest, { recursive: true, dereference: false });
|
|
85345
85531
|
} catch (err) {
|
|
85346
85532
|
throw new Error(`sync-bundled-skills failed: ${err.message}`);
|
|
85347
85533
|
}
|
|
85348
85534
|
}
|
|
85349
85535
|
});
|
|
85536
|
+
steps.push({
|
|
85537
|
+
name: "verify-bundled-skills",
|
|
85538
|
+
description: "Assert every builtin default skill is present in ~/.switchroom/skills/_bundled/ after sync.",
|
|
85539
|
+
run: () => {
|
|
85540
|
+
if (opts.syncBundledSkillsFn)
|
|
85541
|
+
return;
|
|
85542
|
+
const dest = join70(homedir40(), ".switchroom", "skills", "_bundled");
|
|
85543
|
+
if (!existsSync68(dest)) {
|
|
85544
|
+
return;
|
|
85545
|
+
}
|
|
85546
|
+
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync68(join70(dest, key)));
|
|
85547
|
+
if (missing.length > 0) {
|
|
85548
|
+
throw new Error(`verify-bundled-skills: builtin default skill(s) missing from the pool after sync: ` + `${missing.join(", ")}. These ship in the CLI package and must exist in ${dest}. ` + `This is a broken sync or a packaging regression \u2014 the pool is not converged.`);
|
|
85549
|
+
}
|
|
85550
|
+
}
|
|
85551
|
+
});
|
|
85350
85552
|
steps.push({
|
|
85351
85553
|
name: "stamp-restart-marker",
|
|
85352
85554
|
description: 'Write a clean-shutdown marker for every agent (reason="operator: switchroom update") so the post-recreate boot card renders as graceful rather than crash',
|
|
@@ -85376,7 +85578,7 @@ function planUpdate(opts) {
|
|
|
85376
85578
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
85377
85579
|
run: () => {
|
|
85378
85580
|
try {
|
|
85379
|
-
const composeText =
|
|
85581
|
+
const composeText = readFileSync61(composePath, "utf8");
|
|
85380
85582
|
const pf = validateBindSources(composeText);
|
|
85381
85583
|
if (!pf.ok)
|
|
85382
85584
|
throw new Error(formatPreflightError(pf));
|
|
@@ -85453,12 +85655,12 @@ function defaultStatusProbe(composePath) {
|
|
|
85453
85655
|
try {
|
|
85454
85656
|
cliBuiltAt = new Date(statSync40(scriptPath).mtimeMs).toISOString();
|
|
85455
85657
|
} catch {}
|
|
85456
|
-
let dir =
|
|
85658
|
+
let dir = dirname23(scriptPath);
|
|
85457
85659
|
for (let i = 0;i < 8; i++) {
|
|
85458
|
-
const pkgPath =
|
|
85459
|
-
if (
|
|
85660
|
+
const pkgPath = join70(dir, "package.json");
|
|
85661
|
+
if (existsSync68(pkgPath)) {
|
|
85460
85662
|
try {
|
|
85461
|
-
const pkg = JSON.parse(
|
|
85663
|
+
const pkg = JSON.parse(readFileSync61(pkgPath, "utf-8"));
|
|
85462
85664
|
if (typeof pkg.version === "string")
|
|
85463
85665
|
cliVersion = pkg.version;
|
|
85464
85666
|
} catch (err) {
|
|
@@ -85466,7 +85668,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85466
85668
|
}
|
|
85467
85669
|
break;
|
|
85468
85670
|
}
|
|
85469
|
-
const parent =
|
|
85671
|
+
const parent = dirname23(dir);
|
|
85470
85672
|
if (parent === dir)
|
|
85471
85673
|
break;
|
|
85472
85674
|
dir = parent;
|
|
@@ -85479,7 +85681,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85479
85681
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
85480
85682
|
}
|
|
85481
85683
|
const services = [];
|
|
85482
|
-
if (!
|
|
85684
|
+
if (!existsSync68(composePath)) {
|
|
85483
85685
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
85484
85686
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
85485
85687
|
}
|
|
@@ -85675,7 +85877,7 @@ function registerUpdateCommand(program3) {
|
|
|
85675
85877
|
// src/cli/rollout.ts
|
|
85676
85878
|
init_helpers();
|
|
85677
85879
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
85678
|
-
import { readFileSync as
|
|
85880
|
+
import { readFileSync as readFileSync62, chownSync as chownSync6, statSync as statSync41 } from "node:fs";
|
|
85679
85881
|
import { homedir as homedir41 } from "node:os";
|
|
85680
85882
|
init_operator_uid();
|
|
85681
85883
|
init_atomic();
|
|
@@ -85961,7 +86163,7 @@ function resolveRollbackTarget(auditLogPath) {
|
|
|
85961
86163
|
const logPath = auditLogPath ?? defaultAuditLogPath2(homedir41());
|
|
85962
86164
|
let raw;
|
|
85963
86165
|
try {
|
|
85964
|
-
raw =
|
|
86166
|
+
raw = readFileSync62(logPath, "utf8");
|
|
85965
86167
|
} catch {
|
|
85966
86168
|
return null;
|
|
85967
86169
|
}
|
|
@@ -86079,7 +86281,7 @@ function registerRolloutCommand(program3) {
|
|
|
86079
86281
|
`),
|
|
86080
86282
|
webImageTag: () => deployedImageTag("switchroom-web"),
|
|
86081
86283
|
persistPin: (pin) => {
|
|
86082
|
-
const before =
|
|
86284
|
+
const before = readFileSync62(configPath, "utf8");
|
|
86083
86285
|
const after = setReleasePinInConfig(before, pin);
|
|
86084
86286
|
if (after === before)
|
|
86085
86287
|
return;
|
|
@@ -86157,8 +86359,8 @@ init_helpers();
|
|
|
86157
86359
|
init_lifecycle();
|
|
86158
86360
|
init_resolve_version();
|
|
86159
86361
|
import { execSync as execSync3 } from "node:child_process";
|
|
86160
|
-
import { existsSync as
|
|
86161
|
-
import { dirname as
|
|
86362
|
+
import { existsSync as existsSync69, readFileSync as readFileSync63 } from "node:fs";
|
|
86363
|
+
import { dirname as dirname24, join as join71 } from "node:path";
|
|
86162
86364
|
function getClaudeCodeVersion() {
|
|
86163
86365
|
try {
|
|
86164
86366
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -86208,16 +86410,16 @@ function formatUptime3(timestamp) {
|
|
|
86208
86410
|
function locateSwitchroomInstallDir() {
|
|
86209
86411
|
let dir = import.meta.dirname;
|
|
86210
86412
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
86211
|
-
const pkgPath =
|
|
86212
|
-
if (
|
|
86413
|
+
const pkgPath = join71(dir, "package.json");
|
|
86414
|
+
if (existsSync69(pkgPath)) {
|
|
86213
86415
|
try {
|
|
86214
|
-
const pkg = JSON.parse(
|
|
86215
|
-
if (pkg.name === "switchroom" &&
|
|
86416
|
+
const pkg = JSON.parse(readFileSync63(pkgPath, "utf-8"));
|
|
86417
|
+
if (pkg.name === "switchroom" && existsSync69(join71(dir, ".git"))) {
|
|
86216
86418
|
return dir;
|
|
86217
86419
|
}
|
|
86218
86420
|
} catch {}
|
|
86219
86421
|
}
|
|
86220
|
-
dir =
|
|
86422
|
+
dir = dirname24(dir);
|
|
86221
86423
|
}
|
|
86222
86424
|
return null;
|
|
86223
86425
|
}
|
|
@@ -86390,29 +86592,29 @@ import { resolve as resolve42 } from "node:path";
|
|
|
86390
86592
|
|
|
86391
86593
|
// src/agents/session-retention.ts
|
|
86392
86594
|
import {
|
|
86393
|
-
existsSync as
|
|
86394
|
-
readdirSync as
|
|
86595
|
+
existsSync as existsSync70,
|
|
86596
|
+
readdirSync as readdirSync25,
|
|
86395
86597
|
statSync as statSync42,
|
|
86396
86598
|
unlinkSync as unlinkSync14
|
|
86397
86599
|
} from "node:fs";
|
|
86398
|
-
import { join as
|
|
86600
|
+
import { join as join72 } from "node:path";
|
|
86399
86601
|
var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
|
|
86400
86602
|
var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
86401
86603
|
var MIN_KEEP = 2;
|
|
86402
86604
|
function collectSessionJsonl(claudeConfigDir) {
|
|
86403
|
-
const projects =
|
|
86404
|
-
if (!
|
|
86605
|
+
const projects = join72(claudeConfigDir, "projects");
|
|
86606
|
+
if (!existsSync70(projects))
|
|
86405
86607
|
return [];
|
|
86406
86608
|
const found = [];
|
|
86407
86609
|
const walk2 = (dir) => {
|
|
86408
86610
|
let entries;
|
|
86409
86611
|
try {
|
|
86410
|
-
entries =
|
|
86612
|
+
entries = readdirSync25(dir);
|
|
86411
86613
|
} catch {
|
|
86412
86614
|
return;
|
|
86413
86615
|
}
|
|
86414
86616
|
for (const name of entries) {
|
|
86415
|
-
const full =
|
|
86617
|
+
const full = join72(dir, name);
|
|
86416
86618
|
let st;
|
|
86417
86619
|
try {
|
|
86418
86620
|
st = statSync42(full);
|
|
@@ -86532,18 +86734,18 @@ function registerHandoffCommand(program3) {
|
|
|
86532
86734
|
// src/issues/store.ts
|
|
86533
86735
|
import {
|
|
86534
86736
|
closeSync as closeSync12,
|
|
86535
|
-
existsSync as
|
|
86536
|
-
mkdirSync as
|
|
86737
|
+
existsSync as existsSync71,
|
|
86738
|
+
mkdirSync as mkdirSync37,
|
|
86537
86739
|
openSync as openSync12,
|
|
86538
|
-
readdirSync as
|
|
86539
|
-
readFileSync as
|
|
86540
|
-
renameSync as
|
|
86740
|
+
readdirSync as readdirSync26,
|
|
86741
|
+
readFileSync as readFileSync64,
|
|
86742
|
+
renameSync as renameSync17,
|
|
86541
86743
|
statSync as statSync43,
|
|
86542
86744
|
unlinkSync as unlinkSync15,
|
|
86543
|
-
writeFileSync as
|
|
86745
|
+
writeFileSync as writeFileSync20,
|
|
86544
86746
|
writeSync as writeSync9
|
|
86545
86747
|
} from "node:fs";
|
|
86546
|
-
import { join as
|
|
86748
|
+
import { join as join73 } from "node:path";
|
|
86547
86749
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
86548
86750
|
import { execSync as execSync4 } from "node:child_process";
|
|
86549
86751
|
|
|
@@ -86974,12 +87176,12 @@ function redactedMarker(ruleId) {
|
|
|
86974
87176
|
var ISSUES_FILE = "issues.jsonl";
|
|
86975
87177
|
var ISSUES_LOCK = "issues.lock";
|
|
86976
87178
|
function readAll(stateDir) {
|
|
86977
|
-
const path5 =
|
|
86978
|
-
if (!
|
|
87179
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87180
|
+
if (!existsSync71(path5))
|
|
86979
87181
|
return [];
|
|
86980
87182
|
let raw;
|
|
86981
87183
|
try {
|
|
86982
|
-
raw =
|
|
87184
|
+
raw = readFileSync64(path5, "utf-8");
|
|
86983
87185
|
} catch {
|
|
86984
87186
|
return [];
|
|
86985
87187
|
}
|
|
@@ -87052,7 +87254,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
87052
87254
|
});
|
|
87053
87255
|
}
|
|
87054
87256
|
function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
87055
|
-
if (!
|
|
87257
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87056
87258
|
return 0;
|
|
87057
87259
|
return withLock(stateDir, () => {
|
|
87058
87260
|
const all = readAll(stateDir);
|
|
@@ -87070,7 +87272,7 @@ function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
87070
87272
|
});
|
|
87071
87273
|
}
|
|
87072
87274
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
87073
|
-
if (!
|
|
87275
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87074
87276
|
return 0;
|
|
87075
87277
|
return withLock(stateDir, () => {
|
|
87076
87278
|
const all = readAll(stateDir);
|
|
@@ -87088,7 +87290,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
87088
87290
|
});
|
|
87089
87291
|
}
|
|
87090
87292
|
function prune(stateDir, opts = {}) {
|
|
87091
|
-
if (!
|
|
87293
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87092
87294
|
return 0;
|
|
87093
87295
|
return withLock(stateDir, () => {
|
|
87094
87296
|
const all = readAll(stateDir);
|
|
@@ -87118,24 +87320,24 @@ function prune(stateDir, opts = {}) {
|
|
|
87118
87320
|
});
|
|
87119
87321
|
}
|
|
87120
87322
|
function ensureDir(stateDir) {
|
|
87121
|
-
|
|
87323
|
+
mkdirSync37(stateDir, { recursive: true });
|
|
87122
87324
|
}
|
|
87123
87325
|
function writeAll(stateDir, events) {
|
|
87124
|
-
const path5 =
|
|
87326
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87125
87327
|
sweepOrphanTmpFiles(stateDir);
|
|
87126
87328
|
const tmp = `${path5}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
|
|
87127
87329
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
87128
87330
|
`) + `
|
|
87129
87331
|
`;
|
|
87130
|
-
|
|
87131
|
-
|
|
87332
|
+
writeFileSync20(tmp, body, "utf-8");
|
|
87333
|
+
renameSync17(tmp, path5);
|
|
87132
87334
|
}
|
|
87133
87335
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
87134
87336
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
87135
87337
|
function sweepOrphanTmpFiles(stateDir) {
|
|
87136
87338
|
let entries;
|
|
87137
87339
|
try {
|
|
87138
|
-
entries =
|
|
87340
|
+
entries = readdirSync26(stateDir);
|
|
87139
87341
|
} catch {
|
|
87140
87342
|
return;
|
|
87141
87343
|
}
|
|
@@ -87143,7 +87345,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87143
87345
|
for (const entry of entries) {
|
|
87144
87346
|
if (!entry.startsWith(TMP_PREFIX))
|
|
87145
87347
|
continue;
|
|
87146
|
-
const tmpPath =
|
|
87348
|
+
const tmpPath = join73(stateDir, entry);
|
|
87147
87349
|
try {
|
|
87148
87350
|
const stat = statSync43(tmpPath);
|
|
87149
87351
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -87155,7 +87357,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87155
87357
|
var LOCK_RETRY_MS = 25;
|
|
87156
87358
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
87157
87359
|
function withLock(stateDir, fn) {
|
|
87158
|
-
const lockPath =
|
|
87360
|
+
const lockPath = join73(stateDir, ISSUES_LOCK);
|
|
87159
87361
|
const startedAt = Date.now();
|
|
87160
87362
|
let fd = null;
|
|
87161
87363
|
while (fd === null) {
|
|
@@ -87190,7 +87392,7 @@ function withLock(stateDir, fn) {
|
|
|
87190
87392
|
function tryStealStaleLock(lockPath) {
|
|
87191
87393
|
let pidStr;
|
|
87192
87394
|
try {
|
|
87193
|
-
pidStr =
|
|
87395
|
+
pidStr = readFileSync64(lockPath, "utf-8").trim();
|
|
87194
87396
|
} catch {
|
|
87195
87397
|
return true;
|
|
87196
87398
|
}
|
|
@@ -87438,20 +87640,20 @@ function relTime(deltaMs) {
|
|
|
87438
87640
|
|
|
87439
87641
|
// src/cli/deps.ts
|
|
87440
87642
|
init_source();
|
|
87441
|
-
import { existsSync as
|
|
87643
|
+
import { existsSync as existsSync74 } from "node:fs";
|
|
87442
87644
|
import { homedir as homedir44 } from "node:os";
|
|
87443
|
-
import { join as
|
|
87645
|
+
import { join as join76, resolve as resolve44 } from "node:path";
|
|
87444
87646
|
|
|
87445
87647
|
// src/deps/python.ts
|
|
87446
87648
|
import { createHash as createHash13 } from "node:crypto";
|
|
87447
87649
|
import {
|
|
87448
|
-
existsSync as
|
|
87449
|
-
mkdirSync as
|
|
87450
|
-
readFileSync as
|
|
87650
|
+
existsSync as existsSync72,
|
|
87651
|
+
mkdirSync as mkdirSync38,
|
|
87652
|
+
readFileSync as readFileSync65,
|
|
87451
87653
|
rmSync as rmSync13,
|
|
87452
|
-
writeFileSync as
|
|
87654
|
+
writeFileSync as writeFileSync21
|
|
87453
87655
|
} from "node:fs";
|
|
87454
|
-
import { dirname as
|
|
87656
|
+
import { dirname as dirname25, join as join74 } from "node:path";
|
|
87455
87657
|
import { homedir as homedir42 } from "node:os";
|
|
87456
87658
|
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
87457
87659
|
|
|
@@ -87464,26 +87666,26 @@ class PythonEnvError extends Error {
|
|
|
87464
87666
|
}
|
|
87465
87667
|
}
|
|
87466
87668
|
function defaultPythonCacheRoot() {
|
|
87467
|
-
return
|
|
87669
|
+
return join74(homedir42(), ".switchroom", "deps", "python");
|
|
87468
87670
|
}
|
|
87469
87671
|
function hashFile(path5) {
|
|
87470
|
-
return createHash13("sha256").update(
|
|
87672
|
+
return createHash13("sha256").update(readFileSync65(path5)).digest("hex");
|
|
87471
87673
|
}
|
|
87472
87674
|
function ensurePythonEnv(opts) {
|
|
87473
87675
|
const { skillName, requirementsPath, force = false } = opts;
|
|
87474
87676
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
87475
87677
|
const hostPython = opts.pythonBin ?? "python3";
|
|
87476
|
-
if (!
|
|
87678
|
+
if (!existsSync72(requirementsPath)) {
|
|
87477
87679
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
87478
87680
|
}
|
|
87479
|
-
const venvDir =
|
|
87480
|
-
const stampPath =
|
|
87481
|
-
const binDir =
|
|
87482
|
-
const pythonBin =
|
|
87483
|
-
const pipBin =
|
|
87681
|
+
const venvDir = join74(cacheRoot, skillName);
|
|
87682
|
+
const stampPath = join74(venvDir, ".requirements.sha256");
|
|
87683
|
+
const binDir = join74(venvDir, "bin");
|
|
87684
|
+
const pythonBin = join74(binDir, "python");
|
|
87685
|
+
const pipBin = join74(binDir, "pip");
|
|
87484
87686
|
const targetHash = hashFile(requirementsPath);
|
|
87485
|
-
if (!force &&
|
|
87486
|
-
const existingHash =
|
|
87687
|
+
if (!force && existsSync72(stampPath) && existsSync72(pythonBin)) {
|
|
87688
|
+
const existingHash = readFileSync65(stampPath, "utf8").trim();
|
|
87487
87689
|
if (existingHash === targetHash) {
|
|
87488
87690
|
return {
|
|
87489
87691
|
skillName,
|
|
@@ -87495,10 +87697,10 @@ function ensurePythonEnv(opts) {
|
|
|
87495
87697
|
};
|
|
87496
87698
|
}
|
|
87497
87699
|
}
|
|
87498
|
-
if (
|
|
87700
|
+
if (existsSync72(venvDir)) {
|
|
87499
87701
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
87500
87702
|
}
|
|
87501
|
-
|
|
87703
|
+
mkdirSync38(dirname25(venvDir), { recursive: true });
|
|
87502
87704
|
try {
|
|
87503
87705
|
execFileSync21(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
87504
87706
|
} catch (err) {
|
|
@@ -87517,7 +87719,7 @@ function ensurePythonEnv(opts) {
|
|
|
87517
87719
|
const e = err;
|
|
87518
87720
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
87519
87721
|
}
|
|
87520
|
-
|
|
87722
|
+
writeFileSync21(stampPath, targetHash + `
|
|
87521
87723
|
`);
|
|
87522
87724
|
return {
|
|
87523
87725
|
skillName,
|
|
@@ -87533,13 +87735,13 @@ function ensurePythonEnv(opts) {
|
|
|
87533
87735
|
import { createHash as createHash14 } from "node:crypto";
|
|
87534
87736
|
import {
|
|
87535
87737
|
copyFileSync as copyFileSync10,
|
|
87536
|
-
existsSync as
|
|
87537
|
-
mkdirSync as
|
|
87538
|
-
readFileSync as
|
|
87738
|
+
existsSync as existsSync73,
|
|
87739
|
+
mkdirSync as mkdirSync39,
|
|
87740
|
+
readFileSync as readFileSync66,
|
|
87539
87741
|
rmSync as rmSync14,
|
|
87540
|
-
writeFileSync as
|
|
87742
|
+
writeFileSync as writeFileSync22
|
|
87541
87743
|
} from "node:fs";
|
|
87542
|
-
import { dirname as
|
|
87744
|
+
import { dirname as dirname26, join as join75 } from "node:path";
|
|
87543
87745
|
import { homedir as homedir43 } from "node:os";
|
|
87544
87746
|
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
87545
87747
|
|
|
@@ -87563,23 +87765,23 @@ var LOCKFILES_FOR = {
|
|
|
87563
87765
|
npm: ["package-lock.json"]
|
|
87564
87766
|
};
|
|
87565
87767
|
function defaultNodeCacheRoot() {
|
|
87566
|
-
return
|
|
87768
|
+
return join75(homedir43(), ".switchroom", "deps", "node");
|
|
87567
87769
|
}
|
|
87568
87770
|
function hashDepInputs(packageJsonPath) {
|
|
87569
|
-
const sourceDir =
|
|
87771
|
+
const sourceDir = dirname26(packageJsonPath);
|
|
87570
87772
|
const hasher = createHash14("sha256");
|
|
87571
87773
|
hasher.update(`package.json
|
|
87572
87774
|
`);
|
|
87573
|
-
hasher.update(
|
|
87775
|
+
hasher.update(readFileSync66(packageJsonPath));
|
|
87574
87776
|
for (const lockName of ALL_LOCKFILES) {
|
|
87575
|
-
const lockPath =
|
|
87576
|
-
if (
|
|
87777
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87778
|
+
if (existsSync73(lockPath)) {
|
|
87577
87779
|
hasher.update(`
|
|
87578
87780
|
`);
|
|
87579
87781
|
hasher.update(lockName);
|
|
87580
87782
|
hasher.update(`
|
|
87581
87783
|
`);
|
|
87582
|
-
hasher.update(
|
|
87784
|
+
hasher.update(readFileSync66(lockPath));
|
|
87583
87785
|
}
|
|
87584
87786
|
}
|
|
87585
87787
|
return hasher.digest("hex");
|
|
@@ -87588,17 +87790,17 @@ function ensureNodeEnv(opts) {
|
|
|
87588
87790
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
87589
87791
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
87590
87792
|
const installer = opts.installer ?? "bun";
|
|
87591
|
-
if (!
|
|
87793
|
+
if (!existsSync73(packageJsonPath)) {
|
|
87592
87794
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
87593
87795
|
}
|
|
87594
|
-
const sourceDir =
|
|
87595
|
-
const envDir =
|
|
87596
|
-
const stampPath =
|
|
87597
|
-
const nodeModulesDir =
|
|
87598
|
-
const binDir =
|
|
87796
|
+
const sourceDir = dirname26(packageJsonPath);
|
|
87797
|
+
const envDir = join75(cacheRoot, skillName);
|
|
87798
|
+
const stampPath = join75(envDir, ".package.sha256");
|
|
87799
|
+
const nodeModulesDir = join75(envDir, "node_modules");
|
|
87800
|
+
const binDir = join75(nodeModulesDir, ".bin");
|
|
87599
87801
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
87600
|
-
if (!force &&
|
|
87601
|
-
const existingHash =
|
|
87802
|
+
if (!force && existsSync73(stampPath) && existsSync73(nodeModulesDir)) {
|
|
87803
|
+
const existingHash = readFileSync66(stampPath, "utf8").trim();
|
|
87602
87804
|
if (existingHash === targetHash) {
|
|
87603
87805
|
return {
|
|
87604
87806
|
skillName,
|
|
@@ -87609,16 +87811,16 @@ function ensureNodeEnv(opts) {
|
|
|
87609
87811
|
};
|
|
87610
87812
|
}
|
|
87611
87813
|
}
|
|
87612
|
-
if (
|
|
87814
|
+
if (existsSync73(envDir)) {
|
|
87613
87815
|
rmSync14(envDir, { recursive: true, force: true });
|
|
87614
87816
|
}
|
|
87615
|
-
|
|
87616
|
-
copyFileSync10(packageJsonPath,
|
|
87817
|
+
mkdirSync39(envDir, { recursive: true });
|
|
87818
|
+
copyFileSync10(packageJsonPath, join75(envDir, "package.json"));
|
|
87617
87819
|
let copiedLockfile = false;
|
|
87618
87820
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
87619
|
-
const lockPath =
|
|
87620
|
-
if (
|
|
87621
|
-
copyFileSync10(lockPath,
|
|
87821
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87822
|
+
if (existsSync73(lockPath)) {
|
|
87823
|
+
copyFileSync10(lockPath, join75(envDir, lockName));
|
|
87622
87824
|
copiedLockfile = true;
|
|
87623
87825
|
}
|
|
87624
87826
|
}
|
|
@@ -87634,7 +87836,7 @@ function ensureNodeEnv(opts) {
|
|
|
87634
87836
|
const e = err;
|
|
87635
87837
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
87636
87838
|
}
|
|
87637
|
-
|
|
87839
|
+
writeFileSync22(stampPath, targetHash + `
|
|
87638
87840
|
`);
|
|
87639
87841
|
return {
|
|
87640
87842
|
skillName,
|
|
@@ -87653,22 +87855,22 @@ function registerDepsCommand(program3) {
|
|
|
87653
87855
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
87654
87856
|
deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
|
|
87655
87857
|
const skillsRoot = builtinSkillsRoot();
|
|
87656
|
-
if (!
|
|
87858
|
+
if (!existsSync74(skillsRoot)) {
|
|
87657
87859
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
87658
87860
|
process.exit(1);
|
|
87659
87861
|
}
|
|
87660
|
-
const skillDir =
|
|
87661
|
-
if (!
|
|
87862
|
+
const skillDir = join76(skillsRoot, skill);
|
|
87863
|
+
if (!existsSync74(skillDir)) {
|
|
87662
87864
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
87663
87865
|
process.exit(1);
|
|
87664
87866
|
}
|
|
87665
|
-
const requirementsPath =
|
|
87666
|
-
const packageJsonPath =
|
|
87667
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
87668
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
87867
|
+
const requirementsPath = join76(skillDir, "requirements.txt");
|
|
87868
|
+
const packageJsonPath = join76(skillDir, "package.json");
|
|
87869
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync74(requirementsPath));
|
|
87870
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync74(packageJsonPath));
|
|
87669
87871
|
let did = 0;
|
|
87670
87872
|
if (wantPython) {
|
|
87671
|
-
if (!
|
|
87873
|
+
if (!existsSync74(requirementsPath)) {
|
|
87672
87874
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
87673
87875
|
process.exit(1);
|
|
87674
87876
|
}
|
|
@@ -87692,7 +87894,7 @@ function registerDepsCommand(program3) {
|
|
|
87692
87894
|
}
|
|
87693
87895
|
}
|
|
87694
87896
|
if (wantNode) {
|
|
87695
|
-
if (!
|
|
87897
|
+
if (!existsSync74(packageJsonPath)) {
|
|
87696
87898
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
87697
87899
|
process.exit(1);
|
|
87698
87900
|
}
|
|
@@ -87725,7 +87927,7 @@ function registerDepsCommand(program3) {
|
|
|
87725
87927
|
// src/cli/workspace.ts
|
|
87726
87928
|
init_helpers();
|
|
87727
87929
|
init_loader();
|
|
87728
|
-
import { existsSync as
|
|
87930
|
+
import { existsSync as existsSync75 } from "node:fs";
|
|
87729
87931
|
import { resolve as resolve45, sep as sep3 } from "node:path";
|
|
87730
87932
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
87731
87933
|
|
|
@@ -88502,7 +88704,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88502
88704
|
if (!dir)
|
|
88503
88705
|
return;
|
|
88504
88706
|
const gitDir = resolve45(dir, ".git");
|
|
88505
|
-
if (!
|
|
88707
|
+
if (!existsSync75(gitDir)) {
|
|
88506
88708
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
88507
88709
|
`);
|
|
88508
88710
|
return;
|
|
@@ -88556,7 +88758,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88556
88758
|
if (!dir)
|
|
88557
88759
|
return;
|
|
88558
88760
|
const gitDir = resolve45(dir, ".git");
|
|
88559
|
-
if (!
|
|
88761
|
+
if (!existsSync75(gitDir)) {
|
|
88560
88762
|
process.stdout.write(`Workspace is not a git repository.
|
|
88561
88763
|
`);
|
|
88562
88764
|
return;
|
|
@@ -88581,7 +88783,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
88581
88783
|
const agentsDir = resolveAgentsDir(config);
|
|
88582
88784
|
const agentDir = resolve45(agentsDir, agentName);
|
|
88583
88785
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
88584
|
-
if (!
|
|
88786
|
+
if (!existsSync75(dir)) {
|
|
88585
88787
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
88586
88788
|
`);
|
|
88587
88789
|
return;
|
|
@@ -88617,8 +88819,8 @@ function safeParseInt(value, fallback) {
|
|
|
88617
88819
|
init_helpers();
|
|
88618
88820
|
init_loader();
|
|
88619
88821
|
init_merge();
|
|
88620
|
-
import { copyFileSync as copyFileSync11, existsSync as
|
|
88621
|
-
import { join as
|
|
88822
|
+
import { copyFileSync as copyFileSync11, existsSync as existsSync76, readFileSync as readFileSync67, writeFileSync as writeFileSync23 } from "node:fs";
|
|
88823
|
+
import { join as join77, resolve as resolve46 } from "node:path";
|
|
88622
88824
|
init_scaffold();
|
|
88623
88825
|
init_profiles();
|
|
88624
88826
|
init_schema();
|
|
@@ -88635,7 +88837,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88635
88837
|
const agentsDir = resolveAgentsDir(config);
|
|
88636
88838
|
const agentDir = resolve46(agentsDir, agentName);
|
|
88637
88839
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88638
|
-
if (!
|
|
88840
|
+
if (!existsSync76(workspaceDir)) {
|
|
88639
88841
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
88640
88842
|
process.exit(1);
|
|
88641
88843
|
}
|
|
@@ -88644,7 +88846,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88644
88846
|
profileName,
|
|
88645
88847
|
profilePath,
|
|
88646
88848
|
workspaceDir,
|
|
88647
|
-
soulPath:
|
|
88849
|
+
soulPath: join77(workspaceDir, "SOUL.md"),
|
|
88648
88850
|
soul: merged.soul
|
|
88649
88851
|
};
|
|
88650
88852
|
}
|
|
@@ -88661,11 +88863,11 @@ function registerSoulCommand(program3) {
|
|
|
88661
88863
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
88662
88864
|
if (!t)
|
|
88663
88865
|
return;
|
|
88664
|
-
if (!
|
|
88866
|
+
if (!existsSync76(t.soulPath)) {
|
|
88665
88867
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
88666
88868
|
process.exit(1);
|
|
88667
88869
|
}
|
|
88668
|
-
process.stdout.write(
|
|
88870
|
+
process.stdout.write(readFileSync67(t.soulPath, "utf-8"));
|
|
88669
88871
|
}));
|
|
88670
88872
|
cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
|
|
88671
88873
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -88676,7 +88878,7 @@ function registerSoulCommand(program3) {
|
|
|
88676
88878
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
88677
88879
|
process.exit(1);
|
|
88678
88880
|
}
|
|
88679
|
-
const exists =
|
|
88881
|
+
const exists = existsSync76(t.soulPath);
|
|
88680
88882
|
if (exists && !opts.yes) {
|
|
88681
88883
|
if (!isInteractive()) {
|
|
88682
88884
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -88691,12 +88893,12 @@ function registerSoulCommand(program3) {
|
|
|
88691
88893
|
let backupPath;
|
|
88692
88894
|
if (exists) {
|
|
88693
88895
|
backupPath = `${t.soulPath}.bak`;
|
|
88694
|
-
if (
|
|
88896
|
+
if (existsSync76(backupPath)) {
|
|
88695
88897
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
88696
88898
|
}
|
|
88697
88899
|
copyFileSync11(t.soulPath, backupPath);
|
|
88698
88900
|
}
|
|
88699
|
-
|
|
88901
|
+
writeFileSync23(t.soulPath, content, "utf-8");
|
|
88700
88902
|
if (backupPath) {
|
|
88701
88903
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
88702
88904
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -88710,8 +88912,8 @@ function registerSoulCommand(program3) {
|
|
|
88710
88912
|
// src/cli/debug.ts
|
|
88711
88913
|
init_helpers();
|
|
88712
88914
|
init_loader();
|
|
88713
|
-
import { existsSync as
|
|
88714
|
-
import { resolve as resolve47, join as
|
|
88915
|
+
import { existsSync as existsSync77, readFileSync as readFileSync68, readdirSync as readdirSync27, statSync as statSync44 } from "node:fs";
|
|
88916
|
+
import { resolve as resolve47, join as join78 } from "node:path";
|
|
88715
88917
|
import { createHash as createHash15 } from "node:crypto";
|
|
88716
88918
|
init_merge();
|
|
88717
88919
|
init_hindsight2();
|
|
@@ -88722,11 +88924,11 @@ function estimateTokens(bytes) {
|
|
|
88722
88924
|
return Math.round(bytes / 3.7);
|
|
88723
88925
|
}
|
|
88724
88926
|
function readMcpServerNames(agentDir) {
|
|
88725
|
-
const mcpPath =
|
|
88726
|
-
if (!
|
|
88927
|
+
const mcpPath = join78(agentDir, ".mcp.json");
|
|
88928
|
+
if (!existsSync77(mcpPath))
|
|
88727
88929
|
return [];
|
|
88728
88930
|
try {
|
|
88729
|
-
const parsed = JSON.parse(
|
|
88931
|
+
const parsed = JSON.parse(readFileSync68(mcpPath, "utf-8"));
|
|
88730
88932
|
return Object.keys(parsed.mcpServers ?? {});
|
|
88731
88933
|
} catch {
|
|
88732
88934
|
return null;
|
|
@@ -88736,18 +88938,18 @@ function sha256(content) {
|
|
|
88736
88938
|
return createHash15("sha256").update(content).digest("hex").slice(0, 16);
|
|
88737
88939
|
}
|
|
88738
88940
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
88739
|
-
const projectsDir =
|
|
88740
|
-
if (!
|
|
88941
|
+
const projectsDir = join78(claudeConfigDir, "projects");
|
|
88942
|
+
if (!existsSync77(projectsDir))
|
|
88741
88943
|
return;
|
|
88742
88944
|
try {
|
|
88743
|
-
const entries =
|
|
88945
|
+
const entries = readdirSync27(projectsDir, { withFileTypes: true });
|
|
88744
88946
|
let latest;
|
|
88745
88947
|
for (const entry of entries) {
|
|
88746
88948
|
if (!entry.isDirectory())
|
|
88747
88949
|
continue;
|
|
88748
|
-
const projectPath =
|
|
88749
|
-
const transcriptPath =
|
|
88750
|
-
if (!
|
|
88950
|
+
const projectPath = join78(projectsDir, entry.name);
|
|
88951
|
+
const transcriptPath = join78(projectPath, "transcript.jsonl");
|
|
88952
|
+
if (!existsSync77(transcriptPath))
|
|
88751
88953
|
continue;
|
|
88752
88954
|
const stat3 = statSync44(transcriptPath);
|
|
88753
88955
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -88761,7 +88963,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
88761
88963
|
}
|
|
88762
88964
|
function extractLatestUserMessage(transcriptPath) {
|
|
88763
88965
|
try {
|
|
88764
|
-
const content =
|
|
88966
|
+
const content = readFileSync68(transcriptPath, "utf-8");
|
|
88765
88967
|
const lines = content.trim().split(`
|
|
88766
88968
|
`).filter(Boolean);
|
|
88767
88969
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -88810,16 +89012,16 @@ function registerDebugCommand(program3) {
|
|
|
88810
89012
|
}
|
|
88811
89013
|
const agentsDir = resolveAgentsDir(config);
|
|
88812
89014
|
const agentDir = resolve47(agentsDir, agentName);
|
|
88813
|
-
if (!
|
|
89015
|
+
if (!existsSync77(agentDir)) {
|
|
88814
89016
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
88815
89017
|
process.exit(1);
|
|
88816
89018
|
}
|
|
88817
89019
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88818
|
-
const claudeConfigDir =
|
|
88819
|
-
const claudeMdPath =
|
|
88820
|
-
const soulMdPath =
|
|
88821
|
-
const workspaceSoulMdPath =
|
|
88822
|
-
const handoffPath =
|
|
89020
|
+
const claudeConfigDir = join78(agentDir, ".claude");
|
|
89021
|
+
const claudeMdPath = join78(agentDir, "CLAUDE.md");
|
|
89022
|
+
const soulMdPath = join78(agentDir, "SOUL.md");
|
|
89023
|
+
const workspaceSoulMdPath = join78(workspaceDir, "SOUL.md");
|
|
89024
|
+
const handoffPath = join78(agentDir, ".handoff.md");
|
|
88823
89025
|
const lastN = parseInt(opts.last, 10);
|
|
88824
89026
|
if (isNaN(lastN) || lastN < 1) {
|
|
88825
89027
|
console.error("--last must be a positive integer");
|
|
@@ -88865,7 +89067,7 @@ function registerDebugCommand(program3) {
|
|
|
88865
89067
|
}
|
|
88866
89068
|
console.log(`=== Append System Prompt (per-session) ===
|
|
88867
89069
|
`);
|
|
88868
|
-
const handoffContent =
|
|
89070
|
+
const handoffContent = existsSync77(handoffPath) ? readFileSync68(handoffPath, "utf-8") : "";
|
|
88869
89071
|
if (handoffContent.trim().length > 0) {
|
|
88870
89072
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
88871
89073
|
console.log(handoffContent);
|
|
@@ -88876,7 +89078,7 @@ function registerDebugCommand(program3) {
|
|
|
88876
89078
|
}
|
|
88877
89079
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
88878
89080
|
`);
|
|
88879
|
-
const claudeMdContent =
|
|
89081
|
+
const claudeMdContent = existsSync77(claudeMdPath) ? readFileSync68(claudeMdPath, "utf-8") : "";
|
|
88880
89082
|
if (claudeMdContent.trim().length > 0) {
|
|
88881
89083
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
88882
89084
|
console.log(claudeMdContent);
|
|
@@ -88887,7 +89089,7 @@ function registerDebugCommand(program3) {
|
|
|
88887
89089
|
}
|
|
88888
89090
|
console.log(`=== Persona (SOUL.md) ===
|
|
88889
89091
|
`);
|
|
88890
|
-
const soulMdContent =
|
|
89092
|
+
const soulMdContent = existsSync77(soulMdPath) ? readFileSync68(soulMdPath, "utf-8") : existsSync77(workspaceSoulMdPath) ? readFileSync68(workspaceSoulMdPath, "utf-8") : "";
|
|
88891
89093
|
if (soulMdContent.trim().length > 0) {
|
|
88892
89094
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
88893
89095
|
console.log(soulMdContent);
|
|
@@ -88948,11 +89150,11 @@ function registerDebugCommand(program3) {
|
|
|
88948
89150
|
const soulMdBytes = soulMdContent.length;
|
|
88949
89151
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
88950
89152
|
const userBytes = userMessage?.text.length ?? 0;
|
|
88951
|
-
const fleetDir =
|
|
88952
|
-
const fleetInvPath =
|
|
88953
|
-
const fleetClaudePath =
|
|
88954
|
-
const fleetInvBytes =
|
|
88955
|
-
const fleetClaudeBytes =
|
|
89153
|
+
const fleetDir = join78(agentsDir, "..", "fleet");
|
|
89154
|
+
const fleetInvPath = join78(fleetDir, "switchroom-invariants.md");
|
|
89155
|
+
const fleetClaudePath = join78(fleetDir, "CLAUDE.md");
|
|
89156
|
+
const fleetInvBytes = existsSync77(fleetInvPath) ? readFileSync68(fleetInvPath, "utf-8").length : 0;
|
|
89157
|
+
const fleetClaudeBytes = existsSync77(fleetClaudePath) ? readFileSync68(fleetClaudePath, "utf-8").length : 0;
|
|
88956
89158
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
88957
89159
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
88958
89160
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -88985,44 +89187,44 @@ init_source();
|
|
|
88985
89187
|
|
|
88986
89188
|
// src/worktree/claim.ts
|
|
88987
89189
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
88988
|
-
import { closeSync as closeSync13, mkdirSync as
|
|
88989
|
-
import { join as
|
|
89190
|
+
import { closeSync as closeSync13, mkdirSync as mkdirSync41, openSync as openSync13, existsSync as existsSync79, unlinkSync as unlinkSync17 } from "node:fs";
|
|
89191
|
+
import { join as join80, resolve as resolve49 } from "node:path";
|
|
88990
89192
|
import { homedir as homedir46 } from "node:os";
|
|
88991
89193
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
88992
89194
|
|
|
88993
89195
|
// src/worktree/registry.ts
|
|
88994
89196
|
import {
|
|
88995
|
-
mkdirSync as
|
|
88996
|
-
writeFileSync as
|
|
88997
|
-
readFileSync as
|
|
88998
|
-
readdirSync as
|
|
89197
|
+
mkdirSync as mkdirSync40,
|
|
89198
|
+
writeFileSync as writeFileSync24,
|
|
89199
|
+
readFileSync as readFileSync69,
|
|
89200
|
+
readdirSync as readdirSync28,
|
|
88999
89201
|
unlinkSync as unlinkSync16,
|
|
89000
|
-
existsSync as
|
|
89001
|
-
renameSync as
|
|
89202
|
+
existsSync as existsSync78,
|
|
89203
|
+
renameSync as renameSync18
|
|
89002
89204
|
} from "node:fs";
|
|
89003
|
-
import { join as
|
|
89205
|
+
import { join as join79, resolve as resolve48 } from "node:path";
|
|
89004
89206
|
import { homedir as homedir45 } from "node:os";
|
|
89005
89207
|
function registryDir() {
|
|
89006
|
-
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
89208
|
+
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ?? join79(homedir45(), ".switchroom", "worktrees"));
|
|
89007
89209
|
}
|
|
89008
89210
|
function recordPath(id) {
|
|
89009
|
-
return
|
|
89211
|
+
return join79(registryDir(), `${id}.json`);
|
|
89010
89212
|
}
|
|
89011
89213
|
function ensureDir2() {
|
|
89012
|
-
|
|
89214
|
+
mkdirSync40(registryDir(), { recursive: true });
|
|
89013
89215
|
}
|
|
89014
89216
|
function writeRecord(record2) {
|
|
89015
89217
|
ensureDir2();
|
|
89016
89218
|
const target = recordPath(record2.id);
|
|
89017
89219
|
const tmp = `${target}.tmp${process.pid}`;
|
|
89018
|
-
|
|
89220
|
+
writeFileSync24(tmp, JSON.stringify(record2, null, 2) + `
|
|
89019
89221
|
`, { mode: 384 });
|
|
89020
|
-
|
|
89222
|
+
renameSync18(tmp, target);
|
|
89021
89223
|
}
|
|
89022
89224
|
function readRecord(id) {
|
|
89023
89225
|
const path8 = recordPath(id);
|
|
89024
89226
|
try {
|
|
89025
|
-
const raw =
|
|
89227
|
+
const raw = readFileSync69(path8, "utf8");
|
|
89026
89228
|
return JSON.parse(raw);
|
|
89027
89229
|
} catch {
|
|
89028
89230
|
return null;
|
|
@@ -89038,7 +89240,7 @@ function listRecords() {
|
|
|
89038
89240
|
ensureDir2();
|
|
89039
89241
|
const dir = registryDir();
|
|
89040
89242
|
const records = [];
|
|
89041
|
-
for (const entry of
|
|
89243
|
+
for (const entry of readdirSync28(dir)) {
|
|
89042
89244
|
if (!entry.endsWith(".json"))
|
|
89043
89245
|
continue;
|
|
89044
89246
|
const id = entry.slice(0, -5);
|
|
@@ -89055,9 +89257,9 @@ function countByRepo(repoPath) {
|
|
|
89055
89257
|
// src/worktree/claim.ts
|
|
89056
89258
|
function acquireRepoLock(repoPath) {
|
|
89057
89259
|
const lockDir = registryDir();
|
|
89058
|
-
|
|
89260
|
+
mkdirSync41(lockDir, { recursive: true });
|
|
89059
89261
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
89060
|
-
const lockPath =
|
|
89262
|
+
const lockPath = join80(lockDir, `.lock-${lockName}`);
|
|
89061
89263
|
const deadline = Date.now() + 5000;
|
|
89062
89264
|
let fd = null;
|
|
89063
89265
|
while (fd === null) {
|
|
@@ -89084,7 +89286,7 @@ function acquireRepoLock(repoPath) {
|
|
|
89084
89286
|
}
|
|
89085
89287
|
var DEFAULT_CONCURRENCY = 5;
|
|
89086
89288
|
function worktreesBaseDir() {
|
|
89087
|
-
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
89289
|
+
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ?? join80(homedir46(), ".switchroom", "worktree-checkouts"));
|
|
89088
89290
|
}
|
|
89089
89291
|
function shortId() {
|
|
89090
89292
|
return randomBytes13(4).toString("hex");
|
|
@@ -89106,12 +89308,12 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
89106
89308
|
}
|
|
89107
89309
|
function expandHome(p) {
|
|
89108
89310
|
if (p.startsWith("~/"))
|
|
89109
|
-
return
|
|
89311
|
+
return join80(homedir46(), p.slice(2));
|
|
89110
89312
|
return p;
|
|
89111
89313
|
}
|
|
89112
89314
|
async function claimWorktree(input, codeRepos) {
|
|
89113
89315
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
89114
|
-
if (!
|
|
89316
|
+
if (!existsSync79(repoPath)) {
|
|
89115
89317
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
89116
89318
|
}
|
|
89117
89319
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -89133,8 +89335,8 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89133
89335
|
const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
|
|
89134
89336
|
branch = `task/${taskSuffix}-${id}`;
|
|
89135
89337
|
const baseDir = worktreesBaseDir();
|
|
89136
|
-
|
|
89137
|
-
worktreePath =
|
|
89338
|
+
mkdirSync41(baseDir, { recursive: true });
|
|
89339
|
+
worktreePath = join80(baseDir, `${id}-${taskSuffix}`);
|
|
89138
89340
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
89139
89341
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
89140
89342
|
const now = new Date().toISOString();
|
|
@@ -89167,7 +89369,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89167
89369
|
|
|
89168
89370
|
// src/worktree/release.ts
|
|
89169
89371
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
89170
|
-
import { existsSync as
|
|
89372
|
+
import { existsSync as existsSync80 } from "node:fs";
|
|
89171
89373
|
function releaseWorktree(input) {
|
|
89172
89374
|
const { id } = input;
|
|
89173
89375
|
const record2 = readRecord(id);
|
|
@@ -89175,7 +89377,7 @@ function releaseWorktree(input) {
|
|
|
89175
89377
|
return { released: true };
|
|
89176
89378
|
}
|
|
89177
89379
|
let gitSuccess = true;
|
|
89178
|
-
if (
|
|
89380
|
+
if (existsSync80(record2.path)) {
|
|
89179
89381
|
try {
|
|
89180
89382
|
execFileSync24("git", ["worktree", "remove", "--force", record2.path], {
|
|
89181
89383
|
cwd: record2.repo,
|
|
@@ -89214,7 +89416,7 @@ function listWorktrees() {
|
|
|
89214
89416
|
|
|
89215
89417
|
// src/worktree/reaper.ts
|
|
89216
89418
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
89217
|
-
import { existsSync as
|
|
89419
|
+
import { existsSync as existsSync81 } from "node:fs";
|
|
89218
89420
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
89219
89421
|
function reapSkipReasonText(action) {
|
|
89220
89422
|
switch (action) {
|
|
@@ -89275,7 +89477,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
89275
89477
|
const plan = [];
|
|
89276
89478
|
for (const record2 of listRecords()) {
|
|
89277
89479
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
89278
|
-
const worktreeExists =
|
|
89480
|
+
const worktreeExists = existsSync81(record2.path);
|
|
89279
89481
|
if (!worktreeExists) {
|
|
89280
89482
|
plan.push({
|
|
89281
89483
|
record: record2,
|
|
@@ -89356,16 +89558,16 @@ function runReaper(nowMs, deps = {}) {
|
|
|
89356
89558
|
// src/worktree/gc.ts
|
|
89357
89559
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
89358
89560
|
import {
|
|
89359
|
-
existsSync as
|
|
89360
|
-
readFileSync as
|
|
89361
|
-
readdirSync as
|
|
89561
|
+
existsSync as existsSync82,
|
|
89562
|
+
readFileSync as readFileSync70,
|
|
89563
|
+
readdirSync as readdirSync29,
|
|
89362
89564
|
statSync as statSync45,
|
|
89363
|
-
renameSync as
|
|
89364
|
-
mkdirSync as
|
|
89565
|
+
renameSync as renameSync19,
|
|
89566
|
+
mkdirSync as mkdirSync42,
|
|
89365
89567
|
rmSync as rmSync15
|
|
89366
89568
|
} from "node:fs";
|
|
89367
89569
|
import { homedir as homedir47 } from "node:os";
|
|
89368
|
-
import { join as
|
|
89570
|
+
import { join as join81, resolve as resolve50 } from "node:path";
|
|
89369
89571
|
function parseGitdirPointer(dotGitFileContents) {
|
|
89370
89572
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
89371
89573
|
return m ? m[1] : null;
|
|
@@ -89480,17 +89682,17 @@ function defaultPrSignal(repo, branch, exec) {
|
|
|
89480
89682
|
}
|
|
89481
89683
|
}
|
|
89482
89684
|
function trashRoot() {
|
|
89483
|
-
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ??
|
|
89685
|
+
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ?? join81(homedir47(), ".switchroom", "worktree-gc-trash"));
|
|
89484
89686
|
}
|
|
89485
89687
|
function planGc(roots, deps = {}) {
|
|
89486
|
-
const exists = deps.existsSync ??
|
|
89487
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89488
|
-
const readFile4 = deps.readFile ?? ((p) =>
|
|
89688
|
+
const exists = deps.existsSync ?? existsSync82;
|
|
89689
|
+
const readDir = deps.readDir ?? ((p) => readdirSync29(p));
|
|
89690
|
+
const readFile4 = deps.readFile ?? ((p) => readFileSync70(p, "utf8"));
|
|
89489
89691
|
const stat3 = deps.stat ?? ((p) => statSync45(p));
|
|
89490
89692
|
const exec = deps.exec ?? defaultExec;
|
|
89491
89693
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
89492
89694
|
const stamp = deps.dateStamp ?? "undated";
|
|
89493
|
-
const trash =
|
|
89695
|
+
const trash = join81(trashRoot(), stamp);
|
|
89494
89696
|
let claimed;
|
|
89495
89697
|
try {
|
|
89496
89698
|
claimed = new Set(listRecords().map((r) => resolve50(r.path)));
|
|
@@ -89526,10 +89728,10 @@ function planGc(roots, deps = {}) {
|
|
|
89526
89728
|
continue;
|
|
89527
89729
|
}
|
|
89528
89730
|
for (const name of entries) {
|
|
89529
|
-
const dir =
|
|
89731
|
+
const dir = join81(root, name);
|
|
89530
89732
|
if (isEphemeralPath(dir))
|
|
89531
89733
|
continue;
|
|
89532
|
-
const dotGit =
|
|
89734
|
+
const dotGit = join81(dir, ".git");
|
|
89533
89735
|
if (!exists(dotGit))
|
|
89534
89736
|
continue;
|
|
89535
89737
|
let st;
|
|
@@ -89558,7 +89760,7 @@ function planGc(roots, deps = {}) {
|
|
|
89558
89760
|
ownerRepos.add(repoRoot);
|
|
89559
89761
|
if (exists(ptr))
|
|
89560
89762
|
continue;
|
|
89561
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
89763
|
+
orphans.push({ dir, owner: repoRoot, dest: join81(trash, name) });
|
|
89562
89764
|
}
|
|
89563
89765
|
}
|
|
89564
89766
|
const registered = [];
|
|
@@ -89613,10 +89815,10 @@ function planGc(roots, deps = {}) {
|
|
|
89613
89815
|
}
|
|
89614
89816
|
function applyGc(plan, deps = {}) {
|
|
89615
89817
|
const exec = deps.exec ?? defaultExec;
|
|
89616
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
89818
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync42(p, { recursive: true }));
|
|
89617
89819
|
const move = deps.move ?? ((src, dest) => {
|
|
89618
89820
|
try {
|
|
89619
|
-
|
|
89821
|
+
renameSync19(src, dest);
|
|
89620
89822
|
} catch {
|
|
89621
89823
|
exec("mv", [src, dest]);
|
|
89622
89824
|
}
|
|
@@ -89662,14 +89864,14 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
89662
89864
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
89663
89865
|
}
|
|
89664
89866
|
function listTrashEntries(nowMs, deps = {}) {
|
|
89665
|
-
const exists = deps.existsSync ??
|
|
89666
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89867
|
+
const exists = deps.existsSync ?? existsSync82;
|
|
89868
|
+
const readDir = deps.readDir ?? ((p) => readdirSync29(p));
|
|
89667
89869
|
const root = trashRoot();
|
|
89668
89870
|
if (!exists(root))
|
|
89669
89871
|
return [];
|
|
89670
89872
|
const out = [];
|
|
89671
89873
|
for (const stamp of readDir(root)) {
|
|
89672
|
-
const stampDir =
|
|
89874
|
+
const stampDir = join81(root, stamp);
|
|
89673
89875
|
let names;
|
|
89674
89876
|
try {
|
|
89675
89877
|
names = readDir(stampDir);
|
|
@@ -89677,7 +89879,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
89677
89879
|
continue;
|
|
89678
89880
|
}
|
|
89679
89881
|
for (const name of names) {
|
|
89680
|
-
const p =
|
|
89882
|
+
const p = join81(stampDir, name);
|
|
89681
89883
|
let mtimeMs = nowMs;
|
|
89682
89884
|
try {
|
|
89683
89885
|
mtimeMs = statSync45(p).mtimeMs;
|
|
@@ -89701,7 +89903,7 @@ function purgeTrash(paths) {
|
|
|
89701
89903
|
return { deleted, errors: errors2 };
|
|
89702
89904
|
}
|
|
89703
89905
|
function defaultRoots() {
|
|
89704
|
-
return [
|
|
89906
|
+
return [join81(homedir47(), "code")];
|
|
89705
89907
|
}
|
|
89706
89908
|
|
|
89707
89909
|
// src/cli/worktree.ts
|
|
@@ -89924,12 +90126,12 @@ init_drive();
|
|
|
89924
90126
|
init_scaffold_integration();
|
|
89925
90127
|
import {
|
|
89926
90128
|
chmodSync as chmodSync11,
|
|
89927
|
-
mkdirSync as
|
|
89928
|
-
readdirSync as
|
|
90129
|
+
mkdirSync as mkdirSync43,
|
|
90130
|
+
readdirSync as readdirSync30,
|
|
89929
90131
|
rmSync as rmSync16,
|
|
89930
|
-
writeFileSync as
|
|
90132
|
+
writeFileSync as writeFileSync25
|
|
89931
90133
|
} from "node:fs";
|
|
89932
|
-
import { join as
|
|
90134
|
+
import { join as join82 } from "node:path";
|
|
89933
90135
|
function encodeCredentialsFilename(email) {
|
|
89934
90136
|
const SAFE = new Set([
|
|
89935
90137
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -90119,17 +90321,17 @@ function resolveCredentialsDir(env2) {
|
|
|
90119
90321
|
if (explicit && explicit.length > 0)
|
|
90120
90322
|
return explicit;
|
|
90121
90323
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
90122
|
-
return
|
|
90324
|
+
return join82(stateBase, "google-workspace-mcp", "credentials");
|
|
90123
90325
|
}
|
|
90124
90326
|
function writeSeedFile(dir, email, seed) {
|
|
90125
|
-
|
|
90327
|
+
mkdirSync43(dir, { recursive: true, mode: 448 });
|
|
90126
90328
|
chmodSync11(dir, 448);
|
|
90127
|
-
for (const name of
|
|
90128
|
-
rmSync16(
|
|
90329
|
+
for (const name of readdirSync30(dir)) {
|
|
90330
|
+
rmSync16(join82(dir, name), { force: true, recursive: true });
|
|
90129
90331
|
}
|
|
90130
90332
|
const filename = encodeCredentialsFilename(email);
|
|
90131
|
-
const filePath =
|
|
90132
|
-
|
|
90333
|
+
const filePath = join82(dir, filename);
|
|
90334
|
+
writeFileSync25(filePath, JSON.stringify(seed), { mode: 384 });
|
|
90133
90335
|
chmodSync11(filePath, 384);
|
|
90134
90336
|
return filePath;
|
|
90135
90337
|
}
|
|
@@ -90287,8 +90489,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
90287
90489
|
// src/cli/m365-mcp-launcher.ts
|
|
90288
90490
|
init_scaffold_integration();
|
|
90289
90491
|
import { spawn as spawn5 } from "node:child_process";
|
|
90290
|
-
import { writeFileSync as
|
|
90291
|
-
import { dirname as
|
|
90492
|
+
import { writeFileSync as writeFileSync26, mkdirSync as mkdirSync44 } from "node:fs";
|
|
90493
|
+
import { dirname as dirname27, join as join83 } from "node:path";
|
|
90292
90494
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
90293
90495
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
90294
90496
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -90324,8 +90526,8 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
90324
90526
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
90325
90527
|
const path8 = heartbeatPath(agentName, account);
|
|
90326
90528
|
try {
|
|
90327
|
-
|
|
90328
|
-
|
|
90529
|
+
mkdirSync44(dirname27(path8), { recursive: true });
|
|
90530
|
+
writeFileSync26(path8, JSON.stringify(data, null, 2), { mode: 420 });
|
|
90329
90531
|
} catch {}
|
|
90330
90532
|
}
|
|
90331
90533
|
function heartbeatPath(agentName, account) {
|
|
@@ -90333,7 +90535,7 @@ function heartbeatPath(agentName, account) {
|
|
|
90333
90535
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
90334
90536
|
if (override) {
|
|
90335
90537
|
const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
|
|
90336
|
-
return
|
|
90538
|
+
return join83(override, `${base}.heartbeat.json`);
|
|
90337
90539
|
}
|
|
90338
90540
|
return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
|
|
90339
90541
|
}
|
|
@@ -90565,8 +90767,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
90565
90767
|
// src/cli/notion-mcp-launcher.ts
|
|
90566
90768
|
init_scaffold_integration();
|
|
90567
90769
|
import { spawn as spawn6 } from "node:child_process";
|
|
90568
|
-
import { existsSync as
|
|
90569
|
-
import { dirname as
|
|
90770
|
+
import { existsSync as existsSync83, mkdirSync as mkdirSync45, writeFileSync as writeFileSync27 } from "node:fs";
|
|
90771
|
+
import { dirname as dirname28 } from "node:path";
|
|
90570
90772
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
90571
90773
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
90572
90774
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -90576,10 +90778,10 @@ function buildNotionMcpArgs(opts) {
|
|
|
90576
90778
|
}
|
|
90577
90779
|
function defaultWriteHeartbeat(path8, contents) {
|
|
90578
90780
|
try {
|
|
90579
|
-
const dir =
|
|
90580
|
-
if (!
|
|
90581
|
-
|
|
90582
|
-
|
|
90781
|
+
const dir = dirname28(path8);
|
|
90782
|
+
if (!existsSync83(dir))
|
|
90783
|
+
mkdirSync45(dir, { recursive: true });
|
|
90784
|
+
writeFileSync27(path8, contents);
|
|
90583
90785
|
} catch {}
|
|
90584
90786
|
}
|
|
90585
90787
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -90689,9 +90891,9 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
90689
90891
|
|
|
90690
90892
|
// src/cli/hindsight-mcp-shim.ts
|
|
90691
90893
|
init_hindsight();
|
|
90692
|
-
import { mkdirSync as
|
|
90894
|
+
import { mkdirSync as mkdirSync46, readFileSync as readFileSync71, renameSync as renameSync20, writeFileSync as writeFileSync28 } from "node:fs";
|
|
90693
90895
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
90694
|
-
import { join as
|
|
90896
|
+
import { join as join84 } from "node:path";
|
|
90695
90897
|
import { createInterface as createInterface6 } from "node:readline";
|
|
90696
90898
|
var SHIM_SUPPORTED_PROTOCOL_VERSIONS = [
|
|
90697
90899
|
"2025-06-18",
|
|
@@ -90914,22 +91116,22 @@ class HindsightShim {
|
|
|
90914
91116
|
`));
|
|
90915
91117
|
}
|
|
90916
91118
|
get cachePath() {
|
|
90917
|
-
return
|
|
91119
|
+
return join84(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
|
|
90918
91120
|
}
|
|
90919
91121
|
writeCache(result) {
|
|
90920
91122
|
try {
|
|
90921
|
-
|
|
90922
|
-
const tmp =
|
|
90923
|
-
|
|
91123
|
+
mkdirSync46(this.opts.cacheDir, { recursive: true });
|
|
91124
|
+
const tmp = join84(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
91125
|
+
writeFileSync28(tmp, JSON.stringify(result, null, 2) + `
|
|
90924
91126
|
`);
|
|
90925
|
-
|
|
91127
|
+
renameSync20(tmp, this.cachePath);
|
|
90926
91128
|
} catch (err) {
|
|
90927
91129
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
90928
91130
|
}
|
|
90929
91131
|
}
|
|
90930
91132
|
readCache() {
|
|
90931
91133
|
try {
|
|
90932
|
-
const parsed = JSON.parse(
|
|
91134
|
+
const parsed = JSON.parse(readFileSync71(this.cachePath, "utf-8"));
|
|
90933
91135
|
if (Array.isArray(parsed.tools))
|
|
90934
91136
|
return parsed;
|
|
90935
91137
|
return null;
|
|
@@ -91092,7 +91294,7 @@ function resolveShimOptionsFromEnv(env2) {
|
|
|
91092
91294
|
return {
|
|
91093
91295
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
91094
91296
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
91095
|
-
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR ||
|
|
91297
|
+
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join84(home2, ".hindsight-shim")
|
|
91096
91298
|
};
|
|
91097
91299
|
}
|
|
91098
91300
|
function registerHindsightMcpShimCommand(program3) {
|
|
@@ -91105,7 +91307,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
91105
91307
|
|
|
91106
91308
|
// src/cli/deliver-file.ts
|
|
91107
91309
|
init_client2();
|
|
91108
|
-
import { readFileSync as
|
|
91310
|
+
import { readFileSync as readFileSync72, statSync as statSync46 } from "node:fs";
|
|
91109
91311
|
import { basename as basename11 } from "node:path";
|
|
91110
91312
|
|
|
91111
91313
|
// src/delivery/onedrive.ts
|
|
@@ -91445,7 +91647,7 @@ async function defaultResolveProvider() {
|
|
|
91445
91647
|
async function runDeliverFile(localPath, deps = {}) {
|
|
91446
91648
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
91447
91649
|
const sizeOf = deps.fileSize ?? ((p) => statSync46(p).size);
|
|
91448
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
91650
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync72(p)));
|
|
91449
91651
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
91450
91652
|
let size;
|
|
91451
91653
|
try {
|
|
@@ -91766,8 +91968,8 @@ function runRedactStdin() {
|
|
|
91766
91968
|
}
|
|
91767
91969
|
|
|
91768
91970
|
// src/cli/status-ask.ts
|
|
91769
|
-
import { readFileSync as
|
|
91770
|
-
import { join as
|
|
91971
|
+
import { readFileSync as readFileSync76, existsSync as existsSync88, readdirSync as readdirSync32 } from "node:fs";
|
|
91972
|
+
import { join as join89 } from "node:path";
|
|
91771
91973
|
import { homedir as homedir50 } from "node:os";
|
|
91772
91974
|
|
|
91773
91975
|
// src/status-ask/report.ts
|
|
@@ -92042,7 +92244,7 @@ function runReport(opts) {
|
|
|
92042
92244
|
for (const src of sources) {
|
|
92043
92245
|
let content;
|
|
92044
92246
|
try {
|
|
92045
|
-
content =
|
|
92247
|
+
content = readFileSync76(src.path, "utf-8");
|
|
92046
92248
|
} catch (err) {
|
|
92047
92249
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
92048
92250
|
`);
|
|
@@ -92089,7 +92291,7 @@ function runReport(opts) {
|
|
|
92089
92291
|
function resolveSources(explicitPath) {
|
|
92090
92292
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
92091
92293
|
const trimmed = explicitPath.trim();
|
|
92092
|
-
if (!
|
|
92294
|
+
if (!existsSync88(trimmed)) {
|
|
92093
92295
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
92094
92296
|
`);
|
|
92095
92297
|
process.exit(1);
|
|
@@ -92103,20 +92305,20 @@ function resolveSources(explicitPath) {
|
|
|
92103
92305
|
const config = loadConfig();
|
|
92104
92306
|
agentsDir = resolveAgentsDir(config);
|
|
92105
92307
|
} catch {
|
|
92106
|
-
agentsDir =
|
|
92308
|
+
agentsDir = join89(homedir50(), ".switchroom", "agents");
|
|
92107
92309
|
}
|
|
92108
|
-
if (!
|
|
92310
|
+
if (!existsSync88(agentsDir))
|
|
92109
92311
|
return [];
|
|
92110
92312
|
const sources = [];
|
|
92111
92313
|
let entries;
|
|
92112
92314
|
try {
|
|
92113
|
-
entries =
|
|
92315
|
+
entries = readdirSync32(agentsDir);
|
|
92114
92316
|
} catch {
|
|
92115
92317
|
return [];
|
|
92116
92318
|
}
|
|
92117
92319
|
for (const name of entries) {
|
|
92118
|
-
const path9 =
|
|
92119
|
-
if (
|
|
92320
|
+
const path9 = join89(agentsDir, name, "runtime-metrics.jsonl");
|
|
92321
|
+
if (existsSync88(path9)) {
|
|
92120
92322
|
sources.push({ path: path9, agent: name });
|
|
92121
92323
|
}
|
|
92122
92324
|
}
|
|
@@ -92146,45 +92348,45 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
92146
92348
|
init_paths();
|
|
92147
92349
|
import {
|
|
92148
92350
|
closeSync as closeSync14,
|
|
92149
|
-
existsSync as
|
|
92351
|
+
existsSync as existsSync89,
|
|
92150
92352
|
fsyncSync as fsyncSync8,
|
|
92151
|
-
mkdirSync as
|
|
92353
|
+
mkdirSync as mkdirSync51,
|
|
92152
92354
|
openSync as openSync14,
|
|
92153
|
-
readdirSync as
|
|
92154
|
-
readFileSync as
|
|
92155
|
-
renameSync as
|
|
92355
|
+
readdirSync as readdirSync33,
|
|
92356
|
+
readFileSync as readFileSync77,
|
|
92357
|
+
renameSync as renameSync22,
|
|
92156
92358
|
statSync as statSync48,
|
|
92157
92359
|
unlinkSync as unlinkSync18,
|
|
92158
92360
|
writeSync as writeSync10
|
|
92159
92361
|
} from "node:fs";
|
|
92160
|
-
import { join as
|
|
92362
|
+
import { join as join90, resolve as resolve53 } from "node:path";
|
|
92161
92363
|
var STAGING_SUBDIR = ".staging";
|
|
92162
92364
|
function overlayPathsFor(agent, opts = {}) {
|
|
92163
92365
|
const base = opts.root ? resolve53(opts.root, agent) : resolve53(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
92164
|
-
const scheduleDir =
|
|
92165
|
-
const scheduleStagingDir =
|
|
92166
|
-
const skillsDir =
|
|
92167
|
-
const skillsStagingDir =
|
|
92366
|
+
const scheduleDir = join90(base, "schedule.d");
|
|
92367
|
+
const scheduleStagingDir = join90(scheduleDir, STAGING_SUBDIR);
|
|
92368
|
+
const skillsDir = join90(base, "skills.d");
|
|
92369
|
+
const skillsStagingDir = join90(skillsDir, STAGING_SUBDIR);
|
|
92168
92370
|
return {
|
|
92169
92371
|
agentRoot: base,
|
|
92170
92372
|
scheduleDir,
|
|
92171
92373
|
scheduleStagingDir,
|
|
92172
92374
|
skillsDir,
|
|
92173
92375
|
skillsStagingDir,
|
|
92174
|
-
lockPath:
|
|
92376
|
+
lockPath: join90(base, ".lock"),
|
|
92175
92377
|
stagingDir: scheduleStagingDir
|
|
92176
92378
|
};
|
|
92177
92379
|
}
|
|
92178
92380
|
function ensureDirs(paths) {
|
|
92179
|
-
|
|
92180
|
-
|
|
92381
|
+
mkdirSync51(paths.scheduleDir, { recursive: true });
|
|
92382
|
+
mkdirSync51(paths.scheduleStagingDir, { recursive: true });
|
|
92181
92383
|
}
|
|
92182
92384
|
function ensureSkillsDirs(paths) {
|
|
92183
|
-
|
|
92184
|
-
|
|
92385
|
+
mkdirSync51(paths.skillsDir, { recursive: true });
|
|
92386
|
+
mkdirSync51(paths.skillsStagingDir, { recursive: true });
|
|
92185
92387
|
}
|
|
92186
92388
|
function withAgentLock(paths, fn) {
|
|
92187
|
-
|
|
92389
|
+
mkdirSync51(paths.agentRoot, { recursive: true });
|
|
92188
92390
|
const start = Date.now();
|
|
92189
92391
|
const TIMEOUT_MS = 5000;
|
|
92190
92392
|
let fd = null;
|
|
@@ -92225,8 +92427,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92225
92427
|
const paths = overlayPathsFor(agent, opts);
|
|
92226
92428
|
return withAgentLock(paths, () => {
|
|
92227
92429
|
ensureDirs(paths);
|
|
92228
|
-
const stagingPath =
|
|
92229
|
-
const finalPath =
|
|
92430
|
+
const stagingPath = join90(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
92431
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92230
92432
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92231
92433
|
try {
|
|
92232
92434
|
writeSync10(fd, yamlText);
|
|
@@ -92234,7 +92436,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92234
92436
|
} finally {
|
|
92235
92437
|
closeSync14(fd);
|
|
92236
92438
|
}
|
|
92237
|
-
|
|
92439
|
+
renameSync22(stagingPath, finalPath);
|
|
92238
92440
|
return finalPath;
|
|
92239
92441
|
});
|
|
92240
92442
|
}
|
|
@@ -92242,8 +92444,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92242
92444
|
const paths = overlayPathsFor(agent, opts);
|
|
92243
92445
|
return withAgentLock(paths, () => {
|
|
92244
92446
|
ensureSkillsDirs(paths);
|
|
92245
|
-
const stagingPath =
|
|
92246
|
-
const finalPath =
|
|
92447
|
+
const stagingPath = join90(paths.skillsStagingDir, `${slug}.yaml`);
|
|
92448
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92247
92449
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92248
92450
|
try {
|
|
92249
92451
|
writeSync10(fd, yamlText);
|
|
@@ -92251,15 +92453,15 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92251
92453
|
} finally {
|
|
92252
92454
|
closeSync14(fd);
|
|
92253
92455
|
}
|
|
92254
|
-
|
|
92456
|
+
renameSync22(stagingPath, finalPath);
|
|
92255
92457
|
return finalPath;
|
|
92256
92458
|
});
|
|
92257
92459
|
}
|
|
92258
92460
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
92259
92461
|
const paths = overlayPathsFor(agent, opts);
|
|
92260
92462
|
return withAgentLock(paths, () => {
|
|
92261
|
-
const finalPath =
|
|
92262
|
-
if (!
|
|
92463
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92464
|
+
if (!existsSync89(finalPath))
|
|
92263
92465
|
return false;
|
|
92264
92466
|
unlinkSync18(finalPath);
|
|
92265
92467
|
return true;
|
|
@@ -92267,15 +92469,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
92267
92469
|
}
|
|
92268
92470
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
92269
92471
|
const paths = overlayPathsFor(agent, opts);
|
|
92270
|
-
if (!
|
|
92472
|
+
if (!existsSync89(paths.skillsDir))
|
|
92271
92473
|
return [];
|
|
92272
92474
|
const out = [];
|
|
92273
|
-
for (const name of
|
|
92475
|
+
for (const name of readdirSync33(paths.skillsDir)) {
|
|
92274
92476
|
if (!/\.ya?ml$/i.test(name))
|
|
92275
92477
|
continue;
|
|
92276
|
-
const full =
|
|
92478
|
+
const full = join90(paths.skillsDir, name);
|
|
92277
92479
|
try {
|
|
92278
|
-
const raw =
|
|
92480
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92279
92481
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92280
92482
|
out.push({ slug, path: full, raw });
|
|
92281
92483
|
} catch {}
|
|
@@ -92285,8 +92487,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
92285
92487
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
92286
92488
|
const paths = overlayPathsFor(agent, opts);
|
|
92287
92489
|
return withAgentLock(paths, () => {
|
|
92288
|
-
const finalPath =
|
|
92289
|
-
if (!
|
|
92490
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92491
|
+
if (!existsSync89(finalPath))
|
|
92290
92492
|
return false;
|
|
92291
92493
|
unlinkSync18(finalPath);
|
|
92292
92494
|
return true;
|
|
@@ -92294,15 +92496,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
92294
92496
|
}
|
|
92295
92497
|
function listOverlayEntries(agent, opts = {}) {
|
|
92296
92498
|
const paths = overlayPathsFor(agent, opts);
|
|
92297
|
-
if (!
|
|
92499
|
+
if (!existsSync89(paths.scheduleDir))
|
|
92298
92500
|
return [];
|
|
92299
92501
|
const out = [];
|
|
92300
|
-
for (const name of
|
|
92502
|
+
for (const name of readdirSync33(paths.scheduleDir)) {
|
|
92301
92503
|
if (!/\.ya?ml$/i.test(name))
|
|
92302
92504
|
continue;
|
|
92303
|
-
const full =
|
|
92505
|
+
const full = join90(paths.scheduleDir, name);
|
|
92304
92506
|
try {
|
|
92305
|
-
const raw =
|
|
92507
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92306
92508
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92307
92509
|
out.push({ slug, path: full, raw });
|
|
92308
92510
|
} catch {}
|
|
@@ -92522,27 +92724,27 @@ function reconcileAgentCronOnly(agent) {
|
|
|
92522
92724
|
// src/cli/agent-config-pending.ts
|
|
92523
92725
|
import {
|
|
92524
92726
|
closeSync as closeSync15,
|
|
92525
|
-
existsSync as
|
|
92727
|
+
existsSync as existsSync90,
|
|
92526
92728
|
fsyncSync as fsyncSync9,
|
|
92527
|
-
mkdirSync as
|
|
92729
|
+
mkdirSync as mkdirSync52,
|
|
92528
92730
|
openSync as openSync15,
|
|
92529
|
-
readdirSync as
|
|
92530
|
-
readFileSync as
|
|
92531
|
-
renameSync as
|
|
92731
|
+
readdirSync as readdirSync34,
|
|
92732
|
+
readFileSync as readFileSync78,
|
|
92733
|
+
renameSync as renameSync23,
|
|
92532
92734
|
unlinkSync as unlinkSync19,
|
|
92533
|
-
writeFileSync as
|
|
92735
|
+
writeFileSync as writeFileSync33,
|
|
92534
92736
|
writeSync as writeSync11
|
|
92535
92737
|
} from "node:fs";
|
|
92536
|
-
import { join as
|
|
92738
|
+
import { join as join91 } from "node:path";
|
|
92537
92739
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
92538
92740
|
var STAGE_ID_PREFIX = "cap_";
|
|
92539
92741
|
function pendingDir(agent, opts = {}) {
|
|
92540
92742
|
const paths = overlayPathsFor(agent, opts);
|
|
92541
|
-
return
|
|
92743
|
+
return join91(paths.scheduleDir, ".pending");
|
|
92542
92744
|
}
|
|
92543
92745
|
function ensurePendingDir(agent, opts = {}) {
|
|
92544
92746
|
const dir = pendingDir(agent, opts);
|
|
92545
|
-
|
|
92747
|
+
mkdirSync52(dir, { recursive: true });
|
|
92546
92748
|
return dir;
|
|
92547
92749
|
}
|
|
92548
92750
|
function newStageId() {
|
|
@@ -92551,8 +92753,8 @@ function newStageId() {
|
|
|
92551
92753
|
function stagePendingScheduleEntry(opts) {
|
|
92552
92754
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
92553
92755
|
const stageId = opts.stageId ?? newStageId();
|
|
92554
|
-
const yamlPath =
|
|
92555
|
-
const metaPath =
|
|
92756
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92757
|
+
const metaPath = join91(dir, `${stageId}.meta.json`);
|
|
92556
92758
|
const meta = {
|
|
92557
92759
|
v: 1,
|
|
92558
92760
|
stage_id: stageId,
|
|
@@ -92571,27 +92773,27 @@ function stagePendingScheduleEntry(opts) {
|
|
|
92571
92773
|
} finally {
|
|
92572
92774
|
closeSync15(fd);
|
|
92573
92775
|
}
|
|
92574
|
-
|
|
92776
|
+
renameSync23(yamlTmp, yamlPath);
|
|
92575
92777
|
}
|
|
92576
|
-
|
|
92778
|
+
writeFileSync33(metaPath, JSON.stringify(meta, null, 2) + `
|
|
92577
92779
|
`, { mode: 384 });
|
|
92578
92780
|
return { stageId, yamlPath, metaPath };
|
|
92579
92781
|
}
|
|
92580
92782
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
92581
92783
|
const dir = pendingDir(agent, opts);
|
|
92582
|
-
if (!
|
|
92784
|
+
if (!existsSync90(dir))
|
|
92583
92785
|
return [];
|
|
92584
92786
|
const out = [];
|
|
92585
|
-
for (const name of
|
|
92787
|
+
for (const name of readdirSync34(dir).sort()) {
|
|
92586
92788
|
if (!name.endsWith(".meta.json"))
|
|
92587
92789
|
continue;
|
|
92588
92790
|
const stageId = name.slice(0, -".meta.json".length);
|
|
92589
|
-
const metaPath =
|
|
92590
|
-
const yamlPath =
|
|
92591
|
-
if (!
|
|
92791
|
+
const metaPath = join91(dir, name);
|
|
92792
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92793
|
+
if (!existsSync90(yamlPath))
|
|
92592
92794
|
continue;
|
|
92593
92795
|
try {
|
|
92594
|
-
const meta = JSON.parse(
|
|
92796
|
+
const meta = JSON.parse(readFileSync78(metaPath, "utf-8"));
|
|
92595
92797
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
92596
92798
|
continue;
|
|
92597
92799
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -92606,11 +92808,11 @@ function commitPendingScheduleEntry(opts) {
|
|
|
92606
92808
|
return { committed: false, reason: "not_found" };
|
|
92607
92809
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
92608
92810
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
92609
|
-
const finalPath =
|
|
92610
|
-
if (
|
|
92811
|
+
const finalPath = join91(paths.scheduleDir, `${slug}.yaml`);
|
|
92812
|
+
if (existsSync90(finalPath)) {
|
|
92611
92813
|
return { committed: false, reason: "slug_collision" };
|
|
92612
92814
|
}
|
|
92613
|
-
|
|
92815
|
+
renameSync23(match.yamlPath, finalPath);
|
|
92614
92816
|
unlinkSync19(match.metaPath);
|
|
92615
92817
|
return { committed: true, path: finalPath, slug };
|
|
92616
92818
|
}
|
|
@@ -92629,7 +92831,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
92629
92831
|
}
|
|
92630
92832
|
|
|
92631
92833
|
// src/cli/agent-config-write.ts
|
|
92632
|
-
import { existsSync as
|
|
92834
|
+
import { existsSync as existsSync91, readFileSync as readFileSync79 } from "node:fs";
|
|
92633
92835
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
92634
92836
|
|
|
92635
92837
|
// src/scheduler/schedule-report.ts
|
|
@@ -93019,8 +93221,8 @@ function scheduleRemove(opts) {
|
|
|
93019
93221
|
}
|
|
93020
93222
|
let priorContent = null;
|
|
93021
93223
|
try {
|
|
93022
|
-
if (
|
|
93023
|
-
priorContent =
|
|
93224
|
+
if (existsSync91(match.path))
|
|
93225
|
+
priorContent = readFileSync79(match.path, "utf-8");
|
|
93024
93226
|
} catch {}
|
|
93025
93227
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
93026
93228
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -93223,7 +93425,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93223
93425
|
}
|
|
93224
93426
|
let blob;
|
|
93225
93427
|
if (opts.jsonl) {
|
|
93226
|
-
blob =
|
|
93428
|
+
blob = existsSync91(opts.jsonl) ? readFileSync79(opts.jsonl, "utf-8") : "";
|
|
93227
93429
|
} else {
|
|
93228
93430
|
try {
|
|
93229
93431
|
blob = execFileSync28("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -93255,11 +93457,11 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93255
93457
|
|
|
93256
93458
|
// src/cli/agent-config-skill-write.ts
|
|
93257
93459
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
93258
|
-
import { existsSync as
|
|
93460
|
+
import { existsSync as existsSync92 } from "node:fs";
|
|
93259
93461
|
init_reconcile_default_skills();
|
|
93260
93462
|
init_agent_config();
|
|
93261
93463
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
93262
|
-
import { join as
|
|
93464
|
+
import { join as join92 } from "node:path";
|
|
93263
93465
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
93264
93466
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
93265
93467
|
function exitCodeFor2(code) {
|
|
@@ -93334,8 +93536,8 @@ function skillInstall(opts) {
|
|
|
93334
93536
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
93335
93537
|
}
|
|
93336
93538
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
93337
|
-
const skillPath =
|
|
93338
|
-
if (!
|
|
93539
|
+
const skillPath = join92(poolDir, skillName);
|
|
93540
|
+
if (!existsSync92(skillPath)) {
|
|
93339
93541
|
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.`);
|
|
93340
93542
|
}
|
|
93341
93543
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -93499,21 +93701,21 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
93499
93701
|
// src/cli/skill.ts
|
|
93500
93702
|
import {
|
|
93501
93703
|
closeSync as closeSync16,
|
|
93502
|
-
existsSync as
|
|
93704
|
+
existsSync as existsSync93,
|
|
93503
93705
|
lstatSync as lstatSync11,
|
|
93504
|
-
mkdirSync as
|
|
93706
|
+
mkdirSync as mkdirSync53,
|
|
93505
93707
|
mkdtempSync as mkdtempSync5,
|
|
93506
93708
|
openSync as openSync16,
|
|
93507
|
-
readFileSync as
|
|
93508
|
-
readdirSync as
|
|
93709
|
+
readFileSync as readFileSync80,
|
|
93710
|
+
readdirSync as readdirSync35,
|
|
93509
93711
|
realpathSync as realpathSync7,
|
|
93510
|
-
renameSync as
|
|
93712
|
+
renameSync as renameSync24,
|
|
93511
93713
|
rmSync as rmSync18,
|
|
93512
93714
|
statSync as statSync49,
|
|
93513
|
-
writeFileSync as
|
|
93715
|
+
writeFileSync as writeFileSync34
|
|
93514
93716
|
} from "node:fs";
|
|
93515
93717
|
import { tmpdir as tmpdir6, homedir as homedir51 } from "node:os";
|
|
93516
|
-
import { dirname as
|
|
93718
|
+
import { dirname as dirname33, join as join93, relative as relative4, resolve as resolve54 } from "node:path";
|
|
93517
93719
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
93518
93720
|
|
|
93519
93721
|
// src/cli/skill-common.ts
|
|
@@ -93747,7 +93949,7 @@ function scanForClaudeP2(content) {
|
|
|
93747
93949
|
function resolveSkillsPoolDir2(override) {
|
|
93748
93950
|
const raw = override ?? "~/.switchroom/skills";
|
|
93749
93951
|
if (raw.startsWith("~/")) {
|
|
93750
|
-
return
|
|
93952
|
+
return join93(homedir51(), raw.slice(2));
|
|
93751
93953
|
}
|
|
93752
93954
|
if (raw === "~")
|
|
93753
93955
|
return homedir51();
|
|
@@ -93784,10 +93986,10 @@ function loadFromDir(dir) {
|
|
|
93784
93986
|
}
|
|
93785
93987
|
const files = {};
|
|
93786
93988
|
const walk2 = (sub) => {
|
|
93787
|
-
const entries =
|
|
93989
|
+
const entries = readdirSync35(sub, { withFileTypes: true });
|
|
93788
93990
|
for (const ent of entries) {
|
|
93789
|
-
const full =
|
|
93790
|
-
const rel =
|
|
93991
|
+
const full = join93(sub, ent.name);
|
|
93992
|
+
const rel = relative4(abs, full);
|
|
93791
93993
|
if (ent.isSymbolicLink()) {
|
|
93792
93994
|
fail3(`refusing to read symlink inside --from dir: ${rel}`);
|
|
93793
93995
|
}
|
|
@@ -93796,7 +93998,7 @@ function loadFromDir(dir) {
|
|
|
93796
93998
|
continue;
|
|
93797
93999
|
}
|
|
93798
94000
|
if (ent.isFile()) {
|
|
93799
|
-
const buf =
|
|
94001
|
+
const buf = readFileSync80(full);
|
|
93800
94002
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
93801
94003
|
}
|
|
93802
94004
|
}
|
|
@@ -93821,7 +94023,7 @@ function loadFromTarball(tarPath) {
|
|
|
93821
94023
|
fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
93822
94024
|
}
|
|
93823
94025
|
}
|
|
93824
|
-
const staging = mkdtempSync5(
|
|
94026
|
+
const staging = mkdtempSync5(join93(tmpdir6(), "skill-apply-extract-"));
|
|
93825
94027
|
try {
|
|
93826
94028
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
93827
94029
|
const r = spawnSync16("tar", [
|
|
@@ -93845,7 +94047,7 @@ function loadFromTarball(tarPath) {
|
|
|
93845
94047
|
}
|
|
93846
94048
|
}
|
|
93847
94049
|
function loadSingleFile(filePath) {
|
|
93848
|
-
const content =
|
|
94050
|
+
const content = readFileSync80(filePath, "utf-8");
|
|
93849
94051
|
return { "SKILL.md": content };
|
|
93850
94052
|
}
|
|
93851
94053
|
function loadFromStdin() {
|
|
@@ -93907,10 +94109,10 @@ function validatePayload(name, files) {
|
|
|
93907
94109
|
errors2.push(`${path9} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
93908
94110
|
}
|
|
93909
94111
|
} else if (PY_SCRIPT_RE2.test(path9)) {
|
|
93910
|
-
const tmp = mkdtempSync5(
|
|
93911
|
-
const tmpPy =
|
|
94112
|
+
const tmp = mkdtempSync5(join93(tmpdir6(), "skill-apply-py-"));
|
|
94113
|
+
const tmpPy = join93(tmp, "check.py");
|
|
93912
94114
|
try {
|
|
93913
|
-
|
|
94115
|
+
writeFileSync34(tmpPy, content);
|
|
93914
94116
|
const r = spawnSync16("python3", ["-m", "py_compile", tmpPy], {
|
|
93915
94117
|
encoding: "utf-8"
|
|
93916
94118
|
});
|
|
@@ -93928,15 +94130,15 @@ function validatePayload(name, files) {
|
|
|
93928
94130
|
function diffSummary(currentDir, files) {
|
|
93929
94131
|
const lines = [];
|
|
93930
94132
|
const currentFiles = {};
|
|
93931
|
-
if (
|
|
94133
|
+
if (existsSync93(currentDir)) {
|
|
93932
94134
|
const walk2 = (sub) => {
|
|
93933
|
-
for (const ent of
|
|
93934
|
-
const full =
|
|
93935
|
-
const rel =
|
|
94135
|
+
for (const ent of readdirSync35(sub, { withFileTypes: true })) {
|
|
94136
|
+
const full = join93(sub, ent.name);
|
|
94137
|
+
const rel = relative4(currentDir, full);
|
|
93936
94138
|
if (ent.isDirectory()) {
|
|
93937
94139
|
walk2(full);
|
|
93938
94140
|
} else if (ent.isFile()) {
|
|
93939
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
94141
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync80(full, "utf-8");
|
|
93940
94142
|
}
|
|
93941
94143
|
}
|
|
93942
94144
|
};
|
|
@@ -93964,10 +94166,10 @@ function diffSummary(currentDir, files) {
|
|
|
93964
94166
|
`);
|
|
93965
94167
|
}
|
|
93966
94168
|
function writePayload(poolDir, name, files) {
|
|
93967
|
-
if (!
|
|
93968
|
-
|
|
94169
|
+
if (!existsSync93(poolDir)) {
|
|
94170
|
+
mkdirSync53(poolDir, { recursive: true, mode: 493 });
|
|
93969
94171
|
}
|
|
93970
|
-
const target =
|
|
94172
|
+
const target = join93(poolDir, name);
|
|
93971
94173
|
let targetIsSymlink = false;
|
|
93972
94174
|
try {
|
|
93973
94175
|
const st = lstatSync11(target);
|
|
@@ -93978,15 +94180,15 @@ function writePayload(poolDir, name, files) {
|
|
|
93978
94180
|
if (targetIsSymlink) {
|
|
93979
94181
|
fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
93980
94182
|
}
|
|
93981
|
-
const staging = mkdtempSync5(
|
|
94183
|
+
const staging = mkdtempSync5(join93(poolDir, `.skill-apply-stage-${name}-`));
|
|
93982
94184
|
let oldRename = null;
|
|
93983
94185
|
try {
|
|
93984
94186
|
for (const [path9, content] of Object.entries(files)) {
|
|
93985
|
-
const full =
|
|
93986
|
-
|
|
94187
|
+
const full = join93(staging, path9);
|
|
94188
|
+
mkdirSync53(dirname33(full), { recursive: true, mode: 493 });
|
|
93987
94189
|
const fd = openSync16(full, "wx");
|
|
93988
94190
|
try {
|
|
93989
|
-
|
|
94191
|
+
writeFileSync34(fd, content);
|
|
93990
94192
|
} finally {
|
|
93991
94193
|
closeSync16(fd);
|
|
93992
94194
|
}
|
|
@@ -94002,9 +94204,9 @@ function writePayload(poolDir, name, files) {
|
|
|
94002
94204
|
} catch {}
|
|
94003
94205
|
if (targetExists) {
|
|
94004
94206
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
94005
|
-
|
|
94207
|
+
renameSync24(target, oldRename);
|
|
94006
94208
|
}
|
|
94007
|
-
|
|
94209
|
+
renameSync24(staging, target);
|
|
94008
94210
|
if (oldRename) {
|
|
94009
94211
|
rmSync18(oldRename, { recursive: true, force: true });
|
|
94010
94212
|
oldRename = null;
|
|
@@ -94013,12 +94215,12 @@ function writePayload(poolDir, name, files) {
|
|
|
94013
94215
|
try {
|
|
94014
94216
|
rmSync18(staging, { recursive: true, force: true });
|
|
94015
94217
|
} catch {}
|
|
94016
|
-
if (oldRename &&
|
|
94218
|
+
if (oldRename && existsSync93(oldRename)) {
|
|
94017
94219
|
try {
|
|
94018
|
-
if (
|
|
94220
|
+
if (existsSync93(target)) {
|
|
94019
94221
|
rmSync18(target, { recursive: true, force: true });
|
|
94020
94222
|
}
|
|
94021
|
-
|
|
94223
|
+
renameSync24(oldRename, target);
|
|
94022
94224
|
} catch {}
|
|
94023
94225
|
}
|
|
94024
94226
|
throw err2;
|
|
@@ -94036,7 +94238,7 @@ function registerSkillCommand(program3) {
|
|
|
94036
94238
|
files = loadFromStdin();
|
|
94037
94239
|
} else {
|
|
94038
94240
|
const fromPath = resolve54(opts.from);
|
|
94039
|
-
if (!
|
|
94241
|
+
if (!existsSync93(fromPath)) {
|
|
94040
94242
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
94041
94243
|
}
|
|
94042
94244
|
const st = statSync49(fromPath);
|
|
@@ -94060,7 +94262,7 @@ function registerSkillCommand(program3) {
|
|
|
94060
94262
|
}
|
|
94061
94263
|
const config = loadConfig();
|
|
94062
94264
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
94063
|
-
const currentDir =
|
|
94265
|
+
const currentDir = join93(poolDir, name);
|
|
94064
94266
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
94065
94267
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
94066
94268
|
console.log(diffSummary(currentDir, files));
|
|
@@ -94092,20 +94294,20 @@ function sumBytes(files) {
|
|
|
94092
94294
|
init_esm();
|
|
94093
94295
|
import {
|
|
94094
94296
|
closeSync as closeSync17,
|
|
94095
|
-
existsSync as
|
|
94297
|
+
existsSync as existsSync94,
|
|
94096
94298
|
lstatSync as lstatSync12,
|
|
94097
|
-
mkdirSync as
|
|
94299
|
+
mkdirSync as mkdirSync54,
|
|
94098
94300
|
mkdtempSync as mkdtempSync6,
|
|
94099
94301
|
openSync as openSync17,
|
|
94100
|
-
readFileSync as
|
|
94101
|
-
readdirSync as
|
|
94102
|
-
renameSync as
|
|
94302
|
+
readFileSync as readFileSync81,
|
|
94303
|
+
readdirSync as readdirSync36,
|
|
94304
|
+
renameSync as renameSync25,
|
|
94103
94305
|
rmSync as rmSync19,
|
|
94104
94306
|
statSync as statSync50,
|
|
94105
94307
|
utimesSync,
|
|
94106
|
-
writeFileSync as
|
|
94308
|
+
writeFileSync as writeFileSync35
|
|
94107
94309
|
} from "node:fs";
|
|
94108
|
-
import { dirname as
|
|
94310
|
+
import { dirname as dirname34, join as join94, relative as relative5, resolve as resolve55 } from "node:path";
|
|
94109
94311
|
import { homedir as homedir52, tmpdir as tmpdir7 } from "node:os";
|
|
94110
94312
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
94111
94313
|
init_helpers();
|
|
@@ -94117,18 +94319,18 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
94117
94319
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
94118
94320
|
function resolveConfigSkillsDir(agent) {
|
|
94119
94321
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
94120
|
-
const candidate = override ? resolve55(override) :
|
|
94121
|
-
if (!
|
|
94322
|
+
const candidate = override ? resolve55(override) : join94(homedir52(), ".switchroom-config");
|
|
94323
|
+
if (!existsSync94(candidate))
|
|
94122
94324
|
return null;
|
|
94123
|
-
return
|
|
94325
|
+
return join94(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
94124
94326
|
}
|
|
94125
94327
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
94126
94328
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
94127
94329
|
try {
|
|
94128
|
-
if (!
|
|
94330
|
+
if (!existsSync94(configSkillsRoot))
|
|
94129
94331
|
return;
|
|
94130
94332
|
const now = Date.now();
|
|
94131
|
-
for (const ent of
|
|
94333
|
+
for (const ent of readdirSync36(configSkillsRoot)) {
|
|
94132
94334
|
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
94133
94335
|
if (!m)
|
|
94134
94336
|
continue;
|
|
@@ -94138,7 +94340,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
94138
94340
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
94139
94341
|
continue;
|
|
94140
94342
|
try {
|
|
94141
|
-
rmSync19(
|
|
94343
|
+
rmSync19(join94(configSkillsRoot, ent), { recursive: true, force: true });
|
|
94142
94344
|
} catch {}
|
|
94143
94345
|
}
|
|
94144
94346
|
} catch {}
|
|
@@ -94147,7 +94349,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94147
94349
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
94148
94350
|
if (!configSkillsRoot)
|
|
94149
94351
|
return;
|
|
94150
|
-
const dest =
|
|
94352
|
+
const dest = join94(configSkillsRoot, name);
|
|
94151
94353
|
try {
|
|
94152
94354
|
if (liveSkillDir !== null) {
|
|
94153
94355
|
try {
|
|
@@ -94161,35 +94363,35 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94161
94363
|
}
|
|
94162
94364
|
if (liveSkillDir === null) {
|
|
94163
94365
|
sweepMirrorPriors(configSkillsRoot);
|
|
94164
|
-
if (
|
|
94165
|
-
const trash =
|
|
94166
|
-
|
|
94366
|
+
if (existsSync94(dest)) {
|
|
94367
|
+
const trash = join94(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
94368
|
+
renameSync25(dest, trash);
|
|
94167
94369
|
}
|
|
94168
94370
|
return;
|
|
94169
94371
|
}
|
|
94170
|
-
|
|
94372
|
+
mkdirSync54(configSkillsRoot, { recursive: true, mode: 493 });
|
|
94171
94373
|
sweepMirrorPriors(configSkillsRoot);
|
|
94172
|
-
const staging = mkdtempSync6(
|
|
94374
|
+
const staging = mkdtempSync6(join94(configSkillsRoot, `.${name}-staging-`));
|
|
94173
94375
|
const walk2 = (src, dst) => {
|
|
94174
|
-
|
|
94175
|
-
for (const ent of
|
|
94176
|
-
const s =
|
|
94177
|
-
const d =
|
|
94376
|
+
mkdirSync54(dst, { recursive: true, mode: 493 });
|
|
94377
|
+
for (const ent of readdirSync36(src, { withFileTypes: true })) {
|
|
94378
|
+
const s = join94(src, ent.name);
|
|
94379
|
+
const d = join94(dst, ent.name);
|
|
94178
94380
|
if (ent.isSymbolicLink())
|
|
94179
94381
|
continue;
|
|
94180
94382
|
if (ent.isDirectory())
|
|
94181
94383
|
walk2(s, d);
|
|
94182
94384
|
else if (ent.isFile()) {
|
|
94183
|
-
|
|
94385
|
+
writeFileSync35(d, readFileSync81(s));
|
|
94184
94386
|
}
|
|
94185
94387
|
}
|
|
94186
94388
|
};
|
|
94187
94389
|
walk2(liveSkillDir, staging);
|
|
94188
|
-
if (
|
|
94189
|
-
const prior =
|
|
94190
|
-
|
|
94390
|
+
if (existsSync94(dest)) {
|
|
94391
|
+
const prior = join94(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
94392
|
+
renameSync25(dest, prior);
|
|
94191
94393
|
}
|
|
94192
|
-
|
|
94394
|
+
renameSync25(staging, dest);
|
|
94193
94395
|
} catch (err2) {
|
|
94194
94396
|
process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
|
|
94195
94397
|
`));
|
|
@@ -94216,20 +94418,20 @@ function resolveAgent(opts) {
|
|
|
94216
94418
|
function resolveAgentsRoot(opts) {
|
|
94217
94419
|
if (opts.root)
|
|
94218
94420
|
return resolve55(opts.root);
|
|
94219
|
-
return
|
|
94421
|
+
return join94(homedir52(), ".switchroom", "agents");
|
|
94220
94422
|
}
|
|
94221
94423
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
94222
|
-
return
|
|
94424
|
+
return join94(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
94223
94425
|
}
|
|
94224
94426
|
function trashDir(agentsRoot, agent) {
|
|
94225
|
-
return
|
|
94427
|
+
return join94(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
94226
94428
|
}
|
|
94227
94429
|
function countPersonalSkills(agentsRoot, agent) {
|
|
94228
|
-
const skillsDir =
|
|
94229
|
-
if (!
|
|
94430
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94431
|
+
if (!existsSync94(skillsDir))
|
|
94230
94432
|
return 0;
|
|
94231
94433
|
let n = 0;
|
|
94232
|
-
for (const ent of
|
|
94434
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94233
94435
|
if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
|
|
94234
94436
|
n += 1;
|
|
94235
94437
|
}
|
|
@@ -94262,18 +94464,18 @@ function loadFromDir2(dir) {
|
|
|
94262
94464
|
}
|
|
94263
94465
|
const files = {};
|
|
94264
94466
|
const walk2 = (sub) => {
|
|
94265
|
-
for (const ent of
|
|
94266
|
-
const full =
|
|
94467
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94468
|
+
const full = join94(sub, ent.name);
|
|
94267
94469
|
if (ent.isSymbolicLink()) {
|
|
94268
|
-
fail4(`refusing to read symlink in --from dir: ${
|
|
94470
|
+
fail4(`refusing to read symlink in --from dir: ${relative5(abs, full)}`);
|
|
94269
94471
|
}
|
|
94270
94472
|
if (ent.isDirectory()) {
|
|
94271
94473
|
walk2(full);
|
|
94272
94474
|
continue;
|
|
94273
94475
|
}
|
|
94274
94476
|
if (ent.isFile()) {
|
|
94275
|
-
const rel =
|
|
94276
|
-
files[rel] =
|
|
94477
|
+
const rel = relative5(abs, full).replace(/\\/g, "/");
|
|
94478
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94277
94479
|
}
|
|
94278
94480
|
}
|
|
94279
94481
|
};
|
|
@@ -94316,10 +94518,10 @@ function behavioralValidate(files) {
|
|
|
94316
94518
|
errors2.push(`${path9} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
94317
94519
|
}
|
|
94318
94520
|
} else if (PY_SCRIPT_RE.test(path9)) {
|
|
94319
|
-
const tmp = mkdtempSync6(
|
|
94320
|
-
const tmpPy =
|
|
94521
|
+
const tmp = mkdtempSync6(join94(tmpdir7(), "skill-personal-py-"));
|
|
94522
|
+
const tmpPy = join94(tmp, "check.py");
|
|
94321
94523
|
try {
|
|
94322
|
-
|
|
94524
|
+
writeFileSync35(tmpPy, content);
|
|
94323
94525
|
const r = spawnSync17("python3", ["-m", "py_compile", tmpPy], {
|
|
94324
94526
|
encoding: "utf-8"
|
|
94325
94527
|
});
|
|
@@ -94335,13 +94537,13 @@ function behavioralValidate(files) {
|
|
|
94335
94537
|
}
|
|
94336
94538
|
function sweepTrash(agentsRoot, agent) {
|
|
94337
94539
|
const trash = trashDir(agentsRoot, agent);
|
|
94338
|
-
if (!
|
|
94540
|
+
if (!existsSync94(trash))
|
|
94339
94541
|
return;
|
|
94340
94542
|
const now = Date.now();
|
|
94341
|
-
for (const ent of
|
|
94543
|
+
for (const ent of readdirSync36(trash, { withFileTypes: true })) {
|
|
94342
94544
|
if (!ent.isDirectory())
|
|
94343
94545
|
continue;
|
|
94344
|
-
const entPath =
|
|
94546
|
+
const entPath = join94(trash, ent.name);
|
|
94345
94547
|
try {
|
|
94346
94548
|
const st = statSync50(entPath);
|
|
94347
94549
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
@@ -94361,16 +94563,16 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94361
94563
|
if (targetIsSymlink) {
|
|
94362
94564
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
94363
94565
|
}
|
|
94364
|
-
|
|
94365
|
-
const staging = mkdtempSync6(
|
|
94566
|
+
mkdirSync54(dirname34(targetDir), { recursive: true, mode: 493 });
|
|
94567
|
+
const staging = mkdtempSync6(join94(dirname34(targetDir), `.skill-personal-stage-`));
|
|
94366
94568
|
let oldRename = null;
|
|
94367
94569
|
try {
|
|
94368
94570
|
for (const [path9, content] of Object.entries(files)) {
|
|
94369
|
-
const full =
|
|
94370
|
-
|
|
94571
|
+
const full = join94(staging, path9);
|
|
94572
|
+
mkdirSync54(dirname34(full), { recursive: true, mode: 493 });
|
|
94371
94573
|
const fd = openSync17(full, "wx");
|
|
94372
94574
|
try {
|
|
94373
|
-
|
|
94575
|
+
writeFileSync35(fd, content);
|
|
94374
94576
|
} finally {
|
|
94375
94577
|
closeSync17(fd);
|
|
94376
94578
|
}
|
|
@@ -94386,9 +94588,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94386
94588
|
} catch {}
|
|
94387
94589
|
if (targetExists) {
|
|
94388
94590
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
94389
|
-
|
|
94591
|
+
renameSync25(targetDir, oldRename);
|
|
94390
94592
|
}
|
|
94391
|
-
|
|
94593
|
+
renameSync25(staging, targetDir);
|
|
94392
94594
|
if (oldRename) {
|
|
94393
94595
|
rmSync19(oldRename, { recursive: true, force: true });
|
|
94394
94596
|
oldRename = null;
|
|
@@ -94397,12 +94599,12 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94397
94599
|
try {
|
|
94398
94600
|
rmSync19(staging, { recursive: true, force: true });
|
|
94399
94601
|
} catch {}
|
|
94400
|
-
if (oldRename &&
|
|
94602
|
+
if (oldRename && existsSync94(oldRename)) {
|
|
94401
94603
|
try {
|
|
94402
|
-
if (
|
|
94604
|
+
if (existsSync94(targetDir)) {
|
|
94403
94605
|
rmSync19(targetDir, { recursive: true, force: true });
|
|
94404
94606
|
}
|
|
94405
|
-
|
|
94607
|
+
renameSync25(oldRename, targetDir);
|
|
94406
94608
|
} catch {}
|
|
94407
94609
|
}
|
|
94408
94610
|
throw err2;
|
|
@@ -94465,7 +94667,7 @@ function loadFiles(opts) {
|
|
|
94465
94667
|
return loadFromStdin2();
|
|
94466
94668
|
}
|
|
94467
94669
|
const p = resolve55(opts.from);
|
|
94468
|
-
if (!
|
|
94670
|
+
if (!existsSync94(p)) {
|
|
94469
94671
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
94470
94672
|
}
|
|
94471
94673
|
const st = statSync50(p);
|
|
@@ -94473,7 +94675,7 @@ function loadFiles(opts) {
|
|
|
94473
94675
|
return loadFromDir2(p);
|
|
94474
94676
|
}
|
|
94475
94677
|
if (p.endsWith(".md")) {
|
|
94476
|
-
return { "SKILL.md":
|
|
94678
|
+
return { "SKILL.md": readFileSync81(p, "utf-8") };
|
|
94477
94679
|
}
|
|
94478
94680
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
94479
94681
|
}
|
|
@@ -94513,10 +94715,10 @@ function editPersonalAction(name, opts) {
|
|
|
94513
94715
|
}
|
|
94514
94716
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
94515
94717
|
function defaultSharedRoot() {
|
|
94516
|
-
return
|
|
94718
|
+
return join94(homedir52(), ".switchroom", "skills");
|
|
94517
94719
|
}
|
|
94518
94720
|
function defaultBundledRoot() {
|
|
94519
|
-
return
|
|
94721
|
+
return join94(homedir52(), ".switchroom", "skills", "_bundled");
|
|
94520
94722
|
}
|
|
94521
94723
|
function resolveCloneSource(source, opts) {
|
|
94522
94724
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -94526,8 +94728,8 @@ function resolveCloneSource(source, opts) {
|
|
|
94526
94728
|
const tier = m[1];
|
|
94527
94729
|
const slug = m[2];
|
|
94528
94730
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
94529
|
-
const dir =
|
|
94530
|
-
if (!
|
|
94731
|
+
const dir = join94(root, slug);
|
|
94732
|
+
if (!existsSync94(dir)) {
|
|
94531
94733
|
fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
94532
94734
|
}
|
|
94533
94735
|
const st = lstatSync12(dir);
|
|
@@ -94541,8 +94743,8 @@ function readSourceFiles(dir) {
|
|
|
94541
94743
|
const files = {};
|
|
94542
94744
|
const skipped = [];
|
|
94543
94745
|
const walk2 = (sub) => {
|
|
94544
|
-
for (const ent of
|
|
94545
|
-
const full =
|
|
94746
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94747
|
+
const full = join94(sub, ent.name);
|
|
94546
94748
|
if (ent.isSymbolicLink()) {
|
|
94547
94749
|
continue;
|
|
94548
94750
|
}
|
|
@@ -94551,7 +94753,7 @@ function readSourceFiles(dir) {
|
|
|
94551
94753
|
continue;
|
|
94552
94754
|
}
|
|
94553
94755
|
if (ent.isFile()) {
|
|
94554
|
-
const rel =
|
|
94756
|
+
const rel = relative5(dir, full).replace(/\\/g, "/");
|
|
94555
94757
|
if (!validateRelPath(rel)) {
|
|
94556
94758
|
skipped.push(rel);
|
|
94557
94759
|
continue;
|
|
@@ -94562,7 +94764,7 @@ function readSourceFiles(dir) {
|
|
|
94562
94764
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
94563
94765
|
}
|
|
94564
94766
|
} catch {}
|
|
94565
|
-
files[rel] =
|
|
94767
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94566
94768
|
}
|
|
94567
94769
|
}
|
|
94568
94770
|
};
|
|
@@ -94651,10 +94853,10 @@ function removePersonalAction(name, opts) {
|
|
|
94651
94853
|
throw err2;
|
|
94652
94854
|
}
|
|
94653
94855
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
94654
|
-
|
|
94856
|
+
mkdirSync54(trashRoot2, { recursive: true, mode: 493 });
|
|
94655
94857
|
const ts = Date.now();
|
|
94656
|
-
const trashTarget =
|
|
94657
|
-
|
|
94858
|
+
const trashTarget = join94(trashRoot2, `${name}-${ts}`);
|
|
94859
|
+
renameSync25(target, trashTarget);
|
|
94658
94860
|
const now = new Date(ts);
|
|
94659
94861
|
utimesSync(trashTarget, now, now);
|
|
94660
94862
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -94672,27 +94874,27 @@ function listPersonalAction(opts) {
|
|
|
94672
94874
|
const agent = resolveAgent(opts);
|
|
94673
94875
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
94674
94876
|
sweepTrash(agentsRoot, agent);
|
|
94675
|
-
const skillsDir =
|
|
94877
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94676
94878
|
const personal = [];
|
|
94677
|
-
if (
|
|
94678
|
-
for (const ent of
|
|
94879
|
+
if (existsSync94(skillsDir)) {
|
|
94880
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94679
94881
|
if (!ent.isDirectory())
|
|
94680
94882
|
continue;
|
|
94681
94883
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
94682
94884
|
continue;
|
|
94683
94885
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
94684
|
-
const skillPath =
|
|
94886
|
+
const skillPath = join94(skillsDir, ent.name);
|
|
94685
94887
|
let fileCount = 0;
|
|
94686
94888
|
let totalBytes = 0;
|
|
94687
94889
|
const walk2 = (sub) => {
|
|
94688
|
-
for (const e of
|
|
94890
|
+
for (const e of readdirSync36(sub, { withFileTypes: true })) {
|
|
94689
94891
|
if (e.isFile()) {
|
|
94690
94892
|
fileCount += 1;
|
|
94691
94893
|
try {
|
|
94692
|
-
totalBytes += statSync50(
|
|
94894
|
+
totalBytes += statSync50(join94(sub, e.name)).size;
|
|
94693
94895
|
} catch {}
|
|
94694
94896
|
} else if (e.isDirectory()) {
|
|
94695
|
-
walk2(
|
|
94897
|
+
walk2(join94(sub, e.name));
|
|
94696
94898
|
}
|
|
94697
94899
|
}
|
|
94698
94900
|
};
|
|
@@ -94731,11 +94933,11 @@ function registerSkillPersonalCommands(program3) {
|
|
|
94731
94933
|
// src/cli/self-improve-propose-skill.ts
|
|
94732
94934
|
import { createConnection as createConnection4 } from "node:net";
|
|
94733
94935
|
import { homedir as homedir53 } from "node:os";
|
|
94734
|
-
import { join as
|
|
94735
|
-
import { readFileSync as
|
|
94936
|
+
import { join as join95 } from "node:path";
|
|
94937
|
+
import { readFileSync as readFileSync82 } from "node:fs";
|
|
94736
94938
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
94737
94939
|
function gatewaySocketPath() {
|
|
94738
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ?
|
|
94940
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join95(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join95(homedir53(), ".claude", "channels", "telegram", "gateway.sock"));
|
|
94739
94941
|
}
|
|
94740
94942
|
function fail5(msg, code = 1) {
|
|
94741
94943
|
console.error(msg);
|
|
@@ -94770,7 +94972,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94770
94972
|
fail5("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
|
|
94771
94973
|
let draft;
|
|
94772
94974
|
try {
|
|
94773
|
-
draft = JSON.parse(
|
|
94975
|
+
draft = JSON.parse(readFileSync82(opts.draft, "utf-8"));
|
|
94774
94976
|
} catch (e) {
|
|
94775
94977
|
fail5(`failed to read/parse --draft: ${e.message}`);
|
|
94776
94978
|
}
|
|
@@ -94807,9 +95009,9 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94807
95009
|
init_esm();
|
|
94808
95010
|
init_helpers();
|
|
94809
95011
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
94810
|
-
import { existsSync as
|
|
95012
|
+
import { existsSync as existsSync95, readdirSync as readdirSync37, readFileSync as readFileSync83, statSync as statSync51 } from "node:fs";
|
|
94811
95013
|
import { homedir as homedir54 } from "node:os";
|
|
94812
|
-
import { join as
|
|
95014
|
+
import { join as join96, resolve as resolve56 } from "node:path";
|
|
94813
95015
|
var PERSONAL_PREFIX2 = "personal-";
|
|
94814
95016
|
var BUNDLED_SUBDIR = "_bundled";
|
|
94815
95017
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
@@ -94823,12 +95025,12 @@ function defaultBundledRoot2() {
|
|
|
94823
95025
|
return resolve56(homedir54(), ".switchroom/skills/_bundled");
|
|
94824
95026
|
}
|
|
94825
95027
|
function readSkillFrontmatter(skillDir) {
|
|
94826
|
-
const mdPath =
|
|
94827
|
-
if (!
|
|
95028
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
95029
|
+
if (!existsSync95(mdPath))
|
|
94828
95030
|
return null;
|
|
94829
95031
|
let content;
|
|
94830
95032
|
try {
|
|
94831
|
-
content =
|
|
95033
|
+
content = readFileSync83(mdPath, "utf-8");
|
|
94832
95034
|
} catch {
|
|
94833
95035
|
return null;
|
|
94834
95036
|
}
|
|
@@ -94856,7 +95058,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
94856
95058
|
return { fm: parsed };
|
|
94857
95059
|
}
|
|
94858
95060
|
function statSkillMd(skillDir) {
|
|
94859
|
-
const mdPath =
|
|
95061
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
94860
95062
|
try {
|
|
94861
95063
|
const st = statSync51(mdPath);
|
|
94862
95064
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
@@ -94867,20 +95069,20 @@ function statSkillMd(skillDir) {
|
|
|
94867
95069
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
94868
95070
|
if (!AGENT_NAME_RE3.test(agent))
|
|
94869
95071
|
return [];
|
|
94870
|
-
const skillsDir =
|
|
94871
|
-
if (!
|
|
95072
|
+
const skillsDir = join96(agentsRoot, agent, ".claude/skills");
|
|
95073
|
+
if (!existsSync95(skillsDir))
|
|
94872
95074
|
return [];
|
|
94873
95075
|
const out = [];
|
|
94874
95076
|
let entries;
|
|
94875
95077
|
try {
|
|
94876
|
-
entries =
|
|
95078
|
+
entries = readdirSync37(skillsDir);
|
|
94877
95079
|
} catch {
|
|
94878
95080
|
return [];
|
|
94879
95081
|
}
|
|
94880
95082
|
for (const ent of entries) {
|
|
94881
95083
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
94882
95084
|
continue;
|
|
94883
|
-
const dirPath =
|
|
95085
|
+
const dirPath = join96(skillsDir, ent);
|
|
94884
95086
|
try {
|
|
94885
95087
|
if (!statSync51(dirPath).isDirectory())
|
|
94886
95088
|
continue;
|
|
@@ -94906,12 +95108,12 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
94906
95108
|
return out;
|
|
94907
95109
|
}
|
|
94908
95110
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
94909
|
-
if (!
|
|
95111
|
+
if (!existsSync95(sharedRoot))
|
|
94910
95112
|
return [];
|
|
94911
95113
|
const out = [];
|
|
94912
95114
|
let entries;
|
|
94913
95115
|
try {
|
|
94914
|
-
entries =
|
|
95116
|
+
entries = readdirSync37(sharedRoot);
|
|
94915
95117
|
} catch {
|
|
94916
95118
|
return [];
|
|
94917
95119
|
}
|
|
@@ -94920,7 +95122,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94920
95122
|
continue;
|
|
94921
95123
|
if (ent.startsWith("."))
|
|
94922
95124
|
continue;
|
|
94923
|
-
const dirPath =
|
|
95125
|
+
const dirPath = join96(sharedRoot, ent);
|
|
94924
95126
|
try {
|
|
94925
95127
|
if (!statSync51(dirPath).isDirectory())
|
|
94926
95128
|
continue;
|
|
@@ -94944,19 +95146,19 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94944
95146
|
return out;
|
|
94945
95147
|
}
|
|
94946
95148
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
94947
|
-
if (!
|
|
95149
|
+
if (!existsSync95(bundledRoot))
|
|
94948
95150
|
return [];
|
|
94949
95151
|
const out = [];
|
|
94950
95152
|
let entries;
|
|
94951
95153
|
try {
|
|
94952
|
-
entries =
|
|
95154
|
+
entries = readdirSync37(bundledRoot);
|
|
94953
95155
|
} catch {
|
|
94954
95156
|
return [];
|
|
94955
95157
|
}
|
|
94956
95158
|
for (const ent of entries) {
|
|
94957
95159
|
if (ent.startsWith("."))
|
|
94958
95160
|
continue;
|
|
94959
|
-
const dirPath =
|
|
95161
|
+
const dirPath = join96(bundledRoot, ent);
|
|
94960
95162
|
try {
|
|
94961
95163
|
if (!statSync51(dirPath).isDirectory())
|
|
94962
95164
|
continue;
|
|
@@ -95102,18 +95304,18 @@ init_source();
|
|
|
95102
95304
|
init_helpers();
|
|
95103
95305
|
init_operator_uid();
|
|
95104
95306
|
import {
|
|
95105
|
-
existsSync as
|
|
95106
|
-
mkdirSync as
|
|
95107
|
-
readdirSync as
|
|
95108
|
-
readFileSync as
|
|
95109
|
-
writeFileSync as
|
|
95307
|
+
existsSync as existsSync97,
|
|
95308
|
+
mkdirSync as mkdirSync55,
|
|
95309
|
+
readdirSync as readdirSync38,
|
|
95310
|
+
readFileSync as readFileSync85,
|
|
95311
|
+
writeFileSync as writeFileSync36,
|
|
95110
95312
|
statSync as statSync52,
|
|
95111
95313
|
lstatSync as lstatSync13,
|
|
95112
95314
|
realpathSync as realpathSync8,
|
|
95113
95315
|
copyFileSync as copyFileSync13
|
|
95114
95316
|
} from "node:fs";
|
|
95115
95317
|
import { homedir as homedir55 } from "node:os";
|
|
95116
|
-
import { join as
|
|
95318
|
+
import { join as join97 } from "node:path";
|
|
95117
95319
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
95118
95320
|
|
|
95119
95321
|
// src/cli/singleton-stale-cleanup.ts
|
|
@@ -95462,7 +95664,7 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir55()) {
|
|
|
95462
95664
|
return resolved;
|
|
95463
95665
|
}
|
|
95464
95666
|
function resolveHostdSkillsTarget(hostHome) {
|
|
95465
|
-
const skillsPath =
|
|
95667
|
+
const skillsPath = join97(hostHome, ".switchroom", "skills");
|
|
95466
95668
|
let st;
|
|
95467
95669
|
try {
|
|
95468
95670
|
st = lstatSync13(skillsPath);
|
|
@@ -95479,21 +95681,21 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
95479
95681
|
console.warn(`switchroom hostd install: ~/.switchroom/skills is a symlink whose target ` + `does not resolve (dangling) \u2014 skipping the skills bind mount. Bundled ` + `skills will be unavailable to rollout/update until the symlink is fixed.`);
|
|
95480
95682
|
return;
|
|
95481
95683
|
}
|
|
95482
|
-
if (!
|
|
95684
|
+
if (!existsSync97(target)) {
|
|
95483
95685
|
console.warn(`switchroom hostd install: ~/.switchroom/skills resolves to "${target}", ` + `which does not exist \u2014 skipping the skills bind mount. Bundled skills ` + `will be unavailable to rollout/update until the symlink target exists.`);
|
|
95484
95686
|
return;
|
|
95485
95687
|
}
|
|
95486
95688
|
return target;
|
|
95487
95689
|
}
|
|
95488
95690
|
function hostdDir() {
|
|
95489
|
-
return
|
|
95691
|
+
return join97(homedir55(), ".switchroom", "hostd");
|
|
95490
95692
|
}
|
|
95491
95693
|
function hostdComposePath() {
|
|
95492
|
-
return
|
|
95694
|
+
return join97(hostdDir(), "docker-compose.yml");
|
|
95493
95695
|
}
|
|
95494
95696
|
function backupExistingCompose() {
|
|
95495
95697
|
const p = hostdComposePath();
|
|
95496
|
-
if (!
|
|
95698
|
+
if (!existsSync97(p))
|
|
95497
95699
|
return null;
|
|
95498
95700
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95499
95701
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95526,7 +95728,7 @@ async function doInstall(opts, program3) {
|
|
|
95526
95728
|
}
|
|
95527
95729
|
const dir = hostdDir();
|
|
95528
95730
|
const composePath = hostdComposePath();
|
|
95529
|
-
|
|
95731
|
+
mkdirSync55(dir, { recursive: true });
|
|
95530
95732
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
95531
95733
|
const guard = checkDowngrade({
|
|
95532
95734
|
container: "switchroom-hostd",
|
|
@@ -95559,7 +95761,7 @@ async function doInstall(opts, program3) {
|
|
|
95559
95761
|
const bak = backupExistingCompose();
|
|
95560
95762
|
if (bak)
|
|
95561
95763
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95562
|
-
|
|
95764
|
+
writeFileSync36(composePath, yaml, "utf8");
|
|
95563
95765
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
95564
95766
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
95565
95767
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -95591,7 +95793,7 @@ function doStatus() {
|
|
|
95591
95793
|
const composeYml = hostdComposePath();
|
|
95592
95794
|
console.log(source_default.bold("switchroom-hostd"));
|
|
95593
95795
|
console.log("");
|
|
95594
|
-
if (!
|
|
95796
|
+
if (!existsSync97(composeYml)) {
|
|
95595
95797
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95596
95798
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
95597
95799
|
return;
|
|
@@ -95612,14 +95814,14 @@ function doStatus() {
|
|
|
95612
95814
|
} else {
|
|
95613
95815
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
95614
95816
|
}
|
|
95615
|
-
if (
|
|
95817
|
+
if (existsSync97(dir)) {
|
|
95616
95818
|
const entries = [];
|
|
95617
95819
|
try {
|
|
95618
|
-
for (const name of
|
|
95820
|
+
for (const name of readdirSync38(dir)) {
|
|
95619
95821
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
95620
95822
|
continue;
|
|
95621
|
-
const sockPath =
|
|
95622
|
-
if (
|
|
95823
|
+
const sockPath = join97(dir, name, "sock");
|
|
95824
|
+
if (existsSync97(sockPath)) {
|
|
95623
95825
|
const st = statSync52(sockPath);
|
|
95624
95826
|
if ((st.mode & 61440) === 49152) {
|
|
95625
95827
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -95638,7 +95840,7 @@ function doStatus() {
|
|
|
95638
95840
|
}
|
|
95639
95841
|
function doUninstall() {
|
|
95640
95842
|
const composeYml = hostdComposePath();
|
|
95641
|
-
if (!
|
|
95843
|
+
if (!existsSync97(composeYml)) {
|
|
95642
95844
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
95643
95845
|
return;
|
|
95644
95846
|
}
|
|
@@ -95662,12 +95864,12 @@ function registerHostdCommand(program3) {
|
|
|
95662
95864
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
95663
95865
|
hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
|
|
95664
95866
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
95665
|
-
if (!
|
|
95867
|
+
if (!existsSync97(logPath)) {
|
|
95666
95868
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
95667
95869
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
95668
95870
|
return;
|
|
95669
95871
|
}
|
|
95670
|
-
const raw =
|
|
95872
|
+
const raw = readFileSync85(logPath, "utf-8");
|
|
95671
95873
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
95672
95874
|
const filters = {
|
|
95673
95875
|
agent: opts.agent,
|
|
@@ -95710,9 +95912,9 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
95710
95912
|
init_source();
|
|
95711
95913
|
init_helpers();
|
|
95712
95914
|
init_operator_uid();
|
|
95713
|
-
import { chownSync as chownSync9, existsSync as
|
|
95915
|
+
import { chownSync as chownSync9, existsSync as existsSync98, mkdirSync as mkdirSync56, writeFileSync as writeFileSync37, copyFileSync as copyFileSync14 } from "node:fs";
|
|
95714
95916
|
import { homedir as homedir56 } from "node:os";
|
|
95715
|
-
import { join as
|
|
95917
|
+
import { join as join98 } from "node:path";
|
|
95716
95918
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
95717
95919
|
function resolveWebImageTag(explicitTag, release) {
|
|
95718
95920
|
if (explicitTag)
|
|
@@ -95812,14 +96014,14 @@ services:
|
|
|
95812
96014
|
`;
|
|
95813
96015
|
}
|
|
95814
96016
|
function webdDir() {
|
|
95815
|
-
return
|
|
96017
|
+
return join98(homedir56(), ".switchroom", "web");
|
|
95816
96018
|
}
|
|
95817
96019
|
function webdComposePath() {
|
|
95818
|
-
return
|
|
96020
|
+
return join98(webdDir(), "docker-compose.yml");
|
|
95819
96021
|
}
|
|
95820
96022
|
function backupExistingCompose2() {
|
|
95821
96023
|
const p = webdComposePath();
|
|
95822
|
-
if (!
|
|
96024
|
+
if (!existsSync98(p))
|
|
95823
96025
|
return null;
|
|
95824
96026
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95825
96027
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95844,7 +96046,7 @@ async function doInstall2(opts, program3) {
|
|
|
95844
96046
|
}
|
|
95845
96047
|
const dir = webdDir();
|
|
95846
96048
|
const composePath = webdComposePath();
|
|
95847
|
-
|
|
96049
|
+
mkdirSync56(dir, { recursive: true });
|
|
95848
96050
|
const cfg = getConfig(program3);
|
|
95849
96051
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
95850
96052
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -95879,7 +96081,7 @@ async function doInstall2(opts, program3) {
|
|
|
95879
96081
|
const bak = backupExistingCompose2();
|
|
95880
96082
|
if (bak)
|
|
95881
96083
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95882
|
-
|
|
96084
|
+
writeFileSync37(composePath, yaml, "utf8");
|
|
95883
96085
|
try {
|
|
95884
96086
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
95885
96087
|
chownSync9(dir, operatorUid, operatorUid);
|
|
@@ -95921,7 +96123,7 @@ function doStatus2() {
|
|
|
95921
96123
|
const composeYml = webdComposePath();
|
|
95922
96124
|
console.log(source_default.bold("switchroom-web"));
|
|
95923
96125
|
console.log("");
|
|
95924
|
-
if (!
|
|
96126
|
+
if (!existsSync98(composeYml)) {
|
|
95925
96127
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95926
96128
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
95927
96129
|
return;
|
|
@@ -95945,7 +96147,7 @@ function doStatus2() {
|
|
|
95945
96147
|
}
|
|
95946
96148
|
function doUninstall2() {
|
|
95947
96149
|
const composeYml = webdComposePath();
|
|
95948
|
-
if (!
|
|
96150
|
+
if (!existsSync98(composeYml)) {
|
|
95949
96151
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
95950
96152
|
return;
|
|
95951
96153
|
}
|
|
@@ -95975,9 +96177,9 @@ function registerWebdCommand(program3) {
|
|
|
95975
96177
|
// src/cli/host-repair.ts
|
|
95976
96178
|
init_source();
|
|
95977
96179
|
import { homedir as homedir57 } from "node:os";
|
|
95978
|
-
import { join as
|
|
96180
|
+
import { join as join99 } from "node:path";
|
|
95979
96181
|
var ARTIFACT_ALLOWLIST = {
|
|
95980
|
-
dockerComposePluginDir: (home2) =>
|
|
96182
|
+
dockerComposePluginDir: (home2) => join99(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
95981
96183
|
stateSentinel: "/state"
|
|
95982
96184
|
};
|
|
95983
96185
|
function isStateBogusAutoDir(probe2) {
|
|
@@ -96131,7 +96333,7 @@ function applyMountRepairs(items, deps) {
|
|
|
96131
96333
|
function registerHostCommand(program3) {
|
|
96132
96334
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
96133
96335
|
host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
|
|
96134
|
-
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync:
|
|
96336
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync: readdirSync39 } = await import("node:fs");
|
|
96135
96337
|
const probe2 = {
|
|
96136
96338
|
lstat(path9) {
|
|
96137
96339
|
try {
|
|
@@ -96142,7 +96344,7 @@ function registerHostCommand(program3) {
|
|
|
96142
96344
|
},
|
|
96143
96345
|
readdir(path9) {
|
|
96144
96346
|
try {
|
|
96145
|
-
return
|
|
96347
|
+
return readdirSync39(path9);
|
|
96146
96348
|
} catch {
|
|
96147
96349
|
return null;
|
|
96148
96350
|
}
|