agent-insights 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -21,6 +21,9 @@ function configFile() {
21
21
  function authFile() {
22
22
  return join(configRoot(), "auth.json");
23
23
  }
24
+ function bypassSecretFile() {
25
+ return join(configRoot(), "bypass-secret");
26
+ }
24
27
  function sessionCacheDir() {
25
28
  return join(configRoot(), "session-cache");
26
29
  }
@@ -251,9 +254,9 @@ async function saveAuth(state) {
251
254
  }
252
255
  }
253
256
  async function clearAuth() {
254
- const { unlink } = await import("fs/promises");
257
+ const { unlink: unlink2 } = await import("fs/promises");
255
258
  try {
256
- await unlink(authFile());
259
+ await unlink2(authFile());
257
260
  } catch (err) {
258
261
  if (err.code !== "ENOENT") throw err;
259
262
  }
@@ -299,9 +302,17 @@ import { spawn } from "child_process";
299
302
  import { randomUUID } from "crypto";
300
303
 
301
304
  // src/config/config.ts
302
- init_paths();
303
305
  import { mkdir, readFile, writeFile } from "fs/promises";
304
306
  import { dirname } from "path";
307
+
308
+ // src/config/analyzer.ts
309
+ var DEFAULT_ANALYZER_URL = "https://agent-insights.labs.vercel.dev";
310
+ function getAnalyzerUrl() {
311
+ return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;
312
+ }
313
+
314
+ // src/config/config.ts
315
+ init_paths();
305
316
  function defaultConfig() {
306
317
  return {
307
318
  version: 1,
@@ -330,7 +341,7 @@ function defaultConfig() {
330
341
  },
331
342
  sessionAnalysis: {
332
343
  enabled: false,
333
- analyzerUrl: process.env.AGENT_INSIGHTS_ANALYZER_URL ?? "",
344
+ analyzerUrl: getAnalyzerUrl(),
334
345
  githubIssueRepo: "vercel-labs/agent-insights"
335
346
  },
336
347
  agents: {
@@ -878,8 +889,49 @@ init_store();
878
889
 
879
890
  // src/transcript/store.ts
880
891
  init_store();
881
- import { readFile as readFile5 } from "fs/promises";
892
+ import { readFile as readFile6 } from "fs/promises";
882
893
  import { put } from "@vercel/blob";
894
+
895
+ // src/config/bypass.ts
896
+ init_paths();
897
+ import { chmod as chmod2, mkdir as mkdir5, readFile as readFile5, unlink, writeFile as writeFile5 } from "fs/promises";
898
+ import { dirname as dirname5 } from "path";
899
+ async function loadBypassSecret() {
900
+ const fromEnv = process.env.AGENT_INSIGHTS_BYPASS_SECRET || process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
901
+ if (fromEnv) return fromEnv;
902
+ try {
903
+ const raw = (await readFile5(bypassSecretFile(), "utf8")).trim();
904
+ return raw || void 0;
905
+ } catch (err) {
906
+ if (err.code === "ENOENT") return void 0;
907
+ throw err;
908
+ }
909
+ }
910
+ async function saveBypassSecret(secret) {
911
+ const path = bypassSecretFile();
912
+ await mkdir5(dirname5(path), { recursive: true });
913
+ await writeFile5(path, secret.trim(), { encoding: "utf8", mode: 384 });
914
+ try {
915
+ await chmod2(path, 384);
916
+ } catch {
917
+ }
918
+ }
919
+ async function clearBypassSecret() {
920
+ try {
921
+ await unlink(bypassSecretFile());
922
+ } catch (err) {
923
+ if (err.code !== "ENOENT") throw err;
924
+ }
925
+ }
926
+ async function withBypassHeader(headers) {
927
+ const secret = await loadBypassSecret();
928
+ if (secret) {
929
+ headers["x-vercel-protection-bypass"] = secret;
930
+ }
931
+ return headers;
932
+ }
933
+
934
+ // src/transcript/store.ts
883
935
  var NoopTranscriptStore = class {
884
936
  async upload() {
885
937
  throw new Error("transcript store disabled (transcriptStore.type=none)");
@@ -896,7 +948,7 @@ var BlobTranscriptStore = class {
896
948
  token;
897
949
  prefix;
898
950
  async upload(input) {
899
- const body = await readFile5(input.transcriptPath);
951
+ const body = await readFile6(input.transcriptPath);
900
952
  const requestedPath = buildPathname(
901
953
  this.prefix,
902
954
  input.userHash,
@@ -927,19 +979,16 @@ var AnalyzerTranscriptStore = class {
927
979
  "transcript store: not logged in \u2014 run `agent-insights login`"
928
980
  );
929
981
  }
930
- const body = await readFile5(input.transcriptPath);
982
+ const body = await readFile6(input.transcriptPath);
931
983
  const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
932
- const res = await fetch(url, {
933
- method: "POST",
934
- headers: {
935
- authorization: `Bearer ${token}`,
936
- "content-type": "application/jsonl",
937
- "x-session-id": input.sessionId,
938
- "x-agent": input.agent,
939
- "x-user-hash": input.userHash
940
- },
941
- body
984
+ const headers = await withBypassHeader({
985
+ authorization: `Bearer ${token}`,
986
+ "content-type": "application/jsonl",
987
+ "x-session-id": input.sessionId,
988
+ "x-agent": input.agent,
989
+ "x-user-hash": input.userHash
942
990
  });
991
+ const res = await fetch(url, { method: "POST", headers, body });
943
992
  if (!res.ok) {
944
993
  const text = await res.text().catch(() => "");
945
994
  throw new Error(
@@ -957,27 +1006,43 @@ var AnalyzerTranscriptStore = class {
957
1006
  };
958
1007
  }
959
1008
  async ping() {
960
- if (!this.analyzerUrl) {
1009
+ const token = await getValidToken();
1010
+ if (!token) {
961
1011
  return {
962
1012
  ok: false,
963
- reason: "analyzer URL not set (AGENT_INSIGHTS_ANALYZER_URL)"
1013
+ reason: "not logged in \u2014 run `agent-insights login`"
964
1014
  };
965
1015
  }
966
- const token = await getValidToken();
967
- if (!token) {
1016
+ try {
1017
+ const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
1018
+ const headers = await withBypassHeader({
1019
+ authorization: `Bearer ${token}`
1020
+ });
1021
+ const res = await fetch(url, { method: "POST", headers });
1022
+ if (res.status === 400) return { ok: true };
1023
+ if (res.status === 401 || res.status === 403) {
1024
+ const ct = res.headers.get("content-type") ?? "";
1025
+ if (ct.includes("text/html")) {
1026
+ return {
1027
+ ok: false,
1028
+ reason: "blocked by Vercel deployment protection \u2014 set a bypass secret via `agent-insights init --bypass-secret <secret>`"
1029
+ };
1030
+ }
1031
+ return { ok: false, reason: `analyzer rejected token (${res.status})` };
1032
+ }
1033
+ return { ok: false, reason: `unexpected status ${res.status}` };
1034
+ } catch (err) {
968
1035
  return {
969
1036
  ok: false,
970
- reason: "not logged in \u2014 run `agent-insights login`"
1037
+ reason: `analyzer unreachable: ${err.message}`
971
1038
  };
972
1039
  }
973
- return { ok: true };
974
1040
  }
975
1041
  };
976
1042
  function createTranscriptStore(cfg) {
977
1043
  if (cfg.type === "none") return new NoopTranscriptStore();
978
1044
  if (cfg.type === "analyzer") {
979
- const analyzerUrl = process.env.AGENT_INSIGHTS_ANALYZER_URL ?? "";
980
- return new AnalyzerTranscriptStore(analyzerUrl);
1045
+ return new AnalyzerTranscriptStore(getAnalyzerUrl());
981
1046
  }
982
1047
  const token = process.env[cfg.tokenEnv];
983
1048
  if (!token) {
@@ -1093,9 +1158,9 @@ async function runHook(eventName, opts) {
1093
1158
  size: result.size,
1094
1159
  pathname: result.pathname
1095
1160
  });
1096
- if (cfg.userConsent.sessionAnalysis && cfg.sessionAnalysis.analyzerUrl) {
1161
+ if (cfg.userConsent.sessionAnalysis) {
1097
1162
  await notifyAnalyzer({
1098
- analyzerUrl: cfg.sessionAnalysis.analyzerUrl,
1163
+ analyzerUrl: getAnalyzerUrl(),
1099
1164
  payload: {
1100
1165
  sessionId: mapped.sessionId ?? "unknown",
1101
1166
  agent: adapter.id,
@@ -1115,10 +1180,11 @@ async function notifyAnalyzer(opts) {
1115
1180
  try {
1116
1181
  const { getValidToken: getValidToken2 } = await Promise.resolve().then(() => (init_store(), store_exports));
1117
1182
  const token = await getValidToken2();
1118
- const headers = {
1183
+ const baseHeaders = {
1119
1184
  "content-type": "application/json"
1120
1185
  };
1121
- if (token) headers["authorization"] = `Bearer ${token}`;
1186
+ if (token) baseHeaders["authorization"] = `Bearer ${token}`;
1187
+ const headers = await withBypassHeader(baseHeaders);
1122
1188
  const url = new URL("/api/sessions", opts.analyzerUrl).toString();
1123
1189
  const res = await fetch(url, {
1124
1190
  method: "POST",
@@ -1207,7 +1273,7 @@ function parseJsonObject(raw) {
1207
1273
  }
1208
1274
 
1209
1275
  // src/commands/init.ts
1210
- import { checkbox, confirm } from "@inquirer/prompts";
1276
+ import { checkbox, confirm, password } from "@inquirer/prompts";
1211
1277
  import kleur3 from "kleur";
1212
1278
  init_paths();
1213
1279
  init_store();
@@ -1241,7 +1307,7 @@ async function runInit(opts) {
1241
1307
  }
1242
1308
  log.warn(`Config already exists at ${configFile()}.`);
1243
1309
  const overwrite = await safePrompt(
1244
- () => confirm({ message: "Overwrite existing config?", default: false })
1310
+ () => confirm({ message: "Overwrite existing config?", default: true })
1245
1311
  );
1246
1312
  if (overwrite === void 0) return;
1247
1313
  if (!overwrite) {
@@ -1259,8 +1325,9 @@ async function runInit(opts) {
1259
1325
  choices = {
1260
1326
  agents: parseAgents(opts.agents),
1261
1327
  transcripts: opts.transcripts ?? true,
1262
- analysis: opts.analysis ?? false,
1263
- telemetry: opts.telemetry ?? false
1328
+ analysis: opts.analysis ?? true,
1329
+ telemetry: opts.telemetry ?? true,
1330
+ bypassSecret: opts.bypassSecret
1264
1331
  };
1265
1332
  }
1266
1333
  if (choices.agents.size === 0) {
@@ -1275,6 +1342,15 @@ async function runInit(opts) {
1275
1342
  cfg.agents.cursor.enabled = choices.agents.has("cursor");
1276
1343
  await saveConfig(cfg);
1277
1344
  log.ok(`Wrote config to ${configFile()}`);
1345
+ if (choices.bypassSecret !== void 0) {
1346
+ if (choices.bypassSecret === "") {
1347
+ await clearBypassSecret();
1348
+ log.ok("Cleared stored deployment-protection bypass secret.");
1349
+ } else {
1350
+ await saveBypassSecret(choices.bypassSecret);
1351
+ log.ok("Saved deployment-protection bypass secret.");
1352
+ }
1353
+ }
1278
1354
  const installHooks = opts.hooks ?? true;
1279
1355
  const repoRoot = process.cwd();
1280
1356
  if (installHooks) {
@@ -1313,22 +1389,40 @@ async function promptChoices(opts) {
1313
1389
  const analysis = await safePrompt(
1314
1390
  () => confirm({
1315
1391
  message: "Enable SessionEnd analysis (internal LLM review)?",
1316
- default: opts.analysis ?? false
1392
+ default: opts.analysis ?? true
1317
1393
  })
1318
1394
  );
1319
1395
  if (analysis === void 0) return void 0;
1320
1396
  const telemetry = await safePrompt(
1321
1397
  () => confirm({
1322
1398
  message: "Send OTLP event telemetry?",
1323
- default: opts.telemetry ?? false
1399
+ default: opts.telemetry ?? true
1324
1400
  })
1325
1401
  );
1326
1402
  if (telemetry === void 0) return void 0;
1403
+ let bypassSecret;
1404
+ if (opts.bypassSecret !== void 0) {
1405
+ bypassSecret = opts.bypassSecret;
1406
+ } else {
1407
+ const existing = await loadBypassSecret();
1408
+ const answer = await safePrompt(
1409
+ () => password({
1410
+ message: existing ? "Vercel deployment-protection bypass secret (Enter to keep existing, '-' to clear):" : "Vercel deployment-protection bypass secret (Enter to skip):",
1411
+ mask: true
1412
+ })
1413
+ );
1414
+ if (answer === void 0) return void 0;
1415
+ const trimmed = answer.trim();
1416
+ if (trimmed === "") bypassSecret = void 0;
1417
+ else if (trimmed === "-") bypassSecret = "";
1418
+ else bypassSecret = trimmed;
1419
+ }
1327
1420
  return {
1328
1421
  agents: new Set(selected),
1329
1422
  transcripts,
1330
1423
  analysis,
1331
- telemetry
1424
+ telemetry,
1425
+ bypassSecret
1332
1426
  };
1333
1427
  }
1334
1428
  async function safePrompt(run) {
@@ -1475,7 +1569,7 @@ function yn(v) {
1475
1569
  var program = new Command();
1476
1570
  program.name("agent-insights").description(
1477
1571
  "Internal CLI for AI coding agent observability (Claude Code, Cursor)."
1478
- ).version("0.0.10");
1572
+ ).version("0.0.12");
1479
1573
  program.command("login").description("Authenticate with Vercel (Sign in with Vercel).").action(async () => {
1480
1574
  await runLogin();
1481
1575
  });
@@ -1487,7 +1581,10 @@ program.command("init").description(
1487
1581
  ).option(
1488
1582
  "-a, --agents <list>",
1489
1583
  "Comma-separated agent ids (claude-code,cursor); use '*' for all"
1490
- ).option("--transcripts", "Opt into transcript sync to Vercel Blob").option("--no-transcripts", "Skip transcript sync").option("--analysis", "Opt into SessionEnd analysis (Phase 2)").option("--telemetry", "Opt into OTLP event telemetry (Phase 3)").option("--no-hooks", "Skip installing per-agent hooks").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive \u2014 accept defaults / passed flags").action(async (opts) => {
1584
+ ).option("--transcripts", "Opt into transcript sync to Vercel Blob").option("--no-transcripts", "Skip transcript sync").option("--analysis", "Opt into SessionEnd analysis (Phase 2)").option("--telemetry", "Opt into OTLP event telemetry (Phase 3)").option("--no-hooks", "Skip installing per-agent hooks").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive \u2014 accept defaults / passed flags").option(
1585
+ "--bypass-secret <secret>",
1586
+ "Vercel deployment-protection bypass secret (use '' to clear)"
1587
+ ).action(async (opts) => {
1491
1588
  await runInit(opts);
1492
1589
  });
1493
1590
  program.command("status").description("Show current config, consent, and adapter state.").option("--json", "Emit machine-readable JSON").action(async (opts) => {