terminalhire 0.42.35 → 0.42.37
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/bin/jpi-claim.js +629 -85
- package/dist/bin/jpi-dispatch.js +660 -97
- package/dist/bin/jpi-mcp.js +640 -90
- package/dist/bin/jpi-run.js +33501 -524
- package/package.json +1 -1
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -26207,12 +26207,22 @@ function reportedCounts(classified, reparsed) {
|
|
|
26207
26207
|
function readCounts(stdout, stderr = "") {
|
|
26208
26208
|
const out = `${stdout}
|
|
26209
26209
|
${stderr}`;
|
|
26210
|
+
const matched = [];
|
|
26210
26211
|
for (const reader of READERS) {
|
|
26211
26212
|
const counts = reader.read(out);
|
|
26212
26213
|
if (counts)
|
|
26213
|
-
|
|
26214
|
+
matched.push({ reader, counts });
|
|
26214
26215
|
}
|
|
26215
|
-
|
|
26216
|
+
const first = matched[0];
|
|
26217
|
+
if (first === void 0)
|
|
26218
|
+
return null;
|
|
26219
|
+
const legs = matched.filter(({ reader }) => !matched.some((m) => reader.shadowedBy?.includes(m.reader.runner)));
|
|
26220
|
+
const partial = legs.length > 1 || (first.reader.summaries?.(out) ?? 1) > 1;
|
|
26221
|
+
return {
|
|
26222
|
+
...first.counts,
|
|
26223
|
+
runner: first.reader.runner,
|
|
26224
|
+
...partial ? { coverage: "partial" } : {}
|
|
26225
|
+
};
|
|
26216
26226
|
}
|
|
26217
26227
|
function coverageThresholdUnmet(facts, counts) {
|
|
26218
26228
|
if (counts === null || counts.runner !== "node-tap")
|
|
@@ -26371,7 +26381,7 @@ function isGreen(outcome) {
|
|
|
26371
26381
|
function isOurFault(outcome) {
|
|
26372
26382
|
return LIMIT_OWNER[outcome] === "ours";
|
|
26373
26383
|
}
|
|
26374
|
-
var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
|
|
26384
|
+
var VERIFICATION_OUTCOMES, int, withSuiteFailures, countLines, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
|
|
26375
26385
|
var init_classify2 = __esm({
|
|
26376
26386
|
"../../packages/envrun/dist/classify.js"() {
|
|
26377
26387
|
"use strict";
|
|
@@ -26390,6 +26400,7 @@ var init_classify2 = __esm({
|
|
|
26390
26400
|
return counts;
|
|
26391
26401
|
return { ...counts, tests_failed: int(/(\d+) failed/.exec(suiteLine[1])) };
|
|
26392
26402
|
};
|
|
26403
|
+
countLines = (out, re) => (out.match(re) ?? []).length;
|
|
26393
26404
|
READERS = [
|
|
26394
26405
|
{
|
|
26395
26406
|
// `node --test` TAP. Anchored to line start, which is what makes it a
|
|
@@ -26401,7 +26412,10 @@ var init_classify2 = __esm({
|
|
|
26401
26412
|
if (!pass || !fail2)
|
|
26402
26413
|
return null;
|
|
26403
26414
|
return { tests_passed: int(pass), tests_failed: int(fail2) };
|
|
26404
|
-
}
|
|
26415
|
+
},
|
|
26416
|
+
// One `# pass N` per invocation, so a script that runs `node --test` once per file
|
|
26417
|
+
// prints many. envrun's own offline leg printed 67 and was read as the first one's 4.
|
|
26418
|
+
summaries: (out) => countLines(out, /^# pass \d+$/gm)
|
|
26405
26419
|
},
|
|
26406
26420
|
{
|
|
26407
26421
|
// node-tap's runner summary, which is NOT the `# pass N` / `# fail N` pair
|
|
@@ -26428,7 +26442,8 @@ var init_classify2 = __esm({
|
|
|
26428
26442
|
tests_passed: int(/\bpass: (\d+)/.exec(line[1])),
|
|
26429
26443
|
tests_failed: int(/\bfail: (\d+)/.exec(line[1]))
|
|
26430
26444
|
};
|
|
26431
|
-
}
|
|
26445
|
+
},
|
|
26446
|
+
summaries: (out) => countLines(out, /^# \{ total: \d+[^}\n]* \}$/gm)
|
|
26432
26447
|
},
|
|
26433
26448
|
{
|
|
26434
26449
|
// jest: `Tests: 1 failed, 2 passed, 3 total`
|
|
@@ -26441,7 +26456,8 @@ var init_classify2 = __esm({
|
|
|
26441
26456
|
tests_passed: int(/(\d+) passed/.exec(line[1])),
|
|
26442
26457
|
tests_failed: int(/(\d+) failed/.exec(line[1]))
|
|
26443
26458
|
}, /^Test Suites:\s+(.+?)\s*$/m.exec(out));
|
|
26444
|
-
}
|
|
26459
|
+
},
|
|
26460
|
+
summaries: (out) => countLines(out, /^Tests:\s+[^\n]*\btotal\b/gm)
|
|
26445
26461
|
},
|
|
26446
26462
|
{
|
|
26447
26463
|
// vitest: `Tests 3 passed (3)` / `Tests 1 failed | 2 passed (3)`
|
|
@@ -26458,7 +26474,12 @@ var init_classify2 = __esm({
|
|
|
26458
26474
|
if (!passed && !failed)
|
|
26459
26475
|
return null;
|
|
26460
26476
|
return withSuiteFailures({ tests_passed: int(passed), tests_failed: int(failed) }, files);
|
|
26461
|
-
}
|
|
26477
|
+
},
|
|
26478
|
+
// One invocation prints one `Test Files` line, and a numeric `Tests` line only when a
|
|
26479
|
+
// spec loaded (the every-spec-failed shape above has none). The larger of the two
|
|
26480
|
+
// counts invocations without counting a paired block twice; reading `Tests` alone
|
|
26481
|
+
// missed an all-failed run that followed a passing one (Codex review).
|
|
26482
|
+
summaries: (out) => Math.max(countLines(out, /^\s*Tests\s+[^\n]*\(\d+\)\s*$/gm), countLines(out, /^\s*Test Files\s+[^\n]*\(\d+\)\s*$/gm))
|
|
26462
26483
|
},
|
|
26463
26484
|
{
|
|
26464
26485
|
// pytest summary rule: `===== 297 passed in 0.54s =====`,
|
|
@@ -26481,7 +26502,10 @@ var init_classify2 = __esm({
|
|
|
26481
26502
|
if (!passed && !failed && !errors)
|
|
26482
26503
|
return null;
|
|
26483
26504
|
return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
|
|
26484
|
-
}
|
|
26505
|
+
},
|
|
26506
|
+
// Only FINAL tallies, which carry pytest's `in 0.54s`. The interim banners the reader
|
|
26507
|
+
// skips above carry no duration, and counting them would mark every red run partial.
|
|
26508
|
+
summaries: (out) => Math.max(1, countLines(out, /^=+ [^\n]*?(?:passed|failed|error|no tests ran)[^\n]*? in [\d.]+s[^\n]*? =+$/gm))
|
|
26485
26509
|
},
|
|
26486
26510
|
{
|
|
26487
26511
|
// Maven surefire's per-module summary (TERM-1122), measured against surefire
|
|
@@ -26543,7 +26567,8 @@ var init_classify2 = __esm({
|
|
|
26543
26567
|
return null;
|
|
26544
26568
|
const failed = Number(m[1]);
|
|
26545
26569
|
return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
|
|
26546
|
-
}
|
|
26570
|
+
},
|
|
26571
|
+
summaries: (out) => Math.max(1, countLines(out, /^\d+% tests passed, \d+ tests? failed out of \d+$/gm))
|
|
26547
26572
|
},
|
|
26548
26573
|
{
|
|
26549
26574
|
// mocha: ` 440 passing (1s)` and ` 2 failing`
|
|
@@ -26554,7 +26579,8 @@ var init_classify2 = __esm({
|
|
|
26554
26579
|
if (!passing && !failing)
|
|
26555
26580
|
return null;
|
|
26556
26581
|
return { tests_passed: int(passing), tests_failed: int(failing) };
|
|
26557
|
-
}
|
|
26582
|
+
},
|
|
26583
|
+
summaries: (out) => Math.max(countLines(out, /^\s*\d+ passing\b/gm), countLines(out, /^\s*\d+ failing\b/gm))
|
|
26558
26584
|
},
|
|
26559
26585
|
{
|
|
26560
26586
|
// ava: ` 3 tests passed` / ` 1 test failed`
|
|
@@ -26565,7 +26591,8 @@ var init_classify2 = __esm({
|
|
|
26565
26591
|
if (!passed && !failed)
|
|
26566
26592
|
return null;
|
|
26567
26593
|
return { tests_passed: int(passed), tests_failed: int(failed) };
|
|
26568
|
-
}
|
|
26594
|
+
},
|
|
26595
|
+
summaries: (out) => Math.max(countLines(out, /^\s*\d+ tests? passed\b/gm), countLines(out, /^\s*\d+ tests? failed\b/gm))
|
|
26569
26596
|
},
|
|
26570
26597
|
{
|
|
26571
26598
|
// uvu: a `Total:` / `Passed:` / `Skipped:` block.
|
|
@@ -26578,7 +26605,8 @@ var init_classify2 = __esm({
|
|
|
26578
26605
|
const t = int(total);
|
|
26579
26606
|
const p = int(passed);
|
|
26580
26607
|
return { tests_passed: p, tests_failed: Math.max(0, t - p) };
|
|
26581
|
-
}
|
|
26608
|
+
},
|
|
26609
|
+
summaries: (out) => countLines(out, /^\s*Total:\s+\d+/gm)
|
|
26582
26610
|
},
|
|
26583
26611
|
{
|
|
26584
26612
|
/**
|
|
@@ -26613,6 +26641,7 @@ var init_classify2 = __esm({
|
|
|
26613
26641
|
* those would double them against their parent.
|
|
26614
26642
|
*/
|
|
26615
26643
|
runner: "bare TAP",
|
|
26644
|
+
shadowedBy: ["node --test (TAP)", "node-tap"],
|
|
26616
26645
|
read: (out) => {
|
|
26617
26646
|
let n = 0;
|
|
26618
26647
|
let failed = 0;
|
|
@@ -30343,6 +30372,10 @@ var init_venueDescriptor = __esm({
|
|
|
30343
30372
|
function fmtMs(ms) {
|
|
30344
30373
|
return ms < 1e3 ? `${String(ms)}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
30345
30374
|
}
|
|
30375
|
+
function fmtMinutes(ms) {
|
|
30376
|
+
const min = ms / 6e4;
|
|
30377
|
+
return Number.isInteger(min) ? String(min) : min.toFixed(1);
|
|
30378
|
+
}
|
|
30346
30379
|
function renderVerdictLine(r) {
|
|
30347
30380
|
if (r.status === "refused") {
|
|
30348
30381
|
const first = r.boundaryRefusals[0];
|
|
@@ -30354,9 +30387,9 @@ function renderVerdictLine(r) {
|
|
|
30354
30387
|
const t = fmtMs(r.wallMs);
|
|
30355
30388
|
switch (r.outcome) {
|
|
30356
30389
|
case "completed":
|
|
30357
|
-
return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed
|
|
30390
|
+
return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed ` + (r.counts?.coverage === "partial" ? `(${PARTIAL_COUNTS_NOTE}) ` : "") + `\u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
|
|
30358
30391
|
case "tests-failed":
|
|
30359
|
-
return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`} \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
|
|
30392
|
+
return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`}` + (r.counts?.coverage === "partial" ? ` (${PARTIAL_COUNTS_NOTE})` : "") + ` \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
|
|
30360
30393
|
case "no-tests-observed":
|
|
30361
30394
|
return `NOTHING RAN the suite reported zero tests passed and zero failed \u2014 ${t}`;
|
|
30362
30395
|
case "counts-unparsed":
|
|
@@ -30366,7 +30399,7 @@ function renderVerdictLine(r) {
|
|
|
30366
30399
|
case "environment-exhausted":
|
|
30367
30400
|
return `OUR FAULT the run ran out of a resource we cap; your work has not been judged \u2014 ${t}`;
|
|
30368
30401
|
case "budget-exceeded":
|
|
30369
|
-
return `OUR LIMIT we stopped the run at our time limit; this is not a result about the work \u2014 ${t}`;
|
|
30402
|
+
return r.testTimeoutMs === void 0 ? `OUR LIMIT we stopped the run at our time limit; this is not a result about the work \u2014 ${t}` : `OUR LIMIT the ${fmtMinutes(r.testTimeoutMs)}-min test time limit set for this posting stopped the run; this is not a result about the work \u2014 ${t}`;
|
|
30370
30403
|
case null:
|
|
30371
30404
|
throw new Error("a verified run has no outcome \u2014 the result was assembled wrong");
|
|
30372
30405
|
}
|
|
@@ -30388,7 +30421,7 @@ function renderRunReport(r) {
|
|
|
30388
30421
|
if (r.status === "verified" && r.outcome === "counts-unparsed") {
|
|
30389
30422
|
lines.push("", "Your suite exited 0, but we could not read how many tests ran, so this is not a verdict either way.");
|
|
30390
30423
|
} else if (r.status === "verified" && r.outcome === "budget-exceeded") {
|
|
30391
|
-
lines.push("", "Our time limit ended the run; that says nothing about your work.");
|
|
30424
|
+
lines.push("", r.testTimeoutMs === void 0 ? "Our time limit ended the run; that says nothing about your work." : `The ${fmtMinutes(r.testTimeoutMs)}-min test time limit set for this posting ended the run; that says nothing about your work.`);
|
|
30392
30425
|
} else if (r.status === "verified" && r.outcome !== null && isOurFault(r.outcome)) {
|
|
30393
30426
|
lines.push("", "This is an environment failure on our side, not a statement about your work.");
|
|
30394
30427
|
}
|
|
@@ -30410,7 +30443,7 @@ function exitCodeFor(r) {
|
|
|
30410
30443
|
return 2;
|
|
30411
30444
|
return exitCodeForOutcome(r.outcome);
|
|
30412
30445
|
}
|
|
30413
|
-
var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
|
|
30446
|
+
var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, PARTIAL_COUNTS_NOTE, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
|
|
30414
30447
|
var init_result = __esm({
|
|
30415
30448
|
"../../packages/envrun/dist/result.js"() {
|
|
30416
30449
|
"use strict";
|
|
@@ -30433,6 +30466,7 @@ var init_result = __esm({
|
|
|
30433
30466
|
"founder-declared"
|
|
30434
30467
|
];
|
|
30435
30468
|
RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
|
|
30469
|
+
PARTIAL_COUNTS_NOTE = "from one of several test summaries in the output, so not a total for the whole run";
|
|
30436
30470
|
RUN_RESULT_FIELDS = [
|
|
30437
30471
|
"schema",
|
|
30438
30472
|
"runId",
|
|
@@ -30445,6 +30479,9 @@ var init_result = __esm({
|
|
|
30445
30479
|
"testOutputTail",
|
|
30446
30480
|
"counts",
|
|
30447
30481
|
"wallMs",
|
|
30482
|
+
"installMs",
|
|
30483
|
+
"testMs",
|
|
30484
|
+
"testTimeoutMs",
|
|
30448
30485
|
"targetRepo",
|
|
30449
30486
|
"targetSha",
|
|
30450
30487
|
"patchSha256",
|
|
@@ -30475,8 +30512,11 @@ var init_result = __esm({
|
|
|
30475
30512
|
testCommand: (r) => r.testCommand === null ? null : `test command ${r.testCommand}`,
|
|
30476
30513
|
testOutputTail: RENDER_NONE,
|
|
30477
30514
|
// printed as a block below the fields, when red
|
|
30478
|
-
counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})
|
|
30515
|
+
counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})` + (r.counts.coverage === "partial" ? ` \u2014 ${PARTIAL_COUNTS_NOTE}` : ""),
|
|
30479
30516
|
wallMs: (r) => `round trip ${fmtMs(r.wallMs)}`,
|
|
30517
|
+
installMs: (r) => r.installMs === void 0 ? null : `install step ${fmtMs(r.installMs)}`,
|
|
30518
|
+
testMs: (r) => r.testMs === void 0 ? null : `test step ${fmtMs(r.testMs)}`,
|
|
30519
|
+
testTimeoutMs: (r) => r.testTimeoutMs === void 0 ? null : `time limit ${fmtMinutes(r.testTimeoutMs)} min`,
|
|
30480
30520
|
targetRepo: (r) => `target ${r.targetRepo}`,
|
|
30481
30521
|
targetSha: (r) => `commit ${r.targetSha.slice(0, 12)}`,
|
|
30482
30522
|
patchSha256: (r) => r.patchSha256 === null ? null : `patch ${r.patchSha256.slice(0, 12)}`,
|
|
@@ -30571,7 +30611,11 @@ function toTestRunResult(result, outputSha256) {
|
|
|
30571
30611
|
return {
|
|
30572
30612
|
exit_code: result.exitCode ?? -1,
|
|
30573
30613
|
...outputSha256 === void 0 ? {} : { output_sha256: outputSha256 },
|
|
30574
|
-
...result.counts === null ? {} : {
|
|
30614
|
+
...result.counts === null ? {} : {
|
|
30615
|
+
tests_passed: result.counts.tests_passed,
|
|
30616
|
+
tests_failed: result.counts.tests_failed,
|
|
30617
|
+
...result.counts.coverage === "partial" ? { tests_coverage: "partial" } : {}
|
|
30618
|
+
}
|
|
30575
30619
|
};
|
|
30576
30620
|
}
|
|
30577
30621
|
function toAcceptancePredicate(pair, opts = {}) {
|
|
@@ -30726,7 +30770,8 @@ var init_attestation2 = __esm({
|
|
|
30726
30770
|
"test-command-unavailable": null,
|
|
30727
30771
|
// null, with the same reasoning as `test-command-unavailable` and NOT
|
|
30728
30772
|
// `budget-exceeded` (TERM-644). A signed `budget-exceeded` records one fact:
|
|
30729
|
-
// the run hit
|
|
30773
|
+
// the run hit the time budget we applied — our default, or the one set for the
|
|
30774
|
+
// posting (TERM-1308), which is still ours to enforce. It blames no one (TERM-1289; `LIMIT_OWNER` in
|
|
30730
30775
|
// `classify.ts` owns that question), and it is not an acceptable run:
|
|
30731
30776
|
// `acceptRun` (`packages/attest/src/verify.ts`) refuses every `budget_outcome`
|
|
30732
30777
|
// except `completed`. Exhausting the tmpfs we sized, or an OOM kill from memory we
|
|
@@ -31425,7 +31470,7 @@ var init_gcpPlacement = __esm({
|
|
|
31425
31470
|
this.name = "GcpPlacementError";
|
|
31426
31471
|
}
|
|
31427
31472
|
};
|
|
31428
|
-
GCP_MAX_RUN_DURATION_SECONDS =
|
|
31473
|
+
GCP_MAX_RUN_DURATION_SECONDS = 10800;
|
|
31429
31474
|
GCP_MANAGED_LABEL_KEY = "th-managed";
|
|
31430
31475
|
GCP_RUN_LABEL_KEY = "th-run";
|
|
31431
31476
|
CONFIDENTIAL_SPACE_MACHINE_TYPE = "n2d-standard-2";
|
|
@@ -33321,9 +33366,9 @@ var init_placement = __esm({
|
|
|
33321
33366
|
});
|
|
33322
33367
|
|
|
33323
33368
|
// ../../packages/envspec/dist/yaml.js
|
|
33324
|
-
function parseYaml(source) {
|
|
33325
|
-
const lines = splitLines(source);
|
|
33326
|
-
const cursor = { lines, index: 0 };
|
|
33369
|
+
function parseYaml(source, limits = {}) {
|
|
33370
|
+
const lines = splitLines(source, limits.maxLineLength ?? Infinity);
|
|
33371
|
+
const cursor = { lines, index: 0, depth: 0, maxDepth: limits.maxDepth ?? Infinity };
|
|
33327
33372
|
skipIgnorable(cursor);
|
|
33328
33373
|
if (cursor.index >= lines.length)
|
|
33329
33374
|
return null;
|
|
@@ -33335,13 +33380,16 @@ function parseYaml(source) {
|
|
|
33335
33380
|
}
|
|
33336
33381
|
return value;
|
|
33337
33382
|
}
|
|
33338
|
-
function splitLines(source) {
|
|
33383
|
+
function splitLines(source, maxLineLength) {
|
|
33339
33384
|
const raw = source.replace(/\r\n/g, "\n").split("\n");
|
|
33340
33385
|
const out = [];
|
|
33341
33386
|
let sawDocumentStart = false;
|
|
33342
33387
|
for (let i = 0; i < raw.length; i += 1) {
|
|
33343
33388
|
const line = raw[i];
|
|
33344
33389
|
const number = i + 1;
|
|
33390
|
+
if (line.length > maxLineLength) {
|
|
33391
|
+
throw new YamlUnsupportedError("line-length", number, `line longer than ${maxLineLength} characters`);
|
|
33392
|
+
}
|
|
33345
33393
|
const withoutIndent = line.replace(/^[ ]+/, "");
|
|
33346
33394
|
const indent = line.length - withoutIndent.length;
|
|
33347
33395
|
if (/^[ ]*\t/.test(line)) {
|
|
@@ -33378,6 +33426,18 @@ function peek(cursor) {
|
|
|
33378
33426
|
return cursor.index < cursor.lines.length ? cursor.lines[cursor.index] : null;
|
|
33379
33427
|
}
|
|
33380
33428
|
function parseNode(cursor, indent) {
|
|
33429
|
+
if (cursor.depth >= cursor.maxDepth) {
|
|
33430
|
+
const at = peek(cursor);
|
|
33431
|
+
throw new YamlUnsupportedError("depth", at?.number ?? 0, `nesting deeper than ${cursor.maxDepth}`);
|
|
33432
|
+
}
|
|
33433
|
+
cursor.depth += 1;
|
|
33434
|
+
try {
|
|
33435
|
+
return parseNodeAt(cursor, indent);
|
|
33436
|
+
} finally {
|
|
33437
|
+
cursor.depth -= 1;
|
|
33438
|
+
}
|
|
33439
|
+
}
|
|
33440
|
+
function parseNodeAt(cursor, indent) {
|
|
33381
33441
|
const line = peek(cursor);
|
|
33382
33442
|
if (line === null)
|
|
33383
33443
|
return null;
|
|
@@ -33797,6 +33857,178 @@ var init_database = __esm({
|
|
|
33797
33857
|
}
|
|
33798
33858
|
});
|
|
33799
33859
|
|
|
33860
|
+
// ../../packages/envspec/dist/semverRange.js
|
|
33861
|
+
function rangeAdmits(version, range) {
|
|
33862
|
+
const locked = parseVersion(version);
|
|
33863
|
+
if (locked === null || range.length > MAX_RANGE_LENGTH)
|
|
33864
|
+
return null;
|
|
33865
|
+
let unknown = false;
|
|
33866
|
+
for (const alternative of range.split("||")) {
|
|
33867
|
+
const interval = parseSet(alternative);
|
|
33868
|
+
if (interval === null) {
|
|
33869
|
+
unknown = true;
|
|
33870
|
+
continue;
|
|
33871
|
+
}
|
|
33872
|
+
if (within(locked, interval))
|
|
33873
|
+
return true;
|
|
33874
|
+
}
|
|
33875
|
+
return unknown ? null : false;
|
|
33876
|
+
}
|
|
33877
|
+
function parseVersion(text) {
|
|
33878
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)(?:\+[0-9A-Za-z.-]+)?$/.exec(text.trim());
|
|
33879
|
+
if (m === null || !NUMBER.test(m[1]) || !NUMBER.test(m[2]) || !NUMBER.test(m[3]))
|
|
33880
|
+
return null;
|
|
33881
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
33882
|
+
}
|
|
33883
|
+
function parseSet(text) {
|
|
33884
|
+
const set = text.trim();
|
|
33885
|
+
if (set === "")
|
|
33886
|
+
return { low: null, high: null };
|
|
33887
|
+
const hyphen = /^(\S+)\s+-\s+(\S+)$/.exec(set);
|
|
33888
|
+
if (hyphen !== null) {
|
|
33889
|
+
const from = parsePartial(hyphen[1]);
|
|
33890
|
+
const to = parsePartial(hyphen[2]);
|
|
33891
|
+
if (from === null || to === null)
|
|
33892
|
+
return null;
|
|
33893
|
+
return intersect(atLeast2(from), atMost(to));
|
|
33894
|
+
}
|
|
33895
|
+
const comparators = set.replace(/(<=|>=|<|>|=|~>|~|\^)\s+/g, "$1").split(/\s+/);
|
|
33896
|
+
let interval = { low: null, high: null };
|
|
33897
|
+
for (const comparator of comparators) {
|
|
33898
|
+
const next = parseComparator(comparator);
|
|
33899
|
+
if (next === null)
|
|
33900
|
+
return null;
|
|
33901
|
+
interval = intersect(interval, next);
|
|
33902
|
+
}
|
|
33903
|
+
return interval;
|
|
33904
|
+
}
|
|
33905
|
+
function parsePartial(text) {
|
|
33906
|
+
const m = /^v?([^.]+)(?:\.([^.]+))?(?:\.([^.+]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(text);
|
|
33907
|
+
if (m === null)
|
|
33908
|
+
return null;
|
|
33909
|
+
const parts = [];
|
|
33910
|
+
let wild = false;
|
|
33911
|
+
for (const part of [m[1], m[2], m[3]]) {
|
|
33912
|
+
if (part === void 0 || /^[xX*]$/.test(part)) {
|
|
33913
|
+
wild = true;
|
|
33914
|
+
parts.push(null);
|
|
33915
|
+
continue;
|
|
33916
|
+
}
|
|
33917
|
+
if (wild || !NUMBER.test(part))
|
|
33918
|
+
return null;
|
|
33919
|
+
parts.push(Number(part));
|
|
33920
|
+
}
|
|
33921
|
+
return { major: parts[0] ?? null, minor: parts[1] ?? null, patch: parts[2] ?? null };
|
|
33922
|
+
}
|
|
33923
|
+
function parseComparator(text) {
|
|
33924
|
+
const m = /^(<=|>=|<|>|=|~>|~|\^)?(.*)$/.exec(text);
|
|
33925
|
+
if (m === null)
|
|
33926
|
+
return null;
|
|
33927
|
+
const operator = m[1] ?? "";
|
|
33928
|
+
const partial = parsePartial(m[2]);
|
|
33929
|
+
if (partial === null)
|
|
33930
|
+
return null;
|
|
33931
|
+
const { major, minor, patch } = partial;
|
|
33932
|
+
if (major === null) {
|
|
33933
|
+
return operator === "" || operator === "=" || operator === ">=" ? { low: null, high: null } : null;
|
|
33934
|
+
}
|
|
33935
|
+
switch (operator) {
|
|
33936
|
+
case "":
|
|
33937
|
+
case "=":
|
|
33938
|
+
return intersect(atLeast2(partial), atMost(partial));
|
|
33939
|
+
case ">=":
|
|
33940
|
+
return atLeast2(partial);
|
|
33941
|
+
case "<=":
|
|
33942
|
+
return atMost(partial);
|
|
33943
|
+
case ">":
|
|
33944
|
+
return { low: { at: bumpPartial(partial), inclusive: true }, high: null };
|
|
33945
|
+
case "<":
|
|
33946
|
+
return { low: null, high: { at: floor(partial), inclusive: false } };
|
|
33947
|
+
case "~":
|
|
33948
|
+
case "~>":
|
|
33949
|
+
return {
|
|
33950
|
+
low: { at: floor(partial), inclusive: true },
|
|
33951
|
+
high: { at: minor === null ? [major + 1, 0, 0] : [major, minor + 1, 0], inclusive: false }
|
|
33952
|
+
};
|
|
33953
|
+
case "^": {
|
|
33954
|
+
let high;
|
|
33955
|
+
if (major > 0 || minor === null)
|
|
33956
|
+
high = [major + 1, 0, 0];
|
|
33957
|
+
else if (minor > 0 || patch === null)
|
|
33958
|
+
high = [0, minor + 1, 0];
|
|
33959
|
+
else
|
|
33960
|
+
high = [0, 0, patch + 1];
|
|
33961
|
+
return { low: { at: floor(partial), inclusive: true }, high: { at: high, inclusive: false } };
|
|
33962
|
+
}
|
|
33963
|
+
default:
|
|
33964
|
+
return null;
|
|
33965
|
+
}
|
|
33966
|
+
}
|
|
33967
|
+
function floor(p) {
|
|
33968
|
+
return [p.major ?? 0, p.minor ?? 0, p.patch ?? 0];
|
|
33969
|
+
}
|
|
33970
|
+
function bumpPartial(p) {
|
|
33971
|
+
const major = p.major ?? 0;
|
|
33972
|
+
if (p.minor === null)
|
|
33973
|
+
return [major + 1, 0, 0];
|
|
33974
|
+
if (p.patch === null)
|
|
33975
|
+
return [major, p.minor + 1, 0];
|
|
33976
|
+
return [major, p.minor, p.patch + 1];
|
|
33977
|
+
}
|
|
33978
|
+
function atLeast2(p) {
|
|
33979
|
+
return p.major === null ? { low: null, high: null } : { low: { at: floor(p), inclusive: true }, high: null };
|
|
33980
|
+
}
|
|
33981
|
+
function atMost(p) {
|
|
33982
|
+
if (p.major === null)
|
|
33983
|
+
return { low: null, high: null };
|
|
33984
|
+
if (p.patch !== null && p.minor !== null) {
|
|
33985
|
+
return { low: null, high: { at: [p.major, p.minor, p.patch], inclusive: true } };
|
|
33986
|
+
}
|
|
33987
|
+
return { low: null, high: { at: bumpPartial(p), inclusive: false } };
|
|
33988
|
+
}
|
|
33989
|
+
function compare(a, b) {
|
|
33990
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
33991
|
+
}
|
|
33992
|
+
function intersect(a, b) {
|
|
33993
|
+
const low = a.low === null ? b.low : b.low === null ? a.low : tighterLow(a.low, b.low);
|
|
33994
|
+
const high = a.high === null ? b.high : b.high === null ? a.high : tighterHigh(a.high, b.high);
|
|
33995
|
+
return { low, high };
|
|
33996
|
+
}
|
|
33997
|
+
function tighterLow(a, b) {
|
|
33998
|
+
const c = compare(a.at, b.at);
|
|
33999
|
+
if (c !== 0)
|
|
34000
|
+
return c > 0 ? a : b;
|
|
34001
|
+
return a.inclusive ? b : a;
|
|
34002
|
+
}
|
|
34003
|
+
function tighterHigh(a, b) {
|
|
34004
|
+
const c = compare(a.at, b.at);
|
|
34005
|
+
if (c !== 0)
|
|
34006
|
+
return c < 0 ? a : b;
|
|
34007
|
+
return a.inclusive ? b : a;
|
|
34008
|
+
}
|
|
34009
|
+
function within(v, interval) {
|
|
34010
|
+
const { low, high } = interval;
|
|
34011
|
+
if (low !== null) {
|
|
34012
|
+
const c = compare(v, low.at);
|
|
34013
|
+
if (c < 0 || c === 0 && !low.inclusive)
|
|
34014
|
+
return false;
|
|
34015
|
+
}
|
|
34016
|
+
if (high !== null) {
|
|
34017
|
+
const c = compare(v, high.at);
|
|
34018
|
+
if (c > 0 || c === 0 && !high.inclusive)
|
|
34019
|
+
return false;
|
|
34020
|
+
}
|
|
34021
|
+
return true;
|
|
34022
|
+
}
|
|
34023
|
+
var MAX_RANGE_LENGTH, NUMBER;
|
|
34024
|
+
var init_semverRange = __esm({
|
|
34025
|
+
"../../packages/envspec/dist/semverRange.js"() {
|
|
34026
|
+
"use strict";
|
|
34027
|
+
MAX_RANGE_LENGTH = 1024;
|
|
34028
|
+
NUMBER = /^(?:0|[1-9]\d{0,15})$/;
|
|
34029
|
+
}
|
|
34030
|
+
});
|
|
34031
|
+
|
|
33800
34032
|
// ../../packages/envspec/dist/manifest.js
|
|
33801
34033
|
function detectRuntime(repo) {
|
|
33802
34034
|
const candidates = [];
|
|
@@ -33878,47 +34110,155 @@ function lockfileGaps(repo) {
|
|
|
33878
34110
|
const pkg = readJsonObject(repo, "package.json");
|
|
33879
34111
|
if (pkg === null)
|
|
33880
34112
|
return null;
|
|
34113
|
+
const lockfile = ["package-lock.json", "npm-shrinkwrap.json", "yarn.lock", "pnpm-lock.yaml"].find((name) => repo.exists(name));
|
|
34114
|
+
if (lockfile === void 0)
|
|
34115
|
+
return null;
|
|
34116
|
+
const listingFor = lockfileListings(repo, pkg, lockfile);
|
|
34117
|
+
if (listingFor === null)
|
|
34118
|
+
return null;
|
|
34119
|
+
const missing = [];
|
|
34120
|
+
const rootListing = listingFor("");
|
|
34121
|
+
if (rootListing !== null) {
|
|
34122
|
+
for (const [n, r, sec] of wantedDependencies(pkg))
|
|
34123
|
+
if (!rootListing(n, r, sec))
|
|
34124
|
+
missing.push(`${n}@${r}`);
|
|
34125
|
+
}
|
|
34126
|
+
for (const dir of workspaceDirs(repo, pkg, lockfile)) {
|
|
34127
|
+
const listing = listingFor(dir);
|
|
34128
|
+
const manifest = readJsonObject(repo, `${dir}/package.json`);
|
|
34129
|
+
if (listing === null || manifest === null)
|
|
34130
|
+
continue;
|
|
34131
|
+
for (const [n, r, sec] of wantedDependencies(manifest)) {
|
|
34132
|
+
if (!listing(n, r, sec))
|
|
34133
|
+
missing.push(`${n}@${r} (${dir})`);
|
|
34134
|
+
}
|
|
34135
|
+
}
|
|
34136
|
+
return missing.length === 0 ? null : { lockfile, missing };
|
|
34137
|
+
}
|
|
34138
|
+
function wantedDependencies(pkg) {
|
|
33881
34139
|
const wanted = [];
|
|
34140
|
+
const optional = readObjectField(pkg, "optionalDependencies");
|
|
33882
34141
|
for (const section of DEPENDENCY_SECTIONS) {
|
|
33883
34142
|
const deps = readObjectField(pkg, section);
|
|
33884
34143
|
if (deps === null)
|
|
33885
34144
|
continue;
|
|
33886
34145
|
for (const [name, range] of Object.entries(deps)) {
|
|
34146
|
+
if (section === "dependencies" && readStringField(optional, name) !== null)
|
|
34147
|
+
continue;
|
|
33887
34148
|
if (typeof range === "string")
|
|
33888
|
-
wanted.push([name, range]);
|
|
34149
|
+
wanted.push([name, range, section]);
|
|
33889
34150
|
}
|
|
33890
34151
|
}
|
|
33891
|
-
|
|
33892
|
-
|
|
33893
|
-
|
|
33894
|
-
|
|
33895
|
-
|
|
33896
|
-
|
|
33897
|
-
return missing.length === 0 ? null : { lockfile, missing };
|
|
33898
|
-
};
|
|
33899
|
-
for (const lockfile of ["package-lock.json", "npm-shrinkwrap.json"]) {
|
|
33900
|
-
if (repo.exists(lockfile))
|
|
33901
|
-
return check(lockfile, npmLockListing(repo, lockfile));
|
|
34152
|
+
return wanted;
|
|
34153
|
+
}
|
|
34154
|
+
function lockfileListings(repo, pkg, lockfile) {
|
|
34155
|
+
if (lockfile === "yarn.lock") {
|
|
34156
|
+
const root = yarnLockListing(repo.readText(lockfile));
|
|
34157
|
+
return root === null ? null : (dir) => dir === "" ? root : null;
|
|
33902
34158
|
}
|
|
33903
|
-
if (
|
|
33904
|
-
|
|
33905
|
-
|
|
33906
|
-
|
|
34159
|
+
if (lockfile === "pnpm-lock.yaml") {
|
|
34160
|
+
const text = repo.readText(lockfile);
|
|
34161
|
+
const importers = pnpmImporters(text);
|
|
34162
|
+
const settings = pnpmSettings(text);
|
|
34163
|
+
const overrides = pnpmOverrides(text);
|
|
34164
|
+
if (importers === null || settings === null || overrides === null)
|
|
34165
|
+
return null;
|
|
34166
|
+
const honoured = sameOverrides(overrides, declaredPnpmOverrides(repo, pkg)) ? overrides : /* @__PURE__ */ new Map();
|
|
34167
|
+
const withOverrides = (listing) => (name, range, section) => {
|
|
34168
|
+
const override = honoured.get(name);
|
|
34169
|
+
return listing(name, range, section) || override !== void 0 && listing(name, override, section);
|
|
34170
|
+
};
|
|
34171
|
+
const withSettings = (listing) => (name, range, section) => !settings.autoInstallPeers && section === "peerDependencies" || settings.excludeLinksFromLockfile && range.startsWith("link:") || listing(name, range, section);
|
|
34172
|
+
const workspaceAware = text !== null && /^importers:/m.test(text);
|
|
34173
|
+
return (dir) => {
|
|
34174
|
+
const importer = importers.get(dir === "" ? "." : dir);
|
|
34175
|
+
if (importer !== void 0)
|
|
34176
|
+
return withSettings(withOverrides(pnpmImporterListing(importer)));
|
|
34177
|
+
return dir === "" || !workspaceAware ? null : () => false;
|
|
34178
|
+
};
|
|
33907
34179
|
}
|
|
33908
|
-
return
|
|
34180
|
+
return npmLockListings(repo, lockfile);
|
|
33909
34181
|
}
|
|
33910
|
-
function
|
|
34182
|
+
function npmLockListings(repo, lockfile) {
|
|
33911
34183
|
const lock = readJsonObject(repo, lockfile);
|
|
33912
34184
|
if (lock === null)
|
|
33913
34185
|
return null;
|
|
33914
|
-
const
|
|
33915
|
-
if (
|
|
33916
|
-
return (
|
|
34186
|
+
const packages = readObjectField(lock, "packages");
|
|
34187
|
+
if (readObjectField(packages, "") !== null) {
|
|
34188
|
+
return (dir) => {
|
|
34189
|
+
const entry = readObjectField(packages, dir);
|
|
34190
|
+
if (entry === null)
|
|
34191
|
+
return () => false;
|
|
34192
|
+
return (name, range) => DEPENDENCY_SECTIONS.some((s) => readStringField(readObjectField(entry, s), name) === range);
|
|
34193
|
+
};
|
|
33917
34194
|
}
|
|
33918
34195
|
const v1 = readObjectField(lock, "dependencies");
|
|
33919
34196
|
if (v1 === null)
|
|
33920
34197
|
return null;
|
|
33921
|
-
|
|
34198
|
+
const root = (name, range) => {
|
|
34199
|
+
const entry = readObjectField(v1, name);
|
|
34200
|
+
if (entry === null)
|
|
34201
|
+
return false;
|
|
34202
|
+
const version = readStringField(entry, "version");
|
|
34203
|
+
return version === null ? true : rangeAdmits(version, range) ?? true;
|
|
34204
|
+
};
|
|
34205
|
+
return (dir) => dir === "" ? root : null;
|
|
34206
|
+
}
|
|
34207
|
+
function workspaceDirs(repo, pkg, lockfile) {
|
|
34208
|
+
let patterns;
|
|
34209
|
+
if (lockfile === "pnpm-lock.yaml") {
|
|
34210
|
+
const text = repo.readText("pnpm-workspace.yaml");
|
|
34211
|
+
try {
|
|
34212
|
+
patterns = text === null ? [] : asStringList(mapGet(parseYaml(text, PNPM_LIMITS), "packages"));
|
|
34213
|
+
} catch (err) {
|
|
34214
|
+
if (!(err instanceof YamlUnsupportedError))
|
|
34215
|
+
throw err;
|
|
34216
|
+
patterns = [];
|
|
34217
|
+
}
|
|
34218
|
+
} else {
|
|
34219
|
+
const field = pkg["workspaces"];
|
|
34220
|
+
patterns = Array.isArray(field) ? field : readObjectField(pkg, "workspaces")?.["packages"];
|
|
34221
|
+
}
|
|
34222
|
+
if (!Array.isArray(patterns))
|
|
34223
|
+
return [];
|
|
34224
|
+
const budget = { visits: 0 };
|
|
34225
|
+
const included = /* @__PURE__ */ new Set();
|
|
34226
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
34227
|
+
for (const raw of patterns) {
|
|
34228
|
+
if (typeof raw !== "string")
|
|
34229
|
+
continue;
|
|
34230
|
+
const negated = raw.startsWith("!");
|
|
34231
|
+
const pattern = (negated ? raw.slice(1) : raw).replace(/^\.\//, "").replace(/\/+$/, "");
|
|
34232
|
+
const segments = pattern.split("/");
|
|
34233
|
+
if (pattern === "" || /[{}[\]\\]/.test(pattern) || segments.some((s) => s === ".." || s === "." || s === "")) {
|
|
34234
|
+
continue;
|
|
34235
|
+
}
|
|
34236
|
+
for (const dir of matchSegments(repo, "", segments, 0, budget))
|
|
34237
|
+
(negated ? excluded : included).add(dir);
|
|
34238
|
+
}
|
|
34239
|
+
const manifestOf = (dir) => `${dir}/package.json`;
|
|
34240
|
+
return [...included].filter((dir) => !excluded.has(dir) && dir !== "" && readJsonObject(repo, manifestOf(dir)) !== null).sort().slice(0, WORKSPACE_LIMITS.maxWorkspaces);
|
|
34241
|
+
}
|
|
34242
|
+
function matchSegments(repo, base, segments, depth, budget) {
|
|
34243
|
+
if (segments.length === 0)
|
|
34244
|
+
return [base];
|
|
34245
|
+
if (depth > WORKSPACE_LIMITS.maxDepth || budget.visits >= WORKSPACE_LIMITS.maxVisits)
|
|
34246
|
+
return [];
|
|
34247
|
+
budget.visits += 1;
|
|
34248
|
+
const [head, ...rest] = segments;
|
|
34249
|
+
const at = (name) => base === "" ? name : `${base}/${name}`;
|
|
34250
|
+
const children = () => repo.listNames(base).filter((n) => n !== "node_modules" && !n.startsWith(".") && repo.isDirectory(at(n)));
|
|
34251
|
+
if (head === "**") {
|
|
34252
|
+
return [
|
|
34253
|
+
...matchSegments(repo, base, rest, depth + 1, budget),
|
|
34254
|
+
...children().flatMap((n) => matchSegments(repo, at(n), segments, depth + 1, budget))
|
|
34255
|
+
];
|
|
34256
|
+
}
|
|
34257
|
+
if (!/[*?]/.test(head)) {
|
|
34258
|
+
return head !== "node_modules" && repo.isDirectory(at(head)) ? matchSegments(repo, at(head), rest, depth + 1, budget) : [];
|
|
34259
|
+
}
|
|
34260
|
+
const re = new RegExp(`^${head.replace(/[.+^$()|]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
|
|
34261
|
+
return children().filter((n) => re.test(n)).flatMap((n) => matchSegments(repo, at(n), rest, depth + 1, budget));
|
|
33922
34262
|
}
|
|
33923
34263
|
function yarnLockListing(text) {
|
|
33924
34264
|
if (text === null || !/^(# yarn lockfile v1|__metadata:)/m.test(text))
|
|
@@ -33937,40 +34277,120 @@ function yarnLockListing(text) {
|
|
|
33937
34277
|
}
|
|
33938
34278
|
return (name, range) => specs.has(`${name}@${range}`);
|
|
33939
34279
|
}
|
|
33940
|
-
function
|
|
33941
|
-
|
|
34280
|
+
function pnpmImporterListing(importer) {
|
|
34281
|
+
return (name, range) => asString(mapGet(mapGet(importer, "specifiers"), name)) === range || PNPM_SECTIONS.some((section) => asString(mapGet(mapGet(mapGet(importer, section), name), "specifier")) === range);
|
|
34282
|
+
}
|
|
34283
|
+
function pnpmSettings(text) {
|
|
34284
|
+
if (text === null)
|
|
33942
34285
|
return null;
|
|
33943
|
-
const
|
|
33944
|
-
|
|
34286
|
+
const block = topLevelBlock(text.replace(/\r\n/g, "\n").split("\n"), "settings");
|
|
34287
|
+
let node = null;
|
|
34288
|
+
try {
|
|
34289
|
+
node = block === null ? null : mapGet(parseYaml(block, PNPM_LIMITS), "settings");
|
|
34290
|
+
} catch (err) {
|
|
34291
|
+
if (err instanceof YamlUnsupportedError)
|
|
34292
|
+
return null;
|
|
34293
|
+
throw err;
|
|
34294
|
+
}
|
|
34295
|
+
return {
|
|
34296
|
+
autoInstallPeers: asString(mapGet(node, "autoInstallPeers")) !== "false",
|
|
34297
|
+
excludeLinksFromLockfile: asString(mapGet(node, "excludeLinksFromLockfile")) === "true"
|
|
34298
|
+
};
|
|
34299
|
+
}
|
|
34300
|
+
function pnpmOverrides(text) {
|
|
34301
|
+
if (text === null)
|
|
33945
34302
|
return null;
|
|
33946
|
-
const
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
|
|
33950
|
-
|
|
33951
|
-
|
|
33952
|
-
|
|
33953
|
-
|
|
33954
|
-
|
|
34303
|
+
const block = topLevelBlock(text.replace(/\r\n/g, "\n").split("\n"), "overrides");
|
|
34304
|
+
if (block === null)
|
|
34305
|
+
return /* @__PURE__ */ new Map();
|
|
34306
|
+
let node;
|
|
34307
|
+
try {
|
|
34308
|
+
node = mapGet(parseYaml(block, PNPM_LIMITS), "overrides");
|
|
34309
|
+
} catch (err) {
|
|
34310
|
+
if (err instanceof YamlUnsupportedError)
|
|
34311
|
+
return null;
|
|
34312
|
+
throw err;
|
|
34313
|
+
}
|
|
34314
|
+
if (node === null)
|
|
34315
|
+
return /* @__PURE__ */ new Map();
|
|
34316
|
+
if (!isMap(node))
|
|
34317
|
+
return null;
|
|
34318
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
34319
|
+
for (const { key, value } of node.entries) {
|
|
34320
|
+
const spec = asString(value);
|
|
34321
|
+
if (spec === null)
|
|
34322
|
+
return null;
|
|
34323
|
+
overrides.set(key, spec);
|
|
34324
|
+
}
|
|
34325
|
+
return overrides;
|
|
33955
34326
|
}
|
|
33956
|
-
function
|
|
33957
|
-
const
|
|
33958
|
-
|
|
33959
|
-
|
|
33960
|
-
|
|
34327
|
+
function declaredPnpmOverrides(repo, pkg) {
|
|
34328
|
+
const sources = [
|
|
34329
|
+
readObjectField(readObjectField(pkg, "pnpm"), "overrides"),
|
|
34330
|
+
readObjectField(pkg, "resolutions")
|
|
34331
|
+
];
|
|
34332
|
+
const workspace = repo.readText("pnpm-workspace.yaml");
|
|
34333
|
+
if (workspace !== null) {
|
|
34334
|
+
let node;
|
|
34335
|
+
try {
|
|
34336
|
+
node = mapGet(parseYaml(workspace, PNPM_LIMITS), "overrides");
|
|
34337
|
+
} catch (err) {
|
|
34338
|
+
if (err instanceof YamlUnsupportedError)
|
|
34339
|
+
return null;
|
|
34340
|
+
throw err;
|
|
34341
|
+
}
|
|
34342
|
+
if (node !== null && !isMap(node))
|
|
34343
|
+
return null;
|
|
34344
|
+
if (isMap(node))
|
|
34345
|
+
sources.push(Object.fromEntries(node.entries.map((e) => [e.key, e.value])));
|
|
33961
34346
|
}
|
|
33962
|
-
const
|
|
33963
|
-
|
|
34347
|
+
const declared = /* @__PURE__ */ new Map();
|
|
34348
|
+
for (const source of sources) {
|
|
34349
|
+
for (const [key, value] of Object.entries(source ?? {})) {
|
|
34350
|
+
if (typeof value !== "string")
|
|
34351
|
+
return null;
|
|
34352
|
+
const seen = declared.get(key);
|
|
34353
|
+
if (seen !== void 0 && seen !== value)
|
|
34354
|
+
return null;
|
|
34355
|
+
declared.set(key, value);
|
|
34356
|
+
}
|
|
34357
|
+
}
|
|
34358
|
+
return declared;
|
|
34359
|
+
}
|
|
34360
|
+
function sameOverrides(a, b) {
|
|
34361
|
+
return b !== null && a.size === b.size && [...a].every(([key, value]) => b.get(key) === value);
|
|
34362
|
+
}
|
|
34363
|
+
function pnpmImporters(text) {
|
|
34364
|
+
if (text === null || !/^lockfileVersion:/m.test(text))
|
|
33964
34365
|
return null;
|
|
33965
|
-
const
|
|
33966
|
-
|
|
33967
|
-
|
|
33968
|
-
|
|
33969
|
-
|
|
33970
|
-
|
|
33971
|
-
|
|
34366
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
34367
|
+
try {
|
|
34368
|
+
const importers = topLevelBlock(lines, "importers");
|
|
34369
|
+
if (importers !== null) {
|
|
34370
|
+
const node = mapGet(parseYaml(importers, PNPM_LIMITS), "importers");
|
|
34371
|
+
return isMap(node) ? new Map(node.entries.map((e) => [e.key, e.value])) : null;
|
|
34372
|
+
}
|
|
34373
|
+
const entries = [];
|
|
34374
|
+
for (const key of [...PNPM_SECTIONS, "specifiers"]) {
|
|
34375
|
+
const block = topLevelBlock(lines, key);
|
|
34376
|
+
if (block !== null)
|
|
34377
|
+
entries.push({ key, value: mapGet(parseYaml(block, PNPM_LIMITS), key) });
|
|
34378
|
+
}
|
|
34379
|
+
return /* @__PURE__ */ new Map([[".", { kind: "map", entries }]]);
|
|
34380
|
+
} catch (err) {
|
|
34381
|
+
if (err instanceof YamlUnsupportedError)
|
|
34382
|
+
return null;
|
|
34383
|
+
throw err;
|
|
33972
34384
|
}
|
|
33973
|
-
|
|
34385
|
+
}
|
|
34386
|
+
function topLevelBlock(lines, key) {
|
|
34387
|
+
const start = lines.findIndex((l) => l === `${key}:` || l.startsWith(`${key}: `));
|
|
34388
|
+
if (start === -1)
|
|
34389
|
+
return null;
|
|
34390
|
+
let end = start + 1;
|
|
34391
|
+
while (end < lines.length && (lines[end] === "" || /^[ \t#]/.test(lines[end])))
|
|
34392
|
+
end += 1;
|
|
34393
|
+
return lines.slice(start, end).join("\n");
|
|
33974
34394
|
}
|
|
33975
34395
|
function unfrozenInstall(command) {
|
|
33976
34396
|
const lead = /^(?:(?:sudo|time)\s+|env(?:\s+[A-Za-z_]\w*=\S*)*\s+)*/.exec(command)?.[0] ?? "";
|
|
@@ -34259,10 +34679,12 @@ function matchFirst(text, pattern) {
|
|
|
34259
34679
|
const match2 = pattern.exec(text);
|
|
34260
34680
|
return match2 === null ? null : match2[1];
|
|
34261
34681
|
}
|
|
34262
|
-
var RUNTIME_MANIFESTS, MAKEFILE_NAMES, C_FAMILY_SOURCE, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, DEPENDENCY_SECTIONS, YARN_MUTABLE, REQUIREMENT_SPECIFIER, CPP_BUILD_FILES, CMAKE_DECLARES_TESTS, EXACT_VERSION;
|
|
34682
|
+
var RUNTIME_MANIFESTS, MAKEFILE_NAMES, C_FAMILY_SOURCE, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, DEPENDENCY_SECTIONS, WORKSPACE_LIMITS, PNPM_LIMITS, PNPM_SECTIONS, YARN_MUTABLE, REQUIREMENT_SPECIFIER, CPP_BUILD_FILES, CMAKE_DECLARES_TESTS, EXACT_VERSION;
|
|
34263
34683
|
var init_manifest2 = __esm({
|
|
34264
34684
|
"../../packages/envspec/dist/manifest.js"() {
|
|
34265
34685
|
"use strict";
|
|
34686
|
+
init_semverRange();
|
|
34687
|
+
init_yaml();
|
|
34266
34688
|
RUNTIME_MANIFESTS = [
|
|
34267
34689
|
{ runtime: "node", files: ["package.json"] },
|
|
34268
34690
|
{ runtime: "python", files: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt"] },
|
|
@@ -34286,6 +34708,9 @@ var init_manifest2 = __esm({
|
|
|
34286
34708
|
"optionalDependencies",
|
|
34287
34709
|
"peerDependencies"
|
|
34288
34710
|
];
|
|
34711
|
+
WORKSPACE_LIMITS = { maxDepth: 8, maxVisits: 2e3, maxWorkspaces: 200 };
|
|
34712
|
+
PNPM_LIMITS = { maxDepth: 8, maxLineLength: 4096 };
|
|
34713
|
+
PNPM_SECTIONS = ["dependencies", "devDependencies", "optionalDependencies"];
|
|
34289
34714
|
YARN_MUTABLE = "YARN_ENABLE_IMMUTABLE_INSTALLS=false";
|
|
34290
34715
|
REQUIREMENT_SPECIFIER = /^[A-Za-z0-9._-]+(\[[A-Za-z0-9._,-]+\])?([<>=!~]=?[A-Za-z0-9._*+-]+(,[<>=!~]=?[A-Za-z0-9._*+-]+)*)?$/;
|
|
34291
34716
|
CPP_BUILD_FILES = [
|
|
@@ -34366,21 +34791,50 @@ var init_references = __esm({
|
|
|
34366
34791
|
});
|
|
34367
34792
|
|
|
34368
34793
|
// ../../packages/envspec/dist/repo.js
|
|
34369
|
-
import { readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
|
|
34794
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync13, realpathSync as realpathSync2, statSync as statSync5 } from "fs";
|
|
34370
34795
|
import { join as join26, relative as relative2, sep as sep7 } from "path";
|
|
34371
34796
|
function createRepoReader(repoPath) {
|
|
34372
34797
|
const resolveIn = (relativePath) => relativePath === "" ? repoPath : join26(repoPath, relativePath);
|
|
34373
34798
|
const toPosix = (absolute) => relative2(repoPath, absolute).split(sep7).join("/");
|
|
34799
|
+
let realRoot;
|
|
34800
|
+
const rootPrefix = () => {
|
|
34801
|
+
if (realRoot === void 0) {
|
|
34802
|
+
try {
|
|
34803
|
+
const real = realpathSync2(repoPath);
|
|
34804
|
+
realRoot = real.endsWith(sep7) ? real : real + sep7;
|
|
34805
|
+
} catch {
|
|
34806
|
+
realRoot = null;
|
|
34807
|
+
}
|
|
34808
|
+
}
|
|
34809
|
+
return realRoot;
|
|
34810
|
+
};
|
|
34811
|
+
const containedPath = (relativePath) => {
|
|
34812
|
+
const prefix = rootPrefix();
|
|
34813
|
+
if (prefix === null)
|
|
34814
|
+
return null;
|
|
34815
|
+
try {
|
|
34816
|
+
const real = realpathSync2(resolveIn(relativePath));
|
|
34817
|
+
return real + sep7 === prefix || real.startsWith(prefix) ? real : null;
|
|
34818
|
+
} catch {
|
|
34819
|
+
return null;
|
|
34820
|
+
}
|
|
34821
|
+
};
|
|
34374
34822
|
const readText = (relativePath) => {
|
|
34823
|
+
const real = containedPath(relativePath);
|
|
34824
|
+
if (real === null)
|
|
34825
|
+
return null;
|
|
34375
34826
|
try {
|
|
34376
|
-
return readFileSync13(
|
|
34827
|
+
return readFileSync13(real, "utf8");
|
|
34377
34828
|
} catch {
|
|
34378
34829
|
return null;
|
|
34379
34830
|
}
|
|
34380
34831
|
};
|
|
34381
34832
|
const statOf = (relativePath) => {
|
|
34833
|
+
const real = containedPath(relativePath);
|
|
34834
|
+
if (real === null)
|
|
34835
|
+
return null;
|
|
34382
34836
|
try {
|
|
34383
|
-
return statSync5(
|
|
34837
|
+
return statSync5(real);
|
|
34384
34838
|
} catch {
|
|
34385
34839
|
return null;
|
|
34386
34840
|
}
|
|
@@ -34426,11 +34880,14 @@ function createRepoReader(repoPath) {
|
|
|
34426
34880
|
const st = statOf(relativeDir);
|
|
34427
34881
|
if (st === null || !st.isDirectory())
|
|
34428
34882
|
return [];
|
|
34883
|
+
let names;
|
|
34429
34884
|
try {
|
|
34430
|
-
|
|
34885
|
+
names = readdirSync4(resolveIn(relativeDir));
|
|
34431
34886
|
} catch {
|
|
34432
34887
|
return [];
|
|
34433
34888
|
}
|
|
34889
|
+
const at = (name) => relativeDir === "" ? name : `${relativeDir}/${name}`;
|
|
34890
|
+
return names.filter((name) => containedPath(at(name)) !== null).sort();
|
|
34434
34891
|
}
|
|
34435
34892
|
};
|
|
34436
34893
|
}
|
|
@@ -36467,6 +36924,12 @@ async function runVerification(req, ctx) {
|
|
|
36467
36924
|
testOutputTail: outputTail,
|
|
36468
36925
|
counts: verdict.counts,
|
|
36469
36926
|
wallMs: Date.now() - startedAt,
|
|
36927
|
+
// TERM-1308: the steps' own durations, which `runEnvironmentSpec` measures, and the
|
|
36928
|
+
// limit applied to the test step. Absent rather than 0 or a default when there is
|
|
36929
|
+
// nothing to report, so a baseline never sizes a budget from a step that did not run.
|
|
36930
|
+
...verdict.install === null ? {} : { installMs: verdict.install.wallMs },
|
|
36931
|
+
...verdict.test === null ? {} : { testMs: verdict.test.wallMs },
|
|
36932
|
+
...req.testTimeoutMs === void 0 ? {} : { testTimeoutMs: req.testTimeoutMs },
|
|
36470
36933
|
targetRepo: publishableTarget(req.targetRepo),
|
|
36471
36934
|
targetSha: req.targetSha,
|
|
36472
36935
|
patchSha256,
|
|
@@ -37604,6 +38067,7 @@ __export(dist_exports, {
|
|
|
37604
38067
|
LabelWatch: () => LabelWatch,
|
|
37605
38068
|
MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
|
|
37606
38069
|
OUTCOME_TO_BUDGET: () => OUTCOME_TO_BUDGET,
|
|
38070
|
+
PARTIAL_COUNTS_NOTE: () => PARTIAL_COUNTS_NOTE,
|
|
37607
38071
|
PATH_REFUSAL_CODES: () => PATH_REFUSAL_CODES,
|
|
37608
38072
|
PLACEMENTS: () => PLACEMENTS,
|
|
37609
38073
|
PLACEMENT_ALIASES: () => PLACEMENT_ALIASES,
|
|
@@ -38045,7 +38509,7 @@ import {
|
|
|
38045
38509
|
renameSync as renameSync6,
|
|
38046
38510
|
existsSync as existsSync14,
|
|
38047
38511
|
lstatSync as lstatSync5,
|
|
38048
|
-
realpathSync as
|
|
38512
|
+
realpathSync as realpathSync3,
|
|
38049
38513
|
rmSync as rmSync10,
|
|
38050
38514
|
readdirSync as readdirSync5,
|
|
38051
38515
|
statSync as statSync6,
|
|
@@ -38810,7 +39274,7 @@ function canonicalPath2(p) {
|
|
|
38810
39274
|
if (typeof p !== "string" || p === "") return p;
|
|
38811
39275
|
const resolved = pathResolve(p);
|
|
38812
39276
|
try {
|
|
38813
|
-
return
|
|
39277
|
+
return realpathSync3.native(resolved);
|
|
38814
39278
|
} catch {
|
|
38815
39279
|
return resolved;
|
|
38816
39280
|
}
|
|
@@ -38891,6 +39355,66 @@ async function fetchFreshClaimablePool() {
|
|
|
38891
39355
|
const contributions = (index?.contribute ?? []).filter((j) => j.source === "contribute");
|
|
38892
39356
|
return [...bounties, ...contributions];
|
|
38893
39357
|
}
|
|
39358
|
+
var BASELINE_STATUSES = /* @__PURE__ */ new Set([
|
|
39359
|
+
"pending",
|
|
39360
|
+
"not-scheduled",
|
|
39361
|
+
"passed",
|
|
39362
|
+
"failed",
|
|
39363
|
+
"unreadable",
|
|
39364
|
+
"no-tests",
|
|
39365
|
+
"over-ceiling",
|
|
39366
|
+
"not-runnable",
|
|
39367
|
+
"not-runnable-policy",
|
|
39368
|
+
"over-cap"
|
|
39369
|
+
]);
|
|
39370
|
+
var MIN_TEST_BUDGET_MINUTES = 5;
|
|
39371
|
+
var MAX_TEST_BUDGET_MINUTES = 120;
|
|
39372
|
+
function baselineFields(b) {
|
|
39373
|
+
const out = {};
|
|
39374
|
+
const m = b?.testBudgetMinutes;
|
|
39375
|
+
if (Number.isInteger(m) && m >= MIN_TEST_BUDGET_MINUTES && m <= MAX_TEST_BUDGET_MINUTES) {
|
|
39376
|
+
out.testBudgetMinutes = m;
|
|
39377
|
+
}
|
|
39378
|
+
if (typeof b?.baselineStatus === "string" && BASELINE_STATUSES.has(b.baselineStatus)) {
|
|
39379
|
+
out.baselineStatus = b.baselineStatus;
|
|
39380
|
+
}
|
|
39381
|
+
if (typeof b?.baselineSha === "string" && /^[0-9a-f]{7,40}$/.test(b.baselineSha)) {
|
|
39382
|
+
out.baselineSha = b.baselineSha;
|
|
39383
|
+
}
|
|
39384
|
+
return out;
|
|
39385
|
+
}
|
|
39386
|
+
function baselineLine({ baselineStatus, baselineSha, testBudgetMinutes } = {}) {
|
|
39387
|
+
const at = typeof baselineSha === "string" ? ` at ${baselineSha.slice(0, 12)}` : "";
|
|
39388
|
+
const limit2 = typeof testBudgetMinutes === "number" ? ` Time limit ${String(testBudgetMinutes)} min.` : "";
|
|
39389
|
+
switch (baselineStatus) {
|
|
39390
|
+
case "passed":
|
|
39391
|
+
return `Tests ran on Terminal Hire's machines${at} and passed.${limit2}`;
|
|
39392
|
+
case "failed":
|
|
39393
|
+
return `Tests ran on Terminal Hire's machines${at} and did not pass.${limit2}`;
|
|
39394
|
+
case "unreadable":
|
|
39395
|
+
return `Tests ran; Terminal Hire could not read the result.${limit2}`;
|
|
39396
|
+
case "no-tests":
|
|
39397
|
+
return `Tests ran and none were observed.${limit2}`;
|
|
39398
|
+
case "over-ceiling":
|
|
39399
|
+
return "Terminal Hire stopped this repository's tests at the 120-min limit.";
|
|
39400
|
+
case "not-runnable":
|
|
39401
|
+
return "Terminal Hire could not run this repository's tests.";
|
|
39402
|
+
case "not-runnable-policy":
|
|
39403
|
+
return "Terminal Hire does not run tests for postings shared at this level.";
|
|
39404
|
+
case "pending":
|
|
39405
|
+
return "Test time being measured.";
|
|
39406
|
+
case "over-cap":
|
|
39407
|
+
return "Test time not measured yet.";
|
|
39408
|
+
default:
|
|
39409
|
+
return null;
|
|
39410
|
+
}
|
|
39411
|
+
}
|
|
39412
|
+
function postingTestBudgetMinutes(bountyId) {
|
|
39413
|
+
if (typeof bountyId !== "string") return null;
|
|
39414
|
+
const job = findClaimableInCache(bountyId);
|
|
39415
|
+
if (!job || job.source !== "bounty") return null;
|
|
39416
|
+
return baselineFields(job.bounty).testBudgetMinutes ?? null;
|
|
39417
|
+
}
|
|
38894
39418
|
function extractClaimableFields(job) {
|
|
38895
39419
|
if (job.source === "contribute") {
|
|
38896
39420
|
const c = job.contribution ?? {};
|
|
@@ -38934,7 +39458,9 @@ function extractClaimableFields(job) {
|
|
|
38934
39458
|
// (`publicRunRequirements`). Carried, never derived here: the CLI has no copy of the
|
|
38935
39459
|
// repository before a claim, and a second derivation could disagree with the one the
|
|
38936
39460
|
// poster saw. Null when the index carries none.
|
|
38937
|
-
runRequirements: b.runRequirements && typeof b.runRequirements === "object" ? b.runRequirements : null
|
|
39461
|
+
runRequirements: b.runRequirements && typeof b.runRequirements === "object" ? b.runRequirements : null,
|
|
39462
|
+
// TERM-1308. Present only when the index row carries them; no key otherwise.
|
|
39463
|
+
...b.bountySource === "founder" ? baselineFields(b) : {}
|
|
38938
39464
|
};
|
|
38939
39465
|
}
|
|
38940
39466
|
function parseGitHubUrl(url) {
|
|
@@ -39545,6 +40071,7 @@ async function resolveBounty(arg) {
|
|
|
39545
40071
|
let founderPosting = false;
|
|
39546
40072
|
let claimMode = "open";
|
|
39547
40073
|
let runRequirements = null;
|
|
40074
|
+
let baseline = {};
|
|
39548
40075
|
let job = findClaimableInCache(arg) ?? (looksLikeShortRef(arg) ? findClaimableByShortRef(arg) : null);
|
|
39549
40076
|
let freshPool;
|
|
39550
40077
|
if (!job && looksLikeShortRef(arg)) {
|
|
@@ -39552,6 +40079,7 @@ async function resolveBounty(arg) {
|
|
|
39552
40079
|
if (freshPool) job = findByShortRefInPool(freshPool, arg);
|
|
39553
40080
|
}
|
|
39554
40081
|
if (job) {
|
|
40082
|
+
let testBudgetMinutes, baselineStatus, baselineSha;
|
|
39555
40083
|
({
|
|
39556
40084
|
bountyId,
|
|
39557
40085
|
title,
|
|
@@ -39562,8 +40090,16 @@ async function resolveBounty(arg) {
|
|
|
39562
40090
|
openPRsAtDiscovery,
|
|
39563
40091
|
founderPosting,
|
|
39564
40092
|
claimMode,
|
|
39565
|
-
runRequirements
|
|
40093
|
+
runRequirements,
|
|
40094
|
+
testBudgetMinutes,
|
|
40095
|
+
baselineStatus,
|
|
40096
|
+
baselineSha
|
|
39566
40097
|
} = extractClaimableFields(job));
|
|
40098
|
+
baseline = {
|
|
40099
|
+
...testBudgetMinutes === void 0 ? {} : { testBudgetMinutes },
|
|
40100
|
+
...baselineStatus === void 0 ? {} : { baselineStatus },
|
|
40101
|
+
...baselineSha === void 0 ? {} : { baselineSha }
|
|
40102
|
+
};
|
|
39567
40103
|
indexNativeId = bountyId;
|
|
39568
40104
|
} else {
|
|
39569
40105
|
const parsed = parseGitHubUrl(arg);
|
|
@@ -39635,7 +40171,8 @@ async function resolveBounty(arg) {
|
|
|
39635
40171
|
founderPosting,
|
|
39636
40172
|
claimMode,
|
|
39637
40173
|
// TERM-1259. Only a founder index hit carries these; every other path is null.
|
|
39638
|
-
runRequirements: runRequirements ?? null
|
|
40174
|
+
runRequirements: runRequirements ?? null,
|
|
40175
|
+
...baseline
|
|
39639
40176
|
};
|
|
39640
40177
|
}
|
|
39641
40178
|
function fmtOpenPRsLine(b) {
|
|
@@ -40344,6 +40881,8 @@ async function cmdPreview(arg, { json } = {}) {
|
|
|
40344
40881
|
openPRs: b.openPRs,
|
|
40345
40882
|
assignees: b.assignees,
|
|
40346
40883
|
contested: isContested(b),
|
|
40884
|
+
// TERM-1308. The posting's test budget and baseline, only when the index carried them.
|
|
40885
|
+
...baselineFields(b),
|
|
40347
40886
|
// Absent on a founder posting — see the scan above. A consumer must read
|
|
40348
40887
|
// "no policy key" as "no policy step exists here", never as "clean".
|
|
40349
40888
|
...policy ? {
|
|
@@ -40374,6 +40913,8 @@ async function cmdPreview(arg, { json } = {}) {
|
|
|
40374
40913
|
console.log(fmtOpenPRsLine(b));
|
|
40375
40914
|
const previewContested = fmtContestedWarning(b);
|
|
40376
40915
|
if (previewContested) console.log(previewContested);
|
|
40916
|
+
const previewBaseline = baselineLine(b);
|
|
40917
|
+
if (previewBaseline) console.log(` tests: ${previewBaseline}`);
|
|
40377
40918
|
if (policy) printPolicySection(policy);
|
|
40378
40919
|
if (process.stdout.isTTY) {
|
|
40379
40920
|
try {
|
|
@@ -44042,6 +44583,8 @@ export {
|
|
|
44042
44583
|
assertNoBooleanPath,
|
|
44043
44584
|
attemptSliceDelivery,
|
|
44044
44585
|
backgroundEnableFailed,
|
|
44586
|
+
baselineFields,
|
|
44587
|
+
baselineLine,
|
|
44045
44588
|
beatFounderPresence,
|
|
44046
44589
|
buildAssignmentComment,
|
|
44047
44590
|
buildPatchSubmission,
|
|
@@ -44094,6 +44637,7 @@ export {
|
|
|
44094
44637
|
pickBodySource,
|
|
44095
44638
|
pickExistingPr,
|
|
44096
44639
|
pickStartableClaim,
|
|
44640
|
+
postingTestBudgetMinutes,
|
|
44097
44641
|
printNextSteps,
|
|
44098
44642
|
printReleaseIsLocalOnly,
|
|
44099
44643
|
printResolveHint,
|