@bitfab/sdk 0.52.3 → 0.52.5

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 (35) hide show
  1. package/README.md +2 -0
  2. package/dist/{chunk-SECFVEQV.js → chunk-BEAGKWXY.js} +291 -21
  3. package/dist/chunk-BEAGKWXY.js.map +1 -0
  4. package/dist/{chunk-ODF4JSYX.js → chunk-IHAB7SPF.js} +15 -2
  5. package/dist/{chunk-ODF4JSYX.js.map → chunk-IHAB7SPF.js.map} +1 -1
  6. package/dist/chunk-ITIMJ24H.js +747 -0
  7. package/dist/chunk-ITIMJ24H.js.map +1 -0
  8. package/dist/{chunk-45SK3FUL.js → chunk-THKLMOHS.js} +111 -13
  9. package/dist/chunk-THKLMOHS.js.map +1 -0
  10. package/dist/{chunk-QHA46TT6.js → chunk-VAIXHJ7O.js} +14 -2
  11. package/dist/{chunk-QHA46TT6.js.map → chunk-VAIXHJ7O.js.map} +1 -1
  12. package/dist/{http-ZW5THLTW.js → http-6Y3IQQ2Z.js} +2 -2
  13. package/dist/{http-3MPIPYKC.js → http-KZU56HZA.js} +2 -2
  14. package/dist/index.cjs +444 -57
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +44 -2
  17. package/dist/index.d.ts +44 -2
  18. package/dist/index.js +5 -3
  19. package/dist/node.cjs +444 -57
  20. package/dist/node.cjs.map +1 -1
  21. package/dist/node.d.cts +1 -1
  22. package/dist/node.d.ts +1 -1
  23. package/dist/node.js +5 -3
  24. package/dist/node.js.map +1 -1
  25. package/dist/{replay-W5SAHEU5.js → replay-LZ7DIY5L.js} +5 -3
  26. package/dist/replayCli.js +18 -687
  27. package/dist/replayCli.js.map +1 -1
  28. package/dist/seedCli.js +30 -0
  29. package/dist/seedCli.js.map +1 -0
  30. package/package.json +3 -2
  31. package/dist/chunk-45SK3FUL.js.map +0 -1
  32. package/dist/chunk-SECFVEQV.js.map +0 -1
  33. /package/dist/{http-3MPIPYKC.js.map → http-6Y3IQQ2Z.js.map} +0 -0
  34. /package/dist/{http-ZW5THLTW.js.map → http-KZU56HZA.js.map} +0 -0
  35. /package/dist/{replay-W5SAHEU5.js.map → replay-LZ7DIY5L.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -42,7 +42,7 @@ var __version__, __packageName__;
42
42
  var init_version_generated = __esm({
43
43
  "src/version.generated.ts"() {
44
44
  "use strict";
45
- __version__ = "0.52.3";
45
+ __version__ = "0.52.5";
46
46
  __packageName__ = "@bitfab/sdk";
47
47
  }
48
48
  });
@@ -1121,7 +1121,7 @@ var init_otel = __esm({
1121
1121
  if (this.onDelivered === void 0) {
1122
1122
  return;
1123
1123
  }
1124
- const refs = spans.map((span) => span.ref).filter((ref) => ref !== void 0);
1124
+ const refs = spans.map((span) => span.ref).filter((ref2) => ref2 !== void 0);
1125
1125
  if (refs.length === 0) {
1126
1126
  return;
1127
1127
  }
@@ -1378,9 +1378,9 @@ function parseRetryAfterMs(header) {
1378
1378
  }
1379
1379
  return Math.max(0, at - Date.now());
1380
1380
  }
1381
- function carrierMeta(operation, payload, ref) {
1381
+ function carrierMeta(operation, payload, ref2) {
1382
1382
  return {
1383
- ref,
1383
+ ref: ref2,
1384
1384
  name: carrierName(operation, payload),
1385
1385
  startTime: payloadTimestamp(payload, "started_at"),
1386
1386
  endTime: payloadTimestamp(payload, "ended_at"),
@@ -1625,22 +1625,22 @@ var init_http = __esm({
1625
1625
  return reports;
1626
1626
  }
1627
1627
  /** Build a carrier's meta and record what it adds to its trace's expected set. */
1628
- recordedMeta(operation, payload, ref) {
1629
- this.recordSubmittedCarrier(ref);
1630
- return carrierMeta(operation, payload, ref);
1628
+ recordedMeta(operation, payload, ref2) {
1629
+ this.recordSubmittedCarrier(ref2);
1630
+ return carrierMeta(operation, payload, ref2);
1631
1631
  }
1632
- recordSubmittedCarrier(ref) {
1633
- if (ref === void 0) {
1632
+ recordSubmittedCarrier(ref2) {
1633
+ if (ref2 === void 0) {
1634
1634
  return;
1635
1635
  }
1636
- const delivery = this.traceDeliveries.get(ref.traceId);
1636
+ const delivery = this.traceDeliveries.get(ref2.traceId);
1637
1637
  if (delivery === void 0) {
1638
1638
  return;
1639
1639
  }
1640
- if (ref.spanId === void 0) {
1640
+ if (ref2.spanId === void 0) {
1641
1641
  delivery.closed = true;
1642
1642
  } else {
1643
- delivery.submittedSpanIds.add(ref.spanId);
1643
+ delivery.submittedSpanIds.add(ref2.spanId);
1644
1644
  }
1645
1645
  }
1646
1646
  /**
@@ -1666,15 +1666,15 @@ var init_http = __esm({
1666
1666
  }
1667
1667
  }
1668
1668
  recordDeliveredCarriers(refs) {
1669
- for (const ref of refs) {
1670
- const delivery = this.traceDeliveries.get(ref.traceId);
1669
+ for (const ref2 of refs) {
1670
+ const delivery = this.traceDeliveries.get(ref2.traceId);
1671
1671
  if (delivery === void 0) {
1672
1672
  continue;
1673
1673
  }
1674
- if (ref.spanId === void 0) {
1674
+ if (ref2.spanId === void 0) {
1675
1675
  delivery.closingAcked = true;
1676
1676
  } else {
1677
- delivery.ackedSpanIds.add(ref.spanId);
1677
+ delivery.ackedSpanIds.add(ref2.spanId);
1678
1678
  }
1679
1679
  }
1680
1680
  }
@@ -1704,9 +1704,9 @@ var init_http = __esm({
1704
1704
  */
1705
1705
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1706
1706
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1707
- const settled = await this.settleDeferredWork(timeoutMs);
1707
+ const settled2 = await this.settleDeferredWork(timeoutMs);
1708
1708
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1709
- return settled && flushed;
1709
+ return settled2 && flushed;
1710
1710
  }
1711
1711
  /**
1712
1712
  * Flush and permanently close this client's tracing transport. Idempotent:
@@ -1719,14 +1719,14 @@ var init_http = __esm({
1719
1719
  }
1720
1720
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1721
1721
  this.closing = (async () => {
1722
- const settled = await this.settleDeferredWork(
1722
+ const settled2 = await this.settleDeferredWork(
1723
1723
  Math.max(0, deadline - Date.now())
1724
1724
  );
1725
1725
  this.closed = true;
1726
1726
  const transport = this.traceTransport;
1727
1727
  this.traceTransport = void 0;
1728
1728
  const shutdownOk = await transport?.shutdown(Math.max(0, deadline - Date.now())) ?? true;
1729
- return settled && shutdownOk;
1729
+ return settled2 && shutdownOk;
1730
1730
  })();
1731
1731
  return this.closing;
1732
1732
  }
@@ -2115,6 +2115,18 @@ var init_http = __esm({
2115
2115
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
2116
2116
  );
2117
2117
  }
2118
+ async getReseedSource(traceId) {
2119
+ return this.get(
2120
+ `/api/sdk/traces/${encodeURIComponent(traceId)}/reseedSource`
2121
+ );
2122
+ }
2123
+ async reseedTrace(traceId, runTraceId) {
2124
+ return this.request(
2125
+ `/api/sdk/traces/${encodeURIComponent(traceId)}/reseed`,
2126
+ { runTraceId },
2127
+ { timeout: 3e4 }
2128
+ );
2129
+ }
2118
2130
  /**
2119
2131
  * Ask the server to materialize a per-trace DB branch lease from a
2120
2132
  * captured `dbSnapshotRef`. Blocking - the resolver creates a Neon
@@ -2325,6 +2337,112 @@ var init_randomUuid = __esm({
2325
2337
  }
2326
2338
  });
2327
2339
 
2340
+ // src/gitCommand.ts
2341
+ function isUnrefable(value) {
2342
+ return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
2343
+ }
2344
+ function unrefStream(stream) {
2345
+ if (isUnrefable(stream)) {
2346
+ stream.unref();
2347
+ }
2348
+ }
2349
+ function terminate(child) {
2350
+ if (child.pid && process.platform !== "win32") {
2351
+ try {
2352
+ process.kill(-child.pid, "SIGTERM");
2353
+ return;
2354
+ } catch {
2355
+ child.kill("SIGTERM");
2356
+ return;
2357
+ }
2358
+ }
2359
+ child.kill("SIGTERM");
2360
+ }
2361
+ async function gitRunner(options) {
2362
+ let spawn;
2363
+ try {
2364
+ ;
2365
+ ({ spawn } = await import("child_process"));
2366
+ } catch {
2367
+ return null;
2368
+ }
2369
+ return (dir, args) => new Promise((resolve) => {
2370
+ let child;
2371
+ try {
2372
+ child = spawn("git", args, {
2373
+ cwd: dir,
2374
+ detached: process.platform !== "win32",
2375
+ stdio: ["ignore", "pipe", "pipe"]
2376
+ });
2377
+ } catch {
2378
+ resolve(null);
2379
+ return;
2380
+ }
2381
+ const chunks = [];
2382
+ let size = 0;
2383
+ let overflow = false;
2384
+ let exitCode = null;
2385
+ let done = false;
2386
+ const timers = [];
2387
+ const schedule = (callback, ms) => {
2388
+ const timer = setTimeout(callback, ms);
2389
+ if (!options.keepProcessAlive) {
2390
+ timer.unref();
2391
+ }
2392
+ timers.push(timer);
2393
+ };
2394
+ const finish = () => {
2395
+ if (done) {
2396
+ return;
2397
+ }
2398
+ done = true;
2399
+ for (const timer of timers) {
2400
+ clearTimeout(timer);
2401
+ }
2402
+ child.stdout?.destroy();
2403
+ child.stderr?.destroy();
2404
+ resolve(
2405
+ exitCode === 0 && !overflow ? Buffer.concat(chunks).toString("utf8") : null
2406
+ );
2407
+ };
2408
+ child.stdout?.on("data", (chunk) => {
2409
+ size += chunk.length;
2410
+ if (size > options.maxBuffer) {
2411
+ overflow = true;
2412
+ terminate(child);
2413
+ return;
2414
+ }
2415
+ chunks.push(chunk);
2416
+ });
2417
+ child.stderr?.resume();
2418
+ child.on("error", () => {
2419
+ exitCode = null;
2420
+ finish();
2421
+ });
2422
+ child.on("exit", (code) => {
2423
+ exitCode = code;
2424
+ schedule(finish, EXIT_GRACE_MS);
2425
+ });
2426
+ child.on("close", (code) => {
2427
+ exitCode = code ?? exitCode;
2428
+ finish();
2429
+ });
2430
+ schedule(() => terminate(child), options.timeoutMs);
2431
+ if (!options.keepProcessAlive) {
2432
+ child.unref();
2433
+ unrefStream(child.stdout);
2434
+ unrefStream(child.stderr);
2435
+ }
2436
+ });
2437
+ }
2438
+ var EXIT_GRACE_MS;
2439
+ var init_gitCommand = __esm({
2440
+ "src/gitCommand.ts"() {
2441
+ "use strict";
2442
+ EXIT_GRACE_MS = 500;
2443
+ }
2444
+ });
2445
+
2328
2446
  // src/mockOverride.ts
2329
2447
  function resolveMockValue(value, ctx) {
2330
2448
  return typeof value === "function" ? value(ctx) : value;
@@ -2381,37 +2499,33 @@ async function readCodeChangeFile() {
2381
2499
  }
2382
2500
  }
2383
2501
  async function captureCodeChangeFromGit(cwd, label) {
2384
- let execFile;
2385
2502
  let readFile;
2386
2503
  try {
2387
2504
  ;
2388
- ({ execFile } = await import("child_process"));
2389
2505
  ({ readFile } = await import("fs/promises"));
2390
2506
  } catch {
2391
2507
  return null;
2392
2508
  }
2393
- const git = (dir, args) => new Promise((resolve) => {
2394
- execFile(
2395
- "git",
2396
- args,
2397
- // 30s timeout so a hung git (e.g. a network-touching ref op) can't
2398
- // block the whole replay indefinitely.
2399
- { cwd: dir, maxBuffer: 64 * 1024 * 1024, timeout: 3e4 },
2400
- (err, stdout) => resolve(err ? null : stdout)
2401
- );
2509
+ const git = await gitRunner({
2510
+ timeoutMs: 3e4,
2511
+ maxBuffer: 64 * 1024 * 1024,
2512
+ keepProcessAlive: true
2402
2513
  });
2514
+ if (!git) {
2515
+ return null;
2516
+ }
2403
2517
  try {
2404
2518
  const root = (await git(cwd, ["rev-parse", "--show-toplevel"]))?.trim();
2405
2519
  if (!root) {
2406
2520
  return null;
2407
2521
  }
2408
- const resolved = await resolveBase(git, root);
2409
- if (!resolved) {
2522
+ const resolved2 = await resolveBase(git, root);
2523
+ if (!resolved2) {
2410
2524
  return null;
2411
2525
  }
2412
- const { base, fromTrunk } = resolved;
2413
- const blobBytes = async (ref, path) => {
2414
- const out = await git(root, ["cat-file", "-s", `${ref}:${path}`]);
2526
+ const { base, fromTrunk } = resolved2;
2527
+ const blobBytes = async (ref2, path) => {
2528
+ const out = await git(root, ["cat-file", "-s", `${ref2}:${path}`]);
2415
2529
  const n = out ? Number.parseInt(out.trim(), 10) : Number.NaN;
2416
2530
  return Number.isFinite(n) ? n : 0;
2417
2531
  };
@@ -2503,8 +2617,8 @@ async function resolveBase(git, root) {
2503
2617
  }
2504
2618
  return await refExists(git, root, "HEAD") ? { base: "HEAD", fromTrunk: false } : null;
2505
2619
  }
2506
- async function refExists(git, root, ref) {
2507
- return await git(root, ["rev-parse", "--verify", `${ref}^{object}`]) !== null;
2620
+ async function refExists(git, root, ref2) {
2621
+ return await git(root, ["rev-parse", "--verify", `${ref2}^{object}`]) !== null;
2508
2622
  }
2509
2623
  async function readWorkingFile(readFile, root, path) {
2510
2624
  try {
@@ -2539,6 +2653,7 @@ var MAX_FILES, MAX_FILE_BYTES, MAX_TOTAL_BYTES, TRUNK_CANDIDATES, NUL;
2539
2653
  var init_codeChange = __esm({
2540
2654
  "src/codeChange.ts"() {
2541
2655
  "use strict";
2656
+ init_gitCommand();
2542
2657
  MAX_FILES = 60;
2543
2658
  MAX_FILE_BYTES = 5e5;
2544
2659
  MAX_TOTAL_BYTES = 2e6;
@@ -2559,6 +2674,7 @@ __export(replay_exports, {
2559
2674
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
2560
2675
  DbBranchReplayError: () => DbBranchReplayError,
2561
2676
  ReplayError: () => ReplayError,
2677
+ deserializeInputs: () => deserializeInputs,
2562
2678
  replay: () => replay,
2563
2679
  reportReplayProgress: () => reportReplayProgress,
2564
2680
  serializeReplayResult: () => serializeReplayResult,
@@ -2742,9 +2858,9 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2742
2858
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
2743
2859
  try {
2744
2860
  if (includeDbBranchLease && !lease && !leaseError) {
2745
- let resolved;
2861
+ let resolved2;
2746
2862
  try {
2747
- resolved = await httpClient.resolveDbBranchLease(
2863
+ resolved2 = await httpClient.resolveDbBranchLease(
2748
2864
  testRunId,
2749
2865
  originalTraceId,
2750
2866
  dbBranchSettings,
@@ -2758,10 +2874,10 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2758
2874
  cause
2759
2875
  );
2760
2876
  }
2761
- lease = resolved.lease ?? void 0;
2762
- leaseError = resolved.leaseError ?? void 0;
2763
- dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef;
2764
- dbBranchTimings = resolved.timings ?? dbBranchTimings;
2877
+ lease = resolved2.lease ?? void 0;
2878
+ leaseError = resolved2.leaseError ?? void 0;
2879
+ dbSnapshotRef = resolved2.dbSnapshotRef ?? dbSnapshotRef;
2880
+ dbBranchTimings = resolved2.timings ?? dbBranchTimings;
2765
2881
  }
2766
2882
  if (leaseError) {
2767
2883
  throw new DbBranchReplayError(
@@ -3409,6 +3525,7 @@ __export(index_exports, {
3409
3525
  getCurrentSpan: () => getCurrentSpan,
3410
3526
  getCurrentTrace: () => getCurrentTrace,
3411
3527
  reportReplayProgress: () => reportReplayProgress,
3528
+ reseedFromRegistry: () => reseedFromRegistry,
3412
3529
  seedFromRegistry: () => seedFromRegistry,
3413
3530
  serializeReplayResult: () => serializeReplayResult
3414
3531
  });
@@ -4536,24 +4653,239 @@ function mixedTracingError(enteredApi, entered, enclosing, traceFunctionKey) {
4536
4653
  `opt-in and opt-out tracing can't be mixed in one call stack: ${enteredApi} (${entered})${subject} was entered inside a ${SURFACE_API[enclosing]} call (${enclosing}). ${MIXED_SURFACE_REMEDY[entered]}`
4537
4654
  );
4538
4655
  }
4539
- function assertSurfacesCompatible(requested, resolved, parentSurface, traceFunctionKey) {
4656
+ function assertSurfacesCompatible(requested, resolved2, parentSurface, traceFunctionKey) {
4540
4657
  if (requested === "inherit" || requested === "neutral") {
4541
4658
  return;
4542
4659
  }
4543
- if (resolved === void 0 || parentSurface === void 0) {
4660
+ if (resolved2 === void 0 || parentSurface === void 0) {
4544
4661
  return;
4545
4662
  }
4546
- if (parentSurface === resolved) {
4663
+ if (parentSurface === resolved2) {
4547
4664
  return;
4548
4665
  }
4549
4666
  throw mixedTracingError(
4550
- SURFACE_API[resolved],
4551
- resolved,
4667
+ SURFACE_API[resolved2],
4668
+ resolved2,
4552
4669
  parentSurface,
4553
4670
  traceFunctionKey
4554
4671
  );
4555
4672
  }
4556
4673
 
4674
+ // src/commitRef.ts
4675
+ init_gitCommand();
4676
+ init_readEnv();
4677
+ var EXPLICIT_SHA_ENV = "BITFAB_COMMIT_SHA";
4678
+ var DISABLE_ENV = "BITFAB_DISABLE_COMMIT_REF";
4679
+ var GIT_TIMEOUT_MS = 2e3;
4680
+ var GIT_MAX_BUFFER = 1024 * 1024;
4681
+ var VERCEL_PROVIDER_HOSTS = {
4682
+ github: "github.com",
4683
+ gitlab: "gitlab.com",
4684
+ bitbucket: "bitbucket.org"
4685
+ };
4686
+ function read(env, name) {
4687
+ if (name === null) {
4688
+ return null;
4689
+ }
4690
+ const value = env(name)?.trim();
4691
+ return value ? value : null;
4692
+ }
4693
+ function remoteFrom(...names) {
4694
+ return (env) => {
4695
+ const parts = names.map((name) => read(env, name));
4696
+ if (parts.some((part) => part === null)) {
4697
+ return null;
4698
+ }
4699
+ return normalizeRemote(parts.join("/"));
4700
+ };
4701
+ }
4702
+ function vercelRemote(env) {
4703
+ const provider = read(env, "VERCEL_GIT_PROVIDER")?.toLowerCase() ?? "";
4704
+ const host = VERCEL_PROVIDER_HOSTS[provider];
4705
+ const owner = read(env, "VERCEL_GIT_REPO_OWNER");
4706
+ const slug = read(env, "VERCEL_GIT_REPO_SLUG");
4707
+ if (!host || !owner || !slug) {
4708
+ return null;
4709
+ }
4710
+ return `${host}/${owner}/${slug}`;
4711
+ }
4712
+ function noRemote() {
4713
+ return null;
4714
+ }
4715
+ var PLATFORM_ENVS = [
4716
+ ["VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF", vercelRemote],
4717
+ [
4718
+ "GITHUB_SHA",
4719
+ "GITHUB_REF_NAME",
4720
+ remoteFrom("GITHUB_SERVER_URL", "GITHUB_REPOSITORY")
4721
+ ],
4722
+ ["RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH", noRemote],
4723
+ ["RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH", noRemote],
4724
+ ["SOURCE_VERSION", null, noRemote],
4725
+ ["CF_PAGES_COMMIT_SHA", "CF_PAGES_BRANCH", noRemote],
4726
+ ["CI_COMMIT_SHA", "CI_COMMIT_REF_NAME", remoteFrom("CI_REPOSITORY_URL")],
4727
+ [
4728
+ "BUILD_SOURCEVERSION",
4729
+ "BUILD_SOURCEBRANCHNAME",
4730
+ remoteFrom("BUILD_REPOSITORY_URI")
4731
+ ],
4732
+ ["CIRCLE_SHA1", "CIRCLE_BRANCH", remoteFrom("CIRCLE_REPOSITORY_URL")]
4733
+ ];
4734
+ var COMMIT_REF_ENV_NAMES = [
4735
+ EXPLICIT_SHA_ENV,
4736
+ DISABLE_ENV,
4737
+ ...PLATFORM_ENVS.flatMap(
4738
+ ([sha, branch]) => branch === null ? [sha] : [sha, branch]
4739
+ ),
4740
+ "GITHUB_SERVER_URL",
4741
+ "GITHUB_REPOSITORY",
4742
+ "CI_REPOSITORY_URL",
4743
+ "BUILD_REPOSITORY_URI",
4744
+ "CIRCLE_REPOSITORY_URL",
4745
+ "VERCEL_GIT_PROVIDER",
4746
+ "VERCEL_GIT_REPO_OWNER",
4747
+ "VERCEL_GIT_REPO_SLUG"
4748
+ ];
4749
+ function normalizeRemote(raw) {
4750
+ const value = raw?.trim();
4751
+ if (!value) {
4752
+ return null;
4753
+ }
4754
+ let host = "";
4755
+ let path = value;
4756
+ if (value.includes("://")) {
4757
+ try {
4758
+ const parsed = new URL(value);
4759
+ host = parsed.hostname;
4760
+ path = parsed.pathname;
4761
+ } catch {
4762
+ return null;
4763
+ }
4764
+ } else {
4765
+ const separator = value.indexOf(":");
4766
+ if (separator !== -1) {
4767
+ const head = value.slice(0, separator);
4768
+ host = head.slice(head.lastIndexOf("@") + 1);
4769
+ path = value.slice(separator + 1);
4770
+ }
4771
+ }
4772
+ path = path.replace(/^\/+|\/+$/g, "");
4773
+ if (path.endsWith(".git")) {
4774
+ path = path.slice(0, -".git".length).replace(/\/+$/, "");
4775
+ }
4776
+ if (!host) {
4777
+ return path || null;
4778
+ }
4779
+ return path ? `${host}/${path}` : host;
4780
+ }
4781
+ function resolveCommitRefFromEnv(env) {
4782
+ const explicit = read(env, EXPLICIT_SHA_ENV);
4783
+ const platform = PLATFORM_ENVS.find(([sha]) => read(env, sha) !== null);
4784
+ if (!platform) {
4785
+ if (explicit === null) {
4786
+ return null;
4787
+ }
4788
+ return {
4789
+ sha: explicit,
4790
+ branch: null,
4791
+ dirty: null,
4792
+ remote: null,
4793
+ rootSha: null
4794
+ };
4795
+ }
4796
+ const [shaEnv, branchEnv, readRemote] = platform;
4797
+ return {
4798
+ sha: explicit ?? read(env, shaEnv),
4799
+ branch: read(env, branchEnv),
4800
+ dirty: null,
4801
+ remote: readRemote(env),
4802
+ rootSha: null
4803
+ };
4804
+ }
4805
+ async function resolveCommitRefFromGit(cwd) {
4806
+ const git = await gitRunner({
4807
+ timeoutMs: GIT_TIMEOUT_MS,
4808
+ maxBuffer: GIT_MAX_BUFFER,
4809
+ keepProcessAlive: false
4810
+ });
4811
+ if (!git) {
4812
+ return null;
4813
+ }
4814
+ const run = async (args) => (await git(cwd, args))?.trim() ?? null;
4815
+ const sha = await run(["rev-parse", "HEAD"]);
4816
+ if (!sha) {
4817
+ return null;
4818
+ }
4819
+ const [branch, status, remote, roots] = await Promise.all([
4820
+ run(["symbolic-ref", "--short", "-q", "HEAD"]),
4821
+ run(["status", "--porcelain"]),
4822
+ run(["remote", "get-url", "origin"]),
4823
+ run(["rev-list", "--max-parents=0", "HEAD"])
4824
+ ]);
4825
+ return {
4826
+ sha,
4827
+ branch: branch || null,
4828
+ dirty: status === null ? null : status.length > 0,
4829
+ remote: normalizeRemote(remote),
4830
+ rootSha: roots ? roots.split(/\s+/).sort()[0] ?? null : null
4831
+ };
4832
+ }
4833
+ function commitRefWire(ref2) {
4834
+ return {
4835
+ sha: ref2.sha,
4836
+ branch: ref2.branch,
4837
+ dirty: ref2.dirty,
4838
+ remote: ref2.remote,
4839
+ root_sha: ref2.rootSha
4840
+ };
4841
+ }
4842
+ var resolved = false;
4843
+ var ref = null;
4844
+ var gitStarted = false;
4845
+ var settled = Promise.resolve();
4846
+ function startCommitRefResolution() {
4847
+ if (resolved || gitStarted) {
4848
+ return;
4849
+ }
4850
+ if (read(readEnv, DISABLE_ENV) !== null) {
4851
+ resolved = true;
4852
+ return;
4853
+ }
4854
+ const fromEnv = resolveCommitRefFromEnv(readEnv);
4855
+ if (fromEnv) {
4856
+ ref = fromEnv;
4857
+ resolved = true;
4858
+ return;
4859
+ }
4860
+ gitStarted = true;
4861
+ if (typeof process === "undefined") {
4862
+ resolved = true;
4863
+ return;
4864
+ }
4865
+ let cwd;
4866
+ try {
4867
+ cwd = process.cwd?.() ?? ".";
4868
+ } catch {
4869
+ resolved = true;
4870
+ return;
4871
+ }
4872
+ settled = resolveCommitRefFromGit(cwd).then(
4873
+ (fromGit) => {
4874
+ if (!resolved) {
4875
+ ref = fromGit;
4876
+ resolved = true;
4877
+ }
4878
+ },
4879
+ () => {
4880
+ resolved = true;
4881
+ }
4882
+ );
4883
+ }
4884
+ function currentCommitRef() {
4885
+ startCommitRefResolution();
4886
+ return ref;
4887
+ }
4888
+
4557
4889
  // src/client.ts
4558
4890
  init_constants();
4559
4891
 
@@ -5046,11 +5378,11 @@ var BitfabLangGraphCallbackHandler = class {
5046
5378
  this.invocations.set(invocation.rootRunId, invocation);
5047
5379
  }
5048
5380
  if (!willHide) {
5049
- let resolved = parentSpan;
5050
- while (resolved?.hidden === true) {
5051
- resolved = resolved.parentId ? this.runToSpan.get(resolved.parentId) : void 0;
5381
+ let resolved2 = parentSpan;
5382
+ while (resolved2?.hidden === true) {
5383
+ resolved2 = resolved2.parentId ? this.runToSpan.get(resolved2.parentId) : void 0;
5052
5384
  }
5053
- effectiveParentId = resolved ? resolved.spanId : invocation.activeContext?.spanId ?? null;
5385
+ effectiveParentId = resolved2 ? resolved2.spanId : invocation.activeContext?.spanId ?? null;
5054
5386
  } else {
5055
5387
  effectiveParentId = parentRunId ?? null;
5056
5388
  }
@@ -5718,6 +6050,7 @@ var BitfabOpenAIAgentHandler = class {
5718
6050
 
5719
6051
  // src/client.ts
5720
6052
  init_randomUuid();
6053
+ init_replay();
5721
6054
 
5722
6055
  // src/replayBranch.ts
5723
6056
  var _url, _context;
@@ -6549,6 +6882,7 @@ var Bitfab = class {
6549
6882
  validateDbSnapshotConfig(config.dbSnapshot);
6550
6883
  }
6551
6884
  this.dbSnapshot = config.dbSnapshot;
6885
+ startCommitRefResolution();
6552
6886
  this.httpClient = new HttpClient({
6553
6887
  apiKey: () => this.resolveApiKey(),
6554
6888
  serviceUrl: this.serviceUrl,
@@ -7642,15 +7976,15 @@ var Bitfab = class {
7642
7976
  );
7643
7977
  }
7644
7978
  return runWithSpanStack(newStack, async () => {
7645
- const resolved = await resolution;
7646
- if (resolved.matched) {
7979
+ const resolved2 = await resolution;
7980
+ if (resolved2.matched) {
7647
7981
  void sendSpan({
7648
- result: resolved.output,
7982
+ result: resolved2.output,
7649
7983
  mocked: true,
7650
7984
  mockTarget: "output",
7651
7985
  mockSource: "override"
7652
7986
  });
7653
- return resolved.output;
7987
+ return resolved2.output;
7654
7988
  }
7655
7989
  if (shouldMockWithBaseStrategy && !mockSpan) {
7656
7990
  throw new BitfabError(
@@ -7886,6 +8220,10 @@ var Bitfab = class {
7886
8220
  if (params.dbSnapshotRef) {
7887
8221
  rawTrace.db_snapshot_ref = params.dbSnapshotRef;
7888
8222
  }
8223
+ const commitRef = currentCommitRef();
8224
+ if (commitRef) {
8225
+ rawTrace.commit_ref = commitRefWire(commitRef);
8226
+ }
7889
8227
  if (params.ingestionType) {
7890
8228
  rawTrace.ingestion_type = params.ingestionType;
7891
8229
  }
@@ -8145,6 +8483,36 @@ var Bitfab = class {
8145
8483
  }
8146
8484
  return traceId;
8147
8485
  }
8486
+ async reseedTrace(traceFunctionKey, fn, options) {
8487
+ const source = await this.httpClient.getReseedSource(options.traceId);
8488
+ if (source.traceFunctionKey !== traceFunctionKey) {
8489
+ throw new BitfabError(
8490
+ `Trace ${options.traceId} belongs to '${source.traceFunctionKey}', not '${traceFunctionKey}'.`
8491
+ );
8492
+ }
8493
+ const args = deserializeInputs({
8494
+ input: source.input,
8495
+ input_meta: source.inputMeta
8496
+ });
8497
+ const runTraceId = await this.seedTraceByRunning(
8498
+ traceFunctionKey,
8499
+ fn,
8500
+ {
8501
+ args,
8502
+ metadata: source.metadata,
8503
+ ...source.sessionId === null ? {} : { sessionId: source.sessionId },
8504
+ ...source.name === null ? {} : { name: source.name }
8505
+ }
8506
+ );
8507
+ const adopted = await this.httpClient.reseedTrace(
8508
+ options.traceId,
8509
+ runTraceId
8510
+ );
8511
+ return {
8512
+ traceId: adopted.traceId,
8513
+ previousRunTraceId: adopted.previousRunTraceId
8514
+ };
8515
+ }
8148
8516
  async replay(traceFunctionKey, fn, options) {
8149
8517
  const wrappedKey = fn._bitfabTraceFunctionKey;
8150
8518
  let replayFn = fn;
@@ -8406,6 +8774,24 @@ init_replay();
8406
8774
  function defineReplayRegistry(registry) {
8407
8775
  return registry;
8408
8776
  }
8777
+ async function reseedFromRegistry(registry, pipeline, traceIds) {
8778
+ const registration = registry[pipeline];
8779
+ if (registration === void 0) {
8780
+ throw new BitfabError(
8781
+ `Unknown pipeline '${pipeline}'. Registered: ${Object.keys(registry).join(", ")}`
8782
+ );
8783
+ }
8784
+ const traceFunctionKey = resolveTraceFunctionKey(registration);
8785
+ const reseeded = [];
8786
+ for (const traceId of traceIds) {
8787
+ reseeded.push(
8788
+ await registration.client.reseedTrace(traceFunctionKey, registration.fn, {
8789
+ traceId
8790
+ })
8791
+ );
8792
+ }
8793
+ return { pipeline, traceFunctionKey, reseeded };
8794
+ }
8409
8795
  async function seedFromRegistry(registry, pipeline, cases, options = {}) {
8410
8796
  const registration = registry[pipeline];
8411
8797
  if (registration === void 0) {
@@ -8482,6 +8868,7 @@ function resolveTraceFunctionKey(registration) {
8482
8868
  getCurrentSpan,
8483
8869
  getCurrentTrace,
8484
8870
  reportReplayProgress,
8871
+ reseedFromRegistry,
8485
8872
  seedFromRegistry,
8486
8873
  serializeReplayResult
8487
8874
  });