switchroom 0.19.28 → 0.19.30
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 +12 -1
- package/dist/auth-broker/index.js +14 -2
- package/dist/cli/notion-write-pretool.mjs +14 -1
- package/dist/cli/switchroom.js +1041 -374
- package/dist/host-control/main.js +28 -10
- package/dist/vault/approvals/kernel-server.js +19 -3
- package/dist/vault/broker/server.js +19 -3
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +9 -0
- package/telegram-plugin/dist/gateway/gateway.js +23 -7
- package/vendor/hindsight-memory/CHANGELOG.md +50 -0
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +1 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +4 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +14 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +91 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +6 -0
- package/vendor/hindsight-memory/scripts/reconcile_tail.py +1 -0
- package/vendor/hindsight-memory/scripts/retain.py +41 -1
- package/vendor/hindsight-memory/scripts/subagent_retain.py +1 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill.py +23 -1
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +5 -1
- package/vendor/hindsight-memory/scripts/tests/test_observation_scopes.py +325 -0
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +160 -1
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +40 -2
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.19.
|
|
2123
|
+
var VERSION = "0.19.30", COMMIT_SHA = "efe8e2a7";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -13571,13 +13571,46 @@ var init_zod = __esm(() => {
|
|
|
13571
13571
|
init_external();
|
|
13572
13572
|
});
|
|
13573
13573
|
|
|
13574
|
+
// src/memory/observation-scopes.ts
|
|
13575
|
+
function isObservationScope(value) {
|
|
13576
|
+
return typeof value === "string" && OBSERVATION_SCOPES.includes(value);
|
|
13577
|
+
}
|
|
13578
|
+
function observationScopesHint() {
|
|
13579
|
+
return OBSERVATION_SCOPES.join(", ");
|
|
13580
|
+
}
|
|
13581
|
+
function classifyObservationScope(raw) {
|
|
13582
|
+
if (raw === undefined)
|
|
13583
|
+
return { kind: "unset" };
|
|
13584
|
+
const value = raw.trim();
|
|
13585
|
+
if (!value)
|
|
13586
|
+
return { kind: "unset" };
|
|
13587
|
+
if (!isObservationScope(value))
|
|
13588
|
+
return { kind: "invalid", raw };
|
|
13589
|
+
return { kind: "set", scope: value };
|
|
13590
|
+
}
|
|
13591
|
+
function resolveObservationScope(configured, env2 = process.env) {
|
|
13592
|
+
if (configured !== undefined)
|
|
13593
|
+
return classifyObservationScope(configured);
|
|
13594
|
+
return classifyObservationScope(env2[OBSERVATION_SCOPES_ENV]);
|
|
13595
|
+
}
|
|
13596
|
+
var OBSERVATION_SCOPES, OBSERVATION_SCOPES_ENV = "HINDSIGHT_OBSERVATION_SCOPES";
|
|
13597
|
+
var init_observation_scopes = __esm(() => {
|
|
13598
|
+
OBSERVATION_SCOPES = [
|
|
13599
|
+
"per_tag",
|
|
13600
|
+
"combined",
|
|
13601
|
+
"all_combinations",
|
|
13602
|
+
"shared"
|
|
13603
|
+
];
|
|
13604
|
+
});
|
|
13605
|
+
|
|
13574
13606
|
// src/config/schema.ts
|
|
13575
13607
|
function isValidTimezone(value) {
|
|
13576
13608
|
return TIMEZONE_REGEX.test(value);
|
|
13577
13609
|
}
|
|
13578
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
13610
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
13579
13611
|
var init_schema = __esm(() => {
|
|
13580
13612
|
init_zod();
|
|
13613
|
+
init_observation_scopes();
|
|
13581
13614
|
CodeRepoEntrySchema = exports_external.object({
|
|
13582
13615
|
name: exports_external.string().describe("Short alias used when claiming (e.g. 'switchroom')"),
|
|
13583
13616
|
source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
|
|
@@ -13689,6 +13722,7 @@ var init_schema = __esm(() => {
|
|
|
13689
13722
|
allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
|
|
13690
13723
|
deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
|
|
13691
13724
|
}).optional();
|
|
13725
|
+
ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag \u2014 what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
|
|
13692
13726
|
AgentMemorySchema = exports_external.object({
|
|
13693
13727
|
collection: exports_external.string().describe("Hindsight collection name for this agent"),
|
|
13694
13728
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
@@ -13723,6 +13757,7 @@ var init_schema = __esm(() => {
|
|
|
13723
13757
|
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
13724
13758
|
}).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`)."),
|
|
13725
13759
|
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)."),
|
|
13760
|
+
observation_scopes: ObservationScopesSchema,
|
|
13726
13761
|
recall: exports_external.object({
|
|
13727
13762
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
13728
13763
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
@@ -13988,7 +14023,7 @@ var init_schema = __esm(() => {
|
|
|
13988
14023
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
13989
14024
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
13990
14025
|
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
13991
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS \u2014 only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
14026
|
+
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS \u2014 only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS \u2014 the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
13992
14027
|
});
|
|
13993
14028
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
13994
14029
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -14121,6 +14156,7 @@ var init_schema = __esm(() => {
|
|
|
14121
14156
|
file: exports_external.boolean().optional(),
|
|
14122
14157
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
14123
14158
|
directive_capture_nudge: exports_external.boolean().optional(),
|
|
14159
|
+
observation_scopes: ObservationScopesSchema,
|
|
14124
14160
|
recall: exports_external.object({
|
|
14125
14161
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
14126
14162
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -15922,7 +15958,8 @@ var init_hindsight_perf_defaults = __esm(() => {
|
|
|
15922
15958
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
15923
15959
|
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
15924
15960
|
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
15925
|
-
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
15961
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS",
|
|
15962
|
+
"HINDSIGHT_API_WORKER_RETAIN_MAX_SLOTS"
|
|
15926
15963
|
]);
|
|
15927
15964
|
HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
15928
15965
|
...[
|
|
@@ -28867,6 +28904,7 @@ function buildWorkspaceContext(args) {
|
|
|
28867
28904
|
hindsightRecallTypes,
|
|
28868
28905
|
hindsightRecallSkipTrivial,
|
|
28869
28906
|
hindsightDirectiveCaptureNudge,
|
|
28907
|
+
hindsightObservationScopes,
|
|
28870
28908
|
hindsightTopicAliasesJson,
|
|
28871
28909
|
hindsightSenderBanksJson,
|
|
28872
28910
|
hindsightTopicFilterMode
|
|
@@ -28920,6 +28958,7 @@ function buildWorkspaceContext(args) {
|
|
|
28920
28958
|
hindsightRecallTypes,
|
|
28921
28959
|
hindsightRecallSkipTrivial,
|
|
28922
28960
|
hindsightDirectiveCaptureNudge,
|
|
28961
|
+
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
28923
28962
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
28924
28963
|
hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
|
|
28925
28964
|
hindsightTopicFilterMode,
|
|
@@ -29184,6 +29223,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
29184
29223
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
29185
29224
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
29186
29225
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
29226
|
+
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
29187
29227
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
29188
29228
|
const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
|
|
29189
29229
|
const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
|
|
@@ -29219,6 +29259,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
29219
29259
|
hindsightRecallTypes,
|
|
29220
29260
|
hindsightRecallSkipTrivial,
|
|
29221
29261
|
hindsightDirectiveCaptureNudge,
|
|
29262
|
+
hindsightObservationScopes,
|
|
29222
29263
|
hindsightTopicAliasesJson,
|
|
29223
29264
|
hindsightSenderBanksJson,
|
|
29224
29265
|
hindsightTopicFilterMode
|
|
@@ -30163,6 +30204,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
30163
30204
|
const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
|
|
30164
30205
|
const hindsightRecallSkipTrivial = agentConfig.memory?.recall?.skip_trivial === undefined ? undefined : String(agentConfig.memory.recall.skip_trivial);
|
|
30165
30206
|
const hindsightDirectiveCaptureNudge = agentConfig.memory?.directive_capture_nudge === undefined ? undefined : String(agentConfig.memory.directive_capture_nudge);
|
|
30207
|
+
const hindsightObservationScopes = agentConfig.memory?.observation_scopes;
|
|
30166
30208
|
const topicAliases = agentConfig.channels?.telegram?.topic_aliases;
|
|
30167
30209
|
const hindsightTopicAliasesJson = topicAliases && Object.keys(topicAliases).length > 0 ? JSON.stringify(topicAliases) : undefined;
|
|
30168
30210
|
const senderBanks = switchroomConfig ? resolveUsers(switchroomConfig, name).senderBanks : agentConfig.memory?.recall?.sender_banks ?? {};
|
|
@@ -30211,6 +30253,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
|
|
|
30211
30253
|
hindsightRecallTypes,
|
|
30212
30254
|
hindsightRecallSkipTrivial,
|
|
30213
30255
|
hindsightDirectiveCaptureNudge,
|
|
30256
|
+
hindsightObservationScopesQ: hindsightObservationScopes ? shellSingleQuote(hindsightObservationScopes) : undefined,
|
|
30214
30257
|
hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
|
|
30215
30258
|
hindsightSenderBanksJsonQ: hindsightSenderBanksJson ? shellSingleQuote(hindsightSenderBanksJson) : undefined,
|
|
30216
30259
|
hindsightTopicFilterMode,
|
|
@@ -30633,6 +30676,7 @@ ${body}
|
|
|
30633
30676
|
hindsightRecallTypes,
|
|
30634
30677
|
hindsightRecallSkipTrivial,
|
|
30635
30678
|
hindsightDirectiveCaptureNudge,
|
|
30679
|
+
hindsightObservationScopes,
|
|
30636
30680
|
hindsightTopicAliasesJson,
|
|
30637
30681
|
hindsightSenderBanksJson,
|
|
30638
30682
|
hindsightTopicFilterMode
|
|
@@ -50816,6 +50860,209 @@ var init_doctor_drift = __esm(() => {
|
|
|
50816
50860
|
init_write_compose();
|
|
50817
50861
|
});
|
|
50818
50862
|
|
|
50863
|
+
// src/cli/component-versions.ts
|
|
50864
|
+
function versionFromImageRef(ref) {
|
|
50865
|
+
const at = ref.indexOf("@");
|
|
50866
|
+
const withoutDigest = at >= 0 ? ref.slice(0, at) : ref;
|
|
50867
|
+
const slash = withoutDigest.lastIndexOf("/");
|
|
50868
|
+
const colon = withoutDigest.indexOf(":", slash + 1);
|
|
50869
|
+
if (colon < 0) {
|
|
50870
|
+
return { version: null, detail: `image ref has no tag (${ref})` };
|
|
50871
|
+
}
|
|
50872
|
+
const tag = withoutDigest.slice(colon + 1);
|
|
50873
|
+
if (SEMVER_TAG_RE.test(tag))
|
|
50874
|
+
return { version: tag };
|
|
50875
|
+
return { version: null, detail: `non-semver tag "${tag}"` };
|
|
50876
|
+
}
|
|
50877
|
+
function isSwitchroomReleaseImage(_name, imageRef) {
|
|
50878
|
+
return /\/switchroom-[a-z0-9-]+(:|@|$)/.test(imageRef);
|
|
50879
|
+
}
|
|
50880
|
+
function collectContainerComponents(exec) {
|
|
50881
|
+
const r = exec("docker", [
|
|
50882
|
+
"ps",
|
|
50883
|
+
"--filter",
|
|
50884
|
+
"name=switchroom-",
|
|
50885
|
+
"--format",
|
|
50886
|
+
"{{.Names}}\t{{.Image}}"
|
|
50887
|
+
]);
|
|
50888
|
+
if (r.status !== 0)
|
|
50889
|
+
return [];
|
|
50890
|
+
const out = [];
|
|
50891
|
+
for (const line of r.stdout.split(`
|
|
50892
|
+
`)) {
|
|
50893
|
+
const [name, imageRef] = line.trim().split("\t");
|
|
50894
|
+
if (!name || !imageRef)
|
|
50895
|
+
continue;
|
|
50896
|
+
if (!isSwitchroomReleaseImage(name, imageRef))
|
|
50897
|
+
continue;
|
|
50898
|
+
const { version: version2, detail } = versionFromImageRef(imageRef);
|
|
50899
|
+
out.push({
|
|
50900
|
+
name,
|
|
50901
|
+
kind: "container",
|
|
50902
|
+
version: version2,
|
|
50903
|
+
imageRef,
|
|
50904
|
+
...detail ? { detail } : {}
|
|
50905
|
+
});
|
|
50906
|
+
}
|
|
50907
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
50908
|
+
}
|
|
50909
|
+
function normalizeSemverTag(v) {
|
|
50910
|
+
if (!v)
|
|
50911
|
+
return null;
|
|
50912
|
+
const tag = `v${v.trim().replace(/^v/, "")}`;
|
|
50913
|
+
return SEMVER_TAG_RE.test(tag) ? tag : null;
|
|
50914
|
+
}
|
|
50915
|
+
function resolveDriftTarget(components, releasePin) {
|
|
50916
|
+
const pin = normalizeSemverTag(releasePin);
|
|
50917
|
+
if (pin)
|
|
50918
|
+
return { target: pin, targetSource: "release.pin" };
|
|
50919
|
+
let highest = null;
|
|
50920
|
+
for (const c of components) {
|
|
50921
|
+
if (!c.version)
|
|
50922
|
+
continue;
|
|
50923
|
+
if (highest === null) {
|
|
50924
|
+
highest = c.version;
|
|
50925
|
+
continue;
|
|
50926
|
+
}
|
|
50927
|
+
const cmp = compareReleaseTags(c.version, highest);
|
|
50928
|
+
if (cmp !== null && cmp > 0)
|
|
50929
|
+
highest = c.version;
|
|
50930
|
+
}
|
|
50931
|
+
return highest ? { target: highest, targetSource: "highest-deployed" } : { target: null, targetSource: "none" };
|
|
50932
|
+
}
|
|
50933
|
+
function detectComponentDrift(components, releasePin) {
|
|
50934
|
+
const { target, targetSource } = resolveDriftTarget(components, releasePin);
|
|
50935
|
+
const report = {
|
|
50936
|
+
target,
|
|
50937
|
+
targetSource,
|
|
50938
|
+
behind: [],
|
|
50939
|
+
ahead: [],
|
|
50940
|
+
current: [],
|
|
50941
|
+
unknown: []
|
|
50942
|
+
};
|
|
50943
|
+
for (const c of components) {
|
|
50944
|
+
if (!c.version || !target) {
|
|
50945
|
+
report.unknown.push(c);
|
|
50946
|
+
continue;
|
|
50947
|
+
}
|
|
50948
|
+
const cmp = compareReleaseTags(c.version, target);
|
|
50949
|
+
if (cmp === null)
|
|
50950
|
+
report.unknown.push(c);
|
|
50951
|
+
else if (cmp < 0)
|
|
50952
|
+
report.behind.push(c);
|
|
50953
|
+
else if (cmp > 0)
|
|
50954
|
+
report.ahead.push(c);
|
|
50955
|
+
else
|
|
50956
|
+
report.current.push(c);
|
|
50957
|
+
}
|
|
50958
|
+
return report;
|
|
50959
|
+
}
|
|
50960
|
+
function collectComponents(cliVersion, exec) {
|
|
50961
|
+
const cli = {
|
|
50962
|
+
name: "cli (host)",
|
|
50963
|
+
kind: "cli",
|
|
50964
|
+
version: normalizeSemverTag(cliVersion),
|
|
50965
|
+
...normalizeSemverTag(cliVersion) ? {} : { detail: `unparseable CLI version "${cliVersion}"` }
|
|
50966
|
+
};
|
|
50967
|
+
return [cli, ...collectContainerComponents(exec)];
|
|
50968
|
+
}
|
|
50969
|
+
function formatComponentDrift(report) {
|
|
50970
|
+
const lines = [];
|
|
50971
|
+
if (!report.target) {
|
|
50972
|
+
lines.push("Component versions: no comparable version found (no release.pin and no semver-tagged component).");
|
|
50973
|
+
} else {
|
|
50974
|
+
const src = report.targetSource === "release.pin" ? "release.pin" : "highest deployed version (no release.pin set)";
|
|
50975
|
+
lines.push(`Component versions \u2014 target ${report.target} (from ${src}):`);
|
|
50976
|
+
}
|
|
50977
|
+
for (const c of report.behind) {
|
|
50978
|
+
lines.push(` BEHIND ${c.name} \u2014 ${c.version} (target ${report.target})`);
|
|
50979
|
+
}
|
|
50980
|
+
for (const c of report.ahead) {
|
|
50981
|
+
lines.push(` ahead ${c.name} \u2014 ${c.version}`);
|
|
50982
|
+
}
|
|
50983
|
+
for (const c of report.unknown) {
|
|
50984
|
+
lines.push(` unknown ${c.name} \u2014 ${c.detail ?? "version not readable"}`);
|
|
50985
|
+
}
|
|
50986
|
+
for (const c of report.current) {
|
|
50987
|
+
lines.push(` ok ${c.name} \u2014 ${c.version}`);
|
|
50988
|
+
}
|
|
50989
|
+
return lines.join(`
|
|
50990
|
+
`);
|
|
50991
|
+
}
|
|
50992
|
+
var SEMVER_TAG_RE;
|
|
50993
|
+
var init_component_versions = __esm(() => {
|
|
50994
|
+
SEMVER_TAG_RE = /^v\d+\.\d+\.\d+$/;
|
|
50995
|
+
});
|
|
50996
|
+
|
|
50997
|
+
// src/cli/doctor-component-versions.ts
|
|
50998
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
50999
|
+
function fixFor(c, target) {
|
|
51000
|
+
if (c.kind === "cli") {
|
|
51001
|
+
return `switchroom update (self-updates the host CLI to ${target} first, then everything else)`;
|
|
51002
|
+
}
|
|
51003
|
+
if (c.name === "switchroom-web") {
|
|
51004
|
+
return `switchroom webd install --tag ${target} (run host-side: webd install fails inside hostd when the hostd compose predates SWITCHROOM_HOSTD_OPERATOR_UID)`;
|
|
51005
|
+
}
|
|
51006
|
+
if (c.name === "switchroom-hindsight-autoheal" || c.name === "switchroom-hostd") {
|
|
51007
|
+
return `switchroom hostd install --tag ${target} (regenerates the hostd compose project \u2014 both hostd and the autoheal sidecar)`;
|
|
51008
|
+
}
|
|
51009
|
+
return `switchroom update`;
|
|
51010
|
+
}
|
|
51011
|
+
function runComponentVersionChecks(config, opts = {}) {
|
|
51012
|
+
const exec = opts.exec ?? defaultExec2;
|
|
51013
|
+
const components = collectComponents(opts.cliVersion ?? SWITCHROOM_VERSION, exec);
|
|
51014
|
+
const report = detectComponentDrift(components, config.release?.pin);
|
|
51015
|
+
if (!report.target) {
|
|
51016
|
+
return [
|
|
51017
|
+
{
|
|
51018
|
+
name: "component versions",
|
|
51019
|
+
status: "skip",
|
|
51020
|
+
detail: "no release.pin set and no semver-tagged switchroom container running \u2014 nothing to compare."
|
|
51021
|
+
}
|
|
51022
|
+
];
|
|
51023
|
+
}
|
|
51024
|
+
const results = [];
|
|
51025
|
+
const src = report.targetSource === "release.pin" ? `release.pin ${report.target}` : `${report.target} (highest deployed \u2014 no release.pin set)`;
|
|
51026
|
+
if (report.behind.length === 0) {
|
|
51027
|
+
results.push({
|
|
51028
|
+
name: "component versions",
|
|
51029
|
+
status: "ok",
|
|
51030
|
+
detail: `all ${report.current.length} comparable component(s) on ${src}`
|
|
51031
|
+
});
|
|
51032
|
+
}
|
|
51033
|
+
for (const c of report.behind) {
|
|
51034
|
+
results.push({
|
|
51035
|
+
name: `component behind: ${c.name}`,
|
|
51036
|
+
status: "warn",
|
|
51037
|
+
detail: `on ${c.version}, expected ${report.target} (${src})`,
|
|
51038
|
+
fix: fixFor(c, report.target)
|
|
51039
|
+
});
|
|
51040
|
+
}
|
|
51041
|
+
for (const c of report.ahead) {
|
|
51042
|
+
results.push({
|
|
51043
|
+
name: `component ahead: ${c.name}`,
|
|
51044
|
+
status: "warn",
|
|
51045
|
+
detail: `on ${c.version}, ahead of ${src} \u2014 a roll may be mid-flight, or release.pin is stale`
|
|
51046
|
+
});
|
|
51047
|
+
}
|
|
51048
|
+
for (const c of report.unknown) {
|
|
51049
|
+
results.push({
|
|
51050
|
+
name: `component version unknown: ${c.name}`,
|
|
51051
|
+
status: "skip",
|
|
51052
|
+
detail: c.detail ?? "version not readable"
|
|
51053
|
+
});
|
|
51054
|
+
}
|
|
51055
|
+
return results;
|
|
51056
|
+
}
|
|
51057
|
+
var defaultExec2 = (cmd, args) => {
|
|
51058
|
+
const r = spawnSync12(cmd, args, { encoding: "utf-8", timeout: 1e4 });
|
|
51059
|
+
return { status: r.status ?? 1, stdout: r.stdout ?? "" };
|
|
51060
|
+
};
|
|
51061
|
+
var init_doctor_component_versions = __esm(() => {
|
|
51062
|
+
init_resolve_version();
|
|
51063
|
+
init_component_versions();
|
|
51064
|
+
});
|
|
51065
|
+
|
|
50819
51066
|
// src/cli/doctor-scaffold-wiring.ts
|
|
50820
51067
|
import { join as join70, resolve as resolve43 } from "node:path";
|
|
50821
51068
|
function readJson2(d, path7) {
|
|
@@ -52570,7 +52817,7 @@ var init_doctor_fix_session_model = __esm(() => {
|
|
|
52570
52817
|
});
|
|
52571
52818
|
|
|
52572
52819
|
// src/cli/doctor-claude-cli.ts
|
|
52573
|
-
import { spawnSync as
|
|
52820
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
52574
52821
|
import { existsSync as existsSync76, readFileSync as readFileSync70 } from "node:fs";
|
|
52575
52822
|
import { dirname as dirname30, join as join80 } from "node:path";
|
|
52576
52823
|
function parseClaudeCliVersion(raw) {
|
|
@@ -52635,7 +52882,7 @@ function parseClaudeCliProbeOutput(r) {
|
|
|
52635
52882
|
return { state: "read", bin, raw };
|
|
52636
52883
|
}
|
|
52637
52884
|
function probeAgentClaudeCli(agentName) {
|
|
52638
|
-
const r =
|
|
52885
|
+
const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
|
|
52639
52886
|
return parseClaudeCliProbeOutput(r);
|
|
52640
52887
|
}
|
|
52641
52888
|
function assessClaudeCliFloor(input) {
|
|
@@ -52758,7 +53005,7 @@ __export(exports_doctor, {
|
|
|
52758
53005
|
PENDING_RETAINS_EVICTION_WINDOW_DAYS: () => PENDING_RETAINS_EVICTION_WINDOW_DAYS,
|
|
52759
53006
|
MFF_VAULT_KEY: () => MFF_VAULT_KEY
|
|
52760
53007
|
});
|
|
52761
|
-
import { spawnSync as
|
|
53008
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
52762
53009
|
import { Socket as Socket3 } from "node:net";
|
|
52763
53010
|
import {
|
|
52764
53011
|
accessSync as accessSync3,
|
|
@@ -52904,7 +53151,7 @@ function readVersion(bin, parser) {
|
|
|
52904
53151
|
const path7 = which(bin);
|
|
52905
53152
|
if (!path7)
|
|
52906
53153
|
return null;
|
|
52907
|
-
const proc =
|
|
53154
|
+
const proc = spawnSync14(path7, ["--version"], {
|
|
52908
53155
|
stdio: ["ignore", "pipe", "pipe"]
|
|
52909
53156
|
});
|
|
52910
53157
|
if (proc.error || proc.status !== 0)
|
|
@@ -53196,7 +53443,7 @@ function probeVaultBrokerSocketPair(agentName) {
|
|
|
53196
53443
|
const dataPath = `/run/switchroom/broker/${agentName}/sock`;
|
|
53197
53444
|
const unlockPath = `/run/switchroom/broker/${agentName}/unlock`;
|
|
53198
53445
|
const script = `D=0; U=0; ` + `test -S '${dataPath}' && D=1; ` + `test -S '${unlockPath}' && U=1; ` + `echo "D=$D U=$U"`;
|
|
53199
|
-
const r =
|
|
53446
|
+
const r = spawnSync14("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
|
|
53200
53447
|
if (r.error || r.status === null)
|
|
53201
53448
|
return "unreachable";
|
|
53202
53449
|
if (r.status !== 0) {
|
|
@@ -53446,7 +53693,7 @@ function checkHindsightConsumer(config, opts) {
|
|
|
53446
53693
|
}
|
|
53447
53694
|
function probeAuthBrokerSocket(consumerName) {
|
|
53448
53695
|
const containerPath = `/run/switchroom/auth-broker/${consumerName}/sock`;
|
|
53449
|
-
const r =
|
|
53696
|
+
const r = spawnSync14("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
|
|
53450
53697
|
if (r.error || r.status === null)
|
|
53451
53698
|
return "unreachable";
|
|
53452
53699
|
if (r.status === 0)
|
|
@@ -53697,7 +53944,7 @@ async function checkHindsight(config) {
|
|
|
53697
53944
|
function probePendingRetainsQueue(agentName) {
|
|
53698
53945
|
const base = "/state/agent/home/.hindsight";
|
|
53699
53946
|
const script = buildPendingRetainsProbeScript(base);
|
|
53700
|
-
const r =
|
|
53947
|
+
const r = spawnSync14("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
|
|
53701
53948
|
return parsePendingRetainsProbeOutput(r);
|
|
53702
53949
|
}
|
|
53703
53950
|
function buildPendingRetainsProbeScript(base, now = Date.now()) {
|
|
@@ -54564,7 +54811,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
54564
54811
|
const python3 = which("python3") ?? "python3";
|
|
54565
54812
|
let token;
|
|
54566
54813
|
try {
|
|
54567
|
-
const result =
|
|
54814
|
+
const result = spawnSync14(python3, [authScript, "--quiet"], {
|
|
54568
54815
|
timeout: timeoutMs,
|
|
54569
54816
|
encoding: "utf-8",
|
|
54570
54817
|
env: { ...process.env, ...env2 }
|
|
@@ -54993,6 +55240,22 @@ function registerDoctorCommand(program3) {
|
|
|
54993
55240
|
})
|
|
54994
55241
|
},
|
|
54995
55242
|
{ title: "Cron Session", results: runCronSessionChecks(config) },
|
|
55243
|
+
{
|
|
55244
|
+
title: "Component versions (#3919)",
|
|
55245
|
+
results: (() => {
|
|
55246
|
+
try {
|
|
55247
|
+
return runComponentVersionChecks(config);
|
|
55248
|
+
} catch (err) {
|
|
55249
|
+
return [
|
|
55250
|
+
{
|
|
55251
|
+
name: "component versions",
|
|
55252
|
+
status: "skip",
|
|
55253
|
+
detail: `not checkable: ${err?.message ?? String(err)}`
|
|
55254
|
+
}
|
|
55255
|
+
];
|
|
55256
|
+
}
|
|
55257
|
+
})()
|
|
55258
|
+
},
|
|
54996
55259
|
{
|
|
54997
55260
|
title: "Generated-surface drift (KEN-130)",
|
|
54998
55261
|
results: await runGeneratedSurfaceDriftChecks(config, configPath, {
|
|
@@ -55080,6 +55343,7 @@ var init_doctor = __esm(() => {
|
|
|
55080
55343
|
init_doctor_cron_session();
|
|
55081
55344
|
init_doctor_flood_pressure();
|
|
55082
55345
|
init_doctor_drift();
|
|
55346
|
+
init_doctor_component_versions();
|
|
55083
55347
|
init_doctor_microsoft();
|
|
55084
55348
|
init_doctor_notion();
|
|
55085
55349
|
init_doctor_credentials_migration();
|
|
@@ -68028,9 +68292,9 @@ __export(exports_server, {
|
|
|
68028
68292
|
dispatchTool: () => dispatchTool,
|
|
68029
68293
|
TOOLS: () => TOOLS
|
|
68030
68294
|
});
|
|
68031
|
-
import { spawnSync as
|
|
68295
|
+
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
68032
68296
|
function execCli(args, stdin) {
|
|
68033
|
-
const r =
|
|
68297
|
+
const r = spawnSync22(CLI_BIN, args, {
|
|
68034
68298
|
encoding: "utf-8",
|
|
68035
68299
|
env: process.env,
|
|
68036
68300
|
timeout: 15000,
|
|
@@ -68539,7 +68803,7 @@ __export(exports_server2, {
|
|
|
68539
68803
|
TOOLS: () => TOOLS2
|
|
68540
68804
|
});
|
|
68541
68805
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
68542
|
-
import { existsSync as
|
|
68806
|
+
import { existsSync as existsSync105 } from "node:fs";
|
|
68543
68807
|
function selfSocketPath() {
|
|
68544
68808
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
68545
68809
|
}
|
|
@@ -68566,7 +68830,7 @@ async function dispatchTool2(name, args) {
|
|
|
68566
68830
|
return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
|
|
68567
68831
|
}
|
|
68568
68832
|
const sockPath = selfSocketPath();
|
|
68569
|
-
if (!
|
|
68833
|
+
if (!existsSync105(sockPath)) {
|
|
68570
68834
|
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.`);
|
|
68571
68835
|
}
|
|
68572
68836
|
let req;
|
|
@@ -68817,13 +69081,13 @@ function resolveAuditLogPath() {
|
|
|
68817
69081
|
if (process.env.HOSTD_AUDIT_LOG_PATH)
|
|
68818
69082
|
return process.env.HOSTD_AUDIT_LOG_PATH;
|
|
68819
69083
|
const bindMounted = "/host-home/.switchroom/host-control-audit.log";
|
|
68820
|
-
if (
|
|
69084
|
+
if (existsSync105(bindMounted))
|
|
68821
69085
|
return bindMounted;
|
|
68822
69086
|
return defaultAuditLogPath2();
|
|
68823
69087
|
}
|
|
68824
69088
|
function getLastUpdateApplyStatus() {
|
|
68825
69089
|
const path10 = resolveAuditLogPath();
|
|
68826
|
-
if (!
|
|
69090
|
+
if (!existsSync105(path10)) {
|
|
68827
69091
|
return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
|
|
68828
69092
|
}
|
|
68829
69093
|
let raw;
|
|
@@ -69542,11 +69806,11 @@ var init_test_agents = __esm(() => {
|
|
|
69542
69806
|
});
|
|
69543
69807
|
|
|
69544
69808
|
// src/litellm/header-passthrough-guard.ts
|
|
69545
|
-
import { existsSync as
|
|
69809
|
+
import { existsSync as existsSync108, readdirSync as readdirSync41 } from "node:fs";
|
|
69546
69810
|
function discoverLiveLitellmConfigPath(opts) {
|
|
69547
69811
|
const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
|
|
69548
69812
|
const readdir2 = opts?.readdirFn ?? ((p) => readdirSync41(p));
|
|
69549
|
-
const exists = opts?.existsFn ??
|
|
69813
|
+
const exists = opts?.existsFn ?? existsSync108;
|
|
69550
69814
|
let entries;
|
|
69551
69815
|
try {
|
|
69552
69816
|
entries = readdir2(servicesDir);
|
|
@@ -69636,7 +69900,7 @@ var init_header_passthrough_guard = __esm(() => {
|
|
|
69636
69900
|
});
|
|
69637
69901
|
|
|
69638
69902
|
// src/fleet-health/litellm-config-sensor.ts
|
|
69639
|
-
import { readFileSync as readFileSync96, existsSync as
|
|
69903
|
+
import { readFileSync as readFileSync96, existsSync as existsSync109 } from "node:fs";
|
|
69640
69904
|
function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitellmConfigPath()) {
|
|
69641
69905
|
if (explicit)
|
|
69642
69906
|
return explicit;
|
|
@@ -69647,7 +69911,7 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
|
|
|
69647
69911
|
}
|
|
69648
69912
|
function scanLitellmConfig(opts = {}) {
|
|
69649
69913
|
const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
|
|
69650
|
-
const exists = opts.existsFn ??
|
|
69914
|
+
const exists = opts.existsFn ?? existsSync109;
|
|
69651
69915
|
const read = opts.readFn ?? ((p) => readFileSync96(p, "utf-8"));
|
|
69652
69916
|
const log = opts.log ?? (() => {});
|
|
69653
69917
|
const nowIso = opts.nowIso ?? new Date().toISOString();
|
|
@@ -69725,18 +69989,18 @@ __export(exports_scan, {
|
|
|
69725
69989
|
import {
|
|
69726
69990
|
readFileSync as readFileSync97,
|
|
69727
69991
|
readdirSync as readdirSync42,
|
|
69728
|
-
existsSync as
|
|
69729
|
-
mkdirSync as
|
|
69992
|
+
existsSync as existsSync110,
|
|
69993
|
+
mkdirSync as mkdirSync63,
|
|
69730
69994
|
writeFileSync as writeFileSync46
|
|
69731
69995
|
} from "node:fs";
|
|
69732
|
-
import { resolve as resolve61, dirname as
|
|
69996
|
+
import { resolve as resolve61, dirname as dirname42 } from "node:path";
|
|
69733
69997
|
import { homedir as homedir60 } from "node:os";
|
|
69734
69998
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir60()) {
|
|
69735
69999
|
return resolve61(home2, ".switchroom");
|
|
69736
70000
|
}
|
|
69737
70001
|
function listAgents(base) {
|
|
69738
70002
|
const dir = resolve61(base, "agents");
|
|
69739
|
-
if (!
|
|
70003
|
+
if (!existsSync110(dir))
|
|
69740
70004
|
return [];
|
|
69741
70005
|
try {
|
|
69742
70006
|
return readdirSync42(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
@@ -69765,7 +70029,7 @@ function runScan(opts = {}) {
|
|
|
69765
70029
|
let gwText = "";
|
|
69766
70030
|
let sawArtifact = false;
|
|
69767
70031
|
try {
|
|
69768
|
-
if (
|
|
70032
|
+
if (existsSync110(turnsPath)) {
|
|
69769
70033
|
turnsText = readFileSync97(turnsPath, "utf-8");
|
|
69770
70034
|
sawArtifact = true;
|
|
69771
70035
|
}
|
|
@@ -69773,7 +70037,7 @@ function runScan(opts = {}) {
|
|
|
69773
70037
|
log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
|
|
69774
70038
|
}
|
|
69775
70039
|
try {
|
|
69776
|
-
if (
|
|
70040
|
+
if (existsSync110(gwPath)) {
|
|
69777
70041
|
gwText = readFileSync97(gwPath, "utf-8");
|
|
69778
70042
|
sawArtifact = true;
|
|
69779
70043
|
}
|
|
@@ -69816,7 +70080,7 @@ function runScan(opts = {}) {
|
|
|
69816
70080
|
function readLedgerIfPresent(base) {
|
|
69817
70081
|
const path10 = ledgerPathForBase(base);
|
|
69818
70082
|
try {
|
|
69819
|
-
if (!
|
|
70083
|
+
if (!existsSync110(path10))
|
|
69820
70084
|
return null;
|
|
69821
70085
|
return JSON.parse(readFileSync97(path10, "utf-8"));
|
|
69822
70086
|
} catch {
|
|
@@ -69824,11 +70088,11 @@ function readLedgerIfPresent(base) {
|
|
|
69824
70088
|
}
|
|
69825
70089
|
}
|
|
69826
70090
|
function ledgerPathForBase(base) {
|
|
69827
|
-
return fleetHealthLedgerPath(
|
|
70091
|
+
return fleetHealthLedgerPath(dirname42(base));
|
|
69828
70092
|
}
|
|
69829
70093
|
function writeLedger(base, ledger) {
|
|
69830
70094
|
const path10 = ledgerPathForBase(base);
|
|
69831
|
-
|
|
70095
|
+
mkdirSync63(dirname42(path10), { recursive: true });
|
|
69832
70096
|
writeFileSync46(path10, JSON.stringify(ledger, null, 2) + `
|
|
69833
70097
|
`, "utf-8");
|
|
69834
70098
|
return path10;
|
|
@@ -70195,8 +70459,10 @@ import { join as join19 } from "node:path";
|
|
|
70195
70459
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
70196
70460
|
|
|
70197
70461
|
// src/agents/handoff-summarizer.ts
|
|
70462
|
+
init_observation_scopes();
|
|
70198
70463
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync9, renameSync as renameSync7, mkdirSync as mkdirSync16, existsSync as existsSync23, statSync as statSync11, readdirSync as readdirSync10 } from "node:fs";
|
|
70199
70464
|
import { join as join18 } from "node:path";
|
|
70465
|
+
var HANDOFF_STATUS_MIRROR_SKIPPED = "mirror-skipped-invalid-scope";
|
|
70200
70466
|
var DEFAULT_MAX_TURNS = 50;
|
|
70201
70467
|
var TOPIC_MAX_CHARS = 117;
|
|
70202
70468
|
var TURN_TEXT_MAX_CHARS = 1200;
|
|
@@ -70355,8 +70621,8 @@ async function buildHandoff(opts) {
|
|
|
70355
70621
|
`);
|
|
70356
70622
|
return "write-error";
|
|
70357
70623
|
}
|
|
70358
|
-
await mirrorToHindsight(briefing, opts).catch(() =>
|
|
70359
|
-
return "ok";
|
|
70624
|
+
const mirrored = await mirrorToHindsight(briefing, opts).catch(() => true);
|
|
70625
|
+
return mirrored ? "ok" : HANDOFF_STATUS_MIRROR_SKIPPED;
|
|
70360
70626
|
}
|
|
70361
70627
|
function errMsg(err) {
|
|
70362
70628
|
if (err && typeof err === "object" && "message" in err) {
|
|
@@ -70368,15 +70634,22 @@ async function mirrorToHindsight(briefing, opts) {
|
|
|
70368
70634
|
const url = opts.hindsightUrl ?? process.env.HINDSIGHT_API_URL;
|
|
70369
70635
|
const bankId = opts.hindsightBankId ?? process.env.HINDSIGHT_BANK_ID ?? "default";
|
|
70370
70636
|
if (!url)
|
|
70371
|
-
return;
|
|
70637
|
+
return true;
|
|
70372
70638
|
const fetchFn = opts.fetch ?? fetch;
|
|
70373
70639
|
const endpoint = `${url.replace(/\/$/, "")}/v1/default/banks/${encodeURIComponent(bankId)}/memories`;
|
|
70640
|
+
const scope = resolveObservationScope(opts.observationScopes, opts.env ?? process.env);
|
|
70641
|
+
if (scope.kind === "invalid") {
|
|
70642
|
+
process.stderr.write(`handoff: hindsight mirror SKIPPED \u2014 observation scope ` + `${JSON.stringify(scope.raw)} is not valid ` + `(accepted: ${observationScopesHint()}). Fix ` + `memory.observation_scopes in switchroom.yaml (or the ` + `${OBSERVATION_SCOPES_ENV} export), then \`switchroom apply\` and ` + `restart the agent. The on-disk handoff sidecars were written ` + `normally; only the recallable Hindsight copy was skipped.
|
|
70643
|
+
`);
|
|
70644
|
+
return false;
|
|
70645
|
+
}
|
|
70374
70646
|
const body = {
|
|
70375
70647
|
items: [
|
|
70376
70648
|
{
|
|
70377
70649
|
content: briefing,
|
|
70378
70650
|
document_id: "session_handoff",
|
|
70379
|
-
tags: ["session_handoff", opts.agentName]
|
|
70651
|
+
tags: ["session_handoff", opts.agentName],
|
|
70652
|
+
...scope.kind === "set" ? { observation_scopes: scope.scope } : {}
|
|
70380
70653
|
}
|
|
70381
70654
|
],
|
|
70382
70655
|
async: true
|
|
@@ -70391,6 +70664,7 @@ async function mirrorToHindsight(briefing, opts) {
|
|
|
70391
70664
|
process.stderr.write(`handoff: hindsight mirror failed \u2014 ${errMsg(err)}
|
|
70392
70665
|
`);
|
|
70393
70666
|
}
|
|
70667
|
+
return true;
|
|
70394
70668
|
}
|
|
70395
70669
|
function findLatestSessionJsonl(claudeConfigDir) {
|
|
70396
70670
|
const projects = join18(claudeConfigDir, "projects");
|
|
@@ -90529,9 +90803,9 @@ init_source();
|
|
|
90529
90803
|
init_loader();
|
|
90530
90804
|
init_lifecycle();
|
|
90531
90805
|
init_compose_env();
|
|
90532
|
-
import { existsSync as
|
|
90533
|
-
import { spawnSync as
|
|
90534
|
-
import { join as join83, dirname as
|
|
90806
|
+
import { existsSync as existsSync80, mkdirSync as mkdirSync45, readFileSync as readFileSync73, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
|
|
90807
|
+
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
90808
|
+
import { join as join83, dirname as dirname33, resolve as resolve45 } from "node:path";
|
|
90535
90809
|
import { homedir as homedir43 } from "node:os";
|
|
90536
90810
|
|
|
90537
90811
|
// src/cli/release-yaml.ts
|
|
@@ -90779,6 +91053,286 @@ function syncBundledSkills(opts) {
|
|
|
90779
91053
|
|
|
90780
91054
|
// src/cli/update.ts
|
|
90781
91055
|
init_resolve_version();
|
|
91056
|
+
|
|
91057
|
+
// src/cli/self-update.ts
|
|
91058
|
+
var SELF_UPDATE_ENV_SENTINEL = "SWITCHROOM_SELF_UPDATED";
|
|
91059
|
+
var VERSION_STORE_DIRNAME = ".switchroom-versions";
|
|
91060
|
+
var GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/switchroom/switchroom/releases/latest";
|
|
91061
|
+
function detectInstallKind(p) {
|
|
91062
|
+
if (p.inContainer) {
|
|
91063
|
+
return {
|
|
91064
|
+
kind: "container",
|
|
91065
|
+
reason: "running inside a switchroom container \u2014 the CLI here comes from the " + "container image and is updated by pulling a new image, not by " + "replacing a file. Nothing to self-update."
|
|
91066
|
+
};
|
|
91067
|
+
}
|
|
91068
|
+
if (p.bundleDir.startsWith("/$bunfs")) {
|
|
91069
|
+
if (!p.execPath) {
|
|
91070
|
+
return {
|
|
91071
|
+
kind: "unknown",
|
|
91072
|
+
reason: "compiled binary but process.execPath is empty \u2014 cannot locate the " + "file to replace. Re-run install.sh to upgrade."
|
|
91073
|
+
};
|
|
91074
|
+
}
|
|
91075
|
+
return {
|
|
91076
|
+
kind: "static-binary",
|
|
91077
|
+
binaryPath: p.execPath,
|
|
91078
|
+
reason: `published static binary at ${p.execPath}`
|
|
91079
|
+
};
|
|
91080
|
+
}
|
|
91081
|
+
if (/[/\\]node_modules[/\\]switchroom[/\\]/.test(p.scriptPath)) {
|
|
91082
|
+
return {
|
|
91083
|
+
kind: "npm-global",
|
|
91084
|
+
reason: "installed via npm \u2014 self-update does not manage npm installs. " + "Upgrade with `npm i -g switchroom@latest`."
|
|
91085
|
+
};
|
|
91086
|
+
}
|
|
91087
|
+
if (isSwitchroomSourceCheckoutPath(p.scriptPath)) {
|
|
91088
|
+
return {
|
|
91089
|
+
kind: "source-checkout",
|
|
91090
|
+
reason: "running from a switchroom source checkout \u2014 self-update would clobber " + "your working tree. Upgrade with `git pull && bun install && npm run build`."
|
|
91091
|
+
};
|
|
91092
|
+
}
|
|
91093
|
+
return {
|
|
91094
|
+
kind: "unknown",
|
|
91095
|
+
reason: `could not identify how switchroom was installed (running from ` + `"${p.scriptPath}"). Not touching it. Upgrade with the method you used ` + `to install, or re-run install.sh.`
|
|
91096
|
+
};
|
|
91097
|
+
}
|
|
91098
|
+
function isSwitchroomSourceCheckoutPath(scriptPath) {
|
|
91099
|
+
return /[/\\](src|dist)[/\\]cli[/\\]switchroom(\.js|\.ts)?$/.test(scriptPath);
|
|
91100
|
+
}
|
|
91101
|
+
function releaseAssetName(platform, arch) {
|
|
91102
|
+
const os6 = platform === "linux" ? "linux" : platform === "darwin" ? "macos" : null;
|
|
91103
|
+
const cpu = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
|
|
91104
|
+
if (!os6 || !cpu)
|
|
91105
|
+
return null;
|
|
91106
|
+
return `switchroom-${os6}-${cpu}`;
|
|
91107
|
+
}
|
|
91108
|
+
function parseLatestReleaseTag(body) {
|
|
91109
|
+
try {
|
|
91110
|
+
const parsed = JSON.parse(body);
|
|
91111
|
+
if (parsed?.draft === true)
|
|
91112
|
+
return null;
|
|
91113
|
+
const tag = parsed?.tag_name;
|
|
91114
|
+
if (typeof tag !== "string" || !/^v\d+\.\d+\.\d+$/.test(tag))
|
|
91115
|
+
return null;
|
|
91116
|
+
return tag;
|
|
91117
|
+
} catch {
|
|
91118
|
+
return null;
|
|
91119
|
+
}
|
|
91120
|
+
}
|
|
91121
|
+
function expectedChecksum(checksumsText, asset) {
|
|
91122
|
+
for (const line of checksumsText.split(`
|
|
91123
|
+
`)) {
|
|
91124
|
+
const idx = line.indexOf(` ${asset}`);
|
|
91125
|
+
if (idx <= 0)
|
|
91126
|
+
continue;
|
|
91127
|
+
if (line.slice(idx + 2).trim() !== asset)
|
|
91128
|
+
continue;
|
|
91129
|
+
const hash2 = line.slice(0, idx).trim();
|
|
91130
|
+
if (/^[0-9a-f]{64}$/i.test(hash2))
|
|
91131
|
+
return hash2.toLowerCase();
|
|
91132
|
+
}
|
|
91133
|
+
return null;
|
|
91134
|
+
}
|
|
91135
|
+
function planSelfUpdate(opts) {
|
|
91136
|
+
const { detection, currentVersion, latestTag } = opts;
|
|
91137
|
+
if (detection.kind !== "static-binary" || !detection.binaryPath) {
|
|
91138
|
+
return { action: "skip", reason: detection.reason };
|
|
91139
|
+
}
|
|
91140
|
+
if (!latestTag) {
|
|
91141
|
+
return {
|
|
91142
|
+
action: "skip",
|
|
91143
|
+
reason: "could not resolve the latest published release from GitHub " + "(offline, rate-limited, or the API changed shape) \u2014 leaving the CLI as-is."
|
|
91144
|
+
};
|
|
91145
|
+
}
|
|
91146
|
+
const current = `v${currentVersion.trim().replace(/^v/, "")}`;
|
|
91147
|
+
const cmp = compareReleaseTags(current, latestTag);
|
|
91148
|
+
if (cmp === null) {
|
|
91149
|
+
return {
|
|
91150
|
+
action: "skip",
|
|
91151
|
+
reason: `local CLI version "${currentVersion}" is not a comparable semver \u2014 ` + `refusing to guess whether ${latestTag} is an upgrade.`
|
|
91152
|
+
};
|
|
91153
|
+
}
|
|
91154
|
+
if (cmp >= 0)
|
|
91155
|
+
return { action: "current", version: current };
|
|
91156
|
+
return {
|
|
91157
|
+
action: "update",
|
|
91158
|
+
from: current,
|
|
91159
|
+
to: latestTag,
|
|
91160
|
+
binaryPath: detection.binaryPath
|
|
91161
|
+
};
|
|
91162
|
+
}
|
|
91163
|
+
function versionStorePath(installDir, version2, sep4 = "/") {
|
|
91164
|
+
return `${installDir}${sep4}${VERSION_STORE_DIRNAME}${sep4}switchroom-${version2.replace(/^v/, "")}`;
|
|
91165
|
+
}
|
|
91166
|
+
function rollbackHint(installDir, previousVersion) {
|
|
91167
|
+
return `Rollback: cp ${versionStorePath(installDir, previousVersion)} ` + `${installDir}/switchroom`;
|
|
91168
|
+
}
|
|
91169
|
+
function alreadySelfUpdated(env2) {
|
|
91170
|
+
return env2[SELF_UPDATE_ENV_SENTINEL] === "1";
|
|
91171
|
+
}
|
|
91172
|
+
async function fetchLatestReleaseTag(io) {
|
|
91173
|
+
try {
|
|
91174
|
+
return parseLatestReleaseTag(await io.httpGetText(GITHUB_LATEST_RELEASE_URL));
|
|
91175
|
+
} catch {
|
|
91176
|
+
return null;
|
|
91177
|
+
}
|
|
91178
|
+
}
|
|
91179
|
+
async function performSelfUpdate(opts) {
|
|
91180
|
+
const { plan, assetName, io } = opts;
|
|
91181
|
+
const log = opts.log ?? (() => {});
|
|
91182
|
+
const installDir = io.dirname(plan.binaryPath);
|
|
91183
|
+
const store = `${installDir}/${VERSION_STORE_DIRNAME}`;
|
|
91184
|
+
const base = `https://github.com/switchroom/switchroom/releases/download/${plan.to}`;
|
|
91185
|
+
const staged = versionStorePath(installDir, plan.to);
|
|
91186
|
+
const tmp = `${staged}.download`;
|
|
91187
|
+
io.mkdirp(store);
|
|
91188
|
+
io.remove(tmp);
|
|
91189
|
+
log(`downloading ${assetName} ${plan.to}`);
|
|
91190
|
+
try {
|
|
91191
|
+
await io.httpDownload(`${base}/${assetName}`, tmp);
|
|
91192
|
+
} catch (err) {
|
|
91193
|
+
io.remove(tmp);
|
|
91194
|
+
throw new Error(`self-update: download of ${base}/${assetName} failed (${err.message}). ` + `The installed CLI is unchanged.`);
|
|
91195
|
+
}
|
|
91196
|
+
let checksums;
|
|
91197
|
+
try {
|
|
91198
|
+
checksums = await io.httpGetText(`${base}/switchroom-checksums.txt`);
|
|
91199
|
+
} catch (err) {
|
|
91200
|
+
io.remove(tmp);
|
|
91201
|
+
throw new Error(`self-update: could not fetch switchroom-checksums.txt for ${plan.to} ` + `(${err.message}) \u2014 refusing to install an unverified binary. ` + `The installed CLI is unchanged.`);
|
|
91202
|
+
}
|
|
91203
|
+
const expected = expectedChecksum(checksums, assetName);
|
|
91204
|
+
if (!expected) {
|
|
91205
|
+
io.remove(tmp);
|
|
91206
|
+
throw new Error(`self-update: release ${plan.to} has no checksum entry for ${assetName} \u2014 ` + `refusing to install an unverified binary. The installed CLI is unchanged.`);
|
|
91207
|
+
}
|
|
91208
|
+
const actual = io.sha256File(tmp).toLowerCase();
|
|
91209
|
+
if (actual !== expected) {
|
|
91210
|
+
io.remove(tmp);
|
|
91211
|
+
throw new Error(`self-update: SHA256 mismatch for ${assetName} (expected ${expected}, got ${actual}) \u2014 ` + `refusing to install. The installed CLI is unchanged.`);
|
|
91212
|
+
}
|
|
91213
|
+
io.chmodExec(tmp);
|
|
91214
|
+
const proved = io.probeBinaryVersion(tmp);
|
|
91215
|
+
if (!proved) {
|
|
91216
|
+
io.remove(tmp);
|
|
91217
|
+
throw new Error(`self-update: the downloaded ${plan.to} binary did not run cleanly on this host ` + `(\`switchroom version\` failed) \u2014 refusing to install it. The installed CLI is unchanged.`);
|
|
91218
|
+
}
|
|
91219
|
+
const previousArchive = versionStorePath(installDir, plan.from);
|
|
91220
|
+
try {
|
|
91221
|
+
if (!io.exists(previousArchive))
|
|
91222
|
+
io.copyFile(plan.binaryPath, previousArchive);
|
|
91223
|
+
} catch (err) {
|
|
91224
|
+
io.remove(tmp);
|
|
91225
|
+
throw new Error(`self-update: could not archive the current binary to ${previousArchive} ` + `(${err.message}) \u2014 refusing to swap without a rollback copy. ` + `The installed CLI is unchanged.`);
|
|
91226
|
+
}
|
|
91227
|
+
io.rename(tmp, staged);
|
|
91228
|
+
io.copyFile(staged, `${plan.binaryPath}.new`);
|
|
91229
|
+
io.chmodExec(`${plan.binaryPath}.new`);
|
|
91230
|
+
io.rename(`${plan.binaryPath}.new`, plan.binaryPath);
|
|
91231
|
+
log(rollbackHint(installDir, plan.from));
|
|
91232
|
+
return {
|
|
91233
|
+
replaced: true,
|
|
91234
|
+
newVersion: plan.to,
|
|
91235
|
+
binaryPath: plan.binaryPath,
|
|
91236
|
+
message: `host CLI ${plan.from} \u2192 ${plan.to} (verified sha256, ran clean). ` + rollbackHint(installDir, plan.from)
|
|
91237
|
+
};
|
|
91238
|
+
}
|
|
91239
|
+
|
|
91240
|
+
// src/cli/self-update-io.ts
|
|
91241
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
91242
|
+
import {
|
|
91243
|
+
chmodSync as chmodSync15,
|
|
91244
|
+
closeSync as closeSync15,
|
|
91245
|
+
copyFileSync as copyFileSync11,
|
|
91246
|
+
createWriteStream,
|
|
91247
|
+
existsSync as existsSync79,
|
|
91248
|
+
mkdirSync as mkdirSync44,
|
|
91249
|
+
openSync as openSync15,
|
|
91250
|
+
readSync as readSync5,
|
|
91251
|
+
renameSync as renameSync19,
|
|
91252
|
+
rmSync as rmSync14
|
|
91253
|
+
} from "node:fs";
|
|
91254
|
+
import { dirname as dirname32 } from "node:path";
|
|
91255
|
+
import { Readable } from "node:stream";
|
|
91256
|
+
import { pipeline } from "node:stream/promises";
|
|
91257
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
91258
|
+
var HTTP_TIMEOUT_MS = 60000;
|
|
91259
|
+
function defaultSelfUpdateIO() {
|
|
91260
|
+
return {
|
|
91261
|
+
async httpGetText(url) {
|
|
91262
|
+
const res = await fetch(url, {
|
|
91263
|
+
headers: { "user-agent": "switchroom-cli-self-update" },
|
|
91264
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
91265
|
+
});
|
|
91266
|
+
if (!res.ok)
|
|
91267
|
+
throw new Error(`HTTP ${res.status} for ${url}`);
|
|
91268
|
+
return await res.text();
|
|
91269
|
+
},
|
|
91270
|
+
async httpDownload(url, dest) {
|
|
91271
|
+
const res = await fetch(url, {
|
|
91272
|
+
headers: { "user-agent": "switchroom-cli-self-update" },
|
|
91273
|
+
redirect: "follow",
|
|
91274
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
91275
|
+
});
|
|
91276
|
+
if (!res.ok)
|
|
91277
|
+
throw new Error(`HTTP ${res.status} for ${url}`);
|
|
91278
|
+
if (!res.body)
|
|
91279
|
+
throw new Error(`empty response body for ${url}`);
|
|
91280
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(dest));
|
|
91281
|
+
},
|
|
91282
|
+
sha256File(path7) {
|
|
91283
|
+
const hash2 = createHash16("sha256");
|
|
91284
|
+
const fd = openSync15(path7, "r");
|
|
91285
|
+
try {
|
|
91286
|
+
const buf = Buffer.allocUnsafe(1 << 20);
|
|
91287
|
+
for (;; ) {
|
|
91288
|
+
const n = readSync5(fd, buf, 0, buf.length, null);
|
|
91289
|
+
if (n <= 0)
|
|
91290
|
+
break;
|
|
91291
|
+
hash2.update(buf.subarray(0, n));
|
|
91292
|
+
}
|
|
91293
|
+
} finally {
|
|
91294
|
+
closeSync15(fd);
|
|
91295
|
+
}
|
|
91296
|
+
return hash2.digest("hex");
|
|
91297
|
+
},
|
|
91298
|
+
probeBinaryVersion(path7) {
|
|
91299
|
+
const r = spawnSync15(path7, ["version"], {
|
|
91300
|
+
encoding: "utf-8",
|
|
91301
|
+
timeout: 30000,
|
|
91302
|
+
env: { ...process.env, SWITCHROOM_SELF_UPDATED: "" }
|
|
91303
|
+
});
|
|
91304
|
+
if (r.status !== 0)
|
|
91305
|
+
return null;
|
|
91306
|
+
const out = `${r.stdout ?? ""}`.trim();
|
|
91307
|
+
const m = out.match(/\d+\.\d+\.\d+/);
|
|
91308
|
+
return m ? m[0] : null;
|
|
91309
|
+
},
|
|
91310
|
+
mkdirp(dir) {
|
|
91311
|
+
mkdirSync44(dir, { recursive: true });
|
|
91312
|
+
},
|
|
91313
|
+
copyFile(src, dest) {
|
|
91314
|
+
copyFileSync11(src, dest);
|
|
91315
|
+
},
|
|
91316
|
+
chmodExec(path7) {
|
|
91317
|
+
chmodSync15(path7, 493);
|
|
91318
|
+
},
|
|
91319
|
+
rename(src, dest) {
|
|
91320
|
+
renameSync19(src, dest);
|
|
91321
|
+
},
|
|
91322
|
+
remove(path7) {
|
|
91323
|
+
rmSync14(path7, { force: true });
|
|
91324
|
+
},
|
|
91325
|
+
exists(path7) {
|
|
91326
|
+
return existsSync79(path7);
|
|
91327
|
+
},
|
|
91328
|
+
dirname(path7) {
|
|
91329
|
+
return dirname32(path7);
|
|
91330
|
+
}
|
|
91331
|
+
};
|
|
91332
|
+
}
|
|
91333
|
+
|
|
91334
|
+
// src/cli/update.ts
|
|
91335
|
+
init_component_versions();
|
|
90782
91336
|
function defaultPersistPin(configPath) {
|
|
90783
91337
|
return (pin) => {
|
|
90784
91338
|
const path7 = configPath ?? findConfigFile();
|
|
@@ -90798,16 +91352,16 @@ function defaultPersistPin(configPath) {
|
|
|
90798
91352
|
}
|
|
90799
91353
|
var DEFAULT_COMPOSE_PATH2 = join83(homedir43(), ".switchroom", "compose", "docker-compose.yml");
|
|
90800
91354
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
90801
|
-
let dir =
|
|
91355
|
+
let dir = dirname33(scriptPath);
|
|
90802
91356
|
for (let i = 0;i < 12; i++) {
|
|
90803
|
-
if (
|
|
91357
|
+
if (existsSync80(join83(dir, ".git"))) {
|
|
90804
91358
|
try {
|
|
90805
91359
|
const pkg = JSON.parse(readFileSync73(join83(dir, "package.json"), "utf-8"));
|
|
90806
91360
|
if (pkg.name === "switchroom")
|
|
90807
91361
|
return true;
|
|
90808
91362
|
} catch {}
|
|
90809
91363
|
}
|
|
90810
|
-
const parent =
|
|
91364
|
+
const parent = dirname33(dir);
|
|
90811
91365
|
if (parent === dir)
|
|
90812
91366
|
break;
|
|
90813
91367
|
dir = parent;
|
|
@@ -90852,6 +91406,61 @@ function planUpdate(opts) {
|
|
|
90852
91406
|
const scriptPath = opts.scriptPath ?? process.argv[1] ?? "";
|
|
90853
91407
|
const steps = [];
|
|
90854
91408
|
const hostdContext = typeof opts.hostdContext === "boolean" ? opts.hostdContext : isHostdContext();
|
|
91409
|
+
steps.push({
|
|
91410
|
+
name: "self-update-cli",
|
|
91411
|
+
description: "Replace the host operator CLI binary with the latest published release (checksum-verified, run-proved, atomic swap + versioned rollback copy), then re-exec the new binary for the remaining steps",
|
|
91412
|
+
skipReason: opts.skipSelfUpdate ? "--skip-self-update flag set" : alreadySelfUpdated(process.env) ? "already re-exec'd from a freshly installed binary in this run" : undefined,
|
|
91413
|
+
run: async () => {
|
|
91414
|
+
const say = opts.stdout ?? ((t) => process.stdout.write(t));
|
|
91415
|
+
const io = opts.selfUpdateIO ?? defaultSelfUpdateIO();
|
|
91416
|
+
const detection = detectInstallKind(opts.installProbe ?? {
|
|
91417
|
+
bundleDir: import.meta.dirname,
|
|
91418
|
+
execPath: process.execPath,
|
|
91419
|
+
scriptPath,
|
|
91420
|
+
inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync80("/.dockerenv")
|
|
91421
|
+
});
|
|
91422
|
+
if (detection.kind !== "static-binary") {
|
|
91423
|
+
say(source_default.gray(` host CLI not self-updated: ${detection.reason}
|
|
91424
|
+
`));
|
|
91425
|
+
return;
|
|
91426
|
+
}
|
|
91427
|
+
const latestTag = opts.latestReleaseTagFn ? await opts.latestReleaseTagFn() : await fetchLatestReleaseTag(io);
|
|
91428
|
+
const plan = planSelfUpdate({
|
|
91429
|
+
detection,
|
|
91430
|
+
currentVersion: SWITCHROOM_VERSION,
|
|
91431
|
+
latestTag
|
|
91432
|
+
});
|
|
91433
|
+
if (plan.action === "skip") {
|
|
91434
|
+
say(source_default.gray(` host CLI not self-updated: ${plan.reason}
|
|
91435
|
+
`));
|
|
91436
|
+
return;
|
|
91437
|
+
}
|
|
91438
|
+
if (plan.action === "current") {
|
|
91439
|
+
say(source_default.gray(` host CLI already on ${plan.version}
|
|
91440
|
+
`));
|
|
91441
|
+
return;
|
|
91442
|
+
}
|
|
91443
|
+
const asset = releaseAssetName(process.platform, process.arch);
|
|
91444
|
+
if (!asset) {
|
|
91445
|
+
say(source_default.gray(` host CLI not self-updated: no published binary for ${process.platform}/${process.arch}
|
|
91446
|
+
`));
|
|
91447
|
+
return;
|
|
91448
|
+
}
|
|
91449
|
+
const result = await performSelfUpdate({
|
|
91450
|
+
plan,
|
|
91451
|
+
assetName: asset,
|
|
91452
|
+
io,
|
|
91453
|
+
log: (s) => say(source_default.gray(` ${s}
|
|
91454
|
+
`))
|
|
91455
|
+
});
|
|
91456
|
+
if (result.replaced && opts.selfUpdateSink) {
|
|
91457
|
+
opts.selfUpdateSink.replacedWith = result.newVersion;
|
|
91458
|
+
opts.selfUpdateSink.binaryPath = result.binaryPath;
|
|
91459
|
+
}
|
|
91460
|
+
say(source_default.green(` ${result.message}
|
|
91461
|
+
`));
|
|
91462
|
+
}
|
|
91463
|
+
});
|
|
90855
91464
|
if (opts.pin) {
|
|
90856
91465
|
const pin = opts.pin;
|
|
90857
91466
|
const persist = opts.persistPinFn ?? defaultPersistPin(opts.configPath);
|
|
@@ -90887,7 +91496,7 @@ function planUpdate(opts) {
|
|
|
90887
91496
|
steps.push({
|
|
90888
91497
|
name: "pull-images",
|
|
90889
91498
|
description: "Pull broker / kernel / agent images from GHCR",
|
|
90890
|
-
skipReason: opts.skipImages ? "--skip-images flag set" : !
|
|
91499
|
+
skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync80(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
|
|
90891
91500
|
run: () => {
|
|
90892
91501
|
const r = runner("docker", [
|
|
90893
91502
|
"compose",
|
|
@@ -91014,13 +91623,13 @@ function planUpdate(opts) {
|
|
|
91014
91623
|
}
|
|
91015
91624
|
const source = resolve45(import.meta.dirname, "../../skills");
|
|
91016
91625
|
const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
|
|
91017
|
-
if (!
|
|
91626
|
+
if (!existsSync80(source)) {
|
|
91018
91627
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
91019
91628
|
`);
|
|
91020
91629
|
return;
|
|
91021
91630
|
}
|
|
91022
91631
|
try {
|
|
91023
|
-
|
|
91632
|
+
mkdirSync45(dirname33(dest), { recursive: true });
|
|
91024
91633
|
const r = syncBundledSkills({
|
|
91025
91634
|
source,
|
|
91026
91635
|
dest,
|
|
@@ -91046,10 +91655,10 @@ function planUpdate(opts) {
|
|
|
91046
91655
|
if (opts.syncBundledSkillsFn)
|
|
91047
91656
|
return;
|
|
91048
91657
|
const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
|
|
91049
|
-
if (!
|
|
91658
|
+
if (!existsSync80(dest)) {
|
|
91050
91659
|
return;
|
|
91051
91660
|
}
|
|
91052
|
-
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !
|
|
91661
|
+
const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync80(join83(dest, key)));
|
|
91053
91662
|
if (missing.length > 0) {
|
|
91054
91663
|
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.`);
|
|
91055
91664
|
}
|
|
@@ -91118,7 +91727,7 @@ function planUpdate(opts) {
|
|
|
91118
91727
|
return steps;
|
|
91119
91728
|
}
|
|
91120
91729
|
function defaultRunner2(cmd, args) {
|
|
91121
|
-
const r =
|
|
91730
|
+
const r = spawnSync16(cmd, args, { stdio: "inherit" });
|
|
91122
91731
|
return { status: r.status ?? 1 };
|
|
91123
91732
|
}
|
|
91124
91733
|
function writeMarkerInPreferredLocation(agent, reason, runner) {
|
|
@@ -91161,10 +91770,10 @@ function defaultStatusProbe(composePath) {
|
|
|
91161
91770
|
try {
|
|
91162
91771
|
cliBuiltAt = new Date(statSync45(scriptPath).mtimeMs).toISOString();
|
|
91163
91772
|
} catch {}
|
|
91164
|
-
let dir =
|
|
91773
|
+
let dir = dirname33(scriptPath);
|
|
91165
91774
|
for (let i = 0;i < 8; i++) {
|
|
91166
91775
|
const pkgPath = join83(dir, "package.json");
|
|
91167
|
-
if (
|
|
91776
|
+
if (existsSync80(pkgPath)) {
|
|
91168
91777
|
try {
|
|
91169
91778
|
const pkg = JSON.parse(readFileSync73(pkgPath, "utf-8"));
|
|
91170
91779
|
if (typeof pkg.version === "string")
|
|
@@ -91174,7 +91783,7 @@ function defaultStatusProbe(composePath) {
|
|
|
91174
91783
|
}
|
|
91175
91784
|
break;
|
|
91176
91785
|
}
|
|
91177
|
-
const parent =
|
|
91786
|
+
const parent = dirname33(dir);
|
|
91178
91787
|
if (parent === dir)
|
|
91179
91788
|
break;
|
|
91180
91789
|
dir = parent;
|
|
@@ -91187,13 +91796,13 @@ function defaultStatusProbe(composePath) {
|
|
|
91187
91796
|
warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
|
|
91188
91797
|
}
|
|
91189
91798
|
const services = [];
|
|
91190
|
-
if (!
|
|
91799
|
+
if (!existsSync80(composePath)) {
|
|
91191
91800
|
warnings.push(`compose file not found at ${composePath}; service status unknown`);
|
|
91192
91801
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
91193
91802
|
}
|
|
91194
91803
|
let serviceList = [];
|
|
91195
91804
|
try {
|
|
91196
|
-
const r =
|
|
91805
|
+
const r = spawnSync16("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
|
|
91197
91806
|
if (r.status !== 0) {
|
|
91198
91807
|
warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
|
|
91199
91808
|
return { cliVersion, cliBuiltAt, services, warnings };
|
|
@@ -91210,7 +91819,7 @@ function defaultStatusProbe(composePath) {
|
|
|
91210
91819
|
let containerCreatedAt = null;
|
|
91211
91820
|
let status = "<unknown>";
|
|
91212
91821
|
try {
|
|
91213
|
-
const r =
|
|
91822
|
+
const r = spawnSync16("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
|
|
91214
91823
|
if (r.status === 0) {
|
|
91215
91824
|
const [img, created, st] = r.stdout.trim().split("|");
|
|
91216
91825
|
image = img ?? null;
|
|
@@ -91226,7 +91835,7 @@ function defaultStatusProbe(composePath) {
|
|
|
91226
91835
|
let imagePulledAt = null;
|
|
91227
91836
|
if (image) {
|
|
91228
91837
|
try {
|
|
91229
|
-
const r =
|
|
91838
|
+
const r = spawnSync16("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
|
|
91230
91839
|
if (r.status === 0) {
|
|
91231
91840
|
const [id, created, lastTag] = r.stdout.trim().split("|");
|
|
91232
91841
|
imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
|
|
@@ -91290,6 +91899,47 @@ function formatStatusReport(rep) {
|
|
|
91290
91899
|
return lines.join(`
|
|
91291
91900
|
`);
|
|
91292
91901
|
}
|
|
91902
|
+
function buildReexecArgs(opts) {
|
|
91903
|
+
const args = ["update", "--skip-self-update"];
|
|
91904
|
+
if (opts.skipImages)
|
|
91905
|
+
args.push("--skip-images");
|
|
91906
|
+
if (opts.channel)
|
|
91907
|
+
args.push("--channel", opts.channel);
|
|
91908
|
+
if (opts.pin)
|
|
91909
|
+
args.push("--pin", opts.pin);
|
|
91910
|
+
return args;
|
|
91911
|
+
}
|
|
91912
|
+
function defaultReexec(binaryPath, args) {
|
|
91913
|
+
const r = spawnSync16(binaryPath, args, {
|
|
91914
|
+
stdio: "inherit",
|
|
91915
|
+
env: { ...process.env, [SELF_UPDATE_ENV_SENTINEL]: "1" }
|
|
91916
|
+
});
|
|
91917
|
+
return { status: r.status ?? 1 };
|
|
91918
|
+
}
|
|
91919
|
+
function renderDriftPreamble(opts) {
|
|
91920
|
+
try {
|
|
91921
|
+
const exec = opts.componentExec ?? ((cmd, args) => {
|
|
91922
|
+
const r = spawnSync16(cmd, args, { encoding: "utf-8", timeout: 1e4 });
|
|
91923
|
+
return { status: r.status ?? 1, stdout: r.stdout ?? "" };
|
|
91924
|
+
});
|
|
91925
|
+
let pin;
|
|
91926
|
+
try {
|
|
91927
|
+
pin = loadConfig().release?.pin ?? undefined;
|
|
91928
|
+
} catch {
|
|
91929
|
+
pin = undefined;
|
|
91930
|
+
}
|
|
91931
|
+
const report = detectComponentDrift(collectComponents(SWITCHROOM_VERSION, exec), pin);
|
|
91932
|
+
const body = formatComponentDrift(report);
|
|
91933
|
+
return report.behind.length > 0 ? `${body}
|
|
91934
|
+
|
|
91935
|
+
${report.behind.length} component(s) BEHIND ${report.target} \u2014 this update targets them.
|
|
91936
|
+
` : `${body}
|
|
91937
|
+
`;
|
|
91938
|
+
} catch (err) {
|
|
91939
|
+
return `Component versions: not checkable (${err.message})
|
|
91940
|
+
`;
|
|
91941
|
+
}
|
|
91942
|
+
}
|
|
91293
91943
|
async function runUpdate(opts) {
|
|
91294
91944
|
const stdout = opts.stdout ?? ((s) => process.stdout.write(s));
|
|
91295
91945
|
const stderr = opts.stderr ?? ((s) => process.stderr.write(s));
|
|
@@ -91319,11 +91969,14 @@ async function runUpdate(opts) {
|
|
|
91319
91969
|
return 2;
|
|
91320
91970
|
}
|
|
91321
91971
|
}
|
|
91322
|
-
const
|
|
91972
|
+
const selfUpdateSink = opts.selfUpdateSink ?? {};
|
|
91973
|
+
const steps = planUpdate({ ...opts, selfUpdateSink });
|
|
91323
91974
|
if (opts.check) {
|
|
91324
91975
|
stdout(source_default.bold(`switchroom update --check (dry-run)
|
|
91325
91976
|
|
|
91326
91977
|
`));
|
|
91978
|
+
stdout(renderDriftPreamble(opts) + `
|
|
91979
|
+
`);
|
|
91327
91980
|
for (const step of steps) {
|
|
91328
91981
|
const status = step.skipReason ? source_default.gray(`[skip] ${step.skipReason}`) : source_default.green("[run]");
|
|
91329
91982
|
stdout(` ${status} ${step.name} \u2014 ${step.description}
|
|
@@ -91343,7 +91996,15 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
|
|
|
91343
91996
|
stdout(source_default.bold(`\u25b8 ${step.name}
|
|
91344
91997
|
`));
|
|
91345
91998
|
try {
|
|
91346
|
-
step.run();
|
|
91999
|
+
await step.run();
|
|
92000
|
+
if (step.name === "self-update-cli" && selfUpdateSink.binaryPath) {
|
|
92001
|
+
const reexec = opts.reexecFn ?? defaultReexec;
|
|
92002
|
+
stdout(source_default.bold(`
|
|
92003
|
+
\u25b8 re-exec ${selfUpdateSink.binaryPath} (${selfUpdateSink.replacedWith}) for the remaining steps
|
|
92004
|
+
`));
|
|
92005
|
+
const r = reexec(selfUpdateSink.binaryPath, buildReexecArgs(opts));
|
|
92006
|
+
return r.status;
|
|
92007
|
+
}
|
|
91347
92008
|
} catch (err) {
|
|
91348
92009
|
stderr(source_default.red(`\u2717 ${step.name} failed: ${err.message}
|
|
91349
92010
|
`));
|
|
@@ -91366,7 +92027,7 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
|
|
|
91366
92027
|
return 0;
|
|
91367
92028
|
}
|
|
91368
92029
|
function registerUpdateCommand(program3) {
|
|
91369
|
-
program3.command("update").description("Update switchroom on this host
|
|
92030
|
+
program3.command("update").description("Update EVERY switchroom component on this host \u2014 the operator CLI itself (first), images, scaffolds, the hostd / web / hindsight singletons, and the agent fleet. Wraps the full `self-update && pull && apply && up -d` flow.").option("--check", "Dry-run: print the steps that would execute, exit 0.").option("--skip-images", "Skip the docker image pull (offline mode).").option("--skip-self-update", "Do NOT replace the host operator CLI binary. By default `update` updates every switchroom component INCLUDING itself (and does it first, because `apply` renders scaffolds from templates shipped inside the CLI).").option("--rebuild", "Source-checkout / maintainer only: git pull + bun install + npm run build before applying. REFUSED on a published install \u2014 use `npm i -g switchroom@latest && switchroom update` there.").option("--status", "Read-only snapshot: report local CLI version, image digest + pull time, container creation time per service. Does NOT invoke any update steps. Wired by Telegram /upgrade-status (#927).").option("--json", "Output as JSON (currently only honored under --status; other modes ignore).").addOption(new Option("--channel <c>", "Override the resolved release block for this update run: follow the named channel (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved release block for this update run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--force", "[legacy v0.6 no-op]").option("--no-restart", "[legacy v0.6 no-op]").option("--resume <file>", "[legacy v0.6 no-op]").option("--phase <phase>", "[legacy v0.6 no-op]").action(async (opts) => {
|
|
91370
92031
|
if (opts.pin && !/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/.test(opts.pin)) {
|
|
91371
92032
|
console.error(source_default.red(`--pin "${opts.pin}" is invalid. Expected sha-<7-40 hex> or v<semver>.`));
|
|
91372
92033
|
process.exit(2);
|
|
@@ -91382,23 +92043,23 @@ function registerUpdateCommand(program3) {
|
|
|
91382
92043
|
|
|
91383
92044
|
// src/cli/rollout.ts
|
|
91384
92045
|
init_helpers();
|
|
91385
|
-
import { spawnSync as
|
|
92046
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
91386
92047
|
import { readFileSync as readFileSync75, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
|
|
91387
92048
|
import { homedir as homedir45 } from "node:os";
|
|
91388
92049
|
|
|
91389
92050
|
// src/cli/rollout-pin-journal.ts
|
|
91390
92051
|
import {
|
|
91391
|
-
existsSync as
|
|
92052
|
+
existsSync as existsSync81,
|
|
91392
92053
|
readFileSync as readFileSync74,
|
|
91393
92054
|
writeFileSync as writeFileSync31,
|
|
91394
|
-
renameSync as
|
|
92055
|
+
renameSync as renameSync20,
|
|
91395
92056
|
unlinkSync as unlinkSync15,
|
|
91396
92057
|
statSync as statSync46,
|
|
91397
|
-
mkdirSync as
|
|
92058
|
+
mkdirSync as mkdirSync46
|
|
91398
92059
|
} from "node:fs";
|
|
91399
92060
|
import { homedir as homedir44 } from "node:os";
|
|
91400
|
-
import { createHash as
|
|
91401
|
-
import { join as join84, basename as basename9, resolve as resolve46, dirname as
|
|
92061
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
92062
|
+
import { join as join84, basename as basename9, resolve as resolve46, dirname as dirname34 } from "node:path";
|
|
91402
92063
|
init_flock();
|
|
91403
92064
|
var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
|
|
91404
92065
|
var STATE_DIR_NAME = ".switchroom";
|
|
@@ -91407,7 +92068,7 @@ function pinJournalDir(configPath) {
|
|
|
91407
92068
|
if (override && override.trim().length > 0)
|
|
91408
92069
|
return override.trim();
|
|
91409
92070
|
if (configPath) {
|
|
91410
|
-
const dir =
|
|
92071
|
+
const dir = dirname34(resolve46(configPath));
|
|
91411
92072
|
if (basename9(dir) === STATE_DIR_NAME)
|
|
91412
92073
|
return dir;
|
|
91413
92074
|
}
|
|
@@ -91415,7 +92076,7 @@ function pinJournalDir(configPath) {
|
|
|
91415
92076
|
}
|
|
91416
92077
|
function pinJournalPath(configPath) {
|
|
91417
92078
|
const abs = resolve46(configPath);
|
|
91418
|
-
const key =
|
|
92079
|
+
const key = createHash17("sha256").update(abs).digest("hex").slice(0, 12);
|
|
91419
92080
|
return join84(pinJournalDir(abs), `.rollout-pin-journal.${basename9(abs)}.${key}.json`);
|
|
91420
92081
|
}
|
|
91421
92082
|
function isPidAlive(pid) {
|
|
@@ -91452,7 +92113,7 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
|
|
|
91452
92113
|
try {
|
|
91453
92114
|
raw = readFileSync74(p, "utf8");
|
|
91454
92115
|
} catch (e) {
|
|
91455
|
-
if (
|
|
92116
|
+
if (existsSync81(p)) {
|
|
91456
92117
|
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.
|
|
91457
92118
|
`);
|
|
91458
92119
|
}
|
|
@@ -91512,10 +92173,10 @@ function beginPinPersist(configPath, pin, opts = {}) {
|
|
|
91512
92173
|
pid: process.pid,
|
|
91513
92174
|
at: new Date().toISOString()
|
|
91514
92175
|
};
|
|
91515
|
-
|
|
92176
|
+
mkdirSync46(dirname34(p), { recursive: true });
|
|
91516
92177
|
const tmp = `${p}.${process.pid}.tmp`;
|
|
91517
92178
|
writeFileSync31(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
|
|
91518
|
-
|
|
92179
|
+
renameSync20(tmp, p);
|
|
91519
92180
|
return journal;
|
|
91520
92181
|
}
|
|
91521
92182
|
function commitPinPersist(configPath) {
|
|
@@ -91524,7 +92185,7 @@ function commitPinPersist(configPath) {
|
|
|
91524
92185
|
unlinkSync15(p);
|
|
91525
92186
|
return null;
|
|
91526
92187
|
} catch (e) {
|
|
91527
|
-
if (!
|
|
92188
|
+
if (!existsSync81(p))
|
|
91528
92189
|
return null;
|
|
91529
92190
|
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\`.`;
|
|
91530
92191
|
}
|
|
@@ -91602,10 +92263,10 @@ init_audit_reader();
|
|
|
91602
92263
|
init_hindsight();
|
|
91603
92264
|
|
|
91604
92265
|
// src/cli/deploy-version-guard.ts
|
|
91605
|
-
import { spawnSync as
|
|
92266
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
91606
92267
|
var DOCKER_INSPECT_TIMEOUT_MS = 60 * 1000;
|
|
91607
92268
|
var defaultRunner3 = (args) => {
|
|
91608
|
-
const r =
|
|
92269
|
+
const r = spawnSync17("docker", args, {
|
|
91609
92270
|
encoding: "utf8",
|
|
91610
92271
|
timeout: DOCKER_INSPECT_TIMEOUT_MS,
|
|
91611
92272
|
killSignal: "SIGKILL"
|
|
@@ -91979,7 +92640,7 @@ function isSpawnTimeout(r, killSignal) {
|
|
|
91979
92640
|
var ROLLOUT_KILL_SIGNAL = "SIGKILL";
|
|
91980
92641
|
function createRolloutDeps(params) {
|
|
91981
92642
|
const { configPath, scriptPath, hostdCtx } = params;
|
|
91982
|
-
const spawn6 = params.spawn ??
|
|
92643
|
+
const spawn6 = params.spawn ?? spawnSync18;
|
|
91983
92644
|
const warn = params.warn ?? ((line) => process.stderr.write(line));
|
|
91984
92645
|
const dockerRun = (args) => {
|
|
91985
92646
|
let r;
|
|
@@ -92233,8 +92894,8 @@ init_helpers();
|
|
|
92233
92894
|
init_lifecycle();
|
|
92234
92895
|
init_resolve_version();
|
|
92235
92896
|
import { execSync as execSync3 } from "node:child_process";
|
|
92236
|
-
import { existsSync as
|
|
92237
|
-
import { dirname as
|
|
92897
|
+
import { existsSync as existsSync82, readFileSync as readFileSync76 } from "node:fs";
|
|
92898
|
+
import { dirname as dirname35, join as join85 } from "node:path";
|
|
92238
92899
|
function getClaudeCodeVersion() {
|
|
92239
92900
|
try {
|
|
92240
92901
|
const out = execSync3("claude --version 2>/dev/null", {
|
|
@@ -92285,15 +92946,15 @@ function locateSwitchroomInstallDir() {
|
|
|
92285
92946
|
let dir = import.meta.dirname;
|
|
92286
92947
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
92287
92948
|
const pkgPath = join85(dir, "package.json");
|
|
92288
|
-
if (
|
|
92949
|
+
if (existsSync82(pkgPath)) {
|
|
92289
92950
|
try {
|
|
92290
92951
|
const pkg = JSON.parse(readFileSync76(pkgPath, "utf-8"));
|
|
92291
|
-
if (pkg.name === "switchroom" &&
|
|
92952
|
+
if (pkg.name === "switchroom" && existsSync82(join85(dir, ".git"))) {
|
|
92292
92953
|
return dir;
|
|
92293
92954
|
}
|
|
92294
92955
|
} catch {}
|
|
92295
92956
|
}
|
|
92296
|
-
dir =
|
|
92957
|
+
dir = dirname35(dir);
|
|
92297
92958
|
}
|
|
92298
92959
|
return null;
|
|
92299
92960
|
}
|
|
@@ -92463,10 +93124,11 @@ Dependency manifest`));
|
|
|
92463
93124
|
init_helpers();
|
|
92464
93125
|
init_loader();
|
|
92465
93126
|
import { resolve as resolve48 } from "node:path";
|
|
93127
|
+
init_merge();
|
|
92466
93128
|
|
|
92467
93129
|
// src/agents/session-retention.ts
|
|
92468
93130
|
import {
|
|
92469
|
-
existsSync as
|
|
93131
|
+
existsSync as existsSync83,
|
|
92470
93132
|
readdirSync as readdirSync28,
|
|
92471
93133
|
statSync as statSync48,
|
|
92472
93134
|
unlinkSync as unlinkSync16
|
|
@@ -92477,7 +93139,7 @@ var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
|
|
|
92477
93139
|
var MIN_KEEP = 2;
|
|
92478
93140
|
function collectSessionJsonl(claudeConfigDir) {
|
|
92479
93141
|
const projects = join86(claudeConfigDir, "projects");
|
|
92480
|
-
if (!
|
|
93142
|
+
if (!existsSync83(projects))
|
|
92481
93143
|
return [];
|
|
92482
93144
|
const found = [];
|
|
92483
93145
|
const walk2 = (dir) => {
|
|
@@ -92554,12 +93216,13 @@ function registerHandoffCommand(program3) {
|
|
|
92554
93216
|
let agentDir;
|
|
92555
93217
|
try {
|
|
92556
93218
|
const config = getConfig(program3);
|
|
92557
|
-
|
|
92558
|
-
if (!
|
|
93219
|
+
const rawAgent = config.agents[agentName];
|
|
93220
|
+
if (!rawAgent) {
|
|
92559
93221
|
process.stderr.write(`handoff: agent "${agentName}" not defined in switchroom.yaml
|
|
92560
93222
|
`);
|
|
92561
93223
|
return;
|
|
92562
93224
|
}
|
|
93225
|
+
agentConfig = resolveAgentConfig(config.defaults, config.profiles, rawAgent);
|
|
92563
93226
|
const agentsDir = resolveAgentsDir(config);
|
|
92564
93227
|
agentDir = resolve48(agentsDir, agentName);
|
|
92565
93228
|
} catch (err) {
|
|
@@ -92589,10 +93252,14 @@ function registerHandoffCommand(program3) {
|
|
|
92589
93252
|
jsonlPath: jsonl,
|
|
92590
93253
|
agentDir,
|
|
92591
93254
|
agentName,
|
|
92592
|
-
maxTurns: cappedMaxTurns
|
|
93255
|
+
maxTurns: cappedMaxTurns,
|
|
93256
|
+
observationScopes: agentConfig?.memory?.observation_scopes
|
|
92593
93257
|
});
|
|
92594
93258
|
process.stderr.write(`handoff: ${status}
|
|
92595
93259
|
`);
|
|
93260
|
+
if (status === HANDOFF_STATUS_MIRROR_SKIPPED) {
|
|
93261
|
+
process.exitCode = 1;
|
|
93262
|
+
}
|
|
92596
93263
|
const retention = pruneSessionJsonl(claudeConfigDir, {
|
|
92597
93264
|
maxCount: continuity?.session_retention_max_count ?? DEFAULT_SESSION_RETENTION_MAX_COUNT,
|
|
92598
93265
|
maxAgeDays: continuity?.session_retention_max_age_days ?? DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS,
|
|
@@ -92607,13 +93274,13 @@ function registerHandoffCommand(program3) {
|
|
|
92607
93274
|
|
|
92608
93275
|
// src/issues/store.ts
|
|
92609
93276
|
import {
|
|
92610
|
-
closeSync as
|
|
92611
|
-
existsSync as
|
|
92612
|
-
mkdirSync as
|
|
92613
|
-
openSync as
|
|
93277
|
+
closeSync as closeSync16,
|
|
93278
|
+
existsSync as existsSync84,
|
|
93279
|
+
mkdirSync as mkdirSync47,
|
|
93280
|
+
openSync as openSync16,
|
|
92614
93281
|
readdirSync as readdirSync29,
|
|
92615
93282
|
readFileSync as readFileSync77,
|
|
92616
|
-
renameSync as
|
|
93283
|
+
renameSync as renameSync21,
|
|
92617
93284
|
statSync as statSync49,
|
|
92618
93285
|
unlinkSync as unlinkSync17,
|
|
92619
93286
|
writeFileSync as writeFileSync32,
|
|
@@ -93051,7 +93718,7 @@ var ISSUES_FILE = "issues.jsonl";
|
|
|
93051
93718
|
var ISSUES_LOCK = "issues.lock";
|
|
93052
93719
|
function readAll(stateDir) {
|
|
93053
93720
|
const path7 = join87(stateDir, ISSUES_FILE);
|
|
93054
|
-
if (!
|
|
93721
|
+
if (!existsSync84(path7))
|
|
93055
93722
|
return [];
|
|
93056
93723
|
let raw;
|
|
93057
93724
|
try {
|
|
@@ -93128,7 +93795,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
93128
93795
|
});
|
|
93129
93796
|
}
|
|
93130
93797
|
function resolve49(stateDir, fingerprint, nowFn = Date.now) {
|
|
93131
|
-
if (!
|
|
93798
|
+
if (!existsSync84(join87(stateDir, ISSUES_FILE)))
|
|
93132
93799
|
return 0;
|
|
93133
93800
|
return withLock(stateDir, () => {
|
|
93134
93801
|
const all = readAll(stateDir);
|
|
@@ -93146,7 +93813,7 @@ function resolve49(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
93146
93813
|
});
|
|
93147
93814
|
}
|
|
93148
93815
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
93149
|
-
if (!
|
|
93816
|
+
if (!existsSync84(join87(stateDir, ISSUES_FILE)))
|
|
93150
93817
|
return 0;
|
|
93151
93818
|
return withLock(stateDir, () => {
|
|
93152
93819
|
const all = readAll(stateDir);
|
|
@@ -93164,7 +93831,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
93164
93831
|
});
|
|
93165
93832
|
}
|
|
93166
93833
|
function prune(stateDir, opts = {}) {
|
|
93167
|
-
if (!
|
|
93834
|
+
if (!existsSync84(join87(stateDir, ISSUES_FILE)))
|
|
93168
93835
|
return 0;
|
|
93169
93836
|
return withLock(stateDir, () => {
|
|
93170
93837
|
const all = readAll(stateDir);
|
|
@@ -93194,7 +93861,7 @@ function prune(stateDir, opts = {}) {
|
|
|
93194
93861
|
});
|
|
93195
93862
|
}
|
|
93196
93863
|
function ensureDir(stateDir) {
|
|
93197
|
-
|
|
93864
|
+
mkdirSync47(stateDir, { recursive: true });
|
|
93198
93865
|
}
|
|
93199
93866
|
function writeAll(stateDir, events) {
|
|
93200
93867
|
const path7 = join87(stateDir, ISSUES_FILE);
|
|
@@ -93204,7 +93871,7 @@ function writeAll(stateDir, events) {
|
|
|
93204
93871
|
`) + `
|
|
93205
93872
|
`;
|
|
93206
93873
|
writeFileSync32(tmp, body, "utf-8");
|
|
93207
|
-
|
|
93874
|
+
renameSync21(tmp, path7);
|
|
93208
93875
|
}
|
|
93209
93876
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
93210
93877
|
var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
|
|
@@ -93236,7 +93903,7 @@ function withLock(stateDir, fn) {
|
|
|
93236
93903
|
let fd = null;
|
|
93237
93904
|
while (fd === null) {
|
|
93238
93905
|
try {
|
|
93239
|
-
fd =
|
|
93906
|
+
fd = openSync16(lockPath, "wx");
|
|
93240
93907
|
try {
|
|
93241
93908
|
writeSync9(fd, String(process.pid));
|
|
93242
93909
|
} catch {}
|
|
@@ -93256,7 +93923,7 @@ function withLock(stateDir, fn) {
|
|
|
93256
93923
|
return fn();
|
|
93257
93924
|
} finally {
|
|
93258
93925
|
try {
|
|
93259
|
-
|
|
93926
|
+
closeSync16(fd);
|
|
93260
93927
|
} catch {}
|
|
93261
93928
|
try {
|
|
93262
93929
|
unlinkSync17(lockPath);
|
|
@@ -93514,20 +94181,20 @@ function relTime(deltaMs) {
|
|
|
93514
94181
|
|
|
93515
94182
|
// src/cli/deps.ts
|
|
93516
94183
|
init_source();
|
|
93517
|
-
import { existsSync as
|
|
94184
|
+
import { existsSync as existsSync87 } from "node:fs";
|
|
93518
94185
|
import { homedir as homedir48 } from "node:os";
|
|
93519
94186
|
import { join as join90, resolve as resolve50 } from "node:path";
|
|
93520
94187
|
|
|
93521
94188
|
// src/deps/python.ts
|
|
93522
|
-
import { createHash as
|
|
94189
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
93523
94190
|
import {
|
|
93524
|
-
existsSync as
|
|
93525
|
-
mkdirSync as
|
|
94191
|
+
existsSync as existsSync85,
|
|
94192
|
+
mkdirSync as mkdirSync48,
|
|
93526
94193
|
readFileSync as readFileSync78,
|
|
93527
|
-
rmSync as
|
|
94194
|
+
rmSync as rmSync15,
|
|
93528
94195
|
writeFileSync as writeFileSync33
|
|
93529
94196
|
} from "node:fs";
|
|
93530
|
-
import { dirname as
|
|
94197
|
+
import { dirname as dirname36, join as join88 } from "node:path";
|
|
93531
94198
|
import { homedir as homedir46 } from "node:os";
|
|
93532
94199
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
93533
94200
|
|
|
@@ -93543,13 +94210,13 @@ function defaultPythonCacheRoot() {
|
|
|
93543
94210
|
return join88(homedir46(), ".switchroom", "deps", "python");
|
|
93544
94211
|
}
|
|
93545
94212
|
function hashFile(path7) {
|
|
93546
|
-
return
|
|
94213
|
+
return createHash18("sha256").update(readFileSync78(path7)).digest("hex");
|
|
93547
94214
|
}
|
|
93548
94215
|
function ensurePythonEnv(opts) {
|
|
93549
94216
|
const { skillName, requirementsPath, force = false } = opts;
|
|
93550
94217
|
const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
|
|
93551
94218
|
const hostPython = opts.pythonBin ?? "python3";
|
|
93552
|
-
if (!
|
|
94219
|
+
if (!existsSync85(requirementsPath)) {
|
|
93553
94220
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
93554
94221
|
}
|
|
93555
94222
|
const venvDir = join88(cacheRoot, skillName);
|
|
@@ -93558,7 +94225,7 @@ function ensurePythonEnv(opts) {
|
|
|
93558
94225
|
const pythonBin = join88(binDir, "python");
|
|
93559
94226
|
const pipBin = join88(binDir, "pip");
|
|
93560
94227
|
const targetHash = hashFile(requirementsPath);
|
|
93561
|
-
if (!force &&
|
|
94228
|
+
if (!force && existsSync85(stampPath) && existsSync85(pythonBin)) {
|
|
93562
94229
|
const existingHash = readFileSync78(stampPath, "utf8").trim();
|
|
93563
94230
|
if (existingHash === targetHash) {
|
|
93564
94231
|
return {
|
|
@@ -93571,10 +94238,10 @@ function ensurePythonEnv(opts) {
|
|
|
93571
94238
|
};
|
|
93572
94239
|
}
|
|
93573
94240
|
}
|
|
93574
|
-
if (
|
|
93575
|
-
|
|
94241
|
+
if (existsSync85(venvDir)) {
|
|
94242
|
+
rmSync15(venvDir, { recursive: true, force: true });
|
|
93576
94243
|
}
|
|
93577
|
-
|
|
94244
|
+
mkdirSync48(dirname36(venvDir), { recursive: true });
|
|
93578
94245
|
try {
|
|
93579
94246
|
execFileSync24(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
93580
94247
|
} catch (err) {
|
|
@@ -93606,16 +94273,16 @@ function ensurePythonEnv(opts) {
|
|
|
93606
94273
|
}
|
|
93607
94274
|
|
|
93608
94275
|
// src/deps/node.ts
|
|
93609
|
-
import { createHash as
|
|
94276
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
93610
94277
|
import {
|
|
93611
|
-
copyFileSync as
|
|
93612
|
-
existsSync as
|
|
93613
|
-
mkdirSync as
|
|
94278
|
+
copyFileSync as copyFileSync12,
|
|
94279
|
+
existsSync as existsSync86,
|
|
94280
|
+
mkdirSync as mkdirSync49,
|
|
93614
94281
|
readFileSync as readFileSync79,
|
|
93615
|
-
rmSync as
|
|
94282
|
+
rmSync as rmSync16,
|
|
93616
94283
|
writeFileSync as writeFileSync34
|
|
93617
94284
|
} from "node:fs";
|
|
93618
|
-
import { dirname as
|
|
94285
|
+
import { dirname as dirname37, join as join89 } from "node:path";
|
|
93619
94286
|
import { homedir as homedir47 } from "node:os";
|
|
93620
94287
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
93621
94288
|
|
|
@@ -93642,14 +94309,14 @@ function defaultNodeCacheRoot() {
|
|
|
93642
94309
|
return join89(homedir47(), ".switchroom", "deps", "node");
|
|
93643
94310
|
}
|
|
93644
94311
|
function hashDepInputs(packageJsonPath) {
|
|
93645
|
-
const sourceDir =
|
|
93646
|
-
const hasher =
|
|
94312
|
+
const sourceDir = dirname37(packageJsonPath);
|
|
94313
|
+
const hasher = createHash19("sha256");
|
|
93647
94314
|
hasher.update(`package.json
|
|
93648
94315
|
`);
|
|
93649
94316
|
hasher.update(readFileSync79(packageJsonPath));
|
|
93650
94317
|
for (const lockName of ALL_LOCKFILES) {
|
|
93651
94318
|
const lockPath = join89(sourceDir, lockName);
|
|
93652
|
-
if (
|
|
94319
|
+
if (existsSync86(lockPath)) {
|
|
93653
94320
|
hasher.update(`
|
|
93654
94321
|
`);
|
|
93655
94322
|
hasher.update(lockName);
|
|
@@ -93664,16 +94331,16 @@ function ensureNodeEnv(opts) {
|
|
|
93664
94331
|
const { skillName, packageJsonPath, force = false } = opts;
|
|
93665
94332
|
const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
|
|
93666
94333
|
const installer = opts.installer ?? "bun";
|
|
93667
|
-
if (!
|
|
94334
|
+
if (!existsSync86(packageJsonPath)) {
|
|
93668
94335
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
93669
94336
|
}
|
|
93670
|
-
const sourceDir =
|
|
94337
|
+
const sourceDir = dirname37(packageJsonPath);
|
|
93671
94338
|
const envDir = join89(cacheRoot, skillName);
|
|
93672
94339
|
const stampPath = join89(envDir, ".package.sha256");
|
|
93673
94340
|
const nodeModulesDir = join89(envDir, "node_modules");
|
|
93674
94341
|
const binDir = join89(nodeModulesDir, ".bin");
|
|
93675
94342
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
93676
|
-
if (!force &&
|
|
94343
|
+
if (!force && existsSync86(stampPath) && existsSync86(nodeModulesDir)) {
|
|
93677
94344
|
const existingHash = readFileSync79(stampPath, "utf8").trim();
|
|
93678
94345
|
if (existingHash === targetHash) {
|
|
93679
94346
|
return {
|
|
@@ -93685,16 +94352,16 @@ function ensureNodeEnv(opts) {
|
|
|
93685
94352
|
};
|
|
93686
94353
|
}
|
|
93687
94354
|
}
|
|
93688
|
-
if (
|
|
93689
|
-
|
|
94355
|
+
if (existsSync86(envDir)) {
|
|
94356
|
+
rmSync16(envDir, { recursive: true, force: true });
|
|
93690
94357
|
}
|
|
93691
|
-
|
|
93692
|
-
|
|
94358
|
+
mkdirSync49(envDir, { recursive: true });
|
|
94359
|
+
copyFileSync12(packageJsonPath, join89(envDir, "package.json"));
|
|
93693
94360
|
let copiedLockfile = false;
|
|
93694
94361
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
93695
94362
|
const lockPath = join89(sourceDir, lockName);
|
|
93696
|
-
if (
|
|
93697
|
-
|
|
94363
|
+
if (existsSync86(lockPath)) {
|
|
94364
|
+
copyFileSync12(lockPath, join89(envDir, lockName));
|
|
93698
94365
|
copiedLockfile = true;
|
|
93699
94366
|
}
|
|
93700
94367
|
}
|
|
@@ -93729,22 +94396,22 @@ function registerDepsCommand(program3) {
|
|
|
93729
94396
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
93730
94397
|
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) => {
|
|
93731
94398
|
const skillsRoot = builtinSkillsRoot();
|
|
93732
|
-
if (!
|
|
94399
|
+
if (!existsSync87(skillsRoot)) {
|
|
93733
94400
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
93734
94401
|
process.exit(1);
|
|
93735
94402
|
}
|
|
93736
94403
|
const skillDir = join90(skillsRoot, skill);
|
|
93737
|
-
if (!
|
|
94404
|
+
if (!existsSync87(skillDir)) {
|
|
93738
94405
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
93739
94406
|
process.exit(1);
|
|
93740
94407
|
}
|
|
93741
94408
|
const requirementsPath = join90(skillDir, "requirements.txt");
|
|
93742
94409
|
const packageJsonPath = join90(skillDir, "package.json");
|
|
93743
|
-
const wantPython = opts.python ?? (!opts.python && !opts.node &&
|
|
93744
|
-
const wantNode = opts.node ?? (!opts.python && !opts.node &&
|
|
94410
|
+
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync87(requirementsPath));
|
|
94411
|
+
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync87(packageJsonPath));
|
|
93745
94412
|
let did = 0;
|
|
93746
94413
|
if (wantPython) {
|
|
93747
|
-
if (!
|
|
94414
|
+
if (!existsSync87(requirementsPath)) {
|
|
93748
94415
|
console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
|
|
93749
94416
|
process.exit(1);
|
|
93750
94417
|
}
|
|
@@ -93768,7 +94435,7 @@ function registerDepsCommand(program3) {
|
|
|
93768
94435
|
}
|
|
93769
94436
|
}
|
|
93770
94437
|
if (wantNode) {
|
|
93771
|
-
if (!
|
|
94438
|
+
if (!existsSync87(packageJsonPath)) {
|
|
93772
94439
|
console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
|
|
93773
94440
|
process.exit(1);
|
|
93774
94441
|
}
|
|
@@ -93801,9 +94468,9 @@ function registerDepsCommand(program3) {
|
|
|
93801
94468
|
// src/cli/workspace.ts
|
|
93802
94469
|
init_helpers();
|
|
93803
94470
|
init_loader();
|
|
93804
|
-
import { existsSync as
|
|
94471
|
+
import { existsSync as existsSync88 } from "node:fs";
|
|
93805
94472
|
import { resolve as resolve51, sep as sep4 } from "node:path";
|
|
93806
|
-
import { spawnSync as
|
|
94473
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
93807
94474
|
|
|
93808
94475
|
// src/agents/workspace.ts
|
|
93809
94476
|
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
@@ -94516,7 +95183,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
94516
95183
|
process.exit(1);
|
|
94517
95184
|
}
|
|
94518
95185
|
const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
94519
|
-
const child =
|
|
95186
|
+
const child = spawnSync19(editor, [target], { stdio: "inherit" });
|
|
94520
95187
|
if (child.status !== 0 && child.status !== null) {
|
|
94521
95188
|
process.exit(child.status);
|
|
94522
95189
|
}
|
|
@@ -94578,12 +95245,12 @@ function registerWorkspaceCommand(program3) {
|
|
|
94578
95245
|
if (!dir)
|
|
94579
95246
|
return;
|
|
94580
95247
|
const gitDir = resolve51(dir, ".git");
|
|
94581
|
-
if (!
|
|
95248
|
+
if (!existsSync88(gitDir)) {
|
|
94582
95249
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
94583
95250
|
`);
|
|
94584
95251
|
return;
|
|
94585
95252
|
}
|
|
94586
|
-
const statusResult =
|
|
95253
|
+
const statusResult = spawnSync19("git", ["status", "--short"], {
|
|
94587
95254
|
cwd: dir,
|
|
94588
95255
|
encoding: "utf-8"
|
|
94589
95256
|
});
|
|
@@ -94598,7 +95265,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
94598
95265
|
return;
|
|
94599
95266
|
}
|
|
94600
95267
|
const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
|
|
94601
|
-
const addResult =
|
|
95268
|
+
const addResult = spawnSync19("git", ["add", "-A"], {
|
|
94602
95269
|
cwd: dir,
|
|
94603
95270
|
encoding: "utf-8"
|
|
94604
95271
|
});
|
|
@@ -94607,7 +95274,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
94607
95274
|
`);
|
|
94608
95275
|
process.exit(1);
|
|
94609
95276
|
}
|
|
94610
|
-
const commitResult =
|
|
95277
|
+
const commitResult = spawnSync19("git", ["commit", "-m", message], {
|
|
94611
95278
|
cwd: dir,
|
|
94612
95279
|
encoding: "utf-8"
|
|
94613
95280
|
});
|
|
@@ -94616,7 +95283,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
94616
95283
|
`);
|
|
94617
95284
|
process.exit(1);
|
|
94618
95285
|
}
|
|
94619
|
-
const shaResult =
|
|
95286
|
+
const shaResult = spawnSync19("git", ["rev-parse", "--short", "HEAD"], {
|
|
94620
95287
|
cwd: dir,
|
|
94621
95288
|
encoding: "utf-8"
|
|
94622
95289
|
});
|
|
@@ -94632,12 +95299,12 @@ function registerWorkspaceCommand(program3) {
|
|
|
94632
95299
|
if (!dir)
|
|
94633
95300
|
return;
|
|
94634
95301
|
const gitDir = resolve51(dir, ".git");
|
|
94635
|
-
if (!
|
|
95302
|
+
if (!existsSync88(gitDir)) {
|
|
94636
95303
|
process.stdout.write(`Workspace is not a git repository.
|
|
94637
95304
|
`);
|
|
94638
95305
|
return;
|
|
94639
95306
|
}
|
|
94640
|
-
const child =
|
|
95307
|
+
const child = spawnSync19("git", ["status", "--short"], {
|
|
94641
95308
|
cwd: dir,
|
|
94642
95309
|
stdio: "inherit"
|
|
94643
95310
|
});
|
|
@@ -94657,7 +95324,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
94657
95324
|
const agentsDir = resolveAgentsDir(config);
|
|
94658
95325
|
const agentDir = resolve51(agentsDir, agentName);
|
|
94659
95326
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
94660
|
-
if (!
|
|
95327
|
+
if (!existsSync88(dir)) {
|
|
94661
95328
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
94662
95329
|
`);
|
|
94663
95330
|
return;
|
|
@@ -94693,7 +95360,7 @@ function safeParseInt(value, fallback) {
|
|
|
94693
95360
|
init_helpers();
|
|
94694
95361
|
init_loader();
|
|
94695
95362
|
init_merge();
|
|
94696
|
-
import { copyFileSync as
|
|
95363
|
+
import { copyFileSync as copyFileSync13, existsSync as existsSync89, readFileSync as readFileSync80, writeFileSync as writeFileSync35 } from "node:fs";
|
|
94697
95364
|
import { join as join91, resolve as resolve52 } from "node:path";
|
|
94698
95365
|
init_scaffold();
|
|
94699
95366
|
init_profiles();
|
|
@@ -94711,7 +95378,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
94711
95378
|
const agentsDir = resolveAgentsDir(config);
|
|
94712
95379
|
const agentDir = resolve52(agentsDir, agentName);
|
|
94713
95380
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
94714
|
-
if (!
|
|
95381
|
+
if (!existsSync89(workspaceDir)) {
|
|
94715
95382
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
94716
95383
|
process.exit(1);
|
|
94717
95384
|
}
|
|
@@ -94737,7 +95404,7 @@ function registerSoulCommand(program3) {
|
|
|
94737
95404
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
94738
95405
|
if (!t)
|
|
94739
95406
|
return;
|
|
94740
|
-
if (!
|
|
95407
|
+
if (!existsSync89(t.soulPath)) {
|
|
94741
95408
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
94742
95409
|
process.exit(1);
|
|
94743
95410
|
}
|
|
@@ -94752,7 +95419,7 @@ function registerSoulCommand(program3) {
|
|
|
94752
95419
|
console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
|
|
94753
95420
|
process.exit(1);
|
|
94754
95421
|
}
|
|
94755
|
-
const exists =
|
|
95422
|
+
const exists = existsSync89(t.soulPath);
|
|
94756
95423
|
if (exists && !opts.yes) {
|
|
94757
95424
|
if (!isInteractive()) {
|
|
94758
95425
|
console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
|
|
@@ -94767,10 +95434,10 @@ function registerSoulCommand(program3) {
|
|
|
94767
95434
|
let backupPath;
|
|
94768
95435
|
if (exists) {
|
|
94769
95436
|
backupPath = `${t.soulPath}.bak`;
|
|
94770
|
-
if (
|
|
95437
|
+
if (existsSync89(backupPath)) {
|
|
94771
95438
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
94772
95439
|
}
|
|
94773
|
-
|
|
95440
|
+
copyFileSync13(t.soulPath, backupPath);
|
|
94774
95441
|
}
|
|
94775
95442
|
writeFileSync35(t.soulPath, content, "utf-8");
|
|
94776
95443
|
if (backupPath) {
|
|
@@ -94786,9 +95453,9 @@ function registerSoulCommand(program3) {
|
|
|
94786
95453
|
// src/cli/debug.ts
|
|
94787
95454
|
init_helpers();
|
|
94788
95455
|
init_loader();
|
|
94789
|
-
import { existsSync as
|
|
95456
|
+
import { existsSync as existsSync90, readFileSync as readFileSync81, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
|
|
94790
95457
|
import { resolve as resolve53, join as join92 } from "node:path";
|
|
94791
|
-
import { createHash as
|
|
95458
|
+
import { createHash as createHash20 } from "node:crypto";
|
|
94792
95459
|
init_merge();
|
|
94793
95460
|
init_hindsight2();
|
|
94794
95461
|
function formatBytes(bytes) {
|
|
@@ -94799,7 +95466,7 @@ function estimateTokens(bytes) {
|
|
|
94799
95466
|
}
|
|
94800
95467
|
function readMcpServerNames(agentDir) {
|
|
94801
95468
|
const mcpPath = join92(agentDir, ".mcp.json");
|
|
94802
|
-
if (!
|
|
95469
|
+
if (!existsSync90(mcpPath))
|
|
94803
95470
|
return [];
|
|
94804
95471
|
try {
|
|
94805
95472
|
const parsed = JSON.parse(readFileSync81(mcpPath, "utf-8"));
|
|
@@ -94809,11 +95476,11 @@ function readMcpServerNames(agentDir) {
|
|
|
94809
95476
|
}
|
|
94810
95477
|
}
|
|
94811
95478
|
function sha256(content) {
|
|
94812
|
-
return
|
|
95479
|
+
return createHash20("sha256").update(content).digest("hex").slice(0, 16);
|
|
94813
95480
|
}
|
|
94814
95481
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
94815
95482
|
const projectsDir = join92(claudeConfigDir, "projects");
|
|
94816
|
-
if (!
|
|
95483
|
+
if (!existsSync90(projectsDir))
|
|
94817
95484
|
return;
|
|
94818
95485
|
try {
|
|
94819
95486
|
const entries = readdirSync30(projectsDir, { withFileTypes: true });
|
|
@@ -94823,7 +95490,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
94823
95490
|
continue;
|
|
94824
95491
|
const projectPath = join92(projectsDir, entry.name);
|
|
94825
95492
|
const transcriptPath = join92(projectPath, "transcript.jsonl");
|
|
94826
|
-
if (!
|
|
95493
|
+
if (!existsSync90(transcriptPath))
|
|
94827
95494
|
continue;
|
|
94828
95495
|
const stat3 = statSync50(transcriptPath);
|
|
94829
95496
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
@@ -94886,7 +95553,7 @@ function registerDebugCommand(program3) {
|
|
|
94886
95553
|
}
|
|
94887
95554
|
const agentsDir = resolveAgentsDir(config);
|
|
94888
95555
|
const agentDir = resolve53(agentsDir, agentName);
|
|
94889
|
-
if (!
|
|
95556
|
+
if (!existsSync90(agentDir)) {
|
|
94890
95557
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
94891
95558
|
process.exit(1);
|
|
94892
95559
|
}
|
|
@@ -94941,7 +95608,7 @@ function registerDebugCommand(program3) {
|
|
|
94941
95608
|
}
|
|
94942
95609
|
console.log(`=== Append System Prompt (per-session) ===
|
|
94943
95610
|
`);
|
|
94944
|
-
const handoffContent =
|
|
95611
|
+
const handoffContent = existsSync90(handoffPath) ? readFileSync81(handoffPath, "utf-8") : "";
|
|
94945
95612
|
if (handoffContent.trim().length > 0) {
|
|
94946
95613
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
94947
95614
|
console.log(handoffContent);
|
|
@@ -94952,7 +95619,7 @@ function registerDebugCommand(program3) {
|
|
|
94952
95619
|
}
|
|
94953
95620
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
94954
95621
|
`);
|
|
94955
|
-
const claudeMdContent =
|
|
95622
|
+
const claudeMdContent = existsSync90(claudeMdPath) ? readFileSync81(claudeMdPath, "utf-8") : "";
|
|
94956
95623
|
if (claudeMdContent.trim().length > 0) {
|
|
94957
95624
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
94958
95625
|
console.log(claudeMdContent);
|
|
@@ -94963,7 +95630,7 @@ function registerDebugCommand(program3) {
|
|
|
94963
95630
|
}
|
|
94964
95631
|
console.log(`=== Persona (SOUL.md) ===
|
|
94965
95632
|
`);
|
|
94966
|
-
const soulMdContent =
|
|
95633
|
+
const soulMdContent = existsSync90(soulMdPath) ? readFileSync81(soulMdPath, "utf-8") : existsSync90(workspaceSoulMdPath) ? readFileSync81(workspaceSoulMdPath, "utf-8") : "";
|
|
94967
95634
|
if (soulMdContent.trim().length > 0) {
|
|
94968
95635
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
94969
95636
|
console.log(soulMdContent);
|
|
@@ -95027,8 +95694,8 @@ function registerDebugCommand(program3) {
|
|
|
95027
95694
|
const fleetDir = join92(agentsDir, "..", "fleet");
|
|
95028
95695
|
const fleetInvPath = join92(fleetDir, "switchroom-invariants.md");
|
|
95029
95696
|
const fleetClaudePath = join92(fleetDir, "CLAUDE.md");
|
|
95030
|
-
const fleetInvBytes =
|
|
95031
|
-
const fleetClaudeBytes =
|
|
95697
|
+
const fleetInvBytes = existsSync90(fleetInvPath) ? readFileSync81(fleetInvPath, "utf-8").length : 0;
|
|
95698
|
+
const fleetClaudeBytes = existsSync90(fleetClaudePath) ? readFileSync81(fleetClaudePath, "utf-8").length : 0;
|
|
95032
95699
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
95033
95700
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
95034
95701
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -95061,20 +95728,20 @@ init_source();
|
|
|
95061
95728
|
|
|
95062
95729
|
// src/worktree/claim.ts
|
|
95063
95730
|
import { execFileSync as execFileSync26 } from "node:child_process";
|
|
95064
|
-
import { closeSync as
|
|
95731
|
+
import { closeSync as closeSync17, mkdirSync as mkdirSync51, openSync as openSync17, existsSync as existsSync92, unlinkSync as unlinkSync19 } from "node:fs";
|
|
95065
95732
|
import { join as join94, resolve as resolve55 } from "node:path";
|
|
95066
95733
|
import { homedir as homedir50 } from "node:os";
|
|
95067
95734
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
95068
95735
|
|
|
95069
95736
|
// src/worktree/registry.ts
|
|
95070
95737
|
import {
|
|
95071
|
-
mkdirSync as
|
|
95738
|
+
mkdirSync as mkdirSync50,
|
|
95072
95739
|
writeFileSync as writeFileSync36,
|
|
95073
95740
|
readFileSync as readFileSync82,
|
|
95074
95741
|
readdirSync as readdirSync31,
|
|
95075
95742
|
unlinkSync as unlinkSync18,
|
|
95076
|
-
existsSync as
|
|
95077
|
-
renameSync as
|
|
95743
|
+
existsSync as existsSync91,
|
|
95744
|
+
renameSync as renameSync22
|
|
95078
95745
|
} from "node:fs";
|
|
95079
95746
|
import { join as join93, resolve as resolve54 } from "node:path";
|
|
95080
95747
|
import { homedir as homedir49 } from "node:os";
|
|
@@ -95085,7 +95752,7 @@ function recordPath(id) {
|
|
|
95085
95752
|
return join93(registryDir(), `${id}.json`);
|
|
95086
95753
|
}
|
|
95087
95754
|
function ensureDir2() {
|
|
95088
|
-
|
|
95755
|
+
mkdirSync50(registryDir(), { recursive: true });
|
|
95089
95756
|
}
|
|
95090
95757
|
function writeRecord(record2) {
|
|
95091
95758
|
ensureDir2();
|
|
@@ -95093,7 +95760,7 @@ function writeRecord(record2) {
|
|
|
95093
95760
|
const tmp = `${target}.tmp${process.pid}`;
|
|
95094
95761
|
writeFileSync36(tmp, JSON.stringify(record2, null, 2) + `
|
|
95095
95762
|
`, { mode: 384 });
|
|
95096
|
-
|
|
95763
|
+
renameSync22(tmp, target);
|
|
95097
95764
|
}
|
|
95098
95765
|
function readRecord(id) {
|
|
95099
95766
|
const path10 = recordPath(id);
|
|
@@ -95131,14 +95798,14 @@ function countByRepo(repoPath) {
|
|
|
95131
95798
|
// src/worktree/claim.ts
|
|
95132
95799
|
function acquireRepoLock(repoPath) {
|
|
95133
95800
|
const lockDir = registryDir();
|
|
95134
|
-
|
|
95801
|
+
mkdirSync51(lockDir, { recursive: true });
|
|
95135
95802
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
95136
95803
|
const lockPath = join94(lockDir, `.lock-${lockName}`);
|
|
95137
95804
|
const deadline = Date.now() + 5000;
|
|
95138
95805
|
let fd = null;
|
|
95139
95806
|
while (fd === null) {
|
|
95140
95807
|
try {
|
|
95141
|
-
fd =
|
|
95808
|
+
fd = openSync17(lockPath, "wx");
|
|
95142
95809
|
} catch (err) {
|
|
95143
95810
|
if (err.code !== "EEXIST")
|
|
95144
95811
|
throw err;
|
|
@@ -95151,7 +95818,7 @@ function acquireRepoLock(repoPath) {
|
|
|
95151
95818
|
}
|
|
95152
95819
|
return () => {
|
|
95153
95820
|
try {
|
|
95154
|
-
|
|
95821
|
+
closeSync17(fd);
|
|
95155
95822
|
} catch {}
|
|
95156
95823
|
try {
|
|
95157
95824
|
unlinkSync19(lockPath);
|
|
@@ -95187,7 +95854,7 @@ function expandHome(p) {
|
|
|
95187
95854
|
}
|
|
95188
95855
|
async function claimWorktree(input, codeRepos) {
|
|
95189
95856
|
const repoPath = resolveRepoPath(input.repo, codeRepos);
|
|
95190
|
-
if (!
|
|
95857
|
+
if (!existsSync92(repoPath)) {
|
|
95191
95858
|
throw new Error(`Repository path does not exist: ${repoPath}`);
|
|
95192
95859
|
}
|
|
95193
95860
|
let concurrencyCap = DEFAULT_CONCURRENCY;
|
|
@@ -95209,7 +95876,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
95209
95876
|
const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
|
|
95210
95877
|
branch = `task/${taskSuffix}-${id}`;
|
|
95211
95878
|
const baseDir = worktreesBaseDir();
|
|
95212
|
-
|
|
95879
|
+
mkdirSync51(baseDir, { recursive: true });
|
|
95213
95880
|
worktreePath = join94(baseDir, `${id}-${taskSuffix}`);
|
|
95214
95881
|
const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
|
|
95215
95882
|
const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
|
|
@@ -95243,7 +95910,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
95243
95910
|
|
|
95244
95911
|
// src/worktree/release.ts
|
|
95245
95912
|
import { execFileSync as execFileSync27 } from "node:child_process";
|
|
95246
|
-
import { existsSync as
|
|
95913
|
+
import { existsSync as existsSync93 } from "node:fs";
|
|
95247
95914
|
function releaseWorktree(input) {
|
|
95248
95915
|
const { id } = input;
|
|
95249
95916
|
const record2 = readRecord(id);
|
|
@@ -95251,7 +95918,7 @@ function releaseWorktree(input) {
|
|
|
95251
95918
|
return { released: true };
|
|
95252
95919
|
}
|
|
95253
95920
|
let gitSuccess = true;
|
|
95254
|
-
if (
|
|
95921
|
+
if (existsSync93(record2.path)) {
|
|
95255
95922
|
try {
|
|
95256
95923
|
execFileSync27("git", ["worktree", "remove", "--force", record2.path], {
|
|
95257
95924
|
cwd: record2.repo,
|
|
@@ -95290,7 +95957,7 @@ function listWorktrees() {
|
|
|
95290
95957
|
|
|
95291
95958
|
// src/worktree/reaper.ts
|
|
95292
95959
|
import { execFileSync as execFileSync28 } from "node:child_process";
|
|
95293
|
-
import { existsSync as
|
|
95960
|
+
import { existsSync as existsSync94 } from "node:fs";
|
|
95294
95961
|
var STALE_THRESHOLD_MS = 10 * 60 * 1000;
|
|
95295
95962
|
function reapSkipReasonText(action) {
|
|
95296
95963
|
switch (action) {
|
|
@@ -95351,7 +96018,7 @@ function planReaper(nowMs, deps = {}) {
|
|
|
95351
96018
|
const plan = [];
|
|
95352
96019
|
for (const record2 of listRecords()) {
|
|
95353
96020
|
const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
|
|
95354
|
-
const worktreeExists =
|
|
96021
|
+
const worktreeExists = existsSync94(record2.path);
|
|
95355
96022
|
if (!worktreeExists) {
|
|
95356
96023
|
plan.push({
|
|
95357
96024
|
record: record2,
|
|
@@ -95432,13 +96099,13 @@ function runReaper(nowMs, deps = {}) {
|
|
|
95432
96099
|
// src/worktree/gc.ts
|
|
95433
96100
|
import { execFileSync as execFileSync29 } from "node:child_process";
|
|
95434
96101
|
import {
|
|
95435
|
-
existsSync as
|
|
96102
|
+
existsSync as existsSync95,
|
|
95436
96103
|
readFileSync as readFileSync83,
|
|
95437
96104
|
readdirSync as readdirSync32,
|
|
95438
96105
|
statSync as statSync51,
|
|
95439
|
-
renameSync as
|
|
95440
|
-
mkdirSync as
|
|
95441
|
-
rmSync as
|
|
96106
|
+
renameSync as renameSync23,
|
|
96107
|
+
mkdirSync as mkdirSync52,
|
|
96108
|
+
rmSync as rmSync17
|
|
95442
96109
|
} from "node:fs";
|
|
95443
96110
|
import { homedir as homedir51 } from "node:os";
|
|
95444
96111
|
import { join as join95, resolve as resolve56 } from "node:path";
|
|
@@ -95523,7 +96190,7 @@ function isEphemeralPath(path10) {
|
|
|
95523
96190
|
}
|
|
95524
96191
|
return false;
|
|
95525
96192
|
}
|
|
95526
|
-
var
|
|
96193
|
+
var defaultExec3 = (file, args, cwd) => execFileSync29(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
95527
96194
|
function defaultPrSignal(repo, branch, exec) {
|
|
95528
96195
|
try {
|
|
95529
96196
|
const out = exec("gh", [
|
|
@@ -95559,11 +96226,11 @@ function trashRoot() {
|
|
|
95559
96226
|
return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join95(homedir51(), ".switchroom", "worktree-gc-trash"));
|
|
95560
96227
|
}
|
|
95561
96228
|
function planGc(roots, deps = {}) {
|
|
95562
|
-
const exists = deps.existsSync ??
|
|
96229
|
+
const exists = deps.existsSync ?? existsSync95;
|
|
95563
96230
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
95564
96231
|
const readFile4 = deps.readFile ?? ((p) => readFileSync83(p, "utf8"));
|
|
95565
96232
|
const stat3 = deps.stat ?? ((p) => statSync51(p));
|
|
95566
|
-
const exec = deps.exec ??
|
|
96233
|
+
const exec = deps.exec ?? defaultExec3;
|
|
95567
96234
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
95568
96235
|
const stamp = deps.dateStamp ?? "undated";
|
|
95569
96236
|
const trash = join95(trashRoot(), stamp);
|
|
@@ -95688,11 +96355,11 @@ function planGc(roots, deps = {}) {
|
|
|
95688
96355
|
};
|
|
95689
96356
|
}
|
|
95690
96357
|
function applyGc(plan, deps = {}) {
|
|
95691
|
-
const exec = deps.exec ??
|
|
95692
|
-
const mkdirp = deps.mkdirp ?? ((p) => void
|
|
96358
|
+
const exec = deps.exec ?? defaultExec3;
|
|
96359
|
+
const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync52(p, { recursive: true }));
|
|
95693
96360
|
const move = deps.move ?? ((src, dest) => {
|
|
95694
96361
|
try {
|
|
95695
|
-
|
|
96362
|
+
renameSync23(src, dest);
|
|
95696
96363
|
} catch {
|
|
95697
96364
|
exec("mv", [src, dest]);
|
|
95698
96365
|
}
|
|
@@ -95738,7 +96405,7 @@ function selectPurgeTargets(entries, olderThanDays) {
|
|
|
95738
96405
|
return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
|
|
95739
96406
|
}
|
|
95740
96407
|
function listTrashEntries(nowMs, deps = {}) {
|
|
95741
|
-
const exists = deps.existsSync ??
|
|
96408
|
+
const exists = deps.existsSync ?? existsSync95;
|
|
95742
96409
|
const readDir = deps.readDir ?? ((p) => readdirSync32(p));
|
|
95743
96410
|
const root = trashRoot();
|
|
95744
96411
|
if (!exists(root))
|
|
@@ -95768,7 +96435,7 @@ function purgeTrash(paths) {
|
|
|
95768
96435
|
const errors2 = [];
|
|
95769
96436
|
for (const p of paths) {
|
|
95770
96437
|
try {
|
|
95771
|
-
|
|
96438
|
+
rmSync17(p, { recursive: true, force: true });
|
|
95772
96439
|
deleted.push(p);
|
|
95773
96440
|
} catch (e) {
|
|
95774
96441
|
errors2.push(`${p}: ${e.message}`);
|
|
@@ -95999,10 +96666,10 @@ init_drive();
|
|
|
95999
96666
|
// src/cli/drive-mcp-launcher.ts
|
|
96000
96667
|
init_scaffold_integration();
|
|
96001
96668
|
import {
|
|
96002
|
-
chmodSync as
|
|
96003
|
-
mkdirSync as
|
|
96669
|
+
chmodSync as chmodSync16,
|
|
96670
|
+
mkdirSync as mkdirSync53,
|
|
96004
96671
|
readdirSync as readdirSync33,
|
|
96005
|
-
rmSync as
|
|
96672
|
+
rmSync as rmSync18,
|
|
96006
96673
|
writeFileSync as writeFileSync37
|
|
96007
96674
|
} from "node:fs";
|
|
96008
96675
|
import { join as join96 } from "node:path";
|
|
@@ -96198,15 +96865,15 @@ function resolveCredentialsDir(env2) {
|
|
|
96198
96865
|
return join96(stateBase, "google-workspace-mcp", "credentials");
|
|
96199
96866
|
}
|
|
96200
96867
|
function writeSeedFile(dir, email, seed) {
|
|
96201
|
-
|
|
96202
|
-
|
|
96868
|
+
mkdirSync53(dir, { recursive: true, mode: 448 });
|
|
96869
|
+
chmodSync16(dir, 448);
|
|
96203
96870
|
for (const name of readdirSync33(dir)) {
|
|
96204
|
-
|
|
96871
|
+
rmSync18(join96(dir, name), { force: true, recursive: true });
|
|
96205
96872
|
}
|
|
96206
96873
|
const filename = encodeCredentialsFilename(email);
|
|
96207
96874
|
const filePath = join96(dir, filename);
|
|
96208
96875
|
writeFileSync37(filePath, JSON.stringify(seed), { mode: 384 });
|
|
96209
|
-
|
|
96876
|
+
chmodSync16(filePath, 384);
|
|
96210
96877
|
return filePath;
|
|
96211
96878
|
}
|
|
96212
96879
|
async function fetchBrokerGoogleCreds() {
|
|
@@ -96363,8 +97030,8 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
96363
97030
|
// src/cli/m365-mcp-launcher.ts
|
|
96364
97031
|
init_scaffold_integration();
|
|
96365
97032
|
import { spawn as spawn6 } from "node:child_process";
|
|
96366
|
-
import { writeFileSync as writeFileSync38, mkdirSync as
|
|
96367
|
-
import { dirname as
|
|
97033
|
+
import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync54 } from "node:fs";
|
|
97034
|
+
import { dirname as dirname38, join as join97 } from "node:path";
|
|
96368
97035
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
96369
97036
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
96370
97037
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -96400,7 +97067,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
96400
97067
|
function writeRefreshHeartbeat(agentName, data, account) {
|
|
96401
97068
|
const path10 = heartbeatPath(agentName, account);
|
|
96402
97069
|
try {
|
|
96403
|
-
|
|
97070
|
+
mkdirSync54(dirname38(path10), { recursive: true });
|
|
96404
97071
|
writeFileSync38(path10, JSON.stringify(data, null, 2), { mode: 420 });
|
|
96405
97072
|
} catch {}
|
|
96406
97073
|
}
|
|
@@ -96641,8 +97308,8 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
96641
97308
|
// src/cli/notion-mcp-launcher.ts
|
|
96642
97309
|
init_scaffold_integration();
|
|
96643
97310
|
import { spawn as spawn7 } from "node:child_process";
|
|
96644
|
-
import { existsSync as
|
|
96645
|
-
import { dirname as
|
|
97311
|
+
import { existsSync as existsSync96, mkdirSync as mkdirSync55, writeFileSync as writeFileSync39 } from "node:fs";
|
|
97312
|
+
import { dirname as dirname39 } from "node:path";
|
|
96646
97313
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
96647
97314
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
96648
97315
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -96652,9 +97319,9 @@ function buildNotionMcpArgs(opts) {
|
|
|
96652
97319
|
}
|
|
96653
97320
|
function defaultWriteHeartbeat(path10, contents) {
|
|
96654
97321
|
try {
|
|
96655
|
-
const dir =
|
|
96656
|
-
if (!
|
|
96657
|
-
|
|
97322
|
+
const dir = dirname39(path10);
|
|
97323
|
+
if (!existsSync96(dir))
|
|
97324
|
+
mkdirSync55(dir, { recursive: true });
|
|
96658
97325
|
writeFileSync39(path10, contents);
|
|
96659
97326
|
} catch {}
|
|
96660
97327
|
}
|
|
@@ -96765,7 +97432,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
96765
97432
|
|
|
96766
97433
|
// src/cli/hindsight-mcp-shim.ts
|
|
96767
97434
|
init_hindsight();
|
|
96768
|
-
import { mkdirSync as
|
|
97435
|
+
import { mkdirSync as mkdirSync56, readFileSync as readFileSync84, renameSync as renameSync24, writeFileSync as writeFileSync40 } from "node:fs";
|
|
96769
97436
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
96770
97437
|
import { join as join98 } from "node:path";
|
|
96771
97438
|
import { createInterface as createInterface6 } from "node:readline";
|
|
@@ -96997,11 +97664,11 @@ class HindsightShim {
|
|
|
96997
97664
|
}
|
|
96998
97665
|
writeCache(result) {
|
|
96999
97666
|
try {
|
|
97000
|
-
|
|
97667
|
+
mkdirSync56(this.opts.cacheDir, { recursive: true });
|
|
97001
97668
|
const tmp = join98(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
|
|
97002
97669
|
writeFileSync40(tmp, JSON.stringify(result, null, 2) + `
|
|
97003
97670
|
`);
|
|
97004
|
-
|
|
97671
|
+
renameSync24(tmp, this.cachePath);
|
|
97005
97672
|
} catch (err) {
|
|
97006
97673
|
this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
|
|
97007
97674
|
}
|
|
@@ -97845,7 +98512,7 @@ function runRedactStdin() {
|
|
|
97845
98512
|
}
|
|
97846
98513
|
|
|
97847
98514
|
// src/cli/status-ask.ts
|
|
97848
|
-
import { readFileSync as readFileSync86, existsSync as
|
|
98515
|
+
import { readFileSync as readFileSync86, existsSync as existsSync97, readdirSync as readdirSync34 } from "node:fs";
|
|
97849
98516
|
import { join as join99 } from "node:path";
|
|
97850
98517
|
import { homedir as homedir52 } from "node:os";
|
|
97851
98518
|
|
|
@@ -98168,7 +98835,7 @@ function runReport(opts) {
|
|
|
98168
98835
|
function resolveSources(explicitPath) {
|
|
98169
98836
|
if (explicitPath != null && explicitPath.trim() !== "") {
|
|
98170
98837
|
const trimmed = explicitPath.trim();
|
|
98171
|
-
if (!
|
|
98838
|
+
if (!existsSync97(trimmed)) {
|
|
98172
98839
|
process.stderr.write(`status-ask report: ${trimmed}: file not found
|
|
98173
98840
|
`);
|
|
98174
98841
|
process.exit(1);
|
|
@@ -98184,7 +98851,7 @@ function resolveSources(explicitPath) {
|
|
|
98184
98851
|
} catch {
|
|
98185
98852
|
agentsDir = join99(homedir52(), ".switchroom", "agents");
|
|
98186
98853
|
}
|
|
98187
|
-
if (!
|
|
98854
|
+
if (!existsSync97(agentsDir))
|
|
98188
98855
|
return [];
|
|
98189
98856
|
const sources = [];
|
|
98190
98857
|
let entries;
|
|
@@ -98195,7 +98862,7 @@ function resolveSources(explicitPath) {
|
|
|
98195
98862
|
}
|
|
98196
98863
|
for (const name of entries) {
|
|
98197
98864
|
const path10 = join99(agentsDir, name, "runtime-metrics.jsonl");
|
|
98198
|
-
if (
|
|
98865
|
+
if (existsSync97(path10)) {
|
|
98199
98866
|
sources.push({ path: path10, agent: name });
|
|
98200
98867
|
}
|
|
98201
98868
|
}
|
|
@@ -98224,14 +98891,14 @@ var import_yaml21 = __toESM(require_dist(), 1);
|
|
|
98224
98891
|
// src/config/overlay-writer.ts
|
|
98225
98892
|
init_paths();
|
|
98226
98893
|
import {
|
|
98227
|
-
closeSync as
|
|
98228
|
-
existsSync as
|
|
98894
|
+
closeSync as closeSync18,
|
|
98895
|
+
existsSync as existsSync98,
|
|
98229
98896
|
fsyncSync as fsyncSync8,
|
|
98230
|
-
mkdirSync as
|
|
98231
|
-
openSync as
|
|
98897
|
+
mkdirSync as mkdirSync57,
|
|
98898
|
+
openSync as openSync18,
|
|
98232
98899
|
readdirSync as readdirSync35,
|
|
98233
98900
|
readFileSync as readFileSync87,
|
|
98234
|
-
renameSync as
|
|
98901
|
+
renameSync as renameSync25,
|
|
98235
98902
|
statSync as statSync53,
|
|
98236
98903
|
unlinkSync as unlinkSync20,
|
|
98237
98904
|
writeSync as writeSync10
|
|
@@ -98255,21 +98922,21 @@ function overlayPathsFor(agent, opts = {}) {
|
|
|
98255
98922
|
};
|
|
98256
98923
|
}
|
|
98257
98924
|
function ensureDirs(paths) {
|
|
98258
|
-
|
|
98259
|
-
|
|
98925
|
+
mkdirSync57(paths.scheduleDir, { recursive: true });
|
|
98926
|
+
mkdirSync57(paths.scheduleStagingDir, { recursive: true });
|
|
98260
98927
|
}
|
|
98261
98928
|
function ensureSkillsDirs(paths) {
|
|
98262
|
-
|
|
98263
|
-
|
|
98929
|
+
mkdirSync57(paths.skillsDir, { recursive: true });
|
|
98930
|
+
mkdirSync57(paths.skillsStagingDir, { recursive: true });
|
|
98264
98931
|
}
|
|
98265
98932
|
function withAgentLock(paths, fn) {
|
|
98266
|
-
|
|
98933
|
+
mkdirSync57(paths.agentRoot, { recursive: true });
|
|
98267
98934
|
const start = Date.now();
|
|
98268
98935
|
const TIMEOUT_MS = 5000;
|
|
98269
98936
|
let fd = null;
|
|
98270
98937
|
while (Date.now() - start < TIMEOUT_MS) {
|
|
98271
98938
|
try {
|
|
98272
|
-
fd =
|
|
98939
|
+
fd = openSync18(paths.lockPath, "wx");
|
|
98273
98940
|
break;
|
|
98274
98941
|
} catch (err) {
|
|
98275
98942
|
const e = err;
|
|
@@ -98293,7 +98960,7 @@ function withAgentLock(paths, fn) {
|
|
|
98293
98960
|
return fn();
|
|
98294
98961
|
} finally {
|
|
98295
98962
|
try {
|
|
98296
|
-
|
|
98963
|
+
closeSync18(fd);
|
|
98297
98964
|
} catch {}
|
|
98298
98965
|
try {
|
|
98299
98966
|
unlinkSync20(paths.lockPath);
|
|
@@ -98306,14 +98973,14 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
98306
98973
|
ensureDirs(paths);
|
|
98307
98974
|
const stagingPath = join100(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
98308
98975
|
const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
|
|
98309
|
-
const fd =
|
|
98976
|
+
const fd = openSync18(stagingPath, "w", 384);
|
|
98310
98977
|
try {
|
|
98311
98978
|
writeSync10(fd, yamlText);
|
|
98312
98979
|
fsyncSync8(fd);
|
|
98313
98980
|
} finally {
|
|
98314
|
-
|
|
98981
|
+
closeSync18(fd);
|
|
98315
98982
|
}
|
|
98316
|
-
|
|
98983
|
+
renameSync25(stagingPath, finalPath);
|
|
98317
98984
|
return finalPath;
|
|
98318
98985
|
});
|
|
98319
98986
|
}
|
|
@@ -98323,14 +98990,14 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
98323
98990
|
ensureSkillsDirs(paths);
|
|
98324
98991
|
const stagingPath = join100(paths.skillsStagingDir, `${slug}.yaml`);
|
|
98325
98992
|
const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
|
|
98326
|
-
const fd =
|
|
98993
|
+
const fd = openSync18(stagingPath, "w", 384);
|
|
98327
98994
|
try {
|
|
98328
98995
|
writeSync10(fd, yamlText);
|
|
98329
98996
|
fsyncSync8(fd);
|
|
98330
98997
|
} finally {
|
|
98331
|
-
|
|
98998
|
+
closeSync18(fd);
|
|
98332
98999
|
}
|
|
98333
|
-
|
|
99000
|
+
renameSync25(stagingPath, finalPath);
|
|
98334
99001
|
return finalPath;
|
|
98335
99002
|
});
|
|
98336
99003
|
}
|
|
@@ -98338,7 +99005,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
98338
99005
|
const paths = overlayPathsFor(agent, opts);
|
|
98339
99006
|
return withAgentLock(paths, () => {
|
|
98340
99007
|
const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
|
|
98341
|
-
if (!
|
|
99008
|
+
if (!existsSync98(finalPath))
|
|
98342
99009
|
return false;
|
|
98343
99010
|
unlinkSync20(finalPath);
|
|
98344
99011
|
return true;
|
|
@@ -98346,7 +99013,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
|
98346
99013
|
}
|
|
98347
99014
|
function listSkillsOverlayEntries(agent, opts = {}) {
|
|
98348
99015
|
const paths = overlayPathsFor(agent, opts);
|
|
98349
|
-
if (!
|
|
99016
|
+
if (!existsSync98(paths.skillsDir))
|
|
98350
99017
|
return [];
|
|
98351
99018
|
const out = [];
|
|
98352
99019
|
for (const name of readdirSync35(paths.skillsDir)) {
|
|
@@ -98365,7 +99032,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
98365
99032
|
const paths = overlayPathsFor(agent, opts);
|
|
98366
99033
|
return withAgentLock(paths, () => {
|
|
98367
99034
|
const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
|
|
98368
|
-
if (!
|
|
99035
|
+
if (!existsSync98(finalPath))
|
|
98369
99036
|
return false;
|
|
98370
99037
|
unlinkSync20(finalPath);
|
|
98371
99038
|
return true;
|
|
@@ -98373,7 +99040,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
|
98373
99040
|
}
|
|
98374
99041
|
function listOverlayEntries(agent, opts = {}) {
|
|
98375
99042
|
const paths = overlayPathsFor(agent, opts);
|
|
98376
|
-
if (!
|
|
99043
|
+
if (!existsSync98(paths.scheduleDir))
|
|
98377
99044
|
return [];
|
|
98378
99045
|
const out = [];
|
|
98379
99046
|
for (const name of readdirSync35(paths.scheduleDir)) {
|
|
@@ -98600,14 +99267,14 @@ function reconcileAgentCronOnly(agent) {
|
|
|
98600
99267
|
|
|
98601
99268
|
// src/cli/agent-config-pending.ts
|
|
98602
99269
|
import {
|
|
98603
|
-
closeSync as
|
|
98604
|
-
existsSync as
|
|
99270
|
+
closeSync as closeSync19,
|
|
99271
|
+
existsSync as existsSync99,
|
|
98605
99272
|
fsyncSync as fsyncSync9,
|
|
98606
|
-
mkdirSync as
|
|
98607
|
-
openSync as
|
|
99273
|
+
mkdirSync as mkdirSync58,
|
|
99274
|
+
openSync as openSync19,
|
|
98608
99275
|
readdirSync as readdirSync36,
|
|
98609
99276
|
readFileSync as readFileSync88,
|
|
98610
|
-
renameSync as
|
|
99277
|
+
renameSync as renameSync26,
|
|
98611
99278
|
unlinkSync as unlinkSync21,
|
|
98612
99279
|
writeFileSync as writeFileSync41,
|
|
98613
99280
|
writeSync as writeSync11
|
|
@@ -98621,7 +99288,7 @@ function pendingDir(agent, opts = {}) {
|
|
|
98621
99288
|
}
|
|
98622
99289
|
function ensurePendingDir(agent, opts = {}) {
|
|
98623
99290
|
const dir = pendingDir(agent, opts);
|
|
98624
|
-
|
|
99291
|
+
mkdirSync58(dir, { recursive: true });
|
|
98625
99292
|
return dir;
|
|
98626
99293
|
}
|
|
98627
99294
|
function newStageId() {
|
|
@@ -98643,14 +99310,14 @@ function stagePendingScheduleEntry(opts) {
|
|
|
98643
99310
|
};
|
|
98644
99311
|
const yamlTmp = `${yamlPath}.tmp-${process.pid}`;
|
|
98645
99312
|
{
|
|
98646
|
-
const fd =
|
|
99313
|
+
const fd = openSync19(yamlTmp, "w", 384);
|
|
98647
99314
|
try {
|
|
98648
99315
|
writeSync11(fd, opts.yamlText);
|
|
98649
99316
|
fsyncSync9(fd);
|
|
98650
99317
|
} finally {
|
|
98651
|
-
|
|
99318
|
+
closeSync19(fd);
|
|
98652
99319
|
}
|
|
98653
|
-
|
|
99320
|
+
renameSync26(yamlTmp, yamlPath);
|
|
98654
99321
|
}
|
|
98655
99322
|
writeFileSync41(metaPath, JSON.stringify(meta, null, 2) + `
|
|
98656
99323
|
`, { mode: 384 });
|
|
@@ -98658,7 +99325,7 @@ function stagePendingScheduleEntry(opts) {
|
|
|
98658
99325
|
}
|
|
98659
99326
|
function listPendingScheduleEntries(agent, opts = {}) {
|
|
98660
99327
|
const dir = pendingDir(agent, opts);
|
|
98661
|
-
if (!
|
|
99328
|
+
if (!existsSync99(dir))
|
|
98662
99329
|
return [];
|
|
98663
99330
|
const out = [];
|
|
98664
99331
|
for (const name of readdirSync36(dir).sort()) {
|
|
@@ -98667,7 +99334,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
98667
99334
|
const stageId = name.slice(0, -".meta.json".length);
|
|
98668
99335
|
const metaPath = join101(dir, name);
|
|
98669
99336
|
const yamlPath = join101(dir, `${stageId}.yaml`);
|
|
98670
|
-
if (!
|
|
99337
|
+
if (!existsSync99(yamlPath))
|
|
98671
99338
|
continue;
|
|
98672
99339
|
try {
|
|
98673
99340
|
const meta = JSON.parse(readFileSync88(metaPath, "utf-8"));
|
|
@@ -98686,10 +99353,10 @@ function commitPendingScheduleEntry(opts) {
|
|
|
98686
99353
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
98687
99354
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
98688
99355
|
const finalPath = join101(paths.scheduleDir, `${slug}.yaml`);
|
|
98689
|
-
if (
|
|
99356
|
+
if (existsSync99(finalPath)) {
|
|
98690
99357
|
return { committed: false, reason: "slug_collision" };
|
|
98691
99358
|
}
|
|
98692
|
-
|
|
99359
|
+
renameSync26(match.yamlPath, finalPath);
|
|
98693
99360
|
unlinkSync21(match.metaPath);
|
|
98694
99361
|
return { committed: true, path: finalPath, slug };
|
|
98695
99362
|
}
|
|
@@ -98708,7 +99375,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
98708
99375
|
}
|
|
98709
99376
|
|
|
98710
99377
|
// src/cli/agent-config-write.ts
|
|
98711
|
-
import { existsSync as
|
|
99378
|
+
import { existsSync as existsSync100, readFileSync as readFileSync89 } from "node:fs";
|
|
98712
99379
|
import { execFileSync as execFileSync30 } from "node:child_process";
|
|
98713
99380
|
|
|
98714
99381
|
// src/scheduler/schedule-report.ts
|
|
@@ -99098,7 +99765,7 @@ function scheduleRemove(opts) {
|
|
|
99098
99765
|
}
|
|
99099
99766
|
let priorContent = null;
|
|
99100
99767
|
try {
|
|
99101
|
-
if (
|
|
99768
|
+
if (existsSync100(match.path))
|
|
99102
99769
|
priorContent = readFileSync89(match.path, "utf-8");
|
|
99103
99770
|
} catch {}
|
|
99104
99771
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
@@ -99302,7 +99969,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
99302
99969
|
}
|
|
99303
99970
|
let blob;
|
|
99304
99971
|
if (opts.jsonl) {
|
|
99305
|
-
blob =
|
|
99972
|
+
blob = existsSync100(opts.jsonl) ? readFileSync89(opts.jsonl, "utf-8") : "";
|
|
99306
99973
|
} else {
|
|
99307
99974
|
try {
|
|
99308
99975
|
blob = execFileSync30("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -99334,7 +100001,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
99334
100001
|
|
|
99335
100002
|
// src/cli/agent-config-skill-write.ts
|
|
99336
100003
|
var import_yaml22 = __toESM(require_dist(), 1);
|
|
99337
|
-
import { existsSync as
|
|
100004
|
+
import { existsSync as existsSync101 } from "node:fs";
|
|
99338
100005
|
init_reconcile_default_skills();
|
|
99339
100006
|
init_agent_config();
|
|
99340
100007
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
@@ -99414,7 +100081,7 @@ function skillInstall(opts) {
|
|
|
99414
100081
|
}
|
|
99415
100082
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
99416
100083
|
const skillPath = join102(poolDir, skillName);
|
|
99417
|
-
if (!
|
|
100084
|
+
if (!existsSync101(skillPath)) {
|
|
99418
100085
|
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.`);
|
|
99419
100086
|
}
|
|
99420
100087
|
const yamlText = import_yaml22.stringify({ skills: [skillName] });
|
|
@@ -99577,23 +100244,23 @@ function registerAgentConfigSkillWriteCommands(program3) {
|
|
|
99577
100244
|
|
|
99578
100245
|
// src/cli/skill.ts
|
|
99579
100246
|
import {
|
|
99580
|
-
closeSync as
|
|
99581
|
-
existsSync as
|
|
100247
|
+
closeSync as closeSync20,
|
|
100248
|
+
existsSync as existsSync102,
|
|
99582
100249
|
lstatSync as lstatSync12,
|
|
99583
|
-
mkdirSync as
|
|
100250
|
+
mkdirSync as mkdirSync59,
|
|
99584
100251
|
mkdtempSync as mkdtempSync5,
|
|
99585
|
-
openSync as
|
|
100252
|
+
openSync as openSync20,
|
|
99586
100253
|
readFileSync as readFileSync90,
|
|
99587
100254
|
readdirSync as readdirSync37,
|
|
99588
100255
|
realpathSync as realpathSync7,
|
|
99589
|
-
renameSync as
|
|
99590
|
-
rmSync as
|
|
100256
|
+
renameSync as renameSync27,
|
|
100257
|
+
rmSync as rmSync19,
|
|
99591
100258
|
statSync as statSync54,
|
|
99592
100259
|
writeFileSync as writeFileSync42
|
|
99593
100260
|
} from "node:fs";
|
|
99594
100261
|
import { tmpdir as tmpdir6, homedir as homedir53 } from "node:os";
|
|
99595
|
-
import { dirname as
|
|
99596
|
-
import { spawnSync as
|
|
100262
|
+
import { dirname as dirname40, join as join103, relative as relative4, resolve as resolve58 } from "node:path";
|
|
100263
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
99597
100264
|
|
|
99598
100265
|
// src/cli/skill-common.ts
|
|
99599
100266
|
var import_yaml24 = __toESM(require_dist(), 1);
|
|
@@ -99886,7 +100553,7 @@ function loadFromDir(dir) {
|
|
|
99886
100553
|
function loadFromTarball(tarPath) {
|
|
99887
100554
|
const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
|
|
99888
100555
|
const listFlags = isGz ? ["-tzf"] : ["-tf"];
|
|
99889
|
-
const list2 =
|
|
100556
|
+
const list2 = spawnSync20("tar", [...listFlags, tarPath], {
|
|
99890
100557
|
encoding: "utf-8",
|
|
99891
100558
|
stdio: ["ignore", "pipe", "pipe"]
|
|
99892
100559
|
});
|
|
@@ -99903,7 +100570,7 @@ function loadFromTarball(tarPath) {
|
|
|
99903
100570
|
const staging = mkdtempSync5(join103(tmpdir6(), "skill-apply-extract-"));
|
|
99904
100571
|
try {
|
|
99905
100572
|
const flags = isGz ? ["-xzf"] : ["-xf"];
|
|
99906
|
-
const r =
|
|
100573
|
+
const r = spawnSync20("tar", [
|
|
99907
100574
|
...flags,
|
|
99908
100575
|
tarPath,
|
|
99909
100576
|
"-C",
|
|
@@ -99919,7 +100586,7 @@ function loadFromTarball(tarPath) {
|
|
|
99919
100586
|
return loadFromDir(staging);
|
|
99920
100587
|
} finally {
|
|
99921
100588
|
try {
|
|
99922
|
-
|
|
100589
|
+
rmSync19(staging, { recursive: true, force: true });
|
|
99923
100590
|
} catch {}
|
|
99924
100591
|
}
|
|
99925
100592
|
}
|
|
@@ -99978,7 +100645,7 @@ function validatePayload(name, files) {
|
|
|
99978
100645
|
if (errors2.length === 0) {
|
|
99979
100646
|
for (const [path10, content] of Object.entries(files)) {
|
|
99980
100647
|
if (SH_SCRIPT_RE2.test(path10)) {
|
|
99981
|
-
const r =
|
|
100648
|
+
const r = spawnSync20("bash", ["-n"], {
|
|
99982
100649
|
input: content,
|
|
99983
100650
|
encoding: "utf-8"
|
|
99984
100651
|
});
|
|
@@ -99990,14 +100657,14 @@ function validatePayload(name, files) {
|
|
|
99990
100657
|
const tmpPy = join103(tmp, "check.py");
|
|
99991
100658
|
try {
|
|
99992
100659
|
writeFileSync42(tmpPy, content);
|
|
99993
|
-
const r =
|
|
100660
|
+
const r = spawnSync20("python3", ["-m", "py_compile", tmpPy], {
|
|
99994
100661
|
encoding: "utf-8"
|
|
99995
100662
|
});
|
|
99996
100663
|
if (r.status !== 0) {
|
|
99997
100664
|
errors2.push(`${path10} fails \`python3 -m py_compile\` syntax check: ${(r.stderr ?? "").trim()}`);
|
|
99998
100665
|
}
|
|
99999
100666
|
} finally {
|
|
100000
|
-
|
|
100667
|
+
rmSync19(tmp, { recursive: true, force: true });
|
|
100001
100668
|
}
|
|
100002
100669
|
}
|
|
100003
100670
|
}
|
|
@@ -100007,7 +100674,7 @@ function validatePayload(name, files) {
|
|
|
100007
100674
|
function diffSummary(currentDir, files) {
|
|
100008
100675
|
const lines = [];
|
|
100009
100676
|
const currentFiles = {};
|
|
100010
|
-
if (
|
|
100677
|
+
if (existsSync102(currentDir)) {
|
|
100011
100678
|
const walk2 = (sub) => {
|
|
100012
100679
|
for (const ent of readdirSync37(sub, { withFileTypes: true })) {
|
|
100013
100680
|
const full = join103(sub, ent.name);
|
|
@@ -100043,8 +100710,8 @@ function diffSummary(currentDir, files) {
|
|
|
100043
100710
|
`);
|
|
100044
100711
|
}
|
|
100045
100712
|
function writePayload(poolDir, name, files) {
|
|
100046
|
-
if (!
|
|
100047
|
-
|
|
100713
|
+
if (!existsSync102(poolDir)) {
|
|
100714
|
+
mkdirSync59(poolDir, { recursive: true, mode: 493 });
|
|
100048
100715
|
}
|
|
100049
100716
|
const target = join103(poolDir, name);
|
|
100050
100717
|
let targetIsSymlink = false;
|
|
@@ -100062,12 +100729,12 @@ function writePayload(poolDir, name, files) {
|
|
|
100062
100729
|
try {
|
|
100063
100730
|
for (const [path10, content] of Object.entries(files)) {
|
|
100064
100731
|
const full = join103(staging, path10);
|
|
100065
|
-
|
|
100066
|
-
const fd =
|
|
100732
|
+
mkdirSync59(dirname40(full), { recursive: true, mode: 493 });
|
|
100733
|
+
const fd = openSync20(full, "wx");
|
|
100067
100734
|
try {
|
|
100068
100735
|
writeFileSync42(fd, content);
|
|
100069
100736
|
} finally {
|
|
100070
|
-
|
|
100737
|
+
closeSync20(fd);
|
|
100071
100738
|
}
|
|
100072
100739
|
if (SH_SCRIPT_RE2.test(path10) || PY_SCRIPT_RE2.test(path10)) {
|
|
100073
100740
|
const fs7 = __require("node:fs");
|
|
@@ -100081,23 +100748,23 @@ function writePayload(poolDir, name, files) {
|
|
|
100081
100748
|
} catch {}
|
|
100082
100749
|
if (targetExists) {
|
|
100083
100750
|
oldRename = `${target}.skill-apply-old-${Date.now()}`;
|
|
100084
|
-
|
|
100751
|
+
renameSync27(target, oldRename);
|
|
100085
100752
|
}
|
|
100086
|
-
|
|
100753
|
+
renameSync27(staging, target);
|
|
100087
100754
|
if (oldRename) {
|
|
100088
|
-
|
|
100755
|
+
rmSync19(oldRename, { recursive: true, force: true });
|
|
100089
100756
|
oldRename = null;
|
|
100090
100757
|
}
|
|
100091
100758
|
} catch (err2) {
|
|
100092
100759
|
try {
|
|
100093
|
-
|
|
100760
|
+
rmSync19(staging, { recursive: true, force: true });
|
|
100094
100761
|
} catch {}
|
|
100095
|
-
if (oldRename &&
|
|
100762
|
+
if (oldRename && existsSync102(oldRename)) {
|
|
100096
100763
|
try {
|
|
100097
|
-
if (
|
|
100098
|
-
|
|
100764
|
+
if (existsSync102(target)) {
|
|
100765
|
+
rmSync19(target, { recursive: true, force: true });
|
|
100099
100766
|
}
|
|
100100
|
-
|
|
100767
|
+
renameSync27(oldRename, target);
|
|
100101
100768
|
} catch {}
|
|
100102
100769
|
}
|
|
100103
100770
|
throw err2;
|
|
@@ -100115,7 +100782,7 @@ function registerSkillCommand(program3) {
|
|
|
100115
100782
|
files = loadFromStdin();
|
|
100116
100783
|
} else {
|
|
100117
100784
|
const fromPath = resolve58(opts.from);
|
|
100118
|
-
if (!
|
|
100785
|
+
if (!existsSync102(fromPath)) {
|
|
100119
100786
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
100120
100787
|
}
|
|
100121
100788
|
const st = statSync54(fromPath);
|
|
@@ -100153,7 +100820,7 @@ function registerSkillCommand(program3) {
|
|
|
100153
100820
|
\u2713 Wrote ${name} to ${currentDir}`));
|
|
100154
100821
|
const applyBin = process.argv[1] ?? "switchroom";
|
|
100155
100822
|
console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
|
|
100156
|
-
const r =
|
|
100823
|
+
const r = spawnSync20(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
|
|
100157
100824
|
if (r.status !== 0) {
|
|
100158
100825
|
console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
|
|
100159
100826
|
}
|
|
@@ -100170,23 +100837,23 @@ function sumBytes(files) {
|
|
|
100170
100837
|
// src/cli/skill-personal.ts
|
|
100171
100838
|
init_esm();
|
|
100172
100839
|
import {
|
|
100173
|
-
closeSync as
|
|
100174
|
-
existsSync as
|
|
100840
|
+
closeSync as closeSync21,
|
|
100841
|
+
existsSync as existsSync103,
|
|
100175
100842
|
lstatSync as lstatSync13,
|
|
100176
|
-
mkdirSync as
|
|
100843
|
+
mkdirSync as mkdirSync60,
|
|
100177
100844
|
mkdtempSync as mkdtempSync6,
|
|
100178
|
-
openSync as
|
|
100845
|
+
openSync as openSync21,
|
|
100179
100846
|
readFileSync as readFileSync91,
|
|
100180
100847
|
readdirSync as readdirSync38,
|
|
100181
|
-
renameSync as
|
|
100182
|
-
rmSync as
|
|
100848
|
+
renameSync as renameSync28,
|
|
100849
|
+
rmSync as rmSync20,
|
|
100183
100850
|
statSync as statSync55,
|
|
100184
100851
|
utimesSync,
|
|
100185
100852
|
writeFileSync as writeFileSync43
|
|
100186
100853
|
} from "node:fs";
|
|
100187
|
-
import { dirname as
|
|
100854
|
+
import { dirname as dirname41, join as join104, relative as relative5, resolve as resolve59 } from "node:path";
|
|
100188
100855
|
import { homedir as homedir54, tmpdir as tmpdir7 } from "node:os";
|
|
100189
|
-
import { spawnSync as
|
|
100856
|
+
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
100190
100857
|
init_helpers();
|
|
100191
100858
|
init_agent_config();
|
|
100192
100859
|
init_source();
|
|
@@ -100197,14 +100864,14 @@ var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
|
100197
100864
|
function resolveConfigSkillsDir(agent) {
|
|
100198
100865
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
100199
100866
|
const candidate = override ? resolve59(override) : join104(homedir54(), ".switchroom-config");
|
|
100200
|
-
if (!
|
|
100867
|
+
if (!existsSync103(candidate))
|
|
100201
100868
|
return null;
|
|
100202
100869
|
return join104(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
100203
100870
|
}
|
|
100204
100871
|
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
100205
100872
|
function sweepMirrorPriors(configSkillsRoot) {
|
|
100206
100873
|
try {
|
|
100207
|
-
if (!
|
|
100874
|
+
if (!existsSync103(configSkillsRoot))
|
|
100208
100875
|
return;
|
|
100209
100876
|
const now = Date.now();
|
|
100210
100877
|
for (const ent of readdirSync38(configSkillsRoot)) {
|
|
@@ -100217,7 +100884,7 @@ function sweepMirrorPriors(configSkillsRoot) {
|
|
|
100217
100884
|
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
100218
100885
|
continue;
|
|
100219
100886
|
try {
|
|
100220
|
-
|
|
100887
|
+
rmSync20(join104(configSkillsRoot, ent), { recursive: true, force: true });
|
|
100221
100888
|
} catch {}
|
|
100222
100889
|
}
|
|
100223
100890
|
} catch {}
|
|
@@ -100240,17 +100907,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
100240
100907
|
}
|
|
100241
100908
|
if (liveSkillDir === null) {
|
|
100242
100909
|
sweepMirrorPriors(configSkillsRoot);
|
|
100243
|
-
if (
|
|
100910
|
+
if (existsSync103(dest)) {
|
|
100244
100911
|
const trash = join104(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
100245
|
-
|
|
100912
|
+
renameSync28(dest, trash);
|
|
100246
100913
|
}
|
|
100247
100914
|
return;
|
|
100248
100915
|
}
|
|
100249
|
-
|
|
100916
|
+
mkdirSync60(configSkillsRoot, { recursive: true, mode: 493 });
|
|
100250
100917
|
sweepMirrorPriors(configSkillsRoot);
|
|
100251
100918
|
const staging = mkdtempSync6(join104(configSkillsRoot, `.${name}-staging-`));
|
|
100252
100919
|
const walk2 = (src, dst) => {
|
|
100253
|
-
|
|
100920
|
+
mkdirSync60(dst, { recursive: true, mode: 493 });
|
|
100254
100921
|
for (const ent of readdirSync38(src, { withFileTypes: true })) {
|
|
100255
100922
|
const s = join104(src, ent.name);
|
|
100256
100923
|
const d = join104(dst, ent.name);
|
|
@@ -100264,11 +100931,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
100264
100931
|
}
|
|
100265
100932
|
};
|
|
100266
100933
|
walk2(liveSkillDir, staging);
|
|
100267
|
-
if (
|
|
100934
|
+
if (existsSync103(dest)) {
|
|
100268
100935
|
const prior = join104(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
100269
|
-
|
|
100936
|
+
renameSync28(dest, prior);
|
|
100270
100937
|
}
|
|
100271
|
-
|
|
100938
|
+
renameSync28(staging, dest);
|
|
100272
100939
|
} catch (err2) {
|
|
100273
100940
|
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.
|
|
100274
100941
|
`));
|
|
@@ -100305,7 +100972,7 @@ function trashDir(agentsRoot, agent) {
|
|
|
100305
100972
|
}
|
|
100306
100973
|
function countPersonalSkills(agentsRoot, agent) {
|
|
100307
100974
|
const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
|
|
100308
|
-
if (!
|
|
100975
|
+
if (!existsSync103(skillsDir))
|
|
100309
100976
|
return 0;
|
|
100310
100977
|
let n = 0;
|
|
100311
100978
|
for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
|
|
@@ -100390,7 +101057,7 @@ function behavioralValidate(files) {
|
|
|
100390
101057
|
const errors2 = [];
|
|
100391
101058
|
for (const [path10, content] of Object.entries(files)) {
|
|
100392
101059
|
if (SH_SCRIPT_RE.test(path10)) {
|
|
100393
|
-
const r =
|
|
101060
|
+
const r = spawnSync21("bash", ["-n"], { input: content, encoding: "utf-8" });
|
|
100394
101061
|
if (r.status !== 0) {
|
|
100395
101062
|
errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
|
|
100396
101063
|
}
|
|
@@ -100399,14 +101066,14 @@ function behavioralValidate(files) {
|
|
|
100399
101066
|
const tmpPy = join104(tmp, "check.py");
|
|
100400
101067
|
try {
|
|
100401
101068
|
writeFileSync43(tmpPy, content);
|
|
100402
|
-
const r =
|
|
101069
|
+
const r = spawnSync21("python3", ["-m", "py_compile", tmpPy], {
|
|
100403
101070
|
encoding: "utf-8"
|
|
100404
101071
|
});
|
|
100405
101072
|
if (r.status !== 0) {
|
|
100406
101073
|
errors2.push(`${path10} fails \`python3 -m py_compile\`: ${(r.stderr ?? "").trim()}`);
|
|
100407
101074
|
}
|
|
100408
101075
|
} finally {
|
|
100409
|
-
|
|
101076
|
+
rmSync20(tmp, { recursive: true, force: true });
|
|
100410
101077
|
}
|
|
100411
101078
|
}
|
|
100412
101079
|
}
|
|
@@ -100414,7 +101081,7 @@ function behavioralValidate(files) {
|
|
|
100414
101081
|
}
|
|
100415
101082
|
function sweepTrash(agentsRoot, agent) {
|
|
100416
101083
|
const trash = trashDir(agentsRoot, agent);
|
|
100417
|
-
if (!
|
|
101084
|
+
if (!existsSync103(trash))
|
|
100418
101085
|
return;
|
|
100419
101086
|
const now = Date.now();
|
|
100420
101087
|
for (const ent of readdirSync38(trash, { withFileTypes: true })) {
|
|
@@ -100424,7 +101091,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
100424
101091
|
try {
|
|
100425
101092
|
const st = statSync55(entPath);
|
|
100426
101093
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
100427
|
-
|
|
101094
|
+
rmSync20(entPath, { recursive: true, force: true });
|
|
100428
101095
|
}
|
|
100429
101096
|
} catch {}
|
|
100430
101097
|
}
|
|
@@ -100440,18 +101107,18 @@ function writePersonalSkill(targetDir, files) {
|
|
|
100440
101107
|
if (targetIsSymlink) {
|
|
100441
101108
|
fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
100442
101109
|
}
|
|
100443
|
-
|
|
100444
|
-
const staging = mkdtempSync6(join104(
|
|
101110
|
+
mkdirSync60(dirname41(targetDir), { recursive: true, mode: 493 });
|
|
101111
|
+
const staging = mkdtempSync6(join104(dirname41(targetDir), `.skill-personal-stage-`));
|
|
100445
101112
|
let oldRename = null;
|
|
100446
101113
|
try {
|
|
100447
101114
|
for (const [path10, content] of Object.entries(files)) {
|
|
100448
101115
|
const full = join104(staging, path10);
|
|
100449
|
-
|
|
100450
|
-
const fd =
|
|
101116
|
+
mkdirSync60(dirname41(full), { recursive: true, mode: 493 });
|
|
101117
|
+
const fd = openSync21(full, "wx");
|
|
100451
101118
|
try {
|
|
100452
101119
|
writeFileSync43(fd, content);
|
|
100453
101120
|
} finally {
|
|
100454
|
-
|
|
101121
|
+
closeSync21(fd);
|
|
100455
101122
|
}
|
|
100456
101123
|
if (SH_SCRIPT_RE.test(path10) || PY_SCRIPT_RE.test(path10)) {
|
|
100457
101124
|
const fs7 = __require("node:fs");
|
|
@@ -100465,23 +101132,23 @@ function writePersonalSkill(targetDir, files) {
|
|
|
100465
101132
|
} catch {}
|
|
100466
101133
|
if (targetExists) {
|
|
100467
101134
|
oldRename = `${targetDir}.personal-old-${Date.now()}`;
|
|
100468
|
-
|
|
101135
|
+
renameSync28(targetDir, oldRename);
|
|
100469
101136
|
}
|
|
100470
|
-
|
|
101137
|
+
renameSync28(staging, targetDir);
|
|
100471
101138
|
if (oldRename) {
|
|
100472
|
-
|
|
101139
|
+
rmSync20(oldRename, { recursive: true, force: true });
|
|
100473
101140
|
oldRename = null;
|
|
100474
101141
|
}
|
|
100475
101142
|
} catch (err2) {
|
|
100476
101143
|
try {
|
|
100477
|
-
|
|
101144
|
+
rmSync20(staging, { recursive: true, force: true });
|
|
100478
101145
|
} catch {}
|
|
100479
|
-
if (oldRename &&
|
|
101146
|
+
if (oldRename && existsSync103(oldRename)) {
|
|
100480
101147
|
try {
|
|
100481
|
-
if (
|
|
100482
|
-
|
|
101148
|
+
if (existsSync103(targetDir)) {
|
|
101149
|
+
rmSync20(targetDir, { recursive: true, force: true });
|
|
100483
101150
|
}
|
|
100484
|
-
|
|
101151
|
+
renameSync28(oldRename, targetDir);
|
|
100485
101152
|
} catch {}
|
|
100486
101153
|
}
|
|
100487
101154
|
throw err2;
|
|
@@ -100544,7 +101211,7 @@ function loadFiles(opts) {
|
|
|
100544
101211
|
return loadFromStdin2();
|
|
100545
101212
|
}
|
|
100546
101213
|
const p = resolve59(opts.from);
|
|
100547
|
-
if (!
|
|
101214
|
+
if (!existsSync103(p)) {
|
|
100548
101215
|
fail5(`--from path does not exist: ${opts.from}`);
|
|
100549
101216
|
}
|
|
100550
101217
|
const st = statSync55(p);
|
|
@@ -100606,7 +101273,7 @@ function resolveCloneSource(source, opts) {
|
|
|
100606
101273
|
const slug = m[2];
|
|
100607
101274
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
100608
101275
|
const dir = join104(root, slug);
|
|
100609
|
-
if (!
|
|
101276
|
+
if (!existsSync103(dir)) {
|
|
100610
101277
|
fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
100611
101278
|
}
|
|
100612
101279
|
const st = lstatSync13(dir);
|
|
@@ -100730,10 +101397,10 @@ function removePersonalAction(name, opts) {
|
|
|
100730
101397
|
throw err2;
|
|
100731
101398
|
}
|
|
100732
101399
|
const trashRoot2 = trashDir(agentsRoot, agent);
|
|
100733
|
-
|
|
101400
|
+
mkdirSync60(trashRoot2, { recursive: true, mode: 493 });
|
|
100734
101401
|
const ts = Date.now();
|
|
100735
101402
|
const trashTarget = join104(trashRoot2, `${name}-${ts}`);
|
|
100736
|
-
|
|
101403
|
+
renameSync28(target, trashTarget);
|
|
100737
101404
|
const now = new Date(ts);
|
|
100738
101405
|
utimesSync(trashTarget, now, now);
|
|
100739
101406
|
mirrorToConfigRepo(agent, name, null);
|
|
@@ -100753,7 +101420,7 @@ function listPersonalAction(opts) {
|
|
|
100753
101420
|
sweepTrash(agentsRoot, agent);
|
|
100754
101421
|
const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
|
|
100755
101422
|
const personal = [];
|
|
100756
|
-
if (
|
|
101423
|
+
if (existsSync103(skillsDir)) {
|
|
100757
101424
|
for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
|
|
100758
101425
|
if (!ent.isDirectory())
|
|
100759
101426
|
continue;
|
|
@@ -100886,7 +101553,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
|
|
|
100886
101553
|
init_esm();
|
|
100887
101554
|
init_helpers();
|
|
100888
101555
|
var import_yaml25 = __toESM(require_dist(), 1);
|
|
100889
|
-
import { existsSync as
|
|
101556
|
+
import { existsSync as existsSync104, readdirSync as readdirSync39, readFileSync as readFileSync93, statSync as statSync56 } from "node:fs";
|
|
100890
101557
|
import { homedir as homedir56 } from "node:os";
|
|
100891
101558
|
import { join as join106, resolve as resolve60 } from "node:path";
|
|
100892
101559
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -100903,7 +101570,7 @@ function defaultBundledRoot2() {
|
|
|
100903
101570
|
}
|
|
100904
101571
|
function readSkillFrontmatter(skillDir) {
|
|
100905
101572
|
const mdPath = join106(skillDir, "SKILL.md");
|
|
100906
|
-
if (!
|
|
101573
|
+
if (!existsSync104(mdPath))
|
|
100907
101574
|
return null;
|
|
100908
101575
|
let content;
|
|
100909
101576
|
try {
|
|
@@ -100947,7 +101614,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
100947
101614
|
if (!AGENT_NAME_RE3.test(agent))
|
|
100948
101615
|
return [];
|
|
100949
101616
|
const skillsDir = join106(agentsRoot, agent, ".claude/skills");
|
|
100950
|
-
if (!
|
|
101617
|
+
if (!existsSync104(skillsDir))
|
|
100951
101618
|
return [];
|
|
100952
101619
|
const out = [];
|
|
100953
101620
|
let entries;
|
|
@@ -100985,7 +101652,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
100985
101652
|
return out;
|
|
100986
101653
|
}
|
|
100987
101654
|
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
100988
|
-
if (!
|
|
101655
|
+
if (!existsSync104(sharedRoot))
|
|
100989
101656
|
return [];
|
|
100990
101657
|
const out = [];
|
|
100991
101658
|
let entries;
|
|
@@ -101023,7 +101690,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
101023
101690
|
return out;
|
|
101024
101691
|
}
|
|
101025
101692
|
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
101026
|
-
if (!
|
|
101693
|
+
if (!existsSync104(bundledRoot))
|
|
101027
101694
|
return [];
|
|
101028
101695
|
const out = [];
|
|
101029
101696
|
let entries;
|
|
@@ -101181,24 +101848,24 @@ init_source();
|
|
|
101181
101848
|
init_helpers();
|
|
101182
101849
|
init_operator_uid();
|
|
101183
101850
|
import {
|
|
101184
|
-
existsSync as
|
|
101185
|
-
mkdirSync as
|
|
101851
|
+
existsSync as existsSync106,
|
|
101852
|
+
mkdirSync as mkdirSync61,
|
|
101186
101853
|
readdirSync as readdirSync40,
|
|
101187
101854
|
writeFileSync as writeFileSync44,
|
|
101188
101855
|
statSync as statSync57,
|
|
101189
101856
|
lstatSync as lstatSync14,
|
|
101190
101857
|
realpathSync as realpathSync8,
|
|
101191
|
-
copyFileSync as
|
|
101858
|
+
copyFileSync as copyFileSync14
|
|
101192
101859
|
} from "node:fs";
|
|
101193
101860
|
import { homedir as homedir57 } from "node:os";
|
|
101194
101861
|
import { join as join107 } from "node:path";
|
|
101195
|
-
import { spawnSync as
|
|
101862
|
+
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
101196
101863
|
|
|
101197
101864
|
// src/cli/singleton-stale-cleanup.ts
|
|
101198
|
-
import { spawnSync as
|
|
101865
|
+
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
101199
101866
|
function makeDockerRunner() {
|
|
101200
101867
|
return (args) => {
|
|
101201
|
-
const r =
|
|
101868
|
+
const r = spawnSync23("docker", args, { encoding: "utf8" });
|
|
101202
101869
|
return {
|
|
101203
101870
|
ok: r.status === 0,
|
|
101204
101871
|
stdout: r.stdout ?? "",
|
|
@@ -101557,7 +102224,7 @@ function resolveHostdSkillsTarget(hostHome) {
|
|
|
101557
102224
|
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.`);
|
|
101558
102225
|
return;
|
|
101559
102226
|
}
|
|
101560
|
-
if (!
|
|
102227
|
+
if (!existsSync106(target)) {
|
|
101561
102228
|
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.`);
|
|
101562
102229
|
return;
|
|
101563
102230
|
}
|
|
@@ -101571,15 +102238,15 @@ function hostdComposePath() {
|
|
|
101571
102238
|
}
|
|
101572
102239
|
function backupExistingCompose() {
|
|
101573
102240
|
const p = hostdComposePath();
|
|
101574
|
-
if (!
|
|
102241
|
+
if (!existsSync106(p))
|
|
101575
102242
|
return null;
|
|
101576
102243
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
101577
102244
|
const bak = `${p}.bak-${ts}`;
|
|
101578
|
-
|
|
102245
|
+
copyFileSync14(p, bak);
|
|
101579
102246
|
return bak;
|
|
101580
102247
|
}
|
|
101581
102248
|
function runDocker(args) {
|
|
101582
|
-
const r =
|
|
102249
|
+
const r = spawnSync24("docker", args, { encoding: "utf8" });
|
|
101583
102250
|
return {
|
|
101584
102251
|
ok: r.status === 0,
|
|
101585
102252
|
stdout: r.stdout ?? "",
|
|
@@ -101604,7 +102271,7 @@ async function doInstall(opts, program3) {
|
|
|
101604
102271
|
}
|
|
101605
102272
|
const dir = hostdDir();
|
|
101606
102273
|
const composePath = hostdComposePath();
|
|
101607
|
-
|
|
102274
|
+
mkdirSync61(dir, { recursive: true });
|
|
101608
102275
|
const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
|
|
101609
102276
|
const guard = checkDowngrade({
|
|
101610
102277
|
container: "switchroom-hostd",
|
|
@@ -101669,7 +102336,7 @@ function doStatus() {
|
|
|
101669
102336
|
const composeYml = hostdComposePath();
|
|
101670
102337
|
console.log(source_default.bold("switchroom-hostd"));
|
|
101671
102338
|
console.log("");
|
|
101672
|
-
if (!
|
|
102339
|
+
if (!existsSync106(composeYml)) {
|
|
101673
102340
|
console.log(source_default.yellow(" compose: not installed"));
|
|
101674
102341
|
console.log(source_default.dim(" run `switchroom hostd install` to set up."));
|
|
101675
102342
|
return;
|
|
@@ -101690,14 +102357,14 @@ function doStatus() {
|
|
|
101690
102357
|
} else {
|
|
101691
102358
|
console.log(source_default.green(` container: ${ps.stdout.trim()}`));
|
|
101692
102359
|
}
|
|
101693
|
-
if (
|
|
102360
|
+
if (existsSync106(dir)) {
|
|
101694
102361
|
const entries = [];
|
|
101695
102362
|
try {
|
|
101696
102363
|
for (const name of readdirSync40(dir)) {
|
|
101697
102364
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
101698
102365
|
continue;
|
|
101699
102366
|
const sockPath = join107(dir, name, "sock");
|
|
101700
|
-
if (
|
|
102367
|
+
if (existsSync106(sockPath)) {
|
|
101701
102368
|
const st = statSync57(sockPath);
|
|
101702
102369
|
if ((st.mode & 61440) === 49152) {
|
|
101703
102370
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
@@ -101716,7 +102383,7 @@ function doStatus() {
|
|
|
101716
102383
|
}
|
|
101717
102384
|
function doUninstall() {
|
|
101718
102385
|
const composeYml = hostdComposePath();
|
|
101719
|
-
if (!
|
|
102386
|
+
if (!existsSync106(composeYml)) {
|
|
101720
102387
|
console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
|
|
101721
102388
|
return;
|
|
101722
102389
|
}
|
|
@@ -101740,7 +102407,7 @@ function registerHostdCommand(program3) {
|
|
|
101740
102407
|
hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
|
|
101741
102408
|
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) => {
|
|
101742
102409
|
const logPath = opts.path ?? defaultAuditLogPath2();
|
|
101743
|
-
if (!
|
|
102410
|
+
if (!existsSync106(logPath)) {
|
|
101744
102411
|
console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
|
|
101745
102412
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
101746
102413
|
return;
|
|
@@ -101788,10 +102455,10 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
101788
102455
|
init_source();
|
|
101789
102456
|
init_helpers();
|
|
101790
102457
|
init_operator_uid();
|
|
101791
|
-
import { chownSync as chownSync10, existsSync as
|
|
102458
|
+
import { chownSync as chownSync10, existsSync as existsSync107, mkdirSync as mkdirSync62, writeFileSync as writeFileSync45, copyFileSync as copyFileSync15 } from "node:fs";
|
|
101792
102459
|
import { homedir as homedir58 } from "node:os";
|
|
101793
102460
|
import { join as join108 } from "node:path";
|
|
101794
|
-
import { spawnSync as
|
|
102461
|
+
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
101795
102462
|
function resolveWebImageTag(explicitTag, release) {
|
|
101796
102463
|
if (explicitTag)
|
|
101797
102464
|
return explicitTag;
|
|
@@ -101897,15 +102564,15 @@ function webdComposePath() {
|
|
|
101897
102564
|
}
|
|
101898
102565
|
function backupExistingCompose2() {
|
|
101899
102566
|
const p = webdComposePath();
|
|
101900
|
-
if (!
|
|
102567
|
+
if (!existsSync107(p))
|
|
101901
102568
|
return null;
|
|
101902
102569
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
101903
102570
|
const bak = `${p}.bak-${ts}`;
|
|
101904
|
-
|
|
102571
|
+
copyFileSync15(p, bak);
|
|
101905
102572
|
return bak;
|
|
101906
102573
|
}
|
|
101907
102574
|
function runDocker2(args) {
|
|
101908
|
-
const r =
|
|
102575
|
+
const r = spawnSync25("docker", args, { encoding: "utf8" });
|
|
101909
102576
|
return {
|
|
101910
102577
|
ok: r.status === 0,
|
|
101911
102578
|
stdout: r.stdout ?? "",
|
|
@@ -101922,7 +102589,7 @@ async function doInstall2(opts, program3) {
|
|
|
101922
102589
|
}
|
|
101923
102590
|
const dir = webdDir();
|
|
101924
102591
|
const composePath = webdComposePath();
|
|
101925
|
-
|
|
102592
|
+
mkdirSync62(dir, { recursive: true });
|
|
101926
102593
|
const cfg = getConfig(program3);
|
|
101927
102594
|
const imageTag = resolveWebImageTag(opts.tag, cfg.release);
|
|
101928
102595
|
const port = cfg.web_service?.port ?? 8080;
|
|
@@ -101999,7 +102666,7 @@ function doStatus2() {
|
|
|
101999
102666
|
const composeYml = webdComposePath();
|
|
102000
102667
|
console.log(source_default.bold("switchroom-web"));
|
|
102001
102668
|
console.log("");
|
|
102002
|
-
if (!
|
|
102669
|
+
if (!existsSync107(composeYml)) {
|
|
102003
102670
|
console.log(source_default.yellow(" compose: not installed"));
|
|
102004
102671
|
console.log(source_default.dim(" run `switchroom webd install` to set up."));
|
|
102005
102672
|
return;
|
|
@@ -102023,7 +102690,7 @@ function doStatus2() {
|
|
|
102023
102690
|
}
|
|
102024
102691
|
function doUninstall2() {
|
|
102025
102692
|
const composeYml = webdComposePath();
|
|
102026
|
-
if (!
|
|
102693
|
+
if (!existsSync107(composeYml)) {
|
|
102027
102694
|
console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
|
|
102028
102695
|
return;
|
|
102029
102696
|
}
|
|
@@ -102209,7 +102876,7 @@ function applyMountRepairs(items, deps) {
|
|
|
102209
102876
|
function registerHostCommand(program3) {
|
|
102210
102877
|
const host = program3.command("host").description("Host-level maintenance operations for switchroom");
|
|
102211
102878
|
host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
|
|
102212
|
-
const { rmdirSync: rmdirSync2, rmSync:
|
|
102879
|
+
const { rmdirSync: rmdirSync2, rmSync: rmSync21, lstatSync: lstatSync15, readdirSync: readdirSync41 } = await import("node:fs");
|
|
102213
102880
|
const probe2 = {
|
|
102214
102881
|
lstat(path10) {
|
|
102215
102882
|
try {
|
|
@@ -102268,7 +102935,7 @@ ${safeItems.length} item(s) would be removed. Run with --yes to apply.`));
|
|
|
102268
102935
|
rmdirSync2(path10);
|
|
102269
102936
|
},
|
|
102270
102937
|
rmRf(path10) {
|
|
102271
|
-
|
|
102938
|
+
rmSync21(path10, { recursive: true, force: true });
|
|
102272
102939
|
}
|
|
102273
102940
|
};
|
|
102274
102941
|
const results = applyMountRepairs(safeItems, applyDeps);
|
|
@@ -102298,13 +102965,13 @@ init_helpers();
|
|
|
102298
102965
|
init_scan();
|
|
102299
102966
|
|
|
102300
102967
|
// src/fleet-health/gh-sync.ts
|
|
102301
|
-
import { spawnSync as
|
|
102968
|
+
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
102302
102969
|
var LABEL = "fleet-health";
|
|
102303
102970
|
function defaultGhDeps(log) {
|
|
102304
102971
|
return {
|
|
102305
102972
|
log,
|
|
102306
102973
|
run: (args) => {
|
|
102307
|
-
const r =
|
|
102974
|
+
const r = spawnSync26("gh", args, { encoding: "utf-8" });
|
|
102308
102975
|
return {
|
|
102309
102976
|
ok: r.status === 0,
|
|
102310
102977
|
stdout: (r.stdout ?? "").trim(),
|
|
@@ -102509,7 +103176,7 @@ function printDeepDiveBrief(targets) {
|
|
|
102509
103176
|
|
|
102510
103177
|
// src/cli/hindsight-watch.ts
|
|
102511
103178
|
init_source();
|
|
102512
|
-
import { existsSync as
|
|
103179
|
+
import { existsSync as existsSync112, readdirSync as readdirSync45 } from "node:fs";
|
|
102513
103180
|
import { homedir as homedir62 } from "node:os";
|
|
102514
103181
|
import { join as join111, resolve as resolve63 } from "node:path";
|
|
102515
103182
|
|
|
@@ -102579,7 +103246,7 @@ function postOperatorNoticeViaGateways(candidates, text, log, connectTimeoutMs =
|
|
|
102579
103246
|
init_install_cron();
|
|
102580
103247
|
|
|
102581
103248
|
// src/hindsight-watch/probe.ts
|
|
102582
|
-
import { spawnSync as
|
|
103249
|
+
import { spawnSync as spawnSync27 } from "node:child_process";
|
|
102583
103250
|
import { readFileSync as readFileSync98, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
|
|
102584
103251
|
import { homedir as homedir61 } from "node:os";
|
|
102585
103252
|
import { resolve as resolve62 } from "node:path";
|
|
@@ -102708,7 +103375,7 @@ function readLlmSignals(series) {
|
|
|
102708
103375
|
}
|
|
102709
103376
|
|
|
102710
103377
|
// src/hindsight-watch/recall-log.ts
|
|
102711
|
-
import { closeSync as
|
|
103378
|
+
import { closeSync as closeSync22, existsSync as existsSync111, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as statSync58 } from "node:fs";
|
|
102712
103379
|
import { join as join110 } from "node:path";
|
|
102713
103380
|
var RECALL_WINDOW_ROWS = 200;
|
|
102714
103381
|
var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
|
|
@@ -102717,7 +103384,7 @@ function recallLogPath2(agentDir) {
|
|
|
102717
103384
|
return join110(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
102718
103385
|
}
|
|
102719
103386
|
function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
102720
|
-
if (!
|
|
103387
|
+
if (!existsSync111(path10))
|
|
102721
103388
|
return [];
|
|
102722
103389
|
let text;
|
|
102723
103390
|
let truncatedHead = false;
|
|
@@ -102728,10 +103395,10 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
102728
103395
|
truncatedHead = start > 0;
|
|
102729
103396
|
const length = size - start;
|
|
102730
103397
|
const buf = Buffer.alloc(length);
|
|
102731
|
-
fd =
|
|
103398
|
+
fd = openSync22(path10, "r");
|
|
102732
103399
|
let read = 0;
|
|
102733
103400
|
while (read < length) {
|
|
102734
|
-
const n =
|
|
103401
|
+
const n = readSync6(fd, buf, read, length - read, start + read);
|
|
102735
103402
|
if (n <= 0)
|
|
102736
103403
|
break;
|
|
102737
103404
|
read += n;
|
|
@@ -102742,7 +103409,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
|
|
|
102742
103409
|
} finally {
|
|
102743
103410
|
if (fd !== undefined) {
|
|
102744
103411
|
try {
|
|
102745
|
-
|
|
103412
|
+
closeSync22(fd);
|
|
102746
103413
|
} catch {}
|
|
102747
103414
|
}
|
|
102748
103415
|
}
|
|
@@ -102929,7 +103596,7 @@ async function probeMetrics(url = DEFAULT_METRICS_URL, fetchImpl = fetch) {
|
|
|
102929
103596
|
}
|
|
102930
103597
|
}
|
|
102931
103598
|
var defaultRunner4 = (cmd, args) => {
|
|
102932
|
-
const r =
|
|
103599
|
+
const r = spawnSync27(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
|
|
102933
103600
|
if (r.error)
|
|
102934
103601
|
return { status: null, stdout: "", stderr: r.error.message };
|
|
102935
103602
|
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
@@ -103749,7 +104416,7 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
103749
104416
|
try {
|
|
103750
104417
|
for (const name of readdirSync45(agentsDir).sort()) {
|
|
103751
104418
|
const sock = resolve63(agentsDir, name, "telegram", "gateway.sock");
|
|
103752
|
-
if (
|
|
104419
|
+
if (existsSync112(sock))
|
|
103753
104420
|
candidates.push({ agent: name, sock });
|
|
103754
104421
|
}
|
|
103755
104422
|
} catch (e) {
|
|
@@ -103765,13 +104432,13 @@ async function notifyOperator(agentsDir, text, log) {
|
|
|
103765
104432
|
|
|
103766
104433
|
// src/cli/openrouter-watch.ts
|
|
103767
104434
|
init_source();
|
|
103768
|
-
import { existsSync as
|
|
104435
|
+
import { existsSync as existsSync114, readdirSync as readdirSync46 } from "node:fs";
|
|
103769
104436
|
import { homedir as homedir64 } from "node:os";
|
|
103770
104437
|
import { join as join112, resolve as resolve65 } from "node:path";
|
|
103771
104438
|
|
|
103772
104439
|
// src/openrouter/install-cron.ts
|
|
103773
|
-
import { existsSync as
|
|
103774
|
-
import { dirname as
|
|
104440
|
+
import { existsSync as existsSync113, mkdirSync as mkdirSync64, readFileSync as readFileSync99, renameSync as renameSync29, writeFileSync as writeFileSync47 } from "node:fs";
|
|
104441
|
+
import { dirname as dirname43 } from "node:path";
|
|
103775
104442
|
var CRON_PATH2 = "/etc/cron.d/openrouter-watch";
|
|
103776
104443
|
var CRON_SCHEDULE2 = "17 * * * *";
|
|
103777
104444
|
var CRON_LOG_PATH2 = "/var/log/openrouter-watch.log";
|
|
@@ -103788,23 +104455,23 @@ function renderCron2(opts) {
|
|
|
103788
104455
|
function installCron2(opts) {
|
|
103789
104456
|
const path10 = opts.path ?? CRON_PATH2;
|
|
103790
104457
|
const content = renderCron2(opts);
|
|
103791
|
-
if (
|
|
104458
|
+
if (existsSync113(path10)) {
|
|
103792
104459
|
try {
|
|
103793
104460
|
if (readFileSync99(path10, "utf8") === content) {
|
|
103794
104461
|
return { status: "unchanged", path: path10, content };
|
|
103795
104462
|
}
|
|
103796
104463
|
} catch {}
|
|
103797
104464
|
}
|
|
103798
|
-
|
|
104465
|
+
mkdirSync64(dirname43(path10), { recursive: true });
|
|
103799
104466
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
103800
104467
|
writeFileSync47(tmp, content, { mode: 420 });
|
|
103801
|
-
|
|
104468
|
+
renameSync29(tmp, path10);
|
|
103802
104469
|
return { status: "installed", path: path10, content };
|
|
103803
104470
|
}
|
|
103804
104471
|
// src/openrouter/state.ts
|
|
103805
|
-
import { mkdirSync as
|
|
104472
|
+
import { mkdirSync as mkdirSync65, readFileSync as readFileSync100, renameSync as renameSync30, writeFileSync as writeFileSync48 } from "node:fs";
|
|
103806
104473
|
import { homedir as homedir63 } from "node:os";
|
|
103807
|
-
import { dirname as
|
|
104474
|
+
import { dirname as dirname44, resolve as resolve64 } from "node:path";
|
|
103808
104475
|
function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir63()) {
|
|
103809
104476
|
return resolve64(home2, ".switchroom", "openrouter-watch", "state.json");
|
|
103810
104477
|
}
|
|
@@ -103831,12 +104498,12 @@ function loadState2(path10) {
|
|
|
103831
104498
|
return out;
|
|
103832
104499
|
}
|
|
103833
104500
|
function saveState2(path10, state) {
|
|
103834
|
-
|
|
104501
|
+
mkdirSync65(dirname44(path10), { recursive: true, mode: 448 });
|
|
103835
104502
|
const tmp = `${path10}.${process.pid}.tmp`;
|
|
103836
104503
|
const payload = { v: 1, ...state };
|
|
103837
104504
|
writeFileSync48(tmp, JSON.stringify(payload, null, 2) + `
|
|
103838
104505
|
`, { mode: 384 });
|
|
103839
|
-
|
|
104506
|
+
renameSync30(tmp, path10);
|
|
103840
104507
|
}
|
|
103841
104508
|
|
|
103842
104509
|
// src/openrouter/watch.ts
|
|
@@ -104021,7 +104688,7 @@ async function notifyOperator2(agentsDir, text, log) {
|
|
|
104021
104688
|
try {
|
|
104022
104689
|
for (const name of readdirSync46(agentsDir).sort()) {
|
|
104023
104690
|
const sock = resolve65(agentsDir, name, "telegram", "gateway.sock");
|
|
104024
|
-
if (
|
|
104691
|
+
if (existsSync114(sock))
|
|
104025
104692
|
candidates.push({ agent: name, sock });
|
|
104026
104693
|
}
|
|
104027
104694
|
} catch (e) {
|