@guilz-dev/belay 0.4.0 → 0.7.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 +27 -2
- package/dist/adapters/claude/runtime-entry.js +54 -25
- package/dist/adapters/layouts/scope.js +3 -2
- package/dist/adapters/shared/gate-runtime.d.ts +8 -2
- package/dist/adapters/shared/gate-runtime.js +579 -181
- package/dist/bundle/claude-runtime.mjs +13574 -6334
- package/dist/bundle/codex-runtime.mjs +13108 -5903
- package/dist/bundle/cursor-runtime.mjs +12676 -5476
- package/dist/cli.js +80 -2
- package/dist/commands/approve.js +34 -10
- package/dist/commands/classify-for-report.js +1 -1
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +134 -11
- package/dist/commands/doctor.js +87 -1
- package/dist/commands/explain.js +11 -1
- package/dist/commands/judge.js +3 -2
- package/dist/commands/recovery-checkpoints.d.ts +164 -0
- package/dist/commands/recovery-checkpoints.js +347 -0
- package/dist/commands/revoke.js +19 -7
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.js +51 -0
- package/dist/commands/status.js +7 -0
- package/dist/config-io.js +91 -23
- package/dist/conformance/guarantee-posture.d.ts +21 -0
- package/dist/conformance/guarantee-posture.js +46 -0
- package/dist/conformance/guarantee-table.d.ts +1 -0
- package/dist/conformance/guarantee-table.js +20 -9
- package/dist/conformance/types.d.ts +1 -0
- package/dist/core/approval-replay.d.ts +1 -1
- package/dist/core/approval-replay.js +4 -1
- package/dist/core/approval-service.d.ts +6 -1
- package/dist/core/approval-service.js +87 -24
- package/dist/core/approval.d.ts +4 -0
- package/dist/core/approval.js +19 -5
- package/dist/core/audit-types.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.d.ts +32 -0
- package/dist/core/capability/approval-state-mutation.js +165 -0
- package/dist/core/capability/approval-v3.d.ts +32 -0
- package/dist/core/capability/approval-v3.js +114 -0
- package/dist/core/capability/attestation.d.ts +16 -0
- package/dist/core/capability/attestation.js +62 -0
- package/dist/core/capability/boundary-attestation-sign.d.ts +21 -0
- package/dist/core/capability/boundary-attestation-sign.js +84 -0
- package/dist/core/capability/boundary-driver-container.d.ts +19 -0
- package/dist/core/capability/boundary-driver-container.js +189 -0
- package/dist/core/capability/boundary-driver.d.ts +29 -0
- package/dist/core/capability/boundary-driver.js +44 -0
- package/dist/core/capability/boundary-egress.d.ts +21 -0
- package/dist/core/capability/boundary-egress.js +109 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +14 -0
- package/dist/core/capability/boundary-grant-materialize.js +93 -0
- package/dist/core/capability/boundary-profile.d.ts +15 -0
- package/dist/core/capability/boundary-profile.js +25 -0
- package/dist/core/capability/boundary-run.d.ts +48 -0
- package/dist/core/capability/boundary-run.js +51 -0
- package/dist/core/capability/boundary-session.d.ts +61 -0
- package/dist/core/capability/boundary-session.js +138 -0
- package/dist/core/capability/boundary-workspace-mount.d.ts +5 -0
- package/dist/core/capability/boundary-workspace-mount.js +68 -0
- package/dist/core/capability/capability-request-hash.d.ts +2 -0
- package/dist/core/capability/capability-request-hash.js +8 -0
- package/dist/core/capability/gate-latency-ratchet.d.ts +7 -0
- package/dist/core/capability/gate-latency-ratchet.js +18 -0
- package/dist/core/capability/gate-policy-shadow.d.ts +25 -0
- package/dist/core/capability/gate-policy-shadow.js +130 -0
- package/dist/core/capability/gate-shadow-audit.d.ts +1 -0
- package/dist/core/capability/gate-shadow-audit.js +1 -0
- package/dist/core/capability/gate-shadow-ratchet.d.ts +14 -0
- package/dist/core/capability/gate-shadow-ratchet.js +81 -0
- package/dist/core/capability/grant-consumption.d.ts +4 -0
- package/dist/core/capability/grant-consumption.js +11 -0
- package/dist/core/capability/grant-lease.d.ts +44 -0
- package/dist/core/capability/grant-lease.js +243 -0
- package/dist/core/capability/grant-loader.d.ts +12 -0
- package/dist/core/capability/grant-loader.js +23 -0
- package/dist/core/capability/grant-match.d.ts +6 -0
- package/dist/core/capability/grant-match.js +96 -0
- package/dist/core/capability/grant.d.ts +16 -0
- package/dist/core/capability/grant.js +16 -0
- package/dist/core/capability/index.d.ts +8 -0
- package/dist/core/capability/index.js +7 -0
- package/dist/core/capability/limits.d.ts +7 -0
- package/dist/core/capability/limits.js +55 -0
- package/dist/core/capability/policy-bridge.d.ts +18 -0
- package/dist/core/capability/policy-bridge.js +51 -0
- package/dist/core/capability/policy-engine.d.ts +77 -0
- package/dist/core/capability/policy-engine.js +663 -0
- package/dist/core/capability/policy-types.d.ts +20 -0
- package/dist/core/capability/policy-types.js +1 -0
- package/dist/core/capability/request.d.ts +48 -0
- package/dist/core/capability/request.js +1 -0
- package/dist/core/capability/resolver.d.ts +4 -0
- package/dist/core/capability/resolver.js +4 -0
- package/dist/core/classify-subagent.d.ts +2 -1
- package/dist/core/classify-subagent.js +38 -2
- package/dist/core/classify-tool.js +126 -62
- package/dist/core/config.d.ts +34 -3
- package/dist/core/config.js +118 -3
- package/dist/core/effect-ir/audit.d.ts +9 -0
- package/dist/core/effect-ir/audit.js +86 -0
- package/dist/core/effect-ir/build.d.ts +31 -0
- package/dist/core/effect-ir/build.js +277 -0
- package/dist/core/effect-ir/index.d.ts +8 -0
- package/dist/core/effect-ir/index.js +7 -0
- package/dist/core/effect-ir/normalize.d.ts +4 -0
- package/dist/core/effect-ir/normalize.js +104 -0
- package/dist/core/effect-ir/package-exec-eval.d.ts +8 -0
- package/dist/core/effect-ir/package-exec-eval.js +152 -0
- package/dist/core/effect-ir/package-exec.d.ts +29 -0
- package/dist/core/effect-ir/package-exec.js +292 -0
- package/dist/core/effect-ir/policy.d.ts +13 -0
- package/dist/core/effect-ir/policy.js +77 -0
- package/dist/core/effect-ir/types.d.ts +55 -0
- package/dist/core/effect-ir/types.js +1 -0
- package/dist/core/egress/allowlist.d.ts +1 -0
- package/dist/core/egress/allowlist.js +8 -0
- package/dist/core/egress-approval.js +45 -51
- package/dist/core/gate-contract.d.ts +5 -0
- package/dist/core/gate-contract.js +5 -0
- package/dist/core/gate-engine.js +30 -8
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +2 -1
- package/dist/core/judge-cloud-consent.js +29 -15
- package/dist/core/judge-doctor.js +39 -14
- package/dist/core/judge-fallback-hints.d.ts +13 -0
- package/dist/core/judge-fallback-hints.js +108 -0
- package/dist/core/network-endpoint.d.ts +10 -0
- package/dist/core/network-endpoint.js +51 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +5 -2
- package/dist/core/process-runner.d.ts +10 -0
- package/dist/core/process-runner.js +124 -0
- package/dist/core/recover-advice.js +2 -1
- package/dist/core/recovery/artifact-store.d.ts +29 -0
- package/dist/core/recovery/artifact-store.js +336 -0
- package/dist/core/recovery/capability.d.ts +3 -0
- package/dist/core/recovery/capability.js +22 -0
- package/dist/core/recovery/checkpoint.d.ts +25 -0
- package/dist/core/recovery/checkpoint.js +260 -0
- package/dist/core/recovery/fail-closed.d.ts +6 -0
- package/dist/core/recovery/fail-closed.js +25 -0
- package/dist/core/recovery/index.d.ts +5 -0
- package/dist/core/recovery/index.js +4 -0
- package/dist/core/recovery/operator-guidance.d.ts +7 -0
- package/dist/core/recovery/operator-guidance.js +39 -0
- package/dist/core/recovery/reconcile.d.ts +3 -0
- package/dist/core/recovery/reconcile.js +48 -0
- package/dist/core/recovery/resource-identity.d.ts +4 -0
- package/dist/core/recovery/resource-identity.js +34 -0
- package/dist/core/recovery/restore.d.ts +13 -0
- package/dist/core/recovery/restore.js +114 -0
- package/dist/core/recovery/snapshot-node.d.ts +27 -0
- package/dist/core/recovery/snapshot-node.js +261 -0
- package/dist/core/recovery/types.d.ts +104 -0
- package/dist/core/recovery/types.js +1 -0
- package/dist/core/standing-allow.js +3 -0
- package/dist/core/transactional/apply-observed-changes.d.ts +13 -0
- package/dist/core/transactional/apply-observed-changes.js +275 -0
- package/dist/core/transactional/backend-selector.d.ts +8 -0
- package/dist/core/transactional/backend-selector.js +118 -0
- package/dist/core/transactional/backend.d.ts +55 -0
- package/dist/core/transactional/backend.js +1 -0
- package/dist/core/transactional/diff-evaluator.js +4 -1
- package/dist/core/transactional/eligibility.js +4 -0
- package/dist/core/transactional/file-checkpoint-backend.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-backend.js +278 -0
- package/dist/core/transactional/file-checkpoint-git.d.ts +11 -0
- package/dist/core/transactional/file-checkpoint-git.js +217 -0
- package/dist/core/transactional/file-checkpoint-staging.d.ts +13 -0
- package/dist/core/transactional/file-checkpoint-staging.js +54 -0
- package/dist/core/transactional/file-clone.d.ts +15 -0
- package/dist/core/transactional/file-clone.js +139 -0
- package/dist/core/transactional/file-tree-path.d.ts +7 -0
- package/dist/core/transactional/file-tree-path.js +50 -0
- package/dist/core/transactional/file-tree.d.ts +41 -0
- package/dist/core/transactional/file-tree.js +186 -0
- package/dist/core/transactional/git-worktree-backend.d.ts +2 -0
- package/dist/core/transactional/git-worktree-backend.js +50 -0
- package/dist/core/transactional/git-worktree.d.ts +14 -9
- package/dist/core/transactional/git-worktree.js +62 -87
- package/dist/core/transactional/index.d.ts +11 -0
- package/dist/core/transactional/index.js +10 -0
- package/dist/core/transactional/reasons.d.ts +1 -0
- package/dist/core/transactional/reasons.js +5 -0
- package/dist/core/transactional/runner.js +171 -23
- package/dist/core/transactional/snapshot-node.d.ts +26 -0
- package/dist/core/transactional/snapshot-node.js +87 -0
- package/dist/core/transactional/types.d.ts +21 -1
- package/dist/core/types.d.ts +30 -3
- package/dist/core/verdict/adapter.d.ts +3 -4
- package/dist/core/verdict/adapter.js +24 -12
- package/dist/core/verdict/cursor-acp-client.d.ts +67 -0
- package/dist/core/verdict/cursor-acp-client.js +345 -0
- package/dist/core/verdict/egress-classify.d.ts +2 -2
- package/dist/core/verdict/egress-classify.js +2 -2
- package/dist/core/verdict/git-classifier.d.ts +26 -0
- package/dist/core/verdict/git-classifier.js +533 -0
- package/dist/core/verdict/judge-audit.js +1 -0
- package/dist/core/verdict/judge-baseline.d.ts +1 -1
- package/dist/core/verdict/judge-baseline.js +1 -0
- package/dist/core/verdict/judge-broker-service.js +12 -0
- package/dist/core/verdict/judge-catalog.js +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.d.ts +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.js +6 -0
- package/dist/core/verdict/judge-cli.js +5 -3
- package/dist/core/verdict/judge-provider-matrix.js +1 -1
- package/dist/core/verdict/judge-runtime-config.d.ts +2 -0
- package/dist/core/verdict/judge-runtime-config.js +10 -6
- package/dist/core/verdict/judge-session-broker.d.ts +1 -1
- package/dist/core/verdict/judge-session-broker.js +10 -5
- package/dist/core/verdict/judge-transport.d.ts +5 -1
- package/dist/core/verdict/judge-transport.js +24 -14
- package/dist/core/verdict/judge.d.ts +2 -15
- package/dist/core/verdict/judge.js +5 -68
- package/dist/core/verdict/launcher-resolve.d.ts +2 -0
- package/dist/core/verdict/launcher-resolve.js +14 -16
- package/dist/core/verdict/prescan.d.ts +15 -0
- package/dist/core/verdict/prescan.js +68 -0
- package/dist/core/verdict/shell-policy.d.ts +26 -0
- package/dist/core/verdict/shell-policy.js +40 -0
- package/dist/core/verdict/shell-semantics.d.ts +24 -0
- package/dist/core/verdict/shell-semantics.js +225 -0
- package/dist/core/verdict/types.d.ts +18 -1
- package/dist/core/verdict/verdict.js +313 -178
- package/dist/corpus/evaluate.js +3 -7
- package/dist/corpus/gate-latency-budget.d.ts +39 -0
- package/dist/corpus/gate-latency-budget.js +33 -0
- package/dist/corpus/mutators.d.ts +1 -1
- package/dist/corpus/mutators.js +11 -0
- package/dist/corpus/runtime-match.js +1 -3
- package/dist/corpus/standing-allow-catalog.generated.js +29 -9
- package/dist/judge-broker-daemon.js +15 -1
- package/dist/services/sandbox-service.d.ts +4 -0
- package/dist/services/sandbox-service.js +28 -2
- package/dist/types.d.ts +13 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/belay/SKILL.md +8 -3
- package/skills/belay/belay-approve.md +10 -7
package/dist/config-io.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { chmod,
|
|
3
|
+
import { chmod, mkdir, open, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { getAdapterLayout } from './adapters/layouts/index.js';
|
|
5
6
|
import { compactApprovals, isExpired, mergeApprovalStates } from './core/approval.js';
|
|
7
|
+
import { mutateApprovalStateWithRetry } from './core/capability/approval-state-mutation.js';
|
|
6
8
|
import { approvedApprovalsFile, belayStateDir, configuredControlPlaneDir, mergeConfig, pendingApprovalsFile, } from './core/config.js';
|
|
7
9
|
import { resolveLayeredConfig, teamConfigPath, } from './core/config-layers.js';
|
|
8
10
|
export function resolveAdapterName(config) {
|
|
@@ -49,6 +51,42 @@ const APPROVAL_STATE_FILES = ['pending-approvals.json', 'approved-approvals.json
|
|
|
49
51
|
function approvalFilesExist(dir) {
|
|
50
52
|
return APPROVAL_STATE_FILES.some((fileName) => existsSync(path.join(dir, fileName)));
|
|
51
53
|
}
|
|
54
|
+
async function isCorruptApprovalStateFile(filePath) {
|
|
55
|
+
const raw = await readFile(filePath, 'utf8');
|
|
56
|
+
try {
|
|
57
|
+
JSON.parse(raw);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return error instanceof SyntaxError;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function normalizeApprovalStateFile(parsed) {
|
|
65
|
+
const version = parsed.version === 3 ? 3 : parsed.version === 2 ? 2 : 1;
|
|
66
|
+
return {
|
|
67
|
+
version,
|
|
68
|
+
revision: typeof parsed.revision === 'number' ? parsed.revision : undefined,
|
|
69
|
+
approvals: Array.isArray(parsed.approvals) ? parsed.approvals : [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Gate reads: corrupt JSON is treated as empty state (fail-closed). */
|
|
73
|
+
async function readApprovalStateFileForGate(filePath) {
|
|
74
|
+
const raw = await readFile(filePath, 'utf8');
|
|
75
|
+
try {
|
|
76
|
+
return normalizeApprovalStateFile(JSON.parse(raw));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error instanceof SyntaxError) {
|
|
80
|
+
return { version: 1, approvals: [] };
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Migration reads: corrupt JSON must not be treated as empty. */
|
|
86
|
+
async function readApprovalStateFileStrict(filePath) {
|
|
87
|
+
const raw = await readFile(filePath, 'utf8');
|
|
88
|
+
return normalizeApprovalStateFile(JSON.parse(raw));
|
|
89
|
+
}
|
|
52
90
|
async function repoLocalApprovalsEmpty(repoRoot, config) {
|
|
53
91
|
const repoLocalDir = repoLocalStateDirFor(repoRoot, config);
|
|
54
92
|
if (!approvalFilesExist(repoLocalDir)) {
|
|
@@ -59,28 +97,43 @@ async function repoLocalApprovalsEmpty(repoRoot, config) {
|
|
|
59
97
|
if (!existsSync(filePath)) {
|
|
60
98
|
continue;
|
|
61
99
|
}
|
|
62
|
-
|
|
100
|
+
if (await isCorruptApprovalStateFile(filePath)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const state = await readApprovalStateFileForGate(filePath);
|
|
63
104
|
if (state.approvals.length > 0) {
|
|
64
105
|
return false;
|
|
65
106
|
}
|
|
66
107
|
}
|
|
67
108
|
return true;
|
|
68
109
|
}
|
|
69
|
-
async function readApprovalStateFile(filePath) {
|
|
70
|
-
const raw = await readFile(filePath, 'utf8');
|
|
71
|
-
const parsed = JSON.parse(raw);
|
|
72
|
-
return {
|
|
73
|
-
version: parsed.version === 2 ? 2 : 1,
|
|
74
|
-
approvals: Array.isArray(parsed.approvals) ? parsed.approvals : [],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
110
|
async function writeApprovalStateFile(filePath, state) {
|
|
78
|
-
|
|
79
|
-
await
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
const directory = path.dirname(filePath);
|
|
112
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
113
|
+
const tempPath = path.join(directory, `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
114
|
+
let handle = null;
|
|
115
|
+
try {
|
|
116
|
+
handle = await open(tempPath, 'wx', 0o600);
|
|
117
|
+
await handle.writeFile(`${JSON.stringify(compactApprovals(state), null, 2)}\n`, 'utf8');
|
|
118
|
+
await handle.sync();
|
|
119
|
+
await handle.close();
|
|
120
|
+
handle = null;
|
|
121
|
+
await rename(tempPath, filePath);
|
|
122
|
+
await chmod(filePath, 0o600);
|
|
123
|
+
const directoryHandle = await open(directory, 'r');
|
|
124
|
+
try {
|
|
125
|
+
await directoryHandle.sync().catch(() => undefined);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
await directoryHandle.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
if (handle) {
|
|
133
|
+
await handle.close().catch(() => undefined);
|
|
134
|
+
}
|
|
135
|
+
await unlink(tempPath).catch(() => undefined);
|
|
136
|
+
}
|
|
84
137
|
}
|
|
85
138
|
async function migrateApprovalFilesBetween(sourceDir, targetDir) {
|
|
86
139
|
await mkdir(targetDir, { recursive: true, mode: 0o700 });
|
|
@@ -90,14 +143,29 @@ async function migrateApprovalFilesBetween(sourceDir, targetDir) {
|
|
|
90
143
|
if (!existsSync(from)) {
|
|
91
144
|
continue;
|
|
92
145
|
}
|
|
93
|
-
if (
|
|
94
|
-
await
|
|
95
|
-
await chmod(to, 0o600);
|
|
146
|
+
if ((await isCorruptApprovalStateFile(from)) ||
|
|
147
|
+
(existsSync(to) && (await isCorruptApprovalStateFile(to)))) {
|
|
96
148
|
continue;
|
|
97
149
|
}
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
150
|
+
const sourceState = await readApprovalStateFileStrict(from);
|
|
151
|
+
const migrated = await mutateApprovalStateWithRetry({
|
|
152
|
+
load: async () => ({
|
|
153
|
+
filePath: to,
|
|
154
|
+
state: existsSync(to)
|
|
155
|
+
? await readApprovalStateFileStrict(to)
|
|
156
|
+
: { version: 1, approvals: [] },
|
|
157
|
+
}),
|
|
158
|
+
write: writeApprovalStateFile,
|
|
159
|
+
mutate: (targetState) => ({
|
|
160
|
+
state: targetState.approvals.length === 0
|
|
161
|
+
? sourceState
|
|
162
|
+
: mergeApprovalStates(targetState, sourceState),
|
|
163
|
+
result: true,
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
if (migrated !== true) {
|
|
167
|
+
throw new Error(`Failed to migrate approval state to ${to}`);
|
|
168
|
+
}
|
|
101
169
|
}
|
|
102
170
|
}
|
|
103
171
|
export async function migrateRepoLocalApprovalsToControlPlane(repoRoot, config) {
|
|
@@ -172,7 +240,7 @@ export async function loadApprovalState(repoRoot, fileName, config) {
|
|
|
172
240
|
if (!existsSync(filePath)) {
|
|
173
241
|
return { version: 1, approvals: [] };
|
|
174
242
|
}
|
|
175
|
-
return
|
|
243
|
+
return readApprovalStateFileForGate(filePath);
|
|
176
244
|
}
|
|
177
245
|
export async function saveApprovalState(repoRoot, fileName, state, config) {
|
|
178
246
|
const filePath = fileName === 'pending-approvals.json'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BoundaryAttestation } from '../core/capability/attestation.js';
|
|
2
|
+
import type { BelayConfigV4 } from '../core/config.js';
|
|
3
|
+
import type { LayerProfileId } from './types.js';
|
|
4
|
+
export interface GuaranteePosture {
|
|
5
|
+
configuredProfile: LayerProfileId;
|
|
6
|
+
attestedProfile: LayerProfileId;
|
|
7
|
+
attestationFresh: boolean;
|
|
8
|
+
l1FullConfigured: boolean;
|
|
9
|
+
l1FullAttested: boolean;
|
|
10
|
+
postureMismatch: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveConfiguredProfile(config: BelayConfigV4, egressProxyRunning: boolean): LayerProfileId;
|
|
13
|
+
export declare function resolveAttestedProfile(params: {
|
|
14
|
+
attestation: BoundaryAttestation | null;
|
|
15
|
+
l1FullConfigured: boolean;
|
|
16
|
+
}): LayerProfileId;
|
|
17
|
+
export declare function evaluateGuaranteePosture(params: {
|
|
18
|
+
config: BelayConfigV4;
|
|
19
|
+
attestation: BoundaryAttestation | null;
|
|
20
|
+
egressProxyRunning: boolean;
|
|
21
|
+
}): GuaranteePosture;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isAttestationFresh } from '../core/capability/attestation.js';
|
|
2
|
+
import { evaluateL1FullStatus } from '../core/capability/broker.js';
|
|
3
|
+
export function resolveConfiguredProfile(config, egressProxyRunning) {
|
|
4
|
+
const l1Full = evaluateL1FullStatus({ config, egressProxyRunning });
|
|
5
|
+
if (l1Full.active) {
|
|
6
|
+
return 'l1-full';
|
|
7
|
+
}
|
|
8
|
+
if (config.egress.enabled) {
|
|
9
|
+
return 'l1-partial-egress';
|
|
10
|
+
}
|
|
11
|
+
if (config.policy.transactional.enabled) {
|
|
12
|
+
return 'l1-l2-transactional';
|
|
13
|
+
}
|
|
14
|
+
return 'l3-l4-only';
|
|
15
|
+
}
|
|
16
|
+
export function resolveAttestedProfile(params) {
|
|
17
|
+
if (!params.attestation || !isAttestationFresh(params.attestation)) {
|
|
18
|
+
return 'l3-l4-only';
|
|
19
|
+
}
|
|
20
|
+
if (params.l1FullConfigured &&
|
|
21
|
+
params.attestation.deniesUngrantedEffects &&
|
|
22
|
+
params.attestation.materializesGrants) {
|
|
23
|
+
return 'l1-full';
|
|
24
|
+
}
|
|
25
|
+
return 'l3-l4-only';
|
|
26
|
+
}
|
|
27
|
+
export function evaluateGuaranteePosture(params) {
|
|
28
|
+
const l1Full = evaluateL1FullStatus({
|
|
29
|
+
config: params.config,
|
|
30
|
+
egressProxyRunning: params.egressProxyRunning,
|
|
31
|
+
});
|
|
32
|
+
const configuredProfile = resolveConfiguredProfile(params.config, params.egressProxyRunning);
|
|
33
|
+
const attestedProfile = resolveAttestedProfile({
|
|
34
|
+
attestation: params.attestation,
|
|
35
|
+
l1FullConfigured: l1Full.active,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
configuredProfile,
|
|
39
|
+
attestedProfile,
|
|
40
|
+
attestationFresh: params.attestation ? isAttestationFresh(params.attestation) : false,
|
|
41
|
+
l1FullConfigured: l1Full.active,
|
|
42
|
+
l1FullAttested: attestedProfile === 'l1-full' &&
|
|
43
|
+
Boolean(params.attestation?.deniesUngrantedEffects && params.attestation.materializesGrants),
|
|
44
|
+
postureMismatch: configuredProfile === 'l1-full' && attestedProfile !== 'l1-full',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LayerConformanceScenario, LayerProfileId } from './types.js';
|
|
2
|
+
export { evaluateGuaranteePosture, type GuaranteePosture } from './guarantee-posture.js';
|
|
2
3
|
export type { LayerConformanceScenario, LayerProfileId } from './types.js';
|
|
3
4
|
export interface GuaranteeTableRow {
|
|
4
5
|
profile: LayerProfileId;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { evaluateGuaranteePosture } from './guarantee-posture.js';
|
|
1
2
|
/** Normative rows — keep in sync with docs/guarantee-table.md */
|
|
2
3
|
export const GUARANTEE_TABLE_ROWS = [
|
|
3
4
|
{
|
|
@@ -33,9 +34,10 @@ export const GUARANTEE_SCENARIOS = {
|
|
|
33
34
|
permission: 'allow',
|
|
34
35
|
},
|
|
35
36
|
{
|
|
36
|
-
id: 'l3-
|
|
37
|
+
id: 'l3-deny-read-egress',
|
|
37
38
|
command: 'curl https://example.com',
|
|
38
|
-
permission: '
|
|
39
|
+
permission: 'deny',
|
|
40
|
+
reason: 'external_effect',
|
|
39
41
|
},
|
|
40
42
|
],
|
|
41
43
|
'l1-partial-egress': [
|
|
@@ -45,9 +47,10 @@ export const GUARANTEE_SCENARIOS = {
|
|
|
45
47
|
permission: 'allow',
|
|
46
48
|
},
|
|
47
49
|
{
|
|
48
|
-
id: 'l1p-
|
|
50
|
+
id: 'l1p-deny-read-egress',
|
|
49
51
|
command: 'curl https://example.com',
|
|
50
|
-
permission: '
|
|
52
|
+
permission: 'deny',
|
|
53
|
+
reason: 'external_effect',
|
|
51
54
|
},
|
|
52
55
|
{
|
|
53
56
|
id: 'l1p-deny-write-egress',
|
|
@@ -63,9 +66,16 @@ export const GUARANTEE_SCENARIOS = {
|
|
|
63
66
|
permission: 'allow',
|
|
64
67
|
},
|
|
65
68
|
{
|
|
66
|
-
id: 'l2-allow-
|
|
67
|
-
command: '
|
|
69
|
+
id: 'l2-allow-dirty-git-file-checkpoint',
|
|
70
|
+
command: 'touch notes.txt',
|
|
68
71
|
permission: 'allow',
|
|
72
|
+
substrate: 'dirty_git_file_checkpoint',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'l2-deny-read-egress',
|
|
76
|
+
command: 'curl https://example.com',
|
|
77
|
+
permission: 'deny',
|
|
78
|
+
reason: 'external_effect',
|
|
69
79
|
},
|
|
70
80
|
],
|
|
71
81
|
'l1-full': [
|
|
@@ -75,9 +85,10 @@ export const GUARANTEE_SCENARIOS = {
|
|
|
75
85
|
permission: 'allow',
|
|
76
86
|
},
|
|
77
87
|
{
|
|
78
|
-
id: 'l1f-
|
|
88
|
+
id: 'l1f-deny-read-egress',
|
|
79
89
|
command: 'curl https://example.com',
|
|
80
|
-
permission: '
|
|
90
|
+
permission: 'deny',
|
|
91
|
+
reason: 'external_effect',
|
|
81
92
|
},
|
|
82
93
|
{
|
|
83
94
|
id: 'l1f-deny-write-egress',
|
|
@@ -90,7 +101,7 @@ export const GUARANTEE_SCENARIOS = {
|
|
|
90
101
|
id: 'l1f-deny-outside-repo',
|
|
91
102
|
command: 'echo hi > ../../outside.txt',
|
|
92
103
|
permission: 'deny',
|
|
93
|
-
reason: '
|
|
104
|
+
reason: 'outside_repo_mutation',
|
|
94
105
|
},
|
|
95
106
|
{
|
|
96
107
|
id: 'l1f-deny-outside-repo-write',
|
|
@@ -40,6 +40,6 @@ export declare function buildReplayEnvelopeFields(input: ReplayEnvelopeInput): P
|
|
|
40
40
|
export declare function hasReplayEnvelope(approval: ApprovalRecord): boolean;
|
|
41
41
|
export declare function validateReplayEnvelope(approval: ApprovalRecord, action: ReplayActionContext): boolean;
|
|
42
42
|
export declare function canAutoReplay(config: BelayConfigV4, kind: ApprovalRecord['kind'], adapter?: ReplayAdapterId): boolean;
|
|
43
|
-
export declare function buildRetryInstructionForConfig(config: BelayConfigV4, tokenPrefix: string, approvalId: string): string;
|
|
43
|
+
export declare function buildRetryInstructionForConfig(config: BelayConfigV4, tokenPrefix: string, approvalId: string, kind?: ApprovalRecord['kind'], adapter?: ReplayAdapterId): string;
|
|
44
44
|
export declare function buildApprovalRecordedMessage(config: BelayConfigV4, approval: ApprovalRecord, adapter?: ReplayAdapterId): string;
|
|
45
45
|
export declare function buildReplayHint(config: BelayConfigV4, approval: ApprovalRecord, adapter?: ReplayAdapterId): ApprovalReplayHint | null;
|
|
@@ -103,7 +103,10 @@ export function canAutoReplay(config, kind, adapter) {
|
|
|
103
103
|
}
|
|
104
104
|
return kind === 'shell' && scopes?.shell !== false;
|
|
105
105
|
}
|
|
106
|
-
export function buildRetryInstructionForConfig(config, tokenPrefix, approvalId) {
|
|
106
|
+
export function buildRetryInstructionForConfig(config, tokenPrefix, approvalId, kind, adapter) {
|
|
107
|
+
if (kind === 'shell' && canAutoReplay(config, kind, adapter)) {
|
|
108
|
+
return `To approve and run this exact shell action once, send ${tokenPrefix} ${approvalId}. No follow-up prompt is required.`;
|
|
109
|
+
}
|
|
107
110
|
if (approvalFlow(config) === 'one_step') {
|
|
108
111
|
return `To allow this action once, send ${tokenPrefix} ${approvalId}. After approval, retry the same action unchanged.`;
|
|
109
112
|
}
|
|
@@ -26,11 +26,16 @@ export declare function recordApproval(params: {
|
|
|
26
26
|
message: string;
|
|
27
27
|
approval?: ApprovalStateFile['approvals'][number];
|
|
28
28
|
}>;
|
|
29
|
-
/**
|
|
29
|
+
/** @deprecated Replay callers must claim before execution with `claimApprovedForReplay`. */
|
|
30
30
|
export declare function consumeApprovedAfterCliReplay(params: {
|
|
31
31
|
approvalId: string;
|
|
32
32
|
store: ApprovalStore;
|
|
33
33
|
}): Promise<void>;
|
|
34
|
+
/** Atomically spend and return a one-shot approval before executing its replay. */
|
|
35
|
+
export declare function claimApprovedForReplay(params: {
|
|
36
|
+
approvalId: string;
|
|
37
|
+
store: ApprovalStore;
|
|
38
|
+
}): Promise<ApprovalStateFile['approvals'][number] | null>;
|
|
34
39
|
export declare function createGateApprovalStore(repoRoot: string, config: BelayConfigV3): ApprovalStore;
|
|
35
40
|
export declare function gateApprovalStoreFromDeps(deps: {
|
|
36
41
|
loadApprovals: (fileName: 'pending-approvals.json' | 'approved-approvals.json') => Promise<{
|
|
@@ -2,17 +2,25 @@ import { approvedApprovalsPath, loadApprovalState, pendingApprovalsPath, saveApp
|
|
|
2
2
|
import { compactApprovals } from './approval.js';
|
|
3
3
|
import { buildApprovalRecordedMessage } from './approval-replay.js';
|
|
4
4
|
import { verifyApprovalToken } from './approval-token.js';
|
|
5
|
+
import { mutateApprovalStateWithRetry, mutatePendingAndApprovedWithRetry, } from './capability/approval-state-mutation.js';
|
|
6
|
+
import { APPROVAL_STATE_VERSION_V3, mintGrantForApprovedRecord } from './capability/approval-v3.js';
|
|
5
7
|
import { configuredControlPlaneDir } from './config.js';
|
|
6
8
|
export async function recordApproval(params) {
|
|
7
9
|
const { approvalId, config, store, token, requireSignedToken = false, adapter } = params;
|
|
8
|
-
const pending = await store.loadPending();
|
|
10
|
+
const [pending, approved] = await Promise.all([store.loadPending(), store.loadApproved()]);
|
|
9
11
|
pending.state = compactApprovals(pending.state);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
approved.state = compactApprovals(approved.state);
|
|
13
|
+
const pendingApproval = pending.state.approvals.find((entry) => entry.approvalId === approvalId);
|
|
14
|
+
const approvedApproval = approved.state.approvals.find((entry) => entry.approvalId === approvalId);
|
|
15
|
+
const approval = pendingApproval ?? approvedApproval;
|
|
16
|
+
if (!approval) {
|
|
17
|
+
await mutateApprovalStateWithRetry({
|
|
18
|
+
load: store.loadPending,
|
|
19
|
+
write: store.writePending,
|
|
20
|
+
mutate: (state) => ({ state: compactApprovals(state), result: true }),
|
|
21
|
+
});
|
|
13
22
|
return { ok: false, message: 'Belay approval not found or expired.' };
|
|
14
23
|
}
|
|
15
|
-
const [approval] = pending.state.approvals.slice(index, index + 1);
|
|
16
24
|
if (requireSignedToken) {
|
|
17
25
|
if (!token) {
|
|
18
26
|
return { ok: false, message: 'Signed approval token required for out-of-band approval.' };
|
|
@@ -26,31 +34,86 @@ export async function recordApproval(params) {
|
|
|
26
34
|
return { ok: false, message: 'Signed approval token does not match the pending approval.' };
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if (!pendingApproval && approvedApproval) {
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
message: buildApprovalRecordedMessage(config, approvedApproval, adapter),
|
|
41
|
+
approval: approvedApproval,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const recorded = await mutatePendingAndApprovedWithRetry({
|
|
45
|
+
loadPending: store.loadPending,
|
|
46
|
+
loadApproved: store.loadApproved,
|
|
47
|
+
writePending: store.writePending,
|
|
48
|
+
writeApproved: store.writeApproved,
|
|
49
|
+
mutate: (pendingState, approvedState) => {
|
|
50
|
+
const compactedPending = compactApprovals(pendingState);
|
|
51
|
+
const compactedApproved = compactApprovals(approvedState);
|
|
52
|
+
const existing = compactedApproved.approvals.find((entry) => entry.approvalId === approvalId);
|
|
53
|
+
if (existing &&
|
|
54
|
+
(existing.fingerprint !== approval.fingerprint || existing.repoRoot !== approval.repoRoot)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const pendingIndex = compactedPending.approvals.findIndex((entry) => entry.approvalId === approvalId &&
|
|
58
|
+
entry.fingerprint === approval.fingerprint &&
|
|
59
|
+
entry.repoRoot === approval.repoRoot);
|
|
60
|
+
if (pendingIndex === -1) {
|
|
61
|
+
return existing
|
|
62
|
+
? {
|
|
63
|
+
pending: compactedPending,
|
|
64
|
+
approved: compactedApproved,
|
|
65
|
+
result: existing,
|
|
66
|
+
}
|
|
67
|
+
: null;
|
|
68
|
+
}
|
|
69
|
+
const [claimed] = compactedPending.approvals.splice(pendingIndex, 1);
|
|
70
|
+
if (!claimed) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const approvedRecord = existing ??
|
|
74
|
+
mintGrantForApprovedRecord({
|
|
75
|
+
...claimed,
|
|
76
|
+
approvedAt: new Date().toISOString(),
|
|
77
|
+
});
|
|
78
|
+
if (!existing) {
|
|
79
|
+
compactedApproved.version = APPROVAL_STATE_VERSION_V3;
|
|
80
|
+
compactedApproved.approvals.push(approvedRecord);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
pending: compactedPending,
|
|
84
|
+
approved: compactedApproved,
|
|
85
|
+
result: approvedRecord,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
36
88
|
});
|
|
37
|
-
|
|
89
|
+
if (!recorded) {
|
|
90
|
+
return { ok: false, message: 'Belay approval not found, expired, or already claimed.' };
|
|
91
|
+
}
|
|
38
92
|
return {
|
|
39
93
|
ok: true,
|
|
40
|
-
message: buildApprovalRecordedMessage(config,
|
|
41
|
-
approval,
|
|
94
|
+
message: buildApprovalRecordedMessage(config, recorded, adapter),
|
|
95
|
+
approval: recorded,
|
|
42
96
|
};
|
|
43
97
|
}
|
|
44
|
-
/**
|
|
98
|
+
/** @deprecated Replay callers must claim before execution with `claimApprovedForReplay`. */
|
|
45
99
|
export async function consumeApprovedAfterCliReplay(params) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
await claimApprovedForReplay(params);
|
|
101
|
+
}
|
|
102
|
+
/** Atomically spend and return a one-shot approval before executing its replay. */
|
|
103
|
+
export async function claimApprovedForReplay(params) {
|
|
104
|
+
return mutateApprovalStateWithRetry({
|
|
105
|
+
load: params.store.loadApproved,
|
|
106
|
+
write: params.store.writeApproved,
|
|
107
|
+
mutate: (state) => {
|
|
108
|
+
const compacted = compactApprovals(state);
|
|
109
|
+
const index = compacted.approvals.findIndex((approval) => approval.approvalId === params.approvalId);
|
|
110
|
+
if (index === -1) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const [claimed] = compacted.approvals.splice(index, 1);
|
|
114
|
+
return { state: compacted, result: claimed ?? null };
|
|
115
|
+
},
|
|
116
|
+
});
|
|
54
117
|
}
|
|
55
118
|
export function createGateApprovalStore(repoRoot, config) {
|
|
56
119
|
return {
|
package/dist/core/approval.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export declare function createApprovalRecordWithEnvelope(params: {
|
|
|
24
24
|
payload?: Record<string, unknown>;
|
|
25
25
|
};
|
|
26
26
|
scopeHint?: ApprovalRecord['scopeHint'];
|
|
27
|
+
capabilityRequests?: ApprovalRecord['capabilityRequests'];
|
|
28
|
+
effectPlanHash?: string;
|
|
27
29
|
}): ApprovalRecord;
|
|
28
30
|
export declare function createApprovalRecord(params: {
|
|
29
31
|
kind: ApprovalRecord['kind'];
|
|
@@ -40,4 +42,6 @@ export declare function createApprovalRecord(params: {
|
|
|
40
42
|
payloadHash?: string;
|
|
41
43
|
payloadJson?: string;
|
|
42
44
|
scopeHint?: ApprovalRecord['scopeHint'];
|
|
45
|
+
capabilityRequests?: ApprovalRecord['capabilityRequests'];
|
|
46
|
+
effectPlanHash?: string;
|
|
43
47
|
}): ApprovalRecord;
|
package/dist/core/approval.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildReplayEnvelopeFields } from './approval-replay.js';
|
|
2
|
+
import { attachCapabilityEnvelope } from './capability/approval-v3.js';
|
|
2
3
|
import { DEFAULT_APPROVAL_CONFIG } from './config.js';
|
|
3
4
|
/** @deprecated Use `DEFAULT_APPROVAL_CONFIG.executionLeaseMs` */
|
|
4
5
|
export const APPROVAL_EXECUTION_LEASE_MS = DEFAULT_APPROVAL_CONFIG.executionLeaseMs;
|
|
@@ -6,19 +7,25 @@ export function nowIso() {
|
|
|
6
7
|
return new Date().toISOString();
|
|
7
8
|
}
|
|
8
9
|
export function isExpired(approval) {
|
|
9
|
-
|
|
10
|
+
const expiresAt = Date.parse(approval.expiresAt);
|
|
11
|
+
return !Number.isFinite(expiresAt) || expiresAt <= Date.now();
|
|
10
12
|
}
|
|
11
13
|
export function isExecutionLeaseExpired(approval) {
|
|
12
14
|
if (!approval.executionLeaseExpiresAt) {
|
|
13
15
|
return false;
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
const expiresAt = Date.parse(approval.executionLeaseExpiresAt);
|
|
18
|
+
return !Number.isFinite(expiresAt) || expiresAt <= Date.now();
|
|
16
19
|
}
|
|
17
20
|
export function compactApprovals(state) {
|
|
18
|
-
|
|
21
|
+
const compacted = {
|
|
19
22
|
version: state.version,
|
|
20
23
|
approvals: state.approvals.filter((approval) => !isExpired(approval) && !isExecutionLeaseExpired(approval)),
|
|
21
24
|
};
|
|
25
|
+
if (state.revision !== undefined) {
|
|
26
|
+
compacted.revision = state.revision;
|
|
27
|
+
}
|
|
28
|
+
return compacted;
|
|
22
29
|
}
|
|
23
30
|
export function mergeApprovalStates(target, source) {
|
|
24
31
|
const byId = new Map();
|
|
@@ -31,7 +38,12 @@ export function mergeApprovalStates(target, source) {
|
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
return compactApprovals({
|
|
34
|
-
version: target.version ===
|
|
41
|
+
version: target.version === 3 || source.version === 3
|
|
42
|
+
? 3
|
|
43
|
+
: target.version === 2 || source.version === 2
|
|
44
|
+
? 2
|
|
45
|
+
: 1,
|
|
46
|
+
revision: Math.max(target.revision ?? 0, source.revision ?? 0),
|
|
35
47
|
approvals: [...byId.values()],
|
|
36
48
|
});
|
|
37
49
|
}
|
|
@@ -78,6 +90,8 @@ export function createApprovalRecordWithEnvelope(params) {
|
|
|
78
90
|
payloadHash: envelope.payloadHash,
|
|
79
91
|
payloadJson: envelope.payloadJson,
|
|
80
92
|
scopeHint: params.scopeHint,
|
|
93
|
+
capabilityRequests: params.capabilityRequests,
|
|
94
|
+
effectPlanHash: params.effectPlanHash,
|
|
81
95
|
});
|
|
82
96
|
}
|
|
83
97
|
export function createApprovalRecord(params) {
|
|
@@ -112,5 +126,5 @@ export function createApprovalRecord(params) {
|
|
|
112
126
|
if (params.scopeHint) {
|
|
113
127
|
record.scopeHint = params.scopeHint;
|
|
114
128
|
}
|
|
115
|
-
return record;
|
|
129
|
+
return attachCapabilityEnvelope(record, params.capabilityRequests, params.effectPlanHash);
|
|
116
130
|
}
|
|
@@ -20,10 +20,23 @@ export interface AuditRecord {
|
|
|
20
20
|
predictedAssessment?: Assessment;
|
|
21
21
|
observedAssessment?: Assessment;
|
|
22
22
|
transactional?: boolean;
|
|
23
|
+
transactionalBackend?: 'git_worktree' | 'file_checkpoint';
|
|
24
|
+
resourceKind?: 'git_repository' | 'directory';
|
|
25
|
+
baselineTreeHash?: string;
|
|
26
|
+
snapshotFileCount?: number;
|
|
27
|
+
snapshotSourceBytes?: number;
|
|
28
|
+
snapshotWorkspaceBytes?: number;
|
|
29
|
+
snapshotCopyStrategy?: 'clonefile' | 'reflink' | 'copy';
|
|
30
|
+
snapshotPrepareMs?: number;
|
|
23
31
|
transactionalReason?: string;
|
|
24
32
|
transactionalCategories?: string[];
|
|
25
33
|
transactionalChangeCount?: number;
|
|
26
34
|
transactionalSkipReason?: string;
|
|
35
|
+
transactionalSkipDetail?: string;
|
|
36
|
+
recoveryCheckpointId?: string;
|
|
37
|
+
recoveryBackend?: string;
|
|
38
|
+
recoveryProofHash?: string;
|
|
39
|
+
recoveryState?: string;
|
|
27
40
|
replayContext?: AuditReplayContext;
|
|
28
41
|
actionSnapshot?: AuditActionSnapshot;
|
|
29
42
|
[key: string]: unknown;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ApprovalStateFile } from '../types.js';
|
|
2
|
+
export declare function withStateFileLock<T>(filePath: string, operation: () => Promise<T>, maxRetries?: number): Promise<T>;
|
|
3
|
+
export declare function mutateApprovalStateWithRetry<T>(params: {
|
|
4
|
+
load: () => Promise<{
|
|
5
|
+
filePath: string;
|
|
6
|
+
state: ApprovalStateFile;
|
|
7
|
+
}>;
|
|
8
|
+
write: (filePath: string, state: ApprovalStateFile) => Promise<void>;
|
|
9
|
+
mutate: (state: ApprovalStateFile) => {
|
|
10
|
+
state: ApprovalStateFile;
|
|
11
|
+
result: T;
|
|
12
|
+
} | null;
|
|
13
|
+
maxRetries?: number;
|
|
14
|
+
}): Promise<T | null>;
|
|
15
|
+
export declare function mutatePendingAndApprovedWithRetry<T>(params: {
|
|
16
|
+
loadPending: () => Promise<{
|
|
17
|
+
filePath: string;
|
|
18
|
+
state: ApprovalStateFile;
|
|
19
|
+
}>;
|
|
20
|
+
loadApproved: () => Promise<{
|
|
21
|
+
filePath: string;
|
|
22
|
+
state: ApprovalStateFile;
|
|
23
|
+
}>;
|
|
24
|
+
writePending: (filePath: string, state: ApprovalStateFile) => Promise<void>;
|
|
25
|
+
writeApproved: (filePath: string, state: ApprovalStateFile) => Promise<void>;
|
|
26
|
+
mutate: (pending: ApprovalStateFile, approved: ApprovalStateFile) => {
|
|
27
|
+
pending: ApprovalStateFile;
|
|
28
|
+
approved: ApprovalStateFile;
|
|
29
|
+
result: T;
|
|
30
|
+
} | null;
|
|
31
|
+
maxRetries?: number;
|
|
32
|
+
}): Promise<T | null>;
|