@guilz-dev/belay 0.9.1 → 0.9.3
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/README.md +1 -1
- package/dist/adapters/codex/runtime-entry.d.ts +3 -0
- package/dist/adapters/codex/runtime-entry.js +27 -4
- package/dist/adapters/cursor/cwd-resolution.d.ts +10 -0
- package/dist/adapters/cursor/cwd-resolution.js +58 -0
- package/dist/adapters/cursor/hooks.d.ts +5 -3
- package/dist/adapters/cursor/hooks.js +29 -20
- package/dist/adapters/cursor/runtime-entry.d.ts +1 -0
- package/dist/adapters/cursor/runtime-entry.js +107 -6
- package/dist/adapters/shared/gate-runtime.js +26 -3
- package/dist/adapters/shared/repo-root.js +20 -1
- package/dist/bundle/claude-runtime.mjs +1225 -287
- package/dist/bundle/codex-runtime.mjs +1247 -291
- package/dist/bundle/cursor-runtime.mjs +4320 -3154
- package/dist/cli.js +33 -3
- package/dist/commands/doctor.js +38 -9
- package/dist/commands/health-snapshot.d.ts +3 -0
- package/dist/commands/health-snapshot.js +56 -0
- package/dist/commands/report.js +14 -0
- package/dist/commands/status.js +15 -0
- package/dist/commands/where.d.ts +4 -0
- package/dist/commands/where.js +52 -0
- package/dist/core/approval-repo-lookup.d.ts +16 -0
- package/dist/core/approval-repo-lookup.js +48 -0
- package/dist/core/audit-io.d.ts +1 -1
- package/dist/core/audit-io.js +1 -1
- package/dist/core/audit-legacy-archive.d.ts +1 -0
- package/dist/core/audit-legacy-archive.js +5 -0
- package/dist/core/audit-query.d.ts +1 -0
- package/dist/core/audit-query.js +7 -0
- package/dist/core/audit-serialize.d.ts +3 -0
- package/dist/core/audit-serialize.js +39 -3
- package/dist/core/audit-summary.d.ts +9 -0
- package/dist/core/audit-summary.js +58 -1
- package/dist/core/audit-types.d.ts +4 -0
- package/dist/core/effect-ir/shell-lower.js +283 -27
- package/dist/core/replay-scrub.d.ts +1 -0
- package/dist/core/replay-scrub.js +22 -3
- package/dist/core/shell-tokenizer.d.ts +28 -0
- package/dist/core/shell-tokenizer.js +111 -29
- package/dist/core/verdict/docker-compose-run.d.ts +18 -0
- package/dist/core/verdict/docker-compose-run.js +136 -0
- package/dist/core/verdict/launcher-resolve.js +66 -25
- package/dist/core/verdict/makefile-expand.d.ts +4 -0
- package/dist/core/verdict/makefile-expand.js +151 -0
- package/dist/core/verdict/parser.d.ts +4 -0
- package/dist/core/verdict/parser.js +25 -30
- package/dist/core/verdict/recursive-invocation.d.ts +20 -0
- package/dist/core/verdict/recursive-invocation.js +224 -0
- package/dist/corpus/benign-probe-cores.d.ts +1 -1
- package/dist/corpus/benign-probe-cores.js +2 -0
- package/dist/defaults.js +16 -0
- package/dist/installer/scope-config.d.ts +2 -2
- package/dist/installer.d.ts +10 -1
- package/dist/installer.js +43 -2
- package/dist/types.d.ts +34 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/skills/belay/SKILL.md +5 -0
- package/skills/belay/belay-report.md +4 -1
|
@@ -355,60 +355,135 @@ function isRedirectOperator(token) {
|
|
|
355
355
|
function isFdDuplication(token) {
|
|
356
356
|
return FD_DUPLICATION_PATTERN.test(token);
|
|
357
357
|
}
|
|
358
|
-
function
|
|
358
|
+
function lexShell(input) {
|
|
359
359
|
const tokens = [];
|
|
360
|
-
let
|
|
360
|
+
let value = "";
|
|
361
|
+
let wordStart = null;
|
|
362
|
+
let parts = [];
|
|
361
363
|
let quote = null;
|
|
362
|
-
let
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
let quoteStart = -1;
|
|
365
|
+
let quoteHadContent = false;
|
|
366
|
+
let complete = true;
|
|
367
|
+
const startWord = (index) => {
|
|
368
|
+
wordStart ??= index;
|
|
369
|
+
};
|
|
370
|
+
const append = (decoded, start, end, mode, hasExpansion) => {
|
|
371
|
+
startWord(start);
|
|
372
|
+
value += decoded;
|
|
373
|
+
const previous = parts.at(-1);
|
|
374
|
+
if (previous && previous.quote === mode && previous.hasExpansion === hasExpansion && previous.end === start) {
|
|
375
|
+
previous.value += decoded;
|
|
376
|
+
previous.raw += input.slice(start, end);
|
|
377
|
+
previous.end = end;
|
|
378
|
+
return;
|
|
367
379
|
}
|
|
380
|
+
parts.push({
|
|
381
|
+
value: decoded,
|
|
382
|
+
raw: input.slice(start, end),
|
|
383
|
+
start,
|
|
384
|
+
end,
|
|
385
|
+
quote: mode,
|
|
386
|
+
hasExpansion
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
const flushWord = (end) => {
|
|
390
|
+
if (wordStart === null) return;
|
|
391
|
+
tokens.push({
|
|
392
|
+
kind: "word",
|
|
393
|
+
value,
|
|
394
|
+
raw: input.slice(wordStart, end),
|
|
395
|
+
start: wordStart,
|
|
396
|
+
end,
|
|
397
|
+
parts
|
|
398
|
+
});
|
|
399
|
+
value = "";
|
|
400
|
+
wordStart = null;
|
|
401
|
+
parts = [];
|
|
402
|
+
};
|
|
403
|
+
const pushOperator = (token, start, end) => {
|
|
404
|
+
tokens.push({ kind: "operator", value: token, raw: input.slice(start, end), start, end });
|
|
368
405
|
};
|
|
369
406
|
for (let index = 0; index < input.length; index += 1) {
|
|
370
|
-
const char = input[index];
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
407
|
+
const char = input[index] ?? "";
|
|
408
|
+
if (quote === "single") {
|
|
409
|
+
if (char === "'") {
|
|
410
|
+
if (!quoteHadContent) append("", quoteStart, index + 1, "single", false);
|
|
411
|
+
quote = null;
|
|
412
|
+
} else {
|
|
413
|
+
append(char, index, index + 1, "single", false);
|
|
414
|
+
quoteHadContent = true;
|
|
415
|
+
}
|
|
378
416
|
continue;
|
|
379
417
|
}
|
|
380
|
-
if (quote) {
|
|
381
|
-
if (char ===
|
|
418
|
+
if (quote === "double") {
|
|
419
|
+
if (char === '"') {
|
|
420
|
+
if (!quoteHadContent) append("", quoteStart, index + 1, "double", false);
|
|
382
421
|
quote = null;
|
|
383
|
-
|
|
384
|
-
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (char === "\\") {
|
|
425
|
+
const next = input[index + 1];
|
|
426
|
+
if (next === void 0) {
|
|
427
|
+
append("\\", index, index + 1, "double", false);
|
|
428
|
+
complete = false;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (next === "$" || next === "`" || next === '"' || next === "\\" || next === "\n") {
|
|
432
|
+
append(next === "\n" ? "" : next, index, index + 2, "double", false);
|
|
433
|
+
quoteHadContent = true;
|
|
434
|
+
index += 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
append("\\", index, index + 1, "double", false);
|
|
438
|
+
quoteHadContent = true;
|
|
439
|
+
continue;
|
|
385
440
|
}
|
|
441
|
+
append(char, index, index + 1, "double", char === "$" || char === "`");
|
|
442
|
+
quoteHadContent = true;
|
|
386
443
|
continue;
|
|
387
444
|
}
|
|
388
|
-
if (char === '"
|
|
389
|
-
|
|
445
|
+
if (char === "'" || char === '"') {
|
|
446
|
+
startWord(index);
|
|
447
|
+
quote = char === "'" ? "single" : "double";
|
|
448
|
+
quoteStart = index;
|
|
449
|
+
quoteHadContent = false;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (char === "\\") {
|
|
453
|
+
const next = input[index + 1];
|
|
454
|
+
if (next === void 0) {
|
|
455
|
+
append("\\", index, index + 1, "unquoted", false);
|
|
456
|
+
complete = false;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
append(next, index, index + 2, "unquoted", false);
|
|
460
|
+
index += 1;
|
|
390
461
|
continue;
|
|
391
462
|
}
|
|
392
463
|
const operator = readShellOperator(input, index);
|
|
393
464
|
if (operator) {
|
|
394
|
-
|
|
395
|
-
|
|
465
|
+
flushWord(index);
|
|
466
|
+
pushOperator(operator.token, index, index + operator.length);
|
|
396
467
|
index += operator.length - 1;
|
|
397
468
|
continue;
|
|
398
469
|
}
|
|
399
470
|
if (char === "\n" || char === "\r") {
|
|
400
|
-
|
|
401
|
-
|
|
471
|
+
flushWord(index);
|
|
472
|
+
pushOperator(";", index, index + 1);
|
|
402
473
|
continue;
|
|
403
474
|
}
|
|
404
475
|
if (/\s/.test(char)) {
|
|
405
|
-
|
|
476
|
+
flushWord(index);
|
|
406
477
|
continue;
|
|
407
478
|
}
|
|
408
|
-
|
|
479
|
+
append(char, index, index + 1, "unquoted", char === "$" || char === "`");
|
|
409
480
|
}
|
|
410
|
-
|
|
411
|
-
|
|
481
|
+
if (quote !== null) complete = false;
|
|
482
|
+
flushWord(input.length);
|
|
483
|
+
return { tokens, complete };
|
|
484
|
+
}
|
|
485
|
+
function tokenizeShell(input) {
|
|
486
|
+
return lexShell(input).tokens.map((token) => token.value);
|
|
412
487
|
}
|
|
413
488
|
function commandKey(tokens) {
|
|
414
489
|
const filtered = tokens.filter((token) => token !== "sudo");
|
|
@@ -1400,6 +1475,22 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
1400
1475
|
function approvalCorrelationId(approvalId) {
|
|
1401
1476
|
return createHash2("sha256").update(approvalId).digest("hex").slice(0, 16);
|
|
1402
1477
|
}
|
|
1478
|
+
function canonicalToolUseIdForCorrelation(toolUseId) {
|
|
1479
|
+
const trimmed = toolUseId.trim();
|
|
1480
|
+
if (trimmed.startsWith("tool_")) {
|
|
1481
|
+
const remainder = trimmed.slice("tool_".length);
|
|
1482
|
+
if (TOOL_USE_UUID_PATTERN.test(remainder)) {
|
|
1483
|
+
return remainder.toLowerCase();
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
if (TOOL_USE_UUID_PATTERN.test(trimmed)) {
|
|
1487
|
+
return trimmed.toLowerCase();
|
|
1488
|
+
}
|
|
1489
|
+
return trimmed;
|
|
1490
|
+
}
|
|
1491
|
+
function toolInvocationCorrelationId(toolUseId) {
|
|
1492
|
+
return createHash2("sha256").update(canonicalToolUseIdForCorrelation(toolUseId)).digest("hex").slice(0, 16);
|
|
1493
|
+
}
|
|
1403
1494
|
function isValidApprovalCorrelationId(value) {
|
|
1404
1495
|
return /^[a-f0-9]{16}$/.test(value);
|
|
1405
1496
|
}
|
|
@@ -1422,7 +1513,16 @@ function isValidPreservedHashField(field, value) {
|
|
|
1422
1513
|
return isValidAuditFingerprint(value);
|
|
1423
1514
|
}
|
|
1424
1515
|
function scrubAuditContainer(value, options) {
|
|
1425
|
-
|
|
1516
|
+
const withoutRawToolIds = (input) => {
|
|
1517
|
+
if (Array.isArray(input)) return input.map(withoutRawToolIds);
|
|
1518
|
+
if (input && typeof input === "object") {
|
|
1519
|
+
return Object.fromEntries(
|
|
1520
|
+
Object.entries(input).filter(([key]) => key !== "tool_use_id").map(([key, child]) => [key, withoutRawToolIds(child)])
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
return input;
|
|
1524
|
+
};
|
|
1525
|
+
return scrubValue(withoutRawToolIds(value), {
|
|
1426
1526
|
...options,
|
|
1427
1527
|
maskHighEntropyStrings: true
|
|
1428
1528
|
});
|
|
@@ -1441,7 +1541,7 @@ function serializeAuditField(key, value, options) {
|
|
|
1441
1541
|
if (key === "timestamp" && typeof value === "string" && isValidAuditTimestamp(value)) {
|
|
1442
1542
|
return value;
|
|
1443
1543
|
}
|
|
1444
|
-
if (key === "approvalCorrelationId" && typeof value === "string" && isValidApprovalCorrelationId(value)) {
|
|
1544
|
+
if ((key === "approvalCorrelationId" || key === "toolInvocationCorrelationId") && typeof value === "string" && isValidApprovalCorrelationId(value)) {
|
|
1445
1545
|
return value;
|
|
1446
1546
|
}
|
|
1447
1547
|
if ((key === "runtimeVersion" || key === "runtimeBuildStamp" || key === "boundaryProfile") && typeof value === "string" && value.length > 0) {
|
|
@@ -1493,7 +1593,7 @@ function serializeAuditRecordV3(record, options) {
|
|
|
1493
1593
|
serialized.approvalCorrelationId = record.approvalCorrelationId;
|
|
1494
1594
|
}
|
|
1495
1595
|
for (const [key, value] of Object.entries(record)) {
|
|
1496
|
-
if (key === "timestamp" || key === "ts" || key === "approvalId" || key === "schemaVersion") {
|
|
1596
|
+
if (key === "timestamp" || key === "ts" || key === "approvalId" || key === "tool_use_id" || key === "schemaVersion") {
|
|
1497
1597
|
continue;
|
|
1498
1598
|
}
|
|
1499
1599
|
const next = serializeAuditField(key, value, options);
|
|
@@ -1503,7 +1603,7 @@ function serializeAuditRecordV3(record, options) {
|
|
|
1503
1603
|
}
|
|
1504
1604
|
return serialized;
|
|
1505
1605
|
}
|
|
1506
|
-
var AUDIT_SCHEMA_VERSION, ISO8601_PATTERN, HEX64_PATTERN, SCRUB_PLACEHOLDERS, PRESERVED_HASH_FIELDS, PRESERVED_LITERAL_FIELDS, SCRUBBED_CONTAINER_FIELDS;
|
|
1606
|
+
var AUDIT_SCHEMA_VERSION, ISO8601_PATTERN, HEX64_PATTERN, SCRUB_PLACEHOLDERS, PRESERVED_HASH_FIELDS, PRESERVED_LITERAL_FIELDS, SCRUBBED_CONTAINER_FIELDS, TOOL_USE_UUID_PATTERN;
|
|
1507
1607
|
var init_audit_serialize = __esm({
|
|
1508
1608
|
"src/core/audit-serialize.ts"() {
|
|
1509
1609
|
"use strict";
|
|
@@ -1525,6 +1625,7 @@ var init_audit_serialize = __esm({
|
|
|
1525
1625
|
PRESERVED_LITERAL_FIELDS = /* @__PURE__ */ new Set([
|
|
1526
1626
|
"timestamp",
|
|
1527
1627
|
"approvalCorrelationId",
|
|
1628
|
+
"toolInvocationCorrelationId",
|
|
1528
1629
|
"runtimeVersion",
|
|
1529
1630
|
"runtimeBuildStamp",
|
|
1530
1631
|
"boundaryProfile",
|
|
@@ -1545,6 +1646,7 @@ var init_audit_serialize = __esm({
|
|
|
1545
1646
|
"predictedAssessment",
|
|
1546
1647
|
"observedAssessment"
|
|
1547
1648
|
]);
|
|
1649
|
+
TOOL_USE_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1548
1650
|
}
|
|
1549
1651
|
});
|
|
1550
1652
|
|
|
@@ -4130,7 +4232,7 @@ init_approval_replay();
|
|
|
4130
4232
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
4131
4233
|
import { existsSync as existsSync16 } from "node:fs";
|
|
4132
4234
|
import { mkdir as mkdir14, readFile as readFile15, writeFile as writeFile11 } from "node:fs/promises";
|
|
4133
|
-
import
|
|
4235
|
+
import path54 from "node:path";
|
|
4134
4236
|
|
|
4135
4237
|
// src/core/approval-service.ts
|
|
4136
4238
|
init_config_io();
|
|
@@ -10872,6 +10974,24 @@ init_fingerprint2();
|
|
|
10872
10974
|
|
|
10873
10975
|
// src/core/replay-scrub.ts
|
|
10874
10976
|
init_scrub();
|
|
10977
|
+
function redactToolInvocationId(value, rawToolUseId) {
|
|
10978
|
+
if (typeof value === "string") {
|
|
10979
|
+
return rawToolUseId ? value.replaceAll(rawToolUseId, "<tool-use-id>") : value;
|
|
10980
|
+
}
|
|
10981
|
+
if (Array.isArray(value)) {
|
|
10982
|
+
return value.map((item) => redactToolInvocationId(item, rawToolUseId));
|
|
10983
|
+
}
|
|
10984
|
+
if (value && typeof value === "object") {
|
|
10985
|
+
const result = {};
|
|
10986
|
+
for (const [key, child] of Object.entries(value)) {
|
|
10987
|
+
if (key !== "tool_use_id") {
|
|
10988
|
+
result[key] = redactToolInvocationId(child, rawToolUseId);
|
|
10989
|
+
}
|
|
10990
|
+
}
|
|
10991
|
+
return result;
|
|
10992
|
+
}
|
|
10993
|
+
return value;
|
|
10994
|
+
}
|
|
10875
10995
|
function subagentFingerprintSource(payload, scrubOptions) {
|
|
10876
10996
|
const toolInput = payload.tool_input;
|
|
10877
10997
|
if (toolInput && typeof toolInput === "object") {
|
|
@@ -10904,16 +11024,20 @@ function fingerprintReplayPayload(kind, payload, scrubOptions) {
|
|
|
10904
11024
|
if (!payload) {
|
|
10905
11025
|
return void 0;
|
|
10906
11026
|
}
|
|
11027
|
+
const replayPayload = redactToolInvocationId(
|
|
11028
|
+
payload,
|
|
11029
|
+
typeof payload.tool_use_id === "string" ? payload.tool_use_id : void 0
|
|
11030
|
+
);
|
|
10907
11031
|
if (kind === "tool") {
|
|
10908
|
-
const toolInput =
|
|
11032
|
+
const toolInput = replayPayload.tool_input;
|
|
10909
11033
|
if (toolInput && typeof toolInput === "object") {
|
|
10910
11034
|
return scrubValue(toolInput, scrubOptions);
|
|
10911
11035
|
}
|
|
10912
11036
|
}
|
|
10913
11037
|
if (kind === "subagent") {
|
|
10914
|
-
return subagentFingerprintSource(
|
|
11038
|
+
return subagentFingerprintSource(replayPayload, scrubOptions);
|
|
10915
11039
|
}
|
|
10916
|
-
return scrubValue(
|
|
11040
|
+
return scrubValue(replayPayload, scrubOptions);
|
|
10917
11041
|
}
|
|
10918
11042
|
|
|
10919
11043
|
// src/core/classify-subagent.ts
|
|
@@ -11005,7 +11129,7 @@ function classifySubagent(payload, repoRoot, options = {}, config) {
|
|
|
11005
11129
|
}
|
|
11006
11130
|
|
|
11007
11131
|
// src/core/classify-tool.ts
|
|
11008
|
-
import
|
|
11132
|
+
import path42 from "node:path";
|
|
11009
11133
|
init_fingerprint2();
|
|
11010
11134
|
init_path_utils();
|
|
11011
11135
|
init_scrub();
|
|
@@ -11125,12 +11249,349 @@ function worstEffectDecision(decisions) {
|
|
|
11125
11249
|
|
|
11126
11250
|
// src/core/effect-ir/shell-lower.ts
|
|
11127
11251
|
init_git_resource_identity();
|
|
11252
|
+
init_path_utils();
|
|
11128
11253
|
init_shell_tokenizer();
|
|
11129
11254
|
import { lstatSync as lstatSync2, realpathSync as realpathSync4 } from "node:fs";
|
|
11130
|
-
import
|
|
11255
|
+
import path41 from "node:path";
|
|
11131
11256
|
|
|
11132
|
-
// src/core/verdict/
|
|
11257
|
+
// src/core/verdict/docker-compose-run.ts
|
|
11258
|
+
import path36 from "node:path";
|
|
11259
|
+
|
|
11260
|
+
// src/core/verdict/recursive-invocation.ts
|
|
11133
11261
|
import path35 from "node:path";
|
|
11262
|
+
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish"]);
|
|
11263
|
+
var PYTHON_INTERPRETERS = /* @__PURE__ */ new Set(["python", "python3"]);
|
|
11264
|
+
var SHELL_SHORT_OPTIONS = /* @__PURE__ */ new Set(["c", "l", "e", "x", "u"]);
|
|
11265
|
+
var SHELL_NON_SCRIPT_SHORT_OPTIONS = /* @__PURE__ */ new Set(["n"]);
|
|
11266
|
+
var SHELL_TERMINAL_OPTIONS = /* @__PURE__ */ new Map([
|
|
11267
|
+
["bash", /* @__PURE__ */ new Set(["--help", "--version"])],
|
|
11268
|
+
["zsh", /* @__PURE__ */ new Set(["--version"])],
|
|
11269
|
+
["fish", /* @__PURE__ */ new Set(["-h", "--help", "-v", "--version"])]
|
|
11270
|
+
]);
|
|
11271
|
+
var SHELL_VALUE_OPTIONS = /* @__PURE__ */ new Set(["-O", "+O", "--init-file", "--rcfile"]);
|
|
11272
|
+
var NODE_TERMINAL_OPTIONS = /* @__PURE__ */ new Set(["-h", "--help", "--help-all", "-v", "--version"]);
|
|
11273
|
+
var NODE_FILE_OPTIONS = /* @__PURE__ */ new Set(["-c", "--check"]);
|
|
11274
|
+
var PYTHON_PROFILE = {
|
|
11275
|
+
scriptOptions: /* @__PURE__ */ new Set(["-c"]),
|
|
11276
|
+
terminalOptions: /* @__PURE__ */ new Set(["-h", "--help", "-V", "-VV", "--version"]),
|
|
11277
|
+
terminalValueOptions: /* @__PURE__ */ new Set(["-m"]),
|
|
11278
|
+
flagOptions: /* @__PURE__ */ new Set([
|
|
11279
|
+
"-b",
|
|
11280
|
+
"-bb",
|
|
11281
|
+
"-B",
|
|
11282
|
+
"-d",
|
|
11283
|
+
"-E",
|
|
11284
|
+
"-I",
|
|
11285
|
+
"-O",
|
|
11286
|
+
"-OO",
|
|
11287
|
+
"-P",
|
|
11288
|
+
"-q",
|
|
11289
|
+
"-s",
|
|
11290
|
+
"-S",
|
|
11291
|
+
"-u",
|
|
11292
|
+
"-v",
|
|
11293
|
+
"-x"
|
|
11294
|
+
]),
|
|
11295
|
+
valueOptions: /* @__PURE__ */ new Set(["-W", "-X"]),
|
|
11296
|
+
attachedValuePrefixes: ["-W", "-X"]
|
|
11297
|
+
};
|
|
11298
|
+
var RUBY_PROFILE = {
|
|
11299
|
+
scriptOptions: /* @__PURE__ */ new Set(["-e"]),
|
|
11300
|
+
terminalOptions: /* @__PURE__ */ new Set(["-h", "--help", "-v", "--version", "--copyright"]),
|
|
11301
|
+
terminalValueOptions: /* @__PURE__ */ new Set([]),
|
|
11302
|
+
flagOptions: /* @__PURE__ */ new Set(["-d", "--debug", "-w"]),
|
|
11303
|
+
valueOptions: /* @__PURE__ */ new Set(["-I"]),
|
|
11304
|
+
attachedValuePrefixes: ["-I"]
|
|
11305
|
+
};
|
|
11306
|
+
var PERL_PROFILE = {
|
|
11307
|
+
scriptOptions: /* @__PURE__ */ new Set(["-e"]),
|
|
11308
|
+
terminalOptions: /* @__PURE__ */ new Set(["-h", "--help", "-v", "--version"]),
|
|
11309
|
+
terminalValueOptions: /* @__PURE__ */ new Set([]),
|
|
11310
|
+
flagOptions: /* @__PURE__ */ new Set([]),
|
|
11311
|
+
valueOptions: /* @__PURE__ */ new Set(["-I"]),
|
|
11312
|
+
attachedValuePrefixes: ["-I"]
|
|
11313
|
+
};
|
|
11314
|
+
var OSASCRIPT_PROFILE = {
|
|
11315
|
+
scriptOptions: /* @__PURE__ */ new Set(["-e"]),
|
|
11316
|
+
terminalOptions: /* @__PURE__ */ new Set(["-h", "--help"]),
|
|
11317
|
+
terminalValueOptions: /* @__PURE__ */ new Set([]),
|
|
11318
|
+
flagOptions: /* @__PURE__ */ new Set([]),
|
|
11319
|
+
valueOptions: /* @__PURE__ */ new Set(["-l"]),
|
|
11320
|
+
attachedValuePrefixes: []
|
|
11321
|
+
};
|
|
11322
|
+
function normalizeInterpreter(value) {
|
|
11323
|
+
return path35.basename(value);
|
|
11324
|
+
}
|
|
11325
|
+
function scriptResult(interpreter, token) {
|
|
11326
|
+
if (!token) {
|
|
11327
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_argv_incomplete" };
|
|
11328
|
+
}
|
|
11329
|
+
if (token.parts.some((part) => part.hasExpansion)) {
|
|
11330
|
+
return { kind: "dynamic", interpreter, signal: "shell.script_expanded" };
|
|
11331
|
+
}
|
|
11332
|
+
return { kind: "static", interpreter, script: token.value };
|
|
11333
|
+
}
|
|
11334
|
+
function decodeShell(words, interpreter) {
|
|
11335
|
+
for (let index = 1; index < words.length; index += 1) {
|
|
11336
|
+
const option = words[index]?.value ?? "";
|
|
11337
|
+
if (option === "--") return { kind: "none" };
|
|
11338
|
+
if (SHELL_TERMINAL_OPTIONS.get(interpreter)?.has(option)) return { kind: "none" };
|
|
11339
|
+
if (interpreter === "bash" && SHELL_VALUE_OPTIONS.has(option)) {
|
|
11340
|
+
const operand = words[index + 1]?.value;
|
|
11341
|
+
if (!operand || operand.startsWith("-")) {
|
|
11342
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11343
|
+
}
|
|
11344
|
+
index += 1;
|
|
11345
|
+
continue;
|
|
11346
|
+
}
|
|
11347
|
+
if (!option.startsWith("-") || option === "-") return { kind: "none" };
|
|
11348
|
+
const flags = [...option.slice(1)];
|
|
11349
|
+
if (flags.length === 0) {
|
|
11350
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11351
|
+
}
|
|
11352
|
+
if (flags.every((flag) => SHELL_SHORT_OPTIONS.has(flag))) {
|
|
11353
|
+
if (!flags.includes("c")) continue;
|
|
11354
|
+
return scriptResult(interpreter, words[index + 1]);
|
|
11355
|
+
}
|
|
11356
|
+
if (flags.every(
|
|
11357
|
+
(flag) => SHELL_SHORT_OPTIONS.has(flag) || SHELL_NON_SCRIPT_SHORT_OPTIONS.has(flag)
|
|
11358
|
+
) && flags.some((flag) => SHELL_NON_SCRIPT_SHORT_OPTIONS.has(flag))) {
|
|
11359
|
+
return { kind: "none" };
|
|
11360
|
+
}
|
|
11361
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11362
|
+
}
|
|
11363
|
+
return { kind: "none" };
|
|
11364
|
+
}
|
|
11365
|
+
function decodeSeparated(words, interpreter, profile) {
|
|
11366
|
+
for (let index = 1; index < words.length; index += 1) {
|
|
11367
|
+
const option = words[index]?.value ?? "";
|
|
11368
|
+
if (option === "--" || !option.startsWith("-") || option === "-") return { kind: "none" };
|
|
11369
|
+
if (profile.scriptOptions.has(option)) {
|
|
11370
|
+
return scriptResult(interpreter, words[index + 1]);
|
|
11371
|
+
}
|
|
11372
|
+
if (profile.terminalOptions.has(option)) return { kind: "none" };
|
|
11373
|
+
if (profile.terminalValueOptions.has(option)) {
|
|
11374
|
+
const operand = words[index + 1]?.value;
|
|
11375
|
+
if (!operand || operand.startsWith("-")) {
|
|
11376
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11377
|
+
}
|
|
11378
|
+
return { kind: "none" };
|
|
11379
|
+
}
|
|
11380
|
+
if (profile.flagOptions.has(option)) {
|
|
11381
|
+
continue;
|
|
11382
|
+
}
|
|
11383
|
+
if (profile.valueOptions.has(option)) {
|
|
11384
|
+
const operand = words[index + 1]?.value;
|
|
11385
|
+
if (!operand || operand.startsWith("-")) {
|
|
11386
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11387
|
+
}
|
|
11388
|
+
index += 1;
|
|
11389
|
+
continue;
|
|
11390
|
+
}
|
|
11391
|
+
if (profile.attachedValuePrefixes.some(
|
|
11392
|
+
(prefix) => option.startsWith(prefix) && option.length > prefix.length
|
|
11393
|
+
)) {
|
|
11394
|
+
continue;
|
|
11395
|
+
}
|
|
11396
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11397
|
+
}
|
|
11398
|
+
return { kind: "none" };
|
|
11399
|
+
}
|
|
11400
|
+
function decodeNode(words, interpreter) {
|
|
11401
|
+
const option = words[1]?.value ?? "";
|
|
11402
|
+
if (option === "--" || !option.startsWith("-") || option === "-") return { kind: "none" };
|
|
11403
|
+
if (option === "-e" || option === "--eval") {
|
|
11404
|
+
return scriptResult(interpreter, words[2]);
|
|
11405
|
+
}
|
|
11406
|
+
if (option.startsWith("--eval=")) {
|
|
11407
|
+
const script = option.slice("--eval=".length);
|
|
11408
|
+
if (words[1]?.parts.some((part) => part.hasExpansion)) {
|
|
11409
|
+
return { kind: "dynamic", interpreter, signal: "shell.script_expanded" };
|
|
11410
|
+
}
|
|
11411
|
+
return { kind: "static", interpreter, script };
|
|
11412
|
+
}
|
|
11413
|
+
if (NODE_TERMINAL_OPTIONS.has(option)) return { kind: "none" };
|
|
11414
|
+
if (NODE_FILE_OPTIONS.has(option)) return { kind: "none" };
|
|
11415
|
+
return { kind: "indeterminate", interpreter, signal: "shell.interpreter_option_unknown" };
|
|
11416
|
+
}
|
|
11417
|
+
function decodeEval(words) {
|
|
11418
|
+
const arguments_ = words.slice(1);
|
|
11419
|
+
if (arguments_.length === 0) return { kind: "none" };
|
|
11420
|
+
if (arguments_.some((word) => word.parts.some((part) => part.hasExpansion))) {
|
|
11421
|
+
return { kind: "dynamic", interpreter: "eval", signal: "shell.script_expanded" };
|
|
11422
|
+
}
|
|
11423
|
+
return {
|
|
11424
|
+
kind: "static",
|
|
11425
|
+
interpreter: "eval",
|
|
11426
|
+
script: arguments_.map((word) => word.value).join(" ")
|
|
11427
|
+
};
|
|
11428
|
+
}
|
|
11429
|
+
function decodeRecursiveInvocation(tokens) {
|
|
11430
|
+
if (tokens.some((token) => token.kind === "operator")) return { kind: "none" };
|
|
11431
|
+
const words = tokens.filter((token) => token.kind === "word");
|
|
11432
|
+
const interpreter = normalizeInterpreter(words[0]?.value ?? "");
|
|
11433
|
+
if (!interpreter) return { kind: "none" };
|
|
11434
|
+
if (interpreter === "eval") return decodeEval(words);
|
|
11435
|
+
if (SHELL_INTERPRETERS.has(interpreter)) return decodeShell(words, interpreter);
|
|
11436
|
+
if (PYTHON_INTERPRETERS.has(interpreter)) {
|
|
11437
|
+
return decodeSeparated(words, interpreter, PYTHON_PROFILE);
|
|
11438
|
+
}
|
|
11439
|
+
if (interpreter === "node") return decodeNode(words, interpreter);
|
|
11440
|
+
if (interpreter === "ruby") return decodeSeparated(words, interpreter, RUBY_PROFILE);
|
|
11441
|
+
if (interpreter === "perl") return decodeSeparated(words, interpreter, PERL_PROFILE);
|
|
11442
|
+
if (interpreter === "osascript") return decodeSeparated(words, interpreter, OSASCRIPT_PROFILE);
|
|
11443
|
+
return { kind: "none" };
|
|
11444
|
+
}
|
|
11445
|
+
function shellTokensFromValues(values, options = {}) {
|
|
11446
|
+
let offset = 0;
|
|
11447
|
+
return values.map((value) => {
|
|
11448
|
+
const start = offset;
|
|
11449
|
+
const end = start + value.length;
|
|
11450
|
+
offset = end + 1;
|
|
11451
|
+
return {
|
|
11452
|
+
kind: "word",
|
|
11453
|
+
value,
|
|
11454
|
+
raw: value,
|
|
11455
|
+
start,
|
|
11456
|
+
end,
|
|
11457
|
+
parts: [
|
|
11458
|
+
{
|
|
11459
|
+
value,
|
|
11460
|
+
raw: value,
|
|
11461
|
+
start,
|
|
11462
|
+
end,
|
|
11463
|
+
quote: "unquoted",
|
|
11464
|
+
hasExpansion: options.detectExpansion !== false && (value.includes("$") || value.includes("`"))
|
|
11465
|
+
}
|
|
11466
|
+
]
|
|
11467
|
+
};
|
|
11468
|
+
});
|
|
11469
|
+
}
|
|
11470
|
+
|
|
11471
|
+
// src/core/verdict/docker-compose-run.ts
|
|
11472
|
+
var COMPOSE_GLOBAL_OPTIONS = /* @__PURE__ */ new Map([
|
|
11473
|
+
["--all-resources", 0],
|
|
11474
|
+
["--ansi", 1],
|
|
11475
|
+
["--compatibility", 0],
|
|
11476
|
+
["--dry-run", 0],
|
|
11477
|
+
["--env-file", 1],
|
|
11478
|
+
["-f", 1],
|
|
11479
|
+
["--file", 1],
|
|
11480
|
+
["--parallel", 1],
|
|
11481
|
+
["--profile", 1],
|
|
11482
|
+
["--progress", 1],
|
|
11483
|
+
["--project-directory", 1],
|
|
11484
|
+
["-p", 1],
|
|
11485
|
+
["--project-name", 1]
|
|
11486
|
+
]);
|
|
11487
|
+
var COMPOSE_RUN_OPTIONS = /* @__PURE__ */ new Map([
|
|
11488
|
+
["--build", 0],
|
|
11489
|
+
["--cap-add", 1],
|
|
11490
|
+
["--cap-drop", 1],
|
|
11491
|
+
["-d", 0],
|
|
11492
|
+
["--detach", 0],
|
|
11493
|
+
["--entrypoint", 1],
|
|
11494
|
+
["-e", 1],
|
|
11495
|
+
["--env", 1],
|
|
11496
|
+
["--env-from-file", 1],
|
|
11497
|
+
["-i", 0],
|
|
11498
|
+
["--interactive", 0],
|
|
11499
|
+
["-l", 1],
|
|
11500
|
+
["--label", 1],
|
|
11501
|
+
["--name", 1],
|
|
11502
|
+
["--no-deps", 0],
|
|
11503
|
+
["-T", 0],
|
|
11504
|
+
["--no-tty", 0],
|
|
11505
|
+
["-p", 1],
|
|
11506
|
+
["--publish", 1],
|
|
11507
|
+
["--pull", 1],
|
|
11508
|
+
["-q", 0],
|
|
11509
|
+
["--quiet", 0],
|
|
11510
|
+
["--quiet-build", 0],
|
|
11511
|
+
["--quiet-pull", 0],
|
|
11512
|
+
["--remove-orphans", 0],
|
|
11513
|
+
["--rm", 0],
|
|
11514
|
+
["-P", 0],
|
|
11515
|
+
["--service-ports", 0],
|
|
11516
|
+
["--use-aliases", 0],
|
|
11517
|
+
["-u", 1],
|
|
11518
|
+
["--user", 1],
|
|
11519
|
+
["-v", 1],
|
|
11520
|
+
["--volume", 1],
|
|
11521
|
+
["-w", 1],
|
|
11522
|
+
["--workdir", 1]
|
|
11523
|
+
]);
|
|
11524
|
+
function parseOptions(words, start, options) {
|
|
11525
|
+
let index = start;
|
|
11526
|
+
while (index < words.length) {
|
|
11527
|
+
const value = words[index]?.value ?? "";
|
|
11528
|
+
if (value === "--") return { kind: "ok", index: index + 1 };
|
|
11529
|
+
if (!value.startsWith("-") || value === "-") return { kind: "ok", index };
|
|
11530
|
+
const equalsIndex = value.indexOf("=");
|
|
11531
|
+
const name = equalsIndex === -1 ? value : value.slice(0, equalsIndex);
|
|
11532
|
+
const arity = options.get(name);
|
|
11533
|
+
if (arity === void 0) return { kind: "indeterminate" };
|
|
11534
|
+
if (equalsIndex !== -1) {
|
|
11535
|
+
if (!value.startsWith("--") || arity !== 1 || equalsIndex === value.length - 1) {
|
|
11536
|
+
return { kind: "indeterminate" };
|
|
11537
|
+
}
|
|
11538
|
+
index += 1;
|
|
11539
|
+
continue;
|
|
11540
|
+
}
|
|
11541
|
+
if (arity === 1) {
|
|
11542
|
+
if (!words[index + 1]) return { kind: "indeterminate" };
|
|
11543
|
+
index += 2;
|
|
11544
|
+
continue;
|
|
11545
|
+
}
|
|
11546
|
+
index += 1;
|
|
11547
|
+
}
|
|
11548
|
+
return { kind: "ok", index };
|
|
11549
|
+
}
|
|
11550
|
+
function decodeDockerComposeRun(tokens) {
|
|
11551
|
+
if (tokens.some((token) => token.kind === "operator")) return { kind: "none" };
|
|
11552
|
+
const words = tokens.filter((token) => token.kind === "word");
|
|
11553
|
+
const head = path36.basename(words[0]?.value ?? "");
|
|
11554
|
+
let index;
|
|
11555
|
+
if (head === "docker-compose") {
|
|
11556
|
+
index = 1;
|
|
11557
|
+
} else if (head === "docker" && words[1]?.value === "compose") {
|
|
11558
|
+
index = 2;
|
|
11559
|
+
} else {
|
|
11560
|
+
return { kind: "none" };
|
|
11561
|
+
}
|
|
11562
|
+
const globalOptions = parseOptions(words, index, COMPOSE_GLOBAL_OPTIONS);
|
|
11563
|
+
if (globalOptions.kind === "indeterminate") {
|
|
11564
|
+
return { kind: "indeterminate", signal: "shell.compose_argv_indeterminate" };
|
|
11565
|
+
}
|
|
11566
|
+
index = globalOptions.index;
|
|
11567
|
+
if (words[index]?.value !== "run") return { kind: "none" };
|
|
11568
|
+
const runOptions = parseOptions(words, index + 1, COMPOSE_RUN_OPTIONS);
|
|
11569
|
+
if (runOptions.kind === "indeterminate") {
|
|
11570
|
+
return { kind: "indeterminate", signal: "shell.compose_argv_indeterminate" };
|
|
11571
|
+
}
|
|
11572
|
+
index = runOptions.index;
|
|
11573
|
+
const service = words[index]?.value;
|
|
11574
|
+
if (!service) return { kind: "indeterminate", signal: "shell.compose_argv_indeterminate" };
|
|
11575
|
+
const command = words.slice(index + 1);
|
|
11576
|
+
if (command.length === 0) return { kind: "none" };
|
|
11577
|
+
const recursive = decodeRecursiveInvocation(command);
|
|
11578
|
+
if (recursive.kind === "static") {
|
|
11579
|
+
return {
|
|
11580
|
+
kind: "recursive",
|
|
11581
|
+
service,
|
|
11582
|
+
interpreter: recursive.interpreter,
|
|
11583
|
+
script: recursive.script
|
|
11584
|
+
};
|
|
11585
|
+
}
|
|
11586
|
+
if (recursive.kind === "dynamic") return { kind: "dynamic", service, signal: recursive.signal };
|
|
11587
|
+
if (recursive.kind === "indeterminate") {
|
|
11588
|
+
return { kind: "indeterminate", signal: "shell.compose_argv_indeterminate" };
|
|
11589
|
+
}
|
|
11590
|
+
return { kind: "none" };
|
|
11591
|
+
}
|
|
11592
|
+
|
|
11593
|
+
// src/core/verdict/egress-classify.ts
|
|
11594
|
+
import path37 from "node:path";
|
|
11134
11595
|
var CURL_EFFECT_NEUTRAL_FLAGS = /* @__PURE__ */ new Set([
|
|
11135
11596
|
"-f",
|
|
11136
11597
|
"-L",
|
|
@@ -11165,7 +11626,7 @@ var GH_READ_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
11165
11626
|
"workflow view"
|
|
11166
11627
|
]);
|
|
11167
11628
|
function decodeEgressEffects(params) {
|
|
11168
|
-
const head =
|
|
11629
|
+
const head = path37.basename(params.tokens[0] ?? "");
|
|
11169
11630
|
if (head !== "curl" && head !== "wget" && head !== "gh") {
|
|
11170
11631
|
return null;
|
|
11171
11632
|
}
|
|
@@ -11173,7 +11634,7 @@ function decodeEgressEffects(params) {
|
|
|
11173
11634
|
const provenance = { segment: params.segment };
|
|
11174
11635
|
const requirements = [];
|
|
11175
11636
|
for (const file of decoded.files) {
|
|
11176
|
-
const resolved =
|
|
11637
|
+
const resolved = path37.resolve(params.cwd, expandHome2(file));
|
|
11177
11638
|
requirements.push(
|
|
11178
11639
|
requirement("fs.read", "fs.read", { kind: "path", path: resolved }, params.segment, [
|
|
11179
11640
|
"egress.explicit_file_read"
|
|
@@ -11195,7 +11656,7 @@ function decodeEgressEffects(params) {
|
|
|
11195
11656
|
if (file === "-") {
|
|
11196
11657
|
continue;
|
|
11197
11658
|
}
|
|
11198
|
-
const resolved =
|
|
11659
|
+
const resolved = path37.resolve(params.cwd, expandHome2(file));
|
|
11199
11660
|
if (resolved === "/dev/null") {
|
|
11200
11661
|
continue;
|
|
11201
11662
|
}
|
|
@@ -11673,13 +12134,13 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
11673
12134
|
if (head === "wget" && !explicitOutput) {
|
|
11674
12135
|
outputFiles.push(
|
|
11675
12136
|
...endpointOutputNames.map(
|
|
11676
|
-
(name) => outputDirectory ?
|
|
12137
|
+
(name) => outputDirectory ? path37.join(outputDirectory, name) : name
|
|
11677
12138
|
)
|
|
11678
12139
|
);
|
|
11679
12140
|
} else if (head === "curl" && remoteNameOutput) {
|
|
11680
12141
|
outputFiles.push(
|
|
11681
12142
|
...endpointOutputNames.map(
|
|
11682
|
-
(name) => outputDirectory ?
|
|
12143
|
+
(name) => outputDirectory ? path37.join(outputDirectory, name) : name
|
|
11683
12144
|
)
|
|
11684
12145
|
);
|
|
11685
12146
|
}
|
|
@@ -11693,7 +12154,7 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
11693
12154
|
outputFiles: [
|
|
11694
12155
|
...new Set(
|
|
11695
12156
|
outputFiles.map(
|
|
11696
|
-
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !
|
|
12157
|
+
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !path37.isAbsolute(file) ? path37.join(outputDirectory, file) : file
|
|
11697
12158
|
)
|
|
11698
12159
|
)
|
|
11699
12160
|
],
|
|
@@ -11708,7 +12169,7 @@ function remoteOutputName(spec) {
|
|
|
11708
12169
|
} catch {
|
|
11709
12170
|
pathname = spec.split(/[?#]/, 1)[0] ?? "";
|
|
11710
12171
|
}
|
|
11711
|
-
const name =
|
|
12172
|
+
const name = path37.posix.basename(pathname);
|
|
11712
12173
|
return name && name !== "/" ? name : "index.html";
|
|
11713
12174
|
}
|
|
11714
12175
|
function decodeGhGrammar(tokens) {
|
|
@@ -11873,7 +12334,7 @@ function expandHome2(value) {
|
|
|
11873
12334
|
return process.env.HOME ?? value;
|
|
11874
12335
|
}
|
|
11875
12336
|
if (value.startsWith("~/")) {
|
|
11876
|
-
return
|
|
12337
|
+
return path37.join(process.env.HOME ?? "~", value.slice(2));
|
|
11877
12338
|
}
|
|
11878
12339
|
return value;
|
|
11879
12340
|
}
|
|
@@ -11892,7 +12353,7 @@ function requirement(tag, action, resource, segment, signals) {
|
|
|
11892
12353
|
}
|
|
11893
12354
|
|
|
11894
12355
|
// src/core/verdict/git-classifier.ts
|
|
11895
|
-
import
|
|
12356
|
+
import path38 from "node:path";
|
|
11896
12357
|
init_shell_tokenizer();
|
|
11897
12358
|
var GIT_BRANCH_MUTATION_FLAGS = /* @__PURE__ */ new Set([
|
|
11898
12359
|
"--copy",
|
|
@@ -11988,7 +12449,7 @@ var FILE_OPERAND_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
11988
12449
|
var COMPOUND_SUBCOMMAND_HEADS = /* @__PURE__ */ new Set(["worktree", "stash", "tag"]);
|
|
11989
12450
|
var REF_ONLY_WITHOUT_TERMINATOR = /* @__PURE__ */ new Set(["checkout", "show", "log"]);
|
|
11990
12451
|
function isGitExecutable(token) {
|
|
11991
|
-
return
|
|
12452
|
+
return path38.basename(token) === "git";
|
|
11992
12453
|
}
|
|
11993
12454
|
function takesValue(flag) {
|
|
11994
12455
|
return flag === "-C" || flag === "-c" || flag === "--git-dir" || flag === "--work-tree" || flag === "--exec-path" || flag === "--paginate" || flag === "--config-env" || flag.startsWith("-C") || flag.startsWith("-c") || flag.startsWith("--git-dir=") || flag.startsWith("--work-tree=");
|
|
@@ -12016,7 +12477,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12016
12477
|
if (token === "-C" || token === "--work-tree" || token === "--git-dir" || token === "-c") {
|
|
12017
12478
|
const value = tokens[index + 1];
|
|
12018
12479
|
if (token === "-C" && value) {
|
|
12019
|
-
effectiveCwd =
|
|
12480
|
+
effectiveCwd = path38.resolve(baseCwd, value);
|
|
12020
12481
|
} else if (token === "--work-tree" && value) {
|
|
12021
12482
|
workTree = value;
|
|
12022
12483
|
} else if (token === "--git-dir" && value) {
|
|
@@ -12026,7 +12487,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12026
12487
|
continue;
|
|
12027
12488
|
}
|
|
12028
12489
|
if (token.startsWith("-C") && token.length > 2) {
|
|
12029
|
-
effectiveCwd =
|
|
12490
|
+
effectiveCwd = path38.resolve(baseCwd, token.slice(2));
|
|
12030
12491
|
index += 1;
|
|
12031
12492
|
continue;
|
|
12032
12493
|
}
|
|
@@ -12169,7 +12630,7 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12169
12630
|
if (!looksLikeFileOperand(token)) {
|
|
12170
12631
|
return false;
|
|
12171
12632
|
}
|
|
12172
|
-
if (token.startsWith(".") ||
|
|
12633
|
+
if (token.startsWith(".") || path38.isAbsolute(token)) {
|
|
12173
12634
|
return true;
|
|
12174
12635
|
}
|
|
12175
12636
|
return token.includes(".");
|
|
@@ -12177,12 +12638,12 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12177
12638
|
function resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir) {
|
|
12178
12639
|
const resolveBase = effectiveCwd ?? baseCwd;
|
|
12179
12640
|
if (workTree) {
|
|
12180
|
-
return
|
|
12641
|
+
return path38.resolve(resolveBase, workTree);
|
|
12181
12642
|
}
|
|
12182
12643
|
if (gitDir) {
|
|
12183
|
-
const resolvedGitDir =
|
|
12184
|
-
if (
|
|
12185
|
-
return
|
|
12644
|
+
const resolvedGitDir = path38.resolve(resolveBase, gitDir);
|
|
12645
|
+
if (path38.basename(resolvedGitDir) === ".git") {
|
|
12646
|
+
return path38.dirname(resolvedGitDir);
|
|
12186
12647
|
}
|
|
12187
12648
|
}
|
|
12188
12649
|
return void 0;
|
|
@@ -12263,7 +12724,7 @@ function classifyGitCommand(tokens, baseCwd) {
|
|
|
12263
12724
|
const { subcommand, args, effectiveCwd, gitDir, workTree } = normalized;
|
|
12264
12725
|
const normalizedKey = `git ${subcommand}`;
|
|
12265
12726
|
const gitWorkTree = resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir);
|
|
12266
|
-
const effectiveGitDir = gitDir ?
|
|
12727
|
+
const effectiveGitDir = gitDir ? path38.resolve(effectiveCwd ?? baseCwd, gitDir) : void 0;
|
|
12267
12728
|
const scopeTargets = [effectiveCwd, gitWorkTree, effectiveGitDir].filter(
|
|
12268
12729
|
(target, index, targets) => Boolean(target) && targets.indexOf(target) === index
|
|
12269
12730
|
);
|
|
@@ -12415,9 +12876,9 @@ function decodeGitEffects(params) {
|
|
|
12415
12876
|
...subcommand === "push" ? ["tier0_external"] : []
|
|
12416
12877
|
];
|
|
12417
12878
|
const effectiveCwd = normalized.effectiveCwd ?? params.cwd;
|
|
12418
|
-
const workTreeRoot = normalized.workTree ?
|
|
12419
|
-
const gitRefRoot = normalized.gitDir ?
|
|
12420
|
-
const gitControlRoot = normalized.gitDir ? gitRefRoot :
|
|
12879
|
+
const workTreeRoot = normalized.workTree ? path38.resolve(normalized.effectiveCwd ?? params.cwd, normalized.workTree) : normalized.effectiveCwd ?? params.repoRoot;
|
|
12880
|
+
const gitRefRoot = normalized.gitDir ? path38.resolve(effectiveCwd, normalized.gitDir) : workTreeRoot;
|
|
12881
|
+
const gitControlRoot = normalized.gitDir ? gitRefRoot : path38.join(gitRefRoot, ".git");
|
|
12421
12882
|
const requirements = [];
|
|
12422
12883
|
if (subcommand === "fetch" || subcommand === "pull") {
|
|
12423
12884
|
const positionals = gitRemotePositionals(args);
|
|
@@ -12576,7 +13037,7 @@ function decodeGitEffects(params) {
|
|
|
12576
13037
|
gitRequirement(
|
|
12577
13038
|
"control_plane.write",
|
|
12578
13039
|
"control_plane.write",
|
|
12579
|
-
{ kind: "path", path:
|
|
13040
|
+
{ kind: "path", path: path38.join(gitControlRoot, "logs") },
|
|
12580
13041
|
params.segment,
|
|
12581
13042
|
[...signals, "git_history_destructive", "git.reflog.mutate"]
|
|
12582
13043
|
)
|
|
@@ -12634,7 +13095,7 @@ function decodeGitEffects(params) {
|
|
|
12634
13095
|
gitRequirement(
|
|
12635
13096
|
"fs.read",
|
|
12636
13097
|
"fs.read",
|
|
12637
|
-
{ kind: "path", path:
|
|
13098
|
+
{ kind: "path", path: path38.resolve(workTreeRoot, operand) },
|
|
12638
13099
|
params.segment,
|
|
12639
13100
|
[...signals, "git.path.read"]
|
|
12640
13101
|
)
|
|
@@ -12669,7 +13130,7 @@ function decodeGitEffects(params) {
|
|
|
12669
13130
|
gitRequirement(
|
|
12670
13131
|
"fs.write",
|
|
12671
13132
|
"fs.write",
|
|
12672
|
-
{ kind: "path", path:
|
|
13133
|
+
{ kind: "path", path: path38.resolve(workTreeRoot, operand) },
|
|
12673
13134
|
params.segment,
|
|
12674
13135
|
[...signals, "git.path.write"]
|
|
12675
13136
|
)
|
|
@@ -12854,7 +13315,144 @@ function gitRequirement(tag, action, resource, segment, signals) {
|
|
|
12854
13315
|
|
|
12855
13316
|
// src/core/verdict/launcher-resolve.ts
|
|
12856
13317
|
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
|
|
12857
|
-
import
|
|
13318
|
+
import path39 from "node:path";
|
|
13319
|
+
|
|
13320
|
+
// src/core/verdict/makefile-expand.ts
|
|
13321
|
+
var MAX_EXPAND_DEPTH = 16;
|
|
13322
|
+
function parseMakefileVariables(content) {
|
|
13323
|
+
const variables = /* @__PURE__ */ new Map();
|
|
13324
|
+
for (const line of content.split("\n")) {
|
|
13325
|
+
const trimmed = line.trim();
|
|
13326
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
13327
|
+
continue;
|
|
13328
|
+
}
|
|
13329
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*[:?]?=\s*(.+)$/.exec(trimmed);
|
|
13330
|
+
if (!match) {
|
|
13331
|
+
continue;
|
|
13332
|
+
}
|
|
13333
|
+
variables.set(match[1] ?? "", (match[2] ?? "").trim());
|
|
13334
|
+
}
|
|
13335
|
+
return variables;
|
|
13336
|
+
}
|
|
13337
|
+
function normalizeMakeRecipeLine(line) {
|
|
13338
|
+
let normalized = line.trim();
|
|
13339
|
+
while (normalized.startsWith("@") || normalized.startsWith("-") || normalized.startsWith("+")) {
|
|
13340
|
+
normalized = normalized.slice(1).trimStart();
|
|
13341
|
+
}
|
|
13342
|
+
return normalized;
|
|
13343
|
+
}
|
|
13344
|
+
function expandMakeExpression(expression, cliVars, makefileVars) {
|
|
13345
|
+
if (/\$\(\s*shell\b/i.test(expression) || expression.includes("$$")) {
|
|
13346
|
+
return null;
|
|
13347
|
+
}
|
|
13348
|
+
try {
|
|
13349
|
+
const expanded = expandMakeValue(expression, cliVars, makefileVars, 0);
|
|
13350
|
+
if (expanded === null || /\$\(/.test(expanded) || /\$\{/.test(expanded)) {
|
|
13351
|
+
return null;
|
|
13352
|
+
}
|
|
13353
|
+
return expanded;
|
|
13354
|
+
} catch {
|
|
13355
|
+
return null;
|
|
13356
|
+
}
|
|
13357
|
+
}
|
|
13358
|
+
function expandMakeValue(expression, cliVars, makefileVars, depth) {
|
|
13359
|
+
if (depth > MAX_EXPAND_DEPTH) {
|
|
13360
|
+
return null;
|
|
13361
|
+
}
|
|
13362
|
+
let value = expression.trim();
|
|
13363
|
+
let changed = true;
|
|
13364
|
+
let iterations = 0;
|
|
13365
|
+
while (changed && iterations < MAX_EXPAND_DEPTH) {
|
|
13366
|
+
changed = false;
|
|
13367
|
+
iterations += 1;
|
|
13368
|
+
const orMatch = value.match(/\$\(\s*or\s+([^()]*(?:\([^)]*\)[^()]*)*)\)/);
|
|
13369
|
+
if (orMatch) {
|
|
13370
|
+
const [fullMatch, inner] = orMatch;
|
|
13371
|
+
const parts = splitMakeFunctionArgs(inner ?? "");
|
|
13372
|
+
let selected = null;
|
|
13373
|
+
for (const part of parts) {
|
|
13374
|
+
const expanded = expandMakeValue(part.trim(), cliVars, makefileVars, depth + 1);
|
|
13375
|
+
if (expanded !== null && expanded.trim() !== "") {
|
|
13376
|
+
selected = expanded;
|
|
13377
|
+
break;
|
|
13378
|
+
}
|
|
13379
|
+
}
|
|
13380
|
+
if (selected === null) {
|
|
13381
|
+
const fallback = parts.at(-1)?.trim();
|
|
13382
|
+
selected = fallback === void 0 ? "" : expandMakeValue(fallback, cliVars, makefileVars, depth + 1);
|
|
13383
|
+
}
|
|
13384
|
+
if (selected === null) {
|
|
13385
|
+
return null;
|
|
13386
|
+
}
|
|
13387
|
+
value = value.replace(fullMatch, selected);
|
|
13388
|
+
changed = true;
|
|
13389
|
+
continue;
|
|
13390
|
+
}
|
|
13391
|
+
const varMatch = value.match(/\$\(([A-Za-z_][A-Za-z0-9_]*)\)/);
|
|
13392
|
+
if (varMatch) {
|
|
13393
|
+
const [fullMatch, name] = varMatch;
|
|
13394
|
+
const resolved = resolveMakeVariable(name ?? "", cliVars, makefileVars, depth + 1);
|
|
13395
|
+
if (resolved === null) {
|
|
13396
|
+
return null;
|
|
13397
|
+
}
|
|
13398
|
+
value = value.replace(fullMatch, resolved);
|
|
13399
|
+
changed = true;
|
|
13400
|
+
continue;
|
|
13401
|
+
}
|
|
13402
|
+
const bracedMatch = value.match(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/);
|
|
13403
|
+
if (bracedMatch) {
|
|
13404
|
+
const [fullMatch, name] = bracedMatch;
|
|
13405
|
+
const resolved = name === "PWD" ? "." : resolveMakeVariable(name ?? "", cliVars, makefileVars, depth + 1);
|
|
13406
|
+
if (resolved === null) {
|
|
13407
|
+
return null;
|
|
13408
|
+
}
|
|
13409
|
+
value = value.replace(fullMatch, resolved);
|
|
13410
|
+
changed = true;
|
|
13411
|
+
continue;
|
|
13412
|
+
}
|
|
13413
|
+
break;
|
|
13414
|
+
}
|
|
13415
|
+
return value;
|
|
13416
|
+
}
|
|
13417
|
+
function resolveMakeVariable(name, cliVars, makefileVars, depth) {
|
|
13418
|
+
if (Object.hasOwn(cliVars, name)) {
|
|
13419
|
+
return cliVars[name] ?? "";
|
|
13420
|
+
}
|
|
13421
|
+
const definition = makefileVars.get(name);
|
|
13422
|
+
if (definition === void 0) {
|
|
13423
|
+
return null;
|
|
13424
|
+
}
|
|
13425
|
+
return expandMakeValue(definition, cliVars, makefileVars, depth);
|
|
13426
|
+
}
|
|
13427
|
+
function splitMakeFunctionArgs(input) {
|
|
13428
|
+
const parts = [];
|
|
13429
|
+
let current = "";
|
|
13430
|
+
let depth = 0;
|
|
13431
|
+
for (const char of input) {
|
|
13432
|
+
if (char === "(") {
|
|
13433
|
+
depth += 1;
|
|
13434
|
+
current += char;
|
|
13435
|
+
continue;
|
|
13436
|
+
}
|
|
13437
|
+
if (char === ")") {
|
|
13438
|
+
depth -= 1;
|
|
13439
|
+
current += char;
|
|
13440
|
+
continue;
|
|
13441
|
+
}
|
|
13442
|
+
if (char === "," && depth === 0) {
|
|
13443
|
+
parts.push(current.trim());
|
|
13444
|
+
current = "";
|
|
13445
|
+
continue;
|
|
13446
|
+
}
|
|
13447
|
+
current += char;
|
|
13448
|
+
}
|
|
13449
|
+
if (current.trim()) {
|
|
13450
|
+
parts.push(current.trim());
|
|
13451
|
+
}
|
|
13452
|
+
return parts;
|
|
13453
|
+
}
|
|
13454
|
+
|
|
13455
|
+
// src/core/verdict/launcher-resolve.ts
|
|
12858
13456
|
var MAX_RESOLVE_DEPTH = 8;
|
|
12859
13457
|
var PNPM_BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
12860
13458
|
"add",
|
|
@@ -12890,7 +13488,7 @@ var PNPM_BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
12890
13488
|
"why"
|
|
12891
13489
|
]);
|
|
12892
13490
|
function readPackageJson(dir) {
|
|
12893
|
-
const packagePath =
|
|
13491
|
+
const packagePath = path39.join(dir, "package.json");
|
|
12894
13492
|
if (!existsSync11(packagePath)) {
|
|
12895
13493
|
return null;
|
|
12896
13494
|
}
|
|
@@ -12901,17 +13499,17 @@ function readPackageJson(dir) {
|
|
|
12901
13499
|
}
|
|
12902
13500
|
}
|
|
12903
13501
|
function findPackageJson(startDir, stopDir) {
|
|
12904
|
-
let current =
|
|
12905
|
-
const stop =
|
|
13502
|
+
let current = path39.resolve(startDir);
|
|
13503
|
+
const stop = path39.resolve(stopDir);
|
|
12906
13504
|
while (true) {
|
|
12907
|
-
const packagePath =
|
|
13505
|
+
const packagePath = path39.join(current, "package.json");
|
|
12908
13506
|
if (existsSync11(packagePath)) {
|
|
12909
13507
|
return packagePath;
|
|
12910
13508
|
}
|
|
12911
|
-
if (current === stop || current ===
|
|
13509
|
+
if (current === stop || current === path39.dirname(current)) {
|
|
12912
13510
|
return existsSync11(packagePath) ? packagePath : null;
|
|
12913
13511
|
}
|
|
12914
|
-
const parent =
|
|
13512
|
+
const parent = path39.dirname(current);
|
|
12915
13513
|
if (!parent.startsWith(stop) && parent !== current) {
|
|
12916
13514
|
}
|
|
12917
13515
|
if (parent === current) {
|
|
@@ -12968,7 +13566,7 @@ function resolveNpmRecipe(cwd, repoRoot, scriptName, extraArgs) {
|
|
|
12968
13566
|
}
|
|
12969
13567
|
return { recipes: [], opaque: true, reason: "package_json_missing" };
|
|
12970
13568
|
}
|
|
12971
|
-
const pkg = readPackageJson(
|
|
13569
|
+
const pkg = readPackageJson(path39.dirname(packagePath));
|
|
12972
13570
|
const scripts = pkg?.scripts;
|
|
12973
13571
|
if (!scripts || typeof scripts !== "object") {
|
|
12974
13572
|
return { recipes: [], opaque: true, reason: "package_scripts_missing" };
|
|
@@ -12995,10 +13593,9 @@ function resolveNpmRecipe(cwd, repoRoot, scriptName, extraArgs) {
|
|
|
12995
13593
|
reason: "npm_script_resolved"
|
|
12996
13594
|
};
|
|
12997
13595
|
}
|
|
12998
|
-
function
|
|
13596
|
+
function parseMakefileRecipeContent(content) {
|
|
12999
13597
|
const targets = /* @__PURE__ */ new Map();
|
|
13000
13598
|
try {
|
|
13001
|
-
const content = readFileSync5(makefilePath, "utf8");
|
|
13002
13599
|
const lines = content.split("\n");
|
|
13003
13600
|
let currentTarget = null;
|
|
13004
13601
|
let recipeLines = [];
|
|
@@ -13057,78 +13654,96 @@ function parseMakefileRecipes(makefilePath) {
|
|
|
13057
13654
|
}
|
|
13058
13655
|
return targets;
|
|
13059
13656
|
}
|
|
13060
|
-
function resolveMakeRecipe(cwd, repoRoot, target) {
|
|
13657
|
+
function resolveMakeRecipe(cwd, repoRoot, target, cliVars = {}) {
|
|
13061
13658
|
const candidates = ["Makefile", "makefile", "GNUmakefile"];
|
|
13062
13659
|
let makefilePath = null;
|
|
13063
|
-
let searchDir =
|
|
13064
|
-
const stop =
|
|
13660
|
+
let searchDir = path39.resolve(cwd);
|
|
13661
|
+
const stop = path39.resolve(repoRoot);
|
|
13065
13662
|
while (true) {
|
|
13066
13663
|
for (const name of candidates) {
|
|
13067
|
-
const candidate =
|
|
13664
|
+
const candidate = path39.join(searchDir, name);
|
|
13068
13665
|
if (existsSync11(candidate)) {
|
|
13069
13666
|
makefilePath = candidate;
|
|
13070
13667
|
break;
|
|
13071
13668
|
}
|
|
13072
13669
|
}
|
|
13073
|
-
if (makefilePath || searchDir === stop || searchDir ===
|
|
13670
|
+
if (makefilePath || searchDir === stop || searchDir === path39.dirname(searchDir)) {
|
|
13074
13671
|
break;
|
|
13075
13672
|
}
|
|
13076
|
-
searchDir =
|
|
13673
|
+
searchDir = path39.dirname(searchDir);
|
|
13077
13674
|
}
|
|
13078
13675
|
if (!makefilePath) {
|
|
13079
13676
|
return { recipes: [], opaque: true, reason: "unknown_local_effect" };
|
|
13080
13677
|
}
|
|
13081
|
-
const
|
|
13678
|
+
const makefileContent = readFileSync5(makefilePath, "utf8");
|
|
13679
|
+
const makefileVars = parseMakefileVariables(makefileContent);
|
|
13680
|
+
const targets = parseMakefileRecipeContent(makefileContent);
|
|
13082
13681
|
if (!targets.has(target)) {
|
|
13083
13682
|
return { recipes: [], opaque: true, reason: "make_target_undefined" };
|
|
13084
13683
|
}
|
|
13085
13684
|
const recipeLines = [];
|
|
13086
13685
|
const visiting = /* @__PURE__ */ new Set();
|
|
13087
13686
|
const visited = /* @__PURE__ */ new Set();
|
|
13088
|
-
let
|
|
13687
|
+
let hasDynamicPrerequisite = false;
|
|
13688
|
+
let hasUndefinedPrerequisite = false;
|
|
13689
|
+
let hasDependencyCycle = false;
|
|
13089
13690
|
const collect = (name) => {
|
|
13090
13691
|
if (visited.has(name)) {
|
|
13091
|
-
return
|
|
13692
|
+
return;
|
|
13092
13693
|
}
|
|
13093
13694
|
if (visiting.has(name)) {
|
|
13094
|
-
|
|
13695
|
+
hasDependencyCycle = true;
|
|
13696
|
+
return;
|
|
13095
13697
|
}
|
|
13096
13698
|
const entry = targets.get(name);
|
|
13097
13699
|
if (!entry) {
|
|
13098
|
-
|
|
13700
|
+
if (!existsSync11(path39.resolve(path39.dirname(makefilePath), name))) {
|
|
13701
|
+
hasUndefinedPrerequisite = true;
|
|
13702
|
+
}
|
|
13703
|
+
return;
|
|
13099
13704
|
}
|
|
13100
13705
|
visiting.add(name);
|
|
13101
|
-
|
|
13706
|
+
hasDynamicPrerequisite ||= entry.opaquePrerequisites;
|
|
13102
13707
|
for (const prerequisite of entry.prerequisites) {
|
|
13103
|
-
|
|
13104
|
-
return false;
|
|
13105
|
-
}
|
|
13708
|
+
collect(prerequisite);
|
|
13106
13709
|
}
|
|
13107
13710
|
recipeLines.push(...entry.recipes);
|
|
13108
13711
|
visiting.delete(name);
|
|
13109
13712
|
visited.add(name);
|
|
13110
|
-
return true;
|
|
13111
13713
|
};
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
}
|
|
13714
|
+
collect(target);
|
|
13715
|
+
const expandedRecipes = [];
|
|
13115
13716
|
for (const line of recipeLines) {
|
|
13116
|
-
|
|
13717
|
+
const normalized = normalizeMakeRecipeLine(line);
|
|
13718
|
+
const expanded = expandMakeExpression(normalized, cliVars, makefileVars);
|
|
13719
|
+
if (expanded === null) {
|
|
13117
13720
|
return { recipes: recipeLines, opaque: true, reason: "make_recipe_dynamic" };
|
|
13118
13721
|
}
|
|
13722
|
+
expandedRecipes.push(expanded);
|
|
13723
|
+
}
|
|
13724
|
+
for (const line of expandedRecipes) {
|
|
13725
|
+
if (/\$\(/.test(line) || /\$\{/.test(line)) {
|
|
13726
|
+
return { recipes: expandedRecipes, opaque: true, reason: "make_recipe_dynamic" };
|
|
13727
|
+
}
|
|
13119
13728
|
}
|
|
13120
|
-
if (
|
|
13121
|
-
return { recipes:
|
|
13729
|
+
if (hasDependencyCycle) {
|
|
13730
|
+
return { recipes: expandedRecipes, opaque: true, reason: "make_dependency_cycle" };
|
|
13122
13731
|
}
|
|
13123
|
-
|
|
13732
|
+
if (hasDynamicPrerequisite) {
|
|
13733
|
+
return { recipes: expandedRecipes, opaque: true, reason: "make_prerequisite_dynamic" };
|
|
13734
|
+
}
|
|
13735
|
+
if (hasUndefinedPrerequisite) {
|
|
13736
|
+
return { recipes: expandedRecipes, opaque: true, reason: "make_prerequisite_undefined" };
|
|
13737
|
+
}
|
|
13738
|
+
return { recipes: expandedRecipes, opaque: false, reason: "make_recipe_resolved" };
|
|
13124
13739
|
}
|
|
13125
13740
|
function resolveLauncherRecipe(params) {
|
|
13126
|
-
if (params.depth >= MAX_RESOLVE_DEPTH) {
|
|
13127
|
-
return { recipes: [], opaque: true, reason: "launcher_depth_exceeded" };
|
|
13128
|
-
}
|
|
13129
13741
|
const tokens = params.tokens;
|
|
13130
13742
|
const scriptName = npmScriptName(tokens);
|
|
13131
13743
|
if (scriptName) {
|
|
13744
|
+
if (params.depth >= MAX_RESOLVE_DEPTH) {
|
|
13745
|
+
return { recipes: [], opaque: true, reason: "launcher_depth_exceeded" };
|
|
13746
|
+
}
|
|
13132
13747
|
const resolution = resolveNpmRecipe(
|
|
13133
13748
|
params.cwd,
|
|
13134
13749
|
params.repoRoot,
|
|
@@ -13144,8 +13759,31 @@ function resolveLauncherRecipe(params) {
|
|
|
13144
13759
|
}
|
|
13145
13760
|
return resolution;
|
|
13146
13761
|
}
|
|
13147
|
-
if (tokens[0] === "make"
|
|
13148
|
-
|
|
13762
|
+
if (tokens[0] === "make") {
|
|
13763
|
+
if (params.depth >= MAX_RESOLVE_DEPTH) {
|
|
13764
|
+
return { recipes: [], opaque: true, reason: "launcher_depth_exceeded" };
|
|
13765
|
+
}
|
|
13766
|
+
if (tokens.includes("-n") || tokens.includes("--dry-run")) {
|
|
13767
|
+
return null;
|
|
13768
|
+
}
|
|
13769
|
+
let target = null;
|
|
13770
|
+
const cliVars = {};
|
|
13771
|
+
for (const token of tokens.slice(1)) {
|
|
13772
|
+
if (token.startsWith("-")) {
|
|
13773
|
+
continue;
|
|
13774
|
+
}
|
|
13775
|
+
const assignment = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
13776
|
+
if (assignment) {
|
|
13777
|
+
cliVars[assignment[1] ?? ""] = assignment[2] ?? "";
|
|
13778
|
+
continue;
|
|
13779
|
+
}
|
|
13780
|
+
if (!target) {
|
|
13781
|
+
target = token;
|
|
13782
|
+
}
|
|
13783
|
+
}
|
|
13784
|
+
if (target) {
|
|
13785
|
+
return resolveMakeRecipe(params.cwd, params.repoRoot, target, cliVars);
|
|
13786
|
+
}
|
|
13149
13787
|
}
|
|
13150
13788
|
if (tokens[0] === "pnpm" && tokens[1] === "exec" && tokens[2]) {
|
|
13151
13789
|
return {
|
|
@@ -13158,7 +13796,7 @@ function resolveLauncherRecipe(params) {
|
|
|
13158
13796
|
}
|
|
13159
13797
|
|
|
13160
13798
|
// src/core/verdict/parser.ts
|
|
13161
|
-
import
|
|
13799
|
+
import path40 from "node:path";
|
|
13162
13800
|
|
|
13163
13801
|
// src/core/shell-substitution.ts
|
|
13164
13802
|
function findStructuralCommandSubstitutions(command) {
|
|
@@ -13383,9 +14021,8 @@ function hasUnbalancedDollarParen(command) {
|
|
|
13383
14021
|
// src/core/verdict/parser.ts
|
|
13384
14022
|
var ENV_PREFIX_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|"[^"]*"|\S+)$/;
|
|
13385
14023
|
var MAX_WRAPPER_PEEL_DEPTH = 32;
|
|
13386
|
-
var
|
|
14024
|
+
var SHELL_INTERPRETERS2 = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish"]);
|
|
13387
14025
|
var CODE_INTERPRETERS = /* @__PURE__ */ new Set(["python", "python3", "node", "ruby", "perl", "osascript"]);
|
|
13388
|
-
var SCRIPT_FLAGS = /* @__PURE__ */ new Set(["-c", "-lc", "-e", "--eval"]);
|
|
13389
14026
|
var INTERPRETER_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
13390
14027
|
".js",
|
|
13391
14028
|
".mjs",
|
|
@@ -13397,7 +14034,7 @@ var INTERPRETER_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
13397
14034
|
".sh"
|
|
13398
14035
|
]);
|
|
13399
14036
|
function normalizeHead(token) {
|
|
13400
|
-
const base =
|
|
14037
|
+
const base = path40.basename(token);
|
|
13401
14038
|
if (base && base !== "." && base !== "..") {
|
|
13402
14039
|
return base;
|
|
13403
14040
|
}
|
|
@@ -13409,10 +14046,6 @@ function peelTransparentWrappers(tokens) {
|
|
|
13409
14046
|
let encounteredXargs = false;
|
|
13410
14047
|
let peelDepth = 0;
|
|
13411
14048
|
while (current.length > 0) {
|
|
13412
|
-
if (peelDepth >= MAX_WRAPPER_PEEL_DEPTH) {
|
|
13413
|
-
return { tokens: current, xargsStdinOpaque: false, encounteredXargs, opaque: true };
|
|
13414
|
-
}
|
|
13415
|
-
peelDepth += 1;
|
|
13416
14049
|
while (current.length > 0 && ENV_PREFIX_PATTERN.test(current[0] ?? "")) {
|
|
13417
14050
|
current.shift();
|
|
13418
14051
|
}
|
|
@@ -13422,6 +14055,10 @@ function peelTransparentWrappers(tokens) {
|
|
|
13422
14055
|
const head = normalizeHead(current[0] ?? "");
|
|
13423
14056
|
if (head === "xargs") {
|
|
13424
14057
|
encounteredXargs = true;
|
|
14058
|
+
if (peelDepth >= MAX_WRAPPER_PEEL_DEPTH) {
|
|
14059
|
+
return { tokens: current, xargsStdinOpaque: false, encounteredXargs, opaque: true };
|
|
14060
|
+
}
|
|
14061
|
+
peelDepth += 1;
|
|
13425
14062
|
const wrapper2 = peelXargsWrapper(current);
|
|
13426
14063
|
if (wrapper2.kind === "opaque") {
|
|
13427
14064
|
xargsStdinOpaque = current.length === 1;
|
|
@@ -13439,6 +14076,10 @@ function peelTransparentWrappers(tokens) {
|
|
|
13439
14076
|
if (!wrapper) {
|
|
13440
14077
|
break;
|
|
13441
14078
|
}
|
|
14079
|
+
if (peelDepth >= MAX_WRAPPER_PEEL_DEPTH) {
|
|
14080
|
+
return { tokens: current, xargsStdinOpaque: false, encounteredXargs, opaque: true };
|
|
14081
|
+
}
|
|
14082
|
+
peelDepth += 1;
|
|
13442
14083
|
if (wrapper.kind === "opaque") {
|
|
13443
14084
|
return { tokens: current, xargsStdinOpaque: false, encounteredXargs, opaque: true };
|
|
13444
14085
|
}
|
|
@@ -13662,34 +14303,20 @@ function extractRecursiveScript(tokens) {
|
|
|
13662
14303
|
return null;
|
|
13663
14304
|
}
|
|
13664
14305
|
const head = normalizeHead(filtered[0] ?? "");
|
|
13665
|
-
const second = filtered[1] ?? "";
|
|
13666
14306
|
if (head === "eval") {
|
|
13667
14307
|
const body = filtered.slice(1).join(" ").trim();
|
|
13668
14308
|
return body || null;
|
|
13669
14309
|
}
|
|
13670
|
-
|
|
13671
|
-
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
return body || null;
|
|
13675
|
-
}
|
|
13676
|
-
}
|
|
13677
|
-
if (head === "bash" && (second === "-lc" || second === "-c")) {
|
|
13678
|
-
const body = filtered.slice(2).join(" ").replace(/^['"]|['"]$/g, "").trim();
|
|
13679
|
-
return body || null;
|
|
13680
|
-
}
|
|
13681
|
-
return null;
|
|
14310
|
+
const invocation = decodeRecursiveInvocation(
|
|
14311
|
+
shellTokensFromValues(filtered, { detectExpansion: false })
|
|
14312
|
+
);
|
|
14313
|
+
return invocation.kind === "static" ? invocation.script || null : null;
|
|
13682
14314
|
}
|
|
13683
|
-
function
|
|
13684
|
-
const
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
const head = normalizeHead(filtered[0] ?? "");
|
|
13689
|
-
if (head === "eval") {
|
|
13690
|
-
return true;
|
|
13691
|
-
}
|
|
13692
|
-
return (SHELL_INTERPRETERS.has(head) || CODE_INTERPRETERS.has(head)) && filtered.some((token) => SCRIPT_FLAGS.has(token));
|
|
14315
|
+
function decodeRecursiveInvocationTokens(tokens) {
|
|
14316
|
+
const values = tokens.map((token) => token.value);
|
|
14317
|
+
const { tokens: filtered, opaque } = peelTransparentWrappers(values);
|
|
14318
|
+
if (opaque) return { kind: "none" };
|
|
14319
|
+
return decodeRecursiveInvocation(tokens.slice(values.length - filtered.length));
|
|
13693
14320
|
}
|
|
13694
14321
|
function isCommandInspection(tokens) {
|
|
13695
14322
|
return normalizeHead(tokens[0] ?? "") === "command" && peelCommandWrapper(tokens).kind === "preserve";
|
|
@@ -13703,11 +14330,10 @@ function isBareInterpreter(tokens) {
|
|
|
13703
14330
|
return false;
|
|
13704
14331
|
}
|
|
13705
14332
|
const head = normalizeHead(peeled[0] ?? "");
|
|
13706
|
-
if (!
|
|
14333
|
+
if (!SHELL_INTERPRETERS2.has(head) && !CODE_INTERPRETERS.has(head)) {
|
|
13707
14334
|
return false;
|
|
13708
14335
|
}
|
|
13709
|
-
|
|
13710
|
-
if (hasScriptFlag) {
|
|
14336
|
+
if (decodeRecursiveInvocation(shellTokensFromValues(peeled)).kind !== "none") {
|
|
13711
14337
|
return false;
|
|
13712
14338
|
}
|
|
13713
14339
|
const args = peeled.slice(1);
|
|
@@ -13718,7 +14344,7 @@ function isBareInterpreter(tokens) {
|
|
|
13718
14344
|
return false;
|
|
13719
14345
|
}
|
|
13720
14346
|
const scriptArg = args.find((token) => !token.startsWith("-"));
|
|
13721
|
-
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(
|
|
14347
|
+
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(path40.extname(scriptArg))) {
|
|
13722
14348
|
return false;
|
|
13723
14349
|
}
|
|
13724
14350
|
if (scriptArg) {
|
|
@@ -14010,11 +14636,11 @@ function lowerTopLevelSegments(command, context) {
|
|
|
14010
14636
|
}
|
|
14011
14637
|
function startsLocalPostgresService(command) {
|
|
14012
14638
|
const tokens = tokenizeShell(command);
|
|
14013
|
-
return
|
|
14639
|
+
return path41.basename(tokens[0] ?? "") === "docker" && tokens[1] === "compose" && ["up", "start", "restart"].includes(tokens[2] ?? "") && tokens.includes("postgres");
|
|
14014
14640
|
}
|
|
14015
14641
|
function resolveCdTransition(command, currentCwd) {
|
|
14016
14642
|
const tokens = tokenizeShell(command);
|
|
14017
|
-
if (
|
|
14643
|
+
if (path41.basename(tokens[0] ?? "") !== "cd") {
|
|
14018
14644
|
return null;
|
|
14019
14645
|
}
|
|
14020
14646
|
const target = tokens[1] ?? "~";
|
|
@@ -14035,17 +14661,32 @@ function joinNestedOpacity(outer, nested) {
|
|
|
14035
14661
|
}
|
|
14036
14662
|
function lowerSegment(command, context) {
|
|
14037
14663
|
const commandRedacted = redactCommand(command);
|
|
14038
|
-
const
|
|
14664
|
+
const lexed = lexShell(command);
|
|
14665
|
+
const rawTokens = lexed.tokens.map((token) => token.value);
|
|
14039
14666
|
const environment = extractEnvironment(rawTokens, context.env);
|
|
14040
14667
|
const env = environment.env;
|
|
14041
14668
|
const parsed = parseSegment(command);
|
|
14042
|
-
const
|
|
14043
|
-
|
|
14669
|
+
const parsedTokens = environment.commandTokens ?? parsed.tokens;
|
|
14670
|
+
const tokens = stripRedirects(
|
|
14671
|
+
parsedTokens.length === 0 && rawTokens.length > 0 && rawTokens.every((token) => ENV_PREFIX_PATTERN2.test(token)) ? rawTokens : parsedTokens
|
|
14672
|
+
).map((token) => expandKnownVariables(token, env));
|
|
14673
|
+
const decoderTokens = alignStructuredTokens(
|
|
14674
|
+
stripStructuredRedirects(lexed.tokens),
|
|
14675
|
+
stripRedirects(parsedTokens)
|
|
14044
14676
|
);
|
|
14045
|
-
const head =
|
|
14677
|
+
const head = path41.basename(tokens[0] ?? parsed.head);
|
|
14046
14678
|
let opacity = segmentOpacity(command);
|
|
14047
14679
|
const signals = /* @__PURE__ */ new Set();
|
|
14048
14680
|
const requirements = [];
|
|
14681
|
+
if (!lexed.complete) {
|
|
14682
|
+
requirements.push(
|
|
14683
|
+
requirement2("indeterminate", "indeterminate", { kind: "unknown" }, commandRedacted, [
|
|
14684
|
+
"shell.grammar_incomplete"
|
|
14685
|
+
])
|
|
14686
|
+
);
|
|
14687
|
+
signals.add("shell.grammar_incomplete");
|
|
14688
|
+
opacity = joinEffectOpacity(opacity, "unparseable");
|
|
14689
|
+
}
|
|
14049
14690
|
addRedirectEffects(requirements, rawTokens, env, context, commandRedacted);
|
|
14050
14691
|
addSubstitutionEffects(requirements, command, context, commandRedacted, signals);
|
|
14051
14692
|
if (environment.malformed) {
|
|
@@ -14065,7 +14706,7 @@ function lowerSegment(command, context) {
|
|
|
14065
14706
|
signals.add("shell.xargs_stdin_dynamic");
|
|
14066
14707
|
opacity = joinEffectOpacity(opacity, "opaque");
|
|
14067
14708
|
}
|
|
14068
|
-
if (context.depth
|
|
14709
|
+
if (context.depth > MAX_LOWER_DEPTH) {
|
|
14069
14710
|
requirements.push(
|
|
14070
14711
|
requirement2("indeterminate", "indeterminate", { kind: "unknown" }, commandRedacted, [
|
|
14071
14712
|
"shell.lower_depth_exceeded"
|
|
@@ -14112,37 +14753,96 @@ function lowerSegment(command, context) {
|
|
|
14112
14753
|
}
|
|
14113
14754
|
return shellSegment(commandRedacted, head, requirements, opacity, signals);
|
|
14114
14755
|
}
|
|
14115
|
-
const
|
|
14116
|
-
if (
|
|
14117
|
-
const dynamicEvaluation = isDynamicRecursiveEvaluation(tokens);
|
|
14756
|
+
const recursive = decodeRecursiveInvocationTokens(decoderTokens);
|
|
14757
|
+
if (recursive.kind === "static" && opacity !== "opaque" && opacity !== "unparseable") {
|
|
14118
14758
|
requirements.push(
|
|
14119
|
-
processRequirement(
|
|
14759
|
+
processRequirement(recursive.interpreter, "spawn", commandRedacted, [
|
|
14120
14760
|
"shell.recursive_wrapper",
|
|
14121
|
-
|
|
14761
|
+
"dynamic_shell_evaluation"
|
|
14122
14762
|
])
|
|
14123
14763
|
);
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
(
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
signals.add(signal);
|
|
14764
|
+
if (recursive.script !== "") {
|
|
14765
|
+
const nested = lowerTopLevelSegments(recursive.script, {
|
|
14766
|
+
...context,
|
|
14767
|
+
command: recursive.script,
|
|
14768
|
+
env,
|
|
14769
|
+
depth: context.depth + 1
|
|
14770
|
+
});
|
|
14771
|
+
for (const nestedSegment of nested) {
|
|
14772
|
+
requirements.push(
|
|
14773
|
+
...nestedSegment.requirements.map(
|
|
14774
|
+
(entry) => withInnerProvenance(entry, recursive.script, head, commandRedacted)
|
|
14775
|
+
)
|
|
14776
|
+
);
|
|
14777
|
+
for (const signal of nestedSegment.signals) signals.add(signal);
|
|
14138
14778
|
}
|
|
14139
14779
|
}
|
|
14140
14780
|
signals.add("shell.recursive_wrapper");
|
|
14141
|
-
|
|
14142
|
-
|
|
14781
|
+
signals.add("dynamic_shell_evaluation");
|
|
14782
|
+
return shellSegment(commandRedacted, head, requirements, "recursive", signals);
|
|
14783
|
+
}
|
|
14784
|
+
if (recursive.kind === "dynamic" || recursive.kind === "indeterminate") {
|
|
14785
|
+
const recursiveSignals = [
|
|
14786
|
+
recursive.signal,
|
|
14787
|
+
...recursive.kind === "dynamic" ? ["dynamic_shell_evaluation"] : []
|
|
14788
|
+
];
|
|
14789
|
+
requirements.push(
|
|
14790
|
+
processRequirement(recursive.interpreter, "spawn", commandRedacted, recursiveSignals),
|
|
14791
|
+
requirement2("indeterminate", "indeterminate", { kind: "unknown" }, commandRedacted, [
|
|
14792
|
+
...recursiveSignals
|
|
14793
|
+
])
|
|
14794
|
+
);
|
|
14795
|
+
for (const signal of recursiveSignals) signals.add(signal);
|
|
14796
|
+
return shellSegment(
|
|
14797
|
+
commandRedacted,
|
|
14798
|
+
head,
|
|
14799
|
+
requirements,
|
|
14800
|
+
joinEffectOpacity(opacity, "opaque"),
|
|
14801
|
+
signals
|
|
14802
|
+
);
|
|
14803
|
+
}
|
|
14804
|
+
const compose = decodeDockerComposeRun(decoderTokens);
|
|
14805
|
+
if (compose.kind === "recursive" && opacity !== "opaque" && opacity !== "unparseable") {
|
|
14806
|
+
requirements.push(
|
|
14807
|
+
processRequirement(head, "spawn", commandRedacted, ["process.docker_compose_run"])
|
|
14808
|
+
);
|
|
14809
|
+
if (compose.script !== "") {
|
|
14810
|
+
const nested = lowerTopLevelSegments(compose.script, {
|
|
14811
|
+
...context,
|
|
14812
|
+
command: compose.script,
|
|
14813
|
+
env,
|
|
14814
|
+
depth: context.depth + 1
|
|
14815
|
+
});
|
|
14816
|
+
for (const nestedSegment of nested) {
|
|
14817
|
+
requirements.push(
|
|
14818
|
+
...nestedSegment.requirements.map(
|
|
14819
|
+
(entry) => withInnerProvenance(entry, compose.script, head, commandRedacted)
|
|
14820
|
+
)
|
|
14821
|
+
);
|
|
14822
|
+
for (const signal of nestedSegment.signals) signals.add(signal);
|
|
14823
|
+
opacity = joinNestedOpacity(opacity, nestedSegment);
|
|
14824
|
+
}
|
|
14143
14825
|
}
|
|
14826
|
+
signals.add("process.docker_compose_run");
|
|
14144
14827
|
return shellSegment(commandRedacted, head, requirements, "recursive", signals);
|
|
14145
14828
|
}
|
|
14829
|
+
if (compose.kind === "dynamic" || compose.kind === "indeterminate") {
|
|
14830
|
+
requirements.push(
|
|
14831
|
+
processRequirement(head, "spawn", commandRedacted, ["process.docker_compose_run"]),
|
|
14832
|
+
requirement2("indeterminate", "indeterminate", { kind: "unknown" }, commandRedacted, [
|
|
14833
|
+
compose.signal
|
|
14834
|
+
])
|
|
14835
|
+
);
|
|
14836
|
+
signals.add("process.docker_compose_run");
|
|
14837
|
+
signals.add(compose.signal);
|
|
14838
|
+
return shellSegment(
|
|
14839
|
+
commandRedacted,
|
|
14840
|
+
head,
|
|
14841
|
+
requirements,
|
|
14842
|
+
joinEffectOpacity(opacity, "opaque"),
|
|
14843
|
+
signals
|
|
14844
|
+
);
|
|
14845
|
+
}
|
|
14146
14846
|
const launcher = resolveLauncherRecipe({
|
|
14147
14847
|
tokens,
|
|
14148
14848
|
cwd: context.cwd,
|
|
@@ -14354,7 +15054,7 @@ function isMetadataOnlyArgv(argv) {
|
|
|
14354
15054
|
return argv.length > 0 && argv.every((token) => METADATA_ONLY_FLAGS.has(token));
|
|
14355
15055
|
}
|
|
14356
15056
|
function executableBaseName(head) {
|
|
14357
|
-
return
|
|
15057
|
+
return path41.basename(head);
|
|
14358
15058
|
}
|
|
14359
15059
|
var RAILS_READ_ONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["routes", "middleware", "stats", "about", "version"]);
|
|
14360
15060
|
function railsReadOnlySubcommand(args) {
|
|
@@ -14364,6 +15064,111 @@ function railsReadOnlySubcommand(args) {
|
|
|
14364
15064
|
}
|
|
14365
15065
|
return RAILS_READ_ONLY_SUBCOMMANDS.has(subcommand);
|
|
14366
15066
|
}
|
|
15067
|
+
function isRubyTestScript(scriptPath) {
|
|
15068
|
+
const base = path41.basename(scriptPath);
|
|
15069
|
+
return base.endsWith("_test.rb") || base.endsWith("_spec.rb");
|
|
15070
|
+
}
|
|
15071
|
+
function parseRubyTestInvocation(args) {
|
|
15072
|
+
const includePaths = [];
|
|
15073
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
15074
|
+
const arg = args[index] ?? "";
|
|
15075
|
+
if (arg === "-e" || arg === "-r") {
|
|
15076
|
+
return null;
|
|
15077
|
+
}
|
|
15078
|
+
if (arg === "-I") {
|
|
15079
|
+
const includePath = args[index + 1];
|
|
15080
|
+
if (!includePath) {
|
|
15081
|
+
return null;
|
|
15082
|
+
}
|
|
15083
|
+
includePaths.push(includePath);
|
|
15084
|
+
index += 1;
|
|
15085
|
+
continue;
|
|
15086
|
+
}
|
|
15087
|
+
if (arg.startsWith("-I") && arg.length > 2) {
|
|
15088
|
+
includePaths.push(arg.slice(2));
|
|
15089
|
+
continue;
|
|
15090
|
+
}
|
|
15091
|
+
if (arg.startsWith("-")) {
|
|
15092
|
+
if (arg === "-n") {
|
|
15093
|
+
if (!args[index + 1]) {
|
|
15094
|
+
return null;
|
|
15095
|
+
}
|
|
15096
|
+
index += 1;
|
|
15097
|
+
continue;
|
|
15098
|
+
}
|
|
15099
|
+
if (arg.startsWith("-n")) {
|
|
15100
|
+
continue;
|
|
15101
|
+
}
|
|
15102
|
+
return null;
|
|
15103
|
+
}
|
|
15104
|
+
if (isRubyTestScript(arg)) {
|
|
15105
|
+
return { includePaths, scriptPath: arg };
|
|
15106
|
+
}
|
|
15107
|
+
return null;
|
|
15108
|
+
}
|
|
15109
|
+
return null;
|
|
15110
|
+
}
|
|
15111
|
+
function isRubocopMutating(args) {
|
|
15112
|
+
return args.some(
|
|
15113
|
+
(arg) => arg === "-A" || arg === "-a" || arg === "--auto-correct" || arg === "--autocorrect" || arg.startsWith("--auto-correct-all") || arg.startsWith("--autocorrect-all")
|
|
15114
|
+
);
|
|
15115
|
+
}
|
|
15116
|
+
function decodeBundleExecInner(innerHead, innerArgs, segment) {
|
|
15117
|
+
const innerBase = executableBaseName(innerHead);
|
|
15118
|
+
if (innerBase === "rubocop") {
|
|
15119
|
+
const mutating = isRubocopMutating(innerArgs);
|
|
15120
|
+
return [
|
|
15121
|
+
processRequirement(
|
|
15122
|
+
innerHead,
|
|
15123
|
+
mutating ? "spawn" : "inspect",
|
|
15124
|
+
segment,
|
|
15125
|
+
mutating ? ["process.linter.mutating"] : ["process.inspect.linter"]
|
|
15126
|
+
)
|
|
15127
|
+
];
|
|
15128
|
+
}
|
|
15129
|
+
if (innerBase === "rspec") {
|
|
15130
|
+
const targetArgs = innerArgs.filter((arg) => !arg.startsWith("-"));
|
|
15131
|
+
if (targetArgs.length === 0) {
|
|
15132
|
+
return null;
|
|
15133
|
+
}
|
|
15134
|
+
return [processRequirement(innerHead, "spawn", segment, ["process.test_runner.rspec"])];
|
|
15135
|
+
}
|
|
15136
|
+
return null;
|
|
15137
|
+
}
|
|
15138
|
+
function decodeRuby(args, cwd, repoRoot, segment) {
|
|
15139
|
+
const parsed = parseRubyTestInvocation(args);
|
|
15140
|
+
if (!parsed) {
|
|
15141
|
+
return unsupportedProcess("ruby", segment, "process.ruby_grammar_incomplete");
|
|
15142
|
+
}
|
|
15143
|
+
const scriptPath = resolvePathOperand(parsed.scriptPath, cwd);
|
|
15144
|
+
if (!pathWithinRoot(canonicalPath(repoRoot), canonicalPath(scriptPath))) {
|
|
15145
|
+
return unsupportedProcess("ruby", segment, "process.ruby_outside_repo");
|
|
15146
|
+
}
|
|
15147
|
+
for (const includePath of parsed.includePaths) {
|
|
15148
|
+
const resolvedInclude = resolvePathOperand(includePath, cwd);
|
|
15149
|
+
if (!pathWithinRoot(canonicalPath(repoRoot), canonicalPath(resolvedInclude))) {
|
|
15150
|
+
return unsupportedProcess("ruby", segment, "process.ruby_outside_repo");
|
|
15151
|
+
}
|
|
15152
|
+
}
|
|
15153
|
+
const lowered = [
|
|
15154
|
+
processRequirement("ruby", "spawn", segment, ["process.test_runner.minitest"]),
|
|
15155
|
+
requirement2("fs.read", "fs.read", { kind: "path", path: scriptPath }, segment, [
|
|
15156
|
+
"ruby.minitest_script_read"
|
|
15157
|
+
])
|
|
15158
|
+
];
|
|
15159
|
+
for (const includePath of parsed.includePaths) {
|
|
15160
|
+
lowered.push(
|
|
15161
|
+
requirement2(
|
|
15162
|
+
"fs.read",
|
|
15163
|
+
"fs.read",
|
|
15164
|
+
{ kind: "path", path: resolvePathOperand(includePath, cwd) },
|
|
15165
|
+
segment,
|
|
15166
|
+
["ruby.minitest_load_path_read"]
|
|
15167
|
+
)
|
|
15168
|
+
);
|
|
15169
|
+
}
|
|
15170
|
+
return lowered;
|
|
15171
|
+
}
|
|
14367
15172
|
function decodeRuntimeMetadataProcess(head, args, segment) {
|
|
14368
15173
|
if (head === "bundle") {
|
|
14369
15174
|
if (args.length === 1 && isMetadataOnlyArgv(args)) {
|
|
@@ -14385,6 +15190,10 @@ function decodeRuntimeMetadataProcess(head, args, segment) {
|
|
|
14385
15190
|
])
|
|
14386
15191
|
];
|
|
14387
15192
|
}
|
|
15193
|
+
const bundleExecInner = decodeBundleExecInner(innerHead, innerArgs, segment);
|
|
15194
|
+
if (bundleExecInner) {
|
|
15195
|
+
return bundleExecInner;
|
|
15196
|
+
}
|
|
14388
15197
|
}
|
|
14389
15198
|
return null;
|
|
14390
15199
|
}
|
|
@@ -14400,9 +15209,74 @@ function decodeRuntimeMetadataProcess(head, args, segment) {
|
|
|
14400
15209
|
}
|
|
14401
15210
|
return null;
|
|
14402
15211
|
}
|
|
15212
|
+
function decodeSetBuiltin(args) {
|
|
15213
|
+
let index = 0;
|
|
15214
|
+
while (index < args.length) {
|
|
15215
|
+
const arg = args[index] ?? "";
|
|
15216
|
+
if (arg === "--") {
|
|
15217
|
+
index += 1;
|
|
15218
|
+
continue;
|
|
15219
|
+
}
|
|
15220
|
+
if (arg === "-o" || arg === "+o") {
|
|
15221
|
+
if (!args[index + 1]) {
|
|
15222
|
+
return false;
|
|
15223
|
+
}
|
|
15224
|
+
index += 2;
|
|
15225
|
+
continue;
|
|
15226
|
+
}
|
|
15227
|
+
if (/^[-+][A-Za-z0-9]+$/.test(arg)) {
|
|
15228
|
+
index += 1;
|
|
15229
|
+
continue;
|
|
15230
|
+
}
|
|
15231
|
+
return false;
|
|
15232
|
+
}
|
|
15233
|
+
return true;
|
|
15234
|
+
}
|
|
15235
|
+
function decodeShellControlBuiltin(head, args) {
|
|
15236
|
+
if (head === "set") {
|
|
15237
|
+
return decodeSetBuiltin(args) ? [] : null;
|
|
15238
|
+
}
|
|
15239
|
+
if (head === "wait") {
|
|
15240
|
+
if (args.length === 0 || args.every((arg) => /^\d+$/.test(arg))) {
|
|
15241
|
+
return [];
|
|
15242
|
+
}
|
|
15243
|
+
return null;
|
|
15244
|
+
}
|
|
15245
|
+
if (head === "exit") {
|
|
15246
|
+
if (args.length === 0 || args.length === 1 && /^-?\d+$/.test(args[0] ?? "")) {
|
|
15247
|
+
return [];
|
|
15248
|
+
}
|
|
15249
|
+
return null;
|
|
15250
|
+
}
|
|
15251
|
+
return null;
|
|
15252
|
+
}
|
|
15253
|
+
function decodeDockerComposeRun2(head, args, segment) {
|
|
15254
|
+
let composeArgs = null;
|
|
15255
|
+
let command = head;
|
|
15256
|
+
if (head === "docker-compose") {
|
|
15257
|
+
composeArgs = args;
|
|
15258
|
+
} else if (head === "docker" && args[0] === "compose") {
|
|
15259
|
+
composeArgs = args.slice(1);
|
|
15260
|
+
command = "docker";
|
|
15261
|
+
}
|
|
15262
|
+
if (!composeArgs) {
|
|
15263
|
+
return null;
|
|
15264
|
+
}
|
|
15265
|
+
if (composeArgs.includes("run")) {
|
|
15266
|
+
return [processRequirement(command, "spawn", segment, ["process.docker_compose_run"])];
|
|
15267
|
+
}
|
|
15268
|
+
return unsupportedProcess(command, segment, "process.docker_compose_grammar_incomplete");
|
|
15269
|
+
}
|
|
14403
15270
|
function decodeProcessOrFilesystem(params) {
|
|
14404
15271
|
const { tokens, head, env, cwd, repoRoot, segment } = params;
|
|
14405
15272
|
const args = tokens.slice(1);
|
|
15273
|
+
if (tokens.length > 0 && tokens.every((token) => ENV_PREFIX_PATTERN2.test(token))) {
|
|
15274
|
+
return [];
|
|
15275
|
+
}
|
|
15276
|
+
const shellControl = decodeShellControlBuiltin(head, args);
|
|
15277
|
+
if (shellControl) {
|
|
15278
|
+
return shellControl;
|
|
15279
|
+
}
|
|
14406
15280
|
if (isCommandInspection(tokens)) {
|
|
14407
15281
|
return [processRequirement(head, "inspect", segment, ["process.inspect.command_lookup"])];
|
|
14408
15282
|
}
|
|
@@ -14470,7 +15344,7 @@ function decodeProcessOrFilesystem(params) {
|
|
|
14470
15344
|
requirement2(
|
|
14471
15345
|
"fs.read",
|
|
14472
15346
|
"fs.read",
|
|
14473
|
-
{ kind: "path", path:
|
|
15347
|
+
{ kind: "path", path: path41.resolve(cwd, syntax) },
|
|
14474
15348
|
segment,
|
|
14475
15349
|
["shell.syntax_source_read"]
|
|
14476
15350
|
)
|
|
@@ -14485,6 +15359,19 @@ function decodeProcessOrFilesystem(params) {
|
|
|
14485
15359
|
if (runtimeMetadata) {
|
|
14486
15360
|
return runtimeMetadata;
|
|
14487
15361
|
}
|
|
15362
|
+
if (head === "ruby") {
|
|
15363
|
+
return decodeRuby(args, cwd, repoRoot, segment);
|
|
15364
|
+
}
|
|
15365
|
+
if (head === "rubocop" || head === "rspec") {
|
|
15366
|
+
const decoded = decodeBundleExecInner(head, args, segment);
|
|
15367
|
+
if (decoded) {
|
|
15368
|
+
return decoded;
|
|
15369
|
+
}
|
|
15370
|
+
}
|
|
15371
|
+
const dockerCompose = decodeDockerComposeRun2(head, args, segment);
|
|
15372
|
+
if (dockerCompose) {
|
|
15373
|
+
return dockerCompose;
|
|
15374
|
+
}
|
|
14488
15375
|
if ((head === "npm" || head === "pnpm") && args.length === 1 && isMetadataOnlyArgv(args)) {
|
|
14489
15376
|
return [processRequirement(head, "inspect", segment, ["process.inspect.package_manager"])];
|
|
14490
15377
|
}
|
|
@@ -14507,7 +15394,7 @@ function decodeProcessOrFilesystem(params) {
|
|
|
14507
15394
|
return [processRequirement(head, "inspect", segment, ["process.inspect.base64_stdin"])];
|
|
14508
15395
|
}
|
|
14509
15396
|
if (head === "node") {
|
|
14510
|
-
return
|
|
15397
|
+
return decodeNode2(args, cwd, segment);
|
|
14511
15398
|
}
|
|
14512
15399
|
if (head === "vite" || head === "vite-node") {
|
|
14513
15400
|
return [processRequirement(head, "spawn", segment, ["process.local_dev_spawn"])];
|
|
@@ -14597,7 +15484,7 @@ function decodeBelay(args, repoRoot, segment) {
|
|
|
14597
15484
|
requirement2(
|
|
14598
15485
|
"control_plane.write",
|
|
14599
15486
|
"control_plane.write",
|
|
14600
|
-
{ kind: "path", path:
|
|
15487
|
+
{ kind: "path", path: path41.join(repoRoot, ".belay-control-plane") },
|
|
14601
15488
|
segment,
|
|
14602
15489
|
["belay.config_non_judge_mutation"]
|
|
14603
15490
|
)
|
|
@@ -14759,11 +15646,11 @@ function decodeRm(args, cwd, repoRoot, segment) {
|
|
|
14759
15646
|
function canonicalRmOperand(targetPath, finalOperandIsSymlink) {
|
|
14760
15647
|
try {
|
|
14761
15648
|
if (finalOperandIsSymlink) {
|
|
14762
|
-
return
|
|
15649
|
+
return path41.join(realpathSync4.native(path41.dirname(targetPath)), path41.basename(targetPath));
|
|
14763
15650
|
}
|
|
14764
15651
|
return realpathSync4.native(targetPath);
|
|
14765
15652
|
} catch {
|
|
14766
|
-
return
|
|
15653
|
+
return path41.resolve(targetPath);
|
|
14767
15654
|
}
|
|
14768
15655
|
}
|
|
14769
15656
|
function isSymbolicLink(targetPath) {
|
|
@@ -14774,8 +15661,8 @@ function isSymbolicLink(targetPath) {
|
|
|
14774
15661
|
}
|
|
14775
15662
|
}
|
|
14776
15663
|
function pathContains(ancestor, candidate) {
|
|
14777
|
-
const relative =
|
|
14778
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
15664
|
+
const relative = path41.relative(path41.resolve(ancestor), path41.resolve(candidate));
|
|
15665
|
+
return relative === "" || !relative.startsWith("..") && !path41.isAbsolute(relative);
|
|
14779
15666
|
}
|
|
14780
15667
|
function decodeGo(args, segment) {
|
|
14781
15668
|
if (["test", "list", "vet"].includes(args[0] ?? "")) {
|
|
@@ -14921,7 +15808,7 @@ function decodeSed(args, cwd, segment) {
|
|
|
14921
15808
|
}
|
|
14922
15809
|
return lowered;
|
|
14923
15810
|
}
|
|
14924
|
-
function
|
|
15811
|
+
function decodeNode2(args, cwd, segment) {
|
|
14925
15812
|
if (args.length > 0 && args.every((arg) => ["--help", "--version", "-h", "-v"].includes(arg))) {
|
|
14926
15813
|
return [processRequirement("node", "inspect", segment, ["process.inspect.node_metadata"])];
|
|
14927
15814
|
}
|
|
@@ -15187,15 +16074,36 @@ function stripRedirects(tokens) {
|
|
|
15187
16074
|
stripped.push(token);
|
|
15188
16075
|
continue;
|
|
15189
16076
|
}
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
16077
|
+
index += 1;
|
|
16078
|
+
}
|
|
16079
|
+
return stripped;
|
|
16080
|
+
}
|
|
16081
|
+
function stripStructuredRedirects(tokens) {
|
|
16082
|
+
const stripped = [];
|
|
16083
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
16084
|
+
const token = tokens[index];
|
|
16085
|
+
if (!token) continue;
|
|
16086
|
+
if (isFdDuplication(token.value)) {
|
|
16087
|
+
continue;
|
|
15195
16088
|
}
|
|
16089
|
+
if (!isRedirectOperator(token.value)) {
|
|
16090
|
+
stripped.push(token);
|
|
16091
|
+
continue;
|
|
16092
|
+
}
|
|
16093
|
+
index += 1;
|
|
15196
16094
|
}
|
|
15197
16095
|
return stripped;
|
|
15198
16096
|
}
|
|
16097
|
+
function alignStructuredTokens(tokens, values) {
|
|
16098
|
+
if (values.length === 0) return [];
|
|
16099
|
+
for (let start = tokens.length - values.length; start >= 0; start -= 1) {
|
|
16100
|
+
const candidate = tokens.slice(start);
|
|
16101
|
+
if (candidate.length === values.length && candidate.every((token, index) => token.value === values[index])) {
|
|
16102
|
+
return candidate;
|
|
16103
|
+
}
|
|
16104
|
+
}
|
|
16105
|
+
return [];
|
|
16106
|
+
}
|
|
15199
16107
|
function shellSegment(commandRedacted, segmentHead, requirements, opacity, signals) {
|
|
15200
16108
|
const normalizedRequirements = requirements.flatMap((entry) => {
|
|
15201
16109
|
const dynamicSignal = dynamicResourceSignal(entry.resource);
|
|
@@ -15595,9 +16503,9 @@ function resolvePathOperand(operand, cwd) {
|
|
|
15595
16503
|
return process.env.HOME ?? operand;
|
|
15596
16504
|
}
|
|
15597
16505
|
if (operand.startsWith("~/")) {
|
|
15598
|
-
return
|
|
16506
|
+
return path41.join(process.env.HOME ?? "~", operand.slice(2));
|
|
15599
16507
|
}
|
|
15600
|
-
return
|
|
16508
|
+
return path41.resolve(cwd, operand);
|
|
15601
16509
|
}
|
|
15602
16510
|
function isShellHead(head) {
|
|
15603
16511
|
return head === "bash" || head === "sh" || head === "zsh" || head === "dash" || head === "fish";
|
|
@@ -16121,7 +17029,7 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
|
16121
17029
|
};
|
|
16122
17030
|
}
|
|
16123
17031
|
const signals = [];
|
|
16124
|
-
const resolvedPath =
|
|
17032
|
+
const resolvedPath = path42.isAbsolute(filePath) ? filePath : path42.resolve(cwd, filePath);
|
|
16125
17033
|
const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
|
|
16126
17034
|
if (hitsProtectedRoot) {
|
|
16127
17035
|
signals.push("control_plane_path");
|
|
@@ -16723,7 +17631,7 @@ function hashDecisionConfig(config) {
|
|
|
16723
17631
|
init_fingerprint2();
|
|
16724
17632
|
|
|
16725
17633
|
// src/version.ts
|
|
16726
|
-
var PACKAGE_VERSION = "0.9.
|
|
17634
|
+
var PACKAGE_VERSION = "0.9.3";
|
|
16727
17635
|
|
|
16728
17636
|
// src/runtime-provenance.ts
|
|
16729
17637
|
function resolveRuntimeArtifactHash(artifactHash) {
|
|
@@ -16798,7 +17706,7 @@ init_path_utils();
|
|
|
16798
17706
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
16799
17707
|
import { existsSync as existsSync15 } from "node:fs";
|
|
16800
17708
|
import { mkdir as mkdir11, readdir as readdir3, readFile as readFile12, rename as rename3, rm as rm6 } from "node:fs/promises";
|
|
16801
|
-
import
|
|
17709
|
+
import path47 from "node:path";
|
|
16802
17710
|
|
|
16803
17711
|
// src/core/recovery/artifact-store.ts
|
|
16804
17712
|
init_fingerprint2();
|
|
@@ -16806,7 +17714,7 @@ init_path_utils();
|
|
|
16806
17714
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
16807
17715
|
import { existsSync as existsSync13 } from "node:fs";
|
|
16808
17716
|
import { lstat as lstat7, mkdir as mkdir10, open as open5, readdir as readdir2, readFile as readFile9, rename as rename2, rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
16809
|
-
import
|
|
17717
|
+
import path44 from "node:path";
|
|
16810
17718
|
|
|
16811
17719
|
// src/core/recovery/snapshot-node.ts
|
|
16812
17720
|
init_fingerprint2();
|
|
@@ -16826,25 +17734,25 @@ import {
|
|
|
16826
17734
|
symlink as symlink3,
|
|
16827
17735
|
writeFile as writeFile6
|
|
16828
17736
|
} from "node:fs/promises";
|
|
16829
|
-
import
|
|
17737
|
+
import path43 from "node:path";
|
|
16830
17738
|
var RECOVERY_UNSUPPORTED_FILE_KIND = "recovery_unsupported_file_kind";
|
|
16831
17739
|
function validRecoveryRelativePath(relativePath) {
|
|
16832
|
-
if (!relativePath || relativePath.includes("\0") ||
|
|
16833
|
-
const normalized =
|
|
16834
|
-
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${
|
|
17740
|
+
if (!relativePath || relativePath.includes("\0") || path43.isAbsolute(relativePath)) return false;
|
|
17741
|
+
const normalized = path43.normalize(relativePath);
|
|
17742
|
+
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${path43.sep}`);
|
|
16835
17743
|
}
|
|
16836
17744
|
async function assertRecoverySafeTarget(resourceRoot, relativePath) {
|
|
16837
17745
|
if (!validRecoveryRelativePath(relativePath)) throw new Error("recovery_path_escape");
|
|
16838
17746
|
const root = canonicalPath(resourceRoot);
|
|
16839
|
-
const target =
|
|
16840
|
-
const relative =
|
|
16841
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
17747
|
+
const target = path43.resolve(root, relativePath);
|
|
17748
|
+
const relative = path43.relative(root, target);
|
|
17749
|
+
if (relative === ".." || relative.startsWith(`..${path43.sep}`) || path43.isAbsolute(relative)) {
|
|
16842
17750
|
throw new Error("recovery_path_escape");
|
|
16843
17751
|
}
|
|
16844
17752
|
let current = root;
|
|
16845
|
-
const parentParts =
|
|
17753
|
+
const parentParts = path43.relative(root, path43.dirname(target)).split(path43.sep).filter(Boolean);
|
|
16846
17754
|
for (const part of parentParts) {
|
|
16847
|
-
current =
|
|
17755
|
+
current = path43.join(current, part);
|
|
16848
17756
|
if (!existsSync12(current)) break;
|
|
16849
17757
|
const info = await lstat6(current);
|
|
16850
17758
|
if (info.isSymbolicLink()) throw new Error("recovery_symlink_escape");
|
|
@@ -16900,7 +17808,7 @@ async function captureRecoverySnapshot(filePath, options) {
|
|
|
16900
17808
|
let blob;
|
|
16901
17809
|
if (options?.blobDir) {
|
|
16902
17810
|
await mkdir9(options.blobDir, { recursive: true, mode: 448 });
|
|
16903
|
-
const blobPath =
|
|
17811
|
+
const blobPath = path43.join(options.blobDir, hash);
|
|
16904
17812
|
if (!existsSync12(blobPath)) {
|
|
16905
17813
|
await writeFile6(blobPath, content, { mode: 384 });
|
|
16906
17814
|
await fsyncPath(blobPath);
|
|
@@ -16955,7 +17863,7 @@ async function validateRecoverySnapshot(params) {
|
|
|
16955
17863
|
if (record.blob !== `blobs/${record.hash}`) throw new Error(params.corruptReason);
|
|
16956
17864
|
let content;
|
|
16957
17865
|
try {
|
|
16958
|
-
content = await readFile8(
|
|
17866
|
+
content = await readFile8(path43.join(params.artifactDir, record.blob));
|
|
16959
17867
|
} catch {
|
|
16960
17868
|
throw new Error(params.corruptReason);
|
|
16961
17869
|
}
|
|
@@ -16983,13 +17891,13 @@ var RECOVERY_STATES = /* @__PURE__ */ new Set([
|
|
|
16983
17891
|
]);
|
|
16984
17892
|
var STAGING_STALE_MS = 5 * 6e4;
|
|
16985
17893
|
function checkpointsRoot(stateDir) {
|
|
16986
|
-
return
|
|
17894
|
+
return path44.join(stateDir, "recovery", "checkpoints");
|
|
16987
17895
|
}
|
|
16988
17896
|
function checkpointDir(stateDir, checkpointId) {
|
|
16989
17897
|
if (!/^cp_[a-f0-9]{24}$/.test(checkpointId)) {
|
|
16990
17898
|
throw new Error("invalid_recovery_checkpoint_id");
|
|
16991
17899
|
}
|
|
16992
|
-
return
|
|
17900
|
+
return path44.join(checkpointsRoot(stateDir), checkpointId);
|
|
16993
17901
|
}
|
|
16994
17902
|
async function fsyncPath2(filePath) {
|
|
16995
17903
|
const handle = await open5(filePath, "r");
|
|
@@ -17000,13 +17908,13 @@ async function fsyncPath2(filePath) {
|
|
|
17000
17908
|
}
|
|
17001
17909
|
}
|
|
17002
17910
|
async function atomicWriteJson(filePath, value) {
|
|
17003
|
-
await mkdir10(
|
|
17911
|
+
await mkdir10(path44.dirname(filePath), { recursive: true, mode: 448 });
|
|
17004
17912
|
const temporary = `${filePath}.tmp-${randomUUID4()}`;
|
|
17005
17913
|
await writeFile7(temporary, `${JSON.stringify(value, null, 2)}
|
|
17006
17914
|
`, { mode: 384 });
|
|
17007
17915
|
await fsyncPath2(temporary);
|
|
17008
17916
|
await rename2(temporary, filePath);
|
|
17009
|
-
await fsyncPath2(
|
|
17917
|
+
await fsyncPath2(path44.dirname(filePath));
|
|
17010
17918
|
}
|
|
17011
17919
|
async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
17012
17920
|
const value = {
|
|
@@ -17016,13 +17924,13 @@ async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
|
17016
17924
|
manifestHash,
|
|
17017
17925
|
...detail ? { detail } : {}
|
|
17018
17926
|
};
|
|
17019
|
-
await atomicWriteJson(
|
|
17927
|
+
await atomicWriteJson(path44.join(artifactDir, "state.json"), value);
|
|
17020
17928
|
}
|
|
17021
17929
|
async function directorySize(root) {
|
|
17022
17930
|
if (!existsSync13(root)) return 0;
|
|
17023
17931
|
let total = 0;
|
|
17024
17932
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
17025
|
-
const entryPath =
|
|
17933
|
+
const entryPath = path44.join(root, entry.name);
|
|
17026
17934
|
if (entry.isDirectory()) total += await directorySize(entryPath);
|
|
17027
17935
|
else total += (await lstat7(entryPath)).size;
|
|
17028
17936
|
}
|
|
@@ -17067,9 +17975,9 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
17067
17975
|
let rawManifest;
|
|
17068
17976
|
let state;
|
|
17069
17977
|
try {
|
|
17070
|
-
rawManifest = JSON.parse(await readFile9(
|
|
17978
|
+
rawManifest = JSON.parse(await readFile9(path44.join(artifactDir, "manifest.json"), "utf8"));
|
|
17071
17979
|
state = JSON.parse(
|
|
17072
|
-
await readFile9(
|
|
17980
|
+
await readFile9(path44.join(artifactDir, "state.json"), "utf8")
|
|
17073
17981
|
);
|
|
17074
17982
|
} catch {
|
|
17075
17983
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
@@ -17085,10 +17993,10 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
17085
17993
|
}
|
|
17086
17994
|
const entryPaths = /* @__PURE__ */ new Set();
|
|
17087
17995
|
for (const entry of manifest.entries) {
|
|
17088
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).length !== 3 || !Object.keys(entry).every((key) => ["path", "before", "after"].includes(key)) || typeof entry.path !== "string" || !("before" in entry) || !("after" in entry) || !validRecoveryRelativePath(entry.path) || entryPaths.has(
|
|
17996
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).length !== 3 || !Object.keys(entry).every((key) => ["path", "before", "after"].includes(key)) || typeof entry.path !== "string" || !("before" in entry) || !("after" in entry) || !validRecoveryRelativePath(entry.path) || entryPaths.has(path44.normalize(entry.path))) {
|
|
17089
17997
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
17090
17998
|
}
|
|
17091
|
-
entryPaths.add(
|
|
17999
|
+
entryPaths.add(path44.normalize(entry.path));
|
|
17092
18000
|
for (const [side, snapshot] of [
|
|
17093
18001
|
["before", entry.before],
|
|
17094
18002
|
["after", entry.after]
|
|
@@ -17102,7 +18010,7 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
17102
18010
|
});
|
|
17103
18011
|
}
|
|
17104
18012
|
}
|
|
17105
|
-
const receiptPath =
|
|
18013
|
+
const receiptPath = path44.join(artifactDir, "receipt.json");
|
|
17106
18014
|
let receipt;
|
|
17107
18015
|
if (["applied", "restoring", "restored", "conflict"].includes(state.state) || existsSync13(receiptPath)) {
|
|
17108
18016
|
receipt = await readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
@@ -17112,7 +18020,7 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
17112
18020
|
async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
17113
18021
|
let rawReceipt;
|
|
17114
18022
|
try {
|
|
17115
|
-
rawReceipt = JSON.parse(await readFile9(
|
|
18023
|
+
rawReceipt = JSON.parse(await readFile9(path44.join(artifactDir, "receipt.json"), "utf8"));
|
|
17116
18024
|
} catch {
|
|
17117
18025
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
17118
18026
|
}
|
|
@@ -17135,7 +18043,7 @@ async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHas
|
|
|
17135
18043
|
return receipt;
|
|
17136
18044
|
}
|
|
17137
18045
|
async function ensureRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
17138
|
-
const receiptPath =
|
|
18046
|
+
const receiptPath = path44.join(artifactDir, "receipt.json");
|
|
17139
18047
|
if (existsSync13(receiptPath)) {
|
|
17140
18048
|
return readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
17141
18049
|
}
|
|
@@ -17160,7 +18068,7 @@ async function artifactRepoRoot(stateDir, checkpointId) {
|
|
|
17160
18068
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
17161
18069
|
try {
|
|
17162
18070
|
const manifest = JSON.parse(
|
|
17163
|
-
await readFile9(
|
|
18071
|
+
await readFile9(path44.join(artifactDir, "manifest.json"), "utf8")
|
|
17164
18072
|
);
|
|
17165
18073
|
if (typeof manifest.repoRoot === "string" && manifest.repoRoot) {
|
|
17166
18074
|
return canonicalPath(manifest.repoRoot);
|
|
@@ -17168,7 +18076,7 @@ async function artifactRepoRoot(stateDir, checkpointId) {
|
|
|
17168
18076
|
} catch {
|
|
17169
18077
|
}
|
|
17170
18078
|
try {
|
|
17171
|
-
const owner = JSON.parse(await readFile9(
|
|
18079
|
+
const owner = JSON.parse(await readFile9(path44.join(artifactDir, "owner.json"), "utf8"));
|
|
17172
18080
|
return typeof owner.repoRoot === "string" && owner.repoRoot ? canonicalPath(owner.repoRoot) : null;
|
|
17173
18081
|
} catch {
|
|
17174
18082
|
return null;
|
|
@@ -17188,10 +18096,10 @@ async function cleanupOrphanedStaging(stateDir) {
|
|
|
17188
18096
|
const now = Date.now();
|
|
17189
18097
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
17190
18098
|
if (!entry.isDirectory() || !/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) continue;
|
|
17191
|
-
const stagingPath =
|
|
18099
|
+
const stagingPath = path44.join(root, entry.name);
|
|
17192
18100
|
let stale = false;
|
|
17193
18101
|
try {
|
|
17194
|
-
const owner = JSON.parse(await readFile9(
|
|
18102
|
+
const owner = JSON.parse(await readFile9(path44.join(stagingPath, "owner.json"), "utf8"));
|
|
17195
18103
|
const pid = typeof owner.pid === "number" ? owner.pid : Number.NaN;
|
|
17196
18104
|
const createdAt = typeof owner.createdAt === "string" ? Date.parse(owner.createdAt) : NaN;
|
|
17197
18105
|
let alive = false;
|
|
@@ -17233,7 +18141,7 @@ async function markRecoveryCheckpointApplied(stateDir, checkpoint) {
|
|
|
17233
18141
|
init_fingerprint2();
|
|
17234
18142
|
import { existsSync as existsSync14 } from "node:fs";
|
|
17235
18143
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
17236
|
-
import
|
|
18144
|
+
import path45 from "node:path";
|
|
17237
18145
|
async function matchRecoverySide(resourceRoot, entries, side) {
|
|
17238
18146
|
for (const entry of entries) {
|
|
17239
18147
|
const target = await assertRecoverySafeTarget(resourceRoot, entry.path);
|
|
@@ -17248,7 +18156,7 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
17248
18156
|
} catch {
|
|
17249
18157
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
17250
18158
|
if (existsSync14(artifactDir)) {
|
|
17251
|
-
const manifestPath =
|
|
18159
|
+
const manifestPath = path45.join(artifactDir, "manifest.json");
|
|
17252
18160
|
const hash = existsSync14(manifestPath) ? hashValue(await readFile10(manifestPath, "utf8")) : "unavailable";
|
|
17253
18161
|
await writeRecoveryState(artifactDir, "corrupt", hash, RECOVERY_CHECKPOINT_CORRUPT);
|
|
17254
18162
|
}
|
|
@@ -17285,7 +18193,7 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
17285
18193
|
// src/core/recovery/resource-identity.ts
|
|
17286
18194
|
init_fingerprint2();
|
|
17287
18195
|
import { lstat as lstat8, readFile as readFile11, realpath as realpath3 } from "node:fs/promises";
|
|
17288
|
-
import
|
|
18196
|
+
import path46 from "node:path";
|
|
17289
18197
|
async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
17290
18198
|
const resolvedRoot = await realpath3(resourceRoot);
|
|
17291
18199
|
if (resourceKind === "directory") {
|
|
@@ -17293,13 +18201,13 @@ async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
|
17293
18201
|
if (!rootInfo.isDirectory()) throw new Error("recovery_repo_identity_unavailable");
|
|
17294
18202
|
return hashValue(`${resolvedRoot}\0${rootInfo.dev}:${rootInfo.ino}:${rootInfo.birthtimeMs}`);
|
|
17295
18203
|
}
|
|
17296
|
-
const dotGit =
|
|
18204
|
+
const dotGit = path46.join(resolvedRoot, ".git");
|
|
17297
18205
|
const gitInfo = await lstat8(dotGit);
|
|
17298
18206
|
let gitMetadataPath = dotGit;
|
|
17299
18207
|
if (gitInfo.isFile()) {
|
|
17300
18208
|
const marker = (await readFile11(dotGit, "utf8")).trim();
|
|
17301
18209
|
if (!marker.startsWith("gitdir:")) throw new Error("recovery_repo_identity_unavailable");
|
|
17302
|
-
gitMetadataPath =
|
|
18210
|
+
gitMetadataPath = path46.resolve(resolvedRoot, marker.slice("gitdir:".length).trim());
|
|
17303
18211
|
} else if (!gitInfo.isDirectory()) {
|
|
17304
18212
|
throw new Error("recovery_repo_identity_unavailable");
|
|
17305
18213
|
}
|
|
@@ -17372,16 +18280,16 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
17372
18280
|
throw new Error(RECOVERY_CHECKPOINT_QUOTA);
|
|
17373
18281
|
}
|
|
17374
18282
|
const checkpointId = `cp_${randomUUID5().replaceAll("-", "").slice(0, 24)}`;
|
|
17375
|
-
const temporary =
|
|
18283
|
+
const temporary = path47.join(checkpointsRoot(params.stateDir), `.tmp-${checkpointId}`);
|
|
17376
18284
|
const finalDir = checkpointDir(params.stateDir, checkpointId);
|
|
17377
18285
|
await mkdir11(temporary, { recursive: true, mode: 448 });
|
|
17378
|
-
await atomicWriteJson(
|
|
18286
|
+
await atomicWriteJson(path47.join(temporary, "owner.json"), {
|
|
17379
18287
|
version: 1,
|
|
17380
18288
|
pid: process.pid,
|
|
17381
18289
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17382
18290
|
repoRoot: canonicalPath(params.repoRoot)
|
|
17383
18291
|
});
|
|
17384
|
-
await mkdir11(
|
|
18292
|
+
await mkdir11(path47.join(temporary, "blobs"), { recursive: true, mode: 448 });
|
|
17385
18293
|
try {
|
|
17386
18294
|
const entries = [];
|
|
17387
18295
|
const protectedRoots = (params.protectedRoots ?? []).map(canonicalPath);
|
|
@@ -17390,8 +18298,8 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
17390
18298
|
)) {
|
|
17391
18299
|
const target = await assertRecoverySafeTarget(params.repoRoot, change.relativePath);
|
|
17392
18300
|
if (protectedRoots.some((root) => {
|
|
17393
|
-
const relative =
|
|
17394
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
18301
|
+
const relative = path47.relative(root, target);
|
|
18302
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path47.sep}`) && !path47.isAbsolute(relative);
|
|
17395
18303
|
})) {
|
|
17396
18304
|
throw new Error("recovery_protected_path");
|
|
17397
18305
|
}
|
|
@@ -17403,7 +18311,7 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
17403
18311
|
entries.push({
|
|
17404
18312
|
path: change.relativePath,
|
|
17405
18313
|
before: await captureRecoverySnapshot(baseline, {
|
|
17406
|
-
blobDir:
|
|
18314
|
+
blobDir: path47.join(temporary, "blobs")
|
|
17407
18315
|
}),
|
|
17408
18316
|
after: withoutRecoveryBlob(await captureRecoverySnapshot(source))
|
|
17409
18317
|
});
|
|
@@ -17440,7 +18348,7 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
17440
18348
|
entries
|
|
17441
18349
|
};
|
|
17442
18350
|
const manifestHash = hashValue(canonicalStringify(manifest));
|
|
17443
|
-
await atomicWriteJson(
|
|
18351
|
+
await atomicWriteJson(path47.join(temporary, "manifest.json"), manifest);
|
|
17444
18352
|
await writeRecoveryState(temporary, "prepared", manifestHash);
|
|
17445
18353
|
await fsyncPath2(temporary);
|
|
17446
18354
|
const projectedBytes = await recoveryCheckpointStorageBytes(params.stateDir, params.repoRoot);
|
|
@@ -17483,7 +18391,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
17483
18391
|
} catch {
|
|
17484
18392
|
try {
|
|
17485
18393
|
const raw = JSON.parse(
|
|
17486
|
-
await readFile12(
|
|
18394
|
+
await readFile12(path47.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
17487
18395
|
);
|
|
17488
18396
|
rootFromArtifact = typeof raw.repoRoot === "string" && raw.repoRoot ? raw.repoRoot : void 0;
|
|
17489
18397
|
} catch {
|
|
@@ -17517,7 +18425,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
17517
18425
|
} catch {
|
|
17518
18426
|
try {
|
|
17519
18427
|
const manifest = JSON.parse(
|
|
17520
|
-
await readFile12(
|
|
18428
|
+
await readFile12(path47.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
17521
18429
|
);
|
|
17522
18430
|
if (manifest.checkpointId !== id || ![1, 2].includes(manifest.version)) continue;
|
|
17523
18431
|
if (repoRoot && canonicalPath(manifest.repoRoot) !== canonicalPath(repoRoot)) continue;
|
|
@@ -17547,7 +18455,7 @@ async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
|
17547
18455
|
let total = 0;
|
|
17548
18456
|
for (const entry of await readdir3(root, { withFileTypes: true })) {
|
|
17549
18457
|
if (!entry.isDirectory()) continue;
|
|
17550
|
-
const entryPath =
|
|
18458
|
+
const entryPath = path47.join(root, entry.name);
|
|
17551
18459
|
if (/^cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
17552
18460
|
if (await artifactRepoRoot(stateDir, entry.name) === expected) {
|
|
17553
18461
|
total += await directorySize(entryPath);
|
|
@@ -17556,7 +18464,7 @@ async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
|
17556
18464
|
}
|
|
17557
18465
|
if (/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
17558
18466
|
try {
|
|
17559
|
-
const owner = JSON.parse(await readFile12(
|
|
18467
|
+
const owner = JSON.parse(await readFile12(path47.join(entryPath, "owner.json"), "utf8"));
|
|
17560
18468
|
if (typeof owner.repoRoot === "string" && canonicalPath(owner.repoRoot) === expected) {
|
|
17561
18469
|
total += await directorySize(entryPath);
|
|
17562
18470
|
}
|
|
@@ -17600,14 +18508,14 @@ init_scrub();
|
|
|
17600
18508
|
// src/core/transactional/file-checkpoint-backend.ts
|
|
17601
18509
|
import { cp, lstat as lstat11, mkdir as mkdir13, mkdtemp as mkdtemp5, readdir as readdir6, rm as rm9, writeFile as writeFile10 } from "node:fs/promises";
|
|
17602
18510
|
import os5 from "node:os";
|
|
17603
|
-
import
|
|
18511
|
+
import path51 from "node:path";
|
|
17604
18512
|
|
|
17605
18513
|
// src/core/transactional/file-checkpoint-git.ts
|
|
17606
18514
|
init_path_utils();
|
|
17607
18515
|
import { spawn as spawn8 } from "node:child_process";
|
|
17608
18516
|
import { createHash as createHash13 } from "node:crypto";
|
|
17609
18517
|
import { copyFile as copyFile3, lstat as lstat9, readdir as readdir4, readFile as readFile13 } from "node:fs/promises";
|
|
17610
|
-
import
|
|
18518
|
+
import path48 from "node:path";
|
|
17611
18519
|
var FILE_CHECKPOINT_GIT_METADATA_CHANGED = "file_checkpoint_git_metadata_changed";
|
|
17612
18520
|
var FILE_CHECKPOINT_SOURCE_CHANGED = "file_checkpoint_source_changed";
|
|
17613
18521
|
var FILE_CHECKPOINT_CWD_OUTSIDE_ROOT = "file_checkpoint_cwd_outside_root";
|
|
@@ -17641,7 +18549,7 @@ function rethrowStableFileCheckpointError(error) {
|
|
|
17641
18549
|
}
|
|
17642
18550
|
async function rootGitMetadataPresent(repoRoot) {
|
|
17643
18551
|
try {
|
|
17644
|
-
await lstat9(
|
|
18552
|
+
await lstat9(path48.join(repoRoot, ".git"));
|
|
17645
18553
|
return true;
|
|
17646
18554
|
} catch {
|
|
17647
18555
|
return false;
|
|
@@ -17690,10 +18598,10 @@ function execGit2(repoRoot, args) {
|
|
|
17690
18598
|
}
|
|
17691
18599
|
async function resolveGitPath(repoRoot, gitPath) {
|
|
17692
18600
|
const trimmed = gitPath.trim();
|
|
17693
|
-
if (
|
|
18601
|
+
if (path48.isAbsolute(trimmed)) {
|
|
17694
18602
|
return trimmed;
|
|
17695
18603
|
}
|
|
17696
|
-
return
|
|
18604
|
+
return path48.join(repoRoot, trimmed);
|
|
17697
18605
|
}
|
|
17698
18606
|
async function cloneBareWorktreeCopy(sourceRoot, destinationRoot) {
|
|
17699
18607
|
await execGit2(sourceRoot, [
|
|
@@ -17735,7 +18643,7 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
17735
18643
|
destinationRoot,
|
|
17736
18644
|
await execGit2(destinationRoot, ["rev-parse", "--git-dir"])
|
|
17737
18645
|
);
|
|
17738
|
-
const destinationShared =
|
|
18646
|
+
const destinationShared = path48.join(destinationGitDir, path48.basename(sourceShared));
|
|
17739
18647
|
try {
|
|
17740
18648
|
await copyFile3(sourceShared, destinationShared);
|
|
17741
18649
|
} catch (error) {
|
|
@@ -17744,7 +18652,7 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
17744
18652
|
}
|
|
17745
18653
|
async function readGitFile(gitDir, relativePath) {
|
|
17746
18654
|
try {
|
|
17747
|
-
return await readFile13(
|
|
18655
|
+
return await readFile13(path48.join(gitDir, relativePath));
|
|
17748
18656
|
} catch {
|
|
17749
18657
|
return null;
|
|
17750
18658
|
}
|
|
@@ -17768,8 +18676,8 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
17768
18676
|
repoRoot,
|
|
17769
18677
|
await execGit2(repoRoot, ["rev-parse", "--git-path", gitPath])
|
|
17770
18678
|
);
|
|
17771
|
-
const relative =
|
|
17772
|
-
const content = typeof relative === "string" && !relative.startsWith("..") && !
|
|
18679
|
+
const relative = path48.resolve(resolved).startsWith(path48.resolve(gitDir)) ? path48.relative(gitDir, resolved) : resolved;
|
|
18680
|
+
const content = typeof relative === "string" && !relative.startsWith("..") && !path48.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
17773
18681
|
if (content !== null) {
|
|
17774
18682
|
hashGitFileContent(hash, gitPath, content);
|
|
17775
18683
|
}
|
|
@@ -17779,13 +18687,13 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
17779
18687
|
async function hashGitTree(gitDir, relativeDir, hash) {
|
|
17780
18688
|
let names;
|
|
17781
18689
|
try {
|
|
17782
|
-
names = await readdir4(
|
|
18690
|
+
names = await readdir4(path48.join(gitDir, relativeDir));
|
|
17783
18691
|
} catch {
|
|
17784
18692
|
return;
|
|
17785
18693
|
}
|
|
17786
18694
|
for (const name of names.sort()) {
|
|
17787
|
-
const relativePath = relativeDir ?
|
|
17788
|
-
const absolutePath =
|
|
18695
|
+
const relativePath = relativeDir ? path48.join(relativeDir, name) : name;
|
|
18696
|
+
const absolutePath = path48.join(gitDir, relativePath);
|
|
17789
18697
|
let childNames = null;
|
|
17790
18698
|
try {
|
|
17791
18699
|
childNames = await readdir4(absolutePath);
|
|
@@ -17807,7 +18715,7 @@ async function hashGitTree(gitDir, relativeDir, hash) {
|
|
|
17807
18715
|
}
|
|
17808
18716
|
async function computeGitMetadataFingerprint(repoRoot) {
|
|
17809
18717
|
const gitDirRel = (await execGit2(repoRoot, ["rev-parse", "--git-dir"])).trim();
|
|
17810
|
-
const gitDir =
|
|
18718
|
+
const gitDir = path48.isAbsolute(gitDirRel) ? gitDirRel : path48.join(repoRoot, gitDirRel);
|
|
17811
18719
|
const hash = createHash13("sha256");
|
|
17812
18720
|
for (const file of [
|
|
17813
18721
|
"HEAD",
|
|
@@ -17830,8 +18738,8 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
17830
18738
|
const sharedIndex = (await execGit2(repoRoot, ["rev-parse", "--shared-index-path"])).trim();
|
|
17831
18739
|
if (sharedIndex) {
|
|
17832
18740
|
const resolved = await resolveGitPath(repoRoot, sharedIndex);
|
|
17833
|
-
const relative =
|
|
17834
|
-
const content = relative && !relative.startsWith("..") && !
|
|
18741
|
+
const relative = path48.relative(gitDir, resolved);
|
|
18742
|
+
const content = relative && !relative.startsWith("..") && !path48.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
17835
18743
|
if (content !== null) {
|
|
17836
18744
|
hashGitFileContent(hash, "shared-index", content);
|
|
17837
18745
|
}
|
|
@@ -17842,7 +18750,7 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
17842
18750
|
await hashResolvedGitPath(repoRoot, gitDir, gitPath, hash);
|
|
17843
18751
|
}
|
|
17844
18752
|
try {
|
|
17845
|
-
const rootGitPath =
|
|
18753
|
+
const rootGitPath = path48.join(repoRoot, ".git");
|
|
17846
18754
|
const rootGitInfo = await lstat9(rootGitPath);
|
|
17847
18755
|
if (rootGitInfo.isFile()) {
|
|
17848
18756
|
const content = await readAbsoluteGitFile(rootGitPath);
|
|
@@ -17858,14 +18766,14 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
17858
18766
|
function resolveExecutionCwdRelative(resourceRoot, cwd) {
|
|
17859
18767
|
const resolvedCwd = canonicalPath(cwd);
|
|
17860
18768
|
const resourceCanonical = canonicalPath(resourceRoot);
|
|
17861
|
-
const relative =
|
|
18769
|
+
const relative = path48.relative(resourceCanonical, resolvedCwd);
|
|
17862
18770
|
if (relative === "" || relative === ".") {
|
|
17863
18771
|
return "";
|
|
17864
18772
|
}
|
|
17865
|
-
if (relative.startsWith("..") ||
|
|
18773
|
+
if (relative.startsWith("..") || path48.isAbsolute(relative)) {
|
|
17866
18774
|
throw new Error(FILE_CHECKPOINT_CWD_OUTSIDE_ROOT);
|
|
17867
18775
|
}
|
|
17868
|
-
return relative.split(
|
|
18776
|
+
return relative.split(path48.sep).join("/");
|
|
17869
18777
|
}
|
|
17870
18778
|
|
|
17871
18779
|
// src/core/transactional/file-checkpoint-isolation.ts
|
|
@@ -17887,7 +18795,7 @@ function fileCheckpointIsolationReason(context) {
|
|
|
17887
18795
|
|
|
17888
18796
|
// src/core/transactional/file-checkpoint-staging.ts
|
|
17889
18797
|
import { readdir as readdir5, readFile as readFile14, rm as rm7, writeFile as writeFile8 } from "node:fs/promises";
|
|
17890
|
-
import
|
|
18798
|
+
import path49 from "node:path";
|
|
17891
18799
|
function isOwnerProcessAlive(pid) {
|
|
17892
18800
|
try {
|
|
17893
18801
|
process.kill(pid, 0);
|
|
@@ -17897,12 +18805,12 @@ function isOwnerProcessAlive(pid) {
|
|
|
17897
18805
|
}
|
|
17898
18806
|
}
|
|
17899
18807
|
async function writeOwnerMarker(stagingRoot, marker) {
|
|
17900
|
-
await writeFile8(
|
|
18808
|
+
await writeFile8(path49.join(stagingRoot, "owner.json"), `${JSON.stringify(marker)}
|
|
17901
18809
|
`, "utf8");
|
|
17902
18810
|
}
|
|
17903
18811
|
async function readOwnerMarker(stagingRoot) {
|
|
17904
18812
|
try {
|
|
17905
|
-
const raw = await readFile14(
|
|
18813
|
+
const raw = await readFile14(path49.join(stagingRoot, "owner.json"), "utf8");
|
|
17906
18814
|
return JSON.parse(raw.trim());
|
|
17907
18815
|
} catch {
|
|
17908
18816
|
return null;
|
|
@@ -17920,7 +18828,7 @@ async function collectDeadOwnerStaging(parentDir) {
|
|
|
17920
18828
|
if (!name.startsWith("belay-file-checkpoint-")) {
|
|
17921
18829
|
continue;
|
|
17922
18830
|
}
|
|
17923
|
-
const stagingRoot =
|
|
18831
|
+
const stagingRoot = path49.join(parentDir, name);
|
|
17924
18832
|
const marker = await readOwnerMarker(stagingRoot);
|
|
17925
18833
|
if (!marker) {
|
|
17926
18834
|
dead.push(stagingRoot);
|
|
@@ -17952,7 +18860,7 @@ import {
|
|
|
17952
18860
|
writeFile as writeFile9
|
|
17953
18861
|
} from "node:fs/promises";
|
|
17954
18862
|
import os4 from "node:os";
|
|
17955
|
-
import
|
|
18863
|
+
import path50 from "node:path";
|
|
17956
18864
|
var FILE_CHECKPOINT_COPY_FAILED = "file_checkpoint_copy_failed";
|
|
17957
18865
|
async function chmodSafe2(target, mode) {
|
|
17958
18866
|
try {
|
|
@@ -17962,7 +18870,7 @@ async function chmodSafe2(target, mode) {
|
|
|
17962
18870
|
}
|
|
17963
18871
|
}
|
|
17964
18872
|
async function copyRegularFile(sourcePath, destinationPath, mode, strategy) {
|
|
17965
|
-
await mkdir12(
|
|
18873
|
+
await mkdir12(path50.dirname(destinationPath), { recursive: true });
|
|
17966
18874
|
if (strategy === "clonefile" && fsConstants2.COPYFILE_FICLONE !== void 0) {
|
|
17967
18875
|
try {
|
|
17968
18876
|
await copyFile4(sourcePath, destinationPath, fsConstants2.COPYFILE_FICLONE);
|
|
@@ -17988,7 +18896,7 @@ async function copyNode(sourceRoot, destinationRoot, relativePath, strategy) {
|
|
|
17988
18896
|
return strategy;
|
|
17989
18897
|
}
|
|
17990
18898
|
if (info.isSymbolicLink()) {
|
|
17991
|
-
await mkdir12(
|
|
18899
|
+
await mkdir12(path50.dirname(destinationPath), { recursive: true });
|
|
17992
18900
|
await symlink4(await readlink5(sourcePath), destinationPath);
|
|
17993
18901
|
return strategy;
|
|
17994
18902
|
}
|
|
@@ -18044,9 +18952,9 @@ async function mapWithConcurrency(items, concurrency, worker) {
|
|
|
18044
18952
|
async function probeFileCloneStrategy() {
|
|
18045
18953
|
let tempDir = null;
|
|
18046
18954
|
try {
|
|
18047
|
-
tempDir = await mkdtemp4(
|
|
18048
|
-
const source =
|
|
18049
|
-
const destination =
|
|
18955
|
+
tempDir = await mkdtemp4(path50.join(os4.tmpdir(), "belay-clone-probe-"));
|
|
18956
|
+
const source = path50.join(tempDir, "source.txt");
|
|
18957
|
+
const destination = path50.join(tempDir, "dest.txt");
|
|
18050
18958
|
await writeFile9(source, "probe\n");
|
|
18051
18959
|
if (fsConstants2.COPYFILE_FICLONE_FORCE !== void 0) {
|
|
18052
18960
|
try {
|
|
@@ -18199,11 +19107,11 @@ async function protectedRootState(root) {
|
|
|
18199
19107
|
return `directory:${node.hash}:${index.treeHash}`;
|
|
18200
19108
|
}
|
|
18201
19109
|
function executionProtectedRoot(resourceRoot, executionRoot, protectedRoot) {
|
|
18202
|
-
const relative =
|
|
18203
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
19110
|
+
const relative = path51.relative(path51.resolve(resourceRoot), path51.resolve(protectedRoot));
|
|
19111
|
+
if (relative === "" || relative.startsWith("..") || path51.isAbsolute(relative)) {
|
|
18204
19112
|
return null;
|
|
18205
19113
|
}
|
|
18206
|
-
return
|
|
19114
|
+
return path51.join(executionRoot, relative);
|
|
18207
19115
|
}
|
|
18208
19116
|
async function captureProtectedRootStates(resourceRoot, executionRoot, protectedRoots) {
|
|
18209
19117
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -18228,15 +19136,15 @@ async function directoryByteSize(root, deadlineMs) {
|
|
|
18228
19136
|
}
|
|
18229
19137
|
let total = 0;
|
|
18230
19138
|
for (const name of await readdir6(root)) {
|
|
18231
|
-
total += await directoryByteSize(
|
|
19139
|
+
total += await directoryByteSize(path51.join(root, name), deadlineMs);
|
|
18232
19140
|
}
|
|
18233
19141
|
return total;
|
|
18234
19142
|
}
|
|
18235
19143
|
async function copyGitMetadataDirectory(sourceRoot, destinationRoot) {
|
|
18236
19144
|
const gitDirRel = (await execGit2(sourceRoot, ["rev-parse", "--git-dir"])).trim();
|
|
18237
|
-
const sourceGitDir =
|
|
18238
|
-
const relativeGitDir =
|
|
18239
|
-
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ?
|
|
19145
|
+
const sourceGitDir = path51.isAbsolute(gitDirRel) ? gitDirRel : path51.join(sourceRoot, gitDirRel);
|
|
19146
|
+
const relativeGitDir = path51.relative(path51.resolve(sourceRoot), path51.resolve(sourceGitDir));
|
|
19147
|
+
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ? path51.join(destinationRoot, relativeGitDir) : path51.join(destinationRoot, ".git");
|
|
18240
19148
|
await cp(sourceGitDir, destinationGitDir, { recursive: true, force: true });
|
|
18241
19149
|
}
|
|
18242
19150
|
async function prepareDirtyGitSnapshot(context) {
|
|
@@ -18245,7 +19153,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
18245
19153
|
const quotas = context.fileCheckpoint;
|
|
18246
19154
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
18247
19155
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
18248
|
-
const stagingRoot = await mkdtemp5(
|
|
19156
|
+
const stagingRoot = await mkdtemp5(path51.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
18249
19157
|
await writeOwnerMarker(stagingRoot, {
|
|
18250
19158
|
version: 1,
|
|
18251
19159
|
pid: process.pid,
|
|
@@ -18253,8 +19161,8 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
18253
19161
|
resourceRoot: context.repoRoot,
|
|
18254
19162
|
backend: "file_checkpoint"
|
|
18255
19163
|
});
|
|
18256
|
-
const baselineRoot =
|
|
18257
|
-
const executionRoot =
|
|
19164
|
+
const baselineRoot = path51.join(stagingRoot, "baseline");
|
|
19165
|
+
const executionRoot = path51.join(stagingRoot, "execution");
|
|
18258
19166
|
try {
|
|
18259
19167
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
18260
19168
|
const sourceGitMetadataFingerprint = await computeGitMetadataFingerprint(context.repoRoot);
|
|
@@ -18288,7 +19196,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
18288
19196
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
18289
19197
|
}
|
|
18290
19198
|
await writeFile10(
|
|
18291
|
-
|
|
19199
|
+
path51.join(stagingRoot, "baseline-index.json"),
|
|
18292
19200
|
`${JSON.stringify(baselineIndex)}
|
|
18293
19201
|
`,
|
|
18294
19202
|
"utf8"
|
|
@@ -18338,7 +19246,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
18338
19246
|
const quotas = context.fileCheckpoint;
|
|
18339
19247
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
18340
19248
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
18341
|
-
const stagingRoot = await mkdtemp5(
|
|
19249
|
+
const stagingRoot = await mkdtemp5(path51.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
18342
19250
|
await writeOwnerMarker(stagingRoot, {
|
|
18343
19251
|
version: 1,
|
|
18344
19252
|
pid: process.pid,
|
|
@@ -18346,8 +19254,8 @@ async function prepareNonGitSnapshot(context) {
|
|
|
18346
19254
|
resourceRoot: context.repoRoot,
|
|
18347
19255
|
backend: "file_checkpoint"
|
|
18348
19256
|
});
|
|
18349
|
-
const baselineRoot =
|
|
18350
|
-
const executionRoot =
|
|
19257
|
+
const baselineRoot = path51.join(stagingRoot, "baseline");
|
|
19258
|
+
const executionRoot = path51.join(stagingRoot, "execution");
|
|
18351
19259
|
try {
|
|
18352
19260
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
18353
19261
|
const resourceIdentity = await currentRecoveryResourceIdentity(context.repoRoot, "directory");
|
|
@@ -18376,7 +19284,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
18376
19284
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
18377
19285
|
}
|
|
18378
19286
|
await writeFile10(
|
|
18379
|
-
|
|
19287
|
+
path51.join(stagingRoot, "baseline-index.json"),
|
|
18380
19288
|
`${JSON.stringify(baselineIndex)}
|
|
18381
19289
|
`,
|
|
18382
19290
|
"utf8"
|
|
@@ -18729,10 +19637,10 @@ async function selectTransactionalBackend(context) {
|
|
|
18729
19637
|
}
|
|
18730
19638
|
|
|
18731
19639
|
// src/core/transactional/diff-evaluator.ts
|
|
18732
|
-
import
|
|
19640
|
+
import path52 from "node:path";
|
|
18733
19641
|
init_path_utils();
|
|
18734
19642
|
function categorizeChange(change, ctx) {
|
|
18735
|
-
const absolutePath = canonicalPath(
|
|
19643
|
+
const absolutePath = canonicalPath(path52.join(ctx.repoRoot, change.relativePath));
|
|
18736
19644
|
if (!pathWithinRoot(ctx.repoRoot, absolutePath)) {
|
|
18737
19645
|
return "repo_outside";
|
|
18738
19646
|
}
|
|
@@ -19326,7 +20234,7 @@ async function notifyDeny(config, event) {
|
|
|
19326
20234
|
init_path_utils();
|
|
19327
20235
|
|
|
19328
20236
|
// src/adapters/layouts/protected-paths.ts
|
|
19329
|
-
import
|
|
20237
|
+
import path53 from "node:path";
|
|
19330
20238
|
function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
19331
20239
|
const roots = [
|
|
19332
20240
|
layout.configPath(repoRoot),
|
|
@@ -19338,7 +20246,7 @@ function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
|
19338
20246
|
if (controlPlaneDir) {
|
|
19339
20247
|
roots.push(controlPlaneDir);
|
|
19340
20248
|
}
|
|
19341
|
-
return roots.map((entry) =>
|
|
20249
|
+
return roots.map((entry) => path53.resolve(entry));
|
|
19342
20250
|
}
|
|
19343
20251
|
|
|
19344
20252
|
// src/adapters/shared/gate-runtime.ts
|
|
@@ -19391,8 +20299,8 @@ function createDefaultGateRuntimeDeps() {
|
|
|
19391
20299
|
return loadJsonFile(configPath, {});
|
|
19392
20300
|
},
|
|
19393
20301
|
async appendAudit(ctx, event) {
|
|
19394
|
-
const auditPath =
|
|
19395
|
-
await mkdir14(
|
|
20302
|
+
const auditPath = path54.join(ctx.repoRoot, ctx.config.audit.logPath);
|
|
20303
|
+
await mkdir14(path54.dirname(auditPath), { recursive: true });
|
|
19396
20304
|
const provenance = auditProvenance(ctx.config);
|
|
19397
20305
|
const record = {
|
|
19398
20306
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19425,7 +20333,7 @@ function createDefaultGateRuntimeDeps() {
|
|
|
19425
20333
|
};
|
|
19426
20334
|
},
|
|
19427
20335
|
async writeApprovals(filePath, state) {
|
|
19428
|
-
await mkdir14(
|
|
20336
|
+
await mkdir14(path54.dirname(filePath), { recursive: true });
|
|
19429
20337
|
await writeFile11(filePath, `${JSON.stringify(compactApprovals(state), null, 2)}
|
|
19430
20338
|
`, "utf8");
|
|
19431
20339
|
},
|
|
@@ -19587,7 +20495,7 @@ function deriveWorkspaceRootScopeHint(params) {
|
|
|
19587
20495
|
if (!targetPath) {
|
|
19588
20496
|
return void 0;
|
|
19589
20497
|
}
|
|
19590
|
-
const candidateRoot = canonicalPath(
|
|
20498
|
+
const candidateRoot = canonicalPath(path54.dirname(targetPath));
|
|
19591
20499
|
const validation = validateTrustedWorkspaceRootCandidate({
|
|
19592
20500
|
candidatePath: candidateRoot,
|
|
19593
20501
|
repoRoot: action.repoRoot,
|
|
@@ -19921,6 +20829,7 @@ async function evaluateGatedAction(ctx, deps, params) {
|
|
|
19921
20829
|
event: resolveGateAuditEvent(sourceEvent, params.kind),
|
|
19922
20830
|
sourceEvent,
|
|
19923
20831
|
kind: params.kind,
|
|
20832
|
+
...typeof params.payload?.tool_use_id === "string" ? { toolInvocationCorrelationId: toolInvocationCorrelationId(params.payload.tool_use_id) } : {},
|
|
19924
20833
|
fingerprint: verdict2.fingerprint,
|
|
19925
20834
|
verdict: verdict2.verdict,
|
|
19926
20835
|
reason: verdict2.reason,
|
|
@@ -20078,6 +20987,7 @@ async function evaluateGatedAction(ctx, deps, params) {
|
|
|
20078
20987
|
const scrubbedPayload = fingerprintReplayPayload(params.kind, params.payload, scrubOpts);
|
|
20079
20988
|
return gateDecisionToVerdict(ctx, deps, params.kind, result, {
|
|
20080
20989
|
sourceEvent: params.sourceEvent,
|
|
20990
|
+
toolInvocationCorrelationId: typeof params.payload?.tool_use_id === "string" ? toolInvocationCorrelationId(params.payload.tool_use_id) : void 0,
|
|
20081
20991
|
predictedAssessment,
|
|
20082
20992
|
observedAssessment: observedAssessment2,
|
|
20083
20993
|
transactionalLayer,
|
|
@@ -20161,6 +21071,7 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
20161
21071
|
event: auditEvent,
|
|
20162
21072
|
sourceEvent,
|
|
20163
21073
|
kind,
|
|
21074
|
+
...auditExtras.toolInvocationCorrelationId ? { toolInvocationCorrelationId: auditExtras.toolInvocationCorrelationId } : {},
|
|
20164
21075
|
fingerprint: result.fingerprint,
|
|
20165
21076
|
summary: result.normalizedCommand ?? result.summary ?? "",
|
|
20166
21077
|
assessment: result.assessment,
|
|
@@ -20607,29 +21518,56 @@ function gateVerdictToClaudeUserPromptResponse(verdict2) {
|
|
|
20607
21518
|
};
|
|
20608
21519
|
}
|
|
20609
21520
|
async function appendObservedAudit(ctx, deps, eventName, payload) {
|
|
21521
|
+
const rawToolUseId = typeof payload.tool_use_id === "string" ? payload.tool_use_id : void 0;
|
|
21522
|
+
const summaryPayload = redactToolInvocationId(payload, rawToolUseId);
|
|
20610
21523
|
await deps.appendAudit(ctx, {
|
|
20611
21524
|
event: eventName,
|
|
20612
21525
|
kind: "audit",
|
|
20613
21526
|
verdict: "allow",
|
|
20614
21527
|
reason: "observed",
|
|
20615
|
-
|
|
21528
|
+
...rawToolUseId ? { toolInvocationCorrelationId: toolInvocationCorrelationId(rawToolUseId) } : {},
|
|
21529
|
+
...typeof summaryPayload.tool_name === "string" ? { toolName: summaryPayload.tool_name } : {},
|
|
21530
|
+
...typeof summaryPayload.failure_type === "string" ? { failureType: summaryPayload.failure_type } : {},
|
|
21531
|
+
...typeof summaryPayload.error_message === "string" ? { errorMessage: summaryPayload.error_message } : {},
|
|
21532
|
+
...typeof payload.duration === "number" ? { durationMs: payload.duration } : {},
|
|
21533
|
+
...typeof payload.is_interrupt === "boolean" ? { isInterrupt: payload.is_interrupt } : {},
|
|
21534
|
+
summary: canonicalStringify(summaryPayload)
|
|
20616
21535
|
});
|
|
20617
21536
|
}
|
|
20618
21537
|
|
|
20619
21538
|
// src/adapters/shared/repo-root.ts
|
|
20620
21539
|
import { existsSync as existsSync17 } from "node:fs";
|
|
20621
|
-
import
|
|
21540
|
+
import path55 from "node:path";
|
|
21541
|
+
function belayConfigPath(current, adapterName) {
|
|
21542
|
+
if (adapterName === "cursor") {
|
|
21543
|
+
return path55.join(current, ".cursor", "belay.config.json");
|
|
21544
|
+
}
|
|
21545
|
+
if (adapterName === "claude") {
|
|
21546
|
+
return path55.join(current, ".claude", "belay.config.json");
|
|
21547
|
+
}
|
|
21548
|
+
return path55.join(current, ".codex", "belay.config.json");
|
|
21549
|
+
}
|
|
21550
|
+
function markerMatches(current, marker, layout) {
|
|
21551
|
+
const markerPath = path55.join(current, marker);
|
|
21552
|
+
if (!existsSync17(markerPath)) {
|
|
21553
|
+
return false;
|
|
21554
|
+
}
|
|
21555
|
+
if (marker === ".cursor" || marker === ".claude" || marker === ".codex") {
|
|
21556
|
+
return existsSync17(belayConfigPath(current, layout.name));
|
|
21557
|
+
}
|
|
21558
|
+
return true;
|
|
21559
|
+
}
|
|
20622
21560
|
function findRepoRoot(startPath, layout) {
|
|
20623
|
-
let current =
|
|
21561
|
+
let current = path55.resolve(startPath);
|
|
20624
21562
|
while (true) {
|
|
20625
21563
|
for (const marker of layout.repoRootMarkers) {
|
|
20626
|
-
if (
|
|
21564
|
+
if (markerMatches(current, marker, layout)) {
|
|
20627
21565
|
return current;
|
|
20628
21566
|
}
|
|
20629
21567
|
}
|
|
20630
|
-
const parent =
|
|
21568
|
+
const parent = path55.dirname(current);
|
|
20631
21569
|
if (parent === current) {
|
|
20632
|
-
return
|
|
21570
|
+
return path55.resolve(startPath);
|
|
20633
21571
|
}
|
|
20634
21572
|
current = parent;
|
|
20635
21573
|
}
|