agent-insights 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -222,11 +222,15 @@ __export(store_exports, {
222
222
  clearAuth: () => clearAuth,
223
223
  getValidToken: () => getValidToken,
224
224
  loadAuth: () => loadAuth,
225
+ loadEmail: () => loadEmail,
225
226
  saveAuth: () => saveAuth
226
227
  });
227
228
  import { chmod, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
228
229
  import { dirname as dirname4 } from "path";
229
230
  import { mkdir as mkdir4 } from "fs/promises";
231
+ async function loadEmail() {
232
+ return (await loadAuth())?.email;
233
+ }
230
234
  async function loadAuth() {
231
235
  try {
232
236
  const raw = await readFile4(authFile(), "utf8");
@@ -275,7 +279,8 @@ async function getValidToken() {
275
279
  const refreshed = {
276
280
  accessToken: tokens.access_token,
277
281
  refreshToken: tokens.refresh_token ?? auth.refreshToken,
278
- expiresAt: Date.now() + tokens.expires_in * 1e3
282
+ expiresAt: Date.now() + tokens.expires_in * 1e3,
283
+ ...auth.email ? { email: auth.email } : {}
279
284
  };
280
285
  await saveAuth(refreshed);
281
286
  return refreshed.accessToken;
@@ -658,16 +663,15 @@ var claudeCodeAdapter = {
658
663
  const path = resolveClaudePath(repoRoot, scope);
659
664
  const settings = await readJson(path);
660
665
  const next = { ...settings ?? {} };
661
- const hooks = { ...next.hooks ?? {} };
666
+ const hooks = {};
667
+ for (const [event, raw] of Object.entries(next.hooks ?? {})) {
668
+ hooks[event] = normalizeEventHooks(raw);
669
+ }
662
670
  for (const event of CLAUDE_HOOK_EVENTS) {
663
- const existing = (hooks[event] ?? []).filter(
664
- (h) => !isAgentInsightsCommand(h.command)
671
+ const withoutInsights = removeAgentInsightsMatchers(
672
+ hooks[event] ?? []
665
673
  );
666
- existing.push({
667
- type: "command",
668
- command: `${HOOK_COMMAND_NAME} hook ${event}`
669
- });
670
- hooks[event] = existing;
674
+ hooks[event] = addAgentInsightsHook(withoutInsights, event);
671
675
  }
672
676
  next.hooks = hooks;
673
677
  await mkdir2(dirname2(path), { recursive: true });
@@ -679,18 +683,16 @@ var claudeCodeAdapter = {
679
683
  const path = resolveClaudePath(repoRoot, scope);
680
684
  const settings = await readJson(path);
681
685
  if (!settings?.hooks) return { removed: false, path };
682
- const hooks = { ...settings.hooks };
686
+ const hooks = {};
683
687
  let touched = false;
684
- for (const [event, entries] of Object.entries(hooks)) {
685
- const filtered = entries.filter(
686
- (h) => !isAgentInsightsCommand(h.command)
687
- );
688
- if (filtered.length !== entries.length) touched = true;
689
- if (filtered.length === 0) {
690
- delete hooks[event];
691
- } else {
692
- hooks[event] = filtered;
688
+ for (const [event, raw] of Object.entries(settings.hooks)) {
689
+ const before = normalizeEventHooks(raw);
690
+ const after = removeAgentInsightsMatchers(before);
691
+ if (after.length !== before.length) touched = true;
692
+ else if (JSON.stringify(after) !== JSON.stringify(before)) {
693
+ touched = true;
693
694
  }
695
+ if (after.length > 0) hooks[event] = after;
694
696
  }
695
697
  settings.hooks = hooks;
696
698
  await writeFile2(path, `${JSON.stringify(settings, null, 2)}
@@ -703,7 +705,9 @@ var claudeCodeAdapter = {
703
705
  );
704
706
  if (!settings?.hooks) return false;
705
707
  return Object.values(settings.hooks).some(
706
- (entries) => entries.some((h) => isAgentInsightsCommand(h.command))
708
+ (raw) => normalizeEventHooks(raw).some(
709
+ (group) => group.hooks.some((h) => isAgentInsightsCommand(h.command))
710
+ )
707
711
  );
708
712
  }
709
713
  };
@@ -714,6 +718,59 @@ function isAgentInsightsCommand(cmd) {
714
718
  if (!cmd) return false;
715
719
  return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);
716
720
  }
721
+ function isHookCommand(value) {
722
+ if (!value || typeof value !== "object") return false;
723
+ const entry = value;
724
+ return entry.type === "command" && typeof entry.command === "string";
725
+ }
726
+ function normalizeEventHooks(raw) {
727
+ const groups = [];
728
+ for (const entry of raw) {
729
+ if (!entry || typeof entry !== "object") continue;
730
+ const record = entry;
731
+ if (Array.isArray(record.hooks)) {
732
+ const commands = record.hooks.filter(isHookCommand);
733
+ if (commands.length === 0) continue;
734
+ groups.push({
735
+ matcher: typeof record.matcher === "string" ? record.matcher : "",
736
+ hooks: commands
737
+ });
738
+ continue;
739
+ }
740
+ if (isHookCommand(record)) {
741
+ groups.push({
742
+ matcher: "",
743
+ hooks: [record]
744
+ });
745
+ }
746
+ }
747
+ return groups;
748
+ }
749
+ function removeAgentInsightsMatchers(matchers) {
750
+ const result = [];
751
+ for (const group of matchers) {
752
+ const hooks = group.hooks.filter(
753
+ (h) => !isAgentInsightsCommand(h.command)
754
+ );
755
+ if (hooks.length > 0) {
756
+ result.push({ ...group, hooks });
757
+ }
758
+ }
759
+ return result;
760
+ }
761
+ function addAgentInsightsHook(matchers, event) {
762
+ const command = `${HOOK_COMMAND_NAME} hook ${event}`;
763
+ const entry = { type: "command", command };
764
+ for (const group of matchers) {
765
+ if (group.matcher === "") {
766
+ if (!group.hooks.some((h) => h.command === command)) {
767
+ group.hooks.push(entry);
768
+ }
769
+ return matchers;
770
+ }
771
+ }
772
+ return [...matchers, { matcher: "", hooks: [entry] }];
773
+ }
717
774
  async function readJson(path) {
718
775
  try {
719
776
  const raw = await readFile2(path, "utf8");
@@ -986,7 +1043,8 @@ var AnalyzerTranscriptStore = class {
986
1043
  "content-type": "application/jsonl",
987
1044
  "x-session-id": input.sessionId,
988
1045
  "x-agent": input.agent,
989
- "x-user-hash": input.userHash
1046
+ "x-user-hash": input.userHash,
1047
+ ...input.userEmail ? { "x-user-email": input.userEmail } : {}
990
1048
  });
991
1049
  const res = await fetch(url, { method: "POST", headers, body });
992
1050
  if (!res.ok) {
@@ -1120,6 +1178,7 @@ async function runDoctor(opts) {
1120
1178
  }
1121
1179
 
1122
1180
  // src/commands/hook.ts
1181
+ init_store();
1123
1182
  async function runHook(eventName, opts) {
1124
1183
  const cfg = await loadConfig();
1125
1184
  if (!cfg || !cfg.enabled) {
@@ -1148,11 +1207,13 @@ async function runHook(eventName, opts) {
1148
1207
  if (mapped.type === "session.end" && cfg.userConsent.transcriptSync && mapped.transcriptPath) {
1149
1208
  try {
1150
1209
  const store = createTranscriptStore(cfg.transcriptStore);
1210
+ const userEmail = await loadEmail();
1151
1211
  const result = await store.upload({
1152
1212
  transcriptPath: mapped.transcriptPath,
1153
1213
  userHash: event.userHash ?? userHash(),
1154
1214
  sessionId: mapped.sessionId ?? "unknown",
1155
- agent: adapter.id
1215
+ agent: adapter.id,
1216
+ ...userEmail ? { userEmail } : {}
1156
1217
  });
1157
1218
  debug("transcript uploaded", {
1158
1219
  size: result.size,
@@ -1493,7 +1554,8 @@ async function runLogin() {
1493
1554
  await saveAuth({
1494
1555
  accessToken: tokens.access_token,
1495
1556
  ...tokens.refresh_token !== void 0 ? { refreshToken: tokens.refresh_token } : {},
1496
- expiresAt: Date.now() + tokens.expires_in * 1e3
1557
+ expiresAt: Date.now() + tokens.expires_in * 1e3,
1558
+ email: userInfo2.email
1497
1559
  });
1498
1560
  const name = userInfo2.preferred_username ?? userInfo2.email;
1499
1561
  log.ok(`Logged in as ${kleur4.bold(name)} (${userInfo2.email})`);
@@ -1569,7 +1631,7 @@ function yn(v) {
1569
1631
  var program = new Command();
1570
1632
  program.name("agent-insights").description(
1571
1633
  "Internal CLI for AI coding agent observability (Claude Code, Cursor)."
1572
- ).version("0.0.12");
1634
+ ).version("0.0.13");
1573
1635
  program.command("login").description("Authenticate with Vercel (Sign in with Vercel).").action(async () => {
1574
1636
  await runLogin();
1575
1637
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/paths.ts","../src/auth/oauth.ts","../src/auth/store.ts","../src/cli.ts","../src/commands/cursor.ts","../src/config/config.ts","../src/config/analyzer.ts","../src/privacy/sanitize.ts","../src/util/identity.ts","../src/privacy/hash.ts","../src/util/log.ts","../src/commands/disable.ts","../src/adapters/claude-code.ts","../src/adapters/cursor.ts","../src/adapters/registry.ts","../src/commands/doctor.ts","../src/transcript/store.ts","../src/config/bypass.ts","../src/commands/hook.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/status.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Root directory for agent-insights state. Honors `AGENT_INSIGHTS_HOME`. */\nexport function configRoot(): string {\n return process.env.AGENT_INSIGHTS_HOME ?? join(homedir(), \".agent-insights\");\n}\n\nexport function configFile(): string {\n return join(configRoot(), \"config.json\");\n}\n\n/** API key storage (`chmod 600`). Not checked into git. */\nexport function credentialsFile(): string {\n return join(configRoot(), \"credentials.json\");\n}\n\n/** OAuth token storage (`chmod 600`). Not checked into git. */\nexport function authFile(): string {\n return join(configRoot(), \"auth.json\");\n}\n\n/** Vercel deployment-protection bypass secret (`chmod 600`). */\nexport function bypassSecretFile(): string {\n return join(configRoot(), \"bypass-secret\");\n}\n\nexport function sessionCacheDir(): string {\n return join(configRoot(), \"session-cache\");\n}\n\nexport function logsDir(): string {\n return join(configRoot(), \"logs\");\n}\n\nexport const CLAUDE_SETTINGS_FILE = \".claude/settings.json\";\nexport const CURSOR_HOOKS_FILE = \".cursor/hooks.json\";\n\n/** User-level Claude Code settings (hooks apply in every repo). */\nexport function claudeUserSettingsPath(): string {\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** User-level Cursor hooks (hooks apply in every project). */\nexport function cursorUserHooksPath(): string {\n return join(homedir(), \".cursor\", \"hooks.json\");\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport { createServer } from \"node:http\";\nimport { platform } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// OAuth app config — Sign-in-with-Vercel client ID (public, not a secret).\n// Override at runtime via AGENT_INSIGHTS_CLIENT_ID env var.\n// ---------------------------------------------------------------------------\nexport const CLIENT_ID =\n process.env.AGENT_INSIGHTS_CLIENT_ID ?? \"cl_TxqqNFTHcF9kEtM48LBFsYfwjhJ9T8sz\";\n\nconst AUTH_ENDPOINT = \"https://vercel.com/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://api.vercel.com/login/oauth/token\";\nconst USERINFO_ENDPOINT = \"https://api.vercel.com/login/oauth/userinfo\";\nconst REVOKE_ENDPOINT = \"https://api.vercel.com/login/oauth/token/revoke\";\n\n// Fixed port — must match the redirect URI registered in the OAuth app.\nconst CALLBACK_PORT = 9797;\nexport const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;\n\nconst SCOPES = \"openid email profile offline_access\";\n\nexport type TokenResponse = {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n id_token?: string;\n};\n\nexport type UserInfo = {\n sub: string;\n email: string;\n email_verified: boolean;\n name?: string;\n preferred_username?: string;\n picture?: string;\n};\n\n// ---------------------------------------------------------------------------\n// PKCE helpers\n// ---------------------------------------------------------------------------\n\nfunction generateCodeVerifier(): string {\n return randomBytes(48).toString(\"base64url\");\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction generateState(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Code + PKCE flow\n// ---------------------------------------------------------------------------\n\nexport type AuthCodeResult =\n | { status: \"success\"; code: string; state: string }\n | { status: \"error\"; message: string };\n\n/**\n * Starts a localhost callback server, opens the Vercel authorization URL in\n * the browser, and waits for the redirect with the auth code.\n * Resolves (or rejects after timeout) when Vercel redirects back.\n */\nexport async function startPkceFlow(timeoutMs = 120_000): Promise<{\n code: string;\n codeVerifier: string;\n}> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n const authUrl = new URL(AUTH_ENDPOINT);\n authUrl.searchParams.set(\"client_id\", CLIENT_ID);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n authUrl.searchParams.set(\"scope\", SCOPES);\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n\n const url = authUrl.toString();\n const codePromise = listenForCallback(state, timeoutMs);\n openBrowser(url);\n // Always print the URL — browser open may fail silently\n process.stderr.write(`\\nIf the browser did not open, visit:\\n ${url}\\n\\n`);\n\n const code = await codePromise;\n return { code, codeVerifier };\n}\n\nfunction listenForCallback(\n expectedState: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${CALLBACK_PORT}`);\n if (url.pathname !== \"/callback\") {\n res.writeHead(404, { connection: \"close\" });\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n const html = (msg: string) =>\n `<html><body style=\"font-family:sans-serif;text-align:center;padding:80px\">` +\n `<h2>${msg}</h2><p>You can close this tab.</p></body></html>`;\n\n // Tell the browser to drop the socket so the server can shut down cleanly\n // and Node's event loop can exit once the callback returns.\n const headers = { \"content-type\": \"text/html\", connection: \"close\" };\n\n const finish = (\n next: () => void,\n body: string,\n status: number,\n ): void => {\n res.writeHead(status, headers);\n res.end(body, () => {\n clearTimeout(timer);\n server.closeAllConnections?.();\n server.close();\n next();\n });\n };\n\n if (error) {\n finish(\n () => reject(new Error(`OAuth error: ${error}`)),\n html(`Login failed: ${error}`),\n 400,\n );\n return;\n }\n\n if (returnedState !== expectedState) {\n finish(\n () => reject(new Error(\"State mismatch — possible CSRF\")),\n html(\"Invalid state parameter — please try again.\"),\n 400,\n );\n return;\n }\n\n if (!code) {\n finish(\n () => reject(new Error(\"No code in callback\")),\n html(\"No authorization code received.\"),\n 400,\n );\n return;\n }\n\n finish(\n () => resolve(code),\n html(\"✓ Logged in! You can close this tab.\"),\n 200,\n );\n });\n\n const timer = setTimeout(() => {\n server.closeAllConnections?.();\n server.close();\n reject(new Error(\"Login timed out — no response within 2 minutes.\"));\n }, timeoutMs);\n timer.unref();\n\n server.listen(CALLBACK_PORT, \"127.0.0.1\", () => {\n // Server is ready; browser will be opened by caller\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n if (err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${CALLBACK_PORT} is already in use. Close any other agent-insights login process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Token exchange\n// ---------------------------------------------------------------------------\n\nexport async function exchangeCodeForToken(\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: CLIENT_ID,\n code,\n code_verifier: codeVerifier,\n redirect_uri: REDIRECT_URI,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n refreshToken: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: CLIENT_ID,\n refresh_token: refreshToken,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function revokeToken(token: string): Promise<void> {\n await fetch(REVOKE_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }),\n }).catch(() => {\n // best-effort — don't fail logout if revocation fails\n });\n}\n\nexport async function fetchUserInfo(accessToken: string): Promise<UserInfo> {\n const res = await fetch(USERINFO_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch user info (${res.status})`);\n }\n return res.json() as Promise<UserInfo>;\n}\n\n/** Open a URL in the system browser. Fails silently — callers always print the URL too. */\nexport function openBrowser(url: string): void {\n const os = platform();\n let cmd: string;\n if (os === \"darwin\") cmd = `open \"${url}\"`;\n else if (os === \"win32\") cmd = `start \"\" \"${url}\"`;\n else cmd = `xdg-open \"${url}\"`;\n\n // Detach + unref so the child doesn't keep the event loop alive after login.\n const child = exec(cmd);\n child.on(\"error\", () => {\n // Silently ignore — caller has already printed the URL\n });\n child.unref();\n}\n","import { chmod, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { authFile } from \"../config/paths.js\";\nimport { refreshAccessToken } from \"./oauth.js\";\n\nexport type AuthState = {\n accessToken: string;\n refreshToken?: string;\n /** Unix epoch milliseconds when the access token expires. */\n expiresAt: number;\n};\n\n/** How many ms before expiry we proactively refresh. */\nconst REFRESH_BUFFER_MS = 60_000;\n\nexport async function loadAuth(): Promise<AuthState | undefined> {\n try {\n const raw = await readFile(authFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (typeof parsed.accessToken === \"string\" && parsed.accessToken.length > 0) {\n return parsed as AuthState;\n }\n return undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveAuth(state: AuthState): Promise<void> {\n const path = authFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(state, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows doesn't support chmod — ignore\n }\n}\n\nexport async function clearAuth(): Promise<void> {\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(authFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Returns a valid access token, refreshing silently if needed.\n * Returns `undefined` if not logged in or refresh fails (caller should prompt login).\n */\nexport async function getValidToken(): Promise<string | undefined> {\n const auth = await loadAuth();\n if (!auth) return undefined;\n\n const needsRefresh = Date.now() >= auth.expiresAt - REFRESH_BUFFER_MS;\n if (!needsRefresh) return auth.accessToken;\n\n if (!auth.refreshToken) {\n // Access token expired, no refresh token — clear and force re-login\n await clearAuth();\n return undefined;\n }\n\n try {\n const tokens = await refreshAccessToken(auth.refreshToken);\n const refreshed: AuthState = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? auth.refreshToken,\n expiresAt: Date.now() + tokens.expires_in * 1000,\n };\n await saveAuth(refreshed);\n return refreshed.accessToken;\n } catch {\n // Refresh failed (revoked, expired beyond 30 days) — clear and force re-login\n await clearAuth();\n return undefined;\n }\n}\n","import { Command } from \"commander\";\nimport { runCursorWrapper } from \"./commands/cursor.js\";\nimport { runDisable } from \"./commands/disable.js\";\nimport { runDoctor } from \"./commands/doctor.js\";\nimport { runHook } from \"./commands/hook.js\";\nimport { runInit } from \"./commands/init.js\";\nimport { runLogin, runLogout } from \"./commands/login.js\";\nimport { runStatus } from \"./commands/status.js\";\nimport { log } from \"./util/log.js\";\n\ndeclare const __VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name(\"agent-insights\")\n .description(\n \"Internal CLI for AI coding agent observability (Claude Code, Cursor).\",\n )\n .version(__VERSION__);\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with Vercel (Sign in with Vercel).\")\n .action(async () => {\n await runLogin();\n });\n\nprogram\n .command(\"logout\")\n .description(\"Clear stored credentials.\")\n .action(async () => {\n await runLogout();\n });\n\nprogram\n .command(\"init\")\n .description(\n \"Install agent hooks globally. Interactive by default; use --yes for non-interactive.\",\n )\n .option(\n \"-a, --agents <list>\",\n \"Comma-separated agent ids (claude-code,cursor); use '*' for all\",\n )\n .option(\"--transcripts\", \"Opt into transcript sync to Vercel Blob\")\n .option(\"--no-transcripts\", \"Skip transcript sync\")\n .option(\"--analysis\", \"Opt into SessionEnd analysis (Phase 2)\")\n .option(\"--telemetry\", \"Opt into OTLP event telemetry (Phase 3)\")\n .option(\"--no-hooks\", \"Skip installing per-agent hooks\")\n .option(\"--force\", \"Overwrite existing config\")\n .option(\"-y, --yes\", \"Non-interactive — accept defaults / passed flags\")\n .option(\n \"--bypass-secret <secret>\",\n \"Vercel deployment-protection bypass secret (use '' to clear)\",\n )\n .action(async (opts) => {\n await runInit(opts);\n });\n\nprogram\n .command(\"status\")\n .description(\"Show current config, consent, and adapter state.\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts) => {\n await runStatus(opts);\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Verify config, Vercel link, Blob, and per-agent hook install.\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts) => {\n await runDoctor(opts);\n });\n\nprogram\n .command(\"disable\")\n .description(\"Disable agent-insights and/or remove installed hooks.\")\n .option(\"--agent <name>\", \"Disable a specific adapter (claude-code|cursor)\")\n .option(\"--purge\", \"Also delete the local config and caches\")\n .action(async (opts) => {\n await runDisable(opts);\n });\n\nprogram\n .command(\"hook <eventName>\")\n .description(\"Internal: invoked by an agent's hook configuration.\")\n .option(\"--agent <name>\", \"Override agent inference (claude-code|cursor)\")\n .action(async (eventName: string, opts: { agent?: string }) => {\n await runHook(eventName, opts);\n });\n\nprogram\n .command(\"cursor [path]\")\n .description(\"Launch Cursor with session start/end emitted (fallback).\")\n .action(async (path: string | undefined) => {\n await runCursorWrapper({ ...(path !== undefined ? { path } : {}) });\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n log.fail((err as Error).message ?? String(err));\n process.exit(1);\n});\n","import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { loadConfig } from \"../config/config.js\";\nimport { sanitize } from \"../privacy/sanitize.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport { repoHash, userHash, workspaceHash } from \"../util/identity.js\";\nimport { debug, log } from \"../util/log.js\";\n\nexport type CursorOptions = {\n path?: string;\n};\n\n/**\n * Wrapper for launching Cursor when native hooks aren't installed yet.\n * Emits session.start / session.end without prompts or tool inputs.\n */\nexport async function runCursorWrapper(opts: CursorOptions): Promise<void> {\n const cfg = await loadConfig();\n if (!cfg) {\n log.fail(\"agent-insights is not initialized — run `agent-insights init`\");\n process.exitCode = 1;\n return;\n }\n const path = opts.path ?? process.cwd();\n const sessionId = randomUUID();\n\n const baseEvent = {\n agent: \"cursor\" as const,\n sessionId,\n userHash: userHash(),\n repoHash: repoHash(),\n workspaceHash: workspaceHash(),\n };\n\n emit({\n ...baseEvent,\n type: \"session.start\",\n timestamp: new Date().toISOString(),\n });\n\n const code = await spawnCursor(path);\n\n emit({\n ...baseEvent,\n type: \"session.end\",\n timestamp: new Date().toISOString(),\n outcome: code === 0 ? \"success\" : \"error\",\n });\n\n process.exitCode = code;\n}\n\nfunction spawnCursor(path: string): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(\"cursor\", [path], {\n stdio: \"inherit\",\n env: process.env,\n });\n child.on(\"error\", (err) => {\n log.fail(`failed to launch cursor: ${err.message}`);\n resolve(127);\n });\n child.on(\"exit\", (code) => resolve(code ?? 0));\n });\n}\n\nfunction emit(event: AgentEvent): void {\n debug(\"cursor wrapper event\", {\n type: event.type,\n sessionId: event.sessionId,\n });\n // Phase 3 (OTLP) will export sanitized events here.\n void sanitize(event);\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { getAnalyzerUrl } from \"./analyzer.js\";\nimport { configFile, logsDir, sessionCacheDir } from \"./paths.js\";\n\nexport type ExporterConfig = {\n type: \"otlp\";\n endpoint: string;\n headers: Record<string, string>;\n dataset: string;\n};\n\nexport type HooksScope = \"global\" | \"local\";\n\nexport type TranscriptStoreConfig =\n | {\n /** Upload via analyzer API using AGENT_INSIGHTS_API_KEY (preferred). */\n type: \"analyzer\";\n }\n | {\n /** Direct upload with a Blob RW token (legacy / advanced). */\n type: \"blob\";\n tokenEnv: string;\n prefix: string;\n }\n | {\n type: \"none\";\n };\n\nexport type SessionAnalysisConfig = {\n /** Server-side enablement; user opts in via `userConsent.sessionAnalysis`. */\n enabled: boolean;\n /** Analyzer endpoint base URL. CLI POSTs to `${analyzerUrl}/api/sessions`. */\n analyzerUrl: string;\n /** GitHub repo for pre-filled new-issue URLs (display only; constructed by analyzer). */\n githubIssueRepo: string;\n};\n\nexport type AgentConfig = {\n enabled: boolean;\n};\n\nexport type AgentInsightsConfig = {\n version: 1;\n enabled: boolean;\n /** Where agent hook entries are written. Default `global` (user home). */\n hooksScope: HooksScope;\n vercel: {\n team: string;\n project: string;\n };\n userConsent: {\n eventTelemetry: boolean;\n transcriptSync: boolean;\n sessionAnalysis: boolean;\n };\n exporter: ExporterConfig;\n transcriptStore: TranscriptStoreConfig;\n sessionAnalysis: SessionAnalysisConfig;\n agents: Record<\"claudeCode\" | \"cursor\", AgentConfig>;\n privacy: {\n capturePrompts: false;\n captureMessages: false;\n captureToolInputs: false;\n captureFileContents: false;\n };\n};\n\nexport function defaultConfig(): AgentInsightsConfig {\n return {\n version: 1,\n enabled: true,\n hooksScope: \"global\",\n vercel: {\n team: \"vercel-labs\",\n project: \"agent-insights\",\n },\n userConsent: {\n eventTelemetry: false,\n transcriptSync: false,\n sessionAnalysis: false,\n },\n exporter: {\n type: \"otlp\",\n endpoint: process.env.AGENT_INSIGHTS_OTEL_ENDPOINT ?? \"\",\n headers: {},\n dataset: \"agent-insights\",\n },\n transcriptStore: {\n // Upload via the analyzer using the SiwV bearer token — no client-side\n // Blob token required. Set `transcriptStore.type` to \"blob\" in\n // config.json to switch to direct uploads with BLOB_READ_WRITE_TOKEN.\n type: \"analyzer\",\n },\n sessionAnalysis: {\n enabled: false,\n analyzerUrl: getAnalyzerUrl(),\n githubIssueRepo: \"vercel-labs/agent-insights\",\n },\n agents: {\n claudeCode: { enabled: false },\n cursor: { enabled: false },\n },\n privacy: {\n capturePrompts: false,\n captureMessages: false,\n captureToolInputs: false,\n captureFileContents: false,\n },\n };\n}\n\nexport async function loadConfig(): Promise<AgentInsightsConfig | undefined> {\n try {\n const raw = await readFile(configFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AgentInsightsConfig>;\n return mergeConfig(defaultConfig(), parsed);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveConfig(cfg: AgentInsightsConfig): Promise<void> {\n await ensureDirs();\n await writeFile(configFile(), `${JSON.stringify(cfg, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function ensureDirs(): Promise<void> {\n await mkdir(dirname(configFile()), { recursive: true });\n await mkdir(sessionCacheDir(), { recursive: true });\n await mkdir(logsDir(), { recursive: true });\n}\n\nfunction mergeConfig(\n base: AgentInsightsConfig,\n patch: Partial<AgentInsightsConfig>,\n): AgentInsightsConfig {\n return {\n ...base,\n ...patch,\n vercel: { ...base.vercel, ...(patch.vercel ?? {}) },\n userConsent: { ...base.userConsent, ...(patch.userConsent ?? {}) },\n exporter: { ...base.exporter, ...(patch.exporter ?? {}) },\n transcriptStore: (patch.transcriptStore ??\n base.transcriptStore) as TranscriptStoreConfig,\n sessionAnalysis: {\n ...base.sessionAnalysis,\n ...(patch.sessionAnalysis ?? {}),\n },\n agents: {\n claudeCode: {\n ...base.agents.claudeCode,\n ...(patch.agents?.claudeCode ?? {}),\n },\n cursor: { ...base.agents.cursor, ...(patch.agents?.cursor ?? {}) },\n },\n privacy: base.privacy,\n };\n}\n","/**\n * Production analyzer deployment. Override via AGENT_INSIGHTS_ANALYZER_URL\n * for local/dev/staging analyzers.\n */\nexport const DEFAULT_ANALYZER_URL = \"https://agent-insights.labs.vercel.dev\";\n\n/** Resolve the analyzer base URL: env var if set, otherwise the prod default. */\nexport function getAnalyzerUrl(): string {\n return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;\n}\n","/**\n * Keys that must never appear in any payload that leaves the machine.\n *\n * Matching is case-insensitive and snake_case/camelCase agnostic — we strip a\n * key if its normalized form equals one of these values.\n */\nconst FORBIDDEN_KEYS: ReadonlySet<string> = new Set([\n \"prompt\",\n \"prompts\",\n \"message\",\n \"messages\",\n \"content\",\n \"text\",\n \"transcript\",\n \"toolinput\",\n \"input\",\n \"arguments\",\n \"args\",\n \"params\",\n \"file\",\n \"filecontent\",\n \"filecontents\",\n \"diff\",\n \"patch\",\n \"secret\",\n \"secrets\",\n \"token\",\n \"apikey\",\n \"authorization\",\n \"password\",\n \"credentials\",\n]);\n\nconst MAX_STRING_LENGTH = 256;\n\n/** Allowed primitive value types in sanitized metadata. */\nexport type SanitizedPrimitive = string | number | boolean | null;\n\nfunction normalizeKey(key: string): string {\n return key.toLowerCase().replace(/[_\\-\\s]/g, \"\");\n}\n\nfunction truncate(value: string): string {\n if (value.length <= MAX_STRING_LENGTH) return value;\n return `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]`;\n}\n\nfunction sanitizePrimitive(value: unknown): SanitizedPrimitive | undefined {\n if (value === null) return null;\n switch (typeof value) {\n case \"string\":\n return truncate(value);\n case \"number\":\n return Number.isFinite(value) ? value : null;\n case \"boolean\":\n return value;\n default:\n return undefined;\n }\n}\n\n/**\n * Recursively remove forbidden keys from an arbitrary payload.\n *\n * Returned shape:\n * - Objects keep allowed primitive fields (truncated to {@link MAX_STRING_LENGTH}).\n * - Arrays keep allowed primitive elements; nested objects/arrays are recursed.\n * - Non-primitive, non-container values (functions, symbols, Buffers, …) are dropped.\n */\nexport function sanitize(input: unknown): unknown {\n return sanitizeInner(input);\n}\n\nfunction sanitizeInner(value: unknown): unknown {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const item of value) {\n const sanitizedItem = sanitizeInner(item);\n if (sanitizedItem !== undefined) {\n out.push(sanitizedItem);\n }\n }\n return out;\n }\n\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [rawKey, rawValue] of Object.entries(value)) {\n if (FORBIDDEN_KEYS.has(normalizeKey(rawKey))) {\n continue;\n }\n if (rawValue && typeof rawValue === \"object\") {\n out[rawKey] = sanitizeInner(rawValue);\n } else {\n const primitive = sanitizePrimitive(rawValue);\n if (primitive !== undefined) {\n out[rawKey] = primitive;\n }\n }\n }\n return out;\n }\n\n return sanitizePrimitive(value);\n}\n\nexport const __testing = { FORBIDDEN_KEYS, normalizeKey };\n","import { execSync } from \"node:child_process\";\nimport { hostname, userInfo } from \"node:os\";\nimport { sha256 } from \"../privacy/hash.js\";\n\n/** Stable hashed identity of the local user. */\nexport function userHash(): string {\n try {\n const user = userInfo().username;\n return sha256(`${user}@${hostname()}`);\n } catch {\n return sha256(\"unknown\");\n }\n}\n\n/**\n * Stable hashed identity of a repository, derived from `git remote get-url\n * origin` when available, else from `cwd`.\n */\nexport function repoHash(cwd: string = process.cwd()): string {\n const remote = tryGit([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n return sha256(remote ?? cwd);\n}\n\n/** Stable hashed identity of the workspace path. */\nexport function workspaceHash(cwd: string = process.cwd()): string {\n return sha256(cwd);\n}\n\nfunction tryGit(args: string[], cwd: string): string | undefined {\n try {\n const out = execSync(`git ${args.join(\" \")}`, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n timeout: 1500,\n });\n const trimmed = out.trim();\n return trimmed === \"\" ? undefined : trimmed;\n } catch {\n return undefined;\n }\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable SHA-256 hash of a value, hex-encoded.\n *\n * Used to anonymize usernames, repo remote URLs, workspace paths, and\n * session identifiers before they leave the machine.\n */\nexport function sha256(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Short prefix of `sha256(value)` for human-readable dashboards. */\nexport function shortHash(value: string, length = 12): string {\n return sha256(value).slice(0, length);\n}\n","import kleur from \"kleur\";\n\nexport const log = {\n info: (msg: string): void => {\n process.stdout.write(`${msg}\\n`);\n },\n ok: (msg: string): void => {\n process.stdout.write(`${kleur.green(\"✓\")} ${msg}\\n`);\n },\n warn: (msg: string): void => {\n process.stdout.write(`${kleur.yellow(\"!\")} ${msg}\\n`);\n },\n fail: (msg: string): void => {\n process.stderr.write(`${kleur.red(\"✗\")} ${msg}\\n`);\n },\n hint: (msg: string): void => {\n process.stdout.write(`${kleur.dim(msg)}\\n`);\n },\n json: (obj: unknown): void => {\n process.stdout.write(`${JSON.stringify(obj, null, 2)}\\n`);\n },\n};\n\nexport function debug(msg: string, extra?: Record<string, unknown>): void {\n if (process.env.AGENT_INSIGHTS_DEBUG) {\n const payload = extra ? ` ${JSON.stringify(extra)}` : \"\";\n process.stderr.write(`[agent-insights] ${msg}${payload}\\n`);\n }\n}\n","import { rm } from \"node:fs/promises\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig, saveConfig } from \"../config/config.js\";\nimport { configRoot } from \"../config/paths.js\";\nimport { log } from \"../util/log.js\";\nimport type { AgentId } from \"../adapters/types.js\";\n\nexport type DisableOptions = {\n agent?: string;\n purge?: boolean;\n};\n\nexport async function runDisable(opts: DisableOptions): Promise<void> {\n const repoRoot = process.cwd();\n const targets = opts.agent ? [opts.agent] : adapterList.map((a) => a.id);\n\n for (const id of targets) {\n if (id !== \"claude-code\" && id !== \"cursor\") {\n log.warn(`Unknown agent id \"${id}\"`);\n continue;\n }\n const adapter = adapterList.find((a) => a.id === (id as AgentId));\n if (!adapter) continue;\n const { removed, path } = await adapter.uninstall(repoRoot);\n if (removed) log.ok(`Removed ${adapter.label} hooks from ${path}`);\n else log.hint(`${adapter.label}: no hooks to remove (${path})`);\n }\n\n const cfg = await loadConfig();\n if (cfg) {\n if (!opts.agent || opts.agent === \"claude-code\") {\n cfg.agents.claudeCode.enabled = false;\n }\n if (!opts.agent || opts.agent === \"cursor\") {\n cfg.agents.cursor.enabled = false;\n }\n if (!opts.agent) cfg.enabled = false;\n await saveConfig(cfg);\n }\n\n if (opts.purge) {\n await rm(configRoot(), { recursive: true, force: true });\n log.ok(`Purged ${configRoot()}`);\n }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\nconst HOOK_MAP: Record<string, AgentEventType> = {\n SessionStart: \"session.start\",\n SessionEnd: \"session.end\",\n UserPromptSubmit: \"user.prompt.submit\",\n PreToolUse: \"tool.start\",\n PostToolUse: \"tool.end\",\n PostToolUseFailure: \"tool.failure\",\n PermissionRequest: \"permission.request\",\n PermissionDenied: \"permission.denied\",\n SubagentStart: \"subagent.start\",\n SubagentStop: \"subagent.end\",\n Stop: \"agent.stop\",\n Notification: \"notification\",\n PreCompact: \"context.compact.start\",\n PostCompact: \"context.compact.end\",\n};\n\nexport const CLAUDE_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype ClaudeHookEntry = {\n type: \"command\";\n command: string;\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, ClaudeHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const claudeCodeAdapter: Adapter = {\n id: \"claude-code\",\n agent: \"claude-code\",\n label: \"Claude Code\",\n settingsFile: \".claude/settings.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n const sessionId =\n asString(data[\"session_id\"]) ?? asString(data[\"sessionId\"]);\n const toolName =\n asString(data[\"tool_name\"]) ??\n asString(data[\"toolName\"]) ??\n asString((data[\"tool\"] as Record<string, unknown> | undefined)?.[\"name\"]);\n const transcriptPath =\n asString(data[\"transcript_path\"]) ?? asString(data[\"transcriptPath\"]);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n const next: ClaudeSettings = { ...(settings ?? {}) };\n const hooks: Record<string, ClaudeHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CLAUDE_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({\n type: \"command\",\n command: `${HOOK_COMMAND_NAME} hook ${event}`,\n });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<ClaudeSettings>(\n resolveClaudePath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveClaudePath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(repoRoot, \".claude\", \"settings.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\n/**\n * Cursor native hook names (camelCase) → common schema. Reference:\n * https://cursor.com/docs/hooks\n */\nconst HOOK_MAP: Record<string, AgentEventType> = {\n sessionStart: \"session.start\",\n sessionEnd: \"session.end\",\n beforeSubmitPrompt: \"user.prompt.submit\",\n preToolUse: \"tool.start\",\n postToolUse: \"tool.end\",\n postToolUseFailure: \"tool.failure\",\n subagentStart: \"subagent.start\",\n subagentStop: \"subagent.end\",\n stop: \"agent.stop\",\n preCompact: \"context.compact.start\",\n // Additional events — mapped to existing schema types\n beforeShellExecution: \"tool.start\",\n afterShellExecution: \"tool.end\",\n afterFileEdit: \"tool.end\",\n workspaceOpen: \"session.start\",\n};\n\nexport const CURSOR_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype CursorHookEntry = {\n command: string;\n};\n\ntype CursorHooksConfig = {\n /** Cursor expects schema version 1 at the top level. */\n version?: number;\n hooks?: Record<string, CursorHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const cursorAdapter: Adapter = {\n id: \"cursor\",\n agent: \"cursor\",\n label: \"Cursor\",\n settingsFile: \".cursor/hooks.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n // Cursor uses `session_id` in payloads and is identical to `conversation_id`.\n const sessionId =\n asString(data[\"session_id\"]) ??\n asString(data[\"sessionId\"]) ??\n asString(data[\"conversation_id\"]);\n const toolName = asString(data[\"tool_name\"]) ?? asString(data[\"toolName\"]);\n\n // transcript_path is confirmed in all Cursor hook payloads.\n const transcriptPath =\n asString(data[\"transcript_path\"]) ??\n asString(data[\"transcriptPath\"]) ??\n asString(process.env.CURSOR_TRANSCRIPT_PATH);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n const next: CursorHooksConfig = {\n version: 1,\n ...(settings ?? {}),\n };\n const hooks: Record<string, CursorHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CURSOR_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({ command: `${HOOK_COMMAND_NAME} hook ${event}` });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<CursorHooksConfig>(\n resolveCursorPath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveCursorPath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".cursor\", \"hooks.json\")\n : join(repoRoot, \".cursor\", \"hooks.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { claudeCodeAdapter } from \"./claude-code.js\";\nimport { cursorAdapter } from \"./cursor.js\";\nimport type { Adapter, AgentId } from \"./types.js\";\n\nexport const adapters: Record<AgentId, Adapter> = {\n \"claude-code\": claudeCodeAdapter,\n cursor: cursorAdapter,\n};\n\nexport const adapterList: ReadonlyArray<Adapter> = Object.values(adapters);\n\nexport function getAdapter(id: AgentId): Adapter {\n return adapters[id];\n}\n","import kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { createTranscriptStore } from \"../transcript/store.js\";\nimport { log } from \"../util/log.js\";\n\nexport type DoctorOptions = {\n json?: boolean;\n};\n\ntype Check = {\n name: string;\n ok: boolean;\n detail?: string;\n};\n\nexport async function runDoctor(opts: DoctorOptions): Promise<void> {\n const repoRoot = process.cwd();\n const checks: Check[] = [];\n\n const token = await getValidToken();\n checks.push({\n name: \"logged in\",\n ok: token != null,\n ...(token ? {} : { detail: \"run `agent-insights login`\" }),\n });\n\n const cfg = await loadConfig();\n checks.push({\n name: \"config found\",\n ok: cfg != null,\n detail: cfg ? configFile() : `missing ${configFile()}`,\n });\n\n if (cfg) {\n if (cfg.userConsent.transcriptSync) {\n try {\n const store = createTranscriptStore(cfg.transcriptStore);\n const ping = await store.ping();\n if (ping.ok) {\n checks.push({ name: \"transcript store reachable\", ok: true });\n } else {\n checks.push({\n name: \"transcript store reachable\",\n ok: false,\n detail: ping.reason,\n });\n }\n } catch (err) {\n checks.push({\n name: \"transcript store reachable\",\n ok: false,\n detail: (err as Error).message,\n });\n }\n }\n\n for (const adapter of adapterList) {\n const enabled =\n adapter.id === \"claude-code\"\n ? cfg.agents.claudeCode.enabled\n : cfg.agents.cursor.enabled;\n if (!enabled) continue;\n const installed = await adapter.isInstalled(repoRoot, cfg.hooksScope);\n checks.push({\n name: `${adapter.label} hooks installed`,\n ok: installed,\n ...(installed ? {} : { detail: \"run `agent-insights init`\" }),\n });\n }\n }\n\n if (opts.json) {\n log.json({ ok: checks.every((c) => c.ok), checks });\n } else {\n for (const c of checks) {\n const tag = c.ok ? kleur.green(\"✓\") : kleur.red(\"✗\");\n const detail = c.detail ? kleur.dim(` (${c.detail})`) : \"\";\n log.info(`${tag} ${c.name}${detail}`);\n }\n }\n\n process.exitCode = checks.every((c) => c.ok) ? 0 : 1;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { put } from \"@vercel/blob\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport type { TranscriptStoreConfig } from \"../config/config.js\";\n\nexport type TranscriptUploadInput = {\n /** Absolute path on disk to the transcript file. */\n transcriptPath: string;\n /** SHA256-hashed user identifier. Used in the upload pathname. */\n userHash: string;\n /** Session identifier supplied by the agent. */\n sessionId: string;\n /** Agent name (e.g. \"claude-code\"). */\n agent: string;\n};\n\nexport type TranscriptUploadResult = {\n pathname: string;\n url: string;\n size: number;\n};\n\nexport interface TranscriptStore {\n upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult>;\n /** Lightweight health check used by `doctor`. */\n ping(): Promise<{ ok: true } | { ok: false; reason: string }>;\n}\n\nclass NoopTranscriptStore implements TranscriptStore {\n async upload(): Promise<TranscriptUploadResult> {\n throw new Error(\"transcript store disabled (transcriptStore.type=none)\");\n }\n async ping(): Promise<{ ok: false; reason: string }> {\n return { ok: false, reason: \"transcript store disabled\" };\n }\n}\n\nclass BlobTranscriptStore implements TranscriptStore {\n constructor(\n private readonly token: string,\n private readonly prefix: string,\n ) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const body = await readFile(input.transcriptPath);\n const requestedPath = buildPathname(\n this.prefix,\n input.userHash,\n input.sessionId,\n );\n const blob = await put(requestedPath, body, {\n access: \"private\",\n token: this.token,\n contentType: \"application/jsonl\",\n addRandomSuffix: true,\n });\n return { pathname: blob.pathname, url: blob.url, size: body.byteLength };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n if (!this.token) return { ok: false, reason: \"missing token\" };\n return { ok: true };\n }\n}\n\n/**\n * Uploads transcripts via the analyzer's POST /api/sessions/upload endpoint\n * using the Sign-in-with-Vercel bearer token. The analyzer then proxies the\n * write to Vercel Blob with its own server-side credentials, so this client\n * does not need a Blob read/write token.\n */\nclass AnalyzerTranscriptStore implements TranscriptStore {\n constructor(private readonly analyzerUrl: string) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const token = await getValidToken();\n if (!token) {\n throw new Error(\n \"transcript store: not logged in — run `agent-insights login`\",\n );\n }\n const body = await readFile(input.transcriptPath);\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/jsonl\",\n \"x-session-id\": input.sessionId,\n \"x-agent\": input.agent,\n \"x-user-hash\": input.userHash,\n });\n const res = await fetch(url, { method: \"POST\", headers, body });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `transcript upload failed (${res.status}): ${text || res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n ok: boolean;\n blobUrl?: string;\n pathname?: string;\n size?: number;\n error?: string;\n };\n if (!json.ok || !json.blobUrl || !json.pathname) {\n throw new Error(`transcript upload error: ${json.error ?? \"unknown\"}`);\n }\n return {\n pathname: json.pathname,\n url: json.blobUrl,\n size: json.size ?? body.byteLength,\n };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n const token = await getValidToken();\n if (!token) {\n return {\n ok: false,\n reason: \"not logged in — run `agent-insights login`\",\n };\n }\n // Try the actual upload endpoint with an intentionally bad request — a\n // healthy analyzer responds 400 (missing headers); a protection wall\n // responds 401/403 with HTML; an unreachable host throws.\n try {\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n });\n const res = await fetch(url, { method: \"POST\", headers });\n if (res.status === 400) return { ok: true };\n if (res.status === 401 || res.status === 403) {\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n return {\n ok: false,\n reason:\n \"blocked by Vercel deployment protection — set a bypass secret via `agent-insights init --bypass-secret <secret>`\",\n };\n }\n return { ok: false, reason: `analyzer rejected token (${res.status})` };\n }\n return { ok: false, reason: `unexpected status ${res.status}` };\n } catch (err) {\n return {\n ok: false,\n reason: `analyzer unreachable: ${(err as Error).message}`,\n };\n }\n }\n}\n\nexport function createTranscriptStore(\n cfg: TranscriptStoreConfig,\n): TranscriptStore {\n if (cfg.type === \"none\") return new NoopTranscriptStore();\n if (cfg.type === \"analyzer\") {\n return new AnalyzerTranscriptStore(getAnalyzerUrl());\n }\n // type === \"blob\"\n const token = process.env[cfg.tokenEnv];\n if (!token) {\n throw new Error(\n `transcript store: env ${cfg.tokenEnv} is not set — run \\`agent-insights init --force\\` to switch to analyzer-proxied uploads (no token required), or \\`vercel env pull .env.local\\` to keep direct Blob uploads`,\n );\n }\n return new BlobTranscriptStore(token, cfg.prefix);\n}\n\nfunction buildPathname(\n prefix: string,\n userHash: string,\n sessionId: string,\n): string {\n const safePrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n const safeSession = encodeURIComponent(sessionId);\n return `${safePrefix}${userHash}/${safeSession}/transcript.jsonl`;\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { bypassSecretFile } from \"./paths.js\";\n\n/**\n * Resolve the Vercel deployment-protection bypass secret.\n * Precedence:\n * 1. `AGENT_INSIGHTS_BYPASS_SECRET` (explicit override)\n * 2. `VERCEL_AUTOMATION_BYPASS_SECRET` (Vercel's standard env)\n * 3. `~/.agent-insights/bypass-secret` (set via `agent-insights init`)\n */\nexport async function loadBypassSecret(): Promise<string | undefined> {\n const fromEnv =\n process.env.AGENT_INSIGHTS_BYPASS_SECRET ||\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET;\n if (fromEnv) return fromEnv;\n\n try {\n const raw = (await readFile(bypassSecretFile(), \"utf8\")).trim();\n return raw || undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveBypassSecret(secret: string): Promise<void> {\n const path = bypassSecretFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, secret.trim(), { encoding: \"utf8\", mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows — ignore.\n }\n}\n\nexport async function clearBypassSecret(): Promise<void> {\n try {\n await unlink(bypassSecretFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Add the `x-vercel-protection-bypass` header onto an existing headers\n * object if a secret is configured. Mutates and returns the input.\n */\nexport async function withBypassHeader<T extends Record<string, string>>(\n headers: T,\n): Promise<T> {\n const secret = await loadBypassSecret();\n if (secret) {\n (headers as Record<string, string>)[\"x-vercel-protection-bypass\"] = secret;\n }\n return headers;\n}\n","import { adapterList } from \"../adapters/registry.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { sanitize } from \"../privacy/sanitize.js\";\nimport { createTranscriptStore } from \"../transcript/store.js\";\nimport type { Adapter, AgentId, HookPayload } from \"../adapters/types.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport { repoHash, userHash, workspaceHash } from \"../util/identity.js\";\nimport { debug, log } from \"../util/log.js\";\n\nexport type HookOptions = {\n agent?: string;\n};\n\n/**\n * Hook entrypoint. Reads JSON payload from stdin, resolves the adapter, maps\n * the native event into the common schema, and runs transcript upload on\n * session.end. Never fails the hook — errors are logged, exit code is 0.\n */\nexport async function runHook(\n eventName: string,\n opts: HookOptions,\n): Promise<void> {\n const cfg = await loadConfig();\n if (!cfg || !cfg.enabled) {\n debug(\"hook skipped — agent-insights not enabled\");\n return;\n }\n\n const stdin = await readStdinSafely();\n const data = parseJsonObject(stdin);\n const payload: HookPayload = { event: eventName, data };\n\n const adapter = pickAdapter(opts.agent, eventName);\n if (!adapter) {\n debug(`no adapter resolved for event ${eventName}`);\n return;\n }\n\n const mapped = adapter.map(payload);\n if (!mapped) {\n debug(`adapter ${adapter.id} did not map event ${eventName}`);\n return;\n }\n\n const event = buildEvent(adapter, mapped);\n debug(\"hook event\", {\n agent: event.agent,\n type: event.type,\n sessionId: event.sessionId,\n });\n\n if (\n mapped.type === \"session.end\" &&\n cfg.userConsent.transcriptSync &&\n mapped.transcriptPath\n ) {\n try {\n const store = createTranscriptStore(cfg.transcriptStore);\n const result = await store.upload({\n transcriptPath: mapped.transcriptPath,\n userHash: event.userHash ?? userHash(),\n sessionId: mapped.sessionId ?? \"unknown\",\n agent: adapter.id,\n });\n debug(\"transcript uploaded\", {\n size: result.size,\n pathname: result.pathname,\n });\n\n if (cfg.userConsent.sessionAnalysis) {\n await notifyAnalyzer({\n analyzerUrl: getAnalyzerUrl(),\n payload: {\n sessionId: mapped.sessionId ?? \"unknown\",\n agent: adapter.id,\n userHash: event.userHash ?? userHash(),\n blobUrl: result.url,\n timestamp: event.timestamp,\n },\n });\n }\n } catch (err) {\n log.fail(`transcript upload failed: ${(err as Error).message}`);\n }\n }\n\n // Phase 3 (OTLP) will export `event` here.\n void sanitize(event);\n}\n\ntype AnalyzerPayload = {\n sessionId: string;\n agent: string;\n userHash: string;\n blobUrl: string;\n timestamp: string;\n};\n\nasync function notifyAnalyzer(opts: {\n analyzerUrl: string;\n payload: AnalyzerPayload;\n}): Promise<void> {\n try {\n const { getValidToken } = await import(\"../auth/store.js\");\n const token = await getValidToken();\n const baseHeaders: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (token) baseHeaders[\"authorization\"] = `Bearer ${token}`;\n const headers = await withBypassHeader(baseHeaders);\n\n const url = new URL(\"/api/sessions\", opts.analyzerUrl).toString();\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(opts.payload),\n });\n if (!res.ok) {\n debug(`analyzer notify failed: ${res.status} ${res.statusText}`);\n return;\n }\n debug(\"analyzer notified\", { sessionId: opts.payload.sessionId });\n } catch (err) {\n // Never fail the hook because of analyzer issues.\n debug(`analyzer notify error: ${(err as Error).message}`);\n }\n}\n\nfunction pickAdapter(\n override: string | undefined,\n eventName: string,\n): Adapter | undefined {\n if (override) {\n return adapterList.find((a) => a.id === override);\n }\n for (const a of adapterList) {\n if (a.map({ event: eventName })) return a;\n }\n return undefined;\n}\n\nfunction buildEvent(\n adapter: { id: AgentId; agent: AgentEvent[\"agent\"] },\n mapped: NonNullable<ReturnType<Adapter[\"map\"]>>,\n): AgentEvent {\n return {\n type: mapped.type,\n agent: adapter.agent,\n timestamp: new Date().toISOString(),\n ...(mapped.sessionId !== undefined ? { sessionId: mapped.sessionId } : {}),\n ...(mapped.toolName !== undefined ? { toolName: mapped.toolName } : {}),\n ...(mapped.permissionType !== undefined\n ? { permissionType: mapped.permissionType }\n : {}),\n ...(mapped.outcome !== undefined ? { outcome: mapped.outcome } : {}),\n userHash: userHash(),\n repoHash: repoHash(),\n workspaceHash: workspaceHash(),\n };\n}\n\n/**\n * Read stdin if data is available. Returns an empty string when stdin is a\n * TTY or when nothing arrives within `STDIN_TIMEOUT_MS`. Hooks invoked\n * without piped JSON should never hang.\n */\nconst STDIN_TIMEOUT_MS = 200;\n\nasync function readStdinSafely(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n return new Promise<string>((resolve) => {\n let buf = \"\";\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n process.stdin.removeAllListeners(\"data\");\n process.stdin.removeAllListeners(\"end\");\n process.stdin.removeAllListeners(\"error\");\n // Release the event loop — `setEncoding` + 'data' listener cause Node\n // to keep reading stdin even after we've stopped listening.\n try {\n process.stdin.pause();\n process.stdin.unref();\n } catch {\n // ignore — stdin may already be destroyed\n }\n resolve(buf);\n };\n const idle = setTimeout(finish, STDIN_TIMEOUT_MS);\n idle.unref();\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n buf += chunk;\n idle.refresh();\n });\n process.stdin.on(\"end\", () => {\n clearTimeout(idle);\n finish();\n });\n process.stdin.on(\"error\", () => {\n clearTimeout(idle);\n finish();\n });\n });\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n if (!raw) return undefined;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n","import { checkbox, confirm, password } from \"@inquirer/prompts\";\nimport kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport type { AgentId } from \"../adapters/types.js\";\nimport {\n clearBypassSecret,\n loadBypassSecret,\n saveBypassSecret,\n} from \"../config/bypass.js\";\nimport {\n defaultConfig,\n loadConfig,\n saveConfig,\n type AgentInsightsConfig,\n} from \"../config/config.js\";\nimport { configFile, configRoot } from \"../config/paths.js\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { log } from \"../util/log.js\";\n\nexport type InitOptions = {\n agents?: string;\n transcripts?: boolean;\n analysis?: boolean;\n telemetry?: boolean;\n force?: boolean;\n hooks?: boolean;\n yes?: boolean;\n bypassSecret?: string;\n};\n\nconst CONSENT_TEXT = `\\\nAgent Insights is opt-in (internal).\n\nOTLP / lifecycle metrics: event names, timestamps, tool names, outcomes,\nhashed user/repo/workspace IDs — never prompts, messages, or tool inputs.\n\nVercel Blob (optional): full session transcripts for internal analysis;\nusername stored as SHA256 only.\n\nSessionEnd analysis (optional): an internal LLM reviews transcript for\ntooling gaps; may post to Slack with a GitHub issue link.\\\n`;\n\ntype Choices = {\n agents: Set<AgentId>;\n transcripts: boolean;\n analysis: boolean;\n telemetry: boolean;\n /** undefined = leave existing untouched; \"\" = clear; non-empty = save. */\n bypassSecret?: string | undefined;\n};\n\nexport async function runInit(opts: InitOptions): Promise<void> {\n const token = await getValidToken();\n if (!token) {\n log.fail(\"Not logged in. Run `agent-insights login` first.\");\n process.exit(1);\n }\n\n log.info(kleur.bold(\"Agent Insights setup\\n\"));\n log.hint(CONSENT_TEXT);\n log.info(\"\");\n\n const existing = await loadConfig();\n const interactive =\n !opts.yes && Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\n if (existing && !opts.force) {\n if (!interactive) {\n log.warn(\n `Config already exists at ${configFile()} — re-run with --force to overwrite, or use interactive mode.`,\n );\n return;\n }\n log.warn(`Config already exists at ${configFile()}.`);\n const overwrite = await safePrompt(() =>\n confirm({ message: \"Overwrite existing config?\", default: true }),\n );\n if (overwrite === undefined) return; // user aborted\n if (!overwrite) {\n log.info(\"Aborted — existing config left in place.\");\n return;\n }\n log.info(\"\");\n }\n\n let choices: Choices;\n if (interactive) {\n const result = await promptChoices(opts);\n if (!result) return; // aborted\n choices = result;\n } else {\n choices = {\n agents: parseAgents(opts.agents),\n transcripts: opts.transcripts ?? true,\n analysis: opts.analysis ?? true,\n telemetry: opts.telemetry ?? true,\n bypassSecret: opts.bypassSecret,\n };\n }\n\n if (choices.agents.size === 0) {\n log.fail(\"No agents selected — nothing to install.\");\n process.exit(1);\n }\n\n // Always start from defaults so re-running `init` (or `init --force`)\n // resets fields like `transcriptStore.type` that the user can't toggle\n // through the prompts. Otherwise an old \"blob\" mode would survive a\n // re-init forever.\n const cfg: AgentInsightsConfig = defaultConfig();\n cfg.userConsent.transcriptSync = choices.transcripts;\n cfg.userConsent.sessionAnalysis = choices.analysis;\n cfg.userConsent.eventTelemetry = choices.telemetry;\n cfg.agents.claudeCode.enabled = choices.agents.has(\"claude-code\");\n cfg.agents.cursor.enabled = choices.agents.has(\"cursor\");\n\n await saveConfig(cfg);\n log.ok(`Wrote config to ${configFile()}`);\n\n if (choices.bypassSecret !== undefined) {\n if (choices.bypassSecret === \"\") {\n await clearBypassSecret();\n log.ok(\"Cleared stored deployment-protection bypass secret.\");\n } else {\n await saveBypassSecret(choices.bypassSecret);\n log.ok(\"Saved deployment-protection bypass secret.\");\n }\n }\n\n const installHooks = opts.hooks ?? true;\n const repoRoot = process.cwd();\n if (installHooks) {\n for (const adapter of adapterList) {\n const enabled =\n (adapter.id === \"claude-code\" && cfg.agents.claudeCode.enabled) ||\n (adapter.id === \"cursor\" && cfg.agents.cursor.enabled);\n if (!enabled) continue;\n const { path } = await adapter.install(repoRoot, cfg.hooksScope);\n log.ok(`Installed ${adapter.label} hooks → ${rel(path)}`);\n }\n }\n\n log.info(\"\");\n log.info(`Config root: ${configRoot()}`);\n log.hint(\"Run `agent-insights doctor` to verify the setup.\");\n}\n\nasync function promptChoices(opts: InitOptions): Promise<Choices | undefined> {\n const requested = opts.agents ? parseAgents(opts.agents) : null;\n const selected = await safePrompt(() =>\n checkbox<AgentId>({\n message: \"Which agents would you like to install hooks for?\",\n choices: adapterList.map((adapter) => ({\n name: adapter.label,\n value: adapter.id,\n checked: requested ? requested.has(adapter.id) : true,\n })),\n required: true,\n }),\n );\n if (selected === undefined) return undefined;\n\n const transcripts = await safePrompt(() =>\n confirm({\n message: \"Sync session transcripts to Vercel Blob?\",\n default: opts.transcripts ?? true,\n }),\n );\n if (transcripts === undefined) return undefined;\n\n const analysis = await safePrompt(() =>\n confirm({\n message: \"Enable SessionEnd analysis (internal LLM review)?\",\n default: opts.analysis ?? true,\n }),\n );\n if (analysis === undefined) return undefined;\n\n const telemetry = await safePrompt(() =>\n confirm({\n message: \"Send OTLP event telemetry?\",\n default: opts.telemetry ?? true,\n }),\n );\n if (telemetry === undefined) return undefined;\n\n let bypassSecret: string | undefined;\n if (opts.bypassSecret !== undefined) {\n // Explicit flag wins; don't prompt.\n bypassSecret = opts.bypassSecret;\n } else {\n const existing = await loadBypassSecret();\n const answer = await safePrompt(() =>\n password({\n message: existing\n ? \"Vercel deployment-protection bypass secret (Enter to keep existing, '-' to clear):\"\n : \"Vercel deployment-protection bypass secret (Enter to skip):\",\n mask: true,\n }),\n );\n if (answer === undefined) return undefined;\n const trimmed = answer.trim();\n if (trimmed === \"\") bypassSecret = undefined; // leave alone\n else if (trimmed === \"-\") bypassSecret = \"\"; // clear\n else bypassSecret = trimmed;\n }\n\n return {\n agents: new Set(selected),\n transcripts,\n analysis,\n telemetry,\n bypassSecret,\n };\n}\n\n/**\n * Run an inquirer prompt and return undefined if the user aborts (Ctrl+C).\n * Re-throws unexpected errors so they surface in the CLI.\n */\nasync function safePrompt<T>(run: () => Promise<T>): Promise<T | undefined> {\n try {\n return await run();\n } catch (err) {\n if (\n err instanceof Error &&\n (err.name === \"ExitPromptError\" || err.name === \"AbortPromptError\")\n ) {\n log.info(\"Aborted.\");\n return undefined;\n }\n throw err;\n }\n}\n\nfunction parseAgents(value: string | undefined): Set<AgentId> {\n const out = new Set<AgentId>();\n const trimmed = (value ?? \"\").trim();\n if (trimmed === \"*\") {\n for (const a of adapterList) out.add(a.id);\n return out;\n }\n const list = (trimmed || \"claude-code,cursor\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n for (const id of list) {\n if (id === \"claude-code\" || id === \"cursor\") out.add(id);\n else log.warn(`Unknown agent id \"${id}\" — skipping.`);\n }\n return out;\n}\n\nfunction rel(p: string): string {\n const cwd = process.cwd();\n return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;\n}\n","import kleur from \"kleur\";\nimport {\n exchangeCodeForToken,\n fetchUserInfo,\n openBrowser,\n REDIRECT_URI,\n startPkceFlow,\n} from \"../auth/oauth.js\";\nimport { clearAuth, saveAuth } from \"../auth/store.js\";\nimport { log } from \"../util/log.js\";\n\nconst ALLOWED_DOMAIN = \"vercel.com\";\n\nexport async function runLogin(): Promise<void> {\n log.info(kleur.bold(\"Signing in with Vercel...\\n\"));\n\n // 1. Build auth URL + start local callback server\n let pkceResult: { code: string; codeVerifier: string };\n try {\n log.hint(`Listening on ${REDIRECT_URI} — opening browser...`);\n pkceResult = await startPkceFlow();\n } catch (err) {\n log.fail(`Login failed: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 2. Exchange code for tokens\n let tokens;\n try {\n tokens = await exchangeCodeForToken(pkceResult.code, pkceResult.codeVerifier);\n } catch (err) {\n log.fail(`Token exchange failed: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 3. Verify @vercel.com email\n let userInfo;\n try {\n userInfo = await fetchUserInfo(tokens.access_token);\n } catch (err) {\n log.fail(`Could not fetch user info: ${(err as Error).message}`);\n process.exit(1);\n }\n\n if (\n !userInfo.email?.endsWith(`@${ALLOWED_DOMAIN}`) ||\n !userInfo.email_verified\n ) {\n log.fail(\n `Only @${ALLOWED_DOMAIN} accounts are allowed. Got: ${userInfo.email ?? \"(no email)\"}`,\n );\n process.exit(1);\n }\n\n // 4. Save tokens\n await saveAuth({\n accessToken: tokens.access_token,\n ...(tokens.refresh_token !== undefined\n ? { refreshToken: tokens.refresh_token }\n : {}),\n expiresAt: Date.now() + tokens.expires_in * 1000,\n });\n\n const name = userInfo.preferred_username ?? userInfo.email;\n log.ok(`Logged in as ${kleur.bold(name)} (${userInfo.email})`);\n log.hint(\"Run `agent-insights init` to install hooks globally.\");\n}\n\nexport async function runLogout(): Promise<void> {\n await clearAuth();\n log.ok(\"Logged out.\");\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { log } from \"../util/log.js\";\n\nexport type StatusOptions = {\n json?: boolean;\n};\n\nexport async function runStatus(opts: StatusOptions): Promise<void> {\n const cfg = await loadConfig();\n const repoRoot = process.cwd();\n\n if (!cfg) {\n if (opts.json) log.json({ initialized: false });\n else log.warn(`No config found at ${configFile()} — run \\`agent-insights init\\`.`);\n return;\n }\n\n const adapterStatus = await Promise.all(\n adapterList.map(async (a) => ({\n id: a.id,\n label: a.label,\n enabled:\n a.id === \"claude-code\"\n ? cfg.agents.claudeCode.enabled\n : cfg.agents.cursor.enabled,\n installed: await a.isInstalled(repoRoot),\n })),\n );\n\n if (opts.json) {\n log.json({\n initialized: true,\n configPath: configFile(),\n enabled: cfg.enabled,\n consent: cfg.userConsent,\n vercel: {\n linked: existsSync(join(repoRoot, \".vercel/project.json\")),\n envFile: existsSync(join(repoRoot, \".env.local\")),\n },\n adapters: adapterStatus,\n });\n return;\n }\n\n log.info(kleur.bold(\"Agent Insights status\\n\"));\n log.info(`config ${configFile()}`);\n log.info(`enabled ${cfg.enabled ? \"yes\" : \"no\"}`);\n log.info(\"\");\n log.info(kleur.bold(\"Consent\"));\n log.info(` event telemetry ${yn(cfg.userConsent.eventTelemetry)}`);\n log.info(` transcript sync ${yn(cfg.userConsent.transcriptSync)}`);\n log.info(` session analysis ${yn(cfg.userConsent.sessionAnalysis)}`);\n log.info(\"\");\n log.info(kleur.bold(\"Adapters\"));\n for (const a of adapterStatus) {\n log.info(\n ` ${a.label.padEnd(12)} ${a.enabled ? \"enabled\" : \"disabled\"} hooks ${\n a.installed ? \"installed\" : \"not installed\"\n }`,\n );\n }\n log.info(\"\");\n log.info(kleur.bold(\"Vercel\"));\n log.info(\n ` linked ${yn(existsSync(join(repoRoot, \".vercel/project.json\")))}`,\n );\n log.info(` .env.local ${yn(existsSync(join(repoRoot, \".env.local\")))}`);\n}\n\nfunction yn(v: boolean): string {\n return v ? kleur.green(\"yes\") : kleur.dim(\"no\");\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AAGd,SAAS,aAAqB;AACnC,SAAO,QAAQ,IAAI,uBAAuB,KAAK,QAAQ,GAAG,iBAAiB;AAC7E;AAEO,SAAS,aAAqB;AACnC,SAAO,KAAK,WAAW,GAAG,aAAa;AACzC;AAQO,SAAS,WAAmB;AACjC,SAAO,KAAK,WAAW,GAAG,WAAW;AACvC;AAGO,SAAS,mBAA2B;AACzC,SAAO,KAAK,WAAW,GAAG,eAAe;AAC3C;AAEO,SAAS,kBAA0B;AACxC,SAAO,KAAK,WAAW,GAAG,eAAe;AAC3C;AAEO,SAAS,UAAkB;AAChC,SAAO,KAAK,WAAW,GAAG,MAAM;AAClC;AAjCA;AAAA;AAAA;AAAA;AAAA;;;ACAA,SAAS,cAAAA,aAAY,mBAAmB;AACxC,SAAS,YAAY;AACrB,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AAyCzB,SAAS,uBAA+B;AACtC,SAAO,YAAY,EAAE,EAAE,SAAS,WAAW;AAC7C;AAEA,SAAS,sBAAsB,UAA0B;AACvD,SAAOA,YAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,WAAW;AACjE;AAEA,SAAS,gBAAwB;AAC/B,SAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACvC;AAeA,eAAsB,cAAc,YAAY,MAG7C;AACD,QAAM,eAAe,qBAAqB;AAC1C,QAAM,gBAAgB,sBAAsB,YAAY;AACxD,QAAM,QAAQ,cAAc;AAE5B,QAAM,UAAU,IAAI,IAAI,aAAa;AACrC,UAAQ,aAAa,IAAI,aAAa,SAAS;AAC/C,UAAQ,aAAa,IAAI,iBAAiB,MAAM;AAChD,UAAQ,aAAa,IAAI,gBAAgB,YAAY;AACrD,UAAQ,aAAa,IAAI,SAAS,MAAM;AACxC,UAAQ,aAAa,IAAI,kBAAkB,aAAa;AACxD,UAAQ,aAAa,IAAI,yBAAyB,MAAM;AACxD,UAAQ,aAAa,IAAI,SAAS,KAAK;AAEvC,QAAM,MAAM,QAAQ,SAAS;AAC7B,QAAM,cAAc,kBAAkB,OAAO,SAAS;AACtD,cAAY,GAAG;AAEf,UAAQ,OAAO,MAAM;AAAA;AAAA,IAA4C,GAAG;AAAA;AAAA,CAAM;AAE1E,QAAM,OAAO,MAAM;AACnB,SAAO,EAAE,MAAM,aAAa;AAC9B;AAEA,SAAS,kBACP,eACA,WACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,YAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,aAAa,EAAE;AACvE,UAAI,IAAI,aAAa,aAAa;AAChC,YAAI,UAAU,KAAK,EAAE,YAAY,QAAQ,CAAC;AAC1C,YAAI,IAAI,WAAW;AACnB;AAAA,MACF;AAEA,YAAM,OAAO,IAAI,aAAa,IAAI,MAAM;AACxC,YAAM,gBAAgB,IAAI,aAAa,IAAI,OAAO;AAClD,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAE1C,YAAM,OAAO,CAAC,QACZ,iFACO,GAAG;AAIZ,YAAM,UAAU,EAAE,gBAAgB,aAAa,YAAY,QAAQ;AAEnE,YAAM,SAAS,CACb,MACA,MACA,WACS;AACT,YAAI,UAAU,QAAQ,OAAO;AAC7B,YAAI,IAAI,MAAM,MAAM;AAClB,uBAAa,KAAK;AAClB,iBAAO,sBAAsB;AAC7B,iBAAO,MAAM;AACb,eAAK;AAAA,QACP,CAAC;AAAA,MACH;AAEA,UAAI,OAAO;AACT;AAAA,UACE,MAAM,OAAO,IAAI,MAAM,gBAAgB,KAAK,EAAE,CAAC;AAAA,UAC/C,KAAK,iBAAiB,KAAK,EAAE;AAAA,UAC7B;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,kBAAkB,eAAe;AACnC;AAAA,UACE,MAAM,OAAO,IAAI,MAAM,qCAAgC,CAAC;AAAA,UACxD,KAAK,kDAA6C;AAAA,UAClD;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,CAAC,MAAM;AACT;AAAA,UACE,MAAM,OAAO,IAAI,MAAM,qBAAqB,CAAC;AAAA,UAC7C,KAAK,iCAAiC;AAAA,UACtC;AAAA,QACF;AACA;AAAA,MACF;AAEA;AAAA,QACE,MAAM,QAAQ,IAAI;AAAA,QAClB,KAAK,2CAAsC;AAAA,QAC3C;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,sBAAsB;AAC7B,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,sDAAiD,CAAC;AAAA,IACrE,GAAG,SAAS;AACZ,UAAM,MAAM;AAEZ,WAAO,OAAO,eAAe,aAAa,MAAM;AAAA,IAEhD,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAA+B;AACjD,mBAAa,KAAK;AAClB,UAAI,IAAI,SAAS,cAAc;AAC7B;AAAA,UACE,IAAI;AAAA,YACF,QAAQ,aAAa;AAAA,UACvB;AAAA,QACF;AAAA,MACF,OAAO;AACL,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAMA,eAAsB,qBACpB,MACA,cACwB;AACxB,QAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB;AAAA,MACxB,YAAY;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,eAAe;AAAA,MACf,cAAc;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,EAClE;AACA,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,mBACpB,cACwB;AACxB,QAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB;AAAA,MACxB,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,eAAe;AAAA,IACjB,CAAC;AAAA,EACH,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,IAAI,MAAM,yBAAyB,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,EACjE;AACA,SAAO,IAAI,KAAK;AAClB;AAYA,eAAsB,cAAc,aAAwC;AAC1E,QAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,IACzC,SAAS,EAAE,eAAe,UAAU,WAAW,GAAG;AAAA,EACpD,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,8BAA8B,IAAI,MAAM,GAAG;AAAA,EAC7D;AACA,SAAO,IAAI,KAAK;AAClB;AAGO,SAAS,YAAY,KAAmB;AAC7C,QAAM,KAAK,SAAS;AACpB,MAAI;AACJ,MAAI,OAAO,SAAU,OAAM,SAAS,GAAG;AAAA,WAC9B,OAAO,QAAS,OAAM,aAAa,GAAG;AAAA,MAC1C,OAAM,aAAa,GAAG;AAG3B,QAAM,QAAQ,KAAK,GAAG;AACtB,QAAM,GAAG,SAAS,MAAM;AAAA,EAExB,CAAC;AACD,QAAM,MAAM;AACd;AAlRA,IASa,WAGP,eACA,gBACA,mBAIA,eACO,cAEP;AArBN;AAAA;AAAA;AASO,IAAM,YACX,QAAQ,IAAI,4BAA4B;AAE1C,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAI1B,IAAM,gBAAgB;AACf,IAAM,eAAe,oBAAoB,aAAa;AAE7D,IAAM,SAAS;AAAA;AAAA;;;ACrBf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,OAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,gBAAe;AACxB,SAAS,SAAAC,cAAa;AActB,eAAsB,WAA2C;AAC/D,MAAI;AACF,UAAM,MAAM,MAAMH,UAAS,SAAS,GAAG,MAAM;AAC7C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,YAAY,SAAS,GAAG;AAC3E,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,SAAS,OAAiC;AAC9D,QAAM,OAAO,SAAS;AACtB,QAAMG,OAAMD,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAMD,WAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,GAAM;AAAA,IAC3D,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AACD,MAAI;AACF,UAAM,MAAM,MAAM,GAAK;AAAA,EACzB,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,YAA2B;AAC/C,QAAM,EAAE,QAAAG,QAAO,IAAI,MAAM,OAAO,aAAkB;AAClD,MAAI;AACF,UAAMA,QAAO,SAAS,CAAC;AAAA,EACzB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AACF;AAMA,eAAsB,gBAA6C;AACjE,QAAM,OAAO,MAAM,SAAS;AAC5B,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,eAAe,KAAK,IAAI,KAAK,KAAK,YAAY;AACpD,MAAI,CAAC,aAAc,QAAO,KAAK;AAE/B,MAAI,CAAC,KAAK,cAAc;AAEtB,UAAM,UAAU;AAChB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,mBAAmB,KAAK,YAAY;AACzD,UAAM,YAAuB;AAAA,MAC3B,aAAa,OAAO;AAAA,MACpB,cAAc,OAAO,iBAAiB,KAAK;AAAA,MAC3C,WAAW,KAAK,IAAI,IAAI,OAAO,aAAa;AAAA,IAC9C;AACA,UAAM,SAAS,SAAS;AACxB,WAAO,UAAU;AAAA,EACnB,QAAQ;AAEN,UAAM,UAAU;AAChB,WAAO;AAAA,EACT;AACF;AApFA,IAcM;AAdN;AAAA;AAAA;AAGA;AACA;AAUA,IAAM,oBAAoB;AAAA;AAAA;;;ACd1B,SAAS,eAAe;;;ACAxB,SAAS,aAAa;AACtB,SAAS,kBAAkB;;;ACD3B,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,eAAe;;;ACGjB,IAAM,uBAAuB;AAG7B,SAAS,iBAAyB;AACvC,SAAO,QAAQ,IAAI,+BAA+B;AACpD;;;ADNA;AAiEO,SAAS,gBAAqC;AACnD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,aAAa;AAAA,MACX,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU,QAAQ,IAAI,gCAAgC;AAAA,MACtD,SAAS,CAAC;AAAA,MACV,SAAS;AAAA,IACX;AAAA,IACA,iBAAiB;AAAA;AAAA;AAAA;AAAA,MAIf,MAAM;AAAA,IACR;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,aAAa,eAAe;AAAA,MAC5B,iBAAiB;AAAA,IACnB;AAAA,IACA,QAAQ;AAAA,MACN,YAAY,EAAE,SAAS,MAAM;AAAA,MAC7B,QAAQ,EAAE,SAAS,MAAM;AAAA,IAC3B;AAAA,IACA,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,qBAAqB;AAAA,IACvB;AAAA,EACF;AACF;AAEA,eAAsB,aAAuD;AAC3E,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,WAAW,GAAG,MAAM;AAC/C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,YAAY,cAAc,GAAG,MAAM;AAAA,EAC5C,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,WAAW,KAAyC;AACxE,QAAM,WAAW;AACjB,QAAM,UAAU,WAAW,GAAG,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAC3E;AAEA,eAAsB,aAA4B;AAChD,QAAM,MAAM,QAAQ,WAAW,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAM,MAAM,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,QAAM,MAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C;AAEA,SAAS,YACP,MACA,OACqB;AACrB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,EAAE,GAAG,KAAK,QAAQ,GAAI,MAAM,UAAU,CAAC,EAAG;AAAA,IAClD,aAAa,EAAE,GAAG,KAAK,aAAa,GAAI,MAAM,eAAe,CAAC,EAAG;AAAA,IACjE,UAAU,EAAE,GAAG,KAAK,UAAU,GAAI,MAAM,YAAY,CAAC,EAAG;AAAA,IACxD,iBAAkB,MAAM,mBACtB,KAAK;AAAA,IACP,iBAAiB;AAAA,MACf,GAAG,KAAK;AAAA,MACR,GAAI,MAAM,mBAAmB,CAAC;AAAA,IAChC;AAAA,IACA,QAAQ;AAAA,MACN,YAAY;AAAA,QACV,GAAG,KAAK,OAAO;AAAA,QACf,GAAI,MAAM,QAAQ,cAAc,CAAC;AAAA,MACnC;AAAA,MACA,QAAQ,EAAE,GAAG,KAAK,OAAO,QAAQ,GAAI,MAAM,QAAQ,UAAU,CAAC,EAAG;AAAA,IACnE;AAAA,IACA,SAAS,KAAK;AAAA,EAChB;AACF;;;AEzJA,IAAM,iBAAsC,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,oBAAoB;AAK1B,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,YAAY,EAAE,QAAQ,YAAY,EAAE;AACjD;AAEA,SAAS,SAAS,OAAuB;AACvC,MAAI,MAAM,UAAU,kBAAmB,QAAO;AAC9C,SAAO,GAAG,MAAM,MAAM,GAAG,iBAAiB,CAAC;AAC7C;AAEA,SAAS,kBAAkB,OAAgD;AACzE,MAAI,UAAU,KAAM,QAAO;AAC3B,UAAQ,OAAO,OAAO;AAAA,IACpB,KAAK;AACH,aAAO,SAAS,KAAK;AAAA,IACvB,KAAK;AACH,aAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAAA,IAC1C,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAUO,SAAS,SAAS,OAAyB;AAChD,SAAO,cAAc,KAAK;AAC5B;AAEA,SAAS,cAAc,OAAyB;AAC9C,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,MAAiB,CAAC;AACxB,eAAW,QAAQ,OAAO;AACxB,YAAM,gBAAgB,cAAc,IAAI;AACxC,UAAI,kBAAkB,QAAW;AAC/B,YAAI,KAAK,aAAa;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACtD,UAAI,eAAe,IAAI,aAAa,MAAM,CAAC,GAAG;AAC5C;AAAA,MACF;AACA,UAAI,YAAY,OAAO,aAAa,UAAU;AAC5C,YAAI,MAAM,IAAI,cAAc,QAAQ;AAAA,MACtC,OAAO;AACL,cAAM,YAAY,kBAAkB,QAAQ;AAC5C,YAAI,cAAc,QAAW;AAC3B,cAAI,MAAM,IAAI;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,kBAAkB,KAAK;AAChC;;;ACxGA,SAAS,gBAAgB;AACzB,SAAS,UAAU,gBAAgB;;;ACDnC,SAAS,kBAAkB;AAQpB,SAAS,OAAO,OAAuB;AAC5C,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAChE;;;ADLO,SAAS,WAAmB;AACjC,MAAI;AACF,UAAM,OAAO,SAAS,EAAE;AACxB,WAAO,OAAO,GAAG,IAAI,IAAI,SAAS,CAAC,EAAE;AAAA,EACvC,QAAQ;AACN,WAAO,OAAO,SAAS;AAAA,EACzB;AACF;AAMO,SAAS,SAAS,MAAc,QAAQ,IAAI,GAAW;AAC5D,QAAM,SAAS,OAAO,CAAC,UAAU,SAAS,mBAAmB,GAAG,GAAG;AACnE,SAAO,OAAO,UAAU,GAAG;AAC7B;AAGO,SAAS,cAAc,MAAc,QAAQ,IAAI,GAAW;AACjE,SAAO,OAAO,GAAG;AACnB;AAEA,SAAS,OAAO,MAAgB,KAAiC;AAC/D,MAAI;AACF,UAAM,MAAM,SAAS,OAAO,KAAK,KAAK,GAAG,CAAC,IAAI;AAAA,MAC5C;AAAA,MACA,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MAClC,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AACD,UAAM,UAAU,IAAI,KAAK;AACzB,WAAO,YAAY,KAAK,SAAY;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AEzCA,OAAO,WAAW;AAEX,IAAM,MAAM;AAAA,EACjB,MAAM,CAAC,QAAsB;AAC3B,YAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,EACjC;AAAA,EACA,IAAI,CAAC,QAAsB;AACzB,YAAQ,OAAO,MAAM,GAAG,MAAM,MAAM,QAAG,CAAC,IAAI,GAAG;AAAA,CAAI;AAAA,EACrD;AAAA,EACA,MAAM,CAAC,QAAsB;AAC3B,YAAQ,OAAO,MAAM,GAAG,MAAM,OAAO,GAAG,CAAC,IAAI,GAAG;AAAA,CAAI;AAAA,EACtD;AAAA,EACA,MAAM,CAAC,QAAsB;AAC3B,YAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,QAAG,CAAC,IAAI,GAAG;AAAA,CAAI;AAAA,EACnD;AAAA,EACA,MAAM,CAAC,QAAsB;AAC3B,YAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,GAAG,CAAC;AAAA,CAAI;AAAA,EAC5C;AAAA,EACA,MAAM,CAAC,QAAuB;AAC5B,YAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAC1D;AACF;AAEO,SAAS,MAAM,KAAa,OAAuC;AACxE,MAAI,QAAQ,IAAI,sBAAsB;AACpC,UAAM,UAAU,QAAQ,IAAI,KAAK,UAAU,KAAK,CAAC,KAAK;AACtD,YAAQ,OAAO,MAAM,oBAAoB,GAAG,GAAG,OAAO;AAAA,CAAI;AAAA,EAC5D;AACF;;;ANZA,eAAsB,iBAAiB,MAAoC;AACzE,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,CAAC,KAAK;AACR,QAAI,KAAK,oEAA+D;AACxE,YAAQ,WAAW;AACnB;AAAA,EACF;AACA,QAAM,OAAO,KAAK,QAAQ,QAAQ,IAAI;AACtC,QAAM,YAAY,WAAW;AAE7B,QAAM,YAAY;AAAA,IAChB,OAAO;AAAA,IACP;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,UAAU,SAAS;AAAA,IACnB,eAAe,cAAc;AAAA,EAC/B;AAEA,OAAK;AAAA,IACH,GAAG;AAAA,IACH,MAAM;AAAA,IACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC,CAAC;AAED,QAAM,OAAO,MAAM,YAAY,IAAI;AAEnC,OAAK;AAAA,IACH,GAAG;AAAA,IACH,MAAM;AAAA,IACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS,SAAS,IAAI,YAAY;AAAA,EACpC,CAAC;AAED,UAAQ,WAAW;AACrB;AAEA,SAAS,YAAY,MAA+B;AAClD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,MAAM,UAAU,CAAC,IAAI,GAAG;AAAA,MACpC,OAAO;AAAA,MACP,KAAK,QAAQ;AAAA,IACf,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,UAAI,KAAK,4BAA4B,IAAI,OAAO,EAAE;AAClD,cAAQ,GAAG;AAAA,IACb,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/C,CAAC;AACH;AAEA,SAAS,KAAK,OAAyB;AACrC,QAAM,wBAAwB;AAAA,IAC5B,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM;AAAA,EACnB,CAAC;AAED,OAAK,SAAS,KAAK;AACrB;;;AOzEA,SAAS,UAAU;;;ACAnB,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAK9B,IAAM,oBAAoB;AAE1B,IAAM,WAA2C;AAAA,EAC/C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,MAAM;AAAA,EACN,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,IAAM,qBAA4C,OAAO,KAAK,QAAQ;AAYtE,IAAM,oBAA6B;AAAA,EACxC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,cAAc;AAAA,EAEd,IAAI,SAA+C;AACjD,UAAM,OAAO,SAAS,QAAQ,KAAK;AACnC,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,OAAO,QAAQ,QAAQ,CAAC;AAC9B,UAAM,YACJ,SAAS,KAAK,YAAY,CAAC,KAAK,SAAS,KAAK,WAAW,CAAC;AAC5D,UAAM,WACJ,SAAS,KAAK,WAAW,CAAC,KAC1B,SAAS,KAAK,UAAU,CAAC,KACzB,SAAU,KAAK,MAAM,IAA4C,MAAM,CAAC;AAC1E,UAAM,iBACJ,SAAS,KAAK,iBAAiB,CAAC,KAAK,SAAS,KAAK,gBAAgB,CAAC;AAEtE,WAAO;AAAA,MACL;AAAA,MACA,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MAC/C,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC7C,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,UAAkB,QAAoB,UAAU;AAC5D,UAAM,OAAO,kBAAkB,UAAU,KAAK;AAC9C,UAAM,WAAW,MAAM,SAAyB,IAAI;AACpD,UAAM,OAAuB,EAAE,GAAI,YAAY,CAAC,EAAG;AACnD,UAAM,QAA2C,EAAE,GAAI,KAAK,SAAS,CAAC,EAAG;AAEzE,eAAW,SAAS,oBAAoB;AACtC,YAAM,YAAY,MAAM,KAAK,KAAK,CAAC,GAAG;AAAA,QACpC,CAAC,MAAM,CAAC,uBAAuB,EAAE,OAAO;AAAA,MAC1C;AACA,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,GAAG,iBAAiB,SAAS,KAAK;AAAA,MAC7C,CAAC;AACD,YAAM,KAAK,IAAI;AAAA,IACjB;AAEA,SAAK,QAAQ;AACb,UAAML,OAAMI,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAMF,WAAU,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAClE,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAM,UAAU,UAAkB,QAAoB,UAAU;AAC9D,UAAM,OAAO,kBAAkB,UAAU,KAAK;AAC9C,UAAM,WAAW,MAAM,SAAyB,IAAI;AACpD,QAAI,CAAC,UAAU,MAAO,QAAO,EAAE,SAAS,OAAO,KAAK;AACpD,UAAM,QAAQ,EAAE,GAAG,SAAS,MAAM;AAClC,QAAI,UAAU;AACd,eAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,YAAM,WAAW,QAAQ;AAAA,QACvB,CAAC,MAAM,CAAC,uBAAuB,EAAE,OAAO;AAAA,MAC1C;AACA,UAAI,SAAS,WAAW,QAAQ,OAAQ,WAAU;AAClD,UAAI,SAAS,WAAW,GAAG;AACzB,eAAO,MAAM,KAAK;AAAA,MACpB,OAAO;AACL,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AACA,aAAS,QAAQ;AACjB,UAAMA,WAAU,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACtE,WAAO,EAAE,SAAS,SAAS,KAAK;AAAA,EAClC;AAAA,EAEA,MAAM,YAAY,UAAkB,QAAoB,UAAU;AAChE,UAAM,WAAW,MAAM;AAAA,MACrB,kBAAkB,UAAU,KAAK;AAAA,IACnC;AACA,QAAI,CAAC,UAAU,MAAO,QAAO;AAC7B,WAAO,OAAO,OAAO,SAAS,KAAK,EAAE;AAAA,MAAK,CAAC,YACzC,QAAQ,KAAK,CAAC,MAAM,uBAAuB,EAAE,OAAO,CAAC;AAAA,IACvD;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,UAAkB,OAA2B;AACtE,SAAO,UAAU,WACbG,MAAKF,SAAQ,GAAG,WAAW,eAAe,IAC1CE,MAAK,UAAU,WAAW,eAAe;AAC/C;AAEA,SAAS,uBAAuB,KAAkC;AAChE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,IAAI,WAAW,GAAG,iBAAiB,OAAO;AACnD;AAEA,eAAe,SAAY,MAAsC;AAC/D,MAAI;AACF,UAAM,MAAM,MAAMJ,UAAS,MAAM,MAAM;AACvC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,SAAS,SAAS,GAAgC;AAChD,SAAO,OAAO,MAAM,YAAY,EAAE,SAAS,IAAI,IAAI;AACrD;;;ACjJA,SAAS,SAAAK,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAK9B,IAAMC,qBAAoB;AAM1B,IAAMC,YAA2C;AAAA,EAC/C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,MAAM;AAAA,EACN,YAAY;AAAA;AAAA,EAEZ,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,eAAe;AACjB;AAEO,IAAM,qBAA4C,OAAO,KAAKA,SAAQ;AAatE,IAAM,gBAAyB;AAAA,EACpC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,cAAc;AAAA,EAEd,IAAI,SAA+C;AACjD,UAAM,OAAOA,UAAS,QAAQ,KAAK;AACnC,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,OAAO,QAAQ,QAAQ,CAAC;AAE9B,UAAM,YACJC,UAAS,KAAK,YAAY,CAAC,KAC3BA,UAAS,KAAK,WAAW,CAAC,KAC1BA,UAAS,KAAK,iBAAiB,CAAC;AAClC,UAAM,WAAWA,UAAS,KAAK,WAAW,CAAC,KAAKA,UAAS,KAAK,UAAU,CAAC;AAGzE,UAAM,iBACJA,UAAS,KAAK,iBAAiB,CAAC,KAChCA,UAAS,KAAK,gBAAgB,CAAC,KAC/BA,UAAS,QAAQ,IAAI,sBAAsB;AAE7C,WAAO;AAAA,MACL;AAAA,MACA,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MAC/C,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC7C,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,UAAkB,QAAoB,UAAU;AAC5D,UAAM,OAAO,kBAAkB,UAAU,KAAK;AAC9C,UAAM,WAAW,MAAMC,UAA4B,IAAI;AACvD,UAAM,OAA0B;AAAA,MAC9B,SAAS;AAAA,MACT,GAAI,YAAY,CAAC;AAAA,IACnB;AACA,UAAM,QAA2C,EAAE,GAAI,KAAK,SAAS,CAAC,EAAG;AAEzE,eAAW,SAAS,oBAAoB;AACtC,YAAM,YAAY,MAAM,KAAK,KAAK,CAAC,GAAG;AAAA,QACpC,CAAC,MAAM,CAACC,wBAAuB,EAAE,OAAO;AAAA,MAC1C;AACA,eAAS,KAAK,EAAE,SAAS,GAAGJ,kBAAiB,SAAS,KAAK,GAAG,CAAC;AAC/D,YAAM,KAAK,IAAI;AAAA,IACjB;AAEA,SAAK,QAAQ;AACb,UAAMN,OAAMI,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAMF,WAAU,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAClE,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAM,UAAU,UAAkB,QAAoB,UAAU;AAC9D,UAAM,OAAO,kBAAkB,UAAU,KAAK;AAC9C,UAAM,WAAW,MAAMO,UAA4B,IAAI;AACvD,QAAI,CAAC,UAAU,MAAO,QAAO,EAAE,SAAS,OAAO,KAAK;AACpD,UAAM,QAAQ,EAAE,GAAG,SAAS,MAAM;AAClC,QAAI,UAAU;AACd,eAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,YAAM,WAAW,QAAQ;AAAA,QACvB,CAAC,MAAM,CAACC,wBAAuB,EAAE,OAAO;AAAA,MAC1C;AACA,UAAI,SAAS,WAAW,QAAQ,OAAQ,WAAU;AAClD,UAAI,SAAS,WAAW,GAAG;AACzB,eAAO,MAAM,KAAK;AAAA,MACpB,OAAO;AACL,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AACA,aAAS,QAAQ;AACjB,UAAMR,WAAU,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACtE,WAAO,EAAE,SAAS,SAAS,KAAK;AAAA,EAClC;AAAA,EAEA,MAAM,YAAY,UAAkB,QAAoB,UAAU;AAChE,UAAM,WAAW,MAAMO;AAAA,MACrB,kBAAkB,UAAU,KAAK;AAAA,IACnC;AACA,QAAI,CAAC,UAAU,MAAO,QAAO;AAC7B,WAAO,OAAO,OAAO,SAAS,KAAK,EAAE;AAAA,MAAK,CAAC,YACzC,QAAQ,KAAK,CAAC,MAAMC,wBAAuB,EAAE,OAAO,CAAC;AAAA,IACvD;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,UAAkB,OAA2B;AACtE,SAAO,UAAU,WACbL,MAAKF,SAAQ,GAAG,WAAW,YAAY,IACvCE,MAAK,UAAU,WAAW,YAAY;AAC5C;AAEA,SAASK,wBAAuB,KAAkC;AAChE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,IAAI,WAAW,GAAGJ,kBAAiB,OAAO;AACnD;AAEA,eAAeG,UAAY,MAAsC;AAC/D,MAAI;AACF,UAAM,MAAM,MAAMR,UAAS,MAAM,MAAM;AACvC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,SAASO,UAAS,GAAgC;AAChD,SAAO,OAAO,MAAM,YAAY,EAAE,SAAS,IAAI,IAAI;AACrD;;;ACvJO,IAAM,WAAqC;AAAA,EAChD,eAAe;AAAA,EACf,QAAQ;AACV;AAEO,IAAM,cAAsC,OAAO,OAAO,QAAQ;;;AHNzE;AASA,eAAsB,WAAW,MAAqC;AACpE,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,UAAU,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,EAAE;AAEvE,aAAW,MAAM,SAAS;AACxB,QAAI,OAAO,iBAAiB,OAAO,UAAU;AAC3C,UAAI,KAAK,qBAAqB,EAAE,GAAG;AACnC;AAAA,IACF;AACA,UAAM,UAAU,YAAY,KAAK,CAAC,MAAM,EAAE,OAAQ,EAAc;AAChE,QAAI,CAAC,QAAS;AACd,UAAM,EAAE,SAAS,KAAK,IAAI,MAAM,QAAQ,UAAU,QAAQ;AAC1D,QAAI,QAAS,KAAI,GAAG,WAAW,QAAQ,KAAK,eAAe,IAAI,EAAE;AAAA,QAC5D,KAAI,KAAK,GAAG,QAAQ,KAAK,yBAAyB,IAAI,GAAG;AAAA,EAChE;AAEA,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,KAAK;AACP,QAAI,CAAC,KAAK,SAAS,KAAK,UAAU,eAAe;AAC/C,UAAI,OAAO,WAAW,UAAU;AAAA,IAClC;AACA,QAAI,CAAC,KAAK,SAAS,KAAK,UAAU,UAAU;AAC1C,UAAI,OAAO,OAAO,UAAU;AAAA,IAC9B;AACA,QAAI,CAAC,KAAK,MAAO,KAAI,UAAU;AAC/B,UAAM,WAAW,GAAG;AAAA,EACtB;AAEA,MAAI,KAAK,OAAO;AACd,UAAM,GAAG,WAAW,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACvD,QAAI,GAAG,UAAU,WAAW,CAAC,EAAE;AAAA,EACjC;AACF;;;AI5CA,OAAOG,YAAW;AAGlB;AACA;;;ACFA;AAFA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,WAAW;;;ACCpB;AAFA,SAAS,SAAAC,QAAO,SAAAC,QAAO,YAAAC,WAAU,QAAQ,aAAAC,kBAAiB;AAC1D,SAAS,WAAAC,gBAAe;AAUxB,eAAsB,mBAAgD;AACpE,QAAM,UACJ,QAAQ,IAAI,gCACZ,QAAQ,IAAI;AACd,MAAI,QAAS,QAAO;AAEpB,MAAI;AACF,UAAM,OAAO,MAAMF,UAAS,iBAAiB,GAAG,MAAM,GAAG,KAAK;AAC9D,WAAO,OAAO;AAAA,EAChB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,iBAAiB,QAA+B;AACpE,QAAM,OAAO,iBAAiB;AAC9B,QAAMD,OAAMG,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAMD,WAAU,MAAM,OAAO,KAAK,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACtE,MAAI;AACF,UAAMH,OAAM,MAAM,GAAK;AAAA,EACzB,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,oBAAmC;AACvD,MAAI;AACF,UAAM,OAAO,iBAAiB,CAAC;AAAA,EACjC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,EAC9D;AACF;AAMA,eAAsB,iBACpB,SACY;AACZ,QAAM,SAAS,MAAM,iBAAiB;AACtC,MAAI,QAAQ;AACV,IAAC,QAAmC,4BAA4B,IAAI;AAAA,EACtE;AACA,SAAO;AACT;;;AD3BA,IAAM,sBAAN,MAAqD;AAAA,EACnD,MAAM,SAA0C;AAC9C,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAAA,EACA,MAAM,OAA+C;AACnD,WAAO,EAAE,IAAI,OAAO,QAAQ,4BAA4B;AAAA,EAC1D;AACF;AAEA,IAAM,sBAAN,MAAqD;AAAA,EACnD,YACmB,OACA,QACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,OAAO,OAA+D;AAC1E,UAAM,OAAO,MAAMK,UAAS,MAAM,cAAc;AAChD,UAAM,gBAAgB;AAAA,MACpB,KAAK;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AACA,UAAM,OAAO,MAAM,IAAI,eAAe,MAAM;AAAA,MAC1C,QAAQ;AAAA,MACR,OAAO,KAAK;AAAA,MACZ,aAAa;AAAA,MACb,iBAAiB;AAAA,IACnB,CAAC;AACD,WAAO,EAAE,UAAU,KAAK,UAAU,KAAK,KAAK,KAAK,MAAM,KAAK,WAAW;AAAA,EACzE;AAAA,EAEA,MAAM,OAA8D;AAClE,QAAI,CAAC,KAAK,MAAO,QAAO,EAAE,IAAI,OAAO,QAAQ,gBAAgB;AAC7D,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AACF;AAQA,IAAM,0BAAN,MAAyD;AAAA,EACvD,YAA6B,aAAqB;AAArB;AAAA,EAAsB;AAAA,EAAtB;AAAA,EAE7B,MAAM,OAAO,OAA+D;AAC1E,UAAM,QAAQ,MAAM,cAAc;AAClC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,MAAMA,UAAS,MAAM,cAAc;AAChD,UAAM,MAAM,IAAI,IAAI,wBAAwB,KAAK,WAAW,EAAE,SAAS;AACvE,UAAM,UAAU,MAAM,iBAAiB;AAAA,MACrC,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,WAAW,MAAM;AAAA,MACjB,eAAe,MAAM;AAAA,IACvB,CAAC;AACD,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,QAAQ,SAAS,KAAK,CAAC;AAC9D,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAM,IAAI;AAAA,QACR,6BAA6B,IAAI,MAAM,MAAM,QAAQ,IAAI,UAAU;AAAA,MACrE;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAO7B,QAAI,CAAC,KAAK,MAAM,CAAC,KAAK,WAAW,CAAC,KAAK,UAAU;AAC/C,YAAM,IAAI,MAAM,4BAA4B,KAAK,SAAS,SAAS,EAAE;AAAA,IACvE;AACA,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK,QAAQ,KAAK;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAM,OAA8D;AAClE,UAAM,QAAQ,MAAM,cAAc;AAClC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,MACV;AAAA,IACF;AAIA,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,wBAAwB,KAAK,WAAW,EAAE,SAAS;AACvE,YAAM,UAAU,MAAM,iBAAiB;AAAA,QACrC,eAAe,UAAU,KAAK;AAAA,MAChC,CAAC;AACD,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AACxD,UAAI,IAAI,WAAW,IAAK,QAAO,EAAE,IAAI,KAAK;AAC1C,UAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KAAK;AAC5C,cAAM,KAAK,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC9C,YAAI,GAAG,SAAS,WAAW,GAAG;AAC5B,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,QACE;AAAA,UACJ;AAAA,QACF;AACA,eAAO,EAAE,IAAI,OAAO,QAAQ,4BAA4B,IAAI,MAAM,IAAI;AAAA,MACxE;AACA,aAAO,EAAE,IAAI,OAAO,QAAQ,qBAAqB,IAAI,MAAM,GAAG;AAAA,IAChE,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,yBAA0B,IAAc,OAAO;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,sBACd,KACiB;AACjB,MAAI,IAAI,SAAS,OAAQ,QAAO,IAAI,oBAAoB;AACxD,MAAI,IAAI,SAAS,YAAY;AAC3B,WAAO,IAAI,wBAAwB,eAAe,CAAC;AAAA,EACrD;AAEA,QAAM,QAAQ,QAAQ,IAAI,IAAI,QAAQ;AACtC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,yBAAyB,IAAI,QAAQ;AAAA,IACvC;AAAA,EACF;AACA,SAAO,IAAI,oBAAoB,OAAO,IAAI,MAAM;AAClD;AAEA,SAAS,cACP,QACAC,WACA,WACQ;AACR,QAAM,aAAa,OAAO,SAAS,GAAG,IAAI,SAAS,GAAG,MAAM;AAC5D,QAAM,cAAc,mBAAmB,SAAS;AAChD,SAAO,GAAG,UAAU,GAAGA,SAAQ,IAAI,WAAW;AAChD;;;ADlKA,eAAsB,UAAU,MAAoC;AAClE,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAkB,CAAC;AAEzB,QAAM,QAAQ,MAAM,cAAc;AAClC,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,IAAI,SAAS;AAAA,IACb,GAAI,QAAQ,CAAC,IAAI,EAAE,QAAQ,6BAA6B;AAAA,EAC1D,CAAC;AAED,QAAM,MAAM,MAAM,WAAW;AAC7B,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,IAAI,OAAO;AAAA,IACX,QAAQ,MAAM,WAAW,IAAI,WAAW,WAAW,CAAC;AAAA,EACtD,CAAC;AAED,MAAI,KAAK;AACP,QAAI,IAAI,YAAY,gBAAgB;AAClC,UAAI;AACF,cAAM,QAAQ,sBAAsB,IAAI,eAAe;AACvD,cAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,YAAI,KAAK,IAAI;AACX,iBAAO,KAAK,EAAE,MAAM,8BAA8B,IAAI,KAAK,CAAC;AAAA,QAC9D,OAAO;AACL,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,QAAQ,KAAK;AAAA,UACf,CAAC;AAAA,QACH;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,IAAI;AAAA,UACJ,QAAS,IAAc;AAAA,QACzB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,eAAW,WAAW,aAAa;AACjC,YAAM,UACJ,QAAQ,OAAO,gBACX,IAAI,OAAO,WAAW,UACtB,IAAI,OAAO,OAAO;AACxB,UAAI,CAAC,QAAS;AACd,YAAM,YAAY,MAAM,QAAQ,YAAY,UAAU,IAAI,UAAU;AACpE,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,QAAQ,KAAK;AAAA,QACtB,IAAI;AAAA,QACJ,GAAI,YAAY,CAAC,IAAI,EAAE,QAAQ,4BAA4B;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,KAAK,MAAM;AACb,QAAI,KAAK,EAAE,IAAI,OAAO,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC;AAAA,EACpD,OAAO;AACL,eAAW,KAAK,QAAQ;AACtB,YAAM,MAAM,EAAE,KAAKC,OAAM,MAAM,QAAG,IAAIA,OAAM,IAAI,QAAG;AACnD,YAAM,SAAS,EAAE,SAASA,OAAM,IAAI,MAAM,EAAE,MAAM,GAAG,IAAI;AACzD,UAAI,KAAK,GAAG,GAAG,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE;AAAA,IACtC;AAAA,EACF;AAEA,UAAQ,WAAW,OAAO,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,IAAI;AACrD;;;AGjEA,eAAsB,QACpB,WACA,MACe;AACf,QAAM,MAAM,MAAM,WAAW;AAC7B,MAAI,CAAC,OAAO,CAAC,IAAI,SAAS;AACxB,UAAM,gDAA2C;AACjD;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,gBAAgB;AACpC,QAAM,OAAO,gBAAgB,KAAK;AAClC,QAAM,UAAuB,EAAE,OAAO,WAAW,KAAK;AAEtD,QAAM,UAAU,YAAY,KAAK,OAAO,SAAS;AACjD,MAAI,CAAC,SAAS;AACZ,UAAM,iCAAiC,SAAS,EAAE;AAClD;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ,IAAI,OAAO;AAClC,MAAI,CAAC,QAAQ;AACX,UAAM,WAAW,QAAQ,EAAE,sBAAsB,SAAS,EAAE;AAC5D;AAAA,EACF;AAEA,QAAM,QAAQ,WAAW,SAAS,MAAM;AACxC,QAAM,cAAc;AAAA,IAClB,OAAO,MAAM;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM;AAAA,EACnB,CAAC;AAED,MACE,OAAO,SAAS,iBAChB,IAAI,YAAY,kBAChB,OAAO,gBACP;AACA,QAAI;AACF,YAAM,QAAQ,sBAAsB,IAAI,eAAe;AACvD,YAAM,SAAS,MAAM,MAAM,OAAO;AAAA,QAChC,gBAAgB,OAAO;AAAA,QACvB,UAAU,MAAM,YAAY,SAAS;AAAA,QACrC,WAAW,OAAO,aAAa;AAAA,QAC/B,OAAO,QAAQ;AAAA,MACjB,CAAC;AACD,YAAM,uBAAuB;AAAA,QAC3B,MAAM,OAAO;AAAA,QACb,UAAU,OAAO;AAAA,MACnB,CAAC;AAED,UAAI,IAAI,YAAY,iBAAiB;AACnC,cAAM,eAAe;AAAA,UACnB,aAAa,eAAe;AAAA,UAC5B,SAAS;AAAA,YACP,WAAW,OAAO,aAAa;AAAA,YAC/B,OAAO,QAAQ;AAAA,YACf,UAAU,MAAM,YAAY,SAAS;AAAA,YACrC,SAAS,OAAO;AAAA,YAChB,WAAW,MAAM;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,KAAK,6BAA8B,IAAc,OAAO,EAAE;AAAA,IAChE;AAAA,EACF;AAGA,OAAK,SAAS,KAAK;AACrB;AAUA,eAAe,eAAe,MAGZ;AAChB,MAAI;AACF,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,UAAM,QAAQ,MAAMA,eAAc;AAClC,UAAM,cAAsC;AAAA,MAC1C,gBAAgB;AAAA,IAClB;AACA,QAAI,MAAO,aAAY,eAAe,IAAI,UAAU,KAAK;AACzD,UAAM,UAAU,MAAM,iBAAiB,WAAW;AAElD,UAAM,MAAM,IAAI,IAAI,iBAAiB,KAAK,WAAW,EAAE,SAAS;AAChE,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,KAAK,OAAO;AAAA,IACnC,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,2BAA2B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAC/D;AAAA,IACF;AACA,UAAM,qBAAqB,EAAE,WAAW,KAAK,QAAQ,UAAU,CAAC;AAAA,EAClE,SAAS,KAAK;AAEZ,UAAM,0BAA2B,IAAc,OAAO,EAAE;AAAA,EAC1D;AACF;AAEA,SAAS,YACP,UACA,WACqB;AACrB,MAAI,UAAU;AACZ,WAAO,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAAA,EAClD;AACA,aAAW,KAAK,aAAa;AAC3B,QAAI,EAAE,IAAI,EAAE,OAAO,UAAU,CAAC,EAAG,QAAO;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,WACP,SACA,QACY;AACZ,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,OAAO,QAAQ;AAAA,IACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,GAAI,OAAO,cAAc,SAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,IACxE,GAAI,OAAO,aAAa,SAAY,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,IACrE,GAAI,OAAO,mBAAmB,SAC1B,EAAE,gBAAgB,OAAO,eAAe,IACxC,CAAC;AAAA,IACL,GAAI,OAAO,YAAY,SAAY,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,IAClE,UAAU,SAAS;AAAA,IACnB,UAAU,SAAS;AAAA,IACnB,eAAe,cAAc;AAAA,EAC/B;AACF;AAOA,IAAM,mBAAmB;AAEzB,eAAe,kBAAmC;AAChD,MAAI,QAAQ,MAAM,MAAO,QAAO;AAChC,SAAO,IAAI,QAAgB,CAAC,YAAY;AACtC,QAAI,MAAM;AACV,QAAI,UAAU;AACd,UAAM,SAAS,MAAY;AACzB,UAAI,QAAS;AACb,gBAAU;AACV,cAAQ,MAAM,mBAAmB,MAAM;AACvC,cAAQ,MAAM,mBAAmB,KAAK;AACtC,cAAQ,MAAM,mBAAmB,OAAO;AAGxC,UAAI;AACF,gBAAQ,MAAM,MAAM;AACpB,gBAAQ,MAAM,MAAM;AAAA,MACtB,QAAQ;AAAA,MAER;AACA,cAAQ,GAAG;AAAA,IACb;AACA,UAAM,OAAO,WAAW,QAAQ,gBAAgB;AAChD,SAAK,MAAM;AACX,YAAQ,MAAM,YAAY,MAAM;AAChC,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAClC,aAAO;AACP,WAAK,QAAQ;AAAA,IACf,CAAC;AACD,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,mBAAa,IAAI;AACjB,aAAO;AAAA,IACT,CAAC;AACD,YAAQ,MAAM,GAAG,SAAS,MAAM;AAC9B,mBAAa,IAAI;AACjB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,gBAAgB,KAAkD;AACzE,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC5NA,SAAS,UAAU,SAAS,gBAAgB;AAC5C,OAAOC,YAAW;AAclB;AACA;AAcA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBrB,eAAsB,QAAQ,MAAkC;AAC9D,QAAM,QAAQ,MAAM,cAAc;AAClC,MAAI,CAAC,OAAO;AACV,QAAI,KAAK,kDAAkD;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAKC,OAAM,KAAK,wBAAwB,CAAC;AAC7C,MAAI,KAAK,YAAY;AACrB,MAAI,KAAK,EAAE;AAEX,QAAM,WAAW,MAAM,WAAW;AAClC,QAAM,cACJ,CAAC,KAAK,OAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,OAAO,KAAK;AAElE,MAAI,YAAY,CAAC,KAAK,OAAO;AAC3B,QAAI,CAAC,aAAa;AAChB,UAAI;AAAA,QACF,4BAA4B,WAAW,CAAC;AAAA,MAC1C;AACA;AAAA,IACF;AACA,QAAI,KAAK,4BAA4B,WAAW,CAAC,GAAG;AACpD,UAAM,YAAY,MAAM;AAAA,MAAW,MACjC,QAAQ,EAAE,SAAS,8BAA8B,SAAS,KAAK,CAAC;AAAA,IAClE;AACA,QAAI,cAAc,OAAW;AAC7B,QAAI,CAAC,WAAW;AACd,UAAI,KAAK,+CAA0C;AACnD;AAAA,IACF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AAEA,MAAI;AACJ,MAAI,aAAa;AACf,UAAM,SAAS,MAAM,cAAc,IAAI;AACvC,QAAI,CAAC,OAAQ;AACb,cAAU;AAAA,EACZ,OAAO;AACL,cAAU;AAAA,MACR,QAAQ,YAAY,KAAK,MAAM;AAAA,MAC/B,aAAa,KAAK,eAAe;AAAA,MACjC,UAAU,KAAK,YAAY;AAAA,MAC3B,WAAW,KAAK,aAAa;AAAA,MAC7B,cAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,QAAQ,OAAO,SAAS,GAAG;AAC7B,QAAI,KAAK,+CAA0C;AACnD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAMA,QAAM,MAA2B,cAAc;AAC/C,MAAI,YAAY,iBAAiB,QAAQ;AACzC,MAAI,YAAY,kBAAkB,QAAQ;AAC1C,MAAI,YAAY,iBAAiB,QAAQ;AACzC,MAAI,OAAO,WAAW,UAAU,QAAQ,OAAO,IAAI,aAAa;AAChE,MAAI,OAAO,OAAO,UAAU,QAAQ,OAAO,IAAI,QAAQ;AAEvD,QAAM,WAAW,GAAG;AACpB,MAAI,GAAG,mBAAmB,WAAW,CAAC,EAAE;AAExC,MAAI,QAAQ,iBAAiB,QAAW;AACtC,QAAI,QAAQ,iBAAiB,IAAI;AAC/B,YAAM,kBAAkB;AACxB,UAAI,GAAG,qDAAqD;AAAA,IAC9D,OAAO;AACL,YAAM,iBAAiB,QAAQ,YAAY;AAC3C,UAAI,GAAG,4CAA4C;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,eAAe,KAAK,SAAS;AACnC,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,cAAc;AAChB,eAAW,WAAW,aAAa;AACjC,YAAM,UACH,QAAQ,OAAO,iBAAiB,IAAI,OAAO,WAAW,WACtD,QAAQ,OAAO,YAAY,IAAI,OAAO,OAAO;AAChD,UAAI,CAAC,QAAS;AACd,YAAM,EAAE,KAAK,IAAI,MAAM,QAAQ,QAAQ,UAAU,IAAI,UAAU;AAC/D,UAAI,GAAG,aAAa,QAAQ,KAAK,iBAAY,IAAI,IAAI,CAAC,EAAE;AAAA,IAC1D;AAAA,EACF;AAEA,MAAI,KAAK,EAAE;AACX,MAAI,KAAK,gBAAgB,WAAW,CAAC,EAAE;AACvC,MAAI,KAAK,kDAAkD;AAC7D;AAEA,eAAe,cAAc,MAAiD;AAC5E,QAAM,YAAY,KAAK,SAAS,YAAY,KAAK,MAAM,IAAI;AAC3D,QAAM,WAAW,MAAM;AAAA,IAAW,MAChC,SAAkB;AAAA,MAChB,SAAS;AAAA,MACT,SAAS,YAAY,IAAI,CAAC,aAAa;AAAA,QACrC,MAAM,QAAQ;AAAA,QACd,OAAO,QAAQ;AAAA,QACf,SAAS,YAAY,UAAU,IAAI,QAAQ,EAAE,IAAI;AAAA,MACnD,EAAE;AAAA,MACF,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AACA,MAAI,aAAa,OAAW,QAAO;AAEnC,QAAM,cAAc,MAAM;AAAA,IAAW,MACnC,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,SAAS,KAAK,eAAe;AAAA,IAC/B,CAAC;AAAA,EACH;AACA,MAAI,gBAAgB,OAAW,QAAO;AAEtC,QAAM,WAAW,MAAM;AAAA,IAAW,MAChC,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,SAAS,KAAK,YAAY;AAAA,IAC5B,CAAC;AAAA,EACH;AACA,MAAI,aAAa,OAAW,QAAO;AAEnC,QAAM,YAAY,MAAM;AAAA,IAAW,MACjC,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,SAAS,KAAK,aAAa;AAAA,IAC7B,CAAC;AAAA,EACH;AACA,MAAI,cAAc,OAAW,QAAO;AAEpC,MAAI;AACJ,MAAI,KAAK,iBAAiB,QAAW;AAEnC,mBAAe,KAAK;AAAA,EACtB,OAAO;AACL,UAAM,WAAW,MAAM,iBAAiB;AACxC,UAAM,SAAS,MAAM;AAAA,MAAW,MAC9B,SAAS;AAAA,QACP,SAAS,WACL,uFACA;AAAA,QACJ,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,QAAI,WAAW,OAAW,QAAO;AACjC,UAAM,UAAU,OAAO,KAAK;AAC5B,QAAI,YAAY,GAAI,gBAAe;AAAA,aAC1B,YAAY,IAAK,gBAAe;AAAA,QACpC,gBAAe;AAAA,EACtB;AAEA,SAAO;AAAA,IACL,QAAQ,IAAI,IAAI,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,eAAe,WAAc,KAA+C;AAC1E,MAAI;AACF,WAAO,MAAM,IAAI;AAAA,EACnB,SAAS,KAAK;AACZ,QACE,eAAe,UACd,IAAI,SAAS,qBAAqB,IAAI,SAAS,qBAChD;AACA,UAAI,KAAK,UAAU;AACnB,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,oBAAI,IAAa;AAC7B,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,YAAY,KAAK;AACnB,eAAW,KAAK,YAAa,KAAI,IAAI,EAAE,EAAE;AACzC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,WAAW,sBACtB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB,aAAW,MAAM,MAAM;AACrB,QAAI,OAAO,iBAAiB,OAAO,SAAU,KAAI,IAAI,EAAE;AAAA,QAClD,KAAI,KAAK,qBAAqB,EAAE,oBAAe;AAAA,EACtD;AACA,SAAO;AACT;AAEA,SAAS,IAAI,GAAmB;AAC9B,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,IAAI,SAAS,CAAC,IAAI;AACvD;;;AChQA;AAOA;AARA,OAAOC,YAAW;AAWlB,IAAM,iBAAiB;AAEvB,eAAsB,WAA0B;AAC9C,MAAI,KAAKC,OAAM,KAAK,6BAA6B,CAAC;AAGlD,MAAI;AACJ,MAAI;AACF,QAAI,KAAK,gBAAgB,YAAY,4BAAuB;AAC5D,iBAAa,MAAM,cAAc;AAAA,EACnC,SAAS,KAAK;AACZ,QAAI,KAAK,iBAAkB,IAAc,OAAO,EAAE;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,qBAAqB,WAAW,MAAM,WAAW,YAAY;AAAA,EAC9E,SAAS,KAAK;AACZ,QAAI,KAAK,0BAA2B,IAAc,OAAO,EAAE;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAIC;AACJ,MAAI;AACF,IAAAA,YAAW,MAAM,cAAc,OAAO,YAAY;AAAA,EACpD,SAAS,KAAK;AACZ,QAAI,KAAK,8BAA+B,IAAc,OAAO,EAAE;AAC/D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MACE,CAACA,UAAS,OAAO,SAAS,IAAI,cAAc,EAAE,KAC9C,CAACA,UAAS,gBACV;AACA,QAAI;AAAA,MACF,SAAS,cAAc,+BAA+BA,UAAS,SAAS,YAAY;AAAA,IACtF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SAAS;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,GAAI,OAAO,kBAAkB,SACzB,EAAE,cAAc,OAAO,cAAc,IACrC,CAAC;AAAA,IACL,WAAW,KAAK,IAAI,IAAI,OAAO,aAAa;AAAA,EAC9C,CAAC;AAED,QAAM,OAAOA,UAAS,sBAAsBA,UAAS;AACrD,MAAI,GAAG,gBAAgBD,OAAM,KAAK,IAAI,CAAC,KAAKC,UAAS,KAAK,GAAG;AAC7D,MAAI,KAAK,sDAAsD;AACjE;AAEA,eAAsB,YAA2B;AAC/C,QAAM,UAAU;AAChB,MAAI,GAAG,aAAa;AACtB;;;ACvEA,SAAS,kBAAkB;AAC3B,SAAS,QAAAC,aAAY;AACrB,OAAOC,YAAW;AAGlB;AAOA,eAAsB,UAAU,MAAoC;AAClE,QAAM,MAAM,MAAM,WAAW;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAE7B,MAAI,CAAC,KAAK;AACR,QAAI,KAAK,KAAM,KAAI,KAAK,EAAE,aAAa,MAAM,CAAC;AAAA,QACzC,KAAI,KAAK,sBAAsB,WAAW,CAAC,sCAAiC;AACjF;AAAA,EACF;AAEA,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC,YAAY,IAAI,OAAO,OAAO;AAAA,MAC5B,IAAI,EAAE;AAAA,MACN,OAAO,EAAE;AAAA,MACT,SACE,EAAE,OAAO,gBACL,IAAI,OAAO,WAAW,UACtB,IAAI,OAAO,OAAO;AAAA,MACxB,WAAW,MAAM,EAAE,YAAY,QAAQ;AAAA,IACzC,EAAE;AAAA,EACJ;AAEA,MAAI,KAAK,MAAM;AACb,QAAI,KAAK;AAAA,MACP,aAAa;AAAA,MACb,YAAY,WAAW;AAAA,MACvB,SAAS,IAAI;AAAA,MACb,SAAS,IAAI;AAAA,MACb,QAAQ;AAAA,QACN,QAAQ,WAAWC,MAAK,UAAU,sBAAsB,CAAC;AAAA,QACzD,SAAS,WAAWA,MAAK,UAAU,YAAY,CAAC;AAAA,MAClD;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AACD;AAAA,EACF;AAEA,MAAI,KAAKC,OAAM,KAAK,yBAAyB,CAAC;AAC9C,MAAI,KAAK,gBAAgB,WAAW,CAAC,EAAE;AACvC,MAAI,KAAK,gBAAgB,IAAI,UAAU,QAAQ,IAAI,EAAE;AACrD,MAAI,KAAK,EAAE;AACX,MAAI,KAAKA,OAAM,KAAK,SAAS,CAAC;AAC9B,MAAI,KAAK,uBAAuB,GAAG,IAAI,YAAY,cAAc,CAAC,EAAE;AACpE,MAAI,KAAK,uBAAuB,GAAG,IAAI,YAAY,cAAc,CAAC,EAAE;AACpE,MAAI,KAAK,uBAAuB,GAAG,IAAI,YAAY,eAAe,CAAC,EAAE;AACrE,MAAI,KAAK,EAAE;AACX,MAAI,KAAKA,OAAM,KAAK,UAAU,CAAC;AAC/B,aAAW,KAAK,eAAe;AAC7B,QAAI;AAAA,MACF,KAAK,EAAE,MAAM,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,YAAY,UAAU,YAC3D,EAAE,YAAY,cAAc,eAC9B;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,EAAE;AACX,MAAI,KAAKA,OAAM,KAAK,QAAQ,CAAC;AAC7B,MAAI;AAAA,IACF,iBAAiB,GAAG,WAAWD,MAAK,UAAU,sBAAsB,CAAC,CAAC,CAAC;AAAA,EACzE;AACA,MAAI,KAAK,iBAAiB,GAAG,WAAWA,MAAK,UAAU,YAAY,CAAC,CAAC,CAAC,EAAE;AAC1E;AAEA,SAAS,GAAG,GAAoB;AAC9B,SAAO,IAAIC,OAAM,MAAM,KAAK,IAAIA,OAAM,IAAI,IAAI;AAChD;;;AlBhEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,gBAAgB,EACrB;AAAA,EACC;AACF,EACC,QAAQ,QAAW;AAEtB,QACG,QAAQ,OAAO,EACf,YAAY,iDAAiD,EAC7D,OAAO,YAAY;AAClB,QAAM,SAAS;AACjB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,YAAY;AAClB,QAAM,UAAU;AAClB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd;AAAA,EACC;AACF,EACC;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,iBAAiB,yCAAyC,EACjE,OAAO,oBAAoB,sBAAsB,EACjD,OAAO,cAAc,wCAAwC,EAC7D,OAAO,eAAe,yCAAyC,EAC/D,OAAO,cAAc,iCAAiC,EACtD,OAAO,WAAW,2BAA2B,EAC7C,OAAO,aAAa,uDAAkD,EACtE;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,OAAO,SAAS;AACtB,QAAM,QAAQ,IAAI;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,kDAAkD,EAC9D,OAAO,UAAU,4BAA4B,EAC7C,OAAO,OAAO,SAAS;AACtB,QAAM,UAAU,IAAI;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,+DAA+D,EAC3E,OAAO,UAAU,4BAA4B,EAC7C,OAAO,OAAO,SAAS;AACtB,QAAM,UAAU,IAAI;AACtB,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,uDAAuD,EACnE,OAAO,kBAAkB,iDAAiD,EAC1E,OAAO,WAAW,yCAAyC,EAC3D,OAAO,OAAO,SAAS;AACtB,QAAM,WAAW,IAAI;AACvB,CAAC;AAEH,QACG,QAAQ,kBAAkB,EAC1B,YAAY,qDAAqD,EACjE,OAAO,kBAAkB,+CAA+C,EACxE,OAAO,OAAO,WAAmB,SAA6B;AAC7D,QAAM,QAAQ,WAAW,IAAI;AAC/B,CAAC;AAEH,QACG,QAAQ,eAAe,EACvB,YAAY,0DAA0D,EACtE,OAAO,OAAO,SAA6B;AAC1C,QAAM,iBAAiB,EAAE,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC,EAAG,CAAC;AACpE,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAiB;AACvD,MAAI,KAAM,IAAc,WAAW,OAAO,GAAG,CAAC;AAC9C,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["createHash","readFile","writeFile","dirname","mkdir","unlink","mkdir","readFile","writeFile","homedir","dirname","join","mkdir","readFile","writeFile","homedir","dirname","join","HOOK_COMMAND_NAME","HOOK_MAP","asString","readJson","isAgentInsightsCommand","kleur","readFile","chmod","mkdir","readFile","writeFile","dirname","readFile","userHash","kleur","getValidToken","kleur","kleur","kleur","kleur","userInfo","join","kleur","join","kleur"]}
1
+ {"version":3,"sources":["../src/config/paths.ts","../src/auth/oauth.ts","../src/auth/store.ts","../src/cli.ts","../src/commands/cursor.ts","../src/config/config.ts","../src/config/analyzer.ts","../src/privacy/sanitize.ts","../src/util/identity.ts","../src/privacy/hash.ts","../src/util/log.ts","../src/commands/disable.ts","../src/adapters/claude-code.ts","../src/adapters/cursor.ts","../src/adapters/registry.ts","../src/commands/doctor.ts","../src/transcript/store.ts","../src/config/bypass.ts","../src/commands/hook.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/status.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Root directory for agent-insights state. Honors `AGENT_INSIGHTS_HOME`. */\nexport function configRoot(): string {\n return process.env.AGENT_INSIGHTS_HOME ?? join(homedir(), \".agent-insights\");\n}\n\nexport function configFile(): string {\n return join(configRoot(), \"config.json\");\n}\n\n/** API key storage (`chmod 600`). Not checked into git. */\nexport function credentialsFile(): string {\n return join(configRoot(), \"credentials.json\");\n}\n\n/** OAuth token storage (`chmod 600`). Not checked into git. */\nexport function authFile(): string {\n return join(configRoot(), \"auth.json\");\n}\n\n/** Vercel deployment-protection bypass secret (`chmod 600`). */\nexport function bypassSecretFile(): string {\n return join(configRoot(), \"bypass-secret\");\n}\n\nexport function sessionCacheDir(): string {\n return join(configRoot(), \"session-cache\");\n}\n\nexport function logsDir(): string {\n return join(configRoot(), \"logs\");\n}\n\nexport const CLAUDE_SETTINGS_FILE = \".claude/settings.json\";\nexport const CURSOR_HOOKS_FILE = \".cursor/hooks.json\";\n\n/** User-level Claude Code settings (hooks apply in every repo). */\nexport function claudeUserSettingsPath(): string {\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** User-level Cursor hooks (hooks apply in every project). */\nexport function cursorUserHooksPath(): string {\n return join(homedir(), \".cursor\", \"hooks.json\");\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport { createServer } from \"node:http\";\nimport { platform } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// OAuth app config — Sign-in-with-Vercel client ID (public, not a secret).\n// Override at runtime via AGENT_INSIGHTS_CLIENT_ID env var.\n// ---------------------------------------------------------------------------\nexport const CLIENT_ID =\n process.env.AGENT_INSIGHTS_CLIENT_ID ?? \"cl_TxqqNFTHcF9kEtM48LBFsYfwjhJ9T8sz\";\n\nconst AUTH_ENDPOINT = \"https://vercel.com/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://api.vercel.com/login/oauth/token\";\nconst USERINFO_ENDPOINT = \"https://api.vercel.com/login/oauth/userinfo\";\nconst REVOKE_ENDPOINT = \"https://api.vercel.com/login/oauth/token/revoke\";\n\n// Fixed port — must match the redirect URI registered in the OAuth app.\nconst CALLBACK_PORT = 9797;\nexport const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;\n\nconst SCOPES = \"openid email profile offline_access\";\n\nexport type TokenResponse = {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n id_token?: string;\n};\n\nexport type UserInfo = {\n sub: string;\n email: string;\n email_verified: boolean;\n name?: string;\n preferred_username?: string;\n picture?: string;\n};\n\n// ---------------------------------------------------------------------------\n// PKCE helpers\n// ---------------------------------------------------------------------------\n\nfunction generateCodeVerifier(): string {\n return randomBytes(48).toString(\"base64url\");\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction generateState(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Code + PKCE flow\n// ---------------------------------------------------------------------------\n\nexport type AuthCodeResult =\n | { status: \"success\"; code: string; state: string }\n | { status: \"error\"; message: string };\n\n/**\n * Starts a localhost callback server, opens the Vercel authorization URL in\n * the browser, and waits for the redirect with the auth code.\n * Resolves (or rejects after timeout) when Vercel redirects back.\n */\nexport async function startPkceFlow(timeoutMs = 120_000): Promise<{\n code: string;\n codeVerifier: string;\n}> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n const authUrl = new URL(AUTH_ENDPOINT);\n authUrl.searchParams.set(\"client_id\", CLIENT_ID);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n authUrl.searchParams.set(\"scope\", SCOPES);\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n\n const url = authUrl.toString();\n const codePromise = listenForCallback(state, timeoutMs);\n openBrowser(url);\n // Always print the URL — browser open may fail silently\n process.stderr.write(`\\nIf the browser did not open, visit:\\n ${url}\\n\\n`);\n\n const code = await codePromise;\n return { code, codeVerifier };\n}\n\nfunction listenForCallback(\n expectedState: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${CALLBACK_PORT}`);\n if (url.pathname !== \"/callback\") {\n res.writeHead(404, { connection: \"close\" });\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n const html = (msg: string) =>\n `<html><body style=\"font-family:sans-serif;text-align:center;padding:80px\">` +\n `<h2>${msg}</h2><p>You can close this tab.</p></body></html>`;\n\n // Tell the browser to drop the socket so the server can shut down cleanly\n // and Node's event loop can exit once the callback returns.\n const headers = { \"content-type\": \"text/html\", connection: \"close\" };\n\n const finish = (\n next: () => void,\n body: string,\n status: number,\n ): void => {\n res.writeHead(status, headers);\n res.end(body, () => {\n clearTimeout(timer);\n server.closeAllConnections?.();\n server.close();\n next();\n });\n };\n\n if (error) {\n finish(\n () => reject(new Error(`OAuth error: ${error}`)),\n html(`Login failed: ${error}`),\n 400,\n );\n return;\n }\n\n if (returnedState !== expectedState) {\n finish(\n () => reject(new Error(\"State mismatch — possible CSRF\")),\n html(\"Invalid state parameter — please try again.\"),\n 400,\n );\n return;\n }\n\n if (!code) {\n finish(\n () => reject(new Error(\"No code in callback\")),\n html(\"No authorization code received.\"),\n 400,\n );\n return;\n }\n\n finish(\n () => resolve(code),\n html(\"✓ Logged in! You can close this tab.\"),\n 200,\n );\n });\n\n const timer = setTimeout(() => {\n server.closeAllConnections?.();\n server.close();\n reject(new Error(\"Login timed out — no response within 2 minutes.\"));\n }, timeoutMs);\n timer.unref();\n\n server.listen(CALLBACK_PORT, \"127.0.0.1\", () => {\n // Server is ready; browser will be opened by caller\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n if (err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${CALLBACK_PORT} is already in use. Close any other agent-insights login process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Token exchange\n// ---------------------------------------------------------------------------\n\nexport async function exchangeCodeForToken(\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: CLIENT_ID,\n code,\n code_verifier: codeVerifier,\n redirect_uri: REDIRECT_URI,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n refreshToken: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: CLIENT_ID,\n refresh_token: refreshToken,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function revokeToken(token: string): Promise<void> {\n await fetch(REVOKE_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }),\n }).catch(() => {\n // best-effort — don't fail logout if revocation fails\n });\n}\n\nexport async function fetchUserInfo(accessToken: string): Promise<UserInfo> {\n const res = await fetch(USERINFO_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch user info (${res.status})`);\n }\n return res.json() as Promise<UserInfo>;\n}\n\n/** Open a URL in the system browser. Fails silently — callers always print the URL too. */\nexport function openBrowser(url: string): void {\n const os = platform();\n let cmd: string;\n if (os === \"darwin\") cmd = `open \"${url}\"`;\n else if (os === \"win32\") cmd = `start \"\" \"${url}\"`;\n else cmd = `xdg-open \"${url}\"`;\n\n // Detach + unref so the child doesn't keep the event loop alive after login.\n const child = exec(cmd);\n child.on(\"error\", () => {\n // Silently ignore — caller has already printed the URL\n });\n child.unref();\n}\n","import { chmod, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { authFile } from \"../config/paths.js\";\nimport { refreshAccessToken } from \"./oauth.js\";\n\nexport type AuthState = {\n accessToken: string;\n refreshToken?: string;\n /** Unix epoch milliseconds when the access token expires. */\n expiresAt: number;\n /** Captured at login for per-user analytics joins on the analyzer side. */\n email?: string;\n};\n\nexport async function loadEmail(): Promise<string | undefined> {\n return (await loadAuth())?.email;\n}\n\n/** How many ms before expiry we proactively refresh. */\nconst REFRESH_BUFFER_MS = 60_000;\n\nexport async function loadAuth(): Promise<AuthState | undefined> {\n try {\n const raw = await readFile(authFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (typeof parsed.accessToken === \"string\" && parsed.accessToken.length > 0) {\n return parsed as AuthState;\n }\n return undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveAuth(state: AuthState): Promise<void> {\n const path = authFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(state, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows doesn't support chmod — ignore\n }\n}\n\nexport async function clearAuth(): Promise<void> {\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(authFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Returns a valid access token, refreshing silently if needed.\n * Returns `undefined` if not logged in or refresh fails (caller should prompt login).\n */\nexport async function getValidToken(): Promise<string | undefined> {\n const auth = await loadAuth();\n if (!auth) return undefined;\n\n const needsRefresh = Date.now() >= auth.expiresAt - REFRESH_BUFFER_MS;\n if (!needsRefresh) return auth.accessToken;\n\n if (!auth.refreshToken) {\n // Access token expired, no refresh token — clear and force re-login\n await clearAuth();\n return undefined;\n }\n\n try {\n const tokens = await refreshAccessToken(auth.refreshToken);\n const refreshed: AuthState = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? auth.refreshToken,\n expiresAt: Date.now() + tokens.expires_in * 1000,\n ...(auth.email ? { email: auth.email } : {}),\n };\n await saveAuth(refreshed);\n return refreshed.accessToken;\n } catch {\n // Refresh failed (revoked, expired beyond 30 days) — clear and force re-login\n await clearAuth();\n return undefined;\n }\n}\n","import { Command } from \"commander\";\nimport { runCursorWrapper } from \"./commands/cursor.js\";\nimport { runDisable } from \"./commands/disable.js\";\nimport { runDoctor } from \"./commands/doctor.js\";\nimport { runHook } from \"./commands/hook.js\";\nimport { runInit } from \"./commands/init.js\";\nimport { runLogin, runLogout } from \"./commands/login.js\";\nimport { runStatus } from \"./commands/status.js\";\nimport { log } from \"./util/log.js\";\n\ndeclare const __VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name(\"agent-insights\")\n .description(\n \"Internal CLI for AI coding agent observability (Claude Code, Cursor).\",\n )\n .version(__VERSION__);\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with Vercel (Sign in with Vercel).\")\n .action(async () => {\n await runLogin();\n });\n\nprogram\n .command(\"logout\")\n .description(\"Clear stored credentials.\")\n .action(async () => {\n await runLogout();\n });\n\nprogram\n .command(\"init\")\n .description(\n \"Install agent hooks globally. Interactive by default; use --yes for non-interactive.\",\n )\n .option(\n \"-a, --agents <list>\",\n \"Comma-separated agent ids (claude-code,cursor); use '*' for all\",\n )\n .option(\"--transcripts\", \"Opt into transcript sync to Vercel Blob\")\n .option(\"--no-transcripts\", \"Skip transcript sync\")\n .option(\"--analysis\", \"Opt into SessionEnd analysis (Phase 2)\")\n .option(\"--telemetry\", \"Opt into OTLP event telemetry (Phase 3)\")\n .option(\"--no-hooks\", \"Skip installing per-agent hooks\")\n .option(\"--force\", \"Overwrite existing config\")\n .option(\"-y, --yes\", \"Non-interactive — accept defaults / passed flags\")\n .option(\n \"--bypass-secret <secret>\",\n \"Vercel deployment-protection bypass secret (use '' to clear)\",\n )\n .action(async (opts) => {\n await runInit(opts);\n });\n\nprogram\n .command(\"status\")\n .description(\"Show current config, consent, and adapter state.\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts) => {\n await runStatus(opts);\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Verify config, Vercel link, Blob, and per-agent hook install.\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts) => {\n await runDoctor(opts);\n });\n\nprogram\n .command(\"disable\")\n .description(\"Disable agent-insights and/or remove installed hooks.\")\n .option(\"--agent <name>\", \"Disable a specific adapter (claude-code|cursor)\")\n .option(\"--purge\", \"Also delete the local config and caches\")\n .action(async (opts) => {\n await runDisable(opts);\n });\n\nprogram\n .command(\"hook <eventName>\")\n .description(\"Internal: invoked by an agent's hook configuration.\")\n .option(\"--agent <name>\", \"Override agent inference (claude-code|cursor)\")\n .action(async (eventName: string, opts: { agent?: string }) => {\n await runHook(eventName, opts);\n });\n\nprogram\n .command(\"cursor [path]\")\n .description(\"Launch Cursor with session start/end emitted (fallback).\")\n .action(async (path: string | undefined) => {\n await runCursorWrapper({ ...(path !== undefined ? { path } : {}) });\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n log.fail((err as Error).message ?? String(err));\n process.exit(1);\n});\n","import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { loadConfig } from \"../config/config.js\";\nimport { sanitize } from \"../privacy/sanitize.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport { repoHash, userHash, workspaceHash } from \"../util/identity.js\";\nimport { debug, log } from \"../util/log.js\";\n\nexport type CursorOptions = {\n path?: string;\n};\n\n/**\n * Wrapper for launching Cursor when native hooks aren't installed yet.\n * Emits session.start / session.end without prompts or tool inputs.\n */\nexport async function runCursorWrapper(opts: CursorOptions): Promise<void> {\n const cfg = await loadConfig();\n if (!cfg) {\n log.fail(\"agent-insights is not initialized — run `agent-insights init`\");\n process.exitCode = 1;\n return;\n }\n const path = opts.path ?? process.cwd();\n const sessionId = randomUUID();\n\n const baseEvent = {\n agent: \"cursor\" as const,\n sessionId,\n userHash: userHash(),\n repoHash: repoHash(),\n workspaceHash: workspaceHash(),\n };\n\n emit({\n ...baseEvent,\n type: \"session.start\",\n timestamp: new Date().toISOString(),\n });\n\n const code = await spawnCursor(path);\n\n emit({\n ...baseEvent,\n type: \"session.end\",\n timestamp: new Date().toISOString(),\n outcome: code === 0 ? \"success\" : \"error\",\n });\n\n process.exitCode = code;\n}\n\nfunction spawnCursor(path: string): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(\"cursor\", [path], {\n stdio: \"inherit\",\n env: process.env,\n });\n child.on(\"error\", (err) => {\n log.fail(`failed to launch cursor: ${err.message}`);\n resolve(127);\n });\n child.on(\"exit\", (code) => resolve(code ?? 0));\n });\n}\n\nfunction emit(event: AgentEvent): void {\n debug(\"cursor wrapper event\", {\n type: event.type,\n sessionId: event.sessionId,\n });\n // Phase 3 (OTLP) will export sanitized events here.\n void sanitize(event);\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { getAnalyzerUrl } from \"./analyzer.js\";\nimport { configFile, logsDir, sessionCacheDir } from \"./paths.js\";\n\nexport type ExporterConfig = {\n type: \"otlp\";\n endpoint: string;\n headers: Record<string, string>;\n dataset: string;\n};\n\nexport type HooksScope = \"global\" | \"local\";\n\nexport type TranscriptStoreConfig =\n | {\n /** Upload via analyzer API using AGENT_INSIGHTS_API_KEY (preferred). */\n type: \"analyzer\";\n }\n | {\n /** Direct upload with a Blob RW token (legacy / advanced). */\n type: \"blob\";\n tokenEnv: string;\n prefix: string;\n }\n | {\n type: \"none\";\n };\n\nexport type SessionAnalysisConfig = {\n /** Server-side enablement; user opts in via `userConsent.sessionAnalysis`. */\n enabled: boolean;\n /** Analyzer endpoint base URL. CLI POSTs to `${analyzerUrl}/api/sessions`. */\n analyzerUrl: string;\n /** GitHub repo for pre-filled new-issue URLs (display only; constructed by analyzer). */\n githubIssueRepo: string;\n};\n\nexport type AgentConfig = {\n enabled: boolean;\n};\n\nexport type AgentInsightsConfig = {\n version: 1;\n enabled: boolean;\n /** Where agent hook entries are written. Default `global` (user home). */\n hooksScope: HooksScope;\n vercel: {\n team: string;\n project: string;\n };\n userConsent: {\n eventTelemetry: boolean;\n transcriptSync: boolean;\n sessionAnalysis: boolean;\n };\n exporter: ExporterConfig;\n transcriptStore: TranscriptStoreConfig;\n sessionAnalysis: SessionAnalysisConfig;\n agents: Record<\"claudeCode\" | \"cursor\", AgentConfig>;\n privacy: {\n capturePrompts: false;\n captureMessages: false;\n captureToolInputs: false;\n captureFileContents: false;\n };\n};\n\nexport function defaultConfig(): AgentInsightsConfig {\n return {\n version: 1,\n enabled: true,\n hooksScope: \"global\",\n vercel: {\n team: \"vercel-labs\",\n project: \"agent-insights\",\n },\n userConsent: {\n eventTelemetry: false,\n transcriptSync: false,\n sessionAnalysis: false,\n },\n exporter: {\n type: \"otlp\",\n endpoint: process.env.AGENT_INSIGHTS_OTEL_ENDPOINT ?? \"\",\n headers: {},\n dataset: \"agent-insights\",\n },\n transcriptStore: {\n // Upload via the analyzer using the SiwV bearer token — no client-side\n // Blob token required. Set `transcriptStore.type` to \"blob\" in\n // config.json to switch to direct uploads with BLOB_READ_WRITE_TOKEN.\n type: \"analyzer\",\n },\n sessionAnalysis: {\n enabled: false,\n analyzerUrl: getAnalyzerUrl(),\n githubIssueRepo: \"vercel-labs/agent-insights\",\n },\n agents: {\n claudeCode: { enabled: false },\n cursor: { enabled: false },\n },\n privacy: {\n capturePrompts: false,\n captureMessages: false,\n captureToolInputs: false,\n captureFileContents: false,\n },\n };\n}\n\nexport async function loadConfig(): Promise<AgentInsightsConfig | undefined> {\n try {\n const raw = await readFile(configFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AgentInsightsConfig>;\n return mergeConfig(defaultConfig(), parsed);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveConfig(cfg: AgentInsightsConfig): Promise<void> {\n await ensureDirs();\n await writeFile(configFile(), `${JSON.stringify(cfg, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function ensureDirs(): Promise<void> {\n await mkdir(dirname(configFile()), { recursive: true });\n await mkdir(sessionCacheDir(), { recursive: true });\n await mkdir(logsDir(), { recursive: true });\n}\n\nfunction mergeConfig(\n base: AgentInsightsConfig,\n patch: Partial<AgentInsightsConfig>,\n): AgentInsightsConfig {\n return {\n ...base,\n ...patch,\n vercel: { ...base.vercel, ...(patch.vercel ?? {}) },\n userConsent: { ...base.userConsent, ...(patch.userConsent ?? {}) },\n exporter: { ...base.exporter, ...(patch.exporter ?? {}) },\n transcriptStore: (patch.transcriptStore ??\n base.transcriptStore) as TranscriptStoreConfig,\n sessionAnalysis: {\n ...base.sessionAnalysis,\n ...(patch.sessionAnalysis ?? {}),\n },\n agents: {\n claudeCode: {\n ...base.agents.claudeCode,\n ...(patch.agents?.claudeCode ?? {}),\n },\n cursor: { ...base.agents.cursor, ...(patch.agents?.cursor ?? {}) },\n },\n privacy: base.privacy,\n };\n}\n","/**\n * Production analyzer deployment. Override via AGENT_INSIGHTS_ANALYZER_URL\n * for local/dev/staging analyzers.\n */\nexport const DEFAULT_ANALYZER_URL = \"https://agent-insights.labs.vercel.dev\";\n\n/** Resolve the analyzer base URL: env var if set, otherwise the prod default. */\nexport function getAnalyzerUrl(): string {\n return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;\n}\n","/**\n * Keys that must never appear in any payload that leaves the machine.\n *\n * Matching is case-insensitive and snake_case/camelCase agnostic — we strip a\n * key if its normalized form equals one of these values.\n */\nconst FORBIDDEN_KEYS: ReadonlySet<string> = new Set([\n \"prompt\",\n \"prompts\",\n \"message\",\n \"messages\",\n \"content\",\n \"text\",\n \"transcript\",\n \"toolinput\",\n \"input\",\n \"arguments\",\n \"args\",\n \"params\",\n \"file\",\n \"filecontent\",\n \"filecontents\",\n \"diff\",\n \"patch\",\n \"secret\",\n \"secrets\",\n \"token\",\n \"apikey\",\n \"authorization\",\n \"password\",\n \"credentials\",\n]);\n\nconst MAX_STRING_LENGTH = 256;\n\n/** Allowed primitive value types in sanitized metadata. */\nexport type SanitizedPrimitive = string | number | boolean | null;\n\nfunction normalizeKey(key: string): string {\n return key.toLowerCase().replace(/[_\\-\\s]/g, \"\");\n}\n\nfunction truncate(value: string): string {\n if (value.length <= MAX_STRING_LENGTH) return value;\n return `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]`;\n}\n\nfunction sanitizePrimitive(value: unknown): SanitizedPrimitive | undefined {\n if (value === null) return null;\n switch (typeof value) {\n case \"string\":\n return truncate(value);\n case \"number\":\n return Number.isFinite(value) ? value : null;\n case \"boolean\":\n return value;\n default:\n return undefined;\n }\n}\n\n/**\n * Recursively remove forbidden keys from an arbitrary payload.\n *\n * Returned shape:\n * - Objects keep allowed primitive fields (truncated to {@link MAX_STRING_LENGTH}).\n * - Arrays keep allowed primitive elements; nested objects/arrays are recursed.\n * - Non-primitive, non-container values (functions, symbols, Buffers, …) are dropped.\n */\nexport function sanitize(input: unknown): unknown {\n return sanitizeInner(input);\n}\n\nfunction sanitizeInner(value: unknown): unknown {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const item of value) {\n const sanitizedItem = sanitizeInner(item);\n if (sanitizedItem !== undefined) {\n out.push(sanitizedItem);\n }\n }\n return out;\n }\n\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [rawKey, rawValue] of Object.entries(value)) {\n if (FORBIDDEN_KEYS.has(normalizeKey(rawKey))) {\n continue;\n }\n if (rawValue && typeof rawValue === \"object\") {\n out[rawKey] = sanitizeInner(rawValue);\n } else {\n const primitive = sanitizePrimitive(rawValue);\n if (primitive !== undefined) {\n out[rawKey] = primitive;\n }\n }\n }\n return out;\n }\n\n return sanitizePrimitive(value);\n}\n\nexport const __testing = { FORBIDDEN_KEYS, normalizeKey };\n","import { execSync } from \"node:child_process\";\nimport { hostname, userInfo } from \"node:os\";\nimport { sha256 } from \"../privacy/hash.js\";\n\n/** Stable hashed identity of the local user. */\nexport function userHash(): string {\n try {\n const user = userInfo().username;\n return sha256(`${user}@${hostname()}`);\n } catch {\n return sha256(\"unknown\");\n }\n}\n\n/**\n * Stable hashed identity of a repository, derived from `git remote get-url\n * origin` when available, else from `cwd`.\n */\nexport function repoHash(cwd: string = process.cwd()): string {\n const remote = tryGit([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n return sha256(remote ?? cwd);\n}\n\n/** Stable hashed identity of the workspace path. */\nexport function workspaceHash(cwd: string = process.cwd()): string {\n return sha256(cwd);\n}\n\nfunction tryGit(args: string[], cwd: string): string | undefined {\n try {\n const out = execSync(`git ${args.join(\" \")}`, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n timeout: 1500,\n });\n const trimmed = out.trim();\n return trimmed === \"\" ? undefined : trimmed;\n } catch {\n return undefined;\n }\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable SHA-256 hash of a value, hex-encoded.\n *\n * Used to anonymize usernames, repo remote URLs, workspace paths, and\n * session identifiers before they leave the machine.\n */\nexport function sha256(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Short prefix of `sha256(value)` for human-readable dashboards. */\nexport function shortHash(value: string, length = 12): string {\n return sha256(value).slice(0, length);\n}\n","import kleur from \"kleur\";\n\nexport const log = {\n info: (msg: string): void => {\n process.stdout.write(`${msg}\\n`);\n },\n ok: (msg: string): void => {\n process.stdout.write(`${kleur.green(\"✓\")} ${msg}\\n`);\n },\n warn: (msg: string): void => {\n process.stdout.write(`${kleur.yellow(\"!\")} ${msg}\\n`);\n },\n fail: (msg: string): void => {\n process.stderr.write(`${kleur.red(\"✗\")} ${msg}\\n`);\n },\n hint: (msg: string): void => {\n process.stdout.write(`${kleur.dim(msg)}\\n`);\n },\n json: (obj: unknown): void => {\n process.stdout.write(`${JSON.stringify(obj, null, 2)}\\n`);\n },\n};\n\nexport function debug(msg: string, extra?: Record<string, unknown>): void {\n if (process.env.AGENT_INSIGHTS_DEBUG) {\n const payload = extra ? ` ${JSON.stringify(extra)}` : \"\";\n process.stderr.write(`[agent-insights] ${msg}${payload}\\n`);\n }\n}\n","import { rm } from \"node:fs/promises\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig, saveConfig } from \"../config/config.js\";\nimport { configRoot } from \"../config/paths.js\";\nimport { log } from \"../util/log.js\";\nimport type { AgentId } from \"../adapters/types.js\";\n\nexport type DisableOptions = {\n agent?: string;\n purge?: boolean;\n};\n\nexport async function runDisable(opts: DisableOptions): Promise<void> {\n const repoRoot = process.cwd();\n const targets = opts.agent ? [opts.agent] : adapterList.map((a) => a.id);\n\n for (const id of targets) {\n if (id !== \"claude-code\" && id !== \"cursor\") {\n log.warn(`Unknown agent id \"${id}\"`);\n continue;\n }\n const adapter = adapterList.find((a) => a.id === (id as AgentId));\n if (!adapter) continue;\n const { removed, path } = await adapter.uninstall(repoRoot);\n if (removed) log.ok(`Removed ${adapter.label} hooks from ${path}`);\n else log.hint(`${adapter.label}: no hooks to remove (${path})`);\n }\n\n const cfg = await loadConfig();\n if (cfg) {\n if (!opts.agent || opts.agent === \"claude-code\") {\n cfg.agents.claudeCode.enabled = false;\n }\n if (!opts.agent || opts.agent === \"cursor\") {\n cfg.agents.cursor.enabled = false;\n }\n if (!opts.agent) cfg.enabled = false;\n await saveConfig(cfg);\n }\n\n if (opts.purge) {\n await rm(configRoot(), { recursive: true, force: true });\n log.ok(`Purged ${configRoot()}`);\n }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\nconst HOOK_MAP: Record<string, AgentEventType> = {\n SessionStart: \"session.start\",\n SessionEnd: \"session.end\",\n UserPromptSubmit: \"user.prompt.submit\",\n PreToolUse: \"tool.start\",\n PostToolUse: \"tool.end\",\n PostToolUseFailure: \"tool.failure\",\n PermissionRequest: \"permission.request\",\n PermissionDenied: \"permission.denied\",\n SubagentStart: \"subagent.start\",\n SubagentStop: \"subagent.end\",\n Stop: \"agent.stop\",\n Notification: \"notification\",\n PreCompact: \"context.compact.start\",\n PostCompact: \"context.compact.end\",\n};\n\nexport const CLAUDE_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype ClaudeHookCommand = {\n type: \"command\";\n command: string;\n};\n\n/** Claude Code 0.12+ matcher group (see code.claude.com/docs/en/hooks). */\ntype ClaudeHookMatcherGroup = {\n matcher: string;\n hooks: ClaudeHookCommand[];\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, unknown[]>;\n [key: string]: unknown;\n};\n\nexport const claudeCodeAdapter: Adapter = {\n id: \"claude-code\",\n agent: \"claude-code\",\n label: \"Claude Code\",\n settingsFile: \".claude/settings.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n const sessionId =\n asString(data[\"session_id\"]) ?? asString(data[\"sessionId\"]);\n const toolName =\n asString(data[\"tool_name\"]) ??\n asString(data[\"toolName\"]) ??\n asString((data[\"tool\"] as Record<string, unknown> | undefined)?.[\"name\"]);\n const transcriptPath =\n asString(data[\"transcript_path\"]) ?? asString(data[\"transcriptPath\"]);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n const next: ClaudeSettings = { ...(settings ?? {}) };\n const hooks: Record<string, ClaudeHookMatcherGroup[]> = {};\n\n for (const [event, raw] of Object.entries(next.hooks ?? {})) {\n hooks[event] = normalizeEventHooks(raw);\n }\n\n for (const event of CLAUDE_HOOK_EVENTS) {\n const withoutInsights = removeAgentInsightsMatchers(\n hooks[event] ?? [],\n );\n hooks[event] = addAgentInsightsHook(withoutInsights, event);\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks: Record<string, ClaudeHookMatcherGroup[]> = {};\n let touched = false;\n\n for (const [event, raw] of Object.entries(settings.hooks)) {\n const before = normalizeEventHooks(raw);\n const after = removeAgentInsightsMatchers(before);\n if (after.length !== before.length) touched = true;\n else if (JSON.stringify(after) !== JSON.stringify(before)) {\n touched = true;\n }\n if (after.length > 0) hooks[event] = after;\n }\n\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<ClaudeSettings>(\n resolveClaudePath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((raw) =>\n normalizeEventHooks(raw).some((group) =>\n group.hooks.some((h) => isAgentInsightsCommand(h.command)),\n ),\n );\n },\n};\n\nfunction resolveClaudePath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(repoRoot, \".claude\", \"settings.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nfunction isHookCommand(value: unknown): value is ClaudeHookCommand {\n if (!value || typeof value !== \"object\") return false;\n const entry = value as Record<string, unknown>;\n return entry.type === \"command\" && typeof entry.command === \"string\";\n}\n\n/** Accept matcher groups and migrate legacy flat `{ type, command }` entries. */\nfunction normalizeEventHooks(raw: unknown[]): ClaudeHookMatcherGroup[] {\n const groups: ClaudeHookMatcherGroup[] = [];\n\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const record = entry as Record<string, unknown>;\n\n if (Array.isArray(record.hooks)) {\n const commands = record.hooks.filter(isHookCommand);\n if (commands.length === 0) continue;\n groups.push({\n matcher: typeof record.matcher === \"string\" ? record.matcher : \"\",\n hooks: commands,\n });\n continue;\n }\n\n if (isHookCommand(record)) {\n groups.push({\n matcher: \"\",\n hooks: [record],\n });\n }\n }\n\n return groups;\n}\n\nfunction removeAgentInsightsMatchers(\n matchers: ClaudeHookMatcherGroup[],\n): ClaudeHookMatcherGroup[] {\n const result: ClaudeHookMatcherGroup[] = [];\n\n for (const group of matchers) {\n const hooks = group.hooks.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (hooks.length > 0) {\n result.push({ ...group, hooks });\n }\n }\n\n return result;\n}\n\nfunction addAgentInsightsHook(\n matchers: ClaudeHookMatcherGroup[],\n event: string,\n): ClaudeHookMatcherGroup[] {\n const command = `${HOOK_COMMAND_NAME} hook ${event}`;\n const entry: ClaudeHookCommand = { type: \"command\", command };\n\n for (const group of matchers) {\n if (group.matcher === \"\") {\n if (!group.hooks.some((h) => h.command === command)) {\n group.hooks.push(entry);\n }\n return matchers;\n }\n }\n\n return [...matchers, { matcher: \"\", hooks: [entry] }];\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\n/**\n * Cursor native hook names (camelCase) → common schema. Reference:\n * https://cursor.com/docs/hooks\n */\nconst HOOK_MAP: Record<string, AgentEventType> = {\n sessionStart: \"session.start\",\n sessionEnd: \"session.end\",\n beforeSubmitPrompt: \"user.prompt.submit\",\n preToolUse: \"tool.start\",\n postToolUse: \"tool.end\",\n postToolUseFailure: \"tool.failure\",\n subagentStart: \"subagent.start\",\n subagentStop: \"subagent.end\",\n stop: \"agent.stop\",\n preCompact: \"context.compact.start\",\n // Additional events — mapped to existing schema types\n beforeShellExecution: \"tool.start\",\n afterShellExecution: \"tool.end\",\n afterFileEdit: \"tool.end\",\n workspaceOpen: \"session.start\",\n};\n\nexport const CURSOR_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype CursorHookEntry = {\n command: string;\n};\n\ntype CursorHooksConfig = {\n /** Cursor expects schema version 1 at the top level. */\n version?: number;\n hooks?: Record<string, CursorHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const cursorAdapter: Adapter = {\n id: \"cursor\",\n agent: \"cursor\",\n label: \"Cursor\",\n settingsFile: \".cursor/hooks.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n // Cursor uses `session_id` in payloads and is identical to `conversation_id`.\n const sessionId =\n asString(data[\"session_id\"]) ??\n asString(data[\"sessionId\"]) ??\n asString(data[\"conversation_id\"]);\n const toolName = asString(data[\"tool_name\"]) ?? asString(data[\"toolName\"]);\n\n // transcript_path is confirmed in all Cursor hook payloads.\n const transcriptPath =\n asString(data[\"transcript_path\"]) ??\n asString(data[\"transcriptPath\"]) ??\n asString(process.env.CURSOR_TRANSCRIPT_PATH);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n const next: CursorHooksConfig = {\n version: 1,\n ...(settings ?? {}),\n };\n const hooks: Record<string, CursorHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CURSOR_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({ command: `${HOOK_COMMAND_NAME} hook ${event}` });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<CursorHooksConfig>(\n resolveCursorPath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveCursorPath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".cursor\", \"hooks.json\")\n : join(repoRoot, \".cursor\", \"hooks.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { claudeCodeAdapter } from \"./claude-code.js\";\nimport { cursorAdapter } from \"./cursor.js\";\nimport type { Adapter, AgentId } from \"./types.js\";\n\nexport const adapters: Record<AgentId, Adapter> = {\n \"claude-code\": claudeCodeAdapter,\n cursor: cursorAdapter,\n};\n\nexport const adapterList: ReadonlyArray<Adapter> = Object.values(adapters);\n\nexport function getAdapter(id: AgentId): Adapter {\n return adapters[id];\n}\n","import kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { createTranscriptStore } from \"../transcript/store.js\";\nimport { log } from \"../util/log.js\";\n\nexport type DoctorOptions = {\n json?: boolean;\n};\n\ntype Check = {\n name: string;\n ok: boolean;\n detail?: string;\n};\n\nexport async function runDoctor(opts: DoctorOptions): Promise<void> {\n const repoRoot = process.cwd();\n const checks: Check[] = [];\n\n const token = await getValidToken();\n checks.push({\n name: \"logged in\",\n ok: token != null,\n ...(token ? {} : { detail: \"run `agent-insights login`\" }),\n });\n\n const cfg = await loadConfig();\n checks.push({\n name: \"config found\",\n ok: cfg != null,\n detail: cfg ? configFile() : `missing ${configFile()}`,\n });\n\n if (cfg) {\n if (cfg.userConsent.transcriptSync) {\n try {\n const store = createTranscriptStore(cfg.transcriptStore);\n const ping = await store.ping();\n if (ping.ok) {\n checks.push({ name: \"transcript store reachable\", ok: true });\n } else {\n checks.push({\n name: \"transcript store reachable\",\n ok: false,\n detail: ping.reason,\n });\n }\n } catch (err) {\n checks.push({\n name: \"transcript store reachable\",\n ok: false,\n detail: (err as Error).message,\n });\n }\n }\n\n for (const adapter of adapterList) {\n const enabled =\n adapter.id === \"claude-code\"\n ? cfg.agents.claudeCode.enabled\n : cfg.agents.cursor.enabled;\n if (!enabled) continue;\n const installed = await adapter.isInstalled(repoRoot, cfg.hooksScope);\n checks.push({\n name: `${adapter.label} hooks installed`,\n ok: installed,\n ...(installed ? {} : { detail: \"run `agent-insights init`\" }),\n });\n }\n }\n\n if (opts.json) {\n log.json({ ok: checks.every((c) => c.ok), checks });\n } else {\n for (const c of checks) {\n const tag = c.ok ? kleur.green(\"✓\") : kleur.red(\"✗\");\n const detail = c.detail ? kleur.dim(` (${c.detail})`) : \"\";\n log.info(`${tag} ${c.name}${detail}`);\n }\n }\n\n process.exitCode = checks.every((c) => c.ok) ? 0 : 1;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { put } from \"@vercel/blob\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport type { TranscriptStoreConfig } from \"../config/config.js\";\n\nexport type TranscriptUploadInput = {\n /** Absolute path on disk to the transcript file. */\n transcriptPath: string;\n /** SHA256-hashed user identifier. Used in the upload pathname. */\n userHash: string;\n /** Session identifier supplied by the agent. */\n sessionId: string;\n /** Agent name (e.g. \"claude-code\"). */\n agent: string;\n /**\n * Vercel email captured at `agent-insights login`. Lets the analyzer\n * join sessions to real user accounts; never logged or written into\n * the transcript itself.\n */\n userEmail?: string;\n};\n\nexport type TranscriptUploadResult = {\n pathname: string;\n url: string;\n size: number;\n};\n\nexport interface TranscriptStore {\n upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult>;\n /** Lightweight health check used by `doctor`. */\n ping(): Promise<{ ok: true } | { ok: false; reason: string }>;\n}\n\nclass NoopTranscriptStore implements TranscriptStore {\n async upload(): Promise<TranscriptUploadResult> {\n throw new Error(\"transcript store disabled (transcriptStore.type=none)\");\n }\n async ping(): Promise<{ ok: false; reason: string }> {\n return { ok: false, reason: \"transcript store disabled\" };\n }\n}\n\nclass BlobTranscriptStore implements TranscriptStore {\n constructor(\n private readonly token: string,\n private readonly prefix: string,\n ) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const body = await readFile(input.transcriptPath);\n const requestedPath = buildPathname(\n this.prefix,\n input.userHash,\n input.sessionId,\n );\n const blob = await put(requestedPath, body, {\n access: \"private\",\n token: this.token,\n contentType: \"application/jsonl\",\n addRandomSuffix: true,\n });\n return { pathname: blob.pathname, url: blob.url, size: body.byteLength };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n if (!this.token) return { ok: false, reason: \"missing token\" };\n return { ok: true };\n }\n}\n\n/**\n * Uploads transcripts via the analyzer's POST /api/sessions/upload endpoint\n * using the Sign-in-with-Vercel bearer token. The analyzer then proxies the\n * write to Vercel Blob with its own server-side credentials, so this client\n * does not need a Blob read/write token.\n */\nclass AnalyzerTranscriptStore implements TranscriptStore {\n constructor(private readonly analyzerUrl: string) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const token = await getValidToken();\n if (!token) {\n throw new Error(\n \"transcript store: not logged in — run `agent-insights login`\",\n );\n }\n const body = await readFile(input.transcriptPath);\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/jsonl\",\n \"x-session-id\": input.sessionId,\n \"x-agent\": input.agent,\n \"x-user-hash\": input.userHash,\n ...(input.userEmail ? { \"x-user-email\": input.userEmail } : {}),\n });\n const res = await fetch(url, { method: \"POST\", headers, body });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `transcript upload failed (${res.status}): ${text || res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n ok: boolean;\n blobUrl?: string;\n pathname?: string;\n size?: number;\n error?: string;\n };\n if (!json.ok || !json.blobUrl || !json.pathname) {\n throw new Error(`transcript upload error: ${json.error ?? \"unknown\"}`);\n }\n return {\n pathname: json.pathname,\n url: json.blobUrl,\n size: json.size ?? body.byteLength,\n };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n const token = await getValidToken();\n if (!token) {\n return {\n ok: false,\n reason: \"not logged in — run `agent-insights login`\",\n };\n }\n // Try the actual upload endpoint with an intentionally bad request — a\n // healthy analyzer responds 400 (missing headers); a protection wall\n // responds 401/403 with HTML; an unreachable host throws.\n try {\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n });\n const res = await fetch(url, { method: \"POST\", headers });\n if (res.status === 400) return { ok: true };\n if (res.status === 401 || res.status === 403) {\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n return {\n ok: false,\n reason:\n \"blocked by Vercel deployment protection — set a bypass secret via `agent-insights init --bypass-secret <secret>`\",\n };\n }\n return { ok: false, reason: `analyzer rejected token (${res.status})` };\n }\n return { ok: false, reason: `unexpected status ${res.status}` };\n } catch (err) {\n return {\n ok: false,\n reason: `analyzer unreachable: ${(err as Error).message}`,\n };\n }\n }\n}\n\nexport function createTranscriptStore(\n cfg: TranscriptStoreConfig,\n): TranscriptStore {\n if (cfg.type === \"none\") return new NoopTranscriptStore();\n if (cfg.type === \"analyzer\") {\n return new AnalyzerTranscriptStore(getAnalyzerUrl());\n }\n // type === \"blob\"\n const token = process.env[cfg.tokenEnv];\n if (!token) {\n throw new Error(\n `transcript store: env ${cfg.tokenEnv} is not set — run \\`agent-insights init --force\\` to switch to analyzer-proxied uploads (no token required), or \\`vercel env pull .env.local\\` to keep direct Blob uploads`,\n );\n }\n return new BlobTranscriptStore(token, cfg.prefix);\n}\n\nfunction buildPathname(\n prefix: string,\n userHash: string,\n sessionId: string,\n): string {\n const safePrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n const safeSession = encodeURIComponent(sessionId);\n return `${safePrefix}${userHash}/${safeSession}/transcript.jsonl`;\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { bypassSecretFile } from \"./paths.js\";\n\n/**\n * Resolve the Vercel deployment-protection bypass secret.\n * Precedence:\n * 1. `AGENT_INSIGHTS_BYPASS_SECRET` (explicit override)\n * 2. `VERCEL_AUTOMATION_BYPASS_SECRET` (Vercel's standard env)\n * 3. `~/.agent-insights/bypass-secret` (set via `agent-insights init`)\n */\nexport async function loadBypassSecret(): Promise<string | undefined> {\n const fromEnv =\n process.env.AGENT_INSIGHTS_BYPASS_SECRET ||\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET;\n if (fromEnv) return fromEnv;\n\n try {\n const raw = (await readFile(bypassSecretFile(), \"utf8\")).trim();\n return raw || undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveBypassSecret(secret: string): Promise<void> {\n const path = bypassSecretFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, secret.trim(), { encoding: \"utf8\", mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows — ignore.\n }\n}\n\nexport async function clearBypassSecret(): Promise<void> {\n try {\n await unlink(bypassSecretFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Add the `x-vercel-protection-bypass` header onto an existing headers\n * object if a secret is configured. Mutates and returns the input.\n */\nexport async function withBypassHeader<T extends Record<string, string>>(\n headers: T,\n): Promise<T> {\n const secret = await loadBypassSecret();\n if (secret) {\n (headers as Record<string, string>)[\"x-vercel-protection-bypass\"] = secret;\n }\n return headers;\n}\n","import { adapterList } from \"../adapters/registry.js\";\nimport { loadEmail } from \"../auth/store.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { sanitize } from \"../privacy/sanitize.js\";\nimport { createTranscriptStore } from \"../transcript/store.js\";\nimport type { Adapter, AgentId, HookPayload } from \"../adapters/types.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport { repoHash, userHash, workspaceHash } from \"../util/identity.js\";\nimport { debug, log } from \"../util/log.js\";\n\nexport type HookOptions = {\n agent?: string;\n};\n\n/**\n * Hook entrypoint. Reads JSON payload from stdin, resolves the adapter, maps\n * the native event into the common schema, and runs transcript upload on\n * session.end. Never fails the hook — errors are logged, exit code is 0.\n */\nexport async function runHook(\n eventName: string,\n opts: HookOptions,\n): Promise<void> {\n const cfg = await loadConfig();\n if (!cfg || !cfg.enabled) {\n debug(\"hook skipped — agent-insights not enabled\");\n return;\n }\n\n const stdin = await readStdinSafely();\n const data = parseJsonObject(stdin);\n const payload: HookPayload = { event: eventName, data };\n\n const adapter = pickAdapter(opts.agent, eventName);\n if (!adapter) {\n debug(`no adapter resolved for event ${eventName}`);\n return;\n }\n\n const mapped = adapter.map(payload);\n if (!mapped) {\n debug(`adapter ${adapter.id} did not map event ${eventName}`);\n return;\n }\n\n const event = buildEvent(adapter, mapped);\n debug(\"hook event\", {\n agent: event.agent,\n type: event.type,\n sessionId: event.sessionId,\n });\n\n if (\n mapped.type === \"session.end\" &&\n cfg.userConsent.transcriptSync &&\n mapped.transcriptPath\n ) {\n try {\n const store = createTranscriptStore(cfg.transcriptStore);\n const userEmail = await loadEmail();\n const result = await store.upload({\n transcriptPath: mapped.transcriptPath,\n userHash: event.userHash ?? userHash(),\n sessionId: mapped.sessionId ?? \"unknown\",\n agent: adapter.id,\n ...(userEmail ? { userEmail } : {}),\n });\n debug(\"transcript uploaded\", {\n size: result.size,\n pathname: result.pathname,\n });\n\n if (cfg.userConsent.sessionAnalysis) {\n await notifyAnalyzer({\n analyzerUrl: getAnalyzerUrl(),\n payload: {\n sessionId: mapped.sessionId ?? \"unknown\",\n agent: adapter.id,\n userHash: event.userHash ?? userHash(),\n blobUrl: result.url,\n timestamp: event.timestamp,\n },\n });\n }\n } catch (err) {\n log.fail(`transcript upload failed: ${(err as Error).message}`);\n }\n }\n\n // Phase 3 (OTLP) will export `event` here.\n void sanitize(event);\n}\n\ntype AnalyzerPayload = {\n sessionId: string;\n agent: string;\n userHash: string;\n blobUrl: string;\n timestamp: string;\n};\n\nasync function notifyAnalyzer(opts: {\n analyzerUrl: string;\n payload: AnalyzerPayload;\n}): Promise<void> {\n try {\n const { getValidToken } = await import(\"../auth/store.js\");\n const token = await getValidToken();\n const baseHeaders: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (token) baseHeaders[\"authorization\"] = `Bearer ${token}`;\n const headers = await withBypassHeader(baseHeaders);\n\n const url = new URL(\"/api/sessions\", opts.analyzerUrl).toString();\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(opts.payload),\n });\n if (!res.ok) {\n debug(`analyzer notify failed: ${res.status} ${res.statusText}`);\n return;\n }\n debug(\"analyzer notified\", { sessionId: opts.payload.sessionId });\n } catch (err) {\n // Never fail the hook because of analyzer issues.\n debug(`analyzer notify error: ${(err as Error).message}`);\n }\n}\n\nfunction pickAdapter(\n override: string | undefined,\n eventName: string,\n): Adapter | undefined {\n if (override) {\n return adapterList.find((a) => a.id === override);\n }\n for (const a of adapterList) {\n if (a.map({ event: eventName })) return a;\n }\n return undefined;\n}\n\nfunction buildEvent(\n adapter: { id: AgentId; agent: AgentEvent[\"agent\"] },\n mapped: NonNullable<ReturnType<Adapter[\"map\"]>>,\n): AgentEvent {\n return {\n type: mapped.type,\n agent: adapter.agent,\n timestamp: new Date().toISOString(),\n ...(mapped.sessionId !== undefined ? { sessionId: mapped.sessionId } : {}),\n ...(mapped.toolName !== undefined ? { toolName: mapped.toolName } : {}),\n ...(mapped.permissionType !== undefined\n ? { permissionType: mapped.permissionType }\n : {}),\n ...(mapped.outcome !== undefined ? { outcome: mapped.outcome } : {}),\n userHash: userHash(),\n repoHash: repoHash(),\n workspaceHash: workspaceHash(),\n };\n}\n\n/**\n * Read stdin if data is available. Returns an empty string when stdin is a\n * TTY or when nothing arrives within `STDIN_TIMEOUT_MS`. Hooks invoked\n * without piped JSON should never hang.\n */\nconst STDIN_TIMEOUT_MS = 200;\n\nasync function readStdinSafely(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n return new Promise<string>((resolve) => {\n let buf = \"\";\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n process.stdin.removeAllListeners(\"data\");\n process.stdin.removeAllListeners(\"end\");\n process.stdin.removeAllListeners(\"error\");\n // Release the event loop — `setEncoding` + 'data' listener cause Node\n // to keep reading stdin even after we've stopped listening.\n try {\n process.stdin.pause();\n process.stdin.unref();\n } catch {\n // ignore — stdin may already be destroyed\n }\n resolve(buf);\n };\n const idle = setTimeout(finish, STDIN_TIMEOUT_MS);\n idle.unref();\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n buf += chunk;\n idle.refresh();\n });\n process.stdin.on(\"end\", () => {\n clearTimeout(idle);\n finish();\n });\n process.stdin.on(\"error\", () => {\n clearTimeout(idle);\n finish();\n });\n });\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n if (!raw) return undefined;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n","import { checkbox, confirm, password } from \"@inquirer/prompts\";\nimport kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport type { AgentId } from \"../adapters/types.js\";\nimport {\n clearBypassSecret,\n loadBypassSecret,\n saveBypassSecret,\n} from \"../config/bypass.js\";\nimport {\n defaultConfig,\n loadConfig,\n saveConfig,\n type AgentInsightsConfig,\n} from \"../config/config.js\";\nimport { configFile, configRoot } from \"../config/paths.js\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { log } from \"../util/log.js\";\n\nexport type InitOptions = {\n agents?: string;\n transcripts?: boolean;\n analysis?: boolean;\n telemetry?: boolean;\n force?: boolean;\n hooks?: boolean;\n yes?: boolean;\n bypassSecret?: string;\n};\n\nconst CONSENT_TEXT = `\\\nAgent Insights is opt-in (internal).\n\nOTLP / lifecycle metrics: event names, timestamps, tool names, outcomes,\nhashed user/repo/workspace IDs — never prompts, messages, or tool inputs.\n\nVercel Blob (optional): full session transcripts for internal analysis;\nusername stored as SHA256 only.\n\nSessionEnd analysis (optional): an internal LLM reviews transcript for\ntooling gaps; may post to Slack with a GitHub issue link.\\\n`;\n\ntype Choices = {\n agents: Set<AgentId>;\n transcripts: boolean;\n analysis: boolean;\n telemetry: boolean;\n /** undefined = leave existing untouched; \"\" = clear; non-empty = save. */\n bypassSecret?: string | undefined;\n};\n\nexport async function runInit(opts: InitOptions): Promise<void> {\n const token = await getValidToken();\n if (!token) {\n log.fail(\"Not logged in. Run `agent-insights login` first.\");\n process.exit(1);\n }\n\n log.info(kleur.bold(\"Agent Insights setup\\n\"));\n log.hint(CONSENT_TEXT);\n log.info(\"\");\n\n const existing = await loadConfig();\n const interactive =\n !opts.yes && Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\n if (existing && !opts.force) {\n if (!interactive) {\n log.warn(\n `Config already exists at ${configFile()} — re-run with --force to overwrite, or use interactive mode.`,\n );\n return;\n }\n log.warn(`Config already exists at ${configFile()}.`);\n const overwrite = await safePrompt(() =>\n confirm({ message: \"Overwrite existing config?\", default: true }),\n );\n if (overwrite === undefined) return; // user aborted\n if (!overwrite) {\n log.info(\"Aborted — existing config left in place.\");\n return;\n }\n log.info(\"\");\n }\n\n let choices: Choices;\n if (interactive) {\n const result = await promptChoices(opts);\n if (!result) return; // aborted\n choices = result;\n } else {\n choices = {\n agents: parseAgents(opts.agents),\n transcripts: opts.transcripts ?? true,\n analysis: opts.analysis ?? true,\n telemetry: opts.telemetry ?? true,\n bypassSecret: opts.bypassSecret,\n };\n }\n\n if (choices.agents.size === 0) {\n log.fail(\"No agents selected — nothing to install.\");\n process.exit(1);\n }\n\n // Always start from defaults so re-running `init` (or `init --force`)\n // resets fields like `transcriptStore.type` that the user can't toggle\n // through the prompts. Otherwise an old \"blob\" mode would survive a\n // re-init forever.\n const cfg: AgentInsightsConfig = defaultConfig();\n cfg.userConsent.transcriptSync = choices.transcripts;\n cfg.userConsent.sessionAnalysis = choices.analysis;\n cfg.userConsent.eventTelemetry = choices.telemetry;\n cfg.agents.claudeCode.enabled = choices.agents.has(\"claude-code\");\n cfg.agents.cursor.enabled = choices.agents.has(\"cursor\");\n\n await saveConfig(cfg);\n log.ok(`Wrote config to ${configFile()}`);\n\n if (choices.bypassSecret !== undefined) {\n if (choices.bypassSecret === \"\") {\n await clearBypassSecret();\n log.ok(\"Cleared stored deployment-protection bypass secret.\");\n } else {\n await saveBypassSecret(choices.bypassSecret);\n log.ok(\"Saved deployment-protection bypass secret.\");\n }\n }\n\n const installHooks = opts.hooks ?? true;\n const repoRoot = process.cwd();\n if (installHooks) {\n for (const adapter of adapterList) {\n const enabled =\n (adapter.id === \"claude-code\" && cfg.agents.claudeCode.enabled) ||\n (adapter.id === \"cursor\" && cfg.agents.cursor.enabled);\n if (!enabled) continue;\n const { path } = await adapter.install(repoRoot, cfg.hooksScope);\n log.ok(`Installed ${adapter.label} hooks → ${rel(path)}`);\n }\n }\n\n log.info(\"\");\n log.info(`Config root: ${configRoot()}`);\n log.hint(\"Run `agent-insights doctor` to verify the setup.\");\n}\n\nasync function promptChoices(opts: InitOptions): Promise<Choices | undefined> {\n const requested = opts.agents ? parseAgents(opts.agents) : null;\n const selected = await safePrompt(() =>\n checkbox<AgentId>({\n message: \"Which agents would you like to install hooks for?\",\n choices: adapterList.map((adapter) => ({\n name: adapter.label,\n value: adapter.id,\n checked: requested ? requested.has(adapter.id) : true,\n })),\n required: true,\n }),\n );\n if (selected === undefined) return undefined;\n\n const transcripts = await safePrompt(() =>\n confirm({\n message: \"Sync session transcripts to Vercel Blob?\",\n default: opts.transcripts ?? true,\n }),\n );\n if (transcripts === undefined) return undefined;\n\n const analysis = await safePrompt(() =>\n confirm({\n message: \"Enable SessionEnd analysis (internal LLM review)?\",\n default: opts.analysis ?? true,\n }),\n );\n if (analysis === undefined) return undefined;\n\n const telemetry = await safePrompt(() =>\n confirm({\n message: \"Send OTLP event telemetry?\",\n default: opts.telemetry ?? true,\n }),\n );\n if (telemetry === undefined) return undefined;\n\n let bypassSecret: string | undefined;\n if (opts.bypassSecret !== undefined) {\n // Explicit flag wins; don't prompt.\n bypassSecret = opts.bypassSecret;\n } else {\n const existing = await loadBypassSecret();\n const answer = await safePrompt(() =>\n password({\n message: existing\n ? \"Vercel deployment-protection bypass secret (Enter to keep existing, '-' to clear):\"\n : \"Vercel deployment-protection bypass secret (Enter to skip):\",\n mask: true,\n }),\n );\n if (answer === undefined) return undefined;\n const trimmed = answer.trim();\n if (trimmed === \"\") bypassSecret = undefined; // leave alone\n else if (trimmed === \"-\") bypassSecret = \"\"; // clear\n else bypassSecret = trimmed;\n }\n\n return {\n agents: new Set(selected),\n transcripts,\n analysis,\n telemetry,\n bypassSecret,\n };\n}\n\n/**\n * Run an inquirer prompt and return undefined if the user aborts (Ctrl+C).\n * Re-throws unexpected errors so they surface in the CLI.\n */\nasync function safePrompt<T>(run: () => Promise<T>): Promise<T | undefined> {\n try {\n return await run();\n } catch (err) {\n if (\n err instanceof Error &&\n (err.name === \"ExitPromptError\" || err.name === \"AbortPromptError\")\n ) {\n log.info(\"Aborted.\");\n return undefined;\n }\n throw err;\n }\n}\n\nfunction parseAgents(value: string | undefined): Set<AgentId> {\n const out = new Set<AgentId>();\n const trimmed = (value ?? \"\").trim();\n if (trimmed === \"*\") {\n for (const a of adapterList) out.add(a.id);\n return out;\n }\n const list = (trimmed || \"claude-code,cursor\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n for (const id of list) {\n if (id === \"claude-code\" || id === \"cursor\") out.add(id);\n else log.warn(`Unknown agent id \"${id}\" — skipping.`);\n }\n return out;\n}\n\nfunction rel(p: string): string {\n const cwd = process.cwd();\n return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;\n}\n","import kleur from \"kleur\";\nimport {\n exchangeCodeForToken,\n fetchUserInfo,\n openBrowser,\n REDIRECT_URI,\n startPkceFlow,\n} from \"../auth/oauth.js\";\nimport { clearAuth, saveAuth } from \"../auth/store.js\";\nimport { log } from \"../util/log.js\";\n\nconst ALLOWED_DOMAIN = \"vercel.com\";\n\nexport async function runLogin(): Promise<void> {\n log.info(kleur.bold(\"Signing in with Vercel...\\n\"));\n\n // 1. Build auth URL + start local callback server\n let pkceResult: { code: string; codeVerifier: string };\n try {\n log.hint(`Listening on ${REDIRECT_URI} — opening browser...`);\n pkceResult = await startPkceFlow();\n } catch (err) {\n log.fail(`Login failed: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 2. Exchange code for tokens\n let tokens;\n try {\n tokens = await exchangeCodeForToken(pkceResult.code, pkceResult.codeVerifier);\n } catch (err) {\n log.fail(`Token exchange failed: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 3. Verify @vercel.com email\n let userInfo;\n try {\n userInfo = await fetchUserInfo(tokens.access_token);\n } catch (err) {\n log.fail(`Could not fetch user info: ${(err as Error).message}`);\n process.exit(1);\n }\n\n if (\n !userInfo.email?.endsWith(`@${ALLOWED_DOMAIN}`) ||\n !userInfo.email_verified\n ) {\n log.fail(\n `Only @${ALLOWED_DOMAIN} accounts are allowed. Got: ${userInfo.email ?? \"(no email)\"}`,\n );\n process.exit(1);\n }\n\n // 4. Save tokens\n await saveAuth({\n accessToken: tokens.access_token,\n ...(tokens.refresh_token !== undefined\n ? { refreshToken: tokens.refresh_token }\n : {}),\n expiresAt: Date.now() + tokens.expires_in * 1000,\n email: userInfo.email,\n });\n\n const name = userInfo.preferred_username ?? userInfo.email;\n log.ok(`Logged in as ${kleur.bold(name)} (${userInfo.email})`);\n log.hint(\"Run `agent-insights init` to install hooks globally.\");\n}\n\nexport async function runLogout(): Promise<void> {\n await clearAuth();\n log.ok(\"Logged out.\");\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport kleur from \"kleur\";\nimport { adapterList } from \"../adapters/registry.js\";\nimport { loadConfig } from \"../config/config.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { log } from \"../util/log.js\";\n\nexport type StatusOptions = {\n json?: boolean;\n};\n\nexport async function runStatus(opts: StatusOptions): Promise<void> {\n const cfg = await loadConfig();\n const repoRoot = process.cwd();\n\n if (!cfg) {\n if (opts.json) log.json({ initialized: false });\n else log.warn(`No config found at ${configFile()} — run \\`agent-insights init\\`.`);\n return;\n }\n\n const adapterStatus = await Promise.all(\n adapterList.map(async (a) => ({\n id: a.id,\n label: a.label,\n enabled:\n a.id === \"claude-code\"\n ? cfg.agents.claudeCode.enabled\n : cfg.agents.cursor.enabled,\n installed: await a.isInstalled(repoRoot),\n })),\n );\n\n if (opts.json) {\n log.json({\n initialized: true,\n configPath: configFile(),\n enabled: cfg.enabled,\n consent: cfg.userConsent,\n vercel: {\n linked: existsSync(join(repoRoot, \".vercel/project.json\")),\n envFile: existsSync(join(repoRoot, \".env.local\")),\n },\n adapters: adapterStatus,\n });\n return;\n }\n\n log.info(kleur.bold(\"Agent Insights status\\n\"));\n log.info(`config ${configFile()}`);\n log.info(`enabled ${cfg.enabled ? \"yes\" : \"no\"}`);\n log.info(\"\");\n log.info(kleur.bold(\"Consent\"));\n log.info(` event telemetry ${yn(cfg.userConsent.eventTelemetry)}`);\n log.info(` transcript sync ${yn(cfg.userConsent.transcriptSync)}`);\n log.info(` session analysis ${yn(cfg.userConsent.sessionAnalysis)}`);\n log.info(\"\");\n log.info(kleur.bold(\"Adapters\"));\n for (const a of adapterStatus) {\n log.info(\n ` ${a.label.padEnd(12)} ${a.enabled ? \"enabled\" : \"disabled\"} hooks ${\n a.installed ? \"installed\" : \"not installed\"\n }`,\n );\n }\n log.info(\"\");\n log.info(kleur.bold(\"Vercel\"));\n log.info(\n ` linked ${yn(existsSync(join(repoRoot, \".vercel/project.json\")))}`,\n );\n log.info(` .env.local ${yn(existsSync(join(repoRoot, \".env.local\")))}`);\n}\n\nfunction yn(v: boolean): string {\n return v ? kleur.green(\"yes\") : kleur.dim(\"no\");\n}\n"],"mappings":"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","names":["createHash","readFile","writeFile","dirname","mkdir","unlink","mkdir","readFile","writeFile","homedir","dirname","join","mkdir","readFile","writeFile","homedir","dirname","join","HOOK_COMMAND_NAME","HOOK_MAP","asString","readJson","isAgentInsightsCommand","kleur","readFile","chmod","mkdir","readFile","writeFile","dirname","readFile","userHash","kleur","getValidToken","kleur","kleur","kleur","kleur","userInfo","join","kleur","join","kleur"]}
package/dist/index.d.ts CHANGED
@@ -140,6 +140,12 @@ type TranscriptUploadInput = {
140
140
  sessionId: string;
141
141
  /** Agent name (e.g. "claude-code"). */
142
142
  agent: string;
143
+ /**
144
+ * Vercel email captured at `agent-insights login`. Lets the analyzer
145
+ * join sessions to real user accounts; never logged or written into
146
+ * the transcript itself.
147
+ */
148
+ userEmail?: string;
143
149
  };
144
150
  type TranscriptUploadResult = {
145
151
  pathname: string;
package/dist/index.js CHANGED
@@ -136,16 +136,15 @@ var claudeCodeAdapter = {
136
136
  const path = resolveClaudePath(repoRoot, scope);
137
137
  const settings = await readJson(path);
138
138
  const next = { ...settings ?? {} };
139
- const hooks = { ...next.hooks ?? {} };
139
+ const hooks = {};
140
+ for (const [event, raw] of Object.entries(next.hooks ?? {})) {
141
+ hooks[event] = normalizeEventHooks(raw);
142
+ }
140
143
  for (const event of CLAUDE_HOOK_EVENTS) {
141
- const existing = (hooks[event] ?? []).filter(
142
- (h) => !isAgentInsightsCommand(h.command)
144
+ const withoutInsights = removeAgentInsightsMatchers(
145
+ hooks[event] ?? []
143
146
  );
144
- existing.push({
145
- type: "command",
146
- command: `${HOOK_COMMAND_NAME} hook ${event}`
147
- });
148
- hooks[event] = existing;
147
+ hooks[event] = addAgentInsightsHook(withoutInsights, event);
149
148
  }
150
149
  next.hooks = hooks;
151
150
  await mkdir(dirname(path), { recursive: true });
@@ -157,18 +156,16 @@ var claudeCodeAdapter = {
157
156
  const path = resolveClaudePath(repoRoot, scope);
158
157
  const settings = await readJson(path);
159
158
  if (!settings?.hooks) return { removed: false, path };
160
- const hooks = { ...settings.hooks };
159
+ const hooks = {};
161
160
  let touched = false;
162
- for (const [event, entries] of Object.entries(hooks)) {
163
- const filtered = entries.filter(
164
- (h) => !isAgentInsightsCommand(h.command)
165
- );
166
- if (filtered.length !== entries.length) touched = true;
167
- if (filtered.length === 0) {
168
- delete hooks[event];
169
- } else {
170
- hooks[event] = filtered;
161
+ for (const [event, raw] of Object.entries(settings.hooks)) {
162
+ const before = normalizeEventHooks(raw);
163
+ const after = removeAgentInsightsMatchers(before);
164
+ if (after.length !== before.length) touched = true;
165
+ else if (JSON.stringify(after) !== JSON.stringify(before)) {
166
+ touched = true;
171
167
  }
168
+ if (after.length > 0) hooks[event] = after;
172
169
  }
173
170
  settings.hooks = hooks;
174
171
  await writeFile(path, `${JSON.stringify(settings, null, 2)}
@@ -181,7 +178,9 @@ var claudeCodeAdapter = {
181
178
  );
182
179
  if (!settings?.hooks) return false;
183
180
  return Object.values(settings.hooks).some(
184
- (entries) => entries.some((h) => isAgentInsightsCommand(h.command))
181
+ (raw) => normalizeEventHooks(raw).some(
182
+ (group) => group.hooks.some((h) => isAgentInsightsCommand(h.command))
183
+ )
185
184
  );
186
185
  }
187
186
  };
@@ -192,6 +191,59 @@ function isAgentInsightsCommand(cmd) {
192
191
  if (!cmd) return false;
193
192
  return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);
194
193
  }
194
+ function isHookCommand(value) {
195
+ if (!value || typeof value !== "object") return false;
196
+ const entry = value;
197
+ return entry.type === "command" && typeof entry.command === "string";
198
+ }
199
+ function normalizeEventHooks(raw) {
200
+ const groups = [];
201
+ for (const entry of raw) {
202
+ if (!entry || typeof entry !== "object") continue;
203
+ const record = entry;
204
+ if (Array.isArray(record.hooks)) {
205
+ const commands = record.hooks.filter(isHookCommand);
206
+ if (commands.length === 0) continue;
207
+ groups.push({
208
+ matcher: typeof record.matcher === "string" ? record.matcher : "",
209
+ hooks: commands
210
+ });
211
+ continue;
212
+ }
213
+ if (isHookCommand(record)) {
214
+ groups.push({
215
+ matcher: "",
216
+ hooks: [record]
217
+ });
218
+ }
219
+ }
220
+ return groups;
221
+ }
222
+ function removeAgentInsightsMatchers(matchers) {
223
+ const result = [];
224
+ for (const group of matchers) {
225
+ const hooks = group.hooks.filter(
226
+ (h) => !isAgentInsightsCommand(h.command)
227
+ );
228
+ if (hooks.length > 0) {
229
+ result.push({ ...group, hooks });
230
+ }
231
+ }
232
+ return result;
233
+ }
234
+ function addAgentInsightsHook(matchers, event) {
235
+ const command = `${HOOK_COMMAND_NAME} hook ${event}`;
236
+ const entry = { type: "command", command };
237
+ for (const group of matchers) {
238
+ if (group.matcher === "") {
239
+ if (!group.hooks.some((h) => h.command === command)) {
240
+ group.hooks.push(entry);
241
+ }
242
+ return matchers;
243
+ }
244
+ }
245
+ return [...matchers, { matcher: "", hooks: [entry] }];
246
+ }
195
247
  async function readJson(path) {
196
248
  try {
197
249
  const raw = await readFile(path, "utf8");
@@ -534,7 +586,8 @@ async function getValidToken() {
534
586
  const refreshed = {
535
587
  accessToken: tokens.access_token,
536
588
  refreshToken: tokens.refresh_token ?? auth.refreshToken,
537
- expiresAt: Date.now() + tokens.expires_in * 1e3
589
+ expiresAt: Date.now() + tokens.expires_in * 1e3,
590
+ ...auth.email ? { email: auth.email } : {}
538
591
  };
539
592
  await saveAuth(refreshed);
540
593
  return refreshed.accessToken;
@@ -621,7 +674,8 @@ var AnalyzerTranscriptStore = class {
621
674
  "content-type": "application/jsonl",
622
675
  "x-session-id": input.sessionId,
623
676
  "x-agent": input.agent,
624
- "x-user-hash": input.userHash
677
+ "x-user-hash": input.userHash,
678
+ ...input.userEmail ? { "x-user-email": input.userEmail } : {}
625
679
  });
626
680
  const res = await fetch(url, { method: "POST", headers, body });
627
681
  if (!res.ok) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/privacy/hash.ts","../src/privacy/sanitize.ts","../src/adapters/claude-code.ts","../src/adapters/cursor.ts","../src/adapters/registry.ts","../src/config/config.ts","../src/config/analyzer.ts","../src/config/paths.ts","../src/transcript/store.ts","../src/auth/store.ts","../src/auth/oauth.ts","../src/config/bypass.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n/**\n * Stable SHA-256 hash of a value, hex-encoded.\n *\n * Used to anonymize usernames, repo remote URLs, workspace paths, and\n * session identifiers before they leave the machine.\n */\nexport function sha256(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Short prefix of `sha256(value)` for human-readable dashboards. */\nexport function shortHash(value: string, length = 12): string {\n return sha256(value).slice(0, length);\n}\n","/**\n * Keys that must never appear in any payload that leaves the machine.\n *\n * Matching is case-insensitive and snake_case/camelCase agnostic — we strip a\n * key if its normalized form equals one of these values.\n */\nconst FORBIDDEN_KEYS: ReadonlySet<string> = new Set([\n \"prompt\",\n \"prompts\",\n \"message\",\n \"messages\",\n \"content\",\n \"text\",\n \"transcript\",\n \"toolinput\",\n \"input\",\n \"arguments\",\n \"args\",\n \"params\",\n \"file\",\n \"filecontent\",\n \"filecontents\",\n \"diff\",\n \"patch\",\n \"secret\",\n \"secrets\",\n \"token\",\n \"apikey\",\n \"authorization\",\n \"password\",\n \"credentials\",\n]);\n\nconst MAX_STRING_LENGTH = 256;\n\n/** Allowed primitive value types in sanitized metadata. */\nexport type SanitizedPrimitive = string | number | boolean | null;\n\nfunction normalizeKey(key: string): string {\n return key.toLowerCase().replace(/[_\\-\\s]/g, \"\");\n}\n\nfunction truncate(value: string): string {\n if (value.length <= MAX_STRING_LENGTH) return value;\n return `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]`;\n}\n\nfunction sanitizePrimitive(value: unknown): SanitizedPrimitive | undefined {\n if (value === null) return null;\n switch (typeof value) {\n case \"string\":\n return truncate(value);\n case \"number\":\n return Number.isFinite(value) ? value : null;\n case \"boolean\":\n return value;\n default:\n return undefined;\n }\n}\n\n/**\n * Recursively remove forbidden keys from an arbitrary payload.\n *\n * Returned shape:\n * - Objects keep allowed primitive fields (truncated to {@link MAX_STRING_LENGTH}).\n * - Arrays keep allowed primitive elements; nested objects/arrays are recursed.\n * - Non-primitive, non-container values (functions, symbols, Buffers, …) are dropped.\n */\nexport function sanitize(input: unknown): unknown {\n return sanitizeInner(input);\n}\n\nfunction sanitizeInner(value: unknown): unknown {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const item of value) {\n const sanitizedItem = sanitizeInner(item);\n if (sanitizedItem !== undefined) {\n out.push(sanitizedItem);\n }\n }\n return out;\n }\n\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [rawKey, rawValue] of Object.entries(value)) {\n if (FORBIDDEN_KEYS.has(normalizeKey(rawKey))) {\n continue;\n }\n if (rawValue && typeof rawValue === \"object\") {\n out[rawKey] = sanitizeInner(rawValue);\n } else {\n const primitive = sanitizePrimitive(rawValue);\n if (primitive !== undefined) {\n out[rawKey] = primitive;\n }\n }\n }\n return out;\n }\n\n return sanitizePrimitive(value);\n}\n\nexport const __testing = { FORBIDDEN_KEYS, normalizeKey };\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\nconst HOOK_MAP: Record<string, AgentEventType> = {\n SessionStart: \"session.start\",\n SessionEnd: \"session.end\",\n UserPromptSubmit: \"user.prompt.submit\",\n PreToolUse: \"tool.start\",\n PostToolUse: \"tool.end\",\n PostToolUseFailure: \"tool.failure\",\n PermissionRequest: \"permission.request\",\n PermissionDenied: \"permission.denied\",\n SubagentStart: \"subagent.start\",\n SubagentStop: \"subagent.end\",\n Stop: \"agent.stop\",\n Notification: \"notification\",\n PreCompact: \"context.compact.start\",\n PostCompact: \"context.compact.end\",\n};\n\nexport const CLAUDE_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype ClaudeHookEntry = {\n type: \"command\";\n command: string;\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, ClaudeHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const claudeCodeAdapter: Adapter = {\n id: \"claude-code\",\n agent: \"claude-code\",\n label: \"Claude Code\",\n settingsFile: \".claude/settings.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n const sessionId =\n asString(data[\"session_id\"]) ?? asString(data[\"sessionId\"]);\n const toolName =\n asString(data[\"tool_name\"]) ??\n asString(data[\"toolName\"]) ??\n asString((data[\"tool\"] as Record<string, unknown> | undefined)?.[\"name\"]);\n const transcriptPath =\n asString(data[\"transcript_path\"]) ?? asString(data[\"transcriptPath\"]);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n const next: ClaudeSettings = { ...(settings ?? {}) };\n const hooks: Record<string, ClaudeHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CLAUDE_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({\n type: \"command\",\n command: `${HOOK_COMMAND_NAME} hook ${event}`,\n });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<ClaudeSettings>(\n resolveClaudePath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveClaudePath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(repoRoot, \".claude\", \"settings.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\n/**\n * Cursor native hook names (camelCase) → common schema. Reference:\n * https://cursor.com/docs/hooks\n */\nconst HOOK_MAP: Record<string, AgentEventType> = {\n sessionStart: \"session.start\",\n sessionEnd: \"session.end\",\n beforeSubmitPrompt: \"user.prompt.submit\",\n preToolUse: \"tool.start\",\n postToolUse: \"tool.end\",\n postToolUseFailure: \"tool.failure\",\n subagentStart: \"subagent.start\",\n subagentStop: \"subagent.end\",\n stop: \"agent.stop\",\n preCompact: \"context.compact.start\",\n // Additional events — mapped to existing schema types\n beforeShellExecution: \"tool.start\",\n afterShellExecution: \"tool.end\",\n afterFileEdit: \"tool.end\",\n workspaceOpen: \"session.start\",\n};\n\nexport const CURSOR_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype CursorHookEntry = {\n command: string;\n};\n\ntype CursorHooksConfig = {\n /** Cursor expects schema version 1 at the top level. */\n version?: number;\n hooks?: Record<string, CursorHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const cursorAdapter: Adapter = {\n id: \"cursor\",\n agent: \"cursor\",\n label: \"Cursor\",\n settingsFile: \".cursor/hooks.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n // Cursor uses `session_id` in payloads and is identical to `conversation_id`.\n const sessionId =\n asString(data[\"session_id\"]) ??\n asString(data[\"sessionId\"]) ??\n asString(data[\"conversation_id\"]);\n const toolName = asString(data[\"tool_name\"]) ?? asString(data[\"toolName\"]);\n\n // transcript_path is confirmed in all Cursor hook payloads.\n const transcriptPath =\n asString(data[\"transcript_path\"]) ??\n asString(data[\"transcriptPath\"]) ??\n asString(process.env.CURSOR_TRANSCRIPT_PATH);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n const next: CursorHooksConfig = {\n version: 1,\n ...(settings ?? {}),\n };\n const hooks: Record<string, CursorHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CURSOR_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({ command: `${HOOK_COMMAND_NAME} hook ${event}` });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<CursorHooksConfig>(\n resolveCursorPath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveCursorPath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".cursor\", \"hooks.json\")\n : join(repoRoot, \".cursor\", \"hooks.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { claudeCodeAdapter } from \"./claude-code.js\";\nimport { cursorAdapter } from \"./cursor.js\";\nimport type { Adapter, AgentId } from \"./types.js\";\n\nexport const adapters: Record<AgentId, Adapter> = {\n \"claude-code\": claudeCodeAdapter,\n cursor: cursorAdapter,\n};\n\nexport const adapterList: ReadonlyArray<Adapter> = Object.values(adapters);\n\nexport function getAdapter(id: AgentId): Adapter {\n return adapters[id];\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { getAnalyzerUrl } from \"./analyzer.js\";\nimport { configFile, logsDir, sessionCacheDir } from \"./paths.js\";\n\nexport type ExporterConfig = {\n type: \"otlp\";\n endpoint: string;\n headers: Record<string, string>;\n dataset: string;\n};\n\nexport type HooksScope = \"global\" | \"local\";\n\nexport type TranscriptStoreConfig =\n | {\n /** Upload via analyzer API using AGENT_INSIGHTS_API_KEY (preferred). */\n type: \"analyzer\";\n }\n | {\n /** Direct upload with a Blob RW token (legacy / advanced). */\n type: \"blob\";\n tokenEnv: string;\n prefix: string;\n }\n | {\n type: \"none\";\n };\n\nexport type SessionAnalysisConfig = {\n /** Server-side enablement; user opts in via `userConsent.sessionAnalysis`. */\n enabled: boolean;\n /** Analyzer endpoint base URL. CLI POSTs to `${analyzerUrl}/api/sessions`. */\n analyzerUrl: string;\n /** GitHub repo for pre-filled new-issue URLs (display only; constructed by analyzer). */\n githubIssueRepo: string;\n};\n\nexport type AgentConfig = {\n enabled: boolean;\n};\n\nexport type AgentInsightsConfig = {\n version: 1;\n enabled: boolean;\n /** Where agent hook entries are written. Default `global` (user home). */\n hooksScope: HooksScope;\n vercel: {\n team: string;\n project: string;\n };\n userConsent: {\n eventTelemetry: boolean;\n transcriptSync: boolean;\n sessionAnalysis: boolean;\n };\n exporter: ExporterConfig;\n transcriptStore: TranscriptStoreConfig;\n sessionAnalysis: SessionAnalysisConfig;\n agents: Record<\"claudeCode\" | \"cursor\", AgentConfig>;\n privacy: {\n capturePrompts: false;\n captureMessages: false;\n captureToolInputs: false;\n captureFileContents: false;\n };\n};\n\nexport function defaultConfig(): AgentInsightsConfig {\n return {\n version: 1,\n enabled: true,\n hooksScope: \"global\",\n vercel: {\n team: \"vercel-labs\",\n project: \"agent-insights\",\n },\n userConsent: {\n eventTelemetry: false,\n transcriptSync: false,\n sessionAnalysis: false,\n },\n exporter: {\n type: \"otlp\",\n endpoint: process.env.AGENT_INSIGHTS_OTEL_ENDPOINT ?? \"\",\n headers: {},\n dataset: \"agent-insights\",\n },\n transcriptStore: {\n // Upload via the analyzer using the SiwV bearer token — no client-side\n // Blob token required. Set `transcriptStore.type` to \"blob\" in\n // config.json to switch to direct uploads with BLOB_READ_WRITE_TOKEN.\n type: \"analyzer\",\n },\n sessionAnalysis: {\n enabled: false,\n analyzerUrl: getAnalyzerUrl(),\n githubIssueRepo: \"vercel-labs/agent-insights\",\n },\n agents: {\n claudeCode: { enabled: false },\n cursor: { enabled: false },\n },\n privacy: {\n capturePrompts: false,\n captureMessages: false,\n captureToolInputs: false,\n captureFileContents: false,\n },\n };\n}\n\nexport async function loadConfig(): Promise<AgentInsightsConfig | undefined> {\n try {\n const raw = await readFile(configFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AgentInsightsConfig>;\n return mergeConfig(defaultConfig(), parsed);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveConfig(cfg: AgentInsightsConfig): Promise<void> {\n await ensureDirs();\n await writeFile(configFile(), `${JSON.stringify(cfg, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function ensureDirs(): Promise<void> {\n await mkdir(dirname(configFile()), { recursive: true });\n await mkdir(sessionCacheDir(), { recursive: true });\n await mkdir(logsDir(), { recursive: true });\n}\n\nfunction mergeConfig(\n base: AgentInsightsConfig,\n patch: Partial<AgentInsightsConfig>,\n): AgentInsightsConfig {\n return {\n ...base,\n ...patch,\n vercel: { ...base.vercel, ...(patch.vercel ?? {}) },\n userConsent: { ...base.userConsent, ...(patch.userConsent ?? {}) },\n exporter: { ...base.exporter, ...(patch.exporter ?? {}) },\n transcriptStore: (patch.transcriptStore ??\n base.transcriptStore) as TranscriptStoreConfig,\n sessionAnalysis: {\n ...base.sessionAnalysis,\n ...(patch.sessionAnalysis ?? {}),\n },\n agents: {\n claudeCode: {\n ...base.agents.claudeCode,\n ...(patch.agents?.claudeCode ?? {}),\n },\n cursor: { ...base.agents.cursor, ...(patch.agents?.cursor ?? {}) },\n },\n privacy: base.privacy,\n };\n}\n","/**\n * Production analyzer deployment. Override via AGENT_INSIGHTS_ANALYZER_URL\n * for local/dev/staging analyzers.\n */\nexport const DEFAULT_ANALYZER_URL = \"https://agent-insights.labs.vercel.dev\";\n\n/** Resolve the analyzer base URL: env var if set, otherwise the prod default. */\nexport function getAnalyzerUrl(): string {\n return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Root directory for agent-insights state. Honors `AGENT_INSIGHTS_HOME`. */\nexport function configRoot(): string {\n return process.env.AGENT_INSIGHTS_HOME ?? join(homedir(), \".agent-insights\");\n}\n\nexport function configFile(): string {\n return join(configRoot(), \"config.json\");\n}\n\n/** API key storage (`chmod 600`). Not checked into git. */\nexport function credentialsFile(): string {\n return join(configRoot(), \"credentials.json\");\n}\n\n/** OAuth token storage (`chmod 600`). Not checked into git. */\nexport function authFile(): string {\n return join(configRoot(), \"auth.json\");\n}\n\n/** Vercel deployment-protection bypass secret (`chmod 600`). */\nexport function bypassSecretFile(): string {\n return join(configRoot(), \"bypass-secret\");\n}\n\nexport function sessionCacheDir(): string {\n return join(configRoot(), \"session-cache\");\n}\n\nexport function logsDir(): string {\n return join(configRoot(), \"logs\");\n}\n\nexport const CLAUDE_SETTINGS_FILE = \".claude/settings.json\";\nexport const CURSOR_HOOKS_FILE = \".cursor/hooks.json\";\n\n/** User-level Claude Code settings (hooks apply in every repo). */\nexport function claudeUserSettingsPath(): string {\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** User-level Cursor hooks (hooks apply in every project). */\nexport function cursorUserHooksPath(): string {\n return join(homedir(), \".cursor\", \"hooks.json\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { put } from \"@vercel/blob\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport type { TranscriptStoreConfig } from \"../config/config.js\";\n\nexport type TranscriptUploadInput = {\n /** Absolute path on disk to the transcript file. */\n transcriptPath: string;\n /** SHA256-hashed user identifier. Used in the upload pathname. */\n userHash: string;\n /** Session identifier supplied by the agent. */\n sessionId: string;\n /** Agent name (e.g. \"claude-code\"). */\n agent: string;\n};\n\nexport type TranscriptUploadResult = {\n pathname: string;\n url: string;\n size: number;\n};\n\nexport interface TranscriptStore {\n upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult>;\n /** Lightweight health check used by `doctor`. */\n ping(): Promise<{ ok: true } | { ok: false; reason: string }>;\n}\n\nclass NoopTranscriptStore implements TranscriptStore {\n async upload(): Promise<TranscriptUploadResult> {\n throw new Error(\"transcript store disabled (transcriptStore.type=none)\");\n }\n async ping(): Promise<{ ok: false; reason: string }> {\n return { ok: false, reason: \"transcript store disabled\" };\n }\n}\n\nclass BlobTranscriptStore implements TranscriptStore {\n constructor(\n private readonly token: string,\n private readonly prefix: string,\n ) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const body = await readFile(input.transcriptPath);\n const requestedPath = buildPathname(\n this.prefix,\n input.userHash,\n input.sessionId,\n );\n const blob = await put(requestedPath, body, {\n access: \"private\",\n token: this.token,\n contentType: \"application/jsonl\",\n addRandomSuffix: true,\n });\n return { pathname: blob.pathname, url: blob.url, size: body.byteLength };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n if (!this.token) return { ok: false, reason: \"missing token\" };\n return { ok: true };\n }\n}\n\n/**\n * Uploads transcripts via the analyzer's POST /api/sessions/upload endpoint\n * using the Sign-in-with-Vercel bearer token. The analyzer then proxies the\n * write to Vercel Blob with its own server-side credentials, so this client\n * does not need a Blob read/write token.\n */\nclass AnalyzerTranscriptStore implements TranscriptStore {\n constructor(private readonly analyzerUrl: string) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const token = await getValidToken();\n if (!token) {\n throw new Error(\n \"transcript store: not logged in — run `agent-insights login`\",\n );\n }\n const body = await readFile(input.transcriptPath);\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/jsonl\",\n \"x-session-id\": input.sessionId,\n \"x-agent\": input.agent,\n \"x-user-hash\": input.userHash,\n });\n const res = await fetch(url, { method: \"POST\", headers, body });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `transcript upload failed (${res.status}): ${text || res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n ok: boolean;\n blobUrl?: string;\n pathname?: string;\n size?: number;\n error?: string;\n };\n if (!json.ok || !json.blobUrl || !json.pathname) {\n throw new Error(`transcript upload error: ${json.error ?? \"unknown\"}`);\n }\n return {\n pathname: json.pathname,\n url: json.blobUrl,\n size: json.size ?? body.byteLength,\n };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n const token = await getValidToken();\n if (!token) {\n return {\n ok: false,\n reason: \"not logged in — run `agent-insights login`\",\n };\n }\n // Try the actual upload endpoint with an intentionally bad request — a\n // healthy analyzer responds 400 (missing headers); a protection wall\n // responds 401/403 with HTML; an unreachable host throws.\n try {\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n });\n const res = await fetch(url, { method: \"POST\", headers });\n if (res.status === 400) return { ok: true };\n if (res.status === 401 || res.status === 403) {\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n return {\n ok: false,\n reason:\n \"blocked by Vercel deployment protection — set a bypass secret via `agent-insights init --bypass-secret <secret>`\",\n };\n }\n return { ok: false, reason: `analyzer rejected token (${res.status})` };\n }\n return { ok: false, reason: `unexpected status ${res.status}` };\n } catch (err) {\n return {\n ok: false,\n reason: `analyzer unreachable: ${(err as Error).message}`,\n };\n }\n }\n}\n\nexport function createTranscriptStore(\n cfg: TranscriptStoreConfig,\n): TranscriptStore {\n if (cfg.type === \"none\") return new NoopTranscriptStore();\n if (cfg.type === \"analyzer\") {\n return new AnalyzerTranscriptStore(getAnalyzerUrl());\n }\n // type === \"blob\"\n const token = process.env[cfg.tokenEnv];\n if (!token) {\n throw new Error(\n `transcript store: env ${cfg.tokenEnv} is not set — run \\`agent-insights init --force\\` to switch to analyzer-proxied uploads (no token required), or \\`vercel env pull .env.local\\` to keep direct Blob uploads`,\n );\n }\n return new BlobTranscriptStore(token, cfg.prefix);\n}\n\nfunction buildPathname(\n prefix: string,\n userHash: string,\n sessionId: string,\n): string {\n const safePrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n const safeSession = encodeURIComponent(sessionId);\n return `${safePrefix}${userHash}/${safeSession}/transcript.jsonl`;\n}\n","import { chmod, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { authFile } from \"../config/paths.js\";\nimport { refreshAccessToken } from \"./oauth.js\";\n\nexport type AuthState = {\n accessToken: string;\n refreshToken?: string;\n /** Unix epoch milliseconds when the access token expires. */\n expiresAt: number;\n};\n\n/** How many ms before expiry we proactively refresh. */\nconst REFRESH_BUFFER_MS = 60_000;\n\nexport async function loadAuth(): Promise<AuthState | undefined> {\n try {\n const raw = await readFile(authFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (typeof parsed.accessToken === \"string\" && parsed.accessToken.length > 0) {\n return parsed as AuthState;\n }\n return undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveAuth(state: AuthState): Promise<void> {\n const path = authFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(state, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows doesn't support chmod — ignore\n }\n}\n\nexport async function clearAuth(): Promise<void> {\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(authFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Returns a valid access token, refreshing silently if needed.\n * Returns `undefined` if not logged in or refresh fails (caller should prompt login).\n */\nexport async function getValidToken(): Promise<string | undefined> {\n const auth = await loadAuth();\n if (!auth) return undefined;\n\n const needsRefresh = Date.now() >= auth.expiresAt - REFRESH_BUFFER_MS;\n if (!needsRefresh) return auth.accessToken;\n\n if (!auth.refreshToken) {\n // Access token expired, no refresh token — clear and force re-login\n await clearAuth();\n return undefined;\n }\n\n try {\n const tokens = await refreshAccessToken(auth.refreshToken);\n const refreshed: AuthState = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? auth.refreshToken,\n expiresAt: Date.now() + tokens.expires_in * 1000,\n };\n await saveAuth(refreshed);\n return refreshed.accessToken;\n } catch {\n // Refresh failed (revoked, expired beyond 30 days) — clear and force re-login\n await clearAuth();\n return undefined;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport { createServer } from \"node:http\";\nimport { platform } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// OAuth app config — Sign-in-with-Vercel client ID (public, not a secret).\n// Override at runtime via AGENT_INSIGHTS_CLIENT_ID env var.\n// ---------------------------------------------------------------------------\nexport const CLIENT_ID =\n process.env.AGENT_INSIGHTS_CLIENT_ID ?? \"cl_TxqqNFTHcF9kEtM48LBFsYfwjhJ9T8sz\";\n\nconst AUTH_ENDPOINT = \"https://vercel.com/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://api.vercel.com/login/oauth/token\";\nconst USERINFO_ENDPOINT = \"https://api.vercel.com/login/oauth/userinfo\";\nconst REVOKE_ENDPOINT = \"https://api.vercel.com/login/oauth/token/revoke\";\n\n// Fixed port — must match the redirect URI registered in the OAuth app.\nconst CALLBACK_PORT = 9797;\nexport const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;\n\nconst SCOPES = \"openid email profile offline_access\";\n\nexport type TokenResponse = {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n id_token?: string;\n};\n\nexport type UserInfo = {\n sub: string;\n email: string;\n email_verified: boolean;\n name?: string;\n preferred_username?: string;\n picture?: string;\n};\n\n// ---------------------------------------------------------------------------\n// PKCE helpers\n// ---------------------------------------------------------------------------\n\nfunction generateCodeVerifier(): string {\n return randomBytes(48).toString(\"base64url\");\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction generateState(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Code + PKCE flow\n// ---------------------------------------------------------------------------\n\nexport type AuthCodeResult =\n | { status: \"success\"; code: string; state: string }\n | { status: \"error\"; message: string };\n\n/**\n * Starts a localhost callback server, opens the Vercel authorization URL in\n * the browser, and waits for the redirect with the auth code.\n * Resolves (or rejects after timeout) when Vercel redirects back.\n */\nexport async function startPkceFlow(timeoutMs = 120_000): Promise<{\n code: string;\n codeVerifier: string;\n}> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n const authUrl = new URL(AUTH_ENDPOINT);\n authUrl.searchParams.set(\"client_id\", CLIENT_ID);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n authUrl.searchParams.set(\"scope\", SCOPES);\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n\n const url = authUrl.toString();\n const codePromise = listenForCallback(state, timeoutMs);\n openBrowser(url);\n // Always print the URL — browser open may fail silently\n process.stderr.write(`\\nIf the browser did not open, visit:\\n ${url}\\n\\n`);\n\n const code = await codePromise;\n return { code, codeVerifier };\n}\n\nfunction listenForCallback(\n expectedState: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${CALLBACK_PORT}`);\n if (url.pathname !== \"/callback\") {\n res.writeHead(404, { connection: \"close\" });\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n const html = (msg: string) =>\n `<html><body style=\"font-family:sans-serif;text-align:center;padding:80px\">` +\n `<h2>${msg}</h2><p>You can close this tab.</p></body></html>`;\n\n // Tell the browser to drop the socket so the server can shut down cleanly\n // and Node's event loop can exit once the callback returns.\n const headers = { \"content-type\": \"text/html\", connection: \"close\" };\n\n const finish = (\n next: () => void,\n body: string,\n status: number,\n ): void => {\n res.writeHead(status, headers);\n res.end(body, () => {\n clearTimeout(timer);\n server.closeAllConnections?.();\n server.close();\n next();\n });\n };\n\n if (error) {\n finish(\n () => reject(new Error(`OAuth error: ${error}`)),\n html(`Login failed: ${error}`),\n 400,\n );\n return;\n }\n\n if (returnedState !== expectedState) {\n finish(\n () => reject(new Error(\"State mismatch — possible CSRF\")),\n html(\"Invalid state parameter — please try again.\"),\n 400,\n );\n return;\n }\n\n if (!code) {\n finish(\n () => reject(new Error(\"No code in callback\")),\n html(\"No authorization code received.\"),\n 400,\n );\n return;\n }\n\n finish(\n () => resolve(code),\n html(\"✓ Logged in! You can close this tab.\"),\n 200,\n );\n });\n\n const timer = setTimeout(() => {\n server.closeAllConnections?.();\n server.close();\n reject(new Error(\"Login timed out — no response within 2 minutes.\"));\n }, timeoutMs);\n timer.unref();\n\n server.listen(CALLBACK_PORT, \"127.0.0.1\", () => {\n // Server is ready; browser will be opened by caller\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n if (err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${CALLBACK_PORT} is already in use. Close any other agent-insights login process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Token exchange\n// ---------------------------------------------------------------------------\n\nexport async function exchangeCodeForToken(\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: CLIENT_ID,\n code,\n code_verifier: codeVerifier,\n redirect_uri: REDIRECT_URI,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n refreshToken: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: CLIENT_ID,\n refresh_token: refreshToken,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function revokeToken(token: string): Promise<void> {\n await fetch(REVOKE_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }),\n }).catch(() => {\n // best-effort — don't fail logout if revocation fails\n });\n}\n\nexport async function fetchUserInfo(accessToken: string): Promise<UserInfo> {\n const res = await fetch(USERINFO_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch user info (${res.status})`);\n }\n return res.json() as Promise<UserInfo>;\n}\n\n/** Open a URL in the system browser. Fails silently — callers always print the URL too. */\nexport function openBrowser(url: string): void {\n const os = platform();\n let cmd: string;\n if (os === \"darwin\") cmd = `open \"${url}\"`;\n else if (os === \"win32\") cmd = `start \"\" \"${url}\"`;\n else cmd = `xdg-open \"${url}\"`;\n\n // Detach + unref so the child doesn't keep the event loop alive after login.\n const child = exec(cmd);\n child.on(\"error\", () => {\n // Silently ignore — caller has already printed the URL\n });\n child.unref();\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { bypassSecretFile } from \"./paths.js\";\n\n/**\n * Resolve the Vercel deployment-protection bypass secret.\n * Precedence:\n * 1. `AGENT_INSIGHTS_BYPASS_SECRET` (explicit override)\n * 2. `VERCEL_AUTOMATION_BYPASS_SECRET` (Vercel's standard env)\n * 3. `~/.agent-insights/bypass-secret` (set via `agent-insights init`)\n */\nexport async function loadBypassSecret(): Promise<string | undefined> {\n const fromEnv =\n process.env.AGENT_INSIGHTS_BYPASS_SECRET ||\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET;\n if (fromEnv) return fromEnv;\n\n try {\n const raw = (await readFile(bypassSecretFile(), \"utf8\")).trim();\n return raw || undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveBypassSecret(secret: string): Promise<void> {\n const path = bypassSecretFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, secret.trim(), { encoding: \"utf8\", mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows — ignore.\n }\n}\n\nexport async function clearBypassSecret(): Promise<void> {\n try {\n await unlink(bypassSecretFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Add the `x-vercel-protection-bypass` header onto an existing headers\n * object if a secret is configured. Mutates and returns the input.\n */\nexport async function withBypassHeader<T extends Record<string, string>>(\n headers: T,\n): Promise<T> {\n const secret = await loadBypassSecret();\n if (secret) {\n (headers as Record<string, string>)[\"x-vercel-protection-bypass\"] = secret;\n }\n return headers;\n}\n"],"mappings":"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","names":["mkdir","readFile","writeFile","homedir","dirname","join","HOOK_COMMAND_NAME","HOOK_MAP","asString","readJson","isAgentInsightsCommand","mkdir","readFile","writeFile","dirname","homedir","join","join","readFile","writeFile","mkdir","dirname","readFile","readFile","writeFile","dirname","mkdir","createHash","readFile","mkdir","dirname","writeFile","unlink","chmod","mkdir","readFile","writeFile","dirname","readFile","readFile"]}
1
+ {"version":3,"sources":["../src/privacy/hash.ts","../src/privacy/sanitize.ts","../src/adapters/claude-code.ts","../src/adapters/cursor.ts","../src/adapters/registry.ts","../src/config/config.ts","../src/config/analyzer.ts","../src/config/paths.ts","../src/transcript/store.ts","../src/auth/store.ts","../src/auth/oauth.ts","../src/config/bypass.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n/**\n * Stable SHA-256 hash of a value, hex-encoded.\n *\n * Used to anonymize usernames, repo remote URLs, workspace paths, and\n * session identifiers before they leave the machine.\n */\nexport function sha256(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Short prefix of `sha256(value)` for human-readable dashboards. */\nexport function shortHash(value: string, length = 12): string {\n return sha256(value).slice(0, length);\n}\n","/**\n * Keys that must never appear in any payload that leaves the machine.\n *\n * Matching is case-insensitive and snake_case/camelCase agnostic — we strip a\n * key if its normalized form equals one of these values.\n */\nconst FORBIDDEN_KEYS: ReadonlySet<string> = new Set([\n \"prompt\",\n \"prompts\",\n \"message\",\n \"messages\",\n \"content\",\n \"text\",\n \"transcript\",\n \"toolinput\",\n \"input\",\n \"arguments\",\n \"args\",\n \"params\",\n \"file\",\n \"filecontent\",\n \"filecontents\",\n \"diff\",\n \"patch\",\n \"secret\",\n \"secrets\",\n \"token\",\n \"apikey\",\n \"authorization\",\n \"password\",\n \"credentials\",\n]);\n\nconst MAX_STRING_LENGTH = 256;\n\n/** Allowed primitive value types in sanitized metadata. */\nexport type SanitizedPrimitive = string | number | boolean | null;\n\nfunction normalizeKey(key: string): string {\n return key.toLowerCase().replace(/[_\\-\\s]/g, \"\");\n}\n\nfunction truncate(value: string): string {\n if (value.length <= MAX_STRING_LENGTH) return value;\n return `${value.slice(0, MAX_STRING_LENGTH)}…[truncated]`;\n}\n\nfunction sanitizePrimitive(value: unknown): SanitizedPrimitive | undefined {\n if (value === null) return null;\n switch (typeof value) {\n case \"string\":\n return truncate(value);\n case \"number\":\n return Number.isFinite(value) ? value : null;\n case \"boolean\":\n return value;\n default:\n return undefined;\n }\n}\n\n/**\n * Recursively remove forbidden keys from an arbitrary payload.\n *\n * Returned shape:\n * - Objects keep allowed primitive fields (truncated to {@link MAX_STRING_LENGTH}).\n * - Arrays keep allowed primitive elements; nested objects/arrays are recursed.\n * - Non-primitive, non-container values (functions, symbols, Buffers, …) are dropped.\n */\nexport function sanitize(input: unknown): unknown {\n return sanitizeInner(input);\n}\n\nfunction sanitizeInner(value: unknown): unknown {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const item of value) {\n const sanitizedItem = sanitizeInner(item);\n if (sanitizedItem !== undefined) {\n out.push(sanitizedItem);\n }\n }\n return out;\n }\n\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [rawKey, rawValue] of Object.entries(value)) {\n if (FORBIDDEN_KEYS.has(normalizeKey(rawKey))) {\n continue;\n }\n if (rawValue && typeof rawValue === \"object\") {\n out[rawKey] = sanitizeInner(rawValue);\n } else {\n const primitive = sanitizePrimitive(rawValue);\n if (primitive !== undefined) {\n out[rawKey] = primitive;\n }\n }\n }\n return out;\n }\n\n return sanitizePrimitive(value);\n}\n\nexport const __testing = { FORBIDDEN_KEYS, normalizeKey };\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\nconst HOOK_MAP: Record<string, AgentEventType> = {\n SessionStart: \"session.start\",\n SessionEnd: \"session.end\",\n UserPromptSubmit: \"user.prompt.submit\",\n PreToolUse: \"tool.start\",\n PostToolUse: \"tool.end\",\n PostToolUseFailure: \"tool.failure\",\n PermissionRequest: \"permission.request\",\n PermissionDenied: \"permission.denied\",\n SubagentStart: \"subagent.start\",\n SubagentStop: \"subagent.end\",\n Stop: \"agent.stop\",\n Notification: \"notification\",\n PreCompact: \"context.compact.start\",\n PostCompact: \"context.compact.end\",\n};\n\nexport const CLAUDE_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype ClaudeHookCommand = {\n type: \"command\";\n command: string;\n};\n\n/** Claude Code 0.12+ matcher group (see code.claude.com/docs/en/hooks). */\ntype ClaudeHookMatcherGroup = {\n matcher: string;\n hooks: ClaudeHookCommand[];\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, unknown[]>;\n [key: string]: unknown;\n};\n\nexport const claudeCodeAdapter: Adapter = {\n id: \"claude-code\",\n agent: \"claude-code\",\n label: \"Claude Code\",\n settingsFile: \".claude/settings.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n const sessionId =\n asString(data[\"session_id\"]) ?? asString(data[\"sessionId\"]);\n const toolName =\n asString(data[\"tool_name\"]) ??\n asString(data[\"toolName\"]) ??\n asString((data[\"tool\"] as Record<string, unknown> | undefined)?.[\"name\"]);\n const transcriptPath =\n asString(data[\"transcript_path\"]) ?? asString(data[\"transcriptPath\"]);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n const next: ClaudeSettings = { ...(settings ?? {}) };\n const hooks: Record<string, ClaudeHookMatcherGroup[]> = {};\n\n for (const [event, raw] of Object.entries(next.hooks ?? {})) {\n hooks[event] = normalizeEventHooks(raw);\n }\n\n for (const event of CLAUDE_HOOK_EVENTS) {\n const withoutInsights = removeAgentInsightsMatchers(\n hooks[event] ?? [],\n );\n hooks[event] = addAgentInsightsHook(withoutInsights, event);\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveClaudePath(repoRoot, scope);\n const settings = await readJson<ClaudeSettings>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks: Record<string, ClaudeHookMatcherGroup[]> = {};\n let touched = false;\n\n for (const [event, raw] of Object.entries(settings.hooks)) {\n const before = normalizeEventHooks(raw);\n const after = removeAgentInsightsMatchers(before);\n if (after.length !== before.length) touched = true;\n else if (JSON.stringify(after) !== JSON.stringify(before)) {\n touched = true;\n }\n if (after.length > 0) hooks[event] = after;\n }\n\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<ClaudeSettings>(\n resolveClaudePath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((raw) =>\n normalizeEventHooks(raw).some((group) =>\n group.hooks.some((h) => isAgentInsightsCommand(h.command)),\n ),\n );\n },\n};\n\nfunction resolveClaudePath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(repoRoot, \".claude\", \"settings.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nfunction isHookCommand(value: unknown): value is ClaudeHookCommand {\n if (!value || typeof value !== \"object\") return false;\n const entry = value as Record<string, unknown>;\n return entry.type === \"command\" && typeof entry.command === \"string\";\n}\n\n/** Accept matcher groups and migrate legacy flat `{ type, command }` entries. */\nfunction normalizeEventHooks(raw: unknown[]): ClaudeHookMatcherGroup[] {\n const groups: ClaudeHookMatcherGroup[] = [];\n\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const record = entry as Record<string, unknown>;\n\n if (Array.isArray(record.hooks)) {\n const commands = record.hooks.filter(isHookCommand);\n if (commands.length === 0) continue;\n groups.push({\n matcher: typeof record.matcher === \"string\" ? record.matcher : \"\",\n hooks: commands,\n });\n continue;\n }\n\n if (isHookCommand(record)) {\n groups.push({\n matcher: \"\",\n hooks: [record],\n });\n }\n }\n\n return groups;\n}\n\nfunction removeAgentInsightsMatchers(\n matchers: ClaudeHookMatcherGroup[],\n): ClaudeHookMatcherGroup[] {\n const result: ClaudeHookMatcherGroup[] = [];\n\n for (const group of matchers) {\n const hooks = group.hooks.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (hooks.length > 0) {\n result.push({ ...group, hooks });\n }\n }\n\n return result;\n}\n\nfunction addAgentInsightsHook(\n matchers: ClaudeHookMatcherGroup[],\n event: string,\n): ClaudeHookMatcherGroup[] {\n const command = `${HOOK_COMMAND_NAME} hook ${event}`;\n const entry: ClaudeHookCommand = { type: \"command\", command };\n\n for (const group of matchers) {\n if (group.matcher === \"\") {\n if (!group.hooks.some((h) => h.command === command)) {\n group.hooks.push(entry);\n }\n return matchers;\n }\n }\n\n return [...matchers, { matcher: \"\", hooks: [entry] }];\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HooksScope } from \"../config/config.js\";\nimport type { AgentEventType } from \"../types.js\";\nimport type { Adapter, HookPayload, MappedEvent } from \"./types.js\";\n\nconst HOOK_COMMAND_NAME = \"agent-insights\";\n\n/**\n * Cursor native hook names (camelCase) → common schema. Reference:\n * https://cursor.com/docs/hooks\n */\nconst HOOK_MAP: Record<string, AgentEventType> = {\n sessionStart: \"session.start\",\n sessionEnd: \"session.end\",\n beforeSubmitPrompt: \"user.prompt.submit\",\n preToolUse: \"tool.start\",\n postToolUse: \"tool.end\",\n postToolUseFailure: \"tool.failure\",\n subagentStart: \"subagent.start\",\n subagentStop: \"subagent.end\",\n stop: \"agent.stop\",\n preCompact: \"context.compact.start\",\n // Additional events — mapped to existing schema types\n beforeShellExecution: \"tool.start\",\n afterShellExecution: \"tool.end\",\n afterFileEdit: \"tool.end\",\n workspaceOpen: \"session.start\",\n};\n\nexport const CURSOR_HOOK_EVENTS: ReadonlyArray<string> = Object.keys(HOOK_MAP);\n\ntype CursorHookEntry = {\n command: string;\n};\n\ntype CursorHooksConfig = {\n /** Cursor expects schema version 1 at the top level. */\n version?: number;\n hooks?: Record<string, CursorHookEntry[]>;\n [key: string]: unknown;\n};\n\nexport const cursorAdapter: Adapter = {\n id: \"cursor\",\n agent: \"cursor\",\n label: \"Cursor\",\n settingsFile: \".cursor/hooks.json\",\n\n map(payload: HookPayload): MappedEvent | undefined {\n const type = HOOK_MAP[payload.event];\n if (!type) return undefined;\n\n const data = payload.data ?? {};\n // Cursor uses `session_id` in payloads and is identical to `conversation_id`.\n const sessionId =\n asString(data[\"session_id\"]) ??\n asString(data[\"sessionId\"]) ??\n asString(data[\"conversation_id\"]);\n const toolName = asString(data[\"tool_name\"]) ?? asString(data[\"toolName\"]);\n\n // transcript_path is confirmed in all Cursor hook payloads.\n const transcriptPath =\n asString(data[\"transcript_path\"]) ??\n asString(data[\"transcriptPath\"]) ??\n asString(process.env.CURSOR_TRANSCRIPT_PATH);\n\n return {\n type,\n ...(sessionId !== undefined ? { sessionId } : {}),\n ...(toolName !== undefined ? { toolName } : {}),\n ...(transcriptPath !== undefined ? { transcriptPath } : {}),\n };\n },\n\n async install(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n const next: CursorHooksConfig = {\n version: 1,\n ...(settings ?? {}),\n };\n const hooks: Record<string, CursorHookEntry[]> = { ...(next.hooks ?? {}) };\n\n for (const event of CURSOR_HOOK_EVENTS) {\n const existing = (hooks[event] ?? []).filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n existing.push({ command: `${HOOK_COMMAND_NAME} hook ${event}` });\n hooks[event] = existing;\n }\n\n next.hooks = hooks;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n return { written: true, path };\n },\n\n async uninstall(repoRoot: string, scope: HooksScope = \"global\") {\n const path = resolveCursorPath(repoRoot, scope);\n const settings = await readJson<CursorHooksConfig>(path);\n if (!settings?.hooks) return { removed: false, path };\n const hooks = { ...settings.hooks };\n let touched = false;\n for (const [event, entries] of Object.entries(hooks)) {\n const filtered = entries.filter(\n (h) => !isAgentInsightsCommand(h.command),\n );\n if (filtered.length !== entries.length) touched = true;\n if (filtered.length === 0) {\n delete hooks[event];\n } else {\n hooks[event] = filtered;\n }\n }\n settings.hooks = hooks;\n await writeFile(path, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n return { removed: touched, path };\n },\n\n async isInstalled(repoRoot: string, scope: HooksScope = \"global\") {\n const settings = await readJson<CursorHooksConfig>(\n resolveCursorPath(repoRoot, scope),\n );\n if (!settings?.hooks) return false;\n return Object.values(settings.hooks).some((entries) =>\n entries.some((h) => isAgentInsightsCommand(h.command)),\n );\n },\n};\n\nfunction resolveCursorPath(repoRoot: string, scope: HooksScope): string {\n return scope === \"global\"\n ? join(homedir(), \".cursor\", \"hooks.json\")\n : join(repoRoot, \".cursor\", \"hooks.json\");\n}\n\nfunction isAgentInsightsCommand(cmd: string | undefined): boolean {\n if (!cmd) return false;\n return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","import { claudeCodeAdapter } from \"./claude-code.js\";\nimport { cursorAdapter } from \"./cursor.js\";\nimport type { Adapter, AgentId } from \"./types.js\";\n\nexport const adapters: Record<AgentId, Adapter> = {\n \"claude-code\": claudeCodeAdapter,\n cursor: cursorAdapter,\n};\n\nexport const adapterList: ReadonlyArray<Adapter> = Object.values(adapters);\n\nexport function getAdapter(id: AgentId): Adapter {\n return adapters[id];\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { getAnalyzerUrl } from \"./analyzer.js\";\nimport { configFile, logsDir, sessionCacheDir } from \"./paths.js\";\n\nexport type ExporterConfig = {\n type: \"otlp\";\n endpoint: string;\n headers: Record<string, string>;\n dataset: string;\n};\n\nexport type HooksScope = \"global\" | \"local\";\n\nexport type TranscriptStoreConfig =\n | {\n /** Upload via analyzer API using AGENT_INSIGHTS_API_KEY (preferred). */\n type: \"analyzer\";\n }\n | {\n /** Direct upload with a Blob RW token (legacy / advanced). */\n type: \"blob\";\n tokenEnv: string;\n prefix: string;\n }\n | {\n type: \"none\";\n };\n\nexport type SessionAnalysisConfig = {\n /** Server-side enablement; user opts in via `userConsent.sessionAnalysis`. */\n enabled: boolean;\n /** Analyzer endpoint base URL. CLI POSTs to `${analyzerUrl}/api/sessions`. */\n analyzerUrl: string;\n /** GitHub repo for pre-filled new-issue URLs (display only; constructed by analyzer). */\n githubIssueRepo: string;\n};\n\nexport type AgentConfig = {\n enabled: boolean;\n};\n\nexport type AgentInsightsConfig = {\n version: 1;\n enabled: boolean;\n /** Where agent hook entries are written. Default `global` (user home). */\n hooksScope: HooksScope;\n vercel: {\n team: string;\n project: string;\n };\n userConsent: {\n eventTelemetry: boolean;\n transcriptSync: boolean;\n sessionAnalysis: boolean;\n };\n exporter: ExporterConfig;\n transcriptStore: TranscriptStoreConfig;\n sessionAnalysis: SessionAnalysisConfig;\n agents: Record<\"claudeCode\" | \"cursor\", AgentConfig>;\n privacy: {\n capturePrompts: false;\n captureMessages: false;\n captureToolInputs: false;\n captureFileContents: false;\n };\n};\n\nexport function defaultConfig(): AgentInsightsConfig {\n return {\n version: 1,\n enabled: true,\n hooksScope: \"global\",\n vercel: {\n team: \"vercel-labs\",\n project: \"agent-insights\",\n },\n userConsent: {\n eventTelemetry: false,\n transcriptSync: false,\n sessionAnalysis: false,\n },\n exporter: {\n type: \"otlp\",\n endpoint: process.env.AGENT_INSIGHTS_OTEL_ENDPOINT ?? \"\",\n headers: {},\n dataset: \"agent-insights\",\n },\n transcriptStore: {\n // Upload via the analyzer using the SiwV bearer token — no client-side\n // Blob token required. Set `transcriptStore.type` to \"blob\" in\n // config.json to switch to direct uploads with BLOB_READ_WRITE_TOKEN.\n type: \"analyzer\",\n },\n sessionAnalysis: {\n enabled: false,\n analyzerUrl: getAnalyzerUrl(),\n githubIssueRepo: \"vercel-labs/agent-insights\",\n },\n agents: {\n claudeCode: { enabled: false },\n cursor: { enabled: false },\n },\n privacy: {\n capturePrompts: false,\n captureMessages: false,\n captureToolInputs: false,\n captureFileContents: false,\n },\n };\n}\n\nexport async function loadConfig(): Promise<AgentInsightsConfig | undefined> {\n try {\n const raw = await readFile(configFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AgentInsightsConfig>;\n return mergeConfig(defaultConfig(), parsed);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveConfig(cfg: AgentInsightsConfig): Promise<void> {\n await ensureDirs();\n await writeFile(configFile(), `${JSON.stringify(cfg, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function ensureDirs(): Promise<void> {\n await mkdir(dirname(configFile()), { recursive: true });\n await mkdir(sessionCacheDir(), { recursive: true });\n await mkdir(logsDir(), { recursive: true });\n}\n\nfunction mergeConfig(\n base: AgentInsightsConfig,\n patch: Partial<AgentInsightsConfig>,\n): AgentInsightsConfig {\n return {\n ...base,\n ...patch,\n vercel: { ...base.vercel, ...(patch.vercel ?? {}) },\n userConsent: { ...base.userConsent, ...(patch.userConsent ?? {}) },\n exporter: { ...base.exporter, ...(patch.exporter ?? {}) },\n transcriptStore: (patch.transcriptStore ??\n base.transcriptStore) as TranscriptStoreConfig,\n sessionAnalysis: {\n ...base.sessionAnalysis,\n ...(patch.sessionAnalysis ?? {}),\n },\n agents: {\n claudeCode: {\n ...base.agents.claudeCode,\n ...(patch.agents?.claudeCode ?? {}),\n },\n cursor: { ...base.agents.cursor, ...(patch.agents?.cursor ?? {}) },\n },\n privacy: base.privacy,\n };\n}\n","/**\n * Production analyzer deployment. Override via AGENT_INSIGHTS_ANALYZER_URL\n * for local/dev/staging analyzers.\n */\nexport const DEFAULT_ANALYZER_URL = \"https://agent-insights.labs.vercel.dev\";\n\n/** Resolve the analyzer base URL: env var if set, otherwise the prod default. */\nexport function getAnalyzerUrl(): string {\n return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Root directory for agent-insights state. Honors `AGENT_INSIGHTS_HOME`. */\nexport function configRoot(): string {\n return process.env.AGENT_INSIGHTS_HOME ?? join(homedir(), \".agent-insights\");\n}\n\nexport function configFile(): string {\n return join(configRoot(), \"config.json\");\n}\n\n/** API key storage (`chmod 600`). Not checked into git. */\nexport function credentialsFile(): string {\n return join(configRoot(), \"credentials.json\");\n}\n\n/** OAuth token storage (`chmod 600`). Not checked into git. */\nexport function authFile(): string {\n return join(configRoot(), \"auth.json\");\n}\n\n/** Vercel deployment-protection bypass secret (`chmod 600`). */\nexport function bypassSecretFile(): string {\n return join(configRoot(), \"bypass-secret\");\n}\n\nexport function sessionCacheDir(): string {\n return join(configRoot(), \"session-cache\");\n}\n\nexport function logsDir(): string {\n return join(configRoot(), \"logs\");\n}\n\nexport const CLAUDE_SETTINGS_FILE = \".claude/settings.json\";\nexport const CURSOR_HOOKS_FILE = \".cursor/hooks.json\";\n\n/** User-level Claude Code settings (hooks apply in every repo). */\nexport function claudeUserSettingsPath(): string {\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** User-level Cursor hooks (hooks apply in every project). */\nexport function cursorUserHooksPath(): string {\n return join(homedir(), \".cursor\", \"hooks.json\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { put } from \"@vercel/blob\";\nimport { getValidToken } from \"../auth/store.js\";\nimport { getAnalyzerUrl } from \"../config/analyzer.js\";\nimport { withBypassHeader } from \"../config/bypass.js\";\nimport type { TranscriptStoreConfig } from \"../config/config.js\";\n\nexport type TranscriptUploadInput = {\n /** Absolute path on disk to the transcript file. */\n transcriptPath: string;\n /** SHA256-hashed user identifier. Used in the upload pathname. */\n userHash: string;\n /** Session identifier supplied by the agent. */\n sessionId: string;\n /** Agent name (e.g. \"claude-code\"). */\n agent: string;\n /**\n * Vercel email captured at `agent-insights login`. Lets the analyzer\n * join sessions to real user accounts; never logged or written into\n * the transcript itself.\n */\n userEmail?: string;\n};\n\nexport type TranscriptUploadResult = {\n pathname: string;\n url: string;\n size: number;\n};\n\nexport interface TranscriptStore {\n upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult>;\n /** Lightweight health check used by `doctor`. */\n ping(): Promise<{ ok: true } | { ok: false; reason: string }>;\n}\n\nclass NoopTranscriptStore implements TranscriptStore {\n async upload(): Promise<TranscriptUploadResult> {\n throw new Error(\"transcript store disabled (transcriptStore.type=none)\");\n }\n async ping(): Promise<{ ok: false; reason: string }> {\n return { ok: false, reason: \"transcript store disabled\" };\n }\n}\n\nclass BlobTranscriptStore implements TranscriptStore {\n constructor(\n private readonly token: string,\n private readonly prefix: string,\n ) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const body = await readFile(input.transcriptPath);\n const requestedPath = buildPathname(\n this.prefix,\n input.userHash,\n input.sessionId,\n );\n const blob = await put(requestedPath, body, {\n access: \"private\",\n token: this.token,\n contentType: \"application/jsonl\",\n addRandomSuffix: true,\n });\n return { pathname: blob.pathname, url: blob.url, size: body.byteLength };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n if (!this.token) return { ok: false, reason: \"missing token\" };\n return { ok: true };\n }\n}\n\n/**\n * Uploads transcripts via the analyzer's POST /api/sessions/upload endpoint\n * using the Sign-in-with-Vercel bearer token. The analyzer then proxies the\n * write to Vercel Blob with its own server-side credentials, so this client\n * does not need a Blob read/write token.\n */\nclass AnalyzerTranscriptStore implements TranscriptStore {\n constructor(private readonly analyzerUrl: string) {}\n\n async upload(input: TranscriptUploadInput): Promise<TranscriptUploadResult> {\n const token = await getValidToken();\n if (!token) {\n throw new Error(\n \"transcript store: not logged in — run `agent-insights login`\",\n );\n }\n const body = await readFile(input.transcriptPath);\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/jsonl\",\n \"x-session-id\": input.sessionId,\n \"x-agent\": input.agent,\n \"x-user-hash\": input.userHash,\n ...(input.userEmail ? { \"x-user-email\": input.userEmail } : {}),\n });\n const res = await fetch(url, { method: \"POST\", headers, body });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `transcript upload failed (${res.status}): ${text || res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n ok: boolean;\n blobUrl?: string;\n pathname?: string;\n size?: number;\n error?: string;\n };\n if (!json.ok || !json.blobUrl || !json.pathname) {\n throw new Error(`transcript upload error: ${json.error ?? \"unknown\"}`);\n }\n return {\n pathname: json.pathname,\n url: json.blobUrl,\n size: json.size ?? body.byteLength,\n };\n }\n\n async ping(): Promise<{ ok: true } | { ok: false; reason: string }> {\n const token = await getValidToken();\n if (!token) {\n return {\n ok: false,\n reason: \"not logged in — run `agent-insights login`\",\n };\n }\n // Try the actual upload endpoint with an intentionally bad request — a\n // healthy analyzer responds 400 (missing headers); a protection wall\n // responds 401/403 with HTML; an unreachable host throws.\n try {\n const url = new URL(\"/api/sessions/upload\", this.analyzerUrl).toString();\n const headers = await withBypassHeader({\n authorization: `Bearer ${token}`,\n });\n const res = await fetch(url, { method: \"POST\", headers });\n if (res.status === 400) return { ok: true };\n if (res.status === 401 || res.status === 403) {\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n return {\n ok: false,\n reason:\n \"blocked by Vercel deployment protection — set a bypass secret via `agent-insights init --bypass-secret <secret>`\",\n };\n }\n return { ok: false, reason: `analyzer rejected token (${res.status})` };\n }\n return { ok: false, reason: `unexpected status ${res.status}` };\n } catch (err) {\n return {\n ok: false,\n reason: `analyzer unreachable: ${(err as Error).message}`,\n };\n }\n }\n}\n\nexport function createTranscriptStore(\n cfg: TranscriptStoreConfig,\n): TranscriptStore {\n if (cfg.type === \"none\") return new NoopTranscriptStore();\n if (cfg.type === \"analyzer\") {\n return new AnalyzerTranscriptStore(getAnalyzerUrl());\n }\n // type === \"blob\"\n const token = process.env[cfg.tokenEnv];\n if (!token) {\n throw new Error(\n `transcript store: env ${cfg.tokenEnv} is not set — run \\`agent-insights init --force\\` to switch to analyzer-proxied uploads (no token required), or \\`vercel env pull .env.local\\` to keep direct Blob uploads`,\n );\n }\n return new BlobTranscriptStore(token, cfg.prefix);\n}\n\nfunction buildPathname(\n prefix: string,\n userHash: string,\n sessionId: string,\n): string {\n const safePrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n const safeSession = encodeURIComponent(sessionId);\n return `${safePrefix}${userHash}/${safeSession}/transcript.jsonl`;\n}\n","import { chmod, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { authFile } from \"../config/paths.js\";\nimport { refreshAccessToken } from \"./oauth.js\";\n\nexport type AuthState = {\n accessToken: string;\n refreshToken?: string;\n /** Unix epoch milliseconds when the access token expires. */\n expiresAt: number;\n /** Captured at login for per-user analytics joins on the analyzer side. */\n email?: string;\n};\n\nexport async function loadEmail(): Promise<string | undefined> {\n return (await loadAuth())?.email;\n}\n\n/** How many ms before expiry we proactively refresh. */\nconst REFRESH_BUFFER_MS = 60_000;\n\nexport async function loadAuth(): Promise<AuthState | undefined> {\n try {\n const raw = await readFile(authFile(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (typeof parsed.accessToken === \"string\" && parsed.accessToken.length > 0) {\n return parsed as AuthState;\n }\n return undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveAuth(state: AuthState): Promise<void> {\n const path = authFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(state, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows doesn't support chmod — ignore\n }\n}\n\nexport async function clearAuth(): Promise<void> {\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(authFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Returns a valid access token, refreshing silently if needed.\n * Returns `undefined` if not logged in or refresh fails (caller should prompt login).\n */\nexport async function getValidToken(): Promise<string | undefined> {\n const auth = await loadAuth();\n if (!auth) return undefined;\n\n const needsRefresh = Date.now() >= auth.expiresAt - REFRESH_BUFFER_MS;\n if (!needsRefresh) return auth.accessToken;\n\n if (!auth.refreshToken) {\n // Access token expired, no refresh token — clear and force re-login\n await clearAuth();\n return undefined;\n }\n\n try {\n const tokens = await refreshAccessToken(auth.refreshToken);\n const refreshed: AuthState = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? auth.refreshToken,\n expiresAt: Date.now() + tokens.expires_in * 1000,\n ...(auth.email ? { email: auth.email } : {}),\n };\n await saveAuth(refreshed);\n return refreshed.accessToken;\n } catch {\n // Refresh failed (revoked, expired beyond 30 days) — clear and force re-login\n await clearAuth();\n return undefined;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { exec } from \"node:child_process\";\nimport { createServer } from \"node:http\";\nimport { platform } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// OAuth app config — Sign-in-with-Vercel client ID (public, not a secret).\n// Override at runtime via AGENT_INSIGHTS_CLIENT_ID env var.\n// ---------------------------------------------------------------------------\nexport const CLIENT_ID =\n process.env.AGENT_INSIGHTS_CLIENT_ID ?? \"cl_TxqqNFTHcF9kEtM48LBFsYfwjhJ9T8sz\";\n\nconst AUTH_ENDPOINT = \"https://vercel.com/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://api.vercel.com/login/oauth/token\";\nconst USERINFO_ENDPOINT = \"https://api.vercel.com/login/oauth/userinfo\";\nconst REVOKE_ENDPOINT = \"https://api.vercel.com/login/oauth/token/revoke\";\n\n// Fixed port — must match the redirect URI registered in the OAuth app.\nconst CALLBACK_PORT = 9797;\nexport const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;\n\nconst SCOPES = \"openid email profile offline_access\";\n\nexport type TokenResponse = {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n id_token?: string;\n};\n\nexport type UserInfo = {\n sub: string;\n email: string;\n email_verified: boolean;\n name?: string;\n preferred_username?: string;\n picture?: string;\n};\n\n// ---------------------------------------------------------------------------\n// PKCE helpers\n// ---------------------------------------------------------------------------\n\nfunction generateCodeVerifier(): string {\n return randomBytes(48).toString(\"base64url\");\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction generateState(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Code + PKCE flow\n// ---------------------------------------------------------------------------\n\nexport type AuthCodeResult =\n | { status: \"success\"; code: string; state: string }\n | { status: \"error\"; message: string };\n\n/**\n * Starts a localhost callback server, opens the Vercel authorization URL in\n * the browser, and waits for the redirect with the auth code.\n * Resolves (or rejects after timeout) when Vercel redirects back.\n */\nexport async function startPkceFlow(timeoutMs = 120_000): Promise<{\n code: string;\n codeVerifier: string;\n}> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n const authUrl = new URL(AUTH_ENDPOINT);\n authUrl.searchParams.set(\"client_id\", CLIENT_ID);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n authUrl.searchParams.set(\"scope\", SCOPES);\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n\n const url = authUrl.toString();\n const codePromise = listenForCallback(state, timeoutMs);\n openBrowser(url);\n // Always print the URL — browser open may fail silently\n process.stderr.write(`\\nIf the browser did not open, visit:\\n ${url}\\n\\n`);\n\n const code = await codePromise;\n return { code, codeVerifier };\n}\n\nfunction listenForCallback(\n expectedState: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${CALLBACK_PORT}`);\n if (url.pathname !== \"/callback\") {\n res.writeHead(404, { connection: \"close\" });\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n const html = (msg: string) =>\n `<html><body style=\"font-family:sans-serif;text-align:center;padding:80px\">` +\n `<h2>${msg}</h2><p>You can close this tab.</p></body></html>`;\n\n // Tell the browser to drop the socket so the server can shut down cleanly\n // and Node's event loop can exit once the callback returns.\n const headers = { \"content-type\": \"text/html\", connection: \"close\" };\n\n const finish = (\n next: () => void,\n body: string,\n status: number,\n ): void => {\n res.writeHead(status, headers);\n res.end(body, () => {\n clearTimeout(timer);\n server.closeAllConnections?.();\n server.close();\n next();\n });\n };\n\n if (error) {\n finish(\n () => reject(new Error(`OAuth error: ${error}`)),\n html(`Login failed: ${error}`),\n 400,\n );\n return;\n }\n\n if (returnedState !== expectedState) {\n finish(\n () => reject(new Error(\"State mismatch — possible CSRF\")),\n html(\"Invalid state parameter — please try again.\"),\n 400,\n );\n return;\n }\n\n if (!code) {\n finish(\n () => reject(new Error(\"No code in callback\")),\n html(\"No authorization code received.\"),\n 400,\n );\n return;\n }\n\n finish(\n () => resolve(code),\n html(\"✓ Logged in! You can close this tab.\"),\n 200,\n );\n });\n\n const timer = setTimeout(() => {\n server.closeAllConnections?.();\n server.close();\n reject(new Error(\"Login timed out — no response within 2 minutes.\"));\n }, timeoutMs);\n timer.unref();\n\n server.listen(CALLBACK_PORT, \"127.0.0.1\", () => {\n // Server is ready; browser will be opened by caller\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n if (err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${CALLBACK_PORT} is already in use. Close any other agent-insights login process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Token exchange\n// ---------------------------------------------------------------------------\n\nexport async function exchangeCodeForToken(\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: CLIENT_ID,\n code,\n code_verifier: codeVerifier,\n redirect_uri: REDIRECT_URI,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token exchange failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function refreshAccessToken(\n refreshToken: string,\n): Promise<TokenResponse> {\n const res = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: CLIENT_ID,\n refresh_token: refreshToken,\n }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Token refresh failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<TokenResponse>;\n}\n\nexport async function revokeToken(token: string): Promise<void> {\n await fetch(REVOKE_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ token }),\n }).catch(() => {\n // best-effort — don't fail logout if revocation fails\n });\n}\n\nexport async function fetchUserInfo(accessToken: string): Promise<UserInfo> {\n const res = await fetch(USERINFO_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch user info (${res.status})`);\n }\n return res.json() as Promise<UserInfo>;\n}\n\n/** Open a URL in the system browser. Fails silently — callers always print the URL too. */\nexport function openBrowser(url: string): void {\n const os = platform();\n let cmd: string;\n if (os === \"darwin\") cmd = `open \"${url}\"`;\n else if (os === \"win32\") cmd = `start \"\" \"${url}\"`;\n else cmd = `xdg-open \"${url}\"`;\n\n // Detach + unref so the child doesn't keep the event loop alive after login.\n const child = exec(cmd);\n child.on(\"error\", () => {\n // Silently ignore — caller has already printed the URL\n });\n child.unref();\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { bypassSecretFile } from \"./paths.js\";\n\n/**\n * Resolve the Vercel deployment-protection bypass secret.\n * Precedence:\n * 1. `AGENT_INSIGHTS_BYPASS_SECRET` (explicit override)\n * 2. `VERCEL_AUTOMATION_BYPASS_SECRET` (Vercel's standard env)\n * 3. `~/.agent-insights/bypass-secret` (set via `agent-insights init`)\n */\nexport async function loadBypassSecret(): Promise<string | undefined> {\n const fromEnv =\n process.env.AGENT_INSIGHTS_BYPASS_SECRET ||\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET;\n if (fromEnv) return fromEnv;\n\n try {\n const raw = (await readFile(bypassSecretFile(), \"utf8\")).trim();\n return raw || undefined;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw err;\n }\n}\n\nexport async function saveBypassSecret(secret: string): Promise<void> {\n const path = bypassSecretFile();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, secret.trim(), { encoding: \"utf8\", mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows — ignore.\n }\n}\n\nexport async function clearBypassSecret(): Promise<void> {\n try {\n await unlink(bypassSecretFile());\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n}\n\n/**\n * Add the `x-vercel-protection-bypass` header onto an existing headers\n * object if a secret is configured. Mutates and returns the input.\n */\nexport async function withBypassHeader<T extends Record<string, string>>(\n headers: T,\n): Promise<T> {\n const secret = await loadBypassSecret();\n if (secret) {\n (headers as Record<string, string>)[\"x-vercel-protection-bypass\"] = secret;\n }\n return headers;\n}\n"],"mappings":"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","names":["mkdir","readFile","writeFile","homedir","dirname","join","HOOK_COMMAND_NAME","HOOK_MAP","asString","readJson","isAgentInsightsCommand","mkdir","readFile","writeFile","dirname","homedir","join","join","readFile","writeFile","mkdir","dirname","readFile","readFile","writeFile","dirname","mkdir","createHash","readFile","mkdir","dirname","writeFile","unlink","chmod","mkdir","readFile","writeFile","dirname","readFile","readFile"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-insights",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "CLI for AI coding agent lifecycle observability — Claude Code and Cursor hooks → Vercel Blob + optional Slack/GitHub feedback loop.",
5
5
  "keywords": [
6
6
  "claude-code",