agent-insights 0.0.10 → 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 +219 -60
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +142 -44
- 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
|
}
|
|
@@ -219,11 +222,15 @@ __export(store_exports, {
|
|
|
219
222
|
clearAuth: () => clearAuth,
|
|
220
223
|
getValidToken: () => getValidToken,
|
|
221
224
|
loadAuth: () => loadAuth,
|
|
225
|
+
loadEmail: () => loadEmail,
|
|
222
226
|
saveAuth: () => saveAuth
|
|
223
227
|
});
|
|
224
228
|
import { chmod, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
225
229
|
import { dirname as dirname4 } from "path";
|
|
226
230
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
231
|
+
async function loadEmail() {
|
|
232
|
+
return (await loadAuth())?.email;
|
|
233
|
+
}
|
|
227
234
|
async function loadAuth() {
|
|
228
235
|
try {
|
|
229
236
|
const raw = await readFile4(authFile(), "utf8");
|
|
@@ -251,9 +258,9 @@ async function saveAuth(state) {
|
|
|
251
258
|
}
|
|
252
259
|
}
|
|
253
260
|
async function clearAuth() {
|
|
254
|
-
const { unlink } = await import("fs/promises");
|
|
261
|
+
const { unlink: unlink2 } = await import("fs/promises");
|
|
255
262
|
try {
|
|
256
|
-
await
|
|
263
|
+
await unlink2(authFile());
|
|
257
264
|
} catch (err) {
|
|
258
265
|
if (err.code !== "ENOENT") throw err;
|
|
259
266
|
}
|
|
@@ -272,7 +279,8 @@ async function getValidToken() {
|
|
|
272
279
|
const refreshed = {
|
|
273
280
|
accessToken: tokens.access_token,
|
|
274
281
|
refreshToken: tokens.refresh_token ?? auth.refreshToken,
|
|
275
|
-
expiresAt: Date.now() + tokens.expires_in * 1e3
|
|
282
|
+
expiresAt: Date.now() + tokens.expires_in * 1e3,
|
|
283
|
+
...auth.email ? { email: auth.email } : {}
|
|
276
284
|
};
|
|
277
285
|
await saveAuth(refreshed);
|
|
278
286
|
return refreshed.accessToken;
|
|
@@ -299,9 +307,17 @@ import { spawn } from "child_process";
|
|
|
299
307
|
import { randomUUID } from "crypto";
|
|
300
308
|
|
|
301
309
|
// src/config/config.ts
|
|
302
|
-
init_paths();
|
|
303
310
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
304
311
|
import { dirname } from "path";
|
|
312
|
+
|
|
313
|
+
// src/config/analyzer.ts
|
|
314
|
+
var DEFAULT_ANALYZER_URL = "https://agent-insights.labs.vercel.dev";
|
|
315
|
+
function getAnalyzerUrl() {
|
|
316
|
+
return process.env.AGENT_INSIGHTS_ANALYZER_URL || DEFAULT_ANALYZER_URL;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/config/config.ts
|
|
320
|
+
init_paths();
|
|
305
321
|
function defaultConfig() {
|
|
306
322
|
return {
|
|
307
323
|
version: 1,
|
|
@@ -330,7 +346,7 @@ function defaultConfig() {
|
|
|
330
346
|
},
|
|
331
347
|
sessionAnalysis: {
|
|
332
348
|
enabled: false,
|
|
333
|
-
analyzerUrl:
|
|
349
|
+
analyzerUrl: getAnalyzerUrl(),
|
|
334
350
|
githubIssueRepo: "vercel-labs/agent-insights"
|
|
335
351
|
},
|
|
336
352
|
agents: {
|
|
@@ -647,16 +663,15 @@ var claudeCodeAdapter = {
|
|
|
647
663
|
const path = resolveClaudePath(repoRoot, scope);
|
|
648
664
|
const settings = await readJson(path);
|
|
649
665
|
const next = { ...settings ?? {} };
|
|
650
|
-
const hooks = {
|
|
666
|
+
const hooks = {};
|
|
667
|
+
for (const [event, raw] of Object.entries(next.hooks ?? {})) {
|
|
668
|
+
hooks[event] = normalizeEventHooks(raw);
|
|
669
|
+
}
|
|
651
670
|
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
652
|
-
const
|
|
653
|
-
|
|
671
|
+
const withoutInsights = removeAgentInsightsMatchers(
|
|
672
|
+
hooks[event] ?? []
|
|
654
673
|
);
|
|
655
|
-
|
|
656
|
-
type: "command",
|
|
657
|
-
command: `${HOOK_COMMAND_NAME} hook ${event}`
|
|
658
|
-
});
|
|
659
|
-
hooks[event] = existing;
|
|
674
|
+
hooks[event] = addAgentInsightsHook(withoutInsights, event);
|
|
660
675
|
}
|
|
661
676
|
next.hooks = hooks;
|
|
662
677
|
await mkdir2(dirname2(path), { recursive: true });
|
|
@@ -668,18 +683,16 @@ var claudeCodeAdapter = {
|
|
|
668
683
|
const path = resolveClaudePath(repoRoot, scope);
|
|
669
684
|
const settings = await readJson(path);
|
|
670
685
|
if (!settings?.hooks) return { removed: false, path };
|
|
671
|
-
const hooks = {
|
|
686
|
+
const hooks = {};
|
|
672
687
|
let touched = false;
|
|
673
|
-
for (const [event,
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
);
|
|
677
|
-
if (
|
|
678
|
-
|
|
679
|
-
delete hooks[event];
|
|
680
|
-
} else {
|
|
681
|
-
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;
|
|
682
694
|
}
|
|
695
|
+
if (after.length > 0) hooks[event] = after;
|
|
683
696
|
}
|
|
684
697
|
settings.hooks = hooks;
|
|
685
698
|
await writeFile2(path, `${JSON.stringify(settings, null, 2)}
|
|
@@ -692,7 +705,9 @@ var claudeCodeAdapter = {
|
|
|
692
705
|
);
|
|
693
706
|
if (!settings?.hooks) return false;
|
|
694
707
|
return Object.values(settings.hooks).some(
|
|
695
|
-
(
|
|
708
|
+
(raw) => normalizeEventHooks(raw).some(
|
|
709
|
+
(group) => group.hooks.some((h) => isAgentInsightsCommand(h.command))
|
|
710
|
+
)
|
|
696
711
|
);
|
|
697
712
|
}
|
|
698
713
|
};
|
|
@@ -703,6 +718,59 @@ function isAgentInsightsCommand(cmd) {
|
|
|
703
718
|
if (!cmd) return false;
|
|
704
719
|
return cmd.startsWith(`${HOOK_COMMAND_NAME} hook`);
|
|
705
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
|
+
}
|
|
706
774
|
async function readJson(path) {
|
|
707
775
|
try {
|
|
708
776
|
const raw = await readFile2(path, "utf8");
|
|
@@ -878,8 +946,49 @@ init_store();
|
|
|
878
946
|
|
|
879
947
|
// src/transcript/store.ts
|
|
880
948
|
init_store();
|
|
881
|
-
import { readFile as
|
|
949
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
882
950
|
import { put } from "@vercel/blob";
|
|
951
|
+
|
|
952
|
+
// src/config/bypass.ts
|
|
953
|
+
init_paths();
|
|
954
|
+
import { chmod as chmod2, mkdir as mkdir5, readFile as readFile5, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
955
|
+
import { dirname as dirname5 } from "path";
|
|
956
|
+
async function loadBypassSecret() {
|
|
957
|
+
const fromEnv = process.env.AGENT_INSIGHTS_BYPASS_SECRET || process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
958
|
+
if (fromEnv) return fromEnv;
|
|
959
|
+
try {
|
|
960
|
+
const raw = (await readFile5(bypassSecretFile(), "utf8")).trim();
|
|
961
|
+
return raw || void 0;
|
|
962
|
+
} catch (err) {
|
|
963
|
+
if (err.code === "ENOENT") return void 0;
|
|
964
|
+
throw err;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
async function saveBypassSecret(secret) {
|
|
968
|
+
const path = bypassSecretFile();
|
|
969
|
+
await mkdir5(dirname5(path), { recursive: true });
|
|
970
|
+
await writeFile5(path, secret.trim(), { encoding: "utf8", mode: 384 });
|
|
971
|
+
try {
|
|
972
|
+
await chmod2(path, 384);
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
async function clearBypassSecret() {
|
|
977
|
+
try {
|
|
978
|
+
await unlink(bypassSecretFile());
|
|
979
|
+
} catch (err) {
|
|
980
|
+
if (err.code !== "ENOENT") throw err;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async function withBypassHeader(headers) {
|
|
984
|
+
const secret = await loadBypassSecret();
|
|
985
|
+
if (secret) {
|
|
986
|
+
headers["x-vercel-protection-bypass"] = secret;
|
|
987
|
+
}
|
|
988
|
+
return headers;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/transcript/store.ts
|
|
883
992
|
var NoopTranscriptStore = class {
|
|
884
993
|
async upload() {
|
|
885
994
|
throw new Error("transcript store disabled (transcriptStore.type=none)");
|
|
@@ -896,7 +1005,7 @@ var BlobTranscriptStore = class {
|
|
|
896
1005
|
token;
|
|
897
1006
|
prefix;
|
|
898
1007
|
async upload(input) {
|
|
899
|
-
const body = await
|
|
1008
|
+
const body = await readFile6(input.transcriptPath);
|
|
900
1009
|
const requestedPath = buildPathname(
|
|
901
1010
|
this.prefix,
|
|
902
1011
|
input.userHash,
|
|
@@ -927,19 +1036,17 @@ var AnalyzerTranscriptStore = class {
|
|
|
927
1036
|
"transcript store: not logged in \u2014 run `agent-insights login`"
|
|
928
1037
|
);
|
|
929
1038
|
}
|
|
930
|
-
const body = await
|
|
1039
|
+
const body = await readFile6(input.transcriptPath);
|
|
931
1040
|
const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
"x-user-hash": input.userHash
|
|
940
|
-
},
|
|
941
|
-
body
|
|
1041
|
+
const headers = await withBypassHeader({
|
|
1042
|
+
authorization: `Bearer ${token}`,
|
|
1043
|
+
"content-type": "application/jsonl",
|
|
1044
|
+
"x-session-id": input.sessionId,
|
|
1045
|
+
"x-agent": input.agent,
|
|
1046
|
+
"x-user-hash": input.userHash,
|
|
1047
|
+
...input.userEmail ? { "x-user-email": input.userEmail } : {}
|
|
942
1048
|
});
|
|
1049
|
+
const res = await fetch(url, { method: "POST", headers, body });
|
|
943
1050
|
if (!res.ok) {
|
|
944
1051
|
const text = await res.text().catch(() => "");
|
|
945
1052
|
throw new Error(
|
|
@@ -957,27 +1064,43 @@ var AnalyzerTranscriptStore = class {
|
|
|
957
1064
|
};
|
|
958
1065
|
}
|
|
959
1066
|
async ping() {
|
|
960
|
-
|
|
1067
|
+
const token = await getValidToken();
|
|
1068
|
+
if (!token) {
|
|
961
1069
|
return {
|
|
962
1070
|
ok: false,
|
|
963
|
-
reason: "
|
|
1071
|
+
reason: "not logged in \u2014 run `agent-insights login`"
|
|
964
1072
|
};
|
|
965
1073
|
}
|
|
966
|
-
|
|
967
|
-
|
|
1074
|
+
try {
|
|
1075
|
+
const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
|
|
1076
|
+
const headers = await withBypassHeader({
|
|
1077
|
+
authorization: `Bearer ${token}`
|
|
1078
|
+
});
|
|
1079
|
+
const res = await fetch(url, { method: "POST", headers });
|
|
1080
|
+
if (res.status === 400) return { ok: true };
|
|
1081
|
+
if (res.status === 401 || res.status === 403) {
|
|
1082
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
1083
|
+
if (ct.includes("text/html")) {
|
|
1084
|
+
return {
|
|
1085
|
+
ok: false,
|
|
1086
|
+
reason: "blocked by Vercel deployment protection \u2014 set a bypass secret via `agent-insights init --bypass-secret <secret>`"
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
return { ok: false, reason: `analyzer rejected token (${res.status})` };
|
|
1090
|
+
}
|
|
1091
|
+
return { ok: false, reason: `unexpected status ${res.status}` };
|
|
1092
|
+
} catch (err) {
|
|
968
1093
|
return {
|
|
969
1094
|
ok: false,
|
|
970
|
-
reason:
|
|
1095
|
+
reason: `analyzer unreachable: ${err.message}`
|
|
971
1096
|
};
|
|
972
1097
|
}
|
|
973
|
-
return { ok: true };
|
|
974
1098
|
}
|
|
975
1099
|
};
|
|
976
1100
|
function createTranscriptStore(cfg) {
|
|
977
1101
|
if (cfg.type === "none") return new NoopTranscriptStore();
|
|
978
1102
|
if (cfg.type === "analyzer") {
|
|
979
|
-
|
|
980
|
-
return new AnalyzerTranscriptStore(analyzerUrl);
|
|
1103
|
+
return new AnalyzerTranscriptStore(getAnalyzerUrl());
|
|
981
1104
|
}
|
|
982
1105
|
const token = process.env[cfg.tokenEnv];
|
|
983
1106
|
if (!token) {
|
|
@@ -1055,6 +1178,7 @@ async function runDoctor(opts) {
|
|
|
1055
1178
|
}
|
|
1056
1179
|
|
|
1057
1180
|
// src/commands/hook.ts
|
|
1181
|
+
init_store();
|
|
1058
1182
|
async function runHook(eventName, opts) {
|
|
1059
1183
|
const cfg = await loadConfig();
|
|
1060
1184
|
if (!cfg || !cfg.enabled) {
|
|
@@ -1083,19 +1207,21 @@ async function runHook(eventName, opts) {
|
|
|
1083
1207
|
if (mapped.type === "session.end" && cfg.userConsent.transcriptSync && mapped.transcriptPath) {
|
|
1084
1208
|
try {
|
|
1085
1209
|
const store = createTranscriptStore(cfg.transcriptStore);
|
|
1210
|
+
const userEmail = await loadEmail();
|
|
1086
1211
|
const result = await store.upload({
|
|
1087
1212
|
transcriptPath: mapped.transcriptPath,
|
|
1088
1213
|
userHash: event.userHash ?? userHash(),
|
|
1089
1214
|
sessionId: mapped.sessionId ?? "unknown",
|
|
1090
|
-
agent: adapter.id
|
|
1215
|
+
agent: adapter.id,
|
|
1216
|
+
...userEmail ? { userEmail } : {}
|
|
1091
1217
|
});
|
|
1092
1218
|
debug("transcript uploaded", {
|
|
1093
1219
|
size: result.size,
|
|
1094
1220
|
pathname: result.pathname
|
|
1095
1221
|
});
|
|
1096
|
-
if (cfg.userConsent.sessionAnalysis
|
|
1222
|
+
if (cfg.userConsent.sessionAnalysis) {
|
|
1097
1223
|
await notifyAnalyzer({
|
|
1098
|
-
analyzerUrl:
|
|
1224
|
+
analyzerUrl: getAnalyzerUrl(),
|
|
1099
1225
|
payload: {
|
|
1100
1226
|
sessionId: mapped.sessionId ?? "unknown",
|
|
1101
1227
|
agent: adapter.id,
|
|
@@ -1115,10 +1241,11 @@ async function notifyAnalyzer(opts) {
|
|
|
1115
1241
|
try {
|
|
1116
1242
|
const { getValidToken: getValidToken2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
1117
1243
|
const token = await getValidToken2();
|
|
1118
|
-
const
|
|
1244
|
+
const baseHeaders = {
|
|
1119
1245
|
"content-type": "application/json"
|
|
1120
1246
|
};
|
|
1121
|
-
if (token)
|
|
1247
|
+
if (token) baseHeaders["authorization"] = `Bearer ${token}`;
|
|
1248
|
+
const headers = await withBypassHeader(baseHeaders);
|
|
1122
1249
|
const url = new URL("/api/sessions", opts.analyzerUrl).toString();
|
|
1123
1250
|
const res = await fetch(url, {
|
|
1124
1251
|
method: "POST",
|
|
@@ -1207,7 +1334,7 @@ function parseJsonObject(raw) {
|
|
|
1207
1334
|
}
|
|
1208
1335
|
|
|
1209
1336
|
// src/commands/init.ts
|
|
1210
|
-
import { checkbox, confirm } from "@inquirer/prompts";
|
|
1337
|
+
import { checkbox, confirm, password } from "@inquirer/prompts";
|
|
1211
1338
|
import kleur3 from "kleur";
|
|
1212
1339
|
init_paths();
|
|
1213
1340
|
init_store();
|
|
@@ -1241,7 +1368,7 @@ async function runInit(opts) {
|
|
|
1241
1368
|
}
|
|
1242
1369
|
log.warn(`Config already exists at ${configFile()}.`);
|
|
1243
1370
|
const overwrite = await safePrompt(
|
|
1244
|
-
() => confirm({ message: "Overwrite existing config?", default:
|
|
1371
|
+
() => confirm({ message: "Overwrite existing config?", default: true })
|
|
1245
1372
|
);
|
|
1246
1373
|
if (overwrite === void 0) return;
|
|
1247
1374
|
if (!overwrite) {
|
|
@@ -1259,8 +1386,9 @@ async function runInit(opts) {
|
|
|
1259
1386
|
choices = {
|
|
1260
1387
|
agents: parseAgents(opts.agents),
|
|
1261
1388
|
transcripts: opts.transcripts ?? true,
|
|
1262
|
-
analysis: opts.analysis ??
|
|
1263
|
-
telemetry: opts.telemetry ??
|
|
1389
|
+
analysis: opts.analysis ?? true,
|
|
1390
|
+
telemetry: opts.telemetry ?? true,
|
|
1391
|
+
bypassSecret: opts.bypassSecret
|
|
1264
1392
|
};
|
|
1265
1393
|
}
|
|
1266
1394
|
if (choices.agents.size === 0) {
|
|
@@ -1275,6 +1403,15 @@ async function runInit(opts) {
|
|
|
1275
1403
|
cfg.agents.cursor.enabled = choices.agents.has("cursor");
|
|
1276
1404
|
await saveConfig(cfg);
|
|
1277
1405
|
log.ok(`Wrote config to ${configFile()}`);
|
|
1406
|
+
if (choices.bypassSecret !== void 0) {
|
|
1407
|
+
if (choices.bypassSecret === "") {
|
|
1408
|
+
await clearBypassSecret();
|
|
1409
|
+
log.ok("Cleared stored deployment-protection bypass secret.");
|
|
1410
|
+
} else {
|
|
1411
|
+
await saveBypassSecret(choices.bypassSecret);
|
|
1412
|
+
log.ok("Saved deployment-protection bypass secret.");
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1278
1415
|
const installHooks = opts.hooks ?? true;
|
|
1279
1416
|
const repoRoot = process.cwd();
|
|
1280
1417
|
if (installHooks) {
|
|
@@ -1313,22 +1450,40 @@ async function promptChoices(opts) {
|
|
|
1313
1450
|
const analysis = await safePrompt(
|
|
1314
1451
|
() => confirm({
|
|
1315
1452
|
message: "Enable SessionEnd analysis (internal LLM review)?",
|
|
1316
|
-
default: opts.analysis ??
|
|
1453
|
+
default: opts.analysis ?? true
|
|
1317
1454
|
})
|
|
1318
1455
|
);
|
|
1319
1456
|
if (analysis === void 0) return void 0;
|
|
1320
1457
|
const telemetry = await safePrompt(
|
|
1321
1458
|
() => confirm({
|
|
1322
1459
|
message: "Send OTLP event telemetry?",
|
|
1323
|
-
default: opts.telemetry ??
|
|
1460
|
+
default: opts.telemetry ?? true
|
|
1324
1461
|
})
|
|
1325
1462
|
);
|
|
1326
1463
|
if (telemetry === void 0) return void 0;
|
|
1464
|
+
let bypassSecret;
|
|
1465
|
+
if (opts.bypassSecret !== void 0) {
|
|
1466
|
+
bypassSecret = opts.bypassSecret;
|
|
1467
|
+
} else {
|
|
1468
|
+
const existing = await loadBypassSecret();
|
|
1469
|
+
const answer = await safePrompt(
|
|
1470
|
+
() => password({
|
|
1471
|
+
message: existing ? "Vercel deployment-protection bypass secret (Enter to keep existing, '-' to clear):" : "Vercel deployment-protection bypass secret (Enter to skip):",
|
|
1472
|
+
mask: true
|
|
1473
|
+
})
|
|
1474
|
+
);
|
|
1475
|
+
if (answer === void 0) return void 0;
|
|
1476
|
+
const trimmed = answer.trim();
|
|
1477
|
+
if (trimmed === "") bypassSecret = void 0;
|
|
1478
|
+
else if (trimmed === "-") bypassSecret = "";
|
|
1479
|
+
else bypassSecret = trimmed;
|
|
1480
|
+
}
|
|
1327
1481
|
return {
|
|
1328
1482
|
agents: new Set(selected),
|
|
1329
1483
|
transcripts,
|
|
1330
1484
|
analysis,
|
|
1331
|
-
telemetry
|
|
1485
|
+
telemetry,
|
|
1486
|
+
bypassSecret
|
|
1332
1487
|
};
|
|
1333
1488
|
}
|
|
1334
1489
|
async function safePrompt(run) {
|
|
@@ -1399,7 +1554,8 @@ async function runLogin() {
|
|
|
1399
1554
|
await saveAuth({
|
|
1400
1555
|
accessToken: tokens.access_token,
|
|
1401
1556
|
...tokens.refresh_token !== void 0 ? { refreshToken: tokens.refresh_token } : {},
|
|
1402
|
-
expiresAt: Date.now() + tokens.expires_in * 1e3
|
|
1557
|
+
expiresAt: Date.now() + tokens.expires_in * 1e3,
|
|
1558
|
+
email: userInfo2.email
|
|
1403
1559
|
});
|
|
1404
1560
|
const name = userInfo2.preferred_username ?? userInfo2.email;
|
|
1405
1561
|
log.ok(`Logged in as ${kleur4.bold(name)} (${userInfo2.email})`);
|
|
@@ -1475,7 +1631,7 @@ function yn(v) {
|
|
|
1475
1631
|
var program = new Command();
|
|
1476
1632
|
program.name("agent-insights").description(
|
|
1477
1633
|
"Internal CLI for AI coding agent observability (Claude Code, Cursor)."
|
|
1478
|
-
).version("0.0.
|
|
1634
|
+
).version("0.0.13");
|
|
1479
1635
|
program.command("login").description("Authenticate with Vercel (Sign in with Vercel).").action(async () => {
|
|
1480
1636
|
await runLogin();
|
|
1481
1637
|
});
|
|
@@ -1487,7 +1643,10 @@ program.command("init").description(
|
|
|
1487
1643
|
).option(
|
|
1488
1644
|
"-a, --agents <list>",
|
|
1489
1645
|
"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").
|
|
1646
|
+
).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(
|
|
1647
|
+
"--bypass-secret <secret>",
|
|
1648
|
+
"Vercel deployment-protection bypass secret (use '' to clear)"
|
|
1649
|
+
).action(async (opts) => {
|
|
1491
1650
|
await runInit(opts);
|
|
1492
1651
|
});
|
|
1493
1652
|
program.command("status").description("Show current config, consent, and adapter state.").option("--json", "Emit machine-readable JSON").action(async (opts) => {
|