@astrosheep/keiyaku 4.0.4 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/integrations/marketplace/plugins/keiyaku/.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-workflow/SKILL.md +14 -0
- package/build/src/akuma/akuma.d.ts +0 -1
- package/build/src/akuma/akuma.js +1 -6
- package/build/src/akuma/allowed.d.ts +1 -1
- package/build/src/akuma/allowed.js +3 -10
- package/build/src/akuma/body.d.ts +5 -2
- package/build/src/akuma/body.js +29 -23
- package/build/src/akuma/heart/facts.d.ts +35 -9
- package/build/src/akuma/heart/index.d.ts +4 -13
- package/build/src/akuma/heart/index.js +2 -133
- package/build/src/akuma/heart/request-authority.d.ts +12 -0
- package/build/src/akuma/heart/request-authority.js +119 -0
- package/build/src/akuma/heart/request-rows.d.ts +16 -0
- package/build/src/akuma/heart/request-rows.js +88 -0
- package/build/src/akuma/heart/rows.d.ts +1 -28
- package/build/src/akuma/heart/rows.js +0 -137
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +9 -2
- package/build/src/akuma/heart/soul.js +0 -36
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +1 -6
- package/build/src/akuma/providers/acp/core.js +6 -2
- package/build/src/akuma/providers/acp/index.js +0 -1
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +7 -3
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +0 -2
- package/build/src/akuma/providers/grok-build/index.js +0 -1
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
- package/build/src/akuma/providers/pi/index.js +11 -4
- package/build/src/akuma/request-serve.d.ts +87 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +124 -0
- package/build/src/akuma/request-wire.js +380 -0
- package/build/src/akuma/requests.d.ts +28 -94
- package/build/src/akuma/requests.js +52 -562
- package/build/src/akuma-body.d.ts +7 -0
- package/build/src/akuma-body.js +54 -1
- package/build/src/body/arc.js +4 -29
- package/build/src/body/decode.js +5 -39
- package/build/src/body/envelope.d.ts +7 -0
- package/build/src/body/envelope.js +37 -0
- package/build/src/cli/accepted.js +17 -9
- package/build/src/cli/commands/akuma.js +2 -2
- package/build/src/cli/commands/contract.d.ts +6 -6
- package/build/src/cli/commands/contract.js +3 -3
- package/build/src/cli/commands/task-invoke.js +98 -9
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +7 -4
- package/build/src/cli/invoke.js +28 -4
- package/build/src/cli/parse.d.ts +2 -2
- package/build/src/cli/parse.js +21 -8
- package/build/src/cli/render/akuma.js +56 -13
- package/build/src/cli/render/audit.js +2 -6
- package/build/src/cli/render/contract.js +40 -0
- package/build/src/cli/render/receipt.js +28 -1
- package/build/src/cli/render/terminal.d.ts +0 -2
- package/build/src/cli/render/terminal.js +0 -29
- package/build/src/cli/result.d.ts +15 -7
- package/build/src/contract-worktree.d.ts +1 -2
- package/build/src/contract-worktree.js +4 -9
- package/build/src/core/facts/codec.js +9 -0
- package/build/src/core/facts/fold.js +13 -1
- package/build/src/core/facts/types.d.ts +8 -1
- package/build/src/core/subject.js +4 -3
- package/build/src/core/verbs/placement.d.ts +9 -1
- package/build/src/core/verbs/placement.js +24 -6
- package/build/src/core/verbs/reintegrate.d.ts +13 -0
- package/build/src/core/verbs/reintegrate.js +24 -0
- package/build/src/dispatch/index.d.ts +1 -1
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/integration.d.ts +5 -2
- package/build/src/git/integration.js +54 -1
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +26 -0
- package/build/src/git/process.js +94 -0
- package/build/src/git/read-observation.d.ts +2 -1
- package/build/src/git/read-observation.js +3 -2
- package/build/src/git/reconcile.d.ts +2 -1
- package/build/src/git/reconcile.js +9 -6
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +4 -118
- package/build/src/git/scratch.d.ts +1 -1
- package/build/src/git/scratch.js +1 -1
- package/build/src/git/target-placement.d.ts +1 -1
- package/build/src/git/target-placement.js +9 -7
- package/build/src/git/tender.d.ts +1 -1
- package/build/src/git/tender.js +2 -1
- package/build/src/git/terminal-seal.d.ts +2 -2
- package/build/src/git/terminal-seal.js +3 -6
- package/build/src/git/workspace.d.ts +1 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/library/address.js +7 -1
- package/build/src/library/audit.d.ts +2 -1
- package/build/src/library/audit.js +1 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +1 -1
- package/build/src/library/configuration.d.ts +1 -1
- package/build/src/library/configuration.js +52 -18
- package/build/src/library/contract.d.ts +55 -2
- package/build/src/library/contract.js +162 -23
- package/build/src/library/delivery.d.ts +1 -1
- package/build/src/library/fleet.d.ts +27 -7
- package/build/src/library/fleet.js +79 -39
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +0 -5
- package/build/src/library/keiyaku.d.ts +1 -1
- package/build/src/library/mutation.d.ts +2 -1
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +3 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +3 -1
- package/build/src/library/repo.js +8 -4
- package/build/src/protocol/abandon.d.ts +9 -0
- package/build/src/protocol/abandon.js +15 -0
- package/build/src/protocol/amend.d.ts +16 -0
- package/build/src/protocol/amend.js +77 -0
- package/build/src/protocol/arc.d.ts +6 -0
- package/build/src/protocol/arc.js +12 -0
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/audit.d.ts +53 -0
- package/build/src/protocol/audit.js +140 -0
- package/build/src/protocol/bind.d.ts +1 -1
- package/build/src/protocol/completion.d.ts +32 -0
- package/build/src/protocol/completion.js +130 -0
- package/build/src/protocol/deliver.d.ts +37 -0
- package/build/src/protocol/deliver.js +160 -0
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -1
- package/build/src/protocol/operations.d.ts +64 -200
- package/build/src/protocol/operations.js +21 -640
- package/build/src/protocol/placement.d.ts +4 -13
- package/build/src/protocol/placement.js +3 -15
- package/build/src/protocol/read/status.d.ts +1 -1
- package/build/src/protocol/read/status.js +3 -1
- package/build/src/protocol/reconcile.d.ts +35 -0
- package/build/src/protocol/reconcile.js +41 -0
- package/build/src/protocol/reintegrate.d.ts +31 -0
- package/build/src/protocol/reintegrate.js +74 -0
- package/build/src/protocol/review.d.ts +33 -0
- package/build/src/protocol/review.js +148 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/settlement/fence.d.ts +1 -1
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/task/board.d.ts +0 -1
- package/build/src/task/board.js +0 -3
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +11 -4
- package/build/src/task/index.d.ts +1 -1
- package/build/src/task/index.js +2 -127
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +39 -0
- package/build/src/task/mutation.js +85 -0
- package/build/src/task/operations.d.ts +2 -0
- package/build/src/task/operations.js +35 -11
- package/build/src/task/store.js +0 -2
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/workspace-place.d.ts +1 -4
- package/build/src/workspace-place.js +0 -15
- package/package.json +3 -3
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import { gatesSatisfied } from "../facts/gate.js";
|
|
2
|
-
import { activeContract,
|
|
2
|
+
import { activeContract, contractState } from "../facts/observation.js";
|
|
3
|
+
function unmetPrerequisites(prerequisites, observation) {
|
|
4
|
+
const unmet = [];
|
|
5
|
+
for (const contractId of prerequisites) {
|
|
6
|
+
const state = contractState(observation, contractId);
|
|
7
|
+
if (state === null) {
|
|
8
|
+
unmet.push({ contractId, state: "missing" });
|
|
9
|
+
}
|
|
10
|
+
else if (state.terminal?.kind === "abandoned") {
|
|
11
|
+
unmet.push({ contractId, state: "abandoned" });
|
|
12
|
+
}
|
|
13
|
+
else if (state.terminal?.kind !== "claimed") {
|
|
14
|
+
unmet.push({ contractId, state: "active" });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return unmet;
|
|
18
|
+
}
|
|
3
19
|
export function decidePlacement({ input, attempt, observation }) {
|
|
4
20
|
const id = input.contractId;
|
|
5
21
|
const current = activeContract(observation, id);
|
|
6
22
|
if ("kind" in current)
|
|
7
23
|
return { kind: "refused", refusal: current };
|
|
8
24
|
const delivery = current.delivery;
|
|
9
|
-
|
|
25
|
+
const integration = current.currentIntegration;
|
|
26
|
+
if (!delivery || !integration) {
|
|
10
27
|
return { kind: "refused", refusal: { kind: "delivery-missing", contractId: id } };
|
|
11
28
|
}
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
const unmet = unmetPrerequisites(current.terms.after, observation);
|
|
30
|
+
if (unmet.length > 0) {
|
|
31
|
+
return { kind: "refused", refusal: { kind: "prerequisites-unsatisfied", contractId: id, unmet } };
|
|
14
32
|
}
|
|
15
33
|
if (!gatesSatisfied(current)) {
|
|
16
34
|
return { kind: "refused", refusal: { kind: "gates-unsatisfied", contractId: id } };
|
|
@@ -31,8 +49,8 @@ export function decidePlacement({ input, attempt, observation }) {
|
|
|
31
49
|
...(current.coordinates.target === undefined ? {} : {
|
|
32
50
|
target: {
|
|
33
51
|
target: current.coordinates.target,
|
|
34
|
-
expectedOid:
|
|
35
|
-
newOid:
|
|
52
|
+
expectedOid: integration.predecessor,
|
|
53
|
+
newOid: integration.snapshot,
|
|
36
54
|
},
|
|
37
55
|
}),
|
|
38
56
|
},
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DecideInput, OfferDecision, Preparation } from "../decide.js";
|
|
2
|
+
import type { ActorId, ContractId, ReintegratedData } from "../facts/types.js";
|
|
3
|
+
export type ReintegrateInput<Failure = never> = Readonly<{
|
|
4
|
+
contractId: ContractId;
|
|
5
|
+
actor?: ActorId;
|
|
6
|
+
at: string;
|
|
7
|
+
preparation: Preparation<ReintegratedData, Failure>;
|
|
8
|
+
}>;
|
|
9
|
+
export type ReintegrateRefusal = Readonly<{
|
|
10
|
+
kind: "contract-missing" | "delivery-missing" | "terminal";
|
|
11
|
+
contractId: ContractId;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function decideReintegrate<Failure>({ input, attempt, observation, }: DecideInput<ReintegrateInput<Failure>>): OfferDecision<ReintegrateRefusal | Failure>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { activeContract } from "../facts/observation.js";
|
|
2
|
+
export function decideReintegrate({ input, attempt, observation, }) {
|
|
3
|
+
const state = activeContract(observation, input.contractId);
|
|
4
|
+
if ("kind" in state)
|
|
5
|
+
return { kind: "refused", refusal: state };
|
|
6
|
+
if (state.delivery === null) {
|
|
7
|
+
return { kind: "refused", refusal: { kind: "delivery-missing", contractId: input.contractId } };
|
|
8
|
+
}
|
|
9
|
+
if (input.preparation.kind === "refused")
|
|
10
|
+
return { kind: "refused", refusal: input.preparation.refusal };
|
|
11
|
+
const reintegrated = {
|
|
12
|
+
v: 1,
|
|
13
|
+
kind: "reintegrated",
|
|
14
|
+
contract: input.contractId,
|
|
15
|
+
entry: attempt.entryUlids[0],
|
|
16
|
+
at: input.at,
|
|
17
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
18
|
+
data: input.preparation.data,
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
kind: "offer",
|
|
22
|
+
offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries: [reintegrated] }] },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ContractId } from "../core/facts/types.js";
|
|
2
2
|
import { type AkuId } from "../akuma/identity.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { GitRepository } from "../git/process.js";
|
|
4
4
|
import { type GitReadObservation } from "../git/read-observation.js";
|
|
5
5
|
export type Dispatch = Readonly<{
|
|
6
6
|
akuId: AkuId;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Offer } from "../core/facts/offer.js";
|
|
2
2
|
import type { GitAdmissionSnapshot } from "./observe.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type GitRefAssertion } from "./repository.js";
|
|
4
|
+
import type { GitRepository } from "./process.js";
|
|
4
5
|
import { type ContractHead } from "../core/facts/types.js";
|
|
5
6
|
type Accepted = Readonly<{
|
|
6
7
|
kind: "accepted";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Preparation } from "../core/decide.js";
|
|
2
2
|
import type { ChangeId, ContractCoordinates, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
|
-
import { type GitRepository } from "./
|
|
4
|
+
import { type GitRepository } from "./process.js";
|
|
5
5
|
import type { DeliveryCommitMetadata, TenderCapture } from "./tender.js";
|
|
6
|
+
type IntegrationTender = Pick<TenderCapture, "head" | "tree">;
|
|
6
7
|
declare const REQUIRED_GIT: "2.38";
|
|
7
8
|
export type IntegrationPreparationRefusal = Readonly<{
|
|
8
9
|
kind: "integration-failed";
|
|
@@ -27,7 +28,9 @@ export type IntegrationPlan = Readonly<{
|
|
|
27
28
|
export declare function worktreeChangeId(repository: GitRepository, input: IntegrationCoordinates, tender: TenderCapture): Promise<ChangeId>;
|
|
28
29
|
/** Materialize the exact integration tree against its observed predecessor. */
|
|
29
30
|
export declare function materializeIntegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId, metadata: DeliveryCommitMetadata): Promise<SnapshotId>;
|
|
30
|
-
export declare function
|
|
31
|
+
export declare function persistedTender(repository: GitRepository, snapshot: SnapshotId): Promise<IntegrationTender>;
|
|
32
|
+
export declare function materializeReintegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId, template: SnapshotId): Promise<SnapshotId>;
|
|
33
|
+
export declare function planIntegration(repository: GitRepository, input: IntegrationCoordinates, tender: IntegrationTender, requireBranchesToBeUpToDate: boolean): Promise<Preparation<IntegrationPlan, Readonly<{
|
|
31
34
|
kind: "target-missing";
|
|
32
35
|
contractId: ContractId;
|
|
33
36
|
}> | IntegrationPreparationRefusal>>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
2
2
|
import { gitObjectId, gitObjectIdForSnapshot, mintChangeId, mintSnapshotId } from "./identity.js";
|
|
3
|
-
import { decodeGitNameOnly, decodeGitNumstat,
|
|
3
|
+
import { decodeGitNameOnly, decodeGitNumstat, readRef, } from "./repository.js";
|
|
4
|
+
import { GitPlumbingError, runGit, runGitWithEnvironment, } from "./process.js";
|
|
4
5
|
const REQUIRED_GIT = "2.38";
|
|
5
6
|
async function runAllowingNonzero(repository, args, input) {
|
|
6
7
|
try {
|
|
@@ -55,6 +56,58 @@ export async function materializeIntegrationSnapshot(repository, tree, parent, m
|
|
|
55
56
|
})).toString("utf8").trim();
|
|
56
57
|
return mintSnapshotId(commit);
|
|
57
58
|
}
|
|
59
|
+
export async function persistedTender(repository, snapshot) {
|
|
60
|
+
const tree = (await runGit(repository, ["show", "-s", "--format=%T", gitObjectIdForSnapshot(snapshot)]))
|
|
61
|
+
.toString("ascii")
|
|
62
|
+
.trim();
|
|
63
|
+
return { head: snapshot, tree: gitObjectId(tree, "persisted tender tree") };
|
|
64
|
+
}
|
|
65
|
+
export async function materializeReintegrationSnapshot(repository, tree, parent, template) {
|
|
66
|
+
const templateObject = gitObjectIdForSnapshot(template);
|
|
67
|
+
let raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = await runGit(repository, ["cat-file", "commit", templateObject]);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof GitPlumbingError) {
|
|
73
|
+
throw new AuthorityCorruptionError(`recorded delivery snapshot cannot be read: ${template}`);
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
const separator = raw.indexOf(Buffer.from("\n\n"));
|
|
78
|
+
if (separator < 0)
|
|
79
|
+
throw new AuthorityCorruptionError(`recorded delivery snapshot has no commit message: ${template}`);
|
|
80
|
+
const message = raw.subarray(separator + 2);
|
|
81
|
+
let fields;
|
|
82
|
+
try {
|
|
83
|
+
const formatted = (await runGit(repository, [
|
|
84
|
+
"show",
|
|
85
|
+
"-s",
|
|
86
|
+
"--format=%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI",
|
|
87
|
+
templateObject,
|
|
88
|
+
])).toString("utf8").replace(/\n$/u, "");
|
|
89
|
+
fields = formatted.split("\0");
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error instanceof GitPlumbingError) {
|
|
93
|
+
throw new AuthorityCorruptionError(`recorded delivery snapshot metadata cannot be read: ${template}`);
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
if (fields.length !== 6 || fields.some((field) => field.length === 0)) {
|
|
98
|
+
throw new AuthorityCorruptionError(`recorded delivery snapshot metadata is malformed: ${template}`);
|
|
99
|
+
}
|
|
100
|
+
const [authorName, authorEmail, authorDate, committerName, committerEmail, committerDate] = fields;
|
|
101
|
+
const commit = (await runGitWithEnvironment(repository, ["commit-tree", tree, "-p", gitObjectIdForSnapshot(parent)], message, {
|
|
102
|
+
GIT_AUTHOR_NAME: authorName,
|
|
103
|
+
GIT_AUTHOR_EMAIL: authorEmail,
|
|
104
|
+
GIT_AUTHOR_DATE: authorDate,
|
|
105
|
+
GIT_COMMITTER_NAME: committerName,
|
|
106
|
+
GIT_COMMITTER_EMAIL: committerEmail,
|
|
107
|
+
GIT_COMMITTER_DATE: committerDate,
|
|
108
|
+
})).toString("utf8").trim();
|
|
109
|
+
return mintSnapshotId(commit);
|
|
110
|
+
}
|
|
58
111
|
async function isAncestor(repository, ancestor, descendant) {
|
|
59
112
|
const result = await runAllowingNonzero(repository, [
|
|
60
113
|
"merge-base",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ContractId, type ContractState, type JournalEntry, type SnapshotId } from "../core/facts/types.js";
|
|
2
2
|
import type { ContractsObservation } from "../core/facts/observation.js";
|
|
3
|
-
import { type GitSnapshot, type GitOid
|
|
3
|
+
import { type GitSnapshot, type GitOid } from "./repository.js";
|
|
4
|
+
import { type GitRepository } from "./process.js";
|
|
4
5
|
import { type GitDecodeChannel, type GitReadObservation, type GitTreeSelection } from "./read-observation.js";
|
|
5
6
|
export type TargetObservation = Readonly<{
|
|
6
7
|
head: SnapshotId | null;
|
package/build/src/git/observe.js
CHANGED
|
@@ -2,7 +2,8 @@ import { decodeJournal } from "../core/facts/codec.js";
|
|
|
2
2
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
3
3
|
import { foldJournal } from "../core/facts/fold.js";
|
|
4
4
|
import { contractJournalPath, contractJournalPaths, mintContractHead, mintSnapshotId, } from "./identity.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isKeiyakuOwnedRef, } from "./repository.js";
|
|
6
|
+
import { GitPlumbingError, runGit } from "./process.js";
|
|
6
7
|
import { readGitTreeSelection, withGitTargetedReadObservation, } from "./read-observation.js";
|
|
7
8
|
export async function observeDeliveryTargetAt(observation, state) {
|
|
8
9
|
const target = state.coordinates.target;
|
|
@@ -10,12 +11,12 @@ export async function observeDeliveryTargetAt(observation, state) {
|
|
|
10
11
|
return null;
|
|
11
12
|
const value = await observation.resolveRef(target);
|
|
12
13
|
const head = value === null ? null : mintSnapshotId(value);
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
14
|
+
const integration = state.currentIntegration;
|
|
15
|
+
if (integration === null)
|
|
15
16
|
return { head, drift: false };
|
|
16
17
|
const expected = state.terminal?.kind === "claimed"
|
|
17
|
-
?
|
|
18
|
-
:
|
|
18
|
+
? integration.snapshot
|
|
19
|
+
: integration.predecessor;
|
|
19
20
|
return { head, drift: head !== expected };
|
|
20
21
|
}
|
|
21
22
|
const TARGET_COORDINATES_FORMAT = "%(refname)%00%(objectname)%00";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type GitRepository = Readonly<{
|
|
2
|
+
/** The executable selected when this repository capability is created. */
|
|
3
|
+
readonly gitPath: string;
|
|
4
|
+
/** The invocation's effective working directory, including a caller -C worktree. */
|
|
5
|
+
readonly effectiveCwd: string;
|
|
6
|
+
/** The canonical primary worktree root for this repository. */
|
|
7
|
+
readonly primaryWorktree: string;
|
|
8
|
+
/** The canonical common Git directory pinned when this capability is created. */
|
|
9
|
+
readonly commonDirectory: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare class GitPlumbingError extends Error {
|
|
12
|
+
readonly stdout: Buffer;
|
|
13
|
+
readonly stderr: Buffer;
|
|
14
|
+
readonly status: number | null;
|
|
15
|
+
readonly pid: number | null;
|
|
16
|
+
constructor(input: Readonly<{
|
|
17
|
+
stdout?: string | Uint8Array;
|
|
18
|
+
stderr: string | Uint8Array;
|
|
19
|
+
status: number | null;
|
|
20
|
+
message: string;
|
|
21
|
+
pid?: number | null;
|
|
22
|
+
}>);
|
|
23
|
+
}
|
|
24
|
+
export declare function runGit(repository: GitRepository, args: readonly string[], input?: string | Uint8Array): Promise<Buffer>;
|
|
25
|
+
export declare function consumeGitStdout(repository: GitRepository, args: readonly string[], consume: (chunk: Buffer) => void): Promise<void>;
|
|
26
|
+
export declare function runGitWithEnvironment(repository: GitRepository, args: readonly string[], input: string | Uint8Array | undefined, environment: NodeJS.ProcessEnv): Promise<Buffer>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { consumeProcessStdout } from "../runtime/proc/run.js";
|
|
3
|
+
export class GitPlumbingError extends Error {
|
|
4
|
+
stdout;
|
|
5
|
+
stderr;
|
|
6
|
+
status;
|
|
7
|
+
pid;
|
|
8
|
+
constructor(input) {
|
|
9
|
+
super(input.message);
|
|
10
|
+
this.name = "GitPlumbingError";
|
|
11
|
+
this.stdout = Buffer.from(input.stdout ?? "");
|
|
12
|
+
this.stderr = Buffer.from(input.stderr);
|
|
13
|
+
this.status = input.status;
|
|
14
|
+
this.pid = input.pid ?? null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const GIT_STDERR_BYTES = 16 * 1024;
|
|
18
|
+
function commandError(command, error) {
|
|
19
|
+
const candidate = error;
|
|
20
|
+
const stderr = candidate.stderr === undefined ? Buffer.alloc(0) : Buffer.from(candidate.stderr);
|
|
21
|
+
const stdout = candidate.stdout === undefined ? Buffer.alloc(0) : Buffer.from(candidate.stdout);
|
|
22
|
+
const detail = stderr.length === 0 ? candidate.message ?? "git command failed" : stderr.toString("utf8");
|
|
23
|
+
return new GitPlumbingError({
|
|
24
|
+
stdout,
|
|
25
|
+
stderr,
|
|
26
|
+
status: candidate.status ?? null,
|
|
27
|
+
message: `${command.join(" ")}: ${detail}`,
|
|
28
|
+
pid: candidate.pid ?? null,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function executeGit(repository, args, input, environment) {
|
|
32
|
+
const stdout = [];
|
|
33
|
+
const stderr = [];
|
|
34
|
+
let stderrBytes = 0;
|
|
35
|
+
let inputError;
|
|
36
|
+
const child = spawn(repository.gitPath, [...args], {
|
|
37
|
+
cwd: repository.effectiveCwd,
|
|
38
|
+
...(environment === undefined ? {} : { env: { ...process.env, ...environment } }),
|
|
39
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
40
|
+
windowsHide: true,
|
|
41
|
+
});
|
|
42
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
43
|
+
child.stderr.on("data", (chunk) => {
|
|
44
|
+
if (stderrBytes >= GIT_STDERR_BYTES)
|
|
45
|
+
return;
|
|
46
|
+
const retained = chunk.subarray(0, GIT_STDERR_BYTES - stderrBytes);
|
|
47
|
+
stderr.push(retained);
|
|
48
|
+
stderrBytes += retained.length;
|
|
49
|
+
});
|
|
50
|
+
child.stdin.on("error", (error) => { inputError = error; });
|
|
51
|
+
child.stdin.end(input);
|
|
52
|
+
const terminal = await new Promise((resolveTerminal) => {
|
|
53
|
+
child.once("error", (error) => resolveTerminal({ code: null, error }));
|
|
54
|
+
child.once("close", (code) => resolveTerminal({ code }));
|
|
55
|
+
});
|
|
56
|
+
const output = Buffer.concat(stdout);
|
|
57
|
+
if (terminal.code === 0 && inputError === undefined)
|
|
58
|
+
return output;
|
|
59
|
+
throw commandError(args, {
|
|
60
|
+
message: terminal.error?.message ?? inputError?.message ?? `git exited with status ${terminal.code ?? "unknown"}`,
|
|
61
|
+
stdout: output,
|
|
62
|
+
stderr: Buffer.concat(stderr),
|
|
63
|
+
status: terminal.code,
|
|
64
|
+
pid: child.pid,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export async function runGit(repository, args, input) {
|
|
68
|
+
return await executeGit(repository, args, input);
|
|
69
|
+
}
|
|
70
|
+
export async function consumeGitStdout(repository, args, consume) {
|
|
71
|
+
const result = await consumeProcessStdout({
|
|
72
|
+
argv: [repository.gitPath, ...args],
|
|
73
|
+
cwd: repository.effectiveCwd,
|
|
74
|
+
}, consume);
|
|
75
|
+
const { outcome } = result;
|
|
76
|
+
if (outcome.kind === "terminal" && outcome.code === 0)
|
|
77
|
+
return;
|
|
78
|
+
const status = outcome.kind === "terminal" ? outcome.code : null;
|
|
79
|
+
const stderr = outcome.kind === "terminal" ? outcome.stderr : "";
|
|
80
|
+
const message = outcome.kind === "terminal"
|
|
81
|
+
? `git exited with status ${outcome.code}`
|
|
82
|
+
: outcome.kind === "spawn-error" || outcome.kind === "stream-error"
|
|
83
|
+
? outcome.diagnostic
|
|
84
|
+
: `git process ended with ${outcome.kind}`;
|
|
85
|
+
throw commandError(args, {
|
|
86
|
+
message,
|
|
87
|
+
stderr,
|
|
88
|
+
status,
|
|
89
|
+
pid: result.pid ?? undefined,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export async function runGitWithEnvironment(repository, args, input, environment) {
|
|
93
|
+
return await executeGit(repository, args, input, environment);
|
|
94
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type TreeEntry } from "./tree.js";
|
|
2
|
-
import { type GitOid, type
|
|
2
|
+
import { type GitOid, type GitSnapshot } from "./repository.js";
|
|
3
|
+
import { type GitRepository } from "./process.js";
|
|
3
4
|
declare const gitReadObservationBrand: unique symbol;
|
|
4
5
|
declare const gitDecodeChannelBrand: unique symbol;
|
|
5
6
|
export type GitBlobResult = Readonly<{
|
|
@@ -2,7 +2,8 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
3
3
|
import { gitObjectId } from "./identity.js";
|
|
4
4
|
import { parseTreeObject, validPath } from "./tree.js";
|
|
5
|
-
import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF,
|
|
5
|
+
import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readRef, } from "./repository.js";
|
|
6
|
+
import { GitPlumbingError } from "./process.js";
|
|
6
7
|
const gitReadObservationBrand = Symbol("GitReadObservation");
|
|
7
8
|
const gitDecodeChannelBrand = Symbol("GitDecodeChannel");
|
|
8
9
|
function streamCursor(stream) {
|
|
@@ -36,7 +37,7 @@ function streamCursor(stream) {
|
|
|
36
37
|
return { line, exact };
|
|
37
38
|
}
|
|
38
39
|
function batchObjectReader(repository) {
|
|
39
|
-
const child = spawn(
|
|
40
|
+
const child = spawn(repository.gitPath, ["cat-file", "--batch"], {
|
|
40
41
|
cwd: repository.effectiveCwd,
|
|
41
42
|
stdio: ["pipe", "pipe", "pipe"],
|
|
42
43
|
windowsHide: true,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type GitOid
|
|
1
|
+
import { type GitOid } from "./repository.js";
|
|
2
|
+
import { type GitRepository } from "./process.js";
|
|
2
3
|
import type { ContractId, ContractState, SnapshotId } from "../core/facts/types.js";
|
|
3
4
|
import type { GitDecodeChannel } from "./read-observation.js";
|
|
4
5
|
import { type TargetCheckoutEffect, type TargetCheckoutLag } from "./target-placement.js";
|
|
@@ -2,7 +2,8 @@ import { access, mkdir } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
|
|
4
4
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
5
|
-
import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE,
|
|
5
|
+
import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE, isKeiyakuOwnedRef, readRef, registeredWorktreePaths, writeCommit, worktreeGitDirectory, } from "./repository.js";
|
|
6
|
+
import { GitPlumbingError, runGit } from "./process.js";
|
|
6
7
|
import { contractLocator, contractPhysicalName, gitObjectIdForSnapshot, mintSnapshotId, } from "./identity.js";
|
|
7
8
|
import { observeContractAt } from "./observe.js";
|
|
8
9
|
import { acquireTargetPlacementFence, recoverTargetPlacement, } from "./target-placement.js";
|
|
@@ -206,7 +207,9 @@ async function reconcileHereWorkspaceRefs({ repository }, state, { effects, lag
|
|
|
206
207
|
if (state.delivery === null)
|
|
207
208
|
effects.push(await removeRef(repository, pin));
|
|
208
209
|
else {
|
|
209
|
-
const integration = state.
|
|
210
|
+
const integration = state.currentIntegration?.snapshot;
|
|
211
|
+
if (integration === undefined)
|
|
212
|
+
return complete();
|
|
210
213
|
const custodian = await snapshotCustodian(repository, integration);
|
|
211
214
|
effects.push(await (custodian === null
|
|
212
215
|
? await updateRef(repository, pin, integration)
|
|
@@ -216,7 +219,7 @@ async function reconcileHereWorkspaceRefs({ repository }, state, { effects, lag
|
|
|
216
219
|
else if (state.bound) {
|
|
217
220
|
effects.push(await (state.delivery === null
|
|
218
221
|
? await removeRef(repository, pin)
|
|
219
|
-
: await updateRef(repository, pin, state.delivery.data.integration.snapshot)));
|
|
222
|
+
: await updateRef(repository, pin, state.currentIntegration?.snapshot ?? state.delivery.data.integration.snapshot)));
|
|
220
223
|
}
|
|
221
224
|
return complete(effects, lag);
|
|
222
225
|
}
|
|
@@ -314,7 +317,7 @@ async function releaseTerminalCustody({ repository, state, expected, ref, pin, a
|
|
|
314
317
|
return;
|
|
315
318
|
}
|
|
316
319
|
const tender = state.delivery.data.tenderSnapshot;
|
|
317
|
-
const integration = state.delivery.data.integration.snapshot;
|
|
320
|
+
const integration = state.currentIntegration?.snapshot ?? state.delivery.data.integration.snapshot;
|
|
318
321
|
const target = await targetCustodyForClaimedIntegration(repository, state, integration);
|
|
319
322
|
if (sealedTree(expected, tender) === sealedTree(expected, integration) && target !== null) {
|
|
320
323
|
effects.push(await removeRefWithCustody(repository, ref, target.ref, target.oid));
|
|
@@ -337,7 +340,7 @@ async function reconcileTerminalManagedWorktree({ repository, channel, hooks, re
|
|
|
337
340
|
const expected = await terminalSealExpectations(channel, state);
|
|
338
341
|
acc.effects.push(await updateRef(primary, ref, state.delivery?.data.tenderSnapshot ?? state.coordinates.start));
|
|
339
342
|
if (state.delivery !== null) {
|
|
340
|
-
acc.effects.push(await updateRef(primary, pin, state.delivery.data.integration.snapshot));
|
|
343
|
+
acc.effects.push(await updateRef(primary, pin, state.currentIntegration?.snapshot ?? state.delivery.data.integration.snapshot));
|
|
341
344
|
}
|
|
342
345
|
if (retainTerminalWorktree === true)
|
|
343
346
|
return complete(acc.effects, acc.lag);
|
|
@@ -367,7 +370,7 @@ async function reconcileActiveManagedWorktree({ repository, hooks, retryHooks, p
|
|
|
367
370
|
if (hookLag !== null)
|
|
368
371
|
lag.push(hookLag);
|
|
369
372
|
effects.push(await (state.delivery
|
|
370
|
-
? await updateRef(repository, candidatePinRefFor(state.id), state.delivery.data.integration.snapshot)
|
|
373
|
+
? await updateRef(repository, candidatePinRefFor(state.id), state.currentIntegration?.snapshot ?? state.delivery.data.integration.snapshot)
|
|
371
374
|
: await removeRef(repository, candidatePinRefFor(state.id))));
|
|
372
375
|
return complete(effects, lag);
|
|
373
376
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type GitRepository } from "./process.js";
|
|
1
2
|
import { type TreeChange, type TreeEntry } from "./tree.js";
|
|
2
3
|
export type { TreeChange } from "./tree.js";
|
|
3
4
|
export declare const GIT_REF = "refs/heads/keiyaku-state";
|
|
@@ -19,14 +20,6 @@ export type GitRefAssertion = Readonly<{
|
|
|
19
20
|
readonly ref: string;
|
|
20
21
|
readonly oid: GitOid;
|
|
21
22
|
}>;
|
|
22
|
-
export type GitRepository = Readonly<{
|
|
23
|
-
/** The invocation's effective working directory, including a caller -C worktree. */
|
|
24
|
-
readonly effectiveCwd: string;
|
|
25
|
-
/** The canonical primary worktree root for this repository. */
|
|
26
|
-
readonly primaryWorktree: string;
|
|
27
|
-
/** The canonical common Git directory pinned when this capability is created. */
|
|
28
|
-
readonly commonDirectory: string;
|
|
29
|
-
}>;
|
|
30
23
|
export type GitSnapshot = Readonly<{
|
|
31
24
|
readonly commit: GitOid | null;
|
|
32
25
|
readonly tree: GitOid | null;
|
|
@@ -36,41 +29,21 @@ export declare class NoGitWorldError extends Error {
|
|
|
36
29
|
readonly path: string;
|
|
37
30
|
constructor(path: string);
|
|
38
31
|
}
|
|
39
|
-
export declare class GitPlumbingError extends Error {
|
|
40
|
-
readonly stdout: Buffer;
|
|
41
|
-
readonly stderr: Buffer;
|
|
42
|
-
readonly status: number | null;
|
|
43
|
-
readonly pid: number | null;
|
|
44
|
-
constructor(input: Readonly<{
|
|
45
|
-
stdout?: string | Uint8Array;
|
|
46
|
-
stderr: string | Uint8Array;
|
|
47
|
-
status: number | null;
|
|
48
|
-
message: string;
|
|
49
|
-
pid?: number | null;
|
|
50
|
-
}>);
|
|
51
|
-
}
|
|
52
32
|
export type RegisteredWorktree = Readonly<{
|
|
53
33
|
path: string;
|
|
54
34
|
branch: string | null;
|
|
55
35
|
}>;
|
|
56
36
|
export declare function registeredWorktrees(repository: GitRepository): Promise<readonly RegisteredWorktree[]>;
|
|
57
37
|
export declare function registeredWorktreePaths(repository: GitRepository): Promise<readonly string[]>;
|
|
58
|
-
export declare function repositoryAt(cwd: string): Promise<GitRepository>;
|
|
38
|
+
export declare function repositoryAt(cwd: string, gitPath?: string): Promise<GitRepository>;
|
|
59
39
|
export declare function commonGitDirectory(repository: GitRepository): string;
|
|
60
40
|
export declare function worktreeGitDirectory(repository: GitRepository, worktree: string): Promise<string>;
|
|
61
41
|
export declare function worktreeRoot(repository: GitRepository): Promise<string>;
|
|
62
|
-
export declare function runGit(repository: GitRepository, args: readonly string[], input?: string | Uint8Array): Promise<Buffer>;
|
|
63
|
-
export declare function consumeGitStdout(repository: GitRepository, args: readonly string[], consume: (chunk: Buffer) => void): Promise<void>;
|
|
64
|
-
export declare function runGitWithEnvironment(repository: GitRepository, args: readonly string[], input: string | Uint8Array | undefined, environment: NodeJS.ProcessEnv): Promise<Buffer>;
|
|
65
42
|
export declare function readRef(repository: GitRepository, ref: string): Promise<GitOid | null>;
|
|
66
43
|
export declare function readTreeEntries(repository: GitRepository, tree: GitOid): Promise<Map<string, TreeEntry>>;
|
|
67
44
|
export declare function readGit(repository: GitRepository): Promise<GitSnapshot>;
|
|
68
45
|
/** Read only the requested private Git paths from one immutable Git tree. */
|
|
69
46
|
export declare function readGitPaths(repository: GitRepository, requestedPaths: readonly string[]): Promise<GitSnapshot>;
|
|
70
|
-
/** Add paths from the same immutable Git tree without rereading its ref. */
|
|
71
|
-
export declare function extendGitPaths(repository: GitRepository, snapshot: GitSnapshot, requestedPaths: readonly string[]): Promise<GitSnapshot>;
|
|
72
|
-
/** Expand a targeted immutable snapshot to its complete already-pinned tree. */
|
|
73
|
-
export declare function expandGitSnapshot(repository: GitRepository, snapshot: GitSnapshot): Promise<GitSnapshot>;
|
|
74
47
|
export declare function writeBlob(repository: GitRepository, bytes: string | Uint8Array): Promise<GitOid>;
|
|
75
48
|
export declare function readBlob(repository: GitRepository, oid: GitOid): Promise<Buffer>;
|
|
76
49
|
/** Copy only the changed paths' tree ancestors, preserving untouched subtrees by object ID. */
|