@astrosheep/keiyaku 4.0.0 → 4.0.1
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 +2 -9
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +11 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +9 -6
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +94 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +162 -45
- package/build/src/akuma/akuma.d.ts +33 -25
- package/build/src/akuma/akuma.js +93 -61
- package/build/src/akuma/archetype.d.ts +8 -0
- package/build/src/akuma/archetype.js +28 -9
- package/build/src/akuma/body.js +196 -84
- package/build/src/akuma/coordinate.d.ts +9 -0
- package/build/src/akuma/coordinate.js +22 -0
- package/build/src/akuma/heart/facts.d.ts +67 -15
- package/build/src/akuma/heart/facts.js +3 -3
- package/build/src/akuma/heart/index.d.ts +41 -29
- package/build/src/akuma/heart/index.js +72 -193
- package/build/src/akuma/heart/rows.d.ts +30 -53
- package/build/src/akuma/heart/rows.js +91 -111
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +55 -8
- package/build/src/akuma/heart/storage.d.ts +33 -0
- package/build/src/akuma/heart/storage.js +164 -0
- package/build/src/akuma/heart/tells.d.ts +12 -0
- package/build/src/akuma/heart/tells.js +79 -0
- package/build/src/akuma/heart/timeline.d.ts +15 -0
- package/build/src/akuma/heart/timeline.js +86 -0
- package/build/src/akuma/index.d.ts +4 -2
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/projection.d.ts +97 -0
- package/build/src/akuma/projection.js +145 -0
- package/build/src/akuma/provider.d.ts +57 -11
- package/build/src/akuma/provider.js +57 -37
- package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
- package/build/src/akuma/providers/claude/events.js +185 -0
- package/build/src/akuma/providers/claude/index.d.ts +46 -0
- package/build/src/akuma/providers/claude/index.js +265 -0
- package/build/src/akuma/providers/claude/input.d.ts +13 -0
- package/build/src/akuma/providers/claude/input.js +109 -0
- package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
- package/build/src/akuma/providers/codex-app-server/events.js +12 -12
- package/build/src/akuma/providers/codex-app-server/index.d.ts +13 -11
- package/build/src/akuma/providers/codex-app-server/index.js +73 -22
- package/build/src/akuma/providers/index.js +22 -8
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +48 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +116 -0
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +32 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +203 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +41 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +62 -0
- package/build/src/akuma/providers/pi/events.d.ts +33 -0
- package/build/src/akuma/providers/pi/events.js +128 -0
- package/build/src/akuma/providers/pi/index.d.ts +11 -0
- package/build/src/akuma/providers/pi/index.js +162 -0
- package/build/src/akuma/requests.d.ts +1 -0
- package/build/src/akuma/requests.js +5 -2
- package/build/src/alias/index.d.ts +4 -3
- package/build/src/alias/index.js +2 -3
- package/build/src/body/render.js +2 -1
- package/build/src/body/shape.js +7 -1
- package/build/src/body/verification.js +19 -4
- package/build/src/cli/accepted.d.ts +2 -0
- package/build/src/cli/accepted.js +2 -1
- package/build/src/cli/commands/akuma-invoke.d.ts +42 -18
- package/build/src/cli/commands/akuma-invoke.js +94 -50
- package/build/src/cli/commands/akuma.d.ts +15 -7
- package/build/src/cli/commands/akuma.js +94 -56
- package/build/src/cli/commands/contract.d.ts +142 -0
- package/build/src/cli/commands/contract.js +14 -0
- package/build/src/cli/commands/install.js +2 -0
- package/build/src/cli/commands/task-invoke.d.ts +3 -1
- package/build/src/cli/commands/task-invoke.js +27 -2
- package/build/src/cli/commands/task.js +2 -2
- package/build/src/cli/invoke.d.ts +6 -1
- package/build/src/cli/invoke.js +241 -43
- package/build/src/cli/main.js +20 -1
- package/build/src/cli/parse.d.ts +13 -116
- package/build/src/cli/parse.js +51 -78
- package/build/src/cli/render/akuma-tool.d.ts +2 -0
- package/build/src/cli/render/akuma-tool.js +6 -2
- package/build/src/cli/render/akuma.d.ts +2 -1
- package/build/src/cli/render/akuma.js +157 -217
- package/build/src/cli/render/catalog.d.ts +2 -0
- package/build/src/cli/render/catalog.js +16 -0
- package/build/src/cli/render/contract.d.ts +1 -1
- package/build/src/cli/render/contract.js +38 -5
- package/build/src/cli/render/kanshi.js +53 -5
- package/build/src/cli/render/refusal.js +17 -0
- package/build/src/cli/render/terminal.d.ts +13 -0
- package/build/src/cli/render/terminal.js +75 -0
- package/build/src/cli/render/text.js +5 -0
- package/build/src/cli/result.d.ts +15 -3
- package/build/src/contract-worktree.d.ts +43 -0
- package/build/src/contract-worktree.js +213 -0
- package/build/src/coordination/durable-file.d.ts +3 -0
- package/build/src/coordination/durable-file.js +44 -1
- package/build/src/core/facts/codec.js +23 -4
- package/build/src/core/facts/eligibility.d.ts +1 -10
- package/build/src/core/facts/eligibility.js +0 -49
- package/build/src/core/facts/observation.d.ts +3 -0
- package/build/src/core/facts/observation.js +12 -0
- package/build/src/core/facts/types.d.ts +10 -3
- package/build/src/core/subject.js +2 -2
- package/build/src/core/verbs/amend.js +12 -16
- package/build/src/core/verbs/bind.js +2 -16
- package/build/src/core/verbs/deliver.js +20 -4
- package/build/src/core/verbs/placement.js +3 -8
- package/build/src/dispatch/index.d.ts +3 -1
- package/build/src/dispatch/index.js +14 -5
- package/build/src/duration.d.ts +10 -0
- package/build/src/duration.js +22 -0
- package/build/src/git/admission.d.ts +2 -2
- package/build/src/git/admission.js +26 -16
- package/build/src/git/hooks.d.ts +15 -0
- package/build/src/git/hooks.js +57 -5
- package/build/src/git/identity.d.ts +5 -1
- package/build/src/git/identity.js +23 -4
- package/build/src/git/integration.d.ts +49 -0
- package/build/src/git/integration.js +214 -0
- package/build/src/git/observe.d.ts +25 -13
- package/build/src/git/observe.js +175 -72
- package/build/src/git/read-observation.d.ts +41 -0
- package/build/src/git/read-observation.js +361 -0
- package/build/src/git/reconcile.d.ts +15 -5
- package/build/src/git/reconcile.js +238 -85
- package/build/src/git/repository.d.ts +19 -4
- package/build/src/git/repository.js +77 -43
- package/build/src/git/scratch.d.ts +13 -0
- package/build/src/git/scratch.js +66 -0
- package/build/src/git/target-placement.d.ts +56 -0
- package/build/src/git/target-placement.js +255 -0
- package/build/src/git/tender.d.ts +48 -0
- package/build/src/git/tender.js +82 -0
- package/build/src/git/terminal-seal.d.ts +24 -0
- package/build/src/git/terminal-seal.js +87 -0
- package/build/src/git/tree.d.ts +2 -2
- package/build/src/git/tree.js +4 -0
- package/build/src/git/workspace.d.ts +21 -0
- package/build/src/git/workspace.js +104 -0
- package/build/src/identity/selector.d.ts +6 -0
- package/build/src/identity/selector.js +19 -0
- package/build/src/index.d.ts +4 -2
- package/build/src/index.js +2 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.d.ts +5 -2
- package/build/src/kanshi/read.js +154 -45
- package/build/src/kanshi/report.d.ts +31 -6
- package/build/src/kanshi/select.js +1 -1
- package/build/src/library/address.d.ts +48 -0
- package/build/src/library/address.js +134 -0
- package/build/src/library/akuma-creation.d.ts +10 -6
- package/build/src/library/akuma-creation.js +12 -11
- package/build/src/library/bind.d.ts +21 -0
- package/build/src/library/bind.js +56 -0
- package/build/src/library/catalog.d.ts +58 -0
- package/build/src/library/catalog.js +72 -0
- package/build/src/library/configuration.d.ts +6 -15
- package/build/src/library/configuration.js +14 -17
- package/build/src/library/contract.d.ts +21 -46
- package/build/src/library/contract.js +193 -183
- package/build/src/library/delivery.d.ts +23 -0
- package/build/src/library/delivery.js +21 -0
- package/build/src/library/fleet.d.ts +65 -0
- package/build/src/library/fleet.js +189 -0
- package/build/src/library/input.d.ts +3 -0
- package/build/src/library/input.js +19 -0
- package/build/src/library/keiyaku.d.ts +16 -2
- package/build/src/library/keiyaku.js +11 -1
- package/build/src/library/mutation.d.ts +38 -0
- package/build/src/library/mutation.js +39 -0
- package/build/src/library/refusal.d.ts +21 -0
- package/build/src/library/refusal.js +29 -0
- package/build/src/library/region.d.ts +2 -1
- package/build/src/library/region.js +3 -3
- package/build/src/library/repo.d.ts +4 -1
- package/build/src/library/repo.js +31 -16
- package/build/src/protocol/attempt.d.ts +16 -3
- package/build/src/protocol/attempt.js +39 -8
- package/build/src/protocol/bind.d.ts +9 -2
- package/build/src/protocol/bind.js +47 -39
- package/build/src/protocol/intent.d.ts +42 -18
- package/build/src/protocol/intent.js +36 -75
- package/build/src/protocol/operations.d.ts +93 -35
- package/build/src/protocol/operations.js +235 -123
- package/build/src/protocol/outcome.d.ts +7 -2
- package/build/src/protocol/outcome.js +9 -3
- package/build/src/protocol/placement.d.ts +30 -0
- package/build/src/protocol/placement.js +114 -0
- package/build/src/protocol/read/audit.d.ts +9 -3
- package/build/src/protocol/read/audit.js +25 -9
- package/build/src/protocol/read/documents.d.ts +2 -2
- package/build/src/protocol/read/documents.js +4 -4
- package/build/src/protocol/read/status.d.ts +11 -5
- package/build/src/protocol/read/status.js +28 -15
- package/build/src/protocol/run.d.ts +49 -7
- package/build/src/protocol/run.js +53 -25
- package/build/src/runtime/proc/line-rpc.d.ts +1 -0
- package/build/src/runtime/proc/line-rpc.js +21 -0
- package/build/src/runtime/proc/run.d.ts +18 -2
- package/build/src/runtime/proc/run.js +34 -8
- package/build/src/settings.d.ts +6 -0
- package/build/src/settings.js +29 -14
- package/build/src/settlement/fence.d.ts +9 -0
- package/build/src/settlement/fence.js +23 -0
- package/build/src/settlement/holder.d.ts +25 -4
- package/build/src/settlement/holder.js +102 -28
- package/build/src/settlement/settle.d.ts +8 -0
- package/build/src/settlement/settle.js +102 -28
- package/build/src/task/board.d.ts +20 -2
- package/build/src/task/board.js +11 -1
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +2 -2
- package/build/src/task/context.js +2 -7
- package/build/src/task/index.d.ts +9 -9
- package/build/src/task/index.js +15 -12
- package/build/src/task/operations.d.ts +31 -12
- package/build/src/task/operations.js +8 -4
- package/build/src/task/store.d.ts +4 -6
- package/build/src/task/store.js +3 -3
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/execution.d.ts +49 -0
- package/build/src/verification/execution.js +104 -0
- package/build/src/world.d.ts +13 -0
- package/build/src/world.js +103 -0
- package/package.json +7 -4
- package/build/src/akuma/activity.d.ts +0 -66
- package/build/src/akuma/activity.js +0 -139
- package/build/src/akuma/providers/claude.js +0 -305
- package/build/src/context-root.d.ts +0 -4
- package/build/src/context-root.js +0 -15
- package/build/src/git/delivery.d.ts +0 -20
- package/build/src/git/delivery.js +0 -109
- package/build/src/git/verification.d.ts +0 -14
- package/build/src/git/verification.js +0 -56
- package/build/src/verification/producer.d.ts +0 -23
- package/build/src/verification/producer.js +0 -54
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Preparation } from "../core/decide.js";
|
|
2
|
+
import type { ContractCoordinates, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
|
+
import { type GitObjectId } from "./identity.js";
|
|
4
|
+
import { type GitRepository } from "./repository.js";
|
|
5
|
+
import { captureWorkspaceTree } from "./workspace.js";
|
|
6
|
+
export type TenderCaptureCoordinates = Readonly<{
|
|
7
|
+
contractId: ContractId;
|
|
8
|
+
coordinates: ContractCoordinates;
|
|
9
|
+
}>;
|
|
10
|
+
export type TenderCaptureRefusal = Readonly<{
|
|
11
|
+
kind: "worktree-missing";
|
|
12
|
+
contractId: ContractId;
|
|
13
|
+
}>;
|
|
14
|
+
export type WorkspaceDirtyDelta = Readonly<{
|
|
15
|
+
staged: readonly string[];
|
|
16
|
+
unstaged: readonly string[];
|
|
17
|
+
untracked: readonly string[];
|
|
18
|
+
shortStat: Readonly<{
|
|
19
|
+
filesChanged: number;
|
|
20
|
+
insertions: number;
|
|
21
|
+
deletions: number;
|
|
22
|
+
}>;
|
|
23
|
+
}>;
|
|
24
|
+
export type DirtyWorkspaceRefusal = Readonly<{
|
|
25
|
+
kind: "dirty-workspace";
|
|
26
|
+
contractId: ContractId;
|
|
27
|
+
staged: readonly string[];
|
|
28
|
+
unstaged: readonly string[];
|
|
29
|
+
untracked: readonly string[];
|
|
30
|
+
submodules: readonly string[];
|
|
31
|
+
shortStat: WorkspaceDirtyDelta["shortStat"];
|
|
32
|
+
}>;
|
|
33
|
+
export type TenderCapture = Readonly<{
|
|
34
|
+
tree: GitObjectId;
|
|
35
|
+
head: SnapshotId;
|
|
36
|
+
dirty: boolean;
|
|
37
|
+
changes: ReturnType<typeof captureWorkspaceTree>["changes"];
|
|
38
|
+
}>;
|
|
39
|
+
/** 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;
|
|
43
|
+
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
44
|
+
export declare function materializeTenderSnapshot(repository: GitRepository, tender: TenderCapture, input: Readonly<{
|
|
45
|
+
contractId: ContractId;
|
|
46
|
+
title: string;
|
|
47
|
+
message?: string;
|
|
48
|
+
}>): SnapshotId;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
3
|
+
import { registeredWorktreePaths, runGit, runGitWithEnvironment } from "./repository.js";
|
|
4
|
+
import { captureWorkspaceTree, deliveryWorktreePath } from "./workspace.js";
|
|
5
|
+
function workspaceExists(repository, workspace, path) {
|
|
6
|
+
return workspace === "here"
|
|
7
|
+
|| (existsSync(path) && registeredWorktreePaths(repository).includes(path));
|
|
8
|
+
}
|
|
9
|
+
function workspaceFor(repository, id, workspace) {
|
|
10
|
+
return workspace === "worktree" ? deliveryWorktreePath(repository, id) : repository.effectiveCwd;
|
|
11
|
+
}
|
|
12
|
+
/** Capture the complete workspace tree through Git's private index mechanics. */
|
|
13
|
+
export function captureTender(repository, input) {
|
|
14
|
+
const workspace = workspaceFor(repository, input.contractId, input.coordinates.workspace);
|
|
15
|
+
if (!workspaceExists(repository, input.coordinates.workspace, workspace)) {
|
|
16
|
+
return { kind: "refused", refusal: { kind: "worktree-missing", contractId: input.contractId } };
|
|
17
|
+
}
|
|
18
|
+
return { kind: "prepared", data: captureWorkspaceTree(repository, workspace) };
|
|
19
|
+
}
|
|
20
|
+
function dirtyShortStat(repository, tender) {
|
|
21
|
+
const fields = runGit(repository, [
|
|
22
|
+
"diff",
|
|
23
|
+
"--numstat",
|
|
24
|
+
"-z",
|
|
25
|
+
gitObjectIdForSnapshot(tender.head),
|
|
26
|
+
tender.tree,
|
|
27
|
+
]).toString("utf8").split("\0");
|
|
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 };
|
|
50
|
+
}
|
|
51
|
+
export function dirtyTenderRefusal(repository, contractId, tender) {
|
|
52
|
+
return {
|
|
53
|
+
kind: "dirty-workspace",
|
|
54
|
+
contractId,
|
|
55
|
+
...tender.changes,
|
|
56
|
+
shortStat: dirtyShortStat(repository, tender),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function dirtyTenderDelta(repository, tender) {
|
|
60
|
+
if (!tender.dirty)
|
|
61
|
+
return undefined;
|
|
62
|
+
const { staged, unstaged, untracked } = tender.changes;
|
|
63
|
+
return { staged, unstaged, untracked, shortStat: dirtyShortStat(repository, tender) };
|
|
64
|
+
}
|
|
65
|
+
function commitMessage(contractId, title, message) {
|
|
66
|
+
const subject = message ?? `${contractId}: ${title}`;
|
|
67
|
+
return `${subject}\n\nKeiyaku-Contract: ${contractId}\n`;
|
|
68
|
+
}
|
|
69
|
+
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
70
|
+
export function materializeTenderSnapshot(repository, tender, input) {
|
|
71
|
+
if (!tender.dirty)
|
|
72
|
+
return tender.head;
|
|
73
|
+
const commit = runGitWithEnvironment(repository, ["commit-tree", tender.tree, "-p", gitObjectIdForSnapshot(tender.head)], commitMessage(input.contractId, input.title, input.message), {
|
|
74
|
+
GIT_AUTHOR_NAME: "Keiyaku",
|
|
75
|
+
GIT_AUTHOR_EMAIL: "keiyaku@localhost",
|
|
76
|
+
GIT_COMMITTER_NAME: "Keiyaku",
|
|
77
|
+
GIT_COMMITTER_EMAIL: "keiyaku@localhost",
|
|
78
|
+
GIT_AUTHOR_DATE: "Thu, 01 Jan 1970 00:00:00 +0000",
|
|
79
|
+
GIT_COMMITTER_DATE: "Thu, 01 Jan 1970 00:00:00 +0000",
|
|
80
|
+
}).toString("utf8").trim();
|
|
81
|
+
return mintSnapshotId(commit);
|
|
82
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ContractState, SnapshotId } from "../core/facts/types.js";
|
|
2
|
+
import { type GitObjectId } from "./identity.js";
|
|
3
|
+
import { type GitOid, type GitRepository } from "./repository.js";
|
|
4
|
+
export type UnsealedBytes = Readonly<{
|
|
5
|
+
kind: "unsealed-bytes";
|
|
6
|
+
path: string;
|
|
7
|
+
paths: readonly string[];
|
|
8
|
+
head?: SnapshotId;
|
|
9
|
+
}>;
|
|
10
|
+
export type TerminalSealExpectations = Readonly<{
|
|
11
|
+
heads: readonly SnapshotId[];
|
|
12
|
+
trees: readonly GitObjectId[];
|
|
13
|
+
treeBySnapshot: ReadonlyMap<SnapshotId, GitObjectId>;
|
|
14
|
+
}>;
|
|
15
|
+
export type TerminalSealObject = Readonly<{
|
|
16
|
+
kind: "present";
|
|
17
|
+
type: string;
|
|
18
|
+
bytes: Buffer;
|
|
19
|
+
}> | Readonly<{
|
|
20
|
+
kind: "missing";
|
|
21
|
+
}>;
|
|
22
|
+
export declare function terminalSealSnapshots(state: ContractState): readonly SnapshotId[];
|
|
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;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { gitObjectId, gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
2
|
+
import { runGit } from "./repository.js";
|
|
3
|
+
import { captureWorkspaceTree } from "./workspace.js";
|
|
4
|
+
export function terminalSealSnapshots(state) {
|
|
5
|
+
return [...new Set([
|
|
6
|
+
state.coordinates.start,
|
|
7
|
+
...(state.delivery === null ? [] : [
|
|
8
|
+
state.delivery.data.tenderSnapshot,
|
|
9
|
+
state.delivery.data.integration.snapshot,
|
|
10
|
+
]),
|
|
11
|
+
])];
|
|
12
|
+
}
|
|
13
|
+
function commitMetadata(snapshot, result) {
|
|
14
|
+
if (result === undefined || result.kind === "missing") {
|
|
15
|
+
throw new Error(`terminal seal commit is missing: ${snapshot}`);
|
|
16
|
+
}
|
|
17
|
+
if (result.type !== "commit")
|
|
18
|
+
throw new Error(`terminal seal snapshot is not a commit: ${snapshot}`);
|
|
19
|
+
const headerEnd = result.bytes.indexOf("\n\n");
|
|
20
|
+
if (headerEnd < 0)
|
|
21
|
+
throw new Error(`terminal seal commit has no headers: ${snapshot}`);
|
|
22
|
+
const headers = result.bytes.subarray(0, headerEnd).toString("ascii").split("\n");
|
|
23
|
+
const treeHeader = headers[0];
|
|
24
|
+
if (treeHeader === undefined || !treeHeader.startsWith("tree ")) {
|
|
25
|
+
throw new Error(`terminal seal commit has no root tree: ${snapshot}`);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
tree: gitObjectId(treeHeader.slice("tree ".length), "terminal seal tree"),
|
|
29
|
+
parents: headers
|
|
30
|
+
.filter((header) => header.startsWith("parent "))
|
|
31
|
+
.map((header) => mintSnapshotId(header.slice("parent ".length))),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function terminalSealExpectations(state, objects) {
|
|
35
|
+
const snapshots = terminalSealSnapshots(state);
|
|
36
|
+
const metadata = new Map(snapshots.map((snapshot) => [
|
|
37
|
+
snapshot,
|
|
38
|
+
commitMetadata(snapshot, objects.get(gitObjectIdForSnapshot(snapshot))),
|
|
39
|
+
]));
|
|
40
|
+
const treeBySnapshot = new Map([...metadata].map(([snapshot, commit]) => [snapshot, commit.tree]));
|
|
41
|
+
if (state.delivery === null) {
|
|
42
|
+
return { heads: [state.coordinates.start], trees: [...treeBySnapshot.values()], treeBySnapshot };
|
|
43
|
+
}
|
|
44
|
+
const tender = state.delivery.data.tenderSnapshot;
|
|
45
|
+
const tenderMetadata = metadata.get(tender);
|
|
46
|
+
if (tenderMetadata === undefined)
|
|
47
|
+
throw new Error(`terminal seal metadata was not resolved: ${tender}`);
|
|
48
|
+
const [tenderParent] = tenderMetadata.parents;
|
|
49
|
+
return {
|
|
50
|
+
heads: [...new Set([
|
|
51
|
+
state.coordinates.start,
|
|
52
|
+
...(tenderParent === undefined ? [] : [tenderParent]),
|
|
53
|
+
tender,
|
|
54
|
+
state.delivery.data.integration.snapshot,
|
|
55
|
+
])],
|
|
56
|
+
trees: [...treeBySnapshot.values()],
|
|
57
|
+
treeBySnapshot,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function changedPaths(repository, left, right) {
|
|
61
|
+
const fields = runGit(repository, ["diff", "--name-only", "-z", left, right]).toString("utf8").split("\0");
|
|
62
|
+
if (fields.at(-1) !== "")
|
|
63
|
+
throw new Error("Git tree diff paths are not NUL terminated");
|
|
64
|
+
return fields.slice(0, -1).sort();
|
|
65
|
+
}
|
|
66
|
+
export function unsealedBytes(repository, path, expected) {
|
|
67
|
+
const workspace = captureWorkspaceTree(repository, path);
|
|
68
|
+
const headIsSealed = expected.heads.includes(workspace.head);
|
|
69
|
+
if (workspace.changes.submodules.length > 0) {
|
|
70
|
+
return {
|
|
71
|
+
kind: "unsealed-bytes",
|
|
72
|
+
path,
|
|
73
|
+
paths: workspace.changes.submodules,
|
|
74
|
+
...(headIsSealed ? {} : { head: workspace.head }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (headIsSealed && expected.trees.includes(workspace.tree))
|
|
78
|
+
return null;
|
|
79
|
+
const alternatives = expected.trees.map((tree) => changedPaths(repository, tree, workspace.tree));
|
|
80
|
+
alternatives.sort((left, right) => left.length - right.length || left.join("\0").localeCompare(right.join("\0")));
|
|
81
|
+
return {
|
|
82
|
+
kind: "unsealed-bytes",
|
|
83
|
+
path,
|
|
84
|
+
paths: alternatives[0] ?? [],
|
|
85
|
+
...(headIsSealed ? {} : { head: workspace.head }),
|
|
86
|
+
};
|
|
87
|
+
}
|
package/build/src/git/tree.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type TreeChange = Readonly<{
|
|
|
7
7
|
oid: string;
|
|
8
8
|
mode?: string;
|
|
9
9
|
type?: string;
|
|
10
|
-
}
|
|
10
|
+
}> | null;
|
|
11
11
|
type MutableTreeNode = Readonly<{
|
|
12
12
|
files: Map<string, TreeChange>;
|
|
13
13
|
directories: Map<string, MutableTreeNode>;
|
|
@@ -25,7 +25,7 @@ export declare function treeUpdate(changes: ReadonlyMap<string, TreeChange>): Tr
|
|
|
25
25
|
export declare function parseTreeObject(bytes: Buffer, oidBytes: number): Map<string, TreeEntry>;
|
|
26
26
|
export declare function prepareTreeUpdate(input: Readonly<{
|
|
27
27
|
update: TreeUpdate;
|
|
28
|
-
bases: ReadonlyMap<string,
|
|
28
|
+
bases: ReadonlyMap<string, ReadonlyMap<string, TreeEntry>>;
|
|
29
29
|
oidBytes: number;
|
|
30
30
|
}>): Readonly<{
|
|
31
31
|
root: string;
|
package/build/src/git/tree.js
CHANGED
|
@@ -86,6 +86,10 @@ function record(name, change) {
|
|
|
86
86
|
function prepareNode(node, path, bases, oidBytes, prepared) {
|
|
87
87
|
const entries = new Map(bases.get(path) ?? []);
|
|
88
88
|
for (const [name, change] of node.files) {
|
|
89
|
+
if (change === null) {
|
|
90
|
+
entries.delete(name);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
89
93
|
entries.set(name, {
|
|
90
94
|
oid: change.oid,
|
|
91
95
|
mode: change.mode ?? "100644",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ContractId, SnapshotId } from "../core/facts/types.js";
|
|
2
|
+
import { type GitObjectId } from "./identity.js";
|
|
3
|
+
import { type GitRepository } from "./repository.js";
|
|
4
|
+
export type WorkspaceChanges = Readonly<{
|
|
5
|
+
staged: readonly string[];
|
|
6
|
+
unstaged: readonly string[];
|
|
7
|
+
untracked: readonly string[];
|
|
8
|
+
submodules: readonly string[];
|
|
9
|
+
}>;
|
|
10
|
+
export type WorkspaceTree = Readonly<{
|
|
11
|
+
tree: GitObjectId;
|
|
12
|
+
head: SnapshotId;
|
|
13
|
+
dirty: boolean;
|
|
14
|
+
changes: WorkspaceChanges;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function deliveryWorktreePath(repository: GitRepository, contract: ContractId): string;
|
|
17
|
+
export declare function withPrivateGitIndex<Value>(action: (environment: Readonly<{
|
|
18
|
+
GIT_INDEX_FILE: string;
|
|
19
|
+
}>) => Value): Value;
|
|
20
|
+
/** Capture tracked and untracked workspace bytes without changing its real index. */
|
|
21
|
+
export declare function captureWorkspaceTree(repository: GitRepository, workspace: string): WorkspaceTree;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { contractPhysicalName, gitObjectId, mintSnapshotId } from "./identity.js";
|
|
5
|
+
import { runGit, runGitWithEnvironment } from "./repository.js";
|
|
6
|
+
const WORKTREE_DIRECTORY = ["keiyaku", "wt"];
|
|
7
|
+
export function deliveryWorktreePath(repository, contract) {
|
|
8
|
+
return resolve(repository.commonDirectory, ...WORKTREE_DIRECTORY, contractPhysicalName(contract));
|
|
9
|
+
}
|
|
10
|
+
export function withPrivateGitIndex(action) {
|
|
11
|
+
const directory = mkdtempSync(join(tmpdir(), "keiyaku-v4-index-"));
|
|
12
|
+
try {
|
|
13
|
+
return action({ GIT_INDEX_FILE: join(directory, "index") });
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
rmSync(directory, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function fields(record, count) {
|
|
20
|
+
const result = [];
|
|
21
|
+
let start = 0;
|
|
22
|
+
while (result.length < count - 1) {
|
|
23
|
+
const separator = record.indexOf(" ", start);
|
|
24
|
+
if (separator < 0)
|
|
25
|
+
throw new Error(`malformed workspace status record: ${record}`);
|
|
26
|
+
result.push(record.slice(start, separator));
|
|
27
|
+
start = separator + 1;
|
|
28
|
+
}
|
|
29
|
+
result.push(record.slice(start));
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function workspaceChanges(repository, workspace) {
|
|
33
|
+
const records = runGit(repository, [
|
|
34
|
+
"-C", workspace,
|
|
35
|
+
"status", "--porcelain=v2", "-z", "--untracked-files=all", "--ignore-submodules=none",
|
|
36
|
+
]).toString("utf8").split("\0");
|
|
37
|
+
const staged = new Set();
|
|
38
|
+
const unstaged = new Set();
|
|
39
|
+
const untracked = new Set();
|
|
40
|
+
const submodules = new Set();
|
|
41
|
+
for (let index = 0; index < records.length - 1; index += 1) {
|
|
42
|
+
const record = records[index];
|
|
43
|
+
const kind = record[0];
|
|
44
|
+
if (kind === "?") {
|
|
45
|
+
untracked.add(fields(record, 2)[1]);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (kind === "1" || kind === "2" || kind === "u") {
|
|
49
|
+
const parts = fields(record, kind === "1" ? 9 : kind === "2" ? 10 : 11);
|
|
50
|
+
const xy = parts[1];
|
|
51
|
+
const sub = parts[2];
|
|
52
|
+
const path = parts.at(-1);
|
|
53
|
+
if (xy[0] !== ".")
|
|
54
|
+
staged.add(path);
|
|
55
|
+
if (xy[1] !== ".")
|
|
56
|
+
unstaged.add(path);
|
|
57
|
+
if (sub.startsWith("S") && (sub[2] !== "." || sub[3] !== "."))
|
|
58
|
+
submodules.add(path);
|
|
59
|
+
if (kind === "2") {
|
|
60
|
+
const origin = records[index + 1];
|
|
61
|
+
if (origin === undefined)
|
|
62
|
+
throw new Error("renamed workspace status record is missing its origin path");
|
|
63
|
+
if (xy[0] !== ".")
|
|
64
|
+
staged.add(origin);
|
|
65
|
+
if (xy[1] !== ".")
|
|
66
|
+
unstaged.add(origin);
|
|
67
|
+
index += 1;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`unknown workspace status record: ${record}`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
staged: [...staged].sort(),
|
|
75
|
+
unstaged: [...unstaged].sort(),
|
|
76
|
+
untracked: [...untracked].sort(),
|
|
77
|
+
submodules: [...submodules].sort(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Capture tracked and untracked workspace bytes without changing its real index. */
|
|
81
|
+
export function captureWorkspaceTree(repository, workspace) {
|
|
82
|
+
const identities = runGit(repository, ["-C", workspace, "show", "-s", "--format=%H%n%T", "HEAD"])
|
|
83
|
+
.toString("utf8")
|
|
84
|
+
.split("\n");
|
|
85
|
+
if (identities.length !== 3 || identities[0] === undefined || identities[1] === undefined || identities[2] !== "") {
|
|
86
|
+
throw new Error("workspace HEAD/tree resolution did not return exactly two object IDs");
|
|
87
|
+
}
|
|
88
|
+
const head = mintSnapshotId(identities[0]);
|
|
89
|
+
const tree = gitObjectId(identities[1], "workspace tree");
|
|
90
|
+
const changes = workspaceChanges(repository, workspace);
|
|
91
|
+
const dirty = changes.staged.length > 0 || changes.unstaged.length > 0 || changes.untracked.length > 0;
|
|
92
|
+
if (!dirty)
|
|
93
|
+
return { tree, head, dirty, changes };
|
|
94
|
+
return withPrivateGitIndex((environment) => {
|
|
95
|
+
runGitWithEnvironment(repository, ["-C", workspace, "read-tree", "HEAD"], undefined, environment);
|
|
96
|
+
runGitWithEnvironment(repository, ["-C", workspace, "add", "--all"], undefined, environment);
|
|
97
|
+
return {
|
|
98
|
+
tree: gitObjectId(runGitWithEnvironment(repository, ["-C", workspace, "write-tree"], undefined, environment).toString("utf8").trim(), "workspace tree"),
|
|
99
|
+
head,
|
|
100
|
+
dirty,
|
|
101
|
+
changes,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
declare const AKUMA_ALIAS: unique symbol;
|
|
2
|
+
declare const AKUMA_GLOB: unique symbol;
|
|
2
3
|
export type AkumaAlias = string & {
|
|
3
4
|
readonly [AKUMA_ALIAS]: true;
|
|
4
5
|
};
|
|
6
|
+
export type AkumaGlob = string & {
|
|
7
|
+
readonly [AKUMA_GLOB]: true;
|
|
8
|
+
};
|
|
5
9
|
export declare function parseAkumaAlias(value: string): AkumaAlias;
|
|
10
|
+
export declare function parseAkumaGlob(value: string): AkumaGlob;
|
|
11
|
+
export declare function matchesAkumaGlob(glob: AkumaGlob, value: string): boolean;
|
|
6
12
|
export {};
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
const AKUMA_ALIAS_PATTERN = /^@[a-z][a-z0-9-]{0,63}$/u;
|
|
2
|
+
const AKUMA_GLOB_SUFFIX = /^[0-9a-f*]+$/u;
|
|
2
3
|
export function parseAkumaAlias(value) {
|
|
3
4
|
if (!AKUMA_ALIAS_PATTERN.test(value)) {
|
|
4
5
|
throw new TypeError("Akuma alias must match ^@[a-z][a-z0-9-]{0,63}$");
|
|
5
6
|
}
|
|
6
7
|
return value;
|
|
7
8
|
}
|
|
9
|
+
export function parseAkumaGlob(value) {
|
|
10
|
+
const segments = value.split("/");
|
|
11
|
+
const archetype = segments[1] ?? "";
|
|
12
|
+
const archetypeProbe = archetype.replaceAll("*", "x");
|
|
13
|
+
if (segments.length !== 3 || segments[0] !== "aku" || !value.includes("*")
|
|
14
|
+
|| archetype.length === 0 || normalizeIdentityStem({ source: archetypeProbe }) !== archetypeProbe
|
|
15
|
+
|| !AKUMA_GLOB_SUFFIX.test(segments[2] ?? "")) {
|
|
16
|
+
throw new TypeError("Akuma glob must be aku/<archetype-pattern>/<hex-pattern> and contain *");
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
export function matchesAkumaGlob(glob, value) {
|
|
21
|
+
const pattern = parseAkumaGlob(glob)
|
|
22
|
+
.replaceAll(/[.+?^${}()|[\]\\]/gu, "\\$&")
|
|
23
|
+
.replaceAll("*", "[^/]*");
|
|
24
|
+
return new RegExp(`^${pattern}$`, "u").test(value);
|
|
25
|
+
}
|
|
26
|
+
import { normalizeIdentityStem } from "./normalize.js";
|
package/build/src/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export { AuthorityCorruptionError, Delivery, gatesFrom, Keiyaku, KeiyakuRefused, KeiyakuRetry, NoGitWorldError, Repo, SettingsError, worktreeHooksFrom } from "./library/keiyaku.js";
|
|
1
|
+
export { AuthorityCorruptionError, Delivery, gatesFrom, Keiyaku, KeiyakuRefused, KeiyakuRetry, NoGitWorldError, Repo, requireBranchesToBeUpToDateFrom, SettingsError, worktreeHooksFrom } from "./library/keiyaku.js";
|
|
2
2
|
export { settings } from "./settings.js";
|
|
3
|
+
export { World, WorldError } from "./world.js";
|
|
4
|
+
export type { WorldRoot } from "./world.js";
|
|
3
5
|
export type { Settings, SettingsEntry, SettingsInput, SettingsNamespaceFailure, SettingsNamespaceView, SettingsScope, SettingsScopeState, } from "./settings.js";
|
|
4
|
-
export type { AttestationVerdict, AbandonInput, ActorId, AkuId, AkumaAlias, AkumaStatus, AliasBinding, AliasStage, AmendResult, AmendInput, ArcInput, AuditReport, BindInput, BindResult, CallInput, CallObservation, CallResult, ChangeId, ContractId, ContractState, ContractBoard, ContractDisposition, ContractGateCurrent, ContractGateReport, ContractListInput, ContractObservation, ContractObservationInput, ContractPhase, ContractRow, DeliverInput, Fact, FactKind, Dispatch, DispatchFailure, DispatchStage, Gate, GatesFromInput, HookCommand, KeiyakuOfInput, KeiyakuRefusal, KeiyakuRetryReason, ForkInput, ForkResult, IntegrationFailure, Lag, MutationResult, PlacementStop, RegionOverlap, ReconcileInput, RepoReconcileReport, Review, ReconcileReport, ReviewInput, RepoAtInput, AuditInput, SnapshotId, SettlementAction, SettlementLag, SettlementReport, TaskId, TimelineEntry, TopologyEffect, VerificationStop, WorktreeHooks,
|
|
6
|
+
export type { AttestationVerdict, AbandonInput, ActorId, AkuId, AkumaAddressInput, AkumaAlias, AkumaGlob, AkumaHistoryInput, AkumaHistoryResult, AkumaInterruptInput, AkumaInterruptResult, AkumaKillResult, AkumaSetAddressInput, AkumaStatus, AkumaStatusView, AkumaTellInput, AkumaTellResult, AkumaWaitInput, AkumaWaitResult, AliasBinding, AliasStage, AmendResult, AmendInput, ArcInput, AuditReport, BindInput, BindResult, CallInput, CallObservation, CallResult, Catalog, CatalogInput, CatalogQuery, ChangeId, ContractId, ContractState, ContractBoard, ContractDisposition, ContractGateCurrent, ContractGateReport, ContractListInput, ContractObservation, ContractObservationInput, ContractPhase, ContractRow, DeliverInput, Fact, FactKind, Dispatch, DispatchFailure, DispatchStage, DirectAkumaSelector, Gate, GatesFromInput, HookCommand, RequireBranchesToBeUpToDateFromInput, KeiyakuOfInput, KeiyakuRefusal, KeiyakuRetryReason, ForkInput, ForkResult, IntegrationFailure, Lag, MutationResult, PlacementStop, RegionOverlap, ReconcileInput, RepoReconcileReport, Review, ReconcileReport, ReviewInput, RepoAtInput, AuditInput, SnapshotId, SettlementAction, SettlementLag, SettlementReport, SetAkumaSelector, TaskId, TimelineEntry, TopologyEffect, VerificationStop, WorktreeHooks, } from "./library/keiyaku.js";
|
package/build/src/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { AuthorityCorruptionError, Delivery, gatesFrom, Keiyaku, KeiyakuRefused, KeiyakuRetry, NoGitWorldError, Repo, SettingsError, worktreeHooksFrom } from "./library/keiyaku.js";
|
|
1
|
+
export { AuthorityCorruptionError, Delivery, gatesFrom, Keiyaku, KeiyakuRefused, KeiyakuRetry, NoGitWorldError, Repo, requireBranchesToBeUpToDateFrom, SettingsError, worktreeHooksFrom } from "./library/keiyaku.js";
|
|
2
2
|
export { settings } from "./settings.js";
|
|
3
|
+
export { World, WorldError } from "./world.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { kanshi } from "./read.js";
|
|
2
2
|
export { selectKanshi } from "./select.js";
|
|
3
3
|
export type { KanshiInput } from "./read.js";
|
|
4
|
-
export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, } from "./report.js";
|
|
4
|
+
export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, } from "./report.js";
|
|
5
5
|
export type { KanshiSelection } from "./select.js";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { Repo } from "../library/repo.js";
|
|
1
2
|
import type { KanshiReport } from "./report.js";
|
|
3
|
+
import type { WorldRoot } from "../world.js";
|
|
2
4
|
export type KanshiInput = Readonly<{
|
|
3
|
-
|
|
5
|
+
world: WorldRoot | null;
|
|
6
|
+
repo?: Repo;
|
|
4
7
|
}>;
|
|
5
|
-
export declare function kanshi(input
|
|
8
|
+
export declare function kanshi(input: KanshiInput): Promise<KanshiReport>;
|