@astrosheep/keiyaku 2.8.4 → 2.9.0
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 +35 -15
- package/build/.tsbuildinfo +1 -1
- package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
- package/build/agents/harness/control-types.js +1 -0
- package/build/agents/harness/execution-handle.js +23 -3
- package/build/agents/harness/index.js +6 -607
- package/build/agents/harness/outcome.js +141 -0
- package/build/agents/harness/projection.js +310 -0
- package/build/agents/harness/router.js +201 -0
- package/build/agents/harness/runtime.js +2 -1
- package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
- package/build/agents/launch-snapshot/resolve.js +72 -0
- package/build/agents/opencode-sdk.js +24 -3
- package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
- package/build/agents/providers/claude-agent-sdk/events.js +218 -0
- package/build/agents/providers/claude-agent-sdk/session.js +158 -0
- package/build/agents/providers/codex-app-server/adapter.js +347 -0
- package/build/agents/providers/codex-app-server/events.js +352 -0
- package/build/agents/providers/codex-app-server/session.js +266 -0
- package/build/agents/providers/codex-sdk.js +23 -5
- package/build/agents/providers/opencode-sdk/adapter.js +344 -0
- package/build/agents/providers/opencode-sdk/events.js +354 -0
- package/build/agents/providers/opencode-sdk/session.js +180 -0
- package/build/agents/providers/pi/adapter.js +269 -0
- package/build/agents/providers/pi/checkpoint.js +41 -0
- package/build/agents/providers/pi/events.js +282 -0
- package/build/agents/{selector_v2.js → selector.js} +8 -6
- package/build/cli/commands/akuma/akuma/handler.js +4 -0
- package/build/cli/commands/akuma/akuma/meta.js +16 -0
- package/build/cli/commands/akuma/catalog.js +8 -0
- package/build/cli/commands/akuma/list/handler.js +15 -0
- package/build/cli/commands/akuma/list/meta-ls.js +9 -0
- package/build/cli/commands/akuma/view/handler.js +34 -0
- package/build/cli/commands/akuma/view/meta.js +8 -0
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +10 -0
- package/build/cli/commands/contract/amend/meta.js +12 -0
- package/build/cli/commands/contract/arc/handler.js +9 -0
- package/build/cli/commands/contract/arc/meta.js +26 -0
- package/build/cli/commands/contract/bind/handler.js +23 -0
- package/build/cli/commands/contract/bind/meta.js +30 -0
- package/build/cli/commands/contract/catalog.js +16 -0
- package/build/cli/commands/contract/forfeit/handler.js +13 -0
- package/build/cli/commands/contract/forfeit/meta.js +12 -0
- package/build/cli/commands/contract/log/handler.js +12 -0
- package/build/cli/commands/contract/log/meta.js +12 -0
- package/build/cli/commands/contract/petition/handler.js +26 -0
- package/build/cli/commands/contract/petition/meta.js +23 -0
- package/build/cli/commands/contract/renew/handler.js +10 -0
- package/build/cli/commands/contract/renew/meta.js +17 -0
- package/build/cli/commands/projection/call/handler.js +39 -0
- package/build/cli/commands/projection/call/meta.js +9 -0
- package/build/cli/commands/projection/catalog.js +14 -0
- package/build/cli/commands/projection/kill/handler.js +35 -0
- package/build/cli/commands/projection/kill/meta.js +11 -0
- package/build/cli/commands/projection/revive/handler.js +53 -0
- package/build/cli/commands/projection/revive/meta.js +9 -0
- package/build/cli/commands/projection/status/handler.js +23 -0
- package/build/cli/commands/projection/status/meta.js +9 -0
- package/build/cli/commands/projection/tell/handler.js +34 -0
- package/build/cli/commands/projection/tell/meta.js +9 -0
- package/build/cli/commands/projection/wait/handler.js +21 -0
- package/build/cli/commands/projection/wait/meta.js +11 -0
- package/build/cli/commands/registry.js +100 -0
- package/build/cli/commands/shared.js +71 -0
- package/build/cli/commands/skills/catalog.js +6 -0
- package/build/cli/commands/skills/install/handler.js +10 -0
- package/build/cli/commands/skills/install/meta.js +9 -0
- package/build/cli/commands/skills/skills/handler.js +4 -0
- package/build/cli/commands/skills/skills/meta.js +10 -0
- package/build/cli/commands/system/catalog.js +8 -0
- package/build/cli/commands/system/completion/handler.js +10 -0
- package/build/cli/commands/system/completion/meta.js +9 -0
- package/build/cli/commands/system/dump-env/handler.js +3 -0
- package/build/cli/commands/system/dump-env/meta.js +10 -0
- package/build/cli/commands/system/guide/handler.js +4 -0
- package/build/cli/commands/system/guide/meta.js +9 -0
- package/build/cli/commands/task/add/handler.js +21 -0
- package/build/cli/commands/task/add/meta.js +8 -0
- package/build/cli/commands/task/catalog.js +22 -0
- package/build/cli/commands/task/doctor/handler.js +4 -0
- package/build/cli/commands/task/doctor/meta.js +8 -0
- package/build/cli/commands/task/done/handler.js +17 -0
- package/build/cli/commands/task/done/meta.js +8 -0
- package/build/cli/commands/task/drop/handler.js +6 -0
- package/build/cli/commands/task/drop/meta.js +8 -0
- package/build/cli/commands/task/ls/handler.js +13 -0
- package/build/cli/commands/task/ls/meta.js +8 -0
- package/build/cli/commands/task/shared.js +131 -0
- package/build/cli/commands/task/start/handler.js +6 -0
- package/build/cli/commands/task/start/meta.js +8 -0
- package/build/cli/commands/task/stop/handler.js +17 -0
- package/build/cli/commands/task/stop/meta.js +8 -0
- package/build/cli/commands/task/task/handler.js +5 -0
- package/build/cli/commands/task/task/meta.js +11 -0
- package/build/cli/commands/task/update/handler.js +18 -0
- package/build/cli/commands/task/update/meta.js +8 -0
- package/build/cli/commands/task/view/handler.js +14 -0
- package/build/cli/commands/task/view/meta.js +8 -0
- package/build/cli/completion.js +26 -27
- package/build/cli/flags.js +79 -2
- package/build/cli/help.js +27 -49
- package/build/cli/index.js +121 -374
- package/build/cli/output-stream-error-policy.js +17 -0
- package/build/cli/parse.js +159 -72
- package/build/cli/projection-address.js +5 -0
- package/build/cli/render/arc.js +1 -1
- package/build/cli/render/errors.js +4 -3
- package/build/cli/render/format.js +14 -0
- package/build/cli/render/kanshi.js +106 -0
- package/build/cli/render/petition.js +24 -1
- package/build/cli/render/projection-activity.js +5 -1
- package/build/cli/render/shared.js +2 -2
- package/build/cli/render/status.js +79 -13
- package/build/cli/render/task.js +113 -0
- package/build/cli/render/wait.js +23 -3
- package/build/cli/types.js +1 -1
- package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
- package/build/config/env.js +9 -4
- package/build/config/settings/disease.js +26 -0
- package/build/config/settings/knobs.js +8 -0
- package/build/config/settings/loader.js +244 -0
- package/build/config/settings/schema.js +276 -0
- package/build/core/addressing.js +1 -1
- package/build/core/amend.js +3 -3
- package/build/core/arc.js +5 -4
- package/build/core/bind-workspace.js +123 -0
- package/build/core/bind.js +75 -155
- package/build/core/call/call.js +224 -0
- package/build/core/call/context.js +238 -0
- package/build/core/call/execution.js +217 -0
- package/build/core/call/prompt.js +42 -0
- package/build/core/call/reference.js +24 -0
- package/build/core/call-persist.js +1 -1
- package/build/core/claim-delivery.js +247 -0
- package/build/core/claim.js +39 -277
- package/build/core/context.js +2 -1
- package/build/core/{render.js → contract-view.js} +12 -140
- package/build/core/draft.js +107 -9
- package/build/core/entry.js +9 -1
- package/build/core/execution-coordinate.js +4 -20
- package/build/core/execution-pact.js +16 -31
- package/build/core/forfeit.js +35 -22
- package/build/core/hints.js +2 -2
- package/build/core/log.js +2 -2
- package/build/core/petition-claim-gates.js +8 -6
- package/build/core/petition-preview.js +97 -0
- package/build/core/petition-run.js +3 -2
- package/build/core/petition.js +39 -26
- package/build/core/projection/generation/database.js +306 -0
- package/build/core/projection/generation/identity.js +84 -0
- package/build/core/projection/generation/model.js +201 -0
- package/build/core/projection/generation/store.js +73 -0
- package/build/core/projection/generation/transitions.js +258 -0
- package/build/core/projection/heart.js +0 -307
- package/build/core/projection/tell/database.js +109 -0
- package/build/core/projection/tell/model.js +97 -0
- package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
- package/build/core/projection-activity.js +23 -2
- package/build/core/projection-core.js +2 -1
- package/build/core/projection-execution-observer.js +259 -0
- package/build/core/projection-generation-continuation.js +1 -1
- package/build/core/projection-generation-execution.js +90 -20
- package/build/core/projection-generation-launcher.js +1 -1
- package/build/core/projection-generation-runner.js +198 -4
- package/build/core/projection-identity.js +1 -1
- package/build/core/projection-kill.js +50 -17
- package/build/core/projection-life-observer.js +1 -1
- package/build/core/projection-life-protocol.js +10 -0
- package/build/core/projection-mint.js +1 -1
- package/build/core/projection-status.js +45 -32
- package/build/core/projection-wait.js +12 -2
- package/build/core/projection-wake.js +5 -2
- package/build/core/{queue_v2.js → queue.js} +2 -1
- package/build/core/registry.js +1 -1
- package/build/core/renew.js +7 -7
- package/build/core/response-artifact-catalog-sqlite.js +112 -0
- package/build/core/response-artifact-catalog.js +7 -0
- package/build/core/seal.js +44 -16
- package/build/core/status/board.js +286 -0
- package/build/core/status/drift.js +99 -0
- package/build/core/status/lifecycle.js +84 -0
- package/build/core/status/reconciliation.js +131 -0
- package/build/core/stored-agent-event.js +28 -4
- package/build/core/target-ref.js +10 -0
- package/build/core/task/board.js +175 -0
- package/build/core/task/commands.js +211 -0
- package/build/core/task/document.js +189 -0
- package/build/core/task/model.js +1 -0
- package/build/core/task/settlement-git.js +207 -0
- package/build/core/task/settlement-policy.js +82 -0
- package/build/core/task/source-board.js +84 -0
- package/build/core/task/source-model.js +1 -0
- package/build/core/task/validation.js +44 -0
- package/build/core/task-contract.js +224 -0
- package/build/core/task-doctor.js +14 -0
- package/build/core/task-git-actor.js +38 -0
- package/build/core/task-git-runtime.js +34 -0
- package/build/core/task-git-store.js +375 -0
- package/build/core/task-repository.js +1 -0
- package/build/core/task-store-repository.js +86 -0
- package/build/core/task-store.js +1 -0
- package/build/core/task-worktree.js +51 -0
- package/build/core/task.js +4 -0
- package/build/core/transcripts.js +138 -56
- package/build/core/validation.js +17 -0
- package/build/core/verdict.js +2 -2
- package/build/core/worktree-path.js +24 -6
- package/build/flow-error.js +3 -2
- package/build/generated/version.js +1 -1
- package/build/git/commits.js +3 -0
- package/build/git/diff/parsers.js +0 -94
- package/build/index.js +2 -0
- package/build/keiyaku-constants.js +2 -0
- package/build/keiyaku.js +4 -3
- package/package.json +3 -2
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +11 -6
- package/build/agents/harness/pump.js +0 -158
- package/build/agents/providers/codex-app-server.js +0 -864
- package/build/agents/providers/opencode-sdk.js +0 -363
- package/build/agents/providers/pi.js +0 -557
- package/build/config/settings.js +0 -533
- package/build/core/call.js +0 -686
- package/build/core/projection/generation.js +0 -412
- package/build/core/projection/identity.js +0 -81
- package/build/core/projection/mint.js +0 -174
- package/build/core/projection-generation-store.js +0 -707
- package/build/core/status.js +0 -539
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
export class ProjectionGenerationError extends Error {
|
|
2
|
-
code = "PROJECTION_GENERATION_ERROR";
|
|
3
|
-
constructor(message, options) {
|
|
4
|
-
super(message, options);
|
|
5
|
-
this.name = "ProjectionGenerationError";
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export const GENERATION_VERDICTS = [
|
|
9
|
-
"completed",
|
|
10
|
-
"failed",
|
|
11
|
-
"launch-failed",
|
|
12
|
-
"lost-runner",
|
|
13
|
-
"dismissed",
|
|
14
|
-
];
|
|
15
|
-
const VERDICTS = new Set(GENERATION_VERDICTS);
|
|
16
|
-
function isPlainObject(value) {
|
|
17
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
18
|
-
return false;
|
|
19
|
-
const prototype = Object.getPrototypeOf(value);
|
|
20
|
-
return prototype === Object.prototype || prototype === null;
|
|
21
|
-
}
|
|
22
|
-
function assertJsonValue(value, location, ancestors) {
|
|
23
|
-
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
24
|
-
return;
|
|
25
|
-
if (typeof value === "number") {
|
|
26
|
-
if (!Number.isFinite(value)) {
|
|
27
|
-
throw new ProjectionGenerationError(`${location} contains a non-finite number`);
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (typeof value !== "object") {
|
|
32
|
-
throw new ProjectionGenerationError(`${location} contains a non-JSON value`);
|
|
33
|
-
}
|
|
34
|
-
if (ancestors.has(value)) {
|
|
35
|
-
throw new ProjectionGenerationError(`${location} contains a cycle`);
|
|
36
|
-
}
|
|
37
|
-
ancestors.add(value);
|
|
38
|
-
try {
|
|
39
|
-
if (Array.isArray(value)) {
|
|
40
|
-
value.forEach((item, index) => assertJsonValue(item, `${location}[${index}]`, ancestors));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (!isPlainObject(value)) {
|
|
44
|
-
throw new ProjectionGenerationError(`${location} must contain only plain JSON objects`);
|
|
45
|
-
}
|
|
46
|
-
for (const [key, item] of Object.entries(value)) {
|
|
47
|
-
assertJsonValue(item, `${location}.${key}`, ancestors);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
ancestors.delete(value);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function cloneFacts(value, location) {
|
|
55
|
-
if (!isPlainObject(value)) {
|
|
56
|
-
throw new ProjectionGenerationError(`${location} must be a JSON object`);
|
|
57
|
-
}
|
|
58
|
-
assertJsonValue(value, location, new Set());
|
|
59
|
-
return JSON.parse(JSON.stringify(value));
|
|
60
|
-
}
|
|
61
|
-
function assertExecutionId(executionId) {
|
|
62
|
-
if (typeof executionId !== "string" || executionId.trim() === "") {
|
|
63
|
-
throw new ProjectionGenerationError("executionId must be a non-blank string");
|
|
64
|
-
}
|
|
65
|
-
return executionId;
|
|
66
|
-
}
|
|
67
|
-
function assertNonNegativeSafeInteger(value, location) {
|
|
68
|
-
if (!Number.isSafeInteger(value) || value < 0) {
|
|
69
|
-
throw new ProjectionGenerationError(`${location} must be a non-negative safe integer`);
|
|
70
|
-
}
|
|
71
|
-
return value;
|
|
72
|
-
}
|
|
73
|
-
function assertPositiveSafeInteger(value, location) {
|
|
74
|
-
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
75
|
-
throw new ProjectionGenerationError(`${location} must be a positive safe integer`);
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
function assertIsoTimestamp(value, location) {
|
|
80
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
81
|
-
throw new ProjectionGenerationError(`${location} must be an ISO-8601 timestamp`);
|
|
82
|
-
}
|
|
83
|
-
const milliseconds = Date.parse(value);
|
|
84
|
-
if (!Number.isFinite(milliseconds) || new Date(Date.parse(value)).toISOString() !== value) {
|
|
85
|
-
throw new ProjectionGenerationError(`${location} must be a canonical ISO-8601 timestamp`);
|
|
86
|
-
}
|
|
87
|
-
return value;
|
|
88
|
-
}
|
|
89
|
-
function validateTellFence(value, location) {
|
|
90
|
-
if (!Array.isArray(value)) {
|
|
91
|
-
throw new ProjectionGenerationError(`${location} must be an array`);
|
|
92
|
-
}
|
|
93
|
-
const seen = new Set();
|
|
94
|
-
return value.map((tellId, index) => {
|
|
95
|
-
if (typeof tellId !== "string" || tellId.trim() === "") {
|
|
96
|
-
throw new ProjectionGenerationError(`${location}[${index}] must be a non-blank string`);
|
|
97
|
-
}
|
|
98
|
-
if (seen.has(tellId)) {
|
|
99
|
-
throw new ProjectionGenerationError(`${location} contains duplicate tell '${tellId}'`);
|
|
100
|
-
}
|
|
101
|
-
seen.add(tellId);
|
|
102
|
-
return tellId;
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function validateLaunchFacts(value, location) {
|
|
106
|
-
const facts = cloneFacts(value, location);
|
|
107
|
-
facts.createdAt = assertIsoTimestamp(facts.createdAt, `${location}.createdAt`);
|
|
108
|
-
if (facts.tellFence !== undefined) {
|
|
109
|
-
facts.tellFence = validateTellFence(facts.tellFence, `${location}.tellFence`);
|
|
110
|
-
}
|
|
111
|
-
if ("state" in facts) {
|
|
112
|
-
throw new ProjectionGenerationError(`${location}.state is reserved for verdict records`);
|
|
113
|
-
}
|
|
114
|
-
return facts;
|
|
115
|
-
}
|
|
116
|
-
function validateAdoptionFacts(value, location) {
|
|
117
|
-
const facts = cloneFacts(value, location);
|
|
118
|
-
if ("state" in facts) {
|
|
119
|
-
throw new ProjectionGenerationError(`${location}.state is reserved for verdict records`);
|
|
120
|
-
}
|
|
121
|
-
return facts;
|
|
122
|
-
}
|
|
123
|
-
function validateVerdictFacts(value, location) {
|
|
124
|
-
const facts = cloneFacts(value, location);
|
|
125
|
-
if (typeof facts.state !== "string" || !VERDICTS.has(facts.state)) {
|
|
126
|
-
throw new ProjectionGenerationError(`${location}.state is not in the closed verdict vocabulary`);
|
|
127
|
-
}
|
|
128
|
-
return facts;
|
|
129
|
-
}
|
|
130
|
-
function parseStoredRow(row) {
|
|
131
|
-
const seq = typeof row.seq === "bigint" ? Number(row.seq) : row.seq;
|
|
132
|
-
if (!Number.isSafeInteger(seq) || seq <= 0) {
|
|
133
|
-
throw new ProjectionGenerationError(`generation row has invalid seq '${String(row.seq)}'`);
|
|
134
|
-
}
|
|
135
|
-
if (row.kind !== "launch" && row.kind !== "adoption" && row.kind !== "verdict") {
|
|
136
|
-
throw new ProjectionGenerationError(`generation row ${seq} has unknown kind '${row.kind}'`);
|
|
137
|
-
}
|
|
138
|
-
const executionId = assertExecutionId(row.execution_id);
|
|
139
|
-
let decoded;
|
|
140
|
-
try {
|
|
141
|
-
decoded = JSON.parse(row.payload_json);
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
throw new ProjectionGenerationError(`generation row ${seq} contains malformed facts JSON`, {
|
|
145
|
-
cause: error,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
const location = `generation row ${seq} facts`;
|
|
149
|
-
if (row.kind === "launch") {
|
|
150
|
-
return {
|
|
151
|
-
seq,
|
|
152
|
-
kind: "launch",
|
|
153
|
-
executionId,
|
|
154
|
-
facts: validateLaunchFacts(decoded, location),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
if (row.kind === "adoption") {
|
|
158
|
-
return {
|
|
159
|
-
seq,
|
|
160
|
-
kind: "adoption",
|
|
161
|
-
executionId,
|
|
162
|
-
facts: validateAdoptionFacts(decoded, location),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
seq,
|
|
167
|
-
kind: "verdict",
|
|
168
|
-
executionId,
|
|
169
|
-
facts: validateVerdictFacts(decoded, location),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
function validateHistory(records) {
|
|
173
|
-
let current = null;
|
|
174
|
-
const launchIds = new Set();
|
|
175
|
-
for (let index = 0; index < records.length; index += 1) {
|
|
176
|
-
const record = records[index];
|
|
177
|
-
const expectedSeq = index + 1;
|
|
178
|
-
if (record.seq !== expectedSeq) {
|
|
179
|
-
throw new ProjectionGenerationError(`generation history is not dense: expected seq ${expectedSeq}, found ${record.seq}`);
|
|
180
|
-
}
|
|
181
|
-
if (record.kind === "launch") {
|
|
182
|
-
if (current && !current.verdict) {
|
|
183
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} launches before execution '${current.launch.executionId}' is terminal`);
|
|
184
|
-
}
|
|
185
|
-
if (launchIds.has(record.executionId)) {
|
|
186
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} reuses executionId '${record.executionId}'`);
|
|
187
|
-
}
|
|
188
|
-
launchIds.add(record.executionId);
|
|
189
|
-
current = { launch: record };
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (!current) {
|
|
193
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} appears before any launch`);
|
|
194
|
-
}
|
|
195
|
-
if (record.executionId !== current.launch.executionId) {
|
|
196
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} does not join the current launch`);
|
|
197
|
-
}
|
|
198
|
-
if (record.kind === "adoption") {
|
|
199
|
-
if (current.adoption) {
|
|
200
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} duplicates adoption`);
|
|
201
|
-
}
|
|
202
|
-
if (current.verdict) {
|
|
203
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} adopts a terminal execution`);
|
|
204
|
-
}
|
|
205
|
-
current.adoption = record;
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (current.verdict) {
|
|
209
|
-
throw new ProjectionGenerationError(`generation row ${record.seq} duplicates verdict`);
|
|
210
|
-
}
|
|
211
|
-
current.verdict = record;
|
|
212
|
-
}
|
|
213
|
-
return current;
|
|
214
|
-
}
|
|
215
|
-
function readValidatedHistory(database) {
|
|
216
|
-
const rows = database.prepare(`
|
|
217
|
-
SELECT seq, kind, execution_id, payload_json
|
|
218
|
-
FROM generation_records
|
|
219
|
-
ORDER BY seq
|
|
220
|
-
`).all();
|
|
221
|
-
const records = rows.map(parseStoredRow);
|
|
222
|
-
return { records, current: validateHistory(records) };
|
|
223
|
-
}
|
|
224
|
-
function appendRecord(database, kind, executionId, facts, seq) {
|
|
225
|
-
const encoded = JSON.stringify(facts);
|
|
226
|
-
database.prepare(`
|
|
227
|
-
INSERT INTO generation_records (seq, kind, execution_id, payload_json)
|
|
228
|
-
VALUES (?, ?, ?, ?)
|
|
229
|
-
`).run(seq, kind, executionId, encoded);
|
|
230
|
-
return parseStoredRow({
|
|
231
|
-
seq,
|
|
232
|
-
kind,
|
|
233
|
-
execution_id: executionId,
|
|
234
|
-
payload_json: encoded,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
function committed(records) {
|
|
238
|
-
return { status: "committed", records };
|
|
239
|
-
}
|
|
240
|
-
function rejected(reason, current) {
|
|
241
|
-
return { status: "rejected", reason, current };
|
|
242
|
-
}
|
|
243
|
-
/** Read every validated generation record in dense seq order. */
|
|
244
|
-
export function readGenerationRecords(heart) {
|
|
245
|
-
return heart.read((database) => readValidatedHistory(database).records);
|
|
246
|
-
}
|
|
247
|
-
/** Fold the validated history into the current open or terminal generation. */
|
|
248
|
-
export function readCurrentGeneration(heart) {
|
|
249
|
-
return heart.read((database) => readValidatedHistory(database).current);
|
|
250
|
-
}
|
|
251
|
-
export function launchIfSettled(heart, input) {
|
|
252
|
-
const executionId = assertExecutionId(input.executionId);
|
|
253
|
-
const facts = validateLaunchFacts(input.facts, "launch facts");
|
|
254
|
-
return heart.write((database) => {
|
|
255
|
-
const history = readValidatedHistory(database);
|
|
256
|
-
if (history.records.some((record) => record.kind === "launch" && record.executionId === executionId)) {
|
|
257
|
-
return rejected("execution-id-already-used", history.current);
|
|
258
|
-
}
|
|
259
|
-
if (history.current && !history.current.verdict) {
|
|
260
|
-
return rejected("current-generation-open", history.current);
|
|
261
|
-
}
|
|
262
|
-
return committed([
|
|
263
|
-
appendRecord(database, "launch", executionId, facts, history.records.length + 1),
|
|
264
|
-
]);
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
export function adoptionIfCurrent(heart, input) {
|
|
268
|
-
const executionId = assertExecutionId(input.executionId);
|
|
269
|
-
const facts = validateAdoptionFacts(input.facts, "adoption facts");
|
|
270
|
-
return heart.write((database) => {
|
|
271
|
-
const history = readValidatedHistory(database);
|
|
272
|
-
const current = history.current;
|
|
273
|
-
if (!current)
|
|
274
|
-
return rejected("no-current-launch", null);
|
|
275
|
-
if (current.launch.executionId !== executionId)
|
|
276
|
-
return rejected("not-current", current);
|
|
277
|
-
if (current.verdict)
|
|
278
|
-
return rejected("already-terminal", current);
|
|
279
|
-
if (current.adoption)
|
|
280
|
-
return rejected("already-adopted", current);
|
|
281
|
-
return committed([
|
|
282
|
-
appendRecord(database, "adoption", executionId, facts, history.records.length + 1),
|
|
283
|
-
]);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
export function verdictIfOpen(heart, input) {
|
|
287
|
-
const executionId = assertExecutionId(input.executionId);
|
|
288
|
-
if (!VERDICTS.has(input.verdict)) {
|
|
289
|
-
throw new ProjectionGenerationError(`unknown verdict '${String(input.verdict)}'`);
|
|
290
|
-
}
|
|
291
|
-
if (input.facts !== undefined) {
|
|
292
|
-
const supplied = cloneFacts(input.facts, "verdict facts");
|
|
293
|
-
if ("state" in supplied) {
|
|
294
|
-
throw new ProjectionGenerationError("verdict facts.state is reserved");
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
const facts = validateVerdictFacts({ ...(input.facts ?? {}), state: input.verdict }, "verdict facts");
|
|
298
|
-
return heart.write((database) => {
|
|
299
|
-
const history = readValidatedHistory(database);
|
|
300
|
-
const current = history.current;
|
|
301
|
-
if (!current)
|
|
302
|
-
return rejected("no-current-launch", null);
|
|
303
|
-
if (current.launch.executionId !== executionId)
|
|
304
|
-
return rejected("not-current", current);
|
|
305
|
-
if (current.verdict)
|
|
306
|
-
return rejected("already-terminal", current);
|
|
307
|
-
if (input.verdict === "launch-failed" && current.adoption) {
|
|
308
|
-
return rejected("already-adopted", current);
|
|
309
|
-
}
|
|
310
|
-
return committed([
|
|
311
|
-
appendRecord(database, "verdict", executionId, facts, history.records.length + 1),
|
|
312
|
-
]);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
export function supersedeStartupTimeout(heart, input) {
|
|
316
|
-
const executionId = assertExecutionId(input.executionId);
|
|
317
|
-
const successorExecutionId = assertExecutionId(input.successorExecutionId);
|
|
318
|
-
const observedAtMs = assertNonNegativeSafeInteger(input.observedAtMs, "observedAtMs");
|
|
319
|
-
const startupTimeoutMs = assertPositiveSafeInteger(input.startupTimeoutMs, "startupTimeoutMs");
|
|
320
|
-
const diagnostic = cloneFacts(input.diagnostic, "startup-timeout diagnostic");
|
|
321
|
-
const successorFacts = validateLaunchFacts(input.successorFacts, "successor launch facts");
|
|
322
|
-
return heart.write((database) => {
|
|
323
|
-
const history = readValidatedHistory(database);
|
|
324
|
-
const current = history.current;
|
|
325
|
-
if (!current)
|
|
326
|
-
return rejected("no-current-launch", null);
|
|
327
|
-
if (current.launch.executionId !== executionId)
|
|
328
|
-
return rejected("not-current", current);
|
|
329
|
-
if (current.verdict)
|
|
330
|
-
return rejected("already-terminal", current);
|
|
331
|
-
if (current.adoption)
|
|
332
|
-
return rejected("already-adopted", current);
|
|
333
|
-
if (history.records.some((record) => record.kind === "launch" && record.executionId === successorExecutionId)) {
|
|
334
|
-
return rejected("execution-id-already-used", current);
|
|
335
|
-
}
|
|
336
|
-
const launchedAtMs = Date.parse(current.launch.facts.createdAt);
|
|
337
|
-
if (observedAtMs - launchedAtMs < startupTimeoutMs) {
|
|
338
|
-
return rejected("startup-timeout-not-elapsed", current);
|
|
339
|
-
}
|
|
340
|
-
const verdictFacts = validateVerdictFacts({ ...diagnostic, state: "launch-failed" }, "startup-timeout verdict facts");
|
|
341
|
-
const firstSeq = history.records.length + 1;
|
|
342
|
-
return committed([
|
|
343
|
-
appendRecord(database, "verdict", executionId, verdictFacts, firstSeq),
|
|
344
|
-
appendRecord(database, "launch", successorExecutionId, successorFacts, firstSeq + 1),
|
|
345
|
-
]);
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
export function replaceDeadRunner(heart, input) {
|
|
349
|
-
const executionId = assertExecutionId(input.executionId);
|
|
350
|
-
const successorExecutionId = assertExecutionId(input.successorExecutionId);
|
|
351
|
-
const releasedLockEvidence = cloneFacts(input.releasedLockEvidence, "released runner-lock evidence");
|
|
352
|
-
if (Object.keys(releasedLockEvidence).length === 0) {
|
|
353
|
-
throw new ProjectionGenerationError("released runner-lock evidence must not be empty");
|
|
354
|
-
}
|
|
355
|
-
const successorFacts = validateLaunchFacts(input.successorFacts, "dead-runner successor launch facts");
|
|
356
|
-
return heart.write((database) => {
|
|
357
|
-
const history = readValidatedHistory(database);
|
|
358
|
-
const current = history.current;
|
|
359
|
-
if (!current)
|
|
360
|
-
return rejected("no-current-launch", null);
|
|
361
|
-
if (current.launch.executionId !== executionId)
|
|
362
|
-
return rejected("not-current", current);
|
|
363
|
-
if (current.verdict)
|
|
364
|
-
return rejected("already-terminal", current);
|
|
365
|
-
if (!current.adoption)
|
|
366
|
-
return rejected("not-adopted", current);
|
|
367
|
-
if (history.records.some((record) => record.kind === "launch" && record.executionId === successorExecutionId)) {
|
|
368
|
-
return rejected("execution-id-already-used", current);
|
|
369
|
-
}
|
|
370
|
-
const verdictFacts = validateVerdictFacts({ state: "lost-runner", releasedLockEvidence }, "dead-runner verdict facts");
|
|
371
|
-
const firstSeq = history.records.length + 1;
|
|
372
|
-
return committed([
|
|
373
|
-
appendRecord(database, "verdict", executionId, verdictFacts, firstSeq),
|
|
374
|
-
appendRecord(database, "launch", successorExecutionId, successorFacts, firstSeq + 1),
|
|
375
|
-
]);
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
export function handoff(heart, input) {
|
|
379
|
-
const executionId = assertExecutionId(input.executionId);
|
|
380
|
-
const successorExecutionId = assertExecutionId(input.successorExecutionId);
|
|
381
|
-
const completionFacts = cloneFacts(input.completionFacts, "handoff completion facts");
|
|
382
|
-
if ("state" in completionFacts) {
|
|
383
|
-
throw new ProjectionGenerationError("handoff completion facts.state is reserved");
|
|
384
|
-
}
|
|
385
|
-
const successorFactsBase = validateLaunchFacts(input.successorFacts, "handoff successor launch facts");
|
|
386
|
-
if (successorFactsBase.tellFence !== undefined) {
|
|
387
|
-
throw new ProjectionGenerationError("handoff successor launch facts.tellFence must be supplied only through handoff.tellFence");
|
|
388
|
-
}
|
|
389
|
-
const tellFence = Object.freeze(validateTellFence([...input.tellFence], "handoff tellFence"));
|
|
390
|
-
const successorFacts = validateLaunchFacts({ ...successorFactsBase, tellFence: [...tellFence] }, "handoff successor launch facts");
|
|
391
|
-
return heart.write((database) => {
|
|
392
|
-
const history = readValidatedHistory(database);
|
|
393
|
-
const current = history.current;
|
|
394
|
-
if (!current)
|
|
395
|
-
return rejected("no-current-launch", null);
|
|
396
|
-
if (current.launch.executionId !== executionId)
|
|
397
|
-
return rejected("not-current", current);
|
|
398
|
-
if (current.verdict)
|
|
399
|
-
return rejected("already-terminal", current);
|
|
400
|
-
if (!current.adoption)
|
|
401
|
-
return rejected("not-adopted", current);
|
|
402
|
-
if (history.records.some((record) => record.kind === "launch" && record.executionId === successorExecutionId)) {
|
|
403
|
-
return rejected("execution-id-already-used", current);
|
|
404
|
-
}
|
|
405
|
-
const verdictFacts = validateVerdictFacts({ ...completionFacts, state: "completed" }, "handoff verdict facts");
|
|
406
|
-
const firstSeq = history.records.length + 1;
|
|
407
|
-
return committed([
|
|
408
|
-
appendRecord(database, "verdict", executionId, verdictFacts, firstSeq),
|
|
409
|
-
appendRecord(database, "launch", successorExecutionId, successorFacts, firstSeq + 1),
|
|
410
|
-
]);
|
|
411
|
-
});
|
|
412
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { parseExecutionPact, } from "../execution-pact.js";
|
|
2
|
-
import { ProjectionHeartError } from "./heart.js";
|
|
3
|
-
/**
|
|
4
|
-
* Insert the single immutable identity row.
|
|
5
|
-
* There is no product UPDATE path; a second insert fails.
|
|
6
|
-
*/
|
|
7
|
-
export function insertProjectionIdentity(heart, identity) {
|
|
8
|
-
const location = `${heart.databasePath}#projection_identity`;
|
|
9
|
-
const validated = parseExecutionPact(identity, location);
|
|
10
|
-
heart.write((database) => {
|
|
11
|
-
const existing = database.prepare("SELECT COUNT(*) AS count FROM projection_identity").get();
|
|
12
|
-
if (Number(existing?.count ?? 0) !== 0) {
|
|
13
|
-
throw new ProjectionHeartError("projection identity is already initialized");
|
|
14
|
-
}
|
|
15
|
-
database.prepare(`
|
|
16
|
-
INSERT INTO projection_identity (
|
|
17
|
-
singleton,
|
|
18
|
-
version,
|
|
19
|
-
pid,
|
|
20
|
-
akuma,
|
|
21
|
-
provider,
|
|
22
|
-
authority_json,
|
|
23
|
-
binding_json,
|
|
24
|
-
minted_at,
|
|
25
|
-
workspace_root,
|
|
26
|
-
effective_policy_json
|
|
27
|
-
) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
28
|
-
`).run(validated.version, validated.pid, validated.akuma, validated.provider, JSON.stringify(validated.authority), JSON.stringify(validated.binding), validated.mintedAt, validated.workspaceRoot, validated.effectivePolicy === undefined
|
|
29
|
-
? null
|
|
30
|
-
: JSON.stringify(validated.effectivePolicy));
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/** Read the single immutable identity row through the existing pact parser. */
|
|
34
|
-
export function readProjectionIdentity(heart) {
|
|
35
|
-
const location = `${heart.databasePath}#projection_identity`;
|
|
36
|
-
return heart.read((database) => {
|
|
37
|
-
const rows = database.prepare(`
|
|
38
|
-
SELECT
|
|
39
|
-
version,
|
|
40
|
-
pid,
|
|
41
|
-
akuma,
|
|
42
|
-
provider,
|
|
43
|
-
authority_json,
|
|
44
|
-
binding_json,
|
|
45
|
-
minted_at,
|
|
46
|
-
workspace_root,
|
|
47
|
-
effective_policy_json
|
|
48
|
-
FROM projection_identity
|
|
49
|
-
`).all();
|
|
50
|
-
if (rows.length !== 1) {
|
|
51
|
-
throw new ProjectionHeartError(`projection identity row count must be 1, found ${rows.length}`);
|
|
52
|
-
}
|
|
53
|
-
const row = rows[0];
|
|
54
|
-
let authority;
|
|
55
|
-
let binding;
|
|
56
|
-
let effectivePolicy;
|
|
57
|
-
try {
|
|
58
|
-
authority = JSON.parse(row.authority_json);
|
|
59
|
-
binding = JSON.parse(row.binding_json);
|
|
60
|
-
effectivePolicy = row.effective_policy_json === null
|
|
61
|
-
? undefined
|
|
62
|
-
: JSON.parse(row.effective_policy_json);
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
throw new ProjectionHeartError("projection identity contains malformed JSON", {
|
|
66
|
-
cause: error,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return parseExecutionPact({
|
|
70
|
-
version: Number(row.version),
|
|
71
|
-
pid: Number(row.pid),
|
|
72
|
-
akuma: row.akuma,
|
|
73
|
-
provider: row.provider,
|
|
74
|
-
authority,
|
|
75
|
-
binding,
|
|
76
|
-
mintedAt: row.minted_at,
|
|
77
|
-
workspaceRoot: row.workspace_root,
|
|
78
|
-
...(effectivePolicy === undefined ? {} : { effectivePolicy }),
|
|
79
|
-
}, location);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { isErrnoException } from "../../errno.js";
|
|
4
|
-
import { ProjectionStateError, randomHex8 } from "../atomic-publish.js";
|
|
5
|
-
import { isoFromNowMs, PROJECTION_ROOT_SEGMENTS, projectionDir, projectionRoot, } from "../projection-core.js";
|
|
6
|
-
import { parseExecutionAuthoritySelection, parseExecutionPact, parseExecutionPactFields, } from "../execution-pact.js";
|
|
7
|
-
import { ProjectionHeart } from "./heart.js";
|
|
8
|
-
import { insertProjectionIdentity } from "./identity.js";
|
|
9
|
-
import { initializeProjectionLeash } from "./leash.js";
|
|
10
|
-
export class ProjectionMintCollisionError extends Error {
|
|
11
|
-
code = "PROJECTION_MINT_COLLISION";
|
|
12
|
-
constructor(message, options) {
|
|
13
|
-
super(message, options);
|
|
14
|
-
this.name = "ProjectionMintCollisionError";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
const DEFAULT_MINT_ATTEMPTS = 16;
|
|
18
|
-
function assertMintSlug(slug) {
|
|
19
|
-
const normalized = slug.trim();
|
|
20
|
-
if (!normalized) {
|
|
21
|
-
throw new ProjectionStateError("projection slug cannot be empty");
|
|
22
|
-
}
|
|
23
|
-
if (!/^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/.test(normalized)) {
|
|
24
|
-
throw new ProjectionStateError(`invalid projection slug: ${slug}`);
|
|
25
|
-
}
|
|
26
|
-
return normalized;
|
|
27
|
-
}
|
|
28
|
-
function removeEmptyAkumaRoot(dir) {
|
|
29
|
-
try {
|
|
30
|
-
fs.rmdirSync(dir);
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
if (isErrnoException(error) && ["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error.code ?? ""))
|
|
34
|
-
return;
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Validate mint inputs that can fail, then reserve one private staging directory.
|
|
40
|
-
* Dot-prefixed staging is never a published projection address.
|
|
41
|
-
*/
|
|
42
|
-
function reserveProjectionDirectory(input) {
|
|
43
|
-
const slug = assertMintSlug(input.slug);
|
|
44
|
-
if (!Number.isInteger(input.pid) || input.pid < 0) {
|
|
45
|
-
throw new ProjectionStateError("projection pid must be a non-negative integer");
|
|
46
|
-
}
|
|
47
|
-
if (!Number.isSafeInteger(input.nowMs) || input.nowMs < 0) {
|
|
48
|
-
throw new ProjectionStateError("nowMs must be a safe non-negative integer");
|
|
49
|
-
}
|
|
50
|
-
const maxAttempts = input.maxAttempts ?? DEFAULT_MINT_ATTEMPTS;
|
|
51
|
-
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
|
|
52
|
-
throw new ProjectionStateError("maxAttempts must be a positive integer");
|
|
53
|
-
}
|
|
54
|
-
const root = projectionRoot(input.cwd);
|
|
55
|
-
fs.mkdirSync(root, { recursive: true });
|
|
56
|
-
const akumaRoot = path.join(root, slug);
|
|
57
|
-
const createdAkumaRoot = !fs.existsSync(akumaRoot);
|
|
58
|
-
fs.mkdirSync(akumaRoot, { recursive: true });
|
|
59
|
-
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
60
|
-
const hex = (input.randomHex?.(attempt) ?? randomHex8()).toLowerCase();
|
|
61
|
-
if (!/^[0-9a-f]{8}$/.test(hex)) {
|
|
62
|
-
if (createdAkumaRoot)
|
|
63
|
-
removeEmptyAkumaRoot(akumaRoot);
|
|
64
|
-
throw new ProjectionStateError(`projection id random segment must be 8 hex chars, got ${hex}`);
|
|
65
|
-
}
|
|
66
|
-
const id = `${slug}/${hex}`;
|
|
67
|
-
const dir = projectionDir(input.cwd, id);
|
|
68
|
-
const stagingDir = path.join(akumaRoot, `.mint-${hex}`);
|
|
69
|
-
if (fs.existsSync(dir))
|
|
70
|
-
continue;
|
|
71
|
-
try {
|
|
72
|
-
fs.mkdirSync(stagingDir);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
if (isErrnoException(error) && error.code === "EEXIST") {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (createdAkumaRoot)
|
|
79
|
-
removeEmptyAkumaRoot(akumaRoot);
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
if (fs.existsSync(dir)) {
|
|
83
|
-
fs.rmdirSync(stagingDir);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
return { id, dir, stagingDir };
|
|
87
|
-
}
|
|
88
|
-
if (createdAkumaRoot)
|
|
89
|
-
removeEmptyAkumaRoot(akumaRoot);
|
|
90
|
-
throw new ProjectionMintCollisionError(`exhausted ${maxAttempts} projection id attempts under ${PROJECTION_ROOT_SEGMENTS.join("/")} (PROJECTION_MINT_COLLISION)`);
|
|
91
|
-
}
|
|
92
|
-
/** Build the identity fields shared by both authority variants. */
|
|
93
|
-
function buildMintPactFields(input) {
|
|
94
|
-
return {
|
|
95
|
-
version: 1,
|
|
96
|
-
pid: input.pid,
|
|
97
|
-
akuma: input.akuma,
|
|
98
|
-
provider: input.provider,
|
|
99
|
-
binding: input.binding,
|
|
100
|
-
mintedAt: isoFromNowMs(input.nowMs),
|
|
101
|
-
workspaceRoot: input.workspaceRoot ?? path.resolve(input.cwd),
|
|
102
|
-
...(input.effectivePolicy ? { effectivePolicy: input.effectivePolicy } : {}),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Build both hard projection organs in private staging, close every database
|
|
107
|
-
* handle, then publish with one directory rename. Rename is the sole publication
|
|
108
|
-
* boundary; no fallible work runs after it.
|
|
109
|
-
*/
|
|
110
|
-
function publishReservedProjection(pact, reserved) {
|
|
111
|
-
let published = false;
|
|
112
|
-
try {
|
|
113
|
-
const heart = ProjectionHeart.create(reserved.stagingDir);
|
|
114
|
-
try {
|
|
115
|
-
insertProjectionIdentity(heart, pact);
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
heart.close();
|
|
119
|
-
}
|
|
120
|
-
initializeProjectionLeash(reserved.stagingDir);
|
|
121
|
-
fs.renameSync(reserved.stagingDir, reserved.dir);
|
|
122
|
-
published = true;
|
|
123
|
-
return { id: reserved.id, dir: reserved.dir, pact };
|
|
124
|
-
}
|
|
125
|
-
finally {
|
|
126
|
-
if (!published) {
|
|
127
|
-
// Failure removes only this mint's staging.
|
|
128
|
-
fs.rmSync(reserved.stagingDir, { recursive: true, force: true });
|
|
129
|
-
removeEmptyAkumaRoot(path.dirname(reserved.stagingDir));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Mint a user-authority projection directory.
|
|
135
|
-
* Order: validate → reserve staging → heart + identity + leash → close → atomic rename.
|
|
136
|
-
*/
|
|
137
|
-
export function mintProjection(input) {
|
|
138
|
-
const authority = parseExecutionAuthoritySelection(input.authority, "projection identity", "user");
|
|
139
|
-
const fields = parseExecutionPactFields(buildMintPactFields(input));
|
|
140
|
-
const pact = parseExecutionPact({
|
|
141
|
-
version: fields.version,
|
|
142
|
-
pid: fields.pid,
|
|
143
|
-
akuma: fields.akuma,
|
|
144
|
-
provider: fields.provider,
|
|
145
|
-
authority,
|
|
146
|
-
binding: fields.binding,
|
|
147
|
-
mintedAt: fields.mintedAt,
|
|
148
|
-
workspaceRoot: fields.workspaceRoot,
|
|
149
|
-
...(fields.effectivePolicy !== undefined ? { effectivePolicy: fields.effectivePolicy } : {}),
|
|
150
|
-
});
|
|
151
|
-
const reserved = reserveProjectionDirectory(input);
|
|
152
|
-
return publishReservedProjection(pact, reserved);
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Mint a repository-authority projection.
|
|
156
|
-
* Order: validate complete identity → reserve staging → heart + identity + leash → close → atomic rename.
|
|
157
|
-
*/
|
|
158
|
-
export async function mintRepositoryProjection(input) {
|
|
159
|
-
parseExecutionAuthoritySelection(input.authority, "projection identity", "repository");
|
|
160
|
-
const fields = parseExecutionPactFields(buildMintPactFields(input));
|
|
161
|
-
const pact = parseExecutionPact({
|
|
162
|
-
version: fields.version,
|
|
163
|
-
pid: fields.pid,
|
|
164
|
-
akuma: fields.akuma,
|
|
165
|
-
provider: fields.provider,
|
|
166
|
-
authority: { kind: "repository" },
|
|
167
|
-
binding: fields.binding,
|
|
168
|
-
mintedAt: fields.mintedAt,
|
|
169
|
-
workspaceRoot: fields.workspaceRoot,
|
|
170
|
-
...(fields.effectivePolicy !== undefined ? { effectivePolicy: fields.effectivePolicy } : {}),
|
|
171
|
-
});
|
|
172
|
-
const reserved = reserveProjectionDirectory(input);
|
|
173
|
-
return publishReservedProjection(pact, reserved);
|
|
174
|
-
}
|