@astrosheep/keiyaku 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/akuma/akuma.d.ts +34 -11
- package/build/src/akuma/akuma.js +121 -61
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +40 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +3 -1
- package/build/src/akuma/body.js +19 -8
- package/build/src/akuma/heart/facts.d.ts +49 -7
- package/build/src/akuma/heart/index.d.ts +4 -8
- package/build/src/akuma/heart/index.js +76 -15
- package/build/src/akuma/heart/rows.d.ts +7 -5
- package/build/src/akuma/heart/rows.js +98 -11
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +15 -10
- package/build/src/akuma/heart/soul.js +3 -1
- package/build/src/akuma/heart/storage.d.ts +2 -0
- package/build/src/akuma/heart/storage.js +20 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +4 -1
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +4 -4
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -2
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +36 -7
- package/build/src/akuma/requests.d.ts +70 -4
- package/build/src/akuma/requests.js +288 -77
- package/build/src/akuma-body.d.ts +1 -0
- package/build/src/akuma-body.js +31 -0
- package/build/src/cli/accepted.d.ts +4 -1
- package/build/src/cli/accepted.js +26 -4
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +3 -3
- package/build/src/cli/commands/contract.js +1 -1
- package/build/src/cli/commands/install.js +4 -4
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/commands/task.js +4 -4
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +5 -1
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +90 -73
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +1 -0
- package/build/src/cli/parse.js +8 -4
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +84 -13
- package/build/src/cli/render/audit.js +16 -2
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +203 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +9 -1
- package/build/src/cli/render/text.js +3 -1
- package/build/src/cli/result.d.ts +38 -3
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/cli/usage.d.ts +2 -0
- package/build/src/cli/usage.js +4 -0
- package/build/src/contract-worktree.js +3 -0
- package/build/src/dispatch/index.js +50 -39
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +2 -6
- package/build/src/git/integration.js +8 -8
- package/build/src/git/read-observation.js +1 -0
- package/build/src/git/reconcile.d.ts +6 -1
- package/build/src/git/reconcile.js +55 -9
- package/build/src/git/tender.d.ts +19 -4
- package/build/src/git/tender.js +44 -13
- package/build/src/git/terminal-seal.d.ts +14 -0
- package/build/src/git/terminal-seal.js +15 -2
- package/build/src/git/workspace.d.ts +1 -0
- package/build/src/git/workspace.js +12 -4
- package/build/src/index.d.ts +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +20 -22
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/bind.js +3 -3
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +4 -0
- package/build/src/library/configuration.js +19 -0
- package/build/src/library/contract.d.ts +15 -1
- package/build/src/library/contract.js +51 -4
- package/build/src/library/fleet.d.ts +47 -9
- package/build/src/library/fleet.js +184 -44
- package/build/src/library/input.js +1 -0
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/protocol/bind.d.ts +1 -2
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/operations.d.ts +4 -1
- package/build/src/protocol/operations.js +33 -27
- package/build/src/protocol/read/status.d.ts +3 -1
- package/build/src/protocol/read/status.js +24 -14
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/settle.d.ts +1 -0
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -3
- package/build/src/task/board.js +73 -26
- package/build/src/task/index.d.ts +3 -3
- package/build/src/task/index.js +7 -5
- package/build/src/task/operations.d.ts +2 -19
- package/build/src/task/operations.js +29 -7
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/verification/execution.js +2 -1
- package/package.json +3 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
import { parseDocument } from "yaml";
|
|
4
5
|
import { archetypeName } from "./identity.js";
|
|
5
6
|
import { decodeProviderOptions, decodeProviderRecipe, } from "./provider-recipe.js";
|
|
6
7
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
8
|
+
import { effectiveAllowedActions } from "./allowed.js";
|
|
7
9
|
export class AkumaArchetypeError extends Error {
|
|
8
10
|
archetype;
|
|
9
11
|
searched;
|
|
@@ -17,14 +19,11 @@ export class AkumaArchetypeError extends Error {
|
|
|
17
19
|
this.name = "AkumaArchetypeError";
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
|
-
function archetypeDirectory(
|
|
21
|
-
|
|
22
|
-
return userPath === undefined ? null : join(dirname(userPath), "akuma");
|
|
22
|
+
function archetypeDirectory(home) {
|
|
23
|
+
return join(home ?? join(homedir(), ".keiyaku"), "akuma");
|
|
23
24
|
}
|
|
24
|
-
async function archetypePaths(
|
|
25
|
-
const directory = archetypeDirectory(
|
|
26
|
-
if (directory === null)
|
|
27
|
-
return [];
|
|
25
|
+
async function archetypePaths(home) {
|
|
26
|
+
const directory = archetypeDirectory(home);
|
|
28
27
|
try {
|
|
29
28
|
return (await readdir(directory, { withFileTypes: true }))
|
|
30
29
|
.flatMap((entry) => {
|
|
@@ -46,8 +45,8 @@ async function archetypePaths(settings) {
|
|
|
46
45
|
throw error;
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
|
-
export async function listArchetypes(input) {
|
|
50
|
-
return (await archetypePaths(input.
|
|
48
|
+
export async function listArchetypes(input = {}) {
|
|
49
|
+
return (await archetypePaths(input.home)).map(({ name }) => name);
|
|
51
50
|
}
|
|
52
51
|
function archetypeField(values, key, required = false) {
|
|
53
52
|
const value = values[key];
|
|
@@ -96,6 +95,7 @@ function decodeArchetype(name, path, markdown) {
|
|
|
96
95
|
const readonly = archetypeReadonly(values);
|
|
97
96
|
const network = archetypeEnum(values, "network", ["disabled", "enabled"]);
|
|
98
97
|
const description = archetypeField(values, "description");
|
|
98
|
+
const allowed = effectiveAllowedActions(values.allowed);
|
|
99
99
|
const systemPrompt = lines.slice(closing + 1).join("\n");
|
|
100
100
|
return Object.freeze({
|
|
101
101
|
name,
|
|
@@ -109,12 +109,13 @@ function decodeArchetype(name, path, markdown) {
|
|
|
109
109
|
...(network === undefined ? {} : { network }),
|
|
110
110
|
...(systemPrompt.length === 0 ? {} : { systemPrompt }),
|
|
111
111
|
}),
|
|
112
|
+
allowed,
|
|
112
113
|
});
|
|
113
114
|
}
|
|
114
|
-
export async function listArchetypeDefinitions(input) {
|
|
115
|
+
export async function listArchetypeDefinitions(input = {}) {
|
|
115
116
|
// Reads still run concurrently, but the reported failure is the first invalid
|
|
116
117
|
// definition in catalog byte order, not whichever read happened to finish first.
|
|
117
|
-
const settled = await Promise.allSettled((await archetypePaths(input.
|
|
118
|
+
const settled = await Promise.allSettled((await archetypePaths(input.home)).map(async ({ name, path }) => {
|
|
118
119
|
try {
|
|
119
120
|
const definition = decodeArchetype(name, path, await readFile(path, "utf8"));
|
|
120
121
|
return Object.freeze({
|
|
@@ -209,11 +210,9 @@ function admitArchetype(archetype, settings) {
|
|
|
209
210
|
}
|
|
210
211
|
export async function loadArchetype(input) {
|
|
211
212
|
const name = archetypeName(input.name);
|
|
212
|
-
const directory = archetypeDirectory(input.
|
|
213
|
-
const missing = () => new AkumaArchetypeError(name, directory === null ? [] : [join(directory, `${name}.md`)], "was not found", "use `keiyaku ls aku/` to list available Akuma");
|
|
214
|
-
if (directory === null)
|
|
215
|
-
throw missing();
|
|
213
|
+
const directory = archetypeDirectory(input.home);
|
|
216
214
|
const path = join(directory, `${name}.md`);
|
|
215
|
+
const missing = () => new AkumaArchetypeError(name, [path], "was not found", "use `keiyaku ls aku/` to list available Akuma");
|
|
217
216
|
let markdown;
|
|
218
217
|
try {
|
|
219
218
|
markdown = await readFile(path, "utf8");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type SessionFact, type Soul } from "./heart/index.js";
|
|
2
2
|
import type { AkumaPaths } from "./identity.js";
|
|
3
3
|
import { type ProviderAdapter } from "./provider.js";
|
|
4
|
-
import { type RequestChildLaunch } from "./requests.js";
|
|
4
|
+
import { type UpstreamExecutionPort, type RequestChildLaunch } from "./requests.js";
|
|
5
5
|
export declare const CONTROL_RESPONSE_MS = 1000;
|
|
6
6
|
export type BodyLaunch = Readonly<{
|
|
7
7
|
paths: AkumaPaths;
|
|
@@ -12,7 +12,9 @@ export type BodyLaunch = Readonly<{
|
|
|
12
12
|
type BodyRuntime = Readonly<{
|
|
13
13
|
now(): string;
|
|
14
14
|
spawnChild?(launch: RequestChildLaunch): Promise<void>;
|
|
15
|
+
upstream?: UpstreamExecutionPort;
|
|
15
16
|
}>;
|
|
16
17
|
export declare function driveAkumaBody(launch: BodyLaunch, adapter?: ProviderAdapter, runtime?: BodyRuntime): Promise<void>;
|
|
17
18
|
export declare function spawnAkumaBody(launch: BodyLaunch): Promise<void>;
|
|
19
|
+
export declare function runAkumaBody(upstreamFor: (launch: BodyLaunch) => UpstreamExecutionPort): Promise<void>;
|
|
18
20
|
export {};
|
package/build/src/akuma/body.js
CHANGED
|
@@ -185,6 +185,7 @@ async function startTurnDrive(input) {
|
|
|
185
185
|
bodySequence: input.bodySequence,
|
|
186
186
|
now: input.now,
|
|
187
187
|
spawn: input.runtimeSpawn,
|
|
188
|
+
...(input.upstream === undefined ? {} : { upstream: input.upstream }),
|
|
188
189
|
signal: input.supervisor.signal,
|
|
189
190
|
})
|
|
190
191
|
: null;
|
|
@@ -538,6 +539,7 @@ async function runBodyTurns(input) {
|
|
|
538
539
|
body: initial ?? "",
|
|
539
540
|
...(initial === undefined ? {} : { call: initial }),
|
|
540
541
|
launchTells,
|
|
542
|
+
...(runtime.upstream === undefined ? {} : { upstream: runtime.upstream }),
|
|
541
543
|
now: runtime.now,
|
|
542
544
|
});
|
|
543
545
|
if (result.kind === "stopped") {
|
|
@@ -580,8 +582,16 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
580
582
|
}
|
|
581
583
|
}
|
|
582
584
|
catch (error) {
|
|
583
|
-
if (await heartExists(launch.paths))
|
|
584
|
-
|
|
585
|
+
if (!await heartExists(launch.paths))
|
|
586
|
+
return;
|
|
587
|
+
try {
|
|
588
|
+
await leash.sealIfUnborn(launch.paths, {
|
|
589
|
+
evidence: error instanceof Error ? error.message : String(error),
|
|
590
|
+
at: runtime.now(),
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
catch { /* the original Body failure remains authoritative */ }
|
|
594
|
+
throw error;
|
|
585
595
|
}
|
|
586
596
|
finally {
|
|
587
597
|
leash.release();
|
|
@@ -593,20 +603,21 @@ export async function spawnAkumaBody(launch) {
|
|
|
593
603
|
if (actorId === undefined)
|
|
594
604
|
throw new Error("Akuma wake has no born soul");
|
|
595
605
|
const owned = await spawnDetachedProcess({
|
|
596
|
-
argv: [process.execPath, ...process.execArgv, fileURLToPath(import.meta.url), encoded],
|
|
606
|
+
argv: [process.execPath, ...process.execArgv, fileURLToPath(new URL("../akuma-body.js", import.meta.url)), encoded],
|
|
597
607
|
cwd: await launchCwd(launch),
|
|
598
608
|
env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
|
|
599
609
|
log: launch.paths.log,
|
|
600
610
|
});
|
|
601
611
|
owned.release();
|
|
602
612
|
}
|
|
603
|
-
async function
|
|
613
|
+
export async function runAkumaBody(upstreamFor) {
|
|
604
614
|
const encoded = process.argv[2];
|
|
605
615
|
if (encoded === undefined)
|
|
606
616
|
throw new TypeError("Akuma body launch payload is missing");
|
|
607
617
|
const launch = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
608
|
-
await driveAkumaBody(launch
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
618
|
+
await driveAkumaBody(launch, undefined, {
|
|
619
|
+
now: () => new Date().toISOString(),
|
|
620
|
+
spawnChild: spawnAkumaBody,
|
|
621
|
+
upstream: upstreamFor(launch),
|
|
622
|
+
});
|
|
612
623
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AkuId } from "../identity.js";
|
|
2
2
|
import type { ResumeCoordinate } from "../coordinate.js";
|
|
3
3
|
import type { ProviderExecution, ProviderOptions, ReadonlyRestraint } from "../provider-recipe.js";
|
|
4
|
+
import type { AllowedActions } from "../allowed.js";
|
|
4
5
|
export type { ResumeCoordinate } from "../coordinate.js";
|
|
5
6
|
export type Confinement = Readonly<{
|
|
6
7
|
kind: "unconfined";
|
|
@@ -29,9 +30,10 @@ export type Soul = Readonly<{
|
|
|
29
30
|
cwd: string;
|
|
30
31
|
origin: AkumaOrigin;
|
|
31
32
|
confinement: Confinement;
|
|
33
|
+
allowed: AllowedActions;
|
|
32
34
|
createdAt: string;
|
|
33
35
|
}>;
|
|
34
|
-
export type RequestRecipe = Pick<Soul, "description" | "provider" | "options" | "readonly" | "confinement">;
|
|
36
|
+
export type RequestRecipe = Pick<Soul, "description" | "provider" | "options" | "readonly" | "confinement" | "allowed">;
|
|
35
37
|
export type BodyEnd = "exited" | "broke-off" | "put-down";
|
|
36
38
|
export type BodyFact = Readonly<{
|
|
37
39
|
sequence: number;
|
|
@@ -129,26 +131,66 @@ export type TellReceiptInput = Readonly<{
|
|
|
129
131
|
}>);
|
|
130
132
|
export type RequestInput = Readonly<{
|
|
131
133
|
id: string;
|
|
134
|
+
action: "akuma.call";
|
|
132
135
|
archetype: string;
|
|
133
136
|
body: string;
|
|
134
137
|
cwd?: string;
|
|
135
138
|
world: string;
|
|
136
139
|
recipe: RequestRecipe;
|
|
140
|
+
}> | Readonly<{
|
|
141
|
+
id: string;
|
|
142
|
+
action: "akuma.wait";
|
|
143
|
+
targets: readonly AkuId[];
|
|
144
|
+
completion: "any" | "all";
|
|
145
|
+
timeoutMs?: number;
|
|
146
|
+
}> | Readonly<{
|
|
147
|
+
id: string;
|
|
148
|
+
action: "akuma.tell";
|
|
149
|
+
target: AkuId;
|
|
150
|
+
body: string;
|
|
151
|
+
}> | Readonly<{
|
|
152
|
+
id: string;
|
|
153
|
+
action: "akuma.kill";
|
|
154
|
+
targets: readonly AkuId[];
|
|
137
155
|
}>;
|
|
138
|
-
export type
|
|
156
|
+
export type UpstreamRequestService = Readonly<{
|
|
157
|
+
action: "akuma.wait";
|
|
158
|
+
}> | Readonly<{
|
|
159
|
+
action: "akuma.tell";
|
|
160
|
+
target: AkuId;
|
|
161
|
+
tellId: string;
|
|
162
|
+
}> | Readonly<{
|
|
163
|
+
action: "akuma.kill";
|
|
164
|
+
results: readonly Readonly<{
|
|
165
|
+
id: AkuId;
|
|
166
|
+
evidence: KillEvidence;
|
|
167
|
+
}>[];
|
|
168
|
+
}>;
|
|
169
|
+
type AdmittedRequest = RequestInput & Readonly<{
|
|
170
|
+
requester: AkuId;
|
|
139
171
|
admittedAt: string;
|
|
140
|
-
}
|
|
172
|
+
}>;
|
|
173
|
+
export type RequestFact = (AdmittedRequest & Readonly<{
|
|
141
174
|
state: "admitted";
|
|
142
|
-
}> |
|
|
175
|
+
}>) | (Extract<AdmittedRequest, {
|
|
176
|
+
action: "akuma.call";
|
|
177
|
+
}> & Readonly<{
|
|
143
178
|
state: "reserved";
|
|
144
179
|
child: AkuId;
|
|
145
|
-
}> |
|
|
180
|
+
}>) | (Extract<AdmittedRequest, {
|
|
181
|
+
action: "akuma.call";
|
|
182
|
+
}> & Readonly<{
|
|
146
183
|
state: "served";
|
|
147
184
|
child: AkuId;
|
|
148
|
-
}> |
|
|
185
|
+
}>) | (Exclude<AdmittedRequest, {
|
|
186
|
+
action: "akuma.call";
|
|
187
|
+
}> & Readonly<{
|
|
188
|
+
state: "served";
|
|
189
|
+
service: UpstreamRequestService;
|
|
190
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
149
191
|
state: "refused";
|
|
150
192
|
diagnostic: string;
|
|
151
|
-
}> | Readonly<{
|
|
193
|
+
}>) | (AdmittedRequest & Readonly<{
|
|
152
194
|
state: "voided";
|
|
153
195
|
evidence: string;
|
|
154
196
|
}>);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { AkuId, AkumaPaths } from "../identity.js";
|
|
2
|
-
import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, RequestFact, RequestInput, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
|
|
2
|
+
import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, RequestFact, RequestInput, UpstreamRequestService, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
|
|
3
3
|
export type { CallFact } from "./facts.js";
|
|
4
4
|
import type { ActivityFact } from "./rows.js";
|
|
5
5
|
import { type ActivityFactSlice } from "./timeline.js";
|
|
6
6
|
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
|
|
7
7
|
export { life, lifeAt } from "./facts.js";
|
|
8
|
-
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, Confinement, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
|
|
8
|
+
export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, Confinement, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, UpstreamRequestService, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
|
|
9
9
|
export type { ResumeCoordinate } from "../coordinate.js";
|
|
10
10
|
export declare function readSoul(paths: AkumaPaths): Promise<Soul | null>;
|
|
11
11
|
export declare function heartExists(paths: AkumaPaths): Promise<boolean>;
|
|
@@ -16,15 +16,10 @@ export declare function appendActivity(paths: AkumaPaths, input: Readonly<{
|
|
|
16
16
|
event: unknown;
|
|
17
17
|
at: string;
|
|
18
18
|
}>): Promise<number>;
|
|
19
|
-
export type ActivitySliceInput = Readonly<{
|
|
20
|
-
before?: number;
|
|
21
|
-
since?: number;
|
|
22
|
-
limit?: number;
|
|
23
|
-
}>;
|
|
24
19
|
export type ActivitySlice = ActivityFactSlice;
|
|
25
20
|
export type { ActivityFact };
|
|
26
21
|
export type { TimelineFact } from "./timeline.js";
|
|
27
|
-
export declare function activitySlice(paths: AkumaPaths
|
|
22
|
+
export declare function activitySlice(paths: AkumaPaths): Promise<ActivitySlice>;
|
|
28
23
|
export declare function recordTell(paths: AkumaPaths, tell: Omit<TellFact, "sequence" | "state" | "deliveries">): Promise<Readonly<{
|
|
29
24
|
kind: "not-born";
|
|
30
25
|
} | {
|
|
@@ -38,6 +33,7 @@ export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Re
|
|
|
38
33
|
}>): Promise<RequestFact>;
|
|
39
34
|
export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
40
35
|
export declare function serveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
|
|
36
|
+
export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, service: UpstreamRequestService): Promise<RequestFact>;
|
|
41
37
|
export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
|
|
42
38
|
export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
|
|
43
39
|
export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertRequestFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, nonterminalRequestFacts, pauseFact, requestFact, sessionFactForCoordinate, stopFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, } from "./rows.js";
|
|
1
|
+
import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertRequestFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, nonterminalRequestFacts, pauseFact, requestFact, sessionFactForCoordinate, stopFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./rows.js";
|
|
2
2
|
import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingTellFacts, tellFact, tellIdsForFence, } from "./tells.js";
|
|
3
3
|
import { activityFactSlice, pruneActivityFacts, } from "./timeline.js";
|
|
4
|
-
import { isHeartAbsent, readSealFromLeash, transaction, withHeart } from "./storage.js";
|
|
4
|
+
import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart } from "./storage.js";
|
|
5
5
|
import { soulFact } from "./soul.js";
|
|
6
|
+
import { clipAllowedActions } from "../allowed.js";
|
|
6
7
|
export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
|
|
7
8
|
export { life, lifeAt } from "./facts.js";
|
|
8
9
|
const ACTIVITY_LIMIT = 5_000;
|
|
@@ -46,14 +47,20 @@ export async function appendActivity(paths, input) {
|
|
|
46
47
|
return sequence;
|
|
47
48
|
}));
|
|
48
49
|
}
|
|
49
|
-
export async function activitySlice(paths
|
|
50
|
-
|
|
51
|
-
return await withHeart(paths, (heart) => activityFactSlice(heart, { ...input, limit }));
|
|
50
|
+
export async function activitySlice(paths) {
|
|
51
|
+
return await withHeart(paths, (heart) => readTransaction(heart, () => activityFactSlice(heart)));
|
|
52
52
|
}
|
|
53
53
|
export async function recordTell(paths, tell) {
|
|
54
54
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
55
55
|
if (soulFact(heart) === null)
|
|
56
56
|
return { kind: "not-born" };
|
|
57
|
+
const existing = tellFact(heart, tell.id);
|
|
58
|
+
if (existing !== null) {
|
|
59
|
+
if (existing.body !== tell.body || existing.recordedAt !== tell.recordedAt) {
|
|
60
|
+
throw new Error(`tell ${tell.id} reused different input`);
|
|
61
|
+
}
|
|
62
|
+
return { kind: "recorded", tell: existing };
|
|
63
|
+
}
|
|
57
64
|
const sequence = insertTellFact(heart, tell);
|
|
58
65
|
pruneActivityFacts(heart, ACTIVITY_LIMIT);
|
|
59
66
|
return { kind: "recorded", tell: { sequence, ...tell, state: "pending", deliveries: [] } };
|
|
@@ -83,21 +90,55 @@ export async function recordTellReceipt(paths, input) {
|
|
|
83
90
|
}));
|
|
84
91
|
}
|
|
85
92
|
function sameRequestInput(fact, input) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
if (fact.id !== input.id || fact.action !== input.action)
|
|
94
|
+
return false;
|
|
95
|
+
if (fact.action === "akuma.call" && input.action === "akuma.call") {
|
|
96
|
+
return fact.archetype === input.archetype
|
|
97
|
+
&& fact.body === input.body
|
|
98
|
+
&& fact.cwd === input.cwd
|
|
99
|
+
&& fact.world === input.world
|
|
100
|
+
&& JSON.stringify(fact.recipe) === JSON.stringify(input.recipe);
|
|
101
|
+
}
|
|
102
|
+
if (fact.action === "akuma.wait" && input.action === "akuma.wait") {
|
|
103
|
+
return fact.completion === input.completion
|
|
104
|
+
&& fact.timeoutMs === input.timeoutMs
|
|
105
|
+
&& JSON.stringify(fact.targets) === JSON.stringify(input.targets);
|
|
106
|
+
}
|
|
107
|
+
if (fact.action === "akuma.tell" && input.action === "akuma.tell") {
|
|
108
|
+
return fact.target === input.target && fact.body === input.body;
|
|
109
|
+
}
|
|
110
|
+
return fact.action === "akuma.kill" && input.action === "akuma.kill"
|
|
111
|
+
&& JSON.stringify(fact.targets) === JSON.stringify(input.targets);
|
|
92
112
|
}
|
|
93
113
|
export async function admitRequest(paths, input) {
|
|
94
114
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
95
|
-
|
|
115
|
+
const soul = soulFact(heart);
|
|
116
|
+
if (soul === null)
|
|
117
|
+
throw new Error("Akuma request admission requires a born Soul");
|
|
118
|
+
const action = input.action;
|
|
119
|
+
const parentAllowed = soul.allowed;
|
|
120
|
+
const permitted = action === "akuma.wait" || parentAllowed.includes(action);
|
|
121
|
+
const normalized = input.action === "akuma.call"
|
|
122
|
+
? {
|
|
123
|
+
...input,
|
|
124
|
+
recipe: {
|
|
125
|
+
...input.recipe,
|
|
126
|
+
allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
: input;
|
|
130
|
+
insertRequestFact(heart, {
|
|
131
|
+
...normalized,
|
|
132
|
+
requester: soul.id,
|
|
133
|
+
admittedAt: input.admittedAt,
|
|
134
|
+
...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
|
|
135
|
+
});
|
|
96
136
|
const fact = requestFact(heart, input.id);
|
|
97
137
|
if (fact === null)
|
|
98
138
|
throw new Error(`Akuma request ${input.id} was not admitted`);
|
|
99
|
-
if (!sameRequestInput(fact,
|
|
139
|
+
if (fact.requester !== soul.id || !sameRequestInput(fact, normalized)) {
|
|
100
140
|
throw new Error(`Akuma request ${input.id} reused different input`);
|
|
141
|
+
}
|
|
101
142
|
return fact;
|
|
102
143
|
}));
|
|
103
144
|
}
|
|
@@ -106,6 +147,8 @@ export async function reserveRequest(paths, id, child) {
|
|
|
106
147
|
const before = requestFact(heart, id);
|
|
107
148
|
if (before === null)
|
|
108
149
|
throw new Error(`unknown Akuma request ${id}`);
|
|
150
|
+
if (before.action !== "akuma.call")
|
|
151
|
+
throw new Error(`Akuma request ${id} cannot reserve a child`);
|
|
109
152
|
if (before.state === "admitted")
|
|
110
153
|
updateRequestReserved(heart, id, child);
|
|
111
154
|
else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
|
|
@@ -119,6 +162,8 @@ export async function serveRequest(paths, id, child) {
|
|
|
119
162
|
const before = requestFact(heart, id);
|
|
120
163
|
if (before === null)
|
|
121
164
|
throw new Error(`unknown Akuma request ${id}`);
|
|
165
|
+
if (before.action !== "akuma.call")
|
|
166
|
+
throw new Error(`Akuma request ${id} cannot serve a child`);
|
|
122
167
|
if (before.state === "reserved" && before.child === child)
|
|
123
168
|
updateRequestServed(heart, id, child);
|
|
124
169
|
else if (before.state !== "served" || before.child !== child) {
|
|
@@ -127,6 +172,22 @@ export async function serveRequest(paths, id, child) {
|
|
|
127
172
|
return requestFact(heart, id);
|
|
128
173
|
}));
|
|
129
174
|
}
|
|
175
|
+
export async function serveUpstreamRequest(paths, id, service) {
|
|
176
|
+
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
177
|
+
const before = requestFact(heart, id);
|
|
178
|
+
if (before === null)
|
|
179
|
+
throw new Error(`unknown Akuma request ${id}`);
|
|
180
|
+
if (before.action === "akuma.call" || service.action !== before.action) {
|
|
181
|
+
throw new Error(`Akuma request ${id} has a mismatched service reference`);
|
|
182
|
+
}
|
|
183
|
+
if (before.state === "admitted")
|
|
184
|
+
updateUpstreamRequestServed(heart, id, service);
|
|
185
|
+
else if (before.state !== "served" || JSON.stringify(before.service) !== JSON.stringify(service)) {
|
|
186
|
+
throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
|
|
187
|
+
}
|
|
188
|
+
return requestFact(heart, id);
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
130
191
|
export async function refuseRequest(paths, id, diagnostic) {
|
|
131
192
|
return await withHeart(paths, (heart) => transaction(heart, () => {
|
|
132
193
|
const before = requestFact(heart, id);
|
|
@@ -234,7 +295,7 @@ export async function finishBodyIfIdle(paths, input) {
|
|
|
234
295
|
}
|
|
235
296
|
export async function readHeart(paths) {
|
|
236
297
|
try {
|
|
237
|
-
return await withHeart(paths, (heart) => ({
|
|
298
|
+
return await withHeart(paths, (heart) => readTransaction(heart, () => ({
|
|
238
299
|
soul: soulFact(heart),
|
|
239
300
|
latestBody: latestBodyFact(heart),
|
|
240
301
|
latestSession: latestSessionFact(heart),
|
|
@@ -242,7 +303,7 @@ export async function readHeart(paths) {
|
|
|
242
303
|
latestKill: latestKillFact(heart),
|
|
243
304
|
stop: stopFact(heart),
|
|
244
305
|
pause: pauseFact(heart),
|
|
245
|
-
}));
|
|
306
|
+
})));
|
|
246
307
|
}
|
|
247
308
|
catch (error) {
|
|
248
309
|
if (isHeartAbsent(error)) {
|
|
@@ -42,14 +42,13 @@ export type CallRow = Readonly<{
|
|
|
42
42
|
export type RequestRow = Readonly<{
|
|
43
43
|
sequence: number;
|
|
44
44
|
id: string;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
world: string;
|
|
49
|
-
recipe_json: string;
|
|
45
|
+
requester: string;
|
|
46
|
+
action: string;
|
|
47
|
+
payload_json: string;
|
|
50
48
|
admitted_at: string;
|
|
51
49
|
state: RequestFact["state"];
|
|
52
50
|
child: string | null;
|
|
51
|
+
service_json: string | null;
|
|
53
52
|
diagnostic: string | null;
|
|
54
53
|
evidence: string | null;
|
|
55
54
|
}>;
|
|
@@ -102,12 +101,15 @@ export declare function insertActivityFact(database: DatabaseSync, input: Readon
|
|
|
102
101
|
at: string;
|
|
103
102
|
}>): number;
|
|
104
103
|
export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
|
|
104
|
+
requester: AkuId;
|
|
105
105
|
admittedAt: string;
|
|
106
|
+
refusal?: string;
|
|
106
107
|
}>): void;
|
|
107
108
|
export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
|
|
108
109
|
export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
|
|
109
110
|
export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
|
|
110
111
|
export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
|
|
112
|
+
export declare function updateUpstreamRequestServed(database: DatabaseSync, id: string, service: unknown): void;
|
|
111
113
|
export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
|
|
112
114
|
export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
|
|
113
115
|
export declare function insertStopControl(database: DatabaseSync, bodySequence: number, at: string): void;
|
|
@@ -7,6 +7,66 @@ function parsed(value) {
|
|
|
7
7
|
throw new Error("Akuma authority contains non-text JSON");
|
|
8
8
|
return JSON.parse(value);
|
|
9
9
|
}
|
|
10
|
+
function object(value) {
|
|
11
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
12
|
+
? value
|
|
13
|
+
: null;
|
|
14
|
+
}
|
|
15
|
+
function exactKeys(value, expected) {
|
|
16
|
+
const actual = Object.keys(value).sort();
|
|
17
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
18
|
+
}
|
|
19
|
+
const KILL_EVIDENCE = [
|
|
20
|
+
"already-killed",
|
|
21
|
+
"already-stopped",
|
|
22
|
+
"hung",
|
|
23
|
+
"killed",
|
|
24
|
+
"untidy",
|
|
25
|
+
"unavailable",
|
|
26
|
+
];
|
|
27
|
+
function decodeRequestService(value, input) {
|
|
28
|
+
const service = object(value);
|
|
29
|
+
if (service === null || service.action !== input.action) {
|
|
30
|
+
throw new Error("Akuma authority contains a mismatched request service reference");
|
|
31
|
+
}
|
|
32
|
+
if (input.action === "akuma.wait") {
|
|
33
|
+
if (!exactKeys(service, ["action"])) {
|
|
34
|
+
throw new Error("Akuma authority contains an invalid wait service reference");
|
|
35
|
+
}
|
|
36
|
+
return { action: input.action };
|
|
37
|
+
}
|
|
38
|
+
if (input.action === "akuma.tell") {
|
|
39
|
+
if (!exactKeys(service, ["action", "target", "tellId"])
|
|
40
|
+
|| service.target !== input.target
|
|
41
|
+
|| service.tellId !== input.id) {
|
|
42
|
+
throw new Error("Akuma authority contains an invalid tell service reference");
|
|
43
|
+
}
|
|
44
|
+
return { action: input.action, target: input.target, tellId: input.id };
|
|
45
|
+
}
|
|
46
|
+
if (!exactKeys(service, ["action", "results"]) || !Array.isArray(service.results)) {
|
|
47
|
+
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
48
|
+
}
|
|
49
|
+
let targetIndex = -1;
|
|
50
|
+
const results = service.results.map((value) => {
|
|
51
|
+
const result = object(value);
|
|
52
|
+
if (result === null
|
|
53
|
+
|| !exactKeys(result, ["evidence", "id"])
|
|
54
|
+
|| typeof result.id !== "string"
|
|
55
|
+
|| !KILL_EVIDENCE.includes(result.evidence)) {
|
|
56
|
+
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
57
|
+
}
|
|
58
|
+
const index = input.targets.indexOf(result.id);
|
|
59
|
+
if (index <= targetIndex) {
|
|
60
|
+
throw new Error("Akuma authority contains an invalid kill service reference");
|
|
61
|
+
}
|
|
62
|
+
targetIndex = index;
|
|
63
|
+
return {
|
|
64
|
+
id: result.id,
|
|
65
|
+
evidence: result.evidence,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return { action: input.action, results };
|
|
69
|
+
}
|
|
10
70
|
function resumeCoordinate(value) {
|
|
11
71
|
const coordinate = decodeResumeCoordinate(value);
|
|
12
72
|
if (coordinate === null)
|
|
@@ -67,18 +127,39 @@ export function decodeCallRow(row) {
|
|
|
67
127
|
return { kind: "call", sequence: row.sequence, turnSequence: row.turn_sequence, body: row.body, at: row.at };
|
|
68
128
|
}
|
|
69
129
|
export function decodeRequestRow(row) {
|
|
130
|
+
if (!["akuma.call", "akuma.wait", "akuma.tell", "akuma.kill"].includes(row.action)) {
|
|
131
|
+
throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
|
|
132
|
+
}
|
|
133
|
+
const payload = parsed(row.payload_json);
|
|
70
134
|
const input = {
|
|
71
135
|
id: row.id,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
...
|
|
75
|
-
world: row.world,
|
|
76
|
-
recipe: parsed(row.recipe_json),
|
|
136
|
+
requester: row.requester,
|
|
137
|
+
action: row.action,
|
|
138
|
+
...payload,
|
|
77
139
|
admittedAt: row.admitted_at,
|
|
78
140
|
};
|
|
79
|
-
if (row.state === "reserved"
|
|
141
|
+
if (row.state === "reserved") {
|
|
142
|
+
if (input.action !== "akuma.call" || row.child === null) {
|
|
143
|
+
throw new Error("Akuma authority contains an invalid reserved request");
|
|
144
|
+
}
|
|
80
145
|
return { ...input, state: row.state, child: row.child };
|
|
81
146
|
}
|
|
147
|
+
if (row.state === "served") {
|
|
148
|
+
if (input.action === "akuma.call") {
|
|
149
|
+
if (row.child === null)
|
|
150
|
+
throw new Error("Akuma authority contains a served call without a child");
|
|
151
|
+
return { ...input, state: row.state, child: row.child };
|
|
152
|
+
}
|
|
153
|
+
if (row.service_json === null) {
|
|
154
|
+
throw new Error("Akuma authority contains a served request without a service reference");
|
|
155
|
+
}
|
|
156
|
+
const service = decodeRequestService(parsed(row.service_json), input);
|
|
157
|
+
return {
|
|
158
|
+
...input,
|
|
159
|
+
state: row.state,
|
|
160
|
+
service,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
82
163
|
if (row.state === "refused")
|
|
83
164
|
return { ...input, state: row.state, diagnostic: row.diagnostic };
|
|
84
165
|
if (row.state === "voided")
|
|
@@ -129,11 +210,13 @@ export function insertActivityFact(database, input) {
|
|
|
129
210
|
.run(sequence, input.turnSequence, encodeActivityEvent(input.event), input.at);
|
|
130
211
|
return sequence;
|
|
131
212
|
}
|
|
132
|
-
const REQUEST_COLUMNS = `sequence, id,
|
|
133
|
-
state, child, diagnostic, evidence`;
|
|
213
|
+
const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted_at,
|
|
214
|
+
state, child, service_json, diagnostic, evidence`;
|
|
134
215
|
export function insertRequestFact(database, input) {
|
|
135
|
-
|
|
136
|
-
|
|
216
|
+
const { id, action, requester, admittedAt, refusal, ...payload } = input;
|
|
217
|
+
database.prepare(`INSERT OR IGNORE INTO requests(
|
|
218
|
+
id, requester, action, payload_json, admitted_at, state, diagnostic
|
|
219
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(id, requester, action, json(payload), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
|
|
137
220
|
}
|
|
138
221
|
export function requestFact(database, id) {
|
|
139
222
|
const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
|
|
@@ -152,12 +235,16 @@ export function updateRequestServed(database, id, child) {
|
|
|
152
235
|
database.prepare(`UPDATE requests SET state = 'served', child = ?
|
|
153
236
|
WHERE id = ? AND state = 'reserved'`).run(child, id);
|
|
154
237
|
}
|
|
238
|
+
export function updateUpstreamRequestServed(database, id, service) {
|
|
239
|
+
database.prepare(`UPDATE requests SET state = 'served', service_json = ?
|
|
240
|
+
WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`).run(json(service), id);
|
|
241
|
+
}
|
|
155
242
|
export function updateRequestRefused(database, id, diagnostic) {
|
|
156
243
|
database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
|
|
157
244
|
WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
|
|
158
245
|
}
|
|
159
246
|
export function updateRequestVoided(database, id, evidence) {
|
|
160
|
-
database.prepare(`UPDATE requests SET state = 'voided', child = NULL, evidence = ?
|
|
247
|
+
database.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
|
|
161
248
|
WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
|
|
162
249
|
}
|
|
163
250
|
export function insertStopControl(database, bodySequence, at) {
|