@glasstrace/sdk 0.14.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{chunk-ERGEG4ZQ.js → chunk-2LDBR3F3.js} +16 -3
  2. package/dist/chunk-2LDBR3F3.js.map +1 -0
  3. package/dist/chunk-A2AZL6MZ.js +309 -0
  4. package/dist/chunk-A2AZL6MZ.js.map +1 -0
  5. package/dist/{chunk-ARAOZCZT.js → chunk-ROFOJQWN.js} +118 -16
  6. package/dist/chunk-ROFOJQWN.js.map +1 -0
  7. package/dist/{chunk-WV3NIPWJ.js → chunk-ZNOD6FC7.js} +18 -276
  8. package/dist/chunk-ZNOD6FC7.js.map +1 -0
  9. package/dist/cli/init.cjs +458 -115
  10. package/dist/cli/init.cjs.map +1 -1
  11. package/dist/cli/init.d.cts +33 -1
  12. package/dist/cli/init.d.ts +33 -1
  13. package/dist/cli/init.js +144 -42
  14. package/dist/cli/init.js.map +1 -1
  15. package/dist/cli/mcp-add.cjs.map +1 -1
  16. package/dist/cli/mcp-add.js +4 -2
  17. package/dist/cli/mcp-add.js.map +1 -1
  18. package/dist/cli/uninit.cjs +181 -60
  19. package/dist/cli/uninit.cjs.map +1 -1
  20. package/dist/cli/uninit.d.cts +38 -8
  21. package/dist/cli/uninit.d.ts +38 -8
  22. package/dist/cli/uninit.js +6 -3
  23. package/dist/cli/validate.cjs +135 -0
  24. package/dist/cli/validate.cjs.map +1 -0
  25. package/dist/cli/validate.d.cts +60 -0
  26. package/dist/cli/validate.d.ts +60 -0
  27. package/dist/cli/validate.js +103 -0
  28. package/dist/cli/validate.js.map +1 -0
  29. package/dist/index.cjs +123 -47
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +45 -5
  32. package/dist/index.d.ts +45 -5
  33. package/dist/index.js +109 -46
  34. package/dist/index.js.map +1 -1
  35. package/dist/{source-map-uploader-W6VPGY26.js → source-map-uploader-3GWUQDTS.js} +6 -2
  36. package/package.json +6 -4
  37. package/dist/chunk-ARAOZCZT.js.map +0 -1
  38. package/dist/chunk-ERGEG4ZQ.js.map +0 -1
  39. package/dist/chunk-WV3NIPWJ.js.map +0 -1
  40. /package/dist/{source-map-uploader-W6VPGY26.js.map → source-map-uploader-3GWUQDTS.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/validate.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * A single artifact-state inconsistency detected by `sdk init --validate`.\n */\nexport interface ValidationIssue {\n /** Stable machine-readable identifier for the issue class. */\n code:\n | \"glasstrace-dir-without-register-import\"\n | \"sdk-import-without-glasstrace-dir\"\n | \"mcp-marker-without-configs\"\n | \"mcp-configs-without-marker\";\n /** Human-readable message describing the inconsistency. */\n message: string;\n /** Suggested command or manual action to resolve the issue. */\n fix: string;\n}\n\n/** Options for `runValidate`. */\nexport interface ValidateOptions {\n projectRoot: string;\n}\n\n/** Structured result of running the validator. */\nexport interface ValidateResult {\n /** Zero when no issues; non-zero when any issue is detected. */\n exitCode: number;\n /** Ordered lines of human-friendly summary output. */\n summary: string[];\n /** Detailed per-issue findings. */\n issues: ValidationIssue[];\n}\n\n/** MCP config files init may create. Used to detect stale state. */\nconst MCP_CONFIG_CANDIDATES = [\n \".mcp.json\",\n \".cursor/mcp.json\",\n \".gemini/settings.json\",\n \".codex/config.toml\",\n] as const;\n\n/**\n * Returns true when the given instrumentation file imports\n * `@glasstrace/sdk` (which includes the `registerGlasstrace` import\n * emitted by `sdk init`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasGlasstraceImport(content: string): boolean {\n return /@glasstrace\\/sdk/.test(content);\n}\n\n/**\n * Returns true when the file imports `registerGlasstrace` specifically\n * (as opposed to other named exports such as `withGlasstraceConfig`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasRegisterGlasstraceImport(content: string): boolean {\n // Single- or multi-specifier imports from @glasstrace/sdk that include\n // `registerGlasstrace` as a named export.\n const match = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = match.exec(content);\n if (!importMatch) return false;\n return importMatch[1]\n .split(\",\")\n .map((s) => s.trim())\n .includes(\"registerGlasstrace\");\n}\n\n/**\n * Validates consistency between the filesystem artifacts that `sdk init`\n * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:\n *\n * 1. `.glasstrace/` exists but `instrumentation.ts` does not import\n * `registerGlasstrace` from `@glasstrace/sdk`.\n * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports\n * from `@glasstrace/sdk`.\n * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.\n * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.\n *\n * Each issue includes a stable `code`, a message, and a suggested fix.\n * Exit code is non-zero whenever any issue is detected so CI pipelines\n * can gate on `sdk init --validate`.\n *\n * @param options - Configuration for the validator.\n * @returns A structured result describing detected inconsistencies.\n */\nexport function runValidate(options: ValidateOptions): ValidateResult {\n const { projectRoot } = options;\n const issues: ValidationIssue[] = [];\n\n const glasstraceDir = path.join(projectRoot, \".glasstrace\");\n const instrumentationPath = path.join(projectRoot, \"instrumentation.ts\");\n const markerPath = path.join(glasstraceDir, \"mcp-connected\");\n\n const glasstraceDirExists = isDirectorySafe(glasstraceDir);\n const instrumentationExists = fs.existsSync(instrumentationPath);\n const instrumentationContent = instrumentationExists\n ? safeReadFile(instrumentationPath)\n : null;\n const markerExists = fs.existsSync(markerPath);\n\n const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter((rel) =>\n fs.existsSync(path.join(projectRoot, rel)),\n );\n\n // 1. .glasstrace/ present but instrumentation missing the SDK import\n if (glasstraceDirExists) {\n if (\n instrumentationContent === null ||\n !hasRegisterGlasstraceImport(instrumentationContent)\n ) {\n issues.push({\n code: \"glasstrace-dir-without-register-import\",\n message:\n \".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.\",\n fix: \"Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use.\",\n });\n }\n }\n\n // 2. .glasstrace/ missing but instrumentation still imports the SDK\n if (!glasstraceDirExists && instrumentationContent !== null) {\n if (hasGlasstraceImport(instrumentationContent)) {\n issues.push({\n code: \"sdk-import-without-glasstrace-dir\",\n message:\n \"instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.\",\n fix: \"Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK.\",\n });\n }\n }\n\n // 3. MCP marker present but no MCP config files exist\n if (markerExists && mcpConfigsPresent.length === 0) {\n issues.push({\n code: \"mcp-marker-without-configs\",\n message:\n \".glasstrace/mcp-connected marker is present but no MCP config files were found.\",\n fix: \"Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected.\",\n });\n }\n\n // 4. MCP config files exist but no marker\n if (!markerExists && mcpConfigsPresent.length > 0) {\n issues.push({\n code: \"mcp-configs-without-marker\",\n message: `MCP config files exist (${mcpConfigsPresent.join(\", \")}) but .glasstrace/mcp-connected marker is missing.`,\n fix: \"Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration.\",\n });\n }\n\n const summary: string[] = [];\n if (issues.length === 0) {\n summary.push(\"Glasstrace install state is consistent.\");\n } else {\n summary.push(\n `Detected ${issues.length} inconsistenc${issues.length === 1 ? \"y\" : \"ies\"} in Glasstrace install state:`,\n );\n }\n\n return {\n exitCode: issues.length > 0 ? 1 : 0,\n summary,\n issues,\n };\n}\n\n/**\n * Reads a file as UTF-8, returning `null` if the file cannot be read.\n */\nfunction safeReadFile(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Returns true when the path exists and is a directory. Returns false\n * when the path is missing, is not a directory, or when `statSync`\n * throws (permission denied, TOCTOU race between existsSync and\n * statSync, etc). Validation is best-effort and must not throw — a\n * crash here would turn a reporting tool into a hard failure.\n */\nfunction isDirectorySafe(dirPath: string): boolean {\n try {\n if (!fs.existsSync(dirPath)) return false;\n return fs.statSync(dirPath).isDirectory();\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAoB;AACpB,WAAsB;AAkCtB,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,oBAAoB,SAA0B;AAC5D,SAAO,mBAAmB,KAAK,OAAO;AACxC;AAQO,SAAS,4BAA4B,SAA0B;AAGpE,QAAM,QAAQ;AACd,QAAM,cAAc,MAAM,KAAK,OAAO;AACtC,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,CAAC,EACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,SAAS,oBAAoB;AAClC;AAoBO,SAAS,YAAY,SAA0C;AACpE,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,SAA4B,CAAC;AAEnC,QAAM,gBAAqB,UAAK,aAAa,aAAa;AAC1D,QAAM,sBAA2B,UAAK,aAAa,oBAAoB;AACvE,QAAM,aAAkB,UAAK,eAAe,eAAe;AAE3D,QAAM,sBAAsB,gBAAgB,aAAa;AACzD,QAAM,wBAA2B,cAAW,mBAAmB;AAC/D,QAAM,yBAAyB,wBAC3B,aAAa,mBAAmB,IAChC;AACJ,QAAM,eAAkB,cAAW,UAAU;AAE7C,QAAM,oBAAoB,sBAAsB;AAAA,IAAO,CAAC,QACnD,cAAgB,UAAK,aAAa,GAAG,CAAC;AAAA,EAC3C;AAGA,MAAI,qBAAqB;AACvB,QACE,2BAA2B,QAC3B,CAAC,4BAA4B,sBAAsB,GACnD;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,CAAC,uBAAuB,2BAA2B,MAAM;AAC3D,QAAI,oBAAoB,sBAAsB,GAAG;AAC/C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,gBAAgB,kBAAkB,WAAW,GAAG;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SACE;AAAA,MACF,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,gBAAgB,kBAAkB,SAAS,GAAG;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,2BAA2B,kBAAkB,KAAK,IAAI,CAAC;AAAA,MAChE,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAEA,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,KAAK,yCAAyC;AAAA,EACxD,OAAO;AACL,YAAQ;AAAA,MACN,YAAY,OAAO,MAAM,gBAAgB,OAAO,WAAW,IAAI,MAAM,KAAK;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,OAAO,SAAS,IAAI,IAAI;AAAA,IAClC;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,aAAa,UAAiC;AACrD,MAAI;AACF,WAAU,gBAAa,UAAU,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBAAgB,SAA0B;AACjD,MAAI;AACF,QAAI,CAAI,cAAW,OAAO,EAAG,QAAO;AACpC,WAAU,YAAS,OAAO,EAAE,YAAY;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A single artifact-state inconsistency detected by `sdk init --validate`.
3
+ */
4
+ interface ValidationIssue {
5
+ /** Stable machine-readable identifier for the issue class. */
6
+ code: "glasstrace-dir-without-register-import" | "sdk-import-without-glasstrace-dir" | "mcp-marker-without-configs" | "mcp-configs-without-marker";
7
+ /** Human-readable message describing the inconsistency. */
8
+ message: string;
9
+ /** Suggested command or manual action to resolve the issue. */
10
+ fix: string;
11
+ }
12
+ /** Options for `runValidate`. */
13
+ interface ValidateOptions {
14
+ projectRoot: string;
15
+ }
16
+ /** Structured result of running the validator. */
17
+ interface ValidateResult {
18
+ /** Zero when no issues; non-zero when any issue is detected. */
19
+ exitCode: number;
20
+ /** Ordered lines of human-friendly summary output. */
21
+ summary: string[];
22
+ /** Detailed per-issue findings. */
23
+ issues: ValidationIssue[];
24
+ }
25
+ /**
26
+ * Returns true when the given instrumentation file imports
27
+ * `@glasstrace/sdk` (which includes the `registerGlasstrace` import
28
+ * emitted by `sdk init`).
29
+ *
30
+ * @internal Exported for unit testing only.
31
+ */
32
+ declare function hasGlasstraceImport(content: string): boolean;
33
+ /**
34
+ * Returns true when the file imports `registerGlasstrace` specifically
35
+ * (as opposed to other named exports such as `withGlasstraceConfig`).
36
+ *
37
+ * @internal Exported for unit testing only.
38
+ */
39
+ declare function hasRegisterGlasstraceImport(content: string): boolean;
40
+ /**
41
+ * Validates consistency between the filesystem artifacts that `sdk init`
42
+ * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:
43
+ *
44
+ * 1. `.glasstrace/` exists but `instrumentation.ts` does not import
45
+ * `registerGlasstrace` from `@glasstrace/sdk`.
46
+ * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports
47
+ * from `@glasstrace/sdk`.
48
+ * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.
49
+ * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.
50
+ *
51
+ * Each issue includes a stable `code`, a message, and a suggested fix.
52
+ * Exit code is non-zero whenever any issue is detected so CI pipelines
53
+ * can gate on `sdk init --validate`.
54
+ *
55
+ * @param options - Configuration for the validator.
56
+ * @returns A structured result describing detected inconsistencies.
57
+ */
58
+ declare function runValidate(options: ValidateOptions): ValidateResult;
59
+
60
+ export { type ValidateOptions, type ValidateResult, type ValidationIssue, hasGlasstraceImport, hasRegisterGlasstraceImport, runValidate };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A single artifact-state inconsistency detected by `sdk init --validate`.
3
+ */
4
+ interface ValidationIssue {
5
+ /** Stable machine-readable identifier for the issue class. */
6
+ code: "glasstrace-dir-without-register-import" | "sdk-import-without-glasstrace-dir" | "mcp-marker-without-configs" | "mcp-configs-without-marker";
7
+ /** Human-readable message describing the inconsistency. */
8
+ message: string;
9
+ /** Suggested command or manual action to resolve the issue. */
10
+ fix: string;
11
+ }
12
+ /** Options for `runValidate`. */
13
+ interface ValidateOptions {
14
+ projectRoot: string;
15
+ }
16
+ /** Structured result of running the validator. */
17
+ interface ValidateResult {
18
+ /** Zero when no issues; non-zero when any issue is detected. */
19
+ exitCode: number;
20
+ /** Ordered lines of human-friendly summary output. */
21
+ summary: string[];
22
+ /** Detailed per-issue findings. */
23
+ issues: ValidationIssue[];
24
+ }
25
+ /**
26
+ * Returns true when the given instrumentation file imports
27
+ * `@glasstrace/sdk` (which includes the `registerGlasstrace` import
28
+ * emitted by `sdk init`).
29
+ *
30
+ * @internal Exported for unit testing only.
31
+ */
32
+ declare function hasGlasstraceImport(content: string): boolean;
33
+ /**
34
+ * Returns true when the file imports `registerGlasstrace` specifically
35
+ * (as opposed to other named exports such as `withGlasstraceConfig`).
36
+ *
37
+ * @internal Exported for unit testing only.
38
+ */
39
+ declare function hasRegisterGlasstraceImport(content: string): boolean;
40
+ /**
41
+ * Validates consistency between the filesystem artifacts that `sdk init`
42
+ * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:
43
+ *
44
+ * 1. `.glasstrace/` exists but `instrumentation.ts` does not import
45
+ * `registerGlasstrace` from `@glasstrace/sdk`.
46
+ * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports
47
+ * from `@glasstrace/sdk`.
48
+ * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.
49
+ * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.
50
+ *
51
+ * Each issue includes a stable `code`, a message, and a suggested fix.
52
+ * Exit code is non-zero whenever any issue is detected so CI pipelines
53
+ * can gate on `sdk init --validate`.
54
+ *
55
+ * @param options - Configuration for the validator.
56
+ * @returns A structured result describing detected inconsistencies.
57
+ */
58
+ declare function runValidate(options: ValidateOptions): ValidateResult;
59
+
60
+ export { type ValidateOptions, type ValidateResult, type ValidationIssue, hasGlasstraceImport, hasRegisterGlasstraceImport, runValidate };
@@ -0,0 +1,103 @@
1
+ import {
2
+ init_esm_shims
3
+ } from "../chunk-BGZ7J74D.js";
4
+
5
+ // src/cli/validate.ts
6
+ init_esm_shims();
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ var MCP_CONFIG_CANDIDATES = [
10
+ ".mcp.json",
11
+ ".cursor/mcp.json",
12
+ ".gemini/settings.json",
13
+ ".codex/config.toml"
14
+ ];
15
+ function hasGlasstraceImport(content) {
16
+ return /@glasstrace\/sdk/.test(content);
17
+ }
18
+ function hasRegisterGlasstraceImport(content) {
19
+ const match = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
20
+ const importMatch = match.exec(content);
21
+ if (!importMatch) return false;
22
+ return importMatch[1].split(",").map((s) => s.trim()).includes("registerGlasstrace");
23
+ }
24
+ function runValidate(options) {
25
+ const { projectRoot } = options;
26
+ const issues = [];
27
+ const glasstraceDir = path.join(projectRoot, ".glasstrace");
28
+ const instrumentationPath = path.join(projectRoot, "instrumentation.ts");
29
+ const markerPath = path.join(glasstraceDir, "mcp-connected");
30
+ const glasstraceDirExists = isDirectorySafe(glasstraceDir);
31
+ const instrumentationExists = fs.existsSync(instrumentationPath);
32
+ const instrumentationContent = instrumentationExists ? safeReadFile(instrumentationPath) : null;
33
+ const markerExists = fs.existsSync(markerPath);
34
+ const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter(
35
+ (rel) => fs.existsSync(path.join(projectRoot, rel))
36
+ );
37
+ if (glasstraceDirExists) {
38
+ if (instrumentationContent === null || !hasRegisterGlasstraceImport(instrumentationContent)) {
39
+ issues.push({
40
+ code: "glasstrace-dir-without-register-import",
41
+ message: ".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.",
42
+ fix: "Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use."
43
+ });
44
+ }
45
+ }
46
+ if (!glasstraceDirExists && instrumentationContent !== null) {
47
+ if (hasGlasstraceImport(instrumentationContent)) {
48
+ issues.push({
49
+ code: "sdk-import-without-glasstrace-dir",
50
+ message: "instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.",
51
+ fix: "Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK."
52
+ });
53
+ }
54
+ }
55
+ if (markerExists && mcpConfigsPresent.length === 0) {
56
+ issues.push({
57
+ code: "mcp-marker-without-configs",
58
+ message: ".glasstrace/mcp-connected marker is present but no MCP config files were found.",
59
+ fix: "Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected."
60
+ });
61
+ }
62
+ if (!markerExists && mcpConfigsPresent.length > 0) {
63
+ issues.push({
64
+ code: "mcp-configs-without-marker",
65
+ message: `MCP config files exist (${mcpConfigsPresent.join(", ")}) but .glasstrace/mcp-connected marker is missing.`,
66
+ fix: "Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration."
67
+ });
68
+ }
69
+ const summary = [];
70
+ if (issues.length === 0) {
71
+ summary.push("Glasstrace install state is consistent.");
72
+ } else {
73
+ summary.push(
74
+ `Detected ${issues.length} inconsistenc${issues.length === 1 ? "y" : "ies"} in Glasstrace install state:`
75
+ );
76
+ }
77
+ return {
78
+ exitCode: issues.length > 0 ? 1 : 0,
79
+ summary,
80
+ issues
81
+ };
82
+ }
83
+ function safeReadFile(filePath) {
84
+ try {
85
+ return fs.readFileSync(filePath, "utf-8");
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
90
+ function isDirectorySafe(dirPath) {
91
+ try {
92
+ if (!fs.existsSync(dirPath)) return false;
93
+ return fs.statSync(dirPath).isDirectory();
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+ export {
99
+ hasGlasstraceImport,
100
+ hasRegisterGlasstraceImport,
101
+ runValidate
102
+ };
103
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/validate.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * A single artifact-state inconsistency detected by `sdk init --validate`.\n */\nexport interface ValidationIssue {\n /** Stable machine-readable identifier for the issue class. */\n code:\n | \"glasstrace-dir-without-register-import\"\n | \"sdk-import-without-glasstrace-dir\"\n | \"mcp-marker-without-configs\"\n | \"mcp-configs-without-marker\";\n /** Human-readable message describing the inconsistency. */\n message: string;\n /** Suggested command or manual action to resolve the issue. */\n fix: string;\n}\n\n/** Options for `runValidate`. */\nexport interface ValidateOptions {\n projectRoot: string;\n}\n\n/** Structured result of running the validator. */\nexport interface ValidateResult {\n /** Zero when no issues; non-zero when any issue is detected. */\n exitCode: number;\n /** Ordered lines of human-friendly summary output. */\n summary: string[];\n /** Detailed per-issue findings. */\n issues: ValidationIssue[];\n}\n\n/** MCP config files init may create. Used to detect stale state. */\nconst MCP_CONFIG_CANDIDATES = [\n \".mcp.json\",\n \".cursor/mcp.json\",\n \".gemini/settings.json\",\n \".codex/config.toml\",\n] as const;\n\n/**\n * Returns true when the given instrumentation file imports\n * `@glasstrace/sdk` (which includes the `registerGlasstrace` import\n * emitted by `sdk init`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasGlasstraceImport(content: string): boolean {\n return /@glasstrace\\/sdk/.test(content);\n}\n\n/**\n * Returns true when the file imports `registerGlasstrace` specifically\n * (as opposed to other named exports such as `withGlasstraceConfig`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasRegisterGlasstraceImport(content: string): boolean {\n // Single- or multi-specifier imports from @glasstrace/sdk that include\n // `registerGlasstrace` as a named export.\n const match = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = match.exec(content);\n if (!importMatch) return false;\n return importMatch[1]\n .split(\",\")\n .map((s) => s.trim())\n .includes(\"registerGlasstrace\");\n}\n\n/**\n * Validates consistency between the filesystem artifacts that `sdk init`\n * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:\n *\n * 1. `.glasstrace/` exists but `instrumentation.ts` does not import\n * `registerGlasstrace` from `@glasstrace/sdk`.\n * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports\n * from `@glasstrace/sdk`.\n * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.\n * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.\n *\n * Each issue includes a stable `code`, a message, and a suggested fix.\n * Exit code is non-zero whenever any issue is detected so CI pipelines\n * can gate on `sdk init --validate`.\n *\n * @param options - Configuration for the validator.\n * @returns A structured result describing detected inconsistencies.\n */\nexport function runValidate(options: ValidateOptions): ValidateResult {\n const { projectRoot } = options;\n const issues: ValidationIssue[] = [];\n\n const glasstraceDir = path.join(projectRoot, \".glasstrace\");\n const instrumentationPath = path.join(projectRoot, \"instrumentation.ts\");\n const markerPath = path.join(glasstraceDir, \"mcp-connected\");\n\n const glasstraceDirExists = isDirectorySafe(glasstraceDir);\n const instrumentationExists = fs.existsSync(instrumentationPath);\n const instrumentationContent = instrumentationExists\n ? safeReadFile(instrumentationPath)\n : null;\n const markerExists = fs.existsSync(markerPath);\n\n const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter((rel) =>\n fs.existsSync(path.join(projectRoot, rel)),\n );\n\n // 1. .glasstrace/ present but instrumentation missing the SDK import\n if (glasstraceDirExists) {\n if (\n instrumentationContent === null ||\n !hasRegisterGlasstraceImport(instrumentationContent)\n ) {\n issues.push({\n code: \"glasstrace-dir-without-register-import\",\n message:\n \".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.\",\n fix: \"Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use.\",\n });\n }\n }\n\n // 2. .glasstrace/ missing but instrumentation still imports the SDK\n if (!glasstraceDirExists && instrumentationContent !== null) {\n if (hasGlasstraceImport(instrumentationContent)) {\n issues.push({\n code: \"sdk-import-without-glasstrace-dir\",\n message:\n \"instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.\",\n fix: \"Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK.\",\n });\n }\n }\n\n // 3. MCP marker present but no MCP config files exist\n if (markerExists && mcpConfigsPresent.length === 0) {\n issues.push({\n code: \"mcp-marker-without-configs\",\n message:\n \".glasstrace/mcp-connected marker is present but no MCP config files were found.\",\n fix: \"Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected.\",\n });\n }\n\n // 4. MCP config files exist but no marker\n if (!markerExists && mcpConfigsPresent.length > 0) {\n issues.push({\n code: \"mcp-configs-without-marker\",\n message: `MCP config files exist (${mcpConfigsPresent.join(\", \")}) but .glasstrace/mcp-connected marker is missing.`,\n fix: \"Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration.\",\n });\n }\n\n const summary: string[] = [];\n if (issues.length === 0) {\n summary.push(\"Glasstrace install state is consistent.\");\n } else {\n summary.push(\n `Detected ${issues.length} inconsistenc${issues.length === 1 ? \"y\" : \"ies\"} in Glasstrace install state:`,\n );\n }\n\n return {\n exitCode: issues.length > 0 ? 1 : 0,\n summary,\n issues,\n };\n}\n\n/**\n * Reads a file as UTF-8, returning `null` if the file cannot be read.\n */\nfunction safeReadFile(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Returns true when the path exists and is a directory. Returns false\n * when the path is missing, is not a directory, or when `statSync`\n * throws (permission denied, TOCTOU race between existsSync and\n * statSync, etc). Validation is best-effort and must not throw — a\n * crash here would turn a reporting tool into a hard failure.\n */\nfunction isDirectorySafe(dirPath: string): boolean {\n try {\n if (!fs.existsSync(dirPath)) return false;\n return fs.statSync(dirPath).isDirectory();\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;AAAA;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAkCtB,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,oBAAoB,SAA0B;AAC5D,SAAO,mBAAmB,KAAK,OAAO;AACxC;AAQO,SAAS,4BAA4B,SAA0B;AAGpE,QAAM,QAAQ;AACd,QAAM,cAAc,MAAM,KAAK,OAAO;AACtC,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,CAAC,EACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,SAAS,oBAAoB;AAClC;AAoBO,SAAS,YAAY,SAA0C;AACpE,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,SAA4B,CAAC;AAEnC,QAAM,gBAAqB,UAAK,aAAa,aAAa;AAC1D,QAAM,sBAA2B,UAAK,aAAa,oBAAoB;AACvE,QAAM,aAAkB,UAAK,eAAe,eAAe;AAE3D,QAAM,sBAAsB,gBAAgB,aAAa;AACzD,QAAM,wBAA2B,cAAW,mBAAmB;AAC/D,QAAM,yBAAyB,wBAC3B,aAAa,mBAAmB,IAChC;AACJ,QAAM,eAAkB,cAAW,UAAU;AAE7C,QAAM,oBAAoB,sBAAsB;AAAA,IAAO,CAAC,QACnD,cAAgB,UAAK,aAAa,GAAG,CAAC;AAAA,EAC3C;AAGA,MAAI,qBAAqB;AACvB,QACE,2BAA2B,QAC3B,CAAC,4BAA4B,sBAAsB,GACnD;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,CAAC,uBAAuB,2BAA2B,MAAM;AAC3D,QAAI,oBAAoB,sBAAsB,GAAG;AAC/C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,gBAAgB,kBAAkB,WAAW,GAAG;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SACE;AAAA,MACF,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,gBAAgB,kBAAkB,SAAS,GAAG;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,2BAA2B,kBAAkB,KAAK,IAAI,CAAC;AAAA,MAChE,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAEA,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,KAAK,yCAAyC;AAAA,EACxD,OAAO;AACL,YAAQ;AAAA,MACN,YAAY,OAAO,MAAM,gBAAgB,OAAO,WAAW,IAAI,MAAM,KAAK;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,OAAO,SAAS,IAAI,IAAI;AAAA,IAClC;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,aAAa,UAAiC;AACrD,MAAI;AACF,WAAU,gBAAa,UAAU,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBAAgB,SAA0B;AACjD,MAAI;AACF,QAAI,CAAI,cAAW,OAAO,EAAG,QAAO;AACpC,WAAU,YAAS,OAAO,EAAE,YAAY;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
package/dist/index.cjs CHANGED
@@ -16508,6 +16508,8 @@ var init_console_capture = __esm({
16508
16508
  var source_map_uploader_exports = {};
16509
16509
  __export(source_map_uploader_exports, {
16510
16510
  PRESIGNED_THRESHOLD_BYTES: () => PRESIGNED_THRESHOLD_BYTES,
16511
+ _resetBlobClientLoaderForTesting: () => _resetBlobClientLoaderForTesting,
16512
+ _setBlobClientLoaderForTesting: () => _setBlobClientLoaderForTesting,
16511
16513
  collectSourceMaps: () => collectSourceMaps,
16512
16514
  computeBuildHash: () => computeBuildHash,
16513
16515
  discoverSourceMapFiles: () => discoverSourceMapFiles,
@@ -16671,10 +16673,20 @@ async function requestPresignedTokens(apiKey, endpoint, buildHash, files) {
16671
16673
  const json2 = await response.json();
16672
16674
  return PresignedUploadResponseSchema.parse(json2);
16673
16675
  }
16676
+ async function defaultBlobClientLoader() {
16677
+ const dynamicImport = Function("id", "return import(id)");
16678
+ return dynamicImport("@vercel/blob/client");
16679
+ }
16680
+ function _setBlobClientLoaderForTesting(loader) {
16681
+ _blobClientLoader = loader;
16682
+ }
16683
+ function _resetBlobClientLoaderForTesting() {
16684
+ _blobClientLoader = defaultBlobClientLoader;
16685
+ }
16674
16686
  async function uploadToBlob(clientToken, pathname, content) {
16675
16687
  let mod;
16676
16688
  try {
16677
- mod = await import("@vercel/blob/client");
16689
+ mod = await _blobClientLoader();
16678
16690
  } catch (err) {
16679
16691
  const code = err.code;
16680
16692
  if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
@@ -16787,7 +16799,7 @@ async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options)
16787
16799
  }
16788
16800
  const checkAvailable = options?.checkBlobAvailable ?? (async () => {
16789
16801
  try {
16790
- await import("@vercel/blob/client");
16802
+ await _blobClientLoader();
16791
16803
  return true;
16792
16804
  } catch {
16793
16805
  return false;
@@ -16809,7 +16821,7 @@ async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options)
16809
16821
  );
16810
16822
  return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
16811
16823
  }
16812
- var fs2, path2, crypto2, import_node_child_process, LARGE_FILE_WARNING_BYTES, PRESIGNED_THRESHOLD_BYTES;
16824
+ var fs2, path2, crypto2, import_node_child_process, LARGE_FILE_WARNING_BYTES, PRESIGNED_THRESHOLD_BYTES, _blobClientLoader;
16813
16825
  var init_source_map_uploader = __esm({
16814
16826
  "src/source-map-uploader.ts"() {
16815
16827
  "use strict";
@@ -16822,6 +16834,7 @@ var init_source_map_uploader = __esm({
16822
16834
  init_dist();
16823
16835
  LARGE_FILE_WARNING_BYTES = 50 * 1024 * 1024;
16824
16836
  PRESIGNED_THRESHOLD_BYTES = 45e5;
16837
+ _blobClientLoader = defaultBlobClientLoader;
16825
16838
  }
16826
16839
  });
16827
16840
 
@@ -17215,6 +17228,7 @@ async function saveCachedConfig(response, projectRoot) {
17215
17228
  const root = projectRoot ?? process.cwd();
17216
17229
  const dirPath = modules.path.join(root, GLASSTRACE_DIR2);
17217
17230
  const configPath = modules.path.join(dirPath, CONFIG_FILE);
17231
+ const tmpPath = `${configPath}.tmp`;
17218
17232
  try {
17219
17233
  await modules.fs.mkdir(dirPath, { recursive: true, mode: 448 });
17220
17234
  await modules.fs.chmod(dirPath, 448);
@@ -17222,7 +17236,20 @@ async function saveCachedConfig(response, projectRoot) {
17222
17236
  response,
17223
17237
  cachedAt: Date.now()
17224
17238
  };
17225
- await modules.fs.writeFile(configPath, JSON.stringify(cached2), { encoding: "utf-8", mode: 384 });
17239
+ await modules.fs.writeFile(tmpPath, JSON.stringify(cached2), {
17240
+ encoding: "utf-8",
17241
+ mode: 384
17242
+ });
17243
+ try {
17244
+ await modules.fs.chmod(tmpPath, 384);
17245
+ await modules.fs.rename(tmpPath, configPath);
17246
+ } catch (renameErr) {
17247
+ try {
17248
+ await modules.fs.unlink(tmpPath);
17249
+ } catch {
17250
+ }
17251
+ throw renameErr;
17252
+ }
17226
17253
  await modules.fs.chmod(configPath, 384);
17227
17254
  } catch (err) {
17228
17255
  console.warn(
@@ -21370,7 +21397,6 @@ async function configureOtel(config2, sessionManager) {
21370
21397
  await provider.shutdown();
21371
21398
  }
21372
21399
  });
21373
- registerSignalHandlers();
21374
21400
  registerBeforeExitTrigger();
21375
21401
  const prismaModule = await tryImport("@prisma/instrumentation");
21376
21402
  if (prismaModule) {
@@ -21431,12 +21457,15 @@ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
21431
21457
  var BACKOFF_BASE_MS = HEARTBEAT_INTERVAL_MS;
21432
21458
  var BACKOFF_MAX_MS = 30 * 60 * 1e3;
21433
21459
  var BACKOFF_JITTER = 0.2;
21460
+ var HEARTBEAT_SHUTDOWN_PRIORITY = 10;
21461
+ var SHUTDOWN_MARKER_RELPATH = ".glasstrace/shutdown-requested";
21434
21462
  var heartbeatTimer = null;
21435
21463
  var heartbeatGeneration = 0;
21436
21464
  var backoffAttempts = 0;
21437
21465
  var backoffUntil = 0;
21438
21466
  var tickInProgress = false;
21439
- var _shutdownHandler = null;
21467
+ var shutdownHookRegistered = false;
21468
+ var shutdownFired = false;
21440
21469
  function startHeartbeat(config2, anonKey, sdkVersion, generation, onClaimTransition) {
21441
21470
  if (heartbeatTimer !== null) return;
21442
21471
  heartbeatGeneration = generation;
@@ -21444,7 +21473,7 @@ function startHeartbeat(config2, anonKey, sdkVersion, generation, onClaimTransit
21444
21473
  void heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition);
21445
21474
  }, HEARTBEAT_INTERVAL_MS);
21446
21475
  heartbeatTimer.unref();
21447
- registerShutdownHandlers(config2, anonKey, sdkVersion);
21476
+ registerHeartbeatShutdownHook(config2, anonKey, sdkVersion);
21448
21477
  if (config2.verbose) {
21449
21478
  sdkLog("info", "[glasstrace] Heartbeat started (5-minute interval).");
21450
21479
  }
@@ -21454,7 +21483,26 @@ function stopHeartbeat() {
21454
21483
  clearInterval(heartbeatTimer);
21455
21484
  heartbeatTimer = null;
21456
21485
  }
21457
- removeShutdownHandlers();
21486
+ }
21487
+ function checkShutdownMarker(projectRoot) {
21488
+ let fsSync2 = null;
21489
+ let pathSync = null;
21490
+ try {
21491
+ fsSync2 = require("fs");
21492
+ pathSync = require("path");
21493
+ } catch {
21494
+ return { triggered: false };
21495
+ }
21496
+ const root = projectRoot ?? (typeof process !== "undefined" ? process.cwd() : ".");
21497
+ const markerPath = pathSync.join(root, SHUTDOWN_MARKER_RELPATH);
21498
+ if (!fsSync2.existsSync(markerPath)) return { triggered: false };
21499
+ try {
21500
+ fsSync2.unlinkSync(markerPath);
21501
+ } catch {
21502
+ }
21503
+ const shutdown = executeShutdown().catch(() => {
21504
+ });
21505
+ return { triggered: true, shutdown };
21458
21506
  }
21459
21507
  async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition) {
21460
21508
  if (tickInProgress) return;
@@ -21464,6 +21512,14 @@ async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTr
21464
21512
  stopHeartbeat();
21465
21513
  return;
21466
21514
  }
21515
+ const markerResult = checkShutdownMarker();
21516
+ if (markerResult.triggered) {
21517
+ stopHeartbeat();
21518
+ if (markerResult.shutdown) {
21519
+ await markerResult.shutdown;
21520
+ }
21521
+ return;
21522
+ }
21467
21523
  if (Date.now() < backoffUntil) {
21468
21524
  if (config2.verbose) {
21469
21525
  sdkLog("info", "[glasstrace] Heartbeat skipped (rate-limit backoff).");
@@ -21498,35 +21554,26 @@ async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTr
21498
21554
  tickInProgress = false;
21499
21555
  }
21500
21556
  }
21501
- function registerShutdownHandlers(config2, anonKey, sdkVersion) {
21502
- if (typeof process === "undefined" || typeof process.once !== "function") {
21503
- return;
21504
- }
21505
- let shutdownFired = false;
21506
- const handler = (signal) => {
21507
- if (shutdownFired) return;
21508
- shutdownFired = true;
21509
- if (heartbeatTimer !== null) {
21510
- clearInterval(heartbeatTimer);
21511
- heartbeatTimer = null;
21557
+ function registerHeartbeatShutdownHook(config2, anonKey, sdkVersion) {
21558
+ if (shutdownHookRegistered) return;
21559
+ shutdownHookRegistered = true;
21560
+ registerShutdownHook({
21561
+ name: "heartbeat-final-report",
21562
+ priority: HEARTBEAT_SHUTDOWN_PRIORITY,
21563
+ fn: async () => {
21564
+ if (shutdownFired) return;
21565
+ shutdownFired = true;
21566
+ if (heartbeatTimer !== null) {
21567
+ clearInterval(heartbeatTimer);
21568
+ heartbeatTimer = null;
21569
+ }
21570
+ try {
21571
+ const healthReport = collectHealthReport(sdkVersion);
21572
+ await performInit(config2, anonKey, sdkVersion, healthReport);
21573
+ } catch {
21574
+ }
21512
21575
  }
21513
- const healthReport = collectHealthReport(sdkVersion);
21514
- void performInit(config2, anonKey, sdkVersion, healthReport).catch(() => {
21515
- }).finally(() => {
21516
- removeShutdownHandlers();
21517
- process.kill(process.pid, signal);
21518
- });
21519
- };
21520
- _shutdownHandler = handler;
21521
- process.once("SIGTERM", _shutdownHandler);
21522
- process.once("SIGINT", _shutdownHandler);
21523
- }
21524
- function removeShutdownHandlers() {
21525
- if (_shutdownHandler && typeof process !== "undefined") {
21526
- process.removeListener("SIGTERM", _shutdownHandler);
21527
- process.removeListener("SIGINT", _shutdownHandler);
21528
- _shutdownHandler = null;
21529
- }
21576
+ });
21530
21577
  }
21531
21578
 
21532
21579
  // src/runtime-state.ts
@@ -21628,7 +21675,7 @@ function registerGlasstrace(options) {
21628
21675
  setCoreState(CoreState.REGISTERING);
21629
21676
  startRuntimeStateWriter({
21630
21677
  projectRoot: process.cwd(),
21631
- sdkVersion: "0.14.1"
21678
+ sdkVersion: "0.15.1"
21632
21679
  });
21633
21680
  const config2 = resolveConfig(options);
21634
21681
  if (config2.verbose) {
@@ -21644,6 +21691,11 @@ function registerGlasstrace(options) {
21644
21691
  if (config2.verbose) {
21645
21692
  console.info("[glasstrace] Not production-disabled.");
21646
21693
  }
21694
+ const existingProbe = trace.getTracerProvider().getTracer("glasstrace-probe");
21695
+ const anotherProviderRegistered = existingProbe.constructor.name !== "ProxyTracer";
21696
+ if (!anotherProviderRegistered) {
21697
+ registerSignalHandlers();
21698
+ }
21647
21699
  const anonymous = isAnonymousMode(config2);
21648
21700
  let effectiveKey = config2.apiKey;
21649
21701
  initAuthState(anonymous ? AuthState.ANONYMOUS : AuthState.AUTHENTICATED);
@@ -21674,8 +21726,6 @@ function registerGlasstrace(options) {
21674
21726
  }
21675
21727
  setCoreState(CoreState.KEY_PENDING);
21676
21728
  const currentGeneration = registrationGeneration;
21677
- const existingProbe = trace.getTracerProvider().getTracer("glasstrace-probe");
21678
- const anotherProviderRegistered = existingProbe.constructor.name !== "ProxyTracer";
21679
21729
  if (anotherProviderRegistered) {
21680
21730
  if (config2.verbose) {
21681
21731
  console.info("[glasstrace] Another OTel provider detected \u2014 using existing context manager.");
@@ -21790,8 +21840,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
21790
21840
  if (config2.verbose) {
21791
21841
  console.info("[glasstrace] Background init firing.");
21792
21842
  }
21793
- const healthReport = collectHealthReport("0.14.1");
21794
- const initResult = await performInit(config2, anonKeyForInit, "0.14.1", healthReport);
21843
+ const healthReport = collectHealthReport("0.15.1");
21844
+ const initResult = await performInit(config2, anonKeyForInit, "0.15.1", healthReport);
21795
21845
  if (generation !== registrationGeneration) return;
21796
21846
  const currentState = getCoreState();
21797
21847
  if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
@@ -21814,7 +21864,7 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
21814
21864
  }
21815
21865
  maybeInstallConsoleCapture();
21816
21866
  if (didLastInitSucceed()) {
21817
- startHeartbeat(config2, anonKeyForInit, "0.14.1", generation, (newApiKey, accountId) => {
21867
+ startHeartbeat(config2, anonKeyForInit, "0.15.1", generation, (newApiKey, accountId) => {
21818
21868
  setAuthState(AuthState.CLAIMING);
21819
21869
  emitLifecycleEvent("auth:claim_started", { accountId });
21820
21870
  setResolvedApiKey(newApiKey);
@@ -21845,16 +21895,34 @@ function isDiscoveryEnabled(config2) {
21845
21895
 
21846
21896
  // src/config-wrapper.ts
21847
21897
  init_cjs_shims();
21898
+ function isTurbopackBuild() {
21899
+ if (typeof process === "undefined") return false;
21900
+ const argv = Array.isArray(process.argv) ? process.argv : [];
21901
+ if (argv.includes("--webpack")) return false;
21902
+ if (argv.includes("--turbopack")) return true;
21903
+ if (process.env?.TURBOPACK === "1") return true;
21904
+ return false;
21905
+ }
21848
21906
  function withGlasstraceConfig(nextConfig) {
21849
21907
  if (typeof process === "undefined" || typeof process.versions?.node !== "string") {
21850
21908
  return nextConfig != null ? { ...nextConfig } : {};
21851
21909
  }
21852
21910
  const config2 = nextConfig != null ? { ...nextConfig } : {};
21853
- const existingExperimental = config2.experimental ?? {};
21854
- config2.experimental = { ...existingExperimental, serverSourceMaps: true };
21855
- const distDir = typeof config2.distDir === "string" ? config2.distDir : ".next";
21856
- const existingWebpack = config2.webpack;
21857
- config2.webpack = (webpackConfig, context2) => {
21911
+ const bag = config2;
21912
+ const existingExperimental = bag.experimental ?? {};
21913
+ bag.experimental = {
21914
+ ...existingExperimental,
21915
+ serverSourceMaps: true
21916
+ };
21917
+ if (bag.turbopack == null) {
21918
+ bag.turbopack = {};
21919
+ }
21920
+ if (isTurbopackBuild()) {
21921
+ warnTurbopackLimitationOnce();
21922
+ }
21923
+ const distDir = typeof bag.distDir === "string" ? bag.distDir : ".next";
21924
+ const existingWebpack = bag.webpack;
21925
+ bag.webpack = (webpackConfig, context2) => {
21858
21926
  let result = webpackConfig;
21859
21927
  if (typeof existingWebpack === "function") {
21860
21928
  result = existingWebpack(webpackConfig, context2);
@@ -21881,6 +21949,14 @@ function withGlasstraceConfig(nextConfig) {
21881
21949
  };
21882
21950
  return config2;
21883
21951
  }
21952
+ var _turbopackWarningEmitted = false;
21953
+ function warnTurbopackLimitationOnce() {
21954
+ if (_turbopackWarningEmitted) return;
21955
+ _turbopackWarningEmitted = true;
21956
+ console.warn(
21957
+ "[glasstrace] Turbopack detected. Source-map upload currently runs only under webpack \u2014 run `next build --webpack` to upload source maps, or wait for the Turbopack port in a future SDK release."
21958
+ );
21959
+ }
21884
21960
  async function handleSourceMapUpload(distDir) {
21885
21961
  try {
21886
21962
  const apiKey = process.env.GLASSTRACE_API_KEY;