@strideops/bridge 0.1.1 → 0.1.3
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/cli.js +169 -14
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { existsSync, mkdirSync, accessSync, constants, writeFileSync, chmodSync
|
|
|
7
7
|
import { execSync, spawn } from 'child_process';
|
|
8
8
|
|
|
9
9
|
// src/version.ts
|
|
10
|
-
var VERSION = "0.1.
|
|
10
|
+
var VERSION = "0.1.3";
|
|
11
11
|
async function runPair(pairingCode, apiBaseUrl) {
|
|
12
12
|
const url = `${apiBaseUrl.replace(/\/$/, "")}/api/bridge/v1/pair`;
|
|
13
13
|
log(`Pairing with ${apiBaseUrl} ...`);
|
|
@@ -365,6 +365,59 @@ console.log(res.ok ? "identity-sync: onboarding recorded in Stride" : \`identity
|
|
|
365
365
|
mkdirSync(claudeDir, { recursive: true });
|
|
366
366
|
writeFileSync(join(claudeDir, "identity-sync.mjs"), script, { encoding: "utf-8", mode: 448 });
|
|
367
367
|
}
|
|
368
|
+
function writeMemorySearchScript(opts) {
|
|
369
|
+
const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts;
|
|
370
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
371
|
+
const searchUrl = `${base}/api/internal/build/agents/${agentId}/memory/search`;
|
|
372
|
+
const script = `#!/usr/bin/env node
|
|
373
|
+
// Auto-generated by stride-bridge. Do not edit.
|
|
374
|
+
// Semantically searches this agent's synced memory in Stride.
|
|
375
|
+
|
|
376
|
+
import { createHmac } from "node:crypto";
|
|
377
|
+
|
|
378
|
+
const HOOK_SECRET = ${JSON.stringify(hookSecret)};
|
|
379
|
+
const SEARCH_URL = ${JSON.stringify(searchUrl)};
|
|
380
|
+
|
|
381
|
+
const query = process.argv.slice(2).join(" ").trim();
|
|
382
|
+
if (!query) {
|
|
383
|
+
console.log("usage: node memory-search.mjs <query>");
|
|
384
|
+
process.exit(0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const body = JSON.stringify({ query, limit: 5 });
|
|
388
|
+
const ts = Math.floor(Date.now() / 1000).toString();
|
|
389
|
+
const sig = createHmac("sha256", HOOK_SECRET).update(ts + "
|
|
390
|
+
" + body).digest("hex");
|
|
391
|
+
|
|
392
|
+
const res = await fetch(SEARCH_URL, {
|
|
393
|
+
method: "POST",
|
|
394
|
+
headers: {
|
|
395
|
+
"Content-Type": "application/json",
|
|
396
|
+
"x-stride-hook-ts": ts,
|
|
397
|
+
"x-stride-hook-sig": sig,
|
|
398
|
+
},
|
|
399
|
+
body,
|
|
400
|
+
}).catch(() => null);
|
|
401
|
+
|
|
402
|
+
if (!res || !res.ok) {
|
|
403
|
+
console.log("memory-search: unavailable");
|
|
404
|
+
process.exit(0);
|
|
405
|
+
}
|
|
406
|
+
const data = await res.json();
|
|
407
|
+
const results = data?.data?.results ?? [];
|
|
408
|
+
if (results.length === 0) {
|
|
409
|
+
console.log("memory-search: no matches");
|
|
410
|
+
} else {
|
|
411
|
+
for (const r of results) {
|
|
412
|
+
console.log("--- " + (r.label ?? "memory") + " ---");
|
|
413
|
+
console.log(String(r.content ?? "").slice(0, 600));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
`;
|
|
417
|
+
const claudeDir = join(workspaceDir, ".claude");
|
|
418
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
419
|
+
writeFileSync(join(claudeDir, "memory-search.mjs"), script, { encoding: "utf-8", mode: 448 });
|
|
420
|
+
}
|
|
368
421
|
|
|
369
422
|
// src/agents.ts
|
|
370
423
|
var cachedAgents = [];
|
|
@@ -394,7 +447,7 @@ async function ensureAgentWorkspace(config, agent) {
|
|
|
394
447
|
sections.push(agent.soulMd ?? agent.defaultSoulMd ?? "");
|
|
395
448
|
if (agent.memoryProtocolMd) sections.push(agent.memoryProtocolMd);
|
|
396
449
|
sections.push(
|
|
397
|
-
|
|
450
|
+
'## Memory sync (local agent)\n\nAfter updating MEMORY.md or today\'s daily journal, sync them to Stride by running:\n\n```\nnode .claude/memory-sync.mjs\n```\n\nBefore re-researching something you may have learned before, search your own past memory semantically:\n\n```\nnode .claude/memory-search.mjs "your question"\n```\n'
|
|
398
451
|
);
|
|
399
452
|
if (!agent.onboardedAt && agent.onboardingMd) {
|
|
400
453
|
sections.push(
|
|
@@ -436,6 +489,12 @@ async function ensureAgentWorkspace(config, agent) {
|
|
|
436
489
|
apiBaseUrl: config.apiBaseUrl,
|
|
437
490
|
workspaceDir
|
|
438
491
|
});
|
|
492
|
+
writeMemorySearchScript({
|
|
493
|
+
agentId: agent.id,
|
|
494
|
+
hookSecret: agent.hookSecret,
|
|
495
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
496
|
+
workspaceDir
|
|
497
|
+
});
|
|
439
498
|
log(`Provisioned workspace for agent "${agent.name}" (${agent.id})`);
|
|
440
499
|
}
|
|
441
500
|
function agentWorkspaceDir(agentId) {
|
|
@@ -497,6 +556,12 @@ var STDOUT_CAP = 1e4;
|
|
|
497
556
|
var STDERR_CAP = 4e3;
|
|
498
557
|
var REPORT_RETRY_COUNT = 3;
|
|
499
558
|
var REPORT_RETRY_BASE_MS = 2e3;
|
|
559
|
+
function resolveClaudeCommand() {
|
|
560
|
+
if (process.platform !== "win32") return { cmd: "claude", shell: false };
|
|
561
|
+
const nativeExe = join(homedir(), ".local", "bin", "claude.exe");
|
|
562
|
+
if (existsSync(nativeExe)) return { cmd: nativeExe, shell: false };
|
|
563
|
+
return { cmd: "claude", shell: true };
|
|
564
|
+
}
|
|
500
565
|
function parseClaudeOutput(raw) {
|
|
501
566
|
const trimmed = raw.trim();
|
|
502
567
|
const lastBrace = trimmed.lastIndexOf("}");
|
|
@@ -523,6 +588,15 @@ function parseClaudeOutput(raw) {
|
|
|
523
588
|
if (typeof parsed["session_id"] === "string") {
|
|
524
589
|
result.sessionId = parsed["session_id"];
|
|
525
590
|
}
|
|
591
|
+
if (typeof parsed["is_error"] === "boolean") {
|
|
592
|
+
result.isError = parsed["is_error"];
|
|
593
|
+
}
|
|
594
|
+
if (typeof parsed["result"] === "string") {
|
|
595
|
+
result.resultText = parsed["result"];
|
|
596
|
+
}
|
|
597
|
+
if (typeof parsed["api_error_status"] === "number") {
|
|
598
|
+
result.apiErrorStatus = parsed["api_error_status"];
|
|
599
|
+
}
|
|
526
600
|
const usage = parsed["usage"];
|
|
527
601
|
if (usage && typeof usage === "object") {
|
|
528
602
|
const u = usage;
|
|
@@ -534,6 +608,69 @@ function parseClaudeOutput(raw) {
|
|
|
534
608
|
return {};
|
|
535
609
|
}
|
|
536
610
|
}
|
|
611
|
+
function classifyFailure(stderr, resultText, apiErrorStatus) {
|
|
612
|
+
const haystack = `${stderr}
|
|
613
|
+
${resultText ?? ""}`.toLowerCase();
|
|
614
|
+
if (apiErrorStatus === 429 || apiErrorStatus === 529 || /rate.?limit|overloaded|too many requests|usage limit|quota|exhausted/.test(haystack)) {
|
|
615
|
+
return "rate_limited";
|
|
616
|
+
}
|
|
617
|
+
if (/enoent|not recognized|command not found|login|authenticate|credentials|api key/.test(haystack)) {
|
|
618
|
+
return "environment";
|
|
619
|
+
}
|
|
620
|
+
return "error";
|
|
621
|
+
}
|
|
622
|
+
var HANDOFF_TIMEOUT_MS = 4 * 60 * 1e3;
|
|
623
|
+
async function runHandoffTurn(claude, agentWorkspace, settingsPath, model, oldSessionId) {
|
|
624
|
+
const rawArgs = [
|
|
625
|
+
"-p",
|
|
626
|
+
"--output-format",
|
|
627
|
+
"json",
|
|
628
|
+
"--max-turns",
|
|
629
|
+
"6",
|
|
630
|
+
"--model",
|
|
631
|
+
model,
|
|
632
|
+
"--settings",
|
|
633
|
+
settingsPath,
|
|
634
|
+
"--resume",
|
|
635
|
+
oldSessionId
|
|
636
|
+
];
|
|
637
|
+
const args = claude.shell ? rawArgs.map((a) => /[\s"^&|<>%]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a) : rawArgs;
|
|
638
|
+
await new Promise((resolve) => {
|
|
639
|
+
let child;
|
|
640
|
+
try {
|
|
641
|
+
child = spawn(claude.cmd, args, {
|
|
642
|
+
cwd: agentWorkspace,
|
|
643
|
+
env: { ...process.env },
|
|
644
|
+
shell: claude.shell,
|
|
645
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
646
|
+
});
|
|
647
|
+
child.stdin?.write(
|
|
648
|
+
"Your session is about to be rotated (context limit). Write or overwrite HANDOFF.md in your workspace root RIGHT NOW: what you were working on, current state, decisions in flight, and the immediate next step. Max 60 lines. Do nothing else."
|
|
649
|
+
);
|
|
650
|
+
child.stdin?.end();
|
|
651
|
+
} catch (err) {
|
|
652
|
+
logWarn(`Handoff turn failed to spawn: ${err instanceof Error ? err.message : String(err)}`);
|
|
653
|
+
resolve();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const timer = setTimeout(() => {
|
|
657
|
+
logWarn("Handoff turn timed out \u2014 rotating without handoff doc");
|
|
658
|
+
try {
|
|
659
|
+
child.kill("SIGTERM");
|
|
660
|
+
} catch {
|
|
661
|
+
}
|
|
662
|
+
}, HANDOFF_TIMEOUT_MS);
|
|
663
|
+
child.on("close", () => {
|
|
664
|
+
clearTimeout(timer);
|
|
665
|
+
log("Handoff turn complete");
|
|
666
|
+
resolve();
|
|
667
|
+
});
|
|
668
|
+
child.on("error", () => {
|
|
669
|
+
clearTimeout(timer);
|
|
670
|
+
resolve();
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
}
|
|
537
674
|
async function runWorkItem(config, work) {
|
|
538
675
|
const agent = findAgent(work.agent.id);
|
|
539
676
|
const model = agent?.model ?? work.agent.model;
|
|
@@ -548,8 +685,8 @@ async function runWorkItem(config, work) {
|
|
|
548
685
|
}
|
|
549
686
|
log(`Starting run ${work.runId} (agent "${work.agent.name}", model "${model}")`);
|
|
550
687
|
log(` cwd: ${cwd}`);
|
|
551
|
-
const
|
|
552
|
-
const settingsPath = join(agentWorkspace, ".claude", "settings.json");
|
|
688
|
+
const claude = resolveClaudeCommand();
|
|
689
|
+
const settingsPath = join(agentWorkspace, ".claude", "settings.json").replace(/\\/g, "/");
|
|
553
690
|
const rawArgs = [
|
|
554
691
|
"-p",
|
|
555
692
|
"--output-format",
|
|
@@ -563,17 +700,26 @@ async function runWorkItem(config, work) {
|
|
|
563
700
|
];
|
|
564
701
|
const persistent = work.agent.localSessionMode === "persistent";
|
|
565
702
|
let sessionState = loadSessionState(work.agent.id);
|
|
703
|
+
let rotated = false;
|
|
566
704
|
if (persistent) {
|
|
567
705
|
if (shouldRotateSession(sessionState, Date.now())) {
|
|
568
706
|
if (sessionState.sessionId) {
|
|
569
707
|
log(`Rotating session for agent "${work.agent.name}" (age/run limit reached)`);
|
|
708
|
+
await runHandoffTurn(claude, agentWorkspace, settingsPath, model, sessionState.sessionId);
|
|
709
|
+
rotated = true;
|
|
570
710
|
}
|
|
571
711
|
sessionState = { sessionId: null, sessionStartedAt: null, runCount: 0 };
|
|
572
712
|
} else if (sessionState.sessionId) {
|
|
573
713
|
rawArgs.push("--resume", sessionState.sessionId);
|
|
574
714
|
}
|
|
575
715
|
}
|
|
576
|
-
const args =
|
|
716
|
+
const args = claude.shell ? rawArgs.map((a) => /[\s"^&|<>%]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a) : rawArgs;
|
|
717
|
+
const promptText = rotated ? `## Session rotated
|
|
718
|
+
Your previous session ended (context rotation). Read HANDOFF.md and your recent memory/ journal entries before starting.
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
${work.prompt}` : work.prompt;
|
|
577
723
|
const env = { ...process.env };
|
|
578
724
|
let stdoutBuf = "";
|
|
579
725
|
let stderrBuf = "";
|
|
@@ -582,16 +728,16 @@ async function runWorkItem(config, work) {
|
|
|
582
728
|
await new Promise((resolve) => {
|
|
583
729
|
let child;
|
|
584
730
|
try {
|
|
585
|
-
child = spawn(
|
|
731
|
+
child = spawn(claude.cmd, args, {
|
|
586
732
|
cwd,
|
|
587
733
|
env,
|
|
588
|
-
// shell
|
|
589
|
-
//
|
|
590
|
-
shell:
|
|
734
|
+
// shell only as a last resort (Windows .cmd shim). Safe because argv
|
|
735
|
+
// contains only our fixed, pre-quoted tokens.
|
|
736
|
+
shell: claude.shell,
|
|
591
737
|
// stdin: pipe — the prompt is delivered via stdin, not argv.
|
|
592
738
|
stdio: ["pipe", "pipe", "pipe"]
|
|
593
739
|
});
|
|
594
|
-
child.stdin?.write(
|
|
740
|
+
child.stdin?.write(promptText);
|
|
595
741
|
child.stdin?.end();
|
|
596
742
|
} catch (err) {
|
|
597
743
|
spawnError = err instanceof Error ? err.message : String(err);
|
|
@@ -630,17 +776,26 @@ async function runWorkItem(config, work) {
|
|
|
630
776
|
});
|
|
631
777
|
const stdoutExcerpt = stdoutBuf.length > STDOUT_CAP ? stdoutBuf.slice(-STDOUT_CAP) : stdoutBuf;
|
|
632
778
|
const stderrExcerpt = stderrBuf.length > STDERR_CAP ? stderrBuf.slice(-STDERR_CAP) : stderrBuf;
|
|
633
|
-
const
|
|
634
|
-
const
|
|
779
|
+
const parsed = parseClaudeOutput(stdoutBuf);
|
|
780
|
+
const succeeded = spawnError === void 0 && exitCode === 0 && parsed.isError !== true;
|
|
781
|
+
const errorMessage = succeeded ? void 0 : spawnError ?? (parsed.isError ? parsed.resultText?.slice(0, 1e3) : void 0) ?? (stderrExcerpt.trim() ? stderrExcerpt.trim().slice(-1e3) : void 0) ?? `claude exited with code ${exitCode ?? "unknown"}`;
|
|
782
|
+
const failureKind = succeeded ? void 0 : classifyFailure(stderrExcerpt, parsed.resultText, parsed.apiErrorStatus);
|
|
783
|
+
const {
|
|
784
|
+
isError: _isError,
|
|
785
|
+
resultText: _resultText,
|
|
786
|
+
apiErrorStatus: _apiErrorStatus,
|
|
787
|
+
...reportFields
|
|
788
|
+
} = parsed;
|
|
635
789
|
const report = {
|
|
636
790
|
runId: work.runId,
|
|
637
791
|
wakeupId: work.wakeupId,
|
|
638
792
|
status: succeeded ? "completed" : "failed",
|
|
793
|
+
failureKind,
|
|
639
794
|
stdoutExcerpt,
|
|
640
795
|
stderrExcerpt,
|
|
641
|
-
error:
|
|
796
|
+
error: errorMessage,
|
|
642
797
|
exitCode,
|
|
643
|
-
...
|
|
798
|
+
...reportFields
|
|
644
799
|
};
|
|
645
800
|
log(
|
|
646
801
|
`Run ${work.runId} ${report.status} (exit ${exitCode ?? "n/a"}${parsed.costUsd !== void 0 ? `, cost $${parsed.costUsd.toFixed(4)}` : ""})`
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts","../src/pair.ts","../src/api.ts","../src/hooks-writer.ts","../src/agents.ts","../src/session.ts","../src/runner.ts","../src/quota.ts","../src/poller.ts","../src/cli.ts"],"names":["mkdirSync","join","writeFileSync","existsSync","platform","release","sleep","homedir"],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * Daemon version — keep in sync with package.json.\n * Single source of truth used in heartbeats, pair requests, and CLI output.\n */\nexport const VERSION = \"0.1.0\"\n","/**\n * pair.ts — Pair this machine with a StrideOps workspace via a pairing code.\n *\n * Sends machine info to /api/bridge/v1/pair and saves the returned\n * credentials to ~/.stride-bridge/config.json.\n */\n\nimport { hostname, platform, arch, cpus, totalmem, release } from \"node:os\"\nimport { VERSION } from \"./version.js\"\nimport { writeConfig } from \"./config.js\"\nimport { log } from \"./log.js\"\n\ninterface PairRequest {\n pairingCode: string\n daemonVersion: string\n machineInfo: {\n hostname: string\n platform: string\n arch: string\n cpus: number\n totalMemMb: number\n }\n osInfo: {\n platform: string\n release: string\n }\n}\n\ninterface PairResponseData {\n bridgeId: string\n orgId: string\n name: string\n authMode: \"claude_login\" | \"org_api_key\"\n bridgeToken: string\n}\n\ninterface RawPairResponse {\n success: boolean\n data?: PairResponseData\n error?: string\n}\n\n/**\n * Pair with the StrideOps server using a one-time pairing code.\n * On success, saves config and prints a confirmation.\n */\nexport async function runPair(\n pairingCode: string,\n apiBaseUrl: string,\n): Promise<void> {\n const url = `${apiBaseUrl.replace(/\\/$/, \"\")}/api/bridge/v1/pair`\n\n log(`Pairing with ${apiBaseUrl} ...`)\n\n const cpuCount = cpus().length\n const totalMemMb = Math.round(totalmem() / 1024 / 1024)\n\n const payload: PairRequest = {\n pairingCode,\n daemonVersion: VERSION,\n machineInfo: {\n hostname: hostname(),\n platform: platform(),\n arch: arch(),\n cpus: cpuCount,\n totalMemMb,\n },\n osInfo: {\n platform: platform(),\n release: release(),\n },\n }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 15_000)\n\n let res: Response\n try {\n res = await fetch(url, {\n method: \"POST\",\n signal: controller.signal,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n })\n } catch (err: unknown) {\n clearTimeout(timer)\n if (err instanceof Error && err.name === \"AbortError\") {\n console.error(\"[stride-bridge] Pairing request timed out. Check your connection.\")\n process.exit(1)\n }\n console.error(\n `[stride-bridge] Network error during pairing: ${err instanceof Error ? err.message : String(err)}`,\n )\n process.exit(1)\n } finally {\n clearTimeout(timer)\n }\n\n let body: RawPairResponse\n try {\n body = (await res.json()) as RawPairResponse\n } catch {\n console.error(\n `[stride-bridge] Failed to parse pairing response (HTTP ${res.status}).`,\n )\n process.exit(1)\n }\n\n if (!res.ok || !body.success || !body.data) {\n console.error(\n `[stride-bridge] Pairing failed: ${body.error ?? `HTTP ${res.status}`}`,\n )\n process.exit(1)\n }\n\n const { bridgeId, orgId, name, authMode, bridgeToken } = body.data\n\n writeConfig({\n apiBaseUrl,\n bridgeToken,\n bridgeId,\n orgId,\n bridgeName: name,\n authMode,\n })\n\n console.log(`[stride-bridge] Connected successfully!`)\n console.log(` Bridge name : ${name}`)\n console.log(` Bridge ID : ${bridgeId}`)\n console.log(` Org ID : ${orgId}`)\n console.log(` Auth mode : ${authMode}`)\n console.log(` Config : ~/.stride-bridge/config.json`)\n console.log()\n console.log(`Run \\`stride-bridge start\\` to begin accepting work.`)\n}\n","/**\n * api.ts — Minimal authenticated HTTP client for the Bridge API.\n *\n * Uses the global fetch (Node >= 18) + AbortController for timeouts.\n * All responses are expected to be { success: boolean, data?: T, error?: string }.\n * Throws on network errors or when success === false.\n */\n\nimport type { BridgeConfig } from \"./config.js\"\n\nconst REQUEST_TIMEOUT_MS = 10_000\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number | null,\n message: string,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport interface ApiResponse<T> {\n success: boolean\n data?: T\n error?: string\n}\n\n/**\n * Make an authenticated fetch to the Bridge API.\n * Adds Authorization: Bearer header from config.\n * Throws ApiError on non-2xx HTTP status or success:false response.\n */\nexport async function authedFetch<T>(\n config: BridgeConfig,\n path: string,\n init: RequestInit = {},\n): Promise<T> {\n const url = `${config.apiBaseUrl.replace(/\\/$/, \"\")}${path}`\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)\n\n let res: Response\n try {\n res = await fetch(url, {\n ...init,\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.bridgeToken}`,\n ...(init.headers as Record<string, string> | undefined),\n },\n })\n } catch (err: unknown) {\n clearTimeout(timer)\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new ApiError(null, `Request timed out after ${REQUEST_TIMEOUT_MS}ms: ${path}`)\n }\n throw new ApiError(null, `Network error: ${err instanceof Error ? err.message : String(err)}`)\n } finally {\n clearTimeout(timer)\n }\n\n let body: ApiResponse<T>\n try {\n body = (await res.json()) as ApiResponse<T>\n } catch {\n throw new ApiError(res.status, `Failed to parse JSON response from ${path} (HTTP ${res.status})`)\n }\n\n if (!res.ok || !body.success) {\n throw new ApiError(\n res.status,\n body.error ?? `API error from ${path} (HTTP ${res.status})`,\n )\n }\n\n return body.data as T\n}\n\n/**\n * POST JSON to a path. Convenience wrapper over authedFetch.\n */\nexport async function post<T>(\n config: BridgeConfig,\n path: string,\n body: unknown,\n): Promise<T> {\n return authedFetch<T>(config, path, {\n method: \"POST\",\n body: JSON.stringify(body),\n })\n}\n\n/**\n * GET a path. Convenience wrapper over authedFetch.\n */\nexport async function get<T>(config: BridgeConfig, path: string): Promise<T> {\n return authedFetch<T>(config, path, { method: \"GET\" })\n}\n\n/**\n * Like post() but swallows all errors (for best-effort fire-and-forget calls\n * such as daemon_stopping events).\n */\nexport async function postBestEffort(\n config: BridgeConfig,\n path: string,\n body: unknown,\n): Promise<void> {\n try {\n await post(config, path, body)\n } catch {\n // Intentionally swallowed — best-effort only.\n }\n}\n","/**\n * hooks-writer.ts — Generate .claude/settings.json with a signed PreToolUse hook.\n *\n * The signing scheme mirrors hook-signature.ts on the server:\n * HMAC-SHA256(`${unixSeconds}\\n${rawBody}`) → hex\n * with headers x-stride-hook-sig and x-stride-hook-ts.\n *\n * For local agents we generate a small Node.js script (hook-runner.mjs) that\n * Claude Code invokes as the hook command. This avoids bash dependency on\n * Windows and is consistent across platforms.\n *\n * The runner transforms Claude Code's hook input ({tool_name, tool_input,\n * session_id}) into the server's PreToolUse schema ({tool, args, sessionId})\n * and FORWARDS the server's decision — a budget/loop denial from Stride must\n * actually block the tool call. Fail-open on any error: if Stride is\n * unreachable the hook allows, so the agent is never blocked by an outage.\n *\n * Phase 1 wires PreToolUse only. Claude Code's Stop hook input carries no\n * usage/cost data, so per-turn usage reporting is deferred to Phase 2\n * (persistent sessions); run-level cost is reported by runner.ts via\n * POST /api/bridge/v1/runs/{runId} instead.\n */\n\nimport { join } from \"node:path\"\nimport { mkdirSync, writeFileSync, chmodSync } from \"node:fs\"\nimport { writeJsonAtomic } from \"./state.js\"\n\nexport interface HookWriterOptions {\n agentId: string\n hookSecret: string\n apiBaseUrl: string\n workspaceDir: string\n}\n\n/**\n * Generate the Node.js hook runner script content.\n * This script is written once per agent to their .claude/ directory.\n * It reads CLAUDE_HOOK_INPUT from stdin (Claude Code provides it),\n * computes the HMAC signature, and POSTs to the given URL.\n *\n * The script exits 0 (allow) regardless of Stride's response,\n * so the agent is never blocked by an unreachable server.\n */\nfunction buildHookRunnerScript(\n preToolUseUrl: string,\n hookSecret: string,\n): string {\n // We embed the secret directly into the per-agent script.\n // The script lives in the agent's workspace dir (~/.stride-bridge/agents/<id>/)\n // which is chmod 700 (POSIX) — not in the project directory Claude edits.\n // The secret is never printed to stdout/stderr.\n return `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Signed PreToolUse hook runner for Stride Build agents.\n// HMAC scheme: HMAC-SHA256(timestamp+\"\\\\n\"+body) -> hex\n// Headers: x-stride-hook-ts, x-stride-hook-sig\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst PRE_TOOL_USE_URL = ${JSON.stringify(preToolUseUrl)};\nconst TIMEOUT_MS = 8_000;\n\nfunction allow() {\n // No output + exit 0 = allow in Claude Code's hook protocol.\n process.exit(0);\n}\n\nasync function main() {\n // Claude Code writes the hook input to stdin as JSON. fd 0 works on\n // Windows too (\"/dev/stdin\" does not).\n let input = {};\n try {\n input = JSON.parse(readFileSync(0, \"utf-8\"));\n } catch {\n allow(); // unreadable input — fail-open\n }\n\n // Transform Claude Code's hook input into the server's PreToolUse schema.\n const body = JSON.stringify({\n tool: typeof input.tool_name === \"string\" && input.tool_name ? input.tool_name : \"unknown\",\n args: typeof input.tool_input === \"object\" && input.tool_input !== null ? input.tool_input : undefined,\n sessionId: typeof input.session_id === \"string\" ? input.session_id : undefined,\n });\n\n const ts = Math.floor(Date.now() / 1000).toString();\n const sig = createHmac(\"sha256\", HOOK_SECRET)\n .update(ts + \"\\\\n\" + body)\n .digest(\"hex\");\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);\n const res = await fetch(PRE_TOOL_USE_URL, {\n method: \"POST\",\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n });\n clearTimeout(timer);\n\n const decision = await res.json();\n if (decision && (decision.permissionDecision === \"deny\" || decision.permissionDecision === \"ask\")) {\n // Forward Stride's decision so Claude Code actually blocks the call.\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: decision.permissionDecision,\n permissionDecisionReason: decision.permissionDecisionReason || \"Blocked by Stride policy\",\n },\n }) + \"\\\\n\");\n }\n process.exit(0);\n}\n\n// Fail-open: an unreachable server or unexpected error must never block the agent.\nmain().catch(() => allow());\n`\n}\n\n/**\n * Write .claude/settings.json and the hook-runner.mjs script to the agent's\n * workspace directory.\n *\n * settings.json wires PreToolUse (all tools, matcher \".*\") to the\n * hook-runner.mjs script via `node <path>`.\n */\nexport function writeAgentHooks(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const preToolUseUrl = `${base}/api/internal/build/agents/${agentId}/hook/pre-tool-use`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n\n // Write the hook runner script\n const runnerPath = join(claudeDir, \"hook-runner.mjs\")\n const runnerContent = buildHookRunnerScript(preToolUseUrl, hookSecret)\n writeFileSync(runnerPath, runnerContent, { encoding: \"utf-8\", mode: 0o700 })\n\n // Make executable on POSIX\n if (process.platform !== \"win32\") {\n try {\n chmodSync(runnerPath, 0o700)\n } catch {\n // Best effort.\n }\n }\n\n // Build the command that Claude Code will invoke.\n // Use `node <absolute-path>` so it works regardless of PATH. Quote both\n // paths — they regularly contain spaces on Windows (C:\\Users\\First Last\\...).\n const preToolUseCmd = `\"${process.execPath}\" \"${runnerPath}\"`\n\n const settingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: \".*\",\n hooks: [\n {\n type: \"command\",\n command: preToolUseCmd,\n timeout: 10,\n },\n ],\n },\n ],\n },\n }\n\n writeJsonAtomic(join(claudeDir, \"settings.json\"), settingsJson)\n}\n\n/**\n * Write .claude/memory-sync.mjs — a Node script the agent runs (per its\n * CLAUDE.md memory protocol) to upload MEMORY.md and today's daily journal\n * to Stride's HMAC-authenticated memory-sync endpoint. Node instead of the\n * server's bash snippet because local agents run on Windows too.\n */\nexport function writeMemorySyncScript(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const syncUrl = `${base}/api/internal/build/agents/${agentId}/memory/sync`\n\n const script = `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Uploads MEMORY.md + today's daily journal to Stride (HMAC-signed).\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst SYNC_URL = ${JSON.stringify(syncUrl)};\nconst workspace = dirname(dirname(fileURLToPath(import.meta.url)));\n\nconst files = [];\nconst memoryMd = join(workspace, \"MEMORY.md\");\nif (existsSync(memoryMd)) {\n files.push({ type: \"learnings\", content: readFileSync(memoryMd, \"utf-8\").slice(0, 50000) });\n}\nconst today = new Date().toISOString().slice(0, 10);\nconst daily = join(workspace, \"memory\", today + \".md\");\nif (existsSync(daily)) {\n files.push({ type: \"daily\", date: today, content: readFileSync(daily, \"utf-8\").slice(0, 50000) });\n}\n\nif (files.length === 0) {\n console.log(\"memory-sync: nothing to sync\");\n process.exit(0);\n}\n\nconst body = JSON.stringify({ files });\nconst ts = Math.floor(Date.now() / 1000).toString();\nconst sig = createHmac(\"sha256\", HOOK_SECRET).update(ts + \"\\\\n\" + body).digest(\"hex\");\n\nconst res = await fetch(SYNC_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n}).catch((err) => ({ ok: false, statusText: String(err) }));\n\nconsole.log(res.ok ? \\`memory-sync: synced \\${files.length} file(s)\\` : \\`memory-sync: failed (\\${res.statusText ?? res.status})\\`);\n`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n writeFileSync(join(claudeDir, \"memory-sync.mjs\"), script, { encoding: \"utf-8\", mode: 0o700 })\n}\n\n/**\n * Write .claude/identity-sync.mjs — uploads the agent's identity files\n * (SOUL.md, IDENTITY.md, USER.md, HEARTBEAT.md, optional goals.json with\n * {goals: string[]}) to Stride's identity endpoint and marks onboarding\n * complete. The agent runs this once, at the end of its first-boot interview.\n */\nexport function writeIdentitySyncScript(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const identityUrl = `${base}/api/internal/build/agents/${agentId}/identity`\n\n const script = `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Uploads identity files + goals to Stride and marks onboarding complete.\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst IDENTITY_URL = ${JSON.stringify(identityUrl)};\nconst workspace = dirname(dirname(fileURLToPath(import.meta.url)));\n\nfunction readIf(name) {\n const p = join(workspace, name);\n return existsSync(p) ? readFileSync(p, \"utf-8\").slice(0, 20000) : undefined;\n}\n\nconst payload = {\n soulMd: readIf(\"SOUL.md\"),\n identityMd: readIf(\"IDENTITY.md\"),\n userMd: readIf(\"USER.md\"),\n heartbeatChecklist: readIf(\"HEARTBEAT.md\"),\n onboardingComplete: true,\n};\nconst goalsRaw = readIf(\"goals.json\");\nif (goalsRaw) {\n try {\n const parsed = JSON.parse(goalsRaw);\n if (Array.isArray(parsed.goals)) payload.goals = parsed.goals.slice(0, 5);\n } catch { /* ignore malformed goals.json */ }\n}\n\nconst body = JSON.stringify(payload);\nconst ts = Math.floor(Date.now() / 1000).toString();\nconst sig = createHmac(\"sha256\", HOOK_SECRET).update(ts + \"\\\\n\" + body).digest(\"hex\");\n\nconst res = await fetch(IDENTITY_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n}).catch((err) => ({ ok: false, statusText: String(err) }));\n\nconsole.log(res.ok ? \"identity-sync: onboarding recorded in Stride\" : \\`identity-sync: failed (\\${res.statusText ?? res.status})\\`);\n`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n writeFileSync(join(claudeDir, \"identity-sync.mjs\"), script, { encoding: \"utf-8\", mode: 0o700 })\n}\n","/**\n * agents.ts — Fetch agent configs from Stride and provision local workspaces.\n *\n * At startup and every 5 minutes, fetches the list of agents assigned to this\n * bridge. For each agent, ensures:\n * ~/.stride-bridge/agents/<agentId>/workspace/\n * ~/.stride-bridge/agents/<agentId>/workspace/CLAUDE.md\n * ~/.stride-bridge/agents/<agentId>/workspace/.claude/settings.json\n * ~/.stride-bridge/agents/<agentId>/workspace/.claude/hook-runner.mjs\n */\n\nimport { join } from \"node:path\"\nimport { mkdirSync, writeFileSync, existsSync } from \"node:fs\"\nimport { get } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport { AGENTS_DIR } from \"./config.js\"\nimport {\n writeAgentHooks,\n writeMemorySyncScript,\n writeIdentitySyncScript,\n} from \"./hooks-writer.js\"\nimport { log, logError } from \"./log.js\"\n\nexport interface BridgeAgent {\n id: string\n name: string\n status: string\n model: string\n systemPrompt: string\n soulMd?: string | null\n allowedTools: string[]\n maxBudgetUsd: number\n localSessionMode: string\n hookSecret: string\n // Phase 3 identity + memory layer (server-rendered templates)\n identityMd?: string | null\n userMd?: string | null\n heartbeatChecklist?: string | null\n memoryProtocolMd?: string | null\n defaultSoulMd?: string | null\n // Phase 4 first-boot onboarding\n onboardedAt?: string | null\n onboardingMd?: string | null\n}\n\ninterface AgentsResponseData {\n agents: BridgeAgent[]\n}\n\n// In-memory cache of agent configs, refreshed periodically.\nlet cachedAgents: BridgeAgent[] = []\nconst REFRESH_INTERVAL_MS = 5 * 60 * 1000 // 5 minutes\nlet refreshTimer: ReturnType<typeof setInterval> | null = null\n\n/**\n * Fetch agent list from server and provision local workspaces.\n * Errors are caught and logged — a refresh failure must not crash the daemon.\n */\nexport async function refreshAgents(config: BridgeConfig): Promise<void> {\n try {\n const data = await get<AgentsResponseData>(config, \"/api/bridge/v1/agents\")\n cachedAgents = data.agents ?? []\n log(`Refreshed ${cachedAgents.length} agent(s)`)\n for (const agent of cachedAgents) {\n await ensureAgentWorkspace(config, agent)\n }\n } catch (err: unknown) {\n logError(\"Failed to refresh agents\", err)\n }\n}\n\n/**\n * Return the cached agent list (may be stale if refresh failed).\n */\nexport function getCachedAgents(): BridgeAgent[] {\n return cachedAgents\n}\n\n/**\n * Find a cached agent by ID.\n */\nexport function findAgent(agentId: string): BridgeAgent | undefined {\n return cachedAgents.find((a) => a.id === agentId)\n}\n\n/**\n * Ensure the local workspace directory exists for an agent, writing\n * CLAUDE.md and .claude/settings.json (with signed hooks).\n */\nasync function ensureAgentWorkspace(\n config: BridgeConfig,\n agent: BridgeAgent,\n): Promise<void> {\n const workspaceDir = agentWorkspaceDir(agent.id)\n mkdirSync(workspaceDir, { recursive: true })\n // Daily-journal directory (Layer 1 memory) — agent-written, never clobbered.\n mkdirSync(join(workspaceDir, \"memory\"), { recursive: true })\n\n // CLAUDE.md is fully generated — safe to overwrite on every refresh.\n const sections: string[] = []\n if (agent.systemPrompt) sections.push(agent.systemPrompt)\n sections.push(agent.soulMd ?? agent.defaultSoulMd ?? \"\")\n if (agent.memoryProtocolMd) sections.push(agent.memoryProtocolMd)\n sections.push(\n \"## Memory sync (local agent)\\n\\n\" +\n \"After updating MEMORY.md or today's daily journal, sync them to Stride by running:\\n\\n\" +\n \"```\\nnode .claude/memory-sync.mjs\\n```\\n\",\n )\n if (!agent.onboardedAt && agent.onboardingMd) {\n sections.push(\n \"## FIRST BOOT — onboarding required\\n\\n\" +\n \"You have not been onboarded. Read ONBOARDING.md in this workspace and \" +\n \"complete the interview BEFORE regular work. To sync your identity after \" +\n \"the interview, run:\\n\\n```\\nnode .claude/identity-sync.mjs\\n```\\n\",\n )\n }\n writeFileSync(join(workspaceDir, \"CLAUDE.md\"), sections.filter(Boolean).join(\"\\n\\n\"), \"utf-8\")\n\n // ONBOARDING.md present only until the agent completes its interview.\n if (!agent.onboardedAt && agent.onboardingMd) {\n writeFileSync(join(workspaceDir, \"ONBOARDING.md\"), agent.onboardingMd, \"utf-8\")\n }\n\n // Identity files: server-rendered, overwrite (dashboard is source of truth).\n if (agent.identityMd) writeFileSync(join(workspaceDir, \"IDENTITY.md\"), agent.identityMd, \"utf-8\")\n if (agent.userMd) writeFileSync(join(workspaceDir, \"USER.md\"), agent.userMd, \"utf-8\")\n if (agent.heartbeatChecklist) {\n writeFileSync(join(workspaceDir, \"HEARTBEAT.md\"), agent.heartbeatChecklist, \"utf-8\")\n }\n\n // MEMORY.md is AGENT-written (Layer 2) — scaffold once, never overwrite.\n const memoryMdPath = join(workspaceDir, \"MEMORY.md\")\n if (!existsSync(memoryMdPath)) {\n writeFileSync(\n memoryMdPath,\n \"# Long-term memory\\n\\nDurable learnings only — format: `## Topic — YYYY-MM-DD` followed by what you learned.\\n\",\n \"utf-8\",\n )\n }\n\n // .claude/settings.json with HMAC-signed PreToolUse hook\n writeAgentHooks({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n // .claude/memory-sync.mjs — signed memory uploader (node, Windows-safe)\n writeMemorySyncScript({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n // .claude/identity-sync.mjs — uploads identity files after onboarding\n writeIdentitySyncScript({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n log(`Provisioned workspace for agent \"${agent.name}\" (${agent.id})`)\n}\n\n/**\n * Return the workspace directory path for an agent.\n */\nexport function agentWorkspaceDir(agentId: string): string {\n return join(AGENTS_DIR, agentId, \"workspace\")\n}\n\n/**\n * Start the periodic agent refresh timer. Call once from the daemon loop.\n * Cleans up the timer when the process exits.\n */\nexport function startAgentRefreshTimer(config: BridgeConfig): void {\n if (refreshTimer !== null) return // already running\n\n refreshTimer = setInterval(() => {\n void refreshAgents(config)\n }, REFRESH_INTERVAL_MS)\n\n // Do not hold the event loop open if all other work is done.\n if (refreshTimer.unref) {\n refreshTimer.unref()\n }\n}\n\n/**\n * Stop the agent refresh timer.\n */\nexport function stopAgentRefreshTimer(): void {\n if (refreshTimer !== null) {\n clearInterval(refreshTimer)\n refreshTimer = null\n }\n}\n","/**\n * session.ts — Persistent-session state for local agents (Phase 2).\n *\n * Agents with localSessionMode === \"persistent\" carry conversation context\n * across work items via `claude -p --resume <sessionId>`. We track the\n * session per agent in agents/<id>/session.json and rotate (start fresh)\n * when the session gets old or has accumulated many runs — the headless\n * equivalent of cortex-os's 71-hour context rotation, defaulting lower\n * because -p turns are denser than interactive chat.\n */\n\nimport { join, dirname } from \"node:path\"\nimport { agentWorkspaceDir } from \"./agents.js\"\nimport { readJsonSafe, writeJsonAtomic } from \"./state.js\"\n\nexport interface AgentSessionState {\n sessionId: string | null\n sessionStartedAt: number | null\n runCount: number\n}\n\nexport interface RotationOptions {\n /** Max session age before rotation. Default 24h. */\n maxAgeMs?: number\n /** Max work items in one session before rotation. Default 40. */\n maxRuns?: number\n}\n\nconst EMPTY_STATE: AgentSessionState = {\n sessionId: null,\n sessionStartedAt: null,\n runCount: 0,\n}\n\n/**\n * Pure rotation decision: start a fresh session when there is no usable\n * session, the session is too old, or it has absorbed too many runs.\n */\nexport function shouldRotateSession(\n state: AgentSessionState,\n nowMs: number,\n opts: RotationOptions = {},\n): boolean {\n const { maxAgeMs = 24 * 60 * 60_000, maxRuns = 40 } = opts\n if (!state.sessionId || state.sessionStartedAt === null) return true\n if (nowMs - state.sessionStartedAt >= maxAgeMs) return true\n if (state.runCount >= maxRuns) return true\n return false\n}\n\nfunction sessionStatePath(agentId: string): string {\n // agents/<id>/session.json — sibling of the workspace dir\n return join(dirname(agentWorkspaceDir(agentId)), \"session.json\")\n}\n\nexport function loadSessionState(agentId: string): AgentSessionState {\n const parsed = readJsonSafe<Partial<AgentSessionState>>(sessionStatePath(agentId))\n if (!parsed) return { ...EMPTY_STATE }\n return {\n sessionId: typeof parsed.sessionId === \"string\" ? parsed.sessionId : null,\n sessionStartedAt:\n typeof parsed.sessionStartedAt === \"number\" ? parsed.sessionStartedAt : null,\n runCount: typeof parsed.runCount === \"number\" ? parsed.runCount : 0,\n }\n}\n\nexport function saveSessionState(agentId: string, state: AgentSessionState): void {\n writeJsonAtomic(sessionStatePath(agentId), state)\n}\n\n/**\n * Record a completed run's session id. Continuing an existing session bumps\n * runCount; a new session id resets the clock.\n */\nexport function recordRunSession(\n agentId: string,\n newSessionId: string | undefined,\n previous: AgentSessionState,\n nowMs: number,\n): AgentSessionState {\n if (!newSessionId) return previous\n const continued = previous.sessionId === newSessionId\n const next: AgentSessionState = continued\n ? { ...previous, runCount: previous.runCount + 1 }\n : { sessionId: newSessionId, sessionStartedAt: nowMs, runCount: 1 }\n saveSessionState(agentId, next)\n return next\n}\n","/**\n * runner.ts — Execute a single work item via Claude Code CLI.\n *\n * Spawns `claude -p --output-format json --max-turns 25 --model <model>\n * --settings <workspace>/.claude/settings.json` and writes the PROMPT VIA\n * STDIN. Two reasons stdin matters:\n * 1. On Windows `claude` is a .cmd shim, which requires shell:true — and\n * with a shell, an arbitrary prompt in argv would be an injection risk.\n * Via stdin the prompt never touches the shell; the remaining argv\n * tokens are fixed strings (model ids, paths) we quote ourselves.\n * 2. Long prompts can exceed Windows' command-line length limit.\n *\n * --settings always points at the agent workspace so the Stride PreToolUse\n * hook applies even when cwd is a project folder elsewhere on disk.\n *\n * Timeout: 15 minutes (SIGTERM then SIGKILL).\n * Phase 1: authMode is always \"claude_login\" (uses local OAuth session).\n * TODO Phase 2: inject ANTHROPIC_API_KEY when authMode === \"org_api_key\".\n */\n\nimport { spawn } from \"node:child_process\"\nimport { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { post } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport { PENDING_REPORTS_PATH } from \"./config.js\"\nimport { agentWorkspaceDir, findAgent } from \"./agents.js\"\nimport { readJsonSafe, writeJsonAtomic } from \"./state.js\"\nimport {\n loadSessionState,\n shouldRotateSession,\n recordRunSession,\n} from \"./session.js\"\nimport { log, logError, logWarn } from \"./log.js\"\n\nconst RUN_TIMEOUT_MS = 15 * 60 * 1000 // 15 minutes\nconst STDOUT_CAP = 10_000 // chars\nconst STDERR_CAP = 4_000 // chars\nconst REPORT_RETRY_COUNT = 3\nconst REPORT_RETRY_BASE_MS = 2_000\n\nexport interface WorkItem {\n wakeupId: string\n runId: string\n issueId?: string\n prompt: string\n workingDir?: string\n agent: {\n id: string\n name: string\n model: string\n allowedTools: string[]\n maxBudgetUsd: number\n localSessionMode: string\n }\n}\n\ninterface RunReport {\n runId: string\n wakeupId: string\n status: \"completed\" | \"failed\"\n stdoutExcerpt: string\n stderrExcerpt: string\n error?: string\n exitCode: number | null\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n}\n\n/**\n * Parse the JSON output emitted by `claude -p --output-format json`.\n * Defensive: all fields are optional.\n */\nfunction parseClaudeOutput(raw: string): {\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n} {\n // claude -p --output-format json emits a JSON object on stdout.\n // Find the last complete JSON object in the output (may have log lines before it).\n const trimmed = raw.trim()\n // Try to find the last '{...}' block\n const lastBrace = trimmed.lastIndexOf(\"}\")\n if (lastBrace === -1) return {}\n\n // Walk back to find the matching opening brace\n let depth = 0\n let start = -1\n for (let i = lastBrace; i >= 0; i--) {\n if (trimmed[i] === \"}\") depth++\n else if (trimmed[i] === \"{\") {\n depth--\n if (depth === 0) {\n start = i\n break\n }\n }\n }\n if (start === -1) return {}\n\n try {\n const parsed = JSON.parse(trimmed.slice(start, lastBrace + 1)) as Record<\n string,\n unknown\n >\n const result: {\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n } = {}\n\n if (typeof parsed[\"total_cost_usd\"] === \"number\") {\n result.costUsd = parsed[\"total_cost_usd\"]\n }\n if (typeof parsed[\"session_id\"] === \"string\") {\n result.sessionId = parsed[\"session_id\"]\n }\n\n // usage.input_tokens / usage.output_tokens\n const usage = parsed[\"usage\"]\n if (usage && typeof usage === \"object\") {\n const u = usage as Record<string, unknown>\n if (typeof u[\"input_tokens\"] === \"number\") result.tokensInput = u[\"input_tokens\"]\n if (typeof u[\"output_tokens\"] === \"number\") result.tokensOutput = u[\"output_tokens\"]\n }\n\n return result\n } catch {\n return {}\n }\n}\n\n/**\n * Execute a work item. Returns when the run is complete (or timed out).\n * Reports results to the Stride API, with retry + local persistence on failure.\n */\nexport async function runWorkItem(\n config: BridgeConfig,\n work: WorkItem,\n): Promise<void> {\n const agent = findAgent(work.agent.id)\n const model = agent?.model ?? work.agent.model\n const agentWorkspace = agentWorkspaceDir(work.agent.id)\n\n // Determine working directory: prefer explicit workingDir if it exists.\n let cwd = agentWorkspace\n if (work.workingDir && existsSync(work.workingDir)) {\n cwd = work.workingDir\n } else if (work.workingDir) {\n logWarn(\n `workingDir \"${work.workingDir}\" does not exist; falling back to agent workspace`,\n )\n }\n\n log(`Starting run ${work.runId} (agent \"${work.agent.name}\", model \"${model}\")`)\n log(` cwd: ${cwd}`)\n\n const isWindows = process.platform === \"win32\"\n const settingsPath = join(agentWorkspace, \".claude\", \"settings.json\")\n // Fixed tokens only — the prompt goes via stdin, never argv.\n const rawArgs = [\n \"-p\",\n \"--output-format\",\n \"json\",\n \"--max-turns\",\n \"25\",\n \"--model\",\n model,\n \"--settings\",\n settingsPath,\n ]\n\n // Persistent mode: resume the agent's ongoing session so context carries\n // across work items; rotate to a fresh session when stale (see session.ts).\n const persistent = work.agent.localSessionMode === \"persistent\"\n let sessionState = loadSessionState(work.agent.id)\n if (persistent) {\n if (shouldRotateSession(sessionState, Date.now())) {\n if (sessionState.sessionId) {\n log(`Rotating session for agent \"${work.agent.name}\" (age/run limit reached)`)\n }\n sessionState = { sessionId: null, sessionStartedAt: null, runCount: 0 }\n } else if (sessionState.sessionId) {\n rawArgs.push(\"--resume\", sessionState.sessionId)\n }\n }\n // Windows needs shell:true (claude is a .cmd shim); quote args defensively\n // since Node concatenates them without quoting when shell is enabled.\n const args = isWindows\n ? rawArgs.map((a) => (/[\\s\"^&|<>%]/.test(a) ? `\"${a.replace(/\"/g, '\"\"')}\"` : a))\n : rawArgs\n\n // TODO Phase 2: if config.authMode === \"org_api_key\", inject ANTHROPIC_API_KEY\n const env: NodeJS.ProcessEnv = { ...process.env }\n\n let stdoutBuf = \"\"\n let stderrBuf = \"\"\n let exitCode: number | null = null\n let spawnError: string | undefined\n\n await new Promise<void>((resolve) => {\n let child: ReturnType<typeof spawn>\n\n try {\n child = spawn(\"claude\", args, {\n cwd,\n env,\n // shell on Windows: `claude` is a .cmd shim Node can't exec directly.\n // Safe because argv contains only our fixed, pre-quoted tokens.\n shell: isWindows,\n // stdin: pipe — the prompt is delivered via stdin, not argv.\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n child.stdin?.write(work.prompt)\n child.stdin?.end()\n } catch (err: unknown) {\n spawnError = err instanceof Error ? err.message : String(err)\n resolve()\n return\n }\n\n // 15-minute hard timeout\n const timeoutHandle = setTimeout(() => {\n logWarn(`Run ${work.runId} exceeded timeout — sending SIGTERM`)\n try {\n child.kill(\"SIGTERM\")\n } catch {\n // ignore\n }\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\")\n } catch {\n // ignore\n }\n }, 5_000)\n }, RUN_TIMEOUT_MS)\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutBuf += chunk.toString(\"utf-8\")\n })\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrBuf += chunk.toString(\"utf-8\")\n })\n\n child.on(\"close\", (code) => {\n clearTimeout(timeoutHandle)\n exitCode = code\n resolve()\n })\n\n child.on(\"error\", (err) => {\n clearTimeout(timeoutHandle)\n spawnError = err.message\n resolve()\n })\n })\n\n // Cap output sizes\n const stdoutExcerpt =\n stdoutBuf.length > STDOUT_CAP\n ? stdoutBuf.slice(-STDOUT_CAP)\n : stdoutBuf\n\n const stderrExcerpt =\n stderrBuf.length > STDERR_CAP\n ? stderrBuf.slice(-STDERR_CAP)\n : stderrBuf\n\n const succeeded = spawnError === undefined && exitCode === 0\n const parsed = succeeded ? parseClaudeOutput(stdoutBuf) : {}\n\n const report: RunReport = {\n runId: work.runId,\n wakeupId: work.wakeupId,\n status: succeeded ? \"completed\" : \"failed\",\n stdoutExcerpt,\n stderrExcerpt,\n error: spawnError,\n exitCode,\n ...parsed,\n }\n\n log(\n `Run ${work.runId} ${report.status} (exit ${exitCode ?? \"n/a\"}${parsed.costUsd !== undefined ? `, cost $${parsed.costUsd.toFixed(4)}` : \"\"})`,\n )\n\n if (persistent && report.status === \"completed\") {\n recordRunSession(work.agent.id, parsed.sessionId, sessionState, Date.now())\n }\n\n await sendReport(config, report)\n}\n\n/**\n * POST a run report to the server with retry logic.\n * If all retries fail, persist to pending-reports.json for later retry.\n */\nasync function sendReport(\n config: BridgeConfig,\n report: RunReport,\n): Promise<void> {\n const path = `/api/bridge/v1/runs/${report.runId}`\n\n for (let attempt = 1; attempt <= REPORT_RETRY_COUNT; attempt++) {\n try {\n await post(config, path, report)\n log(`Reported run ${report.runId} (attempt ${attempt})`)\n // On success, remove from pending if it was there from a previous attempt.\n removePendingReport(report.runId)\n return\n } catch (err: unknown) {\n logError(`Failed to report run ${report.runId} (attempt ${attempt})`, err)\n if (attempt < REPORT_RETRY_COUNT) {\n const delay = REPORT_RETRY_BASE_MS * Math.pow(2, attempt - 1)\n await sleep(delay)\n }\n }\n }\n\n // All retries exhausted — persist locally for next poll iteration.\n logWarn(`Persisting failed report for run ${report.runId} to pending-reports.json`)\n appendPendingReport(report)\n}\n\n/**\n * Retry any pending reports from previous failures.\n * Called each poll iteration.\n */\nexport async function retryPendingReports(config: BridgeConfig): Promise<void> {\n const pending = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH)\n if (!pending || pending.length === 0) return\n\n log(`Retrying ${pending.length} pending report(s)`)\n for (const report of pending) {\n await sendReport(config, report)\n }\n}\n\nfunction appendPendingReport(report: RunReport): void {\n const existing = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH) ?? []\n // Avoid duplicates\n const deduped = existing.filter((r) => r.runId !== report.runId)\n deduped.push(report)\n writeJsonAtomic(PENDING_REPORTS_PATH, deduped)\n}\n\nfunction removePendingReport(runId: string): void {\n const existing = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH)\n if (!existing || existing.length === 0) return\n const filtered = existing.filter((r) => r.runId !== runId)\n if (filtered.length !== existing.length) {\n writeJsonAtomic(PENDING_REPORTS_PATH, filtered)\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","/**\n * quota.ts — Claude subscription quota snapshot (Phase 2).\n *\n * In claude_login mode the daemon runs on the customer's Claude Code OAuth\n * session, which is rate-limited in 5-hour and 7-day windows. We read the\n * OAuth token from ~/.claude/.credentials.json and query Anthropic's usage\n * endpoint, reporting \"% remaining\" in heartbeats so the dashboard can show\n * a capacity chip per bridge. Pattern ported from cortex-os dashboard\n * lib/quota.ts (MIT).\n *\n * Everything here is fail-soft: no credentials / API errors → null quota.\n */\n\nimport { join } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { readJsonSafe } from \"./state.js\"\nimport { logWarn } from \"./log.js\"\n\nexport interface QuotaSnapshot {\n fiveHourRemainingPct: number | null\n sevenDayRemainingPct: number | null\n fetchedAt: string\n}\n\ninterface OauthUsageResponse {\n five_hour?: { utilization?: number }\n seven_day?: { utilization?: number }\n}\n\nconst USAGE_URL = \"https://api.anthropic.com/api/oauth/usage\"\nconst CACHE_TTL_MS = 60_000\nconst FETCH_TIMEOUT_MS = 5_000\n\n/** Pure: utilization (0..1) → whole-number percent remaining, clamped. */\nexport function remainingPct(utilization: unknown): number | null {\n if (typeof utilization !== \"number\" || !Number.isFinite(utilization)) return null\n return Math.max(0, Math.min(100, Math.round((1 - utilization) * 100)))\n}\n\n/** Pure: map the usage API response to a snapshot. */\nexport function parseUsageResponse(body: OauthUsageResponse, nowIso: string): QuotaSnapshot {\n return {\n fiveHourRemainingPct: remainingPct(body.five_hour?.utilization),\n sevenDayRemainingPct: remainingPct(body.seven_day?.utilization),\n fetchedAt: nowIso,\n }\n}\n\nfunction readOauthToken(): string | null {\n if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return process.env.CLAUDE_CODE_OAUTH_TOKEN\n const creds = readJsonSafe<{ claudeAiOauth?: { accessToken?: string } }>(\n join(homedir(), \".claude\", \".credentials.json\"),\n )\n return creds?.claudeAiOauth?.accessToken ?? null\n}\n\nlet cached: { snapshot: QuotaSnapshot; at: number } | null = null\nlet warnedNoToken = false\n\n/**\n * Fetch the current quota snapshot, cached for 60s. Returns null when no\n * OAuth token is available (org_api_key mode, or Claude not logged in).\n */\nexport async function getQuotaSnapshot(): Promise<QuotaSnapshot | null> {\n if (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.snapshot\n\n const token = readOauthToken()\n if (!token) {\n if (!warnedNoToken) {\n logWarn(\"No Claude OAuth token found — quota reporting disabled\")\n warnedNoToken = true\n }\n return null\n }\n\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS)\n const res = await fetch(USAGE_URL, {\n headers: { Authorization: `Bearer ${token}` },\n signal: controller.signal,\n })\n clearTimeout(timer)\n if (!res.ok) return cached?.snapshot ?? null\n\n const body = (await res.json()) as OauthUsageResponse\n const snapshot = parseUsageResponse(body, new Date().toISOString())\n cached = { snapshot, at: Date.now() }\n return snapshot\n } catch {\n // Stale-but-present beats nothing; otherwise null.\n return cached?.snapshot ?? null\n }\n}\n","/**\n * poller.ts — Main daemon loop for `stride-bridge start`.\n *\n * Polls /api/bridge/v1/work every 2.5 seconds.\n * Handles one work item at a time (single-flight Phase 1).\n * Sends heartbeat every 60 seconds independently.\n * Exponential backoff on fetch errors: 2.5s → 5 → 10 → 30s cap.\n * Posts daemon_started on startup and daemon_stopping on SIGINT/SIGTERM.\n */\n\nimport { platform, release } from \"node:os\"\nimport { post, postBestEffort } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport {\n refreshAgents,\n startAgentRefreshTimer,\n stopAgentRefreshTimer,\n} from \"./agents.js\"\nimport { runWorkItem, retryPendingReports } from \"./runner.js\"\nimport type { WorkItem } from \"./runner.js\"\nimport { getQuotaSnapshot } from \"./quota.js\"\nimport type { QuotaSnapshot } from \"./quota.js\"\nimport { VERSION } from \"./version.js\"\nimport { log, logError, logWarn } from \"./log.js\"\n\nconst POLL_INTERVAL_MS = 2_500\nconst HEARTBEAT_INTERVAL_MS = 60_000\nconst BACKOFF_STEPS_MS = [2_500, 5_000, 10_000, 30_000]\n\ninterface WorkResponse {\n work: WorkItem | null\n}\n\ninterface HeartbeatPayload {\n daemonVersion: string\n osInfo: { platform: string; release: string }\n agents: unknown[]\n quota?: QuotaSnapshot\n}\n\nlet running = false\n\n/**\n * Start the daemon loop. Blocks until SIGINT/SIGTERM.\n */\nexport async function startDaemon(config: BridgeConfig): Promise<void> {\n running = true\n\n // Register signal handlers first so they fire even if startup work fails.\n const shutdown = async (signal: string) => {\n if (!running) return\n running = false\n log(`Received ${signal} — shutting down gracefully`)\n stopAgentRefreshTimer()\n await postBestEffort(config, \"/api/bridge/v1/events\", {\n kind: \"daemon_stopping\",\n message: `Daemon stopping (${signal})`,\n daemonVersion: VERSION,\n })\n process.exit(0)\n }\n\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\"))\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"))\n\n // Announce startup\n await postBestEffort(config, \"/api/bridge/v1/events\", {\n kind: \"daemon_started\",\n message: \"Daemon started\",\n daemonVersion: VERSION,\n osInfo: { platform: platform(), release: release() },\n })\n\n log(`Daemon started (v${VERSION}, bridge ${config.bridgeId})`)\n\n // Fetch agents on startup\n await refreshAgents(config)\n startAgentRefreshTimer(config)\n\n // Heartbeat timer — runs independently of the poll loop\n const sendHeartbeat = async () => {\n const payload: HeartbeatPayload = {\n daemonVersion: VERSION,\n osInfo: { platform: platform(), release: release() },\n agents: [],\n }\n const quota = await getQuotaSnapshot()\n if (quota) payload.quota = quota\n try {\n await post(config, \"/api/bridge/v1/heartbeat\", payload)\n } catch (err: unknown) {\n logWarn(`Heartbeat failed: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n // First heartbeat immediately so the dashboard flips to connected fast.\n void sendHeartbeat()\n const heartbeatTimer = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS)\n\n if (heartbeatTimer.unref) heartbeatTimer.unref()\n\n // Main poll loop\n let backoffIndex = 0\n\n while (running) {\n // Retry any persisted failed reports before fetching new work\n try {\n await retryPendingReports(config)\n } catch {\n // Non-fatal\n }\n\n try {\n const data = await post<WorkResponse>(config, \"/api/bridge/v1/work\", {})\n backoffIndex = 0 // reset backoff on success\n\n if (data.work) {\n log(`Received work item: run ${data.work.runId}`)\n // Single-flight: await the run before polling again\n try {\n await runWorkItem(config, data.work)\n } catch (err: unknown) {\n logError(`Unhandled error in runWorkItem for run ${data.work.runId}`, err)\n }\n // Poll immediately after completing a run (don't wait full interval)\n continue\n }\n } catch (err: unknown) {\n const delay = BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)]\n logError(\n `Poll error (backoff ${delay}ms)`,\n err,\n )\n backoffIndex = Math.min(backoffIndex + 1, BACKOFF_STEPS_MS.length - 1)\n await sleep(delay)\n continue\n }\n\n await sleep(POLL_INTERVAL_MS)\n }\n\n clearInterval(heartbeatTimer)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","/**\n * cli.ts — Stride Bridge CLI entry point.\n *\n * Commands:\n * connect <pairingCode> [--api-url <url>] — Pair this machine with StrideOps\n * start — Start the daemon loop (foreground)\n * status — Print config + connectivity info\n * doctor — Run prerequisite checks\n */\n\nimport { Command } from \"commander\"\nimport { VERSION } from \"./version.js\"\nimport { runPair } from \"./pair.js\"\nimport { startDaemon } from \"./poller.js\"\nimport { readConfig, requireConfig, CONFIG_PATH, isConfigured } from \"./config.js\"\nimport { log } from \"./log.js\"\nimport { execSync } from \"node:child_process\"\nimport { existsSync, accessSync, constants, mkdirSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { post } from \"./api.js\"\n\nconst DEFAULT_API_URL = \"https://app.strideops.ai\"\n\nconst program = new Command()\n\nprogram\n .name(\"stride-bridge\")\n .description(\"Stride Bridge daemon — run StrideOps Build agents locally\")\n .version(VERSION)\n\n// ---------------------------------------------------------------------------\n// connect\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"connect <pairingCode>\")\n .description(\"Pair this machine with StrideOps using a one-time pairing code\")\n .option(\n \"--api-url <url>\",\n \"StrideOps API base URL\",\n DEFAULT_API_URL,\n )\n .action(async (pairingCode: string, opts: { apiUrl: string }) => {\n await runPair(pairingCode, opts.apiUrl)\n })\n\n// ---------------------------------------------------------------------------\n// start\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"start\")\n .description(\"Start the daemon (foreground; press Ctrl+C to stop)\")\n .action(async () => {\n const config = requireConfig()\n log(`Starting daemon with bridge \"${config.bridgeName}\" (${config.bridgeId})`)\n await startDaemon(config)\n })\n\n// ---------------------------------------------------------------------------\n// status\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"status\")\n .description(\"Print current configuration and server connectivity\")\n .action(async () => {\n const config = readConfig()\n\n console.log(\"=== Stride Bridge Status ===\")\n console.log()\n\n if (!config) {\n console.log(\"Status : NOT CONNECTED\")\n console.log(`Config path : ${CONFIG_PATH}`)\n console.log()\n console.log(\"Run `stride-bridge connect <PAIRING_CODE>` to get started.\")\n return\n }\n\n console.log(`Status : CONNECTED`)\n console.log(`Bridge name : ${config.bridgeName}`)\n console.log(`Bridge ID : ${config.bridgeId}`)\n console.log(`Org ID : ${config.orgId}`)\n console.log(`API URL : ${config.apiBaseUrl}`)\n console.log(`Auth mode : ${config.authMode}`)\n console.log(`Config path : ${CONFIG_PATH}`)\n console.log()\n\n // Check server reachability\n process.stdout.write(\"Server : checking... \")\n try {\n await post(config, \"/api/bridge/v1/heartbeat\", {\n daemonVersion: VERSION,\n osInfo: {},\n agents: [],\n })\n console.log(\"REACHABLE\")\n } catch (err: unknown) {\n console.log(\n `UNREACHABLE (${err instanceof Error ? err.message : String(err)})`,\n )\n }\n console.log()\n })\n\n// ---------------------------------------------------------------------------\n// autostart\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"autostart <action>\")\n .description(\"Manage start-at-logon: install | remove | status (Windows Task Scheduler)\")\n .action(async (action: string) => {\n const { autostartInstall, autostartRemove, autostartStatus } = await import(\"./autostart.js\")\n if (action === \"install\") autostartInstall()\n else if (action === \"remove\") autostartRemove()\n else if (action === \"status\") autostartStatus()\n else {\n console.error(`Unknown action \"${action}\" — use install, remove, or status`)\n process.exitCode = 1\n }\n })\n\n// ---------------------------------------------------------------------------\n// doctor\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"doctor\")\n .description(\"Check all prerequisites for running the bridge daemon\")\n .action(async () => {\n console.log(\"=== Stride Bridge Doctor ===\")\n console.log()\n\n let allGood = true\n\n // 1. Node version\n const nodeVersion = process.versions.node\n const [major] = nodeVersion.split(\".\").map(Number)\n const nodeOk = major >= 20\n printCheck(\n \"Node.js >= 20\",\n nodeOk,\n `Found v${nodeVersion}`,\n `Found v${nodeVersion} — upgrade to Node.js 20+`,\n )\n if (!nodeOk) allGood = false\n\n // 2. claude CLI on PATH\n let claudeVersion = \"\"\n let claudeOnPath = false\n try {\n claudeVersion = execSync(\"claude --version\", {\n encoding: \"utf-8\",\n timeout: 5_000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim()\n claudeOnPath = true\n } catch {\n claudeOnPath = false\n }\n printCheck(\n \"claude CLI on PATH\",\n claudeOnPath,\n claudeVersion || \"found\",\n \"claude not found — install Claude Code CLI from https://docs.anthropic.com/claude-code\",\n )\n if (!claudeOnPath) allGood = false\n\n // 3. Claude authenticated (config presence heuristic)\n let claudeAuthed = false\n let claudeAuthNote = \"\"\n if (claudeOnPath) {\n // Claude Code stores its config under ~/.claude/ or %APPDATA%/claude/\n const possiblePaths = [\n join(homedir(), \".claude\", \"settings.json\"),\n join(homedir(), \".config\", \"claude\", \"settings.json\"),\n join(\n process.env[\"APPDATA\"] ?? join(homedir(), \"AppData\", \"Roaming\"),\n \"Claude\",\n \"settings.json\",\n ),\n ]\n claudeAuthed = possiblePaths.some(existsSync)\n claudeAuthNote = claudeAuthed\n ? \"config found\"\n : \"no config found — run `claude` to authenticate\"\n } else {\n claudeAuthNote = \"skipped (claude not on PATH)\"\n }\n printCheck(\n \"Claude authenticated\",\n claudeAuthed,\n claudeAuthNote,\n claudeAuthNote,\n )\n if (!claudeAuthed && claudeOnPath) allGood = false\n\n // 4. Config file valid\n const configured = isConfigured()\n const configData = readConfig()\n const configValid = configured && configData !== null\n printCheck(\n \"Bridge config\",\n configValid,\n configValid\n ? `${CONFIG_PATH} (bridge: ${configData?.bridgeName})`\n : configured\n ? `${CONFIG_PATH} exists but could not be parsed`\n : \"not found — run `stride-bridge connect <CODE>`\",\n configured\n ? `${CONFIG_PATH} could not be parsed — re-run connect`\n : \"not found — run `stride-bridge connect <CODE>`\",\n )\n if (!configValid) allGood = false\n\n // 5. Server reachable\n if (configData) {\n process.stdout.write(\"[ ] StrideOps server reachable ... \")\n try {\n await post(configData, \"/api/bridge/v1/heartbeat\", {\n daemonVersion: VERSION,\n osInfo: {},\n agents: [],\n })\n clearLine()\n printCheck(\"StrideOps server reachable\", true, configData.apiBaseUrl, \"\")\n } catch (err: unknown) {\n clearLine()\n printCheck(\n \"StrideOps server reachable\",\n false,\n \"\",\n `${configData.apiBaseUrl} — ${err instanceof Error ? err.message : String(err)}`,\n )\n allGood = false\n }\n } else {\n printCheck(\n \"StrideOps server reachable\",\n false,\n \"\",\n \"skipped (not configured)\",\n )\n }\n\n // 6. Workspace dir writable\n const bridgeDir = join(homedir(), \".stride-bridge\")\n try {\n mkdirSync(bridgeDir, { recursive: true })\n accessSync(bridgeDir, constants.W_OK)\n printCheck(\"Bridge workspace writable\", true, bridgeDir, \"\")\n } catch {\n printCheck(\n \"Bridge workspace writable\",\n false,\n \"\",\n `${bridgeDir} is not writable`,\n )\n allGood = false\n }\n\n console.log()\n if (allGood) {\n console.log(\"All checks passed. Run `stride-bridge start` to launch the daemon.\")\n } else {\n console.log(\n \"Some checks failed. Fix the issues above, then re-run `stride-bridge doctor`.\",\n )\n process.exit(1)\n }\n })\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction printCheck(\n label: string,\n ok: boolean,\n successNote: string,\n failureNote: string,\n): void {\n const icon = ok ? \"[ok]\" : \"[!!]\"\n const note = ok ? successNote : failureNote\n console.log(`${icon} ${label}${note ? \" — \" + note : \"\"}`)\n}\n\nfunction clearLine(): void {\n // Move cursor up one line and clear it (for the \"checking...\" line)\n process.stdout.write(\"\\r\\x1b[K\")\n}\n\n// ---------------------------------------------------------------------------\n// Parse\n// ---------------------------------------------------------------------------\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(\n `[stride-bridge] Fatal: ${err instanceof Error ? err.message : String(err)}`,\n )\n process.exit(1)\n})\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/pair.ts","../src/api.ts","../src/hooks-writer.ts","../src/agents.ts","../src/session.ts","../src/runner.ts","../src/quota.ts","../src/poller.ts","../src/cli.ts"],"names":["mkdirSync","join","writeFileSync","existsSync","homedir","platform","release","sleep"],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * Daemon version — keep in sync with package.json.\n * Single source of truth used in heartbeats, pair requests, and CLI output.\n */\nexport const VERSION = \"0.1.3\"\n","/**\n * pair.ts — Pair this machine with a StrideOps workspace via a pairing code.\n *\n * Sends machine info to /api/bridge/v1/pair and saves the returned\n * credentials to ~/.stride-bridge/config.json.\n */\n\nimport { hostname, platform, arch, cpus, totalmem, release } from \"node:os\"\nimport { VERSION } from \"./version.js\"\nimport { writeConfig } from \"./config.js\"\nimport { log } from \"./log.js\"\n\ninterface PairRequest {\n pairingCode: string\n daemonVersion: string\n machineInfo: {\n hostname: string\n platform: string\n arch: string\n cpus: number\n totalMemMb: number\n }\n osInfo: {\n platform: string\n release: string\n }\n}\n\ninterface PairResponseData {\n bridgeId: string\n orgId: string\n name: string\n authMode: \"claude_login\" | \"org_api_key\"\n bridgeToken: string\n}\n\ninterface RawPairResponse {\n success: boolean\n data?: PairResponseData\n error?: string\n}\n\n/**\n * Pair with the StrideOps server using a one-time pairing code.\n * On success, saves config and prints a confirmation.\n */\nexport async function runPair(\n pairingCode: string,\n apiBaseUrl: string,\n): Promise<void> {\n const url = `${apiBaseUrl.replace(/\\/$/, \"\")}/api/bridge/v1/pair`\n\n log(`Pairing with ${apiBaseUrl} ...`)\n\n const cpuCount = cpus().length\n const totalMemMb = Math.round(totalmem() / 1024 / 1024)\n\n const payload: PairRequest = {\n pairingCode,\n daemonVersion: VERSION,\n machineInfo: {\n hostname: hostname(),\n platform: platform(),\n arch: arch(),\n cpus: cpuCount,\n totalMemMb,\n },\n osInfo: {\n platform: platform(),\n release: release(),\n },\n }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 15_000)\n\n let res: Response\n try {\n res = await fetch(url, {\n method: \"POST\",\n signal: controller.signal,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n })\n } catch (err: unknown) {\n clearTimeout(timer)\n if (err instanceof Error && err.name === \"AbortError\") {\n console.error(\"[stride-bridge] Pairing request timed out. Check your connection.\")\n process.exit(1)\n }\n console.error(\n `[stride-bridge] Network error during pairing: ${err instanceof Error ? err.message : String(err)}`,\n )\n process.exit(1)\n } finally {\n clearTimeout(timer)\n }\n\n let body: RawPairResponse\n try {\n body = (await res.json()) as RawPairResponse\n } catch {\n console.error(\n `[stride-bridge] Failed to parse pairing response (HTTP ${res.status}).`,\n )\n process.exit(1)\n }\n\n if (!res.ok || !body.success || !body.data) {\n console.error(\n `[stride-bridge] Pairing failed: ${body.error ?? `HTTP ${res.status}`}`,\n )\n process.exit(1)\n }\n\n const { bridgeId, orgId, name, authMode, bridgeToken } = body.data\n\n writeConfig({\n apiBaseUrl,\n bridgeToken,\n bridgeId,\n orgId,\n bridgeName: name,\n authMode,\n })\n\n console.log(`[stride-bridge] Connected successfully!`)\n console.log(` Bridge name : ${name}`)\n console.log(` Bridge ID : ${bridgeId}`)\n console.log(` Org ID : ${orgId}`)\n console.log(` Auth mode : ${authMode}`)\n console.log(` Config : ~/.stride-bridge/config.json`)\n console.log()\n console.log(`Run \\`stride-bridge start\\` to begin accepting work.`)\n}\n","/**\n * api.ts — Minimal authenticated HTTP client for the Bridge API.\n *\n * Uses the global fetch (Node >= 18) + AbortController for timeouts.\n * All responses are expected to be { success: boolean, data?: T, error?: string }.\n * Throws on network errors or when success === false.\n */\n\nimport type { BridgeConfig } from \"./config.js\"\n\nconst REQUEST_TIMEOUT_MS = 10_000\n\nexport class ApiError extends Error {\n constructor(\n public readonly status: number | null,\n message: string,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport interface ApiResponse<T> {\n success: boolean\n data?: T\n error?: string\n}\n\n/**\n * Make an authenticated fetch to the Bridge API.\n * Adds Authorization: Bearer header from config.\n * Throws ApiError on non-2xx HTTP status or success:false response.\n */\nexport async function authedFetch<T>(\n config: BridgeConfig,\n path: string,\n init: RequestInit = {},\n): Promise<T> {\n const url = `${config.apiBaseUrl.replace(/\\/$/, \"\")}${path}`\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)\n\n let res: Response\n try {\n res = await fetch(url, {\n ...init,\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.bridgeToken}`,\n ...(init.headers as Record<string, string> | undefined),\n },\n })\n } catch (err: unknown) {\n clearTimeout(timer)\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new ApiError(null, `Request timed out after ${REQUEST_TIMEOUT_MS}ms: ${path}`)\n }\n throw new ApiError(null, `Network error: ${err instanceof Error ? err.message : String(err)}`)\n } finally {\n clearTimeout(timer)\n }\n\n let body: ApiResponse<T>\n try {\n body = (await res.json()) as ApiResponse<T>\n } catch {\n throw new ApiError(res.status, `Failed to parse JSON response from ${path} (HTTP ${res.status})`)\n }\n\n if (!res.ok || !body.success) {\n throw new ApiError(\n res.status,\n body.error ?? `API error from ${path} (HTTP ${res.status})`,\n )\n }\n\n return body.data as T\n}\n\n/**\n * POST JSON to a path. Convenience wrapper over authedFetch.\n */\nexport async function post<T>(\n config: BridgeConfig,\n path: string,\n body: unknown,\n): Promise<T> {\n return authedFetch<T>(config, path, {\n method: \"POST\",\n body: JSON.stringify(body),\n })\n}\n\n/**\n * GET a path. Convenience wrapper over authedFetch.\n */\nexport async function get<T>(config: BridgeConfig, path: string): Promise<T> {\n return authedFetch<T>(config, path, { method: \"GET\" })\n}\n\n/**\n * Like post() but swallows all errors (for best-effort fire-and-forget calls\n * such as daemon_stopping events).\n */\nexport async function postBestEffort(\n config: BridgeConfig,\n path: string,\n body: unknown,\n): Promise<void> {\n try {\n await post(config, path, body)\n } catch {\n // Intentionally swallowed — best-effort only.\n }\n}\n","/**\n * hooks-writer.ts — Generate .claude/settings.json with a signed PreToolUse hook.\n *\n * The signing scheme mirrors hook-signature.ts on the server:\n * HMAC-SHA256(`${unixSeconds}\\n${rawBody}`) → hex\n * with headers x-stride-hook-sig and x-stride-hook-ts.\n *\n * For local agents we generate a small Node.js script (hook-runner.mjs) that\n * Claude Code invokes as the hook command. This avoids bash dependency on\n * Windows and is consistent across platforms.\n *\n * The runner transforms Claude Code's hook input ({tool_name, tool_input,\n * session_id}) into the server's PreToolUse schema ({tool, args, sessionId})\n * and FORWARDS the server's decision — a budget/loop denial from Stride must\n * actually block the tool call. Fail-open on any error: if Stride is\n * unreachable the hook allows, so the agent is never blocked by an outage.\n *\n * Phase 1 wires PreToolUse only. Claude Code's Stop hook input carries no\n * usage/cost data, so per-turn usage reporting is deferred to Phase 2\n * (persistent sessions); run-level cost is reported by runner.ts via\n * POST /api/bridge/v1/runs/{runId} instead.\n */\n\nimport { join } from \"node:path\"\nimport { mkdirSync, writeFileSync, chmodSync } from \"node:fs\"\nimport { writeJsonAtomic } from \"./state.js\"\n\nexport interface HookWriterOptions {\n agentId: string\n hookSecret: string\n apiBaseUrl: string\n workspaceDir: string\n}\n\n/**\n * Generate the Node.js hook runner script content.\n * This script is written once per agent to their .claude/ directory.\n * It reads CLAUDE_HOOK_INPUT from stdin (Claude Code provides it),\n * computes the HMAC signature, and POSTs to the given URL.\n *\n * The script exits 0 (allow) regardless of Stride's response,\n * so the agent is never blocked by an unreachable server.\n */\nfunction buildHookRunnerScript(\n preToolUseUrl: string,\n hookSecret: string,\n): string {\n // We embed the secret directly into the per-agent script.\n // The script lives in the agent's workspace dir (~/.stride-bridge/agents/<id>/)\n // which is chmod 700 (POSIX) — not in the project directory Claude edits.\n // The secret is never printed to stdout/stderr.\n return `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Signed PreToolUse hook runner for Stride Build agents.\n// HMAC scheme: HMAC-SHA256(timestamp+\"\\\\n\"+body) -> hex\n// Headers: x-stride-hook-ts, x-stride-hook-sig\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst PRE_TOOL_USE_URL = ${JSON.stringify(preToolUseUrl)};\nconst TIMEOUT_MS = 8_000;\n\nfunction allow() {\n // No output + exit 0 = allow in Claude Code's hook protocol.\n process.exit(0);\n}\n\nasync function main() {\n // Claude Code writes the hook input to stdin as JSON. fd 0 works on\n // Windows too (\"/dev/stdin\" does not).\n let input = {};\n try {\n input = JSON.parse(readFileSync(0, \"utf-8\"));\n } catch {\n allow(); // unreadable input — fail-open\n }\n\n // Transform Claude Code's hook input into the server's PreToolUse schema.\n const body = JSON.stringify({\n tool: typeof input.tool_name === \"string\" && input.tool_name ? input.tool_name : \"unknown\",\n args: typeof input.tool_input === \"object\" && input.tool_input !== null ? input.tool_input : undefined,\n sessionId: typeof input.session_id === \"string\" ? input.session_id : undefined,\n });\n\n const ts = Math.floor(Date.now() / 1000).toString();\n const sig = createHmac(\"sha256\", HOOK_SECRET)\n .update(ts + \"\\\\n\" + body)\n .digest(\"hex\");\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);\n const res = await fetch(PRE_TOOL_USE_URL, {\n method: \"POST\",\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n });\n clearTimeout(timer);\n\n const decision = await res.json();\n if (decision && (decision.permissionDecision === \"deny\" || decision.permissionDecision === \"ask\")) {\n // Forward Stride's decision so Claude Code actually blocks the call.\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: decision.permissionDecision,\n permissionDecisionReason: decision.permissionDecisionReason || \"Blocked by Stride policy\",\n },\n }) + \"\\\\n\");\n }\n process.exit(0);\n}\n\n// Fail-open: an unreachable server or unexpected error must never block the agent.\nmain().catch(() => allow());\n`\n}\n\n/**\n * Write .claude/settings.json and the hook-runner.mjs script to the agent's\n * workspace directory.\n *\n * settings.json wires PreToolUse (all tools, matcher \".*\") to the\n * hook-runner.mjs script via `node <path>`.\n */\nexport function writeAgentHooks(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const preToolUseUrl = `${base}/api/internal/build/agents/${agentId}/hook/pre-tool-use`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n\n // Write the hook runner script\n const runnerPath = join(claudeDir, \"hook-runner.mjs\")\n const runnerContent = buildHookRunnerScript(preToolUseUrl, hookSecret)\n writeFileSync(runnerPath, runnerContent, { encoding: \"utf-8\", mode: 0o700 })\n\n // Make executable on POSIX\n if (process.platform !== \"win32\") {\n try {\n chmodSync(runnerPath, 0o700)\n } catch {\n // Best effort.\n }\n }\n\n // Build the command that Claude Code will invoke.\n // Use `node <absolute-path>` so it works regardless of PATH. Quote both\n // paths — they regularly contain spaces on Windows (C:\\Users\\First Last\\...).\n const preToolUseCmd = `\"${process.execPath}\" \"${runnerPath}\"`\n\n const settingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: \".*\",\n hooks: [\n {\n type: \"command\",\n command: preToolUseCmd,\n timeout: 10,\n },\n ],\n },\n ],\n },\n }\n\n writeJsonAtomic(join(claudeDir, \"settings.json\"), settingsJson)\n}\n\n/**\n * Write .claude/memory-sync.mjs — a Node script the agent runs (per its\n * CLAUDE.md memory protocol) to upload MEMORY.md and today's daily journal\n * to Stride's HMAC-authenticated memory-sync endpoint. Node instead of the\n * server's bash snippet because local agents run on Windows too.\n */\nexport function writeMemorySyncScript(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const syncUrl = `${base}/api/internal/build/agents/${agentId}/memory/sync`\n\n const script = `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Uploads MEMORY.md + today's daily journal to Stride (HMAC-signed).\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst SYNC_URL = ${JSON.stringify(syncUrl)};\nconst workspace = dirname(dirname(fileURLToPath(import.meta.url)));\n\nconst files = [];\nconst memoryMd = join(workspace, \"MEMORY.md\");\nif (existsSync(memoryMd)) {\n files.push({ type: \"learnings\", content: readFileSync(memoryMd, \"utf-8\").slice(0, 50000) });\n}\nconst today = new Date().toISOString().slice(0, 10);\nconst daily = join(workspace, \"memory\", today + \".md\");\nif (existsSync(daily)) {\n files.push({ type: \"daily\", date: today, content: readFileSync(daily, \"utf-8\").slice(0, 50000) });\n}\n\nif (files.length === 0) {\n console.log(\"memory-sync: nothing to sync\");\n process.exit(0);\n}\n\nconst body = JSON.stringify({ files });\nconst ts = Math.floor(Date.now() / 1000).toString();\nconst sig = createHmac(\"sha256\", HOOK_SECRET).update(ts + \"\\\\n\" + body).digest(\"hex\");\n\nconst res = await fetch(SYNC_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n}).catch((err) => ({ ok: false, statusText: String(err) }));\n\nconsole.log(res.ok ? \\`memory-sync: synced \\${files.length} file(s)\\` : \\`memory-sync: failed (\\${res.statusText ?? res.status})\\`);\n`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n writeFileSync(join(claudeDir, \"memory-sync.mjs\"), script, { encoding: \"utf-8\", mode: 0o700 })\n}\n\n/**\n * Write .claude/identity-sync.mjs — uploads the agent's identity files\n * (SOUL.md, IDENTITY.md, USER.md, HEARTBEAT.md, optional goals.json with\n * {goals: string[]}) to Stride's identity endpoint and marks onboarding\n * complete. The agent runs this once, at the end of its first-boot interview.\n */\nexport function writeIdentitySyncScript(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const identityUrl = `${base}/api/internal/build/agents/${agentId}/identity`\n\n const script = `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Uploads identity files + goals to Stride and marks onboarding complete.\n\nimport { createHmac } from \"node:crypto\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst IDENTITY_URL = ${JSON.stringify(identityUrl)};\nconst workspace = dirname(dirname(fileURLToPath(import.meta.url)));\n\nfunction readIf(name) {\n const p = join(workspace, name);\n return existsSync(p) ? readFileSync(p, \"utf-8\").slice(0, 20000) : undefined;\n}\n\nconst payload = {\n soulMd: readIf(\"SOUL.md\"),\n identityMd: readIf(\"IDENTITY.md\"),\n userMd: readIf(\"USER.md\"),\n heartbeatChecklist: readIf(\"HEARTBEAT.md\"),\n onboardingComplete: true,\n};\nconst goalsRaw = readIf(\"goals.json\");\nif (goalsRaw) {\n try {\n const parsed = JSON.parse(goalsRaw);\n if (Array.isArray(parsed.goals)) payload.goals = parsed.goals.slice(0, 5);\n } catch { /* ignore malformed goals.json */ }\n}\n\nconst body = JSON.stringify(payload);\nconst ts = Math.floor(Date.now() / 1000).toString();\nconst sig = createHmac(\"sha256\", HOOK_SECRET).update(ts + \"\\\\n\" + body).digest(\"hex\");\n\nconst res = await fetch(IDENTITY_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n}).catch((err) => ({ ok: false, statusText: String(err) }));\n\nconsole.log(res.ok ? \"identity-sync: onboarding recorded in Stride\" : \\`identity-sync: failed (\\${res.statusText ?? res.status})\\`);\n`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n writeFileSync(join(claudeDir, \"identity-sync.mjs\"), script, { encoding: \"utf-8\", mode: 0o700 })\n}\n\n/**\n * Write .claude/memory-search.mjs — semantic search over the agent's own\n * synced memory (Phase D). Usage: node .claude/memory-search.mjs \"query\"\n */\nexport function writeMemorySearchScript(opts: HookWriterOptions): void {\n const { agentId, hookSecret, apiBaseUrl, workspaceDir } = opts\n const base = apiBaseUrl.replace(/\\/$/, \"\")\n const searchUrl = `${base}/api/internal/build/agents/${agentId}/memory/search`\n\n const script = `#!/usr/bin/env node\n// Auto-generated by stride-bridge. Do not edit.\n// Semantically searches this agent's synced memory in Stride.\n\nimport { createHmac } from \"node:crypto\";\n\nconst HOOK_SECRET = ${JSON.stringify(hookSecret)};\nconst SEARCH_URL = ${JSON.stringify(searchUrl)};\n\nconst query = process.argv.slice(2).join(\" \").trim();\nif (!query) {\n console.log(\"usage: node memory-search.mjs <query>\");\n process.exit(0);\n}\n\nconst body = JSON.stringify({ query, limit: 5 });\nconst ts = Math.floor(Date.now() / 1000).toString();\nconst sig = createHmac(\"sha256\", HOOK_SECRET).update(ts + \"\\n\" + body).digest(\"hex\");\n\nconst res = await fetch(SEARCH_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-stride-hook-ts\": ts,\n \"x-stride-hook-sig\": sig,\n },\n body,\n}).catch(() => null);\n\nif (!res || !res.ok) {\n console.log(\"memory-search: unavailable\");\n process.exit(0);\n}\nconst data = await res.json();\nconst results = data?.data?.results ?? [];\nif (results.length === 0) {\n console.log(\"memory-search: no matches\");\n} else {\n for (const r of results) {\n console.log(\"--- \" + (r.label ?? \"memory\") + \" ---\");\n console.log(String(r.content ?? \"\").slice(0, 600));\n }\n}\n`\n\n const claudeDir = join(workspaceDir, \".claude\")\n mkdirSync(claudeDir, { recursive: true })\n writeFileSync(join(claudeDir, \"memory-search.mjs\"), script, { encoding: \"utf-8\", mode: 0o700 })\n}\n","/**\n * agents.ts — Fetch agent configs from Stride and provision local workspaces.\n *\n * At startup and every 5 minutes, fetches the list of agents assigned to this\n * bridge. For each agent, ensures:\n * ~/.stride-bridge/agents/<agentId>/workspace/\n * ~/.stride-bridge/agents/<agentId>/workspace/CLAUDE.md\n * ~/.stride-bridge/agents/<agentId>/workspace/.claude/settings.json\n * ~/.stride-bridge/agents/<agentId>/workspace/.claude/hook-runner.mjs\n */\n\nimport { join } from \"node:path\"\nimport { mkdirSync, writeFileSync, existsSync } from \"node:fs\"\nimport { get } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport { AGENTS_DIR } from \"./config.js\"\nimport {\n writeAgentHooks,\n writeMemorySyncScript,\n writeIdentitySyncScript,\n writeMemorySearchScript,\n} from \"./hooks-writer.js\"\nimport { log, logError } from \"./log.js\"\n\nexport interface BridgeAgent {\n id: string\n name: string\n status: string\n model: string\n systemPrompt: string\n soulMd?: string | null\n allowedTools: string[]\n maxBudgetUsd: number\n localSessionMode: string\n hookSecret: string\n // Phase 3 identity + memory layer (server-rendered templates)\n identityMd?: string | null\n userMd?: string | null\n heartbeatChecklist?: string | null\n memoryProtocolMd?: string | null\n defaultSoulMd?: string | null\n // Phase 4 first-boot onboarding\n onboardedAt?: string | null\n onboardingMd?: string | null\n}\n\ninterface AgentsResponseData {\n agents: BridgeAgent[]\n}\n\n// In-memory cache of agent configs, refreshed periodically.\nlet cachedAgents: BridgeAgent[] = []\nconst REFRESH_INTERVAL_MS = 5 * 60 * 1000 // 5 minutes\nlet refreshTimer: ReturnType<typeof setInterval> | null = null\n\n/**\n * Fetch agent list from server and provision local workspaces.\n * Errors are caught and logged — a refresh failure must not crash the daemon.\n */\nexport async function refreshAgents(config: BridgeConfig): Promise<void> {\n try {\n const data = await get<AgentsResponseData>(config, \"/api/bridge/v1/agents\")\n cachedAgents = data.agents ?? []\n log(`Refreshed ${cachedAgents.length} agent(s)`)\n for (const agent of cachedAgents) {\n await ensureAgentWorkspace(config, agent)\n }\n } catch (err: unknown) {\n logError(\"Failed to refresh agents\", err)\n }\n}\n\n/**\n * Return the cached agent list (may be stale if refresh failed).\n */\nexport function getCachedAgents(): BridgeAgent[] {\n return cachedAgents\n}\n\n/**\n * Find a cached agent by ID.\n */\nexport function findAgent(agentId: string): BridgeAgent | undefined {\n return cachedAgents.find((a) => a.id === agentId)\n}\n\n/**\n * Ensure the local workspace directory exists for an agent, writing\n * CLAUDE.md and .claude/settings.json (with signed hooks).\n */\nasync function ensureAgentWorkspace(\n config: BridgeConfig,\n agent: BridgeAgent,\n): Promise<void> {\n const workspaceDir = agentWorkspaceDir(agent.id)\n mkdirSync(workspaceDir, { recursive: true })\n // Daily-journal directory (Layer 1 memory) — agent-written, never clobbered.\n mkdirSync(join(workspaceDir, \"memory\"), { recursive: true })\n\n // CLAUDE.md is fully generated — safe to overwrite on every refresh.\n const sections: string[] = []\n if (agent.systemPrompt) sections.push(agent.systemPrompt)\n sections.push(agent.soulMd ?? agent.defaultSoulMd ?? \"\")\n if (agent.memoryProtocolMd) sections.push(agent.memoryProtocolMd)\n sections.push(\n \"## Memory sync (local agent)\\n\\n\" +\n \"After updating MEMORY.md or today's daily journal, sync them to Stride by running:\\n\\n\" +\n \"```\\nnode .claude/memory-sync.mjs\\n```\\n\\n\" +\n \"Before re-researching something you may have learned before, search your \" +\n \"own past memory semantically:\\n\\n\" +\n '```\\nnode .claude/memory-search.mjs \"your question\"\\n```\\n',\n )\n if (!agent.onboardedAt && agent.onboardingMd) {\n sections.push(\n \"## FIRST BOOT — onboarding required\\n\\n\" +\n \"You have not been onboarded. Read ONBOARDING.md in this workspace and \" +\n \"complete the interview BEFORE regular work. To sync your identity after \" +\n \"the interview, run:\\n\\n```\\nnode .claude/identity-sync.mjs\\n```\\n\",\n )\n }\n writeFileSync(join(workspaceDir, \"CLAUDE.md\"), sections.filter(Boolean).join(\"\\n\\n\"), \"utf-8\")\n\n // ONBOARDING.md present only until the agent completes its interview.\n if (!agent.onboardedAt && agent.onboardingMd) {\n writeFileSync(join(workspaceDir, \"ONBOARDING.md\"), agent.onboardingMd, \"utf-8\")\n }\n\n // Identity files: server-rendered, overwrite (dashboard is source of truth).\n if (agent.identityMd) writeFileSync(join(workspaceDir, \"IDENTITY.md\"), agent.identityMd, \"utf-8\")\n if (agent.userMd) writeFileSync(join(workspaceDir, \"USER.md\"), agent.userMd, \"utf-8\")\n if (agent.heartbeatChecklist) {\n writeFileSync(join(workspaceDir, \"HEARTBEAT.md\"), agent.heartbeatChecklist, \"utf-8\")\n }\n\n // MEMORY.md is AGENT-written (Layer 2) — scaffold once, never overwrite.\n const memoryMdPath = join(workspaceDir, \"MEMORY.md\")\n if (!existsSync(memoryMdPath)) {\n writeFileSync(\n memoryMdPath,\n \"# Long-term memory\\n\\nDurable learnings only — format: `## Topic — YYYY-MM-DD` followed by what you learned.\\n\",\n \"utf-8\",\n )\n }\n\n // .claude/settings.json with HMAC-signed PreToolUse hook\n writeAgentHooks({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n // .claude/memory-sync.mjs — signed memory uploader (node, Windows-safe)\n writeMemorySyncScript({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n // .claude/identity-sync.mjs — uploads identity files after onboarding\n writeIdentitySyncScript({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n // .claude/memory-search.mjs — semantic search over synced memory (Phase D)\n writeMemorySearchScript({\n agentId: agent.id,\n hookSecret: agent.hookSecret,\n apiBaseUrl: config.apiBaseUrl,\n workspaceDir,\n })\n\n log(`Provisioned workspace for agent \"${agent.name}\" (${agent.id})`)\n}\n\n/**\n * Return the workspace directory path for an agent.\n */\nexport function agentWorkspaceDir(agentId: string): string {\n return join(AGENTS_DIR, agentId, \"workspace\")\n}\n\n/**\n * Start the periodic agent refresh timer. Call once from the daemon loop.\n * Cleans up the timer when the process exits.\n */\nexport function startAgentRefreshTimer(config: BridgeConfig): void {\n if (refreshTimer !== null) return // already running\n\n refreshTimer = setInterval(() => {\n void refreshAgents(config)\n }, REFRESH_INTERVAL_MS)\n\n // Do not hold the event loop open if all other work is done.\n if (refreshTimer.unref) {\n refreshTimer.unref()\n }\n}\n\n/**\n * Stop the agent refresh timer.\n */\nexport function stopAgentRefreshTimer(): void {\n if (refreshTimer !== null) {\n clearInterval(refreshTimer)\n refreshTimer = null\n }\n}\n","/**\n * session.ts — Persistent-session state for local agents (Phase 2).\n *\n * Agents with localSessionMode === \"persistent\" carry conversation context\n * across work items via `claude -p --resume <sessionId>`. We track the\n * session per agent in agents/<id>/session.json and rotate (start fresh)\n * when the session gets old or has accumulated many runs — the headless\n * equivalent of cortex-os's 71-hour context rotation, defaulting lower\n * because -p turns are denser than interactive chat.\n */\n\nimport { join, dirname } from \"node:path\"\nimport { agentWorkspaceDir } from \"./agents.js\"\nimport { readJsonSafe, writeJsonAtomic } from \"./state.js\"\n\nexport interface AgentSessionState {\n sessionId: string | null\n sessionStartedAt: number | null\n runCount: number\n}\n\nexport interface RotationOptions {\n /** Max session age before rotation. Default 24h. */\n maxAgeMs?: number\n /** Max work items in one session before rotation. Default 40. */\n maxRuns?: number\n}\n\nconst EMPTY_STATE: AgentSessionState = {\n sessionId: null,\n sessionStartedAt: null,\n runCount: 0,\n}\n\n/**\n * Pure rotation decision: start a fresh session when there is no usable\n * session, the session is too old, or it has absorbed too many runs.\n */\nexport function shouldRotateSession(\n state: AgentSessionState,\n nowMs: number,\n opts: RotationOptions = {},\n): boolean {\n const { maxAgeMs = 24 * 60 * 60_000, maxRuns = 40 } = opts\n if (!state.sessionId || state.sessionStartedAt === null) return true\n if (nowMs - state.sessionStartedAt >= maxAgeMs) return true\n if (state.runCount >= maxRuns) return true\n return false\n}\n\nfunction sessionStatePath(agentId: string): string {\n // agents/<id>/session.json — sibling of the workspace dir\n return join(dirname(agentWorkspaceDir(agentId)), \"session.json\")\n}\n\nexport function loadSessionState(agentId: string): AgentSessionState {\n const parsed = readJsonSafe<Partial<AgentSessionState>>(sessionStatePath(agentId))\n if (!parsed) return { ...EMPTY_STATE }\n return {\n sessionId: typeof parsed.sessionId === \"string\" ? parsed.sessionId : null,\n sessionStartedAt:\n typeof parsed.sessionStartedAt === \"number\" ? parsed.sessionStartedAt : null,\n runCount: typeof parsed.runCount === \"number\" ? parsed.runCount : 0,\n }\n}\n\nexport function saveSessionState(agentId: string, state: AgentSessionState): void {\n writeJsonAtomic(sessionStatePath(agentId), state)\n}\n\n/**\n * Record a completed run's session id. Continuing an existing session bumps\n * runCount; a new session id resets the clock.\n */\nexport function recordRunSession(\n agentId: string,\n newSessionId: string | undefined,\n previous: AgentSessionState,\n nowMs: number,\n): AgentSessionState {\n if (!newSessionId) return previous\n const continued = previous.sessionId === newSessionId\n const next: AgentSessionState = continued\n ? { ...previous, runCount: previous.runCount + 1 }\n : { sessionId: newSessionId, sessionStartedAt: nowMs, runCount: 1 }\n saveSessionState(agentId, next)\n return next\n}\n","/**\n * runner.ts — Execute a single work item via Claude Code CLI.\n *\n * Spawns `claude -p --output-format json --max-turns 25 --model <model>\n * --settings <workspace>/.claude/settings.json` and writes the PROMPT VIA\n * STDIN. Two reasons stdin matters:\n * 1. On Windows `claude` is a .cmd shim, which requires shell:true — and\n * with a shell, an arbitrary prompt in argv would be an injection risk.\n * Via stdin the prompt never touches the shell; the remaining argv\n * tokens are fixed strings (model ids, paths) we quote ourselves.\n * 2. Long prompts can exceed Windows' command-line length limit.\n *\n * --settings always points at the agent workspace so the Stride PreToolUse\n * hook applies even when cwd is a project folder elsewhere on disk.\n *\n * Timeout: 15 minutes (SIGTERM then SIGKILL).\n * Phase 1: authMode is always \"claude_login\" (uses local OAuth session).\n * TODO Phase 2: inject ANTHROPIC_API_KEY when authMode === \"org_api_key\".\n */\n\nimport { spawn } from \"node:child_process\"\nimport { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { post } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport { PENDING_REPORTS_PATH } from \"./config.js\"\nimport { agentWorkspaceDir, findAgent } from \"./agents.js\"\nimport { readJsonSafe, writeJsonAtomic } from \"./state.js\"\nimport {\n loadSessionState,\n shouldRotateSession,\n recordRunSession,\n} from \"./session.js\"\nimport { log, logError, logWarn } from \"./log.js\"\n\nconst RUN_TIMEOUT_MS = 15 * 60 * 1000 // 15 minutes\nconst STDOUT_CAP = 10_000 // chars\nconst STDERR_CAP = 4_000 // chars\nconst REPORT_RETRY_COUNT = 3\nconst REPORT_RETRY_BASE_MS = 2_000\n\n/**\n * Resolve how to launch Claude Code.\n *\n * Windows landmine (found in E2E testing): npm installs a `claude` bash-script\n * shim that EATS BACKSLASHES from arguments, silently corrupting paths like\n * --settings C:\\Users\\... → C:Users.... The native installer's claude.exe has\n * no such problem and can be spawned directly without a shell. Preference:\n * 1. ~/.local/bin/claude.exe (native installer) — shell:false\n * 2. `claude` via shell — last-resort fallback for npm-only installs\n */\nexport function resolveClaudeCommand(): { cmd: string; shell: boolean } {\n if (process.platform !== \"win32\") return { cmd: \"claude\", shell: false }\n const nativeExe = join(homedir(), \".local\", \"bin\", \"claude.exe\")\n if (existsSync(nativeExe)) return { cmd: nativeExe, shell: false }\n return { cmd: \"claude\", shell: true }\n}\n\nexport interface WorkItem {\n wakeupId: string\n runId: string\n issueId?: string\n prompt: string\n workingDir?: string\n agent: {\n id: string\n name: string\n model: string\n allowedTools: string[]\n maxBudgetUsd: number\n localSessionMode: string\n }\n}\n\ninterface RunReport {\n runId: string\n wakeupId: string\n failureKind?: string\n status: \"completed\" | \"failed\"\n stdoutExcerpt: string\n stderrExcerpt: string\n error?: string\n exitCode: number | null\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n}\n\n/**\n * Parse the JSON output emitted by `claude -p --output-format json`.\n * Defensive: all fields are optional.\n */\nfunction parseClaudeOutput(raw: string): {\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n isError?: boolean\n resultText?: string\n apiErrorStatus?: number\n} {\n // claude -p --output-format json emits a JSON object on stdout.\n // Find the last complete JSON object in the output (may have log lines before it).\n const trimmed = raw.trim()\n // Try to find the last '{...}' block\n const lastBrace = trimmed.lastIndexOf(\"}\")\n if (lastBrace === -1) return {}\n\n // Walk back to find the matching opening brace\n let depth = 0\n let start = -1\n for (let i = lastBrace; i >= 0; i--) {\n if (trimmed[i] === \"}\") depth++\n else if (trimmed[i] === \"{\") {\n depth--\n if (depth === 0) {\n start = i\n break\n }\n }\n }\n if (start === -1) return {}\n\n try {\n const parsed = JSON.parse(trimmed.slice(start, lastBrace + 1)) as Record<\n string,\n unknown\n >\n const result: {\n costUsd?: number\n tokensInput?: number\n tokensOutput?: number\n sessionId?: string\n isError?: boolean\n resultText?: string\n apiErrorStatus?: number\n } = {}\n\n if (typeof parsed[\"total_cost_usd\"] === \"number\") {\n result.costUsd = parsed[\"total_cost_usd\"]\n }\n if (typeof parsed[\"session_id\"] === \"string\") {\n result.sessionId = parsed[\"session_id\"]\n }\n // Claude Code reports API-level failures (bad model, auth) as exit 0 with\n // is_error: true — those must count as failed runs, not successes.\n if (typeof parsed[\"is_error\"] === \"boolean\") {\n result.isError = parsed[\"is_error\"]\n }\n if (typeof parsed[\"result\"] === \"string\") {\n result.resultText = parsed[\"result\"]\n }\n if (typeof parsed[\"api_error_status\"] === \"number\") {\n result.apiErrorStatus = parsed[\"api_error_status\"]\n }\n\n // usage.input_tokens / usage.output_tokens\n const usage = parsed[\"usage\"]\n if (usage && typeof usage === \"object\") {\n const u = usage as Record<string, unknown>\n if (typeof u[\"input_tokens\"] === \"number\") result.tokensInput = u[\"input_tokens\"]\n if (typeof u[\"output_tokens\"] === \"number\") result.tokensOutput = u[\"output_tokens\"]\n }\n\n return result\n } catch {\n return {}\n }\n}\n\n/**\n * Classify a failed run so the cloud can react appropriately — rate-limited\n * failures must NOT count toward the crash-loop breaker (the agent isn't\n * broken, Anthropic is just busy / the subscription window is exhausted).\n * Pure — unit tested.\n */\nexport function classifyFailure(\n stderr: string,\n resultText: string | undefined,\n apiErrorStatus: number | undefined,\n): \"rate_limited\" | \"environment\" | \"error\" {\n const haystack = `${stderr}\\n${resultText ?? \"\"}`.toLowerCase()\n if (\n apiErrorStatus === 429 ||\n apiErrorStatus === 529 ||\n /rate.?limit|overloaded|too many requests|usage limit|quota|exhausted/.test(haystack)\n ) {\n return \"rate_limited\"\n }\n if (/enoent|not recognized|command not found|login|authenticate|credentials|api key/.test(haystack)) {\n return \"environment\"\n }\n return \"error\"\n}\n\nconst HANDOFF_TIMEOUT_MS = 4 * 60 * 1000\n\n/**\n * One short turn in the OLD session before rotation: write HANDOFF.md.\n * Best-effort — any failure is logged and rotation proceeds.\n */\nasync function runHandoffTurn(\n claude: { cmd: string; shell: boolean },\n agentWorkspace: string,\n settingsPath: string,\n model: string,\n oldSessionId: string,\n): Promise<void> {\n const rawArgs = [\n \"-p\",\n \"--output-format\",\n \"json\",\n \"--max-turns\",\n \"6\",\n \"--model\",\n model,\n \"--settings\",\n settingsPath,\n \"--resume\",\n oldSessionId,\n ]\n const args = claude.shell\n ? rawArgs.map((a) => (/[\\s\"^&|<>%]/.test(a) ? `\"${a.replace(/\"/g, '\"\"')}\"` : a))\n : rawArgs\n\n await new Promise<void>((resolve) => {\n let child: ReturnType<typeof spawn>\n try {\n child = spawn(claude.cmd, args, {\n cwd: agentWorkspace,\n env: { ...process.env },\n shell: claude.shell,\n stdio: [\"pipe\", \"ignore\", \"ignore\"],\n })\n child.stdin?.write(\n \"Your session is about to be rotated (context limit). Write or overwrite \" +\n \"HANDOFF.md in your workspace root RIGHT NOW: what you were working on, \" +\n \"current state, decisions in flight, and the immediate next step. \" +\n \"Max 60 lines. Do nothing else.\",\n )\n child.stdin?.end()\n } catch (err) {\n logWarn(`Handoff turn failed to spawn: ${err instanceof Error ? err.message : String(err)}`)\n resolve()\n return\n }\n const timer = setTimeout(() => {\n logWarn(\"Handoff turn timed out — rotating without handoff doc\")\n try {\n child.kill(\"SIGTERM\")\n } catch {\n // ignore\n }\n }, HANDOFF_TIMEOUT_MS)\n child.on(\"close\", () => {\n clearTimeout(timer)\n log(\"Handoff turn complete\")\n resolve()\n })\n child.on(\"error\", () => {\n clearTimeout(timer)\n resolve()\n })\n })\n}\n\n/**\n * Execute a work item. Returns when the run is complete (or timed out).\n * Reports results to the Stride API, with retry + local persistence on failure.\n */\nexport async function runWorkItem(\n config: BridgeConfig,\n work: WorkItem,\n): Promise<void> {\n const agent = findAgent(work.agent.id)\n const model = agent?.model ?? work.agent.model\n const agentWorkspace = agentWorkspaceDir(work.agent.id)\n\n // Determine working directory: prefer explicit workingDir if it exists.\n let cwd = agentWorkspace\n if (work.workingDir && existsSync(work.workingDir)) {\n cwd = work.workingDir\n } else if (work.workingDir) {\n logWarn(\n `workingDir \"${work.workingDir}\" does not exist; falling back to agent workspace`,\n )\n }\n\n log(`Starting run ${work.runId} (agent \"${work.agent.name}\", model \"${model}\")`)\n log(` cwd: ${cwd}`)\n\n const claude = resolveClaudeCommand()\n // Forward slashes survive every launch path (Windows APIs accept them;\n // bash-based npm shims EAT backslashes — confirmed in E2E testing).\n const settingsPath = join(agentWorkspace, \".claude\", \"settings.json\").replace(/\\\\/g, \"/\")\n // Fixed tokens only — the prompt goes via stdin, never argv.\n const rawArgs = [\n \"-p\",\n \"--output-format\",\n \"json\",\n \"--max-turns\",\n \"25\",\n \"--model\",\n model,\n \"--settings\",\n settingsPath,\n ]\n\n // Persistent mode: resume the agent's ongoing session so context carries\n // across work items; rotate to a fresh session when stale (see session.ts).\n const persistent = work.agent.localSessionMode === \"persistent\"\n let sessionState = loadSessionState(work.agent.id)\n let rotated = false\n if (persistent) {\n if (shouldRotateSession(sessionState, Date.now())) {\n if (sessionState.sessionId) {\n log(`Rotating session for agent \"${work.agent.name}\" (age/run limit reached)`)\n // Context handoff (cortex pattern): give the OLD session one short\n // turn to write HANDOFF.md before we abandon it, so the fresh session\n // doesn't start blind. Fail-soft — rotation proceeds regardless.\n await runHandoffTurn(claude, agentWorkspace, settingsPath, model, sessionState.sessionId)\n rotated = true\n }\n sessionState = { sessionId: null, sessionStartedAt: null, runCount: 0 }\n } else if (sessionState.sessionId) {\n rawArgs.push(\"--resume\", sessionState.sessionId)\n }\n }\n // When falling back to a shell shim, quote args defensively since Node\n // concatenates them without quoting when shell is enabled.\n const args = claude.shell\n ? rawArgs.map((a) => (/[\\s\"^&|<>%]/.test(a) ? `\"${a.replace(/\"/g, '\"\"')}\"` : a))\n : rawArgs\n\n // After a rotation the fresh session starts blind — point it at the\n // handoff doc the old session just wrote.\n const promptText = rotated\n ? `## Session rotated\\nYour previous session ended (context rotation). Read HANDOFF.md and your recent memory/ journal entries before starting.\\n\\n---\\n\\n${work.prompt}`\n : work.prompt\n\n // TODO Phase 2: if config.authMode === \"org_api_key\", inject ANTHROPIC_API_KEY\n const env: NodeJS.ProcessEnv = { ...process.env }\n\n let stdoutBuf = \"\"\n let stderrBuf = \"\"\n let exitCode: number | null = null\n let spawnError: string | undefined\n\n await new Promise<void>((resolve) => {\n let child: ReturnType<typeof spawn>\n\n try {\n child = spawn(claude.cmd, args, {\n cwd,\n env,\n // shell only as a last resort (Windows .cmd shim). Safe because argv\n // contains only our fixed, pre-quoted tokens.\n shell: claude.shell,\n // stdin: pipe — the prompt is delivered via stdin, not argv.\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n child.stdin?.write(promptText)\n child.stdin?.end()\n } catch (err: unknown) {\n spawnError = err instanceof Error ? err.message : String(err)\n resolve()\n return\n }\n\n // 15-minute hard timeout\n const timeoutHandle = setTimeout(() => {\n logWarn(`Run ${work.runId} exceeded timeout — sending SIGTERM`)\n try {\n child.kill(\"SIGTERM\")\n } catch {\n // ignore\n }\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\")\n } catch {\n // ignore\n }\n }, 5_000)\n }, RUN_TIMEOUT_MS)\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutBuf += chunk.toString(\"utf-8\")\n })\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrBuf += chunk.toString(\"utf-8\")\n })\n\n child.on(\"close\", (code) => {\n clearTimeout(timeoutHandle)\n exitCode = code\n resolve()\n })\n\n child.on(\"error\", (err) => {\n clearTimeout(timeoutHandle)\n spawnError = err.message\n resolve()\n })\n })\n\n // Cap output sizes\n const stdoutExcerpt =\n stdoutBuf.length > STDOUT_CAP\n ? stdoutBuf.slice(-STDOUT_CAP)\n : stdoutBuf\n\n const stderrExcerpt =\n stderrBuf.length > STDERR_CAP\n ? stderrBuf.slice(-STDERR_CAP)\n : stderrBuf\n\n // Parse output even on non-zero exit — Claude Code sometimes emits its\n // JSON result before failing, and API-level errors (bad model, auth) come\n // back as exit 0 with is_error: true.\n const parsed = parseClaudeOutput(stdoutBuf)\n const succeeded =\n spawnError === undefined && exitCode === 0 && parsed.isError !== true\n\n // Build the most useful error message available for the dashboard.\n const errorMessage = succeeded\n ? undefined\n : spawnError ??\n (parsed.isError ? parsed.resultText?.slice(0, 1000) : undefined) ??\n (stderrExcerpt.trim() ? stderrExcerpt.trim().slice(-1000) : undefined) ??\n `claude exited with code ${exitCode ?? \"unknown\"}`\n\n const failureKind = succeeded\n ? undefined\n : classifyFailure(stderrExcerpt, parsed.resultText, parsed.apiErrorStatus)\n const {\n isError: _isError,\n resultText: _resultText,\n apiErrorStatus: _apiErrorStatus,\n ...reportFields\n } = parsed\n const report: RunReport = {\n runId: work.runId,\n wakeupId: work.wakeupId,\n status: succeeded ? \"completed\" : \"failed\",\n failureKind,\n stdoutExcerpt,\n stderrExcerpt,\n error: errorMessage,\n exitCode,\n ...reportFields,\n }\n\n log(\n `Run ${work.runId} ${report.status} (exit ${exitCode ?? \"n/a\"}${parsed.costUsd !== undefined ? `, cost $${parsed.costUsd.toFixed(4)}` : \"\"})`,\n )\n\n if (persistent && report.status === \"completed\") {\n recordRunSession(work.agent.id, parsed.sessionId, sessionState, Date.now())\n }\n\n await sendReport(config, report)\n}\n\n/**\n * POST a run report to the server with retry logic.\n * If all retries fail, persist to pending-reports.json for later retry.\n */\nasync function sendReport(\n config: BridgeConfig,\n report: RunReport,\n): Promise<void> {\n const path = `/api/bridge/v1/runs/${report.runId}`\n\n for (let attempt = 1; attempt <= REPORT_RETRY_COUNT; attempt++) {\n try {\n await post(config, path, report)\n log(`Reported run ${report.runId} (attempt ${attempt})`)\n // On success, remove from pending if it was there from a previous attempt.\n removePendingReport(report.runId)\n return\n } catch (err: unknown) {\n logError(`Failed to report run ${report.runId} (attempt ${attempt})`, err)\n if (attempt < REPORT_RETRY_COUNT) {\n const delay = REPORT_RETRY_BASE_MS * Math.pow(2, attempt - 1)\n await sleep(delay)\n }\n }\n }\n\n // All retries exhausted — persist locally for next poll iteration.\n logWarn(`Persisting failed report for run ${report.runId} to pending-reports.json`)\n appendPendingReport(report)\n}\n\n/**\n * Retry any pending reports from previous failures.\n * Called each poll iteration.\n */\nexport async function retryPendingReports(config: BridgeConfig): Promise<void> {\n const pending = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH)\n if (!pending || pending.length === 0) return\n\n log(`Retrying ${pending.length} pending report(s)`)\n for (const report of pending) {\n await sendReport(config, report)\n }\n}\n\nfunction appendPendingReport(report: RunReport): void {\n const existing = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH) ?? []\n // Avoid duplicates\n const deduped = existing.filter((r) => r.runId !== report.runId)\n deduped.push(report)\n writeJsonAtomic(PENDING_REPORTS_PATH, deduped)\n}\n\nfunction removePendingReport(runId: string): void {\n const existing = readJsonSafe<RunReport[]>(PENDING_REPORTS_PATH)\n if (!existing || existing.length === 0) return\n const filtered = existing.filter((r) => r.runId !== runId)\n if (filtered.length !== existing.length) {\n writeJsonAtomic(PENDING_REPORTS_PATH, filtered)\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","/**\n * quota.ts — Claude subscription quota snapshot (Phase 2).\n *\n * In claude_login mode the daemon runs on the customer's Claude Code OAuth\n * session, which is rate-limited in 5-hour and 7-day windows. We read the\n * OAuth token from ~/.claude/.credentials.json and query Anthropic's usage\n * endpoint, reporting \"% remaining\" in heartbeats so the dashboard can show\n * a capacity chip per bridge. Pattern ported from cortex-os dashboard\n * lib/quota.ts (MIT).\n *\n * Everything here is fail-soft: no credentials / API errors → null quota.\n */\n\nimport { join } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { readJsonSafe } from \"./state.js\"\nimport { logWarn } from \"./log.js\"\n\nexport interface QuotaSnapshot {\n fiveHourRemainingPct: number | null\n sevenDayRemainingPct: number | null\n fetchedAt: string\n}\n\ninterface OauthUsageResponse {\n five_hour?: { utilization?: number }\n seven_day?: { utilization?: number }\n}\n\nconst USAGE_URL = \"https://api.anthropic.com/api/oauth/usage\"\nconst CACHE_TTL_MS = 60_000\nconst FETCH_TIMEOUT_MS = 5_000\n\n/** Pure: utilization (0..1) → whole-number percent remaining, clamped. */\nexport function remainingPct(utilization: unknown): number | null {\n if (typeof utilization !== \"number\" || !Number.isFinite(utilization)) return null\n return Math.max(0, Math.min(100, Math.round((1 - utilization) * 100)))\n}\n\n/** Pure: map the usage API response to a snapshot. */\nexport function parseUsageResponse(body: OauthUsageResponse, nowIso: string): QuotaSnapshot {\n return {\n fiveHourRemainingPct: remainingPct(body.five_hour?.utilization),\n sevenDayRemainingPct: remainingPct(body.seven_day?.utilization),\n fetchedAt: nowIso,\n }\n}\n\nfunction readOauthToken(): string | null {\n if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return process.env.CLAUDE_CODE_OAUTH_TOKEN\n const creds = readJsonSafe<{ claudeAiOauth?: { accessToken?: string } }>(\n join(homedir(), \".claude\", \".credentials.json\"),\n )\n return creds?.claudeAiOauth?.accessToken ?? null\n}\n\nlet cached: { snapshot: QuotaSnapshot; at: number } | null = null\nlet warnedNoToken = false\n\n/**\n * Fetch the current quota snapshot, cached for 60s. Returns null when no\n * OAuth token is available (org_api_key mode, or Claude not logged in).\n */\nexport async function getQuotaSnapshot(): Promise<QuotaSnapshot | null> {\n if (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.snapshot\n\n const token = readOauthToken()\n if (!token) {\n if (!warnedNoToken) {\n logWarn(\"No Claude OAuth token found — quota reporting disabled\")\n warnedNoToken = true\n }\n return null\n }\n\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS)\n const res = await fetch(USAGE_URL, {\n headers: { Authorization: `Bearer ${token}` },\n signal: controller.signal,\n })\n clearTimeout(timer)\n if (!res.ok) return cached?.snapshot ?? null\n\n const body = (await res.json()) as OauthUsageResponse\n const snapshot = parseUsageResponse(body, new Date().toISOString())\n cached = { snapshot, at: Date.now() }\n return snapshot\n } catch {\n // Stale-but-present beats nothing; otherwise null.\n return cached?.snapshot ?? null\n }\n}\n","/**\n * poller.ts — Main daemon loop for `stride-bridge start`.\n *\n * Polls /api/bridge/v1/work every 2.5 seconds.\n * Handles one work item at a time (single-flight Phase 1).\n * Sends heartbeat every 60 seconds independently.\n * Exponential backoff on fetch errors: 2.5s → 5 → 10 → 30s cap.\n * Posts daemon_started on startup and daemon_stopping on SIGINT/SIGTERM.\n */\n\nimport { platform, release } from \"node:os\"\nimport { post, postBestEffort } from \"./api.js\"\nimport type { BridgeConfig } from \"./config.js\"\nimport {\n refreshAgents,\n startAgentRefreshTimer,\n stopAgentRefreshTimer,\n} from \"./agents.js\"\nimport { runWorkItem, retryPendingReports } from \"./runner.js\"\nimport type { WorkItem } from \"./runner.js\"\nimport { getQuotaSnapshot } from \"./quota.js\"\nimport type { QuotaSnapshot } from \"./quota.js\"\nimport { VERSION } from \"./version.js\"\nimport { log, logError, logWarn } from \"./log.js\"\n\nconst POLL_INTERVAL_MS = 2_500\nconst HEARTBEAT_INTERVAL_MS = 60_000\nconst BACKOFF_STEPS_MS = [2_500, 5_000, 10_000, 30_000]\n\ninterface WorkResponse {\n work: WorkItem | null\n}\n\ninterface HeartbeatPayload {\n daemonVersion: string\n osInfo: { platform: string; release: string }\n agents: unknown[]\n quota?: QuotaSnapshot\n}\n\nlet running = false\n\n/**\n * Start the daemon loop. Blocks until SIGINT/SIGTERM.\n */\nexport async function startDaemon(config: BridgeConfig): Promise<void> {\n running = true\n\n // Register signal handlers first so they fire even if startup work fails.\n const shutdown = async (signal: string) => {\n if (!running) return\n running = false\n log(`Received ${signal} — shutting down gracefully`)\n stopAgentRefreshTimer()\n await postBestEffort(config, \"/api/bridge/v1/events\", {\n kind: \"daemon_stopping\",\n message: `Daemon stopping (${signal})`,\n daemonVersion: VERSION,\n })\n process.exit(0)\n }\n\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\"))\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"))\n\n // Announce startup\n await postBestEffort(config, \"/api/bridge/v1/events\", {\n kind: \"daemon_started\",\n message: \"Daemon started\",\n daemonVersion: VERSION,\n osInfo: { platform: platform(), release: release() },\n })\n\n log(`Daemon started (v${VERSION}, bridge ${config.bridgeId})`)\n\n // Fetch agents on startup\n await refreshAgents(config)\n startAgentRefreshTimer(config)\n\n // Heartbeat timer — runs independently of the poll loop\n const sendHeartbeat = async () => {\n const payload: HeartbeatPayload = {\n daemonVersion: VERSION,\n osInfo: { platform: platform(), release: release() },\n agents: [],\n }\n const quota = await getQuotaSnapshot()\n if (quota) payload.quota = quota\n try {\n await post(config, \"/api/bridge/v1/heartbeat\", payload)\n } catch (err: unknown) {\n logWarn(`Heartbeat failed: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n // First heartbeat immediately so the dashboard flips to connected fast.\n void sendHeartbeat()\n const heartbeatTimer = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS)\n\n if (heartbeatTimer.unref) heartbeatTimer.unref()\n\n // Main poll loop\n let backoffIndex = 0\n\n while (running) {\n // Retry any persisted failed reports before fetching new work\n try {\n await retryPendingReports(config)\n } catch {\n // Non-fatal\n }\n\n try {\n const data = await post<WorkResponse>(config, \"/api/bridge/v1/work\", {})\n backoffIndex = 0 // reset backoff on success\n\n if (data.work) {\n log(`Received work item: run ${data.work.runId}`)\n // Single-flight: await the run before polling again\n try {\n await runWorkItem(config, data.work)\n } catch (err: unknown) {\n logError(`Unhandled error in runWorkItem for run ${data.work.runId}`, err)\n }\n // Poll immediately after completing a run (don't wait full interval)\n continue\n }\n } catch (err: unknown) {\n const delay = BACKOFF_STEPS_MS[Math.min(backoffIndex, BACKOFF_STEPS_MS.length - 1)]\n logError(\n `Poll error (backoff ${delay}ms)`,\n err,\n )\n backoffIndex = Math.min(backoffIndex + 1, BACKOFF_STEPS_MS.length - 1)\n await sleep(delay)\n continue\n }\n\n await sleep(POLL_INTERVAL_MS)\n }\n\n clearInterval(heartbeatTimer)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","/**\n * cli.ts — Stride Bridge CLI entry point.\n *\n * Commands:\n * connect <pairingCode> [--api-url <url>] — Pair this machine with StrideOps\n * start — Start the daemon loop (foreground)\n * status — Print config + connectivity info\n * doctor — Run prerequisite checks\n */\n\nimport { Command } from \"commander\"\nimport { VERSION } from \"./version.js\"\nimport { runPair } from \"./pair.js\"\nimport { startDaemon } from \"./poller.js\"\nimport { readConfig, requireConfig, CONFIG_PATH, isConfigured } from \"./config.js\"\nimport { log } from \"./log.js\"\nimport { execSync } from \"node:child_process\"\nimport { existsSync, accessSync, constants, mkdirSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { post } from \"./api.js\"\n\nconst DEFAULT_API_URL = \"https://app.strideops.ai\"\n\nconst program = new Command()\n\nprogram\n .name(\"stride-bridge\")\n .description(\"Stride Bridge daemon — run StrideOps Build agents locally\")\n .version(VERSION)\n\n// ---------------------------------------------------------------------------\n// connect\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"connect <pairingCode>\")\n .description(\"Pair this machine with StrideOps using a one-time pairing code\")\n .option(\n \"--api-url <url>\",\n \"StrideOps API base URL\",\n DEFAULT_API_URL,\n )\n .action(async (pairingCode: string, opts: { apiUrl: string }) => {\n await runPair(pairingCode, opts.apiUrl)\n })\n\n// ---------------------------------------------------------------------------\n// start\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"start\")\n .description(\"Start the daemon (foreground; press Ctrl+C to stop)\")\n .action(async () => {\n const config = requireConfig()\n log(`Starting daemon with bridge \"${config.bridgeName}\" (${config.bridgeId})`)\n await startDaemon(config)\n })\n\n// ---------------------------------------------------------------------------\n// status\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"status\")\n .description(\"Print current configuration and server connectivity\")\n .action(async () => {\n const config = readConfig()\n\n console.log(\"=== Stride Bridge Status ===\")\n console.log()\n\n if (!config) {\n console.log(\"Status : NOT CONNECTED\")\n console.log(`Config path : ${CONFIG_PATH}`)\n console.log()\n console.log(\"Run `stride-bridge connect <PAIRING_CODE>` to get started.\")\n return\n }\n\n console.log(`Status : CONNECTED`)\n console.log(`Bridge name : ${config.bridgeName}`)\n console.log(`Bridge ID : ${config.bridgeId}`)\n console.log(`Org ID : ${config.orgId}`)\n console.log(`API URL : ${config.apiBaseUrl}`)\n console.log(`Auth mode : ${config.authMode}`)\n console.log(`Config path : ${CONFIG_PATH}`)\n console.log()\n\n // Check server reachability\n process.stdout.write(\"Server : checking... \")\n try {\n await post(config, \"/api/bridge/v1/heartbeat\", {\n daemonVersion: VERSION,\n osInfo: {},\n agents: [],\n })\n console.log(\"REACHABLE\")\n } catch (err: unknown) {\n console.log(\n `UNREACHABLE (${err instanceof Error ? err.message : String(err)})`,\n )\n }\n console.log()\n })\n\n// ---------------------------------------------------------------------------\n// autostart\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"autostart <action>\")\n .description(\"Manage start-at-logon: install | remove | status (Windows Task Scheduler)\")\n .action(async (action: string) => {\n const { autostartInstall, autostartRemove, autostartStatus } = await import(\"./autostart.js\")\n if (action === \"install\") autostartInstall()\n else if (action === \"remove\") autostartRemove()\n else if (action === \"status\") autostartStatus()\n else {\n console.error(`Unknown action \"${action}\" — use install, remove, or status`)\n process.exitCode = 1\n }\n })\n\n// ---------------------------------------------------------------------------\n// doctor\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"doctor\")\n .description(\"Check all prerequisites for running the bridge daemon\")\n .action(async () => {\n console.log(\"=== Stride Bridge Doctor ===\")\n console.log()\n\n let allGood = true\n\n // 1. Node version\n const nodeVersion = process.versions.node\n const [major] = nodeVersion.split(\".\").map(Number)\n const nodeOk = major >= 20\n printCheck(\n \"Node.js >= 20\",\n nodeOk,\n `Found v${nodeVersion}`,\n `Found v${nodeVersion} — upgrade to Node.js 20+`,\n )\n if (!nodeOk) allGood = false\n\n // 2. claude CLI on PATH\n let claudeVersion = \"\"\n let claudeOnPath = false\n try {\n claudeVersion = execSync(\"claude --version\", {\n encoding: \"utf-8\",\n timeout: 5_000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim()\n claudeOnPath = true\n } catch {\n claudeOnPath = false\n }\n printCheck(\n \"claude CLI on PATH\",\n claudeOnPath,\n claudeVersion || \"found\",\n \"claude not found — install Claude Code CLI from https://docs.anthropic.com/claude-code\",\n )\n if (!claudeOnPath) allGood = false\n\n // 3. Claude authenticated (config presence heuristic)\n let claudeAuthed = false\n let claudeAuthNote = \"\"\n if (claudeOnPath) {\n // Claude Code stores its config under ~/.claude/ or %APPDATA%/claude/\n const possiblePaths = [\n join(homedir(), \".claude\", \"settings.json\"),\n join(homedir(), \".config\", \"claude\", \"settings.json\"),\n join(\n process.env[\"APPDATA\"] ?? join(homedir(), \"AppData\", \"Roaming\"),\n \"Claude\",\n \"settings.json\",\n ),\n ]\n claudeAuthed = possiblePaths.some(existsSync)\n claudeAuthNote = claudeAuthed\n ? \"config found\"\n : \"no config found — run `claude` to authenticate\"\n } else {\n claudeAuthNote = \"skipped (claude not on PATH)\"\n }\n printCheck(\n \"Claude authenticated\",\n claudeAuthed,\n claudeAuthNote,\n claudeAuthNote,\n )\n if (!claudeAuthed && claudeOnPath) allGood = false\n\n // 4. Config file valid\n const configured = isConfigured()\n const configData = readConfig()\n const configValid = configured && configData !== null\n printCheck(\n \"Bridge config\",\n configValid,\n configValid\n ? `${CONFIG_PATH} (bridge: ${configData?.bridgeName})`\n : configured\n ? `${CONFIG_PATH} exists but could not be parsed`\n : \"not found — run `stride-bridge connect <CODE>`\",\n configured\n ? `${CONFIG_PATH} could not be parsed — re-run connect`\n : \"not found — run `stride-bridge connect <CODE>`\",\n )\n if (!configValid) allGood = false\n\n // 5. Server reachable\n if (configData) {\n process.stdout.write(\"[ ] StrideOps server reachable ... \")\n try {\n await post(configData, \"/api/bridge/v1/heartbeat\", {\n daemonVersion: VERSION,\n osInfo: {},\n agents: [],\n })\n clearLine()\n printCheck(\"StrideOps server reachable\", true, configData.apiBaseUrl, \"\")\n } catch (err: unknown) {\n clearLine()\n printCheck(\n \"StrideOps server reachable\",\n false,\n \"\",\n `${configData.apiBaseUrl} — ${err instanceof Error ? err.message : String(err)}`,\n )\n allGood = false\n }\n } else {\n printCheck(\n \"StrideOps server reachable\",\n false,\n \"\",\n \"skipped (not configured)\",\n )\n }\n\n // 6. Workspace dir writable\n const bridgeDir = join(homedir(), \".stride-bridge\")\n try {\n mkdirSync(bridgeDir, { recursive: true })\n accessSync(bridgeDir, constants.W_OK)\n printCheck(\"Bridge workspace writable\", true, bridgeDir, \"\")\n } catch {\n printCheck(\n \"Bridge workspace writable\",\n false,\n \"\",\n `${bridgeDir} is not writable`,\n )\n allGood = false\n }\n\n console.log()\n if (allGood) {\n console.log(\"All checks passed. Run `stride-bridge start` to launch the daemon.\")\n } else {\n console.log(\n \"Some checks failed. Fix the issues above, then re-run `stride-bridge doctor`.\",\n )\n process.exit(1)\n }\n })\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction printCheck(\n label: string,\n ok: boolean,\n successNote: string,\n failureNote: string,\n): void {\n const icon = ok ? \"[ok]\" : \"[!!]\"\n const note = ok ? successNote : failureNote\n console.log(`${icon} ${label}${note ? \" — \" + note : \"\"}`)\n}\n\nfunction clearLine(): void {\n // Move cursor up one line and clear it (for the \"checking...\" line)\n process.stdout.write(\"\\r\\x1b[K\")\n}\n\n// ---------------------------------------------------------------------------\n// Parse\n// ---------------------------------------------------------------------------\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(\n `[stride-bridge] Fatal: ${err instanceof Error ? err.message : String(err)}`,\n )\n process.exit(1)\n})\n"]}
|