@glasstrace/sdk 0.14.1 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ERGEG4ZQ.js → chunk-2LDBR3F3.js} +16 -3
- package/dist/chunk-2LDBR3F3.js.map +1 -0
- package/dist/chunk-A2AZL6MZ.js +309 -0
- package/dist/chunk-A2AZL6MZ.js.map +1 -0
- package/dist/{chunk-ARAOZCZT.js → chunk-ROFOJQWN.js} +118 -16
- package/dist/chunk-ROFOJQWN.js.map +1 -0
- package/dist/{chunk-WV3NIPWJ.js → chunk-ZNOD6FC7.js} +18 -276
- package/dist/chunk-ZNOD6FC7.js.map +1 -0
- package/dist/cli/init.cjs +458 -115
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +33 -1
- package/dist/cli/init.d.ts +33 -1
- package/dist/cli/init.js +144 -42
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +4 -2
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/uninit.cjs +181 -60
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +38 -8
- package/dist/cli/uninit.d.ts +38 -8
- package/dist/cli/uninit.js +6 -3
- package/dist/cli/validate.cjs +135 -0
- package/dist/cli/validate.cjs.map +1 -0
- package/dist/cli/validate.d.cts +60 -0
- package/dist/cli/validate.d.ts +60 -0
- package/dist/cli/validate.js +103 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/index.cjs +123 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -5
- package/dist/index.d.ts +45 -5
- package/dist/index.js +109 -46
- package/dist/index.js.map +1 -1
- package/dist/{source-map-uploader-W6VPGY26.js → source-map-uploader-3GWUQDTS.js} +6 -2
- package/package.json +6 -4
- package/dist/chunk-ARAOZCZT.js.map +0 -1
- package/dist/chunk-ERGEG4ZQ.js.map +0 -1
- package/dist/chunk-WV3NIPWJ.js.map +0 -1
- /package/dist/{source-map-uploader-W6VPGY26.js.map → source-map-uploader-3GWUQDTS.js.map} +0 -0
package/dist/cli/init.d.cts
CHANGED
|
@@ -9,6 +9,14 @@ interface InitOptions {
|
|
|
9
9
|
projectRoot: string;
|
|
10
10
|
yes: boolean;
|
|
11
11
|
coverageMap: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When true, skip interactive confirmation and overwrite existing
|
|
14
|
+
* MCP configuration files without prompting. Preservation of the
|
|
15
|
+
* anonymous key, config cache, and developer API key still applies
|
|
16
|
+
* regardless of this flag — `--force` only affects the MCP diff
|
|
17
|
+
* prompt (DISC-1247 Scenario 2c). Defaults to `false`.
|
|
18
|
+
*/
|
|
19
|
+
force?: boolean;
|
|
12
20
|
}
|
|
13
21
|
/** Result of running the init command. */
|
|
14
22
|
interface InitResult {
|
|
@@ -17,6 +25,30 @@ interface InitResult {
|
|
|
17
25
|
warnings: string[];
|
|
18
26
|
errors: string[];
|
|
19
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Decides whether the MCP config at `configPath` should be overwritten
|
|
30
|
+
* during re-init. Returns the action to take.
|
|
31
|
+
*
|
|
32
|
+
* - `"write"` — file does not exist, or existing content already matches
|
|
33
|
+
* the expected content. Safe to write.
|
|
34
|
+
* - `"skip"` — existing file differs AND the user chose to keep it, or
|
|
35
|
+
* we are in a non-interactive environment without `--force`.
|
|
36
|
+
* - `"force-overwrite"` — `force === true` (or user accepted the prompt)
|
|
37
|
+
* and content differs; overwrite.
|
|
38
|
+
*
|
|
39
|
+
* The prompt is skipped entirely when `force` is true (non-interactive
|
|
40
|
+
* overwrite) or when there is no existing file / content already matches.
|
|
41
|
+
*
|
|
42
|
+
* @internal Exported for unit testing only.
|
|
43
|
+
*/
|
|
44
|
+
declare function decideMcpConfigAction(options: {
|
|
45
|
+
configPath: string | null;
|
|
46
|
+
expectedContent: string;
|
|
47
|
+
force: boolean;
|
|
48
|
+
readFile?: (p: string) => string;
|
|
49
|
+
existsSync?: (p: string) => boolean;
|
|
50
|
+
prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;
|
|
51
|
+
}): Promise<"write" | "skip" | "force-overwrite">;
|
|
20
52
|
/**
|
|
21
53
|
* Identifies a scaffolding step that can be reversed during rollback.
|
|
22
54
|
* Steps are tracked in execution order and rolled back in reverse.
|
|
@@ -47,4 +79,4 @@ declare function rollbackSteps(steps: CompletedStep[], projectRoot: string, stat
|
|
|
47
79
|
*/
|
|
48
80
|
declare function runInit(options: InitOptions): Promise<InitResult>;
|
|
49
81
|
|
|
50
|
-
export { type InitOptions, type InitResult, meetsNodeVersion, rollbackSteps, runInit };
|
|
82
|
+
export { type InitOptions, type InitResult, decideMcpConfigAction, meetsNodeVersion, rollbackSteps, runInit };
|
package/dist/cli/init.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ interface InitOptions {
|
|
|
9
9
|
projectRoot: string;
|
|
10
10
|
yes: boolean;
|
|
11
11
|
coverageMap: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When true, skip interactive confirmation and overwrite existing
|
|
14
|
+
* MCP configuration files without prompting. Preservation of the
|
|
15
|
+
* anonymous key, config cache, and developer API key still applies
|
|
16
|
+
* regardless of this flag — `--force` only affects the MCP diff
|
|
17
|
+
* prompt (DISC-1247 Scenario 2c). Defaults to `false`.
|
|
18
|
+
*/
|
|
19
|
+
force?: boolean;
|
|
12
20
|
}
|
|
13
21
|
/** Result of running the init command. */
|
|
14
22
|
interface InitResult {
|
|
@@ -17,6 +25,30 @@ interface InitResult {
|
|
|
17
25
|
warnings: string[];
|
|
18
26
|
errors: string[];
|
|
19
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Decides whether the MCP config at `configPath` should be overwritten
|
|
30
|
+
* during re-init. Returns the action to take.
|
|
31
|
+
*
|
|
32
|
+
* - `"write"` — file does not exist, or existing content already matches
|
|
33
|
+
* the expected content. Safe to write.
|
|
34
|
+
* - `"skip"` — existing file differs AND the user chose to keep it, or
|
|
35
|
+
* we are in a non-interactive environment without `--force`.
|
|
36
|
+
* - `"force-overwrite"` — `force === true` (or user accepted the prompt)
|
|
37
|
+
* and content differs; overwrite.
|
|
38
|
+
*
|
|
39
|
+
* The prompt is skipped entirely when `force` is true (non-interactive
|
|
40
|
+
* overwrite) or when there is no existing file / content already matches.
|
|
41
|
+
*
|
|
42
|
+
* @internal Exported for unit testing only.
|
|
43
|
+
*/
|
|
44
|
+
declare function decideMcpConfigAction(options: {
|
|
45
|
+
configPath: string | null;
|
|
46
|
+
expectedContent: string;
|
|
47
|
+
force: boolean;
|
|
48
|
+
readFile?: (p: string) => string;
|
|
49
|
+
existsSync?: (p: string) => boolean;
|
|
50
|
+
prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;
|
|
51
|
+
}): Promise<"write" | "skip" | "force-overwrite">;
|
|
20
52
|
/**
|
|
21
53
|
* Identifies a scaffolding step that can be reversed during rollback.
|
|
22
54
|
* Steps are tracked in execution order and rolled back in reverse.
|
|
@@ -47,4 +79,4 @@ declare function rollbackSteps(steps: CompletedStep[], projectRoot: string, stat
|
|
|
47
79
|
*/
|
|
48
80
|
declare function runInit(options: InitOptions): Promise<InitResult>;
|
|
49
81
|
|
|
50
|
-
export { type InitOptions, type InitResult, meetsNodeVersion, rollbackSteps, runInit };
|
|
82
|
+
export { type InitOptions, type InitResult, decideMcpConfigAction, meetsNodeVersion, rollbackSteps, runInit };
|
package/dist/cli/init.js
CHANGED
|
@@ -11,25 +11,31 @@ import {
|
|
|
11
11
|
removeRegisterGlasstrace,
|
|
12
12
|
unwrapCJSExport,
|
|
13
13
|
unwrapExport
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-ROFOJQWN.js";
|
|
15
15
|
import {
|
|
16
|
-
addCoverageMapEnv,
|
|
17
16
|
detectAgents,
|
|
18
17
|
generateInfoSection,
|
|
19
18
|
generateMcpConfig,
|
|
20
19
|
injectInfoSection,
|
|
21
|
-
scaffoldEnvLocal,
|
|
22
|
-
scaffoldGitignore,
|
|
23
|
-
scaffoldInstrumentation,
|
|
24
|
-
scaffoldMcpMarker,
|
|
25
|
-
scaffoldNextConfig,
|
|
26
20
|
updateGitignore,
|
|
27
21
|
writeMcpConfig
|
|
28
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-ZNOD6FC7.js";
|
|
29
23
|
import {
|
|
30
|
-
getOrCreateAnonKey
|
|
24
|
+
getOrCreateAnonKey,
|
|
25
|
+
readAnonKey
|
|
31
26
|
} from "../chunk-ECEN724Y.js";
|
|
32
27
|
import "../chunk-YMEXDDTA.js";
|
|
28
|
+
import {
|
|
29
|
+
addCoverageMapEnv,
|
|
30
|
+
isDevApiKey,
|
|
31
|
+
mcpConfigMatches,
|
|
32
|
+
readEnvLocalApiKey,
|
|
33
|
+
scaffoldEnvLocal,
|
|
34
|
+
scaffoldGitignore,
|
|
35
|
+
scaffoldInstrumentation,
|
|
36
|
+
scaffoldMcpMarker,
|
|
37
|
+
scaffoldNextConfig
|
|
38
|
+
} from "../chunk-A2AZL6MZ.js";
|
|
33
39
|
import {
|
|
34
40
|
MCP_ENDPOINT,
|
|
35
41
|
NEXT_CONFIG_NAMES,
|
|
@@ -48,6 +54,31 @@ function meetsNodeVersion(minMajor) {
|
|
|
48
54
|
const [major] = process.versions.node.split(".").map(Number);
|
|
49
55
|
return major >= minMajor;
|
|
50
56
|
}
|
|
57
|
+
async function decideMcpConfigAction(options) {
|
|
58
|
+
const { configPath, expectedContent, force } = options;
|
|
59
|
+
if (configPath === null) return "write";
|
|
60
|
+
const exists = options.existsSync ?? fs.existsSync;
|
|
61
|
+
const read = options.readFile ?? ((p) => fs.readFileSync(p, "utf-8"));
|
|
62
|
+
const prompt = options.prompt ?? promptYesNo;
|
|
63
|
+
if (!exists(configPath)) return "write";
|
|
64
|
+
let existingContent;
|
|
65
|
+
try {
|
|
66
|
+
existingContent = read(configPath);
|
|
67
|
+
} catch {
|
|
68
|
+
return "write";
|
|
69
|
+
}
|
|
70
|
+
if (mcpConfigMatches(existingContent, expectedContent)) {
|
|
71
|
+
return "write";
|
|
72
|
+
}
|
|
73
|
+
if (force) {
|
|
74
|
+
return "force-overwrite";
|
|
75
|
+
}
|
|
76
|
+
const answer = await prompt(
|
|
77
|
+
`Existing MCP config at ${configPath} differs from Glasstrace's template. Overwrite?`,
|
|
78
|
+
false
|
|
79
|
+
);
|
|
80
|
+
return answer ? "force-overwrite" : "skip";
|
|
81
|
+
}
|
|
51
82
|
async function promptYesNo(question, defaultValue) {
|
|
52
83
|
if (!process.stdin.isTTY) {
|
|
53
84
|
return defaultValue;
|
|
@@ -228,10 +259,20 @@ async function runInit(options) {
|
|
|
228
259
|
return { exitCode: 1, summary, warnings, errors };
|
|
229
260
|
}
|
|
230
261
|
try {
|
|
262
|
+
const envPathForCheck = path.join(projectRoot, ".env.local");
|
|
263
|
+
let existingDevKey = false;
|
|
264
|
+
if (fs.existsSync(envPathForCheck)) {
|
|
265
|
+
const existingContent = fs.readFileSync(envPathForCheck, "utf-8");
|
|
266
|
+
existingDevKey = isDevApiKey(readEnvLocalApiKey(existingContent));
|
|
267
|
+
}
|
|
231
268
|
const envCreated = await scaffoldEnvLocal(projectRoot);
|
|
232
269
|
if (envCreated) {
|
|
233
270
|
summary.push("Updated .env.local with Glasstrace configuration");
|
|
234
271
|
rollbackState.steps.push("env-local");
|
|
272
|
+
} else if (existingDevKey) {
|
|
273
|
+
summary.push(
|
|
274
|
+
"Preserved existing .env.local (GLASSTRACE_API_KEY contains a claimed dev key)"
|
|
275
|
+
);
|
|
235
276
|
} else {
|
|
236
277
|
summary.push("Skipped .env.local (GLASSTRACE_API_KEY already configured)");
|
|
237
278
|
}
|
|
@@ -256,7 +297,11 @@ async function runInit(options) {
|
|
|
256
297
|
const ciEnv = process.env["CI"];
|
|
257
298
|
const isCI = typeof ciEnv === "string" && ciEnv.trim() !== "" && ciEnv.toLowerCase() !== "false" && ciEnv.trim() !== "0" || process.env["GITHUB_ACTIONS"] === "true";
|
|
258
299
|
try {
|
|
300
|
+
const preExistingAnonKey = await readAnonKey(projectRoot);
|
|
259
301
|
const anonKey = await getOrCreateAnonKey(projectRoot);
|
|
302
|
+
if (preExistingAnonKey !== null) {
|
|
303
|
+
summary.push("Preserved existing .glasstrace/anon_key");
|
|
304
|
+
}
|
|
260
305
|
let anyConfigWritten = false;
|
|
261
306
|
if (isCI) {
|
|
262
307
|
const genericAgent = {
|
|
@@ -267,7 +312,14 @@ async function runInit(options) {
|
|
|
267
312
|
registrationCommand: null
|
|
268
313
|
};
|
|
269
314
|
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);
|
|
270
|
-
await
|
|
315
|
+
const decision = await decideMcpConfigAction({
|
|
316
|
+
configPath: genericAgent.mcpConfigPath,
|
|
317
|
+
expectedContent: genericConfig,
|
|
318
|
+
force: true
|
|
319
|
+
});
|
|
320
|
+
if (decision !== "skip") {
|
|
321
|
+
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
322
|
+
}
|
|
271
323
|
if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
|
|
272
324
|
anyConfigWritten = true;
|
|
273
325
|
summary.push("Created .glasstrace/mcp.json (CI mode)");
|
|
@@ -298,6 +350,20 @@ async function runInit(options) {
|
|
|
298
350
|
for (const agent of agents) {
|
|
299
351
|
try {
|
|
300
352
|
const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);
|
|
353
|
+
const decision = await decideMcpConfigAction({
|
|
354
|
+
configPath: agent.mcpConfigPath,
|
|
355
|
+
expectedContent: configContent,
|
|
356
|
+
force: options.force === true || options.yes
|
|
357
|
+
});
|
|
358
|
+
if (decision === "skip") {
|
|
359
|
+
summary.push(
|
|
360
|
+
`Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`
|
|
361
|
+
);
|
|
362
|
+
if (agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath)) {
|
|
363
|
+
anyConfigWritten = true;
|
|
364
|
+
}
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
301
367
|
await writeMcpConfig(agent, configContent, projectRoot);
|
|
302
368
|
const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);
|
|
303
369
|
if (!configExists) {
|
|
@@ -369,11 +435,14 @@ function parseArgs(argv) {
|
|
|
369
435
|
const args = argv.slice(2);
|
|
370
436
|
let yes = false;
|
|
371
437
|
let coverageMap = false;
|
|
438
|
+
let force = false;
|
|
372
439
|
for (const arg of args) {
|
|
373
440
|
if (arg === "--yes" || arg === "-y") {
|
|
374
441
|
yes = true;
|
|
375
442
|
} else if (arg === "--coverage-map") {
|
|
376
443
|
coverageMap = true;
|
|
444
|
+
} else if (arg === "--force") {
|
|
445
|
+
force = true;
|
|
377
446
|
}
|
|
378
447
|
}
|
|
379
448
|
if (!process.stdin.isTTY) {
|
|
@@ -382,7 +451,8 @@ function parseArgs(argv) {
|
|
|
382
451
|
return {
|
|
383
452
|
projectRoot: process.cwd(),
|
|
384
453
|
yes,
|
|
385
|
-
coverageMap
|
|
454
|
+
coverageMap,
|
|
455
|
+
force
|
|
386
456
|
};
|
|
387
457
|
}
|
|
388
458
|
var scriptPath = typeof process !== "undefined" && process.argv[1] !== void 0 ? process.argv[1].replace(/\\/g, "/") : void 0;
|
|
@@ -424,47 +494,78 @@ Usage: glasstrace mcp add [--force] [--dry-run]
|
|
|
424
494
|
process.exit(1);
|
|
425
495
|
}
|
|
426
496
|
} else if (subcommand === void 0 || subcommand === "init" || subcommand.startsWith("-")) {
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
497
|
+
const forwardedArgs = process.argv.slice(subcommand === "init" ? 3 : 2);
|
|
498
|
+
if (forwardedArgs.includes("--validate")) {
|
|
499
|
+
let validateProjectRoot = process.cwd();
|
|
500
|
+
try {
|
|
501
|
+
validateProjectRoot = resolveProjectRoot(validateProjectRoot).projectRoot;
|
|
502
|
+
} catch {
|
|
434
503
|
}
|
|
435
|
-
|
|
436
|
-
for (const
|
|
437
|
-
process.stderr.write(
|
|
504
|
+
import("./validate.js").then(({ runValidate }) => runValidate({ projectRoot: validateProjectRoot })).then((result) => {
|
|
505
|
+
for (const line of result.summary) {
|
|
506
|
+
process.stderr.write(`${line}
|
|
438
507
|
`);
|
|
439
508
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
509
|
+
for (const issue of result.issues) {
|
|
510
|
+
process.stderr.write(` - ${issue.message}
|
|
511
|
+
`);
|
|
512
|
+
if (issue.fix) {
|
|
513
|
+
process.stderr.write(` Fix: ${issue.fix}
|
|
445
514
|
`);
|
|
515
|
+
}
|
|
446
516
|
}
|
|
447
|
-
process.
|
|
448
|
-
|
|
517
|
+
process.exit(result.exitCode);
|
|
518
|
+
}).catch((err) => {
|
|
449
519
|
process.stderr.write(
|
|
450
|
-
|
|
520
|
+
`Fatal error: ${err instanceof Error ? err.message : String(err)}
|
|
521
|
+
`
|
|
451
522
|
);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
});
|
|
525
|
+
} else {
|
|
526
|
+
const options = parseArgs(process.argv);
|
|
527
|
+
runInit(options).then((result) => {
|
|
528
|
+
if (result.errors.length > 0) {
|
|
529
|
+
for (const err of result.errors) {
|
|
530
|
+
process.stderr.write(`Error: ${err}
|
|
531
|
+
`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (result.warnings.length > 0) {
|
|
535
|
+
for (const warn of result.warnings) {
|
|
536
|
+
process.stderr.write(`Warning: ${warn}
|
|
537
|
+
`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (result.summary.length > 0) {
|
|
541
|
+
process.stderr.write("\nGlasstrace initialized successfully!\n\n");
|
|
542
|
+
for (const line of result.summary) {
|
|
543
|
+
process.stderr.write(` - ${line}
|
|
544
|
+
`);
|
|
545
|
+
}
|
|
546
|
+
process.stderr.write("\nNext steps:\n");
|
|
547
|
+
process.stderr.write(" 1. Start your Next.js dev server\n");
|
|
548
|
+
process.stderr.write(
|
|
549
|
+
" 2. Glasstrace works immediately in anonymous mode\n"
|
|
550
|
+
);
|
|
551
|
+
process.stderr.write(
|
|
552
|
+
" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\n\n"
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
process.exit(result.exitCode);
|
|
556
|
+
}).catch((err) => {
|
|
452
557
|
process.stderr.write(
|
|
453
|
-
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
process.exit(result.exitCode);
|
|
457
|
-
}).catch((err) => {
|
|
458
|
-
process.stderr.write(
|
|
459
|
-
`Fatal error: ${err instanceof Error ? err.message : String(err)}
|
|
558
|
+
`Fatal error: ${err instanceof Error ? err.message : String(err)}
|
|
460
559
|
`
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
560
|
+
);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
464
564
|
} else if (subcommand === "uninit") {
|
|
465
565
|
const remainingArgs = process.argv.slice(3);
|
|
466
566
|
const dryRun = remainingArgs.includes("--dry-run");
|
|
467
|
-
|
|
567
|
+
const force = remainingArgs.includes("--force");
|
|
568
|
+
import("./uninit.js").then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun, force })).then((result) => {
|
|
468
569
|
if (result.errors.length > 0) {
|
|
469
570
|
for (const err of result.errors) {
|
|
470
571
|
process.stderr.write(`Error: ${err}
|
|
@@ -538,8 +639,8 @@ Usage: glasstrace mcp add [--force] [--dry-run]
|
|
|
538
639
|
`Unknown command: ${subcommand}
|
|
539
640
|
|
|
540
641
|
Usage:
|
|
541
|
-
glasstrace init [--yes] [--coverage-map]
|
|
542
|
-
glasstrace uninit [--dry-run]
|
|
642
|
+
glasstrace init [--yes] [--coverage-map] [--force] [--validate]
|
|
643
|
+
glasstrace uninit [--dry-run] [--force]
|
|
543
644
|
glasstrace status [--json]
|
|
544
645
|
glasstrace mcp add [--force] [--dry-run]
|
|
545
646
|
`
|
|
@@ -548,6 +649,7 @@ Usage:
|
|
|
548
649
|
}
|
|
549
650
|
}
|
|
550
651
|
export {
|
|
652
|
+
decideMcpConfigAction,
|
|
551
653
|
meetsNodeVersion,
|
|
552
654
|
rollbackSteps,
|
|
553
655
|
runInit
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/init.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n scaffoldMcpMarker,\n addCoverageMapEnv,\n} from \"./scaffolder.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, NEXT_CONFIG_NAMES, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\nimport {\n isInitCreatedInstrumentation,\n removeRegisterGlasstrace,\n unwrapExport,\n unwrapCJSExport,\n removeGlasstraceConfigImport,\n} from \"./uninit.js\";\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Identifies a scaffolding step that can be reversed during rollback.\n * Steps are tracked in execution order and rolled back in reverse.\n */\ntype CompletedStep = \"instrumentation\" | \"next-config\" | \"env-local\" | \"gitignore\";\n\n/**\n * Tracks state needed for accurate rollback of init steps.\n * Separating this from the step list allows rollback to restore\n * original file content rather than doing surgical removal.\n */\ninterface RollbackState {\n steps: CompletedStep[];\n /** Original instrumentation.ts content saved before injection.\n * When present, rollback restores this instead of using removeRegisterGlasstrace. */\n originalInstrumentationContent?: string;\n}\n\n/**\n * Removes leading blank lines that can appear after removing import lines.\n * Duplicated from uninit.ts to avoid exporting a trivial utility.\n */\nfunction cleanLeadingBlankLines(content: string): string {\n return content.replace(/^\\n{2,}/, \"\\n\");\n}\n\n/**\n * Best-effort rollback of completed init steps in reverse order.\n * Each step is individually try/caught so that a failure in one\n * rollback does not prevent the remaining steps from being attempted.\n *\n * @internal Exported for unit testing only.\n */\nexport async function rollbackSteps(\n steps: CompletedStep[],\n projectRoot: string,\n state?: Omit<RollbackState, \"steps\">,\n): Promise<void> {\n for (const step of [...steps].reverse()) {\n try {\n switch (step) {\n case \"instrumentation\": {\n const instrPath = path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n const content = fs.readFileSync(instrPath, \"utf-8\");\n if (isInitCreatedInstrumentation(content)) {\n fs.unlinkSync(instrPath);\n } else if (state?.originalInstrumentationContent !== undefined) {\n // Restore the exact original content to avoid removing\n // pre-existing imports that removeRegisterGlasstrace would strip.\n fs.writeFileSync(instrPath, state.originalInstrumentationContent, \"utf-8\");\n } else {\n const cleaned = removeRegisterGlasstrace(content);\n if (cleaned !== content) {\n fs.writeFileSync(instrPath, cleaned, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"next-config\": {\n for (const name of NEXT_CONFIG_NAMES) {\n const configPath = path.join(projectRoot, name);\n if (!fs.existsSync(configPath)) {\n continue;\n }\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"withGlasstraceConfig\")) {\n continue;\n }\n const isESM = name.endsWith(\".ts\") || name.endsWith(\".mjs\");\n const unwrapResult = isESM\n ? unwrapExport(content)\n : unwrapCJSExport(content);\n if (unwrapResult.unwrapped) {\n const cleaned = removeGlasstraceConfigImport(unwrapResult.content);\n fs.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), \"utf-8\");\n }\n break;\n }\n break;\n }\n case \"env-local\": {\n // Only remove GLASSTRACE_API_KEY lines — scaffoldEnvLocal (step 5)\n // only adds the API key. Removing GLASSTRACE_COVERAGE_MAP here would\n // delete a user's pre-existing coverage map setting if init fails\n // after step 5 but before the coverage map step.\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const content = fs.readFileSync(envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n return !/^\\s*#?\\s*GLASSTRACE_API_KEY\\s*=/.test(trimmed);\n });\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(envPath);\n } else {\n fs.writeFileSync(envPath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"gitignore\": {\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter(\n (line) => line.trim() !== \".glasstrace/\",\n );\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(gitignorePath);\n } else {\n fs.writeFileSync(gitignorePath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n }\n } catch {\n // Best-effort rollback — log nothing, continue with remaining steps\n }\n }\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Track completed steps so we can roll them back if a later step fails.\n // Only steps that modify the filesystem are tracked — pre-existing state\n // (e.g., \"already-registered\") is never rolled back.\n const rollbackState: RollbackState = { steps: [] };\n\n // Step 2: Ensure instrumentation.ts has registerGlasstrace()\n try {\n // Save original content before scaffolding modifies the file.\n // This allows rollback to restore exactly what was there, rather than\n // relying on surgical removal (which can strip pre-existing imports).\n const instrPath = path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(instrPath, \"utf-8\");\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot);\n switch (instrResult.action) {\n case \"created\":\n summary.push(\"Created instrumentation.ts\");\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(\"Added registerGlasstrace() to existing instrumentation.ts\");\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(\"Skipped instrumentation.ts (registerGlasstrace already present)\");\n break;\n case \"unrecognized\":\n warnings.push(\n \"instrumentation.ts exists but has no recognizable register() function.\\n\" +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to write instrumentation.ts: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n rollbackState.steps.push(\"next-config\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n try {\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n rollbackState.steps.push(\"env-local\");\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n rollbackState.steps.push(\"gitignore\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n const anonKey = await getOrCreateAnonKey(projectRoot);\n let anyConfigWritten = false;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if at least one config was successfully written.\n // Without this gate, a failed MCP setup would suppress future nudges,\n // leaving users stuck without MCP configuration.\n if (anyConfigWritten) {\n const markerCreated = await scaffoldMcpMarker(projectRoot, anonKey);\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"status\") {\n const remainingArgs = process.argv.slice(3);\n const json = remainingArgs.includes(\"--json\");\n\n Promise.all([import(\"./status.js\"), import(\"./monorepo.js\")])\n .then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {\n let projectRoot = process.cwd();\n try {\n projectRoot = resolve(projectRoot).projectRoot;\n } catch {\n // Fall back to cwd if monorepo resolution fails\n }\n const result = runStatus({ projectRoot });\n if (json) {\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n } else {\n const checks = [\n [\"Installed\", result.installed],\n [\"Initialized\", result.initialized],\n [\"Instrumentation\", result.instrumentation],\n [\"Config wrapped\", result.configWrapped],\n [\"Anon key\", result.anonKey],\n [\"MCP configured\", result.mcpConfigured],\n ] as const;\n for (const [label, ok] of checks) {\n process.stderr.write(` ${ok ? \"+\" : \"-\"} ${label}\\n`);\n }\n if (result.agents.length > 0) {\n process.stderr.write(` + Agents: ${result.agents.join(\", \")}\\n`);\n } else {\n process.stderr.write(\" - Agents\\n\");\n }\n }\n process.exit(0);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map]\\n\" +\n \" glasstrace uninit [--dry-run]\\n\" +\n \" glasstrace status [--json]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/init.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n scaffoldMcpMarker,\n addCoverageMapEnv,\n mcpConfigMatches,\n readEnvLocalApiKey,\n isDevApiKey,\n} from \"./scaffolder.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey, readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, NEXT_CONFIG_NAMES, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\nimport {\n isInitCreatedInstrumentation,\n removeRegisterGlasstrace,\n unwrapExport,\n unwrapCJSExport,\n removeGlasstraceConfigImport,\n} from \"./uninit.js\";\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n /**\n * When true, skip interactive confirmation and overwrite existing\n * MCP configuration files without prompting. Preservation of the\n * anonymous key, config cache, and developer API key still applies\n * regardless of this flag — `--force` only affects the MCP diff\n * prompt (DISC-1247 Scenario 2c). Defaults to `false`.\n */\n force?: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Decides whether the MCP config at `configPath` should be overwritten\n * during re-init. Returns the action to take.\n *\n * - `\"write\"` — file does not exist, or existing content already matches\n * the expected content. Safe to write.\n * - `\"skip\"` — existing file differs AND the user chose to keep it, or\n * we are in a non-interactive environment without `--force`.\n * - `\"force-overwrite\"` — `force === true` (or user accepted the prompt)\n * and content differs; overwrite.\n *\n * The prompt is skipped entirely when `force` is true (non-interactive\n * overwrite) or when there is no existing file / content already matches.\n *\n * @internal Exported for unit testing only.\n */\nexport async function decideMcpConfigAction(options: {\n configPath: string | null;\n expectedContent: string;\n force: boolean;\n readFile?: (p: string) => string;\n existsSync?: (p: string) => boolean;\n prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;\n}): Promise<\"write\" | \"skip\" | \"force-overwrite\"> {\n const { configPath, expectedContent, force } = options;\n if (configPath === null) return \"write\";\n\n const exists = options.existsSync ?? fs.existsSync;\n const read = options.readFile ?? ((p: string) => fs.readFileSync(p, \"utf-8\"));\n const prompt = options.prompt ?? promptYesNo;\n\n if (!exists(configPath)) return \"write\";\n\n let existingContent: string;\n try {\n existingContent = read(configPath);\n } catch {\n // Unreadable — treat as \"write\" since we can't assess drift.\n // This preserves the pre-hardening behavior for corrupt or\n // permission-restricted files.\n return \"write\";\n }\n\n if (mcpConfigMatches(existingContent, expectedContent)) {\n return \"write\";\n }\n\n if (force) {\n return \"force-overwrite\";\n }\n\n const answer = await prompt(\n `Existing MCP config at ${configPath} differs from Glasstrace's template. Overwrite?`,\n false,\n );\n return answer ? \"force-overwrite\" : \"skip\";\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Identifies a scaffolding step that can be reversed during rollback.\n * Steps are tracked in execution order and rolled back in reverse.\n */\ntype CompletedStep = \"instrumentation\" | \"next-config\" | \"env-local\" | \"gitignore\";\n\n/**\n * Tracks state needed for accurate rollback of init steps.\n * Separating this from the step list allows rollback to restore\n * original file content rather than doing surgical removal.\n */\ninterface RollbackState {\n steps: CompletedStep[];\n /** Original instrumentation.ts content saved before injection.\n * When present, rollback restores this instead of using removeRegisterGlasstrace. */\n originalInstrumentationContent?: string;\n}\n\n/**\n * Removes leading blank lines that can appear after removing import lines.\n * Duplicated from uninit.ts to avoid exporting a trivial utility.\n */\nfunction cleanLeadingBlankLines(content: string): string {\n return content.replace(/^\\n{2,}/, \"\\n\");\n}\n\n/**\n * Best-effort rollback of completed init steps in reverse order.\n * Each step is individually try/caught so that a failure in one\n * rollback does not prevent the remaining steps from being attempted.\n *\n * @internal Exported for unit testing only.\n */\nexport async function rollbackSteps(\n steps: CompletedStep[],\n projectRoot: string,\n state?: Omit<RollbackState, \"steps\">,\n): Promise<void> {\n for (const step of [...steps].reverse()) {\n try {\n switch (step) {\n case \"instrumentation\": {\n const instrPath = path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n const content = fs.readFileSync(instrPath, \"utf-8\");\n if (isInitCreatedInstrumentation(content)) {\n fs.unlinkSync(instrPath);\n } else if (state?.originalInstrumentationContent !== undefined) {\n // Restore the exact original content to avoid removing\n // pre-existing imports that removeRegisterGlasstrace would strip.\n fs.writeFileSync(instrPath, state.originalInstrumentationContent, \"utf-8\");\n } else {\n const cleaned = removeRegisterGlasstrace(content);\n if (cleaned !== content) {\n fs.writeFileSync(instrPath, cleaned, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"next-config\": {\n for (const name of NEXT_CONFIG_NAMES) {\n const configPath = path.join(projectRoot, name);\n if (!fs.existsSync(configPath)) {\n continue;\n }\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"withGlasstraceConfig\")) {\n continue;\n }\n const isESM = name.endsWith(\".ts\") || name.endsWith(\".mjs\");\n const unwrapResult = isESM\n ? unwrapExport(content)\n : unwrapCJSExport(content);\n if (unwrapResult.unwrapped) {\n const cleaned = removeGlasstraceConfigImport(unwrapResult.content);\n fs.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), \"utf-8\");\n }\n break;\n }\n break;\n }\n case \"env-local\": {\n // Only remove GLASSTRACE_API_KEY lines — scaffoldEnvLocal (step 5)\n // only adds the API key. Removing GLASSTRACE_COVERAGE_MAP here would\n // delete a user's pre-existing coverage map setting if init fails\n // after step 5 but before the coverage map step.\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const content = fs.readFileSync(envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n return !/^\\s*#?\\s*GLASSTRACE_API_KEY\\s*=/.test(trimmed);\n });\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(envPath);\n } else {\n fs.writeFileSync(envPath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"gitignore\": {\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter(\n (line) => line.trim() !== \".glasstrace/\",\n );\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(gitignorePath);\n } else {\n fs.writeFileSync(gitignorePath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n }\n } catch {\n // Best-effort rollback — log nothing, continue with remaining steps\n }\n }\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Track completed steps so we can roll them back if a later step fails.\n // Only steps that modify the filesystem are tracked — pre-existing state\n // (e.g., \"already-registered\") is never rolled back.\n const rollbackState: RollbackState = { steps: [] };\n\n // Step 2: Ensure instrumentation.ts has registerGlasstrace()\n try {\n // Save original content before scaffolding modifies the file.\n // This allows rollback to restore exactly what was there, rather than\n // relying on surgical removal (which can strip pre-existing imports).\n const instrPath = path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(instrPath, \"utf-8\");\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot);\n switch (instrResult.action) {\n case \"created\":\n summary.push(\"Created instrumentation.ts\");\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(\"Added registerGlasstrace() to existing instrumentation.ts\");\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(\"Skipped instrumentation.ts (registerGlasstrace already present)\");\n break;\n case \"unrecognized\":\n warnings.push(\n \"instrumentation.ts exists but has no recognizable register() function.\\n\" +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to write instrumentation.ts: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n rollbackState.steps.push(\"next-config\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n // DISC-1247 Scenario 6: if .env.local already defines a claimed\n // developer key (gt_dev_*), scaffoldEnvLocal preserves it and this\n // step reports the preservation so the user knows re-init did not\n // overwrite their claim.\n try {\n const envPathForCheck = path.join(projectRoot, \".env.local\");\n let existingDevKey = false;\n if (fs.existsSync(envPathForCheck)) {\n const existingContent = fs.readFileSync(envPathForCheck, \"utf-8\");\n existingDevKey = isDevApiKey(readEnvLocalApiKey(existingContent));\n }\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n rollbackState.steps.push(\"env-local\");\n } else if (existingDevKey) {\n summary.push(\n \"Preserved existing .env.local (GLASSTRACE_API_KEY contains a claimed dev key)\",\n );\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n rollbackState.steps.push(\"gitignore\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n // DISC-1247 Scenario 2a: preserve any existing anonymous key.\n // getOrCreateAnonKey already reads an existing key if present, so\n // re-running init never overwrites a key that may be linked to an\n // account. We explicitly check first so we can report the preservation\n // in the summary — without this, users have no feedback that re-init\n // respected their existing claim linkage.\n const preExistingAnonKey = await readAnonKey(projectRoot);\n const anonKey = await getOrCreateAnonKey(projectRoot);\n if (preExistingAnonKey !== null) {\n summary.push(\"Preserved existing .glasstrace/anon_key\");\n }\n let anyConfigWritten = false;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json.\n // CI uses `force: true` for MCP diff decisions because there's no\n // interactive terminal to prompt on — existing configs in CI\n // workspaces are rare and safe to overwrite.\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n const decision = await decideMcpConfigAction({\n configPath: genericAgent.mcpConfigPath,\n expectedContent: genericConfig,\n force: true,\n });\n if (decision !== \"skip\") {\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n }\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n\n // Diff-aware MCP write (DISC-1247 Scenario 2c): if the existing\n // config differs from what init would write, prompt before\n // overwriting. `--force` (or --yes in non-interactive mode)\n // skips the prompt.\n const decision = await decideMcpConfigAction({\n configPath: agent.mcpConfigPath,\n expectedContent: configContent,\n force: options.force === true || options.yes,\n });\n\n if (decision === \"skip\") {\n summary.push(\n `Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`,\n );\n if (agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath)) {\n // Count existing user-edited config as \"present\" so the\n // marker file still gets written — otherwise nudges would\n // nag the user about MCP setup they consciously preserved.\n anyConfigWritten = true;\n }\n continue;\n }\n\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if at least one config was successfully written.\n // Without this gate, a failed MCP setup would suppress future nudges,\n // leaving users stuck without MCP configuration.\n if (anyConfigWritten) {\n const markerCreated = await scaffoldMcpMarker(projectRoot, anonKey);\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n let force = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n } else if (arg === \"--force\") {\n force = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n force,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const forwardedArgs = process.argv.slice(subcommand === \"init\" ? 3 : 2);\n\n // `--validate` is an init sub-mode that checks artifact consistency\n // without scaffolding (DISC-1247 Scenario 4). We dispatch to a\n // dedicated module so the main init path stays unburdened.\n //\n // Resolve the app root via the same monorepo-aware logic that\n // `runInit` and `runStatus` use so validation in a monorepo root\n // inspects the actual Next.js app directory rather than the empty\n // workspace root (addresses Codex P2 review feedback).\n if (forwardedArgs.includes(\"--validate\")) {\n let validateProjectRoot = process.cwd();\n try {\n validateProjectRoot = resolveProjectRoot(validateProjectRoot).projectRoot;\n } catch {\n // Fall back to cwd if the monorepo resolver can't find an app —\n // validate can still report orphan-artifact issues at the raw\n // cwd and will exit non-zero rather than hiding the problem.\n }\n import(\"./validate.js\")\n .then(({ runValidate }) => runValidate({ projectRoot: validateProjectRoot }))\n .then((result) => {\n for (const line of result.summary) {\n process.stderr.write(`${line}\\n`);\n }\n for (const issue of result.issues) {\n process.stderr.write(` - ${issue.message}\\n`);\n if (issue.fix) {\n process.stderr.write(` Fix: ${issue.fix}\\n`);\n }\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n }\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n const force = remainingArgs.includes(\"--force\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun, force }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"status\") {\n const remainingArgs = process.argv.slice(3);\n const json = remainingArgs.includes(\"--json\");\n\n Promise.all([import(\"./status.js\"), import(\"./monorepo.js\")])\n .then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {\n let projectRoot = process.cwd();\n try {\n projectRoot = resolve(projectRoot).projectRoot;\n } catch {\n // Fall back to cwd if monorepo resolution fails\n }\n const result = runStatus({ projectRoot });\n if (json) {\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n } else {\n const checks = [\n [\"Installed\", result.installed],\n [\"Initialized\", result.initialized],\n [\"Instrumentation\", result.instrumentation],\n [\"Config wrapped\", result.configWrapped],\n [\"Anon key\", result.anonKey],\n [\"MCP configured\", result.mcpConfigured],\n ] as const;\n for (const [label, ok] of checks) {\n process.stderr.write(` ${ok ? \"+\" : \"-\"} ${label}\\n`);\n }\n if (result.agents.length > 0) {\n process.stderr.write(` + Agents: ${result.agents.join(\", \")}\\n`);\n } else {\n process.stderr.write(\" - Agents\\n\");\n }\n }\n process.exit(0);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map] [--force] [--validate]\\n\" +\n \" glasstrace uninit [--dry-run] [--force]\\n\" +\n \" glasstrace status [--json]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,cAAc;AAgCnB,SAAS,iBAAiB,UAA2B;AAC1D,QAAM,CAAC,KAAK,IAAI,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM;AAC3D,SAAO,SAAS;AAClB;AAyCA,eAAsB,sBAAsB,SAOM;AAChD,QAAM,EAAE,YAAY,iBAAiB,MAAM,IAAI;AAC/C,MAAI,eAAe,KAAM,QAAO;AAEhC,QAAM,SAAS,QAAQ,cAAiB;AACxC,QAAM,OAAO,QAAQ,aAAa,CAAC,MAAiB,gBAAa,GAAG,OAAO;AAC3E,QAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,CAAC,OAAO,UAAU,EAAG,QAAO;AAEhC,MAAI;AACJ,MAAI;AACF,sBAAkB,KAAK,UAAU;AAAA,EACnC,QAAQ;AAIN,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,iBAAiB,eAAe,GAAG;AACtD,WAAO;AAAA,EACT;AAEA,MAAI,OAAO;AACT,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB,0BAA0B,UAAU;AAAA,IACpC;AAAA,EACF;AACA,SAAO,SAAS,oBAAoB;AACtC;AAMA,eAAe,YAAY,UAAkB,cAAyC;AACpF,MAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,KAAc,yBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,SAAS,eAAe,YAAY;AAC1C,OAAG,SAAS,WAAW,QAAQ,CAAC,WAAW;AACzC,SAAG,MAAM;AACT,YAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAC1C,UAAI,YAAY,IAAI;AAClB,gBAAQ,YAAY;AACpB;AAAA,MACF;AACA,cAAQ,YAAY,OAAO,YAAY,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC;AACH;AAwBA,SAAS,uBAAuB,SAAyB;AACvD,SAAO,QAAQ,QAAQ,WAAW,IAAI;AACxC;AASA,eAAsB,cACpB,OACA,aACA,OACe;AACf,aAAW,QAAQ,CAAC,GAAG,KAAK,EAAE,QAAQ,GAAG;AACvC,QAAI;AACF,cAAQ,MAAM;AAAA,QACZ,KAAK,mBAAmB;AACtB,gBAAM,YAAiB,UAAK,aAAa,oBAAoB;AAC7D,cAAO,cAAW,SAAS,GAAG;AAC5B,kBAAM,UAAa,gBAAa,WAAW,OAAO;AAClD,gBAAI,6BAA6B,OAAO,GAAG;AACzC,cAAG,cAAW,SAAS;AAAA,YACzB,WAAW,OAAO,mCAAmC,QAAW;AAG9D,cAAG,iBAAc,WAAW,MAAM,gCAAgC,OAAO;AAAA,YAC3E,OAAO;AACL,oBAAM,UAAU,yBAAyB,OAAO;AAChD,kBAAI,YAAY,SAAS;AACvB,gBAAG,iBAAc,WAAW,SAAS,OAAO;AAAA,cAC9C;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,qBAAW,QAAQ,mBAAmB;AACpC,kBAAM,aAAkB,UAAK,aAAa,IAAI;AAC9C,gBAAI,CAAI,cAAW,UAAU,GAAG;AAC9B;AAAA,YACF;AACA,kBAAM,UAAa,gBAAa,YAAY,OAAO;AACnD,gBAAI,CAAC,QAAQ,SAAS,sBAAsB,GAAG;AAC7C;AAAA,YACF;AACA,kBAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,KAAK,SAAS,MAAM;AAC1D,kBAAM,eAAe,QACjB,aAAa,OAAO,IACpB,gBAAgB,OAAO;AAC3B,gBAAI,aAAa,WAAW;AAC1B,oBAAM,UAAU,6BAA6B,aAAa,OAAO;AACjE,cAAG,iBAAc,YAAY,uBAAuB,OAAO,GAAG,OAAO;AAAA,YACvE;AACA;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAKhB,gBAAM,UAAe,UAAK,aAAa,YAAY;AACnD,cAAO,cAAW,OAAO,GAAG;AAC1B,kBAAM,UAAa,gBAAa,SAAS,OAAO;AAChD,kBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,kBAAM,WAAW,MAAM,OAAO,CAAC,SAAS;AACtC,oBAAM,UAAU,KAAK,KAAK;AAC1B,qBAAO,CAAC,kCAAkC,KAAK,OAAO;AAAA,YACxD,CAAC;AACD,gBAAI,SAAS,WAAW,MAAM,QAAQ;AACpC,oBAAM,SAAS,SAAS,KAAK,IAAI;AACjC,kBAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,gBAAG,cAAW,OAAO;AAAA,cACvB,OAAO;AACL,gBAAG,iBAAc,SAAS,QAAQ,OAAO;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAChB,gBAAM,gBAAqB,UAAK,aAAa,YAAY;AACzD,cAAO,cAAW,aAAa,GAAG;AAChC,kBAAM,UAAa,gBAAa,eAAe,OAAO;AACtD,kBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,kBAAM,WAAW,MAAM;AAAA,cACrB,CAAC,SAAS,KAAK,KAAK,MAAM;AAAA,YAC5B;AACA,gBAAI,SAAS,WAAW,MAAM,QAAQ;AACpC,oBAAM,SAAS,SAAS,KAAK,IAAI;AACjC,kBAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,gBAAG,cAAW,aAAa;AAAA,cAC7B,OAAO;AACL,gBAAG,iBAAc,eAAe,QAAQ,OAAO;AAAA,cACjD;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAMA,eAAsB,QAAQ,SAA2C;AACvE,QAAM,EAAE,KAAK,YAAY,IAAI;AAC7B,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAC5B,QAAM,SAAmB,CAAC;AAG1B,MAAI;AACJ,MAAI;AACF,UAAM,iBAAiB,mBAAmB,QAAQ,WAAW;AAC7D,kBAAc,eAAe;AAC7B,QAAI,eAAe,cAAc,eAAe,iBAAiB;AAC/D,cAAQ,KAAK,wBAAwB,eAAe,eAAe,0BAAqB;AAAA,IAC1F;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC5D,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,QAAM,kBAAuB,UAAK,aAAa,cAAc;AAC7D,MAAI,CAAI,cAAW,eAAe,GAAG;AACnC,WAAO,KAAK,sEAAsE;AAClF,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAKA,QAAM,gBAA+B,EAAE,OAAO,CAAC,EAAE;AAGjD,MAAI;AAIF,UAAM,YAAiB,UAAK,aAAa,oBAAoB;AAC7D,QAAO,cAAW,SAAS,GAAG;AAC5B,oBAAc,iCAAoC,gBAAa,WAAW,OAAO;AAAA,IACnF;AAEA,UAAM,cAAc,MAAM,wBAAwB,WAAW;AAC7D,YAAQ,YAAY,QAAQ;AAAA,MAC1B,KAAK;AACH,gBAAQ,KAAK,4BAA4B;AACzC,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,2DAA2D;AACxE,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,iEAAiE;AAC9E;AAAA,MACF,KAAK;AACH,iBAAS;AAAA,UACP;AAAA,QAKF;AACA;AAAA,IACJ;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,uCAAuC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AACrG,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,MAAI;AACF,UAAM,eAAe,MAAM,mBAAmB,WAAW;AACzD,QAAI,cAAc,UAAU;AAC1B,cAAQ,KAAK,iDAAiD;AAC9D,oBAAc,MAAM,KAAK,aAAa;AAAA,IACxC,WAAW,iBAAiB,MAAM;AAChC,eAAS,KAAK,0EAA0E;AAAA,IAC1F,WAAW,aAAa,WAAW,mBAAmB;AACpD,cAAQ,KAAK,6DAA6D;AAAA,IAC5E,WAAW,aAAa,WAAW,cAAc;AAC/C,eAAS,KAAK,mFAA8E;AAAA,IAC9F,OAAO;AACL,eAAS,KAAK,2FAAsF;AAAA,IACtG;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC/F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAOA,MAAI;AACF,UAAM,kBAAuB,UAAK,aAAa,YAAY;AAC3D,QAAI,iBAAiB;AACrB,QAAO,cAAW,eAAe,GAAG;AAClC,YAAM,kBAAqB,gBAAa,iBAAiB,OAAO;AAChE,uBAAiB,YAAY,mBAAmB,eAAe,CAAC;AAAA,IAClE;AACA,UAAM,aAAa,MAAM,iBAAiB,WAAW;AACrD,QAAI,YAAY;AACd,cAAQ,KAAK,kDAAkD;AAC/D,oBAAc,MAAM,KAAK,WAAW;AAAA,IACtC,WAAW,gBAAgB;AACzB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF,OAAO;AACL,cAAQ,KAAK,4DAA4D;AAAA,IAC3E;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC9F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,MAAI;AACF,UAAM,mBAAmB,MAAM,kBAAkB,WAAW;AAC5D,QAAI,kBAAkB;AACpB,cAAQ,KAAK,sCAAsC;AACnD,oBAAc,MAAM,KAAK,WAAW;AAAA,IACtC,OAAO;AACL,cAAQ,KAAK,kDAAkD;AAAA,IACjE;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC9F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAQA,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,QAAM,OACH,OAAO,UAAU,YAChB,MAAM,KAAK,MAAM,MACjB,MAAM,YAAY,MAAM,WACxB,MAAM,KAAK,MAAM,OACnB,QAAQ,IAAI,gBAAgB,MAAM;AAEpC,MAAI;AAOF,UAAM,qBAAqB,MAAM,YAAY,WAAW;AACxD,UAAM,UAAU,MAAM,mBAAmB,WAAW;AACpD,QAAI,uBAAuB,MAAM;AAC/B,cAAQ,KAAK,yCAAyC;AAAA,IACxD;AACA,QAAI,mBAAmB;AAEvB,QAAI,MAAM;AAKR,YAAM,eAA8B;AAAA,QAClC,MAAM;AAAA,QACN,eAAoB,UAAK,aAAa,eAAe,UAAU;AAAA,QAC/D,cAAc;AAAA,QACd,cAAc;AAAA,QACd,qBAAqB;AAAA,MACvB;AACA,YAAM,gBAAgB,kBAAkB,cAAc,cAAc,OAAO;AAC3E,YAAM,WAAW,MAAM,sBAAsB;AAAA,QAC3C,YAAY,aAAa;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO;AAAA,MACT,CAAC;AACD,UAAI,aAAa,QAAQ;AACvB,cAAM,eAAe,cAAc,eAAe,WAAW;AAAA,MAC/D;AACA,UAAI,aAAa,kBAAkB,QAAW,cAAW,aAAa,aAAa,GAAG;AACpF,2BAAmB;AACnB,gBAAQ,KAAK,wCAAwC;AAAA,MACvD;AAAA,IACF,OAAO;AAEL,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,aAAa,WAAW;AAAA,MACzC,SAAS,WAAW;AAClB,iBAAS;AAAA,UACP,2BAA2B,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,QAC/F;AAEA,cAAM,eAA8B;AAAA,UAClC,MAAM;AAAA,UACN,eAAoB,UAAK,aAAa,eAAe,UAAU;AAAA,UAC/D,cAAc;AAAA,UACd,cAAc;AAAA,UACd,qBAAqB;AAAA,QACvB;AACA,cAAM,gBAAgB,kBAAkB,cAAc,cAAc,OAAO;AAC3E,cAAM,eAAe,cAAc,eAAe,WAAW;AAC7D,YAAI,aAAa,kBAAkB,QAAW,cAAW,aAAa,aAAa,GAAG;AACpF,6BAAmB;AAAA,QACrB;AACA,iBAAS,CAAC;AAAA,MACZ;AAEA,YAAM,kBAA4B,CAAC;AAEnC,iBAAW,SAAS,QAAQ;AAC1B,YAAI;AACF,gBAAM,gBAAgB,kBAAkB,OAAO,cAAc,OAAO;AAMpE,gBAAM,WAAW,MAAM,sBAAsB;AAAA,YAC3C,YAAY,MAAM;AAAA,YAClB,iBAAiB;AAAA,YACjB,OAAO,QAAQ,UAAU,QAAQ,QAAQ;AAAA,UAC3C,CAAC;AAED,cAAI,aAAa,QAAQ;AACvB,oBAAQ;AAAA,cACN,sBAAsB,MAAM,iBAAiB,MAAM,IAAI;AAAA,YACzD;AACA,gBAAI,MAAM,kBAAkB,QAAW,cAAW,MAAM,aAAa,GAAG;AAItE,iCAAmB;AAAA,YACrB;AACA;AAAA,UACF;AAEA,gBAAM,eAAe,OAAO,eAAe,WAAW;AAItD,gBAAM,eAAe,MAAM,kBAAkB,QAAW,cAAW,MAAM,aAAa;AACtF,cAAI,CAAC,cAAc;AACjB;AAAA,UACF;AAEA,6BAAmB;AAEnB,gBAAM,cAAc,oBAAoB,OAAO,YAAY;AAC3D,cAAI,gBAAgB,IAAI;AACtB,kBAAM,kBAAkB,OAAO,aAAa,WAAW;AAAA,UACzD;AAEA,cAAI,MAAM,SAAS,WAAW;AAC5B,4BAAgB,KAAK,gBAAgB,MAAM,IAAI,CAAC;AAAA,UAClD;AAAA,QACF,SAAS,UAAU;AACjB,mBAAS;AAAA,YACP,+BAA+B,MAAM,IAAI,KAAK,oBAAoB,QAAQ,SAAS,UAAU,OAAO,QAAQ,CAAC;AAAA,UAC/G;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAQ,KAAK,uBAAuB,gBAAgB,KAAK,IAAI,CAAC,EAAE;AAAA,MAClE,WAAW,kBAAkB;AAC3B,gBAAQ,KAAK,+CAA+C;AAAA,MAC9D;AAAA,IACF;AAGA,UAAM;AAAA,MACJ,CAAC,aAAa,oBAAoB,yBAAyB,oBAAoB;AAAA,MAC/E;AAAA,IACF;AAKA,QAAI,kBAAkB;AACpB,YAAM,gBAAgB,MAAM,kBAAkB,aAAa,OAAO;AAClE,UAAI,eAAe;AACjB,gBAAQ,KAAK,0CAA0C;AAAA,MACzD;AAAA,IACF;AAAA,EACF,SAAS,QAAQ;AACf,aAAS;AAAA,MACP,kCAAkC,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IAC7F;AAAA,EACF;AAGA,MAAI,oBAAoB;AACxB,MAAI,CAAC,OAAO,CAAC,aAAa;AACxB,QAAI,QAAQ,MAAM,OAAO;AACvB,0BAAoB,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB;AACrB,QAAI;AACF,YAAM,QAAQ,MAAM,kBAAkB,WAAW;AACjD,UAAI,OAAO;AACT,gBAAQ,KAAK,kDAAkD;AAAA,MACjE;AAAA,IACF,SAAS,KAAK;AACZ,eAAS,KAAK,mCAAmC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IACrG;AAGA,QAAI;AACF,YAAM,iBAAiB,WAAW;AAClC,cAAQ,KAAK,qCAAqC;AAAA,IACpD,SAAS,KAAK;AACZ,eAAS,KAAK,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,yBAAyB;AAAA,IACtH;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAClD;AAKA,SAAS,UAAU,MAA6B;AAC9C,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI,MAAM;AACV,MAAI,cAAc;AAClB,MAAI,QAAQ;AAEZ,aAAW,OAAO,MAAM;AACtB,QAAI,QAAQ,WAAW,QAAQ,MAAM;AACnC,YAAM;AAAA,IACR,WAAW,QAAQ,kBAAkB;AACnC,oBAAc;AAAA,IAChB,WAAW,QAAQ,WAAW;AAC5B,cAAQ;AAAA,IACV;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,UAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,aAAa,QAAQ,IAAI;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,IAAM,aACJ,OAAO,YAAY,eAAe,QAAQ,KAAK,CAAC,MAAM,SAClD,QAAQ,KAAK,CAAC,EAAE,QAAQ,OAAO,GAAG,IAClC;AAEN,IAAM,iBAAiB,eAAe,SAAiB,cAAS,UAAU,IAAI;AAE9E,IAAM,oBACJ,eAAe,WACd,WAAW,SAAS,cAAc,KACjC,WAAW,SAAS,cAAc,KAClC,mBAAmB;AAEvB,IAAI,mBAAmB;AAIrB,MAAI,CAAC,iBAAiB,EAAE,GAAG;AACzB,YAAQ,OAAO;AAAA,MACb,mEAAmE,QAAQ,OAAO;AAAA;AAAA,IACpF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,QAAQ,KAAK,CAAC;AAEjC,MAAI,eAAe,OAAO;AACxB,QAAI,QAAQ,KAAK,CAAC,MAAM,OAAO;AAE7B,YAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,YAAM,QAAQ,cAAc,SAAS,SAAS;AAC9C,YAAM,SAAS,cAAc,SAAS,WAAW;AAEjD,aAAO,cAAc,EAClB,KAAK,CAAC,EAAE,OAAO,MAAM,OAAO,EAAE,OAAO,OAAO,CAAC,CAAC,EAC9C,KAAK,CAAC,WAAW;AAChB,mBAAW,OAAO,OAAO,UAAU;AACjC,kBAAQ,OAAO,MAAM,MAAM,IAAI;AAAA,QACjC;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL,OAAO;AACL,cAAQ,OAAO;AAAA,QACb,2BAA2B,QAAQ,KAAK,CAAC,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA,MAExD;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,WAAW,eAAe,UAAa,eAAe,UAAU,WAAW,WAAW,GAAG,GAAG;AAE1F,UAAM,gBAAgB,QAAQ,KAAK,MAAM,eAAe,SAAS,IAAI,CAAC;AAUtE,QAAI,cAAc,SAAS,YAAY,GAAG;AACxC,UAAI,sBAAsB,QAAQ,IAAI;AACtC,UAAI;AACF,8BAAsB,mBAAmB,mBAAmB,EAAE;AAAA,MAChE,QAAQ;AAAA,MAIR;AACA,aAAO,eAAe,EACnB,KAAK,CAAC,EAAE,YAAY,MAAM,YAAY,EAAE,aAAa,oBAAoB,CAAC,CAAC,EAC3E,KAAK,CAAC,WAAW;AAChB,mBAAW,QAAQ,OAAO,SAAS;AACjC,kBAAQ,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,QAClC;AACA,mBAAW,SAAS,OAAO,QAAQ;AACjC,kBAAQ,OAAO,MAAM,OAAO,MAAM,OAAO;AAAA,CAAI;AAC7C,cAAI,MAAM,KAAK;AACb,oBAAQ,OAAO,MAAM,cAAc,MAAM,GAAG;AAAA,CAAI;AAAA,UAClD;AAAA,QACF;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL,OAAO;AACL,YAAM,UAAU,UAAU,QAAQ,IAAI;AAEtC,cAAQ,OAAO,EACZ,KAAK,CAAC,WAAW;AAChB,YAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,qBAAW,OAAO,OAAO,QAAQ;AAC/B,oBAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AAAA,UACxC;AAAA,QACF;AACA,YAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,qBAAW,QAAQ,OAAO,UAAU;AAClC,oBAAQ,OAAO,MAAM,YAAY,IAAI;AAAA,CAAI;AAAA,UAC3C;AAAA,QACF;AACA,YAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,kBAAQ,OAAO,MAAM,4CAA4C;AACjE,qBAAW,QAAQ,OAAO,SAAS;AACjC,oBAAQ,OAAO,MAAM,OAAO,IAAI;AAAA,CAAI;AAAA,UACtC;AACA,kBAAQ,OAAO,MAAM,iBAAiB;AACtC,kBAAQ,OAAO,MAAM,sCAAsC;AAC3D,kBAAQ,OAAO;AAAA,YACb;AAAA,UACF;AACA,kBAAQ,OAAO;AAAA,YACb;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL;AAAA,EACF,WAAW,eAAe,UAAU;AAClC,UAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,UAAM,SAAS,cAAc,SAAS,WAAW;AACjD,UAAM,QAAQ,cAAc,SAAS,SAAS;AAE9C,WAAO,aAAa,EACjB,KAAK,CAAC,EAAE,UAAU,MAAM,UAAU,EAAE,aAAa,QAAQ,IAAI,GAAG,QAAQ,MAAM,CAAC,CAAC,EAChF,KAAK,CAAC,WAAW;AAChB,UAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,mBAAW,OAAO,OAAO,QAAQ;AAC/B,kBAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AAAA,QACxC;AAAA,MACF;AACA,UAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,mBAAW,QAAQ,OAAO,UAAU;AAClC,kBAAQ,OAAO,MAAM,YAAY,IAAI;AAAA,CAAI;AAAA,QAC3C;AAAA,MACF;AACA,UAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,gBAAQ,OAAO,MAAM,IAAI;AACzB,mBAAW,QAAQ,OAAO,SAAS;AACjC,kBAAQ,OAAO,MAAM,KAAK,IAAI;AAAA,CAAI;AAAA,QACpC;AACA,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AACA,cAAQ,KAAK,OAAO,QAAQ;AAAA,IAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,cAAQ,OAAO;AAAA,QACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACL,WAAW,eAAe,UAAU;AAClC,UAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,UAAM,OAAO,cAAc,SAAS,QAAQ;AAE5C,YAAQ,IAAI,CAAC,OAAO,aAAa,GAAG,OAAO,yBAAe,CAAC,CAAC,EACzD,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,EAAE,oBAAoB,QAAQ,CAAC,MAAM;AAC1D,UAAI,cAAc,QAAQ,IAAI;AAC9B,UAAI;AACF,sBAAc,QAAQ,WAAW,EAAE;AAAA,MACrC,QAAQ;AAAA,MAER;AACA,YAAM,SAAS,UAAU,EAAE,YAAY,CAAC;AACxC,UAAI,MAAM;AACR,gBAAQ,OAAO,MAAM,KAAK,UAAU,MAAM,IAAI,IAAI;AAAA,MACpD,OAAO;AACL,cAAM,SAAS;AAAA,UACb,CAAC,aAAa,OAAO,SAAS;AAAA,UAC9B,CAAC,eAAe,OAAO,WAAW;AAAA,UAClC,CAAC,mBAAmB,OAAO,eAAe;AAAA,UAC1C,CAAC,kBAAkB,OAAO,aAAa;AAAA,UACvC,CAAC,YAAY,OAAO,OAAO;AAAA,UAC3B,CAAC,kBAAkB,OAAO,aAAa;AAAA,QACzC;AACA,mBAAW,CAAC,OAAO,EAAE,KAAK,QAAQ;AAChC,kBAAQ,OAAO,MAAM,KAAK,KAAK,MAAM,GAAG,IAAI,KAAK;AAAA,CAAI;AAAA,QACvD;AACA,YAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,kBAAQ,OAAO,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,QAClE,OAAO;AACL,kBAAQ,OAAO,MAAM,cAAc;AAAA,QACrC;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,cAAQ,OAAO;AAAA,QACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACL,OAAO;AACL,YAAQ,OAAO;AAAA,MACb,oBAAoB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|