switchroom 0.15.11 → 0.15.12
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 +5 -0
- package/dist/auth-broker/index.js +5 -0
- package/dist/cli/notion-write-pretool.mjs +5 -0
- package/dist/cli/switchroom.js +290 -181
- package/dist/host-control/main.js +5 -0
- package/dist/vault/approvals/kernel-server.js +5 -0
- package/dist/vault/broker/server.js +5 -0
- package/package.json +1 -1
- package/telegram-plugin/bridge/bridge.ts +24 -0
- package/telegram-plugin/dist/bridge/bridge.js +23 -0
- package/telegram-plugin/dist/gateway/gateway.js +200 -6
- package/telegram-plugin/dist/server.js +23 -0
- package/telegram-plugin/gateway/gateway.ts +8 -0
- package/telegram-plugin/gateway/linear-activity.ts +160 -0
- package/telegram-plugin/gateway/model-command.ts +13 -5
- package/telegram-plugin/tests/gateway-request-secret.test.ts +1 -1
- package/telegram-plugin/tests/linear-agent-activity.test.ts +124 -0
- package/telegram-plugin/tests/model-command.test.ts +40 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -13711,6 +13711,11 @@ var init_schema = __esm(() => {
|
|
|
13711
13711
|
}).optional().describe("Per-source rate limit for the webhook ingest path (#714). " + "Off by default \u2014 when this key is absent the handler skips " + "rate-limit checks entirely. Opt in by setting `rpm` to an " + "integer requests-per-minute (token bucket per (agent, source); " + "burst equal to rpm). When enabled, exceeding the limit returns " + "429 with Retry-After header; first throttle event per " + "(agent, source) per 60s window is written to " + "<agent>/telegram/issues.jsonl. " + "Cascades from defaults.channels.telegram.webhook_rate_limit."),
|
|
13712
13712
|
webhook_via_gateway: exports_external.boolean().optional().describe("Route verified webhook events to the agent's in-container gateway " + "over a peercred-gated UDS (<agent>/telegram/webhook.sock) instead " + "of having the host-side web receiver write the agent dir directly. " + "Required under the Docker runtime: the receiver runs as the host " + "operator UID and cannot write the per-agent-UID-owned agent dir " + "(EACCES 500) nor connect the gateway socket. When true the gateway " + "(running as the agent UID) becomes the sole writer of " + "webhook-events.jsonl + dedup/cooldown state and also fires " + "webhook_dispatch. Off by default for back-compat with host-runtime " + "installs. See docs/rfcs/webhook-via-gateway-socket.md."),
|
|
13713
13713
|
webhook_require_edge: exports_external.boolean().optional().describe("Cloudflare-only edge lock: require the X-Switchroom-Edge header " + "(injected by a Cloudflare Transform Rule on hooks.switchroom.ai) to " + "match the operator's edge secret at ~/.switchroom/webhook-edge-secret " + "before any HMAC verification; reject 403 otherwise. Proves the " + "request entered through our Cloudflare edge \u2014 the per-agent HMAC " + "alone can't (it proves body provenance, not network path). Stacks " + "on the GitHub-IP WAF + per-agent HMAC. Fail-closed: when required " + "but the secret file is missing/empty every request is rejected. Off " + "by default. See docs/rfcs/webhook-cloudflare-edge-lock.md."),
|
|
13714
|
+
linear_agent: exports_external.object({
|
|
13715
|
+
enabled: exports_external.boolean(),
|
|
13716
|
+
token: exports_external.string().describe("vault:<key> reference to the Linear OAuth app token (actor=app). " + "Resolved at runtime via the vault broker (canonically " + "vault:linear/<agent>/token). Never an inline literal."),
|
|
13717
|
+
workspace_id: exports_external.string().optional().describe("Optional Linear workspace (organization) id this agent is " + "installed into. Informational \u2014 used for setup hints and " + "multi-workspace disambiguation; the token already scopes the " + "app to its workspace.")
|
|
13718
|
+
}).optional().describe("Linear first-class agent integration (#2298). When enabled, the " + "agent appears in a Linear workspace as an app actor (own name/" + "avatar, @-mentionable, delegate-assignable). Linear AgentSessionEvent " + "webhooks (mention / delegation) wake the agent instantly via the " + "same gateway inject path as webhook_dispatch, tagged " + 'meta.source="linear" with the agent_session_id, and the agent ' + "responds with structured AgentActivity (thought/message/complete/" + "error) via the linear_agent_activity MCP tool. Builds the " + "session-lifecycle layer on top of the plain webhook_sources:[linear] " + "+ webhook_dispatch support (#2272). The OAuth app token is stored in " + "the vault and referenced here as vault:linear/<agent>/token; run " + "`switchroom linear-agent setup <agent>` to provision it. Off by " + "default \u2014 opt in per agent. Cascades from " + "defaults.channels.telegram.linear_agent."),
|
|
13714
13719
|
chat_id: exports_external.string().regex(/^-\d+$/, 'supergroup chat_id must be a negative integer as a string (e.g. "-1001234567890")').optional().describe("Per-agent supergroup ID \u2014 overrides fleet `telegram.forum_chat_id`. " + "When set, requires `default_topic_id`. Negative integer as string. " + "Forbidden when `dm_only: true`. See docs/rfcs/supergroup-mode.md."),
|
|
13715
13720
|
default_topic_id: exports_external.number().int().positive().optional().describe("Forum topic ID this agent's automated outbounds default to when " + "no more-specific alias resolves. Defaults to General (topic 1) when " + "`chat_id` is set and this is omitted \u2014 set it only to pin a different " + "fallback topic. " + "Telegram's General topic is `id=1` at MTProto but sends omit the " + "field \u2014 the outbound wrapper strips `message_thread_id === 1` " + "on send. Forbidden when `dm_only: true`."),
|
|
13716
13721
|
topic_aliases: exports_external.record(exports_external.string(), exports_external.number().int().positive()).optional().describe("Operator-friendly names for forum topic IDs (e.g. " + "`{ general: 1, planning: 17, cron: 23, admin: 31, alerts: 41 }`). " + "Referenced from per-cron `topic:` fields and the outbound router " + "for autonomous events (boot \u2192 alerts, hostd \u2192 admin, etc.). " + "Cascades per-key through defaults \u2192 profile \u2192 agent.")
|
|
@@ -29166,7 +29171,7 @@ var init_thinking_effort_risk = __esm(() => {
|
|
|
29166
29171
|
// src/manifest.ts
|
|
29167
29172
|
import {
|
|
29168
29173
|
existsSync as existsSync52,
|
|
29169
|
-
readFileSync as
|
|
29174
|
+
readFileSync as readFileSync48,
|
|
29170
29175
|
readdirSync as readdirSync19
|
|
29171
29176
|
} from "node:fs";
|
|
29172
29177
|
import { dirname as dirname12, join as join47 } from "node:path";
|
|
@@ -29188,7 +29193,7 @@ function loadManifest(manifestPath) {
|
|
|
29188
29193
|
}
|
|
29189
29194
|
let raw;
|
|
29190
29195
|
try {
|
|
29191
|
-
raw =
|
|
29196
|
+
raw = readFileSync48(path4, "utf-8");
|
|
29192
29197
|
} catch (err) {
|
|
29193
29198
|
throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
|
|
29194
29199
|
}
|
|
@@ -29253,7 +29258,7 @@ function probePlaywrightMcpVersion() {
|
|
|
29253
29258
|
const pkgPath = join47(npxCache, entry, "node_modules/@playwright/mcp/package.json");
|
|
29254
29259
|
if (existsSync52(pkgPath)) {
|
|
29255
29260
|
try {
|
|
29256
|
-
const pkg = JSON.parse(
|
|
29261
|
+
const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
|
|
29257
29262
|
if (pkg.version)
|
|
29258
29263
|
return pkg.version;
|
|
29259
29264
|
} catch {}
|
|
@@ -29488,7 +29493,7 @@ var init_doctor_memory = __esm(() => {
|
|
|
29488
29493
|
});
|
|
29489
29494
|
|
|
29490
29495
|
// src/cli/doctor-docker.ts
|
|
29491
|
-
import { readFileSync as
|
|
29496
|
+
import { readFileSync as readFileSync49 } from "node:fs";
|
|
29492
29497
|
function imageTagOf(ref) {
|
|
29493
29498
|
if (!ref)
|
|
29494
29499
|
return "<absent>";
|
|
@@ -29503,7 +29508,7 @@ function isDockerMode(opts) {
|
|
|
29503
29508
|
return true;
|
|
29504
29509
|
if (opts?.composePath) {
|
|
29505
29510
|
try {
|
|
29506
|
-
|
|
29511
|
+
readFileSync49(opts.composePath, "utf8");
|
|
29507
29512
|
return true;
|
|
29508
29513
|
} catch {}
|
|
29509
29514
|
}
|
|
@@ -29692,7 +29697,7 @@ var init_doctor_docker = __esm(() => {
|
|
|
29692
29697
|
});
|
|
29693
29698
|
|
|
29694
29699
|
// src/cli/doctor-auth-broker.ts
|
|
29695
|
-
import { existsSync as existsSync53, readFileSync as
|
|
29700
|
+
import { existsSync as existsSync53, readFileSync as readFileSync50 } from "node:fs";
|
|
29696
29701
|
import { createHash as createHash10 } from "node:crypto";
|
|
29697
29702
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
29698
29703
|
import { homedir as homedir26 } from "node:os";
|
|
@@ -29806,7 +29811,7 @@ function checkAuthBrokerDrift(deps = {}) {
|
|
|
29806
29811
|
}
|
|
29807
29812
|
let index;
|
|
29808
29813
|
try {
|
|
29809
|
-
index = JSON.parse(
|
|
29814
|
+
index = JSON.parse(readFileSync50(indexPath, "utf-8"));
|
|
29810
29815
|
} catch (err) {
|
|
29811
29816
|
return {
|
|
29812
29817
|
name: "auth-broker: drift",
|
|
@@ -29826,7 +29831,7 @@ function checkAuthBrokerDrift(deps = {}) {
|
|
|
29826
29831
|
}
|
|
29827
29832
|
let got;
|
|
29828
29833
|
try {
|
|
29829
|
-
got = sha256Hex(
|
|
29834
|
+
got = sha256Hex(readFileSync50(credsPath, "utf-8"));
|
|
29830
29835
|
} catch (err) {
|
|
29831
29836
|
divergent.push(`${label} (read failed: ${err.message})`);
|
|
29832
29837
|
continue;
|
|
@@ -29867,7 +29872,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
|
|
|
29867
29872
|
}
|
|
29868
29873
|
let violations;
|
|
29869
29874
|
try {
|
|
29870
|
-
violations = JSON.parse(
|
|
29875
|
+
violations = JSON.parse(readFileSync50(path4, "utf-8"));
|
|
29871
29876
|
} catch (err) {
|
|
29872
29877
|
return {
|
|
29873
29878
|
name: "auth-broker: threshold violations",
|
|
@@ -31903,7 +31908,7 @@ import {
|
|
|
31903
31908
|
existsSync as existsSync57,
|
|
31904
31909
|
lstatSync as lstatSync6,
|
|
31905
31910
|
mkdirSync as mkdirSync31,
|
|
31906
|
-
readFileSync as
|
|
31911
|
+
readFileSync as readFileSync51,
|
|
31907
31912
|
readdirSync as readdirSync21,
|
|
31908
31913
|
statSync as statSync25
|
|
31909
31914
|
} from "node:fs";
|
|
@@ -32663,7 +32668,7 @@ function classifyReadError(err) {
|
|
|
32663
32668
|
}
|
|
32664
32669
|
function tryReadHostFile(path4) {
|
|
32665
32670
|
try {
|
|
32666
|
-
return { kind: "ok", content:
|
|
32671
|
+
return { kind: "ok", content: readFileSync51(path4, "utf-8") };
|
|
32667
32672
|
} catch (err) {
|
|
32668
32673
|
const kind = classifyReadError(err);
|
|
32669
32674
|
const error = err?.message ?? String(err);
|
|
@@ -32679,7 +32684,7 @@ function parseEnvFile(path4) {
|
|
|
32679
32684
|
return {};
|
|
32680
32685
|
let content;
|
|
32681
32686
|
try {
|
|
32682
|
-
content =
|
|
32687
|
+
content = readFileSync51(path4, "utf-8");
|
|
32683
32688
|
} catch {
|
|
32684
32689
|
return {};
|
|
32685
32690
|
}
|
|
@@ -32796,7 +32801,7 @@ function checkStartShStale(agentName, startShPath) {
|
|
|
32796
32801
|
}
|
|
32797
32802
|
let content;
|
|
32798
32803
|
try {
|
|
32799
|
-
content =
|
|
32804
|
+
content = readFileSync51(startShPath, "utf-8");
|
|
32800
32805
|
} catch (err) {
|
|
32801
32806
|
return {
|
|
32802
32807
|
name: label,
|
|
@@ -32909,7 +32914,7 @@ function isSwitchroomCheckout(dir) {
|
|
|
32909
32914
|
const pkgPath = join59(dir, "package.json");
|
|
32910
32915
|
if (!existsSync57(pkgPath))
|
|
32911
32916
|
return false;
|
|
32912
|
-
const pkg = JSON.parse(
|
|
32917
|
+
const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
|
|
32913
32918
|
return pkg.name === "switchroom";
|
|
32914
32919
|
} catch {
|
|
32915
32920
|
return false;
|
|
@@ -33030,7 +33035,7 @@ function checkAgents(config, configPath) {
|
|
|
33030
33035
|
});
|
|
33031
33036
|
} else {
|
|
33032
33037
|
try {
|
|
33033
|
-
const mcp = JSON.parse(
|
|
33038
|
+
const mcp = JSON.parse(readFileSync51(mcpJsonPath, "utf-8"));
|
|
33034
33039
|
const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
|
|
33035
33040
|
const memoryEnabled = isHindsightEnabled(config);
|
|
33036
33041
|
const hasHindsight = !!mcp.mcpServers?.hindsight;
|
|
@@ -33525,11 +33530,11 @@ function runDockerSection(config) {
|
|
|
33525
33530
|
let composeYaml;
|
|
33526
33531
|
let dockerfileAgent;
|
|
33527
33532
|
try {
|
|
33528
|
-
composeYaml =
|
|
33533
|
+
composeYaml = readFileSync51(composePath, "utf8");
|
|
33529
33534
|
} catch {}
|
|
33530
33535
|
const dockerfilePath = resolve32(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
|
|
33531
33536
|
try {
|
|
33532
|
-
dockerfileAgent =
|
|
33537
|
+
dockerfileAgent = readFileSync51(dockerfilePath, "utf8");
|
|
33533
33538
|
} catch {}
|
|
33534
33539
|
return runDockerChecks({
|
|
33535
33540
|
config,
|
|
@@ -49836,7 +49841,7 @@ __export(exports_server2, {
|
|
|
49836
49841
|
TOOLS: () => TOOLS2
|
|
49837
49842
|
});
|
|
49838
49843
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
49839
|
-
import { existsSync as existsSync83, readFileSync as
|
|
49844
|
+
import { existsSync as existsSync83, readFileSync as readFileSync70 } from "node:fs";
|
|
49840
49845
|
function selfSocketPath() {
|
|
49841
49846
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
49842
49847
|
}
|
|
@@ -50014,7 +50019,7 @@ function getLastUpdateApplyStatus() {
|
|
|
50014
50019
|
}
|
|
50015
50020
|
let raw;
|
|
50016
50021
|
try {
|
|
50017
|
-
raw =
|
|
50022
|
+
raw = readFileSync70(path8, "utf-8");
|
|
50018
50023
|
} catch (err2) {
|
|
50019
50024
|
return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
|
|
50020
50025
|
}
|
|
@@ -50247,8 +50252,8 @@ var {
|
|
|
50247
50252
|
} = import__.default;
|
|
50248
50253
|
|
|
50249
50254
|
// src/build-info.ts
|
|
50250
|
-
var VERSION = "0.15.
|
|
50251
|
-
var COMMIT_SHA = "
|
|
50255
|
+
var VERSION = "0.15.12";
|
|
50256
|
+
var COMMIT_SHA = "18b7b6e6";
|
|
50252
50257
|
|
|
50253
50258
|
// src/cli/agent.ts
|
|
50254
50259
|
init_source();
|
|
@@ -67029,6 +67034,15 @@ import { createInterface as createInterface4 } from "node:readline";
|
|
|
67029
67034
|
|
|
67030
67035
|
// src/cli/telegram-yaml.ts
|
|
67031
67036
|
var import_yaml11 = __toESM(require_dist(), 1);
|
|
67037
|
+
function setLinearAgent(yamlText, agentName, opts) {
|
|
67038
|
+
const doc = import_yaml11.parseDocument(yamlText);
|
|
67039
|
+
ensureAgent(doc, agentName);
|
|
67040
|
+
const block = { enabled: true, token: opts.token };
|
|
67041
|
+
if (opts.workspaceId)
|
|
67042
|
+
block.workspace_id = opts.workspaceId;
|
|
67043
|
+
doc.setIn(["agents", agentName, "channels", "telegram", "linear_agent"], block);
|
|
67044
|
+
return String(doc);
|
|
67045
|
+
}
|
|
67032
67046
|
function setTelegramFeature(yamlText, agentName, feature, value) {
|
|
67033
67047
|
const doc = import_yaml11.parseDocument(yamlText);
|
|
67034
67048
|
ensureAgent(doc, agentName);
|
|
@@ -67587,6 +67601,100 @@ function promptHidden2(prompt) {
|
|
|
67587
67601
|
});
|
|
67588
67602
|
}
|
|
67589
67603
|
|
|
67604
|
+
// src/cli/linear-agent.ts
|
|
67605
|
+
init_source();
|
|
67606
|
+
init_helpers();
|
|
67607
|
+
import { readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
|
|
67608
|
+
function registerLinearAgentCommand(program3) {
|
|
67609
|
+
const linear = program3.command("linear-agent").description("Install an agent into a Linear workspace as a first-class app actor (#2298) \u2014 @-mentionable, delegate-assignable, agent sessions wake it instantly.");
|
|
67610
|
+
linear.command("setup").description("Provision <agent> as a Linear agent. Vault-stores the Linear OAuth app token (actor=app) under 'linear/<agent>/token' and enables the linear_agent block in switchroom.yaml. The OAuth browser authorize step is printed as instructions (it can't run headless); pass the already-obtained --token.").requiredOption("--agent <name>", "Agent name (must exist in switchroom.yaml)").requiredOption("--token <token>", "The Linear OAuth app token (actor=app), obtained out-of-band via the browser authorize step. Stored in the vault, never in switchroom.yaml.").option("--client-id <id>", "Linear OAuth app client id (for the printed authorize-URL hint).").option("--client-secret <secret>", "Linear OAuth app client secret (informational \u2014 not stored by this verb).").option("--redirect-uri <uri>", "OAuth redirect URI registered on the Linear app (for the authorize-URL hint).").option("--workspace-id <id>", "Optional Linear workspace (organization) id to record in config.").option("--webhook-base <url>", "Base URL of the switchroom web server (e.g. https://hooks.switchroom.ai). Used to print the webhook URL to register in Linear. Defaults to a placeholder.").option("--dry-run", "Print the YAML diff + instructions without writing or vaulting anything").action(withConfigError(async (opts) => {
|
|
67611
|
+
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(opts.agent)) {
|
|
67612
|
+
fail2(`--agent must be a lowercase agent slug (got '${opts.agent}').`);
|
|
67613
|
+
}
|
|
67614
|
+
if (!opts.token || opts.token.trim().length === 0) {
|
|
67615
|
+
fail2("--token must be a non-empty Linear app token.");
|
|
67616
|
+
}
|
|
67617
|
+
const vaultKey = `linear/${opts.agent}/token`;
|
|
67618
|
+
if (!opts.dryRun) {
|
|
67619
|
+
await vaultPut(program3, vaultKey, opts.token);
|
|
67620
|
+
} else {
|
|
67621
|
+
console.log(source_default.gray(`[dry-run] would store the Linear token in the vault as '${vaultKey}'`));
|
|
67622
|
+
}
|
|
67623
|
+
const path4 = getConfigPath(program3);
|
|
67624
|
+
const before = readFileSync39(path4, "utf-8");
|
|
67625
|
+
let after;
|
|
67626
|
+
try {
|
|
67627
|
+
after = setLinearAgent(before, opts.agent, {
|
|
67628
|
+
token: `vault:${vaultKey}`,
|
|
67629
|
+
...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
|
|
67630
|
+
});
|
|
67631
|
+
} catch (err) {
|
|
67632
|
+
fail2(err.message);
|
|
67633
|
+
}
|
|
67634
|
+
if (opts.dryRun) {
|
|
67635
|
+
console.log(source_default.bold(`[dry-run] would edit ${path4}`));
|
|
67636
|
+
console.log(makeUnifiedDiff2(before, after));
|
|
67637
|
+
} else {
|
|
67638
|
+
writeFileSync22(path4, after, "utf-8");
|
|
67639
|
+
console.log(source_default.green(`\u2713 Enabled linear-agent for agent '${opts.agent}'`));
|
|
67640
|
+
console.log(source_default.gray(` Vault key: ${vaultKey}`));
|
|
67641
|
+
console.log(source_default.gray(` Run 'switchroom agent restart ${opts.agent}' to pick up the change.`));
|
|
67642
|
+
}
|
|
67643
|
+
printLinearInstructions(opts, vaultKey);
|
|
67644
|
+
}));
|
|
67645
|
+
}
|
|
67646
|
+
function printLinearInstructions(opts, vaultKey) {
|
|
67647
|
+
const base = opts.webhookBase ?? "https://<your-switchroom-web-host>";
|
|
67648
|
+
const webhookUrl = `${base.replace(/\/$/, "")}/webhook/${opts.agent}/linear`;
|
|
67649
|
+
console.log("");
|
|
67650
|
+
console.log(source_default.bold("Next steps in Linear (browser, one-time per agent):"));
|
|
67651
|
+
console.log(source_default.gray(" 1. Create / open your Linear OAuth app with actor=app and scopes"));
|
|
67652
|
+
console.log(source_default.gray(" app:mentionable + app:assignable (https://linear.app/developers/agents)."));
|
|
67653
|
+
if (opts.clientId) {
|
|
67654
|
+
const redirect = opts.redirectUri ?? `${base.replace(/\/$/, "")}/oauth/callback`;
|
|
67655
|
+
const authorizeUrl = `https://linear.app/oauth/authorize?` + `client_id=${encodeURIComponent(opts.clientId)}` + `&redirect_uri=${encodeURIComponent(redirect)}` + `&response_type=code` + `&scope=${encodeURIComponent("read,write,app:assignable,app:mentionable")}` + `&actor=app`;
|
|
67656
|
+
console.log(source_default.gray(" 2. Authorize the app as an actor (open in a browser):"));
|
|
67657
|
+
console.log(source_default.cyan(` ${authorizeUrl}`));
|
|
67658
|
+
console.log(source_default.gray(" The redirect delivers the app token you pass to this verb via --token."));
|
|
67659
|
+
} else {
|
|
67660
|
+
console.log(source_default.gray(" 2. Authorize the app (actor=app) in a browser; capture the app token and"));
|
|
67661
|
+
console.log(source_default.gray(" re-run this verb with --token (and optionally --client-id to print the"));
|
|
67662
|
+
console.log(source_default.gray(" authorize URL)."));
|
|
67663
|
+
}
|
|
67664
|
+
console.log(source_default.gray(" 3. Register this webhook URL on the app (AgentSessionEvent + Issue/Comment):"));
|
|
67665
|
+
console.log(source_default.cyan(` ${webhookUrl}`));
|
|
67666
|
+
console.log(source_default.gray(" Use Linear's signing secret as the webhook secret \u2014 store it in the vault " + `under webhook/${opts.agent}/linear (e.g. 'switchroom telegram enable webhook ` + `--agent ${opts.agent} --source linear --secret <signing-secret>').`));
|
|
67667
|
+
console.log("");
|
|
67668
|
+
console.log(source_default.gray(` Token is read at runtime from vault:${vaultKey} (actor=app).`));
|
|
67669
|
+
}
|
|
67670
|
+
function makeUnifiedDiff2(before, after) {
|
|
67671
|
+
const a = before.split(`
|
|
67672
|
+
`);
|
|
67673
|
+
const b = after.split(`
|
|
67674
|
+
`);
|
|
67675
|
+
const out = [];
|
|
67676
|
+
let i = 0, j = 0;
|
|
67677
|
+
while (i < a.length || j < b.length) {
|
|
67678
|
+
if (i < a.length && j < b.length && a[i] === b[j]) {
|
|
67679
|
+
out.push(` ${a[i]}`);
|
|
67680
|
+
i++;
|
|
67681
|
+
j++;
|
|
67682
|
+
} else if (j < b.length && (i >= a.length || a[i] !== b[j])) {
|
|
67683
|
+
out.push(source_default.green(`+ ${b[j]}`));
|
|
67684
|
+
j++;
|
|
67685
|
+
} else {
|
|
67686
|
+
out.push(source_default.red(`- ${a[i]}`));
|
|
67687
|
+
i++;
|
|
67688
|
+
}
|
|
67689
|
+
}
|
|
67690
|
+
return out.join(`
|
|
67691
|
+
`);
|
|
67692
|
+
}
|
|
67693
|
+
function fail2(msg) {
|
|
67694
|
+
console.error(source_default.red(`Error: ${msg}`));
|
|
67695
|
+
process.exit(1);
|
|
67696
|
+
}
|
|
67697
|
+
|
|
67590
67698
|
// src/cli/memory.ts
|
|
67591
67699
|
init_source();
|
|
67592
67700
|
init_hindsight();
|
|
@@ -67856,7 +67964,7 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
|
|
|
67856
67964
|
// src/cli/memory.ts
|
|
67857
67965
|
init_loader();
|
|
67858
67966
|
var import_yaml12 = __toESM(require_dist(), 1);
|
|
67859
|
-
import { existsSync as existsSync44, readFileSync as
|
|
67967
|
+
import { existsSync as existsSync44, readFileSync as readFileSync40, writeFileSync as writeFileSync23 } from "node:fs";
|
|
67860
67968
|
import { join as join39 } from "node:path";
|
|
67861
67969
|
function readRecallLog(agentDir, limit) {
|
|
67862
67970
|
const path4 = join39(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
@@ -67864,7 +67972,7 @@ function readRecallLog(agentDir, limit) {
|
|
|
67864
67972
|
return [];
|
|
67865
67973
|
let raw;
|
|
67866
67974
|
try {
|
|
67867
|
-
raw =
|
|
67975
|
+
raw = readFileSync40(path4, "utf-8");
|
|
67868
67976
|
} catch {
|
|
67869
67977
|
return [];
|
|
67870
67978
|
}
|
|
@@ -68059,7 +68167,7 @@ Cross-agent reflection plan
|
|
|
68059
68167
|
const configPath = getConfigPath(program3);
|
|
68060
68168
|
try {
|
|
68061
68169
|
if (existsSync44(configPath)) {
|
|
68062
|
-
const raw =
|
|
68170
|
+
const raw = readFileSync40(configPath, "utf-8");
|
|
68063
68171
|
const doc = import_yaml12.default.parseDocument(raw);
|
|
68064
68172
|
if (!doc.has("memory")) {
|
|
68065
68173
|
doc.set("memory", { backend: "hindsight", shared_collection: "shared", config: { provider, url } });
|
|
@@ -68075,7 +68183,7 @@ Cross-agent reflection plan
|
|
|
68075
68183
|
}
|
|
68076
68184
|
}
|
|
68077
68185
|
}
|
|
68078
|
-
|
|
68186
|
+
writeFileSync23(configPath, doc.toString(), "utf-8");
|
|
68079
68187
|
console.log(source_default.gray(` Updated ${configPath} with memory.config.url = ${url}`));
|
|
68080
68188
|
console.log(source_default.gray(" Run `switchroom agent reconcile all --restart` to apply this to existing agents."));
|
|
68081
68189
|
}
|
|
@@ -68168,7 +68276,7 @@ init_loader();
|
|
|
68168
68276
|
init_client();
|
|
68169
68277
|
init_lifecycle();
|
|
68170
68278
|
import {
|
|
68171
|
-
readFileSync as
|
|
68279
|
+
readFileSync as readFileSync46,
|
|
68172
68280
|
existsSync as existsSync50,
|
|
68173
68281
|
realpathSync as realpathSync4,
|
|
68174
68282
|
mkdirSync as mkdirSync29,
|
|
@@ -68187,7 +68295,7 @@ init_lifecycle();
|
|
|
68187
68295
|
init_manager();
|
|
68188
68296
|
init_hindsight();
|
|
68189
68297
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
68190
|
-
import { existsSync as existsSync47, readFileSync as
|
|
68298
|
+
import { existsSync as existsSync47, readFileSync as readFileSync43, statSync as statSync22 } from "node:fs";
|
|
68191
68299
|
import { resolve as resolve27 } from "node:path";
|
|
68192
68300
|
init_audit_reader();
|
|
68193
68301
|
|
|
@@ -72695,8 +72803,8 @@ init_paths();
|
|
|
72695
72803
|
import {
|
|
72696
72804
|
existsSync as existsSync45,
|
|
72697
72805
|
mkdirSync as mkdirSync25,
|
|
72698
|
-
readFileSync as
|
|
72699
|
-
writeFileSync as
|
|
72806
|
+
readFileSync as readFileSync41,
|
|
72807
|
+
writeFileSync as writeFileSync24
|
|
72700
72808
|
} from "node:fs";
|
|
72701
72809
|
import { dirname as dirname9 } from "node:path";
|
|
72702
72810
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
@@ -72716,7 +72824,7 @@ function getDistinctId() {
|
|
|
72716
72824
|
const path4 = resolveStatePath("analytics-id");
|
|
72717
72825
|
try {
|
|
72718
72826
|
if (existsSync45(path4)) {
|
|
72719
|
-
const existing =
|
|
72827
|
+
const existing = readFileSync41(path4, "utf-8").trim();
|
|
72720
72828
|
if (existing) {
|
|
72721
72829
|
cachedDistinctId = existing;
|
|
72722
72830
|
return existing;
|
|
@@ -72727,7 +72835,7 @@ function getDistinctId() {
|
|
|
72727
72835
|
cachedDistinctId = id;
|
|
72728
72836
|
try {
|
|
72729
72837
|
mkdirSync25(dirname9(path4), { recursive: true });
|
|
72730
|
-
|
|
72838
|
+
writeFileSync24(path4, id, "utf-8");
|
|
72731
72839
|
} catch {}
|
|
72732
72840
|
return id;
|
|
72733
72841
|
}
|
|
@@ -72852,7 +72960,7 @@ function getAgentWorkspaceAccount(yamlText, provider, agent) {
|
|
|
72852
72960
|
// src/web/config-edit-plan.ts
|
|
72853
72961
|
init_schema();
|
|
72854
72962
|
var import_yaml14 = __toESM(require_dist(), 1);
|
|
72855
|
-
import { readFileSync as
|
|
72963
|
+
import { readFileSync as readFileSync42 } from "node:fs";
|
|
72856
72964
|
|
|
72857
72965
|
class ConfigPlanError extends Error {
|
|
72858
72966
|
}
|
|
@@ -72862,7 +72970,7 @@ function composeTransforms(...transforms) {
|
|
|
72862
72970
|
function planConfigEdit(configPath, transform) {
|
|
72863
72971
|
let before;
|
|
72864
72972
|
try {
|
|
72865
|
-
before =
|
|
72973
|
+
before = readFileSync42(configPath, "utf-8");
|
|
72866
72974
|
} catch (err) {
|
|
72867
72975
|
throw new ConfigPlanError(`could not read config: ${err.message}`);
|
|
72868
72976
|
}
|
|
@@ -72893,7 +73001,7 @@ import {
|
|
|
72893
73001
|
mkdirSync as mkdirSync26,
|
|
72894
73002
|
mkdtempSync as mkdtemp,
|
|
72895
73003
|
rmSync as rmrf,
|
|
72896
|
-
writeFileSync as
|
|
73004
|
+
writeFileSync as writeFileSync25
|
|
72897
73005
|
} from "node:fs";
|
|
72898
73006
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
72899
73007
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
@@ -72908,8 +73016,8 @@ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "
|
|
|
72908
73016
|
try {
|
|
72909
73017
|
mkdirSync26(join41(dir, "cur"), { recursive: true });
|
|
72910
73018
|
mkdirSync26(join41(dir, "new"), { recursive: true });
|
|
72911
|
-
|
|
72912
|
-
|
|
73019
|
+
writeFileSync25(join41(dir, "cur", name), before);
|
|
73020
|
+
writeFileSync25(join41(dir, "new", name), after);
|
|
72913
73021
|
const r = spawnSync4(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
|
|
72914
73022
|
if (r.status === 0)
|
|
72915
73023
|
return "";
|
|
@@ -73546,7 +73654,7 @@ async function handleGetSystemHealth(config, home2) {
|
|
|
73546
73654
|
const logPath = defaultAuditLogPath2(home2);
|
|
73547
73655
|
if (existsSync47(logPath)) {
|
|
73548
73656
|
hostd.auditLogPresent = true;
|
|
73549
|
-
const raw =
|
|
73657
|
+
const raw = readFileSync43(logPath, "utf-8");
|
|
73550
73658
|
hostd.recent = readAndFilter(raw, {}, 10);
|
|
73551
73659
|
}
|
|
73552
73660
|
} catch (err) {
|
|
@@ -73906,7 +74014,7 @@ async function handleGetMemoryHealth(config, opts) {
|
|
|
73906
74014
|
}
|
|
73907
74015
|
|
|
73908
74016
|
// src/web/webhook-handler.ts
|
|
73909
|
-
import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as
|
|
74017
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
|
|
73910
74018
|
import { join as join45 } from "path";
|
|
73911
74019
|
import { homedir as homedir24 } from "os";
|
|
73912
74020
|
|
|
@@ -74113,7 +74221,7 @@ function forwardToGateway(socketPath, req, opts = {}) {
|
|
|
74113
74221
|
}
|
|
74114
74222
|
|
|
74115
74223
|
// src/web/webhook-edge.ts
|
|
74116
|
-
import { existsSync as existsSync48, readFileSync as
|
|
74224
|
+
import { existsSync as existsSync48, readFileSync as readFileSync44 } from "fs";
|
|
74117
74225
|
import { join as join44 } from "path";
|
|
74118
74226
|
import { homedir as homedir23 } from "os";
|
|
74119
74227
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
@@ -74126,7 +74234,7 @@ function loadEdgeSecret(path4) {
|
|
|
74126
74234
|
try {
|
|
74127
74235
|
if (!existsSync48(p))
|
|
74128
74236
|
return null;
|
|
74129
|
-
const raw =
|
|
74237
|
+
const raw = readFileSync44(p, "utf-8").trim();
|
|
74130
74238
|
return raw.length > 0 ? raw : null;
|
|
74131
74239
|
} catch {
|
|
74132
74240
|
return null;
|
|
@@ -74160,7 +74268,7 @@ function loadDedupFile(path4) {
|
|
|
74160
74268
|
try {
|
|
74161
74269
|
if (!existsSync49(path4))
|
|
74162
74270
|
return {};
|
|
74163
|
-
const raw = JSON.parse(
|
|
74271
|
+
const raw = JSON.parse(readFileSync45(path4, "utf-8"));
|
|
74164
74272
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
74165
74273
|
} catch {
|
|
74166
74274
|
return {};
|
|
@@ -74174,7 +74282,7 @@ function saveDedupFile(path4, deliveries, now) {
|
|
|
74174
74282
|
}
|
|
74175
74283
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
74176
74284
|
const final = Object.fromEntries(sorted);
|
|
74177
|
-
|
|
74285
|
+
writeFileSync26(path4, JSON.stringify({ deliveries: final }), {
|
|
74178
74286
|
mode: 384
|
|
74179
74287
|
});
|
|
74180
74288
|
}
|
|
@@ -74443,7 +74551,7 @@ function resolveWebToken() {
|
|
|
74443
74551
|
const home2 = process.env.HOME ?? homedir25();
|
|
74444
74552
|
const tokenPath = join46(home2, ".switchroom", "web-token");
|
|
74445
74553
|
if (existsSync50(tokenPath)) {
|
|
74446
|
-
const existing =
|
|
74554
|
+
const existing = readFileSync46(tokenPath, "utf8").trim();
|
|
74447
74555
|
if (existing.length > 0)
|
|
74448
74556
|
return existing;
|
|
74449
74557
|
}
|
|
@@ -74460,7 +74568,7 @@ function resolveWebToken() {
|
|
|
74460
74568
|
return token;
|
|
74461
74569
|
} catch (err) {
|
|
74462
74570
|
if (err.code === "EEXIST") {
|
|
74463
|
-
const existing =
|
|
74571
|
+
const existing = readFileSync46(tokenPath, "utf8").trim();
|
|
74464
74572
|
if (existing.length > 0)
|
|
74465
74573
|
return existing;
|
|
74466
74574
|
}
|
|
@@ -74530,7 +74638,7 @@ function loadWebhookSecrets() {
|
|
|
74530
74638
|
if (!existsSync50(path4))
|
|
74531
74639
|
return {};
|
|
74532
74640
|
try {
|
|
74533
|
-
const parsed = JSON.parse(
|
|
74641
|
+
const parsed = JSON.parse(readFileSync46(path4, "utf-8"));
|
|
74534
74642
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
74535
74643
|
} catch (err) {
|
|
74536
74644
|
process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
|
|
@@ -74903,7 +75011,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
74903
75011
|
}
|
|
74904
75012
|
const ext = extname(realFullPath);
|
|
74905
75013
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
74906
|
-
const content =
|
|
75014
|
+
const content = readFileSync46(realFullPath);
|
|
74907
75015
|
return new Response(content, {
|
|
74908
75016
|
headers: { "Content-Type": contentType }
|
|
74909
75017
|
});
|
|
@@ -75037,7 +75145,7 @@ Starting Switchroom dashboard...
|
|
|
75037
75145
|
// src/cli/setup.ts
|
|
75038
75146
|
init_source();
|
|
75039
75147
|
init_loader();
|
|
75040
|
-
import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as
|
|
75148
|
+
import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as readFileSync47, writeFileSync as writeFileSync27, mkdirSync as mkdirSync30 } from "node:fs";
|
|
75041
75149
|
import { resolve as resolve29, dirname as dirname11 } from "node:path";
|
|
75042
75150
|
init_vault();
|
|
75043
75151
|
init_manager();
|
|
@@ -75766,10 +75874,10 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
|
|
|
75766
75874
|
try {
|
|
75767
75875
|
const yamlPath = existsSync51(resolve29(process.cwd(), "switchroom.yaml")) ? resolve29(process.cwd(), "switchroom.yaml") : resolve29(process.cwd(), "switchroom.yml");
|
|
75768
75876
|
if (existsSync51(yamlPath)) {
|
|
75769
|
-
const content =
|
|
75877
|
+
const content = readFileSync47(yamlPath, "utf-8");
|
|
75770
75878
|
const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
|
|
75771
75879
|
if (result.kind === "rewritten") {
|
|
75772
|
-
|
|
75880
|
+
writeFileSync27(yamlPath, result.content, "utf-8");
|
|
75773
75881
|
console.log(source_default.green(` ${STEP_DONE} Set vault.broker.approvalAuth: telegram-id in ${yamlPath}`));
|
|
75774
75882
|
} else if (result.kind === "already-set") {
|
|
75775
75883
|
console.log(source_default.gray(" approvalAuth already set \u2014 leaving it alone."));
|
|
@@ -75801,7 +75909,7 @@ async function stepDangerousMode(config, nonInteractive) {
|
|
|
75801
75909
|
];
|
|
75802
75910
|
for (const configPath of configPaths) {
|
|
75803
75911
|
if (existsSync51(configPath)) {
|
|
75804
|
-
let content =
|
|
75912
|
+
let content = readFileSync47(configPath, "utf-8");
|
|
75805
75913
|
const agentNames = Object.keys(config.agents);
|
|
75806
75914
|
for (const name of agentNames) {
|
|
75807
75915
|
const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
|
|
@@ -75815,7 +75923,7 @@ async function stepDangerousMode(config, nonInteractive) {
|
|
|
75815
75923
|
}
|
|
75816
75924
|
config.agents[name].dangerous_mode = true;
|
|
75817
75925
|
}
|
|
75818
|
-
|
|
75926
|
+
writeFileSync27(configPath, content, "utf-8");
|
|
75819
75927
|
console.log(source_default.green(` ${STEP_DONE} Enabled dangerous_mode for all agents in ${configPath}`));
|
|
75820
75928
|
break;
|
|
75821
75929
|
}
|
|
@@ -75930,7 +76038,7 @@ init_doctor();
|
|
|
75930
76038
|
init_source();
|
|
75931
76039
|
init_loader();
|
|
75932
76040
|
init_lifecycle();
|
|
75933
|
-
import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as
|
|
76041
|
+
import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync52, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
|
|
75934
76042
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
75935
76043
|
import { join as join60, dirname as dirname14, resolve as resolve33 } from "node:path";
|
|
75936
76044
|
import { homedir as homedir36 } from "node:os";
|
|
@@ -75940,7 +76048,7 @@ function runningFromSwitchroomCheckout(scriptPath) {
|
|
|
75940
76048
|
for (let i = 0;i < 12; i++) {
|
|
75941
76049
|
if (existsSync58(join60(dir, ".git"))) {
|
|
75942
76050
|
try {
|
|
75943
|
-
const pkg = JSON.parse(
|
|
76051
|
+
const pkg = JSON.parse(readFileSync52(join60(dir, "package.json"), "utf-8"));
|
|
75944
76052
|
if (pkg.name === "switchroom")
|
|
75945
76053
|
return true;
|
|
75946
76054
|
} catch {}
|
|
@@ -76208,7 +76316,7 @@ function defaultStatusProbe(composePath) {
|
|
|
76208
76316
|
const pkgPath = join60(dir, "package.json");
|
|
76209
76317
|
if (existsSync58(pkgPath)) {
|
|
76210
76318
|
try {
|
|
76211
|
-
const pkg = JSON.parse(
|
|
76319
|
+
const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
|
|
76212
76320
|
if (typeof pkg.version === "string")
|
|
76213
76321
|
cliVersion = pkg.version;
|
|
76214
76322
|
} catch (err) {
|
|
@@ -76424,7 +76532,7 @@ init_source();
|
|
|
76424
76532
|
init_helpers();
|
|
76425
76533
|
init_lifecycle();
|
|
76426
76534
|
import { execSync as execSync4 } from "node:child_process";
|
|
76427
|
-
import { existsSync as existsSync59, readFileSync as
|
|
76535
|
+
import { existsSync as existsSync59, readFileSync as readFileSync53 } from "node:fs";
|
|
76428
76536
|
import { dirname as dirname15, join as join61 } from "node:path";
|
|
76429
76537
|
function getClaudeCodeVersion() {
|
|
76430
76538
|
try {
|
|
@@ -76478,7 +76586,7 @@ function locateSwitchroomInstallDir() {
|
|
|
76478
76586
|
const pkgPath = join61(dir, "package.json");
|
|
76479
76587
|
if (existsSync59(pkgPath)) {
|
|
76480
76588
|
try {
|
|
76481
|
-
const pkg = JSON.parse(
|
|
76589
|
+
const pkg = JSON.parse(readFileSync53(pkgPath, "utf-8"));
|
|
76482
76590
|
if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
|
|
76483
76591
|
return dir;
|
|
76484
76592
|
}
|
|
@@ -76709,11 +76817,11 @@ import {
|
|
|
76709
76817
|
mkdirSync as mkdirSync33,
|
|
76710
76818
|
openSync as openSync11,
|
|
76711
76819
|
readdirSync as readdirSync22,
|
|
76712
|
-
readFileSync as
|
|
76820
|
+
readFileSync as readFileSync54,
|
|
76713
76821
|
renameSync as renameSync12,
|
|
76714
76822
|
statSync as statSync27,
|
|
76715
76823
|
unlinkSync as unlinkSync11,
|
|
76716
|
-
writeFileSync as
|
|
76824
|
+
writeFileSync as writeFileSync28,
|
|
76717
76825
|
writeSync as writeSync7
|
|
76718
76826
|
} from "node:fs";
|
|
76719
76827
|
import { join as join62 } from "node:path";
|
|
@@ -77119,7 +77227,7 @@ function readAll(stateDir) {
|
|
|
77119
77227
|
return [];
|
|
77120
77228
|
let raw;
|
|
77121
77229
|
try {
|
|
77122
|
-
raw =
|
|
77230
|
+
raw = readFileSync54(path4, "utf-8");
|
|
77123
77231
|
} catch {
|
|
77124
77232
|
return [];
|
|
77125
77233
|
}
|
|
@@ -77267,7 +77375,7 @@ function writeAll(stateDir, events) {
|
|
|
77267
77375
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
77268
77376
|
`) + `
|
|
77269
77377
|
`;
|
|
77270
|
-
|
|
77378
|
+
writeFileSync28(tmp, body, "utf-8");
|
|
77271
77379
|
renameSync12(tmp, path4);
|
|
77272
77380
|
}
|
|
77273
77381
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -77330,7 +77438,7 @@ function withLock(stateDir, fn) {
|
|
|
77330
77438
|
function tryStealStaleLock(lockPath) {
|
|
77331
77439
|
let pidStr;
|
|
77332
77440
|
try {
|
|
77333
|
-
pidStr =
|
|
77441
|
+
pidStr = readFileSync54(lockPath, "utf-8").trim();
|
|
77334
77442
|
} catch {
|
|
77335
77443
|
return true;
|
|
77336
77444
|
}
|
|
@@ -77587,9 +77695,9 @@ import { createHash as createHash11 } from "node:crypto";
|
|
|
77587
77695
|
import {
|
|
77588
77696
|
existsSync as existsSync61,
|
|
77589
77697
|
mkdirSync as mkdirSync34,
|
|
77590
|
-
readFileSync as
|
|
77698
|
+
readFileSync as readFileSync55,
|
|
77591
77699
|
rmSync as rmSync13,
|
|
77592
|
-
writeFileSync as
|
|
77700
|
+
writeFileSync as writeFileSync29
|
|
77593
77701
|
} from "node:fs";
|
|
77594
77702
|
import { dirname as dirname16, join as join63 } from "node:path";
|
|
77595
77703
|
import { homedir as homedir37 } from "node:os";
|
|
@@ -77607,7 +77715,7 @@ function defaultPythonCacheRoot() {
|
|
|
77607
77715
|
return join63(homedir37(), ".switchroom", "deps", "python");
|
|
77608
77716
|
}
|
|
77609
77717
|
function hashFile(path4) {
|
|
77610
|
-
return createHash11("sha256").update(
|
|
77718
|
+
return createHash11("sha256").update(readFileSync55(path4)).digest("hex");
|
|
77611
77719
|
}
|
|
77612
77720
|
function ensurePythonEnv(opts) {
|
|
77613
77721
|
const { skillName, requirementsPath, force = false } = opts;
|
|
@@ -77623,7 +77731,7 @@ function ensurePythonEnv(opts) {
|
|
|
77623
77731
|
const pipBin = join63(binDir, "pip");
|
|
77624
77732
|
const targetHash = hashFile(requirementsPath);
|
|
77625
77733
|
if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
|
|
77626
|
-
const existingHash =
|
|
77734
|
+
const existingHash = readFileSync55(stampPath, "utf8").trim();
|
|
77627
77735
|
if (existingHash === targetHash) {
|
|
77628
77736
|
return {
|
|
77629
77737
|
skillName,
|
|
@@ -77657,7 +77765,7 @@ function ensurePythonEnv(opts) {
|
|
|
77657
77765
|
const e = err;
|
|
77658
77766
|
throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
|
|
77659
77767
|
}
|
|
77660
|
-
|
|
77768
|
+
writeFileSync29(stampPath, targetHash + `
|
|
77661
77769
|
`);
|
|
77662
77770
|
return {
|
|
77663
77771
|
skillName,
|
|
@@ -77675,9 +77783,9 @@ import {
|
|
|
77675
77783
|
copyFileSync as copyFileSync9,
|
|
77676
77784
|
existsSync as existsSync62,
|
|
77677
77785
|
mkdirSync as mkdirSync35,
|
|
77678
|
-
readFileSync as
|
|
77786
|
+
readFileSync as readFileSync56,
|
|
77679
77787
|
rmSync as rmSync14,
|
|
77680
|
-
writeFileSync as
|
|
77788
|
+
writeFileSync as writeFileSync30
|
|
77681
77789
|
} from "node:fs";
|
|
77682
77790
|
import { dirname as dirname17, join as join64 } from "node:path";
|
|
77683
77791
|
import { homedir as homedir38 } from "node:os";
|
|
@@ -77710,7 +77818,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
77710
77818
|
const hasher = createHash12("sha256");
|
|
77711
77819
|
hasher.update(`package.json
|
|
77712
77820
|
`);
|
|
77713
|
-
hasher.update(
|
|
77821
|
+
hasher.update(readFileSync56(packageJsonPath));
|
|
77714
77822
|
for (const lockName of ALL_LOCKFILES) {
|
|
77715
77823
|
const lockPath = join64(sourceDir, lockName);
|
|
77716
77824
|
if (existsSync62(lockPath)) {
|
|
@@ -77719,7 +77827,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
77719
77827
|
hasher.update(lockName);
|
|
77720
77828
|
hasher.update(`
|
|
77721
77829
|
`);
|
|
77722
|
-
hasher.update(
|
|
77830
|
+
hasher.update(readFileSync56(lockPath));
|
|
77723
77831
|
}
|
|
77724
77832
|
}
|
|
77725
77833
|
return hasher.digest("hex");
|
|
@@ -77738,7 +77846,7 @@ function ensureNodeEnv(opts) {
|
|
|
77738
77846
|
const binDir = join64(nodeModulesDir, ".bin");
|
|
77739
77847
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
77740
77848
|
if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
|
|
77741
|
-
const existingHash =
|
|
77849
|
+
const existingHash = readFileSync56(stampPath, "utf8").trim();
|
|
77742
77850
|
if (existingHash === targetHash) {
|
|
77743
77851
|
return {
|
|
77744
77852
|
skillName,
|
|
@@ -77774,7 +77882,7 @@ function ensureNodeEnv(opts) {
|
|
|
77774
77882
|
const e = err;
|
|
77775
77883
|
throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
|
|
77776
77884
|
}
|
|
77777
|
-
|
|
77885
|
+
writeFileSync30(stampPath, targetHash + `
|
|
77778
77886
|
`);
|
|
77779
77887
|
return {
|
|
77780
77888
|
skillName,
|
|
@@ -78757,7 +78865,7 @@ function safeParseInt(value, fallback) {
|
|
|
78757
78865
|
init_helpers();
|
|
78758
78866
|
init_loader();
|
|
78759
78867
|
init_merge();
|
|
78760
|
-
import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as
|
|
78868
|
+
import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync57, writeFileSync as writeFileSync31 } from "node:fs";
|
|
78761
78869
|
import { join as join66, resolve as resolve39 } from "node:path";
|
|
78762
78870
|
init_schema();
|
|
78763
78871
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
@@ -78803,7 +78911,7 @@ function registerSoulCommand(program3) {
|
|
|
78803
78911
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
78804
78912
|
process.exit(1);
|
|
78805
78913
|
}
|
|
78806
|
-
process.stdout.write(
|
|
78914
|
+
process.stdout.write(readFileSync57(t.soulPath, "utf-8"));
|
|
78807
78915
|
}));
|
|
78808
78916
|
cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
|
|
78809
78917
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -78834,7 +78942,7 @@ function registerSoulCommand(program3) {
|
|
|
78834
78942
|
}
|
|
78835
78943
|
copyFileSync10(t.soulPath, backupPath);
|
|
78836
78944
|
}
|
|
78837
|
-
|
|
78945
|
+
writeFileSync31(t.soulPath, content, "utf-8");
|
|
78838
78946
|
if (backupPath) {
|
|
78839
78947
|
console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
|
|
78840
78948
|
` + ` Previous version saved to ${backupPath}`);
|
|
@@ -78848,7 +78956,7 @@ function registerSoulCommand(program3) {
|
|
|
78848
78956
|
// src/cli/debug.ts
|
|
78849
78957
|
init_helpers();
|
|
78850
78958
|
init_loader();
|
|
78851
|
-
import { existsSync as existsSync66, readFileSync as
|
|
78959
|
+
import { existsSync as existsSync66, readFileSync as readFileSync58, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
|
|
78852
78960
|
import { resolve as resolve40, join as join67 } from "node:path";
|
|
78853
78961
|
import { createHash as createHash13 } from "node:crypto";
|
|
78854
78962
|
init_merge();
|
|
@@ -78864,7 +78972,7 @@ function readMcpServerNames(agentDir) {
|
|
|
78864
78972
|
if (!existsSync66(mcpPath))
|
|
78865
78973
|
return [];
|
|
78866
78974
|
try {
|
|
78867
|
-
const parsed = JSON.parse(
|
|
78975
|
+
const parsed = JSON.parse(readFileSync58(mcpPath, "utf-8"));
|
|
78868
78976
|
return Object.keys(parsed.mcpServers ?? {});
|
|
78869
78977
|
} catch {
|
|
78870
78978
|
return null;
|
|
@@ -78899,7 +79007,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
78899
79007
|
}
|
|
78900
79008
|
function extractLatestUserMessage(transcriptPath) {
|
|
78901
79009
|
try {
|
|
78902
|
-
const content =
|
|
79010
|
+
const content = readFileSync58(transcriptPath, "utf-8");
|
|
78903
79011
|
const lines = content.trim().split(`
|
|
78904
79012
|
`).filter(Boolean);
|
|
78905
79013
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -79003,7 +79111,7 @@ function registerDebugCommand(program3) {
|
|
|
79003
79111
|
}
|
|
79004
79112
|
console.log(`=== Append System Prompt (per-session) ===
|
|
79005
79113
|
`);
|
|
79006
|
-
const handoffContent = existsSync66(handoffPath) ?
|
|
79114
|
+
const handoffContent = existsSync66(handoffPath) ? readFileSync58(handoffPath, "utf-8") : "";
|
|
79007
79115
|
if (handoffContent.trim().length > 0) {
|
|
79008
79116
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
79009
79117
|
console.log(handoffContent);
|
|
@@ -79014,7 +79122,7 @@ function registerDebugCommand(program3) {
|
|
|
79014
79122
|
}
|
|
79015
79123
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
79016
79124
|
`);
|
|
79017
|
-
const claudeMdContent = existsSync66(claudeMdPath) ?
|
|
79125
|
+
const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync58(claudeMdPath, "utf-8") : "";
|
|
79018
79126
|
if (claudeMdContent.trim().length > 0) {
|
|
79019
79127
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
79020
79128
|
console.log(claudeMdContent);
|
|
@@ -79025,7 +79133,7 @@ function registerDebugCommand(program3) {
|
|
|
79025
79133
|
}
|
|
79026
79134
|
console.log(`=== Persona (SOUL.md) ===
|
|
79027
79135
|
`);
|
|
79028
|
-
const soulMdContent = existsSync66(soulMdPath) ?
|
|
79136
|
+
const soulMdContent = existsSync66(soulMdPath) ? readFileSync58(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync58(workspaceSoulMdPath, "utf-8") : "";
|
|
79029
79137
|
if (soulMdContent.trim().length > 0) {
|
|
79030
79138
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
79031
79139
|
console.log(soulMdContent);
|
|
@@ -79089,8 +79197,8 @@ function registerDebugCommand(program3) {
|
|
|
79089
79197
|
const fleetDir = join67(agentsDir, "..", "fleet");
|
|
79090
79198
|
const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
|
|
79091
79199
|
const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
|
|
79092
|
-
const fleetInvBytes = existsSync66(fleetInvPath) ?
|
|
79093
|
-
const fleetClaudeBytes = existsSync66(fleetClaudePath) ?
|
|
79200
|
+
const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync58(fleetInvPath, "utf-8").length : 0;
|
|
79201
|
+
const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync58(fleetClaudePath, "utf-8").length : 0;
|
|
79094
79202
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
79095
79203
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
79096
79204
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -79131,8 +79239,8 @@ import { randomBytes as randomBytes13 } from "node:crypto";
|
|
|
79131
79239
|
// src/worktree/registry.ts
|
|
79132
79240
|
import {
|
|
79133
79241
|
mkdirSync as mkdirSync36,
|
|
79134
|
-
writeFileSync as
|
|
79135
|
-
readFileSync as
|
|
79242
|
+
writeFileSync as writeFileSync32,
|
|
79243
|
+
readFileSync as readFileSync59,
|
|
79136
79244
|
readdirSync as readdirSync24,
|
|
79137
79245
|
unlinkSync as unlinkSync12,
|
|
79138
79246
|
existsSync as existsSync67,
|
|
@@ -79153,14 +79261,14 @@ function writeRecord(record2) {
|
|
|
79153
79261
|
ensureDir2();
|
|
79154
79262
|
const target = recordPath(record2.id);
|
|
79155
79263
|
const tmp = `${target}.tmp${process.pid}`;
|
|
79156
|
-
|
|
79264
|
+
writeFileSync32(tmp, JSON.stringify(record2, null, 2) + `
|
|
79157
79265
|
`, { mode: 384 });
|
|
79158
79266
|
renameSync13(tmp, target);
|
|
79159
79267
|
}
|
|
79160
79268
|
function readRecord(id) {
|
|
79161
79269
|
const path7 = recordPath(id);
|
|
79162
79270
|
try {
|
|
79163
|
-
const raw =
|
|
79271
|
+
const raw = readFileSync59(path7, "utf8");
|
|
79164
79272
|
return JSON.parse(raw);
|
|
79165
79273
|
} catch {
|
|
79166
79274
|
return null;
|
|
@@ -79525,7 +79633,7 @@ import {
|
|
|
79525
79633
|
mkdirSync as mkdirSync38,
|
|
79526
79634
|
readdirSync as readdirSync25,
|
|
79527
79635
|
rmSync as rmSync15,
|
|
79528
|
-
writeFileSync as
|
|
79636
|
+
writeFileSync as writeFileSync33
|
|
79529
79637
|
} from "node:fs";
|
|
79530
79638
|
import { join as join70 } from "node:path";
|
|
79531
79639
|
function encodeCredentialsFilename(email) {
|
|
@@ -79727,7 +79835,7 @@ function writeSeedFile(dir, email, seed) {
|
|
|
79727
79835
|
}
|
|
79728
79836
|
const filename = encodeCredentialsFilename(email);
|
|
79729
79837
|
const filePath = join70(dir, filename);
|
|
79730
|
-
|
|
79838
|
+
writeFileSync33(filePath, JSON.stringify(seed), { mode: 384 });
|
|
79731
79839
|
chmodSync9(filePath, 384);
|
|
79732
79840
|
return filePath;
|
|
79733
79841
|
}
|
|
@@ -79885,7 +79993,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
79885
79993
|
// src/cli/m365-mcp-launcher.ts
|
|
79886
79994
|
init_scaffold_integration();
|
|
79887
79995
|
import { spawn as spawn6 } from "node:child_process";
|
|
79888
|
-
import { writeFileSync as
|
|
79996
|
+
import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync39 } from "node:fs";
|
|
79889
79997
|
import { dirname as dirname18, join as join71 } from "node:path";
|
|
79890
79998
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
79891
79999
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
@@ -79912,7 +80020,7 @@ function writeRefreshHeartbeat(agentName, data) {
|
|
|
79912
80020
|
const path7 = heartbeatPath(agentName);
|
|
79913
80021
|
try {
|
|
79914
80022
|
mkdirSync39(dirname18(path7), { recursive: true });
|
|
79915
|
-
|
|
80023
|
+
writeFileSync34(path7, JSON.stringify(data, null, 2), { mode: 420 });
|
|
79916
80024
|
} catch {}
|
|
79917
80025
|
}
|
|
79918
80026
|
function heartbeatPath(agentName) {
|
|
@@ -80103,7 +80211,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
80103
80211
|
// src/cli/notion-mcp-launcher.ts
|
|
80104
80212
|
init_scaffold_integration();
|
|
80105
80213
|
import { spawn as spawn7 } from "node:child_process";
|
|
80106
|
-
import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as
|
|
80214
|
+
import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as writeFileSync35 } from "node:fs";
|
|
80107
80215
|
import { dirname as dirname19 } from "node:path";
|
|
80108
80216
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
80109
80217
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
@@ -80117,7 +80225,7 @@ function defaultWriteHeartbeat(path7, contents) {
|
|
|
80117
80225
|
const dir = dirname19(path7);
|
|
80118
80226
|
if (!existsSync71(dir))
|
|
80119
80227
|
mkdirSync40(dir, { recursive: true });
|
|
80120
|
-
|
|
80228
|
+
writeFileSync35(path7, contents);
|
|
80121
80229
|
} catch {}
|
|
80122
80230
|
}
|
|
80123
80231
|
async function runNotionMcpLauncher(opts, runtime) {
|
|
@@ -80227,7 +80335,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
80227
80335
|
|
|
80228
80336
|
// src/cli/deliver-file.ts
|
|
80229
80337
|
init_client2();
|
|
80230
|
-
import { readFileSync as
|
|
80338
|
+
import { readFileSync as readFileSync60, statSync as statSync29 } from "node:fs";
|
|
80231
80339
|
import { basename as basename8 } from "node:path";
|
|
80232
80340
|
|
|
80233
80341
|
// src/delivery/onedrive.ts
|
|
@@ -80567,7 +80675,7 @@ async function defaultResolveProvider() {
|
|
|
80567
80675
|
async function runDeliverFile(localPath, deps = {}) {
|
|
80568
80676
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
80569
80677
|
const sizeOf = deps.fileSize ?? ((p) => statSync29(p).size);
|
|
80570
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
80678
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync60(p)));
|
|
80571
80679
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
80572
80680
|
let size;
|
|
80573
80681
|
try {
|
|
@@ -80857,7 +80965,7 @@ async function fetchToken(vaultKey) {
|
|
|
80857
80965
|
|
|
80858
80966
|
// src/cli/apply.ts
|
|
80859
80967
|
init_source();
|
|
80860
|
-
import { accessSync as accessSync3, chownSync as chownSync5, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync74, mkdirSync as mkdirSync42, readFileSync as
|
|
80968
|
+
import { accessSync as accessSync3, chownSync as chownSync5, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync74, mkdirSync as mkdirSync42, readFileSync as readFileSync62, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync37 } from "node:fs";
|
|
80861
80969
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
80862
80970
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
80863
80971
|
import readline from "node:readline";
|
|
@@ -81254,7 +81362,7 @@ init_loader();
|
|
|
81254
81362
|
init_loader();
|
|
81255
81363
|
|
|
81256
81364
|
// src/cli/update-prompt-hook.ts
|
|
81257
|
-
import { existsSync as existsSync72, readFileSync as
|
|
81365
|
+
import { existsSync as existsSync72, readFileSync as readFileSync61, writeFileSync as writeFileSync36, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
|
|
81258
81366
|
import { join as join72 } from "node:path";
|
|
81259
81367
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
81260
81368
|
function updatePromptHookScript() {
|
|
@@ -81326,9 +81434,9 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81326
81434
|
const scriptPath = join72(hooksDir, HOOK_FILENAME);
|
|
81327
81435
|
const desired = updatePromptHookScript();
|
|
81328
81436
|
let installed = false;
|
|
81329
|
-
const existing = existsSync72(scriptPath) ?
|
|
81437
|
+
const existing = existsSync72(scriptPath) ? readFileSync61(scriptPath, "utf-8") : "";
|
|
81330
81438
|
if (existing !== desired) {
|
|
81331
|
-
|
|
81439
|
+
writeFileSync36(scriptPath, desired, { mode: 493 });
|
|
81332
81440
|
chmodSync10(scriptPath, 493);
|
|
81333
81441
|
installed = true;
|
|
81334
81442
|
} else {
|
|
@@ -81340,7 +81448,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81340
81448
|
if (!existsSync72(settingsPath)) {
|
|
81341
81449
|
return { scriptPath, settingsPath, installed };
|
|
81342
81450
|
}
|
|
81343
|
-
const raw =
|
|
81451
|
+
const raw = readFileSync61(settingsPath, "utf-8");
|
|
81344
81452
|
let parsed;
|
|
81345
81453
|
try {
|
|
81346
81454
|
parsed = JSON.parse(raw);
|
|
@@ -81373,7 +81481,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81373
81481
|
});
|
|
81374
81482
|
hooks.UserPromptSubmit = list2;
|
|
81375
81483
|
parsed.hooks = hooks;
|
|
81376
|
-
|
|
81484
|
+
writeFileSync36(settingsPath, JSON.stringify(parsed, null, 2) + `
|
|
81377
81485
|
`, { mode: 384 });
|
|
81378
81486
|
installed = true;
|
|
81379
81487
|
}
|
|
@@ -81496,24 +81604,24 @@ async function ensureHostMountSources(config) {
|
|
|
81496
81604
|
}
|
|
81497
81605
|
const autoUnlockPath = join74(home2, ".switchroom", "vault-auto-unlock");
|
|
81498
81606
|
if (!existsSync74(autoUnlockPath)) {
|
|
81499
|
-
|
|
81607
|
+
writeFileSync37(autoUnlockPath, "", { mode: 384 });
|
|
81500
81608
|
}
|
|
81501
81609
|
const auditLogPath = join74(home2, ".switchroom", "vault-audit.log");
|
|
81502
81610
|
if (!existsSync74(auditLogPath)) {
|
|
81503
|
-
|
|
81611
|
+
writeFileSync37(auditLogPath, "", { mode: 420 });
|
|
81504
81612
|
}
|
|
81505
81613
|
const grantsDbPath = join74(home2, ".switchroom", "vault-grants.db");
|
|
81506
81614
|
if (!existsSync74(grantsDbPath)) {
|
|
81507
|
-
|
|
81615
|
+
writeFileSync37(grantsDbPath, "", { mode: 384 });
|
|
81508
81616
|
}
|
|
81509
81617
|
const hostdAuditLogPath = join74(home2, ".switchroom", "host-control-audit.log");
|
|
81510
81618
|
if (!existsSync74(hostdAuditLogPath)) {
|
|
81511
|
-
|
|
81619
|
+
writeFileSync37(hostdAuditLogPath, "", { mode: 420 });
|
|
81512
81620
|
}
|
|
81513
81621
|
for (const name of Object.keys(config.agents)) {
|
|
81514
81622
|
const tokenPath = join74(home2, ".switchroom", "agents", name, ".vault-token");
|
|
81515
81623
|
if (!existsSync74(tokenPath)) {
|
|
81516
|
-
|
|
81624
|
+
writeFileSync37(tokenPath, "", { mode: 384 });
|
|
81517
81625
|
}
|
|
81518
81626
|
try {
|
|
81519
81627
|
const uid = allocateAgentUid(name);
|
|
@@ -81524,13 +81632,13 @@ async function ensureHostMountSources(config) {
|
|
|
81524
81632
|
await mkdir2(fleetDir, { recursive: true });
|
|
81525
81633
|
const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
|
|
81526
81634
|
const invariantsCanonical = renderFleetInvariants();
|
|
81527
|
-
const invariantsCurrent = existsSync74(invariantsPath) ?
|
|
81635
|
+
const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync62(invariantsPath, "utf-8") : null;
|
|
81528
81636
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
81529
|
-
|
|
81637
|
+
writeFileSync37(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
81530
81638
|
}
|
|
81531
81639
|
const fleetClaudePath = join74(fleetDir, "CLAUDE.md");
|
|
81532
81640
|
if (!existsSync74(fleetClaudePath)) {
|
|
81533
|
-
|
|
81641
|
+
writeFileSync37(fleetClaudePath, [
|
|
81534
81642
|
"# Switchroom fleet defaults",
|
|
81535
81643
|
"",
|
|
81536
81644
|
"Operator-owned fleet brain. Every agent reads this via",
|
|
@@ -81622,7 +81730,7 @@ function writeInstallTypeCache(homeDir = homedir43()) {
|
|
|
81622
81730
|
detected_at: new Date().toISOString(),
|
|
81623
81731
|
source_paths: ctx.source_paths
|
|
81624
81732
|
};
|
|
81625
|
-
|
|
81733
|
+
writeFileSync37(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
|
|
81626
81734
|
renameSync14(tmp, out);
|
|
81627
81735
|
return out;
|
|
81628
81736
|
}
|
|
@@ -81818,10 +81926,10 @@ Done. Scaffolded ${scaffolded}/${allAgentNames.length} agents.
|
|
|
81818
81926
|
};
|
|
81819
81927
|
}
|
|
81820
81928
|
function formatScaffoldFailureResolution(failures, scaffolded, agentsTotal) {
|
|
81821
|
-
const
|
|
81929
|
+
const fail3 = failures.length;
|
|
81822
81930
|
const lines = [];
|
|
81823
81931
|
lines.push("");
|
|
81824
|
-
lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${
|
|
81932
|
+
lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${fail3} failed.`);
|
|
81825
81933
|
lines.push("");
|
|
81826
81934
|
lines.push("Per-agent state dirs are mode 0700 owned by per-agent UIDs (the v0.7+");
|
|
81827
81935
|
lines.push("docker model). The operator cannot write into them without privilege");
|
|
@@ -81854,7 +81962,7 @@ function copyExampleConfig2(name) {
|
|
|
81854
81962
|
}
|
|
81855
81963
|
const embedded = EMBEDDED_EXAMPLES[name];
|
|
81856
81964
|
if (embedded !== undefined) {
|
|
81857
|
-
|
|
81965
|
+
writeFileSync37(dest, embedded, { encoding: "utf8" });
|
|
81858
81966
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
81859
81967
|
return;
|
|
81860
81968
|
}
|
|
@@ -82049,7 +82157,7 @@ function runRedactStdin() {
|
|
|
82049
82157
|
}
|
|
82050
82158
|
|
|
82051
82159
|
// src/cli/status-ask.ts
|
|
82052
|
-
import { readFileSync as
|
|
82160
|
+
import { readFileSync as readFileSync63, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
|
|
82053
82161
|
import { join as join75 } from "node:path";
|
|
82054
82162
|
import { homedir as homedir44 } from "node:os";
|
|
82055
82163
|
|
|
@@ -82325,7 +82433,7 @@ function runReport(opts) {
|
|
|
82325
82433
|
for (const src of sources) {
|
|
82326
82434
|
let content;
|
|
82327
82435
|
try {
|
|
82328
|
-
content =
|
|
82436
|
+
content = readFileSync63(src.path, "utf-8");
|
|
82329
82437
|
} catch (err) {
|
|
82330
82438
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
82331
82439
|
`);
|
|
@@ -82430,7 +82538,7 @@ import {
|
|
|
82430
82538
|
mkdirSync as mkdirSync43,
|
|
82431
82539
|
openSync as openSync13,
|
|
82432
82540
|
readdirSync as readdirSync28,
|
|
82433
|
-
readFileSync as
|
|
82541
|
+
readFileSync as readFileSync64,
|
|
82434
82542
|
renameSync as renameSync15,
|
|
82435
82543
|
statSync as statSync30,
|
|
82436
82544
|
unlinkSync as unlinkSync14,
|
|
@@ -82554,7 +82662,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
82554
82662
|
continue;
|
|
82555
82663
|
const full = join76(paths.skillsDir, name);
|
|
82556
82664
|
try {
|
|
82557
|
-
const raw =
|
|
82665
|
+
const raw = readFileSync64(full, "utf-8");
|
|
82558
82666
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
82559
82667
|
out.push({ slug, path: full, raw });
|
|
82560
82668
|
} catch {}
|
|
@@ -82581,7 +82689,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
82581
82689
|
continue;
|
|
82582
82690
|
const full = join76(paths.scheduleDir, name);
|
|
82583
82691
|
try {
|
|
82584
|
-
const raw =
|
|
82692
|
+
const raw = readFileSync64(full, "utf-8");
|
|
82585
82693
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
82586
82694
|
out.push({ slug, path: full, raw });
|
|
82587
82695
|
} catch {}
|
|
@@ -82729,10 +82837,10 @@ import {
|
|
|
82729
82837
|
mkdirSync as mkdirSync44,
|
|
82730
82838
|
openSync as openSync14,
|
|
82731
82839
|
readdirSync as readdirSync29,
|
|
82732
|
-
readFileSync as
|
|
82840
|
+
readFileSync as readFileSync65,
|
|
82733
82841
|
renameSync as renameSync16,
|
|
82734
82842
|
unlinkSync as unlinkSync15,
|
|
82735
|
-
writeFileSync as
|
|
82843
|
+
writeFileSync as writeFileSync38,
|
|
82736
82844
|
writeSync as writeSync9
|
|
82737
82845
|
} from "node:fs";
|
|
82738
82846
|
import { join as join77 } from "node:path";
|
|
@@ -82775,7 +82883,7 @@ function stagePendingScheduleEntry(opts) {
|
|
|
82775
82883
|
}
|
|
82776
82884
|
renameSync16(yamlTmp, yamlPath);
|
|
82777
82885
|
}
|
|
82778
|
-
|
|
82886
|
+
writeFileSync38(metaPath, JSON.stringify(meta, null, 2) + `
|
|
82779
82887
|
`, { mode: 384 });
|
|
82780
82888
|
return { stageId, yamlPath, metaPath };
|
|
82781
82889
|
}
|
|
@@ -82793,7 +82901,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
82793
82901
|
if (!existsSync77(yamlPath))
|
|
82794
82902
|
continue;
|
|
82795
82903
|
try {
|
|
82796
|
-
const meta = JSON.parse(
|
|
82904
|
+
const meta = JSON.parse(readFileSync65(metaPath, "utf-8"));
|
|
82797
82905
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
82798
82906
|
continue;
|
|
82799
82907
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -82831,7 +82939,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
82831
82939
|
}
|
|
82832
82940
|
|
|
82833
82941
|
// src/cli/agent-config-write.ts
|
|
82834
|
-
import { existsSync as existsSync78, readFileSync as
|
|
82942
|
+
import { existsSync as existsSync78, readFileSync as readFileSync66 } from "node:fs";
|
|
82835
82943
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
82836
82944
|
|
|
82837
82945
|
// src/scheduler/schedule-report.ts
|
|
@@ -83199,7 +83307,7 @@ function scheduleRemove(opts) {
|
|
|
83199
83307
|
let priorContent = null;
|
|
83200
83308
|
try {
|
|
83201
83309
|
if (existsSync78(match.path))
|
|
83202
|
-
priorContent =
|
|
83310
|
+
priorContent = readFileSync66(match.path, "utf-8");
|
|
83203
83311
|
} catch {}
|
|
83204
83312
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
83205
83313
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -83402,7 +83510,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
83402
83510
|
}
|
|
83403
83511
|
let blob;
|
|
83404
83512
|
if (opts.jsonl) {
|
|
83405
|
-
blob = existsSync78(opts.jsonl) ?
|
|
83513
|
+
blob = existsSync78(opts.jsonl) ? readFileSync66(opts.jsonl, "utf-8") : "";
|
|
83406
83514
|
} else {
|
|
83407
83515
|
try {
|
|
83408
83516
|
blob = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -83682,13 +83790,13 @@ import {
|
|
|
83682
83790
|
mkdirSync as mkdirSync45,
|
|
83683
83791
|
mkdtempSync as mkdtempSync5,
|
|
83684
83792
|
openSync as openSync15,
|
|
83685
|
-
readFileSync as
|
|
83793
|
+
readFileSync as readFileSync67,
|
|
83686
83794
|
readdirSync as readdirSync30,
|
|
83687
83795
|
realpathSync as realpathSync7,
|
|
83688
83796
|
renameSync as renameSync17,
|
|
83689
83797
|
rmSync as rmSync16,
|
|
83690
83798
|
statSync as statSync31,
|
|
83691
|
-
writeFileSync as
|
|
83799
|
+
writeFileSync as writeFileSync39
|
|
83692
83800
|
} from "node:fs";
|
|
83693
83801
|
import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
|
|
83694
83802
|
import { dirname as dirname23, join as join79, relative as relative2, resolve as resolve46 } from "node:path";
|
|
@@ -83918,7 +84026,7 @@ function isTarballPath(p) {
|
|
|
83918
84026
|
function loadFromDir(dir) {
|
|
83919
84027
|
const abs = realpathSync7(dir);
|
|
83920
84028
|
if (!statSync31(abs).isDirectory()) {
|
|
83921
|
-
|
|
84029
|
+
fail3(`--from path is not a directory: ${dir}`);
|
|
83922
84030
|
}
|
|
83923
84031
|
const files = {};
|
|
83924
84032
|
const walk2 = (sub) => {
|
|
@@ -83927,14 +84035,14 @@ function loadFromDir(dir) {
|
|
|
83927
84035
|
const full = join79(sub, ent.name);
|
|
83928
84036
|
const rel = relative2(abs, full);
|
|
83929
84037
|
if (ent.isSymbolicLink()) {
|
|
83930
|
-
|
|
84038
|
+
fail3(`refusing to read symlink inside --from dir: ${rel}`);
|
|
83931
84039
|
}
|
|
83932
84040
|
if (ent.isDirectory()) {
|
|
83933
84041
|
walk2(full);
|
|
83934
84042
|
continue;
|
|
83935
84043
|
}
|
|
83936
84044
|
if (ent.isFile()) {
|
|
83937
|
-
const buf =
|
|
84045
|
+
const buf = readFileSync67(full);
|
|
83938
84046
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
83939
84047
|
}
|
|
83940
84048
|
}
|
|
@@ -83950,13 +84058,13 @@ function loadFromTarball(tarPath) {
|
|
|
83950
84058
|
stdio: ["ignore", "pipe", "pipe"]
|
|
83951
84059
|
});
|
|
83952
84060
|
if (list2.status !== 0) {
|
|
83953
|
-
|
|
84061
|
+
fail3(`tar list failed (exit ${list2.status}): ${(list2.stderr ?? "").trim()}`);
|
|
83954
84062
|
}
|
|
83955
84063
|
const entries = (list2.stdout ?? "").split(`
|
|
83956
84064
|
`).map((s) => s.trim()).filter((s) => s.length > 0 && !s.endsWith("/"));
|
|
83957
84065
|
for (const entry of entries) {
|
|
83958
84066
|
if (!validateRelPath(entry)) {
|
|
83959
|
-
|
|
84067
|
+
fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
|
|
83960
84068
|
}
|
|
83961
84069
|
}
|
|
83962
84070
|
const staging = mkdtempSync5(join79(tmpdir5(), "skill-apply-extract-"));
|
|
@@ -83973,7 +84081,7 @@ function loadFromTarball(tarPath) {
|
|
|
83973
84081
|
], { stdio: "pipe" });
|
|
83974
84082
|
if (r.status !== 0) {
|
|
83975
84083
|
const stderr = r.stderr?.toString("utf-8") ?? "(no stderr)";
|
|
83976
|
-
|
|
84084
|
+
fail3(`tar extraction failed (exit ${r.status}): ${stderr.trim()}`);
|
|
83977
84085
|
}
|
|
83978
84086
|
return loadFromDir(staging);
|
|
83979
84087
|
} finally {
|
|
@@ -83983,13 +84091,13 @@ function loadFromTarball(tarPath) {
|
|
|
83983
84091
|
}
|
|
83984
84092
|
}
|
|
83985
84093
|
function loadSingleFile(filePath) {
|
|
83986
|
-
const content =
|
|
84094
|
+
const content = readFileSync67(filePath, "utf-8");
|
|
83987
84095
|
return { "SKILL.md": content };
|
|
83988
84096
|
}
|
|
83989
84097
|
function loadFromStdin() {
|
|
83990
84098
|
const content = readStdinSync();
|
|
83991
84099
|
if (content.length === 0) {
|
|
83992
|
-
|
|
84100
|
+
fail3("no content on stdin; either pipe SKILL.md content in or pass --from");
|
|
83993
84101
|
}
|
|
83994
84102
|
return { "SKILL.md": content };
|
|
83995
84103
|
}
|
|
@@ -84048,7 +84156,7 @@ function validatePayload(name, files) {
|
|
|
84048
84156
|
const tmp = mkdtempSync5(join79(tmpdir5(), "skill-apply-py-"));
|
|
84049
84157
|
const tmpPy = join79(tmp, "check.py");
|
|
84050
84158
|
try {
|
|
84051
|
-
|
|
84159
|
+
writeFileSync39(tmpPy, content);
|
|
84052
84160
|
const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
|
|
84053
84161
|
encoding: "utf-8"
|
|
84054
84162
|
});
|
|
@@ -84074,7 +84182,7 @@ function diffSummary(currentDir, files) {
|
|
|
84074
84182
|
if (ent.isDirectory()) {
|
|
84075
84183
|
walk2(full);
|
|
84076
84184
|
} else if (ent.isFile()) {
|
|
84077
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
84185
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync67(full, "utf-8");
|
|
84078
84186
|
}
|
|
84079
84187
|
}
|
|
84080
84188
|
};
|
|
@@ -84114,7 +84222,7 @@ function writePayload(poolDir, name, files) {
|
|
|
84114
84222
|
}
|
|
84115
84223
|
} catch {}
|
|
84116
84224
|
if (targetIsSymlink) {
|
|
84117
|
-
|
|
84225
|
+
fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
|
|
84118
84226
|
}
|
|
84119
84227
|
const staging = mkdtempSync5(join79(poolDir, `.skill-apply-stage-${name}-`));
|
|
84120
84228
|
let oldRename = null;
|
|
@@ -84124,7 +84232,7 @@ function writePayload(poolDir, name, files) {
|
|
|
84124
84232
|
mkdirSync45(dirname23(full), { recursive: true, mode: 493 });
|
|
84125
84233
|
const fd = openSync15(full, "wx");
|
|
84126
84234
|
try {
|
|
84127
|
-
|
|
84235
|
+
writeFileSync39(fd, content);
|
|
84128
84236
|
} finally {
|
|
84129
84237
|
closeSync15(fd);
|
|
84130
84238
|
}
|
|
@@ -84162,7 +84270,7 @@ function writePayload(poolDir, name, files) {
|
|
|
84162
84270
|
throw err2;
|
|
84163
84271
|
}
|
|
84164
84272
|
}
|
|
84165
|
-
function
|
|
84273
|
+
function fail3(msg) {
|
|
84166
84274
|
console.error(source_default.red(`error: ${msg}`));
|
|
84167
84275
|
process.exit(2);
|
|
84168
84276
|
}
|
|
@@ -84175,7 +84283,7 @@ function registerSkillCommand(program3) {
|
|
|
84175
84283
|
} else {
|
|
84176
84284
|
const fromPath = resolve46(opts.from);
|
|
84177
84285
|
if (!existsSync80(fromPath)) {
|
|
84178
|
-
|
|
84286
|
+
fail3(`--from path does not exist: ${opts.from}`);
|
|
84179
84287
|
}
|
|
84180
84288
|
const st = statSync31(fromPath);
|
|
84181
84289
|
if (st.isDirectory()) {
|
|
@@ -84185,7 +84293,7 @@ function registerSkillCommand(program3) {
|
|
|
84185
84293
|
} else if (fromPath.endsWith(".md")) {
|
|
84186
84294
|
files = loadSingleFile(fromPath);
|
|
84187
84295
|
} else {
|
|
84188
|
-
|
|
84296
|
+
fail3(`--from must be a directory, a .tar/.tar.gz/.tgz tarball, or a ` + `.md file. Got: ${opts.from}`);
|
|
84189
84297
|
}
|
|
84190
84298
|
}
|
|
84191
84299
|
const v = validatePayload(name, files);
|
|
@@ -84234,13 +84342,13 @@ import {
|
|
|
84234
84342
|
mkdirSync as mkdirSync46,
|
|
84235
84343
|
mkdtempSync as mkdtempSync6,
|
|
84236
84344
|
openSync as openSync16,
|
|
84237
|
-
readFileSync as
|
|
84345
|
+
readFileSync as readFileSync68,
|
|
84238
84346
|
readdirSync as readdirSync31,
|
|
84239
84347
|
renameSync as renameSync18,
|
|
84240
84348
|
rmSync as rmSync17,
|
|
84241
84349
|
statSync as statSync32,
|
|
84242
84350
|
utimesSync,
|
|
84243
|
-
writeFileSync as
|
|
84351
|
+
writeFileSync as writeFileSync40
|
|
84244
84352
|
} from "node:fs";
|
|
84245
84353
|
import { dirname as dirname24, join as join80, relative as relative3, resolve as resolve47 } from "node:path";
|
|
84246
84354
|
import { homedir as homedir46, tmpdir as tmpdir6 } from "node:os";
|
|
@@ -84316,7 +84424,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
84316
84424
|
if (ent.isDirectory())
|
|
84317
84425
|
walk2(s, d);
|
|
84318
84426
|
else if (ent.isFile()) {
|
|
84319
|
-
|
|
84427
|
+
writeFileSync40(d, readFileSync68(s));
|
|
84320
84428
|
}
|
|
84321
84429
|
}
|
|
84322
84430
|
};
|
|
@@ -84331,7 +84439,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
84331
84439
|
`));
|
|
84332
84440
|
}
|
|
84333
84441
|
}
|
|
84334
|
-
function
|
|
84442
|
+
function fail4(msg, exit = 2) {
|
|
84335
84443
|
console.error(source_default.red(`error: ${msg}`));
|
|
84336
84444
|
process.exit(exit);
|
|
84337
84445
|
}
|
|
@@ -84339,10 +84447,10 @@ function resolveAgent(opts) {
|
|
|
84339
84447
|
const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
|
|
84340
84448
|
const agent = opts.agent ?? fromEnv;
|
|
84341
84449
|
if (!agent) {
|
|
84342
|
-
|
|
84450
|
+
fail4("agent name required: pass --agent <name>, or set SWITCHROOM_AGENT_NAME " + "in the calling environment (set by switchroom for in-container agents).");
|
|
84343
84451
|
}
|
|
84344
84452
|
if (!SKILL_SLUG_RE.test(agent)) {
|
|
84345
|
-
|
|
84453
|
+
fail4(`agent name has invalid shape: ${JSON.stringify(agent)}`);
|
|
84346
84454
|
}
|
|
84347
84455
|
return agent;
|
|
84348
84456
|
}
|
|
@@ -84380,14 +84488,14 @@ function readStdinSync2() {
|
|
|
84380
84488
|
function loadFromDir2(dir) {
|
|
84381
84489
|
const abs = resolve47(dir);
|
|
84382
84490
|
if (!statSync32(abs).isDirectory()) {
|
|
84383
|
-
|
|
84491
|
+
fail4(`--from path is not a directory: ${dir}`);
|
|
84384
84492
|
}
|
|
84385
84493
|
const files = {};
|
|
84386
84494
|
const walk2 = (sub) => {
|
|
84387
84495
|
for (const ent of readdirSync31(sub, { withFileTypes: true })) {
|
|
84388
84496
|
const full = join80(sub, ent.name);
|
|
84389
84497
|
if (ent.isSymbolicLink()) {
|
|
84390
|
-
|
|
84498
|
+
fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
|
|
84391
84499
|
}
|
|
84392
84500
|
if (ent.isDirectory()) {
|
|
84393
84501
|
walk2(full);
|
|
@@ -84395,7 +84503,7 @@ function loadFromDir2(dir) {
|
|
|
84395
84503
|
}
|
|
84396
84504
|
if (ent.isFile()) {
|
|
84397
84505
|
const rel = relative3(abs, full).replace(/\\/g, "/");
|
|
84398
|
-
files[rel] =
|
|
84506
|
+
files[rel] = readFileSync68(full, "utf-8");
|
|
84399
84507
|
}
|
|
84400
84508
|
}
|
|
84401
84509
|
};
|
|
@@ -84405,7 +84513,7 @@ function loadFromDir2(dir) {
|
|
|
84405
84513
|
function loadFromStdin2() {
|
|
84406
84514
|
const raw = readStdinSync2();
|
|
84407
84515
|
if (raw.length === 0) {
|
|
84408
|
-
|
|
84516
|
+
fail4("no content on stdin; pipe a SKILL.md or JSON file-map");
|
|
84409
84517
|
}
|
|
84410
84518
|
const trimmed = raw.trimStart();
|
|
84411
84519
|
if (trimmed.startsWith("{")) {
|
|
@@ -84413,15 +84521,15 @@ function loadFromStdin2() {
|
|
|
84413
84521
|
try {
|
|
84414
84522
|
parsed = JSON.parse(raw);
|
|
84415
84523
|
} catch (err2) {
|
|
84416
|
-
|
|
84524
|
+
fail4(`stdin starts with '{' but is not valid JSON: ${err2.message}`);
|
|
84417
84525
|
}
|
|
84418
84526
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
84419
|
-
|
|
84527
|
+
fail4("stdin JSON must be an object of {path: content, ...}");
|
|
84420
84528
|
}
|
|
84421
84529
|
const files = {};
|
|
84422
84530
|
for (const [k, v] of Object.entries(parsed)) {
|
|
84423
84531
|
if (typeof v !== "string") {
|
|
84424
|
-
|
|
84532
|
+
fail4(`stdin JSON: value for ${JSON.stringify(k)} must be a string`);
|
|
84425
84533
|
}
|
|
84426
84534
|
files[k] = v;
|
|
84427
84535
|
}
|
|
@@ -84441,7 +84549,7 @@ function behavioralValidate(files) {
|
|
|
84441
84549
|
const tmp = mkdtempSync6(join80(tmpdir6(), "skill-personal-py-"));
|
|
84442
84550
|
const tmpPy = join80(tmp, "check.py");
|
|
84443
84551
|
try {
|
|
84444
|
-
|
|
84552
|
+
writeFileSync40(tmpPy, content);
|
|
84445
84553
|
const r = spawnSync12("python3", ["-m", "py_compile", tmpPy], {
|
|
84446
84554
|
encoding: "utf-8"
|
|
84447
84555
|
});
|
|
@@ -84481,7 +84589,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
84481
84589
|
}
|
|
84482
84590
|
} catch {}
|
|
84483
84591
|
if (targetIsSymlink) {
|
|
84484
|
-
|
|
84592
|
+
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
84485
84593
|
}
|
|
84486
84594
|
mkdirSync46(dirname24(targetDir), { recursive: true, mode: 493 });
|
|
84487
84595
|
const staging = mkdtempSync6(join80(dirname24(targetDir), `.skill-personal-stage-`));
|
|
@@ -84492,7 +84600,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
84492
84600
|
mkdirSync46(dirname24(full), { recursive: true, mode: 493 });
|
|
84493
84601
|
const fd = openSync16(full, "wx");
|
|
84494
84602
|
try {
|
|
84495
|
-
|
|
84603
|
+
writeFileSync40(fd, content);
|
|
84496
84604
|
} finally {
|
|
84497
84605
|
closeSync16(fd);
|
|
84498
84606
|
}
|
|
@@ -84533,7 +84641,7 @@ function writePersonalSkill(targetDir, files) {
|
|
|
84533
84641
|
function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
84534
84642
|
sweepTrash(agentsRoot, agent);
|
|
84535
84643
|
if (!SKILL_SLUG_RE.test(name)) {
|
|
84536
|
-
|
|
84644
|
+
fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
|
|
84537
84645
|
}
|
|
84538
84646
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
84539
84647
|
const exists = (() => {
|
|
@@ -84545,10 +84653,10 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
|
84545
84653
|
}
|
|
84546
84654
|
})();
|
|
84547
84655
|
if (ensureNew && exists) {
|
|
84548
|
-
|
|
84656
|
+
fail4(`personal skill ${JSON.stringify(name)} already exists for agent ${JSON.stringify(agent)}. ` + `Use \`skill edit-personal\` to overwrite, or \`skill remove-personal\` first.`, 9);
|
|
84549
84657
|
}
|
|
84550
84658
|
if (!ensureNew && !exists) {
|
|
84551
|
-
|
|
84659
|
+
fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}. ` + `Use \`skill init-personal\` to create it.`, 9);
|
|
84552
84660
|
}
|
|
84553
84661
|
const v = validateSkillBundle(name, files);
|
|
84554
84662
|
if (!v.ok) {
|
|
@@ -84574,16 +84682,16 @@ function loadFiles(opts) {
|
|
|
84574
84682
|
}
|
|
84575
84683
|
const p = resolve47(opts.from);
|
|
84576
84684
|
if (!existsSync81(p)) {
|
|
84577
|
-
|
|
84685
|
+
fail4(`--from path does not exist: ${opts.from}`);
|
|
84578
84686
|
}
|
|
84579
84687
|
const st = statSync32(p);
|
|
84580
84688
|
if (st.isDirectory()) {
|
|
84581
84689
|
return loadFromDir2(p);
|
|
84582
84690
|
}
|
|
84583
84691
|
if (p.endsWith(".md")) {
|
|
84584
|
-
return { "SKILL.md":
|
|
84692
|
+
return { "SKILL.md": readFileSync68(p, "utf-8") };
|
|
84585
84693
|
}
|
|
84586
|
-
|
|
84694
|
+
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
84587
84695
|
}
|
|
84588
84696
|
function initPersonalAction(name, opts) {
|
|
84589
84697
|
const agent = resolveAgent(opts);
|
|
@@ -84629,18 +84737,18 @@ function defaultBundledRoot() {
|
|
|
84629
84737
|
function resolveCloneSource(source, opts) {
|
|
84630
84738
|
const m = CLONE_SOURCE_RE.exec(source);
|
|
84631
84739
|
if (!m) {
|
|
84632
|
-
|
|
84740
|
+
fail4(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
|
|
84633
84741
|
}
|
|
84634
84742
|
const tier = m[1];
|
|
84635
84743
|
const slug = m[2];
|
|
84636
84744
|
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
84637
84745
|
const dir = join80(root, slug);
|
|
84638
84746
|
if (!existsSync81(dir)) {
|
|
84639
|
-
|
|
84747
|
+
fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
84640
84748
|
}
|
|
84641
84749
|
const st = lstatSync9(dir);
|
|
84642
84750
|
if (st.isSymbolicLink()) {
|
|
84643
|
-
|
|
84751
|
+
fail4(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
|
|
84644
84752
|
}
|
|
84645
84753
|
return { tier, slug, dir };
|
|
84646
84754
|
}
|
|
@@ -84667,10 +84775,10 @@ function readSourceFiles(dir) {
|
|
|
84667
84775
|
try {
|
|
84668
84776
|
const st = lstatSync9(full);
|
|
84669
84777
|
if (st.size > CLONE_MAX_FILE_BYTES) {
|
|
84670
|
-
|
|
84778
|
+
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
84671
84779
|
}
|
|
84672
84780
|
} catch {}
|
|
84673
|
-
files[rel] =
|
|
84781
|
+
files[rel] = readFileSync68(full, "utf-8");
|
|
84674
84782
|
}
|
|
84675
84783
|
}
|
|
84676
84784
|
};
|
|
@@ -84701,11 +84809,11 @@ function clonePersonalAction(source, opts) {
|
|
|
84701
84809
|
const src = resolveCloneSource(source, opts);
|
|
84702
84810
|
const newName = opts.name ?? src.slug;
|
|
84703
84811
|
if (!SKILL_SLUG_RE.test(newName)) {
|
|
84704
|
-
|
|
84812
|
+
fail4(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
|
|
84705
84813
|
}
|
|
84706
84814
|
const { files, skipped } = readSourceFiles(src.dir);
|
|
84707
84815
|
if (!files["SKILL.md"]) {
|
|
84708
|
-
|
|
84816
|
+
fail4(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
|
|
84709
84817
|
}
|
|
84710
84818
|
if (newName !== src.slug) {
|
|
84711
84819
|
files["SKILL.md"] = rewriteSkillMdName(files["SKILL.md"], newName);
|
|
@@ -84743,18 +84851,18 @@ function removePersonalAction(name, opts) {
|
|
|
84743
84851
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
84744
84852
|
sweepTrash(agentsRoot, agent);
|
|
84745
84853
|
if (!SKILL_SLUG_RE.test(name)) {
|
|
84746
|
-
|
|
84854
|
+
fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
|
|
84747
84855
|
}
|
|
84748
84856
|
const target = personalSkillDir(agentsRoot, agent, name);
|
|
84749
84857
|
try {
|
|
84750
84858
|
const st = lstatSync9(target);
|
|
84751
84859
|
if (st.isSymbolicLink()) {
|
|
84752
|
-
|
|
84860
|
+
fail4(`refusing to remove symlink at ${target}; investigate manually`);
|
|
84753
84861
|
}
|
|
84754
84862
|
} catch (err2) {
|
|
84755
84863
|
const e = err2;
|
|
84756
84864
|
if (e.code === "ENOENT") {
|
|
84757
|
-
|
|
84865
|
+
fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}`, 1);
|
|
84758
84866
|
}
|
|
84759
84867
|
throw err2;
|
|
84760
84868
|
}
|
|
@@ -84839,7 +84947,7 @@ function registerSkillPersonalCommands(program3) {
|
|
|
84839
84947
|
// src/cli/skill-search.ts
|
|
84840
84948
|
init_helpers();
|
|
84841
84949
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
84842
|
-
import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as
|
|
84950
|
+
import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync69, statSync as statSync33 } from "node:fs";
|
|
84843
84951
|
import { homedir as homedir47 } from "node:os";
|
|
84844
84952
|
import { join as join81, resolve as resolve48 } from "node:path";
|
|
84845
84953
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -84860,7 +84968,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
84860
84968
|
return null;
|
|
84861
84969
|
let content;
|
|
84862
84970
|
try {
|
|
84863
|
-
content =
|
|
84971
|
+
content = readFileSync69(mdPath, "utf-8");
|
|
84864
84972
|
} catch {
|
|
84865
84973
|
return null;
|
|
84866
84974
|
}
|
|
@@ -85132,7 +85240,7 @@ function registerHostdMcpCommand(program3) {
|
|
|
85132
85240
|
// src/cli/hostd.ts
|
|
85133
85241
|
init_source();
|
|
85134
85242
|
init_helpers();
|
|
85135
|
-
import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as
|
|
85243
|
+
import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync71, writeFileSync as writeFileSync41, statSync as statSync34, copyFileSync as copyFileSync12 } from "node:fs";
|
|
85136
85244
|
import { homedir as homedir48 } from "node:os";
|
|
85137
85245
|
import { join as join82 } from "node:path";
|
|
85138
85246
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
@@ -85308,7 +85416,7 @@ async function doInstall(opts, program3) {
|
|
|
85308
85416
|
const bak = backupExistingCompose();
|
|
85309
85417
|
if (bak)
|
|
85310
85418
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
85311
|
-
|
|
85419
|
+
writeFileSync41(composePath, yaml, "utf8");
|
|
85312
85420
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
85313
85421
|
const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
|
|
85314
85422
|
console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
|
|
@@ -85415,7 +85523,7 @@ function registerHostdCommand(program3) {
|
|
|
85415
85523
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
85416
85524
|
return;
|
|
85417
85525
|
}
|
|
85418
|
-
const raw =
|
|
85526
|
+
const raw = readFileSync71(logPath, "utf-8");
|
|
85419
85527
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
85420
85528
|
const filters = {
|
|
85421
85529
|
agent: opts.agent,
|
|
@@ -85457,7 +85565,7 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
85457
85565
|
// src/cli/webd.ts
|
|
85458
85566
|
init_source();
|
|
85459
85567
|
init_helpers();
|
|
85460
|
-
import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as
|
|
85568
|
+
import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync42, copyFileSync as copyFileSync13 } from "node:fs";
|
|
85461
85569
|
import { homedir as homedir49 } from "node:os";
|
|
85462
85570
|
import { join as join83 } from "node:path";
|
|
85463
85571
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
@@ -85593,7 +85701,7 @@ async function doInstall2(opts, program3) {
|
|
|
85593
85701
|
const bak = backupExistingCompose2();
|
|
85594
85702
|
if (bak)
|
|
85595
85703
|
console.log(source_default.dim(` Backed up existing compose to ${bak}`));
|
|
85596
|
-
|
|
85704
|
+
writeFileSync42(composePath, yaml, "utf8");
|
|
85597
85705
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|
|
85598
85706
|
console.log(source_default.dim(` running as uid ${operatorUid} (operator), network_mode: host`));
|
|
85599
85707
|
console.log(source_default.dim(` Pulling ghcr.io/switchroom/switchroom-web:${imageTag}\u2026`));
|
|
@@ -85698,6 +85806,7 @@ registerTopicsCommand(program3);
|
|
|
85698
85806
|
registerAuthCommand(program3);
|
|
85699
85807
|
registerVaultCommand(program3);
|
|
85700
85808
|
registerTelegramCommand(program3);
|
|
85809
|
+
registerLinearAgentCommand(program3);
|
|
85701
85810
|
registerMemoryCommand(program3);
|
|
85702
85811
|
registerWebCommand(program3);
|
|
85703
85812
|
registerHandoffCommand(program3);
|