@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.
package/dist/index.d.cts CHANGED
@@ -354,9 +354,13 @@ declare class GlasstraceExporter implements SpanExporter {
354
354
  * Enriches a ReadableSpan with all glasstrace.* attributes.
355
355
  * Returns a new ReadableSpan wrapper; the original span is not mutated.
356
356
  *
357
- * External function calls (getSessionId, deriveErrorCategory,
358
- * deriveOrmProvider, classifyFetchTarget) are individually guarded so a
359
- * failure in one does not prevent the remaining attributes from being set.
357
+ * Only {@link SessionManager.getSessionId} is individually guarded because
358
+ * it calls into crypto and schema validation — a session ID failure should
359
+ * not prevent the rest of enrichment. The other helper calls
360
+ * ({@link deriveErrorCategory}, {@link deriveOrmProvider},
361
+ * {@link classifyFetchTarget}) are pure functions on typed string inputs
362
+ * and rely on the outer catch for any unexpected failure.
363
+ *
360
364
  * On total failure, returns the original span unchanged.
361
365
  */
362
366
  private enrichSpan;
@@ -467,6 +471,9 @@ declare function uploadSourceMaps(apiKey: string, endpoint: string, buildHash: s
467
471
  * explicit, opt-in API for manual error reporting. If no span is active
468
472
  * or OTel is not available, the call is silently ignored.
469
473
  *
474
+ * On the first captured error, may display a one-time diagnostic nudge
475
+ * to stderr if the MCP connection marker is absent (dev environments only).
476
+ *
470
477
  * @param error - The error to capture. Accepts `Error` objects, strings, or any value.
471
478
  *
472
479
  * @example
package/dist/index.d.ts CHANGED
@@ -354,9 +354,13 @@ declare class GlasstraceExporter implements SpanExporter {
354
354
  * Enriches a ReadableSpan with all glasstrace.* attributes.
355
355
  * Returns a new ReadableSpan wrapper; the original span is not mutated.
356
356
  *
357
- * External function calls (getSessionId, deriveErrorCategory,
358
- * deriveOrmProvider, classifyFetchTarget) are individually guarded so a
359
- * failure in one does not prevent the remaining attributes from being set.
357
+ * Only {@link SessionManager.getSessionId} is individually guarded because
358
+ * it calls into crypto and schema validation — a session ID failure should
359
+ * not prevent the rest of enrichment. The other helper calls
360
+ * ({@link deriveErrorCategory}, {@link deriveOrmProvider},
361
+ * {@link classifyFetchTarget}) are pure functions on typed string inputs
362
+ * and rely on the outer catch for any unexpected failure.
363
+ *
360
364
  * On total failure, returns the original span unchanged.
361
365
  */
362
366
  private enrichSpan;
@@ -467,6 +471,9 @@ declare function uploadSourceMaps(apiKey: string, endpoint: string, buildHash: s
467
471
  * explicit, opt-in API for manual error reporting. If no span is active
468
472
  * or OTel is not available, the call is silently ignored.
469
473
  *
474
+ * On the first captured error, may display a one-time diagnostic nudge
475
+ * to stderr if the MCP connection marker is absent (dev environments only).
476
+ *
470
477
  * @param error - The error to capture. Accepts `Error` objects, strings, or any value.
471
478
  *
472
479
  * @example
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  buildImportGraph,
3
3
  discoverTestFiles,
4
4
  extractImports
5
- } from "./chunk-TJ6ETQPH.js";
5
+ } from "./chunk-67H2JEI4.js";
6
6
  import {
7
7
  DEFAULT_CAPTURE_CONFIG,
8
8
  GLASSTRACE_ATTRIBUTE_NAMES,
@@ -12,7 +12,7 @@ import {
12
12
  SourceMapUploadResponseSchema,
13
13
  getOrCreateAnonKey,
14
14
  readAnonKey
15
- } from "./chunk-EC5IINUT.js";
15
+ } from "./chunk-TVOYTJ7I.js";
16
16
  import {
17
17
  INVALID_SPAN_CONTEXT,
18
18
  SamplingDecision,
@@ -452,9 +452,13 @@ var GlasstraceExporter = class {
452
452
  * Enriches a ReadableSpan with all glasstrace.* attributes.
453
453
  * Returns a new ReadableSpan wrapper; the original span is not mutated.
454
454
  *
455
- * External function calls (getSessionId, deriveErrorCategory,
456
- * deriveOrmProvider, classifyFetchTarget) are individually guarded so a
457
- * failure in one does not prevent the remaining attributes from being set.
455
+ * Only {@link SessionManager.getSessionId} is individually guarded because
456
+ * it calls into crypto and schema validation — a session ID failure should
457
+ * not prevent the rest of enrichment. The other helper calls
458
+ * ({@link deriveErrorCategory}, {@link deriveOrmProvider},
459
+ * {@link classifyFetchTarget}) are pure functions on typed string inputs
460
+ * and rely on the outer catch for any unexpected failure.
461
+ *
458
462
  * On total failure, returns the original span unchanged.
459
463
  */
460
464
  enrichSpan(span) {
@@ -497,44 +501,39 @@ var GlasstraceExporter = class {
497
501
  }
498
502
  }
499
503
  const errorMessage = attrs["exception.message"];
500
- if (errorMessage) {
504
+ if (typeof errorMessage === "string") {
501
505
  extra[ATTR.ERROR_MESSAGE] = errorMessage;
502
506
  }
503
- try {
504
- const errorType = attrs["exception.type"];
505
- if (errorType) {
506
- extra[ATTR.ERROR_CODE] = errorType;
507
- extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
508
- }
509
- } catch {
507
+ const errorType = attrs["exception.type"];
508
+ if (typeof errorType === "string") {
509
+ extra[ATTR.ERROR_CODE] = errorType;
510
+ extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
510
511
  }
511
512
  const errorField = attrs["error.field"];
512
- if (errorField) {
513
+ if (typeof errorField === "string") {
513
514
  extra[ATTR.ERROR_FIELD] = errorField;
514
515
  }
515
- try {
516
- const spanAny = span;
517
- const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
518
- const ormProvider = deriveOrmProvider(instrumentationName);
519
- if (ormProvider) {
520
- extra[ATTR.ORM_PROVIDER] = ormProvider;
521
- const model = attrs["db.sql.table"] ?? attrs["db.prisma.model"];
522
- if (model) {
523
- extra[ATTR.ORM_MODEL] = model;
524
- }
525
- const operation = attrs["db.operation"];
526
- if (operation) {
527
- extra[ATTR.ORM_OPERATION] = operation;
528
- }
516
+ const spanAny = span;
517
+ const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
518
+ const ormProvider = deriveOrmProvider(instrumentationName);
519
+ if (ormProvider) {
520
+ extra[ATTR.ORM_PROVIDER] = ormProvider;
521
+ const table = attrs["db.sql.table"];
522
+ const prismaModel = attrs["db.prisma.model"];
523
+ const model = typeof table === "string" ? table : typeof prismaModel === "string" ? prismaModel : void 0;
524
+ if (model) {
525
+ extra[ATTR.ORM_MODEL] = model;
529
526
  }
530
- } catch {
531
- }
532
- try {
533
- const url = attrs["http.url"] ?? attrs["url.full"];
534
- if (url && span.kind === SpanKind.CLIENT) {
535
- extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
527
+ const operation = attrs["db.operation"];
528
+ if (typeof operation === "string") {
529
+ extra[ATTR.ORM_OPERATION] = operation;
536
530
  }
537
- } catch {
531
+ }
532
+ const httpUrl = attrs["http.url"];
533
+ const fullUrl = attrs["url.full"];
534
+ const url = typeof httpUrl === "string" ? httpUrl : typeof fullUrl === "string" ? fullUrl : void 0;
535
+ if (url && span.kind === SpanKind.CLIENT) {
536
+ extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
538
537
  }
539
538
  return createEnrichedSpan(span, extra);
540
539
  } catch {
@@ -3542,7 +3541,7 @@ function registerGlasstrace(options) {
3542
3541
  if (config.verbose) {
3543
3542
  console.info("[glasstrace] Background init firing.");
3544
3543
  }
3545
- await performInit(config, anonKey, "0.4.0");
3544
+ await performInit(config, anonKey, "0.4.2");
3546
3545
  maybeInstallConsoleCapture();
3547
3546
  } catch (err) {
3548
3547
  console.warn(
@@ -3562,7 +3561,7 @@ function registerGlasstrace(options) {
3562
3561
  if (config.verbose) {
3563
3562
  console.info("[glasstrace] Background init firing.");
3564
3563
  }
3565
- await performInit(config, anonKey, "0.4.0");
3564
+ await performInit(config, anonKey, "0.4.2");
3566
3565
  maybeInstallConsoleCapture();
3567
3566
  } catch (err) {
3568
3567
  console.warn(
@@ -3584,7 +3583,7 @@ function registerGlasstrace(options) {
3584
3583
  if (config.verbose) {
3585
3584
  console.info("[glasstrace] Background init firing.");
3586
3585
  }
3587
- await performInit(config, anonKeyForInit, "0.4.0");
3586
+ await performInit(config, anonKeyForInit, "0.4.2");
3588
3587
  maybeInstallConsoleCapture();
3589
3588
  } catch (err) {
3590
3589
  console.warn(
@@ -3683,7 +3682,10 @@ async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
3683
3682
  sourceMap: m.content
3684
3683
  }))
3685
3684
  };
3686
- const baseUrl = endpoint.replace(/\/+$/, "");
3685
+ let baseUrl = endpoint;
3686
+ while (baseUrl.endsWith("/")) {
3687
+ baseUrl = baseUrl.slice(0, -1);
3688
+ }
3687
3689
  const response = await fetch(`${baseUrl}/v1/source-maps`, {
3688
3690
  method: "POST",
3689
3691
  headers: {
@@ -3767,6 +3769,41 @@ async function handleSourceMapUpload(distDir) {
3767
3769
  }
3768
3770
  }
3769
3771
 
3772
+ // src/nudge/error-nudge.ts
3773
+ import { existsSync } from "fs";
3774
+ import { join as join3 } from "path";
3775
+ var hasFired = false;
3776
+ function sanitize(input) {
3777
+ return input.replace(/[\x00-\x1f\x7f]/g, "");
3778
+ }
3779
+ function maybeShowMcpNudge(errorSummary) {
3780
+ if (hasFired) {
3781
+ return;
3782
+ }
3783
+ const config = resolveConfig();
3784
+ if (isProductionDisabled(config)) {
3785
+ return;
3786
+ }
3787
+ let markerExists = false;
3788
+ try {
3789
+ const markerPath = join3(process.cwd(), ".glasstrace", "mcp-connected");
3790
+ markerExists = existsSync(markerPath);
3791
+ } catch {
3792
+ markerExists = false;
3793
+ }
3794
+ if (markerExists) {
3795
+ return;
3796
+ }
3797
+ hasFired = true;
3798
+ const safe = sanitize(errorSummary);
3799
+ process.stderr.write(
3800
+ `[glasstrace] Error captured: ${safe}
3801
+ Debug with AI: ask your agent "What's the latest Glasstrace error?"
3802
+ Not connected? Run: npx glasstrace mcp add
3803
+ `
3804
+ );
3805
+ }
3806
+
3770
3807
  // src/capture-error.ts
3771
3808
  function captureError(error) {
3772
3809
  try {
@@ -3779,6 +3816,7 @@ function captureError(error) {
3779
3816
  attributes["error.type"] = error.constructor.name;
3780
3817
  }
3781
3818
  span.addEvent("glasstrace.error", attributes);
3819
+ maybeShowMcpNudge(String(error));
3782
3820
  } catch {
3783
3821
  }
3784
3822
  }