@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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readAliases } from "../alias/index.js";
|
|
2
|
+
import { Akuma } from "../akuma/akuma.js";
|
|
3
|
+
import { probeBornAkuma } from "../akuma/index.js";
|
|
4
|
+
import { parseAkuId } from "../akuma/identity.js";
|
|
5
|
+
import { contractId } from "../core/facts/types.js";
|
|
6
|
+
import { readDispatches } from "../dispatch/index.js";
|
|
7
|
+
import { matchesAkumaGlob, parseAkumaAlias, parseAkumaGlob, } from "../identity/selector.js";
|
|
8
|
+
import { requireInput } from "./input.js";
|
|
9
|
+
import { scopeForRepo } from "./repo.js";
|
|
10
|
+
export class AkumaWorldScopeError extends TypeError {
|
|
11
|
+
refusal;
|
|
12
|
+
constructor(refusal) {
|
|
13
|
+
super(`akuma-not-in-world ${refusal.world} ${refusal.ids.join(" ")}`);
|
|
14
|
+
this.name = "AkumaWorldScopeError";
|
|
15
|
+
this.refusal = refusal;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function nonblank(value, label) {
|
|
19
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
20
|
+
throw new TypeError(`${label} must be a nonblank string`);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function settingsOption(value) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof value !== "object" || value === null || typeof value.namespace !== "function") {
|
|
27
|
+
throw new TypeError("settings must be a Settings");
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function world(path, settings) {
|
|
32
|
+
return Akuma.of(path, settings);
|
|
33
|
+
}
|
|
34
|
+
async function directId(path, selector) {
|
|
35
|
+
if (selector.startsWith("@")) {
|
|
36
|
+
const alias = parseAkumaAlias(selector);
|
|
37
|
+
const resolved = (await readAliases(path)).find((binding) => binding.alias === alias)?.akuId ?? null;
|
|
38
|
+
if (resolved === null)
|
|
39
|
+
throw new TypeError(`unknown Akuma alias: ${alias}`);
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
return parseAkuId(selector).id;
|
|
43
|
+
}
|
|
44
|
+
export async function resolveNamedAddress(input) {
|
|
45
|
+
const selector = nonblank(input.selector, "selector");
|
|
46
|
+
if (selector.startsWith("kei/"))
|
|
47
|
+
return { kind: "contract", id: contractId(selector) };
|
|
48
|
+
if (selector.startsWith("aku/"))
|
|
49
|
+
return { kind: "akuma", id: parseAkuId(selector).id };
|
|
50
|
+
const alias = parseAkumaAlias(selector);
|
|
51
|
+
const contractMatches = input.contracts.filter((row) => row.disposition === "active"
|
|
52
|
+
&& row.workspace === "worktree" && row.worktreePath !== null
|
|
53
|
+
&& `@${row.id.slice("kei/".length)}` === alias);
|
|
54
|
+
const aliasId = input.path === null
|
|
55
|
+
? null
|
|
56
|
+
: (await readAliases(input.path)).find((binding) => binding.alias === alias)?.akuId ?? null;
|
|
57
|
+
if (contractMatches.length > 0 && aliasId !== null)
|
|
58
|
+
throw new TypeError(`ambiguous selector matches Contract and Akuma: ${selector}`);
|
|
59
|
+
if (contractMatches.length === 1)
|
|
60
|
+
return { kind: "contract", id: contractMatches[0].id };
|
|
61
|
+
if (contractMatches.length > 1)
|
|
62
|
+
throw new TypeError(`ambiguous Contract selector: ${selector}`);
|
|
63
|
+
if (aliasId !== null)
|
|
64
|
+
return { kind: "akuma", id: aliasId };
|
|
65
|
+
throw new TypeError(`unknown selector: ${selector}`);
|
|
66
|
+
}
|
|
67
|
+
export async function addressAkuma(input) {
|
|
68
|
+
const values = requireInput(input, "Akuma address input");
|
|
69
|
+
for (const key of Object.keys(values)) {
|
|
70
|
+
if (!["path", "akuma", "settings", "repo"].includes(key))
|
|
71
|
+
throw new TypeError(`Akuma address input has unknown field: ${key}`);
|
|
72
|
+
}
|
|
73
|
+
if (values.repo !== undefined)
|
|
74
|
+
scopeForRepo(values.repo);
|
|
75
|
+
const path = nonblank(values.path, "path");
|
|
76
|
+
const settings = settingsOption(values.settings);
|
|
77
|
+
return { path, id: await directId(path, nonblank(values.akuma, "akuma")), ...(settings === undefined ? {} : { settings }) };
|
|
78
|
+
}
|
|
79
|
+
function idsFromFleet(fleet) {
|
|
80
|
+
return fleet.rows.map((row) => row.id);
|
|
81
|
+
}
|
|
82
|
+
function parseSetSelector(raw) {
|
|
83
|
+
const selector = nonblank(raw, "akuma selector");
|
|
84
|
+
if (selector.startsWith("kei/"))
|
|
85
|
+
return { kind: "contract", value: contractId(selector) };
|
|
86
|
+
if (selector.includes("*"))
|
|
87
|
+
return { kind: "glob", value: parseAkumaGlob(selector) };
|
|
88
|
+
if (selector.startsWith("@"))
|
|
89
|
+
return { kind: "alias", value: parseAkumaAlias(selector) };
|
|
90
|
+
return { kind: "direct", value: parseAkuId(selector).id };
|
|
91
|
+
}
|
|
92
|
+
function hasSelectorKind(selectors, kind) {
|
|
93
|
+
return selectors.some((selector) => selector.kind === kind);
|
|
94
|
+
}
|
|
95
|
+
function addSelectorIds(selector, sources, selected, contractMembers) {
|
|
96
|
+
if (selector.kind === "contract") {
|
|
97
|
+
for (const dispatch of sources.dispatches) {
|
|
98
|
+
if (dispatch.contractId !== selector.value)
|
|
99
|
+
continue;
|
|
100
|
+
selected.add(dispatch.akuId);
|
|
101
|
+
contractMembers.add(dispatch.akuId);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (selector.kind === "glob") {
|
|
106
|
+
for (const id of sources.fleetIds)
|
|
107
|
+
if (matchesAkumaGlob(selector.value, id))
|
|
108
|
+
selected.add(id);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (selector.kind === "alias") {
|
|
112
|
+
const id = sources.aliases.get(selector.value);
|
|
113
|
+
if (id === undefined)
|
|
114
|
+
throw new TypeError(`unknown Akuma alias: ${selector.value}`);
|
|
115
|
+
selected.add(id);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
selected.add(selector.value);
|
|
119
|
+
}
|
|
120
|
+
async function contractMemberInWorld(path, id) {
|
|
121
|
+
return await probeBornAkuma(path, id);
|
|
122
|
+
}
|
|
123
|
+
async function refuseForeignContractMembers(path, ids, contractMembers) {
|
|
124
|
+
const foreign = [];
|
|
125
|
+
for (const id of ids) {
|
|
126
|
+
if (contractMembers.has(id) && !await contractMemberInWorld(path, id))
|
|
127
|
+
foreign.push(id);
|
|
128
|
+
}
|
|
129
|
+
if (foreign.length > 0)
|
|
130
|
+
throw new AkumaWorldScopeError({ kind: "akuma-not-in-world", ids: foreign, world: path });
|
|
131
|
+
}
|
|
132
|
+
export async function addressAkumaSet(input) {
|
|
133
|
+
const values = requireInput(input, "Akuma set address input");
|
|
134
|
+
for (const key of Object.keys(values)) {
|
|
135
|
+
if (!["path", "akuma", "settings", "repo"].includes(key))
|
|
136
|
+
throw new TypeError(`Akuma set address input has unknown field: ${key}`);
|
|
137
|
+
}
|
|
138
|
+
if (!Array.isArray(values.akuma) || values.akuma.length === 0)
|
|
139
|
+
throw new TypeError("akuma must be a nonempty selector array");
|
|
140
|
+
const path = nonblank(values.path, "path");
|
|
141
|
+
const settings = settingsOption(values.settings);
|
|
142
|
+
const selectors = values.akuma.map(parseSetSelector);
|
|
143
|
+
if (hasSelectorKind(selectors, "contract") && values.repo === undefined) {
|
|
144
|
+
throw new TypeError("Contract Akuma selector requires repo");
|
|
145
|
+
}
|
|
146
|
+
const fleetIds = hasSelectorKind(selectors, "glob")
|
|
147
|
+
? idsFromFleet(await world(path, settings).list())
|
|
148
|
+
: [];
|
|
149
|
+
const aliases = hasSelectorKind(selectors, "alias")
|
|
150
|
+
? new Map((await readAliases(path)).map((binding) => [binding.alias, binding.akuId]))
|
|
151
|
+
: new Map();
|
|
152
|
+
const dispatches = hasSelectorKind(selectors, "contract")
|
|
153
|
+
? await readDispatches(scopeForRepo(values.repo))
|
|
154
|
+
: [];
|
|
155
|
+
const selected = new Set();
|
|
156
|
+
const contractMembers = new Set();
|
|
157
|
+
const sources = { fleetIds, aliases, dispatches };
|
|
158
|
+
for (const selector of selectors)
|
|
159
|
+
addSelectorIds(selector, sources, selected, contractMembers);
|
|
160
|
+
const ids = [...selected].sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
|
|
161
|
+
if (ids.length === 0)
|
|
162
|
+
throw new TypeError("Akuma selector snapshot is empty");
|
|
163
|
+
await refuseForeignContractMembers(path, ids, contractMembers);
|
|
164
|
+
return { path, ids, ...(settings === undefined ? {} : { settings }) };
|
|
165
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type AliasBinding } from "../alias/index.js";
|
|
2
|
-
import { type AkumaStatus, type ForkReceipt } from "../akuma/akuma.js";
|
|
3
|
-
import {
|
|
2
|
+
import { type AkumaStatus, type ForkReceipt, type ReadonlyRestraint } from "../akuma/akuma.js";
|
|
3
|
+
import type { AkuId } from "../akuma/identity.js";
|
|
4
4
|
import { type Dispatch, type DispatchFailure } from "../dispatch/index.js";
|
|
5
5
|
import { type AkumaAlias } from "../identity/selector.js";
|
|
6
6
|
import type { Settings } from "../settings.js";
|
|
7
|
+
import type { WorldRoot } from "../world.js";
|
|
7
8
|
import { type Keiyaku } from "./contract.js";
|
|
8
9
|
import { type Repo } from "./repo.js";
|
|
9
10
|
export type { AkumaStatus } from "../akuma/akuma.js";
|
|
@@ -34,7 +35,7 @@ export type AliasStage = Readonly<{
|
|
|
34
35
|
failure: IntegrationFailure;
|
|
35
36
|
}>;
|
|
36
37
|
export type CallInput = Readonly<{
|
|
37
|
-
path:
|
|
38
|
+
path: WorldRoot;
|
|
38
39
|
archetype: string;
|
|
39
40
|
body: string;
|
|
40
41
|
cwd?: string;
|
|
@@ -56,24 +57,32 @@ export type CallObservation = Readonly<{
|
|
|
56
57
|
export type CallResult = Readonly<{
|
|
57
58
|
kind: "called";
|
|
58
59
|
akuma: AkuId;
|
|
60
|
+
readonly?: ReadonlyRestraint;
|
|
61
|
+
execution: Readonly<{
|
|
62
|
+
cwd: string;
|
|
63
|
+
source: "input" | "world" | "contract-worktree";
|
|
64
|
+
}>;
|
|
59
65
|
dispatch: DispatchStage;
|
|
60
66
|
alias: AliasStage;
|
|
61
67
|
observation: CallObservation;
|
|
62
68
|
}>;
|
|
63
69
|
export type ForkInput = Readonly<{
|
|
64
|
-
path:
|
|
65
|
-
akuma:
|
|
70
|
+
path: WorldRoot;
|
|
71
|
+
akuma: string;
|
|
66
72
|
at: string;
|
|
67
73
|
settings?: Settings;
|
|
68
74
|
repo?: Repo;
|
|
69
75
|
}>;
|
|
70
76
|
export type ForkResult = Readonly<{
|
|
71
77
|
kind: "forked";
|
|
78
|
+
parent: AkuId;
|
|
72
79
|
child: AkuId;
|
|
73
80
|
dispatch: DispatchStage;
|
|
74
|
-
}> | Exclude<ForkReceipt, Readonly<{
|
|
81
|
+
}> | (Exclude<ForkReceipt, Readonly<{
|
|
75
82
|
kind: "forked";
|
|
76
83
|
child: AkuId;
|
|
77
|
-
}
|
|
84
|
+
}>> & Readonly<{
|
|
85
|
+
parent: AkuId;
|
|
86
|
+
}>);
|
|
78
87
|
export declare function callKeiyaku(input: CallInput): Promise<CallResult>;
|
|
79
88
|
export declare function forkKeiyaku(input: ForkInput): Promise<ForkResult>;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { realpath, stat } from "node:fs/promises";
|
|
2
2
|
import { moveAlias } from "../alias/index.js";
|
|
3
|
-
import { Akuma } from "../akuma/akuma.js";
|
|
4
|
-
import { parseAkuId } from "../akuma/identity.js";
|
|
3
|
+
import { Akuma, } from "../akuma/akuma.js";
|
|
5
4
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
6
5
|
import { publishDispatch, readDispatch, } from "../dispatch/index.js";
|
|
7
6
|
import { parseAkumaAlias } from "../identity/selector.js";
|
|
7
|
+
import { readManagedWorktreeAppointment } from "../workspace-place.js";
|
|
8
8
|
import { requireInput } from "./input.js";
|
|
9
|
-
import {
|
|
9
|
+
import { addressAkuma } from "./address.js";
|
|
10
|
+
import { KeiyakuRefused, seatForKeiyaku } from "./contract.js";
|
|
10
11
|
import { scopeForRepo } from "./repo.js";
|
|
11
12
|
function nonblank(value, label) {
|
|
12
13
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
@@ -68,9 +69,9 @@ async function observeCall(handle, mode, timeoutMs) {
|
|
|
68
69
|
return { kind: "failed", failure: integrationFailure(error) };
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
function dispatchStage(input) {
|
|
72
|
+
async function dispatchStage(input) {
|
|
72
73
|
try {
|
|
73
|
-
const published = publishDispatch(input);
|
|
74
|
+
const published = await publishDispatch(input);
|
|
74
75
|
return published.kind === "dispatched"
|
|
75
76
|
? { kind: "dispatched", dispatch: published.dispatch }
|
|
76
77
|
: { kind: "failed", failure: published.failure };
|
|
@@ -79,21 +80,78 @@ function dispatchStage(input) {
|
|
|
79
80
|
return { kind: "failed", failure: integrationFailure(error) };
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
function forkDispatchStage(input) {
|
|
83
|
+
async function forkDispatchStage(input) {
|
|
83
84
|
try {
|
|
84
|
-
const parent = readDispatch(input.repository, input.parent);
|
|
85
|
+
const parent = await readDispatch(input.repository, input.parent);
|
|
85
86
|
return parent === null
|
|
86
87
|
? { kind: "none" }
|
|
87
|
-
: dispatchStage({ repository: input.repository, akuId: input.child, contractId: parent.contractId });
|
|
88
|
+
: await dispatchStage({ repository: input.repository, akuId: input.child, contractId: parent.contractId });
|
|
88
89
|
}
|
|
89
90
|
catch (error) {
|
|
90
91
|
return { kind: "failed", failure: integrationFailure(error) };
|
|
91
92
|
}
|
|
92
93
|
}
|
|
94
|
+
async function canonicalizeExistingDirectory(path, diagnostic) {
|
|
95
|
+
try {
|
|
96
|
+
const real = await realpath(path);
|
|
97
|
+
if (!(await stat(real)).isDirectory())
|
|
98
|
+
throw new Error(diagnostic);
|
|
99
|
+
return real;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof Error && error.message === diagnostic)
|
|
103
|
+
throw error;
|
|
104
|
+
throw new Error(diagnostic);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function unavailableWorkspace(contractId, detail) {
|
|
108
|
+
return new Error(`Contract workspace is unavailable: ${contractId} ${detail}`);
|
|
109
|
+
}
|
|
110
|
+
async function currentManagedContract(contract, contractId) {
|
|
111
|
+
let state;
|
|
112
|
+
try {
|
|
113
|
+
state = await contract.state();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error instanceof Error && error.message === `contract does not exist: ${contractId}`) {
|
|
117
|
+
throw new KeiyakuRefused({ kind: "contract-missing", contractId });
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
if (state.terminal !== null)
|
|
122
|
+
throw new KeiyakuRefused({ kind: "terminal", contractId: state.id });
|
|
123
|
+
if (state.coordinates.workspace !== "worktree") {
|
|
124
|
+
throw unavailableWorkspace(state.id, "is here");
|
|
125
|
+
}
|
|
126
|
+
return state;
|
|
127
|
+
}
|
|
128
|
+
async function resolveCallExecution(input) {
|
|
129
|
+
if (input.cwd !== undefined) {
|
|
130
|
+
return {
|
|
131
|
+
cwd: await canonicalizeExistingDirectory(input.cwd, `cwd is not an existing directory: ${input.cwd}`),
|
|
132
|
+
source: "input",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (input.contract === undefined)
|
|
136
|
+
return { cwd: input.path, source: "world" };
|
|
137
|
+
const seat = seatForKeiyaku(input.contract);
|
|
138
|
+
const state = await currentManagedContract(input.contract, seat.id);
|
|
139
|
+
const appointment = await readManagedWorktreeAppointment(seat.scope, state.id);
|
|
140
|
+
if (appointment.kind === "unappointed") {
|
|
141
|
+
throw unavailableWorkspace(state.id, "is unappointed; use reconcile");
|
|
142
|
+
}
|
|
143
|
+
if (appointment.kind === "failed") {
|
|
144
|
+
throw new Error(`${appointment.diagnostic}; use reconcile`);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
cwd: await canonicalizeExistingDirectory(appointment.path, `Contract workspace is unavailable: ${appointment.path}`),
|
|
148
|
+
source: "contract-worktree",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
93
151
|
export async function callKeiyaku(input) {
|
|
94
152
|
const values = requireInput(input, "Keiyaku.call input");
|
|
95
153
|
onlyKeys(values, ["path", "archetype", "body", "cwd", "mode", "timeoutMs", "settings", "contract", "alias"], "Keiyaku.call input");
|
|
96
|
-
const path =
|
|
154
|
+
const path = nonblank(values.path, "path");
|
|
97
155
|
const archetype = nonblank(values.archetype, "archetype");
|
|
98
156
|
const body = text(values.body, "body");
|
|
99
157
|
const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
|
|
@@ -104,14 +162,20 @@ export async function callKeiyaku(input) {
|
|
|
104
162
|
? undefined
|
|
105
163
|
: parseAkumaAlias(nonblank(values.alias, "alias"));
|
|
106
164
|
const seat = values.contract === undefined ? undefined : seatForKeiyaku(values.contract);
|
|
107
|
-
const
|
|
165
|
+
const execution = await resolveCallExecution({
|
|
166
|
+
path,
|
|
167
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
168
|
+
...(seat === undefined ? {} : { contract: values.contract }),
|
|
169
|
+
});
|
|
170
|
+
const handle = await Akuma.of(path, settings).call({
|
|
108
171
|
archetype,
|
|
109
172
|
body,
|
|
110
|
-
|
|
173
|
+
cwd: execution.cwd,
|
|
111
174
|
});
|
|
175
|
+
const readonly = (await handle.status()).readonly;
|
|
112
176
|
const dispatch = seat === undefined
|
|
113
177
|
? { kind: "none" }
|
|
114
|
-
: dispatchStage({ repository: seat.scope, akuId: handle.id, contractId: seat.id });
|
|
178
|
+
: await dispatchStage({ repository: seat.scope, akuId: handle.id, contractId: seat.id });
|
|
115
179
|
let aliasStage = { kind: "none" };
|
|
116
180
|
if (alias !== undefined) {
|
|
117
181
|
if (dispatch.kind === "failed")
|
|
@@ -127,25 +191,35 @@ export async function callKeiyaku(input) {
|
|
|
127
191
|
}
|
|
128
192
|
}
|
|
129
193
|
const observation = await observeCall(handle, mode, timeoutMs);
|
|
130
|
-
return {
|
|
194
|
+
return {
|
|
195
|
+
kind: "called",
|
|
196
|
+
akuma: handle.id,
|
|
197
|
+
...(readonly === undefined ? {} : { readonly }),
|
|
198
|
+
execution,
|
|
199
|
+
dispatch,
|
|
200
|
+
alias: aliasStage,
|
|
201
|
+
observation,
|
|
202
|
+
};
|
|
131
203
|
}
|
|
132
204
|
export async function forkKeiyaku(input) {
|
|
133
205
|
const values = requireInput(input, "Keiyaku.fork input");
|
|
134
206
|
onlyKeys(values, ["path", "akuma", "at", "settings", "repo"], "Keiyaku.fork input");
|
|
135
|
-
const path =
|
|
136
|
-
if (typeof values.akuma !== "string")
|
|
137
|
-
throw new TypeError("akuma must be an AkuId");
|
|
138
|
-
const akuma = parseAkuId(values.akuma).id;
|
|
207
|
+
const path = nonblank(values.path, "path");
|
|
139
208
|
const at = nonblank(values.at, "at");
|
|
140
209
|
const settings = settingsOption(values.settings);
|
|
210
|
+
const akuma = (await addressAkuma({
|
|
211
|
+
path,
|
|
212
|
+
akuma: nonblank(values.akuma, "akuma"),
|
|
213
|
+
...(settings === undefined ? {} : { settings }),
|
|
214
|
+
})).id;
|
|
141
215
|
const repository = values.repo === undefined ? undefined : scopeForRepo(values.repo);
|
|
142
|
-
const receipt = await Akuma.
|
|
216
|
+
const receipt = await Akuma.of(path, settings)
|
|
143
217
|
.of({ id: akuma })
|
|
144
218
|
.fork({ at });
|
|
145
219
|
if (receipt.kind !== "forked")
|
|
146
|
-
return receipt;
|
|
220
|
+
return { ...receipt, parent: akuma };
|
|
147
221
|
const dispatch = repository === undefined
|
|
148
222
|
? { kind: "none" }
|
|
149
|
-
: forkDispatchStage({ repository, parent: akuma, child: receipt.child });
|
|
150
|
-
return { kind: "forked", child: receipt.child, dispatch };
|
|
223
|
+
: await forkDispatchStage({ repository, parent: akuma, child: receipt.child });
|
|
224
|
+
return { kind: "forked", parent: akuma, child: receipt.child, dispatch };
|
|
151
225
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ContractId } from "../core/facts/types.js";
|
|
2
|
+
import { type AuditReport, type RepositoryScope } from "../protocol/operations.js";
|
|
3
|
+
import { type WorktreeHooks } from "./configuration.js";
|
|
4
|
+
import { type MutationResult } from "./mutation.js";
|
|
5
|
+
export type AuditInput = Readonly<{
|
|
6
|
+
actor?: string;
|
|
7
|
+
hooks?: WorktreeHooks;
|
|
8
|
+
includeDirty?: boolean;
|
|
9
|
+
showDiff?: boolean;
|
|
10
|
+
requireBranchesToBeUpToDate?: boolean;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}>;
|
|
13
|
+
/** Normalize public audit input and project the protocol audit operation. */
|
|
14
|
+
export declare function auditContract(input: Readonly<{
|
|
15
|
+
scope: RepositoryScope;
|
|
16
|
+
contractId: ContractId;
|
|
17
|
+
input?: AuditInput;
|
|
18
|
+
}>): Promise<MutationResult<AuditReport>>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { decodeContractDocument } from "../body/decode.js";
|
|
2
|
+
import { withGitDecodeChannel } from "../git/read-observation.js";
|
|
3
|
+
import { auditOperation, } from "../protocol/operations.js";
|
|
4
|
+
import { worktreeHooksOption } from "./configuration.js";
|
|
5
|
+
import { actorOption, documentDerivation, optionalBoolean, optionalSignal, requireInput, } from "./input.js";
|
|
6
|
+
import { completeMutation } from "./mutation.js";
|
|
7
|
+
import { requireAccepted } from "./refusal.js";
|
|
8
|
+
function normalizeAuditInput(input) {
|
|
9
|
+
const values = input === undefined ? undefined : requireInput(input, "audit input");
|
|
10
|
+
const signal = optionalSignal(values?.signal);
|
|
11
|
+
return {
|
|
12
|
+
hooks: worktreeHooksOption(values?.hooks),
|
|
13
|
+
...actorOption(values?.actor),
|
|
14
|
+
includeDirty: optionalBoolean(values?.includeDirty, "includeDirty") ?? false,
|
|
15
|
+
showDiff: optionalBoolean(values?.showDiff, "showDiff") ?? false,
|
|
16
|
+
requireBranchesToBeUpToDate: optionalBoolean(values?.requireBranchesToBeUpToDate, "requireBranchesToBeUpToDate") ?? false,
|
|
17
|
+
...(signal === undefined ? {} : { signal }),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Normalize public audit input and project the protocol audit operation. */
|
|
21
|
+
export async function auditContract(input) {
|
|
22
|
+
const normalized = normalizeAuditInput(input.input);
|
|
23
|
+
return withGitDecodeChannel(input.scope, async (channel) => {
|
|
24
|
+
const accepted = requireAccepted(await auditOperation({
|
|
25
|
+
scope: input.scope,
|
|
26
|
+
channel,
|
|
27
|
+
contractId: input.contractId,
|
|
28
|
+
deriveDocument: (state) => documentDerivation(decodeContractDocument(state.terms.document.bytes), state.terms.gates, state.id),
|
|
29
|
+
includeDirty: normalized.includeDirty,
|
|
30
|
+
showDiff: normalized.showDiff,
|
|
31
|
+
requireBranchesToBeUpToDate: normalized.requireBranchesToBeUpToDate,
|
|
32
|
+
...(normalized.signal === undefined ? {} : { signal: normalized.signal }),
|
|
33
|
+
...(normalized.actor === undefined ? {} : { actor: normalized.actor }),
|
|
34
|
+
}));
|
|
35
|
+
return completeMutation({
|
|
36
|
+
scope: input.scope,
|
|
37
|
+
channel,
|
|
38
|
+
contractId: input.contractId,
|
|
39
|
+
accepted,
|
|
40
|
+
value: (report) => report,
|
|
41
|
+
hooks: normalized.hooks,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ActorId, type ContractId } from "../core/facts/types.js";
|
|
2
|
+
import type { GitDecodeChannel } from "../git/read-observation.js";
|
|
3
|
+
import { bindOperation, type IntentOutcome, type RepositoryScope } from "../protocol/operations.js";
|
|
4
|
+
import type { TaskId } from "../task/identity.js";
|
|
5
|
+
import type { VerificationDeclarationPreparation } from "../verification/declaration.js";
|
|
6
|
+
import type { KeiyakuRefusal } from "./refusal.js";
|
|
7
|
+
type BindAttemptInput = Readonly<{
|
|
8
|
+
scope: RepositoryScope;
|
|
9
|
+
channel: GitDecodeChannel;
|
|
10
|
+
title: string;
|
|
11
|
+
terms: Parameters<typeof bindOperation>[0]["terms"];
|
|
12
|
+
verification: VerificationDeclarationPreparation;
|
|
13
|
+
workspace: "worktree" | "here";
|
|
14
|
+
target?: string;
|
|
15
|
+
task?: TaskId;
|
|
16
|
+
actor?: ActorId;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function admitBindWithAppointment(input: BindAttemptInput): Promise<IntentOutcome<Readonly<{
|
|
19
|
+
contractId: ContractId;
|
|
20
|
+
}>, KeiyakuRefusal>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { releaseContractWorktree, reserveContractWorktree, withContractWorktreeAppointment, } from "../contract-worktree.js";
|
|
3
|
+
import { contractIdFromSegment } from "../core/facts/types.js";
|
|
4
|
+
import { fitIdentityStem, normalizeIdentityStem } from "../identity/normalize.js";
|
|
5
|
+
import { bindOperation } from "../protocol/operations.js";
|
|
6
|
+
import { claimTaskHolder } from "../settlement/holder.js";
|
|
7
|
+
function candidateId(title, collision) {
|
|
8
|
+
const stem = fitIdentityStem({ stem: normalizeIdentityStem({ source: title }) || "contract", maxBytes: 48 });
|
|
9
|
+
const suffix = collision === 0 ? "" : `-${randomBytes(8).toString("hex")}`;
|
|
10
|
+
return contractIdFromSegment(`${stem}${suffix}`);
|
|
11
|
+
}
|
|
12
|
+
async function attempt(input, id) {
|
|
13
|
+
return bindOperation({
|
|
14
|
+
scope: input.scope,
|
|
15
|
+
channel: input.channel,
|
|
16
|
+
title: input.title,
|
|
17
|
+
terms: input.terms,
|
|
18
|
+
verification: input.verification,
|
|
19
|
+
workspace: input.workspace,
|
|
20
|
+
contractId: id,
|
|
21
|
+
...(input.target === undefined ? {} : { target: input.target }),
|
|
22
|
+
...(input.task === undefined ? {} : {
|
|
23
|
+
decorateOffer: ({ contractId: owner }) => [claimTaskHolder(input.task, owner)],
|
|
24
|
+
}),
|
|
25
|
+
...(input.actor === undefined ? {} : { actor: input.actor }),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function attemptCandidates(input) {
|
|
29
|
+
for (let collision = 0; collision <= 3; collision += 1) {
|
|
30
|
+
const id = candidateId(input.title, collision);
|
|
31
|
+
let reserved = false;
|
|
32
|
+
if (input.workspace === "here") {
|
|
33
|
+
const reservation = await reserveContractWorktree(input.scope, id);
|
|
34
|
+
if (reservation.kind !== "reserved") {
|
|
35
|
+
return { kind: "refused", refusal: {
|
|
36
|
+
kind: "here-worktree-appointed",
|
|
37
|
+
path: reservation.path,
|
|
38
|
+
...(reservation.kind === "appointed" ? { contract: reservation.contract } : {}),
|
|
39
|
+
} };
|
|
40
|
+
}
|
|
41
|
+
reserved = true;
|
|
42
|
+
}
|
|
43
|
+
const result = await attempt(input, id);
|
|
44
|
+
if (result.kind === "accepted")
|
|
45
|
+
return result;
|
|
46
|
+
if (reserved)
|
|
47
|
+
await releaseContractWorktree(input.scope, id);
|
|
48
|
+
if (result.kind !== "refused" || result.refusal.kind !== "contract-exists")
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
throw new Error("contract identity collision attempts exhausted");
|
|
52
|
+
}
|
|
53
|
+
export async function admitBindWithAppointment(input) {
|
|
54
|
+
const action = () => attemptCandidates(input);
|
|
55
|
+
return input.workspace === "here" ? await withContractWorktreeAppointment(input.scope, action) : action();
|
|
56
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type AkumaList } from "../akuma/akuma.js";
|
|
2
|
+
import { type ArchetypeCatalogRow } from "../akuma/archetype.js";
|
|
3
|
+
import type { Settings } from "../settings.js";
|
|
4
|
+
import type { TaskRow } from "../task/index.js";
|
|
5
|
+
import { type ContractBoard } from "./contract.js";
|
|
6
|
+
import { Repo } from "./repo.js";
|
|
7
|
+
import type { WorldRoot } from "../world.js";
|
|
8
|
+
export type CatalogQuery = Readonly<{
|
|
9
|
+
kind: "tasks";
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: "contracts";
|
|
12
|
+
}> | Readonly<{
|
|
13
|
+
kind: "archetypes";
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
kind: "akuma";
|
|
16
|
+
archetype?: string;
|
|
17
|
+
}>;
|
|
18
|
+
export type CatalogInput = Readonly<{
|
|
19
|
+
query: Extract<CatalogQuery, {
|
|
20
|
+
kind: "tasks";
|
|
21
|
+
}>;
|
|
22
|
+
path: WorldRoot;
|
|
23
|
+
}> | Readonly<{
|
|
24
|
+
query: Extract<CatalogQuery, {
|
|
25
|
+
kind: "contracts";
|
|
26
|
+
}>;
|
|
27
|
+
repo: Repo;
|
|
28
|
+
}> | Readonly<{
|
|
29
|
+
query: Extract<CatalogQuery, {
|
|
30
|
+
kind: "archetypes";
|
|
31
|
+
}>;
|
|
32
|
+
settings: Settings;
|
|
33
|
+
}> | Readonly<{
|
|
34
|
+
query: Extract<CatalogQuery, {
|
|
35
|
+
kind: "akuma";
|
|
36
|
+
}>;
|
|
37
|
+
path: WorldRoot;
|
|
38
|
+
}>;
|
|
39
|
+
export type Catalog = Readonly<{
|
|
40
|
+
kind: "tasks";
|
|
41
|
+
root: WorldRoot;
|
|
42
|
+
rows: readonly TaskRow[];
|
|
43
|
+
}> | Readonly<{
|
|
44
|
+
kind: "contracts";
|
|
45
|
+
root: string;
|
|
46
|
+
state: string | null;
|
|
47
|
+
rows: ContractBoard["rows"];
|
|
48
|
+
}> | Readonly<{
|
|
49
|
+
kind: "archetypes";
|
|
50
|
+
rows: readonly ArchetypeCatalogRow[];
|
|
51
|
+
}> | Readonly<{
|
|
52
|
+
kind: "akuma";
|
|
53
|
+
root: WorldRoot;
|
|
54
|
+
archetype: string | null;
|
|
55
|
+
rows: AkumaList["rows"];
|
|
56
|
+
searched: readonly string[];
|
|
57
|
+
}>;
|
|
58
|
+
export declare function listCatalog(input: CatalogInput): Promise<Catalog>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Akuma } from "../akuma/akuma.js";
|
|
2
|
+
import { listArchetypeDefinitions } from "../akuma/archetype.js";
|
|
3
|
+
import { observeTaskCatalogRows } from "../task/operations.js";
|
|
4
|
+
import { listKeiyaku } from "./contract.js";
|
|
5
|
+
import { requireInput } from "./input.js";
|
|
6
|
+
import { Repo } from "./repo.js";
|
|
7
|
+
function settingsValue(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null
|
|
9
|
+
|| typeof value.namespace !== "function") {
|
|
10
|
+
throw new TypeError("settings must be a Settings");
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function worldRoot(value) {
|
|
15
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
16
|
+
throw new TypeError("path must be a WorldRoot");
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function queryValue(value) {
|
|
20
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
21
|
+
throw new TypeError("Keiyaku.ls query must be an object");
|
|
22
|
+
}
|
|
23
|
+
const query = value;
|
|
24
|
+
if (query.kind !== "tasks" && query.kind !== "contracts" && query.kind !== "archetypes" && query.kind !== "akuma") {
|
|
25
|
+
throw new TypeError("Keiyaku.ls query kind is invalid");
|
|
26
|
+
}
|
|
27
|
+
for (const key of Object.keys(query)) {
|
|
28
|
+
if (key !== "kind" && !(query.kind === "akuma" && key === "archetype")) {
|
|
29
|
+
throw new TypeError(`Keiyaku.ls query has unknown field: ${key}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (query.kind !== "akuma")
|
|
33
|
+
return { kind: query.kind };
|
|
34
|
+
if (query.archetype !== undefined && typeof query.archetype !== "string") {
|
|
35
|
+
throw new TypeError("Keiyaku.ls Akuma name must be a string");
|
|
36
|
+
}
|
|
37
|
+
return { kind: "akuma", ...(query.archetype === undefined ? {} : { archetype: query.archetype }) };
|
|
38
|
+
}
|
|
39
|
+
export async function listCatalog(input) {
|
|
40
|
+
const values = requireInput(input, "Keiyaku.ls input");
|
|
41
|
+
const query = queryValue(values.query);
|
|
42
|
+
const allowed = query.kind === "contracts" ? ["query", "repo"]
|
|
43
|
+
: query.kind === "archetypes" ? ["query", "settings"] : ["query", "path"];
|
|
44
|
+
for (const key of Object.keys(values)) {
|
|
45
|
+
if (!allowed.includes(key))
|
|
46
|
+
throw new TypeError(`Keiyaku.ls input has unknown field: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
if (query.kind === "contracts") {
|
|
49
|
+
if (!(values.repo instanceof Repo))
|
|
50
|
+
throw new TypeError("repo must be a Repo");
|
|
51
|
+
const board = await listKeiyaku({ repo: values.repo });
|
|
52
|
+
return { kind: "contracts", root: board.root, state: board.state, rows: board.rows };
|
|
53
|
+
}
|
|
54
|
+
if (query.kind === "archetypes") {
|
|
55
|
+
return { kind: "archetypes", rows: await listArchetypeDefinitions({ settings: settingsValue(values.settings) }) };
|
|
56
|
+
}
|
|
57
|
+
const path = worldRoot(values.path);
|
|
58
|
+
if (query.kind === "tasks") {
|
|
59
|
+
return { kind: "tasks", root: path, rows: await observeTaskCatalogRows(path) };
|
|
60
|
+
}
|
|
61
|
+
const listed = await Akuma.of(path).list(query.archetype === undefined ? undefined : { archetype: query.archetype });
|
|
62
|
+
return {
|
|
63
|
+
kind: "akuma",
|
|
64
|
+
root: path,
|
|
65
|
+
archetype: query.archetype ?? null,
|
|
66
|
+
rows: listed.rows,
|
|
67
|
+
searched: listed.searched,
|
|
68
|
+
};
|
|
69
|
+
}
|