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 +135 -38
- package/dist/cli.js.map +1 -1
- package/dist/index.js +68 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
982
|
+
const body = await readFile6(input.transcriptPath);
|
|
931
983
|
const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
-
|
|
1009
|
+
const token = await getValidToken();
|
|
1010
|
+
if (!token) {
|
|
961
1011
|
return {
|
|
962
1012
|
ok: false,
|
|
963
|
-
reason: "
|
|
1013
|
+
reason: "not logged in \u2014 run `agent-insights login`"
|
|
964
1014
|
};
|
|
965
1015
|
}
|
|
966
|
-
|
|
967
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
1161
|
+
if (cfg.userConsent.sessionAnalysis) {
|
|
1097
1162
|
await notifyAnalyzer({
|
|
1098
|
-
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
|
|
1183
|
+
const baseHeaders = {
|
|
1119
1184
|
"content-type": "application/json"
|
|
1120
1185
|
};
|
|
1121
|
-
if (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:
|
|
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 ??
|
|
1263
|
-
telemetry: opts.telemetry ??
|
|
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 ??
|
|
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 ??
|
|
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.
|
|
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").
|
|
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) => {
|