@guilz-dev/belay 0.9.4 → 0.10.0
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 +3 -2
- package/dist/adapters/cursor/hook-dispatch-entry.d.ts +3 -0
- package/dist/adapters/cursor/hook-dispatch-entry.js +11 -0
- package/dist/adapters/cursor/hook-router.js +14 -7
- package/dist/adapters/cursor/hooks.d.ts +1 -1
- package/dist/adapters/cursor/hooks.js +16 -5
- package/dist/adapters/cursor/routing-config-trust.d.ts +1 -0
- package/dist/adapters/cursor/routing-config-trust.js +67 -0
- package/dist/adapters/cursor/runtime-entry.d.ts +2 -2
- package/dist/adapters/cursor/runtime-entry.js +35 -10
- package/dist/adapters/shared/gate-runtime.js +28 -17
- package/dist/bundle/claude-runtime.mjs +602 -438
- package/dist/bundle/codex-runtime.mjs +612 -448
- package/dist/bundle/cursor-dispatcher.mjs +100 -26
- package/dist/bundle/cursor-runtime.mjs +662 -470
- package/dist/cli.js +53 -4
- package/dist/commands/approval-token.d.ts +10 -0
- package/dist/commands/approval-token.js +26 -0
- package/dist/commands/audit.d.ts +2 -1
- package/dist/commands/audit.js +2 -2
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +28 -2
- package/dist/commands/doctor.js +10 -54
- package/dist/commands/dogfood-check.d.ts +3 -0
- package/dist/commands/dogfood-check.js +116 -0
- package/dist/commands/dogfood.d.ts +1 -0
- package/dist/commands/dogfood.js +4 -3
- package/dist/commands/judge.js +2 -2
- package/dist/commands/recovery-checkpoints.js +2 -11
- package/dist/config-io.d.ts +1 -0
- package/dist/config-io.js +8 -1
- package/dist/core/dogfood-environment.d.ts +8 -0
- package/dist/core/dogfood-environment.js +55 -0
- package/dist/core/effect-ir/shell-lower/decoders/belay.js +12 -0
- package/dist/core/egress-approval.js +0 -18
- package/dist/core/notify.d.ts +8 -2
- package/dist/core/notify.js +63 -7
- package/dist/core/recovery/operator-guidance.js +4 -4
- package/dist/core/repo-config-trust.d.ts +31 -0
- package/dist/core/repo-config-trust.js +152 -0
- package/dist/corpus/benign-probe-cores.d.ts +1 -1
- package/dist/corpus/benign-probe-cores.js +0 -1
- package/dist/defaults.js +0 -25
- package/dist/installer/scope-config.js +2 -2
- package/dist/installer.js +4 -4
- package/dist/types.d.ts +19 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
|
@@ -3510,9 +3510,125 @@ var init_config_layers = __esm({
|
|
|
3510
3510
|
}
|
|
3511
3511
|
});
|
|
3512
3512
|
|
|
3513
|
-
// src/config-
|
|
3513
|
+
// src/core/repo-config-trust.ts
|
|
3514
3514
|
import { existsSync as existsSync2 } from "node:fs";
|
|
3515
|
-
import { chmod, mkdir as mkdir2, open as open2, readFile as readFile2, rename, unlink as unlink2
|
|
3515
|
+
import { chmod, mkdir as mkdir2, open as open2, readFile as readFile2, rename, unlink as unlink2 } from "node:fs/promises";
|
|
3516
|
+
import path11 from "node:path";
|
|
3517
|
+
function isObjectRecord(value) {
|
|
3518
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3519
|
+
}
|
|
3520
|
+
function hasOnlyExpectedKeys(value) {
|
|
3521
|
+
const expected = /* @__PURE__ */ new Set([
|
|
3522
|
+
"schemaVersion",
|
|
3523
|
+
"repoRoot",
|
|
3524
|
+
"adapter",
|
|
3525
|
+
"repoConfigFingerprint",
|
|
3526
|
+
"trustedAt"
|
|
3527
|
+
]);
|
|
3528
|
+
const keys = Object.keys(value);
|
|
3529
|
+
if (keys.length !== expected.size) {
|
|
3530
|
+
return false;
|
|
3531
|
+
}
|
|
3532
|
+
return keys.every((key) => expected.has(key));
|
|
3533
|
+
}
|
|
3534
|
+
function isAdapterName(value) {
|
|
3535
|
+
return value === "cursor" || value === "claude" || value === "codex";
|
|
3536
|
+
}
|
|
3537
|
+
function isIsoTimestamp(value) {
|
|
3538
|
+
return Number.isFinite(Date.parse(value));
|
|
3539
|
+
}
|
|
3540
|
+
function parseStrictTrustRecord(value) {
|
|
3541
|
+
if (!isObjectRecord(value) || !hasOnlyExpectedKeys(value)) {
|
|
3542
|
+
return null;
|
|
3543
|
+
}
|
|
3544
|
+
if (value.schemaVersion !== 1) {
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
if (typeof value.repoRoot !== "string" || value.repoRoot.trim().length === 0) {
|
|
3548
|
+
return null;
|
|
3549
|
+
}
|
|
3550
|
+
if (!isAdapterName(value.adapter)) {
|
|
3551
|
+
return null;
|
|
3552
|
+
}
|
|
3553
|
+
if (typeof value.repoConfigFingerprint !== "string" || !/^[a-f0-9]{64}$/.test(value.repoConfigFingerprint)) {
|
|
3554
|
+
return null;
|
|
3555
|
+
}
|
|
3556
|
+
if (typeof value.trustedAt !== "string" || !isIsoTimestamp(value.trustedAt)) {
|
|
3557
|
+
return null;
|
|
3558
|
+
}
|
|
3559
|
+
return {
|
|
3560
|
+
schemaVersion: 1,
|
|
3561
|
+
repoRoot: value.repoRoot,
|
|
3562
|
+
adapter: value.adapter,
|
|
3563
|
+
repoConfigFingerprint: value.repoConfigFingerprint,
|
|
3564
|
+
trustedAt: value.trustedAt
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
function repoConfigFingerprint(rawConfig) {
|
|
3568
|
+
return hashValue(canonicalStringify(rawConfig));
|
|
3569
|
+
}
|
|
3570
|
+
function repoConfigTrustPath(repoRoot, adapter) {
|
|
3571
|
+
const canonicalRepoRoot = canonicalPath(repoRoot);
|
|
3572
|
+
const identity = hashValue(`${canonicalRepoRoot}\0${adapter}`);
|
|
3573
|
+
return path11.join(defaultControlPlaneDir(), "config-trust", `${identity}.json`);
|
|
3574
|
+
}
|
|
3575
|
+
async function inspectRepoConfigTrust(repoRoot, adapter, rawConfig) {
|
|
3576
|
+
const canonicalRepoRoot = canonicalPath(repoRoot);
|
|
3577
|
+
const recordPath = repoConfigTrustPath(repoRoot, adapter);
|
|
3578
|
+
if (!existsSync2(recordPath)) {
|
|
3579
|
+
return { trusted: false, recordPath, reason: "missing" };
|
|
3580
|
+
}
|
|
3581
|
+
let parsed;
|
|
3582
|
+
try {
|
|
3583
|
+
parsed = JSON.parse(await readFile2(recordPath, "utf8"));
|
|
3584
|
+
} catch {
|
|
3585
|
+
return { trusted: false, recordPath, reason: "malformed" };
|
|
3586
|
+
}
|
|
3587
|
+
const record = parseStrictTrustRecord(parsed);
|
|
3588
|
+
if (!record) {
|
|
3589
|
+
return { trusted: false, recordPath, reason: "malformed" };
|
|
3590
|
+
}
|
|
3591
|
+
if (record.adapter !== adapter || canonicalPath(record.repoRoot) !== canonicalRepoRoot) {
|
|
3592
|
+
return { trusted: false, recordPath, reason: "identity_mismatch" };
|
|
3593
|
+
}
|
|
3594
|
+
const fingerprint = repoConfigFingerprint(rawConfig);
|
|
3595
|
+
if (record.repoConfigFingerprint !== fingerprint) {
|
|
3596
|
+
return { trusted: false, recordPath, reason: "fingerprint_mismatch" };
|
|
3597
|
+
}
|
|
3598
|
+
return { trusted: true, recordPath, fingerprint };
|
|
3599
|
+
}
|
|
3600
|
+
function isRepoConfigTrustError(error) {
|
|
3601
|
+
return error instanceof RepoConfigTrustError;
|
|
3602
|
+
}
|
|
3603
|
+
async function assertRepoConfigTrusted(repoRoot, adapter, rawConfig) {
|
|
3604
|
+
const status = await inspectRepoConfigTrust(repoRoot, adapter, rawConfig);
|
|
3605
|
+
if (status.trusted) {
|
|
3606
|
+
return;
|
|
3607
|
+
}
|
|
3608
|
+
throw new RepoConfigTrustError(status);
|
|
3609
|
+
}
|
|
3610
|
+
var TRUST_MESSAGE, RepoConfigTrustError;
|
|
3611
|
+
var init_repo_config_trust = __esm({
|
|
3612
|
+
"src/core/repo-config-trust.ts"() {
|
|
3613
|
+
"use strict";
|
|
3614
|
+
init_config();
|
|
3615
|
+
init_fingerprint2();
|
|
3616
|
+
init_path_utils();
|
|
3617
|
+
TRUST_MESSAGE = "Repository config is not trusted. Review it, then run `belay config trust`.";
|
|
3618
|
+
RepoConfigTrustError = class extends Error {
|
|
3619
|
+
status;
|
|
3620
|
+
constructor(status) {
|
|
3621
|
+
super(TRUST_MESSAGE);
|
|
3622
|
+
this.name = "RepoConfigTrustError";
|
|
3623
|
+
this.status = status;
|
|
3624
|
+
}
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
// src/config-io.ts
|
|
3630
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3631
|
+
import { chmod as chmod2, mkdir as mkdir3, open as open3, readFile as readFile3, rename as rename2, unlink as unlink3, writeFile } from "node:fs/promises";
|
|
3516
3632
|
function resolveAdapterName(config) {
|
|
3517
3633
|
if (config.adapter === "claude") {
|
|
3518
3634
|
return "claude";
|
|
@@ -3523,10 +3639,10 @@ function resolveAdapterName(config) {
|
|
|
3523
3639
|
return "cursor";
|
|
3524
3640
|
}
|
|
3525
3641
|
function detectAdapterName(repoRoot) {
|
|
3526
|
-
if (
|
|
3642
|
+
if (existsSync3(configPathFor(repoRoot, "claude"))) {
|
|
3527
3643
|
return "claude";
|
|
3528
3644
|
}
|
|
3529
|
-
if (
|
|
3645
|
+
if (existsSync3(configPathFor(repoRoot, "codex"))) {
|
|
3530
3646
|
return "codex";
|
|
3531
3647
|
}
|
|
3532
3648
|
return "cursor";
|
|
@@ -3547,20 +3663,20 @@ async function loadLayeredConfig(repoRoot, adapter = detectAdapterName(repoRoot)
|
|
|
3547
3663
|
const layout = getAdapterLayout(adapter);
|
|
3548
3664
|
const configPath = configPathFor(repoRoot, adapter);
|
|
3549
3665
|
let repoConfig = {};
|
|
3550
|
-
if (
|
|
3551
|
-
repoConfig = JSON.parse(await
|
|
3666
|
+
if (existsSync3(configPath)) {
|
|
3667
|
+
repoConfig = JSON.parse(await readFile3(configPath, "utf8"));
|
|
3552
3668
|
}
|
|
3553
3669
|
let teamConfig = null;
|
|
3554
3670
|
const teamPath = teamConfigPath();
|
|
3555
|
-
if (
|
|
3556
|
-
teamConfig = JSON.parse(await
|
|
3671
|
+
if (existsSync3(teamPath)) {
|
|
3672
|
+
teamConfig = JSON.parse(await readFile3(teamPath, "utf8"));
|
|
3557
3673
|
}
|
|
3558
3674
|
return resolveLayeredConfig({
|
|
3559
3675
|
repoConfig,
|
|
3560
3676
|
adapterDefaults: layout.defaultConfig(repoRoot),
|
|
3561
3677
|
teamConfig,
|
|
3562
3678
|
teamConfigPath: teamPath,
|
|
3563
|
-
repoConfigPath:
|
|
3679
|
+
repoConfigPath: existsSync3(configPath) ? configPath : void 0
|
|
3564
3680
|
});
|
|
3565
3681
|
}
|
|
3566
3682
|
async function loadConfigFile(repoRoot, adapter = detectAdapterName(repoRoot)) {
|
|
@@ -3575,21 +3691,22 @@ var init_config_io = __esm({
|
|
|
3575
3691
|
init_approval_state_mutation();
|
|
3576
3692
|
init_config();
|
|
3577
3693
|
init_config_layers();
|
|
3694
|
+
init_repo_config_trust();
|
|
3578
3695
|
}
|
|
3579
3696
|
});
|
|
3580
3697
|
|
|
3581
3698
|
// src/adapters/cursor/runtime-entry.ts
|
|
3582
3699
|
init_approval();
|
|
3583
|
-
import
|
|
3700
|
+
import path67 from "node:path";
|
|
3584
3701
|
import process2 from "node:process";
|
|
3585
3702
|
|
|
3586
3703
|
// src/core/approval-repo-lookup.ts
|
|
3587
3704
|
init_config_io();
|
|
3588
|
-
import { readFile as
|
|
3589
|
-
import
|
|
3705
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
3706
|
+
import path12 from "node:path";
|
|
3590
3707
|
async function approvalStateContainsId(filePath, approvalId) {
|
|
3591
3708
|
try {
|
|
3592
|
-
const raw = await
|
|
3709
|
+
const raw = await readFile4(filePath, "utf8");
|
|
3593
3710
|
const parsed = JSON.parse(raw);
|
|
3594
3711
|
return parsed.approvals?.some((entry) => entry.approvalId === approvalId) ?? false;
|
|
3595
3712
|
} catch {
|
|
@@ -3600,7 +3717,7 @@ async function findApprovalRepoRoots(params) {
|
|
|
3600
3717
|
const matches = [];
|
|
3601
3718
|
const seen = /* @__PURE__ */ new Set();
|
|
3602
3719
|
for (const candidate of params.candidateRepoRoots) {
|
|
3603
|
-
const repoRoot =
|
|
3720
|
+
const repoRoot = path12.resolve(candidate);
|
|
3604
3721
|
if (seen.has(repoRoot)) {
|
|
3605
3722
|
continue;
|
|
3606
3723
|
}
|
|
@@ -3632,15 +3749,16 @@ function formatAmbiguousApprovalRepoMessage(repoRoots) {
|
|
|
3632
3749
|
|
|
3633
3750
|
// src/adapters/cursor/runtime-entry.ts
|
|
3634
3751
|
init_config();
|
|
3752
|
+
init_repo_config_trust();
|
|
3635
3753
|
init_cursor();
|
|
3636
3754
|
|
|
3637
3755
|
// src/adapters/shared/gate-runtime.ts
|
|
3638
3756
|
init_approval();
|
|
3639
3757
|
init_approval_replay();
|
|
3640
3758
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
3641
|
-
import { existsSync as
|
|
3642
|
-
import { mkdir as
|
|
3643
|
-
import
|
|
3759
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
3760
|
+
import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile11 } from "node:fs/promises";
|
|
3761
|
+
import path64 from "node:path";
|
|
3644
3762
|
|
|
3645
3763
|
// src/core/approval-service.ts
|
|
3646
3764
|
init_config_io();
|
|
@@ -3650,47 +3768,35 @@ init_approval_replay();
|
|
|
3650
3768
|
// src/core/approval-token.ts
|
|
3651
3769
|
init_config();
|
|
3652
3770
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
3653
|
-
import { existsSync as
|
|
3654
|
-
import { mkdir as
|
|
3655
|
-
import
|
|
3656
|
-
function base64UrlEncode(value) {
|
|
3657
|
-
return Buffer.from(value, "utf8").toString("base64url");
|
|
3658
|
-
}
|
|
3771
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
3772
|
+
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
|
|
3773
|
+
import path13 from "node:path";
|
|
3659
3774
|
function base64UrlDecode(value) {
|
|
3660
3775
|
return Buffer.from(value, "base64url").toString("utf8");
|
|
3661
3776
|
}
|
|
3662
3777
|
function approvalSigningKeyPath(controlPlaneDir = defaultControlPlaneDir()) {
|
|
3663
|
-
return
|
|
3778
|
+
return path13.join(controlPlaneDir, "approval-signing.key");
|
|
3664
3779
|
}
|
|
3665
3780
|
async function loadOrCreateApprovalSigningKey(controlPlaneDir = defaultControlPlaneDir()) {
|
|
3666
3781
|
const keyPath = approvalSigningKeyPath(controlPlaneDir);
|
|
3667
|
-
if (
|
|
3668
|
-
return
|
|
3782
|
+
if (existsSync4(keyPath)) {
|
|
3783
|
+
return readFile5(keyPath);
|
|
3669
3784
|
}
|
|
3670
|
-
await
|
|
3785
|
+
await mkdir4(controlPlaneDir, { recursive: true });
|
|
3671
3786
|
const key = randomBytes(32);
|
|
3672
3787
|
await writeFile2(keyPath, key, { mode: 384 });
|
|
3673
3788
|
return key;
|
|
3674
3789
|
}
|
|
3675
|
-
function signPayload(payload, key) {
|
|
3676
|
-
const body = base64UrlEncode(JSON.stringify(payload));
|
|
3677
|
-
const signature = createHmac("sha256", key).update(body).digest("base64url");
|
|
3678
|
-
return `${body}.${signature}`;
|
|
3679
|
-
}
|
|
3680
|
-
async function issueApprovalToken(payload, controlPlaneDir = defaultControlPlaneDir()) {
|
|
3681
|
-
const key = await loadOrCreateApprovalSigningKey(controlPlaneDir);
|
|
3682
|
-
return signPayload(payload, key);
|
|
3683
|
-
}
|
|
3684
3790
|
async function verifyApprovalToken(token, controlPlaneDir = defaultControlPlaneDir()) {
|
|
3685
3791
|
const [body, signature] = token.split(".");
|
|
3686
3792
|
if (!body || !signature) {
|
|
3687
3793
|
return null;
|
|
3688
3794
|
}
|
|
3689
3795
|
const keyPath = approvalSigningKeyPath(controlPlaneDir);
|
|
3690
|
-
if (!
|
|
3796
|
+
if (!existsSync4(keyPath)) {
|
|
3691
3797
|
return null;
|
|
3692
3798
|
}
|
|
3693
|
-
const key = await
|
|
3799
|
+
const key = await readFile5(keyPath);
|
|
3694
3800
|
const expected = createHmac("sha256", key).update(body).digest("base64url");
|
|
3695
3801
|
const actualBuffer = Buffer.from(signature);
|
|
3696
3802
|
const expectedBuffer = Buffer.from(expected);
|
|
@@ -3897,11 +4003,11 @@ init_approval_v3();
|
|
|
3897
4003
|
|
|
3898
4004
|
// src/core/capability/boundary-attestation-sign.ts
|
|
3899
4005
|
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
3900
|
-
import { access, readFile as
|
|
4006
|
+
import { access, readFile as readFile6 } from "node:fs/promises";
|
|
3901
4007
|
init_fingerprint2();
|
|
3902
4008
|
|
|
3903
4009
|
// src/core/capability/attestation.ts
|
|
3904
|
-
import
|
|
4010
|
+
import path14 from "node:path";
|
|
3905
4011
|
var BOUNDARY_ATTESTATION_VERSION = 1;
|
|
3906
4012
|
var CONTAINED_EXECUTION_ATTESTATION_VERSION = 1;
|
|
3907
4013
|
var KNOWN_DRIVERS = /* @__PURE__ */ new Set([
|
|
@@ -3969,7 +4075,7 @@ function validateContainedExecutionAttestation(value) {
|
|
|
3969
4075
|
const record = value;
|
|
3970
4076
|
const dockerSubstrate = record.dockerSubstrate;
|
|
3971
4077
|
const dockerConfiguration = record.dockerConfiguration;
|
|
3972
|
-
if (record.version !== CONTAINED_EXECUTION_ATTESTATION_VERSION || typeof record.imageId !== "string" || !/^sha256:[a-f0-9]{64}$/i.test(record.imageId) || typeof record.imageReference !== "string" || !record.imageReference || /[\0\n\r]/.test(record.imageReference) || record.networkNone !== true || record.isolatesWorkspaceMirror !== true || record.readOnlyRoot !== true || record.sanitizedEnvironment !== true || !isRecord(dockerSubstrate) || typeof dockerSubstrate.binaryPath !== "string" || !
|
|
4078
|
+
if (record.version !== CONTAINED_EXECUTION_ATTESTATION_VERSION || typeof record.imageId !== "string" || !/^sha256:[a-f0-9]{64}$/i.test(record.imageId) || typeof record.imageReference !== "string" || !record.imageReference || /[\0\n\r]/.test(record.imageReference) || record.networkNone !== true || record.isolatesWorkspaceMirror !== true || record.readOnlyRoot !== true || record.sanitizedEnvironment !== true || !isRecord(dockerSubstrate) || typeof dockerSubstrate.binaryPath !== "string" || !path14.isAbsolute(dockerSubstrate.binaryPath) || /[\0\n\r]/.test(dockerSubstrate.binaryPath) || typeof dockerSubstrate.binarySha256 !== "string" || !/^[a-f0-9]{64}$/.test(dockerSubstrate.binarySha256) || typeof dockerSubstrate.endpoint !== "string" || !dockerSubstrate.endpoint.startsWith("unix:///") || !path14.isAbsolute(dockerSubstrate.endpoint.slice("unix://".length)) || /[\0\n\r]/.test(dockerSubstrate.endpoint) || typeof dockerSubstrate.daemonId !== "string" || !dockerSubstrate.daemonId || /[\0\n\r]/.test(dockerSubstrate.daemonId) || !isRecord(dockerConfiguration) || typeof dockerConfiguration.executable !== "string" || !path14.isAbsolute(dockerConfiguration.executable) || /[\0\n\r]/.test(dockerConfiguration.executable) || typeof dockerConfiguration.host !== "string" || !dockerConfiguration.host.startsWith("unix:///") || !path14.isAbsolute(dockerConfiguration.host.slice("unix://".length)) || /[\0\n\r]/.test(dockerConfiguration.host) || typeof record.user !== "string" || !/^\d+:\d+$/.test(record.user) || record.entrypoint !== "/bin/sh" || record.capDropAll !== true || record.noNewPrivileges !== true || record.logDriver !== "none" || record.proxyEnvironment !== "neutralized-empty" || typeof record.probedAt !== "string" || typeof record.expiresAt !== "string") {
|
|
3973
4079
|
return false;
|
|
3974
4080
|
}
|
|
3975
4081
|
const probedAt = Date.parse(record.probedAt);
|
|
@@ -4039,7 +4145,7 @@ async function verifySignedBoundaryAttestation(params) {
|
|
|
4039
4145
|
return record.attestation;
|
|
4040
4146
|
}
|
|
4041
4147
|
async function readSignedAttestationFile(filePath) {
|
|
4042
|
-
return JSON.parse(await
|
|
4148
|
+
return JSON.parse(await readFile6(filePath, "utf8"));
|
|
4043
4149
|
}
|
|
4044
4150
|
|
|
4045
4151
|
// src/core/capability/boundary-egress.ts
|
|
@@ -4208,13 +4314,13 @@ async function runWithBoundaryRunnable(target, params) {
|
|
|
4208
4314
|
}
|
|
4209
4315
|
|
|
4210
4316
|
// src/core/capability/boundary-session.ts
|
|
4211
|
-
import
|
|
4317
|
+
import path25 from "node:path";
|
|
4212
4318
|
|
|
4213
4319
|
// src/services/egress-service.ts
|
|
4214
|
-
import { existsSync as
|
|
4215
|
-
import { mkdir as
|
|
4320
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
4321
|
+
import { mkdir as mkdir5, readFile as readFile7, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
4216
4322
|
import net from "node:net";
|
|
4217
|
-
import
|
|
4323
|
+
import path15 from "node:path";
|
|
4218
4324
|
init_config_io();
|
|
4219
4325
|
init_config();
|
|
4220
4326
|
|
|
@@ -4227,16 +4333,16 @@ function egressStatePaths(repoRoot, config) {
|
|
|
4227
4333
|
const stateDir = belayStateDir(config, repoLocalStateDirFor(repoRoot, config));
|
|
4228
4334
|
return {
|
|
4229
4335
|
stateDir,
|
|
4230
|
-
pidPath:
|
|
4231
|
-
statusPath:
|
|
4336
|
+
pidPath: path15.join(stateDir, "egress-proxy.pid"),
|
|
4337
|
+
statusPath: path15.join(stateDir, "egress-proxy.json")
|
|
4232
4338
|
};
|
|
4233
4339
|
}
|
|
4234
4340
|
async function readStatusFile(statusPath) {
|
|
4235
|
-
if (!
|
|
4341
|
+
if (!existsSync5(statusPath)) {
|
|
4236
4342
|
return null;
|
|
4237
4343
|
}
|
|
4238
4344
|
try {
|
|
4239
|
-
const raw = JSON.parse(await
|
|
4345
|
+
const raw = JSON.parse(await readFile7(statusPath, "utf8"));
|
|
4240
4346
|
if (typeof raw.pid !== "number") {
|
|
4241
4347
|
return null;
|
|
4242
4348
|
}
|
|
@@ -4254,9 +4360,9 @@ async function readStatusFile(statusPath) {
|
|
|
4254
4360
|
async function isPortOpen(host, port) {
|
|
4255
4361
|
return new Promise((resolve) => {
|
|
4256
4362
|
const socket = net.createConnection({ host, port });
|
|
4257
|
-
const finish = (
|
|
4363
|
+
const finish = (open7) => {
|
|
4258
4364
|
socket.destroy();
|
|
4259
|
-
resolve(
|
|
4365
|
+
resolve(open7);
|
|
4260
4366
|
};
|
|
4261
4367
|
socket.setTimeout(300);
|
|
4262
4368
|
socket.on("connect", () => finish(true));
|
|
@@ -4267,7 +4373,7 @@ async function isPortOpen(host, port) {
|
|
|
4267
4373
|
async function resolveLiveEgressStatus(repoRoot, config) {
|
|
4268
4374
|
const { statusPath } = egressStatePaths(repoRoot, config);
|
|
4269
4375
|
const statusCandidates = [statusPath];
|
|
4270
|
-
const controlPlaneStatus =
|
|
4376
|
+
const controlPlaneStatus = path15.join(configuredControlPlaneDir(config), "egress-proxy.json");
|
|
4271
4377
|
if (!statusCandidates.includes(controlPlaneStatus)) {
|
|
4272
4378
|
statusCandidates.push(controlPlaneStatus);
|
|
4273
4379
|
}
|
|
@@ -4293,7 +4399,7 @@ function isProcessAlive(pid) {
|
|
|
4293
4399
|
}
|
|
4294
4400
|
}
|
|
4295
4401
|
async function egressStatus(options = {}) {
|
|
4296
|
-
const repoRoot =
|
|
4402
|
+
const repoRoot = path15.resolve(options.targetDir ?? process.cwd());
|
|
4297
4403
|
const config = await loadConfigFile(repoRoot);
|
|
4298
4404
|
const { status, host, port, portOccupied } = await resolveLiveEgressStatus(repoRoot, config);
|
|
4299
4405
|
const ownedRunning = Boolean(status);
|
|
@@ -4324,7 +4430,7 @@ init_config();
|
|
|
4324
4430
|
import { createHash as createHash8, randomUUID as randomUUID2 } from "node:crypto";
|
|
4325
4431
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4326
4432
|
import { access as access2, constants as constants2, lstat as lstat4, mkdtemp as mkdtemp2, realpath as realpath2, rm as rm2 } from "node:fs/promises";
|
|
4327
|
-
import
|
|
4433
|
+
import path20 from "node:path";
|
|
4328
4434
|
init_fingerprint2();
|
|
4329
4435
|
init_path_utils();
|
|
4330
4436
|
|
|
@@ -4503,7 +4609,7 @@ function runProcessWithBoundedOutput(file, args, options, timeoutMs, outputPolic
|
|
|
4503
4609
|
}
|
|
4504
4610
|
|
|
4505
4611
|
// src/core/contained-execution/docker-policy.ts
|
|
4506
|
-
import
|
|
4612
|
+
import path16 from "node:path";
|
|
4507
4613
|
|
|
4508
4614
|
// src/core/contained-execution/policy.ts
|
|
4509
4615
|
var CONTAINED_EXECUTION_APPROVAL_FALLBACK_REASONS = [
|
|
@@ -4632,7 +4738,7 @@ var PROXY_ENV_NAMES = [
|
|
|
4632
4738
|
var IMAGE_ID_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
4633
4739
|
var SAFE_CONTAINER_NAME = /^belay-contained-[0-9a-f-]{36}$/;
|
|
4634
4740
|
function assertSafeDockerPath(value, code) {
|
|
4635
|
-
if (!
|
|
4741
|
+
if (!path16.isAbsolute(value) || /[\0\n\r,]/.test(value)) {
|
|
4636
4742
|
throw new ContainedExecutionFailureError(code);
|
|
4637
4743
|
}
|
|
4638
4744
|
}
|
|
@@ -4711,11 +4817,11 @@ init_path_utils();
|
|
|
4711
4817
|
import { createHash as createHash7 } from "node:crypto";
|
|
4712
4818
|
import { constants as fsConstants } from "node:fs";
|
|
4713
4819
|
import {
|
|
4714
|
-
chmod as
|
|
4820
|
+
chmod as chmod3,
|
|
4715
4821
|
lstat as lstat3,
|
|
4716
|
-
mkdir as
|
|
4822
|
+
mkdir as mkdir6,
|
|
4717
4823
|
mkdtemp,
|
|
4718
|
-
open as
|
|
4824
|
+
open as open4,
|
|
4719
4825
|
opendir,
|
|
4720
4826
|
readlink as readlink2,
|
|
4721
4827
|
realpath,
|
|
@@ -4723,41 +4829,41 @@ import {
|
|
|
4723
4829
|
symlink
|
|
4724
4830
|
} from "node:fs/promises";
|
|
4725
4831
|
import os from "node:os";
|
|
4726
|
-
import
|
|
4832
|
+
import path19 from "node:path";
|
|
4727
4833
|
|
|
4728
4834
|
// src/core/transactional/file-tree.ts
|
|
4729
4835
|
import { createHash as createHash6 } from "node:crypto";
|
|
4730
4836
|
import { lstat as lstat2, readdir } from "node:fs/promises";
|
|
4731
|
-
import
|
|
4837
|
+
import path18 from "node:path";
|
|
4732
4838
|
|
|
4733
4839
|
// src/core/transactional/file-tree-path.ts
|
|
4734
4840
|
init_path_utils();
|
|
4735
|
-
import
|
|
4841
|
+
import path17 from "node:path";
|
|
4736
4842
|
var FILE_CHECKPOINT_PATH_ESCAPE = "file_checkpoint_path_escape";
|
|
4737
4843
|
function validateRelativePath(relativePath) {
|
|
4738
4844
|
if (!relativePath || relativePath.includes("\0")) {
|
|
4739
4845
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4740
4846
|
}
|
|
4741
|
-
if (
|
|
4847
|
+
if (path17.isAbsolute(relativePath)) {
|
|
4742
4848
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4743
4849
|
}
|
|
4744
4850
|
if (isPathOutsideRoot(relativePath)) {
|
|
4745
4851
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4746
4852
|
}
|
|
4747
|
-
const normalized =
|
|
4748
|
-
if (normalized === ".." || normalized.startsWith(`..${
|
|
4853
|
+
const normalized = path17.normalize(relativePath);
|
|
4854
|
+
if (normalized === ".." || normalized.startsWith(`..${path17.sep}`)) {
|
|
4749
4855
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4750
4856
|
}
|
|
4751
4857
|
}
|
|
4752
4858
|
function joinRelativePath(root, relativePath) {
|
|
4753
4859
|
validateRelativePath(relativePath);
|
|
4754
|
-
return
|
|
4860
|
+
return path17.join(canonicalPath(root), relativePath);
|
|
4755
4861
|
}
|
|
4756
4862
|
function isRootGitMetadataPath(relativePath) {
|
|
4757
|
-
return relativePath === ".git" || relativePath.startsWith(`.git${
|
|
4863
|
+
return relativePath === ".git" || relativePath.startsWith(`.git${path17.sep}`);
|
|
4758
4864
|
}
|
|
4759
4865
|
function isNestedGitPath(relativePath) {
|
|
4760
|
-
const segments = relativePath.split(
|
|
4866
|
+
const segments = relativePath.split(path17.sep).filter(Boolean);
|
|
4761
4867
|
if (segments.length === 0) {
|
|
4762
4868
|
return false;
|
|
4763
4869
|
}
|
|
@@ -4924,7 +5030,7 @@ async function readPresentNode(absolutePath, counters) {
|
|
|
4924
5030
|
async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters, quotas, deadlineMs, entries) {
|
|
4925
5031
|
assertWithinDeadline(deadlineMs);
|
|
4926
5032
|
assertWithinQuotas(counters, quotas);
|
|
4927
|
-
const absoluteDir = relativeDir ? joinRelativePath(resourceRoot, relativeDir) :
|
|
5033
|
+
const absoluteDir = relativeDir ? joinRelativePath(resourceRoot, relativeDir) : path18.resolve(resourceRoot);
|
|
4928
5034
|
const dirInfo = await lstat2(absoluteDir);
|
|
4929
5035
|
if (!dirInfo.isDirectory()) {
|
|
4930
5036
|
throw new Error(FILE_CHECKPOINT_UNSUPPORTED_NODE);
|
|
@@ -4950,7 +5056,7 @@ async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters,
|
|
|
4950
5056
|
const names = await readdir(absoluteDir);
|
|
4951
5057
|
for (const name of names) {
|
|
4952
5058
|
assertWithinDeadline(deadlineMs);
|
|
4953
|
-
const childRelative = relativeDir ?
|
|
5059
|
+
const childRelative = relativeDir ? path18.join(relativeDir, name) : name;
|
|
4954
5060
|
validateRelativePath(childRelative);
|
|
4955
5061
|
if (isNestedGitPath(childRelative)) {
|
|
4956
5062
|
throw new Error(FILE_CHECKPOINT_NESTED_REPOSITORY);
|
|
@@ -4958,7 +5064,7 @@ async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters,
|
|
|
4958
5064
|
if (isExcludedTreePath(childRelative, excludedRoots, resourceRoot)) {
|
|
4959
5065
|
continue;
|
|
4960
5066
|
}
|
|
4961
|
-
const childAbsolute =
|
|
5067
|
+
const childAbsolute = path18.join(absoluteDir, name);
|
|
4962
5068
|
const childInfo = await lstat2(childAbsolute);
|
|
4963
5069
|
if (childInfo.isDirectory() && !childInfo.isSymbolicLink()) {
|
|
4964
5070
|
await walkDirectory(
|
|
@@ -5069,16 +5175,16 @@ function validateContainedExecutionMirrorLease(handle, expected) {
|
|
|
5069
5175
|
);
|
|
5070
5176
|
}
|
|
5071
5177
|
var productionDependencies = {
|
|
5072
|
-
makeTempRoot: () => mkdtemp(
|
|
5178
|
+
makeTempRoot: () => mkdtemp(path19.join(os.tmpdir(), "belay-contained-mirror-")),
|
|
5073
5179
|
removeRoot: (root) => rm(root, { recursive: true, force: true }),
|
|
5074
5180
|
now: () => Date.now()
|
|
5075
5181
|
};
|
|
5076
5182
|
function isAtOrWithin(root, target) {
|
|
5077
|
-
const relative =
|
|
5078
|
-
return relative === "" || !
|
|
5183
|
+
const relative = path19.relative(root, target);
|
|
5184
|
+
return relative === "" || !path19.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path19.sep}`);
|
|
5079
5185
|
}
|
|
5080
5186
|
function isGitMetadataRelativePath(relativePath) {
|
|
5081
|
-
return relativePath.split(
|
|
5187
|
+
return relativePath.split(path19.sep).some((segment) => segment.toLowerCase() === ".git");
|
|
5082
5188
|
}
|
|
5083
5189
|
function identityFromStats(stats) {
|
|
5084
5190
|
return {
|
|
@@ -5160,7 +5266,7 @@ async function readStableFile(absolutePath, context) {
|
|
|
5160
5266
|
if (before.nlink > 1n) {
|
|
5161
5267
|
throw new Error(FILE_CHECKPOINT_HARDLINK_UNSUPPORTED);
|
|
5162
5268
|
}
|
|
5163
|
-
const source = await
|
|
5269
|
+
const source = await open4(absolutePath, safeReadFlags());
|
|
5164
5270
|
try {
|
|
5165
5271
|
const opened = await source.stat({ bigint: true });
|
|
5166
5272
|
assertRegularSingleLinkBigInt(opened);
|
|
@@ -5215,7 +5321,7 @@ function addMetadataRoots(context, directoryPath) {
|
|
|
5215
5321
|
}
|
|
5216
5322
|
}
|
|
5217
5323
|
function pathMatchesRoots(absolutePath, roots) {
|
|
5218
|
-
const lexical =
|
|
5324
|
+
const lexical = path19.resolve(absolutePath);
|
|
5219
5325
|
const canonical = canonicalPath(absolutePath);
|
|
5220
5326
|
for (const root of roots) {
|
|
5221
5327
|
if (isAtOrWithin(root, lexical) || isAtOrWithin(root, canonical)) {
|
|
@@ -5225,7 +5331,7 @@ function pathMatchesRoots(absolutePath, roots) {
|
|
|
5225
5331
|
return false;
|
|
5226
5332
|
}
|
|
5227
5333
|
function pathLexicallyMatchesRoots(absolutePath, roots) {
|
|
5228
|
-
const lexical =
|
|
5334
|
+
const lexical = path19.resolve(absolutePath);
|
|
5229
5335
|
for (const root of roots) {
|
|
5230
5336
|
if (isAtOrWithin(root, lexical)) {
|
|
5231
5337
|
return true;
|
|
@@ -5246,11 +5352,11 @@ async function readSafeSymlink(absolutePath, context) {
|
|
|
5246
5352
|
}
|
|
5247
5353
|
const identity = identityFromStats(before);
|
|
5248
5354
|
const target = await readlink2(absolutePath);
|
|
5249
|
-
if (
|
|
5355
|
+
if (path19.isAbsolute(target)) {
|
|
5250
5356
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_UNSAFE_SYMLINK);
|
|
5251
5357
|
}
|
|
5252
|
-
const lexicalTarget =
|
|
5253
|
-
if (!isAtOrWithin(context.sourceRoot, lexicalTarget) || isGitMetadataRelativePath(
|
|
5358
|
+
const lexicalTarget = path19.resolve(path19.dirname(absolutePath), target);
|
|
5359
|
+
if (!isAtOrWithin(context.sourceRoot, lexicalTarget) || isGitMetadataRelativePath(path19.relative(context.sourceRoot, lexicalTarget)) || pathMatchesRoots(lexicalTarget, context.protectedRoots) || pathMatchesRoots(lexicalTarget, context.metadataRoots)) {
|
|
5254
5360
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_UNSAFE_SYMLINK);
|
|
5255
5361
|
}
|
|
5256
5362
|
let resolvedTarget;
|
|
@@ -5286,7 +5392,7 @@ async function walkDirectory2(relativeDirectory, entries, context) {
|
|
|
5286
5392
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5287
5393
|
}
|
|
5288
5394
|
const directoryFlags = safeReadFlags() | (fsConstants.O_DIRECTORY ?? 0);
|
|
5289
|
-
const directory = await
|
|
5395
|
+
const directory = await open4(absoluteDirectory, directoryFlags);
|
|
5290
5396
|
try {
|
|
5291
5397
|
const opened = await directory.stat({ bigint: true });
|
|
5292
5398
|
if (!opened.isDirectory() || !identitiesEqual(beforeIdentity, identityFromStats(opened))) {
|
|
@@ -5295,7 +5401,7 @@ async function walkDirectory2(relativeDirectory, entries, context) {
|
|
|
5295
5401
|
const entriesDirectory = await opendir(absoluteDirectory, { bufferSize: 32 });
|
|
5296
5402
|
for await (const directoryEntry of entriesDirectory) {
|
|
5297
5403
|
assertDeadline(context.deadlineMs, context.now);
|
|
5298
|
-
const relativePath = relativeDirectory ?
|
|
5404
|
+
const relativePath = relativeDirectory ? path19.join(relativeDirectory, directoryEntry.name) : directoryEntry.name;
|
|
5299
5405
|
const absolutePath = joinRelativePath(context.sourceRoot, relativePath);
|
|
5300
5406
|
const info = await lstat3(absolutePath);
|
|
5301
5407
|
if (info.isSymbolicLink()) {
|
|
@@ -5379,7 +5485,7 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5379
5485
|
}
|
|
5380
5486
|
assertEntryIdentity(entry, identityFromStats(before));
|
|
5381
5487
|
await context.beforeCopyOpen?.(sourcePath);
|
|
5382
|
-
const source = await
|
|
5488
|
+
const source = await open4(sourcePath, safeReadFlags());
|
|
5383
5489
|
let destination;
|
|
5384
5490
|
try {
|
|
5385
5491
|
const opened = await source.stat({ bigint: true });
|
|
@@ -5389,8 +5495,8 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5389
5495
|
if (entry.node.kind !== "file") {
|
|
5390
5496
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5391
5497
|
}
|
|
5392
|
-
await
|
|
5393
|
-
destination = await
|
|
5498
|
+
await mkdir6(path19.dirname(destinationPath), { recursive: true, mode: 448 });
|
|
5499
|
+
destination = await open4(
|
|
5394
5500
|
destinationPath,
|
|
5395
5501
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,
|
|
5396
5502
|
safePermissionMode(opened.mode)
|
|
@@ -5432,7 +5538,7 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5432
5538
|
if (entry.node.kind !== "file") {
|
|
5433
5539
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5434
5540
|
}
|
|
5435
|
-
await
|
|
5541
|
+
await chmod3(destinationPath, safePermissionMode(entry.node.mode));
|
|
5436
5542
|
}
|
|
5437
5543
|
async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, context) {
|
|
5438
5544
|
const directoryEntries = [];
|
|
@@ -5441,7 +5547,7 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5441
5547
|
const sourcePath = joinRelativePath(sourceRoot, entry.relativePath);
|
|
5442
5548
|
const destinationPath = joinRelativePath(destinationRoot, entry.relativePath);
|
|
5443
5549
|
if (entry.node.kind === "directory") {
|
|
5444
|
-
await
|
|
5550
|
+
await mkdir6(destinationPath, { recursive: true, mode: 448 });
|
|
5445
5551
|
directoryEntries.push(entry);
|
|
5446
5552
|
continue;
|
|
5447
5553
|
}
|
|
@@ -5453,7 +5559,7 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5453
5559
|
if (current !== entry.node.target) {
|
|
5454
5560
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5455
5561
|
}
|
|
5456
|
-
await
|
|
5562
|
+
await mkdir6(path19.dirname(destinationPath), { recursive: true, mode: 448 });
|
|
5457
5563
|
await symlink(entry.node.target, destinationPath);
|
|
5458
5564
|
continue;
|
|
5459
5565
|
}
|
|
@@ -5463,12 +5569,12 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5463
5569
|
if (entry.node.kind !== "directory") {
|
|
5464
5570
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5465
5571
|
}
|
|
5466
|
-
await
|
|
5572
|
+
await chmod3(
|
|
5467
5573
|
joinRelativePath(destinationRoot, entry.relativePath),
|
|
5468
5574
|
safePermissionMode(entry.node.mode)
|
|
5469
5575
|
);
|
|
5470
5576
|
}
|
|
5471
|
-
await
|
|
5577
|
+
await chmod3(destinationRoot, 448);
|
|
5472
5578
|
}
|
|
5473
5579
|
async function assertStableCopiedSnapshot(sourceRoot, destinationRoot, protectedRoots, limits, deadlineMs, now, before) {
|
|
5474
5580
|
const [after, copied] = await Promise.all([
|
|
@@ -5513,7 +5619,7 @@ async function cleanupOwnedRoot(root, dependencies) {
|
|
|
5513
5619
|
}
|
|
5514
5620
|
async function prepareWithDependencies(options, dependencies) {
|
|
5515
5621
|
validateOptions(options);
|
|
5516
|
-
const guestWorkspacePath =
|
|
5622
|
+
const guestWorkspacePath = path19.resolve(options.sourceRoot);
|
|
5517
5623
|
const sourceRoot = canonicalPath(options.sourceRoot);
|
|
5518
5624
|
const protectedRoots = options.controlPlaneRoots.map((root) => canonicalPath(root));
|
|
5519
5625
|
if (pathMatchesRoots(sourceRoot, protectedRoots)) {
|
|
@@ -5521,7 +5627,7 @@ async function prepareWithDependencies(options, dependencies) {
|
|
|
5521
5627
|
}
|
|
5522
5628
|
const guestRoot = await dependencies.makeTempRoot();
|
|
5523
5629
|
try {
|
|
5524
|
-
await
|
|
5630
|
+
await chmod3(guestRoot, 448);
|
|
5525
5631
|
const deadlineMs = dependencies.now() + options.limits.prepareTimeoutMs;
|
|
5526
5632
|
const before = await buildSafeMirrorSnapshot(
|
|
5527
5633
|
sourceRoot,
|
|
@@ -5549,7 +5655,7 @@ async function prepareWithDependencies(options, dependencies) {
|
|
|
5549
5655
|
dependencies.now,
|
|
5550
5656
|
before
|
|
5551
5657
|
);
|
|
5552
|
-
await
|
|
5658
|
+
await chmod3(guestRoot, 448);
|
|
5553
5659
|
const lease = {
|
|
5554
5660
|
sourceRoot,
|
|
5555
5661
|
hostMirrorRoot: guestRoot,
|
|
@@ -5683,14 +5789,14 @@ async function digestFile(file) {
|
|
|
5683
5789
|
return hash.digest("hex");
|
|
5684
5790
|
}
|
|
5685
5791
|
async function resolveConfiguredDockerSubstrate(params) {
|
|
5686
|
-
if (!
|
|
5792
|
+
if (!path20.isAbsolute(params.executable) || /[\0\n\r]/.test(params.executable)) {
|
|
5687
5793
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_binary_invalid");
|
|
5688
5794
|
}
|
|
5689
5795
|
if (!params.host.startsWith("unix:///") || /[\0\n\r]/.test(params.host)) {
|
|
5690
5796
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_host_invalid");
|
|
5691
5797
|
}
|
|
5692
5798
|
const configuredSocket = params.host.slice("unix://".length);
|
|
5693
|
-
if (!
|
|
5799
|
+
if (!path20.isAbsolute(configuredSocket)) {
|
|
5694
5800
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_host_invalid");
|
|
5695
5801
|
}
|
|
5696
5802
|
let binaryPath;
|
|
@@ -5951,7 +6057,7 @@ async function validatedGuestCwd(params) {
|
|
|
5951
6057
|
const requiredExclusions = [
|
|
5952
6058
|
...new Set([params.controlPlaneDir, ...params.protectedRoots].map(canonicalPath))
|
|
5953
6059
|
];
|
|
5954
|
-
if (params.mirror.backend !== "file_copy" || params.mirror.guestWorkspacePath !==
|
|
6060
|
+
if (params.mirror.backend !== "file_copy" || params.mirror.guestWorkspacePath !== path20.resolve(params.repoRoot) || !path20.isAbsolute(params.guestCwd) || !pathWithinRoot(params.mirror.guestWorkspacePath, params.guestCwd))
|
|
5955
6061
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5956
6062
|
const resolvedRoot = await realpath2(params.mirror.hostMirrorRoot);
|
|
5957
6063
|
const protectedRoots = [canonicalPath(params.repoRoot), ...requiredExclusions];
|
|
@@ -5963,10 +6069,10 @@ async function validatedGuestCwd(params) {
|
|
|
5963
6069
|
protectedRoots: requiredExclusions
|
|
5964
6070
|
}))
|
|
5965
6071
|
throw new ContainedExecutionFailureError("contained_execution_invalid_mirror_lease");
|
|
5966
|
-
const relative =
|
|
6072
|
+
const relative = path20.relative(params.mirror.guestWorkspacePath, path20.resolve(params.guestCwd));
|
|
5967
6073
|
let resolvedCwd;
|
|
5968
6074
|
try {
|
|
5969
|
-
resolvedCwd = await realpath2(
|
|
6075
|
+
resolvedCwd = await realpath2(path20.join(resolvedRoot, relative));
|
|
5970
6076
|
} catch {
|
|
5971
6077
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5972
6078
|
}
|
|
@@ -5974,7 +6080,7 @@ async function validatedGuestCwd(params) {
|
|
|
5974
6080
|
if (!info.isDirectory() || !pathWithinRoot(resolvedRoot, resolvedCwd)) {
|
|
5975
6081
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5976
6082
|
}
|
|
5977
|
-
return
|
|
6083
|
+
return path20.join(params.mirror.guestWorkspacePath, path20.relative(resolvedRoot, resolvedCwd));
|
|
5978
6084
|
}
|
|
5979
6085
|
function exactKeys(value, allowed) {
|
|
5980
6086
|
const names = new Set(allowed);
|
|
@@ -6201,19 +6307,19 @@ async function executeContainedDocker(params) {
|
|
|
6201
6307
|
init_path_utils();
|
|
6202
6308
|
import { spawn as spawn3 } from "node:child_process";
|
|
6203
6309
|
import os3 from "node:os";
|
|
6204
|
-
import
|
|
6310
|
+
import path22 from "node:path";
|
|
6205
6311
|
|
|
6206
6312
|
// src/core/transactional/apply-observed-changes.ts
|
|
6207
|
-
import { copyFile, lstat as lstat5, mkdir as
|
|
6313
|
+
import { copyFile, lstat as lstat5, mkdir as mkdir7, mkdtemp as mkdtemp3, readlink as readlink3, rm as rm3, rmdir, symlink as symlink2 } from "node:fs/promises";
|
|
6208
6314
|
import os2 from "node:os";
|
|
6209
|
-
import
|
|
6315
|
+
import path21 from "node:path";
|
|
6210
6316
|
var TRANSACTIONAL_APPLY_TOCTOU = "transactional_apply_toctou";
|
|
6211
6317
|
var TRANSACTIONAL_APPLY_CONFLICT = "transactional_apply_conflict";
|
|
6212
6318
|
var TRANSACTIONAL_APPLY_ROLLBACK_FAILED = "transactional_apply_rollback_failed";
|
|
6213
6319
|
async function chmodSafe(target, mode) {
|
|
6214
6320
|
try {
|
|
6215
|
-
const { chmod:
|
|
6216
|
-
await
|
|
6321
|
+
const { chmod: chmod5 } = await import("node:fs/promises");
|
|
6322
|
+
await chmod5(target, mode & 511);
|
|
6217
6323
|
} catch {
|
|
6218
6324
|
}
|
|
6219
6325
|
}
|
|
@@ -6247,13 +6353,13 @@ async function copyPathPreservingType(source, target) {
|
|
|
6247
6353
|
}
|
|
6248
6354
|
}
|
|
6249
6355
|
await removePathIfExists(target);
|
|
6250
|
-
await
|
|
6356
|
+
await mkdir7(path21.dirname(target), { recursive: true });
|
|
6251
6357
|
if (info.isSymbolicLink()) {
|
|
6252
6358
|
await symlink2(await readlink3(source), target);
|
|
6253
6359
|
return;
|
|
6254
6360
|
}
|
|
6255
6361
|
if (info.isDirectory() && !info.isSymbolicLink()) {
|
|
6256
|
-
await
|
|
6362
|
+
await mkdir7(target, { recursive: true, mode: info.mode & 511 });
|
|
6257
6363
|
return;
|
|
6258
6364
|
}
|
|
6259
6365
|
if (!info.isFile()) {
|
|
@@ -6263,12 +6369,12 @@ async function copyPathPreservingType(source, target) {
|
|
|
6263
6369
|
await chmodSafe(target, info.mode);
|
|
6264
6370
|
}
|
|
6265
6371
|
async function assertParentChainSafe(targetRoot, relativePath, plannedDirectories = /* @__PURE__ */ new Set()) {
|
|
6266
|
-
const segments = relativePath.split(
|
|
6372
|
+
const segments = relativePath.split(path21.sep).filter(Boolean);
|
|
6267
6373
|
if (segments.length <= 1) {
|
|
6268
6374
|
return;
|
|
6269
6375
|
}
|
|
6270
6376
|
for (let index = 1; index < segments.length; index++) {
|
|
6271
|
-
const prefix = segments.slice(0, index).join(
|
|
6377
|
+
const prefix = segments.slice(0, index).join(path21.sep);
|
|
6272
6378
|
const absolute = joinRelativePath(targetRoot, prefix);
|
|
6273
6379
|
let info;
|
|
6274
6380
|
try {
|
|
@@ -6288,8 +6394,8 @@ async function assertParentChainSafe(targetRoot, relativePath, plannedDirectorie
|
|
|
6288
6394
|
}
|
|
6289
6395
|
}
|
|
6290
6396
|
async function restorePathFromBackup(backupPath, target, rollbackRoot) {
|
|
6291
|
-
const stagingRoot = await mkdtemp3(
|
|
6292
|
-
const staged =
|
|
6397
|
+
const stagingRoot = await mkdtemp3(path21.join(rollbackRoot, "restore-"));
|
|
6398
|
+
const staged = path21.join(stagingRoot, "node");
|
|
6293
6399
|
try {
|
|
6294
6400
|
await copyPathPreservingType(backupPath, staged);
|
|
6295
6401
|
await copyPathPreservingType(staged, target);
|
|
@@ -6373,7 +6479,7 @@ async function applySingleChange(sourceRoot, targetRoot, change) {
|
|
|
6373
6479
|
if (change.before.kind !== "directory") {
|
|
6374
6480
|
await removePathIfExists(target);
|
|
6375
6481
|
}
|
|
6376
|
-
await
|
|
6482
|
+
await mkdir7(target, { recursive: true, mode: change.after.mode & 511 });
|
|
6377
6483
|
await chmodSafe(target, change.after.mode);
|
|
6378
6484
|
return;
|
|
6379
6485
|
}
|
|
@@ -6392,7 +6498,7 @@ async function applyObservedChanges(params) {
|
|
|
6392
6498
|
await assertParentChainSafe(targetRoot, change.relativePath, plannedDirectories);
|
|
6393
6499
|
await assertTargetMatches(targetRoot, change);
|
|
6394
6500
|
}
|
|
6395
|
-
const backupRoot = await mkdtemp3(
|
|
6501
|
+
const backupRoot = await mkdtemp3(path21.join(os2.tmpdir(), "belay-tx-rollback-"));
|
|
6396
6502
|
const rollbackActions = [];
|
|
6397
6503
|
let mutationAttempted = false;
|
|
6398
6504
|
let resourceIdentityChanged = false;
|
|
@@ -6411,12 +6517,12 @@ async function applyObservedChanges(params) {
|
|
|
6411
6517
|
targetExists = false;
|
|
6412
6518
|
}
|
|
6413
6519
|
if (targetExists) {
|
|
6414
|
-
const backupPath =
|
|
6520
|
+
const backupPath = path21.join(
|
|
6415
6521
|
backupRoot,
|
|
6416
6522
|
String(rollbackActions.length),
|
|
6417
6523
|
change.relativePath
|
|
6418
6524
|
);
|
|
6419
|
-
await
|
|
6525
|
+
await mkdir7(path21.dirname(backupPath), { recursive: true });
|
|
6420
6526
|
await copyPathPreservingType(target, backupPath);
|
|
6421
6527
|
await assertParentChainSafe(targetRoot, change.relativePath);
|
|
6422
6528
|
rollbackActions.push({
|
|
@@ -6515,7 +6621,7 @@ function isIgnoredDirtyPath(repoRoot, relativePath, ignoreRoots) {
|
|
|
6515
6621
|
if (ignoreRoots.length === 0) {
|
|
6516
6622
|
return false;
|
|
6517
6623
|
}
|
|
6518
|
-
const absolutePath = canonicalPath(
|
|
6624
|
+
const absolutePath = canonicalPath(path22.join(repoRoot, relativePath));
|
|
6519
6625
|
return ignoreRoots.some(
|
|
6520
6626
|
(root) => pathWithinRoot(root, absolutePath) || root === absolutePath || pathWithinRoot(absolutePath, root)
|
|
6521
6627
|
);
|
|
@@ -6553,7 +6659,7 @@ async function isDirtyWorktree(repoRoot, options) {
|
|
|
6553
6659
|
}
|
|
6554
6660
|
async function createGitWorktreeSnapshot(repoRoot, _stateDir) {
|
|
6555
6661
|
const { mkdtemp: mkdtemp6, rm: rm10 } = await import("node:fs/promises");
|
|
6556
|
-
const worktreePath = await mkdtemp6(
|
|
6662
|
+
const worktreePath = await mkdtemp6(path22.join(os3.tmpdir(), "belay-tx-"));
|
|
6557
6663
|
await execGit(repoRoot, ["worktree", "add", "--detach", worktreePath, "HEAD"]);
|
|
6558
6664
|
return {
|
|
6559
6665
|
worktreePath,
|
|
@@ -6572,14 +6678,14 @@ async function createGitWorktreeSnapshot(repoRoot, _stateDir) {
|
|
|
6572
6678
|
}
|
|
6573
6679
|
function resolveWorktreeCwd(repoRoot, worktreePath, cwd) {
|
|
6574
6680
|
const resolvedCwd = canonicalPath(cwd);
|
|
6575
|
-
const relative =
|
|
6576
|
-
if (isPathOutsideRoot(relative) ||
|
|
6681
|
+
const relative = path22.relative(canonicalPath(repoRoot), resolvedCwd);
|
|
6682
|
+
if (isPathOutsideRoot(relative) || path22.isAbsolute(relative)) {
|
|
6577
6683
|
return worktreePath;
|
|
6578
6684
|
}
|
|
6579
6685
|
if (relative === "") {
|
|
6580
6686
|
return worktreePath;
|
|
6581
6687
|
}
|
|
6582
|
-
return
|
|
6688
|
+
return path22.join(worktreePath, relative);
|
|
6583
6689
|
}
|
|
6584
6690
|
function runShellCommand(command, cwd, timeoutMs) {
|
|
6585
6691
|
return runProcessWithBoundedOutput(command, [], { cwd, shell: true, env: process.env }, timeoutMs);
|
|
@@ -6644,7 +6750,7 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
6644
6750
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6645
6751
|
|
|
6646
6752
|
// src/core/capability/boundary-grant-materialize.ts
|
|
6647
|
-
import
|
|
6753
|
+
import path23 from "node:path";
|
|
6648
6754
|
|
|
6649
6755
|
// src/core/glob.ts
|
|
6650
6756
|
init_approval();
|
|
@@ -6804,7 +6910,7 @@ function grantMatchesRequest(grant, request) {
|
|
|
6804
6910
|
var BOUNDARY_GRANT_TTL_MS = 15 * 6e4;
|
|
6805
6911
|
var BOUNDARY_GRANT_ISSUER_CONTAINER = "boundary:container";
|
|
6806
6912
|
function resolveCapabilityPath(targetPath, cwd) {
|
|
6807
|
-
const joined =
|
|
6913
|
+
const joined = path23.isAbsolute(targetPath) ? targetPath : path23.join(cwd, targetPath);
|
|
6808
6914
|
return canonicalPath(joined);
|
|
6809
6915
|
}
|
|
6810
6916
|
function isPathWithinBoundaryMount(request) {
|
|
@@ -6879,7 +6985,7 @@ function materializeContainerBoundaryGrant(request, params) {
|
|
|
6879
6985
|
|
|
6880
6986
|
// src/core/capability/boundary-workspace-mount.ts
|
|
6881
6987
|
init_path_utils();
|
|
6882
|
-
import
|
|
6988
|
+
import path24 from "node:path";
|
|
6883
6989
|
var HOST_PATH_ENV_VARS = [
|
|
6884
6990
|
"BELAY_EGRESS_REPO_ROOT",
|
|
6885
6991
|
"BELAY_JUDGE_BROKER_REPO_ROOT",
|
|
@@ -6904,23 +7010,23 @@ function validateWorkspaceMount(mount) {
|
|
|
6904
7010
|
if (mount.cwdRelative.includes("\0")) {
|
|
6905
7011
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6906
7012
|
}
|
|
6907
|
-
const normalizedRelative =
|
|
7013
|
+
const normalizedRelative = path24.posix.normalize(mount.cwdRelative.replace(/\\/g, "/"));
|
|
6908
7014
|
if (normalizedRelative === ".." || normalizedRelative.startsWith("../")) {
|
|
6909
7015
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6910
7016
|
}
|
|
6911
|
-
if (
|
|
7017
|
+
if (path24.posix.isAbsolute(normalizedRelative)) {
|
|
6912
7018
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6913
7019
|
}
|
|
6914
7020
|
}
|
|
6915
7021
|
function resolveGuestWorkdir(mount) {
|
|
6916
7022
|
validateWorkspaceMount(mount);
|
|
6917
7023
|
const guestTargetRoot = canonicalPath(mount.guestTargetRoot);
|
|
6918
|
-
const normalizedRelative =
|
|
7024
|
+
const normalizedRelative = path24.posix.normalize(mount.cwdRelative.replace(/\\/g, "/"));
|
|
6919
7025
|
if (normalizedRelative === "." || normalizedRelative === "") {
|
|
6920
7026
|
return guestTargetRoot;
|
|
6921
7027
|
}
|
|
6922
7028
|
const segments = normalizedRelative.split("/").filter(Boolean);
|
|
6923
|
-
return
|
|
7029
|
+
return path24.posix.join(guestTargetRoot, ...segments);
|
|
6924
7030
|
}
|
|
6925
7031
|
function buildWorkspaceMountSpec(mount) {
|
|
6926
7032
|
validateWorkspaceMount(mount);
|
|
@@ -7211,7 +7317,7 @@ function containedExecutionFresh(attestation, config, now = Date.now()) {
|
|
|
7211
7317
|
}
|
|
7212
7318
|
function boundaryAttestationPath(repoRoot, config) {
|
|
7213
7319
|
const rel = config.capability?.attestationRelPath ?? ".belay/attestation.json";
|
|
7214
|
-
return
|
|
7320
|
+
return path25.isAbsolute(rel) ? rel : path25.join(repoRoot, rel);
|
|
7215
7321
|
}
|
|
7216
7322
|
async function loadBoundaryAttestation(filePath, expectedRepoRoot, controlPlaneDir) {
|
|
7217
7323
|
try {
|
|
@@ -7316,7 +7422,7 @@ init_capability_request_hash();
|
|
|
7316
7422
|
|
|
7317
7423
|
// src/core/capability/gate-policy-shadow.ts
|
|
7318
7424
|
init_config();
|
|
7319
|
-
import
|
|
7425
|
+
import path31 from "node:path";
|
|
7320
7426
|
|
|
7321
7427
|
// src/core/effect-ir/audit.ts
|
|
7322
7428
|
init_fingerprint2();
|
|
@@ -7410,8 +7516,8 @@ function evidenceRank(level) {
|
|
|
7410
7516
|
}
|
|
7411
7517
|
|
|
7412
7518
|
// src/core/effect-ir/package-exec.ts
|
|
7413
|
-
import { existsSync as
|
|
7414
|
-
import
|
|
7519
|
+
import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
|
|
7520
|
+
import path26 from "node:path";
|
|
7415
7521
|
|
|
7416
7522
|
// src/core/network-endpoint.ts
|
|
7417
7523
|
var SUPPORTED_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "ssh:", "git:"]);
|
|
@@ -7702,18 +7808,18 @@ function classifyPackageAcquisitionSpec(spec) {
|
|
|
7702
7808
|
return { kind: "registry" };
|
|
7703
7809
|
}
|
|
7704
7810
|
function resolveLocalBin(binName, cwd, repoRoot) {
|
|
7705
|
-
const base =
|
|
7811
|
+
const base = path26.basename(binName);
|
|
7706
7812
|
if (!base || base === "." || base === "..") {
|
|
7707
7813
|
return null;
|
|
7708
7814
|
}
|
|
7709
|
-
let current =
|
|
7710
|
-
const stop =
|
|
7815
|
+
let current = path26.resolve(cwd);
|
|
7816
|
+
const stop = path26.resolve(repoRoot);
|
|
7711
7817
|
if (!pathWithinRoot(stop, current)) {
|
|
7712
7818
|
return null;
|
|
7713
7819
|
}
|
|
7714
7820
|
while (true) {
|
|
7715
|
-
const candidate =
|
|
7716
|
-
if (
|
|
7821
|
+
const candidate = path26.join(current, "node_modules", ".bin", base);
|
|
7822
|
+
if (existsSync6(candidate)) {
|
|
7717
7823
|
try {
|
|
7718
7824
|
const resolvedCandidate = realpathSync3.native(candidate);
|
|
7719
7825
|
if (!pathWithinRoot(stop, resolvedCandidate) || !statSync2(resolvedCandidate).isFile()) {
|
|
@@ -7727,7 +7833,7 @@ function resolveLocalBin(binName, cwd, repoRoot) {
|
|
|
7727
7833
|
if (current === stop) {
|
|
7728
7834
|
break;
|
|
7729
7835
|
}
|
|
7730
|
-
const parent =
|
|
7836
|
+
const parent = path26.dirname(current);
|
|
7731
7837
|
if (parent === current) {
|
|
7732
7838
|
break;
|
|
7733
7839
|
}
|
|
@@ -7922,7 +8028,7 @@ init_path_utils();
|
|
|
7922
8028
|
|
|
7923
8029
|
// src/core/verdict/containment.ts
|
|
7924
8030
|
init_git_resource_identity();
|
|
7925
|
-
import
|
|
8031
|
+
import path27 from "node:path";
|
|
7926
8032
|
init_path_utils();
|
|
7927
8033
|
|
|
7928
8034
|
// src/core/verdict/persistent-paths.ts
|
|
@@ -7942,7 +8048,7 @@ function expandHome(token) {
|
|
|
7942
8048
|
if (!home) {
|
|
7943
8049
|
return token;
|
|
7944
8050
|
}
|
|
7945
|
-
return token === "~" ? home :
|
|
8051
|
+
return token === "~" ? home : path27.join(home, token.slice(2));
|
|
7946
8052
|
}
|
|
7947
8053
|
return token;
|
|
7948
8054
|
}
|
|
@@ -7954,10 +8060,10 @@ function resolveTrustedPath(token, trustedCwd, trusted) {
|
|
|
7954
8060
|
return null;
|
|
7955
8061
|
}
|
|
7956
8062
|
const expanded = expandHome(token);
|
|
7957
|
-
if (
|
|
8063
|
+
if (path27.isAbsolute(expanded)) {
|
|
7958
8064
|
return canonicalPath(expanded);
|
|
7959
8065
|
}
|
|
7960
|
-
return canonicalPath(
|
|
8066
|
+
return canonicalPath(path27.resolve(trustedCwd, expanded));
|
|
7961
8067
|
}
|
|
7962
8068
|
function isGitPath(resolvedPath, repoRoot) {
|
|
7963
8069
|
if (isGitMetadataPath(resolvedPath, repoRoot)) {
|
|
@@ -8074,20 +8180,20 @@ function parseTier1Json(raw) {
|
|
|
8074
8180
|
init_judge_runtime_config();
|
|
8075
8181
|
|
|
8076
8182
|
// src/core/verdict/judge-session-kill-switch.ts
|
|
8077
|
-
import { existsSync as
|
|
8078
|
-
import { mkdir as
|
|
8079
|
-
import
|
|
8183
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
8184
|
+
import { mkdir as mkdir8, readFile as readFile8, unlink as unlink5, writeFile as writeFile4 } from "node:fs/promises";
|
|
8185
|
+
import path28 from "node:path";
|
|
8080
8186
|
var JUDGE_SESSION_KILL_FILE = "judge-session-kill.json";
|
|
8081
8187
|
function judgeSessionKillSwitchPath(stateDir) {
|
|
8082
|
-
return
|
|
8188
|
+
return path28.join(stateDir, JUDGE_SESSION_KILL_FILE);
|
|
8083
8189
|
}
|
|
8084
8190
|
async function readJudgeSessionKillSwitch(stateDir) {
|
|
8085
8191
|
const filePath = judgeSessionKillSwitchPath(stateDir);
|
|
8086
|
-
if (!
|
|
8192
|
+
if (!existsSync7(filePath)) {
|
|
8087
8193
|
return null;
|
|
8088
8194
|
}
|
|
8089
8195
|
try {
|
|
8090
|
-
const raw = JSON.parse(await
|
|
8196
|
+
const raw = JSON.parse(await readFile8(filePath, "utf8"));
|
|
8091
8197
|
return raw.triggered === true ? raw : null;
|
|
8092
8198
|
} catch {
|
|
8093
8199
|
return null;
|
|
@@ -8098,7 +8204,7 @@ async function isJudgeSessionKillSwitchPersisted(stateDir) {
|
|
|
8098
8204
|
return record?.triggered === true;
|
|
8099
8205
|
}
|
|
8100
8206
|
async function persistJudgeSessionKillSwitch(stateDir, reason = "shadow_mismatch") {
|
|
8101
|
-
await
|
|
8207
|
+
await mkdir8(stateDir, { recursive: true, mode: 448 });
|
|
8102
8208
|
const record = {
|
|
8103
8209
|
triggered: true,
|
|
8104
8210
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8259,10 +8365,10 @@ function recordJudgeLatency(phase, latencyMs, at = Date.now()) {
|
|
|
8259
8365
|
|
|
8260
8366
|
// src/core/verdict/judge-broker-service.ts
|
|
8261
8367
|
import { spawn as spawn6 } from "node:child_process";
|
|
8262
|
-
import { existsSync as
|
|
8263
|
-
import { unlink as
|
|
8368
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
8369
|
+
import { unlink as unlink6 } from "node:fs/promises";
|
|
8264
8370
|
import net2 from "node:net";
|
|
8265
|
-
import
|
|
8371
|
+
import path29 from "node:path";
|
|
8266
8372
|
import { fileURLToPath } from "node:url";
|
|
8267
8373
|
|
|
8268
8374
|
// src/core/verdict/judge-cli.ts
|
|
@@ -8808,10 +8914,10 @@ var JUDGE_BROKER_SESSION = "judge-broker-session.json";
|
|
|
8808
8914
|
function judgeBrokerPaths(stateDir) {
|
|
8809
8915
|
return {
|
|
8810
8916
|
stateDir,
|
|
8811
|
-
socketPath:
|
|
8812
|
-
statusPath:
|
|
8813
|
-
pidPath:
|
|
8814
|
-
sessionConfigPath:
|
|
8917
|
+
socketPath: path29.join(stateDir, JUDGE_BROKER_SOCKET),
|
|
8918
|
+
statusPath: path29.join(stateDir, JUDGE_BROKER_STATUS),
|
|
8919
|
+
pidPath: path29.join(stateDir, JUDGE_BROKER_PID),
|
|
8920
|
+
sessionConfigPath: path29.join(stateDir, JUDGE_BROKER_SESSION)
|
|
8815
8921
|
};
|
|
8816
8922
|
}
|
|
8817
8923
|
function daemonScriptPath() {
|
|
@@ -8829,12 +8935,12 @@ function useInProcessBroker(env = process.env) {
|
|
|
8829
8935
|
return Boolean(env.VITEST || env.VITEST_WORKER_ID || env.BELAY_JUDGE_BROKER_IN_PROCESS === "1");
|
|
8830
8936
|
}
|
|
8831
8937
|
async function readBrokerStatus(statusPath) {
|
|
8832
|
-
if (!
|
|
8938
|
+
if (!existsSync8(statusPath)) {
|
|
8833
8939
|
return null;
|
|
8834
8940
|
}
|
|
8835
8941
|
try {
|
|
8836
|
-
const { readFile:
|
|
8837
|
-
const raw = JSON.parse(await
|
|
8942
|
+
const { readFile: readFile18 } = await import("node:fs/promises");
|
|
8943
|
+
const raw = JSON.parse(await readFile18(statusPath, "utf8"));
|
|
8838
8944
|
if (typeof raw.pid !== "number" || typeof raw.socketPath !== "string") {
|
|
8839
8945
|
return null;
|
|
8840
8946
|
}
|
|
@@ -8844,13 +8950,13 @@ async function readBrokerStatus(statusPath) {
|
|
|
8844
8950
|
}
|
|
8845
8951
|
}
|
|
8846
8952
|
async function readBrokerSessionConfig(sessionConfigPath) {
|
|
8847
|
-
if (!
|
|
8953
|
+
if (!existsSync8(sessionConfigPath)) {
|
|
8848
8954
|
return null;
|
|
8849
8955
|
}
|
|
8850
8956
|
try {
|
|
8851
|
-
const { readFile:
|
|
8957
|
+
const { readFile: readFile18 } = await import("node:fs/promises");
|
|
8852
8958
|
const raw = JSON.parse(
|
|
8853
|
-
await
|
|
8959
|
+
await readFile18(sessionConfigPath, "utf8")
|
|
8854
8960
|
);
|
|
8855
8961
|
return normalizeJudgeSessionConfig({ ...raw, enabled: true });
|
|
8856
8962
|
} catch {
|
|
@@ -8941,7 +9047,7 @@ async function ensureJudgeBrokerDaemon(stateDir, repoRoot, sessionConfig) {
|
|
|
8941
9047
|
const existingConfig = await readBrokerSessionConfig(paths.sessionConfigPath);
|
|
8942
9048
|
const configChanged = existingConfig !== null && brokerSessionConfigPayload(existingConfig) !== nextConfigPayload;
|
|
8943
9049
|
const status = await readBrokerStatus(paths.statusPath);
|
|
8944
|
-
if (status && isProcessAlive2(status.pid) &&
|
|
9050
|
+
if (status && isProcessAlive2(status.pid) && existsSync8(paths.socketPath) && !configChanged) {
|
|
8945
9051
|
await writeBrokerSessionConfig(paths, brokerSessionConfig);
|
|
8946
9052
|
return paths;
|
|
8947
9053
|
}
|
|
@@ -8963,7 +9069,7 @@ async function ensureJudgeBrokerDaemon(stateDir, repoRoot, sessionConfig) {
|
|
|
8963
9069
|
child.unref();
|
|
8964
9070
|
const deadline = Date.now() + sessionConfig.connectTimeoutMs;
|
|
8965
9071
|
while (Date.now() < deadline) {
|
|
8966
|
-
if (
|
|
9072
|
+
if (existsSync8(paths.socketPath)) {
|
|
8967
9073
|
const live = await readBrokerStatus(paths.statusPath);
|
|
8968
9074
|
if (live && isProcessAlive2(live.pid)) {
|
|
8969
9075
|
return paths;
|
|
@@ -8980,8 +9086,8 @@ async function cleanupJudgeBrokerArtifacts(paths) {
|
|
|
8980
9086
|
paths.pidPath,
|
|
8981
9087
|
paths.sessionConfigPath
|
|
8982
9088
|
]) {
|
|
8983
|
-
if (
|
|
8984
|
-
await
|
|
9089
|
+
if (existsSync8(artifact)) {
|
|
9090
|
+
await unlink6(artifact).catch(() => void 0);
|
|
8985
9091
|
}
|
|
8986
9092
|
}
|
|
8987
9093
|
}
|
|
@@ -9477,9 +9583,9 @@ async function evaluateWithJudgeTransport(request, options = {}) {
|
|
|
9477
9583
|
}
|
|
9478
9584
|
|
|
9479
9585
|
// src/core/capability/gate-shadow-ratchet.ts
|
|
9480
|
-
import { existsSync as
|
|
9481
|
-
import { mkdir as
|
|
9482
|
-
import
|
|
9586
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
9587
|
+
import { mkdir as mkdir9, readFile as readFile9, writeFile as writeFile5 } from "node:fs/promises";
|
|
9588
|
+
import path30 from "node:path";
|
|
9483
9589
|
var DEFAULT_STATE = {
|
|
9484
9590
|
version: 1,
|
|
9485
9591
|
policyJudgeComparisons: 0,
|
|
@@ -9488,15 +9594,15 @@ var DEFAULT_STATE = {
|
|
|
9488
9594
|
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
9489
9595
|
};
|
|
9490
9596
|
function ratchetPath(stateDir) {
|
|
9491
|
-
return
|
|
9597
|
+
return path30.join(stateDir, "gate-shadow-ratchet.json");
|
|
9492
9598
|
}
|
|
9493
9599
|
async function loadState(stateDir) {
|
|
9494
9600
|
const filePath = ratchetPath(stateDir);
|
|
9495
|
-
if (!
|
|
9601
|
+
if (!existsSync9(filePath)) {
|
|
9496
9602
|
return { ...DEFAULT_STATE, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9497
9603
|
}
|
|
9498
9604
|
try {
|
|
9499
|
-
const raw = JSON.parse(await
|
|
9605
|
+
const raw = JSON.parse(await readFile9(filePath, "utf8"));
|
|
9500
9606
|
return {
|
|
9501
9607
|
...DEFAULT_STATE,
|
|
9502
9608
|
...raw,
|
|
@@ -9507,7 +9613,7 @@ async function loadState(stateDir) {
|
|
|
9507
9613
|
}
|
|
9508
9614
|
}
|
|
9509
9615
|
async function saveState(stateDir, state) {
|
|
9510
|
-
await
|
|
9616
|
+
await mkdir9(stateDir, { recursive: true, mode: 448 });
|
|
9511
9617
|
await writeFile5(
|
|
9512
9618
|
ratchetPath(stateDir),
|
|
9513
9619
|
`${JSON.stringify({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
@@ -9630,7 +9736,7 @@ function scheduleGateShadowAudit(params) {
|
|
|
9630
9736
|
async function runGatePolicyShadowComparison(params) {
|
|
9631
9737
|
const judge = params.config.judge;
|
|
9632
9738
|
const runtime = normalizeJudgeRuntimeConfig(judge.runtime);
|
|
9633
|
-
const stateDir = params.stateDir ?? belayStateDir(params.config,
|
|
9739
|
+
const stateDir = params.stateDir ?? belayStateDir(params.config, path31.join(params.repoRoot, ".belay"));
|
|
9634
9740
|
const transport = await evaluateWithJudgeTransport(
|
|
9635
9741
|
{
|
|
9636
9742
|
prompt: buildTier1Prompt(params.command),
|
|
@@ -9920,15 +10026,15 @@ async function loadClassifierAuthorization(params) {
|
|
|
9920
10026
|
}
|
|
9921
10027
|
|
|
9922
10028
|
// src/core/capability/allowlist.ts
|
|
9923
|
-
import { existsSync as
|
|
10029
|
+
import { existsSync as existsSync10, readFileSync as readFileSync3 } from "node:fs";
|
|
9924
10030
|
init_config();
|
|
9925
10031
|
init_path_utils();
|
|
9926
|
-
import
|
|
10032
|
+
import path32 from "node:path";
|
|
9927
10033
|
function fsScopeAllowlistPath(config, repoLocalStateDir) {
|
|
9928
|
-
return
|
|
10034
|
+
return path32.join(belayStateDir(config, repoLocalStateDir), "fs-scope-allowlist.json");
|
|
9929
10035
|
}
|
|
9930
10036
|
function loadFsScopeAllowlistSync(filePath) {
|
|
9931
|
-
if (!
|
|
10037
|
+
if (!existsSync10(filePath)) {
|
|
9932
10038
|
return { version: 1, paths: [] };
|
|
9933
10039
|
}
|
|
9934
10040
|
const raw = JSON.parse(readFileSync3(filePath, "utf8"));
|
|
@@ -10020,7 +10126,7 @@ function checkGatedActionLimits(action) {
|
|
|
10020
10126
|
// src/core/capability/paths.ts
|
|
10021
10127
|
init_path_utils();
|
|
10022
10128
|
init_shell_tokenizer();
|
|
10023
|
-
import
|
|
10129
|
+
import path33 from "node:path";
|
|
10024
10130
|
function applyPatchTargets(patch) {
|
|
10025
10131
|
const targets = [];
|
|
10026
10132
|
for (const line of patch.split("\n")) {
|
|
@@ -10085,7 +10191,7 @@ function collectOutsideRepoPathsFromToolPayload(payload, cwd, repoRoot, trustedW
|
|
|
10085
10191
|
const paths = /* @__PURE__ */ new Set();
|
|
10086
10192
|
const filePath = extractToolFilePath(payload);
|
|
10087
10193
|
if (filePath) {
|
|
10088
|
-
const resolved =
|
|
10194
|
+
const resolved = path33.isAbsolute(filePath) ? filePath : path33.resolve(cwd, filePath);
|
|
10089
10195
|
if (resolveWorkspaceRootMatch(repoRoot, trustedWorkspaceRoots, resolved) === null) {
|
|
10090
10196
|
paths.add(resolved);
|
|
10091
10197
|
}
|
|
@@ -10095,7 +10201,7 @@ function collectOutsideRepoPathsFromToolPayload(payload, cwd, repoRoot, trustedW
|
|
|
10095
10201
|
const patch = extractToolPatch(payload);
|
|
10096
10202
|
if (patch) {
|
|
10097
10203
|
for (const target of applyPatchTargets(patch)) {
|
|
10098
|
-
const resolved =
|
|
10204
|
+
const resolved = path33.isAbsolute(target) ? target : path33.resolve(cwd, target);
|
|
10099
10205
|
if (resolveWorkspaceRootMatch(repoRoot, trustedWorkspaceRoots, resolved) === null) {
|
|
10100
10206
|
paths.add(resolved);
|
|
10101
10207
|
}
|
|
@@ -10149,7 +10255,7 @@ function policyReasonToLegacyReason(decision) {
|
|
|
10149
10255
|
// src/core/capability/policy-engine.ts
|
|
10150
10256
|
init_git_resource_identity();
|
|
10151
10257
|
import { createHash as createHash10 } from "node:crypto";
|
|
10152
|
-
import
|
|
10258
|
+
import path34 from "node:path";
|
|
10153
10259
|
init_path_utils();
|
|
10154
10260
|
init_shell_tokenizer();
|
|
10155
10261
|
init_grant();
|
|
@@ -10441,7 +10547,7 @@ function hashSession(value) {
|
|
|
10441
10547
|
return createHash10("sha256").update(value).digest("hex").slice(0, 16);
|
|
10442
10548
|
}
|
|
10443
10549
|
function resolveCapabilityPath2(targetPath, cwd) {
|
|
10444
|
-
const joined =
|
|
10550
|
+
const joined = path34.isAbsolute(targetPath) ? targetPath : path34.join(cwd, targetPath);
|
|
10445
10551
|
return canonicalPath(joined);
|
|
10446
10552
|
}
|
|
10447
10553
|
function actionForFileMutation(analysis) {
|
|
@@ -10520,7 +10626,7 @@ function isRepoLocalPackageExec(request) {
|
|
|
10520
10626
|
return false;
|
|
10521
10627
|
}
|
|
10522
10628
|
const commandPath = canonicalPath(request.resource.command);
|
|
10523
|
-
return
|
|
10629
|
+
return path34.isAbsolute(commandPath) && pathWithinRoot(request.principal.repoRoot, commandPath);
|
|
10524
10630
|
}
|
|
10525
10631
|
function isRepoLocalRoutineWrite(request, sensitivePaths) {
|
|
10526
10632
|
if (!isRepoLocalShellLocation(request)) {
|
|
@@ -10784,15 +10890,15 @@ function shouldSkipBrokerApprovedRecord(brokerActive, approvalReason) {
|
|
|
10784
10890
|
}
|
|
10785
10891
|
|
|
10786
10892
|
// src/core/capability/trusted-workspace-roots.ts
|
|
10787
|
-
import { existsSync as
|
|
10893
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
10788
10894
|
init_config();
|
|
10789
10895
|
init_path_utils();
|
|
10790
|
-
import
|
|
10896
|
+
import path35 from "node:path";
|
|
10791
10897
|
function trustedWorkspaceRootsPath(config, repoLocalStateDir) {
|
|
10792
|
-
return
|
|
10898
|
+
return path35.join(belayStateDir(config, repoLocalStateDir), "trusted-workspace-roots.json");
|
|
10793
10899
|
}
|
|
10794
10900
|
function loadTrustedWorkspaceRootsSync(filePath) {
|
|
10795
|
-
if (!
|
|
10901
|
+
if (!existsSync11(filePath)) {
|
|
10796
10902
|
return { version: 1, roots: [] };
|
|
10797
10903
|
}
|
|
10798
10904
|
const raw = JSON.parse(readFileSync4(filePath, "utf8"));
|
|
@@ -10818,7 +10924,7 @@ function sanitizeTrustedWorkspaceRootEntries(input) {
|
|
|
10818
10924
|
const source = record.source === "approval" ? "approval" : void 0;
|
|
10819
10925
|
return [
|
|
10820
10926
|
{
|
|
10821
|
-
path:
|
|
10927
|
+
path: path35.resolve(record.path),
|
|
10822
10928
|
approvedAt,
|
|
10823
10929
|
approvalId,
|
|
10824
10930
|
...source ? { source } : {}
|
|
@@ -10847,7 +10953,7 @@ function isDirectoryPath(targetPath) {
|
|
|
10847
10953
|
function isBroadTrustedWorkspaceRoot(targetPath) {
|
|
10848
10954
|
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
10849
10955
|
const root = normalizeTrustedWorkspaceRootPath(targetPath);
|
|
10850
|
-
if (root === normalizeTrustedWorkspaceRootPath(
|
|
10956
|
+
if (root === normalizeTrustedWorkspaceRootPath(path35.parse(root).root)) {
|
|
10851
10957
|
return true;
|
|
10852
10958
|
}
|
|
10853
10959
|
if (home && normalizeTrustedWorkspaceRootPath(home) === root) {
|
|
@@ -10871,7 +10977,7 @@ function isHighStakesTrustedWorkspaceRoot(targetPath) {
|
|
|
10871
10977
|
return false;
|
|
10872
10978
|
}
|
|
10873
10979
|
return HOME_HIGH_STAKES_SEGMENTS.some(
|
|
10874
|
-
(segment) => pathWithinRoot(
|
|
10980
|
+
(segment) => pathWithinRoot(path35.join(homeRoot, segment), normalized)
|
|
10875
10981
|
);
|
|
10876
10982
|
}
|
|
10877
10983
|
function validateTrustedWorkspaceRootCandidate(params) {
|
|
@@ -10908,7 +11014,7 @@ function validateTrustedWorkspaceRootCandidate(params) {
|
|
|
10908
11014
|
init_config_layers();
|
|
10909
11015
|
|
|
10910
11016
|
// src/core/contained-execution/eligibility.ts
|
|
10911
|
-
import
|
|
11017
|
+
import path36 from "node:path";
|
|
10912
11018
|
init_path_utils();
|
|
10913
11019
|
var FORBIDDEN_SIGNALS = /* @__PURE__ */ new Set([
|
|
10914
11020
|
"command_substitution",
|
|
@@ -10925,7 +11031,7 @@ var FORBIDDEN_SIGNALS = /* @__PURE__ */ new Set([
|
|
|
10925
11031
|
]);
|
|
10926
11032
|
function isContainedUnknownExecutionEligible(config, action, result) {
|
|
10927
11033
|
const contained = config.sandbox.containedExecution;
|
|
10928
|
-
if (!contained?.enabled || !config.sandbox.enabled || config.sandbox.runtime !== "container" || action.kind !== "shell" || !
|
|
11034
|
+
if (!contained?.enabled || !config.sandbox.enabled || config.sandbox.runtime !== "container" || action.kind !== "shell" || !path36.isAbsolute(action.repoRoot) || !hasResolvedRepoIdentity(action.repoRoot) || !config.gates.shell || result.reason !== "unknown_local_effect" || result.axes?.location !== "repo_local" || result.assessment.external) {
|
|
10929
11035
|
return false;
|
|
10930
11036
|
}
|
|
10931
11037
|
const plan = result.effectPlan;
|
|
@@ -11205,7 +11311,7 @@ function classifySubagent(payload, repoRoot, options = {}, config) {
|
|
|
11205
11311
|
}
|
|
11206
11312
|
|
|
11207
11313
|
// src/core/classify-tool.ts
|
|
11208
|
-
import
|
|
11314
|
+
import path51 from "node:path";
|
|
11209
11315
|
init_fingerprint2();
|
|
11210
11316
|
init_path_utils();
|
|
11211
11317
|
init_scrub();
|
|
@@ -11325,13 +11431,13 @@ function worstEffectDecision(decisions) {
|
|
|
11325
11431
|
|
|
11326
11432
|
// src/core/effect-ir/shell-lower.ts
|
|
11327
11433
|
init_shell_tokenizer();
|
|
11328
|
-
import
|
|
11434
|
+
import path50 from "node:path";
|
|
11329
11435
|
|
|
11330
11436
|
// src/core/verdict/docker-compose-run.ts
|
|
11331
|
-
import
|
|
11437
|
+
import path38 from "node:path";
|
|
11332
11438
|
|
|
11333
11439
|
// src/core/verdict/recursive-invocation.ts
|
|
11334
|
-
import
|
|
11440
|
+
import path37 from "node:path";
|
|
11335
11441
|
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish"]);
|
|
11336
11442
|
var PYTHON_INTERPRETERS = /* @__PURE__ */ new Set(["python", "python3"]);
|
|
11337
11443
|
var SHELL_SHORT_OPTIONS = /* @__PURE__ */ new Set(["c", "l", "e", "x", "u"]);
|
|
@@ -11393,7 +11499,7 @@ var OSASCRIPT_PROFILE = {
|
|
|
11393
11499
|
attachedValuePrefixes: []
|
|
11394
11500
|
};
|
|
11395
11501
|
function normalizeInterpreter(value) {
|
|
11396
|
-
return
|
|
11502
|
+
return path37.basename(value);
|
|
11397
11503
|
}
|
|
11398
11504
|
function scriptResult(interpreter, token) {
|
|
11399
11505
|
if (!token) {
|
|
@@ -11623,7 +11729,7 @@ function parseOptions(words, start, options) {
|
|
|
11623
11729
|
function decodeDockerComposeRun(tokens) {
|
|
11624
11730
|
if (tokens.some((token) => token.kind === "operator")) return { kind: "none" };
|
|
11625
11731
|
const words = tokens.filter((token) => token.kind === "word");
|
|
11626
|
-
const head =
|
|
11732
|
+
const head = path38.basename(words[0]?.value ?? "");
|
|
11627
11733
|
let index;
|
|
11628
11734
|
if (head === "docker-compose") {
|
|
11629
11735
|
index = 1;
|
|
@@ -11664,7 +11770,7 @@ function decodeDockerComposeRun(tokens) {
|
|
|
11664
11770
|
}
|
|
11665
11771
|
|
|
11666
11772
|
// src/core/verdict/egress-classify.ts
|
|
11667
|
-
import
|
|
11773
|
+
import path39 from "node:path";
|
|
11668
11774
|
var EGRESS_TOOL_HEADS = /* @__PURE__ */ new Set([
|
|
11669
11775
|
"aws",
|
|
11670
11776
|
"curl",
|
|
@@ -11713,7 +11819,7 @@ function isEgressToolHead(head) {
|
|
|
11713
11819
|
return EGRESS_TOOL_HEADS.has(head);
|
|
11714
11820
|
}
|
|
11715
11821
|
function decodeEgressEffects(params) {
|
|
11716
|
-
const head =
|
|
11822
|
+
const head = path39.basename(params.tokens[0] ?? "");
|
|
11717
11823
|
if (head !== "curl" && head !== "wget" && head !== "gh") {
|
|
11718
11824
|
return null;
|
|
11719
11825
|
}
|
|
@@ -11721,7 +11827,7 @@ function decodeEgressEffects(params) {
|
|
|
11721
11827
|
const provenance = { segment: params.segment };
|
|
11722
11828
|
const requirements = [];
|
|
11723
11829
|
for (const file of decoded.files) {
|
|
11724
|
-
const resolved =
|
|
11830
|
+
const resolved = path39.resolve(params.cwd, expandHome2(file));
|
|
11725
11831
|
requirements.push(
|
|
11726
11832
|
requirement("fs.read", "fs.read", { kind: "path", path: resolved }, params.segment, [
|
|
11727
11833
|
"egress.explicit_file_read"
|
|
@@ -11743,7 +11849,7 @@ function decodeEgressEffects(params) {
|
|
|
11743
11849
|
if (file === "-") {
|
|
11744
11850
|
continue;
|
|
11745
11851
|
}
|
|
11746
|
-
const resolved =
|
|
11852
|
+
const resolved = path39.resolve(params.cwd, expandHome2(file));
|
|
11747
11853
|
if (resolved === "/dev/null") {
|
|
11748
11854
|
continue;
|
|
11749
11855
|
}
|
|
@@ -12221,13 +12327,13 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
12221
12327
|
if (head === "wget" && !explicitOutput) {
|
|
12222
12328
|
outputFiles.push(
|
|
12223
12329
|
...endpointOutputNames.map(
|
|
12224
|
-
(name) => outputDirectory ?
|
|
12330
|
+
(name) => outputDirectory ? path39.join(outputDirectory, name) : name
|
|
12225
12331
|
)
|
|
12226
12332
|
);
|
|
12227
12333
|
} else if (head === "curl" && remoteNameOutput) {
|
|
12228
12334
|
outputFiles.push(
|
|
12229
12335
|
...endpointOutputNames.map(
|
|
12230
|
-
(name) => outputDirectory ?
|
|
12336
|
+
(name) => outputDirectory ? path39.join(outputDirectory, name) : name
|
|
12231
12337
|
)
|
|
12232
12338
|
);
|
|
12233
12339
|
}
|
|
@@ -12241,7 +12347,7 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
12241
12347
|
outputFiles: [
|
|
12242
12348
|
...new Set(
|
|
12243
12349
|
outputFiles.map(
|
|
12244
|
-
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !
|
|
12350
|
+
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !path39.isAbsolute(file) ? path39.join(outputDirectory, file) : file
|
|
12245
12351
|
)
|
|
12246
12352
|
)
|
|
12247
12353
|
],
|
|
@@ -12256,7 +12362,7 @@ function remoteOutputName(spec) {
|
|
|
12256
12362
|
} catch {
|
|
12257
12363
|
pathname = spec.split(/[?#]/, 1)[0] ?? "";
|
|
12258
12364
|
}
|
|
12259
|
-
const name =
|
|
12365
|
+
const name = path39.posix.basename(pathname);
|
|
12260
12366
|
return name && name !== "/" ? name : "index.html";
|
|
12261
12367
|
}
|
|
12262
12368
|
function decodeGhGrammar(tokens) {
|
|
@@ -12421,7 +12527,7 @@ function expandHome2(value) {
|
|
|
12421
12527
|
return process.env.HOME ?? value;
|
|
12422
12528
|
}
|
|
12423
12529
|
if (value.startsWith("~/")) {
|
|
12424
|
-
return
|
|
12530
|
+
return path39.join(process.env.HOME ?? "~", value.slice(2));
|
|
12425
12531
|
}
|
|
12426
12532
|
return value;
|
|
12427
12533
|
}
|
|
@@ -12440,7 +12546,7 @@ function requirement(tag, action, resource, segment, signals) {
|
|
|
12440
12546
|
}
|
|
12441
12547
|
|
|
12442
12548
|
// src/core/verdict/git-classifier.ts
|
|
12443
|
-
import
|
|
12549
|
+
import path40 from "node:path";
|
|
12444
12550
|
init_shell_tokenizer();
|
|
12445
12551
|
var GIT_BRANCH_MUTATION_FLAGS = /* @__PURE__ */ new Set([
|
|
12446
12552
|
"--copy",
|
|
@@ -12536,7 +12642,7 @@ var FILE_OPERAND_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
12536
12642
|
var COMPOUND_SUBCOMMAND_HEADS = /* @__PURE__ */ new Set(["worktree", "stash", "tag"]);
|
|
12537
12643
|
var REF_ONLY_WITHOUT_TERMINATOR = /* @__PURE__ */ new Set(["checkout", "show", "log"]);
|
|
12538
12644
|
function isGitExecutable(token) {
|
|
12539
|
-
return
|
|
12645
|
+
return path40.basename(token) === "git";
|
|
12540
12646
|
}
|
|
12541
12647
|
function takesValue(flag) {
|
|
12542
12648
|
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=");
|
|
@@ -12564,7 +12670,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12564
12670
|
if (token === "-C" || token === "--work-tree" || token === "--git-dir" || token === "-c") {
|
|
12565
12671
|
const value = tokens[index + 1];
|
|
12566
12672
|
if (token === "-C" && value) {
|
|
12567
|
-
effectiveCwd =
|
|
12673
|
+
effectiveCwd = path40.resolve(baseCwd, value);
|
|
12568
12674
|
} else if (token === "--work-tree" && value) {
|
|
12569
12675
|
workTree = value;
|
|
12570
12676
|
} else if (token === "--git-dir" && value) {
|
|
@@ -12574,7 +12680,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12574
12680
|
continue;
|
|
12575
12681
|
}
|
|
12576
12682
|
if (token.startsWith("-C") && token.length > 2) {
|
|
12577
|
-
effectiveCwd =
|
|
12683
|
+
effectiveCwd = path40.resolve(baseCwd, token.slice(2));
|
|
12578
12684
|
index += 1;
|
|
12579
12685
|
continue;
|
|
12580
12686
|
}
|
|
@@ -12717,7 +12823,7 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12717
12823
|
if (!looksLikeFileOperand(token)) {
|
|
12718
12824
|
return false;
|
|
12719
12825
|
}
|
|
12720
|
-
if (token.startsWith(".") ||
|
|
12826
|
+
if (token.startsWith(".") || path40.isAbsolute(token)) {
|
|
12721
12827
|
return true;
|
|
12722
12828
|
}
|
|
12723
12829
|
return token.includes(".");
|
|
@@ -12725,12 +12831,12 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12725
12831
|
function resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir) {
|
|
12726
12832
|
const resolveBase = effectiveCwd ?? baseCwd;
|
|
12727
12833
|
if (workTree) {
|
|
12728
|
-
return
|
|
12834
|
+
return path40.resolve(resolveBase, workTree);
|
|
12729
12835
|
}
|
|
12730
12836
|
if (gitDir) {
|
|
12731
|
-
const resolvedGitDir =
|
|
12732
|
-
if (
|
|
12733
|
-
return
|
|
12837
|
+
const resolvedGitDir = path40.resolve(resolveBase, gitDir);
|
|
12838
|
+
if (path40.basename(resolvedGitDir) === ".git") {
|
|
12839
|
+
return path40.dirname(resolvedGitDir);
|
|
12734
12840
|
}
|
|
12735
12841
|
}
|
|
12736
12842
|
return void 0;
|
|
@@ -12811,7 +12917,7 @@ function classifyGitCommand(tokens, baseCwd) {
|
|
|
12811
12917
|
const { subcommand, args, effectiveCwd, gitDir, workTree } = normalized;
|
|
12812
12918
|
const normalizedKey = `git ${subcommand}`;
|
|
12813
12919
|
const gitWorkTree = resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir);
|
|
12814
|
-
const effectiveGitDir = gitDir ?
|
|
12920
|
+
const effectiveGitDir = gitDir ? path40.resolve(effectiveCwd ?? baseCwd, gitDir) : void 0;
|
|
12815
12921
|
const scopeTargets = [effectiveCwd, gitWorkTree, effectiveGitDir].filter(
|
|
12816
12922
|
(target, index, targets) => Boolean(target) && targets.indexOf(target) === index
|
|
12817
12923
|
);
|
|
@@ -12963,9 +13069,9 @@ function decodeGitEffects(params) {
|
|
|
12963
13069
|
...subcommand === "push" ? ["tier0_external"] : []
|
|
12964
13070
|
];
|
|
12965
13071
|
const effectiveCwd = normalized.effectiveCwd ?? params.cwd;
|
|
12966
|
-
const workTreeRoot = normalized.workTree ?
|
|
12967
|
-
const gitRefRoot = normalized.gitDir ?
|
|
12968
|
-
const gitControlRoot = normalized.gitDir ? gitRefRoot :
|
|
13072
|
+
const workTreeRoot = normalized.workTree ? path40.resolve(normalized.effectiveCwd ?? params.cwd, normalized.workTree) : normalized.effectiveCwd ?? params.repoRoot;
|
|
13073
|
+
const gitRefRoot = normalized.gitDir ? path40.resolve(effectiveCwd, normalized.gitDir) : workTreeRoot;
|
|
13074
|
+
const gitControlRoot = normalized.gitDir ? gitRefRoot : path40.join(gitRefRoot, ".git");
|
|
12969
13075
|
const requirements = [];
|
|
12970
13076
|
if (subcommand === "fetch" || subcommand === "pull") {
|
|
12971
13077
|
const positionals = gitRemotePositionals(args);
|
|
@@ -13124,7 +13230,7 @@ function decodeGitEffects(params) {
|
|
|
13124
13230
|
gitRequirement(
|
|
13125
13231
|
"control_plane.write",
|
|
13126
13232
|
"control_plane.write",
|
|
13127
|
-
{ kind: "path", path:
|
|
13233
|
+
{ kind: "path", path: path40.join(gitControlRoot, "logs") },
|
|
13128
13234
|
params.segment,
|
|
13129
13235
|
[...signals, "git_history_destructive", "git.reflog.mutate"]
|
|
13130
13236
|
)
|
|
@@ -13182,7 +13288,7 @@ function decodeGitEffects(params) {
|
|
|
13182
13288
|
gitRequirement(
|
|
13183
13289
|
"fs.read",
|
|
13184
13290
|
"fs.read",
|
|
13185
|
-
{ kind: "path", path:
|
|
13291
|
+
{ kind: "path", path: path40.resolve(workTreeRoot, operand) },
|
|
13186
13292
|
params.segment,
|
|
13187
13293
|
[...signals, "git.path.read"]
|
|
13188
13294
|
)
|
|
@@ -13217,7 +13323,7 @@ function decodeGitEffects(params) {
|
|
|
13217
13323
|
gitRequirement(
|
|
13218
13324
|
"fs.write",
|
|
13219
13325
|
"fs.write",
|
|
13220
|
-
{ kind: "path", path:
|
|
13326
|
+
{ kind: "path", path: path40.resolve(workTreeRoot, operand) },
|
|
13221
13327
|
params.segment,
|
|
13222
13328
|
[...signals, "git.path.write"]
|
|
13223
13329
|
)
|
|
@@ -13401,8 +13507,8 @@ function gitRequirement(tag, action, resource, segment, signals) {
|
|
|
13401
13507
|
}
|
|
13402
13508
|
|
|
13403
13509
|
// src/core/verdict/launcher-resolve.ts
|
|
13404
|
-
import { existsSync as
|
|
13405
|
-
import
|
|
13510
|
+
import { existsSync as existsSync12, readFileSync as readFileSync5 } from "node:fs";
|
|
13511
|
+
import path41 from "node:path";
|
|
13406
13512
|
|
|
13407
13513
|
// src/core/verdict/makefile-expand.ts
|
|
13408
13514
|
var MAX_EXPAND_DEPTH = 16;
|
|
@@ -13575,8 +13681,8 @@ var PNPM_BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
13575
13681
|
"why"
|
|
13576
13682
|
]);
|
|
13577
13683
|
function readPackageJson(dir) {
|
|
13578
|
-
const packagePath =
|
|
13579
|
-
if (!
|
|
13684
|
+
const packagePath = path41.join(dir, "package.json");
|
|
13685
|
+
if (!existsSync12(packagePath)) {
|
|
13580
13686
|
return null;
|
|
13581
13687
|
}
|
|
13582
13688
|
try {
|
|
@@ -13586,17 +13692,17 @@ function readPackageJson(dir) {
|
|
|
13586
13692
|
}
|
|
13587
13693
|
}
|
|
13588
13694
|
function findPackageJson(startDir, stopDir) {
|
|
13589
|
-
let current =
|
|
13590
|
-
const stop =
|
|
13695
|
+
let current = path41.resolve(startDir);
|
|
13696
|
+
const stop = path41.resolve(stopDir);
|
|
13591
13697
|
while (true) {
|
|
13592
|
-
const packagePath =
|
|
13593
|
-
if (
|
|
13698
|
+
const packagePath = path41.join(current, "package.json");
|
|
13699
|
+
if (existsSync12(packagePath)) {
|
|
13594
13700
|
return packagePath;
|
|
13595
13701
|
}
|
|
13596
|
-
if (current === stop || current ===
|
|
13597
|
-
return
|
|
13702
|
+
if (current === stop || current === path41.dirname(current)) {
|
|
13703
|
+
return existsSync12(packagePath) ? packagePath : null;
|
|
13598
13704
|
}
|
|
13599
|
-
const parent =
|
|
13705
|
+
const parent = path41.dirname(current);
|
|
13600
13706
|
if (!parent.startsWith(stop) && parent !== current) {
|
|
13601
13707
|
}
|
|
13602
13708
|
if (parent === current) {
|
|
@@ -13653,7 +13759,7 @@ function resolveNpmRecipe(cwd, repoRoot, scriptName, extraArgs) {
|
|
|
13653
13759
|
}
|
|
13654
13760
|
return { recipes: [], opaque: true, reason: "package_json_missing" };
|
|
13655
13761
|
}
|
|
13656
|
-
const pkg = readPackageJson(
|
|
13762
|
+
const pkg = readPackageJson(path41.dirname(packagePath));
|
|
13657
13763
|
const scripts = pkg?.scripts;
|
|
13658
13764
|
if (!scripts || typeof scripts !== "object") {
|
|
13659
13765
|
return { recipes: [], opaque: true, reason: "package_scripts_missing" };
|
|
@@ -13744,20 +13850,20 @@ function parseMakefileRecipeContent(content) {
|
|
|
13744
13850
|
function resolveMakeRecipe(cwd, repoRoot, target, cliVars = {}) {
|
|
13745
13851
|
const candidates = ["Makefile", "makefile", "GNUmakefile"];
|
|
13746
13852
|
let makefilePath = null;
|
|
13747
|
-
let searchDir =
|
|
13748
|
-
const stop =
|
|
13853
|
+
let searchDir = path41.resolve(cwd);
|
|
13854
|
+
const stop = path41.resolve(repoRoot);
|
|
13749
13855
|
while (true) {
|
|
13750
13856
|
for (const name of candidates) {
|
|
13751
|
-
const candidate =
|
|
13752
|
-
if (
|
|
13857
|
+
const candidate = path41.join(searchDir, name);
|
|
13858
|
+
if (existsSync12(candidate)) {
|
|
13753
13859
|
makefilePath = candidate;
|
|
13754
13860
|
break;
|
|
13755
13861
|
}
|
|
13756
13862
|
}
|
|
13757
|
-
if (makefilePath || searchDir === stop || searchDir ===
|
|
13863
|
+
if (makefilePath || searchDir === stop || searchDir === path41.dirname(searchDir)) {
|
|
13758
13864
|
break;
|
|
13759
13865
|
}
|
|
13760
|
-
searchDir =
|
|
13866
|
+
searchDir = path41.dirname(searchDir);
|
|
13761
13867
|
}
|
|
13762
13868
|
if (!makefilePath) {
|
|
13763
13869
|
return { recipes: [], opaque: true, reason: "unknown_local_effect" };
|
|
@@ -13784,7 +13890,7 @@ function resolveMakeRecipe(cwd, repoRoot, target, cliVars = {}) {
|
|
|
13784
13890
|
}
|
|
13785
13891
|
const entry = targets.get(name);
|
|
13786
13892
|
if (!entry) {
|
|
13787
|
-
if (!
|
|
13893
|
+
if (!existsSync12(path41.resolve(path41.dirname(makefilePath), name))) {
|
|
13788
13894
|
hasUndefinedPrerequisite = true;
|
|
13789
13895
|
}
|
|
13790
13896
|
return;
|
|
@@ -13883,7 +13989,7 @@ function resolveLauncherRecipe(params) {
|
|
|
13883
13989
|
}
|
|
13884
13990
|
|
|
13885
13991
|
// src/core/verdict/parser.ts
|
|
13886
|
-
import
|
|
13992
|
+
import path42 from "node:path";
|
|
13887
13993
|
|
|
13888
13994
|
// src/core/shell-substitution.ts
|
|
13889
13995
|
function findStructuralCommandSubstitutions(command) {
|
|
@@ -14123,7 +14229,7 @@ var INTERPRETER_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
14123
14229
|
".sh"
|
|
14124
14230
|
]);
|
|
14125
14231
|
function normalizeHead(token) {
|
|
14126
|
-
const base =
|
|
14232
|
+
const base = path42.basename(token);
|
|
14127
14233
|
if (base && base !== "." && base !== "..") {
|
|
14128
14234
|
return base;
|
|
14129
14235
|
}
|
|
@@ -14433,7 +14539,7 @@ function isBareInterpreter(tokens) {
|
|
|
14433
14539
|
return false;
|
|
14434
14540
|
}
|
|
14435
14541
|
const scriptArg = args.find((token) => !token.startsWith("-"));
|
|
14436
|
-
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(
|
|
14542
|
+
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(path42.extname(scriptArg))) {
|
|
14437
14543
|
return false;
|
|
14438
14544
|
}
|
|
14439
14545
|
if (scriptArg) {
|
|
@@ -14673,7 +14779,7 @@ function mergeNodes(nodes) {
|
|
|
14673
14779
|
}
|
|
14674
14780
|
|
|
14675
14781
|
// src/core/effect-ir/shell-lower/argv-delegate-gate.ts
|
|
14676
|
-
import
|
|
14782
|
+
import path43 from "node:path";
|
|
14677
14783
|
var ARGV_DELEGATE_INNER_BLOCKLIST = /* @__PURE__ */ new Set([
|
|
14678
14784
|
"sudo",
|
|
14679
14785
|
"env",
|
|
@@ -14694,7 +14800,7 @@ function shouldApplyArgvDelegate(head, innerTokens, depth) {
|
|
|
14694
14800
|
if (ARGV_DELEGATE_INNER_BLOCKLIST.has(head)) {
|
|
14695
14801
|
return false;
|
|
14696
14802
|
}
|
|
14697
|
-
const innerHead =
|
|
14803
|
+
const innerHead = path43.basename(innerTokens[0] ?? "");
|
|
14698
14804
|
if (ARGV_DELEGATE_INNER_BLOCKLIST.has(innerHead)) {
|
|
14699
14805
|
return false;
|
|
14700
14806
|
}
|
|
@@ -14801,7 +14907,7 @@ function withInnerProvenance(requirementValue, innerCommand, launcher, outerSegm
|
|
|
14801
14907
|
|
|
14802
14908
|
// src/core/effect-ir/shell-lower/tokens.ts
|
|
14803
14909
|
init_shell_tokenizer();
|
|
14804
|
-
import
|
|
14910
|
+
import path44 from "node:path";
|
|
14805
14911
|
var ENV_PREFIX_PATTERN2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
|
|
14806
14912
|
var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "0:0:0:0:0:0:0:1"]);
|
|
14807
14913
|
var METADATA_ONLY_FLAGS = /* @__PURE__ */ new Set(["--version", "-v", "-V", "--help", "-h"]);
|
|
@@ -14945,9 +15051,9 @@ function resolvePathOperand(operand, cwd) {
|
|
|
14945
15051
|
return process.env.HOME ?? operand;
|
|
14946
15052
|
}
|
|
14947
15053
|
if (operand.startsWith("~/")) {
|
|
14948
|
-
return
|
|
15054
|
+
return path44.join(process.env.HOME ?? "~", operand.slice(2));
|
|
14949
15055
|
}
|
|
14950
|
-
return
|
|
15056
|
+
return path44.resolve(cwd, operand);
|
|
14951
15057
|
}
|
|
14952
15058
|
function isOptionToken(value) {
|
|
14953
15059
|
return value.startsWith("-") || value.startsWith("+");
|
|
@@ -14959,7 +15065,7 @@ function pipeToShell(command) {
|
|
|
14959
15065
|
return /(?:^|[|;&]\s*)(?:bash|sh|zsh|dash|fish)(?:\s|$)/.test(command) && /\|/.test(command);
|
|
14960
15066
|
}
|
|
14961
15067
|
function executableBaseName(head) {
|
|
14962
|
-
return
|
|
15068
|
+
return path44.basename(head);
|
|
14963
15069
|
}
|
|
14964
15070
|
function isMetadataOnlyArgv(argv) {
|
|
14965
15071
|
return argv.length > 0 && argv.every((token) => METADATA_ONLY_FLAGS.has(token));
|
|
@@ -15078,15 +15184,22 @@ function effectChangingEnvironmentSignals(head, env, changedNames) {
|
|
|
15078
15184
|
}
|
|
15079
15185
|
|
|
15080
15186
|
// src/core/effect-ir/shell-lower/decode-process.ts
|
|
15081
|
-
import
|
|
15187
|
+
import path48 from "node:path";
|
|
15082
15188
|
|
|
15083
15189
|
// src/core/effect-ir/shell-lower/decoders/belay.ts
|
|
15084
|
-
import
|
|
15190
|
+
import path45 from "node:path";
|
|
15085
15191
|
function decodeBelay(args, repoRoot, segment) {
|
|
15086
15192
|
const [section, operation, key] = args;
|
|
15087
15193
|
const judgeCommand = section === "judge" && operation !== void 0 && ["consent", "list", "status", "test", "use"].includes(operation);
|
|
15088
15194
|
const configRead = section === "config" && (operation === void 0 || operation === "list" || operation === "get" && key?.startsWith("judge."));
|
|
15089
15195
|
const configJudgeMutation = section === "config" && (["set", "unset"].includes(operation ?? "") && key?.startsWith("judge.") || operation === "credential" && key === "mode");
|
|
15196
|
+
const approvalAuthorityCommand = [
|
|
15197
|
+
"approval-token",
|
|
15198
|
+
"approve",
|
|
15199
|
+
"revoke",
|
|
15200
|
+
"standing-allow"
|
|
15201
|
+
].includes(section ?? "");
|
|
15202
|
+
const configTrustMutation = section === "config" && operation === "trust";
|
|
15090
15203
|
if (judgeCommand || configRead || configJudgeMutation) {
|
|
15091
15204
|
return [
|
|
15092
15205
|
processRequirement("belay", "inspect", segment, [
|
|
@@ -15095,12 +15208,23 @@ function decodeBelay(args, repoRoot, segment) {
|
|
|
15095
15208
|
])
|
|
15096
15209
|
];
|
|
15097
15210
|
}
|
|
15211
|
+
if (approvalAuthorityCommand || configTrustMutation) {
|
|
15212
|
+
return [
|
|
15213
|
+
requirement2(
|
|
15214
|
+
"control_plane.write",
|
|
15215
|
+
"control_plane.write",
|
|
15216
|
+
{ kind: "path", path: path45.join(repoRoot, ".belay-control-plane") },
|
|
15217
|
+
segment,
|
|
15218
|
+
[approvalAuthorityCommand ? "belay.approval_authority" : "belay.config_trust"]
|
|
15219
|
+
)
|
|
15220
|
+
];
|
|
15221
|
+
}
|
|
15098
15222
|
if (section === "config" && ["set", "unset", "credential"].includes(operation ?? "")) {
|
|
15099
15223
|
return [
|
|
15100
15224
|
requirement2(
|
|
15101
15225
|
"control_plane.write",
|
|
15102
15226
|
"control_plane.write",
|
|
15103
|
-
{ kind: "path", path:
|
|
15227
|
+
{ kind: "path", path: path45.join(repoRoot, ".belay-control-plane") },
|
|
15104
15228
|
segment,
|
|
15105
15229
|
["belay.config_non_judge_mutation"]
|
|
15106
15230
|
)
|
|
@@ -15341,7 +15465,7 @@ function validDockerInfo(args) {
|
|
|
15341
15465
|
// src/core/effect-ir/shell-lower/decoders/filesystem.ts
|
|
15342
15466
|
init_git_resource_identity();
|
|
15343
15467
|
import { lstatSync as lstatSync2, realpathSync as realpathSync4 } from "node:fs";
|
|
15344
|
-
import
|
|
15468
|
+
import path46 from "node:path";
|
|
15345
15469
|
function decodeCopyMove(head, args, cwd, segment) {
|
|
15346
15470
|
const requirements = [processRequirement(head, "spawn", segment, ["process.filesystem_mutation"])];
|
|
15347
15471
|
const operands = [];
|
|
@@ -15469,11 +15593,11 @@ function decodeRm(args, cwd, repoRoot, segment) {
|
|
|
15469
15593
|
function canonicalRmOperand(targetPath, finalOperandIsSymlink) {
|
|
15470
15594
|
try {
|
|
15471
15595
|
if (finalOperandIsSymlink) {
|
|
15472
|
-
return
|
|
15596
|
+
return path46.join(realpathSync4.native(path46.dirname(targetPath)), path46.basename(targetPath));
|
|
15473
15597
|
}
|
|
15474
15598
|
return realpathSync4.native(targetPath);
|
|
15475
15599
|
} catch {
|
|
15476
|
-
return
|
|
15600
|
+
return path46.resolve(targetPath);
|
|
15477
15601
|
}
|
|
15478
15602
|
}
|
|
15479
15603
|
function isSymbolicLink(targetPath) {
|
|
@@ -15484,8 +15608,8 @@ function isSymbolicLink(targetPath) {
|
|
|
15484
15608
|
}
|
|
15485
15609
|
}
|
|
15486
15610
|
function pathContains(ancestor, candidate) {
|
|
15487
|
-
const relative =
|
|
15488
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
15611
|
+
const relative = path46.relative(path46.resolve(ancestor), path46.resolve(candidate));
|
|
15612
|
+
return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative);
|
|
15489
15613
|
}
|
|
15490
15614
|
function filesystemReadOperands(head, args) {
|
|
15491
15615
|
switch (head) {
|
|
@@ -15625,7 +15749,7 @@ function decodePrisma(args, env, repoRoot, segment) {
|
|
|
15625
15749
|
|
|
15626
15750
|
// src/core/effect-ir/shell-lower/decoders/ruby.ts
|
|
15627
15751
|
init_path_utils();
|
|
15628
|
-
import
|
|
15752
|
+
import path47 from "node:path";
|
|
15629
15753
|
var RAILS_READ_ONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["routes", "middleware", "stats", "about", "version"]);
|
|
15630
15754
|
function railsReadOnlySubcommand(args) {
|
|
15631
15755
|
const subcommand = args[0];
|
|
@@ -15635,7 +15759,7 @@ function railsReadOnlySubcommand(args) {
|
|
|
15635
15759
|
return RAILS_READ_ONLY_SUBCOMMANDS.has(subcommand);
|
|
15636
15760
|
}
|
|
15637
15761
|
function isRubyTestScript(scriptPath) {
|
|
15638
|
-
const base =
|
|
15762
|
+
const base = path47.basename(scriptPath);
|
|
15639
15763
|
return base.endsWith("_test.rb") || base.endsWith("_spec.rb");
|
|
15640
15764
|
}
|
|
15641
15765
|
function parseRubyTestInvocation(args) {
|
|
@@ -16049,7 +16173,7 @@ function decodeProcessOrFilesystem(params) {
|
|
|
16049
16173
|
requirement2(
|
|
16050
16174
|
"fs.read",
|
|
16051
16175
|
"fs.read",
|
|
16052
|
-
{ kind: "path", path:
|
|
16176
|
+
{ kind: "path", path: path48.resolve(cwd, syntax) },
|
|
16053
16177
|
segment,
|
|
16054
16178
|
["shell.syntax_source_read"]
|
|
16055
16179
|
)
|
|
@@ -16255,7 +16379,7 @@ function packageExecInnerIsMetadata(peel) {
|
|
|
16255
16379
|
|
|
16256
16380
|
// src/core/effect-ir/shell-lower/segment.ts
|
|
16257
16381
|
init_shell_tokenizer();
|
|
16258
|
-
import
|
|
16382
|
+
import path49 from "node:path";
|
|
16259
16383
|
var DYNAMIC_SHELL_VALUE_PATTERN = /(?:\$\(|`|\$(?:\d+|[@*#?$!-]|\{[^}]*\}|[A-Za-z_][A-Za-z0-9_]*))/;
|
|
16260
16384
|
var SHELL_GLOB_PATTERN = /[*?[]/;
|
|
16261
16385
|
function requiresKnownCwd(requirementValue) {
|
|
@@ -16270,11 +16394,11 @@ function joinNestedOpacity(outer, nested) {
|
|
|
16270
16394
|
}
|
|
16271
16395
|
function startsLocalPostgresService(command) {
|
|
16272
16396
|
const tokens = tokenizeShell(command);
|
|
16273
|
-
return
|
|
16397
|
+
return path49.basename(tokens[0] ?? "") === "docker" && tokens[1] === "compose" && ["up", "start", "restart"].includes(tokens[2] ?? "") && tokens.includes("postgres");
|
|
16274
16398
|
}
|
|
16275
16399
|
function resolveCdTransition(command, currentCwd) {
|
|
16276
16400
|
const tokens = tokenizeShell(command);
|
|
16277
|
-
if (
|
|
16401
|
+
if (path49.basename(tokens[0] ?? "") !== "cd") {
|
|
16278
16402
|
return null;
|
|
16279
16403
|
}
|
|
16280
16404
|
const target = tokens[1] ?? "~";
|
|
@@ -16437,7 +16561,7 @@ function lowerSegment(command, context) {
|
|
|
16437
16561
|
stripStructuredRedirects(lexed.tokens),
|
|
16438
16562
|
stripRedirects(parsedTokens)
|
|
16439
16563
|
);
|
|
16440
|
-
const head =
|
|
16564
|
+
const head = path50.basename(tokens[0] ?? parsed.head);
|
|
16441
16565
|
let opacity = segmentOpacity(command);
|
|
16442
16566
|
const signals = /* @__PURE__ */ new Set();
|
|
16443
16567
|
const requirements = [];
|
|
@@ -17289,7 +17413,7 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
|
17289
17413
|
};
|
|
17290
17414
|
}
|
|
17291
17415
|
const signals = [];
|
|
17292
|
-
const resolvedPath =
|
|
17416
|
+
const resolvedPath = path51.isAbsolute(filePath) ? filePath : path51.resolve(cwd, filePath);
|
|
17293
17417
|
const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
|
|
17294
17418
|
if (hitsProtectedRoot) {
|
|
17295
17419
|
signals.push("control_plane_path");
|
|
@@ -17875,7 +17999,7 @@ function hashDecisionConfig(config) {
|
|
|
17875
17999
|
init_fingerprint2();
|
|
17876
18000
|
|
|
17877
18001
|
// src/version.ts
|
|
17878
|
-
var PACKAGE_VERSION = "0.
|
|
18002
|
+
var PACKAGE_VERSION = "0.10.0";
|
|
17879
18003
|
|
|
17880
18004
|
// src/runtime-provenance.ts
|
|
17881
18005
|
function resolveRuntimeArtifactHash(artifactHash) {
|
|
@@ -17948,56 +18072,56 @@ function capabilityRequestsBlockRecovery(requests) {
|
|
|
17948
18072
|
init_fingerprint2();
|
|
17949
18073
|
init_path_utils();
|
|
17950
18074
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
17951
|
-
import { existsSync as
|
|
17952
|
-
import { mkdir as
|
|
17953
|
-
import
|
|
18075
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
18076
|
+
import { mkdir as mkdir12, readdir as readdir3, readFile as readFile14, rename as rename4, rm as rm6 } from "node:fs/promises";
|
|
18077
|
+
import path56 from "node:path";
|
|
17954
18078
|
|
|
17955
18079
|
// src/core/recovery/artifact-store.ts
|
|
17956
18080
|
init_fingerprint2();
|
|
17957
18081
|
init_path_utils();
|
|
17958
18082
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
17959
|
-
import { existsSync as
|
|
17960
|
-
import { lstat as lstat7, mkdir as
|
|
17961
|
-
import
|
|
18083
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
18084
|
+
import { lstat as lstat7, mkdir as mkdir11, open as open6, readdir as readdir2, readFile as readFile11, rename as rename3, rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
18085
|
+
import path53 from "node:path";
|
|
17962
18086
|
|
|
17963
18087
|
// src/core/recovery/snapshot-node.ts
|
|
17964
18088
|
init_fingerprint2();
|
|
17965
18089
|
init_path_utils();
|
|
17966
18090
|
import { createHash as createHash12 } from "node:crypto";
|
|
17967
|
-
import { existsSync as
|
|
18091
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
17968
18092
|
import {
|
|
17969
|
-
chmod as
|
|
18093
|
+
chmod as chmod4,
|
|
17970
18094
|
copyFile as copyFile2,
|
|
17971
18095
|
lstat as lstat6,
|
|
17972
|
-
mkdir as
|
|
17973
|
-
open as
|
|
17974
|
-
readFile as
|
|
18096
|
+
mkdir as mkdir10,
|
|
18097
|
+
open as open5,
|
|
18098
|
+
readFile as readFile10,
|
|
17975
18099
|
readlink as readlink4,
|
|
17976
18100
|
rm as rm4,
|
|
17977
18101
|
rmdir as rmdir2,
|
|
17978
18102
|
symlink as symlink3,
|
|
17979
18103
|
writeFile as writeFile6
|
|
17980
18104
|
} from "node:fs/promises";
|
|
17981
|
-
import
|
|
18105
|
+
import path52 from "node:path";
|
|
17982
18106
|
var RECOVERY_UNSUPPORTED_FILE_KIND = "recovery_unsupported_file_kind";
|
|
17983
18107
|
function validRecoveryRelativePath(relativePath) {
|
|
17984
|
-
if (!relativePath || relativePath.includes("\0") ||
|
|
17985
|
-
const normalized =
|
|
17986
|
-
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${
|
|
18108
|
+
if (!relativePath || relativePath.includes("\0") || path52.isAbsolute(relativePath)) return false;
|
|
18109
|
+
const normalized = path52.normalize(relativePath);
|
|
18110
|
+
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${path52.sep}`);
|
|
17987
18111
|
}
|
|
17988
18112
|
async function assertRecoverySafeTarget(resourceRoot, relativePath) {
|
|
17989
18113
|
if (!validRecoveryRelativePath(relativePath)) throw new Error("recovery_path_escape");
|
|
17990
18114
|
const root = canonicalPath(resourceRoot);
|
|
17991
|
-
const target =
|
|
17992
|
-
const relative =
|
|
17993
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
18115
|
+
const target = path52.resolve(root, relativePath);
|
|
18116
|
+
const relative = path52.relative(root, target);
|
|
18117
|
+
if (relative === ".." || relative.startsWith(`..${path52.sep}`) || path52.isAbsolute(relative)) {
|
|
17994
18118
|
throw new Error("recovery_path_escape");
|
|
17995
18119
|
}
|
|
17996
18120
|
let current = root;
|
|
17997
|
-
const parentParts =
|
|
18121
|
+
const parentParts = path52.relative(root, path52.dirname(target)).split(path52.sep).filter(Boolean);
|
|
17998
18122
|
for (const part of parentParts) {
|
|
17999
|
-
current =
|
|
18000
|
-
if (!
|
|
18123
|
+
current = path52.join(current, part);
|
|
18124
|
+
if (!existsSync13(current)) break;
|
|
18001
18125
|
const info = await lstat6(current);
|
|
18002
18126
|
if (info.isSymbolicLink()) throw new Error("recovery_symlink_escape");
|
|
18003
18127
|
if (!info.isDirectory()) break;
|
|
@@ -18005,7 +18129,7 @@ async function assertRecoverySafeTarget(resourceRoot, relativePath) {
|
|
|
18005
18129
|
return target;
|
|
18006
18130
|
}
|
|
18007
18131
|
async function fsyncPath(filePath) {
|
|
18008
|
-
const handle = await
|
|
18132
|
+
const handle = await open5(filePath, "r");
|
|
18009
18133
|
try {
|
|
18010
18134
|
await handle.sync();
|
|
18011
18135
|
} finally {
|
|
@@ -18047,13 +18171,13 @@ async function captureRecoverySnapshot(filePath, options) {
|
|
|
18047
18171
|
return { kind: "directory", mode, hash: recoveryDirectoryHash(mode) };
|
|
18048
18172
|
}
|
|
18049
18173
|
if (!info.isFile()) throw new Error(RECOVERY_UNSUPPORTED_FILE_KIND);
|
|
18050
|
-
const content = await
|
|
18174
|
+
const content = await readFile10(filePath);
|
|
18051
18175
|
const hash = createHash12("sha256").update(content).digest("hex");
|
|
18052
18176
|
let blob;
|
|
18053
18177
|
if (options?.blobDir) {
|
|
18054
|
-
await
|
|
18055
|
-
const blobPath =
|
|
18056
|
-
if (!
|
|
18178
|
+
await mkdir10(options.blobDir, { recursive: true, mode: 448 });
|
|
18179
|
+
const blobPath = path52.join(options.blobDir, hash);
|
|
18180
|
+
if (!existsSync13(blobPath)) {
|
|
18057
18181
|
await writeFile6(blobPath, content, { mode: 384 });
|
|
18058
18182
|
await fsyncPath(blobPath);
|
|
18059
18183
|
}
|
|
@@ -18107,7 +18231,7 @@ async function validateRecoverySnapshot(params) {
|
|
|
18107
18231
|
if (record.blob !== `blobs/${record.hash}`) throw new Error(params.corruptReason);
|
|
18108
18232
|
let content;
|
|
18109
18233
|
try {
|
|
18110
|
-
content = await
|
|
18234
|
+
content = await readFile10(path52.join(params.artifactDir, record.blob));
|
|
18111
18235
|
} catch {
|
|
18112
18236
|
throw new Error(params.corruptReason);
|
|
18113
18237
|
}
|
|
@@ -18135,16 +18259,16 @@ var RECOVERY_STATES = /* @__PURE__ */ new Set([
|
|
|
18135
18259
|
]);
|
|
18136
18260
|
var STAGING_STALE_MS = 5 * 6e4;
|
|
18137
18261
|
function checkpointsRoot(stateDir) {
|
|
18138
|
-
return
|
|
18262
|
+
return path53.join(stateDir, "recovery", "checkpoints");
|
|
18139
18263
|
}
|
|
18140
18264
|
function checkpointDir(stateDir, checkpointId) {
|
|
18141
18265
|
if (!/^cp_[a-f0-9]{24}$/.test(checkpointId)) {
|
|
18142
18266
|
throw new Error("invalid_recovery_checkpoint_id");
|
|
18143
18267
|
}
|
|
18144
|
-
return
|
|
18268
|
+
return path53.join(checkpointsRoot(stateDir), checkpointId);
|
|
18145
18269
|
}
|
|
18146
18270
|
async function fsyncPath2(filePath) {
|
|
18147
|
-
const handle = await
|
|
18271
|
+
const handle = await open6(filePath, "r");
|
|
18148
18272
|
try {
|
|
18149
18273
|
await handle.sync();
|
|
18150
18274
|
} finally {
|
|
@@ -18152,13 +18276,13 @@ async function fsyncPath2(filePath) {
|
|
|
18152
18276
|
}
|
|
18153
18277
|
}
|
|
18154
18278
|
async function atomicWriteJson(filePath, value) {
|
|
18155
|
-
await
|
|
18279
|
+
await mkdir11(path53.dirname(filePath), { recursive: true, mode: 448 });
|
|
18156
18280
|
const temporary = `${filePath}.tmp-${randomUUID4()}`;
|
|
18157
18281
|
await writeFile7(temporary, `${JSON.stringify(value, null, 2)}
|
|
18158
18282
|
`, { mode: 384 });
|
|
18159
18283
|
await fsyncPath2(temporary);
|
|
18160
|
-
await
|
|
18161
|
-
await fsyncPath2(
|
|
18284
|
+
await rename3(temporary, filePath);
|
|
18285
|
+
await fsyncPath2(path53.dirname(filePath));
|
|
18162
18286
|
}
|
|
18163
18287
|
async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
18164
18288
|
const value = {
|
|
@@ -18168,13 +18292,13 @@ async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
|
18168
18292
|
manifestHash,
|
|
18169
18293
|
...detail ? { detail } : {}
|
|
18170
18294
|
};
|
|
18171
|
-
await atomicWriteJson(
|
|
18295
|
+
await atomicWriteJson(path53.join(artifactDir, "state.json"), value);
|
|
18172
18296
|
}
|
|
18173
18297
|
async function directorySize(root) {
|
|
18174
|
-
if (!
|
|
18298
|
+
if (!existsSync14(root)) return 0;
|
|
18175
18299
|
let total = 0;
|
|
18176
18300
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
18177
|
-
const entryPath =
|
|
18301
|
+
const entryPath = path53.join(root, entry.name);
|
|
18178
18302
|
if (entry.isDirectory()) total += await directorySize(entryPath);
|
|
18179
18303
|
else total += (await lstat7(entryPath)).size;
|
|
18180
18304
|
}
|
|
@@ -18219,9 +18343,9 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18219
18343
|
let rawManifest;
|
|
18220
18344
|
let state;
|
|
18221
18345
|
try {
|
|
18222
|
-
rawManifest = JSON.parse(await
|
|
18346
|
+
rawManifest = JSON.parse(await readFile11(path53.join(artifactDir, "manifest.json"), "utf8"));
|
|
18223
18347
|
state = JSON.parse(
|
|
18224
|
-
await
|
|
18348
|
+
await readFile11(path53.join(artifactDir, "state.json"), "utf8")
|
|
18225
18349
|
);
|
|
18226
18350
|
} catch {
|
|
18227
18351
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
@@ -18237,10 +18361,10 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18237
18361
|
}
|
|
18238
18362
|
const entryPaths = /* @__PURE__ */ new Set();
|
|
18239
18363
|
for (const entry of manifest.entries) {
|
|
18240
|
-
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(
|
|
18364
|
+
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(path53.normalize(entry.path))) {
|
|
18241
18365
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
18242
18366
|
}
|
|
18243
|
-
entryPaths.add(
|
|
18367
|
+
entryPaths.add(path53.normalize(entry.path));
|
|
18244
18368
|
for (const [side, snapshot] of [
|
|
18245
18369
|
["before", entry.before],
|
|
18246
18370
|
["after", entry.after]
|
|
@@ -18254,9 +18378,9 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18254
18378
|
});
|
|
18255
18379
|
}
|
|
18256
18380
|
}
|
|
18257
|
-
const receiptPath =
|
|
18381
|
+
const receiptPath = path53.join(artifactDir, "receipt.json");
|
|
18258
18382
|
let receipt;
|
|
18259
|
-
if (["applied", "restoring", "restored", "conflict"].includes(state.state) ||
|
|
18383
|
+
if (["applied", "restoring", "restored", "conflict"].includes(state.state) || existsSync14(receiptPath)) {
|
|
18260
18384
|
receipt = await readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
18261
18385
|
}
|
|
18262
18386
|
return { artifactDir, manifest, state, manifestHash, ...receipt ? { receipt } : {} };
|
|
@@ -18264,7 +18388,7 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18264
18388
|
async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
18265
18389
|
let rawReceipt;
|
|
18266
18390
|
try {
|
|
18267
|
-
rawReceipt = JSON.parse(await
|
|
18391
|
+
rawReceipt = JSON.parse(await readFile11(path53.join(artifactDir, "receipt.json"), "utf8"));
|
|
18268
18392
|
} catch {
|
|
18269
18393
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
18270
18394
|
}
|
|
@@ -18287,8 +18411,8 @@ async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHas
|
|
|
18287
18411
|
return receipt;
|
|
18288
18412
|
}
|
|
18289
18413
|
async function ensureRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
18290
|
-
const receiptPath =
|
|
18291
|
-
if (
|
|
18414
|
+
const receiptPath = path53.join(artifactDir, "receipt.json");
|
|
18415
|
+
if (existsSync14(receiptPath)) {
|
|
18292
18416
|
return readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
18293
18417
|
}
|
|
18294
18418
|
const receipt = {
|
|
@@ -18305,14 +18429,14 @@ async function ensureRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
|
18305
18429
|
}
|
|
18306
18430
|
async function checkpointIds(stateDir) {
|
|
18307
18431
|
const root = checkpointsRoot(stateDir);
|
|
18308
|
-
if (!
|
|
18432
|
+
if (!existsSync14(root)) return [];
|
|
18309
18433
|
return (await readdir2(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^cp_[a-f0-9]{24}$/.test(entry.name)).map((entry) => entry.name);
|
|
18310
18434
|
}
|
|
18311
18435
|
async function artifactRepoRoot(stateDir, checkpointId) {
|
|
18312
18436
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
18313
18437
|
try {
|
|
18314
18438
|
const manifest = JSON.parse(
|
|
18315
|
-
await
|
|
18439
|
+
await readFile11(path53.join(artifactDir, "manifest.json"), "utf8")
|
|
18316
18440
|
);
|
|
18317
18441
|
if (typeof manifest.repoRoot === "string" && manifest.repoRoot) {
|
|
18318
18442
|
return canonicalPath(manifest.repoRoot);
|
|
@@ -18320,7 +18444,7 @@ async function artifactRepoRoot(stateDir, checkpointId) {
|
|
|
18320
18444
|
} catch {
|
|
18321
18445
|
}
|
|
18322
18446
|
try {
|
|
18323
|
-
const owner = JSON.parse(await
|
|
18447
|
+
const owner = JSON.parse(await readFile11(path53.join(artifactDir, "owner.json"), "utf8"));
|
|
18324
18448
|
return typeof owner.repoRoot === "string" && owner.repoRoot ? canonicalPath(owner.repoRoot) : null;
|
|
18325
18449
|
} catch {
|
|
18326
18450
|
return null;
|
|
@@ -18336,14 +18460,14 @@ async function checkpointIdsForRepo(stateDir, repoRoot) {
|
|
|
18336
18460
|
}
|
|
18337
18461
|
async function cleanupOrphanedStaging(stateDir) {
|
|
18338
18462
|
const root = checkpointsRoot(stateDir);
|
|
18339
|
-
if (!
|
|
18463
|
+
if (!existsSync14(root)) return;
|
|
18340
18464
|
const now = Date.now();
|
|
18341
18465
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
18342
18466
|
if (!entry.isDirectory() || !/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) continue;
|
|
18343
|
-
const stagingPath =
|
|
18467
|
+
const stagingPath = path53.join(root, entry.name);
|
|
18344
18468
|
let stale = false;
|
|
18345
18469
|
try {
|
|
18346
|
-
const owner = JSON.parse(await
|
|
18470
|
+
const owner = JSON.parse(await readFile11(path53.join(stagingPath, "owner.json"), "utf8"));
|
|
18347
18471
|
const pid = typeof owner.pid === "number" ? owner.pid : Number.NaN;
|
|
18348
18472
|
const createdAt = typeof owner.createdAt === "string" ? Date.parse(owner.createdAt) : NaN;
|
|
18349
18473
|
let alive = false;
|
|
@@ -18383,9 +18507,9 @@ async function markRecoveryCheckpointApplied(stateDir, checkpoint) {
|
|
|
18383
18507
|
|
|
18384
18508
|
// src/core/recovery/reconcile.ts
|
|
18385
18509
|
init_fingerprint2();
|
|
18386
|
-
import { existsSync as
|
|
18387
|
-
import { readFile as
|
|
18388
|
-
import
|
|
18510
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
18511
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
18512
|
+
import path54 from "node:path";
|
|
18389
18513
|
async function matchRecoverySide(resourceRoot, entries, side) {
|
|
18390
18514
|
for (const entry of entries) {
|
|
18391
18515
|
const target = await assertRecoverySafeTarget(resourceRoot, entry.path);
|
|
@@ -18399,9 +18523,9 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
18399
18523
|
loaded = await readRecoveryArtifact(stateDir, checkpointId);
|
|
18400
18524
|
} catch {
|
|
18401
18525
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
18402
|
-
if (
|
|
18403
|
-
const manifestPath =
|
|
18404
|
-
const hash =
|
|
18526
|
+
if (existsSync15(artifactDir)) {
|
|
18527
|
+
const manifestPath = path54.join(artifactDir, "manifest.json");
|
|
18528
|
+
const hash = existsSync15(manifestPath) ? hashValue(await readFile12(manifestPath, "utf8")) : "unavailable";
|
|
18405
18529
|
await writeRecoveryState(artifactDir, "corrupt", hash, RECOVERY_CHECKPOINT_CORRUPT);
|
|
18406
18530
|
}
|
|
18407
18531
|
return "corrupt";
|
|
@@ -18436,8 +18560,8 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
18436
18560
|
|
|
18437
18561
|
// src/core/recovery/resource-identity.ts
|
|
18438
18562
|
init_fingerprint2();
|
|
18439
|
-
import { lstat as lstat8, readFile as
|
|
18440
|
-
import
|
|
18563
|
+
import { lstat as lstat8, readFile as readFile13, realpath as realpath3 } from "node:fs/promises";
|
|
18564
|
+
import path55 from "node:path";
|
|
18441
18565
|
async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
18442
18566
|
const resolvedRoot = await realpath3(resourceRoot);
|
|
18443
18567
|
if (resourceKind === "directory") {
|
|
@@ -18445,13 +18569,13 @@ async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
|
18445
18569
|
if (!rootInfo.isDirectory()) throw new Error("recovery_repo_identity_unavailable");
|
|
18446
18570
|
return hashValue(`${resolvedRoot}\0${rootInfo.dev}:${rootInfo.ino}:${rootInfo.birthtimeMs}`);
|
|
18447
18571
|
}
|
|
18448
|
-
const dotGit =
|
|
18572
|
+
const dotGit = path55.join(resolvedRoot, ".git");
|
|
18449
18573
|
const gitInfo = await lstat8(dotGit);
|
|
18450
18574
|
let gitMetadataPath = dotGit;
|
|
18451
18575
|
if (gitInfo.isFile()) {
|
|
18452
|
-
const marker = (await
|
|
18576
|
+
const marker = (await readFile13(dotGit, "utf8")).trim();
|
|
18453
18577
|
if (!marker.startsWith("gitdir:")) throw new Error("recovery_repo_identity_unavailable");
|
|
18454
|
-
gitMetadataPath =
|
|
18578
|
+
gitMetadataPath = path55.resolve(resolvedRoot, marker.slice("gitdir:".length).trim());
|
|
18455
18579
|
} else if (!gitInfo.isDirectory()) {
|
|
18456
18580
|
throw new Error("recovery_repo_identity_unavailable");
|
|
18457
18581
|
}
|
|
@@ -18516,7 +18640,7 @@ async function garbageCollect(stateDir, config, repoRoot) {
|
|
|
18516
18640
|
async function prepareRecoveryCheckpoint(params) {
|
|
18517
18641
|
const backend = params.backend ?? "git_worktree";
|
|
18518
18642
|
const resourceKind = await resolveRecoveryResourceKind(backend, params.repoRoot);
|
|
18519
|
-
await
|
|
18643
|
+
await mkdir12(checkpointsRoot(params.stateDir), { recursive: true, mode: 448 });
|
|
18520
18644
|
await cleanupOrphanedStaging(params.stateDir);
|
|
18521
18645
|
await garbageCollect(params.stateDir, params.config, params.repoRoot);
|
|
18522
18646
|
const existing = await checkpointIdsForRepo(params.stateDir, params.repoRoot);
|
|
@@ -18524,16 +18648,16 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18524
18648
|
throw new Error(RECOVERY_CHECKPOINT_QUOTA);
|
|
18525
18649
|
}
|
|
18526
18650
|
const checkpointId = `cp_${randomUUID5().replaceAll("-", "").slice(0, 24)}`;
|
|
18527
|
-
const temporary =
|
|
18651
|
+
const temporary = path56.join(checkpointsRoot(params.stateDir), `.tmp-${checkpointId}`);
|
|
18528
18652
|
const finalDir = checkpointDir(params.stateDir, checkpointId);
|
|
18529
|
-
await
|
|
18530
|
-
await atomicWriteJson(
|
|
18653
|
+
await mkdir12(temporary, { recursive: true, mode: 448 });
|
|
18654
|
+
await atomicWriteJson(path56.join(temporary, "owner.json"), {
|
|
18531
18655
|
version: 1,
|
|
18532
18656
|
pid: process.pid,
|
|
18533
18657
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18534
18658
|
repoRoot: canonicalPath(params.repoRoot)
|
|
18535
18659
|
});
|
|
18536
|
-
await
|
|
18660
|
+
await mkdir12(path56.join(temporary, "blobs"), { recursive: true, mode: 448 });
|
|
18537
18661
|
try {
|
|
18538
18662
|
const entries = [];
|
|
18539
18663
|
const protectedRoots = (params.protectedRoots ?? []).map(canonicalPath);
|
|
@@ -18542,8 +18666,8 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18542
18666
|
)) {
|
|
18543
18667
|
const target = await assertRecoverySafeTarget(params.repoRoot, change.relativePath);
|
|
18544
18668
|
if (protectedRoots.some((root) => {
|
|
18545
|
-
const relative =
|
|
18546
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
18669
|
+
const relative = path56.relative(root, target);
|
|
18670
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path56.sep}`) && !path56.isAbsolute(relative);
|
|
18547
18671
|
})) {
|
|
18548
18672
|
throw new Error("recovery_protected_path");
|
|
18549
18673
|
}
|
|
@@ -18555,7 +18679,7 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18555
18679
|
entries.push({
|
|
18556
18680
|
path: change.relativePath,
|
|
18557
18681
|
before: await captureRecoverySnapshot(baseline, {
|
|
18558
|
-
blobDir:
|
|
18682
|
+
blobDir: path56.join(temporary, "blobs")
|
|
18559
18683
|
}),
|
|
18560
18684
|
after: withoutRecoveryBlob(await captureRecoverySnapshot(source))
|
|
18561
18685
|
});
|
|
@@ -18592,12 +18716,12 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18592
18716
|
entries
|
|
18593
18717
|
};
|
|
18594
18718
|
const manifestHash = hashValue(canonicalStringify(manifest));
|
|
18595
|
-
await atomicWriteJson(
|
|
18719
|
+
await atomicWriteJson(path56.join(temporary, "manifest.json"), manifest);
|
|
18596
18720
|
await writeRecoveryState(temporary, "prepared", manifestHash);
|
|
18597
18721
|
await fsyncPath2(temporary);
|
|
18598
18722
|
const projectedBytes = await recoveryCheckpointStorageBytes(params.stateDir, params.repoRoot);
|
|
18599
18723
|
if (projectedBytes > params.config.maxBytes) throw new Error(RECOVERY_CHECKPOINT_QUOTA);
|
|
18600
|
-
await
|
|
18724
|
+
await rename4(temporary, finalDir);
|
|
18601
18725
|
await fsyncPath2(checkpointsRoot(params.stateDir));
|
|
18602
18726
|
return {
|
|
18603
18727
|
checkpointId,
|
|
@@ -18635,7 +18759,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18635
18759
|
} catch {
|
|
18636
18760
|
try {
|
|
18637
18761
|
const raw = JSON.parse(
|
|
18638
|
-
await
|
|
18762
|
+
await readFile14(path56.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
18639
18763
|
);
|
|
18640
18764
|
rootFromArtifact = typeof raw.repoRoot === "string" && raw.repoRoot ? raw.repoRoot : void 0;
|
|
18641
18765
|
} catch {
|
|
@@ -18669,7 +18793,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18669
18793
|
} catch {
|
|
18670
18794
|
try {
|
|
18671
18795
|
const manifest = JSON.parse(
|
|
18672
|
-
await
|
|
18796
|
+
await readFile14(path56.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
18673
18797
|
);
|
|
18674
18798
|
if (manifest.checkpointId !== id || ![1, 2].includes(manifest.version)) continue;
|
|
18675
18799
|
if (repoRoot && canonicalPath(manifest.repoRoot) !== canonicalPath(repoRoot)) continue;
|
|
@@ -18694,12 +18818,12 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18694
18818
|
async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
18695
18819
|
const root = checkpointsRoot(stateDir);
|
|
18696
18820
|
if (!repoRoot) return directorySize(root);
|
|
18697
|
-
if (!
|
|
18821
|
+
if (!existsSync16(root)) return 0;
|
|
18698
18822
|
const expected = canonicalPath(repoRoot);
|
|
18699
18823
|
let total = 0;
|
|
18700
18824
|
for (const entry of await readdir3(root, { withFileTypes: true })) {
|
|
18701
18825
|
if (!entry.isDirectory()) continue;
|
|
18702
|
-
const entryPath =
|
|
18826
|
+
const entryPath = path56.join(root, entry.name);
|
|
18703
18827
|
if (/^cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
18704
18828
|
if (await artifactRepoRoot(stateDir, entry.name) === expected) {
|
|
18705
18829
|
total += await directorySize(entryPath);
|
|
@@ -18708,7 +18832,7 @@ async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
|
18708
18832
|
}
|
|
18709
18833
|
if (/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
18710
18834
|
try {
|
|
18711
|
-
const owner = JSON.parse(await
|
|
18835
|
+
const owner = JSON.parse(await readFile14(path56.join(entryPath, "owner.json"), "utf8"));
|
|
18712
18836
|
if (typeof owner.repoRoot === "string" && canonicalPath(owner.repoRoot) === expected) {
|
|
18713
18837
|
total += await directorySize(entryPath);
|
|
18714
18838
|
}
|
|
@@ -18750,16 +18874,16 @@ function recoveryFailClosedResult(predicted, reason, signals = []) {
|
|
|
18750
18874
|
init_scrub();
|
|
18751
18875
|
|
|
18752
18876
|
// src/core/transactional/file-checkpoint-backend.ts
|
|
18753
|
-
import { cp, lstat as lstat11, mkdir as
|
|
18877
|
+
import { cp, lstat as lstat11, mkdir as mkdir14, mkdtemp as mkdtemp5, readdir as readdir6, rm as rm9, writeFile as writeFile10 } from "node:fs/promises";
|
|
18754
18878
|
import os5 from "node:os";
|
|
18755
|
-
import
|
|
18879
|
+
import path60 from "node:path";
|
|
18756
18880
|
|
|
18757
18881
|
// src/core/transactional/file-checkpoint-git.ts
|
|
18758
18882
|
init_path_utils();
|
|
18759
18883
|
import { spawn as spawn8 } from "node:child_process";
|
|
18760
18884
|
import { createHash as createHash13 } from "node:crypto";
|
|
18761
|
-
import { copyFile as copyFile3, lstat as lstat9, readdir as readdir4, readFile as
|
|
18762
|
-
import
|
|
18885
|
+
import { copyFile as copyFile3, lstat as lstat9, readdir as readdir4, readFile as readFile15 } from "node:fs/promises";
|
|
18886
|
+
import path57 from "node:path";
|
|
18763
18887
|
var FILE_CHECKPOINT_GIT_METADATA_CHANGED = "file_checkpoint_git_metadata_changed";
|
|
18764
18888
|
var FILE_CHECKPOINT_SOURCE_CHANGED = "file_checkpoint_source_changed";
|
|
18765
18889
|
var FILE_CHECKPOINT_CWD_OUTSIDE_ROOT = "file_checkpoint_cwd_outside_root";
|
|
@@ -18793,7 +18917,7 @@ function rethrowStableFileCheckpointError(error) {
|
|
|
18793
18917
|
}
|
|
18794
18918
|
async function rootGitMetadataPresent(repoRoot) {
|
|
18795
18919
|
try {
|
|
18796
|
-
await lstat9(
|
|
18920
|
+
await lstat9(path57.join(repoRoot, ".git"));
|
|
18797
18921
|
return true;
|
|
18798
18922
|
} catch {
|
|
18799
18923
|
return false;
|
|
@@ -18842,10 +18966,10 @@ function execGit2(repoRoot, args) {
|
|
|
18842
18966
|
}
|
|
18843
18967
|
async function resolveGitPath(repoRoot, gitPath) {
|
|
18844
18968
|
const trimmed = gitPath.trim();
|
|
18845
|
-
if (
|
|
18969
|
+
if (path57.isAbsolute(trimmed)) {
|
|
18846
18970
|
return trimmed;
|
|
18847
18971
|
}
|
|
18848
|
-
return
|
|
18972
|
+
return path57.join(repoRoot, trimmed);
|
|
18849
18973
|
}
|
|
18850
18974
|
async function cloneBareWorktreeCopy(sourceRoot, destinationRoot) {
|
|
18851
18975
|
await execGit2(sourceRoot, [
|
|
@@ -18887,7 +19011,7 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
18887
19011
|
destinationRoot,
|
|
18888
19012
|
await execGit2(destinationRoot, ["rev-parse", "--git-dir"])
|
|
18889
19013
|
);
|
|
18890
|
-
const destinationShared =
|
|
19014
|
+
const destinationShared = path57.join(destinationGitDir, path57.basename(sourceShared));
|
|
18891
19015
|
try {
|
|
18892
19016
|
await copyFile3(sourceShared, destinationShared);
|
|
18893
19017
|
} catch (error) {
|
|
@@ -18896,14 +19020,14 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
18896
19020
|
}
|
|
18897
19021
|
async function readGitFile(gitDir, relativePath) {
|
|
18898
19022
|
try {
|
|
18899
|
-
return await
|
|
19023
|
+
return await readFile15(path57.join(gitDir, relativePath));
|
|
18900
19024
|
} catch {
|
|
18901
19025
|
return null;
|
|
18902
19026
|
}
|
|
18903
19027
|
}
|
|
18904
19028
|
async function readAbsoluteGitFile(absolutePath) {
|
|
18905
19029
|
try {
|
|
18906
|
-
return await
|
|
19030
|
+
return await readFile15(absolutePath);
|
|
18907
19031
|
} catch {
|
|
18908
19032
|
return null;
|
|
18909
19033
|
}
|
|
@@ -18920,8 +19044,8 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
18920
19044
|
repoRoot,
|
|
18921
19045
|
await execGit2(repoRoot, ["rev-parse", "--git-path", gitPath])
|
|
18922
19046
|
);
|
|
18923
|
-
const relative =
|
|
18924
|
-
const content = typeof relative === "string" && !relative.startsWith("..") && !
|
|
19047
|
+
const relative = path57.resolve(resolved).startsWith(path57.resolve(gitDir)) ? path57.relative(gitDir, resolved) : resolved;
|
|
19048
|
+
const content = typeof relative === "string" && !relative.startsWith("..") && !path57.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
18925
19049
|
if (content !== null) {
|
|
18926
19050
|
hashGitFileContent(hash, gitPath, content);
|
|
18927
19051
|
}
|
|
@@ -18931,13 +19055,13 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
18931
19055
|
async function hashGitTree(gitDir, relativeDir, hash) {
|
|
18932
19056
|
let names;
|
|
18933
19057
|
try {
|
|
18934
|
-
names = await readdir4(
|
|
19058
|
+
names = await readdir4(path57.join(gitDir, relativeDir));
|
|
18935
19059
|
} catch {
|
|
18936
19060
|
return;
|
|
18937
19061
|
}
|
|
18938
19062
|
for (const name of names.sort()) {
|
|
18939
|
-
const relativePath = relativeDir ?
|
|
18940
|
-
const absolutePath =
|
|
19063
|
+
const relativePath = relativeDir ? path57.join(relativeDir, name) : name;
|
|
19064
|
+
const absolutePath = path57.join(gitDir, relativePath);
|
|
18941
19065
|
let childNames = null;
|
|
18942
19066
|
try {
|
|
18943
19067
|
childNames = await readdir4(absolutePath);
|
|
@@ -18959,7 +19083,7 @@ async function hashGitTree(gitDir, relativeDir, hash) {
|
|
|
18959
19083
|
}
|
|
18960
19084
|
async function computeGitMetadataFingerprint(repoRoot) {
|
|
18961
19085
|
const gitDirRel = (await execGit2(repoRoot, ["rev-parse", "--git-dir"])).trim();
|
|
18962
|
-
const gitDir =
|
|
19086
|
+
const gitDir = path57.isAbsolute(gitDirRel) ? gitDirRel : path57.join(repoRoot, gitDirRel);
|
|
18963
19087
|
const hash = createHash13("sha256");
|
|
18964
19088
|
for (const file of [
|
|
18965
19089
|
"HEAD",
|
|
@@ -18982,8 +19106,8 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
18982
19106
|
const sharedIndex = (await execGit2(repoRoot, ["rev-parse", "--shared-index-path"])).trim();
|
|
18983
19107
|
if (sharedIndex) {
|
|
18984
19108
|
const resolved = await resolveGitPath(repoRoot, sharedIndex);
|
|
18985
|
-
const relative =
|
|
18986
|
-
const content = relative && !relative.startsWith("..") && !
|
|
19109
|
+
const relative = path57.relative(gitDir, resolved);
|
|
19110
|
+
const content = relative && !relative.startsWith("..") && !path57.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
18987
19111
|
if (content !== null) {
|
|
18988
19112
|
hashGitFileContent(hash, "shared-index", content);
|
|
18989
19113
|
}
|
|
@@ -18994,7 +19118,7 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
18994
19118
|
await hashResolvedGitPath(repoRoot, gitDir, gitPath, hash);
|
|
18995
19119
|
}
|
|
18996
19120
|
try {
|
|
18997
|
-
const rootGitPath =
|
|
19121
|
+
const rootGitPath = path57.join(repoRoot, ".git");
|
|
18998
19122
|
const rootGitInfo = await lstat9(rootGitPath);
|
|
18999
19123
|
if (rootGitInfo.isFile()) {
|
|
19000
19124
|
const content = await readAbsoluteGitFile(rootGitPath);
|
|
@@ -19010,14 +19134,14 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
19010
19134
|
function resolveExecutionCwdRelative(resourceRoot, cwd) {
|
|
19011
19135
|
const resolvedCwd = canonicalPath(cwd);
|
|
19012
19136
|
const resourceCanonical = canonicalPath(resourceRoot);
|
|
19013
|
-
const relative =
|
|
19137
|
+
const relative = path57.relative(resourceCanonical, resolvedCwd);
|
|
19014
19138
|
if (relative === "" || relative === ".") {
|
|
19015
19139
|
return "";
|
|
19016
19140
|
}
|
|
19017
|
-
if (relative.startsWith("..") ||
|
|
19141
|
+
if (relative.startsWith("..") || path57.isAbsolute(relative)) {
|
|
19018
19142
|
throw new Error(FILE_CHECKPOINT_CWD_OUTSIDE_ROOT);
|
|
19019
19143
|
}
|
|
19020
|
-
return relative.split(
|
|
19144
|
+
return relative.split(path57.sep).join("/");
|
|
19021
19145
|
}
|
|
19022
19146
|
|
|
19023
19147
|
// src/core/transactional/file-checkpoint-isolation.ts
|
|
@@ -19038,8 +19162,8 @@ function fileCheckpointIsolationReason(context) {
|
|
|
19038
19162
|
}
|
|
19039
19163
|
|
|
19040
19164
|
// src/core/transactional/file-checkpoint-staging.ts
|
|
19041
|
-
import { readdir as readdir5, readFile as
|
|
19042
|
-
import
|
|
19165
|
+
import { readdir as readdir5, readFile as readFile16, rm as rm7, writeFile as writeFile8 } from "node:fs/promises";
|
|
19166
|
+
import path58 from "node:path";
|
|
19043
19167
|
function isOwnerProcessAlive(pid) {
|
|
19044
19168
|
try {
|
|
19045
19169
|
process.kill(pid, 0);
|
|
@@ -19049,12 +19173,12 @@ function isOwnerProcessAlive(pid) {
|
|
|
19049
19173
|
}
|
|
19050
19174
|
}
|
|
19051
19175
|
async function writeOwnerMarker(stagingRoot, marker) {
|
|
19052
|
-
await writeFile8(
|
|
19176
|
+
await writeFile8(path58.join(stagingRoot, "owner.json"), `${JSON.stringify(marker)}
|
|
19053
19177
|
`, "utf8");
|
|
19054
19178
|
}
|
|
19055
19179
|
async function readOwnerMarker(stagingRoot) {
|
|
19056
19180
|
try {
|
|
19057
|
-
const raw = await
|
|
19181
|
+
const raw = await readFile16(path58.join(stagingRoot, "owner.json"), "utf8");
|
|
19058
19182
|
return JSON.parse(raw.trim());
|
|
19059
19183
|
} catch {
|
|
19060
19184
|
return null;
|
|
@@ -19072,7 +19196,7 @@ async function collectDeadOwnerStaging(parentDir) {
|
|
|
19072
19196
|
if (!name.startsWith("belay-file-checkpoint-")) {
|
|
19073
19197
|
continue;
|
|
19074
19198
|
}
|
|
19075
|
-
const stagingRoot =
|
|
19199
|
+
const stagingRoot = path58.join(parentDir, name);
|
|
19076
19200
|
const marker = await readOwnerMarker(stagingRoot);
|
|
19077
19201
|
if (!marker) {
|
|
19078
19202
|
dead.push(stagingRoot);
|
|
@@ -19095,7 +19219,7 @@ import { constants as fsConstants2 } from "node:fs";
|
|
|
19095
19219
|
import {
|
|
19096
19220
|
copyFile as copyFile4,
|
|
19097
19221
|
lstat as lstat10,
|
|
19098
|
-
mkdir as
|
|
19222
|
+
mkdir as mkdir13,
|
|
19099
19223
|
mkdtemp as mkdtemp4,
|
|
19100
19224
|
readlink as readlink5,
|
|
19101
19225
|
rm as rm8,
|
|
@@ -19104,17 +19228,17 @@ import {
|
|
|
19104
19228
|
writeFile as writeFile9
|
|
19105
19229
|
} from "node:fs/promises";
|
|
19106
19230
|
import os4 from "node:os";
|
|
19107
|
-
import
|
|
19231
|
+
import path59 from "node:path";
|
|
19108
19232
|
var FILE_CHECKPOINT_COPY_FAILED = "file_checkpoint_copy_failed";
|
|
19109
19233
|
async function chmodSafe2(target, mode) {
|
|
19110
19234
|
try {
|
|
19111
|
-
const { chmod:
|
|
19112
|
-
await
|
|
19235
|
+
const { chmod: chmod5 } = await import("node:fs/promises");
|
|
19236
|
+
await chmod5(target, mode & 511);
|
|
19113
19237
|
} catch {
|
|
19114
19238
|
}
|
|
19115
19239
|
}
|
|
19116
19240
|
async function copyRegularFile(sourcePath, destinationPath, mode, strategy) {
|
|
19117
|
-
await
|
|
19241
|
+
await mkdir13(path59.dirname(destinationPath), { recursive: true });
|
|
19118
19242
|
if (strategy === "clonefile" && fsConstants2.COPYFILE_FICLONE !== void 0) {
|
|
19119
19243
|
try {
|
|
19120
19244
|
await copyFile4(sourcePath, destinationPath, fsConstants2.COPYFILE_FICLONE);
|
|
@@ -19136,11 +19260,11 @@ async function copyNode(sourceRoot, destinationRoot, relativePath, strategy) {
|
|
|
19136
19260
|
const info = await lstat10(sourcePath);
|
|
19137
19261
|
await rm8(destinationPath, { force: true, recursive: false });
|
|
19138
19262
|
if (info.isDirectory() && !info.isSymbolicLink()) {
|
|
19139
|
-
await
|
|
19263
|
+
await mkdir13(destinationPath, { recursive: true, mode: info.mode & 511 });
|
|
19140
19264
|
return strategy;
|
|
19141
19265
|
}
|
|
19142
19266
|
if (info.isSymbolicLink()) {
|
|
19143
|
-
await
|
|
19267
|
+
await mkdir13(path59.dirname(destinationPath), { recursive: true });
|
|
19144
19268
|
await symlink4(await readlink5(sourcePath), destinationPath);
|
|
19145
19269
|
return strategy;
|
|
19146
19270
|
}
|
|
@@ -19196,9 +19320,9 @@ async function mapWithConcurrency(items, concurrency, worker) {
|
|
|
19196
19320
|
async function probeFileCloneStrategy() {
|
|
19197
19321
|
let tempDir = null;
|
|
19198
19322
|
try {
|
|
19199
|
-
tempDir = await mkdtemp4(
|
|
19200
|
-
const source =
|
|
19201
|
-
const destination =
|
|
19323
|
+
tempDir = await mkdtemp4(path59.join(os4.tmpdir(), "belay-clone-probe-"));
|
|
19324
|
+
const source = path59.join(tempDir, "source.txt");
|
|
19325
|
+
const destination = path59.join(tempDir, "dest.txt");
|
|
19202
19326
|
await writeFile9(source, "probe\n");
|
|
19203
19327
|
if (fsConstants2.COPYFILE_FICLONE_FORCE !== void 0) {
|
|
19204
19328
|
try {
|
|
@@ -19351,11 +19475,11 @@ async function protectedRootState(root) {
|
|
|
19351
19475
|
return `directory:${node.hash}:${index.treeHash}`;
|
|
19352
19476
|
}
|
|
19353
19477
|
function executionProtectedRoot(resourceRoot, executionRoot, protectedRoot) {
|
|
19354
|
-
const relative =
|
|
19355
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
19478
|
+
const relative = path60.relative(path60.resolve(resourceRoot), path60.resolve(protectedRoot));
|
|
19479
|
+
if (relative === "" || relative.startsWith("..") || path60.isAbsolute(relative)) {
|
|
19356
19480
|
return null;
|
|
19357
19481
|
}
|
|
19358
|
-
return
|
|
19482
|
+
return path60.join(executionRoot, relative);
|
|
19359
19483
|
}
|
|
19360
19484
|
async function captureProtectedRootStates(resourceRoot, executionRoot, protectedRoots) {
|
|
19361
19485
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -19380,15 +19504,15 @@ async function directoryByteSize(root, deadlineMs) {
|
|
|
19380
19504
|
}
|
|
19381
19505
|
let total = 0;
|
|
19382
19506
|
for (const name of await readdir6(root)) {
|
|
19383
|
-
total += await directoryByteSize(
|
|
19507
|
+
total += await directoryByteSize(path60.join(root, name), deadlineMs);
|
|
19384
19508
|
}
|
|
19385
19509
|
return total;
|
|
19386
19510
|
}
|
|
19387
19511
|
async function copyGitMetadataDirectory(sourceRoot, destinationRoot) {
|
|
19388
19512
|
const gitDirRel = (await execGit2(sourceRoot, ["rev-parse", "--git-dir"])).trim();
|
|
19389
|
-
const sourceGitDir =
|
|
19390
|
-
const relativeGitDir =
|
|
19391
|
-
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ?
|
|
19513
|
+
const sourceGitDir = path60.isAbsolute(gitDirRel) ? gitDirRel : path60.join(sourceRoot, gitDirRel);
|
|
19514
|
+
const relativeGitDir = path60.relative(path60.resolve(sourceRoot), path60.resolve(sourceGitDir));
|
|
19515
|
+
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ? path60.join(destinationRoot, relativeGitDir) : path60.join(destinationRoot, ".git");
|
|
19392
19516
|
await cp(sourceGitDir, destinationGitDir, { recursive: true, force: true });
|
|
19393
19517
|
}
|
|
19394
19518
|
async function prepareDirtyGitSnapshot(context) {
|
|
@@ -19397,7 +19521,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19397
19521
|
const quotas = context.fileCheckpoint;
|
|
19398
19522
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
19399
19523
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
19400
|
-
const stagingRoot = await mkdtemp5(
|
|
19524
|
+
const stagingRoot = await mkdtemp5(path60.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
19401
19525
|
await writeOwnerMarker(stagingRoot, {
|
|
19402
19526
|
version: 1,
|
|
19403
19527
|
pid: process.pid,
|
|
@@ -19405,8 +19529,8 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19405
19529
|
resourceRoot: context.repoRoot,
|
|
19406
19530
|
backend: "file_checkpoint"
|
|
19407
19531
|
});
|
|
19408
|
-
const baselineRoot =
|
|
19409
|
-
const executionRoot =
|
|
19532
|
+
const baselineRoot = path60.join(stagingRoot, "baseline");
|
|
19533
|
+
const executionRoot = path60.join(stagingRoot, "execution");
|
|
19410
19534
|
try {
|
|
19411
19535
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
19412
19536
|
const sourceGitMetadataFingerprint = await computeGitMetadataFingerprint(context.repoRoot);
|
|
@@ -19440,7 +19564,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19440
19564
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
19441
19565
|
}
|
|
19442
19566
|
await writeFile10(
|
|
19443
|
-
|
|
19567
|
+
path60.join(stagingRoot, "baseline-index.json"),
|
|
19444
19568
|
`${JSON.stringify(baselineIndex)}
|
|
19445
19569
|
`,
|
|
19446
19570
|
"utf8"
|
|
@@ -19490,7 +19614,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19490
19614
|
const quotas = context.fileCheckpoint;
|
|
19491
19615
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
19492
19616
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
19493
|
-
const stagingRoot = await mkdtemp5(
|
|
19617
|
+
const stagingRoot = await mkdtemp5(path60.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
19494
19618
|
await writeOwnerMarker(stagingRoot, {
|
|
19495
19619
|
version: 1,
|
|
19496
19620
|
pid: process.pid,
|
|
@@ -19498,8 +19622,8 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19498
19622
|
resourceRoot: context.repoRoot,
|
|
19499
19623
|
backend: "file_checkpoint"
|
|
19500
19624
|
});
|
|
19501
|
-
const baselineRoot =
|
|
19502
|
-
const executionRoot =
|
|
19625
|
+
const baselineRoot = path60.join(stagingRoot, "baseline");
|
|
19626
|
+
const executionRoot = path60.join(stagingRoot, "execution");
|
|
19503
19627
|
try {
|
|
19504
19628
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
19505
19629
|
const resourceIdentity = await currentRecoveryResourceIdentity(context.repoRoot, "directory");
|
|
@@ -19508,7 +19632,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19508
19632
|
quotas,
|
|
19509
19633
|
deadlineMs
|
|
19510
19634
|
});
|
|
19511
|
-
await
|
|
19635
|
+
await mkdir14(baselineRoot, { recursive: true });
|
|
19512
19636
|
const baselineIndex = await buildFileTreeIndex({
|
|
19513
19637
|
resourceRoot: baselineRoot,
|
|
19514
19638
|
excludedRoots,
|
|
@@ -19528,7 +19652,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19528
19652
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
19529
19653
|
}
|
|
19530
19654
|
await writeFile10(
|
|
19531
|
-
|
|
19655
|
+
path60.join(stagingRoot, "baseline-index.json"),
|
|
19532
19656
|
`${JSON.stringify(baselineIndex)}
|
|
19533
19657
|
`,
|
|
19534
19658
|
"utf8"
|
|
@@ -19538,7 +19662,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19538
19662
|
quotas,
|
|
19539
19663
|
deadlineMs
|
|
19540
19664
|
});
|
|
19541
|
-
await
|
|
19665
|
+
await mkdir14(executionRoot, { recursive: true });
|
|
19542
19666
|
const finalSourceIndex = await buildFileTreeIndex({
|
|
19543
19667
|
resourceRoot: context.repoRoot,
|
|
19544
19668
|
excludedRoots,
|
|
@@ -19881,10 +20005,10 @@ async function selectTransactionalBackend(context) {
|
|
|
19881
20005
|
}
|
|
19882
20006
|
|
|
19883
20007
|
// src/core/transactional/diff-evaluator.ts
|
|
19884
|
-
import
|
|
20008
|
+
import path61 from "node:path";
|
|
19885
20009
|
init_path_utils();
|
|
19886
20010
|
function categorizeChange(change, ctx) {
|
|
19887
|
-
const absolutePath = canonicalPath(
|
|
20011
|
+
const absolutePath = canonicalPath(path61.join(ctx.repoRoot, change.relativePath));
|
|
19888
20012
|
if (!pathWithinRoot(ctx.repoRoot, absolutePath)) {
|
|
19889
20013
|
return "repo_outside";
|
|
19890
20014
|
}
|
|
@@ -20434,17 +20558,55 @@ function formatJudgeInfrastructureDenyMessage(params) {
|
|
|
20434
20558
|
}
|
|
20435
20559
|
|
|
20436
20560
|
// src/core/notify.ts
|
|
20561
|
+
init_path_utils();
|
|
20437
20562
|
import { execFile } from "node:child_process";
|
|
20563
|
+
import path62 from "node:path";
|
|
20438
20564
|
import { promisify } from "node:util";
|
|
20439
20565
|
var execFileAsync = promisify(execFile);
|
|
20440
|
-
|
|
20441
|
-
|
|
20442
|
-
|
|
20566
|
+
var LOOPBACK_WEBHOOK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
20567
|
+
function webhookConfigIssue(url) {
|
|
20568
|
+
let parsed;
|
|
20569
|
+
try {
|
|
20570
|
+
parsed = new URL(url);
|
|
20571
|
+
} catch {
|
|
20572
|
+
return `notifications.webhookUrl is invalid: ${url}`;
|
|
20573
|
+
}
|
|
20574
|
+
if (parsed.protocol === "https:") {
|
|
20575
|
+
return null;
|
|
20576
|
+
}
|
|
20577
|
+
const normalizedHostname = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
20578
|
+
if (parsed.protocol === "http:" && LOOPBACK_WEBHOOK_HOSTS.has(normalizedHostname)) {
|
|
20579
|
+
return null;
|
|
20580
|
+
}
|
|
20581
|
+
return `notifications.webhookUrl must use https (http is allowed only for localhost, 127.0.0.1, or ::1): ${url}`;
|
|
20582
|
+
}
|
|
20583
|
+
function commandHookConfigIssue(commandHook, repoRoot) {
|
|
20584
|
+
if (!path62.isAbsolute(commandHook)) {
|
|
20585
|
+
return `notifications.commandHook must be an absolute path: ${commandHook}`;
|
|
20586
|
+
}
|
|
20587
|
+
if (pathWithinRoot(canonicalPath(repoRoot), canonicalPath(commandHook))) {
|
|
20588
|
+
return `notifications.commandHook must not be inside the repository: ${commandHook}`;
|
|
20589
|
+
}
|
|
20590
|
+
return null;
|
|
20591
|
+
}
|
|
20592
|
+
async function notifyDeny(config, event, deps = {
|
|
20593
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
20594
|
+
execFile: (file, args, options) => execFileAsync(file, [...args], options)
|
|
20595
|
+
}) {
|
|
20596
|
+
const payload = JSON.stringify({
|
|
20597
|
+
approvalId: event.approvalId,
|
|
20598
|
+
reason: event.reason,
|
|
20599
|
+
summary: event.summary,
|
|
20600
|
+
repoRoot: event.repoRoot,
|
|
20601
|
+
fingerprint: event.fingerprint
|
|
20602
|
+
});
|
|
20603
|
+
const webhookIssue = config.webhookUrl ? webhookConfigIssue(config.webhookUrl) : null;
|
|
20604
|
+
if (config.webhookUrl && !webhookIssue) {
|
|
20443
20605
|
try {
|
|
20444
20606
|
const controller = new AbortController();
|
|
20445
20607
|
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
20446
20608
|
try {
|
|
20447
|
-
await fetch(config.webhookUrl, {
|
|
20609
|
+
await deps.fetch(config.webhookUrl, {
|
|
20448
20610
|
method: "POST",
|
|
20449
20611
|
headers: { "content-type": "application/json" },
|
|
20450
20612
|
body: payload,
|
|
@@ -20456,17 +20618,16 @@ async function notifyDeny(config, event) {
|
|
|
20456
20618
|
} catch {
|
|
20457
20619
|
}
|
|
20458
20620
|
}
|
|
20459
|
-
|
|
20621
|
+
const commandHookIssue = config.commandHook ? commandHookConfigIssue(config.commandHook, event.repoRoot) : null;
|
|
20622
|
+
if (config.commandHook && !commandHookIssue) {
|
|
20460
20623
|
try {
|
|
20461
|
-
await
|
|
20624
|
+
await deps.execFile(config.commandHook, [], {
|
|
20462
20625
|
env: {
|
|
20463
|
-
...process.env,
|
|
20464
20626
|
BELAY_APPROVAL_ID: event.approvalId,
|
|
20465
20627
|
BELAY_REASON: event.reason,
|
|
20466
20628
|
BELAY_SUMMARY: event.summary,
|
|
20467
20629
|
BELAY_REPO_ROOT: event.repoRoot,
|
|
20468
|
-
BELAY_FINGERPRINT: event.fingerprint
|
|
20469
|
-
BELAY_APPROVAL_TOKEN: event.approvalToken ?? ""
|
|
20630
|
+
BELAY_FINGERPRINT: event.fingerprint
|
|
20470
20631
|
}
|
|
20471
20632
|
});
|
|
20472
20633
|
} catch {
|
|
@@ -20476,9 +20637,10 @@ async function notifyDeny(config, event) {
|
|
|
20476
20637
|
|
|
20477
20638
|
// src/adapters/shared/gate-runtime.ts
|
|
20478
20639
|
init_path_utils();
|
|
20640
|
+
init_repo_config_trust();
|
|
20479
20641
|
|
|
20480
20642
|
// src/adapters/layouts/protected-paths.ts
|
|
20481
|
-
import
|
|
20643
|
+
import path63 from "node:path";
|
|
20482
20644
|
function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
20483
20645
|
const roots = [
|
|
20484
20646
|
layout.configPath(repoRoot),
|
|
@@ -20490,7 +20652,7 @@ function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
|
20490
20652
|
if (controlPlaneDir) {
|
|
20491
20653
|
roots.push(controlPlaneDir);
|
|
20492
20654
|
}
|
|
20493
|
-
return roots.map((entry) =>
|
|
20655
|
+
return roots.map((entry) => path63.resolve(entry));
|
|
20494
20656
|
}
|
|
20495
20657
|
|
|
20496
20658
|
// src/adapters/shared/gate-runtime.ts
|
|
@@ -20531,7 +20693,7 @@ async function appendReplayAuditSafely(ctx, deps, event) {
|
|
|
20531
20693
|
}
|
|
20532
20694
|
async function loadJsonFile(filePath, fallback) {
|
|
20533
20695
|
try {
|
|
20534
|
-
const raw = await
|
|
20696
|
+
const raw = await readFile17(filePath, "utf8");
|
|
20535
20697
|
return JSON.parse(raw);
|
|
20536
20698
|
} catch {
|
|
20537
20699
|
return fallback;
|
|
@@ -20540,11 +20702,18 @@ async function loadJsonFile(filePath, fallback) {
|
|
|
20540
20702
|
function createDefaultGateRuntimeDeps() {
|
|
20541
20703
|
return {
|
|
20542
20704
|
async readConfig(configPath) {
|
|
20543
|
-
|
|
20705
|
+
try {
|
|
20706
|
+
return JSON.parse(await readFile17(configPath, "utf8"));
|
|
20707
|
+
} catch (error) {
|
|
20708
|
+
if (error.code === "ENOENT") {
|
|
20709
|
+
return {};
|
|
20710
|
+
}
|
|
20711
|
+
throw error;
|
|
20712
|
+
}
|
|
20544
20713
|
},
|
|
20545
20714
|
async appendAudit(ctx, event) {
|
|
20546
|
-
const auditPath =
|
|
20547
|
-
await
|
|
20715
|
+
const auditPath = path64.join(ctx.repoRoot, ctx.config.audit.logPath);
|
|
20716
|
+
await mkdir15(path64.dirname(auditPath), { recursive: true });
|
|
20548
20717
|
const provenance = auditProvenance(ctx.config);
|
|
20549
20718
|
const record = {
|
|
20550
20719
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -20577,7 +20746,7 @@ function createDefaultGateRuntimeDeps() {
|
|
|
20577
20746
|
};
|
|
20578
20747
|
},
|
|
20579
20748
|
async writeApprovals(filePath, state) {
|
|
20580
|
-
await
|
|
20749
|
+
await mkdir15(path64.dirname(filePath), { recursive: true });
|
|
20581
20750
|
await writeFile11(filePath, `${JSON.stringify(compactApprovals(state), null, 2)}
|
|
20582
20751
|
`, "utf8");
|
|
20583
20752
|
},
|
|
@@ -20598,10 +20767,11 @@ function createDefaultGateRuntimeDeps() {
|
|
|
20598
20767
|
}
|
|
20599
20768
|
async function resolveGateConfig(ctx, deps) {
|
|
20600
20769
|
const loaded = await deps.readConfig(ctx.configPath);
|
|
20770
|
+
await assertRepoConfigTrusted(ctx.repoRoot, ctx.layout.name, loaded);
|
|
20601
20771
|
let teamConfig = null;
|
|
20602
20772
|
const teamPath = teamConfigPath();
|
|
20603
|
-
if (
|
|
20604
|
-
teamConfig = JSON.parse(await
|
|
20773
|
+
if (existsSync17(teamPath)) {
|
|
20774
|
+
teamConfig = JSON.parse(await readFile17(teamPath, "utf8"));
|
|
20605
20775
|
}
|
|
20606
20776
|
return resolveLayeredConfig({
|
|
20607
20777
|
repoConfig: loaded,
|
|
@@ -20739,7 +20909,7 @@ function deriveWorkspaceRootScopeHint(params) {
|
|
|
20739
20909
|
if (!targetPath) {
|
|
20740
20910
|
return void 0;
|
|
20741
20911
|
}
|
|
20742
|
-
const candidateRoot = canonicalPath(
|
|
20912
|
+
const candidateRoot = canonicalPath(path64.dirname(targetPath));
|
|
20743
20913
|
const validation = validateTrustedWorkspaceRootCandidate({
|
|
20744
20914
|
candidatePath: candidateRoot,
|
|
20745
20915
|
repoRoot: action.repoRoot,
|
|
@@ -21130,7 +21300,21 @@ async function evaluateGatedAction(ctx, deps, params) {
|
|
|
21130
21300
|
...authorization,
|
|
21131
21301
|
egressProxyActive
|
|
21132
21302
|
};
|
|
21133
|
-
|
|
21303
|
+
let predicted = await classifyGatedActionAsync(action, ctx.config, enrichedClassifierOptions);
|
|
21304
|
+
if (action.kind === "tool" && predicted.reason === "unclassified_tool" && (ctx.config.policy.codexUnmappedTool ?? "deny") === "deny") {
|
|
21305
|
+
predicted = {
|
|
21306
|
+
...predicted,
|
|
21307
|
+
verdict: "deny_pending_approval",
|
|
21308
|
+
reason: "unmapped_tool",
|
|
21309
|
+
assessment: {
|
|
21310
|
+
reversibility: "irreversible",
|
|
21311
|
+
external: false,
|
|
21312
|
+
blastRadius: "unknown tool action",
|
|
21313
|
+
confidence: 0.5,
|
|
21314
|
+
signals: ["unmapped_tool"]
|
|
21315
|
+
}
|
|
21316
|
+
};
|
|
21317
|
+
}
|
|
21134
21318
|
if (action.kind === "shell" && action.command && isContainedUnknownExecutionEligible(ctx.config, action, predicted)) {
|
|
21135
21319
|
const mediated = await mediateContainedUnknownExecution({
|
|
21136
21320
|
ctx,
|
|
@@ -21315,7 +21499,7 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21315
21499
|
event: auditEvent,
|
|
21316
21500
|
sourceEvent,
|
|
21317
21501
|
kind,
|
|
21318
|
-
repoLabel:
|
|
21502
|
+
repoLabel: path64.basename(ctx.repoRoot),
|
|
21319
21503
|
...auditExtras.toolInvocationCorrelationId ? { toolInvocationCorrelationId: auditExtras.toolInvocationCorrelationId } : {},
|
|
21320
21504
|
fingerprint: result.fingerprint,
|
|
21321
21505
|
summary: result.normalizedCommand ?? result.summary ?? "",
|
|
@@ -21347,21 +21531,6 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21347
21531
|
if (created) {
|
|
21348
21532
|
await recordGateApprovalAsk(stateDir, result.reason, false);
|
|
21349
21533
|
}
|
|
21350
|
-
let approvalToken;
|
|
21351
|
-
try {
|
|
21352
|
-
approvalToken = await issueApprovalToken(
|
|
21353
|
-
{
|
|
21354
|
-
approvalId: approval.approvalId,
|
|
21355
|
-
fingerprint: approval.fingerprint,
|
|
21356
|
-
repoRoot: approval.repoRoot,
|
|
21357
|
-
issuedAt: approval.createdAt,
|
|
21358
|
-
expiresAt: approval.expiresAt
|
|
21359
|
-
},
|
|
21360
|
-
configuredControlPlaneDir(ctx.config)
|
|
21361
|
-
);
|
|
21362
|
-
} catch {
|
|
21363
|
-
approvalToken = void 0;
|
|
21364
|
-
}
|
|
21365
21534
|
const denialReason = failure?.reason ?? result.reason;
|
|
21366
21535
|
if (ctx.config.notifications.webhookUrl || ctx.config.notifications.commandHook) {
|
|
21367
21536
|
await notifyDeny(ctx.config.notifications, {
|
|
@@ -21369,8 +21538,7 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21369
21538
|
reason: denialReason,
|
|
21370
21539
|
summary: result.normalizedCommand ?? result.summary ?? "",
|
|
21371
21540
|
repoRoot: ctx.repoRoot,
|
|
21372
|
-
fingerprint: result.fingerprint
|
|
21373
|
-
approvalToken
|
|
21541
|
+
fingerprint: result.fingerprint
|
|
21374
21542
|
});
|
|
21375
21543
|
}
|
|
21376
21544
|
const failureAuditFields = typeof failure?.auditFields === "function" ? failure.auditFields(approval) : failure?.auditFields;
|
|
@@ -21762,38 +21930,38 @@ async function appendObservedAudit(ctx, deps, eventName, payload) {
|
|
|
21762
21930
|
}
|
|
21763
21931
|
|
|
21764
21932
|
// src/adapters/shared/repo-root.ts
|
|
21765
|
-
import { existsSync as
|
|
21766
|
-
import
|
|
21933
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
21934
|
+
import path65 from "node:path";
|
|
21767
21935
|
function belayConfigPath(current, adapterName) {
|
|
21768
21936
|
if (adapterName === "cursor") {
|
|
21769
|
-
return
|
|
21937
|
+
return path65.join(current, ".cursor", "belay.config.json");
|
|
21770
21938
|
}
|
|
21771
21939
|
if (adapterName === "claude") {
|
|
21772
|
-
return
|
|
21940
|
+
return path65.join(current, ".claude", "belay.config.json");
|
|
21773
21941
|
}
|
|
21774
|
-
return
|
|
21942
|
+
return path65.join(current, ".codex", "belay.config.json");
|
|
21775
21943
|
}
|
|
21776
21944
|
function markerMatches(current, marker, layout) {
|
|
21777
|
-
const markerPath =
|
|
21778
|
-
if (!
|
|
21945
|
+
const markerPath = path65.join(current, marker);
|
|
21946
|
+
if (!existsSync18(markerPath)) {
|
|
21779
21947
|
return false;
|
|
21780
21948
|
}
|
|
21781
21949
|
if (marker === ".cursor" || marker === ".claude" || marker === ".codex") {
|
|
21782
|
-
return
|
|
21950
|
+
return existsSync18(belayConfigPath(current, layout.name));
|
|
21783
21951
|
}
|
|
21784
21952
|
return true;
|
|
21785
21953
|
}
|
|
21786
21954
|
function findRepoRoot(startPath, layout) {
|
|
21787
|
-
let current =
|
|
21955
|
+
let current = path65.resolve(startPath);
|
|
21788
21956
|
while (true) {
|
|
21789
21957
|
for (const marker of layout.repoRootMarkers) {
|
|
21790
21958
|
if (markerMatches(current, marker, layout)) {
|
|
21791
21959
|
return current;
|
|
21792
21960
|
}
|
|
21793
21961
|
}
|
|
21794
|
-
const parent =
|
|
21962
|
+
const parent = path65.dirname(current);
|
|
21795
21963
|
if (parent === current) {
|
|
21796
|
-
return
|
|
21964
|
+
return path65.resolve(startPath);
|
|
21797
21965
|
}
|
|
21798
21966
|
current = parent;
|
|
21799
21967
|
}
|
|
@@ -21801,7 +21969,7 @@ function findRepoRoot(startPath, layout) {
|
|
|
21801
21969
|
|
|
21802
21970
|
// src/adapters/cursor/cwd-resolution.ts
|
|
21803
21971
|
import os6 from "node:os";
|
|
21804
|
-
import
|
|
21972
|
+
import path66 from "node:path";
|
|
21805
21973
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21806
21974
|
function nonEmptyPathString(value) {
|
|
21807
21975
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
@@ -21813,36 +21981,36 @@ function resolveCursorActionCwdDetails(payload, fallback = process.cwd()) {
|
|
|
21813
21981
|
const workspaceRoot = Array.isArray(payload.workspace_roots) ? payload.workspace_roots.map(nonEmptyPathString).find(Boolean) : void 0;
|
|
21814
21982
|
if (toolInputWorkingDirectory) {
|
|
21815
21983
|
return {
|
|
21816
|
-
cwd:
|
|
21984
|
+
cwd: path66.resolve(toolInputWorkingDirectory),
|
|
21817
21985
|
source: "tool_input.working_directory",
|
|
21818
21986
|
fromPayload: true
|
|
21819
21987
|
};
|
|
21820
21988
|
}
|
|
21821
21989
|
if (topLevelCwd) {
|
|
21822
21990
|
return {
|
|
21823
|
-
cwd:
|
|
21991
|
+
cwd: path66.resolve(topLevelCwd),
|
|
21824
21992
|
source: "cwd",
|
|
21825
21993
|
fromPayload: true
|
|
21826
21994
|
};
|
|
21827
21995
|
}
|
|
21828
21996
|
if (workspaceRoot) {
|
|
21829
21997
|
return {
|
|
21830
|
-
cwd:
|
|
21998
|
+
cwd: path66.resolve(workspaceRoot),
|
|
21831
21999
|
source: "workspace_roots",
|
|
21832
22000
|
fromPayload: true
|
|
21833
22001
|
};
|
|
21834
22002
|
}
|
|
21835
22003
|
return {
|
|
21836
|
-
cwd:
|
|
22004
|
+
cwd: path66.resolve(fallback),
|
|
21837
22005
|
source: "fallback",
|
|
21838
22006
|
fromPayload: false
|
|
21839
22007
|
};
|
|
21840
22008
|
}
|
|
21841
22009
|
function isGlobalCursorHookRuntime() {
|
|
21842
22010
|
try {
|
|
21843
|
-
const runtimePath =
|
|
21844
|
-
const globalRuntimeDir =
|
|
21845
|
-
return runtimePath === globalRuntimeDir || runtimePath.startsWith(`${globalRuntimeDir}${
|
|
22011
|
+
const runtimePath = path66.resolve(fileURLToPath2(import.meta.url));
|
|
22012
|
+
const globalRuntimeDir = path66.resolve(path66.join(os6.homedir(), ".cursor", "belay", "runtime"));
|
|
22013
|
+
return runtimePath === globalRuntimeDir || runtimePath.startsWith(`${globalRuntimeDir}${path66.sep}`);
|
|
21846
22014
|
} catch {
|
|
21847
22015
|
return false;
|
|
21848
22016
|
}
|
|
@@ -21892,7 +22060,7 @@ function resolveCursorToolActionCwdDetails(payload, fallback, eventName, toolNam
|
|
|
21892
22060
|
function collectCandidateRepoRoots(payload, resolution) {
|
|
21893
22061
|
const roots = [];
|
|
21894
22062
|
const add = (value) => {
|
|
21895
|
-
const resolved =
|
|
22063
|
+
const resolved = path67.resolve(value);
|
|
21896
22064
|
if (!roots.includes(resolved)) {
|
|
21897
22065
|
roots.push(resolved);
|
|
21898
22066
|
}
|
|
@@ -21954,6 +22122,9 @@ function isSubagentEvent(payload, eventName) {
|
|
|
21954
22122
|
function isFileMutationTool(toolName) {
|
|
21955
22123
|
return toolName === "Write" || toolName === "StrReplace" || toolName === "Delete";
|
|
21956
22124
|
}
|
|
22125
|
+
function isCursorPreToolUseEvent(eventName) {
|
|
22126
|
+
return eventName === "preToolUse" || eventName === "PreToolUse";
|
|
22127
|
+
}
|
|
21957
22128
|
async function handleBeforeSubmitPromptHook(payload) {
|
|
21958
22129
|
try {
|
|
21959
22130
|
const prompt = String(payload.prompt ?? "");
|
|
@@ -21978,7 +22149,13 @@ async function handleBeforeSubmitPromptHook(payload) {
|
|
|
21978
22149
|
...result.user_message ? { user_message: result.user_message } : {},
|
|
21979
22150
|
...result.replay ? { replay: result.replay } : {}
|
|
21980
22151
|
};
|
|
21981
|
-
} catch {
|
|
22152
|
+
} catch (error) {
|
|
22153
|
+
if (isRepoConfigTrustError(error)) {
|
|
22154
|
+
return {
|
|
22155
|
+
continue: false,
|
|
22156
|
+
user_message: error.message
|
|
22157
|
+
};
|
|
22158
|
+
}
|
|
21982
22159
|
return {
|
|
21983
22160
|
continue: false,
|
|
21984
22161
|
user_message: "belay failed while processing approval state. Run belay doctor, then retry."
|
|
@@ -22009,7 +22186,13 @@ async function handleShellGateHook(payload) {
|
|
|
22009
22186
|
sourceEvent: "beforeShellExecution"
|
|
22010
22187
|
});
|
|
22011
22188
|
return gateVerdictToCursorResponse(verdict2);
|
|
22012
|
-
} catch {
|
|
22189
|
+
} catch (error) {
|
|
22190
|
+
if (isRepoConfigTrustError(error)) {
|
|
22191
|
+
return {
|
|
22192
|
+
permission: "deny",
|
|
22193
|
+
user_message: error.message
|
|
22194
|
+
};
|
|
22195
|
+
}
|
|
22013
22196
|
return {
|
|
22014
22197
|
permission: "deny",
|
|
22015
22198
|
user_message: "belay failed while classifying this shell command. Run belay doctor, then retry."
|
|
@@ -22022,6 +22205,9 @@ async function runShellGateHook() {
|
|
|
22022
22205
|
async function handleToolGateHook(eventName, payload) {
|
|
22023
22206
|
try {
|
|
22024
22207
|
const toolName = String(payload.tool_name ?? "");
|
|
22208
|
+
if (isCursorPreToolUseEvent(eventName) && toolName === "Shell") {
|
|
22209
|
+
return { permission: "allow" };
|
|
22210
|
+
}
|
|
22025
22211
|
const resolution = resolveCursorToolActionCwdDetails(
|
|
22026
22212
|
payload,
|
|
22027
22213
|
process2.cwd(),
|
|
@@ -22046,9 +22232,9 @@ async function handleToolGateHook(eventName, payload) {
|
|
|
22046
22232
|
});
|
|
22047
22233
|
return gateVerdictToCursorResponse(verdict2);
|
|
22048
22234
|
}
|
|
22049
|
-
if (toolName
|
|
22235
|
+
if (isFileMutationTool(toolName)) {
|
|
22050
22236
|
const verdict2 = await evaluateGatedAction(ctx, deps, {
|
|
22051
|
-
kind: "
|
|
22237
|
+
kind: "tool",
|
|
22052
22238
|
cwd,
|
|
22053
22239
|
payload,
|
|
22054
22240
|
toolName,
|
|
@@ -22056,27 +22242,33 @@ async function handleToolGateHook(eventName, payload) {
|
|
|
22056
22242
|
});
|
|
22057
22243
|
return gateVerdictToCursorResponse(verdict2);
|
|
22058
22244
|
}
|
|
22059
|
-
if (
|
|
22245
|
+
if (payload.tool_name === "Task") {
|
|
22060
22246
|
const verdict2 = await evaluateGatedAction(ctx, deps, {
|
|
22061
|
-
kind: "
|
|
22247
|
+
kind: "subagent",
|
|
22062
22248
|
cwd,
|
|
22063
22249
|
payload,
|
|
22064
|
-
toolName,
|
|
22065
22250
|
sourceEvent: eventName
|
|
22066
22251
|
});
|
|
22067
22252
|
return gateVerdictToCursorResponse(verdict2);
|
|
22068
22253
|
}
|
|
22069
|
-
if (
|
|
22254
|
+
if (isCursorPreToolUseEvent(eventName)) {
|
|
22070
22255
|
const verdict2 = await evaluateGatedAction(ctx, deps, {
|
|
22071
|
-
kind: "
|
|
22256
|
+
kind: "tool",
|
|
22072
22257
|
cwd,
|
|
22073
22258
|
payload,
|
|
22259
|
+
toolName,
|
|
22074
22260
|
sourceEvent: eventName
|
|
22075
22261
|
});
|
|
22076
22262
|
return gateVerdictToCursorResponse(verdict2);
|
|
22077
22263
|
}
|
|
22078
22264
|
return { permission: "allow" };
|
|
22079
|
-
} catch {
|
|
22265
|
+
} catch (error) {
|
|
22266
|
+
if (isRepoConfigTrustError(error)) {
|
|
22267
|
+
return {
|
|
22268
|
+
permission: "deny",
|
|
22269
|
+
user_message: error.message
|
|
22270
|
+
};
|
|
22271
|
+
}
|
|
22080
22272
|
return {
|
|
22081
22273
|
permission: "deny",
|
|
22082
22274
|
user_message: "belay failed while classifying this tool action. Run belay doctor, then retry."
|