@glasstrace/sdk 1.1.0 → 1.1.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-KE7MCPO5.js → chunk-4EZ6JTDG.js} +2 -2
- package/dist/{chunk-67RIOAXV.js → chunk-6RNBUUBR.js} +2 -2
- package/dist/{chunk-DXRZKKSO.js → chunk-7SZQN6IU.js} +1 -3
- package/dist/chunk-7SZQN6IU.js.map +1 -0
- package/dist/{chunk-55FBXXER.js → chunk-DIM4JRXM.js} +2 -2
- package/dist/{chunk-UGJ3X4CT.js → chunk-DST4UBXU.js} +2 -2
- package/dist/{chunk-DO2YPMQ5.js → chunk-MXDZHFJQ.js} +23 -5
- package/dist/chunk-MXDZHFJQ.js.map +1 -0
- package/dist/chunk-P22UQ2OJ.js +384 -0
- package/dist/chunk-P22UQ2OJ.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-TQ54WLCZ.js → chunk-X5MAXP5T.js} +2 -1
- package/dist/{chunk-ZBTC5QIQ.js → chunk-Y26HJUPD.js} +9 -9
- package/dist/{chunk-LU3PPAOQ.js → chunk-ZRDQ6ZKI.js} +474 -93
- package/dist/chunk-ZRDQ6ZKI.js.map +1 -0
- package/dist/cli/init.cjs +1118 -946
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +42 -29
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +243 -83
- 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 +48 -24
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.js +3 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/uninit.cjs +4 -4
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.js +4 -4
- package/dist/edge-entry.js +2 -2
- package/dist/index.cjs +293 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/{monorepo-N5Z63XP7.js → monorepo-GSL6JD3G.js} +5 -3
- package/dist/node-entry.cjs +293 -11
- 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-KE7MCPO5.js.map → chunk-4EZ6JTDG.js.map} +0 -0
- /package/dist/{chunk-67RIOAXV.js.map → chunk-6RNBUUBR.js.map} +0 -0
- /package/dist/{chunk-55FBXXER.js.map → chunk-DIM4JRXM.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/{chunk-ZBTC5QIQ.js.map → chunk-Y26HJUPD.js.map} +0 -0
- /package/dist/{monorepo-N5Z63XP7.js.map → monorepo-GSL6JD3G.js.map} +0 -0
- /package/dist/{source-map-uploader-BJIXRLJ6.js.map → source-map-uploader-DPUUCLNW.js.map} +0 -0
package/dist/cli/init.js
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveProjectRoot
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-DIM4JRXM.js";
|
|
5
5
|
import {
|
|
6
6
|
verifyInitReachable
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-MXDZHFJQ.js";
|
|
8
8
|
import {
|
|
9
9
|
buildImportGraph
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-DST4UBXU.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveConfig
|
|
13
13
|
} from "../chunk-VUZCLMIX.js";
|
|
14
14
|
import {
|
|
15
|
+
addCoverageMapEnv,
|
|
15
16
|
isInitCreatedInstrumentation,
|
|
16
17
|
relativeDiscoveryPath,
|
|
17
18
|
removeDiscoveryFile,
|
|
18
19
|
removeGlasstraceConfigImport,
|
|
19
20
|
removeRegisterGlasstrace,
|
|
21
|
+
resolveInstrumentationTarget,
|
|
20
22
|
resolveStaticRoot,
|
|
23
|
+
scaffoldEnvLocal,
|
|
24
|
+
scaffoldGitignore,
|
|
25
|
+
scaffoldInstrumentation,
|
|
26
|
+
scaffoldNextConfig,
|
|
21
27
|
unwrapCJSExport,
|
|
22
28
|
unwrapExport,
|
|
23
29
|
writeDiscoveryFile
|
|
24
|
-
} from "../chunk-
|
|
30
|
+
} from "../chunk-ZRDQ6ZKI.js";
|
|
25
31
|
import {
|
|
26
32
|
detectAgents,
|
|
27
33
|
generateInfoSection,
|
|
@@ -29,29 +35,23 @@ import {
|
|
|
29
35
|
injectInfoSection,
|
|
30
36
|
updateGitignore,
|
|
31
37
|
writeMcpConfig
|
|
32
|
-
} from "../chunk-
|
|
38
|
+
} from "../chunk-P4OYPFQ5.js";
|
|
33
39
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} from "../chunk-
|
|
40
|
+
NEXT_CONFIG_NAMES,
|
|
41
|
+
formatAgentName
|
|
42
|
+
} from "../chunk-7SZQN6IU.js";
|
|
37
43
|
import {
|
|
38
|
-
|
|
44
|
+
MCP_ENDPOINT,
|
|
45
|
+
getOrCreateAnonKey,
|
|
46
|
+
identityFingerprint,
|
|
39
47
|
isDevApiKey,
|
|
40
48
|
mcpConfigMatches,
|
|
49
|
+
readAnonKey,
|
|
41
50
|
readEnvLocalApiKey,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
scaffoldMcpMarker,
|
|
47
|
-
scaffoldNextConfig
|
|
48
|
-
} from "../chunk-O63DJKIJ.js";
|
|
49
|
-
import "../chunk-TQ54WLCZ.js";
|
|
50
|
-
import {
|
|
51
|
-
MCP_ENDPOINT,
|
|
52
|
-
NEXT_CONFIG_NAMES,
|
|
53
|
-
formatAgentName
|
|
54
|
-
} from "../chunk-DXRZKKSO.js";
|
|
51
|
+
resolveEffectiveMcpCredential,
|
|
52
|
+
writeMcpMarker
|
|
53
|
+
} from "../chunk-P22UQ2OJ.js";
|
|
54
|
+
import "../chunk-X5MAXP5T.js";
|
|
55
55
|
import "../chunk-NSBPE2FW.js";
|
|
56
56
|
|
|
57
57
|
// src/cli/init.ts
|
|
@@ -450,6 +450,9 @@ Then add this as the first statement in your register() function:
|
|
|
450
450
|
);
|
|
451
451
|
}
|
|
452
452
|
let anyConfigWritten = false;
|
|
453
|
+
let anyConfigRewrittenWithBearer = false;
|
|
454
|
+
const resolved = await resolveEffectiveMcpCredential(projectRoot);
|
|
455
|
+
const bearer = resolved.effective?.key ?? anonKey;
|
|
453
456
|
if (isCI) {
|
|
454
457
|
const genericAgent = {
|
|
455
458
|
name: "generic",
|
|
@@ -458,7 +461,7 @@ Then add this as the first statement in your register() function:
|
|
|
458
461
|
cliAvailable: false,
|
|
459
462
|
registrationCommand: null
|
|
460
463
|
};
|
|
461
|
-
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT,
|
|
464
|
+
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
|
|
462
465
|
const decision = await decideMcpConfigAction({
|
|
463
466
|
configPath: genericAgent.mcpConfigPath,
|
|
464
467
|
expectedContent: genericConfig,
|
|
@@ -466,6 +469,9 @@ Then add this as the first statement in your register() function:
|
|
|
466
469
|
});
|
|
467
470
|
if (decision !== "skip") {
|
|
468
471
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
472
|
+
if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
|
|
473
|
+
anyConfigRewrittenWithBearer = true;
|
|
474
|
+
}
|
|
469
475
|
}
|
|
470
476
|
if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
|
|
471
477
|
anyConfigWritten = true;
|
|
@@ -486,17 +492,18 @@ Then add this as the first statement in your register() function:
|
|
|
486
492
|
cliAvailable: false,
|
|
487
493
|
registrationCommand: null
|
|
488
494
|
};
|
|
489
|
-
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT,
|
|
495
|
+
const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);
|
|
490
496
|
await writeMcpConfig(genericAgent, genericConfig, projectRoot);
|
|
491
497
|
if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {
|
|
492
498
|
anyConfigWritten = true;
|
|
499
|
+
anyConfigRewrittenWithBearer = true;
|
|
493
500
|
}
|
|
494
501
|
agents = [];
|
|
495
502
|
}
|
|
496
503
|
const configuredNames = [];
|
|
497
504
|
for (const agent of agents) {
|
|
498
505
|
try {
|
|
499
|
-
const configContent = generateMcpConfig(agent, MCP_ENDPOINT,
|
|
506
|
+
const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);
|
|
500
507
|
const decision = await decideMcpConfigAction({
|
|
501
508
|
configPath: agent.mcpConfigPath,
|
|
502
509
|
expectedContent: configContent,
|
|
@@ -517,6 +524,7 @@ Then add this as the first statement in your register() function:
|
|
|
517
524
|
continue;
|
|
518
525
|
}
|
|
519
526
|
anyConfigWritten = true;
|
|
527
|
+
anyConfigRewrittenWithBearer = true;
|
|
520
528
|
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
521
529
|
if (infoContent !== "") {
|
|
522
530
|
await injectInfoSection(agent, infoContent, projectRoot);
|
|
@@ -540,8 +548,13 @@ Then add this as the first statement in your register() function:
|
|
|
540
548
|
[".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".codex/config.toml"],
|
|
541
549
|
projectRoot
|
|
542
550
|
);
|
|
543
|
-
if (
|
|
544
|
-
const
|
|
551
|
+
if (anyConfigRewrittenWithBearer) {
|
|
552
|
+
const markerSource = resolved.effective?.source ?? "anon";
|
|
553
|
+
const markerHash = identityFingerprint(bearer);
|
|
554
|
+
const markerCreated = await writeMcpMarker(projectRoot, {
|
|
555
|
+
credentialSource: markerSource,
|
|
556
|
+
credentialHash: markerHash
|
|
557
|
+
});
|
|
545
558
|
if (markerCreated) {
|
|
546
559
|
summary.push("Created .glasstrace/mcp-connected marker");
|
|
547
560
|
}
|
|
@@ -610,7 +623,7 @@ async function verifyAnonKeyRegistration(projectRoot) {
|
|
|
610
623
|
}
|
|
611
624
|
const baseConfig = resolveConfig({ apiKey: devKey });
|
|
612
625
|
const config = { ...baseConfig, apiKey: devKey };
|
|
613
|
-
const sdkVersion = true ? "1.1.
|
|
626
|
+
const sdkVersion = true ? "1.1.1" : "0.0.0-dev";
|
|
614
627
|
const result = await verifyInitReachable(config, anonKey, sdkVersion);
|
|
615
628
|
if (result.ok) {
|
|
616
629
|
return { outcome: "verified" };
|
|
@@ -806,7 +819,7 @@ Usage: glasstrace mcp add [--force] [--dry-run]
|
|
|
806
819
|
} else if (subcommand === "status") {
|
|
807
820
|
const remainingArgs = process.argv.slice(3);
|
|
808
821
|
const json = remainingArgs.includes("--json");
|
|
809
|
-
Promise.all([import("./status.js"), import("../monorepo-
|
|
822
|
+
Promise.all([import("./status.js"), import("../monorepo-GSL6JD3G.js")]).then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {
|
|
810
823
|
let projectRoot = process.cwd();
|
|
811
824
|
try {
|
|
812
825
|
projectRoot = resolve(projectRoot).projectRoot;
|
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 mcpConfigMatches,\n readEnvLocalApiKey,\n isDevApiKey,\n resolveInstrumentationTarget,\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\";\nimport { verifyInitReachable, type VerifyInitResult } from \"../init-client.js\";\nimport { resolveConfig } from \"../env-detection.js\";\nimport {\n writeDiscoveryFile,\n removeDiscoveryFile,\n relativeDiscoveryPath,\n resolveStaticRoot,\n} from \"./discovery-file.js\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when\n// running tests under vitest (no tsup build step).\ndeclare const __SDK_VERSION__: string;\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 =\n | \"instrumentation\"\n | \"next-config\"\n | \"env-local\"\n | \"gitignore\"\n | \"discovery-file\";\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 /**\n * Absolute path of the instrumentation file that the scaffolder\n * wrote to. May be either `{root}/instrumentation.ts` or\n * `{root}/src/instrumentation.ts` depending on the project layout\n * (DISC-493 Issue 1). When absent, rollback falls back to the\n * root path for backward compatibility with callers that do not\n * populate this field.\n */\n instrumentationPath?: string;\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 * Returns true when the given `.gitignore` content would exclude the\n * static discovery file at `<root>/public/.well-known/glasstrace.json`\n * or `<root>/static/.well-known/glasstrace.json` (depending on `layout`)\n * from being committed.\n *\n * Model: the file has three ancestors in its committed path — the\n * static root directory (`public/` or `static/`), the `.well-known/`\n * sub-directory, and the file itself. Each pattern in `.gitignore` is\n * classified by which ancestor paths it matches, and each ancestor\n * carries its own current ignore state that later patterns can flip.\n * Patterns that don't match any of the three ancestors are ignored.\n *\n * Per `gitignore(5)`:\n *\n * > It is not possible to re-include a file if a parent directory of\n * > that file is excluded.\n *\n * Consequently, the file is reported as ignored when the final state\n * of ANY ancestor (static root, `.well-known/`, or the file itself)\n * is ignored — a `!<file>` negation alone cannot \"escape\" an ignored\n * ancestor.\n *\n * Per-ancestor tracking is what distinguishes overlapping patterns:\n *\n * - `public/` then `!public/` — root re-included, file OK.\n * - `public/` then `!public/.well-known/` — root still ignored (scope-2\n * negation doesn't match the scope-1 ancestor path), file ignored.\n * - `.well-known/` then `!public/.well-known/` — `.well-known/` re-included\n * (both patterns match the scope-2 ancestor), file OK.\n * - `.well-known/` then `!public/` — `!public/` matches the\n * scope-1 ancestor only; the scope-2 ancestor (`.well-known/`) is still\n * ignored, so the file is ignored.\n * - `<file>` then `!<file>` — file-level ignore flipped.\n * - `<file>` then `!public/.well-known/` — directory negation does\n * not match the file path; file still ignored.\n *\n * Not-modeled rules — glob wildcards (star, question mark, character\n * classes), overlapping any-depth matches across multiple parents, and\n * nested `.gitignore` files — skew the heuristic toward false positives\n * (extra warning output) rather than false negatives. The warning is\n * advisory.\n *\n * @internal Exported for unit testing only.\n */\nexport function gitignoreExcludesDiscoveryFile(\n gitignoreContent: string,\n layout: \"public\" | \"static\",\n): boolean {\n const staticRoot = layout === \"static\" ? \"static\" : \"public\";\n\n // Per-ancestor target sets. `matchesDiscoveryPath` handles trailing\n // slash and leading `**/` shapes on top of these exact paths.\n const rootTargets = [staticRoot, `${staticRoot}/`];\n const wellKnownTargets = [\n `${staticRoot}/.well-known`,\n `${staticRoot}/.well-known/`,\n \".well-known\",\n \".well-known/\",\n ];\n const fileTargets = [\n `${staticRoot}/.well-known/glasstrace.json`,\n \".well-known/glasstrace.json\",\n ];\n\n // Current ignore state of each ancestor path. Later patterns flip\n // these independently because a pattern only affects ancestors it\n // actually matches.\n let rootIgnored = false;\n let wellKnownIgnored = false;\n let fileIgnored = false;\n\n for (const rawLine of gitignoreContent.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line === \"\" || line.startsWith(\"#\")) continue;\n\n const negation = line.startsWith(\"!\");\n const pattern = negation ? line.slice(1).trim() : line;\n if (pattern === \"\") continue;\n\n // Normalize: strip a leading slash (anchors to root in git's grammar;\n // for our purposes the patterns we check are all root-relative).\n const normalized = pattern.startsWith(\"/\") ? pattern.slice(1) : pattern;\n\n const matchesRoot = matchesDiscoveryPath(normalized, rootTargets);\n const matchesWellKnown = matchesDiscoveryPath(normalized, wellKnownTargets);\n const matchesFile = matchesDiscoveryPath(normalized, fileTargets);\n\n if (!matchesRoot && !matchesWellKnown && !matchesFile) continue;\n\n const newState = !negation;\n if (matchesRoot) rootIgnored = newState;\n if (matchesWellKnown) wellKnownIgnored = newState;\n if (matchesFile) fileIgnored = newState;\n }\n\n // A file is ignored whenever any ancestor's final state is ignored;\n // gitignore(5) does not permit re-including a file below an ignored\n // parent with a file-level `!<path>` pattern alone.\n return rootIgnored || wellKnownIgnored || fileIgnored;\n}\n\n/**\n * Returns true when the (normalized) gitignore pattern matches any of the\n * discovery-file-relevant paths. Supports three common pattern shapes:\n *\n * - Exact path: e.g. `public/.well-known/glasstrace.json`\n * - Directory: e.g. `.well-known/` or `public/.well-known`\n * - Leading `**\\/`: e.g. `**\\/.well-known/` (any-depth wildcard)\n *\n * Complex glob patterns (`*`, `?`, character classes) are not modeled;\n * the warning is advisory and false negatives are acceptable.\n */\nfunction matchesDiscoveryPath(\n pattern: string,\n targets: string[],\n): boolean {\n // Strip trailing slash — both `.well-known` and `.well-known/` should\n // match a directory-style target.\n const bare = pattern.endsWith(\"/\") ? pattern.slice(0, -1) : pattern;\n\n for (const target of targets) {\n const tBare = target.endsWith(\"/\") ? target.slice(0, -1) : target;\n if (bare === tBare) return true;\n // `**/X` matches any-depth occurrence of X\n if (pattern.startsWith(\"**/\")) {\n const suffix = pattern.slice(3);\n const sBare = suffix.endsWith(\"/\") ? suffix.slice(0, -1) : suffix;\n if (tBare === sBare || tBare.endsWith(`/${sBare}`)) return true;\n }\n }\n return false;\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 // Prefer the exact path the scaffolder wrote to — the resolver\n // may have chosen `src/instrumentation.ts` on Next.js `src/`\n // layouts (DISC-493 Issue 1). Fall back to the root path for\n // callers that do not populate `instrumentationPath`.\n const instrPath =\n state?.instrumentationPath ?? 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 case \"discovery-file\": {\n // Remove the static discovery file scaffolded in Step 7. The\n // removeDiscoveryFile helper also best-effort removes an empty\n // `.well-known/` directory but never touches sibling content.\n removeDiscoveryFile(projectRoot);\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 the instrumentation file has registerGlasstrace().\n // DISC-493 Issue 1: detect `src/` layout and merge into the existing file\n // rather than overwriting the project root.\n try {\n // Pre-resolve the target so we can save the original content (for\n // faithful rollback) and record the path for the rollback state.\n const preResolved = resolveInstrumentationTarget(projectRoot);\n if (!preResolved.conflict && preResolved.target !== null) {\n rollbackState.instrumentationPath = preResolved.target;\n if (fs.existsSync(preResolved.target)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(\n preResolved.target,\n \"utf-8\",\n );\n }\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot, {\n // `--yes` implies non-interactive automation and must not hang on a\n // merge confirmation prompt. `--force` skips the prompt explicitly\n // (DISC-1247 Scenario 2c parity).\n force: options.force === true || options.yes,\n });\n // Record the exact path the scaffolder wrote to, in case the resolver\n // and scaffolder ever disagree (symlinks, TOCTOU) — rollback is more\n // accurate when it targets the file that was actually mutated.\n if (instrResult.filePath !== undefined) {\n rollbackState.instrumentationPath = instrResult.filePath;\n }\n const relativePath =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"instrumentation.ts\";\n switch (instrResult.action) {\n case \"created\":\n summary.push(`Created ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(`Added registerGlasstrace() to existing ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"appended\":\n summary.push(\n `Appended register() with registerGlasstrace() to ${relativePath}`,\n );\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(`Skipped ${relativePath} (registerGlasstrace already present)`);\n break;\n case \"skipped\":\n // User declined the merge prompt (DISC-1247 Scenario 2c parity).\n // Emit a warning so re-init output makes clear nothing changed.\n // \"--force\" here only bypasses the confirmation — the scaffolder\n // merges rather than overwriting, so the wording is deliberate.\n warnings.push(\n `Preserved ${relativePath} (merge declined; re-run with --force to apply the merge without prompting)`,\n );\n break;\n case \"conflict\": {\n // Both root and src/ instrumentation files exist — Next.js's\n // loader behavior is undefined (DISC-493 Issue 1). Refuse to\n // write a third competing file; point the user at the file to\n // merge into and tell them to remove the other.\n const primary =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"src/instrumentation.ts\";\n const competing =\n instrResult.conflictingPath !== undefined\n ? path.relative(projectRoot, instrResult.conflictingPath)\n : \"instrumentation.ts\";\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(\n `Both ${primary} and ${competing} exist. Next.js's loader behavior is undefined when both are present.\\n` +\n `Merge your instrumentation into ${primary} and remove ${competing}, then re-run init.`,\n );\n return { exitCode: 1, summary, warnings, errors };\n }\n case \"unrecognized\":\n warnings.push(\n `${relativePath} 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 file: ${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\n // Step 7a: Write the static discovery file at\n // `<staticRoot>/.well-known/glasstrace.json` so the Glasstrace browser\n // extension can discover the project's anon key without a runtime HTTP\n // handler (design doc \"SDK Discovery Endpoint / Static File\" §6.1).\n //\n // Re-init (DISC-1247 Scenario 2) preserves any user-added fields and\n // only rewrites when the on-disk key no longer matches. A failed\n // write is surfaced as a warning rather than an init-blocking error:\n // scaffolding has already succeeded at this point and the user can\n // unblock themselves by inspecting the path and re-running.\n try {\n const discoveryResult = writeDiscoveryFile(projectRoot, anonKey);\n const relPath = relativeDiscoveryPath(discoveryResult.layout);\n switch (discoveryResult.action) {\n case \"created\":\n summary.push(`Created ${relPath}`);\n rollbackState.steps.push(\"discovery-file\");\n break;\n case \"updated-stale\":\n summary.push(`Updated ${relPath} (anon key had changed)`);\n // Not pushed to rollback: the pre-existing file was user-owned\n // content (just with a stale key). A rollback should restore\n // the original key rather than delete the file outright, and\n // we do not snapshot the prior content. Leaving it alone is\n // the safer behavior.\n break;\n case \"skipped-matches\":\n summary.push(`Skipped ${relPath} (already matches anon key)`);\n break;\n case \"skipped-foreign\":\n summary.push(\n `Rewrote ${relPath} (existing file was malformed or not SDK-managed)`,\n );\n // Same reasoning as \"updated-stale\": we did not snapshot the\n // original content, so rollback can only delete. Leaving off\n // the rollback list prevents data loss on a later-step failure.\n break;\n case \"failed\":\n warnings.push(\n `Failed to write ${relPath}${\n discoveryResult.error !== undefined\n ? `: ${discoveryResult.error}`\n : \"\"\n }. The Glasstrace browser extension will fall back to the runtime handler until the file is written.`,\n );\n break;\n }\n\n // Emit a warning if the user's `.gitignore` excludes `.well-known/`\n // or the discovery file path — otherwise the file will not be\n // deployed and the extension will silently fail to discover the\n // project. We never modify `.gitignore` (the file is public\n // metadata, not a secret).\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n try {\n const gitignoreContent = fs.readFileSync(gitignorePath, \"utf-8\");\n if (gitignoreExcludesDiscoveryFile(gitignoreContent, discoveryResult.layout)) {\n warnings.push(\n `Your .gitignore excludes ${relPath} (directly or via a parent rule). ` +\n \"The discovery file must be committed for the Glasstrace browser extension to find it in deployed builds. \" +\n \"Remove the matching line from .gitignore or add an explicit negation (e.g. `!\" +\n relPath +\n \"`).\",\n );\n }\n } catch {\n // Unreadable .gitignore is not actionable here — skip the check.\n }\n }\n } catch (err) {\n warnings.push(\n `Failed to write ${relativeDiscoveryPath(\n resolveStaticRoot(projectRoot).layout,\n )}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\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 // Step 10: Blocking verification that the anon key is registered\n // server-side (DISC-493 Issue 3, DISC-494). Runs last so failures here\n // surface the exit code without blocking the file scaffolding work.\n //\n // Skipped when:\n // - `GLASSTRACE_SKIP_INIT_VERIFY=1` is set (offline installs)\n // - CI mode is active (CI builds should not reach external networks)\n // - Vitest is running (`VITEST` env var) — unit tests that don't\n // care about the network path shouldn't be forced to mock the\n // transport; tests that DO care use `_setTransportForTesting`\n // and explicitly un-set `VITEST` before calling runInit.\n const skipVerify =\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"1\" ||\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"true\" ||\n process.env[\"VITEST\"] === \"true\";\n\n if (!skipVerify && !isCI) {\n const verifyResult = await verifyAnonKeyRegistration(projectRoot);\n if (verifyResult.outcome === \"failed\") {\n errors.push(verifyResult.error);\n return { exitCode: 2, summary, warnings, errors };\n }\n if (verifyResult.outcome === \"verified\") {\n summary.push(\"Verified anon key registration with Glasstrace API\");\n } else {\n // \"skipped\": no anon key on disk means MCP auto-configuration\n // failed earlier (a warning was already emitted). Reporting\n // \"Verified ...\" here would misrepresent the state of setup —\n // a verification request was never even sent.\n summary.push(\"Skipped anon key verification (no anon key on disk)\");\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Outcome of {@link verifyAnonKeyRegistration}:\n *\n * - `\"verified\"` — the server registered the anon key (HTTP 2xx with a\n * schema-valid response body).\n * - `\"skipped\"` — no anon key was on disk, so no verification request\n * was sent. Typically means MCP auto-configuration failed earlier\n * (warnings were already emitted) — distinct from a verification\n * success.\n * - `\"failed\"` — the verification request was sent and failed.\n * `error` is a user-facing message distinguishing the failure class\n * (`fetch failed: ...`, `server rejected the key ...`, or\n * `server returned malformed response ...`) with no anon key bytes.\n */\nexport type VerifyAnonKeyOutcome =\n | { outcome: \"verified\" }\n | { outcome: \"skipped\" }\n | { outcome: \"failed\"; error: string };\n\n/**\n * Verifies that the anonymous key written by init is registered\n * server-side. Called at the end of the scaffold flow so that when it\n * fails, the user sees an actionable error rather than a misleading\n * \"initialized successfully\" followed by silent MCP authentication\n * failures (DISC-494).\n *\n * Returns a discriminated outcome so the CLI can distinguish a genuine\n * verification pass from the \"no anon key on disk\" skip case. On\n * failure the error message distinguishes three classes:\n * - \"fetch failed\" — transport error (DNS, TCP, TLS, timeout)\n * - \"server rejected the key\" — HTTP 4xx/5xx\n * - \"server returned malformed response\" — HTTP 2xx with unparseable\n * body\n *\n * The anon key is NEVER included in the returned message. Callers\n * (the CLI entry point) render it verbatim to stderr via the errors\n * array and exit non-zero.\n *\n * @internal Exported for testability.\n */\nexport async function verifyAnonKeyRegistration(\n projectRoot: string,\n): Promise<VerifyAnonKeyOutcome> {\n // Read env the same way the runtime would — scaffoldEnvLocal may have\n // written GLASSTRACE_API_KEY=gt_anon_..., so prefer the generated\n // anon key on disk as the authoritative value to verify.\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n // No anon key on disk means MCP wasn't configured. Skip\n // verification — this is a partial install, not a verification\n // failure. The init flow already emitted warnings above. Report\n // \"skipped\" so the caller doesn't claim verification succeeded.\n return { outcome: \"skipped\" };\n }\n\n // Load the dev key from .env.local if present (for straggler linking)\n // but fall through to anon-only verification otherwise. Reading is\n // best-effort: any error leaves devKey undefined and we verify the\n // anon key in isolation.\n //\n // Use `readEnvLocalApiKey` (shared with the rest of the init flow) so\n // we match dotenv-style last-wins semantics. A hand-rolled regex here\n // would pick the FIRST `GLASSTRACE_API_KEY=` assignment, which in a\n // rotated env file would authenticate with a stale key and surface a\n // false `server rejected` failure even when the effective key is\n // valid.\n let devKey: string | undefined;\n try {\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const envContent = fs.readFileSync(envPath, \"utf-8\");\n const effective = readEnvLocalApiKey(envContent);\n // Only send a real dev key in the dev slot — don't double-count\n // the anon key as both keys.\n if (effective !== null && isDevApiKey(effective)) {\n devKey = effective;\n }\n }\n } catch {\n // Ignore — fall through to anon-only verification.\n }\n\n // Resolve endpoint / environment from the user's shell, then pin\n // `apiKey` to whatever we read from .env.local. `resolveConfig` uses\n // `??` to fall back to `process.env.GLASSTRACE_API_KEY` when the\n // option is undefined, which would cause verification to authenticate\n // with an unrelated stale key in shells that export\n // `GLASSTRACE_API_KEY` — producing false \"server rejected\" failures\n // even when the freshly generated anon key is valid. The explicit\n // override keeps verification tied to disk state.\n const baseConfig = resolveConfig({ apiKey: devKey });\n const config = { ...baseConfig, apiKey: devKey };\n const sdkVersion = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n const result: VerifyInitResult = await verifyInitReachable(config, anonKey, sdkVersion);\n\n if (result.ok) {\n return { outcome: \"verified\" };\n }\n\n const hint = \"Run 'npx glasstrace status' or 'npx glasstrace doctor' to diagnose.\";\n switch (result.reason) {\n case \"transport\":\n // `result.detail` is the raw cause with any leading `fetch failed: `\n // stripped by verifyInitReachable. Format once here so the output\n // matches the documented `fetch failed: <reason>` shape and never\n // renders `fetch failed (fetch failed: ...)`.\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: fetch failed: ${result.detail}. ${hint}`,\n };\n case \"rejected\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server rejected the key (HTTP ${result.status}). ${hint}`,\n };\n case \"malformed\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server returned malformed response. ${hint}`,\n };\n }\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 // Only claim success when exitCode is 0. A non-zero exit\n // means init scaffolding completed but a later step (e.g.,\n // verifyAnonKeyRegistration) failed — we must not tell the\n // user everything is fine (DISC-494 root cause).\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n } else {\n process.stderr.write(\"\\nGlasstrace init completed with errors.\\n\\n\");\n }\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n if (result.exitCode === 0) {\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 }\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,cAAc;AA8CnB,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;AAsCA,SAAS,uBAAuB,SAAyB;AACvD,SAAO,QAAQ,QAAQ,WAAW,IAAI;AACxC;AA+CO,SAAS,+BACd,kBACA,QACS;AACT,QAAM,aAAa,WAAW,WAAW,WAAW;AAIpD,QAAM,cAAc,CAAC,YAAY,GAAG,UAAU,GAAG;AACjD,QAAM,mBAAmB;AAAA,IACvB,GAAG,UAAU;AAAA,IACb,GAAG,UAAU;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc;AAAA,IAClB,GAAG,UAAU;AAAA,IACb;AAAA,EACF;AAKA,MAAI,cAAc;AAClB,MAAI,mBAAmB;AACvB,MAAI,cAAc;AAElB,aAAW,WAAW,iBAAiB,MAAM,IAAI,GAAG;AAClD,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,SAAS,MAAM,KAAK,WAAW,GAAG,EAAG;AAEzC,UAAM,WAAW,KAAK,WAAW,GAAG;AACpC,UAAM,UAAU,WAAW,KAAK,MAAM,CAAC,EAAE,KAAK,IAAI;AAClD,QAAI,YAAY,GAAI;AAIpB,UAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAEhE,UAAM,cAAc,qBAAqB,YAAY,WAAW;AAChE,UAAM,mBAAmB,qBAAqB,YAAY,gBAAgB;AAC1E,UAAM,cAAc,qBAAqB,YAAY,WAAW;AAEhE,QAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,YAAa;AAEvD,UAAM,WAAW,CAAC;AAClB,QAAI,YAAa,eAAc;AAC/B,QAAI,iBAAkB,oBAAmB;AACzC,QAAI,YAAa,eAAc;AAAA,EACjC;AAKA,SAAO,eAAe,oBAAoB;AAC5C;AAaA,SAAS,qBACP,SACA,SACS;AAGT,QAAM,OAAO,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AAE5D,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAC3D,QAAI,SAAS,MAAO,QAAO;AAE3B,QAAI,QAAQ,WAAW,KAAK,GAAG;AAC7B,YAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,YAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAC3D,UAAI,UAAU,SAAS,MAAM,SAAS,IAAI,KAAK,EAAE,EAAG,QAAO;AAAA,IAC7D;AAAA,EACF;AACA,SAAO;AACT;AASA,eAAsB,cACpB,OACA,aACA,OACe;AACf,aAAW,QAAQ,CAAC,GAAG,KAAK,EAAE,QAAQ,GAAG;AACvC,QAAI;AACF,cAAQ,MAAM;AAAA,QACZ,KAAK,mBAAmB;AAKtB,gBAAM,YACJ,OAAO,uBAA4B,UAAK,aAAa,oBAAoB;AAC3E,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,QACA,KAAK,kBAAkB;AAIrB,8BAAoB,WAAW;AAC/B;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;AAKjD,MAAI;AAGF,UAAM,cAAc,6BAA6B,WAAW;AAC5D,QAAI,CAAC,YAAY,YAAY,YAAY,WAAW,MAAM;AACxD,oBAAc,sBAAsB,YAAY;AAChD,UAAO,cAAW,YAAY,MAAM,GAAG;AACrC,sBAAc,iCAAoC;AAAA,UAChD,YAAY;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,wBAAwB,aAAa;AAAA;AAAA;AAAA;AAAA,MAI7D,OAAO,QAAQ,UAAU,QAAQ,QAAQ;AAAA,IAC3C,CAAC;AAID,QAAI,YAAY,aAAa,QAAW;AACtC,oBAAc,sBAAsB,YAAY;AAAA,IAClD;AACA,UAAM,eACJ,YAAY,aAAa,SAChB,cAAS,aAAa,YAAY,QAAQ,IAC/C;AACN,YAAQ,YAAY,QAAQ;AAAA,MAC1B,KAAK;AACH,gBAAQ,KAAK,WAAW,YAAY,EAAE;AACtC,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,0CAA0C,YAAY,EAAE;AACrE,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ;AAAA,UACN,oDAAoD,YAAY;AAAA,QAClE;AACA,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,WAAW,YAAY,uCAAuC;AAC3E;AAAA,MACF,KAAK;AAKH,iBAAS;AAAA,UACP,aAAa,YAAY;AAAA,QAC3B;AACA;AAAA,MACF,KAAK,YAAY;AAKf,cAAM,UACJ,YAAY,aAAa,SAChB,cAAS,aAAa,YAAY,QAAQ,IAC/C;AACN,cAAM,YACJ,YAAY,oBAAoB,SACvB,cAAS,aAAa,YAAY,eAAe,IACtD;AACN,cAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,eAAO;AAAA,UACL,QAAQ,OAAO,QAAQ,SAAS;AAAA,kCACK,OAAO,eAAe,SAAS;AAAA,QACtE;AACA,eAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,MAClD;AAAA,MACA,KAAK;AACH,iBAAS;AAAA,UACP,GAAG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKjB;AACA;AAAA,IACJ;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AACvG,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;AAYA,QAAI;AACF,YAAM,kBAAkB,mBAAmB,aAAa,OAAO;AAC/D,YAAM,UAAU,sBAAsB,gBAAgB,MAAM;AAC5D,cAAQ,gBAAgB,QAAQ;AAAA,QAC9B,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,EAAE;AACjC,wBAAc,MAAM,KAAK,gBAAgB;AACzC;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,yBAAyB;AAMxD;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,6BAA6B;AAC5D;AAAA,QACF,KAAK;AACH,kBAAQ;AAAA,YACN,WAAW,OAAO;AAAA,UACpB;AAIA;AAAA,QACF,KAAK;AACH,mBAAS;AAAA,YACP,mBAAmB,OAAO,GACxB,gBAAgB,UAAU,SACtB,KAAK,gBAAgB,KAAK,KAC1B,EACN;AAAA,UACF;AACA;AAAA,MACJ;AAOA,YAAM,gBAAqB,UAAK,aAAa,YAAY;AACzD,UAAO,cAAW,aAAa,GAAG;AAChC,YAAI;AACF,gBAAM,mBAAsB,gBAAa,eAAe,OAAO;AAC/D,cAAI,+BAA+B,kBAAkB,gBAAgB,MAAM,GAAG;AAC5E,qBAAS;AAAA,cACP,4BAA4B,OAAO,8NAGjC,UACA;AAAA,YACJ;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,mBAAmB;AAAA,UACjB,kBAAkB,WAAW,EAAE;AAAA,QACjC,CAAC,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACxD;AAAA,IACF;AAEA,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;AAaA,QAAM,aACJ,QAAQ,IAAI,6BAA6B,MAAM,OAC/C,QAAQ,IAAI,6BAA6B,MAAM,UAC/C,QAAQ,IAAI,QAAQ,MAAM;AAE5B,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,UAAM,eAAe,MAAM,0BAA0B,WAAW;AAChE,QAAI,aAAa,YAAY,UAAU;AACrC,aAAO,KAAK,aAAa,KAAK;AAC9B,aAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,IAClD;AACA,QAAI,aAAa,YAAY,YAAY;AACvC,cAAQ,KAAK,oDAAoD;AAAA,IACnE,OAAO;AAKL,cAAQ,KAAK,qDAAqD;AAAA,IACpE;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAClD;AA0CA,eAAsB,0BACpB,aAC+B;AAI/B,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,MAAI,YAAY,MAAM;AAKpB,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AAaA,MAAI;AACJ,MAAI;AACF,UAAM,UAAe,UAAK,aAAa,YAAY;AACnD,QAAO,cAAW,OAAO,GAAG;AAC1B,YAAM,aAAgB,gBAAa,SAAS,OAAO;AACnD,YAAM,YAAY,mBAAmB,UAAU;AAG/C,UAAI,cAAc,QAAQ,YAAY,SAAS,GAAG;AAChD,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAUA,QAAM,aAAa,cAAc,EAAE,QAAQ,OAAO,CAAC;AACnD,QAAM,SAAS,EAAE,GAAG,YAAY,QAAQ,OAAO;AAC/C,QAAM,aAAa,OAAsC,UAAkB;AAE3E,QAAM,SAA2B,MAAM,oBAAoB,QAAQ,SAAS,UAAU;AAEtF,MAAI,OAAO,IAAI;AACb,WAAO,EAAE,SAAS,WAAW;AAAA,EAC/B;AAEA,QAAM,OAAO;AACb,UAAQ,OAAO,QAAQ;AAAA,IACrB,KAAK;AAKH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sDAAsD,OAAO,MAAM,KAAK,IAAI;AAAA,MACrF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sEAAsE,OAAO,MAAM,MAAM,IAAI;AAAA,MACtG;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,4EAA4E,IAAI;AAAA,MACzF;AAAA,EACJ;AACF;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;AAK7B,cAAI,OAAO,aAAa,GAAG;AACzB,oBAAQ,OAAO,MAAM,4CAA4C;AAAA,UACnE,OAAO;AACL,oBAAQ,OAAO,MAAM,8CAA8C;AAAA,UACrE;AACA,qBAAW,QAAQ,OAAO,SAAS;AACjC,oBAAQ,OAAO,MAAM,OAAO,IAAI;AAAA,CAAI;AAAA,UACtC;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,oBAAQ,OAAO,MAAM,iBAAiB;AACtC,oBAAQ,OAAO,MAAM,sCAAsC;AAC3D,oBAAQ,OAAO;AAAA,cACb;AAAA,YACF;AACA,oBAAQ,OAAO;AAAA,cACb;AAAA,YACF;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":[]}
|
|
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 addCoverageMapEnv,\n mcpConfigMatches,\n readEnvLocalApiKey,\n isDevApiKey,\n resolveInstrumentationTarget,\n} from \"./scaffolder.js\";\nimport {\n identityFingerprint,\n resolveEffectiveMcpCredential,\n writeMcpMarker,\n} from \"../mcp-runtime.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\";\nimport { verifyInitReachable, type VerifyInitResult } from \"../init-client.js\";\nimport { resolveConfig } from \"../env-detection.js\";\nimport {\n writeDiscoveryFile,\n removeDiscoveryFile,\n relativeDiscoveryPath,\n resolveStaticRoot,\n} from \"./discovery-file.js\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when\n// running tests under vitest (no tsup build step).\ndeclare const __SDK_VERSION__: string;\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 =\n | \"instrumentation\"\n | \"next-config\"\n | \"env-local\"\n | \"gitignore\"\n | \"discovery-file\";\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 /**\n * Absolute path of the instrumentation file that the scaffolder\n * wrote to. May be either `{root}/instrumentation.ts` or\n * `{root}/src/instrumentation.ts` depending on the project layout\n * (DISC-493 Issue 1). When absent, rollback falls back to the\n * root path for backward compatibility with callers that do not\n * populate this field.\n */\n instrumentationPath?: string;\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 * Returns true when the given `.gitignore` content would exclude the\n * static discovery file at `<root>/public/.well-known/glasstrace.json`\n * or `<root>/static/.well-known/glasstrace.json` (depending on `layout`)\n * from being committed.\n *\n * Model: the file has three ancestors in its committed path — the\n * static root directory (`public/` or `static/`), the `.well-known/`\n * sub-directory, and the file itself. Each pattern in `.gitignore` is\n * classified by which ancestor paths it matches, and each ancestor\n * carries its own current ignore state that later patterns can flip.\n * Patterns that don't match any of the three ancestors are ignored.\n *\n * Per `gitignore(5)`:\n *\n * > It is not possible to re-include a file if a parent directory of\n * > that file is excluded.\n *\n * Consequently, the file is reported as ignored when the final state\n * of ANY ancestor (static root, `.well-known/`, or the file itself)\n * is ignored — a `!<file>` negation alone cannot \"escape\" an ignored\n * ancestor.\n *\n * Per-ancestor tracking is what distinguishes overlapping patterns:\n *\n * - `public/` then `!public/` — root re-included, file OK.\n * - `public/` then `!public/.well-known/` — root still ignored (scope-2\n * negation doesn't match the scope-1 ancestor path), file ignored.\n * - `.well-known/` then `!public/.well-known/` — `.well-known/` re-included\n * (both patterns match the scope-2 ancestor), file OK.\n * - `.well-known/` then `!public/` — `!public/` matches the\n * scope-1 ancestor only; the scope-2 ancestor (`.well-known/`) is still\n * ignored, so the file is ignored.\n * - `<file>` then `!<file>` — file-level ignore flipped.\n * - `<file>` then `!public/.well-known/` — directory negation does\n * not match the file path; file still ignored.\n *\n * Not-modeled rules — glob wildcards (star, question mark, character\n * classes), overlapping any-depth matches across multiple parents, and\n * nested `.gitignore` files — skew the heuristic toward false positives\n * (extra warning output) rather than false negatives. The warning is\n * advisory.\n *\n * @internal Exported for unit testing only.\n */\nexport function gitignoreExcludesDiscoveryFile(\n gitignoreContent: string,\n layout: \"public\" | \"static\",\n): boolean {\n const staticRoot = layout === \"static\" ? \"static\" : \"public\";\n\n // Per-ancestor target sets. `matchesDiscoveryPath` handles trailing\n // slash and leading `**/` shapes on top of these exact paths.\n const rootTargets = [staticRoot, `${staticRoot}/`];\n const wellKnownTargets = [\n `${staticRoot}/.well-known`,\n `${staticRoot}/.well-known/`,\n \".well-known\",\n \".well-known/\",\n ];\n const fileTargets = [\n `${staticRoot}/.well-known/glasstrace.json`,\n \".well-known/glasstrace.json\",\n ];\n\n // Current ignore state of each ancestor path. Later patterns flip\n // these independently because a pattern only affects ancestors it\n // actually matches.\n let rootIgnored = false;\n let wellKnownIgnored = false;\n let fileIgnored = false;\n\n for (const rawLine of gitignoreContent.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line === \"\" || line.startsWith(\"#\")) continue;\n\n const negation = line.startsWith(\"!\");\n const pattern = negation ? line.slice(1).trim() : line;\n if (pattern === \"\") continue;\n\n // Normalize: strip a leading slash (anchors to root in git's grammar;\n // for our purposes the patterns we check are all root-relative).\n const normalized = pattern.startsWith(\"/\") ? pattern.slice(1) : pattern;\n\n const matchesRoot = matchesDiscoveryPath(normalized, rootTargets);\n const matchesWellKnown = matchesDiscoveryPath(normalized, wellKnownTargets);\n const matchesFile = matchesDiscoveryPath(normalized, fileTargets);\n\n if (!matchesRoot && !matchesWellKnown && !matchesFile) continue;\n\n const newState = !negation;\n if (matchesRoot) rootIgnored = newState;\n if (matchesWellKnown) wellKnownIgnored = newState;\n if (matchesFile) fileIgnored = newState;\n }\n\n // A file is ignored whenever any ancestor's final state is ignored;\n // gitignore(5) does not permit re-including a file below an ignored\n // parent with a file-level `!<path>` pattern alone.\n return rootIgnored || wellKnownIgnored || fileIgnored;\n}\n\n/**\n * Returns true when the (normalized) gitignore pattern matches any of the\n * discovery-file-relevant paths. Supports three common pattern shapes:\n *\n * - Exact path: e.g. `public/.well-known/glasstrace.json`\n * - Directory: e.g. `.well-known/` or `public/.well-known`\n * - Leading `**\\/`: e.g. `**\\/.well-known/` (any-depth wildcard)\n *\n * Complex glob patterns (`*`, `?`, character classes) are not modeled;\n * the warning is advisory and false negatives are acceptable.\n */\nfunction matchesDiscoveryPath(\n pattern: string,\n targets: string[],\n): boolean {\n // Strip trailing slash — both `.well-known` and `.well-known/` should\n // match a directory-style target.\n const bare = pattern.endsWith(\"/\") ? pattern.slice(0, -1) : pattern;\n\n for (const target of targets) {\n const tBare = target.endsWith(\"/\") ? target.slice(0, -1) : target;\n if (bare === tBare) return true;\n // `**/X` matches any-depth occurrence of X\n if (pattern.startsWith(\"**/\")) {\n const suffix = pattern.slice(3);\n const sBare = suffix.endsWith(\"/\") ? suffix.slice(0, -1) : suffix;\n if (tBare === sBare || tBare.endsWith(`/${sBare}`)) return true;\n }\n }\n return false;\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 // Prefer the exact path the scaffolder wrote to — the resolver\n // may have chosen `src/instrumentation.ts` on Next.js `src/`\n // layouts (DISC-493 Issue 1). Fall back to the root path for\n // callers that do not populate `instrumentationPath`.\n const instrPath =\n state?.instrumentationPath ?? 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 case \"discovery-file\": {\n // Remove the static discovery file scaffolded in Step 7. The\n // removeDiscoveryFile helper also best-effort removes an empty\n // `.well-known/` directory but never touches sibling content.\n removeDiscoveryFile(projectRoot);\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 the instrumentation file has registerGlasstrace().\n // DISC-493 Issue 1: detect `src/` layout and merge into the existing file\n // rather than overwriting the project root.\n try {\n // Pre-resolve the target so we can save the original content (for\n // faithful rollback) and record the path for the rollback state.\n const preResolved = resolveInstrumentationTarget(projectRoot);\n if (!preResolved.conflict && preResolved.target !== null) {\n rollbackState.instrumentationPath = preResolved.target;\n if (fs.existsSync(preResolved.target)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(\n preResolved.target,\n \"utf-8\",\n );\n }\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot, {\n // `--yes` implies non-interactive automation and must not hang on a\n // merge confirmation prompt. `--force` skips the prompt explicitly\n // (DISC-1247 Scenario 2c parity).\n force: options.force === true || options.yes,\n });\n // Record the exact path the scaffolder wrote to, in case the resolver\n // and scaffolder ever disagree (symlinks, TOCTOU) — rollback is more\n // accurate when it targets the file that was actually mutated.\n if (instrResult.filePath !== undefined) {\n rollbackState.instrumentationPath = instrResult.filePath;\n }\n const relativePath =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"instrumentation.ts\";\n switch (instrResult.action) {\n case \"created\":\n summary.push(`Created ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(`Added registerGlasstrace() to existing ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"appended\":\n summary.push(\n `Appended register() with registerGlasstrace() to ${relativePath}`,\n );\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(`Skipped ${relativePath} (registerGlasstrace already present)`);\n break;\n case \"skipped\":\n // User declined the merge prompt (DISC-1247 Scenario 2c parity).\n // Emit a warning so re-init output makes clear nothing changed.\n // \"--force\" here only bypasses the confirmation — the scaffolder\n // merges rather than overwriting, so the wording is deliberate.\n warnings.push(\n `Preserved ${relativePath} (merge declined; re-run with --force to apply the merge without prompting)`,\n );\n break;\n case \"conflict\": {\n // Both root and src/ instrumentation files exist — Next.js's\n // loader behavior is undefined (DISC-493 Issue 1). Refuse to\n // write a third competing file; point the user at the file to\n // merge into and tell them to remove the other.\n const primary =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"src/instrumentation.ts\";\n const competing =\n instrResult.conflictingPath !== undefined\n ? path.relative(projectRoot, instrResult.conflictingPath)\n : \"instrumentation.ts\";\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(\n `Both ${primary} and ${competing} exist. Next.js's loader behavior is undefined when both are present.\\n` +\n `Merge your instrumentation into ${primary} and remove ${competing}, then re-run init.`,\n );\n return { exitCode: 1, summary, warnings, errors };\n }\n case \"unrecognized\":\n warnings.push(\n `${relativePath} 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 file: ${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\n // Step 7a: Write the static discovery file at\n // `<staticRoot>/.well-known/glasstrace.json` so the Glasstrace browser\n // extension can discover the project's anon key without a runtime HTTP\n // handler (design doc \"SDK Discovery Endpoint / Static File\" §6.1).\n //\n // Re-init (DISC-1247 Scenario 2) preserves any user-added fields and\n // only rewrites when the on-disk key no longer matches. A failed\n // write is surfaced as a warning rather than an init-blocking error:\n // scaffolding has already succeeded at this point and the user can\n // unblock themselves by inspecting the path and re-running.\n try {\n const discoveryResult = writeDiscoveryFile(projectRoot, anonKey);\n const relPath = relativeDiscoveryPath(discoveryResult.layout);\n switch (discoveryResult.action) {\n case \"created\":\n summary.push(`Created ${relPath}`);\n rollbackState.steps.push(\"discovery-file\");\n break;\n case \"updated-stale\":\n summary.push(`Updated ${relPath} (anon key had changed)`);\n // Not pushed to rollback: the pre-existing file was user-owned\n // content (just with a stale key). A rollback should restore\n // the original key rather than delete the file outright, and\n // we do not snapshot the prior content. Leaving it alone is\n // the safer behavior.\n break;\n case \"skipped-matches\":\n summary.push(`Skipped ${relPath} (already matches anon key)`);\n break;\n case \"skipped-foreign\":\n summary.push(\n `Rewrote ${relPath} (existing file was malformed or not SDK-managed)`,\n );\n // Same reasoning as \"updated-stale\": we did not snapshot the\n // original content, so rollback can only delete. Leaving off\n // the rollback list prevents data loss on a later-step failure.\n break;\n case \"failed\":\n warnings.push(\n `Failed to write ${relPath}${\n discoveryResult.error !== undefined\n ? `: ${discoveryResult.error}`\n : \"\"\n }. The Glasstrace browser extension will fall back to the runtime handler until the file is written.`,\n );\n break;\n }\n\n // Emit a warning if the user's `.gitignore` excludes `.well-known/`\n // or the discovery file path — otherwise the file will not be\n // deployed and the extension will silently fail to discover the\n // project. We never modify `.gitignore` (the file is public\n // metadata, not a secret).\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n try {\n const gitignoreContent = fs.readFileSync(gitignorePath, \"utf-8\");\n if (gitignoreExcludesDiscoveryFile(gitignoreContent, discoveryResult.layout)) {\n warnings.push(\n `Your .gitignore excludes ${relPath} (directly or via a parent rule). ` +\n \"The discovery file must be committed for the Glasstrace browser extension to find it in deployed builds. \" +\n \"Remove the matching line from .gitignore or add an explicit negation (e.g. `!\" +\n relPath +\n \"`).\",\n );\n }\n } catch {\n // Unreadable .gitignore is not actionable here — skip the check.\n }\n }\n } catch (err) {\n warnings.push(\n `Failed to write ${relativeDiscoveryPath(\n resolveStaticRoot(projectRoot).layout,\n )}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // `anyConfigWritten` covers both fresh writes AND user-preserved\n // configs — it gates nudge suppression (we don't want to pester\n // users about MCP setup they consciously preserved) and the\n // gitignore update.\n //\n // `anyConfigRewrittenWithBearer` is stricter: it tracks whether\n // this invocation actually wrote new content with the resolved\n // bearer. The marker is gated on this stricter flag so we don't\n // stamp the marker as \"this credential is what's on disk\" when the\n // user declined an overwrite — that would let a later\n // `glasstrace mcp add` falsely conclude the project is already\n // configured for the new credential and skip the refresh\n // (DISC-1512).\n let anyConfigWritten = false;\n let anyConfigRewrittenWithBearer = false;\n\n // Resolve the effective MCP credential. Managed configs embed\n // whichever bearer the project is authenticating ingestion with —\n // anon key on first init, dev key on a re-init after an account\n // claim. The discovery file at `.well-known/glasstrace.json`\n // continues to use the anon key (it is a public project\n // identifier read by the browser extension, not an auth token).\n const resolved = await resolveEffectiveMcpCredential(projectRoot);\n const bearer = resolved.effective?.key ?? anonKey;\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, bearer);\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 if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigRewrittenWithBearer = true;\n }\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, bearer);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n anyConfigRewrittenWithBearer = 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, bearer);\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 anyConfigRewrittenWithBearer = 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 this invocation actually wrote a\n // config with the resolved bearer. We intentionally use the\n // stricter `anyConfigRewrittenWithBearer` flag instead of the\n // looser `anyConfigWritten`: stamping the marker for a credential\n // we did not actually write to disk would let a later\n // `glasstrace mcp add` short-circuit on a stale config, missing\n // the claim-transition refresh (DISC-1512). When all configs were\n // user-preserved, no marker is written — the existing on-disk\n // marker (if any) stays untouched so its source/hash continues to\n // describe what was actually written previously.\n if (anyConfigRewrittenWithBearer) {\n const markerSource = resolved.effective?.source ?? \"anon\";\n const markerHash = identityFingerprint(bearer);\n const markerCreated = await writeMcpMarker(projectRoot, {\n credentialSource: markerSource,\n credentialHash: markerHash,\n });\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 // Step 10: Blocking verification that the anon key is registered\n // server-side (DISC-493 Issue 3, DISC-494). Runs last so failures here\n // surface the exit code without blocking the file scaffolding work.\n //\n // Skipped when:\n // - `GLASSTRACE_SKIP_INIT_VERIFY=1` is set (offline installs)\n // - CI mode is active (CI builds should not reach external networks)\n // - Vitest is running (`VITEST` env var) — unit tests that don't\n // care about the network path shouldn't be forced to mock the\n // transport; tests that DO care use `_setTransportForTesting`\n // and explicitly un-set `VITEST` before calling runInit.\n const skipVerify =\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"1\" ||\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"true\" ||\n process.env[\"VITEST\"] === \"true\";\n\n if (!skipVerify && !isCI) {\n const verifyResult = await verifyAnonKeyRegistration(projectRoot);\n if (verifyResult.outcome === \"failed\") {\n errors.push(verifyResult.error);\n return { exitCode: 2, summary, warnings, errors };\n }\n if (verifyResult.outcome === \"verified\") {\n summary.push(\"Verified anon key registration with Glasstrace API\");\n } else {\n // \"skipped\": no anon key on disk means MCP auto-configuration\n // failed earlier (a warning was already emitted). Reporting\n // \"Verified ...\" here would misrepresent the state of setup —\n // a verification request was never even sent.\n summary.push(\"Skipped anon key verification (no anon key on disk)\");\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Outcome of {@link verifyAnonKeyRegistration}:\n *\n * - `\"verified\"` — the server registered the anon key (HTTP 2xx with a\n * schema-valid response body).\n * - `\"skipped\"` — no anon key was on disk, so no verification request\n * was sent. Typically means MCP auto-configuration failed earlier\n * (warnings were already emitted) — distinct from a verification\n * success.\n * - `\"failed\"` — the verification request was sent and failed.\n * `error` is a user-facing message distinguishing the failure class\n * (`fetch failed: ...`, `server rejected the key ...`, or\n * `server returned malformed response ...`) with no anon key bytes.\n */\nexport type VerifyAnonKeyOutcome =\n | { outcome: \"verified\" }\n | { outcome: \"skipped\" }\n | { outcome: \"failed\"; error: string };\n\n/**\n * Verifies that the anonymous key written by init is registered\n * server-side. Called at the end of the scaffold flow so that when it\n * fails, the user sees an actionable error rather than a misleading\n * \"initialized successfully\" followed by silent MCP authentication\n * failures (DISC-494).\n *\n * Returns a discriminated outcome so the CLI can distinguish a genuine\n * verification pass from the \"no anon key on disk\" skip case. On\n * failure the error message distinguishes three classes:\n * - \"fetch failed\" — transport error (DNS, TCP, TLS, timeout)\n * - \"server rejected the key\" — HTTP 4xx/5xx\n * - \"server returned malformed response\" — HTTP 2xx with unparseable\n * body\n *\n * The anon key is NEVER included in the returned message. Callers\n * (the CLI entry point) render it verbatim to stderr via the errors\n * array and exit non-zero.\n *\n * @internal Exported for testability.\n */\nexport async function verifyAnonKeyRegistration(\n projectRoot: string,\n): Promise<VerifyAnonKeyOutcome> {\n // Read env the same way the runtime would — scaffoldEnvLocal may have\n // written GLASSTRACE_API_KEY=gt_anon_..., so prefer the generated\n // anon key on disk as the authoritative value to verify.\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n // No anon key on disk means MCP wasn't configured. Skip\n // verification — this is a partial install, not a verification\n // failure. The init flow already emitted warnings above. Report\n // \"skipped\" so the caller doesn't claim verification succeeded.\n return { outcome: \"skipped\" };\n }\n\n // Load the dev key from .env.local if present (for straggler linking)\n // but fall through to anon-only verification otherwise. Reading is\n // best-effort: any error leaves devKey undefined and we verify the\n // anon key in isolation.\n //\n // Use `readEnvLocalApiKey` (shared with the rest of the init flow) so\n // we match dotenv-style last-wins semantics. A hand-rolled regex here\n // would pick the FIRST `GLASSTRACE_API_KEY=` assignment, which in a\n // rotated env file would authenticate with a stale key and surface a\n // false `server rejected` failure even when the effective key is\n // valid.\n let devKey: string | undefined;\n try {\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const envContent = fs.readFileSync(envPath, \"utf-8\");\n const effective = readEnvLocalApiKey(envContent);\n // Only send a real dev key in the dev slot — don't double-count\n // the anon key as both keys.\n if (effective !== null && isDevApiKey(effective)) {\n devKey = effective;\n }\n }\n } catch {\n // Ignore — fall through to anon-only verification.\n }\n\n // Resolve endpoint / environment from the user's shell, then pin\n // `apiKey` to whatever we read from .env.local. `resolveConfig` uses\n // `??` to fall back to `process.env.GLASSTRACE_API_KEY` when the\n // option is undefined, which would cause verification to authenticate\n // with an unrelated stale key in shells that export\n // `GLASSTRACE_API_KEY` — producing false \"server rejected\" failures\n // even when the freshly generated anon key is valid. The explicit\n // override keeps verification tied to disk state.\n const baseConfig = resolveConfig({ apiKey: devKey });\n const config = { ...baseConfig, apiKey: devKey };\n const sdkVersion = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n const result: VerifyInitResult = await verifyInitReachable(config, anonKey, sdkVersion);\n\n if (result.ok) {\n return { outcome: \"verified\" };\n }\n\n const hint = \"Run 'npx glasstrace status' or 'npx glasstrace doctor' to diagnose.\";\n switch (result.reason) {\n case \"transport\":\n // `result.detail` is the raw cause with any leading `fetch failed: `\n // stripped by verifyInitReachable. Format once here so the output\n // matches the documented `fetch failed: <reason>` shape and never\n // renders `fetch failed (fetch failed: ...)`.\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: fetch failed: ${result.detail}. ${hint}`,\n };\n case \"rejected\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server rejected the key (HTTP ${result.status}). ${hint}`,\n };\n case \"malformed\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server returned malformed response. ${hint}`,\n };\n }\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 // Only claim success when exitCode is 0. A non-zero exit\n // means init scaffolding completed but a later step (e.g.,\n // verifyAnonKeyRegistration) failed — we must not tell the\n // user everything is fine (DISC-494 root cause).\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n } else {\n process.stderr.write(\"\\nGlasstrace init completed with errors.\\n\\n\");\n }\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n if (result.exitCode === 0) {\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 }\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":"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","names":[]}
|