@astrosheep/keiyaku 4.4.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +4 -2
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +9 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +16 -5
- package/build/integrations/pi/keiyaku.ts +159 -0
- package/build/src/akuma/abort.js +9 -9
- package/build/src/akuma/akuma-handle.d.ts +4 -2
- package/build/src/akuma/akuma-handle.js +14 -2
- package/build/src/akuma/akuma-product.d.ts +32 -3
- package/build/src/akuma/akuma-product.js +44 -20
- package/build/src/akuma/akuma.d.ts +0 -1
- package/build/src/akuma/body.d.ts +1 -7
- package/build/src/akuma/body.js +35 -13
- package/build/src/akuma/heart/index.js +7 -3
- package/build/src/akuma/heart/rows.d.ts +1 -1
- package/build/src/akuma/heart/rows.js +3 -3
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +28 -6
- package/build/src/akuma/identity.d.ts +1 -0
- package/build/src/akuma/identity.js +7 -0
- package/build/src/akuma/index.d.ts +1 -2
- package/build/src/akuma/projection.d.ts +18 -2
- package/build/src/akuma/projection.js +11 -1
- package/build/src/akuma/providers/claude/index.js +68 -37
- package/build/src/akuma/publication.d.ts +9 -3
- package/build/src/akuma/publication.js +20 -3
- package/build/src/akuma/request-lifecycle.d.ts +45 -0
- package/build/src/akuma/request-lifecycle.js +171 -0
- package/build/src/akuma/request-serve.d.ts +9 -32
- package/build/src/akuma/request-serve.js +48 -178
- package/build/src/akuma/request-wire.js +1 -1
- package/build/src/akuma/requests.js +3 -2
- package/build/src/cli/commands/akuma-invoke.d.ts +11 -2
- package/build/src/cli/commands/akuma-invoke.js +29 -7
- package/build/src/cli/commands/akuma.d.ts +1 -0
- package/build/src/cli/commands/akuma.js +16 -6
- package/build/src/cli/commands/contract.d.ts +3 -2
- package/build/src/cli/commands/contract.js +5 -2
- package/build/src/cli/commands/status-set.d.ts +4 -0
- package/build/src/cli/commands/status-set.js +61 -0
- package/build/src/cli/invoke.js +6 -3
- package/build/src/cli/parse.js +1 -1
- package/build/src/cli/render/akuma-activity.js +11 -0
- package/build/src/cli/render/akuma.js +7 -0
- package/build/src/cli/render/status-set.d.ts +5 -0
- package/build/src/cli/render/status-set.js +14 -0
- package/build/src/cli/render/text.js +3 -0
- package/build/src/cli/result.d.ts +15 -1
- package/build/src/cli/runtime.js +2 -0
- package/build/src/cli/square-edge.d.ts +6 -0
- package/build/src/cli/square-edge.js +74 -0
- package/build/src/contract-worktree.js +18 -5
- package/build/src/library/akuma-creation.d.ts +14 -3
- package/build/src/library/akuma-creation.js +31 -18
- package/build/src/library/fleet.d.ts +12 -1
- package/build/src/library/fleet.js +21 -5
- package/build/src/library/repo.js +3 -1
- package/build/src/runtime/proc/process-exit.js +6 -1
- package/build/src/runtime/proc/run.js +6 -10
- package/build/src/runtime/proc/termination.d.ts +1 -0
- package/build/src/runtime/proc/termination.js +22 -2
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/runtime/proc/windows-run.js +10 -7
- package/build/src/world.d.ts +2 -0
- package/build/src/world.js +4 -0
- package/package.json +8 -3
- package/build/src/akuma/result-route.d.ts +0 -2
- package/build/src/akuma/result-route.js +0 -1
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { type KillEvidence, type RequestFact
|
|
2
|
-
import { type AkuId
|
|
1
|
+
import { type KillEvidence, type RequestFact } from "./heart/index.js";
|
|
2
|
+
import { type AkuId } from "./identity.js";
|
|
3
3
|
import { type StructuralRequestClaim } from "./request-wire.js";
|
|
4
4
|
import { type TaskMutationRequest } from "../task/mutation.js";
|
|
5
|
-
import type
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
seed: Omit<Soul, "createdAt">;
|
|
9
|
-
initialBody: string;
|
|
10
|
-
}>;
|
|
5
|
+
import { BodyRequestPump as LifecycleBodyRequestPump, type RequestChildLaunch as LifecycleRequestChildLaunch, type PumpInput as LifecyclePumpInput } from "./request-lifecycle.js";
|
|
6
|
+
export { clearBodyRequestTransport, settleBodyRequests } from "./request-lifecycle.js";
|
|
7
|
+
export type RequestChildLaunch = LifecycleRequestChildLaunch;
|
|
11
8
|
type UpstreamCall = Readonly<{
|
|
12
9
|
signal: AbortSignal;
|
|
13
10
|
}>;
|
|
@@ -60,17 +57,12 @@ export type UpstreamExecutionPort = Readonly<{
|
|
|
60
57
|
request: TaskMutationRequest;
|
|
61
58
|
}>): Promise<unknown>;
|
|
62
59
|
}>;
|
|
63
|
-
type PumpInput = Readonly<{
|
|
64
|
-
paths: AkumaPaths;
|
|
65
|
-
parent: Soul;
|
|
66
|
-
bodySequence: number;
|
|
67
|
-
now(): string;
|
|
68
|
-
spawn(launch: RequestChildLaunch): Promise<OwnedProcess | void>;
|
|
60
|
+
type PumpInput = Omit<LifecyclePumpInput, "upstream"> & Readonly<{
|
|
69
61
|
upstream?: UpstreamExecutionPort;
|
|
70
|
-
signal: AbortSignal;
|
|
71
62
|
}>;
|
|
72
63
|
export type ServeInput = Omit<PumpInput, "bodySequence"> & Readonly<{
|
|
73
64
|
directory: string;
|
|
65
|
+
transportId: string;
|
|
74
66
|
claim: StructuralRequestClaim;
|
|
75
67
|
admissionOpen(): boolean;
|
|
76
68
|
}>;
|
|
@@ -79,22 +71,7 @@ export type UpstreamFact = Exclude<Extract<RequestFact, {
|
|
|
79
71
|
}>, Extract<RequestFact, {
|
|
80
72
|
action: "akuma.call";
|
|
81
73
|
}>>;
|
|
82
|
-
export declare
|
|
83
|
-
|
|
84
|
-
readonly directory: string;
|
|
85
|
-
readonly failure: Promise<never>;
|
|
86
|
-
private readonly closeSignal;
|
|
87
|
-
private readonly executionSignal;
|
|
88
|
-
private readonly handled;
|
|
89
|
-
private readonly running;
|
|
90
|
-
private readonly cancelBody;
|
|
91
|
-
private admissionClosed;
|
|
92
|
-
private constructor();
|
|
74
|
+
export declare function serveRequest(input: ServeInput): Promise<void>;
|
|
75
|
+
export declare class BodyRequestPump extends LifecycleBodyRequestPump {
|
|
93
76
|
static open(input: PumpInput): Promise<BodyRequestPump>;
|
|
94
|
-
private run;
|
|
95
|
-
close(): Promise<void>;
|
|
96
|
-
stopAdmission(): void;
|
|
97
77
|
}
|
|
98
|
-
export declare function clearBodyRequestTransport(paths: AkumaPaths): Promise<void>;
|
|
99
|
-
export declare function settleBodyRequests(paths: AkumaPaths, parent: Soul, now: () => string, signal?: AbortSignal): Promise<"settled" | "pending">;
|
|
100
|
-
export {};
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { pathsForAkuId, worldRootForAkumaPaths } from "./identity.js";
|
|
6
|
-
import { BIRTH_TIMEOUT_MS, publishAkuma } from "./publication.js";
|
|
7
|
-
import { atomicJson, canonicalAkuId, decodeClaim, decodeRecipe, receiptPath, } from "./request-wire.js";
|
|
1
|
+
import { admitRequest, readRequest, refuseRequest, reserveRequest, serveRequest as serveChildRequest, serveUpstreamRequest, voidRequest, } from "./heart/index.js";
|
|
2
|
+
import { worldRootForAkumaPaths } from "./identity.js";
|
|
3
|
+
import { publishAkuma } from "./publication.js";
|
|
4
|
+
import { atomicJson, canonicalAkuId, decodeRecipe, receiptPath, } from "./request-wire.js";
|
|
8
5
|
import { isTaskMutationAction } from "../task/mutation.js";
|
|
9
6
|
import { executeRequest } from "./request-execution.js";
|
|
10
|
-
|
|
7
|
+
import { BodyRequestPump as LifecycleBodyRequestPump, } from "./request-lifecycle.js";
|
|
8
|
+
export { clearBodyRequestTransport, settleBodyRequests } from "./request-lifecycle.js";
|
|
11
9
|
function diagnostic(error) {
|
|
12
10
|
return error instanceof Error ? error.message : String(error);
|
|
13
11
|
}
|
|
14
12
|
function isTask(value) {
|
|
15
13
|
return isTaskMutationAction(value.action);
|
|
16
14
|
}
|
|
15
|
+
function refusalRecipe(value) {
|
|
16
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
17
|
+
const recipe = value;
|
|
18
|
+
if (Array.isArray(recipe.allowed))
|
|
19
|
+
return recipe;
|
|
20
|
+
return { ...recipe, allowed: [] };
|
|
21
|
+
}
|
|
22
|
+
return { allowed: [] };
|
|
23
|
+
}
|
|
17
24
|
function acceptedReceipt(fact, service) {
|
|
18
25
|
return {
|
|
19
26
|
id: fact.id,
|
|
@@ -62,7 +69,7 @@ function receiptFor(fact) {
|
|
|
62
69
|
}
|
|
63
70
|
return null;
|
|
64
71
|
}
|
|
65
|
-
async function projectReceipt(directory, fact, outcome) {
|
|
72
|
+
async function projectReceipt(directory, transportId, fact, outcome) {
|
|
66
73
|
const receipt = outcome === undefined
|
|
67
74
|
? receiptFor(fact)
|
|
68
75
|
: fact.action === "akuma.call"
|
|
@@ -71,29 +78,45 @@ async function projectReceipt(directory, fact, outcome) {
|
|
|
71
78
|
if (receipt === null)
|
|
72
79
|
return;
|
|
73
80
|
try {
|
|
74
|
-
await atomicJson(receiptPath(directory,
|
|
81
|
+
await atomicJson(receiptPath(directory, transportId), receipt);
|
|
75
82
|
}
|
|
76
83
|
catch (error) {
|
|
77
84
|
if (error.code !== "ENOENT")
|
|
78
85
|
throw error;
|
|
79
86
|
}
|
|
80
87
|
}
|
|
88
|
+
async function refuseInvalidCall(input) {
|
|
89
|
+
let fact = await admitRequest(input.paths, {
|
|
90
|
+
...input.claim,
|
|
91
|
+
recipe: refusalRecipe(input.claim.recipe),
|
|
92
|
+
admittedAt: input.now(),
|
|
93
|
+
});
|
|
94
|
+
const existing = receiptFor(fact);
|
|
95
|
+
if (existing !== null) {
|
|
96
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
fact = await refuseRequest(input.paths, input.claim.id, "invalid akuma.call recipe");
|
|
100
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
101
|
+
}
|
|
81
102
|
async function serveCall(input) {
|
|
82
103
|
input.signal.throwIfAborted();
|
|
83
104
|
if (!input.admissionOpen())
|
|
84
105
|
return;
|
|
85
106
|
const recipe = decodeRecipe(input.claim.recipe);
|
|
86
|
-
if (recipe === null)
|
|
107
|
+
if (recipe === null) {
|
|
108
|
+
await refuseInvalidCall(input);
|
|
87
109
|
return;
|
|
110
|
+
}
|
|
88
111
|
let fact = await admitRequest(input.paths, { ...input.claim, recipe, admittedAt: input.now() });
|
|
89
112
|
const existing = receiptFor(fact);
|
|
90
113
|
if (existing !== null) {
|
|
91
|
-
await projectReceipt(input.directory, fact);
|
|
114
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
92
115
|
return;
|
|
93
116
|
}
|
|
94
117
|
if (!input.admissionOpen()) {
|
|
95
118
|
fact = await voidRequest(input.paths, input.claim.id, "body closed request admission");
|
|
96
|
-
await projectReceipt(input.directory, fact);
|
|
119
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
97
120
|
return;
|
|
98
121
|
}
|
|
99
122
|
if (fact.action !== "akuma.call" || fact.state !== "admitted") {
|
|
@@ -103,7 +126,7 @@ async function serveCall(input) {
|
|
|
103
126
|
const world = worldRootForAkumaPaths(input.paths);
|
|
104
127
|
if (request.world !== world) {
|
|
105
128
|
fact = await refuseRequest(input.paths, request.id, `request world ${request.world} does not match ${world}`);
|
|
106
|
-
await projectReceipt(input.directory, fact);
|
|
129
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
107
130
|
return;
|
|
108
131
|
}
|
|
109
132
|
try {
|
|
@@ -111,14 +134,10 @@ async function serveCall(input) {
|
|
|
111
134
|
worldPath: world,
|
|
112
135
|
archetype: request.archetype,
|
|
113
136
|
signal: input.signal,
|
|
114
|
-
reserve: async (allocated) => {
|
|
115
|
-
if (!input.admissionOpen())
|
|
116
|
-
throw new Error("body closed request admission");
|
|
117
|
-
fact = await reserveRequest(input.paths, request.id, allocated.id);
|
|
118
|
-
},
|
|
119
137
|
launch: async (allocated) => {
|
|
120
138
|
if (!input.admissionOpen())
|
|
121
139
|
throw new Error("body closed request admission");
|
|
140
|
+
fact = await reserveRequest(input.paths, request.id, allocated.id);
|
|
122
141
|
return await input.spawn({
|
|
123
142
|
paths: allocated.paths,
|
|
124
143
|
seed: {
|
|
@@ -140,7 +159,7 @@ async function serveCall(input) {
|
|
|
140
159
|
});
|
|
141
160
|
},
|
|
142
161
|
});
|
|
143
|
-
fact = await
|
|
162
|
+
fact = await serveChildRequest(input.paths, request.id, published.id);
|
|
144
163
|
}
|
|
145
164
|
catch (error) {
|
|
146
165
|
const current = await readRequest(input.paths, request.id);
|
|
@@ -151,7 +170,7 @@ async function serveCall(input) {
|
|
|
151
170
|
? await voidRequest(input.paths, request.id, diagnostic(error))
|
|
152
171
|
: current;
|
|
153
172
|
}
|
|
154
|
-
await projectReceipt(input.directory, fact);
|
|
173
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
155
174
|
}
|
|
156
175
|
function failure(error) {
|
|
157
176
|
const value = error !== null && typeof error === "object" ? error : null;
|
|
@@ -178,11 +197,11 @@ async function serveUpstream(input) {
|
|
|
178
197
|
});
|
|
179
198
|
const existing = receiptFor(fact);
|
|
180
199
|
if (existing !== null) {
|
|
181
|
-
await projectReceipt(input.directory, fact);
|
|
200
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
182
201
|
return;
|
|
183
202
|
}
|
|
184
203
|
if (fact.state === "served") {
|
|
185
|
-
await projectReceipt(input.directory, fact, {
|
|
204
|
+
await projectReceipt(input.directory, input.transportId, fact, {
|
|
186
205
|
kind: "failed",
|
|
187
206
|
failure: {
|
|
188
207
|
kind: "failed",
|
|
@@ -193,7 +212,7 @@ async function serveUpstream(input) {
|
|
|
193
212
|
}
|
|
194
213
|
if (!input.admissionOpen()) {
|
|
195
214
|
fact = await voidRequest(input.paths, input.claim.id, "body closed request admission");
|
|
196
|
-
await projectReceipt(input.directory, fact);
|
|
215
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
197
216
|
return;
|
|
198
217
|
}
|
|
199
218
|
if (fact.state !== "admitted" || fact.action === "akuma.call") {
|
|
@@ -211,171 +230,22 @@ async function serveUpstream(input) {
|
|
|
211
230
|
catch (error) {
|
|
212
231
|
outcome = { kind: "failed", failure: failure(error) };
|
|
213
232
|
fact = await voidRequest(input.paths, request.id, "body failed before serving the request");
|
|
214
|
-
await projectReceipt(input.directory, fact);
|
|
233
|
+
await projectReceipt(input.directory, input.transportId, fact);
|
|
215
234
|
return;
|
|
216
235
|
}
|
|
217
236
|
fact =
|
|
218
237
|
service === undefined
|
|
219
238
|
? await voidRequest(input.paths, request.id, "body completed without a durable Contract fact reference")
|
|
220
239
|
: await serveUpstreamRequest(input.paths, request.id, service);
|
|
221
|
-
await projectReceipt(input.directory, fact, outcome);
|
|
240
|
+
await projectReceipt(input.directory, input.transportId, fact, outcome);
|
|
222
241
|
}
|
|
223
|
-
async function
|
|
242
|
+
export async function serveRequest(input) {
|
|
224
243
|
return input.claim.action === "akuma.call"
|
|
225
244
|
? await serveCall({ ...input, claim: input.claim })
|
|
226
245
|
: await serveUpstream({ ...input, claim: input.claim });
|
|
227
246
|
}
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
return (await readdir(directory)).filter((name) => name.endsWith(".request.json")).sort();
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
if (error.code === "ENOENT")
|
|
234
|
-
return [];
|
|
235
|
-
throw error;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
export class BodyRequestPump {
|
|
239
|
-
input;
|
|
240
|
-
directory;
|
|
241
|
-
failure;
|
|
242
|
-
closeSignal = new AbortController();
|
|
243
|
-
executionSignal = new AbortController();
|
|
244
|
-
handled = new Set();
|
|
245
|
-
running;
|
|
246
|
-
cancelBody;
|
|
247
|
-
admissionClosed = false;
|
|
248
|
-
constructor(input) {
|
|
249
|
-
this.input = input;
|
|
250
|
-
this.directory = join(input.paths.directory, "requests", String(input.bodySequence));
|
|
251
|
-
this.cancelBody = () => {
|
|
252
|
-
this.stopAdmission();
|
|
253
|
-
if (!this.executionSignal.signal.aborted) {
|
|
254
|
-
this.executionSignal.abort(input.signal.reason);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
input.signal.addEventListener("abort", this.cancelBody, { once: true });
|
|
258
|
-
if (input.signal.aborted)
|
|
259
|
-
this.cancelBody();
|
|
260
|
-
this.running = this.run();
|
|
261
|
-
this.failure = this.running.then(() => new Promise(() => { }), (error) => Promise.reject(error));
|
|
262
|
-
}
|
|
247
|
+
export class BodyRequestPump extends LifecycleBodyRequestPump {
|
|
263
248
|
static async open(input) {
|
|
264
|
-
await
|
|
265
|
-
return new BodyRequestPump(input);
|
|
266
|
-
}
|
|
267
|
-
async run() {
|
|
268
|
-
while (!this.closeSignal.signal.aborted) {
|
|
269
|
-
for (const name of await requestFiles(this.directory)) {
|
|
270
|
-
if (this.closeSignal.signal.aborted)
|
|
271
|
-
return;
|
|
272
|
-
const id = name.slice(0, -".request.json".length);
|
|
273
|
-
if (this.handled.has(id))
|
|
274
|
-
continue;
|
|
275
|
-
const path = join(this.directory, name);
|
|
276
|
-
const claim = decodeClaim(await readFile(path, "utf8"), id);
|
|
277
|
-
if (claim === null) {
|
|
278
|
-
await rm(path, { force: true });
|
|
279
|
-
this.handled.add(id);
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (!this.admissionClosed) {
|
|
283
|
-
await serve({
|
|
284
|
-
directory: this.directory,
|
|
285
|
-
claim,
|
|
286
|
-
...this.input,
|
|
287
|
-
signal: this.executionSignal.signal,
|
|
288
|
-
admissionOpen: () => !this.admissionClosed,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
this.handled.add(id);
|
|
292
|
-
}
|
|
293
|
-
try {
|
|
294
|
-
await abortableDelay(POLL_MS, this.closeSignal.signal);
|
|
295
|
-
}
|
|
296
|
-
catch (error) {
|
|
297
|
-
if (this.closeSignal.signal.aborted)
|
|
298
|
-
return;
|
|
299
|
-
throw error;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
async close() {
|
|
304
|
-
this.stopAdmission();
|
|
305
|
-
try {
|
|
306
|
-
await this.running;
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
this.input.signal.removeEventListener("abort", this.cancelBody);
|
|
310
|
-
await rm(this.directory, { recursive: true, force: true });
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
stopAdmission() {
|
|
314
|
-
this.admissionClosed = true;
|
|
315
|
-
if (!this.closeSignal.signal.aborted) {
|
|
316
|
-
this.closeSignal.abort(new Error("Body request pump closed"));
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
export async function clearBodyRequestTransport(paths) {
|
|
321
|
-
await rm(join(paths.directory, "requests"), { recursive: true, force: true });
|
|
322
|
-
}
|
|
323
|
-
function matchingRequestOrigin(soul, parent, id) {
|
|
324
|
-
return soul.origin.kind === "request" && soul.origin.parent === parent && soul.origin.requestId === id;
|
|
325
|
-
}
|
|
326
|
-
async function settleReservedSoul(paths, parent, request, soul) {
|
|
327
|
-
await (matchingRequestOrigin(soul, parent.id, request.id)
|
|
328
|
-
? serveRequest(paths, request.id, request.child)
|
|
329
|
-
: voidRequest(paths, request.id, "reserved child origin does not match the request"));
|
|
330
|
-
}
|
|
331
|
-
async function settleReserved(paths, parent, request, now, signal) {
|
|
332
|
-
const childPaths = pathsForAkuId(request.world, request.child);
|
|
333
|
-
const deadline = performance.now() + BIRTH_TIMEOUT_MS;
|
|
334
|
-
for (;;) {
|
|
335
|
-
signal?.throwIfAborted();
|
|
336
|
-
if (!(await access(childPaths.directory).then(() => true, () => false))) {
|
|
337
|
-
await voidRequest(paths, request.id, "reserved child directory is absent");
|
|
338
|
-
return true;
|
|
339
|
-
}
|
|
340
|
-
const soul = await readSoul(childPaths);
|
|
341
|
-
if (soul !== null) {
|
|
342
|
-
await settleReservedSoul(paths, parent, request, soul);
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
const leash = await HeldAkumaLeash.try(childPaths);
|
|
346
|
-
if (leash !== null) {
|
|
347
|
-
try {
|
|
348
|
-
const settled = await readSoul(childPaths);
|
|
349
|
-
if (settled !== null) {
|
|
350
|
-
await settleReservedSoul(paths, parent, request, settled);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
await leash.sealIfUnborn(childPaths, {
|
|
354
|
-
evidence: "request settlement",
|
|
355
|
-
at: now(),
|
|
356
|
-
});
|
|
357
|
-
await voidRequest(paths, request.id, "reserved child was sealed unborn");
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
finally {
|
|
361
|
-
leash.release();
|
|
362
|
-
}
|
|
363
|
-
return true;
|
|
364
|
-
}
|
|
365
|
-
if (performance.now() >= deadline)
|
|
366
|
-
return false;
|
|
367
|
-
await abortableDelay(Math.min(POLL_MS, Math.max(0, deadline - performance.now())), signal);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
export async function settleBodyRequests(paths, parent, now, signal) {
|
|
371
|
-
let pending = false;
|
|
372
|
-
for (const request of await readNonterminalRequests(paths)) {
|
|
373
|
-
signal?.throwIfAborted();
|
|
374
|
-
if (request.state === "admitted") {
|
|
375
|
-
await voidRequest(paths, request.id, "body died before serving the request");
|
|
376
|
-
}
|
|
377
|
-
else if (request.state === "reserved" && !(await settleReserved(paths, parent, request, now, signal)))
|
|
378
|
-
pending = true;
|
|
249
|
+
return (await LifecycleBodyRequestPump.openWithService(input, async (claim) => await serveRequest(claim)));
|
|
379
250
|
}
|
|
380
|
-
return pending ? "pending" : "settled";
|
|
381
251
|
}
|
|
@@ -210,7 +210,7 @@ export function decodeClaim(bytes, fileId) {
|
|
|
210
210
|
const value = object(decoded);
|
|
211
211
|
if (value === null ||
|
|
212
212
|
!exactKeys(value, ["action", "id", "payload"]) ||
|
|
213
|
-
|
|
213
|
+
!UUID.test(fileId) ||
|
|
214
214
|
typeof value.id !== "string" ||
|
|
215
215
|
!UUID.test(value.id))
|
|
216
216
|
return null;
|
|
@@ -26,12 +26,13 @@ export function injectedBodyRequests() {
|
|
|
26
26
|
}
|
|
27
27
|
async function requestBody(input) {
|
|
28
28
|
input.signal?.throwIfAborted();
|
|
29
|
-
|
|
29
|
+
const transportId = randomUUID();
|
|
30
|
+
await atomicJson(requestPath(input.directory, transportId), {
|
|
30
31
|
id: input.claim.id,
|
|
31
32
|
action: input.claim.action,
|
|
32
33
|
payload: requestPayload(input.claim),
|
|
33
34
|
});
|
|
34
|
-
const path = receiptPath(input.directory,
|
|
35
|
+
const path = receiptPath(input.directory, transportId);
|
|
35
36
|
for (;;) {
|
|
36
37
|
try {
|
|
37
38
|
const receipt = decodeReceipt(await readFile(path, "utf8"), input.claim.id, input.claim.action);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type AkuId, type ActivityHistory
|
|
1
|
+
import { type AkuId, type ActivityHistory } from "../../akuma/index.js";
|
|
2
2
|
import { Keiyaku, type AkumaKillResult, type AkumaHistoryResult, type AkumaObservation, type AkumaTellResult, type AkumaWaitResult, type CallResult, type ForkResult, type Keiyaku as KeiyakuContract, type Repo } from "../../index.js";
|
|
3
3
|
import type { Settings } from "../../settings.js";
|
|
4
4
|
import type { WorldRoot } from "../../world.js";
|
|
5
5
|
import type { InvokedAkumaCommand } from "./akuma.js";
|
|
6
|
+
import { finishCall } from "../../library/akuma-creation.js";
|
|
6
7
|
export type AkumaInvocationResult = Readonly<{
|
|
7
8
|
kind: "akuma";
|
|
8
9
|
action: "call";
|
|
@@ -55,6 +56,13 @@ export type AkumaInvocationResult = Readonly<{
|
|
|
55
56
|
mode: "no-answer";
|
|
56
57
|
historyResult: AkumaHistoryResult;
|
|
57
58
|
alias?: string;
|
|
59
|
+
}> | Readonly<{
|
|
60
|
+
kind: "akuma";
|
|
61
|
+
action: "history";
|
|
62
|
+
akuma: AkuId;
|
|
63
|
+
mode: "exact";
|
|
64
|
+
historyResult: AkumaHistoryResult;
|
|
65
|
+
alias?: string;
|
|
58
66
|
}> | Readonly<{
|
|
59
67
|
kind: "akuma";
|
|
60
68
|
action: "fork";
|
|
@@ -72,8 +80,9 @@ type InvokeInput = Readonly<{
|
|
|
72
80
|
settings?: Settings;
|
|
73
81
|
contract?: KeiyakuContract;
|
|
74
82
|
repo?: Repo;
|
|
75
|
-
|
|
83
|
+
environment: NodeJS.ProcessEnv;
|
|
76
84
|
readStdin(): Promise<string>;
|
|
85
|
+
finishCall?: typeof finishCall;
|
|
77
86
|
}>;
|
|
78
87
|
export declare function invokeAkuma(command: InvokedAkumaCommand, input: InvokeInput): Promise<AkumaInvocationResult>;
|
|
79
88
|
export declare function invokeAkumaStatus(path: WorldRoot, akuma: string, alias?: string, repo?: Repo): Promise<AkumaInvocationResult>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Keiyaku, } from "../../index.js";
|
|
2
|
+
import { beginCall, finishCall } from "../../library/akuma-creation.js";
|
|
3
|
+
import { recognizeAndListen } from "../square-edge.js";
|
|
2
4
|
function inputAlias(selector) {
|
|
3
5
|
return selector.startsWith("@") ? selector : undefined;
|
|
4
6
|
}
|
|
@@ -56,6 +58,7 @@ async function invokeHistory(command, input) {
|
|
|
56
58
|
...(command.before === undefined ? {} : { before: command.before }),
|
|
57
59
|
...(command.since === undefined ? {} : { since: command.since }),
|
|
58
60
|
...(command.limit === undefined ? {} : { limit: command.limit }),
|
|
61
|
+
...(command.id === undefined ? {} : { id: command.id }),
|
|
59
62
|
last: command.last,
|
|
60
63
|
});
|
|
61
64
|
return {
|
|
@@ -64,11 +67,13 @@ async function invokeHistory(command, input) {
|
|
|
64
67
|
akuma: result.id,
|
|
65
68
|
historyResult: result,
|
|
66
69
|
...(inputAlias(command.akuma) === undefined ? {} : { alias: command.akuma }),
|
|
67
|
-
...(
|
|
68
|
-
? { mode: "
|
|
69
|
-
: result.kind === "
|
|
70
|
-
? { mode: "
|
|
71
|
-
:
|
|
70
|
+
...(command.id !== undefined
|
|
71
|
+
? { mode: "exact" }
|
|
72
|
+
: result.kind === "history"
|
|
73
|
+
? { mode: "page", history: result.history }
|
|
74
|
+
: result.kind === "last"
|
|
75
|
+
? { mode: "last", answer: result.answer }
|
|
76
|
+
: { mode: "no-answer" }),
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
async function invokeFork(command, input) {
|
|
@@ -100,7 +105,7 @@ export async function invokeAkuma(command, input) {
|
|
|
100
105
|
switch (command.command) {
|
|
101
106
|
case "call": {
|
|
102
107
|
const body = await promptBody(command, input);
|
|
103
|
-
const
|
|
108
|
+
const born = await beginCall({
|
|
104
109
|
path: input.path,
|
|
105
110
|
archetype: command.archetype,
|
|
106
111
|
body,
|
|
@@ -113,8 +118,25 @@ export async function invokeAkuma(command, input) {
|
|
|
113
118
|
...(input.contract === undefined ? {} : { contract: input.contract }),
|
|
114
119
|
...(command.alias === undefined ? {} : { alias: command.alias }),
|
|
115
120
|
...(command.allowed === undefined ? {} : { allowed: command.allowed }),
|
|
116
|
-
...(input.resultRoute === undefined ? {} : { resultRoute: input.resultRoute }),
|
|
117
121
|
});
|
|
122
|
+
const listener = born.born.kind === "born"
|
|
123
|
+
? await recognizeAndListen(input.path, input.environment, born.born.allocated)
|
|
124
|
+
: undefined;
|
|
125
|
+
let result;
|
|
126
|
+
try {
|
|
127
|
+
result = await (input.finishCall ?? finishCall)(born);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (listener?.committed === true) {
|
|
131
|
+
try {
|
|
132
|
+
await listener.rollback();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* preserve launch failure */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
118
140
|
return { kind: "akuma", action: "call", result, world: input.path };
|
|
119
141
|
}
|
|
120
142
|
case "wait":
|
|
@@ -3,6 +3,7 @@ import { parseAkuId } from "../../akuma/identity.js";
|
|
|
3
3
|
import { parseDuration as decodeDuration } from "../../duration.js";
|
|
4
4
|
import { parseAkumaAlias, parseAkumaGlob } from "../../identity/selector.js";
|
|
5
5
|
import { decodeAllowedActions } from "../../akuma/allowed.js";
|
|
6
|
+
import { parsePublicHistoryId } from "../../akuma/identity.js";
|
|
6
7
|
const AKUMA_COMMAND_SPECS = {
|
|
7
8
|
call: {
|
|
8
9
|
arity: 1,
|
|
@@ -49,8 +50,8 @@ const AKUMA_COMMAND_SPECS = {
|
|
|
49
50
|
history: {
|
|
50
51
|
arity: 1,
|
|
51
52
|
stdin: false,
|
|
52
|
-
flags: { before: "value", since: "value", limit: "value", last: "boolean", json: "boolean" },
|
|
53
|
-
usage: "history <aku/...|@alias|kei/...> [--before <index> | --since <index>] [--limit <count>] [--last]",
|
|
53
|
+
flags: { id: "value", before: "value", since: "value", limit: "value", last: "boolean", json: "boolean" },
|
|
54
|
+
usage: "history <aku/...|@alias|kei/...> [--id <historyId> | --before <index> | --since <index>] [--limit <count>] [--last]",
|
|
54
55
|
purpose: "Read Akuma execution history or one complete Contract journal and Dispatch timeline.",
|
|
55
56
|
},
|
|
56
57
|
fork: {
|
|
@@ -203,22 +204,31 @@ function parseWait(rawSelectors, flags, output, fail) {
|
|
|
203
204
|
}
|
|
204
205
|
function parseHistory(selector, flags, output, fail) {
|
|
205
206
|
if (selector.startsWith("kei/")) {
|
|
206
|
-
if (flags.before
|
|
207
|
-
fail("history kei/... does not accept --before, --since, --limit, or --last");
|
|
207
|
+
if ([flags.id, flags.before, flags.since, flags.limit, flags.last].some((value) => value !== undefined)) {
|
|
208
|
+
fail("history kei/... does not accept --id, --before, --since, --limit, or --last");
|
|
208
209
|
}
|
|
209
210
|
return { command: "history", contract: selector, output };
|
|
210
211
|
}
|
|
212
|
+
return parseAkumaHistory(selector, flags, output, fail);
|
|
213
|
+
}
|
|
214
|
+
function parseAkumaHistory(selector, flags, output, fail) {
|
|
215
|
+
const bounded = flags.before !== undefined || flags.since !== undefined || flags.limit !== undefined;
|
|
211
216
|
if (flags.before !== undefined && flags.since !== undefined)
|
|
212
217
|
fail("history --before and --since are mutually exclusive");
|
|
213
|
-
if (flags.
|
|
218
|
+
if (flags.id !== undefined && (flags.last === true || bounded))
|
|
219
|
+
fail("history --id cannot be combined with --last, --before, --since, or --limit");
|
|
220
|
+
if (flags.last === true && bounded)
|
|
214
221
|
fail("history --last cannot be combined with --before, --since, or --limit");
|
|
215
|
-
}
|
|
216
222
|
const limit = flags.limit === undefined ? undefined : positiveIndex(flags.limit, "--limit", fail);
|
|
217
223
|
if (limit !== undefined && limit > 5_000)
|
|
218
224
|
fail("--limit must be no greater than 5000");
|
|
225
|
+
const id = flags.id === undefined ? undefined : stringFlag(flags.id, "history --id requires a nonblank historyId", fail);
|
|
226
|
+
if (id !== undefined && parsePublicHistoryId(id) === null)
|
|
227
|
+
fail("history --id requires turn/<positive safe integer>");
|
|
219
228
|
return {
|
|
220
229
|
command: "history",
|
|
221
230
|
akuma: validateDirect(selector, fail),
|
|
231
|
+
...(id === undefined ? {} : { id }),
|
|
222
232
|
last: flags.last === true,
|
|
223
233
|
...(flags.before === undefined ? {} : { before: positiveIndex(flags.before, "--before", fail) }),
|
|
224
234
|
...(flags.since === undefined ? {} : { since: positiveIndex(flags.since, "--since", fail) }),
|
|
@@ -56,6 +56,7 @@ export type ParsedAbandon = Output & Actor & Readonly<{
|
|
|
56
56
|
export type ParsedStatus = Output & (Readonly<{
|
|
57
57
|
command: "status";
|
|
58
58
|
contract?: string;
|
|
59
|
+
selectors?: readonly string[];
|
|
59
60
|
akuma?: never;
|
|
60
61
|
}> | Readonly<{
|
|
61
62
|
command: "status";
|
|
@@ -178,8 +179,8 @@ export declare const CONTRACT_COMMAND_SPECS: {
|
|
|
178
179
|
readonly flags: {
|
|
179
180
|
readonly json: "boolean";
|
|
180
181
|
};
|
|
181
|
-
readonly usage: "status [<contract>|@name|<aku/...>]";
|
|
182
|
-
readonly purpose: "Read the world status board or one Contract
|
|
182
|
+
readonly usage: "status [<contract>|@name|<aku/...>]...";
|
|
183
|
+
readonly purpose: "Read the world status board or one or more Contract and Akuma projections.";
|
|
183
184
|
};
|
|
184
185
|
readonly show: {
|
|
185
186
|
readonly positional: "optional";
|
|
@@ -87,8 +87,8 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
87
87
|
positional: "optional",
|
|
88
88
|
stdin: "none",
|
|
89
89
|
flags: { json: "boolean" },
|
|
90
|
-
usage: "status [<contract>|@name|<aku/...>]",
|
|
91
|
-
purpose: "Read the world status board or one Contract
|
|
90
|
+
usage: "status [<contract>|@name|<aku/...>]...",
|
|
91
|
+
purpose: "Read the world status board or one or more Contract and Akuma projections.",
|
|
92
92
|
},
|
|
93
93
|
show: {
|
|
94
94
|
positional: "optional",
|
|
@@ -244,6 +244,9 @@ function parseReview(parts) {
|
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
246
|
function parseStatus(parts) {
|
|
247
|
+
if (parts.positionals.length > 1) {
|
|
248
|
+
return { command: "status", selectors: parts.positionals, output: parts.output };
|
|
249
|
+
}
|
|
247
250
|
const contract = parts.positionals[0];
|
|
248
251
|
if (contract?.startsWith("aku/") === true) {
|
|
249
252
|
return { command: "status", contract, akuma: true, output: parts.output };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { StatusSetResult } from "../result.js";
|
|
2
|
+
import type { Repo } from "../../library/repo.js";
|
|
3
|
+
import type { WorldRoot } from "../../world.js";
|
|
4
|
+
export declare function invokeStatusSet(selectors: readonly string[], world: WorldRoot | null, repo: Repo | undefined): Promise<StatusSetResult>;
|