switchroom 0.21.14 → 0.21.15
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 +6 -2
- package/dist/auth-broker/index.js +6 -2
- package/dist/cli/notion-write-pretool.mjs +6 -2
- package/dist/cli/switchroom.js +999 -490
- package/dist/host-control/main.js +7 -3
- package/dist/vault/approvals/kernel-server.js +6 -2
- package/dist/vault/broker/server.js +6 -2
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +9 -0
- package/telegram-plugin/dist/gateway/gateway.js +10 -6
- package/vendor/hindsight-memory/scripts/lib/client.py +14 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +22 -0
- package/vendor/hindsight-memory/scripts/lib/directives.py +45 -7
- package/vendor/hindsight-memory/scripts/lib/watermark.py +27 -0
- package/vendor/hindsight-memory/scripts/recall.py +335 -5
- package/vendor/hindsight-memory/scripts/reconcile_tail.py +4 -12
- package/vendor/hindsight-memory/scripts/retain.py +59 -3
- package/vendor/hindsight-memory/scripts/tests/test_config_retain_tool_calls_env.py +98 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +52 -0
- package/vendor/hindsight-memory/scripts/tests/test_incremental_sweep.py +293 -0
- package/vendor/hindsight-memory/scripts/tests/test_profile_capture_nudge.py +335 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_query_timestamp.py +376 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.21.
|
|
2123
|
+
var VERSION = "0.21.15", COMMIT_SHA = "efa77e51", COMMIT_DATE = "2026-08-17T00:42:23Z";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -13750,6 +13750,7 @@ var init_schema = __esm(() => {
|
|
|
13750
13750
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
13751
13751
|
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
|
|
13752
13752
|
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
13753
|
+
profile_capture_nudge: exports_external.boolean().optional().describe("Deterministic operator-profile capture nudge (RFC phase4 P3, serves " + 'the "save memories about him" want). When on (switchroom default ' + "true), the auto-recall hook regex-detects a first-person durable " + 'self-statement by the operator ("I prefer \u2026", "my \u2026 is \u2026", "I ' + 'always \u2026", "remind me that I \u2026") and appends a terse advisory to ' + "the turn's context telling the model to persist it with an explicit " + "mcp__hindsight__retain tagged `profile:ken` into the agent's OWN bank " + "(not a shared/cross-agent person bank). Detection is pure regex \u2014 the " + "model does the judgment in-session and calls retain itself (no model " + "callsite, no silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
13753
13754
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
13754
13755
|
observation_scopes: ObservationScopesSchema,
|
|
13755
13756
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -13798,7 +13799,8 @@ var init_schema = __esm(() => {
|
|
|
13798
13799
|
}).optional().describe("Auto-recall tuning knobs"),
|
|
13799
13800
|
retain: exports_external.object({
|
|
13800
13801
|
every_n_turns: exports_external.number().int().min(1).optional().describe("How often the Stop hook fires auto-retention, in turns. The " + "vendor plugin default is 10 (a short session can end before " + "retention ever fires); switchroom's scaffold default is 3. " + "Lower = more frequent, smaller retains + tighter crash " + "durability (at 1, every turn is retained before a restart can " + "lose it); higher = fewer, larger retains + less LLM churn. " + "Raised from the historical 1 to 3 because the local reasoning " + "consolidation model (Ollama gpt-oss-20b) ran away on the large " + "overlapping every-turn payloads. Set to 1 for the old " + "every-turn crash-durability guarantee. Min 1. Cascade: " + "per-field merge (agent wins over default)."),
|
|
13801
|
-
overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge.")
|
|
13802
|
+
overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge."),
|
|
13803
|
+
tool_calls: exports_external.boolean().optional().describe("Whether auto-retain stores assistant tool_use blocks (with their " + "entire input dict) and tool_result content (up to 2000 chars) " + "alongside the human/assistant turns. Vendor + switchroom default " + "is true \u2014 the fleet behaves byte-identically until an operator " + "sets this false. Tool inputs/results are a large fraction of " + "retained volume (RFC memory-redesign P4) but also where evidence " + "lives (a commit SHA, a failing test's output, the diff that " + "fixed something), so the tradeoff is real and per-agent. false is " + "not a new mode: sidechains already force it off " + "(subagent_retain.py). Set false to drop tool exhaust from an " + "agent's retains. Cascade: per-field merge (agent wins over " + "default).")
|
|
13802
13804
|
}).optional().describe("Auto-retain (Stop-hook consolidation) cadence knobs")
|
|
13803
13805
|
}).optional();
|
|
13804
13806
|
HookEntrySchema = exports_external.object({
|
|
@@ -14186,6 +14188,7 @@ var init_schema = __esm(() => {
|
|
|
14186
14188
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
14187
14189
|
profile: exports_external.string().optional(),
|
|
14188
14190
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
14191
|
+
profile_capture_nudge: exports_external.boolean().optional(),
|
|
14189
14192
|
anti_confabulation_directive: AntiConfabulationDirectiveSchema,
|
|
14190
14193
|
observation_scopes: ObservationScopesSchema,
|
|
14191
14194
|
observation_scope_strategy: ObservationScopeStrategySchema,
|
|
@@ -14234,7 +14237,8 @@ var init_schema = __esm(() => {
|
|
|
14234
14237
|
}).optional(),
|
|
14235
14238
|
retain: exports_external.object({
|
|
14236
14239
|
every_n_turns: exports_external.number().int().min(1).optional(),
|
|
14237
|
-
overlap_turns: exports_external.number().int().min(0).optional()
|
|
14240
|
+
overlap_turns: exports_external.number().int().min(0).optional(),
|
|
14241
|
+
tool_calls: exports_external.boolean().optional()
|
|
14238
14242
|
}).optional(),
|
|
14239
14243
|
bank_mission: exports_external.string().optional(),
|
|
14240
14244
|
reflect_mission: exports_external.string().optional(),
|
|
@@ -30444,7 +30448,8 @@ function resolveHindsightRetainConfig(switchroomConfig, agentName, resolvedAgent
|
|
|
30444
30448
|
const retain = resolvedAgentConfig ? resolvedAgentConfig.memory?.retain : switchroomConfig ? resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {})?.memory?.retain : undefined;
|
|
30445
30449
|
return {
|
|
30446
30450
|
everyNTurns: retain?.every_n_turns ?? HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS,
|
|
30447
|
-
overlapTurns: retain?.overlap_turns ?? HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS
|
|
30451
|
+
overlapTurns: retain?.overlap_turns ?? HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS,
|
|
30452
|
+
toolCalls: retain?.tool_calls ?? HINDSIGHT_DEFAULT_RETAIN_TOOL_CALLS
|
|
30448
30453
|
};
|
|
30449
30454
|
}
|
|
30450
30455
|
function resolveHindsightAutoRecall(switchroomConfig, agentName, resolvedAgentConfig) {
|
|
@@ -30520,6 +30525,7 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, re
|
|
|
30520
30525
|
settings.retainEveryNTurns = retainConfig.everyNTurns;
|
|
30521
30526
|
settings.retainMode = "chunked";
|
|
30522
30527
|
settings.retainOverlapTurns = retainConfig.overlapTurns;
|
|
30528
|
+
settings.retainToolCalls = retainConfig.toolCalls;
|
|
30523
30529
|
settings.retainTags = [...RETAIN_TAGS_DEFAULT];
|
|
30524
30530
|
settings.recallMaxMemories = recallCaps.maxMemories;
|
|
30525
30531
|
settings.recallMaxTokens = recallCaps.maxTokens;
|
|
@@ -30530,6 +30536,7 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, re
|
|
|
30530
30536
|
settings.recallAdditionalBankMinSlots = defaultRecallAdditionalBankMinSlots(recallCaps.maxMemories);
|
|
30531
30537
|
settings.recallSkipTrivial = true;
|
|
30532
30538
|
settings.directiveCaptureNudge = true;
|
|
30539
|
+
settings.profileCaptureNudge = true;
|
|
30533
30540
|
settings.recallTagWeights = { ...HINDSIGHT_RECALL_TAG_WEIGHT_SEED };
|
|
30534
30541
|
if (additionalBanks.length > 0) {
|
|
30535
30542
|
settings.recallAdditionalBanks = [...additionalBanks];
|
|
@@ -30583,6 +30590,7 @@ function buildWorkspaceContext(args) {
|
|
|
30583
30590
|
hindsightRecallTypes,
|
|
30584
30591
|
hindsightRecallSkipTrivial,
|
|
30585
30592
|
hindsightDirectiveCaptureNudge,
|
|
30593
|
+
hindsightProfileCaptureNudge,
|
|
30586
30594
|
hindsightObservationScopes,
|
|
30587
30595
|
hindsightObservationScopeStrategy,
|
|
30588
30596
|
hindsightTopicAliasesJson,
|
|
@@ -30637,6 +30645,7 @@ function buildWorkspaceContext(args) {
|
|
|
30637
30645
|
hindsightRecallTypes,
|
|
30638
30646
|
hindsightRecallSkipTrivial,
|
|
30639
30647
|
hindsightDirectiveCaptureNudge,
|
|
30648
|
+
hindsightProfileCaptureNudge,
|
|
30640
30649
|
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
30641
30650
|
hindsightObservationScopeStrategyQ: hindsightObservationScopeStrategy ? shellSingleQuote(hindsightObservationScopeStrategy) : undefined,
|
|
30642
30651
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
@@ -30912,6 +30921,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
30912
30921
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
30913
30922
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
30914
30923
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
30924
|
+
const hindsightProfileCaptureNudge = agentConfig.memory?.profile_capture_nudge === undefined ? undefined : String(agentConfig.memory.profile_capture_nudge);
|
|
30915
30925
|
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
30916
30926
|
const hindsightObservationScopeStrategy = agentConfig.memory?.observation_scope_strategy;
|
|
30917
30927
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
@@ -30949,6 +30959,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
30949
30959
|
hindsightRecallTypes,
|
|
30950
30960
|
hindsightRecallSkipTrivial,
|
|
30951
30961
|
hindsightDirectiveCaptureNudge,
|
|
30962
|
+
hindsightProfileCaptureNudge,
|
|
30952
30963
|
hindsightObservationScopes,
|
|
30953
30964
|
hindsightObservationScopeStrategy,
|
|
30954
30965
|
hindsightTopicAliasesJson,
|
|
@@ -31925,6 +31936,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
31925
31936
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
31926
31937
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
31927
31938
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
31939
|
+
const hindsightProfileCaptureNudge = agentConfig.memory?.profile_capture_nudge === undefined ? undefined : String(agentConfig.memory.profile_capture_nudge);
|
|
31928
31940
|
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
31929
31941
|
const hindsightObservationScopeStrategy = agentConfig.memory?.observation_scope_strategy;
|
|
31930
31942
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
@@ -31974,6 +31986,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
31974
31986
|
hindsightRecallTypes,
|
|
31975
31987
|
hindsightRecallSkipTrivial,
|
|
31976
31988
|
hindsightDirectiveCaptureNudge,
|
|
31989
|
+
hindsightProfileCaptureNudge,
|
|
31977
31990
|
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
31978
31991
|
hindsightObservationScopeStrategyQ: hindsightObservationScopeStrategy ? shellSingleQuote(hindsightObservationScopeStrategy) : undefined,
|
|
31979
31992
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
@@ -32411,6 +32424,7 @@ ${body}
|
|
|
32411
32424
|
hindsightRecallTypes,
|
|
32412
32425
|
hindsightRecallSkipTrivial,
|
|
32413
32426
|
hindsightDirectiveCaptureNudge,
|
|
32427
|
+
hindsightProfileCaptureNudge,
|
|
32414
32428
|
hindsightObservationScopes,
|
|
32415
32429
|
hindsightObservationScopeStrategy,
|
|
32416
32430
|
hindsightTopicAliasesJson,
|
|
@@ -32955,6 +32969,13 @@ When the user asks you to forget something ("forget that", "delete X", "drop wha
|
|
|
32955
32969
|
### Inspect proactively
|
|
32956
32970
|
When the user asks "what do you know about X / me", "what do you remember about Y", or any memory audit, use \`reflect\` to synthesize an answer across the bank. Return it as honest prose, not a raw dump. If the bank has little on the topic, say so.
|
|
32957
32971
|
|
|
32972
|
+
### Route repo knowledge to the shared repo bank
|
|
32973
|
+
Auto-retain always writes your OWN bank, and that stays true for everything above \u2014 don't fight it. The one place you reach past it, by hand, is durable knowledge about a REPOSITORY: a build invariant, a migration gotcha, a house-style rule, a "this suite only passes single-threaded" trap. That kind of fact is worth having for every agent that touches the repo, not just you \u2014 so persist it to the shared repo bank with an explicit \`retain\` that names the bank, in the same turn you learn it:
|
|
32974
|
+
|
|
32975
|
+
\`mcp__hindsight__retain(content="switchroom's vitest config must set assetsInclude to ['**/*.yaml'], or a .yaml imported with { type: 'text' } fails to resolve under vitest", bank_id="switchroom-dev", tags=["repo:switchroom", "build"])\`
|
|
32976
|
+
|
|
32977
|
+
Passing \`bank_id\` is what makes this the exception: auto-retain can't reach a shared bank (it always resolves your own), so if you don't make the call by hand the knowledge stays trapped in your bank. Two rules keep the shared bank clean: route only durable, repo-scoped facts there \u2014 never conversation, user preferences, or anything about a person (those stay in your own bank via ordinary retain) \u2014 and tag every one \`repo:<name>\` so it is findable. Writes are always durable and readable by the configured agents; on the read side, ordinary \`recall\` / \`reflect\` reach the shared repo bank only for agents that list it in \`memory.recall.additional_banks\` (a handful of dev agents) \u2014 for everyone else the shared write is write-only from their own recall.
|
|
32978
|
+
|
|
32958
32979
|
Don't wait for a slash command. Don't ask permission. Memory work is table stakes, like a colleague who takes notes and remembers.`, WEB_FETCH_GUIDANCE = `## Fetching from the web
|
|
32959
32980
|
|
|
32960
32981
|
The native \`WebFetch\` tool is disabled in this fleet \u2014 fetch pages
|
|
@@ -33121,7 +33142,7 @@ plainer wins.
|
|
|
33121
33142
|
Every turn that answers a user message ends with a user-visible \`reply\`
|
|
33122
33143
|
\u2014 Telegram is all the user sees; your terminal output
|
|
33123
33144
|
never reaches them.`, TELEGRAM_ENV_PLACEHOLDER = `# Set your bot token: TELEGRAM_BOT_TOKEN=your-token-here
|
|
33124
|
-
`, SWITCHROOM_TELEGRAM_MCP_TOOLS, HINDSIGHT_MCP_TOOLS, AGENT_CONFIG_MCP_TOOLS, HOSTD_MCP_TOOLS, WEBKITE_MCP_TOOLS, CONTEXT7_MCP_TOOLS, CONTEXT7_BLANKET_TOKENS, LEGACY_SWITCHROOM_MCP_TOKENS, LEGACY_HOSTD_BLANKET_TOKENS, DEFAULT_READ_ONLY_PREAPPROVED_TOOLS, NATIVE_WEBSEARCH_PREAPPROVED_TOOLS, WEBKITE_FLEET_DENY_TOOLS, INTERACTIVE_TUI_FLEET_DENY_TOOLS, WEBKITE_BINARY_CONTAINER_PATH = "/usr/local/bin/webkite", SWITCHROOM_DEFAULT_MAIN_MODEL = "claude-sonnet-5", SWITCHROOM_DEFAULT_THINKING_EFFORT = "low", CLAUDE_MD_YOURS_PLACEHOLDER, pendingBankOps, SWITCHROOM_OWNED_SETTINGS_KEYS, ALL_BUILTIN_TOOLS, HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS = 3, HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS = 1, RECALL_OWN_BANK_SLOT_DIVISOR = 3, RECALL_ADDITIONAL_BANK_SLOT_DIVISOR = 6, HINDSIGHT_RECALL_MIN_SCORE_DEFAULT = 0, HINDSIGHT_RECALL_MIN_SCORE_SCOPE_DEFAULT = "degraded", VALID_SETTINGS_DEFAULT_MODES, SWITCHROOM_DEFAULT_PERMISSION_MODE = "acceptEdits", DOCKER_TELEGRAM_PLUGIN_PATH = "/opt/switchroom/telegram-plugin", DOCKER_HOOKS_PATH, DOCKER_BUNDLED_HOOKS_PATH = "/opt/switchroom/hooks", DOCKER_BIN_PATH = "/opt/switchroom/bin", DOCKER_SECURITY_PLUGIN_PATH = "/opt/switchroom/security-plugin", DOCKER_CONFIG_PATH = "/state/config/switchroom.yaml", DOCKER_SWITCHROOM_CLI_PATH = "/usr/local/bin/switchroom", DOCKER_AUTH_BROKER_SOCKET = "/run/switchroom/auth-broker/sock", DOCKER_VAULT_BROKER_SOCKET = "/run/switchroom/broker/sock", DOCKER_AGENT_HOME = "/state/agent/home", CONTEXT7_MCP_URL = "https://mcp.context7.com/mcp", INTEGRATION_MCP_RESOLVERS;
|
|
33145
|
+
`, SWITCHROOM_TELEGRAM_MCP_TOOLS, HINDSIGHT_MCP_TOOLS, AGENT_CONFIG_MCP_TOOLS, HOSTD_MCP_TOOLS, WEBKITE_MCP_TOOLS, CONTEXT7_MCP_TOOLS, CONTEXT7_BLANKET_TOKENS, LEGACY_SWITCHROOM_MCP_TOKENS, LEGACY_HOSTD_BLANKET_TOKENS, DEFAULT_READ_ONLY_PREAPPROVED_TOOLS, NATIVE_WEBSEARCH_PREAPPROVED_TOOLS, WEBKITE_FLEET_DENY_TOOLS, INTERACTIVE_TUI_FLEET_DENY_TOOLS, WEBKITE_BINARY_CONTAINER_PATH = "/usr/local/bin/webkite", SWITCHROOM_DEFAULT_MAIN_MODEL = "claude-sonnet-5", SWITCHROOM_DEFAULT_THINKING_EFFORT = "low", CLAUDE_MD_YOURS_PLACEHOLDER, pendingBankOps, SWITCHROOM_OWNED_SETTINGS_KEYS, ALL_BUILTIN_TOOLS, HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS = 3, HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS = 1, HINDSIGHT_DEFAULT_RETAIN_TOOL_CALLS = true, RECALL_OWN_BANK_SLOT_DIVISOR = 3, RECALL_ADDITIONAL_BANK_SLOT_DIVISOR = 6, HINDSIGHT_RECALL_MIN_SCORE_DEFAULT = 0, HINDSIGHT_RECALL_MIN_SCORE_SCOPE_DEFAULT = "degraded", VALID_SETTINGS_DEFAULT_MODES, SWITCHROOM_DEFAULT_PERMISSION_MODE = "acceptEdits", DOCKER_TELEGRAM_PLUGIN_PATH = "/opt/switchroom/telegram-plugin", DOCKER_HOOKS_PATH, DOCKER_BUNDLED_HOOKS_PATH = "/opt/switchroom/hooks", DOCKER_BIN_PATH = "/opt/switchroom/bin", DOCKER_SECURITY_PLUGIN_PATH = "/opt/switchroom/security-plugin", DOCKER_CONFIG_PATH = "/state/config/switchroom.yaml", DOCKER_SWITCHROOM_CLI_PATH = "/usr/local/bin/switchroom", DOCKER_AUTH_BROKER_SOCKET = "/run/switchroom/auth-broker/sock", DOCKER_VAULT_BROKER_SOCKET = "/run/switchroom/broker/sock", DOCKER_AGENT_HOME = "/state/agent/home", CONTEXT7_MCP_URL = "https://mcp.context7.com/mcp", INTEGRATION_MCP_RESOLVERS;
|
|
33125
33146
|
var init_scaffold = __esm(() => {
|
|
33126
33147
|
init_source();
|
|
33127
33148
|
init_cron_unit_name();
|
|
@@ -84741,7 +84762,7 @@ __export(exports_server2, {
|
|
|
84741
84762
|
TOOLS: () => TOOLS2
|
|
84742
84763
|
});
|
|
84743
84764
|
import { randomBytes as randomBytes18 } from "node:crypto";
|
|
84744
|
-
import { existsSync as
|
|
84765
|
+
import { existsSync as existsSync124 } from "node:fs";
|
|
84745
84766
|
function selfSocketPath() {
|
|
84746
84767
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
84747
84768
|
}
|
|
@@ -84768,7 +84789,7 @@ async function dispatchTool2(name, args) {
|
|
|
84768
84789
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
84769
84790
|
}
|
|
84770
84791
|
const sockPath = selfSocketPath();
|
|
84771
|
-
if (!
|
|
84792
|
+
if (!existsSync124(sockPath)) {
|
|
84772
84793
|
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.`);
|
|
84773
84794
|
}
|
|
84774
84795
|
let req;
|
|
@@ -85019,13 +85040,13 @@ function resolveAuditLogPath() {
|
|
|
85019
85040
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
85020
85041
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
85021
85042
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
85022
|
-
if (
|
|
85043
|
+
if (existsSync124(bindMounted))
|
|
85023
85044
|
return bindMounted;
|
|
85024
85045
|
return defaultAuditLogPath2();
|
|
85025
85046
|
}
|
|
85026
85047
|
function getLastUpdateApplyStatus() {
|
|
85027
85048
|
const path10 = resolveAuditLogPath();
|
|
85028
|
-
if (!
|
|
85049
|
+
if (!existsSync124(path10)) {
|
|
85029
85050
|
return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
|
|
85030
85051
|
}
|
|
85031
85052
|
let raw;
|
|
@@ -85993,11 +86014,11 @@ var init_test_agents = __esm(() => {
|
|
|
85993
86014
|
});
|
|
85994
86015
|
|
|
85995
86016
|
// src/litellm/header-passthrough-guard.ts
|
|
85996
|
-
import { existsSync as
|
|
86017
|
+
import { existsSync as existsSync127, readdirSync as readdirSync49 } from "node:fs";
|
|
85997
86018
|
function discoverLiveLitellmConfigPath(opts) {
|
|
85998
86019
|
const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
|
|
85999
86020
|
const readdir2 = opts?.readdirFn ?? ((p) => readdirSync49(p));
|
|
86000
|
-
const exists = opts?.existsFn ??
|
|
86021
|
+
const exists = opts?.existsFn ?? existsSync127;
|
|
86001
86022
|
let entries;
|
|
86002
86023
|
try {
|
|
86003
86024
|
entries = readdir2(servicesDir);
|
|
@@ -86215,12 +86236,12 @@ var init_passthrough_mount_guard = __esm(() => {
|
|
|
86215
86236
|
});
|
|
86216
86237
|
|
|
86217
86238
|
// src/fleet-health/litellm-config-sensor.ts
|
|
86218
|
-
import { execFileSync as
|
|
86219
|
-
import { readFileSync as
|
|
86220
|
-
import { dirname as
|
|
86239
|
+
import { execFileSync as execFileSync38 } from "node:child_process";
|
|
86240
|
+
import { readFileSync as readFileSync114, existsSync as existsSync128 } from "node:fs";
|
|
86241
|
+
import { dirname as dirname55, join as join131 } from "node:path";
|
|
86221
86242
|
function resolveLiveLitellmPythonVersion() {
|
|
86222
86243
|
try {
|
|
86223
|
-
const ps =
|
|
86244
|
+
const ps = execFileSync38("docker", ["ps", "--format", "{{.Names}}\t{{.Image}}"], {
|
|
86224
86245
|
encoding: "utf-8",
|
|
86225
86246
|
timeout: 5000,
|
|
86226
86247
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -86232,7 +86253,7 @@ function resolveLiveLitellmPythonVersion() {
|
|
|
86232
86253
|
const name = line.split("\t")[0]?.trim();
|
|
86233
86254
|
if (!name)
|
|
86234
86255
|
return null;
|
|
86235
|
-
const lsOut =
|
|
86256
|
+
const lsOut = execFileSync38("docker", ["exec", name, "sh", "-c", "ls -d /app/.venv/lib/python*/ 2>/dev/null"], { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] });
|
|
86236
86257
|
const m = lsOut.match(/python(\d+\.\d+)/);
|
|
86237
86258
|
return m ? m[1] : null;
|
|
86238
86259
|
} catch {
|
|
@@ -86249,8 +86270,8 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
|
|
|
86249
86270
|
}
|
|
86250
86271
|
function scanLitellmConfig(opts = {}) {
|
|
86251
86272
|
const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
|
|
86252
|
-
const exists = opts.existsFn ??
|
|
86253
|
-
const read = opts.readFn ?? ((p) =>
|
|
86273
|
+
const exists = opts.existsFn ?? existsSync128;
|
|
86274
|
+
const read = opts.readFn ?? ((p) => readFileSync114(p, "utf-8"));
|
|
86254
86275
|
const log = opts.log ?? (() => {});
|
|
86255
86276
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
86256
86277
|
if (path10 === null) {
|
|
@@ -86301,7 +86322,7 @@ function scanLitellmConfig(opts = {}) {
|
|
|
86301
86322
|
ts: nowIso
|
|
86302
86323
|
});
|
|
86303
86324
|
}
|
|
86304
|
-
const composePath = join131(
|
|
86325
|
+
const composePath = join131(dirname55(path10), LIVE_COMPOSE_BASENAME);
|
|
86305
86326
|
let composeText = null;
|
|
86306
86327
|
if (exists(composePath)) {
|
|
86307
86328
|
try {
|
|
@@ -86404,20 +86425,20 @@ __export(exports_scan, {
|
|
|
86404
86425
|
ledgerPathForBase: () => ledgerPathForBase
|
|
86405
86426
|
});
|
|
86406
86427
|
import {
|
|
86407
|
-
readFileSync as
|
|
86428
|
+
readFileSync as readFileSync115,
|
|
86408
86429
|
readdirSync as readdirSync50,
|
|
86409
|
-
existsSync as
|
|
86410
|
-
mkdirSync as
|
|
86411
|
-
writeFileSync as
|
|
86430
|
+
existsSync as existsSync129,
|
|
86431
|
+
mkdirSync as mkdirSync74,
|
|
86432
|
+
writeFileSync as writeFileSync56
|
|
86412
86433
|
} from "node:fs";
|
|
86413
|
-
import { resolve as resolve65, dirname as
|
|
86434
|
+
import { resolve as resolve65, dirname as dirname56 } from "node:path";
|
|
86414
86435
|
import { homedir as homedir69 } from "node:os";
|
|
86415
86436
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir69()) {
|
|
86416
86437
|
return resolve65(home2, ".switchroom");
|
|
86417
86438
|
}
|
|
86418
86439
|
function listAgents(base) {
|
|
86419
86440
|
const dir = resolve65(base, "agents");
|
|
86420
|
-
if (!
|
|
86441
|
+
if (!existsSync129(dir))
|
|
86421
86442
|
return [];
|
|
86422
86443
|
try {
|
|
86423
86444
|
return readdirSync50(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
@@ -86446,16 +86467,16 @@ function runScan(opts = {}) {
|
|
|
86446
86467
|
let gwText = "";
|
|
86447
86468
|
let sawArtifact = false;
|
|
86448
86469
|
try {
|
|
86449
|
-
if (
|
|
86450
|
-
turnsText =
|
|
86470
|
+
if (existsSync129(turnsPath)) {
|
|
86471
|
+
turnsText = readFileSync115(turnsPath, "utf-8");
|
|
86451
86472
|
sawArtifact = true;
|
|
86452
86473
|
}
|
|
86453
86474
|
} catch (e) {
|
|
86454
86475
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
86455
86476
|
}
|
|
86456
86477
|
try {
|
|
86457
|
-
if (
|
|
86458
|
-
gwText =
|
|
86478
|
+
if (existsSync129(gwPath)) {
|
|
86479
|
+
gwText = readFileSync115(gwPath, "utf-8");
|
|
86459
86480
|
sawArtifact = true;
|
|
86460
86481
|
}
|
|
86461
86482
|
} catch (e) {
|
|
@@ -86499,20 +86520,20 @@ function runScan(opts = {}) {
|
|
|
86499
86520
|
function readLedgerIfPresent(base) {
|
|
86500
86521
|
const path10 = ledgerPathForBase(base);
|
|
86501
86522
|
try {
|
|
86502
|
-
if (!
|
|
86523
|
+
if (!existsSync129(path10))
|
|
86503
86524
|
return null;
|
|
86504
|
-
return JSON.parse(
|
|
86525
|
+
return JSON.parse(readFileSync115(path10, "utf-8"));
|
|
86505
86526
|
} catch {
|
|
86506
86527
|
return null;
|
|
86507
86528
|
}
|
|
86508
86529
|
}
|
|
86509
86530
|
function ledgerPathForBase(base) {
|
|
86510
|
-
return fleetHealthLedgerPath(
|
|
86531
|
+
return fleetHealthLedgerPath(dirname56(base));
|
|
86511
86532
|
}
|
|
86512
86533
|
function writeLedger(base, ledger) {
|
|
86513
86534
|
const path10 = ledgerPathForBase(base);
|
|
86514
|
-
|
|
86515
|
-
|
|
86535
|
+
mkdirSync74(dirname56(path10), { recursive: true });
|
|
86536
|
+
writeFileSync56(path10, JSON.stringify(ledger, null, 2) + `
|
|
86516
86537
|
`, "utf-8");
|
|
86517
86538
|
return path10;
|
|
86518
86539
|
}
|
|
@@ -116745,9 +116766,9 @@ init_source();
|
|
|
116745
116766
|
init_loader();
|
|
116746
116767
|
init_lifecycle();
|
|
116747
116768
|
init_compose_env();
|
|
116748
|
-
import { existsSync as
|
|
116769
|
+
import { existsSync as existsSync95, mkdirSync as mkdirSync55, readFileSync as readFileSync86, realpathSync as realpathSync8, statSync as statSync56, chownSync as chownSync13 } from "node:fs";
|
|
116749
116770
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
116750
|
-
import { join as join98, dirname as
|
|
116771
|
+
import { join as join98, dirname as dirname42 } from "node:path";
|
|
116751
116772
|
import { homedir as homedir51 } from "node:os";
|
|
116752
116773
|
|
|
116753
116774
|
// src/cli/release-yaml.ts
|
|
@@ -117049,12 +117070,141 @@ function defaultSelfUpdateIO() {
|
|
|
117049
117070
|
init_self_invoke();
|
|
117050
117071
|
init_component_versions();
|
|
117051
117072
|
init_install_cron();
|
|
117073
|
+
|
|
117074
|
+
// src/memory/mm-refresh-cron.ts
|
|
117075
|
+
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
117076
|
+
import { chownSync as chownSync12, existsSync as existsSync93, mkdirSync as mkdirSync53, readFileSync as readFileSync84, renameSync as renameSync24, statSync as statSync55, writeFileSync as writeFileSync37 } from "node:fs";
|
|
117077
|
+
import { dirname as dirname40 } from "node:path";
|
|
117078
|
+
var CRON_PATH3 = "/etc/cron.d/mental-model-refresh";
|
|
117079
|
+
var CRON_SCHEDULE2 = "27 4 * * *";
|
|
117080
|
+
var CRON_LOG_PATH3 = "/var/log/mental-model-refresh.log";
|
|
117081
|
+
var LOGROTATE_PATH3 = "/etc/logrotate.d/mental-model-refresh";
|
|
117082
|
+
var CRON_LOCK_PATH3 = "/run/lock/mental-model-refresh.lock";
|
|
117083
|
+
function renderCron3(opts) {
|
|
117084
|
+
return `# switchroom mental-model-refresh \u2014 model-free stale-model refresh sweep (RFC P10).
|
|
117085
|
+
` + `# Managed by \`switchroom mental-model-refresh --install-cron\`; edits are overwritten.
|
|
117086
|
+
` + `# Exit 0 = swept (some refreshes may have failed; see log), 1 = the sweep could not complete.
|
|
117087
|
+
` + `SHELL=/bin/sh
|
|
117088
|
+
` + `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
117089
|
+
` + `${CRON_SCHEDULE2} ${opts.user} /usr/bin/flock -n ${CRON_LOCK_PATH3} ` + `${opts.binary} mental-model-refresh >> ${CRON_LOG_PATH3} 2>&1
|
|
117090
|
+
`;
|
|
117091
|
+
}
|
|
117092
|
+
function installCron3(opts) {
|
|
117093
|
+
const path7 = opts.path ?? CRON_PATH3;
|
|
117094
|
+
const content = renderCron3(opts);
|
|
117095
|
+
if (existsSync93(path7)) {
|
|
117096
|
+
try {
|
|
117097
|
+
if (readFileSync84(path7, "utf8") === content) {
|
|
117098
|
+
return { status: "unchanged", path: path7, content };
|
|
117099
|
+
}
|
|
117100
|
+
} catch {}
|
|
117101
|
+
}
|
|
117102
|
+
mkdirSync53(dirname40(path7), { recursive: true });
|
|
117103
|
+
const tmp = `${path7}.${process.pid}.tmp`;
|
|
117104
|
+
writeFileSync37(tmp, content, { mode: 420 });
|
|
117105
|
+
renameSync24(tmp, path7);
|
|
117106
|
+
return { status: "installed", path: path7, content };
|
|
117107
|
+
}
|
|
117108
|
+
var defaultIdResolver3 = (user) => {
|
|
117109
|
+
const uid = Number(execFileSync30("id", ["-u", user], { encoding: "utf8" }).trim());
|
|
117110
|
+
const gid = Number(execFileSync30("id", ["-g", user], { encoding: "utf8" }).trim());
|
|
117111
|
+
if (!Number.isInteger(uid) || !Number.isInteger(gid)) {
|
|
117112
|
+
throw new Error(`could not resolve uid/gid for user ${JSON.stringify(user)}`);
|
|
117113
|
+
}
|
|
117114
|
+
return { uid, gid };
|
|
117115
|
+
};
|
|
117116
|
+
function ensureLogFile3(opts) {
|
|
117117
|
+
const path7 = opts.path ?? CRON_LOG_PATH3;
|
|
117118
|
+
if (existsSync93(path7))
|
|
117119
|
+
return { status: "unchanged", path: path7 };
|
|
117120
|
+
const { uid, gid } = (opts.resolveIds ?? defaultIdResolver3)(opts.user);
|
|
117121
|
+
mkdirSync53(dirname40(path7), { recursive: true });
|
|
117122
|
+
writeFileSync37(path7, "", { mode: 420, flag: "a" });
|
|
117123
|
+
const isRoot = process.getuid?.() === 0;
|
|
117124
|
+
let alreadyOwned = false;
|
|
117125
|
+
try {
|
|
117126
|
+
const st = statSync55(path7);
|
|
117127
|
+
alreadyOwned = st.uid === uid && st.gid === gid;
|
|
117128
|
+
} catch {}
|
|
117129
|
+
if (isRoot && !alreadyOwned) {
|
|
117130
|
+
try {
|
|
117131
|
+
chownSync12(path7, uid, gid);
|
|
117132
|
+
} catch (e) {
|
|
117133
|
+
if (e.code !== "EPERM")
|
|
117134
|
+
throw e;
|
|
117135
|
+
}
|
|
117136
|
+
}
|
|
117137
|
+
return { status: "created", path: path7 };
|
|
117138
|
+
}
|
|
117139
|
+
function renderLogrotate3(opts) {
|
|
117140
|
+
return `${opts.logPath} {
|
|
117141
|
+
` + ` weekly
|
|
117142
|
+
` + ` rotate 8
|
|
117143
|
+
` + ` compress
|
|
117144
|
+
` + ` delaycompress
|
|
117145
|
+
` + ` missingok
|
|
117146
|
+
` + ` notifempty
|
|
117147
|
+
` + ` copytruncate
|
|
117148
|
+
` + ` su ${opts.user} ${opts.user}
|
|
117149
|
+
` + `}
|
|
117150
|
+
`;
|
|
117151
|
+
}
|
|
117152
|
+
function installLogrotate3(opts) {
|
|
117153
|
+
const path7 = opts.path ?? LOGROTATE_PATH3;
|
|
117154
|
+
const content = renderLogrotate3({ logPath: opts.logPath ?? CRON_LOG_PATH3, user: opts.user });
|
|
117155
|
+
if (existsSync93(path7)) {
|
|
117156
|
+
try {
|
|
117157
|
+
if (readFileSync84(path7, "utf8") === content) {
|
|
117158
|
+
return { status: "unchanged", path: path7, content };
|
|
117159
|
+
}
|
|
117160
|
+
} catch {}
|
|
117161
|
+
}
|
|
117162
|
+
mkdirSync53(dirname40(path7), { recursive: true });
|
|
117163
|
+
const tmp = `${path7}.${process.pid}.tmp`;
|
|
117164
|
+
writeFileSync37(tmp, content, { mode: 420 });
|
|
117165
|
+
renameSync24(tmp, path7);
|
|
117166
|
+
return { status: "installed", path: path7, content };
|
|
117167
|
+
}
|
|
117168
|
+
function parseCronUser3(content) {
|
|
117169
|
+
for (const line of content.split(`
|
|
117170
|
+
`)) {
|
|
117171
|
+
const trimmed = line.trim();
|
|
117172
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
117173
|
+
continue;
|
|
117174
|
+
if (!trimmed.includes("mental-model-refresh"))
|
|
117175
|
+
continue;
|
|
117176
|
+
const fields = trimmed.split(/\s+/);
|
|
117177
|
+
if (fields.length >= 7)
|
|
117178
|
+
return fields[5];
|
|
117179
|
+
}
|
|
117180
|
+
return null;
|
|
117181
|
+
}
|
|
117182
|
+
function reconcileCron3(opts) {
|
|
117183
|
+
const path7 = opts.path ?? CRON_PATH3;
|
|
117184
|
+
if (!existsSync93(path7))
|
|
117185
|
+
return { status: "absent", path: path7 };
|
|
117186
|
+
let user = opts.fallbackUser;
|
|
117187
|
+
try {
|
|
117188
|
+
user = parseCronUser3(readFileSync84(path7, "utf8")) ?? opts.fallbackUser;
|
|
117189
|
+
} catch {}
|
|
117190
|
+
if (!user) {
|
|
117191
|
+
throw new Error(`cannot reconcile ${path7}: no cron-user parseable from the existing fragment and no fallback user available`);
|
|
117192
|
+
}
|
|
117193
|
+
const r = installCron3({ user, binary: opts.binary, path: path7 });
|
|
117194
|
+
return {
|
|
117195
|
+
status: r.status === "installed" ? "reconciled" : "unchanged",
|
|
117196
|
+
path: path7,
|
|
117197
|
+
content: r.content
|
|
117198
|
+
};
|
|
117199
|
+
}
|
|
117200
|
+
|
|
117201
|
+
// src/cli/update.ts
|
|
117052
117202
|
init_install_cron2();
|
|
117053
117203
|
|
|
117054
117204
|
// src/host-cli-self-heal/install-timer.ts
|
|
117055
117205
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
117056
|
-
import { existsSync as
|
|
117057
|
-
import { dirname as
|
|
117206
|
+
import { existsSync as existsSync94, mkdirSync as mkdirSync54, readFileSync as readFileSync85, renameSync as renameSync25, writeFileSync as writeFileSync38 } from "node:fs";
|
|
117207
|
+
import { dirname as dirname41 } from "node:path";
|
|
117058
117208
|
var SERVICE_PATH = "/etc/systemd/system/switchroom-self-heal.service";
|
|
117059
117209
|
var TIMER_PATH = "/etc/systemd/system/switchroom-self-heal.timer";
|
|
117060
117210
|
var TIMER_UNIT = "switchroom-self-heal.timer";
|
|
@@ -117100,21 +117250,21 @@ function renderTimer() {
|
|
|
117100
117250
|
`;
|
|
117101
117251
|
}
|
|
117102
117252
|
function writeUnitIdempotent(path7, content) {
|
|
117103
|
-
if (
|
|
117253
|
+
if (existsSync94(path7)) {
|
|
117104
117254
|
try {
|
|
117105
|
-
if (
|
|
117255
|
+
if (readFileSync85(path7, "utf8") === content)
|
|
117106
117256
|
return "unchanged";
|
|
117107
117257
|
} catch {}
|
|
117108
117258
|
}
|
|
117109
|
-
|
|
117259
|
+
mkdirSync54(dirname41(path7), { recursive: true });
|
|
117110
117260
|
const tmp = `${path7}.${process.pid}.tmp`;
|
|
117111
|
-
|
|
117112
|
-
|
|
117261
|
+
writeFileSync38(tmp, content, { mode: 420 });
|
|
117262
|
+
renameSync25(tmp, path7);
|
|
117113
117263
|
return "written";
|
|
117114
117264
|
}
|
|
117115
117265
|
function homeFromPasswd2(user) {
|
|
117116
117266
|
try {
|
|
117117
|
-
for (const line of
|
|
117267
|
+
for (const line of readFileSync85("/etc/passwd", "utf8").split(`
|
|
117118
117268
|
`)) {
|
|
117119
117269
|
const f = line.split(":");
|
|
117120
117270
|
if (f[0] === user && f[5])
|
|
@@ -117131,7 +117281,7 @@ function resolveOperatorUser(env2) {
|
|
|
117131
117281
|
}
|
|
117132
117282
|
function installSelfHealTimer(deps = {}) {
|
|
117133
117283
|
const env2 = deps.env ?? process.env;
|
|
117134
|
-
const systemdBooted = deps.systemdBooted ?? (() =>
|
|
117284
|
+
const systemdBooted = deps.systemdBooted ?? (() => existsSync94(SYSTEMD_MARKER));
|
|
117135
117285
|
const geteuid = deps.geteuid ?? (() => typeof process.geteuid === "function" ? process.geteuid() : undefined);
|
|
117136
117286
|
const homeForUser = deps.homeForUser ?? homeFromPasswd2;
|
|
117137
117287
|
const servicePath = deps.servicePath ?? SERVICE_PATH;
|
|
@@ -117193,16 +117343,16 @@ function installSelfHealTimer(deps = {}) {
|
|
|
117193
117343
|
function defaultPersistPin(configPath) {
|
|
117194
117344
|
return (pin) => {
|
|
117195
117345
|
const path7 = configPath ?? findConfigFile();
|
|
117196
|
-
const before =
|
|
117346
|
+
const before = readFileSync86(path7, "utf8");
|
|
117197
117347
|
const after = setReleasePinInConfig(before, pin);
|
|
117198
117348
|
if (after === before)
|
|
117199
117349
|
return;
|
|
117200
|
-
writeConfigFileSync(path7, after,
|
|
117350
|
+
writeConfigFileSync(path7, after, statSync56(path7).mode & 511);
|
|
117201
117351
|
try {
|
|
117202
117352
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
117203
117353
|
const uid = resolveOperatorUid();
|
|
117204
117354
|
if (uid !== undefined)
|
|
117205
|
-
|
|
117355
|
+
chownSync13(path7, uid, uid);
|
|
117206
117356
|
}
|
|
117207
117357
|
} catch {}
|
|
117208
117358
|
};
|
|
@@ -117214,16 +117364,16 @@ var PACKAGED_INSTALL_KINDS = new Set([
|
|
|
117214
117364
|
"container"
|
|
117215
117365
|
]);
|
|
117216
117366
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
117217
|
-
let dir =
|
|
117367
|
+
let dir = dirname42(scriptPath);
|
|
117218
117368
|
for (let i2 = 0;i2 < 12; i2++) {
|
|
117219
|
-
if (
|
|
117369
|
+
if (existsSync95(join98(dir, ".git"))) {
|
|
117220
117370
|
try {
|
|
117221
|
-
const pkg = JSON.parse(
|
|
117371
|
+
const pkg = JSON.parse(readFileSync86(join98(dir, "package.json"), "utf-8"));
|
|
117222
117372
|
if (pkg.name === "switchroom")
|
|
117223
117373
|
return true;
|
|
117224
117374
|
} catch {}
|
|
117225
117375
|
}
|
|
117226
|
-
const parent =
|
|
117376
|
+
const parent = dirname42(dir);
|
|
117227
117377
|
if (parent === dir)
|
|
117228
117378
|
break;
|
|
117229
117379
|
dir = parent;
|
|
@@ -117285,7 +117435,7 @@ function planUpdate(opts) {
|
|
|
117285
117435
|
bundleDir: import.meta.dirname,
|
|
117286
117436
|
execPath: process.execPath,
|
|
117287
117437
|
scriptPath,
|
|
117288
|
-
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" ||
|
|
117438
|
+
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync95("/.dockerenv")
|
|
117289
117439
|
});
|
|
117290
117440
|
if (detection.kind !== "static-binary") {
|
|
117291
117441
|
say(source_default.gray(` host CLI not self-updated: ${detection.reason}
|
|
@@ -117368,7 +117518,7 @@ function planUpdate(opts) {
|
|
|
117368
117518
|
steps.push({
|
|
117369
117519
|
name: "pull-images",
|
|
117370
117520
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
117371
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
117521
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync95(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
117372
117522
|
run: () => {
|
|
117373
117523
|
const r = runner("docker", [
|
|
117374
117524
|
"compose",
|
|
@@ -117441,6 +117591,34 @@ function planUpdate(opts) {
|
|
|
117441
117591
|
`));
|
|
117442
117592
|
} else {
|
|
117443
117593
|
say(source_default.gray(` hindsight-watch cron already current at ${res.path}
|
|
117594
|
+
`));
|
|
117595
|
+
}
|
|
117596
|
+
}
|
|
117597
|
+
});
|
|
117598
|
+
steps.push({
|
|
117599
|
+
name: "reconcile-mental-model-refresh-cron",
|
|
117600
|
+
description: "Reconcile /etc/cron.d/mental-model-refresh (binary path / schedule / flags) on already-armed hosts \u2014 idempotent; no-op when unarmed or already current",
|
|
117601
|
+
skipReason: hostdContext ? "deferred (hostd-context): /etc/cron.d is on the host, not in the hostd container \u2014 finish host-side with `switchroom mental-model-refresh --install-cron`" : undefined,
|
|
117602
|
+
isHostdDeferred: hostdContext,
|
|
117603
|
+
run: () => {
|
|
117604
|
+
const say = opts.stdout ?? ((t) => process.stdout.write(t));
|
|
117605
|
+
const binary = process.env.SWITCHROOM_BINARY ?? "/usr/local/bin/switchroom";
|
|
117606
|
+
const fallbackUser = process.env.SUDO_USER ?? process.env.USER ?? process.env.LOGNAME;
|
|
117607
|
+
let res;
|
|
117608
|
+
try {
|
|
117609
|
+
res = reconcileCron3({ binary, fallbackUser });
|
|
117610
|
+
} catch (e) {
|
|
117611
|
+
say(source_default.yellow(` mental-model-refresh cron not reconciled: ${e.message}
|
|
117612
|
+
`));
|
|
117613
|
+
return;
|
|
117614
|
+
}
|
|
117615
|
+
if (res.status === "absent") {
|
|
117616
|
+
say(source_default.gray(" mental-model-refresh cron not armed \u2014 skipping reconcile " + "(arm with `switchroom mental-model-refresh --install-cron`)\n"));
|
|
117617
|
+
} else if (res.status === "reconciled") {
|
|
117618
|
+
say(source_default.green(` mental-model-refresh cron reconciled at ${res.path}
|
|
117619
|
+
`));
|
|
117620
|
+
} else {
|
|
117621
|
+
say(source_default.gray(` mental-model-refresh cron already current at ${res.path}
|
|
117444
117622
|
`));
|
|
117445
117623
|
}
|
|
117446
117624
|
}
|
|
@@ -117603,7 +117781,7 @@ function planUpdate(opts) {
|
|
|
117603
117781
|
bundleDir: import.meta.dirname,
|
|
117604
117782
|
execPath: process.execPath,
|
|
117605
117783
|
scriptPath,
|
|
117606
|
-
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" ||
|
|
117784
|
+
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync95("/.dockerenv")
|
|
117607
117785
|
});
|
|
117608
117786
|
const detail = `no shipped skills/ payload found for this install ` + `(${detection.kind}; ${describeShippedAssetSearch(resolution)})`;
|
|
117609
117787
|
if (PACKAGED_INSTALL_KINDS.has(detection.kind)) {
|
|
@@ -117617,7 +117795,7 @@ function planUpdate(opts) {
|
|
|
117617
117795
|
}
|
|
117618
117796
|
const source = resolution.path;
|
|
117619
117797
|
try {
|
|
117620
|
-
|
|
117798
|
+
mkdirSync55(dirname42(dest), { recursive: true });
|
|
117621
117799
|
const r = syncBundledSkills({
|
|
117622
117800
|
source,
|
|
117623
117801
|
dest,
|
|
@@ -117643,10 +117821,10 @@ function planUpdate(opts) {
|
|
|
117643
117821
|
if (opts.syncBundledSkillsFn)
|
|
117644
117822
|
return;
|
|
117645
117823
|
const dest = join98(homedir51(), ".switchroom", "skills", "_bundled");
|
|
117646
|
-
if (!
|
|
117824
|
+
if (!existsSync95(dest)) {
|
|
117647
117825
|
return;
|
|
117648
117826
|
}
|
|
117649
|
-
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !
|
|
117827
|
+
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync95(join98(dest, key)));
|
|
117650
117828
|
if (missing.length > 0) {
|
|
117651
117829
|
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.`);
|
|
117652
117830
|
}
|
|
@@ -117681,7 +117859,7 @@ function planUpdate(opts) {
|
|
|
117681
117859
|
description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
|
|
117682
117860
|
run: () => {
|
|
117683
117861
|
try {
|
|
117684
|
-
const composeText =
|
|
117862
|
+
const composeText = readFileSync86(composePath, "utf8");
|
|
117685
117863
|
const pf = validateBindSources(composeText);
|
|
117686
117864
|
if (!pf.ok)
|
|
117687
117865
|
throw new Error(formatPreflightError(pf));
|
|
@@ -117756,14 +117934,14 @@ function defaultStatusProbe(composePath) {
|
|
|
117756
117934
|
} catch {}
|
|
117757
117935
|
if (scriptPath) {
|
|
117758
117936
|
try {
|
|
117759
|
-
cliBuiltAt = new Date(
|
|
117937
|
+
cliBuiltAt = new Date(statSync56(scriptPath).mtimeMs).toISOString();
|
|
117760
117938
|
} catch {}
|
|
117761
|
-
let dir =
|
|
117939
|
+
let dir = dirname42(scriptPath);
|
|
117762
117940
|
for (let i2 = 0;i2 < 8; i2++) {
|
|
117763
117941
|
const pkgPath = join98(dir, "package.json");
|
|
117764
|
-
if (
|
|
117942
|
+
if (existsSync95(pkgPath)) {
|
|
117765
117943
|
try {
|
|
117766
|
-
const pkg = JSON.parse(
|
|
117944
|
+
const pkg = JSON.parse(readFileSync86(pkgPath, "utf-8"));
|
|
117767
117945
|
if (typeof pkg.version === "string")
|
|
117768
117946
|
cliVersion = pkg.version;
|
|
117769
117947
|
} catch (err) {
|
|
@@ -117771,7 +117949,7 @@ function defaultStatusProbe(composePath) {
|
|
|
117771
117949
|
}
|
|
117772
117950
|
break;
|
|
117773
117951
|
}
|
|
117774
|
-
const parent =
|
|
117952
|
+
const parent = dirname42(dir);
|
|
117775
117953
|
if (parent === dir)
|
|
117776
117954
|
break;
|
|
117777
117955
|
dir = parent;
|
|
@@ -117784,7 +117962,7 @@ function defaultStatusProbe(composePath) {
|
|
|
117784
117962
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
117785
117963
|
}
|
|
117786
117964
|
const services = [];
|
|
117787
|
-
if (!
|
|
117965
|
+
if (!existsSync95(composePath)) {
|
|
117788
117966
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
117789
117967
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
117790
117968
|
}
|
|
@@ -118047,22 +118225,22 @@ function registerUpdateCommand(program3) {
|
|
|
118047
118225
|
// src/cli/rollout.ts
|
|
118048
118226
|
init_helpers();
|
|
118049
118227
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
118050
|
-
import { readFileSync as
|
|
118228
|
+
import { readFileSync as readFileSync88, chownSync as chownSync14, statSync as statSync59 } from "node:fs";
|
|
118051
118229
|
import { homedir as homedir53 } from "node:os";
|
|
118052
118230
|
|
|
118053
118231
|
// src/cli/rollout-pin-journal.ts
|
|
118054
118232
|
import {
|
|
118055
|
-
existsSync as
|
|
118056
|
-
readFileSync as
|
|
118057
|
-
writeFileSync as
|
|
118058
|
-
renameSync as
|
|
118233
|
+
existsSync as existsSync96,
|
|
118234
|
+
readFileSync as readFileSync87,
|
|
118235
|
+
writeFileSync as writeFileSync39,
|
|
118236
|
+
renameSync as renameSync26,
|
|
118059
118237
|
unlinkSync as unlinkSync19,
|
|
118060
|
-
statSync as
|
|
118061
|
-
mkdirSync as
|
|
118238
|
+
statSync as statSync57,
|
|
118239
|
+
mkdirSync as mkdirSync56
|
|
118062
118240
|
} from "node:fs";
|
|
118063
118241
|
import { homedir as homedir52 } from "node:os";
|
|
118064
118242
|
import { createHash as createHash17 } from "node:crypto";
|
|
118065
|
-
import { join as join99, basename as basename9, resolve as resolve50, dirname as
|
|
118243
|
+
import { join as join99, basename as basename9, resolve as resolve50, dirname as dirname43 } from "node:path";
|
|
118066
118244
|
init_flock();
|
|
118067
118245
|
var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
|
|
118068
118246
|
var STATE_DIR_NAME = ".switchroom";
|
|
@@ -118071,7 +118249,7 @@ function pinJournalDir(configPath) {
|
|
|
118071
118249
|
if (override && override.trim().length > 0)
|
|
118072
118250
|
return override.trim();
|
|
118073
118251
|
if (configPath) {
|
|
118074
|
-
const dir =
|
|
118252
|
+
const dir = dirname43(resolve50(configPath));
|
|
118075
118253
|
if (basename9(dir) === STATE_DIR_NAME)
|
|
118076
118254
|
return dir;
|
|
118077
118255
|
}
|
|
@@ -118114,9 +118292,9 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
|
|
|
118114
118292
|
const p = pinJournalPath(configPath);
|
|
118115
118293
|
let raw;
|
|
118116
118294
|
try {
|
|
118117
|
-
raw =
|
|
118295
|
+
raw = readFileSync87(p, "utf8");
|
|
118118
118296
|
} catch (e) {
|
|
118119
|
-
if (
|
|
118297
|
+
if (existsSync96(p)) {
|
|
118120
118298
|
warn(`\u26a0\ufe0f rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted \u2014 verify it host-side before the next reconcile.
|
|
118121
118299
|
`);
|
|
118122
118300
|
}
|
|
@@ -118167,7 +118345,7 @@ function beginPinPersist(configPath, pin, opts = {}) {
|
|
|
118167
118345
|
warn(`\u26a0\ufe0f rollout pin journal: overwriting an ABANDONED journal at ${p} ` + `(pid ${existing.pid} recorded ${existing.at}, provisional pin ` + `${existing.pin}). Its roll never committed or reverted; \`release.pin\` ` + `in ${configPath} may still name that unproven build \u2014 verify it.
|
|
118168
118346
|
`);
|
|
118169
118347
|
}
|
|
118170
|
-
const priorPin = getReleasePinFromConfig(
|
|
118348
|
+
const priorPin = getReleasePinFromConfig(readFileSync87(configPath, "utf8"));
|
|
118171
118349
|
const journal = {
|
|
118172
118350
|
v: 1,
|
|
118173
118351
|
configPath,
|
|
@@ -118176,10 +118354,10 @@ function beginPinPersist(configPath, pin, opts = {}) {
|
|
|
118176
118354
|
pid: process.pid,
|
|
118177
118355
|
at: new Date().toISOString()
|
|
118178
118356
|
};
|
|
118179
|
-
|
|
118357
|
+
mkdirSync56(dirname43(p), { recursive: true });
|
|
118180
118358
|
const tmp = `${p}.${process.pid}.tmp`;
|
|
118181
|
-
|
|
118182
|
-
|
|
118359
|
+
writeFileSync39(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
|
|
118360
|
+
renameSync26(tmp, p);
|
|
118183
118361
|
return journal;
|
|
118184
118362
|
}
|
|
118185
118363
|
function commitPinPersist(configPath) {
|
|
@@ -118188,7 +118366,7 @@ function commitPinPersist(configPath) {
|
|
|
118188
118366
|
unlinkSync19(p);
|
|
118189
118367
|
return null;
|
|
118190
118368
|
} catch (e) {
|
|
118191
|
-
if (!
|
|
118369
|
+
if (!existsSync96(p))
|
|
118192
118370
|
return null;
|
|
118193
118371
|
return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side \u2014 while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
|
|
118194
118372
|
}
|
|
@@ -118210,16 +118388,16 @@ function rollbackPinPersist(configPath, opts = {}) {
|
|
|
118210
118388
|
}
|
|
118211
118389
|
}
|
|
118212
118390
|
try {
|
|
118213
|
-
const current =
|
|
118391
|
+
const current = readFileSync87(configPath, "utf8");
|
|
118214
118392
|
const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
|
|
118215
118393
|
let mode = 384;
|
|
118216
118394
|
try {
|
|
118217
|
-
mode =
|
|
118395
|
+
mode = statSync57(configPath).mode & 511;
|
|
118218
118396
|
} catch {}
|
|
118219
118397
|
if (opts.writeConfig) {
|
|
118220
118398
|
opts.writeConfig(configPath, next, mode);
|
|
118221
118399
|
} else {
|
|
118222
|
-
|
|
118400
|
+
writeFileSync39(configPath, next, { encoding: "utf8", mode });
|
|
118223
118401
|
}
|
|
118224
118402
|
} catch (e) {
|
|
118225
118403
|
return {
|
|
@@ -118264,8 +118442,8 @@ init_host_cli_stamp();
|
|
|
118264
118442
|
|
|
118265
118443
|
// src/cli/host-cli-upgrade.ts
|
|
118266
118444
|
init_self_update();
|
|
118267
|
-
import { lchownSync, lstatSync as lstatSync14, readdirSync as readdirSync33, statSync as
|
|
118268
|
-
import { basename as basename10, dirname as
|
|
118445
|
+
import { lchownSync, lstatSync as lstatSync14, readdirSync as readdirSync33, statSync as statSync58 } from "node:fs";
|
|
118446
|
+
import { basename as basename10, dirname as dirname44, join as join100 } from "node:path";
|
|
118269
118447
|
init_shipped_assets();
|
|
118270
118448
|
var HOST_CLI_UPGRADE_SENTINEL = "SWITCHROOM_HOST_CLI_UPGRADE:";
|
|
118271
118449
|
function encodeHostCliUpgradeResult(r) {
|
|
@@ -118301,7 +118479,7 @@ function defaultIo() {
|
|
|
118301
118479
|
return {
|
|
118302
118480
|
isFile: (p) => {
|
|
118303
118481
|
try {
|
|
118304
|
-
return
|
|
118482
|
+
return statSync58(p).isFile();
|
|
118305
118483
|
} catch {
|
|
118306
118484
|
return false;
|
|
118307
118485
|
}
|
|
@@ -118401,8 +118579,8 @@ async function runHostCliUpgrade(opts, io = defaultIo(), log = () => {}) {
|
|
|
118401
118579
|
const installRoot = payloadInstallRoot(binary);
|
|
118402
118580
|
const failures = handBackOwnership([
|
|
118403
118581
|
binary,
|
|
118404
|
-
join100(
|
|
118405
|
-
|
|
118582
|
+
join100(dirname44(binary), ".switchroom-versions"),
|
|
118583
|
+
dirname44(installRoot),
|
|
118406
118584
|
installRoot,
|
|
118407
118585
|
payloadVersionDir(installRoot, pin)
|
|
118408
118586
|
], priorOwner, io);
|
|
@@ -119202,7 +119380,7 @@ function createRolloutDeps(params) {
|
|
|
119202
119380
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
119203
119381
|
const uid = resolveOperatorUid();
|
|
119204
119382
|
if (uid !== undefined) {
|
|
119205
|
-
|
|
119383
|
+
chownSync14(path7, uid, uid);
|
|
119206
119384
|
} else if (hostdCtx) {
|
|
119207
119385
|
warn("\u26a0\ufe0f rollout (hostd path): could not resolve operator uid; " + "skipping config chown-back to avoid leaving switchroom.yaml " + "root-owned. Verify ownership host-side if other yaml verbs " + `EACCES.
|
|
119208
119386
|
`);
|
|
@@ -119261,12 +119439,12 @@ function createRolloutDeps(params) {
|
|
|
119261
119439
|
return stamp ? { hostCli: stamp } : {};
|
|
119262
119440
|
})(),
|
|
119263
119441
|
persistPin: (pin) => {
|
|
119264
|
-
const before =
|
|
119442
|
+
const before = readFileSync88(configPath, "utf8");
|
|
119265
119443
|
const after = setReleasePinInConfig(before, pin);
|
|
119266
119444
|
if (after === before)
|
|
119267
119445
|
return false;
|
|
119268
119446
|
beginPinPersist(configPath, pin);
|
|
119269
|
-
writeConfigPreservingOwnership(configPath, after,
|
|
119447
|
+
writeConfigPreservingOwnership(configPath, after, statSync59(configPath).mode & 511);
|
|
119270
119448
|
return true;
|
|
119271
119449
|
},
|
|
119272
119450
|
commitPin: () => {
|
|
@@ -119550,8 +119728,8 @@ init_helpers();
|
|
|
119550
119728
|
init_lifecycle();
|
|
119551
119729
|
init_resolve_version();
|
|
119552
119730
|
import { execSync as execSync3 } from "node:child_process";
|
|
119553
|
-
import { existsSync as
|
|
119554
|
-
import { dirname as
|
|
119731
|
+
import { existsSync as existsSync97, readFileSync as readFileSync89 } from "node:fs";
|
|
119732
|
+
import { dirname as dirname45, join as join101 } from "node:path";
|
|
119555
119733
|
function getClaudeCodeVersion() {
|
|
119556
119734
|
try {
|
|
119557
119735
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -119602,15 +119780,15 @@ function locateSwitchroomInstallDir() {
|
|
|
119602
119780
|
let dir = import.meta.dirname;
|
|
119603
119781
|
for (let i2 = 0;i2 < 10 && dir && dir !== "/"; i2++) {
|
|
119604
119782
|
const pkgPath = join101(dir, "package.json");
|
|
119605
|
-
if (
|
|
119783
|
+
if (existsSync97(pkgPath)) {
|
|
119606
119784
|
try {
|
|
119607
|
-
const pkg = JSON.parse(
|
|
119608
|
-
if (pkg.name === "switchroom" &&
|
|
119785
|
+
const pkg = JSON.parse(readFileSync89(pkgPath, "utf-8"));
|
|
119786
|
+
if (pkg.name === "switchroom" && existsSync97(join101(dir, ".git"))) {
|
|
119609
119787
|
return dir;
|
|
119610
119788
|
}
|
|
119611
119789
|
} catch {}
|
|
119612
119790
|
}
|
|
119613
|
-
dir =
|
|
119791
|
+
dir = dirname45(dir);
|
|
119614
119792
|
}
|
|
119615
119793
|
return null;
|
|
119616
119794
|
}
|
|
@@ -119786,9 +119964,9 @@ init_merge();
|
|
|
119786
119964
|
|
|
119787
119965
|
// src/agents/session-retention.ts
|
|
119788
119966
|
import {
|
|
119789
|
-
existsSync as
|
|
119967
|
+
existsSync as existsSync98,
|
|
119790
119968
|
readdirSync as readdirSync34,
|
|
119791
|
-
statSync as
|
|
119969
|
+
statSync as statSync60,
|
|
119792
119970
|
unlinkSync as unlinkSync20
|
|
119793
119971
|
} from "node:fs";
|
|
119794
119972
|
import { join as join102 } from "node:path";
|
|
@@ -119797,7 +119975,7 @@ var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
|
119797
119975
|
var MIN_KEEP = 2;
|
|
119798
119976
|
function collectSessionJsonl(claudeConfigDir) {
|
|
119799
119977
|
const projects = join102(claudeConfigDir, "projects");
|
|
119800
|
-
if (!
|
|
119978
|
+
if (!existsSync98(projects))
|
|
119801
119979
|
return [];
|
|
119802
119980
|
const found = [];
|
|
119803
119981
|
const walk2 = (dir) => {
|
|
@@ -119811,7 +119989,7 @@ function collectSessionJsonl(claudeConfigDir) {
|
|
|
119811
119989
|
const full = join102(dir, name);
|
|
119812
119990
|
let st;
|
|
119813
119991
|
try {
|
|
119814
|
-
st =
|
|
119992
|
+
st = statSync60(full);
|
|
119815
119993
|
} catch {
|
|
119816
119994
|
continue;
|
|
119817
119995
|
}
|
|
@@ -119929,15 +120107,15 @@ function registerHandoffCommand(program3) {
|
|
|
119929
120107
|
// src/issues/store.ts
|
|
119930
120108
|
import {
|
|
119931
120109
|
closeSync as closeSync19,
|
|
119932
|
-
existsSync as
|
|
119933
|
-
mkdirSync as
|
|
120110
|
+
existsSync as existsSync99,
|
|
120111
|
+
mkdirSync as mkdirSync57,
|
|
119934
120112
|
openSync as openSync19,
|
|
119935
120113
|
readdirSync as readdirSync35,
|
|
119936
|
-
readFileSync as
|
|
119937
|
-
renameSync as
|
|
119938
|
-
statSync as
|
|
120114
|
+
readFileSync as readFileSync90,
|
|
120115
|
+
renameSync as renameSync27,
|
|
120116
|
+
statSync as statSync61,
|
|
119939
120117
|
unlinkSync as unlinkSync21,
|
|
119940
|
-
writeFileSync as
|
|
120118
|
+
writeFileSync as writeFileSync40,
|
|
119941
120119
|
writeSync as writeSync9
|
|
119942
120120
|
} from "node:fs";
|
|
119943
120121
|
import { join as join103 } from "node:path";
|
|
@@ -119968,11 +120146,11 @@ var ISSUES_FILE = "issues.jsonl";
|
|
|
119968
120146
|
var ISSUES_LOCK = "issues.lock";
|
|
119969
120147
|
function readAll(stateDir) {
|
|
119970
120148
|
const path7 = join103(stateDir, ISSUES_FILE);
|
|
119971
|
-
if (!
|
|
120149
|
+
if (!existsSync99(path7))
|
|
119972
120150
|
return [];
|
|
119973
120151
|
let raw;
|
|
119974
120152
|
try {
|
|
119975
|
-
raw =
|
|
120153
|
+
raw = readFileSync90(path7, "utf-8");
|
|
119976
120154
|
} catch {
|
|
119977
120155
|
return [];
|
|
119978
120156
|
}
|
|
@@ -120045,7 +120223,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
120045
120223
|
});
|
|
120046
120224
|
}
|
|
120047
120225
|
function resolve53(stateDir, fingerprint, nowFn = Date.now) {
|
|
120048
|
-
if (!
|
|
120226
|
+
if (!existsSync99(join103(stateDir, ISSUES_FILE)))
|
|
120049
120227
|
return 0;
|
|
120050
120228
|
return withLock(stateDir, () => {
|
|
120051
120229
|
const all = readAll(stateDir);
|
|
@@ -120063,7 +120241,7 @@ function resolve53(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
120063
120241
|
});
|
|
120064
120242
|
}
|
|
120065
120243
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
120066
|
-
if (!
|
|
120244
|
+
if (!existsSync99(join103(stateDir, ISSUES_FILE)))
|
|
120067
120245
|
return 0;
|
|
120068
120246
|
return withLock(stateDir, () => {
|
|
120069
120247
|
const all = readAll(stateDir);
|
|
@@ -120081,7 +120259,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
120081
120259
|
});
|
|
120082
120260
|
}
|
|
120083
120261
|
function prune(stateDir, opts = {}) {
|
|
120084
|
-
if (!
|
|
120262
|
+
if (!existsSync99(join103(stateDir, ISSUES_FILE)))
|
|
120085
120263
|
return 0;
|
|
120086
120264
|
return withLock(stateDir, () => {
|
|
120087
120265
|
const all = readAll(stateDir);
|
|
@@ -120111,7 +120289,7 @@ function prune(stateDir, opts = {}) {
|
|
|
120111
120289
|
});
|
|
120112
120290
|
}
|
|
120113
120291
|
function ensureDir2(stateDir) {
|
|
120114
|
-
|
|
120292
|
+
mkdirSync57(stateDir, { recursive: true });
|
|
120115
120293
|
}
|
|
120116
120294
|
function writeAll(stateDir, events) {
|
|
120117
120295
|
const path7 = join103(stateDir, ISSUES_FILE);
|
|
@@ -120120,8 +120298,8 @@ function writeAll(stateDir, events) {
|
|
|
120120
120298
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
120121
120299
|
`) + `
|
|
120122
120300
|
`;
|
|
120123
|
-
|
|
120124
|
-
|
|
120301
|
+
writeFileSync40(tmp, body, "utf-8");
|
|
120302
|
+
renameSync27(tmp, path7);
|
|
120125
120303
|
}
|
|
120126
120304
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
120127
120305
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -120138,7 +120316,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
120138
120316
|
continue;
|
|
120139
120317
|
const tmpPath = join103(stateDir, entry);
|
|
120140
120318
|
try {
|
|
120141
|
-
const stat =
|
|
120319
|
+
const stat = statSync61(tmpPath);
|
|
120142
120320
|
if (stat.mtimeMs < cutoff) {
|
|
120143
120321
|
unlinkSync21(tmpPath);
|
|
120144
120322
|
}
|
|
@@ -120183,7 +120361,7 @@ function withLock(stateDir, fn) {
|
|
|
120183
120361
|
function tryStealStaleLock(lockPath) {
|
|
120184
120362
|
let pidStr;
|
|
120185
120363
|
try {
|
|
120186
|
-
pidStr =
|
|
120364
|
+
pidStr = readFileSync90(lockPath, "utf-8").trim();
|
|
120187
120365
|
} catch {
|
|
120188
120366
|
return true;
|
|
120189
120367
|
}
|
|
@@ -120431,22 +120609,22 @@ function relTime(deltaMs) {
|
|
|
120431
120609
|
|
|
120432
120610
|
// src/cli/deps.ts
|
|
120433
120611
|
init_source();
|
|
120434
|
-
import { existsSync as
|
|
120612
|
+
import { existsSync as existsSync102 } from "node:fs";
|
|
120435
120613
|
import { homedir as homedir56 } from "node:os";
|
|
120436
120614
|
import { join as join106, resolve as resolve54 } from "node:path";
|
|
120437
120615
|
|
|
120438
120616
|
// src/deps/python.ts
|
|
120439
120617
|
import { createHash as createHash18 } from "node:crypto";
|
|
120440
120618
|
import {
|
|
120441
|
-
existsSync as
|
|
120442
|
-
mkdirSync as
|
|
120443
|
-
readFileSync as
|
|
120619
|
+
existsSync as existsSync100,
|
|
120620
|
+
mkdirSync as mkdirSync58,
|
|
120621
|
+
readFileSync as readFileSync91,
|
|
120444
120622
|
rmSync as rmSync19,
|
|
120445
|
-
writeFileSync as
|
|
120623
|
+
writeFileSync as writeFileSync41
|
|
120446
120624
|
} from "node:fs";
|
|
120447
|
-
import { dirname as
|
|
120625
|
+
import { dirname as dirname46, join as join104 } from "node:path";
|
|
120448
120626
|
import { homedir as homedir54 } from "node:os";
|
|
120449
|
-
import { execFileSync as
|
|
120627
|
+
import { execFileSync as execFileSync31 } from "node:child_process";
|
|
120450
120628
|
|
|
120451
120629
|
class PythonEnvError extends Error {
|
|
120452
120630
|
stderr;
|
|
@@ -120460,13 +120638,13 @@ function defaultPythonCacheRoot() {
|
|
|
120460
120638
|
return join104(homedir54(), ".switchroom", "deps", "python");
|
|
120461
120639
|
}
|
|
120462
120640
|
function hashFile(path7) {
|
|
120463
|
-
return createHash18("sha256").update(
|
|
120641
|
+
return createHash18("sha256").update(readFileSync91(path7)).digest("hex");
|
|
120464
120642
|
}
|
|
120465
120643
|
function ensurePythonEnv(opts) {
|
|
120466
120644
|
const { skillName, requirementsPath, force = false } = opts;
|
|
120467
120645
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
120468
120646
|
const hostPython = opts.pythonBin ?? "python3";
|
|
120469
|
-
if (!
|
|
120647
|
+
if (!existsSync100(requirementsPath)) {
|
|
120470
120648
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
120471
120649
|
}
|
|
120472
120650
|
const venvDir = join104(cacheRoot, skillName);
|
|
@@ -120475,8 +120653,8 @@ function ensurePythonEnv(opts) {
|
|
|
120475
120653
|
const pythonBin = join104(binDir, "python");
|
|
120476
120654
|
const pipBin = join104(binDir, "pip");
|
|
120477
120655
|
const targetHash = hashFile(requirementsPath);
|
|
120478
|
-
if (!force &&
|
|
120479
|
-
const existingHash =
|
|
120656
|
+
if (!force && existsSync100(stampPath) && existsSync100(pythonBin)) {
|
|
120657
|
+
const existingHash = readFileSync91(stampPath, "utf8").trim();
|
|
120480
120658
|
if (existingHash === targetHash) {
|
|
120481
120659
|
return {
|
|
120482
120660
|
skillName,
|
|
@@ -120488,12 +120666,12 @@ function ensurePythonEnv(opts) {
|
|
|
120488
120666
|
};
|
|
120489
120667
|
}
|
|
120490
120668
|
}
|
|
120491
|
-
if (
|
|
120669
|
+
if (existsSync100(venvDir)) {
|
|
120492
120670
|
rmSync19(venvDir, { recursive: true, force: true });
|
|
120493
120671
|
}
|
|
120494
|
-
|
|
120672
|
+
mkdirSync58(dirname46(venvDir), { recursive: true });
|
|
120495
120673
|
try {
|
|
120496
|
-
|
|
120674
|
+
execFileSync31(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
120497
120675
|
} catch (err) {
|
|
120498
120676
|
const e = err;
|
|
120499
120677
|
throw new PythonEnvError(`Failed to create venv for skill "${skillName}" with ${hostPython}: ${e.message}`, e.stderr?.toString());
|
|
@@ -120505,12 +120683,12 @@ function ensurePythonEnv(opts) {
|
|
|
120505
120683
|
delete childEnv.PIP_TARGET;
|
|
120506
120684
|
delete childEnv.PIP_PREFIX;
|
|
120507
120685
|
delete childEnv.PYTHONUSERBASE;
|
|
120508
|
-
|
|
120686
|
+
execFileSync31(pipBin, ["install", "--disable-pip-version-check", "-r", requirementsPath], { stdio: "pipe", env: childEnv });
|
|
120509
120687
|
} catch (err) {
|
|
120510
120688
|
const e = err;
|
|
120511
120689
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
120512
120690
|
}
|
|
120513
|
-
|
|
120691
|
+
writeFileSync41(stampPath, targetHash + `
|
|
120514
120692
|
`);
|
|
120515
120693
|
return {
|
|
120516
120694
|
skillName,
|
|
@@ -120526,15 +120704,15 @@ function ensurePythonEnv(opts) {
|
|
|
120526
120704
|
import { createHash as createHash19 } from "node:crypto";
|
|
120527
120705
|
import {
|
|
120528
120706
|
copyFileSync as copyFileSync13,
|
|
120529
|
-
existsSync as
|
|
120530
|
-
mkdirSync as
|
|
120531
|
-
readFileSync as
|
|
120707
|
+
existsSync as existsSync101,
|
|
120708
|
+
mkdirSync as mkdirSync59,
|
|
120709
|
+
readFileSync as readFileSync92,
|
|
120532
120710
|
rmSync as rmSync20,
|
|
120533
|
-
writeFileSync as
|
|
120711
|
+
writeFileSync as writeFileSync42
|
|
120534
120712
|
} from "node:fs";
|
|
120535
|
-
import { dirname as
|
|
120713
|
+
import { dirname as dirname47, join as join105 } from "node:path";
|
|
120536
120714
|
import { homedir as homedir55 } from "node:os";
|
|
120537
|
-
import { execFileSync as
|
|
120715
|
+
import { execFileSync as execFileSync32 } from "node:child_process";
|
|
120538
120716
|
|
|
120539
120717
|
class NodeEnvError extends Error {
|
|
120540
120718
|
stderr;
|
|
@@ -120559,20 +120737,20 @@ function defaultNodeCacheRoot() {
|
|
|
120559
120737
|
return join105(homedir55(), ".switchroom", "deps", "node");
|
|
120560
120738
|
}
|
|
120561
120739
|
function hashDepInputs(packageJsonPath) {
|
|
120562
|
-
const sourceDir =
|
|
120740
|
+
const sourceDir = dirname47(packageJsonPath);
|
|
120563
120741
|
const hasher = createHash19("sha256");
|
|
120564
120742
|
hasher.update(`package.json
|
|
120565
120743
|
`);
|
|
120566
|
-
hasher.update(
|
|
120744
|
+
hasher.update(readFileSync92(packageJsonPath));
|
|
120567
120745
|
for (const lockName of ALL_LOCKFILES) {
|
|
120568
120746
|
const lockPath = join105(sourceDir, lockName);
|
|
120569
|
-
if (
|
|
120747
|
+
if (existsSync101(lockPath)) {
|
|
120570
120748
|
hasher.update(`
|
|
120571
120749
|
`);
|
|
120572
120750
|
hasher.update(lockName);
|
|
120573
120751
|
hasher.update(`
|
|
120574
120752
|
`);
|
|
120575
|
-
hasher.update(
|
|
120753
|
+
hasher.update(readFileSync92(lockPath));
|
|
120576
120754
|
}
|
|
120577
120755
|
}
|
|
120578
120756
|
return hasher.digest("hex");
|
|
@@ -120581,17 +120759,17 @@ function ensureNodeEnv(opts) {
|
|
|
120581
120759
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
120582
120760
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
120583
120761
|
const installer = opts.installer ?? "bun";
|
|
120584
|
-
if (!
|
|
120762
|
+
if (!existsSync101(packageJsonPath)) {
|
|
120585
120763
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
120586
120764
|
}
|
|
120587
|
-
const sourceDir =
|
|
120765
|
+
const sourceDir = dirname47(packageJsonPath);
|
|
120588
120766
|
const envDir = join105(cacheRoot, skillName);
|
|
120589
120767
|
const stampPath = join105(envDir, ".package.sha256");
|
|
120590
120768
|
const nodeModulesDir = join105(envDir, "node_modules");
|
|
120591
120769
|
const binDir = join105(nodeModulesDir, ".bin");
|
|
120592
120770
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
120593
|
-
if (!force &&
|
|
120594
|
-
const existingHash =
|
|
120771
|
+
if (!force && existsSync101(stampPath) && existsSync101(nodeModulesDir)) {
|
|
120772
|
+
const existingHash = readFileSync92(stampPath, "utf8").trim();
|
|
120595
120773
|
if (existingHash === targetHash) {
|
|
120596
120774
|
return {
|
|
120597
120775
|
skillName,
|
|
@@ -120602,15 +120780,15 @@ function ensureNodeEnv(opts) {
|
|
|
120602
120780
|
};
|
|
120603
120781
|
}
|
|
120604
120782
|
}
|
|
120605
|
-
if (
|
|
120783
|
+
if (existsSync101(envDir)) {
|
|
120606
120784
|
rmSync20(envDir, { recursive: true, force: true });
|
|
120607
120785
|
}
|
|
120608
|
-
|
|
120786
|
+
mkdirSync59(envDir, { recursive: true });
|
|
120609
120787
|
copyFileSync13(packageJsonPath, join105(envDir, "package.json"));
|
|
120610
120788
|
let copiedLockfile = false;
|
|
120611
120789
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
120612
120790
|
const lockPath = join105(sourceDir, lockName);
|
|
120613
|
-
if (
|
|
120791
|
+
if (existsSync101(lockPath)) {
|
|
120614
120792
|
copyFileSync13(lockPath, join105(envDir, lockName));
|
|
120615
120793
|
copiedLockfile = true;
|
|
120616
120794
|
}
|
|
@@ -120618,16 +120796,16 @@ function ensureNodeEnv(opts) {
|
|
|
120618
120796
|
try {
|
|
120619
120797
|
if (installer === "bun") {
|
|
120620
120798
|
const args = copiedLockfile ? ["install", "--frozen-lockfile"] : ["install"];
|
|
120621
|
-
|
|
120799
|
+
execFileSync32("bun", args, { cwd: envDir, stdio: "pipe" });
|
|
120622
120800
|
} else {
|
|
120623
120801
|
const args = copiedLockfile ? ["ci"] : ["install"];
|
|
120624
|
-
|
|
120802
|
+
execFileSync32("npm", args, { cwd: envDir, stdio: "pipe" });
|
|
120625
120803
|
}
|
|
120626
120804
|
} catch (err) {
|
|
120627
120805
|
const e = err;
|
|
120628
120806
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
120629
120807
|
}
|
|
120630
|
-
|
|
120808
|
+
writeFileSync42(stampPath, targetHash + `
|
|
120631
120809
|
`);
|
|
120632
120810
|
return {
|
|
120633
120811
|
skillName,
|
|
@@ -120646,22 +120824,22 @@ function registerDepsCommand(program3) {
|
|
|
120646
120824
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
120647
120825
|
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) => {
|
|
120648
120826
|
const skillsRoot = builtinSkillsRoot();
|
|
120649
|
-
if (!
|
|
120827
|
+
if (!existsSync102(skillsRoot)) {
|
|
120650
120828
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
120651
120829
|
process.exit(1);
|
|
120652
120830
|
}
|
|
120653
120831
|
const skillDir = join106(skillsRoot, skill);
|
|
120654
|
-
if (!
|
|
120832
|
+
if (!existsSync102(skillDir)) {
|
|
120655
120833
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
120656
120834
|
process.exit(1);
|
|
120657
120835
|
}
|
|
120658
120836
|
const requirementsPath = join106(skillDir, "requirements.txt");
|
|
120659
120837
|
const packageJsonPath = join106(skillDir, "package.json");
|
|
120660
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
120661
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
120838
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync102(requirementsPath));
|
|
120839
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync102(packageJsonPath));
|
|
120662
120840
|
let did = 0;
|
|
120663
120841
|
if (wantPython) {
|
|
120664
|
-
if (!
|
|
120842
|
+
if (!existsSync102(requirementsPath)) {
|
|
120665
120843
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
120666
120844
|
process.exit(1);
|
|
120667
120845
|
}
|
|
@@ -120685,7 +120863,7 @@ function registerDepsCommand(program3) {
|
|
|
120685
120863
|
}
|
|
120686
120864
|
}
|
|
120687
120865
|
if (wantNode) {
|
|
120688
|
-
if (!
|
|
120866
|
+
if (!existsSync102(packageJsonPath)) {
|
|
120689
120867
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
120690
120868
|
process.exit(1);
|
|
120691
120869
|
}
|
|
@@ -120718,7 +120896,7 @@ function registerDepsCommand(program3) {
|
|
|
120718
120896
|
// src/cli/workspace.ts
|
|
120719
120897
|
init_helpers();
|
|
120720
120898
|
init_loader();
|
|
120721
|
-
import { existsSync as
|
|
120899
|
+
import { existsSync as existsSync103 } from "node:fs";
|
|
120722
120900
|
import { resolve as resolve55, sep as sep7 } from "node:path";
|
|
120723
120901
|
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
120724
120902
|
|
|
@@ -121495,7 +121673,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
121495
121673
|
if (!dir)
|
|
121496
121674
|
return;
|
|
121497
121675
|
const gitDir = resolve55(dir, ".git");
|
|
121498
|
-
if (!
|
|
121676
|
+
if (!existsSync103(gitDir)) {
|
|
121499
121677
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
121500
121678
|
`);
|
|
121501
121679
|
return;
|
|
@@ -121549,7 +121727,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
121549
121727
|
if (!dir)
|
|
121550
121728
|
return;
|
|
121551
121729
|
const gitDir = resolve55(dir, ".git");
|
|
121552
|
-
if (!
|
|
121730
|
+
if (!existsSync103(gitDir)) {
|
|
121553
121731
|
process.stdout.write(`Workspace is not a git repository.
|
|
121554
121732
|
`);
|
|
121555
121733
|
return;
|
|
@@ -121574,7 +121752,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
121574
121752
|
const agentsDir = resolveAgentsDir(config);
|
|
121575
121753
|
const agentDir = resolve55(agentsDir, agentName);
|
|
121576
121754
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
121577
|
-
if (!
|
|
121755
|
+
if (!existsSync103(dir)) {
|
|
121578
121756
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
121579
121757
|
`);
|
|
121580
121758
|
return;
|
|
@@ -121610,7 +121788,7 @@ function safeParseInt(value, fallback) {
|
|
|
121610
121788
|
init_helpers();
|
|
121611
121789
|
init_loader();
|
|
121612
121790
|
init_merge();
|
|
121613
|
-
import { copyFileSync as copyFileSync14, existsSync as
|
|
121791
|
+
import { copyFileSync as copyFileSync14, existsSync as existsSync104, readFileSync as readFileSync93, writeFileSync as writeFileSync43 } from "node:fs";
|
|
121614
121792
|
import { join as join107, resolve as resolve56 } from "node:path";
|
|
121615
121793
|
init_scaffold();
|
|
121616
121794
|
init_profiles();
|
|
@@ -121628,7 +121806,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
121628
121806
|
const agentsDir = resolveAgentsDir(config);
|
|
121629
121807
|
const agentDir = resolve56(agentsDir, agentName);
|
|
121630
121808
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
121631
|
-
if (!
|
|
121809
|
+
if (!existsSync104(workspaceDir)) {
|
|
121632
121810
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
121633
121811
|
process.exit(1);
|
|
121634
121812
|
}
|
|
@@ -121654,11 +121832,11 @@ function registerSoulCommand(program3) {
|
|
|
121654
121832
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
121655
121833
|
if (!t)
|
|
121656
121834
|
return;
|
|
121657
|
-
if (!
|
|
121835
|
+
if (!existsSync104(t.soulPath)) {
|
|
121658
121836
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
121659
121837
|
process.exit(1);
|
|
121660
121838
|
}
|
|
121661
|
-
process.stdout.write(
|
|
121839
|
+
process.stdout.write(readFileSync93(t.soulPath, "utf-8"));
|
|
121662
121840
|
}));
|
|
121663
121841
|
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) => {
|
|
121664
121842
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -121669,7 +121847,7 @@ function registerSoulCommand(program3) {
|
|
|
121669
121847
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
121670
121848
|
process.exit(1);
|
|
121671
121849
|
}
|
|
121672
|
-
const exists =
|
|
121850
|
+
const exists = existsSync104(t.soulPath);
|
|
121673
121851
|
if (exists && !opts.yes) {
|
|
121674
121852
|
if (!isInteractive()) {
|
|
121675
121853
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -121684,12 +121862,12 @@ function registerSoulCommand(program3) {
|
|
|
121684
121862
|
let backupPath;
|
|
121685
121863
|
if (exists) {
|
|
121686
121864
|
backupPath = `${t.soulPath}.bak`;
|
|
121687
|
-
if (
|
|
121865
|
+
if (existsSync104(backupPath)) {
|
|
121688
121866
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
121689
121867
|
}
|
|
121690
121868
|
copyFileSync14(t.soulPath, backupPath);
|
|
121691
121869
|
}
|
|
121692
|
-
|
|
121870
|
+
writeFileSync43(t.soulPath, content, "utf-8");
|
|
121693
121871
|
if (backupPath) {
|
|
121694
121872
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
121695
121873
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -121703,7 +121881,7 @@ function registerSoulCommand(program3) {
|
|
|
121703
121881
|
// src/cli/debug.ts
|
|
121704
121882
|
init_helpers();
|
|
121705
121883
|
init_loader();
|
|
121706
|
-
import { existsSync as
|
|
121884
|
+
import { existsSync as existsSync105, readFileSync as readFileSync94, readdirSync as readdirSync36, statSync as statSync62 } from "node:fs";
|
|
121707
121885
|
import { resolve as resolve57, join as join108 } from "node:path";
|
|
121708
121886
|
import { createHash as createHash20 } from "node:crypto";
|
|
121709
121887
|
init_merge();
|
|
@@ -121716,10 +121894,10 @@ function estimateTokens(bytes) {
|
|
|
121716
121894
|
}
|
|
121717
121895
|
function readMcpServerNames(agentDir) {
|
|
121718
121896
|
const mcpPath = join108(agentDir, ".mcp.json");
|
|
121719
|
-
if (!
|
|
121897
|
+
if (!existsSync105(mcpPath))
|
|
121720
121898
|
return [];
|
|
121721
121899
|
try {
|
|
121722
|
-
const parsed = JSON.parse(
|
|
121900
|
+
const parsed = JSON.parse(readFileSync94(mcpPath, "utf-8"));
|
|
121723
121901
|
return Object.keys(parsed.mcpServers ?? {});
|
|
121724
121902
|
} catch {
|
|
121725
121903
|
return null;
|
|
@@ -121730,7 +121908,7 @@ function sha2562(content) {
|
|
|
121730
121908
|
}
|
|
121731
121909
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
121732
121910
|
const projectsDir = join108(claudeConfigDir, "projects");
|
|
121733
|
-
if (!
|
|
121911
|
+
if (!existsSync105(projectsDir))
|
|
121734
121912
|
return;
|
|
121735
121913
|
try {
|
|
121736
121914
|
const entries = readdirSync36(projectsDir, { withFileTypes: true });
|
|
@@ -121740,9 +121918,9 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
121740
121918
|
continue;
|
|
121741
121919
|
const projectPath = join108(projectsDir, entry.name);
|
|
121742
121920
|
const transcriptPath = join108(projectPath, "transcript.jsonl");
|
|
121743
|
-
if (!
|
|
121921
|
+
if (!existsSync105(transcriptPath))
|
|
121744
121922
|
continue;
|
|
121745
|
-
const stat3 =
|
|
121923
|
+
const stat3 = statSync62(transcriptPath);
|
|
121746
121924
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
121747
121925
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
121748
121926
|
}
|
|
@@ -121754,7 +121932,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
121754
121932
|
}
|
|
121755
121933
|
function extractLatestUserMessage(transcriptPath) {
|
|
121756
121934
|
try {
|
|
121757
|
-
const content =
|
|
121935
|
+
const content = readFileSync94(transcriptPath, "utf-8");
|
|
121758
121936
|
const lines = content.trim().split(`
|
|
121759
121937
|
`).filter(Boolean);
|
|
121760
121938
|
for (let i2 = lines.length - 1;i2 >= 0; i2--) {
|
|
@@ -121803,7 +121981,7 @@ function registerDebugCommand(program3) {
|
|
|
121803
121981
|
}
|
|
121804
121982
|
const agentsDir = resolveAgentsDir(config);
|
|
121805
121983
|
const agentDir = resolve57(agentsDir, agentName);
|
|
121806
|
-
if (!
|
|
121984
|
+
if (!existsSync105(agentDir)) {
|
|
121807
121985
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
121808
121986
|
process.exit(1);
|
|
121809
121987
|
}
|
|
@@ -121858,7 +122036,7 @@ function registerDebugCommand(program3) {
|
|
|
121858
122036
|
}
|
|
121859
122037
|
console.log(`=== Append System Prompt (per-session) ===
|
|
121860
122038
|
`);
|
|
121861
|
-
const handoffContent =
|
|
122039
|
+
const handoffContent = existsSync105(handoffPath) ? readFileSync94(handoffPath, "utf-8") : "";
|
|
121862
122040
|
if (handoffContent.trim().length > 0) {
|
|
121863
122041
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
121864
122042
|
console.log(handoffContent);
|
|
@@ -121869,7 +122047,7 @@ function registerDebugCommand(program3) {
|
|
|
121869
122047
|
}
|
|
121870
122048
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
121871
122049
|
`);
|
|
121872
|
-
const claudeMdContent =
|
|
122050
|
+
const claudeMdContent = existsSync105(claudeMdPath) ? readFileSync94(claudeMdPath, "utf-8") : "";
|
|
121873
122051
|
if (claudeMdContent.trim().length > 0) {
|
|
121874
122052
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
121875
122053
|
console.log(claudeMdContent);
|
|
@@ -121880,7 +122058,7 @@ function registerDebugCommand(program3) {
|
|
|
121880
122058
|
}
|
|
121881
122059
|
console.log(`=== Persona (SOUL.md) ===
|
|
121882
122060
|
`);
|
|
121883
|
-
const soulMdContent =
|
|
122061
|
+
const soulMdContent = existsSync105(soulMdPath) ? readFileSync94(soulMdPath, "utf-8") : existsSync105(workspaceSoulMdPath) ? readFileSync94(workspaceSoulMdPath, "utf-8") : "";
|
|
121884
122062
|
if (soulMdContent.trim().length > 0) {
|
|
121885
122063
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
121886
122064
|
console.log(soulMdContent);
|
|
@@ -121944,8 +122122,8 @@ function registerDebugCommand(program3) {
|
|
|
121944
122122
|
const fleetDir = join108(agentsDir, "..", "fleet");
|
|
121945
122123
|
const fleetInvPath = join108(fleetDir, "switchroom-invariants.md");
|
|
121946
122124
|
const fleetClaudePath = join108(fleetDir, "CLAUDE.md");
|
|
121947
|
-
const fleetInvBytes =
|
|
121948
|
-
const fleetClaudeBytes =
|
|
122125
|
+
const fleetInvBytes = existsSync105(fleetInvPath) ? readFileSync94(fleetInvPath, "utf-8").length : 0;
|
|
122126
|
+
const fleetClaudeBytes = existsSync105(fleetClaudePath) ? readFileSync94(fleetClaudePath, "utf-8").length : 0;
|
|
121949
122127
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
121950
122128
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
121951
122129
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -121981,7 +122159,7 @@ import { appendFileSync as appendFileSync6 } from "node:fs";
|
|
|
121981
122159
|
// src/worktree/release.ts
|
|
121982
122160
|
init_registry();
|
|
121983
122161
|
init_remove_checkout();
|
|
121984
|
-
import { existsSync as
|
|
122162
|
+
import { existsSync as existsSync106 } from "node:fs";
|
|
121985
122163
|
function releaseWorktree(input) {
|
|
121986
122164
|
const { id } = input;
|
|
121987
122165
|
const record2 = readRecord(id);
|
|
@@ -121989,7 +122167,7 @@ function releaseWorktree(input) {
|
|
|
121989
122167
|
return { released: true };
|
|
121990
122168
|
}
|
|
121991
122169
|
let gitSuccess = true;
|
|
121992
|
-
if (
|
|
122170
|
+
if (existsSync106(record2.path)) {
|
|
121993
122171
|
try {
|
|
121994
122172
|
removeCheckout(record2.repo, record2.path);
|
|
121995
122173
|
} catch {
|
|
@@ -122025,11 +122203,11 @@ function listWorktrees() {
|
|
|
122025
122203
|
}
|
|
122026
122204
|
|
|
122027
122205
|
// src/worktree/reaper.ts
|
|
122028
|
-
import { execFileSync as
|
|
122029
|
-
import { existsSync as
|
|
122206
|
+
import { execFileSync as execFileSync33 } from "node:child_process";
|
|
122207
|
+
import { existsSync as existsSync107 } from "node:fs";
|
|
122030
122208
|
|
|
122031
122209
|
// src/worktree/proc-liveness.ts
|
|
122032
|
-
import { readdirSync as readdirSync37, readlinkSync as readlinkSync8, realpathSync as realpathSync9, statSync as
|
|
122210
|
+
import { readdirSync as readdirSync37, readlinkSync as readlinkSync8, realpathSync as realpathSync9, statSync as statSync63, readFileSync as readFileSync95 } from "node:fs";
|
|
122033
122211
|
import { join as join109, sep as sep8 } from "node:path";
|
|
122034
122212
|
function isPidDir(name) {
|
|
122035
122213
|
return /^\d+$/.test(name);
|
|
@@ -122045,7 +122223,7 @@ function errno(e) {
|
|
|
122045
122223
|
}
|
|
122046
122224
|
function isKernelThread(procRoot, pid) {
|
|
122047
122225
|
try {
|
|
122048
|
-
return
|
|
122226
|
+
return readFileSync95(join109(procRoot, pid, "cmdline")).length === 0;
|
|
122049
122227
|
} catch {
|
|
122050
122228
|
return false;
|
|
122051
122229
|
}
|
|
@@ -122057,7 +122235,7 @@ function cwdIsUnderRootByInode(linkPath, rootId, maxDepth) {
|
|
|
122057
122235
|
for (let hop = 0;hop <= maxDepth; hop++) {
|
|
122058
122236
|
let st;
|
|
122059
122237
|
try {
|
|
122060
|
-
st =
|
|
122238
|
+
st = statSync63(linkPath + suffix);
|
|
122061
122239
|
} catch {
|
|
122062
122240
|
return false;
|
|
122063
122241
|
}
|
|
@@ -122078,7 +122256,7 @@ function scanProcForHolders(path10, opts = {}) {
|
|
|
122078
122256
|
let rootId;
|
|
122079
122257
|
try {
|
|
122080
122258
|
root = realpathSync9(path10);
|
|
122081
|
-
const st =
|
|
122259
|
+
const st = statSync63(root);
|
|
122082
122260
|
rootId = { dev: st.dev, ino: st.ino };
|
|
122083
122261
|
} catch {
|
|
122084
122262
|
return { state: "unavailable", inaccessible: 0 };
|
|
@@ -122163,11 +122341,11 @@ function reapSkipReasonText(action) {
|
|
|
122163
122341
|
function probePathInUseWithTools(path10) {
|
|
122164
122342
|
let probeRan = false;
|
|
122165
122343
|
try {
|
|
122166
|
-
|
|
122344
|
+
execFileSync33("fuser", [path10], { stdio: "pipe" });
|
|
122167
122345
|
return "in-use";
|
|
122168
122346
|
} catch {}
|
|
122169
122347
|
try {
|
|
122170
|
-
const out =
|
|
122348
|
+
const out = execFileSync33("lsof", ["-t", "+D", path10], {
|
|
122171
122349
|
stdio: ["ignore", "pipe", "ignore"]
|
|
122172
122350
|
}).toString().trim();
|
|
122173
122351
|
probeRan = true;
|
|
@@ -122197,7 +122375,7 @@ function probePathInUse(path10, deps = {}) {
|
|
|
122197
122375
|
}
|
|
122198
122376
|
function hasUncommittedChanges(_repoPath, worktreePath) {
|
|
122199
122377
|
try {
|
|
122200
|
-
const out =
|
|
122378
|
+
const out = execFileSync33("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" }).toString();
|
|
122201
122379
|
return out.trim().length > 0;
|
|
122202
122380
|
} catch {
|
|
122203
122381
|
return true;
|
|
@@ -122215,7 +122393,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
122215
122393
|
const plan = [];
|
|
122216
122394
|
for (const record2 of listRecords()) {
|
|
122217
122395
|
const heartbeatAge = now2 - new Date(record2.heartbeatAt).getTime();
|
|
122218
|
-
const worktreeExists =
|
|
122396
|
+
const worktreeExists = existsSync107(record2.path);
|
|
122219
122397
|
if (!worktreeExists) {
|
|
122220
122398
|
plan.push({
|
|
122221
122399
|
record: record2,
|
|
@@ -122295,15 +122473,15 @@ function runReaper(nowMs, deps = {}) {
|
|
|
122295
122473
|
|
|
122296
122474
|
// src/worktree/gc.ts
|
|
122297
122475
|
init_registry();
|
|
122298
|
-
import { execFileSync as
|
|
122476
|
+
import { execFileSync as execFileSync34 } from "node:child_process";
|
|
122299
122477
|
import { createHash as createHash21 } from "node:crypto";
|
|
122300
122478
|
import {
|
|
122301
|
-
existsSync as
|
|
122302
|
-
readFileSync as
|
|
122479
|
+
existsSync as existsSync108,
|
|
122480
|
+
readFileSync as readFileSync96,
|
|
122303
122481
|
readdirSync as readdirSync38,
|
|
122304
|
-
statSync as
|
|
122305
|
-
renameSync as
|
|
122306
|
-
mkdirSync as
|
|
122482
|
+
statSync as statSync64,
|
|
122483
|
+
renameSync as renameSync28,
|
|
122484
|
+
mkdirSync as mkdirSync60,
|
|
122307
122485
|
rmSync as rmSync21
|
|
122308
122486
|
} from "node:fs";
|
|
122309
122487
|
import { homedir as homedir57 } from "node:os";
|
|
@@ -122453,7 +122631,7 @@ function describeExecFailure(e) {
|
|
|
122453
122631
|
}
|
|
122454
122632
|
var execCapture = (file, args, cwd) => {
|
|
122455
122633
|
try {
|
|
122456
|
-
return
|
|
122634
|
+
return execFileSync34(file, args, {
|
|
122457
122635
|
cwd,
|
|
122458
122636
|
encoding: "utf8",
|
|
122459
122637
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -122509,7 +122687,7 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
|
122509
122687
|
let newest = 0;
|
|
122510
122688
|
for (const f of files) {
|
|
122511
122689
|
try {
|
|
122512
|
-
const m =
|
|
122690
|
+
const m = statSync64(join110(dir, f)).mtimeMs;
|
|
122513
122691
|
if (m > newest)
|
|
122514
122692
|
newest = m;
|
|
122515
122693
|
} catch {}
|
|
@@ -122531,10 +122709,10 @@ function trashRoot() {
|
|
|
122531
122709
|
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join110(homedir57(), ".switchroom", "worktree-gc-trash"));
|
|
122532
122710
|
}
|
|
122533
122711
|
function planGc(roots, deps = {}, taskTreeRoots = [], taskTreeDirs = []) {
|
|
122534
|
-
const exists = deps.existsSync ??
|
|
122712
|
+
const exists = deps.existsSync ?? existsSync108;
|
|
122535
122713
|
const readDir = deps.readDir ?? ((p) => readdirSync38(p));
|
|
122536
|
-
const readFile4 = deps.readFile ?? ((p) =>
|
|
122537
|
-
const stat3 = deps.stat ?? ((p) =>
|
|
122714
|
+
const readFile4 = deps.readFile ?? ((p) => readFileSync96(p, "utf8"));
|
|
122715
|
+
const stat3 = deps.stat ?? ((p) => statSync64(p));
|
|
122538
122716
|
const exec = deps.exec ?? execCapture;
|
|
122539
122717
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
122540
122718
|
const stamp = deps.dateStamp ?? "undated";
|
|
@@ -122830,10 +123008,10 @@ function planGc(roots, deps = {}, taskTreeRoots = [], taskTreeDirs = []) {
|
|
|
122830
123008
|
}
|
|
122831
123009
|
function applyGc(plan, deps = {}) {
|
|
122832
123010
|
const exec = deps.exec ?? execCapture;
|
|
122833
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
123011
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync60(p, { recursive: true }));
|
|
122834
123012
|
const move = deps.move ?? ((src, dest) => {
|
|
122835
123013
|
try {
|
|
122836
|
-
|
|
123014
|
+
renameSync28(src, dest);
|
|
122837
123015
|
} catch {
|
|
122838
123016
|
exec("mv", [src, dest]);
|
|
122839
123017
|
}
|
|
@@ -122893,7 +123071,7 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
122893
123071
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
122894
123072
|
}
|
|
122895
123073
|
function listTrashEntries(nowMs, deps = {}) {
|
|
122896
|
-
const exists = deps.existsSync ??
|
|
123074
|
+
const exists = deps.existsSync ?? existsSync108;
|
|
122897
123075
|
const readDir = deps.readDir ?? ((p) => readdirSync38(p));
|
|
122898
123076
|
const root = trashRoot();
|
|
122899
123077
|
if (!exists(root))
|
|
@@ -122911,7 +123089,7 @@ function listTrashEntries(nowMs, deps = {}) {
|
|
|
122911
123089
|
const p = join110(stampDir, name);
|
|
122912
123090
|
let mtimeMs = nowMs;
|
|
122913
123091
|
try {
|
|
122914
|
-
mtimeMs =
|
|
123092
|
+
mtimeMs = statSync64(p).mtimeMs;
|
|
122915
123093
|
} catch {}
|
|
122916
123094
|
out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
|
|
122917
123095
|
}
|
|
@@ -122946,7 +123124,7 @@ function defaultTaskTreeRoots() {
|
|
|
122946
123124
|
for (const name of names.sort()) {
|
|
122947
123125
|
for (const sub of ["home/work", "home/workspace"]) {
|
|
122948
123126
|
const r = join110(agentsDir, name, sub);
|
|
122949
|
-
if (
|
|
123127
|
+
if (existsSync108(r))
|
|
122950
123128
|
roots.push(r);
|
|
122951
123129
|
}
|
|
122952
123130
|
}
|
|
@@ -122994,7 +123172,7 @@ function reservedAgentPath(relParts) {
|
|
|
122994
123172
|
}
|
|
122995
123173
|
function discoverAgentCheckouts(agentsDir, deps = {}) {
|
|
122996
123174
|
const readDir = deps.readDir ?? ((p) => readdirSync38(p, { withFileTypes: true }));
|
|
122997
|
-
const exists = deps.existsSync ??
|
|
123175
|
+
const exists = deps.existsSync ?? existsSync108;
|
|
122998
123176
|
const maxDepth = deps.maxDepth ?? 4;
|
|
122999
123177
|
const maxVisits = deps.maxVisits ?? 50000;
|
|
123000
123178
|
let agents;
|
|
@@ -123046,7 +123224,7 @@ function defaultTaskTreeDirs(deps = {}) {
|
|
|
123046
123224
|
}
|
|
123047
123225
|
|
|
123048
123226
|
// src/worktree/reap-report.ts
|
|
123049
|
-
import { execFileSync as
|
|
123227
|
+
import { execFileSync as execFileSync35 } from "node:child_process";
|
|
123050
123228
|
import { sep as sep9 } from "node:path";
|
|
123051
123229
|
var DEFAULT_AGENT_TREE_BUDGET_BYTES = 5 * 1024 * 1024 * 1024;
|
|
123052
123230
|
var UNATTRIBUTED_AGENT = "(unattributed)";
|
|
@@ -123068,7 +123246,7 @@ function agentFromPath(path10) {
|
|
|
123068
123246
|
return name && name !== "" ? name : null;
|
|
123069
123247
|
}
|
|
123070
123248
|
function defaultDirSizeBytes(path10) {
|
|
123071
|
-
const out =
|
|
123249
|
+
const out = execFileSync35("du", ["-sk", path10], {
|
|
123072
123250
|
stdio: ["ignore", "pipe", "pipe"],
|
|
123073
123251
|
maxBuffer: 8 * 1024 * 1024
|
|
123074
123252
|
}).toString();
|
|
@@ -123258,17 +123436,17 @@ function formatReapReport(report) {
|
|
|
123258
123436
|
}
|
|
123259
123437
|
|
|
123260
123438
|
// src/worktree/capture.ts
|
|
123261
|
-
import { execFileSync as
|
|
123439
|
+
import { execFileSync as execFileSync36 } from "node:child_process";
|
|
123262
123440
|
import {
|
|
123263
|
-
existsSync as
|
|
123264
|
-
mkdirSync as
|
|
123441
|
+
existsSync as existsSync109,
|
|
123442
|
+
mkdirSync as mkdirSync61,
|
|
123265
123443
|
mkdtempSync as mkdtempSync5,
|
|
123266
123444
|
rmSync as rmSync22,
|
|
123267
|
-
statSync as
|
|
123268
|
-
writeFileSync as
|
|
123445
|
+
statSync as statSync65,
|
|
123446
|
+
writeFileSync as writeFileSync44
|
|
123269
123447
|
} from "node:fs";
|
|
123270
123448
|
import { homedir as homedir58 } from "node:os";
|
|
123271
|
-
import { basename as basename12, dirname as
|
|
123449
|
+
import { basename as basename12, dirname as dirname48, join as join111, resolve as resolve59 } from "node:path";
|
|
123272
123450
|
init_remove_checkout();
|
|
123273
123451
|
init_scratch();
|
|
123274
123452
|
init_loader();
|
|
@@ -123354,7 +123532,7 @@ function rescueRootIsFallback(destRoot) {
|
|
|
123354
123532
|
}
|
|
123355
123533
|
function run(args, input, cwd, env2) {
|
|
123356
123534
|
try {
|
|
123357
|
-
const stdout =
|
|
123535
|
+
const stdout = execFileSync36("git", args, {
|
|
123358
123536
|
encoding: "utf-8",
|
|
123359
123537
|
stdio: input === undefined ? ["ignore", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
|
|
123360
123538
|
maxBuffer: MAX_BUFFER,
|
|
@@ -123421,7 +123599,7 @@ function captureCheckout(opts) {
|
|
|
123421
123599
|
};
|
|
123422
123600
|
let st;
|
|
123423
123601
|
try {
|
|
123424
|
-
st =
|
|
123602
|
+
st = statSync65(source);
|
|
123425
123603
|
} catch {
|
|
123426
123604
|
return { ...base, failure: "missing", error: `No such directory: ${source}` };
|
|
123427
123605
|
}
|
|
@@ -123435,7 +123613,7 @@ function captureCheckout(opts) {
|
|
|
123435
123613
|
failure: "not-a-git-repo",
|
|
123436
123614
|
error: `${source} is not a usable git repository (${commonDirProbe.stderr}).
|
|
123437
123615
|
` + `A bundle cannot represent it. Capture it as a tarball instead:
|
|
123438
|
-
` + ` tar -C ${
|
|
123616
|
+
` + ` tar -C ${dirname48(source)} -czf <dest>/${basename12(source)}.tar.gz ${basename12(source)}
|
|
123439
123617
|
` + "then verify with `tar -tzf <file> >/dev/null` before deleting anything."
|
|
123440
123618
|
};
|
|
123441
123619
|
}
|
|
@@ -123444,7 +123622,7 @@ function captureCheckout(opts) {
|
|
|
123444
123622
|
const headSha = run(gitArgs(source, "rev-parse", "--verify", "--quiet", "HEAD")).stdout.trim();
|
|
123445
123623
|
const headBranch = run(gitArgs(source, "symbolic-ref", "--quiet", "HEAD")).stdout.trim();
|
|
123446
123624
|
const head = headBranch ? headBranch.replace(/^refs\/heads\//, "") : headSha ? `detached@${headSha.slice(0, 12)}` : null;
|
|
123447
|
-
const destRoot = opts.out ?
|
|
123625
|
+
const destRoot = opts.out ? dirname48(resolve59(opts.out)) : opts.dest ? resolve59(opts.dest) : rescueDestRoot();
|
|
123448
123626
|
const bundlePath = opts.out ? resolve59(opts.out) : join111(destRoot, bundleFileName(source, now2));
|
|
123449
123627
|
if (bundlePath === source || bundlePath.startsWith(source + "/")) {
|
|
123450
123628
|
return {
|
|
@@ -123455,7 +123633,7 @@ function captureCheckout(opts) {
|
|
|
123455
123633
|
};
|
|
123456
123634
|
}
|
|
123457
123635
|
try {
|
|
123458
|
-
|
|
123636
|
+
mkdirSync61(destRoot, { recursive: true });
|
|
123459
123637
|
} catch (e) {
|
|
123460
123638
|
return {
|
|
123461
123639
|
...base,
|
|
@@ -123478,8 +123656,8 @@ function captureCheckout(opts) {
|
|
|
123478
123656
|
if (!init.ok) {
|
|
123479
123657
|
return { ...base, failure: "bundle-failed", error: `staging init failed: ${init.stderr}` };
|
|
123480
123658
|
}
|
|
123481
|
-
|
|
123482
|
-
|
|
123659
|
+
mkdirSync61(join111(stageDir, "objects", "info"), { recursive: true });
|
|
123660
|
+
writeFileSync44(join111(stageDir, "objects", "info", "alternates"), objectsDir + `
|
|
123483
123661
|
`);
|
|
123484
123662
|
const sourceRefs = parseForEachRef(run(gitArgs(source, "for-each-ref", "--format=%(objectname) %(refname)")).stdout);
|
|
123485
123663
|
const sourceRefNames = new Set(sourceRefs.map((r) => r.ref));
|
|
@@ -123502,7 +123680,7 @@ function captureCheckout(opts) {
|
|
|
123502
123680
|
const nStash = createRefs(stageDir, stashRefs, warnings);
|
|
123503
123681
|
let wip = false;
|
|
123504
123682
|
let wipCommit = null;
|
|
123505
|
-
if (!bare &&
|
|
123683
|
+
if (!bare && existsSync109(source)) {
|
|
123506
123684
|
const add2 = run(stageArgs(stageDir, "--work-tree", source, "add", "-A"), undefined, source);
|
|
123507
123685
|
if (!add2.ok) {
|
|
123508
123686
|
return {
|
|
@@ -123537,7 +123715,7 @@ function captureCheckout(opts) {
|
|
|
123537
123715
|
} else {
|
|
123538
123716
|
warnings.push("could not write a tree for the working-tree snapshot");
|
|
123539
123717
|
}
|
|
123540
|
-
if (
|
|
123718
|
+
if (existsSync109(join111(source, ".gitmodules"))) {
|
|
123541
123719
|
warnings.push("this checkout has submodules: they are captured as gitlinks only, " + "so submodule CONTENT is not in this bundle \u2014 capture each submodule " + "separately if it holds unpushed work");
|
|
123542
123720
|
}
|
|
123543
123721
|
}
|
|
@@ -123569,7 +123747,7 @@ function captureCheckout(opts) {
|
|
|
123569
123747
|
};
|
|
123570
123748
|
}
|
|
123571
123749
|
const created = run(stageArgs(stageDir, "bundle", "create", bundlePath, "--all"));
|
|
123572
|
-
if (!created.ok || !
|
|
123750
|
+
if (!created.ok || !existsSync109(bundlePath)) {
|
|
123573
123751
|
return {
|
|
123574
123752
|
...base,
|
|
123575
123753
|
head,
|
|
@@ -123579,7 +123757,7 @@ function captureCheckout(opts) {
|
|
|
123579
123757
|
};
|
|
123580
123758
|
}
|
|
123581
123759
|
opts.onBundleWritten?.(bundlePath);
|
|
123582
|
-
const bytes =
|
|
123760
|
+
const bytes = statSync65(bundlePath).size;
|
|
123583
123761
|
if (bytes === 0) {
|
|
123584
123762
|
return {
|
|
123585
123763
|
...base,
|
|
@@ -123658,7 +123836,7 @@ function captureCheckout(opts) {
|
|
|
123658
123836
|
};
|
|
123659
123837
|
const writeManifest = (r) => {
|
|
123660
123838
|
try {
|
|
123661
|
-
|
|
123839
|
+
writeFileSync44(`${bundlePath}.json`, JSON.stringify(r, null, 2) + `
|
|
123662
123840
|
`);
|
|
123663
123841
|
} catch (e) {
|
|
123664
123842
|
warnings.push(`could not write the manifest sidecar: ${e.message}`);
|
|
@@ -123676,7 +123854,7 @@ function captureCheckout(opts) {
|
|
|
123676
123854
|
return failed;
|
|
123677
123855
|
}
|
|
123678
123856
|
if (kind === "worktree") {
|
|
123679
|
-
const repoRoot =
|
|
123857
|
+
const repoRoot = dirname48(resolve59(source, commonDirProbe.stdout.trim()));
|
|
123680
123858
|
const pruned = run(gitArgs(repoRoot, "worktree", "prune"));
|
|
123681
123859
|
if (!pruned.ok)
|
|
123682
123860
|
warnings.push(`git worktree prune failed in ${repoRoot}: ${pruned.stderr}`);
|
|
@@ -124042,10 +124220,10 @@ init_drive();
|
|
|
124042
124220
|
init_scaffold_integration();
|
|
124043
124221
|
import {
|
|
124044
124222
|
chmodSync as chmodSync17,
|
|
124045
|
-
mkdirSync as
|
|
124223
|
+
mkdirSync as mkdirSync62,
|
|
124046
124224
|
readdirSync as readdirSync39,
|
|
124047
124225
|
rmSync as rmSync23,
|
|
124048
|
-
writeFileSync as
|
|
124226
|
+
writeFileSync as writeFileSync45
|
|
124049
124227
|
} from "node:fs";
|
|
124050
124228
|
import { join as join112 } from "node:path";
|
|
124051
124229
|
function encodeCredentialsFilename(email) {
|
|
@@ -124314,14 +124492,14 @@ function resolveCredentialsDir(env2) {
|
|
|
124314
124492
|
return join112(stateBase, "google-workspace-mcp", "credentials");
|
|
124315
124493
|
}
|
|
124316
124494
|
function writeSeedFile(dir, email, seed) {
|
|
124317
|
-
|
|
124495
|
+
mkdirSync62(dir, { recursive: true, mode: 448 });
|
|
124318
124496
|
chmodSync17(dir, 448);
|
|
124319
124497
|
for (const name of readdirSync39(dir)) {
|
|
124320
124498
|
rmSync23(join112(dir, name), { force: true, recursive: true });
|
|
124321
124499
|
}
|
|
124322
124500
|
const filename = encodeCredentialsFilename(email);
|
|
124323
124501
|
const filePath = join112(dir, filename);
|
|
124324
|
-
|
|
124502
|
+
writeFileSync45(filePath, JSON.stringify(seed), { mode: 384 });
|
|
124325
124503
|
chmodSync17(filePath, 384);
|
|
124326
124504
|
return filePath;
|
|
124327
124505
|
}
|
|
@@ -124516,8 +124694,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
124516
124694
|
// src/cli/m365-mcp-launcher.ts
|
|
124517
124695
|
init_scaffold_integration();
|
|
124518
124696
|
import { spawn as spawn6 } from "node:child_process";
|
|
124519
|
-
import { writeFileSync as
|
|
124520
|
-
import { dirname as
|
|
124697
|
+
import { writeFileSync as writeFileSync46, mkdirSync as mkdirSync63 } from "node:fs";
|
|
124698
|
+
import { dirname as dirname49, join as join113 } from "node:path";
|
|
124521
124699
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
124522
124700
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
124523
124701
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -124553,8 +124731,8 @@ function computeRefreshDelayMs(expiresAt, now2, leadMs = DEFAULT_REFRESH_LEAD_MS
|
|
|
124553
124731
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
124554
124732
|
const path10 = heartbeatPath(agentName, account);
|
|
124555
124733
|
try {
|
|
124556
|
-
|
|
124557
|
-
|
|
124734
|
+
mkdirSync63(dirname49(path10), { recursive: true });
|
|
124735
|
+
writeFileSync46(path10, JSON.stringify(data, null, 2), { mode: 420 });
|
|
124558
124736
|
} catch {}
|
|
124559
124737
|
}
|
|
124560
124738
|
function heartbeatPath(agentName, account) {
|
|
@@ -124794,8 +124972,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
124794
124972
|
// src/cli/notion-mcp-launcher.ts
|
|
124795
124973
|
init_scaffold_integration();
|
|
124796
124974
|
import { spawn as spawn7 } from "node:child_process";
|
|
124797
|
-
import { existsSync as
|
|
124798
|
-
import { dirname as
|
|
124975
|
+
import { existsSync as existsSync110, mkdirSync as mkdirSync64, writeFileSync as writeFileSync47 } from "node:fs";
|
|
124976
|
+
import { dirname as dirname50 } from "node:path";
|
|
124799
124977
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
124800
124978
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
124801
124979
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -124805,10 +124983,10 @@ function buildNotionMcpArgs(opts) {
|
|
|
124805
124983
|
}
|
|
124806
124984
|
function defaultWriteHeartbeat(path10, contents) {
|
|
124807
124985
|
try {
|
|
124808
|
-
const dir =
|
|
124809
|
-
if (!
|
|
124810
|
-
|
|
124811
|
-
|
|
124986
|
+
const dir = dirname50(path10);
|
|
124987
|
+
if (!existsSync110(dir))
|
|
124988
|
+
mkdirSync64(dir, { recursive: true });
|
|
124989
|
+
writeFileSync47(path10, contents);
|
|
124812
124990
|
} catch {}
|
|
124813
124991
|
}
|
|
124814
124992
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -124918,7 +125096,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
124918
125096
|
|
|
124919
125097
|
// src/cli/hindsight-mcp-shim.ts
|
|
124920
125098
|
init_hindsight_directive_admin();
|
|
124921
|
-
import { mkdirSync as
|
|
125099
|
+
import { mkdirSync as mkdirSync65, readFileSync as readFileSync97, renameSync as renameSync29, writeFileSync as writeFileSync48 } from "node:fs";
|
|
124922
125100
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
124923
125101
|
import { join as join114 } from "node:path";
|
|
124924
125102
|
import { createInterface as createInterface7 } from "node:readline";
|
|
@@ -125200,6 +125378,63 @@ function isReflectToolCallFailure(toolName, res) {
|
|
|
125200
125378
|
}
|
|
125201
125379
|
return texts.some((t) => t.includes(REFLECT_NO_TOOL_CALL_SIGNATURE));
|
|
125202
125380
|
}
|
|
125381
|
+
var REFLECT_EMPTY_EVIDENCE_TEXT = "No relevant memories: reflect retrieved ZERO evidence from your memory " + "bank for this query \u2014 no memories, mental models, or directives matched. " + "This is not an error and not a synthesized answer: the bank has nothing " + "recorded on this topic. Treat it as an explicit absence, not a fact, and " + "do not present the (suppressed) synthesized prose as if it were recalled.";
|
|
125382
|
+
function reflectEvidenceCardinality(basedOn) {
|
|
125383
|
+
if (!basedOn || typeof basedOn !== "object" || Array.isArray(basedOn)) {
|
|
125384
|
+
return null;
|
|
125385
|
+
}
|
|
125386
|
+
let total = 0;
|
|
125387
|
+
for (const v of Object.values(basedOn)) {
|
|
125388
|
+
if (!Array.isArray(v))
|
|
125389
|
+
return null;
|
|
125390
|
+
total += v.length;
|
|
125391
|
+
}
|
|
125392
|
+
return total;
|
|
125393
|
+
}
|
|
125394
|
+
function applyReflectCardinalityGuard(result, callerWantedBasedOn) {
|
|
125395
|
+
const abstain = {
|
|
125396
|
+
content: [{ type: "text", text: REFLECT_EMPTY_EVIDENCE_TEXT }],
|
|
125397
|
+
isError: false
|
|
125398
|
+
};
|
|
125399
|
+
if (!result || typeof result !== "object")
|
|
125400
|
+
return result;
|
|
125401
|
+
const res = result;
|
|
125402
|
+
if (res.isError)
|
|
125403
|
+
return result;
|
|
125404
|
+
const content = res.content;
|
|
125405
|
+
if (!Array.isArray(content) || content.length === 0)
|
|
125406
|
+
return result;
|
|
125407
|
+
const first = content[0];
|
|
125408
|
+
if (!first || typeof first.text !== "string")
|
|
125409
|
+
return result;
|
|
125410
|
+
let payload;
|
|
125411
|
+
try {
|
|
125412
|
+
payload = JSON.parse(first.text);
|
|
125413
|
+
} catch {
|
|
125414
|
+
return result;
|
|
125415
|
+
}
|
|
125416
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
125417
|
+
return result;
|
|
125418
|
+
}
|
|
125419
|
+
const obj = payload;
|
|
125420
|
+
if (!("based_on" in obj))
|
|
125421
|
+
return result;
|
|
125422
|
+
const cardinality = reflectEvidenceCardinality(obj.based_on);
|
|
125423
|
+
if (cardinality === null)
|
|
125424
|
+
return result;
|
|
125425
|
+
if (cardinality === 0)
|
|
125426
|
+
return abstain;
|
|
125427
|
+
if (callerWantedBasedOn)
|
|
125428
|
+
return result;
|
|
125429
|
+
delete obj.based_on;
|
|
125430
|
+
return {
|
|
125431
|
+
...res,
|
|
125432
|
+
content: [
|
|
125433
|
+
{ ...first, text: JSON.stringify(obj, null, 2) },
|
|
125434
|
+
...content.slice(1)
|
|
125435
|
+
]
|
|
125436
|
+
};
|
|
125437
|
+
}
|
|
125203
125438
|
function redactToolArguments(value) {
|
|
125204
125439
|
if (typeof value === "string")
|
|
125205
125440
|
return redact(value);
|
|
@@ -125324,6 +125559,9 @@ function guardAndClampToolCall(params, env2) {
|
|
|
125324
125559
|
if (injected.budget === undefined) {
|
|
125325
125560
|
injected.budget = name === "recall" ? resolveClampBudget(env2.HINDSIGHT_SHIM_RECALL_BUDGET, DEFAULT_RECALL_BUDGET) : resolveClampBudget(env2.HINDSIGHT_SHIM_REFLECT_BUDGET, DEFAULT_REFLECT_BUDGET);
|
|
125326
125561
|
}
|
|
125562
|
+
if (name === "reflect" && injected.include_based_on === undefined) {
|
|
125563
|
+
injected.include_based_on = true;
|
|
125564
|
+
}
|
|
125327
125565
|
return { ok: true, params: { ...called, arguments: injected } };
|
|
125328
125566
|
}
|
|
125329
125567
|
return { ok: true, params };
|
|
@@ -125513,18 +125751,18 @@ class HindsightShim {
|
|
|
125513
125751
|
}
|
|
125514
125752
|
writeCache(result) {
|
|
125515
125753
|
try {
|
|
125516
|
-
|
|
125754
|
+
mkdirSync65(this.opts.cacheDir, { recursive: true });
|
|
125517
125755
|
const tmp = join114(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
125518
|
-
|
|
125756
|
+
writeFileSync48(tmp, JSON.stringify(result, null, 2) + `
|
|
125519
125757
|
`);
|
|
125520
|
-
|
|
125758
|
+
renameSync29(tmp, this.cachePath);
|
|
125521
125759
|
} catch (err) {
|
|
125522
125760
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
125523
125761
|
}
|
|
125524
125762
|
}
|
|
125525
125763
|
readCache() {
|
|
125526
125764
|
try {
|
|
125527
|
-
const parsed = JSON.parse(
|
|
125765
|
+
const parsed = JSON.parse(readFileSync97(this.cachePath, "utf-8"));
|
|
125528
125766
|
if (Array.isArray(parsed.tools))
|
|
125529
125767
|
return parsed;
|
|
125530
125768
|
return null;
|
|
@@ -125718,6 +125956,7 @@ class HindsightShim {
|
|
|
125718
125956
|
}
|
|
125719
125957
|
const forwarded = redactToolCallParams(guarded.params);
|
|
125720
125958
|
const toolName = guarded.params?.name;
|
|
125959
|
+
const callerWantedBasedOn = toolName === "reflect" && Boolean(called?.arguments?.include_based_on);
|
|
125721
125960
|
const timeoutMs = this.opts.toolsCallTimeoutMs ?? TOOLS_CALL_TIMEOUT_MS;
|
|
125722
125961
|
try {
|
|
125723
125962
|
let res = await this.upstream.request("tools/call", forwarded, timeoutMs);
|
|
@@ -125734,6 +125973,9 @@ class HindsightShim {
|
|
|
125734
125973
|
isError: true
|
|
125735
125974
|
};
|
|
125736
125975
|
}
|
|
125976
|
+
if (toolName === "reflect") {
|
|
125977
|
+
return applyReflectCardinalityGuard(res.result, callerWantedBasedOn);
|
|
125978
|
+
}
|
|
125737
125979
|
return res.result;
|
|
125738
125980
|
} catch (err) {
|
|
125739
125981
|
const name = params?.name ?? "unknown";
|
|
@@ -125825,7 +126067,7 @@ function registerHindsightMcpShimCommand(program3) {
|
|
|
125825
126067
|
|
|
125826
126068
|
// src/cli/deliver-file.ts
|
|
125827
126069
|
init_client2();
|
|
125828
|
-
import { readFileSync as
|
|
126070
|
+
import { readFileSync as readFileSync98, statSync as statSync66 } from "node:fs";
|
|
125829
126071
|
import { basename as basename15 } from "node:path";
|
|
125830
126072
|
|
|
125831
126073
|
// src/delivery/onedrive.ts
|
|
@@ -126164,8 +126406,8 @@ async function defaultResolveProvider() {
|
|
|
126164
126406
|
}
|
|
126165
126407
|
async function runDeliverFile(localPath, deps = {}) {
|
|
126166
126408
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
126167
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
126168
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
126409
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync66(p).size);
|
|
126410
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync98(p)));
|
|
126169
126411
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
126170
126412
|
let size;
|
|
126171
126413
|
try {
|
|
@@ -126486,7 +126728,7 @@ function runRedactStdin() {
|
|
|
126486
126728
|
}
|
|
126487
126729
|
|
|
126488
126730
|
// src/cli/status-ask.ts
|
|
126489
|
-
import { readFileSync as
|
|
126731
|
+
import { readFileSync as readFileSync99, existsSync as existsSync111, readdirSync as readdirSync40 } from "node:fs";
|
|
126490
126732
|
import { join as join115 } from "node:path";
|
|
126491
126733
|
import { homedir as homedir59 } from "node:os";
|
|
126492
126734
|
|
|
@@ -126762,7 +127004,7 @@ function runReport(opts) {
|
|
|
126762
127004
|
for (const src of sources) {
|
|
126763
127005
|
let content;
|
|
126764
127006
|
try {
|
|
126765
|
-
content =
|
|
127007
|
+
content = readFileSync99(src.path, "utf-8");
|
|
126766
127008
|
} catch (err) {
|
|
126767
127009
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
126768
127010
|
`);
|
|
@@ -126809,7 +127051,7 @@ function runReport(opts) {
|
|
|
126809
127051
|
function resolveSources(explicitPath) {
|
|
126810
127052
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
126811
127053
|
const trimmed = explicitPath.trim();
|
|
126812
|
-
if (!
|
|
127054
|
+
if (!existsSync111(trimmed)) {
|
|
126813
127055
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
126814
127056
|
`);
|
|
126815
127057
|
process.exit(1);
|
|
@@ -126825,7 +127067,7 @@ function resolveSources(explicitPath) {
|
|
|
126825
127067
|
} catch {
|
|
126826
127068
|
agentsDir = join115(homedir59(), ".switchroom", "agents");
|
|
126827
127069
|
}
|
|
126828
|
-
if (!
|
|
127070
|
+
if (!existsSync111(agentsDir))
|
|
126829
127071
|
return [];
|
|
126830
127072
|
const sources = [];
|
|
126831
127073
|
let entries;
|
|
@@ -126836,7 +127078,7 @@ function resolveSources(explicitPath) {
|
|
|
126836
127078
|
}
|
|
126837
127079
|
for (const name of entries) {
|
|
126838
127080
|
const path10 = join115(agentsDir, name, "runtime-metrics.jsonl");
|
|
126839
|
-
if (
|
|
127081
|
+
if (existsSync111(path10)) {
|
|
126840
127082
|
sources.push({ path: path10, agent: name });
|
|
126841
127083
|
}
|
|
126842
127084
|
}
|
|
@@ -126865,16 +127107,16 @@ var import_yaml23 = __toESM(require_dist(), 1);
|
|
|
126865
127107
|
// src/config/overlay-writer.ts
|
|
126866
127108
|
init_paths();
|
|
126867
127109
|
import {
|
|
126868
|
-
chownSync as
|
|
127110
|
+
chownSync as chownSync15,
|
|
126869
127111
|
closeSync as closeSync20,
|
|
126870
|
-
existsSync as
|
|
127112
|
+
existsSync as existsSync112,
|
|
126871
127113
|
fsyncSync as fsyncSync8,
|
|
126872
|
-
mkdirSync as
|
|
127114
|
+
mkdirSync as mkdirSync66,
|
|
126873
127115
|
openSync as openSync20,
|
|
126874
127116
|
readdirSync as readdirSync41,
|
|
126875
|
-
readFileSync as
|
|
126876
|
-
renameSync as
|
|
126877
|
-
statSync as
|
|
127117
|
+
readFileSync as readFileSync100,
|
|
127118
|
+
renameSync as renameSync30,
|
|
127119
|
+
statSync as statSync67,
|
|
126878
127120
|
unlinkSync as unlinkSync22,
|
|
126879
127121
|
writeSync as writeSync10
|
|
126880
127122
|
} from "node:fs";
|
|
@@ -126897,15 +127139,15 @@ function overlayPathsFor(agent, opts = {}) {
|
|
|
126897
127139
|
};
|
|
126898
127140
|
}
|
|
126899
127141
|
function ensureDirs(paths) {
|
|
126900
|
-
|
|
126901
|
-
|
|
127142
|
+
mkdirSync66(paths.scheduleDir, { recursive: true });
|
|
127143
|
+
mkdirSync66(paths.scheduleStagingDir, { recursive: true });
|
|
126902
127144
|
}
|
|
126903
127145
|
function ensureSkillsDirs(paths) {
|
|
126904
|
-
|
|
126905
|
-
|
|
127146
|
+
mkdirSync66(paths.skillsDir, { recursive: true });
|
|
127147
|
+
mkdirSync66(paths.skillsStagingDir, { recursive: true });
|
|
126906
127148
|
}
|
|
126907
127149
|
function withAgentLock(paths, fn) {
|
|
126908
|
-
|
|
127150
|
+
mkdirSync66(paths.agentRoot, { recursive: true });
|
|
126909
127151
|
const start = Date.now();
|
|
126910
127152
|
const TIMEOUT_MS = 5000;
|
|
126911
127153
|
let fd = null;
|
|
@@ -126918,7 +127160,7 @@ function withAgentLock(paths, fn) {
|
|
|
126918
127160
|
if (e.code !== "EEXIST")
|
|
126919
127161
|
throw err;
|
|
126920
127162
|
try {
|
|
126921
|
-
const age = Date.now() -
|
|
127163
|
+
const age = Date.now() - statSync67(paths.lockPath).mtimeMs;
|
|
126922
127164
|
if (age > 30000) {
|
|
126923
127165
|
unlinkSync22(paths.lockPath);
|
|
126924
127166
|
continue;
|
|
@@ -126944,14 +127186,14 @@ function withAgentLock(paths, fn) {
|
|
|
126944
127186
|
}
|
|
126945
127187
|
var overlayWriterRuntime = {
|
|
126946
127188
|
geteuid: () => process.geteuid?.(),
|
|
126947
|
-
chown: (path10, uid, gid) =>
|
|
127189
|
+
chown: (path10, uid, gid) => chownSync15(path10, uid, gid)
|
|
126948
127190
|
};
|
|
126949
127191
|
function alignStagedOwnerToDir(stagingPath, targetDir) {
|
|
126950
127192
|
try {
|
|
126951
127193
|
const euid = overlayWriterRuntime.geteuid();
|
|
126952
127194
|
if (euid === undefined)
|
|
126953
127195
|
return;
|
|
126954
|
-
const dirStat =
|
|
127196
|
+
const dirStat = statSync67(targetDir);
|
|
126955
127197
|
if (dirStat.uid === euid)
|
|
126956
127198
|
return;
|
|
126957
127199
|
overlayWriterRuntime.chown(stagingPath, dirStat.uid, dirStat.gid);
|
|
@@ -126971,7 +127213,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
126971
127213
|
closeSync20(fd);
|
|
126972
127214
|
}
|
|
126973
127215
|
alignStagedOwnerToDir(stagingPath, paths.scheduleDir);
|
|
126974
|
-
|
|
127216
|
+
renameSync30(stagingPath, finalPath);
|
|
126975
127217
|
return finalPath;
|
|
126976
127218
|
});
|
|
126977
127219
|
}
|
|
@@ -126989,7 +127231,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
126989
127231
|
closeSync20(fd);
|
|
126990
127232
|
}
|
|
126991
127233
|
alignStagedOwnerToDir(stagingPath, paths.skillsDir);
|
|
126992
|
-
|
|
127234
|
+
renameSync30(stagingPath, finalPath);
|
|
126993
127235
|
return finalPath;
|
|
126994
127236
|
});
|
|
126995
127237
|
}
|
|
@@ -126997,7 +127239,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
126997
127239
|
const paths = overlayPathsFor(agent, opts);
|
|
126998
127240
|
return withAgentLock(paths, () => {
|
|
126999
127241
|
const finalPath = join116(paths.skillsDir, `${slug}.yaml`);
|
|
127000
|
-
if (!
|
|
127242
|
+
if (!existsSync112(finalPath))
|
|
127001
127243
|
return false;
|
|
127002
127244
|
unlinkSync22(finalPath);
|
|
127003
127245
|
return true;
|
|
@@ -127005,7 +127247,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
127005
127247
|
}
|
|
127006
127248
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
127007
127249
|
const paths = overlayPathsFor(agent, opts);
|
|
127008
|
-
if (!
|
|
127250
|
+
if (!existsSync112(paths.skillsDir))
|
|
127009
127251
|
return [];
|
|
127010
127252
|
const out = [];
|
|
127011
127253
|
for (const name of readdirSync41(paths.skillsDir)) {
|
|
@@ -127013,7 +127255,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
127013
127255
|
continue;
|
|
127014
127256
|
const full = join116(paths.skillsDir, name);
|
|
127015
127257
|
try {
|
|
127016
|
-
const raw =
|
|
127258
|
+
const raw = readFileSync100(full, "utf-8");
|
|
127017
127259
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
127018
127260
|
out.push({ slug, path: full, raw });
|
|
127019
127261
|
} catch {}
|
|
@@ -127024,7 +127266,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
127024
127266
|
const paths = overlayPathsFor(agent, opts);
|
|
127025
127267
|
return withAgentLock(paths, () => {
|
|
127026
127268
|
const finalPath = join116(paths.scheduleDir, `${slug}.yaml`);
|
|
127027
|
-
if (!
|
|
127269
|
+
if (!existsSync112(finalPath))
|
|
127028
127270
|
return false;
|
|
127029
127271
|
unlinkSync22(finalPath);
|
|
127030
127272
|
return true;
|
|
@@ -127032,7 +127274,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
127032
127274
|
}
|
|
127033
127275
|
function listOverlayEntries(agent, opts = {}) {
|
|
127034
127276
|
const paths = overlayPathsFor(agent, opts);
|
|
127035
|
-
if (!
|
|
127277
|
+
if (!existsSync112(paths.scheduleDir))
|
|
127036
127278
|
return [];
|
|
127037
127279
|
const out = [];
|
|
127038
127280
|
for (const name of readdirSync41(paths.scheduleDir)) {
|
|
@@ -127040,7 +127282,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
127040
127282
|
continue;
|
|
127041
127283
|
const full = join116(paths.scheduleDir, name);
|
|
127042
127284
|
try {
|
|
127043
|
-
const raw =
|
|
127285
|
+
const raw = readFileSync100(full, "utf-8");
|
|
127044
127286
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
127045
127287
|
out.push({ slug, path: full, raw });
|
|
127046
127288
|
} catch {}
|
|
@@ -127260,15 +127502,15 @@ function reconcileAgentCronOnly(agent) {
|
|
|
127260
127502
|
// src/cli/agent-config-pending.ts
|
|
127261
127503
|
import {
|
|
127262
127504
|
closeSync as closeSync21,
|
|
127263
|
-
existsSync as
|
|
127505
|
+
existsSync as existsSync113,
|
|
127264
127506
|
fsyncSync as fsyncSync9,
|
|
127265
|
-
mkdirSync as
|
|
127507
|
+
mkdirSync as mkdirSync67,
|
|
127266
127508
|
openSync as openSync21,
|
|
127267
127509
|
readdirSync as readdirSync42,
|
|
127268
|
-
readFileSync as
|
|
127269
|
-
renameSync as
|
|
127510
|
+
readFileSync as readFileSync101,
|
|
127511
|
+
renameSync as renameSync31,
|
|
127270
127512
|
unlinkSync as unlinkSync23,
|
|
127271
|
-
writeFileSync as
|
|
127513
|
+
writeFileSync as writeFileSync49,
|
|
127272
127514
|
writeSync as writeSync11
|
|
127273
127515
|
} from "node:fs";
|
|
127274
127516
|
import { join as join117 } from "node:path";
|
|
@@ -127280,7 +127522,7 @@ function pendingDir(agent, opts = {}) {
|
|
|
127280
127522
|
}
|
|
127281
127523
|
function ensurePendingDir(agent, opts = {}) {
|
|
127282
127524
|
const dir = pendingDir(agent, opts);
|
|
127283
|
-
|
|
127525
|
+
mkdirSync67(dir, { recursive: true });
|
|
127284
127526
|
return dir;
|
|
127285
127527
|
}
|
|
127286
127528
|
function newStageId() {
|
|
@@ -127309,15 +127551,15 @@ function stagePendingScheduleEntry(opts) {
|
|
|
127309
127551
|
} finally {
|
|
127310
127552
|
closeSync21(fd);
|
|
127311
127553
|
}
|
|
127312
|
-
|
|
127554
|
+
renameSync31(yamlTmp, yamlPath);
|
|
127313
127555
|
}
|
|
127314
|
-
|
|
127556
|
+
writeFileSync49(metaPath, JSON.stringify(meta, null, 2) + `
|
|
127315
127557
|
`, { mode: 384 });
|
|
127316
127558
|
return { stageId, yamlPath, metaPath };
|
|
127317
127559
|
}
|
|
127318
127560
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
127319
127561
|
const dir = pendingDir(agent, opts);
|
|
127320
|
-
if (!
|
|
127562
|
+
if (!existsSync113(dir))
|
|
127321
127563
|
return [];
|
|
127322
127564
|
const out = [];
|
|
127323
127565
|
for (const name of readdirSync42(dir).sort()) {
|
|
@@ -127326,10 +127568,10 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
127326
127568
|
const stageId = name.slice(0, -".meta.json".length);
|
|
127327
127569
|
const metaPath = join117(dir, name);
|
|
127328
127570
|
const yamlPath = join117(dir, `${stageId}.yaml`);
|
|
127329
|
-
if (!
|
|
127571
|
+
if (!existsSync113(yamlPath))
|
|
127330
127572
|
continue;
|
|
127331
127573
|
try {
|
|
127332
|
-
const meta = JSON.parse(
|
|
127574
|
+
const meta = JSON.parse(readFileSync101(metaPath, "utf-8"));
|
|
127333
127575
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
127334
127576
|
continue;
|
|
127335
127577
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -127345,10 +127587,10 @@ function commitPendingScheduleEntry(opts) {
|
|
|
127345
127587
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
127346
127588
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
127347
127589
|
const finalPath = join117(paths.scheduleDir, `${slug}.yaml`);
|
|
127348
|
-
if (
|
|
127590
|
+
if (existsSync113(finalPath)) {
|
|
127349
127591
|
return { committed: false, reason: "slug_collision" };
|
|
127350
127592
|
}
|
|
127351
|
-
|
|
127593
|
+
renameSync31(match.yamlPath, finalPath);
|
|
127352
127594
|
unlinkSync23(match.metaPath);
|
|
127353
127595
|
return { committed: true, path: finalPath, slug };
|
|
127354
127596
|
}
|
|
@@ -127367,8 +127609,8 @@ function denyPendingScheduleEntry(opts) {
|
|
|
127367
127609
|
}
|
|
127368
127610
|
|
|
127369
127611
|
// src/cli/agent-config-write.ts
|
|
127370
|
-
import { existsSync as
|
|
127371
|
-
import { execFileSync as
|
|
127612
|
+
import { existsSync as existsSync114, readFileSync as readFileSync102 } from "node:fs";
|
|
127613
|
+
import { execFileSync as execFileSync37 } from "node:child_process";
|
|
127372
127614
|
|
|
127373
127615
|
// src/scheduler/schedule-report.ts
|
|
127374
127616
|
var TIER_WEIGHT = { poll: 0, action: 0, cheap: 1, main: 5 };
|
|
@@ -127757,8 +127999,8 @@ function scheduleRemove(opts) {
|
|
|
127757
127999
|
}
|
|
127758
128000
|
let priorContent = null;
|
|
127759
128001
|
try {
|
|
127760
|
-
if (
|
|
127761
|
-
priorContent =
|
|
128002
|
+
if (existsSync114(match.path))
|
|
128003
|
+
priorContent = readFileSync102(match.path, "utf-8");
|
|
127762
128004
|
} catch {}
|
|
127763
128005
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
127764
128006
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -127961,10 +128203,10 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
127961
128203
|
}
|
|
127962
128204
|
let blob;
|
|
127963
128205
|
if (opts.jsonl) {
|
|
127964
|
-
blob =
|
|
128206
|
+
blob = existsSync114(opts.jsonl) ? readFileSync102(opts.jsonl, "utf-8") : "";
|
|
127965
128207
|
} else {
|
|
127966
128208
|
try {
|
|
127967
|
-
blob =
|
|
128209
|
+
blob = execFileSync37("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
127968
128210
|
encoding: "utf-8",
|
|
127969
128211
|
stdio: ["ignore", "pipe", "ignore"]
|
|
127970
128212
|
});
|
|
@@ -127993,7 +128235,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
127993
128235
|
|
|
127994
128236
|
// src/cli/agent-config-skill-write.ts
|
|
127995
128237
|
var import_yaml24 = __toESM(require_dist(), 1);
|
|
127996
|
-
import { existsSync as
|
|
128238
|
+
import { existsSync as existsSync115 } from "node:fs";
|
|
127997
128239
|
init_reconcile_default_skills();
|
|
127998
128240
|
init_agent_config();
|
|
127999
128241
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
@@ -128073,7 +128315,7 @@ function skillInstall(opts) {
|
|
|
128073
128315
|
}
|
|
128074
128316
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
128075
128317
|
const skillPath = join118(poolDir, skillName);
|
|
128076
|
-
if (!
|
|
128318
|
+
if (!existsSync115(skillPath)) {
|
|
128077
128319
|
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.`);
|
|
128078
128320
|
}
|
|
128079
128321
|
const yamlText = import_yaml24.stringify({ skills: [skillName] });
|
|
@@ -128243,21 +128485,21 @@ init_source();
|
|
|
128243
128485
|
import {
|
|
128244
128486
|
chmodSync as chmodSync18,
|
|
128245
128487
|
closeSync as closeSync22,
|
|
128246
|
-
existsSync as
|
|
128488
|
+
existsSync as existsSync116,
|
|
128247
128489
|
lstatSync as lstatSync15,
|
|
128248
|
-
mkdirSync as
|
|
128490
|
+
mkdirSync as mkdirSync68,
|
|
128249
128491
|
mkdtempSync as mkdtempSync6,
|
|
128250
128492
|
openSync as openSync22,
|
|
128251
|
-
readFileSync as
|
|
128493
|
+
readFileSync as readFileSync103,
|
|
128252
128494
|
readdirSync as readdirSync43,
|
|
128253
128495
|
realpathSync as realpathSync10,
|
|
128254
|
-
renameSync as
|
|
128496
|
+
renameSync as renameSync32,
|
|
128255
128497
|
rmSync as rmSync24,
|
|
128256
|
-
statSync as
|
|
128257
|
-
writeFileSync as
|
|
128498
|
+
statSync as statSync68,
|
|
128499
|
+
writeFileSync as writeFileSync50
|
|
128258
128500
|
} from "node:fs";
|
|
128259
128501
|
import { tmpdir as tmpdir7, homedir as homedir60 } from "node:os";
|
|
128260
|
-
import { dirname as
|
|
128502
|
+
import { dirname as dirname51, join as join119, relative as relative6, resolve as resolve61 } from "node:path";
|
|
128261
128503
|
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
128262
128504
|
var SKILL_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
128263
128505
|
var SH_SCRIPT_RE2 = /^scripts\/[A-Za-z0-9_.-]+\.sh$/;
|
|
@@ -128303,7 +128545,7 @@ function isTarballPath(p) {
|
|
|
128303
128545
|
}
|
|
128304
128546
|
function loadFromDir(dir) {
|
|
128305
128547
|
const abs = realpathSync10(dir);
|
|
128306
|
-
if (!
|
|
128548
|
+
if (!statSync68(abs).isDirectory()) {
|
|
128307
128549
|
fail4(`--from path is not a directory: ${dir}`);
|
|
128308
128550
|
}
|
|
128309
128551
|
const files = {};
|
|
@@ -128320,7 +128562,7 @@ function loadFromDir(dir) {
|
|
|
128320
128562
|
continue;
|
|
128321
128563
|
}
|
|
128322
128564
|
if (ent.isFile()) {
|
|
128323
|
-
const buf =
|
|
128565
|
+
const buf = readFileSync103(full);
|
|
128324
128566
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
128325
128567
|
}
|
|
128326
128568
|
}
|
|
@@ -128369,7 +128611,7 @@ function loadFromTarball(tarPath) {
|
|
|
128369
128611
|
}
|
|
128370
128612
|
}
|
|
128371
128613
|
function loadSingleFile(filePath) {
|
|
128372
|
-
const content =
|
|
128614
|
+
const content = readFileSync103(filePath, "utf-8");
|
|
128373
128615
|
return { "SKILL.md": content };
|
|
128374
128616
|
}
|
|
128375
128617
|
function loadFromStdin() {
|
|
@@ -128434,7 +128676,7 @@ function validatePayload(name, files) {
|
|
|
128434
128676
|
const tmp = mkdtempSync6(join119(tmpdir7(), "skill-apply-py-"));
|
|
128435
128677
|
const tmpPy = join119(tmp, "check.py");
|
|
128436
128678
|
try {
|
|
128437
|
-
|
|
128679
|
+
writeFileSync50(tmpPy, content);
|
|
128438
128680
|
const r = spawnSync24("python3", ["-m", "py_compile", tmpPy], {
|
|
128439
128681
|
encoding: "utf-8"
|
|
128440
128682
|
});
|
|
@@ -128452,7 +128694,7 @@ function validatePayload(name, files) {
|
|
|
128452
128694
|
function diffSummary(currentDir, files) {
|
|
128453
128695
|
const lines = [];
|
|
128454
128696
|
const currentFiles = {};
|
|
128455
|
-
if (
|
|
128697
|
+
if (existsSync116(currentDir)) {
|
|
128456
128698
|
const walk2 = (sub) => {
|
|
128457
128699
|
for (const ent of readdirSync43(sub, { withFileTypes: true })) {
|
|
128458
128700
|
const full = join119(sub, ent.name);
|
|
@@ -128460,7 +128702,7 @@ function diffSummary(currentDir, files) {
|
|
|
128460
128702
|
if (ent.isDirectory()) {
|
|
128461
128703
|
walk2(full);
|
|
128462
128704
|
} else if (ent.isFile()) {
|
|
128463
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
128705
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync103(full, "utf-8");
|
|
128464
128706
|
}
|
|
128465
128707
|
}
|
|
128466
128708
|
};
|
|
@@ -128488,8 +128730,8 @@ function diffSummary(currentDir, files) {
|
|
|
128488
128730
|
`);
|
|
128489
128731
|
}
|
|
128490
128732
|
function writePayload(poolDir, name, files) {
|
|
128491
|
-
if (!
|
|
128492
|
-
|
|
128733
|
+
if (!existsSync116(poolDir)) {
|
|
128734
|
+
mkdirSync68(poolDir, { recursive: true, mode: 493 });
|
|
128493
128735
|
}
|
|
128494
128736
|
const target = join119(poolDir, name);
|
|
128495
128737
|
let targetIsSymlink = false;
|
|
@@ -128508,10 +128750,10 @@ function writePayload(poolDir, name, files) {
|
|
|
128508
128750
|
try {
|
|
128509
128751
|
for (const [path10, content] of Object.entries(files)) {
|
|
128510
128752
|
const full = join119(staging, path10);
|
|
128511
|
-
|
|
128753
|
+
mkdirSync68(dirname51(full), { recursive: true, mode: 493 });
|
|
128512
128754
|
const fd = openSync22(full, "wx");
|
|
128513
128755
|
try {
|
|
128514
|
-
|
|
128756
|
+
writeFileSync50(fd, content);
|
|
128515
128757
|
} finally {
|
|
128516
128758
|
closeSync22(fd);
|
|
128517
128759
|
}
|
|
@@ -128527,9 +128769,9 @@ function writePayload(poolDir, name, files) {
|
|
|
128527
128769
|
} catch {}
|
|
128528
128770
|
if (targetExists) {
|
|
128529
128771
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
128530
|
-
|
|
128772
|
+
renameSync32(target, oldRename);
|
|
128531
128773
|
}
|
|
128532
|
-
|
|
128774
|
+
renameSync32(staging, target);
|
|
128533
128775
|
if (oldRename) {
|
|
128534
128776
|
rmSync24(oldRename, { recursive: true, force: true });
|
|
128535
128777
|
oldRename = null;
|
|
@@ -128538,12 +128780,12 @@ function writePayload(poolDir, name, files) {
|
|
|
128538
128780
|
try {
|
|
128539
128781
|
rmSync24(staging, { recursive: true, force: true });
|
|
128540
128782
|
} catch {}
|
|
128541
|
-
if (oldRename &&
|
|
128783
|
+
if (oldRename && existsSync116(oldRename)) {
|
|
128542
128784
|
try {
|
|
128543
|
-
if (
|
|
128785
|
+
if (existsSync116(target)) {
|
|
128544
128786
|
rmSync24(target, { recursive: true, force: true });
|
|
128545
128787
|
}
|
|
128546
|
-
|
|
128788
|
+
renameSync32(oldRename, target);
|
|
128547
128789
|
} catch {}
|
|
128548
128790
|
}
|
|
128549
128791
|
throw err2;
|
|
@@ -128561,10 +128803,10 @@ function registerSkillCommand(program3) {
|
|
|
128561
128803
|
files = loadFromStdin();
|
|
128562
128804
|
} else {
|
|
128563
128805
|
const fromPath = resolve61(opts.from);
|
|
128564
|
-
if (!
|
|
128806
|
+
if (!existsSync116(fromPath)) {
|
|
128565
128807
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
128566
128808
|
}
|
|
128567
|
-
const st =
|
|
128809
|
+
const st = statSync68(fromPath);
|
|
128568
128810
|
if (st.isDirectory()) {
|
|
128569
128811
|
files = loadFromDir(fromPath);
|
|
128570
128812
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -128622,20 +128864,20 @@ init_source();
|
|
|
128622
128864
|
import {
|
|
128623
128865
|
chmodSync as chmodSync19,
|
|
128624
128866
|
closeSync as closeSync23,
|
|
128625
|
-
existsSync as
|
|
128867
|
+
existsSync as existsSync117,
|
|
128626
128868
|
lstatSync as lstatSync16,
|
|
128627
|
-
mkdirSync as
|
|
128869
|
+
mkdirSync as mkdirSync69,
|
|
128628
128870
|
mkdtempSync as mkdtempSync7,
|
|
128629
128871
|
openSync as openSync23,
|
|
128630
|
-
readFileSync as
|
|
128872
|
+
readFileSync as readFileSync104,
|
|
128631
128873
|
readdirSync as readdirSync44,
|
|
128632
|
-
renameSync as
|
|
128874
|
+
renameSync as renameSync33,
|
|
128633
128875
|
rmSync as rmSync25,
|
|
128634
|
-
statSync as
|
|
128876
|
+
statSync as statSync69,
|
|
128635
128877
|
utimesSync,
|
|
128636
|
-
writeFileSync as
|
|
128878
|
+
writeFileSync as writeFileSync51
|
|
128637
128879
|
} from "node:fs";
|
|
128638
|
-
import { dirname as
|
|
128880
|
+
import { dirname as dirname52, join as join120, relative as relative7, resolve as resolve62 } from "node:path";
|
|
128639
128881
|
import { homedir as homedir61, tmpdir as tmpdir8 } from "node:os";
|
|
128640
128882
|
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
128641
128883
|
var PERSONAL_PREFIX = "personal-";
|
|
@@ -128645,14 +128887,14 @@ var PERSONAL_SKILLS_SUBPATH2 = "personal-skills";
|
|
|
128645
128887
|
function resolveConfigSkillsDir(agent) {
|
|
128646
128888
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
128647
128889
|
const candidate = override ? resolve62(override) : join120(homedir61(), ".switchroom-config");
|
|
128648
|
-
if (!
|
|
128890
|
+
if (!existsSync117(candidate))
|
|
128649
128891
|
return null;
|
|
128650
128892
|
return join120(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH2);
|
|
128651
128893
|
}
|
|
128652
128894
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
128653
128895
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
128654
128896
|
try {
|
|
128655
|
-
if (!
|
|
128897
|
+
if (!existsSync117(configSkillsRoot))
|
|
128656
128898
|
return;
|
|
128657
128899
|
const now2 = Date.now();
|
|
128658
128900
|
for (const ent of readdirSync44(configSkillsRoot)) {
|
|
@@ -128688,17 +128930,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
128688
128930
|
}
|
|
128689
128931
|
if (liveSkillDir === null) {
|
|
128690
128932
|
sweepMirrorPriors(configSkillsRoot);
|
|
128691
|
-
if (
|
|
128933
|
+
if (existsSync117(dest)) {
|
|
128692
128934
|
const trash = join120(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
128693
|
-
|
|
128935
|
+
renameSync33(dest, trash);
|
|
128694
128936
|
}
|
|
128695
128937
|
return;
|
|
128696
128938
|
}
|
|
128697
|
-
|
|
128939
|
+
mkdirSync69(configSkillsRoot, { recursive: true, mode: 493 });
|
|
128698
128940
|
sweepMirrorPriors(configSkillsRoot);
|
|
128699
128941
|
const staging = mkdtempSync7(join120(configSkillsRoot, `.${name}-staging-`));
|
|
128700
128942
|
const walk2 = (src, dst) => {
|
|
128701
|
-
|
|
128943
|
+
mkdirSync69(dst, { recursive: true, mode: 493 });
|
|
128702
128944
|
for (const ent of readdirSync44(src, { withFileTypes: true })) {
|
|
128703
128945
|
const s = join120(src, ent.name);
|
|
128704
128946
|
const d = join120(dst, ent.name);
|
|
@@ -128707,17 +128949,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
128707
128949
|
if (ent.isDirectory())
|
|
128708
128950
|
walk2(s, d);
|
|
128709
128951
|
else if (ent.isFile()) {
|
|
128710
|
-
|
|
128952
|
+
writeFileSync51(d, readFileSync104(s));
|
|
128711
128953
|
}
|
|
128712
128954
|
}
|
|
128713
128955
|
};
|
|
128714
128956
|
walk2(liveSkillDir, staging);
|
|
128715
128957
|
chmodSync19(staging, 493);
|
|
128716
|
-
if (
|
|
128958
|
+
if (existsSync117(dest)) {
|
|
128717
128959
|
const prior = join120(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
128718
|
-
|
|
128960
|
+
renameSync33(dest, prior);
|
|
128719
128961
|
}
|
|
128720
|
-
|
|
128962
|
+
renameSync33(staging, dest);
|
|
128721
128963
|
} catch (err2) {
|
|
128722
128964
|
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.
|
|
128723
128965
|
`));
|
|
@@ -128754,7 +128996,7 @@ function trashDir(agentsRoot, agent) {
|
|
|
128754
128996
|
}
|
|
128755
128997
|
function countPersonalSkills(agentsRoot, agent) {
|
|
128756
128998
|
const skillsDir = join120(agentsRoot, agent, ".claude", "skills");
|
|
128757
|
-
if (!
|
|
128999
|
+
if (!existsSync117(skillsDir))
|
|
128758
129000
|
return 0;
|
|
128759
129001
|
let n = 0;
|
|
128760
129002
|
for (const ent of readdirSync44(skillsDir, { withFileTypes: true })) {
|
|
@@ -128785,7 +129027,7 @@ function readStdinSync2() {
|
|
|
128785
129027
|
}
|
|
128786
129028
|
function loadFromDir2(dir) {
|
|
128787
129029
|
const abs = resolve62(dir);
|
|
128788
|
-
if (!
|
|
129030
|
+
if (!statSync69(abs).isDirectory()) {
|
|
128789
129031
|
fail5(`--from path is not a directory: ${dir}`);
|
|
128790
129032
|
}
|
|
128791
129033
|
const files = {};
|
|
@@ -128801,7 +129043,7 @@ function loadFromDir2(dir) {
|
|
|
128801
129043
|
}
|
|
128802
129044
|
if (ent.isFile()) {
|
|
128803
129045
|
const rel = relative7(abs, full).replace(/\\/g, "/");
|
|
128804
|
-
files[rel] =
|
|
129046
|
+
files[rel] = readFileSync104(full, "utf-8");
|
|
128805
129047
|
}
|
|
128806
129048
|
}
|
|
128807
129049
|
};
|
|
@@ -128847,7 +129089,7 @@ function behavioralValidate(files) {
|
|
|
128847
129089
|
const tmp = mkdtempSync7(join120(tmpdir8(), "skill-personal-py-"));
|
|
128848
129090
|
const tmpPy = join120(tmp, "check.py");
|
|
128849
129091
|
try {
|
|
128850
|
-
|
|
129092
|
+
writeFileSync51(tmpPy, content);
|
|
128851
129093
|
const r = spawnSync25("python3", ["-m", "py_compile", tmpPy], {
|
|
128852
129094
|
encoding: "utf-8"
|
|
128853
129095
|
});
|
|
@@ -128863,7 +129105,7 @@ function behavioralValidate(files) {
|
|
|
128863
129105
|
}
|
|
128864
129106
|
function sweepTrash(agentsRoot, agent) {
|
|
128865
129107
|
const trash = trashDir(agentsRoot, agent);
|
|
128866
|
-
if (!
|
|
129108
|
+
if (!existsSync117(trash))
|
|
128867
129109
|
return;
|
|
128868
129110
|
const now2 = Date.now();
|
|
128869
129111
|
for (const ent of readdirSync44(trash, { withFileTypes: true })) {
|
|
@@ -128871,7 +129113,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
128871
129113
|
continue;
|
|
128872
129114
|
const entPath = join120(trash, ent.name);
|
|
128873
129115
|
try {
|
|
128874
|
-
const st =
|
|
129116
|
+
const st = statSync69(entPath);
|
|
128875
129117
|
if (now2 - st.mtimeMs > TRASH_TTL_MS) {
|
|
128876
129118
|
rmSync25(entPath, { recursive: true, force: true });
|
|
128877
129119
|
}
|
|
@@ -128889,17 +129131,17 @@ function writePersonalSkill(targetDir, files) {
|
|
|
128889
129131
|
if (targetIsSymlink) {
|
|
128890
129132
|
fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
128891
129133
|
}
|
|
128892
|
-
|
|
128893
|
-
const staging = mkdtempSync7(join120(
|
|
129134
|
+
mkdirSync69(dirname52(targetDir), { recursive: true, mode: 493 });
|
|
129135
|
+
const staging = mkdtempSync7(join120(dirname52(targetDir), `.skill-personal-stage-`));
|
|
128894
129136
|
chmodSync19(staging, 493);
|
|
128895
129137
|
let oldRename = null;
|
|
128896
129138
|
try {
|
|
128897
129139
|
for (const [path10, content] of Object.entries(files)) {
|
|
128898
129140
|
const full = join120(staging, path10);
|
|
128899
|
-
|
|
129141
|
+
mkdirSync69(dirname52(full), { recursive: true, mode: 493 });
|
|
128900
129142
|
const fd = openSync23(full, "wx");
|
|
128901
129143
|
try {
|
|
128902
|
-
|
|
129144
|
+
writeFileSync51(fd, content);
|
|
128903
129145
|
} finally {
|
|
128904
129146
|
closeSync23(fd);
|
|
128905
129147
|
}
|
|
@@ -128915,9 +129157,9 @@ function writePersonalSkill(targetDir, files) {
|
|
|
128915
129157
|
} catch {}
|
|
128916
129158
|
if (targetExists) {
|
|
128917
129159
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
128918
|
-
|
|
129160
|
+
renameSync33(targetDir, oldRename);
|
|
128919
129161
|
}
|
|
128920
|
-
|
|
129162
|
+
renameSync33(staging, targetDir);
|
|
128921
129163
|
if (oldRename) {
|
|
128922
129164
|
rmSync25(oldRename, { recursive: true, force: true });
|
|
128923
129165
|
oldRename = null;
|
|
@@ -128926,12 +129168,12 @@ function writePersonalSkill(targetDir, files) {
|
|
|
128926
129168
|
try {
|
|
128927
129169
|
rmSync25(staging, { recursive: true, force: true });
|
|
128928
129170
|
} catch {}
|
|
128929
|
-
if (oldRename &&
|
|
129171
|
+
if (oldRename && existsSync117(oldRename)) {
|
|
128930
129172
|
try {
|
|
128931
|
-
if (
|
|
129173
|
+
if (existsSync117(targetDir)) {
|
|
128932
129174
|
rmSync25(targetDir, { recursive: true, force: true });
|
|
128933
129175
|
}
|
|
128934
|
-
|
|
129176
|
+
renameSync33(oldRename, targetDir);
|
|
128935
129177
|
} catch {}
|
|
128936
129178
|
}
|
|
128937
129179
|
throw err2;
|
|
@@ -128994,15 +129236,15 @@ function loadFiles(opts) {
|
|
|
128994
129236
|
return loadFromStdin2();
|
|
128995
129237
|
}
|
|
128996
129238
|
const p = resolve62(opts.from);
|
|
128997
|
-
if (!
|
|
129239
|
+
if (!existsSync117(p)) {
|
|
128998
129240
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
128999
129241
|
}
|
|
129000
|
-
const st =
|
|
129242
|
+
const st = statSync69(p);
|
|
129001
129243
|
if (st.isDirectory()) {
|
|
129002
129244
|
return loadFromDir2(p);
|
|
129003
129245
|
}
|
|
129004
129246
|
if (p.endsWith(".md")) {
|
|
129005
|
-
return { "SKILL.md":
|
|
129247
|
+
return { "SKILL.md": readFileSync104(p, "utf-8") };
|
|
129006
129248
|
}
|
|
129007
129249
|
fail5(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
129008
129250
|
}
|
|
@@ -129056,7 +129298,7 @@ function resolveCloneSource(source, opts) {
|
|
|
129056
129298
|
const slug = m[2];
|
|
129057
129299
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
129058
129300
|
const dir = join120(root, slug);
|
|
129059
|
-
if (!
|
|
129301
|
+
if (!existsSync117(dir)) {
|
|
129060
129302
|
fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
129061
129303
|
}
|
|
129062
129304
|
const st = lstatSync16(dir);
|
|
@@ -129091,7 +129333,7 @@ function readSourceFiles(dir) {
|
|
|
129091
129333
|
fail5(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
129092
129334
|
}
|
|
129093
129335
|
} catch {}
|
|
129094
|
-
files[rel] =
|
|
129336
|
+
files[rel] = readFileSync104(full, "utf-8");
|
|
129095
129337
|
}
|
|
129096
129338
|
}
|
|
129097
129339
|
};
|
|
@@ -129180,10 +129422,10 @@ function removePersonalAction(name, opts) {
|
|
|
129180
129422
|
throw err2;
|
|
129181
129423
|
}
|
|
129182
129424
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
129183
|
-
|
|
129425
|
+
mkdirSync69(trashRoot2, { recursive: true, mode: 493 });
|
|
129184
129426
|
const ts = Date.now();
|
|
129185
129427
|
const trashTarget = join120(trashRoot2, `${name}-${ts}`);
|
|
129186
|
-
|
|
129428
|
+
renameSync33(target, trashTarget);
|
|
129187
129429
|
const now2 = new Date(ts);
|
|
129188
129430
|
utimesSync(trashTarget, now2, now2);
|
|
129189
129431
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -129203,7 +129445,7 @@ function listPersonalAction(opts) {
|
|
|
129203
129445
|
sweepTrash(agentsRoot, agent);
|
|
129204
129446
|
const skillsDir = join120(agentsRoot, agent, ".claude", "skills");
|
|
129205
129447
|
const personal = [];
|
|
129206
|
-
if (
|
|
129448
|
+
if (existsSync117(skillsDir)) {
|
|
129207
129449
|
for (const ent of readdirSync44(skillsDir, { withFileTypes: true })) {
|
|
129208
129450
|
if (!ent.isDirectory())
|
|
129209
129451
|
continue;
|
|
@@ -129218,7 +129460,7 @@ function listPersonalAction(opts) {
|
|
|
129218
129460
|
if (e.isFile()) {
|
|
129219
129461
|
fileCount += 1;
|
|
129220
129462
|
try {
|
|
129221
|
-
totalBytes +=
|
|
129463
|
+
totalBytes += statSync69(join120(sub, e.name)).size;
|
|
129222
129464
|
} catch {}
|
|
129223
129465
|
} else if (e.isDirectory()) {
|
|
129224
129466
|
walk2(join120(sub, e.name));
|
|
@@ -129261,7 +129503,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
129261
129503
|
import { createConnection as createConnection4 } from "node:net";
|
|
129262
129504
|
import { homedir as homedir62 } from "node:os";
|
|
129263
129505
|
import { join as join121 } from "node:path";
|
|
129264
|
-
import { readFileSync as
|
|
129506
|
+
import { readFileSync as readFileSync105 } from "node:fs";
|
|
129265
129507
|
|
|
129266
129508
|
// src/self-improve/skill-proposals.ts
|
|
129267
129509
|
var REJECTION_TTL_MS = 90 * 24 * 60 * 60 * 1000;
|
|
@@ -129350,7 +129592,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
129350
129592
|
}
|
|
129351
129593
|
let draft;
|
|
129352
129594
|
try {
|
|
129353
|
-
draft = JSON.parse(
|
|
129595
|
+
draft = JSON.parse(readFileSync105(opts.draft, "utf-8"));
|
|
129354
129596
|
} catch (e) {
|
|
129355
129597
|
fail6(`failed to read/parse --draft: ${e.message}`);
|
|
129356
129598
|
}
|
|
@@ -129389,10 +129631,10 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
129389
129631
|
import { createConnection as createConnection5 } from "node:net";
|
|
129390
129632
|
import { homedir as homedir63 } from "node:os";
|
|
129391
129633
|
import { join as join125, resolve as resolve63, sep as sep10 } from "node:path";
|
|
129392
|
-
import { existsSync as
|
|
129634
|
+
import { existsSync as existsSync122, readFileSync as readFileSync110, realpathSync as realpathSync11 } from "node:fs";
|
|
129393
129635
|
|
|
129394
129636
|
// src/self-improve/apply-guard.ts
|
|
129395
|
-
import { existsSync as
|
|
129637
|
+
import { existsSync as existsSync120, lstatSync as lstatSync17, readFileSync as readFileSync108, statSync as statSync71 } from "node:fs";
|
|
129396
129638
|
|
|
129397
129639
|
// src/self-improve/config.ts
|
|
129398
129640
|
function intEnv(name, def) {
|
|
@@ -129413,7 +129655,7 @@ function resolveSelfImproveConfig() {
|
|
|
129413
129655
|
}
|
|
129414
129656
|
|
|
129415
129657
|
// src/self-improve/eval-gate.ts
|
|
129416
|
-
import { existsSync as
|
|
129658
|
+
import { existsSync as existsSync118, readFileSync as readFileSync106, readdirSync as readdirSync45, statSync as statSync70 } from "node:fs";
|
|
129417
129659
|
import { join as join122 } from "node:path";
|
|
129418
129660
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
129419
129661
|
function evalsJsonPath(skillDir) {
|
|
@@ -129421,10 +129663,10 @@ function evalsJsonPath(skillDir) {
|
|
|
129421
129663
|
}
|
|
129422
129664
|
function skillHasEvals(skillDir) {
|
|
129423
129665
|
const p = evalsJsonPath(skillDir);
|
|
129424
|
-
if (!
|
|
129666
|
+
if (!existsSync118(p))
|
|
129425
129667
|
return false;
|
|
129426
129668
|
try {
|
|
129427
|
-
const parsed = JSON.parse(
|
|
129669
|
+
const parsed = JSON.parse(readFileSync106(p, "utf-8"));
|
|
129428
129670
|
return Array.isArray(parsed.evals) && parsed.evals.length > 0;
|
|
129429
129671
|
} catch {
|
|
129430
129672
|
return false;
|
|
@@ -129432,7 +129674,7 @@ function skillHasEvals(skillDir) {
|
|
|
129432
129674
|
}
|
|
129433
129675
|
function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWITCHROOM_PYTHON ?? "python3") {
|
|
129434
129676
|
const script = join122(repoRoot, "skills", "skill-creator", "scripts", "aggregate_benchmark.py");
|
|
129435
|
-
if (!
|
|
129677
|
+
if (!existsSync118(script)) {
|
|
129436
129678
|
return { ok: false, error: `aggregate_benchmark.py not found at ${script}` };
|
|
129437
129679
|
}
|
|
129438
129680
|
const r = spawnSync26(pythonBin, [script, benchmarkDir], {
|
|
@@ -129449,7 +129691,7 @@ function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWIT
|
|
|
129449
129691
|
}
|
|
129450
129692
|
function isBenchmarkDir(dir) {
|
|
129451
129693
|
try {
|
|
129452
|
-
if (!
|
|
129694
|
+
if (!statSync70(dir).isDirectory())
|
|
129453
129695
|
return false;
|
|
129454
129696
|
} catch {
|
|
129455
129697
|
return false;
|
|
@@ -129464,7 +129706,7 @@ function isBenchmarkDir(dir) {
|
|
|
129464
129706
|
if (hasEvalDirs(dir))
|
|
129465
129707
|
return true;
|
|
129466
129708
|
const runs = join122(dir, "runs");
|
|
129467
|
-
return
|
|
129709
|
+
return existsSync118(runs) && hasEvalDirs(runs);
|
|
129468
129710
|
}
|
|
129469
129711
|
function resolveBenchmarkDir(baseDir, explicit) {
|
|
129470
129712
|
if (explicit != null && explicit.length > 0) {
|
|
@@ -129480,7 +129722,7 @@ function resolveBenchmarkDir(baseDir, explicit) {
|
|
|
129480
129722
|
}
|
|
129481
129723
|
if (entries.length === 0)
|
|
129482
129724
|
return null;
|
|
129483
|
-
entries.sort((a, b) =>
|
|
129725
|
+
entries.sort((a, b) => statSync70(b).mtimeMs - statSync70(a).mtimeMs);
|
|
129484
129726
|
return entries[0];
|
|
129485
129727
|
}
|
|
129486
129728
|
var CANDIDATE_CONFIGS = ["with_skill", "candidate", "new_skill", "after"];
|
|
@@ -129497,7 +129739,7 @@ function pickPassRate(summary, names) {
|
|
|
129497
129739
|
function evalVerdict(benchmarkJson, _cfg = resolveSelfImproveConfig()) {
|
|
129498
129740
|
let bench;
|
|
129499
129741
|
try {
|
|
129500
|
-
bench = JSON.parse(
|
|
129742
|
+
bench = JSON.parse(readFileSync106(benchmarkJson, "utf-8"));
|
|
129501
129743
|
} catch (err2) {
|
|
129502
129744
|
return { pass: false, reason: `unreadable benchmark.json: ${err2.message}` };
|
|
129503
129745
|
}
|
|
@@ -129543,16 +129785,16 @@ function floatEnv(name, def) {
|
|
|
129543
129785
|
import {
|
|
129544
129786
|
closeSync as closeSync24,
|
|
129545
129787
|
copyFileSync as copyFileSync15,
|
|
129546
|
-
existsSync as
|
|
129547
|
-
mkdirSync as
|
|
129788
|
+
existsSync as existsSync119,
|
|
129789
|
+
mkdirSync as mkdirSync70,
|
|
129548
129790
|
openSync as openSync24,
|
|
129549
|
-
readFileSync as
|
|
129791
|
+
readFileSync as readFileSync107,
|
|
129550
129792
|
readdirSync as readdirSync46,
|
|
129551
|
-
renameSync as
|
|
129552
|
-
writeFileSync as
|
|
129793
|
+
renameSync as renameSync34,
|
|
129794
|
+
writeFileSync as writeFileSync52,
|
|
129553
129795
|
writeSync as writeSync12
|
|
129554
129796
|
} from "node:fs";
|
|
129555
|
-
import { dirname as
|
|
129797
|
+
import { dirname as dirname53, join as join123 } from "node:path";
|
|
129556
129798
|
import { createHash as createHash22 } from "node:crypto";
|
|
129557
129799
|
|
|
129558
129800
|
// src/self-improve/pii-scan.ts
|
|
@@ -129690,10 +129932,10 @@ function caseFingerprint(prompt) {
|
|
|
129690
129932
|
}
|
|
129691
129933
|
function readEvalsDoc(skillDir) {
|
|
129692
129934
|
const p = evalsJsonPath(skillDir);
|
|
129693
|
-
if (!
|
|
129935
|
+
if (!existsSync119(p))
|
|
129694
129936
|
return null;
|
|
129695
129937
|
try {
|
|
129696
|
-
const parsed = JSON.parse(
|
|
129938
|
+
const parsed = JSON.parse(readFileSync107(p, "utf-8"));
|
|
129697
129939
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.evals)) {
|
|
129698
129940
|
return null;
|
|
129699
129941
|
}
|
|
@@ -129703,10 +129945,10 @@ function readEvalsDoc(skillDir) {
|
|
|
129703
129945
|
}
|
|
129704
129946
|
}
|
|
129705
129947
|
function writeAtomic(path10, content) {
|
|
129706
|
-
|
|
129948
|
+
mkdirSync70(dirname53(path10), { recursive: true });
|
|
129707
129949
|
const tmp = `${path10}.tmp-${process.pid}-${Date.now()}`;
|
|
129708
|
-
|
|
129709
|
-
|
|
129950
|
+
writeFileSync52(tmp, content, { encoding: "utf-8", mode: 420 });
|
|
129951
|
+
renameSync34(tmp, path10);
|
|
129710
129952
|
}
|
|
129711
129953
|
function appendEvalCase(skillDir, slug, input) {
|
|
129712
129954
|
const parsed = parseEvalCase(input);
|
|
@@ -129714,7 +129956,7 @@ function appendEvalCase(skillDir, slug, input) {
|
|
|
129714
129956
|
return { ok: false, reason: parsed.error };
|
|
129715
129957
|
const ec = parsed.case;
|
|
129716
129958
|
const existingDoc = readEvalsDoc(skillDir);
|
|
129717
|
-
if (existingDoc === null &&
|
|
129959
|
+
if (existingDoc === null && existsSync119(evalsJsonPath(skillDir))) {
|
|
129718
129960
|
return {
|
|
129719
129961
|
ok: false,
|
|
129720
129962
|
reason: "evals.json exists but is unparseable or malformed \u2014 refusing to " + "overwrite it (fix or remove the file first)"
|
|
@@ -129746,11 +129988,11 @@ function heldOutPath(stateDir) {
|
|
|
129746
129988
|
}
|
|
129747
129989
|
function heldOutHasFingerprint(stateDir, slug, fp) {
|
|
129748
129990
|
const path10 = heldOutPath(stateDir);
|
|
129749
|
-
if (!
|
|
129991
|
+
if (!existsSync119(path10))
|
|
129750
129992
|
return false;
|
|
129751
129993
|
let text;
|
|
129752
129994
|
try {
|
|
129753
|
-
text =
|
|
129995
|
+
text = readFileSync107(path10, "utf-8");
|
|
129754
129996
|
} catch {
|
|
129755
129997
|
return false;
|
|
129756
129998
|
}
|
|
@@ -129778,7 +130020,7 @@ function appendHeldOutCase(stateDir, slug, ec, opts = {}) {
|
|
|
129778
130020
|
};
|
|
129779
130021
|
}
|
|
129780
130022
|
const now2 = opts.now ?? Date.now;
|
|
129781
|
-
|
|
130023
|
+
mkdirSync70(stateDir, { recursive: true });
|
|
129782
130024
|
const fd = openSync24(heldOutPath(stateDir), "a");
|
|
129783
130025
|
try {
|
|
129784
130026
|
writeSync12(fd, JSON.stringify({ at: new Date(now2()).toISOString(), slug, case: ec }) + `
|
|
@@ -129798,20 +130040,20 @@ function baselineEvalsPath(stateDir, slug) {
|
|
|
129798
130040
|
}
|
|
129799
130041
|
function evalsSha256(skillDir) {
|
|
129800
130042
|
const p = evalsJsonPath(skillDir);
|
|
129801
|
-
if (!
|
|
130043
|
+
if (!existsSync119(p))
|
|
129802
130044
|
return null;
|
|
129803
130045
|
try {
|
|
129804
|
-
return createHash22("sha256").update(
|
|
130046
|
+
return createHash22("sha256").update(readFileSync107(p)).digest("hex");
|
|
129805
130047
|
} catch {
|
|
129806
130048
|
return null;
|
|
129807
130049
|
}
|
|
129808
130050
|
}
|
|
129809
130051
|
function readManifest(stateDir) {
|
|
129810
130052
|
const p = manifestPath(stateDir);
|
|
129811
|
-
if (!
|
|
130053
|
+
if (!existsSync119(p))
|
|
129812
130054
|
return {};
|
|
129813
130055
|
try {
|
|
129814
|
-
const parsed = JSON.parse(
|
|
130056
|
+
const parsed = JSON.parse(readFileSync107(p, "utf-8"));
|
|
129815
130057
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
129816
130058
|
} catch {
|
|
129817
130059
|
return {};
|
|
@@ -129830,7 +130072,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
|
|
|
129830
130072
|
m[slug] = { sha256: sha, updated_at: new Date(now2()).toISOString(), skill_dir: skillDir };
|
|
129831
130073
|
writeManifest(stateDir, m);
|
|
129832
130074
|
const snap = baselineEvalsPath(stateDir, slug);
|
|
129833
|
-
|
|
130075
|
+
mkdirSync70(dirname53(snap), { recursive: true });
|
|
129834
130076
|
copyFileSync15(evalsJsonPath(skillDir), snap);
|
|
129835
130077
|
}
|
|
129836
130078
|
|
|
@@ -129850,13 +130092,13 @@ function ownsSkill(skillDir) {
|
|
|
129850
130092
|
// src/self-improve/eval-case-proposals.ts
|
|
129851
130093
|
import {
|
|
129852
130094
|
closeSync as closeSync25,
|
|
129853
|
-
existsSync as
|
|
129854
|
-
mkdirSync as
|
|
130095
|
+
existsSync as existsSync121,
|
|
130096
|
+
mkdirSync as mkdirSync71,
|
|
129855
130097
|
openSync as openSync25,
|
|
129856
|
-
readFileSync as
|
|
129857
|
-
renameSync as
|
|
130098
|
+
readFileSync as readFileSync109,
|
|
130099
|
+
renameSync as renameSync35,
|
|
129858
130100
|
unlinkSync as unlinkSync24,
|
|
129859
|
-
writeFileSync as
|
|
130101
|
+
writeFileSync as writeFileSync53,
|
|
129860
130102
|
writeSync as writeSync13
|
|
129861
130103
|
} from "node:fs";
|
|
129862
130104
|
import { join as join124 } from "node:path";
|
|
@@ -129869,11 +130111,11 @@ function isRecord2(v) {
|
|
|
129869
130111
|
}
|
|
129870
130112
|
function readEvalCaseProposals(stateDir) {
|
|
129871
130113
|
const p = proposalsPath(stateDir);
|
|
129872
|
-
if (!
|
|
130114
|
+
if (!existsSync121(p))
|
|
129873
130115
|
return [];
|
|
129874
130116
|
let raw;
|
|
129875
130117
|
try {
|
|
129876
|
-
raw =
|
|
130118
|
+
raw = readFileSync109(p, "utf-8");
|
|
129877
130119
|
} catch {
|
|
129878
130120
|
return [];
|
|
129879
130121
|
}
|
|
@@ -129972,7 +130214,7 @@ function buildCaseFromOpts(opts) {
|
|
|
129972
130214
|
let raw;
|
|
129973
130215
|
if (opts.case) {
|
|
129974
130216
|
try {
|
|
129975
|
-
raw = JSON.parse(
|
|
130217
|
+
raw = JSON.parse(readFileSync110(opts.case, "utf-8"));
|
|
129976
130218
|
} catch (e) {
|
|
129977
130219
|
fail7(`failed to read/parse --case: ${e.message}`);
|
|
129978
130220
|
}
|
|
@@ -130066,7 +130308,7 @@ function registerApply(parent) {
|
|
|
130066
130308
|
}));
|
|
130067
130309
|
process.exit(0);
|
|
130068
130310
|
}
|
|
130069
|
-
if (!
|
|
130311
|
+
if (!existsSync122(proposal.skill_dir)) {
|
|
130070
130312
|
fail7(`skill dir gone: ${proposal.skill_dir}`);
|
|
130071
130313
|
}
|
|
130072
130314
|
const { dir, resolvedDir } = authorizeApplyTarget(proposal.skill_dir, proposal.skill_slug);
|
|
@@ -130104,7 +130346,7 @@ function registerSelfImproveEvalCaseCommands(program3) {
|
|
|
130104
130346
|
|
|
130105
130347
|
// src/cli/self-improve-bench.ts
|
|
130106
130348
|
import { homedir as homedir64 } from "node:os";
|
|
130107
|
-
import { dirname as
|
|
130349
|
+
import { dirname as dirname54, join as join126 } from "node:path";
|
|
130108
130350
|
init_shipped_assets();
|
|
130109
130351
|
function fail8(msg, code = 1) {
|
|
130110
130352
|
console.error(msg);
|
|
@@ -130129,7 +130371,7 @@ function resolveAggregatorRepoRoot() {
|
|
|
130129
130371
|
`);
|
|
130130
130372
|
return null;
|
|
130131
130373
|
}
|
|
130132
|
-
return
|
|
130374
|
+
return dirname54(res.path);
|
|
130133
130375
|
}
|
|
130134
130376
|
function registerSelfImproveBenchCommand(program3) {
|
|
130135
130377
|
const parent = program3.commands.find((c) => c.name() === "self-improve") ?? program3.command("self-improve").description("Agent self-improvement ops");
|
|
@@ -130179,7 +130421,7 @@ function registerSelfImproveBenchCommand(program3) {
|
|
|
130179
130421
|
init_esm();
|
|
130180
130422
|
init_helpers();
|
|
130181
130423
|
var import_yaml26 = __toESM(require_dist(), 1);
|
|
130182
|
-
import { existsSync as
|
|
130424
|
+
import { existsSync as existsSync123, readdirSync as readdirSync47, readFileSync as readFileSync111, statSync as statSync72 } from "node:fs";
|
|
130183
130425
|
import { homedir as homedir65 } from "node:os";
|
|
130184
130426
|
import { join as join127, resolve as resolve64 } from "node:path";
|
|
130185
130427
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -130196,11 +130438,11 @@ function defaultBundledRoot2() {
|
|
|
130196
130438
|
}
|
|
130197
130439
|
function readSkillFrontmatter(skillDir) {
|
|
130198
130440
|
const mdPath = join127(skillDir, "SKILL.md");
|
|
130199
|
-
if (!
|
|
130441
|
+
if (!existsSync123(mdPath))
|
|
130200
130442
|
return null;
|
|
130201
130443
|
let content;
|
|
130202
130444
|
try {
|
|
130203
|
-
content =
|
|
130445
|
+
content = readFileSync111(mdPath, "utf-8");
|
|
130204
130446
|
} catch {
|
|
130205
130447
|
return null;
|
|
130206
130448
|
}
|
|
@@ -130230,7 +130472,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
130230
130472
|
function statSkillMd(skillDir) {
|
|
130231
130473
|
const mdPath = join127(skillDir, "SKILL.md");
|
|
130232
130474
|
try {
|
|
130233
|
-
const st =
|
|
130475
|
+
const st = statSync72(mdPath);
|
|
130234
130476
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
130235
130477
|
} catch {
|
|
130236
130478
|
return null;
|
|
@@ -130240,7 +130482,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
130240
130482
|
if (!AGENT_NAME_RE3.test(agent))
|
|
130241
130483
|
return [];
|
|
130242
130484
|
const skillsDir = join127(agentsRoot, agent, ".claude/skills");
|
|
130243
|
-
if (!
|
|
130485
|
+
if (!existsSync123(skillsDir))
|
|
130244
130486
|
return [];
|
|
130245
130487
|
const out = [];
|
|
130246
130488
|
let entries;
|
|
@@ -130254,7 +130496,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
130254
130496
|
continue;
|
|
130255
130497
|
const dirPath = join127(skillsDir, ent);
|
|
130256
130498
|
try {
|
|
130257
|
-
if (!
|
|
130499
|
+
if (!statSync72(dirPath).isDirectory())
|
|
130258
130500
|
continue;
|
|
130259
130501
|
} catch {
|
|
130260
130502
|
continue;
|
|
@@ -130278,7 +130520,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
130278
130520
|
return out;
|
|
130279
130521
|
}
|
|
130280
130522
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
130281
|
-
if (!
|
|
130523
|
+
if (!existsSync123(sharedRoot))
|
|
130282
130524
|
return [];
|
|
130283
130525
|
const out = [];
|
|
130284
130526
|
let entries;
|
|
@@ -130294,7 +130536,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
130294
130536
|
continue;
|
|
130295
130537
|
const dirPath = join127(sharedRoot, ent);
|
|
130296
130538
|
try {
|
|
130297
|
-
if (!
|
|
130539
|
+
if (!statSync72(dirPath).isDirectory())
|
|
130298
130540
|
continue;
|
|
130299
130541
|
} catch {
|
|
130300
130542
|
continue;
|
|
@@ -130316,7 +130558,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
130316
130558
|
return out;
|
|
130317
130559
|
}
|
|
130318
130560
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
130319
|
-
if (!
|
|
130561
|
+
if (!existsSync123(bundledRoot))
|
|
130320
130562
|
return [];
|
|
130321
130563
|
const out = [];
|
|
130322
130564
|
let entries;
|
|
@@ -130330,7 +130572,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
130330
130572
|
continue;
|
|
130331
130573
|
const dirPath = join127(bundledRoot, ent);
|
|
130332
130574
|
try {
|
|
130333
|
-
if (!
|
|
130575
|
+
if (!statSync72(dirPath).isDirectory())
|
|
130334
130576
|
continue;
|
|
130335
130577
|
} catch {
|
|
130336
130578
|
continue;
|
|
@@ -130475,11 +130717,11 @@ init_helpers();
|
|
|
130475
130717
|
init_docker_socket();
|
|
130476
130718
|
init_operator_uid();
|
|
130477
130719
|
import {
|
|
130478
|
-
existsSync as
|
|
130479
|
-
mkdirSync as
|
|
130720
|
+
existsSync as existsSync125,
|
|
130721
|
+
mkdirSync as mkdirSync72,
|
|
130480
130722
|
readdirSync as readdirSync48,
|
|
130481
|
-
writeFileSync as
|
|
130482
|
-
statSync as
|
|
130723
|
+
writeFileSync as writeFileSync54,
|
|
130724
|
+
statSync as statSync73,
|
|
130483
130725
|
lstatSync as lstatSync18,
|
|
130484
130726
|
realpathSync as realpathSync12,
|
|
130485
130727
|
copyFileSync as copyFileSync16
|
|
@@ -130858,7 +131100,7 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
130858
131100
|
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.`);
|
|
130859
131101
|
return;
|
|
130860
131102
|
}
|
|
130861
|
-
if (!
|
|
131103
|
+
if (!existsSync125(target)) {
|
|
130862
131104
|
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.`);
|
|
130863
131105
|
return;
|
|
130864
131106
|
}
|
|
@@ -130872,7 +131114,7 @@ function hostdComposePath() {
|
|
|
130872
131114
|
}
|
|
130873
131115
|
function backupExistingCompose() {
|
|
130874
131116
|
const p = hostdComposePath();
|
|
130875
|
-
if (!
|
|
131117
|
+
if (!existsSync125(p))
|
|
130876
131118
|
return null;
|
|
130877
131119
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
130878
131120
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -130905,7 +131147,7 @@ async function doInstall(opts, program3) {
|
|
|
130905
131147
|
}
|
|
130906
131148
|
const dir = hostdDir();
|
|
130907
131149
|
const composePath = hostdComposePath();
|
|
130908
|
-
|
|
131150
|
+
mkdirSync72(dir, { recursive: true });
|
|
130909
131151
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
130910
131152
|
const guard = checkDowngrade({
|
|
130911
131153
|
container: "switchroom-hostd",
|
|
@@ -130939,7 +131181,7 @@ async function doInstall(opts, program3) {
|
|
|
130939
131181
|
const bak = backupExistingCompose();
|
|
130940
131182
|
if (bak)
|
|
130941
131183
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
130942
|
-
|
|
131184
|
+
writeFileSync54(composePath, yaml, "utf8");
|
|
130943
131185
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
130944
131186
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
130945
131187
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -130971,7 +131213,7 @@ function doStatus() {
|
|
|
130971
131213
|
const composeYml = hostdComposePath();
|
|
130972
131214
|
console.log(source_default.bold("switchroom-hostd"));
|
|
130973
131215
|
console.log("");
|
|
130974
|
-
if (!
|
|
131216
|
+
if (!existsSync125(composeYml)) {
|
|
130975
131217
|
console.log(source_default.yellow(" compose: not installed"));
|
|
130976
131218
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
130977
131219
|
return;
|
|
@@ -130992,15 +131234,15 @@ function doStatus() {
|
|
|
130992
131234
|
} else {
|
|
130993
131235
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
130994
131236
|
}
|
|
130995
|
-
if (
|
|
131237
|
+
if (existsSync125(dir)) {
|
|
130996
131238
|
const entries = [];
|
|
130997
131239
|
try {
|
|
130998
131240
|
for (const name of readdirSync48(dir)) {
|
|
130999
131241
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
131000
131242
|
continue;
|
|
131001
131243
|
const sockPath = join128(dir, name, "sock");
|
|
131002
|
-
if (
|
|
131003
|
-
const st =
|
|
131244
|
+
if (existsSync125(sockPath)) {
|
|
131245
|
+
const st = statSync73(sockPath);
|
|
131004
131246
|
if ((st.mode & 61440) === 49152) {
|
|
131005
131247
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
131006
131248
|
}
|
|
@@ -131018,7 +131260,7 @@ function doStatus() {
|
|
|
131018
131260
|
}
|
|
131019
131261
|
function doUninstall() {
|
|
131020
131262
|
const composeYml = hostdComposePath();
|
|
131021
|
-
if (!
|
|
131263
|
+
if (!existsSync125(composeYml)) {
|
|
131022
131264
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
131023
131265
|
return;
|
|
131024
131266
|
}
|
|
@@ -131042,7 +131284,7 @@ function registerHostdCommand(program3) {
|
|
|
131042
131284
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
131043
131285
|
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) => {
|
|
131044
131286
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
131045
|
-
if (!
|
|
131287
|
+
if (!existsSync125(logPath)) {
|
|
131046
131288
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
131047
131289
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
131048
131290
|
return;
|
|
@@ -131091,7 +131333,7 @@ init_source();
|
|
|
131091
131333
|
init_helpers();
|
|
131092
131334
|
init_operator_uid();
|
|
131093
131335
|
init_compose();
|
|
131094
|
-
import { chownSync as
|
|
131336
|
+
import { chownSync as chownSync16, existsSync as existsSync126, mkdirSync as mkdirSync73, writeFileSync as writeFileSync55, copyFileSync as copyFileSync17 } from "node:fs";
|
|
131095
131337
|
import { homedir as homedir67 } from "node:os";
|
|
131096
131338
|
import { join as join129 } from "node:path";
|
|
131097
131339
|
import { spawnSync as spawnSync30 } from "node:child_process";
|
|
@@ -131202,7 +131444,7 @@ function webdComposePath() {
|
|
|
131202
131444
|
}
|
|
131203
131445
|
function backupExistingCompose2() {
|
|
131204
131446
|
const p = webdComposePath();
|
|
131205
|
-
if (!
|
|
131447
|
+
if (!existsSync126(p))
|
|
131206
131448
|
return null;
|
|
131207
131449
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
131208
131450
|
const bak = `${p}.bak-${ts}`;
|
|
@@ -131233,7 +131475,7 @@ async function doInstall2(opts, program3) {
|
|
|
131233
131475
|
}
|
|
131234
131476
|
const dir = webdDir();
|
|
131235
131477
|
const composePath = webdComposePath();
|
|
131236
|
-
|
|
131478
|
+
mkdirSync73(dir, { recursive: true });
|
|
131237
131479
|
const cfg = getConfig(program3);
|
|
131238
131480
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
131239
131481
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -131268,13 +131510,13 @@ async function doInstall2(opts, program3) {
|
|
|
131268
131510
|
const bak = backupExistingCompose2();
|
|
131269
131511
|
if (bak)
|
|
131270
131512
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
131271
|
-
|
|
131513
|
+
writeFileSync55(composePath, yaml, "utf8");
|
|
131272
131514
|
try {
|
|
131273
131515
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
131274
|
-
|
|
131275
|
-
|
|
131516
|
+
chownSync16(dir, operatorUid, operatorUid);
|
|
131517
|
+
chownSync16(composePath, operatorUid, operatorUid);
|
|
131276
131518
|
if (bak)
|
|
131277
|
-
|
|
131519
|
+
chownSync16(bak, operatorUid, operatorUid);
|
|
131278
131520
|
}
|
|
131279
131521
|
} catch {}
|
|
131280
131522
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
@@ -131310,7 +131552,7 @@ function doStatus2() {
|
|
|
131310
131552
|
const composeYml = webdComposePath();
|
|
131311
131553
|
console.log(source_default.bold("switchroom-web"));
|
|
131312
131554
|
console.log("");
|
|
131313
|
-
if (!
|
|
131555
|
+
if (!existsSync126(composeYml)) {
|
|
131314
131556
|
console.log(source_default.yellow(" compose: not installed"));
|
|
131315
131557
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
131316
131558
|
return;
|
|
@@ -131334,7 +131576,7 @@ function doStatus2() {
|
|
|
131334
131576
|
}
|
|
131335
131577
|
function doUninstall2() {
|
|
131336
131578
|
const composeYml = webdComposePath();
|
|
131337
|
-
if (!
|
|
131579
|
+
if (!existsSync126(composeYml)) {
|
|
131338
131580
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
131339
131581
|
return;
|
|
131340
131582
|
}
|
|
@@ -131830,7 +132072,7 @@ function printDeepDiveBrief(targets) {
|
|
|
131830
132072
|
|
|
131831
132073
|
// src/cli/hindsight-watch.ts
|
|
131832
132074
|
init_source();
|
|
131833
|
-
import { existsSync as
|
|
132075
|
+
import { existsSync as existsSync131, readdirSync as readdirSync53 } from "node:fs";
|
|
131834
132076
|
import { homedir as homedir71 } from "node:os";
|
|
131835
132077
|
import { join as join133, resolve as resolve67 } from "node:path";
|
|
131836
132078
|
|
|
@@ -131901,7 +132143,7 @@ init_install_cron();
|
|
|
131901
132143
|
|
|
131902
132144
|
// src/hindsight-watch/probe.ts
|
|
131903
132145
|
import { spawnSync as spawnSync32 } from "node:child_process";
|
|
131904
|
-
import { readFileSync as
|
|
132146
|
+
import { readFileSync as readFileSync116, readdirSync as readdirSync52, statSync as statSync75 } from "node:fs";
|
|
131905
132147
|
import { homedir as homedir70 } from "node:os";
|
|
131906
132148
|
import { resolve as resolve66 } from "node:path";
|
|
131907
132149
|
|
|
@@ -132029,7 +132271,7 @@ function readLlmSignals(series) {
|
|
|
132029
132271
|
}
|
|
132030
132272
|
|
|
132031
132273
|
// src/hindsight-watch/recall-log.ts
|
|
132032
|
-
import { closeSync as closeSync26, existsSync as
|
|
132274
|
+
import { closeSync as closeSync26, existsSync as existsSync130, openSync as openSync26, readdirSync as readdirSync51, readSync as readSync7, statSync as statSync74 } from "node:fs";
|
|
132033
132275
|
import { join as join132 } from "node:path";
|
|
132034
132276
|
var RECALL_WINDOW_ROWS = 200;
|
|
132035
132277
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
@@ -132038,13 +132280,13 @@ function recallLogPath2(agentDir) {
|
|
|
132038
132280
|
return join132(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
132039
132281
|
}
|
|
132040
132282
|
function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
132041
|
-
if (!
|
|
132283
|
+
if (!existsSync130(path10))
|
|
132042
132284
|
return [];
|
|
132043
132285
|
let text;
|
|
132044
132286
|
let truncatedHead = false;
|
|
132045
132287
|
let fd;
|
|
132046
132288
|
try {
|
|
132047
|
-
const size =
|
|
132289
|
+
const size = statSync74(path10).size;
|
|
132048
132290
|
const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
|
|
132049
132291
|
truncatedHead = start > 0;
|
|
132050
132292
|
const length = size - start;
|
|
@@ -132292,9 +132534,9 @@ function readDropCount(hindsightDir) {
|
|
|
132292
132534
|
const path10 = resolve66(hindsightDir, "pending-drops.json");
|
|
132293
132535
|
let parsed;
|
|
132294
132536
|
try {
|
|
132295
|
-
if (
|
|
132537
|
+
if (statSync75(path10).size > DROPS_LEDGER_MAX_BYTES)
|
|
132296
132538
|
return 0;
|
|
132297
|
-
parsed = JSON.parse(
|
|
132539
|
+
parsed = JSON.parse(readFileSync116(path10, "utf8"));
|
|
132298
132540
|
} catch {
|
|
132299
132541
|
return 0;
|
|
132300
132542
|
}
|
|
@@ -133598,7 +133840,7 @@ function collectRelayCandidates(agentsDir) {
|
|
|
133598
133840
|
const candidates = [];
|
|
133599
133841
|
for (const name of readdirSync53(agentsDir).sort()) {
|
|
133600
133842
|
const sock = resolve67(agentsDir, name, "telegram", "gateway.sock");
|
|
133601
|
-
if (
|
|
133843
|
+
if (existsSync131(sock))
|
|
133602
133844
|
candidates.push({ agent: name, sock });
|
|
133603
133845
|
}
|
|
133604
133846
|
return orderRelayCandidates(candidates);
|
|
@@ -133618,10 +133860,276 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
133618
133860
|
return agent !== null;
|
|
133619
133861
|
}
|
|
133620
133862
|
|
|
133863
|
+
// src/cli/mental-model-refresh.ts
|
|
133864
|
+
init_source();
|
|
133865
|
+
init_helpers();
|
|
133866
|
+
init_hindsight2();
|
|
133867
|
+
init_hindsight();
|
|
133868
|
+
|
|
133869
|
+
// src/memory/mental-model-refresh.ts
|
|
133870
|
+
init_hindsight2();
|
|
133871
|
+
var DEFAULT_STALE_DAYS = 7;
|
|
133872
|
+
var DEFAULT_REFRESH_TIMEOUT_MS = 30000;
|
|
133873
|
+
var INIT_TIMEOUT_MS = 3000;
|
|
133874
|
+
function parseMcpBody(text) {
|
|
133875
|
+
const dataLine = text.split(`
|
|
133876
|
+
`).find((line) => line.startsWith("data: "));
|
|
133877
|
+
const payload = dataLine ? dataLine.slice("data: ".length) : text;
|
|
133878
|
+
return JSON.parse(payload);
|
|
133879
|
+
}
|
|
133880
|
+
async function refreshMentalModel2(mcpUrl, bankId, modelId, opts) {
|
|
133881
|
+
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
133882
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_REFRESH_TIMEOUT_MS;
|
|
133883
|
+
const headers = {
|
|
133884
|
+
"Content-Type": "application/json",
|
|
133885
|
+
Accept: "application/json, text/event-stream",
|
|
133886
|
+
"X-Bank-Id": bankId
|
|
133887
|
+
};
|
|
133888
|
+
try {
|
|
133889
|
+
await fetchImpl(mcpUrl, {
|
|
133890
|
+
method: "POST",
|
|
133891
|
+
headers,
|
|
133892
|
+
body: JSON.stringify({
|
|
133893
|
+
jsonrpc: "2.0",
|
|
133894
|
+
id: 1,
|
|
133895
|
+
method: "initialize",
|
|
133896
|
+
params: {
|
|
133897
|
+
protocolVersion: "2024-11-05",
|
|
133898
|
+
capabilities: {},
|
|
133899
|
+
clientInfo: { name: "mm-refresh", version: "0.1" }
|
|
133900
|
+
}
|
|
133901
|
+
}),
|
|
133902
|
+
signal: AbortSignal.timeout(INIT_TIMEOUT_MS)
|
|
133903
|
+
});
|
|
133904
|
+
} catch {}
|
|
133905
|
+
const controller = new AbortController;
|
|
133906
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
133907
|
+
try {
|
|
133908
|
+
const resp = await fetchImpl(mcpUrl, {
|
|
133909
|
+
method: "POST",
|
|
133910
|
+
headers,
|
|
133911
|
+
body: JSON.stringify({
|
|
133912
|
+
jsonrpc: "2.0",
|
|
133913
|
+
id: 2,
|
|
133914
|
+
method: "tools/call",
|
|
133915
|
+
params: {
|
|
133916
|
+
name: "refresh_mental_model",
|
|
133917
|
+
arguments: { mental_model_id: modelId }
|
|
133918
|
+
}
|
|
133919
|
+
}),
|
|
133920
|
+
signal: controller.signal
|
|
133921
|
+
});
|
|
133922
|
+
if (!resp.ok)
|
|
133923
|
+
return { ok: false, reason: `HTTP ${resp.status}` };
|
|
133924
|
+
const parsed = parseMcpBody(await resp.text());
|
|
133925
|
+
if (parsed.result?.isError === true) {
|
|
133926
|
+
return {
|
|
133927
|
+
ok: false,
|
|
133928
|
+
reason: parsed.result.content?.[0]?.text ?? "refresh returned isError"
|
|
133929
|
+
};
|
|
133930
|
+
}
|
|
133931
|
+
return { ok: true };
|
|
133932
|
+
} catch (err2) {
|
|
133933
|
+
if (err2.name === "AbortError")
|
|
133934
|
+
return { ok: false, reason: "Timeout" };
|
|
133935
|
+
return { ok: false, reason: String(err2.message ?? err2) };
|
|
133936
|
+
} finally {
|
|
133937
|
+
clearTimeout(timeout);
|
|
133938
|
+
}
|
|
133939
|
+
}
|
|
133940
|
+
async function refreshStaleModelsInBank(mcpUrl, bankId, opts) {
|
|
133941
|
+
const staleDays = opts?.staleDays ?? DEFAULT_STALE_DAYS;
|
|
133942
|
+
const now2 = opts?.now ?? new Date;
|
|
133943
|
+
const dryRun = opts?.dryRun ?? false;
|
|
133944
|
+
const health = await inspectBankHealth(mcpUrl, bankId, {
|
|
133945
|
+
fetchImpl: opts?.fetchImpl,
|
|
133946
|
+
timeoutMs: opts?.timeoutMs
|
|
133947
|
+
});
|
|
133948
|
+
if (!health.ok) {
|
|
133949
|
+
return {
|
|
133950
|
+
bankId,
|
|
133951
|
+
ok: false,
|
|
133952
|
+
reason: health.reason,
|
|
133953
|
+
totalModels: 0,
|
|
133954
|
+
staleModels: 0,
|
|
133955
|
+
refreshed: 0,
|
|
133956
|
+
failed: 0,
|
|
133957
|
+
dryRun,
|
|
133958
|
+
models: []
|
|
133959
|
+
};
|
|
133960
|
+
}
|
|
133961
|
+
const stale = staleMentalModels(health.mentalModels, staleDays, now2);
|
|
133962
|
+
const models = [];
|
|
133963
|
+
let refreshed = 0;
|
|
133964
|
+
let failed = 0;
|
|
133965
|
+
for (const m of stale) {
|
|
133966
|
+
const age = ageDays(m.lastRefreshedAt ?? m.createdAt, now2);
|
|
133967
|
+
if (dryRun) {
|
|
133968
|
+
models.push({ id: m.id, name: m.name, ageDays: age, refreshed: false });
|
|
133969
|
+
continue;
|
|
133970
|
+
}
|
|
133971
|
+
const res = await refreshMentalModel2(mcpUrl, bankId, m.id, {
|
|
133972
|
+
fetchImpl: opts?.fetchImpl,
|
|
133973
|
+
timeoutMs: opts?.timeoutMs
|
|
133974
|
+
});
|
|
133975
|
+
if (res.ok) {
|
|
133976
|
+
refreshed++;
|
|
133977
|
+
models.push({ id: m.id, name: m.name, ageDays: age, refreshed: true });
|
|
133978
|
+
} else {
|
|
133979
|
+
failed++;
|
|
133980
|
+
models.push({ id: m.id, name: m.name, ageDays: age, refreshed: false, reason: res.reason });
|
|
133981
|
+
}
|
|
133982
|
+
}
|
|
133983
|
+
return {
|
|
133984
|
+
bankId,
|
|
133985
|
+
ok: true,
|
|
133986
|
+
totalModels: health.mentalModels.length,
|
|
133987
|
+
staleModels: stale.length,
|
|
133988
|
+
refreshed,
|
|
133989
|
+
failed,
|
|
133990
|
+
dryRun,
|
|
133991
|
+
models
|
|
133992
|
+
};
|
|
133993
|
+
}
|
|
133994
|
+
function collectRefreshBanks(config2) {
|
|
133995
|
+
const banks = new Set;
|
|
133996
|
+
for (const [agentName, agentConfig] of Object.entries(config2.agents)) {
|
|
133997
|
+
banks.add(agentConfig.memory?.collection ?? agentName);
|
|
133998
|
+
}
|
|
133999
|
+
for (const bank of collectProfileBanks(config2))
|
|
134000
|
+
banks.add(bank);
|
|
134001
|
+
return [...banks].sort();
|
|
134002
|
+
}
|
|
134003
|
+
async function runMentalModelRefresh(opts) {
|
|
134004
|
+
const banks = [];
|
|
134005
|
+
let totalRefreshed = 0;
|
|
134006
|
+
let totalFailed = 0;
|
|
134007
|
+
for (const bankId of opts.bankIds) {
|
|
134008
|
+
const r = await refreshStaleModelsInBank(opts.mcpUrl, bankId, {
|
|
134009
|
+
staleDays: opts.staleDays,
|
|
134010
|
+
now: opts.now,
|
|
134011
|
+
dryRun: opts.dryRun,
|
|
134012
|
+
fetchImpl: opts.fetchImpl,
|
|
134013
|
+
timeoutMs: opts.timeoutMs
|
|
134014
|
+
});
|
|
134015
|
+
banks.push(r);
|
|
134016
|
+
totalRefreshed += r.refreshed;
|
|
134017
|
+
totalFailed += r.failed;
|
|
134018
|
+
if (opts.log) {
|
|
134019
|
+
opts.log(r.ok ? `${r.bankId}: ${r.staleModels}/${r.totalModels} stale, ` + `${r.dryRun ? "would refresh" : "refreshed"} ${r.dryRun ? r.staleModels : r.refreshed}` + (r.failed > 0 ? `, ${r.failed} failed` : "") : `${r.bankId}: could not inspect (${r.reason})`);
|
|
134020
|
+
}
|
|
134021
|
+
}
|
|
134022
|
+
const couldNotComplete = banks.length > 0 && banks.every((b) => !b.ok);
|
|
134023
|
+
return { banks, totalRefreshed, totalFailed, couldNotComplete };
|
|
134024
|
+
}
|
|
134025
|
+
|
|
134026
|
+
// src/cli/mental-model-refresh.ts
|
|
134027
|
+
function registerMentalModelRefreshCommand(program3) {
|
|
134028
|
+
program3.command("mental-model-refresh").description("Model-free refresh of stale mental models: list each bank's models, " + "select those past the staleness interval, refresh via the hindsight MCP tool.").option("--install-cron", `arm the sweep: write ${CRON_PATH3} (${CRON_SCHEDULE2}, flock-guarded) and exit`, false).option("--cron-user <user>", "unix user the cron sweep runs as (default: current user)").option("--stale-days <n>", `refresh models whose last refresh is older than N days (default: ${DEFAULT_STALE_DAYS})`, String(DEFAULT_STALE_DAYS)).option("--bank <id>", "restrict the sweep to a single bank (default: every fleet bank)").option("--mcp-url <url>", "hindsight MCP endpoint (default: config or built-in)").option("--dry-run", "select and print stale models but issue no refreshes", false).option("--json", "emit machine-readable JSON", false).action(async (opts) => {
|
|
134029
|
+
if (opts.installCron) {
|
|
134030
|
+
process.exitCode = runInstallCron2(opts.cronUser);
|
|
134031
|
+
return;
|
|
134032
|
+
}
|
|
134033
|
+
const staleDays = Number(opts.staleDays);
|
|
134034
|
+
if (!Number.isFinite(staleDays) || staleDays < 0) {
|
|
134035
|
+
process.stderr.write(source_default.red(`mental-model-refresh: --stale-days must be a non-negative number (got ${opts.staleDays})
|
|
134036
|
+
`));
|
|
134037
|
+
process.exitCode = 1;
|
|
134038
|
+
return;
|
|
134039
|
+
}
|
|
134040
|
+
const config2 = getConfig(program3);
|
|
134041
|
+
if (!isHindsightEnabled(config2)) {
|
|
134042
|
+
process.stderr.write(source_default.red(`mental-model-refresh: hindsight memory backend is not enabled \u2014 nothing to refresh.
|
|
134043
|
+
`));
|
|
134044
|
+
process.exitCode = 1;
|
|
134045
|
+
return;
|
|
134046
|
+
}
|
|
134047
|
+
const mcpUrl = opts.mcpUrl ?? config2.memory?.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
134048
|
+
const bankIds = opts.bank ? [String(opts.bank)] : collectRefreshBanks(config2);
|
|
134049
|
+
if (bankIds.length === 0) {
|
|
134050
|
+
process.stderr.write(source_default.yellow(`mental-model-refresh: no banks to sweep.
|
|
134051
|
+
`));
|
|
134052
|
+
process.exitCode = 0;
|
|
134053
|
+
return;
|
|
134054
|
+
}
|
|
134055
|
+
const log = opts.json ? undefined : (m) => {
|
|
134056
|
+
process.stderr.write(`${m}
|
|
134057
|
+
`);
|
|
134058
|
+
};
|
|
134059
|
+
const result = await runMentalModelRefresh({
|
|
134060
|
+
mcpUrl,
|
|
134061
|
+
bankIds,
|
|
134062
|
+
staleDays,
|
|
134063
|
+
dryRun: Boolean(opts.dryRun),
|
|
134064
|
+
log
|
|
134065
|
+
});
|
|
134066
|
+
if (opts.json) {
|
|
134067
|
+
process.stdout.write(JSON.stringify({ staleDays, dryRun: Boolean(opts.dryRun), ...result }, null, 2) + `
|
|
134068
|
+
`);
|
|
134069
|
+
} else {
|
|
134070
|
+
const verb = opts.dryRun ? "would refresh" : "refreshed";
|
|
134071
|
+
process.stdout.write(`
|
|
134072
|
+
${source_default.bold(`${verb} ${result.totalRefreshed} model(s)`)}` + (result.totalFailed > 0 ? source_default.yellow(` \u2014 ${result.totalFailed} refresh failure(s)`) : "") + ` across ${result.banks.length} bank(s).
|
|
134073
|
+
`);
|
|
134074
|
+
if (result.couldNotComplete) {
|
|
134075
|
+
process.stdout.write(source_default.red(` every bank failed inspection \u2014 hindsight unreachable?
|
|
134076
|
+
`));
|
|
134077
|
+
}
|
|
134078
|
+
}
|
|
134079
|
+
process.exitCode = result.couldNotComplete ? 1 : 0;
|
|
134080
|
+
});
|
|
134081
|
+
}
|
|
134082
|
+
function runInstallCron2(cronUser) {
|
|
134083
|
+
const user = cronUser ?? process.env.SUDO_USER ?? process.env.USER ?? process.env.LOGNAME;
|
|
134084
|
+
if (!user || user === "root") {
|
|
134085
|
+
process.stderr.write(source_default.red("mental-model-refresh: refusing to install a cron for `root`.\n") + " The config the sweep reads lives under the OPERATOR's ~/.switchroom, " + `so a root tick would read the wrong config.
|
|
134086
|
+
` + " Re-run with `--cron-user <operator>`.\n");
|
|
134087
|
+
return 1;
|
|
134088
|
+
}
|
|
134089
|
+
const binary = process.env.SWITCHROOM_BINARY ?? "/usr/local/bin/switchroom";
|
|
134090
|
+
if (!binary.startsWith("/")) {
|
|
134091
|
+
process.stderr.write(source_default.red(`mental-model-refresh: SWITCHROOM_BINARY must be an absolute path (got ${binary})
|
|
134092
|
+
`));
|
|
134093
|
+
return 1;
|
|
134094
|
+
}
|
|
134095
|
+
let res;
|
|
134096
|
+
try {
|
|
134097
|
+
res = installCron3({ user, binary });
|
|
134098
|
+
} catch (e) {
|
|
134099
|
+
process.stderr.write(source_default.red(`mental-model-refresh: could not write ${CRON_PATH3}: ${e.message}
|
|
134100
|
+
`) + " This path needs root; re-run under sudo with `--cron-user <operator>`.\n");
|
|
134101
|
+
return 1;
|
|
134102
|
+
}
|
|
134103
|
+
let logStatus;
|
|
134104
|
+
try {
|
|
134105
|
+
const logRes = ensureLogFile3({ user });
|
|
134106
|
+
logStatus = logRes.status === "created" ? `created ${logRes.path} (owned by ${user})` : `${logRes.path} already present`;
|
|
134107
|
+
} catch (e) {
|
|
134108
|
+
process.stderr.write(source_default.red(`mental-model-refresh: could not provision the log file: ${e.message}
|
|
134109
|
+
`) + ` The cron redirects into ${CRON_LOG_PATH3} and would die at the redirection every tick.
|
|
134110
|
+
` + " This path needs root; re-run under sudo with `--cron-user <operator>`.\n");
|
|
134111
|
+
return 1;
|
|
134112
|
+
}
|
|
134113
|
+
let rotateStatus;
|
|
134114
|
+
try {
|
|
134115
|
+
const rotateRes = installLogrotate3({ user });
|
|
134116
|
+
rotateStatus = rotateRes.status === "installed" ? `installed ${rotateRes.path}` : `${rotateRes.path} already up to date`;
|
|
134117
|
+
} catch (e) {
|
|
134118
|
+
rotateStatus = source_default.yellow(`logrotate drop-in NOT written (${e.message}) \u2014 log growth is unbounded`);
|
|
134119
|
+
}
|
|
134120
|
+
const verb = res.status === "installed" ? "installed" : "already up to date";
|
|
134121
|
+
process.stdout.write(`${source_default.green("\u2713")} mental-model-refresh cron ${verb} at ${res.path} ` + `(${CRON_SCHEDULE2}, as ${user})
|
|
134122
|
+
` + ` log: ${logStatus}
|
|
134123
|
+
` + ` logrotate: ${rotateStatus}
|
|
134124
|
+
` + ` Verify with: switchroom mental-model-refresh --dry-run
|
|
134125
|
+
`);
|
|
134126
|
+
return 0;
|
|
134127
|
+
}
|
|
134128
|
+
|
|
133621
134129
|
// src/cli/hindsight-bench.ts
|
|
133622
134130
|
init_source();
|
|
133623
|
-
import { mkdirSync as
|
|
133624
|
-
import { dirname as
|
|
134131
|
+
import { mkdirSync as mkdirSync75, readFileSync as readFileSync117, writeFileSync as writeFileSync57 } from "node:fs";
|
|
134132
|
+
import { dirname as dirname57, resolve as resolve68 } from "node:path";
|
|
133625
134133
|
|
|
133626
134134
|
// src/hindsight-bench/anonymise.ts
|
|
133627
134135
|
function pseudonym(n, width) {
|
|
@@ -134731,7 +135239,7 @@ function fail9(msg) {
|
|
|
134731
135239
|
function readResult(path10) {
|
|
134732
135240
|
let parsed;
|
|
134733
135241
|
try {
|
|
134734
|
-
parsed = JSON.parse(
|
|
135242
|
+
parsed = JSON.parse(readFileSync117(path10, "utf8"));
|
|
134735
135243
|
} catch (e) {
|
|
134736
135244
|
fail9(`cannot read ${path10}: ${e.message}`);
|
|
134737
135245
|
}
|
|
@@ -134749,8 +135257,8 @@ function readResult(path10) {
|
|
|
134749
135257
|
}
|
|
134750
135258
|
function writeOut(path10, body) {
|
|
134751
135259
|
const abs = resolve68(path10);
|
|
134752
|
-
|
|
134753
|
-
|
|
135260
|
+
mkdirSync75(dirname57(abs), { recursive: true });
|
|
135261
|
+
writeFileSync57(abs, body);
|
|
134754
135262
|
process.stderr.write(`${source_default.green("\u2713")} wrote ${abs}
|
|
134755
135263
|
`);
|
|
134756
135264
|
}
|
|
@@ -134954,53 +135462,53 @@ async function runMeasureMode(opts) {
|
|
|
134954
135462
|
|
|
134955
135463
|
// src/cli/openrouter-watch.ts
|
|
134956
135464
|
init_source();
|
|
134957
|
-
import { existsSync as
|
|
135465
|
+
import { existsSync as existsSync133, readdirSync as readdirSync54 } from "node:fs";
|
|
134958
135466
|
import { homedir as homedir73 } from "node:os";
|
|
134959
135467
|
import { join as join134, resolve as resolve70 } from "node:path";
|
|
134960
135468
|
|
|
134961
135469
|
// src/openrouter/install-cron.ts
|
|
134962
|
-
import { existsSync as
|
|
134963
|
-
import { dirname as
|
|
134964
|
-
var
|
|
134965
|
-
var
|
|
134966
|
-
var
|
|
134967
|
-
var
|
|
134968
|
-
function
|
|
135470
|
+
import { existsSync as existsSync132, mkdirSync as mkdirSync76, readFileSync as readFileSync118, renameSync as renameSync36, writeFileSync as writeFileSync58 } from "node:fs";
|
|
135471
|
+
import { dirname as dirname58 } from "node:path";
|
|
135472
|
+
var CRON_PATH4 = "/etc/cron.d/openrouter-watch";
|
|
135473
|
+
var CRON_SCHEDULE3 = "17 * * * *";
|
|
135474
|
+
var CRON_LOG_PATH4 = "/var/log/openrouter-watch.log";
|
|
135475
|
+
var CRON_LOCK_PATH4 = "/run/lock/openrouter-watch.lock";
|
|
135476
|
+
function renderCron4(opts) {
|
|
134969
135477
|
return `# switchroom openrouter-watch \u2014 model-free OpenRouter credit watchdog.
|
|
134970
135478
|
` + `# Managed by \`switchroom openrouter-watch --install-cron\`; edits are overwritten.
|
|
134971
135479
|
` + `# Exit 0 = clean, 10 = credit signal firing (DM sent), 1 = the check could not complete.
|
|
134972
135480
|
` + `SHELL=/bin/sh
|
|
134973
135481
|
` + `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
134974
|
-
` + `${
|
|
135482
|
+
` + `${CRON_SCHEDULE3} ${opts.user} /usr/bin/flock -n ${CRON_LOCK_PATH4} ` + `${opts.binary} openrouter-watch >> ${CRON_LOG_PATH4} 2>&1
|
|
134975
135483
|
`;
|
|
134976
135484
|
}
|
|
134977
|
-
function
|
|
134978
|
-
const path10 = opts.path ??
|
|
134979
|
-
const content =
|
|
134980
|
-
if (
|
|
135485
|
+
function installCron4(opts) {
|
|
135486
|
+
const path10 = opts.path ?? CRON_PATH4;
|
|
135487
|
+
const content = renderCron4(opts);
|
|
135488
|
+
if (existsSync132(path10)) {
|
|
134981
135489
|
try {
|
|
134982
|
-
if (
|
|
135490
|
+
if (readFileSync118(path10, "utf8") === content) {
|
|
134983
135491
|
return { status: "unchanged", path: path10, content };
|
|
134984
135492
|
}
|
|
134985
135493
|
} catch {}
|
|
134986
135494
|
}
|
|
134987
|
-
|
|
135495
|
+
mkdirSync76(dirname58(path10), { recursive: true });
|
|
134988
135496
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
134989
|
-
|
|
134990
|
-
|
|
135497
|
+
writeFileSync58(tmp, content, { mode: 420 });
|
|
135498
|
+
renameSync36(tmp, path10);
|
|
134991
135499
|
return { status: "installed", path: path10, content };
|
|
134992
135500
|
}
|
|
134993
135501
|
// src/openrouter/state.ts
|
|
134994
|
-
import { mkdirSync as
|
|
135502
|
+
import { mkdirSync as mkdirSync77, readFileSync as readFileSync119, renameSync as renameSync37, writeFileSync as writeFileSync59 } from "node:fs";
|
|
134995
135503
|
import { homedir as homedir72 } from "node:os";
|
|
134996
|
-
import { dirname as
|
|
135504
|
+
import { dirname as dirname59, resolve as resolve69 } from "node:path";
|
|
134997
135505
|
function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir72()) {
|
|
134998
135506
|
return resolve69(home2, ".switchroom", "openrouter-watch", "state.json");
|
|
134999
135507
|
}
|
|
135000
135508
|
function loadState2(path10) {
|
|
135001
135509
|
let parsed;
|
|
135002
135510
|
try {
|
|
135003
|
-
parsed = JSON.parse(
|
|
135511
|
+
parsed = JSON.parse(readFileSync119(path10, "utf8"));
|
|
135004
135512
|
} catch {
|
|
135005
135513
|
return {};
|
|
135006
135514
|
}
|
|
@@ -135020,12 +135528,12 @@ function loadState2(path10) {
|
|
|
135020
135528
|
return out;
|
|
135021
135529
|
}
|
|
135022
135530
|
function saveState2(path10, state) {
|
|
135023
|
-
|
|
135531
|
+
mkdirSync77(dirname59(path10), { recursive: true, mode: 448 });
|
|
135024
135532
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
135025
135533
|
const payload = { v: 1, ...state };
|
|
135026
|
-
|
|
135534
|
+
writeFileSync59(tmp, JSON.stringify(payload, null, 2) + `
|
|
135027
135535
|
`, { mode: 384 });
|
|
135028
|
-
|
|
135536
|
+
renameSync37(tmp, path10);
|
|
135029
135537
|
}
|
|
135030
135538
|
|
|
135031
135539
|
// src/openrouter/watch.ts
|
|
@@ -135106,9 +135614,9 @@ async function tick2(deps) {
|
|
|
135106
135614
|
}
|
|
135107
135615
|
// src/cli/openrouter-watch.ts
|
|
135108
135616
|
function registerOpenRouterWatchCommand(program3) {
|
|
135109
|
-
program3.command("openrouter-watch").description("Model-free OpenRouter credit watchdog: warns before the key's credit runs out. " + "Alerts at most once per 6h, and escalates immediately.").option("--install-cron", `arm the watchdog: write ${
|
|
135617
|
+
program3.command("openrouter-watch").description("Model-free OpenRouter credit watchdog: warns before the key's credit runs out. " + "Alerts at most once per 6h, and escalates immediately.").option("--install-cron", `arm the watchdog: write ${CRON_PATH4} (${CRON_SCHEDULE3}, flock-guarded) and exit`, false).option("--cron-user <user>", "unix user the cron tick runs as (default: current user)").option("--dry-run", "evaluate and print; send no DM and write no state", false).option("--json", "emit machine-readable JSON", false).option("--state <path>", "state file path (default ~/.switchroom/openrouter-watch/state.json)").option("--agents-dir <path>", "agents scaffold dir (default ~/.switchroom/agents)").action(async (opts) => {
|
|
135110
135618
|
if (opts.installCron) {
|
|
135111
|
-
process.exitCode =
|
|
135619
|
+
process.exitCode = runInstallCron3(opts.cronUser);
|
|
135112
135620
|
return;
|
|
135113
135621
|
}
|
|
135114
135622
|
const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join134(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir73(), ".switchroom", "agents");
|
|
@@ -135178,7 +135686,7 @@ ${source_default.bold(`operator DM ${header}:`)}
|
|
|
135178
135686
|
process.exitCode = result.exitCode;
|
|
135179
135687
|
});
|
|
135180
135688
|
}
|
|
135181
|
-
function
|
|
135689
|
+
function runInstallCron3(cronUser) {
|
|
135182
135690
|
const user = cronUser ?? process.env.SUDO_USER ?? process.env.USER ?? process.env.LOGNAME;
|
|
135183
135691
|
if (!user || user === "root") {
|
|
135184
135692
|
process.stderr.write(source_default.red("openrouter-watch: refusing to install a cron for `root`.\n") + " The state file and the agents scaffold live under the OPERATOR's " + "~/.switchroom, so a root tick would keep its re-notify ledger somewhere " + `nothing else reads.
|
|
@@ -135193,14 +135701,14 @@ function runInstallCron2(cronUser) {
|
|
|
135193
135701
|
}
|
|
135194
135702
|
let res;
|
|
135195
135703
|
try {
|
|
135196
|
-
res =
|
|
135704
|
+
res = installCron4({ user, binary });
|
|
135197
135705
|
} catch (e) {
|
|
135198
|
-
process.stderr.write(source_default.red(`openrouter-watch: could not write ${
|
|
135706
|
+
process.stderr.write(source_default.red(`openrouter-watch: could not write ${CRON_PATH4}: ${e.message}
|
|
135199
135707
|
`) + " This path needs root; re-run under sudo with `--cron-user <operator>`.\n");
|
|
135200
135708
|
return 1;
|
|
135201
135709
|
}
|
|
135202
135710
|
const verb = res.status === "installed" ? "installed" : "already up to date";
|
|
135203
|
-
process.stdout.write(`${source_default.green("\u2713")} openrouter-watch cron ${verb} at ${res.path} ` + `(${
|
|
135711
|
+
process.stdout.write(`${source_default.green("\u2713")} openrouter-watch cron ${verb} at ${res.path} ` + `(${CRON_SCHEDULE3}, as ${user})
|
|
135204
135712
|
` + ` Verify with: switchroom openrouter-watch --dry-run
|
|
135205
135713
|
`);
|
|
135206
135714
|
return 0;
|
|
@@ -135210,7 +135718,7 @@ async function notifyOperator2(agentsDir, text, log) {
|
|
|
135210
135718
|
try {
|
|
135211
135719
|
for (const name of readdirSync54(agentsDir).sort()) {
|
|
135212
135720
|
const sock = resolve70(agentsDir, name, "telegram", "gateway.sock");
|
|
135213
|
-
if (
|
|
135721
|
+
if (existsSync133(sock))
|
|
135214
135722
|
candidates.push({ agent: name, sock });
|
|
135215
135723
|
}
|
|
135216
135724
|
} catch (e) {
|
|
@@ -135311,13 +135819,13 @@ function registerConfigRepoCommand(program3) {
|
|
|
135311
135819
|
});
|
|
135312
135820
|
configRepo.command("install-cron").description("Arm the scheduled auto-backup: write /etc/cron.d/switchroom-config-sync " + "(30-min `config-repo sync` tick + a daily `vault backup && sync` leg), " + "provision its log, and install a logrotate drop-in. Gated on " + "config_repo.enabled; refuses to run as root.").option("--cron-user <user>", "unix user the cron tick runs as (default: current user)").action((opts) => {
|
|
135313
135821
|
const parentOpts = program3.opts();
|
|
135314
|
-
process.exitCode =
|
|
135822
|
+
process.exitCode = runInstallCron4(parentOpts.config, opts.cronUser);
|
|
135315
135823
|
});
|
|
135316
135824
|
configRepo.command("uninstall-cron").description("Disarm the scheduled auto-backup: remove /etc/cron.d/switchroom-config-sync " + "and its logrotate drop-in (the sync verb still works on demand). Idempotent.").action(() => {
|
|
135317
135825
|
process.exitCode = runUninstallCron();
|
|
135318
135826
|
});
|
|
135319
135827
|
}
|
|
135320
|
-
function
|
|
135828
|
+
function runInstallCron4(configPath, cronUser) {
|
|
135321
135829
|
let intervalMinutes = DEFAULT_INTERVAL_MINUTES;
|
|
135322
135830
|
let includeVaultBackup = "daily";
|
|
135323
135831
|
let enabled = false;
|
|
@@ -135476,6 +135984,7 @@ registerWebdCommand(program3);
|
|
|
135476
135984
|
registerHostCommand(program3);
|
|
135477
135985
|
registerFleetHealthCommand(program3);
|
|
135478
135986
|
registerHindsightWatchCommand(program3);
|
|
135987
|
+
registerMentalModelRefreshCommand(program3);
|
|
135479
135988
|
registerHindsightBenchCommand(program3);
|
|
135480
135989
|
registerOpenRouterWatchCommand(program3);
|
|
135481
135990
|
registerConfigRepoCommand(program3);
|