@glasstrace/sdk 1.1.0 → 1.1.2

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.
Files changed (66) hide show
  1. package/README.md +78 -1
  2. package/dist/{chunk-55FBXXER.js → chunk-2M57EO6U.js} +2 -2
  3. package/dist/chunk-3LILTM3T.js +384 -0
  4. package/dist/chunk-3LILTM3T.js.map +1 -0
  5. package/dist/{chunk-KE7MCPO5.js → chunk-4EZ6JTDG.js} +2 -2
  6. package/dist/{chunk-67RIOAXV.js → chunk-6RNBUUBR.js} +2 -2
  7. package/dist/{chunk-DO2YPMQ5.js → chunk-C567H5EQ.js} +23 -5
  8. package/dist/chunk-C567H5EQ.js.map +1 -0
  9. package/dist/{chunk-UGJ3X4CT.js → chunk-DST4UBXU.js} +2 -2
  10. package/dist/{chunk-DXRZKKSO.js → chunk-NB7GJE4S.js} +2 -4
  11. package/dist/chunk-NB7GJE4S.js.map +1 -0
  12. package/dist/{chunk-HAU66QBQ.js → chunk-P4OYPFQ5.js} +9 -9
  13. package/dist/chunk-P4OYPFQ5.js.map +1 -0
  14. package/dist/{chunk-LU3PPAOQ.js → chunk-UJ2JC7PZ.js} +4 -4
  15. package/dist/chunk-UJ2JC7PZ.js.map +1 -0
  16. package/dist/{chunk-TQ54WLCZ.js → chunk-X5MAXP5T.js} +2 -1
  17. package/dist/{chunk-ZBTC5QIQ.js → chunk-Z35HKVSO.js} +137 -10
  18. package/dist/chunk-Z35HKVSO.js.map +1 -0
  19. package/dist/cli/init.cjs +1313 -1118
  20. package/dist/cli/init.cjs.map +1 -1
  21. package/dist/cli/init.js +456 -72
  22. package/dist/cli/init.js.map +1 -1
  23. package/dist/cli/mcp-add.cjs +257 -85
  24. package/dist/cli/mcp-add.cjs.map +1 -1
  25. package/dist/cli/mcp-add.d.cts +35 -4
  26. package/dist/cli/mcp-add.d.ts +35 -4
  27. package/dist/cli/mcp-add.js +61 -25
  28. package/dist/cli/mcp-add.js.map +1 -1
  29. package/dist/cli/status.cjs.map +1 -1
  30. package/dist/cli/status.js +1 -1
  31. package/dist/cli/uninit.cjs +1 -1
  32. package/dist/cli/uninit.cjs.map +1 -1
  33. package/dist/cli/uninit.js +4 -4
  34. package/dist/cli/validate.cjs +14162 -2
  35. package/dist/cli/validate.cjs.map +1 -1
  36. package/dist/cli/validate.d.cts +7 -3
  37. package/dist/cli/validate.d.ts +7 -3
  38. package/dist/cli/validate.js +25 -2
  39. package/dist/cli/validate.js.map +1 -1
  40. package/dist/edge-entry.js +2 -2
  41. package/dist/index.cjs +423 -12
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.js +5 -5
  44. package/dist/{monorepo-N5Z63XP7.js → monorepo-PFVNPQ6X.js} +3 -3
  45. package/dist/node-entry.cjs +423 -12
  46. package/dist/node-entry.cjs.map +1 -1
  47. package/dist/node-entry.js +7 -7
  48. package/dist/node-subpath.js +3 -3
  49. package/dist/{source-map-uploader-BJIXRLJ6.js → source-map-uploader-DPUUCLNW.js} +3 -3
  50. package/package.json +1 -1
  51. package/dist/chunk-DO2YPMQ5.js.map +0 -1
  52. package/dist/chunk-DXRZKKSO.js.map +0 -1
  53. package/dist/chunk-HAU66QBQ.js.map +0 -1
  54. package/dist/chunk-IP4NMDJK.js +0 -98
  55. package/dist/chunk-IP4NMDJK.js.map +0 -1
  56. package/dist/chunk-LU3PPAOQ.js.map +0 -1
  57. package/dist/chunk-O63DJKIJ.js +0 -460
  58. package/dist/chunk-O63DJKIJ.js.map +0 -1
  59. package/dist/chunk-ZBTC5QIQ.js.map +0 -1
  60. /package/dist/{chunk-55FBXXER.js.map → chunk-2M57EO6U.js.map} +0 -0
  61. /package/dist/{chunk-KE7MCPO5.js.map → chunk-4EZ6JTDG.js.map} +0 -0
  62. /package/dist/{chunk-67RIOAXV.js.map → chunk-6RNBUUBR.js.map} +0 -0
  63. /package/dist/{chunk-UGJ3X4CT.js.map → chunk-DST4UBXU.js.map} +0 -0
  64. /package/dist/{chunk-TQ54WLCZ.js.map → chunk-X5MAXP5T.js.map} +0 -0
  65. /package/dist/{monorepo-N5Z63XP7.js.map → monorepo-PFVNPQ6X.js.map} +0 -0
  66. /package/dist/{source-map-uploader-BJIXRLJ6.js.map → source-map-uploader-DPUUCLNW.js.map} +0 -0
package/dist/cli/init.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveProjectRoot
4
- } from "../chunk-55FBXXER.js";
4
+ } from "../chunk-2M57EO6U.js";
5
5
  import {
6
6
  verifyInitReachable
7
- } from "../chunk-DO2YPMQ5.js";
7
+ } from "../chunk-C567H5EQ.js";
8
8
  import {
9
9
  buildImportGraph
10
- } from "../chunk-UGJ3X4CT.js";
10
+ } from "../chunk-DST4UBXU.js";
11
11
  import {
12
12
  resolveConfig
13
13
  } from "../chunk-VUZCLMIX.js";
@@ -21,7 +21,7 @@ import {
21
21
  unwrapCJSExport,
22
22
  unwrapExport,
23
23
  writeDiscoveryFile
24
- } from "../chunk-LU3PPAOQ.js";
24
+ } from "../chunk-UJ2JC7PZ.js";
25
25
  import {
26
26
  detectAgents,
27
27
  generateInfoSection,
@@ -29,35 +29,406 @@ import {
29
29
  injectInfoSection,
30
30
  updateGitignore,
31
31
  writeMcpConfig
32
- } from "../chunk-HAU66QBQ.js";
32
+ } from "../chunk-P4OYPFQ5.js";
33
33
  import {
34
+ MCP_ENDPOINT,
34
35
  getOrCreateAnonKey,
35
- readAnonKey
36
- } from "../chunk-IP4NMDJK.js";
37
- import {
38
- addCoverageMapEnv,
36
+ identityFingerprint,
39
37
  isDevApiKey,
40
38
  mcpConfigMatches,
39
+ readAnonKey,
41
40
  readEnvLocalApiKey,
42
- resolveInstrumentationTarget,
43
- scaffoldEnvLocal,
44
- scaffoldGitignore,
45
- scaffoldInstrumentation,
46
- scaffoldMcpMarker,
47
- scaffoldNextConfig
48
- } from "../chunk-O63DJKIJ.js";
49
- import "../chunk-TQ54WLCZ.js";
41
+ resolveEffectiveMcpCredential,
42
+ writeMcpMarker
43
+ } from "../chunk-3LILTM3T.js";
44
+ import "../chunk-X5MAXP5T.js";
50
45
  import {
51
- MCP_ENDPOINT,
52
46
  NEXT_CONFIG_NAMES,
53
47
  formatAgentName
54
- } from "../chunk-DXRZKKSO.js";
48
+ } from "../chunk-NB7GJE4S.js";
55
49
  import "../chunk-NSBPE2FW.js";
56
50
 
57
51
  // src/cli/init.ts
52
+ import * as fs2 from "node:fs";
53
+ import * as path2 from "node:path";
54
+ import * as readline from "node:readline";
55
+
56
+ // src/cli/scaffolder.ts
58
57
  import * as fs from "node:fs";
59
58
  import * as path from "node:path";
60
- import * as readline from "node:readline";
59
+ function hasRegisterGlasstraceCall(content) {
60
+ return content.split("\n").some((line) => {
61
+ const uncommented = line.replace(/\/\/.*$/, "");
62
+ return /\bregisterGlasstrace\s*\(/.test(uncommented);
63
+ });
64
+ }
65
+ function injectRegisterGlasstrace(content) {
66
+ if (hasRegisterGlasstraceCall(content)) {
67
+ return { injected: false, content };
68
+ }
69
+ const registerFnRegex = /export\s+(?:async\s+)?function\s+register\s*\([^)]*\)\s*\{/;
70
+ const match = registerFnRegex.exec(content);
71
+ if (!match) {
72
+ return { injected: false, content };
73
+ }
74
+ const afterBrace = content.slice(match.index + match[0].length);
75
+ const indentMatch = /\n([ \t]+)/.exec(afterBrace);
76
+ const indent = indentMatch ? indentMatch[1] : " ";
77
+ const importLine = 'import { registerGlasstrace } from "@glasstrace/sdk";\n';
78
+ const hasGlasstraceImport = content.includes("@glasstrace/sdk");
79
+ const insertPoint = match.index + match[0].length;
80
+ const callInjection = `
81
+ ${indent}// Glasstrace must be registered before other instrumentation
82
+ ${indent}registerGlasstrace();
83
+ `;
84
+ let modified;
85
+ if (hasGlasstraceImport) {
86
+ const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
87
+ const importMatch = importRegex.exec(content);
88
+ if (importMatch) {
89
+ const specifiers = importMatch[1];
90
+ const alreadyImported = specifiers.split(",").some((s) => s.trim() === "registerGlasstrace");
91
+ if (alreadyImported) {
92
+ modified = content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);
93
+ } else {
94
+ const existingImports = specifiers.trimEnd();
95
+ const separator = existingImports.endsWith(",") ? " " : ", ";
96
+ const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from "@glasstrace/sdk"`;
97
+ modified = content.replace(importMatch[0], updatedImport);
98
+ const newMatch = registerFnRegex.exec(modified);
99
+ if (newMatch) {
100
+ const newInsertPoint = newMatch.index + newMatch[0].length;
101
+ modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);
102
+ }
103
+ }
104
+ } else {
105
+ modified = importLine + content;
106
+ const newMatch = registerFnRegex.exec(modified);
107
+ if (newMatch) {
108
+ const newInsertPoint = newMatch.index + newMatch[0].length;
109
+ modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);
110
+ }
111
+ }
112
+ } else {
113
+ modified = importLine + content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);
114
+ }
115
+ return { injected: true, content: modified };
116
+ }
117
+ var INSTRUMENTATION_FILENAMES = [
118
+ "instrumentation.ts",
119
+ "instrumentation.js",
120
+ "instrumentation.mjs"
121
+ ];
122
+ function resolveInstrumentationTarget(projectRoot) {
123
+ const rootExisting = [];
124
+ const srcExisting = [];
125
+ for (const name of INSTRUMENTATION_FILENAMES) {
126
+ const rootPath = path.join(projectRoot, name);
127
+ if (isRegularFile(rootPath)) {
128
+ rootExisting.push(rootPath);
129
+ }
130
+ const srcPath = path.join(projectRoot, "src", name);
131
+ if (isRegularFile(srcPath)) {
132
+ srcExisting.push(srcPath);
133
+ }
134
+ }
135
+ const existing = [...rootExisting, ...srcExisting];
136
+ if (rootExisting.length > 0 && srcExisting.length > 0) {
137
+ return {
138
+ target: null,
139
+ layout: null,
140
+ existing,
141
+ rootExisting,
142
+ srcExisting,
143
+ conflict: true
144
+ };
145
+ }
146
+ if (srcExisting.length > 0) {
147
+ return {
148
+ target: srcExisting[0],
149
+ layout: "src",
150
+ existing,
151
+ rootExisting,
152
+ srcExisting,
153
+ conflict: false
154
+ };
155
+ }
156
+ if (rootExisting.length > 0) {
157
+ return {
158
+ target: rootExisting[0],
159
+ layout: "root",
160
+ existing,
161
+ rootExisting,
162
+ srcExisting,
163
+ conflict: false
164
+ };
165
+ }
166
+ const srcDir = path.join(projectRoot, "src");
167
+ const layout = isDirectory(srcDir) ? "src" : "root";
168
+ const target = layout === "src" ? path.join(projectRoot, "src", "instrumentation.ts") : path.join(projectRoot, "instrumentation.ts");
169
+ return {
170
+ target,
171
+ layout,
172
+ existing,
173
+ rootExisting,
174
+ srcExisting,
175
+ conflict: false
176
+ };
177
+ }
178
+ function isDirectory(p) {
179
+ try {
180
+ return fs.statSync(p).isDirectory();
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+ function isRegularFile(p) {
186
+ try {
187
+ const stat = fs.lstatSync(p);
188
+ if (stat.isSymbolicLink()) {
189
+ return fs.statSync(p).isFile();
190
+ }
191
+ return stat.isFile();
192
+ } catch {
193
+ return false;
194
+ }
195
+ }
196
+ function appendRegisterFunction(content) {
197
+ const importLine = 'import { registerGlasstrace } from "@glasstrace/sdk";\n';
198
+ const functionBlock = "\nexport async function register() {\n // Glasstrace must be registered before Prisma instrumentation\n // to ensure all ORM spans are captured correctly.\n // If you use @prisma/instrumentation, import it after this call.\n registerGlasstrace();\n}\n";
199
+ let withImport = content;
200
+ const hasGlasstraceImport = content.includes("@glasstrace/sdk");
201
+ if (!hasGlasstraceImport) {
202
+ withImport = importLine + content;
203
+ } else {
204
+ const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
205
+ const importMatch = importRegex.exec(content);
206
+ if (importMatch) {
207
+ const specifiers = importMatch[1];
208
+ const alreadyImported = specifiers.split(",").some((s) => s.trim() === "registerGlasstrace");
209
+ if (!alreadyImported) {
210
+ const existingImports = specifiers.trimEnd();
211
+ const separator = existingImports.endsWith(",") ? " " : ", ";
212
+ const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from "@glasstrace/sdk"`;
213
+ withImport = content.replace(importMatch[0], updatedImport);
214
+ }
215
+ } else {
216
+ withImport = importLine + content;
217
+ }
218
+ }
219
+ const trailingNewline = withImport.endsWith("\n") ? "" : "\n";
220
+ return withImport + trailingNewline + functionBlock;
221
+ }
222
+ async function defaultInstrumentationPrompt(question, defaultValue) {
223
+ if (!process.stdin.isTTY) return defaultValue;
224
+ const readline2 = await import("node:readline");
225
+ const rl = readline2.createInterface({
226
+ input: process.stdin,
227
+ output: process.stdout
228
+ });
229
+ return new Promise((resolve) => {
230
+ const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
231
+ rl.question(question + suffix, (answer) => {
232
+ rl.close();
233
+ const trimmed = answer.trim().toLowerCase();
234
+ if (trimmed === "") {
235
+ resolve(defaultValue);
236
+ return;
237
+ }
238
+ resolve(trimmed === "y" || trimmed === "yes");
239
+ });
240
+ });
241
+ }
242
+ async function scaffoldInstrumentation(projectRoot, options = {}) {
243
+ const target = resolveInstrumentationTarget(projectRoot);
244
+ if (target.conflict) {
245
+ return {
246
+ action: "conflict",
247
+ // Point the user at the `src/` variant — modern Next.js apps with a
248
+ // `src/` directory load from there, so that's the merge target. The
249
+ // competing path is reported separately for the error message.
250
+ filePath: target.srcExisting[0],
251
+ conflictingPath: target.rootExisting[0]
252
+ };
253
+ }
254
+ const filePath = target.target;
255
+ const layout = target.layout;
256
+ if (filePath === null || layout === null) {
257
+ return { action: "unrecognized" };
258
+ }
259
+ const force = options.force === true;
260
+ const prompt = options.prompt ?? defaultInstrumentationPrompt;
261
+ if (!fs.existsSync(filePath)) {
262
+ const content = `import { registerGlasstrace } from "@glasstrace/sdk";
263
+
264
+ export async function register() {
265
+ // Glasstrace must be registered before Prisma instrumentation
266
+ // to ensure all ORM spans are captured correctly.
267
+ // If you use @prisma/instrumentation, import it after this call.
268
+ registerGlasstrace();
269
+ }
270
+ `;
271
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
272
+ fs.writeFileSync(filePath, content, "utf-8");
273
+ return { action: "created", filePath, layout };
274
+ }
275
+ const existing = fs.readFileSync(filePath, "utf-8");
276
+ if (hasRegisterGlasstraceCall(existing)) {
277
+ return { action: "already-registered", filePath, layout };
278
+ }
279
+ if (!force) {
280
+ const approved = await prompt(
281
+ `Merge registerGlasstrace() into ${path.relative(projectRoot, filePath)}?`,
282
+ false
283
+ );
284
+ if (!approved) {
285
+ return { action: "skipped", filePath, layout };
286
+ }
287
+ }
288
+ const injectResult = injectRegisterGlasstrace(existing);
289
+ if (injectResult.injected) {
290
+ fs.writeFileSync(filePath, injectResult.content, "utf-8");
291
+ return { action: "injected", filePath, layout };
292
+ }
293
+ const appended = appendRegisterFunction(existing);
294
+ fs.writeFileSync(filePath, appended, "utf-8");
295
+ return { action: "appended", filePath, layout };
296
+ }
297
+ async function scaffoldNextConfig(projectRoot) {
298
+ let configPath;
299
+ let configName;
300
+ for (const name of NEXT_CONFIG_NAMES) {
301
+ const candidate = path.join(projectRoot, name);
302
+ if (fs.existsSync(candidate)) {
303
+ configPath = candidate;
304
+ configName = name;
305
+ break;
306
+ }
307
+ }
308
+ if (configPath === void 0 || configName === void 0) {
309
+ return null;
310
+ }
311
+ const existing = fs.readFileSync(configPath, "utf-8");
312
+ if (existing.trim().length === 0) {
313
+ return { modified: false, reason: "empty-file" };
314
+ }
315
+ if (existing.includes("withGlasstraceConfig")) {
316
+ return { modified: false, reason: "already-wrapped" };
317
+ }
318
+ const isESM = configName.endsWith(".ts") || configName.endsWith(".mjs");
319
+ if (isESM) {
320
+ const importLine = 'import { withGlasstraceConfig } from "@glasstrace/sdk";\n';
321
+ const wrapResult2 = wrapExport(existing);
322
+ if (!wrapResult2.wrapped) {
323
+ return { modified: false, reason: "no-export" };
324
+ }
325
+ const modified2 = importLine + "\n" + wrapResult2.content;
326
+ fs.writeFileSync(configPath, modified2, "utf-8");
327
+ return { modified: true };
328
+ }
329
+ const requireLine = 'const { withGlasstraceConfig } = require("@glasstrace/sdk");\n';
330
+ const wrapResult = wrapCJSExport(existing);
331
+ if (!wrapResult.wrapped) {
332
+ return { modified: false, reason: "no-export" };
333
+ }
334
+ const modified = requireLine + "\n" + wrapResult.content;
335
+ fs.writeFileSync(configPath, modified, "utf-8");
336
+ return { modified: true };
337
+ }
338
+ function wrapExport(content) {
339
+ const marker = "export default";
340
+ const idx = content.lastIndexOf(marker);
341
+ if (idx === -1) {
342
+ return { content, wrapped: false };
343
+ }
344
+ const preamble = content.slice(0, idx);
345
+ const exprRaw = content.slice(idx + marker.length);
346
+ const expr = exprRaw.trim().replace(/;?\s*$/, "");
347
+ if (expr.length === 0) {
348
+ return { content, wrapped: false };
349
+ }
350
+ return {
351
+ content: preamble + `export default withGlasstraceConfig(${expr});
352
+ `,
353
+ wrapped: true
354
+ };
355
+ }
356
+ function wrapCJSExport(content) {
357
+ const cjsMarker = "module.exports";
358
+ const cjsIdx = content.lastIndexOf(cjsMarker);
359
+ if (cjsIdx === -1) {
360
+ return { content, wrapped: false };
361
+ }
362
+ const preamble = content.slice(0, cjsIdx);
363
+ const afterMarker = content.slice(cjsIdx + cjsMarker.length);
364
+ const eqMatch = /^\s*=\s*/.exec(afterMarker);
365
+ if (!eqMatch) {
366
+ return { content, wrapped: false };
367
+ }
368
+ const exprRaw = afterMarker.slice(eqMatch[0].length);
369
+ const expr = exprRaw.trim().replace(/;?\s*$/, "");
370
+ if (expr.length === 0) {
371
+ return { content, wrapped: false };
372
+ }
373
+ return {
374
+ content: preamble + `module.exports = withGlasstraceConfig(${expr});
375
+ `,
376
+ wrapped: true
377
+ };
378
+ }
379
+ async function scaffoldEnvLocal(projectRoot) {
380
+ const filePath = path.join(projectRoot, ".env.local");
381
+ if (fs.existsSync(filePath)) {
382
+ const existing = fs.readFileSync(filePath, "utf-8");
383
+ if (/^\s*#?\s*GLASSTRACE_API_KEY\s*=/m.test(existing)) {
384
+ return false;
385
+ }
386
+ const separator = existing.endsWith("\n") ? "" : "\n";
387
+ fs.writeFileSync(filePath, existing + separator + "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
388
+ return true;
389
+ }
390
+ fs.writeFileSync(filePath, "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
391
+ return true;
392
+ }
393
+ async function addCoverageMapEnv(projectRoot) {
394
+ const filePath = path.join(projectRoot, ".env.local");
395
+ if (!fs.existsSync(filePath)) {
396
+ fs.writeFileSync(filePath, "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
397
+ return true;
398
+ }
399
+ const existing = fs.readFileSync(filePath, "utf-8");
400
+ const keyRegex = /^(\s*GLASSTRACE_COVERAGE_MAP\s*=\s*)(.*)$/m;
401
+ const keyMatch = keyRegex.exec(existing);
402
+ if (keyMatch) {
403
+ const currentValue = keyMatch[2].trim();
404
+ if (currentValue === "true") {
405
+ return false;
406
+ }
407
+ const updated = existing.replace(keyRegex, `${keyMatch[1]}true`);
408
+ fs.writeFileSync(filePath, updated, "utf-8");
409
+ return true;
410
+ }
411
+ const separator = existing.endsWith("\n") ? "" : "\n";
412
+ fs.writeFileSync(filePath, existing + separator + "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
413
+ return true;
414
+ }
415
+ async function scaffoldGitignore(projectRoot) {
416
+ const filePath = path.join(projectRoot, ".gitignore");
417
+ if (fs.existsSync(filePath)) {
418
+ const existing = fs.readFileSync(filePath, "utf-8");
419
+ const lines = existing.split("\n").map((l) => l.trim());
420
+ if (lines.includes(".glasstrace/")) {
421
+ return false;
422
+ }
423
+ const separator = existing.endsWith("\n") ? "" : "\n";
424
+ fs.writeFileSync(filePath, existing + separator + ".glasstrace/\n", "utf-8");
425
+ return true;
426
+ }
427
+ fs.writeFileSync(filePath, ".glasstrace/\n", "utf-8");
428
+ return true;
429
+ }
430
+
431
+ // src/cli/init.ts
61
432
  function meetsNodeVersion(minMajor) {
62
433
  const [major] = process.versions.node.split(".").map(Number);
63
434
  return major >= minMajor;
@@ -65,8 +436,8 @@ function meetsNodeVersion(minMajor) {
65
436
  async function decideMcpConfigAction(options) {
66
437
  const { configPath, expectedContent, force } = options;
67
438
  if (configPath === null) return "write";
68
- const exists = options.existsSync ?? fs.existsSync;
69
- const read = options.readFile ?? ((p) => fs.readFileSync(p, "utf-8"));
439
+ const exists = options.existsSync ?? fs2.existsSync;
440
+ const read = options.readFile ?? ((p) => fs2.readFileSync(p, "utf-8"));
70
441
  const prompt = options.prompt ?? promptYesNo;
71
442
  if (!exists(configPath)) return "write";
72
443
  let existingContent;
@@ -163,17 +534,17 @@ async function rollbackSteps(steps, projectRoot, state) {
163
534
  try {
164
535
  switch (step) {
165
536
  case "instrumentation": {
166
- const instrPath = state?.instrumentationPath ?? path.join(projectRoot, "instrumentation.ts");
167
- if (fs.existsSync(instrPath)) {
168
- const content = fs.readFileSync(instrPath, "utf-8");
537
+ const instrPath = state?.instrumentationPath ?? path2.join(projectRoot, "instrumentation.ts");
538
+ if (fs2.existsSync(instrPath)) {
539
+ const content = fs2.readFileSync(instrPath, "utf-8");
169
540
  if (isInitCreatedInstrumentation(content)) {
170
- fs.unlinkSync(instrPath);
541
+ fs2.unlinkSync(instrPath);
171
542
  } else if (state?.originalInstrumentationContent !== void 0) {
172
- fs.writeFileSync(instrPath, state.originalInstrumentationContent, "utf-8");
543
+ fs2.writeFileSync(instrPath, state.originalInstrumentationContent, "utf-8");
173
544
  } else {
174
545
  const cleaned = removeRegisterGlasstrace(content);
175
546
  if (cleaned !== content) {
176
- fs.writeFileSync(instrPath, cleaned, "utf-8");
547
+ fs2.writeFileSync(instrPath, cleaned, "utf-8");
177
548
  }
178
549
  }
179
550
  }
@@ -181,11 +552,11 @@ async function rollbackSteps(steps, projectRoot, state) {
181
552
  }
182
553
  case "next-config": {
183
554
  for (const name of NEXT_CONFIG_NAMES) {
184
- const configPath = path.join(projectRoot, name);
185
- if (!fs.existsSync(configPath)) {
555
+ const configPath = path2.join(projectRoot, name);
556
+ if (!fs2.existsSync(configPath)) {
186
557
  continue;
187
558
  }
188
- const content = fs.readFileSync(configPath, "utf-8");
559
+ const content = fs2.readFileSync(configPath, "utf-8");
189
560
  if (!content.includes("withGlasstraceConfig")) {
190
561
  continue;
191
562
  }
@@ -193,16 +564,16 @@ async function rollbackSteps(steps, projectRoot, state) {
193
564
  const unwrapResult = isESM ? unwrapExport(content) : unwrapCJSExport(content);
194
565
  if (unwrapResult.unwrapped) {
195
566
  const cleaned = removeGlasstraceConfigImport(unwrapResult.content);
196
- fs.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), "utf-8");
567
+ fs2.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), "utf-8");
197
568
  }
198
569
  break;
199
570
  }
200
571
  break;
201
572
  }
202
573
  case "env-local": {
203
- const envPath = path.join(projectRoot, ".env.local");
204
- if (fs.existsSync(envPath)) {
205
- const content = fs.readFileSync(envPath, "utf-8");
574
+ const envPath = path2.join(projectRoot, ".env.local");
575
+ if (fs2.existsSync(envPath)) {
576
+ const content = fs2.readFileSync(envPath, "utf-8");
206
577
  const lines = content.split("\n");
207
578
  const filtered = lines.filter((line) => {
208
579
  const trimmed = line.trim();
@@ -211,18 +582,18 @@ async function rollbackSteps(steps, projectRoot, state) {
211
582
  if (filtered.length !== lines.length) {
212
583
  const result = filtered.join("\n");
213
584
  if (result.trim().length === 0) {
214
- fs.unlinkSync(envPath);
585
+ fs2.unlinkSync(envPath);
215
586
  } else {
216
- fs.writeFileSync(envPath, result, "utf-8");
587
+ fs2.writeFileSync(envPath, result, "utf-8");
217
588
  }
218
589
  }
219
590
  }
220
591
  break;
221
592
  }
222
593
  case "gitignore": {
223
- const gitignorePath = path.join(projectRoot, ".gitignore");
224
- if (fs.existsSync(gitignorePath)) {
225
- const content = fs.readFileSync(gitignorePath, "utf-8");
594
+ const gitignorePath = path2.join(projectRoot, ".gitignore");
595
+ if (fs2.existsSync(gitignorePath)) {
596
+ const content = fs2.readFileSync(gitignorePath, "utf-8");
226
597
  const lines = content.split("\n");
227
598
  const filtered = lines.filter(
228
599
  (line) => line.trim() !== ".glasstrace/"
@@ -230,9 +601,9 @@ async function rollbackSteps(steps, projectRoot, state) {
230
601
  if (filtered.length !== lines.length) {
231
602
  const result = filtered.join("\n");
232
603
  if (result.trim().length === 0) {
233
- fs.unlinkSync(gitignorePath);
604
+ fs2.unlinkSync(gitignorePath);
234
605
  } else {
235
- fs.writeFileSync(gitignorePath, result, "utf-8");
606
+ fs2.writeFileSync(gitignorePath, result, "utf-8");
236
607
  }
237
608
  }
238
609
  }
@@ -263,8 +634,8 @@ async function runInit(options) {
263
634
  errors.push(err instanceof Error ? err.message : String(err));
264
635
  return { exitCode: 1, summary, warnings, errors };
265
636
  }
266
- const packageJsonPath = path.join(projectRoot, "package.json");
267
- if (!fs.existsSync(packageJsonPath)) {
637
+ const packageJsonPath = path2.join(projectRoot, "package.json");
638
+ if (!fs2.existsSync(packageJsonPath)) {
268
639
  errors.push("No package.json found. Run this command from a Node.js project root.");
269
640
  return { exitCode: 1, summary, warnings, errors };
270
641
  }
@@ -273,8 +644,8 @@ async function runInit(options) {
273
644
  const preResolved = resolveInstrumentationTarget(projectRoot);
274
645
  if (!preResolved.conflict && preResolved.target !== null) {
275
646
  rollbackState.instrumentationPath = preResolved.target;
276
- if (fs.existsSync(preResolved.target)) {
277
- rollbackState.originalInstrumentationContent = fs.readFileSync(
647
+ if (fs2.existsSync(preResolved.target)) {
648
+ rollbackState.originalInstrumentationContent = fs2.readFileSync(
278
649
  preResolved.target,
279
650
  "utf-8"
280
651
  );
@@ -289,7 +660,7 @@ async function runInit(options) {
289
660
  if (instrResult.filePath !== void 0) {
290
661
  rollbackState.instrumentationPath = instrResult.filePath;
291
662
  }
292
- const relativePath = instrResult.filePath !== void 0 ? path.relative(projectRoot, instrResult.filePath) : "instrumentation.ts";
663
+ const relativePath = instrResult.filePath !== void 0 ? path2.relative(projectRoot, instrResult.filePath) : "instrumentation.ts";
293
664
  switch (instrResult.action) {
294
665
  case "created":
295
666
  summary.push(`Created ${relativePath}`);
@@ -314,8 +685,8 @@ async function runInit(options) {
314
685
  );
315
686
  break;
316
687
  case "conflict": {
317
- const primary = instrResult.filePath !== void 0 ? path.relative(projectRoot, instrResult.filePath) : "src/instrumentation.ts";
318
- const competing = instrResult.conflictingPath !== void 0 ? path.relative(projectRoot, instrResult.conflictingPath) : "instrumentation.ts";
688
+ const primary = instrResult.filePath !== void 0 ? path2.relative(projectRoot, instrResult.filePath) : "src/instrumentation.ts";
689
+ const competing = instrResult.conflictingPath !== void 0 ? path2.relative(projectRoot, instrResult.conflictingPath) : "instrumentation.ts";
319
690
  await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);
320
691
  errors.push(
321
692
  `Both ${primary} and ${competing} exist. Next.js's loader behavior is undefined when both are present.
@@ -362,10 +733,10 @@ Then add this as the first statement in your register() function:
362
733
  return { exitCode: 1, summary, warnings, errors };
363
734
  }
364
735
  try {
365
- const envPathForCheck = path.join(projectRoot, ".env.local");
736
+ const envPathForCheck = path2.join(projectRoot, ".env.local");
366
737
  let existingDevKey = false;
367
- if (fs.existsSync(envPathForCheck)) {
368
- const existingContent = fs.readFileSync(envPathForCheck, "utf-8");
738
+ if (fs2.existsSync(envPathForCheck)) {
739
+ const existingContent = fs2.readFileSync(envPathForCheck, "utf-8");
369
740
  existingDevKey = isDevApiKey(readEnvLocalApiKey(existingContent));
370
741
  }
371
742
  const envCreated = await scaffoldEnvLocal(projectRoot);
@@ -430,10 +801,10 @@ Then add this as the first statement in your register() function:
430
801
  );
431
802
  break;
432
803
  }
433
- const gitignorePath = path.join(projectRoot, ".gitignore");
434
- if (fs.existsSync(gitignorePath)) {
804
+ const gitignorePath = path2.join(projectRoot, ".gitignore");
805
+ if (fs2.existsSync(gitignorePath)) {
435
806
  try {
436
- const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
807
+ const gitignoreContent = fs2.readFileSync(gitignorePath, "utf-8");
437
808
  if (gitignoreExcludesDiscoveryFile(gitignoreContent, discoveryResult.layout)) {
438
809
  warnings.push(
439
810
  `Your .gitignore excludes ${relPath} (directly or via a parent rule). The discovery file must be committed for the Glasstrace browser extension to find it in deployed builds. Remove the matching line from .gitignore or add an explicit negation (e.g. \`!` + relPath + "`)."
@@ -450,15 +821,18 @@ Then add this as the first statement in your register() function:
450
821
  );
451
822
  }
452
823
  let anyConfigWritten = false;
824
+ let anyConfigRewrittenWithBearer = false;
825
+ const resolved = await resolveEffectiveMcpCredential(projectRoot);
826
+ const bearer = resolved.effective?.key ?? anonKey;
453
827
  if (isCI) {
454
828
  const genericAgent = {
455
829
  name: "generic",
456
- mcpConfigPath: path.join(projectRoot, ".glasstrace", "mcp.json"),
830
+ mcpConfigPath: path2.join(projectRoot, ".glasstrace", "mcp.json"),
457
831
  infoFilePath: null,
458
832
  cliAvailable: false,
459
833
  registrationCommand: null
460
834
  };
461
- const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);
835
+ const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
462
836
  const decision = await decideMcpConfigAction({
463
837
  configPath: genericAgent.mcpConfigPath,
464
838
  expectedContent: genericConfig,
@@ -466,8 +840,11 @@ Then add this as the first statement in your register() function:
466
840
  });
467
841
  if (decision !== "skip") {
468
842
  await writeMcpConfig(genericAgent, genericConfig, projectRoot);
843
+ if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
844
+ anyConfigRewrittenWithBearer = true;
845
+ }
469
846
  }
470
- if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
847
+ if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
471
848
  anyConfigWritten = true;
472
849
  summary.push("Created .glasstrace/mcp.json (CI mode)");
473
850
  }
@@ -481,22 +858,23 @@ Then add this as the first statement in your register() function:
481
858
  );
482
859
  const genericAgent = {
483
860
  name: "generic",
484
- mcpConfigPath: path.join(projectRoot, ".glasstrace", "mcp.json"),
861
+ mcpConfigPath: path2.join(projectRoot, ".glasstrace", "mcp.json"),
485
862
  infoFilePath: null,
486
863
  cliAvailable: false,
487
864
  registrationCommand: null
488
865
  };
489
- const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);
866
+ const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
490
867
  await writeMcpConfig(genericAgent, genericConfig, projectRoot);
491
- if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
868
+ if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
492
869
  anyConfigWritten = true;
870
+ anyConfigRewrittenWithBearer = true;
493
871
  }
494
872
  agents = [];
495
873
  }
496
874
  const configuredNames = [];
497
875
  for (const agent of agents) {
498
876
  try {
499
- const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);
877
+ const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);
500
878
  const decision = await decideMcpConfigAction({
501
879
  configPath: agent.mcpConfigPath,
502
880
  expectedContent: configContent,
@@ -506,17 +884,18 @@ Then add this as the first statement in your register() function:
506
884
  summary.push(
507
885
  `Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`
508
886
  );
509
- if (agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath)) {
887
+ if (agent.mcpConfigPath !== null && fs2.existsSync(agent.mcpConfigPath)) {
510
888
  anyConfigWritten = true;
511
889
  }
512
890
  continue;
513
891
  }
514
892
  await writeMcpConfig(agent, configContent, projectRoot);
515
- const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);
893
+ const configExists = agent.mcpConfigPath !== null && fs2.existsSync(agent.mcpConfigPath);
516
894
  if (!configExists) {
517
895
  continue;
518
896
  }
519
897
  anyConfigWritten = true;
898
+ anyConfigRewrittenWithBearer = true;
520
899
  const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
521
900
  if (infoContent !== "") {
522
901
  await injectInfoSection(agent, infoContent, projectRoot);
@@ -540,8 +919,13 @@ Then add this as the first statement in your register() function:
540
919
  [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".codex/config.toml"],
541
920
  projectRoot
542
921
  );
543
- if (anyConfigWritten) {
544
- const markerCreated = await scaffoldMcpMarker(projectRoot, anonKey);
922
+ if (anyConfigRewrittenWithBearer) {
923
+ const markerSource = resolved.effective?.source ?? "anon";
924
+ const markerHash = identityFingerprint(bearer);
925
+ const markerCreated = await writeMcpMarker(projectRoot, {
926
+ credentialSource: markerSource,
927
+ credentialHash: markerHash
928
+ });
545
929
  if (markerCreated) {
546
930
  summary.push("Created .glasstrace/mcp-connected marker");
547
931
  }
@@ -598,9 +982,9 @@ async function verifyAnonKeyRegistration(projectRoot) {
598
982
  }
599
983
  let devKey;
600
984
  try {
601
- const envPath = path.join(projectRoot, ".env.local");
602
- if (fs.existsSync(envPath)) {
603
- const envContent = fs.readFileSync(envPath, "utf-8");
985
+ const envPath = path2.join(projectRoot, ".env.local");
986
+ if (fs2.existsSync(envPath)) {
987
+ const envContent = fs2.readFileSync(envPath, "utf-8");
604
988
  const effective = readEnvLocalApiKey(envContent);
605
989
  if (effective !== null && isDevApiKey(effective)) {
606
990
  devKey = effective;
@@ -610,7 +994,7 @@ async function verifyAnonKeyRegistration(projectRoot) {
610
994
  }
611
995
  const baseConfig = resolveConfig({ apiKey: devKey });
612
996
  const config = { ...baseConfig, apiKey: devKey };
613
- const sdkVersion = true ? "1.1.0" : "0.0.0-dev";
997
+ const sdkVersion = true ? "1.1.2" : "0.0.0-dev";
614
998
  const result = await verifyInitReachable(config, anonKey, sdkVersion);
615
999
  if (result.ok) {
616
1000
  return { outcome: "verified" };
@@ -659,7 +1043,7 @@ function parseArgs(argv) {
659
1043
  };
660
1044
  }
661
1045
  var scriptPath = typeof process !== "undefined" && process.argv[1] !== void 0 ? process.argv[1].replace(/\\/g, "/") : void 0;
662
- var scriptBasename = scriptPath !== void 0 ? path.basename(scriptPath) : void 0;
1046
+ var scriptBasename = scriptPath !== void 0 ? path2.basename(scriptPath) : void 0;
663
1047
  var isDirectExecution = scriptPath !== void 0 && (scriptPath.endsWith("/cli/init.js") || scriptPath.endsWith("/cli/init.ts") || scriptBasename === "glasstrace");
664
1048
  if (isDirectExecution) {
665
1049
  if (!meetsNodeVersion(20)) {
@@ -806,7 +1190,7 @@ Usage: glasstrace mcp add [--force] [--dry-run]
806
1190
  } else if (subcommand === "status") {
807
1191
  const remainingArgs = process.argv.slice(3);
808
1192
  const json = remainingArgs.includes("--json");
809
- Promise.all([import("./status.js"), import("../monorepo-N5Z63XP7.js")]).then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {
1193
+ Promise.all([import("./status.js"), import("../monorepo-PFVNPQ6X.js")]).then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {
810
1194
  let projectRoot = process.cwd();
811
1195
  try {
812
1196
  projectRoot = resolve(projectRoot).projectRoot;