@astrosheep/keiyaku 4.1.1 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/akuma/akuma-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +9 -0
- package/build/src/akuma/akuma-handle.d.ts +39 -0
- package/build/src/akuma/akuma-handle.js +263 -0
- package/build/src/akuma/akuma-observe.d.ts +19 -0
- package/build/src/akuma/akuma-observe.js +74 -0
- package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
- package/build/src/akuma/akuma-product-symbols.js +1 -0
- package/build/src/akuma/akuma-product.d.ts +18 -0
- package/build/src/akuma/akuma-product.js +154 -0
- package/build/src/akuma/akuma.d.ts +23 -70
- package/build/src/akuma/akuma.js +17 -433
- package/build/src/akuma/body-supervisor.d.ts +26 -0
- package/build/src/akuma/body-supervisor.js +89 -0
- package/build/src/akuma/body.d.ts +1 -1
- package/build/src/akuma/body.js +3 -2
- package/build/src/akuma/projection-read.d.ts +17 -0
- package/build/src/akuma/projection-read.js +135 -0
- package/build/src/akuma/projection.d.ts +1 -18
- package/build/src/akuma/projection.js +21 -155
- package/build/src/akuma/request-execution.d.ts +6 -0
- package/build/src/akuma/request-execution.js +86 -0
- package/build/src/akuma/request-serve.d.ts +12 -1
- package/build/src/akuma/request-serve.js +2 -102
- package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
- package/build/src/akuma/{body-turn.js → turn-drive.js} +4 -99
- package/build/src/cli/commands/akuma.d.ts +0 -6
- package/build/src/cli/commands/akuma.js +1 -11
- package/build/src/cli/commands/contract-invoke.d.ts +25 -0
- package/build/src/cli/commands/contract-invoke.js +193 -0
- package/build/src/cli/commands/contract.d.ts +12 -0
- package/build/src/cli/commands/contract.js +229 -0
- package/build/src/cli/invoke.js +24 -184
- package/build/src/cli/parse.d.ts +1 -1
- package/build/src/cli/parse.js +8 -222
- package/build/src/cli/render/akuma-activity.d.ts +38 -0
- package/build/src/cli/render/akuma-activity.js +362 -0
- package/build/src/cli/render/akuma.d.ts +1 -1
- package/build/src/cli/render/akuma.js +14 -371
- package/build/src/cli/render/contract-history.d.ts +2 -0
- package/build/src/cli/render/contract-history.js +83 -0
- package/build/src/cli/render/contract.d.ts +1 -2
- package/build/src/cli/render/contract.js +1 -83
- package/build/src/git/reconcile.d.ts +8 -1
- package/build/src/git/reconcile.js +13 -301
- package/build/src/git/scratch.d.ts +8 -5
- package/build/src/git/scratch.js +29 -2
- package/build/src/git/terminal-reconcile.d.ts +21 -0
- package/build/src/git/terminal-reconcile.js +278 -0
- package/build/src/library/contract-bind.d.ts +26 -0
- package/build/src/library/contract-bind.js +106 -0
- package/build/src/library/contract-operations.d.ts +77 -0
- package/build/src/library/contract-operations.js +130 -0
- package/build/src/library/contract-types.d.ts +95 -0
- package/build/src/library/contract-types.js +1 -0
- package/build/src/library/contract.d.ts +11 -136
- package/build/src/library/contract.js +9 -222
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { pathsForAkuId, worldRootForAkumaPaths } from "./identity.js";
|
|
|
6
6
|
import { BIRTH_TIMEOUT_MS, publishAkuma } from "./publication.js";
|
|
7
7
|
import { atomicJson, canonicalAkuId, decodeClaim, decodeRecipe, receiptPath, } from "./request-wire.js";
|
|
8
8
|
import { isTaskMutationAction } from "../task/mutation.js";
|
|
9
|
+
import { executeRequest } from "./request-execution.js";
|
|
9
10
|
const POLL_MS = 100;
|
|
10
11
|
function diagnostic(error) {
|
|
11
12
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -161,107 +162,6 @@ function failure(error) {
|
|
|
161
162
|
}
|
|
162
163
|
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
163
164
|
}
|
|
164
|
-
async function execute(input, request) {
|
|
165
|
-
if (input.upstream === undefined)
|
|
166
|
-
throw new Error("upstream execution port is unavailable");
|
|
167
|
-
if (request.action === "akuma.wait") {
|
|
168
|
-
const result = await input.upstream.wait({
|
|
169
|
-
targets: request.targets,
|
|
170
|
-
completion: request.completion,
|
|
171
|
-
...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
|
|
172
|
-
signal: input.signal,
|
|
173
|
-
});
|
|
174
|
-
return { result, service: { action: request.action } };
|
|
175
|
-
}
|
|
176
|
-
if (request.action === "akuma.tell") {
|
|
177
|
-
const result = await input.upstream.tell({
|
|
178
|
-
target: request.target,
|
|
179
|
-
body: request.body,
|
|
180
|
-
tellId: request.id,
|
|
181
|
-
recordedAt: request.admittedAt,
|
|
182
|
-
signal: input.signal,
|
|
183
|
-
});
|
|
184
|
-
return {
|
|
185
|
-
result,
|
|
186
|
-
service: { action: request.action, target: request.target, tellId: request.id },
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
if (request.action === "akuma.kill") {
|
|
190
|
-
const served = await input.upstream.kill({
|
|
191
|
-
targets: request.targets,
|
|
192
|
-
signal: input.signal,
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
result: served.result,
|
|
196
|
-
service: { action: request.action, results: served.service },
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (request.action === "contract.deliver") {
|
|
200
|
-
return await executeDeliver(input, request);
|
|
201
|
-
}
|
|
202
|
-
if (request.action === "contract.review") {
|
|
203
|
-
return await executeReview(input, request);
|
|
204
|
-
}
|
|
205
|
-
const task = request;
|
|
206
|
-
const result = await input.upstream.task({
|
|
207
|
-
world: task.world,
|
|
208
|
-
request: task.request,
|
|
209
|
-
requester: task.requester,
|
|
210
|
-
signal: input.signal,
|
|
211
|
-
});
|
|
212
|
-
return { result, service: { action: task.action } };
|
|
213
|
-
}
|
|
214
|
-
async function executeDeliver(input, request) {
|
|
215
|
-
if (input.upstream === undefined)
|
|
216
|
-
throw new Error("upstream execution port is unavailable");
|
|
217
|
-
const served = await input.upstream.deliver({
|
|
218
|
-
repoRoot: request.repoRoot,
|
|
219
|
-
contractId: request.contractId,
|
|
220
|
-
...(request.message === undefined ? {} : { message: request.message }),
|
|
221
|
-
includeDirty: request.includeDirty,
|
|
222
|
-
materializeConflict: request.materializeConflict,
|
|
223
|
-
requester: request.requester,
|
|
224
|
-
signal: input.signal,
|
|
225
|
-
});
|
|
226
|
-
return {
|
|
227
|
-
result: served.result,
|
|
228
|
-
...(served.deliveryFactId === undefined
|
|
229
|
-
? {}
|
|
230
|
-
: {
|
|
231
|
-
service: {
|
|
232
|
-
action: request.action,
|
|
233
|
-
repoRoot: request.repoRoot,
|
|
234
|
-
contractId: request.contractId,
|
|
235
|
-
deliveryFactId: served.deliveryFactId,
|
|
236
|
-
},
|
|
237
|
-
}),
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
async function executeReview(input, request) {
|
|
241
|
-
if (input.upstream === undefined)
|
|
242
|
-
throw new Error("upstream execution port is unavailable");
|
|
243
|
-
const served = await input.upstream.review({
|
|
244
|
-
repoRoot: request.repoRoot,
|
|
245
|
-
contractId: request.contractId,
|
|
246
|
-
verdict: request.verdict,
|
|
247
|
-
...(request.summary === undefined ? {} : { summary: request.summary }),
|
|
248
|
-
requester: request.requester,
|
|
249
|
-
signal: input.signal,
|
|
250
|
-
});
|
|
251
|
-
return {
|
|
252
|
-
result: served.result,
|
|
253
|
-
...(served.reviewFactId === undefined
|
|
254
|
-
? {}
|
|
255
|
-
: {
|
|
256
|
-
service: {
|
|
257
|
-
action: request.action,
|
|
258
|
-
repoRoot: request.repoRoot,
|
|
259
|
-
contractId: request.contractId,
|
|
260
|
-
reviewFactId: served.reviewFactId,
|
|
261
|
-
},
|
|
262
|
-
}),
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
165
|
function pendingService(request) {
|
|
266
166
|
if (request.action === "akuma.wait")
|
|
267
167
|
return { action: request.action };
|
|
@@ -303,7 +203,7 @@ async function serveUpstream(input) {
|
|
|
303
203
|
let outcome;
|
|
304
204
|
let service = pendingService(request);
|
|
305
205
|
try {
|
|
306
|
-
const served = await
|
|
206
|
+
const served = await executeRequest(input, request);
|
|
307
207
|
outcome = { kind: "returned", result: served.result };
|
|
308
208
|
if (served.service !== undefined)
|
|
309
209
|
service = served.service;
|
|
@@ -1,31 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ResumeCoordinate, type Soul, type TellFact } from "./heart/index.js";
|
|
2
2
|
import type { AkumaPaths } from "./identity.js";
|
|
3
3
|
import { type ProviderAdapter, type TurnResult } from "./provider.js";
|
|
4
4
|
import { type RequestChildLaunch, type UpstreamExecutionPort } from "./request-serve.js";
|
|
5
|
-
|
|
6
|
-
export declare class BodySupervisor {
|
|
7
|
-
private readonly paths;
|
|
8
|
-
private readonly bodySequence;
|
|
9
|
-
private readonly leash;
|
|
10
|
-
readonly signal: AbortSignal;
|
|
11
|
-
private readonly controller;
|
|
12
|
-
private readonly finished;
|
|
13
|
-
private readonly observer;
|
|
14
|
-
private waiter;
|
|
15
|
-
private observation;
|
|
16
|
-
private stopping?;
|
|
17
|
-
private constructor();
|
|
18
|
-
static open(paths: AkumaPaths, bodySequence: number, leash: HeldAkumaLeash): Promise<BodySupervisor>;
|
|
19
|
-
get reason(): "control" | "heart-gone" | undefined;
|
|
20
|
-
current(): HeartSnapshot;
|
|
21
|
-
recordHung(diagnostic: string, at: string): Promise<void>;
|
|
22
|
-
refresh(): Promise<HeartSnapshot>;
|
|
23
|
-
next(after: HeartSnapshot): Promise<HeartSnapshot>;
|
|
24
|
-
cancel(reason: "control" | "heart-gone"): void;
|
|
25
|
-
close(): Promise<void>;
|
|
26
|
-
private publish;
|
|
27
|
-
private observe;
|
|
28
|
-
}
|
|
5
|
+
import { BodySupervisor } from "./body-supervisor.js";
|
|
29
6
|
export declare function turnRecipe(paths: AkumaPaths, soul: Soul): Promise<Readonly<{
|
|
30
7
|
cwd: string;
|
|
31
8
|
options: Soul["options"];
|
|
@@ -2,93 +2,7 @@ import { abortableDelay } from "./abort.js";
|
|
|
2
2
|
import { appendActivity, beginTurn, breakBody, heartExists, readHeart, recordSession, recordTellDeliveries, recordTellReceipt, } from "./heart/index.js";
|
|
3
3
|
import { encodeAgentEvent } from "./provider.js";
|
|
4
4
|
import { BodyRequestPump } from "./request-serve.js";
|
|
5
|
-
|
|
6
|
-
export const CONTROL_RESPONSE_MS = 1_000;
|
|
7
|
-
export class BodySupervisor {
|
|
8
|
-
paths;
|
|
9
|
-
bodySequence;
|
|
10
|
-
leash;
|
|
11
|
-
signal;
|
|
12
|
-
controller = new AbortController();
|
|
13
|
-
finished = new AbortController();
|
|
14
|
-
observer;
|
|
15
|
-
waiter;
|
|
16
|
-
observation;
|
|
17
|
-
stopping;
|
|
18
|
-
constructor(paths, bodySequence, leash, observation) {
|
|
19
|
-
this.paths = paths;
|
|
20
|
-
this.bodySequence = bodySequence;
|
|
21
|
-
this.leash = leash;
|
|
22
|
-
this.signal = this.controller.signal;
|
|
23
|
-
this.observation = observation;
|
|
24
|
-
this.observer = this.observe();
|
|
25
|
-
}
|
|
26
|
-
static async open(paths, bodySequence, leash) {
|
|
27
|
-
return new BodySupervisor(paths, bodySequence, leash, await readHeart(paths));
|
|
28
|
-
}
|
|
29
|
-
get reason() {
|
|
30
|
-
return this.stopping;
|
|
31
|
-
}
|
|
32
|
-
current() {
|
|
33
|
-
return this.observation;
|
|
34
|
-
}
|
|
35
|
-
async recordHung(diagnostic, at) {
|
|
36
|
-
await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
|
|
37
|
-
}
|
|
38
|
-
async refresh() {
|
|
39
|
-
this.publish(await readHeart(this.paths));
|
|
40
|
-
return this.observation;
|
|
41
|
-
}
|
|
42
|
-
next(after) {
|
|
43
|
-
if (this.observation !== after)
|
|
44
|
-
return Promise.resolve(this.observation);
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
this.waiter = resolve;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
cancel(reason) {
|
|
50
|
-
if (this.stopping !== undefined)
|
|
51
|
-
return;
|
|
52
|
-
this.stopping = reason;
|
|
53
|
-
this.controller.abort(new Error(reason === "control" ? "Akuma Body interrupted by durable control" : "Akuma Heart disappeared"));
|
|
54
|
-
this.waiter?.(this.observation);
|
|
55
|
-
this.waiter = undefined;
|
|
56
|
-
}
|
|
57
|
-
async close() {
|
|
58
|
-
this.finished.abort();
|
|
59
|
-
await this.observer;
|
|
60
|
-
this.waiter = undefined;
|
|
61
|
-
}
|
|
62
|
-
publish(snapshot) {
|
|
63
|
-
this.observation = snapshot;
|
|
64
|
-
this.waiter?.(snapshot);
|
|
65
|
-
this.waiter = undefined;
|
|
66
|
-
}
|
|
67
|
-
async observe() {
|
|
68
|
-
for (;;) {
|
|
69
|
-
const snapshot = this.observation;
|
|
70
|
-
if (!(await heartExists(this.paths)))
|
|
71
|
-
return this.cancel("heart-gone");
|
|
72
|
-
if (snapshot.stop?.bodySequence === this.bodySequence || snapshot.pause?.bodySequence === this.bodySequence) {
|
|
73
|
-
return this.cancel("control");
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
await abortableDelay(BODY_CONTROL_OBSERVATION_MS, this.finished.signal);
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await this.refresh();
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
if (await heartExists(this.paths))
|
|
86
|
-
throw error;
|
|
87
|
-
return this.cancel("heart-gone");
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
5
|
+
import { CONTROL_RESPONSE_MS } from "./body-supervisor.js";
|
|
92
6
|
function serializeEffects() {
|
|
93
7
|
let tail = Promise.resolve();
|
|
94
8
|
let failed = false;
|
|
@@ -132,11 +46,7 @@ async function failedTurnSetup(input, turnSequence, requests, cancelDrive, error
|
|
|
132
46
|
throw error;
|
|
133
47
|
return { kind: "stopped" };
|
|
134
48
|
}
|
|
135
|
-
return {
|
|
136
|
-
kind: "failed",
|
|
137
|
-
turnSequence,
|
|
138
|
-
diagnostic: error instanceof Error ? error.message : String(error),
|
|
139
|
-
};
|
|
49
|
+
return { kind: "failed", turnSequence, diagnostic: error instanceof Error ? error.message : String(error) };
|
|
140
50
|
}
|
|
141
51
|
async function writeProviderEvent(input, active, event) {
|
|
142
52
|
if (input.supervisor.signal.aborted)
|
|
@@ -151,11 +61,7 @@ async function writeProviderEvent(input, active, event) {
|
|
|
151
61
|
admittedAt: at,
|
|
152
62
|
});
|
|
153
63
|
}
|
|
154
|
-
await appendActivity(input.paths, {
|
|
155
|
-
turnSequence: active.turnSequence,
|
|
156
|
-
event: encodeAgentEvent(event),
|
|
157
|
-
at,
|
|
158
|
-
});
|
|
64
|
+
await appendActivity(input.paths, { turnSequence: active.turnSequence, event: encodeAgentEvent(event), at });
|
|
159
65
|
}
|
|
160
66
|
async function startTurnDrive(input) {
|
|
161
67
|
const { cwd, options, session } = await turnRecipe(input.paths, input.soul);
|
|
@@ -212,7 +118,7 @@ async function startTurnDrive(input) {
|
|
|
212
118
|
}
|
|
213
119
|
return { kind: "stopped" };
|
|
214
120
|
}
|
|
215
|
-
if (input.launchTells.length > 0)
|
|
121
|
+
if (input.launchTells.length > 0)
|
|
216
122
|
await recordTellDeliveries(input.paths, input.launchTells.map((tell) => ({
|
|
217
123
|
tellId: tell.id,
|
|
218
124
|
route: "launch",
|
|
@@ -220,7 +126,6 @@ async function startTurnDrive(input) {
|
|
|
220
126
|
fence: selected.admission.fence,
|
|
221
127
|
deliveredAt: input.now(),
|
|
222
128
|
})));
|
|
223
|
-
}
|
|
224
129
|
void selected.completion.then(() => requests.stopAdmission());
|
|
225
130
|
return {
|
|
226
131
|
turnSequence: turn.sequence,
|
|
@@ -60,11 +60,5 @@ export declare function isParsedAkumaCommand(command: Readonly<{
|
|
|
60
60
|
export declare function renderAkumaRootRows(): readonly string[];
|
|
61
61
|
export declare function renderAkumaHelp(action: AkumaAction): string;
|
|
62
62
|
export declare function renderAkumaUsage(action: AkumaAction): string;
|
|
63
|
-
export declare function parseAkumaCatalogPath(value: string): Readonly<{
|
|
64
|
-
kind: "archetypes";
|
|
65
|
-
} | {
|
|
66
|
-
kind: "akuma";
|
|
67
|
-
archetype?: string;
|
|
68
|
-
}> | null;
|
|
69
63
|
export declare function parseAkumaCommand(argv: readonly string[]): ParsedAkumaCommand;
|
|
70
64
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CliUsageError, isBlankInput, usageLine } from "../usage.js";
|
|
2
|
-
import {
|
|
2
|
+
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";
|
|
@@ -84,16 +84,6 @@ export function renderAkumaHelp(action) {
|
|
|
84
84
|
export function renderAkumaUsage(action) {
|
|
85
85
|
return usageLine(AKUMA_COMMAND_SPECS[action].usage);
|
|
86
86
|
}
|
|
87
|
-
export function parseAkumaCatalogPath(value) {
|
|
88
|
-
if (value === "aku" || value === "aku/")
|
|
89
|
-
return { kind: "archetypes" };
|
|
90
|
-
if (value === "aku/*/*")
|
|
91
|
-
return { kind: "akuma" };
|
|
92
|
-
const match = /^aku\/([^/]+)\/?$/u.exec(value);
|
|
93
|
-
if (match === null)
|
|
94
|
-
return null;
|
|
95
|
-
return { kind: "akuma", archetype: archetypeName(match[1]) };
|
|
96
|
-
}
|
|
97
87
|
function stringFlag(value, diagnostic, fail) {
|
|
98
88
|
if (typeof value !== "string")
|
|
99
89
|
fail(diagnostic);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { InvocationResult } from "../result.js";
|
|
2
|
+
import type { ParsedCommand } from "../parse.js";
|
|
3
|
+
import type { WorktreeHooks } from "../../library/configuration.js";
|
|
4
|
+
import type { Repo } from "../../library/repo.js";
|
|
5
|
+
import type { Settings } from "../../settings.js";
|
|
6
|
+
import type { WorldRoot } from "../../world.js";
|
|
7
|
+
type ContractMutation = Extract<ParsedCommand, {
|
|
8
|
+
command: "bind" | "amend" | "deliver" | "review" | "arc" | "abandon" | "audit";
|
|
9
|
+
}>;
|
|
10
|
+
type InvocationEdge = Readonly<{
|
|
11
|
+
environment: NodeJS.ProcessEnv;
|
|
12
|
+
readStdin: () => Promise<string>;
|
|
13
|
+
}>;
|
|
14
|
+
export type ContractMutationInput = Readonly<{
|
|
15
|
+
parsed: ContractMutation;
|
|
16
|
+
repo: Repo;
|
|
17
|
+
edge: InvocationEdge;
|
|
18
|
+
scope: string;
|
|
19
|
+
configuration?: Settings;
|
|
20
|
+
hooks: WorktreeHooks;
|
|
21
|
+
establishWorld: () => Promise<WorldRoot>;
|
|
22
|
+
forwarded?: true;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function invokeContractMutation(input: ContractMutationInput): Promise<InvocationResult>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { resolveActor } from "../actor.js";
|
|
2
|
+
import { CliUsageError } from "../usage.js";
|
|
3
|
+
function actorFromEdge(actor, environment) {
|
|
4
|
+
try {
|
|
5
|
+
return resolveActor({ env: environment, ...(actor === undefined ? {} : { actor }) });
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw new CliUsageError(error instanceof Error ? error.message : String(error));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function consumeSettings(run, ErrorType) {
|
|
12
|
+
try {
|
|
13
|
+
return run();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof ErrorType)
|
|
17
|
+
throw new CliUsageError(error.message);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function selectedGates(value, names) {
|
|
22
|
+
const { gatesFrom, SettingsError } = await import("../../library/configuration.js");
|
|
23
|
+
return consumeSettings(() => gatesFrom({ settings: value, ...(names === undefined ? {} : { names }) }), SettingsError);
|
|
24
|
+
}
|
|
25
|
+
async function selectedGitPolicy(value) {
|
|
26
|
+
const { requireBranchesToBeUpToDateFrom, SettingsError } = await import("../../library/configuration.js");
|
|
27
|
+
return consumeSettings(() => requireBranchesToBeUpToDateFrom({ settings: value }), SettingsError);
|
|
28
|
+
}
|
|
29
|
+
async function selectContract(repo, selector, scope) {
|
|
30
|
+
const { contractFromInput, resolveContextualContract } = await import("../selectors.js");
|
|
31
|
+
if (selector !== undefined && !selector.startsWith("@"))
|
|
32
|
+
return contractFromInput(repo, selector);
|
|
33
|
+
const { Keiyaku } = await import("../../library/keiyaku.js");
|
|
34
|
+
const id = resolveContextualContract(await Keiyaku.list({ repo }), selector, scope);
|
|
35
|
+
return contractFromInput(repo, id);
|
|
36
|
+
}
|
|
37
|
+
async function bindDraftReceipt(establishWorld, markdown) {
|
|
38
|
+
const { preserveBindDraft } = await import("../draft.js");
|
|
39
|
+
try {
|
|
40
|
+
return preserveBindDraft(await establishWorld(), markdown);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return { warning: `bind draft could not be preserved: ${error instanceof Error ? error.message : String(error)}` };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function invokeBind(input) {
|
|
47
|
+
const { parsed, repo, edge, configuration, hooks, establishWorld } = input;
|
|
48
|
+
if (parsed.command !== "bind")
|
|
49
|
+
throw new Error("bind invocation requires bind syntax");
|
|
50
|
+
const actor = actorFromEdge(parsed.actor, edge.environment);
|
|
51
|
+
const { bindFromCommand } = await import("./bind.js");
|
|
52
|
+
const { acceptedBind, resultFromMutationCall } = await import("../accepted.js");
|
|
53
|
+
if (parsed.forkOf !== undefined) {
|
|
54
|
+
return resultFromMutationCall("bind", () => bindFromCommand({ command: parsed, repo, ...(actor === undefined ? {} : { actor }), hooks }), (accepted) => {
|
|
55
|
+
const bound = accepted.facts.find((fact) => fact.kind === "bind");
|
|
56
|
+
if (bound === undefined || bound.kind !== "bind")
|
|
57
|
+
throw new Error("accepted bind is missing its bind fact");
|
|
58
|
+
return acceptedBind(accepted, bound.data.coordinates);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (configuration === undefined)
|
|
62
|
+
throw new Error("bind invocation requires Settings");
|
|
63
|
+
const markdown = await edge.readStdin();
|
|
64
|
+
const gates = await selectedGates(configuration, parsed.gates);
|
|
65
|
+
try {
|
|
66
|
+
const result = await resultFromMutationCall("bind", () => bindFromCommand({
|
|
67
|
+
command: parsed,
|
|
68
|
+
repo,
|
|
69
|
+
markdown,
|
|
70
|
+
gates,
|
|
71
|
+
...(actor === undefined ? {} : { actor }),
|
|
72
|
+
hooks,
|
|
73
|
+
}), (accepted) => {
|
|
74
|
+
const bound = accepted.facts.find((fact) => fact.kind === "bind");
|
|
75
|
+
if (bound === undefined || bound.kind !== "bind")
|
|
76
|
+
throw new Error("accepted bind is missing its bind fact");
|
|
77
|
+
return acceptedBind(accepted, bound.data.coordinates);
|
|
78
|
+
});
|
|
79
|
+
if (result.kind !== "refused")
|
|
80
|
+
return result;
|
|
81
|
+
return { ...result, draft: await bindDraftReceipt(establishWorld, markdown) };
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (!(error instanceof TypeError))
|
|
85
|
+
throw error;
|
|
86
|
+
const { BindDraftError } = await import("../draft.js");
|
|
87
|
+
throw new BindDraftError(error, await bindDraftReceipt(establishWorld, markdown));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function deliverRefusal(refusal) {
|
|
91
|
+
if (typeof refusal !== "object" || refusal === null || !("kind" in refusal) || refusal.kind !== "dirty-workspace") {
|
|
92
|
+
return refusal;
|
|
93
|
+
}
|
|
94
|
+
const submodules = "submodules" in refusal && Array.isArray(refusal.submodules) ? refusal.submodules : [];
|
|
95
|
+
return { ...refusal, option: { flag: "--include-dirty", available: submodules.length === 0 } };
|
|
96
|
+
}
|
|
97
|
+
async function existingSeat(input, parsed) {
|
|
98
|
+
const { repo, edge, scope, hooks } = input;
|
|
99
|
+
const { id, contract } = await selectContract(repo, parsed.contract, scope);
|
|
100
|
+
const actor = actorFromEdge(parsed.actor, edge.environment);
|
|
101
|
+
return { contract, id, ...(actor === undefined ? {} : { actor }), hooks };
|
|
102
|
+
}
|
|
103
|
+
async function invokeDeliver(parsed, seat, requireBranchesToBeUpToDate) {
|
|
104
|
+
const { acceptedDeliver } = await import("../accepted.js");
|
|
105
|
+
try {
|
|
106
|
+
const delivered = await seat.contract.deliver({
|
|
107
|
+
...(seat.actor === undefined ? {} : { actor: seat.actor }),
|
|
108
|
+
...(parsed.message === undefined ? {} : { message: parsed.message }),
|
|
109
|
+
requireBranchesToBeUpToDate,
|
|
110
|
+
includeDirty: parsed.includeDirty,
|
|
111
|
+
materializeConflict: parsed.materializeConflict,
|
|
112
|
+
hooks: seat.hooks,
|
|
113
|
+
});
|
|
114
|
+
if (!("facts" in delivered))
|
|
115
|
+
return delivered;
|
|
116
|
+
return acceptedDeliver(delivered, seat.id);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const { KeiyakuRefused, KeiyakuRetry } = await import("../../library/keiyaku.js");
|
|
120
|
+
if (error instanceof KeiyakuRefused) {
|
|
121
|
+
return { kind: "refused", verb: "deliver", contract: seat.id, refusal: deliverRefusal(error.refusal) };
|
|
122
|
+
}
|
|
123
|
+
if (error instanceof KeiyakuRetry)
|
|
124
|
+
return { kind: "retry", verb: "deliver", contract: seat.id, detail: error.reason };
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function invokeReview(parsed, seat, readStdin) {
|
|
129
|
+
const summary = parsed.summaryFromStdin === true ? await readStdin() : parsed.summary;
|
|
130
|
+
const { acceptedReview, resultFromMutationCall } = await import("../accepted.js");
|
|
131
|
+
return resultFromMutationCall("review", () => seat.contract.review({
|
|
132
|
+
verdict: parsed.verdict,
|
|
133
|
+
...(seat.actor === undefined ? {} : { actor: seat.actor }),
|
|
134
|
+
...(summary === undefined ? {} : { summary }),
|
|
135
|
+
hooks: seat.hooks,
|
|
136
|
+
}), (result) => acceptedReview(result, seat.id), { coordinate: seat.id });
|
|
137
|
+
}
|
|
138
|
+
export async function invokeContractMutation(input) {
|
|
139
|
+
const { parsed, repo, edge, configuration, hooks, forwarded } = input;
|
|
140
|
+
if (parsed.command === "bind")
|
|
141
|
+
return invokeBind(input);
|
|
142
|
+
const seat = await existingSeat(input, parsed);
|
|
143
|
+
const { id, contract, actor } = seat;
|
|
144
|
+
switch (parsed.command) {
|
|
145
|
+
case "amend": {
|
|
146
|
+
if (configuration === undefined)
|
|
147
|
+
throw new Error("amend invocation requires Settings");
|
|
148
|
+
const markdown = parsed.stdin === true ? await edge.readStdin() : undefined;
|
|
149
|
+
const gates = parsed.gates === undefined ? undefined : await selectedGates(configuration, parsed.gates);
|
|
150
|
+
const { amendFromCommand } = await import("./amend.js");
|
|
151
|
+
const { acceptedAmend, resultFromMutationCall } = await import("../accepted.js");
|
|
152
|
+
return resultFromMutationCall("amend", () => amendFromCommand({
|
|
153
|
+
command: parsed,
|
|
154
|
+
repo,
|
|
155
|
+
contract,
|
|
156
|
+
...(markdown === undefined ? {} : { markdown }),
|
|
157
|
+
gates,
|
|
158
|
+
...(actor === undefined ? {} : { actor }),
|
|
159
|
+
hooks,
|
|
160
|
+
}), (result) => acceptedAmend(result, id), { coordinate: id });
|
|
161
|
+
}
|
|
162
|
+
case "deliver":
|
|
163
|
+
return invokeDeliver(parsed, seat, forwarded === true ? false : await selectedGitPolicy(configuration));
|
|
164
|
+
case "review":
|
|
165
|
+
return invokeReview(parsed, seat, edge.readStdin);
|
|
166
|
+
case "arc": {
|
|
167
|
+
const markdown = await edge.readStdin();
|
|
168
|
+
const { acceptedArc, resultFromMutationCall } = await import("../accepted.js");
|
|
169
|
+
return resultFromMutationCall("arc", () => contract.arc({ markdown, ...(actor === undefined ? {} : { actor }), hooks }), (result) => acceptedArc(result, id), { coordinate: id });
|
|
170
|
+
}
|
|
171
|
+
case "abandon": {
|
|
172
|
+
const { acceptedAbandon, resultFromMutationCall } = await import("../accepted.js");
|
|
173
|
+
return resultFromMutationCall("abandon", () => contract.abandon({
|
|
174
|
+
...(actor === undefined ? {} : { actor }),
|
|
175
|
+
...(parsed.note === undefined ? {} : { note: parsed.note }),
|
|
176
|
+
hooks,
|
|
177
|
+
}), (result) => acceptedAbandon(result, id), { coordinate: id });
|
|
178
|
+
}
|
|
179
|
+
case "audit": {
|
|
180
|
+
if (configuration === undefined)
|
|
181
|
+
throw new Error("audit invocation requires Settings");
|
|
182
|
+
const requireBranchesToBeUpToDate = await selectedGitPolicy(configuration);
|
|
183
|
+
const { acceptedAudit, resultFromMutationCall } = await import("../accepted.js");
|
|
184
|
+
return resultFromMutationCall("audit", () => contract.audit({
|
|
185
|
+
...(actor === undefined ? {} : { actor }),
|
|
186
|
+
includeDirty: parsed.includeDirty,
|
|
187
|
+
showDiff: parsed.showDiff,
|
|
188
|
+
requireBranchesToBeUpToDate,
|
|
189
|
+
hooks,
|
|
190
|
+
}), (result) => acceptedAudit(result, id), { coordinate: id });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -252,4 +252,16 @@ export declare const CONTRACT_COMMAND_SPECS: {
|
|
|
252
252
|
};
|
|
253
253
|
};
|
|
254
254
|
export type ContractCommand = keyof typeof CONTRACT_COMMAND_SPECS;
|
|
255
|
+
export type ParsedContractParts = Readonly<{
|
|
256
|
+
command: ContractCommand;
|
|
257
|
+
flags: Readonly<Record<string, string | true | readonly string[]>>;
|
|
258
|
+
positionals: readonly string[];
|
|
259
|
+
stdin: boolean;
|
|
260
|
+
output: "text" | "json";
|
|
261
|
+
actor?: string;
|
|
262
|
+
}>;
|
|
263
|
+
export type ParsedContractCommand = ParsedBind | ParsedAmend | ParsedDeliver | ParsedReview | ParsedArc | ParsedAbandon | ParsedStatus | ParsedShow | ParsedLs | ParsedAudit | ParsedReconcile | ParsedNuke | ParsedSettings | ParsedRegion;
|
|
264
|
+
export declare function renderContractHelp(command: ContractCommand): string;
|
|
265
|
+
export declare function renderContractUsage(command: ContractCommand): string;
|
|
266
|
+
export declare function parseContractCommand(parts: ParsedContractParts): ParsedContractCommand;
|
|
255
267
|
export {};
|