@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.
- package/README.md +78 -1
- package/dist/{chunk-55FBXXER.js → chunk-2M57EO6U.js} +2 -2
- package/dist/chunk-3LILTM3T.js +384 -0
- package/dist/chunk-3LILTM3T.js.map +1 -0
- package/dist/{chunk-KE7MCPO5.js → chunk-4EZ6JTDG.js} +2 -2
- package/dist/{chunk-67RIOAXV.js → chunk-6RNBUUBR.js} +2 -2
- package/dist/{chunk-DO2YPMQ5.js → chunk-C567H5EQ.js} +23 -5
- package/dist/chunk-C567H5EQ.js.map +1 -0
- package/dist/{chunk-UGJ3X4CT.js → chunk-DST4UBXU.js} +2 -2
- package/dist/{chunk-DXRZKKSO.js → chunk-NB7GJE4S.js} +2 -4
- package/dist/chunk-NB7GJE4S.js.map +1 -0
- package/dist/{chunk-HAU66QBQ.js → chunk-P4OYPFQ5.js} +9 -9
- package/dist/chunk-P4OYPFQ5.js.map +1 -0
- package/dist/{chunk-LU3PPAOQ.js → chunk-UJ2JC7PZ.js} +4 -4
- package/dist/chunk-UJ2JC7PZ.js.map +1 -0
- package/dist/{chunk-TQ54WLCZ.js → chunk-X5MAXP5T.js} +2 -1
- package/dist/{chunk-ZBTC5QIQ.js → chunk-Z35HKVSO.js} +137 -10
- package/dist/chunk-Z35HKVSO.js.map +1 -0
- package/dist/cli/init.cjs +1313 -1118
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +456 -72
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +257 -85
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.d.cts +35 -4
- package/dist/cli/mcp-add.d.ts +35 -4
- package/dist/cli/mcp-add.js +61 -25
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.js +1 -1
- package/dist/cli/uninit.cjs +1 -1
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.js +4 -4
- 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/edge-entry.js +2 -2
- package/dist/index.cjs +423 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/{monorepo-N5Z63XP7.js → monorepo-PFVNPQ6X.js} +3 -3
- package/dist/node-entry.cjs +423 -12
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.js +7 -7
- package/dist/node-subpath.js +3 -3
- package/dist/{source-map-uploader-BJIXRLJ6.js → source-map-uploader-DPUUCLNW.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-DO2YPMQ5.js.map +0 -1
- package/dist/chunk-DXRZKKSO.js.map +0 -1
- package/dist/chunk-HAU66QBQ.js.map +0 -1
- package/dist/chunk-IP4NMDJK.js +0 -98
- package/dist/chunk-IP4NMDJK.js.map +0 -1
- package/dist/chunk-LU3PPAOQ.js.map +0 -1
- package/dist/chunk-O63DJKIJ.js +0 -460
- package/dist/chunk-O63DJKIJ.js.map +0 -1
- package/dist/chunk-ZBTC5QIQ.js.map +0 -1
- /package/dist/{chunk-55FBXXER.js.map → chunk-2M57EO6U.js.map} +0 -0
- /package/dist/{chunk-KE7MCPO5.js.map → chunk-4EZ6JTDG.js.map} +0 -0
- /package/dist/{chunk-67RIOAXV.js.map → chunk-6RNBUUBR.js.map} +0 -0
- /package/dist/{chunk-UGJ3X4CT.js.map → chunk-DST4UBXU.js.map} +0 -0
- /package/dist/{chunk-TQ54WLCZ.js.map → chunk-X5MAXP5T.js.map} +0 -0
- /package/dist/{monorepo-N5Z63XP7.js.map → monorepo-PFVNPQ6X.js.map} +0 -0
- /package/dist/{source-map-uploader-BJIXRLJ6.js.map → source-map-uploader-DPUUCLNW.js.map} +0 -0
package/dist/cli/init.cjs
CHANGED
|
@@ -39,547 +39,96 @@ function formatAgentName(name) {
|
|
|
39
39
|
gemini: "Gemini",
|
|
40
40
|
cursor: "Cursor",
|
|
41
41
|
windsurf: "Windsurf",
|
|
42
|
-
generic: "Generic"
|
|
42
|
+
generic: "Generic helper"
|
|
43
43
|
};
|
|
44
44
|
return displayNames[name];
|
|
45
45
|
}
|
|
46
|
-
var
|
|
46
|
+
var NEXT_CONFIG_NAMES;
|
|
47
47
|
var init_constants = __esm({
|
|
48
48
|
"src/cli/constants.ts"() {
|
|
49
49
|
"use strict";
|
|
50
|
-
MCP_ENDPOINT = "https://api.glasstrace.dev/mcp";
|
|
51
50
|
NEXT_CONFIG_NAMES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
52
51
|
}
|
|
53
52
|
});
|
|
54
53
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 hasGlasstraceImport2 = 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 (hasGlasstraceImport2) {
|
|
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
|
-
}
|
|
54
|
+
// ../../node_modules/zod/v4/core/core.js
|
|
55
|
+
// @__NO_SIDE_EFFECTS__
|
|
56
|
+
function $constructor(name, initializer3, params) {
|
|
57
|
+
function init(inst, def) {
|
|
58
|
+
if (!inst._zod) {
|
|
59
|
+
Object.defineProperty(inst, "_zod", {
|
|
60
|
+
value: {
|
|
61
|
+
def,
|
|
62
|
+
constr: _,
|
|
63
|
+
traits: /* @__PURE__ */ new Set()
|
|
64
|
+
},
|
|
65
|
+
enumerable: false
|
|
66
|
+
});
|
|
111
67
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return { injected: true, content: modified };
|
|
116
|
-
}
|
|
117
|
-
function resolveInstrumentationTarget(projectRoot) {
|
|
118
|
-
const rootExisting = [];
|
|
119
|
-
const srcExisting = [];
|
|
120
|
-
for (const name of INSTRUMENTATION_FILENAMES) {
|
|
121
|
-
const rootPath = path.join(projectRoot, name);
|
|
122
|
-
if (isRegularFile(rootPath)) {
|
|
123
|
-
rootExisting.push(rootPath);
|
|
68
|
+
if (inst._zod.traits.has(name)) {
|
|
69
|
+
return;
|
|
124
70
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
71
|
+
inst._zod.traits.add(name);
|
|
72
|
+
initializer3(inst, def);
|
|
73
|
+
const proto = _.prototype;
|
|
74
|
+
const keys = Object.keys(proto);
|
|
75
|
+
for (let i = 0; i < keys.length; i++) {
|
|
76
|
+
const k = keys[i];
|
|
77
|
+
if (!(k in inst)) {
|
|
78
|
+
inst[k] = proto[k].bind(inst);
|
|
79
|
+
}
|
|
128
80
|
}
|
|
129
81
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
target: null,
|
|
134
|
-
layout: null,
|
|
135
|
-
existing,
|
|
136
|
-
rootExisting,
|
|
137
|
-
srcExisting,
|
|
138
|
-
conflict: true
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (srcExisting.length > 0) {
|
|
142
|
-
return {
|
|
143
|
-
target: srcExisting[0],
|
|
144
|
-
layout: "src",
|
|
145
|
-
existing,
|
|
146
|
-
rootExisting,
|
|
147
|
-
srcExisting,
|
|
148
|
-
conflict: false
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
if (rootExisting.length > 0) {
|
|
152
|
-
return {
|
|
153
|
-
target: rootExisting[0],
|
|
154
|
-
layout: "root",
|
|
155
|
-
existing,
|
|
156
|
-
rootExisting,
|
|
157
|
-
srcExisting,
|
|
158
|
-
conflict: false
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
const srcDir = path.join(projectRoot, "src");
|
|
162
|
-
const layout = isDirectory(srcDir) ? "src" : "root";
|
|
163
|
-
const target = layout === "src" ? path.join(projectRoot, "src", "instrumentation.ts") : path.join(projectRoot, "instrumentation.ts");
|
|
164
|
-
return {
|
|
165
|
-
target,
|
|
166
|
-
layout,
|
|
167
|
-
existing,
|
|
168
|
-
rootExisting,
|
|
169
|
-
srcExisting,
|
|
170
|
-
conflict: false
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function isDirectory(p) {
|
|
174
|
-
try {
|
|
175
|
-
return fs.statSync(p).isDirectory();
|
|
176
|
-
} catch {
|
|
177
|
-
return false;
|
|
82
|
+
const Parent = params?.Parent ?? Object;
|
|
83
|
+
class Definition extends Parent {
|
|
178
84
|
}
|
|
179
|
-
}
|
|
180
|
-
function
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
85
|
+
Object.defineProperty(Definition, "name", { value: name });
|
|
86
|
+
function _(def) {
|
|
87
|
+
var _a2;
|
|
88
|
+
const inst = params?.Parent ? new Definition() : this;
|
|
89
|
+
init(inst, def);
|
|
90
|
+
(_a2 = inst._zod).deferred ?? (_a2.deferred = []);
|
|
91
|
+
for (const fn of inst._zod.deferred) {
|
|
92
|
+
fn();
|
|
185
93
|
}
|
|
186
|
-
return
|
|
187
|
-
} catch {
|
|
188
|
-
return false;
|
|
94
|
+
return inst;
|
|
189
95
|
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (!hasGlasstraceImport2) {
|
|
197
|
-
withImport = importLine + content;
|
|
198
|
-
} else {
|
|
199
|
-
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
|
|
200
|
-
const importMatch = importRegex.exec(content);
|
|
201
|
-
if (importMatch) {
|
|
202
|
-
const specifiers = importMatch[1];
|
|
203
|
-
const alreadyImported = specifiers.split(",").some((s) => s.trim() === "registerGlasstrace");
|
|
204
|
-
if (!alreadyImported) {
|
|
205
|
-
const existingImports = specifiers.trimEnd();
|
|
206
|
-
const separator = existingImports.endsWith(",") ? " " : ", ";
|
|
207
|
-
const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from "@glasstrace/sdk"`;
|
|
208
|
-
withImport = content.replace(importMatch[0], updatedImport);
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
withImport = importLine + content;
|
|
96
|
+
Object.defineProperty(_, "init", { value: init });
|
|
97
|
+
Object.defineProperty(_, Symbol.hasInstance, {
|
|
98
|
+
value: (inst) => {
|
|
99
|
+
if (params?.Parent && inst instanceof params.Parent)
|
|
100
|
+
return true;
|
|
101
|
+
return inst?._zod?.traits?.has(name);
|
|
212
102
|
}
|
|
213
|
-
}
|
|
214
|
-
const trailingNewline = withImport.endsWith("\n") ? "" : "\n";
|
|
215
|
-
return withImport + trailingNewline + functionBlock;
|
|
216
|
-
}
|
|
217
|
-
async function defaultInstrumentationPrompt(question, defaultValue) {
|
|
218
|
-
if (!process.stdin.isTTY) return defaultValue;
|
|
219
|
-
const readline2 = await import("node:readline");
|
|
220
|
-
const rl = readline2.createInterface({
|
|
221
|
-
input: process.stdin,
|
|
222
|
-
output: process.stdout
|
|
223
|
-
});
|
|
224
|
-
return new Promise((resolve2) => {
|
|
225
|
-
const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
|
|
226
|
-
rl.question(question + suffix, (answer) => {
|
|
227
|
-
rl.close();
|
|
228
|
-
const trimmed = answer.trim().toLowerCase();
|
|
229
|
-
if (trimmed === "") {
|
|
230
|
-
resolve2(defaultValue);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
resolve2(trimmed === "y" || trimmed === "yes");
|
|
234
|
-
});
|
|
235
103
|
});
|
|
104
|
+
Object.defineProperty(_, "name", { value: name });
|
|
105
|
+
return _;
|
|
236
106
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
107
|
+
function config(newConfig) {
|
|
108
|
+
if (newConfig)
|
|
109
|
+
Object.assign(globalConfig, newConfig);
|
|
110
|
+
return globalConfig;
|
|
111
|
+
}
|
|
112
|
+
var NEVER, $brand, $ZodAsyncError, $ZodEncodeError, globalConfig;
|
|
113
|
+
var init_core = __esm({
|
|
114
|
+
"../../node_modules/zod/v4/core/core.js"() {
|
|
115
|
+
"use strict";
|
|
116
|
+
NEVER = Object.freeze({
|
|
117
|
+
status: "aborted"
|
|
118
|
+
});
|
|
119
|
+
$brand = /* @__PURE__ */ Symbol("zod_brand");
|
|
120
|
+
$ZodAsyncError = class extends Error {
|
|
121
|
+
constructor() {
|
|
122
|
+
super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
|
|
123
|
+
}
|
|
247
124
|
};
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const prompt = options.prompt ?? defaultInstrumentationPrompt;
|
|
256
|
-
if (!fs.existsSync(filePath)) {
|
|
257
|
-
const content = `import { registerGlasstrace } from "@glasstrace/sdk";
|
|
258
|
-
|
|
259
|
-
export async function register() {
|
|
260
|
-
// Glasstrace must be registered before Prisma instrumentation
|
|
261
|
-
// to ensure all ORM spans are captured correctly.
|
|
262
|
-
// If you use @prisma/instrumentation, import it after this call.
|
|
263
|
-
registerGlasstrace();
|
|
264
|
-
}
|
|
265
|
-
`;
|
|
266
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
267
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
268
|
-
return { action: "created", filePath, layout };
|
|
269
|
-
}
|
|
270
|
-
const existing = fs.readFileSync(filePath, "utf-8");
|
|
271
|
-
if (hasRegisterGlasstraceCall(existing)) {
|
|
272
|
-
return { action: "already-registered", filePath, layout };
|
|
273
|
-
}
|
|
274
|
-
if (!force) {
|
|
275
|
-
const approved = await prompt(
|
|
276
|
-
`Merge registerGlasstrace() into ${path.relative(projectRoot, filePath)}?`,
|
|
277
|
-
false
|
|
278
|
-
);
|
|
279
|
-
if (!approved) {
|
|
280
|
-
return { action: "skipped", filePath, layout };
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
const injectResult = injectRegisterGlasstrace(existing);
|
|
284
|
-
if (injectResult.injected) {
|
|
285
|
-
fs.writeFileSync(filePath, injectResult.content, "utf-8");
|
|
286
|
-
return { action: "injected", filePath, layout };
|
|
287
|
-
}
|
|
288
|
-
const appended = appendRegisterFunction(existing);
|
|
289
|
-
fs.writeFileSync(filePath, appended, "utf-8");
|
|
290
|
-
return { action: "appended", filePath, layout };
|
|
291
|
-
}
|
|
292
|
-
async function scaffoldNextConfig(projectRoot) {
|
|
293
|
-
let configPath;
|
|
294
|
-
let configName;
|
|
295
|
-
for (const name of NEXT_CONFIG_NAMES) {
|
|
296
|
-
const candidate = path.join(projectRoot, name);
|
|
297
|
-
if (fs.existsSync(candidate)) {
|
|
298
|
-
configPath = candidate;
|
|
299
|
-
configName = name;
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (configPath === void 0 || configName === void 0) {
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
const existing = fs.readFileSync(configPath, "utf-8");
|
|
307
|
-
if (existing.trim().length === 0) {
|
|
308
|
-
return { modified: false, reason: "empty-file" };
|
|
309
|
-
}
|
|
310
|
-
if (existing.includes("withGlasstraceConfig")) {
|
|
311
|
-
return { modified: false, reason: "already-wrapped" };
|
|
312
|
-
}
|
|
313
|
-
const isESM = configName.endsWith(".ts") || configName.endsWith(".mjs");
|
|
314
|
-
if (isESM) {
|
|
315
|
-
const importLine = 'import { withGlasstraceConfig } from "@glasstrace/sdk";\n';
|
|
316
|
-
const wrapResult2 = wrapExport(existing);
|
|
317
|
-
if (!wrapResult2.wrapped) {
|
|
318
|
-
return { modified: false, reason: "no-export" };
|
|
319
|
-
}
|
|
320
|
-
const modified2 = importLine + "\n" + wrapResult2.content;
|
|
321
|
-
fs.writeFileSync(configPath, modified2, "utf-8");
|
|
322
|
-
return { modified: true };
|
|
323
|
-
}
|
|
324
|
-
const requireLine = 'const { withGlasstraceConfig } = require("@glasstrace/sdk");\n';
|
|
325
|
-
const wrapResult = wrapCJSExport(existing);
|
|
326
|
-
if (!wrapResult.wrapped) {
|
|
327
|
-
return { modified: false, reason: "no-export" };
|
|
328
|
-
}
|
|
329
|
-
const modified = requireLine + "\n" + wrapResult.content;
|
|
330
|
-
fs.writeFileSync(configPath, modified, "utf-8");
|
|
331
|
-
return { modified: true };
|
|
332
|
-
}
|
|
333
|
-
function wrapExport(content) {
|
|
334
|
-
const marker = "export default";
|
|
335
|
-
const idx = content.lastIndexOf(marker);
|
|
336
|
-
if (idx === -1) {
|
|
337
|
-
return { content, wrapped: false };
|
|
338
|
-
}
|
|
339
|
-
const preamble = content.slice(0, idx);
|
|
340
|
-
const exprRaw = content.slice(idx + marker.length);
|
|
341
|
-
const expr = exprRaw.trim().replace(/;?\s*$/, "");
|
|
342
|
-
if (expr.length === 0) {
|
|
343
|
-
return { content, wrapped: false };
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
content: preamble + `export default withGlasstraceConfig(${expr});
|
|
347
|
-
`,
|
|
348
|
-
wrapped: true
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function wrapCJSExport(content) {
|
|
352
|
-
const cjsMarker = "module.exports";
|
|
353
|
-
const cjsIdx = content.lastIndexOf(cjsMarker);
|
|
354
|
-
if (cjsIdx === -1) {
|
|
355
|
-
return { content, wrapped: false };
|
|
356
|
-
}
|
|
357
|
-
const preamble = content.slice(0, cjsIdx);
|
|
358
|
-
const afterMarker = content.slice(cjsIdx + cjsMarker.length);
|
|
359
|
-
const eqMatch = /^\s*=\s*/.exec(afterMarker);
|
|
360
|
-
if (!eqMatch) {
|
|
361
|
-
return { content, wrapped: false };
|
|
362
|
-
}
|
|
363
|
-
const exprRaw = afterMarker.slice(eqMatch[0].length);
|
|
364
|
-
const expr = exprRaw.trim().replace(/;?\s*$/, "");
|
|
365
|
-
if (expr.length === 0) {
|
|
366
|
-
return { content, wrapped: false };
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
content: preamble + `module.exports = withGlasstraceConfig(${expr});
|
|
370
|
-
`,
|
|
371
|
-
wrapped: true
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
function readEnvLocalApiKey(content) {
|
|
375
|
-
let last = null;
|
|
376
|
-
const regex = /^\s*GLASSTRACE_API_KEY\s*=\s*(.*)$/gm;
|
|
377
|
-
let match;
|
|
378
|
-
while ((match = regex.exec(content)) !== null) {
|
|
379
|
-
const raw = match[1].trim();
|
|
380
|
-
if (raw === "") continue;
|
|
381
|
-
const unquoted = raw.replace(/^(['"])(.*)\1$/, "$2");
|
|
382
|
-
if (unquoted === "" || unquoted === "your_key_here") continue;
|
|
383
|
-
last = unquoted;
|
|
384
|
-
}
|
|
385
|
-
return last;
|
|
386
|
-
}
|
|
387
|
-
function isDevApiKey(value) {
|
|
388
|
-
if (value === null || value === void 0) return false;
|
|
389
|
-
return value.trim().startsWith("gt_dev_");
|
|
390
|
-
}
|
|
391
|
-
async function scaffoldEnvLocal(projectRoot) {
|
|
392
|
-
const filePath = path.join(projectRoot, ".env.local");
|
|
393
|
-
if (fs.existsSync(filePath)) {
|
|
394
|
-
const existing = fs.readFileSync(filePath, "utf-8");
|
|
395
|
-
if (/^\s*#?\s*GLASSTRACE_API_KEY\s*=/m.test(existing)) {
|
|
396
|
-
return false;
|
|
397
|
-
}
|
|
398
|
-
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
399
|
-
fs.writeFileSync(filePath, existing + separator + "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
|
|
400
|
-
return true;
|
|
401
|
-
}
|
|
402
|
-
fs.writeFileSync(filePath, "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
405
|
-
async function addCoverageMapEnv(projectRoot) {
|
|
406
|
-
const filePath = path.join(projectRoot, ".env.local");
|
|
407
|
-
if (!fs.existsSync(filePath)) {
|
|
408
|
-
fs.writeFileSync(filePath, "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
const existing = fs.readFileSync(filePath, "utf-8");
|
|
412
|
-
const keyRegex = /^(\s*GLASSTRACE_COVERAGE_MAP\s*=\s*)(.*)$/m;
|
|
413
|
-
const keyMatch = keyRegex.exec(existing);
|
|
414
|
-
if (keyMatch) {
|
|
415
|
-
const currentValue = keyMatch[2].trim();
|
|
416
|
-
if (currentValue === "true") {
|
|
417
|
-
return false;
|
|
418
|
-
}
|
|
419
|
-
const updated = existing.replace(keyRegex, `${keyMatch[1]}true`);
|
|
420
|
-
fs.writeFileSync(filePath, updated, "utf-8");
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
424
|
-
fs.writeFileSync(filePath, existing + separator + "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
|
|
425
|
-
return true;
|
|
426
|
-
}
|
|
427
|
-
async function scaffoldGitignore(projectRoot) {
|
|
428
|
-
const filePath = path.join(projectRoot, ".gitignore");
|
|
429
|
-
if (fs.existsSync(filePath)) {
|
|
430
|
-
const existing = fs.readFileSync(filePath, "utf-8");
|
|
431
|
-
const lines = existing.split("\n").map((l) => l.trim());
|
|
432
|
-
if (lines.includes(".glasstrace/")) {
|
|
433
|
-
return false;
|
|
434
|
-
}
|
|
435
|
-
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
436
|
-
fs.writeFileSync(filePath, existing + separator + ".glasstrace/\n", "utf-8");
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
fs.writeFileSync(filePath, ".glasstrace/\n", "utf-8");
|
|
440
|
-
return true;
|
|
441
|
-
}
|
|
442
|
-
function mcpConfigMatches(existingContent, expectedContent) {
|
|
443
|
-
const trimmedExpected = expectedContent.trim();
|
|
444
|
-
try {
|
|
445
|
-
const existingParsed = JSON.parse(existingContent);
|
|
446
|
-
const expectedParsed = JSON.parse(trimmedExpected);
|
|
447
|
-
return JSON.stringify(canonicalize(existingParsed)) === JSON.stringify(canonicalize(expectedParsed));
|
|
448
|
-
} catch {
|
|
449
|
-
}
|
|
450
|
-
return existingContent.trim() === trimmedExpected;
|
|
451
|
-
}
|
|
452
|
-
function canonicalize(value) {
|
|
453
|
-
if (Array.isArray(value)) {
|
|
454
|
-
return value.map(canonicalize);
|
|
455
|
-
}
|
|
456
|
-
if (value !== null && typeof value === "object") {
|
|
457
|
-
const obj = value;
|
|
458
|
-
const sorted = {};
|
|
459
|
-
for (const key of Object.keys(obj).sort()) {
|
|
460
|
-
sorted[key] = canonicalize(obj[key]);
|
|
461
|
-
}
|
|
462
|
-
return sorted;
|
|
463
|
-
}
|
|
464
|
-
return value;
|
|
465
|
-
}
|
|
466
|
-
async function scaffoldMcpMarker(projectRoot, anonKey) {
|
|
467
|
-
const dirPath = path.join(projectRoot, ".glasstrace");
|
|
468
|
-
const markerPath = path.join(dirPath, "mcp-connected");
|
|
469
|
-
const keyHash = identityFingerprint(anonKey);
|
|
470
|
-
if (fs.existsSync(markerPath)) {
|
|
471
|
-
try {
|
|
472
|
-
const existing = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
473
|
-
if (existing.keyHash === keyHash) {
|
|
474
|
-
return false;
|
|
475
|
-
}
|
|
476
|
-
} catch {
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
fs.mkdirSync(dirPath, { recursive: true, mode: 448 });
|
|
480
|
-
const marker = JSON.stringify(
|
|
481
|
-
{ keyHash, configuredAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
482
|
-
null,
|
|
483
|
-
2
|
|
484
|
-
);
|
|
485
|
-
fs.writeFileSync(markerPath, marker, { mode: 384 });
|
|
486
|
-
fs.chmodSync(markerPath, 384);
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
var import_node_crypto, fs, path, INSTRUMENTATION_FILENAMES;
|
|
490
|
-
var init_scaffolder = __esm({
|
|
491
|
-
"src/cli/scaffolder.ts"() {
|
|
492
|
-
"use strict";
|
|
493
|
-
import_node_crypto = require("node:crypto");
|
|
494
|
-
fs = __toESM(require("node:fs"), 1);
|
|
495
|
-
path = __toESM(require("node:path"), 1);
|
|
496
|
-
init_constants();
|
|
497
|
-
INSTRUMENTATION_FILENAMES = [
|
|
498
|
-
"instrumentation.ts",
|
|
499
|
-
"instrumentation.js",
|
|
500
|
-
"instrumentation.mjs"
|
|
501
|
-
];
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
// ../../node_modules/zod/v4/core/core.js
|
|
506
|
-
// @__NO_SIDE_EFFECTS__
|
|
507
|
-
function $constructor(name, initializer3, params) {
|
|
508
|
-
function init(inst, def) {
|
|
509
|
-
if (!inst._zod) {
|
|
510
|
-
Object.defineProperty(inst, "_zod", {
|
|
511
|
-
value: {
|
|
512
|
-
def,
|
|
513
|
-
constr: _,
|
|
514
|
-
traits: /* @__PURE__ */ new Set()
|
|
515
|
-
},
|
|
516
|
-
enumerable: false
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
if (inst._zod.traits.has(name)) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
inst._zod.traits.add(name);
|
|
523
|
-
initializer3(inst, def);
|
|
524
|
-
const proto = _.prototype;
|
|
525
|
-
const keys = Object.keys(proto);
|
|
526
|
-
for (let i = 0; i < keys.length; i++) {
|
|
527
|
-
const k = keys[i];
|
|
528
|
-
if (!(k in inst)) {
|
|
529
|
-
inst[k] = proto[k].bind(inst);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
const Parent = params?.Parent ?? Object;
|
|
534
|
-
class Definition extends Parent {
|
|
535
|
-
}
|
|
536
|
-
Object.defineProperty(Definition, "name", { value: name });
|
|
537
|
-
function _(def) {
|
|
538
|
-
var _a2;
|
|
539
|
-
const inst = params?.Parent ? new Definition() : this;
|
|
540
|
-
init(inst, def);
|
|
541
|
-
(_a2 = inst._zod).deferred ?? (_a2.deferred = []);
|
|
542
|
-
for (const fn of inst._zod.deferred) {
|
|
543
|
-
fn();
|
|
544
|
-
}
|
|
545
|
-
return inst;
|
|
546
|
-
}
|
|
547
|
-
Object.defineProperty(_, "init", { value: init });
|
|
548
|
-
Object.defineProperty(_, Symbol.hasInstance, {
|
|
549
|
-
value: (inst) => {
|
|
550
|
-
if (params?.Parent && inst instanceof params.Parent)
|
|
551
|
-
return true;
|
|
552
|
-
return inst?._zod?.traits?.has(name);
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
Object.defineProperty(_, "name", { value: name });
|
|
556
|
-
return _;
|
|
557
|
-
}
|
|
558
|
-
function config(newConfig) {
|
|
559
|
-
if (newConfig)
|
|
560
|
-
Object.assign(globalConfig, newConfig);
|
|
561
|
-
return globalConfig;
|
|
562
|
-
}
|
|
563
|
-
var NEVER, $brand, $ZodAsyncError, $ZodEncodeError, globalConfig;
|
|
564
|
-
var init_core = __esm({
|
|
565
|
-
"../../node_modules/zod/v4/core/core.js"() {
|
|
566
|
-
"use strict";
|
|
567
|
-
NEVER = Object.freeze({
|
|
568
|
-
status: "aborted"
|
|
569
|
-
});
|
|
570
|
-
$brand = /* @__PURE__ */ Symbol("zod_brand");
|
|
571
|
-
$ZodAsyncError = class extends Error {
|
|
572
|
-
constructor() {
|
|
573
|
-
super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
|
-
$ZodEncodeError = class extends Error {
|
|
577
|
-
constructor(name) {
|
|
578
|
-
super(`Encountered unidirectional transform during encode: ${name}`);
|
|
579
|
-
this.name = "ZodEncodeError";
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
globalConfig = {};
|
|
125
|
+
$ZodEncodeError = class extends Error {
|
|
126
|
+
constructor(name) {
|
|
127
|
+
super(`Encountered unidirectional transform during encode: ${name}`);
|
|
128
|
+
this.name = "ZodEncodeError";
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
globalConfig = {};
|
|
583
132
|
}
|
|
584
133
|
});
|
|
585
134
|
|
|
@@ -15141,6 +14690,22 @@ async function readAnonKey(projectRoot) {
|
|
|
15141
14690
|
}
|
|
15142
14691
|
return null;
|
|
15143
14692
|
}
|
|
14693
|
+
async function readClaimedKey(projectRoot) {
|
|
14694
|
+
const root = projectRoot ?? process.cwd();
|
|
14695
|
+
const modules = await loadFsPath();
|
|
14696
|
+
if (!modules) return null;
|
|
14697
|
+
const keyPath = modules.path.join(root, GLASSTRACE_DIR, CLAIMED_KEY_FILE);
|
|
14698
|
+
try {
|
|
14699
|
+
const content = await modules.fs.readFile(keyPath, "utf-8");
|
|
14700
|
+
const trimmed = content.trim();
|
|
14701
|
+
const parsed = DevApiKeySchema.safeParse(trimmed);
|
|
14702
|
+
if (parsed.success) {
|
|
14703
|
+
return parsed.data;
|
|
14704
|
+
}
|
|
14705
|
+
} catch {
|
|
14706
|
+
}
|
|
14707
|
+
return null;
|
|
14708
|
+
}
|
|
15144
14709
|
async function getOrCreateAnonKey(projectRoot) {
|
|
15145
14710
|
const root = projectRoot ?? process.cwd();
|
|
15146
14711
|
const existingKey = await readAnonKey(root);
|
|
@@ -15182,21 +14747,208 @@ async function getOrCreateAnonKey(projectRoot) {
|
|
|
15182
14747
|
} catch {
|
|
15183
14748
|
}
|
|
15184
14749
|
}
|
|
15185
|
-
ephemeralKeyCache.set(root, newKey);
|
|
15186
|
-
console.warn(
|
|
15187
|
-
`[glasstrace] Failed to persist anonymous key to ${keyPath}: ${err instanceof Error ? err.message : String(err)}. Using ephemeral key.`
|
|
15188
|
-
);
|
|
15189
|
-
return newKey;
|
|
14750
|
+
ephemeralKeyCache.set(root, newKey);
|
|
14751
|
+
console.warn(
|
|
14752
|
+
`[glasstrace] Failed to persist anonymous key to ${keyPath}: ${err instanceof Error ? err.message : String(err)}. Using ephemeral key.`
|
|
14753
|
+
);
|
|
14754
|
+
return newKey;
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14757
|
+
var GLASSTRACE_DIR, ANON_KEY_FILE, CLAIMED_KEY_FILE, fsPathCache, ephemeralKeyCache;
|
|
14758
|
+
var init_anon_key = __esm({
|
|
14759
|
+
"src/anon-key.ts"() {
|
|
14760
|
+
"use strict";
|
|
14761
|
+
init_dist();
|
|
14762
|
+
GLASSTRACE_DIR = ".glasstrace";
|
|
14763
|
+
ANON_KEY_FILE = "anon_key";
|
|
14764
|
+
CLAIMED_KEY_FILE = "claimed-key";
|
|
14765
|
+
ephemeralKeyCache = /* @__PURE__ */ new Map();
|
|
14766
|
+
}
|
|
14767
|
+
});
|
|
14768
|
+
|
|
14769
|
+
// src/mcp-runtime.ts
|
|
14770
|
+
async function loadFsPath2() {
|
|
14771
|
+
if (fsPathCache2 !== void 0) return fsPathCache2;
|
|
14772
|
+
try {
|
|
14773
|
+
const [fs10, path10] = await Promise.all([
|
|
14774
|
+
import("node:fs/promises"),
|
|
14775
|
+
import("node:path")
|
|
14776
|
+
]);
|
|
14777
|
+
fsPathCache2 = { fs: fs10, path: path10 };
|
|
14778
|
+
return fsPathCache2;
|
|
14779
|
+
} catch {
|
|
14780
|
+
fsPathCache2 = null;
|
|
14781
|
+
return null;
|
|
14782
|
+
}
|
|
14783
|
+
}
|
|
14784
|
+
function identityFingerprint(token) {
|
|
14785
|
+
return `sha256:${(0, import_node_crypto.createHash)("sha256").update(token).digest("hex")}`;
|
|
14786
|
+
}
|
|
14787
|
+
function mcpConfigMatches(existingContent, expectedContent) {
|
|
14788
|
+
const trimmedExpected = expectedContent.trim();
|
|
14789
|
+
try {
|
|
14790
|
+
const existingParsed = JSON.parse(existingContent);
|
|
14791
|
+
const expectedParsed = JSON.parse(trimmedExpected);
|
|
14792
|
+
return JSON.stringify(canonicalize(existingParsed)) === JSON.stringify(canonicalize(expectedParsed));
|
|
14793
|
+
} catch {
|
|
14794
|
+
}
|
|
14795
|
+
return existingContent.trim() === trimmedExpected;
|
|
14796
|
+
}
|
|
14797
|
+
function canonicalize(value) {
|
|
14798
|
+
if (Array.isArray(value)) {
|
|
14799
|
+
return value.map(canonicalize);
|
|
14800
|
+
}
|
|
14801
|
+
if (value !== null && typeof value === "object") {
|
|
14802
|
+
const obj = value;
|
|
14803
|
+
const sorted = {};
|
|
14804
|
+
for (const key of Object.keys(obj).sort()) {
|
|
14805
|
+
sorted[key] = canonicalize(obj[key]);
|
|
14806
|
+
}
|
|
14807
|
+
return sorted;
|
|
14808
|
+
}
|
|
14809
|
+
return value;
|
|
14810
|
+
}
|
|
14811
|
+
function readEnvLocalApiKey(content) {
|
|
14812
|
+
let last = null;
|
|
14813
|
+
const regex = /^\s*GLASSTRACE_API_KEY\s*=\s*(.*)$/gm;
|
|
14814
|
+
let match;
|
|
14815
|
+
while ((match = regex.exec(content)) !== null) {
|
|
14816
|
+
const raw = match[1].trim();
|
|
14817
|
+
if (raw === "") continue;
|
|
14818
|
+
const unquoted = raw.replace(/^(['"])(.*)\1$/, "$2");
|
|
14819
|
+
if (unquoted === "" || unquoted === "your_key_here") continue;
|
|
14820
|
+
last = unquoted;
|
|
14821
|
+
}
|
|
14822
|
+
return last;
|
|
14823
|
+
}
|
|
14824
|
+
function isDevApiKey(value) {
|
|
14825
|
+
if (value === null || value === void 0) return false;
|
|
14826
|
+
return value.trim().startsWith("gt_dev_");
|
|
14827
|
+
}
|
|
14828
|
+
function isAnonApiKey(value) {
|
|
14829
|
+
if (value === null || value === void 0) return false;
|
|
14830
|
+
return AnonApiKeySchema.safeParse(value).success;
|
|
14831
|
+
}
|
|
14832
|
+
async function resolveEffectiveMcpCredential(projectRoot) {
|
|
14833
|
+
const root = projectRoot ?? process.cwd();
|
|
14834
|
+
const warnings = [];
|
|
14835
|
+
const envLocalKey = await readEnvLocalDevKey(root, warnings);
|
|
14836
|
+
const claimedKey = envLocalKey === null ? await readClaimedKey(root) : null;
|
|
14837
|
+
const anonKey = await readAnonKey(root);
|
|
14838
|
+
let effective = null;
|
|
14839
|
+
if (envLocalKey !== null) {
|
|
14840
|
+
effective = { source: "env-local", key: envLocalKey };
|
|
14841
|
+
} else if (claimedKey !== null) {
|
|
14842
|
+
effective = { source: "claimed-key", key: claimedKey };
|
|
14843
|
+
warnings.push("claimed-key-only");
|
|
14844
|
+
} else if (anonKey !== null) {
|
|
14845
|
+
effective = { source: "anon", key: anonKey };
|
|
14846
|
+
}
|
|
14847
|
+
return { effective, anonKey, warnings };
|
|
14848
|
+
}
|
|
14849
|
+
async function readEnvLocalDevKey(root, warnings) {
|
|
14850
|
+
const modules = await loadFsPath2();
|
|
14851
|
+
if (!modules) return null;
|
|
14852
|
+
const envPath = modules.path.join(root, ".env.local");
|
|
14853
|
+
let content;
|
|
14854
|
+
try {
|
|
14855
|
+
content = await modules.fs.readFile(envPath, "utf-8");
|
|
14856
|
+
} catch {
|
|
14857
|
+
return null;
|
|
14858
|
+
}
|
|
14859
|
+
const raw = readEnvLocalApiKey(content);
|
|
14860
|
+
if (raw === null) return null;
|
|
14861
|
+
const parsed = DevApiKeySchema.safeParse(raw);
|
|
14862
|
+
if (!parsed.success) {
|
|
14863
|
+
warnings.push("malformed-env-local");
|
|
14864
|
+
return null;
|
|
14865
|
+
}
|
|
14866
|
+
return parsed.data;
|
|
14867
|
+
}
|
|
14868
|
+
async function readMcpMarker(projectRoot) {
|
|
14869
|
+
const root = projectRoot ?? process.cwd();
|
|
14870
|
+
const modules = await loadFsPath2();
|
|
14871
|
+
if (!modules) return { status: "absent" };
|
|
14872
|
+
const markerPath = modules.path.join(root, GLASSTRACE_DIR2, MCP_MARKER_FILE);
|
|
14873
|
+
let content;
|
|
14874
|
+
try {
|
|
14875
|
+
content = await modules.fs.readFile(markerPath, "utf-8");
|
|
14876
|
+
} catch {
|
|
14877
|
+
return { status: "absent" };
|
|
14878
|
+
}
|
|
14879
|
+
let parsed;
|
|
14880
|
+
try {
|
|
14881
|
+
parsed = JSON.parse(content);
|
|
14882
|
+
} catch {
|
|
14883
|
+
return { status: "corrupted" };
|
|
14884
|
+
}
|
|
14885
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
14886
|
+
return { status: "corrupted" };
|
|
14887
|
+
}
|
|
14888
|
+
const obj = parsed;
|
|
14889
|
+
const version2 = obj["version"];
|
|
14890
|
+
if (version2 === void 0) {
|
|
14891
|
+
const keyHash = obj["keyHash"];
|
|
14892
|
+
if (typeof keyHash !== "string" || keyHash === "") {
|
|
14893
|
+
return { status: "corrupted" };
|
|
14894
|
+
}
|
|
14895
|
+
return {
|
|
14896
|
+
status: "valid",
|
|
14897
|
+
credentialSource: "anon",
|
|
14898
|
+
credentialHash: keyHash
|
|
14899
|
+
};
|
|
14900
|
+
}
|
|
14901
|
+
if (version2 === 2) {
|
|
14902
|
+
const source = obj["credentialSource"];
|
|
14903
|
+
const hash2 = obj["credentialHash"];
|
|
14904
|
+
if (source !== "env-local" && source !== "claimed-key" && source !== "anon" || typeof hash2 !== "string" || hash2 === "") {
|
|
14905
|
+
return { status: "corrupted" };
|
|
14906
|
+
}
|
|
14907
|
+
return {
|
|
14908
|
+
status: "valid",
|
|
14909
|
+
credentialSource: source,
|
|
14910
|
+
credentialHash: hash2
|
|
14911
|
+
};
|
|
14912
|
+
}
|
|
14913
|
+
if (typeof version2 === "number" && version2 > 2) {
|
|
14914
|
+
return { status: "unknown-version" };
|
|
15190
14915
|
}
|
|
14916
|
+
return { status: "corrupted" };
|
|
15191
14917
|
}
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
14918
|
+
async function writeMcpMarker(projectRoot, target) {
|
|
14919
|
+
const modules = await loadFsPath2();
|
|
14920
|
+
if (!modules) return false;
|
|
14921
|
+
const dirPath = modules.path.join(projectRoot, GLASSTRACE_DIR2);
|
|
14922
|
+
const markerPath = modules.path.join(dirPath, MCP_MARKER_FILE);
|
|
14923
|
+
const state = await readMcpMarker(projectRoot);
|
|
14924
|
+
if (state.status === "valid" && state.credentialSource === target.credentialSource && state.credentialHash === target.credentialHash) {
|
|
14925
|
+
return false;
|
|
14926
|
+
}
|
|
14927
|
+
await modules.fs.mkdir(dirPath, { recursive: true, mode: 448 });
|
|
14928
|
+
const body = JSON.stringify(
|
|
14929
|
+
{
|
|
14930
|
+
version: 2,
|
|
14931
|
+
credentialSource: target.credentialSource,
|
|
14932
|
+
credentialHash: target.credentialHash,
|
|
14933
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14934
|
+
},
|
|
14935
|
+
null,
|
|
14936
|
+
2
|
|
14937
|
+
);
|
|
14938
|
+
await modules.fs.writeFile(markerPath, body, { mode: 384 });
|
|
14939
|
+
await modules.fs.chmod(markerPath, 384);
|
|
14940
|
+
return true;
|
|
14941
|
+
}
|
|
14942
|
+
var import_node_crypto, MCP_ENDPOINT, fsPathCache2, MCP_MARKER_FILE, GLASSTRACE_DIR2;
|
|
14943
|
+
var init_mcp_runtime = __esm({
|
|
14944
|
+
"src/mcp-runtime.ts"() {
|
|
15195
14945
|
"use strict";
|
|
14946
|
+
import_node_crypto = require("node:crypto");
|
|
15196
14947
|
init_dist();
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
14948
|
+
init_anon_key();
|
|
14949
|
+
MCP_ENDPOINT = "https://api.glasstrace.dev/mcp";
|
|
14950
|
+
MCP_MARKER_FILE = "mcp-connected";
|
|
14951
|
+
GLASSTRACE_DIR2 = ".glasstrace";
|
|
15200
14952
|
}
|
|
15201
14953
|
});
|
|
15202
14954
|
|
|
@@ -15360,12 +15112,12 @@ var init_detect = __esm({
|
|
|
15360
15112
|
});
|
|
15361
15113
|
|
|
15362
15114
|
// src/agent-detection/configs.ts
|
|
15363
|
-
function generateMcpConfig(agent, endpoint,
|
|
15115
|
+
function generateMcpConfig(agent, endpoint, bearer) {
|
|
15364
15116
|
if (!endpoint || endpoint.trim() === "") {
|
|
15365
15117
|
throw new Error("endpoint must not be empty");
|
|
15366
15118
|
}
|
|
15367
|
-
if (!
|
|
15368
|
-
throw new Error("
|
|
15119
|
+
if (!bearer || bearer.trim() === "") {
|
|
15120
|
+
throw new Error("bearer must not be empty");
|
|
15369
15121
|
}
|
|
15370
15122
|
switch (agent.name) {
|
|
15371
15123
|
case "claude":
|
|
@@ -15376,7 +15128,7 @@ function generateMcpConfig(agent, endpoint, anonKey) {
|
|
|
15376
15128
|
type: "http",
|
|
15377
15129
|
url: endpoint,
|
|
15378
15130
|
headers: {
|
|
15379
|
-
Authorization: `Bearer ${
|
|
15131
|
+
Authorization: `Bearer ${bearer}`
|
|
15380
15132
|
}
|
|
15381
15133
|
}
|
|
15382
15134
|
}
|
|
@@ -15400,7 +15152,7 @@ function generateMcpConfig(agent, endpoint, anonKey) {
|
|
|
15400
15152
|
glasstrace: {
|
|
15401
15153
|
httpUrl: endpoint,
|
|
15402
15154
|
headers: {
|
|
15403
|
-
Authorization: `Bearer ${
|
|
15155
|
+
Authorization: `Bearer ${bearer}`
|
|
15404
15156
|
}
|
|
15405
15157
|
}
|
|
15406
15158
|
}
|
|
@@ -15415,7 +15167,7 @@ function generateMcpConfig(agent, endpoint, anonKey) {
|
|
|
15415
15167
|
glasstrace: {
|
|
15416
15168
|
url: endpoint,
|
|
15417
15169
|
headers: {
|
|
15418
|
-
Authorization: `Bearer ${
|
|
15170
|
+
Authorization: `Bearer ${bearer}`
|
|
15419
15171
|
}
|
|
15420
15172
|
}
|
|
15421
15173
|
}
|
|
@@ -15430,7 +15182,7 @@ function generateMcpConfig(agent, endpoint, anonKey) {
|
|
|
15430
15182
|
glasstrace: {
|
|
15431
15183
|
serverUrl: endpoint,
|
|
15432
15184
|
headers: {
|
|
15433
|
-
Authorization: `Bearer ${
|
|
15185
|
+
Authorization: `Bearer ${bearer}`
|
|
15434
15186
|
}
|
|
15435
15187
|
}
|
|
15436
15188
|
}
|
|
@@ -15445,7 +15197,7 @@ function generateMcpConfig(agent, endpoint, anonKey) {
|
|
|
15445
15197
|
glasstrace: {
|
|
15446
15198
|
url: endpoint,
|
|
15447
15199
|
headers: {
|
|
15448
|
-
Authorization: `Bearer ${
|
|
15200
|
+
Authorization: `Bearer ${bearer}`
|
|
15449
15201
|
}
|
|
15450
15202
|
}
|
|
15451
15203
|
}
|
|
@@ -16743,677 +16495,1107 @@ async function runUninit(options) {
|
|
|
16743
16495
|
}
|
|
16744
16496
|
}
|
|
16745
16497
|
}
|
|
16746
|
-
} catch (err) {
|
|
16747
|
-
errors.push(
|
|
16748
|
-
`Failed to process .gitignore: ${err instanceof Error ? err.message : String(err)}`
|
|
16498
|
+
} catch (err) {
|
|
16499
|
+
errors.push(
|
|
16500
|
+
`Failed to process .gitignore: ${err instanceof Error ? err.message : String(err)}`
|
|
16501
|
+
);
|
|
16502
|
+
}
|
|
16503
|
+
try {
|
|
16504
|
+
for (const configFile of MCP_CONFIG_FILES) {
|
|
16505
|
+
const configPath = path5.join(projectRoot, configFile);
|
|
16506
|
+
if (!fs5.existsSync(configPath)) {
|
|
16507
|
+
continue;
|
|
16508
|
+
}
|
|
16509
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
16510
|
+
const result = processJsonMcpConfig(content);
|
|
16511
|
+
if (result.action === "deleted") {
|
|
16512
|
+
if (!dryRun) {
|
|
16513
|
+
fs5.unlinkSync(configPath);
|
|
16514
|
+
}
|
|
16515
|
+
summary.push(`${prefix}Deleted ${configFile}`);
|
|
16516
|
+
} else if (result.action === "removed-key" && result.content !== void 0) {
|
|
16517
|
+
if (!dryRun) {
|
|
16518
|
+
fs5.writeFileSync(configPath, result.content, "utf-8");
|
|
16519
|
+
}
|
|
16520
|
+
summary.push(`${prefix}Removed glasstrace from ${configFile}`);
|
|
16521
|
+
}
|
|
16522
|
+
}
|
|
16523
|
+
const codexConfigPath = path5.join(projectRoot, ".codex", "config.toml");
|
|
16524
|
+
if (fs5.existsSync(codexConfigPath)) {
|
|
16525
|
+
const content = fs5.readFileSync(codexConfigPath, "utf-8");
|
|
16526
|
+
const tomlResult = processTomlMcpConfig(content);
|
|
16527
|
+
if (tomlResult.action === "deleted") {
|
|
16528
|
+
if (!dryRun) {
|
|
16529
|
+
fs5.unlinkSync(codexConfigPath);
|
|
16530
|
+
}
|
|
16531
|
+
summary.push(`${prefix}Deleted .codex/config.toml`);
|
|
16532
|
+
} else if (tomlResult.action === "removed-section" && tomlResult.content !== void 0) {
|
|
16533
|
+
if (!dryRun) {
|
|
16534
|
+
fs5.writeFileSync(codexConfigPath, tomlResult.content, "utf-8");
|
|
16535
|
+
}
|
|
16536
|
+
summary.push(`${prefix}Removed glasstrace from .codex/config.toml`);
|
|
16537
|
+
}
|
|
16538
|
+
}
|
|
16539
|
+
const hasWindsurfMarkers = fs5.existsSync(path5.join(projectRoot, ".windsurfrules")) || fs5.existsSync(path5.join(projectRoot, ".windsurf"));
|
|
16540
|
+
if (hasWindsurfMarkers) {
|
|
16541
|
+
const windsurfConfigPath = path5.join(
|
|
16542
|
+
os.homedir(),
|
|
16543
|
+
".codeium",
|
|
16544
|
+
"windsurf",
|
|
16545
|
+
"mcp_config.json"
|
|
16546
|
+
);
|
|
16547
|
+
if (fs5.existsSync(windsurfConfigPath)) {
|
|
16548
|
+
const content = fs5.readFileSync(windsurfConfigPath, "utf-8");
|
|
16549
|
+
const windsurfResult = processJsonMcpConfig(content);
|
|
16550
|
+
const home = os.homedir();
|
|
16551
|
+
const displayPath = windsurfConfigPath.startsWith(home) ? "~" + windsurfConfigPath.slice(home.length) : windsurfConfigPath;
|
|
16552
|
+
if (windsurfResult.action === "deleted") {
|
|
16553
|
+
if (!dryRun) {
|
|
16554
|
+
fs5.unlinkSync(windsurfConfigPath);
|
|
16555
|
+
}
|
|
16556
|
+
summary.push(
|
|
16557
|
+
`${prefix}Deleted global Windsurf config (${displayPath})`
|
|
16558
|
+
);
|
|
16559
|
+
} else if (windsurfResult.action === "removed-key" && windsurfResult.content !== void 0) {
|
|
16560
|
+
if (!dryRun) {
|
|
16561
|
+
fs5.writeFileSync(windsurfConfigPath, windsurfResult.content, "utf-8");
|
|
16562
|
+
}
|
|
16563
|
+
summary.push(
|
|
16564
|
+
`${prefix}Removed glasstrace from global Windsurf config (${displayPath})`
|
|
16565
|
+
);
|
|
16566
|
+
}
|
|
16567
|
+
}
|
|
16568
|
+
}
|
|
16569
|
+
} catch (err) {
|
|
16570
|
+
errors.push(
|
|
16571
|
+
`Failed to process MCP config: ${err instanceof Error ? err.message : String(err)}`
|
|
16572
|
+
);
|
|
16573
|
+
}
|
|
16574
|
+
try {
|
|
16575
|
+
for (const infoFile of AGENT_INFO_FILES) {
|
|
16576
|
+
const filePath = path5.join(projectRoot, infoFile);
|
|
16577
|
+
if (!fs5.existsSync(filePath)) {
|
|
16578
|
+
continue;
|
|
16579
|
+
}
|
|
16580
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
16581
|
+
const result = removeMarkerSection(content);
|
|
16582
|
+
if (result.removed) {
|
|
16583
|
+
if (result.content.trim().length === 0) {
|
|
16584
|
+
if (!dryRun) {
|
|
16585
|
+
fs5.unlinkSync(filePath);
|
|
16586
|
+
}
|
|
16587
|
+
summary.push(`${prefix}Deleted ${infoFile} (only contained Glasstrace section)`);
|
|
16588
|
+
} else {
|
|
16589
|
+
if (!dryRun) {
|
|
16590
|
+
fs5.writeFileSync(filePath, result.content, "utf-8");
|
|
16591
|
+
}
|
|
16592
|
+
summary.push(`${prefix}Removed Glasstrace section from ${infoFile}`);
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16595
|
+
}
|
|
16596
|
+
} catch (err) {
|
|
16597
|
+
errors.push(
|
|
16598
|
+
`Failed to process agent info files: ${err instanceof Error ? err.message : String(err)}`
|
|
16599
|
+
);
|
|
16600
|
+
}
|
|
16601
|
+
if (summary.length === 0 && errors.length === 0) {
|
|
16602
|
+
summary.push("No Glasstrace artifacts found \u2014 nothing to do.");
|
|
16603
|
+
}
|
|
16604
|
+
return { exitCode: errors.length > 0 ? 1 : 0, summary, warnings, errors };
|
|
16605
|
+
}
|
|
16606
|
+
var fs5, os, path5, MCP_CONFIG_FILES, AGENT_INFO_FILES;
|
|
16607
|
+
var init_uninit = __esm({
|
|
16608
|
+
"src/cli/uninit.ts"() {
|
|
16609
|
+
"use strict";
|
|
16610
|
+
fs5 = __toESM(require("node:fs"), 1);
|
|
16611
|
+
os = __toESM(require("node:os"), 1);
|
|
16612
|
+
path5 = __toESM(require("node:path"), 1);
|
|
16613
|
+
init_constants();
|
|
16614
|
+
init_mcp_runtime();
|
|
16615
|
+
init_discovery_file();
|
|
16616
|
+
MCP_CONFIG_FILES = [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json"];
|
|
16617
|
+
AGENT_INFO_FILES = [
|
|
16618
|
+
"CLAUDE.md",
|
|
16619
|
+
"codex.md",
|
|
16620
|
+
".cursorrules"
|
|
16621
|
+
];
|
|
16622
|
+
}
|
|
16623
|
+
});
|
|
16624
|
+
|
|
16625
|
+
// src/cli/mcp-add.ts
|
|
16626
|
+
var mcp_add_exports = {};
|
|
16627
|
+
__export(mcp_add_exports, {
|
|
16628
|
+
mcpAdd: () => mcpAdd,
|
|
16629
|
+
registerViaCli: () => registerViaCli
|
|
16630
|
+
});
|
|
16631
|
+
async function registerViaCli(agent, bearer) {
|
|
16632
|
+
if (!agent.cliAvailable) {
|
|
16633
|
+
return false;
|
|
16634
|
+
}
|
|
16635
|
+
if (agent.name !== "codex" && !isAnonApiKey(bearer)) {
|
|
16636
|
+
return false;
|
|
16637
|
+
}
|
|
16638
|
+
try {
|
|
16639
|
+
switch (agent.name) {
|
|
16640
|
+
case "claude": {
|
|
16641
|
+
const payload = JSON.stringify({
|
|
16642
|
+
type: "http",
|
|
16643
|
+
url: MCP_ENDPOINT,
|
|
16644
|
+
headers: { Authorization: `Bearer ${bearer}` }
|
|
16645
|
+
});
|
|
16646
|
+
await execFileAsync("claude", [
|
|
16647
|
+
"mcp",
|
|
16648
|
+
"add-json",
|
|
16649
|
+
"glasstrace",
|
|
16650
|
+
payload,
|
|
16651
|
+
"--scope",
|
|
16652
|
+
"project"
|
|
16653
|
+
]);
|
|
16654
|
+
return true;
|
|
16655
|
+
}
|
|
16656
|
+
case "codex": {
|
|
16657
|
+
await execFileAsync("codex", [
|
|
16658
|
+
"mcp",
|
|
16659
|
+
"add",
|
|
16660
|
+
"glasstrace",
|
|
16661
|
+
"--url",
|
|
16662
|
+
MCP_ENDPOINT
|
|
16663
|
+
]);
|
|
16664
|
+
const configPath = agent.mcpConfigPath;
|
|
16665
|
+
if (configPath !== null && fs6.existsSync(configPath)) {
|
|
16666
|
+
const content = fs6.readFileSync(configPath, "utf-8");
|
|
16667
|
+
if (!content.includes("bearer_token_env_var")) {
|
|
16668
|
+
const appendContent = content.endsWith("\n") ? "" : "\n";
|
|
16669
|
+
fs6.writeFileSync(
|
|
16670
|
+
configPath,
|
|
16671
|
+
content + appendContent + 'bearer_token_env_var = "GLASSTRACE_API_KEY"\n',
|
|
16672
|
+
"utf-8"
|
|
16673
|
+
);
|
|
16674
|
+
}
|
|
16675
|
+
}
|
|
16676
|
+
process.stderr.write(
|
|
16677
|
+
" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\n"
|
|
16678
|
+
);
|
|
16679
|
+
return true;
|
|
16680
|
+
}
|
|
16681
|
+
case "gemini": {
|
|
16682
|
+
await execFileAsync("gemini", [
|
|
16683
|
+
"mcp",
|
|
16684
|
+
"add",
|
|
16685
|
+
"--transport",
|
|
16686
|
+
"http",
|
|
16687
|
+
"--header",
|
|
16688
|
+
`Authorization: Bearer ${bearer}`,
|
|
16689
|
+
"glasstrace",
|
|
16690
|
+
MCP_ENDPOINT
|
|
16691
|
+
]);
|
|
16692
|
+
return true;
|
|
16693
|
+
}
|
|
16694
|
+
default:
|
|
16695
|
+
return false;
|
|
16696
|
+
}
|
|
16697
|
+
} catch {
|
|
16698
|
+
return false;
|
|
16699
|
+
}
|
|
16700
|
+
}
|
|
16701
|
+
async function markerMatchesEffective(projectRoot, effective) {
|
|
16702
|
+
const state = await readMcpMarker(projectRoot);
|
|
16703
|
+
if (state.status !== "valid") return false;
|
|
16704
|
+
return state.credentialHash === identityFingerprint(effective.key);
|
|
16705
|
+
}
|
|
16706
|
+
async function mcpAdd(options) {
|
|
16707
|
+
const force = options?.force ?? false;
|
|
16708
|
+
const dryRun = options?.dryRun ?? false;
|
|
16709
|
+
const projectRoot = process.cwd();
|
|
16710
|
+
const messages = [];
|
|
16711
|
+
const resolved = await resolveEffectiveMcpCredential(projectRoot);
|
|
16712
|
+
if (resolved.effective === null) {
|
|
16713
|
+
return {
|
|
16714
|
+
exitCode: 1,
|
|
16715
|
+
results: [],
|
|
16716
|
+
messages: ["Error: Run `glasstrace init` first to generate an API key."]
|
|
16717
|
+
};
|
|
16718
|
+
}
|
|
16719
|
+
if (resolved.warnings.includes("claimed-key-only")) {
|
|
16720
|
+
messages.push(
|
|
16721
|
+
"Note: dev key was loaded from .glasstrace/claimed-key. Copy it into .env.local so your app and Codex pick it up automatically."
|
|
16722
|
+
);
|
|
16723
|
+
}
|
|
16724
|
+
const markerPath = path6.join(projectRoot, ".glasstrace", "mcp-connected");
|
|
16725
|
+
if (fs6.existsSync(markerPath) && !force) {
|
|
16726
|
+
if (await markerMatchesEffective(projectRoot, resolved.effective)) {
|
|
16727
|
+
return {
|
|
16728
|
+
exitCode: 0,
|
|
16729
|
+
results: [],
|
|
16730
|
+
messages: ["MCP already configured. Use --force to reconfigure."]
|
|
16731
|
+
};
|
|
16732
|
+
}
|
|
16733
|
+
messages.push(
|
|
16734
|
+
"Detected a credential change since MCP was last configured. Refreshing MCP config so queries use the current account credential."
|
|
16735
|
+
);
|
|
16736
|
+
}
|
|
16737
|
+
const agents = await detectAgents(projectRoot);
|
|
16738
|
+
const detectedNonGeneric = agents.filter((a) => a.name !== "generic");
|
|
16739
|
+
const genericAgent = agents.find((a) => a.name === "generic");
|
|
16740
|
+
const targetAgents = genericAgent ? [...detectedNonGeneric, genericAgent] : detectedNonGeneric;
|
|
16741
|
+
if (dryRun) {
|
|
16742
|
+
messages.push("Dry run: would perform the following actions:", "");
|
|
16743
|
+
for (const agent of targetAgents) {
|
|
16744
|
+
const name = formatAgentName(agent.name);
|
|
16745
|
+
if (agent.cliAvailable && resolved.effective.source === "anon") {
|
|
16746
|
+
messages.push(
|
|
16747
|
+
` ${name}: Register via CLI (${agent.name} mcp add)`
|
|
16748
|
+
);
|
|
16749
|
+
} else if (agent.mcpConfigPath !== null) {
|
|
16750
|
+
messages.push(
|
|
16751
|
+
` ${name}: Write config to ${agent.mcpConfigPath}`
|
|
16752
|
+
);
|
|
16753
|
+
}
|
|
16754
|
+
if (agent.infoFilePath !== null) {
|
|
16755
|
+
messages.push(
|
|
16756
|
+
` ${name}: Inject info section into ${agent.infoFilePath}`
|
|
16757
|
+
);
|
|
16758
|
+
}
|
|
16759
|
+
}
|
|
16760
|
+
messages.push(
|
|
16761
|
+
"",
|
|
16762
|
+
" Update .gitignore with MCP config paths",
|
|
16763
|
+
" Create .glasstrace/mcp-connected marker"
|
|
16749
16764
|
);
|
|
16765
|
+
return { exitCode: 0, results: [], messages };
|
|
16750
16766
|
}
|
|
16751
|
-
|
|
16752
|
-
|
|
16753
|
-
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
fs5.unlinkSync(configPath);
|
|
16762
|
-
}
|
|
16763
|
-
summary.push(`${prefix}Deleted ${configFile}`);
|
|
16764
|
-
} else if (result.action === "removed-key" && result.content !== void 0) {
|
|
16765
|
-
if (!dryRun) {
|
|
16766
|
-
fs5.writeFileSync(configPath, result.content, "utf-8");
|
|
16767
|
-
}
|
|
16768
|
-
summary.push(`${prefix}Removed glasstrace from ${configFile}`);
|
|
16769
|
-
}
|
|
16770
|
-
}
|
|
16771
|
-
const codexConfigPath = path5.join(projectRoot, ".codex", "config.toml");
|
|
16772
|
-
if (fs5.existsSync(codexConfigPath)) {
|
|
16773
|
-
const content = fs5.readFileSync(codexConfigPath, "utf-8");
|
|
16774
|
-
const tomlResult = processTomlMcpConfig(content);
|
|
16775
|
-
if (tomlResult.action === "deleted") {
|
|
16776
|
-
if (!dryRun) {
|
|
16777
|
-
fs5.unlinkSync(codexConfigPath);
|
|
16778
|
-
}
|
|
16779
|
-
summary.push(`${prefix}Deleted .codex/config.toml`);
|
|
16780
|
-
} else if (tomlResult.action === "removed-section" && tomlResult.content !== void 0) {
|
|
16781
|
-
if (!dryRun) {
|
|
16782
|
-
fs5.writeFileSync(codexConfigPath, tomlResult.content, "utf-8");
|
|
16767
|
+
const results = [];
|
|
16768
|
+
const bearer = resolved.effective.key;
|
|
16769
|
+
for (const agent of targetAgents) {
|
|
16770
|
+
const name = formatAgentName(agent.name);
|
|
16771
|
+
if (agent.name !== "generic") {
|
|
16772
|
+
const cliSuccess = await registerViaCli(agent, bearer);
|
|
16773
|
+
if (cliSuccess) {
|
|
16774
|
+
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
16775
|
+
if (infoContent !== "") {
|
|
16776
|
+
await injectInfoSection(agent, infoContent, projectRoot);
|
|
16783
16777
|
}
|
|
16784
|
-
|
|
16778
|
+
results.push({
|
|
16779
|
+
agent: agent.name,
|
|
16780
|
+
success: true,
|
|
16781
|
+
method: "cli",
|
|
16782
|
+
message: `${name}: Registered via CLI`
|
|
16783
|
+
});
|
|
16784
|
+
continue;
|
|
16785
16785
|
}
|
|
16786
16786
|
}
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
if (fs5.existsSync(windsurfConfigPath)) {
|
|
16796
|
-
const content = fs5.readFileSync(windsurfConfigPath, "utf-8");
|
|
16797
|
-
const windsurfResult = processJsonMcpConfig(content);
|
|
16798
|
-
const home = os.homedir();
|
|
16799
|
-
const displayPath = windsurfConfigPath.startsWith(home) ? "~" + windsurfConfigPath.slice(home.length) : windsurfConfigPath;
|
|
16800
|
-
if (windsurfResult.action === "deleted") {
|
|
16801
|
-
if (!dryRun) {
|
|
16802
|
-
fs5.unlinkSync(windsurfConfigPath);
|
|
16803
|
-
}
|
|
16804
|
-
summary.push(
|
|
16805
|
-
`${prefix}Deleted global Windsurf config (${displayPath})`
|
|
16806
|
-
);
|
|
16807
|
-
} else if (windsurfResult.action === "removed-key" && windsurfResult.content !== void 0) {
|
|
16808
|
-
if (!dryRun) {
|
|
16809
|
-
fs5.writeFileSync(windsurfConfigPath, windsurfResult.content, "utf-8");
|
|
16787
|
+
if (agent.mcpConfigPath !== null) {
|
|
16788
|
+
try {
|
|
16789
|
+
const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);
|
|
16790
|
+
await writeMcpConfig(agent, configContent, projectRoot);
|
|
16791
|
+
if (fs6.existsSync(agent.mcpConfigPath)) {
|
|
16792
|
+
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
16793
|
+
if (infoContent !== "") {
|
|
16794
|
+
await injectInfoSection(agent, infoContent, projectRoot);
|
|
16810
16795
|
}
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16796
|
+
results.push({
|
|
16797
|
+
agent: agent.name,
|
|
16798
|
+
success: true,
|
|
16799
|
+
method: "file",
|
|
16800
|
+
message: `${name}: Configured via ${agent.mcpConfigPath}`
|
|
16801
|
+
});
|
|
16802
|
+
continue;
|
|
16814
16803
|
}
|
|
16804
|
+
results.push({
|
|
16805
|
+
agent: agent.name,
|
|
16806
|
+
success: false,
|
|
16807
|
+
method: "file",
|
|
16808
|
+
message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`
|
|
16809
|
+
});
|
|
16810
|
+
continue;
|
|
16811
|
+
} catch (err) {
|
|
16812
|
+
results.push({
|
|
16813
|
+
agent: agent.name,
|
|
16814
|
+
success: false,
|
|
16815
|
+
method: "file",
|
|
16816
|
+
message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`
|
|
16817
|
+
});
|
|
16818
|
+
continue;
|
|
16815
16819
|
}
|
|
16816
16820
|
}
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16821
|
+
results.push({
|
|
16822
|
+
agent: agent.name,
|
|
16823
|
+
success: false,
|
|
16824
|
+
method: "skipped",
|
|
16825
|
+
message: `${name}: No registration method available`
|
|
16826
|
+
});
|
|
16827
|
+
}
|
|
16828
|
+
await updateGitignore(
|
|
16829
|
+
[".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".codex/config.toml"],
|
|
16830
|
+
projectRoot
|
|
16831
|
+
);
|
|
16832
|
+
const anySuccess = results.some((r) => r.success);
|
|
16833
|
+
if (anySuccess) {
|
|
16834
|
+
await writeMcpMarker(projectRoot, {
|
|
16835
|
+
credentialSource: resolved.effective.source,
|
|
16836
|
+
credentialHash: identityFingerprint(resolved.effective.key)
|
|
16837
|
+
});
|
|
16838
|
+
}
|
|
16839
|
+
messages.push("", "MCP registration summary:");
|
|
16840
|
+
for (const result of results) {
|
|
16841
|
+
const icon = result.success ? "+" : "-";
|
|
16842
|
+
messages.push(` [${icon}] ${result.message}`);
|
|
16843
|
+
}
|
|
16844
|
+
if (results.length === 0) {
|
|
16845
|
+
messages.push(
|
|
16846
|
+
" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project."
|
|
16820
16847
|
);
|
|
16821
16848
|
}
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
|
|
16849
|
+
const detectedNonGenericResults = results.filter(
|
|
16850
|
+
(r) => detectedNonGeneric.some((a) => a.name === r.agent)
|
|
16851
|
+
);
|
|
16852
|
+
const allDetectedNonGenericFailed = detectedNonGeneric.length > 0 && !detectedNonGenericResults.some((r) => r.success);
|
|
16853
|
+
if (allDetectedNonGenericFailed) {
|
|
16854
|
+
messages.push(
|
|
16855
|
+
"",
|
|
16856
|
+
"All detected agent registrations failed. Check errors above."
|
|
16857
|
+
);
|
|
16858
|
+
return { exitCode: 1, results, messages };
|
|
16859
|
+
}
|
|
16860
|
+
if (!anySuccess && results.length > 0) {
|
|
16861
|
+
messages.push(
|
|
16862
|
+
"",
|
|
16863
|
+
"All agent registrations failed. Check errors above."
|
|
16864
|
+
);
|
|
16865
|
+
return { exitCode: 1, results, messages };
|
|
16866
|
+
}
|
|
16867
|
+
if (anySuccess) {
|
|
16868
|
+
messages.push("", "MCP registration complete.");
|
|
16869
|
+
}
|
|
16870
|
+
return { exitCode: 0, results, messages };
|
|
16871
|
+
}
|
|
16872
|
+
var import_node_child_process2, fs6, path6, import_node_util, execFileAsync;
|
|
16873
|
+
var init_mcp_add = __esm({
|
|
16874
|
+
"src/cli/mcp-add.ts"() {
|
|
16875
|
+
"use strict";
|
|
16876
|
+
import_node_child_process2 = require("node:child_process");
|
|
16877
|
+
fs6 = __toESM(require("node:fs"), 1);
|
|
16878
|
+
path6 = __toESM(require("node:path"), 1);
|
|
16879
|
+
import_node_util = require("node:util");
|
|
16880
|
+
init_mcp_runtime();
|
|
16881
|
+
init_detect();
|
|
16882
|
+
init_configs();
|
|
16883
|
+
init_inject();
|
|
16884
|
+
init_constants();
|
|
16885
|
+
execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
16886
|
+
}
|
|
16887
|
+
});
|
|
16888
|
+
|
|
16889
|
+
// src/cli/validate.ts
|
|
16890
|
+
var validate_exports = {};
|
|
16891
|
+
__export(validate_exports, {
|
|
16892
|
+
hasGlasstraceImport: () => hasGlasstraceImport,
|
|
16893
|
+
hasRegisterGlasstraceImport: () => hasRegisterGlasstraceImport,
|
|
16894
|
+
runValidate: () => runValidate
|
|
16895
|
+
});
|
|
16896
|
+
function hasGlasstraceImport(content) {
|
|
16897
|
+
return /@glasstrace\/sdk/.test(content);
|
|
16898
|
+
}
|
|
16899
|
+
function hasRegisterGlasstraceImport(content) {
|
|
16900
|
+
const match = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
|
|
16901
|
+
const importMatch = match.exec(content);
|
|
16902
|
+
if (!importMatch) return false;
|
|
16903
|
+
return importMatch[1].split(",").map((s) => s.trim()).includes("registerGlasstrace");
|
|
16904
|
+
}
|
|
16905
|
+
async function runValidate(options) {
|
|
16906
|
+
const { projectRoot } = options;
|
|
16907
|
+
const issues = [];
|
|
16908
|
+
const glasstraceDir = path7.join(projectRoot, ".glasstrace");
|
|
16909
|
+
const instrumentationPath = path7.join(projectRoot, "instrumentation.ts");
|
|
16910
|
+
const markerPath = path7.join(glasstraceDir, "mcp-connected");
|
|
16911
|
+
const glasstraceDirExists = isDirectorySafe(glasstraceDir);
|
|
16912
|
+
const instrumentationExists = fs7.existsSync(instrumentationPath);
|
|
16913
|
+
const instrumentationContent = instrumentationExists ? safeReadFile(instrumentationPath) : null;
|
|
16914
|
+
const markerExists = fs7.existsSync(markerPath);
|
|
16915
|
+
const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter(
|
|
16916
|
+
(rel) => fs7.existsSync(path7.join(projectRoot, rel))
|
|
16917
|
+
);
|
|
16918
|
+
if (glasstraceDirExists) {
|
|
16919
|
+
if (instrumentationContent === null || !hasRegisterGlasstraceImport(instrumentationContent)) {
|
|
16920
|
+
issues.push({
|
|
16921
|
+
code: "glasstrace-dir-without-register-import",
|
|
16922
|
+
message: ".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.",
|
|
16923
|
+
fix: "Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use."
|
|
16924
|
+
});
|
|
16925
|
+
}
|
|
16926
|
+
}
|
|
16927
|
+
if (!glasstraceDirExists && instrumentationContent !== null) {
|
|
16928
|
+
if (hasGlasstraceImport(instrumentationContent)) {
|
|
16929
|
+
issues.push({
|
|
16930
|
+
code: "sdk-import-without-glasstrace-dir",
|
|
16931
|
+
message: "instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.",
|
|
16932
|
+
fix: "Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK."
|
|
16933
|
+
});
|
|
16934
|
+
}
|
|
16935
|
+
}
|
|
16936
|
+
if (markerExists && mcpConfigsPresent.length === 0) {
|
|
16937
|
+
issues.push({
|
|
16938
|
+
code: "mcp-marker-without-configs",
|
|
16939
|
+
message: ".glasstrace/mcp-connected marker is present but no MCP config files were found.",
|
|
16940
|
+
fix: "Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected."
|
|
16941
|
+
});
|
|
16942
|
+
}
|
|
16943
|
+
if (!markerExists && mcpConfigsPresent.length > 0) {
|
|
16944
|
+
issues.push({
|
|
16945
|
+
code: "mcp-configs-without-marker",
|
|
16946
|
+
message: `MCP config files exist (${mcpConfigsPresent.join(", ")}) but .glasstrace/mcp-connected marker is missing.`,
|
|
16947
|
+
fix: "Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration."
|
|
16948
|
+
});
|
|
16949
|
+
}
|
|
16950
|
+
if (markerExists) {
|
|
16951
|
+
try {
|
|
16952
|
+
const [markerState, resolved] = await Promise.all([
|
|
16953
|
+
readMcpMarker(projectRoot),
|
|
16954
|
+
resolveEffectiveMcpCredential(projectRoot)
|
|
16955
|
+
]);
|
|
16956
|
+
if (markerState.status === "valid" && resolved.effective !== null && markerState.credentialHash !== identityFingerprint(resolved.effective.key)) {
|
|
16957
|
+
issues.push({
|
|
16958
|
+
code: "mcp-helper-stale-credential",
|
|
16959
|
+
message: "Managed MCP configs were last refreshed with a different credential than the project is now using. MCP queries may return no traces while the dashboard sees them.",
|
|
16960
|
+
fix: "Run `npx glasstrace mcp add --force` to refresh managed MCP configs with the current credential."
|
|
16961
|
+
});
|
|
16842
16962
|
}
|
|
16963
|
+
} catch {
|
|
16843
16964
|
}
|
|
16844
|
-
}
|
|
16845
|
-
|
|
16846
|
-
|
|
16965
|
+
}
|
|
16966
|
+
const summary = [];
|
|
16967
|
+
if (issues.length === 0) {
|
|
16968
|
+
summary.push("Glasstrace install state is consistent.");
|
|
16969
|
+
} else {
|
|
16970
|
+
summary.push(
|
|
16971
|
+
`Detected ${issues.length} inconsistenc${issues.length === 1 ? "y" : "ies"} in Glasstrace install state:`
|
|
16847
16972
|
);
|
|
16848
16973
|
}
|
|
16849
|
-
|
|
16850
|
-
|
|
16974
|
+
return {
|
|
16975
|
+
exitCode: issues.length > 0 ? 1 : 0,
|
|
16976
|
+
summary,
|
|
16977
|
+
issues
|
|
16978
|
+
};
|
|
16979
|
+
}
|
|
16980
|
+
function safeReadFile(filePath) {
|
|
16981
|
+
try {
|
|
16982
|
+
return fs7.readFileSync(filePath, "utf-8");
|
|
16983
|
+
} catch {
|
|
16984
|
+
return null;
|
|
16851
16985
|
}
|
|
16852
|
-
return { exitCode: errors.length > 0 ? 1 : 0, summary, warnings, errors };
|
|
16853
16986
|
}
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16987
|
+
function isDirectorySafe(dirPath) {
|
|
16988
|
+
try {
|
|
16989
|
+
if (!fs7.existsSync(dirPath)) return false;
|
|
16990
|
+
return fs7.statSync(dirPath).isDirectory();
|
|
16991
|
+
} catch {
|
|
16992
|
+
return false;
|
|
16993
|
+
}
|
|
16994
|
+
}
|
|
16995
|
+
var fs7, path7, MCP_CONFIG_CANDIDATES;
|
|
16996
|
+
var init_validate = __esm({
|
|
16997
|
+
"src/cli/validate.ts"() {
|
|
16857
16998
|
"use strict";
|
|
16858
|
-
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
"
|
|
16867
|
-
"codex.md",
|
|
16868
|
-
".cursorrules"
|
|
16999
|
+
fs7 = __toESM(require("node:fs"), 1);
|
|
17000
|
+
path7 = __toESM(require("node:path"), 1);
|
|
17001
|
+
init_mcp_runtime();
|
|
17002
|
+
MCP_CONFIG_CANDIDATES = [
|
|
17003
|
+
".mcp.json",
|
|
17004
|
+
".cursor/mcp.json",
|
|
17005
|
+
".gemini/settings.json",
|
|
17006
|
+
".codex/config.toml",
|
|
17007
|
+
".glasstrace/mcp.json"
|
|
16869
17008
|
];
|
|
16870
17009
|
}
|
|
16871
17010
|
});
|
|
16872
17011
|
|
|
16873
|
-
// src/cli/
|
|
16874
|
-
var
|
|
16875
|
-
__export(
|
|
16876
|
-
|
|
17012
|
+
// src/cli/status.ts
|
|
17013
|
+
var status_exports = {};
|
|
17014
|
+
__export(status_exports, {
|
|
17015
|
+
runStatus: () => runStatus
|
|
16877
17016
|
});
|
|
16878
|
-
|
|
16879
|
-
|
|
17017
|
+
function runStatus(options) {
|
|
17018
|
+
const root = options.projectRoot;
|
|
17019
|
+
return {
|
|
17020
|
+
installed: checkInstalled(root),
|
|
17021
|
+
initialized: checkInitialized(root),
|
|
17022
|
+
instrumentation: checkInstrumentation(root),
|
|
17023
|
+
configWrapped: checkConfigWrapped(root),
|
|
17024
|
+
anonKey: checkAnonKey(root),
|
|
17025
|
+
mcpConfigured: checkMcpConfigured(root),
|
|
17026
|
+
agents: checkAgents(root),
|
|
17027
|
+
runtime: readRuntimeState(root)
|
|
17028
|
+
};
|
|
17029
|
+
}
|
|
17030
|
+
function checkInstalled(root) {
|
|
17031
|
+
try {
|
|
17032
|
+
const pkgPath = path8.join(root, "package.json");
|
|
17033
|
+
const content = fs8.readFileSync(pkgPath, "utf-8");
|
|
17034
|
+
const pkg = JSON.parse(content);
|
|
17035
|
+
const deps = pkg["dependencies"];
|
|
17036
|
+
const devDeps = pkg["devDependencies"];
|
|
17037
|
+
return deps != null && "@glasstrace/sdk" in deps || devDeps != null && "@glasstrace/sdk" in devDeps;
|
|
17038
|
+
} catch {
|
|
16880
17039
|
return false;
|
|
16881
17040
|
}
|
|
17041
|
+
}
|
|
17042
|
+
function checkInitialized(root) {
|
|
16882
17043
|
try {
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
"glasstrace",
|
|
16894
|
-
payload,
|
|
16895
|
-
"--scope",
|
|
16896
|
-
"project"
|
|
16897
|
-
]);
|
|
16898
|
-
return true;
|
|
16899
|
-
}
|
|
16900
|
-
case "codex": {
|
|
16901
|
-
await execFileAsync("codex", [
|
|
16902
|
-
"mcp",
|
|
16903
|
-
"add",
|
|
16904
|
-
"glasstrace",
|
|
16905
|
-
"--url",
|
|
16906
|
-
MCP_ENDPOINT
|
|
16907
|
-
]);
|
|
16908
|
-
const configPath = agent.mcpConfigPath;
|
|
16909
|
-
if (configPath !== null && fs6.existsSync(configPath)) {
|
|
16910
|
-
const content = fs6.readFileSync(configPath, "utf-8");
|
|
16911
|
-
if (!content.includes("bearer_token_env_var")) {
|
|
16912
|
-
const appendContent = content.endsWith("\n") ? "" : "\n";
|
|
16913
|
-
fs6.writeFileSync(
|
|
16914
|
-
configPath,
|
|
16915
|
-
content + appendContent + 'bearer_token_env_var = "GLASSTRACE_API_KEY"\n',
|
|
16916
|
-
"utf-8"
|
|
16917
|
-
);
|
|
16918
|
-
}
|
|
16919
|
-
}
|
|
16920
|
-
process.stderr.write(
|
|
16921
|
-
" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\n"
|
|
16922
|
-
);
|
|
17044
|
+
return fs8.statSync(path8.join(root, ".glasstrace")).isDirectory();
|
|
17045
|
+
} catch {
|
|
17046
|
+
return false;
|
|
17047
|
+
}
|
|
17048
|
+
}
|
|
17049
|
+
function checkInstrumentation(root) {
|
|
17050
|
+
for (const name of INSTRUMENTATION_FILES) {
|
|
17051
|
+
try {
|
|
17052
|
+
const content = fs8.readFileSync(path8.join(root, name), "utf-8");
|
|
17053
|
+
if (content.includes("registerGlasstrace")) {
|
|
16923
17054
|
return true;
|
|
16924
17055
|
}
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
]);
|
|
17056
|
+
} catch {
|
|
17057
|
+
}
|
|
17058
|
+
}
|
|
17059
|
+
return false;
|
|
17060
|
+
}
|
|
17061
|
+
function checkConfigWrapped(root) {
|
|
17062
|
+
for (const name of NEXT_CONFIG_NAMES) {
|
|
17063
|
+
try {
|
|
17064
|
+
const content = fs8.readFileSync(path8.join(root, name), "utf-8");
|
|
17065
|
+
if (content.includes("withGlasstraceConfig")) {
|
|
16936
17066
|
return true;
|
|
16937
17067
|
}
|
|
16938
|
-
|
|
16939
|
-
return false;
|
|
17068
|
+
} catch {
|
|
16940
17069
|
}
|
|
17070
|
+
}
|
|
17071
|
+
return false;
|
|
17072
|
+
}
|
|
17073
|
+
function checkAnonKey(root) {
|
|
17074
|
+
try {
|
|
17075
|
+
return fs8.statSync(path8.join(root, ".glasstrace", "anon_key")).isFile();
|
|
16941
17076
|
} catch {
|
|
16942
17077
|
return false;
|
|
16943
17078
|
}
|
|
16944
17079
|
}
|
|
16945
|
-
|
|
16946
|
-
const
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
|
|
16953
|
-
exitCode: 1,
|
|
16954
|
-
results: [],
|
|
16955
|
-
messages: ["Error: Run `glasstrace init` first to generate an API key."]
|
|
16956
|
-
};
|
|
16957
|
-
}
|
|
16958
|
-
const markerPath = path6.join(projectRoot, ".glasstrace", "mcp-connected");
|
|
16959
|
-
if (fs6.existsSync(markerPath) && !force) {
|
|
16960
|
-
return {
|
|
16961
|
-
exitCode: 0,
|
|
16962
|
-
results: [],
|
|
16963
|
-
messages: ["MCP already configured. Use --force to reconfigure."]
|
|
16964
|
-
};
|
|
16965
|
-
}
|
|
16966
|
-
const agents = await detectAgents(projectRoot);
|
|
16967
|
-
const detectedNonGeneric = agents.filter((a) => a.name !== "generic");
|
|
16968
|
-
const targetAgents = detectedNonGeneric.length > 0 ? detectedNonGeneric : agents.filter((a) => a.name === "generic");
|
|
16969
|
-
if (dryRun) {
|
|
16970
|
-
messages.push("Dry run: would perform the following actions:", "");
|
|
16971
|
-
for (const agent of targetAgents) {
|
|
16972
|
-
const name = formatAgentName(agent.name);
|
|
16973
|
-
if (agent.cliAvailable) {
|
|
16974
|
-
messages.push(
|
|
16975
|
-
` ${name}: Register via CLI (${agent.name} mcp add)`
|
|
16976
|
-
);
|
|
16977
|
-
} else if (agent.mcpConfigPath !== null) {
|
|
16978
|
-
messages.push(
|
|
16979
|
-
` ${name}: Write config to ${agent.mcpConfigPath}`
|
|
16980
|
-
);
|
|
17080
|
+
function checkMcpConfigured(root) {
|
|
17081
|
+
for (const name of MCP_JSON_FILES) {
|
|
17082
|
+
try {
|
|
17083
|
+
const content = fs8.readFileSync(path8.join(root, name), "utf-8");
|
|
17084
|
+
const parsed = JSON.parse(content);
|
|
17085
|
+
const mcpServers = parsed["mcpServers"];
|
|
17086
|
+
if (mcpServers && typeof mcpServers === "object" && "glasstrace" in mcpServers) {
|
|
17087
|
+
return true;
|
|
16981
17088
|
}
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
17089
|
+
} catch {
|
|
17090
|
+
}
|
|
17091
|
+
}
|
|
17092
|
+
for (const name of MCP_TOML_FILES) {
|
|
17093
|
+
try {
|
|
17094
|
+
const content = fs8.readFileSync(path8.join(root, name), "utf-8");
|
|
17095
|
+
if (content.includes("[mcp_servers.glasstrace]")) {
|
|
17096
|
+
return true;
|
|
16986
17097
|
}
|
|
17098
|
+
} catch {
|
|
16987
17099
|
}
|
|
16988
|
-
messages.push(
|
|
16989
|
-
"",
|
|
16990
|
-
" Update .gitignore with MCP config paths",
|
|
16991
|
-
" Create .glasstrace/mcp-connected marker"
|
|
16992
|
-
);
|
|
16993
|
-
return { exitCode: 0, results: [], messages };
|
|
16994
17100
|
}
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
agent: agent.name,
|
|
17007
|
-
success: true,
|
|
17008
|
-
method: "cli",
|
|
17009
|
-
message: `${name}: Registered via CLI`
|
|
17010
|
-
});
|
|
17011
|
-
continue;
|
|
17101
|
+
return false;
|
|
17102
|
+
}
|
|
17103
|
+
function checkAgents(root) {
|
|
17104
|
+
const found = [];
|
|
17105
|
+
for (const name of AGENT_INFO_FILES2) {
|
|
17106
|
+
try {
|
|
17107
|
+
const content = fs8.readFileSync(path8.join(root, name), "utf-8");
|
|
17108
|
+
const hasHtmlMarkers = content.includes("<!-- glasstrace:mcp:start -->") && content.includes("<!-- glasstrace:mcp:end -->");
|
|
17109
|
+
const hasHashMarkers = content.includes("# glasstrace:mcp:start") && content.includes("# glasstrace:mcp:end");
|
|
17110
|
+
if (hasHtmlMarkers || hasHashMarkers) {
|
|
17111
|
+
found.push(name);
|
|
17012
17112
|
}
|
|
17113
|
+
} catch {
|
|
17013
17114
|
}
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17115
|
+
}
|
|
17116
|
+
return found;
|
|
17117
|
+
}
|
|
17118
|
+
function readRuntimeState(root) {
|
|
17119
|
+
const empty = {
|
|
17120
|
+
available: false,
|
|
17121
|
+
stale: false,
|
|
17122
|
+
coreState: null,
|
|
17123
|
+
authState: null,
|
|
17124
|
+
otelState: null,
|
|
17125
|
+
otelScenario: null,
|
|
17126
|
+
updatedAt: null,
|
|
17127
|
+
pid: null
|
|
17128
|
+
};
|
|
17129
|
+
try {
|
|
17130
|
+
const filePath = path8.join(root, ".glasstrace", "runtime-state.json");
|
|
17131
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
17132
|
+
const parsed = JSON.parse(content);
|
|
17133
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : null;
|
|
17134
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
17135
|
+
const core = parsed.core;
|
|
17136
|
+
const auth = parsed.auth;
|
|
17137
|
+
const otel = parsed.otel;
|
|
17138
|
+
const coreState = typeof core?.state === "string" ? core.state : null;
|
|
17139
|
+
const authState = typeof auth?.state === "string" ? auth.state : null;
|
|
17140
|
+
const otelState = typeof otel?.state === "string" ? otel.state : null;
|
|
17141
|
+
const otelScenario = typeof otel?.scenario === "string" ? otel.scenario : null;
|
|
17142
|
+
let stale = false;
|
|
17143
|
+
if (coreState === "SHUTDOWN") {
|
|
17144
|
+
stale = false;
|
|
17145
|
+
} else if (updatedAt) {
|
|
17146
|
+
const updatedMs = new Date(updatedAt).getTime();
|
|
17147
|
+
const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;
|
|
17148
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
17149
|
+
if (pid && pid > 0) {
|
|
17150
|
+
try {
|
|
17151
|
+
process.kill(pid, 0);
|
|
17152
|
+
stale = false;
|
|
17153
|
+
} catch (err) {
|
|
17154
|
+
const code = err?.code;
|
|
17155
|
+
if (code === "EPERM") {
|
|
17156
|
+
stale = false;
|
|
17157
|
+
} else {
|
|
17158
|
+
stale = true;
|
|
17159
|
+
}
|
|
17022
17160
|
}
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
success: true,
|
|
17026
|
-
method: "file",
|
|
17027
|
-
message: `${name}: Configured via ${agent.mcpConfigPath}`
|
|
17028
|
-
});
|
|
17029
|
-
continue;
|
|
17161
|
+
} else {
|
|
17162
|
+
stale = true;
|
|
17030
17163
|
}
|
|
17031
|
-
results.push({
|
|
17032
|
-
agent: agent.name,
|
|
17033
|
-
success: false,
|
|
17034
|
-
method: "file",
|
|
17035
|
-
message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`
|
|
17036
|
-
});
|
|
17037
|
-
continue;
|
|
17038
|
-
} catch (err) {
|
|
17039
|
-
results.push({
|
|
17040
|
-
agent: agent.name,
|
|
17041
|
-
success: false,
|
|
17042
|
-
method: "file",
|
|
17043
|
-
message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`
|
|
17044
|
-
});
|
|
17045
|
-
continue;
|
|
17046
17164
|
}
|
|
17047
17165
|
}
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
if (anySuccess) {
|
|
17061
|
-
await scaffoldMcpMarker(projectRoot, anonKey);
|
|
17062
|
-
}
|
|
17063
|
-
messages.push("", "MCP registration summary:");
|
|
17064
|
-
for (const result of results) {
|
|
17065
|
-
const icon = result.success ? "+" : "-";
|
|
17066
|
-
messages.push(` [${icon}] ${result.message}`);
|
|
17067
|
-
}
|
|
17068
|
-
if (results.length === 0) {
|
|
17069
|
-
messages.push(
|
|
17070
|
-
" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project."
|
|
17071
|
-
);
|
|
17072
|
-
}
|
|
17073
|
-
if (!anySuccess && results.length > 0) {
|
|
17074
|
-
messages.push(
|
|
17075
|
-
"",
|
|
17076
|
-
"All agent registrations failed. Check errors above."
|
|
17077
|
-
);
|
|
17078
|
-
return { exitCode: 1, results, messages };
|
|
17079
|
-
}
|
|
17080
|
-
if (anySuccess) {
|
|
17081
|
-
messages.push("", "MCP registration complete.");
|
|
17166
|
+
return {
|
|
17167
|
+
available: true,
|
|
17168
|
+
stale,
|
|
17169
|
+
coreState,
|
|
17170
|
+
authState,
|
|
17171
|
+
otelState,
|
|
17172
|
+
otelScenario,
|
|
17173
|
+
updatedAt,
|
|
17174
|
+
pid
|
|
17175
|
+
};
|
|
17176
|
+
} catch {
|
|
17177
|
+
return empty;
|
|
17082
17178
|
}
|
|
17083
|
-
return { exitCode: 0, results, messages };
|
|
17084
17179
|
}
|
|
17085
|
-
var
|
|
17086
|
-
var
|
|
17087
|
-
"src/cli/
|
|
17180
|
+
var fs8, path8, MCP_JSON_FILES, MCP_TOML_FILES, AGENT_INFO_FILES2, INSTRUMENTATION_FILES, STALE_THRESHOLD_MS;
|
|
17181
|
+
var init_status = __esm({
|
|
17182
|
+
"src/cli/status.ts"() {
|
|
17088
17183
|
"use strict";
|
|
17089
|
-
|
|
17090
|
-
|
|
17091
|
-
path6 = __toESM(require("node:path"), 1);
|
|
17092
|
-
import_node_util = require("node:util");
|
|
17093
|
-
init_anon_key();
|
|
17094
|
-
init_detect();
|
|
17095
|
-
init_configs();
|
|
17096
|
-
init_inject();
|
|
17097
|
-
init_scaffolder();
|
|
17184
|
+
fs8 = __toESM(require("node:fs"), 1);
|
|
17185
|
+
path8 = __toESM(require("node:path"), 1);
|
|
17098
17186
|
init_constants();
|
|
17099
|
-
|
|
17187
|
+
MCP_JSON_FILES = [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".glasstrace/mcp.json"];
|
|
17188
|
+
MCP_TOML_FILES = [".codex/config.toml"];
|
|
17189
|
+
AGENT_INFO_FILES2 = [
|
|
17190
|
+
"CLAUDE.md",
|
|
17191
|
+
"codex.md",
|
|
17192
|
+
".cursorrules"
|
|
17193
|
+
];
|
|
17194
|
+
INSTRUMENTATION_FILES = [
|
|
17195
|
+
"instrumentation.ts",
|
|
17196
|
+
"instrumentation.js",
|
|
17197
|
+
"instrumentation.mjs",
|
|
17198
|
+
"src/instrumentation.ts",
|
|
17199
|
+
"src/instrumentation.js",
|
|
17200
|
+
"src/instrumentation.mjs"
|
|
17201
|
+
];
|
|
17202
|
+
STALE_THRESHOLD_MS = 3e4;
|
|
17100
17203
|
}
|
|
17101
17204
|
});
|
|
17102
17205
|
|
|
17103
|
-
// src/cli/
|
|
17104
|
-
var
|
|
17105
|
-
__export(
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17206
|
+
// src/cli/init.ts
|
|
17207
|
+
var init_exports = {};
|
|
17208
|
+
__export(init_exports, {
|
|
17209
|
+
decideMcpConfigAction: () => decideMcpConfigAction,
|
|
17210
|
+
gitignoreExcludesDiscoveryFile: () => gitignoreExcludesDiscoveryFile,
|
|
17211
|
+
meetsNodeVersion: () => meetsNodeVersion,
|
|
17212
|
+
rollbackSteps: () => rollbackSteps,
|
|
17213
|
+
runInit: () => runInit,
|
|
17214
|
+
verifyAnonKeyRegistration: () => verifyAnonKeyRegistration
|
|
17109
17215
|
});
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17216
|
+
module.exports = __toCommonJS(init_exports);
|
|
17217
|
+
var fs9 = __toESM(require("node:fs"), 1);
|
|
17218
|
+
var path9 = __toESM(require("node:path"), 1);
|
|
17219
|
+
var readline = __toESM(require("node:readline"), 1);
|
|
17220
|
+
|
|
17221
|
+
// src/cli/scaffolder.ts
|
|
17222
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
17223
|
+
var path = __toESM(require("node:path"), 1);
|
|
17224
|
+
init_constants();
|
|
17225
|
+
function hasRegisterGlasstraceCall(content) {
|
|
17226
|
+
return content.split("\n").some((line) => {
|
|
17227
|
+
const uncommented = line.replace(/\/\/.*$/, "");
|
|
17228
|
+
return /\bregisterGlasstrace\s*\(/.test(uncommented);
|
|
17229
|
+
});
|
|
17118
17230
|
}
|
|
17119
|
-
function
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
const
|
|
17124
|
-
const
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
const
|
|
17129
|
-
const
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17231
|
+
function injectRegisterGlasstrace(content) {
|
|
17232
|
+
if (hasRegisterGlasstraceCall(content)) {
|
|
17233
|
+
return { injected: false, content };
|
|
17234
|
+
}
|
|
17235
|
+
const registerFnRegex = /export\s+(?:async\s+)?function\s+register\s*\([^)]*\)\s*\{/;
|
|
17236
|
+
const match = registerFnRegex.exec(content);
|
|
17237
|
+
if (!match) {
|
|
17238
|
+
return { injected: false, content };
|
|
17239
|
+
}
|
|
17240
|
+
const afterBrace = content.slice(match.index + match[0].length);
|
|
17241
|
+
const indentMatch = /\n([ \t]+)/.exec(afterBrace);
|
|
17242
|
+
const indent = indentMatch ? indentMatch[1] : " ";
|
|
17243
|
+
const importLine = 'import { registerGlasstrace } from "@glasstrace/sdk";\n';
|
|
17244
|
+
const hasGlasstraceImport2 = content.includes("@glasstrace/sdk");
|
|
17245
|
+
const insertPoint = match.index + match[0].length;
|
|
17246
|
+
const callInjection = `
|
|
17247
|
+
${indent}// Glasstrace must be registered before other instrumentation
|
|
17248
|
+
${indent}registerGlasstrace();
|
|
17249
|
+
`;
|
|
17250
|
+
let modified;
|
|
17251
|
+
if (hasGlasstraceImport2) {
|
|
17252
|
+
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
|
|
17253
|
+
const importMatch = importRegex.exec(content);
|
|
17254
|
+
if (importMatch) {
|
|
17255
|
+
const specifiers = importMatch[1];
|
|
17256
|
+
const alreadyImported = specifiers.split(",").some((s) => s.trim() === "registerGlasstrace");
|
|
17257
|
+
if (alreadyImported) {
|
|
17258
|
+
modified = content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);
|
|
17259
|
+
} else {
|
|
17260
|
+
const existingImports = specifiers.trimEnd();
|
|
17261
|
+
const separator = existingImports.endsWith(",") ? " " : ", ";
|
|
17262
|
+
const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from "@glasstrace/sdk"`;
|
|
17263
|
+
modified = content.replace(importMatch[0], updatedImport);
|
|
17264
|
+
const newMatch = registerFnRegex.exec(modified);
|
|
17265
|
+
if (newMatch) {
|
|
17266
|
+
const newInsertPoint = newMatch.index + newMatch[0].length;
|
|
17267
|
+
modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);
|
|
17268
|
+
}
|
|
17269
|
+
}
|
|
17270
|
+
} else {
|
|
17271
|
+
modified = importLine + content;
|
|
17272
|
+
const newMatch = registerFnRegex.exec(modified);
|
|
17273
|
+
if (newMatch) {
|
|
17274
|
+
const newInsertPoint = newMatch.index + newMatch[0].length;
|
|
17275
|
+
modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);
|
|
17276
|
+
}
|
|
17139
17277
|
}
|
|
17278
|
+
} else {
|
|
17279
|
+
modified = importLine + content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);
|
|
17140
17280
|
}
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17281
|
+
return { injected: true, content: modified };
|
|
17282
|
+
}
|
|
17283
|
+
var INSTRUMENTATION_FILENAMES = [
|
|
17284
|
+
"instrumentation.ts",
|
|
17285
|
+
"instrumentation.js",
|
|
17286
|
+
"instrumentation.mjs"
|
|
17287
|
+
];
|
|
17288
|
+
function resolveInstrumentationTarget(projectRoot) {
|
|
17289
|
+
const rootExisting = [];
|
|
17290
|
+
const srcExisting = [];
|
|
17291
|
+
for (const name of INSTRUMENTATION_FILENAMES) {
|
|
17292
|
+
const rootPath = path.join(projectRoot, name);
|
|
17293
|
+
if (isRegularFile(rootPath)) {
|
|
17294
|
+
rootExisting.push(rootPath);
|
|
17295
|
+
}
|
|
17296
|
+
const srcPath = path.join(projectRoot, "src", name);
|
|
17297
|
+
if (isRegularFile(srcPath)) {
|
|
17298
|
+
srcExisting.push(srcPath);
|
|
17148
17299
|
}
|
|
17149
17300
|
}
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17155
|
-
|
|
17301
|
+
const existing = [...rootExisting, ...srcExisting];
|
|
17302
|
+
if (rootExisting.length > 0 && srcExisting.length > 0) {
|
|
17303
|
+
return {
|
|
17304
|
+
target: null,
|
|
17305
|
+
layout: null,
|
|
17306
|
+
existing,
|
|
17307
|
+
rootExisting,
|
|
17308
|
+
srcExisting,
|
|
17309
|
+
conflict: true
|
|
17310
|
+
};
|
|
17156
17311
|
}
|
|
17157
|
-
if (
|
|
17158
|
-
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17312
|
+
if (srcExisting.length > 0) {
|
|
17313
|
+
return {
|
|
17314
|
+
target: srcExisting[0],
|
|
17315
|
+
layout: "src",
|
|
17316
|
+
existing,
|
|
17317
|
+
rootExisting,
|
|
17318
|
+
srcExisting,
|
|
17319
|
+
conflict: false
|
|
17320
|
+
};
|
|
17163
17321
|
}
|
|
17164
|
-
|
|
17165
|
-
|
|
17166
|
-
|
|
17167
|
-
|
|
17168
|
-
|
|
17169
|
-
|
|
17170
|
-
|
|
17322
|
+
if (rootExisting.length > 0) {
|
|
17323
|
+
return {
|
|
17324
|
+
target: rootExisting[0],
|
|
17325
|
+
layout: "root",
|
|
17326
|
+
existing,
|
|
17327
|
+
rootExisting,
|
|
17328
|
+
srcExisting,
|
|
17329
|
+
conflict: false
|
|
17330
|
+
};
|
|
17171
17331
|
}
|
|
17332
|
+
const srcDir = path.join(projectRoot, "src");
|
|
17333
|
+
const layout = isDirectory(srcDir) ? "src" : "root";
|
|
17334
|
+
const target = layout === "src" ? path.join(projectRoot, "src", "instrumentation.ts") : path.join(projectRoot, "instrumentation.ts");
|
|
17172
17335
|
return {
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17336
|
+
target,
|
|
17337
|
+
layout,
|
|
17338
|
+
existing,
|
|
17339
|
+
rootExisting,
|
|
17340
|
+
srcExisting,
|
|
17341
|
+
conflict: false
|
|
17176
17342
|
};
|
|
17177
17343
|
}
|
|
17178
|
-
function
|
|
17344
|
+
function isDirectory(p) {
|
|
17179
17345
|
try {
|
|
17180
|
-
return
|
|
17346
|
+
return fs.statSync(p).isDirectory();
|
|
17181
17347
|
} catch {
|
|
17182
|
-
return
|
|
17348
|
+
return false;
|
|
17183
17349
|
}
|
|
17184
17350
|
}
|
|
17185
|
-
function
|
|
17351
|
+
function isRegularFile(p) {
|
|
17186
17352
|
try {
|
|
17187
|
-
|
|
17188
|
-
|
|
17353
|
+
const stat2 = fs.lstatSync(p);
|
|
17354
|
+
if (stat2.isSymbolicLink()) {
|
|
17355
|
+
return fs.statSync(p).isFile();
|
|
17356
|
+
}
|
|
17357
|
+
return stat2.isFile();
|
|
17189
17358
|
} catch {
|
|
17190
17359
|
return false;
|
|
17191
17360
|
}
|
|
17192
17361
|
}
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
"
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
|
|
17204
|
-
|
|
17362
|
+
function appendRegisterFunction(content) {
|
|
17363
|
+
const importLine = 'import { registerGlasstrace } from "@glasstrace/sdk";\n';
|
|
17364
|
+
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";
|
|
17365
|
+
let withImport = content;
|
|
17366
|
+
const hasGlasstraceImport2 = content.includes("@glasstrace/sdk");
|
|
17367
|
+
if (!hasGlasstraceImport2) {
|
|
17368
|
+
withImport = importLine + content;
|
|
17369
|
+
} else {
|
|
17370
|
+
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
|
|
17371
|
+
const importMatch = importRegex.exec(content);
|
|
17372
|
+
if (importMatch) {
|
|
17373
|
+
const specifiers = importMatch[1];
|
|
17374
|
+
const alreadyImported = specifiers.split(",").some((s) => s.trim() === "registerGlasstrace");
|
|
17375
|
+
if (!alreadyImported) {
|
|
17376
|
+
const existingImports = specifiers.trimEnd();
|
|
17377
|
+
const separator = existingImports.endsWith(",") ? " " : ", ";
|
|
17378
|
+
const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from "@glasstrace/sdk"`;
|
|
17379
|
+
withImport = content.replace(importMatch[0], updatedImport);
|
|
17380
|
+
}
|
|
17381
|
+
} else {
|
|
17382
|
+
withImport = importLine + content;
|
|
17383
|
+
}
|
|
17205
17384
|
}
|
|
17206
|
-
|
|
17207
|
-
|
|
17208
|
-
// src/cli/status.ts
|
|
17209
|
-
var status_exports = {};
|
|
17210
|
-
__export(status_exports, {
|
|
17211
|
-
runStatus: () => runStatus
|
|
17212
|
-
});
|
|
17213
|
-
function runStatus(options) {
|
|
17214
|
-
const root = options.projectRoot;
|
|
17215
|
-
return {
|
|
17216
|
-
installed: checkInstalled(root),
|
|
17217
|
-
initialized: checkInitialized(root),
|
|
17218
|
-
instrumentation: checkInstrumentation(root),
|
|
17219
|
-
configWrapped: checkConfigWrapped(root),
|
|
17220
|
-
anonKey: checkAnonKey(root),
|
|
17221
|
-
mcpConfigured: checkMcpConfigured(root),
|
|
17222
|
-
agents: checkAgents(root),
|
|
17223
|
-
runtime: readRuntimeState(root)
|
|
17224
|
-
};
|
|
17385
|
+
const trailingNewline = withImport.endsWith("\n") ? "" : "\n";
|
|
17386
|
+
return withImport + trailingNewline + functionBlock;
|
|
17225
17387
|
}
|
|
17226
|
-
function
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
|
|
17234
|
-
|
|
17235
|
-
|
|
17388
|
+
async function defaultInstrumentationPrompt(question, defaultValue) {
|
|
17389
|
+
if (!process.stdin.isTTY) return defaultValue;
|
|
17390
|
+
const readline2 = await import("node:readline");
|
|
17391
|
+
const rl = readline2.createInterface({
|
|
17392
|
+
input: process.stdin,
|
|
17393
|
+
output: process.stdout
|
|
17394
|
+
});
|
|
17395
|
+
return new Promise((resolve2) => {
|
|
17396
|
+
const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
|
|
17397
|
+
rl.question(question + suffix, (answer) => {
|
|
17398
|
+
rl.close();
|
|
17399
|
+
const trimmed = answer.trim().toLowerCase();
|
|
17400
|
+
if (trimmed === "") {
|
|
17401
|
+
resolve2(defaultValue);
|
|
17402
|
+
return;
|
|
17403
|
+
}
|
|
17404
|
+
resolve2(trimmed === "y" || trimmed === "yes");
|
|
17405
|
+
});
|
|
17406
|
+
});
|
|
17407
|
+
}
|
|
17408
|
+
async function scaffoldInstrumentation(projectRoot, options = {}) {
|
|
17409
|
+
const target = resolveInstrumentationTarget(projectRoot);
|
|
17410
|
+
if (target.conflict) {
|
|
17411
|
+
return {
|
|
17412
|
+
action: "conflict",
|
|
17413
|
+
// Point the user at the `src/` variant — modern Next.js apps with a
|
|
17414
|
+
// `src/` directory load from there, so that's the merge target. The
|
|
17415
|
+
// competing path is reported separately for the error message.
|
|
17416
|
+
filePath: target.srcExisting[0],
|
|
17417
|
+
conflictingPath: target.rootExisting[0]
|
|
17418
|
+
};
|
|
17419
|
+
}
|
|
17420
|
+
const filePath = target.target;
|
|
17421
|
+
const layout = target.layout;
|
|
17422
|
+
if (filePath === null || layout === null) {
|
|
17423
|
+
return { action: "unrecognized" };
|
|
17236
17424
|
}
|
|
17425
|
+
const force = options.force === true;
|
|
17426
|
+
const prompt = options.prompt ?? defaultInstrumentationPrompt;
|
|
17427
|
+
if (!fs.existsSync(filePath)) {
|
|
17428
|
+
const content = `import { registerGlasstrace } from "@glasstrace/sdk";
|
|
17429
|
+
|
|
17430
|
+
export async function register() {
|
|
17431
|
+
// Glasstrace must be registered before Prisma instrumentation
|
|
17432
|
+
// to ensure all ORM spans are captured correctly.
|
|
17433
|
+
// If you use @prisma/instrumentation, import it after this call.
|
|
17434
|
+
registerGlasstrace();
|
|
17237
17435
|
}
|
|
17238
|
-
|
|
17239
|
-
|
|
17240
|
-
|
|
17241
|
-
|
|
17242
|
-
|
|
17436
|
+
`;
|
|
17437
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17438
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
17439
|
+
return { action: "created", filePath, layout };
|
|
17440
|
+
}
|
|
17441
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
17442
|
+
if (hasRegisterGlasstraceCall(existing)) {
|
|
17443
|
+
return { action: "already-registered", filePath, layout };
|
|
17243
17444
|
}
|
|
17445
|
+
if (!force) {
|
|
17446
|
+
const approved = await prompt(
|
|
17447
|
+
`Merge registerGlasstrace() into ${path.relative(projectRoot, filePath)}?`,
|
|
17448
|
+
false
|
|
17449
|
+
);
|
|
17450
|
+
if (!approved) {
|
|
17451
|
+
return { action: "skipped", filePath, layout };
|
|
17452
|
+
}
|
|
17453
|
+
}
|
|
17454
|
+
const injectResult = injectRegisterGlasstrace(existing);
|
|
17455
|
+
if (injectResult.injected) {
|
|
17456
|
+
fs.writeFileSync(filePath, injectResult.content, "utf-8");
|
|
17457
|
+
return { action: "injected", filePath, layout };
|
|
17458
|
+
}
|
|
17459
|
+
const appended = appendRegisterFunction(existing);
|
|
17460
|
+
fs.writeFileSync(filePath, appended, "utf-8");
|
|
17461
|
+
return { action: "appended", filePath, layout };
|
|
17244
17462
|
}
|
|
17245
|
-
function
|
|
17246
|
-
|
|
17247
|
-
|
|
17248
|
-
|
|
17249
|
-
|
|
17250
|
-
|
|
17251
|
-
|
|
17252
|
-
|
|
17463
|
+
async function scaffoldNextConfig(projectRoot) {
|
|
17464
|
+
let configPath;
|
|
17465
|
+
let configName;
|
|
17466
|
+
for (const name of NEXT_CONFIG_NAMES) {
|
|
17467
|
+
const candidate = path.join(projectRoot, name);
|
|
17468
|
+
if (fs.existsSync(candidate)) {
|
|
17469
|
+
configPath = candidate;
|
|
17470
|
+
configName = name;
|
|
17471
|
+
break;
|
|
17253
17472
|
}
|
|
17254
17473
|
}
|
|
17255
|
-
|
|
17256
|
-
|
|
17257
|
-
|
|
17258
|
-
|
|
17259
|
-
|
|
17260
|
-
|
|
17261
|
-
|
|
17262
|
-
|
|
17263
|
-
|
|
17264
|
-
|
|
17474
|
+
if (configPath === void 0 || configName === void 0) {
|
|
17475
|
+
return null;
|
|
17476
|
+
}
|
|
17477
|
+
const existing = fs.readFileSync(configPath, "utf-8");
|
|
17478
|
+
if (existing.trim().length === 0) {
|
|
17479
|
+
return { modified: false, reason: "empty-file" };
|
|
17480
|
+
}
|
|
17481
|
+
if (existing.includes("withGlasstraceConfig")) {
|
|
17482
|
+
return { modified: false, reason: "already-wrapped" };
|
|
17483
|
+
}
|
|
17484
|
+
const isESM = configName.endsWith(".ts") || configName.endsWith(".mjs");
|
|
17485
|
+
if (isESM) {
|
|
17486
|
+
const importLine = 'import { withGlasstraceConfig } from "@glasstrace/sdk";\n';
|
|
17487
|
+
const wrapResult2 = wrapExport(existing);
|
|
17488
|
+
if (!wrapResult2.wrapped) {
|
|
17489
|
+
return { modified: false, reason: "no-export" };
|
|
17265
17490
|
}
|
|
17491
|
+
const modified2 = importLine + "\n" + wrapResult2.content;
|
|
17492
|
+
fs.writeFileSync(configPath, modified2, "utf-8");
|
|
17493
|
+
return { modified: true };
|
|
17266
17494
|
}
|
|
17267
|
-
|
|
17495
|
+
const requireLine = 'const { withGlasstraceConfig } = require("@glasstrace/sdk");\n';
|
|
17496
|
+
const wrapResult = wrapCJSExport(existing);
|
|
17497
|
+
if (!wrapResult.wrapped) {
|
|
17498
|
+
return { modified: false, reason: "no-export" };
|
|
17499
|
+
}
|
|
17500
|
+
const modified = requireLine + "\n" + wrapResult.content;
|
|
17501
|
+
fs.writeFileSync(configPath, modified, "utf-8");
|
|
17502
|
+
return { modified: true };
|
|
17268
17503
|
}
|
|
17269
|
-
function
|
|
17270
|
-
|
|
17271
|
-
|
|
17272
|
-
|
|
17273
|
-
return false;
|
|
17504
|
+
function wrapExport(content) {
|
|
17505
|
+
const marker = "export default";
|
|
17506
|
+
const idx = content.lastIndexOf(marker);
|
|
17507
|
+
if (idx === -1) {
|
|
17508
|
+
return { content, wrapped: false };
|
|
17509
|
+
}
|
|
17510
|
+
const preamble = content.slice(0, idx);
|
|
17511
|
+
const exprRaw = content.slice(idx + marker.length);
|
|
17512
|
+
const expr = exprRaw.trim().replace(/;?\s*$/, "");
|
|
17513
|
+
if (expr.length === 0) {
|
|
17514
|
+
return { content, wrapped: false };
|
|
17274
17515
|
}
|
|
17516
|
+
return {
|
|
17517
|
+
content: preamble + `export default withGlasstraceConfig(${expr});
|
|
17518
|
+
`,
|
|
17519
|
+
wrapped: true
|
|
17520
|
+
};
|
|
17275
17521
|
}
|
|
17276
|
-
function
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
|
|
17280
|
-
|
|
17281
|
-
const mcpServers = parsed["mcpServers"];
|
|
17282
|
-
if (mcpServers && typeof mcpServers === "object" && "glasstrace" in mcpServers) {
|
|
17283
|
-
return true;
|
|
17284
|
-
}
|
|
17285
|
-
} catch {
|
|
17286
|
-
}
|
|
17522
|
+
function wrapCJSExport(content) {
|
|
17523
|
+
const cjsMarker = "module.exports";
|
|
17524
|
+
const cjsIdx = content.lastIndexOf(cjsMarker);
|
|
17525
|
+
if (cjsIdx === -1) {
|
|
17526
|
+
return { content, wrapped: false };
|
|
17287
17527
|
}
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
}
|
|
17294
|
-
} catch {
|
|
17295
|
-
}
|
|
17528
|
+
const preamble = content.slice(0, cjsIdx);
|
|
17529
|
+
const afterMarker = content.slice(cjsIdx + cjsMarker.length);
|
|
17530
|
+
const eqMatch = /^\s*=\s*/.exec(afterMarker);
|
|
17531
|
+
if (!eqMatch) {
|
|
17532
|
+
return { content, wrapped: false };
|
|
17296
17533
|
}
|
|
17297
|
-
|
|
17534
|
+
const exprRaw = afterMarker.slice(eqMatch[0].length);
|
|
17535
|
+
const expr = exprRaw.trim().replace(/;?\s*$/, "");
|
|
17536
|
+
if (expr.length === 0) {
|
|
17537
|
+
return { content, wrapped: false };
|
|
17538
|
+
}
|
|
17539
|
+
return {
|
|
17540
|
+
content: preamble + `module.exports = withGlasstraceConfig(${expr});
|
|
17541
|
+
`,
|
|
17542
|
+
wrapped: true
|
|
17543
|
+
};
|
|
17298
17544
|
}
|
|
17299
|
-
function
|
|
17300
|
-
const
|
|
17301
|
-
|
|
17302
|
-
|
|
17303
|
-
|
|
17304
|
-
|
|
17305
|
-
const hasHashMarkers = content.includes("# glasstrace:mcp:start") && content.includes("# glasstrace:mcp:end");
|
|
17306
|
-
if (hasHtmlMarkers || hasHashMarkers) {
|
|
17307
|
-
found.push(name);
|
|
17308
|
-
}
|
|
17309
|
-
} catch {
|
|
17545
|
+
async function scaffoldEnvLocal(projectRoot) {
|
|
17546
|
+
const filePath = path.join(projectRoot, ".env.local");
|
|
17547
|
+
if (fs.existsSync(filePath)) {
|
|
17548
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
17549
|
+
if (/^\s*#?\s*GLASSTRACE_API_KEY\s*=/m.test(existing)) {
|
|
17550
|
+
return false;
|
|
17310
17551
|
}
|
|
17552
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
17553
|
+
fs.writeFileSync(filePath, existing + separator + "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
|
|
17554
|
+
return true;
|
|
17311
17555
|
}
|
|
17312
|
-
|
|
17556
|
+
fs.writeFileSync(filePath, "# GLASSTRACE_API_KEY=your_key_here\n", "utf-8");
|
|
17557
|
+
return true;
|
|
17313
17558
|
}
|
|
17314
|
-
function
|
|
17315
|
-
const
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
|
|
17325
|
-
|
|
17326
|
-
|
|
17327
|
-
const content = fs8.readFileSync(filePath, "utf-8");
|
|
17328
|
-
const parsed = JSON.parse(content);
|
|
17329
|
-
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : null;
|
|
17330
|
-
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
17331
|
-
const core = parsed.core;
|
|
17332
|
-
const auth = parsed.auth;
|
|
17333
|
-
const otel = parsed.otel;
|
|
17334
|
-
const coreState = typeof core?.state === "string" ? core.state : null;
|
|
17335
|
-
const authState = typeof auth?.state === "string" ? auth.state : null;
|
|
17336
|
-
const otelState = typeof otel?.state === "string" ? otel.state : null;
|
|
17337
|
-
const otelScenario = typeof otel?.scenario === "string" ? otel.scenario : null;
|
|
17338
|
-
let stale = false;
|
|
17339
|
-
if (coreState === "SHUTDOWN") {
|
|
17340
|
-
stale = false;
|
|
17341
|
-
} else if (updatedAt) {
|
|
17342
|
-
const updatedMs = new Date(updatedAt).getTime();
|
|
17343
|
-
const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;
|
|
17344
|
-
if (age > STALE_THRESHOLD_MS) {
|
|
17345
|
-
if (pid && pid > 0) {
|
|
17346
|
-
try {
|
|
17347
|
-
process.kill(pid, 0);
|
|
17348
|
-
stale = false;
|
|
17349
|
-
} catch (err) {
|
|
17350
|
-
const code = err?.code;
|
|
17351
|
-
if (code === "EPERM") {
|
|
17352
|
-
stale = false;
|
|
17353
|
-
} else {
|
|
17354
|
-
stale = true;
|
|
17355
|
-
}
|
|
17356
|
-
}
|
|
17357
|
-
} else {
|
|
17358
|
-
stale = true;
|
|
17359
|
-
}
|
|
17360
|
-
}
|
|
17559
|
+
async function addCoverageMapEnv(projectRoot) {
|
|
17560
|
+
const filePath = path.join(projectRoot, ".env.local");
|
|
17561
|
+
if (!fs.existsSync(filePath)) {
|
|
17562
|
+
fs.writeFileSync(filePath, "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
|
|
17563
|
+
return true;
|
|
17564
|
+
}
|
|
17565
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
17566
|
+
const keyRegex = /^(\s*GLASSTRACE_COVERAGE_MAP\s*=\s*)(.*)$/m;
|
|
17567
|
+
const keyMatch = keyRegex.exec(existing);
|
|
17568
|
+
if (keyMatch) {
|
|
17569
|
+
const currentValue = keyMatch[2].trim();
|
|
17570
|
+
if (currentValue === "true") {
|
|
17571
|
+
return false;
|
|
17361
17572
|
}
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
|
|
17365
|
-
coreState,
|
|
17366
|
-
authState,
|
|
17367
|
-
otelState,
|
|
17368
|
-
otelScenario,
|
|
17369
|
-
updatedAt,
|
|
17370
|
-
pid
|
|
17371
|
-
};
|
|
17372
|
-
} catch {
|
|
17373
|
-
return empty;
|
|
17573
|
+
const updated = existing.replace(keyRegex, `${keyMatch[1]}true`);
|
|
17574
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
17575
|
+
return true;
|
|
17374
17576
|
}
|
|
17577
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
17578
|
+
fs.writeFileSync(filePath, existing + separator + "GLASSTRACE_COVERAGE_MAP=true\n", "utf-8");
|
|
17579
|
+
return true;
|
|
17375
17580
|
}
|
|
17376
|
-
|
|
17377
|
-
|
|
17378
|
-
|
|
17379
|
-
|
|
17380
|
-
|
|
17381
|
-
|
|
17382
|
-
|
|
17383
|
-
|
|
17384
|
-
|
|
17385
|
-
|
|
17386
|
-
|
|
17387
|
-
"codex.md",
|
|
17388
|
-
".cursorrules"
|
|
17389
|
-
];
|
|
17390
|
-
INSTRUMENTATION_FILES = [
|
|
17391
|
-
"instrumentation.ts",
|
|
17392
|
-
"instrumentation.js",
|
|
17393
|
-
"instrumentation.mjs",
|
|
17394
|
-
"src/instrumentation.ts",
|
|
17395
|
-
"src/instrumentation.js",
|
|
17396
|
-
"src/instrumentation.mjs"
|
|
17397
|
-
];
|
|
17398
|
-
STALE_THRESHOLD_MS = 3e4;
|
|
17581
|
+
async function scaffoldGitignore(projectRoot) {
|
|
17582
|
+
const filePath = path.join(projectRoot, ".gitignore");
|
|
17583
|
+
if (fs.existsSync(filePath)) {
|
|
17584
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
17585
|
+
const lines = existing.split("\n").map((l) => l.trim());
|
|
17586
|
+
if (lines.includes(".glasstrace/")) {
|
|
17587
|
+
return false;
|
|
17588
|
+
}
|
|
17589
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
17590
|
+
fs.writeFileSync(filePath, existing + separator + ".glasstrace/\n", "utf-8");
|
|
17591
|
+
return true;
|
|
17399
17592
|
}
|
|
17400
|
-
|
|
17593
|
+
fs.writeFileSync(filePath, ".glasstrace/\n", "utf-8");
|
|
17594
|
+
return true;
|
|
17595
|
+
}
|
|
17401
17596
|
|
|
17402
17597
|
// src/cli/init.ts
|
|
17403
|
-
|
|
17404
|
-
__export(init_exports, {
|
|
17405
|
-
decideMcpConfigAction: () => decideMcpConfigAction,
|
|
17406
|
-
gitignoreExcludesDiscoveryFile: () => gitignoreExcludesDiscoveryFile,
|
|
17407
|
-
meetsNodeVersion: () => meetsNodeVersion,
|
|
17408
|
-
rollbackSteps: () => rollbackSteps,
|
|
17409
|
-
runInit: () => runInit,
|
|
17410
|
-
verifyAnonKeyRegistration: () => verifyAnonKeyRegistration
|
|
17411
|
-
});
|
|
17412
|
-
module.exports = __toCommonJS(init_exports);
|
|
17413
|
-
var fs9 = __toESM(require("node:fs"), 1);
|
|
17414
|
-
var path9 = __toESM(require("node:path"), 1);
|
|
17415
|
-
var readline = __toESM(require("node:readline"), 1);
|
|
17416
|
-
init_scaffolder();
|
|
17598
|
+
init_mcp_runtime();
|
|
17417
17599
|
|
|
17418
17600
|
// src/import-graph.ts
|
|
17419
17601
|
var fs2 = __toESM(require("node:fs/promises"), 1);
|
|
@@ -18332,6 +18514,9 @@ Then add this as the first statement in your register() function:
|
|
|
18332
18514
|
);
|
|
18333
18515
|
}
|
|
18334
18516
|
let anyConfigWritten = false;
|
|
18517
|
+
let anyConfigRewrittenWithBearer = false;
|
|
18518
|
+
const resolved = await resolveEffectiveMcpCredential(projectRoot);
|
|
18519
|
+
const bearer = resolved.effective?.key ?? anonKey;
|
|
18335
18520
|
if (isCI) {
|
|
18336
18521
|
const genericAgent = {
|
|
18337
18522
|
name: "generic",
|
|
@@ -18340,7 +18525,7 @@ Then add this as the first statement in your register() function:
|
|
|
18340
18525
|
cliAvailable: false,
|
|
18341
18526
|
registrationCommand: null
|
|
18342
18527
|
};
|
|
18343
|
-
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT,
|
|
18528
|
+
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
|
|
18344
18529
|
const decision = await decideMcpConfigAction({
|
|
18345
18530
|
configPath: genericAgent.mcpConfigPath,
|
|
18346
18531
|
expectedContent: genericConfig,
|
|
@@ -18348,6 +18533,9 @@ Then add this as the first statement in your register() function:
|
|
|
18348
18533
|
});
|
|
18349
18534
|
if (decision !== "skip") {
|
|
18350
18535
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
18536
|
+
if (genericAgent.mcpConfigPath !== null && fs9.existsSync(genericAgent.mcpConfigPath)) {
|
|
18537
|
+
anyConfigRewrittenWithBearer = true;
|
|
18538
|
+
}
|
|
18351
18539
|
}
|
|
18352
18540
|
if (genericAgent.mcpConfigPath !== null && fs9.existsSync(genericAgent.mcpConfigPath)) {
|
|
18353
18541
|
anyConfigWritten = true;
|
|
@@ -18368,17 +18556,18 @@ Then add this as the first statement in your register() function:
|
|
|
18368
18556
|
cliAvailable: false,
|
|
18369
18557
|
registrationCommand: null
|
|
18370
18558
|
};
|
|
18371
|
-
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT,
|
|
18559
|
+
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
|
|
18372
18560
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
18373
18561
|
if (genericAgent.mcpConfigPath !== null && fs9.existsSync(genericAgent.mcpConfigPath)) {
|
|
18374
18562
|
anyConfigWritten = true;
|
|
18563
|
+
anyConfigRewrittenWithBearer = true;
|
|
18375
18564
|
}
|
|
18376
18565
|
agents = [];
|
|
18377
18566
|
}
|
|
18378
18567
|
const configuredNames = [];
|
|
18379
18568
|
for (const agent of agents) {
|
|
18380
18569
|
try {
|
|
18381
|
-
const configContent = generateMcpConfig(agent, MCP_ENDPOINT,
|
|
18570
|
+
const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);
|
|
18382
18571
|
const decision = await decideMcpConfigAction({
|
|
18383
18572
|
configPath: agent.mcpConfigPath,
|
|
18384
18573
|
expectedContent: configContent,
|
|
@@ -18399,6 +18588,7 @@ Then add this as the first statement in your register() function:
|
|
|
18399
18588
|
continue;
|
|
18400
18589
|
}
|
|
18401
18590
|
anyConfigWritten = true;
|
|
18591
|
+
anyConfigRewrittenWithBearer = true;
|
|
18402
18592
|
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
18403
18593
|
if (infoContent !== "") {
|
|
18404
18594
|
await injectInfoSection(agent, infoContent, projectRoot);
|
|
@@ -18422,8 +18612,13 @@ Then add this as the first statement in your register() function:
|
|
|
18422
18612
|
[".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".codex/config.toml"],
|
|
18423
18613
|
projectRoot
|
|
18424
18614
|
);
|
|
18425
|
-
if (
|
|
18426
|
-
const
|
|
18615
|
+
if (anyConfigRewrittenWithBearer) {
|
|
18616
|
+
const markerSource = resolved.effective?.source ?? "anon";
|
|
18617
|
+
const markerHash = identityFingerprint(bearer);
|
|
18618
|
+
const markerCreated = await writeMcpMarker(projectRoot, {
|
|
18619
|
+
credentialSource: markerSource,
|
|
18620
|
+
credentialHash: markerHash
|
|
18621
|
+
});
|
|
18427
18622
|
if (markerCreated) {
|
|
18428
18623
|
summary.push("Created .glasstrace/mcp-connected marker");
|
|
18429
18624
|
}
|
|
@@ -18492,7 +18687,7 @@ async function verifyAnonKeyRegistration(projectRoot) {
|
|
|
18492
18687
|
}
|
|
18493
18688
|
const baseConfig = resolveConfig({ apiKey: devKey });
|
|
18494
18689
|
const config2 = { ...baseConfig, apiKey: devKey };
|
|
18495
|
-
const sdkVersion = true ? "1.1.
|
|
18690
|
+
const sdkVersion = true ? "1.1.2" : "0.0.0-dev";
|
|
18496
18691
|
const result = await verifyInitReachable(config2, anonKey, sdkVersion);
|
|
18497
18692
|
if (result.ok) {
|
|
18498
18693
|
return { outcome: "verified" };
|