@bitfab/sdk 0.29.1 → 0.30.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.
- package/dist/{chunk-RTPEBWVO.js → chunk-ERHYLE4S.js} +8 -5
- package/dist/chunk-ERHYLE4S.js.map +1 -0
- package/dist/{chunk-YZU6WFG2.js → chunk-MYDYCNW4.js} +205 -4
- package/dist/chunk-MYDYCNW4.js.map +1 -0
- package/dist/index.cjs +216 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +2 -2
- package/dist/node.cjs +216 -5
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +2 -2
- package/dist/{replay-SKW5A7II.js → replay-NFPCEVES.js} +2 -2
- package/package.json +2 -2
- package/dist/chunk-RTPEBWVO.js.map +0 -1
- package/dist/chunk-YZU6WFG2.js.map +0 -1
- /package/dist/{replay-SKW5A7II.js.map → replay-NFPCEVES.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -749,6 +749,15 @@ interface ReplayOptions {
|
|
|
749
749
|
* reconstructed. Validated server-side against the org.
|
|
750
750
|
*/
|
|
751
751
|
datasetId?: string;
|
|
752
|
+
/**
|
|
753
|
+
* Graders to attach directly to this experiment (test run), independent of any
|
|
754
|
+
* graders already on the dataset. The resulting experiment is graded by the
|
|
755
|
+
* union of these and the dataset's runnable graders at completion, so use this
|
|
756
|
+
* to grade a single run with a check you don't want to add to the dataset
|
|
757
|
+
* permanently. Each id must be an active/live grader belonging to the same
|
|
758
|
+
* organization and trace function, otherwise the server rejects the replay.
|
|
759
|
+
*/
|
|
760
|
+
graderIds?: string[];
|
|
752
761
|
/**
|
|
753
762
|
* Reshape recorded inputs before they are spread into `fn`.
|
|
754
763
|
*
|
|
@@ -1799,7 +1808,7 @@ declare class BitfabFunction {
|
|
|
1799
1808
|
/**
|
|
1800
1809
|
* SDK version from package.json (injected at build time)
|
|
1801
1810
|
*/
|
|
1802
|
-
declare const __version__ = "0.
|
|
1811
|
+
declare const __version__ = "0.30.1";
|
|
1803
1812
|
|
|
1804
1813
|
/**
|
|
1805
1814
|
* Constants for the Bitfab SDK.
|
package/dist/index.d.ts
CHANGED
|
@@ -749,6 +749,15 @@ interface ReplayOptions {
|
|
|
749
749
|
* reconstructed. Validated server-side against the org.
|
|
750
750
|
*/
|
|
751
751
|
datasetId?: string;
|
|
752
|
+
/**
|
|
753
|
+
* Graders to attach directly to this experiment (test run), independent of any
|
|
754
|
+
* graders already on the dataset. The resulting experiment is graded by the
|
|
755
|
+
* union of these and the dataset's runnable graders at completion, so use this
|
|
756
|
+
* to grade a single run with a check you don't want to add to the dataset
|
|
757
|
+
* permanently. Each id must be an active/live grader belonging to the same
|
|
758
|
+
* organization and trace function, otherwise the server rejects the replay.
|
|
759
|
+
*/
|
|
760
|
+
graderIds?: string[];
|
|
752
761
|
/**
|
|
753
762
|
* Reshape recorded inputs before they are spread into `fn`.
|
|
754
763
|
*
|
|
@@ -1799,7 +1808,7 @@ declare class BitfabFunction {
|
|
|
1799
1808
|
/**
|
|
1800
1809
|
* SDK version from package.json (injected at build time)
|
|
1801
1810
|
*/
|
|
1802
|
-
declare const __version__ = "0.
|
|
1811
|
+
declare const __version__ = "0.30.1";
|
|
1803
1812
|
|
|
1804
1813
|
/**
|
|
1805
1814
|
* Constants for the Bitfab SDK.
|
package/dist/index.js
CHANGED
|
@@ -14,12 +14,12 @@ import {
|
|
|
14
14
|
flushTraces,
|
|
15
15
|
getCurrentSpan,
|
|
16
16
|
getCurrentTrace
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ERHYLE4S.js";
|
|
18
18
|
import {
|
|
19
19
|
BITFAB_PROGRESS_PREFIX,
|
|
20
20
|
BitfabError,
|
|
21
21
|
reportReplayProgress
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-MYDYCNW4.js";
|
|
23
23
|
export {
|
|
24
24
|
BITFAB_PROGRESS_PREFIX,
|
|
25
25
|
Bitfab,
|
package/dist/node.cjs
CHANGED
|
@@ -327,6 +327,203 @@ var init_replayContext = __esm({
|
|
|
327
327
|
}
|
|
328
328
|
});
|
|
329
329
|
|
|
330
|
+
// src/codeChange.ts
|
|
331
|
+
async function resolveAutoCodeChange(label) {
|
|
332
|
+
if (typeof process === "undefined") {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
if (process.env?.BITFAB_DISABLE_CODE_CHANGE_CAPTURE) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
const fromEnv = await readCodeChangeFile();
|
|
339
|
+
if (fromEnv) {
|
|
340
|
+
return fromEnv;
|
|
341
|
+
}
|
|
342
|
+
return captureCodeChangeFromGit(process.cwd?.() ?? ".", label);
|
|
343
|
+
}
|
|
344
|
+
async function readCodeChangeFile() {
|
|
345
|
+
const path = process.env?.BITFAB_CODE_CHANGE_PATH;
|
|
346
|
+
if (!path) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const { readFile } = await import("fs/promises");
|
|
351
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
352
|
+
const files = Array.isArray(parsed?.files) && parsed.files.every(
|
|
353
|
+
(f) => typeof f === "object" && f !== null && !Array.isArray(f)
|
|
354
|
+
) ? parsed.files : void 0;
|
|
355
|
+
const description = typeof parsed?.description === "string" ? parsed.description : void 0;
|
|
356
|
+
if (!files && description === void 0) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
return { description, files };
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function captureCodeChangeFromGit(cwd, label) {
|
|
365
|
+
let execFile;
|
|
366
|
+
let readFile;
|
|
367
|
+
try {
|
|
368
|
+
;
|
|
369
|
+
({ execFile } = await import("child_process"));
|
|
370
|
+
({ readFile } = await import("fs/promises"));
|
|
371
|
+
} catch {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const git = (dir, args) => new Promise((resolve) => {
|
|
375
|
+
execFile(
|
|
376
|
+
"git",
|
|
377
|
+
args,
|
|
378
|
+
// 30s timeout so a hung git (e.g. a network-touching ref op) can't
|
|
379
|
+
// block the whole replay indefinitely.
|
|
380
|
+
{ cwd: dir, maxBuffer: 64 * 1024 * 1024, timeout: 3e4 },
|
|
381
|
+
(err, stdout) => resolve(err ? null : stdout)
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
try {
|
|
385
|
+
const root = (await git(cwd, ["rev-parse", "--show-toplevel"]))?.trim();
|
|
386
|
+
if (!root) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const resolved = await resolveBase(git, root);
|
|
390
|
+
if (!resolved) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const { base, fromTrunk } = resolved;
|
|
394
|
+
const blobBytes = async (ref, path) => {
|
|
395
|
+
const out = await git(root, ["cat-file", "-s", `${ref}:${path}`]);
|
|
396
|
+
const n = out ? Number.parseInt(out.trim(), 10) : Number.NaN;
|
|
397
|
+
return Number.isFinite(n) ? n : 0;
|
|
398
|
+
};
|
|
399
|
+
const workingBytes = async (path) => {
|
|
400
|
+
try {
|
|
401
|
+
const { stat } = await import("fs/promises");
|
|
402
|
+
const { join } = await import("path");
|
|
403
|
+
return (await stat(join(root, path))).size;
|
|
404
|
+
} catch {
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const tracked = await git(root, [
|
|
409
|
+
"diff",
|
|
410
|
+
"--name-status",
|
|
411
|
+
"--no-renames",
|
|
412
|
+
"-z",
|
|
413
|
+
base,
|
|
414
|
+
"--",
|
|
415
|
+
":!.bitfab"
|
|
416
|
+
]);
|
|
417
|
+
const untracked = await git(root, [
|
|
418
|
+
"ls-files",
|
|
419
|
+
"--others",
|
|
420
|
+
"--exclude-standard",
|
|
421
|
+
"-z",
|
|
422
|
+
"--",
|
|
423
|
+
":!.bitfab"
|
|
424
|
+
]);
|
|
425
|
+
const entries = [
|
|
426
|
+
...parseNameStatusZ(tracked ?? ""),
|
|
427
|
+
...(untracked ?? "").split(NUL).filter((p) => p.length > 0).map((path) => ({ status: "A", path }))
|
|
428
|
+
];
|
|
429
|
+
if (entries.length === 0) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
const files = [];
|
|
433
|
+
let totalBytes = 0;
|
|
434
|
+
for (const { status, path } of entries) {
|
|
435
|
+
if (files.length >= MAX_FILES) {
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
const beforeBytes = status === "A" ? 0 : await blobBytes(base, path);
|
|
439
|
+
const afterBytes = status === "D" ? 0 : await workingBytes(path);
|
|
440
|
+
if (beforeBytes > MAX_FILE_BYTES || afterBytes > MAX_FILE_BYTES) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const before = (status === "A" ? "" : await git(root, ["show", `${base}:${path}`]) ?? "").replace(/\r\n/g, "\n");
|
|
444
|
+
const after = (status === "D" ? "" : await readWorkingFile(readFile, root, path)).replace(/\r\n/g, "\n");
|
|
445
|
+
if (before === after) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const size = Buffer.byteLength(before, "utf8") + Buffer.byteLength(after, "utf8");
|
|
449
|
+
if (totalBytes + size > MAX_TOTAL_BYTES || looksBinary(before) || looksBinary(after)) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
totalBytes += size;
|
|
453
|
+
files.push({ path, before, after });
|
|
454
|
+
}
|
|
455
|
+
if (files.length === 0) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
const subject = (await git(root, ["log", "-1", "--format=%s", "HEAD"]))?.trim();
|
|
459
|
+
const fileWord = files.length === 1 ? "file" : "files";
|
|
460
|
+
const head = label?.trim() || subject || "Working-tree change";
|
|
461
|
+
const against = fromTrunk ? "vs trunk" : "uncommitted (vs HEAD)";
|
|
462
|
+
return {
|
|
463
|
+
description: `${head} (${files.length} ${fileWord} changed ${against})`,
|
|
464
|
+
files
|
|
465
|
+
};
|
|
466
|
+
} catch {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async function resolveBase(git, root) {
|
|
471
|
+
const forced = process.env?.BITFAB_CODE_CHANGE_BASE;
|
|
472
|
+
if (forced && await refExists(git, root, forced)) {
|
|
473
|
+
const base = (await git(root, ["merge-base", "HEAD", forced]))?.trim() || (await git(root, ["rev-parse", "--verify", forced]))?.trim() || null;
|
|
474
|
+
return base ? { base, fromTrunk: true } : null;
|
|
475
|
+
}
|
|
476
|
+
for (const candidate of TRUNK_CANDIDATES) {
|
|
477
|
+
if (!await refExists(git, root, candidate)) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const mb = (await git(root, ["merge-base", "HEAD", candidate]))?.trim();
|
|
481
|
+
if (mb) {
|
|
482
|
+
return { base: mb, fromTrunk: true };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return await refExists(git, root, "HEAD") ? { base: "HEAD", fromTrunk: false } : null;
|
|
486
|
+
}
|
|
487
|
+
async function refExists(git, root, ref) {
|
|
488
|
+
return await git(root, ["rev-parse", "--verify", `${ref}^{object}`]) !== null;
|
|
489
|
+
}
|
|
490
|
+
async function readWorkingFile(readFile, root, path) {
|
|
491
|
+
try {
|
|
492
|
+
const { join } = await import("path");
|
|
493
|
+
return await readFile(join(root, path), "utf8");
|
|
494
|
+
} catch {
|
|
495
|
+
return "";
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function parseNameStatusZ(raw) {
|
|
499
|
+
const parts = raw.split(NUL).filter((p) => p.length > 0);
|
|
500
|
+
const out = [];
|
|
501
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
502
|
+
out.push({ status: parts[i].charAt(0), path: parts[i + 1] });
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
function looksBinary(s) {
|
|
507
|
+
return s.slice(0, 8e3).includes(NUL);
|
|
508
|
+
}
|
|
509
|
+
var MAX_FILES, MAX_FILE_BYTES, MAX_TOTAL_BYTES, TRUNK_CANDIDATES, NUL;
|
|
510
|
+
var init_codeChange = __esm({
|
|
511
|
+
"src/codeChange.ts"() {
|
|
512
|
+
"use strict";
|
|
513
|
+
MAX_FILES = 60;
|
|
514
|
+
MAX_FILE_BYTES = 5e5;
|
|
515
|
+
MAX_TOTAL_BYTES = 2e6;
|
|
516
|
+
TRUNK_CANDIDATES = [
|
|
517
|
+
"origin/HEAD",
|
|
518
|
+
"origin/main",
|
|
519
|
+
"origin/master",
|
|
520
|
+
"main",
|
|
521
|
+
"master"
|
|
522
|
+
];
|
|
523
|
+
NUL = String.fromCharCode(0);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
330
527
|
// src/replay.ts
|
|
331
528
|
var replay_exports = {};
|
|
332
529
|
__export(replay_exports, {
|
|
@@ -551,6 +748,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
551
748
|
}
|
|
552
749
|
}
|
|
553
750
|
await replayContextReady;
|
|
751
|
+
let codeChangeDescription = options?.codeChangeDescription;
|
|
752
|
+
let codeChangeFiles = options?.codeChangeFiles;
|
|
753
|
+
if (codeChangeDescription === void 0 && codeChangeFiles === void 0) {
|
|
754
|
+
const captured = await resolveAutoCodeChange(options?.name);
|
|
755
|
+
if (captured) {
|
|
756
|
+
codeChangeDescription = captured.description;
|
|
757
|
+
codeChangeFiles = captured.files;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
554
760
|
const {
|
|
555
761
|
testRunId,
|
|
556
762
|
testRunUrl,
|
|
@@ -562,12 +768,13 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
562
768
|
options?.traceIds ? void 0 : options?.limit ?? 5,
|
|
563
769
|
options?.traceIds,
|
|
564
770
|
options?.name,
|
|
565
|
-
|
|
566
|
-
|
|
771
|
+
codeChangeDescription,
|
|
772
|
+
codeChangeFiles,
|
|
567
773
|
options?.environment !== void 0,
|
|
568
774
|
// includeDbBranchLease
|
|
569
775
|
options?.experimentGroupId,
|
|
570
|
-
options?.datasetId
|
|
776
|
+
options?.datasetId,
|
|
777
|
+
options?.graderIds
|
|
571
778
|
);
|
|
572
779
|
const mockStrategy = options?.mock ?? "marked";
|
|
573
780
|
const maxConcurrency = options?.maxConcurrency ?? 10;
|
|
@@ -718,6 +925,7 @@ var BITFAB_PROGRESS_PREFIX;
|
|
|
718
925
|
var init_replay = __esm({
|
|
719
926
|
"src/replay.ts"() {
|
|
720
927
|
"use strict";
|
|
928
|
+
init_codeChange();
|
|
721
929
|
init_errors();
|
|
722
930
|
init_mockOverride();
|
|
723
931
|
init_randomUuid();
|
|
@@ -760,7 +968,7 @@ registerAsyncLocalStorageClass(
|
|
|
760
968
|
);
|
|
761
969
|
|
|
762
970
|
// src/version.generated.ts
|
|
763
|
-
var __version__ = "0.
|
|
971
|
+
var __version__ = "0.30.1";
|
|
764
972
|
|
|
765
973
|
// src/constants.ts
|
|
766
974
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
@@ -1119,7 +1327,7 @@ var HttpClient = class {
|
|
|
1119
1327
|
* Start a replay session by fetching historical traces.
|
|
1120
1328
|
* Blocking call - creates a test run and returns lightweight item references.
|
|
1121
1329
|
*/
|
|
1122
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetId) {
|
|
1330
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetId, graderIds) {
|
|
1123
1331
|
const payload = { traceFunctionKey };
|
|
1124
1332
|
if (limit !== void 0) {
|
|
1125
1333
|
payload.limit = limit;
|
|
@@ -1145,6 +1353,9 @@ var HttpClient = class {
|
|
|
1145
1353
|
if (datasetId !== void 0) {
|
|
1146
1354
|
payload.datasetId = datasetId;
|
|
1147
1355
|
}
|
|
1356
|
+
if (graderIds !== void 0) {
|
|
1357
|
+
payload.graderIds = graderIds;
|
|
1358
|
+
}
|
|
1148
1359
|
const timeout = includeDbBranchLease ? 18e4 : 3e4;
|
|
1149
1360
|
return this.request("/api/sdk/replay/start", payload, {
|
|
1150
1361
|
timeout
|