@glasstrace/sdk 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +78 -1
  2. package/dist/{chunk-55FBXXER.js → chunk-2M57EO6U.js} +2 -2
  3. package/dist/chunk-3LILTM3T.js +384 -0
  4. package/dist/chunk-3LILTM3T.js.map +1 -0
  5. package/dist/{chunk-KE7MCPO5.js → chunk-4EZ6JTDG.js} +2 -2
  6. package/dist/{chunk-67RIOAXV.js → chunk-6RNBUUBR.js} +2 -2
  7. package/dist/{chunk-DO2YPMQ5.js → chunk-C567H5EQ.js} +23 -5
  8. package/dist/chunk-C567H5EQ.js.map +1 -0
  9. package/dist/{chunk-UGJ3X4CT.js → chunk-DST4UBXU.js} +2 -2
  10. package/dist/{chunk-DXRZKKSO.js → chunk-NB7GJE4S.js} +2 -4
  11. package/dist/chunk-NB7GJE4S.js.map +1 -0
  12. package/dist/{chunk-HAU66QBQ.js → chunk-P4OYPFQ5.js} +9 -9
  13. package/dist/chunk-P4OYPFQ5.js.map +1 -0
  14. package/dist/{chunk-LU3PPAOQ.js → chunk-UJ2JC7PZ.js} +4 -4
  15. package/dist/chunk-UJ2JC7PZ.js.map +1 -0
  16. package/dist/{chunk-TQ54WLCZ.js → chunk-X5MAXP5T.js} +2 -1
  17. package/dist/{chunk-ZBTC5QIQ.js → chunk-Z35HKVSO.js} +137 -10
  18. package/dist/chunk-Z35HKVSO.js.map +1 -0
  19. package/dist/cli/init.cjs +1313 -1118
  20. package/dist/cli/init.cjs.map +1 -1
  21. package/dist/cli/init.js +456 -72
  22. package/dist/cli/init.js.map +1 -1
  23. package/dist/cli/mcp-add.cjs +257 -85
  24. package/dist/cli/mcp-add.cjs.map +1 -1
  25. package/dist/cli/mcp-add.d.cts +35 -4
  26. package/dist/cli/mcp-add.d.ts +35 -4
  27. package/dist/cli/mcp-add.js +61 -25
  28. package/dist/cli/mcp-add.js.map +1 -1
  29. package/dist/cli/status.cjs.map +1 -1
  30. package/dist/cli/status.js +1 -1
  31. package/dist/cli/uninit.cjs +1 -1
  32. package/dist/cli/uninit.cjs.map +1 -1
  33. package/dist/cli/uninit.js +4 -4
  34. package/dist/cli/validate.cjs +14162 -2
  35. package/dist/cli/validate.cjs.map +1 -1
  36. package/dist/cli/validate.d.cts +7 -3
  37. package/dist/cli/validate.d.ts +7 -3
  38. package/dist/cli/validate.js +25 -2
  39. package/dist/cli/validate.js.map +1 -1
  40. package/dist/edge-entry.js +2 -2
  41. package/dist/index.cjs +423 -12
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.js +5 -5
  44. package/dist/{monorepo-N5Z63XP7.js → monorepo-PFVNPQ6X.js} +3 -3
  45. package/dist/node-entry.cjs +423 -12
  46. package/dist/node-entry.cjs.map +1 -1
  47. package/dist/node-entry.js +7 -7
  48. package/dist/node-subpath.js +3 -3
  49. package/dist/{source-map-uploader-BJIXRLJ6.js → source-map-uploader-DPUUCLNW.js} +3 -3
  50. package/package.json +1 -1
  51. package/dist/chunk-DO2YPMQ5.js.map +0 -1
  52. package/dist/chunk-DXRZKKSO.js.map +0 -1
  53. package/dist/chunk-HAU66QBQ.js.map +0 -1
  54. package/dist/chunk-IP4NMDJK.js +0 -98
  55. package/dist/chunk-IP4NMDJK.js.map +0 -1
  56. package/dist/chunk-LU3PPAOQ.js.map +0 -1
  57. package/dist/chunk-O63DJKIJ.js +0 -460
  58. package/dist/chunk-O63DJKIJ.js.map +0 -1
  59. package/dist/chunk-ZBTC5QIQ.js.map +0 -1
  60. /package/dist/{chunk-55FBXXER.js.map → chunk-2M57EO6U.js.map} +0 -0
  61. /package/dist/{chunk-KE7MCPO5.js.map → chunk-4EZ6JTDG.js.map} +0 -0
  62. /package/dist/{chunk-67RIOAXV.js.map → chunk-6RNBUUBR.js.map} +0 -0
  63. /package/dist/{chunk-UGJ3X4CT.js.map → chunk-DST4UBXU.js.map} +0 -0
  64. /package/dist/{chunk-TQ54WLCZ.js.map → chunk-X5MAXP5T.js.map} +0 -0
  65. /package/dist/{monorepo-N5Z63XP7.js.map → monorepo-PFVNPQ6X.js.map} +0 -0
  66. /package/dist/{source-map-uploader-BJIXRLJ6.js.map → source-map-uploader-DPUUCLNW.js.map} +0 -0
package/dist/index.js CHANGED
@@ -12,12 +12,12 @@ import {
12
12
  registerGlasstrace,
13
13
  waitForReady,
14
14
  withGlasstraceConfig
15
- } from "./chunk-ZBTC5QIQ.js";
15
+ } from "./chunk-Z35HKVSO.js";
16
16
  import {
17
17
  GlasstraceSpanProcessor,
18
18
  SdkError,
19
19
  captureCorrelationId
20
- } from "./chunk-67RIOAXV.js";
20
+ } from "./chunk-6RNBUUBR.js";
21
21
  import "./chunk-DQ25VOKK.js";
22
22
  import "./chunk-3TU62WD6.js";
23
23
  import {
@@ -27,7 +27,7 @@ import {
27
27
  performInit,
28
28
  saveCachedConfig,
29
29
  sendInitRequest
30
- } from "./chunk-DO2YPMQ5.js";
30
+ } from "./chunk-C567H5EQ.js";
31
31
  import {
32
32
  isAnonymousMode,
33
33
  isProductionDisabled,
@@ -37,10 +37,10 @@ import {
37
37
  import {
38
38
  getOrCreateAnonKey,
39
39
  readAnonKey
40
- } from "./chunk-IP4NMDJK.js";
40
+ } from "./chunk-3LILTM3T.js";
41
41
  import {
42
42
  deriveSessionId
43
- } from "./chunk-TQ54WLCZ.js";
43
+ } from "./chunk-X5MAXP5T.js";
44
44
  import "./chunk-NSBPE2FW.js";
45
45
  export {
46
46
  GlasstraceExporter,
@@ -3,8 +3,8 @@ import {
3
3
  isMonorepoRoot,
4
4
  parsePnpmWorkspaceYaml,
5
5
  resolveProjectRoot
6
- } from "./chunk-55FBXXER.js";
7
- import "./chunk-DXRZKKSO.js";
6
+ } from "./chunk-2M57EO6U.js";
7
+ import "./chunk-NB7GJE4S.js";
8
8
  import "./chunk-NSBPE2FW.js";
9
9
  export {
10
10
  findNextJsApps,
@@ -12,4 +12,4 @@ export {
12
12
  parsePnpmWorkspaceYaml,
13
13
  resolveProjectRoot
14
14
  };
15
- //# sourceMappingURL=monorepo-N5Z63XP7.js.map
15
+ //# sourceMappingURL=monorepo-PFVNPQ6X.js.map
@@ -17103,6 +17103,7 @@ function firstToken(value) {
17103
17103
  init_dist();
17104
17104
  var GLASSTRACE_DIR = ".glasstrace";
17105
17105
  var ANON_KEY_FILE = "anon_key";
17106
+ var CLAIMED_KEY_FILE = "claimed-key";
17106
17107
  var fsPathCache;
17107
17108
  async function loadFsPath() {
17108
17109
  if (fsPathCache !== void 0) return fsPathCache;
@@ -17139,6 +17140,22 @@ async function readAnonKey(projectRoot) {
17139
17140
  }
17140
17141
  return null;
17141
17142
  }
17143
+ async function readClaimedKey(projectRoot) {
17144
+ const root = projectRoot ?? process.cwd();
17145
+ const modules = await loadFsPath();
17146
+ if (!modules) return null;
17147
+ const keyPath = modules.path.join(root, GLASSTRACE_DIR, CLAIMED_KEY_FILE);
17148
+ try {
17149
+ const content = await modules.fs.readFile(keyPath, "utf-8");
17150
+ const trimmed = content.trim();
17151
+ const parsed = DevApiKeySchema.safeParse(trimmed);
17152
+ if (parsed.success) {
17153
+ return parsed.data;
17154
+ }
17155
+ } catch {
17156
+ }
17157
+ return null;
17158
+ }
17142
17159
  async function getOrCreateAnonKey(projectRoot) {
17143
17160
  const root = projectRoot ?? process.cwd();
17144
17161
  const existingKey = await readAnonKey(root);
@@ -17475,8 +17492,259 @@ function sleep(ms, scheduler, signal) {
17475
17492
  });
17476
17493
  }
17477
17494
 
17478
- // src/init-client.ts
17495
+ // src/mcp-runtime.ts
17496
+ var import_node_crypto = require("node:crypto");
17497
+ init_dist();
17498
+ var MCP_ENDPOINT = "https://api.glasstrace.dev/mcp";
17499
+ var fsPathCache2;
17500
+ async function loadFsPath2() {
17501
+ if (fsPathCache2 !== void 0) return fsPathCache2;
17502
+ try {
17503
+ const [fs4, path4] = await Promise.all([
17504
+ import("node:fs/promises"),
17505
+ import("node:path")
17506
+ ]);
17507
+ fsPathCache2 = { fs: fs4, path: path4 };
17508
+ return fsPathCache2;
17509
+ } catch {
17510
+ fsPathCache2 = null;
17511
+ return null;
17512
+ }
17513
+ }
17514
+ function identityFingerprint(token) {
17515
+ return `sha256:${(0, import_node_crypto.createHash)("sha256").update(token).digest("hex")}`;
17516
+ }
17517
+ function mcpConfigMatches(existingContent, expectedContent) {
17518
+ const trimmedExpected = expectedContent.trim();
17519
+ try {
17520
+ const existingParsed = JSON.parse(existingContent);
17521
+ const expectedParsed = JSON.parse(trimmedExpected);
17522
+ return JSON.stringify(canonicalize(existingParsed)) === JSON.stringify(canonicalize(expectedParsed));
17523
+ } catch {
17524
+ }
17525
+ return existingContent.trim() === trimmedExpected;
17526
+ }
17527
+ function canonicalize(value) {
17528
+ if (Array.isArray(value)) {
17529
+ return value.map(canonicalize);
17530
+ }
17531
+ if (value !== null && typeof value === "object") {
17532
+ const obj = value;
17533
+ const sorted = {};
17534
+ for (const key of Object.keys(obj).sort()) {
17535
+ sorted[key] = canonicalize(obj[key]);
17536
+ }
17537
+ return sorted;
17538
+ }
17539
+ return value;
17540
+ }
17541
+ function readEnvLocalApiKey(content) {
17542
+ let last = null;
17543
+ const regex = /^\s*GLASSTRACE_API_KEY\s*=\s*(.*)$/gm;
17544
+ let match;
17545
+ while ((match = regex.exec(content)) !== null) {
17546
+ const raw = match[1].trim();
17547
+ if (raw === "") continue;
17548
+ const unquoted = raw.replace(/^(['"])(.*)\1$/, "$2");
17549
+ if (unquoted === "" || unquoted === "your_key_here") continue;
17550
+ last = unquoted;
17551
+ }
17552
+ return last;
17553
+ }
17554
+ async function resolveEffectiveMcpCredential(projectRoot) {
17555
+ const root = projectRoot ?? process.cwd();
17556
+ const warnings = [];
17557
+ const envLocalKey = await readEnvLocalDevKey(root, warnings);
17558
+ const claimedKey = envLocalKey === null ? await readClaimedKey(root) : null;
17559
+ const anonKey = await readAnonKey(root);
17560
+ let effective = null;
17561
+ if (envLocalKey !== null) {
17562
+ effective = { source: "env-local", key: envLocalKey };
17563
+ } else if (claimedKey !== null) {
17564
+ effective = { source: "claimed-key", key: claimedKey };
17565
+ warnings.push("claimed-key-only");
17566
+ } else if (anonKey !== null) {
17567
+ effective = { source: "anon", key: anonKey };
17568
+ }
17569
+ return { effective, anonKey, warnings };
17570
+ }
17571
+ async function readEnvLocalDevKey(root, warnings) {
17572
+ const modules = await loadFsPath2();
17573
+ if (!modules) return null;
17574
+ const envPath = modules.path.join(root, ".env.local");
17575
+ let content;
17576
+ try {
17577
+ content = await modules.fs.readFile(envPath, "utf-8");
17578
+ } catch {
17579
+ return null;
17580
+ }
17581
+ const raw = readEnvLocalApiKey(content);
17582
+ if (raw === null) return null;
17583
+ const parsed = DevApiKeySchema.safeParse(raw);
17584
+ if (!parsed.success) {
17585
+ warnings.push("malformed-env-local");
17586
+ return null;
17587
+ }
17588
+ return parsed.data;
17589
+ }
17590
+ var MCP_MARKER_FILE = "mcp-connected";
17479
17591
  var GLASSTRACE_DIR2 = ".glasstrace";
17592
+ async function readMcpMarker(projectRoot) {
17593
+ const root = projectRoot ?? process.cwd();
17594
+ const modules = await loadFsPath2();
17595
+ if (!modules) return { status: "absent" };
17596
+ const markerPath = modules.path.join(root, GLASSTRACE_DIR2, MCP_MARKER_FILE);
17597
+ let content;
17598
+ try {
17599
+ content = await modules.fs.readFile(markerPath, "utf-8");
17600
+ } catch {
17601
+ return { status: "absent" };
17602
+ }
17603
+ let parsed;
17604
+ try {
17605
+ parsed = JSON.parse(content);
17606
+ } catch {
17607
+ return { status: "corrupted" };
17608
+ }
17609
+ if (parsed === null || typeof parsed !== "object") {
17610
+ return { status: "corrupted" };
17611
+ }
17612
+ const obj = parsed;
17613
+ const version2 = obj["version"];
17614
+ if (version2 === void 0) {
17615
+ const keyHash = obj["keyHash"];
17616
+ if (typeof keyHash !== "string" || keyHash === "") {
17617
+ return { status: "corrupted" };
17618
+ }
17619
+ return {
17620
+ status: "valid",
17621
+ credentialSource: "anon",
17622
+ credentialHash: keyHash
17623
+ };
17624
+ }
17625
+ if (version2 === 2) {
17626
+ const source = obj["credentialSource"];
17627
+ const hash2 = obj["credentialHash"];
17628
+ if (source !== "env-local" && source !== "claimed-key" && source !== "anon" || typeof hash2 !== "string" || hash2 === "") {
17629
+ return { status: "corrupted" };
17630
+ }
17631
+ return {
17632
+ status: "valid",
17633
+ credentialSource: source,
17634
+ credentialHash: hash2
17635
+ };
17636
+ }
17637
+ if (typeof version2 === "number" && version2 > 2) {
17638
+ return { status: "unknown-version" };
17639
+ }
17640
+ return { status: "corrupted" };
17641
+ }
17642
+ async function writeMcpMarker(projectRoot, target) {
17643
+ const modules = await loadFsPath2();
17644
+ if (!modules) return false;
17645
+ const dirPath = modules.path.join(projectRoot, GLASSTRACE_DIR2);
17646
+ const markerPath = modules.path.join(dirPath, MCP_MARKER_FILE);
17647
+ const state = await readMcpMarker(projectRoot);
17648
+ if (state.status === "valid" && state.credentialSource === target.credentialSource && state.credentialHash === target.credentialHash) {
17649
+ return false;
17650
+ }
17651
+ await modules.fs.mkdir(dirPath, { recursive: true, mode: 448 });
17652
+ const body = JSON.stringify(
17653
+ {
17654
+ version: 2,
17655
+ credentialSource: target.credentialSource,
17656
+ credentialHash: target.credentialHash,
17657
+ configuredAt: (/* @__PURE__ */ new Date()).toISOString()
17658
+ },
17659
+ null,
17660
+ 2
17661
+ );
17662
+ await modules.fs.writeFile(markerPath, body, { mode: 384 });
17663
+ await modules.fs.chmod(markerPath, 384);
17664
+ return true;
17665
+ }
17666
+ var MCP_CONFIG_FILE = "mcp.json";
17667
+ var refreshNudgeEmitted = false;
17668
+ function emitRefreshNudge(persistedSource) {
17669
+ if (refreshNudgeEmitted) return;
17670
+ refreshNudgeEmitted = true;
17671
+ try {
17672
+ if (persistedSource === "claimed-key") {
17673
+ process.stderr.write(
17674
+ "[glasstrace] MCP config refreshed for the new credential. Copy .glasstrace/claimed-key into .env.local so Codex can pick it up on next restart.\n"
17675
+ );
17676
+ } else {
17677
+ process.stderr.write(
17678
+ "[glasstrace] MCP config refreshed for the new credential.\n"
17679
+ );
17680
+ }
17681
+ } catch {
17682
+ }
17683
+ }
17684
+ function genericMcpConfigContent(endpoint, bearer) {
17685
+ return JSON.stringify(
17686
+ {
17687
+ mcpServers: {
17688
+ glasstrace: {
17689
+ url: endpoint,
17690
+ headers: {
17691
+ Authorization: `Bearer ${bearer}`
17692
+ }
17693
+ }
17694
+ }
17695
+ },
17696
+ null,
17697
+ 2
17698
+ );
17699
+ }
17700
+ async function refreshGenericMcpConfigAtRuntime(projectRoot, effective, anonKeyOnDisk) {
17701
+ if (effective === null || effective.source === "anon") {
17702
+ return { action: "skipped-anon-source" };
17703
+ }
17704
+ if (anonKeyOnDisk === null) {
17705
+ return { action: "absent" };
17706
+ }
17707
+ const modules = await loadFsPath2();
17708
+ if (!modules) return { action: "absent" };
17709
+ const dirPath = modules.path.join(projectRoot, GLASSTRACE_DIR2);
17710
+ const configPath = modules.path.join(dirPath, MCP_CONFIG_FILE);
17711
+ const tmpPath = configPath + ".tmp";
17712
+ let existing;
17713
+ try {
17714
+ existing = await modules.fs.readFile(configPath, "utf-8");
17715
+ } catch (err) {
17716
+ const code = err.code;
17717
+ if (code === "ENOENT") {
17718
+ return { action: "absent" };
17719
+ }
17720
+ return { action: "preserved" };
17721
+ }
17722
+ const expectedAnon = genericMcpConfigContent(MCP_ENDPOINT, anonKeyOnDisk);
17723
+ if (!mcpConfigMatches(existing, expectedAnon)) {
17724
+ return { action: "preserved" };
17725
+ }
17726
+ const replacement = genericMcpConfigContent(MCP_ENDPOINT, effective.key);
17727
+ try {
17728
+ await modules.fs.writeFile(tmpPath, replacement, { mode: 384 });
17729
+ await modules.fs.chmod(tmpPath, 384);
17730
+ await modules.fs.rename(tmpPath, configPath);
17731
+ await writeMcpMarker(projectRoot, {
17732
+ credentialSource: effective.source,
17733
+ credentialHash: identityFingerprint(effective.key)
17734
+ });
17735
+ } catch {
17736
+ try {
17737
+ await modules.fs.unlink(tmpPath);
17738
+ } catch {
17739
+ }
17740
+ return { action: "preserved" };
17741
+ }
17742
+ emitRefreshNudge(effective.source);
17743
+ return { action: "rewrote" };
17744
+ }
17745
+
17746
+ // src/init-client.ts
17747
+ var GLASSTRACE_DIR3 = ".glasstrace";
17480
17748
  var CONFIG_FILE = "config";
17481
17749
  var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
17482
17750
  var INIT_TIMEOUT_MS = 1e4;
@@ -17513,7 +17781,7 @@ function loadCachedConfig(projectRoot) {
17513
17781
  const modules = loadFsSyncOrNull();
17514
17782
  if (!modules) return null;
17515
17783
  const root = projectRoot ?? process.cwd();
17516
- const configPath = modules.join(root, GLASSTRACE_DIR2, CONFIG_FILE);
17784
+ const configPath = modules.join(root, GLASSTRACE_DIR3, CONFIG_FILE);
17517
17785
  try {
17518
17786
  const content = modules.readFileSync(configPath, "utf-8");
17519
17787
  const parsed = JSON.parse(content);
@@ -17539,7 +17807,7 @@ async function saveCachedConfig(response, projectRoot) {
17539
17807
  const modules = await loadFsPathAsync();
17540
17808
  if (!modules) return;
17541
17809
  const root = projectRoot ?? process.cwd();
17542
- const dirPath = modules.path.join(root, GLASSTRACE_DIR2);
17810
+ const dirPath = modules.path.join(root, GLASSTRACE_DIR3);
17543
17811
  const configPath = modules.path.join(dirPath, CONFIG_FILE);
17544
17812
  const tmpPath = `${configPath}.tmp`;
17545
17813
  try {
@@ -17665,11 +17933,11 @@ async function writeClaimedKey(newApiKey, projectRoot) {
17665
17933
  );
17666
17934
  } catch {
17667
17935
  }
17668
- return;
17936
+ return { persisted: "env-local" };
17669
17937
  }
17670
17938
  let claimedKeyWritten = false;
17671
17939
  try {
17672
- const dirPath = modules.path.join(root, GLASSTRACE_DIR2);
17940
+ const dirPath = modules.path.join(root, GLASSTRACE_DIR3);
17673
17941
  await modules.fs.mkdir(dirPath, { recursive: true, mode: 448 });
17674
17942
  await modules.fs.chmod(dirPath, 448);
17675
17943
  const claimedKeyPath = modules.path.join(dirPath, "claimed-key");
@@ -17688,7 +17956,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
17688
17956
  );
17689
17957
  } catch {
17690
17958
  }
17691
- return;
17959
+ return { persisted: "claimed-key" };
17692
17960
  }
17693
17961
  }
17694
17962
  try {
@@ -17697,6 +17965,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
17697
17965
  );
17698
17966
  } catch {
17699
17967
  }
17968
+ return { persisted: "none" };
17700
17969
  }
17701
17970
  async function performInit(config2, anonKey, sdkVersion, healthReport) {
17702
17971
  lastInitSucceeded = false;
@@ -17728,10 +17997,23 @@ async function performInit(config2, anonKey, sdkVersion, healthReport) {
17728
17997
  lastInitSucceeded = true;
17729
17998
  await saveCachedConfig(result);
17730
17999
  if (result.claimResult) {
18000
+ let persisted = "none";
17731
18001
  try {
17732
- await writeClaimedKey(result.claimResult.newApiKey);
18002
+ const w = await writeClaimedKey(result.claimResult.newApiKey);
18003
+ persisted = w.persisted;
17733
18004
  } catch {
17734
18005
  }
18006
+ if (persisted !== "none") {
18007
+ try {
18008
+ const resolved = await resolveEffectiveMcpCredential();
18009
+ await refreshGenericMcpConfigAtRuntime(
18010
+ process.cwd(),
18011
+ resolved.effective,
18012
+ resolved.anonKey
18013
+ );
18014
+ } catch {
18015
+ }
18016
+ }
17735
18017
  return { claimResult: result.claimResult };
17736
18018
  }
17737
18019
  return null;
@@ -17827,6 +18109,128 @@ init_esm();
17827
18109
  init_dist();
17828
18110
  init_console_capture();
17829
18111
  init_error_nudge();
18112
+
18113
+ // src/error-response-body.ts
18114
+ var ERROR_RESPONSE_BODY_MAX_BYTES = 4096;
18115
+ var ERROR_RESPONSE_BODY_TRUNCATION_MARKER = "...[truncated]";
18116
+ var REDACTED = "[REDACTED]";
18117
+ var ERROR_STATUS_MIN = 400;
18118
+ var ERROR_STATUS_MAX = 599;
18119
+ function isHttpErrorStatus(status) {
18120
+ let numeric;
18121
+ if (typeof status === "number") {
18122
+ numeric = status;
18123
+ } else if (typeof status === "string" && status.length > 0) {
18124
+ numeric = Number(status);
18125
+ } else {
18126
+ return false;
18127
+ }
18128
+ if (!Number.isFinite(numeric)) return false;
18129
+ return numeric >= ERROR_STATUS_MIN && numeric <= ERROR_STATUS_MAX;
18130
+ }
18131
+ var REDACTION_PATTERNS = [
18132
+ // Order matters: redact specific token shapes BEFORE the generic
18133
+ // key=value catcher so a literal `Bearer eyJ…` collapses into a single
18134
+ // [REDACTED] and the JWT regex does not separately match the suffix.
18135
+ {
18136
+ name: "bearer",
18137
+ // Case-insensitive on the scheme: HTTP frameworks and proxies
18138
+ // round-trip the auth scheme with inconsistent casing
18139
+ // (`Bearer`, `bearer`, `BEARER`), and a real token leaks just as
18140
+ // badly under any of them.
18141
+ pattern: /\bBearer\s+[A-Za-z0-9._\-+/=]+/gi
18142
+ },
18143
+ {
18144
+ name: "jwt",
18145
+ // Three base64url segments separated by dots. Real JWTs encode at
18146
+ // minimum a small JSON header in the first segment, which alone is
18147
+ // typically ≥10 chars after base64url; a 16-char floor avoids false
18148
+ // positives on dotted text like a stack-trace frame
18149
+ // (`react.dom.server`) while still catching every real JWT we have
18150
+ // seen in the wild. Anchored with word boundaries on both sides so
18151
+ // a 3-dot semantic version like "next@15.4.1.2" does not match.
18152
+ pattern: /\b[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
18153
+ },
18154
+ {
18155
+ name: "glasstrace-api-key",
18156
+ // gt_dev_* and gt_anon_* keys are >=24 chars of [A-Za-z0-9].
18157
+ pattern: /\bgt_(?:dev|anon)_[A-Za-z0-9]{16,}\b/g
18158
+ },
18159
+ {
18160
+ name: "aws-access-key",
18161
+ // 20-char prefix-fixed identifier.
18162
+ pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
18163
+ },
18164
+ {
18165
+ name: "key-value-secret-quoted",
18166
+ // Quoted-string variant: (key) [:=] "<value>". The value runs to
18167
+ // the next unescaped closing quote so a multi-word secret like
18168
+ // `password="my secret phrase"` is fully consumed instead of
18169
+ // splitting at the first space and leaving the tail visible.
18170
+ // The leading `(?<![A-Za-z0-9_])` prevents matching inside
18171
+ // identifiers like `passwordless`. The trailing `"?` after the
18172
+ // keyword absorbs the closing quote in JSON-style `"apikey":
18173
+ // "value"` so the colon is still seen as the separator.
18174
+ pattern: /(?<![A-Za-z0-9_])(?:api[_-]?key|apikey|secret|password|token)"?\s*[:=]\s*"(?:[^"\\]|\\.)*"/gi
18175
+ },
18176
+ {
18177
+ name: "key-value-secret-bare",
18178
+ // Unquoted variant: (key) [:=] <bare-value>. The bare value
18179
+ // capture stops at common JSON/text delimiters so we redact only
18180
+ // the value, not surrounding structure. Listed AFTER the quoted
18181
+ // variant so a quoted value's surrounding `"` are consumed by
18182
+ // the first pattern and we never fall through here for a quoted
18183
+ // secret.
18184
+ pattern: /(?<![A-Za-z0-9_])(?:api[_-]?key|apikey|secret|password|token)"?\s*[:=]\s*[^\s,;}\]"]+/gi
18185
+ }
18186
+ ];
18187
+ function sanitizeErrorResponseBody(body) {
18188
+ let out = body;
18189
+ for (const { pattern } of REDACTION_PATTERNS) {
18190
+ out = out.replace(pattern, REDACTED);
18191
+ }
18192
+ return out;
18193
+ }
18194
+ function truncateErrorResponseBody(body) {
18195
+ const encoder = new TextEncoder();
18196
+ const encoded = encoder.encode(body);
18197
+ if (encoded.byteLength <= ERROR_RESPONSE_BODY_MAX_BYTES) {
18198
+ return body;
18199
+ }
18200
+ let cut = ERROR_RESPONSE_BODY_MAX_BYTES;
18201
+ let scan = cut - 1;
18202
+ while (scan >= 0 && (encoded[scan] & 192) === 128) {
18203
+ scan -= 1;
18204
+ }
18205
+ if (scan >= 0) {
18206
+ const leading = encoded[scan];
18207
+ let expected = 1;
18208
+ if ((leading & 128) === 0) {
18209
+ expected = 1;
18210
+ } else if ((leading & 224) === 192) {
18211
+ expected = 2;
18212
+ } else if ((leading & 240) === 224) {
18213
+ expected = 3;
18214
+ } else if ((leading & 248) === 240) {
18215
+ expected = 4;
18216
+ }
18217
+ if (scan + expected > cut) {
18218
+ cut = scan;
18219
+ }
18220
+ }
18221
+ const decoder = new TextDecoder("utf-8", { fatal: false });
18222
+ const sliced = encoded.subarray(0, cut);
18223
+ const decoded = decoder.decode(sliced);
18224
+ return decoded + ERROR_RESPONSE_BODY_TRUNCATION_MARKER;
18225
+ }
18226
+ function prepareErrorResponseBody(body) {
18227
+ if (body.length === 0) return null;
18228
+ if (body.trim().length === 0) return null;
18229
+ const sanitized = sanitizeErrorResponseBody(body);
18230
+ return truncateErrorResponseBody(sanitized);
18231
+ }
18232
+
18233
+ // src/enriching-exporter.ts
17830
18234
  var ATTR2 = GLASSTRACE_ATTRIBUTE_NAMES;
17831
18235
  var API_KEY_PENDING = "pending";
17832
18236
  var MAX_PENDING_SPANS = 1024;
@@ -18054,7 +18458,14 @@ var GlasstraceExporter = class {
18054
18458
  if (this.getConfig().errorResponseBodies) {
18055
18459
  const responseBody = attrs["glasstrace.internal.response_body"];
18056
18460
  if (typeof responseBody === "string") {
18057
- extra[ATTR2.ERROR_RESPONSE_BODY] = responseBody.slice(0, 500);
18461
+ const enrichedStatus = extra[ATTR2.HTTP_STATUS_CODE];
18462
+ const effectiveStatus = typeof enrichedStatus === "number" ? enrichedStatus : statusCode;
18463
+ if (isHttpErrorStatus(effectiveStatus)) {
18464
+ const prepared = prepareErrorResponseBody(responseBody);
18465
+ if (prepared !== null) {
18466
+ extra[ATTR2.ERROR_RESPONSE_BODY] = prepared;
18467
+ }
18468
+ }
18058
18469
  }
18059
18470
  }
18060
18471
  const spanAny = span;
@@ -21920,7 +22331,7 @@ function registerGlasstrace(options) {
21920
22331
  setCoreState(CoreState.REGISTERING);
21921
22332
  startRuntimeStateWriter({
21922
22333
  projectRoot: process.cwd(),
21923
- sdkVersion: "1.1.0"
22334
+ sdkVersion: "1.1.2"
21924
22335
  });
21925
22336
  const config2 = resolveConfig(options);
21926
22337
  if (config2.verbose) {
@@ -22086,8 +22497,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
22086
22497
  if (config2.verbose) {
22087
22498
  console.info("[glasstrace] Background init firing.");
22088
22499
  }
22089
- const healthReport = collectHealthReport("1.1.0");
22090
- const initResult = await performInit(config2, anonKeyForInit, "1.1.0", healthReport);
22500
+ const healthReport = collectHealthReport("1.1.2");
22501
+ const initResult = await performInit(config2, anonKeyForInit, "1.1.2", healthReport);
22091
22502
  if (generation !== registrationGeneration) return;
22092
22503
  const currentState = getCoreState();
22093
22504
  if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
@@ -22110,7 +22521,7 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
22110
22521
  }
22111
22522
  maybeInstallConsoleCapture();
22112
22523
  if (didLastInitSucceed()) {
22113
- startHeartbeat(config2, anonKeyForInit, "1.1.0", generation, (newApiKey, accountId) => {
22524
+ startHeartbeat(config2, anonKeyForInit, "1.1.2", generation, (newApiKey, accountId) => {
22114
22525
  setAuthState(AuthState.CLAIMING);
22115
22526
  emitLifecycleEvent("auth:claim_started", { accountId });
22116
22527
  setResolvedApiKey(newApiKey);