switchroom 0.19.7 → 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/auth-broker/index.js +9 -8
- package/dist/cli/switchroom.js +903 -695
- package/dist/host-control/main.js +15 -14
- package/dist/vault/approvals/kernel-server.js +5 -4
- package/dist/vault/broker/server.js +9 -8
- package/package.json +1 -1
- package/profiles/default/CLAUDE.md.hbs +4 -4
- package/skills/telegram-formatting/SKILL.md +147 -0
- package/telegram-plugin/dist/gateway/gateway.js +30 -11
- package/telegram-plugin/gateway/gateway.ts +2 -2
- package/telegram-plugin/render/ir.ts +34 -26
- package/telegram-plugin/render/render.ts +12 -3
- package/telegram-plugin/rich-send.ts +16 -10
- package/telegram-plugin/shared/bot-runtime.ts +57 -0
- package/telegram-plugin/tests/format-guard-pins.test.ts +93 -0
- package/telegram-plugin/tests/render/underline-wire-outcome.test.ts +32 -0
- package/telegram-plugin/tests/rich-markdown-guard-transformer.test.ts +121 -0
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";
|
|
@@ -23861,7 +23861,8 @@ function getBuiltinDefaultSkillEntries() {
|
|
|
23861
23861
|
"switchroom-health",
|
|
23862
23862
|
"switchroom-runtime",
|
|
23863
23863
|
"mental-model-curator",
|
|
23864
|
-
"dev-protocol"
|
|
23864
|
+
"dev-protocol",
|
|
23865
|
+
"telegram-formatting"
|
|
23865
23866
|
];
|
|
23866
23867
|
return [
|
|
23867
23868
|
...anthropic.map((key) => ({ key, optOutKey: key, source: "anthropic" })),
|
|
@@ -23938,7 +23939,7 @@ function bindingsForAgent(agentName, mw, microsoftAccounts) {
|
|
|
23938
23939
|
// src/agents/reconcile-default-skills.ts
|
|
23939
23940
|
import { existsSync as existsSync8, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readlinkSync as readlinkSync3, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
23940
23941
|
import { homedir as homedir3 } from "node:os";
|
|
23941
|
-
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";
|
|
23942
23943
|
function warnMissingPoolDir(poolDir) {
|
|
23943
23944
|
if (warnedMissingPool.has(poolDir))
|
|
23944
23945
|
return;
|
|
@@ -23946,6 +23947,14 @@ function warnMissingPoolDir(poolDir) {
|
|
|
23946
23947
|
process.stderr.write(`switchroom: bundled skills pool dir not found at ${poolDir} \u2014 run \`switchroom update\` to install it.
|
|
23947
23948
|
`);
|
|
23948
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
|
+
}
|
|
23949
23958
|
function getBundledSkillsPoolDir() {
|
|
23950
23959
|
return resolve5(homedir3(), ".switchroom/skills/_bundled");
|
|
23951
23960
|
}
|
|
@@ -23960,6 +23969,27 @@ function isOwnedStaleLink(target, poolDir) {
|
|
|
23960
23969
|
return true;
|
|
23961
23970
|
return false;
|
|
23962
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
|
+
}
|
|
23963
23993
|
function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuiltinDefaultSkillEntries(), poolDir = getBundledSkillsPoolDir()) {
|
|
23964
23994
|
const name = agentDir.split("/").pop() ?? agentDir;
|
|
23965
23995
|
const result = {
|
|
@@ -23968,6 +23998,8 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23968
23998
|
alreadyPresent: [],
|
|
23969
23999
|
optedOut: [],
|
|
23970
24000
|
conflicts: [],
|
|
24001
|
+
missingFromPool: [],
|
|
24002
|
+
pruned: [],
|
|
23971
24003
|
changed: false
|
|
23972
24004
|
};
|
|
23973
24005
|
const claudeDir = join6(agentDir, ".claude");
|
|
@@ -23981,15 +24013,32 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23981
24013
|
return result;
|
|
23982
24014
|
}
|
|
23983
24015
|
for (const entry of defaults) {
|
|
24016
|
+
const dest = join6(targetDir, entry.key);
|
|
23984
24017
|
if (optOuts[entry.optOutKey] === false) {
|
|
23985
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
|
+
}
|
|
23986
24026
|
continue;
|
|
23987
24027
|
}
|
|
23988
24028
|
const src = join6(poolDir, entry.key);
|
|
23989
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
|
+
}
|
|
23990
24039
|
continue;
|
|
23991
24040
|
}
|
|
23992
|
-
const
|
|
24041
|
+
const relTarget = linkTargetFor(dest, src);
|
|
23993
24042
|
let existing;
|
|
23994
24043
|
try {
|
|
23995
24044
|
existing = lstatSync2(dest);
|
|
@@ -24002,11 +24051,12 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24002
24051
|
try {
|
|
24003
24052
|
currentTarget = readlinkSync3(dest);
|
|
24004
24053
|
} catch {}
|
|
24005
|
-
if (currentTarget ===
|
|
24054
|
+
if (currentTarget === relTarget) {
|
|
24006
24055
|
result.alreadyPresent.push(entry.key);
|
|
24007
24056
|
continue;
|
|
24008
24057
|
}
|
|
24009
|
-
|
|
24058
|
+
const resolvedTarget = currentTarget ? absoluteLinkTarget(dest, currentTarget) : null;
|
|
24059
|
+
if (resolvedTarget && isOwnedStaleLink(resolvedTarget, poolDir)) {
|
|
24010
24060
|
try {
|
|
24011
24061
|
rmSync2(dest, { force: true });
|
|
24012
24062
|
} catch {}
|
|
@@ -24020,7 +24070,7 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24020
24070
|
}
|
|
24021
24071
|
}
|
|
24022
24072
|
try {
|
|
24023
|
-
symlinkSync(
|
|
24073
|
+
symlinkSync(relTarget, dest);
|
|
24024
24074
|
result.added.push(entry.key);
|
|
24025
24075
|
result.changed = true;
|
|
24026
24076
|
} catch (err) {
|
|
@@ -24029,10 +24079,11 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24029
24079
|
}
|
|
24030
24080
|
return result;
|
|
24031
24081
|
}
|
|
24032
|
-
var warnedMissingPool;
|
|
24082
|
+
var warnedMissingPool, warnedMissingDefault;
|
|
24033
24083
|
var init_reconcile_default_skills = __esm(() => {
|
|
24034
24084
|
init_scaffold_integration();
|
|
24035
24085
|
warnedMissingPool = new Set;
|
|
24086
|
+
warnedMissingDefault = new Set;
|
|
24036
24087
|
});
|
|
24037
24088
|
|
|
24038
24089
|
// src/agents/sub-agent-telegram-prompt.ts
|
|
@@ -24088,7 +24139,7 @@ import {
|
|
|
24088
24139
|
copyFileSync as copyFileSync2,
|
|
24089
24140
|
unlinkSync
|
|
24090
24141
|
} from "node:fs";
|
|
24091
|
-
import { basename as basename2, dirname as
|
|
24142
|
+
import { basename as basename2, dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
24092
24143
|
function defaultStatePath() {
|
|
24093
24144
|
return resolveStatePath("topics.json");
|
|
24094
24145
|
}
|
|
@@ -24115,7 +24166,7 @@ function loadTopicState(statePath) {
|
|
|
24115
24166
|
}
|
|
24116
24167
|
function saveTopicState(state, statePath) {
|
|
24117
24168
|
const path = statePath ?? defaultStatePath();
|
|
24118
|
-
const dir =
|
|
24169
|
+
const dir = dirname3(path);
|
|
24119
24170
|
if (!existsSync9(dir)) {
|
|
24120
24171
|
mkdirSync4(dir, { recursive: true });
|
|
24121
24172
|
}
|
|
@@ -24387,7 +24438,7 @@ import {
|
|
|
24387
24438
|
lstatSync as lstatSync3,
|
|
24388
24439
|
realpathSync as realpathSync2
|
|
24389
24440
|
} from "node:fs";
|
|
24390
|
-
import { dirname as
|
|
24441
|
+
import { dirname as dirname4, basename as basename3, resolve as resolve7 } from "node:path";
|
|
24391
24442
|
function atomicWriteFileSync2(path, data, mode) {
|
|
24392
24443
|
let effectivePath = path;
|
|
24393
24444
|
try {
|
|
@@ -24395,7 +24446,7 @@ function atomicWriteFileSync2(path, data, mode) {
|
|
|
24395
24446
|
effectivePath = realpathSync2(path);
|
|
24396
24447
|
}
|
|
24397
24448
|
} catch {}
|
|
24398
|
-
const dir =
|
|
24449
|
+
const dir = dirname4(resolve7(effectivePath));
|
|
24399
24450
|
const tmp = resolve7(dir, `.${basename3(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
24400
24451
|
try {
|
|
24401
24452
|
const fd = openSync4(tmp, "wx", mode);
|
|
@@ -24532,7 +24583,7 @@ function createVault(passphrase, vaultPath) {
|
|
|
24532
24583
|
if (existsSync11(vaultPath)) {
|
|
24533
24584
|
throw new VaultError(`Vault file already exists: ${vaultPath}`);
|
|
24534
24585
|
}
|
|
24535
|
-
const dir =
|
|
24586
|
+
const dir = dirname4(vaultPath);
|
|
24536
24587
|
if (!existsSync11(dir)) {
|
|
24537
24588
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
24538
24589
|
}
|
|
@@ -26140,7 +26191,7 @@ import {
|
|
|
26140
26191
|
} from "node:fs";
|
|
26141
26192
|
import { homedir as homedir5 } from "node:os";
|
|
26142
26193
|
import { execSync, execFileSync as execFileSync6 } from "node:child_process";
|
|
26143
|
-
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";
|
|
26144
26195
|
import { createHash as createHash3 } from "node:crypto";
|
|
26145
26196
|
function prependReplyDiscipline(rendered, context) {
|
|
26146
26197
|
const replyDiscipline = renderReplyDisciplineFragment(context);
|
|
@@ -26526,7 +26577,8 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
|
|
|
26526
26577
|
} catch {
|
|
26527
26578
|
continue;
|
|
26528
26579
|
}
|
|
26529
|
-
|
|
26580
|
+
const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(entryPath), target) : null;
|
|
26581
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26530
26582
|
try {
|
|
26531
26583
|
rmSync4(entryPath, { force: true });
|
|
26532
26584
|
} catch {}
|
|
@@ -26549,6 +26601,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26549
26601
|
console.warn(` WARNING: skill "${name}" not found in pool (${skillsPool}) \u2014 skipping`);
|
|
26550
26602
|
continue;
|
|
26551
26603
|
}
|
|
26604
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26552
26605
|
let linkStat;
|
|
26553
26606
|
try {
|
|
26554
26607
|
linkStat = lstatSync4(dest);
|
|
@@ -26561,7 +26614,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26561
26614
|
try {
|
|
26562
26615
|
target = readlinkSync4(dest);
|
|
26563
26616
|
} catch {}
|
|
26564
|
-
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)) {
|
|
26565
26622
|
try {
|
|
26566
26623
|
rmSync4(dest, { force: true });
|
|
26567
26624
|
} catch {}
|
|
@@ -26573,7 +26630,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26573
26630
|
}
|
|
26574
26631
|
}
|
|
26575
26632
|
try {
|
|
26576
|
-
symlinkSync2(
|
|
26633
|
+
symlinkSync2(relTarget, dest);
|
|
26577
26634
|
} catch (err) {
|
|
26578
26635
|
console.warn(` WARNING: failed to symlink skill "${name}": ${err.message}`);
|
|
26579
26636
|
}
|
|
@@ -26589,10 +26646,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26589
26646
|
} catch {
|
|
26590
26647
|
continue;
|
|
26591
26648
|
}
|
|
26592
|
-
|
|
26649
|
+
const resolved = linkTarget ? isAbsolute2(linkTarget) ? linkTarget : resolve11(dirname5(entryPath), linkTarget) : null;
|
|
26650
|
+
if (resolved && resolved.includes("/.switchroom/skills/_bundled/")) {
|
|
26593
26651
|
continue;
|
|
26594
26652
|
}
|
|
26595
|
-
if (
|
|
26653
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26596
26654
|
rmSync4(entryPath, { force: true });
|
|
26597
26655
|
}
|
|
26598
26656
|
}
|
|
@@ -26645,7 +26703,10 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26645
26703
|
try {
|
|
26646
26704
|
currentTarget = readlinkSync4(dest);
|
|
26647
26705
|
} catch {}
|
|
26648
|
-
if (currentTarget
|
|
26706
|
+
if (!currentTarget)
|
|
26707
|
+
continue;
|
|
26708
|
+
const resolvedTarget = isAbsolute2(currentTarget) ? currentTarget : resolve11(dirname5(dest), currentTarget);
|
|
26709
|
+
if (resolvedTarget !== join11(builtinSkillsDir, name))
|
|
26649
26710
|
continue;
|
|
26650
26711
|
try {
|
|
26651
26712
|
rmSync4(dest, { force: true });
|
|
@@ -26656,6 +26717,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26656
26717
|
for (const name of switchroomSkillNames) {
|
|
26657
26718
|
const src = join11(builtinSkillsDir, name);
|
|
26658
26719
|
const dest = join11(targetDir, name);
|
|
26720
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26659
26721
|
let existing;
|
|
26660
26722
|
try {
|
|
26661
26723
|
existing = lstatSync4(dest);
|
|
@@ -26668,7 +26730,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26668
26730
|
try {
|
|
26669
26731
|
currentTarget = readlinkSync4(dest);
|
|
26670
26732
|
} catch {}
|
|
26671
|
-
if (currentTarget ===
|
|
26733
|
+
if (currentTarget === relTarget)
|
|
26672
26734
|
continue;
|
|
26673
26735
|
try {
|
|
26674
26736
|
rmSync4(dest, { force: true });
|
|
@@ -26678,7 +26740,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26678
26740
|
}
|
|
26679
26741
|
}
|
|
26680
26742
|
try {
|
|
26681
|
-
symlinkSync2(
|
|
26743
|
+
symlinkSync2(relTarget, dest);
|
|
26682
26744
|
} catch (err) {
|
|
26683
26745
|
console.warn(` WARNING: failed to symlink switchroom skill "${name}": ${err.message}`);
|
|
26684
26746
|
}
|
|
@@ -27380,6 +27442,24 @@ function resolveWebkiteMcpEntry(_agentName, agentConfig, _switchroomConfig) {
|
|
|
27380
27442
|
}
|
|
27381
27443
|
};
|
|
27382
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
|
+
}
|
|
27383
27463
|
function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchroomConfig, userIdOverride, switchroomConfigPath) {
|
|
27384
27464
|
const agentConfig = resolveAgentConfig(switchroomConfig?.defaults, switchroomConfig?.profiles, agentConfigRaw);
|
|
27385
27465
|
const agentDir = resolve11(agentsDir, name);
|
|
@@ -27402,20 +27482,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
27402
27482
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
27403
27483
|
const rawAllow = tools.allow ?? [];
|
|
27404
27484
|
const hasAllWildcard = rawAllow.includes("all");
|
|
27405
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
27406
|
-
const dangerousMode = agentConfig.dangerous_mode === true;
|
|
27407
|
-
const hadExplicitAllow = rawAllow.length > 0;
|
|
27408
|
-
const readOnlyDefaults = !dangerousMode && !hadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
27409
27485
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
27410
|
-
const permissionAllow =
|
|
27411
|
-
...baseAllow,
|
|
27412
|
-
...readOnlyDefaults,
|
|
27413
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
27414
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
27415
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
27416
|
-
...HOSTD_MCP_TOOLS,
|
|
27417
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
27418
|
-
]);
|
|
27486
|
+
const permissionAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
27419
27487
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
27420
27488
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
27421
27489
|
const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
@@ -28331,23 +28399,11 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
28331
28399
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
28332
28400
|
const rawAllow = tools.allow ?? [];
|
|
28333
28401
|
const hasAllWildcard = rawAllow.includes("all");
|
|
28334
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
28335
|
-
const reconcileDangerousMode = agentConfig.dangerous_mode === true;
|
|
28336
|
-
const reconcileHadExplicitAllow = rawAllow.length > 0;
|
|
28337
|
-
const reconcileReadOnlyDefaults = !reconcileDangerousMode && !reconcileHadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
28338
28402
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
28403
|
+
const desiredAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
28339
28404
|
if (Array.isArray(tools.allow)) {
|
|
28340
28405
|
tools.allow = tools.allow.filter((p) => !LEGACY_SWITCHROOM_MCP_TOKENS.includes(p) && !LEGACY_HOSTD_BLANKET_TOKENS.includes(p));
|
|
28341
28406
|
}
|
|
28342
|
-
const desiredAllow = dedupe2([
|
|
28343
|
-
...baseAllow,
|
|
28344
|
-
...reconcileReadOnlyDefaults,
|
|
28345
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
28346
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
28347
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
28348
|
-
...HOSTD_MCP_TOOLS,
|
|
28349
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
28350
|
-
]);
|
|
28351
28407
|
const desiredDeny = dedupe2([
|
|
28352
28408
|
...tools.deny ?? [],
|
|
28353
28409
|
...webkiteDenyForAgent(agentConfig),
|
|
@@ -29440,6 +29496,11 @@ right answer at all. Structure exists for the reader, not the writer: a two-item
|
|
|
29440
29496
|
bullet list is worse than a sentence, a heading on a three-line reply is noise. When
|
|
29441
29497
|
in doubt, shorter and plainer wins.
|
|
29442
29498
|
|
|
29499
|
+
Full palette when a rich or long message earns it \u2014 expandable blockquotes, spoilers,
|
|
29500
|
+
highlight, code-fence language hints, tables, escaping and chunking rules: load the
|
|
29501
|
+
\`telegram-formatting\` skill. Reach for it only when you're actually composing that
|
|
29502
|
+
message, never for everyday replies.
|
|
29503
|
+
|
|
29443
29504
|
Every turn that answers a user message ends with a user-visible \`reply\`
|
|
29444
29505
|
\u2014 Telegram is all the user sees; your terminal output
|
|
29445
29506
|
never reaches them.`, TELEGRAM_ENV_PLACEHOLDER = `# Set your bot token: TELEGRAM_BOT_TOKEN=your-token-here
|
|
@@ -29600,7 +29661,7 @@ var init_scaffold = __esm(() => {
|
|
|
29600
29661
|
|
|
29601
29662
|
// src/setup/host-capabilities.ts
|
|
29602
29663
|
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
|
|
29603
|
-
import { dirname as
|
|
29664
|
+
import { dirname as dirname6 } from "node:path";
|
|
29604
29665
|
function hostCapabilitiesPath() {
|
|
29605
29666
|
return resolveStatePath("host-capabilities.json");
|
|
29606
29667
|
}
|
|
@@ -29615,7 +29676,7 @@ function saveVoiceCapability(caps, now = () => new Date) {
|
|
|
29615
29676
|
}
|
|
29616
29677
|
};
|
|
29617
29678
|
const path = hostCapabilitiesPath();
|
|
29618
|
-
mkdirSync11(
|
|
29679
|
+
mkdirSync11(dirname6(path), { recursive: true });
|
|
29619
29680
|
writeFileSync5(path, JSON.stringify(doc, null, 2) + `
|
|
29620
29681
|
`, {
|
|
29621
29682
|
encoding: "utf-8",
|
|
@@ -29722,11 +29783,11 @@ var init_grants_db_path = __esm(() => {
|
|
|
29722
29783
|
|
|
29723
29784
|
// src/agents/compose.ts
|
|
29724
29785
|
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync15, lstatSync as lstatSync5, readlinkSync as readlinkSync5, chmodSync as chmodSync4 } from "node:fs";
|
|
29725
|
-
import { join as join13, isAbsolute, dirname as
|
|
29786
|
+
import { join as join13, isAbsolute as isAbsolute3, dirname as dirname8, resolve as resolve13 } from "node:path";
|
|
29726
29787
|
function assertPlausibleHostHome(homePrefix) {
|
|
29727
29788
|
if (homePrefix === "${HOME}")
|
|
29728
29789
|
return;
|
|
29729
|
-
const bad = !
|
|
29790
|
+
const bad = !isAbsolute3(homePrefix) || CONTAINER_ROOT_PREFIXES.some((p) => homePrefix === p || homePrefix.startsWith(p + "/"));
|
|
29730
29791
|
if (!bad)
|
|
29731
29792
|
return;
|
|
29732
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").
|
|
@@ -29970,8 +30031,8 @@ function conditionalMountPresent(probePath, hostHome, probeHome) {
|
|
|
29970
30031
|
if (!lstatSync5(probePath).isSymbolicLink())
|
|
29971
30032
|
return false;
|
|
29972
30033
|
let target = readlinkSync5(probePath);
|
|
29973
|
-
if (!
|
|
29974
|
-
target = resolve13(
|
|
30034
|
+
if (!isAbsolute3(target))
|
|
30035
|
+
target = resolve13(dirname8(probePath), target);
|
|
29975
30036
|
if (hostHome && probeHome && hostHome !== probeHome) {
|
|
29976
30037
|
if (target.startsWith(hostHome + "/")) {
|
|
29977
30038
|
return true;
|
|
@@ -30020,7 +30081,7 @@ function generateCompose(opts) {
|
|
|
30020
30081
|
const lines = [];
|
|
30021
30082
|
lines.push("# generated by switchroom \u2014 do not edit by hand.");
|
|
30022
30083
|
lines.push("# Manual edits will be overwritten on the next `switchroom agent add`");
|
|
30023
|
-
lines.push("#
|
|
30084
|
+
lines.push("# or `switchroom apply`. To customise an agent, edit");
|
|
30024
30085
|
lines.push("# switchroom.yaml and re-run the regenerating command.");
|
|
30025
30086
|
lines.push("");
|
|
30026
30087
|
lines.push(`# image tag: ${imageTag}`);
|
|
@@ -30692,7 +30753,7 @@ var init_operator_uid = () => {};
|
|
|
30692
30753
|
import { chownSync as chownSync2 } from "node:fs";
|
|
30693
30754
|
import { mkdir, readFile, writeFile, rename, copyFile } from "node:fs/promises";
|
|
30694
30755
|
import { homedir as homedir7 } from "node:os";
|
|
30695
|
-
import { basename as basename4, dirname as
|
|
30756
|
+
import { basename as basename4, dirname as dirname9, join as join15 } from "node:path";
|
|
30696
30757
|
function agentHadLiteLLMRouting(composeContent, agentName) {
|
|
30697
30758
|
const lines = composeContent.split(`
|
|
30698
30759
|
`);
|
|
@@ -30833,7 +30894,7 @@ async function computeComposeContent(opts) {
|
|
|
30833
30894
|
async function writeComposeFile(opts) {
|
|
30834
30895
|
const { content, imageTag, previous, previousImageTag } = await computeComposeContent(opts);
|
|
30835
30896
|
const operatorUid = resolveOperatorUid();
|
|
30836
|
-
await mkdir(
|
|
30897
|
+
await mkdir(dirname9(opts.composePath), { recursive: true });
|
|
30837
30898
|
if (previous !== null) {
|
|
30838
30899
|
try {
|
|
30839
30900
|
await copyFile(opts.composePath, opts.composePath + ".bak");
|
|
@@ -30902,9 +30963,9 @@ var init_tmux = __esm(() => {
|
|
|
30902
30963
|
|
|
30903
30964
|
// src/agents/compose-env.ts
|
|
30904
30965
|
import { existsSync as existsSync21 } from "node:fs";
|
|
30905
|
-
import { dirname as
|
|
30966
|
+
import { dirname as dirname10 } from "node:path";
|
|
30906
30967
|
function composeEnvPath(composePath) {
|
|
30907
|
-
return
|
|
30968
|
+
return dirname10(composePath) + "/.env";
|
|
30908
30969
|
}
|
|
30909
30970
|
function composeEnvFileArgs(composePath) {
|
|
30910
30971
|
const envPath = composeEnvPath(composePath);
|
|
@@ -35572,7 +35633,7 @@ import {
|
|
|
35572
35633
|
unlinkSync as unlinkSync7
|
|
35573
35634
|
} from "node:fs";
|
|
35574
35635
|
import { createHash as createHash6 } from "node:crypto";
|
|
35575
|
-
import { basename as basename5, dirname as
|
|
35636
|
+
import { basename as basename5, dirname as dirname11, join as join31 } from "node:path";
|
|
35576
35637
|
function vaultLayoutPaths(home2) {
|
|
35577
35638
|
const switchroomRoot = join31(home2, ".switchroom");
|
|
35578
35639
|
return {
|
|
@@ -35725,7 +35786,7 @@ function sha256File(path2) {
|
|
|
35725
35786
|
return createHash6("sha256").update(data).digest("hex");
|
|
35726
35787
|
}
|
|
35727
35788
|
function atomicReplaceWithSymlink(target, linkTarget) {
|
|
35728
|
-
const tmp = join31(
|
|
35789
|
+
const tmp = join31(dirname11(target), `.${basename5(target)}.symlink-tmp`);
|
|
35729
35790
|
if (existsSync39(tmp)) {
|
|
35730
35791
|
try {
|
|
35731
35792
|
unlinkSync7(tmp);
|
|
@@ -35779,7 +35840,7 @@ import {
|
|
|
35779
35840
|
unlinkSync as unlinkSync8,
|
|
35780
35841
|
writeSync as writeSync5
|
|
35781
35842
|
} from "node:fs";
|
|
35782
|
-
import { basename as basename6, dirname as
|
|
35843
|
+
import { basename as basename6, dirname as dirname12, resolve as resolve27 } from "node:path";
|
|
35783
35844
|
function readMachineId() {
|
|
35784
35845
|
const vitestVal = process.env.VITEST;
|
|
35785
35846
|
const isTestEnv = vitestVal !== undefined && vitestVal.length > 0;
|
|
@@ -35844,7 +35905,7 @@ function decryptAutoUnlock(blob, machineId) {
|
|
|
35844
35905
|
}
|
|
35845
35906
|
function writeAutoUnlockFile(passphrase, filePath) {
|
|
35846
35907
|
const blob = encryptAutoUnlock(passphrase);
|
|
35847
|
-
const dir =
|
|
35908
|
+
const dir = dirname12(filePath);
|
|
35848
35909
|
mkdirSync23(dir, { recursive: true, mode: 448 });
|
|
35849
35910
|
const tmp = resolve27(dir, `.${basename6(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
35850
35911
|
try {
|
|
@@ -36951,7 +37012,7 @@ function formatForCli(entries, opts = {}) {
|
|
|
36951
37012
|
var init_audit_reader = () => {};
|
|
36952
37013
|
|
|
36953
37014
|
// node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
36954
|
-
import { dirname as
|
|
37015
|
+
import { dirname as dirname16, posix, sep as sep2 } from "path";
|
|
36955
37016
|
function createModulerModifier() {
|
|
36956
37017
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
36957
37018
|
return async (frames) => {
|
|
@@ -36960,7 +37021,7 @@ function createModulerModifier() {
|
|
|
36960
37021
|
return frames;
|
|
36961
37022
|
};
|
|
36962
37023
|
}
|
|
36963
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
37024
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname16(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
36964
37025
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
36965
37026
|
return (filename) => {
|
|
36966
37027
|
if (!filename)
|
|
@@ -41576,7 +41637,7 @@ import {
|
|
|
41576
41637
|
readFileSync as readFileSync45,
|
|
41577
41638
|
writeFileSync as writeFileSync15
|
|
41578
41639
|
} from "node:fs";
|
|
41579
|
-
import { dirname as
|
|
41640
|
+
import { dirname as dirname17 } from "node:path";
|
|
41580
41641
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
41581
41642
|
function telemetryDisabled() {
|
|
41582
41643
|
const v = process.env.SWITCHROOM_TELEMETRY_DISABLED;
|
|
@@ -41598,7 +41659,7 @@ function getDistinctId() {
|
|
|
41598
41659
|
const id = randomUUID3();
|
|
41599
41660
|
cachedDistinctId = id;
|
|
41600
41661
|
try {
|
|
41601
|
-
mkdirSync27(
|
|
41662
|
+
mkdirSync27(dirname17(path5), { recursive: true });
|
|
41602
41663
|
writeFileSync15(path5, id, "utf-8");
|
|
41603
41664
|
} catch {}
|
|
41604
41665
|
return id;
|
|
@@ -41845,7 +41906,7 @@ import {
|
|
|
41845
41906
|
readFileSync as readFileSync55,
|
|
41846
41907
|
readdirSync as readdirSync21
|
|
41847
41908
|
} from "node:fs";
|
|
41848
|
-
import { dirname as
|
|
41909
|
+
import { dirname as dirname21, join as join53 } from "node:path";
|
|
41849
41910
|
import { execSync as execSync2 } from "node:child_process";
|
|
41850
41911
|
function locateManifestPath() {
|
|
41851
41912
|
let dir = import.meta.dirname;
|
|
@@ -41853,7 +41914,7 @@ function locateManifestPath() {
|
|
|
41853
41914
|
const candidate = join53(dir, "dependencies.json");
|
|
41854
41915
|
if (existsSync60(candidate))
|
|
41855
41916
|
return candidate;
|
|
41856
|
-
dir =
|
|
41917
|
+
dir = dirname21(dir);
|
|
41857
41918
|
}
|
|
41858
41919
|
return null;
|
|
41859
41920
|
}
|
|
@@ -42707,7 +42768,7 @@ function checkAgentSocketMounts(composeYaml) {
|
|
|
42707
42768
|
name: "agent socket-volume isolation",
|
|
42708
42769
|
status: "fail",
|
|
42709
42770
|
detail: `Cross-mounted socket volumes: ${violations.join("; ")}`,
|
|
42710
|
-
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."
|
|
42711
42772
|
};
|
|
42712
42773
|
}
|
|
42713
42774
|
function checkAgentCaps(config) {
|
|
@@ -42905,7 +42966,7 @@ function runDockerChecks(args) {
|
|
|
42905
42966
|
name: "compose file present",
|
|
42906
42967
|
status: "warn",
|
|
42907
42968
|
detail: "Docker mode active but no docker-compose.yml found at ~/.switchroom/compose/docker-compose.yml",
|
|
42908
|
-
fix: "Run `switchroom
|
|
42969
|
+
fix: "Run `switchroom apply` to generate it."
|
|
42909
42970
|
});
|
|
42910
42971
|
}
|
|
42911
42972
|
out.push(...checkContainerRuntimeHealth(args.config, args.dockerPsDeps));
|
|
@@ -45927,7 +45988,7 @@ import {
|
|
|
45927
45988
|
readdirSync as readdirSync23,
|
|
45928
45989
|
statSync as statSync38
|
|
45929
45990
|
} from "node:fs";
|
|
45930
|
-
import { dirname as
|
|
45991
|
+
import { dirname as dirname22, join as join68, resolve as resolve39 } from "node:path";
|
|
45931
45992
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
45932
45993
|
function findInNvm(bin) {
|
|
45933
45994
|
const nvmRoot = join68(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -47524,7 +47585,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
47524
47585
|
detail: "skipped (MFF_API_URL not set)"
|
|
47525
47586
|
};
|
|
47526
47587
|
}
|
|
47527
|
-
const credDir =
|
|
47588
|
+
const credDir = dirname22(envPath);
|
|
47528
47589
|
const authScript = join68(credDir, "claude-auth.py");
|
|
47529
47590
|
if (!existsSync66(authScript)) {
|
|
47530
47591
|
return {
|
|
@@ -48584,8 +48645,8 @@ var init_fleet_defaults = __esm(() => {
|
|
|
48584
48645
|
});
|
|
48585
48646
|
|
|
48586
48647
|
// src/agents/connection-health.ts
|
|
48587
|
-
import { mkdirSync as
|
|
48588
|
-
import { join as
|
|
48648
|
+
import { mkdirSync as mkdirSync47, writeFileSync as writeFileSync29 } from "node:fs";
|
|
48649
|
+
import { join as join85 } from "node:path";
|
|
48589
48650
|
async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
48590
48651
|
const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
|
|
48591
48652
|
if (reqs.length === 0)
|
|
@@ -48642,10 +48703,10 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
|
48642
48703
|
return issues;
|
|
48643
48704
|
}
|
|
48644
48705
|
function writeConnectionHealthFile(agentDir, health, deps) {
|
|
48645
|
-
const dir =
|
|
48646
|
-
const path8 =
|
|
48647
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
48648
|
-
(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) + `
|
|
48649
48710
|
`);
|
|
48650
48711
|
}
|
|
48651
48712
|
async function refreshAgentConnectionHealth(config, agentName, agentDir, deps) {
|
|
@@ -48666,10 +48727,10 @@ var CONNECTION_HEALTH_FILENAME = "connection-health.json";
|
|
|
48666
48727
|
var init_connection_health = () => {};
|
|
48667
48728
|
|
|
48668
48729
|
// src/cli/update-prompt-hook.ts
|
|
48669
|
-
import { existsSync as
|
|
48670
|
-
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";
|
|
48671
48732
|
function containerHookCommand() {
|
|
48672
|
-
return
|
|
48733
|
+
return join86(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
|
|
48673
48734
|
}
|
|
48674
48735
|
function updatePromptHookScript() {
|
|
48675
48736
|
return `#!/bin/bash
|
|
@@ -48735,14 +48796,14 @@ exit 0
|
|
|
48735
48796
|
`;
|
|
48736
48797
|
}
|
|
48737
48798
|
function installUpdatePromptHook(agentDir) {
|
|
48738
|
-
const hooksDir =
|
|
48739
|
-
|
|
48740
|
-
const scriptPath =
|
|
48799
|
+
const hooksDir = join86(agentDir, ".claude", "hooks");
|
|
48800
|
+
mkdirSync48(hooksDir, { recursive: true });
|
|
48801
|
+
const scriptPath = join86(hooksDir, HOOK_FILENAME);
|
|
48741
48802
|
const desired = updatePromptHookScript();
|
|
48742
48803
|
let installed = false;
|
|
48743
|
-
const existing =
|
|
48804
|
+
const existing = existsSync84(scriptPath) ? readFileSync73(scriptPath, "utf-8") : "";
|
|
48744
48805
|
if (existing !== desired) {
|
|
48745
|
-
|
|
48806
|
+
writeFileSync30(scriptPath, desired, { mode: 493 });
|
|
48746
48807
|
chmodSync12(scriptPath, 493);
|
|
48747
48808
|
installed = true;
|
|
48748
48809
|
} else {
|
|
@@ -48750,11 +48811,11 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48750
48811
|
chmodSync12(scriptPath, 493);
|
|
48751
48812
|
} catch {}
|
|
48752
48813
|
}
|
|
48753
|
-
const settingsPath =
|
|
48754
|
-
if (!
|
|
48814
|
+
const settingsPath = join86(agentDir, ".claude", "settings.json");
|
|
48815
|
+
if (!existsSync84(settingsPath)) {
|
|
48755
48816
|
return { scriptPath, settingsPath, installed };
|
|
48756
48817
|
}
|
|
48757
|
-
const raw =
|
|
48818
|
+
const raw = readFileSync73(settingsPath, "utf-8");
|
|
48758
48819
|
let parsed;
|
|
48759
48820
|
try {
|
|
48760
48821
|
parsed = JSON.parse(raw);
|
|
@@ -48790,7 +48851,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48790
48851
|
if (mutated) {
|
|
48791
48852
|
hooks.UserPromptSubmit = list2;
|
|
48792
48853
|
parsed.hooks = hooks;
|
|
48793
|
-
|
|
48854
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48794
48855
|
`, { mode: 384 });
|
|
48795
48856
|
installed = true;
|
|
48796
48857
|
} else if (!alreadyCorrect) {
|
|
@@ -48799,7 +48860,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48799
48860
|
});
|
|
48800
48861
|
hooks.UserPromptSubmit = list2;
|
|
48801
48862
|
parsed.hooks = hooks;
|
|
48802
|
-
|
|
48863
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48803
48864
|
`, { mode: 384 });
|
|
48804
48865
|
installed = true;
|
|
48805
48866
|
}
|
|
@@ -49183,8 +49244,8 @@ __export(exports_voice_sidecar_token, {
|
|
|
49183
49244
|
VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
|
|
49184
49245
|
});
|
|
49185
49246
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
49186
|
-
import { chmodSync as chmodSync13, chownSync as chownSync7, existsSync as
|
|
49187
|
-
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";
|
|
49188
49249
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
49189
49250
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
49190
49251
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
@@ -49218,8 +49279,8 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49218
49279
|
const envPath = composeEnvPath(composePath);
|
|
49219
49280
|
if (engine !== "local") {
|
|
49220
49281
|
try {
|
|
49221
|
-
if (
|
|
49222
|
-
const body =
|
|
49282
|
+
if (existsSync86(envPath)) {
|
|
49283
|
+
const body = readFileSync74(envPath, "utf-8");
|
|
49223
49284
|
if (body.includes(`${VOICE_SIDECAR_TOKEN_ENV}=`))
|
|
49224
49285
|
rmSync17(envPath);
|
|
49225
49286
|
}
|
|
@@ -49238,11 +49299,11 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49238
49299
|
if (!token)
|
|
49239
49300
|
return;
|
|
49240
49301
|
try {
|
|
49241
|
-
|
|
49302
|
+
mkdirSync49(dirname31(envPath), { recursive: true });
|
|
49242
49303
|
let body = "";
|
|
49243
49304
|
try {
|
|
49244
|
-
if (
|
|
49245
|
-
body =
|
|
49305
|
+
if (existsSync86(envPath))
|
|
49306
|
+
body = readFileSync74(envPath, "utf-8");
|
|
49246
49307
|
} catch {}
|
|
49247
49308
|
const line = `${VOICE_SIDECAR_TOKEN_ENV}=${token}`;
|
|
49248
49309
|
const keyRe = new RegExp(`^${VOICE_SIDECAR_TOKEN_ENV}=.*$`, "m");
|
|
@@ -49250,7 +49311,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49250
49311
|
`) ? body + `
|
|
49251
49312
|
` : body) + line + `
|
|
49252
49313
|
`;
|
|
49253
|
-
|
|
49314
|
+
writeFileSync31(envPath, next, {
|
|
49254
49315
|
encoding: "utf-8",
|
|
49255
49316
|
mode: 384
|
|
49256
49317
|
});
|
|
@@ -49302,11 +49363,11 @@ __export(exports_apply, {
|
|
|
49302
49363
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
|
|
49303
49364
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
49304
49365
|
});
|
|
49305
|
-
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";
|
|
49306
49367
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
49307
49368
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
49308
49369
|
import readline from "node:readline";
|
|
49309
|
-
import { dirname as
|
|
49370
|
+
import { dirname as dirname32, join as join88, resolve as resolve52 } from "node:path";
|
|
49310
49371
|
import { homedir as homedir49 } from "node:os";
|
|
49311
49372
|
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
49312
49373
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
@@ -49318,7 +49379,7 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49318
49379
|
return envPass;
|
|
49319
49380
|
try {
|
|
49320
49381
|
const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
|
|
49321
|
-
const blobPath =
|
|
49382
|
+
const blobPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49322
49383
|
const pass = readAutoUnlockFile2(blobPath);
|
|
49323
49384
|
return pass && pass.length > 0 ? pass : null;
|
|
49324
49385
|
} catch {
|
|
@@ -49327,10 +49388,10 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49327
49388
|
}
|
|
49328
49389
|
function materializeLitellmMasterKeyForBroker(masterKey, home2 = process.env.HOME ?? "/root") {
|
|
49329
49390
|
try {
|
|
49330
|
-
const stateDir =
|
|
49331
|
-
|
|
49332
|
-
const path9 =
|
|
49333
|
-
|
|
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() + `
|
|
49334
49395
|
`, { mode: 384 });
|
|
49335
49396
|
try {
|
|
49336
49397
|
chmodSync14(path9, 384);
|
|
@@ -49422,9 +49483,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49422
49483
|
const oauthAccount = config.auth?.active;
|
|
49423
49484
|
let pendingConfigEdits = false;
|
|
49424
49485
|
let configText = null;
|
|
49425
|
-
if (switchroomConfigPath &&
|
|
49486
|
+
if (switchroomConfigPath && existsSync87(switchroomConfigPath)) {
|
|
49426
49487
|
try {
|
|
49427
|
-
configText =
|
|
49488
|
+
configText = readFileSync75(switchroomConfigPath, "utf-8");
|
|
49428
49489
|
} catch (err) {
|
|
49429
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.
|
|
49430
49491
|
`));
|
|
@@ -49653,14 +49714,14 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49653
49714
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
49654
49715
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
49655
49716
|
if (isCustomPath && ctx.customVaultPath) {
|
|
49656
|
-
return
|
|
49717
|
+
return dirname32(ctx.customVaultPath);
|
|
49657
49718
|
}
|
|
49658
|
-
return
|
|
49719
|
+
return join88(homeDir, ".switchroom", "vault");
|
|
49659
49720
|
}
|
|
49660
49721
|
function inspectVaultBindMountDir(vaultDir) {
|
|
49661
|
-
if (!
|
|
49722
|
+
if (!existsSync87(vaultDir))
|
|
49662
49723
|
return { kind: "missing" };
|
|
49663
|
-
const entries =
|
|
49724
|
+
const entries = readdirSync31(vaultDir);
|
|
49664
49725
|
const unknown = [];
|
|
49665
49726
|
for (const name of entries) {
|
|
49666
49727
|
if (KNOWN_VAULT_ARTIFACT_NAMES.has(name))
|
|
@@ -49686,60 +49747,60 @@ function hasVaultRefs(value) {
|
|
|
49686
49747
|
async function ensureHostMountSources(config) {
|
|
49687
49748
|
const home2 = resolveHostHomeForCompose();
|
|
49688
49749
|
const dirs = [
|
|
49689
|
-
|
|
49690
|
-
|
|
49691
|
-
|
|
49692
|
-
|
|
49693
|
-
|
|
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")
|
|
49694
49755
|
];
|
|
49695
49756
|
for (const name of Object.keys(config.agents)) {
|
|
49696
|
-
dirs.push(
|
|
49697
|
-
dirs.push(
|
|
49698
|
-
dirs.push(
|
|
49699
|
-
dirs.push(
|
|
49700
|
-
if (
|
|
49701
|
-
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"));
|
|
49702
49763
|
}
|
|
49703
49764
|
}
|
|
49704
49765
|
for (const dir of dirs) {
|
|
49705
49766
|
await mkdir2(dir, { recursive: true });
|
|
49706
49767
|
}
|
|
49707
|
-
const autoUnlockPath =
|
|
49708
|
-
if (!
|
|
49709
|
-
|
|
49768
|
+
const autoUnlockPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49769
|
+
if (!existsSync87(autoUnlockPath)) {
|
|
49770
|
+
writeFileSync32(autoUnlockPath, "", { mode: 384 });
|
|
49710
49771
|
}
|
|
49711
|
-
const auditLogPath =
|
|
49712
|
-
if (!
|
|
49713
|
-
|
|
49772
|
+
const auditLogPath = join88(home2, ".switchroom", "vault-audit.log");
|
|
49773
|
+
if (!existsSync87(auditLogPath)) {
|
|
49774
|
+
writeFileSync32(auditLogPath, "", { mode: 420 });
|
|
49714
49775
|
}
|
|
49715
49776
|
const grantsDbDir = getGrantsDbDir(home2);
|
|
49716
|
-
|
|
49777
|
+
mkdirSync50(grantsDbDir, { recursive: true, mode: 448 });
|
|
49717
49778
|
migrateLegacyGrantsDbLocation(getGrantsDbPath(home2));
|
|
49718
|
-
const hostdAuditLogPath =
|
|
49719
|
-
if (!
|
|
49720
|
-
|
|
49779
|
+
const hostdAuditLogPath = join88(home2, ".switchroom", "host-control-audit.log");
|
|
49780
|
+
if (!existsSync87(hostdAuditLogPath)) {
|
|
49781
|
+
writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
|
|
49721
49782
|
}
|
|
49722
49783
|
for (const name of Object.keys(config.agents)) {
|
|
49723
|
-
const tokenPath =
|
|
49724
|
-
if (!
|
|
49725
|
-
|
|
49784
|
+
const tokenPath = join88(home2, ".switchroom", "agents", name, ".vault-token");
|
|
49785
|
+
if (!existsSync87(tokenPath)) {
|
|
49786
|
+
writeFileSync32(tokenPath, "", { mode: 384 });
|
|
49726
49787
|
}
|
|
49727
49788
|
try {
|
|
49728
49789
|
const uid = allocateAgentUid(name);
|
|
49729
49790
|
chownSync8(tokenPath, uid, uid);
|
|
49730
49791
|
} catch {}
|
|
49731
49792
|
}
|
|
49732
|
-
const fleetDir =
|
|
49793
|
+
const fleetDir = join88(home2, ".switchroom", "fleet");
|
|
49733
49794
|
await mkdir2(fleetDir, { recursive: true });
|
|
49734
|
-
const invariantsPath =
|
|
49795
|
+
const invariantsPath = join88(fleetDir, "switchroom-invariants.md");
|
|
49735
49796
|
const invariantsCanonical = renderFleetInvariants();
|
|
49736
|
-
const invariantsCurrent =
|
|
49797
|
+
const invariantsCurrent = existsSync87(invariantsPath) ? readFileSync75(invariantsPath, "utf-8") : null;
|
|
49737
49798
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
49738
|
-
|
|
49799
|
+
writeFileSync32(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
49739
49800
|
}
|
|
49740
|
-
const fleetClaudePath =
|
|
49741
|
-
if (!
|
|
49742
|
-
|
|
49801
|
+
const fleetClaudePath = join88(fleetDir, "CLAUDE.md");
|
|
49802
|
+
if (!existsSync87(fleetClaudePath)) {
|
|
49803
|
+
writeFileSync32(fleetClaudePath, renderFleetDefaultsClaudeMd(), {
|
|
49743
49804
|
mode: 420
|
|
49744
49805
|
});
|
|
49745
49806
|
}
|
|
@@ -49768,7 +49829,7 @@ function isInAgentContainer(vaultPresent, composeV2Present, env2 = process.env)
|
|
|
49768
49829
|
function runApplyPreflight(config, opts = {}) {
|
|
49769
49830
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
49770
49831
|
const detect = opts.detectComposeV2 ?? detectComposeV2;
|
|
49771
|
-
const vaultMissing = hasVaultRefs(config) && !
|
|
49832
|
+
const vaultMissing = hasVaultRefs(config) && !existsSync87(vaultPath);
|
|
49772
49833
|
const composeErr = detect();
|
|
49773
49834
|
if ((vaultMissing || composeErr) && isInAgentContainer(!vaultMissing, composeErr === null)) {
|
|
49774
49835
|
throw new Error(IN_AGENT_CONTAINER_APPLY_MSG);
|
|
@@ -49782,7 +49843,7 @@ function runApplyPreflight(config, opts = {}) {
|
|
|
49782
49843
|
detectAndReportLegacyGdriveSlots(vaultPath);
|
|
49783
49844
|
}
|
|
49784
49845
|
function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
49785
|
-
if (!
|
|
49846
|
+
if (!existsSync87(vaultPath))
|
|
49786
49847
|
return;
|
|
49787
49848
|
const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
49788
49849
|
if (!passphrase)
|
|
@@ -49823,17 +49884,17 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
49823
49884
|
}
|
|
49824
49885
|
function writeInstallTypeCache(homeDir = homedir49()) {
|
|
49825
49886
|
const ctx = detectInstallType();
|
|
49826
|
-
const dir =
|
|
49827
|
-
const out =
|
|
49887
|
+
const dir = join88(homeDir, ".switchroom");
|
|
49888
|
+
const out = join88(dir, "install-type.json");
|
|
49828
49889
|
const tmp = `${out}.tmp`;
|
|
49829
|
-
|
|
49890
|
+
mkdirSync50(dir, { recursive: true });
|
|
49830
49891
|
const payload = {
|
|
49831
49892
|
install_type: ctx.install_type,
|
|
49832
49893
|
detected_at: new Date().toISOString(),
|
|
49833
49894
|
source_paths: ctx.source_paths
|
|
49834
49895
|
};
|
|
49835
|
-
|
|
49836
|
-
|
|
49896
|
+
writeFileSync32(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
|
|
49897
|
+
renameSync21(tmp, out);
|
|
49837
49898
|
return out;
|
|
49838
49899
|
}
|
|
49839
49900
|
async function runApply(config, options, deps = {}, switchroomConfigPath) {
|
|
@@ -49891,17 +49952,17 @@ Applying switchroom config...
|
|
|
49891
49952
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
49892
49953
|
`));
|
|
49893
49954
|
try {
|
|
49894
|
-
installUpdatePromptHook(
|
|
49955
|
+
installUpdatePromptHook(join88(agentsDir, name));
|
|
49895
49956
|
} catch (hookErr) {
|
|
49896
49957
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
49897
49958
|
`));
|
|
49898
49959
|
}
|
|
49899
|
-
await refreshAgentConnectionHealth(config, name,
|
|
49960
|
+
await refreshAgentConnectionHealth(config, name, join88(agentsDir, name), {
|
|
49900
49961
|
vaultAclReader: connHealthVaultAclReader
|
|
49901
49962
|
});
|
|
49902
49963
|
try {
|
|
49903
49964
|
const uid = allocateAgentUid(name);
|
|
49904
|
-
alignAgentUid(name,
|
|
49965
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49905
49966
|
confirm: !options.nonInteractive,
|
|
49906
49967
|
writeOut
|
|
49907
49968
|
});
|
|
@@ -49946,7 +50007,7 @@ Applying switchroom config...
|
|
|
49946
50007
|
for (const name of agentNames) {
|
|
49947
50008
|
try {
|
|
49948
50009
|
const uid = allocateAgentUid(name);
|
|
49949
|
-
alignAgentUid(name,
|
|
50010
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49950
50011
|
confirm: !options.nonInteractive,
|
|
49951
50012
|
writeOut
|
|
49952
50013
|
});
|
|
@@ -50284,18 +50345,18 @@ function copyExampleConfig2(name) {
|
|
|
50284
50345
|
throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
|
|
50285
50346
|
}
|
|
50286
50347
|
const dest = resolve52(process.cwd(), "switchroom.yaml");
|
|
50287
|
-
if (
|
|
50348
|
+
if (existsSync87(dest)) {
|
|
50288
50349
|
console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
|
|
50289
50350
|
return;
|
|
50290
50351
|
}
|
|
50291
50352
|
const embedded = EMBEDDED_EXAMPLES[name];
|
|
50292
50353
|
if (embedded !== undefined) {
|
|
50293
|
-
|
|
50354
|
+
writeFileSync32(dest, embedded, { encoding: "utf8" });
|
|
50294
50355
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
50295
50356
|
return;
|
|
50296
50357
|
}
|
|
50297
50358
|
const exampleFile = resolve52(import.meta.dirname, `../../examples/${name}.yaml`);
|
|
50298
|
-
if (!
|
|
50359
|
+
if (!existsSync87(exampleFile)) {
|
|
50299
50360
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
50300
50361
|
}
|
|
50301
50362
|
copyFileSync12(exampleFile, dest);
|
|
@@ -50306,8 +50367,8 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
50306
50367
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
50307
50368
|
const unwritable = [];
|
|
50308
50369
|
for (const name of targets) {
|
|
50309
|
-
const startSh =
|
|
50310
|
-
if (!
|
|
50370
|
+
const startSh = join88(agentsDir, name, "start.sh");
|
|
50371
|
+
if (!existsSync87(startSh))
|
|
50311
50372
|
continue;
|
|
50312
50373
|
try {
|
|
50313
50374
|
accessSync3(startSh, fsConstants6.W_OK);
|
|
@@ -50512,7 +50573,7 @@ var init_apply = __esm(() => {
|
|
|
50512
50573
|
switchroom: switchroom_default,
|
|
50513
50574
|
minimal: minimal_default
|
|
50514
50575
|
};
|
|
50515
|
-
DEFAULT_COMPOSE_PATH2 =
|
|
50576
|
+
DEFAULT_COMPOSE_PATH2 = join88(homedir49(), ".switchroom", "compose", "docker-compose.yml");
|
|
50516
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.
|
|
50517
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.";
|
|
50518
50579
|
SELF_ELEVATE_PRESERVED_ENV = [
|
|
@@ -59823,49 +59884,49 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59823
59884
|
schemelessOptions.skipEscape = true;
|
|
59824
59885
|
return serialize(resolved, schemelessOptions);
|
|
59825
59886
|
}
|
|
59826
|
-
function resolveComponent(base,
|
|
59887
|
+
function resolveComponent(base, relative6, options, skipNormalization) {
|
|
59827
59888
|
const target = {};
|
|
59828
59889
|
if (!skipNormalization) {
|
|
59829
59890
|
base = parse6(serialize(base, options), options);
|
|
59830
|
-
|
|
59891
|
+
relative6 = parse6(serialize(relative6, options), options);
|
|
59831
59892
|
}
|
|
59832
59893
|
options = options || {};
|
|
59833
|
-
if (!options.tolerant &&
|
|
59834
|
-
target.scheme =
|
|
59835
|
-
target.userinfo =
|
|
59836
|
-
target.host =
|
|
59837
|
-
target.port =
|
|
59838
|
-
target.path = removeDotSegments(
|
|
59839
|
-
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;
|
|
59840
59901
|
} else {
|
|
59841
|
-
if (
|
|
59842
|
-
target.userinfo =
|
|
59843
|
-
target.host =
|
|
59844
|
-
target.port =
|
|
59845
|
-
target.path = removeDotSegments(
|
|
59846
|
-
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;
|
|
59847
59908
|
} else {
|
|
59848
|
-
if (!
|
|
59909
|
+
if (!relative6.path) {
|
|
59849
59910
|
target.path = base.path;
|
|
59850
|
-
if (
|
|
59851
|
-
target.query =
|
|
59911
|
+
if (relative6.query !== undefined) {
|
|
59912
|
+
target.query = relative6.query;
|
|
59852
59913
|
} else {
|
|
59853
59914
|
target.query = base.query;
|
|
59854
59915
|
}
|
|
59855
59916
|
} else {
|
|
59856
|
-
if (
|
|
59857
|
-
target.path = removeDotSegments(
|
|
59917
|
+
if (relative6.path[0] === "/") {
|
|
59918
|
+
target.path = removeDotSegments(relative6.path);
|
|
59858
59919
|
} else {
|
|
59859
59920
|
if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
|
|
59860
|
-
target.path = "/" +
|
|
59921
|
+
target.path = "/" + relative6.path;
|
|
59861
59922
|
} else if (!base.path) {
|
|
59862
|
-
target.path =
|
|
59923
|
+
target.path = relative6.path;
|
|
59863
59924
|
} else {
|
|
59864
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
59925
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
|
|
59865
59926
|
}
|
|
59866
59927
|
target.path = removeDotSegments(target.path);
|
|
59867
59928
|
}
|
|
59868
|
-
target.query =
|
|
59929
|
+
target.query = relative6.query;
|
|
59869
59930
|
}
|
|
59870
59931
|
target.userinfo = base.userinfo;
|
|
59871
59932
|
target.host = base.host;
|
|
@@ -59873,7 +59934,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59873
59934
|
}
|
|
59874
59935
|
target.scheme = base.scheme;
|
|
59875
59936
|
}
|
|
59876
|
-
target.fragment =
|
|
59937
|
+
target.fragment = relative6.fragment;
|
|
59877
59938
|
return target;
|
|
59878
59939
|
}
|
|
59879
59940
|
function equal(uriA, uriB, options) {
|
|
@@ -63961,7 +64022,7 @@ __export(exports_server2, {
|
|
|
63961
64022
|
TOOLS: () => TOOLS2
|
|
63962
64023
|
});
|
|
63963
64024
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
63964
|
-
import { existsSync as
|
|
64025
|
+
import { existsSync as existsSync96, readFileSync as readFileSync84 } from "node:fs";
|
|
63965
64026
|
function selfSocketPath() {
|
|
63966
64027
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
63967
64028
|
}
|
|
@@ -63988,7 +64049,7 @@ async function dispatchTool2(name, args) {
|
|
|
63988
64049
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
63989
64050
|
}
|
|
63990
64051
|
const sockPath = selfSocketPath();
|
|
63991
|
-
if (!
|
|
64052
|
+
if (!existsSync96(sockPath)) {
|
|
63992
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.`);
|
|
63993
64054
|
}
|
|
63994
64055
|
let req;
|
|
@@ -64239,18 +64300,18 @@ function resolveAuditLogPath() {
|
|
|
64239
64300
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
64240
64301
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
64241
64302
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
64242
|
-
if (
|
|
64303
|
+
if (existsSync96(bindMounted))
|
|
64243
64304
|
return bindMounted;
|
|
64244
64305
|
return defaultAuditLogPath2();
|
|
64245
64306
|
}
|
|
64246
64307
|
function getLastUpdateApplyStatus() {
|
|
64247
64308
|
const path9 = resolveAuditLogPath();
|
|
64248
|
-
if (!
|
|
64309
|
+
if (!existsSync96(path9)) {
|
|
64249
64310
|
return errorText2(`get_status: audit log not found at ${path9}. No update_apply has run yet?`);
|
|
64250
64311
|
}
|
|
64251
64312
|
let raw;
|
|
64252
64313
|
try {
|
|
64253
|
-
raw =
|
|
64314
|
+
raw = readFileSync84(path9, "utf-8");
|
|
64254
64315
|
} catch (err2) {
|
|
64255
64316
|
return errorText2(`get_status: failed to read audit log at ${path9}: ${err2.message}`);
|
|
64256
64317
|
}
|
|
@@ -65000,14 +65061,14 @@ var init_header_passthrough_guard = __esm(() => {
|
|
|
65000
65061
|
});
|
|
65001
65062
|
|
|
65002
65063
|
// src/fleet-health/litellm-config-sensor.ts
|
|
65003
|
-
import { readFileSync as
|
|
65064
|
+
import { readFileSync as readFileSync86, existsSync as existsSync99 } from "node:fs";
|
|
65004
65065
|
function resolveLitellmConfigPath(explicit) {
|
|
65005
65066
|
return explicit ?? process.env.LITELLM_CONFIG_PATH ?? DEFAULT_LITELLM_CONFIG_PATH;
|
|
65006
65067
|
}
|
|
65007
65068
|
function scanLitellmConfig(opts = {}) {
|
|
65008
65069
|
const path9 = resolveLitellmConfigPath(opts.path);
|
|
65009
|
-
const exists = opts.existsFn ??
|
|
65010
|
-
const read = opts.readFn ?? ((p) =>
|
|
65070
|
+
const exists = opts.existsFn ?? existsSync99;
|
|
65071
|
+
const read = opts.readFn ?? ((p) => readFileSync86(p, "utf-8"));
|
|
65011
65072
|
const log = opts.log ?? (() => {});
|
|
65012
65073
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
65013
65074
|
if (!exists(path9)) {
|
|
@@ -65062,23 +65123,23 @@ __export(exports_scan, {
|
|
|
65062
65123
|
ledgerPathForBase: () => ledgerPathForBase
|
|
65063
65124
|
});
|
|
65064
65125
|
import {
|
|
65065
|
-
readFileSync as
|
|
65066
|
-
readdirSync as
|
|
65067
|
-
existsSync as
|
|
65068
|
-
mkdirSync as
|
|
65069
|
-
writeFileSync as
|
|
65126
|
+
readFileSync as readFileSync87,
|
|
65127
|
+
readdirSync as readdirSync39,
|
|
65128
|
+
existsSync as existsSync100,
|
|
65129
|
+
mkdirSync as mkdirSync57,
|
|
65130
|
+
writeFileSync as writeFileSync38
|
|
65070
65131
|
} from "node:fs";
|
|
65071
|
-
import { resolve as resolve57, dirname as
|
|
65132
|
+
import { resolve as resolve57, dirname as dirname35 } from "node:path";
|
|
65072
65133
|
import { homedir as homedir58 } from "node:os";
|
|
65073
65134
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir58()) {
|
|
65074
65135
|
return resolve57(home2, ".switchroom");
|
|
65075
65136
|
}
|
|
65076
65137
|
function listAgents(base) {
|
|
65077
65138
|
const dir = resolve57(base, "agents");
|
|
65078
|
-
if (!
|
|
65139
|
+
if (!existsSync100(dir))
|
|
65079
65140
|
return [];
|
|
65080
65141
|
try {
|
|
65081
|
-
return
|
|
65142
|
+
return readdirSync39(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
65082
65143
|
} catch {
|
|
65083
65144
|
return [];
|
|
65084
65145
|
}
|
|
@@ -65104,16 +65165,16 @@ function runScan(opts = {}) {
|
|
|
65104
65165
|
let gwText = "";
|
|
65105
65166
|
let sawArtifact = false;
|
|
65106
65167
|
try {
|
|
65107
|
-
if (
|
|
65108
|
-
turnsText =
|
|
65168
|
+
if (existsSync100(turnsPath)) {
|
|
65169
|
+
turnsText = readFileSync87(turnsPath, "utf-8");
|
|
65109
65170
|
sawArtifact = true;
|
|
65110
65171
|
}
|
|
65111
65172
|
} catch (e) {
|
|
65112
65173
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
65113
65174
|
}
|
|
65114
65175
|
try {
|
|
65115
|
-
if (
|
|
65116
|
-
gwText =
|
|
65176
|
+
if (existsSync100(gwPath)) {
|
|
65177
|
+
gwText = readFileSync87(gwPath, "utf-8");
|
|
65117
65178
|
sawArtifact = true;
|
|
65118
65179
|
}
|
|
65119
65180
|
} catch (e) {
|
|
@@ -65155,20 +65216,20 @@ function runScan(opts = {}) {
|
|
|
65155
65216
|
function readLedgerIfPresent(base) {
|
|
65156
65217
|
const path9 = ledgerPathForBase(base);
|
|
65157
65218
|
try {
|
|
65158
|
-
if (!
|
|
65219
|
+
if (!existsSync100(path9))
|
|
65159
65220
|
return null;
|
|
65160
|
-
return JSON.parse(
|
|
65221
|
+
return JSON.parse(readFileSync87(path9, "utf-8"));
|
|
65161
65222
|
} catch {
|
|
65162
65223
|
return null;
|
|
65163
65224
|
}
|
|
65164
65225
|
}
|
|
65165
65226
|
function ledgerPathForBase(base) {
|
|
65166
|
-
return fleetHealthLedgerPath(
|
|
65227
|
+
return fleetHealthLedgerPath(dirname35(base));
|
|
65167
65228
|
}
|
|
65168
65229
|
function writeLedger(base, ledger) {
|
|
65169
65230
|
const path9 = ledgerPathForBase(base);
|
|
65170
|
-
|
|
65171
|
-
|
|
65231
|
+
mkdirSync57(dirname35(path9), { recursive: true });
|
|
65232
|
+
writeFileSync38(path9, JSON.stringify(ledger, null, 2) + `
|
|
65172
65233
|
`, "utf-8");
|
|
65173
65234
|
return path9;
|
|
65174
65235
|
}
|
|
@@ -72174,7 +72235,7 @@ init_audit_log();
|
|
|
72174
72235
|
init_test_isolation_guard();
|
|
72175
72236
|
import * as net3 from "node:net";
|
|
72176
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";
|
|
72177
|
-
import { dirname as
|
|
72238
|
+
import { dirname as dirname15, resolve as resolve29, basename as basename7 } from "node:path";
|
|
72178
72239
|
import * as os4 from "node:os";
|
|
72179
72240
|
import * as path4 from "node:path";
|
|
72180
72241
|
|
|
@@ -74550,7 +74611,7 @@ class VaultBroker {
|
|
|
74550
74611
|
this.passphrase = this.testOpts._testPassphrase;
|
|
74551
74612
|
}
|
|
74552
74613
|
process.umask(63);
|
|
74553
|
-
const parentDir =
|
|
74614
|
+
const parentDir = dirname15(this.socketPath);
|
|
74554
74615
|
mkdirSync25(parentDir, { recursive: true, mode: 448 });
|
|
74555
74616
|
try {
|
|
74556
74617
|
chmodSync9(parentDir, 448);
|
|
@@ -76095,15 +76156,15 @@ class VaultBroker {
|
|
|
76095
76156
|
}
|
|
76096
76157
|
}
|
|
76097
76158
|
function detectVaultLayoutDrift(vaultPath) {
|
|
76098
|
-
const dir =
|
|
76159
|
+
const dir = dirname15(vaultPath);
|
|
76099
76160
|
if (basename7(dir) !== "vault")
|
|
76100
76161
|
return;
|
|
76101
76162
|
if (basename7(vaultPath) !== "vault.enc")
|
|
76102
76163
|
return;
|
|
76103
|
-
const switchroomDir =
|
|
76164
|
+
const switchroomDir = dirname15(dir);
|
|
76104
76165
|
if (basename7(switchroomDir) !== ".switchroom")
|
|
76105
76166
|
return;
|
|
76106
|
-
const home2 =
|
|
76167
|
+
const home2 = dirname15(switchroomDir);
|
|
76107
76168
|
const result = inspectVaultLayout(home2);
|
|
76108
76169
|
if (result.kind === "divergent") {
|
|
76109
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.`);
|
|
@@ -79584,7 +79645,7 @@ import {
|
|
|
79584
79645
|
writeSync as writeSync8,
|
|
79585
79646
|
constants as fsConstants3
|
|
79586
79647
|
} from "node:fs";
|
|
79587
|
-
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";
|
|
79588
79649
|
import { homedir as homedir28 } from "node:os";
|
|
79589
79650
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
79590
79651
|
|
|
@@ -83045,7 +83106,7 @@ function resolveWebToken() {
|
|
|
83045
83106
|
return existing;
|
|
83046
83107
|
}
|
|
83047
83108
|
const token = randomBytes11(32).toString("hex");
|
|
83048
|
-
mkdirSync31(
|
|
83109
|
+
mkdirSync31(dirname18(tokenPath), { recursive: true, mode: 448 });
|
|
83049
83110
|
try {
|
|
83050
83111
|
const fd = openSync11(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
|
|
83051
83112
|
try {
|
|
@@ -83632,7 +83693,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
83632
83693
|
} catch {
|
|
83633
83694
|
return new Response("Not Found", { status: 404 });
|
|
83634
83695
|
}
|
|
83635
|
-
const rel =
|
|
83696
|
+
const rel = relative3(uiDir, realFullPath);
|
|
83636
83697
|
if (rel.startsWith("..") || resolve34(uiDir, rel) !== realFullPath) {
|
|
83637
83698
|
return new Response("Forbidden", { status: 403 });
|
|
83638
83699
|
}
|
|
@@ -83762,7 +83823,7 @@ init_loader();
|
|
|
83762
83823
|
|
|
83763
83824
|
// src/web/startup-guard.ts
|
|
83764
83825
|
import { existsSync as existsSync58, readFileSync as readFileSync53, writeFileSync as writeFileSync18, mkdirSync as mkdirSync32, statSync as statSync33, unlinkSync as unlinkSync13 } from "node:fs";
|
|
83765
|
-
import { dirname as
|
|
83826
|
+
import { dirname as dirname19 } from "node:path";
|
|
83766
83827
|
function detectConfigMountFault(configPath, deps = {}) {
|
|
83767
83828
|
const stat = deps.stat ?? ((p) => statSync33(p));
|
|
83768
83829
|
let st;
|
|
@@ -83809,7 +83870,7 @@ function readCrashState(statePath) {
|
|
|
83809
83870
|
}
|
|
83810
83871
|
function writeCrashState(statePath, state) {
|
|
83811
83872
|
try {
|
|
83812
|
-
mkdirSync32(
|
|
83873
|
+
mkdirSync32(dirname19(statePath), { recursive: true });
|
|
83813
83874
|
writeFileSync18(statePath, JSON.stringify(state), { mode: 384 });
|
|
83814
83875
|
} catch {}
|
|
83815
83876
|
}
|
|
@@ -83905,7 +83966,7 @@ init_atomic();
|
|
|
83905
83966
|
init_loader();
|
|
83906
83967
|
init_scaffold();
|
|
83907
83968
|
import { existsSync as existsSync59, copyFileSync as copyFileSync9, readFileSync as readFileSync54, mkdirSync as mkdirSync33, statSync as statSync34 } from "node:fs";
|
|
83908
|
-
import { resolve as resolve35, dirname as
|
|
83969
|
+
import { resolve as resolve35, dirname as dirname20 } from "node:path";
|
|
83909
83970
|
init_state();
|
|
83910
83971
|
init_vault();
|
|
83911
83972
|
init_manager();
|
|
@@ -84217,7 +84278,7 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
84217
84278
|
if (!existsSync59(srcFile)) {
|
|
84218
84279
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
84219
84280
|
}
|
|
84220
|
-
mkdirSync33(
|
|
84281
|
+
mkdirSync33(dirname20(destFile), { recursive: true });
|
|
84221
84282
|
copyFileSync9(srcFile, destFile);
|
|
84222
84283
|
console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
|
|
84223
84284
|
console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
|
|
@@ -84997,9 +85058,9 @@ init_source();
|
|
|
84997
85058
|
init_loader();
|
|
84998
85059
|
init_lifecycle();
|
|
84999
85060
|
init_compose_env();
|
|
85000
|
-
import {
|
|
85061
|
+
import { existsSync as existsSync68, mkdirSync as mkdirSync36, readFileSync as readFileSync61, realpathSync as realpathSync6, statSync as statSync40, chownSync as chownSync5 } from "node:fs";
|
|
85001
85062
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
85002
|
-
import { join as
|
|
85063
|
+
import { join as join70, dirname as dirname23, resolve as resolve40 } from "node:path";
|
|
85003
85064
|
import { homedir as homedir40 } from "node:os";
|
|
85004
85065
|
|
|
85005
85066
|
// src/cli/release-yaml.ts
|
|
@@ -85090,10 +85151,132 @@ ${lines.join(`
|
|
|
85090
85151
|
|
|
85091
85152
|
// src/cli/update.ts
|
|
85092
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();
|
|
85093
85276
|
function defaultPersistPin(configPath) {
|
|
85094
85277
|
return (pin) => {
|
|
85095
85278
|
const path5 = configPath ?? findConfigFile();
|
|
85096
|
-
const before =
|
|
85279
|
+
const before = readFileSync61(path5, "utf8");
|
|
85097
85280
|
const after = setReleasePinInConfig(before, pin);
|
|
85098
85281
|
if (after === before)
|
|
85099
85282
|
return;
|
|
@@ -85107,18 +85290,18 @@ function defaultPersistPin(configPath) {
|
|
|
85107
85290
|
} catch {}
|
|
85108
85291
|
};
|
|
85109
85292
|
}
|
|
85110
|
-
var DEFAULT_COMPOSE_PATH =
|
|
85293
|
+
var DEFAULT_COMPOSE_PATH = join70(homedir40(), ".switchroom", "compose", "docker-compose.yml");
|
|
85111
85294
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
85112
|
-
let dir =
|
|
85295
|
+
let dir = dirname23(scriptPath);
|
|
85113
85296
|
for (let i = 0;i < 12; i++) {
|
|
85114
|
-
if (
|
|
85297
|
+
if (existsSync68(join70(dir, ".git"))) {
|
|
85115
85298
|
try {
|
|
85116
|
-
const pkg = JSON.parse(
|
|
85299
|
+
const pkg = JSON.parse(readFileSync61(join70(dir, "package.json"), "utf-8"));
|
|
85117
85300
|
if (pkg.name === "switchroom")
|
|
85118
85301
|
return true;
|
|
85119
85302
|
} catch {}
|
|
85120
85303
|
}
|
|
85121
|
-
const parent =
|
|
85304
|
+
const parent = dirname23(dir);
|
|
85122
85305
|
if (parent === dir)
|
|
85123
85306
|
break;
|
|
85124
85307
|
dir = parent;
|
|
@@ -85198,7 +85381,7 @@ function planUpdate(opts) {
|
|
|
85198
85381
|
steps.push({
|
|
85199
85382
|
name: "pull-images",
|
|
85200
85383
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
85201
|
-
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,
|
|
85202
85385
|
run: () => {
|
|
85203
85386
|
const r = runner("docker", [
|
|
85204
85387
|
"compose",
|
|
@@ -85324,23 +85507,48 @@ function planUpdate(opts) {
|
|
|
85324
85507
|
return;
|
|
85325
85508
|
}
|
|
85326
85509
|
const source = resolve40(import.meta.dirname, "../../skills");
|
|
85327
|
-
const dest =
|
|
85328
|
-
if (!
|
|
85510
|
+
const dest = join70(homedir40(), ".switchroom", "skills", "_bundled");
|
|
85511
|
+
if (!existsSync68(source)) {
|
|
85329
85512
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
85330
85513
|
`);
|
|
85331
85514
|
return;
|
|
85332
85515
|
}
|
|
85333
85516
|
try {
|
|
85334
|
-
|
|
85335
|
-
|
|
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
|
+
`);
|
|
85336
85530
|
}
|
|
85337
|
-
mkdirSync35(dirname21(dest), { recursive: true });
|
|
85338
|
-
cpSync2(source, dest, { recursive: true, dereference: false });
|
|
85339
85531
|
} catch (err) {
|
|
85340
85532
|
throw new Error(`sync-bundled-skills failed: ${err.message}`);
|
|
85341
85533
|
}
|
|
85342
85534
|
}
|
|
85343
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
|
+
});
|
|
85344
85552
|
steps.push({
|
|
85345
85553
|
name: "stamp-restart-marker",
|
|
85346
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',
|
|
@@ -85370,7 +85578,7 @@ function planUpdate(opts) {
|
|
|
85370
85578
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
85371
85579
|
run: () => {
|
|
85372
85580
|
try {
|
|
85373
|
-
const composeText =
|
|
85581
|
+
const composeText = readFileSync61(composePath, "utf8");
|
|
85374
85582
|
const pf = validateBindSources(composeText);
|
|
85375
85583
|
if (!pf.ok)
|
|
85376
85584
|
throw new Error(formatPreflightError(pf));
|
|
@@ -85447,12 +85655,12 @@ function defaultStatusProbe(composePath) {
|
|
|
85447
85655
|
try {
|
|
85448
85656
|
cliBuiltAt = new Date(statSync40(scriptPath).mtimeMs).toISOString();
|
|
85449
85657
|
} catch {}
|
|
85450
|
-
let dir =
|
|
85658
|
+
let dir = dirname23(scriptPath);
|
|
85451
85659
|
for (let i = 0;i < 8; i++) {
|
|
85452
|
-
const pkgPath =
|
|
85453
|
-
if (
|
|
85660
|
+
const pkgPath = join70(dir, "package.json");
|
|
85661
|
+
if (existsSync68(pkgPath)) {
|
|
85454
85662
|
try {
|
|
85455
|
-
const pkg = JSON.parse(
|
|
85663
|
+
const pkg = JSON.parse(readFileSync61(pkgPath, "utf-8"));
|
|
85456
85664
|
if (typeof pkg.version === "string")
|
|
85457
85665
|
cliVersion = pkg.version;
|
|
85458
85666
|
} catch (err) {
|
|
@@ -85460,7 +85668,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85460
85668
|
}
|
|
85461
85669
|
break;
|
|
85462
85670
|
}
|
|
85463
|
-
const parent =
|
|
85671
|
+
const parent = dirname23(dir);
|
|
85464
85672
|
if (parent === dir)
|
|
85465
85673
|
break;
|
|
85466
85674
|
dir = parent;
|
|
@@ -85473,7 +85681,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85473
85681
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
85474
85682
|
}
|
|
85475
85683
|
const services = [];
|
|
85476
|
-
if (!
|
|
85684
|
+
if (!existsSync68(composePath)) {
|
|
85477
85685
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
85478
85686
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
85479
85687
|
}
|
|
@@ -85669,7 +85877,7 @@ function registerUpdateCommand(program3) {
|
|
|
85669
85877
|
// src/cli/rollout.ts
|
|
85670
85878
|
init_helpers();
|
|
85671
85879
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
85672
|
-
import { readFileSync as
|
|
85880
|
+
import { readFileSync as readFileSync62, chownSync as chownSync6, statSync as statSync41 } from "node:fs";
|
|
85673
85881
|
import { homedir as homedir41 } from "node:os";
|
|
85674
85882
|
init_operator_uid();
|
|
85675
85883
|
init_atomic();
|
|
@@ -85955,7 +86163,7 @@ function resolveRollbackTarget(auditLogPath) {
|
|
|
85955
86163
|
const logPath = auditLogPath ?? defaultAuditLogPath2(homedir41());
|
|
85956
86164
|
let raw;
|
|
85957
86165
|
try {
|
|
85958
|
-
raw =
|
|
86166
|
+
raw = readFileSync62(logPath, "utf8");
|
|
85959
86167
|
} catch {
|
|
85960
86168
|
return null;
|
|
85961
86169
|
}
|
|
@@ -86073,7 +86281,7 @@ function registerRolloutCommand(program3) {
|
|
|
86073
86281
|
`),
|
|
86074
86282
|
webImageTag: () => deployedImageTag("switchroom-web"),
|
|
86075
86283
|
persistPin: (pin) => {
|
|
86076
|
-
const before =
|
|
86284
|
+
const before = readFileSync62(configPath, "utf8");
|
|
86077
86285
|
const after = setReleasePinInConfig(before, pin);
|
|
86078
86286
|
if (after === before)
|
|
86079
86287
|
return;
|
|
@@ -86151,8 +86359,8 @@ init_helpers();
|
|
|
86151
86359
|
init_lifecycle();
|
|
86152
86360
|
init_resolve_version();
|
|
86153
86361
|
import { execSync as execSync3 } from "node:child_process";
|
|
86154
|
-
import { existsSync as
|
|
86155
|
-
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";
|
|
86156
86364
|
function getClaudeCodeVersion() {
|
|
86157
86365
|
try {
|
|
86158
86366
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -86202,16 +86410,16 @@ function formatUptime3(timestamp) {
|
|
|
86202
86410
|
function locateSwitchroomInstallDir() {
|
|
86203
86411
|
let dir = import.meta.dirname;
|
|
86204
86412
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
86205
|
-
const pkgPath =
|
|
86206
|
-
if (
|
|
86413
|
+
const pkgPath = join71(dir, "package.json");
|
|
86414
|
+
if (existsSync69(pkgPath)) {
|
|
86207
86415
|
try {
|
|
86208
|
-
const pkg = JSON.parse(
|
|
86209
|
-
if (pkg.name === "switchroom" &&
|
|
86416
|
+
const pkg = JSON.parse(readFileSync63(pkgPath, "utf-8"));
|
|
86417
|
+
if (pkg.name === "switchroom" && existsSync69(join71(dir, ".git"))) {
|
|
86210
86418
|
return dir;
|
|
86211
86419
|
}
|
|
86212
86420
|
} catch {}
|
|
86213
86421
|
}
|
|
86214
|
-
dir =
|
|
86422
|
+
dir = dirname24(dir);
|
|
86215
86423
|
}
|
|
86216
86424
|
return null;
|
|
86217
86425
|
}
|
|
@@ -86384,29 +86592,29 @@ import { resolve as resolve42 } from "node:path";
|
|
|
86384
86592
|
|
|
86385
86593
|
// src/agents/session-retention.ts
|
|
86386
86594
|
import {
|
|
86387
|
-
existsSync as
|
|
86388
|
-
readdirSync as
|
|
86595
|
+
existsSync as existsSync70,
|
|
86596
|
+
readdirSync as readdirSync25,
|
|
86389
86597
|
statSync as statSync42,
|
|
86390
86598
|
unlinkSync as unlinkSync14
|
|
86391
86599
|
} from "node:fs";
|
|
86392
|
-
import { join as
|
|
86600
|
+
import { join as join72 } from "node:path";
|
|
86393
86601
|
var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
|
|
86394
86602
|
var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
86395
86603
|
var MIN_KEEP = 2;
|
|
86396
86604
|
function collectSessionJsonl(claudeConfigDir) {
|
|
86397
|
-
const projects =
|
|
86398
|
-
if (!
|
|
86605
|
+
const projects = join72(claudeConfigDir, "projects");
|
|
86606
|
+
if (!existsSync70(projects))
|
|
86399
86607
|
return [];
|
|
86400
86608
|
const found = [];
|
|
86401
86609
|
const walk2 = (dir) => {
|
|
86402
86610
|
let entries;
|
|
86403
86611
|
try {
|
|
86404
|
-
entries =
|
|
86612
|
+
entries = readdirSync25(dir);
|
|
86405
86613
|
} catch {
|
|
86406
86614
|
return;
|
|
86407
86615
|
}
|
|
86408
86616
|
for (const name of entries) {
|
|
86409
|
-
const full =
|
|
86617
|
+
const full = join72(dir, name);
|
|
86410
86618
|
let st;
|
|
86411
86619
|
try {
|
|
86412
86620
|
st = statSync42(full);
|
|
@@ -86526,18 +86734,18 @@ function registerHandoffCommand(program3) {
|
|
|
86526
86734
|
// src/issues/store.ts
|
|
86527
86735
|
import {
|
|
86528
86736
|
closeSync as closeSync12,
|
|
86529
|
-
existsSync as
|
|
86530
|
-
mkdirSync as
|
|
86737
|
+
existsSync as existsSync71,
|
|
86738
|
+
mkdirSync as mkdirSync37,
|
|
86531
86739
|
openSync as openSync12,
|
|
86532
|
-
readdirSync as
|
|
86533
|
-
readFileSync as
|
|
86534
|
-
renameSync as
|
|
86740
|
+
readdirSync as readdirSync26,
|
|
86741
|
+
readFileSync as readFileSync64,
|
|
86742
|
+
renameSync as renameSync17,
|
|
86535
86743
|
statSync as statSync43,
|
|
86536
86744
|
unlinkSync as unlinkSync15,
|
|
86537
|
-
writeFileSync as
|
|
86745
|
+
writeFileSync as writeFileSync20,
|
|
86538
86746
|
writeSync as writeSync9
|
|
86539
86747
|
} from "node:fs";
|
|
86540
|
-
import { join as
|
|
86748
|
+
import { join as join73 } from "node:path";
|
|
86541
86749
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
86542
86750
|
import { execSync as execSync4 } from "node:child_process";
|
|
86543
86751
|
|
|
@@ -86968,12 +87176,12 @@ function redactedMarker(ruleId) {
|
|
|
86968
87176
|
var ISSUES_FILE = "issues.jsonl";
|
|
86969
87177
|
var ISSUES_LOCK = "issues.lock";
|
|
86970
87178
|
function readAll(stateDir) {
|
|
86971
|
-
const path5 =
|
|
86972
|
-
if (!
|
|
87179
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87180
|
+
if (!existsSync71(path5))
|
|
86973
87181
|
return [];
|
|
86974
87182
|
let raw;
|
|
86975
87183
|
try {
|
|
86976
|
-
raw =
|
|
87184
|
+
raw = readFileSync64(path5, "utf-8");
|
|
86977
87185
|
} catch {
|
|
86978
87186
|
return [];
|
|
86979
87187
|
}
|
|
@@ -87046,7 +87254,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
87046
87254
|
});
|
|
87047
87255
|
}
|
|
87048
87256
|
function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
87049
|
-
if (!
|
|
87257
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87050
87258
|
return 0;
|
|
87051
87259
|
return withLock(stateDir, () => {
|
|
87052
87260
|
const all = readAll(stateDir);
|
|
@@ -87064,7 +87272,7 @@ function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
87064
87272
|
});
|
|
87065
87273
|
}
|
|
87066
87274
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
87067
|
-
if (!
|
|
87275
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87068
87276
|
return 0;
|
|
87069
87277
|
return withLock(stateDir, () => {
|
|
87070
87278
|
const all = readAll(stateDir);
|
|
@@ -87082,7 +87290,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
87082
87290
|
});
|
|
87083
87291
|
}
|
|
87084
87292
|
function prune(stateDir, opts = {}) {
|
|
87085
|
-
if (!
|
|
87293
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87086
87294
|
return 0;
|
|
87087
87295
|
return withLock(stateDir, () => {
|
|
87088
87296
|
const all = readAll(stateDir);
|
|
@@ -87112,24 +87320,24 @@ function prune(stateDir, opts = {}) {
|
|
|
87112
87320
|
});
|
|
87113
87321
|
}
|
|
87114
87322
|
function ensureDir(stateDir) {
|
|
87115
|
-
|
|
87323
|
+
mkdirSync37(stateDir, { recursive: true });
|
|
87116
87324
|
}
|
|
87117
87325
|
function writeAll(stateDir, events) {
|
|
87118
|
-
const path5 =
|
|
87326
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87119
87327
|
sweepOrphanTmpFiles(stateDir);
|
|
87120
87328
|
const tmp = `${path5}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
|
|
87121
87329
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
87122
87330
|
`) + `
|
|
87123
87331
|
`;
|
|
87124
|
-
|
|
87125
|
-
|
|
87332
|
+
writeFileSync20(tmp, body, "utf-8");
|
|
87333
|
+
renameSync17(tmp, path5);
|
|
87126
87334
|
}
|
|
87127
87335
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
87128
87336
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
87129
87337
|
function sweepOrphanTmpFiles(stateDir) {
|
|
87130
87338
|
let entries;
|
|
87131
87339
|
try {
|
|
87132
|
-
entries =
|
|
87340
|
+
entries = readdirSync26(stateDir);
|
|
87133
87341
|
} catch {
|
|
87134
87342
|
return;
|
|
87135
87343
|
}
|
|
@@ -87137,7 +87345,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87137
87345
|
for (const entry of entries) {
|
|
87138
87346
|
if (!entry.startsWith(TMP_PREFIX))
|
|
87139
87347
|
continue;
|
|
87140
|
-
const tmpPath =
|
|
87348
|
+
const tmpPath = join73(stateDir, entry);
|
|
87141
87349
|
try {
|
|
87142
87350
|
const stat = statSync43(tmpPath);
|
|
87143
87351
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -87149,7 +87357,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87149
87357
|
var LOCK_RETRY_MS = 25;
|
|
87150
87358
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
87151
87359
|
function withLock(stateDir, fn) {
|
|
87152
|
-
const lockPath =
|
|
87360
|
+
const lockPath = join73(stateDir, ISSUES_LOCK);
|
|
87153
87361
|
const startedAt = Date.now();
|
|
87154
87362
|
let fd = null;
|
|
87155
87363
|
while (fd === null) {
|
|
@@ -87184,7 +87392,7 @@ function withLock(stateDir, fn) {
|
|
|
87184
87392
|
function tryStealStaleLock(lockPath) {
|
|
87185
87393
|
let pidStr;
|
|
87186
87394
|
try {
|
|
87187
|
-
pidStr =
|
|
87395
|
+
pidStr = readFileSync64(lockPath, "utf-8").trim();
|
|
87188
87396
|
} catch {
|
|
87189
87397
|
return true;
|
|
87190
87398
|
}
|
|
@@ -87432,20 +87640,20 @@ function relTime(deltaMs) {
|
|
|
87432
87640
|
|
|
87433
87641
|
// src/cli/deps.ts
|
|
87434
87642
|
init_source();
|
|
87435
|
-
import { existsSync as
|
|
87643
|
+
import { existsSync as existsSync74 } from "node:fs";
|
|
87436
87644
|
import { homedir as homedir44 } from "node:os";
|
|
87437
|
-
import { join as
|
|
87645
|
+
import { join as join76, resolve as resolve44 } from "node:path";
|
|
87438
87646
|
|
|
87439
87647
|
// src/deps/python.ts
|
|
87440
87648
|
import { createHash as createHash13 } from "node:crypto";
|
|
87441
87649
|
import {
|
|
87442
|
-
existsSync as
|
|
87443
|
-
mkdirSync as
|
|
87444
|
-
readFileSync as
|
|
87650
|
+
existsSync as existsSync72,
|
|
87651
|
+
mkdirSync as mkdirSync38,
|
|
87652
|
+
readFileSync as readFileSync65,
|
|
87445
87653
|
rmSync as rmSync13,
|
|
87446
|
-
writeFileSync as
|
|
87654
|
+
writeFileSync as writeFileSync21
|
|
87447
87655
|
} from "node:fs";
|
|
87448
|
-
import { dirname as
|
|
87656
|
+
import { dirname as dirname25, join as join74 } from "node:path";
|
|
87449
87657
|
import { homedir as homedir42 } from "node:os";
|
|
87450
87658
|
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
87451
87659
|
|
|
@@ -87458,26 +87666,26 @@ class PythonEnvError extends Error {
|
|
|
87458
87666
|
}
|
|
87459
87667
|
}
|
|
87460
87668
|
function defaultPythonCacheRoot() {
|
|
87461
|
-
return
|
|
87669
|
+
return join74(homedir42(), ".switchroom", "deps", "python");
|
|
87462
87670
|
}
|
|
87463
87671
|
function hashFile(path5) {
|
|
87464
|
-
return createHash13("sha256").update(
|
|
87672
|
+
return createHash13("sha256").update(readFileSync65(path5)).digest("hex");
|
|
87465
87673
|
}
|
|
87466
87674
|
function ensurePythonEnv(opts) {
|
|
87467
87675
|
const { skillName, requirementsPath, force = false } = opts;
|
|
87468
87676
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
87469
87677
|
const hostPython = opts.pythonBin ?? "python3";
|
|
87470
|
-
if (!
|
|
87678
|
+
if (!existsSync72(requirementsPath)) {
|
|
87471
87679
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
87472
87680
|
}
|
|
87473
|
-
const venvDir =
|
|
87474
|
-
const stampPath =
|
|
87475
|
-
const binDir =
|
|
87476
|
-
const pythonBin =
|
|
87477
|
-
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");
|
|
87478
87686
|
const targetHash = hashFile(requirementsPath);
|
|
87479
|
-
if (!force &&
|
|
87480
|
-
const existingHash =
|
|
87687
|
+
if (!force && existsSync72(stampPath) && existsSync72(pythonBin)) {
|
|
87688
|
+
const existingHash = readFileSync65(stampPath, "utf8").trim();
|
|
87481
87689
|
if (existingHash === targetHash) {
|
|
87482
87690
|
return {
|
|
87483
87691
|
skillName,
|
|
@@ -87489,10 +87697,10 @@ function ensurePythonEnv(opts) {
|
|
|
87489
87697
|
};
|
|
87490
87698
|
}
|
|
87491
87699
|
}
|
|
87492
|
-
if (
|
|
87700
|
+
if (existsSync72(venvDir)) {
|
|
87493
87701
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
87494
87702
|
}
|
|
87495
|
-
|
|
87703
|
+
mkdirSync38(dirname25(venvDir), { recursive: true });
|
|
87496
87704
|
try {
|
|
87497
87705
|
execFileSync21(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
87498
87706
|
} catch (err) {
|
|
@@ -87511,7 +87719,7 @@ function ensurePythonEnv(opts) {
|
|
|
87511
87719
|
const e = err;
|
|
87512
87720
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
87513
87721
|
}
|
|
87514
|
-
|
|
87722
|
+
writeFileSync21(stampPath, targetHash + `
|
|
87515
87723
|
`);
|
|
87516
87724
|
return {
|
|
87517
87725
|
skillName,
|
|
@@ -87527,13 +87735,13 @@ function ensurePythonEnv(opts) {
|
|
|
87527
87735
|
import { createHash as createHash14 } from "node:crypto";
|
|
87528
87736
|
import {
|
|
87529
87737
|
copyFileSync as copyFileSync10,
|
|
87530
|
-
existsSync as
|
|
87531
|
-
mkdirSync as
|
|
87532
|
-
readFileSync as
|
|
87738
|
+
existsSync as existsSync73,
|
|
87739
|
+
mkdirSync as mkdirSync39,
|
|
87740
|
+
readFileSync as readFileSync66,
|
|
87533
87741
|
rmSync as rmSync14,
|
|
87534
|
-
writeFileSync as
|
|
87742
|
+
writeFileSync as writeFileSync22
|
|
87535
87743
|
} from "node:fs";
|
|
87536
|
-
import { dirname as
|
|
87744
|
+
import { dirname as dirname26, join as join75 } from "node:path";
|
|
87537
87745
|
import { homedir as homedir43 } from "node:os";
|
|
87538
87746
|
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
87539
87747
|
|
|
@@ -87557,23 +87765,23 @@ var LOCKFILES_FOR = {
|
|
|
87557
87765
|
npm: ["package-lock.json"]
|
|
87558
87766
|
};
|
|
87559
87767
|
function defaultNodeCacheRoot() {
|
|
87560
|
-
return
|
|
87768
|
+
return join75(homedir43(), ".switchroom", "deps", "node");
|
|
87561
87769
|
}
|
|
87562
87770
|
function hashDepInputs(packageJsonPath) {
|
|
87563
|
-
const sourceDir =
|
|
87771
|
+
const sourceDir = dirname26(packageJsonPath);
|
|
87564
87772
|
const hasher = createHash14("sha256");
|
|
87565
87773
|
hasher.update(`package.json
|
|
87566
87774
|
`);
|
|
87567
|
-
hasher.update(
|
|
87775
|
+
hasher.update(readFileSync66(packageJsonPath));
|
|
87568
87776
|
for (const lockName of ALL_LOCKFILES) {
|
|
87569
|
-
const lockPath =
|
|
87570
|
-
if (
|
|
87777
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87778
|
+
if (existsSync73(lockPath)) {
|
|
87571
87779
|
hasher.update(`
|
|
87572
87780
|
`);
|
|
87573
87781
|
hasher.update(lockName);
|
|
87574
87782
|
hasher.update(`
|
|
87575
87783
|
`);
|
|
87576
|
-
hasher.update(
|
|
87784
|
+
hasher.update(readFileSync66(lockPath));
|
|
87577
87785
|
}
|
|
87578
87786
|
}
|
|
87579
87787
|
return hasher.digest("hex");
|
|
@@ -87582,17 +87790,17 @@ function ensureNodeEnv(opts) {
|
|
|
87582
87790
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
87583
87791
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
87584
87792
|
const installer = opts.installer ?? "bun";
|
|
87585
|
-
if (!
|
|
87793
|
+
if (!existsSync73(packageJsonPath)) {
|
|
87586
87794
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
87587
87795
|
}
|
|
87588
|
-
const sourceDir =
|
|
87589
|
-
const envDir =
|
|
87590
|
-
const stampPath =
|
|
87591
|
-
const nodeModulesDir =
|
|
87592
|
-
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");
|
|
87593
87801
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
87594
|
-
if (!force &&
|
|
87595
|
-
const existingHash =
|
|
87802
|
+
if (!force && existsSync73(stampPath) && existsSync73(nodeModulesDir)) {
|
|
87803
|
+
const existingHash = readFileSync66(stampPath, "utf8").trim();
|
|
87596
87804
|
if (existingHash === targetHash) {
|
|
87597
87805
|
return {
|
|
87598
87806
|
skillName,
|
|
@@ -87603,16 +87811,16 @@ function ensureNodeEnv(opts) {
|
|
|
87603
87811
|
};
|
|
87604
87812
|
}
|
|
87605
87813
|
}
|
|
87606
|
-
if (
|
|
87814
|
+
if (existsSync73(envDir)) {
|
|
87607
87815
|
rmSync14(envDir, { recursive: true, force: true });
|
|
87608
87816
|
}
|
|
87609
|
-
|
|
87610
|
-
copyFileSync10(packageJsonPath,
|
|
87817
|
+
mkdirSync39(envDir, { recursive: true });
|
|
87818
|
+
copyFileSync10(packageJsonPath, join75(envDir, "package.json"));
|
|
87611
87819
|
let copiedLockfile = false;
|
|
87612
87820
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
87613
|
-
const lockPath =
|
|
87614
|
-
if (
|
|
87615
|
-
copyFileSync10(lockPath,
|
|
87821
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87822
|
+
if (existsSync73(lockPath)) {
|
|
87823
|
+
copyFileSync10(lockPath, join75(envDir, lockName));
|
|
87616
87824
|
copiedLockfile = true;
|
|
87617
87825
|
}
|
|
87618
87826
|
}
|
|
@@ -87628,7 +87836,7 @@ function ensureNodeEnv(opts) {
|
|
|
87628
87836
|
const e = err;
|
|
87629
87837
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
87630
87838
|
}
|
|
87631
|
-
|
|
87839
|
+
writeFileSync22(stampPath, targetHash + `
|
|
87632
87840
|
`);
|
|
87633
87841
|
return {
|
|
87634
87842
|
skillName,
|
|
@@ -87647,22 +87855,22 @@ function registerDepsCommand(program3) {
|
|
|
87647
87855
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
87648
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) => {
|
|
87649
87857
|
const skillsRoot = builtinSkillsRoot();
|
|
87650
|
-
if (!
|
|
87858
|
+
if (!existsSync74(skillsRoot)) {
|
|
87651
87859
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
87652
87860
|
process.exit(1);
|
|
87653
87861
|
}
|
|
87654
|
-
const skillDir =
|
|
87655
|
-
if (!
|
|
87862
|
+
const skillDir = join76(skillsRoot, skill);
|
|
87863
|
+
if (!existsSync74(skillDir)) {
|
|
87656
87864
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
87657
87865
|
process.exit(1);
|
|
87658
87866
|
}
|
|
87659
|
-
const requirementsPath =
|
|
87660
|
-
const packageJsonPath =
|
|
87661
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
87662
|
-
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));
|
|
87663
87871
|
let did = 0;
|
|
87664
87872
|
if (wantPython) {
|
|
87665
|
-
if (!
|
|
87873
|
+
if (!existsSync74(requirementsPath)) {
|
|
87666
87874
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
87667
87875
|
process.exit(1);
|
|
87668
87876
|
}
|
|
@@ -87686,7 +87894,7 @@ function registerDepsCommand(program3) {
|
|
|
87686
87894
|
}
|
|
87687
87895
|
}
|
|
87688
87896
|
if (wantNode) {
|
|
87689
|
-
if (!
|
|
87897
|
+
if (!existsSync74(packageJsonPath)) {
|
|
87690
87898
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
87691
87899
|
process.exit(1);
|
|
87692
87900
|
}
|
|
@@ -87719,7 +87927,7 @@ function registerDepsCommand(program3) {
|
|
|
87719
87927
|
// src/cli/workspace.ts
|
|
87720
87928
|
init_helpers();
|
|
87721
87929
|
init_loader();
|
|
87722
|
-
import { existsSync as
|
|
87930
|
+
import { existsSync as existsSync75 } from "node:fs";
|
|
87723
87931
|
import { resolve as resolve45, sep as sep3 } from "node:path";
|
|
87724
87932
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
87725
87933
|
|
|
@@ -88496,7 +88704,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88496
88704
|
if (!dir)
|
|
88497
88705
|
return;
|
|
88498
88706
|
const gitDir = resolve45(dir, ".git");
|
|
88499
|
-
if (!
|
|
88707
|
+
if (!existsSync75(gitDir)) {
|
|
88500
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.
|
|
88501
88709
|
`);
|
|
88502
88710
|
return;
|
|
@@ -88550,7 +88758,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88550
88758
|
if (!dir)
|
|
88551
88759
|
return;
|
|
88552
88760
|
const gitDir = resolve45(dir, ".git");
|
|
88553
|
-
if (!
|
|
88761
|
+
if (!existsSync75(gitDir)) {
|
|
88554
88762
|
process.stdout.write(`Workspace is not a git repository.
|
|
88555
88763
|
`);
|
|
88556
88764
|
return;
|
|
@@ -88575,7 +88783,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
88575
88783
|
const agentsDir = resolveAgentsDir(config);
|
|
88576
88784
|
const agentDir = resolve45(agentsDir, agentName);
|
|
88577
88785
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
88578
|
-
if (!
|
|
88786
|
+
if (!existsSync75(dir)) {
|
|
88579
88787
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
88580
88788
|
`);
|
|
88581
88789
|
return;
|
|
@@ -88611,8 +88819,8 @@ function safeParseInt(value, fallback) {
|
|
|
88611
88819
|
init_helpers();
|
|
88612
88820
|
init_loader();
|
|
88613
88821
|
init_merge();
|
|
88614
|
-
import { copyFileSync as copyFileSync11, existsSync as
|
|
88615
|
-
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";
|
|
88616
88824
|
init_scaffold();
|
|
88617
88825
|
init_profiles();
|
|
88618
88826
|
init_schema();
|
|
@@ -88629,7 +88837,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88629
88837
|
const agentsDir = resolveAgentsDir(config);
|
|
88630
88838
|
const agentDir = resolve46(agentsDir, agentName);
|
|
88631
88839
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88632
|
-
if (!
|
|
88840
|
+
if (!existsSync76(workspaceDir)) {
|
|
88633
88841
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
88634
88842
|
process.exit(1);
|
|
88635
88843
|
}
|
|
@@ -88638,7 +88846,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88638
88846
|
profileName,
|
|
88639
88847
|
profilePath,
|
|
88640
88848
|
workspaceDir,
|
|
88641
|
-
soulPath:
|
|
88849
|
+
soulPath: join77(workspaceDir, "SOUL.md"),
|
|
88642
88850
|
soul: merged.soul
|
|
88643
88851
|
};
|
|
88644
88852
|
}
|
|
@@ -88655,11 +88863,11 @@ function registerSoulCommand(program3) {
|
|
|
88655
88863
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
88656
88864
|
if (!t)
|
|
88657
88865
|
return;
|
|
88658
|
-
if (!
|
|
88866
|
+
if (!existsSync76(t.soulPath)) {
|
|
88659
88867
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
88660
88868
|
process.exit(1);
|
|
88661
88869
|
}
|
|
88662
|
-
process.stdout.write(
|
|
88870
|
+
process.stdout.write(readFileSync67(t.soulPath, "utf-8"));
|
|
88663
88871
|
}));
|
|
88664
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) => {
|
|
88665
88873
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -88670,7 +88878,7 @@ function registerSoulCommand(program3) {
|
|
|
88670
88878
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
88671
88879
|
process.exit(1);
|
|
88672
88880
|
}
|
|
88673
|
-
const exists =
|
|
88881
|
+
const exists = existsSync76(t.soulPath);
|
|
88674
88882
|
if (exists && !opts.yes) {
|
|
88675
88883
|
if (!isInteractive()) {
|
|
88676
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).`);
|
|
@@ -88685,12 +88893,12 @@ function registerSoulCommand(program3) {
|
|
|
88685
88893
|
let backupPath;
|
|
88686
88894
|
if (exists) {
|
|
88687
88895
|
backupPath = `${t.soulPath}.bak`;
|
|
88688
|
-
if (
|
|
88896
|
+
if (existsSync76(backupPath)) {
|
|
88689
88897
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
88690
88898
|
}
|
|
88691
88899
|
copyFileSync11(t.soulPath, backupPath);
|
|
88692
88900
|
}
|
|
88693
|
-
|
|
88901
|
+
writeFileSync23(t.soulPath, content, "utf-8");
|
|
88694
88902
|
if (backupPath) {
|
|
88695
88903
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
88696
88904
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -88704,8 +88912,8 @@ function registerSoulCommand(program3) {
|
|
|
88704
88912
|
// src/cli/debug.ts
|
|
88705
88913
|
init_helpers();
|
|
88706
88914
|
init_loader();
|
|
88707
|
-
import { existsSync as
|
|
88708
|
-
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";
|
|
88709
88917
|
import { createHash as createHash15 } from "node:crypto";
|
|
88710
88918
|
init_merge();
|
|
88711
88919
|
init_hindsight2();
|
|
@@ -88716,11 +88924,11 @@ function estimateTokens(bytes) {
|
|
|
88716
88924
|
return Math.round(bytes / 3.7);
|
|
88717
88925
|
}
|
|
88718
88926
|
function readMcpServerNames(agentDir) {
|
|
88719
|
-
const mcpPath =
|
|
88720
|
-
if (!
|
|
88927
|
+
const mcpPath = join78(agentDir, ".mcp.json");
|
|
88928
|
+
if (!existsSync77(mcpPath))
|
|
88721
88929
|
return [];
|
|
88722
88930
|
try {
|
|
88723
|
-
const parsed = JSON.parse(
|
|
88931
|
+
const parsed = JSON.parse(readFileSync68(mcpPath, "utf-8"));
|
|
88724
88932
|
return Object.keys(parsed.mcpServers ?? {});
|
|
88725
88933
|
} catch {
|
|
88726
88934
|
return null;
|
|
@@ -88730,18 +88938,18 @@ function sha256(content) {
|
|
|
88730
88938
|
return createHash15("sha256").update(content).digest("hex").slice(0, 16);
|
|
88731
88939
|
}
|
|
88732
88940
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
88733
|
-
const projectsDir =
|
|
88734
|
-
if (!
|
|
88941
|
+
const projectsDir = join78(claudeConfigDir, "projects");
|
|
88942
|
+
if (!existsSync77(projectsDir))
|
|
88735
88943
|
return;
|
|
88736
88944
|
try {
|
|
88737
|
-
const entries =
|
|
88945
|
+
const entries = readdirSync27(projectsDir, { withFileTypes: true });
|
|
88738
88946
|
let latest;
|
|
88739
88947
|
for (const entry of entries) {
|
|
88740
88948
|
if (!entry.isDirectory())
|
|
88741
88949
|
continue;
|
|
88742
|
-
const projectPath =
|
|
88743
|
-
const transcriptPath =
|
|
88744
|
-
if (!
|
|
88950
|
+
const projectPath = join78(projectsDir, entry.name);
|
|
88951
|
+
const transcriptPath = join78(projectPath, "transcript.jsonl");
|
|
88952
|
+
if (!existsSync77(transcriptPath))
|
|
88745
88953
|
continue;
|
|
88746
88954
|
const stat3 = statSync44(transcriptPath);
|
|
88747
88955
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -88755,7 +88963,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
88755
88963
|
}
|
|
88756
88964
|
function extractLatestUserMessage(transcriptPath) {
|
|
88757
88965
|
try {
|
|
88758
|
-
const content =
|
|
88966
|
+
const content = readFileSync68(transcriptPath, "utf-8");
|
|
88759
88967
|
const lines = content.trim().split(`
|
|
88760
88968
|
`).filter(Boolean);
|
|
88761
88969
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -88804,16 +89012,16 @@ function registerDebugCommand(program3) {
|
|
|
88804
89012
|
}
|
|
88805
89013
|
const agentsDir = resolveAgentsDir(config);
|
|
88806
89014
|
const agentDir = resolve47(agentsDir, agentName);
|
|
88807
|
-
if (!
|
|
89015
|
+
if (!existsSync77(agentDir)) {
|
|
88808
89016
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
88809
89017
|
process.exit(1);
|
|
88810
89018
|
}
|
|
88811
89019
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88812
|
-
const claudeConfigDir =
|
|
88813
|
-
const claudeMdPath =
|
|
88814
|
-
const soulMdPath =
|
|
88815
|
-
const workspaceSoulMdPath =
|
|
88816
|
-
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");
|
|
88817
89025
|
const lastN = parseInt(opts.last, 10);
|
|
88818
89026
|
if (isNaN(lastN) || lastN < 1) {
|
|
88819
89027
|
console.error("--last must be a positive integer");
|
|
@@ -88859,7 +89067,7 @@ function registerDebugCommand(program3) {
|
|
|
88859
89067
|
}
|
|
88860
89068
|
console.log(`=== Append System Prompt (per-session) ===
|
|
88861
89069
|
`);
|
|
88862
|
-
const handoffContent =
|
|
89070
|
+
const handoffContent = existsSync77(handoffPath) ? readFileSync68(handoffPath, "utf-8") : "";
|
|
88863
89071
|
if (handoffContent.trim().length > 0) {
|
|
88864
89072
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
88865
89073
|
console.log(handoffContent);
|
|
@@ -88870,7 +89078,7 @@ function registerDebugCommand(program3) {
|
|
|
88870
89078
|
}
|
|
88871
89079
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
88872
89080
|
`);
|
|
88873
|
-
const claudeMdContent =
|
|
89081
|
+
const claudeMdContent = existsSync77(claudeMdPath) ? readFileSync68(claudeMdPath, "utf-8") : "";
|
|
88874
89082
|
if (claudeMdContent.trim().length > 0) {
|
|
88875
89083
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
88876
89084
|
console.log(claudeMdContent);
|
|
@@ -88881,7 +89089,7 @@ function registerDebugCommand(program3) {
|
|
|
88881
89089
|
}
|
|
88882
89090
|
console.log(`=== Persona (SOUL.md) ===
|
|
88883
89091
|
`);
|
|
88884
|
-
const soulMdContent =
|
|
89092
|
+
const soulMdContent = existsSync77(soulMdPath) ? readFileSync68(soulMdPath, "utf-8") : existsSync77(workspaceSoulMdPath) ? readFileSync68(workspaceSoulMdPath, "utf-8") : "";
|
|
88885
89093
|
if (soulMdContent.trim().length > 0) {
|
|
88886
89094
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
88887
89095
|
console.log(soulMdContent);
|
|
@@ -88942,11 +89150,11 @@ function registerDebugCommand(program3) {
|
|
|
88942
89150
|
const soulMdBytes = soulMdContent.length;
|
|
88943
89151
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
88944
89152
|
const userBytes = userMessage?.text.length ?? 0;
|
|
88945
|
-
const fleetDir =
|
|
88946
|
-
const fleetInvPath =
|
|
88947
|
-
const fleetClaudePath =
|
|
88948
|
-
const fleetInvBytes =
|
|
88949
|
-
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;
|
|
88950
89158
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
88951
89159
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
88952
89160
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -88979,44 +89187,44 @@ init_source();
|
|
|
88979
89187
|
|
|
88980
89188
|
// src/worktree/claim.ts
|
|
88981
89189
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
88982
|
-
import { closeSync as closeSync13, mkdirSync as
|
|
88983
|
-
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";
|
|
88984
89192
|
import { homedir as homedir46 } from "node:os";
|
|
88985
89193
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
88986
89194
|
|
|
88987
89195
|
// src/worktree/registry.ts
|
|
88988
89196
|
import {
|
|
88989
|
-
mkdirSync as
|
|
88990
|
-
writeFileSync as
|
|
88991
|
-
readFileSync as
|
|
88992
|
-
readdirSync as
|
|
89197
|
+
mkdirSync as mkdirSync40,
|
|
89198
|
+
writeFileSync as writeFileSync24,
|
|
89199
|
+
readFileSync as readFileSync69,
|
|
89200
|
+
readdirSync as readdirSync28,
|
|
88993
89201
|
unlinkSync as unlinkSync16,
|
|
88994
|
-
existsSync as
|
|
88995
|
-
renameSync as
|
|
89202
|
+
existsSync as existsSync78,
|
|
89203
|
+
renameSync as renameSync18
|
|
88996
89204
|
} from "node:fs";
|
|
88997
|
-
import { join as
|
|
89205
|
+
import { join as join79, resolve as resolve48 } from "node:path";
|
|
88998
89206
|
import { homedir as homedir45 } from "node:os";
|
|
88999
89207
|
function registryDir() {
|
|
89000
|
-
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
89208
|
+
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ?? join79(homedir45(), ".switchroom", "worktrees"));
|
|
89001
89209
|
}
|
|
89002
89210
|
function recordPath(id) {
|
|
89003
|
-
return
|
|
89211
|
+
return join79(registryDir(), `${id}.json`);
|
|
89004
89212
|
}
|
|
89005
89213
|
function ensureDir2() {
|
|
89006
|
-
|
|
89214
|
+
mkdirSync40(registryDir(), { recursive: true });
|
|
89007
89215
|
}
|
|
89008
89216
|
function writeRecord(record2) {
|
|
89009
89217
|
ensureDir2();
|
|
89010
89218
|
const target = recordPath(record2.id);
|
|
89011
89219
|
const tmp = `${target}.tmp${process.pid}`;
|
|
89012
|
-
|
|
89220
|
+
writeFileSync24(tmp, JSON.stringify(record2, null, 2) + `
|
|
89013
89221
|
`, { mode: 384 });
|
|
89014
|
-
|
|
89222
|
+
renameSync18(tmp, target);
|
|
89015
89223
|
}
|
|
89016
89224
|
function readRecord(id) {
|
|
89017
89225
|
const path8 = recordPath(id);
|
|
89018
89226
|
try {
|
|
89019
|
-
const raw =
|
|
89227
|
+
const raw = readFileSync69(path8, "utf8");
|
|
89020
89228
|
return JSON.parse(raw);
|
|
89021
89229
|
} catch {
|
|
89022
89230
|
return null;
|
|
@@ -89032,7 +89240,7 @@ function listRecords() {
|
|
|
89032
89240
|
ensureDir2();
|
|
89033
89241
|
const dir = registryDir();
|
|
89034
89242
|
const records = [];
|
|
89035
|
-
for (const entry of
|
|
89243
|
+
for (const entry of readdirSync28(dir)) {
|
|
89036
89244
|
if (!entry.endsWith(".json"))
|
|
89037
89245
|
continue;
|
|
89038
89246
|
const id = entry.slice(0, -5);
|
|
@@ -89049,9 +89257,9 @@ function countByRepo(repoPath) {
|
|
|
89049
89257
|
// src/worktree/claim.ts
|
|
89050
89258
|
function acquireRepoLock(repoPath) {
|
|
89051
89259
|
const lockDir = registryDir();
|
|
89052
|
-
|
|
89260
|
+
mkdirSync41(lockDir, { recursive: true });
|
|
89053
89261
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
89054
|
-
const lockPath =
|
|
89262
|
+
const lockPath = join80(lockDir, `.lock-${lockName}`);
|
|
89055
89263
|
const deadline = Date.now() + 5000;
|
|
89056
89264
|
let fd = null;
|
|
89057
89265
|
while (fd === null) {
|
|
@@ -89078,7 +89286,7 @@ function acquireRepoLock(repoPath) {
|
|
|
89078
89286
|
}
|
|
89079
89287
|
var DEFAULT_CONCURRENCY = 5;
|
|
89080
89288
|
function worktreesBaseDir() {
|
|
89081
|
-
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
89289
|
+
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ?? join80(homedir46(), ".switchroom", "worktree-checkouts"));
|
|
89082
89290
|
}
|
|
89083
89291
|
function shortId() {
|
|
89084
89292
|
return randomBytes13(4).toString("hex");
|
|
@@ -89100,12 +89308,12 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
89100
89308
|
}
|
|
89101
89309
|
function expandHome(p) {
|
|
89102
89310
|
if (p.startsWith("~/"))
|
|
89103
|
-
return
|
|
89311
|
+
return join80(homedir46(), p.slice(2));
|
|
89104
89312
|
return p;
|
|
89105
89313
|
}
|
|
89106
89314
|
async function claimWorktree(input, codeRepos) {
|
|
89107
89315
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
89108
|
-
if (!
|
|
89316
|
+
if (!existsSync79(repoPath)) {
|
|
89109
89317
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
89110
89318
|
}
|
|
89111
89319
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -89127,8 +89335,8 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89127
89335
|
const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
|
|
89128
89336
|
branch = `task/${taskSuffix}-${id}`;
|
|
89129
89337
|
const baseDir = worktreesBaseDir();
|
|
89130
|
-
|
|
89131
|
-
worktreePath =
|
|
89338
|
+
mkdirSync41(baseDir, { recursive: true });
|
|
89339
|
+
worktreePath = join80(baseDir, `${id}-${taskSuffix}`);
|
|
89132
89340
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
89133
89341
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
89134
89342
|
const now = new Date().toISOString();
|
|
@@ -89161,7 +89369,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89161
89369
|
|
|
89162
89370
|
// src/worktree/release.ts
|
|
89163
89371
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
89164
|
-
import { existsSync as
|
|
89372
|
+
import { existsSync as existsSync80 } from "node:fs";
|
|
89165
89373
|
function releaseWorktree(input) {
|
|
89166
89374
|
const { id } = input;
|
|
89167
89375
|
const record2 = readRecord(id);
|
|
@@ -89169,7 +89377,7 @@ function releaseWorktree(input) {
|
|
|
89169
89377
|
return { released: true };
|
|
89170
89378
|
}
|
|
89171
89379
|
let gitSuccess = true;
|
|
89172
|
-
if (
|
|
89380
|
+
if (existsSync80(record2.path)) {
|
|
89173
89381
|
try {
|
|
89174
89382
|
execFileSync24("git", ["worktree", "remove", "--force", record2.path], {
|
|
89175
89383
|
cwd: record2.repo,
|
|
@@ -89208,7 +89416,7 @@ function listWorktrees() {
|
|
|
89208
89416
|
|
|
89209
89417
|
// src/worktree/reaper.ts
|
|
89210
89418
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
89211
|
-
import { existsSync as
|
|
89419
|
+
import { existsSync as existsSync81 } from "node:fs";
|
|
89212
89420
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
89213
89421
|
function reapSkipReasonText(action) {
|
|
89214
89422
|
switch (action) {
|
|
@@ -89269,7 +89477,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
89269
89477
|
const plan = [];
|
|
89270
89478
|
for (const record2 of listRecords()) {
|
|
89271
89479
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
89272
|
-
const worktreeExists =
|
|
89480
|
+
const worktreeExists = existsSync81(record2.path);
|
|
89273
89481
|
if (!worktreeExists) {
|
|
89274
89482
|
plan.push({
|
|
89275
89483
|
record: record2,
|
|
@@ -89350,16 +89558,16 @@ function runReaper(nowMs, deps = {}) {
|
|
|
89350
89558
|
// src/worktree/gc.ts
|
|
89351
89559
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
89352
89560
|
import {
|
|
89353
|
-
existsSync as
|
|
89354
|
-
readFileSync as
|
|
89355
|
-
readdirSync as
|
|
89561
|
+
existsSync as existsSync82,
|
|
89562
|
+
readFileSync as readFileSync70,
|
|
89563
|
+
readdirSync as readdirSync29,
|
|
89356
89564
|
statSync as statSync45,
|
|
89357
|
-
renameSync as
|
|
89358
|
-
mkdirSync as
|
|
89565
|
+
renameSync as renameSync19,
|
|
89566
|
+
mkdirSync as mkdirSync42,
|
|
89359
89567
|
rmSync as rmSync15
|
|
89360
89568
|
} from "node:fs";
|
|
89361
89569
|
import { homedir as homedir47 } from "node:os";
|
|
89362
|
-
import { join as
|
|
89570
|
+
import { join as join81, resolve as resolve50 } from "node:path";
|
|
89363
89571
|
function parseGitdirPointer(dotGitFileContents) {
|
|
89364
89572
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
89365
89573
|
return m ? m[1] : null;
|
|
@@ -89474,17 +89682,17 @@ function defaultPrSignal(repo, branch, exec) {
|
|
|
89474
89682
|
}
|
|
89475
89683
|
}
|
|
89476
89684
|
function trashRoot() {
|
|
89477
|
-
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ??
|
|
89685
|
+
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ?? join81(homedir47(), ".switchroom", "worktree-gc-trash"));
|
|
89478
89686
|
}
|
|
89479
89687
|
function planGc(roots, deps = {}) {
|
|
89480
|
-
const exists = deps.existsSync ??
|
|
89481
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89482
|
-
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"));
|
|
89483
89691
|
const stat3 = deps.stat ?? ((p) => statSync45(p));
|
|
89484
89692
|
const exec = deps.exec ?? defaultExec;
|
|
89485
89693
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
89486
89694
|
const stamp = deps.dateStamp ?? "undated";
|
|
89487
|
-
const trash =
|
|
89695
|
+
const trash = join81(trashRoot(), stamp);
|
|
89488
89696
|
let claimed;
|
|
89489
89697
|
try {
|
|
89490
89698
|
claimed = new Set(listRecords().map((r) => resolve50(r.path)));
|
|
@@ -89520,10 +89728,10 @@ function planGc(roots, deps = {}) {
|
|
|
89520
89728
|
continue;
|
|
89521
89729
|
}
|
|
89522
89730
|
for (const name of entries) {
|
|
89523
|
-
const dir =
|
|
89731
|
+
const dir = join81(root, name);
|
|
89524
89732
|
if (isEphemeralPath(dir))
|
|
89525
89733
|
continue;
|
|
89526
|
-
const dotGit =
|
|
89734
|
+
const dotGit = join81(dir, ".git");
|
|
89527
89735
|
if (!exists(dotGit))
|
|
89528
89736
|
continue;
|
|
89529
89737
|
let st;
|
|
@@ -89552,7 +89760,7 @@ function planGc(roots, deps = {}) {
|
|
|
89552
89760
|
ownerRepos.add(repoRoot);
|
|
89553
89761
|
if (exists(ptr))
|
|
89554
89762
|
continue;
|
|
89555
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
89763
|
+
orphans.push({ dir, owner: repoRoot, dest: join81(trash, name) });
|
|
89556
89764
|
}
|
|
89557
89765
|
}
|
|
89558
89766
|
const registered = [];
|
|
@@ -89607,10 +89815,10 @@ function planGc(roots, deps = {}) {
|
|
|
89607
89815
|
}
|
|
89608
89816
|
function applyGc(plan, deps = {}) {
|
|
89609
89817
|
const exec = deps.exec ?? defaultExec;
|
|
89610
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
89818
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync42(p, { recursive: true }));
|
|
89611
89819
|
const move = deps.move ?? ((src, dest) => {
|
|
89612
89820
|
try {
|
|
89613
|
-
|
|
89821
|
+
renameSync19(src, dest);
|
|
89614
89822
|
} catch {
|
|
89615
89823
|
exec("mv", [src, dest]);
|
|
89616
89824
|
}
|
|
@@ -89656,14 +89864,14 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
89656
89864
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
89657
89865
|
}
|
|
89658
89866
|
function listTrashEntries(nowMs, deps = {}) {
|
|
89659
|
-
const exists = deps.existsSync ??
|
|
89660
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89867
|
+
const exists = deps.existsSync ?? existsSync82;
|
|
89868
|
+
const readDir = deps.readDir ?? ((p) => readdirSync29(p));
|
|
89661
89869
|
const root = trashRoot();
|
|
89662
89870
|
if (!exists(root))
|
|
89663
89871
|
return [];
|
|
89664
89872
|
const out = [];
|
|
89665
89873
|
for (const stamp of readDir(root)) {
|
|
89666
|
-
const stampDir =
|
|
89874
|
+
const stampDir = join81(root, stamp);
|
|
89667
89875
|
let names;
|
|
89668
89876
|
try {
|
|
89669
89877
|
names = readDir(stampDir);
|
|
@@ -89671,7 +89879,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
89671
89879
|
continue;
|
|
89672
89880
|
}
|
|
89673
89881
|
for (const name of names) {
|
|
89674
|
-
const p =
|
|
89882
|
+
const p = join81(stampDir, name);
|
|
89675
89883
|
let mtimeMs = nowMs;
|
|
89676
89884
|
try {
|
|
89677
89885
|
mtimeMs = statSync45(p).mtimeMs;
|
|
@@ -89695,7 +89903,7 @@ function purgeTrash(paths) {
|
|
|
89695
89903
|
return { deleted, errors: errors2 };
|
|
89696
89904
|
}
|
|
89697
89905
|
function defaultRoots() {
|
|
89698
|
-
return [
|
|
89906
|
+
return [join81(homedir47(), "code")];
|
|
89699
89907
|
}
|
|
89700
89908
|
|
|
89701
89909
|
// src/cli/worktree.ts
|
|
@@ -89918,12 +90126,12 @@ init_drive();
|
|
|
89918
90126
|
init_scaffold_integration();
|
|
89919
90127
|
import {
|
|
89920
90128
|
chmodSync as chmodSync11,
|
|
89921
|
-
mkdirSync as
|
|
89922
|
-
readdirSync as
|
|
90129
|
+
mkdirSync as mkdirSync43,
|
|
90130
|
+
readdirSync as readdirSync30,
|
|
89923
90131
|
rmSync as rmSync16,
|
|
89924
|
-
writeFileSync as
|
|
90132
|
+
writeFileSync as writeFileSync25
|
|
89925
90133
|
} from "node:fs";
|
|
89926
|
-
import { join as
|
|
90134
|
+
import { join as join82 } from "node:path";
|
|
89927
90135
|
function encodeCredentialsFilename(email) {
|
|
89928
90136
|
const SAFE = new Set([
|
|
89929
90137
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -90113,17 +90321,17 @@ function resolveCredentialsDir(env2) {
|
|
|
90113
90321
|
if (explicit && explicit.length > 0)
|
|
90114
90322
|
return explicit;
|
|
90115
90323
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
90116
|
-
return
|
|
90324
|
+
return join82(stateBase, "google-workspace-mcp", "credentials");
|
|
90117
90325
|
}
|
|
90118
90326
|
function writeSeedFile(dir, email, seed) {
|
|
90119
|
-
|
|
90327
|
+
mkdirSync43(dir, { recursive: true, mode: 448 });
|
|
90120
90328
|
chmodSync11(dir, 448);
|
|
90121
|
-
for (const name of
|
|
90122
|
-
rmSync16(
|
|
90329
|
+
for (const name of readdirSync30(dir)) {
|
|
90330
|
+
rmSync16(join82(dir, name), { force: true, recursive: true });
|
|
90123
90331
|
}
|
|
90124
90332
|
const filename = encodeCredentialsFilename(email);
|
|
90125
|
-
const filePath =
|
|
90126
|
-
|
|
90333
|
+
const filePath = join82(dir, filename);
|
|
90334
|
+
writeFileSync25(filePath, JSON.stringify(seed), { mode: 384 });
|
|
90127
90335
|
chmodSync11(filePath, 384);
|
|
90128
90336
|
return filePath;
|
|
90129
90337
|
}
|
|
@@ -90281,8 +90489,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
90281
90489
|
// src/cli/m365-mcp-launcher.ts
|
|
90282
90490
|
init_scaffold_integration();
|
|
90283
90491
|
import { spawn as spawn5 } from "node:child_process";
|
|
90284
|
-
import { writeFileSync as
|
|
90285
|
-
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";
|
|
90286
90494
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
90287
90495
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
90288
90496
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -90318,8 +90526,8 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
90318
90526
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
90319
90527
|
const path8 = heartbeatPath(agentName, account);
|
|
90320
90528
|
try {
|
|
90321
|
-
|
|
90322
|
-
|
|
90529
|
+
mkdirSync44(dirname27(path8), { recursive: true });
|
|
90530
|
+
writeFileSync26(path8, JSON.stringify(data, null, 2), { mode: 420 });
|
|
90323
90531
|
} catch {}
|
|
90324
90532
|
}
|
|
90325
90533
|
function heartbeatPath(agentName, account) {
|
|
@@ -90327,7 +90535,7 @@ function heartbeatPath(agentName, account) {
|
|
|
90327
90535
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
90328
90536
|
if (override) {
|
|
90329
90537
|
const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
|
|
90330
|
-
return
|
|
90538
|
+
return join83(override, `${base}.heartbeat.json`);
|
|
90331
90539
|
}
|
|
90332
90540
|
return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
|
|
90333
90541
|
}
|
|
@@ -90559,8 +90767,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
90559
90767
|
// src/cli/notion-mcp-launcher.ts
|
|
90560
90768
|
init_scaffold_integration();
|
|
90561
90769
|
import { spawn as spawn6 } from "node:child_process";
|
|
90562
|
-
import { existsSync as
|
|
90563
|
-
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";
|
|
90564
90772
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
90565
90773
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
90566
90774
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -90570,10 +90778,10 @@ function buildNotionMcpArgs(opts) {
|
|
|
90570
90778
|
}
|
|
90571
90779
|
function defaultWriteHeartbeat(path8, contents) {
|
|
90572
90780
|
try {
|
|
90573
|
-
const dir =
|
|
90574
|
-
if (!
|
|
90575
|
-
|
|
90576
|
-
|
|
90781
|
+
const dir = dirname28(path8);
|
|
90782
|
+
if (!existsSync83(dir))
|
|
90783
|
+
mkdirSync45(dir, { recursive: true });
|
|
90784
|
+
writeFileSync27(path8, contents);
|
|
90577
90785
|
} catch {}
|
|
90578
90786
|
}
|
|
90579
90787
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -90683,9 +90891,9 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
90683
90891
|
|
|
90684
90892
|
// src/cli/hindsight-mcp-shim.ts
|
|
90685
90893
|
init_hindsight();
|
|
90686
|
-
import { mkdirSync as
|
|
90894
|
+
import { mkdirSync as mkdirSync46, readFileSync as readFileSync71, renameSync as renameSync20, writeFileSync as writeFileSync28 } from "node:fs";
|
|
90687
90895
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
90688
|
-
import { join as
|
|
90896
|
+
import { join as join84 } from "node:path";
|
|
90689
90897
|
import { createInterface as createInterface6 } from "node:readline";
|
|
90690
90898
|
var SHIM_SUPPORTED_PROTOCOL_VERSIONS = [
|
|
90691
90899
|
"2025-06-18",
|
|
@@ -90908,22 +91116,22 @@ class HindsightShim {
|
|
|
90908
91116
|
`));
|
|
90909
91117
|
}
|
|
90910
91118
|
get cachePath() {
|
|
90911
|
-
return
|
|
91119
|
+
return join84(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
|
|
90912
91120
|
}
|
|
90913
91121
|
writeCache(result) {
|
|
90914
91122
|
try {
|
|
90915
|
-
|
|
90916
|
-
const tmp =
|
|
90917
|
-
|
|
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) + `
|
|
90918
91126
|
`);
|
|
90919
|
-
|
|
91127
|
+
renameSync20(tmp, this.cachePath);
|
|
90920
91128
|
} catch (err) {
|
|
90921
91129
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
90922
91130
|
}
|
|
90923
91131
|
}
|
|
90924
91132
|
readCache() {
|
|
90925
91133
|
try {
|
|
90926
|
-
const parsed = JSON.parse(
|
|
91134
|
+
const parsed = JSON.parse(readFileSync71(this.cachePath, "utf-8"));
|
|
90927
91135
|
if (Array.isArray(parsed.tools))
|
|
90928
91136
|
return parsed;
|
|
90929
91137
|
return null;
|
|
@@ -91086,7 +91294,7 @@ function resolveShimOptionsFromEnv(env2) {
|
|
|
91086
91294
|
return {
|
|
91087
91295
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
91088
91296
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
91089
|
-
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR ||
|
|
91297
|
+
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join84(home2, ".hindsight-shim")
|
|
91090
91298
|
};
|
|
91091
91299
|
}
|
|
91092
91300
|
function registerHindsightMcpShimCommand(program3) {
|
|
@@ -91099,7 +91307,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
91099
91307
|
|
|
91100
91308
|
// src/cli/deliver-file.ts
|
|
91101
91309
|
init_client2();
|
|
91102
|
-
import { readFileSync as
|
|
91310
|
+
import { readFileSync as readFileSync72, statSync as statSync46 } from "node:fs";
|
|
91103
91311
|
import { basename as basename11 } from "node:path";
|
|
91104
91312
|
|
|
91105
91313
|
// src/delivery/onedrive.ts
|
|
@@ -91439,7 +91647,7 @@ async function defaultResolveProvider() {
|
|
|
91439
91647
|
async function runDeliverFile(localPath, deps = {}) {
|
|
91440
91648
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
91441
91649
|
const sizeOf = deps.fileSize ?? ((p) => statSync46(p).size);
|
|
91442
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
91650
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync72(p)));
|
|
91443
91651
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
91444
91652
|
let size;
|
|
91445
91653
|
try {
|
|
@@ -91760,8 +91968,8 @@ function runRedactStdin() {
|
|
|
91760
91968
|
}
|
|
91761
91969
|
|
|
91762
91970
|
// src/cli/status-ask.ts
|
|
91763
|
-
import { readFileSync as
|
|
91764
|
-
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";
|
|
91765
91973
|
import { homedir as homedir50 } from "node:os";
|
|
91766
91974
|
|
|
91767
91975
|
// src/status-ask/report.ts
|
|
@@ -92036,7 +92244,7 @@ function runReport(opts) {
|
|
|
92036
92244
|
for (const src of sources) {
|
|
92037
92245
|
let content;
|
|
92038
92246
|
try {
|
|
92039
|
-
content =
|
|
92247
|
+
content = readFileSync76(src.path, "utf-8");
|
|
92040
92248
|
} catch (err) {
|
|
92041
92249
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
92042
92250
|
`);
|
|
@@ -92083,7 +92291,7 @@ function runReport(opts) {
|
|
|
92083
92291
|
function resolveSources(explicitPath) {
|
|
92084
92292
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
92085
92293
|
const trimmed = explicitPath.trim();
|
|
92086
|
-
if (!
|
|
92294
|
+
if (!existsSync88(trimmed)) {
|
|
92087
92295
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
92088
92296
|
`);
|
|
92089
92297
|
process.exit(1);
|
|
@@ -92097,20 +92305,20 @@ function resolveSources(explicitPath) {
|
|
|
92097
92305
|
const config = loadConfig();
|
|
92098
92306
|
agentsDir = resolveAgentsDir(config);
|
|
92099
92307
|
} catch {
|
|
92100
|
-
agentsDir =
|
|
92308
|
+
agentsDir = join89(homedir50(), ".switchroom", "agents");
|
|
92101
92309
|
}
|
|
92102
|
-
if (!
|
|
92310
|
+
if (!existsSync88(agentsDir))
|
|
92103
92311
|
return [];
|
|
92104
92312
|
const sources = [];
|
|
92105
92313
|
let entries;
|
|
92106
92314
|
try {
|
|
92107
|
-
entries =
|
|
92315
|
+
entries = readdirSync32(agentsDir);
|
|
92108
92316
|
} catch {
|
|
92109
92317
|
return [];
|
|
92110
92318
|
}
|
|
92111
92319
|
for (const name of entries) {
|
|
92112
|
-
const path9 =
|
|
92113
|
-
if (
|
|
92320
|
+
const path9 = join89(agentsDir, name, "runtime-metrics.jsonl");
|
|
92321
|
+
if (existsSync88(path9)) {
|
|
92114
92322
|
sources.push({ path: path9, agent: name });
|
|
92115
92323
|
}
|
|
92116
92324
|
}
|
|
@@ -92140,45 +92348,45 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
92140
92348
|
init_paths();
|
|
92141
92349
|
import {
|
|
92142
92350
|
closeSync as closeSync14,
|
|
92143
|
-
existsSync as
|
|
92351
|
+
existsSync as existsSync89,
|
|
92144
92352
|
fsyncSync as fsyncSync8,
|
|
92145
|
-
mkdirSync as
|
|
92353
|
+
mkdirSync as mkdirSync51,
|
|
92146
92354
|
openSync as openSync14,
|
|
92147
|
-
readdirSync as
|
|
92148
|
-
readFileSync as
|
|
92149
|
-
renameSync as
|
|
92355
|
+
readdirSync as readdirSync33,
|
|
92356
|
+
readFileSync as readFileSync77,
|
|
92357
|
+
renameSync as renameSync22,
|
|
92150
92358
|
statSync as statSync48,
|
|
92151
92359
|
unlinkSync as unlinkSync18,
|
|
92152
92360
|
writeSync as writeSync10
|
|
92153
92361
|
} from "node:fs";
|
|
92154
|
-
import { join as
|
|
92362
|
+
import { join as join90, resolve as resolve53 } from "node:path";
|
|
92155
92363
|
var STAGING_SUBDIR = ".staging";
|
|
92156
92364
|
function overlayPathsFor(agent, opts = {}) {
|
|
92157
92365
|
const base = opts.root ? resolve53(opts.root, agent) : resolve53(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
92158
|
-
const scheduleDir =
|
|
92159
|
-
const scheduleStagingDir =
|
|
92160
|
-
const skillsDir =
|
|
92161
|
-
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);
|
|
92162
92370
|
return {
|
|
92163
92371
|
agentRoot: base,
|
|
92164
92372
|
scheduleDir,
|
|
92165
92373
|
scheduleStagingDir,
|
|
92166
92374
|
skillsDir,
|
|
92167
92375
|
skillsStagingDir,
|
|
92168
|
-
lockPath:
|
|
92376
|
+
lockPath: join90(base, ".lock"),
|
|
92169
92377
|
stagingDir: scheduleStagingDir
|
|
92170
92378
|
};
|
|
92171
92379
|
}
|
|
92172
92380
|
function ensureDirs(paths) {
|
|
92173
|
-
|
|
92174
|
-
|
|
92381
|
+
mkdirSync51(paths.scheduleDir, { recursive: true });
|
|
92382
|
+
mkdirSync51(paths.scheduleStagingDir, { recursive: true });
|
|
92175
92383
|
}
|
|
92176
92384
|
function ensureSkillsDirs(paths) {
|
|
92177
|
-
|
|
92178
|
-
|
|
92385
|
+
mkdirSync51(paths.skillsDir, { recursive: true });
|
|
92386
|
+
mkdirSync51(paths.skillsStagingDir, { recursive: true });
|
|
92179
92387
|
}
|
|
92180
92388
|
function withAgentLock(paths, fn) {
|
|
92181
|
-
|
|
92389
|
+
mkdirSync51(paths.agentRoot, { recursive: true });
|
|
92182
92390
|
const start = Date.now();
|
|
92183
92391
|
const TIMEOUT_MS = 5000;
|
|
92184
92392
|
let fd = null;
|
|
@@ -92219,8 +92427,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92219
92427
|
const paths = overlayPathsFor(agent, opts);
|
|
92220
92428
|
return withAgentLock(paths, () => {
|
|
92221
92429
|
ensureDirs(paths);
|
|
92222
|
-
const stagingPath =
|
|
92223
|
-
const finalPath =
|
|
92430
|
+
const stagingPath = join90(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
92431
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92224
92432
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92225
92433
|
try {
|
|
92226
92434
|
writeSync10(fd, yamlText);
|
|
@@ -92228,7 +92436,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92228
92436
|
} finally {
|
|
92229
92437
|
closeSync14(fd);
|
|
92230
92438
|
}
|
|
92231
|
-
|
|
92439
|
+
renameSync22(stagingPath, finalPath);
|
|
92232
92440
|
return finalPath;
|
|
92233
92441
|
});
|
|
92234
92442
|
}
|
|
@@ -92236,8 +92444,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92236
92444
|
const paths = overlayPathsFor(agent, opts);
|
|
92237
92445
|
return withAgentLock(paths, () => {
|
|
92238
92446
|
ensureSkillsDirs(paths);
|
|
92239
|
-
const stagingPath =
|
|
92240
|
-
const finalPath =
|
|
92447
|
+
const stagingPath = join90(paths.skillsStagingDir, `${slug}.yaml`);
|
|
92448
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92241
92449
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92242
92450
|
try {
|
|
92243
92451
|
writeSync10(fd, yamlText);
|
|
@@ -92245,15 +92453,15 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92245
92453
|
} finally {
|
|
92246
92454
|
closeSync14(fd);
|
|
92247
92455
|
}
|
|
92248
|
-
|
|
92456
|
+
renameSync22(stagingPath, finalPath);
|
|
92249
92457
|
return finalPath;
|
|
92250
92458
|
});
|
|
92251
92459
|
}
|
|
92252
92460
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
92253
92461
|
const paths = overlayPathsFor(agent, opts);
|
|
92254
92462
|
return withAgentLock(paths, () => {
|
|
92255
|
-
const finalPath =
|
|
92256
|
-
if (!
|
|
92463
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92464
|
+
if (!existsSync89(finalPath))
|
|
92257
92465
|
return false;
|
|
92258
92466
|
unlinkSync18(finalPath);
|
|
92259
92467
|
return true;
|
|
@@ -92261,15 +92469,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
92261
92469
|
}
|
|
92262
92470
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
92263
92471
|
const paths = overlayPathsFor(agent, opts);
|
|
92264
|
-
if (!
|
|
92472
|
+
if (!existsSync89(paths.skillsDir))
|
|
92265
92473
|
return [];
|
|
92266
92474
|
const out = [];
|
|
92267
|
-
for (const name of
|
|
92475
|
+
for (const name of readdirSync33(paths.skillsDir)) {
|
|
92268
92476
|
if (!/\.ya?ml$/i.test(name))
|
|
92269
92477
|
continue;
|
|
92270
|
-
const full =
|
|
92478
|
+
const full = join90(paths.skillsDir, name);
|
|
92271
92479
|
try {
|
|
92272
|
-
const raw =
|
|
92480
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92273
92481
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92274
92482
|
out.push({ slug, path: full, raw });
|
|
92275
92483
|
} catch {}
|
|
@@ -92279,8 +92487,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
92279
92487
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
92280
92488
|
const paths = overlayPathsFor(agent, opts);
|
|
92281
92489
|
return withAgentLock(paths, () => {
|
|
92282
|
-
const finalPath =
|
|
92283
|
-
if (!
|
|
92490
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92491
|
+
if (!existsSync89(finalPath))
|
|
92284
92492
|
return false;
|
|
92285
92493
|
unlinkSync18(finalPath);
|
|
92286
92494
|
return true;
|
|
@@ -92288,15 +92496,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
92288
92496
|
}
|
|
92289
92497
|
function listOverlayEntries(agent, opts = {}) {
|
|
92290
92498
|
const paths = overlayPathsFor(agent, opts);
|
|
92291
|
-
if (!
|
|
92499
|
+
if (!existsSync89(paths.scheduleDir))
|
|
92292
92500
|
return [];
|
|
92293
92501
|
const out = [];
|
|
92294
|
-
for (const name of
|
|
92502
|
+
for (const name of readdirSync33(paths.scheduleDir)) {
|
|
92295
92503
|
if (!/\.ya?ml$/i.test(name))
|
|
92296
92504
|
continue;
|
|
92297
|
-
const full =
|
|
92505
|
+
const full = join90(paths.scheduleDir, name);
|
|
92298
92506
|
try {
|
|
92299
|
-
const raw =
|
|
92507
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92300
92508
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92301
92509
|
out.push({ slug, path: full, raw });
|
|
92302
92510
|
} catch {}
|
|
@@ -92516,27 +92724,27 @@ function reconcileAgentCronOnly(agent) {
|
|
|
92516
92724
|
// src/cli/agent-config-pending.ts
|
|
92517
92725
|
import {
|
|
92518
92726
|
closeSync as closeSync15,
|
|
92519
|
-
existsSync as
|
|
92727
|
+
existsSync as existsSync90,
|
|
92520
92728
|
fsyncSync as fsyncSync9,
|
|
92521
|
-
mkdirSync as
|
|
92729
|
+
mkdirSync as mkdirSync52,
|
|
92522
92730
|
openSync as openSync15,
|
|
92523
|
-
readdirSync as
|
|
92524
|
-
readFileSync as
|
|
92525
|
-
renameSync as
|
|
92731
|
+
readdirSync as readdirSync34,
|
|
92732
|
+
readFileSync as readFileSync78,
|
|
92733
|
+
renameSync as renameSync23,
|
|
92526
92734
|
unlinkSync as unlinkSync19,
|
|
92527
|
-
writeFileSync as
|
|
92735
|
+
writeFileSync as writeFileSync33,
|
|
92528
92736
|
writeSync as writeSync11
|
|
92529
92737
|
} from "node:fs";
|
|
92530
|
-
import { join as
|
|
92738
|
+
import { join as join91 } from "node:path";
|
|
92531
92739
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
92532
92740
|
var STAGE_ID_PREFIX = "cap_";
|
|
92533
92741
|
function pendingDir(agent, opts = {}) {
|
|
92534
92742
|
const paths = overlayPathsFor(agent, opts);
|
|
92535
|
-
return
|
|
92743
|
+
return join91(paths.scheduleDir, ".pending");
|
|
92536
92744
|
}
|
|
92537
92745
|
function ensurePendingDir(agent, opts = {}) {
|
|
92538
92746
|
const dir = pendingDir(agent, opts);
|
|
92539
|
-
|
|
92747
|
+
mkdirSync52(dir, { recursive: true });
|
|
92540
92748
|
return dir;
|
|
92541
92749
|
}
|
|
92542
92750
|
function newStageId() {
|
|
@@ -92545,8 +92753,8 @@ function newStageId() {
|
|
|
92545
92753
|
function stagePendingScheduleEntry(opts) {
|
|
92546
92754
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
92547
92755
|
const stageId = opts.stageId ?? newStageId();
|
|
92548
|
-
const yamlPath =
|
|
92549
|
-
const metaPath =
|
|
92756
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92757
|
+
const metaPath = join91(dir, `${stageId}.meta.json`);
|
|
92550
92758
|
const meta = {
|
|
92551
92759
|
v: 1,
|
|
92552
92760
|
stage_id: stageId,
|
|
@@ -92565,27 +92773,27 @@ function stagePendingScheduleEntry(opts) {
|
|
|
92565
92773
|
} finally {
|
|
92566
92774
|
closeSync15(fd);
|
|
92567
92775
|
}
|
|
92568
|
-
|
|
92776
|
+
renameSync23(yamlTmp, yamlPath);
|
|
92569
92777
|
}
|
|
92570
|
-
|
|
92778
|
+
writeFileSync33(metaPath, JSON.stringify(meta, null, 2) + `
|
|
92571
92779
|
`, { mode: 384 });
|
|
92572
92780
|
return { stageId, yamlPath, metaPath };
|
|
92573
92781
|
}
|
|
92574
92782
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
92575
92783
|
const dir = pendingDir(agent, opts);
|
|
92576
|
-
if (!
|
|
92784
|
+
if (!existsSync90(dir))
|
|
92577
92785
|
return [];
|
|
92578
92786
|
const out = [];
|
|
92579
|
-
for (const name of
|
|
92787
|
+
for (const name of readdirSync34(dir).sort()) {
|
|
92580
92788
|
if (!name.endsWith(".meta.json"))
|
|
92581
92789
|
continue;
|
|
92582
92790
|
const stageId = name.slice(0, -".meta.json".length);
|
|
92583
|
-
const metaPath =
|
|
92584
|
-
const yamlPath =
|
|
92585
|
-
if (!
|
|
92791
|
+
const metaPath = join91(dir, name);
|
|
92792
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92793
|
+
if (!existsSync90(yamlPath))
|
|
92586
92794
|
continue;
|
|
92587
92795
|
try {
|
|
92588
|
-
const meta = JSON.parse(
|
|
92796
|
+
const meta = JSON.parse(readFileSync78(metaPath, "utf-8"));
|
|
92589
92797
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
92590
92798
|
continue;
|
|
92591
92799
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -92600,11 +92808,11 @@ function commitPendingScheduleEntry(opts) {
|
|
|
92600
92808
|
return { committed: false, reason: "not_found" };
|
|
92601
92809
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
92602
92810
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
92603
|
-
const finalPath =
|
|
92604
|
-
if (
|
|
92811
|
+
const finalPath = join91(paths.scheduleDir, `${slug}.yaml`);
|
|
92812
|
+
if (existsSync90(finalPath)) {
|
|
92605
92813
|
return { committed: false, reason: "slug_collision" };
|
|
92606
92814
|
}
|
|
92607
|
-
|
|
92815
|
+
renameSync23(match.yamlPath, finalPath);
|
|
92608
92816
|
unlinkSync19(match.metaPath);
|
|
92609
92817
|
return { committed: true, path: finalPath, slug };
|
|
92610
92818
|
}
|
|
@@ -92623,7 +92831,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
92623
92831
|
}
|
|
92624
92832
|
|
|
92625
92833
|
// src/cli/agent-config-write.ts
|
|
92626
|
-
import { existsSync as
|
|
92834
|
+
import { existsSync as existsSync91, readFileSync as readFileSync79 } from "node:fs";
|
|
92627
92835
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
92628
92836
|
|
|
92629
92837
|
// src/scheduler/schedule-report.ts
|
|
@@ -93013,8 +93221,8 @@ function scheduleRemove(opts) {
|
|
|
93013
93221
|
}
|
|
93014
93222
|
let priorContent = null;
|
|
93015
93223
|
try {
|
|
93016
|
-
if (
|
|
93017
|
-
priorContent =
|
|
93224
|
+
if (existsSync91(match.path))
|
|
93225
|
+
priorContent = readFileSync79(match.path, "utf-8");
|
|
93018
93226
|
} catch {}
|
|
93019
93227
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
93020
93228
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -93217,7 +93425,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93217
93425
|
}
|
|
93218
93426
|
let blob;
|
|
93219
93427
|
if (opts.jsonl) {
|
|
93220
|
-
blob =
|
|
93428
|
+
blob = existsSync91(opts.jsonl) ? readFileSync79(opts.jsonl, "utf-8") : "";
|
|
93221
93429
|
} else {
|
|
93222
93430
|
try {
|
|
93223
93431
|
blob = execFileSync28("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -93249,11 +93457,11 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93249
93457
|
|
|
93250
93458
|
// src/cli/agent-config-skill-write.ts
|
|
93251
93459
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
93252
|
-
import { existsSync as
|
|
93460
|
+
import { existsSync as existsSync92 } from "node:fs";
|
|
93253
93461
|
init_reconcile_default_skills();
|
|
93254
93462
|
init_agent_config();
|
|
93255
93463
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
93256
|
-
import { join as
|
|
93464
|
+
import { join as join92 } from "node:path";
|
|
93257
93465
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
93258
93466
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
93259
93467
|
function exitCodeFor2(code) {
|
|
@@ -93328,8 +93536,8 @@ function skillInstall(opts) {
|
|
|
93328
93536
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
93329
93537
|
}
|
|
93330
93538
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
93331
|
-
const skillPath =
|
|
93332
|
-
if (!
|
|
93539
|
+
const skillPath = join92(poolDir, skillName);
|
|
93540
|
+
if (!existsSync92(skillPath)) {
|
|
93333
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.`);
|
|
93334
93542
|
}
|
|
93335
93543
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -93493,21 +93701,21 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
93493
93701
|
// src/cli/skill.ts
|
|
93494
93702
|
import {
|
|
93495
93703
|
closeSync as closeSync16,
|
|
93496
|
-
existsSync as
|
|
93704
|
+
existsSync as existsSync93,
|
|
93497
93705
|
lstatSync as lstatSync11,
|
|
93498
|
-
mkdirSync as
|
|
93706
|
+
mkdirSync as mkdirSync53,
|
|
93499
93707
|
mkdtempSync as mkdtempSync5,
|
|
93500
93708
|
openSync as openSync16,
|
|
93501
|
-
readFileSync as
|
|
93502
|
-
readdirSync as
|
|
93709
|
+
readFileSync as readFileSync80,
|
|
93710
|
+
readdirSync as readdirSync35,
|
|
93503
93711
|
realpathSync as realpathSync7,
|
|
93504
|
-
renameSync as
|
|
93712
|
+
renameSync as renameSync24,
|
|
93505
93713
|
rmSync as rmSync18,
|
|
93506
93714
|
statSync as statSync49,
|
|
93507
|
-
writeFileSync as
|
|
93715
|
+
writeFileSync as writeFileSync34
|
|
93508
93716
|
} from "node:fs";
|
|
93509
93717
|
import { tmpdir as tmpdir6, homedir as homedir51 } from "node:os";
|
|
93510
|
-
import { dirname as
|
|
93718
|
+
import { dirname as dirname33, join as join93, relative as relative4, resolve as resolve54 } from "node:path";
|
|
93511
93719
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
93512
93720
|
|
|
93513
93721
|
// src/cli/skill-common.ts
|
|
@@ -93741,7 +93949,7 @@ function scanForClaudeP2(content) {
|
|
|
93741
93949
|
function resolveSkillsPoolDir2(override) {
|
|
93742
93950
|
const raw = override ?? "~/.switchroom/skills";
|
|
93743
93951
|
if (raw.startsWith("~/")) {
|
|
93744
|
-
return
|
|
93952
|
+
return join93(homedir51(), raw.slice(2));
|
|
93745
93953
|
}
|
|
93746
93954
|
if (raw === "~")
|
|
93747
93955
|
return homedir51();
|
|
@@ -93778,10 +93986,10 @@ function loadFromDir(dir) {
|
|
|
93778
93986
|
}
|
|
93779
93987
|
const files = {};
|
|
93780
93988
|
const walk2 = (sub) => {
|
|
93781
|
-
const entries =
|
|
93989
|
+
const entries = readdirSync35(sub, { withFileTypes: true });
|
|
93782
93990
|
for (const ent of entries) {
|
|
93783
|
-
const full =
|
|
93784
|
-
const rel =
|
|
93991
|
+
const full = join93(sub, ent.name);
|
|
93992
|
+
const rel = relative4(abs, full);
|
|
93785
93993
|
if (ent.isSymbolicLink()) {
|
|
93786
93994
|
fail3(`refusing to read symlink inside --from dir: ${rel}`);
|
|
93787
93995
|
}
|
|
@@ -93790,7 +93998,7 @@ function loadFromDir(dir) {
|
|
|
93790
93998
|
continue;
|
|
93791
93999
|
}
|
|
93792
94000
|
if (ent.isFile()) {
|
|
93793
|
-
const buf =
|
|
94001
|
+
const buf = readFileSync80(full);
|
|
93794
94002
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
93795
94003
|
}
|
|
93796
94004
|
}
|
|
@@ -93815,7 +94023,7 @@ function loadFromTarball(tarPath) {
|
|
|
93815
94023
|
fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
93816
94024
|
}
|
|
93817
94025
|
}
|
|
93818
|
-
const staging = mkdtempSync5(
|
|
94026
|
+
const staging = mkdtempSync5(join93(tmpdir6(), "skill-apply-extract-"));
|
|
93819
94027
|
try {
|
|
93820
94028
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
93821
94029
|
const r = spawnSync16("tar", [
|
|
@@ -93839,7 +94047,7 @@ function loadFromTarball(tarPath) {
|
|
|
93839
94047
|
}
|
|
93840
94048
|
}
|
|
93841
94049
|
function loadSingleFile(filePath) {
|
|
93842
|
-
const content =
|
|
94050
|
+
const content = readFileSync80(filePath, "utf-8");
|
|
93843
94051
|
return { "SKILL.md": content };
|
|
93844
94052
|
}
|
|
93845
94053
|
function loadFromStdin() {
|
|
@@ -93901,10 +94109,10 @@ function validatePayload(name, files) {
|
|
|
93901
94109
|
errors2.push(`${path9} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
93902
94110
|
}
|
|
93903
94111
|
} else if (PY_SCRIPT_RE2.test(path9)) {
|
|
93904
|
-
const tmp = mkdtempSync5(
|
|
93905
|
-
const tmpPy =
|
|
94112
|
+
const tmp = mkdtempSync5(join93(tmpdir6(), "skill-apply-py-"));
|
|
94113
|
+
const tmpPy = join93(tmp, "check.py");
|
|
93906
94114
|
try {
|
|
93907
|
-
|
|
94115
|
+
writeFileSync34(tmpPy, content);
|
|
93908
94116
|
const r = spawnSync16("python3", ["-m", "py_compile", tmpPy], {
|
|
93909
94117
|
encoding: "utf-8"
|
|
93910
94118
|
});
|
|
@@ -93922,15 +94130,15 @@ function validatePayload(name, files) {
|
|
|
93922
94130
|
function diffSummary(currentDir, files) {
|
|
93923
94131
|
const lines = [];
|
|
93924
94132
|
const currentFiles = {};
|
|
93925
|
-
if (
|
|
94133
|
+
if (existsSync93(currentDir)) {
|
|
93926
94134
|
const walk2 = (sub) => {
|
|
93927
|
-
for (const ent of
|
|
93928
|
-
const full =
|
|
93929
|
-
const rel =
|
|
94135
|
+
for (const ent of readdirSync35(sub, { withFileTypes: true })) {
|
|
94136
|
+
const full = join93(sub, ent.name);
|
|
94137
|
+
const rel = relative4(currentDir, full);
|
|
93930
94138
|
if (ent.isDirectory()) {
|
|
93931
94139
|
walk2(full);
|
|
93932
94140
|
} else if (ent.isFile()) {
|
|
93933
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
94141
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync80(full, "utf-8");
|
|
93934
94142
|
}
|
|
93935
94143
|
}
|
|
93936
94144
|
};
|
|
@@ -93958,10 +94166,10 @@ function diffSummary(currentDir, files) {
|
|
|
93958
94166
|
`);
|
|
93959
94167
|
}
|
|
93960
94168
|
function writePayload(poolDir, name, files) {
|
|
93961
|
-
if (!
|
|
93962
|
-
|
|
94169
|
+
if (!existsSync93(poolDir)) {
|
|
94170
|
+
mkdirSync53(poolDir, { recursive: true, mode: 493 });
|
|
93963
94171
|
}
|
|
93964
|
-
const target =
|
|
94172
|
+
const target = join93(poolDir, name);
|
|
93965
94173
|
let targetIsSymlink = false;
|
|
93966
94174
|
try {
|
|
93967
94175
|
const st = lstatSync11(target);
|
|
@@ -93972,15 +94180,15 @@ function writePayload(poolDir, name, files) {
|
|
|
93972
94180
|
if (targetIsSymlink) {
|
|
93973
94181
|
fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
93974
94182
|
}
|
|
93975
|
-
const staging = mkdtempSync5(
|
|
94183
|
+
const staging = mkdtempSync5(join93(poolDir, `.skill-apply-stage-${name}-`));
|
|
93976
94184
|
let oldRename = null;
|
|
93977
94185
|
try {
|
|
93978
94186
|
for (const [path9, content] of Object.entries(files)) {
|
|
93979
|
-
const full =
|
|
93980
|
-
|
|
94187
|
+
const full = join93(staging, path9);
|
|
94188
|
+
mkdirSync53(dirname33(full), { recursive: true, mode: 493 });
|
|
93981
94189
|
const fd = openSync16(full, "wx");
|
|
93982
94190
|
try {
|
|
93983
|
-
|
|
94191
|
+
writeFileSync34(fd, content);
|
|
93984
94192
|
} finally {
|
|
93985
94193
|
closeSync16(fd);
|
|
93986
94194
|
}
|
|
@@ -93996,9 +94204,9 @@ function writePayload(poolDir, name, files) {
|
|
|
93996
94204
|
} catch {}
|
|
93997
94205
|
if (targetExists) {
|
|
93998
94206
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
93999
|
-
|
|
94207
|
+
renameSync24(target, oldRename);
|
|
94000
94208
|
}
|
|
94001
|
-
|
|
94209
|
+
renameSync24(staging, target);
|
|
94002
94210
|
if (oldRename) {
|
|
94003
94211
|
rmSync18(oldRename, { recursive: true, force: true });
|
|
94004
94212
|
oldRename = null;
|
|
@@ -94007,12 +94215,12 @@ function writePayload(poolDir, name, files) {
|
|
|
94007
94215
|
try {
|
|
94008
94216
|
rmSync18(staging, { recursive: true, force: true });
|
|
94009
94217
|
} catch {}
|
|
94010
|
-
if (oldRename &&
|
|
94218
|
+
if (oldRename && existsSync93(oldRename)) {
|
|
94011
94219
|
try {
|
|
94012
|
-
if (
|
|
94220
|
+
if (existsSync93(target)) {
|
|
94013
94221
|
rmSync18(target, { recursive: true, force: true });
|
|
94014
94222
|
}
|
|
94015
|
-
|
|
94223
|
+
renameSync24(oldRename, target);
|
|
94016
94224
|
} catch {}
|
|
94017
94225
|
}
|
|
94018
94226
|
throw err2;
|
|
@@ -94030,7 +94238,7 @@ function registerSkillCommand(program3) {
|
|
|
94030
94238
|
files = loadFromStdin();
|
|
94031
94239
|
} else {
|
|
94032
94240
|
const fromPath = resolve54(opts.from);
|
|
94033
|
-
if (!
|
|
94241
|
+
if (!existsSync93(fromPath)) {
|
|
94034
94242
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
94035
94243
|
}
|
|
94036
94244
|
const st = statSync49(fromPath);
|
|
@@ -94054,7 +94262,7 @@ function registerSkillCommand(program3) {
|
|
|
94054
94262
|
}
|
|
94055
94263
|
const config = loadConfig();
|
|
94056
94264
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
94057
|
-
const currentDir =
|
|
94265
|
+
const currentDir = join93(poolDir, name);
|
|
94058
94266
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
94059
94267
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
94060
94268
|
console.log(diffSummary(currentDir, files));
|
|
@@ -94086,20 +94294,20 @@ function sumBytes(files) {
|
|
|
94086
94294
|
init_esm();
|
|
94087
94295
|
import {
|
|
94088
94296
|
closeSync as closeSync17,
|
|
94089
|
-
existsSync as
|
|
94297
|
+
existsSync as existsSync94,
|
|
94090
94298
|
lstatSync as lstatSync12,
|
|
94091
|
-
mkdirSync as
|
|
94299
|
+
mkdirSync as mkdirSync54,
|
|
94092
94300
|
mkdtempSync as mkdtempSync6,
|
|
94093
94301
|
openSync as openSync17,
|
|
94094
|
-
readFileSync as
|
|
94095
|
-
readdirSync as
|
|
94096
|
-
renameSync as
|
|
94302
|
+
readFileSync as readFileSync81,
|
|
94303
|
+
readdirSync as readdirSync36,
|
|
94304
|
+
renameSync as renameSync25,
|
|
94097
94305
|
rmSync as rmSync19,
|
|
94098
94306
|
statSync as statSync50,
|
|
94099
94307
|
utimesSync,
|
|
94100
|
-
writeFileSync as
|
|
94308
|
+
writeFileSync as writeFileSync35
|
|
94101
94309
|
} from "node:fs";
|
|
94102
|
-
import { dirname as
|
|
94310
|
+
import { dirname as dirname34, join as join94, relative as relative5, resolve as resolve55 } from "node:path";
|
|
94103
94311
|
import { homedir as homedir52, tmpdir as tmpdir7 } from "node:os";
|
|
94104
94312
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
94105
94313
|
init_helpers();
|
|
@@ -94111,18 +94319,18 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
94111
94319
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
94112
94320
|
function resolveConfigSkillsDir(agent) {
|
|
94113
94321
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
94114
|
-
const candidate = override ? resolve55(override) :
|
|
94115
|
-
if (!
|
|
94322
|
+
const candidate = override ? resolve55(override) : join94(homedir52(), ".switchroom-config");
|
|
94323
|
+
if (!existsSync94(candidate))
|
|
94116
94324
|
return null;
|
|
94117
|
-
return
|
|
94325
|
+
return join94(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
94118
94326
|
}
|
|
94119
94327
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
94120
94328
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
94121
94329
|
try {
|
|
94122
|
-
if (!
|
|
94330
|
+
if (!existsSync94(configSkillsRoot))
|
|
94123
94331
|
return;
|
|
94124
94332
|
const now = Date.now();
|
|
94125
|
-
for (const ent of
|
|
94333
|
+
for (const ent of readdirSync36(configSkillsRoot)) {
|
|
94126
94334
|
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
94127
94335
|
if (!m)
|
|
94128
94336
|
continue;
|
|
@@ -94132,7 +94340,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
94132
94340
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
94133
94341
|
continue;
|
|
94134
94342
|
try {
|
|
94135
|
-
rmSync19(
|
|
94343
|
+
rmSync19(join94(configSkillsRoot, ent), { recursive: true, force: true });
|
|
94136
94344
|
} catch {}
|
|
94137
94345
|
}
|
|
94138
94346
|
} catch {}
|
|
@@ -94141,7 +94349,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94141
94349
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
94142
94350
|
if (!configSkillsRoot)
|
|
94143
94351
|
return;
|
|
94144
|
-
const dest =
|
|
94352
|
+
const dest = join94(configSkillsRoot, name);
|
|
94145
94353
|
try {
|
|
94146
94354
|
if (liveSkillDir !== null) {
|
|
94147
94355
|
try {
|
|
@@ -94155,35 +94363,35 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94155
94363
|
}
|
|
94156
94364
|
if (liveSkillDir === null) {
|
|
94157
94365
|
sweepMirrorPriors(configSkillsRoot);
|
|
94158
|
-
if (
|
|
94159
|
-
const trash =
|
|
94160
|
-
|
|
94366
|
+
if (existsSync94(dest)) {
|
|
94367
|
+
const trash = join94(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
94368
|
+
renameSync25(dest, trash);
|
|
94161
94369
|
}
|
|
94162
94370
|
return;
|
|
94163
94371
|
}
|
|
94164
|
-
|
|
94372
|
+
mkdirSync54(configSkillsRoot, { recursive: true, mode: 493 });
|
|
94165
94373
|
sweepMirrorPriors(configSkillsRoot);
|
|
94166
|
-
const staging = mkdtempSync6(
|
|
94374
|
+
const staging = mkdtempSync6(join94(configSkillsRoot, `.${name}-staging-`));
|
|
94167
94375
|
const walk2 = (src, dst) => {
|
|
94168
|
-
|
|
94169
|
-
for (const ent of
|
|
94170
|
-
const s =
|
|
94171
|
-
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);
|
|
94172
94380
|
if (ent.isSymbolicLink())
|
|
94173
94381
|
continue;
|
|
94174
94382
|
if (ent.isDirectory())
|
|
94175
94383
|
walk2(s, d);
|
|
94176
94384
|
else if (ent.isFile()) {
|
|
94177
|
-
|
|
94385
|
+
writeFileSync35(d, readFileSync81(s));
|
|
94178
94386
|
}
|
|
94179
94387
|
}
|
|
94180
94388
|
};
|
|
94181
94389
|
walk2(liveSkillDir, staging);
|
|
94182
|
-
if (
|
|
94183
|
-
const prior =
|
|
94184
|
-
|
|
94390
|
+
if (existsSync94(dest)) {
|
|
94391
|
+
const prior = join94(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
94392
|
+
renameSync25(dest, prior);
|
|
94185
94393
|
}
|
|
94186
|
-
|
|
94394
|
+
renameSync25(staging, dest);
|
|
94187
94395
|
} catch (err2) {
|
|
94188
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.
|
|
94189
94397
|
`));
|
|
@@ -94210,20 +94418,20 @@ function resolveAgent(opts) {
|
|
|
94210
94418
|
function resolveAgentsRoot(opts) {
|
|
94211
94419
|
if (opts.root)
|
|
94212
94420
|
return resolve55(opts.root);
|
|
94213
|
-
return
|
|
94421
|
+
return join94(homedir52(), ".switchroom", "agents");
|
|
94214
94422
|
}
|
|
94215
94423
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
94216
|
-
return
|
|
94424
|
+
return join94(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
94217
94425
|
}
|
|
94218
94426
|
function trashDir(agentsRoot, agent) {
|
|
94219
|
-
return
|
|
94427
|
+
return join94(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
94220
94428
|
}
|
|
94221
94429
|
function countPersonalSkills(agentsRoot, agent) {
|
|
94222
|
-
const skillsDir =
|
|
94223
|
-
if (!
|
|
94430
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94431
|
+
if (!existsSync94(skillsDir))
|
|
94224
94432
|
return 0;
|
|
94225
94433
|
let n = 0;
|
|
94226
|
-
for (const ent of
|
|
94434
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94227
94435
|
if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
|
|
94228
94436
|
n += 1;
|
|
94229
94437
|
}
|
|
@@ -94256,18 +94464,18 @@ function loadFromDir2(dir) {
|
|
|
94256
94464
|
}
|
|
94257
94465
|
const files = {};
|
|
94258
94466
|
const walk2 = (sub) => {
|
|
94259
|
-
for (const ent of
|
|
94260
|
-
const full =
|
|
94467
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94468
|
+
const full = join94(sub, ent.name);
|
|
94261
94469
|
if (ent.isSymbolicLink()) {
|
|
94262
|
-
fail4(`refusing to read symlink in --from dir: ${
|
|
94470
|
+
fail4(`refusing to read symlink in --from dir: ${relative5(abs, full)}`);
|
|
94263
94471
|
}
|
|
94264
94472
|
if (ent.isDirectory()) {
|
|
94265
94473
|
walk2(full);
|
|
94266
94474
|
continue;
|
|
94267
94475
|
}
|
|
94268
94476
|
if (ent.isFile()) {
|
|
94269
|
-
const rel =
|
|
94270
|
-
files[rel] =
|
|
94477
|
+
const rel = relative5(abs, full).replace(/\\/g, "/");
|
|
94478
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94271
94479
|
}
|
|
94272
94480
|
}
|
|
94273
94481
|
};
|
|
@@ -94310,10 +94518,10 @@ function behavioralValidate(files) {
|
|
|
94310
94518
|
errors2.push(`${path9} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
94311
94519
|
}
|
|
94312
94520
|
} else if (PY_SCRIPT_RE.test(path9)) {
|
|
94313
|
-
const tmp = mkdtempSync6(
|
|
94314
|
-
const tmpPy =
|
|
94521
|
+
const tmp = mkdtempSync6(join94(tmpdir7(), "skill-personal-py-"));
|
|
94522
|
+
const tmpPy = join94(tmp, "check.py");
|
|
94315
94523
|
try {
|
|
94316
|
-
|
|
94524
|
+
writeFileSync35(tmpPy, content);
|
|
94317
94525
|
const r = spawnSync17("python3", ["-m", "py_compile", tmpPy], {
|
|
94318
94526
|
encoding: "utf-8"
|
|
94319
94527
|
});
|
|
@@ -94329,13 +94537,13 @@ function behavioralValidate(files) {
|
|
|
94329
94537
|
}
|
|
94330
94538
|
function sweepTrash(agentsRoot, agent) {
|
|
94331
94539
|
const trash = trashDir(agentsRoot, agent);
|
|
94332
|
-
if (!
|
|
94540
|
+
if (!existsSync94(trash))
|
|
94333
94541
|
return;
|
|
94334
94542
|
const now = Date.now();
|
|
94335
|
-
for (const ent of
|
|
94543
|
+
for (const ent of readdirSync36(trash, { withFileTypes: true })) {
|
|
94336
94544
|
if (!ent.isDirectory())
|
|
94337
94545
|
continue;
|
|
94338
|
-
const entPath =
|
|
94546
|
+
const entPath = join94(trash, ent.name);
|
|
94339
94547
|
try {
|
|
94340
94548
|
const st = statSync50(entPath);
|
|
94341
94549
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
@@ -94355,16 +94563,16 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94355
94563
|
if (targetIsSymlink) {
|
|
94356
94564
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
94357
94565
|
}
|
|
94358
|
-
|
|
94359
|
-
const staging = mkdtempSync6(
|
|
94566
|
+
mkdirSync54(dirname34(targetDir), { recursive: true, mode: 493 });
|
|
94567
|
+
const staging = mkdtempSync6(join94(dirname34(targetDir), `.skill-personal-stage-`));
|
|
94360
94568
|
let oldRename = null;
|
|
94361
94569
|
try {
|
|
94362
94570
|
for (const [path9, content] of Object.entries(files)) {
|
|
94363
|
-
const full =
|
|
94364
|
-
|
|
94571
|
+
const full = join94(staging, path9);
|
|
94572
|
+
mkdirSync54(dirname34(full), { recursive: true, mode: 493 });
|
|
94365
94573
|
const fd = openSync17(full, "wx");
|
|
94366
94574
|
try {
|
|
94367
|
-
|
|
94575
|
+
writeFileSync35(fd, content);
|
|
94368
94576
|
} finally {
|
|
94369
94577
|
closeSync17(fd);
|
|
94370
94578
|
}
|
|
@@ -94380,9 +94588,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94380
94588
|
} catch {}
|
|
94381
94589
|
if (targetExists) {
|
|
94382
94590
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
94383
|
-
|
|
94591
|
+
renameSync25(targetDir, oldRename);
|
|
94384
94592
|
}
|
|
94385
|
-
|
|
94593
|
+
renameSync25(staging, targetDir);
|
|
94386
94594
|
if (oldRename) {
|
|
94387
94595
|
rmSync19(oldRename, { recursive: true, force: true });
|
|
94388
94596
|
oldRename = null;
|
|
@@ -94391,12 +94599,12 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94391
94599
|
try {
|
|
94392
94600
|
rmSync19(staging, { recursive: true, force: true });
|
|
94393
94601
|
} catch {}
|
|
94394
|
-
if (oldRename &&
|
|
94602
|
+
if (oldRename && existsSync94(oldRename)) {
|
|
94395
94603
|
try {
|
|
94396
|
-
if (
|
|
94604
|
+
if (existsSync94(targetDir)) {
|
|
94397
94605
|
rmSync19(targetDir, { recursive: true, force: true });
|
|
94398
94606
|
}
|
|
94399
|
-
|
|
94607
|
+
renameSync25(oldRename, targetDir);
|
|
94400
94608
|
} catch {}
|
|
94401
94609
|
}
|
|
94402
94610
|
throw err2;
|
|
@@ -94459,7 +94667,7 @@ function loadFiles(opts) {
|
|
|
94459
94667
|
return loadFromStdin2();
|
|
94460
94668
|
}
|
|
94461
94669
|
const p = resolve55(opts.from);
|
|
94462
|
-
if (!
|
|
94670
|
+
if (!existsSync94(p)) {
|
|
94463
94671
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
94464
94672
|
}
|
|
94465
94673
|
const st = statSync50(p);
|
|
@@ -94467,7 +94675,7 @@ function loadFiles(opts) {
|
|
|
94467
94675
|
return loadFromDir2(p);
|
|
94468
94676
|
}
|
|
94469
94677
|
if (p.endsWith(".md")) {
|
|
94470
|
-
return { "SKILL.md":
|
|
94678
|
+
return { "SKILL.md": readFileSync81(p, "utf-8") };
|
|
94471
94679
|
}
|
|
94472
94680
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
94473
94681
|
}
|
|
@@ -94507,10 +94715,10 @@ function editPersonalAction(name, opts) {
|
|
|
94507
94715
|
}
|
|
94508
94716
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
94509
94717
|
function defaultSharedRoot() {
|
|
94510
|
-
return
|
|
94718
|
+
return join94(homedir52(), ".switchroom", "skills");
|
|
94511
94719
|
}
|
|
94512
94720
|
function defaultBundledRoot() {
|
|
94513
|
-
return
|
|
94721
|
+
return join94(homedir52(), ".switchroom", "skills", "_bundled");
|
|
94514
94722
|
}
|
|
94515
94723
|
function resolveCloneSource(source, opts) {
|
|
94516
94724
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -94520,8 +94728,8 @@ function resolveCloneSource(source, opts) {
|
|
|
94520
94728
|
const tier = m[1];
|
|
94521
94729
|
const slug = m[2];
|
|
94522
94730
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
94523
|
-
const dir =
|
|
94524
|
-
if (!
|
|
94731
|
+
const dir = join94(root, slug);
|
|
94732
|
+
if (!existsSync94(dir)) {
|
|
94525
94733
|
fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
94526
94734
|
}
|
|
94527
94735
|
const st = lstatSync12(dir);
|
|
@@ -94535,8 +94743,8 @@ function readSourceFiles(dir) {
|
|
|
94535
94743
|
const files = {};
|
|
94536
94744
|
const skipped = [];
|
|
94537
94745
|
const walk2 = (sub) => {
|
|
94538
|
-
for (const ent of
|
|
94539
|
-
const full =
|
|
94746
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94747
|
+
const full = join94(sub, ent.name);
|
|
94540
94748
|
if (ent.isSymbolicLink()) {
|
|
94541
94749
|
continue;
|
|
94542
94750
|
}
|
|
@@ -94545,7 +94753,7 @@ function readSourceFiles(dir) {
|
|
|
94545
94753
|
continue;
|
|
94546
94754
|
}
|
|
94547
94755
|
if (ent.isFile()) {
|
|
94548
|
-
const rel =
|
|
94756
|
+
const rel = relative5(dir, full).replace(/\\/g, "/");
|
|
94549
94757
|
if (!validateRelPath(rel)) {
|
|
94550
94758
|
skipped.push(rel);
|
|
94551
94759
|
continue;
|
|
@@ -94556,7 +94764,7 @@ function readSourceFiles(dir) {
|
|
|
94556
94764
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
94557
94765
|
}
|
|
94558
94766
|
} catch {}
|
|
94559
|
-
files[rel] =
|
|
94767
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94560
94768
|
}
|
|
94561
94769
|
}
|
|
94562
94770
|
};
|
|
@@ -94645,10 +94853,10 @@ function removePersonalAction(name, opts) {
|
|
|
94645
94853
|
throw err2;
|
|
94646
94854
|
}
|
|
94647
94855
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
94648
|
-
|
|
94856
|
+
mkdirSync54(trashRoot2, { recursive: true, mode: 493 });
|
|
94649
94857
|
const ts = Date.now();
|
|
94650
|
-
const trashTarget =
|
|
94651
|
-
|
|
94858
|
+
const trashTarget = join94(trashRoot2, `${name}-${ts}`);
|
|
94859
|
+
renameSync25(target, trashTarget);
|
|
94652
94860
|
const now = new Date(ts);
|
|
94653
94861
|
utimesSync(trashTarget, now, now);
|
|
94654
94862
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -94666,27 +94874,27 @@ function listPersonalAction(opts) {
|
|
|
94666
94874
|
const agent = resolveAgent(opts);
|
|
94667
94875
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
94668
94876
|
sweepTrash(agentsRoot, agent);
|
|
94669
|
-
const skillsDir =
|
|
94877
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94670
94878
|
const personal = [];
|
|
94671
|
-
if (
|
|
94672
|
-
for (const ent of
|
|
94879
|
+
if (existsSync94(skillsDir)) {
|
|
94880
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94673
94881
|
if (!ent.isDirectory())
|
|
94674
94882
|
continue;
|
|
94675
94883
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
94676
94884
|
continue;
|
|
94677
94885
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
94678
|
-
const skillPath =
|
|
94886
|
+
const skillPath = join94(skillsDir, ent.name);
|
|
94679
94887
|
let fileCount = 0;
|
|
94680
94888
|
let totalBytes = 0;
|
|
94681
94889
|
const walk2 = (sub) => {
|
|
94682
|
-
for (const e of
|
|
94890
|
+
for (const e of readdirSync36(sub, { withFileTypes: true })) {
|
|
94683
94891
|
if (e.isFile()) {
|
|
94684
94892
|
fileCount += 1;
|
|
94685
94893
|
try {
|
|
94686
|
-
totalBytes += statSync50(
|
|
94894
|
+
totalBytes += statSync50(join94(sub, e.name)).size;
|
|
94687
94895
|
} catch {}
|
|
94688
94896
|
} else if (e.isDirectory()) {
|
|
94689
|
-
walk2(
|
|
94897
|
+
walk2(join94(sub, e.name));
|
|
94690
94898
|
}
|
|
94691
94899
|
}
|
|
94692
94900
|
};
|
|
@@ -94725,11 +94933,11 @@ function registerSkillPersonalCommands(program3) {
|
|
|
94725
94933
|
// src/cli/self-improve-propose-skill.ts
|
|
94726
94934
|
import { createConnection as createConnection4 } from "node:net";
|
|
94727
94935
|
import { homedir as homedir53 } from "node:os";
|
|
94728
|
-
import { join as
|
|
94729
|
-
import { readFileSync as
|
|
94936
|
+
import { join as join95 } from "node:path";
|
|
94937
|
+
import { readFileSync as readFileSync82 } from "node:fs";
|
|
94730
94938
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
94731
94939
|
function gatewaySocketPath() {
|
|
94732
|
-
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"));
|
|
94733
94941
|
}
|
|
94734
94942
|
function fail5(msg, code = 1) {
|
|
94735
94943
|
console.error(msg);
|
|
@@ -94764,7 +94972,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94764
94972
|
fail5("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
|
|
94765
94973
|
let draft;
|
|
94766
94974
|
try {
|
|
94767
|
-
draft = JSON.parse(
|
|
94975
|
+
draft = JSON.parse(readFileSync82(opts.draft, "utf-8"));
|
|
94768
94976
|
} catch (e) {
|
|
94769
94977
|
fail5(`failed to read/parse --draft: ${e.message}`);
|
|
94770
94978
|
}
|
|
@@ -94801,9 +95009,9 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94801
95009
|
init_esm();
|
|
94802
95010
|
init_helpers();
|
|
94803
95011
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
94804
|
-
import { existsSync as
|
|
95012
|
+
import { existsSync as existsSync95, readdirSync as readdirSync37, readFileSync as readFileSync83, statSync as statSync51 } from "node:fs";
|
|
94805
95013
|
import { homedir as homedir54 } from "node:os";
|
|
94806
|
-
import { join as
|
|
95014
|
+
import { join as join96, resolve as resolve56 } from "node:path";
|
|
94807
95015
|
var PERSONAL_PREFIX2 = "personal-";
|
|
94808
95016
|
var BUNDLED_SUBDIR = "_bundled";
|
|
94809
95017
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
@@ -94817,12 +95025,12 @@ function defaultBundledRoot2() {
|
|
|
94817
95025
|
return resolve56(homedir54(), ".switchroom/skills/_bundled");
|
|
94818
95026
|
}
|
|
94819
95027
|
function readSkillFrontmatter(skillDir) {
|
|
94820
|
-
const mdPath =
|
|
94821
|
-
if (!
|
|
95028
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
95029
|
+
if (!existsSync95(mdPath))
|
|
94822
95030
|
return null;
|
|
94823
95031
|
let content;
|
|
94824
95032
|
try {
|
|
94825
|
-
content =
|
|
95033
|
+
content = readFileSync83(mdPath, "utf-8");
|
|
94826
95034
|
} catch {
|
|
94827
95035
|
return null;
|
|
94828
95036
|
}
|
|
@@ -94850,7 +95058,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
94850
95058
|
return { fm: parsed };
|
|
94851
95059
|
}
|
|
94852
95060
|
function statSkillMd(skillDir) {
|
|
94853
|
-
const mdPath =
|
|
95061
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
94854
95062
|
try {
|
|
94855
95063
|
const st = statSync51(mdPath);
|
|
94856
95064
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
@@ -94861,20 +95069,20 @@ function statSkillMd(skillDir) {
|
|
|
94861
95069
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
94862
95070
|
if (!AGENT_NAME_RE3.test(agent))
|
|
94863
95071
|
return [];
|
|
94864
|
-
const skillsDir =
|
|
94865
|
-
if (!
|
|
95072
|
+
const skillsDir = join96(agentsRoot, agent, ".claude/skills");
|
|
95073
|
+
if (!existsSync95(skillsDir))
|
|
94866
95074
|
return [];
|
|
94867
95075
|
const out = [];
|
|
94868
95076
|
let entries;
|
|
94869
95077
|
try {
|
|
94870
|
-
entries =
|
|
95078
|
+
entries = readdirSync37(skillsDir);
|
|
94871
95079
|
} catch {
|
|
94872
95080
|
return [];
|
|
94873
95081
|
}
|
|
94874
95082
|
for (const ent of entries) {
|
|
94875
95083
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
94876
95084
|
continue;
|
|
94877
|
-
const dirPath =
|
|
95085
|
+
const dirPath = join96(skillsDir, ent);
|
|
94878
95086
|
try {
|
|
94879
95087
|
if (!statSync51(dirPath).isDirectory())
|
|
94880
95088
|
continue;
|
|
@@ -94900,12 +95108,12 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
94900
95108
|
return out;
|
|
94901
95109
|
}
|
|
94902
95110
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
94903
|
-
if (!
|
|
95111
|
+
if (!existsSync95(sharedRoot))
|
|
94904
95112
|
return [];
|
|
94905
95113
|
const out = [];
|
|
94906
95114
|
let entries;
|
|
94907
95115
|
try {
|
|
94908
|
-
entries =
|
|
95116
|
+
entries = readdirSync37(sharedRoot);
|
|
94909
95117
|
} catch {
|
|
94910
95118
|
return [];
|
|
94911
95119
|
}
|
|
@@ -94914,7 +95122,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94914
95122
|
continue;
|
|
94915
95123
|
if (ent.startsWith("."))
|
|
94916
95124
|
continue;
|
|
94917
|
-
const dirPath =
|
|
95125
|
+
const dirPath = join96(sharedRoot, ent);
|
|
94918
95126
|
try {
|
|
94919
95127
|
if (!statSync51(dirPath).isDirectory())
|
|
94920
95128
|
continue;
|
|
@@ -94938,19 +95146,19 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94938
95146
|
return out;
|
|
94939
95147
|
}
|
|
94940
95148
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
94941
|
-
if (!
|
|
95149
|
+
if (!existsSync95(bundledRoot))
|
|
94942
95150
|
return [];
|
|
94943
95151
|
const out = [];
|
|
94944
95152
|
let entries;
|
|
94945
95153
|
try {
|
|
94946
|
-
entries =
|
|
95154
|
+
entries = readdirSync37(bundledRoot);
|
|
94947
95155
|
} catch {
|
|
94948
95156
|
return [];
|
|
94949
95157
|
}
|
|
94950
95158
|
for (const ent of entries) {
|
|
94951
95159
|
if (ent.startsWith("."))
|
|
94952
95160
|
continue;
|
|
94953
|
-
const dirPath =
|
|
95161
|
+
const dirPath = join96(bundledRoot, ent);
|
|
94954
95162
|
try {
|
|
94955
95163
|
if (!statSync51(dirPath).isDirectory())
|
|
94956
95164
|
continue;
|
|
@@ -95096,18 +95304,18 @@ init_source();
|
|
|
95096
95304
|
init_helpers();
|
|
95097
95305
|
init_operator_uid();
|
|
95098
95306
|
import {
|
|
95099
|
-
existsSync as
|
|
95100
|
-
mkdirSync as
|
|
95101
|
-
readdirSync as
|
|
95102
|
-
readFileSync as
|
|
95103
|
-
writeFileSync as
|
|
95307
|
+
existsSync as existsSync97,
|
|
95308
|
+
mkdirSync as mkdirSync55,
|
|
95309
|
+
readdirSync as readdirSync38,
|
|
95310
|
+
readFileSync as readFileSync85,
|
|
95311
|
+
writeFileSync as writeFileSync36,
|
|
95104
95312
|
statSync as statSync52,
|
|
95105
95313
|
lstatSync as lstatSync13,
|
|
95106
95314
|
realpathSync as realpathSync8,
|
|
95107
95315
|
copyFileSync as copyFileSync13
|
|
95108
95316
|
} from "node:fs";
|
|
95109
95317
|
import { homedir as homedir55 } from "node:os";
|
|
95110
|
-
import { join as
|
|
95318
|
+
import { join as join97 } from "node:path";
|
|
95111
95319
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
95112
95320
|
|
|
95113
95321
|
// src/cli/singleton-stale-cleanup.ts
|
|
@@ -95456,7 +95664,7 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir55()) {
|
|
|
95456
95664
|
return resolved;
|
|
95457
95665
|
}
|
|
95458
95666
|
function resolveHostdSkillsTarget(hostHome) {
|
|
95459
|
-
const skillsPath =
|
|
95667
|
+
const skillsPath = join97(hostHome, ".switchroom", "skills");
|
|
95460
95668
|
let st;
|
|
95461
95669
|
try {
|
|
95462
95670
|
st = lstatSync13(skillsPath);
|
|
@@ -95473,21 +95681,21 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
95473
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.`);
|
|
95474
95682
|
return;
|
|
95475
95683
|
}
|
|
95476
|
-
if (!
|
|
95684
|
+
if (!existsSync97(target)) {
|
|
95477
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.`);
|
|
95478
95686
|
return;
|
|
95479
95687
|
}
|
|
95480
95688
|
return target;
|
|
95481
95689
|
}
|
|
95482
95690
|
function hostdDir() {
|
|
95483
|
-
return
|
|
95691
|
+
return join97(homedir55(), ".switchroom", "hostd");
|
|
95484
95692
|
}
|
|
95485
95693
|
function hostdComposePath() {
|
|
95486
|
-
return
|
|
95694
|
+
return join97(hostdDir(), "docker-compose.yml");
|
|
95487
95695
|
}
|
|
95488
95696
|
function backupExistingCompose() {
|
|
95489
95697
|
const p = hostdComposePath();
|
|
95490
|
-
if (!
|
|
95698
|
+
if (!existsSync97(p))
|
|
95491
95699
|
return null;
|
|
95492
95700
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95493
95701
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95520,7 +95728,7 @@ async function doInstall(opts, program3) {
|
|
|
95520
95728
|
}
|
|
95521
95729
|
const dir = hostdDir();
|
|
95522
95730
|
const composePath = hostdComposePath();
|
|
95523
|
-
|
|
95731
|
+
mkdirSync55(dir, { recursive: true });
|
|
95524
95732
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
95525
95733
|
const guard = checkDowngrade({
|
|
95526
95734
|
container: "switchroom-hostd",
|
|
@@ -95553,7 +95761,7 @@ async function doInstall(opts, program3) {
|
|
|
95553
95761
|
const bak = backupExistingCompose();
|
|
95554
95762
|
if (bak)
|
|
95555
95763
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95556
|
-
|
|
95764
|
+
writeFileSync36(composePath, yaml, "utf8");
|
|
95557
95765
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
95558
95766
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
95559
95767
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -95585,7 +95793,7 @@ function doStatus() {
|
|
|
95585
95793
|
const composeYml = hostdComposePath();
|
|
95586
95794
|
console.log(source_default.bold("switchroom-hostd"));
|
|
95587
95795
|
console.log("");
|
|
95588
|
-
if (!
|
|
95796
|
+
if (!existsSync97(composeYml)) {
|
|
95589
95797
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95590
95798
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
95591
95799
|
return;
|
|
@@ -95606,14 +95814,14 @@ function doStatus() {
|
|
|
95606
95814
|
} else {
|
|
95607
95815
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
95608
95816
|
}
|
|
95609
|
-
if (
|
|
95817
|
+
if (existsSync97(dir)) {
|
|
95610
95818
|
const entries = [];
|
|
95611
95819
|
try {
|
|
95612
|
-
for (const name of
|
|
95820
|
+
for (const name of readdirSync38(dir)) {
|
|
95613
95821
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
95614
95822
|
continue;
|
|
95615
|
-
const sockPath =
|
|
95616
|
-
if (
|
|
95823
|
+
const sockPath = join97(dir, name, "sock");
|
|
95824
|
+
if (existsSync97(sockPath)) {
|
|
95617
95825
|
const st = statSync52(sockPath);
|
|
95618
95826
|
if ((st.mode & 61440) === 49152) {
|
|
95619
95827
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -95632,7 +95840,7 @@ function doStatus() {
|
|
|
95632
95840
|
}
|
|
95633
95841
|
function doUninstall() {
|
|
95634
95842
|
const composeYml = hostdComposePath();
|
|
95635
|
-
if (!
|
|
95843
|
+
if (!existsSync97(composeYml)) {
|
|
95636
95844
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
95637
95845
|
return;
|
|
95638
95846
|
}
|
|
@@ -95656,12 +95864,12 @@ function registerHostdCommand(program3) {
|
|
|
95656
95864
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
95657
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) => {
|
|
95658
95866
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
95659
|
-
if (!
|
|
95867
|
+
if (!existsSync97(logPath)) {
|
|
95660
95868
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
95661
95869
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
95662
95870
|
return;
|
|
95663
95871
|
}
|
|
95664
|
-
const raw =
|
|
95872
|
+
const raw = readFileSync85(logPath, "utf-8");
|
|
95665
95873
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
95666
95874
|
const filters = {
|
|
95667
95875
|
agent: opts.agent,
|
|
@@ -95704,9 +95912,9 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
95704
95912
|
init_source();
|
|
95705
95913
|
init_helpers();
|
|
95706
95914
|
init_operator_uid();
|
|
95707
|
-
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";
|
|
95708
95916
|
import { homedir as homedir56 } from "node:os";
|
|
95709
|
-
import { join as
|
|
95917
|
+
import { join as join98 } from "node:path";
|
|
95710
95918
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
95711
95919
|
function resolveWebImageTag(explicitTag, release) {
|
|
95712
95920
|
if (explicitTag)
|
|
@@ -95806,14 +96014,14 @@ services:
|
|
|
95806
96014
|
`;
|
|
95807
96015
|
}
|
|
95808
96016
|
function webdDir() {
|
|
95809
|
-
return
|
|
96017
|
+
return join98(homedir56(), ".switchroom", "web");
|
|
95810
96018
|
}
|
|
95811
96019
|
function webdComposePath() {
|
|
95812
|
-
return
|
|
96020
|
+
return join98(webdDir(), "docker-compose.yml");
|
|
95813
96021
|
}
|
|
95814
96022
|
function backupExistingCompose2() {
|
|
95815
96023
|
const p = webdComposePath();
|
|
95816
|
-
if (!
|
|
96024
|
+
if (!existsSync98(p))
|
|
95817
96025
|
return null;
|
|
95818
96026
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95819
96027
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95838,7 +96046,7 @@ async function doInstall2(opts, program3) {
|
|
|
95838
96046
|
}
|
|
95839
96047
|
const dir = webdDir();
|
|
95840
96048
|
const composePath = webdComposePath();
|
|
95841
|
-
|
|
96049
|
+
mkdirSync56(dir, { recursive: true });
|
|
95842
96050
|
const cfg = getConfig(program3);
|
|
95843
96051
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
95844
96052
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -95873,7 +96081,7 @@ async function doInstall2(opts, program3) {
|
|
|
95873
96081
|
const bak = backupExistingCompose2();
|
|
95874
96082
|
if (bak)
|
|
95875
96083
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95876
|
-
|
|
96084
|
+
writeFileSync37(composePath, yaml, "utf8");
|
|
95877
96085
|
try {
|
|
95878
96086
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
95879
96087
|
chownSync9(dir, operatorUid, operatorUid);
|
|
@@ -95915,7 +96123,7 @@ function doStatus2() {
|
|
|
95915
96123
|
const composeYml = webdComposePath();
|
|
95916
96124
|
console.log(source_default.bold("switchroom-web"));
|
|
95917
96125
|
console.log("");
|
|
95918
|
-
if (!
|
|
96126
|
+
if (!existsSync98(composeYml)) {
|
|
95919
96127
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95920
96128
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
95921
96129
|
return;
|
|
@@ -95939,7 +96147,7 @@ function doStatus2() {
|
|
|
95939
96147
|
}
|
|
95940
96148
|
function doUninstall2() {
|
|
95941
96149
|
const composeYml = webdComposePath();
|
|
95942
|
-
if (!
|
|
96150
|
+
if (!existsSync98(composeYml)) {
|
|
95943
96151
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
95944
96152
|
return;
|
|
95945
96153
|
}
|
|
@@ -95969,9 +96177,9 @@ function registerWebdCommand(program3) {
|
|
|
95969
96177
|
// src/cli/host-repair.ts
|
|
95970
96178
|
init_source();
|
|
95971
96179
|
import { homedir as homedir57 } from "node:os";
|
|
95972
|
-
import { join as
|
|
96180
|
+
import { join as join99 } from "node:path";
|
|
95973
96181
|
var ARTIFACT_ALLOWLIST = {
|
|
95974
|
-
dockerComposePluginDir: (home2) =>
|
|
96182
|
+
dockerComposePluginDir: (home2) => join99(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
95975
96183
|
stateSentinel: "/state"
|
|
95976
96184
|
};
|
|
95977
96185
|
function isStateBogusAutoDir(probe2) {
|
|
@@ -96125,7 +96333,7 @@ function applyMountRepairs(items, deps) {
|
|
|
96125
96333
|
function registerHostCommand(program3) {
|
|
96126
96334
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
96127
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) => {
|
|
96128
|
-
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync:
|
|
96336
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync: readdirSync39 } = await import("node:fs");
|
|
96129
96337
|
const probe2 = {
|
|
96130
96338
|
lstat(path9) {
|
|
96131
96339
|
try {
|
|
@@ -96136,7 +96344,7 @@ function registerHostCommand(program3) {
|
|
|
96136
96344
|
},
|
|
96137
96345
|
readdir(path9) {
|
|
96138
96346
|
try {
|
|
96139
|
-
return
|
|
96347
|
+
return readdirSync39(path9);
|
|
96140
96348
|
} catch {
|
|
96141
96349
|
return null;
|
|
96142
96350
|
}
|