@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,11 +1,12 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
1
2
|
import { resolve } from "node:path";
|
|
2
3
|
import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
|
|
3
4
|
import { gitObjectId, gitObjectIdForSnapshot, gitRefLocator, mintSnapshotId } from "./identity.js";
|
|
4
5
|
import { currentBranch } from "./observe.js";
|
|
5
|
-
import { commonGitDirectory, GitPlumbingError, readRef, registeredWorktrees, runGit, } from "./repository.js";
|
|
6
|
-
import { captureWorkspaceTree
|
|
7
|
-
export function observeTargetHead(repository, target) {
|
|
8
|
-
const value = readRef(repository, target);
|
|
6
|
+
import { commonGitDirectory, consumeGitStdout, GitPlumbingError, readRef, registeredWorktrees, runGit, } from "./repository.js";
|
|
7
|
+
import { captureWorkspaceTree } from "./workspace.js";
|
|
8
|
+
export async function observeTargetHead(repository, target) {
|
|
9
|
+
const value = await readRef(repository, target);
|
|
9
10
|
return value === null ? null : mintSnapshotId(value);
|
|
10
11
|
}
|
|
11
12
|
function diagnostic(error) {
|
|
@@ -17,115 +18,198 @@ function nulPaths(bytes) {
|
|
|
17
18
|
throw new Error("Git path output is not NUL terminated");
|
|
18
19
|
return [...new Set(fields.slice(0, -1))].sort();
|
|
19
20
|
}
|
|
20
|
-
function gitPaths(repository, path, args) {
|
|
21
|
-
return nulPaths(runGit(repository, ["-C", path, ...args]));
|
|
21
|
+
async function gitPaths(repository, path, args) {
|
|
22
|
+
return nulPaths(await runGit(repository, ["-C", path, ...args]));
|
|
22
23
|
}
|
|
23
|
-
function
|
|
24
|
-
return
|
|
24
|
+
function literalPath(path) {
|
|
25
|
+
return `:(literal)${path}`;
|
|
26
|
+
}
|
|
27
|
+
async function commitTree(repository, snapshot) {
|
|
28
|
+
return gitObjectId((await runGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
|
|
25
29
|
.toString("utf8")
|
|
26
30
|
.trim(), "commit tree");
|
|
27
31
|
}
|
|
28
|
-
function
|
|
29
|
-
return
|
|
32
|
+
function checkoutRefusal(contractId, target, path, reason, paths) {
|
|
33
|
+
return { kind: "checkout-not-followable", contractId, target: target.target, path, reason, paths };
|
|
30
34
|
}
|
|
31
|
-
function
|
|
32
|
-
return
|
|
35
|
+
async function changedPaths(repository, path, predecessor, candidate, filter) {
|
|
36
|
+
return await gitPaths(repository, path, [
|
|
37
|
+
"diff",
|
|
38
|
+
"--name-only",
|
|
39
|
+
"--no-renames",
|
|
40
|
+
...(filter === undefined ? [] : [`--diff-filter=${filter}`]),
|
|
41
|
+
"-z",
|
|
42
|
+
predecessor,
|
|
43
|
+
candidate,
|
|
44
|
+
]);
|
|
33
45
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
async function dryRunRefusal(input, scopes) {
|
|
47
|
+
const { repository, contractId, target, path, predecessor, candidate } = input;
|
|
48
|
+
const changed = await changedPaths(repository, path, predecessor, candidate);
|
|
49
|
+
const pathspecs = changed.map(literalPath);
|
|
50
|
+
if (pathspecs.length === 0)
|
|
51
|
+
return checkoutRefusal(contractId, target, path, "conflict", []);
|
|
52
|
+
const staged = await gitPaths(repository, path, [
|
|
53
|
+
"diff",
|
|
54
|
+
"--cached",
|
|
55
|
+
"--name-only",
|
|
56
|
+
"-z",
|
|
57
|
+
predecessor,
|
|
58
|
+
"--",
|
|
59
|
+
...pathspecs,
|
|
60
|
+
]);
|
|
61
|
+
if (staged.length > 0)
|
|
62
|
+
return checkoutRefusal(contractId, target, path, "staged", staged);
|
|
63
|
+
const dirty = await gitPaths(repository, path, ["diff-files", "--name-only", "-z", "--", ...pathspecs]);
|
|
64
|
+
if (dirty.length > 0)
|
|
65
|
+
return checkoutRefusal(contractId, target, path, "conflict", dirty);
|
|
66
|
+
const unmerged = await gitPaths(repository, path, ["diff", "--name-only", "--diff-filter=U", "-z", "--", ...pathspecs]);
|
|
67
|
+
if (unmerged.length > 0)
|
|
68
|
+
return checkoutRefusal(contractId, target, path, "conflict", unmerged);
|
|
69
|
+
return await untrackedRefusalWithinScopes(input, scopes, false)
|
|
70
|
+
?? checkoutRefusal(contractId, target, path, "conflict", []);
|
|
42
71
|
}
|
|
43
|
-
function
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
async function physicalScope(worktree, candidatePath) {
|
|
73
|
+
const components = candidatePath.split("/");
|
|
74
|
+
for (let index = 0; index < components.length; index += 1) {
|
|
75
|
+
const scope = components.slice(0, index + 1).join("/");
|
|
76
|
+
let stat;
|
|
77
|
+
try {
|
|
78
|
+
stat = await lstat(resolve(worktree, ...components.slice(0, index + 1)));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const code = error.code;
|
|
82
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
83
|
+
return null;
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
if (!stat.isDirectory())
|
|
87
|
+
return { path: scope, kind: "leaf" };
|
|
88
|
+
if (index + 1 === components.length)
|
|
89
|
+
return { path: scope, kind: "directory" };
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
48
92
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
93
|
+
async function candidateEntryIsBlob(repository, path, candidate, candidatePath) {
|
|
94
|
+
const output = await runGit(repository, ["-C", path, "ls-tree", "-z", candidate, "--", literalPath(candidatePath)]);
|
|
95
|
+
const records = output.toString("utf8").split("\0").filter((record) => record.length > 0);
|
|
96
|
+
if (records.length !== 1)
|
|
97
|
+
throw new Error(`candidate path has no unique Git entry: ${candidatePath}`);
|
|
98
|
+
const separator = records[0].indexOf("\t");
|
|
99
|
+
if (separator < 0)
|
|
100
|
+
throw new Error(`candidate path has a malformed Git entry: ${candidatePath}`);
|
|
101
|
+
const fields = records[0].slice(0, separator).split(" ");
|
|
102
|
+
return fields[1] === "blob";
|
|
51
103
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
104
|
+
async function destructionScopes(repository, path, candidate, writes) {
|
|
105
|
+
const scopes = new Map();
|
|
106
|
+
for (const write of writes) {
|
|
107
|
+
const scope = await physicalScope(path, write);
|
|
108
|
+
if (scope === null)
|
|
109
|
+
continue;
|
|
110
|
+
if (scope.kind === "directory" && !(await candidateEntryIsBlob(repository, path, candidate, write)))
|
|
111
|
+
continue;
|
|
112
|
+
scopes.set(scope.path, scope);
|
|
113
|
+
}
|
|
114
|
+
return [...scopes.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
54
115
|
}
|
|
55
|
-
function
|
|
56
|
-
return
|
|
116
|
+
function untrackedArgs(ignored) {
|
|
117
|
+
return [
|
|
118
|
+
"ls-files",
|
|
119
|
+
"--others",
|
|
120
|
+
...(ignored ? ["--ignored"] : []),
|
|
121
|
+
"--exclude-standard",
|
|
122
|
+
"--directory",
|
|
123
|
+
"--no-empty",
|
|
124
|
+
"-z",
|
|
125
|
+
];
|
|
57
126
|
}
|
|
58
|
-
function
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
127
|
+
async function untrackedRefusalWithinScopes(input, scopes, ignored) {
|
|
128
|
+
const { repository, contractId, target, path } = input;
|
|
129
|
+
const args = untrackedArgs(ignored);
|
|
130
|
+
const leaves = scopes.filter((scope) => scope.kind === "leaf");
|
|
131
|
+
if (leaves.length > 0) {
|
|
132
|
+
const collisions = await gitPaths(repository, path, [
|
|
133
|
+
...args,
|
|
134
|
+
"--",
|
|
135
|
+
...leaves.map((scope) => literalPath(scope.path)),
|
|
136
|
+
]);
|
|
137
|
+
if (collisions.length > 0)
|
|
138
|
+
return checkoutRefusal(contractId, target, path, "untracked", collisions);
|
|
65
139
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (conflicts.length > 0) {
|
|
73
|
-
return {
|
|
74
|
-
kind: "checkout-not-followable",
|
|
75
|
-
contractId,
|
|
76
|
-
target: target.target,
|
|
77
|
-
path,
|
|
78
|
-
reason: "conflict",
|
|
79
|
-
paths: conflicts,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const additions = gitPaths(repository, path, ["diff", "--name-only", "--diff-filter=A", "-z", predecessor, candidate]);
|
|
83
|
-
const untracked = gitPaths(repository, path, ["ls-files", "--others", "-z"]);
|
|
84
|
-
const collisions = intersection(additions, untracked);
|
|
85
|
-
if (collisions.length > 0) {
|
|
86
|
-
return {
|
|
87
|
-
kind: "checkout-not-followable",
|
|
88
|
-
contractId,
|
|
89
|
-
target: target.target,
|
|
140
|
+
for (const scope of scopes) {
|
|
141
|
+
if (scope.kind !== "directory")
|
|
142
|
+
continue;
|
|
143
|
+
let found = false;
|
|
144
|
+
await consumeGitStdout(repository, [
|
|
145
|
+
"-C",
|
|
90
146
|
path,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
147
|
+
...args,
|
|
148
|
+
"--",
|
|
149
|
+
literalPath(scope.path),
|
|
150
|
+
], (chunk) => {
|
|
151
|
+
if (chunk.length > 0)
|
|
152
|
+
found = true;
|
|
153
|
+
});
|
|
154
|
+
if (found)
|
|
155
|
+
return checkoutRefusal(contractId, target, path, "untracked", [scope.path]);
|
|
94
156
|
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
async function indexMatchesTreeOnPaths(repository, path, tree, paths) {
|
|
160
|
+
return (await gitPaths(repository, path, ["diff-index", "--cached", "--name-only", "-z", tree, "--", ...paths])).length === 0;
|
|
161
|
+
}
|
|
162
|
+
async function workspaceMatchesTreeOnPaths(repository, path, tree, workspaceTree, paths) {
|
|
163
|
+
return (await gitPaths(repository, path, ["diff", "--name-only", "-z", tree, workspaceTree, "--", ...paths])).length === 0;
|
|
164
|
+
}
|
|
165
|
+
async function sourceWorktree(repository) {
|
|
166
|
+
return resolve((await runGit(repository, ["rev-parse", "--show-toplevel"])).toString("utf8").trim());
|
|
167
|
+
}
|
|
168
|
+
async function ordinaryPrecheck(repository, contractId, target, path) {
|
|
169
|
+
const predecessor = gitObjectIdForSnapshot(target.expectedOid);
|
|
170
|
+
const candidate = gitObjectIdForSnapshot(target.newOid);
|
|
171
|
+
const observation = { repository, contractId, target, path, predecessor, candidate };
|
|
172
|
+
let dryRunFailed = false;
|
|
95
173
|
try {
|
|
96
|
-
runGit(repository, ["-C", path, "read-tree", "--dry-run", "-m", "-u", predecessor, candidate]);
|
|
174
|
+
await runGit(repository, ["-C", path, "read-tree", "--dry-run", "-m", "-u", predecessor, candidate]);
|
|
97
175
|
}
|
|
98
176
|
catch (error) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return checkoutRefusal(contractId, target, path, reason, error);
|
|
177
|
+
if (!(error instanceof GitPlumbingError))
|
|
178
|
+
throw error;
|
|
179
|
+
dryRunFailed = true;
|
|
103
180
|
}
|
|
104
|
-
|
|
181
|
+
const writes = await changedPaths(repository, path, predecessor, candidate, "ACMRT");
|
|
182
|
+
const scopes = await destructionScopes(repository, path, candidate, writes);
|
|
183
|
+
if (dryRunFailed)
|
|
184
|
+
return await dryRunRefusal(observation, scopes);
|
|
185
|
+
return await untrackedRefusalWithinScopes(observation, scopes, true);
|
|
105
186
|
}
|
|
106
187
|
export async function acquireTargetPlacementFence(repository, target) {
|
|
107
188
|
const locator = gitRefLocator(target);
|
|
108
|
-
return acquireSqliteTransactionLock({
|
|
189
|
+
return await acquireSqliteTransactionLock({
|
|
109
190
|
path: resolve(commonGitDirectory(repository), "keiyaku", "locks", "target-placement", `${locator}.sqlite`),
|
|
110
191
|
mode: "immediate",
|
|
111
192
|
});
|
|
112
193
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
194
|
+
/** Observe whether registered target checkouts can follow a predecessor-to-candidate movement. */
|
|
195
|
+
export async function observeTargetPlacement(repository, input) {
|
|
196
|
+
const target = {
|
|
197
|
+
target: input.coordinates.target,
|
|
198
|
+
expectedOid: input.predecessor,
|
|
199
|
+
newOid: input.candidate,
|
|
200
|
+
};
|
|
201
|
+
const worktrees = (await registeredWorktrees(repository))
|
|
118
202
|
.filter((worktree) => worktree.branch === target.target)
|
|
119
203
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
120
|
-
const hereSource =
|
|
204
|
+
const hereSource = input.coordinates.workspace === "here" ? await sourceWorktree(repository) : null;
|
|
121
205
|
if (hereSource !== null) {
|
|
122
|
-
const branch = currentBranch(repository, hereSource);
|
|
206
|
+
const branch = await currentBranch(repository, hereSource);
|
|
123
207
|
if (branch !== target.target) {
|
|
124
208
|
return {
|
|
125
209
|
kind: "refused",
|
|
126
210
|
refusal: {
|
|
127
211
|
kind: "workspace-not-on-target",
|
|
128
|
-
contractId:
|
|
212
|
+
contractId: input.contractId,
|
|
129
213
|
target: target.target,
|
|
130
214
|
branch,
|
|
131
215
|
},
|
|
@@ -136,7 +220,7 @@ export function prepareTargetPlacement(repository, state, target) {
|
|
|
136
220
|
for (const worktree of worktrees) {
|
|
137
221
|
const kind = hereSource !== null && resolve(worktree.path) === hereSource ? "here" : "ordinary";
|
|
138
222
|
if (kind === "ordinary") {
|
|
139
|
-
const refusal = ordinaryPrecheck(repository,
|
|
223
|
+
const refusal = await ordinaryPrecheck(repository, input.contractId, target, worktree.path);
|
|
140
224
|
if (refusal !== null)
|
|
141
225
|
return { kind: "refused", refusal };
|
|
142
226
|
}
|
|
@@ -145,9 +229,45 @@ export function prepareTargetPlacement(repository, state, target) {
|
|
|
145
229
|
if (hereSource !== null && !arms.some((arm) => arm.kind === "here")) {
|
|
146
230
|
throw new Error("targeted here workspace is not a registered checkout of its target");
|
|
147
231
|
}
|
|
148
|
-
return { kind: "
|
|
232
|
+
return { kind: "ready", arms };
|
|
233
|
+
}
|
|
234
|
+
/** Adjudicate the complete post-Verification audit target answer without placing. */
|
|
235
|
+
export async function adjudicateAuditTarget(repository, input) {
|
|
236
|
+
const ref = input.coordinates.target;
|
|
237
|
+
const expected = input.predecessor;
|
|
238
|
+
try {
|
|
239
|
+
const head = await observeTargetHead(repository, ref);
|
|
240
|
+
if (head !== expected)
|
|
241
|
+
return { kind: "moved", ref, expected, observed: head };
|
|
242
|
+
const follow = await observeTargetPlacement(repository, input);
|
|
243
|
+
const observed = await observeTargetHead(repository, ref);
|
|
244
|
+
if (observed !== expected)
|
|
245
|
+
return { kind: "moved", ref, expected, observed };
|
|
246
|
+
return follow.kind === "refused"
|
|
247
|
+
? { kind: "refused", refusal: follow.refusal }
|
|
248
|
+
: { kind: "placeable", ref, head: expected };
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
if (!(error instanceof GitPlumbingError))
|
|
252
|
+
throw error;
|
|
253
|
+
return { kind: "failed", diagnostic: error.message };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export async function prepareTargetPlacement(repository, state, target) {
|
|
257
|
+
if (state.coordinates.target !== target.target || state.delivery?.data.integration.snapshot !== target.newOid) {
|
|
258
|
+
throw new Error("placement state does not match its offered target movement");
|
|
259
|
+
}
|
|
260
|
+
const observation = await observeTargetPlacement(repository, {
|
|
261
|
+
contractId: state.id,
|
|
262
|
+
coordinates: { ...state.coordinates, target: target.target },
|
|
263
|
+
predecessor: target.expectedOid,
|
|
264
|
+
candidate: target.newOid,
|
|
265
|
+
});
|
|
266
|
+
return observation.kind === "refused"
|
|
267
|
+
? observation
|
|
268
|
+
: { kind: "prepared", placement: { target, arms: observation.arms } };
|
|
149
269
|
}
|
|
150
|
-
export function followTargetPlacement(repository, prepared) {
|
|
270
|
+
export async function followTargetPlacement(repository, prepared) {
|
|
151
271
|
const effects = [];
|
|
152
272
|
const lag = [];
|
|
153
273
|
const predecessor = gitObjectIdForSnapshot(prepared.target.expectedOid);
|
|
@@ -155,9 +275,9 @@ export function followTargetPlacement(repository, prepared) {
|
|
|
155
275
|
for (const arm of prepared.arms) {
|
|
156
276
|
try {
|
|
157
277
|
if (arm.kind === "here")
|
|
158
|
-
runGit(repository, ["-C", arm.path, "read-tree", candidate]);
|
|
278
|
+
await runGit(repository, ["-C", arm.path, "read-tree", candidate]);
|
|
159
279
|
else
|
|
160
|
-
runGit(repository, ["-C", arm.path, "read-tree", "-m", "-u", predecessor, candidate]);
|
|
280
|
+
await runGit(repository, ["-C", arm.path, "read-tree", "-m", "-u", predecessor, candidate]);
|
|
161
281
|
effects.push({ kind: "target-checkout", path: arm.path, target: prepared.target.target, action: "followed" });
|
|
162
282
|
}
|
|
163
283
|
catch (error) {
|
|
@@ -174,23 +294,23 @@ export function followTargetPlacement(repository, prepared) {
|
|
|
174
294
|
function recoveryLag(path, target, detail) {
|
|
175
295
|
return { kind: "target-checkout-retained", path, target, diagnostic: detail };
|
|
176
296
|
}
|
|
177
|
-
function recoverCheckout(input) {
|
|
297
|
+
async function recoverCheckout(input) {
|
|
178
298
|
const { repository, path, predecessor, candidate, predecessorTree, candidateTree } = input;
|
|
179
|
-
const changedPaths = gitPaths(repository, path, ["diff", "--name-only", "-z", predecessorTree, candidateTree]);
|
|
299
|
+
const changedPaths = await gitPaths(repository, path, ["diff", "--name-only", "-z", predecessorTree, candidateTree]);
|
|
180
300
|
if (changedPaths.length === 0)
|
|
181
301
|
return "complete";
|
|
182
|
-
const workspaceTree = captureWorkspaceTree(repository, path).tree;
|
|
183
|
-
const candidateIndex = indexMatchesTreeOnPaths(repository, path, candidateTree, changedPaths);
|
|
184
|
-
const candidateWorkspace = workspaceMatchesTreeOnPaths(repository, path, candidateTree, workspaceTree, changedPaths);
|
|
302
|
+
const workspaceTree = (await captureWorkspaceTree(repository, path)).tree;
|
|
303
|
+
const candidateIndex = await indexMatchesTreeOnPaths(repository, path, candidateTree, changedPaths);
|
|
304
|
+
const candidateWorkspace = await workspaceMatchesTreeOnPaths(repository, path, candidateTree, workspaceTree, changedPaths);
|
|
185
305
|
if (candidateIndex && candidateWorkspace)
|
|
186
306
|
return "complete";
|
|
187
307
|
if (workspaceTree === candidateTree) {
|
|
188
|
-
runGit(repository, ["-C", path, "read-tree", gitObjectIdForSnapshot(candidate)]);
|
|
308
|
+
await runGit(repository, ["-C", path, "read-tree", gitObjectIdForSnapshot(candidate)]);
|
|
189
309
|
return "recovered";
|
|
190
310
|
}
|
|
191
|
-
const predecessorIndex = indexMatchesTreeOnPaths(repository, path, predecessorTree, changedPaths);
|
|
311
|
+
const predecessorIndex = await indexMatchesTreeOnPaths(repository, path, predecessorTree, changedPaths);
|
|
192
312
|
if (predecessorIndex && candidateWorkspace) {
|
|
193
|
-
runGit(repository, [
|
|
313
|
+
await runGit(repository, [
|
|
194
314
|
"-C",
|
|
195
315
|
path,
|
|
196
316
|
"read-tree",
|
|
@@ -201,10 +321,10 @@ function recoverCheckout(input) {
|
|
|
201
321
|
]);
|
|
202
322
|
return "recovered";
|
|
203
323
|
}
|
|
204
|
-
const predecessorWorkspace = workspaceMatchesTreeOnPaths(repository, path, predecessorTree, workspaceTree, changedPaths);
|
|
324
|
+
const predecessorWorkspace = await workspaceMatchesTreeOnPaths(repository, path, predecessorTree, workspaceTree, changedPaths);
|
|
205
325
|
if (!predecessorIndex || !predecessorWorkspace)
|
|
206
326
|
return "retained";
|
|
207
|
-
runGit(repository, [
|
|
327
|
+
await runGit(repository, [
|
|
208
328
|
"-C",
|
|
209
329
|
path,
|
|
210
330
|
"read-tree",
|
|
@@ -215,23 +335,23 @@ function recoverCheckout(input) {
|
|
|
215
335
|
]);
|
|
216
336
|
return "recovered";
|
|
217
337
|
}
|
|
218
|
-
export function recoverTargetPlacement(repository, state) {
|
|
338
|
+
export async function recoverTargetPlacement(repository, state) {
|
|
219
339
|
const target = state.coordinates.target;
|
|
220
340
|
const delivery = state.delivery;
|
|
221
341
|
if (state.terminal?.kind !== "claimed" || target === undefined || delivery === null)
|
|
222
342
|
return { effects: [], lag: [] };
|
|
223
|
-
if (readRef(repository, target) !== delivery.data.integration.snapshot)
|
|
343
|
+
if (await readRef(repository, target) !== delivery.data.integration.snapshot)
|
|
224
344
|
return { effects: [], lag: [] };
|
|
225
|
-
const candidateTree = commitTree(repository, delivery.data.integration.snapshot);
|
|
226
|
-
const predecessorTree = commitTree(repository, delivery.data.integration.predecessor);
|
|
227
|
-
const worktrees = registeredWorktrees(repository)
|
|
345
|
+
const candidateTree = await commitTree(repository, delivery.data.integration.snapshot);
|
|
346
|
+
const predecessorTree = await commitTree(repository, delivery.data.integration.predecessor);
|
|
347
|
+
const worktrees = (await registeredWorktrees(repository))
|
|
228
348
|
.filter((worktree) => worktree.branch === target)
|
|
229
349
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
230
350
|
const effects = [];
|
|
231
351
|
const lag = [];
|
|
232
352
|
for (const worktree of worktrees) {
|
|
233
353
|
try {
|
|
234
|
-
const recovery = recoverCheckout({
|
|
354
|
+
const recovery = await recoverCheckout({
|
|
235
355
|
repository,
|
|
236
356
|
path: worktree.path,
|
|
237
357
|
predecessor: delivery.data.integration.predecessor,
|
|
@@ -6,6 +6,7 @@ import { captureWorkspaceTree } from "./workspace.js";
|
|
|
6
6
|
export type TenderCaptureCoordinates = Readonly<{
|
|
7
7
|
contractId: ContractId;
|
|
8
8
|
coordinates: ContractCoordinates;
|
|
9
|
+
place?: string;
|
|
9
10
|
}>;
|
|
10
11
|
export type TenderCaptureRefusal = Readonly<{
|
|
11
12
|
kind: "worktree-missing";
|
|
@@ -34,15 +35,15 @@ export type TenderCapture = Readonly<{
|
|
|
34
35
|
tree: GitObjectId;
|
|
35
36
|
head: SnapshotId;
|
|
36
37
|
dirty: boolean;
|
|
37
|
-
changes: ReturnType<typeof captureWorkspaceTree
|
|
38
|
+
changes: Awaited<ReturnType<typeof captureWorkspaceTree>>["changes"];
|
|
38
39
|
}>;
|
|
39
40
|
/** Capture the complete workspace tree through Git's private index mechanics. */
|
|
40
|
-
export declare function captureTender(repository: GitRepository, input: TenderCaptureCoordinates): Preparation<TenderCapture, TenderCaptureRefusal
|
|
41
|
-
export declare function dirtyTenderRefusal(repository: GitRepository, contractId: ContractId, tender: TenderCapture): DirtyWorkspaceRefusal
|
|
42
|
-
export declare function dirtyTenderDelta(repository: GitRepository, tender: TenderCapture): WorkspaceDirtyDelta | undefined
|
|
41
|
+
export declare function captureTender(repository: GitRepository, input: TenderCaptureCoordinates): Promise<Preparation<TenderCapture, TenderCaptureRefusal>>;
|
|
42
|
+
export declare function dirtyTenderRefusal(repository: GitRepository, contractId: ContractId, tender: TenderCapture): Promise<DirtyWorkspaceRefusal>;
|
|
43
|
+
export declare function dirtyTenderDelta(repository: GitRepository, tender: TenderCapture): Promise<WorkspaceDirtyDelta | undefined>;
|
|
43
44
|
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
44
45
|
export declare function materializeTenderSnapshot(repository: GitRepository, tender: TenderCapture, input: Readonly<{
|
|
45
46
|
contractId: ContractId;
|
|
46
47
|
title: string;
|
|
47
48
|
message?: string;
|
|
48
|
-
}>): SnapshotId
|
|
49
|
+
}>): Promise<SnapshotId>;
|
package/build/src/git/tender.js
CHANGED
|
@@ -1,82 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
2
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
3
|
-
import { registeredWorktreePaths, runGit, runGitWithEnvironment } from "./repository.js";
|
|
4
|
-
import { captureWorkspaceTree,
|
|
5
|
-
function workspaceExists(repository, workspace, path) {
|
|
3
|
+
import { decodeGitNumstat, registeredWorktreePaths, runGit, runGitWithEnvironment } from "./repository.js";
|
|
4
|
+
import { captureWorkspaceTree, worktreePath } from "./workspace.js";
|
|
5
|
+
async function workspaceExists(repository, workspace, path) {
|
|
6
|
+
const exists = await access(path).then(() => true, () => false);
|
|
6
7
|
return workspace === "here"
|
|
7
|
-
|| (
|
|
8
|
+
|| (exists && (await registeredWorktreePaths(repository)).includes(path));
|
|
8
9
|
}
|
|
9
|
-
function workspaceFor(repository,
|
|
10
|
-
|
|
10
|
+
function workspaceFor(repository, input) {
|
|
11
|
+
if (input.coordinates.workspace === "here")
|
|
12
|
+
return repository.effectiveCwd;
|
|
13
|
+
return input.place === undefined ? undefined : worktreePath(repository, input.place);
|
|
11
14
|
}
|
|
12
15
|
/** Capture the complete workspace tree through Git's private index mechanics. */
|
|
13
|
-
export function captureTender(repository, input) {
|
|
14
|
-
const workspace = workspaceFor(repository, input
|
|
15
|
-
if (!workspaceExists(repository, input.coordinates.workspace, workspace)) {
|
|
16
|
+
export async function captureTender(repository, input) {
|
|
17
|
+
const workspace = workspaceFor(repository, input);
|
|
18
|
+
if (workspace === undefined || !(await workspaceExists(repository, input.coordinates.workspace, workspace))) {
|
|
16
19
|
return { kind: "refused", refusal: { kind: "worktree-missing", contractId: input.contractId } };
|
|
17
20
|
}
|
|
18
|
-
return { kind: "prepared", data: captureWorkspaceTree(repository, workspace) };
|
|
21
|
+
return { kind: "prepared", data: await captureWorkspaceTree(repository, workspace) };
|
|
19
22
|
}
|
|
20
|
-
function dirtyShortStat(repository, tender) {
|
|
21
|
-
|
|
23
|
+
async function dirtyShortStat(repository, tender) {
|
|
24
|
+
return decodeGitNumstat(await runGit(repository, [
|
|
22
25
|
"diff",
|
|
23
26
|
"--numstat",
|
|
24
27
|
"-z",
|
|
25
28
|
gitObjectIdForSnapshot(tender.head),
|
|
26
29
|
tender.tree,
|
|
27
|
-
])
|
|
28
|
-
if (fields.at(-1) !== "")
|
|
29
|
-
throw new Error("Git numstat output is not NUL terminated");
|
|
30
|
-
let filesChanged = 0;
|
|
31
|
-
let insertions = 0;
|
|
32
|
-
let deletions = 0;
|
|
33
|
-
for (let index = 0; index < fields.length - 1; index += 1) {
|
|
34
|
-
const field = fields[index];
|
|
35
|
-
const [added, deleted, path, extra] = field.split("\t");
|
|
36
|
-
if (added === undefined || deleted === undefined || path === undefined || extra !== undefined) {
|
|
37
|
-
throw new Error("Git numstat output is malformed");
|
|
38
|
-
}
|
|
39
|
-
if (path.length === 0) {
|
|
40
|
-
if (fields[index + 1] === undefined || fields[index + 2] === undefined) {
|
|
41
|
-
throw new Error("Git rename numstat output is missing paths");
|
|
42
|
-
}
|
|
43
|
-
index += 2;
|
|
44
|
-
}
|
|
45
|
-
filesChanged += 1;
|
|
46
|
-
insertions += added === "-" ? 0 : Number.parseInt(added, 10);
|
|
47
|
-
deletions += deleted === "-" ? 0 : Number.parseInt(deleted, 10);
|
|
48
|
-
}
|
|
49
|
-
return { filesChanged, insertions, deletions };
|
|
30
|
+
]));
|
|
50
31
|
}
|
|
51
|
-
export function dirtyTenderRefusal(repository, contractId, tender) {
|
|
32
|
+
export async function dirtyTenderRefusal(repository, contractId, tender) {
|
|
52
33
|
return {
|
|
53
34
|
kind: "dirty-workspace",
|
|
54
35
|
contractId,
|
|
55
36
|
...tender.changes,
|
|
56
|
-
shortStat: dirtyShortStat(repository, tender),
|
|
37
|
+
shortStat: await dirtyShortStat(repository, tender),
|
|
57
38
|
};
|
|
58
39
|
}
|
|
59
|
-
export function dirtyTenderDelta(repository, tender) {
|
|
40
|
+
export async function dirtyTenderDelta(repository, tender) {
|
|
60
41
|
if (!tender.dirty)
|
|
61
42
|
return undefined;
|
|
62
43
|
const { staged, unstaged, untracked } = tender.changes;
|
|
63
|
-
return { staged, unstaged, untracked, shortStat: dirtyShortStat(repository, tender) };
|
|
44
|
+
return { staged, unstaged, untracked, shortStat: await dirtyShortStat(repository, tender) };
|
|
64
45
|
}
|
|
65
46
|
function commitMessage(contractId, title, message) {
|
|
66
47
|
const subject = message ?? `${contractId}: ${title}`;
|
|
67
48
|
return `${subject}\n\nKeiyaku-Contract: ${contractId}\n`;
|
|
68
49
|
}
|
|
69
50
|
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
70
|
-
export function materializeTenderSnapshot(repository, tender, input) {
|
|
51
|
+
export async function materializeTenderSnapshot(repository, tender, input) {
|
|
71
52
|
if (!tender.dirty)
|
|
72
53
|
return tender.head;
|
|
73
|
-
const commit = runGitWithEnvironment(repository, ["commit-tree", tender.tree, "-p", gitObjectIdForSnapshot(tender.head)], commitMessage(input.contractId, input.title, input.message), {
|
|
54
|
+
const commit = (await runGitWithEnvironment(repository, ["commit-tree", tender.tree, "-p", gitObjectIdForSnapshot(tender.head)], commitMessage(input.contractId, input.title, input.message), {
|
|
74
55
|
GIT_AUTHOR_NAME: "Keiyaku",
|
|
75
56
|
GIT_AUTHOR_EMAIL: "keiyaku@localhost",
|
|
76
57
|
GIT_COMMITTER_NAME: "Keiyaku",
|
|
77
58
|
GIT_COMMITTER_EMAIL: "keiyaku@localhost",
|
|
78
59
|
GIT_AUTHOR_DATE: "Thu, 01 Jan 1970 00:00:00 +0000",
|
|
79
60
|
GIT_COMMITTER_DATE: "Thu, 01 Jan 1970 00:00:00 +0000",
|
|
80
|
-
}).toString("utf8").trim();
|
|
61
|
+
})).toString("utf8").trim();
|
|
81
62
|
return mintSnapshotId(commit);
|
|
82
63
|
}
|
|
@@ -21,4 +21,4 @@ export type TerminalSealObject = Readonly<{
|
|
|
21
21
|
}>;
|
|
22
22
|
export declare function terminalSealSnapshots(state: ContractState): readonly SnapshotId[];
|
|
23
23
|
export declare function terminalSealExpectations(state: ContractState, objects: ReadonlyMap<GitOid, TerminalSealObject>): TerminalSealExpectations;
|
|
24
|
-
export declare function unsealedBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations): UnsealedBytes | null
|
|
24
|
+
export declare function unsealedBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations): Promise<UnsealedBytes | null>;
|
|
@@ -57,14 +57,14 @@ export function terminalSealExpectations(state, objects) {
|
|
|
57
57
|
treeBySnapshot,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
function changedPaths(repository, left, right) {
|
|
61
|
-
const fields = runGit(repository, ["diff", "--name-only", "-z", left, right]).toString("utf8").split("\0");
|
|
60
|
+
async function changedPaths(repository, left, right) {
|
|
61
|
+
const fields = (await runGit(repository, ["diff", "--name-only", "-z", left, right])).toString("utf8").split("\0");
|
|
62
62
|
if (fields.at(-1) !== "")
|
|
63
63
|
throw new Error("Git tree diff paths are not NUL terminated");
|
|
64
64
|
return fields.slice(0, -1).sort();
|
|
65
65
|
}
|
|
66
|
-
export function unsealedBytes(repository, path, expected) {
|
|
67
|
-
const workspace = captureWorkspaceTree(repository, path);
|
|
66
|
+
export async function unsealedBytes(repository, path, expected) {
|
|
67
|
+
const workspace = await captureWorkspaceTree(repository, path);
|
|
68
68
|
const headIsSealed = expected.heads.includes(workspace.head);
|
|
69
69
|
if (workspace.changes.submodules.length > 0) {
|
|
70
70
|
return {
|
|
@@ -76,7 +76,9 @@ export function unsealedBytes(repository, path, expected) {
|
|
|
76
76
|
}
|
|
77
77
|
if (headIsSealed && expected.trees.includes(workspace.tree))
|
|
78
78
|
return null;
|
|
79
|
-
const alternatives =
|
|
79
|
+
const alternatives = [];
|
|
80
|
+
for (const tree of expected.trees)
|
|
81
|
+
alternatives.push(await changedPaths(repository, tree, workspace.tree));
|
|
80
82
|
alternatives.sort((left, right) => left.length - right.length || left.join("\0").localeCompare(right.join("\0")));
|
|
81
83
|
return {
|
|
82
84
|
kind: "unsealed-bytes",
|