@glasstrace/sdk 0.11.0 → 0.12.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 (48) hide show
  1. package/dist/adapters/drizzle.js +1 -1
  2. package/dist/{chunk-2JUH3VGJ.js → chunk-6GRNJ722.js} +38 -17
  3. package/dist/chunk-6GRNJ722.js.map +1 -0
  4. package/dist/chunk-IPGOKORJ.js +580 -0
  5. package/dist/chunk-IPGOKORJ.js.map +1 -0
  6. package/dist/{chunk-M7RDFOFR.js → chunk-LW7DPKBA.js} +2 -2
  7. package/dist/{chunk-SLSWEQCC.js → chunk-MSMOH6IH.js} +108 -38
  8. package/dist/chunk-MSMOH6IH.js.map +1 -0
  9. package/dist/chunk-NSBPE2FW.js +17 -0
  10. package/dist/{chunk-D3JC3LAK.js → chunk-OKIP4SRG.js} +2 -2
  11. package/dist/cli/init.cjs +454 -329
  12. package/dist/cli/init.cjs.map +1 -1
  13. package/dist/cli/init.d.cts +25 -1
  14. package/dist/cli/init.d.ts +25 -1
  15. package/dist/cli/init.js +114 -4
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/mcp-add.cjs +60 -46
  18. package/dist/cli/mcp-add.cjs.map +1 -1
  19. package/dist/cli/mcp-add.js +3 -3
  20. package/dist/cli/uninit.js +15 -564
  21. package/dist/cli/uninit.js.map +1 -1
  22. package/dist/{esm-POMEQPKL.js → esm-KBPHCVB4.js} +2 -2
  23. package/dist/{getMachineId-bsd-TC3JSTY5.js → getMachineId-bsd-345PYXFX.js} +2 -2
  24. package/dist/{getMachineId-darwin-2SUKQCE6.js → getMachineId-darwin-5L2D25AD.js} +2 -2
  25. package/dist/{getMachineId-linux-PNAFHLXH.js → getMachineId-linux-KJR4P5HN.js} +2 -2
  26. package/dist/{getMachineId-unsupported-L2MNYW3W.js → getMachineId-unsupported-NDNXDYDY.js} +2 -2
  27. package/dist/{getMachineId-win-D6D42WOQ.js → getMachineId-win-T7PJNJXG.js} +2 -2
  28. package/dist/index.cjs +297 -154
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +51 -10
  31. package/dist/index.d.ts +51 -10
  32. package/dist/index.js +133 -78
  33. package/dist/index.js.map +1 -1
  34. package/dist/{source-map-uploader-OFEM54UE.js → source-map-uploader-ZFCYOURS.js} +6 -4
  35. package/package.json +1 -1
  36. package/dist/chunk-2JUH3VGJ.js.map +0 -1
  37. package/dist/chunk-PZ5AY32C.js +0 -10
  38. package/dist/chunk-SLSWEQCC.js.map +0 -1
  39. /package/dist/{chunk-M7RDFOFR.js.map → chunk-LW7DPKBA.js.map} +0 -0
  40. /package/dist/{chunk-PZ5AY32C.js.map → chunk-NSBPE2FW.js.map} +0 -0
  41. /package/dist/{chunk-D3JC3LAK.js.map → chunk-OKIP4SRG.js.map} +0 -0
  42. /package/dist/{esm-POMEQPKL.js.map → esm-KBPHCVB4.js.map} +0 -0
  43. /package/dist/{getMachineId-bsd-TC3JSTY5.js.map → getMachineId-bsd-345PYXFX.js.map} +0 -0
  44. /package/dist/{getMachineId-darwin-2SUKQCE6.js.map → getMachineId-darwin-5L2D25AD.js.map} +0 -0
  45. /package/dist/{getMachineId-linux-PNAFHLXH.js.map → getMachineId-linux-KJR4P5HN.js.map} +0 -0
  46. /package/dist/{getMachineId-unsupported-L2MNYW3W.js.map → getMachineId-unsupported-NDNXDYDY.js.map} +0 -0
  47. /package/dist/{getMachineId-win-D6D42WOQ.js.map → getMachineId-win-T7PJNJXG.js.map} +0 -0
  48. /package/dist/{source-map-uploader-OFEM54UE.js.map → source-map-uploader-ZFCYOURS.js.map} +0 -0
@@ -2,19 +2,21 @@ import {
2
2
  PRESIGNED_THRESHOLD_BYTES,
3
3
  collectSourceMaps,
4
4
  computeBuildHash,
5
+ discoverSourceMapFiles,
5
6
  requestPresignedTokens,
6
7
  submitManifest,
7
8
  uploadSourceMaps,
8
9
  uploadSourceMapsAuto,
9
10
  uploadSourceMapsPresigned,
10
11
  uploadToBlob
11
- } from "./chunk-SLSWEQCC.js";
12
- import "./chunk-D3JC3LAK.js";
13
- import "./chunk-PZ5AY32C.js";
12
+ } from "./chunk-MSMOH6IH.js";
13
+ import "./chunk-OKIP4SRG.js";
14
+ import "./chunk-NSBPE2FW.js";
14
15
  export {
15
16
  PRESIGNED_THRESHOLD_BYTES,
16
17
  collectSourceMaps,
17
18
  computeBuildHash,
19
+ discoverSourceMapFiles,
18
20
  requestPresignedTokens,
19
21
  submitManifest,
20
22
  uploadSourceMaps,
@@ -22,4 +24,4 @@ export {
22
24
  uploadSourceMapsPresigned,
23
25
  uploadToBlob
24
26
  };
25
- //# sourceMappingURL=source-map-uploader-OFEM54UE.js.map
27
+ //# sourceMappingURL=source-map-uploader-ZFCYOURS.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasstrace/sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Glasstrace server-side debugging SDK for AI coding agents",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/anon-key.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, chmod } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { AnonApiKeySchema, createAnonApiKey } from \"@glasstrace/protocol\";\nimport type { AnonApiKey } from \"@glasstrace/protocol\";\n\nconst GLASSTRACE_DIR = \".glasstrace\";\nconst ANON_KEY_FILE = \"anon_key\";\n\n/**\n * In-memory cache for ephemeral keys when filesystem persistence fails.\n * Keyed by resolved project root to support multiple roots in tests.\n */\nconst ephemeralKeyCache = new Map<string, AnonApiKey>();\n\n/**\n * Reads an existing anonymous key from the filesystem.\n * Returns the key if valid, or null if:\n * - The file does not exist\n * - The file content is invalid\n * - An I/O error occurs\n */\nexport async function readAnonKey(projectRoot?: string): Promise<AnonApiKey | null> {\n const root = projectRoot ?? process.cwd();\n const keyPath = join(root, GLASSTRACE_DIR, ANON_KEY_FILE);\n\n try {\n const content = await readFile(keyPath, \"utf-8\");\n const result = AnonApiKeySchema.safeParse(content);\n if (result.success) {\n return result.data;\n }\n } catch {\n // Fall through to check ephemeral cache\n }\n\n // Check in-memory cache (used when filesystem persistence failed)\n const cached = ephemeralKeyCache.get(root);\n if (cached !== undefined) {\n return cached;\n }\n\n return null;\n}\n\n/**\n * Gets an existing anonymous key from the filesystem, or creates a new one.\n *\n * - If file exists and contains a valid key, returns it\n * - If file does not exist or content is invalid, generates a new key via createAnonApiKey()\n * - Writes the new key to `.glasstrace/anon_key`, creating the directory if needed\n * - On file write failure: logs a warning, caches an ephemeral in-memory key so\n * repeated calls in the same process return the same key\n */\nexport async function getOrCreateAnonKey(projectRoot?: string): Promise<AnonApiKey> {\n const root = projectRoot ?? process.cwd();\n const dirPath = join(root, GLASSTRACE_DIR);\n const keyPath = join(dirPath, ANON_KEY_FILE);\n\n // Try reading existing key from filesystem\n const existingKey = await readAnonKey(root);\n if (existingKey !== null) {\n return existingKey;\n }\n\n // Check in-memory cache (used when filesystem is unavailable)\n const cached = ephemeralKeyCache.get(root);\n if (cached !== undefined) {\n return cached;\n }\n\n // Generate a new key\n const newKey = createAnonApiKey();\n\n // Persist to filesystem using atomic create-or-fail (O_CREAT | O_EXCL)\n // to prevent TOCTOU races where concurrent cold starts both generate keys.\n try {\n await mkdir(dirPath, { recursive: true, mode: 0o700 });\n await writeFile(keyPath, newKey, { flag: \"wx\", mode: 0o600 });\n return newKey;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"EEXIST\") {\n // Another process won the race. Retry reading their key with\n // short delays — the winner's writeFile is atomic for small\n // payloads but the filesystem may not have flushed yet.\n for (let attempt = 0; attempt < 3; attempt++) {\n const winnerKey = await readAnonKey(root);\n if (winnerKey !== null) {\n return winnerKey;\n }\n // Short delay before next retry (50ms), skip after final attempt\n if (attempt < 2) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n }\n // All retries exhausted — overwrite as last resort.\n // Use explicit chmod after overwrite since writeFile mode only\n // applies on creation on some platforms.\n try {\n await writeFile(keyPath, newKey, { mode: 0o600 });\n await chmod(keyPath, 0o600);\n return newKey;\n } catch {\n // Overwrite failed — fall through to ephemeral cache\n }\n }\n\n // Non-EEXIST error (EACCES, ENOTDIR, etc.) — cache in memory so\n // repeated calls get the same ephemeral key within this process.\n ephemeralKeyCache.set(root, newKey);\n console.warn(\n `[glasstrace] Failed to persist anonymous key to ${keyPath}: ${err instanceof Error ? err.message : String(err)}. Using ephemeral key.`,\n );\n return newKey;\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,UAAU,WAAW,OAAO,aAAa;AAClD,SAAS,YAAY;AAIrB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAMtB,IAAM,oBAAoB,oBAAI,IAAwB;AAStD,eAAsB,YAAY,aAAkD;AAClF,QAAM,OAAO,eAAe,QAAQ,IAAI;AACxC,QAAM,UAAU,KAAK,MAAM,gBAAgB,aAAa;AAExD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,UAAM,SAAS,iBAAiB,UAAU,OAAO;AACjD,QAAI,OAAO,SAAS;AAClB,aAAO,OAAO;AAAA,IAChB;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,MAAI,WAAW,QAAW;AACxB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAWA,eAAsB,mBAAmB,aAA2C;AAClF,QAAM,OAAO,eAAe,QAAQ,IAAI;AACxC,QAAM,UAAU,KAAK,MAAM,cAAc;AACzC,QAAM,UAAU,KAAK,SAAS,aAAa;AAG3C,QAAM,cAAc,MAAM,YAAY,IAAI;AAC1C,MAAI,gBAAgB,MAAM;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,kBAAkB,IAAI,IAAI;AACzC,MAAI,WAAW,QAAW;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,iBAAiB;AAIhC,MAAI;AACF,UAAM,MAAM,SAAS,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACrD,UAAM,UAAU,SAAS,QAAQ,EAAE,MAAM,MAAM,MAAM,IAAM,CAAC;AAC5D,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,OAAQ,IAA8B;AAC5C,QAAI,SAAS,UAAU;AAIrB,eAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,cAAM,YAAY,MAAM,YAAY,IAAI;AACxC,YAAI,cAAc,MAAM;AACtB,iBAAO;AAAA,QACT;AAEA,YAAI,UAAU,GAAG;AACf,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,QACxD;AAAA,MACF;AAIA,UAAI;AACF,cAAM,UAAU,SAAS,QAAQ,EAAE,MAAM,IAAM,CAAC;AAChD,cAAM,MAAM,SAAS,GAAK;AAC1B,eAAO;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AAIA,sBAAkB,IAAI,MAAM,MAAM;AAClC,YAAQ;AAAA,MACN,mDAAmD,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACjH;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1,10 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- export {
8
- __export
9
- };
10
- //# sourceMappingURL=chunk-PZ5AY32C.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/source-map-uploader.ts","../src/nudge/error-nudge.ts","../src/env-detection.ts","../src/console-capture.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport { execFileSync } from \"node:child_process\";\nimport { sdkLog } from \"./console-capture.js\";\nimport {\n SourceMapUploadResponseSchema,\n type SourceMapUploadResponse,\n PresignedUploadResponseSchema,\n type PresignedUploadResponse,\n SourceMapManifestResponseSchema,\n type SourceMapManifestResponse,\n} from \"@glasstrace/protocol\";\n\nexport interface SourceMapEntry {\n filePath: string;\n content: string;\n}\n\n/**\n * Recursively finds all .map files in the given build directory.\n * Returns relative paths and file contents.\n */\nexport async function collectSourceMaps(\n buildDir: string,\n): Promise<SourceMapEntry[]> {\n const results: SourceMapEntry[] = [];\n\n try {\n await walkDir(buildDir, buildDir, results);\n } catch {\n // Directory doesn't exist or is unreadable — return empty\n return [];\n }\n\n return results;\n}\n\nasync function walkDir(\n baseDir: string,\n currentDir: string,\n results: SourceMapEntry[],\n): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fs.readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n await walkDir(baseDir, fullPath, results);\n } else if (entry.isFile() && entry.name.endsWith(\".map\")) {\n try {\n const content = await fs.readFile(fullPath, \"utf-8\");\n const relativePath = path.relative(baseDir, fullPath).replace(/\\\\/g, \"/\");\n // Strip the trailing .map extension so the key matches the compiled\n // JS path that the runtime uses for stack-frame lookups (e.g.\n // \"static/chunks/main.js\" instead of \"static/chunks/main.js.map\").\n const compiledPath = relativePath.replace(/\\.map$/, \"\");\n results.push({ filePath: compiledPath, content });\n } catch {\n // Skip unreadable files\n }\n }\n }\n}\n\n/**\n * Computes a build hash for source map uploads.\n *\n * First tries `git rev-parse HEAD` to get the git commit SHA.\n * On failure, falls back to a deterministic content hash:\n * sort source map file paths alphabetically, concatenate each as\n * `{relativePath}\\n{fileLength}\\n{fileContent}`, then SHA-256 the result.\n */\nexport async function computeBuildHash(\n maps?: SourceMapEntry[],\n): Promise<string> {\n // Try git first\n try {\n const sha = execFileSync(\"git\", [\"rev-parse\", \"HEAD\"], { encoding: \"utf-8\" }).trim();\n if (sha) {\n return sha;\n }\n } catch {\n // Git not available, fall through to content hash\n }\n\n // Fallback: content-based hash\n const sortedMaps = [...(maps ?? [])].sort((a, b) =>\n a.filePath.localeCompare(b.filePath),\n );\n\n const hashInput = sortedMaps\n .map((m) => `${m.filePath}\\n${m.content.length}\\n${m.content}`)\n .join(\"\");\n\n const hash = crypto.createHash(\"sha256\").update(hashInput).digest(\"hex\");\n return hash;\n}\n\n/**\n * Uploads source maps to the ingestion API.\n *\n * POSTs to `{endpoint}/v1/source-maps` with the API key, build hash,\n * and file entries. Validates the response against SourceMapUploadResponseSchema.\n */\nexport async function uploadSourceMaps(\n apiKey: string,\n endpoint: string,\n buildHash: string,\n maps: SourceMapEntry[],\n): Promise<SourceMapUploadResponse> {\n const body = {\n apiKey,\n buildHash,\n files: maps.map((m) => ({\n filePath: m.filePath,\n sourceMap: m.content,\n })),\n };\n\n const baseUrl = stripTrailingSlashes(endpoint);\n const response = await fetch(`${baseUrl}/v1/source-maps`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n // Consume the response body to release the connection back to the pool.\n // Without this, the underlying TCP socket stays allocated until GC, which\n // causes connection pool exhaustion under sustained error conditions.\n // Wrapped in try-catch so a stream error doesn't mask the HTTP status error.\n try { await response.text(); } catch { /* body drain is best-effort */ }\n throw new Error(\n `Source map upload failed: ${String(response.status)} ${response.statusText}`,\n );\n }\n\n const json: unknown = await response.json();\n return SourceMapUploadResponseSchema.parse(json);\n}\n\n// ---------------------------------------------------------------------------\n// Presigned source map upload (3-phase flow for large builds)\n// ---------------------------------------------------------------------------\n\n/** Builds at or above this byte size route to the presigned upload flow. */\nexport const PRESIGNED_THRESHOLD_BYTES = 4_500_000;\n\n/** Signature for the blob upload function, injectable for testing. */\nexport type BlobUploader = (\n clientToken: string,\n pathname: string,\n content: string,\n) => Promise<{ url: string; size: number }>;\n\n/**\n * Strips trailing slashes from a URL string.\n * Uses an iterative approach to avoid regex (CodeQL js/polynomial-redos).\n */\nfunction stripTrailingSlashes(url: string): string {\n let result = url;\n while (result.endsWith(\"/\")) {\n result = result.slice(0, -1);\n }\n return result;\n}\n\n/**\n * Phase 1: Request presigned upload tokens from the ingestion API.\n *\n * POSTs to `{endpoint}/v1/source-maps/presign` with the build hash and\n * file metadata. Returns presigned tokens for each file that the client\n * uses to upload directly to blob storage.\n */\nexport async function requestPresignedTokens(\n apiKey: string,\n endpoint: string,\n buildHash: string,\n files: Array<{ filePath: string; sizeBytes: number }>,\n): Promise<PresignedUploadResponse> {\n const baseUrl = stripTrailingSlashes(endpoint);\n const response = await fetch(`${baseUrl}/v1/source-maps/presign`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ buildHash, files }),\n });\n\n if (!response.ok) {\n try { await response.text(); } catch { /* body drain is best-effort */ }\n throw new Error(\n `Presigned token request failed: ${String(response.status)} ${response.statusText}`,\n );\n }\n\n const json: unknown = await response.json();\n return PresignedUploadResponseSchema.parse(json);\n}\n\n/**\n * Phase 2: Upload a single source map to blob storage using a presigned token.\n *\n * Dynamically imports `@vercel/blob/client` to avoid bundling the dependency.\n * Throws a descriptive error if the package is not installed.\n */\nexport async function uploadToBlob(\n clientToken: string,\n pathname: string,\n content: string,\n): Promise<{ url: string; size: number }> {\n let mod: { put: (pathname: string, body: Blob, options: { access: string; token: string }) => Promise<{ url: string }> };\n try {\n mod = await import(\"@vercel/blob/client\") as typeof mod;\n } catch (err) {\n // Distinguish \"not installed\" from other import errors\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\") {\n throw new Error(\n \"Presigned upload requires @vercel/blob. Install it: npm install @vercel/blob\",\n );\n }\n throw err;\n }\n\n const result = await mod.put(pathname, new Blob([content]), {\n access: \"public\",\n token: clientToken,\n });\n\n return { url: result.url, size: Buffer.byteLength(content, \"utf-8\") };\n}\n\n/**\n * Phase 3: Submit the upload manifest to finalize a presigned upload.\n *\n * POSTs to `{endpoint}/v1/source-maps/manifest` with the upload ID,\n * build hash, and blob URLs for each uploaded file. The backend activates\n * the source maps for stack trace resolution.\n */\nexport async function submitManifest(\n apiKey: string,\n endpoint: string,\n uploadId: string,\n buildHash: string,\n files: Array<{ filePath: string; sizeBytes: number; blobUrl: string }>,\n): Promise<SourceMapManifestResponse> {\n const baseUrl = stripTrailingSlashes(endpoint);\n const response = await fetch(`${baseUrl}/v1/source-maps/manifest`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ uploadId, buildHash, files }),\n });\n\n if (!response.ok) {\n try { await response.text(); } catch { /* body drain is best-effort */ }\n throw new Error(\n `Source map manifest submission failed: ${String(response.status)} ${response.statusText}`,\n );\n }\n\n const json: unknown = await response.json();\n return SourceMapManifestResponseSchema.parse(json);\n}\n\n/**\n * Orchestrates the 3-phase presigned upload flow.\n *\n * 1. Requests presigned tokens for all source map files\n * 2. Uploads each file to blob storage with a concurrency limit of 5\n * 3. Submits the manifest to finalize the upload\n *\n * Accepts an optional `blobUploader` for test injection; defaults to\n * {@link uploadToBlob}.\n */\nexport async function uploadSourceMapsPresigned(\n apiKey: string,\n endpoint: string,\n buildHash: string,\n maps: SourceMapEntry[],\n blobUploader: BlobUploader = uploadToBlob,\n): Promise<SourceMapManifestResponse> {\n if (maps.length === 0) {\n throw new Error(\"No source maps to upload\");\n }\n\n // Phase 1: request presigned tokens\n const presigned = await requestPresignedTokens(apiKey, endpoint, buildHash,\n maps.map((m) => ({\n filePath: m.filePath,\n sizeBytes: Buffer.byteLength(m.content, \"utf-8\"),\n })),\n );\n\n // Build a lookup map for O(1) access by filePath\n const mapsByPath = new Map(maps.map((m) => [m.filePath, m]));\n\n if (mapsByPath.size !== maps.length) {\n throw new Error(\"Duplicate filePath entries in source maps\");\n }\n\n // Phase 2: upload to blob storage with concurrency limit of 5.\n // Validate all tokens have matching entries before starting any uploads.\n for (const token of presigned.files) {\n if (!mapsByPath.has(token.filePath)) {\n throw new Error(\n `Presigned token for \"${token.filePath}\" has no matching source map entry`,\n );\n }\n }\n\n // Phase 2: upload to blob storage in chunks of CONCURRENCY\n const CONCURRENCY = 5;\n const uploadResults: Array<{ filePath: string; sizeBytes: number; blobUrl: string }> = [];\n\n for (let i = 0; i < presigned.files.length; i += CONCURRENCY) {\n const chunk = presigned.files.slice(i, i + CONCURRENCY);\n const chunkResults = await Promise.all(\n chunk.map(async (token) => {\n const entry = mapsByPath.get(token.filePath)!;\n const result = await blobUploader(token.clientToken, token.pathname, entry.content);\n return {\n filePath: token.filePath,\n sizeBytes: result.size,\n blobUrl: result.url,\n };\n }),\n );\n uploadResults.push(...chunkResults);\n }\n\n // Phase 3: submit manifest\n return submitManifest(apiKey, endpoint, presigned.uploadId, buildHash, uploadResults);\n}\n\n/**\n * Options for {@link uploadSourceMapsAuto}, primarily used for test injection.\n */\nexport interface AutoUploadOptions {\n /** Override blob availability check (for testing). */\n checkBlobAvailable?: () => Promise<boolean>;\n /** Override blob uploader (for testing). */\n blobUploader?: BlobUploader;\n}\n\n/**\n * Automatically routes source map uploads based on total build size.\n *\n * - Below {@link PRESIGNED_THRESHOLD_BYTES}: uses the legacy single-request\n * {@link uploadSourceMaps} endpoint.\n * - At or above the threshold: checks if `@vercel/blob` is available and\n * uses the presigned 3-phase flow. Falls back to legacy with a warning\n * if the package is not installed.\n */\nexport async function uploadSourceMapsAuto(\n apiKey: string,\n endpoint: string,\n buildHash: string,\n maps: SourceMapEntry[],\n options?: AutoUploadOptions,\n): Promise<SourceMapUploadResponse | SourceMapManifestResponse> {\n if (maps.length === 0) {\n throw new Error(\"No source maps to upload\");\n }\n\n const totalBytes = maps.reduce(\n (sum, m) => sum + Buffer.byteLength(m.content, \"utf-8\"),\n 0,\n );\n\n if (totalBytes < PRESIGNED_THRESHOLD_BYTES) {\n return uploadSourceMaps(apiKey, endpoint, buildHash, maps);\n }\n\n // Check if @vercel/blob is available\n const checkAvailable = options?.checkBlobAvailable ?? (async () => {\n try {\n await import(\"@vercel/blob/client\");\n return true;\n } catch {\n return false;\n }\n });\n\n const blobAvailable = await checkAvailable();\n\n if (blobAvailable) {\n return uploadSourceMapsPresigned(\n apiKey, endpoint, buildHash, maps, options?.blobUploader,\n );\n }\n\n // Fall back to legacy upload with a warning\n sdkLog(\"warn\",\n `[glasstrace] Build exceeds 4.5MB (${totalBytes} bytes). Install @vercel/blob for ` +\n `presigned uploads to avoid serverless body size limits. Falling back to legacy upload.`\n );\n\n return uploadSourceMaps(apiKey, endpoint, buildHash, maps);\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveConfig, isProductionDisabled } from \"../env-detection.js\";\n\n/**\n * Module-level flag ensuring the nudge fires at most once per process.\n */\nlet hasFired = false;\n\n/**\n * Strips control characters (except space) from a string to prevent\n * terminal escape sequence injection via error summaries written to stderr.\n */\nfunction sanitize(input: string): string {\n // eslint-disable-next-line no-control-regex\n return input.replace(/[\\x00-\\x1f\\x7f]/g, \"\");\n}\n\n/**\n * Shows a one-time stderr nudge when the SDK captures its first error\n * and the MCP connection marker file is absent.\n *\n * The nudge is suppressed when:\n * - It has already fired in this process\n * - The `.glasstrace/mcp-connected` marker file exists at the project root\n * - The environment is detected as production (and force-enable is off)\n *\n * Uses `process.stderr.write()` instead of `console.error()` to avoid\n * being captured by OpenTelemetry console instrumentation.\n */\nexport function maybeShowMcpNudge(errorSummary: string): void {\n if (hasFired) {\n return;\n }\n\n // Production check — suppress silently, but remember the decision\n // so subsequent calls fast-exit via hasFired without re-running I/O.\n const config = resolveConfig();\n if (isProductionDisabled(config)) {\n hasFired = true;\n return;\n }\n\n // Check for MCP connection marker file.\n // Guard process.cwd() — it throws ENOENT if the working directory has been removed.\n let markerExists = false;\n try {\n const markerPath = join(process.cwd(), \".glasstrace\", \"mcp-connected\");\n markerExists = existsSync(markerPath);\n } catch {\n // Permission denied, ENOENT from cwd(), or other filesystem error — treat as not connected\n markerExists = false;\n }\n\n if (markerExists) {\n hasFired = true;\n return;\n }\n\n // Fire the nudge exactly once\n hasFired = true;\n\n const safe = sanitize(errorSummary);\n process.stderr.write(\n `[glasstrace] Error captured: ${safe}\\n` +\n ` Debug with AI: ask your agent \"What's the latest Glasstrace error?\"\\n` +\n ` Not connected? Run: npx glasstrace mcp add\\n`,\n );\n}\n","import type { GlasstraceEnvVars, GlasstraceOptions } from \"@glasstrace/protocol\";\n\n/**\n * Resolved configuration after merging explicit options with environment variables.\n */\nexport interface ResolvedConfig {\n apiKey: string | undefined;\n endpoint: string;\n forceEnable: boolean;\n verbose: boolean;\n environment: string | undefined;\n coverageMapEnabled: boolean;\n nodeEnv: string | undefined;\n vercelEnv: string | undefined;\n}\n\nconst DEFAULT_ENDPOINT = \"https://api.glasstrace.dev\";\n\n/**\n * Reads all recognized Glasstrace environment variables from process.env.\n * Returns undefined for any variable not set. Never throws.\n */\nexport function readEnvVars(): GlasstraceEnvVars {\n return {\n GLASSTRACE_API_KEY: process.env.GLASSTRACE_API_KEY?.trim() || undefined,\n GLASSTRACE_FORCE_ENABLE: process.env.GLASSTRACE_FORCE_ENABLE,\n GLASSTRACE_ENV: process.env.GLASSTRACE_ENV,\n GLASSTRACE_COVERAGE_MAP: process.env.GLASSTRACE_COVERAGE_MAP,\n NODE_ENV: process.env.NODE_ENV,\n VERCEL_ENV: process.env.VERCEL_ENV,\n };\n}\n\n/**\n * Merges explicit GlasstraceOptions with environment variables.\n * Explicit options take precedence over environment variables.\n */\nexport function resolveConfig(options?: GlasstraceOptions): ResolvedConfig {\n const env = readEnvVars();\n\n return {\n apiKey: options?.apiKey ?? env.GLASSTRACE_API_KEY,\n endpoint: options?.endpoint ?? DEFAULT_ENDPOINT,\n forceEnable: options?.forceEnable ?? env.GLASSTRACE_FORCE_ENABLE === \"true\",\n verbose: options?.verbose ?? false,\n environment: env.GLASSTRACE_ENV,\n coverageMapEnabled: env.GLASSTRACE_COVERAGE_MAP === \"true\",\n nodeEnv: env.NODE_ENV,\n vercelEnv: env.VERCEL_ENV,\n };\n}\n\n/**\n * Returns true when the SDK should be inactive (production detected without force-enable).\n * Logic order:\n * 1. forceEnable === true → return false (override)\n * 2. NODE_ENV === 'production' → return true\n * 3. VERCEL_ENV === 'production' → return true\n * 4. Otherwise → return false\n */\nexport function isProductionDisabled(config: ResolvedConfig): boolean {\n if (config.forceEnable) {\n return false;\n }\n if (config.nodeEnv === \"production\") {\n return true;\n }\n if (config.vercelEnv === \"production\") {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true when no API key is configured (anonymous mode).\n * Treats undefined, empty string, whitespace-only, and gt_anon_* keys as anonymous.\n */\nexport function isAnonymousMode(config: ResolvedConfig): boolean {\n if (config.apiKey === undefined) {\n return true;\n }\n if (config.apiKey.trim() === \"\") {\n return true;\n }\n if (config.apiKey.startsWith(\"gt_anon_\")) {\n return true;\n }\n return false;\n}\n","/**\n * Console error/warn capture module.\n *\n * When enabled, monkey-patches `console.error` and `console.warn` to record\n * their output as OTel span events on the currently active span. SDK-internal\n * log messages (prefixed with \"[glasstrace]\") are never captured.\n */\n\nimport { maybeShowMcpNudge } from \"./nudge/error-nudge.js\";\n\n/**\n * Module-level flag to suppress capture of SDK-internal log messages.\n * Set to `true` before calling `console.warn`/`console.error` from SDK code,\n * then reset to `false` immediately after.\n */\nexport let isGlasstraceLog = false;\n\n/** Saved reference to the original `console.error`. */\nlet originalError: typeof console.error | null = null;\n\n/** Saved reference to the original `console.warn`. */\nlet originalWarn: typeof console.warn | null = null;\n\n/** Whether the console capture is currently installed. */\nlet installed = false;\n\n/** Cached OTel API module reference, resolved at install time. */\nlet otelApi: typeof import(\"@opentelemetry/api\") | null = null;\n\n/**\n * Formats console arguments into a single string for span event attributes.\n * Uses best-effort serialization: strings pass through, Errors preserve their\n * stack trace, and other values are JSON-stringified with a String() fallback.\n */\nfunction formatArgs(args: unknown[]): string {\n return args\n .map((arg) => {\n if (typeof arg === \"string\") return arg;\n if (arg instanceof Error) return arg.stack ?? arg.message;\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n })\n .join(\" \");\n}\n\n/**\n * Returns `true` if the first argument is a string starting with \"[glasstrace]\".\n * Used to skip capture of SDK-internal log messages without requiring every\n * call site to set the `isGlasstraceLog` flag.\n */\nfunction isSdkMessage(args: unknown[]): boolean {\n return typeof args[0] === \"string\" && args[0].startsWith(\"[glasstrace]\");\n}\n\n/**\n * Installs console capture by replacing `console.error` and `console.warn`\n * with wrappers that record span events on the active OTel span.\n *\n * Must be called after OTel is configured so the API module is available.\n * Safe to call multiple times; subsequent calls are no-ops.\n */\nexport async function installConsoleCapture(): Promise<void> {\n if (installed) return;\n\n // Resolve OTel API at install time via dynamic import so that:\n // 1. tsup inlines @opentelemetry/api into the bundle (it's in noExternal)\n // 2. vitest's vi.doMock can intercept this import for testing\n try {\n otelApi = await import(\"@opentelemetry/api\");\n } catch {\n otelApi = null;\n }\n\n originalError = console.error;\n originalWarn = console.warn;\n installed = true;\n\n console.error = (...args: unknown[]) => {\n // Always call the original first to preserve developer experience\n originalError!.apply(console, args);\n\n // Skip SDK-internal messages and flagged messages\n if (isGlasstraceLog || isSdkMessage(args)) return;\n\n if (otelApi) {\n const span = otelApi.trace.getSpan(otelApi.context.active());\n if (span) {\n const formattedMessage = formatArgs(args);\n span.addEvent(\"console.error\", {\n \"console.message\": formattedMessage,\n });\n // Show one-time MCP connection nudge on first captured error\n try {\n maybeShowMcpNudge(formattedMessage);\n } catch {\n // Never allow the monkey-patched console.error wrapper to throw\n }\n }\n }\n };\n\n console.warn = (...args: unknown[]) => {\n originalWarn!.apply(console, args);\n\n if (isGlasstraceLog || isSdkMessage(args)) return;\n\n if (otelApi) {\n const span = otelApi.trace.getSpan(otelApi.context.active());\n if (span) {\n span.addEvent(\"console.warn\", {\n \"console.message\": formatArgs(args),\n });\n }\n }\n };\n}\n\n/**\n * Restores the original `console.error` and `console.warn` methods.\n * Primarily intended for use in tests.\n */\nexport function uninstallConsoleCapture(): void {\n if (!installed) return;\n\n if (originalError) console.error = originalError;\n if (originalWarn) console.warn = originalWarn;\n\n originalError = null;\n originalWarn = null;\n otelApi = null;\n installed = false;\n}\n\n/**\n * Logs a message from SDK-internal code without triggering console capture.\n *\n * Use this helper in new SDK code instead of bare `console.warn(...)` calls\n * to prevent SDK log messages from being recorded as user-facing span events.\n *\n * @param level - The console log level to use.\n * @param message - The message to log.\n */\nexport function sdkLog(level: \"warn\" | \"info\" | \"error\", message: string): void {\n isGlasstraceLog = true;\n try {\n console[level](message);\n } finally {\n isGlasstraceLog = false;\n }\n}\n"],"mappings":"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","names":[]}