@astrosheep/keiyaku 4.0.0 → 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 +133 -8
- 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 +13 -5
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -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 +262 -46
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +50 -33
- package/build/src/akuma/akuma.js +175 -127
- package/build/src/akuma/archetype.d.ts +14 -5
- package/build/src/akuma/archetype.js +85 -39
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +490 -183
- 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 +88 -45
- package/build/src/akuma/heart/facts.js +19 -8
- package/build/src/akuma/heart/index.d.ts +64 -55
- package/build/src/akuma/heart/index.js +172 -252
- package/build/src/akuma/heart/rows.d.ts +38 -83
- package/build/src/akuma/heart/rows.js +117 -155
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +61 -22
- 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 +47 -0
- package/build/src/akuma/heart/storage.js +224 -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/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +10 -3
- package/build/src/akuma/index.js +7 -1
- package/build/src/akuma/projection.d.ts +176 -0
- package/build/src/akuma/projection.js +256 -0
- 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 +67 -13
- package/build/src/akuma/provider.js +172 -105
- 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.d.ts → claude/events.d.ts} +9 -41
- package/build/src/akuma/providers/claude/events.js +238 -0
- package/build/src/akuma/providers/claude/index.d.ts +47 -0
- package/build/src/akuma/providers/claude/index.js +279 -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 +15 -12
- package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
- package/build/src/akuma/providers/codex-app-server/index.js +77 -33
- 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 +35 -56
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
- package/build/src/akuma/providers/pi/events.d.ts +33 -0
- package/build/src/akuma/providers/pi/events.js +166 -0
- package/build/src/akuma/providers/pi/index.d.ts +11 -0
- package/build/src/akuma/providers/pi/index.js +191 -0
- 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 -6
- package/build/src/akuma/requests.js +95 -65
- package/build/src/alias/index.d.ts +4 -3
- package/build/src/alias/index.js +12 -13
- 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/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 +18 -12
- package/build/src/cli/accepted.js +73 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
- package/build/src/cli/commands/akuma-invoke.js +101 -52
- package/build/src/cli/commands/akuma.d.ts +27 -9
- package/build/src/cli/commands/akuma.js +151 -80
- package/build/src/cli/commands/contract.d.ts +155 -0
- package/build/src/cli/commands/contract.js +20 -0
- package/build/src/cli/commands/install.js +2 -0
- package/build/src/cli/commands/task-invoke.d.ts +19 -4
- package/build/src/cli/commands/task-invoke.js +65 -17
- 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 +52 -14
- 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 +9 -3
- package/build/src/cli/invoke.js +287 -108
- package/build/src/cli/main.js +54 -7
- package/build/src/cli/parse.d.ts +21 -117
- package/build/src/cli/parse.js +79 -84
- 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 +4 -2
- package/build/src/cli/render/akuma-tool.js +35 -5
- package/build/src/cli/render/akuma.d.ts +3 -1
- package/build/src/cli/render/akuma.js +247 -212
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- 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 +3 -3
- package/build/src/cli/render/contract.js +200 -54
- package/build/src/cli/render/kanshi.js +272 -129
- 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 -3
- 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 +20 -0
- package/build/src/cli/render/terminal.js +121 -0
- package/build/src/cli/render/text.js +11 -3
- package/build/src/cli/result.d.ts +126 -19
- package/build/src/cli/usage.d.ts +1 -0
- package/build/src/cli/usage.js +3 -0
- package/build/src/contract-worktree.d.ts +44 -0
- package/build/src/contract-worktree.js +251 -0
- package/build/src/coordination/durable-file.d.ts +5 -1
- package/build/src/coordination/durable-file.js +57 -10
- 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/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/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/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.d.ts +1 -1
- package/build/src/core/verbs/amend.js +3 -18
- package/build/src/core/verbs/bind.js +2 -16
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +14 -4
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +7 -9
- package/build/src/dispatch/index.d.ts +5 -3
- package/build/src/dispatch/index.js +30 -21
- 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 +33 -23
- package/build/src/git/hooks.d.ts +15 -0
- package/build/src/git/hooks.js +70 -18
- package/build/src/git/identity.d.ts +5 -1
- package/build/src/git/identity.js +23 -4
- package/build/src/git/integration.d.ts +48 -0
- package/build/src/git/integration.js +250 -0
- package/build/src/git/observe.d.ts +29 -15
- package/build/src/git/observe.js +185 -76
- 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 +17 -5
- package/build/src/git/reconcile.js +305 -109
- package/build/src/git/repository.d.ts +41 -18
- package/build/src/git/repository.js +220 -114
- package/build/src/git/scratch.d.ts +17 -0
- package/build/src/git/scratch.js +73 -0
- package/build/src/git/target-placement.d.ts +92 -0
- package/build/src/git/target-placement.js +375 -0
- package/build/src/git/tender.d.ts +49 -0
- package/build/src/git/tender.js +63 -0
- package/build/src/git/terminal-seal.d.ts +24 -0
- package/build/src/git/terminal-seal.js +89 -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 +53 -0
- package/build/src/git/workspace.js +139 -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 +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +7 -3
- package/build/src/kanshi/read.js +245 -45
- package/build/src/kanshi/report.d.ts +81 -6
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +33 -1
- package/build/src/library/address.d.ts +57 -0
- package/build/src/library/address.js +165 -0
- package/build/src/library/akuma-creation.d.ts +16 -7
- package/build/src/library/akuma-creation.js +96 -22
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- 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 +69 -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 +23 -49
- package/build/src/library/contract.js +171 -185
- package/build/src/library/delivery.d.ts +24 -0
- package/build/src/library/delivery.js +21 -0
- package/build/src/library/fleet.d.ts +65 -0
- package/build/src/library/fleet.js +153 -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 +18 -3
- package/build/src/library/keiyaku.js +12 -1
- package/build/src/library/mutation.d.ts +39 -0
- package/build/src/library/mutation.js +37 -0
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -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 +14 -1
- package/build/src/library/region.js +18 -4
- package/build/src/library/repo.d.ts +4 -13
- package/build/src/library/repo.js +14 -24
- package/build/src/protocol/attempt.d.ts +16 -3
- package/build/src/protocol/attempt.js +41 -10
- package/build/src/protocol/bind.d.ts +9 -2
- package/build/src/protocol/bind.js +48 -40
- package/build/src/protocol/intent.d.ts +57 -20
- package/build/src/protocol/intent.js +55 -79
- package/build/src/protocol/operations.d.ts +152 -43
- package/build/src/protocol/operations.js +482 -166
- package/build/src/protocol/outcome.d.ts +14 -3
- package/build/src/protocol/outcome.js +11 -3
- package/build/src/protocol/placement.d.ts +41 -0
- package/build/src/protocol/placement.js +147 -0
- 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 +18 -5
- package/build/src/protocol/read/status.js +73 -13
- 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 +4 -8
- package/build/src/runtime/proc/line-rpc.js +22 -28
- package/build/src/runtime/proc/run.d.ts +23 -21
- package/build/src/runtime/proc/run.js +123 -89
- 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 +7 -1
- package/build/src/settings.js +32 -17
- package/build/src/settlement/fence.d.ts +5 -0
- package/build/src/settlement/fence.js +14 -0
- package/build/src/settlement/holder.d.ts +35 -4
- package/build/src/settlement/holder.js +127 -28
- package/build/src/settlement/settle.d.ts +9 -1
- package/build/src/settlement/settle.js +74 -37
- package/build/src/task/board.d.ts +23 -5
- package/build/src/task/board.js +17 -10
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +15 -20
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +27 -15
- package/build/src/task/index.js +57 -35
- package/build/src/task/operations.d.ts +38 -13
- package/build/src/task/operations.js +80 -46
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +5 -7
- package/build/src/task/store.js +43 -28
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/execution.d.ts +51 -0
- package/build/src/verification/execution.js +108 -0
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +24 -0
- package/build/src/world.js +136 -0
- package/package.json +8 -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/protocol/read/audit.d.ts +0 -25
- package/build/src/protocol/read/audit.js +0 -39
- package/build/src/verification/producer.d.ts +0 -23
- package/build/src/verification/producer.js +0 -54
|
@@ -39,6 +39,11 @@ function stringValue(value, path, nonblank = true) {
|
|
|
39
39
|
}
|
|
40
40
|
return value;
|
|
41
41
|
}
|
|
42
|
+
function booleanValue(value, path) {
|
|
43
|
+
if (typeof value !== "boolean")
|
|
44
|
+
fail(path, "expected a boolean");
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
42
47
|
function brandedValue(value, path, brand) {
|
|
43
48
|
const text = stringValue(value, path);
|
|
44
49
|
try {
|
|
@@ -156,11 +161,25 @@ function validateData(kind, value) {
|
|
|
156
161
|
}
|
|
157
162
|
case "deliver": {
|
|
158
163
|
const object = requireRecord(value, path);
|
|
159
|
-
requireKeys(object, ["
|
|
164
|
+
requireKeys(object, ["tenderSnapshot", "integration", "method", "policy"], path);
|
|
165
|
+
const integration = requireRecord(object.integration, `${path}.integration`);
|
|
166
|
+
requireKeys(integration, ["predecessor", "snapshot", "changeId"], `${path}.integration`);
|
|
167
|
+
const method = stringValue(object.method, `${path}.method`);
|
|
168
|
+
if (method !== "squash")
|
|
169
|
+
fail(`${path}.method`, "unknown merge method");
|
|
170
|
+
const policy = requireRecord(object.policy, `${path}.policy`);
|
|
171
|
+
requireKeys(policy, ["requireBranchesToBeUpToDate"], `${path}.policy`);
|
|
160
172
|
return {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
tenderSnapshot: brandedValue(object.tenderSnapshot, `${path}.tenderSnapshot`, snapshotId),
|
|
174
|
+
integration: {
|
|
175
|
+
predecessor: brandedValue(integration.predecessor, `${path}.integration.predecessor`, snapshotId),
|
|
176
|
+
snapshot: brandedValue(integration.snapshot, `${path}.integration.snapshot`, snapshotId),
|
|
177
|
+
changeId: brandedValue(integration.changeId, `${path}.integration.changeId`, changeId),
|
|
178
|
+
},
|
|
179
|
+
method,
|
|
180
|
+
policy: {
|
|
181
|
+
requireBranchesToBeUpToDate: booleanValue(policy.requireBranchesToBeUpToDate, `${path}.policy.requireBranchesToBeUpToDate`),
|
|
182
|
+
},
|
|
164
183
|
};
|
|
165
184
|
}
|
|
166
185
|
case "attestation":
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import type { Offer } from "./offer.js";
|
|
2
1
|
import { type ContractsObservation } from "./observation.js";
|
|
3
|
-
import type { ContractId
|
|
2
|
+
import type { ContractId } from "./types.js";
|
|
4
3
|
type EligibilityObservation = ContractsObservation;
|
|
5
|
-
type EligibilityAttempt = Readonly<{
|
|
6
|
-
entryUlids: readonly EntryUlid[];
|
|
7
|
-
}>;
|
|
8
|
-
type PrerequisiteStatus = "unknown" | "pending" | "claimed";
|
|
9
|
-
/** Resolve prerequisite existence and eligibility from one decision snapshot. */
|
|
10
|
-
export declare function prerequisiteStatus(prerequisites: readonly ContractId[], observation: EligibilityObservation, offeredClaims?: ReadonlySet<ContractId>): PrerequisiteStatus;
|
|
11
4
|
/** Decide whether resulting prerequisites reach the amended contract. */
|
|
12
5
|
export declare function prerequisitesReach(contract: ContractId, prerequisites: readonly ContractId[], observation: EligibilityObservation): boolean;
|
|
13
6
|
export declare function samePrerequisites(left: readonly ContractId[], right: readonly ContractId[]): boolean;
|
|
14
|
-
/** Append bounds made eligible by a claimed offer from its full snapshot. */
|
|
15
|
-
export declare function placeEligibleBounds(offer: Offer, observation: EligibilityObservation, attempt: EligibilityAttempt): Offer;
|
|
16
7
|
export {};
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import { contractState } from "./observation.js";
|
|
2
|
-
/** Resolve prerequisite existence and eligibility from one decision snapshot. */
|
|
3
|
-
export function prerequisiteStatus(prerequisites, observation, offeredClaims) {
|
|
4
|
-
let pending = false;
|
|
5
|
-
for (const dependency of prerequisites) {
|
|
6
|
-
const state = contractState(observation, dependency);
|
|
7
|
-
if (state === null)
|
|
8
|
-
return "unknown";
|
|
9
|
-
if (state.terminal?.kind !== "claimed" && offeredClaims?.has(dependency) !== true)
|
|
10
|
-
pending = true;
|
|
11
|
-
}
|
|
12
|
-
return pending ? "pending" : "claimed";
|
|
13
|
-
}
|
|
14
2
|
/** Decide whether resulting prerequisites reach the amended contract. */
|
|
15
3
|
export function prerequisitesReach(contract, prerequisites, observation) {
|
|
16
4
|
const pending = [...prerequisites];
|
|
@@ -36,40 +24,3 @@ export function samePrerequisites(left, right) {
|
|
|
36
24
|
return false;
|
|
37
25
|
return left.every((id, index) => id === right[index]);
|
|
38
26
|
}
|
|
39
|
-
/** Append bounds made eligible by a claimed offer from its full snapshot. */
|
|
40
|
-
export function placeEligibleBounds(offer, observation, attempt) {
|
|
41
|
-
const claimedEntry = offer.facts[0]?.entries[0];
|
|
42
|
-
if (offer.facts.length !== 1 || offer.facts[0]?.entries.length !== 1 || claimedEntry?.kind !== "claimed") {
|
|
43
|
-
throw new Error("eligible-bound placement requires exactly one claimed fact");
|
|
44
|
-
}
|
|
45
|
-
const claimed = new Set([claimedEntry.contract]);
|
|
46
|
-
const used = new Set([claimedEntry.entry]);
|
|
47
|
-
const available = attempt.entryUlids.filter((entry) => !used.has(entry));
|
|
48
|
-
let availableIndex = 0;
|
|
49
|
-
const additions = [];
|
|
50
|
-
for (const id of observation.keys()) {
|
|
51
|
-
const state = contractState(observation, id);
|
|
52
|
-
if (!state || state.bound || state.terminal)
|
|
53
|
-
continue;
|
|
54
|
-
if (prerequisiteStatus(state.terms.after, observation, claimed) !== "claimed")
|
|
55
|
-
continue;
|
|
56
|
-
const entry = available[availableIndex];
|
|
57
|
-
if (entry === undefined)
|
|
58
|
-
throw new Error("placement attempt lacks an entry ULID for an eligible contract");
|
|
59
|
-
availableIndex += 1;
|
|
60
|
-
additions.push({
|
|
61
|
-
contractId: id,
|
|
62
|
-
expectedHead: state.head,
|
|
63
|
-
entries: [{
|
|
64
|
-
v: 1,
|
|
65
|
-
kind: "bound",
|
|
66
|
-
contract: id,
|
|
67
|
-
entry,
|
|
68
|
-
at: claimedEntry.at,
|
|
69
|
-
...(claimedEntry.actor === undefined ? {} : { actor: claimedEntry.actor }),
|
|
70
|
-
data: {},
|
|
71
|
-
}],
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return additions.length === 0 ? offer : { ...offer, facts: [...additions, ...offer.facts] };
|
|
75
|
-
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { samePrerequisites } from "./eligibility.js";
|
|
2
1
|
import { gatesSatisfied } from "./gate.js";
|
|
3
2
|
import { AuthorityCorruptionError } from "./errors.js";
|
|
4
3
|
function foldError(message) {
|
|
@@ -47,12 +46,8 @@ function foldEntry(state, entry, attestations) {
|
|
|
47
46
|
switch (entry.kind) {
|
|
48
47
|
case "bind":
|
|
49
48
|
foldError("bind may appear only once");
|
|
50
|
-
case "amend":
|
|
51
|
-
if (state.bound !== null && !samePrerequisites(state.terms.after, entry.data.after)) {
|
|
52
|
-
foldError("cannot change after once prerequisites are consumed");
|
|
53
|
-
}
|
|
49
|
+
case "amend":
|
|
54
50
|
return { ...state, terms: cloneTerms(entry.data) };
|
|
55
|
-
}
|
|
56
51
|
case "bound":
|
|
57
52
|
if (state.bound !== null)
|
|
58
53
|
foldError("bound may appear only once");
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ContractId, ContractState, DocumentKey } from "./types.js";
|
|
2
2
|
/** The complete state projection consumed by one pure pact decision. */
|
|
3
3
|
export type ContractsObservation = ReadonlyMap<ContractId, ContractState | null>;
|
|
4
|
+
export type PrerequisiteStatus = "unknown" | "pending" | "claimed";
|
|
4
5
|
/** Read one requested identity; a missing key violates the observation contract. */
|
|
5
6
|
export declare function contractState(observation: ContractsObservation, contract: ContractId): ContractState | null;
|
|
7
|
+
/** Project prerequisite existence and terminal satisfaction from one observation. */
|
|
8
|
+
export declare function prerequisiteStatus(prerequisites: readonly ContractId[], observation: ContractsObservation): PrerequisiteStatus;
|
|
6
9
|
export type ActiveContractRefusal = Readonly<{
|
|
7
10
|
kind: "contract-missing" | "terminal";
|
|
8
11
|
contractId: ContractId;
|
|
@@ -9,6 +9,18 @@ export function contractState(observation, contract) {
|
|
|
9
9
|
}
|
|
10
10
|
return state;
|
|
11
11
|
}
|
|
12
|
+
/** Project prerequisite existence and terminal satisfaction from one observation. */
|
|
13
|
+
export function prerequisiteStatus(prerequisites, observation) {
|
|
14
|
+
let pending = false;
|
|
15
|
+
for (const dependency of prerequisites) {
|
|
16
|
+
const state = contractState(observation, dependency);
|
|
17
|
+
if (state === null)
|
|
18
|
+
return "unknown";
|
|
19
|
+
if (state.terminal?.kind !== "claimed")
|
|
20
|
+
pending = true;
|
|
21
|
+
}
|
|
22
|
+
return pending ? "pending" : "claimed";
|
|
23
|
+
}
|
|
12
24
|
/** The single lifecycle guard shared by every operation on an existing contract. */
|
|
13
25
|
export function activeContract(observation, contract) {
|
|
14
26
|
const state = contractState(observation, contract);
|
|
@@ -74,9 +74,16 @@ export type BindData = Readonly<{
|
|
|
74
74
|
export type AmendData = ContractTerms;
|
|
75
75
|
export type BoundData = Readonly<Record<string, never>>;
|
|
76
76
|
export type DeliverData = Readonly<{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
tenderSnapshot: SnapshotId;
|
|
78
|
+
integration: Readonly<{
|
|
79
|
+
predecessor: SnapshotId;
|
|
80
|
+
snapshot: SnapshotId;
|
|
81
|
+
changeId: ChangeId;
|
|
82
|
+
}>;
|
|
83
|
+
method: "squash";
|
|
84
|
+
policy: Readonly<{
|
|
85
|
+
requireBranchesToBeUpToDate: boolean;
|
|
86
|
+
}>;
|
|
80
87
|
}>;
|
|
81
88
|
export type AttestationData = Readonly<{
|
|
82
89
|
gate: Gate;
|
|
@@ -69,8 +69,8 @@ function currentKeys(state) {
|
|
|
69
69
|
encodeKey({ kind: "document", value: state.terms.document.key }),
|
|
70
70
|
...state.terms.segments.map((value) => encodeKey({ kind: "segment", value })),
|
|
71
71
|
...(state.delivery === null ? [] : [
|
|
72
|
-
encodeKey({ kind: "snapshot", value: state.delivery.data.
|
|
73
|
-
encodeKey({ kind: "change", value: state.delivery.data.
|
|
72
|
+
encodeKey({ kind: "snapshot", value: state.delivery.data.integration.snapshot }),
|
|
73
|
+
encodeKey({ kind: "change", value: state.delivery.data.integration.changeId }),
|
|
74
74
|
]),
|
|
75
75
|
]);
|
|
76
76
|
}
|
|
@@ -8,7 +8,7 @@ export type AmendInput<Failure = never> = Readonly<{
|
|
|
8
8
|
preparation?: Preparation<AmendData, Failure>;
|
|
9
9
|
}>;
|
|
10
10
|
export type AmendRefusal = Readonly<{
|
|
11
|
-
kind: "contract-missing" | "terminal" | "terms-moved" | "
|
|
11
|
+
kind: "contract-missing" | "terminal" | "terms-moved" | "unknown-prerequisite" | "cyclic-prerequisite";
|
|
12
12
|
contractId: ContractId;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function decideAmend<Failure>({ input, attempt, observation }: DecideInput<AmendInput<Failure>>): OfferDecision<AmendRefusal | Failure>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { activeContract } from "../facts/observation.js";
|
|
1
|
+
import { prerequisitesReach, samePrerequisites } from "../facts/eligibility.js";
|
|
2
|
+
import { activeContract, prerequisiteStatus } from "../facts/observation.js";
|
|
3
3
|
export function decideAmend({ input, attempt, observation }) {
|
|
4
4
|
const id = input.contractId;
|
|
5
5
|
const current = activeContract(observation, id);
|
|
@@ -21,9 +21,6 @@ export function decideAmend({ input, attempt, observation }) {
|
|
|
21
21
|
if (input.preparation.kind === "refused")
|
|
22
22
|
return { kind: "refused", refusal: input.preparation.refusal };
|
|
23
23
|
const { data } = input.preparation;
|
|
24
|
-
if (current.bound !== null && !samePrerequisites(current.terms.after, data.after)) {
|
|
25
|
-
return { kind: "refused", refusal: { kind: "prerequisites-already-consumed", contractId: id } };
|
|
26
|
-
}
|
|
27
24
|
const prerequisites = prerequisiteStatus(data.after, observation);
|
|
28
25
|
if (prerequisites === "unknown") {
|
|
29
26
|
return { kind: "refused", refusal: { kind: "unknown-prerequisite", contractId: id } };
|
|
@@ -40,17 +37,5 @@ export function decideAmend({ input, attempt, observation }) {
|
|
|
40
37
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
41
38
|
data,
|
|
42
39
|
};
|
|
43
|
-
|
|
44
|
-
if (current.bound === null && prerequisites === "claimed") {
|
|
45
|
-
entries.push({
|
|
46
|
-
v: 1,
|
|
47
|
-
kind: "bound",
|
|
48
|
-
contract: id,
|
|
49
|
-
entry: attempt.entryUlids[1],
|
|
50
|
-
at: input.at,
|
|
51
|
-
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
52
|
-
data: {},
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: current.head, entries }] } };
|
|
40
|
+
return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: current.head, entries: [entry] }] } };
|
|
56
41
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { prerequisiteStatus } from "../facts/
|
|
2
|
-
import { contractState } from "../facts/observation.js";
|
|
1
|
+
import { contractState, prerequisiteStatus } from "../facts/observation.js";
|
|
3
2
|
export function decideBind({ input, attempt, observation }) {
|
|
4
3
|
const id = input.contractId;
|
|
5
4
|
const current = contractState(observation, id);
|
|
@@ -23,18 +22,5 @@ export function decideBind({ input, attempt, observation }) {
|
|
|
23
22
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
24
23
|
data,
|
|
25
24
|
};
|
|
26
|
-
|
|
27
|
-
if (prerequisites === "claimed") {
|
|
28
|
-
const bound = {
|
|
29
|
-
v: 1,
|
|
30
|
-
kind: "bound",
|
|
31
|
-
contract: id,
|
|
32
|
-
entry: attempt.entryUlids[1],
|
|
33
|
-
at: input.at,
|
|
34
|
-
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
35
|
-
data: {},
|
|
36
|
-
};
|
|
37
|
-
entries.push(bound);
|
|
38
|
-
}
|
|
39
|
-
return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: null, entries }] } };
|
|
25
|
+
return { kind: "offer", offer: { facts: [{ contractId: id, expectedHead: null, entries: [bind] }] } };
|
|
40
26
|
}
|
|
@@ -7,7 +7,7 @@ export type DeliverInput<Failure = never> = Readonly<{
|
|
|
7
7
|
preparation: StampedPreparation<DeliverData, Failure>;
|
|
8
8
|
}>;
|
|
9
9
|
export type DeliverRefusal = Readonly<{
|
|
10
|
-
kind: "contract-missing" | "
|
|
10
|
+
kind: "contract-missing" | "terminal" | "document-moved";
|
|
11
11
|
contractId: ContractId;
|
|
12
12
|
}>;
|
|
13
13
|
export declare function decideDeliver<Failure>({ input, attempt, observation, }: DecideInput<DeliverInput<Failure>>): OfferDecision<DeliverRefusal | Failure>;
|
|
@@ -3,8 +3,6 @@ export function decideDeliver({ input, attempt, observation, }) {
|
|
|
3
3
|
const state = activeContract(observation, input.contractId);
|
|
4
4
|
if ("kind" in state)
|
|
5
5
|
return { kind: "refused", refusal: state };
|
|
6
|
-
if (state.bound === null)
|
|
7
|
-
return { kind: "refused", refusal: { kind: "not-bound", contractId: input.contractId } };
|
|
8
6
|
if (input.preparation.kind === "unavailable") {
|
|
9
7
|
throw new Error("existing contract requires a stamped delivery preparation");
|
|
10
8
|
}
|
|
@@ -13,14 +11,26 @@ export function decideDeliver({ input, attempt, observation, }) {
|
|
|
13
11
|
}
|
|
14
12
|
if (input.preparation.kind === "refused")
|
|
15
13
|
return { kind: "refused", refusal: input.preparation.refusal };
|
|
14
|
+
const entries = [];
|
|
15
|
+
if (state.bound === null)
|
|
16
|
+
entries.push({
|
|
17
|
+
v: 1,
|
|
18
|
+
kind: "bound",
|
|
19
|
+
contract: input.contractId,
|
|
20
|
+
entry: attempt.entryUlids[0],
|
|
21
|
+
at: input.at,
|
|
22
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
23
|
+
data: {},
|
|
24
|
+
});
|
|
16
25
|
const deliver = {
|
|
17
26
|
v: 1,
|
|
18
27
|
kind: "deliver",
|
|
19
28
|
contract: input.contractId,
|
|
20
|
-
entry: attempt.entryUlids[0],
|
|
29
|
+
entry: attempt.entryUlids[state.bound === null ? 1 : 0],
|
|
21
30
|
at: input.at,
|
|
22
31
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
23
32
|
data: input.preparation.data,
|
|
24
33
|
};
|
|
25
|
-
|
|
34
|
+
entries.push(deliver);
|
|
35
|
+
return { kind: "offer", offer: { facts: [{ contractId: input.contractId, expectedHead: state.head, entries }] } };
|
|
26
36
|
}
|
|
@@ -6,7 +6,7 @@ type PlacementInput = Readonly<{
|
|
|
6
6
|
at: string;
|
|
7
7
|
}>;
|
|
8
8
|
export type PlacementRefusal = Readonly<{
|
|
9
|
-
kind: "contract-missing" | "delivery-missing" | "terminal" | "gates-unsatisfied";
|
|
9
|
+
kind: "contract-missing" | "delivery-missing" | "terminal" | "prerequisites-unsatisfied" | "gates-unsatisfied";
|
|
10
10
|
contractId: ContractId;
|
|
11
11
|
}>;
|
|
12
12
|
export declare function decidePlacement({ input, attempt, observation }: DecideInput<PlacementInput>): OfferDecision<PlacementRefusal>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { gatesSatisfied } from "../facts/gate.js";
|
|
2
|
-
import {
|
|
3
|
-
import { activeContract } from "../facts/observation.js";
|
|
2
|
+
import { activeContract, prerequisiteStatus } from "../facts/observation.js";
|
|
4
3
|
export function decidePlacement({ input, attempt, observation }) {
|
|
5
4
|
const id = input.contractId;
|
|
6
5
|
const current = activeContract(observation, id);
|
|
@@ -10,6 +9,9 @@ export function decidePlacement({ input, attempt, observation }) {
|
|
|
10
9
|
if (!delivery) {
|
|
11
10
|
return { kind: "refused", refusal: { kind: "delivery-missing", contractId: id } };
|
|
12
11
|
}
|
|
12
|
+
if (prerequisiteStatus(current.terms.after, observation) !== "claimed") {
|
|
13
|
+
return { kind: "refused", refusal: { kind: "prerequisites-unsatisfied", contractId: id } };
|
|
14
|
+
}
|
|
13
15
|
if (!gatesSatisfied(current)) {
|
|
14
16
|
return { kind: "refused", refusal: { kind: "gates-unsatisfied", contractId: id } };
|
|
15
17
|
}
|
|
@@ -22,21 +24,17 @@ export function decidePlacement({ input, attempt, observation }) {
|
|
|
22
24
|
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
23
25
|
data: { delivery: delivery.entry },
|
|
24
26
|
};
|
|
25
|
-
|
|
27
|
+
return {
|
|
26
28
|
kind: "offer",
|
|
27
29
|
offer: {
|
|
28
30
|
facts: [{ contractId: id, expectedHead: current.head, entries: [claimed] }],
|
|
29
31
|
...(current.coordinates.target === undefined ? {} : {
|
|
30
32
|
target: {
|
|
31
33
|
target: current.coordinates.target,
|
|
32
|
-
expectedOid: delivery.data.
|
|
33
|
-
newOid: delivery.data.
|
|
34
|
+
expectedOid: delivery.data.integration.predecessor,
|
|
35
|
+
newOid: delivery.data.integration.snapshot,
|
|
34
36
|
},
|
|
35
37
|
}),
|
|
36
38
|
},
|
|
37
39
|
};
|
|
38
|
-
return {
|
|
39
|
-
...offer,
|
|
40
|
-
offer: placeEligibleBounds(offer.offer, observation, attempt),
|
|
41
|
-
};
|
|
42
40
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ContractId } from "../core/facts/types.js";
|
|
2
2
|
import { type AkuId } from "../akuma/identity.js";
|
|
3
3
|
import { type GitRepository } from "../git/repository.js";
|
|
4
|
+
import { type GitReadObservation } from "../git/read-observation.js";
|
|
4
5
|
export type Dispatch = Readonly<{
|
|
5
6
|
akuId: AkuId;
|
|
6
7
|
contractId: ContractId;
|
|
@@ -22,10 +23,11 @@ export type DispatchPublication = Readonly<{
|
|
|
22
23
|
kind: "failed";
|
|
23
24
|
failure: DispatchFailure;
|
|
24
25
|
}>;
|
|
25
|
-
export declare function readDispatch(repository: GitRepository, value: AkuId): Dispatch | null
|
|
26
|
-
export declare function
|
|
26
|
+
export declare function readDispatch(repository: GitRepository, value: AkuId): Promise<Dispatch | null>;
|
|
27
|
+
export declare function readDispatchesAt(observation: GitReadObservation): Promise<readonly Dispatch[]>;
|
|
28
|
+
export declare function readDispatches(repository: GitRepository): Promise<readonly Dispatch[]>;
|
|
27
29
|
export declare function publishDispatch(input: Readonly<{
|
|
28
30
|
repository: GitRepository;
|
|
29
31
|
akuId: AkuId;
|
|
30
32
|
contractId: ContractId;
|
|
31
|
-
}>): DispatchPublication
|
|
33
|
+
}>): Promise<DispatchPublication>;
|
|
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
3
3
|
import { contractId } from "../core/facts/types.js";
|
|
4
4
|
import { parseAkuId } from "../akuma/identity.js";
|
|
5
|
-
import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob,
|
|
5
|
+
import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeCommit, } from "../git/repository.js";
|
|
6
|
+
import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observation.js";
|
|
6
7
|
const DISPATCH_ROOT = "dispatch";
|
|
7
8
|
const DISPATCH_PREFIX = `${DISPATCH_ROOT}/`;
|
|
8
9
|
const DISPATCH_SUFFIX = ".json";
|
|
@@ -63,25 +64,27 @@ function decode(path, bytes) {
|
|
|
63
64
|
}
|
|
64
65
|
return dispatch;
|
|
65
66
|
}
|
|
66
|
-
function dispatchFromSnapshot(repository, snapshot, akuId) {
|
|
67
|
+
async function dispatchFromSnapshot(repository, snapshot, akuId) {
|
|
67
68
|
const path = pathFor(akuId);
|
|
68
69
|
const entry = snapshot.paths.get(path);
|
|
69
70
|
if (entry === undefined)
|
|
70
71
|
return null;
|
|
71
72
|
if (entry.type !== "blob")
|
|
72
73
|
corruption(`Dispatch path is not a blob: ${path}`);
|
|
73
|
-
return decode(path, readBlob(repository, entry.oid));
|
|
74
|
+
return decode(path, await readBlob(repository, entry.oid));
|
|
74
75
|
}
|
|
75
|
-
export function readDispatch(repository, value) {
|
|
76
|
+
export async function readDispatch(repository, value) {
|
|
76
77
|
const akuId = parseAkuId(value).id;
|
|
77
78
|
const path = pathFor(akuId);
|
|
78
|
-
return dispatchFromSnapshot(repository, readGitPaths(repository, [path]), akuId);
|
|
79
|
+
return await dispatchFromSnapshot(repository, await readGitPaths(repository, [path]), akuId);
|
|
79
80
|
}
|
|
80
|
-
export function
|
|
81
|
-
const
|
|
81
|
+
export async function readDispatchesAt(observation) {
|
|
82
|
+
const entries = [...observation.snapshot.paths]
|
|
83
|
+
.filter(([path, entry]) => path.startsWith(DISPATCH_PREFIX) && entry.type === "blob");
|
|
84
|
+
const blobs = await observation.readBlobs(entries.map(([, entry]) => entry.oid));
|
|
82
85
|
const dispatches = [];
|
|
83
86
|
const seen = new Set();
|
|
84
|
-
for (const [path, entry] of snapshot.paths) {
|
|
87
|
+
for (const [path, entry] of observation.snapshot.paths) {
|
|
85
88
|
if (path === DISPATCH_ROOT)
|
|
86
89
|
corruption(`Dispatch authority root is not a tree: ${path}`);
|
|
87
90
|
if (!path.startsWith(DISPATCH_PREFIX))
|
|
@@ -90,7 +93,10 @@ export function readDispatches(repository) {
|
|
|
90
93
|
corruption(`unexpected Dispatch authority path: ${path}`);
|
|
91
94
|
if (entry.type !== "blob")
|
|
92
95
|
corruption(`Dispatch path is not a blob: ${path}`);
|
|
93
|
-
const
|
|
96
|
+
const result = blobs.get(entry.oid);
|
|
97
|
+
if (result?.kind !== "present")
|
|
98
|
+
corruption(`missing Dispatch Git object: ${path}`);
|
|
99
|
+
const dispatch = decode(path, result.bytes);
|
|
94
100
|
if (seen.has(dispatch.akuId))
|
|
95
101
|
corruption(`duplicate Dispatch identity: ${dispatch.akuId}`);
|
|
96
102
|
seen.add(dispatch.akuId);
|
|
@@ -98,8 +104,11 @@ export function readDispatches(repository) {
|
|
|
98
104
|
}
|
|
99
105
|
return dispatches.sort((left, right) => Buffer.compare(Buffer.from(left.akuId), Buffer.from(right.akuId)));
|
|
100
106
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
107
|
+
export async function readDispatches(repository) {
|
|
108
|
+
return await withGitDecodeChannel(repository, (channel) => withGitReadObservation(repository, channel, readDispatchesAt));
|
|
109
|
+
}
|
|
110
|
+
async function observedPublication(repository, akuId, owner) {
|
|
111
|
+
const current = await readDispatch(repository, akuId);
|
|
103
112
|
if (current === null)
|
|
104
113
|
return null;
|
|
105
114
|
return current.contractId === owner
|
|
@@ -109,14 +118,14 @@ function observedPublication(repository, akuId, owner) {
|
|
|
109
118
|
function diagnostic(error) {
|
|
110
119
|
return error instanceof Error ? error.message : String(error);
|
|
111
120
|
}
|
|
112
|
-
export function publishDispatch(input) {
|
|
121
|
+
export async function publishDispatch(input) {
|
|
113
122
|
const akuId = parseAkuId(input.akuId).id;
|
|
114
123
|
const owner = contractId(input.contractId);
|
|
115
124
|
const intended = { akuId, contractId: owner, dispatchedAt: new Date().toISOString() };
|
|
116
125
|
const path = pathFor(akuId);
|
|
117
126
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
|
|
118
|
-
const snapshot = readGitPaths(input.repository, [path]);
|
|
119
|
-
const current = dispatchFromSnapshot(input.repository, snapshot, akuId);
|
|
127
|
+
const snapshot = await readGitPaths(input.repository, [path]);
|
|
128
|
+
const current = await dispatchFromSnapshot(input.repository, snapshot, akuId);
|
|
120
129
|
if (current !== null) {
|
|
121
130
|
return current.contractId === owner
|
|
122
131
|
? { kind: "dispatched", dispatch: current }
|
|
@@ -124,29 +133,29 @@ export function publishDispatch(input) {
|
|
|
124
133
|
}
|
|
125
134
|
const changes = new Map();
|
|
126
135
|
if (snapshot.commit === null) {
|
|
127
|
-
changes.set(GIT_FORMAT_PATH, { oid: writeBlob(input.repository, GIT_FORMAT_BYTES) });
|
|
136
|
+
changes.set(GIT_FORMAT_PATH, { oid: await writeBlob(input.repository, GIT_FORMAT_BYTES) });
|
|
128
137
|
}
|
|
129
|
-
changes.set(path, { oid: writeBlob(input.repository, bytesFor(intended)) });
|
|
130
|
-
const tree = updateGitTree(input.repository, snapshot.tree, changes);
|
|
131
|
-
const commit = writeCommit({
|
|
138
|
+
changes.set(path, { oid: await writeBlob(input.repository, bytesFor(intended)) });
|
|
139
|
+
const tree = await updateGitTree(input.repository, snapshot.tree, changes);
|
|
140
|
+
const commit = await writeCommit({
|
|
132
141
|
repository: input.repository,
|
|
133
142
|
tree,
|
|
134
143
|
parent: snapshot.commit,
|
|
135
144
|
message: `dispatch ${akuId}`,
|
|
136
145
|
at: intended.dispatchedAt,
|
|
137
146
|
});
|
|
138
|
-
const publication = updateRefsAtomically(input.repository, [{
|
|
147
|
+
const publication = await updateRefsAtomically(input.repository, [{
|
|
139
148
|
ref: GIT_REF,
|
|
140
149
|
newOid: commit,
|
|
141
150
|
expectedOid: snapshot.commit,
|
|
142
151
|
}]);
|
|
143
152
|
if (publication.kind === "published")
|
|
144
153
|
return { kind: "dispatched", dispatch: intended };
|
|
145
|
-
const observed = observedPublication(input.repository, akuId, owner);
|
|
154
|
+
const observed = await observedPublication(input.repository, akuId, owner);
|
|
146
155
|
if (observed !== null)
|
|
147
156
|
return observed;
|
|
148
157
|
if (publication.kind === "non-published") {
|
|
149
|
-
const fresh = readGitPaths(input.repository, [path]);
|
|
158
|
+
const fresh = await readGitPaths(input.repository, [path]);
|
|
150
159
|
if (fresh.commit === snapshot.commit) {
|
|
151
160
|
return {
|
|
152
161
|
kind: "failed",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type DurationParseResult = Readonly<{
|
|
2
|
+
kind: "parsed";
|
|
3
|
+
milliseconds: number;
|
|
4
|
+
}> | Readonly<{
|
|
5
|
+
kind: "invalid";
|
|
6
|
+
}> | Readonly<{
|
|
7
|
+
kind: "overflow";
|
|
8
|
+
}>;
|
|
9
|
+
export declare function parseDuration(value: string): DurationParseResult;
|
|
10
|
+
export declare function formatDuration(milliseconds: number): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const MULTIPLIERS = Object.freeze({
|
|
2
|
+
ms: 1n,
|
|
3
|
+
s: 1000n,
|
|
4
|
+
m: 60000n,
|
|
5
|
+
h: 3600000n,
|
|
6
|
+
});
|
|
7
|
+
export function parseDuration(value) {
|
|
8
|
+
const match = /^(0|[1-9][0-9]*)(ms|s|m|h)$/u.exec(value);
|
|
9
|
+
if (match === null)
|
|
10
|
+
return { kind: "invalid" };
|
|
11
|
+
const milliseconds = BigInt(match[1]) * MULTIPLIERS[match[2]];
|
|
12
|
+
if (milliseconds > BigInt(Number.MAX_SAFE_INTEGER))
|
|
13
|
+
return { kind: "overflow" };
|
|
14
|
+
return { kind: "parsed", milliseconds: Number(milliseconds) };
|
|
15
|
+
}
|
|
16
|
+
export function formatDuration(milliseconds) {
|
|
17
|
+
for (const [unit, multiplier] of [["h", 3_600_000], ["m", 60_000], ["s", 1_000]]) {
|
|
18
|
+
if (milliseconds >= multiplier && milliseconds % multiplier === 0)
|
|
19
|
+
return `${milliseconds / multiplier}${unit}`;
|
|
20
|
+
}
|
|
21
|
+
return `${milliseconds}ms`;
|
|
22
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Offer } from "../core/facts/offer.js";
|
|
2
2
|
import type { GitAdmissionSnapshot } from "./observe.js";
|
|
3
|
-
import { type GitRepository } from "./repository.js";
|
|
3
|
+
import { type GitRepository, type GitRefAssertion } from "./repository.js";
|
|
4
4
|
import { type ContractHead } from "../core/facts/types.js";
|
|
5
5
|
type Accepted = Readonly<{
|
|
6
6
|
kind: "accepted";
|
|
@@ -14,5 +14,5 @@ type Unknown = Readonly<{
|
|
|
14
14
|
kind: "unknown";
|
|
15
15
|
}>;
|
|
16
16
|
export type Admission = Accepted | PublicationFailed | Unknown;
|
|
17
|
-
export declare function admit(repository: GitRepository, offer: Offer, admission: GitAdmissionSnapshot): Admission
|
|
17
|
+
export declare function admit(repository: GitRepository, offer: Offer, admission: GitAdmissionSnapshot, assertions?: readonly GitRefAssertion[]): Promise<Admission>;
|
|
18
18
|
export {};
|