@astrosheep/keiyaku 4.0.1 → 4.0.2
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 +137 -5
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +23 -14
- package/build/src/akuma/akuma.js +120 -104
- package/build/src/akuma/archetype.d.ts +7 -6
- package/build/src/akuma/archetype.js +68 -41
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +408 -213
- package/build/src/akuma/heart/facts.d.ts +25 -34
- package/build/src/akuma/heart/facts.js +18 -7
- package/build/src/akuma/heart/index.d.ts +39 -42
- package/build/src/akuma/heart/index.js +134 -93
- package/build/src/akuma/heart/rows.d.ts +9 -31
- package/build/src/akuma/heart/rows.js +26 -44
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +7 -15
- package/build/src/akuma/heart/soul.d.ts +8 -0
- package/build/src/akuma/heart/soul.js +136 -0
- package/build/src/akuma/heart/storage.d.ts +28 -14
- package/build/src/akuma/heart/storage.js +86 -26
- package/build/src/akuma/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +6 -1
- package/build/src/akuma/index.js +6 -0
- package/build/src/akuma/projection.d.ts +117 -38
- package/build/src/akuma/projection.js +208 -97
- package/build/src/akuma/provider-recipe.d.ts +24 -0
- package/build/src/akuma/provider-recipe.js +100 -0
- package/build/src/akuma/provider.d.ts +12 -4
- package/build/src/akuma/provider.js +117 -70
- package/build/src/akuma/providers/acp/core.d.ts +19 -0
- package/build/src/akuma/providers/acp/core.js +159 -0
- package/build/src/akuma/providers/acp/events.d.ts +20 -0
- package/build/src/akuma/providers/acp/events.js +73 -0
- package/build/src/akuma/providers/acp/index.d.ts +12 -0
- package/build/src/akuma/providers/acp/index.js +94 -0
- package/build/src/akuma/providers/claude/events.js +66 -13
- package/build/src/akuma/providers/claude/index.d.ts +2 -1
- package/build/src/akuma/providers/claude/index.js +35 -21
- package/build/src/akuma/providers/codex-app-server/events.js +4 -1
- package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
- package/build/src/akuma/providers/codex-app-server/index.js +24 -31
- package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
- package/build/src/akuma/providers/grok-build/index.js +70 -0
- package/build/src/akuma/providers/index.d.ts +5 -3
- package/build/src/akuma/providers/index.js +24 -59
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
- package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
- package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
- package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
- package/build/src/akuma/providers/pi/events.js +49 -11
- package/build/src/akuma/providers/pi/index.d.ts +1 -1
- package/build/src/akuma/providers/pi/index.js +50 -21
- package/build/src/akuma/publication.d.ts +3 -3
- package/build/src/akuma/publication.js +28 -40
- package/build/src/akuma/requests.d.ts +9 -7
- package/build/src/akuma/requests.js +92 -65
- package/build/src/alias/index.d.ts +2 -2
- package/build/src/alias/index.js +10 -10
- package/build/src/body/decode.js +7 -1
- package/build/src/body/region.d.ts +2 -0
- package/build/src/body/region.js +77 -7
- package/build/src/cli/accepted.d.ts +17 -13
- package/build/src/cli/accepted.js +72 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
- package/build/src/cli/commands/akuma-invoke.js +14 -9
- package/build/src/cli/commands/akuma.d.ts +13 -3
- package/build/src/cli/commands/akuma.js +67 -34
- package/build/src/cli/commands/contract.d.ts +18 -5
- package/build/src/cli/commands/contract.js +9 -3
- package/build/src/cli/commands/task-invoke.d.ts +17 -4
- package/build/src/cli/commands/task-invoke.js +43 -20
- package/build/src/cli/commands/task-query.d.ts +5 -0
- package/build/src/cli/commands/task-query.js +247 -0
- package/build/src/cli/commands/task.d.ts +3 -1
- package/build/src/cli/commands/task.js +51 -13
- package/build/src/cli/coordinates.d.ts +18 -0
- package/build/src/cli/coordinates.js +120 -0
- package/build/src/cli/draft.d.ts +8 -0
- package/build/src/cli/draft.js +96 -0
- package/build/src/cli/invoke.d.ts +4 -3
- package/build/src/cli/invoke.js +160 -179
- package/build/src/cli/main.js +34 -6
- package/build/src/cli/parse.d.ts +9 -2
- package/build/src/cli/parse.js +29 -7
- package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
- package/build/src/cli/render/akuma-tool-command.js +144 -0
- package/build/src/cli/render/akuma-tool.d.ts +2 -2
- package/build/src/cli/render/akuma-tool.js +30 -4
- package/build/src/cli/render/akuma.d.ts +1 -0
- package/build/src/cli/render/akuma.js +211 -116
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- package/build/src/cli/render/contract.d.ts +3 -3
- package/build/src/cli/render/contract.js +192 -79
- package/build/src/cli/render/kanshi.js +263 -168
- package/build/src/cli/render/receipt.d.ts +19 -0
- package/build/src/cli/render/receipt.js +106 -0
- package/build/src/cli/render/refusal.d.ts +16 -2
- package/build/src/cli/render/refusal.js +88 -20
- package/build/src/cli/render/region.d.ts +2 -0
- package/build/src/cli/render/region.js +17 -0
- package/build/src/cli/render/task.d.ts +2 -1
- package/build/src/cli/render/task.js +223 -60
- package/build/src/cli/render/terminal.d.ts +7 -0
- package/build/src/cli/render/terminal.js +46 -0
- package/build/src/cli/render/text.js +6 -3
- package/build/src/cli/result.d.ts +115 -20
- package/build/src/cli/usage.d.ts +1 -0
- package/build/src/cli/usage.js +3 -0
- package/build/src/contract-worktree.d.ts +6 -5
- package/build/src/contract-worktree.js +95 -57
- package/build/src/coordination/durable-file.d.ts +4 -3
- package/build/src/coordination/durable-file.js +29 -25
- package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
- package/build/src/coordination/sqlite-transaction-lock.js +22 -5
- package/build/src/core/facts/fold.js +1 -6
- package/build/src/core/facts/gate.d.ts +1 -0
- package/build/src/core/facts/gate.js +1 -0
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +0 -11
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +1 -7
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +4 -1
- package/build/src/dispatch/index.d.ts +2 -2
- package/build/src/dispatch/index.js +17 -17
- package/build/src/git/admission.d.ts +1 -1
- package/build/src/git/admission.js +11 -11
- package/build/src/git/hooks.js +13 -13
- package/build/src/git/integration.d.ts +14 -15
- package/build/src/git/integration.js +80 -44
- package/build/src/git/observe.d.ts +5 -3
- package/build/src/git/observe.js +18 -12
- package/build/src/git/read-observation.js +9 -9
- package/build/src/git/reconcile.d.ts +2 -0
- package/build/src/git/reconcile.js +138 -95
- package/build/src/git/repository.d.ts +27 -19
- package/build/src/git/repository.js +155 -83
- package/build/src/git/scratch.d.ts +7 -3
- package/build/src/git/scratch.js +44 -37
- package/build/src/git/target-placement.d.ts +40 -4
- package/build/src/git/target-placement.js +221 -101
- package/build/src/git/tender.d.ts +6 -5
- package/build/src/git/tender.js +24 -43
- package/build/src/git/terminal-seal.d.ts +1 -1
- package/build/src/git/terminal-seal.js +7 -5
- package/build/src/git/workspace.d.ts +36 -4
- package/build/src/git/workspace.js +53 -18
- package/build/src/identity/selector.js +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/index.js +1 -1
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +2 -1
- package/build/src/kanshi/read.js +118 -27
- package/build/src/kanshi/report.d.ts +50 -0
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +32 -0
- package/build/src/library/address.d.ts +12 -3
- package/build/src/library/address.js +77 -46
- package/build/src/library/akuma-creation.d.ts +6 -1
- package/build/src/library/akuma-creation.js +86 -13
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- package/build/src/library/bind.js +4 -4
- package/build/src/library/catalog.d.ts +1 -1
- package/build/src/library/catalog.js +5 -8
- package/build/src/library/contract.d.ts +5 -6
- package/build/src/library/contract.js +11 -35
- package/build/src/library/delivery.d.ts +2 -1
- package/build/src/library/fleet.d.ts +2 -2
- package/build/src/library/fleet.js +35 -71
- package/build/src/library/keiyaku.d.ts +3 -2
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +7 -6
- package/build/src/library/mutation.js +23 -25
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -0
- package/build/src/library/region.d.ts +12 -0
- package/build/src/library/region.js +15 -1
- package/build/src/library/repo.d.ts +4 -16
- package/build/src/library/repo.js +13 -38
- package/build/src/protocol/attempt.js +6 -6
- package/build/src/protocol/bind.js +4 -4
- package/build/src/protocol/intent.d.ts +15 -2
- package/build/src/protocol/intent.js +21 -6
- package/build/src/protocol/operations.d.ts +73 -22
- package/build/src/protocol/operations.js +292 -88
- package/build/src/protocol/outcome.d.ts +9 -3
- package/build/src/protocol/outcome.js +3 -1
- package/build/src/protocol/placement.d.ts +15 -4
- package/build/src/protocol/placement.js +58 -25
- package/build/src/protocol/read/status.d.ts +8 -1
- package/build/src/protocol/read/status.js +61 -14
- package/build/src/runtime/proc/line-rpc.d.ts +3 -8
- package/build/src/runtime/proc/line-rpc.js +16 -43
- package/build/src/runtime/proc/run.d.ts +15 -29
- package/build/src/runtime/proc/run.js +113 -105
- package/build/src/runtime/proc/stdio.d.ts +18 -0
- package/build/src/runtime/proc/stdio.js +58 -0
- package/build/src/settings.d.ts +2 -2
- package/build/src/settings.js +8 -8
- package/build/src/settlement/fence.d.ts +0 -4
- package/build/src/settlement/fence.js +0 -9
- package/build/src/settlement/holder.d.ts +11 -1
- package/build/src/settlement/holder.js +25 -0
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/settlement/settle.js +52 -89
- package/build/src/task/board.d.ts +3 -3
- package/build/src/task/board.js +6 -9
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +14 -14
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +19 -7
- package/build/src/task/index.js +42 -23
- package/build/src/task/operations.d.ts +15 -9
- package/build/src/task/operations.js +78 -48
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +2 -2
- package/build/src/task/store.js +40 -25
- package/build/src/verification/execution.d.ts +4 -2
- package/build/src/verification/execution.js +9 -5
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +13 -2
- package/build/src/world.js +81 -48
- package/package.json +3 -2
- package/build/src/protocol/read/audit.d.ts +0 -31
- package/build/src/protocol/read/audit.js +0 -55
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import { isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
4
|
+
import { consumeProcessStdout } from "../runtime/proc/run.js";
|
|
4
5
|
import { gitObjectId } from "./identity.js";
|
|
5
6
|
import { parseTreeObject, prepareTreeUpdate, treeUpdate, validPath, } from "./tree.js";
|
|
6
7
|
export const GIT_REF = "refs/heads/keiyaku-state";
|
|
@@ -38,6 +39,7 @@ export class GitPlumbingError extends Error {
|
|
|
38
39
|
this.pid = input.pid ?? null;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
const GIT_STDERR_BYTES = 16 * 1024;
|
|
41
43
|
function worktreesFromPorcelain(output) {
|
|
42
44
|
const fields = output.toString("utf8").split("\0");
|
|
43
45
|
const worktrees = [];
|
|
@@ -71,11 +73,13 @@ function worktreesFromPorcelain(output) {
|
|
|
71
73
|
throw new Error("Git worktree porcelain output has no worktrees");
|
|
72
74
|
return worktrees;
|
|
73
75
|
}
|
|
74
|
-
export function registeredWorktrees(repository) {
|
|
75
|
-
return worktreesFromPorcelain(runGit(repository, ["worktree", "list", "--porcelain", "-z"]));
|
|
76
|
+
export async function registeredWorktrees(repository) {
|
|
77
|
+
return worktreesFromPorcelain(await runGit(repository, ["worktree", "list", "--porcelain", "-z"]));
|
|
76
78
|
}
|
|
77
|
-
export function registeredWorktreePaths(repository) {
|
|
78
|
-
|
|
79
|
+
export async function registeredWorktreePaths(repository) {
|
|
80
|
+
return (await registeredWorktrees(repository)).map((worktree) => worktree.path);
|
|
81
|
+
}
|
|
82
|
+
export async function repositoryAt(cwd) {
|
|
79
83
|
if (typeof cwd !== "string" || cwd.length === 0) {
|
|
80
84
|
throw new Error("repository path must be a nonempty string");
|
|
81
85
|
}
|
|
@@ -88,8 +92,8 @@ export function repositoryAt(cwd) {
|
|
|
88
92
|
let primaryWorktree;
|
|
89
93
|
let commonDirectory;
|
|
90
94
|
try {
|
|
91
|
-
primaryWorktree = registeredWorktreePaths(provisional)[0];
|
|
92
|
-
commonDirectory = absoluteGitPath(provisional, ["--git-common-dir"], "common Git directory");
|
|
95
|
+
primaryWorktree = (await registeredWorktreePaths(provisional))[0];
|
|
96
|
+
commonDirectory = await absoluteGitPath(provisional, ["--git-common-dir"], "common Git directory");
|
|
93
97
|
}
|
|
94
98
|
catch (error) {
|
|
95
99
|
if (error instanceof GitPlumbingError && error.status === 128) {
|
|
@@ -99,8 +103,8 @@ export function repositoryAt(cwd) {
|
|
|
99
103
|
}
|
|
100
104
|
return { effectiveCwd, primaryWorktree, commonDirectory };
|
|
101
105
|
}
|
|
102
|
-
function absoluteGitPath(repository, args, label) {
|
|
103
|
-
const value = runGit(repository, ["rev-parse", "--path-format=absolute", ...args]).toString("utf8").trim();
|
|
106
|
+
async function absoluteGitPath(repository, args, label) {
|
|
107
|
+
const value = (await runGit(repository, ["rev-parse", "--path-format=absolute", ...args])).toString("utf8").trim();
|
|
104
108
|
if (value.length === 0)
|
|
105
109
|
throw new Error(`${label} is empty`);
|
|
106
110
|
return resolve(isAbsolute(value) ? value : resolve(repository.effectiveCwd, value));
|
|
@@ -108,12 +112,12 @@ function absoluteGitPath(repository, args, label) {
|
|
|
108
112
|
export function commonGitDirectory(repository) {
|
|
109
113
|
return repository.commonDirectory;
|
|
110
114
|
}
|
|
111
|
-
export function worktreeGitDirectory(repository, worktree) {
|
|
115
|
+
export async function worktreeGitDirectory(repository, worktree) {
|
|
112
116
|
const scoped = { ...repository, effectiveCwd: worktree };
|
|
113
|
-
return absoluteGitPath(scoped, ["--git-dir"], "worktree Git directory");
|
|
117
|
+
return await absoluteGitPath(scoped, ["--git-dir"], "worktree Git directory");
|
|
114
118
|
}
|
|
115
|
-
export function worktreeRoot(repository) {
|
|
116
|
-
return absoluteGitPath(repository, ["--show-toplevel"], "worktree root");
|
|
119
|
+
export async function worktreeRoot(repository) {
|
|
120
|
+
return await absoluteGitPath(repository, ["--show-toplevel"], "worktree root");
|
|
117
121
|
}
|
|
118
122
|
function commandError(command, error) {
|
|
119
123
|
const candidate = error;
|
|
@@ -128,32 +132,69 @@ function commandError(command, error) {
|
|
|
128
132
|
pid: candidate.pid ?? null,
|
|
129
133
|
});
|
|
130
134
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
async function executeGit(repository, args, input, environment) {
|
|
136
|
+
const stdout = [];
|
|
137
|
+
const stderr = [];
|
|
138
|
+
let stderrBytes = 0;
|
|
139
|
+
let inputError;
|
|
140
|
+
const child = spawn("git", [...args], {
|
|
141
|
+
cwd: repository.effectiveCwd,
|
|
142
|
+
...(environment === undefined ? {} : { env: { ...process.env, ...environment } }),
|
|
143
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
144
|
+
windowsHide: true,
|
|
145
|
+
});
|
|
146
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
147
|
+
child.stderr.on("data", (chunk) => {
|
|
148
|
+
if (stderrBytes >= GIT_STDERR_BYTES)
|
|
149
|
+
return;
|
|
150
|
+
const retained = chunk.subarray(0, GIT_STDERR_BYTES - stderrBytes);
|
|
151
|
+
stderr.push(retained);
|
|
152
|
+
stderrBytes += retained.length;
|
|
153
|
+
});
|
|
154
|
+
child.stdin.on("error", (error) => { inputError = error; });
|
|
155
|
+
child.stdin.end(input);
|
|
156
|
+
const terminal = await new Promise((resolveTerminal) => {
|
|
157
|
+
child.once("error", (error) => resolveTerminal({ code: null, error }));
|
|
158
|
+
child.once("close", (code) => resolveTerminal({ code }));
|
|
159
|
+
});
|
|
160
|
+
const output = Buffer.concat(stdout);
|
|
161
|
+
if (terminal.code === 0 && inputError === undefined)
|
|
162
|
+
return output;
|
|
163
|
+
throw commandError(args, {
|
|
164
|
+
message: terminal.error?.message ?? inputError?.message ?? `git exited with status ${terminal.code ?? "unknown"}`,
|
|
165
|
+
stdout: output,
|
|
166
|
+
stderr: Buffer.concat(stderr),
|
|
167
|
+
status: terminal.code,
|
|
168
|
+
pid: child.pid,
|
|
169
|
+
});
|
|
143
170
|
}
|
|
144
|
-
export function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
171
|
+
export async function runGit(repository, args, input) {
|
|
172
|
+
return await executeGit(repository, args, input);
|
|
173
|
+
}
|
|
174
|
+
export async function consumeGitStdout(repository, args, consume) {
|
|
175
|
+
const result = await consumeProcessStdout({
|
|
176
|
+
argv: ["git", ...args],
|
|
177
|
+
cwd: repository.effectiveCwd,
|
|
178
|
+
}, consume);
|
|
179
|
+
const { outcome } = result;
|
|
180
|
+
if (outcome.kind === "terminal" && outcome.code === 0)
|
|
181
|
+
return;
|
|
182
|
+
const status = outcome.kind === "terminal" ? outcome.code : null;
|
|
183
|
+
const stderr = outcome.kind === "terminal" ? outcome.stderr : "";
|
|
184
|
+
const message = outcome.kind === "terminal"
|
|
185
|
+
? `git exited with status ${outcome.code}`
|
|
186
|
+
: outcome.kind === "spawn-error" || outcome.kind === "stream-error"
|
|
187
|
+
? outcome.diagnostic
|
|
188
|
+
: `git process ended with ${outcome.kind}`;
|
|
189
|
+
throw commandError(args, {
|
|
190
|
+
message,
|
|
191
|
+
stderr,
|
|
192
|
+
status,
|
|
193
|
+
pid: result.pid ?? undefined,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
export async function runGitWithEnvironment(repository, args, input, environment) {
|
|
197
|
+
return await executeGit(repository, args, input, environment);
|
|
157
198
|
}
|
|
158
199
|
function assertOid(oid, label) {
|
|
159
200
|
gitObjectId(oid, label);
|
|
@@ -167,10 +208,10 @@ function assertAssertionRef(ref) {
|
|
|
167
208
|
if (ref !== "HEAD")
|
|
168
209
|
assertRef(ref);
|
|
169
210
|
}
|
|
170
|
-
export function readRef(repository, ref) {
|
|
211
|
+
export async function readRef(repository, ref) {
|
|
171
212
|
assertAssertionRef(ref);
|
|
172
213
|
try {
|
|
173
|
-
const oid = runGit(repository, ["rev-parse", "--verify", "--quiet", ref]).toString("utf8").trim();
|
|
214
|
+
const oid = (await runGit(repository, ["rev-parse", "--verify", "--quiet", ref])).toString("utf8").trim();
|
|
174
215
|
if (oid.length === 0)
|
|
175
216
|
return null;
|
|
176
217
|
assertOid(oid, `ref ${ref}`);
|
|
@@ -182,9 +223,9 @@ export function readRef(repository, ref) {
|
|
|
182
223
|
throw error;
|
|
183
224
|
}
|
|
184
225
|
}
|
|
185
|
-
function readTreeForCommit(repository, commit) {
|
|
226
|
+
async function readTreeForCommit(repository, commit) {
|
|
186
227
|
assertOid(commit, "Git commit");
|
|
187
|
-
const tree = runGit(repository, ["show", "-s", "--format=%T", commit]).toString("utf8").trim();
|
|
228
|
+
const tree = (await runGit(repository, ["show", "-s", "--format=%T", commit])).toString("utf8").trim();
|
|
188
229
|
assertOid(tree, "Git tree");
|
|
189
230
|
return tree;
|
|
190
231
|
}
|
|
@@ -206,30 +247,30 @@ function parseTreeEntries(output) {
|
|
|
206
247
|
}
|
|
207
248
|
return entries;
|
|
208
249
|
}
|
|
209
|
-
export function readTreeEntries(repository, tree) {
|
|
250
|
+
export async function readTreeEntries(repository, tree) {
|
|
210
251
|
assertOid(tree, "tree");
|
|
211
|
-
const output = runGit(repository, ["ls-tree", "-r", "-z", "--full-tree", tree]);
|
|
252
|
+
const output = await runGit(repository, ["ls-tree", "-r", "-z", "--full-tree", tree]);
|
|
212
253
|
return parseTreeEntries(output);
|
|
213
254
|
}
|
|
214
|
-
export function readGit(repository) {
|
|
215
|
-
const commit = readRef(repository, GIT_REF);
|
|
255
|
+
export async function readGit(repository) {
|
|
256
|
+
const commit = await readRef(repository, GIT_REF);
|
|
216
257
|
if (commit === null) {
|
|
217
258
|
return { commit: null, tree: null, paths: new Map() };
|
|
218
259
|
}
|
|
219
|
-
const tree = readTreeForCommit(repository, commit);
|
|
220
|
-
const paths = readTreeEntries(repository, tree);
|
|
221
|
-
validateGitFormat(repository, paths);
|
|
260
|
+
const tree = await readTreeForCommit(repository, commit);
|
|
261
|
+
const paths = await readTreeEntries(repository, tree);
|
|
262
|
+
await validateGitFormat(repository, paths);
|
|
222
263
|
return { commit, tree, paths };
|
|
223
264
|
}
|
|
224
265
|
/** Read only the requested private Git paths from one immutable Git tree. */
|
|
225
|
-
export function readGitPaths(repository, requestedPaths) {
|
|
266
|
+
export async function readGitPaths(repository, requestedPaths) {
|
|
226
267
|
for (const path of requestedPaths)
|
|
227
268
|
validPath(path);
|
|
228
|
-
const commit = readRef(repository, GIT_REF);
|
|
269
|
+
const commit = await readRef(repository, GIT_REF);
|
|
229
270
|
if (commit === null)
|
|
230
271
|
return { commit: null, tree: null, paths: new Map() };
|
|
231
|
-
const tree = readTreeForCommit(repository, commit);
|
|
232
|
-
const paths = parseTreeEntries(runGit(repository, [
|
|
272
|
+
const tree = await readTreeForCommit(repository, commit);
|
|
273
|
+
const paths = parseTreeEntries(await runGit(repository, [
|
|
233
274
|
"ls-tree",
|
|
234
275
|
"-z",
|
|
235
276
|
"--full-tree",
|
|
@@ -238,16 +279,16 @@ export function readGitPaths(repository, requestedPaths) {
|
|
|
238
279
|
GIT_FORMAT_PATH,
|
|
239
280
|
...requestedPaths,
|
|
240
281
|
]));
|
|
241
|
-
validateGitFormat(repository, paths);
|
|
282
|
+
await validateGitFormat(repository, paths);
|
|
242
283
|
return { commit, tree, paths };
|
|
243
284
|
}
|
|
244
285
|
/** Add paths from the same immutable Git tree without rereading its ref. */
|
|
245
|
-
export function extendGitPaths(repository, snapshot, requestedPaths) {
|
|
286
|
+
export async function extendGitPaths(repository, snapshot, requestedPaths) {
|
|
246
287
|
for (const path of requestedPaths)
|
|
247
288
|
validPath(path);
|
|
248
289
|
if (snapshot.tree === null || requestedPaths.length === 0)
|
|
249
290
|
return snapshot;
|
|
250
|
-
const additions = parseTreeEntries(runGit(repository, [
|
|
291
|
+
const additions = parseTreeEntries(await runGit(repository, [
|
|
251
292
|
"ls-tree",
|
|
252
293
|
"-z",
|
|
253
294
|
"--full-tree",
|
|
@@ -258,19 +299,19 @@ export function extendGitPaths(repository, snapshot, requestedPaths) {
|
|
|
258
299
|
return { ...snapshot, paths: new Map([...snapshot.paths, ...additions]) };
|
|
259
300
|
}
|
|
260
301
|
/** Expand a targeted immutable snapshot to its complete already-pinned tree. */
|
|
261
|
-
export function expandGitSnapshot(repository, snapshot) {
|
|
302
|
+
export async function expandGitSnapshot(repository, snapshot) {
|
|
262
303
|
if (snapshot.tree === null)
|
|
263
304
|
return snapshot;
|
|
264
|
-
return { ...snapshot, paths: readTreeEntries(repository, snapshot.tree) };
|
|
305
|
+
return { ...snapshot, paths: await readTreeEntries(repository, snapshot.tree) };
|
|
265
306
|
}
|
|
266
|
-
export function writeBlob(repository, bytes) {
|
|
267
|
-
const oid = runGit(repository, ["hash-object", "-w", "--stdin"], bytes).toString("utf8").trim();
|
|
307
|
+
export async function writeBlob(repository, bytes) {
|
|
308
|
+
const oid = (await runGit(repository, ["hash-object", "-w", "--stdin"], bytes)).toString("utf8").trim();
|
|
268
309
|
assertOid(oid, "written blob");
|
|
269
310
|
return oid;
|
|
270
311
|
}
|
|
271
|
-
export function readBlob(repository, oid) {
|
|
312
|
+
export async function readBlob(repository, oid) {
|
|
272
313
|
assertOid(oid, "blob");
|
|
273
|
-
return runGit(repository, ["cat-file", "blob", oid]);
|
|
314
|
+
return await runGit(repository, ["cat-file", "blob", oid]);
|
|
274
315
|
}
|
|
275
316
|
function malformedBatchOutput(detail) {
|
|
276
317
|
throw new GitPlumbingError({
|
|
@@ -279,13 +320,13 @@ function malformedBatchOutput(detail) {
|
|
|
279
320
|
message: `malformed cat-file --batch output: ${detail}`,
|
|
280
321
|
});
|
|
281
322
|
}
|
|
282
|
-
function readObjects(repository, oids) {
|
|
323
|
+
async function readObjects(repository, oids) {
|
|
283
324
|
const unique = [...new Set(oids)];
|
|
284
325
|
for (const oid of unique)
|
|
285
326
|
assertOid(oid, "Git object");
|
|
286
327
|
if (unique.length === 0)
|
|
287
328
|
return new Map();
|
|
288
|
-
const output = runGit(repository, ["cat-file", "--batch"], `${unique.join("\n")}\n`);
|
|
329
|
+
const output = await runGit(repository, ["cat-file", "--batch"], `${unique.join("\n")}\n`);
|
|
289
330
|
const objects = new Map();
|
|
290
331
|
let offset = 0;
|
|
291
332
|
for (const oid of unique) {
|
|
@@ -321,22 +362,22 @@ function readObjects(repository, oids) {
|
|
|
321
362
|
malformedBatchOutput("trailing bytes");
|
|
322
363
|
return objects;
|
|
323
364
|
}
|
|
324
|
-
function validateGitFormat(repository, paths) {
|
|
365
|
+
async function validateGitFormat(repository, paths) {
|
|
325
366
|
const format = paths.get(GIT_FORMAT_PATH);
|
|
326
367
|
if (format === undefined)
|
|
327
368
|
throw new AuthorityCorruptionError(`Git is missing ${GIT_FORMAT_PATH}`);
|
|
328
369
|
if (format.type !== "blob")
|
|
329
370
|
throw new AuthorityCorruptionError(`Git format is not a blob: ${GIT_FORMAT_PATH}`);
|
|
330
|
-
const bytes = readBlob(repository, format.oid);
|
|
371
|
+
const bytes = await readBlob(repository, format.oid);
|
|
331
372
|
if (bytes.toString("utf8") !== GIT_FORMAT_BYTES) {
|
|
332
373
|
throw new AuthorityCorruptionError(`Git format is not current: ${GIT_FORMAT_PATH}`);
|
|
333
374
|
}
|
|
334
375
|
}
|
|
335
|
-
function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
376
|
+
async function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
336
377
|
const byPath = new Map();
|
|
337
378
|
if (baseTree === null)
|
|
338
379
|
return byPath;
|
|
339
|
-
const ancestry = parseTreeEntries(runGit(repository, [
|
|
380
|
+
const ancestry = parseTreeEntries(await runGit(repository, [
|
|
340
381
|
"ls-tree",
|
|
341
382
|
"-z",
|
|
342
383
|
"-t",
|
|
@@ -357,7 +398,7 @@ function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
|
357
398
|
throw new Error(`Git path is both file and directory: ${path}`);
|
|
358
399
|
treeByPath.set(path, entry.oid);
|
|
359
400
|
}
|
|
360
|
-
const objects = readObjects(repository, [...treeByPath.values()]);
|
|
401
|
+
const objects = await readObjects(repository, [...treeByPath.values()]);
|
|
361
402
|
const oidBytes = baseTree.length / 2;
|
|
362
403
|
for (const [path, oid] of treeByPath) {
|
|
363
404
|
const object = objects.get(oid);
|
|
@@ -368,8 +409,8 @@ function baseTreeEntries(repository, baseTree, changedPaths, nodePaths) {
|
|
|
368
409
|
}
|
|
369
410
|
return byPath;
|
|
370
411
|
}
|
|
371
|
-
function writePreparedTrees(repository, prepared) {
|
|
372
|
-
const output = runGit(repository, ["mktree", "--batch"], prepared.map(({ records }) => `${records}\n`).join(""));
|
|
412
|
+
async function writePreparedTrees(repository, prepared) {
|
|
413
|
+
const output = await runGit(repository, ["mktree", "--batch"], prepared.map(({ records }) => `${records}\n`).join(""));
|
|
373
414
|
const written = output.toString("ascii").trimEnd().split("\n");
|
|
374
415
|
if (written.length !== prepared.length) {
|
|
375
416
|
throw new GitPlumbingError({ stderr: output, status: null, message: "malformed mktree --batch output" });
|
|
@@ -384,18 +425,18 @@ function writePreparedTrees(repository, prepared) {
|
|
|
384
425
|
}
|
|
385
426
|
}
|
|
386
427
|
/** Copy only the changed paths' tree ancestors, preserving untouched subtrees by object ID. */
|
|
387
|
-
export function updateGitTree(repository, baseTree, changes) {
|
|
428
|
+
export async function updateGitTree(repository, baseTree, changes) {
|
|
388
429
|
const update = treeUpdate(changes);
|
|
389
|
-
const bases = baseTreeEntries(repository, baseTree, [...changes.keys()], update.nodePaths);
|
|
430
|
+
const bases = await baseTreeEntries(repository, baseTree, [...changes.keys()], update.nodePaths);
|
|
390
431
|
const representativeOid = baseTree ?? [...changes.values()].find((change) => change !== null)?.oid;
|
|
391
432
|
if (representativeOid === undefined)
|
|
392
433
|
throw new Error("cannot write an empty Git tree without an object format");
|
|
393
434
|
const prepared = prepareTreeUpdate({ update, bases, oidBytes: representativeOid.length / 2 });
|
|
394
|
-
writePreparedTrees(repository, prepared.trees);
|
|
435
|
+
await writePreparedTrees(repository, prepared.trees);
|
|
395
436
|
return prepared.root;
|
|
396
437
|
}
|
|
397
438
|
/** Write a tree update from directory entries frozen with the admitted base tree. */
|
|
398
|
-
export function updateGitTreeFromFrozenDirectories(repository, baseTree, directories, changes) {
|
|
439
|
+
export async function updateGitTreeFromFrozenDirectories(repository, baseTree, directories, changes) {
|
|
399
440
|
const update = treeUpdate(changes);
|
|
400
441
|
const representativeOid = baseTree ?? [...changes.values()].find((change) => change !== null)?.oid;
|
|
401
442
|
if (representativeOid === undefined)
|
|
@@ -405,10 +446,10 @@ export function updateGitTreeFromFrozenDirectories(repository, baseTree, directo
|
|
|
405
446
|
bases: directories,
|
|
406
447
|
oidBytes: representativeOid.length / 2,
|
|
407
448
|
});
|
|
408
|
-
writePreparedTrees(repository, prepared.trees);
|
|
449
|
+
await writePreparedTrees(repository, prepared.trees);
|
|
409
450
|
return prepared.root;
|
|
410
451
|
}
|
|
411
|
-
function writeCommitObject(input) {
|
|
452
|
+
async function writeCommitObject(input) {
|
|
412
453
|
const { repository, tree, parents, message, actor, email, at } = input;
|
|
413
454
|
assertOid(tree, "commit tree");
|
|
414
455
|
for (const parent of parents)
|
|
@@ -416,20 +457,20 @@ function writeCommitObject(input) {
|
|
|
416
457
|
const args = ["commit-tree", tree];
|
|
417
458
|
for (const parent of parents)
|
|
418
459
|
args.push("-p", parent);
|
|
419
|
-
const commit = runGitWithEnvironment(repository, args, `${message}\n`, {
|
|
460
|
+
const commit = (await runGitWithEnvironment(repository, args, `${message}\n`, {
|
|
420
461
|
GIT_AUTHOR_NAME: actor,
|
|
421
462
|
GIT_AUTHOR_EMAIL: email,
|
|
422
463
|
GIT_COMMITTER_NAME: actor,
|
|
423
464
|
GIT_COMMITTER_EMAIL: email,
|
|
424
465
|
...(at === undefined ? {} : { GIT_AUTHOR_DATE: at, GIT_COMMITTER_DATE: at }),
|
|
425
|
-
})
|
|
466
|
+
}))
|
|
426
467
|
.toString("utf8")
|
|
427
468
|
.trim();
|
|
428
469
|
assertOid(commit, "written Git commit");
|
|
429
470
|
return commit;
|
|
430
471
|
}
|
|
431
|
-
export function writeCommit(input) {
|
|
432
|
-
return writeCommitObject({
|
|
472
|
+
export async function writeCommit(input) {
|
|
473
|
+
return await writeCommitObject({
|
|
433
474
|
repository: input.repository,
|
|
434
475
|
tree: input.tree,
|
|
435
476
|
parents: input.parent === null ? [] : [input.parent],
|
|
@@ -455,7 +496,7 @@ function refUpdateLine(update, index) {
|
|
|
455
496
|
assertOid(update.expectedOid, `expected oid for ${update.ref}`);
|
|
456
497
|
return `option no-deref\nupdate ${update.ref} ${update.newOid} ${update.expectedOid ?? "0".repeat(update.newOid.length)}`;
|
|
457
498
|
}
|
|
458
|
-
export function updateRefsAtomically(repository, updates, assertions = []) {
|
|
499
|
+
export async function updateRefsAtomically(repository, updates, assertions = []) {
|
|
459
500
|
if (updates.length === 0)
|
|
460
501
|
throw new Error("an atomic ref transaction needs a Git update");
|
|
461
502
|
if (updates.length > 2)
|
|
@@ -473,7 +514,7 @@ export function updateRefsAtomically(repository, updates, assertions = []) {
|
|
|
473
514
|
"",
|
|
474
515
|
];
|
|
475
516
|
try {
|
|
476
|
-
runGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
|
|
517
|
+
await runGit(repository, ["update-ref", "--stdin"], lines.join("\n"));
|
|
477
518
|
return { kind: "published" };
|
|
478
519
|
}
|
|
479
520
|
catch (error) {
|
|
@@ -488,3 +529,34 @@ export function updateRefsAtomically(repository, updates, assertions = []) {
|
|
|
488
529
|
throw error;
|
|
489
530
|
}
|
|
490
531
|
}
|
|
532
|
+
export function decodeGitNumstat(output) {
|
|
533
|
+
const fields = output.toString("utf8").split("\0");
|
|
534
|
+
if (fields.at(-1) !== "")
|
|
535
|
+
throw new Error("Git numstat output is not NUL terminated");
|
|
536
|
+
let filesChanged = 0;
|
|
537
|
+
let insertions = 0;
|
|
538
|
+
let deletions = 0;
|
|
539
|
+
for (let index = 0; index < fields.length - 1; index += 1) {
|
|
540
|
+
const field = fields[index];
|
|
541
|
+
const [added, deleted, path, extra] = field.split("\t");
|
|
542
|
+
if (added === undefined || deleted === undefined || path === undefined || extra !== undefined) {
|
|
543
|
+
throw new Error("Git numstat output is malformed");
|
|
544
|
+
}
|
|
545
|
+
if (path.length === 0) {
|
|
546
|
+
if (fields[index + 1] === undefined || fields[index + 2] === undefined) {
|
|
547
|
+
throw new Error("Git rename numstat output is missing paths");
|
|
548
|
+
}
|
|
549
|
+
index += 2;
|
|
550
|
+
}
|
|
551
|
+
filesChanged += 1;
|
|
552
|
+
insertions += added === "-" ? 0 : Number.parseInt(added, 10);
|
|
553
|
+
deletions += deleted === "-" ? 0 : Number.parseInt(deleted, 10);
|
|
554
|
+
}
|
|
555
|
+
return { filesChanged, insertions, deletions };
|
|
556
|
+
}
|
|
557
|
+
export function decodeGitNameOnly(output) {
|
|
558
|
+
const names = output.toString("utf8").split("\0");
|
|
559
|
+
if (names.at(-1) !== "")
|
|
560
|
+
throw new Error("Git name-only output is not NUL terminated");
|
|
561
|
+
return names.slice(0, -1);
|
|
562
|
+
}
|
|
@@ -2,12 +2,16 @@ import type { SnapshotId } from "../core/facts/types.js";
|
|
|
2
2
|
import { type GitRepository } from "./repository.js";
|
|
3
3
|
export type MaterializedScratchCandidate = Readonly<{
|
|
4
4
|
cwd: string;
|
|
5
|
-
dispose: () => WorktreeLeak | null
|
|
5
|
+
dispose: () => Promise<WorktreeLeak | null>;
|
|
6
|
+
}>;
|
|
7
|
+
export type CollectableScratchWorktree = Readonly<{
|
|
8
|
+
path: string;
|
|
9
|
+
release(): void;
|
|
6
10
|
}>;
|
|
7
11
|
export type WorktreeLeak = Readonly<{
|
|
8
12
|
path: string;
|
|
9
13
|
diagnostic: string;
|
|
10
14
|
}>;
|
|
11
|
-
export declare function
|
|
15
|
+
export declare function collectableScratchWorktrees(paths: Iterable<string>): Promise<readonly CollectableScratchWorktree[]>;
|
|
12
16
|
/** Materialize a disposable candidate worktree and own only its removal. */
|
|
13
|
-
export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId): MaterializedScratchCandidate
|
|
17
|
+
export declare function materializeScratchCandidate(repository: GitRepository, candidate: SnapshotId): Promise<MaterializedScratchCandidate>;
|
package/build/src/git/scratch.js
CHANGED
|
@@ -1,58 +1,62 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
|
+
import { tryAcquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
|
|
5
6
|
import { gitObjectIdForSnapshot } from "./identity.js";
|
|
6
7
|
import { runGit } from "./repository.js";
|
|
7
|
-
import { currentProcessIdentity, probeProcessIdentity, } from "../runtime/proc/run.js";
|
|
8
8
|
const SCRATCH_PREFIX = "keiyaku-v4-verify-";
|
|
9
|
-
const SCRATCH_ROOT =
|
|
9
|
+
const SCRATCH_ROOT = await realpath(tmpdir());
|
|
10
|
+
const SCRATCH_PATTERN = /^keiyaku-v4-verify-([0-9a-f]{24})$/;
|
|
10
11
|
function scratchPath() {
|
|
11
|
-
|
|
12
|
-
const identity = Buffer.from(owner.spawnedAt, "utf8").toString("base64url");
|
|
13
|
-
return join(SCRATCH_ROOT, `${SCRATCH_PREFIX}${owner.pid}-${identity}-${randomBytes(12).toString("hex")}`);
|
|
12
|
+
return join(SCRATCH_ROOT, `${SCRATCH_PREFIX}${randomBytes(12).toString("hex")}`);
|
|
14
13
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
if (!path.startsWith(prefix))
|
|
14
|
+
function ownershipLockPath(path) {
|
|
15
|
+
if (dirname(path) !== SCRATCH_ROOT)
|
|
18
16
|
return null;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
if (match === null)
|
|
22
|
-
return null;
|
|
23
|
-
const pid = Number(match[1]);
|
|
24
|
-
if (!Number.isSafeInteger(pid))
|
|
25
|
-
return null;
|
|
26
|
-
let spawnedAt;
|
|
27
|
-
try {
|
|
28
|
-
spawnedAt = Buffer.from(match[2], "base64url").toString("utf8");
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
return spawnedAt.length === 0 ? null : { pid, spawnedAt };
|
|
17
|
+
const match = SCRATCH_PATTERN.exec(basename(path));
|
|
18
|
+
return match === null ? null : join(SCRATCH_ROOT, `.${SCRATCH_PREFIX}${match[1]}.owner.sqlite`);
|
|
34
19
|
}
|
|
35
|
-
export function
|
|
36
|
-
const
|
|
20
|
+
export async function collectableScratchWorktrees(paths) {
|
|
21
|
+
const collectable = [];
|
|
37
22
|
for (const path of paths) {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
23
|
+
const lockPath = ownershipLockPath(path);
|
|
24
|
+
if (lockPath === null)
|
|
40
25
|
continue;
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
|
|
26
|
+
const lock = await tryAcquireSqliteTransactionLock({ path: lockPath, mode: "exclusive" });
|
|
27
|
+
if (lock !== null)
|
|
28
|
+
collectable.push({ path, release: () => lock.close() });
|
|
44
29
|
}
|
|
45
|
-
return
|
|
30
|
+
return collectable;
|
|
46
31
|
}
|
|
47
32
|
/** Materialize a disposable candidate worktree and own only its removal. */
|
|
48
|
-
export function materializeScratchCandidate(repository, candidate) {
|
|
33
|
+
export async function materializeScratchCandidate(repository, candidate) {
|
|
49
34
|
const cwd = scratchPath();
|
|
50
|
-
|
|
35
|
+
const lockPath = ownershipLockPath(cwd);
|
|
36
|
+
if (lockPath === null)
|
|
37
|
+
throw new Error("scratch ownership lock path is invalid");
|
|
38
|
+
const ownership = await tryAcquireSqliteTransactionLock({
|
|
39
|
+
path: lockPath,
|
|
40
|
+
mode: "exclusive",
|
|
41
|
+
});
|
|
42
|
+
if (ownership === null)
|
|
43
|
+
throw new Error("new scratch ownership lock is unexpectedly held");
|
|
44
|
+
try {
|
|
45
|
+
await runGit(repository, ["worktree", "add", "--detach", cwd, gitObjectIdForSnapshot(candidate)]);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
ownership.close();
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
let disposed = false;
|
|
51
52
|
return {
|
|
52
53
|
cwd,
|
|
53
|
-
dispose: () => {
|
|
54
|
+
dispose: async () => {
|
|
55
|
+
if (disposed)
|
|
56
|
+
return null;
|
|
57
|
+
disposed = true;
|
|
54
58
|
try {
|
|
55
|
-
runGit(repository, ["worktree", "remove", "--force", cwd]);
|
|
59
|
+
await runGit(repository, ["worktree", "remove", "--force", cwd]);
|
|
56
60
|
return null;
|
|
57
61
|
}
|
|
58
62
|
catch (error) {
|
|
@@ -61,6 +65,9 @@ export function materializeScratchCandidate(repository, candidate) {
|
|
|
61
65
|
diagnostic: error instanceof Error ? error.message : String(error),
|
|
62
66
|
};
|
|
63
67
|
}
|
|
68
|
+
finally {
|
|
69
|
+
ownership.close();
|
|
70
|
+
}
|
|
64
71
|
},
|
|
65
72
|
};
|
|
66
73
|
}
|
|
@@ -48,9 +48,45 @@ export type TargetPlacementPreparation = Readonly<{
|
|
|
48
48
|
kind: "refused";
|
|
49
49
|
refusal: TargetPlacementRefusal;
|
|
50
50
|
}>;
|
|
51
|
-
export declare function observeTargetHead(repository: GitRepository, target: string): SnapshotId | null
|
|
51
|
+
export declare function observeTargetHead(repository: GitRepository, target: string): Promise<SnapshotId | null>;
|
|
52
52
|
export declare function acquireTargetPlacementFence(repository: GitRepository, target: string): Promise<HeldSqliteTransactionLock>;
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
export type TargetedContractCoordinates = ContractState["coordinates"] & Readonly<{
|
|
54
|
+
target: string;
|
|
55
|
+
}>;
|
|
56
|
+
export type TargetPlacementObservationInput = Readonly<{
|
|
57
|
+
contractId: ContractId;
|
|
58
|
+
coordinates: TargetedContractCoordinates;
|
|
59
|
+
predecessor: SnapshotId;
|
|
60
|
+
candidate: SnapshotId;
|
|
61
|
+
}>;
|
|
62
|
+
export type TargetPlacementObservation = Readonly<{
|
|
63
|
+
kind: "ready";
|
|
64
|
+
arms: readonly FollowArm[];
|
|
65
|
+
}> | Readonly<{
|
|
66
|
+
kind: "refused";
|
|
67
|
+
refusal: TargetPlacementRefusal;
|
|
68
|
+
}>;
|
|
69
|
+
/** Observe whether registered target checkouts can follow a predecessor-to-candidate movement. */
|
|
70
|
+
export declare function observeTargetPlacement(repository: GitRepository, input: TargetPlacementObservationInput): Promise<TargetPlacementObservation>;
|
|
71
|
+
export type AuditTargetAnswer = Readonly<{
|
|
72
|
+
kind: "placeable";
|
|
73
|
+
ref: string;
|
|
74
|
+
head: SnapshotId;
|
|
75
|
+
}> | Readonly<{
|
|
76
|
+
kind: "moved";
|
|
77
|
+
ref: string;
|
|
78
|
+
expected: SnapshotId;
|
|
79
|
+
observed: SnapshotId | null;
|
|
80
|
+
}> | Readonly<{
|
|
81
|
+
kind: "refused";
|
|
82
|
+
refusal: TargetPlacementRefusal;
|
|
83
|
+
}> | Readonly<{
|
|
84
|
+
kind: "failed";
|
|
85
|
+
diagnostic: string;
|
|
86
|
+
}>;
|
|
87
|
+
/** Adjudicate the complete post-Verification audit target answer without placing. */
|
|
88
|
+
export declare function adjudicateAuditTarget(repository: GitRepository, input: TargetPlacementObservationInput): Promise<AuditTargetAnswer>;
|
|
89
|
+
export declare function prepareTargetPlacement(repository: GitRepository, state: ContractState, target: RefOperation): Promise<TargetPlacementPreparation>;
|
|
90
|
+
export declare function followTargetPlacement(repository: GitRepository, prepared: PreparedTargetPlacement): Promise<TargetPlacementPhysicalResult>;
|
|
91
|
+
export declare function recoverTargetPlacement(repository: GitRepository, state: ContractState): Promise<TargetPlacementPhysicalResult>;
|
|
56
92
|
export {};
|