@glasstrace/sdk 1.1.1 → 1.1.3
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/README.md +78 -1
- package/dist/{chunk-DIM4JRXM.js → chunk-2M57EO6U.js} +2 -2
- package/dist/{chunk-Y26HJUPD.js → chunk-FGDS33I2.js} +138 -12
- package/dist/chunk-FGDS33I2.js.map +1 -0
- package/dist/{chunk-MXDZHFJQ.js → chunk-JKI4OCFV.js} +4 -14
- package/dist/chunk-JKI4OCFV.js.map +1 -0
- package/dist/{chunk-7SZQN6IU.js → chunk-NB7GJE4S.js} +2 -2
- package/dist/chunk-NB7GJE4S.js.map +1 -0
- package/dist/{chunk-ZRDQ6ZKI.js → chunk-TWHCJKRS.js} +101 -481
- package/dist/chunk-TWHCJKRS.js.map +1 -0
- package/dist/{chunk-P22UQ2OJ.js → chunk-TWTWRJ25.js} +233 -9
- package/dist/chunk-TWTWRJ25.js.map +1 -0
- package/dist/cli/init.cjs +2494 -2332
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +434 -63
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +14 -2
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +17 -5
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.js +1 -3
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/uninit.cjs +116 -14
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.js +3 -3
- package/dist/cli/validate.cjs +14162 -2
- package/dist/cli/validate.cjs.map +1 -1
- package/dist/cli/validate.d.cts +7 -3
- package/dist/cli/validate.d.ts +7 -3
- package/dist/cli/validate.js +25 -2
- package/dist/cli/validate.js.map +1 -1
- package/dist/index.cjs +339 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -9
- package/dist/index.d.ts +12 -9
- package/dist/index.js +3 -3
- package/dist/{monorepo-GSL6JD3G.js → monorepo-PFVNPQ6X.js} +3 -5
- package/dist/node-entry.cjs +339 -28
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-7SZQN6IU.js.map +0 -1
- package/dist/chunk-MXDZHFJQ.js.map +0 -1
- package/dist/chunk-P22UQ2OJ.js.map +0 -1
- package/dist/chunk-Y26HJUPD.js.map +0 -1
- package/dist/chunk-ZRDQ6ZKI.js.map +0 -1
- /package/dist/{chunk-DIM4JRXM.js.map → chunk-2M57EO6U.js.map} +0 -0
- /package/dist/{monorepo-GSL6JD3G.js.map → monorepo-PFVNPQ6X.js.map} +0 -0
package/dist/cli/init.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveProjectRoot
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-2M57EO6U.js";
|
|
5
5
|
import {
|
|
6
6
|
verifyInitReachable
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-JKI4OCFV.js";
|
|
8
8
|
import {
|
|
9
9
|
buildImportGraph
|
|
10
10
|
} from "../chunk-DST4UBXU.js";
|
|
@@ -12,22 +12,16 @@ import {
|
|
|
12
12
|
resolveConfig
|
|
13
13
|
} from "../chunk-VUZCLMIX.js";
|
|
14
14
|
import {
|
|
15
|
-
addCoverageMapEnv,
|
|
16
15
|
isInitCreatedInstrumentation,
|
|
17
16
|
relativeDiscoveryPath,
|
|
18
17
|
removeDiscoveryFile,
|
|
19
18
|
removeGlasstraceConfigImport,
|
|
20
19
|
removeRegisterGlasstrace,
|
|
21
|
-
resolveInstrumentationTarget,
|
|
22
20
|
resolveStaticRoot,
|
|
23
|
-
scaffoldEnvLocal,
|
|
24
|
-
scaffoldGitignore,
|
|
25
|
-
scaffoldInstrumentation,
|
|
26
|
-
scaffoldNextConfig,
|
|
27
21
|
unwrapCJSExport,
|
|
28
22
|
unwrapExport,
|
|
29
23
|
writeDiscoveryFile
|
|
30
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-TWHCJKRS.js";
|
|
31
25
|
import {
|
|
32
26
|
detectAgents,
|
|
33
27
|
generateInfoSection,
|
|
@@ -36,10 +30,6 @@ import {
|
|
|
36
30
|
updateGitignore,
|
|
37
31
|
writeMcpConfig
|
|
38
32
|
} from "../chunk-P4OYPFQ5.js";
|
|
39
|
-
import {
|
|
40
|
-
NEXT_CONFIG_NAMES,
|
|
41
|
-
formatAgentName
|
|
42
|
-
} from "../chunk-7SZQN6IU.js";
|
|
43
33
|
import {
|
|
44
34
|
MCP_ENDPOINT,
|
|
45
35
|
getOrCreateAnonKey,
|
|
@@ -50,14 +40,395 @@ import {
|
|
|
50
40
|
readEnvLocalApiKey,
|
|
51
41
|
resolveEffectiveMcpCredential,
|
|
52
42
|
writeMcpMarker
|
|
53
|
-
} from "../chunk-
|
|
43
|
+
} from "../chunk-TWTWRJ25.js";
|
|
54
44
|
import "../chunk-X5MAXP5T.js";
|
|
45
|
+
import {
|
|
46
|
+
NEXT_CONFIG_NAMES,
|
|
47
|
+
formatAgentName
|
|
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
|
-
|
|
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 ??
|
|
69
|
-
const read = options.readFile ?? ((p) =>
|
|
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 ??
|
|
167
|
-
if (
|
|
168
|
-
const content =
|
|
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
|
-
|
|
541
|
+
fs2.unlinkSync(instrPath);
|
|
171
542
|
} else if (state?.originalInstrumentationContent !== void 0) {
|
|
172
|
-
|
|
543
|
+
fs2.writeFileSync(instrPath, state.originalInstrumentationContent, "utf-8");
|
|
173
544
|
} else {
|
|
174
545
|
const cleaned = removeRegisterGlasstrace(content);
|
|
175
546
|
if (cleaned !== content) {
|
|
176
|
-
|
|
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 =
|
|
185
|
-
if (!
|
|
555
|
+
const configPath = path2.join(projectRoot, name);
|
|
556
|
+
if (!fs2.existsSync(configPath)) {
|
|
186
557
|
continue;
|
|
187
558
|
}
|
|
188
|
-
const content =
|
|
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
|
-
|
|
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 =
|
|
204
|
-
if (
|
|
205
|
-
const content =
|
|
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
|
-
|
|
585
|
+
fs2.unlinkSync(envPath);
|
|
215
586
|
} else {
|
|
216
|
-
|
|
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 =
|
|
224
|
-
if (
|
|
225
|
-
const content =
|
|
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
|
-
|
|
604
|
+
fs2.unlinkSync(gitignorePath);
|
|
234
605
|
} else {
|
|
235
|
-
|
|
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 =
|
|
267
|
-
if (!
|
|
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 (
|
|
277
|
-
rollbackState.originalInstrumentationContent =
|
|
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 ?
|
|
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 ?
|
|
318
|
-
const competing = instrResult.conflictingPath !== void 0 ?
|
|
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 =
|
|
736
|
+
const envPathForCheck = path2.join(projectRoot, ".env.local");
|
|
366
737
|
let existingDevKey = false;
|
|
367
|
-
if (
|
|
368
|
-
const existingContent =
|
|
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 =
|
|
434
|
-
if (
|
|
804
|
+
const gitignorePath = path2.join(projectRoot, ".gitignore");
|
|
805
|
+
if (fs2.existsSync(gitignorePath)) {
|
|
435
806
|
try {
|
|
436
|
-
const gitignoreContent =
|
|
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 + "`)."
|
|
@@ -456,7 +827,7 @@ Then add this as the first statement in your register() function:
|
|
|
456
827
|
if (isCI) {
|
|
457
828
|
const genericAgent = {
|
|
458
829
|
name: "generic",
|
|
459
|
-
mcpConfigPath:
|
|
830
|
+
mcpConfigPath: path2.join(projectRoot, ".glasstrace", "mcp.json"),
|
|
460
831
|
infoFilePath: null,
|
|
461
832
|
cliAvailable: false,
|
|
462
833
|
registrationCommand: null
|
|
@@ -469,11 +840,11 @@ Then add this as the first statement in your register() function:
|
|
|
469
840
|
});
|
|
470
841
|
if (decision !== "skip") {
|
|
471
842
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
472
|
-
if (genericAgent.mcpConfigPath !== null &&
|
|
843
|
+
if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
|
|
473
844
|
anyConfigRewrittenWithBearer = true;
|
|
474
845
|
}
|
|
475
846
|
}
|
|
476
|
-
if (genericAgent.mcpConfigPath !== null &&
|
|
847
|
+
if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
|
|
477
848
|
anyConfigWritten = true;
|
|
478
849
|
summary.push("Created .glasstrace/mcp.json (CI mode)");
|
|
479
850
|
}
|
|
@@ -487,14 +858,14 @@ Then add this as the first statement in your register() function:
|
|
|
487
858
|
);
|
|
488
859
|
const genericAgent = {
|
|
489
860
|
name: "generic",
|
|
490
|
-
mcpConfigPath:
|
|
861
|
+
mcpConfigPath: path2.join(projectRoot, ".glasstrace", "mcp.json"),
|
|
491
862
|
infoFilePath: null,
|
|
492
863
|
cliAvailable: false,
|
|
493
864
|
registrationCommand: null
|
|
494
865
|
};
|
|
495
866
|
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
|
|
496
867
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
497
|
-
if (genericAgent.mcpConfigPath !== null &&
|
|
868
|
+
if (genericAgent.mcpConfigPath !== null && fs2.existsSync(genericAgent.mcpConfigPath)) {
|
|
498
869
|
anyConfigWritten = true;
|
|
499
870
|
anyConfigRewrittenWithBearer = true;
|
|
500
871
|
}
|
|
@@ -513,13 +884,13 @@ Then add this as the first statement in your register() function:
|
|
|
513
884
|
summary.push(
|
|
514
885
|
`Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`
|
|
515
886
|
);
|
|
516
|
-
if (agent.mcpConfigPath !== null &&
|
|
887
|
+
if (agent.mcpConfigPath !== null && fs2.existsSync(agent.mcpConfigPath)) {
|
|
517
888
|
anyConfigWritten = true;
|
|
518
889
|
}
|
|
519
890
|
continue;
|
|
520
891
|
}
|
|
521
892
|
await writeMcpConfig(agent, configContent, projectRoot);
|
|
522
|
-
const configExists = agent.mcpConfigPath !== null &&
|
|
893
|
+
const configExists = agent.mcpConfigPath !== null && fs2.existsSync(agent.mcpConfigPath);
|
|
523
894
|
if (!configExists) {
|
|
524
895
|
continue;
|
|
525
896
|
}
|
|
@@ -611,9 +982,9 @@ async function verifyAnonKeyRegistration(projectRoot) {
|
|
|
611
982
|
}
|
|
612
983
|
let devKey;
|
|
613
984
|
try {
|
|
614
|
-
const envPath =
|
|
615
|
-
if (
|
|
616
|
-
const envContent =
|
|
985
|
+
const envPath = path2.join(projectRoot, ".env.local");
|
|
986
|
+
if (fs2.existsSync(envPath)) {
|
|
987
|
+
const envContent = fs2.readFileSync(envPath, "utf-8");
|
|
617
988
|
const effective = readEnvLocalApiKey(envContent);
|
|
618
989
|
if (effective !== null && isDevApiKey(effective)) {
|
|
619
990
|
devKey = effective;
|
|
@@ -623,7 +994,7 @@ async function verifyAnonKeyRegistration(projectRoot) {
|
|
|
623
994
|
}
|
|
624
995
|
const baseConfig = resolveConfig({ apiKey: devKey });
|
|
625
996
|
const config = { ...baseConfig, apiKey: devKey };
|
|
626
|
-
const sdkVersion = true ? "1.1.
|
|
997
|
+
const sdkVersion = true ? "1.1.3" : "0.0.0-dev";
|
|
627
998
|
const result = await verifyInitReachable(config, anonKey, sdkVersion);
|
|
628
999
|
if (result.ok) {
|
|
629
1000
|
return { outcome: "verified" };
|
|
@@ -672,7 +1043,7 @@ function parseArgs(argv) {
|
|
|
672
1043
|
};
|
|
673
1044
|
}
|
|
674
1045
|
var scriptPath = typeof process !== "undefined" && process.argv[1] !== void 0 ? process.argv[1].replace(/\\/g, "/") : void 0;
|
|
675
|
-
var scriptBasename = scriptPath !== void 0 ?
|
|
1046
|
+
var scriptBasename = scriptPath !== void 0 ? path2.basename(scriptPath) : void 0;
|
|
676
1047
|
var isDirectExecution = scriptPath !== void 0 && (scriptPath.endsWith("/cli/init.js") || scriptPath.endsWith("/cli/init.ts") || scriptBasename === "glasstrace");
|
|
677
1048
|
if (isDirectExecution) {
|
|
678
1049
|
if (!meetsNodeVersion(20)) {
|
|
@@ -819,7 +1190,7 @@ Usage: glasstrace mcp add [--force] [--dry-run]
|
|
|
819
1190
|
} else if (subcommand === "status") {
|
|
820
1191
|
const remainingArgs = process.argv.slice(3);
|
|
821
1192
|
const json = remainingArgs.includes("--json");
|
|
822
|
-
Promise.all([import("./status.js"), import("../monorepo-
|
|
1193
|
+
Promise.all([import("./status.js"), import("../monorepo-PFVNPQ6X.js")]).then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {
|
|
823
1194
|
let projectRoot = process.cwd();
|
|
824
1195
|
try {
|
|
825
1196
|
projectRoot = resolve(projectRoot).projectRoot;
|