@astrosheep/keiyaku 4.6.4 → 4.6.6
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/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +43 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +7 -8
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
- package/build/src/akuma/akuma-observe.d.ts +18 -15
- package/build/src/akuma/akuma-observe.js +75 -16
- package/build/src/akuma/akuma-wait.d.ts +4 -3
- package/build/src/akuma/akuma-wait.js +75 -53
- package/build/src/akuma/akuma.d.ts +6 -4
- package/build/src/akuma/akuma.js +4 -0
- package/build/src/akuma/call-initial-tell.d.ts +2 -2
- package/build/src/akuma/call-initial-tell.js +1 -1
- package/build/src/akuma/call-request.d.ts +2 -2
- package/build/src/akuma/heart/index.d.ts +7 -2
- package/build/src/akuma/heart/index.js +50 -5
- package/build/src/akuma/heart/tells.d.ts +33 -2
- package/build/src/akuma/heart/tells.js +24 -18
- package/build/src/akuma/heart/timeline.d.ts +23 -1
- package/build/src/akuma/heart/timeline.js +28 -21
- package/build/src/akuma/projection.d.ts +10 -10
- package/build/src/akuma/providers/pi/index.js +72 -39
- package/build/src/akuma/selection-observation.d.ts +20 -20
- package/build/src/akuma-body.js +1 -0
- package/build/src/alias/index.d.ts +0 -1
- package/build/src/alias/index.js +0 -4
- package/build/src/body/amend.d.ts +0 -4
- package/build/src/body/amend.js +0 -3
- package/build/src/body/region.d.ts +0 -1
- package/build/src/body/region.js +0 -3
- package/build/src/body/verification.d.ts +0 -1
- package/build/src/cli/commands/akuma-grammar.js +23 -66
- package/build/src/cli/commands/akuma.js +17 -30
- package/build/src/cli/commands/contract-grammar.d.ts +14 -1
- package/build/src/cli/commands/contract-grammar.js +54 -21
- package/build/src/cli/commands/contract-help.d.ts +9 -8
- package/build/src/cli/commands/contract-help.js +14 -7
- package/build/src/cli/commands/contract.js +57 -117
- package/build/src/cli/commands/install.d.ts +5 -5
- package/build/src/cli/commands/install.js +1 -4
- package/build/src/cli/commands/task-grammar.js +10 -66
- package/build/src/cli/commands/task.d.ts +0 -1
- package/build/src/cli/commands/task.js +34 -53
- package/build/src/cli/coordinates.d.ts +1 -2
- package/build/src/cli/coordinates.js +1 -4
- package/build/src/cli/parse.d.ts +2 -3
- package/build/src/cli/parse.js +24 -66
- package/build/src/cli/render/akuma.d.ts +3 -16
- package/build/src/cli/render/akuma.js +1 -13
- package/build/src/cli/render/audit.js +1 -1
- package/build/src/cli/render/contract.js +34 -32
- package/build/src/cli/render/kanshi-akuma.d.ts +8 -2
- package/build/src/cli/render/kanshi-akuma.js +14 -7
- package/build/src/cli/render/kanshi.d.ts +3 -1
- package/build/src/cli/render/kanshi.js +44 -32
- package/build/src/cli/render/receipt.js +6 -1
- package/build/src/cli/render/task.d.ts +1 -1
- package/build/src/cli/render/task.js +3 -27
- package/build/src/cli/render/terminal.d.ts +6 -2
- package/build/src/cli/render/terminal.js +8 -2
- package/build/src/cli/runtime.js +3 -16
- package/build/src/cli/scan.d.ts +26 -0
- package/build/src/cli/scan.js +82 -0
- package/build/src/cli/streams.d.ts +5 -0
- package/build/src/cli/streams.js +11 -0
- package/build/src/contract-worktree.js +38 -21
- package/build/src/core/facts/codec.d.ts +1 -2
- package/build/src/core/facts/codec.js +5 -11
- package/build/src/core/facts/types.d.ts +16 -2
- package/build/src/core/facts/types.js +15 -0
- package/build/src/core/verbs/abandon.js +3 -9
- package/build/src/core/verbs/amend.js +2 -9
- package/build/src/core/verbs/arc.js +6 -13
- package/build/src/core/verbs/attestation.js +2 -9
- package/build/src/core/verbs/bind.js +2 -9
- package/build/src/core/verbs/deliver.js +2 -9
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +2 -9
- package/build/src/core/verbs/reintegrate.d.ts +1 -1
- package/build/src/core/verbs/reintegrate.js +2 -9
- package/build/src/dispatch/index.d.ts +2 -2
- package/build/src/dispatch/index.js +33 -11
- package/build/src/git/hooks.d.ts +2 -4
- package/build/src/git/hooks.js +0 -12
- package/build/src/git/integration.d.ts +5 -3
- package/build/src/git/integration.js +36 -25
- package/build/src/git/nuke.js +4 -4
- package/build/src/git/observe.d.ts +4 -4
- package/build/src/git/observe.js +5 -5
- package/build/src/git/process.d.ts +1 -4
- package/build/src/git/process.js +1 -11
- package/build/src/git/read-observation.js +1 -1
- package/build/src/git/reconcile.d.ts +1 -1
- package/build/src/git/reconcile.js +15 -13
- package/build/src/git/repository.d.ts +2 -6
- package/build/src/git/repository.js +18 -31
- package/build/src/git/scratch.d.ts +6 -6
- package/build/src/git/scratch.js +110 -46
- package/build/src/git/target-placement.d.ts +5 -5
- package/build/src/git/target-placement.js +18 -33
- package/build/src/git/tender.d.ts +6 -2
- package/build/src/git/tender.js +19 -12
- package/build/src/git/terminal-reconcile.js +24 -13
- package/build/src/git/terminal-seal.d.ts +2 -5
- package/build/src/git/terminal-seal.js +5 -3
- package/build/src/git/workspace.d.ts +17 -13
- package/build/src/git/workspace.js +58 -41
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +8 -1
- package/build/src/kanshi/read.js +72 -7
- package/build/src/kanshi/report.d.ts +12 -0
- package/build/src/library/akuma-creation.d.ts +4 -4
- package/build/src/library/bind.d.ts +1 -63
- package/build/src/library/bind.js +3 -9
- package/build/src/library/contract-creation.js +2 -30
- package/build/src/library/contract-execution.d.ts +0 -1
- package/build/src/library/contract-execution.js +9 -34
- package/build/src/library/contract-operations.d.ts +5 -1
- package/build/src/library/contract-operations.js +7 -3
- package/build/src/library/contract-types.d.ts +1 -1
- package/build/src/library/delivery.d.ts +91 -93
- package/build/src/library/delivery.js +2 -1
- package/build/src/library/input.d.ts +0 -2
- package/build/src/library/input.js +0 -4
- package/build/src/library/invocation.d.ts +0 -1
- package/build/src/library/invocation.js +3 -25
- package/build/src/library/outcome.d.ts +651 -602
- package/build/src/library/outcome.js +4 -4
- package/build/src/library/reconcile.d.ts +6 -0
- package/build/src/library/reconcile.js +31 -1
- package/build/src/library/repo.d.ts +0 -1
- package/build/src/library/selection.d.ts +1 -10
- package/build/src/library/selection.js +14 -15
- package/build/src/protocol/abandon.d.ts +1 -1
- package/build/src/protocol/abandon.js +3 -10
- package/build/src/protocol/amend.js +2 -5
- package/build/src/protocol/arc.d.ts +1 -1
- package/build/src/protocol/arc.js +2 -9
- package/build/src/protocol/attempt.d.ts +3 -20
- package/build/src/protocol/attempt.js +6 -5
- package/build/src/protocol/audit.d.ts +1 -0
- package/build/src/protocol/audit.js +10 -11
- package/build/src/protocol/completion.d.ts +129 -55
- package/build/src/protocol/completion.js +62 -73
- package/build/src/protocol/deliver.d.ts +7 -5
- package/build/src/protocol/deliver.js +74 -44
- package/build/src/protocol/execution-observation.d.ts +2 -4
- package/build/src/protocol/execution-observation.js +3 -7
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -0
- package/build/src/protocol/operations.d.ts +24 -12
- package/build/src/protocol/operations.js +36 -15
- package/build/src/protocol/placement.d.ts +2 -1
- package/build/src/protocol/placement.js +21 -5
- package/build/src/protocol/progress.d.ts +1 -1
- package/build/src/protocol/read/status.js +58 -41
- package/build/src/protocol/reintegrate.d.ts +1 -1
- package/build/src/protocol/reintegrate.js +10 -19
- package/build/src/protocol/review.d.ts +3 -28
- package/build/src/protocol/review.js +10 -37
- package/build/src/protocol/run.d.ts +19 -7
- package/build/src/protocol/run.js +19 -9
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/holder.d.ts +2 -4
- package/build/src/settlement/holder.js +3 -6
- package/build/src/task/board.d.ts +6 -0
- package/build/src/task/board.js +30 -1
- package/build/src/task/catalog.js +1 -4
- package/build/src/task/compose.d.ts +0 -1
- package/build/src/task/compose.js +0 -1
- package/build/src/task/context.d.ts +0 -1
- package/build/src/task/context.js +0 -3
- package/build/src/task/created-observation.d.ts +5 -1
- package/build/src/task/created-observation.js +12 -5
- package/build/src/task/index.d.ts +1 -1
- package/build/src/task/index.js +7 -6
- package/build/src/task/input.d.ts +0 -6
- package/build/src/task/input.js +2 -14
- package/build/src/task/mutation.d.ts +3 -4
- package/build/src/task/mutation.js +11 -39
- package/build/src/task/operations.d.ts +2 -7
- package/build/src/task/operations.js +37 -49
- package/build/src/task/query.d.ts +0 -1
- package/build/src/task/query.js +1 -4
- package/build/src/task/store.d.ts +0 -2
- package/build/src/task/store.js +27 -11
- package/build/src/verification/declaration.js +3 -2
- package/build/src/verification/execution.d.ts +3 -2
- package/build/src/verification/execution.js +10 -6
- package/build/src/workspace-place.d.ts +0 -1
- package/build/src/workspace-place.js +0 -11
- package/package.json +7 -3
package/build/src/git/scratch.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { lstat, realpath, unlink } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { basename, dirname, join } from "node:path";
|
|
6
|
-
import { tryAcquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
|
|
7
|
-
import { gitObjectIdForSnapshot } from "./identity.js";
|
|
8
|
-
import {
|
|
6
|
+
import { acquireSqliteTransactionLock, tryAcquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
|
|
7
|
+
import { contractLocator, gitRefLocator, gitObjectIdForSnapshot } from "./identity.js";
|
|
8
|
+
import { executeGit } from "./process.js";
|
|
9
9
|
const SCRATCH_PREFIX = "keiyaku-v4-verify-";
|
|
10
10
|
const SCRATCH_ROOT = await realpath(tmpdir());
|
|
11
|
-
const SCRATCH_PATTERN = /^keiyaku-v4-verify-([0-9a-f]{24})$/;
|
|
12
|
-
|
|
11
|
+
const SCRATCH_PATTERN = /^keiyaku-v4-verify-([0-9a-f]{24}|[0-9a-f]{64}-[0-9a-f]{64})$/;
|
|
12
|
+
/** One bounded removal attempt per scratch; expiry is reported, never retried inside this call. */
|
|
13
|
+
const SCRATCH_REMOVAL_TIMEOUT_MS = 120_000;
|
|
14
|
+
export const worktreeLeakSchema = z.object({ path: z.string().optional(), diagnostic: z.string() }).strict();
|
|
13
15
|
function scratchPath() {
|
|
14
16
|
return join(SCRATCH_ROOT, `${SCRATCH_PREFIX}${randomBytes(12).toString("hex")}`);
|
|
15
17
|
}
|
|
18
|
+
function retainedScratchPath(repository, contract) {
|
|
19
|
+
return join(SCRATCH_ROOT, `${SCRATCH_PREFIX}${gitRefLocator(repository.commonDirectory)}-${contractLocator(contract)}`);
|
|
20
|
+
}
|
|
16
21
|
function ownershipLockPath(path) {
|
|
17
22
|
if (dirname(path) !== SCRATCH_ROOT)
|
|
18
23
|
return null;
|
|
@@ -25,69 +30,132 @@ async function removeOwnershipLock(lockPath) {
|
|
|
25
30
|
throw error;
|
|
26
31
|
});
|
|
27
32
|
}
|
|
28
|
-
async function collectableScratchWorktrees(paths) {
|
|
33
|
+
async function collectableScratchWorktrees(paths, terminalScratch) {
|
|
29
34
|
const collectable = [];
|
|
30
35
|
for (const path of paths) {
|
|
31
36
|
const lockPath = ownershipLockPath(path);
|
|
32
37
|
if (lockPath === null)
|
|
33
38
|
continue;
|
|
39
|
+
if (basename(path).length !== SCRATCH_PREFIX.length + 24 && path !== terminalScratch)
|
|
40
|
+
continue;
|
|
34
41
|
const lock = await tryAcquireSqliteTransactionLock({ path: lockPath, mode: "exclusive" });
|
|
35
42
|
if (lock !== null)
|
|
36
43
|
collectable.push({ path, lockPath, release: () => lock.close() });
|
|
37
44
|
}
|
|
38
45
|
return collectable;
|
|
39
46
|
}
|
|
40
|
-
|
|
47
|
+
/** Absence is ENOENT on the path entry itself: a dangling symlink is present residue, not a missing path. */
|
|
48
|
+
async function pathExists(path) {
|
|
49
|
+
try {
|
|
50
|
+
await lstat(path);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === "ENOENT")
|
|
55
|
+
return false;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function removeScratch(repository, path) {
|
|
60
|
+
const removal = AbortSignal.timeout(SCRATCH_REMOVAL_TIMEOUT_MS);
|
|
61
|
+
try {
|
|
62
|
+
const present = await pathExists(path);
|
|
63
|
+
await executeGit({ ...repository, signal: removal }, ["worktree", "remove", ...(present ? ["--force"] : []), path]);
|
|
64
|
+
return (await pathExists(path))
|
|
65
|
+
? { path, diagnostic: `Git reported scratch worktree removal but the path remains: ${path}` }
|
|
66
|
+
: null;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const original = error instanceof Error ? error.message : String(error);
|
|
70
|
+
return {
|
|
71
|
+
path,
|
|
72
|
+
diagnostic: removal.aborted
|
|
73
|
+
? `scratch worktree removal timed out after ${SCRATCH_REMOVAL_TIMEOUT_MS}ms: ${original}`
|
|
74
|
+
: original,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
41
78
|
/** Remove only abandoned Verification scratch worktrees whose ownership locks are held. */
|
|
42
|
-
export async function removeCollectableScratchWorktrees(repository, paths) {
|
|
79
|
+
export async function removeCollectableScratchWorktrees(repository, paths, terminalContract) {
|
|
43
80
|
const removals = [];
|
|
44
|
-
|
|
81
|
+
const terminalScratch = terminalContract === undefined ? undefined : retainedScratchPath(repository, terminalContract);
|
|
82
|
+
for (const scratch of await collectableScratchWorktrees(paths, terminalScratch)) {
|
|
45
83
|
let removed = false;
|
|
46
84
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
removed = true;
|
|
51
|
-
removals.push({ path: scratch.path, action: "removed", retained: false });
|
|
85
|
+
const leak = await removeScratch(repository, scratch.path);
|
|
86
|
+
if (leak !== null) {
|
|
87
|
+
removals.push({ path: scratch.path, retained: true, diagnostic: leak.diagnostic });
|
|
52
88
|
continue;
|
|
53
89
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
removed = true;
|
|
58
|
-
removals.push({ path: scratch.path, action: "removed", retained: false });
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
removals.push({
|
|
62
|
-
path: scratch.path,
|
|
63
|
-
action: "unchanged",
|
|
64
|
-
retained: true,
|
|
65
|
-
diagnostic: error instanceof Error ? error.message : String(error),
|
|
66
|
-
});
|
|
67
|
-
}
|
|
90
|
+
paths.delete(scratch.path);
|
|
91
|
+
removed = true;
|
|
92
|
+
removals.push({ path: scratch.path, retained: false });
|
|
68
93
|
}
|
|
69
94
|
finally {
|
|
70
95
|
scratch.release();
|
|
71
|
-
if (removed)
|
|
96
|
+
if (removed && scratch.path !== terminalScratch)
|
|
72
97
|
await removeOwnershipLock(scratch.lockPath);
|
|
73
98
|
}
|
|
74
99
|
}
|
|
75
100
|
return removals;
|
|
76
101
|
}
|
|
102
|
+
async function retainScratch(repository, cwd, contract) {
|
|
103
|
+
const retained = retainedScratchPath(repository, contract);
|
|
104
|
+
const lockPath = ownershipLockPath(retained);
|
|
105
|
+
if (lockPath === null)
|
|
106
|
+
throw new Error("scratch ownership lock path is invalid");
|
|
107
|
+
const { signal: _signal, ...retirementRepository } = repository;
|
|
108
|
+
const fallback = async (reason) => {
|
|
109
|
+
const ownLeak = await removeScratch(retirementRepository, cwd);
|
|
110
|
+
return {
|
|
111
|
+
...(ownLeak === null ? {} : { path: ownLeak.path }),
|
|
112
|
+
diagnostic: `failed Verification scratch could not be retained: ${reason}${ownLeak === null ? "" : `; ${ownLeak.diagnostic}`}`,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
let lock;
|
|
116
|
+
try {
|
|
117
|
+
lock = await acquireSqliteTransactionLock({
|
|
118
|
+
path: lockPath,
|
|
119
|
+
mode: "exclusive",
|
|
120
|
+
timeoutMs: SCRATCH_REMOVAL_TIMEOUT_MS,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return await fallback(`retained scratch busy: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const replaced = await pathExists(retained);
|
|
128
|
+
if (replaced) {
|
|
129
|
+
const leak = await removeScratch(retirementRepository, retained);
|
|
130
|
+
if (leak !== null)
|
|
131
|
+
return await fallback(leak.diagnostic);
|
|
132
|
+
}
|
|
133
|
+
await executeGit(retirementRepository, ["worktree", "move", cwd, retained]);
|
|
134
|
+
return {
|
|
135
|
+
path: retained,
|
|
136
|
+
diagnostic: `failed Verification scratch retained for diagnosis${replaced ? " (replaced the previous retained scratch)" : ""}`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return await fallback(error instanceof Error ? error.message : String(error));
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
lock.close();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
77
146
|
/** Materialize a disposable candidate worktree and own only its removal. */
|
|
78
|
-
export async function materializeScratchCandidate(repository, candidate) {
|
|
147
|
+
export async function materializeScratchCandidate(repository, candidate, contract) {
|
|
79
148
|
const cwd = scratchPath();
|
|
80
149
|
const lockPath = ownershipLockPath(cwd);
|
|
81
150
|
if (lockPath === null)
|
|
82
151
|
throw new Error("scratch ownership lock path is invalid");
|
|
83
|
-
const ownership = await
|
|
152
|
+
const ownership = await acquireSqliteTransactionLock({
|
|
84
153
|
path: lockPath,
|
|
85
154
|
mode: "exclusive",
|
|
155
|
+
...(repository.signal === undefined ? {} : { signal: repository.signal }),
|
|
86
156
|
});
|
|
87
|
-
if (ownership === null)
|
|
88
|
-
throw new Error("new scratch ownership lock is unexpectedly held");
|
|
89
157
|
try {
|
|
90
|
-
await
|
|
158
|
+
await executeGit(repository, ["worktree", "add", "--detach", cwd, gitObjectIdForSnapshot(candidate)]);
|
|
91
159
|
}
|
|
92
160
|
catch (error) {
|
|
93
161
|
ownership.close();
|
|
@@ -97,26 +165,22 @@ export async function materializeScratchCandidate(repository, candidate) {
|
|
|
97
165
|
let disposed = false;
|
|
98
166
|
return {
|
|
99
167
|
cwd,
|
|
100
|
-
dispose: async () => {
|
|
168
|
+
dispose: async (retain = false) => {
|
|
101
169
|
if (disposed)
|
|
102
170
|
return null;
|
|
103
171
|
disposed = true;
|
|
104
|
-
let leak = null;
|
|
105
172
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
diagnostic: error instanceof Error ? error.message : String(error),
|
|
112
|
-
};
|
|
173
|
+
if (!retain)
|
|
174
|
+
return await removeScratch(repository, cwd);
|
|
175
|
+
if (contract === undefined)
|
|
176
|
+
return { path: cwd, diagnostic: "failed Verification scratch retained for diagnosis" };
|
|
177
|
+
return await retainScratch(repository, cwd, contract);
|
|
113
178
|
}
|
|
114
179
|
finally {
|
|
115
180
|
ownership.close();
|
|
116
|
-
if (
|
|
181
|
+
if (!(await pathExists(cwd)))
|
|
117
182
|
await removeOwnershipLock(lockPath);
|
|
118
183
|
}
|
|
119
|
-
return leak;
|
|
120
184
|
},
|
|
121
185
|
};
|
|
122
186
|
}
|
|
@@ -30,7 +30,7 @@ export declare const targetCheckoutLagSchema: z.ZodObject<{
|
|
|
30
30
|
diagnostic: z.ZodString;
|
|
31
31
|
}, z.core.$strict>;
|
|
32
32
|
type TargetCheckoutLag = z.infer<typeof targetCheckoutLagSchema>;
|
|
33
|
-
|
|
33
|
+
type TargetPlacementPhysicalResult = Readonly<{
|
|
34
34
|
effects: readonly TargetCheckoutEffect[];
|
|
35
35
|
lag: readonly TargetCheckoutLag[];
|
|
36
36
|
}>;
|
|
@@ -42,7 +42,7 @@ export type PreparedTargetPlacement = Readonly<{
|
|
|
42
42
|
target: RefOperation;
|
|
43
43
|
arms: readonly FollowArm[];
|
|
44
44
|
}>;
|
|
45
|
-
|
|
45
|
+
type TargetPlacementPreparation = Readonly<{
|
|
46
46
|
kind: "prepared";
|
|
47
47
|
placement: PreparedTargetPlacement;
|
|
48
48
|
}> | Readonly<{
|
|
@@ -63,16 +63,16 @@ export type TargetPlacementChanges = Readonly<{
|
|
|
63
63
|
/** Only immutable commit contents are prepared outside the publication seat. */
|
|
64
64
|
export declare function prepareTargetPlacementChanges(repository: GitRepository, target: RefOperation): Promise<TargetPlacementChanges>;
|
|
65
65
|
export declare function acquireTargetPlacementFence(repository: GitRepository, target: string): Promise<HeldSqliteTransactionLock>;
|
|
66
|
-
|
|
66
|
+
type TargetedContractCoordinates = ContractState["coordinates"] & Readonly<{
|
|
67
67
|
target: string;
|
|
68
68
|
}>;
|
|
69
|
-
|
|
69
|
+
type TargetPlacementObservationInput = Readonly<{
|
|
70
70
|
contractId: ContractId;
|
|
71
71
|
coordinates: TargetedContractCoordinates;
|
|
72
72
|
predecessor: SnapshotId;
|
|
73
73
|
candidate: SnapshotId;
|
|
74
74
|
}>;
|
|
75
|
-
|
|
75
|
+
type TargetPlacementObservation = Readonly<{
|
|
76
76
|
kind: "ready";
|
|
77
77
|
arms: readonly FollowArm[];
|
|
78
78
|
}> | Readonly<{
|
|
@@ -6,7 +6,7 @@ import { resolve } from "node:path";
|
|
|
6
6
|
import { acquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
|
|
7
7
|
import { gitObjectId, gitObjectIdForSnapshot, gitRefLocator, mintSnapshotId } from "./identity.js";
|
|
8
8
|
import { commonGitDirectory, decodeGitNameOnly, readRef, registeredWorktrees } from "./repository.js";
|
|
9
|
-
import { consumeGitStdout, GitPlumbingError,
|
|
9
|
+
import { consumeGitStdout, GitPlumbingError, executeGit } from "./process.js";
|
|
10
10
|
import { captureWorkspaceTree } from "./workspace.js";
|
|
11
11
|
export const checkoutNotFollowableRefusalSchema = z
|
|
12
12
|
.object({
|
|
@@ -42,13 +42,15 @@ function diagnostic(error) {
|
|
|
42
42
|
return error instanceof Error ? error.message : String(error);
|
|
43
43
|
}
|
|
44
44
|
async function gitPaths(repository, path, args) {
|
|
45
|
-
return [...new Set(decodeGitNameOnly(await
|
|
45
|
+
return [...new Set(decodeGitNameOnly(await executeGit(repository, ["-C", path, ...args])))].sort();
|
|
46
46
|
}
|
|
47
47
|
function literalPath(path) {
|
|
48
48
|
return `:(literal)${path}`;
|
|
49
49
|
}
|
|
50
50
|
async function commitTree(repository, snapshot) {
|
|
51
|
-
return gitObjectId((await
|
|
51
|
+
return gitObjectId((await executeGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
|
|
52
|
+
.toString("utf8")
|
|
53
|
+
.trim(), "commit tree");
|
|
52
54
|
}
|
|
53
55
|
export async function observedTreeEqualsCandidate(repository, observed, snapshot) {
|
|
54
56
|
if (observed === null)
|
|
@@ -60,7 +62,7 @@ function checkoutRefusal(contractId, target, path, reason, paths) {
|
|
|
60
62
|
}
|
|
61
63
|
/** Only immutable commit contents are prepared outside the publication seat. */
|
|
62
64
|
export async function prepareTargetPlacementChanges(repository, target) {
|
|
63
|
-
const records = (await
|
|
65
|
+
const records = (await executeGit(repository, [
|
|
64
66
|
"diff",
|
|
65
67
|
"--raw",
|
|
66
68
|
"--no-renames",
|
|
@@ -175,27 +177,10 @@ async function untrackedRefusalWithinScopes(input, scopes) {
|
|
|
175
177
|
const first = directories.find((scope) => found.has(scope.path));
|
|
176
178
|
return first === undefined ? null : checkoutRefusal(contractId, target, path, "untracked", [first.path]);
|
|
177
179
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"--name-only",
|
|
183
|
-
"-z",
|
|
184
|
-
tree,
|
|
185
|
-
"--",
|
|
186
|
-
...paths.map(literalPath),
|
|
187
|
-
])).length === 0);
|
|
188
|
-
}
|
|
189
|
-
async function workspaceMatchesTreeOnPaths(repository, path, tree, workspaceTree, paths) {
|
|
190
|
-
return ((await gitPaths(repository, path, [
|
|
191
|
-
"diff",
|
|
192
|
-
"--name-only",
|
|
193
|
-
"-z",
|
|
194
|
-
tree,
|
|
195
|
-
workspaceTree,
|
|
196
|
-
"--",
|
|
197
|
-
...paths.map(literalPath),
|
|
198
|
-
])).length === 0);
|
|
180
|
+
/** Compare the index or a workspace tree against another tree over the given paths. */
|
|
181
|
+
async function matchesTreeOnPaths(repository, path, command, trees, paths) {
|
|
182
|
+
const args = [...command, "--name-only", "-z", ...trees, "--", ...paths.map(literalPath)];
|
|
183
|
+
return (await gitPaths(repository, path, args)).length === 0;
|
|
199
184
|
}
|
|
200
185
|
/**
|
|
201
186
|
* A checkout that git cannot carry is judged by the physical follow, which records its own lag.
|
|
@@ -207,7 +192,7 @@ async function ordinaryPrecheck(repository, contractId, target, path, changes) {
|
|
|
207
192
|
const candidate = gitObjectIdForSnapshot(target.newOid);
|
|
208
193
|
const observation = { repository, contractId, target, path, predecessor, candidate, changes };
|
|
209
194
|
try {
|
|
210
|
-
await
|
|
195
|
+
await executeGit(repository, ["-C", path, "read-tree", "--dry-run", "-m", "-u", predecessor, candidate]);
|
|
211
196
|
}
|
|
212
197
|
catch (error) {
|
|
213
198
|
if (!(error instanceof GitPlumbingError) || repository.signal?.aborted === true)
|
|
@@ -311,7 +296,7 @@ export async function followTargetPlacement(repository, prepared, onPhysical) {
|
|
|
311
296
|
for (const arm of prepared.arms) {
|
|
312
297
|
const beforeEffects = effects.length, beforeLag = lag.length;
|
|
313
298
|
try {
|
|
314
|
-
await
|
|
299
|
+
await executeGit(repository, ["-C", arm.path, "read-tree", "-m", "-u", predecessor, candidate]);
|
|
315
300
|
effects.push({ kind: "target-checkout", path: arm.path, target: prepared.target.target, action: "followed" });
|
|
316
301
|
}
|
|
317
302
|
catch (error) {
|
|
@@ -337,15 +322,15 @@ async function classifyCheckout(input) {
|
|
|
337
322
|
if (changedPaths.length === 0)
|
|
338
323
|
return { kind: "complete" };
|
|
339
324
|
const workspaceTree = (await captureWorkspaceTree(repository, path)).tree;
|
|
340
|
-
const candidateIndex = await
|
|
341
|
-
const candidateWorkspace = await
|
|
325
|
+
const candidateIndex = await matchesTreeOnPaths(repository, path, ["diff-index", "--cached"], [candidateTree], changedPaths);
|
|
326
|
+
const candidateWorkspace = await matchesTreeOnPaths(repository, path, ["diff"], [candidateTree, workspaceTree], changedPaths);
|
|
342
327
|
if (candidateIndex && candidateWorkspace)
|
|
343
328
|
return { kind: "complete" };
|
|
344
|
-
const predecessorIndex = await
|
|
329
|
+
const predecessorIndex = await matchesTreeOnPaths(repository, path, ["diff-index", "--cached"], [predecessorTree], changedPaths);
|
|
345
330
|
if (predecessorIndex && candidateWorkspace)
|
|
346
331
|
return { kind: "recoverable", action: "index-merge" };
|
|
347
332
|
return !predecessorIndex ||
|
|
348
|
-
!(await
|
|
333
|
+
!(await matchesTreeOnPaths(repository, path, ["diff"], [predecessorTree, workspaceTree], changedPaths))
|
|
349
334
|
? { kind: "retained" }
|
|
350
335
|
: { kind: "recoverable", action: "worktree-merge" };
|
|
351
336
|
}
|
|
@@ -365,7 +350,7 @@ async function recoverCheckout(input) {
|
|
|
365
350
|
return "retained";
|
|
366
351
|
const { repository, path, predecessor, candidate } = input;
|
|
367
352
|
if (classified.action === "index-merge") {
|
|
368
|
-
await
|
|
353
|
+
await executeGit(repository, [
|
|
369
354
|
"-C",
|
|
370
355
|
path,
|
|
371
356
|
"read-tree",
|
|
@@ -376,7 +361,7 @@ async function recoverCheckout(input) {
|
|
|
376
361
|
]);
|
|
377
362
|
return "recovered";
|
|
378
363
|
}
|
|
379
|
-
await
|
|
364
|
+
await executeGit(repository, [
|
|
380
365
|
"-C",
|
|
381
366
|
path,
|
|
382
367
|
"read-tree",
|
|
@@ -4,7 +4,7 @@ import type { ActorId, ContractCoordinates, ContractId, SnapshotId } from "../co
|
|
|
4
4
|
import { type GitObjectId } from "./identity.js";
|
|
5
5
|
import { type GitRepository } from "./process.js";
|
|
6
6
|
import { captureWorkspaceTree } from "./workspace.js";
|
|
7
|
-
|
|
7
|
+
type TenderCaptureCoordinates = Readonly<{
|
|
8
8
|
contractId: ContractId;
|
|
9
9
|
coordinates: ContractCoordinates;
|
|
10
10
|
place?: string;
|
|
@@ -23,7 +23,7 @@ export declare const unmergedPathsRefusalSchema: z.ZodObject<{
|
|
|
23
23
|
contractId: z.ZodPipe<z.ZodString, z.ZodTransform<ContractId, string>>;
|
|
24
24
|
paths: z.ZodReadonly<z.ZodArray<z.ZodString>>;
|
|
25
25
|
}, z.core.$strict>;
|
|
26
|
-
|
|
26
|
+
type TenderCaptureRefusal = WorktreeMissingRefusal | z.infer<typeof unmergedPathsRefusalSchema>;
|
|
27
27
|
export declare const workspaceDirtyDeltaSchema: z.ZodObject<{
|
|
28
28
|
staged: z.ZodReadonly<z.ZodArray<z.ZodString>>;
|
|
29
29
|
unstaged: z.ZodReadonly<z.ZodArray<z.ZodString>>;
|
|
@@ -81,6 +81,10 @@ export declare function prepareDeliveryCommitMetadata(repository: GitRepository,
|
|
|
81
81
|
actor?: ActorId;
|
|
82
82
|
message?: string;
|
|
83
83
|
}>): Promise<DeliveryCommitMetadata>;
|
|
84
|
+
/** The Git identity environment for one delivery snapshot commit. */
|
|
85
|
+
export declare function deliveryEnvironment(commit: DeliveryCommitMetadata): NodeJS.ProcessEnv;
|
|
86
|
+
/** Materialize one delivery snapshot commit over the given parents. */
|
|
87
|
+
export declare function materializeSnapshotCommit(repository: GitRepository, tree: GitObjectId, parents: readonly SnapshotId[], message: string | Uint8Array, environment: NodeJS.ProcessEnv): Promise<SnapshotId>;
|
|
84
88
|
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
85
89
|
export declare function materializeTenderSnapshot(repository: GitRepository, tender: TenderCapture, commit: DeliveryCommitMetadata): Promise<SnapshotId>;
|
|
86
90
|
export {};
|
package/build/src/git/tender.js
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { access } from "node:fs/promises";
|
|
4
4
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
5
5
|
import { decodeGitNumstat, registeredWorktreePaths } from "./repository.js";
|
|
6
|
-
import {
|
|
6
|
+
import { executeGit, GitPlumbingError } from "./process.js";
|
|
7
7
|
import { captureWorkspaceTree, unmergedWorkspacePaths, workspaceMergeHead, worktreePath } from "./workspace.js";
|
|
8
8
|
export const worktreeMissingRefusalSchema = z
|
|
9
9
|
.object({ kind: z.literal("worktree-missing"), contractId: contractIdSchema })
|
|
@@ -69,7 +69,7 @@ export async function captureTender(repository, input) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
async function dirtyShortStat(repository, tender) {
|
|
72
|
-
return decodeGitNumstat(await
|
|
72
|
+
return decodeGitNumstat(await executeGit(repository, ["diff", "--numstat", "-z", gitObjectIdForSnapshot(tender.head), tender.tree]));
|
|
73
73
|
}
|
|
74
74
|
export async function dirtyTenderRefusal(repository, contractId, tender) {
|
|
75
75
|
return {
|
|
@@ -87,7 +87,7 @@ export async function dirtyTenderDelta(repository, tender) {
|
|
|
87
87
|
}
|
|
88
88
|
async function effectiveConfig(repository, key) {
|
|
89
89
|
try {
|
|
90
|
-
const value = (await
|
|
90
|
+
const value = (await executeGit(repository, ["config", "--get", key])).toString("utf8").trim();
|
|
91
91
|
return value.length === 0 ? undefined : value;
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
@@ -119,22 +119,29 @@ export async function prepareDeliveryCommitMetadata(repository, input) {
|
|
|
119
119
|
identity,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
/**
|
|
123
|
-
export
|
|
124
|
-
|
|
125
|
-
return tender.head;
|
|
126
|
-
const parents = ["-p", gitObjectIdForSnapshot(tender.head)];
|
|
127
|
-
if (tender.mergeHead !== undefined)
|
|
128
|
-
parents.push("-p", gitObjectIdForSnapshot(tender.mergeHead));
|
|
129
|
-
const oid = (await runGitWithEnvironment(repository, ["commit-tree", tender.tree, ...parents], commit.message, {
|
|
122
|
+
/** The Git identity environment for one delivery snapshot commit. */
|
|
123
|
+
export function deliveryEnvironment(commit) {
|
|
124
|
+
return {
|
|
130
125
|
GIT_AUTHOR_NAME: commit.identity.name,
|
|
131
126
|
GIT_AUTHOR_EMAIL: commit.identity.email,
|
|
132
127
|
GIT_COMMITTER_NAME: commit.identity.name,
|
|
133
128
|
GIT_COMMITTER_EMAIL: commit.identity.email,
|
|
134
129
|
GIT_AUTHOR_DATE: commit.at,
|
|
135
130
|
GIT_COMMITTER_DATE: commit.at,
|
|
136
|
-
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Materialize one delivery snapshot commit over the given parents. */
|
|
134
|
+
export async function materializeSnapshotCommit(repository, tree, parents, message, environment) {
|
|
135
|
+
const args = parents.flatMap((parent) => ["-p", gitObjectIdForSnapshot(parent)]);
|
|
136
|
+
const oid = (await executeGit(repository, ["commit-tree", tree, ...args], message, environment))
|
|
137
137
|
.toString("utf8")
|
|
138
138
|
.trim();
|
|
139
139
|
return mintSnapshotId(oid);
|
|
140
140
|
}
|
|
141
|
+
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
142
|
+
export async function materializeTenderSnapshot(repository, tender, commit) {
|
|
143
|
+
if (!tender.dirty && tender.mergeHead === undefined)
|
|
144
|
+
return tender.head;
|
|
145
|
+
const parents = tender.mergeHead === undefined ? [tender.head] : [tender.head, tender.mergeHead];
|
|
146
|
+
return await materializeSnapshotCommit(repository, tender.tree, parents, commit.message, deliveryEnvironment(commit));
|
|
147
|
+
}
|
|
@@ -3,7 +3,7 @@ import { isKeiyakuOwnedRef, readRef, writeCommit } from "./repository.js";
|
|
|
3
3
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
4
4
|
import { runDestroyHooks } from "./hooks.js";
|
|
5
5
|
import { terminalSealExpectations as decodeTerminalSealExpectations, terminalSealSnapshots, observeTerminalWorkspace, } from "./terminal-seal.js";
|
|
6
|
-
import { GitPlumbingError,
|
|
6
|
+
import { GitPlumbingError, executeGit } from "./process.js";
|
|
7
7
|
import { worktreePath } from "./workspace.js";
|
|
8
8
|
async function pathExists(path) {
|
|
9
9
|
try {
|
|
@@ -16,29 +16,38 @@ async function pathExists(path) {
|
|
|
16
16
|
throw error;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/** Attempt removal once. Retention is a lag carrying its reason; no effect claims the worktree state after it. */
|
|
19
20
|
async function removeTerminalWorktree(repository, topology, path) {
|
|
20
21
|
const registered = topology.paths.has(path);
|
|
21
22
|
if (!registered) {
|
|
22
|
-
return
|
|
23
|
+
return (await pathExists(path)) ? { retainedLag: { kind: "worktree-retained", path } } : {};
|
|
23
24
|
}
|
|
24
25
|
const present = await pathExists(path);
|
|
25
26
|
try {
|
|
26
|
-
await
|
|
27
|
+
await executeGit(repository, ["worktree", "remove", ...(present ? ["--force"] : []), path]);
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
30
|
if (!(error instanceof GitPlumbingError))
|
|
30
31
|
throw error;
|
|
31
|
-
return {
|
|
32
|
+
return { retainedLag: { kind: "worktree-retained", path, diagnostic: error.message } };
|
|
32
33
|
}
|
|
33
34
|
topology.paths.delete(path);
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if (await pathExists(path)) {
|
|
36
|
+
return {
|
|
37
|
+
retainedLag: {
|
|
38
|
+
kind: "worktree-retained",
|
|
39
|
+
path,
|
|
40
|
+
diagnostic: `Git reported worktree removal but the path remains: ${path}`,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { effect: { kind: "worktree", path, action: "removed" } };
|
|
36
45
|
}
|
|
37
46
|
export async function updateRef(repository, ref, desired) {
|
|
38
47
|
const before = await readRef(repository, ref);
|
|
39
48
|
if (before === desired)
|
|
40
49
|
return { kind: "ref", name: ref, action: "unchanged", before, after: desired };
|
|
41
|
-
await
|
|
50
|
+
await executeGit(repository, [
|
|
42
51
|
"update-ref",
|
|
43
52
|
"--no-deref",
|
|
44
53
|
ref,
|
|
@@ -51,7 +60,7 @@ export async function removeRef(repository, ref) {
|
|
|
51
60
|
const before = await readRef(repository, ref);
|
|
52
61
|
if (before === null)
|
|
53
62
|
return { kind: "ref", name: ref, action: "unchanged", before: null, after: null };
|
|
54
|
-
await
|
|
63
|
+
await executeGit(repository, ["update-ref", "--no-deref", "-d", ref, before]);
|
|
55
64
|
return { kind: "ref", name: ref, action: "removed", before, after: null };
|
|
56
65
|
}
|
|
57
66
|
async function removeRefWithCustody(repository, ref, custodian, expectedCustodian) {
|
|
@@ -61,7 +70,7 @@ async function removeRefWithCustody(repository, ref, custodian, expectedCustodia
|
|
|
61
70
|
if ((await readRef(repository, custodian)) !== expectedCustodian) {
|
|
62
71
|
return { kind: "ref", name: ref, action: "unchanged", before, after: before };
|
|
63
72
|
}
|
|
64
|
-
await
|
|
73
|
+
await executeGit(repository, ["update-ref", "--stdin", "--no-deref"], [
|
|
65
74
|
"start",
|
|
66
75
|
`verify ${custodian} ${gitObjectIdForSnapshot(expectedCustodian)}`,
|
|
67
76
|
`delete ${ref} ${before}`,
|
|
@@ -75,7 +84,7 @@ function retainedRefEffect(ref, snapshot) {
|
|
|
75
84
|
return { kind: "ref", name: ref, action: "unchanged", before: snapshot, after: snapshot };
|
|
76
85
|
}
|
|
77
86
|
async function refRows(repository) {
|
|
78
|
-
const output = (await
|
|
87
|
+
const output = (await executeGit(repository, ["for-each-ref", "--format=%(refname)%00%(objectname)"])).toString("utf8");
|
|
79
88
|
if (output.length === 0)
|
|
80
89
|
return [];
|
|
81
90
|
return output
|
|
@@ -90,7 +99,7 @@ async function refRows(repository) {
|
|
|
90
99
|
}
|
|
91
100
|
async function commitIsAncestor(repository, ancestor, descendant) {
|
|
92
101
|
try {
|
|
93
|
-
await
|
|
102
|
+
await executeGit(repository, ["merge-base", "--is-ancestor", gitObjectIdForSnapshot(ancestor), descendant]);
|
|
94
103
|
return true;
|
|
95
104
|
}
|
|
96
105
|
catch (error) {
|
|
@@ -181,8 +190,10 @@ async function removeSealedTerminalWorktree({ repository, topology, path, state,
|
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
const removal = await removeTerminalWorktree(repository, topology, path);
|
|
184
|
-
if (removal.
|
|
185
|
-
|
|
193
|
+
if (removal.retainedLag !== undefined) {
|
|
194
|
+
acc.recordLag(removal.retainedLag);
|
|
195
|
+
return complete(acc.effects, acc.lag, acc.hookRuns);
|
|
196
|
+
}
|
|
186
197
|
if (removal.effect !== undefined)
|
|
187
198
|
acc.recordEffect(removal.effect);
|
|
188
199
|
return null;
|
|
@@ -3,7 +3,6 @@ import type { ContractState, SnapshotId } from "../core/facts/types.js";
|
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
4
|
import type { GitOid } from "./repository.js";
|
|
5
5
|
import { type GitRepository } from "./process.js";
|
|
6
|
-
import { captureWorkspaceTree } from "./workspace.js";
|
|
7
6
|
export declare const unsealedBytesSchema: z.ZodObject<{
|
|
8
7
|
kind: z.ZodLiteral<"unsealed-bytes">;
|
|
9
8
|
path: z.ZodString;
|
|
@@ -16,25 +15,23 @@ export type TerminalSealExpectations = Readonly<{
|
|
|
16
15
|
trees: readonly GitObjectId[];
|
|
17
16
|
treeBySnapshot: ReadonlyMap<SnapshotId, GitObjectId>;
|
|
18
17
|
}>;
|
|
19
|
-
|
|
18
|
+
type TerminalSealObject = Readonly<{
|
|
20
19
|
kind: "present";
|
|
21
20
|
type: string;
|
|
22
21
|
bytes: Buffer;
|
|
23
22
|
}> | Readonly<{
|
|
24
23
|
kind: "missing";
|
|
25
24
|
}>;
|
|
26
|
-
type WorkspaceTree = Awaited<ReturnType<typeof captureWorkspaceTree>>;
|
|
27
25
|
export type TerminalWorkspace = Readonly<{
|
|
28
26
|
tree: GitObjectId;
|
|
29
27
|
head: SnapshotId;
|
|
30
28
|
submodules: readonly string[];
|
|
31
29
|
}>;
|
|
32
|
-
|
|
30
|
+
type TerminalWorkspaceObservation = Readonly<{
|
|
33
31
|
workspace: TerminalWorkspace;
|
|
34
32
|
unsealed: UnsealedBytes | null;
|
|
35
33
|
}>;
|
|
36
34
|
export declare function terminalSealSnapshots(state: ContractState): readonly SnapshotId[];
|
|
37
35
|
export declare function terminalSealExpectations(state: ContractState, objects: ReadonlyMap<GitOid, TerminalSealObject>): TerminalSealExpectations;
|
|
38
|
-
export declare function unsealedWorkspaceBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations, workspace: WorkspaceTree): Promise<UnsealedBytes | null>;
|
|
39
36
|
export declare function observeTerminalWorkspace(repository: GitRepository, path: string, expected: TerminalSealExpectations): Promise<TerminalWorkspaceObservation>;
|
|
40
37
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mintedSnapshotInputSchema } from "./identity.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { gitObjectId, gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
4
|
-
import {
|
|
4
|
+
import { executeGit } from "./process.js";
|
|
5
5
|
import { captureWorkspaceTree } from "./workspace.js";
|
|
6
6
|
export const unsealedBytesSchema = z
|
|
7
7
|
.object({
|
|
@@ -71,12 +71,14 @@ export function terminalSealExpectations(state, objects) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
async function changedPaths(repository, left, right) {
|
|
74
|
-
const fields = (await
|
|
74
|
+
const fields = (await executeGit(repository, ["diff", "--name-only", "-z", left, right]))
|
|
75
|
+
.toString("utf8")
|
|
76
|
+
.split("\0");
|
|
75
77
|
if (fields.at(-1) !== "")
|
|
76
78
|
throw new Error("Git tree diff paths are not NUL terminated");
|
|
77
79
|
return fields.slice(0, -1).sort();
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
async function unsealedWorkspaceBytes(repository, path, expected, workspace) {
|
|
80
82
|
const headIsSealed = expected.heads.includes(workspace.head);
|
|
81
83
|
if (workspace.changes.submodules.length > 0) {
|
|
82
84
|
return {
|