@glasstrace/sdk 0.4.0 → 0.4.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.
@@ -6,10 +6,10 @@ import {
6
6
  scaffoldMcpMarker,
7
7
  updateGitignore,
8
8
  writeMcpConfig
9
- } from "../chunk-STECO33B.js";
9
+ } from "../chunk-PLJVIWHN.js";
10
10
  import {
11
11
  readAnonKey
12
- } from "../chunk-EC5IINUT.js";
12
+ } from "../chunk-TVOYTJ7I.js";
13
13
  import "../chunk-PZ5AY32C.js";
14
14
 
15
15
  // src/cli/mcp-add.ts
package/dist/index.cjs CHANGED
@@ -15741,15 +15741,28 @@ async function getOrCreateAnonKey(projectRoot) {
15741
15741
  const newKey = createAnonApiKey();
15742
15742
  try {
15743
15743
  await (0, import_promises.mkdir)(dirPath, { recursive: true, mode: 448 });
15744
- await (0, import_promises.writeFile)(keyPath, newKey, "utf-8");
15745
- await (0, import_promises.chmod)(keyPath, 384);
15744
+ await (0, import_promises.writeFile)(keyPath, newKey, { flag: "wx", mode: 384 });
15745
+ return newKey;
15746
15746
  } catch (err) {
15747
+ const code = err.code;
15748
+ if (code === "EEXIST") {
15749
+ const winnerKey = await readAnonKey(root);
15750
+ if (winnerKey !== null) {
15751
+ return winnerKey;
15752
+ }
15753
+ try {
15754
+ await (0, import_promises.writeFile)(keyPath, newKey, { mode: 384 });
15755
+ await (0, import_promises.chmod)(keyPath, 384);
15756
+ return newKey;
15757
+ } catch {
15758
+ }
15759
+ }
15747
15760
  ephemeralKeyCache.set(root, newKey);
15748
15761
  console.warn(
15749
15762
  `[glasstrace] Failed to persist anonymous key to ${keyPath}: ${err instanceof Error ? err.message : String(err)}. Using ephemeral key.`
15750
15763
  );
15764
+ return newKey;
15751
15765
  }
15752
- return newKey;
15753
15766
  }
15754
15767
 
15755
15768
  // src/init-client.ts
@@ -16023,9 +16036,13 @@ var GlasstraceExporter = class {
16023
16036
  * Enriches a ReadableSpan with all glasstrace.* attributes.
16024
16037
  * Returns a new ReadableSpan wrapper; the original span is not mutated.
16025
16038
  *
16026
- * External function calls (getSessionId, deriveErrorCategory,
16027
- * deriveOrmProvider, classifyFetchTarget) are individually guarded so a
16028
- * failure in one does not prevent the remaining attributes from being set.
16039
+ * Only {@link SessionManager.getSessionId} is individually guarded because
16040
+ * it calls into crypto and schema validation — a session ID failure should
16041
+ * not prevent the rest of enrichment. The other helper calls
16042
+ * ({@link deriveErrorCategory}, {@link deriveOrmProvider},
16043
+ * {@link classifyFetchTarget}) are pure functions on typed string inputs
16044
+ * and rely on the outer catch for any unexpected failure.
16045
+ *
16029
16046
  * On total failure, returns the original span unchanged.
16030
16047
  */
16031
16048
  enrichSpan(span) {
@@ -16068,44 +16085,39 @@ var GlasstraceExporter = class {
16068
16085
  }
16069
16086
  }
16070
16087
  const errorMessage = attrs["exception.message"];
16071
- if (errorMessage) {
16088
+ if (typeof errorMessage === "string") {
16072
16089
  extra[ATTR.ERROR_MESSAGE] = errorMessage;
16073
16090
  }
16074
- try {
16075
- const errorType = attrs["exception.type"];
16076
- if (errorType) {
16077
- extra[ATTR.ERROR_CODE] = errorType;
16078
- extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
16079
- }
16080
- } catch {
16091
+ const errorType = attrs["exception.type"];
16092
+ if (typeof errorType === "string") {
16093
+ extra[ATTR.ERROR_CODE] = errorType;
16094
+ extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
16081
16095
  }
16082
16096
  const errorField = attrs["error.field"];
16083
- if (errorField) {
16097
+ if (typeof errorField === "string") {
16084
16098
  extra[ATTR.ERROR_FIELD] = errorField;
16085
16099
  }
16086
- try {
16087
- const spanAny = span;
16088
- const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
16089
- const ormProvider = deriveOrmProvider(instrumentationName);
16090
- if (ormProvider) {
16091
- extra[ATTR.ORM_PROVIDER] = ormProvider;
16092
- const model = attrs["db.sql.table"] ?? attrs["db.prisma.model"];
16093
- if (model) {
16094
- extra[ATTR.ORM_MODEL] = model;
16095
- }
16096
- const operation = attrs["db.operation"];
16097
- if (operation) {
16098
- extra[ATTR.ORM_OPERATION] = operation;
16099
- }
16100
+ const spanAny = span;
16101
+ const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
16102
+ const ormProvider = deriveOrmProvider(instrumentationName);
16103
+ if (ormProvider) {
16104
+ extra[ATTR.ORM_PROVIDER] = ormProvider;
16105
+ const table = attrs["db.sql.table"];
16106
+ const prismaModel = attrs["db.prisma.model"];
16107
+ const model = typeof table === "string" ? table : typeof prismaModel === "string" ? prismaModel : void 0;
16108
+ if (model) {
16109
+ extra[ATTR.ORM_MODEL] = model;
16100
16110
  }
16101
- } catch {
16102
- }
16103
- try {
16104
- const url2 = attrs["http.url"] ?? attrs["url.full"];
16105
- if (url2 && span.kind === SpanKind.CLIENT) {
16106
- extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url2);
16111
+ const operation = attrs["db.operation"];
16112
+ if (typeof operation === "string") {
16113
+ extra[ATTR.ORM_OPERATION] = operation;
16107
16114
  }
16108
- } catch {
16115
+ }
16116
+ const httpUrl2 = attrs["http.url"];
16117
+ const fullUrl = attrs["url.full"];
16118
+ const url2 = typeof httpUrl2 === "string" ? httpUrl2 : typeof fullUrl === "string" ? fullUrl : void 0;
16119
+ if (url2 && span.kind === SpanKind.CLIENT) {
16120
+ extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url2);
16109
16121
  }
16110
16122
  return createEnrichedSpan(span, extra);
16111
16123
  } catch {
@@ -19143,7 +19155,7 @@ function registerGlasstrace(options) {
19143
19155
  if (config2.verbose) {
19144
19156
  console.info("[glasstrace] Background init firing.");
19145
19157
  }
19146
- await performInit(config2, anonKey, "0.4.0");
19158
+ await performInit(config2, anonKey, "0.4.2");
19147
19159
  maybeInstallConsoleCapture();
19148
19160
  } catch (err) {
19149
19161
  console.warn(
@@ -19163,7 +19175,7 @@ function registerGlasstrace(options) {
19163
19175
  if (config2.verbose) {
19164
19176
  console.info("[glasstrace] Background init firing.");
19165
19177
  }
19166
- await performInit(config2, anonKey, "0.4.0");
19178
+ await performInit(config2, anonKey, "0.4.2");
19167
19179
  maybeInstallConsoleCapture();
19168
19180
  } catch (err) {
19169
19181
  console.warn(
@@ -19185,7 +19197,7 @@ function registerGlasstrace(options) {
19185
19197
  if (config2.verbose) {
19186
19198
  console.info("[glasstrace] Background init firing.");
19187
19199
  }
19188
- await performInit(config2, anonKeyForInit, "0.4.0");
19200
+ await performInit(config2, anonKeyForInit, "0.4.2");
19189
19201
  maybeInstallConsoleCapture();
19190
19202
  } catch (err) {
19191
19203
  console.warn(
@@ -19284,7 +19296,10 @@ async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
19284
19296
  sourceMap: m.content
19285
19297
  }))
19286
19298
  };
19287
- const baseUrl = endpoint.replace(/\/+$/, "");
19299
+ let baseUrl = endpoint;
19300
+ while (baseUrl.endsWith("/")) {
19301
+ baseUrl = baseUrl.slice(0, -1);
19302
+ }
19288
19303
  const response = await fetch(`${baseUrl}/v1/source-maps`, {
19289
19304
  method: "POST",
19290
19305
  headers: {
@@ -19370,6 +19385,43 @@ async function handleSourceMapUpload(distDir) {
19370
19385
 
19371
19386
  // src/capture-error.ts
19372
19387
  init_esm();
19388
+
19389
+ // src/nudge/error-nudge.ts
19390
+ var import_node_fs2 = require("fs");
19391
+ var import_node_path3 = require("path");
19392
+ var hasFired = false;
19393
+ function sanitize(input) {
19394
+ return input.replace(/[\x00-\x1f\x7f]/g, "");
19395
+ }
19396
+ function maybeShowMcpNudge(errorSummary) {
19397
+ if (hasFired) {
19398
+ return;
19399
+ }
19400
+ const config2 = resolveConfig();
19401
+ if (isProductionDisabled(config2)) {
19402
+ return;
19403
+ }
19404
+ let markerExists = false;
19405
+ try {
19406
+ const markerPath = (0, import_node_path3.join)(process.cwd(), ".glasstrace", "mcp-connected");
19407
+ markerExists = (0, import_node_fs2.existsSync)(markerPath);
19408
+ } catch {
19409
+ markerExists = false;
19410
+ }
19411
+ if (markerExists) {
19412
+ return;
19413
+ }
19414
+ hasFired = true;
19415
+ const safe = sanitize(errorSummary);
19416
+ process.stderr.write(
19417
+ `[glasstrace] Error captured: ${safe}
19418
+ Debug with AI: ask your agent "What's the latest Glasstrace error?"
19419
+ Not connected? Run: npx glasstrace mcp add
19420
+ `
19421
+ );
19422
+ }
19423
+
19424
+ // src/capture-error.ts
19373
19425
  function captureError(error48) {
19374
19426
  try {
19375
19427
  const span = trace.getSpan(context.active());
@@ -19381,6 +19433,7 @@ function captureError(error48) {
19381
19433
  attributes["error.type"] = error48.constructor.name;
19382
19434
  }
19383
19435
  span.addEvent("glasstrace.error", attributes);
19436
+ maybeShowMcpNudge(String(error48));
19384
19437
  } catch {
19385
19438
  }
19386
19439
  }
@@ -19506,12 +19559,18 @@ function extractImports(fileContent) {
19506
19559
  imports.push(importPath);
19507
19560
  }
19508
19561
  };
19509
- const esImportRegex = /import\s+(?:(?:[\w*{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g;
19562
+ const esFromImportRegex = /\bimport\b[^'"]+\bfrom\s+['"]([^'"]+)['"]/g;
19563
+ const esSideEffectRegex = /\bimport\s+['"]([^'"]+)['"]/g;
19510
19564
  let match;
19511
- match = esImportRegex.exec(fileContent);
19565
+ match = esFromImportRegex.exec(fileContent);
19566
+ while (match !== null) {
19567
+ addUnique(match[1]);
19568
+ match = esFromImportRegex.exec(fileContent);
19569
+ }
19570
+ match = esSideEffectRegex.exec(fileContent);
19512
19571
  while (match !== null) {
19513
19572
  addUnique(match[1]);
19514
- match = esImportRegex.exec(fileContent);
19573
+ match = esSideEffectRegex.exec(fileContent);
19515
19574
  }
19516
19575
  const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
19517
19576
  match = requireRegex.exec(fileContent);