switchroom 0.18.3 → 0.18.7
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/agent-scheduler/index.js +3 -1
- package/dist/auth-broker/index.js +3 -1
- package/dist/cli/autoaccept-poll.js +140 -33
- package/dist/cli/notion-write-pretool.mjs +3 -1
- package/dist/cli/switchroom.js +386 -128
- package/dist/host-control/main.js +4 -2
- package/dist/vault/approvals/kernel-server.js +3 -1
- package/dist/vault/broker/server.js +38 -8
- package/package.json +3 -3
- package/profiles/_base/cron-session.sh.hbs +55 -16
- package/profiles/_base/start.sh.hbs +35 -16
- package/profiles/default/CLAUDE.md.hbs +1 -1
- package/skills/switchroom-release/SKILL.md +78 -0
- package/telegram-plugin/auth-snapshot-format.ts +15 -1
- package/telegram-plugin/dist/bridge/bridge.js +22 -0
- package/telegram-plugin/dist/gateway/gateway.js +2852 -1032
- package/telegram-plugin/dist/server.js +24 -0
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +438 -0
- package/telegram-plugin/gateway/approval-timeout-inbound-builders.ts +150 -0
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +68 -20
- package/telegram-plugin/gateway/gateway.ts +1331 -151
- package/telegram-plugin/gateway/inbound-spool.ts +2 -1
- package/telegram-plugin/gateway/inject-handler.test.ts +19 -0
- package/telegram-plugin/gateway/inject-handler.ts +17 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +44 -2
- package/telegram-plugin/gateway/ipc-server.ts +40 -0
- package/telegram-plugin/gateway/model-command.ts +212 -51
- package/telegram-plugin/gateway/pending-card-expiry.ts +98 -0
- package/telegram-plugin/gateway/pending-card-store.ts +173 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +12 -2
- package/telegram-plugin/gateway/resolve-person.ts +304 -0
- package/telegram-plugin/gateway/resume-inbound-builder.ts +240 -2
- package/telegram-plugin/gateway/session-model-source.ts +73 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +21 -1
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +24 -1
- package/telegram-plugin/hooks/silent-end-scan.mjs +164 -40
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +30 -7
- package/telegram-plugin/model-label.ts +69 -0
- package/telegram-plugin/operator-events.ts +45 -0
- package/telegram-plugin/pending-work-progress.ts +42 -7
- package/telegram-plugin/permission-diff.ts +128 -0
- package/telegram-plugin/quota-bar-format.ts +360 -0
- package/telegram-plugin/registry/subagents-schema.ts +80 -1
- package/telegram-plugin/registry/subagents.test.ts +90 -0
- package/telegram-plugin/session-tail.ts +28 -0
- package/telegram-plugin/silent-end.ts +49 -4
- package/telegram-plugin/subagent-watcher.ts +249 -46
- package/telegram-plugin/tests/always-allow-persist-queue.test.ts +529 -0
- package/telegram-plugin/tests/approval-timeout-inbound-builders.test.ts +94 -0
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +21 -0
- package/telegram-plugin/tests/button-tap-turn-gated.test.ts +263 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +85 -27
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +4 -2
- package/telegram-plugin/tests/ipc-server-query-pending-permission.test.ts +157 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +8 -5
- package/telegram-plugin/tests/model-command.test.ts +202 -42
- package/telegram-plugin/tests/model-label.test.ts +64 -0
- package/telegram-plugin/tests/operator-events.test.ts +17 -0
- package/telegram-plugin/tests/pending-card-durability-wiring.test.ts +202 -0
- package/telegram-plugin/tests/pending-card-expiry.test.ts +190 -0
- package/telegram-plugin/tests/pending-card-store.test.ts +173 -0
- package/telegram-plugin/tests/pending-work-progress.test.ts +116 -3
- package/telegram-plugin/tests/permission-diff.test.ts +111 -0
- package/telegram-plugin/tests/quota-bar-format.test.ts +444 -0
- package/telegram-plugin/tests/resolve-person.test.ts +290 -0
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +286 -0
- package/telegram-plugin/tests/session-model-source.test.ts +67 -0
- package/telegram-plugin/tests/session-tail.test.ts +64 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +53 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +138 -0
- package/telegram-plugin/tests/silent-end.test.ts +46 -1
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +39 -0
- package/telegram-plugin/tests/subagent-watcher-boot-promotion-replay.test.ts +107 -4
- package/telegram-plugin/tests/subagent-watcher-handback-gaps.test.ts +42 -4
- package/telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts +47 -0
- package/telegram-plugin/tests/subagent-watcher-terminated-ids-cap.test.ts +150 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +115 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +37 -0
- package/telegram-plugin/tests/typing-wrap.test.ts +23 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +19 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +108 -0
- package/telegram-plugin/tests/worker-feed-dispatch.test.ts +126 -0
- package/telegram-plugin/tool-activity-summary.ts +22 -2
- package/telegram-plugin/typing-wrap.ts +72 -25
- package/telegram-plugin/worker-activity-feed.ts +229 -15
- package/profiles/default/CLAUDE.md +0 -116
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -13741,6 +13741,7 @@ var init_schema = __esm(() => {
|
|
|
13741
13741
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
13742
13742
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
13743
13743
|
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale."),
|
|
13744
|
+
boot_resume: exports_external.enum(["always", "in-flight", "never"]).optional().describe("How the gateway auto-resumes a turn that was IN FLIGHT when the " + "agent restarted. 'in-flight' (default) resumes genuinely " + "interrupted work even after a deliberate/operator restart \u2014 a " + "sanctioned restart landing mid-turn no longer silently drops the " + "work. 'always' forces resume unconditionally (same as the " + "SWITCHROOM_BOOT_RESUME_ALWAYS=1 escape hatch). 'never' is the " + "quota-saving posture: don't auto-replay work across a clean " + "restart \u2014 but the user is STILL sent a passive notice of what was " + "in flight (silence is never used). Independent of the at-most-once " + "resume ledger and the bounded resume-chain loop-guard, which always " + "apply. Threaded to the gateway as SWITCHROOM_BOOT_RESUME."),
|
|
13744
13745
|
session_retention_max_count: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): keep at most this many " + "newest session transcripts under .claude/projects; older ones " + "past both this count and the age bound are pruned by the Stop " + "hook. The newest sessions (and the handoff source) are always " + "kept. Default 20; set 0 to disable the count bound."),
|
|
13745
13746
|
session_retention_max_age_days: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): prune session transcripts " + "older than this many days (a file is deleted only when it is BOTH " + "over the count bound and older than this). Default 30; set 0 to " + "disable the age bound.")
|
|
13746
13747
|
}).optional();
|
|
@@ -14205,7 +14206,8 @@ var init_schema = __esm(() => {
|
|
|
14205
14206
|
UserSchema = exports_external.object({
|
|
14206
14207
|
name: exports_external.string().optional().describe("Display name for the user."),
|
|
14207
14208
|
telegram_ids: exports_external.array(exports_external.string()).min(1).describe("Telegram username(s) and/or numeric user id(s) identifying this user " + "(a leading @ is optional). Matched against the message sender for " + "per-speaker memory routing."),
|
|
14208
|
-
profile_bank: exports_external.string().describe("Hindsight bank holding this user's memory profile (author via " + "`switchroom memory profile add <bank> ...`).")
|
|
14209
|
+
profile_bank: exports_external.string().describe("Hindsight bank holding this user's memory profile (author via " + "`switchroom memory profile add <bank> ...`)."),
|
|
14210
|
+
person_id: exports_external.string().optional().describe("Free-text display name projected into the inbound `<channel>` " + 'tag\'s `user` attribute (e.g. "Lisa") so an agent can greet by ' + "name instead of only seeing the raw Telegram id/username. NOT a " + "stable identity system \u2014 just a label. Resolution is boot-time-only " + "(no hot-reload; a config change needs an agent restart) and " + "chat-scoped (a resolved name is only shown in a chat/group the " + "person is actually a member of, per that chat's access.json " + "membership \u2014 never broadcast into every chat the agent operates " + "in). Keep this broadly safe to show, the same discipline as " + "picking a `profile_bank` name: there is no automated enforcement " + "that a `person_id` stays safe if a group's membership changes " + "later \u2014 see docs/configuration.md.")
|
|
14209
14211
|
});
|
|
14210
14212
|
SwitchroomConfigSchema = exports_external.object({
|
|
14211
14213
|
switchroom: exports_external.object({
|
|
@@ -15177,6 +15179,16 @@ function resolveUsers(config, agentName) {
|
|
|
15177
15179
|
]);
|
|
15178
15180
|
return { senderBanks, additionalBanks };
|
|
15179
15181
|
}
|
|
15182
|
+
function resolvePersonEntries(config) {
|
|
15183
|
+
const users = config.users ?? {};
|
|
15184
|
+
const entries = [];
|
|
15185
|
+
for (const [key, u] of Object.entries(users)) {
|
|
15186
|
+
if (!u.person_id)
|
|
15187
|
+
continue;
|
|
15188
|
+
entries.push({ key, person_id: u.person_id, telegram_ids: u.telegram_ids });
|
|
15189
|
+
}
|
|
15190
|
+
return entries;
|
|
15191
|
+
}
|
|
15180
15192
|
var init_users = __esm(() => {
|
|
15181
15193
|
init_merge();
|
|
15182
15194
|
});
|
|
@@ -26933,6 +26945,7 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
|
|
|
26933
26945
|
handoffEnabled: agentConfig.session_continuity?.enabled !== false,
|
|
26934
26946
|
resumeMode: agentConfig.session_continuity?.resume_mode ?? "handoff",
|
|
26935
26947
|
resumeMaxBytes: agentConfig.session_continuity?.resume_max_bytes ?? 2000000,
|
|
26948
|
+
bootResumeMode: agentConfig.session_continuity?.boot_resume ?? "in-flight",
|
|
26936
26949
|
resumeModeHasContinuePath: (() => {
|
|
26937
26950
|
const mode = agentConfig.session_continuity?.resume_mode ?? "handoff";
|
|
26938
26951
|
return mode === "auto" || mode === "continue";
|
|
@@ -27360,6 +27373,7 @@ This file is auto-maintained. Do not edit manually.
|
|
|
27360
27373
|
}, created, skipped, 384);
|
|
27361
27374
|
writeIfMissing(join10(agentDir, "telegram", "access.json"), () => buildAccessJson2(agentConfig, telegramConfig, topicId, userId), created, skipped, 384);
|
|
27362
27375
|
reconcileConfiguredGroup(join10(agentDir, "telegram", "access.json"), agentConfig, telegramConfig);
|
|
27376
|
+
writeFileSyncIfChanged(join10(agentDir, "telegram", "people.json"), buildPeopleJson(switchroomConfig), 384);
|
|
27363
27377
|
if (agentConfig.subagents) {
|
|
27364
27378
|
const agentsDir2 = join10(agentDir, ".claude", "agents");
|
|
27365
27379
|
mkdirSync10(agentsDir2, { recursive: true });
|
|
@@ -28034,7 +28048,8 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
|
|
|
28034
28048
|
sessionMaxTurns: agentConfig.session?.max_turns,
|
|
28035
28049
|
handoffEnabled: agentConfig.session_continuity?.enabled !== false,
|
|
28036
28050
|
resumeMode: agentConfig.session_continuity?.resume_mode ?? "handoff",
|
|
28037
|
-
resumeMaxBytes: agentConfig.session_continuity?.resume_max_bytes ?? 2000000
|
|
28051
|
+
resumeMaxBytes: agentConfig.session_continuity?.resume_max_bytes ?? 2000000,
|
|
28052
|
+
bootResumeMode: agentConfig.session_continuity?.boot_resume ?? "in-flight"
|
|
28038
28053
|
};
|
|
28039
28054
|
const beforeStartSh = existsSync15(startShPath) ? readFileSync13(startShPath, "utf-8") : "";
|
|
28040
28055
|
const afterStartSh = renderTemplate(join10(basePath, "start.sh.hbs"), startShContext);
|
|
@@ -28582,6 +28597,11 @@ function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
|
|
|
28582
28597
|
renameSync4(tmp, accessPath);
|
|
28583
28598
|
console.log(source_default.green(` registered supergroup ${forumChatId} in access.json ` + `(responds to all topics; requireMention=false)`));
|
|
28584
28599
|
}
|
|
28600
|
+
function buildPeopleJson(switchroomConfig) {
|
|
28601
|
+
const entries = switchroomConfig ? resolvePersonEntries(switchroomConfig) : [];
|
|
28602
|
+
return JSON.stringify({ entries }, null, 2) + `
|
|
28603
|
+
`;
|
|
28604
|
+
}
|
|
28585
28605
|
function buildAccessJson2(agentConfig, telegramConfig, resolvedTopicId, userId) {
|
|
28586
28606
|
const allowFrom = userId ? [String(userId)] : [];
|
|
28587
28607
|
if (allowFrom.length === 0) {
|
|
@@ -30030,7 +30050,25 @@ import { chownSync as chownSync2 } from "node:fs";
|
|
|
30030
30050
|
import { mkdir, readFile, writeFile, rename, copyFile } from "node:fs/promises";
|
|
30031
30051
|
import { homedir as homedir6 } from "node:os";
|
|
30032
30052
|
import { basename as basename4, dirname as dirname6, join as join13 } from "node:path";
|
|
30033
|
-
|
|
30053
|
+
function agentHadLiteLLMRouting(composeContent, agentName) {
|
|
30054
|
+
const lines = composeContent.split(`
|
|
30055
|
+
`);
|
|
30056
|
+
const header = ` agent-${agentName}:`;
|
|
30057
|
+
let i = lines.indexOf(header);
|
|
30058
|
+
if (i < 0)
|
|
30059
|
+
return false;
|
|
30060
|
+
for (i = i + 1;i < lines.length; i++) {
|
|
30061
|
+
const line = lines[i];
|
|
30062
|
+
if (/^ {2}\S/.test(line))
|
|
30063
|
+
break;
|
|
30064
|
+
if (/^\S/.test(line))
|
|
30065
|
+
break;
|
|
30066
|
+
if (/^\s+SWITCHROOM_LITELLM:\s*"1"\s*$/.test(line))
|
|
30067
|
+
return true;
|
|
30068
|
+
}
|
|
30069
|
+
return false;
|
|
30070
|
+
}
|
|
30071
|
+
async function resolveLiteLLMConfirmedAgents(config, previousCompose, deps) {
|
|
30034
30072
|
const confirmed = new Set;
|
|
30035
30073
|
const topEnabled = config.litellm?.enabled ?? false;
|
|
30036
30074
|
const optedIn = [];
|
|
@@ -30045,16 +30083,52 @@ async function resolveLiteLLMConfirmedAgents(config) {
|
|
|
30045
30083
|
}
|
|
30046
30084
|
if (optedIn.length === 0)
|
|
30047
30085
|
return confirmed;
|
|
30048
|
-
|
|
30049
|
-
|
|
30050
|
-
|
|
30051
|
-
|
|
30052
|
-
|
|
30053
|
-
|
|
30054
|
-
confirmed.add(name);
|
|
30055
|
-
} catch {}
|
|
30086
|
+
let getKey = deps?.getKey ?? null;
|
|
30087
|
+
if (!getKey) {
|
|
30088
|
+
try {
|
|
30089
|
+
({ getViaBrokerStructured: getKey } = await Promise.resolve().then(() => (init_client(), exports_client)));
|
|
30090
|
+
} catch {
|
|
30091
|
+
getKey = null;
|
|
30056
30092
|
}
|
|
30057
|
-
}
|
|
30093
|
+
}
|
|
30094
|
+
const unreachableAgents = [];
|
|
30095
|
+
for (const name of optedIn) {
|
|
30096
|
+
if (!getKey) {
|
|
30097
|
+
unreachableAgents.push(name);
|
|
30098
|
+
continue;
|
|
30099
|
+
}
|
|
30100
|
+
try {
|
|
30101
|
+
const r = await getKey(`litellm/${name}/api-key`);
|
|
30102
|
+
if (r.kind === "ok") {
|
|
30103
|
+
confirmed.add(name);
|
|
30104
|
+
} else if (r.kind === "unreachable") {
|
|
30105
|
+
unreachableAgents.push(name);
|
|
30106
|
+
} else if (r.kind === "denied" && r.code !== "DENIED") {
|
|
30107
|
+
unreachableAgents.push(name);
|
|
30108
|
+
}
|
|
30109
|
+
} catch {
|
|
30110
|
+
unreachableAgents.push(name);
|
|
30111
|
+
}
|
|
30112
|
+
}
|
|
30113
|
+
if (unreachableAgents.length > 0) {
|
|
30114
|
+
const preserved = [];
|
|
30115
|
+
const couldNotPreserve = [];
|
|
30116
|
+
for (const name of unreachableAgents) {
|
|
30117
|
+
if (previousCompose && agentHadLiteLLMRouting(previousCompose, name)) {
|
|
30118
|
+
confirmed.add(name);
|
|
30119
|
+
preserved.push(name);
|
|
30120
|
+
} else {
|
|
30121
|
+
couldNotPreserve.push(name);
|
|
30122
|
+
}
|
|
30123
|
+
}
|
|
30124
|
+
console.warn(`switchroom: vault-broker unreachable or unable to answer (locked/internal) while resolving LiteLLM key status for ${unreachableAgents.length} opted-in agent(s): ${unreachableAgents.join(", ")}. Refusing to silently strip proxy routing (that would boot them on untracked direct OAuth). Re-run \`switchroom apply\` once the broker is back to reconcile.`);
|
|
30125
|
+
if (preserved.length > 0) {
|
|
30126
|
+
console.warn(`switchroom: PRESERVED existing proxy routing from the on-disk compose for: ${preserved.join(", ")}.`);
|
|
30127
|
+
}
|
|
30128
|
+
if (couldNotPreserve.length > 0) {
|
|
30129
|
+
console.warn(`switchroom: no prior routing verdict on disk for: ${couldNotPreserve.join(", ")} ` + `\u2014 leaving them unrouted (they were not previously routed).`);
|
|
30130
|
+
}
|
|
30131
|
+
}
|
|
30058
30132
|
return confirmed;
|
|
30059
30133
|
}
|
|
30060
30134
|
function resolveHostSwitchroomConfigPath(rawPath) {
|
|
@@ -30092,7 +30166,13 @@ async function computeComposeContent(opts) {
|
|
|
30092
30166
|
const imageTag = resolveImageTag(release);
|
|
30093
30167
|
const operatorUid = resolveOperatorUid();
|
|
30094
30168
|
const resolvedConfigPath = opts.switchroomConfigPath !== undefined ? resolveHostSwitchroomConfigPath(opts.switchroomConfigPath) : undefined;
|
|
30095
|
-
|
|
30169
|
+
let previous = null;
|
|
30170
|
+
try {
|
|
30171
|
+
previous = await readFile(opts.composePath, "utf8");
|
|
30172
|
+
} catch {
|
|
30173
|
+
previous = null;
|
|
30174
|
+
}
|
|
30175
|
+
const litellmConfirmedAgents = await resolveLiteLLMConfirmedAgents(opts.config, previous);
|
|
30096
30176
|
const content = generateCompose({
|
|
30097
30177
|
config: opts.config,
|
|
30098
30178
|
imageTag,
|
|
@@ -30104,12 +30184,6 @@ async function computeComposeContent(opts) {
|
|
|
30104
30184
|
switchroomConfigPath: resolvedConfigPath,
|
|
30105
30185
|
operatorUid
|
|
30106
30186
|
});
|
|
30107
|
-
let previous = null;
|
|
30108
|
-
try {
|
|
30109
|
-
previous = await readFile(opts.composePath, "utf8");
|
|
30110
|
-
} catch {
|
|
30111
|
-
previous = null;
|
|
30112
|
-
}
|
|
30113
30187
|
const previousImageTag = previous ? AGENT_IMAGE_TAG_RE.exec(previous)?.[1] ?? null : null;
|
|
30114
30188
|
return { content, imageTag, previous, previousImageTag };
|
|
30115
30189
|
}
|
|
@@ -35505,7 +35579,7 @@ function formatForCli(entries, opts = {}) {
|
|
|
35505
35579
|
var init_audit_reader = () => {};
|
|
35506
35580
|
|
|
35507
35581
|
// node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
35508
|
-
import { dirname as
|
|
35582
|
+
import { dirname as dirname13, posix, sep as sep2 } from "path";
|
|
35509
35583
|
function createModulerModifier() {
|
|
35510
35584
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
35511
35585
|
return async (frames) => {
|
|
@@ -35514,7 +35588,7 @@ function createModulerModifier() {
|
|
|
35514
35588
|
return frames;
|
|
35515
35589
|
};
|
|
35516
35590
|
}
|
|
35517
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
35591
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname13(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
35518
35592
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
35519
35593
|
return (filename) => {
|
|
35520
35594
|
if (!filename)
|
|
@@ -40130,7 +40204,7 @@ import {
|
|
|
40130
40204
|
readFileSync as readFileSync43,
|
|
40131
40205
|
writeFileSync as writeFileSync23
|
|
40132
40206
|
} from "node:fs";
|
|
40133
|
-
import { dirname as
|
|
40207
|
+
import { dirname as dirname14 } from "node:path";
|
|
40134
40208
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
40135
40209
|
function telemetryDisabled() {
|
|
40136
40210
|
const v = process.env.SWITCHROOM_TELEMETRY_DISABLED;
|
|
@@ -40152,7 +40226,7 @@ function getDistinctId() {
|
|
|
40152
40226
|
const id = randomUUID3();
|
|
40153
40227
|
cachedDistinctId = id;
|
|
40154
40228
|
try {
|
|
40155
|
-
mkdirSync26(
|
|
40229
|
+
mkdirSync26(dirname14(path4), { recursive: true });
|
|
40156
40230
|
writeFileSync23(path4, id, "utf-8");
|
|
40157
40231
|
} catch {}
|
|
40158
40232
|
return id;
|
|
@@ -40399,7 +40473,7 @@ import {
|
|
|
40399
40473
|
readFileSync as readFileSync52,
|
|
40400
40474
|
readdirSync as readdirSync19
|
|
40401
40475
|
} from "node:fs";
|
|
40402
|
-
import { dirname as
|
|
40476
|
+
import { dirname as dirname17, join as join50 } from "node:path";
|
|
40403
40477
|
import { execSync as execSync2 } from "node:child_process";
|
|
40404
40478
|
function locateManifestPath() {
|
|
40405
40479
|
let dir = import.meta.dirname;
|
|
@@ -40407,7 +40481,7 @@ function locateManifestPath() {
|
|
|
40407
40481
|
const candidate = join50(dir, "dependencies.json");
|
|
40408
40482
|
if (existsSync57(candidate))
|
|
40409
40483
|
return candidate;
|
|
40410
|
-
dir =
|
|
40484
|
+
dir = dirname17(dir);
|
|
40411
40485
|
}
|
|
40412
40486
|
return null;
|
|
40413
40487
|
}
|
|
@@ -43578,7 +43652,7 @@ import {
|
|
|
43578
43652
|
readdirSync as readdirSync21,
|
|
43579
43653
|
statSync as statSync26
|
|
43580
43654
|
} from "node:fs";
|
|
43581
|
-
import { dirname as
|
|
43655
|
+
import { dirname as dirname18, join as join62, resolve as resolve37 } from "node:path";
|
|
43582
43656
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
43583
43657
|
function findInNvm(bin) {
|
|
43584
43658
|
const nvmRoot = join62(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -45060,7 +45134,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
45060
45134
|
detail: "skipped (MFF_API_URL not set)"
|
|
45061
45135
|
};
|
|
45062
45136
|
}
|
|
45063
|
-
const credDir =
|
|
45137
|
+
const credDir = dirname18(envPath);
|
|
45064
45138
|
const authScript = join62(credDir, "claude-auth.py");
|
|
45065
45139
|
if (!existsSync62(authScript)) {
|
|
45066
45140
|
return {
|
|
@@ -48866,6 +48940,7 @@ var init_install_detect = () => {};
|
|
|
48866
48940
|
// src/litellm/provision.ts
|
|
48867
48941
|
var exports_provision = {};
|
|
48868
48942
|
__export(exports_provision, {
|
|
48943
|
+
validateKey: () => validateKey,
|
|
48869
48944
|
ensureTeam: () => ensureTeam,
|
|
48870
48945
|
ensureKey: () => ensureKey,
|
|
48871
48946
|
LiteLLMProvisionError: () => LiteLLMProvisionError
|
|
@@ -48887,6 +48962,12 @@ function looksLikeAlreadyExists(status, body) {
|
|
|
48887
48962
|
return true;
|
|
48888
48963
|
return false;
|
|
48889
48964
|
}
|
|
48965
|
+
function looksLikeUnknownKey(status, body) {
|
|
48966
|
+
if (status !== 400 && status !== 401 && status !== 403 && status !== 404)
|
|
48967
|
+
return false;
|
|
48968
|
+
const t = body.toLowerCase();
|
|
48969
|
+
return t.includes("not found") || t.includes("does not exist") || t.includes("no such key") || t.includes("no key found");
|
|
48970
|
+
}
|
|
48890
48971
|
async function ensureTeam(baseUrl, masterKey, teamName, fetchFn = fetch) {
|
|
48891
48972
|
const url = `${normalizeBase(baseUrl)}/team/new`;
|
|
48892
48973
|
let resp;
|
|
@@ -48906,7 +48987,7 @@ async function ensureTeam(baseUrl, masterKey, teamName, fetchFn = fetch) {
|
|
|
48906
48987
|
return;
|
|
48907
48988
|
throw new LiteLLMProvisionError(`LiteLLM /team/new returned ${resp.status}`, resp.status, body);
|
|
48908
48989
|
}
|
|
48909
|
-
async function
|
|
48990
|
+
async function generateKeyOnce(opts, fetchFn) {
|
|
48910
48991
|
const url = `${normalizeBase(opts.baseUrl)}/key/generate`;
|
|
48911
48992
|
const payload = {
|
|
48912
48993
|
key_alias: opts.alias
|
|
@@ -48926,23 +49007,129 @@ async function ensureKey(opts, fetchFn = fetch) {
|
|
|
48926
49007
|
body: JSON.stringify(payload)
|
|
48927
49008
|
});
|
|
48928
49009
|
} catch (err) {
|
|
48929
|
-
|
|
49010
|
+
return {
|
|
49011
|
+
kind: "error",
|
|
49012
|
+
error: new LiteLLMProvisionError(`LiteLLM /key/generate request failed: ${err.message}`)
|
|
49013
|
+
};
|
|
48930
49014
|
}
|
|
48931
49015
|
if (!resp.ok) {
|
|
48932
49016
|
const body = await safeText(resp);
|
|
48933
|
-
|
|
49017
|
+
if (looksLikeAlreadyExists(resp.status, body)) {
|
|
49018
|
+
return { kind: "duplicate-alias", status: resp.status, body };
|
|
49019
|
+
}
|
|
49020
|
+
return {
|
|
49021
|
+
kind: "error",
|
|
49022
|
+
error: new LiteLLMProvisionError(`LiteLLM /key/generate returned ${resp.status}`, resp.status, body)
|
|
49023
|
+
};
|
|
48934
49024
|
}
|
|
48935
49025
|
let json;
|
|
48936
49026
|
try {
|
|
48937
49027
|
json = await resp.json();
|
|
48938
49028
|
} catch (err) {
|
|
48939
|
-
|
|
49029
|
+
return {
|
|
49030
|
+
kind: "error",
|
|
49031
|
+
error: new LiteLLMProvisionError(`LiteLLM /key/generate returned non-JSON body: ${err.message}`, resp.status)
|
|
49032
|
+
};
|
|
48940
49033
|
}
|
|
48941
49034
|
const key = json?.key;
|
|
48942
49035
|
if (typeof key !== "string" || key.length === 0) {
|
|
48943
|
-
|
|
49036
|
+
return {
|
|
49037
|
+
kind: "error",
|
|
49038
|
+
error: new LiteLLMProvisionError(`LiteLLM /key/generate response missing a "key" field`, resp.status, JSON.stringify(json))
|
|
49039
|
+
};
|
|
48944
49040
|
}
|
|
48945
|
-
return { key };
|
|
49041
|
+
return { kind: "ok", key };
|
|
49042
|
+
}
|
|
49043
|
+
async function resolveTokensForAlias(baseUrl, masterKey, alias, fetchFn) {
|
|
49044
|
+
const url = `${normalizeBase(baseUrl)}/key/list?key_alias=${encodeURIComponent(alias)}` + `&return_full_object=true&include_team_keys=true`;
|
|
49045
|
+
let resp;
|
|
49046
|
+
try {
|
|
49047
|
+
resp = await fetchFn(url, { method: "GET", headers: authHeaders3(masterKey) });
|
|
49048
|
+
} catch {
|
|
49049
|
+
return [];
|
|
49050
|
+
}
|
|
49051
|
+
if (!resp.ok)
|
|
49052
|
+
return [];
|
|
49053
|
+
let json;
|
|
49054
|
+
try {
|
|
49055
|
+
json = await resp.json();
|
|
49056
|
+
} catch {
|
|
49057
|
+
return [];
|
|
49058
|
+
}
|
|
49059
|
+
const keys = json?.keys;
|
|
49060
|
+
if (!Array.isArray(keys))
|
|
49061
|
+
return [];
|
|
49062
|
+
const tokens = [];
|
|
49063
|
+
for (const k of keys) {
|
|
49064
|
+
if (typeof k === "string" && k.length > 0)
|
|
49065
|
+
tokens.push(k);
|
|
49066
|
+
else if (k && typeof k === "object") {
|
|
49067
|
+
const tok = k.token;
|
|
49068
|
+
const raw = k.key;
|
|
49069
|
+
if (typeof tok === "string" && tok.length > 0)
|
|
49070
|
+
tokens.push(tok);
|
|
49071
|
+
else if (typeof raw === "string" && raw.length > 0)
|
|
49072
|
+
tokens.push(raw);
|
|
49073
|
+
}
|
|
49074
|
+
}
|
|
49075
|
+
return tokens;
|
|
49076
|
+
}
|
|
49077
|
+
async function deleteOrphanedAlias(opts, fetchFn, log) {
|
|
49078
|
+
const tokens = await resolveTokensForAlias(opts.baseUrl, opts.masterKey, opts.alias, fetchFn);
|
|
49079
|
+
const body = { key_aliases: [opts.alias] };
|
|
49080
|
+
if (tokens.length > 0)
|
|
49081
|
+
body.keys = tokens;
|
|
49082
|
+
const manual = `Manual remediation: delete the key with alias '${opts.alias}' in the LiteLLM ` + `admin UI, or run ` + `\`curl -X POST "${normalizeBase(opts.baseUrl)}/key/delete" -H "Authorization: Bearer <master-key>" ` + `-H "content-type: application/json" -d '{"key_aliases":["${opts.alias}"]}'\`, ` + `then re-run \`switchroom apply\`.`;
|
|
49083
|
+
const url = `${normalizeBase(opts.baseUrl)}/key/delete`;
|
|
49084
|
+
let resp;
|
|
49085
|
+
try {
|
|
49086
|
+
resp = await fetchFn(url, {
|
|
49087
|
+
method: "POST",
|
|
49088
|
+
headers: authHeaders3(opts.masterKey),
|
|
49089
|
+
body: JSON.stringify(body)
|
|
49090
|
+
});
|
|
49091
|
+
} catch (err) {
|
|
49092
|
+
throw new LiteLLMProvisionError(`LiteLLM /key/delete request failed while recovering orphaned alias ` + `'${opts.alias}': ${err.message}. ${manual}`);
|
|
49093
|
+
}
|
|
49094
|
+
if (!resp.ok) {
|
|
49095
|
+
const errBody = await safeText(resp);
|
|
49096
|
+
throw new LiteLLMProvisionError(`LiteLLM /key/delete returned ${resp.status} while recovering orphaned ` + `alias '${opts.alias}'. ${manual}`, resp.status, errBody);
|
|
49097
|
+
}
|
|
49098
|
+
log(`litellm: deleted orphaned key(s) for alias '${opts.alias}'` + (tokens.length > 0 ? ` (${tokens.length} token(s) resolved)` : "") + ` \u2014 regenerating`);
|
|
49099
|
+
}
|
|
49100
|
+
async function ensureKey(opts, fetchFn = fetch) {
|
|
49101
|
+
const log = opts.log ?? (() => {});
|
|
49102
|
+
const first = await generateKeyOnce(opts, fetchFn);
|
|
49103
|
+
if (first.kind === "ok")
|
|
49104
|
+
return { key: first.key };
|
|
49105
|
+
if (first.kind === "error")
|
|
49106
|
+
throw first.error;
|
|
49107
|
+
log(`litellm: key_alias '${opts.alias}' already exists in LiteLLM but the vault ` + `has no recoverable key (orphaned by a prior failed provision) \u2014 ` + `self-healing: deleting the orphan and regenerating`);
|
|
49108
|
+
await deleteOrphanedAlias(opts, fetchFn, log);
|
|
49109
|
+
const second = await generateKeyOnce(opts, fetchFn);
|
|
49110
|
+
if (second.kind === "ok") {
|
|
49111
|
+
log(`litellm: regenerated key for alias '${opts.alias}' after orphan recovery`);
|
|
49112
|
+
return { key: second.key };
|
|
49113
|
+
}
|
|
49114
|
+
if (second.kind === "duplicate-alias") {
|
|
49115
|
+
throw new LiteLLMProvisionError(`LiteLLM /key/generate still reports alias '${opts.alias}' already exists ` + `after deleting the orphan \u2014 the delete did not take effect. Manual ` + `remediation: remove the key with alias '${opts.alias}' via the LiteLLM ` + `admin UI (or POST /key/delete {"key_aliases":["${opts.alias}"]}) then ` + `re-run \`switchroom apply\`.`, second.status, second.body);
|
|
49116
|
+
}
|
|
49117
|
+
throw second.error;
|
|
49118
|
+
}
|
|
49119
|
+
async function validateKey(opts, fetchFn = fetch) {
|
|
49120
|
+
const url = `${normalizeBase(opts.baseUrl)}/key/info?key=${encodeURIComponent(opts.key)}`;
|
|
49121
|
+
let resp;
|
|
49122
|
+
try {
|
|
49123
|
+
resp = await fetchFn(url, { method: "GET", headers: authHeaders3(opts.masterKey) });
|
|
49124
|
+
} catch (err) {
|
|
49125
|
+
return { kind: "unreachable", detail: err.message };
|
|
49126
|
+
}
|
|
49127
|
+
if (resp.ok)
|
|
49128
|
+
return { kind: "valid" };
|
|
49129
|
+
const body = await safeText(resp);
|
|
49130
|
+
if (looksLikeUnknownKey(resp.status, body))
|
|
49131
|
+
return { kind: "unknown" };
|
|
49132
|
+
return { kind: "unreachable", detail: `HTTP ${resp.status}: ${body.slice(0, 200)}` };
|
|
48946
49133
|
}
|
|
48947
49134
|
async function safeText(resp) {
|
|
48948
49135
|
try {
|
|
@@ -48983,7 +49170,7 @@ __export(exports_voice_sidecar_token, {
|
|
|
48983
49170
|
});
|
|
48984
49171
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
48985
49172
|
import { chmodSync as chmodSync11, chownSync as chownSync7, existsSync as existsSync81, mkdirSync as mkdirSync45, readFileSync as readFileSync68, rmSync as rmSync17, writeFileSync as writeFileSync37 } from "node:fs";
|
|
48986
|
-
import { dirname as
|
|
49173
|
+
import { dirname as dirname27 } from "node:path";
|
|
48987
49174
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
48988
49175
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
48989
49176
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
@@ -49037,7 +49224,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
49037
49224
|
if (!token)
|
|
49038
49225
|
return;
|
|
49039
49226
|
try {
|
|
49040
|
-
mkdirSync45(
|
|
49227
|
+
mkdirSync45(dirname27(envPath), { recursive: true });
|
|
49041
49228
|
let body = "";
|
|
49042
49229
|
try {
|
|
49043
49230
|
if (existsSync81(envPath))
|
|
@@ -49099,11 +49286,11 @@ __export(exports_apply, {
|
|
|
49099
49286
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
|
|
49100
49287
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
49101
49288
|
});
|
|
49102
|
-
import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as
|
|
49289
|
+
import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync69, readdirSync as readdirSync28, renameSync as renameSync18, writeFileSync as writeFileSync38 } from "node:fs";
|
|
49103
49290
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
49104
49291
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
49105
49292
|
import readline from "node:readline";
|
|
49106
|
-
import { dirname as
|
|
49293
|
+
import { dirname as dirname28, join as join80, resolve as resolve50 } from "node:path";
|
|
49107
49294
|
import { homedir as homedir47 } from "node:os";
|
|
49108
49295
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
49109
49296
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
@@ -49137,7 +49324,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49137
49324
|
const needsHindsight = config.litellm?.enabled === true && config.memory?.backend === "hindsight";
|
|
49138
49325
|
if (optedIn.length === 0 && !needsHindsight)
|
|
49139
49326
|
return;
|
|
49140
|
-
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { ensureTeam: ensureTeam2, ensureKey: ensureKey2 }, { addAgentSecret: addAgentSecret2 }] = await Promise.all([
|
|
49327
|
+
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { ensureTeam: ensureTeam2, ensureKey: ensureKey2, validateKey: validateKey2 }, { addAgentSecret: addAgentSecret2 }] = await Promise.all([
|
|
49141
49328
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
49142
49329
|
Promise.resolve().then(() => (init_provision(), exports_provision)),
|
|
49143
49330
|
Promise.resolve().then(() => (init_telegram_yaml(), exports_telegram_yaml))
|
|
@@ -49159,7 +49346,21 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49159
49346
|
unprovisionedNew.push(name);
|
|
49160
49347
|
}
|
|
49161
49348
|
}
|
|
49162
|
-
|
|
49349
|
+
let hindsightPending = false;
|
|
49350
|
+
if (needsHindsight) {
|
|
49351
|
+
const h = await getViaBrokerStructured3("litellm/hindsight/api-key");
|
|
49352
|
+
if (h.kind === "ok") {
|
|
49353
|
+
hindsightPending = false;
|
|
49354
|
+
writeOut(source_default.gray(` ~ litellm/hindsight: key already provisioned (skipped) \u2014 unaffected by missing passphrase.
|
|
49355
|
+
`));
|
|
49356
|
+
} else if (h.kind === "unreachable") {
|
|
49357
|
+
hindsightPending = false;
|
|
49358
|
+
ctx.writeErr(source_default.yellow(` ! litellm/hindsight: vault-broker unreachable \u2014 could not verify the ` + `service key; it keeps its previous state.
|
|
49359
|
+
`));
|
|
49360
|
+
} else {
|
|
49361
|
+
hindsightPending = true;
|
|
49362
|
+
}
|
|
49363
|
+
}
|
|
49163
49364
|
if (alreadyProvisioned.length > 0) {
|
|
49164
49365
|
writeOut(source_default.gray(` ~ litellm: ${alreadyProvisioned.length} agent(s) already provisioned (${alreadyProvisioned.join(", ")}) \u2014 unaffected by missing passphrase.
|
|
49165
49366
|
`));
|
|
@@ -49210,43 +49411,62 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49210
49411
|
continue;
|
|
49211
49412
|
}
|
|
49212
49413
|
const vaultKey = `litellm/${name}/api-key`;
|
|
49414
|
+
const alias = `agent:${name}`;
|
|
49213
49415
|
const existing = await getViaBrokerStructured2(vaultKey);
|
|
49214
|
-
if (existing.kind === "ok") {
|
|
49215
|
-
writeOut(source_default.gray(` ~ litellm/${name}: key already provisioned (skipped)
|
|
49216
|
-
`));
|
|
49217
|
-
if (configText !== null) {
|
|
49218
|
-
const after = addAgentSecret2(configText, name, vaultKey);
|
|
49219
|
-
if (after !== configText) {
|
|
49220
|
-
configText = after;
|
|
49221
|
-
pendingConfigEdits = true;
|
|
49222
|
-
}
|
|
49223
|
-
}
|
|
49224
|
-
continue;
|
|
49225
|
-
}
|
|
49226
49416
|
if (existing.kind === "unreachable") {
|
|
49227
49417
|
ctx.writeErr(source_default.yellow(` ! litellm/${name}: vault-broker unreachable (${existing.msg ?? "no detail"}) \u2014 ` + `skipping key provisioning; agent keeps its previous routing env.
|
|
49228
49418
|
`));
|
|
49229
49419
|
continue;
|
|
49230
49420
|
}
|
|
49231
49421
|
let masterKey = adminKeyRef;
|
|
49422
|
+
let masterKeyErr = null;
|
|
49232
49423
|
if (isVaultReference(adminKeyRef)) {
|
|
49233
49424
|
const refKey = parseVaultReference(adminKeyRef);
|
|
49234
49425
|
const resolved = await getViaBrokerStructured2(refKey);
|
|
49235
49426
|
if (resolved.kind !== "ok") {
|
|
49236
|
-
|
|
49237
|
-
|
|
49238
|
-
|
|
49239
|
-
|
|
49240
|
-
|
|
49427
|
+
masterKey = null;
|
|
49428
|
+
masterKeyErr = `litellm: admin_key vault ref '${adminKeyRef}' did not resolve (${resolved.kind}${"msg" in resolved && resolved.msg ? `: ${resolved.msg}` : ""}).`;
|
|
49429
|
+
} else if (resolved.entry.kind !== "string") {
|
|
49430
|
+
masterKey = null;
|
|
49431
|
+
masterKeyErr = `litellm: admin_key vault ref '${adminKeyRef}' is not a string secret.`;
|
|
49432
|
+
} else {
|
|
49433
|
+
masterKey = resolved.entry.value;
|
|
49241
49434
|
}
|
|
49242
|
-
|
|
49243
|
-
|
|
49244
|
-
|
|
49245
|
-
|
|
49246
|
-
|
|
49435
|
+
}
|
|
49436
|
+
if (existing.kind === "ok") {
|
|
49437
|
+
const storedKey = existing.entry.kind === "string" ? existing.entry.value : null;
|
|
49438
|
+
let driftReprovision = false;
|
|
49439
|
+
if (storedKey && masterKey) {
|
|
49440
|
+
const validation = await validateKey2({ baseUrl, masterKey, key: storedKey });
|
|
49441
|
+
if (validation.kind === "unknown") {
|
|
49442
|
+
driftReprovision = true;
|
|
49443
|
+
ctx.writeErr(source_default.yellow(` ! litellm/${name}: stored virtual key not recognized by the proxy ` + `(DB drift) \u2014 re-provisioning a fresh key.
|
|
49444
|
+
`));
|
|
49445
|
+
} else if (validation.kind === "unreachable") {
|
|
49446
|
+
ctx.writeErr(source_default.yellow(` ! litellm/${name}: proxy unreachable during key validation (${validation.detail}) \u2014 keeping the stored key unverified.
|
|
49447
|
+
`));
|
|
49448
|
+
} else {
|
|
49449
|
+
writeOut(source_default.gray(` ~ litellm/${name}: key already provisioned + validated (skipped)
|
|
49450
|
+
`));
|
|
49451
|
+
}
|
|
49452
|
+
} else {
|
|
49453
|
+
writeOut(source_default.gray(` ~ litellm/${name}: key already provisioned (skipped)
|
|
49454
|
+
`));
|
|
49455
|
+
}
|
|
49456
|
+
if (!driftReprovision) {
|
|
49457
|
+
if (configText !== null) {
|
|
49458
|
+
const after = addAgentSecret2(configText, name, vaultKey);
|
|
49459
|
+
if (after !== configText) {
|
|
49460
|
+
configText = after;
|
|
49461
|
+
pendingConfigEdits = true;
|
|
49462
|
+
}
|
|
49463
|
+
}
|
|
49247
49464
|
continue;
|
|
49248
49465
|
}
|
|
49249
|
-
|
|
49466
|
+
}
|
|
49467
|
+
if (!masterKey) {
|
|
49468
|
+
failures.push({ agent: name, message: masterKeyErr ?? `litellm: admin_key unresolved` });
|
|
49469
|
+
continue;
|
|
49250
49470
|
}
|
|
49251
49471
|
await ensureTeam2(baseUrl, masterKey, team);
|
|
49252
49472
|
const metadata = {
|
|
@@ -49264,9 +49484,11 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49264
49484
|
const { key } = await ensureKey2({
|
|
49265
49485
|
baseUrl,
|
|
49266
49486
|
masterKey,
|
|
49267
|
-
alias
|
|
49487
|
+
alias,
|
|
49268
49488
|
team,
|
|
49269
|
-
metadata
|
|
49489
|
+
metadata,
|
|
49490
|
+
log: (m) => ctx.writeErr(source_default.gray(` ~ litellm/${name}: ${m}
|
|
49491
|
+
`))
|
|
49270
49492
|
});
|
|
49271
49493
|
const put = await putViaBroker2(vaultKey, { kind: "string", value: key }, { passphrase });
|
|
49272
49494
|
if (put.kind !== "ok") {
|
|
@@ -49328,7 +49550,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49328
49550
|
masterKey,
|
|
49329
49551
|
alias: "service:hindsight",
|
|
49330
49552
|
team,
|
|
49331
|
-
metadata: { service: "hindsight", env: "fleet", ...oauthAccount ? { oauth_account: oauthAccount } : {} }
|
|
49553
|
+
metadata: { service: "hindsight", env: "fleet", ...oauthAccount ? { oauth_account: oauthAccount } : {} },
|
|
49554
|
+
log: (m) => ctx.writeErr(source_default.gray(` ~ litellm/hindsight: ${m}
|
|
49555
|
+
`))
|
|
49332
49556
|
});
|
|
49333
49557
|
const put = await putViaBroker2(hindsightVaultKey, { kind: "string", value: key }, { passphrase });
|
|
49334
49558
|
if (put.kind === "ok") {
|
|
@@ -49348,7 +49572,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49348
49572
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
49349
49573
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
49350
49574
|
if (isCustomPath && ctx.customVaultPath) {
|
|
49351
|
-
return
|
|
49575
|
+
return dirname28(ctx.customVaultPath);
|
|
49352
49576
|
}
|
|
49353
49577
|
return join80(homeDir, ".switchroom", "vault");
|
|
49354
49578
|
}
|
|
@@ -50003,7 +50227,7 @@ function copyExampleConfig2(name) {
|
|
|
50003
50227
|
if (!existsSync82(exampleFile)) {
|
|
50004
50228
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
50005
50229
|
}
|
|
50006
|
-
|
|
50230
|
+
copyFileSync12(exampleFile, dest);
|
|
50007
50231
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
50008
50232
|
}
|
|
50009
50233
|
function findUnwritableAgentDirs(config, opts) {
|
|
@@ -64550,7 +64774,7 @@ import {
|
|
|
64550
64774
|
mkdirSync as mkdirSync53,
|
|
64551
64775
|
writeFileSync as writeFileSync44
|
|
64552
64776
|
} from "node:fs";
|
|
64553
|
-
import { resolve as resolve55, dirname as
|
|
64777
|
+
import { resolve as resolve55, dirname as dirname31 } from "node:path";
|
|
64554
64778
|
import { homedir as homedir56 } from "node:os";
|
|
64555
64779
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir56()) {
|
|
64556
64780
|
return resolve55(home2, ".switchroom");
|
|
@@ -64641,11 +64865,11 @@ function readLedgerIfPresent(base) {
|
|
|
64641
64865
|
}
|
|
64642
64866
|
}
|
|
64643
64867
|
function ledgerPathForBase(base) {
|
|
64644
|
-
return fleetHealthLedgerPath(
|
|
64868
|
+
return fleetHealthLedgerPath(dirname31(base));
|
|
64645
64869
|
}
|
|
64646
64870
|
function writeLedger(base, ledger) {
|
|
64647
64871
|
const path8 = ledgerPathForBase(base);
|
|
64648
|
-
mkdirSync53(
|
|
64872
|
+
mkdirSync53(dirname31(path8), { recursive: true });
|
|
64649
64873
|
writeFileSync44(path8, JSON.stringify(ledger, null, 2) + `
|
|
64650
64874
|
`, "utf-8");
|
|
64651
64875
|
return path8;
|
|
@@ -64665,8 +64889,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
64665
64889
|
import { dirname, join } from "node:path";
|
|
64666
64890
|
|
|
64667
64891
|
// src/build-info.ts
|
|
64668
|
-
var VERSION = "0.18.
|
|
64669
|
-
var COMMIT_SHA = "
|
|
64892
|
+
var VERSION = "0.18.7";
|
|
64893
|
+
var COMMIT_SHA = "21478348";
|
|
64670
64894
|
|
|
64671
64895
|
// src/cli/resolve-version.ts
|
|
64672
64896
|
function readPackageVersion() {
|
|
@@ -71499,7 +71723,7 @@ init_acl();
|
|
|
71499
71723
|
init_protocol();
|
|
71500
71724
|
import * as net3 from "node:net";
|
|
71501
71725
|
import { mkdirSync as mkdirSync24, chmodSync as chmodSync7, chownSync as chownSync4, existsSync as existsSync41, readFileSync as readFileSync34, readdirSync as readdirSync17, statSync as statSync21, unlinkSync as unlinkSync10, writeFileSync as writeFileSync18, renameSync as renameSync13 } from "node:fs";
|
|
71502
|
-
import { dirname as
|
|
71726
|
+
import { dirname as dirname12, resolve as resolve28, basename as basename7 } from "node:path";
|
|
71503
71727
|
import * as os4 from "node:os";
|
|
71504
71728
|
import * as path3 from "node:path";
|
|
71505
71729
|
|
|
@@ -71592,10 +71816,38 @@ function rotateAuditLog(logPath, maxFiles) {
|
|
|
71592
71816
|
return;
|
|
71593
71817
|
}
|
|
71594
71818
|
}
|
|
71819
|
+
const snapshotPath = `${logPath}.1`;
|
|
71595
71820
|
try {
|
|
71596
|
-
fs2.
|
|
71821
|
+
fs2.copyFileSync(logPath, snapshotPath);
|
|
71597
71822
|
} catch (err) {
|
|
71598
|
-
process.stderr.write(`[vault-audit] ERROR: could not
|
|
71823
|
+
process.stderr.write(`[vault-audit] ERROR: could not snapshot active audit log ${logPath} \u2192 ${snapshotPath}: ${err.message}
|
|
71824
|
+
`);
|
|
71825
|
+
return;
|
|
71826
|
+
}
|
|
71827
|
+
try {
|
|
71828
|
+
const fd = fs2.openSync(snapshotPath, "r");
|
|
71829
|
+
try {
|
|
71830
|
+
fs2.fsyncSync(fd);
|
|
71831
|
+
} finally {
|
|
71832
|
+
fs2.closeSync(fd);
|
|
71833
|
+
}
|
|
71834
|
+
} catch (err) {
|
|
71835
|
+
process.stderr.write(`[vault-audit] ERROR: could not fsync audit snapshot ${snapshotPath}; leaving active log intact to avoid data loss: ${err.message}
|
|
71836
|
+
`);
|
|
71837
|
+
return;
|
|
71838
|
+
}
|
|
71839
|
+
try {
|
|
71840
|
+
const dirFd = fs2.openSync(path.dirname(snapshotPath), "r");
|
|
71841
|
+
try {
|
|
71842
|
+
fs2.fsyncSync(dirFd);
|
|
71843
|
+
} finally {
|
|
71844
|
+
fs2.closeSync(dirFd);
|
|
71845
|
+
}
|
|
71846
|
+
} catch {}
|
|
71847
|
+
try {
|
|
71848
|
+
fs2.truncateSync(logPath, 0);
|
|
71849
|
+
} catch (err) {
|
|
71850
|
+
process.stderr.write(`[vault-audit] ERROR: could not truncate active audit log ${logPath}: ${err.message}
|
|
71599
71851
|
`);
|
|
71600
71852
|
}
|
|
71601
71853
|
}
|
|
@@ -74004,7 +74256,7 @@ class VaultBroker {
|
|
|
74004
74256
|
this.passphrase = this.testOpts._testPassphrase;
|
|
74005
74257
|
}
|
|
74006
74258
|
process.umask(63);
|
|
74007
|
-
const parentDir =
|
|
74259
|
+
const parentDir = dirname12(this.socketPath);
|
|
74008
74260
|
mkdirSync24(parentDir, { recursive: true, mode: 448 });
|
|
74009
74261
|
try {
|
|
74010
74262
|
chmodSync7(parentDir, 448);
|
|
@@ -75485,15 +75737,15 @@ class VaultBroker {
|
|
|
75485
75737
|
}
|
|
75486
75738
|
}
|
|
75487
75739
|
function detectVaultLayoutDrift(vaultPath) {
|
|
75488
|
-
const dir =
|
|
75740
|
+
const dir = dirname12(vaultPath);
|
|
75489
75741
|
if (basename7(dir) !== "vault")
|
|
75490
75742
|
return;
|
|
75491
75743
|
if (basename7(vaultPath) !== "vault.enc")
|
|
75492
75744
|
return;
|
|
75493
|
-
const switchroomDir =
|
|
75745
|
+
const switchroomDir = dirname12(dir);
|
|
75494
75746
|
if (basename7(switchroomDir) !== ".switchroom")
|
|
75495
75747
|
return;
|
|
75496
|
-
const home2 =
|
|
75748
|
+
const home2 = dirname12(switchroomDir);
|
|
75497
75749
|
const result = inspectVaultLayout(home2);
|
|
75498
75750
|
if (result.kind === "divergent") {
|
|
75499
75751
|
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.`);
|
|
@@ -76525,7 +76777,7 @@ import {
|
|
|
76525
76777
|
appendFileSync as appendFileSync3,
|
|
76526
76778
|
closeSync as closeSync10,
|
|
76527
76779
|
existsSync as existsSync45,
|
|
76528
|
-
fsyncSync as
|
|
76780
|
+
fsyncSync as fsyncSync7,
|
|
76529
76781
|
mkdirSync as mkdirSync25,
|
|
76530
76782
|
openSync as openSync10,
|
|
76531
76783
|
readdirSync as readdirSync18,
|
|
@@ -76672,7 +76924,7 @@ function backupVault(opts) {
|
|
|
76672
76924
|
const fd = openSync10(tmpPath, "wx", 384);
|
|
76673
76925
|
try {
|
|
76674
76926
|
writeSync7(fd, src);
|
|
76675
|
-
|
|
76927
|
+
fsyncSync7(fd);
|
|
76676
76928
|
} finally {
|
|
76677
76929
|
closeSync10(fd);
|
|
76678
76930
|
}
|
|
@@ -76703,7 +76955,7 @@ function backupVault(opts) {
|
|
|
76703
76955
|
try {
|
|
76704
76956
|
const dirFd = openSync10(opts.destDir, "r");
|
|
76705
76957
|
try {
|
|
76706
|
-
|
|
76958
|
+
fsyncSync7(dirFd);
|
|
76707
76959
|
} finally {
|
|
76708
76960
|
closeSync10(dirFd);
|
|
76709
76961
|
}
|
|
@@ -76719,7 +76971,7 @@ function backupVault(opts) {
|
|
|
76719
76971
|
try {
|
|
76720
76972
|
const dirFd = openSync10(opts.destDir, "r");
|
|
76721
76973
|
try {
|
|
76722
|
-
|
|
76974
|
+
fsyncSync7(dirFd);
|
|
76723
76975
|
} finally {
|
|
76724
76976
|
closeSync10(dirFd);
|
|
76725
76977
|
}
|
|
@@ -78889,7 +79141,7 @@ import {
|
|
|
78889
79141
|
writeSync as writeSync8,
|
|
78890
79142
|
constants as fsConstants3
|
|
78891
79143
|
} from "node:fs";
|
|
78892
|
-
import { resolve as resolve32, extname, join as join49, relative, dirname as
|
|
79144
|
+
import { resolve as resolve32, extname, join as join49, relative, dirname as dirname15 } from "node:path";
|
|
78893
79145
|
import { homedir as homedir27 } from "node:os";
|
|
78894
79146
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
78895
79147
|
|
|
@@ -79509,7 +79761,8 @@ var SUBAGENTS_SCHEMA_SQL = `
|
|
|
79509
79761
|
status TEXT NOT NULL,
|
|
79510
79762
|
result_summary TEXT,
|
|
79511
79763
|
jsonl_agent_id TEXT,
|
|
79512
|
-
parent_agent_id TEXT
|
|
79764
|
+
parent_agent_id TEXT,
|
|
79765
|
+
model TEXT
|
|
79513
79766
|
);
|
|
79514
79767
|
CREATE INDEX IF NOT EXISTS subagents_turn ON subagents(parent_turn_key);
|
|
79515
79768
|
CREATE INDEX IF NOT EXISTS subagents_status ON subagents(status);
|
|
@@ -79525,6 +79778,10 @@ function applySubagentsSchema(db) {
|
|
|
79525
79778
|
if (!hasParentAgentId) {
|
|
79526
79779
|
db.exec("ALTER TABLE subagents ADD COLUMN parent_agent_id TEXT");
|
|
79527
79780
|
}
|
|
79781
|
+
const hasModel = cols.some((c) => c.name === "model");
|
|
79782
|
+
if (!hasModel) {
|
|
79783
|
+
db.exec("ALTER TABLE subagents ADD COLUMN model TEXT");
|
|
79784
|
+
}
|
|
79528
79785
|
db.exec("CREATE INDEX IF NOT EXISTS subagents_jsonl_id ON subagents(jsonl_agent_id)");
|
|
79529
79786
|
}
|
|
79530
79787
|
function mapSubagentRow(row) {
|
|
@@ -79541,7 +79798,8 @@ function mapSubagentRow(row) {
|
|
|
79541
79798
|
status: row.status,
|
|
79542
79799
|
result_summary: row.result_summary,
|
|
79543
79800
|
jsonl_agent_id: row.jsonl_agent_id,
|
|
79544
|
-
parent_agent_id: row.parent_agent_id ?? null
|
|
79801
|
+
parent_agent_id: row.parent_agent_id ?? null,
|
|
79802
|
+
model: row.model ?? null
|
|
79545
79803
|
};
|
|
79546
79804
|
}
|
|
79547
79805
|
function listSubagents(db, opts = {}) {
|
|
@@ -82165,7 +82423,7 @@ function resolveWebToken() {
|
|
|
82165
82423
|
return existing;
|
|
82166
82424
|
}
|
|
82167
82425
|
const token = randomBytes11(32).toString("hex");
|
|
82168
|
-
mkdirSync30(
|
|
82426
|
+
mkdirSync30(dirname15(tokenPath), { recursive: true, mode: 448 });
|
|
82169
82427
|
try {
|
|
82170
82428
|
const fd = openSync11(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
|
|
82171
82429
|
try {
|
|
@@ -82923,8 +83181,8 @@ Starting Switchroom dashboard...
|
|
|
82923
83181
|
init_source();
|
|
82924
83182
|
init_loader();
|
|
82925
83183
|
init_scaffold();
|
|
82926
|
-
import { existsSync as existsSync56, copyFileSync as
|
|
82927
|
-
import { resolve as resolve33, dirname as
|
|
83184
|
+
import { existsSync as existsSync56, copyFileSync as copyFileSync9, readFileSync as readFileSync51, writeFileSync as writeFileSync26, mkdirSync as mkdirSync31 } from "node:fs";
|
|
83185
|
+
import { resolve as resolve33, dirname as dirname16 } from "node:path";
|
|
82928
83186
|
init_state();
|
|
82929
83187
|
init_vault();
|
|
82930
83188
|
init_manager();
|
|
@@ -83229,8 +83487,8 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
83229
83487
|
if (!existsSync56(srcFile)) {
|
|
83230
83488
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
83231
83489
|
}
|
|
83232
|
-
mkdirSync31(
|
|
83233
|
-
|
|
83490
|
+
mkdirSync31(dirname16(destFile), { recursive: true });
|
|
83491
|
+
copyFileSync9(srcFile, destFile);
|
|
83234
83492
|
console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
|
|
83235
83493
|
console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
|
|
83236
83494
|
await writeDetectedTimezone(destFile, nonInteractive);
|
|
@@ -84012,7 +84270,7 @@ init_lifecycle();
|
|
|
84012
84270
|
init_compose_env();
|
|
84013
84271
|
import { cpSync as cpSync2, existsSync as existsSync63, mkdirSync as mkdirSync33, readFileSync as readFileSync56, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync28, chownSync as chownSync5 } from "node:fs";
|
|
84014
84272
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
84015
|
-
import { join as join63, dirname as
|
|
84273
|
+
import { join as join63, dirname as dirname19, resolve as resolve38 } from "node:path";
|
|
84016
84274
|
import { homedir as homedir38 } from "node:os";
|
|
84017
84275
|
|
|
84018
84276
|
// src/cli/release-yaml.ts
|
|
@@ -84121,7 +84379,7 @@ function defaultPersistPin(configPath) {
|
|
|
84121
84379
|
}
|
|
84122
84380
|
var DEFAULT_COMPOSE_PATH = join63(homedir38(), ".switchroom", "compose", "docker-compose.yml");
|
|
84123
84381
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
84124
|
-
let dir =
|
|
84382
|
+
let dir = dirname19(scriptPath);
|
|
84125
84383
|
for (let i = 0;i < 12; i++) {
|
|
84126
84384
|
if (existsSync63(join63(dir, ".git"))) {
|
|
84127
84385
|
try {
|
|
@@ -84130,7 +84388,7 @@ function runningFromSwitchroomCheckout(scriptPath) {
|
|
|
84130
84388
|
return true;
|
|
84131
84389
|
} catch {}
|
|
84132
84390
|
}
|
|
84133
|
-
const parent =
|
|
84391
|
+
const parent = dirname19(dir);
|
|
84134
84392
|
if (parent === dir)
|
|
84135
84393
|
break;
|
|
84136
84394
|
dir = parent;
|
|
@@ -84346,7 +84604,7 @@ function planUpdate(opts) {
|
|
|
84346
84604
|
if (existsSync63(dest)) {
|
|
84347
84605
|
rmSync12(dest, { recursive: true, force: true });
|
|
84348
84606
|
}
|
|
84349
|
-
mkdirSync33(
|
|
84607
|
+
mkdirSync33(dirname19(dest), { recursive: true });
|
|
84350
84608
|
cpSync2(source, dest, { recursive: true, dereference: false });
|
|
84351
84609
|
} catch (err) {
|
|
84352
84610
|
throw new Error(`sync-bundled-skills failed: ${err.message}`);
|
|
@@ -84459,7 +84717,7 @@ function defaultStatusProbe(composePath) {
|
|
|
84459
84717
|
try {
|
|
84460
84718
|
cliBuiltAt = new Date(statSync28(scriptPath).mtimeMs).toISOString();
|
|
84461
84719
|
} catch {}
|
|
84462
|
-
let dir =
|
|
84720
|
+
let dir = dirname19(scriptPath);
|
|
84463
84721
|
for (let i = 0;i < 8; i++) {
|
|
84464
84722
|
const pkgPath = join63(dir, "package.json");
|
|
84465
84723
|
if (existsSync63(pkgPath)) {
|
|
@@ -84472,7 +84730,7 @@ function defaultStatusProbe(composePath) {
|
|
|
84472
84730
|
}
|
|
84473
84731
|
break;
|
|
84474
84732
|
}
|
|
84475
|
-
const parent =
|
|
84733
|
+
const parent = dirname19(dir);
|
|
84476
84734
|
if (parent === dir)
|
|
84477
84735
|
break;
|
|
84478
84736
|
dir = parent;
|
|
@@ -85103,7 +85361,7 @@ init_helpers();
|
|
|
85103
85361
|
init_lifecycle();
|
|
85104
85362
|
import { execSync as execSync4 } from "node:child_process";
|
|
85105
85363
|
import { existsSync as existsSync64, readFileSync as readFileSync58 } from "node:fs";
|
|
85106
|
-
import { dirname as
|
|
85364
|
+
import { dirname as dirname20, join as join64 } from "node:path";
|
|
85107
85365
|
function getClaudeCodeVersion() {
|
|
85108
85366
|
try {
|
|
85109
85367
|
const out = execSync4("claude --version 2>/dev/null", {
|
|
@@ -85162,7 +85420,7 @@ function locateSwitchroomInstallDir() {
|
|
|
85162
85420
|
}
|
|
85163
85421
|
} catch {}
|
|
85164
85422
|
}
|
|
85165
|
-
dir =
|
|
85423
|
+
dir = dirname20(dir);
|
|
85166
85424
|
}
|
|
85167
85425
|
return null;
|
|
85168
85426
|
}
|
|
@@ -86363,7 +86621,7 @@ import {
|
|
|
86363
86621
|
rmSync as rmSync13,
|
|
86364
86622
|
writeFileSync as writeFileSync28
|
|
86365
86623
|
} from "node:fs";
|
|
86366
|
-
import { dirname as
|
|
86624
|
+
import { dirname as dirname21, join as join67 } from "node:path";
|
|
86367
86625
|
import { homedir as homedir40 } from "node:os";
|
|
86368
86626
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
86369
86627
|
|
|
@@ -86410,7 +86668,7 @@ function ensurePythonEnv(opts) {
|
|
|
86410
86668
|
if (existsSync67(venvDir)) {
|
|
86411
86669
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
86412
86670
|
}
|
|
86413
|
-
mkdirSync35(
|
|
86671
|
+
mkdirSync35(dirname21(venvDir), { recursive: true });
|
|
86414
86672
|
try {
|
|
86415
86673
|
execFileSync19(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
86416
86674
|
} catch (err) {
|
|
@@ -86444,14 +86702,14 @@ function ensurePythonEnv(opts) {
|
|
|
86444
86702
|
// src/deps/node.ts
|
|
86445
86703
|
import { createHash as createHash13 } from "node:crypto";
|
|
86446
86704
|
import {
|
|
86447
|
-
copyFileSync as
|
|
86705
|
+
copyFileSync as copyFileSync10,
|
|
86448
86706
|
existsSync as existsSync68,
|
|
86449
86707
|
mkdirSync as mkdirSync36,
|
|
86450
86708
|
readFileSync as readFileSync61,
|
|
86451
86709
|
rmSync as rmSync14,
|
|
86452
86710
|
writeFileSync as writeFileSync29
|
|
86453
86711
|
} from "node:fs";
|
|
86454
|
-
import { dirname as
|
|
86712
|
+
import { dirname as dirname22, join as join68 } from "node:path";
|
|
86455
86713
|
import { homedir as homedir41 } from "node:os";
|
|
86456
86714
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
86457
86715
|
|
|
@@ -86478,7 +86736,7 @@ function defaultNodeCacheRoot() {
|
|
|
86478
86736
|
return join68(homedir41(), ".switchroom", "deps", "node");
|
|
86479
86737
|
}
|
|
86480
86738
|
function hashDepInputs(packageJsonPath) {
|
|
86481
|
-
const sourceDir =
|
|
86739
|
+
const sourceDir = dirname22(packageJsonPath);
|
|
86482
86740
|
const hasher = createHash13("sha256");
|
|
86483
86741
|
hasher.update(`package.json
|
|
86484
86742
|
`);
|
|
@@ -86503,7 +86761,7 @@ function ensureNodeEnv(opts) {
|
|
|
86503
86761
|
if (!existsSync68(packageJsonPath)) {
|
|
86504
86762
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
86505
86763
|
}
|
|
86506
|
-
const sourceDir =
|
|
86764
|
+
const sourceDir = dirname22(packageJsonPath);
|
|
86507
86765
|
const envDir = join68(cacheRoot, skillName);
|
|
86508
86766
|
const stampPath = join68(envDir, ".package.sha256");
|
|
86509
86767
|
const nodeModulesDir = join68(envDir, "node_modules");
|
|
@@ -86525,12 +86783,12 @@ function ensureNodeEnv(opts) {
|
|
|
86525
86783
|
rmSync14(envDir, { recursive: true, force: true });
|
|
86526
86784
|
}
|
|
86527
86785
|
mkdirSync36(envDir, { recursive: true });
|
|
86528
|
-
|
|
86786
|
+
copyFileSync10(packageJsonPath, join68(envDir, "package.json"));
|
|
86529
86787
|
let copiedLockfile = false;
|
|
86530
86788
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
86531
86789
|
const lockPath = join68(sourceDir, lockName);
|
|
86532
86790
|
if (existsSync68(lockPath)) {
|
|
86533
|
-
|
|
86791
|
+
copyFileSync10(lockPath, join68(envDir, lockName));
|
|
86534
86792
|
copiedLockfile = true;
|
|
86535
86793
|
}
|
|
86536
86794
|
}
|
|
@@ -87529,7 +87787,7 @@ function safeParseInt(value, fallback) {
|
|
|
87529
87787
|
init_helpers();
|
|
87530
87788
|
init_loader();
|
|
87531
87789
|
init_merge();
|
|
87532
|
-
import { copyFileSync as
|
|
87790
|
+
import { copyFileSync as copyFileSync11, existsSync as existsSync71, readFileSync as readFileSync62, writeFileSync as writeFileSync30 } from "node:fs";
|
|
87533
87791
|
import { join as join70, resolve as resolve44 } from "node:path";
|
|
87534
87792
|
init_scaffold();
|
|
87535
87793
|
init_profiles();
|
|
@@ -87606,7 +87864,7 @@ function registerSoulCommand(program3) {
|
|
|
87606
87864
|
if (existsSync71(backupPath)) {
|
|
87607
87865
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
87608
87866
|
}
|
|
87609
|
-
|
|
87867
|
+
copyFileSync11(t.soulPath, backupPath);
|
|
87610
87868
|
}
|
|
87611
87869
|
writeFileSync30(t.soulPath, content, "utf-8");
|
|
87612
87870
|
if (backupPath) {
|
|
@@ -89092,7 +89350,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
89092
89350
|
init_scaffold_integration();
|
|
89093
89351
|
import { spawn as spawn5 } from "node:child_process";
|
|
89094
89352
|
import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync41 } from "node:fs";
|
|
89095
|
-
import { dirname as
|
|
89353
|
+
import { dirname as dirname23, join as join76 } from "node:path";
|
|
89096
89354
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
89097
89355
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
89098
89356
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -89117,7 +89375,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
89117
89375
|
function writeRefreshHeartbeat(agentName, data) {
|
|
89118
89376
|
const path7 = heartbeatPath(agentName);
|
|
89119
89377
|
try {
|
|
89120
|
-
mkdirSync41(
|
|
89378
|
+
mkdirSync41(dirname23(path7), { recursive: true });
|
|
89121
89379
|
writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
|
|
89122
89380
|
} catch {}
|
|
89123
89381
|
}
|
|
@@ -89310,7 +89568,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
89310
89568
|
init_scaffold_integration();
|
|
89311
89569
|
import { spawn as spawn6 } from "node:child_process";
|
|
89312
89570
|
import { existsSync as existsSync78, mkdirSync as mkdirSync42, writeFileSync as writeFileSync34 } from "node:fs";
|
|
89313
|
-
import { dirname as
|
|
89571
|
+
import { dirname as dirname24 } from "node:path";
|
|
89314
89572
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
89315
89573
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
89316
89574
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -89320,7 +89578,7 @@ function buildNotionMcpArgs(opts) {
|
|
|
89320
89578
|
}
|
|
89321
89579
|
function defaultWriteHeartbeat(path7, contents) {
|
|
89322
89580
|
try {
|
|
89323
|
-
const dir =
|
|
89581
|
+
const dir = dirname24(path7);
|
|
89324
89582
|
if (!existsSync78(dir))
|
|
89325
89583
|
mkdirSync42(dir, { recursive: true });
|
|
89326
89584
|
writeFileSync34(path7, contents);
|
|
@@ -90475,7 +90733,7 @@ init_paths();
|
|
|
90475
90733
|
import {
|
|
90476
90734
|
closeSync as closeSync14,
|
|
90477
90735
|
existsSync as existsSync84,
|
|
90478
|
-
fsyncSync as
|
|
90736
|
+
fsyncSync as fsyncSync8,
|
|
90479
90737
|
mkdirSync as mkdirSync47,
|
|
90480
90738
|
openSync as openSync14,
|
|
90481
90739
|
readdirSync as readdirSync30,
|
|
@@ -90558,7 +90816,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
90558
90816
|
const fd = openSync14(stagingPath, "w", 384);
|
|
90559
90817
|
try {
|
|
90560
90818
|
writeSync10(fd, yamlText);
|
|
90561
|
-
|
|
90819
|
+
fsyncSync8(fd);
|
|
90562
90820
|
} finally {
|
|
90563
90821
|
closeSync14(fd);
|
|
90564
90822
|
}
|
|
@@ -90575,7 +90833,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
90575
90833
|
const fd = openSync14(stagingPath, "w", 384);
|
|
90576
90834
|
try {
|
|
90577
90835
|
writeSync10(fd, yamlText);
|
|
90578
|
-
|
|
90836
|
+
fsyncSync8(fd);
|
|
90579
90837
|
} finally {
|
|
90580
90838
|
closeSync14(fd);
|
|
90581
90839
|
}
|
|
@@ -90782,7 +91040,7 @@ function reconcileAgentCronOnly(agent) {
|
|
|
90782
91040
|
import {
|
|
90783
91041
|
closeSync as closeSync15,
|
|
90784
91042
|
existsSync as existsSync85,
|
|
90785
|
-
fsyncSync as
|
|
91043
|
+
fsyncSync as fsyncSync9,
|
|
90786
91044
|
mkdirSync as mkdirSync48,
|
|
90787
91045
|
openSync as openSync15,
|
|
90788
91046
|
readdirSync as readdirSync31,
|
|
@@ -90826,7 +91084,7 @@ function stagePendingScheduleEntry(opts) {
|
|
|
90826
91084
|
const fd = openSync15(yamlTmp, "w", 384);
|
|
90827
91085
|
try {
|
|
90828
91086
|
writeSync11(fd, opts.yamlText);
|
|
90829
|
-
|
|
91087
|
+
fsyncSync9(fd);
|
|
90830
91088
|
} finally {
|
|
90831
91089
|
closeSync15(fd);
|
|
90832
91090
|
}
|
|
@@ -91794,7 +92052,7 @@ import {
|
|
|
91794
92052
|
writeFileSync as writeFileSync40
|
|
91795
92053
|
} from "node:fs";
|
|
91796
92054
|
import { tmpdir as tmpdir5, homedir as homedir49 } from "node:os";
|
|
91797
|
-
import { dirname as
|
|
92055
|
+
import { dirname as dirname29, join as join85, relative as relative2, resolve as resolve52 } from "node:path";
|
|
91798
92056
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
91799
92057
|
|
|
91800
92058
|
// src/cli/skill-common.ts
|
|
@@ -92264,7 +92522,7 @@ function writePayload(poolDir, name, files) {
|
|
|
92264
92522
|
try {
|
|
92265
92523
|
for (const [path8, content] of Object.entries(files)) {
|
|
92266
92524
|
const full = join85(staging, path8);
|
|
92267
|
-
mkdirSync49(
|
|
92525
|
+
mkdirSync49(dirname29(full), { recursive: true, mode: 493 });
|
|
92268
92526
|
const fd = openSync16(full, "wx");
|
|
92269
92527
|
try {
|
|
92270
92528
|
writeFileSync40(fd, content);
|
|
@@ -92386,7 +92644,7 @@ import {
|
|
|
92386
92644
|
utimesSync,
|
|
92387
92645
|
writeFileSync as writeFileSync41
|
|
92388
92646
|
} from "node:fs";
|
|
92389
|
-
import { dirname as
|
|
92647
|
+
import { dirname as dirname30, join as join86, relative as relative3, resolve as resolve53 } from "node:path";
|
|
92390
92648
|
import { homedir as homedir50, tmpdir as tmpdir6 } from "node:os";
|
|
92391
92649
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
92392
92650
|
init_helpers();
|
|
@@ -92639,13 +92897,13 @@ function writePersonalSkill(targetDir, files) {
|
|
|
92639
92897
|
if (targetIsSymlink) {
|
|
92640
92898
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
92641
92899
|
}
|
|
92642
|
-
mkdirSync50(
|
|
92643
|
-
const staging = mkdtempSync6(join86(
|
|
92900
|
+
mkdirSync50(dirname30(targetDir), { recursive: true, mode: 493 });
|
|
92901
|
+
const staging = mkdtempSync6(join86(dirname30(targetDir), `.skill-personal-stage-`));
|
|
92644
92902
|
let oldRename = null;
|
|
92645
92903
|
try {
|
|
92646
92904
|
for (const [path8, content] of Object.entries(files)) {
|
|
92647
92905
|
const full = join86(staging, path8);
|
|
92648
|
-
mkdirSync50(
|
|
92906
|
+
mkdirSync50(dirname30(full), { recursive: true, mode: 493 });
|
|
92649
92907
|
const fd = openSync17(full, "wx");
|
|
92650
92908
|
try {
|
|
92651
92909
|
writeFileSync41(fd, content);
|
|
@@ -93388,7 +93646,7 @@ import {
|
|
|
93388
93646
|
statSync as statSync39,
|
|
93389
93647
|
lstatSync as lstatSync11,
|
|
93390
93648
|
realpathSync as realpathSync8,
|
|
93391
|
-
copyFileSync as
|
|
93649
|
+
copyFileSync as copyFileSync13
|
|
93392
93650
|
} from "node:fs";
|
|
93393
93651
|
import { homedir as homedir53 } from "node:os";
|
|
93394
93652
|
import { join as join89 } from "node:path";
|
|
@@ -93659,7 +93917,7 @@ function backupExistingCompose() {
|
|
|
93659
93917
|
return null;
|
|
93660
93918
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
93661
93919
|
const bak = `${p}.bak-${ts}`;
|
|
93662
|
-
|
|
93920
|
+
copyFileSync13(p, bak);
|
|
93663
93921
|
return bak;
|
|
93664
93922
|
}
|
|
93665
93923
|
function runDocker(args) {
|
|
@@ -93872,7 +94130,7 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
93872
94130
|
init_source();
|
|
93873
94131
|
init_helpers();
|
|
93874
94132
|
init_operator_uid();
|
|
93875
|
-
import { existsSync as existsSync93, mkdirSync as mkdirSync52, writeFileSync as writeFileSync43, copyFileSync as
|
|
94133
|
+
import { existsSync as existsSync93, mkdirSync as mkdirSync52, writeFileSync as writeFileSync43, copyFileSync as copyFileSync14 } from "node:fs";
|
|
93876
94134
|
import { homedir as homedir54 } from "node:os";
|
|
93877
94135
|
import { join as join90 } from "node:path";
|
|
93878
94136
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
@@ -93970,7 +94228,7 @@ function backupExistingCompose2() {
|
|
|
93970
94228
|
return null;
|
|
93971
94229
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
93972
94230
|
const bak = `${p}.bak-${ts}`;
|
|
93973
|
-
|
|
94231
|
+
copyFileSync14(p, bak);
|
|
93974
94232
|
return bak;
|
|
93975
94233
|
}
|
|
93976
94234
|
function runDocker2(args) {
|