@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,122 @@ 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
|
+
async function assertRepoConfigTrusted(repoRoot, adapter, rawConfig) {
|
|
3601
|
+
const status = await inspectRepoConfigTrust(repoRoot, adapter, rawConfig);
|
|
3602
|
+
if (status.trusted) {
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
throw new RepoConfigTrustError(status);
|
|
3606
|
+
}
|
|
3607
|
+
var TRUST_MESSAGE, RepoConfigTrustError;
|
|
3608
|
+
var init_repo_config_trust = __esm({
|
|
3609
|
+
"src/core/repo-config-trust.ts"() {
|
|
3610
|
+
"use strict";
|
|
3611
|
+
init_config();
|
|
3612
|
+
init_fingerprint2();
|
|
3613
|
+
init_path_utils();
|
|
3614
|
+
TRUST_MESSAGE = "Repository config is not trusted. Review it, then run `belay config trust`.";
|
|
3615
|
+
RepoConfigTrustError = class extends Error {
|
|
3616
|
+
status;
|
|
3617
|
+
constructor(status) {
|
|
3618
|
+
super(TRUST_MESSAGE);
|
|
3619
|
+
this.name = "RepoConfigTrustError";
|
|
3620
|
+
this.status = status;
|
|
3621
|
+
}
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
});
|
|
3625
|
+
|
|
3626
|
+
// src/config-io.ts
|
|
3627
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3628
|
+
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
3629
|
function resolveAdapterName(config) {
|
|
3517
3630
|
if (config.adapter === "claude") {
|
|
3518
3631
|
return "claude";
|
|
@@ -3523,10 +3636,10 @@ function resolveAdapterName(config) {
|
|
|
3523
3636
|
return "cursor";
|
|
3524
3637
|
}
|
|
3525
3638
|
function detectAdapterName(repoRoot) {
|
|
3526
|
-
if (
|
|
3639
|
+
if (existsSync3(configPathFor(repoRoot, "claude"))) {
|
|
3527
3640
|
return "claude";
|
|
3528
3641
|
}
|
|
3529
|
-
if (
|
|
3642
|
+
if (existsSync3(configPathFor(repoRoot, "codex"))) {
|
|
3530
3643
|
return "codex";
|
|
3531
3644
|
}
|
|
3532
3645
|
return "cursor";
|
|
@@ -3541,20 +3654,20 @@ async function loadLayeredConfig(repoRoot, adapter = detectAdapterName(repoRoot)
|
|
|
3541
3654
|
const layout = getAdapterLayout(adapter);
|
|
3542
3655
|
const configPath = configPathFor(repoRoot, adapter);
|
|
3543
3656
|
let repoConfig = {};
|
|
3544
|
-
if (
|
|
3545
|
-
repoConfig = JSON.parse(await
|
|
3657
|
+
if (existsSync3(configPath)) {
|
|
3658
|
+
repoConfig = JSON.parse(await readFile3(configPath, "utf8"));
|
|
3546
3659
|
}
|
|
3547
3660
|
let teamConfig = null;
|
|
3548
3661
|
const teamPath = teamConfigPath();
|
|
3549
|
-
if (
|
|
3550
|
-
teamConfig = JSON.parse(await
|
|
3662
|
+
if (existsSync3(teamPath)) {
|
|
3663
|
+
teamConfig = JSON.parse(await readFile3(teamPath, "utf8"));
|
|
3551
3664
|
}
|
|
3552
3665
|
return resolveLayeredConfig({
|
|
3553
3666
|
repoConfig,
|
|
3554
3667
|
adapterDefaults: layout.defaultConfig(repoRoot),
|
|
3555
3668
|
teamConfig,
|
|
3556
3669
|
teamConfigPath: teamPath,
|
|
3557
|
-
repoConfigPath:
|
|
3670
|
+
repoConfigPath: existsSync3(configPath) ? configPath : void 0
|
|
3558
3671
|
});
|
|
3559
3672
|
}
|
|
3560
3673
|
async function loadConfigFile(repoRoot, adapter = detectAdapterName(repoRoot)) {
|
|
@@ -3569,21 +3682,22 @@ var init_config_io = __esm({
|
|
|
3569
3682
|
init_approval_state_mutation();
|
|
3570
3683
|
init_config();
|
|
3571
3684
|
init_config_layers();
|
|
3685
|
+
init_repo_config_trust();
|
|
3572
3686
|
}
|
|
3573
3687
|
});
|
|
3574
3688
|
|
|
3575
3689
|
// src/adapters/codex/runtime-entry.ts
|
|
3576
3690
|
init_codex();
|
|
3577
|
-
import
|
|
3691
|
+
import path65 from "node:path";
|
|
3578
3692
|
import process2 from "node:process";
|
|
3579
3693
|
|
|
3580
3694
|
// src/adapters/shared/gate-runtime.ts
|
|
3581
3695
|
init_approval();
|
|
3582
3696
|
init_approval_replay();
|
|
3583
3697
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
3584
|
-
import { existsSync as
|
|
3585
|
-
import { mkdir as
|
|
3586
|
-
import
|
|
3698
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
3699
|
+
import { mkdir as mkdir15, readFile as readFile16, writeFile as writeFile11 } from "node:fs/promises";
|
|
3700
|
+
import path63 from "node:path";
|
|
3587
3701
|
|
|
3588
3702
|
// src/core/approval-service.ts
|
|
3589
3703
|
init_config_io();
|
|
@@ -3593,47 +3707,35 @@ init_approval_replay();
|
|
|
3593
3707
|
// src/core/approval-token.ts
|
|
3594
3708
|
init_config();
|
|
3595
3709
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
3596
|
-
import { existsSync as
|
|
3597
|
-
import { mkdir as
|
|
3598
|
-
import
|
|
3599
|
-
function base64UrlEncode(value) {
|
|
3600
|
-
return Buffer.from(value, "utf8").toString("base64url");
|
|
3601
|
-
}
|
|
3710
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
3711
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
3712
|
+
import path12 from "node:path";
|
|
3602
3713
|
function base64UrlDecode(value) {
|
|
3603
3714
|
return Buffer.from(value, "base64url").toString("utf8");
|
|
3604
3715
|
}
|
|
3605
3716
|
function approvalSigningKeyPath(controlPlaneDir = defaultControlPlaneDir()) {
|
|
3606
|
-
return
|
|
3717
|
+
return path12.join(controlPlaneDir, "approval-signing.key");
|
|
3607
3718
|
}
|
|
3608
3719
|
async function loadOrCreateApprovalSigningKey(controlPlaneDir = defaultControlPlaneDir()) {
|
|
3609
3720
|
const keyPath = approvalSigningKeyPath(controlPlaneDir);
|
|
3610
|
-
if (
|
|
3611
|
-
return
|
|
3721
|
+
if (existsSync4(keyPath)) {
|
|
3722
|
+
return readFile4(keyPath);
|
|
3612
3723
|
}
|
|
3613
|
-
await
|
|
3724
|
+
await mkdir4(controlPlaneDir, { recursive: true });
|
|
3614
3725
|
const key = randomBytes(32);
|
|
3615
3726
|
await writeFile2(keyPath, key, { mode: 384 });
|
|
3616
3727
|
return key;
|
|
3617
3728
|
}
|
|
3618
|
-
function signPayload(payload, key) {
|
|
3619
|
-
const body = base64UrlEncode(JSON.stringify(payload));
|
|
3620
|
-
const signature = createHmac("sha256", key).update(body).digest("base64url");
|
|
3621
|
-
return `${body}.${signature}`;
|
|
3622
|
-
}
|
|
3623
|
-
async function issueApprovalToken(payload, controlPlaneDir = defaultControlPlaneDir()) {
|
|
3624
|
-
const key = await loadOrCreateApprovalSigningKey(controlPlaneDir);
|
|
3625
|
-
return signPayload(payload, key);
|
|
3626
|
-
}
|
|
3627
3729
|
async function verifyApprovalToken(token, controlPlaneDir = defaultControlPlaneDir()) {
|
|
3628
3730
|
const [body, signature] = token.split(".");
|
|
3629
3731
|
if (!body || !signature) {
|
|
3630
3732
|
return null;
|
|
3631
3733
|
}
|
|
3632
3734
|
const keyPath = approvalSigningKeyPath(controlPlaneDir);
|
|
3633
|
-
if (!
|
|
3735
|
+
if (!existsSync4(keyPath)) {
|
|
3634
3736
|
return null;
|
|
3635
3737
|
}
|
|
3636
|
-
const key = await
|
|
3738
|
+
const key = await readFile4(keyPath);
|
|
3637
3739
|
const expected = createHmac("sha256", key).update(body).digest("base64url");
|
|
3638
3740
|
const actualBuffer = Buffer.from(signature);
|
|
3639
3741
|
const expectedBuffer = Buffer.from(expected);
|
|
@@ -3840,11 +3942,11 @@ init_approval_v3();
|
|
|
3840
3942
|
|
|
3841
3943
|
// src/core/capability/boundary-attestation-sign.ts
|
|
3842
3944
|
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
3843
|
-
import { access, readFile as
|
|
3945
|
+
import { access, readFile as readFile5 } from "node:fs/promises";
|
|
3844
3946
|
init_fingerprint2();
|
|
3845
3947
|
|
|
3846
3948
|
// src/core/capability/attestation.ts
|
|
3847
|
-
import
|
|
3949
|
+
import path13 from "node:path";
|
|
3848
3950
|
var BOUNDARY_ATTESTATION_VERSION = 1;
|
|
3849
3951
|
var CONTAINED_EXECUTION_ATTESTATION_VERSION = 1;
|
|
3850
3952
|
var KNOWN_DRIVERS = /* @__PURE__ */ new Set([
|
|
@@ -3912,7 +4014,7 @@ function validateContainedExecutionAttestation(value) {
|
|
|
3912
4014
|
const record = value;
|
|
3913
4015
|
const dockerSubstrate = record.dockerSubstrate;
|
|
3914
4016
|
const dockerConfiguration = record.dockerConfiguration;
|
|
3915
|
-
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" || !
|
|
4017
|
+
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" || !path13.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:///") || !path13.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" || !path13.isAbsolute(dockerConfiguration.executable) || /[\0\n\r]/.test(dockerConfiguration.executable) || typeof dockerConfiguration.host !== "string" || !dockerConfiguration.host.startsWith("unix:///") || !path13.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") {
|
|
3916
4018
|
return false;
|
|
3917
4019
|
}
|
|
3918
4020
|
const probedAt = Date.parse(record.probedAt);
|
|
@@ -3982,7 +4084,7 @@ async function verifySignedBoundaryAttestation(params) {
|
|
|
3982
4084
|
return record.attestation;
|
|
3983
4085
|
}
|
|
3984
4086
|
async function readSignedAttestationFile(filePath) {
|
|
3985
|
-
return JSON.parse(await
|
|
4087
|
+
return JSON.parse(await readFile5(filePath, "utf8"));
|
|
3986
4088
|
}
|
|
3987
4089
|
|
|
3988
4090
|
// src/core/capability/boundary-egress.ts
|
|
@@ -4151,13 +4253,13 @@ async function runWithBoundaryRunnable(target, params) {
|
|
|
4151
4253
|
}
|
|
4152
4254
|
|
|
4153
4255
|
// src/core/capability/boundary-session.ts
|
|
4154
|
-
import
|
|
4256
|
+
import path24 from "node:path";
|
|
4155
4257
|
|
|
4156
4258
|
// src/services/egress-service.ts
|
|
4157
|
-
import { existsSync as
|
|
4158
|
-
import { mkdir as
|
|
4259
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
4260
|
+
import { mkdir as mkdir5, readFile as readFile6, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
4159
4261
|
import net from "node:net";
|
|
4160
|
-
import
|
|
4262
|
+
import path14 from "node:path";
|
|
4161
4263
|
init_config_io();
|
|
4162
4264
|
init_config();
|
|
4163
4265
|
|
|
@@ -4170,16 +4272,16 @@ function egressStatePaths(repoRoot, config) {
|
|
|
4170
4272
|
const stateDir = belayStateDir(config, repoLocalStateDirFor(repoRoot, config));
|
|
4171
4273
|
return {
|
|
4172
4274
|
stateDir,
|
|
4173
|
-
pidPath:
|
|
4174
|
-
statusPath:
|
|
4275
|
+
pidPath: path14.join(stateDir, "egress-proxy.pid"),
|
|
4276
|
+
statusPath: path14.join(stateDir, "egress-proxy.json")
|
|
4175
4277
|
};
|
|
4176
4278
|
}
|
|
4177
4279
|
async function readStatusFile(statusPath) {
|
|
4178
|
-
if (!
|
|
4280
|
+
if (!existsSync5(statusPath)) {
|
|
4179
4281
|
return null;
|
|
4180
4282
|
}
|
|
4181
4283
|
try {
|
|
4182
|
-
const raw = JSON.parse(await
|
|
4284
|
+
const raw = JSON.parse(await readFile6(statusPath, "utf8"));
|
|
4183
4285
|
if (typeof raw.pid !== "number") {
|
|
4184
4286
|
return null;
|
|
4185
4287
|
}
|
|
@@ -4197,9 +4299,9 @@ async function readStatusFile(statusPath) {
|
|
|
4197
4299
|
async function isPortOpen(host, port) {
|
|
4198
4300
|
return new Promise((resolve) => {
|
|
4199
4301
|
const socket = net.createConnection({ host, port });
|
|
4200
|
-
const finish = (
|
|
4302
|
+
const finish = (open7) => {
|
|
4201
4303
|
socket.destroy();
|
|
4202
|
-
resolve(
|
|
4304
|
+
resolve(open7);
|
|
4203
4305
|
};
|
|
4204
4306
|
socket.setTimeout(300);
|
|
4205
4307
|
socket.on("connect", () => finish(true));
|
|
@@ -4210,7 +4312,7 @@ async function isPortOpen(host, port) {
|
|
|
4210
4312
|
async function resolveLiveEgressStatus(repoRoot, config) {
|
|
4211
4313
|
const { statusPath } = egressStatePaths(repoRoot, config);
|
|
4212
4314
|
const statusCandidates = [statusPath];
|
|
4213
|
-
const controlPlaneStatus =
|
|
4315
|
+
const controlPlaneStatus = path14.join(configuredControlPlaneDir(config), "egress-proxy.json");
|
|
4214
4316
|
if (!statusCandidates.includes(controlPlaneStatus)) {
|
|
4215
4317
|
statusCandidates.push(controlPlaneStatus);
|
|
4216
4318
|
}
|
|
@@ -4236,7 +4338,7 @@ function isProcessAlive(pid) {
|
|
|
4236
4338
|
}
|
|
4237
4339
|
}
|
|
4238
4340
|
async function egressStatus(options = {}) {
|
|
4239
|
-
const repoRoot =
|
|
4341
|
+
const repoRoot = path14.resolve(options.targetDir ?? process.cwd());
|
|
4240
4342
|
const config = await loadConfigFile(repoRoot);
|
|
4241
4343
|
const { status, host, port, portOccupied } = await resolveLiveEgressStatus(repoRoot, config);
|
|
4242
4344
|
const ownedRunning = Boolean(status);
|
|
@@ -4267,7 +4369,7 @@ init_config();
|
|
|
4267
4369
|
import { createHash as createHash8, randomUUID as randomUUID2 } from "node:crypto";
|
|
4268
4370
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4269
4371
|
import { access as access2, constants as constants2, lstat as lstat4, mkdtemp as mkdtemp2, realpath as realpath2, rm as rm2 } from "node:fs/promises";
|
|
4270
|
-
import
|
|
4372
|
+
import path19 from "node:path";
|
|
4271
4373
|
init_fingerprint2();
|
|
4272
4374
|
init_path_utils();
|
|
4273
4375
|
|
|
@@ -4446,7 +4548,7 @@ function runProcessWithBoundedOutput(file, args, options, timeoutMs, outputPolic
|
|
|
4446
4548
|
}
|
|
4447
4549
|
|
|
4448
4550
|
// src/core/contained-execution/docker-policy.ts
|
|
4449
|
-
import
|
|
4551
|
+
import path15 from "node:path";
|
|
4450
4552
|
|
|
4451
4553
|
// src/core/contained-execution/policy.ts
|
|
4452
4554
|
var CONTAINED_EXECUTION_APPROVAL_FALLBACK_REASONS = [
|
|
@@ -4575,7 +4677,7 @@ var PROXY_ENV_NAMES = [
|
|
|
4575
4677
|
var IMAGE_ID_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
4576
4678
|
var SAFE_CONTAINER_NAME = /^belay-contained-[0-9a-f-]{36}$/;
|
|
4577
4679
|
function assertSafeDockerPath(value, code) {
|
|
4578
|
-
if (!
|
|
4680
|
+
if (!path15.isAbsolute(value) || /[\0\n\r,]/.test(value)) {
|
|
4579
4681
|
throw new ContainedExecutionFailureError(code);
|
|
4580
4682
|
}
|
|
4581
4683
|
}
|
|
@@ -4654,11 +4756,11 @@ init_path_utils();
|
|
|
4654
4756
|
import { createHash as createHash7 } from "node:crypto";
|
|
4655
4757
|
import { constants as fsConstants } from "node:fs";
|
|
4656
4758
|
import {
|
|
4657
|
-
chmod as
|
|
4759
|
+
chmod as chmod3,
|
|
4658
4760
|
lstat as lstat3,
|
|
4659
|
-
mkdir as
|
|
4761
|
+
mkdir as mkdir6,
|
|
4660
4762
|
mkdtemp,
|
|
4661
|
-
open as
|
|
4763
|
+
open as open4,
|
|
4662
4764
|
opendir,
|
|
4663
4765
|
readlink as readlink2,
|
|
4664
4766
|
realpath,
|
|
@@ -4666,41 +4768,41 @@ import {
|
|
|
4666
4768
|
symlink
|
|
4667
4769
|
} from "node:fs/promises";
|
|
4668
4770
|
import os from "node:os";
|
|
4669
|
-
import
|
|
4771
|
+
import path18 from "node:path";
|
|
4670
4772
|
|
|
4671
4773
|
// src/core/transactional/file-tree.ts
|
|
4672
4774
|
import { createHash as createHash6 } from "node:crypto";
|
|
4673
4775
|
import { lstat as lstat2, readdir } from "node:fs/promises";
|
|
4674
|
-
import
|
|
4776
|
+
import path17 from "node:path";
|
|
4675
4777
|
|
|
4676
4778
|
// src/core/transactional/file-tree-path.ts
|
|
4677
4779
|
init_path_utils();
|
|
4678
|
-
import
|
|
4780
|
+
import path16 from "node:path";
|
|
4679
4781
|
var FILE_CHECKPOINT_PATH_ESCAPE = "file_checkpoint_path_escape";
|
|
4680
4782
|
function validateRelativePath(relativePath) {
|
|
4681
4783
|
if (!relativePath || relativePath.includes("\0")) {
|
|
4682
4784
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4683
4785
|
}
|
|
4684
|
-
if (
|
|
4786
|
+
if (path16.isAbsolute(relativePath)) {
|
|
4685
4787
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4686
4788
|
}
|
|
4687
4789
|
if (isPathOutsideRoot(relativePath)) {
|
|
4688
4790
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4689
4791
|
}
|
|
4690
|
-
const normalized =
|
|
4691
|
-
if (normalized === ".." || normalized.startsWith(`..${
|
|
4792
|
+
const normalized = path16.normalize(relativePath);
|
|
4793
|
+
if (normalized === ".." || normalized.startsWith(`..${path16.sep}`)) {
|
|
4692
4794
|
throw new Error(FILE_CHECKPOINT_PATH_ESCAPE);
|
|
4693
4795
|
}
|
|
4694
4796
|
}
|
|
4695
4797
|
function joinRelativePath(root, relativePath) {
|
|
4696
4798
|
validateRelativePath(relativePath);
|
|
4697
|
-
return
|
|
4799
|
+
return path16.join(canonicalPath(root), relativePath);
|
|
4698
4800
|
}
|
|
4699
4801
|
function isRootGitMetadataPath(relativePath) {
|
|
4700
|
-
return relativePath === ".git" || relativePath.startsWith(`.git${
|
|
4802
|
+
return relativePath === ".git" || relativePath.startsWith(`.git${path16.sep}`);
|
|
4701
4803
|
}
|
|
4702
4804
|
function isNestedGitPath(relativePath) {
|
|
4703
|
-
const segments = relativePath.split(
|
|
4805
|
+
const segments = relativePath.split(path16.sep).filter(Boolean);
|
|
4704
4806
|
if (segments.length === 0) {
|
|
4705
4807
|
return false;
|
|
4706
4808
|
}
|
|
@@ -4867,7 +4969,7 @@ async function readPresentNode(absolutePath, counters) {
|
|
|
4867
4969
|
async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters, quotas, deadlineMs, entries) {
|
|
4868
4970
|
assertWithinDeadline(deadlineMs);
|
|
4869
4971
|
assertWithinQuotas(counters, quotas);
|
|
4870
|
-
const absoluteDir = relativeDir ? joinRelativePath(resourceRoot, relativeDir) :
|
|
4972
|
+
const absoluteDir = relativeDir ? joinRelativePath(resourceRoot, relativeDir) : path17.resolve(resourceRoot);
|
|
4871
4973
|
const dirInfo = await lstat2(absoluteDir);
|
|
4872
4974
|
if (!dirInfo.isDirectory()) {
|
|
4873
4975
|
throw new Error(FILE_CHECKPOINT_UNSUPPORTED_NODE);
|
|
@@ -4893,7 +4995,7 @@ async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters,
|
|
|
4893
4995
|
const names = await readdir(absoluteDir);
|
|
4894
4996
|
for (const name of names) {
|
|
4895
4997
|
assertWithinDeadline(deadlineMs);
|
|
4896
|
-
const childRelative = relativeDir ?
|
|
4998
|
+
const childRelative = relativeDir ? path17.join(relativeDir, name) : name;
|
|
4897
4999
|
validateRelativePath(childRelative);
|
|
4898
5000
|
if (isNestedGitPath(childRelative)) {
|
|
4899
5001
|
throw new Error(FILE_CHECKPOINT_NESTED_REPOSITORY);
|
|
@@ -4901,7 +5003,7 @@ async function walkDirectory(resourceRoot, relativeDir, excludedRoots, counters,
|
|
|
4901
5003
|
if (isExcludedTreePath(childRelative, excludedRoots, resourceRoot)) {
|
|
4902
5004
|
continue;
|
|
4903
5005
|
}
|
|
4904
|
-
const childAbsolute =
|
|
5006
|
+
const childAbsolute = path17.join(absoluteDir, name);
|
|
4905
5007
|
const childInfo = await lstat2(childAbsolute);
|
|
4906
5008
|
if (childInfo.isDirectory() && !childInfo.isSymbolicLink()) {
|
|
4907
5009
|
await walkDirectory(
|
|
@@ -5012,16 +5114,16 @@ function validateContainedExecutionMirrorLease(handle, expected) {
|
|
|
5012
5114
|
);
|
|
5013
5115
|
}
|
|
5014
5116
|
var productionDependencies = {
|
|
5015
|
-
makeTempRoot: () => mkdtemp(
|
|
5117
|
+
makeTempRoot: () => mkdtemp(path18.join(os.tmpdir(), "belay-contained-mirror-")),
|
|
5016
5118
|
removeRoot: (root) => rm(root, { recursive: true, force: true }),
|
|
5017
5119
|
now: () => Date.now()
|
|
5018
5120
|
};
|
|
5019
5121
|
function isAtOrWithin(root, target) {
|
|
5020
|
-
const relative =
|
|
5021
|
-
return relative === "" || !
|
|
5122
|
+
const relative = path18.relative(root, target);
|
|
5123
|
+
return relative === "" || !path18.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path18.sep}`);
|
|
5022
5124
|
}
|
|
5023
5125
|
function isGitMetadataRelativePath(relativePath) {
|
|
5024
|
-
return relativePath.split(
|
|
5126
|
+
return relativePath.split(path18.sep).some((segment) => segment.toLowerCase() === ".git");
|
|
5025
5127
|
}
|
|
5026
5128
|
function identityFromStats(stats) {
|
|
5027
5129
|
return {
|
|
@@ -5103,7 +5205,7 @@ async function readStableFile(absolutePath, context) {
|
|
|
5103
5205
|
if (before.nlink > 1n) {
|
|
5104
5206
|
throw new Error(FILE_CHECKPOINT_HARDLINK_UNSUPPORTED);
|
|
5105
5207
|
}
|
|
5106
|
-
const source = await
|
|
5208
|
+
const source = await open4(absolutePath, safeReadFlags());
|
|
5107
5209
|
try {
|
|
5108
5210
|
const opened = await source.stat({ bigint: true });
|
|
5109
5211
|
assertRegularSingleLinkBigInt(opened);
|
|
@@ -5158,7 +5260,7 @@ function addMetadataRoots(context, directoryPath) {
|
|
|
5158
5260
|
}
|
|
5159
5261
|
}
|
|
5160
5262
|
function pathMatchesRoots(absolutePath, roots) {
|
|
5161
|
-
const lexical =
|
|
5263
|
+
const lexical = path18.resolve(absolutePath);
|
|
5162
5264
|
const canonical = canonicalPath(absolutePath);
|
|
5163
5265
|
for (const root of roots) {
|
|
5164
5266
|
if (isAtOrWithin(root, lexical) || isAtOrWithin(root, canonical)) {
|
|
@@ -5168,7 +5270,7 @@ function pathMatchesRoots(absolutePath, roots) {
|
|
|
5168
5270
|
return false;
|
|
5169
5271
|
}
|
|
5170
5272
|
function pathLexicallyMatchesRoots(absolutePath, roots) {
|
|
5171
|
-
const lexical =
|
|
5273
|
+
const lexical = path18.resolve(absolutePath);
|
|
5172
5274
|
for (const root of roots) {
|
|
5173
5275
|
if (isAtOrWithin(root, lexical)) {
|
|
5174
5276
|
return true;
|
|
@@ -5189,11 +5291,11 @@ async function readSafeSymlink(absolutePath, context) {
|
|
|
5189
5291
|
}
|
|
5190
5292
|
const identity = identityFromStats(before);
|
|
5191
5293
|
const target = await readlink2(absolutePath);
|
|
5192
|
-
if (
|
|
5294
|
+
if (path18.isAbsolute(target)) {
|
|
5193
5295
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_UNSAFE_SYMLINK);
|
|
5194
5296
|
}
|
|
5195
|
-
const lexicalTarget =
|
|
5196
|
-
if (!isAtOrWithin(context.sourceRoot, lexicalTarget) || isGitMetadataRelativePath(
|
|
5297
|
+
const lexicalTarget = path18.resolve(path18.dirname(absolutePath), target);
|
|
5298
|
+
if (!isAtOrWithin(context.sourceRoot, lexicalTarget) || isGitMetadataRelativePath(path18.relative(context.sourceRoot, lexicalTarget)) || pathMatchesRoots(lexicalTarget, context.protectedRoots) || pathMatchesRoots(lexicalTarget, context.metadataRoots)) {
|
|
5197
5299
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_UNSAFE_SYMLINK);
|
|
5198
5300
|
}
|
|
5199
5301
|
let resolvedTarget;
|
|
@@ -5229,7 +5331,7 @@ async function walkDirectory2(relativeDirectory, entries, context) {
|
|
|
5229
5331
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5230
5332
|
}
|
|
5231
5333
|
const directoryFlags = safeReadFlags() | (fsConstants.O_DIRECTORY ?? 0);
|
|
5232
|
-
const directory = await
|
|
5334
|
+
const directory = await open4(absoluteDirectory, directoryFlags);
|
|
5233
5335
|
try {
|
|
5234
5336
|
const opened = await directory.stat({ bigint: true });
|
|
5235
5337
|
if (!opened.isDirectory() || !identitiesEqual(beforeIdentity, identityFromStats(opened))) {
|
|
@@ -5238,7 +5340,7 @@ async function walkDirectory2(relativeDirectory, entries, context) {
|
|
|
5238
5340
|
const entriesDirectory = await opendir(absoluteDirectory, { bufferSize: 32 });
|
|
5239
5341
|
for await (const directoryEntry of entriesDirectory) {
|
|
5240
5342
|
assertDeadline(context.deadlineMs, context.now);
|
|
5241
|
-
const relativePath = relativeDirectory ?
|
|
5343
|
+
const relativePath = relativeDirectory ? path18.join(relativeDirectory, directoryEntry.name) : directoryEntry.name;
|
|
5242
5344
|
const absolutePath = joinRelativePath(context.sourceRoot, relativePath);
|
|
5243
5345
|
const info = await lstat3(absolutePath);
|
|
5244
5346
|
if (info.isSymbolicLink()) {
|
|
@@ -5322,7 +5424,7 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5322
5424
|
}
|
|
5323
5425
|
assertEntryIdentity(entry, identityFromStats(before));
|
|
5324
5426
|
await context.beforeCopyOpen?.(sourcePath);
|
|
5325
|
-
const source = await
|
|
5427
|
+
const source = await open4(sourcePath, safeReadFlags());
|
|
5326
5428
|
let destination;
|
|
5327
5429
|
try {
|
|
5328
5430
|
const opened = await source.stat({ bigint: true });
|
|
@@ -5332,8 +5434,8 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5332
5434
|
if (entry.node.kind !== "file") {
|
|
5333
5435
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5334
5436
|
}
|
|
5335
|
-
await
|
|
5336
|
-
destination = await
|
|
5437
|
+
await mkdir6(path18.dirname(destinationPath), { recursive: true, mode: 448 });
|
|
5438
|
+
destination = await open4(
|
|
5337
5439
|
destinationPath,
|
|
5338
5440
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,
|
|
5339
5441
|
safePermissionMode(opened.mode)
|
|
@@ -5375,7 +5477,7 @@ async function copyStableRegularFile(sourcePath, destinationPath, entry, context
|
|
|
5375
5477
|
if (entry.node.kind !== "file") {
|
|
5376
5478
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5377
5479
|
}
|
|
5378
|
-
await
|
|
5480
|
+
await chmod3(destinationPath, safePermissionMode(entry.node.mode));
|
|
5379
5481
|
}
|
|
5380
5482
|
async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, context) {
|
|
5381
5483
|
const directoryEntries = [];
|
|
@@ -5384,7 +5486,7 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5384
5486
|
const sourcePath = joinRelativePath(sourceRoot, entry.relativePath);
|
|
5385
5487
|
const destinationPath = joinRelativePath(destinationRoot, entry.relativePath);
|
|
5386
5488
|
if (entry.node.kind === "directory") {
|
|
5387
|
-
await
|
|
5489
|
+
await mkdir6(destinationPath, { recursive: true, mode: 448 });
|
|
5388
5490
|
directoryEntries.push(entry);
|
|
5389
5491
|
continue;
|
|
5390
5492
|
}
|
|
@@ -5396,7 +5498,7 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5396
5498
|
if (current !== entry.node.target) {
|
|
5397
5499
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5398
5500
|
}
|
|
5399
|
-
await
|
|
5501
|
+
await mkdir6(path18.dirname(destinationPath), { recursive: true, mode: 448 });
|
|
5400
5502
|
await symlink(entry.node.target, destinationPath);
|
|
5401
5503
|
continue;
|
|
5402
5504
|
}
|
|
@@ -5406,12 +5508,12 @@ async function materializeSnapshot(sourceRoot, destinationRoot, snapshot, contex
|
|
|
5406
5508
|
if (entry.node.kind !== "directory") {
|
|
5407
5509
|
throw new ContainedExecutionFailureError(CONTAINED_EXECUTION_SOURCE_CHANGED);
|
|
5408
5510
|
}
|
|
5409
|
-
await
|
|
5511
|
+
await chmod3(
|
|
5410
5512
|
joinRelativePath(destinationRoot, entry.relativePath),
|
|
5411
5513
|
safePermissionMode(entry.node.mode)
|
|
5412
5514
|
);
|
|
5413
5515
|
}
|
|
5414
|
-
await
|
|
5516
|
+
await chmod3(destinationRoot, 448);
|
|
5415
5517
|
}
|
|
5416
5518
|
async function assertStableCopiedSnapshot(sourceRoot, destinationRoot, protectedRoots, limits, deadlineMs, now, before) {
|
|
5417
5519
|
const [after, copied] = await Promise.all([
|
|
@@ -5456,7 +5558,7 @@ async function cleanupOwnedRoot(root, dependencies) {
|
|
|
5456
5558
|
}
|
|
5457
5559
|
async function prepareWithDependencies(options, dependencies) {
|
|
5458
5560
|
validateOptions(options);
|
|
5459
|
-
const guestWorkspacePath =
|
|
5561
|
+
const guestWorkspacePath = path18.resolve(options.sourceRoot);
|
|
5460
5562
|
const sourceRoot = canonicalPath(options.sourceRoot);
|
|
5461
5563
|
const protectedRoots = options.controlPlaneRoots.map((root) => canonicalPath(root));
|
|
5462
5564
|
if (pathMatchesRoots(sourceRoot, protectedRoots)) {
|
|
@@ -5464,7 +5566,7 @@ async function prepareWithDependencies(options, dependencies) {
|
|
|
5464
5566
|
}
|
|
5465
5567
|
const guestRoot = await dependencies.makeTempRoot();
|
|
5466
5568
|
try {
|
|
5467
|
-
await
|
|
5569
|
+
await chmod3(guestRoot, 448);
|
|
5468
5570
|
const deadlineMs = dependencies.now() + options.limits.prepareTimeoutMs;
|
|
5469
5571
|
const before = await buildSafeMirrorSnapshot(
|
|
5470
5572
|
sourceRoot,
|
|
@@ -5492,7 +5594,7 @@ async function prepareWithDependencies(options, dependencies) {
|
|
|
5492
5594
|
dependencies.now,
|
|
5493
5595
|
before
|
|
5494
5596
|
);
|
|
5495
|
-
await
|
|
5597
|
+
await chmod3(guestRoot, 448);
|
|
5496
5598
|
const lease = {
|
|
5497
5599
|
sourceRoot,
|
|
5498
5600
|
hostMirrorRoot: guestRoot,
|
|
@@ -5626,14 +5728,14 @@ async function digestFile(file) {
|
|
|
5626
5728
|
return hash.digest("hex");
|
|
5627
5729
|
}
|
|
5628
5730
|
async function resolveConfiguredDockerSubstrate(params) {
|
|
5629
|
-
if (!
|
|
5731
|
+
if (!path19.isAbsolute(params.executable) || /[\0\n\r]/.test(params.executable)) {
|
|
5630
5732
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_binary_invalid");
|
|
5631
5733
|
}
|
|
5632
5734
|
if (!params.host.startsWith("unix:///") || /[\0\n\r]/.test(params.host)) {
|
|
5633
5735
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_host_invalid");
|
|
5634
5736
|
}
|
|
5635
5737
|
const configuredSocket = params.host.slice("unix://".length);
|
|
5636
|
-
if (!
|
|
5738
|
+
if (!path19.isAbsolute(configuredSocket)) {
|
|
5637
5739
|
throw new ContainedDockerBoundaryUnavailableError("contained_execution_docker_host_invalid");
|
|
5638
5740
|
}
|
|
5639
5741
|
let binaryPath;
|
|
@@ -5894,7 +5996,7 @@ async function validatedGuestCwd(params) {
|
|
|
5894
5996
|
const requiredExclusions = [
|
|
5895
5997
|
...new Set([params.controlPlaneDir, ...params.protectedRoots].map(canonicalPath))
|
|
5896
5998
|
];
|
|
5897
|
-
if (params.mirror.backend !== "file_copy" || params.mirror.guestWorkspacePath !==
|
|
5999
|
+
if (params.mirror.backend !== "file_copy" || params.mirror.guestWorkspacePath !== path19.resolve(params.repoRoot) || !path19.isAbsolute(params.guestCwd) || !pathWithinRoot(params.mirror.guestWorkspacePath, params.guestCwd))
|
|
5898
6000
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5899
6001
|
const resolvedRoot = await realpath2(params.mirror.hostMirrorRoot);
|
|
5900
6002
|
const protectedRoots = [canonicalPath(params.repoRoot), ...requiredExclusions];
|
|
@@ -5906,10 +6008,10 @@ async function validatedGuestCwd(params) {
|
|
|
5906
6008
|
protectedRoots: requiredExclusions
|
|
5907
6009
|
}))
|
|
5908
6010
|
throw new ContainedExecutionFailureError("contained_execution_invalid_mirror_lease");
|
|
5909
|
-
const relative =
|
|
6011
|
+
const relative = path19.relative(params.mirror.guestWorkspacePath, path19.resolve(params.guestCwd));
|
|
5910
6012
|
let resolvedCwd;
|
|
5911
6013
|
try {
|
|
5912
|
-
resolvedCwd = await realpath2(
|
|
6014
|
+
resolvedCwd = await realpath2(path19.join(resolvedRoot, relative));
|
|
5913
6015
|
} catch {
|
|
5914
6016
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5915
6017
|
}
|
|
@@ -5917,7 +6019,7 @@ async function validatedGuestCwd(params) {
|
|
|
5917
6019
|
if (!info.isDirectory() || !pathWithinRoot(resolvedRoot, resolvedCwd)) {
|
|
5918
6020
|
throw new ContainedExecutionFailureError("contained_execution_invalid_cwd");
|
|
5919
6021
|
}
|
|
5920
|
-
return
|
|
6022
|
+
return path19.join(params.mirror.guestWorkspacePath, path19.relative(resolvedRoot, resolvedCwd));
|
|
5921
6023
|
}
|
|
5922
6024
|
function exactKeys(value, allowed) {
|
|
5923
6025
|
const names = new Set(allowed);
|
|
@@ -6144,19 +6246,19 @@ async function executeContainedDocker(params) {
|
|
|
6144
6246
|
init_path_utils();
|
|
6145
6247
|
import { spawn as spawn3 } from "node:child_process";
|
|
6146
6248
|
import os3 from "node:os";
|
|
6147
|
-
import
|
|
6249
|
+
import path21 from "node:path";
|
|
6148
6250
|
|
|
6149
6251
|
// src/core/transactional/apply-observed-changes.ts
|
|
6150
|
-
import { copyFile, lstat as lstat5, mkdir as
|
|
6252
|
+
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";
|
|
6151
6253
|
import os2 from "node:os";
|
|
6152
|
-
import
|
|
6254
|
+
import path20 from "node:path";
|
|
6153
6255
|
var TRANSACTIONAL_APPLY_TOCTOU = "transactional_apply_toctou";
|
|
6154
6256
|
var TRANSACTIONAL_APPLY_CONFLICT = "transactional_apply_conflict";
|
|
6155
6257
|
var TRANSACTIONAL_APPLY_ROLLBACK_FAILED = "transactional_apply_rollback_failed";
|
|
6156
6258
|
async function chmodSafe(target, mode) {
|
|
6157
6259
|
try {
|
|
6158
|
-
const { chmod:
|
|
6159
|
-
await
|
|
6260
|
+
const { chmod: chmod5 } = await import("node:fs/promises");
|
|
6261
|
+
await chmod5(target, mode & 511);
|
|
6160
6262
|
} catch {
|
|
6161
6263
|
}
|
|
6162
6264
|
}
|
|
@@ -6190,13 +6292,13 @@ async function copyPathPreservingType(source, target) {
|
|
|
6190
6292
|
}
|
|
6191
6293
|
}
|
|
6192
6294
|
await removePathIfExists(target);
|
|
6193
|
-
await
|
|
6295
|
+
await mkdir7(path20.dirname(target), { recursive: true });
|
|
6194
6296
|
if (info.isSymbolicLink()) {
|
|
6195
6297
|
await symlink2(await readlink3(source), target);
|
|
6196
6298
|
return;
|
|
6197
6299
|
}
|
|
6198
6300
|
if (info.isDirectory() && !info.isSymbolicLink()) {
|
|
6199
|
-
await
|
|
6301
|
+
await mkdir7(target, { recursive: true, mode: info.mode & 511 });
|
|
6200
6302
|
return;
|
|
6201
6303
|
}
|
|
6202
6304
|
if (!info.isFile()) {
|
|
@@ -6206,12 +6308,12 @@ async function copyPathPreservingType(source, target) {
|
|
|
6206
6308
|
await chmodSafe(target, info.mode);
|
|
6207
6309
|
}
|
|
6208
6310
|
async function assertParentChainSafe(targetRoot, relativePath, plannedDirectories = /* @__PURE__ */ new Set()) {
|
|
6209
|
-
const segments = relativePath.split(
|
|
6311
|
+
const segments = relativePath.split(path20.sep).filter(Boolean);
|
|
6210
6312
|
if (segments.length <= 1) {
|
|
6211
6313
|
return;
|
|
6212
6314
|
}
|
|
6213
6315
|
for (let index = 1; index < segments.length; index++) {
|
|
6214
|
-
const prefix = segments.slice(0, index).join(
|
|
6316
|
+
const prefix = segments.slice(0, index).join(path20.sep);
|
|
6215
6317
|
const absolute = joinRelativePath(targetRoot, prefix);
|
|
6216
6318
|
let info;
|
|
6217
6319
|
try {
|
|
@@ -6231,8 +6333,8 @@ async function assertParentChainSafe(targetRoot, relativePath, plannedDirectorie
|
|
|
6231
6333
|
}
|
|
6232
6334
|
}
|
|
6233
6335
|
async function restorePathFromBackup(backupPath, target, rollbackRoot) {
|
|
6234
|
-
const stagingRoot = await mkdtemp3(
|
|
6235
|
-
const staged =
|
|
6336
|
+
const stagingRoot = await mkdtemp3(path20.join(rollbackRoot, "restore-"));
|
|
6337
|
+
const staged = path20.join(stagingRoot, "node");
|
|
6236
6338
|
try {
|
|
6237
6339
|
await copyPathPreservingType(backupPath, staged);
|
|
6238
6340
|
await copyPathPreservingType(staged, target);
|
|
@@ -6316,7 +6418,7 @@ async function applySingleChange(sourceRoot, targetRoot, change) {
|
|
|
6316
6418
|
if (change.before.kind !== "directory") {
|
|
6317
6419
|
await removePathIfExists(target);
|
|
6318
6420
|
}
|
|
6319
|
-
await
|
|
6421
|
+
await mkdir7(target, { recursive: true, mode: change.after.mode & 511 });
|
|
6320
6422
|
await chmodSafe(target, change.after.mode);
|
|
6321
6423
|
return;
|
|
6322
6424
|
}
|
|
@@ -6335,7 +6437,7 @@ async function applyObservedChanges(params) {
|
|
|
6335
6437
|
await assertParentChainSafe(targetRoot, change.relativePath, plannedDirectories);
|
|
6336
6438
|
await assertTargetMatches(targetRoot, change);
|
|
6337
6439
|
}
|
|
6338
|
-
const backupRoot = await mkdtemp3(
|
|
6440
|
+
const backupRoot = await mkdtemp3(path20.join(os2.tmpdir(), "belay-tx-rollback-"));
|
|
6339
6441
|
const rollbackActions = [];
|
|
6340
6442
|
let mutationAttempted = false;
|
|
6341
6443
|
let resourceIdentityChanged = false;
|
|
@@ -6354,12 +6456,12 @@ async function applyObservedChanges(params) {
|
|
|
6354
6456
|
targetExists = false;
|
|
6355
6457
|
}
|
|
6356
6458
|
if (targetExists) {
|
|
6357
|
-
const backupPath =
|
|
6459
|
+
const backupPath = path20.join(
|
|
6358
6460
|
backupRoot,
|
|
6359
6461
|
String(rollbackActions.length),
|
|
6360
6462
|
change.relativePath
|
|
6361
6463
|
);
|
|
6362
|
-
await
|
|
6464
|
+
await mkdir7(path20.dirname(backupPath), { recursive: true });
|
|
6363
6465
|
await copyPathPreservingType(target, backupPath);
|
|
6364
6466
|
await assertParentChainSafe(targetRoot, change.relativePath);
|
|
6365
6467
|
rollbackActions.push({
|
|
@@ -6458,7 +6560,7 @@ function isIgnoredDirtyPath(repoRoot, relativePath, ignoreRoots) {
|
|
|
6458
6560
|
if (ignoreRoots.length === 0) {
|
|
6459
6561
|
return false;
|
|
6460
6562
|
}
|
|
6461
|
-
const absolutePath = canonicalPath(
|
|
6563
|
+
const absolutePath = canonicalPath(path21.join(repoRoot, relativePath));
|
|
6462
6564
|
return ignoreRoots.some(
|
|
6463
6565
|
(root) => pathWithinRoot(root, absolutePath) || root === absolutePath || pathWithinRoot(absolutePath, root)
|
|
6464
6566
|
);
|
|
@@ -6496,7 +6598,7 @@ async function isDirtyWorktree(repoRoot, options) {
|
|
|
6496
6598
|
}
|
|
6497
6599
|
async function createGitWorktreeSnapshot(repoRoot, _stateDir) {
|
|
6498
6600
|
const { mkdtemp: mkdtemp6, rm: rm10 } = await import("node:fs/promises");
|
|
6499
|
-
const worktreePath = await mkdtemp6(
|
|
6601
|
+
const worktreePath = await mkdtemp6(path21.join(os3.tmpdir(), "belay-tx-"));
|
|
6500
6602
|
await execGit(repoRoot, ["worktree", "add", "--detach", worktreePath, "HEAD"]);
|
|
6501
6603
|
return {
|
|
6502
6604
|
worktreePath,
|
|
@@ -6515,14 +6617,14 @@ async function createGitWorktreeSnapshot(repoRoot, _stateDir) {
|
|
|
6515
6617
|
}
|
|
6516
6618
|
function resolveWorktreeCwd(repoRoot, worktreePath, cwd) {
|
|
6517
6619
|
const resolvedCwd = canonicalPath(cwd);
|
|
6518
|
-
const relative =
|
|
6519
|
-
if (isPathOutsideRoot(relative) ||
|
|
6620
|
+
const relative = path21.relative(canonicalPath(repoRoot), resolvedCwd);
|
|
6621
|
+
if (isPathOutsideRoot(relative) || path21.isAbsolute(relative)) {
|
|
6520
6622
|
return worktreePath;
|
|
6521
6623
|
}
|
|
6522
6624
|
if (relative === "") {
|
|
6523
6625
|
return worktreePath;
|
|
6524
6626
|
}
|
|
6525
|
-
return
|
|
6627
|
+
return path21.join(worktreePath, relative);
|
|
6526
6628
|
}
|
|
6527
6629
|
function runShellCommand(command, cwd, timeoutMs) {
|
|
6528
6630
|
return runProcessWithBoundedOutput(command, [], { cwd, shell: true, env: process.env }, timeoutMs);
|
|
@@ -6587,7 +6689,7 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
6587
6689
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6588
6690
|
|
|
6589
6691
|
// src/core/capability/boundary-grant-materialize.ts
|
|
6590
|
-
import
|
|
6692
|
+
import path22 from "node:path";
|
|
6591
6693
|
|
|
6592
6694
|
// src/core/glob.ts
|
|
6593
6695
|
init_approval();
|
|
@@ -6747,7 +6849,7 @@ function grantMatchesRequest(grant, request) {
|
|
|
6747
6849
|
var BOUNDARY_GRANT_TTL_MS = 15 * 6e4;
|
|
6748
6850
|
var BOUNDARY_GRANT_ISSUER_CONTAINER = "boundary:container";
|
|
6749
6851
|
function resolveCapabilityPath(targetPath, cwd) {
|
|
6750
|
-
const joined =
|
|
6852
|
+
const joined = path22.isAbsolute(targetPath) ? targetPath : path22.join(cwd, targetPath);
|
|
6751
6853
|
return canonicalPath(joined);
|
|
6752
6854
|
}
|
|
6753
6855
|
function isPathWithinBoundaryMount(request) {
|
|
@@ -6822,7 +6924,7 @@ function materializeContainerBoundaryGrant(request, params) {
|
|
|
6822
6924
|
|
|
6823
6925
|
// src/core/capability/boundary-workspace-mount.ts
|
|
6824
6926
|
init_path_utils();
|
|
6825
|
-
import
|
|
6927
|
+
import path23 from "node:path";
|
|
6826
6928
|
var HOST_PATH_ENV_VARS = [
|
|
6827
6929
|
"BELAY_EGRESS_REPO_ROOT",
|
|
6828
6930
|
"BELAY_JUDGE_BROKER_REPO_ROOT",
|
|
@@ -6847,23 +6949,23 @@ function validateWorkspaceMount(mount) {
|
|
|
6847
6949
|
if (mount.cwdRelative.includes("\0")) {
|
|
6848
6950
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6849
6951
|
}
|
|
6850
|
-
const normalizedRelative =
|
|
6952
|
+
const normalizedRelative = path23.posix.normalize(mount.cwdRelative.replace(/\\/g, "/"));
|
|
6851
6953
|
if (normalizedRelative === ".." || normalizedRelative.startsWith("../")) {
|
|
6852
6954
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6853
6955
|
}
|
|
6854
|
-
if (
|
|
6956
|
+
if (path23.posix.isAbsolute(normalizedRelative)) {
|
|
6855
6957
|
throw new Error("boundary_workspace_mount_invalid_cwd");
|
|
6856
6958
|
}
|
|
6857
6959
|
}
|
|
6858
6960
|
function resolveGuestWorkdir(mount) {
|
|
6859
6961
|
validateWorkspaceMount(mount);
|
|
6860
6962
|
const guestTargetRoot = canonicalPath(mount.guestTargetRoot);
|
|
6861
|
-
const normalizedRelative =
|
|
6963
|
+
const normalizedRelative = path23.posix.normalize(mount.cwdRelative.replace(/\\/g, "/"));
|
|
6862
6964
|
if (normalizedRelative === "." || normalizedRelative === "") {
|
|
6863
6965
|
return guestTargetRoot;
|
|
6864
6966
|
}
|
|
6865
6967
|
const segments = normalizedRelative.split("/").filter(Boolean);
|
|
6866
|
-
return
|
|
6968
|
+
return path23.posix.join(guestTargetRoot, ...segments);
|
|
6867
6969
|
}
|
|
6868
6970
|
function buildWorkspaceMountSpec(mount) {
|
|
6869
6971
|
validateWorkspaceMount(mount);
|
|
@@ -7154,7 +7256,7 @@ function containedExecutionFresh(attestation, config, now = Date.now()) {
|
|
|
7154
7256
|
}
|
|
7155
7257
|
function boundaryAttestationPath(repoRoot, config) {
|
|
7156
7258
|
const rel = config.capability?.attestationRelPath ?? ".belay/attestation.json";
|
|
7157
|
-
return
|
|
7259
|
+
return path24.isAbsolute(rel) ? rel : path24.join(repoRoot, rel);
|
|
7158
7260
|
}
|
|
7159
7261
|
async function loadBoundaryAttestation(filePath, expectedRepoRoot, controlPlaneDir) {
|
|
7160
7262
|
try {
|
|
@@ -7259,7 +7361,7 @@ init_capability_request_hash();
|
|
|
7259
7361
|
|
|
7260
7362
|
// src/core/capability/gate-policy-shadow.ts
|
|
7261
7363
|
init_config();
|
|
7262
|
-
import
|
|
7364
|
+
import path30 from "node:path";
|
|
7263
7365
|
|
|
7264
7366
|
// src/core/effect-ir/audit.ts
|
|
7265
7367
|
init_fingerprint2();
|
|
@@ -7353,8 +7455,8 @@ function evidenceRank(level) {
|
|
|
7353
7455
|
}
|
|
7354
7456
|
|
|
7355
7457
|
// src/core/effect-ir/package-exec.ts
|
|
7356
|
-
import { existsSync as
|
|
7357
|
-
import
|
|
7458
|
+
import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
|
|
7459
|
+
import path25 from "node:path";
|
|
7358
7460
|
|
|
7359
7461
|
// src/core/network-endpoint.ts
|
|
7360
7462
|
var SUPPORTED_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "ssh:", "git:"]);
|
|
@@ -7645,18 +7747,18 @@ function classifyPackageAcquisitionSpec(spec) {
|
|
|
7645
7747
|
return { kind: "registry" };
|
|
7646
7748
|
}
|
|
7647
7749
|
function resolveLocalBin(binName, cwd, repoRoot) {
|
|
7648
|
-
const base =
|
|
7750
|
+
const base = path25.basename(binName);
|
|
7649
7751
|
if (!base || base === "." || base === "..") {
|
|
7650
7752
|
return null;
|
|
7651
7753
|
}
|
|
7652
|
-
let current =
|
|
7653
|
-
const stop =
|
|
7754
|
+
let current = path25.resolve(cwd);
|
|
7755
|
+
const stop = path25.resolve(repoRoot);
|
|
7654
7756
|
if (!pathWithinRoot(stop, current)) {
|
|
7655
7757
|
return null;
|
|
7656
7758
|
}
|
|
7657
7759
|
while (true) {
|
|
7658
|
-
const candidate =
|
|
7659
|
-
if (
|
|
7760
|
+
const candidate = path25.join(current, "node_modules", ".bin", base);
|
|
7761
|
+
if (existsSync6(candidate)) {
|
|
7660
7762
|
try {
|
|
7661
7763
|
const resolvedCandidate = realpathSync3.native(candidate);
|
|
7662
7764
|
if (!pathWithinRoot(stop, resolvedCandidate) || !statSync2(resolvedCandidate).isFile()) {
|
|
@@ -7670,7 +7772,7 @@ function resolveLocalBin(binName, cwd, repoRoot) {
|
|
|
7670
7772
|
if (current === stop) {
|
|
7671
7773
|
break;
|
|
7672
7774
|
}
|
|
7673
|
-
const parent =
|
|
7775
|
+
const parent = path25.dirname(current);
|
|
7674
7776
|
if (parent === current) {
|
|
7675
7777
|
break;
|
|
7676
7778
|
}
|
|
@@ -7865,7 +7967,7 @@ init_path_utils();
|
|
|
7865
7967
|
|
|
7866
7968
|
// src/core/verdict/containment.ts
|
|
7867
7969
|
init_git_resource_identity();
|
|
7868
|
-
import
|
|
7970
|
+
import path26 from "node:path";
|
|
7869
7971
|
init_path_utils();
|
|
7870
7972
|
|
|
7871
7973
|
// src/core/verdict/persistent-paths.ts
|
|
@@ -7885,7 +7987,7 @@ function expandHome(token) {
|
|
|
7885
7987
|
if (!home) {
|
|
7886
7988
|
return token;
|
|
7887
7989
|
}
|
|
7888
|
-
return token === "~" ? home :
|
|
7990
|
+
return token === "~" ? home : path26.join(home, token.slice(2));
|
|
7889
7991
|
}
|
|
7890
7992
|
return token;
|
|
7891
7993
|
}
|
|
@@ -7897,10 +7999,10 @@ function resolveTrustedPath(token, trustedCwd, trusted) {
|
|
|
7897
7999
|
return null;
|
|
7898
8000
|
}
|
|
7899
8001
|
const expanded = expandHome(token);
|
|
7900
|
-
if (
|
|
8002
|
+
if (path26.isAbsolute(expanded)) {
|
|
7901
8003
|
return canonicalPath(expanded);
|
|
7902
8004
|
}
|
|
7903
|
-
return canonicalPath(
|
|
8005
|
+
return canonicalPath(path26.resolve(trustedCwd, expanded));
|
|
7904
8006
|
}
|
|
7905
8007
|
function isGitPath(resolvedPath, repoRoot) {
|
|
7906
8008
|
if (isGitMetadataPath(resolvedPath, repoRoot)) {
|
|
@@ -8017,20 +8119,20 @@ function parseTier1Json(raw) {
|
|
|
8017
8119
|
init_judge_runtime_config();
|
|
8018
8120
|
|
|
8019
8121
|
// src/core/verdict/judge-session-kill-switch.ts
|
|
8020
|
-
import { existsSync as
|
|
8021
|
-
import { mkdir as
|
|
8022
|
-
import
|
|
8122
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
8123
|
+
import { mkdir as mkdir8, readFile as readFile7, unlink as unlink5, writeFile as writeFile4 } from "node:fs/promises";
|
|
8124
|
+
import path27 from "node:path";
|
|
8023
8125
|
var JUDGE_SESSION_KILL_FILE = "judge-session-kill.json";
|
|
8024
8126
|
function judgeSessionKillSwitchPath(stateDir) {
|
|
8025
|
-
return
|
|
8127
|
+
return path27.join(stateDir, JUDGE_SESSION_KILL_FILE);
|
|
8026
8128
|
}
|
|
8027
8129
|
async function readJudgeSessionKillSwitch(stateDir) {
|
|
8028
8130
|
const filePath = judgeSessionKillSwitchPath(stateDir);
|
|
8029
|
-
if (!
|
|
8131
|
+
if (!existsSync7(filePath)) {
|
|
8030
8132
|
return null;
|
|
8031
8133
|
}
|
|
8032
8134
|
try {
|
|
8033
|
-
const raw = JSON.parse(await
|
|
8135
|
+
const raw = JSON.parse(await readFile7(filePath, "utf8"));
|
|
8034
8136
|
return raw.triggered === true ? raw : null;
|
|
8035
8137
|
} catch {
|
|
8036
8138
|
return null;
|
|
@@ -8041,7 +8143,7 @@ async function isJudgeSessionKillSwitchPersisted(stateDir) {
|
|
|
8041
8143
|
return record?.triggered === true;
|
|
8042
8144
|
}
|
|
8043
8145
|
async function persistJudgeSessionKillSwitch(stateDir, reason = "shadow_mismatch") {
|
|
8044
|
-
await
|
|
8146
|
+
await mkdir8(stateDir, { recursive: true, mode: 448 });
|
|
8045
8147
|
const record = {
|
|
8046
8148
|
triggered: true,
|
|
8047
8149
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8202,10 +8304,10 @@ function recordJudgeLatency(phase, latencyMs, at = Date.now()) {
|
|
|
8202
8304
|
|
|
8203
8305
|
// src/core/verdict/judge-broker-service.ts
|
|
8204
8306
|
import { spawn as spawn6 } from "node:child_process";
|
|
8205
|
-
import { existsSync as
|
|
8206
|
-
import { unlink as
|
|
8307
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
8308
|
+
import { unlink as unlink6 } from "node:fs/promises";
|
|
8207
8309
|
import net2 from "node:net";
|
|
8208
|
-
import
|
|
8310
|
+
import path28 from "node:path";
|
|
8209
8311
|
import { fileURLToPath } from "node:url";
|
|
8210
8312
|
|
|
8211
8313
|
// src/core/verdict/judge-cli.ts
|
|
@@ -8751,10 +8853,10 @@ var JUDGE_BROKER_SESSION = "judge-broker-session.json";
|
|
|
8751
8853
|
function judgeBrokerPaths(stateDir) {
|
|
8752
8854
|
return {
|
|
8753
8855
|
stateDir,
|
|
8754
|
-
socketPath:
|
|
8755
|
-
statusPath:
|
|
8756
|
-
pidPath:
|
|
8757
|
-
sessionConfigPath:
|
|
8856
|
+
socketPath: path28.join(stateDir, JUDGE_BROKER_SOCKET),
|
|
8857
|
+
statusPath: path28.join(stateDir, JUDGE_BROKER_STATUS),
|
|
8858
|
+
pidPath: path28.join(stateDir, JUDGE_BROKER_PID),
|
|
8859
|
+
sessionConfigPath: path28.join(stateDir, JUDGE_BROKER_SESSION)
|
|
8758
8860
|
};
|
|
8759
8861
|
}
|
|
8760
8862
|
function daemonScriptPath() {
|
|
@@ -8772,12 +8874,12 @@ function useInProcessBroker(env = process.env) {
|
|
|
8772
8874
|
return Boolean(env.VITEST || env.VITEST_WORKER_ID || env.BELAY_JUDGE_BROKER_IN_PROCESS === "1");
|
|
8773
8875
|
}
|
|
8774
8876
|
async function readBrokerStatus(statusPath) {
|
|
8775
|
-
if (!
|
|
8877
|
+
if (!existsSync8(statusPath)) {
|
|
8776
8878
|
return null;
|
|
8777
8879
|
}
|
|
8778
8880
|
try {
|
|
8779
|
-
const { readFile:
|
|
8780
|
-
const raw = JSON.parse(await
|
|
8881
|
+
const { readFile: readFile17 } = await import("node:fs/promises");
|
|
8882
|
+
const raw = JSON.parse(await readFile17(statusPath, "utf8"));
|
|
8781
8883
|
if (typeof raw.pid !== "number" || typeof raw.socketPath !== "string") {
|
|
8782
8884
|
return null;
|
|
8783
8885
|
}
|
|
@@ -8787,13 +8889,13 @@ async function readBrokerStatus(statusPath) {
|
|
|
8787
8889
|
}
|
|
8788
8890
|
}
|
|
8789
8891
|
async function readBrokerSessionConfig(sessionConfigPath) {
|
|
8790
|
-
if (!
|
|
8892
|
+
if (!existsSync8(sessionConfigPath)) {
|
|
8791
8893
|
return null;
|
|
8792
8894
|
}
|
|
8793
8895
|
try {
|
|
8794
|
-
const { readFile:
|
|
8896
|
+
const { readFile: readFile17 } = await import("node:fs/promises");
|
|
8795
8897
|
const raw = JSON.parse(
|
|
8796
|
-
await
|
|
8898
|
+
await readFile17(sessionConfigPath, "utf8")
|
|
8797
8899
|
);
|
|
8798
8900
|
return normalizeJudgeSessionConfig({ ...raw, enabled: true });
|
|
8799
8901
|
} catch {
|
|
@@ -8884,7 +8986,7 @@ async function ensureJudgeBrokerDaemon(stateDir, repoRoot, sessionConfig) {
|
|
|
8884
8986
|
const existingConfig = await readBrokerSessionConfig(paths.sessionConfigPath);
|
|
8885
8987
|
const configChanged = existingConfig !== null && brokerSessionConfigPayload(existingConfig) !== nextConfigPayload;
|
|
8886
8988
|
const status = await readBrokerStatus(paths.statusPath);
|
|
8887
|
-
if (status && isProcessAlive2(status.pid) &&
|
|
8989
|
+
if (status && isProcessAlive2(status.pid) && existsSync8(paths.socketPath) && !configChanged) {
|
|
8888
8990
|
await writeBrokerSessionConfig(paths, brokerSessionConfig);
|
|
8889
8991
|
return paths;
|
|
8890
8992
|
}
|
|
@@ -8906,7 +9008,7 @@ async function ensureJudgeBrokerDaemon(stateDir, repoRoot, sessionConfig) {
|
|
|
8906
9008
|
child.unref();
|
|
8907
9009
|
const deadline = Date.now() + sessionConfig.connectTimeoutMs;
|
|
8908
9010
|
while (Date.now() < deadline) {
|
|
8909
|
-
if (
|
|
9011
|
+
if (existsSync8(paths.socketPath)) {
|
|
8910
9012
|
const live = await readBrokerStatus(paths.statusPath);
|
|
8911
9013
|
if (live && isProcessAlive2(live.pid)) {
|
|
8912
9014
|
return paths;
|
|
@@ -8923,8 +9025,8 @@ async function cleanupJudgeBrokerArtifacts(paths) {
|
|
|
8923
9025
|
paths.pidPath,
|
|
8924
9026
|
paths.sessionConfigPath
|
|
8925
9027
|
]) {
|
|
8926
|
-
if (
|
|
8927
|
-
await
|
|
9028
|
+
if (existsSync8(artifact)) {
|
|
9029
|
+
await unlink6(artifact).catch(() => void 0);
|
|
8928
9030
|
}
|
|
8929
9031
|
}
|
|
8930
9032
|
}
|
|
@@ -9420,9 +9522,9 @@ async function evaluateWithJudgeTransport(request, options = {}) {
|
|
|
9420
9522
|
}
|
|
9421
9523
|
|
|
9422
9524
|
// src/core/capability/gate-shadow-ratchet.ts
|
|
9423
|
-
import { existsSync as
|
|
9424
|
-
import { mkdir as
|
|
9425
|
-
import
|
|
9525
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
9526
|
+
import { mkdir as mkdir9, readFile as readFile8, writeFile as writeFile5 } from "node:fs/promises";
|
|
9527
|
+
import path29 from "node:path";
|
|
9426
9528
|
var DEFAULT_STATE = {
|
|
9427
9529
|
version: 1,
|
|
9428
9530
|
policyJudgeComparisons: 0,
|
|
@@ -9431,15 +9533,15 @@ var DEFAULT_STATE = {
|
|
|
9431
9533
|
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
9432
9534
|
};
|
|
9433
9535
|
function ratchetPath(stateDir) {
|
|
9434
|
-
return
|
|
9536
|
+
return path29.join(stateDir, "gate-shadow-ratchet.json");
|
|
9435
9537
|
}
|
|
9436
9538
|
async function loadState(stateDir) {
|
|
9437
9539
|
const filePath = ratchetPath(stateDir);
|
|
9438
|
-
if (!
|
|
9540
|
+
if (!existsSync9(filePath)) {
|
|
9439
9541
|
return { ...DEFAULT_STATE, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9440
9542
|
}
|
|
9441
9543
|
try {
|
|
9442
|
-
const raw = JSON.parse(await
|
|
9544
|
+
const raw = JSON.parse(await readFile8(filePath, "utf8"));
|
|
9443
9545
|
return {
|
|
9444
9546
|
...DEFAULT_STATE,
|
|
9445
9547
|
...raw,
|
|
@@ -9450,7 +9552,7 @@ async function loadState(stateDir) {
|
|
|
9450
9552
|
}
|
|
9451
9553
|
}
|
|
9452
9554
|
async function saveState(stateDir, state) {
|
|
9453
|
-
await
|
|
9555
|
+
await mkdir9(stateDir, { recursive: true, mode: 448 });
|
|
9454
9556
|
await writeFile5(
|
|
9455
9557
|
ratchetPath(stateDir),
|
|
9456
9558
|
`${JSON.stringify({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
@@ -9573,7 +9675,7 @@ function scheduleGateShadowAudit(params) {
|
|
|
9573
9675
|
async function runGatePolicyShadowComparison(params) {
|
|
9574
9676
|
const judge = params.config.judge;
|
|
9575
9677
|
const runtime = normalizeJudgeRuntimeConfig(judge.runtime);
|
|
9576
|
-
const stateDir = params.stateDir ?? belayStateDir(params.config,
|
|
9678
|
+
const stateDir = params.stateDir ?? belayStateDir(params.config, path30.join(params.repoRoot, ".belay"));
|
|
9577
9679
|
const transport = await evaluateWithJudgeTransport(
|
|
9578
9680
|
{
|
|
9579
9681
|
prompt: buildTier1Prompt(params.command),
|
|
@@ -9863,15 +9965,15 @@ async function loadClassifierAuthorization(params) {
|
|
|
9863
9965
|
}
|
|
9864
9966
|
|
|
9865
9967
|
// src/core/capability/allowlist.ts
|
|
9866
|
-
import { existsSync as
|
|
9968
|
+
import { existsSync as existsSync10, readFileSync as readFileSync3 } from "node:fs";
|
|
9867
9969
|
init_config();
|
|
9868
9970
|
init_path_utils();
|
|
9869
|
-
import
|
|
9971
|
+
import path31 from "node:path";
|
|
9870
9972
|
function fsScopeAllowlistPath(config, repoLocalStateDir) {
|
|
9871
|
-
return
|
|
9973
|
+
return path31.join(belayStateDir(config, repoLocalStateDir), "fs-scope-allowlist.json");
|
|
9872
9974
|
}
|
|
9873
9975
|
function loadFsScopeAllowlistSync(filePath) {
|
|
9874
|
-
if (!
|
|
9976
|
+
if (!existsSync10(filePath)) {
|
|
9875
9977
|
return { version: 1, paths: [] };
|
|
9876
9978
|
}
|
|
9877
9979
|
const raw = JSON.parse(readFileSync3(filePath, "utf8"));
|
|
@@ -9963,7 +10065,7 @@ function checkGatedActionLimits(action) {
|
|
|
9963
10065
|
// src/core/capability/paths.ts
|
|
9964
10066
|
init_path_utils();
|
|
9965
10067
|
init_shell_tokenizer();
|
|
9966
|
-
import
|
|
10068
|
+
import path32 from "node:path";
|
|
9967
10069
|
function applyPatchTargets(patch) {
|
|
9968
10070
|
const targets = [];
|
|
9969
10071
|
for (const line of patch.split("\n")) {
|
|
@@ -10028,7 +10130,7 @@ function collectOutsideRepoPathsFromToolPayload(payload, cwd, repoRoot, trustedW
|
|
|
10028
10130
|
const paths = /* @__PURE__ */ new Set();
|
|
10029
10131
|
const filePath = extractToolFilePath(payload);
|
|
10030
10132
|
if (filePath) {
|
|
10031
|
-
const resolved =
|
|
10133
|
+
const resolved = path32.isAbsolute(filePath) ? filePath : path32.resolve(cwd, filePath);
|
|
10032
10134
|
if (resolveWorkspaceRootMatch(repoRoot, trustedWorkspaceRoots, resolved) === null) {
|
|
10033
10135
|
paths.add(resolved);
|
|
10034
10136
|
}
|
|
@@ -10038,7 +10140,7 @@ function collectOutsideRepoPathsFromToolPayload(payload, cwd, repoRoot, trustedW
|
|
|
10038
10140
|
const patch = extractToolPatch(payload);
|
|
10039
10141
|
if (patch) {
|
|
10040
10142
|
for (const target of applyPatchTargets(patch)) {
|
|
10041
|
-
const resolved =
|
|
10143
|
+
const resolved = path32.isAbsolute(target) ? target : path32.resolve(cwd, target);
|
|
10042
10144
|
if (resolveWorkspaceRootMatch(repoRoot, trustedWorkspaceRoots, resolved) === null) {
|
|
10043
10145
|
paths.add(resolved);
|
|
10044
10146
|
}
|
|
@@ -10092,7 +10194,7 @@ function policyReasonToLegacyReason(decision) {
|
|
|
10092
10194
|
// src/core/capability/policy-engine.ts
|
|
10093
10195
|
init_git_resource_identity();
|
|
10094
10196
|
import { createHash as createHash10 } from "node:crypto";
|
|
10095
|
-
import
|
|
10197
|
+
import path33 from "node:path";
|
|
10096
10198
|
init_path_utils();
|
|
10097
10199
|
init_shell_tokenizer();
|
|
10098
10200
|
init_grant();
|
|
@@ -10384,7 +10486,7 @@ function hashSession(value) {
|
|
|
10384
10486
|
return createHash10("sha256").update(value).digest("hex").slice(0, 16);
|
|
10385
10487
|
}
|
|
10386
10488
|
function resolveCapabilityPath2(targetPath, cwd) {
|
|
10387
|
-
const joined =
|
|
10489
|
+
const joined = path33.isAbsolute(targetPath) ? targetPath : path33.join(cwd, targetPath);
|
|
10388
10490
|
return canonicalPath(joined);
|
|
10389
10491
|
}
|
|
10390
10492
|
function actionForFileMutation(analysis) {
|
|
@@ -10463,7 +10565,7 @@ function isRepoLocalPackageExec(request) {
|
|
|
10463
10565
|
return false;
|
|
10464
10566
|
}
|
|
10465
10567
|
const commandPath = canonicalPath(request.resource.command);
|
|
10466
|
-
return
|
|
10568
|
+
return path33.isAbsolute(commandPath) && pathWithinRoot(request.principal.repoRoot, commandPath);
|
|
10467
10569
|
}
|
|
10468
10570
|
function isRepoLocalRoutineWrite(request, sensitivePaths) {
|
|
10469
10571
|
if (!isRepoLocalShellLocation(request)) {
|
|
@@ -10727,15 +10829,15 @@ function shouldSkipBrokerApprovedRecord(brokerActive, approvalReason) {
|
|
|
10727
10829
|
}
|
|
10728
10830
|
|
|
10729
10831
|
// src/core/capability/trusted-workspace-roots.ts
|
|
10730
|
-
import { existsSync as
|
|
10832
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
10731
10833
|
init_config();
|
|
10732
10834
|
init_path_utils();
|
|
10733
|
-
import
|
|
10835
|
+
import path34 from "node:path";
|
|
10734
10836
|
function trustedWorkspaceRootsPath(config, repoLocalStateDir) {
|
|
10735
|
-
return
|
|
10837
|
+
return path34.join(belayStateDir(config, repoLocalStateDir), "trusted-workspace-roots.json");
|
|
10736
10838
|
}
|
|
10737
10839
|
function loadTrustedWorkspaceRootsSync(filePath) {
|
|
10738
|
-
if (!
|
|
10840
|
+
if (!existsSync11(filePath)) {
|
|
10739
10841
|
return { version: 1, roots: [] };
|
|
10740
10842
|
}
|
|
10741
10843
|
const raw = JSON.parse(readFileSync4(filePath, "utf8"));
|
|
@@ -10761,7 +10863,7 @@ function sanitizeTrustedWorkspaceRootEntries(input) {
|
|
|
10761
10863
|
const source = record.source === "approval" ? "approval" : void 0;
|
|
10762
10864
|
return [
|
|
10763
10865
|
{
|
|
10764
|
-
path:
|
|
10866
|
+
path: path34.resolve(record.path),
|
|
10765
10867
|
approvedAt,
|
|
10766
10868
|
approvalId,
|
|
10767
10869
|
...source ? { source } : {}
|
|
@@ -10790,7 +10892,7 @@ function isDirectoryPath(targetPath) {
|
|
|
10790
10892
|
function isBroadTrustedWorkspaceRoot(targetPath) {
|
|
10791
10893
|
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
10792
10894
|
const root = normalizeTrustedWorkspaceRootPath(targetPath);
|
|
10793
|
-
if (root === normalizeTrustedWorkspaceRootPath(
|
|
10895
|
+
if (root === normalizeTrustedWorkspaceRootPath(path34.parse(root).root)) {
|
|
10794
10896
|
return true;
|
|
10795
10897
|
}
|
|
10796
10898
|
if (home && normalizeTrustedWorkspaceRootPath(home) === root) {
|
|
@@ -10814,7 +10916,7 @@ function isHighStakesTrustedWorkspaceRoot(targetPath) {
|
|
|
10814
10916
|
return false;
|
|
10815
10917
|
}
|
|
10816
10918
|
return HOME_HIGH_STAKES_SEGMENTS.some(
|
|
10817
|
-
(segment) => pathWithinRoot(
|
|
10919
|
+
(segment) => pathWithinRoot(path34.join(homeRoot, segment), normalized)
|
|
10818
10920
|
);
|
|
10819
10921
|
}
|
|
10820
10922
|
function validateTrustedWorkspaceRootCandidate(params) {
|
|
@@ -10851,7 +10953,7 @@ function validateTrustedWorkspaceRootCandidate(params) {
|
|
|
10851
10953
|
init_config_layers();
|
|
10852
10954
|
|
|
10853
10955
|
// src/core/contained-execution/eligibility.ts
|
|
10854
|
-
import
|
|
10956
|
+
import path35 from "node:path";
|
|
10855
10957
|
init_path_utils();
|
|
10856
10958
|
var FORBIDDEN_SIGNALS = /* @__PURE__ */ new Set([
|
|
10857
10959
|
"command_substitution",
|
|
@@ -10868,7 +10970,7 @@ var FORBIDDEN_SIGNALS = /* @__PURE__ */ new Set([
|
|
|
10868
10970
|
]);
|
|
10869
10971
|
function isContainedUnknownExecutionEligible(config, action, result) {
|
|
10870
10972
|
const contained = config.sandbox.containedExecution;
|
|
10871
|
-
if (!contained?.enabled || !config.sandbox.enabled || config.sandbox.runtime !== "container" || action.kind !== "shell" || !
|
|
10973
|
+
if (!contained?.enabled || !config.sandbox.enabled || config.sandbox.runtime !== "container" || action.kind !== "shell" || !path35.isAbsolute(action.repoRoot) || !hasResolvedRepoIdentity(action.repoRoot) || !config.gates.shell || result.reason !== "unknown_local_effect" || result.axes?.location !== "repo_local" || result.assessment.external) {
|
|
10872
10974
|
return false;
|
|
10873
10975
|
}
|
|
10874
10976
|
const plan = result.effectPlan;
|
|
@@ -11148,7 +11250,7 @@ function classifySubagent(payload, repoRoot, options = {}, config) {
|
|
|
11148
11250
|
}
|
|
11149
11251
|
|
|
11150
11252
|
// src/core/classify-tool.ts
|
|
11151
|
-
import
|
|
11253
|
+
import path50 from "node:path";
|
|
11152
11254
|
init_fingerprint2();
|
|
11153
11255
|
init_path_utils();
|
|
11154
11256
|
init_scrub();
|
|
@@ -11268,13 +11370,13 @@ function worstEffectDecision(decisions) {
|
|
|
11268
11370
|
|
|
11269
11371
|
// src/core/effect-ir/shell-lower.ts
|
|
11270
11372
|
init_shell_tokenizer();
|
|
11271
|
-
import
|
|
11373
|
+
import path49 from "node:path";
|
|
11272
11374
|
|
|
11273
11375
|
// src/core/verdict/docker-compose-run.ts
|
|
11274
|
-
import
|
|
11376
|
+
import path37 from "node:path";
|
|
11275
11377
|
|
|
11276
11378
|
// src/core/verdict/recursive-invocation.ts
|
|
11277
|
-
import
|
|
11379
|
+
import path36 from "node:path";
|
|
11278
11380
|
var SHELL_INTERPRETERS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish"]);
|
|
11279
11381
|
var PYTHON_INTERPRETERS = /* @__PURE__ */ new Set(["python", "python3"]);
|
|
11280
11382
|
var SHELL_SHORT_OPTIONS = /* @__PURE__ */ new Set(["c", "l", "e", "x", "u"]);
|
|
@@ -11336,7 +11438,7 @@ var OSASCRIPT_PROFILE = {
|
|
|
11336
11438
|
attachedValuePrefixes: []
|
|
11337
11439
|
};
|
|
11338
11440
|
function normalizeInterpreter(value) {
|
|
11339
|
-
return
|
|
11441
|
+
return path36.basename(value);
|
|
11340
11442
|
}
|
|
11341
11443
|
function scriptResult(interpreter, token) {
|
|
11342
11444
|
if (!token) {
|
|
@@ -11566,7 +11668,7 @@ function parseOptions(words, start, options) {
|
|
|
11566
11668
|
function decodeDockerComposeRun(tokens) {
|
|
11567
11669
|
if (tokens.some((token) => token.kind === "operator")) return { kind: "none" };
|
|
11568
11670
|
const words = tokens.filter((token) => token.kind === "word");
|
|
11569
|
-
const head =
|
|
11671
|
+
const head = path37.basename(words[0]?.value ?? "");
|
|
11570
11672
|
let index;
|
|
11571
11673
|
if (head === "docker-compose") {
|
|
11572
11674
|
index = 1;
|
|
@@ -11607,7 +11709,7 @@ function decodeDockerComposeRun(tokens) {
|
|
|
11607
11709
|
}
|
|
11608
11710
|
|
|
11609
11711
|
// src/core/verdict/egress-classify.ts
|
|
11610
|
-
import
|
|
11712
|
+
import path38 from "node:path";
|
|
11611
11713
|
var EGRESS_TOOL_HEADS = /* @__PURE__ */ new Set([
|
|
11612
11714
|
"aws",
|
|
11613
11715
|
"curl",
|
|
@@ -11656,7 +11758,7 @@ function isEgressToolHead(head) {
|
|
|
11656
11758
|
return EGRESS_TOOL_HEADS.has(head);
|
|
11657
11759
|
}
|
|
11658
11760
|
function decodeEgressEffects(params) {
|
|
11659
|
-
const head =
|
|
11761
|
+
const head = path38.basename(params.tokens[0] ?? "");
|
|
11660
11762
|
if (head !== "curl" && head !== "wget" && head !== "gh") {
|
|
11661
11763
|
return null;
|
|
11662
11764
|
}
|
|
@@ -11664,7 +11766,7 @@ function decodeEgressEffects(params) {
|
|
|
11664
11766
|
const provenance = { segment: params.segment };
|
|
11665
11767
|
const requirements = [];
|
|
11666
11768
|
for (const file of decoded.files) {
|
|
11667
|
-
const resolved =
|
|
11769
|
+
const resolved = path38.resolve(params.cwd, expandHome2(file));
|
|
11668
11770
|
requirements.push(
|
|
11669
11771
|
requirement("fs.read", "fs.read", { kind: "path", path: resolved }, params.segment, [
|
|
11670
11772
|
"egress.explicit_file_read"
|
|
@@ -11686,7 +11788,7 @@ function decodeEgressEffects(params) {
|
|
|
11686
11788
|
if (file === "-") {
|
|
11687
11789
|
continue;
|
|
11688
11790
|
}
|
|
11689
|
-
const resolved =
|
|
11791
|
+
const resolved = path38.resolve(params.cwd, expandHome2(file));
|
|
11690
11792
|
if (resolved === "/dev/null") {
|
|
11691
11793
|
continue;
|
|
11692
11794
|
}
|
|
@@ -12164,13 +12266,13 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
12164
12266
|
if (head === "wget" && !explicitOutput) {
|
|
12165
12267
|
outputFiles.push(
|
|
12166
12268
|
...endpointOutputNames.map(
|
|
12167
|
-
(name) => outputDirectory ?
|
|
12269
|
+
(name) => outputDirectory ? path38.join(outputDirectory, name) : name
|
|
12168
12270
|
)
|
|
12169
12271
|
);
|
|
12170
12272
|
} else if (head === "curl" && remoteNameOutput) {
|
|
12171
12273
|
outputFiles.push(
|
|
12172
12274
|
...endpointOutputNames.map(
|
|
12173
|
-
(name) => outputDirectory ?
|
|
12275
|
+
(name) => outputDirectory ? path38.join(outputDirectory, name) : name
|
|
12174
12276
|
)
|
|
12175
12277
|
);
|
|
12176
12278
|
}
|
|
@@ -12184,7 +12286,7 @@ function decodeSingleCurlWgetGrammar(head, tokens) {
|
|
|
12184
12286
|
outputFiles: [
|
|
12185
12287
|
...new Set(
|
|
12186
12288
|
outputFiles.map(
|
|
12187
|
-
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !
|
|
12289
|
+
(file) => outputDirectory && directoryEligibleOutputs.has(file) && !path38.isAbsolute(file) ? path38.join(outputDirectory, file) : file
|
|
12188
12290
|
)
|
|
12189
12291
|
)
|
|
12190
12292
|
],
|
|
@@ -12199,7 +12301,7 @@ function remoteOutputName(spec) {
|
|
|
12199
12301
|
} catch {
|
|
12200
12302
|
pathname = spec.split(/[?#]/, 1)[0] ?? "";
|
|
12201
12303
|
}
|
|
12202
|
-
const name =
|
|
12304
|
+
const name = path38.posix.basename(pathname);
|
|
12203
12305
|
return name && name !== "/" ? name : "index.html";
|
|
12204
12306
|
}
|
|
12205
12307
|
function decodeGhGrammar(tokens) {
|
|
@@ -12364,7 +12466,7 @@ function expandHome2(value) {
|
|
|
12364
12466
|
return process.env.HOME ?? value;
|
|
12365
12467
|
}
|
|
12366
12468
|
if (value.startsWith("~/")) {
|
|
12367
|
-
return
|
|
12469
|
+
return path38.join(process.env.HOME ?? "~", value.slice(2));
|
|
12368
12470
|
}
|
|
12369
12471
|
return value;
|
|
12370
12472
|
}
|
|
@@ -12383,7 +12485,7 @@ function requirement(tag, action, resource, segment, signals) {
|
|
|
12383
12485
|
}
|
|
12384
12486
|
|
|
12385
12487
|
// src/core/verdict/git-classifier.ts
|
|
12386
|
-
import
|
|
12488
|
+
import path39 from "node:path";
|
|
12387
12489
|
init_shell_tokenizer();
|
|
12388
12490
|
var GIT_BRANCH_MUTATION_FLAGS = /* @__PURE__ */ new Set([
|
|
12389
12491
|
"--copy",
|
|
@@ -12479,7 +12581,7 @@ var FILE_OPERAND_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
12479
12581
|
var COMPOUND_SUBCOMMAND_HEADS = /* @__PURE__ */ new Set(["worktree", "stash", "tag"]);
|
|
12480
12582
|
var REF_ONLY_WITHOUT_TERMINATOR = /* @__PURE__ */ new Set(["checkout", "show", "log"]);
|
|
12481
12583
|
function isGitExecutable(token) {
|
|
12482
|
-
return
|
|
12584
|
+
return path39.basename(token) === "git";
|
|
12483
12585
|
}
|
|
12484
12586
|
function takesValue(flag) {
|
|
12485
12587
|
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=");
|
|
@@ -12507,7 +12609,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12507
12609
|
if (token === "-C" || token === "--work-tree" || token === "--git-dir" || token === "-c") {
|
|
12508
12610
|
const value = tokens[index + 1];
|
|
12509
12611
|
if (token === "-C" && value) {
|
|
12510
|
-
effectiveCwd =
|
|
12612
|
+
effectiveCwd = path39.resolve(baseCwd, value);
|
|
12511
12613
|
} else if (token === "--work-tree" && value) {
|
|
12512
12614
|
workTree = value;
|
|
12513
12615
|
} else if (token === "--git-dir" && value) {
|
|
@@ -12517,7 +12619,7 @@ function peelGlobalOptions(tokens, baseCwd) {
|
|
|
12517
12619
|
continue;
|
|
12518
12620
|
}
|
|
12519
12621
|
if (token.startsWith("-C") && token.length > 2) {
|
|
12520
|
-
effectiveCwd =
|
|
12622
|
+
effectiveCwd = path39.resolve(baseCwd, token.slice(2));
|
|
12521
12623
|
index += 1;
|
|
12522
12624
|
continue;
|
|
12523
12625
|
}
|
|
@@ -12660,7 +12762,7 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12660
12762
|
if (!looksLikeFileOperand(token)) {
|
|
12661
12763
|
return false;
|
|
12662
12764
|
}
|
|
12663
|
-
if (token.startsWith(".") ||
|
|
12765
|
+
if (token.startsWith(".") || path39.isAbsolute(token)) {
|
|
12664
12766
|
return true;
|
|
12665
12767
|
}
|
|
12666
12768
|
return token.includes(".");
|
|
@@ -12668,12 +12770,12 @@ function looksLikeDiffPathOperand(token) {
|
|
|
12668
12770
|
function resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir) {
|
|
12669
12771
|
const resolveBase = effectiveCwd ?? baseCwd;
|
|
12670
12772
|
if (workTree) {
|
|
12671
|
-
return
|
|
12773
|
+
return path39.resolve(resolveBase, workTree);
|
|
12672
12774
|
}
|
|
12673
12775
|
if (gitDir) {
|
|
12674
|
-
const resolvedGitDir =
|
|
12675
|
-
if (
|
|
12676
|
-
return
|
|
12776
|
+
const resolvedGitDir = path39.resolve(resolveBase, gitDir);
|
|
12777
|
+
if (path39.basename(resolvedGitDir) === ".git") {
|
|
12778
|
+
return path39.dirname(resolvedGitDir);
|
|
12677
12779
|
}
|
|
12678
12780
|
}
|
|
12679
12781
|
return void 0;
|
|
@@ -12754,7 +12856,7 @@ function classifyGitCommand(tokens, baseCwd) {
|
|
|
12754
12856
|
const { subcommand, args, effectiveCwd, gitDir, workTree } = normalized;
|
|
12755
12857
|
const normalizedKey = `git ${subcommand}`;
|
|
12756
12858
|
const gitWorkTree = resolveGitWorkTree(baseCwd, effectiveCwd, workTree, gitDir);
|
|
12757
|
-
const effectiveGitDir = gitDir ?
|
|
12859
|
+
const effectiveGitDir = gitDir ? path39.resolve(effectiveCwd ?? baseCwd, gitDir) : void 0;
|
|
12758
12860
|
const scopeTargets = [effectiveCwd, gitWorkTree, effectiveGitDir].filter(
|
|
12759
12861
|
(target, index, targets) => Boolean(target) && targets.indexOf(target) === index
|
|
12760
12862
|
);
|
|
@@ -12906,9 +13008,9 @@ function decodeGitEffects(params) {
|
|
|
12906
13008
|
...subcommand === "push" ? ["tier0_external"] : []
|
|
12907
13009
|
];
|
|
12908
13010
|
const effectiveCwd = normalized.effectiveCwd ?? params.cwd;
|
|
12909
|
-
const workTreeRoot = normalized.workTree ?
|
|
12910
|
-
const gitRefRoot = normalized.gitDir ?
|
|
12911
|
-
const gitControlRoot = normalized.gitDir ? gitRefRoot :
|
|
13011
|
+
const workTreeRoot = normalized.workTree ? path39.resolve(normalized.effectiveCwd ?? params.cwd, normalized.workTree) : normalized.effectiveCwd ?? params.repoRoot;
|
|
13012
|
+
const gitRefRoot = normalized.gitDir ? path39.resolve(effectiveCwd, normalized.gitDir) : workTreeRoot;
|
|
13013
|
+
const gitControlRoot = normalized.gitDir ? gitRefRoot : path39.join(gitRefRoot, ".git");
|
|
12912
13014
|
const requirements = [];
|
|
12913
13015
|
if (subcommand === "fetch" || subcommand === "pull") {
|
|
12914
13016
|
const positionals = gitRemotePositionals(args);
|
|
@@ -13067,7 +13169,7 @@ function decodeGitEffects(params) {
|
|
|
13067
13169
|
gitRequirement(
|
|
13068
13170
|
"control_plane.write",
|
|
13069
13171
|
"control_plane.write",
|
|
13070
|
-
{ kind: "path", path:
|
|
13172
|
+
{ kind: "path", path: path39.join(gitControlRoot, "logs") },
|
|
13071
13173
|
params.segment,
|
|
13072
13174
|
[...signals, "git_history_destructive", "git.reflog.mutate"]
|
|
13073
13175
|
)
|
|
@@ -13125,7 +13227,7 @@ function decodeGitEffects(params) {
|
|
|
13125
13227
|
gitRequirement(
|
|
13126
13228
|
"fs.read",
|
|
13127
13229
|
"fs.read",
|
|
13128
|
-
{ kind: "path", path:
|
|
13230
|
+
{ kind: "path", path: path39.resolve(workTreeRoot, operand) },
|
|
13129
13231
|
params.segment,
|
|
13130
13232
|
[...signals, "git.path.read"]
|
|
13131
13233
|
)
|
|
@@ -13160,7 +13262,7 @@ function decodeGitEffects(params) {
|
|
|
13160
13262
|
gitRequirement(
|
|
13161
13263
|
"fs.write",
|
|
13162
13264
|
"fs.write",
|
|
13163
|
-
{ kind: "path", path:
|
|
13265
|
+
{ kind: "path", path: path39.resolve(workTreeRoot, operand) },
|
|
13164
13266
|
params.segment,
|
|
13165
13267
|
[...signals, "git.path.write"]
|
|
13166
13268
|
)
|
|
@@ -13344,8 +13446,8 @@ function gitRequirement(tag, action, resource, segment, signals) {
|
|
|
13344
13446
|
}
|
|
13345
13447
|
|
|
13346
13448
|
// src/core/verdict/launcher-resolve.ts
|
|
13347
|
-
import { existsSync as
|
|
13348
|
-
import
|
|
13449
|
+
import { existsSync as existsSync12, readFileSync as readFileSync5 } from "node:fs";
|
|
13450
|
+
import path40 from "node:path";
|
|
13349
13451
|
|
|
13350
13452
|
// src/core/verdict/makefile-expand.ts
|
|
13351
13453
|
var MAX_EXPAND_DEPTH = 16;
|
|
@@ -13518,8 +13620,8 @@ var PNPM_BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
13518
13620
|
"why"
|
|
13519
13621
|
]);
|
|
13520
13622
|
function readPackageJson(dir) {
|
|
13521
|
-
const packagePath =
|
|
13522
|
-
if (!
|
|
13623
|
+
const packagePath = path40.join(dir, "package.json");
|
|
13624
|
+
if (!existsSync12(packagePath)) {
|
|
13523
13625
|
return null;
|
|
13524
13626
|
}
|
|
13525
13627
|
try {
|
|
@@ -13529,17 +13631,17 @@ function readPackageJson(dir) {
|
|
|
13529
13631
|
}
|
|
13530
13632
|
}
|
|
13531
13633
|
function findPackageJson(startDir, stopDir) {
|
|
13532
|
-
let current =
|
|
13533
|
-
const stop =
|
|
13634
|
+
let current = path40.resolve(startDir);
|
|
13635
|
+
const stop = path40.resolve(stopDir);
|
|
13534
13636
|
while (true) {
|
|
13535
|
-
const packagePath =
|
|
13536
|
-
if (
|
|
13637
|
+
const packagePath = path40.join(current, "package.json");
|
|
13638
|
+
if (existsSync12(packagePath)) {
|
|
13537
13639
|
return packagePath;
|
|
13538
13640
|
}
|
|
13539
|
-
if (current === stop || current ===
|
|
13540
|
-
return
|
|
13641
|
+
if (current === stop || current === path40.dirname(current)) {
|
|
13642
|
+
return existsSync12(packagePath) ? packagePath : null;
|
|
13541
13643
|
}
|
|
13542
|
-
const parent =
|
|
13644
|
+
const parent = path40.dirname(current);
|
|
13543
13645
|
if (!parent.startsWith(stop) && parent !== current) {
|
|
13544
13646
|
}
|
|
13545
13647
|
if (parent === current) {
|
|
@@ -13596,7 +13698,7 @@ function resolveNpmRecipe(cwd, repoRoot, scriptName, extraArgs) {
|
|
|
13596
13698
|
}
|
|
13597
13699
|
return { recipes: [], opaque: true, reason: "package_json_missing" };
|
|
13598
13700
|
}
|
|
13599
|
-
const pkg = readPackageJson(
|
|
13701
|
+
const pkg = readPackageJson(path40.dirname(packagePath));
|
|
13600
13702
|
const scripts = pkg?.scripts;
|
|
13601
13703
|
if (!scripts || typeof scripts !== "object") {
|
|
13602
13704
|
return { recipes: [], opaque: true, reason: "package_scripts_missing" };
|
|
@@ -13687,20 +13789,20 @@ function parseMakefileRecipeContent(content) {
|
|
|
13687
13789
|
function resolveMakeRecipe(cwd, repoRoot, target, cliVars = {}) {
|
|
13688
13790
|
const candidates = ["Makefile", "makefile", "GNUmakefile"];
|
|
13689
13791
|
let makefilePath = null;
|
|
13690
|
-
let searchDir =
|
|
13691
|
-
const stop =
|
|
13792
|
+
let searchDir = path40.resolve(cwd);
|
|
13793
|
+
const stop = path40.resolve(repoRoot);
|
|
13692
13794
|
while (true) {
|
|
13693
13795
|
for (const name of candidates) {
|
|
13694
|
-
const candidate =
|
|
13695
|
-
if (
|
|
13796
|
+
const candidate = path40.join(searchDir, name);
|
|
13797
|
+
if (existsSync12(candidate)) {
|
|
13696
13798
|
makefilePath = candidate;
|
|
13697
13799
|
break;
|
|
13698
13800
|
}
|
|
13699
13801
|
}
|
|
13700
|
-
if (makefilePath || searchDir === stop || searchDir ===
|
|
13802
|
+
if (makefilePath || searchDir === stop || searchDir === path40.dirname(searchDir)) {
|
|
13701
13803
|
break;
|
|
13702
13804
|
}
|
|
13703
|
-
searchDir =
|
|
13805
|
+
searchDir = path40.dirname(searchDir);
|
|
13704
13806
|
}
|
|
13705
13807
|
if (!makefilePath) {
|
|
13706
13808
|
return { recipes: [], opaque: true, reason: "unknown_local_effect" };
|
|
@@ -13727,7 +13829,7 @@ function resolveMakeRecipe(cwd, repoRoot, target, cliVars = {}) {
|
|
|
13727
13829
|
}
|
|
13728
13830
|
const entry = targets.get(name);
|
|
13729
13831
|
if (!entry) {
|
|
13730
|
-
if (!
|
|
13832
|
+
if (!existsSync12(path40.resolve(path40.dirname(makefilePath), name))) {
|
|
13731
13833
|
hasUndefinedPrerequisite = true;
|
|
13732
13834
|
}
|
|
13733
13835
|
return;
|
|
@@ -13826,7 +13928,7 @@ function resolveLauncherRecipe(params) {
|
|
|
13826
13928
|
}
|
|
13827
13929
|
|
|
13828
13930
|
// src/core/verdict/parser.ts
|
|
13829
|
-
import
|
|
13931
|
+
import path41 from "node:path";
|
|
13830
13932
|
|
|
13831
13933
|
// src/core/shell-substitution.ts
|
|
13832
13934
|
function findStructuralCommandSubstitutions(command) {
|
|
@@ -14066,7 +14168,7 @@ var INTERPRETER_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
14066
14168
|
".sh"
|
|
14067
14169
|
]);
|
|
14068
14170
|
function normalizeHead(token) {
|
|
14069
|
-
const base =
|
|
14171
|
+
const base = path41.basename(token);
|
|
14070
14172
|
if (base && base !== "." && base !== "..") {
|
|
14071
14173
|
return base;
|
|
14072
14174
|
}
|
|
@@ -14376,7 +14478,7 @@ function isBareInterpreter(tokens) {
|
|
|
14376
14478
|
return false;
|
|
14377
14479
|
}
|
|
14378
14480
|
const scriptArg = args.find((token) => !token.startsWith("-"));
|
|
14379
|
-
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(
|
|
14481
|
+
if (scriptArg && INTERPRETER_SCRIPT_EXTENSIONS.has(path41.extname(scriptArg))) {
|
|
14380
14482
|
return false;
|
|
14381
14483
|
}
|
|
14382
14484
|
if (scriptArg) {
|
|
@@ -14616,7 +14718,7 @@ function mergeNodes(nodes) {
|
|
|
14616
14718
|
}
|
|
14617
14719
|
|
|
14618
14720
|
// src/core/effect-ir/shell-lower/argv-delegate-gate.ts
|
|
14619
|
-
import
|
|
14721
|
+
import path42 from "node:path";
|
|
14620
14722
|
var ARGV_DELEGATE_INNER_BLOCKLIST = /* @__PURE__ */ new Set([
|
|
14621
14723
|
"sudo",
|
|
14622
14724
|
"env",
|
|
@@ -14637,7 +14739,7 @@ function shouldApplyArgvDelegate(head, innerTokens, depth) {
|
|
|
14637
14739
|
if (ARGV_DELEGATE_INNER_BLOCKLIST.has(head)) {
|
|
14638
14740
|
return false;
|
|
14639
14741
|
}
|
|
14640
|
-
const innerHead =
|
|
14742
|
+
const innerHead = path42.basename(innerTokens[0] ?? "");
|
|
14641
14743
|
if (ARGV_DELEGATE_INNER_BLOCKLIST.has(innerHead)) {
|
|
14642
14744
|
return false;
|
|
14643
14745
|
}
|
|
@@ -14744,7 +14846,7 @@ function withInnerProvenance(requirementValue, innerCommand, launcher, outerSegm
|
|
|
14744
14846
|
|
|
14745
14847
|
// src/core/effect-ir/shell-lower/tokens.ts
|
|
14746
14848
|
init_shell_tokenizer();
|
|
14747
|
-
import
|
|
14849
|
+
import path43 from "node:path";
|
|
14748
14850
|
var ENV_PREFIX_PATTERN2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
|
|
14749
14851
|
var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "0:0:0:0:0:0:0:1"]);
|
|
14750
14852
|
var METADATA_ONLY_FLAGS = /* @__PURE__ */ new Set(["--version", "-v", "-V", "--help", "-h"]);
|
|
@@ -14888,9 +14990,9 @@ function resolvePathOperand(operand, cwd) {
|
|
|
14888
14990
|
return process.env.HOME ?? operand;
|
|
14889
14991
|
}
|
|
14890
14992
|
if (operand.startsWith("~/")) {
|
|
14891
|
-
return
|
|
14993
|
+
return path43.join(process.env.HOME ?? "~", operand.slice(2));
|
|
14892
14994
|
}
|
|
14893
|
-
return
|
|
14995
|
+
return path43.resolve(cwd, operand);
|
|
14894
14996
|
}
|
|
14895
14997
|
function isOptionToken(value) {
|
|
14896
14998
|
return value.startsWith("-") || value.startsWith("+");
|
|
@@ -14902,7 +15004,7 @@ function pipeToShell(command) {
|
|
|
14902
15004
|
return /(?:^|[|;&]\s*)(?:bash|sh|zsh|dash|fish)(?:\s|$)/.test(command) && /\|/.test(command);
|
|
14903
15005
|
}
|
|
14904
15006
|
function executableBaseName(head) {
|
|
14905
|
-
return
|
|
15007
|
+
return path43.basename(head);
|
|
14906
15008
|
}
|
|
14907
15009
|
function isMetadataOnlyArgv(argv) {
|
|
14908
15010
|
return argv.length > 0 && argv.every((token) => METADATA_ONLY_FLAGS.has(token));
|
|
@@ -15021,15 +15123,22 @@ function effectChangingEnvironmentSignals(head, env, changedNames) {
|
|
|
15021
15123
|
}
|
|
15022
15124
|
|
|
15023
15125
|
// src/core/effect-ir/shell-lower/decode-process.ts
|
|
15024
|
-
import
|
|
15126
|
+
import path47 from "node:path";
|
|
15025
15127
|
|
|
15026
15128
|
// src/core/effect-ir/shell-lower/decoders/belay.ts
|
|
15027
|
-
import
|
|
15129
|
+
import path44 from "node:path";
|
|
15028
15130
|
function decodeBelay(args, repoRoot, segment) {
|
|
15029
15131
|
const [section, operation, key] = args;
|
|
15030
15132
|
const judgeCommand = section === "judge" && operation !== void 0 && ["consent", "list", "status", "test", "use"].includes(operation);
|
|
15031
15133
|
const configRead = section === "config" && (operation === void 0 || operation === "list" || operation === "get" && key?.startsWith("judge."));
|
|
15032
15134
|
const configJudgeMutation = section === "config" && (["set", "unset"].includes(operation ?? "") && key?.startsWith("judge.") || operation === "credential" && key === "mode");
|
|
15135
|
+
const approvalAuthorityCommand = [
|
|
15136
|
+
"approval-token",
|
|
15137
|
+
"approve",
|
|
15138
|
+
"revoke",
|
|
15139
|
+
"standing-allow"
|
|
15140
|
+
].includes(section ?? "");
|
|
15141
|
+
const configTrustMutation = section === "config" && operation === "trust";
|
|
15033
15142
|
if (judgeCommand || configRead || configJudgeMutation) {
|
|
15034
15143
|
return [
|
|
15035
15144
|
processRequirement("belay", "inspect", segment, [
|
|
@@ -15038,12 +15147,23 @@ function decodeBelay(args, repoRoot, segment) {
|
|
|
15038
15147
|
])
|
|
15039
15148
|
];
|
|
15040
15149
|
}
|
|
15150
|
+
if (approvalAuthorityCommand || configTrustMutation) {
|
|
15151
|
+
return [
|
|
15152
|
+
requirement2(
|
|
15153
|
+
"control_plane.write",
|
|
15154
|
+
"control_plane.write",
|
|
15155
|
+
{ kind: "path", path: path44.join(repoRoot, ".belay-control-plane") },
|
|
15156
|
+
segment,
|
|
15157
|
+
[approvalAuthorityCommand ? "belay.approval_authority" : "belay.config_trust"]
|
|
15158
|
+
)
|
|
15159
|
+
];
|
|
15160
|
+
}
|
|
15041
15161
|
if (section === "config" && ["set", "unset", "credential"].includes(operation ?? "")) {
|
|
15042
15162
|
return [
|
|
15043
15163
|
requirement2(
|
|
15044
15164
|
"control_plane.write",
|
|
15045
15165
|
"control_plane.write",
|
|
15046
|
-
{ kind: "path", path:
|
|
15166
|
+
{ kind: "path", path: path44.join(repoRoot, ".belay-control-plane") },
|
|
15047
15167
|
segment,
|
|
15048
15168
|
["belay.config_non_judge_mutation"]
|
|
15049
15169
|
)
|
|
@@ -15284,7 +15404,7 @@ function validDockerInfo(args) {
|
|
|
15284
15404
|
// src/core/effect-ir/shell-lower/decoders/filesystem.ts
|
|
15285
15405
|
init_git_resource_identity();
|
|
15286
15406
|
import { lstatSync as lstatSync2, realpathSync as realpathSync4 } from "node:fs";
|
|
15287
|
-
import
|
|
15407
|
+
import path45 from "node:path";
|
|
15288
15408
|
function decodeCopyMove(head, args, cwd, segment) {
|
|
15289
15409
|
const requirements = [processRequirement(head, "spawn", segment, ["process.filesystem_mutation"])];
|
|
15290
15410
|
const operands = [];
|
|
@@ -15412,11 +15532,11 @@ function decodeRm(args, cwd, repoRoot, segment) {
|
|
|
15412
15532
|
function canonicalRmOperand(targetPath, finalOperandIsSymlink) {
|
|
15413
15533
|
try {
|
|
15414
15534
|
if (finalOperandIsSymlink) {
|
|
15415
|
-
return
|
|
15535
|
+
return path45.join(realpathSync4.native(path45.dirname(targetPath)), path45.basename(targetPath));
|
|
15416
15536
|
}
|
|
15417
15537
|
return realpathSync4.native(targetPath);
|
|
15418
15538
|
} catch {
|
|
15419
|
-
return
|
|
15539
|
+
return path45.resolve(targetPath);
|
|
15420
15540
|
}
|
|
15421
15541
|
}
|
|
15422
15542
|
function isSymbolicLink(targetPath) {
|
|
@@ -15427,8 +15547,8 @@ function isSymbolicLink(targetPath) {
|
|
|
15427
15547
|
}
|
|
15428
15548
|
}
|
|
15429
15549
|
function pathContains(ancestor, candidate) {
|
|
15430
|
-
const relative =
|
|
15431
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
15550
|
+
const relative = path45.relative(path45.resolve(ancestor), path45.resolve(candidate));
|
|
15551
|
+
return relative === "" || !relative.startsWith("..") && !path45.isAbsolute(relative);
|
|
15432
15552
|
}
|
|
15433
15553
|
function filesystemReadOperands(head, args) {
|
|
15434
15554
|
switch (head) {
|
|
@@ -15568,7 +15688,7 @@ function decodePrisma(args, env, repoRoot, segment) {
|
|
|
15568
15688
|
|
|
15569
15689
|
// src/core/effect-ir/shell-lower/decoders/ruby.ts
|
|
15570
15690
|
init_path_utils();
|
|
15571
|
-
import
|
|
15691
|
+
import path46 from "node:path";
|
|
15572
15692
|
var RAILS_READ_ONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["routes", "middleware", "stats", "about", "version"]);
|
|
15573
15693
|
function railsReadOnlySubcommand(args) {
|
|
15574
15694
|
const subcommand = args[0];
|
|
@@ -15578,7 +15698,7 @@ function railsReadOnlySubcommand(args) {
|
|
|
15578
15698
|
return RAILS_READ_ONLY_SUBCOMMANDS.has(subcommand);
|
|
15579
15699
|
}
|
|
15580
15700
|
function isRubyTestScript(scriptPath) {
|
|
15581
|
-
const base =
|
|
15701
|
+
const base = path46.basename(scriptPath);
|
|
15582
15702
|
return base.endsWith("_test.rb") || base.endsWith("_spec.rb");
|
|
15583
15703
|
}
|
|
15584
15704
|
function parseRubyTestInvocation(args) {
|
|
@@ -15992,7 +16112,7 @@ function decodeProcessOrFilesystem(params) {
|
|
|
15992
16112
|
requirement2(
|
|
15993
16113
|
"fs.read",
|
|
15994
16114
|
"fs.read",
|
|
15995
|
-
{ kind: "path", path:
|
|
16115
|
+
{ kind: "path", path: path47.resolve(cwd, syntax) },
|
|
15996
16116
|
segment,
|
|
15997
16117
|
["shell.syntax_source_read"]
|
|
15998
16118
|
)
|
|
@@ -16198,7 +16318,7 @@ function packageExecInnerIsMetadata(peel) {
|
|
|
16198
16318
|
|
|
16199
16319
|
// src/core/effect-ir/shell-lower/segment.ts
|
|
16200
16320
|
init_shell_tokenizer();
|
|
16201
|
-
import
|
|
16321
|
+
import path48 from "node:path";
|
|
16202
16322
|
var DYNAMIC_SHELL_VALUE_PATTERN = /(?:\$\(|`|\$(?:\d+|[@*#?$!-]|\{[^}]*\}|[A-Za-z_][A-Za-z0-9_]*))/;
|
|
16203
16323
|
var SHELL_GLOB_PATTERN = /[*?[]/;
|
|
16204
16324
|
function requiresKnownCwd(requirementValue) {
|
|
@@ -16213,11 +16333,11 @@ function joinNestedOpacity(outer, nested) {
|
|
|
16213
16333
|
}
|
|
16214
16334
|
function startsLocalPostgresService(command) {
|
|
16215
16335
|
const tokens = tokenizeShell(command);
|
|
16216
|
-
return
|
|
16336
|
+
return path48.basename(tokens[0] ?? "") === "docker" && tokens[1] === "compose" && ["up", "start", "restart"].includes(tokens[2] ?? "") && tokens.includes("postgres");
|
|
16217
16337
|
}
|
|
16218
16338
|
function resolveCdTransition(command, currentCwd) {
|
|
16219
16339
|
const tokens = tokenizeShell(command);
|
|
16220
|
-
if (
|
|
16340
|
+
if (path48.basename(tokens[0] ?? "") !== "cd") {
|
|
16221
16341
|
return null;
|
|
16222
16342
|
}
|
|
16223
16343
|
const target = tokens[1] ?? "~";
|
|
@@ -16380,7 +16500,7 @@ function lowerSegment(command, context) {
|
|
|
16380
16500
|
stripStructuredRedirects(lexed.tokens),
|
|
16381
16501
|
stripRedirects(parsedTokens)
|
|
16382
16502
|
);
|
|
16383
|
-
const head =
|
|
16503
|
+
const head = path49.basename(tokens[0] ?? parsed.head);
|
|
16384
16504
|
let opacity = segmentOpacity(command);
|
|
16385
16505
|
const signals = /* @__PURE__ */ new Set();
|
|
16386
16506
|
const requirements = [];
|
|
@@ -17232,7 +17352,7 @@ async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
|
17232
17352
|
};
|
|
17233
17353
|
}
|
|
17234
17354
|
const signals = [];
|
|
17235
|
-
const resolvedPath =
|
|
17355
|
+
const resolvedPath = path50.isAbsolute(filePath) ? filePath : path50.resolve(cwd, filePath);
|
|
17236
17356
|
const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
|
|
17237
17357
|
if (hitsProtectedRoot) {
|
|
17238
17358
|
signals.push("control_plane_path");
|
|
@@ -17818,7 +17938,7 @@ function hashDecisionConfig(config) {
|
|
|
17818
17938
|
init_fingerprint2();
|
|
17819
17939
|
|
|
17820
17940
|
// src/version.ts
|
|
17821
|
-
var PACKAGE_VERSION = "0.
|
|
17941
|
+
var PACKAGE_VERSION = "0.10.0";
|
|
17822
17942
|
|
|
17823
17943
|
// src/runtime-provenance.ts
|
|
17824
17944
|
function resolveRuntimeArtifactHash(artifactHash) {
|
|
@@ -17891,56 +18011,56 @@ function capabilityRequestsBlockRecovery(requests) {
|
|
|
17891
18011
|
init_fingerprint2();
|
|
17892
18012
|
init_path_utils();
|
|
17893
18013
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
17894
|
-
import { existsSync as
|
|
17895
|
-
import { mkdir as
|
|
17896
|
-
import
|
|
18014
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
18015
|
+
import { mkdir as mkdir12, readdir as readdir3, readFile as readFile13, rename as rename4, rm as rm6 } from "node:fs/promises";
|
|
18016
|
+
import path55 from "node:path";
|
|
17897
18017
|
|
|
17898
18018
|
// src/core/recovery/artifact-store.ts
|
|
17899
18019
|
init_fingerprint2();
|
|
17900
18020
|
init_path_utils();
|
|
17901
18021
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
17902
|
-
import { existsSync as
|
|
17903
|
-
import { lstat as lstat7, mkdir as
|
|
17904
|
-
import
|
|
18022
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
18023
|
+
import { lstat as lstat7, mkdir as mkdir11, open as open6, readdir as readdir2, readFile as readFile10, rename as rename3, rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
18024
|
+
import path52 from "node:path";
|
|
17905
18025
|
|
|
17906
18026
|
// src/core/recovery/snapshot-node.ts
|
|
17907
18027
|
init_fingerprint2();
|
|
17908
18028
|
init_path_utils();
|
|
17909
18029
|
import { createHash as createHash12 } from "node:crypto";
|
|
17910
|
-
import { existsSync as
|
|
18030
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
17911
18031
|
import {
|
|
17912
|
-
chmod as
|
|
18032
|
+
chmod as chmod4,
|
|
17913
18033
|
copyFile as copyFile2,
|
|
17914
18034
|
lstat as lstat6,
|
|
17915
|
-
mkdir as
|
|
17916
|
-
open as
|
|
17917
|
-
readFile as
|
|
18035
|
+
mkdir as mkdir10,
|
|
18036
|
+
open as open5,
|
|
18037
|
+
readFile as readFile9,
|
|
17918
18038
|
readlink as readlink4,
|
|
17919
18039
|
rm as rm4,
|
|
17920
18040
|
rmdir as rmdir2,
|
|
17921
18041
|
symlink as symlink3,
|
|
17922
18042
|
writeFile as writeFile6
|
|
17923
18043
|
} from "node:fs/promises";
|
|
17924
|
-
import
|
|
18044
|
+
import path51 from "node:path";
|
|
17925
18045
|
var RECOVERY_UNSUPPORTED_FILE_KIND = "recovery_unsupported_file_kind";
|
|
17926
18046
|
function validRecoveryRelativePath(relativePath) {
|
|
17927
|
-
if (!relativePath || relativePath.includes("\0") ||
|
|
17928
|
-
const normalized =
|
|
17929
|
-
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${
|
|
18047
|
+
if (!relativePath || relativePath.includes("\0") || path51.isAbsolute(relativePath)) return false;
|
|
18048
|
+
const normalized = path51.normalize(relativePath);
|
|
18049
|
+
return normalized !== "." && normalized !== ".." && !normalized.startsWith(`..${path51.sep}`);
|
|
17930
18050
|
}
|
|
17931
18051
|
async function assertRecoverySafeTarget(resourceRoot, relativePath) {
|
|
17932
18052
|
if (!validRecoveryRelativePath(relativePath)) throw new Error("recovery_path_escape");
|
|
17933
18053
|
const root = canonicalPath(resourceRoot);
|
|
17934
|
-
const target =
|
|
17935
|
-
const relative =
|
|
17936
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
18054
|
+
const target = path51.resolve(root, relativePath);
|
|
18055
|
+
const relative = path51.relative(root, target);
|
|
18056
|
+
if (relative === ".." || relative.startsWith(`..${path51.sep}`) || path51.isAbsolute(relative)) {
|
|
17937
18057
|
throw new Error("recovery_path_escape");
|
|
17938
18058
|
}
|
|
17939
18059
|
let current = root;
|
|
17940
|
-
const parentParts =
|
|
18060
|
+
const parentParts = path51.relative(root, path51.dirname(target)).split(path51.sep).filter(Boolean);
|
|
17941
18061
|
for (const part of parentParts) {
|
|
17942
|
-
current =
|
|
17943
|
-
if (!
|
|
18062
|
+
current = path51.join(current, part);
|
|
18063
|
+
if (!existsSync13(current)) break;
|
|
17944
18064
|
const info = await lstat6(current);
|
|
17945
18065
|
if (info.isSymbolicLink()) throw new Error("recovery_symlink_escape");
|
|
17946
18066
|
if (!info.isDirectory()) break;
|
|
@@ -17948,7 +18068,7 @@ async function assertRecoverySafeTarget(resourceRoot, relativePath) {
|
|
|
17948
18068
|
return target;
|
|
17949
18069
|
}
|
|
17950
18070
|
async function fsyncPath(filePath) {
|
|
17951
|
-
const handle = await
|
|
18071
|
+
const handle = await open5(filePath, "r");
|
|
17952
18072
|
try {
|
|
17953
18073
|
await handle.sync();
|
|
17954
18074
|
} finally {
|
|
@@ -17990,13 +18110,13 @@ async function captureRecoverySnapshot(filePath, options) {
|
|
|
17990
18110
|
return { kind: "directory", mode, hash: recoveryDirectoryHash(mode) };
|
|
17991
18111
|
}
|
|
17992
18112
|
if (!info.isFile()) throw new Error(RECOVERY_UNSUPPORTED_FILE_KIND);
|
|
17993
|
-
const content = await
|
|
18113
|
+
const content = await readFile9(filePath);
|
|
17994
18114
|
const hash = createHash12("sha256").update(content).digest("hex");
|
|
17995
18115
|
let blob;
|
|
17996
18116
|
if (options?.blobDir) {
|
|
17997
|
-
await
|
|
17998
|
-
const blobPath =
|
|
17999
|
-
if (!
|
|
18117
|
+
await mkdir10(options.blobDir, { recursive: true, mode: 448 });
|
|
18118
|
+
const blobPath = path51.join(options.blobDir, hash);
|
|
18119
|
+
if (!existsSync13(blobPath)) {
|
|
18000
18120
|
await writeFile6(blobPath, content, { mode: 384 });
|
|
18001
18121
|
await fsyncPath(blobPath);
|
|
18002
18122
|
}
|
|
@@ -18050,7 +18170,7 @@ async function validateRecoverySnapshot(params) {
|
|
|
18050
18170
|
if (record.blob !== `blobs/${record.hash}`) throw new Error(params.corruptReason);
|
|
18051
18171
|
let content;
|
|
18052
18172
|
try {
|
|
18053
|
-
content = await
|
|
18173
|
+
content = await readFile9(path51.join(params.artifactDir, record.blob));
|
|
18054
18174
|
} catch {
|
|
18055
18175
|
throw new Error(params.corruptReason);
|
|
18056
18176
|
}
|
|
@@ -18078,16 +18198,16 @@ var RECOVERY_STATES = /* @__PURE__ */ new Set([
|
|
|
18078
18198
|
]);
|
|
18079
18199
|
var STAGING_STALE_MS = 5 * 6e4;
|
|
18080
18200
|
function checkpointsRoot(stateDir) {
|
|
18081
|
-
return
|
|
18201
|
+
return path52.join(stateDir, "recovery", "checkpoints");
|
|
18082
18202
|
}
|
|
18083
18203
|
function checkpointDir(stateDir, checkpointId) {
|
|
18084
18204
|
if (!/^cp_[a-f0-9]{24}$/.test(checkpointId)) {
|
|
18085
18205
|
throw new Error("invalid_recovery_checkpoint_id");
|
|
18086
18206
|
}
|
|
18087
|
-
return
|
|
18207
|
+
return path52.join(checkpointsRoot(stateDir), checkpointId);
|
|
18088
18208
|
}
|
|
18089
18209
|
async function fsyncPath2(filePath) {
|
|
18090
|
-
const handle = await
|
|
18210
|
+
const handle = await open6(filePath, "r");
|
|
18091
18211
|
try {
|
|
18092
18212
|
await handle.sync();
|
|
18093
18213
|
} finally {
|
|
@@ -18095,13 +18215,13 @@ async function fsyncPath2(filePath) {
|
|
|
18095
18215
|
}
|
|
18096
18216
|
}
|
|
18097
18217
|
async function atomicWriteJson(filePath, value) {
|
|
18098
|
-
await
|
|
18218
|
+
await mkdir11(path52.dirname(filePath), { recursive: true, mode: 448 });
|
|
18099
18219
|
const temporary = `${filePath}.tmp-${randomUUID4()}`;
|
|
18100
18220
|
await writeFile7(temporary, `${JSON.stringify(value, null, 2)}
|
|
18101
18221
|
`, { mode: 384 });
|
|
18102
18222
|
await fsyncPath2(temporary);
|
|
18103
|
-
await
|
|
18104
|
-
await fsyncPath2(
|
|
18223
|
+
await rename3(temporary, filePath);
|
|
18224
|
+
await fsyncPath2(path52.dirname(filePath));
|
|
18105
18225
|
}
|
|
18106
18226
|
async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
18107
18227
|
const value = {
|
|
@@ -18111,13 +18231,13 @@ async function writeRecoveryState(artifactDir, state, manifestHash, detail) {
|
|
|
18111
18231
|
manifestHash,
|
|
18112
18232
|
...detail ? { detail } : {}
|
|
18113
18233
|
};
|
|
18114
|
-
await atomicWriteJson(
|
|
18234
|
+
await atomicWriteJson(path52.join(artifactDir, "state.json"), value);
|
|
18115
18235
|
}
|
|
18116
18236
|
async function directorySize(root) {
|
|
18117
|
-
if (!
|
|
18237
|
+
if (!existsSync14(root)) return 0;
|
|
18118
18238
|
let total = 0;
|
|
18119
18239
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
18120
|
-
const entryPath =
|
|
18240
|
+
const entryPath = path52.join(root, entry.name);
|
|
18121
18241
|
if (entry.isDirectory()) total += await directorySize(entryPath);
|
|
18122
18242
|
else total += (await lstat7(entryPath)).size;
|
|
18123
18243
|
}
|
|
@@ -18162,9 +18282,9 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18162
18282
|
let rawManifest;
|
|
18163
18283
|
let state;
|
|
18164
18284
|
try {
|
|
18165
|
-
rawManifest = JSON.parse(await
|
|
18285
|
+
rawManifest = JSON.parse(await readFile10(path52.join(artifactDir, "manifest.json"), "utf8"));
|
|
18166
18286
|
state = JSON.parse(
|
|
18167
|
-
await
|
|
18287
|
+
await readFile10(path52.join(artifactDir, "state.json"), "utf8")
|
|
18168
18288
|
);
|
|
18169
18289
|
} catch {
|
|
18170
18290
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
@@ -18180,10 +18300,10 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18180
18300
|
}
|
|
18181
18301
|
const entryPaths = /* @__PURE__ */ new Set();
|
|
18182
18302
|
for (const entry of manifest.entries) {
|
|
18183
|
-
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(
|
|
18303
|
+
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(path52.normalize(entry.path))) {
|
|
18184
18304
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
18185
18305
|
}
|
|
18186
|
-
entryPaths.add(
|
|
18306
|
+
entryPaths.add(path52.normalize(entry.path));
|
|
18187
18307
|
for (const [side, snapshot] of [
|
|
18188
18308
|
["before", entry.before],
|
|
18189
18309
|
["after", entry.after]
|
|
@@ -18197,9 +18317,9 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18197
18317
|
});
|
|
18198
18318
|
}
|
|
18199
18319
|
}
|
|
18200
|
-
const receiptPath =
|
|
18320
|
+
const receiptPath = path52.join(artifactDir, "receipt.json");
|
|
18201
18321
|
let receipt;
|
|
18202
|
-
if (["applied", "restoring", "restored", "conflict"].includes(state.state) ||
|
|
18322
|
+
if (["applied", "restoring", "restored", "conflict"].includes(state.state) || existsSync14(receiptPath)) {
|
|
18203
18323
|
receipt = await readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
18204
18324
|
}
|
|
18205
18325
|
return { artifactDir, manifest, state, manifestHash, ...receipt ? { receipt } : {} };
|
|
@@ -18207,7 +18327,7 @@ async function readRecoveryArtifact(stateDir, checkpointId) {
|
|
|
18207
18327
|
async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
18208
18328
|
let rawReceipt;
|
|
18209
18329
|
try {
|
|
18210
|
-
rawReceipt = JSON.parse(await
|
|
18330
|
+
rawReceipt = JSON.parse(await readFile10(path52.join(artifactDir, "receipt.json"), "utf8"));
|
|
18211
18331
|
} catch {
|
|
18212
18332
|
throw new Error(RECOVERY_CHECKPOINT_CORRUPT);
|
|
18213
18333
|
}
|
|
@@ -18230,8 +18350,8 @@ async function readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHas
|
|
|
18230
18350
|
return receipt;
|
|
18231
18351
|
}
|
|
18232
18352
|
async function ensureRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
18233
|
-
const receiptPath =
|
|
18234
|
-
if (
|
|
18353
|
+
const receiptPath = path52.join(artifactDir, "receipt.json");
|
|
18354
|
+
if (existsSync14(receiptPath)) {
|
|
18235
18355
|
return readAndValidateRecoveryReceipt(artifactDir, manifest, manifestHash);
|
|
18236
18356
|
}
|
|
18237
18357
|
const receipt = {
|
|
@@ -18248,14 +18368,14 @@ async function ensureRecoveryReceipt(artifactDir, manifest, manifestHash) {
|
|
|
18248
18368
|
}
|
|
18249
18369
|
async function checkpointIds(stateDir) {
|
|
18250
18370
|
const root = checkpointsRoot(stateDir);
|
|
18251
|
-
if (!
|
|
18371
|
+
if (!existsSync14(root)) return [];
|
|
18252
18372
|
return (await readdir2(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^cp_[a-f0-9]{24}$/.test(entry.name)).map((entry) => entry.name);
|
|
18253
18373
|
}
|
|
18254
18374
|
async function artifactRepoRoot(stateDir, checkpointId) {
|
|
18255
18375
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
18256
18376
|
try {
|
|
18257
18377
|
const manifest = JSON.parse(
|
|
18258
|
-
await
|
|
18378
|
+
await readFile10(path52.join(artifactDir, "manifest.json"), "utf8")
|
|
18259
18379
|
);
|
|
18260
18380
|
if (typeof manifest.repoRoot === "string" && manifest.repoRoot) {
|
|
18261
18381
|
return canonicalPath(manifest.repoRoot);
|
|
@@ -18263,7 +18383,7 @@ async function artifactRepoRoot(stateDir, checkpointId) {
|
|
|
18263
18383
|
} catch {
|
|
18264
18384
|
}
|
|
18265
18385
|
try {
|
|
18266
|
-
const owner = JSON.parse(await
|
|
18386
|
+
const owner = JSON.parse(await readFile10(path52.join(artifactDir, "owner.json"), "utf8"));
|
|
18267
18387
|
return typeof owner.repoRoot === "string" && owner.repoRoot ? canonicalPath(owner.repoRoot) : null;
|
|
18268
18388
|
} catch {
|
|
18269
18389
|
return null;
|
|
@@ -18279,14 +18399,14 @@ async function checkpointIdsForRepo(stateDir, repoRoot) {
|
|
|
18279
18399
|
}
|
|
18280
18400
|
async function cleanupOrphanedStaging(stateDir) {
|
|
18281
18401
|
const root = checkpointsRoot(stateDir);
|
|
18282
|
-
if (!
|
|
18402
|
+
if (!existsSync14(root)) return;
|
|
18283
18403
|
const now = Date.now();
|
|
18284
18404
|
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
18285
18405
|
if (!entry.isDirectory() || !/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) continue;
|
|
18286
|
-
const stagingPath =
|
|
18406
|
+
const stagingPath = path52.join(root, entry.name);
|
|
18287
18407
|
let stale = false;
|
|
18288
18408
|
try {
|
|
18289
|
-
const owner = JSON.parse(await
|
|
18409
|
+
const owner = JSON.parse(await readFile10(path52.join(stagingPath, "owner.json"), "utf8"));
|
|
18290
18410
|
const pid = typeof owner.pid === "number" ? owner.pid : Number.NaN;
|
|
18291
18411
|
const createdAt = typeof owner.createdAt === "string" ? Date.parse(owner.createdAt) : NaN;
|
|
18292
18412
|
let alive = false;
|
|
@@ -18326,9 +18446,9 @@ async function markRecoveryCheckpointApplied(stateDir, checkpoint) {
|
|
|
18326
18446
|
|
|
18327
18447
|
// src/core/recovery/reconcile.ts
|
|
18328
18448
|
init_fingerprint2();
|
|
18329
|
-
import { existsSync as
|
|
18330
|
-
import { readFile as
|
|
18331
|
-
import
|
|
18449
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
18450
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
18451
|
+
import path53 from "node:path";
|
|
18332
18452
|
async function matchRecoverySide(resourceRoot, entries, side) {
|
|
18333
18453
|
for (const entry of entries) {
|
|
18334
18454
|
const target = await assertRecoverySafeTarget(resourceRoot, entry.path);
|
|
@@ -18342,9 +18462,9 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
18342
18462
|
loaded = await readRecoveryArtifact(stateDir, checkpointId);
|
|
18343
18463
|
} catch {
|
|
18344
18464
|
const artifactDir = checkpointDir(stateDir, checkpointId);
|
|
18345
|
-
if (
|
|
18346
|
-
const manifestPath =
|
|
18347
|
-
const hash =
|
|
18465
|
+
if (existsSync15(artifactDir)) {
|
|
18466
|
+
const manifestPath = path53.join(artifactDir, "manifest.json");
|
|
18467
|
+
const hash = existsSync15(manifestPath) ? hashValue(await readFile11(manifestPath, "utf8")) : "unavailable";
|
|
18348
18468
|
await writeRecoveryState(artifactDir, "corrupt", hash, RECOVERY_CHECKPOINT_CORRUPT);
|
|
18349
18469
|
}
|
|
18350
18470
|
return "corrupt";
|
|
@@ -18379,8 +18499,8 @@ async function reconcileRecoveryCheckpoint(stateDir, checkpointId) {
|
|
|
18379
18499
|
|
|
18380
18500
|
// src/core/recovery/resource-identity.ts
|
|
18381
18501
|
init_fingerprint2();
|
|
18382
|
-
import { lstat as lstat8, readFile as
|
|
18383
|
-
import
|
|
18502
|
+
import { lstat as lstat8, readFile as readFile12, realpath as realpath3 } from "node:fs/promises";
|
|
18503
|
+
import path54 from "node:path";
|
|
18384
18504
|
async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
18385
18505
|
const resolvedRoot = await realpath3(resourceRoot);
|
|
18386
18506
|
if (resourceKind === "directory") {
|
|
@@ -18388,13 +18508,13 @@ async function currentRecoveryResourceIdentity(resourceRoot, resourceKind) {
|
|
|
18388
18508
|
if (!rootInfo.isDirectory()) throw new Error("recovery_repo_identity_unavailable");
|
|
18389
18509
|
return hashValue(`${resolvedRoot}\0${rootInfo.dev}:${rootInfo.ino}:${rootInfo.birthtimeMs}`);
|
|
18390
18510
|
}
|
|
18391
|
-
const dotGit =
|
|
18511
|
+
const dotGit = path54.join(resolvedRoot, ".git");
|
|
18392
18512
|
const gitInfo = await lstat8(dotGit);
|
|
18393
18513
|
let gitMetadataPath = dotGit;
|
|
18394
18514
|
if (gitInfo.isFile()) {
|
|
18395
|
-
const marker = (await
|
|
18515
|
+
const marker = (await readFile12(dotGit, "utf8")).trim();
|
|
18396
18516
|
if (!marker.startsWith("gitdir:")) throw new Error("recovery_repo_identity_unavailable");
|
|
18397
|
-
gitMetadataPath =
|
|
18517
|
+
gitMetadataPath = path54.resolve(resolvedRoot, marker.slice("gitdir:".length).trim());
|
|
18398
18518
|
} else if (!gitInfo.isDirectory()) {
|
|
18399
18519
|
throw new Error("recovery_repo_identity_unavailable");
|
|
18400
18520
|
}
|
|
@@ -18459,7 +18579,7 @@ async function garbageCollect(stateDir, config, repoRoot) {
|
|
|
18459
18579
|
async function prepareRecoveryCheckpoint(params) {
|
|
18460
18580
|
const backend = params.backend ?? "git_worktree";
|
|
18461
18581
|
const resourceKind = await resolveRecoveryResourceKind(backend, params.repoRoot);
|
|
18462
|
-
await
|
|
18582
|
+
await mkdir12(checkpointsRoot(params.stateDir), { recursive: true, mode: 448 });
|
|
18463
18583
|
await cleanupOrphanedStaging(params.stateDir);
|
|
18464
18584
|
await garbageCollect(params.stateDir, params.config, params.repoRoot);
|
|
18465
18585
|
const existing = await checkpointIdsForRepo(params.stateDir, params.repoRoot);
|
|
@@ -18467,16 +18587,16 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18467
18587
|
throw new Error(RECOVERY_CHECKPOINT_QUOTA);
|
|
18468
18588
|
}
|
|
18469
18589
|
const checkpointId = `cp_${randomUUID5().replaceAll("-", "").slice(0, 24)}`;
|
|
18470
|
-
const temporary =
|
|
18590
|
+
const temporary = path55.join(checkpointsRoot(params.stateDir), `.tmp-${checkpointId}`);
|
|
18471
18591
|
const finalDir = checkpointDir(params.stateDir, checkpointId);
|
|
18472
|
-
await
|
|
18473
|
-
await atomicWriteJson(
|
|
18592
|
+
await mkdir12(temporary, { recursive: true, mode: 448 });
|
|
18593
|
+
await atomicWriteJson(path55.join(temporary, "owner.json"), {
|
|
18474
18594
|
version: 1,
|
|
18475
18595
|
pid: process.pid,
|
|
18476
18596
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18477
18597
|
repoRoot: canonicalPath(params.repoRoot)
|
|
18478
18598
|
});
|
|
18479
|
-
await
|
|
18599
|
+
await mkdir12(path55.join(temporary, "blobs"), { recursive: true, mode: 448 });
|
|
18480
18600
|
try {
|
|
18481
18601
|
const entries = [];
|
|
18482
18602
|
const protectedRoots = (params.protectedRoots ?? []).map(canonicalPath);
|
|
@@ -18485,8 +18605,8 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18485
18605
|
)) {
|
|
18486
18606
|
const target = await assertRecoverySafeTarget(params.repoRoot, change.relativePath);
|
|
18487
18607
|
if (protectedRoots.some((root) => {
|
|
18488
|
-
const relative =
|
|
18489
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
18608
|
+
const relative = path55.relative(root, target);
|
|
18609
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path55.sep}`) && !path55.isAbsolute(relative);
|
|
18490
18610
|
})) {
|
|
18491
18611
|
throw new Error("recovery_protected_path");
|
|
18492
18612
|
}
|
|
@@ -18498,7 +18618,7 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18498
18618
|
entries.push({
|
|
18499
18619
|
path: change.relativePath,
|
|
18500
18620
|
before: await captureRecoverySnapshot(baseline, {
|
|
18501
|
-
blobDir:
|
|
18621
|
+
blobDir: path55.join(temporary, "blobs")
|
|
18502
18622
|
}),
|
|
18503
18623
|
after: withoutRecoveryBlob(await captureRecoverySnapshot(source))
|
|
18504
18624
|
});
|
|
@@ -18535,12 +18655,12 @@ async function prepareRecoveryCheckpoint(params) {
|
|
|
18535
18655
|
entries
|
|
18536
18656
|
};
|
|
18537
18657
|
const manifestHash = hashValue(canonicalStringify(manifest));
|
|
18538
|
-
await atomicWriteJson(
|
|
18658
|
+
await atomicWriteJson(path55.join(temporary, "manifest.json"), manifest);
|
|
18539
18659
|
await writeRecoveryState(temporary, "prepared", manifestHash);
|
|
18540
18660
|
await fsyncPath2(temporary);
|
|
18541
18661
|
const projectedBytes = await recoveryCheckpointStorageBytes(params.stateDir, params.repoRoot);
|
|
18542
18662
|
if (projectedBytes > params.config.maxBytes) throw new Error(RECOVERY_CHECKPOINT_QUOTA);
|
|
18543
|
-
await
|
|
18663
|
+
await rename4(temporary, finalDir);
|
|
18544
18664
|
await fsyncPath2(checkpointsRoot(params.stateDir));
|
|
18545
18665
|
return {
|
|
18546
18666
|
checkpointId,
|
|
@@ -18578,7 +18698,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18578
18698
|
} catch {
|
|
18579
18699
|
try {
|
|
18580
18700
|
const raw = JSON.parse(
|
|
18581
|
-
await
|
|
18701
|
+
await readFile13(path55.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
18582
18702
|
);
|
|
18583
18703
|
rootFromArtifact = typeof raw.repoRoot === "string" && raw.repoRoot ? raw.repoRoot : void 0;
|
|
18584
18704
|
} catch {
|
|
@@ -18612,7 +18732,7 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18612
18732
|
} catch {
|
|
18613
18733
|
try {
|
|
18614
18734
|
const manifest = JSON.parse(
|
|
18615
|
-
await
|
|
18735
|
+
await readFile13(path55.join(checkpointDir(stateDir, id), "manifest.json"), "utf8")
|
|
18616
18736
|
);
|
|
18617
18737
|
if (manifest.checkpointId !== id || ![1, 2].includes(manifest.version)) continue;
|
|
18618
18738
|
if (repoRoot && canonicalPath(manifest.repoRoot) !== canonicalPath(repoRoot)) continue;
|
|
@@ -18637,12 +18757,12 @@ async function listRecoveryCheckpoints(stateDir, repoRoot) {
|
|
|
18637
18757
|
async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
18638
18758
|
const root = checkpointsRoot(stateDir);
|
|
18639
18759
|
if (!repoRoot) return directorySize(root);
|
|
18640
|
-
if (!
|
|
18760
|
+
if (!existsSync16(root)) return 0;
|
|
18641
18761
|
const expected = canonicalPath(repoRoot);
|
|
18642
18762
|
let total = 0;
|
|
18643
18763
|
for (const entry of await readdir3(root, { withFileTypes: true })) {
|
|
18644
18764
|
if (!entry.isDirectory()) continue;
|
|
18645
|
-
const entryPath =
|
|
18765
|
+
const entryPath = path55.join(root, entry.name);
|
|
18646
18766
|
if (/^cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
18647
18767
|
if (await artifactRepoRoot(stateDir, entry.name) === expected) {
|
|
18648
18768
|
total += await directorySize(entryPath);
|
|
@@ -18651,7 +18771,7 @@ async function recoveryCheckpointStorageBytes(stateDir, repoRoot) {
|
|
|
18651
18771
|
}
|
|
18652
18772
|
if (/^\.tmp-cp_[a-f0-9]{24}$/.test(entry.name)) {
|
|
18653
18773
|
try {
|
|
18654
|
-
const owner = JSON.parse(await
|
|
18774
|
+
const owner = JSON.parse(await readFile13(path55.join(entryPath, "owner.json"), "utf8"));
|
|
18655
18775
|
if (typeof owner.repoRoot === "string" && canonicalPath(owner.repoRoot) === expected) {
|
|
18656
18776
|
total += await directorySize(entryPath);
|
|
18657
18777
|
}
|
|
@@ -18693,16 +18813,16 @@ function recoveryFailClosedResult(predicted, reason, signals = []) {
|
|
|
18693
18813
|
init_scrub();
|
|
18694
18814
|
|
|
18695
18815
|
// src/core/transactional/file-checkpoint-backend.ts
|
|
18696
|
-
import { cp, lstat as lstat11, mkdir as
|
|
18816
|
+
import { cp, lstat as lstat11, mkdir as mkdir14, mkdtemp as mkdtemp5, readdir as readdir6, rm as rm9, writeFile as writeFile10 } from "node:fs/promises";
|
|
18697
18817
|
import os5 from "node:os";
|
|
18698
|
-
import
|
|
18818
|
+
import path59 from "node:path";
|
|
18699
18819
|
|
|
18700
18820
|
// src/core/transactional/file-checkpoint-git.ts
|
|
18701
18821
|
init_path_utils();
|
|
18702
18822
|
import { spawn as spawn8 } from "node:child_process";
|
|
18703
18823
|
import { createHash as createHash13 } from "node:crypto";
|
|
18704
|
-
import { copyFile as copyFile3, lstat as lstat9, readdir as readdir4, readFile as
|
|
18705
|
-
import
|
|
18824
|
+
import { copyFile as copyFile3, lstat as lstat9, readdir as readdir4, readFile as readFile14 } from "node:fs/promises";
|
|
18825
|
+
import path56 from "node:path";
|
|
18706
18826
|
var FILE_CHECKPOINT_GIT_METADATA_CHANGED = "file_checkpoint_git_metadata_changed";
|
|
18707
18827
|
var FILE_CHECKPOINT_SOURCE_CHANGED = "file_checkpoint_source_changed";
|
|
18708
18828
|
var FILE_CHECKPOINT_CWD_OUTSIDE_ROOT = "file_checkpoint_cwd_outside_root";
|
|
@@ -18736,7 +18856,7 @@ function rethrowStableFileCheckpointError(error) {
|
|
|
18736
18856
|
}
|
|
18737
18857
|
async function rootGitMetadataPresent(repoRoot) {
|
|
18738
18858
|
try {
|
|
18739
|
-
await lstat9(
|
|
18859
|
+
await lstat9(path56.join(repoRoot, ".git"));
|
|
18740
18860
|
return true;
|
|
18741
18861
|
} catch {
|
|
18742
18862
|
return false;
|
|
@@ -18785,10 +18905,10 @@ function execGit2(repoRoot, args) {
|
|
|
18785
18905
|
}
|
|
18786
18906
|
async function resolveGitPath(repoRoot, gitPath) {
|
|
18787
18907
|
const trimmed = gitPath.trim();
|
|
18788
|
-
if (
|
|
18908
|
+
if (path56.isAbsolute(trimmed)) {
|
|
18789
18909
|
return trimmed;
|
|
18790
18910
|
}
|
|
18791
|
-
return
|
|
18911
|
+
return path56.join(repoRoot, trimmed);
|
|
18792
18912
|
}
|
|
18793
18913
|
async function cloneBareWorktreeCopy(sourceRoot, destinationRoot) {
|
|
18794
18914
|
await execGit2(sourceRoot, [
|
|
@@ -18830,7 +18950,7 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
18830
18950
|
destinationRoot,
|
|
18831
18951
|
await execGit2(destinationRoot, ["rev-parse", "--git-dir"])
|
|
18832
18952
|
);
|
|
18833
|
-
const destinationShared =
|
|
18953
|
+
const destinationShared = path56.join(destinationGitDir, path56.basename(sourceShared));
|
|
18834
18954
|
try {
|
|
18835
18955
|
await copyFile3(sourceShared, destinationShared);
|
|
18836
18956
|
} catch (error) {
|
|
@@ -18839,14 +18959,14 @@ async function copyGitIndexState(sourceRoot, destinationRoot) {
|
|
|
18839
18959
|
}
|
|
18840
18960
|
async function readGitFile(gitDir, relativePath) {
|
|
18841
18961
|
try {
|
|
18842
|
-
return await
|
|
18962
|
+
return await readFile14(path56.join(gitDir, relativePath));
|
|
18843
18963
|
} catch {
|
|
18844
18964
|
return null;
|
|
18845
18965
|
}
|
|
18846
18966
|
}
|
|
18847
18967
|
async function readAbsoluteGitFile(absolutePath) {
|
|
18848
18968
|
try {
|
|
18849
|
-
return await
|
|
18969
|
+
return await readFile14(absolutePath);
|
|
18850
18970
|
} catch {
|
|
18851
18971
|
return null;
|
|
18852
18972
|
}
|
|
@@ -18863,8 +18983,8 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
18863
18983
|
repoRoot,
|
|
18864
18984
|
await execGit2(repoRoot, ["rev-parse", "--git-path", gitPath])
|
|
18865
18985
|
);
|
|
18866
|
-
const relative =
|
|
18867
|
-
const content = typeof relative === "string" && !relative.startsWith("..") && !
|
|
18986
|
+
const relative = path56.resolve(resolved).startsWith(path56.resolve(gitDir)) ? path56.relative(gitDir, resolved) : resolved;
|
|
18987
|
+
const content = typeof relative === "string" && !relative.startsWith("..") && !path56.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
18868
18988
|
if (content !== null) {
|
|
18869
18989
|
hashGitFileContent(hash, gitPath, content);
|
|
18870
18990
|
}
|
|
@@ -18874,13 +18994,13 @@ async function hashResolvedGitPath(repoRoot, gitDir, gitPath, hash) {
|
|
|
18874
18994
|
async function hashGitTree(gitDir, relativeDir, hash) {
|
|
18875
18995
|
let names;
|
|
18876
18996
|
try {
|
|
18877
|
-
names = await readdir4(
|
|
18997
|
+
names = await readdir4(path56.join(gitDir, relativeDir));
|
|
18878
18998
|
} catch {
|
|
18879
18999
|
return;
|
|
18880
19000
|
}
|
|
18881
19001
|
for (const name of names.sort()) {
|
|
18882
|
-
const relativePath = relativeDir ?
|
|
18883
|
-
const absolutePath =
|
|
19002
|
+
const relativePath = relativeDir ? path56.join(relativeDir, name) : name;
|
|
19003
|
+
const absolutePath = path56.join(gitDir, relativePath);
|
|
18884
19004
|
let childNames = null;
|
|
18885
19005
|
try {
|
|
18886
19006
|
childNames = await readdir4(absolutePath);
|
|
@@ -18902,7 +19022,7 @@ async function hashGitTree(gitDir, relativeDir, hash) {
|
|
|
18902
19022
|
}
|
|
18903
19023
|
async function computeGitMetadataFingerprint(repoRoot) {
|
|
18904
19024
|
const gitDirRel = (await execGit2(repoRoot, ["rev-parse", "--git-dir"])).trim();
|
|
18905
|
-
const gitDir =
|
|
19025
|
+
const gitDir = path56.isAbsolute(gitDirRel) ? gitDirRel : path56.join(repoRoot, gitDirRel);
|
|
18906
19026
|
const hash = createHash13("sha256");
|
|
18907
19027
|
for (const file of [
|
|
18908
19028
|
"HEAD",
|
|
@@ -18925,8 +19045,8 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
18925
19045
|
const sharedIndex = (await execGit2(repoRoot, ["rev-parse", "--shared-index-path"])).trim();
|
|
18926
19046
|
if (sharedIndex) {
|
|
18927
19047
|
const resolved = await resolveGitPath(repoRoot, sharedIndex);
|
|
18928
|
-
const relative =
|
|
18929
|
-
const content = relative && !relative.startsWith("..") && !
|
|
19048
|
+
const relative = path56.relative(gitDir, resolved);
|
|
19049
|
+
const content = relative && !relative.startsWith("..") && !path56.isAbsolute(relative) ? await readGitFile(gitDir, relative) : await readAbsoluteGitFile(resolved);
|
|
18930
19050
|
if (content !== null) {
|
|
18931
19051
|
hashGitFileContent(hash, "shared-index", content);
|
|
18932
19052
|
}
|
|
@@ -18937,7 +19057,7 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
18937
19057
|
await hashResolvedGitPath(repoRoot, gitDir, gitPath, hash);
|
|
18938
19058
|
}
|
|
18939
19059
|
try {
|
|
18940
|
-
const rootGitPath =
|
|
19060
|
+
const rootGitPath = path56.join(repoRoot, ".git");
|
|
18941
19061
|
const rootGitInfo = await lstat9(rootGitPath);
|
|
18942
19062
|
if (rootGitInfo.isFile()) {
|
|
18943
19063
|
const content = await readAbsoluteGitFile(rootGitPath);
|
|
@@ -18953,14 +19073,14 @@ async function computeGitMetadataFingerprint(repoRoot) {
|
|
|
18953
19073
|
function resolveExecutionCwdRelative(resourceRoot, cwd) {
|
|
18954
19074
|
const resolvedCwd = canonicalPath(cwd);
|
|
18955
19075
|
const resourceCanonical = canonicalPath(resourceRoot);
|
|
18956
|
-
const relative =
|
|
19076
|
+
const relative = path56.relative(resourceCanonical, resolvedCwd);
|
|
18957
19077
|
if (relative === "" || relative === ".") {
|
|
18958
19078
|
return "";
|
|
18959
19079
|
}
|
|
18960
|
-
if (relative.startsWith("..") ||
|
|
19080
|
+
if (relative.startsWith("..") || path56.isAbsolute(relative)) {
|
|
18961
19081
|
throw new Error(FILE_CHECKPOINT_CWD_OUTSIDE_ROOT);
|
|
18962
19082
|
}
|
|
18963
|
-
return relative.split(
|
|
19083
|
+
return relative.split(path56.sep).join("/");
|
|
18964
19084
|
}
|
|
18965
19085
|
|
|
18966
19086
|
// src/core/transactional/file-checkpoint-isolation.ts
|
|
@@ -18981,8 +19101,8 @@ function fileCheckpointIsolationReason(context) {
|
|
|
18981
19101
|
}
|
|
18982
19102
|
|
|
18983
19103
|
// src/core/transactional/file-checkpoint-staging.ts
|
|
18984
|
-
import { readdir as readdir5, readFile as
|
|
18985
|
-
import
|
|
19104
|
+
import { readdir as readdir5, readFile as readFile15, rm as rm7, writeFile as writeFile8 } from "node:fs/promises";
|
|
19105
|
+
import path57 from "node:path";
|
|
18986
19106
|
function isOwnerProcessAlive(pid) {
|
|
18987
19107
|
try {
|
|
18988
19108
|
process.kill(pid, 0);
|
|
@@ -18992,12 +19112,12 @@ function isOwnerProcessAlive(pid) {
|
|
|
18992
19112
|
}
|
|
18993
19113
|
}
|
|
18994
19114
|
async function writeOwnerMarker(stagingRoot, marker) {
|
|
18995
|
-
await writeFile8(
|
|
19115
|
+
await writeFile8(path57.join(stagingRoot, "owner.json"), `${JSON.stringify(marker)}
|
|
18996
19116
|
`, "utf8");
|
|
18997
19117
|
}
|
|
18998
19118
|
async function readOwnerMarker(stagingRoot) {
|
|
18999
19119
|
try {
|
|
19000
|
-
const raw = await
|
|
19120
|
+
const raw = await readFile15(path57.join(stagingRoot, "owner.json"), "utf8");
|
|
19001
19121
|
return JSON.parse(raw.trim());
|
|
19002
19122
|
} catch {
|
|
19003
19123
|
return null;
|
|
@@ -19015,7 +19135,7 @@ async function collectDeadOwnerStaging(parentDir) {
|
|
|
19015
19135
|
if (!name.startsWith("belay-file-checkpoint-")) {
|
|
19016
19136
|
continue;
|
|
19017
19137
|
}
|
|
19018
|
-
const stagingRoot =
|
|
19138
|
+
const stagingRoot = path57.join(parentDir, name);
|
|
19019
19139
|
const marker = await readOwnerMarker(stagingRoot);
|
|
19020
19140
|
if (!marker) {
|
|
19021
19141
|
dead.push(stagingRoot);
|
|
@@ -19038,7 +19158,7 @@ import { constants as fsConstants2 } from "node:fs";
|
|
|
19038
19158
|
import {
|
|
19039
19159
|
copyFile as copyFile4,
|
|
19040
19160
|
lstat as lstat10,
|
|
19041
|
-
mkdir as
|
|
19161
|
+
mkdir as mkdir13,
|
|
19042
19162
|
mkdtemp as mkdtemp4,
|
|
19043
19163
|
readlink as readlink5,
|
|
19044
19164
|
rm as rm8,
|
|
@@ -19047,17 +19167,17 @@ import {
|
|
|
19047
19167
|
writeFile as writeFile9
|
|
19048
19168
|
} from "node:fs/promises";
|
|
19049
19169
|
import os4 from "node:os";
|
|
19050
|
-
import
|
|
19170
|
+
import path58 from "node:path";
|
|
19051
19171
|
var FILE_CHECKPOINT_COPY_FAILED = "file_checkpoint_copy_failed";
|
|
19052
19172
|
async function chmodSafe2(target, mode) {
|
|
19053
19173
|
try {
|
|
19054
|
-
const { chmod:
|
|
19055
|
-
await
|
|
19174
|
+
const { chmod: chmod5 } = await import("node:fs/promises");
|
|
19175
|
+
await chmod5(target, mode & 511);
|
|
19056
19176
|
} catch {
|
|
19057
19177
|
}
|
|
19058
19178
|
}
|
|
19059
19179
|
async function copyRegularFile(sourcePath, destinationPath, mode, strategy) {
|
|
19060
|
-
await
|
|
19180
|
+
await mkdir13(path58.dirname(destinationPath), { recursive: true });
|
|
19061
19181
|
if (strategy === "clonefile" && fsConstants2.COPYFILE_FICLONE !== void 0) {
|
|
19062
19182
|
try {
|
|
19063
19183
|
await copyFile4(sourcePath, destinationPath, fsConstants2.COPYFILE_FICLONE);
|
|
@@ -19079,11 +19199,11 @@ async function copyNode(sourceRoot, destinationRoot, relativePath, strategy) {
|
|
|
19079
19199
|
const info = await lstat10(sourcePath);
|
|
19080
19200
|
await rm8(destinationPath, { force: true, recursive: false });
|
|
19081
19201
|
if (info.isDirectory() && !info.isSymbolicLink()) {
|
|
19082
|
-
await
|
|
19202
|
+
await mkdir13(destinationPath, { recursive: true, mode: info.mode & 511 });
|
|
19083
19203
|
return strategy;
|
|
19084
19204
|
}
|
|
19085
19205
|
if (info.isSymbolicLink()) {
|
|
19086
|
-
await
|
|
19206
|
+
await mkdir13(path58.dirname(destinationPath), { recursive: true });
|
|
19087
19207
|
await symlink4(await readlink5(sourcePath), destinationPath);
|
|
19088
19208
|
return strategy;
|
|
19089
19209
|
}
|
|
@@ -19139,9 +19259,9 @@ async function mapWithConcurrency(items, concurrency, worker) {
|
|
|
19139
19259
|
async function probeFileCloneStrategy() {
|
|
19140
19260
|
let tempDir = null;
|
|
19141
19261
|
try {
|
|
19142
|
-
tempDir = await mkdtemp4(
|
|
19143
|
-
const source =
|
|
19144
|
-
const destination =
|
|
19262
|
+
tempDir = await mkdtemp4(path58.join(os4.tmpdir(), "belay-clone-probe-"));
|
|
19263
|
+
const source = path58.join(tempDir, "source.txt");
|
|
19264
|
+
const destination = path58.join(tempDir, "dest.txt");
|
|
19145
19265
|
await writeFile9(source, "probe\n");
|
|
19146
19266
|
if (fsConstants2.COPYFILE_FICLONE_FORCE !== void 0) {
|
|
19147
19267
|
try {
|
|
@@ -19294,11 +19414,11 @@ async function protectedRootState(root) {
|
|
|
19294
19414
|
return `directory:${node.hash}:${index.treeHash}`;
|
|
19295
19415
|
}
|
|
19296
19416
|
function executionProtectedRoot(resourceRoot, executionRoot, protectedRoot) {
|
|
19297
|
-
const relative =
|
|
19298
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
19417
|
+
const relative = path59.relative(path59.resolve(resourceRoot), path59.resolve(protectedRoot));
|
|
19418
|
+
if (relative === "" || relative.startsWith("..") || path59.isAbsolute(relative)) {
|
|
19299
19419
|
return null;
|
|
19300
19420
|
}
|
|
19301
|
-
return
|
|
19421
|
+
return path59.join(executionRoot, relative);
|
|
19302
19422
|
}
|
|
19303
19423
|
async function captureProtectedRootStates(resourceRoot, executionRoot, protectedRoots) {
|
|
19304
19424
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -19323,15 +19443,15 @@ async function directoryByteSize(root, deadlineMs) {
|
|
|
19323
19443
|
}
|
|
19324
19444
|
let total = 0;
|
|
19325
19445
|
for (const name of await readdir6(root)) {
|
|
19326
|
-
total += await directoryByteSize(
|
|
19446
|
+
total += await directoryByteSize(path59.join(root, name), deadlineMs);
|
|
19327
19447
|
}
|
|
19328
19448
|
return total;
|
|
19329
19449
|
}
|
|
19330
19450
|
async function copyGitMetadataDirectory(sourceRoot, destinationRoot) {
|
|
19331
19451
|
const gitDirRel = (await execGit2(sourceRoot, ["rev-parse", "--git-dir"])).trim();
|
|
19332
|
-
const sourceGitDir =
|
|
19333
|
-
const relativeGitDir =
|
|
19334
|
-
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ?
|
|
19452
|
+
const sourceGitDir = path59.isAbsolute(gitDirRel) ? gitDirRel : path59.join(sourceRoot, gitDirRel);
|
|
19453
|
+
const relativeGitDir = path59.relative(path59.resolve(sourceRoot), path59.resolve(sourceGitDir));
|
|
19454
|
+
const destinationGitDir = relativeGitDir && !relativeGitDir.startsWith("..") ? path59.join(destinationRoot, relativeGitDir) : path59.join(destinationRoot, ".git");
|
|
19335
19455
|
await cp(sourceGitDir, destinationGitDir, { recursive: true, force: true });
|
|
19336
19456
|
}
|
|
19337
19457
|
async function prepareDirtyGitSnapshot(context) {
|
|
@@ -19340,7 +19460,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19340
19460
|
const quotas = context.fileCheckpoint;
|
|
19341
19461
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
19342
19462
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
19343
|
-
const stagingRoot = await mkdtemp5(
|
|
19463
|
+
const stagingRoot = await mkdtemp5(path59.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
19344
19464
|
await writeOwnerMarker(stagingRoot, {
|
|
19345
19465
|
version: 1,
|
|
19346
19466
|
pid: process.pid,
|
|
@@ -19348,8 +19468,8 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19348
19468
|
resourceRoot: context.repoRoot,
|
|
19349
19469
|
backend: "file_checkpoint"
|
|
19350
19470
|
});
|
|
19351
|
-
const baselineRoot =
|
|
19352
|
-
const executionRoot =
|
|
19471
|
+
const baselineRoot = path59.join(stagingRoot, "baseline");
|
|
19472
|
+
const executionRoot = path59.join(stagingRoot, "execution");
|
|
19353
19473
|
try {
|
|
19354
19474
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
19355
19475
|
const sourceGitMetadataFingerprint = await computeGitMetadataFingerprint(context.repoRoot);
|
|
@@ -19383,7 +19503,7 @@ async function prepareDirtyGitSnapshot(context) {
|
|
|
19383
19503
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
19384
19504
|
}
|
|
19385
19505
|
await writeFile10(
|
|
19386
|
-
|
|
19506
|
+
path59.join(stagingRoot, "baseline-index.json"),
|
|
19387
19507
|
`${JSON.stringify(baselineIndex)}
|
|
19388
19508
|
`,
|
|
19389
19509
|
"utf8"
|
|
@@ -19433,7 +19553,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19433
19553
|
const quotas = context.fileCheckpoint;
|
|
19434
19554
|
const deadlineMs = Date.now() + quotas.prepareTimeoutMs;
|
|
19435
19555
|
await removeDeadOwnerStaging(os5.tmpdir());
|
|
19436
|
-
const stagingRoot = await mkdtemp5(
|
|
19556
|
+
const stagingRoot = await mkdtemp5(path59.join(os5.tmpdir(), "belay-file-checkpoint-"));
|
|
19437
19557
|
await writeOwnerMarker(stagingRoot, {
|
|
19438
19558
|
version: 1,
|
|
19439
19559
|
pid: process.pid,
|
|
@@ -19441,8 +19561,8 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19441
19561
|
resourceRoot: context.repoRoot,
|
|
19442
19562
|
backend: "file_checkpoint"
|
|
19443
19563
|
});
|
|
19444
|
-
const baselineRoot =
|
|
19445
|
-
const executionRoot =
|
|
19564
|
+
const baselineRoot = path59.join(stagingRoot, "baseline");
|
|
19565
|
+
const executionRoot = path59.join(stagingRoot, "execution");
|
|
19446
19566
|
try {
|
|
19447
19567
|
resolveExecutionCwdRelative(context.repoRoot, context.cwd);
|
|
19448
19568
|
const resourceIdentity = await currentRecoveryResourceIdentity(context.repoRoot, "directory");
|
|
@@ -19451,7 +19571,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19451
19571
|
quotas,
|
|
19452
19572
|
deadlineMs
|
|
19453
19573
|
});
|
|
19454
|
-
await
|
|
19574
|
+
await mkdir14(baselineRoot, { recursive: true });
|
|
19455
19575
|
const baselineIndex = await buildFileTreeIndex({
|
|
19456
19576
|
resourceRoot: baselineRoot,
|
|
19457
19577
|
excludedRoots,
|
|
@@ -19471,7 +19591,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19471
19591
|
throw new Error(FILE_CHECKPOINT_SOURCE_CHANGED);
|
|
19472
19592
|
}
|
|
19473
19593
|
await writeFile10(
|
|
19474
|
-
|
|
19594
|
+
path59.join(stagingRoot, "baseline-index.json"),
|
|
19475
19595
|
`${JSON.stringify(baselineIndex)}
|
|
19476
19596
|
`,
|
|
19477
19597
|
"utf8"
|
|
@@ -19481,7 +19601,7 @@ async function prepareNonGitSnapshot(context) {
|
|
|
19481
19601
|
quotas,
|
|
19482
19602
|
deadlineMs
|
|
19483
19603
|
});
|
|
19484
|
-
await
|
|
19604
|
+
await mkdir14(executionRoot, { recursive: true });
|
|
19485
19605
|
const finalSourceIndex = await buildFileTreeIndex({
|
|
19486
19606
|
resourceRoot: context.repoRoot,
|
|
19487
19607
|
excludedRoots,
|
|
@@ -19824,10 +19944,10 @@ async function selectTransactionalBackend(context) {
|
|
|
19824
19944
|
}
|
|
19825
19945
|
|
|
19826
19946
|
// src/core/transactional/diff-evaluator.ts
|
|
19827
|
-
import
|
|
19947
|
+
import path60 from "node:path";
|
|
19828
19948
|
init_path_utils();
|
|
19829
19949
|
function categorizeChange(change, ctx) {
|
|
19830
|
-
const absolutePath = canonicalPath(
|
|
19950
|
+
const absolutePath = canonicalPath(path60.join(ctx.repoRoot, change.relativePath));
|
|
19831
19951
|
if (!pathWithinRoot(ctx.repoRoot, absolutePath)) {
|
|
19832
19952
|
return "repo_outside";
|
|
19833
19953
|
}
|
|
@@ -20377,17 +20497,55 @@ function formatJudgeInfrastructureDenyMessage(params) {
|
|
|
20377
20497
|
}
|
|
20378
20498
|
|
|
20379
20499
|
// src/core/notify.ts
|
|
20500
|
+
init_path_utils();
|
|
20380
20501
|
import { execFile } from "node:child_process";
|
|
20502
|
+
import path61 from "node:path";
|
|
20381
20503
|
import { promisify } from "node:util";
|
|
20382
20504
|
var execFileAsync = promisify(execFile);
|
|
20383
|
-
|
|
20384
|
-
|
|
20385
|
-
|
|
20505
|
+
var LOOPBACK_WEBHOOK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
20506
|
+
function webhookConfigIssue(url) {
|
|
20507
|
+
let parsed;
|
|
20508
|
+
try {
|
|
20509
|
+
parsed = new URL(url);
|
|
20510
|
+
} catch {
|
|
20511
|
+
return `notifications.webhookUrl is invalid: ${url}`;
|
|
20512
|
+
}
|
|
20513
|
+
if (parsed.protocol === "https:") {
|
|
20514
|
+
return null;
|
|
20515
|
+
}
|
|
20516
|
+
const normalizedHostname = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
20517
|
+
if (parsed.protocol === "http:" && LOOPBACK_WEBHOOK_HOSTS.has(normalizedHostname)) {
|
|
20518
|
+
return null;
|
|
20519
|
+
}
|
|
20520
|
+
return `notifications.webhookUrl must use https (http is allowed only for localhost, 127.0.0.1, or ::1): ${url}`;
|
|
20521
|
+
}
|
|
20522
|
+
function commandHookConfigIssue(commandHook, repoRoot) {
|
|
20523
|
+
if (!path61.isAbsolute(commandHook)) {
|
|
20524
|
+
return `notifications.commandHook must be an absolute path: ${commandHook}`;
|
|
20525
|
+
}
|
|
20526
|
+
if (pathWithinRoot(canonicalPath(repoRoot), canonicalPath(commandHook))) {
|
|
20527
|
+
return `notifications.commandHook must not be inside the repository: ${commandHook}`;
|
|
20528
|
+
}
|
|
20529
|
+
return null;
|
|
20530
|
+
}
|
|
20531
|
+
async function notifyDeny(config, event, deps = {
|
|
20532
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
20533
|
+
execFile: (file, args, options) => execFileAsync(file, [...args], options)
|
|
20534
|
+
}) {
|
|
20535
|
+
const payload = JSON.stringify({
|
|
20536
|
+
approvalId: event.approvalId,
|
|
20537
|
+
reason: event.reason,
|
|
20538
|
+
summary: event.summary,
|
|
20539
|
+
repoRoot: event.repoRoot,
|
|
20540
|
+
fingerprint: event.fingerprint
|
|
20541
|
+
});
|
|
20542
|
+
const webhookIssue = config.webhookUrl ? webhookConfigIssue(config.webhookUrl) : null;
|
|
20543
|
+
if (config.webhookUrl && !webhookIssue) {
|
|
20386
20544
|
try {
|
|
20387
20545
|
const controller = new AbortController();
|
|
20388
20546
|
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
20389
20547
|
try {
|
|
20390
|
-
await fetch(config.webhookUrl, {
|
|
20548
|
+
await deps.fetch(config.webhookUrl, {
|
|
20391
20549
|
method: "POST",
|
|
20392
20550
|
headers: { "content-type": "application/json" },
|
|
20393
20551
|
body: payload,
|
|
@@ -20399,17 +20557,16 @@ async function notifyDeny(config, event) {
|
|
|
20399
20557
|
} catch {
|
|
20400
20558
|
}
|
|
20401
20559
|
}
|
|
20402
|
-
|
|
20560
|
+
const commandHookIssue = config.commandHook ? commandHookConfigIssue(config.commandHook, event.repoRoot) : null;
|
|
20561
|
+
if (config.commandHook && !commandHookIssue) {
|
|
20403
20562
|
try {
|
|
20404
|
-
await
|
|
20563
|
+
await deps.execFile(config.commandHook, [], {
|
|
20405
20564
|
env: {
|
|
20406
|
-
...process.env,
|
|
20407
20565
|
BELAY_APPROVAL_ID: event.approvalId,
|
|
20408
20566
|
BELAY_REASON: event.reason,
|
|
20409
20567
|
BELAY_SUMMARY: event.summary,
|
|
20410
20568
|
BELAY_REPO_ROOT: event.repoRoot,
|
|
20411
|
-
BELAY_FINGERPRINT: event.fingerprint
|
|
20412
|
-
BELAY_APPROVAL_TOKEN: event.approvalToken ?? ""
|
|
20569
|
+
BELAY_FINGERPRINT: event.fingerprint
|
|
20413
20570
|
}
|
|
20414
20571
|
});
|
|
20415
20572
|
} catch {
|
|
@@ -20419,9 +20576,10 @@ async function notifyDeny(config, event) {
|
|
|
20419
20576
|
|
|
20420
20577
|
// src/adapters/shared/gate-runtime.ts
|
|
20421
20578
|
init_path_utils();
|
|
20579
|
+
init_repo_config_trust();
|
|
20422
20580
|
|
|
20423
20581
|
// src/adapters/layouts/protected-paths.ts
|
|
20424
|
-
import
|
|
20582
|
+
import path62 from "node:path";
|
|
20425
20583
|
function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
20426
20584
|
const roots = [
|
|
20427
20585
|
layout.configPath(repoRoot),
|
|
@@ -20433,7 +20591,7 @@ function protectedArtifactRoots(layout, repoRoot, controlPlaneDir) {
|
|
|
20433
20591
|
if (controlPlaneDir) {
|
|
20434
20592
|
roots.push(controlPlaneDir);
|
|
20435
20593
|
}
|
|
20436
|
-
return roots.map((entry) =>
|
|
20594
|
+
return roots.map((entry) => path62.resolve(entry));
|
|
20437
20595
|
}
|
|
20438
20596
|
|
|
20439
20597
|
// src/adapters/shared/gate-runtime.ts
|
|
@@ -20474,7 +20632,7 @@ async function appendReplayAuditSafely(ctx, deps, event) {
|
|
|
20474
20632
|
}
|
|
20475
20633
|
async function loadJsonFile(filePath, fallback) {
|
|
20476
20634
|
try {
|
|
20477
|
-
const raw = await
|
|
20635
|
+
const raw = await readFile16(filePath, "utf8");
|
|
20478
20636
|
return JSON.parse(raw);
|
|
20479
20637
|
} catch {
|
|
20480
20638
|
return fallback;
|
|
@@ -20483,11 +20641,18 @@ async function loadJsonFile(filePath, fallback) {
|
|
|
20483
20641
|
function createDefaultGateRuntimeDeps() {
|
|
20484
20642
|
return {
|
|
20485
20643
|
async readConfig(configPath) {
|
|
20486
|
-
|
|
20644
|
+
try {
|
|
20645
|
+
return JSON.parse(await readFile16(configPath, "utf8"));
|
|
20646
|
+
} catch (error) {
|
|
20647
|
+
if (error.code === "ENOENT") {
|
|
20648
|
+
return {};
|
|
20649
|
+
}
|
|
20650
|
+
throw error;
|
|
20651
|
+
}
|
|
20487
20652
|
},
|
|
20488
20653
|
async appendAudit(ctx, event) {
|
|
20489
|
-
const auditPath =
|
|
20490
|
-
await
|
|
20654
|
+
const auditPath = path63.join(ctx.repoRoot, ctx.config.audit.logPath);
|
|
20655
|
+
await mkdir15(path63.dirname(auditPath), { recursive: true });
|
|
20491
20656
|
const provenance = auditProvenance(ctx.config);
|
|
20492
20657
|
const record = {
|
|
20493
20658
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -20520,7 +20685,7 @@ function createDefaultGateRuntimeDeps() {
|
|
|
20520
20685
|
};
|
|
20521
20686
|
},
|
|
20522
20687
|
async writeApprovals(filePath, state) {
|
|
20523
|
-
await
|
|
20688
|
+
await mkdir15(path63.dirname(filePath), { recursive: true });
|
|
20524
20689
|
await writeFile11(filePath, `${JSON.stringify(compactApprovals(state), null, 2)}
|
|
20525
20690
|
`, "utf8");
|
|
20526
20691
|
},
|
|
@@ -20541,10 +20706,11 @@ function createDefaultGateRuntimeDeps() {
|
|
|
20541
20706
|
}
|
|
20542
20707
|
async function resolveGateConfig(ctx, deps) {
|
|
20543
20708
|
const loaded = await deps.readConfig(ctx.configPath);
|
|
20709
|
+
await assertRepoConfigTrusted(ctx.repoRoot, ctx.layout.name, loaded);
|
|
20544
20710
|
let teamConfig = null;
|
|
20545
20711
|
const teamPath = teamConfigPath();
|
|
20546
|
-
if (
|
|
20547
|
-
teamConfig = JSON.parse(await
|
|
20712
|
+
if (existsSync17(teamPath)) {
|
|
20713
|
+
teamConfig = JSON.parse(await readFile16(teamPath, "utf8"));
|
|
20548
20714
|
}
|
|
20549
20715
|
return resolveLayeredConfig({
|
|
20550
20716
|
repoConfig: loaded,
|
|
@@ -20682,7 +20848,7 @@ function deriveWorkspaceRootScopeHint(params) {
|
|
|
20682
20848
|
if (!targetPath) {
|
|
20683
20849
|
return void 0;
|
|
20684
20850
|
}
|
|
20685
|
-
const candidateRoot = canonicalPath(
|
|
20851
|
+
const candidateRoot = canonicalPath(path63.dirname(targetPath));
|
|
20686
20852
|
const validation = validateTrustedWorkspaceRootCandidate({
|
|
20687
20853
|
candidatePath: candidateRoot,
|
|
20688
20854
|
repoRoot: action.repoRoot,
|
|
@@ -21073,7 +21239,21 @@ async function evaluateGatedAction(ctx, deps, params) {
|
|
|
21073
21239
|
...authorization,
|
|
21074
21240
|
egressProxyActive
|
|
21075
21241
|
};
|
|
21076
|
-
|
|
21242
|
+
let predicted = await classifyGatedActionAsync(action, ctx.config, enrichedClassifierOptions);
|
|
21243
|
+
if (action.kind === "tool" && predicted.reason === "unclassified_tool" && (ctx.config.policy.codexUnmappedTool ?? "deny") === "deny") {
|
|
21244
|
+
predicted = {
|
|
21245
|
+
...predicted,
|
|
21246
|
+
verdict: "deny_pending_approval",
|
|
21247
|
+
reason: "unmapped_tool",
|
|
21248
|
+
assessment: {
|
|
21249
|
+
reversibility: "irreversible",
|
|
21250
|
+
external: false,
|
|
21251
|
+
blastRadius: "unknown tool action",
|
|
21252
|
+
confidence: 0.5,
|
|
21253
|
+
signals: ["unmapped_tool"]
|
|
21254
|
+
}
|
|
21255
|
+
};
|
|
21256
|
+
}
|
|
21077
21257
|
if (action.kind === "shell" && action.command && isContainedUnknownExecutionEligible(ctx.config, action, predicted)) {
|
|
21078
21258
|
const mediated = await mediateContainedUnknownExecution({
|
|
21079
21259
|
ctx,
|
|
@@ -21292,7 +21472,7 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21292
21472
|
event: auditEvent,
|
|
21293
21473
|
sourceEvent,
|
|
21294
21474
|
kind,
|
|
21295
|
-
repoLabel:
|
|
21475
|
+
repoLabel: path63.basename(ctx.repoRoot),
|
|
21296
21476
|
...auditExtras.toolInvocationCorrelationId ? { toolInvocationCorrelationId: auditExtras.toolInvocationCorrelationId } : {},
|
|
21297
21477
|
fingerprint: result.fingerprint,
|
|
21298
21478
|
summary: result.normalizedCommand ?? result.summary ?? "",
|
|
@@ -21324,21 +21504,6 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21324
21504
|
if (created) {
|
|
21325
21505
|
await recordGateApprovalAsk(stateDir, result.reason, false);
|
|
21326
21506
|
}
|
|
21327
|
-
let approvalToken;
|
|
21328
|
-
try {
|
|
21329
|
-
approvalToken = await issueApprovalToken(
|
|
21330
|
-
{
|
|
21331
|
-
approvalId: approval.approvalId,
|
|
21332
|
-
fingerprint: approval.fingerprint,
|
|
21333
|
-
repoRoot: approval.repoRoot,
|
|
21334
|
-
issuedAt: approval.createdAt,
|
|
21335
|
-
expiresAt: approval.expiresAt
|
|
21336
|
-
},
|
|
21337
|
-
configuredControlPlaneDir(ctx.config)
|
|
21338
|
-
);
|
|
21339
|
-
} catch {
|
|
21340
|
-
approvalToken = void 0;
|
|
21341
|
-
}
|
|
21342
21507
|
const denialReason = failure?.reason ?? result.reason;
|
|
21343
21508
|
if (ctx.config.notifications.webhookUrl || ctx.config.notifications.commandHook) {
|
|
21344
21509
|
await notifyDeny(ctx.config.notifications, {
|
|
@@ -21346,8 +21511,7 @@ async function gateDecisionToVerdict(ctx, deps, kind, result, auditExtras = {})
|
|
|
21346
21511
|
reason: denialReason,
|
|
21347
21512
|
summary: result.normalizedCommand ?? result.summary ?? "",
|
|
21348
21513
|
repoRoot: ctx.repoRoot,
|
|
21349
|
-
fingerprint: result.fingerprint
|
|
21350
|
-
approvalToken
|
|
21514
|
+
fingerprint: result.fingerprint
|
|
21351
21515
|
});
|
|
21352
21516
|
}
|
|
21353
21517
|
const failureAuditFields = typeof failure?.auditFields === "function" ? failure.auditFields(approval) : failure?.auditFields;
|
|
@@ -21762,38 +21926,38 @@ async function appendObservedAudit(ctx, deps, eventName, payload) {
|
|
|
21762
21926
|
}
|
|
21763
21927
|
|
|
21764
21928
|
// src/adapters/shared/repo-root.ts
|
|
21765
|
-
import { existsSync as
|
|
21766
|
-
import
|
|
21929
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
21930
|
+
import path64 from "node:path";
|
|
21767
21931
|
function belayConfigPath(current, adapterName) {
|
|
21768
21932
|
if (adapterName === "cursor") {
|
|
21769
|
-
return
|
|
21933
|
+
return path64.join(current, ".cursor", "belay.config.json");
|
|
21770
21934
|
}
|
|
21771
21935
|
if (adapterName === "claude") {
|
|
21772
|
-
return
|
|
21936
|
+
return path64.join(current, ".claude", "belay.config.json");
|
|
21773
21937
|
}
|
|
21774
|
-
return
|
|
21938
|
+
return path64.join(current, ".codex", "belay.config.json");
|
|
21775
21939
|
}
|
|
21776
21940
|
function markerMatches(current, marker, layout) {
|
|
21777
|
-
const markerPath =
|
|
21778
|
-
if (!
|
|
21941
|
+
const markerPath = path64.join(current, marker);
|
|
21942
|
+
if (!existsSync18(markerPath)) {
|
|
21779
21943
|
return false;
|
|
21780
21944
|
}
|
|
21781
21945
|
if (marker === ".cursor" || marker === ".claude" || marker === ".codex") {
|
|
21782
|
-
return
|
|
21946
|
+
return existsSync18(belayConfigPath(current, layout.name));
|
|
21783
21947
|
}
|
|
21784
21948
|
return true;
|
|
21785
21949
|
}
|
|
21786
21950
|
function findRepoRoot(startPath, layout) {
|
|
21787
|
-
let current =
|
|
21951
|
+
let current = path64.resolve(startPath);
|
|
21788
21952
|
while (true) {
|
|
21789
21953
|
for (const marker of layout.repoRootMarkers) {
|
|
21790
21954
|
if (markerMatches(current, marker, layout)) {
|
|
21791
21955
|
return current;
|
|
21792
21956
|
}
|
|
21793
21957
|
}
|
|
21794
|
-
const parent =
|
|
21958
|
+
const parent = path64.dirname(current);
|
|
21795
21959
|
if (parent === current) {
|
|
21796
|
-
return
|
|
21960
|
+
return path64.resolve(startPath);
|
|
21797
21961
|
}
|
|
21798
21962
|
current = parent;
|
|
21799
21963
|
}
|
|
@@ -21827,7 +21991,7 @@ function resolveCodexActionCwd(payload, fallbackCwd = process2.cwd(), options =
|
|
|
21827
21991
|
const nestedCwd = options.includeToolInputCwd ? nonEmptyPathString(toolInput?.working_directory) ?? nonEmptyPathString(toolInput?.cwd) : void 0;
|
|
21828
21992
|
const payloadCwd = nonEmptyPathString(payload.cwd);
|
|
21829
21993
|
const workspaceRoot = Array.isArray(payload.workspace_roots) ? payload.workspace_roots.map(nonEmptyPathString).find(Boolean) : void 0;
|
|
21830
|
-
return
|
|
21994
|
+
return path65.resolve(nestedCwd ?? payloadCwd ?? workspaceRoot ?? fallbackCwd);
|
|
21831
21995
|
}
|
|
21832
21996
|
async function loadRuntimeContext(cwd) {
|
|
21833
21997
|
const repoRoot = findRepoRoot(cwd, codexLayout);
|