switchroom 0.19.8 → 0.19.10
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 +957 -721
- package/dist/host-control/main.js +17 -15
- package/dist/vault/approvals/kernel-server.js +5 -4
- package/dist/vault/broker/server.js +9 -8
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +57 -11
- package/telegram-plugin/render/unsupported-token-guard.ts +121 -0
- package/telegram-plugin/rich-send.ts +9 -0
- package/telegram-plugin/tests/render/unsupported-token-guard.test.ts +125 -0
- package/skills/telegram-formatting/SKILL.md +0 -147
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.10", COMMIT_SHA = "d88584dd";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -23861,8 +23861,7 @@ function getBuiltinDefaultSkillEntries() {
|
|
|
23861
23861
|
"switchroom-health",
|
|
23862
23862
|
"switchroom-runtime",
|
|
23863
23863
|
"mental-model-curator",
|
|
23864
|
-
"dev-protocol"
|
|
23865
|
-
"telegram-formatting"
|
|
23864
|
+
"dev-protocol"
|
|
23866
23865
|
];
|
|
23867
23866
|
return [
|
|
23868
23867
|
...anthropic.map((key) => ({ key, optOutKey: key, source: "anthropic" })),
|
|
@@ -23939,7 +23938,7 @@ function bindingsForAgent(agentName, mw, microsoftAccounts) {
|
|
|
23939
23938
|
// src/agents/reconcile-default-skills.ts
|
|
23940
23939
|
import { existsSync as existsSync8, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readlinkSync as readlinkSync3, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
23941
23940
|
import { homedir as homedir3 } from "node:os";
|
|
23942
|
-
import { join as join6, resolve as resolve5 } from "node:path";
|
|
23941
|
+
import { dirname as dirname2, isAbsolute, join as join6, relative, resolve as resolve5 } from "node:path";
|
|
23943
23942
|
function warnMissingPoolDir(poolDir) {
|
|
23944
23943
|
if (warnedMissingPool.has(poolDir))
|
|
23945
23944
|
return;
|
|
@@ -23947,6 +23946,14 @@ function warnMissingPoolDir(poolDir) {
|
|
|
23947
23946
|
process.stderr.write(`switchroom: bundled skills pool dir not found at ${poolDir} \u2014 run \`switchroom update\` to install it.
|
|
23948
23947
|
`);
|
|
23949
23948
|
}
|
|
23949
|
+
function warnMissingBuiltinDefault(poolDir, key) {
|
|
23950
|
+
const marker = `${poolDir} ${key}`;
|
|
23951
|
+
if (warnedMissingDefault.has(marker))
|
|
23952
|
+
return;
|
|
23953
|
+
warnedMissingDefault.add(marker);
|
|
23954
|
+
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.
|
|
23955
|
+
`);
|
|
23956
|
+
}
|
|
23950
23957
|
function getBundledSkillsPoolDir() {
|
|
23951
23958
|
return resolve5(homedir3(), ".switchroom/skills/_bundled");
|
|
23952
23959
|
}
|
|
@@ -23961,6 +23968,27 @@ function isOwnedStaleLink(target, poolDir) {
|
|
|
23961
23968
|
return true;
|
|
23962
23969
|
return false;
|
|
23963
23970
|
}
|
|
23971
|
+
function absoluteLinkTarget(linkPath, storedTarget) {
|
|
23972
|
+
return isAbsolute(storedTarget) ? storedTarget : resolve5(dirname2(linkPath), storedTarget);
|
|
23973
|
+
}
|
|
23974
|
+
function linkTargetFor(dest, src) {
|
|
23975
|
+
return relative(dirname2(dest), src);
|
|
23976
|
+
}
|
|
23977
|
+
function isOwnedBundledLink(dest, poolDir) {
|
|
23978
|
+
let stored = null;
|
|
23979
|
+
try {
|
|
23980
|
+
if (!lstatSync2(dest).isSymbolicLink())
|
|
23981
|
+
return false;
|
|
23982
|
+
stored = readlinkSync3(dest);
|
|
23983
|
+
} catch {
|
|
23984
|
+
return false;
|
|
23985
|
+
}
|
|
23986
|
+
if (!stored)
|
|
23987
|
+
return false;
|
|
23988
|
+
const resolved = isAbsolute(stored) ? stored : resolve5(dirname2(dest), stored);
|
|
23989
|
+
const poolPrefix = poolDir.endsWith("/") ? poolDir : poolDir + "/";
|
|
23990
|
+
return resolved === poolDir || resolved.startsWith(poolPrefix);
|
|
23991
|
+
}
|
|
23964
23992
|
function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuiltinDefaultSkillEntries(), poolDir = getBundledSkillsPoolDir()) {
|
|
23965
23993
|
const name = agentDir.split("/").pop() ?? agentDir;
|
|
23966
23994
|
const result = {
|
|
@@ -23969,6 +23997,8 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23969
23997
|
alreadyPresent: [],
|
|
23970
23998
|
optedOut: [],
|
|
23971
23999
|
conflicts: [],
|
|
24000
|
+
missingFromPool: [],
|
|
24001
|
+
pruned: [],
|
|
23972
24002
|
changed: false
|
|
23973
24003
|
};
|
|
23974
24004
|
const claudeDir = join6(agentDir, ".claude");
|
|
@@ -23982,15 +24012,32 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
23982
24012
|
return result;
|
|
23983
24013
|
}
|
|
23984
24014
|
for (const entry of defaults) {
|
|
24015
|
+
const dest = join6(targetDir, entry.key);
|
|
23985
24016
|
if (optOuts[entry.optOutKey] === false) {
|
|
23986
24017
|
result.optedOut.push(entry.key);
|
|
24018
|
+
if (isOwnedBundledLink(dest, poolDir)) {
|
|
24019
|
+
try {
|
|
24020
|
+
rmSync2(dest, { force: true });
|
|
24021
|
+
result.pruned.push(entry.key);
|
|
24022
|
+
result.changed = true;
|
|
24023
|
+
} catch {}
|
|
24024
|
+
}
|
|
23987
24025
|
continue;
|
|
23988
24026
|
}
|
|
23989
24027
|
const src = join6(poolDir, entry.key);
|
|
23990
24028
|
if (!existsSync8(src)) {
|
|
24029
|
+
result.missingFromPool.push(entry.key);
|
|
24030
|
+
warnMissingBuiltinDefault(poolDir, entry.key);
|
|
24031
|
+
if (isOwnedBundledLink(dest, poolDir)) {
|
|
24032
|
+
try {
|
|
24033
|
+
rmSync2(dest, { force: true });
|
|
24034
|
+
result.pruned.push(entry.key);
|
|
24035
|
+
result.changed = true;
|
|
24036
|
+
} catch {}
|
|
24037
|
+
}
|
|
23991
24038
|
continue;
|
|
23992
24039
|
}
|
|
23993
|
-
const
|
|
24040
|
+
const relTarget = linkTargetFor(dest, src);
|
|
23994
24041
|
let existing;
|
|
23995
24042
|
try {
|
|
23996
24043
|
existing = lstatSync2(dest);
|
|
@@ -24003,11 +24050,12 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24003
24050
|
try {
|
|
24004
24051
|
currentTarget = readlinkSync3(dest);
|
|
24005
24052
|
} catch {}
|
|
24006
|
-
if (currentTarget ===
|
|
24053
|
+
if (currentTarget === relTarget) {
|
|
24007
24054
|
result.alreadyPresent.push(entry.key);
|
|
24008
24055
|
continue;
|
|
24009
24056
|
}
|
|
24010
|
-
|
|
24057
|
+
const resolvedTarget = currentTarget ? absoluteLinkTarget(dest, currentTarget) : null;
|
|
24058
|
+
if (resolvedTarget && isOwnedStaleLink(resolvedTarget, poolDir)) {
|
|
24011
24059
|
try {
|
|
24012
24060
|
rmSync2(dest, { force: true });
|
|
24013
24061
|
} catch {}
|
|
@@ -24021,19 +24069,52 @@ function reconcileAgentDefaultSkills(agentDir, optOuts = {}, defaults = getBuilt
|
|
|
24021
24069
|
}
|
|
24022
24070
|
}
|
|
24023
24071
|
try {
|
|
24024
|
-
symlinkSync(
|
|
24072
|
+
symlinkSync(relTarget, dest);
|
|
24025
24073
|
result.added.push(entry.key);
|
|
24026
24074
|
result.changed = true;
|
|
24027
24075
|
} catch (err) {
|
|
24028
24076
|
result.conflicts.push(entry.key);
|
|
24029
24077
|
}
|
|
24030
24078
|
}
|
|
24079
|
+
const defaultKeys = new Set(defaults.map((d) => d.key));
|
|
24080
|
+
const legitimate = new Set(defaultKeys);
|
|
24081
|
+
try {
|
|
24082
|
+
for (const poolName of readdirSync4(poolDir)) {
|
|
24083
|
+
if (!poolName.startsWith("switchroom-"))
|
|
24084
|
+
continue;
|
|
24085
|
+
if (defaultKeys.has(poolName))
|
|
24086
|
+
continue;
|
|
24087
|
+
try {
|
|
24088
|
+
const st = lstatSync2(join6(poolDir, poolName));
|
|
24089
|
+
if (st.isDirectory() && existsSync8(join6(poolDir, poolName, "SKILL.md"))) {
|
|
24090
|
+
legitimate.add(poolName);
|
|
24091
|
+
}
|
|
24092
|
+
} catch {}
|
|
24093
|
+
}
|
|
24094
|
+
} catch {}
|
|
24095
|
+
let dirEntries = [];
|
|
24096
|
+
try {
|
|
24097
|
+
dirEntries = readdirSync4(targetDir);
|
|
24098
|
+
} catch {}
|
|
24099
|
+
for (const linkName of dirEntries) {
|
|
24100
|
+
if (legitimate.has(linkName))
|
|
24101
|
+
continue;
|
|
24102
|
+
const dest = join6(targetDir, linkName);
|
|
24103
|
+
if (!isOwnedBundledLink(dest, poolDir))
|
|
24104
|
+
continue;
|
|
24105
|
+
try {
|
|
24106
|
+
rmSync2(dest, { force: true });
|
|
24107
|
+
result.pruned.push(linkName);
|
|
24108
|
+
result.changed = true;
|
|
24109
|
+
} catch {}
|
|
24110
|
+
}
|
|
24031
24111
|
return result;
|
|
24032
24112
|
}
|
|
24033
|
-
var warnedMissingPool;
|
|
24113
|
+
var warnedMissingPool, warnedMissingDefault;
|
|
24034
24114
|
var init_reconcile_default_skills = __esm(() => {
|
|
24035
24115
|
init_scaffold_integration();
|
|
24036
24116
|
warnedMissingPool = new Set;
|
|
24117
|
+
warnedMissingDefault = new Set;
|
|
24037
24118
|
});
|
|
24038
24119
|
|
|
24039
24120
|
// src/agents/sub-agent-telegram-prompt.ts
|
|
@@ -24089,7 +24170,7 @@ import {
|
|
|
24089
24170
|
copyFileSync as copyFileSync2,
|
|
24090
24171
|
unlinkSync
|
|
24091
24172
|
} from "node:fs";
|
|
24092
|
-
import { basename as basename2, dirname as
|
|
24173
|
+
import { basename as basename2, dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
24093
24174
|
function defaultStatePath() {
|
|
24094
24175
|
return resolveStatePath("topics.json");
|
|
24095
24176
|
}
|
|
@@ -24116,7 +24197,7 @@ function loadTopicState(statePath) {
|
|
|
24116
24197
|
}
|
|
24117
24198
|
function saveTopicState(state, statePath) {
|
|
24118
24199
|
const path = statePath ?? defaultStatePath();
|
|
24119
|
-
const dir =
|
|
24200
|
+
const dir = dirname3(path);
|
|
24120
24201
|
if (!existsSync9(dir)) {
|
|
24121
24202
|
mkdirSync4(dir, { recursive: true });
|
|
24122
24203
|
}
|
|
@@ -24388,7 +24469,7 @@ import {
|
|
|
24388
24469
|
lstatSync as lstatSync3,
|
|
24389
24470
|
realpathSync as realpathSync2
|
|
24390
24471
|
} from "node:fs";
|
|
24391
|
-
import { dirname as
|
|
24472
|
+
import { dirname as dirname4, basename as basename3, resolve as resolve7 } from "node:path";
|
|
24392
24473
|
function atomicWriteFileSync2(path, data, mode) {
|
|
24393
24474
|
let effectivePath = path;
|
|
24394
24475
|
try {
|
|
@@ -24396,7 +24477,7 @@ function atomicWriteFileSync2(path, data, mode) {
|
|
|
24396
24477
|
effectivePath = realpathSync2(path);
|
|
24397
24478
|
}
|
|
24398
24479
|
} catch {}
|
|
24399
|
-
const dir =
|
|
24480
|
+
const dir = dirname4(resolve7(effectivePath));
|
|
24400
24481
|
const tmp = resolve7(dir, `.${basename3(effectivePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
24401
24482
|
try {
|
|
24402
24483
|
const fd = openSync4(tmp, "wx", mode);
|
|
@@ -24533,7 +24614,7 @@ function createVault(passphrase, vaultPath) {
|
|
|
24533
24614
|
if (existsSync11(vaultPath)) {
|
|
24534
24615
|
throw new VaultError(`Vault file already exists: ${vaultPath}`);
|
|
24535
24616
|
}
|
|
24536
|
-
const dir =
|
|
24617
|
+
const dir = dirname4(vaultPath);
|
|
24537
24618
|
if (!existsSync11(dir)) {
|
|
24538
24619
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
24539
24620
|
}
|
|
@@ -26141,7 +26222,7 @@ import {
|
|
|
26141
26222
|
} from "node:fs";
|
|
26142
26223
|
import { homedir as homedir5 } from "node:os";
|
|
26143
26224
|
import { execSync, execFileSync as execFileSync6 } from "node:child_process";
|
|
26144
|
-
import { join as join11, resolve as resolve11 } from "node:path";
|
|
26225
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join11, relative as relative2, resolve as resolve11 } from "node:path";
|
|
26145
26226
|
import { createHash as createHash3 } from "node:crypto";
|
|
26146
26227
|
function prependReplyDiscipline(rendered, context) {
|
|
26147
26228
|
const replyDiscipline = renderReplyDisciplineFragment(context);
|
|
@@ -26527,7 +26608,8 @@ function migrateLegacySkillsDir(agentDir, skillsPool) {
|
|
|
26527
26608
|
} catch {
|
|
26528
26609
|
continue;
|
|
26529
26610
|
}
|
|
26530
|
-
|
|
26611
|
+
const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(entryPath), target) : null;
|
|
26612
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26531
26613
|
try {
|
|
26532
26614
|
rmSync4(entryPath, { force: true });
|
|
26533
26615
|
} catch {}
|
|
@@ -26550,6 +26632,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26550
26632
|
console.warn(` WARNING: skill "${name}" not found in pool (${skillsPool}) \u2014 skipping`);
|
|
26551
26633
|
continue;
|
|
26552
26634
|
}
|
|
26635
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26553
26636
|
let linkStat;
|
|
26554
26637
|
try {
|
|
26555
26638
|
linkStat = lstatSync4(dest);
|
|
@@ -26562,7 +26645,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26562
26645
|
try {
|
|
26563
26646
|
target = readlinkSync4(dest);
|
|
26564
26647
|
} catch {}
|
|
26565
|
-
if (target
|
|
26648
|
+
if (target === relTarget) {
|
|
26649
|
+
continue;
|
|
26650
|
+
}
|
|
26651
|
+
const resolved = target ? isAbsolute2(target) ? target : resolve11(dirname5(dest), target) : null;
|
|
26652
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26566
26653
|
try {
|
|
26567
26654
|
rmSync4(dest, { force: true });
|
|
26568
26655
|
} catch {}
|
|
@@ -26574,7 +26661,7 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26574
26661
|
}
|
|
26575
26662
|
}
|
|
26576
26663
|
try {
|
|
26577
|
-
symlinkSync2(
|
|
26664
|
+
symlinkSync2(relTarget, dest);
|
|
26578
26665
|
} catch (err) {
|
|
26579
26666
|
console.warn(` WARNING: failed to symlink skill "${name}": ${err.message}`);
|
|
26580
26667
|
}
|
|
@@ -26590,10 +26677,11 @@ function syncGlobalSkills(agentDir, declared, skillsDirOverride) {
|
|
|
26590
26677
|
} catch {
|
|
26591
26678
|
continue;
|
|
26592
26679
|
}
|
|
26593
|
-
|
|
26680
|
+
const resolved = linkTarget ? isAbsolute2(linkTarget) ? linkTarget : resolve11(dirname5(entryPath), linkTarget) : null;
|
|
26681
|
+
if (resolved && resolved.includes("/.switchroom/skills/_bundled/")) {
|
|
26594
26682
|
continue;
|
|
26595
26683
|
}
|
|
26596
|
-
if (
|
|
26684
|
+
if (resolved && resolved.startsWith(skillsPool)) {
|
|
26597
26685
|
rmSync4(entryPath, { force: true });
|
|
26598
26686
|
}
|
|
26599
26687
|
}
|
|
@@ -26646,7 +26734,10 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26646
26734
|
try {
|
|
26647
26735
|
currentTarget = readlinkSync4(dest);
|
|
26648
26736
|
} catch {}
|
|
26649
|
-
if (currentTarget
|
|
26737
|
+
if (!currentTarget)
|
|
26738
|
+
continue;
|
|
26739
|
+
const resolvedTarget = isAbsolute2(currentTarget) ? currentTarget : resolve11(dirname5(dest), currentTarget);
|
|
26740
|
+
if (resolvedTarget !== join11(builtinSkillsDir, name))
|
|
26650
26741
|
continue;
|
|
26651
26742
|
try {
|
|
26652
26743
|
rmSync4(dest, { force: true });
|
|
@@ -26657,6 +26748,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26657
26748
|
for (const name of switchroomSkillNames) {
|
|
26658
26749
|
const src = join11(builtinSkillsDir, name);
|
|
26659
26750
|
const dest = join11(targetDir, name);
|
|
26751
|
+
const relTarget = relative2(dirname5(dest), src);
|
|
26660
26752
|
let existing;
|
|
26661
26753
|
try {
|
|
26662
26754
|
existing = lstatSync4(dest);
|
|
@@ -26669,7 +26761,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26669
26761
|
try {
|
|
26670
26762
|
currentTarget = readlinkSync4(dest);
|
|
26671
26763
|
} catch {}
|
|
26672
|
-
if (currentTarget ===
|
|
26764
|
+
if (currentTarget === relTarget)
|
|
26673
26765
|
continue;
|
|
26674
26766
|
try {
|
|
26675
26767
|
rmSync4(dest, { force: true });
|
|
@@ -26679,7 +26771,7 @@ function installSwitchroomSkills(agentDir, opts = {}) {
|
|
|
26679
26771
|
}
|
|
26680
26772
|
}
|
|
26681
26773
|
try {
|
|
26682
|
-
symlinkSync2(
|
|
26774
|
+
symlinkSync2(relTarget, dest);
|
|
26683
26775
|
} catch (err) {
|
|
26684
26776
|
console.warn(` WARNING: failed to symlink switchroom skill "${name}": ${err.message}`);
|
|
26685
26777
|
}
|
|
@@ -27381,6 +27473,24 @@ function resolveWebkiteMcpEntry(_agentName, agentConfig, _switchroomConfig) {
|
|
|
27381
27473
|
}
|
|
27382
27474
|
};
|
|
27383
27475
|
}
|
|
27476
|
+
function computeDesiredPermissionAllow(agentConfig, hindsightEnabled) {
|
|
27477
|
+
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
27478
|
+
const rawAllow = tools.allow ?? [];
|
|
27479
|
+
const hasAllWildcard = rawAllow.includes("all");
|
|
27480
|
+
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
27481
|
+
const dangerousMode = agentConfig.dangerous_mode === true;
|
|
27482
|
+
const hadExplicitAllow = rawAllow.length > 0;
|
|
27483
|
+
const readOnlyDefaults = !dangerousMode && !hadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
27484
|
+
return dedupe2([
|
|
27485
|
+
...baseAllow,
|
|
27486
|
+
...readOnlyDefaults,
|
|
27487
|
+
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
27488
|
+
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
27489
|
+
...AGENT_CONFIG_MCP_TOOLS,
|
|
27490
|
+
...HOSTD_MCP_TOOLS,
|
|
27491
|
+
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
27492
|
+
]);
|
|
27493
|
+
}
|
|
27384
27494
|
function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchroomConfig, userIdOverride, switchroomConfigPath) {
|
|
27385
27495
|
const agentConfig = resolveAgentConfig(switchroomConfig?.defaults, switchroomConfig?.profiles, agentConfigRaw);
|
|
27386
27496
|
const agentDir = resolve11(agentsDir, name);
|
|
@@ -27403,20 +27513,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
27403
27513
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
27404
27514
|
const rawAllow = tools.allow ?? [];
|
|
27405
27515
|
const hasAllWildcard = rawAllow.includes("all");
|
|
27406
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
27407
|
-
const dangerousMode = agentConfig.dangerous_mode === true;
|
|
27408
|
-
const hadExplicitAllow = rawAllow.length > 0;
|
|
27409
|
-
const readOnlyDefaults = !dangerousMode && !hadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
27410
27516
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
27411
|
-
const permissionAllow =
|
|
27412
|
-
...baseAllow,
|
|
27413
|
-
...readOnlyDefaults,
|
|
27414
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
27415
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
27416
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
27417
|
-
...HOSTD_MCP_TOOLS,
|
|
27418
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
27419
|
-
]);
|
|
27517
|
+
const permissionAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
27420
27518
|
const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
|
|
27421
27519
|
const hindsightBankId = agentConfig.memory?.collection ?? name;
|
|
27422
27520
|
const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
|
|
@@ -28332,23 +28430,11 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
28332
28430
|
const tools = agentConfig.tools ?? { allow: [], deny: [] };
|
|
28333
28431
|
const rawAllow = tools.allow ?? [];
|
|
28334
28432
|
const hasAllWildcard = rawAllow.includes("all");
|
|
28335
|
-
const baseAllow = hasAllWildcard ? [...ALL_BUILTIN_TOOLS, ...rawAllow.filter((t) => t !== "all")] : rawAllow.filter((t) => t !== "all");
|
|
28336
|
-
const reconcileDangerousMode = agentConfig.dangerous_mode === true;
|
|
28337
|
-
const reconcileHadExplicitAllow = rawAllow.length > 0;
|
|
28338
|
-
const reconcileReadOnlyDefaults = !reconcileDangerousMode && !reconcileHadExplicitAllow ? DEFAULT_READ_ONLY_PREAPPROVED_TOOLS : [];
|
|
28339
28433
|
const hindsightEnabled = isHindsightEnabled(switchroomConfig);
|
|
28434
|
+
const desiredAllow = computeDesiredPermissionAllow(agentConfig, hindsightEnabled);
|
|
28340
28435
|
if (Array.isArray(tools.allow)) {
|
|
28341
28436
|
tools.allow = tools.allow.filter((p) => !LEGACY_SWITCHROOM_MCP_TOKENS.includes(p) && !LEGACY_HOSTD_BLANKET_TOKENS.includes(p));
|
|
28342
28437
|
}
|
|
28343
|
-
const desiredAllow = dedupe2([
|
|
28344
|
-
...baseAllow,
|
|
28345
|
-
...reconcileReadOnlyDefaults,
|
|
28346
|
-
...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
|
|
28347
|
-
...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
|
|
28348
|
-
...AGENT_CONFIG_MCP_TOOLS,
|
|
28349
|
-
...HOSTD_MCP_TOOLS,
|
|
28350
|
-
...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
|
|
28351
|
-
]);
|
|
28352
28438
|
const desiredDeny = dedupe2([
|
|
28353
28439
|
...tools.deny ?? [],
|
|
28354
28440
|
...webkiteDenyForAgent(agentConfig),
|
|
@@ -29420,31 +29506,34 @@ You're writing for a phone screen in Telegram. Every reply renders as rich Markd
|
|
|
29420
29506
|
|
|
29421
29507
|
- **Short answers (a line or two): plain prose, no formatting.** "on it, pulling the
|
|
29422
29508
|
logs now" is already perfect. No bold, no bullets, no headings.
|
|
29423
|
-
- **Default: light structure.** Bold ONLY the one key fact or answer, never more.
|
|
29424
|
-
|
|
29425
|
-
|
|
29426
|
-
|
|
29427
|
-
-
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
29431
|
-
|
|
29432
|
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29437
|
-
|
|
29438
|
-
|
|
29439
|
-
|
|
29440
|
-
|
|
29441
|
-
|
|
29442
|
-
|
|
29443
|
-
|
|
29444
|
-
|
|
29445
|
-
|
|
29446
|
-
|
|
29447
|
-
|
|
29509
|
+
- **Default: light structure.** Bold ONLY the one key fact or answer, never more. A
|
|
29510
|
+
list only for 3+ genuinely parallel items the reader will scan or compare; two items
|
|
29511
|
+
or a flowing thought stay prose. \`code spans\` for identifiers: filenames, commands,
|
|
29512
|
+
config keys, error codes (tap-to-copy). Wrap dynamic identifiers in backticks:
|
|
29513
|
+
code-span content is literal, so it never needs escaping. Links as \`[label](url)\`,
|
|
29514
|
+
never bare pasted URLs mid-prose.
|
|
29515
|
+
- **Long answers may add the rich constructs, but only when they cut the reader's
|
|
29516
|
+
effort:** a GFM pipe table for real 2-D data (rows x columns); headings only in a
|
|
29517
|
+
multi-section answer; \`>\` for quoted text; fenced code blocks ALWAYS with a language
|
|
29518
|
+
hint (\`\`\`diff, \`\`\`json, \`\`\`bash \u2014 bare fence only for non-code fixed-width output);
|
|
29519
|
+
and the flagship \u2014 the **expandable blockquote** \`**> first line\` + \`> continuation\`
|
|
29520
|
+
for a long quote, stack trace, or detailed aside the reader can collapse. Use it
|
|
29521
|
+
whenever a bulky supporting block would otherwise dominate the message.
|
|
29522
|
+
|
|
29523
|
+
Renders wrong on this path \u2014 never emit: underline (\`__x__\` renders as bold),
|
|
29524
|
+
\`^sup^\`/\`~sub~\`, \`$math$\`, \`<details>\`, footnotes \`[^1]\`. Write "squared", not \`x^2^\`.
|
|
29525
|
+
|
|
29526
|
+
The framework normalizes mechanics in code on every outbound message: block spacing,
|
|
29527
|
+
em/en dashes, and \`\u2022\` bullet markers are
|
|
29528
|
+
rewritten deterministically; unsupported tokens (\`^highlight^\`, \`$math$\`, \`<details>\`,
|
|
29529
|
+
footnotes) are repaired; long messages are chunked safely at 32768 chars (fences and
|
|
29530
|
+
table rows never bisected); over-bolded messages get their bold stripped. Don't
|
|
29531
|
+
hand-tune spacing or fight it \u2014 write the content, the gateway makes typography
|
|
29532
|
+
consistent. Long before the cap, ask whether a wall of text is the right answer at all.
|
|
29533
|
+
|
|
29534
|
+
Structure exists for the reader, not the writer: a two-item bullet list is worse than
|
|
29535
|
+
a sentence, a heading on a three-line reply is noise. When in doubt, shorter and
|
|
29536
|
+
plainer wins.
|
|
29448
29537
|
|
|
29449
29538
|
Every turn that answers a user message ends with a user-visible \`reply\`
|
|
29450
29539
|
\u2014 Telegram is all the user sees; your terminal output
|
|
@@ -29606,7 +29695,7 @@ var init_scaffold = __esm(() => {
|
|
|
29606
29695
|
|
|
29607
29696
|
// src/setup/host-capabilities.ts
|
|
29608
29697
|
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
|
|
29609
|
-
import { dirname as
|
|
29698
|
+
import { dirname as dirname6 } from "node:path";
|
|
29610
29699
|
function hostCapabilitiesPath() {
|
|
29611
29700
|
return resolveStatePath("host-capabilities.json");
|
|
29612
29701
|
}
|
|
@@ -29621,7 +29710,7 @@ function saveVoiceCapability(caps, now = () => new Date) {
|
|
|
29621
29710
|
}
|
|
29622
29711
|
};
|
|
29623
29712
|
const path = hostCapabilitiesPath();
|
|
29624
|
-
mkdirSync11(
|
|
29713
|
+
mkdirSync11(dirname6(path), { recursive: true });
|
|
29625
29714
|
writeFileSync5(path, JSON.stringify(doc, null, 2) + `
|
|
29626
29715
|
`, {
|
|
29627
29716
|
encoding: "utf-8",
|
|
@@ -29728,11 +29817,11 @@ var init_grants_db_path = __esm(() => {
|
|
|
29728
29817
|
|
|
29729
29818
|
// src/agents/compose.ts
|
|
29730
29819
|
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync15, lstatSync as lstatSync5, readlinkSync as readlinkSync5, chmodSync as chmodSync4 } from "node:fs";
|
|
29731
|
-
import { join as join13, isAbsolute, dirname as
|
|
29820
|
+
import { join as join13, isAbsolute as isAbsolute3, dirname as dirname8, resolve as resolve13 } from "node:path";
|
|
29732
29821
|
function assertPlausibleHostHome(homePrefix) {
|
|
29733
29822
|
if (homePrefix === "${HOME}")
|
|
29734
29823
|
return;
|
|
29735
|
-
const bad = !
|
|
29824
|
+
const bad = !isAbsolute3(homePrefix) || CONTAINER_ROOT_PREFIXES.some((p) => homePrefix === p || homePrefix.startsWith(p + "/"));
|
|
29736
29825
|
if (!bad)
|
|
29737
29826
|
return;
|
|
29738
29827
|
throw new Error(`compose: refusing to generate \u2014 the host-home prefix resolved to "${homePrefix}", ` + `which is not a real host path (it looks like an in-container root). Emitting it as a ` + `bind-mount source would make docker auto-create empty dirs on the host and crash the ` + `fleet (start.sh missing \u2192 exec 127; broker EISDIR / SQLite "unable to open").
|
|
@@ -29976,8 +30065,8 @@ function conditionalMountPresent(probePath, hostHome, probeHome) {
|
|
|
29976
30065
|
if (!lstatSync5(probePath).isSymbolicLink())
|
|
29977
30066
|
return false;
|
|
29978
30067
|
let target = readlinkSync5(probePath);
|
|
29979
|
-
if (!
|
|
29980
|
-
target = resolve13(
|
|
30068
|
+
if (!isAbsolute3(target))
|
|
30069
|
+
target = resolve13(dirname8(probePath), target);
|
|
29981
30070
|
if (hostHome && probeHome && hostHome !== probeHome) {
|
|
29982
30071
|
if (target.startsWith(hostHome + "/")) {
|
|
29983
30072
|
return true;
|
|
@@ -30026,7 +30115,7 @@ function generateCompose(opts) {
|
|
|
30026
30115
|
const lines = [];
|
|
30027
30116
|
lines.push("# generated by switchroom \u2014 do not edit by hand.");
|
|
30028
30117
|
lines.push("# Manual edits will be overwritten on the next `switchroom agent add`");
|
|
30029
|
-
lines.push("#
|
|
30118
|
+
lines.push("# or `switchroom apply`. To customise an agent, edit");
|
|
30030
30119
|
lines.push("# switchroom.yaml and re-run the regenerating command.");
|
|
30031
30120
|
lines.push("");
|
|
30032
30121
|
lines.push(`# image tag: ${imageTag}`);
|
|
@@ -30698,7 +30787,7 @@ var init_operator_uid = () => {};
|
|
|
30698
30787
|
import { chownSync as chownSync2 } from "node:fs";
|
|
30699
30788
|
import { mkdir, readFile, writeFile, rename, copyFile } from "node:fs/promises";
|
|
30700
30789
|
import { homedir as homedir7 } from "node:os";
|
|
30701
|
-
import { basename as basename4, dirname as
|
|
30790
|
+
import { basename as basename4, dirname as dirname9, join as join15 } from "node:path";
|
|
30702
30791
|
function agentHadLiteLLMRouting(composeContent, agentName) {
|
|
30703
30792
|
const lines = composeContent.split(`
|
|
30704
30793
|
`);
|
|
@@ -30839,7 +30928,7 @@ async function computeComposeContent(opts) {
|
|
|
30839
30928
|
async function writeComposeFile(opts) {
|
|
30840
30929
|
const { content, imageTag, previous, previousImageTag } = await computeComposeContent(opts);
|
|
30841
30930
|
const operatorUid = resolveOperatorUid();
|
|
30842
|
-
await mkdir(
|
|
30931
|
+
await mkdir(dirname9(opts.composePath), { recursive: true });
|
|
30843
30932
|
if (previous !== null) {
|
|
30844
30933
|
try {
|
|
30845
30934
|
await copyFile(opts.composePath, opts.composePath + ".bak");
|
|
@@ -30908,9 +30997,9 @@ var init_tmux = __esm(() => {
|
|
|
30908
30997
|
|
|
30909
30998
|
// src/agents/compose-env.ts
|
|
30910
30999
|
import { existsSync as existsSync21 } from "node:fs";
|
|
30911
|
-
import { dirname as
|
|
31000
|
+
import { dirname as dirname10 } from "node:path";
|
|
30912
31001
|
function composeEnvPath(composePath) {
|
|
30913
|
-
return
|
|
31002
|
+
return dirname10(composePath) + "/.env";
|
|
30914
31003
|
}
|
|
30915
31004
|
function composeEnvFileArgs(composePath) {
|
|
30916
31005
|
const envPath = composeEnvPath(composePath);
|
|
@@ -35578,7 +35667,7 @@ import {
|
|
|
35578
35667
|
unlinkSync as unlinkSync7
|
|
35579
35668
|
} from "node:fs";
|
|
35580
35669
|
import { createHash as createHash6 } from "node:crypto";
|
|
35581
|
-
import { basename as basename5, dirname as
|
|
35670
|
+
import { basename as basename5, dirname as dirname11, join as join31 } from "node:path";
|
|
35582
35671
|
function vaultLayoutPaths(home2) {
|
|
35583
35672
|
const switchroomRoot = join31(home2, ".switchroom");
|
|
35584
35673
|
return {
|
|
@@ -35731,7 +35820,7 @@ function sha256File(path2) {
|
|
|
35731
35820
|
return createHash6("sha256").update(data).digest("hex");
|
|
35732
35821
|
}
|
|
35733
35822
|
function atomicReplaceWithSymlink(target, linkTarget) {
|
|
35734
|
-
const tmp = join31(
|
|
35823
|
+
const tmp = join31(dirname11(target), `.${basename5(target)}.symlink-tmp`);
|
|
35735
35824
|
if (existsSync39(tmp)) {
|
|
35736
35825
|
try {
|
|
35737
35826
|
unlinkSync7(tmp);
|
|
@@ -35785,7 +35874,7 @@ import {
|
|
|
35785
35874
|
unlinkSync as unlinkSync8,
|
|
35786
35875
|
writeSync as writeSync5
|
|
35787
35876
|
} from "node:fs";
|
|
35788
|
-
import { basename as basename6, dirname as
|
|
35877
|
+
import { basename as basename6, dirname as dirname12, resolve as resolve27 } from "node:path";
|
|
35789
35878
|
function readMachineId() {
|
|
35790
35879
|
const vitestVal = process.env.VITEST;
|
|
35791
35880
|
const isTestEnv = vitestVal !== undefined && vitestVal.length > 0;
|
|
@@ -35850,7 +35939,7 @@ function decryptAutoUnlock(blob, machineId) {
|
|
|
35850
35939
|
}
|
|
35851
35940
|
function writeAutoUnlockFile(passphrase, filePath) {
|
|
35852
35941
|
const blob = encryptAutoUnlock(passphrase);
|
|
35853
|
-
const dir =
|
|
35942
|
+
const dir = dirname12(filePath);
|
|
35854
35943
|
mkdirSync23(dir, { recursive: true, mode: 448 });
|
|
35855
35944
|
const tmp = resolve27(dir, `.${basename6(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
35856
35945
|
try {
|
|
@@ -36957,7 +37046,7 @@ function formatForCli(entries, opts = {}) {
|
|
|
36957
37046
|
var init_audit_reader = () => {};
|
|
36958
37047
|
|
|
36959
37048
|
// node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
36960
|
-
import { dirname as
|
|
37049
|
+
import { dirname as dirname16, posix, sep as sep2 } from "path";
|
|
36961
37050
|
function createModulerModifier() {
|
|
36962
37051
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
36963
37052
|
return async (frames) => {
|
|
@@ -36966,7 +37055,7 @@ function createModulerModifier() {
|
|
|
36966
37055
|
return frames;
|
|
36967
37056
|
};
|
|
36968
37057
|
}
|
|
36969
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
37058
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname16(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
36970
37059
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
36971
37060
|
return (filename) => {
|
|
36972
37061
|
if (!filename)
|
|
@@ -41582,7 +41671,7 @@ import {
|
|
|
41582
41671
|
readFileSync as readFileSync45,
|
|
41583
41672
|
writeFileSync as writeFileSync15
|
|
41584
41673
|
} from "node:fs";
|
|
41585
|
-
import { dirname as
|
|
41674
|
+
import { dirname as dirname17 } from "node:path";
|
|
41586
41675
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
41587
41676
|
function telemetryDisabled() {
|
|
41588
41677
|
const v = process.env.SWITCHROOM_TELEMETRY_DISABLED;
|
|
@@ -41604,7 +41693,7 @@ function getDistinctId() {
|
|
|
41604
41693
|
const id = randomUUID3();
|
|
41605
41694
|
cachedDistinctId = id;
|
|
41606
41695
|
try {
|
|
41607
|
-
mkdirSync27(
|
|
41696
|
+
mkdirSync27(dirname17(path5), { recursive: true });
|
|
41608
41697
|
writeFileSync15(path5, id, "utf-8");
|
|
41609
41698
|
} catch {}
|
|
41610
41699
|
return id;
|
|
@@ -41851,7 +41940,7 @@ import {
|
|
|
41851
41940
|
readFileSync as readFileSync55,
|
|
41852
41941
|
readdirSync as readdirSync21
|
|
41853
41942
|
} from "node:fs";
|
|
41854
|
-
import { dirname as
|
|
41943
|
+
import { dirname as dirname21, join as join53 } from "node:path";
|
|
41855
41944
|
import { execSync as execSync2 } from "node:child_process";
|
|
41856
41945
|
function locateManifestPath() {
|
|
41857
41946
|
let dir = import.meta.dirname;
|
|
@@ -41859,7 +41948,7 @@ function locateManifestPath() {
|
|
|
41859
41948
|
const candidate = join53(dir, "dependencies.json");
|
|
41860
41949
|
if (existsSync60(candidate))
|
|
41861
41950
|
return candidate;
|
|
41862
|
-
dir =
|
|
41951
|
+
dir = dirname21(dir);
|
|
41863
41952
|
}
|
|
41864
41953
|
return null;
|
|
41865
41954
|
}
|
|
@@ -42713,7 +42802,7 @@ function checkAgentSocketMounts(composeYaml) {
|
|
|
42713
42802
|
name: "agent socket-volume isolation",
|
|
42714
42803
|
status: "fail",
|
|
42715
42804
|
detail: `Cross-mounted socket volumes: ${violations.join("; ")}`,
|
|
42716
|
-
fix: "Re-run `switchroom
|
|
42805
|
+
fix: "Re-run `switchroom apply` to regenerate the compose from cascade. Hand-edits violating per-agent socket isolation are the load-bearing security invariant."
|
|
42717
42806
|
};
|
|
42718
42807
|
}
|
|
42719
42808
|
function checkAgentCaps(config) {
|
|
@@ -42911,7 +43000,7 @@ function runDockerChecks(args) {
|
|
|
42911
43000
|
name: "compose file present",
|
|
42912
43001
|
status: "warn",
|
|
42913
43002
|
detail: "Docker mode active but no docker-compose.yml found at ~/.switchroom/compose/docker-compose.yml",
|
|
42914
|
-
fix: "Run `switchroom
|
|
43003
|
+
fix: "Run `switchroom apply` to generate it."
|
|
42915
43004
|
});
|
|
42916
43005
|
}
|
|
42917
43006
|
out.push(...checkContainerRuntimeHealth(args.config, args.dockerPsDeps));
|
|
@@ -45933,7 +46022,7 @@ import {
|
|
|
45933
46022
|
readdirSync as readdirSync23,
|
|
45934
46023
|
statSync as statSync38
|
|
45935
46024
|
} from "node:fs";
|
|
45936
|
-
import { dirname as
|
|
46025
|
+
import { dirname as dirname22, join as join68, resolve as resolve39 } from "node:path";
|
|
45937
46026
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
45938
46027
|
function findInNvm(bin) {
|
|
45939
46028
|
const nvmRoot = join68(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -47530,7 +47619,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
47530
47619
|
detail: "skipped (MFF_API_URL not set)"
|
|
47531
47620
|
};
|
|
47532
47621
|
}
|
|
47533
|
-
const credDir =
|
|
47622
|
+
const credDir = dirname22(envPath);
|
|
47534
47623
|
const authScript = join68(credDir, "claude-auth.py");
|
|
47535
47624
|
if (!existsSync66(authScript)) {
|
|
47536
47625
|
return {
|
|
@@ -48590,8 +48679,8 @@ var init_fleet_defaults = __esm(() => {
|
|
|
48590
48679
|
});
|
|
48591
48680
|
|
|
48592
48681
|
// src/agents/connection-health.ts
|
|
48593
|
-
import { mkdirSync as
|
|
48594
|
-
import { join as
|
|
48682
|
+
import { mkdirSync as mkdirSync47, writeFileSync as writeFileSync29 } from "node:fs";
|
|
48683
|
+
import { join as join85 } from "node:path";
|
|
48595
48684
|
async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
48596
48685
|
const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
|
|
48597
48686
|
if (reqs.length === 0)
|
|
@@ -48648,10 +48737,10 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
|
|
|
48648
48737
|
return issues;
|
|
48649
48738
|
}
|
|
48650
48739
|
function writeConnectionHealthFile(agentDir, health, deps) {
|
|
48651
|
-
const dir =
|
|
48652
|
-
const path8 =
|
|
48653
|
-
(deps?.mkdir ?? ((p, o) =>
|
|
48654
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
48740
|
+
const dir = join85(agentDir, ".claude");
|
|
48741
|
+
const path8 = join85(dir, CONNECTION_HEALTH_FILENAME);
|
|
48742
|
+
(deps?.mkdir ?? ((p, o) => mkdirSync47(p, o)))(dir, { recursive: true });
|
|
48743
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync29(p, d)))(path8, JSON.stringify(health, null, 2) + `
|
|
48655
48744
|
`);
|
|
48656
48745
|
}
|
|
48657
48746
|
async function refreshAgentConnectionHealth(config, agentName, agentDir, deps) {
|
|
@@ -48672,10 +48761,10 @@ var CONNECTION_HEALTH_FILENAME = "connection-health.json";
|
|
|
48672
48761
|
var init_connection_health = () => {};
|
|
48673
48762
|
|
|
48674
48763
|
// src/cli/update-prompt-hook.ts
|
|
48675
|
-
import { existsSync as
|
|
48676
|
-
import { join as
|
|
48764
|
+
import { existsSync as existsSync84, readFileSync as readFileSync73, writeFileSync as writeFileSync30, chmodSync as chmodSync12, mkdirSync as mkdirSync48 } from "node:fs";
|
|
48765
|
+
import { join as join86 } from "node:path";
|
|
48677
48766
|
function containerHookCommand() {
|
|
48678
|
-
return
|
|
48767
|
+
return join86(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
|
|
48679
48768
|
}
|
|
48680
48769
|
function updatePromptHookScript() {
|
|
48681
48770
|
return `#!/bin/bash
|
|
@@ -48741,14 +48830,14 @@ exit 0
|
|
|
48741
48830
|
`;
|
|
48742
48831
|
}
|
|
48743
48832
|
function installUpdatePromptHook(agentDir) {
|
|
48744
|
-
const hooksDir =
|
|
48745
|
-
|
|
48746
|
-
const scriptPath =
|
|
48833
|
+
const hooksDir = join86(agentDir, ".claude", "hooks");
|
|
48834
|
+
mkdirSync48(hooksDir, { recursive: true });
|
|
48835
|
+
const scriptPath = join86(hooksDir, HOOK_FILENAME);
|
|
48747
48836
|
const desired = updatePromptHookScript();
|
|
48748
48837
|
let installed = false;
|
|
48749
|
-
const existing =
|
|
48838
|
+
const existing = existsSync84(scriptPath) ? readFileSync73(scriptPath, "utf-8") : "";
|
|
48750
48839
|
if (existing !== desired) {
|
|
48751
|
-
|
|
48840
|
+
writeFileSync30(scriptPath, desired, { mode: 493 });
|
|
48752
48841
|
chmodSync12(scriptPath, 493);
|
|
48753
48842
|
installed = true;
|
|
48754
48843
|
} else {
|
|
@@ -48756,11 +48845,11 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48756
48845
|
chmodSync12(scriptPath, 493);
|
|
48757
48846
|
} catch {}
|
|
48758
48847
|
}
|
|
48759
|
-
const settingsPath =
|
|
48760
|
-
if (!
|
|
48848
|
+
const settingsPath = join86(agentDir, ".claude", "settings.json");
|
|
48849
|
+
if (!existsSync84(settingsPath)) {
|
|
48761
48850
|
return { scriptPath, settingsPath, installed };
|
|
48762
48851
|
}
|
|
48763
|
-
const raw =
|
|
48852
|
+
const raw = readFileSync73(settingsPath, "utf-8");
|
|
48764
48853
|
let parsed;
|
|
48765
48854
|
try {
|
|
48766
48855
|
parsed = JSON.parse(raw);
|
|
@@ -48796,7 +48885,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48796
48885
|
if (mutated) {
|
|
48797
48886
|
hooks.UserPromptSubmit = list2;
|
|
48798
48887
|
parsed.hooks = hooks;
|
|
48799
|
-
|
|
48888
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48800
48889
|
`, { mode: 384 });
|
|
48801
48890
|
installed = true;
|
|
48802
48891
|
} else if (!alreadyCorrect) {
|
|
@@ -48805,7 +48894,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
48805
48894
|
});
|
|
48806
48895
|
hooks.UserPromptSubmit = list2;
|
|
48807
48896
|
parsed.hooks = hooks;
|
|
48808
|
-
|
|
48897
|
+
writeFileSync30(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
48809
48898
|
`, { mode: 384 });
|
|
48810
48899
|
installed = true;
|
|
48811
48900
|
}
|
|
@@ -49189,8 +49278,8 @@ __export(exports_voice_sidecar_token, {
|
|
|
49189
49278
|
VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
|
|
49190
49279
|
});
|
|
49191
49280
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
49192
|
-
import { chmodSync as chmodSync13, chownSync as chownSync7, existsSync as
|
|
49193
|
-
import { dirname as
|
|
49281
|
+
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";
|
|
49282
|
+
import { dirname as dirname31 } from "node:path";
|
|
49194
49283
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
49195
49284
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
49196
49285
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
@@ -49224,8 +49313,8 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49224
49313
|
const envPath = composeEnvPath(composePath);
|
|
49225
49314
|
if (engine !== "local") {
|
|
49226
49315
|
try {
|
|
49227
|
-
if (
|
|
49228
|
-
const body =
|
|
49316
|
+
if (existsSync86(envPath)) {
|
|
49317
|
+
const body = readFileSync74(envPath, "utf-8");
|
|
49229
49318
|
if (body.includes(`${VOICE_SIDECAR_TOKEN_ENV}=`))
|
|
49230
49319
|
rmSync17(envPath);
|
|
49231
49320
|
}
|
|
@@ -49244,11 +49333,11 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49244
49333
|
if (!token)
|
|
49245
49334
|
return;
|
|
49246
49335
|
try {
|
|
49247
|
-
|
|
49336
|
+
mkdirSync49(dirname31(envPath), { recursive: true });
|
|
49248
49337
|
let body = "";
|
|
49249
49338
|
try {
|
|
49250
|
-
if (
|
|
49251
|
-
body =
|
|
49339
|
+
if (existsSync86(envPath))
|
|
49340
|
+
body = readFileSync74(envPath, "utf-8");
|
|
49252
49341
|
} catch {}
|
|
49253
49342
|
const line = `${VOICE_SIDECAR_TOKEN_ENV}=${token}`;
|
|
49254
49343
|
const keyRe = new RegExp(`^${VOICE_SIDECAR_TOKEN_ENV}=.*$`, "m");
|
|
@@ -49256,7 +49345,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49256
49345
|
`) ? body + `
|
|
49257
49346
|
` : body) + line + `
|
|
49258
49347
|
`;
|
|
49259
|
-
|
|
49348
|
+
writeFileSync31(envPath, next, {
|
|
49260
49349
|
encoding: "utf-8",
|
|
49261
49350
|
mode: 384
|
|
49262
49351
|
});
|
|
@@ -49308,11 +49397,11 @@ __export(exports_apply, {
|
|
|
49308
49397
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
|
|
49309
49398
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
49310
49399
|
});
|
|
49311
|
-
import { accessSync as accessSync3, chmodSync as chmodSync14, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as
|
|
49400
|
+
import { accessSync as accessSync3, chmodSync as chmodSync14, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync87, mkdirSync as mkdirSync50, readFileSync as readFileSync75, readdirSync as readdirSync31, renameSync as renameSync21, statSync as statSync47, writeFileSync as writeFileSync32 } from "node:fs";
|
|
49312
49401
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
49313
49402
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
49314
49403
|
import readline from "node:readline";
|
|
49315
|
-
import { dirname as
|
|
49404
|
+
import { dirname as dirname32, join as join88, resolve as resolve52 } from "node:path";
|
|
49316
49405
|
import { homedir as homedir49 } from "node:os";
|
|
49317
49406
|
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
49318
49407
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
@@ -49324,7 +49413,7 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49324
49413
|
return envPass;
|
|
49325
49414
|
try {
|
|
49326
49415
|
const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
|
|
49327
|
-
const blobPath =
|
|
49416
|
+
const blobPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49328
49417
|
const pass = readAutoUnlockFile2(blobPath);
|
|
49329
49418
|
return pass && pass.length > 0 ? pass : null;
|
|
49330
49419
|
} catch {
|
|
@@ -49333,10 +49422,10 @@ async function resolveOperatorVaultPassphrase(home2) {
|
|
|
49333
49422
|
}
|
|
49334
49423
|
function materializeLitellmMasterKeyForBroker(masterKey, home2 = process.env.HOME ?? "/root") {
|
|
49335
49424
|
try {
|
|
49336
|
-
const stateDir =
|
|
49337
|
-
|
|
49338
|
-
const path9 =
|
|
49339
|
-
|
|
49425
|
+
const stateDir = join88(home2, ".switchroom", "state", "auth-broker");
|
|
49426
|
+
mkdirSync50(stateDir, { recursive: true, mode: 448 });
|
|
49427
|
+
const path9 = join88(stateDir, LITELLM_MASTER_KEY_STATE_BASENAME);
|
|
49428
|
+
writeFileSync32(path9, masterKey.trim() + `
|
|
49340
49429
|
`, { mode: 384 });
|
|
49341
49430
|
try {
|
|
49342
49431
|
chmodSync14(path9, 384);
|
|
@@ -49428,9 +49517,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49428
49517
|
const oauthAccount = config.auth?.active;
|
|
49429
49518
|
let pendingConfigEdits = false;
|
|
49430
49519
|
let configText = null;
|
|
49431
|
-
if (switchroomConfigPath &&
|
|
49520
|
+
if (switchroomConfigPath && existsSync87(switchroomConfigPath)) {
|
|
49432
49521
|
try {
|
|
49433
|
-
configText =
|
|
49522
|
+
configText = readFileSync75(switchroomConfigPath, "utf-8");
|
|
49434
49523
|
} catch (err) {
|
|
49435
49524
|
ctx.writeErr(source_default.yellow(` ! litellm: could not read config for ACL grants (${err.message}); keys will be provisioned but agents may lack read-ACL.
|
|
49436
49525
|
`));
|
|
@@ -49659,14 +49748,14 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49659
49748
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
49660
49749
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
49661
49750
|
if (isCustomPath && ctx.customVaultPath) {
|
|
49662
|
-
return
|
|
49751
|
+
return dirname32(ctx.customVaultPath);
|
|
49663
49752
|
}
|
|
49664
|
-
return
|
|
49753
|
+
return join88(homeDir, ".switchroom", "vault");
|
|
49665
49754
|
}
|
|
49666
49755
|
function inspectVaultBindMountDir(vaultDir) {
|
|
49667
|
-
if (!
|
|
49756
|
+
if (!existsSync87(vaultDir))
|
|
49668
49757
|
return { kind: "missing" };
|
|
49669
|
-
const entries =
|
|
49758
|
+
const entries = readdirSync31(vaultDir);
|
|
49670
49759
|
const unknown = [];
|
|
49671
49760
|
for (const name of entries) {
|
|
49672
49761
|
if (KNOWN_VAULT_ARTIFACT_NAMES.has(name))
|
|
@@ -49692,60 +49781,60 @@ function hasVaultRefs(value) {
|
|
|
49692
49781
|
async function ensureHostMountSources(config) {
|
|
49693
49782
|
const home2 = resolveHostHomeForCompose();
|
|
49694
49783
|
const dirs = [
|
|
49695
|
-
|
|
49696
|
-
|
|
49697
|
-
|
|
49698
|
-
|
|
49699
|
-
|
|
49784
|
+
join88(home2, ".switchroom", "approvals"),
|
|
49785
|
+
join88(home2, ".switchroom", "scheduler"),
|
|
49786
|
+
join88(home2, ".switchroom", "logs"),
|
|
49787
|
+
join88(home2, ".switchroom", "compose"),
|
|
49788
|
+
join88(home2, ".switchroom", "broker-operator")
|
|
49700
49789
|
];
|
|
49701
49790
|
for (const name of Object.keys(config.agents)) {
|
|
49702
|
-
dirs.push(
|
|
49703
|
-
dirs.push(
|
|
49704
|
-
dirs.push(
|
|
49705
|
-
dirs.push(
|
|
49706
|
-
if (
|
|
49707
|
-
dirs.push(
|
|
49791
|
+
dirs.push(join88(home2, ".switchroom", "agents", name));
|
|
49792
|
+
dirs.push(join88(home2, ".switchroom", "logs", name));
|
|
49793
|
+
dirs.push(join88(home2, ".claude", "projects", name));
|
|
49794
|
+
dirs.push(join88(home2, ".switchroom", "audit", name));
|
|
49795
|
+
if (existsSync87(join88(home2, ".switchroom-config"))) {
|
|
49796
|
+
dirs.push(join88(home2, ".switchroom-config", "agents", name, "personal-skills"));
|
|
49708
49797
|
}
|
|
49709
49798
|
}
|
|
49710
49799
|
for (const dir of dirs) {
|
|
49711
49800
|
await mkdir2(dir, { recursive: true });
|
|
49712
49801
|
}
|
|
49713
|
-
const autoUnlockPath =
|
|
49714
|
-
if (!
|
|
49715
|
-
|
|
49802
|
+
const autoUnlockPath = join88(home2, ".switchroom", "vault-auto-unlock");
|
|
49803
|
+
if (!existsSync87(autoUnlockPath)) {
|
|
49804
|
+
writeFileSync32(autoUnlockPath, "", { mode: 384 });
|
|
49716
49805
|
}
|
|
49717
|
-
const auditLogPath =
|
|
49718
|
-
if (!
|
|
49719
|
-
|
|
49806
|
+
const auditLogPath = join88(home2, ".switchroom", "vault-audit.log");
|
|
49807
|
+
if (!existsSync87(auditLogPath)) {
|
|
49808
|
+
writeFileSync32(auditLogPath, "", { mode: 420 });
|
|
49720
49809
|
}
|
|
49721
49810
|
const grantsDbDir = getGrantsDbDir(home2);
|
|
49722
|
-
|
|
49811
|
+
mkdirSync50(grantsDbDir, { recursive: true, mode: 448 });
|
|
49723
49812
|
migrateLegacyGrantsDbLocation(getGrantsDbPath(home2));
|
|
49724
|
-
const hostdAuditLogPath =
|
|
49725
|
-
if (!
|
|
49726
|
-
|
|
49813
|
+
const hostdAuditLogPath = join88(home2, ".switchroom", "host-control-audit.log");
|
|
49814
|
+
if (!existsSync87(hostdAuditLogPath)) {
|
|
49815
|
+
writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
|
|
49727
49816
|
}
|
|
49728
49817
|
for (const name of Object.keys(config.agents)) {
|
|
49729
|
-
const tokenPath =
|
|
49730
|
-
if (!
|
|
49731
|
-
|
|
49818
|
+
const tokenPath = join88(home2, ".switchroom", "agents", name, ".vault-token");
|
|
49819
|
+
if (!existsSync87(tokenPath)) {
|
|
49820
|
+
writeFileSync32(tokenPath, "", { mode: 384 });
|
|
49732
49821
|
}
|
|
49733
49822
|
try {
|
|
49734
49823
|
const uid = allocateAgentUid(name);
|
|
49735
49824
|
chownSync8(tokenPath, uid, uid);
|
|
49736
49825
|
} catch {}
|
|
49737
49826
|
}
|
|
49738
|
-
const fleetDir =
|
|
49827
|
+
const fleetDir = join88(home2, ".switchroom", "fleet");
|
|
49739
49828
|
await mkdir2(fleetDir, { recursive: true });
|
|
49740
|
-
const invariantsPath =
|
|
49829
|
+
const invariantsPath = join88(fleetDir, "switchroom-invariants.md");
|
|
49741
49830
|
const invariantsCanonical = renderFleetInvariants();
|
|
49742
|
-
const invariantsCurrent =
|
|
49831
|
+
const invariantsCurrent = existsSync87(invariantsPath) ? readFileSync75(invariantsPath, "utf-8") : null;
|
|
49743
49832
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
49744
|
-
|
|
49833
|
+
writeFileSync32(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
49745
49834
|
}
|
|
49746
|
-
const fleetClaudePath =
|
|
49747
|
-
if (!
|
|
49748
|
-
|
|
49835
|
+
const fleetClaudePath = join88(fleetDir, "CLAUDE.md");
|
|
49836
|
+
if (!existsSync87(fleetClaudePath)) {
|
|
49837
|
+
writeFileSync32(fleetClaudePath, renderFleetDefaultsClaudeMd(), {
|
|
49749
49838
|
mode: 420
|
|
49750
49839
|
});
|
|
49751
49840
|
}
|
|
@@ -49774,7 +49863,7 @@ function isInAgentContainer(vaultPresent, composeV2Present, env2 = process.env)
|
|
|
49774
49863
|
function runApplyPreflight(config, opts = {}) {
|
|
49775
49864
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
49776
49865
|
const detect = opts.detectComposeV2 ?? detectComposeV2;
|
|
49777
|
-
const vaultMissing = hasVaultRefs(config) && !
|
|
49866
|
+
const vaultMissing = hasVaultRefs(config) && !existsSync87(vaultPath);
|
|
49778
49867
|
const composeErr = detect();
|
|
49779
49868
|
if ((vaultMissing || composeErr) && isInAgentContainer(!vaultMissing, composeErr === null)) {
|
|
49780
49869
|
throw new Error(IN_AGENT_CONTAINER_APPLY_MSG);
|
|
@@ -49788,7 +49877,7 @@ function runApplyPreflight(config, opts = {}) {
|
|
|
49788
49877
|
detectAndReportLegacyGdriveSlots(vaultPath);
|
|
49789
49878
|
}
|
|
49790
49879
|
function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
49791
|
-
if (!
|
|
49880
|
+
if (!existsSync87(vaultPath))
|
|
49792
49881
|
return;
|
|
49793
49882
|
const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
49794
49883
|
if (!passphrase)
|
|
@@ -49829,17 +49918,17 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
49829
49918
|
}
|
|
49830
49919
|
function writeInstallTypeCache(homeDir = homedir49()) {
|
|
49831
49920
|
const ctx = detectInstallType();
|
|
49832
|
-
const dir =
|
|
49833
|
-
const out =
|
|
49921
|
+
const dir = join88(homeDir, ".switchroom");
|
|
49922
|
+
const out = join88(dir, "install-type.json");
|
|
49834
49923
|
const tmp = `${out}.tmp`;
|
|
49835
|
-
|
|
49924
|
+
mkdirSync50(dir, { recursive: true });
|
|
49836
49925
|
const payload = {
|
|
49837
49926
|
install_type: ctx.install_type,
|
|
49838
49927
|
detected_at: new Date().toISOString(),
|
|
49839
49928
|
source_paths: ctx.source_paths
|
|
49840
49929
|
};
|
|
49841
|
-
|
|
49842
|
-
|
|
49930
|
+
writeFileSync32(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
|
|
49931
|
+
renameSync21(tmp, out);
|
|
49843
49932
|
return out;
|
|
49844
49933
|
}
|
|
49845
49934
|
async function runApply(config, options, deps = {}, switchroomConfigPath) {
|
|
@@ -49897,17 +49986,17 @@ Applying switchroom config...
|
|
|
49897
49986
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
49898
49987
|
`));
|
|
49899
49988
|
try {
|
|
49900
|
-
installUpdatePromptHook(
|
|
49989
|
+
installUpdatePromptHook(join88(agentsDir, name));
|
|
49901
49990
|
} catch (hookErr) {
|
|
49902
49991
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
49903
49992
|
`));
|
|
49904
49993
|
}
|
|
49905
|
-
await refreshAgentConnectionHealth(config, name,
|
|
49994
|
+
await refreshAgentConnectionHealth(config, name, join88(agentsDir, name), {
|
|
49906
49995
|
vaultAclReader: connHealthVaultAclReader
|
|
49907
49996
|
});
|
|
49908
49997
|
try {
|
|
49909
49998
|
const uid = allocateAgentUid(name);
|
|
49910
|
-
alignAgentUid(name,
|
|
49999
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49911
50000
|
confirm: !options.nonInteractive,
|
|
49912
50001
|
writeOut
|
|
49913
50002
|
});
|
|
@@ -49952,7 +50041,7 @@ Applying switchroom config...
|
|
|
49952
50041
|
for (const name of agentNames) {
|
|
49953
50042
|
try {
|
|
49954
50043
|
const uid = allocateAgentUid(name);
|
|
49955
|
-
alignAgentUid(name,
|
|
50044
|
+
alignAgentUid(name, join88(agentsDir, name), uid, {
|
|
49956
50045
|
confirm: !options.nonInteractive,
|
|
49957
50046
|
writeOut
|
|
49958
50047
|
});
|
|
@@ -50290,18 +50379,18 @@ function copyExampleConfig2(name) {
|
|
|
50290
50379
|
throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
|
|
50291
50380
|
}
|
|
50292
50381
|
const dest = resolve52(process.cwd(), "switchroom.yaml");
|
|
50293
|
-
if (
|
|
50382
|
+
if (existsSync87(dest)) {
|
|
50294
50383
|
console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
|
|
50295
50384
|
return;
|
|
50296
50385
|
}
|
|
50297
50386
|
const embedded = EMBEDDED_EXAMPLES[name];
|
|
50298
50387
|
if (embedded !== undefined) {
|
|
50299
|
-
|
|
50388
|
+
writeFileSync32(dest, embedded, { encoding: "utf8" });
|
|
50300
50389
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
50301
50390
|
return;
|
|
50302
50391
|
}
|
|
50303
50392
|
const exampleFile = resolve52(import.meta.dirname, `../../examples/${name}.yaml`);
|
|
50304
|
-
if (!
|
|
50393
|
+
if (!existsSync87(exampleFile)) {
|
|
50305
50394
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
50306
50395
|
}
|
|
50307
50396
|
copyFileSync12(exampleFile, dest);
|
|
@@ -50312,8 +50401,8 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
50312
50401
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
50313
50402
|
const unwritable = [];
|
|
50314
50403
|
for (const name of targets) {
|
|
50315
|
-
const startSh =
|
|
50316
|
-
if (!
|
|
50404
|
+
const startSh = join88(agentsDir, name, "start.sh");
|
|
50405
|
+
if (!existsSync87(startSh))
|
|
50317
50406
|
continue;
|
|
50318
50407
|
try {
|
|
50319
50408
|
accessSync3(startSh, fsConstants6.W_OK);
|
|
@@ -50518,7 +50607,7 @@ var init_apply = __esm(() => {
|
|
|
50518
50607
|
switchroom: switchroom_default,
|
|
50519
50608
|
minimal: minimal_default
|
|
50520
50609
|
};
|
|
50521
|
-
DEFAULT_COMPOSE_PATH2 =
|
|
50610
|
+
DEFAULT_COMPOSE_PATH2 = join88(homedir49(), ".switchroom", "compose", "docker-compose.yml");
|
|
50522
50611
|
IN_AGENT_CONTAINER_APPLY_MSG = "`switchroom apply`'s full per-agent scaffold cannot run from inside an " + "agent container \u2014 this is a host/hostd operation by construction " + "(no vault at the container HOME, and no `docker compose` v2 plugin here).\nTo roll the fleet to a new version, drive the hostd rollout (`mcp__hostd__rollout`): it runs a `--compose-only` apply plus a per-agent restart-reconcile, and each agent refreshes its own templates " + `on restart \u2014 the roll completes without any agent running a full apply.
|
|
50523
50612
|
` + "A full host-side `sudo switchroom apply` is only needed for structural changes (compose regeneration / new-agent scaffolding), and is run by the operator on the host, never from inside an agent.";
|
|
50524
50613
|
SELF_ELEVATE_PRESERVED_ENV = [
|
|
@@ -59829,49 +59918,49 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59829
59918
|
schemelessOptions.skipEscape = true;
|
|
59830
59919
|
return serialize(resolved, schemelessOptions);
|
|
59831
59920
|
}
|
|
59832
|
-
function resolveComponent(base,
|
|
59921
|
+
function resolveComponent(base, relative6, options, skipNormalization) {
|
|
59833
59922
|
const target = {};
|
|
59834
59923
|
if (!skipNormalization) {
|
|
59835
59924
|
base = parse6(serialize(base, options), options);
|
|
59836
|
-
|
|
59925
|
+
relative6 = parse6(serialize(relative6, options), options);
|
|
59837
59926
|
}
|
|
59838
59927
|
options = options || {};
|
|
59839
|
-
if (!options.tolerant &&
|
|
59840
|
-
target.scheme =
|
|
59841
|
-
target.userinfo =
|
|
59842
|
-
target.host =
|
|
59843
|
-
target.port =
|
|
59844
|
-
target.path = removeDotSegments(
|
|
59845
|
-
target.query =
|
|
59928
|
+
if (!options.tolerant && relative6.scheme) {
|
|
59929
|
+
target.scheme = relative6.scheme;
|
|
59930
|
+
target.userinfo = relative6.userinfo;
|
|
59931
|
+
target.host = relative6.host;
|
|
59932
|
+
target.port = relative6.port;
|
|
59933
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
59934
|
+
target.query = relative6.query;
|
|
59846
59935
|
} else {
|
|
59847
|
-
if (
|
|
59848
|
-
target.userinfo =
|
|
59849
|
-
target.host =
|
|
59850
|
-
target.port =
|
|
59851
|
-
target.path = removeDotSegments(
|
|
59852
|
-
target.query =
|
|
59936
|
+
if (relative6.userinfo !== undefined || relative6.host !== undefined || relative6.port !== undefined) {
|
|
59937
|
+
target.userinfo = relative6.userinfo;
|
|
59938
|
+
target.host = relative6.host;
|
|
59939
|
+
target.port = relative6.port;
|
|
59940
|
+
target.path = removeDotSegments(relative6.path || "");
|
|
59941
|
+
target.query = relative6.query;
|
|
59853
59942
|
} else {
|
|
59854
|
-
if (!
|
|
59943
|
+
if (!relative6.path) {
|
|
59855
59944
|
target.path = base.path;
|
|
59856
|
-
if (
|
|
59857
|
-
target.query =
|
|
59945
|
+
if (relative6.query !== undefined) {
|
|
59946
|
+
target.query = relative6.query;
|
|
59858
59947
|
} else {
|
|
59859
59948
|
target.query = base.query;
|
|
59860
59949
|
}
|
|
59861
59950
|
} else {
|
|
59862
|
-
if (
|
|
59863
|
-
target.path = removeDotSegments(
|
|
59951
|
+
if (relative6.path[0] === "/") {
|
|
59952
|
+
target.path = removeDotSegments(relative6.path);
|
|
59864
59953
|
} else {
|
|
59865
59954
|
if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
|
|
59866
|
-
target.path = "/" +
|
|
59955
|
+
target.path = "/" + relative6.path;
|
|
59867
59956
|
} else if (!base.path) {
|
|
59868
|
-
target.path =
|
|
59957
|
+
target.path = relative6.path;
|
|
59869
59958
|
} else {
|
|
59870
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
59959
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
|
|
59871
59960
|
}
|
|
59872
59961
|
target.path = removeDotSegments(target.path);
|
|
59873
59962
|
}
|
|
59874
|
-
target.query =
|
|
59963
|
+
target.query = relative6.query;
|
|
59875
59964
|
}
|
|
59876
59965
|
target.userinfo = base.userinfo;
|
|
59877
59966
|
target.host = base.host;
|
|
@@ -59879,7 +59968,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
59879
59968
|
}
|
|
59880
59969
|
target.scheme = base.scheme;
|
|
59881
59970
|
}
|
|
59882
|
-
target.fragment =
|
|
59971
|
+
target.fragment = relative6.fragment;
|
|
59883
59972
|
return target;
|
|
59884
59973
|
}
|
|
59885
59974
|
function equal(uriA, uriB, options) {
|
|
@@ -63967,7 +64056,7 @@ __export(exports_server2, {
|
|
|
63967
64056
|
TOOLS: () => TOOLS2
|
|
63968
64057
|
});
|
|
63969
64058
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
63970
|
-
import { existsSync as
|
|
64059
|
+
import { existsSync as existsSync96, readFileSync as readFileSync84 } from "node:fs";
|
|
63971
64060
|
function selfSocketPath() {
|
|
63972
64061
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
63973
64062
|
}
|
|
@@ -63994,7 +64083,7 @@ async function dispatchTool2(name, args) {
|
|
|
63994
64083
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
63995
64084
|
}
|
|
63996
64085
|
const sockPath = selfSocketPath();
|
|
63997
|
-
if (!
|
|
64086
|
+
if (!existsSync96(sockPath)) {
|
|
63998
64087
|
return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
|
|
63999
64088
|
}
|
|
64000
64089
|
let req;
|
|
@@ -64245,18 +64334,18 @@ function resolveAuditLogPath() {
|
|
|
64245
64334
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
64246
64335
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
64247
64336
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
64248
|
-
if (
|
|
64337
|
+
if (existsSync96(bindMounted))
|
|
64249
64338
|
return bindMounted;
|
|
64250
64339
|
return defaultAuditLogPath2();
|
|
64251
64340
|
}
|
|
64252
64341
|
function getLastUpdateApplyStatus() {
|
|
64253
64342
|
const path9 = resolveAuditLogPath();
|
|
64254
|
-
if (!
|
|
64343
|
+
if (!existsSync96(path9)) {
|
|
64255
64344
|
return errorText2(`get_status: audit log not found at ${path9}. No update_apply has run yet?`);
|
|
64256
64345
|
}
|
|
64257
64346
|
let raw;
|
|
64258
64347
|
try {
|
|
64259
|
-
raw =
|
|
64348
|
+
raw = readFileSync84(path9, "utf-8");
|
|
64260
64349
|
} catch (err2) {
|
|
64261
64350
|
return errorText2(`get_status: failed to read audit log at ${path9}: ${err2.message}`);
|
|
64262
64351
|
}
|
|
@@ -65006,14 +65095,14 @@ var init_header_passthrough_guard = __esm(() => {
|
|
|
65006
65095
|
});
|
|
65007
65096
|
|
|
65008
65097
|
// src/fleet-health/litellm-config-sensor.ts
|
|
65009
|
-
import { readFileSync as
|
|
65098
|
+
import { readFileSync as readFileSync86, existsSync as existsSync99 } from "node:fs";
|
|
65010
65099
|
function resolveLitellmConfigPath(explicit) {
|
|
65011
65100
|
return explicit ?? process.env.LITELLM_CONFIG_PATH ?? DEFAULT_LITELLM_CONFIG_PATH;
|
|
65012
65101
|
}
|
|
65013
65102
|
function scanLitellmConfig(opts = {}) {
|
|
65014
65103
|
const path9 = resolveLitellmConfigPath(opts.path);
|
|
65015
|
-
const exists = opts.existsFn ??
|
|
65016
|
-
const read = opts.readFn ?? ((p) =>
|
|
65104
|
+
const exists = opts.existsFn ?? existsSync99;
|
|
65105
|
+
const read = opts.readFn ?? ((p) => readFileSync86(p, "utf-8"));
|
|
65017
65106
|
const log = opts.log ?? (() => {});
|
|
65018
65107
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
65019
65108
|
if (!exists(path9)) {
|
|
@@ -65068,23 +65157,23 @@ __export(exports_scan, {
|
|
|
65068
65157
|
ledgerPathForBase: () => ledgerPathForBase
|
|
65069
65158
|
});
|
|
65070
65159
|
import {
|
|
65071
|
-
readFileSync as
|
|
65072
|
-
readdirSync as
|
|
65073
|
-
existsSync as
|
|
65074
|
-
mkdirSync as
|
|
65075
|
-
writeFileSync as
|
|
65160
|
+
readFileSync as readFileSync87,
|
|
65161
|
+
readdirSync as readdirSync39,
|
|
65162
|
+
existsSync as existsSync100,
|
|
65163
|
+
mkdirSync as mkdirSync57,
|
|
65164
|
+
writeFileSync as writeFileSync38
|
|
65076
65165
|
} from "node:fs";
|
|
65077
|
-
import { resolve as resolve57, dirname as
|
|
65166
|
+
import { resolve as resolve57, dirname as dirname35 } from "node:path";
|
|
65078
65167
|
import { homedir as homedir58 } from "node:os";
|
|
65079
65168
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir58()) {
|
|
65080
65169
|
return resolve57(home2, ".switchroom");
|
|
65081
65170
|
}
|
|
65082
65171
|
function listAgents(base) {
|
|
65083
65172
|
const dir = resolve57(base, "agents");
|
|
65084
|
-
if (!
|
|
65173
|
+
if (!existsSync100(dir))
|
|
65085
65174
|
return [];
|
|
65086
65175
|
try {
|
|
65087
|
-
return
|
|
65176
|
+
return readdirSync39(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
65088
65177
|
} catch {
|
|
65089
65178
|
return [];
|
|
65090
65179
|
}
|
|
@@ -65110,16 +65199,16 @@ function runScan(opts = {}) {
|
|
|
65110
65199
|
let gwText = "";
|
|
65111
65200
|
let sawArtifact = false;
|
|
65112
65201
|
try {
|
|
65113
|
-
if (
|
|
65114
|
-
turnsText =
|
|
65202
|
+
if (existsSync100(turnsPath)) {
|
|
65203
|
+
turnsText = readFileSync87(turnsPath, "utf-8");
|
|
65115
65204
|
sawArtifact = true;
|
|
65116
65205
|
}
|
|
65117
65206
|
} catch (e) {
|
|
65118
65207
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
65119
65208
|
}
|
|
65120
65209
|
try {
|
|
65121
|
-
if (
|
|
65122
|
-
gwText =
|
|
65210
|
+
if (existsSync100(gwPath)) {
|
|
65211
|
+
gwText = readFileSync87(gwPath, "utf-8");
|
|
65123
65212
|
sawArtifact = true;
|
|
65124
65213
|
}
|
|
65125
65214
|
} catch (e) {
|
|
@@ -65161,20 +65250,20 @@ function runScan(opts = {}) {
|
|
|
65161
65250
|
function readLedgerIfPresent(base) {
|
|
65162
65251
|
const path9 = ledgerPathForBase(base);
|
|
65163
65252
|
try {
|
|
65164
|
-
if (!
|
|
65253
|
+
if (!existsSync100(path9))
|
|
65165
65254
|
return null;
|
|
65166
|
-
return JSON.parse(
|
|
65255
|
+
return JSON.parse(readFileSync87(path9, "utf-8"));
|
|
65167
65256
|
} catch {
|
|
65168
65257
|
return null;
|
|
65169
65258
|
}
|
|
65170
65259
|
}
|
|
65171
65260
|
function ledgerPathForBase(base) {
|
|
65172
|
-
return fleetHealthLedgerPath(
|
|
65261
|
+
return fleetHealthLedgerPath(dirname35(base));
|
|
65173
65262
|
}
|
|
65174
65263
|
function writeLedger(base, ledger) {
|
|
65175
65264
|
const path9 = ledgerPathForBase(base);
|
|
65176
|
-
|
|
65177
|
-
|
|
65265
|
+
mkdirSync57(dirname35(path9), { recursive: true });
|
|
65266
|
+
writeFileSync38(path9, JSON.stringify(ledger, null, 2) + `
|
|
65178
65267
|
`, "utf-8");
|
|
65179
65268
|
return path9;
|
|
65180
65269
|
}
|
|
@@ -72180,7 +72269,7 @@ init_audit_log();
|
|
|
72180
72269
|
init_test_isolation_guard();
|
|
72181
72270
|
import * as net3 from "node:net";
|
|
72182
72271
|
import { mkdirSync as mkdirSync25, chmodSync as chmodSync9, chownSync as chownSync4, existsSync as existsSync43, readFileSync as readFileSync35, readdirSync as readdirSync18, statSync as statSync26, unlinkSync as unlinkSync10, writeFileSync as writeFileSync14, renameSync as renameSync14 } from "node:fs";
|
|
72183
|
-
import { dirname as
|
|
72272
|
+
import { dirname as dirname15, resolve as resolve29, basename as basename7 } from "node:path";
|
|
72184
72273
|
import * as os4 from "node:os";
|
|
72185
72274
|
import * as path4 from "node:path";
|
|
72186
72275
|
|
|
@@ -74556,7 +74645,7 @@ class VaultBroker {
|
|
|
74556
74645
|
this.passphrase = this.testOpts._testPassphrase;
|
|
74557
74646
|
}
|
|
74558
74647
|
process.umask(63);
|
|
74559
|
-
const parentDir =
|
|
74648
|
+
const parentDir = dirname15(this.socketPath);
|
|
74560
74649
|
mkdirSync25(parentDir, { recursive: true, mode: 448 });
|
|
74561
74650
|
try {
|
|
74562
74651
|
chmodSync9(parentDir, 448);
|
|
@@ -76101,15 +76190,15 @@ class VaultBroker {
|
|
|
76101
76190
|
}
|
|
76102
76191
|
}
|
|
76103
76192
|
function detectVaultLayoutDrift(vaultPath) {
|
|
76104
|
-
const dir =
|
|
76193
|
+
const dir = dirname15(vaultPath);
|
|
76105
76194
|
if (basename7(dir) !== "vault")
|
|
76106
76195
|
return;
|
|
76107
76196
|
if (basename7(vaultPath) !== "vault.enc")
|
|
76108
76197
|
return;
|
|
76109
|
-
const switchroomDir =
|
|
76198
|
+
const switchroomDir = dirname15(dir);
|
|
76110
76199
|
if (basename7(switchroomDir) !== ".switchroom")
|
|
76111
76200
|
return;
|
|
76112
|
-
const home2 =
|
|
76201
|
+
const home2 = dirname15(switchroomDir);
|
|
76113
76202
|
const result = inspectVaultLayout(home2);
|
|
76114
76203
|
if (result.kind === "divergent") {
|
|
76115
76204
|
throw new VaultError(`Vault layout divergence detected at boot: ${result.details.oldPath} and ${result.details.newPath} are both regular files with different content. An older switchroom CLI may have written to the legacy path after migration ran. Run \`switchroom apply\` from the host to surface the recovery recipe (state E refusal with literal \`mv\` commands). See docs/operators/state-e-recovery.md.`);
|
|
@@ -79590,7 +79679,7 @@ import {
|
|
|
79590
79679
|
writeSync as writeSync8,
|
|
79591
79680
|
constants as fsConstants3
|
|
79592
79681
|
} from "node:fs";
|
|
79593
|
-
import { resolve as resolve34, extname, join as join51, relative, dirname as
|
|
79682
|
+
import { resolve as resolve34, extname, join as join51, relative as relative3, dirname as dirname18 } from "node:path";
|
|
79594
79683
|
import { homedir as homedir28 } from "node:os";
|
|
79595
79684
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
79596
79685
|
|
|
@@ -83051,7 +83140,7 @@ function resolveWebToken() {
|
|
|
83051
83140
|
return existing;
|
|
83052
83141
|
}
|
|
83053
83142
|
const token = randomBytes11(32).toString("hex");
|
|
83054
|
-
mkdirSync31(
|
|
83143
|
+
mkdirSync31(dirname18(tokenPath), { recursive: true, mode: 448 });
|
|
83055
83144
|
try {
|
|
83056
83145
|
const fd = openSync11(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
|
|
83057
83146
|
try {
|
|
@@ -83638,7 +83727,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
83638
83727
|
} catch {
|
|
83639
83728
|
return new Response("Not Found", { status: 404 });
|
|
83640
83729
|
}
|
|
83641
|
-
const rel =
|
|
83730
|
+
const rel = relative3(uiDir, realFullPath);
|
|
83642
83731
|
if (rel.startsWith("..") || resolve34(uiDir, rel) !== realFullPath) {
|
|
83643
83732
|
return new Response("Forbidden", { status: 403 });
|
|
83644
83733
|
}
|
|
@@ -83768,7 +83857,7 @@ init_loader();
|
|
|
83768
83857
|
|
|
83769
83858
|
// src/web/startup-guard.ts
|
|
83770
83859
|
import { existsSync as existsSync58, readFileSync as readFileSync53, writeFileSync as writeFileSync18, mkdirSync as mkdirSync32, statSync as statSync33, unlinkSync as unlinkSync13 } from "node:fs";
|
|
83771
|
-
import { dirname as
|
|
83860
|
+
import { dirname as dirname19 } from "node:path";
|
|
83772
83861
|
function detectConfigMountFault(configPath, deps = {}) {
|
|
83773
83862
|
const stat = deps.stat ?? ((p) => statSync33(p));
|
|
83774
83863
|
let st;
|
|
@@ -83815,7 +83904,7 @@ function readCrashState(statePath) {
|
|
|
83815
83904
|
}
|
|
83816
83905
|
function writeCrashState(statePath, state) {
|
|
83817
83906
|
try {
|
|
83818
|
-
mkdirSync32(
|
|
83907
|
+
mkdirSync32(dirname19(statePath), { recursive: true });
|
|
83819
83908
|
writeFileSync18(statePath, JSON.stringify(state), { mode: 384 });
|
|
83820
83909
|
} catch {}
|
|
83821
83910
|
}
|
|
@@ -83911,7 +84000,7 @@ init_atomic();
|
|
|
83911
84000
|
init_loader();
|
|
83912
84001
|
init_scaffold();
|
|
83913
84002
|
import { existsSync as existsSync59, copyFileSync as copyFileSync9, readFileSync as readFileSync54, mkdirSync as mkdirSync33, statSync as statSync34 } from "node:fs";
|
|
83914
|
-
import { resolve as resolve35, dirname as
|
|
84003
|
+
import { resolve as resolve35, dirname as dirname20 } from "node:path";
|
|
83915
84004
|
init_state();
|
|
83916
84005
|
init_vault();
|
|
83917
84006
|
init_manager();
|
|
@@ -84223,7 +84312,7 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
84223
84312
|
if (!existsSync59(srcFile)) {
|
|
84224
84313
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
84225
84314
|
}
|
|
84226
|
-
mkdirSync33(
|
|
84315
|
+
mkdirSync33(dirname20(destFile), { recursive: true });
|
|
84227
84316
|
copyFileSync9(srcFile, destFile);
|
|
84228
84317
|
console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
|
|
84229
84318
|
console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
|
|
@@ -85003,9 +85092,9 @@ init_source();
|
|
|
85003
85092
|
init_loader();
|
|
85004
85093
|
init_lifecycle();
|
|
85005
85094
|
init_compose_env();
|
|
85006
|
-
import {
|
|
85095
|
+
import { existsSync as existsSync68, mkdirSync as mkdirSync36, readFileSync as readFileSync61, realpathSync as realpathSync6, statSync as statSync40, chownSync as chownSync5 } from "node:fs";
|
|
85007
85096
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
85008
|
-
import { join as
|
|
85097
|
+
import { join as join70, dirname as dirname23, resolve as resolve40 } from "node:path";
|
|
85009
85098
|
import { homedir as homedir40 } from "node:os";
|
|
85010
85099
|
|
|
85011
85100
|
// src/cli/release-yaml.ts
|
|
@@ -85096,10 +85185,132 @@ ${lines.join(`
|
|
|
85096
85185
|
|
|
85097
85186
|
// src/cli/update.ts
|
|
85098
85187
|
init_hindsight();
|
|
85188
|
+
init_scaffold_integration();
|
|
85189
|
+
|
|
85190
|
+
// src/cli/sync-bundled-skills.ts
|
|
85191
|
+
import {
|
|
85192
|
+
cpSync as cpSync2,
|
|
85193
|
+
existsSync as existsSync67,
|
|
85194
|
+
mkdirSync as mkdirSync35,
|
|
85195
|
+
readFileSync as readFileSync60,
|
|
85196
|
+
readdirSync as readdirSync24,
|
|
85197
|
+
renameSync as renameSync16,
|
|
85198
|
+
rmSync as rmSync12,
|
|
85199
|
+
writeFileSync as writeFileSync19
|
|
85200
|
+
} from "node:fs";
|
|
85201
|
+
import { join as join69 } from "node:path";
|
|
85202
|
+
var BUNDLED_SKILL_MANIFEST_NAME = ".switchroom-manifest.json";
|
|
85203
|
+
function listSkillDirs(dir) {
|
|
85204
|
+
if (!existsSync67(dir))
|
|
85205
|
+
return [];
|
|
85206
|
+
return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
85207
|
+
}
|
|
85208
|
+
function readBundledSkillManifest(poolDir) {
|
|
85209
|
+
const path5 = join69(poolDir, BUNDLED_SKILL_MANIFEST_NAME);
|
|
85210
|
+
if (!existsSync67(path5))
|
|
85211
|
+
return { firstRun: true };
|
|
85212
|
+
try {
|
|
85213
|
+
const parsed = JSON.parse(readFileSync60(path5, "utf8"));
|
|
85214
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.skills) || !parsed.skills.every((s) => typeof s === "string")) {
|
|
85215
|
+
return { corrupt: true };
|
|
85216
|
+
}
|
|
85217
|
+
const m = parsed;
|
|
85218
|
+
return { manifest: { version: String(m.version ?? ""), skills: m.skills, updatedAt: String(m.updatedAt ?? "") } };
|
|
85219
|
+
} catch {
|
|
85220
|
+
return { corrupt: true };
|
|
85221
|
+
}
|
|
85222
|
+
}
|
|
85223
|
+
function stageAndSwap(srcSkill, destSkill, poolDir, name) {
|
|
85224
|
+
const staging = join69(poolDir, `.tmp-${name}-${process.pid}-${Date.now()}`);
|
|
85225
|
+
try {
|
|
85226
|
+
rmSync12(staging, { recursive: true, force: true });
|
|
85227
|
+
cpSync2(srcSkill, staging, { recursive: true, dereference: false });
|
|
85228
|
+
rmSync12(destSkill, { recursive: true, force: true });
|
|
85229
|
+
renameSync16(staging, destSkill);
|
|
85230
|
+
} finally {
|
|
85231
|
+
rmSync12(staging, { recursive: true, force: true });
|
|
85232
|
+
}
|
|
85233
|
+
}
|
|
85234
|
+
function syncBundledSkills(opts) {
|
|
85235
|
+
const { source, dest, version: version2 } = opts;
|
|
85236
|
+
const result = {
|
|
85237
|
+
added: [],
|
|
85238
|
+
updated: [],
|
|
85239
|
+
removed: [],
|
|
85240
|
+
preserved: [],
|
|
85241
|
+
ownershipTransferred: [],
|
|
85242
|
+
firstRun: false,
|
|
85243
|
+
manifestCorrupt: false
|
|
85244
|
+
};
|
|
85245
|
+
mkdirSync35(dest, { recursive: true });
|
|
85246
|
+
const prior = readBundledSkillManifest(dest);
|
|
85247
|
+
const priorSkills = new Set("manifest" in prior ? prior.manifest.skills : []);
|
|
85248
|
+
result.firstRun = "firstRun" in prior;
|
|
85249
|
+
result.manifestCorrupt = "corrupt" in prior;
|
|
85250
|
+
const shipped = listSkillDirs(source).sort();
|
|
85251
|
+
const shippedSet = new Set(shipped);
|
|
85252
|
+
for (const name of shipped) {
|
|
85253
|
+
const destSkill = join69(dest, name);
|
|
85254
|
+
const existed = existsSync67(destSkill);
|
|
85255
|
+
let transferred = false;
|
|
85256
|
+
if (existed && !priorSkills.has(name) && !result.firstRun) {
|
|
85257
|
+
const backup = join69(dest, `${name}.operator-backup-${process.pid}-${Date.now()}`);
|
|
85258
|
+
try {
|
|
85259
|
+
renameSync16(destSkill, backup);
|
|
85260
|
+
transferred = true;
|
|
85261
|
+
result.ownershipTransferred.push(name);
|
|
85262
|
+
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.
|
|
85263
|
+
`);
|
|
85264
|
+
} catch {
|
|
85265
|
+
result.ownershipTransferred.push(name);
|
|
85266
|
+
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.
|
|
85267
|
+
`);
|
|
85268
|
+
continue;
|
|
85269
|
+
}
|
|
85270
|
+
}
|
|
85271
|
+
stageAndSwap(join69(source, name), destSkill, dest, name);
|
|
85272
|
+
if (!transferred && (priorSkills.has(name) || existed))
|
|
85273
|
+
result.updated.push(name);
|
|
85274
|
+
else
|
|
85275
|
+
result.added.push(name);
|
|
85276
|
+
}
|
|
85277
|
+
if (!result.firstRun && !result.manifestCorrupt) {
|
|
85278
|
+
for (const name of priorSkills) {
|
|
85279
|
+
if (shippedSet.has(name))
|
|
85280
|
+
continue;
|
|
85281
|
+
const target = join69(dest, name);
|
|
85282
|
+
if (existsSync67(target)) {
|
|
85283
|
+
rmSync12(target, { recursive: true, force: true });
|
|
85284
|
+
result.removed.push(name);
|
|
85285
|
+
}
|
|
85286
|
+
}
|
|
85287
|
+
}
|
|
85288
|
+
for (const name of listSkillDirs(dest)) {
|
|
85289
|
+
if (!shippedSet.has(name) && !priorSkills.has(name)) {
|
|
85290
|
+
result.preserved.push(name);
|
|
85291
|
+
}
|
|
85292
|
+
}
|
|
85293
|
+
result.removed.sort();
|
|
85294
|
+
result.preserved.sort();
|
|
85295
|
+
const manifest = {
|
|
85296
|
+
version: version2,
|
|
85297
|
+
skills: shipped,
|
|
85298
|
+
updatedAt: new Date().toISOString()
|
|
85299
|
+
};
|
|
85300
|
+
const manifestPath = join69(dest, BUNDLED_SKILL_MANIFEST_NAME);
|
|
85301
|
+
const manifestTmp = join69(dest, `${BUNDLED_SKILL_MANIFEST_NAME}.tmp-${process.pid}-${Date.now()}`);
|
|
85302
|
+
writeFileSync19(manifestTmp, JSON.stringify(manifest, null, 2) + `
|
|
85303
|
+
`, "utf8");
|
|
85304
|
+
renameSync16(manifestTmp, manifestPath);
|
|
85305
|
+
return result;
|
|
85306
|
+
}
|
|
85307
|
+
|
|
85308
|
+
// src/cli/update.ts
|
|
85309
|
+
init_resolve_version();
|
|
85099
85310
|
function defaultPersistPin(configPath) {
|
|
85100
85311
|
return (pin) => {
|
|
85101
85312
|
const path5 = configPath ?? findConfigFile();
|
|
85102
|
-
const before =
|
|
85313
|
+
const before = readFileSync61(path5, "utf8");
|
|
85103
85314
|
const after = setReleasePinInConfig(before, pin);
|
|
85104
85315
|
if (after === before)
|
|
85105
85316
|
return;
|
|
@@ -85113,18 +85324,18 @@ function defaultPersistPin(configPath) {
|
|
|
85113
85324
|
} catch {}
|
|
85114
85325
|
};
|
|
85115
85326
|
}
|
|
85116
|
-
var DEFAULT_COMPOSE_PATH =
|
|
85327
|
+
var DEFAULT_COMPOSE_PATH = join70(homedir40(), ".switchroom", "compose", "docker-compose.yml");
|
|
85117
85328
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
85118
|
-
let dir =
|
|
85329
|
+
let dir = dirname23(scriptPath);
|
|
85119
85330
|
for (let i = 0;i < 12; i++) {
|
|
85120
|
-
if (
|
|
85331
|
+
if (existsSync68(join70(dir, ".git"))) {
|
|
85121
85332
|
try {
|
|
85122
|
-
const pkg = JSON.parse(
|
|
85333
|
+
const pkg = JSON.parse(readFileSync61(join70(dir, "package.json"), "utf-8"));
|
|
85123
85334
|
if (pkg.name === "switchroom")
|
|
85124
85335
|
return true;
|
|
85125
85336
|
} catch {}
|
|
85126
85337
|
}
|
|
85127
|
-
const parent =
|
|
85338
|
+
const parent = dirname23(dir);
|
|
85128
85339
|
if (parent === dir)
|
|
85129
85340
|
break;
|
|
85130
85341
|
dir = parent;
|
|
@@ -85204,7 +85415,7 @@ function planUpdate(opts) {
|
|
|
85204
85415
|
steps.push({
|
|
85205
85416
|
name: "pull-images",
|
|
85206
85417
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
85207
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
85418
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync68(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
85208
85419
|
run: () => {
|
|
85209
85420
|
const r = runner("docker", [
|
|
85210
85421
|
"compose",
|
|
@@ -85330,23 +85541,48 @@ function planUpdate(opts) {
|
|
|
85330
85541
|
return;
|
|
85331
85542
|
}
|
|
85332
85543
|
const source = resolve40(import.meta.dirname, "../../skills");
|
|
85333
|
-
const dest =
|
|
85334
|
-
if (!
|
|
85544
|
+
const dest = join70(homedir40(), ".switchroom", "skills", "_bundled");
|
|
85545
|
+
if (!existsSync68(source)) {
|
|
85335
85546
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
85336
85547
|
`);
|
|
85337
85548
|
return;
|
|
85338
85549
|
}
|
|
85339
85550
|
try {
|
|
85340
|
-
|
|
85341
|
-
|
|
85551
|
+
mkdirSync36(dirname23(dest), { recursive: true });
|
|
85552
|
+
const r = syncBundledSkills({
|
|
85553
|
+
source,
|
|
85554
|
+
dest,
|
|
85555
|
+
version: SWITCHROOM_VERSION
|
|
85556
|
+
});
|
|
85557
|
+
if (r.manifestCorrupt) {
|
|
85558
|
+
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 pool manifest was unreadable; ` + `deleted nothing (fail-closed) and rewrote a clean manifest.
|
|
85559
|
+
`);
|
|
85560
|
+
}
|
|
85561
|
+
if (r.removed.length > 0) {
|
|
85562
|
+
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 removed ${r.removed.length} retired ` + `bundled skill(s): ${r.removed.join(", ")}.
|
|
85563
|
+
`);
|
|
85342
85564
|
}
|
|
85343
|
-
mkdirSync35(dirname21(dest), { recursive: true });
|
|
85344
|
-
cpSync2(source, dest, { recursive: true, dereference: false });
|
|
85345
85565
|
} catch (err) {
|
|
85346
85566
|
throw new Error(`sync-bundled-skills failed: ${err.message}`);
|
|
85347
85567
|
}
|
|
85348
85568
|
}
|
|
85349
85569
|
});
|
|
85570
|
+
steps.push({
|
|
85571
|
+
name: "verify-bundled-skills",
|
|
85572
|
+
description: "Assert every builtin default skill is present in ~/.switchroom/skills/_bundled/ after sync.",
|
|
85573
|
+
run: () => {
|
|
85574
|
+
if (opts.syncBundledSkillsFn)
|
|
85575
|
+
return;
|
|
85576
|
+
const dest = join70(homedir40(), ".switchroom", "skills", "_bundled");
|
|
85577
|
+
if (!existsSync68(dest)) {
|
|
85578
|
+
return;
|
|
85579
|
+
}
|
|
85580
|
+
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync68(join70(dest, key)));
|
|
85581
|
+
if (missing.length > 0) {
|
|
85582
|
+
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.`);
|
|
85583
|
+
}
|
|
85584
|
+
}
|
|
85585
|
+
});
|
|
85350
85586
|
steps.push({
|
|
85351
85587
|
name: "stamp-restart-marker",
|
|
85352
85588
|
description: 'Write a clean-shutdown marker for every agent (reason="operator: switchroom update") so the post-recreate boot card renders as graceful rather than crash',
|
|
@@ -85376,7 +85612,7 @@ function planUpdate(opts) {
|
|
|
85376
85612
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
85377
85613
|
run: () => {
|
|
85378
85614
|
try {
|
|
85379
|
-
const composeText =
|
|
85615
|
+
const composeText = readFileSync61(composePath, "utf8");
|
|
85380
85616
|
const pf = validateBindSources(composeText);
|
|
85381
85617
|
if (!pf.ok)
|
|
85382
85618
|
throw new Error(formatPreflightError(pf));
|
|
@@ -85453,12 +85689,12 @@ function defaultStatusProbe(composePath) {
|
|
|
85453
85689
|
try {
|
|
85454
85690
|
cliBuiltAt = new Date(statSync40(scriptPath).mtimeMs).toISOString();
|
|
85455
85691
|
} catch {}
|
|
85456
|
-
let dir =
|
|
85692
|
+
let dir = dirname23(scriptPath);
|
|
85457
85693
|
for (let i = 0;i < 8; i++) {
|
|
85458
|
-
const pkgPath =
|
|
85459
|
-
if (
|
|
85694
|
+
const pkgPath = join70(dir, "package.json");
|
|
85695
|
+
if (existsSync68(pkgPath)) {
|
|
85460
85696
|
try {
|
|
85461
|
-
const pkg = JSON.parse(
|
|
85697
|
+
const pkg = JSON.parse(readFileSync61(pkgPath, "utf-8"));
|
|
85462
85698
|
if (typeof pkg.version === "string")
|
|
85463
85699
|
cliVersion = pkg.version;
|
|
85464
85700
|
} catch (err) {
|
|
@@ -85466,7 +85702,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85466
85702
|
}
|
|
85467
85703
|
break;
|
|
85468
85704
|
}
|
|
85469
|
-
const parent =
|
|
85705
|
+
const parent = dirname23(dir);
|
|
85470
85706
|
if (parent === dir)
|
|
85471
85707
|
break;
|
|
85472
85708
|
dir = parent;
|
|
@@ -85479,7 +85715,7 @@ function defaultStatusProbe(composePath) {
|
|
|
85479
85715
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
85480
85716
|
}
|
|
85481
85717
|
const services = [];
|
|
85482
|
-
if (!
|
|
85718
|
+
if (!existsSync68(composePath)) {
|
|
85483
85719
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
85484
85720
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
85485
85721
|
}
|
|
@@ -85675,7 +85911,7 @@ function registerUpdateCommand(program3) {
|
|
|
85675
85911
|
// src/cli/rollout.ts
|
|
85676
85912
|
init_helpers();
|
|
85677
85913
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
85678
|
-
import { readFileSync as
|
|
85914
|
+
import { readFileSync as readFileSync62, chownSync as chownSync6, statSync as statSync41 } from "node:fs";
|
|
85679
85915
|
import { homedir as homedir41 } from "node:os";
|
|
85680
85916
|
init_operator_uid();
|
|
85681
85917
|
init_atomic();
|
|
@@ -85961,7 +86197,7 @@ function resolveRollbackTarget(auditLogPath) {
|
|
|
85961
86197
|
const logPath = auditLogPath ?? defaultAuditLogPath2(homedir41());
|
|
85962
86198
|
let raw;
|
|
85963
86199
|
try {
|
|
85964
|
-
raw =
|
|
86200
|
+
raw = readFileSync62(logPath, "utf8");
|
|
85965
86201
|
} catch {
|
|
85966
86202
|
return null;
|
|
85967
86203
|
}
|
|
@@ -86079,7 +86315,7 @@ function registerRolloutCommand(program3) {
|
|
|
86079
86315
|
`),
|
|
86080
86316
|
webImageTag: () => deployedImageTag("switchroom-web"),
|
|
86081
86317
|
persistPin: (pin) => {
|
|
86082
|
-
const before =
|
|
86318
|
+
const before = readFileSync62(configPath, "utf8");
|
|
86083
86319
|
const after = setReleasePinInConfig(before, pin);
|
|
86084
86320
|
if (after === before)
|
|
86085
86321
|
return;
|
|
@@ -86157,8 +86393,8 @@ init_helpers();
|
|
|
86157
86393
|
init_lifecycle();
|
|
86158
86394
|
init_resolve_version();
|
|
86159
86395
|
import { execSync as execSync3 } from "node:child_process";
|
|
86160
|
-
import { existsSync as
|
|
86161
|
-
import { dirname as
|
|
86396
|
+
import { existsSync as existsSync69, readFileSync as readFileSync63 } from "node:fs";
|
|
86397
|
+
import { dirname as dirname24, join as join71 } from "node:path";
|
|
86162
86398
|
function getClaudeCodeVersion() {
|
|
86163
86399
|
try {
|
|
86164
86400
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -86208,16 +86444,16 @@ function formatUptime3(timestamp) {
|
|
|
86208
86444
|
function locateSwitchroomInstallDir() {
|
|
86209
86445
|
let dir = import.meta.dirname;
|
|
86210
86446
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
86211
|
-
const pkgPath =
|
|
86212
|
-
if (
|
|
86447
|
+
const pkgPath = join71(dir, "package.json");
|
|
86448
|
+
if (existsSync69(pkgPath)) {
|
|
86213
86449
|
try {
|
|
86214
|
-
const pkg = JSON.parse(
|
|
86215
|
-
if (pkg.name === "switchroom" &&
|
|
86450
|
+
const pkg = JSON.parse(readFileSync63(pkgPath, "utf-8"));
|
|
86451
|
+
if (pkg.name === "switchroom" && existsSync69(join71(dir, ".git"))) {
|
|
86216
86452
|
return dir;
|
|
86217
86453
|
}
|
|
86218
86454
|
} catch {}
|
|
86219
86455
|
}
|
|
86220
|
-
dir =
|
|
86456
|
+
dir = dirname24(dir);
|
|
86221
86457
|
}
|
|
86222
86458
|
return null;
|
|
86223
86459
|
}
|
|
@@ -86390,29 +86626,29 @@ import { resolve as resolve42 } from "node:path";
|
|
|
86390
86626
|
|
|
86391
86627
|
// src/agents/session-retention.ts
|
|
86392
86628
|
import {
|
|
86393
|
-
existsSync as
|
|
86394
|
-
readdirSync as
|
|
86629
|
+
existsSync as existsSync70,
|
|
86630
|
+
readdirSync as readdirSync25,
|
|
86395
86631
|
statSync as statSync42,
|
|
86396
86632
|
unlinkSync as unlinkSync14
|
|
86397
86633
|
} from "node:fs";
|
|
86398
|
-
import { join as
|
|
86634
|
+
import { join as join72 } from "node:path";
|
|
86399
86635
|
var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
|
|
86400
86636
|
var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
86401
86637
|
var MIN_KEEP = 2;
|
|
86402
86638
|
function collectSessionJsonl(claudeConfigDir) {
|
|
86403
|
-
const projects =
|
|
86404
|
-
if (!
|
|
86639
|
+
const projects = join72(claudeConfigDir, "projects");
|
|
86640
|
+
if (!existsSync70(projects))
|
|
86405
86641
|
return [];
|
|
86406
86642
|
const found = [];
|
|
86407
86643
|
const walk2 = (dir) => {
|
|
86408
86644
|
let entries;
|
|
86409
86645
|
try {
|
|
86410
|
-
entries =
|
|
86646
|
+
entries = readdirSync25(dir);
|
|
86411
86647
|
} catch {
|
|
86412
86648
|
return;
|
|
86413
86649
|
}
|
|
86414
86650
|
for (const name of entries) {
|
|
86415
|
-
const full =
|
|
86651
|
+
const full = join72(dir, name);
|
|
86416
86652
|
let st;
|
|
86417
86653
|
try {
|
|
86418
86654
|
st = statSync42(full);
|
|
@@ -86532,18 +86768,18 @@ function registerHandoffCommand(program3) {
|
|
|
86532
86768
|
// src/issues/store.ts
|
|
86533
86769
|
import {
|
|
86534
86770
|
closeSync as closeSync12,
|
|
86535
|
-
existsSync as
|
|
86536
|
-
mkdirSync as
|
|
86771
|
+
existsSync as existsSync71,
|
|
86772
|
+
mkdirSync as mkdirSync37,
|
|
86537
86773
|
openSync as openSync12,
|
|
86538
|
-
readdirSync as
|
|
86539
|
-
readFileSync as
|
|
86540
|
-
renameSync as
|
|
86774
|
+
readdirSync as readdirSync26,
|
|
86775
|
+
readFileSync as readFileSync64,
|
|
86776
|
+
renameSync as renameSync17,
|
|
86541
86777
|
statSync as statSync43,
|
|
86542
86778
|
unlinkSync as unlinkSync15,
|
|
86543
|
-
writeFileSync as
|
|
86779
|
+
writeFileSync as writeFileSync20,
|
|
86544
86780
|
writeSync as writeSync9
|
|
86545
86781
|
} from "node:fs";
|
|
86546
|
-
import { join as
|
|
86782
|
+
import { join as join73 } from "node:path";
|
|
86547
86783
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
86548
86784
|
import { execSync as execSync4 } from "node:child_process";
|
|
86549
86785
|
|
|
@@ -86974,12 +87210,12 @@ function redactedMarker(ruleId) {
|
|
|
86974
87210
|
var ISSUES_FILE = "issues.jsonl";
|
|
86975
87211
|
var ISSUES_LOCK = "issues.lock";
|
|
86976
87212
|
function readAll(stateDir) {
|
|
86977
|
-
const path5 =
|
|
86978
|
-
if (!
|
|
87213
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87214
|
+
if (!existsSync71(path5))
|
|
86979
87215
|
return [];
|
|
86980
87216
|
let raw;
|
|
86981
87217
|
try {
|
|
86982
|
-
raw =
|
|
87218
|
+
raw = readFileSync64(path5, "utf-8");
|
|
86983
87219
|
} catch {
|
|
86984
87220
|
return [];
|
|
86985
87221
|
}
|
|
@@ -87052,7 +87288,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
87052
87288
|
});
|
|
87053
87289
|
}
|
|
87054
87290
|
function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
87055
|
-
if (!
|
|
87291
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87056
87292
|
return 0;
|
|
87057
87293
|
return withLock(stateDir, () => {
|
|
87058
87294
|
const all = readAll(stateDir);
|
|
@@ -87070,7 +87306,7 @@ function resolve43(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
87070
87306
|
});
|
|
87071
87307
|
}
|
|
87072
87308
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
87073
|
-
if (!
|
|
87309
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87074
87310
|
return 0;
|
|
87075
87311
|
return withLock(stateDir, () => {
|
|
87076
87312
|
const all = readAll(stateDir);
|
|
@@ -87088,7 +87324,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
87088
87324
|
});
|
|
87089
87325
|
}
|
|
87090
87326
|
function prune(stateDir, opts = {}) {
|
|
87091
|
-
if (!
|
|
87327
|
+
if (!existsSync71(join73(stateDir, ISSUES_FILE)))
|
|
87092
87328
|
return 0;
|
|
87093
87329
|
return withLock(stateDir, () => {
|
|
87094
87330
|
const all = readAll(stateDir);
|
|
@@ -87118,24 +87354,24 @@ function prune(stateDir, opts = {}) {
|
|
|
87118
87354
|
});
|
|
87119
87355
|
}
|
|
87120
87356
|
function ensureDir(stateDir) {
|
|
87121
|
-
|
|
87357
|
+
mkdirSync37(stateDir, { recursive: true });
|
|
87122
87358
|
}
|
|
87123
87359
|
function writeAll(stateDir, events) {
|
|
87124
|
-
const path5 =
|
|
87360
|
+
const path5 = join73(stateDir, ISSUES_FILE);
|
|
87125
87361
|
sweepOrphanTmpFiles(stateDir);
|
|
87126
87362
|
const tmp = `${path5}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
|
|
87127
87363
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
87128
87364
|
`) + `
|
|
87129
87365
|
`;
|
|
87130
|
-
|
|
87131
|
-
|
|
87366
|
+
writeFileSync20(tmp, body, "utf-8");
|
|
87367
|
+
renameSync17(tmp, path5);
|
|
87132
87368
|
}
|
|
87133
87369
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
87134
87370
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
87135
87371
|
function sweepOrphanTmpFiles(stateDir) {
|
|
87136
87372
|
let entries;
|
|
87137
87373
|
try {
|
|
87138
|
-
entries =
|
|
87374
|
+
entries = readdirSync26(stateDir);
|
|
87139
87375
|
} catch {
|
|
87140
87376
|
return;
|
|
87141
87377
|
}
|
|
@@ -87143,7 +87379,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87143
87379
|
for (const entry of entries) {
|
|
87144
87380
|
if (!entry.startsWith(TMP_PREFIX))
|
|
87145
87381
|
continue;
|
|
87146
|
-
const tmpPath =
|
|
87382
|
+
const tmpPath = join73(stateDir, entry);
|
|
87147
87383
|
try {
|
|
87148
87384
|
const stat = statSync43(tmpPath);
|
|
87149
87385
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -87155,7 +87391,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
87155
87391
|
var LOCK_RETRY_MS = 25;
|
|
87156
87392
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
87157
87393
|
function withLock(stateDir, fn) {
|
|
87158
|
-
const lockPath =
|
|
87394
|
+
const lockPath = join73(stateDir, ISSUES_LOCK);
|
|
87159
87395
|
const startedAt = Date.now();
|
|
87160
87396
|
let fd = null;
|
|
87161
87397
|
while (fd === null) {
|
|
@@ -87190,7 +87426,7 @@ function withLock(stateDir, fn) {
|
|
|
87190
87426
|
function tryStealStaleLock(lockPath) {
|
|
87191
87427
|
let pidStr;
|
|
87192
87428
|
try {
|
|
87193
|
-
pidStr =
|
|
87429
|
+
pidStr = readFileSync64(lockPath, "utf-8").trim();
|
|
87194
87430
|
} catch {
|
|
87195
87431
|
return true;
|
|
87196
87432
|
}
|
|
@@ -87438,20 +87674,20 @@ function relTime(deltaMs) {
|
|
|
87438
87674
|
|
|
87439
87675
|
// src/cli/deps.ts
|
|
87440
87676
|
init_source();
|
|
87441
|
-
import { existsSync as
|
|
87677
|
+
import { existsSync as existsSync74 } from "node:fs";
|
|
87442
87678
|
import { homedir as homedir44 } from "node:os";
|
|
87443
|
-
import { join as
|
|
87679
|
+
import { join as join76, resolve as resolve44 } from "node:path";
|
|
87444
87680
|
|
|
87445
87681
|
// src/deps/python.ts
|
|
87446
87682
|
import { createHash as createHash13 } from "node:crypto";
|
|
87447
87683
|
import {
|
|
87448
|
-
existsSync as
|
|
87449
|
-
mkdirSync as
|
|
87450
|
-
readFileSync as
|
|
87684
|
+
existsSync as existsSync72,
|
|
87685
|
+
mkdirSync as mkdirSync38,
|
|
87686
|
+
readFileSync as readFileSync65,
|
|
87451
87687
|
rmSync as rmSync13,
|
|
87452
|
-
writeFileSync as
|
|
87688
|
+
writeFileSync as writeFileSync21
|
|
87453
87689
|
} from "node:fs";
|
|
87454
|
-
import { dirname as
|
|
87690
|
+
import { dirname as dirname25, join as join74 } from "node:path";
|
|
87455
87691
|
import { homedir as homedir42 } from "node:os";
|
|
87456
87692
|
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
87457
87693
|
|
|
@@ -87464,26 +87700,26 @@ class PythonEnvError extends Error {
|
|
|
87464
87700
|
}
|
|
87465
87701
|
}
|
|
87466
87702
|
function defaultPythonCacheRoot() {
|
|
87467
|
-
return
|
|
87703
|
+
return join74(homedir42(), ".switchroom", "deps", "python");
|
|
87468
87704
|
}
|
|
87469
87705
|
function hashFile(path5) {
|
|
87470
|
-
return createHash13("sha256").update(
|
|
87706
|
+
return createHash13("sha256").update(readFileSync65(path5)).digest("hex");
|
|
87471
87707
|
}
|
|
87472
87708
|
function ensurePythonEnv(opts) {
|
|
87473
87709
|
const { skillName, requirementsPath, force = false } = opts;
|
|
87474
87710
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
87475
87711
|
const hostPython = opts.pythonBin ?? "python3";
|
|
87476
|
-
if (!
|
|
87712
|
+
if (!existsSync72(requirementsPath)) {
|
|
87477
87713
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
87478
87714
|
}
|
|
87479
|
-
const venvDir =
|
|
87480
|
-
const stampPath =
|
|
87481
|
-
const binDir =
|
|
87482
|
-
const pythonBin =
|
|
87483
|
-
const pipBin =
|
|
87715
|
+
const venvDir = join74(cacheRoot, skillName);
|
|
87716
|
+
const stampPath = join74(venvDir, ".requirements.sha256");
|
|
87717
|
+
const binDir = join74(venvDir, "bin");
|
|
87718
|
+
const pythonBin = join74(binDir, "python");
|
|
87719
|
+
const pipBin = join74(binDir, "pip");
|
|
87484
87720
|
const targetHash = hashFile(requirementsPath);
|
|
87485
|
-
if (!force &&
|
|
87486
|
-
const existingHash =
|
|
87721
|
+
if (!force && existsSync72(stampPath) && existsSync72(pythonBin)) {
|
|
87722
|
+
const existingHash = readFileSync65(stampPath, "utf8").trim();
|
|
87487
87723
|
if (existingHash === targetHash) {
|
|
87488
87724
|
return {
|
|
87489
87725
|
skillName,
|
|
@@ -87495,10 +87731,10 @@ function ensurePythonEnv(opts) {
|
|
|
87495
87731
|
};
|
|
87496
87732
|
}
|
|
87497
87733
|
}
|
|
87498
|
-
if (
|
|
87734
|
+
if (existsSync72(venvDir)) {
|
|
87499
87735
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
87500
87736
|
}
|
|
87501
|
-
|
|
87737
|
+
mkdirSync38(dirname25(venvDir), { recursive: true });
|
|
87502
87738
|
try {
|
|
87503
87739
|
execFileSync21(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
87504
87740
|
} catch (err) {
|
|
@@ -87517,7 +87753,7 @@ function ensurePythonEnv(opts) {
|
|
|
87517
87753
|
const e = err;
|
|
87518
87754
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
87519
87755
|
}
|
|
87520
|
-
|
|
87756
|
+
writeFileSync21(stampPath, targetHash + `
|
|
87521
87757
|
`);
|
|
87522
87758
|
return {
|
|
87523
87759
|
skillName,
|
|
@@ -87533,13 +87769,13 @@ function ensurePythonEnv(opts) {
|
|
|
87533
87769
|
import { createHash as createHash14 } from "node:crypto";
|
|
87534
87770
|
import {
|
|
87535
87771
|
copyFileSync as copyFileSync10,
|
|
87536
|
-
existsSync as
|
|
87537
|
-
mkdirSync as
|
|
87538
|
-
readFileSync as
|
|
87772
|
+
existsSync as existsSync73,
|
|
87773
|
+
mkdirSync as mkdirSync39,
|
|
87774
|
+
readFileSync as readFileSync66,
|
|
87539
87775
|
rmSync as rmSync14,
|
|
87540
|
-
writeFileSync as
|
|
87776
|
+
writeFileSync as writeFileSync22
|
|
87541
87777
|
} from "node:fs";
|
|
87542
|
-
import { dirname as
|
|
87778
|
+
import { dirname as dirname26, join as join75 } from "node:path";
|
|
87543
87779
|
import { homedir as homedir43 } from "node:os";
|
|
87544
87780
|
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
87545
87781
|
|
|
@@ -87563,23 +87799,23 @@ var LOCKFILES_FOR = {
|
|
|
87563
87799
|
npm: ["package-lock.json"]
|
|
87564
87800
|
};
|
|
87565
87801
|
function defaultNodeCacheRoot() {
|
|
87566
|
-
return
|
|
87802
|
+
return join75(homedir43(), ".switchroom", "deps", "node");
|
|
87567
87803
|
}
|
|
87568
87804
|
function hashDepInputs(packageJsonPath) {
|
|
87569
|
-
const sourceDir =
|
|
87805
|
+
const sourceDir = dirname26(packageJsonPath);
|
|
87570
87806
|
const hasher = createHash14("sha256");
|
|
87571
87807
|
hasher.update(`package.json
|
|
87572
87808
|
`);
|
|
87573
|
-
hasher.update(
|
|
87809
|
+
hasher.update(readFileSync66(packageJsonPath));
|
|
87574
87810
|
for (const lockName of ALL_LOCKFILES) {
|
|
87575
|
-
const lockPath =
|
|
87576
|
-
if (
|
|
87811
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87812
|
+
if (existsSync73(lockPath)) {
|
|
87577
87813
|
hasher.update(`
|
|
87578
87814
|
`);
|
|
87579
87815
|
hasher.update(lockName);
|
|
87580
87816
|
hasher.update(`
|
|
87581
87817
|
`);
|
|
87582
|
-
hasher.update(
|
|
87818
|
+
hasher.update(readFileSync66(lockPath));
|
|
87583
87819
|
}
|
|
87584
87820
|
}
|
|
87585
87821
|
return hasher.digest("hex");
|
|
@@ -87588,17 +87824,17 @@ function ensureNodeEnv(opts) {
|
|
|
87588
87824
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
87589
87825
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
87590
87826
|
const installer = opts.installer ?? "bun";
|
|
87591
|
-
if (!
|
|
87827
|
+
if (!existsSync73(packageJsonPath)) {
|
|
87592
87828
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
87593
87829
|
}
|
|
87594
|
-
const sourceDir =
|
|
87595
|
-
const envDir =
|
|
87596
|
-
const stampPath =
|
|
87597
|
-
const nodeModulesDir =
|
|
87598
|
-
const binDir =
|
|
87830
|
+
const sourceDir = dirname26(packageJsonPath);
|
|
87831
|
+
const envDir = join75(cacheRoot, skillName);
|
|
87832
|
+
const stampPath = join75(envDir, ".package.sha256");
|
|
87833
|
+
const nodeModulesDir = join75(envDir, "node_modules");
|
|
87834
|
+
const binDir = join75(nodeModulesDir, ".bin");
|
|
87599
87835
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
87600
|
-
if (!force &&
|
|
87601
|
-
const existingHash =
|
|
87836
|
+
if (!force && existsSync73(stampPath) && existsSync73(nodeModulesDir)) {
|
|
87837
|
+
const existingHash = readFileSync66(stampPath, "utf8").trim();
|
|
87602
87838
|
if (existingHash === targetHash) {
|
|
87603
87839
|
return {
|
|
87604
87840
|
skillName,
|
|
@@ -87609,16 +87845,16 @@ function ensureNodeEnv(opts) {
|
|
|
87609
87845
|
};
|
|
87610
87846
|
}
|
|
87611
87847
|
}
|
|
87612
|
-
if (
|
|
87848
|
+
if (existsSync73(envDir)) {
|
|
87613
87849
|
rmSync14(envDir, { recursive: true, force: true });
|
|
87614
87850
|
}
|
|
87615
|
-
|
|
87616
|
-
copyFileSync10(packageJsonPath,
|
|
87851
|
+
mkdirSync39(envDir, { recursive: true });
|
|
87852
|
+
copyFileSync10(packageJsonPath, join75(envDir, "package.json"));
|
|
87617
87853
|
let copiedLockfile = false;
|
|
87618
87854
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
87619
|
-
const lockPath =
|
|
87620
|
-
if (
|
|
87621
|
-
copyFileSync10(lockPath,
|
|
87855
|
+
const lockPath = join75(sourceDir, lockName);
|
|
87856
|
+
if (existsSync73(lockPath)) {
|
|
87857
|
+
copyFileSync10(lockPath, join75(envDir, lockName));
|
|
87622
87858
|
copiedLockfile = true;
|
|
87623
87859
|
}
|
|
87624
87860
|
}
|
|
@@ -87634,7 +87870,7 @@ function ensureNodeEnv(opts) {
|
|
|
87634
87870
|
const e = err;
|
|
87635
87871
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
87636
87872
|
}
|
|
87637
|
-
|
|
87873
|
+
writeFileSync22(stampPath, targetHash + `
|
|
87638
87874
|
`);
|
|
87639
87875
|
return {
|
|
87640
87876
|
skillName,
|
|
@@ -87653,22 +87889,22 @@ function registerDepsCommand(program3) {
|
|
|
87653
87889
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
87654
87890
|
deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
|
|
87655
87891
|
const skillsRoot = builtinSkillsRoot();
|
|
87656
|
-
if (!
|
|
87892
|
+
if (!existsSync74(skillsRoot)) {
|
|
87657
87893
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
87658
87894
|
process.exit(1);
|
|
87659
87895
|
}
|
|
87660
|
-
const skillDir =
|
|
87661
|
-
if (!
|
|
87896
|
+
const skillDir = join76(skillsRoot, skill);
|
|
87897
|
+
if (!existsSync74(skillDir)) {
|
|
87662
87898
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
87663
87899
|
process.exit(1);
|
|
87664
87900
|
}
|
|
87665
|
-
const requirementsPath =
|
|
87666
|
-
const packageJsonPath =
|
|
87667
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
87668
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
87901
|
+
const requirementsPath = join76(skillDir, "requirements.txt");
|
|
87902
|
+
const packageJsonPath = join76(skillDir, "package.json");
|
|
87903
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync74(requirementsPath));
|
|
87904
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync74(packageJsonPath));
|
|
87669
87905
|
let did = 0;
|
|
87670
87906
|
if (wantPython) {
|
|
87671
|
-
if (!
|
|
87907
|
+
if (!existsSync74(requirementsPath)) {
|
|
87672
87908
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
87673
87909
|
process.exit(1);
|
|
87674
87910
|
}
|
|
@@ -87692,7 +87928,7 @@ function registerDepsCommand(program3) {
|
|
|
87692
87928
|
}
|
|
87693
87929
|
}
|
|
87694
87930
|
if (wantNode) {
|
|
87695
|
-
if (!
|
|
87931
|
+
if (!existsSync74(packageJsonPath)) {
|
|
87696
87932
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
87697
87933
|
process.exit(1);
|
|
87698
87934
|
}
|
|
@@ -87725,7 +87961,7 @@ function registerDepsCommand(program3) {
|
|
|
87725
87961
|
// src/cli/workspace.ts
|
|
87726
87962
|
init_helpers();
|
|
87727
87963
|
init_loader();
|
|
87728
|
-
import { existsSync as
|
|
87964
|
+
import { existsSync as existsSync75 } from "node:fs";
|
|
87729
87965
|
import { resolve as resolve45, sep as sep3 } from "node:path";
|
|
87730
87966
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
87731
87967
|
|
|
@@ -88502,7 +88738,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88502
88738
|
if (!dir)
|
|
88503
88739
|
return;
|
|
88504
88740
|
const gitDir = resolve45(dir, ".git");
|
|
88505
|
-
if (!
|
|
88741
|
+
if (!existsSync75(gitDir)) {
|
|
88506
88742
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
88507
88743
|
`);
|
|
88508
88744
|
return;
|
|
@@ -88556,7 +88792,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
88556
88792
|
if (!dir)
|
|
88557
88793
|
return;
|
|
88558
88794
|
const gitDir = resolve45(dir, ".git");
|
|
88559
|
-
if (!
|
|
88795
|
+
if (!existsSync75(gitDir)) {
|
|
88560
88796
|
process.stdout.write(`Workspace is not a git repository.
|
|
88561
88797
|
`);
|
|
88562
88798
|
return;
|
|
@@ -88581,7 +88817,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
88581
88817
|
const agentsDir = resolveAgentsDir(config);
|
|
88582
88818
|
const agentDir = resolve45(agentsDir, agentName);
|
|
88583
88819
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
88584
|
-
if (!
|
|
88820
|
+
if (!existsSync75(dir)) {
|
|
88585
88821
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
88586
88822
|
`);
|
|
88587
88823
|
return;
|
|
@@ -88617,8 +88853,8 @@ function safeParseInt(value, fallback) {
|
|
|
88617
88853
|
init_helpers();
|
|
88618
88854
|
init_loader();
|
|
88619
88855
|
init_merge();
|
|
88620
|
-
import { copyFileSync as copyFileSync11, existsSync as
|
|
88621
|
-
import { join as
|
|
88856
|
+
import { copyFileSync as copyFileSync11, existsSync as existsSync76, readFileSync as readFileSync67, writeFileSync as writeFileSync23 } from "node:fs";
|
|
88857
|
+
import { join as join77, resolve as resolve46 } from "node:path";
|
|
88622
88858
|
init_scaffold();
|
|
88623
88859
|
init_profiles();
|
|
88624
88860
|
init_schema();
|
|
@@ -88635,7 +88871,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88635
88871
|
const agentsDir = resolveAgentsDir(config);
|
|
88636
88872
|
const agentDir = resolve46(agentsDir, agentName);
|
|
88637
88873
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88638
|
-
if (!
|
|
88874
|
+
if (!existsSync76(workspaceDir)) {
|
|
88639
88875
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
88640
88876
|
process.exit(1);
|
|
88641
88877
|
}
|
|
@@ -88644,7 +88880,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
88644
88880
|
profileName,
|
|
88645
88881
|
profilePath,
|
|
88646
88882
|
workspaceDir,
|
|
88647
|
-
soulPath:
|
|
88883
|
+
soulPath: join77(workspaceDir, "SOUL.md"),
|
|
88648
88884
|
soul: merged.soul
|
|
88649
88885
|
};
|
|
88650
88886
|
}
|
|
@@ -88661,11 +88897,11 @@ function registerSoulCommand(program3) {
|
|
|
88661
88897
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
88662
88898
|
if (!t)
|
|
88663
88899
|
return;
|
|
88664
|
-
if (!
|
|
88900
|
+
if (!existsSync76(t.soulPath)) {
|
|
88665
88901
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
88666
88902
|
process.exit(1);
|
|
88667
88903
|
}
|
|
88668
|
-
process.stdout.write(
|
|
88904
|
+
process.stdout.write(readFileSync67(t.soulPath, "utf-8"));
|
|
88669
88905
|
}));
|
|
88670
88906
|
cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
|
|
88671
88907
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -88676,7 +88912,7 @@ function registerSoulCommand(program3) {
|
|
|
88676
88912
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
88677
88913
|
process.exit(1);
|
|
88678
88914
|
}
|
|
88679
|
-
const exists =
|
|
88915
|
+
const exists = existsSync76(t.soulPath);
|
|
88680
88916
|
if (exists && !opts.yes) {
|
|
88681
88917
|
if (!isInteractive()) {
|
|
88682
88918
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -88691,12 +88927,12 @@ function registerSoulCommand(program3) {
|
|
|
88691
88927
|
let backupPath;
|
|
88692
88928
|
if (exists) {
|
|
88693
88929
|
backupPath = `${t.soulPath}.bak`;
|
|
88694
|
-
if (
|
|
88930
|
+
if (existsSync76(backupPath)) {
|
|
88695
88931
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
88696
88932
|
}
|
|
88697
88933
|
copyFileSync11(t.soulPath, backupPath);
|
|
88698
88934
|
}
|
|
88699
|
-
|
|
88935
|
+
writeFileSync23(t.soulPath, content, "utf-8");
|
|
88700
88936
|
if (backupPath) {
|
|
88701
88937
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
88702
88938
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -88710,8 +88946,8 @@ function registerSoulCommand(program3) {
|
|
|
88710
88946
|
// src/cli/debug.ts
|
|
88711
88947
|
init_helpers();
|
|
88712
88948
|
init_loader();
|
|
88713
|
-
import { existsSync as
|
|
88714
|
-
import { resolve as resolve47, join as
|
|
88949
|
+
import { existsSync as existsSync77, readFileSync as readFileSync68, readdirSync as readdirSync27, statSync as statSync44 } from "node:fs";
|
|
88950
|
+
import { resolve as resolve47, join as join78 } from "node:path";
|
|
88715
88951
|
import { createHash as createHash15 } from "node:crypto";
|
|
88716
88952
|
init_merge();
|
|
88717
88953
|
init_hindsight2();
|
|
@@ -88722,11 +88958,11 @@ function estimateTokens(bytes) {
|
|
|
88722
88958
|
return Math.round(bytes / 3.7);
|
|
88723
88959
|
}
|
|
88724
88960
|
function readMcpServerNames(agentDir) {
|
|
88725
|
-
const mcpPath =
|
|
88726
|
-
if (!
|
|
88961
|
+
const mcpPath = join78(agentDir, ".mcp.json");
|
|
88962
|
+
if (!existsSync77(mcpPath))
|
|
88727
88963
|
return [];
|
|
88728
88964
|
try {
|
|
88729
|
-
const parsed = JSON.parse(
|
|
88965
|
+
const parsed = JSON.parse(readFileSync68(mcpPath, "utf-8"));
|
|
88730
88966
|
return Object.keys(parsed.mcpServers ?? {});
|
|
88731
88967
|
} catch {
|
|
88732
88968
|
return null;
|
|
@@ -88736,18 +88972,18 @@ function sha256(content) {
|
|
|
88736
88972
|
return createHash15("sha256").update(content).digest("hex").slice(0, 16);
|
|
88737
88973
|
}
|
|
88738
88974
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
88739
|
-
const projectsDir =
|
|
88740
|
-
if (!
|
|
88975
|
+
const projectsDir = join78(claudeConfigDir, "projects");
|
|
88976
|
+
if (!existsSync77(projectsDir))
|
|
88741
88977
|
return;
|
|
88742
88978
|
try {
|
|
88743
|
-
const entries =
|
|
88979
|
+
const entries = readdirSync27(projectsDir, { withFileTypes: true });
|
|
88744
88980
|
let latest;
|
|
88745
88981
|
for (const entry of entries) {
|
|
88746
88982
|
if (!entry.isDirectory())
|
|
88747
88983
|
continue;
|
|
88748
|
-
const projectPath =
|
|
88749
|
-
const transcriptPath =
|
|
88750
|
-
if (!
|
|
88984
|
+
const projectPath = join78(projectsDir, entry.name);
|
|
88985
|
+
const transcriptPath = join78(projectPath, "transcript.jsonl");
|
|
88986
|
+
if (!existsSync77(transcriptPath))
|
|
88751
88987
|
continue;
|
|
88752
88988
|
const stat3 = statSync44(transcriptPath);
|
|
88753
88989
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -88761,7 +88997,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
88761
88997
|
}
|
|
88762
88998
|
function extractLatestUserMessage(transcriptPath) {
|
|
88763
88999
|
try {
|
|
88764
|
-
const content =
|
|
89000
|
+
const content = readFileSync68(transcriptPath, "utf-8");
|
|
88765
89001
|
const lines = content.trim().split(`
|
|
88766
89002
|
`).filter(Boolean);
|
|
88767
89003
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -88810,16 +89046,16 @@ function registerDebugCommand(program3) {
|
|
|
88810
89046
|
}
|
|
88811
89047
|
const agentsDir = resolveAgentsDir(config);
|
|
88812
89048
|
const agentDir = resolve47(agentsDir, agentName);
|
|
88813
|
-
if (!
|
|
89049
|
+
if (!existsSync77(agentDir)) {
|
|
88814
89050
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
88815
89051
|
process.exit(1);
|
|
88816
89052
|
}
|
|
88817
89053
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
88818
|
-
const claudeConfigDir =
|
|
88819
|
-
const claudeMdPath =
|
|
88820
|
-
const soulMdPath =
|
|
88821
|
-
const workspaceSoulMdPath =
|
|
88822
|
-
const handoffPath =
|
|
89054
|
+
const claudeConfigDir = join78(agentDir, ".claude");
|
|
89055
|
+
const claudeMdPath = join78(agentDir, "CLAUDE.md");
|
|
89056
|
+
const soulMdPath = join78(agentDir, "SOUL.md");
|
|
89057
|
+
const workspaceSoulMdPath = join78(workspaceDir, "SOUL.md");
|
|
89058
|
+
const handoffPath = join78(agentDir, ".handoff.md");
|
|
88823
89059
|
const lastN = parseInt(opts.last, 10);
|
|
88824
89060
|
if (isNaN(lastN) || lastN < 1) {
|
|
88825
89061
|
console.error("--last must be a positive integer");
|
|
@@ -88865,7 +89101,7 @@ function registerDebugCommand(program3) {
|
|
|
88865
89101
|
}
|
|
88866
89102
|
console.log(`=== Append System Prompt (per-session) ===
|
|
88867
89103
|
`);
|
|
88868
|
-
const handoffContent =
|
|
89104
|
+
const handoffContent = existsSync77(handoffPath) ? readFileSync68(handoffPath, "utf-8") : "";
|
|
88869
89105
|
if (handoffContent.trim().length > 0) {
|
|
88870
89106
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
88871
89107
|
console.log(handoffContent);
|
|
@@ -88876,7 +89112,7 @@ function registerDebugCommand(program3) {
|
|
|
88876
89112
|
}
|
|
88877
89113
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
88878
89114
|
`);
|
|
88879
|
-
const claudeMdContent =
|
|
89115
|
+
const claudeMdContent = existsSync77(claudeMdPath) ? readFileSync68(claudeMdPath, "utf-8") : "";
|
|
88880
89116
|
if (claudeMdContent.trim().length > 0) {
|
|
88881
89117
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
88882
89118
|
console.log(claudeMdContent);
|
|
@@ -88887,7 +89123,7 @@ function registerDebugCommand(program3) {
|
|
|
88887
89123
|
}
|
|
88888
89124
|
console.log(`=== Persona (SOUL.md) ===
|
|
88889
89125
|
`);
|
|
88890
|
-
const soulMdContent =
|
|
89126
|
+
const soulMdContent = existsSync77(soulMdPath) ? readFileSync68(soulMdPath, "utf-8") : existsSync77(workspaceSoulMdPath) ? readFileSync68(workspaceSoulMdPath, "utf-8") : "";
|
|
88891
89127
|
if (soulMdContent.trim().length > 0) {
|
|
88892
89128
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
88893
89129
|
console.log(soulMdContent);
|
|
@@ -88948,11 +89184,11 @@ function registerDebugCommand(program3) {
|
|
|
88948
89184
|
const soulMdBytes = soulMdContent.length;
|
|
88949
89185
|
const perTurnBytes = dynamicResult.concatenated.length;
|
|
88950
89186
|
const userBytes = userMessage?.text.length ?? 0;
|
|
88951
|
-
const fleetDir =
|
|
88952
|
-
const fleetInvPath =
|
|
88953
|
-
const fleetClaudePath =
|
|
88954
|
-
const fleetInvBytes =
|
|
88955
|
-
const fleetClaudeBytes =
|
|
89187
|
+
const fleetDir = join78(agentsDir, "..", "fleet");
|
|
89188
|
+
const fleetInvPath = join78(fleetDir, "switchroom-invariants.md");
|
|
89189
|
+
const fleetClaudePath = join78(fleetDir, "CLAUDE.md");
|
|
89190
|
+
const fleetInvBytes = existsSync77(fleetInvPath) ? readFileSync68(fleetInvPath, "utf-8").length : 0;
|
|
89191
|
+
const fleetClaudeBytes = existsSync77(fleetClaudePath) ? readFileSync68(fleetClaudePath, "utf-8").length : 0;
|
|
88956
89192
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
88957
89193
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
88958
89194
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -88985,44 +89221,44 @@ init_source();
|
|
|
88985
89221
|
|
|
88986
89222
|
// src/worktree/claim.ts
|
|
88987
89223
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
88988
|
-
import { closeSync as closeSync13, mkdirSync as
|
|
88989
|
-
import { join as
|
|
89224
|
+
import { closeSync as closeSync13, mkdirSync as mkdirSync41, openSync as openSync13, existsSync as existsSync79, unlinkSync as unlinkSync17 } from "node:fs";
|
|
89225
|
+
import { join as join80, resolve as resolve49 } from "node:path";
|
|
88990
89226
|
import { homedir as homedir46 } from "node:os";
|
|
88991
89227
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
88992
89228
|
|
|
88993
89229
|
// src/worktree/registry.ts
|
|
88994
89230
|
import {
|
|
88995
|
-
mkdirSync as
|
|
88996
|
-
writeFileSync as
|
|
88997
|
-
readFileSync as
|
|
88998
|
-
readdirSync as
|
|
89231
|
+
mkdirSync as mkdirSync40,
|
|
89232
|
+
writeFileSync as writeFileSync24,
|
|
89233
|
+
readFileSync as readFileSync69,
|
|
89234
|
+
readdirSync as readdirSync28,
|
|
88999
89235
|
unlinkSync as unlinkSync16,
|
|
89000
|
-
existsSync as
|
|
89001
|
-
renameSync as
|
|
89236
|
+
existsSync as existsSync78,
|
|
89237
|
+
renameSync as renameSync18
|
|
89002
89238
|
} from "node:fs";
|
|
89003
|
-
import { join as
|
|
89239
|
+
import { join as join79, resolve as resolve48 } from "node:path";
|
|
89004
89240
|
import { homedir as homedir45 } from "node:os";
|
|
89005
89241
|
function registryDir() {
|
|
89006
|
-
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
89242
|
+
return resolve48(process.env.SWITCHROOM_WORKTREE_DIR ?? join79(homedir45(), ".switchroom", "worktrees"));
|
|
89007
89243
|
}
|
|
89008
89244
|
function recordPath(id) {
|
|
89009
|
-
return
|
|
89245
|
+
return join79(registryDir(), `${id}.json`);
|
|
89010
89246
|
}
|
|
89011
89247
|
function ensureDir2() {
|
|
89012
|
-
|
|
89248
|
+
mkdirSync40(registryDir(), { recursive: true });
|
|
89013
89249
|
}
|
|
89014
89250
|
function writeRecord(record2) {
|
|
89015
89251
|
ensureDir2();
|
|
89016
89252
|
const target = recordPath(record2.id);
|
|
89017
89253
|
const tmp = `${target}.tmp${process.pid}`;
|
|
89018
|
-
|
|
89254
|
+
writeFileSync24(tmp, JSON.stringify(record2, null, 2) + `
|
|
89019
89255
|
`, { mode: 384 });
|
|
89020
|
-
|
|
89256
|
+
renameSync18(tmp, target);
|
|
89021
89257
|
}
|
|
89022
89258
|
function readRecord(id) {
|
|
89023
89259
|
const path8 = recordPath(id);
|
|
89024
89260
|
try {
|
|
89025
|
-
const raw =
|
|
89261
|
+
const raw = readFileSync69(path8, "utf8");
|
|
89026
89262
|
return JSON.parse(raw);
|
|
89027
89263
|
} catch {
|
|
89028
89264
|
return null;
|
|
@@ -89038,7 +89274,7 @@ function listRecords() {
|
|
|
89038
89274
|
ensureDir2();
|
|
89039
89275
|
const dir = registryDir();
|
|
89040
89276
|
const records = [];
|
|
89041
|
-
for (const entry of
|
|
89277
|
+
for (const entry of readdirSync28(dir)) {
|
|
89042
89278
|
if (!entry.endsWith(".json"))
|
|
89043
89279
|
continue;
|
|
89044
89280
|
const id = entry.slice(0, -5);
|
|
@@ -89055,9 +89291,9 @@ function countByRepo(repoPath) {
|
|
|
89055
89291
|
// src/worktree/claim.ts
|
|
89056
89292
|
function acquireRepoLock(repoPath) {
|
|
89057
89293
|
const lockDir = registryDir();
|
|
89058
|
-
|
|
89294
|
+
mkdirSync41(lockDir, { recursive: true });
|
|
89059
89295
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
89060
|
-
const lockPath =
|
|
89296
|
+
const lockPath = join80(lockDir, `.lock-${lockName}`);
|
|
89061
89297
|
const deadline = Date.now() + 5000;
|
|
89062
89298
|
let fd = null;
|
|
89063
89299
|
while (fd === null) {
|
|
@@ -89084,7 +89320,7 @@ function acquireRepoLock(repoPath) {
|
|
|
89084
89320
|
}
|
|
89085
89321
|
var DEFAULT_CONCURRENCY = 5;
|
|
89086
89322
|
function worktreesBaseDir() {
|
|
89087
|
-
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
89323
|
+
return resolve49(process.env.SWITCHROOM_WORKTREE_BASE ?? join80(homedir46(), ".switchroom", "worktree-checkouts"));
|
|
89088
89324
|
}
|
|
89089
89325
|
function shortId() {
|
|
89090
89326
|
return randomBytes13(4).toString("hex");
|
|
@@ -89106,12 +89342,12 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
89106
89342
|
}
|
|
89107
89343
|
function expandHome(p) {
|
|
89108
89344
|
if (p.startsWith("~/"))
|
|
89109
|
-
return
|
|
89345
|
+
return join80(homedir46(), p.slice(2));
|
|
89110
89346
|
return p;
|
|
89111
89347
|
}
|
|
89112
89348
|
async function claimWorktree(input, codeRepos) {
|
|
89113
89349
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
89114
|
-
if (!
|
|
89350
|
+
if (!existsSync79(repoPath)) {
|
|
89115
89351
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
89116
89352
|
}
|
|
89117
89353
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -89133,8 +89369,8 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89133
89369
|
const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
|
|
89134
89370
|
branch = `task/${taskSuffix}-${id}`;
|
|
89135
89371
|
const baseDir = worktreesBaseDir();
|
|
89136
|
-
|
|
89137
|
-
worktreePath =
|
|
89372
|
+
mkdirSync41(baseDir, { recursive: true });
|
|
89373
|
+
worktreePath = join80(baseDir, `${id}-${taskSuffix}`);
|
|
89138
89374
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
89139
89375
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
89140
89376
|
const now = new Date().toISOString();
|
|
@@ -89167,7 +89403,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
89167
89403
|
|
|
89168
89404
|
// src/worktree/release.ts
|
|
89169
89405
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
89170
|
-
import { existsSync as
|
|
89406
|
+
import { existsSync as existsSync80 } from "node:fs";
|
|
89171
89407
|
function releaseWorktree(input) {
|
|
89172
89408
|
const { id } = input;
|
|
89173
89409
|
const record2 = readRecord(id);
|
|
@@ -89175,7 +89411,7 @@ function releaseWorktree(input) {
|
|
|
89175
89411
|
return { released: true };
|
|
89176
89412
|
}
|
|
89177
89413
|
let gitSuccess = true;
|
|
89178
|
-
if (
|
|
89414
|
+
if (existsSync80(record2.path)) {
|
|
89179
89415
|
try {
|
|
89180
89416
|
execFileSync24("git", ["worktree", "remove", "--force", record2.path], {
|
|
89181
89417
|
cwd: record2.repo,
|
|
@@ -89214,7 +89450,7 @@ function listWorktrees() {
|
|
|
89214
89450
|
|
|
89215
89451
|
// src/worktree/reaper.ts
|
|
89216
89452
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
89217
|
-
import { existsSync as
|
|
89453
|
+
import { existsSync as existsSync81 } from "node:fs";
|
|
89218
89454
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
89219
89455
|
function reapSkipReasonText(action) {
|
|
89220
89456
|
switch (action) {
|
|
@@ -89275,7 +89511,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
89275
89511
|
const plan = [];
|
|
89276
89512
|
for (const record2 of listRecords()) {
|
|
89277
89513
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
89278
|
-
const worktreeExists =
|
|
89514
|
+
const worktreeExists = existsSync81(record2.path);
|
|
89279
89515
|
if (!worktreeExists) {
|
|
89280
89516
|
plan.push({
|
|
89281
89517
|
record: record2,
|
|
@@ -89356,16 +89592,16 @@ function runReaper(nowMs, deps = {}) {
|
|
|
89356
89592
|
// src/worktree/gc.ts
|
|
89357
89593
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
89358
89594
|
import {
|
|
89359
|
-
existsSync as
|
|
89360
|
-
readFileSync as
|
|
89361
|
-
readdirSync as
|
|
89595
|
+
existsSync as existsSync82,
|
|
89596
|
+
readFileSync as readFileSync70,
|
|
89597
|
+
readdirSync as readdirSync29,
|
|
89362
89598
|
statSync as statSync45,
|
|
89363
|
-
renameSync as
|
|
89364
|
-
mkdirSync as
|
|
89599
|
+
renameSync as renameSync19,
|
|
89600
|
+
mkdirSync as mkdirSync42,
|
|
89365
89601
|
rmSync as rmSync15
|
|
89366
89602
|
} from "node:fs";
|
|
89367
89603
|
import { homedir as homedir47 } from "node:os";
|
|
89368
|
-
import { join as
|
|
89604
|
+
import { join as join81, resolve as resolve50 } from "node:path";
|
|
89369
89605
|
function parseGitdirPointer(dotGitFileContents) {
|
|
89370
89606
|
const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
|
|
89371
89607
|
return m ? m[1] : null;
|
|
@@ -89480,17 +89716,17 @@ function defaultPrSignal(repo, branch, exec) {
|
|
|
89480
89716
|
}
|
|
89481
89717
|
}
|
|
89482
89718
|
function trashRoot() {
|
|
89483
|
-
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ??
|
|
89719
|
+
return resolve50(process.env.SWITCHROOM_WORKTREE_TRASH ?? join81(homedir47(), ".switchroom", "worktree-gc-trash"));
|
|
89484
89720
|
}
|
|
89485
89721
|
function planGc(roots, deps = {}) {
|
|
89486
|
-
const exists = deps.existsSync ??
|
|
89487
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89488
|
-
const readFile4 = deps.readFile ?? ((p) =>
|
|
89722
|
+
const exists = deps.existsSync ?? existsSync82;
|
|
89723
|
+
const readDir = deps.readDir ?? ((p) => readdirSync29(p));
|
|
89724
|
+
const readFile4 = deps.readFile ?? ((p) => readFileSync70(p, "utf8"));
|
|
89489
89725
|
const stat3 = deps.stat ?? ((p) => statSync45(p));
|
|
89490
89726
|
const exec = deps.exec ?? defaultExec;
|
|
89491
89727
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
89492
89728
|
const stamp = deps.dateStamp ?? "undated";
|
|
89493
|
-
const trash =
|
|
89729
|
+
const trash = join81(trashRoot(), stamp);
|
|
89494
89730
|
let claimed;
|
|
89495
89731
|
try {
|
|
89496
89732
|
claimed = new Set(listRecords().map((r) => resolve50(r.path)));
|
|
@@ -89526,10 +89762,10 @@ function planGc(roots, deps = {}) {
|
|
|
89526
89762
|
continue;
|
|
89527
89763
|
}
|
|
89528
89764
|
for (const name of entries) {
|
|
89529
|
-
const dir =
|
|
89765
|
+
const dir = join81(root, name);
|
|
89530
89766
|
if (isEphemeralPath(dir))
|
|
89531
89767
|
continue;
|
|
89532
|
-
const dotGit =
|
|
89768
|
+
const dotGit = join81(dir, ".git");
|
|
89533
89769
|
if (!exists(dotGit))
|
|
89534
89770
|
continue;
|
|
89535
89771
|
let st;
|
|
@@ -89558,7 +89794,7 @@ function planGc(roots, deps = {}) {
|
|
|
89558
89794
|
ownerRepos.add(repoRoot);
|
|
89559
89795
|
if (exists(ptr))
|
|
89560
89796
|
continue;
|
|
89561
|
-
orphans.push({ dir, owner: repoRoot, dest:
|
|
89797
|
+
orphans.push({ dir, owner: repoRoot, dest: join81(trash, name) });
|
|
89562
89798
|
}
|
|
89563
89799
|
}
|
|
89564
89800
|
const registered = [];
|
|
@@ -89613,10 +89849,10 @@ function planGc(roots, deps = {}) {
|
|
|
89613
89849
|
}
|
|
89614
89850
|
function applyGc(plan, deps = {}) {
|
|
89615
89851
|
const exec = deps.exec ?? defaultExec;
|
|
89616
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
89852
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync42(p, { recursive: true }));
|
|
89617
89853
|
const move = deps.move ?? ((src, dest) => {
|
|
89618
89854
|
try {
|
|
89619
|
-
|
|
89855
|
+
renameSync19(src, dest);
|
|
89620
89856
|
} catch {
|
|
89621
89857
|
exec("mv", [src, dest]);
|
|
89622
89858
|
}
|
|
@@ -89662,14 +89898,14 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
89662
89898
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
89663
89899
|
}
|
|
89664
89900
|
function listTrashEntries(nowMs, deps = {}) {
|
|
89665
|
-
const exists = deps.existsSync ??
|
|
89666
|
-
const readDir = deps.readDir ?? ((p) =>
|
|
89901
|
+
const exists = deps.existsSync ?? existsSync82;
|
|
89902
|
+
const readDir = deps.readDir ?? ((p) => readdirSync29(p));
|
|
89667
89903
|
const root = trashRoot();
|
|
89668
89904
|
if (!exists(root))
|
|
89669
89905
|
return [];
|
|
89670
89906
|
const out = [];
|
|
89671
89907
|
for (const stamp of readDir(root)) {
|
|
89672
|
-
const stampDir =
|
|
89908
|
+
const stampDir = join81(root, stamp);
|
|
89673
89909
|
let names;
|
|
89674
89910
|
try {
|
|
89675
89911
|
names = readDir(stampDir);
|
|
@@ -89677,7 +89913,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
89677
89913
|
continue;
|
|
89678
89914
|
}
|
|
89679
89915
|
for (const name of names) {
|
|
89680
|
-
const p =
|
|
89916
|
+
const p = join81(stampDir, name);
|
|
89681
89917
|
let mtimeMs = nowMs;
|
|
89682
89918
|
try {
|
|
89683
89919
|
mtimeMs = statSync45(p).mtimeMs;
|
|
@@ -89701,7 +89937,7 @@ function purgeTrash(paths) {
|
|
|
89701
89937
|
return { deleted, errors: errors2 };
|
|
89702
89938
|
}
|
|
89703
89939
|
function defaultRoots() {
|
|
89704
|
-
return [
|
|
89940
|
+
return [join81(homedir47(), "code")];
|
|
89705
89941
|
}
|
|
89706
89942
|
|
|
89707
89943
|
// src/cli/worktree.ts
|
|
@@ -89924,12 +90160,12 @@ init_drive();
|
|
|
89924
90160
|
init_scaffold_integration();
|
|
89925
90161
|
import {
|
|
89926
90162
|
chmodSync as chmodSync11,
|
|
89927
|
-
mkdirSync as
|
|
89928
|
-
readdirSync as
|
|
90163
|
+
mkdirSync as mkdirSync43,
|
|
90164
|
+
readdirSync as readdirSync30,
|
|
89929
90165
|
rmSync as rmSync16,
|
|
89930
|
-
writeFileSync as
|
|
90166
|
+
writeFileSync as writeFileSync25
|
|
89931
90167
|
} from "node:fs";
|
|
89932
|
-
import { join as
|
|
90168
|
+
import { join as join82 } from "node:path";
|
|
89933
90169
|
function encodeCredentialsFilename(email) {
|
|
89934
90170
|
const SAFE = new Set([
|
|
89935
90171
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -90119,17 +90355,17 @@ function resolveCredentialsDir(env2) {
|
|
|
90119
90355
|
if (explicit && explicit.length > 0)
|
|
90120
90356
|
return explicit;
|
|
90121
90357
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
90122
|
-
return
|
|
90358
|
+
return join82(stateBase, "google-workspace-mcp", "credentials");
|
|
90123
90359
|
}
|
|
90124
90360
|
function writeSeedFile(dir, email, seed) {
|
|
90125
|
-
|
|
90361
|
+
mkdirSync43(dir, { recursive: true, mode: 448 });
|
|
90126
90362
|
chmodSync11(dir, 448);
|
|
90127
|
-
for (const name of
|
|
90128
|
-
rmSync16(
|
|
90363
|
+
for (const name of readdirSync30(dir)) {
|
|
90364
|
+
rmSync16(join82(dir, name), { force: true, recursive: true });
|
|
90129
90365
|
}
|
|
90130
90366
|
const filename = encodeCredentialsFilename(email);
|
|
90131
|
-
const filePath =
|
|
90132
|
-
|
|
90367
|
+
const filePath = join82(dir, filename);
|
|
90368
|
+
writeFileSync25(filePath, JSON.stringify(seed), { mode: 384 });
|
|
90133
90369
|
chmodSync11(filePath, 384);
|
|
90134
90370
|
return filePath;
|
|
90135
90371
|
}
|
|
@@ -90287,8 +90523,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
90287
90523
|
// src/cli/m365-mcp-launcher.ts
|
|
90288
90524
|
init_scaffold_integration();
|
|
90289
90525
|
import { spawn as spawn5 } from "node:child_process";
|
|
90290
|
-
import { writeFileSync as
|
|
90291
|
-
import { dirname as
|
|
90526
|
+
import { writeFileSync as writeFileSync26, mkdirSync as mkdirSync44 } from "node:fs";
|
|
90527
|
+
import { dirname as dirname27, join as join83 } from "node:path";
|
|
90292
90528
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
90293
90529
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
90294
90530
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -90324,8 +90560,8 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
90324
90560
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
90325
90561
|
const path8 = heartbeatPath(agentName, account);
|
|
90326
90562
|
try {
|
|
90327
|
-
|
|
90328
|
-
|
|
90563
|
+
mkdirSync44(dirname27(path8), { recursive: true });
|
|
90564
|
+
writeFileSync26(path8, JSON.stringify(data, null, 2), { mode: 420 });
|
|
90329
90565
|
} catch {}
|
|
90330
90566
|
}
|
|
90331
90567
|
function heartbeatPath(agentName, account) {
|
|
@@ -90333,7 +90569,7 @@ function heartbeatPath(agentName, account) {
|
|
|
90333
90569
|
const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
|
|
90334
90570
|
if (override) {
|
|
90335
90571
|
const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
|
|
90336
|
-
return
|
|
90572
|
+
return join83(override, `${base}.heartbeat.json`);
|
|
90337
90573
|
}
|
|
90338
90574
|
return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
|
|
90339
90575
|
}
|
|
@@ -90565,8 +90801,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
90565
90801
|
// src/cli/notion-mcp-launcher.ts
|
|
90566
90802
|
init_scaffold_integration();
|
|
90567
90803
|
import { spawn as spawn6 } from "node:child_process";
|
|
90568
|
-
import { existsSync as
|
|
90569
|
-
import { dirname as
|
|
90804
|
+
import { existsSync as existsSync83, mkdirSync as mkdirSync45, writeFileSync as writeFileSync27 } from "node:fs";
|
|
90805
|
+
import { dirname as dirname28 } from "node:path";
|
|
90570
90806
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
90571
90807
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
90572
90808
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -90576,10 +90812,10 @@ function buildNotionMcpArgs(opts) {
|
|
|
90576
90812
|
}
|
|
90577
90813
|
function defaultWriteHeartbeat(path8, contents) {
|
|
90578
90814
|
try {
|
|
90579
|
-
const dir =
|
|
90580
|
-
if (!
|
|
90581
|
-
|
|
90582
|
-
|
|
90815
|
+
const dir = dirname28(path8);
|
|
90816
|
+
if (!existsSync83(dir))
|
|
90817
|
+
mkdirSync45(dir, { recursive: true });
|
|
90818
|
+
writeFileSync27(path8, contents);
|
|
90583
90819
|
} catch {}
|
|
90584
90820
|
}
|
|
90585
90821
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -90689,9 +90925,9 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
90689
90925
|
|
|
90690
90926
|
// src/cli/hindsight-mcp-shim.ts
|
|
90691
90927
|
init_hindsight();
|
|
90692
|
-
import { mkdirSync as
|
|
90928
|
+
import { mkdirSync as mkdirSync46, readFileSync as readFileSync71, renameSync as renameSync20, writeFileSync as writeFileSync28 } from "node:fs";
|
|
90693
90929
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
90694
|
-
import { join as
|
|
90930
|
+
import { join as join84 } from "node:path";
|
|
90695
90931
|
import { createInterface as createInterface6 } from "node:readline";
|
|
90696
90932
|
var SHIM_SUPPORTED_PROTOCOL_VERSIONS = [
|
|
90697
90933
|
"2025-06-18",
|
|
@@ -90914,22 +91150,22 @@ class HindsightShim {
|
|
|
90914
91150
|
`));
|
|
90915
91151
|
}
|
|
90916
91152
|
get cachePath() {
|
|
90917
|
-
return
|
|
91153
|
+
return join84(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
|
|
90918
91154
|
}
|
|
90919
91155
|
writeCache(result) {
|
|
90920
91156
|
try {
|
|
90921
|
-
|
|
90922
|
-
const tmp =
|
|
90923
|
-
|
|
91157
|
+
mkdirSync46(this.opts.cacheDir, { recursive: true });
|
|
91158
|
+
const tmp = join84(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
91159
|
+
writeFileSync28(tmp, JSON.stringify(result, null, 2) + `
|
|
90924
91160
|
`);
|
|
90925
|
-
|
|
91161
|
+
renameSync20(tmp, this.cachePath);
|
|
90926
91162
|
} catch (err) {
|
|
90927
91163
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
90928
91164
|
}
|
|
90929
91165
|
}
|
|
90930
91166
|
readCache() {
|
|
90931
91167
|
try {
|
|
90932
|
-
const parsed = JSON.parse(
|
|
91168
|
+
const parsed = JSON.parse(readFileSync71(this.cachePath, "utf-8"));
|
|
90933
91169
|
if (Array.isArray(parsed.tools))
|
|
90934
91170
|
return parsed;
|
|
90935
91171
|
return null;
|
|
@@ -91092,7 +91328,7 @@ function resolveShimOptionsFromEnv(env2) {
|
|
|
91092
91328
|
return {
|
|
91093
91329
|
url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
|
|
91094
91330
|
bankId: env2.HINDSIGHT_BANK_ID || "",
|
|
91095
|
-
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR ||
|
|
91331
|
+
cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join84(home2, ".hindsight-shim")
|
|
91096
91332
|
};
|
|
91097
91333
|
}
|
|
91098
91334
|
function registerHindsightMcpShimCommand(program3) {
|
|
@@ -91105,7 +91341,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
91105
91341
|
|
|
91106
91342
|
// src/cli/deliver-file.ts
|
|
91107
91343
|
init_client2();
|
|
91108
|
-
import { readFileSync as
|
|
91344
|
+
import { readFileSync as readFileSync72, statSync as statSync46 } from "node:fs";
|
|
91109
91345
|
import { basename as basename11 } from "node:path";
|
|
91110
91346
|
|
|
91111
91347
|
// src/delivery/onedrive.ts
|
|
@@ -91445,7 +91681,7 @@ async function defaultResolveProvider() {
|
|
|
91445
91681
|
async function runDeliverFile(localPath, deps = {}) {
|
|
91446
91682
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
91447
91683
|
const sizeOf = deps.fileSize ?? ((p) => statSync46(p).size);
|
|
91448
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
91684
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync72(p)));
|
|
91449
91685
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
91450
91686
|
let size;
|
|
91451
91687
|
try {
|
|
@@ -91766,8 +92002,8 @@ function runRedactStdin() {
|
|
|
91766
92002
|
}
|
|
91767
92003
|
|
|
91768
92004
|
// src/cli/status-ask.ts
|
|
91769
|
-
import { readFileSync as
|
|
91770
|
-
import { join as
|
|
92005
|
+
import { readFileSync as readFileSync76, existsSync as existsSync88, readdirSync as readdirSync32 } from "node:fs";
|
|
92006
|
+
import { join as join89 } from "node:path";
|
|
91771
92007
|
import { homedir as homedir50 } from "node:os";
|
|
91772
92008
|
|
|
91773
92009
|
// src/status-ask/report.ts
|
|
@@ -92042,7 +92278,7 @@ function runReport(opts) {
|
|
|
92042
92278
|
for (const src of sources) {
|
|
92043
92279
|
let content;
|
|
92044
92280
|
try {
|
|
92045
|
-
content =
|
|
92281
|
+
content = readFileSync76(src.path, "utf-8");
|
|
92046
92282
|
} catch (err) {
|
|
92047
92283
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
92048
92284
|
`);
|
|
@@ -92089,7 +92325,7 @@ function runReport(opts) {
|
|
|
92089
92325
|
function resolveSources(explicitPath) {
|
|
92090
92326
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
92091
92327
|
const trimmed = explicitPath.trim();
|
|
92092
|
-
if (!
|
|
92328
|
+
if (!existsSync88(trimmed)) {
|
|
92093
92329
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
92094
92330
|
`);
|
|
92095
92331
|
process.exit(1);
|
|
@@ -92103,20 +92339,20 @@ function resolveSources(explicitPath) {
|
|
|
92103
92339
|
const config = loadConfig();
|
|
92104
92340
|
agentsDir = resolveAgentsDir(config);
|
|
92105
92341
|
} catch {
|
|
92106
|
-
agentsDir =
|
|
92342
|
+
agentsDir = join89(homedir50(), ".switchroom", "agents");
|
|
92107
92343
|
}
|
|
92108
|
-
if (!
|
|
92344
|
+
if (!existsSync88(agentsDir))
|
|
92109
92345
|
return [];
|
|
92110
92346
|
const sources = [];
|
|
92111
92347
|
let entries;
|
|
92112
92348
|
try {
|
|
92113
|
-
entries =
|
|
92349
|
+
entries = readdirSync32(agentsDir);
|
|
92114
92350
|
} catch {
|
|
92115
92351
|
return [];
|
|
92116
92352
|
}
|
|
92117
92353
|
for (const name of entries) {
|
|
92118
|
-
const path9 =
|
|
92119
|
-
if (
|
|
92354
|
+
const path9 = join89(agentsDir, name, "runtime-metrics.jsonl");
|
|
92355
|
+
if (existsSync88(path9)) {
|
|
92120
92356
|
sources.push({ path: path9, agent: name });
|
|
92121
92357
|
}
|
|
92122
92358
|
}
|
|
@@ -92146,45 +92382,45 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
92146
92382
|
init_paths();
|
|
92147
92383
|
import {
|
|
92148
92384
|
closeSync as closeSync14,
|
|
92149
|
-
existsSync as
|
|
92385
|
+
existsSync as existsSync89,
|
|
92150
92386
|
fsyncSync as fsyncSync8,
|
|
92151
|
-
mkdirSync as
|
|
92387
|
+
mkdirSync as mkdirSync51,
|
|
92152
92388
|
openSync as openSync14,
|
|
92153
|
-
readdirSync as
|
|
92154
|
-
readFileSync as
|
|
92155
|
-
renameSync as
|
|
92389
|
+
readdirSync as readdirSync33,
|
|
92390
|
+
readFileSync as readFileSync77,
|
|
92391
|
+
renameSync as renameSync22,
|
|
92156
92392
|
statSync as statSync48,
|
|
92157
92393
|
unlinkSync as unlinkSync18,
|
|
92158
92394
|
writeSync as writeSync10
|
|
92159
92395
|
} from "node:fs";
|
|
92160
|
-
import { join as
|
|
92396
|
+
import { join as join90, resolve as resolve53 } from "node:path";
|
|
92161
92397
|
var STAGING_SUBDIR = ".staging";
|
|
92162
92398
|
function overlayPathsFor(agent, opts = {}) {
|
|
92163
92399
|
const base = opts.root ? resolve53(opts.root, agent) : resolve53(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
92164
|
-
const scheduleDir =
|
|
92165
|
-
const scheduleStagingDir =
|
|
92166
|
-
const skillsDir =
|
|
92167
|
-
const skillsStagingDir =
|
|
92400
|
+
const scheduleDir = join90(base, "schedule.d");
|
|
92401
|
+
const scheduleStagingDir = join90(scheduleDir, STAGING_SUBDIR);
|
|
92402
|
+
const skillsDir = join90(base, "skills.d");
|
|
92403
|
+
const skillsStagingDir = join90(skillsDir, STAGING_SUBDIR);
|
|
92168
92404
|
return {
|
|
92169
92405
|
agentRoot: base,
|
|
92170
92406
|
scheduleDir,
|
|
92171
92407
|
scheduleStagingDir,
|
|
92172
92408
|
skillsDir,
|
|
92173
92409
|
skillsStagingDir,
|
|
92174
|
-
lockPath:
|
|
92410
|
+
lockPath: join90(base, ".lock"),
|
|
92175
92411
|
stagingDir: scheduleStagingDir
|
|
92176
92412
|
};
|
|
92177
92413
|
}
|
|
92178
92414
|
function ensureDirs(paths) {
|
|
92179
|
-
|
|
92180
|
-
|
|
92415
|
+
mkdirSync51(paths.scheduleDir, { recursive: true });
|
|
92416
|
+
mkdirSync51(paths.scheduleStagingDir, { recursive: true });
|
|
92181
92417
|
}
|
|
92182
92418
|
function ensureSkillsDirs(paths) {
|
|
92183
|
-
|
|
92184
|
-
|
|
92419
|
+
mkdirSync51(paths.skillsDir, { recursive: true });
|
|
92420
|
+
mkdirSync51(paths.skillsStagingDir, { recursive: true });
|
|
92185
92421
|
}
|
|
92186
92422
|
function withAgentLock(paths, fn) {
|
|
92187
|
-
|
|
92423
|
+
mkdirSync51(paths.agentRoot, { recursive: true });
|
|
92188
92424
|
const start = Date.now();
|
|
92189
92425
|
const TIMEOUT_MS = 5000;
|
|
92190
92426
|
let fd = null;
|
|
@@ -92225,8 +92461,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92225
92461
|
const paths = overlayPathsFor(agent, opts);
|
|
92226
92462
|
return withAgentLock(paths, () => {
|
|
92227
92463
|
ensureDirs(paths);
|
|
92228
|
-
const stagingPath =
|
|
92229
|
-
const finalPath =
|
|
92464
|
+
const stagingPath = join90(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
92465
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92230
92466
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92231
92467
|
try {
|
|
92232
92468
|
writeSync10(fd, yamlText);
|
|
@@ -92234,7 +92470,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92234
92470
|
} finally {
|
|
92235
92471
|
closeSync14(fd);
|
|
92236
92472
|
}
|
|
92237
|
-
|
|
92473
|
+
renameSync22(stagingPath, finalPath);
|
|
92238
92474
|
return finalPath;
|
|
92239
92475
|
});
|
|
92240
92476
|
}
|
|
@@ -92242,8 +92478,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92242
92478
|
const paths = overlayPathsFor(agent, opts);
|
|
92243
92479
|
return withAgentLock(paths, () => {
|
|
92244
92480
|
ensureSkillsDirs(paths);
|
|
92245
|
-
const stagingPath =
|
|
92246
|
-
const finalPath =
|
|
92481
|
+
const stagingPath = join90(paths.skillsStagingDir, `${slug}.yaml`);
|
|
92482
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92247
92483
|
const fd = openSync14(stagingPath, "w", 384);
|
|
92248
92484
|
try {
|
|
92249
92485
|
writeSync10(fd, yamlText);
|
|
@@ -92251,15 +92487,15 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
92251
92487
|
} finally {
|
|
92252
92488
|
closeSync14(fd);
|
|
92253
92489
|
}
|
|
92254
|
-
|
|
92490
|
+
renameSync22(stagingPath, finalPath);
|
|
92255
92491
|
return finalPath;
|
|
92256
92492
|
});
|
|
92257
92493
|
}
|
|
92258
92494
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
92259
92495
|
const paths = overlayPathsFor(agent, opts);
|
|
92260
92496
|
return withAgentLock(paths, () => {
|
|
92261
|
-
const finalPath =
|
|
92262
|
-
if (!
|
|
92497
|
+
const finalPath = join90(paths.skillsDir, `${slug}.yaml`);
|
|
92498
|
+
if (!existsSync89(finalPath))
|
|
92263
92499
|
return false;
|
|
92264
92500
|
unlinkSync18(finalPath);
|
|
92265
92501
|
return true;
|
|
@@ -92267,15 +92503,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
92267
92503
|
}
|
|
92268
92504
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
92269
92505
|
const paths = overlayPathsFor(agent, opts);
|
|
92270
|
-
if (!
|
|
92506
|
+
if (!existsSync89(paths.skillsDir))
|
|
92271
92507
|
return [];
|
|
92272
92508
|
const out = [];
|
|
92273
|
-
for (const name of
|
|
92509
|
+
for (const name of readdirSync33(paths.skillsDir)) {
|
|
92274
92510
|
if (!/\.ya?ml$/i.test(name))
|
|
92275
92511
|
continue;
|
|
92276
|
-
const full =
|
|
92512
|
+
const full = join90(paths.skillsDir, name);
|
|
92277
92513
|
try {
|
|
92278
|
-
const raw =
|
|
92514
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92279
92515
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92280
92516
|
out.push({ slug, path: full, raw });
|
|
92281
92517
|
} catch {}
|
|
@@ -92285,8 +92521,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
92285
92521
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
92286
92522
|
const paths = overlayPathsFor(agent, opts);
|
|
92287
92523
|
return withAgentLock(paths, () => {
|
|
92288
|
-
const finalPath =
|
|
92289
|
-
if (!
|
|
92524
|
+
const finalPath = join90(paths.scheduleDir, `${slug}.yaml`);
|
|
92525
|
+
if (!existsSync89(finalPath))
|
|
92290
92526
|
return false;
|
|
92291
92527
|
unlinkSync18(finalPath);
|
|
92292
92528
|
return true;
|
|
@@ -92294,15 +92530,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
92294
92530
|
}
|
|
92295
92531
|
function listOverlayEntries(agent, opts = {}) {
|
|
92296
92532
|
const paths = overlayPathsFor(agent, opts);
|
|
92297
|
-
if (!
|
|
92533
|
+
if (!existsSync89(paths.scheduleDir))
|
|
92298
92534
|
return [];
|
|
92299
92535
|
const out = [];
|
|
92300
|
-
for (const name of
|
|
92536
|
+
for (const name of readdirSync33(paths.scheduleDir)) {
|
|
92301
92537
|
if (!/\.ya?ml$/i.test(name))
|
|
92302
92538
|
continue;
|
|
92303
|
-
const full =
|
|
92539
|
+
const full = join90(paths.scheduleDir, name);
|
|
92304
92540
|
try {
|
|
92305
|
-
const raw =
|
|
92541
|
+
const raw = readFileSync77(full, "utf-8");
|
|
92306
92542
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
92307
92543
|
out.push({ slug, path: full, raw });
|
|
92308
92544
|
} catch {}
|
|
@@ -92522,27 +92758,27 @@ function reconcileAgentCronOnly(agent) {
|
|
|
92522
92758
|
// src/cli/agent-config-pending.ts
|
|
92523
92759
|
import {
|
|
92524
92760
|
closeSync as closeSync15,
|
|
92525
|
-
existsSync as
|
|
92761
|
+
existsSync as existsSync90,
|
|
92526
92762
|
fsyncSync as fsyncSync9,
|
|
92527
|
-
mkdirSync as
|
|
92763
|
+
mkdirSync as mkdirSync52,
|
|
92528
92764
|
openSync as openSync15,
|
|
92529
|
-
readdirSync as
|
|
92530
|
-
readFileSync as
|
|
92531
|
-
renameSync as
|
|
92765
|
+
readdirSync as readdirSync34,
|
|
92766
|
+
readFileSync as readFileSync78,
|
|
92767
|
+
renameSync as renameSync23,
|
|
92532
92768
|
unlinkSync as unlinkSync19,
|
|
92533
|
-
writeFileSync as
|
|
92769
|
+
writeFileSync as writeFileSync33,
|
|
92534
92770
|
writeSync as writeSync11
|
|
92535
92771
|
} from "node:fs";
|
|
92536
|
-
import { join as
|
|
92772
|
+
import { join as join91 } from "node:path";
|
|
92537
92773
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
92538
92774
|
var STAGE_ID_PREFIX = "cap_";
|
|
92539
92775
|
function pendingDir(agent, opts = {}) {
|
|
92540
92776
|
const paths = overlayPathsFor(agent, opts);
|
|
92541
|
-
return
|
|
92777
|
+
return join91(paths.scheduleDir, ".pending");
|
|
92542
92778
|
}
|
|
92543
92779
|
function ensurePendingDir(agent, opts = {}) {
|
|
92544
92780
|
const dir = pendingDir(agent, opts);
|
|
92545
|
-
|
|
92781
|
+
mkdirSync52(dir, { recursive: true });
|
|
92546
92782
|
return dir;
|
|
92547
92783
|
}
|
|
92548
92784
|
function newStageId() {
|
|
@@ -92551,8 +92787,8 @@ function newStageId() {
|
|
|
92551
92787
|
function stagePendingScheduleEntry(opts) {
|
|
92552
92788
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
92553
92789
|
const stageId = opts.stageId ?? newStageId();
|
|
92554
|
-
const yamlPath =
|
|
92555
|
-
const metaPath =
|
|
92790
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92791
|
+
const metaPath = join91(dir, `${stageId}.meta.json`);
|
|
92556
92792
|
const meta = {
|
|
92557
92793
|
v: 1,
|
|
92558
92794
|
stage_id: stageId,
|
|
@@ -92571,27 +92807,27 @@ function stagePendingScheduleEntry(opts) {
|
|
|
92571
92807
|
} finally {
|
|
92572
92808
|
closeSync15(fd);
|
|
92573
92809
|
}
|
|
92574
|
-
|
|
92810
|
+
renameSync23(yamlTmp, yamlPath);
|
|
92575
92811
|
}
|
|
92576
|
-
|
|
92812
|
+
writeFileSync33(metaPath, JSON.stringify(meta, null, 2) + `
|
|
92577
92813
|
`, { mode: 384 });
|
|
92578
92814
|
return { stageId, yamlPath, metaPath };
|
|
92579
92815
|
}
|
|
92580
92816
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
92581
92817
|
const dir = pendingDir(agent, opts);
|
|
92582
|
-
if (!
|
|
92818
|
+
if (!existsSync90(dir))
|
|
92583
92819
|
return [];
|
|
92584
92820
|
const out = [];
|
|
92585
|
-
for (const name of
|
|
92821
|
+
for (const name of readdirSync34(dir).sort()) {
|
|
92586
92822
|
if (!name.endsWith(".meta.json"))
|
|
92587
92823
|
continue;
|
|
92588
92824
|
const stageId = name.slice(0, -".meta.json".length);
|
|
92589
|
-
const metaPath =
|
|
92590
|
-
const yamlPath =
|
|
92591
|
-
if (!
|
|
92825
|
+
const metaPath = join91(dir, name);
|
|
92826
|
+
const yamlPath = join91(dir, `${stageId}.yaml`);
|
|
92827
|
+
if (!existsSync90(yamlPath))
|
|
92592
92828
|
continue;
|
|
92593
92829
|
try {
|
|
92594
|
-
const meta = JSON.parse(
|
|
92830
|
+
const meta = JSON.parse(readFileSync78(metaPath, "utf-8"));
|
|
92595
92831
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
92596
92832
|
continue;
|
|
92597
92833
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -92606,11 +92842,11 @@ function commitPendingScheduleEntry(opts) {
|
|
|
92606
92842
|
return { committed: false, reason: "not_found" };
|
|
92607
92843
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
92608
92844
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
92609
|
-
const finalPath =
|
|
92610
|
-
if (
|
|
92845
|
+
const finalPath = join91(paths.scheduleDir, `${slug}.yaml`);
|
|
92846
|
+
if (existsSync90(finalPath)) {
|
|
92611
92847
|
return { committed: false, reason: "slug_collision" };
|
|
92612
92848
|
}
|
|
92613
|
-
|
|
92849
|
+
renameSync23(match.yamlPath, finalPath);
|
|
92614
92850
|
unlinkSync19(match.metaPath);
|
|
92615
92851
|
return { committed: true, path: finalPath, slug };
|
|
92616
92852
|
}
|
|
@@ -92629,7 +92865,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
92629
92865
|
}
|
|
92630
92866
|
|
|
92631
92867
|
// src/cli/agent-config-write.ts
|
|
92632
|
-
import { existsSync as
|
|
92868
|
+
import { existsSync as existsSync91, readFileSync as readFileSync79 } from "node:fs";
|
|
92633
92869
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
92634
92870
|
|
|
92635
92871
|
// src/scheduler/schedule-report.ts
|
|
@@ -93019,8 +93255,8 @@ function scheduleRemove(opts) {
|
|
|
93019
93255
|
}
|
|
93020
93256
|
let priorContent = null;
|
|
93021
93257
|
try {
|
|
93022
|
-
if (
|
|
93023
|
-
priorContent =
|
|
93258
|
+
if (existsSync91(match.path))
|
|
93259
|
+
priorContent = readFileSync79(match.path, "utf-8");
|
|
93024
93260
|
} catch {}
|
|
93025
93261
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
93026
93262
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -93223,7 +93459,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93223
93459
|
}
|
|
93224
93460
|
let blob;
|
|
93225
93461
|
if (opts.jsonl) {
|
|
93226
|
-
blob =
|
|
93462
|
+
blob = existsSync91(opts.jsonl) ? readFileSync79(opts.jsonl, "utf-8") : "";
|
|
93227
93463
|
} else {
|
|
93228
93464
|
try {
|
|
93229
93465
|
blob = execFileSync28("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -93255,11 +93491,11 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
93255
93491
|
|
|
93256
93492
|
// src/cli/agent-config-skill-write.ts
|
|
93257
93493
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
93258
|
-
import { existsSync as
|
|
93494
|
+
import { existsSync as existsSync92 } from "node:fs";
|
|
93259
93495
|
init_reconcile_default_skills();
|
|
93260
93496
|
init_agent_config();
|
|
93261
93497
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
93262
|
-
import { join as
|
|
93498
|
+
import { join as join92 } from "node:path";
|
|
93263
93499
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
93264
93500
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
93265
93501
|
function exitCodeFor2(code) {
|
|
@@ -93334,8 +93570,8 @@ function skillInstall(opts) {
|
|
|
93334
93570
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
93335
93571
|
}
|
|
93336
93572
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
93337
|
-
const skillPath =
|
|
93338
|
-
if (!
|
|
93573
|
+
const skillPath = join92(poolDir, skillName);
|
|
93574
|
+
if (!existsSync92(skillPath)) {
|
|
93339
93575
|
return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
|
|
93340
93576
|
}
|
|
93341
93577
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -93499,21 +93735,21 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
93499
93735
|
// src/cli/skill.ts
|
|
93500
93736
|
import {
|
|
93501
93737
|
closeSync as closeSync16,
|
|
93502
|
-
existsSync as
|
|
93738
|
+
existsSync as existsSync93,
|
|
93503
93739
|
lstatSync as lstatSync11,
|
|
93504
|
-
mkdirSync as
|
|
93740
|
+
mkdirSync as mkdirSync53,
|
|
93505
93741
|
mkdtempSync as mkdtempSync5,
|
|
93506
93742
|
openSync as openSync16,
|
|
93507
|
-
readFileSync as
|
|
93508
|
-
readdirSync as
|
|
93743
|
+
readFileSync as readFileSync80,
|
|
93744
|
+
readdirSync as readdirSync35,
|
|
93509
93745
|
realpathSync as realpathSync7,
|
|
93510
|
-
renameSync as
|
|
93746
|
+
renameSync as renameSync24,
|
|
93511
93747
|
rmSync as rmSync18,
|
|
93512
93748
|
statSync as statSync49,
|
|
93513
|
-
writeFileSync as
|
|
93749
|
+
writeFileSync as writeFileSync34
|
|
93514
93750
|
} from "node:fs";
|
|
93515
93751
|
import { tmpdir as tmpdir6, homedir as homedir51 } from "node:os";
|
|
93516
|
-
import { dirname as
|
|
93752
|
+
import { dirname as dirname33, join as join93, relative as relative4, resolve as resolve54 } from "node:path";
|
|
93517
93753
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
93518
93754
|
|
|
93519
93755
|
// src/cli/skill-common.ts
|
|
@@ -93747,7 +93983,7 @@ function scanForClaudeP2(content) {
|
|
|
93747
93983
|
function resolveSkillsPoolDir2(override) {
|
|
93748
93984
|
const raw = override ?? "~/.switchroom/skills";
|
|
93749
93985
|
if (raw.startsWith("~/")) {
|
|
93750
|
-
return
|
|
93986
|
+
return join93(homedir51(), raw.slice(2));
|
|
93751
93987
|
}
|
|
93752
93988
|
if (raw === "~")
|
|
93753
93989
|
return homedir51();
|
|
@@ -93784,10 +94020,10 @@ function loadFromDir(dir) {
|
|
|
93784
94020
|
}
|
|
93785
94021
|
const files = {};
|
|
93786
94022
|
const walk2 = (sub) => {
|
|
93787
|
-
const entries =
|
|
94023
|
+
const entries = readdirSync35(sub, { withFileTypes: true });
|
|
93788
94024
|
for (const ent of entries) {
|
|
93789
|
-
const full =
|
|
93790
|
-
const rel =
|
|
94025
|
+
const full = join93(sub, ent.name);
|
|
94026
|
+
const rel = relative4(abs, full);
|
|
93791
94027
|
if (ent.isSymbolicLink()) {
|
|
93792
94028
|
fail3(`refusing to read symlink inside --from dir: ${rel}`);
|
|
93793
94029
|
}
|
|
@@ -93796,7 +94032,7 @@ function loadFromDir(dir) {
|
|
|
93796
94032
|
continue;
|
|
93797
94033
|
}
|
|
93798
94034
|
if (ent.isFile()) {
|
|
93799
|
-
const buf =
|
|
94035
|
+
const buf = readFileSync80(full);
|
|
93800
94036
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
93801
94037
|
}
|
|
93802
94038
|
}
|
|
@@ -93821,7 +94057,7 @@ function loadFromTarball(tarPath) {
|
|
|
93821
94057
|
fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
93822
94058
|
}
|
|
93823
94059
|
}
|
|
93824
|
-
const staging = mkdtempSync5(
|
|
94060
|
+
const staging = mkdtempSync5(join93(tmpdir6(), "skill-apply-extract-"));
|
|
93825
94061
|
try {
|
|
93826
94062
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
93827
94063
|
const r = spawnSync16("tar", [
|
|
@@ -93845,7 +94081,7 @@ function loadFromTarball(tarPath) {
|
|
|
93845
94081
|
}
|
|
93846
94082
|
}
|
|
93847
94083
|
function loadSingleFile(filePath) {
|
|
93848
|
-
const content =
|
|
94084
|
+
const content = readFileSync80(filePath, "utf-8");
|
|
93849
94085
|
return { "SKILL.md": content };
|
|
93850
94086
|
}
|
|
93851
94087
|
function loadFromStdin() {
|
|
@@ -93907,10 +94143,10 @@ function validatePayload(name, files) {
|
|
|
93907
94143
|
errors2.push(`${path9} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
93908
94144
|
}
|
|
93909
94145
|
} else if (PY_SCRIPT_RE2.test(path9)) {
|
|
93910
|
-
const tmp = mkdtempSync5(
|
|
93911
|
-
const tmpPy =
|
|
94146
|
+
const tmp = mkdtempSync5(join93(tmpdir6(), "skill-apply-py-"));
|
|
94147
|
+
const tmpPy = join93(tmp, "check.py");
|
|
93912
94148
|
try {
|
|
93913
|
-
|
|
94149
|
+
writeFileSync34(tmpPy, content);
|
|
93914
94150
|
const r = spawnSync16("python3", ["-m", "py_compile", tmpPy], {
|
|
93915
94151
|
encoding: "utf-8"
|
|
93916
94152
|
});
|
|
@@ -93928,15 +94164,15 @@ function validatePayload(name, files) {
|
|
|
93928
94164
|
function diffSummary(currentDir, files) {
|
|
93929
94165
|
const lines = [];
|
|
93930
94166
|
const currentFiles = {};
|
|
93931
|
-
if (
|
|
94167
|
+
if (existsSync93(currentDir)) {
|
|
93932
94168
|
const walk2 = (sub) => {
|
|
93933
|
-
for (const ent of
|
|
93934
|
-
const full =
|
|
93935
|
-
const rel =
|
|
94169
|
+
for (const ent of readdirSync35(sub, { withFileTypes: true })) {
|
|
94170
|
+
const full = join93(sub, ent.name);
|
|
94171
|
+
const rel = relative4(currentDir, full);
|
|
93936
94172
|
if (ent.isDirectory()) {
|
|
93937
94173
|
walk2(full);
|
|
93938
94174
|
} else if (ent.isFile()) {
|
|
93939
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
94175
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync80(full, "utf-8");
|
|
93940
94176
|
}
|
|
93941
94177
|
}
|
|
93942
94178
|
};
|
|
@@ -93964,10 +94200,10 @@ function diffSummary(currentDir, files) {
|
|
|
93964
94200
|
`);
|
|
93965
94201
|
}
|
|
93966
94202
|
function writePayload(poolDir, name, files) {
|
|
93967
|
-
if (!
|
|
93968
|
-
|
|
94203
|
+
if (!existsSync93(poolDir)) {
|
|
94204
|
+
mkdirSync53(poolDir, { recursive: true, mode: 493 });
|
|
93969
94205
|
}
|
|
93970
|
-
const target =
|
|
94206
|
+
const target = join93(poolDir, name);
|
|
93971
94207
|
let targetIsSymlink = false;
|
|
93972
94208
|
try {
|
|
93973
94209
|
const st = lstatSync11(target);
|
|
@@ -93978,15 +94214,15 @@ function writePayload(poolDir, name, files) {
|
|
|
93978
94214
|
if (targetIsSymlink) {
|
|
93979
94215
|
fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
93980
94216
|
}
|
|
93981
|
-
const staging = mkdtempSync5(
|
|
94217
|
+
const staging = mkdtempSync5(join93(poolDir, `.skill-apply-stage-${name}-`));
|
|
93982
94218
|
let oldRename = null;
|
|
93983
94219
|
try {
|
|
93984
94220
|
for (const [path9, content] of Object.entries(files)) {
|
|
93985
|
-
const full =
|
|
93986
|
-
|
|
94221
|
+
const full = join93(staging, path9);
|
|
94222
|
+
mkdirSync53(dirname33(full), { recursive: true, mode: 493 });
|
|
93987
94223
|
const fd = openSync16(full, "wx");
|
|
93988
94224
|
try {
|
|
93989
|
-
|
|
94225
|
+
writeFileSync34(fd, content);
|
|
93990
94226
|
} finally {
|
|
93991
94227
|
closeSync16(fd);
|
|
93992
94228
|
}
|
|
@@ -94002,9 +94238,9 @@ function writePayload(poolDir, name, files) {
|
|
|
94002
94238
|
} catch {}
|
|
94003
94239
|
if (targetExists) {
|
|
94004
94240
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
94005
|
-
|
|
94241
|
+
renameSync24(target, oldRename);
|
|
94006
94242
|
}
|
|
94007
|
-
|
|
94243
|
+
renameSync24(staging, target);
|
|
94008
94244
|
if (oldRename) {
|
|
94009
94245
|
rmSync18(oldRename, { recursive: true, force: true });
|
|
94010
94246
|
oldRename = null;
|
|
@@ -94013,12 +94249,12 @@ function writePayload(poolDir, name, files) {
|
|
|
94013
94249
|
try {
|
|
94014
94250
|
rmSync18(staging, { recursive: true, force: true });
|
|
94015
94251
|
} catch {}
|
|
94016
|
-
if (oldRename &&
|
|
94252
|
+
if (oldRename && existsSync93(oldRename)) {
|
|
94017
94253
|
try {
|
|
94018
|
-
if (
|
|
94254
|
+
if (existsSync93(target)) {
|
|
94019
94255
|
rmSync18(target, { recursive: true, force: true });
|
|
94020
94256
|
}
|
|
94021
|
-
|
|
94257
|
+
renameSync24(oldRename, target);
|
|
94022
94258
|
} catch {}
|
|
94023
94259
|
}
|
|
94024
94260
|
throw err2;
|
|
@@ -94036,7 +94272,7 @@ function registerSkillCommand(program3) {
|
|
|
94036
94272
|
files = loadFromStdin();
|
|
94037
94273
|
} else {
|
|
94038
94274
|
const fromPath = resolve54(opts.from);
|
|
94039
|
-
if (!
|
|
94275
|
+
if (!existsSync93(fromPath)) {
|
|
94040
94276
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
94041
94277
|
}
|
|
94042
94278
|
const st = statSync49(fromPath);
|
|
@@ -94060,7 +94296,7 @@ function registerSkillCommand(program3) {
|
|
|
94060
94296
|
}
|
|
94061
94297
|
const config = loadConfig();
|
|
94062
94298
|
const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
|
|
94063
|
-
const currentDir =
|
|
94299
|
+
const currentDir = join93(poolDir, name);
|
|
94064
94300
|
console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
|
|
94065
94301
|
console.log(source_default.bold("Diff vs current pool content:"));
|
|
94066
94302
|
console.log(diffSummary(currentDir, files));
|
|
@@ -94092,20 +94328,20 @@ function sumBytes(files) {
|
|
|
94092
94328
|
init_esm();
|
|
94093
94329
|
import {
|
|
94094
94330
|
closeSync as closeSync17,
|
|
94095
|
-
existsSync as
|
|
94331
|
+
existsSync as existsSync94,
|
|
94096
94332
|
lstatSync as lstatSync12,
|
|
94097
|
-
mkdirSync as
|
|
94333
|
+
mkdirSync as mkdirSync54,
|
|
94098
94334
|
mkdtempSync as mkdtempSync6,
|
|
94099
94335
|
openSync as openSync17,
|
|
94100
|
-
readFileSync as
|
|
94101
|
-
readdirSync as
|
|
94102
|
-
renameSync as
|
|
94336
|
+
readFileSync as readFileSync81,
|
|
94337
|
+
readdirSync as readdirSync36,
|
|
94338
|
+
renameSync as renameSync25,
|
|
94103
94339
|
rmSync as rmSync19,
|
|
94104
94340
|
statSync as statSync50,
|
|
94105
94341
|
utimesSync,
|
|
94106
|
-
writeFileSync as
|
|
94342
|
+
writeFileSync as writeFileSync35
|
|
94107
94343
|
} from "node:fs";
|
|
94108
|
-
import { dirname as
|
|
94344
|
+
import { dirname as dirname34, join as join94, relative as relative5, resolve as resolve55 } from "node:path";
|
|
94109
94345
|
import { homedir as homedir52, tmpdir as tmpdir7 } from "node:os";
|
|
94110
94346
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
94111
94347
|
init_helpers();
|
|
@@ -94117,18 +94353,18 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
94117
94353
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
94118
94354
|
function resolveConfigSkillsDir(agent) {
|
|
94119
94355
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
94120
|
-
const candidate = override ? resolve55(override) :
|
|
94121
|
-
if (!
|
|
94356
|
+
const candidate = override ? resolve55(override) : join94(homedir52(), ".switchroom-config");
|
|
94357
|
+
if (!existsSync94(candidate))
|
|
94122
94358
|
return null;
|
|
94123
|
-
return
|
|
94359
|
+
return join94(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
94124
94360
|
}
|
|
94125
94361
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
94126
94362
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
94127
94363
|
try {
|
|
94128
|
-
if (!
|
|
94364
|
+
if (!existsSync94(configSkillsRoot))
|
|
94129
94365
|
return;
|
|
94130
94366
|
const now = Date.now();
|
|
94131
|
-
for (const ent of
|
|
94367
|
+
for (const ent of readdirSync36(configSkillsRoot)) {
|
|
94132
94368
|
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
94133
94369
|
if (!m)
|
|
94134
94370
|
continue;
|
|
@@ -94138,7 +94374,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
94138
94374
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
94139
94375
|
continue;
|
|
94140
94376
|
try {
|
|
94141
|
-
rmSync19(
|
|
94377
|
+
rmSync19(join94(configSkillsRoot, ent), { recursive: true, force: true });
|
|
94142
94378
|
} catch {}
|
|
94143
94379
|
}
|
|
94144
94380
|
} catch {}
|
|
@@ -94147,7 +94383,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94147
94383
|
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
94148
94384
|
if (!configSkillsRoot)
|
|
94149
94385
|
return;
|
|
94150
|
-
const dest =
|
|
94386
|
+
const dest = join94(configSkillsRoot, name);
|
|
94151
94387
|
try {
|
|
94152
94388
|
if (liveSkillDir !== null) {
|
|
94153
94389
|
try {
|
|
@@ -94161,35 +94397,35 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
94161
94397
|
}
|
|
94162
94398
|
if (liveSkillDir === null) {
|
|
94163
94399
|
sweepMirrorPriors(configSkillsRoot);
|
|
94164
|
-
if (
|
|
94165
|
-
const trash =
|
|
94166
|
-
|
|
94400
|
+
if (existsSync94(dest)) {
|
|
94401
|
+
const trash = join94(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
94402
|
+
renameSync25(dest, trash);
|
|
94167
94403
|
}
|
|
94168
94404
|
return;
|
|
94169
94405
|
}
|
|
94170
|
-
|
|
94406
|
+
mkdirSync54(configSkillsRoot, { recursive: true, mode: 493 });
|
|
94171
94407
|
sweepMirrorPriors(configSkillsRoot);
|
|
94172
|
-
const staging = mkdtempSync6(
|
|
94408
|
+
const staging = mkdtempSync6(join94(configSkillsRoot, `.${name}-staging-`));
|
|
94173
94409
|
const walk2 = (src, dst) => {
|
|
94174
|
-
|
|
94175
|
-
for (const ent of
|
|
94176
|
-
const s =
|
|
94177
|
-
const d =
|
|
94410
|
+
mkdirSync54(dst, { recursive: true, mode: 493 });
|
|
94411
|
+
for (const ent of readdirSync36(src, { withFileTypes: true })) {
|
|
94412
|
+
const s = join94(src, ent.name);
|
|
94413
|
+
const d = join94(dst, ent.name);
|
|
94178
94414
|
if (ent.isSymbolicLink())
|
|
94179
94415
|
continue;
|
|
94180
94416
|
if (ent.isDirectory())
|
|
94181
94417
|
walk2(s, d);
|
|
94182
94418
|
else if (ent.isFile()) {
|
|
94183
|
-
|
|
94419
|
+
writeFileSync35(d, readFileSync81(s));
|
|
94184
94420
|
}
|
|
94185
94421
|
}
|
|
94186
94422
|
};
|
|
94187
94423
|
walk2(liveSkillDir, staging);
|
|
94188
|
-
if (
|
|
94189
|
-
const prior =
|
|
94190
|
-
|
|
94424
|
+
if (existsSync94(dest)) {
|
|
94425
|
+
const prior = join94(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
94426
|
+
renameSync25(dest, prior);
|
|
94191
94427
|
}
|
|
94192
|
-
|
|
94428
|
+
renameSync25(staging, dest);
|
|
94193
94429
|
} catch (err2) {
|
|
94194
94430
|
process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
|
|
94195
94431
|
`));
|
|
@@ -94216,20 +94452,20 @@ function resolveAgent(opts) {
|
|
|
94216
94452
|
function resolveAgentsRoot(opts) {
|
|
94217
94453
|
if (opts.root)
|
|
94218
94454
|
return resolve55(opts.root);
|
|
94219
|
-
return
|
|
94455
|
+
return join94(homedir52(), ".switchroom", "agents");
|
|
94220
94456
|
}
|
|
94221
94457
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
94222
|
-
return
|
|
94458
|
+
return join94(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
|
|
94223
94459
|
}
|
|
94224
94460
|
function trashDir(agentsRoot, agent) {
|
|
94225
|
-
return
|
|
94461
|
+
return join94(agentsRoot, agent, ".claude", TRASH_DIRNAME);
|
|
94226
94462
|
}
|
|
94227
94463
|
function countPersonalSkills(agentsRoot, agent) {
|
|
94228
|
-
const skillsDir =
|
|
94229
|
-
if (!
|
|
94464
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94465
|
+
if (!existsSync94(skillsDir))
|
|
94230
94466
|
return 0;
|
|
94231
94467
|
let n = 0;
|
|
94232
|
-
for (const ent of
|
|
94468
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94233
94469
|
if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
|
|
94234
94470
|
n += 1;
|
|
94235
94471
|
}
|
|
@@ -94262,18 +94498,18 @@ function loadFromDir2(dir) {
|
|
|
94262
94498
|
}
|
|
94263
94499
|
const files = {};
|
|
94264
94500
|
const walk2 = (sub) => {
|
|
94265
|
-
for (const ent of
|
|
94266
|
-
const full =
|
|
94501
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94502
|
+
const full = join94(sub, ent.name);
|
|
94267
94503
|
if (ent.isSymbolicLink()) {
|
|
94268
|
-
fail4(`refusing to read symlink in --from dir: ${
|
|
94504
|
+
fail4(`refusing to read symlink in --from dir: ${relative5(abs, full)}`);
|
|
94269
94505
|
}
|
|
94270
94506
|
if (ent.isDirectory()) {
|
|
94271
94507
|
walk2(full);
|
|
94272
94508
|
continue;
|
|
94273
94509
|
}
|
|
94274
94510
|
if (ent.isFile()) {
|
|
94275
|
-
const rel =
|
|
94276
|
-
files[rel] =
|
|
94511
|
+
const rel = relative5(abs, full).replace(/\\/g, "/");
|
|
94512
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94277
94513
|
}
|
|
94278
94514
|
}
|
|
94279
94515
|
};
|
|
@@ -94316,10 +94552,10 @@ function behavioralValidate(files) {
|
|
|
94316
94552
|
errors2.push(`${path9} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
94317
94553
|
}
|
|
94318
94554
|
} else if (PY_SCRIPT_RE.test(path9)) {
|
|
94319
|
-
const tmp = mkdtempSync6(
|
|
94320
|
-
const tmpPy =
|
|
94555
|
+
const tmp = mkdtempSync6(join94(tmpdir7(), "skill-personal-py-"));
|
|
94556
|
+
const tmpPy = join94(tmp, "check.py");
|
|
94321
94557
|
try {
|
|
94322
|
-
|
|
94558
|
+
writeFileSync35(tmpPy, content);
|
|
94323
94559
|
const r = spawnSync17("python3", ["-m", "py_compile", tmpPy], {
|
|
94324
94560
|
encoding: "utf-8"
|
|
94325
94561
|
});
|
|
@@ -94335,13 +94571,13 @@ function behavioralValidate(files) {
|
|
|
94335
94571
|
}
|
|
94336
94572
|
function sweepTrash(agentsRoot, agent) {
|
|
94337
94573
|
const trash = trashDir(agentsRoot, agent);
|
|
94338
|
-
if (!
|
|
94574
|
+
if (!existsSync94(trash))
|
|
94339
94575
|
return;
|
|
94340
94576
|
const now = Date.now();
|
|
94341
|
-
for (const ent of
|
|
94577
|
+
for (const ent of readdirSync36(trash, { withFileTypes: true })) {
|
|
94342
94578
|
if (!ent.isDirectory())
|
|
94343
94579
|
continue;
|
|
94344
|
-
const entPath =
|
|
94580
|
+
const entPath = join94(trash, ent.name);
|
|
94345
94581
|
try {
|
|
94346
94582
|
const st = statSync50(entPath);
|
|
94347
94583
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
@@ -94361,16 +94597,16 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94361
94597
|
if (targetIsSymlink) {
|
|
94362
94598
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
94363
94599
|
}
|
|
94364
|
-
|
|
94365
|
-
const staging = mkdtempSync6(
|
|
94600
|
+
mkdirSync54(dirname34(targetDir), { recursive: true, mode: 493 });
|
|
94601
|
+
const staging = mkdtempSync6(join94(dirname34(targetDir), `.skill-personal-stage-`));
|
|
94366
94602
|
let oldRename = null;
|
|
94367
94603
|
try {
|
|
94368
94604
|
for (const [path9, content] of Object.entries(files)) {
|
|
94369
|
-
const full =
|
|
94370
|
-
|
|
94605
|
+
const full = join94(staging, path9);
|
|
94606
|
+
mkdirSync54(dirname34(full), { recursive: true, mode: 493 });
|
|
94371
94607
|
const fd = openSync17(full, "wx");
|
|
94372
94608
|
try {
|
|
94373
|
-
|
|
94609
|
+
writeFileSync35(fd, content);
|
|
94374
94610
|
} finally {
|
|
94375
94611
|
closeSync17(fd);
|
|
94376
94612
|
}
|
|
@@ -94386,9 +94622,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94386
94622
|
} catch {}
|
|
94387
94623
|
if (targetExists) {
|
|
94388
94624
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
94389
|
-
|
|
94625
|
+
renameSync25(targetDir, oldRename);
|
|
94390
94626
|
}
|
|
94391
|
-
|
|
94627
|
+
renameSync25(staging, targetDir);
|
|
94392
94628
|
if (oldRename) {
|
|
94393
94629
|
rmSync19(oldRename, { recursive: true, force: true });
|
|
94394
94630
|
oldRename = null;
|
|
@@ -94397,12 +94633,12 @@ function writePersonalSkill(targetDir, files) {
|
|
|
94397
94633
|
try {
|
|
94398
94634
|
rmSync19(staging, { recursive: true, force: true });
|
|
94399
94635
|
} catch {}
|
|
94400
|
-
if (oldRename &&
|
|
94636
|
+
if (oldRename && existsSync94(oldRename)) {
|
|
94401
94637
|
try {
|
|
94402
|
-
if (
|
|
94638
|
+
if (existsSync94(targetDir)) {
|
|
94403
94639
|
rmSync19(targetDir, { recursive: true, force: true });
|
|
94404
94640
|
}
|
|
94405
|
-
|
|
94641
|
+
renameSync25(oldRename, targetDir);
|
|
94406
94642
|
} catch {}
|
|
94407
94643
|
}
|
|
94408
94644
|
throw err2;
|
|
@@ -94465,7 +94701,7 @@ function loadFiles(opts) {
|
|
|
94465
94701
|
return loadFromStdin2();
|
|
94466
94702
|
}
|
|
94467
94703
|
const p = resolve55(opts.from);
|
|
94468
|
-
if (!
|
|
94704
|
+
if (!existsSync94(p)) {
|
|
94469
94705
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
94470
94706
|
}
|
|
94471
94707
|
const st = statSync50(p);
|
|
@@ -94473,7 +94709,7 @@ function loadFiles(opts) {
|
|
|
94473
94709
|
return loadFromDir2(p);
|
|
94474
94710
|
}
|
|
94475
94711
|
if (p.endsWith(".md")) {
|
|
94476
|
-
return { "SKILL.md":
|
|
94712
|
+
return { "SKILL.md": readFileSync81(p, "utf-8") };
|
|
94477
94713
|
}
|
|
94478
94714
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
94479
94715
|
}
|
|
@@ -94513,10 +94749,10 @@ function editPersonalAction(name, opts) {
|
|
|
94513
94749
|
}
|
|
94514
94750
|
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
94515
94751
|
function defaultSharedRoot() {
|
|
94516
|
-
return
|
|
94752
|
+
return join94(homedir52(), ".switchroom", "skills");
|
|
94517
94753
|
}
|
|
94518
94754
|
function defaultBundledRoot() {
|
|
94519
|
-
return
|
|
94755
|
+
return join94(homedir52(), ".switchroom", "skills", "_bundled");
|
|
94520
94756
|
}
|
|
94521
94757
|
function resolveCloneSource(source, opts) {
|
|
94522
94758
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
@@ -94526,8 +94762,8 @@ function resolveCloneSource(source, opts) {
|
|
|
94526
94762
|
const tier = m[1];
|
|
94527
94763
|
const slug = m[2];
|
|
94528
94764
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
94529
|
-
const dir =
|
|
94530
|
-
if (!
|
|
94765
|
+
const dir = join94(root, slug);
|
|
94766
|
+
if (!existsSync94(dir)) {
|
|
94531
94767
|
fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
94532
94768
|
}
|
|
94533
94769
|
const st = lstatSync12(dir);
|
|
@@ -94541,8 +94777,8 @@ function readSourceFiles(dir) {
|
|
|
94541
94777
|
const files = {};
|
|
94542
94778
|
const skipped = [];
|
|
94543
94779
|
const walk2 = (sub) => {
|
|
94544
|
-
for (const ent of
|
|
94545
|
-
const full =
|
|
94780
|
+
for (const ent of readdirSync36(sub, { withFileTypes: true })) {
|
|
94781
|
+
const full = join94(sub, ent.name);
|
|
94546
94782
|
if (ent.isSymbolicLink()) {
|
|
94547
94783
|
continue;
|
|
94548
94784
|
}
|
|
@@ -94551,7 +94787,7 @@ function readSourceFiles(dir) {
|
|
|
94551
94787
|
continue;
|
|
94552
94788
|
}
|
|
94553
94789
|
if (ent.isFile()) {
|
|
94554
|
-
const rel =
|
|
94790
|
+
const rel = relative5(dir, full).replace(/\\/g, "/");
|
|
94555
94791
|
if (!validateRelPath(rel)) {
|
|
94556
94792
|
skipped.push(rel);
|
|
94557
94793
|
continue;
|
|
@@ -94562,7 +94798,7 @@ function readSourceFiles(dir) {
|
|
|
94562
94798
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
94563
94799
|
}
|
|
94564
94800
|
} catch {}
|
|
94565
|
-
files[rel] =
|
|
94801
|
+
files[rel] = readFileSync81(full, "utf-8");
|
|
94566
94802
|
}
|
|
94567
94803
|
}
|
|
94568
94804
|
};
|
|
@@ -94651,10 +94887,10 @@ function removePersonalAction(name, opts) {
|
|
|
94651
94887
|
throw err2;
|
|
94652
94888
|
}
|
|
94653
94889
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
94654
|
-
|
|
94890
|
+
mkdirSync54(trashRoot2, { recursive: true, mode: 493 });
|
|
94655
94891
|
const ts = Date.now();
|
|
94656
|
-
const trashTarget =
|
|
94657
|
-
|
|
94892
|
+
const trashTarget = join94(trashRoot2, `${name}-${ts}`);
|
|
94893
|
+
renameSync25(target, trashTarget);
|
|
94658
94894
|
const now = new Date(ts);
|
|
94659
94895
|
utimesSync(trashTarget, now, now);
|
|
94660
94896
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -94672,27 +94908,27 @@ function listPersonalAction(opts) {
|
|
|
94672
94908
|
const agent = resolveAgent(opts);
|
|
94673
94909
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
94674
94910
|
sweepTrash(agentsRoot, agent);
|
|
94675
|
-
const skillsDir =
|
|
94911
|
+
const skillsDir = join94(agentsRoot, agent, ".claude", "skills");
|
|
94676
94912
|
const personal = [];
|
|
94677
|
-
if (
|
|
94678
|
-
for (const ent of
|
|
94913
|
+
if (existsSync94(skillsDir)) {
|
|
94914
|
+
for (const ent of readdirSync36(skillsDir, { withFileTypes: true })) {
|
|
94679
94915
|
if (!ent.isDirectory())
|
|
94680
94916
|
continue;
|
|
94681
94917
|
if (!ent.name.startsWith(PERSONAL_PREFIX))
|
|
94682
94918
|
continue;
|
|
94683
94919
|
const skillName = ent.name.slice(PERSONAL_PREFIX.length);
|
|
94684
|
-
const skillPath =
|
|
94920
|
+
const skillPath = join94(skillsDir, ent.name);
|
|
94685
94921
|
let fileCount = 0;
|
|
94686
94922
|
let totalBytes = 0;
|
|
94687
94923
|
const walk2 = (sub) => {
|
|
94688
|
-
for (const e of
|
|
94924
|
+
for (const e of readdirSync36(sub, { withFileTypes: true })) {
|
|
94689
94925
|
if (e.isFile()) {
|
|
94690
94926
|
fileCount += 1;
|
|
94691
94927
|
try {
|
|
94692
|
-
totalBytes += statSync50(
|
|
94928
|
+
totalBytes += statSync50(join94(sub, e.name)).size;
|
|
94693
94929
|
} catch {}
|
|
94694
94930
|
} else if (e.isDirectory()) {
|
|
94695
|
-
walk2(
|
|
94931
|
+
walk2(join94(sub, e.name));
|
|
94696
94932
|
}
|
|
94697
94933
|
}
|
|
94698
94934
|
};
|
|
@@ -94731,11 +94967,11 @@ function registerSkillPersonalCommands(program3) {
|
|
|
94731
94967
|
// src/cli/self-improve-propose-skill.ts
|
|
94732
94968
|
import { createConnection as createConnection4 } from "node:net";
|
|
94733
94969
|
import { homedir as homedir53 } from "node:os";
|
|
94734
|
-
import { join as
|
|
94735
|
-
import { readFileSync as
|
|
94970
|
+
import { join as join95 } from "node:path";
|
|
94971
|
+
import { readFileSync as readFileSync82 } from "node:fs";
|
|
94736
94972
|
var IPC_CONNECT_TIMEOUT_MS = 5000;
|
|
94737
94973
|
function gatewaySocketPath() {
|
|
94738
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ?
|
|
94974
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join95(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join95(homedir53(), ".claude", "channels", "telegram", "gateway.sock"));
|
|
94739
94975
|
}
|
|
94740
94976
|
function fail5(msg, code = 1) {
|
|
94741
94977
|
console.error(msg);
|
|
@@ -94770,7 +95006,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94770
95006
|
fail5("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
|
|
94771
95007
|
let draft;
|
|
94772
95008
|
try {
|
|
94773
|
-
draft = JSON.parse(
|
|
95009
|
+
draft = JSON.parse(readFileSync82(opts.draft, "utf-8"));
|
|
94774
95010
|
} catch (e) {
|
|
94775
95011
|
fail5(`failed to read/parse --draft: ${e.message}`);
|
|
94776
95012
|
}
|
|
@@ -94807,9 +95043,9 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
94807
95043
|
init_esm();
|
|
94808
95044
|
init_helpers();
|
|
94809
95045
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
94810
|
-
import { existsSync as
|
|
95046
|
+
import { existsSync as existsSync95, readdirSync as readdirSync37, readFileSync as readFileSync83, statSync as statSync51 } from "node:fs";
|
|
94811
95047
|
import { homedir as homedir54 } from "node:os";
|
|
94812
|
-
import { join as
|
|
95048
|
+
import { join as join96, resolve as resolve56 } from "node:path";
|
|
94813
95049
|
var PERSONAL_PREFIX2 = "personal-";
|
|
94814
95050
|
var BUNDLED_SUBDIR = "_bundled";
|
|
94815
95051
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
@@ -94823,12 +95059,12 @@ function defaultBundledRoot2() {
|
|
|
94823
95059
|
return resolve56(homedir54(), ".switchroom/skills/_bundled");
|
|
94824
95060
|
}
|
|
94825
95061
|
function readSkillFrontmatter(skillDir) {
|
|
94826
|
-
const mdPath =
|
|
94827
|
-
if (!
|
|
95062
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
95063
|
+
if (!existsSync95(mdPath))
|
|
94828
95064
|
return null;
|
|
94829
95065
|
let content;
|
|
94830
95066
|
try {
|
|
94831
|
-
content =
|
|
95067
|
+
content = readFileSync83(mdPath, "utf-8");
|
|
94832
95068
|
} catch {
|
|
94833
95069
|
return null;
|
|
94834
95070
|
}
|
|
@@ -94856,7 +95092,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
94856
95092
|
return { fm: parsed };
|
|
94857
95093
|
}
|
|
94858
95094
|
function statSkillMd(skillDir) {
|
|
94859
|
-
const mdPath =
|
|
95095
|
+
const mdPath = join96(skillDir, "SKILL.md");
|
|
94860
95096
|
try {
|
|
94861
95097
|
const st = statSync51(mdPath);
|
|
94862
95098
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
@@ -94867,20 +95103,20 @@ function statSkillMd(skillDir) {
|
|
|
94867
95103
|
function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
94868
95104
|
if (!AGENT_NAME_RE3.test(agent))
|
|
94869
95105
|
return [];
|
|
94870
|
-
const skillsDir =
|
|
94871
|
-
if (!
|
|
95106
|
+
const skillsDir = join96(agentsRoot, agent, ".claude/skills");
|
|
95107
|
+
if (!existsSync95(skillsDir))
|
|
94872
95108
|
return [];
|
|
94873
95109
|
const out = [];
|
|
94874
95110
|
let entries;
|
|
94875
95111
|
try {
|
|
94876
|
-
entries =
|
|
95112
|
+
entries = readdirSync37(skillsDir);
|
|
94877
95113
|
} catch {
|
|
94878
95114
|
return [];
|
|
94879
95115
|
}
|
|
94880
95116
|
for (const ent of entries) {
|
|
94881
95117
|
if (!ent.startsWith(PERSONAL_PREFIX2))
|
|
94882
95118
|
continue;
|
|
94883
|
-
const dirPath =
|
|
95119
|
+
const dirPath = join96(skillsDir, ent);
|
|
94884
95120
|
try {
|
|
94885
95121
|
if (!statSync51(dirPath).isDirectory())
|
|
94886
95122
|
continue;
|
|
@@ -94906,12 +95142,12 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
94906
95142
|
return out;
|
|
94907
95143
|
}
|
|
94908
95144
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
94909
|
-
if (!
|
|
95145
|
+
if (!existsSync95(sharedRoot))
|
|
94910
95146
|
return [];
|
|
94911
95147
|
const out = [];
|
|
94912
95148
|
let entries;
|
|
94913
95149
|
try {
|
|
94914
|
-
entries =
|
|
95150
|
+
entries = readdirSync37(sharedRoot);
|
|
94915
95151
|
} catch {
|
|
94916
95152
|
return [];
|
|
94917
95153
|
}
|
|
@@ -94920,7 +95156,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94920
95156
|
continue;
|
|
94921
95157
|
if (ent.startsWith("."))
|
|
94922
95158
|
continue;
|
|
94923
|
-
const dirPath =
|
|
95159
|
+
const dirPath = join96(sharedRoot, ent);
|
|
94924
95160
|
try {
|
|
94925
95161
|
if (!statSync51(dirPath).isDirectory())
|
|
94926
95162
|
continue;
|
|
@@ -94944,19 +95180,19 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
94944
95180
|
return out;
|
|
94945
95181
|
}
|
|
94946
95182
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
94947
|
-
if (!
|
|
95183
|
+
if (!existsSync95(bundledRoot))
|
|
94948
95184
|
return [];
|
|
94949
95185
|
const out = [];
|
|
94950
95186
|
let entries;
|
|
94951
95187
|
try {
|
|
94952
|
-
entries =
|
|
95188
|
+
entries = readdirSync37(bundledRoot);
|
|
94953
95189
|
} catch {
|
|
94954
95190
|
return [];
|
|
94955
95191
|
}
|
|
94956
95192
|
for (const ent of entries) {
|
|
94957
95193
|
if (ent.startsWith("."))
|
|
94958
95194
|
continue;
|
|
94959
|
-
const dirPath =
|
|
95195
|
+
const dirPath = join96(bundledRoot, ent);
|
|
94960
95196
|
try {
|
|
94961
95197
|
if (!statSync51(dirPath).isDirectory())
|
|
94962
95198
|
continue;
|
|
@@ -95102,18 +95338,18 @@ init_source();
|
|
|
95102
95338
|
init_helpers();
|
|
95103
95339
|
init_operator_uid();
|
|
95104
95340
|
import {
|
|
95105
|
-
existsSync as
|
|
95106
|
-
mkdirSync as
|
|
95107
|
-
readdirSync as
|
|
95108
|
-
readFileSync as
|
|
95109
|
-
writeFileSync as
|
|
95341
|
+
existsSync as existsSync97,
|
|
95342
|
+
mkdirSync as mkdirSync55,
|
|
95343
|
+
readdirSync as readdirSync38,
|
|
95344
|
+
readFileSync as readFileSync85,
|
|
95345
|
+
writeFileSync as writeFileSync36,
|
|
95110
95346
|
statSync as statSync52,
|
|
95111
95347
|
lstatSync as lstatSync13,
|
|
95112
95348
|
realpathSync as realpathSync8,
|
|
95113
95349
|
copyFileSync as copyFileSync13
|
|
95114
95350
|
} from "node:fs";
|
|
95115
95351
|
import { homedir as homedir55 } from "node:os";
|
|
95116
|
-
import { join as
|
|
95352
|
+
import { join as join97 } from "node:path";
|
|
95117
95353
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
95118
95354
|
|
|
95119
95355
|
// src/cli/singleton-stale-cleanup.ts
|
|
@@ -95462,7 +95698,7 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir55()) {
|
|
|
95462
95698
|
return resolved;
|
|
95463
95699
|
}
|
|
95464
95700
|
function resolveHostdSkillsTarget(hostHome) {
|
|
95465
|
-
const skillsPath =
|
|
95701
|
+
const skillsPath = join97(hostHome, ".switchroom", "skills");
|
|
95466
95702
|
let st;
|
|
95467
95703
|
try {
|
|
95468
95704
|
st = lstatSync13(skillsPath);
|
|
@@ -95479,21 +95715,21 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
95479
95715
|
console.warn(`switchroom hostd install: ~/.switchroom/skills is a symlink whose target ` + `does not resolve (dangling) \u2014 skipping the skills bind mount. Bundled ` + `skills will be unavailable to rollout/update until the symlink is fixed.`);
|
|
95480
95716
|
return;
|
|
95481
95717
|
}
|
|
95482
|
-
if (!
|
|
95718
|
+
if (!existsSync97(target)) {
|
|
95483
95719
|
console.warn(`switchroom hostd install: ~/.switchroom/skills resolves to "${target}", ` + `which does not exist \u2014 skipping the skills bind mount. Bundled skills ` + `will be unavailable to rollout/update until the symlink target exists.`);
|
|
95484
95720
|
return;
|
|
95485
95721
|
}
|
|
95486
95722
|
return target;
|
|
95487
95723
|
}
|
|
95488
95724
|
function hostdDir() {
|
|
95489
|
-
return
|
|
95725
|
+
return join97(homedir55(), ".switchroom", "hostd");
|
|
95490
95726
|
}
|
|
95491
95727
|
function hostdComposePath() {
|
|
95492
|
-
return
|
|
95728
|
+
return join97(hostdDir(), "docker-compose.yml");
|
|
95493
95729
|
}
|
|
95494
95730
|
function backupExistingCompose() {
|
|
95495
95731
|
const p = hostdComposePath();
|
|
95496
|
-
if (!
|
|
95732
|
+
if (!existsSync97(p))
|
|
95497
95733
|
return null;
|
|
95498
95734
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95499
95735
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95526,7 +95762,7 @@ async function doInstall(opts, program3) {
|
|
|
95526
95762
|
}
|
|
95527
95763
|
const dir = hostdDir();
|
|
95528
95764
|
const composePath = hostdComposePath();
|
|
95529
|
-
|
|
95765
|
+
mkdirSync55(dir, { recursive: true });
|
|
95530
95766
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
95531
95767
|
const guard = checkDowngrade({
|
|
95532
95768
|
container: "switchroom-hostd",
|
|
@@ -95559,7 +95795,7 @@ async function doInstall(opts, program3) {
|
|
|
95559
95795
|
const bak = backupExistingCompose();
|
|
95560
95796
|
if (bak)
|
|
95561
95797
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95562
|
-
|
|
95798
|
+
writeFileSync36(composePath, yaml, "utf8");
|
|
95563
95799
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
95564
95800
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
95565
95801
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -95591,7 +95827,7 @@ function doStatus() {
|
|
|
95591
95827
|
const composeYml = hostdComposePath();
|
|
95592
95828
|
console.log(source_default.bold("switchroom-hostd"));
|
|
95593
95829
|
console.log("");
|
|
95594
|
-
if (!
|
|
95830
|
+
if (!existsSync97(composeYml)) {
|
|
95595
95831
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95596
95832
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
95597
95833
|
return;
|
|
@@ -95612,14 +95848,14 @@ function doStatus() {
|
|
|
95612
95848
|
} else {
|
|
95613
95849
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
95614
95850
|
}
|
|
95615
|
-
if (
|
|
95851
|
+
if (existsSync97(dir)) {
|
|
95616
95852
|
const entries = [];
|
|
95617
95853
|
try {
|
|
95618
|
-
for (const name of
|
|
95854
|
+
for (const name of readdirSync38(dir)) {
|
|
95619
95855
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
95620
95856
|
continue;
|
|
95621
|
-
const sockPath =
|
|
95622
|
-
if (
|
|
95857
|
+
const sockPath = join97(dir, name, "sock");
|
|
95858
|
+
if (existsSync97(sockPath)) {
|
|
95623
95859
|
const st = statSync52(sockPath);
|
|
95624
95860
|
if ((st.mode & 61440) === 49152) {
|
|
95625
95861
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -95638,7 +95874,7 @@ function doStatus() {
|
|
|
95638
95874
|
}
|
|
95639
95875
|
function doUninstall() {
|
|
95640
95876
|
const composeYml = hostdComposePath();
|
|
95641
|
-
if (!
|
|
95877
|
+
if (!existsSync97(composeYml)) {
|
|
95642
95878
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
95643
95879
|
return;
|
|
95644
95880
|
}
|
|
@@ -95662,12 +95898,12 @@ function registerHostdCommand(program3) {
|
|
|
95662
95898
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
95663
95899
|
hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
|
|
95664
95900
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
95665
|
-
if (!
|
|
95901
|
+
if (!existsSync97(logPath)) {
|
|
95666
95902
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
95667
95903
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
95668
95904
|
return;
|
|
95669
95905
|
}
|
|
95670
|
-
const raw =
|
|
95906
|
+
const raw = readFileSync85(logPath, "utf-8");
|
|
95671
95907
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
95672
95908
|
const filters = {
|
|
95673
95909
|
agent: opts.agent,
|
|
@@ -95710,9 +95946,9 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
95710
95946
|
init_source();
|
|
95711
95947
|
init_helpers();
|
|
95712
95948
|
init_operator_uid();
|
|
95713
|
-
import { chownSync as chownSync9, existsSync as
|
|
95949
|
+
import { chownSync as chownSync9, existsSync as existsSync98, mkdirSync as mkdirSync56, writeFileSync as writeFileSync37, copyFileSync as copyFileSync14 } from "node:fs";
|
|
95714
95950
|
import { homedir as homedir56 } from "node:os";
|
|
95715
|
-
import { join as
|
|
95951
|
+
import { join as join98 } from "node:path";
|
|
95716
95952
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
95717
95953
|
function resolveWebImageTag(explicitTag, release) {
|
|
95718
95954
|
if (explicitTag)
|
|
@@ -95812,14 +96048,14 @@ services:
|
|
|
95812
96048
|
`;
|
|
95813
96049
|
}
|
|
95814
96050
|
function webdDir() {
|
|
95815
|
-
return
|
|
96051
|
+
return join98(homedir56(), ".switchroom", "web");
|
|
95816
96052
|
}
|
|
95817
96053
|
function webdComposePath() {
|
|
95818
|
-
return
|
|
96054
|
+
return join98(webdDir(), "docker-compose.yml");
|
|
95819
96055
|
}
|
|
95820
96056
|
function backupExistingCompose2() {
|
|
95821
96057
|
const p = webdComposePath();
|
|
95822
|
-
if (!
|
|
96058
|
+
if (!existsSync98(p))
|
|
95823
96059
|
return null;
|
|
95824
96060
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
95825
96061
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -95844,7 +96080,7 @@ async function doInstall2(opts, program3) {
|
|
|
95844
96080
|
}
|
|
95845
96081
|
const dir = webdDir();
|
|
95846
96082
|
const composePath = webdComposePath();
|
|
95847
|
-
|
|
96083
|
+
mkdirSync56(dir, { recursive: true });
|
|
95848
96084
|
const cfg = getConfig(program3);
|
|
95849
96085
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
95850
96086
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -95879,7 +96115,7 @@ async function doInstall2(opts, program3) {
|
|
|
95879
96115
|
const bak = backupExistingCompose2();
|
|
95880
96116
|
if (bak)
|
|
95881
96117
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
95882
|
-
|
|
96118
|
+
writeFileSync37(composePath, yaml, "utf8");
|
|
95883
96119
|
try {
|
|
95884
96120
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
95885
96121
|
chownSync9(dir, operatorUid, operatorUid);
|
|
@@ -95921,7 +96157,7 @@ function doStatus2() {
|
|
|
95921
96157
|
const composeYml = webdComposePath();
|
|
95922
96158
|
console.log(source_default.bold("switchroom-web"));
|
|
95923
96159
|
console.log("");
|
|
95924
|
-
if (!
|
|
96160
|
+
if (!existsSync98(composeYml)) {
|
|
95925
96161
|
console.log(source_default.yellow(" compose: not installed"));
|
|
95926
96162
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
95927
96163
|
return;
|
|
@@ -95945,7 +96181,7 @@ function doStatus2() {
|
|
|
95945
96181
|
}
|
|
95946
96182
|
function doUninstall2() {
|
|
95947
96183
|
const composeYml = webdComposePath();
|
|
95948
|
-
if (!
|
|
96184
|
+
if (!existsSync98(composeYml)) {
|
|
95949
96185
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
95950
96186
|
return;
|
|
95951
96187
|
}
|
|
@@ -95975,9 +96211,9 @@ function registerWebdCommand(program3) {
|
|
|
95975
96211
|
// src/cli/host-repair.ts
|
|
95976
96212
|
init_source();
|
|
95977
96213
|
import { homedir as homedir57 } from "node:os";
|
|
95978
|
-
import { join as
|
|
96214
|
+
import { join as join99 } from "node:path";
|
|
95979
96215
|
var ARTIFACT_ALLOWLIST = {
|
|
95980
|
-
dockerComposePluginDir: (home2) =>
|
|
96216
|
+
dockerComposePluginDir: (home2) => join99(home2, ".docker", "cli-plugins", "docker-compose"),
|
|
95981
96217
|
stateSentinel: "/state"
|
|
95982
96218
|
};
|
|
95983
96219
|
function isStateBogusAutoDir(probe2) {
|
|
@@ -96131,7 +96367,7 @@ function applyMountRepairs(items, deps) {
|
|
|
96131
96367
|
function registerHostCommand(program3) {
|
|
96132
96368
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
96133
96369
|
host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
|
|
96134
|
-
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync:
|
|
96370
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync: readdirSync39 } = await import("node:fs");
|
|
96135
96371
|
const probe2 = {
|
|
96136
96372
|
lstat(path9) {
|
|
96137
96373
|
try {
|
|
@@ -96142,7 +96378,7 @@ function registerHostCommand(program3) {
|
|
|
96142
96378
|
},
|
|
96143
96379
|
readdir(path9) {
|
|
96144
96380
|
try {
|
|
96145
|
-
return
|
|
96381
|
+
return readdirSync39(path9);
|
|
96146
96382
|
} catch {
|
|
96147
96383
|
return null;
|
|
96148
96384
|
}
|