runtape 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -18,15 +18,24 @@ npm install -g runtape
18
18
 
19
19
  ## Get started
20
20
 
21
+ One command walks you through the whole setup:
22
+
23
+ ```bash
24
+ runtape setup
25
+ ```
26
+
27
+ It asks for your backend (defaults to `https://runtape.dev`), opens the dashboard so you can copy your API key, then installs the Claude Code hooks.
28
+
29
+ After setup, run Claude Code normally and your sessions stream to the dashboard.
30
+
21
31
  ```bash
22
- runtape login # paste the API key from your runtape.dev dashboard
23
- runtape install # add hooks to ~/.claude/settings.json
24
- # …run Claude Code normally…
25
- runtape runs # open your dashboard in the browser
26
32
  runtape status # buffer state, server reachability, flusher PID
33
+ runtape runs # open your dashboard in the browser
27
34
  runtape uninstall # remove the hooks when you're done
28
35
  ```
29
36
 
37
+ If you prefer the granular commands instead of the wizard, `runtape login` and `runtape install` are still available — `setup` is just a convenience that chains them.
38
+
30
39
  ## How it works
31
40
 
32
41
  `runtape install` adds entries to `~/.claude/settings.json` so that Claude Code fires `runtape push --event <HookName>` on every relevant hook event. When installed under any `node_modules` tree (npm-global, pnpm-workspace, etc.), the entry uses the bare command name `runtape` so it survives package upgrades; when run from source (e.g. `tsx bin/runtape.ts` during local development) it uses the absolute path from `process.argv[1]`. Override with the `RUNTAPE_CLI_BIN` env var if you need a specific path. Each invocation appends one validated JSON line to `~/.runtape/buffer/<session_id>.ndjson` (sub-10ms) and lazily spawns a detached flusher daemon. The daemon batches events (up to 100 per POST) and ships them to the backend with exponential backoff on transient failures. It exits after 30s of idle.
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ import {
3
3
  } from "./chunk-Q4RSBPGN.js";
4
4
 
5
5
  // src/index.ts
6
+ import { readFileSync } from "fs";
7
+ import { fileURLToPath as fileURLToPath2 } from "url";
8
+ import { dirname as dirname6, join as join2 } from "path";
6
9
  import { Command } from "commander";
7
10
 
8
11
  // src/commands/login.ts
@@ -609,6 +612,119 @@ ${url}
609
612
  return 0;
610
613
  }
611
614
 
615
+ // src/commands/setup.ts
616
+ import { spawn as spawn3 } from "child_process";
617
+ import { platform as platform2 } from "process";
618
+ import { createInterface as createInterface3 } from "readline/promises";
619
+ import { stdin as input3, stdout as output3 } from "process";
620
+ function openCommand2() {
621
+ if (platform2 === "darwin") return { cmd: "open", args: [] };
622
+ if (platform2 === "win32") return { cmd: "cmd", args: ["/c", "start", ""] };
623
+ return { cmd: "xdg-open", args: [] };
624
+ }
625
+ function openInBrowser(url) {
626
+ const { cmd, args } = openCommand2();
627
+ const child = spawn3(cmd, [...args, url], { stdio: "ignore", detached: true });
628
+ child.on("error", () => {
629
+ });
630
+ child.unref();
631
+ }
632
+ async function promptYesNo(rl, question, defaultYes) {
633
+ const suffix = defaultYes ? "(Y/n)" : "(y/N)";
634
+ const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
635
+ if (answer === "") return defaultYes;
636
+ return answer === "y" || answer === "yes";
637
+ }
638
+ async function setupCommand(opts) {
639
+ const rl = createInterface3({ input: input3, output: output3 });
640
+ try {
641
+ process.stdout.write("\nRuntape setup\n");
642
+ process.stdout.write("Let's get your Claude Code runs captured.\n\n");
643
+ const existing = await readConfig();
644
+ if (existing) {
645
+ process.stdout.write(`Already logged in to ${existing.server_url}
646
+ `);
647
+ process.stdout.write(`API key: ${existing.api_key.slice(0, 8)}\u2026${existing.api_key.slice(-4)}
648
+ `);
649
+ const reconfigure = await promptYesNo(rl, "Reconfigure?", false);
650
+ if (!reconfigure) {
651
+ process.stdout.write("\nKeeping existing credentials. Moving on to hook install.\n");
652
+ return await installAndFinish(rl, existing.server_url);
653
+ }
654
+ }
655
+ const suggestedUrl = defaultServerUrl();
656
+ process.stdout.write(`Step 1/3 \u2014 Backend
657
+ `);
658
+ const urlInput = (await rl.question(`Server URL [${suggestedUrl}]: `)).trim();
659
+ const serverUrl = urlInput === "" ? suggestedUrl : urlInput;
660
+ const dashboardUrl = `${serverUrl.replace(/\/$/, "")}/dashboard`;
661
+ process.stdout.write(`
662
+ Step 2/3 \u2014 API key
663
+ `);
664
+ process.stdout.write(`Your API key lives in the dashboard:
665
+ ${dashboardUrl}
666
+ `);
667
+ if (!opts.noBrowser) {
668
+ openInBrowser(dashboardUrl);
669
+ process.stdout.write("(opened in your browser)\n");
670
+ }
671
+ const apiKey = (await rl.question("\nPaste your API key (rtk_\u2026): ")).trim();
672
+ const validation = Config.shape.api_key.safeParse(apiKey);
673
+ if (!validation.success) {
674
+ process.stderr.write(`
675
+ Invalid API key format. Expected rtk_<64 hex chars>.
676
+ `);
677
+ return 2;
678
+ }
679
+ process.stdout.write(`Validating against ${serverUrl}\u2026
680
+ `);
681
+ const ping = await pingProject(serverUrl, apiKey);
682
+ if (!ping.ok) {
683
+ process.stderr.write(
684
+ `
685
+ Login failed: ${ping.status === 401 ? "unknown API key" : ping.detail ?? "server unreachable"}
686
+ `
687
+ );
688
+ return 1;
689
+ }
690
+ await writeConfig({ api_key: apiKey, server_url: serverUrl });
691
+ process.stdout.write("Credentials saved.\n");
692
+ return await installAndFinish(rl, serverUrl);
693
+ } finally {
694
+ rl.close();
695
+ }
696
+ }
697
+ async function installAndFinish(rl, serverUrl) {
698
+ process.stdout.write(`
699
+ Step 3/3 \u2014 Claude Code hooks
700
+ `);
701
+ const install = await promptYesNo(
702
+ rl,
703
+ "Install Runtape hooks into ~/.claude/settings.json now?",
704
+ true
705
+ );
706
+ if (!install) {
707
+ process.stdout.write("\nSkipped hook install. Run `runtape install` when ready.\n");
708
+ return 0;
709
+ }
710
+ const cliBinPath = resolveCliBinPath();
711
+ const result = await installHooks("user", cliBinPath);
712
+ process.stdout.write(`Updated ${result.settingsPath}
713
+ `);
714
+ if (result.addedHooks.length === 0) {
715
+ process.stdout.write("Hooks already installed \u2014 nothing changed.\n");
716
+ } else {
717
+ process.stdout.write(`Added: ${result.addedHooks.join(", ")}
718
+ `);
719
+ }
720
+ process.stdout.write("\nSetup complete.\n");
721
+ process.stdout.write('Now run `claude -p "any prompt"` from any directory and watch the run land at:\n');
722
+ process.stdout.write(` ${serverUrl.replace(/\/$/, "")}/dashboard
723
+
724
+ `);
725
+ return 0;
726
+ }
727
+
612
728
  // src/lib/flusher.ts
613
729
  import { appendFile as appendFile2, mkdir as mkdir5, readFile as readFile6, unlink as unlink3, writeFile as writeFile5 } from "fs/promises";
614
730
  import { dirname as dirname5 } from "path";
@@ -730,6 +846,8 @@ async function runFlusher() {
730
846
  }
731
847
 
732
848
  // src/index.ts
849
+ var pkgPath = join2(dirname6(fileURLToPath2(import.meta.url)), "..", "package.json");
850
+ var PKG_VERSION = JSON.parse(readFileSync(pkgPath, "utf8")).version;
733
851
  if (process.argv.includes("--internal-flusher")) {
734
852
  void runFlusher().then(
735
853
  () => process.exit(0),
@@ -740,7 +858,8 @@ if (process.argv.includes("--internal-flusher")) {
740
858
  );
741
859
  } else {
742
860
  const program = new Command();
743
- program.name("runtape").description("Flight recorder for AI coding agents.").version("0.1.2");
861
+ program.name("runtape").description("Flight recorder for AI coding agents.").version(PKG_VERSION);
862
+ program.command("setup").description("Guided onboarding: login + install hooks + verify in one flow.").option("--no-browser", "Do not open the dashboard URL in a browser").action(async (opts) => process.exit(await setupCommand(opts)));
744
863
  program.command("login").description("Paste your API key from runtape.dev/dashboard and save it locally.").option("-k, --key <key>", "API key (skip the prompt)").option("-s, --server-url <url>", "Override server URL").action(async (opts) => process.exit(await loginCommand(opts)));
745
864
  program.command("logout").description("Remove saved credentials.").action(async () => process.exit(await logoutCommand()));
746
865
  program.command("install").description("Add Runtape hooks to ~/.claude/settings.json (or project-local with --project).").option("--project", "Install into ./.claude/settings.json instead of user-level").option("-y, --yes", "Skip the confirmation prompt").action(async (opts) => process.exit(await installCommand(opts)));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/paths.ts","../src/lib/api.ts","../src/commands/logout.ts","../src/commands/install.ts","../src/lib/hooks-installer.ts","../src/lib/hook-mapping.ts","../src/lib/cli-bin.ts","../src/commands/uninstall.ts","../src/commands/push.ts","../src/lib/sequence.ts","../src/lib/buffer.ts","../src/commands/status.ts","../src/commands/runs.ts","../src/lib/flusher.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { installCommand } from './commands/install.js';\nimport { uninstallCommand } from './commands/uninstall.js';\nimport { pushCommand } from './commands/push.js';\nimport { statusCommand } from './commands/status.js';\nimport { runsCommand } from './commands/runs.js';\nimport { runFlusher } from './lib/flusher.js';\n\n// Internal flag for the detached flusher daemon — not a user-facing subcommand.\n// Goes before commander.parse so we can short-circuit before commander sees argv.\nif (process.argv.includes('--internal-flusher')) {\n void runFlusher().then(\n () => process.exit(0),\n (err: unknown) => {\n console.error(err);\n process.exit(1);\n },\n );\n} else {\n const program = new Command();\n\n program\n .name('runtape')\n .description('Flight recorder for AI coding agents.')\n .version('0.1.2');\n\n program\n .command('login')\n .description('Paste your API key from runtape.dev/dashboard and save it locally.')\n .option('-k, --key <key>', 'API key (skip the prompt)')\n .option('-s, --server-url <url>', 'Override server URL')\n .action(async (opts) => process.exit(await loginCommand(opts)));\n\n program\n .command('logout')\n .description('Remove saved credentials.')\n .action(async () => process.exit(await logoutCommand()));\n\n program\n .command('install')\n .description('Add Runtape hooks to ~/.claude/settings.json (or project-local with --project).')\n .option('--project', 'Install into ./.claude/settings.json instead of user-level')\n .option('-y, --yes', 'Skip the confirmation prompt')\n .action(async (opts) => process.exit(await installCommand(opts)));\n\n program\n .command('uninstall')\n .description('Remove Runtape hooks from Claude settings.')\n .option('--project', 'Operate on ./.claude/settings.json instead of user-level')\n .action(async (opts) => process.exit(await uninstallCommand(opts)));\n\n program\n .command('push')\n .description('Internal: invoked by Claude Code hooks. Reads stdin and buffers an event.')\n .requiredOption('--event <name>', 'Claude hook event name (SessionStart, PostToolUse, …)')\n .action(async (opts) => process.exit(await pushCommand(opts)));\n\n program\n .command('status')\n .description('Show current login, buffer state, and server reachability.')\n .action(async () => process.exit(await statusCommand()));\n\n program\n .command('runs')\n .description('Open your Runtape dashboard in the default browser.')\n .action(async () => process.exit(await runsCommand()));\n\n program.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\n\nexport async function loginCommand(opts: { key?: string; serverUrl?: string }): Promise<number> {\n const serverUrl = opts.serverUrl ?? defaultServerUrl();\n\n let apiKey = opts.key;\n if (!apiKey) {\n const rl = createInterface({ input, output });\n apiKey = (await rl.question('Paste your Runtape API key (rtk_…): ')).trim();\n rl.close();\n }\n\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`Invalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(`Login failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`);\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write(`Saved. You can now run: runtape install\\n`);\n return 0;\n}\n","import { chmod, mkdir, readFile, writeFile, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { z } from 'zod';\nimport { paths } from './paths.js';\n\nexport const Config = z.object({\n api_key: z.string().regex(/^rtk_[a-f0-9]{64}$/, 'api_key must be rtk_ followed by 64 hex chars'),\n server_url: z.string().url(),\n});\n\nexport type Config = z.infer<typeof Config>;\n\nconst DEFAULT_SERVER_URL = process.env.RUNTAPE_API_URL ?? 'https://runtape.dev';\n\nexport async function readConfig(): Promise<Config | null> {\n let raw: string;\n try {\n raw = await readFile(paths.config, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const parsed = Config.safeParse(JSON.parse(raw));\n if (!parsed.success) {\n throw new Error(`Invalid config at ${paths.config}: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\nexport async function writeConfig(c: Config): Promise<void> {\n await mkdir(dirname(paths.config), { recursive: true });\n await writeFile(paths.config, JSON.stringify(c, null, 2) + '\\n', { mode: 0o600 });\n // writeFile's `mode` is only applied on create. Re-chmod explicitly so a re-login\n // tightens permissions if the file was previously loosened (e.g. `chmod 644`).\n await chmod(paths.config, 0o600);\n}\n\nexport async function clearConfig(): Promise<void> {\n try {\n await unlink(paths.config);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport function defaultServerUrl(): string {\n return DEFAULT_SERVER_URL;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst RUNTAPE_HOME = process.env.RUNTAPE_HOME ?? join(homedir(), '.runtape');\n\nexport const paths = {\n home: RUNTAPE_HOME,\n config: join(RUNTAPE_HOME, 'config.json'),\n bufferDir: join(RUNTAPE_HOME, 'buffer'),\n seqDir: join(RUNTAPE_HOME, 'seq'),\n flusherPid: join(RUNTAPE_HOME, 'flusher.pid'),\n flusherLog: join(RUNTAPE_HOME, 'flusher.log'),\n bufferFile: (sessionId: string) => join(RUNTAPE_HOME, 'buffer', `${sessionId}.ndjson`),\n seqFile: (sessionId: string) => join(RUNTAPE_HOME, 'seq', sessionId),\n claudeSettings: (scope: 'user' | 'project') =>\n scope === 'user' ? join(homedir(), '.claude', 'settings.json') : join(process.cwd(), '.claude', 'settings.json'),\n claudeSettingsBackup: (scope: 'user' | 'project') =>\n scope === 'user'\n ? join(homedir(), '.claude', 'settings.json.runtape-backup')\n : join(process.cwd(), '.claude', 'settings.json.runtape-backup'),\n};\n","import type { RuntapeEvent } from '../types.js';\n\nexport type PostResult =\n | { ok: true; accepted: number; errors: Array<{ index: number; reason: string }> }\n | { ok: false; status: number; error: string; retryable: boolean };\n\nfunction isRetryableStatus(status: number): boolean {\n // 408 timeout, 425 too-early, 429 rate-limited, 5xx — retry. 4xx — drop (poison).\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nexport async function postEvents(\n serverUrl: string,\n apiKey: string,\n events: RuntapeEvent[],\n): Promise<PostResult> {\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n } catch (err) {\n // Network failure (ENOTFOUND, ECONNREFUSED, fetch abort). Retryable.\n return {\n ok: false,\n status: 0,\n error: err instanceof Error ? err.message : String(err),\n retryable: true,\n };\n }\n\n if (response.ok) {\n const body = (await response.json()) as {\n accepted: number;\n errors: Array<{ index: number; reason: string }>;\n };\n return { ok: true, accepted: body.accepted, errors: body.errors };\n }\n\n let detail = '';\n try {\n detail = await response.text();\n } catch {\n /* ignore */\n }\n return {\n ok: false,\n status: response.status,\n error: detail || response.statusText,\n retryable: isRetryableStatus(response.status),\n };\n}\n\nexport async function pingProject(serverUrl: string, apiKey: string): Promise<{ ok: boolean; status: number; detail?: string }> {\n // We don't have a dedicated /me endpoint yet (we may add one in Task 3b).\n // For now, \"ping\" = POST /api/v1/events with an empty events array and inspect the response.\n // Server returns 400 invalid_request_body (events must be ≥1) on success — that proves auth works.\n // 401 = bad key; 5xx = server problem.\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events: [] }),\n });\n } catch (err) {\n return { ok: false, status: 0, detail: err instanceof Error ? err.message : String(err) };\n }\n\n if (response.status === 400) return { ok: true, status: 400 };\n if (response.status === 401) return { ok: false, status: 401, detail: 'invalid api key' };\n return { ok: false, status: response.status, detail: response.statusText };\n}\n","import { clearConfig } from '../lib/config.js';\n\nexport async function logoutCommand(): Promise<number> {\n await clearConfig();\n process.stdout.write('Logged out. Config removed.\\n');\n return 0;\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { readConfig } from '../lib/config.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nexport async function installCommand(opts: { project?: boolean; yes?: boolean }): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const cliBinPath = resolveCliBinPath();\n\n if (!opts.yes) {\n const rl = createInterface({ input, output });\n const answer = (await rl.question(`Install Runtape hooks into ${scope} settings (yes/no)? `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n process.stdout.write('Aborted.\\n');\n return 0;\n }\n }\n\n const result = await installHooks(scope, cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n process.stdout.write(`Backup: ${result.backupPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\nimport { SUPPORTED_HOOKS } from './hook-mapping.js';\n\nconst RUNTAPE_MARKER = 'runtape:managed';\n\ntype HookEntry = { type: 'command'; command: string; [k: string]: unknown };\ntype HookMatcher = { matcher: string; hooks: HookEntry[]; [k: string]: unknown };\ntype HooksBlock = Record<string, HookMatcher[]>;\ntype ClaudeSettings = { hooks?: HooksBlock; [k: string]: unknown };\n\nfunction runtapeEntry(hookName: string, cliBinPath: string): HookEntry {\n return {\n type: 'command',\n command: `${cliBinPath} push --event ${hookName}`,\n [RUNTAPE_MARKER]: true,\n };\n}\n\nasync function readSettings(file: string): Promise<ClaudeSettings> {\n try {\n const raw = await readFile(file, 'utf8');\n return JSON.parse(raw) as ClaudeSettings;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nasync function writeSettings(file: string, data: ClaudeSettings): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(data, null, 2) + '\\n');\n}\n\nexport type InstallResult = {\n settingsPath: string;\n backupPath: string;\n addedHooks: string[];\n};\n\nexport async function installHooks(scope: 'user' | 'project', cliBinPath: string): Promise<InstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const backupPath = paths.claudeSettingsBackup(scope);\n\n // Backup (no-op if settings doesn't exist yet — we still create the dir).\n try {\n await copyFile(settingsPath, backupPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const settings = await readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n const added: string[] = [];\n for (const hookName of SUPPORTED_HOOKS) {\n const matchers = (settings.hooks[hookName] = settings.hooks[hookName] ?? []);\n // Find or create a matcher: \"*\". Append our entry there.\n let star = matchers.find((m) => m.matcher === '*');\n if (!star) {\n star = { matcher: '*', hooks: [] };\n matchers.push(star);\n }\n star.hooks = star.hooks ?? [];\n const already = star.hooks.some((h) => (h as HookEntry)[RUNTAPE_MARKER] === true);\n if (!already) {\n star.hooks.push(runtapeEntry(hookName, cliBinPath));\n added.push(hookName);\n }\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, backupPath, addedHooks: added };\n}\n\nexport type UninstallResult = {\n settingsPath: string;\n removedHooks: string[];\n};\n\nexport async function uninstallHooks(scope: 'user' | 'project'): Promise<UninstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const settings = await readSettings(settingsPath);\n const removed: string[] = [];\n\n if (settings.hooks) {\n for (const hookName of Object.keys(settings.hooks)) {\n const matchers = settings.hooks[hookName];\n for (const matcher of matchers) {\n if (!Array.isArray(matcher.hooks)) continue;\n const before = matcher.hooks.length;\n matcher.hooks = matcher.hooks.filter((h) => (h as HookEntry)[RUNTAPE_MARKER] !== true);\n if (matcher.hooks.length < before) removed.push(hookName);\n }\n // Clean up matchers that are now empty.\n settings.hooks[hookName] = matchers.filter((m) => Array.isArray(m.hooks) && m.hooks.length > 0);\n if (settings.hooks[hookName].length === 0) delete settings.hooks[hookName];\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, removedHooks: Array.from(new Set(removed)) };\n}\n","import { z } from 'zod';\nimport { RuntapeEvent } from '../types.js';\n\n// The seven Claude Code hook names we register, plus a sentinel \"drop\" for unknown events.\n// Validated 2026-05-14 against Claude Code 2.1.128 — see spike findings spec.\nexport type ClaudeHookName =\n | 'SessionStart'\n | 'UserPromptSubmit'\n | 'PreToolUse'\n | 'PostToolUse'\n | 'Stop'\n | 'SubagentStop'\n | 'Notification';\n\nexport const SUPPORTED_HOOKS: ClaudeHookName[] = [\n 'SessionStart',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'SubagentStop',\n];\n\nexport type MappedEvent = z.infer<typeof RuntapeEvent>;\n\nexport type MapResult =\n | { kind: 'event'; event: MappedEvent }\n | { kind: 'drop'; reason: string };\n\n// Maps a Claude hook payload + the hook name we were invoked with into a RuntapeEvent.\n// Returns { kind: 'drop' } for Notification or unknown events (we just exit cleanly).\nexport function mapHookPayload(\n hookName: string,\n payload: Record<string, unknown>,\n augment: { wall_ts: string; sequence: number },\n): MapResult {\n // Shared envelope every Claude hook carries.\n const base = {\n session_id: payload.session_id,\n transcript_path: payload.transcript_path,\n cwd: payload.cwd,\n hook_event_name: payload.hook_event_name ?? hookName,\n permission_mode: payload.permission_mode,\n wall_ts: augment.wall_ts,\n sequence: augment.sequence,\n };\n\n let candidate: Record<string, unknown>;\n switch (hookName) {\n case 'SessionStart':\n candidate = { ...base, type: 'session_start', source: payload.source ?? 'startup' };\n break;\n case 'UserPromptSubmit':\n candidate = { ...base, type: 'user_prompt', prompt: payload.prompt };\n break;\n case 'PreToolUse':\n candidate = {\n ...base,\n type: 'tool_attempt',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_use_id: payload.tool_use_id,\n };\n break;\n case 'PostToolUse':\n candidate = {\n ...base,\n type: 'tool_call',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_response: payload.tool_response,\n tool_use_id: payload.tool_use_id,\n duration_ms: payload.duration_ms,\n };\n break;\n case 'Stop':\n candidate = {\n ...base,\n type: 'session_end',\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n case 'SubagentStop':\n candidate = {\n ...base,\n type: 'subagent_end',\n agent_id: payload.agent_id,\n agent_type: payload.agent_type,\n agent_transcript_path: payload.agent_transcript_path,\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n default:\n return { kind: 'drop', reason: `unsupported hook: ${hookName}` };\n }\n\n const parsed = RuntapeEvent.safeParse(candidate);\n if (!parsed.success) {\n return { kind: 'drop', reason: `validation failed: ${parsed.error.issues.map((i) => i.path.join('.') + ': ' + i.message).join('; ')}` };\n }\n return { kind: 'event', event: parsed.data };\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, sep } from 'node:path';\n\n// Returns an absolute path to the `runtape` binary the user just invoked.\n// This is what we write into `~/.claude/settings.json` so the hooks call the same install.\n// Falls back to \"runtape\" (on PATH) if we can't resolve it — useful when users want a\n// non-pinned reference and have the CLI installed globally.\nexport function resolveCliBinPath(): string {\n if (process.env.RUNTAPE_CLI_BIN) return process.env.RUNTAPE_CLI_BIN;\n\n const argv1 = process.argv[1];\n if (argv1) {\n // If we live under a node_modules tree, the absolute path embeds the\n // package's extraction location, which changes on `npm install -g <newer>`.\n // Writing that path into ~/.claude/settings.json would break the hook on\n // the next upgrade. Fall back to the bare command name — `runtape` is on\n // PATH after `npm install -g`, and a fresh install re-points the symlink.\n if (argv1.includes(`${sep}node_modules${sep}`)) return 'runtape';\n try {\n return resolve(argv1);\n } catch {\n /* fall through */\n }\n }\n // Last resort: assume \"runtape\" is on PATH.\n return 'runtape';\n}\n\n// Exported for tests — turns a relative path into something we can stick in JSON.\nexport function moduleFileFromImportMeta(metaUrl: string): string {\n return fileURLToPath(metaUrl);\n}\n\n// Suppress unused-import warning for `dirname`.\nvoid dirname;\n","import { uninstallHooks } from '../lib/hooks-installer.js';\n\nexport async function uninstallCommand(opts: { project?: boolean }): Promise<number> {\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const result = await uninstallHooks(scope);\n if (result.removedHooks.length === 0) {\n process.stdout.write(`No Runtape hooks found in ${result.settingsPath}.\\n`);\n } else {\n process.stdout.write(`Removed Runtape entries from: ${result.removedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { mapHookPayload } from '../lib/hook-mapping.js';\nimport { nextSequence } from '../lib/sequence.js';\nimport { appendEvent } from '../lib/buffer.js';\nimport { readConfig } from '../lib/config.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return '';\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nfunction spawnFlusher(cliBinPath: string): void {\n // Detached so it survives this process exit. stdio:'ignore' so the daemon\n // doesn't inherit the hook's stdin/stdout (Claude Code reads hook stdout!).\n const child = spawn(cliBinPath, ['--internal-flusher'], {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n child.unref();\n}\n\nexport async function pushCommand(opts: { event: string }): Promise<number> {\n // The contract is: never block the hook. If we can't parse/auth/whatever, exit 0 quietly\n // (printing to stderr is fine; printing to stdout could be parsed by Claude Code).\n try {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('runtape: not logged in — skipping event\\n');\n return 0;\n }\n\n const raw = await readStdin();\n if (!raw.trim()) {\n // SessionStart in some Claude Code versions may invoke hooks with no stdin; ignore.\n return 0;\n }\n\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n process.stderr.write(`runtape: invalid JSON on stdin: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0;\n }\n\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : null;\n if (!sessionId) {\n process.stderr.write('runtape: missing session_id on hook payload\\n');\n return 0;\n }\n\n const sequence = await nextSequence(sessionId);\n const result = mapHookPayload(opts.event, payload, {\n wall_ts: new Date().toISOString(),\n sequence,\n });\n\n if (result.kind === 'drop') {\n // Notification, unknown hook, or validation failure. Quiet log + exit clean.\n process.stderr.write(`runtape: dropped ${opts.event}: ${result.reason}\\n`);\n return 0;\n }\n\n await appendEvent(sessionId, result.event);\n spawnFlusher(resolveCliBinPath());\n return 0;\n } catch (err) {\n process.stderr.write(`runtape: push error: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0; // Never fail the hook.\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Per-session monotonic counter. Persisted as a single integer in seq/<session_id>.\n// Hooks fire one-at-a-time per session (Claude Code waits for the hook to exit before\n// firing the next), so we do NOT need cross-process locking — but we DO need the value\n// to survive across hook invocations, since each `runtape push` is a separate process.\nexport async function nextSequence(sessionId: string): Promise<number> {\n const file = paths.seqFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n\n let current = 0;\n try {\n const raw = await readFile(file, 'utf8');\n const parsed = Number.parseInt(raw.trim(), 10);\n if (Number.isFinite(parsed) && parsed >= 0) current = parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const next = current + 1;\n await writeFile(file, String(next));\n return next - 1; // First call returns 0 (matching the Zod schema's nonnegative invariant).\n}\n","import { appendFile, mkdir, readFile, readdir, writeFile, unlink, stat } from 'node:fs/promises';\nimport type { RuntapeEvent } from '../types.js';\nimport { paths } from './paths.js';\n\n// Atomic append-as-line. POSIX guarantees a single write() of <PIPE_BUF bytes (≥512) is\n// atomic, and we further constrain ourselves to one line per call. Two concurrent\n// appenders (e.g. two Claude Code instances writing to the same session — impossible\n// today, but cheap defense) cannot interleave a single line.\nexport async function appendEvent(sessionId: string, event: RuntapeEvent): Promise<void> {\n await mkdir(paths.bufferDir, { recursive: true });\n const line = JSON.stringify(event) + '\\n';\n await appendFile(paths.bufferFile(sessionId), line, { encoding: 'utf8' });\n}\n\nexport type BufferedSession = {\n sessionId: string;\n events: RuntapeEvent[];\n raw: string[]; // Raw lines, so we can rewrite exactly what we read on partial-flush.\n};\n\nexport async function listBufferedSessions(): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(paths.bufferDir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n return entries.filter((e) => e.endsWith('.ndjson')).map((e) => e.slice(0, -'.ndjson'.length));\n}\n\nexport async function readBufferedSession(sessionId: string): Promise<BufferedSession | null> {\n let raw: string;\n try {\n raw = await readFile(paths.bufferFile(sessionId), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n const events: RuntapeEvent[] = [];\n for (const line of lines) {\n try {\n events.push(JSON.parse(line) as RuntapeEvent);\n } catch {\n // Skip malformed lines — they could only get there via a half-written disk; drop them.\n }\n }\n return { sessionId, events, raw: lines };\n}\n\n// Atomic truncate-after-flush: write a temp file with the unflushed remainder, then rename.\n// If `unflushedLines` is empty, delete the buffer file entirely.\nexport async function rewriteBufferedSession(sessionId: string, unflushedLines: string[]): Promise<void> {\n const file = paths.bufferFile(sessionId);\n if (unflushedLines.length === 0) {\n try {\n await unlink(file);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n return;\n }\n const tmp = file + '.tmp';\n await writeFile(tmp, unflushedLines.map((l) => l + '\\n').join(''));\n // rename is atomic within the same filesystem; both paths are in ~/.runtape/buffer.\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\n}\n\nexport async function bufferSize(sessionId: string): Promise<number> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return 0;\n throw err;\n }\n}\n\nexport function bufferDirPath(): string {\n return paths.bufferDir;\n}\n\n// Used by tests + future GC: stat-mtime in ms for the buffer file.\nexport async function bufferMtimeMs(sessionId: string): Promise<number | null> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\n// Re-export for callers that want to know where the file lives without importing paths twice.\nexport function bufferFilePath(sessionId: string): string {\n return paths.bufferFile(sessionId);\n}\n","import { readFile } from 'node:fs/promises';\nimport { readConfig } from '../lib/config.js';\nimport { listBufferedSessions, bufferSize, bufferMtimeMs } from '../lib/buffer.js';\nimport { paths } from '../lib/paths.js';\nimport { pingProject } from '../lib/api.js';\n\nasync function readFlusherPid(): Promise<number | null> {\n try {\n const raw = await readFile(paths.flusherPid, 'utf8');\n const n = Number.parseInt(raw.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\nexport async function statusCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stdout.write('Not logged in. Run `runtape login`.\\n');\n return 0;\n }\n\n process.stdout.write(`Server: ${cfg.server_url}\\n`);\n process.stdout.write(`API key: ${cfg.api_key.slice(0, 8)}…${cfg.api_key.slice(-4)}\\n`);\n\n const ping = await pingProject(cfg.server_url, cfg.api_key);\n process.stdout.write(`Reachable: ${ping.ok ? 'yes' : `no (${ping.detail ?? ping.status})`}\\n`);\n\n const sessions = await listBufferedSessions();\n if (sessions.length === 0) {\n process.stdout.write('Buffer: empty.\\n');\n } else {\n process.stdout.write(`Buffer: ${sessions.length} session(s) pending.\\n`);\n for (const s of sessions) {\n const size = await bufferSize(s);\n const mtime = await bufferMtimeMs(s);\n const ageSec = mtime ? Math.round((Date.now() - mtime) / 1000) : null;\n process.stdout.write(` ${s}: ${size} bytes${ageSec !== null ? `, updated ${ageSec}s ago` : ''}\\n`);\n }\n }\n\n const flusherPid = await readFlusherPid();\n if (flusherPid !== null) {\n process.stdout.write(`Flusher: PID ${flusherPid}\\n`);\n } else {\n process.stdout.write('Flusher: not running.\\n');\n }\n\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { readConfig } from '../lib/config.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n // Cross-platform \"open this URL in the default browser\".\n // macOS: `open <url>`. Linux: `xdg-open <url>`. Windows: `cmd /c start \"\" <url>`.\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nexport async function runsCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const url = `${cfg.server_url.replace(/\\/$/, '')}/dashboard/runs`;\n const { cmd, args } = openCommand();\n\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', (err) => {\n process.stderr.write(`Could not launch browser (${err.message}). Open this manually:\\n${url}\\n`);\n });\n child.unref();\n\n process.stdout.write(`Opening ${url}\\n`);\n return 0;\n}\n","import { appendFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readConfig } from './config.js';\nimport { listBufferedSessions, readBufferedSession, rewriteBufferedSession } from './buffer.js';\nimport { postEvents } from './api.js';\nimport { paths } from './paths.js';\n\nconst POLL_INTERVAL_MS = 1500;\nconst IDLE_EXIT_MS = 30_000;\nconst BATCH_MAX = 100;\nconst BACKOFF_STEPS_MS = [1000, 2000, 4000, 8000, 16_000, 32_000, 60_000];\n\nasync function log(line: string): Promise<void> {\n try {\n await mkdir(dirname(paths.flusherLog), { recursive: true });\n await appendFile(paths.flusherLog, `${new Date().toISOString()} ${line}\\n`);\n } catch {\n /* never throw out of logging */\n }\n}\n\nasync function isProcessAlive(pid: number): Promise<boolean> {\n try {\n process.kill(pid, 0); // Signal 0 = existence check, doesn't actually signal.\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ESRCH') return false;\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true; // Exists, owned by someone else.\n return false;\n }\n}\n\n// Acquire-or-detect-running. Returns true if we became the flusher; false if one is already running.\nexport async function acquirePidLock(): Promise<boolean> {\n await mkdir(dirname(paths.flusherPid), { recursive: true });\n try {\n const existing = await readFile(paths.flusherPid, 'utf8');\n const pid = Number.parseInt(existing.trim(), 10);\n if (Number.isFinite(pid) && (await isProcessAlive(pid))) {\n return false; // Another flusher is alive.\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await writeFile(paths.flusherPid, String(process.pid));\n return true;\n}\n\nasync function releasePidLock(): Promise<void> {\n try {\n await unlink(paths.flusherPid);\n } catch {\n /* ignore */\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// Drain a single session's buffer in batches of up to BATCH_MAX. Returns true if any\n// events were successfully flushed.\nasync function drainSession(sessionId: string, serverUrl: string, apiKey: string): Promise<boolean> {\n const snapshot = await readBufferedSession(sessionId);\n if (!snapshot || snapshot.events.length === 0) {\n await rewriteBufferedSession(sessionId, []);\n return false;\n }\n\n let cursor = 0;\n let anyFlushed = false;\n while (cursor < snapshot.events.length) {\n const slice = snapshot.events.slice(cursor, cursor + BATCH_MAX);\n const result = await postEvents(serverUrl, apiKey, slice);\n\n if (result.ok) {\n cursor += slice.length;\n anyFlushed = true;\n continue;\n }\n\n if (!result.retryable) {\n // Poison batch — log + drop the slice to prevent stuck buffer. 4xx is on us (or stale CLI vs server).\n await log(`drop_poison session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);\n cursor += slice.length;\n anyFlushed = true; // We've made forward progress (toward emptying the buffer), so don't backoff.\n continue;\n }\n\n // Retryable — stop draining this session, leave the rest for next poll cycle.\n await log(`retryable session=${sessionId} status=${result.status} cursor=${cursor} error=${result.error.slice(0, 200)}`);\n break;\n }\n\n const remaining = snapshot.raw.slice(cursor);\n await rewriteBufferedSession(sessionId, remaining);\n return anyFlushed;\n}\n\nexport async function runFlusher(): Promise<void> {\n const acquired = await acquirePidLock();\n if (!acquired) {\n await log('exit_already_running');\n return;\n }\n\n await log(`start pid=${process.pid}`);\n\n let lastActivityMs = Date.now();\n let backoffIdx = 0;\n\n try {\n while (true) {\n const cfg = await readConfig();\n if (!cfg) {\n // No config yet — we shouldn't have been spawned. Exit cleanly.\n await log('exit_no_config');\n return;\n }\n\n const sessions = await listBufferedSessions();\n let flushedThisCycle = false;\n for (const sessionId of sessions) {\n const flushed = await drainSession(sessionId, cfg.server_url, cfg.api_key);\n flushedThisCycle = flushedThisCycle || flushed;\n }\n\n if (flushedThisCycle) {\n lastActivityMs = Date.now();\n backoffIdx = 0;\n }\n\n // Idle exit.\n const remaining = await listBufferedSessions();\n const idleMs = Date.now() - lastActivityMs;\n if (remaining.length === 0 && idleMs >= IDLE_EXIT_MS) {\n await log(`exit_idle idle_ms=${idleMs}`);\n return;\n }\n\n // Backoff when buffer is non-empty but we couldn't drain anything (server down).\n // Reset backoff when we made progress.\n const wait =\n remaining.length > 0 && !flushedThisCycle\n ? BACKOFF_STEPS_MS[Math.min(backoffIdx++, BACKOFF_STEPS_MS.length - 1)]\n : POLL_INTERVAL_MS;\n await delay(wait);\n }\n } catch (err) {\n await log(`crash error=${err instanceof Error ? err.message : String(err)}`);\n throw err;\n } finally {\n await releasePidLock();\n }\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,uBAAuB;AAChC,SAAS,SAAS,OAAO,UAAU,cAAc;;;ACDjD,SAAS,OAAO,OAAO,UAAU,WAAW,cAAc;AAC1D,SAAS,eAAe;AACxB,SAAS,SAAS;;;ACFlB,SAAS,eAAe;AACxB,SAAS,YAAY;AAErB,IAAM,eAAe,QAAQ,IAAI,gBAAgB,KAAK,QAAQ,GAAG,UAAU;AAEpE,IAAM,QAAQ;AAAA,EACnB,MAAM;AAAA,EACN,QAAQ,KAAK,cAAc,aAAa;AAAA,EACxC,WAAW,KAAK,cAAc,QAAQ;AAAA,EACtC,QAAQ,KAAK,cAAc,KAAK;AAAA,EAChC,YAAY,KAAK,cAAc,aAAa;AAAA,EAC5C,YAAY,KAAK,cAAc,aAAa;AAAA,EAC5C,YAAY,CAAC,cAAsB,KAAK,cAAc,UAAU,GAAG,SAAS,SAAS;AAAA,EACrF,SAAS,CAAC,cAAsB,KAAK,cAAc,OAAO,SAAS;AAAA,EACnE,gBAAgB,CAAC,UACf,UAAU,SAAS,KAAK,QAAQ,GAAG,WAAW,eAAe,IAAI,KAAK,QAAQ,IAAI,GAAG,WAAW,eAAe;AAAA,EACjH,sBAAsB,CAAC,UACrB,UAAU,SACN,KAAK,QAAQ,GAAG,WAAW,8BAA8B,IACzD,KAAK,QAAQ,IAAI,GAAG,WAAW,8BAA8B;AACrE;;;ADfO,IAAM,SAAS,EAAE,OAAO;AAAA,EAC7B,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,+CAA+C;AAAA,EAC/F,YAAY,EAAE,OAAO,EAAE,IAAI;AAC7B,CAAC;AAID,IAAM,qBAAqB,QAAQ,IAAI,mBAAmB;AAE1D,eAAsB,aAAqC;AACzD,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,SAAS,MAAM,QAAQ,MAAM;AAAA,EAC3C,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACA,QAAM,SAAS,OAAO,UAAU,KAAK,MAAM,GAAG,CAAC;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,qBAAqB,MAAM,MAAM,KAAK,OAAO,MAAM,OAAO,EAAE;AAAA,EAC9E;AACA,SAAO,OAAO;AAChB;AAEA,eAAsB,YAAY,GAA0B;AAC1D,QAAM,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAM,UAAU,MAAM,QAAQ,KAAK,UAAU,GAAG,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AAGhF,QAAM,MAAM,MAAM,QAAQ,GAAK;AACjC;AAEA,eAAsB,cAA6B;AACjD,MAAI;AACF,UAAM,OAAO,MAAM,MAAM;AAAA,EAC3B,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU;AACtD,UAAM;AAAA,EACR;AACF;AAEO,SAAS,mBAA2B;AACzC,SAAO;AACT;;;AE1CA,SAAS,kBAAkB,QAAyB;AAElD,SAAO,WAAW,OAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACzE;AAEA,eAAsB,WACpB,WACA,QACA,QACqB;AACrB,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,GAAG,UAAU,QAAQ,OAAO,EAAE,CAAC,kBAAkB;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;AAAA,IACjC,CAAC;AAAA,EACH,SAAS,KAAK;AAEZ,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACtD,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,SAAS,IAAI;AACf,UAAM,OAAQ,MAAM,SAAS,KAAK;AAIlC,WAAO,EAAE,IAAI,MAAM,UAAU,KAAK,UAAU,QAAQ,KAAK,OAAO;AAAA,EAClE;AAEA,MAAI,SAAS;AACb,MAAI;AACF,aAAS,MAAM,SAAS,KAAK;AAAA,EAC/B,QAAQ;AAAA,EAER;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QAAQ,SAAS;AAAA,IACjB,OAAO,UAAU,SAAS;AAAA,IAC1B,WAAW,kBAAkB,SAAS,MAAM;AAAA,EAC9C;AACF;AAEA,eAAsB,YAAY,WAAmB,QAA2E;AAK9H,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,GAAG,UAAU,QAAQ,OAAO,EAAE,CAAC,kBAAkB;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,IACrC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,EAC1F;AAEA,MAAI,SAAS,WAAW,IAAK,QAAO,EAAE,IAAI,MAAM,QAAQ,IAAI;AAC5D,MAAI,SAAS,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,QAAQ,kBAAkB;AACxF,SAAO,EAAE,IAAI,OAAO,QAAQ,SAAS,QAAQ,QAAQ,SAAS,WAAW;AAC3E;;;AH3EA,eAAsB,aAAa,MAA6D;AAC9F,QAAM,YAAY,KAAK,aAAa,iBAAiB;AAErD,MAAI,SAAS,KAAK;AAClB,MAAI,CAAC,QAAQ;AACX,UAAM,KAAK,gBAAgB,EAAE,OAAO,OAAO,CAAC;AAC5C,cAAU,MAAM,GAAG,SAAS,2CAAsC,GAAG,KAAK;AAC1E,OAAG,MAAM;AAAA,EACX;AAEA,QAAM,aAAa,OAAO,MAAM,QAAQ,UAAU,MAAM;AACxD,MAAI,CAAC,WAAW,SAAS;AACvB,YAAQ,OAAO,MAAM;AAAA,CAAwD;AAC7E,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO,MAAM,sBAAsB,SAAS;AAAA,CAAK;AACzD,QAAM,OAAO,MAAM,YAAY,WAAW,MAAM;AAChD,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,OAAO,MAAM,iBAAiB,KAAK,WAAW,MAAM,oBAAoB,KAAK,UAAU,oBAAoB;AAAA,CAAI;AACvH,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,EAAE,SAAS,QAAQ,YAAY,UAAU,CAAC;AAC5D,UAAQ,OAAO,MAAM;AAAA,CAA2C;AAChE,SAAO;AACT;;;AI7BA,eAAsB,gBAAiC;AACrD,QAAM,YAAY;AAClB,UAAQ,OAAO,MAAM,+BAA+B;AACpD,SAAO;AACT;;;ACNA,SAAS,mBAAAA,wBAAuB;AAChC,SAAS,SAASC,QAAO,UAAUC,eAAc;;;ACDjD,SAAS,UAAU,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AACrD,SAAS,WAAAC,gBAAe;;;ACajB,IAAM,kBAAoC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAUO,SAAS,eACd,UACA,SACA,SACW;AAEX,QAAM,OAAO;AAAA,IACX,YAAY,QAAQ;AAAA,IACpB,iBAAiB,QAAQ;AAAA,IACzB,KAAK,QAAQ;AAAA,IACb,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,iBAAiB,QAAQ;AAAA,IACzB,SAAS,QAAQ;AAAA,IACjB,UAAU,QAAQ;AAAA,EACpB;AAEA,MAAI;AACJ,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,kBAAY,EAAE,GAAG,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,UAAU,UAAU;AAClF;AAAA,IACF,KAAK;AACH,kBAAY,EAAE,GAAG,MAAM,MAAM,eAAe,QAAQ,QAAQ,OAAO;AACnE;AAAA,IACF,KAAK;AACH,kBAAY;AAAA,QACV,GAAG;AAAA,QACH,MAAM;AAAA,QACN,WAAW,QAAQ;AAAA,QACnB,YAAY,QAAQ;AAAA,QACpB,aAAa,QAAQ;AAAA,MACvB;AACA;AAAA,IACF,KAAK;AACH,kBAAY;AAAA,QACV,GAAG;AAAA,QACH,MAAM;AAAA,QACN,WAAW,QAAQ;AAAA,QACnB,YAAY,QAAQ;AAAA,QACpB,eAAe,QAAQ;AAAA,QACvB,aAAa,QAAQ;AAAA,QACrB,aAAa,QAAQ;AAAA,MACvB;AACA;AAAA,IACF,KAAK;AACH,kBAAY;AAAA,QACV,GAAG;AAAA,QACH,MAAM;AAAA,QACN,wBAAwB,QAAQ;AAAA,QAChC,kBAAkB,QAAQ;AAAA,MAC5B;AACA;AAAA,IACF,KAAK;AACH,kBAAY;AAAA,QACV,GAAG;AAAA,QACH,MAAM;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,YAAY,QAAQ;AAAA,QACpB,uBAAuB,QAAQ;AAAA,QAC/B,wBAAwB,QAAQ;AAAA,QAChC,kBAAkB,QAAQ;AAAA,MAC5B;AACA;AAAA,IACF;AACE,aAAO,EAAE,MAAM,QAAQ,QAAQ,qBAAqB,QAAQ,GAAG;AAAA,EACnE;AAEA,QAAM,SAAS,aAAa,UAAU,SAAS;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,EAAE,MAAM,QAAQ,QAAQ,sBAAsB,OAAO,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,KAAK,GAAG,IAAI,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,EACxI;AACA,SAAO,EAAE,MAAM,SAAS,OAAO,OAAO,KAAK;AAC7C;;;ADlGA,IAAM,iBAAiB;AAOvB,SAAS,aAAa,UAAkB,YAA+B;AACrE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,GAAG,UAAU,iBAAiB,QAAQ;AAAA,IAC/C,CAAC,cAAc,GAAG;AAAA,EACpB;AACF;AAEA,eAAe,aAAa,MAAuC;AACjE,MAAI;AACF,UAAM,MAAM,MAAMC,UAAS,MAAM,MAAM;AACvC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAe,cAAc,MAAc,MAAqC;AAC9E,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAMC,WAAU,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,IAAI;AAC5D;AAQA,eAAsB,aAAa,OAA2B,YAA4C;AACxG,QAAM,eAAe,MAAM,eAAe,KAAK;AAC/C,QAAM,aAAa,MAAM,qBAAqB,KAAK;AAGnD,MAAI;AACF,UAAM,SAAS,cAAc,UAAU;AAAA,EACzC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AAEA,QAAM,WAAW,MAAM,aAAa,YAAY;AAChD,WAAS,QAAQ,SAAS,SAAS,CAAC;AAEpC,QAAM,QAAkB,CAAC;AACzB,aAAW,YAAY,iBAAiB;AACtC,UAAM,WAAY,SAAS,MAAM,QAAQ,IAAI,SAAS,MAAM,QAAQ,KAAK,CAAC;AAE1E,QAAI,OAAO,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG;AACjD,QAAI,CAAC,MAAM;AACT,aAAO,EAAE,SAAS,KAAK,OAAO,CAAC,EAAE;AACjC,eAAS,KAAK,IAAI;AAAA,IACpB;AACA,SAAK,QAAQ,KAAK,SAAS,CAAC;AAC5B,UAAM,UAAU,KAAK,MAAM,KAAK,CAAC,MAAO,EAAgB,cAAc,MAAM,IAAI;AAChF,QAAI,CAAC,SAAS;AACZ,WAAK,MAAM,KAAK,aAAa,UAAU,UAAU,CAAC;AAClD,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,QAAQ;AAC1C,SAAO,EAAE,cAAc,YAAY,YAAY,MAAM;AACvD;AAOA,eAAsB,eAAe,OAAqD;AACxF,QAAM,eAAe,MAAM,eAAe,KAAK;AAC/C,QAAM,WAAW,MAAM,aAAa,YAAY;AAChD,QAAM,UAAoB,CAAC;AAE3B,MAAI,SAAS,OAAO;AAClB,eAAW,YAAY,OAAO,KAAK,SAAS,KAAK,GAAG;AAClD,YAAM,WAAW,SAAS,MAAM,QAAQ;AACxC,iBAAW,WAAW,UAAU;AAC9B,YAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,EAAG;AACnC,cAAM,SAAS,QAAQ,MAAM;AAC7B,gBAAQ,QAAQ,QAAQ,MAAM,OAAO,CAAC,MAAO,EAAgB,cAAc,MAAM,IAAI;AACrF,YAAI,QAAQ,MAAM,SAAS,OAAQ,SAAQ,KAAK,QAAQ;AAAA,MAC1D;AAEA,eAAS,MAAM,QAAQ,IAAI,SAAS,OAAO,CAAC,MAAM,MAAM,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9F,UAAI,SAAS,MAAM,QAAQ,EAAE,WAAW,EAAG,QAAO,SAAS,MAAM,QAAQ;AAAA,IAC3E;AACA,QAAI,OAAO,KAAK,SAAS,KAAK,EAAE,WAAW,EAAG,QAAO,SAAS;AAAA,EAChE;AAEA,QAAM,cAAc,cAAc,QAAQ;AAC1C,SAAO,EAAE,cAAc,cAAc,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,EAAE;AACpE;;;AExGA,SAAS,qBAAqB;AAC9B,SAAS,WAAAC,UAAS,SAAS,WAAW;AAM/B,SAAS,oBAA4B;AAC1C,MAAI,QAAQ,IAAI,gBAAiB,QAAO,QAAQ,IAAI;AAEpD,QAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,MAAI,OAAO;AAMT,QAAI,MAAM,SAAS,GAAG,GAAG,eAAe,GAAG,EAAE,EAAG,QAAO;AACvD,QAAI;AACF,aAAO,QAAQ,KAAK;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;;;AHpBA,eAAsB,eAAe,MAA6D;AAChG,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,CAAC,KAAK;AACR,YAAQ,OAAO,MAAM,6CAA6C;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,QAA4B,KAAK,UAAU,YAAY;AAC7D,QAAM,aAAa,kBAAkB;AAErC,MAAI,CAAC,KAAK,KAAK;AACb,UAAM,KAAKC,iBAAgB,EAAE,OAAAC,QAAO,QAAAC,QAAO,CAAC;AAC5C,UAAM,UAAU,MAAM,GAAG,SAAS,8BAA8B,KAAK,sBAAsB,GAAG,KAAK,EAAE,YAAY;AACjH,OAAG,MAAM;AACT,QAAI,WAAW,OAAO,WAAW,OAAO;AACtC,cAAQ,OAAO,MAAM,YAAY;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,aAAa,OAAO,UAAU;AACnD,UAAQ,OAAO,MAAM,WAAW,OAAO,YAAY;AAAA,CAAI;AACvD,UAAQ,OAAO,MAAM,WAAW,OAAO,UAAU;AAAA,CAAI;AACrD,MAAI,OAAO,WAAW,WAAW,GAAG;AAClC,YAAQ,OAAO,MAAM,mDAA8C;AAAA,EACrE,OAAO;AACL,YAAQ,OAAO,MAAM,UAAU,OAAO,WAAW,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,EACjE;AACA,SAAO;AACT;;;AIjCA,eAAsB,iBAAiB,MAA8C;AACnF,QAAM,QAA4B,KAAK,UAAU,YAAY;AAC7D,QAAM,SAAS,MAAM,eAAe,KAAK;AACzC,MAAI,OAAO,aAAa,WAAW,GAAG;AACpC,YAAQ,OAAO,MAAM,6BAA6B,OAAO,YAAY;AAAA,CAAK;AAAA,EAC5E,OAAO;AACL,YAAQ,OAAO,MAAM,iCAAiC,OAAO,aAAa,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,EAC1F;AACA,SAAO;AACT;;;ACXA,SAAS,aAAa;;;ACAtB,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,gBAAe;AAOxB,eAAsB,aAAa,WAAoC;AACrE,QAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,MAAI,UAAU;AACd,MAAI;AACF,UAAM,MAAM,MAAMC,UAAS,MAAM,MAAM;AACvC,UAAM,SAAS,OAAO,SAAS,IAAI,KAAK,GAAG,EAAE;AAC7C,QAAI,OAAO,SAAS,MAAM,KAAK,UAAU,EAAG,WAAU;AAAA,EACxD,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AAEA,QAAM,OAAO,UAAU;AACvB,QAAMC,WAAU,MAAM,OAAO,IAAI,CAAC;AAClC,SAAO,OAAO;AAChB;;;ACxBA,SAAS,YAAY,SAAAC,QAAO,YAAAC,WAAU,SAAS,aAAAC,YAAW,UAAAC,SAAQ,YAAY;AAQ9E,eAAsB,YAAY,WAAmB,OAAoC;AACvF,QAAMC,OAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAChD,QAAM,OAAO,KAAK,UAAU,KAAK,IAAI;AACrC,QAAM,WAAW,MAAM,WAAW,SAAS,GAAG,MAAM,EAAE,UAAU,OAAO,CAAC;AAC1E;AAQA,eAAsB,uBAA0C;AAC9D,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,MAAM,SAAS;AAAA,EACzC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO,CAAC;AAC9D,UAAM;AAAA,EACR;AACA,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,UAAU,MAAM,CAAC;AAC9F;AAEA,eAAsB,oBAAoB,WAAoD;AAC5F,MAAI;AACJ,MAAI;AACF,UAAM,MAAMC,UAAS,MAAM,WAAW,SAAS,GAAG,MAAM;AAAA,EAC1D,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AACxD,QAAM,SAAyB,CAAC;AAChC,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,aAAO,KAAK,KAAK,MAAM,IAAI,CAAiB;AAAA,IAC9C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,EAAE,WAAW,QAAQ,KAAK,MAAM;AACzC;AAIA,eAAsB,uBAAuB,WAAmB,gBAAyC;AACvG,QAAM,OAAO,MAAM,WAAW,SAAS;AACvC,MAAI,eAAe,WAAW,GAAG;AAC/B,QAAI;AACF,YAAMC,QAAO,IAAI;AAAA,IACnB,SAAS,KAAK;AACZ,UAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,IAC9D;AACA;AAAA,EACF;AACA,QAAM,MAAM,OAAO;AACnB,QAAMC,WAAU,KAAK,eAAe,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;AAEjE,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,aAAkB;AAClD,QAAM,OAAO,KAAK,IAAI;AACxB;AAEA,eAAsB,WAAW,WAAoC;AACnE,MAAI;AACF,UAAM,IAAI,MAAM,KAAK,MAAM,WAAW,SAAS,CAAC;AAChD,WAAO,EAAE;AAAA,EACX,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAOA,eAAsB,cAAc,WAA2C;AAC7E,MAAI;AACF,UAAM,IAAI,MAAM,KAAK,MAAM,WAAW,SAAS,CAAC;AAChD,WAAO,EAAE;AAAA,EACX,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;;;AFtFA,eAAe,YAA6B;AAC1C,MAAI,QAAQ,MAAM,MAAO,QAAO;AAChC,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,EACjE;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC9C;AAEA,SAAS,aAAa,YAA0B;AAG9C,QAAM,QAAQ,MAAM,YAAY,CAAC,oBAAoB,GAAG;AAAA,IACtD,UAAU;AAAA,IACV,OAAO;AAAA,IACP,KAAK,QAAQ;AAAA,EACf,CAAC;AACD,QAAM,MAAM;AACd;AAEA,eAAsB,YAAY,MAA0C;AAG1E,MAAI;AACF,UAAM,MAAM,MAAM,WAAW;AAC7B,QAAI,CAAC,KAAK;AACR,cAAQ,OAAO,MAAM,gDAA2C;AAChE,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,MAAM,UAAU;AAC5B,QAAI,CAAC,IAAI,KAAK,GAAG;AAEf,aAAO;AAAA,IACT;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,GAAG;AAAA,IAC1B,SAAS,KAAK;AACZ,cAAQ,OAAO,MAAM,mCAAmC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC5G,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa;AAChF,QAAI,CAAC,WAAW;AACd,cAAQ,OAAO,MAAM,+CAA+C;AACpE,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,MAAM,aAAa,SAAS;AAC7C,UAAM,SAAS,eAAe,KAAK,OAAO,SAAS;AAAA,MACjD,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,MAChC;AAAA,IACF,CAAC;AAED,QAAI,OAAO,SAAS,QAAQ;AAE1B,cAAQ,OAAO,MAAM,oBAAoB,KAAK,KAAK,KAAK,OAAO,MAAM;AAAA,CAAI;AACzE,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,WAAW,OAAO,KAAK;AACzC,iBAAa,kBAAkB,CAAC;AAChC,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,wBAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AACjG,WAAO;AAAA,EACT;AACF;;;AG5EA,SAAS,YAAAC,iBAAgB;AAMzB,eAAe,iBAAyC;AACtD,MAAI;AACF,UAAM,MAAM,MAAMC,UAAS,MAAM,YAAY,MAAM;AACnD,UAAM,IAAI,OAAO,SAAS,IAAI,KAAK,GAAG,EAAE;AACxC,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EAClC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,gBAAiC;AACrD,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,CAAC,KAAK;AACR,YAAQ,OAAO,MAAM,uCAAuC;AAC5D,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO,MAAM,WAAW,IAAI,UAAU;AAAA,CAAI;AAClD,UAAQ,OAAO,MAAM,YAAY,IAAI,QAAQ,MAAM,GAAG,CAAC,CAAC,SAAI,IAAI,QAAQ,MAAM,EAAE,CAAC;AAAA,CAAI;AAErF,QAAM,OAAO,MAAM,YAAY,IAAI,YAAY,IAAI,OAAO;AAC1D,UAAQ,OAAO,MAAM,cAAc,KAAK,KAAK,QAAQ,OAAO,KAAK,UAAU,KAAK,MAAM,GAAG;AAAA,CAAI;AAE7F,QAAM,WAAW,MAAM,qBAAqB;AAC5C,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,OAAO,MAAM,kBAAkB;AAAA,EACzC,OAAO;AACL,YAAQ,OAAO,MAAM,WAAW,SAAS,MAAM;AAAA,CAAwB;AACvE,eAAW,KAAK,UAAU;AACxB,YAAM,OAAO,MAAM,WAAW,CAAC;AAC/B,YAAM,QAAQ,MAAM,cAAc,CAAC;AACnC,YAAM,SAAS,QAAQ,KAAK,OAAO,KAAK,IAAI,IAAI,SAAS,GAAI,IAAI;AACjE,cAAQ,OAAO,MAAM,KAAK,CAAC,KAAK,IAAI,SAAS,WAAW,OAAO,aAAa,MAAM,UAAU,EAAE;AAAA,CAAI;AAAA,IACpG;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,eAAe;AACxC,MAAI,eAAe,MAAM;AACvB,YAAQ,OAAO,MAAM,gBAAgB,UAAU;AAAA,CAAI;AAAA,EACrD,OAAO;AACL,YAAQ,OAAO,MAAM,yBAAyB;AAAA,EAChD;AAEA,SAAO;AACT;;;ACnDA,SAAS,SAAAC,cAAa;AACtB,SAAS,gBAAgB;AAGzB,SAAS,cAA+C;AAGtD,MAAI,aAAa,SAAU,QAAO,EAAE,KAAK,QAAQ,MAAM,CAAC,EAAE;AAC1D,MAAI,aAAa,QAAS,QAAO,EAAE,KAAK,OAAO,MAAM,CAAC,MAAM,SAAS,EAAE,EAAE;AACzE,SAAO,EAAE,KAAK,YAAY,MAAM,CAAC,EAAE;AACrC;AAEA,eAAsB,cAA+B;AACnD,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,CAAC,KAAK;AACR,YAAQ,OAAO,MAAM,6CAA6C;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,GAAG,IAAI,WAAW,QAAQ,OAAO,EAAE,CAAC;AAChD,QAAM,EAAE,KAAK,KAAK,IAAI,YAAY;AAElC,QAAM,QAAQC,OAAM,KAAK,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,OAAO,UAAU,UAAU,KAAK,CAAC;AAC5E,QAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,YAAQ,OAAO,MAAM,6BAA6B,IAAI,OAAO;AAAA,EAA2B,GAAG;AAAA,CAAI;AAAA,EACjG,CAAC;AACD,QAAM,MAAM;AAEZ,UAAQ,OAAO,MAAM,WAAW,GAAG;AAAA,CAAI;AACvC,SAAO;AACT;;;AC9BA,SAAS,cAAAC,aAAY,SAAAC,QAAO,YAAAC,WAAU,UAAAC,SAAQ,aAAAC,kBAAiB;AAC/D,SAAS,WAAAC,gBAAe;AAMxB,IAAM,mBAAmB;AACzB,IAAM,eAAe;AACrB,IAAM,YAAY;AAClB,IAAM,mBAAmB,CAAC,KAAM,KAAM,KAAM,KAAM,MAAQ,MAAQ,GAAM;AAExE,eAAe,IAAI,MAA6B;AAC9C,MAAI;AACF,UAAMC,OAAMC,SAAQ,MAAM,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,UAAMC,YAAW,MAAM,YAAY,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,IAAI;AAAA,CAAI;AAAA,EAC5E,QAAQ;AAAA,EAER;AACF;AAEA,eAAe,eAAe,KAA+B;AAC3D,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,QAAS,QAAO;AAC5D,QAAK,IAA8B,SAAS,QAAS,QAAO;AAC5D,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,iBAAmC;AACvD,QAAMF,OAAMC,SAAQ,MAAM,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,MAAI;AACF,UAAM,WAAW,MAAME,UAAS,MAAM,YAAY,MAAM;AACxD,UAAM,MAAM,OAAO,SAAS,SAAS,KAAK,GAAG,EAAE;AAC/C,QAAI,OAAO,SAAS,GAAG,KAAM,MAAM,eAAe,GAAG,GAAI;AACvD,aAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AACA,QAAMC,WAAU,MAAM,YAAY,OAAO,QAAQ,GAAG,CAAC;AACrD,SAAO;AACT;AAEA,eAAe,iBAAgC;AAC7C,MAAI;AACF,UAAMC,QAAO,MAAM,UAAU;AAAA,EAC/B,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAIA,eAAe,aAAa,WAAmB,WAAmB,QAAkC;AAClG,QAAM,WAAW,MAAM,oBAAoB,SAAS;AACpD,MAAI,CAAC,YAAY,SAAS,OAAO,WAAW,GAAG;AAC7C,UAAM,uBAAuB,WAAW,CAAC,CAAC;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACb,MAAI,aAAa;AACjB,SAAO,SAAS,SAAS,OAAO,QAAQ;AACtC,UAAM,QAAQ,SAAS,OAAO,MAAM,QAAQ,SAAS,SAAS;AAC9D,UAAM,SAAS,MAAM,WAAW,WAAW,QAAQ,KAAK;AAExD,QAAI,OAAO,IAAI;AACb,gBAAU,MAAM;AAChB,mBAAa;AACb;AAAA,IACF;AAEA,QAAI,CAAC,OAAO,WAAW;AAErB,YAAM,IAAI,uBAAuB,SAAS,WAAW,OAAO,MAAM,UAAU,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE;AACxG,gBAAU,MAAM;AAChB,mBAAa;AACb;AAAA,IACF;AAGA,UAAM,IAAI,qBAAqB,SAAS,WAAW,OAAO,MAAM,WAAW,MAAM,UAAU,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE;AACvH;AAAA,EACF;AAEA,QAAM,YAAY,SAAS,IAAI,MAAM,MAAM;AAC3C,QAAM,uBAAuB,WAAW,SAAS;AACjD,SAAO;AACT;AAEA,eAAsB,aAA4B;AAChD,QAAM,WAAW,MAAM,eAAe;AACtC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,sBAAsB;AAChC;AAAA,EACF;AAEA,QAAM,IAAI,aAAa,QAAQ,GAAG,EAAE;AAEpC,MAAI,iBAAiB,KAAK,IAAI;AAC9B,MAAI,aAAa;AAEjB,MAAI;AACF,WAAO,MAAM;AACX,YAAM,MAAM,MAAM,WAAW;AAC7B,UAAI,CAAC,KAAK;AAER,cAAM,IAAI,gBAAgB;AAC1B;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,qBAAqB;AAC5C,UAAI,mBAAmB;AACvB,iBAAW,aAAa,UAAU;AAChC,cAAM,UAAU,MAAM,aAAa,WAAW,IAAI,YAAY,IAAI,OAAO;AACzE,2BAAmB,oBAAoB;AAAA,MACzC;AAEA,UAAI,kBAAkB;AACpB,yBAAiB,KAAK,IAAI;AAC1B,qBAAa;AAAA,MACf;AAGA,YAAM,YAAY,MAAM,qBAAqB;AAC7C,YAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,UAAI,UAAU,WAAW,KAAK,UAAU,cAAc;AACpD,cAAM,IAAI,qBAAqB,MAAM,EAAE;AACvC;AAAA,MACF;AAIA,YAAM,OACJ,UAAU,SAAS,KAAK,CAAC,mBACrB,iBAAiB,KAAK,IAAI,cAAc,iBAAiB,SAAS,CAAC,CAAC,IACpE;AACN,YAAM,MAAM,IAAI;AAAA,IAClB;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,IAAI,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC3E,UAAM;AAAA,EACR,UAAE;AACA,UAAM,eAAe;AAAA,EACvB;AACF;;;AhB9IA,IAAI,QAAQ,KAAK,SAAS,oBAAoB,GAAG;AAC/C,OAAK,WAAW,EAAE;AAAA,IAChB,MAAM,QAAQ,KAAK,CAAC;AAAA,IACpB,CAAC,QAAiB;AAChB,cAAQ,MAAM,GAAG;AACjB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,OAAO;AACL,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,SAAS,EACd,YAAY,uCAAuC,EACnD,QAAQ,OAAO;AAElB,UACG,QAAQ,OAAO,EACf,YAAY,oEAAoE,EAChF,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,0BAA0B,qBAAqB,EACtD,OAAO,OAAO,SAAS,QAAQ,KAAK,MAAM,aAAa,IAAI,CAAC,CAAC;AAEhE,UACG,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,YAAY,QAAQ,KAAK,MAAM,cAAc,CAAC,CAAC;AAEzD,UACG,QAAQ,SAAS,EACjB,YAAY,iFAAiF,EAC7F,OAAO,aAAa,4DAA4D,EAChF,OAAO,aAAa,8BAA8B,EAClD,OAAO,OAAO,SAAS,QAAQ,KAAK,MAAM,eAAe,IAAI,CAAC,CAAC;AAElE,UACG,QAAQ,WAAW,EACnB,YAAY,4CAA4C,EACxD,OAAO,aAAa,0DAA0D,EAC9E,OAAO,OAAO,SAAS,QAAQ,KAAK,MAAM,iBAAiB,IAAI,CAAC,CAAC;AAEpE,UACG,QAAQ,MAAM,EACd,YAAY,2EAA2E,EACvF,eAAe,kBAAkB,4DAAuD,EACxF,OAAO,OAAO,SAAS,QAAQ,KAAK,MAAM,YAAY,IAAI,CAAC,CAAC;AAE/D,UACG,QAAQ,QAAQ,EAChB,YAAY,4DAA4D,EACxE,OAAO,YAAY,QAAQ,KAAK,MAAM,cAAc,CAAC,CAAC;AAEzD,UACG,QAAQ,MAAM,EACd,YAAY,qDAAqD,EACjE,OAAO,YAAY,QAAQ,KAAK,MAAM,YAAY,CAAC,CAAC;AAEvD,UAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAQ;AAC9C,YAAQ,MAAM,GAAG;AACjB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":["createInterface","input","output","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","dirname","createInterface","input","output","mkdir","readFile","writeFile","dirname","mkdir","dirname","readFile","writeFile","mkdir","readFile","writeFile","unlink","mkdir","readFile","unlink","writeFile","readFile","readFile","spawn","spawn","appendFile","mkdir","readFile","unlink","writeFile","dirname","mkdir","dirname","appendFile","readFile","writeFile","unlink","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/paths.ts","../src/lib/api.ts","../src/commands/logout.ts","../src/commands/install.ts","../src/lib/hooks-installer.ts","../src/lib/hook-mapping.ts","../src/lib/cli-bin.ts","../src/commands/uninstall.ts","../src/commands/push.ts","../src/lib/sequence.ts","../src/lib/buffer.ts","../src/commands/status.ts","../src/commands/runs.ts","../src/commands/setup.ts","../src/lib/flusher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { installCommand } from './commands/install.js';\nimport { uninstallCommand } from './commands/uninstall.js';\nimport { pushCommand } from './commands/push.js';\nimport { statusCommand } from './commands/status.js';\nimport { runsCommand } from './commands/runs.js';\nimport { setupCommand } from './commands/setup.js';\nimport { runFlusher } from './lib/flusher.js';\n\n// Read package.json at runtime so --version can never desync from the publish.\nconst pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');\nconst PKG_VERSION = (JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string }).version;\n\n// Internal flag for the detached flusher daemon — not a user-facing subcommand.\n// Goes before commander.parse so we can short-circuit before commander sees argv.\nif (process.argv.includes('--internal-flusher')) {\n void runFlusher().then(\n () => process.exit(0),\n (err: unknown) => {\n console.error(err);\n process.exit(1);\n },\n );\n} else {\n const program = new Command();\n\n program\n .name('runtape')\n .description('Flight recorder for AI coding agents.')\n .version(PKG_VERSION);\n\n program\n .command('setup')\n .description('Guided onboarding: login + install hooks + verify in one flow.')\n .option('--no-browser', 'Do not open the dashboard URL in a browser')\n .action(async (opts) => process.exit(await setupCommand(opts)));\n\n program\n .command('login')\n .description('Paste your API key from runtape.dev/dashboard and save it locally.')\n .option('-k, --key <key>', 'API key (skip the prompt)')\n .option('-s, --server-url <url>', 'Override server URL')\n .action(async (opts) => process.exit(await loginCommand(opts)));\n\n program\n .command('logout')\n .description('Remove saved credentials.')\n .action(async () => process.exit(await logoutCommand()));\n\n program\n .command('install')\n .description('Add Runtape hooks to ~/.claude/settings.json (or project-local with --project).')\n .option('--project', 'Install into ./.claude/settings.json instead of user-level')\n .option('-y, --yes', 'Skip the confirmation prompt')\n .action(async (opts) => process.exit(await installCommand(opts)));\n\n program\n .command('uninstall')\n .description('Remove Runtape hooks from Claude settings.')\n .option('--project', 'Operate on ./.claude/settings.json instead of user-level')\n .action(async (opts) => process.exit(await uninstallCommand(opts)));\n\n program\n .command('push')\n .description('Internal: invoked by Claude Code hooks. Reads stdin and buffers an event.')\n .requiredOption('--event <name>', 'Claude hook event name (SessionStart, PostToolUse, …)')\n .action(async (opts) => process.exit(await pushCommand(opts)));\n\n program\n .command('status')\n .description('Show current login, buffer state, and server reachability.')\n .action(async () => process.exit(await statusCommand()));\n\n program\n .command('runs')\n .description('Open your Runtape dashboard in the default browser.')\n .action(async () => process.exit(await runsCommand()));\n\n program.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\n\nexport async function loginCommand(opts: { key?: string; serverUrl?: string }): Promise<number> {\n const serverUrl = opts.serverUrl ?? defaultServerUrl();\n\n let apiKey = opts.key;\n if (!apiKey) {\n const rl = createInterface({ input, output });\n apiKey = (await rl.question('Paste your Runtape API key (rtk_…): ')).trim();\n rl.close();\n }\n\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`Invalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(`Login failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`);\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write(`Saved. You can now run: runtape install\\n`);\n return 0;\n}\n","import { chmod, mkdir, readFile, writeFile, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { z } from 'zod';\nimport { paths } from './paths.js';\n\nexport const Config = z.object({\n api_key: z.string().regex(/^rtk_[a-f0-9]{64}$/, 'api_key must be rtk_ followed by 64 hex chars'),\n server_url: z.string().url(),\n});\n\nexport type Config = z.infer<typeof Config>;\n\nconst DEFAULT_SERVER_URL = process.env.RUNTAPE_API_URL ?? 'https://runtape.dev';\n\nexport async function readConfig(): Promise<Config | null> {\n let raw: string;\n try {\n raw = await readFile(paths.config, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const parsed = Config.safeParse(JSON.parse(raw));\n if (!parsed.success) {\n throw new Error(`Invalid config at ${paths.config}: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\nexport async function writeConfig(c: Config): Promise<void> {\n await mkdir(dirname(paths.config), { recursive: true });\n await writeFile(paths.config, JSON.stringify(c, null, 2) + '\\n', { mode: 0o600 });\n // writeFile's `mode` is only applied on create. Re-chmod explicitly so a re-login\n // tightens permissions if the file was previously loosened (e.g. `chmod 644`).\n await chmod(paths.config, 0o600);\n}\n\nexport async function clearConfig(): Promise<void> {\n try {\n await unlink(paths.config);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport function defaultServerUrl(): string {\n return DEFAULT_SERVER_URL;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst RUNTAPE_HOME = process.env.RUNTAPE_HOME ?? join(homedir(), '.runtape');\n\nexport const paths = {\n home: RUNTAPE_HOME,\n config: join(RUNTAPE_HOME, 'config.json'),\n bufferDir: join(RUNTAPE_HOME, 'buffer'),\n seqDir: join(RUNTAPE_HOME, 'seq'),\n flusherPid: join(RUNTAPE_HOME, 'flusher.pid'),\n flusherLog: join(RUNTAPE_HOME, 'flusher.log'),\n bufferFile: (sessionId: string) => join(RUNTAPE_HOME, 'buffer', `${sessionId}.ndjson`),\n seqFile: (sessionId: string) => join(RUNTAPE_HOME, 'seq', sessionId),\n claudeSettings: (scope: 'user' | 'project') =>\n scope === 'user' ? join(homedir(), '.claude', 'settings.json') : join(process.cwd(), '.claude', 'settings.json'),\n claudeSettingsBackup: (scope: 'user' | 'project') =>\n scope === 'user'\n ? join(homedir(), '.claude', 'settings.json.runtape-backup')\n : join(process.cwd(), '.claude', 'settings.json.runtape-backup'),\n};\n","import type { RuntapeEvent } from '../types.js';\n\nexport type PostResult =\n | { ok: true; accepted: number; errors: Array<{ index: number; reason: string }> }\n | { ok: false; status: number; error: string; retryable: boolean };\n\nfunction isRetryableStatus(status: number): boolean {\n // 408 timeout, 425 too-early, 429 rate-limited, 5xx — retry. 4xx — drop (poison).\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\nexport async function postEvents(\n serverUrl: string,\n apiKey: string,\n events: RuntapeEvent[],\n): Promise<PostResult> {\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n } catch (err) {\n // Network failure (ENOTFOUND, ECONNREFUSED, fetch abort). Retryable.\n return {\n ok: false,\n status: 0,\n error: err instanceof Error ? err.message : String(err),\n retryable: true,\n };\n }\n\n if (response.ok) {\n const body = (await response.json()) as {\n accepted: number;\n errors: Array<{ index: number; reason: string }>;\n };\n return { ok: true, accepted: body.accepted, errors: body.errors };\n }\n\n let detail = '';\n try {\n detail = await response.text();\n } catch {\n /* ignore */\n }\n return {\n ok: false,\n status: response.status,\n error: detail || response.statusText,\n retryable: isRetryableStatus(response.status),\n };\n}\n\nexport async function pingProject(serverUrl: string, apiKey: string): Promise<{ ok: boolean; status: number; detail?: string }> {\n // We don't have a dedicated /me endpoint yet (we may add one in Task 3b).\n // For now, \"ping\" = POST /api/v1/events with an empty events array and inspect the response.\n // Server returns 400 invalid_request_body (events must be ≥1) on success — that proves auth works.\n // 401 = bad key; 5xx = server problem.\n let response: Response;\n try {\n response = await fetch(`${serverUrl.replace(/\\/$/, '')}/api/v1/events`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ events: [] }),\n });\n } catch (err) {\n return { ok: false, status: 0, detail: err instanceof Error ? err.message : String(err) };\n }\n\n if (response.status === 400) return { ok: true, status: 400 };\n if (response.status === 401) return { ok: false, status: 401, detail: 'invalid api key' };\n return { ok: false, status: response.status, detail: response.statusText };\n}\n","import { clearConfig } from '../lib/config.js';\n\nexport async function logoutCommand(): Promise<number> {\n await clearConfig();\n process.stdout.write('Logged out. Config removed.\\n');\n return 0;\n}\n","import { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { readConfig } from '../lib/config.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nexport async function installCommand(opts: { project?: boolean; yes?: boolean }): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const cliBinPath = resolveCliBinPath();\n\n if (!opts.yes) {\n const rl = createInterface({ input, output });\n const answer = (await rl.question(`Install Runtape hooks into ${scope} settings (yes/no)? `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n process.stdout.write('Aborted.\\n');\n return 0;\n }\n }\n\n const result = await installHooks(scope, cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n process.stdout.write(`Backup: ${result.backupPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\nimport { SUPPORTED_HOOKS } from './hook-mapping.js';\n\nconst RUNTAPE_MARKER = 'runtape:managed';\n\ntype HookEntry = { type: 'command'; command: string; [k: string]: unknown };\ntype HookMatcher = { matcher: string; hooks: HookEntry[]; [k: string]: unknown };\ntype HooksBlock = Record<string, HookMatcher[]>;\ntype ClaudeSettings = { hooks?: HooksBlock; [k: string]: unknown };\n\nfunction runtapeEntry(hookName: string, cliBinPath: string): HookEntry {\n return {\n type: 'command',\n command: `${cliBinPath} push --event ${hookName}`,\n [RUNTAPE_MARKER]: true,\n };\n}\n\nasync function readSettings(file: string): Promise<ClaudeSettings> {\n try {\n const raw = await readFile(file, 'utf8');\n return JSON.parse(raw) as ClaudeSettings;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nasync function writeSettings(file: string, data: ClaudeSettings): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(data, null, 2) + '\\n');\n}\n\nexport type InstallResult = {\n settingsPath: string;\n backupPath: string;\n addedHooks: string[];\n};\n\nexport async function installHooks(scope: 'user' | 'project', cliBinPath: string): Promise<InstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const backupPath = paths.claudeSettingsBackup(scope);\n\n // Backup (no-op if settings doesn't exist yet — we still create the dir).\n try {\n await copyFile(settingsPath, backupPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const settings = await readSettings(settingsPath);\n settings.hooks = settings.hooks ?? {};\n\n const added: string[] = [];\n for (const hookName of SUPPORTED_HOOKS) {\n const matchers = (settings.hooks[hookName] = settings.hooks[hookName] ?? []);\n // Find or create a matcher: \"*\". Append our entry there.\n let star = matchers.find((m) => m.matcher === '*');\n if (!star) {\n star = { matcher: '*', hooks: [] };\n matchers.push(star);\n }\n star.hooks = star.hooks ?? [];\n const already = star.hooks.some((h) => (h as HookEntry)[RUNTAPE_MARKER] === true);\n if (!already) {\n star.hooks.push(runtapeEntry(hookName, cliBinPath));\n added.push(hookName);\n }\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, backupPath, addedHooks: added };\n}\n\nexport type UninstallResult = {\n settingsPath: string;\n removedHooks: string[];\n};\n\nexport async function uninstallHooks(scope: 'user' | 'project'): Promise<UninstallResult> {\n const settingsPath = paths.claudeSettings(scope);\n const settings = await readSettings(settingsPath);\n const removed: string[] = [];\n\n if (settings.hooks) {\n for (const hookName of Object.keys(settings.hooks)) {\n const matchers = settings.hooks[hookName];\n for (const matcher of matchers) {\n if (!Array.isArray(matcher.hooks)) continue;\n const before = matcher.hooks.length;\n matcher.hooks = matcher.hooks.filter((h) => (h as HookEntry)[RUNTAPE_MARKER] !== true);\n if (matcher.hooks.length < before) removed.push(hookName);\n }\n // Clean up matchers that are now empty.\n settings.hooks[hookName] = matchers.filter((m) => Array.isArray(m.hooks) && m.hooks.length > 0);\n if (settings.hooks[hookName].length === 0) delete settings.hooks[hookName];\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n\n await writeSettings(settingsPath, settings);\n return { settingsPath, removedHooks: Array.from(new Set(removed)) };\n}\n","import { z } from 'zod';\nimport { RuntapeEvent } from '../types.js';\n\n// The seven Claude Code hook names we register, plus a sentinel \"drop\" for unknown events.\n// Validated 2026-05-14 against Claude Code 2.1.128 — see spike findings spec.\nexport type ClaudeHookName =\n | 'SessionStart'\n | 'UserPromptSubmit'\n | 'PreToolUse'\n | 'PostToolUse'\n | 'Stop'\n | 'SubagentStop'\n | 'Notification';\n\nexport const SUPPORTED_HOOKS: ClaudeHookName[] = [\n 'SessionStart',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'SubagentStop',\n];\n\nexport type MappedEvent = z.infer<typeof RuntapeEvent>;\n\nexport type MapResult =\n | { kind: 'event'; event: MappedEvent }\n | { kind: 'drop'; reason: string };\n\n// Maps a Claude hook payload + the hook name we were invoked with into a RuntapeEvent.\n// Returns { kind: 'drop' } for Notification or unknown events (we just exit cleanly).\nexport function mapHookPayload(\n hookName: string,\n payload: Record<string, unknown>,\n augment: { wall_ts: string; sequence: number },\n): MapResult {\n // Shared envelope every Claude hook carries.\n const base = {\n session_id: payload.session_id,\n transcript_path: payload.transcript_path,\n cwd: payload.cwd,\n hook_event_name: payload.hook_event_name ?? hookName,\n permission_mode: payload.permission_mode,\n wall_ts: augment.wall_ts,\n sequence: augment.sequence,\n };\n\n let candidate: Record<string, unknown>;\n switch (hookName) {\n case 'SessionStart':\n candidate = { ...base, type: 'session_start', source: payload.source ?? 'startup' };\n break;\n case 'UserPromptSubmit':\n candidate = { ...base, type: 'user_prompt', prompt: payload.prompt };\n break;\n case 'PreToolUse':\n candidate = {\n ...base,\n type: 'tool_attempt',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_use_id: payload.tool_use_id,\n };\n break;\n case 'PostToolUse':\n candidate = {\n ...base,\n type: 'tool_call',\n tool_name: payload.tool_name,\n tool_input: payload.tool_input,\n tool_response: payload.tool_response,\n tool_use_id: payload.tool_use_id,\n duration_ms: payload.duration_ms,\n };\n break;\n case 'Stop':\n candidate = {\n ...base,\n type: 'session_end',\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n case 'SubagentStop':\n candidate = {\n ...base,\n type: 'subagent_end',\n agent_id: payload.agent_id,\n agent_type: payload.agent_type,\n agent_transcript_path: payload.agent_transcript_path,\n last_assistant_message: payload.last_assistant_message,\n stop_hook_active: payload.stop_hook_active,\n };\n break;\n default:\n return { kind: 'drop', reason: `unsupported hook: ${hookName}` };\n }\n\n const parsed = RuntapeEvent.safeParse(candidate);\n if (!parsed.success) {\n return { kind: 'drop', reason: `validation failed: ${parsed.error.issues.map((i) => i.path.join('.') + ': ' + i.message).join('; ')}` };\n }\n return { kind: 'event', event: parsed.data };\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, sep } from 'node:path';\n\n// Returns an absolute path to the `runtape` binary the user just invoked.\n// This is what we write into `~/.claude/settings.json` so the hooks call the same install.\n// Falls back to \"runtape\" (on PATH) if we can't resolve it — useful when users want a\n// non-pinned reference and have the CLI installed globally.\nexport function resolveCliBinPath(): string {\n if (process.env.RUNTAPE_CLI_BIN) return process.env.RUNTAPE_CLI_BIN;\n\n const argv1 = process.argv[1];\n if (argv1) {\n // If we live under a node_modules tree, the absolute path embeds the\n // package's extraction location, which changes on `npm install -g <newer>`.\n // Writing that path into ~/.claude/settings.json would break the hook on\n // the next upgrade. Fall back to the bare command name — `runtape` is on\n // PATH after `npm install -g`, and a fresh install re-points the symlink.\n if (argv1.includes(`${sep}node_modules${sep}`)) return 'runtape';\n try {\n return resolve(argv1);\n } catch {\n /* fall through */\n }\n }\n // Last resort: assume \"runtape\" is on PATH.\n return 'runtape';\n}\n\n// Exported for tests — turns a relative path into something we can stick in JSON.\nexport function moduleFileFromImportMeta(metaUrl: string): string {\n return fileURLToPath(metaUrl);\n}\n\n// Suppress unused-import warning for `dirname`.\nvoid dirname;\n","import { uninstallHooks } from '../lib/hooks-installer.js';\n\nexport async function uninstallCommand(opts: { project?: boolean }): Promise<number> {\n const scope: 'user' | 'project' = opts.project ? 'project' : 'user';\n const result = await uninstallHooks(scope);\n if (result.removedHooks.length === 0) {\n process.stdout.write(`No Runtape hooks found in ${result.settingsPath}.\\n`);\n } else {\n process.stdout.write(`Removed Runtape entries from: ${result.removedHooks.join(', ')}\\n`);\n }\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { mapHookPayload } from '../lib/hook-mapping.js';\nimport { nextSequence } from '../lib/sequence.js';\nimport { appendEvent } from '../lib/buffer.js';\nimport { readConfig } from '../lib/config.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return '';\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nfunction spawnFlusher(cliBinPath: string): void {\n // Detached so it survives this process exit. stdio:'ignore' so the daemon\n // doesn't inherit the hook's stdin/stdout (Claude Code reads hook stdout!).\n const child = spawn(cliBinPath, ['--internal-flusher'], {\n detached: true,\n stdio: 'ignore',\n env: process.env,\n });\n child.unref();\n}\n\nexport async function pushCommand(opts: { event: string }): Promise<number> {\n // The contract is: never block the hook. If we can't parse/auth/whatever, exit 0 quietly\n // (printing to stderr is fine; printing to stdout could be parsed by Claude Code).\n try {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('runtape: not logged in — skipping event\\n');\n return 0;\n }\n\n const raw = await readStdin();\n if (!raw.trim()) {\n // SessionStart in some Claude Code versions may invoke hooks with no stdin; ignore.\n return 0;\n }\n\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n process.stderr.write(`runtape: invalid JSON on stdin: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0;\n }\n\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : null;\n if (!sessionId) {\n process.stderr.write('runtape: missing session_id on hook payload\\n');\n return 0;\n }\n\n const sequence = await nextSequence(sessionId);\n const result = mapHookPayload(opts.event, payload, {\n wall_ts: new Date().toISOString(),\n sequence,\n });\n\n if (result.kind === 'drop') {\n // Notification, unknown hook, or validation failure. Quiet log + exit clean.\n process.stderr.write(`runtape: dropped ${opts.event}: ${result.reason}\\n`);\n return 0;\n }\n\n await appendEvent(sessionId, result.event);\n spawnFlusher(resolveCliBinPath());\n return 0;\n } catch (err) {\n process.stderr.write(`runtape: push error: ${err instanceof Error ? err.message : String(err)}\\n`);\n return 0; // Never fail the hook.\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { paths } from './paths.js';\n\n// Per-session monotonic counter. Persisted as a single integer in seq/<session_id>.\n// Hooks fire one-at-a-time per session (Claude Code waits for the hook to exit before\n// firing the next), so we do NOT need cross-process locking — but we DO need the value\n// to survive across hook invocations, since each `runtape push` is a separate process.\nexport async function nextSequence(sessionId: string): Promise<number> {\n const file = paths.seqFile(sessionId);\n await mkdir(dirname(file), { recursive: true });\n\n let current = 0;\n try {\n const raw = await readFile(file, 'utf8');\n const parsed = Number.parseInt(raw.trim(), 10);\n if (Number.isFinite(parsed) && parsed >= 0) current = parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n const next = current + 1;\n await writeFile(file, String(next));\n return next - 1; // First call returns 0 (matching the Zod schema's nonnegative invariant).\n}\n","import { appendFile, mkdir, readFile, readdir, writeFile, unlink, stat } from 'node:fs/promises';\nimport type { RuntapeEvent } from '../types.js';\nimport { paths } from './paths.js';\n\n// Atomic append-as-line. POSIX guarantees a single write() of <PIPE_BUF bytes (≥512) is\n// atomic, and we further constrain ourselves to one line per call. Two concurrent\n// appenders (e.g. two Claude Code instances writing to the same session — impossible\n// today, but cheap defense) cannot interleave a single line.\nexport async function appendEvent(sessionId: string, event: RuntapeEvent): Promise<void> {\n await mkdir(paths.bufferDir, { recursive: true });\n const line = JSON.stringify(event) + '\\n';\n await appendFile(paths.bufferFile(sessionId), line, { encoding: 'utf8' });\n}\n\nexport type BufferedSession = {\n sessionId: string;\n events: RuntapeEvent[];\n raw: string[]; // Raw lines, so we can rewrite exactly what we read on partial-flush.\n};\n\nexport async function listBufferedSessions(): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(paths.bufferDir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n return entries.filter((e) => e.endsWith('.ndjson')).map((e) => e.slice(0, -'.ndjson'.length));\n}\n\nexport async function readBufferedSession(sessionId: string): Promise<BufferedSession | null> {\n let raw: string;\n try {\n raw = await readFile(paths.bufferFile(sessionId), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n const events: RuntapeEvent[] = [];\n for (const line of lines) {\n try {\n events.push(JSON.parse(line) as RuntapeEvent);\n } catch {\n // Skip malformed lines — they could only get there via a half-written disk; drop them.\n }\n }\n return { sessionId, events, raw: lines };\n}\n\n// Atomic truncate-after-flush: write a temp file with the unflushed remainder, then rename.\n// If `unflushedLines` is empty, delete the buffer file entirely.\nexport async function rewriteBufferedSession(sessionId: string, unflushedLines: string[]): Promise<void> {\n const file = paths.bufferFile(sessionId);\n if (unflushedLines.length === 0) {\n try {\n await unlink(file);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n return;\n }\n const tmp = file + '.tmp';\n await writeFile(tmp, unflushedLines.map((l) => l + '\\n').join(''));\n // rename is atomic within the same filesystem; both paths are in ~/.runtape/buffer.\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\n}\n\nexport async function bufferSize(sessionId: string): Promise<number> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return 0;\n throw err;\n }\n}\n\nexport function bufferDirPath(): string {\n return paths.bufferDir;\n}\n\n// Used by tests + future GC: stat-mtime in ms for the buffer file.\nexport async function bufferMtimeMs(sessionId: string): Promise<number | null> {\n try {\n const s = await stat(paths.bufferFile(sessionId));\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\n// Re-export for callers that want to know where the file lives without importing paths twice.\nexport function bufferFilePath(sessionId: string): string {\n return paths.bufferFile(sessionId);\n}\n","import { readFile } from 'node:fs/promises';\nimport { readConfig } from '../lib/config.js';\nimport { listBufferedSessions, bufferSize, bufferMtimeMs } from '../lib/buffer.js';\nimport { paths } from '../lib/paths.js';\nimport { pingProject } from '../lib/api.js';\n\nasync function readFlusherPid(): Promise<number | null> {\n try {\n const raw = await readFile(paths.flusherPid, 'utf8');\n const n = Number.parseInt(raw.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\nexport async function statusCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stdout.write('Not logged in. Run `runtape login`.\\n');\n return 0;\n }\n\n process.stdout.write(`Server: ${cfg.server_url}\\n`);\n process.stdout.write(`API key: ${cfg.api_key.slice(0, 8)}…${cfg.api_key.slice(-4)}\\n`);\n\n const ping = await pingProject(cfg.server_url, cfg.api_key);\n process.stdout.write(`Reachable: ${ping.ok ? 'yes' : `no (${ping.detail ?? ping.status})`}\\n`);\n\n const sessions = await listBufferedSessions();\n if (sessions.length === 0) {\n process.stdout.write('Buffer: empty.\\n');\n } else {\n process.stdout.write(`Buffer: ${sessions.length} session(s) pending.\\n`);\n for (const s of sessions) {\n const size = await bufferSize(s);\n const mtime = await bufferMtimeMs(s);\n const ageSec = mtime ? Math.round((Date.now() - mtime) / 1000) : null;\n process.stdout.write(` ${s}: ${size} bytes${ageSec !== null ? `, updated ${ageSec}s ago` : ''}\\n`);\n }\n }\n\n const flusherPid = await readFlusherPid();\n if (flusherPid !== null) {\n process.stdout.write(`Flusher: PID ${flusherPid}\\n`);\n } else {\n process.stdout.write('Flusher: not running.\\n');\n }\n\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { readConfig } from '../lib/config.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n // Cross-platform \"open this URL in the default browser\".\n // macOS: `open <url>`. Linux: `xdg-open <url>`. Windows: `cmd /c start \"\" <url>`.\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nexport async function runsCommand(): Promise<number> {\n const cfg = await readConfig();\n if (!cfg) {\n process.stderr.write('Not logged in. Run `runtape login` first.\\n');\n return 1;\n }\n\n const url = `${cfg.server_url.replace(/\\/$/, '')}/dashboard/runs`;\n const { cmd, args } = openCommand();\n\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', (err) => {\n process.stderr.write(`Could not launch browser (${err.message}). Open this manually:\\n${url}\\n`);\n });\n child.unref();\n\n process.stdout.write(`Opening ${url}\\n`);\n return 0;\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { Config, defaultServerUrl, readConfig, writeConfig } from '../lib/config.js';\nimport { pingProject } from '../lib/api.js';\nimport { installHooks } from '../lib/hooks-installer.js';\nimport { resolveCliBinPath } from '../lib/cli-bin.js';\n\nfunction openCommand(): { cmd: string; args: string[] } {\n if (platform === 'darwin') return { cmd: 'open', args: [] };\n if (platform === 'win32') return { cmd: 'cmd', args: ['/c', 'start', ''] };\n return { cmd: 'xdg-open', args: [] };\n}\n\nfunction openInBrowser(url: string): void {\n const { cmd, args } = openCommand();\n const child = spawn(cmd, [...args, url], { stdio: 'ignore', detached: true });\n child.on('error', () => {\n /* swallow — caller already printed the URL to fall back on */\n });\n child.unref();\n}\n\nasync function promptYesNo(rl: Awaited<ReturnType<typeof createInterface>>, question: string, defaultYes: boolean): Promise<boolean> {\n const suffix = defaultYes ? '(Y/n)' : '(y/N)';\n const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();\n if (answer === '') return defaultYes;\n return answer === 'y' || answer === 'yes';\n}\n\nexport async function setupCommand(opts: { noBrowser?: boolean }): Promise<number> {\n const rl = createInterface({ input, output });\n\n try {\n process.stdout.write('\\nRuntape setup\\n');\n process.stdout.write('Let\\'s get your Claude Code runs captured.\\n\\n');\n\n // Step 1 — short-circuit if already configured\n const existing = await readConfig();\n if (existing) {\n process.stdout.write(`Already logged in to ${existing.server_url}\\n`);\n process.stdout.write(`API key: ${existing.api_key.slice(0, 8)}…${existing.api_key.slice(-4)}\\n`);\n const reconfigure = await promptYesNo(rl, 'Reconfigure?', false);\n if (!reconfigure) {\n process.stdout.write('\\nKeeping existing credentials. Moving on to hook install.\\n');\n return await installAndFinish(rl, existing.server_url);\n }\n }\n\n // Step 2 — server URL\n const suggestedUrl = defaultServerUrl();\n process.stdout.write(`Step 1/3 — Backend\\n`);\n const urlInput = (await rl.question(`Server URL [${suggestedUrl}]: `)).trim();\n const serverUrl = urlInput === '' ? suggestedUrl : urlInput;\n\n // Step 3 — open dashboard for the user to grab their key\n const dashboardUrl = `${serverUrl.replace(/\\/$/, '')}/dashboard`;\n process.stdout.write(`\\nStep 2/3 — API key\\n`);\n process.stdout.write(`Your API key lives in the dashboard:\\n ${dashboardUrl}\\n`);\n if (!opts.noBrowser) {\n openInBrowser(dashboardUrl);\n process.stdout.write('(opened in your browser)\\n');\n }\n\n // Step 4 — paste + validate\n const apiKey = (await rl.question('\\nPaste your API key (rtk_…): ')).trim();\n const validation = Config.shape.api_key.safeParse(apiKey);\n if (!validation.success) {\n process.stderr.write(`\\nInvalid API key format. Expected rtk_<64 hex chars>.\\n`);\n return 2;\n }\n\n process.stdout.write(`Validating against ${serverUrl}…\\n`);\n const ping = await pingProject(serverUrl, apiKey);\n if (!ping.ok) {\n process.stderr.write(\n `\\nLogin failed: ${ping.status === 401 ? 'unknown API key' : ping.detail ?? 'server unreachable'}\\n`,\n );\n return 1;\n }\n\n await writeConfig({ api_key: apiKey, server_url: serverUrl });\n process.stdout.write('Credentials saved.\\n');\n\n // Step 5 — install hooks\n return await installAndFinish(rl, serverUrl);\n } finally {\n rl.close();\n }\n}\n\nasync function installAndFinish(\n rl: Awaited<ReturnType<typeof createInterface>>,\n serverUrl: string,\n): Promise<number> {\n process.stdout.write(`\\nStep 3/3 — Claude Code hooks\\n`);\n const install = await promptYesNo(\n rl,\n 'Install Runtape hooks into ~/.claude/settings.json now?',\n true,\n );\n\n if (!install) {\n process.stdout.write('\\nSkipped hook install. Run `runtape install` when ready.\\n');\n return 0;\n }\n\n const cliBinPath = resolveCliBinPath();\n const result = await installHooks('user', cliBinPath);\n process.stdout.write(`Updated ${result.settingsPath}\\n`);\n if (result.addedHooks.length === 0) {\n process.stdout.write('Hooks already installed — nothing changed.\\n');\n } else {\n process.stdout.write(`Added: ${result.addedHooks.join(', ')}\\n`);\n }\n\n process.stdout.write('\\nSetup complete.\\n');\n process.stdout.write('Now run `claude -p \"any prompt\"` from any directory and watch the run land at:\\n');\n process.stdout.write(` ${serverUrl.replace(/\\/$/, '')}/dashboard\\n\\n`);\n return 0;\n}\n","import { appendFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readConfig } from './config.js';\nimport { listBufferedSessions, readBufferedSession, rewriteBufferedSession } from './buffer.js';\nimport { postEvents } from './api.js';\nimport { paths } from './paths.js';\n\nconst POLL_INTERVAL_MS = 1500;\nconst IDLE_EXIT_MS = 30_000;\nconst BATCH_MAX = 100;\nconst BACKOFF_STEPS_MS = [1000, 2000, 4000, 8000, 16_000, 32_000, 60_000];\n\nasync function log(line: string): Promise<void> {\n try {\n await mkdir(dirname(paths.flusherLog), { recursive: true });\n await appendFile(paths.flusherLog, `${new Date().toISOString()} ${line}\\n`);\n } catch {\n /* never throw out of logging */\n }\n}\n\nasync function isProcessAlive(pid: number): Promise<boolean> {\n try {\n process.kill(pid, 0); // Signal 0 = existence check, doesn't actually signal.\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ESRCH') return false;\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true; // Exists, owned by someone else.\n return false;\n }\n}\n\n// Acquire-or-detect-running. Returns true if we became the flusher; false if one is already running.\nexport async function acquirePidLock(): Promise<boolean> {\n await mkdir(dirname(paths.flusherPid), { recursive: true });\n try {\n const existing = await readFile(paths.flusherPid, 'utf8');\n const pid = Number.parseInt(existing.trim(), 10);\n if (Number.isFinite(pid) && (await isProcessAlive(pid))) {\n return false; // Another flusher is alive.\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await writeFile(paths.flusherPid, String(process.pid));\n return true;\n}\n\nasync function releasePidLock(): Promise<void> {\n try {\n await unlink(paths.flusherPid);\n } catch {\n /* ignore */\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// Drain a single session's buffer in batches of up to BATCH_MAX. Returns true if any\n// events were successfully flushed.\nasync function drainSession(sessionId: string, serverUrl: string, apiKey: string): Promise<boolean> {\n const snapshot = await readBufferedSession(sessionId);\n if (!snapshot || snapshot.events.length === 0) {\n await rewriteBufferedSession(sessionId, []);\n return false;\n }\n\n let cursor = 0;\n let anyFlushed = false;\n while (cursor < snapshot.events.length) {\n const slice = snapshot.events.slice(cursor, cursor + BATCH_MAX);\n const result = await postEvents(serverUrl, apiKey, slice);\n\n if (result.ok) {\n cursor += slice.length;\n anyFlushed = true;\n continue;\n }\n\n if (!result.retryable) {\n // Poison batch — log + drop the slice to prevent stuck buffer. 4xx is on us (or stale CLI vs server).\n await log(`drop_poison session=${sessionId} status=${result.status} error=${result.error.slice(0, 200)}`);\n cursor += slice.length;\n anyFlushed = true; // We've made forward progress (toward emptying the buffer), so don't backoff.\n continue;\n }\n\n // Retryable — stop draining this session, leave the rest for next poll cycle.\n await log(`retryable session=${sessionId} status=${result.status} cursor=${cursor} error=${result.error.slice(0, 200)}`);\n break;\n }\n\n const remaining = snapshot.raw.slice(cursor);\n await rewriteBufferedSession(sessionId, remaining);\n return anyFlushed;\n}\n\nexport async function runFlusher(): Promise<void> {\n const acquired = await acquirePidLock();\n if (!acquired) {\n await log('exit_already_running');\n return;\n }\n\n await log(`start pid=${process.pid}`);\n\n let lastActivityMs = Date.now();\n let backoffIdx = 0;\n\n try {\n while (true) {\n const cfg = await readConfig();\n if (!cfg) {\n // No config yet — we shouldn't have been spawned. Exit cleanly.\n await log('exit_no_config');\n return;\n }\n\n const sessions = await listBufferedSessions();\n let flushedThisCycle = false;\n for (const sessionId of sessions) {\n const flushed = await drainSession(sessionId, cfg.server_url, cfg.api_key);\n flushedThisCycle = flushedThisCycle || flushed;\n }\n\n if (flushedThisCycle) {\n lastActivityMs = Date.now();\n backoffIdx = 0;\n }\n\n // Idle exit.\n const remaining = await listBufferedSessions();\n const idleMs = Date.now() - lastActivityMs;\n if (remaining.length === 0 && idleMs >= IDLE_EXIT_MS) {\n await log(`exit_idle idle_ms=${idleMs}`);\n return;\n }\n\n // Backoff when buffer is non-empty but we couldn't drain anything (server down).\n // Reset backoff when we made progress.\n const wait =\n remaining.length > 0 && !flushedThisCycle\n ? BACKOFF_STEPS_MS[Math.min(backoffIdx++, BACKOFF_STEPS_MS.length - 1)]\n : POLL_INTERVAL_MS;\n await delay(wait);\n }\n } catch (err) {\n await log(`crash error=${err instanceof Error ? err.message : String(err)}`);\n throw err;\n } finally {\n await releasePidLock();\n }\n}\n"],"mappings":"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","names":["fileURLToPath","dirname","join","createInterface","input","output","mkdir","readFile","writeFile","dirname","readFile","mkdir","dirname","writeFile","dirname","createInterface","input","output","mkdir","readFile","writeFile","dirname","mkdir","dirname","readFile","writeFile","mkdir","readFile","writeFile","unlink","mkdir","readFile","unlink","writeFile","readFile","readFile","spawn","spawn","spawn","platform","createInterface","input","output","openCommand","platform","spawn","createInterface","input","output","appendFile","mkdir","readFile","unlink","writeFile","dirname","mkdir","dirname","appendFile","readFile","writeFile","unlink","resolve","join","dirname","fileURLToPath"]}
package/dist/types.d.ts CHANGED
@@ -13,21 +13,21 @@ declare const SessionStartEvent: z.ZodObject<{
13
13
  type: z.ZodLiteral<"session_start">;
14
14
  source: z.ZodString;
15
15
  }, "strip", z.ZodTypeAny, {
16
- type: "session_start";
17
16
  session_id: string;
18
17
  transcript_path: string;
19
18
  cwd: string;
20
19
  hook_event_name: string;
20
+ type: "session_start";
21
21
  wall_ts: string;
22
22
  sequence: number;
23
23
  source: string;
24
24
  permission_mode?: string | undefined;
25
25
  }, {
26
- type: "session_start";
27
26
  session_id: string;
28
27
  transcript_path: string;
29
28
  cwd: string;
30
29
  hook_event_name: string;
30
+ type: "session_start";
31
31
  wall_ts: string;
32
32
  sequence: number;
33
33
  source: string;
@@ -46,21 +46,21 @@ declare const UserPromptEvent: z.ZodObject<{
46
46
  type: z.ZodLiteral<"user_prompt">;
47
47
  prompt: z.ZodString;
48
48
  }, "strip", z.ZodTypeAny, {
49
- type: "user_prompt";
50
49
  session_id: string;
51
50
  transcript_path: string;
52
51
  cwd: string;
53
52
  hook_event_name: string;
53
+ type: "user_prompt";
54
54
  wall_ts: string;
55
55
  sequence: number;
56
56
  prompt: string;
57
57
  permission_mode?: string | undefined;
58
58
  }, {
59
- type: "user_prompt";
60
59
  session_id: string;
61
60
  transcript_path: string;
62
61
  cwd: string;
63
62
  hook_event_name: string;
63
+ type: "user_prompt";
64
64
  wall_ts: string;
65
65
  sequence: number;
66
66
  prompt: string;
@@ -81,11 +81,11 @@ declare const ToolAttemptEvent: z.ZodObject<{
81
81
  tool_input: z.ZodUnknown;
82
82
  tool_use_id: z.ZodString;
83
83
  }, "strip", z.ZodTypeAny, {
84
- type: "tool_attempt";
85
84
  session_id: string;
86
85
  transcript_path: string;
87
86
  cwd: string;
88
87
  hook_event_name: string;
88
+ type: "tool_attempt";
89
89
  wall_ts: string;
90
90
  sequence: number;
91
91
  tool_name: string;
@@ -93,11 +93,11 @@ declare const ToolAttemptEvent: z.ZodObject<{
93
93
  permission_mode?: string | undefined;
94
94
  tool_input?: unknown;
95
95
  }, {
96
- type: "tool_attempt";
97
96
  session_id: string;
98
97
  transcript_path: string;
99
98
  cwd: string;
100
99
  hook_event_name: string;
100
+ type: "tool_attempt";
101
101
  wall_ts: string;
102
102
  sequence: number;
103
103
  tool_name: string;
@@ -122,11 +122,11 @@ declare const ToolCallEvent: z.ZodObject<{
122
122
  tool_use_id: z.ZodString;
123
123
  duration_ms: z.ZodNumber;
124
124
  }, "strip", z.ZodTypeAny, {
125
- type: "tool_call";
126
125
  session_id: string;
127
126
  transcript_path: string;
128
127
  cwd: string;
129
128
  hook_event_name: string;
129
+ type: "tool_call";
130
130
  wall_ts: string;
131
131
  sequence: number;
132
132
  tool_name: string;
@@ -136,11 +136,11 @@ declare const ToolCallEvent: z.ZodObject<{
136
136
  tool_input?: unknown;
137
137
  tool_response?: unknown;
138
138
  }, {
139
- type: "tool_call";
140
139
  session_id: string;
141
140
  transcript_path: string;
142
141
  cwd: string;
143
142
  hook_event_name: string;
143
+ type: "tool_call";
144
144
  wall_ts: string;
145
145
  sequence: number;
146
146
  tool_name: string;
@@ -167,11 +167,11 @@ declare const SubagentEndEvent: z.ZodObject<{
167
167
  last_assistant_message: z.ZodString;
168
168
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
169
169
  }, "strip", z.ZodTypeAny, {
170
- type: "subagent_end";
171
170
  session_id: string;
172
171
  transcript_path: string;
173
172
  cwd: string;
174
173
  hook_event_name: string;
174
+ type: "subagent_end";
175
175
  wall_ts: string;
176
176
  sequence: number;
177
177
  agent_id: string;
@@ -181,11 +181,11 @@ declare const SubagentEndEvent: z.ZodObject<{
181
181
  permission_mode?: string | undefined;
182
182
  stop_hook_active?: boolean | undefined;
183
183
  }, {
184
- type: "subagent_end";
185
184
  session_id: string;
186
185
  transcript_path: string;
187
186
  cwd: string;
188
187
  hook_event_name: string;
188
+ type: "subagent_end";
189
189
  wall_ts: string;
190
190
  sequence: number;
191
191
  agent_id: string;
@@ -209,22 +209,22 @@ declare const SessionEndEvent: z.ZodObject<{
209
209
  last_assistant_message: z.ZodOptional<z.ZodString>;
210
210
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
211
211
  }, "strip", z.ZodTypeAny, {
212
- type: "session_end";
213
212
  session_id: string;
214
213
  transcript_path: string;
215
214
  cwd: string;
216
215
  hook_event_name: string;
216
+ type: "session_end";
217
217
  wall_ts: string;
218
218
  sequence: number;
219
219
  permission_mode?: string | undefined;
220
220
  last_assistant_message?: string | undefined;
221
221
  stop_hook_active?: boolean | undefined;
222
222
  }, {
223
- type: "session_end";
224
223
  session_id: string;
225
224
  transcript_path: string;
226
225
  cwd: string;
227
226
  hook_event_name: string;
227
+ type: "session_end";
228
228
  wall_ts: string;
229
229
  sequence: number;
230
230
  permission_mode?: string | undefined;
@@ -244,21 +244,21 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
244
244
  type: z.ZodLiteral<"session_start">;
245
245
  source: z.ZodString;
246
246
  }, "strip", z.ZodTypeAny, {
247
- type: "session_start";
248
247
  session_id: string;
249
248
  transcript_path: string;
250
249
  cwd: string;
251
250
  hook_event_name: string;
251
+ type: "session_start";
252
252
  wall_ts: string;
253
253
  sequence: number;
254
254
  source: string;
255
255
  permission_mode?: string | undefined;
256
256
  }, {
257
- type: "session_start";
258
257
  session_id: string;
259
258
  transcript_path: string;
260
259
  cwd: string;
261
260
  hook_event_name: string;
261
+ type: "session_start";
262
262
  wall_ts: string;
263
263
  sequence: number;
264
264
  source: string;
@@ -276,21 +276,21 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
276
276
  type: z.ZodLiteral<"user_prompt">;
277
277
  prompt: z.ZodString;
278
278
  }, "strip", z.ZodTypeAny, {
279
- type: "user_prompt";
280
279
  session_id: string;
281
280
  transcript_path: string;
282
281
  cwd: string;
283
282
  hook_event_name: string;
283
+ type: "user_prompt";
284
284
  wall_ts: string;
285
285
  sequence: number;
286
286
  prompt: string;
287
287
  permission_mode?: string | undefined;
288
288
  }, {
289
- type: "user_prompt";
290
289
  session_id: string;
291
290
  transcript_path: string;
292
291
  cwd: string;
293
292
  hook_event_name: string;
293
+ type: "user_prompt";
294
294
  wall_ts: string;
295
295
  sequence: number;
296
296
  prompt: string;
@@ -310,11 +310,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
310
310
  tool_input: z.ZodUnknown;
311
311
  tool_use_id: z.ZodString;
312
312
  }, "strip", z.ZodTypeAny, {
313
- type: "tool_attempt";
314
313
  session_id: string;
315
314
  transcript_path: string;
316
315
  cwd: string;
317
316
  hook_event_name: string;
317
+ type: "tool_attempt";
318
318
  wall_ts: string;
319
319
  sequence: number;
320
320
  tool_name: string;
@@ -322,11 +322,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
322
322
  permission_mode?: string | undefined;
323
323
  tool_input?: unknown;
324
324
  }, {
325
- type: "tool_attempt";
326
325
  session_id: string;
327
326
  transcript_path: string;
328
327
  cwd: string;
329
328
  hook_event_name: string;
329
+ type: "tool_attempt";
330
330
  wall_ts: string;
331
331
  sequence: number;
332
332
  tool_name: string;
@@ -350,11 +350,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
350
350
  tool_use_id: z.ZodString;
351
351
  duration_ms: z.ZodNumber;
352
352
  }, "strip", z.ZodTypeAny, {
353
- type: "tool_call";
354
353
  session_id: string;
355
354
  transcript_path: string;
356
355
  cwd: string;
357
356
  hook_event_name: string;
357
+ type: "tool_call";
358
358
  wall_ts: string;
359
359
  sequence: number;
360
360
  tool_name: string;
@@ -364,11 +364,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
364
364
  tool_input?: unknown;
365
365
  tool_response?: unknown;
366
366
  }, {
367
- type: "tool_call";
368
367
  session_id: string;
369
368
  transcript_path: string;
370
369
  cwd: string;
371
370
  hook_event_name: string;
371
+ type: "tool_call";
372
372
  wall_ts: string;
373
373
  sequence: number;
374
374
  tool_name: string;
@@ -394,11 +394,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
394
394
  last_assistant_message: z.ZodString;
395
395
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
396
396
  }, "strip", z.ZodTypeAny, {
397
- type: "subagent_end";
398
397
  session_id: string;
399
398
  transcript_path: string;
400
399
  cwd: string;
401
400
  hook_event_name: string;
401
+ type: "subagent_end";
402
402
  wall_ts: string;
403
403
  sequence: number;
404
404
  agent_id: string;
@@ -408,11 +408,11 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
408
408
  permission_mode?: string | undefined;
409
409
  stop_hook_active?: boolean | undefined;
410
410
  }, {
411
- type: "subagent_end";
412
411
  session_id: string;
413
412
  transcript_path: string;
414
413
  cwd: string;
415
414
  hook_event_name: string;
415
+ type: "subagent_end";
416
416
  wall_ts: string;
417
417
  sequence: number;
418
418
  agent_id: string;
@@ -435,22 +435,22 @@ declare const RuntapeEvent: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
435
435
  last_assistant_message: z.ZodOptional<z.ZodString>;
436
436
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
437
437
  }, "strip", z.ZodTypeAny, {
438
- type: "session_end";
439
438
  session_id: string;
440
439
  transcript_path: string;
441
440
  cwd: string;
442
441
  hook_event_name: string;
442
+ type: "session_end";
443
443
  wall_ts: string;
444
444
  sequence: number;
445
445
  permission_mode?: string | undefined;
446
446
  last_assistant_message?: string | undefined;
447
447
  stop_hook_active?: boolean | undefined;
448
448
  }, {
449
- type: "session_end";
450
449
  session_id: string;
451
450
  transcript_path: string;
452
451
  cwd: string;
453
452
  hook_event_name: string;
453
+ type: "session_end";
454
454
  wall_ts: string;
455
455
  sequence: number;
456
456
  permission_mode?: string | undefined;
@@ -472,21 +472,21 @@ declare const IngestionRequest: z.ZodObject<{
472
472
  type: z.ZodLiteral<"session_start">;
473
473
  source: z.ZodString;
474
474
  }, "strip", z.ZodTypeAny, {
475
- type: "session_start";
476
475
  session_id: string;
477
476
  transcript_path: string;
478
477
  cwd: string;
479
478
  hook_event_name: string;
479
+ type: "session_start";
480
480
  wall_ts: string;
481
481
  sequence: number;
482
482
  source: string;
483
483
  permission_mode?: string | undefined;
484
484
  }, {
485
- type: "session_start";
486
485
  session_id: string;
487
486
  transcript_path: string;
488
487
  cwd: string;
489
488
  hook_event_name: string;
489
+ type: "session_start";
490
490
  wall_ts: string;
491
491
  sequence: number;
492
492
  source: string;
@@ -504,21 +504,21 @@ declare const IngestionRequest: z.ZodObject<{
504
504
  type: z.ZodLiteral<"user_prompt">;
505
505
  prompt: z.ZodString;
506
506
  }, "strip", z.ZodTypeAny, {
507
- type: "user_prompt";
508
507
  session_id: string;
509
508
  transcript_path: string;
510
509
  cwd: string;
511
510
  hook_event_name: string;
511
+ type: "user_prompt";
512
512
  wall_ts: string;
513
513
  sequence: number;
514
514
  prompt: string;
515
515
  permission_mode?: string | undefined;
516
516
  }, {
517
- type: "user_prompt";
518
517
  session_id: string;
519
518
  transcript_path: string;
520
519
  cwd: string;
521
520
  hook_event_name: string;
521
+ type: "user_prompt";
522
522
  wall_ts: string;
523
523
  sequence: number;
524
524
  prompt: string;
@@ -538,11 +538,11 @@ declare const IngestionRequest: z.ZodObject<{
538
538
  tool_input: z.ZodUnknown;
539
539
  tool_use_id: z.ZodString;
540
540
  }, "strip", z.ZodTypeAny, {
541
- type: "tool_attempt";
542
541
  session_id: string;
543
542
  transcript_path: string;
544
543
  cwd: string;
545
544
  hook_event_name: string;
545
+ type: "tool_attempt";
546
546
  wall_ts: string;
547
547
  sequence: number;
548
548
  tool_name: string;
@@ -550,11 +550,11 @@ declare const IngestionRequest: z.ZodObject<{
550
550
  permission_mode?: string | undefined;
551
551
  tool_input?: unknown;
552
552
  }, {
553
- type: "tool_attempt";
554
553
  session_id: string;
555
554
  transcript_path: string;
556
555
  cwd: string;
557
556
  hook_event_name: string;
557
+ type: "tool_attempt";
558
558
  wall_ts: string;
559
559
  sequence: number;
560
560
  tool_name: string;
@@ -578,11 +578,11 @@ declare const IngestionRequest: z.ZodObject<{
578
578
  tool_use_id: z.ZodString;
579
579
  duration_ms: z.ZodNumber;
580
580
  }, "strip", z.ZodTypeAny, {
581
- type: "tool_call";
582
581
  session_id: string;
583
582
  transcript_path: string;
584
583
  cwd: string;
585
584
  hook_event_name: string;
585
+ type: "tool_call";
586
586
  wall_ts: string;
587
587
  sequence: number;
588
588
  tool_name: string;
@@ -592,11 +592,11 @@ declare const IngestionRequest: z.ZodObject<{
592
592
  tool_input?: unknown;
593
593
  tool_response?: unknown;
594
594
  }, {
595
- type: "tool_call";
596
595
  session_id: string;
597
596
  transcript_path: string;
598
597
  cwd: string;
599
598
  hook_event_name: string;
599
+ type: "tool_call";
600
600
  wall_ts: string;
601
601
  sequence: number;
602
602
  tool_name: string;
@@ -622,11 +622,11 @@ declare const IngestionRequest: z.ZodObject<{
622
622
  last_assistant_message: z.ZodString;
623
623
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
624
624
  }, "strip", z.ZodTypeAny, {
625
- type: "subagent_end";
626
625
  session_id: string;
627
626
  transcript_path: string;
628
627
  cwd: string;
629
628
  hook_event_name: string;
629
+ type: "subagent_end";
630
630
  wall_ts: string;
631
631
  sequence: number;
632
632
  agent_id: string;
@@ -636,11 +636,11 @@ declare const IngestionRequest: z.ZodObject<{
636
636
  permission_mode?: string | undefined;
637
637
  stop_hook_active?: boolean | undefined;
638
638
  }, {
639
- type: "subagent_end";
640
639
  session_id: string;
641
640
  transcript_path: string;
642
641
  cwd: string;
643
642
  hook_event_name: string;
643
+ type: "subagent_end";
644
644
  wall_ts: string;
645
645
  sequence: number;
646
646
  agent_id: string;
@@ -663,22 +663,22 @@ declare const IngestionRequest: z.ZodObject<{
663
663
  last_assistant_message: z.ZodOptional<z.ZodString>;
664
664
  stop_hook_active: z.ZodOptional<z.ZodBoolean>;
665
665
  }, "strip", z.ZodTypeAny, {
666
- type: "session_end";
667
666
  session_id: string;
668
667
  transcript_path: string;
669
668
  cwd: string;
670
669
  hook_event_name: string;
670
+ type: "session_end";
671
671
  wall_ts: string;
672
672
  sequence: number;
673
673
  permission_mode?: string | undefined;
674
674
  last_assistant_message?: string | undefined;
675
675
  stop_hook_active?: boolean | undefined;
676
676
  }, {
677
- type: "session_end";
678
677
  session_id: string;
679
678
  transcript_path: string;
680
679
  cwd: string;
681
680
  hook_event_name: string;
681
+ type: "session_end";
682
682
  wall_ts: string;
683
683
  sequence: number;
684
684
  permission_mode?: string | undefined;
@@ -687,31 +687,31 @@ declare const IngestionRequest: z.ZodObject<{
687
687
  }>]>, "many">;
688
688
  }, "strip", z.ZodTypeAny, {
689
689
  events: ({
690
- type: "session_start";
691
690
  session_id: string;
692
691
  transcript_path: string;
693
692
  cwd: string;
694
693
  hook_event_name: string;
694
+ type: "session_start";
695
695
  wall_ts: string;
696
696
  sequence: number;
697
697
  source: string;
698
698
  permission_mode?: string | undefined;
699
699
  } | {
700
- type: "user_prompt";
701
700
  session_id: string;
702
701
  transcript_path: string;
703
702
  cwd: string;
704
703
  hook_event_name: string;
704
+ type: "user_prompt";
705
705
  wall_ts: string;
706
706
  sequence: number;
707
707
  prompt: string;
708
708
  permission_mode?: string | undefined;
709
709
  } | {
710
- type: "tool_attempt";
711
710
  session_id: string;
712
711
  transcript_path: string;
713
712
  cwd: string;
714
713
  hook_event_name: string;
714
+ type: "tool_attempt";
715
715
  wall_ts: string;
716
716
  sequence: number;
717
717
  tool_name: string;
@@ -719,11 +719,11 @@ declare const IngestionRequest: z.ZodObject<{
719
719
  permission_mode?: string | undefined;
720
720
  tool_input?: unknown;
721
721
  } | {
722
- type: "tool_call";
723
722
  session_id: string;
724
723
  transcript_path: string;
725
724
  cwd: string;
726
725
  hook_event_name: string;
726
+ type: "tool_call";
727
727
  wall_ts: string;
728
728
  sequence: number;
729
729
  tool_name: string;
@@ -733,11 +733,11 @@ declare const IngestionRequest: z.ZodObject<{
733
733
  tool_input?: unknown;
734
734
  tool_response?: unknown;
735
735
  } | {
736
- type: "subagent_end";
737
736
  session_id: string;
738
737
  transcript_path: string;
739
738
  cwd: string;
740
739
  hook_event_name: string;
740
+ type: "subagent_end";
741
741
  wall_ts: string;
742
742
  sequence: number;
743
743
  agent_id: string;
@@ -747,11 +747,11 @@ declare const IngestionRequest: z.ZodObject<{
747
747
  permission_mode?: string | undefined;
748
748
  stop_hook_active?: boolean | undefined;
749
749
  } | {
750
- type: "session_end";
751
750
  session_id: string;
752
751
  transcript_path: string;
753
752
  cwd: string;
754
753
  hook_event_name: string;
754
+ type: "session_end";
755
755
  wall_ts: string;
756
756
  sequence: number;
757
757
  permission_mode?: string | undefined;
@@ -760,31 +760,31 @@ declare const IngestionRequest: z.ZodObject<{
760
760
  })[];
761
761
  }, {
762
762
  events: ({
763
- type: "session_start";
764
763
  session_id: string;
765
764
  transcript_path: string;
766
765
  cwd: string;
767
766
  hook_event_name: string;
767
+ type: "session_start";
768
768
  wall_ts: string;
769
769
  sequence: number;
770
770
  source: string;
771
771
  permission_mode?: string | undefined;
772
772
  } | {
773
- type: "user_prompt";
774
773
  session_id: string;
775
774
  transcript_path: string;
776
775
  cwd: string;
777
776
  hook_event_name: string;
777
+ type: "user_prompt";
778
778
  wall_ts: string;
779
779
  sequence: number;
780
780
  prompt: string;
781
781
  permission_mode?: string | undefined;
782
782
  } | {
783
- type: "tool_attempt";
784
783
  session_id: string;
785
784
  transcript_path: string;
786
785
  cwd: string;
787
786
  hook_event_name: string;
787
+ type: "tool_attempt";
788
788
  wall_ts: string;
789
789
  sequence: number;
790
790
  tool_name: string;
@@ -792,11 +792,11 @@ declare const IngestionRequest: z.ZodObject<{
792
792
  permission_mode?: string | undefined;
793
793
  tool_input?: unknown;
794
794
  } | {
795
- type: "tool_call";
796
795
  session_id: string;
797
796
  transcript_path: string;
798
797
  cwd: string;
799
798
  hook_event_name: string;
799
+ type: "tool_call";
800
800
  wall_ts: string;
801
801
  sequence: number;
802
802
  tool_name: string;
@@ -806,11 +806,11 @@ declare const IngestionRequest: z.ZodObject<{
806
806
  tool_input?: unknown;
807
807
  tool_response?: unknown;
808
808
  } | {
809
- type: "subagent_end";
810
809
  session_id: string;
811
810
  transcript_path: string;
812
811
  cwd: string;
813
812
  hook_event_name: string;
813
+ type: "subagent_end";
814
814
  wall_ts: string;
815
815
  sequence: number;
816
816
  agent_id: string;
@@ -820,11 +820,11 @@ declare const IngestionRequest: z.ZodObject<{
820
820
  permission_mode?: string | undefined;
821
821
  stop_hook_active?: boolean | undefined;
822
822
  } | {
823
- type: "session_end";
824
823
  session_id: string;
825
824
  transcript_path: string;
826
825
  cwd: string;
827
826
  hook_event_name: string;
827
+ type: "session_end";
828
828
  wall_ts: string;
829
829
  sequence: number;
830
830
  permission_mode?: string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runtape",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Flight recorder for AI coding agents. Captures Claude Code sessions for replay in runtape.dev.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://runtape.dev",