cloving 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -15
- package/dist/src/broker/actor/authority-health.d.ts +9 -0
- package/dist/src/broker/actor/authority-health.js +24 -0
- package/dist/src/broker/actor/authority-runtime.d.ts +4 -2
- package/dist/src/broker/actor/authority-runtime.js +29 -20
- package/dist/src/broker/actor/authority-session.d.ts +6 -19
- package/dist/src/broker/actor/authority-session.js +66 -88
- package/dist/src/broker/actor/authority-settings.d.ts +2 -1
- package/dist/src/broker/actor/authority-settings.js +4 -1
- package/dist/src/broker/actor/authority-upgrades.d.ts +5 -1
- package/dist/src/broker/actor/authority-upgrades.js +2 -1
- package/dist/src/broker/actor/authority-watchdog.d.ts +6 -2
- package/dist/src/broker/actor/authority-watchdog.js +31 -9
- package/dist/src/broker/actor/authority.js +3 -1
- package/dist/src/broker/actor/projection-memory.d.ts +43 -0
- package/dist/src/broker/actor/projection-memory.js +207 -0
- package/dist/src/broker/actor/runtime-parts.d.ts +5 -0
- package/dist/src/broker/actor/runtime-parts.js +8 -3
- package/dist/src/broker/authority-restore.js +1 -1
- package/dist/src/broker/effect-upgrade.d.ts +1 -0
- package/dist/src/broker/effect-upgrade.js +4 -1
- package/dist/src/cli/arguments.js +1 -0
- package/dist/src/cli/client.d.ts +3 -1
- package/dist/src/cli/client.js +4 -4
- package/dist/src/cli/command-requests.d.ts +30 -1
- package/dist/src/cli/command-requests.js +43 -2
- package/dist/src/cli/commands.js +57 -48
- package/dist/src/cli/config-notice.js +5 -2
- package/dist/src/cli/doctor-retention.d.ts +2 -0
- package/dist/src/cli/doctor-retention.js +93 -0
- package/dist/src/cli/doctor.js +5 -0
- package/dist/src/cli/init-prompt.js +1 -1
- package/dist/src/cli/mcp-server.js +4 -4
- package/dist/src/cli/mcp-tools.d.ts +14 -14
- package/dist/src/cli/mcp-tools.js +212 -50
- package/dist/src/cli/queue-arguments.d.ts +15 -0
- package/dist/src/cli/queue-arguments.js +31 -0
- package/dist/src/cli/queue-render.d.ts +3 -0
- package/dist/src/cli/queue-render.js +26 -0
- package/dist/src/cli/reload-render.d.ts +2 -0
- package/dist/src/cli/reload-render.js +12 -0
- package/dist/src/cli/render.js +37 -8
- package/dist/src/cli/report-github-reader.js +1 -1
- package/dist/src/cli/runtime-arguments.d.ts +17 -3
- package/dist/src/cli/runtime-arguments.js +23 -19
- package/dist/src/cli/toolchain.d.ts +1 -0
- package/dist/src/cli/toolchain.js +1 -0
- package/dist/src/cli/usage.d.ts +2 -2
- package/dist/src/cli/usage.js +40 -5
- package/dist/src/cli/watch-api-document.d.ts +430 -0
- package/dist/src/cli/watch-api-document.js +291 -0
- package/dist/src/cli/watch-api-run.d.ts +56 -0
- package/dist/src/cli/watch-api-run.js +112 -0
- package/dist/src/cli/watch-api.d.ts +17 -0
- package/dist/src/cli/watch-api.js +230 -0
- package/dist/src/cli/watch-header-view.d.ts +1 -0
- package/dist/src/cli/watch-header-view.js +9 -1
- package/dist/src/cli/watch-model.d.ts +1 -0
- package/dist/src/cli/watch-model.js +3 -0
- package/dist/src/cli/watch-page.js +3 -1
- package/dist/src/cli/watch-reads.d.ts +1 -0
- package/dist/src/cli/watch-reads.js +6 -5
- package/dist/src/cli/watch-render.d.ts +1 -0
- package/dist/src/cli/watch-server.js +18 -4
- package/dist/src/cli.js +50 -43
- package/dist/src/configuration-display.js +2 -0
- package/dist/src/configuration-schema.d.ts +5 -0
- package/dist/src/daemon/claude-recovery.js +13 -9
- package/dist/src/daemon/claude-worker.d.ts +3 -3
- package/dist/src/daemon/claude-worker.js +10 -11
- package/dist/src/daemon/daemon-adapters.d.ts +7 -0
- package/dist/src/daemon/daemon-adapters.js +7 -3
- package/dist/src/daemon/daemon-loops.d.ts +2 -0
- package/dist/src/daemon/daemon-loops.js +17 -11
- package/dist/src/daemon/daemon.js +71 -60
- package/dist/src/daemon/github-attach.js +77 -4
- package/dist/src/daemon/github-calls.js +1 -1
- package/dist/src/daemon/github-methods.js +6 -2
- package/dist/src/daemon/issue-queue.d.ts +3 -0
- package/dist/src/daemon/issue-queue.js +66 -73
- package/dist/src/daemon/merge-turns.d.ts +10 -0
- package/dist/src/daemon/merge-turns.js +37 -0
- package/dist/src/daemon/queue-control.d.ts +13 -0
- package/dist/src/daemon/queue-control.js +86 -0
- package/dist/src/daemon/queue-journal.d.ts +20 -0
- package/dist/src/daemon/queue-journal.js +89 -0
- package/dist/src/daemon/record-archive.d.ts +19 -0
- package/dist/src/daemon/record-archive.js +82 -0
- package/dist/src/daemon/record-sweep.d.ts +24 -0
- package/dist/src/daemon/record-sweep.js +147 -0
- package/dist/src/daemon/retrying-turn.js +4 -4
- package/dist/src/daemon/settings-reload.d.ts +26 -0
- package/dist/src/daemon/settings-reload.js +82 -0
- package/dist/src/daemon/turn-details.d.ts +4 -0
- package/dist/src/daemon/turn-details.js +33 -0
- package/dist/src/daemon/worker-profile.d.ts +1 -0
- package/dist/src/daemon/worker-profile.js +4 -0
- package/dist/src/daemon/workflow-generations.d.ts +7 -4
- package/dist/src/daemon/workflow-generations.js +22 -14
- package/dist/src/daemon/workflow-reload.d.ts +41 -0
- package/dist/src/daemon/workflow-reload.js +244 -0
- package/dist/src/daemon/workflow-replay.d.ts +2 -1
- package/dist/src/daemon/workflow-replay.js +7 -3
- package/dist/src/doctor-report.d.ts +2 -2
- package/dist/src/doctor-report.js +2 -0
- package/dist/src/private-config.schema.json +31 -0
- package/dist/src/project-settings.d.ts +15 -1
- package/dist/src/project-settings.js +33 -1
- package/dist/src/queue-state.d.ts +26 -0
- package/dist/src/queue-state.js +34 -0
- package/dist/src/record-retention.d.ts +46 -0
- package/dist/src/record-retention.js +81 -0
- package/dist/src/service/active-tickets.js +5 -0
- package/dist/src/service/dispatcher.d.ts +4 -0
- package/dist/src/service/dispatcher.js +33 -0
- package/dist/src/service/queue-handlers.d.ts +4 -0
- package/dist/src/service/queue-handlers.js +32 -0
- package/dist/src/service/registry.d.ts +28 -2
- package/dist/src/service/registry.js +48 -0
- package/dist/src/service/replies.d.ts +16 -9
- package/dist/src/service/replies.js +5 -2
- package/dist/src/service/run-handlers.js +15 -10
- package/dist/src/service/service.d.ts +7 -3
- package/dist/src/service/service.js +16 -31
- package/dist/src/service/worker-totals.d.ts +4 -0
- package/dist/src/service/worker-totals.js +7 -0
- package/dist/src/service/workflow-reload.d.ts +35 -0
- package/dist/src/service/workflow-reload.js +52 -0
- package/dist/src/storage/decision-snapshots.d.ts +1 -2
- package/dist/src/storage/decision-snapshots.js +1 -7
- package/dist/src/storage/domain-changes.d.ts +1 -1
- package/dist/src/storage/domain-changes.js +1 -0
- package/dist/src/storage/domain-schema.d.ts +4 -1
- package/dist/src/storage/domain-schema.js +6 -2
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +4 -0
- package/dist/src/storage/projection-changes.d.ts +12 -0
- package/dist/src/storage/projection-changes.js +194 -0
- package/dist/src/storage/projection-lookups.d.ts +7 -0
- package/dist/src/storage/projection-lookups.js +30 -0
- package/dist/src/workflow-declaration.d.ts +1 -0
- package/dist/src/workflow-declaration.js +3 -0
- package/dist/tools/compile-workflows.js +3 -0
- package/dist/tools/project-configuration.d.ts +7 -2
- package/dist/tools/project-configuration.js +30 -1
- package/dist/tools/self-host-processes.js +37 -13
- package/dist/tools/self-host-project.d.ts +2 -0
- package/dist/tools/self-host-project.js +21 -3
- package/dist/tools/self-host.js +34 -24
- package/dist/tools/workflow-candidate-program.d.ts +4 -2
- package/dist/tools/workflow-candidate-program.js +30 -11
- package/dist/tools/workflow-candidate-source.d.ts +1 -0
- package/dist/tools/workflow-candidate-source.js +11 -3
- package/package.json +2 -2
- package/templates/develop/README.md +81 -4
- package/templates/develop/develop/flow-tests.ts +1 -1
- package/templates/develop/develop/interruption-tests.ts +157 -0
- package/templates/develop/develop/local.ts +1 -0
- package/templates/develop/develop/option-tests.ts +138 -9
- package/templates/develop/develop/red-green.ts +55 -0
- package/templates/develop/develop/report.ts +31 -11
- package/templates/develop/develop/round.ts +1 -0
- package/templates/develop/develop/scenarios.ts +27 -0
- package/templates/develop/develop/turn.ts +26 -6
- package/templates/develop/develop.declaration.json +1 -1
- package/templates/develop/develop.test.ts +4 -0
- package/templates/develop/template.json +29 -2
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { eligibleIntents } from "../../authority/reducer.js";
|
|
2
|
+
import { loadProjections, } from "../../storage/decision-snapshots.js";
|
|
3
|
+
import { messageOutcomes, recordedTimes, runTimes, storedWorkflows, submittedWorkflows, } from "../../storage/projection-lookups.js";
|
|
4
|
+
import { changedProjection, followingSequence, projectedChange, } from "../../storage/projection-changes.js";
|
|
5
|
+
import { currentState, intentKind, } from "../authority-restore.js";
|
|
6
|
+
const pendingLimit = 1_000;
|
|
7
|
+
export function projectionMemory(options) {
|
|
8
|
+
const memory = {
|
|
9
|
+
held: undefined,
|
|
10
|
+
stale: null,
|
|
11
|
+
recorded: new Map(),
|
|
12
|
+
logger: options.logger,
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
record: ({ messageId, change }) => {
|
|
16
|
+
memory.recorded.delete(messageId);
|
|
17
|
+
memory.recorded.set(messageId, projectedChange(change));
|
|
18
|
+
if (memory.recorded.size <= pendingLimit)
|
|
19
|
+
return;
|
|
20
|
+
memory.recorded.clear();
|
|
21
|
+
memory.held = undefined;
|
|
22
|
+
},
|
|
23
|
+
load: (database) => database.connection(async (connection) => {
|
|
24
|
+
memory.recorded.clear();
|
|
25
|
+
memory.held = await rebuilt(connection);
|
|
26
|
+
}),
|
|
27
|
+
read: (database) => database.connection(async (connection) => {
|
|
28
|
+
const held = await advanced(memory, connection);
|
|
29
|
+
return held.projection;
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async function advanced(memory, connection) {
|
|
34
|
+
const recorded = [...memory.recorded];
|
|
35
|
+
memory.recorded.clear();
|
|
36
|
+
try {
|
|
37
|
+
memory.held = await updated(memory, { recorded, connection });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
memory.held = undefined;
|
|
41
|
+
memory.stale = `update failed: ${String(Reflect.get(Object(error), "message"))}`;
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
return memory.held;
|
|
45
|
+
}
|
|
46
|
+
async function updated(memory, update) {
|
|
47
|
+
const { recorded, connection } = update;
|
|
48
|
+
if (memory.held === undefined)
|
|
49
|
+
return rebuiltAfter(memory, connection);
|
|
50
|
+
if (recorded.length === 0)
|
|
51
|
+
return memory.held;
|
|
52
|
+
const outcomes = await messageOutcomes(connection, recorded.map(([messageId]) => messageId));
|
|
53
|
+
const committed = [];
|
|
54
|
+
let sequence = memory.held.snapshot.sequence;
|
|
55
|
+
for (const [messageId, change] of recorded) {
|
|
56
|
+
const outcome = outcomes.get(messageId) ?? "missing";
|
|
57
|
+
if (outcome === "rolled-back")
|
|
58
|
+
continue;
|
|
59
|
+
const reason = mismatch({ messageId, change, outcome, sequence });
|
|
60
|
+
if (reason !== null) {
|
|
61
|
+
memory.stale = reason;
|
|
62
|
+
return rebuiltAfter(memory, connection);
|
|
63
|
+
}
|
|
64
|
+
committed.push(change);
|
|
65
|
+
sequence = followingSequence(sequence, change);
|
|
66
|
+
}
|
|
67
|
+
return applied(memory.held, { changes: committed, connection });
|
|
68
|
+
}
|
|
69
|
+
async function rebuiltAfter(memory, connection) {
|
|
70
|
+
const held = await rebuilt(connection);
|
|
71
|
+
if (memory.stale !== null)
|
|
72
|
+
memory.logger.warn({
|
|
73
|
+
event: "authority-projection-rebuilt",
|
|
74
|
+
reason: memory.stale,
|
|
75
|
+
});
|
|
76
|
+
memory.stale = null;
|
|
77
|
+
return held;
|
|
78
|
+
}
|
|
79
|
+
function mismatch(recorded) {
|
|
80
|
+
const { messageId, change, outcome, sequence } = recorded;
|
|
81
|
+
if (outcome === "missing")
|
|
82
|
+
return `message ${messageId} is not stored`;
|
|
83
|
+
if (change.previous === sequence)
|
|
84
|
+
return null;
|
|
85
|
+
return `sequence ${String(change.previous)} does not follow ${String(sequence)}`;
|
|
86
|
+
}
|
|
87
|
+
async function rebuilt(connection) {
|
|
88
|
+
const snapshot = await loadProjections(connection);
|
|
89
|
+
return withProjection({
|
|
90
|
+
snapshot,
|
|
91
|
+
times: await runTimes(connection),
|
|
92
|
+
workflows: new Map([
|
|
93
|
+
...snapshot.details.flatMap((row) => row.submission === null ? [] : [[row.submission, null]]),
|
|
94
|
+
...(await submittedWorkflows(connection)),
|
|
95
|
+
]),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function applied(previous, input) {
|
|
99
|
+
const { changes } = input;
|
|
100
|
+
if (changes.length === 0)
|
|
101
|
+
return previous;
|
|
102
|
+
return withProjection({
|
|
103
|
+
snapshot: changedProjection(previous.snapshot, changes),
|
|
104
|
+
times: await changedTimes(previous.times, input),
|
|
105
|
+
workflows: await changedWorkflows(previous.workflows, input),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function changedTimes(previous, input) {
|
|
109
|
+
const runs = new Map(input.changes.flatMap((change) => change.runs.map((row) => [row.runId, row.sequence])));
|
|
110
|
+
const carried = new Map(input.changes.flatMap((change) => change.events.map((event) => [event.sequence, event.recordedAt])));
|
|
111
|
+
const stored = await recordedTimes(input.connection, [
|
|
112
|
+
...new Set([...runs.values()].filter((sequence) => !carried.has(sequence))),
|
|
113
|
+
]);
|
|
114
|
+
const times = new Map(previous);
|
|
115
|
+
for (const [runId, sequence] of runs) {
|
|
116
|
+
const time = carried.get(sequence) ?? stored.get(sequence);
|
|
117
|
+
if (time === undefined)
|
|
118
|
+
times.delete(runId);
|
|
119
|
+
else
|
|
120
|
+
times.set(runId, time);
|
|
121
|
+
}
|
|
122
|
+
return times;
|
|
123
|
+
}
|
|
124
|
+
async function changedWorkflows(previous, input) {
|
|
125
|
+
const submissions = [
|
|
126
|
+
...new Set(input.changes.flatMap((change) => change.details.flatMap((row) => row.submission === null || previous.has(row.submission)
|
|
127
|
+
? []
|
|
128
|
+
: [row.submission]))),
|
|
129
|
+
];
|
|
130
|
+
const stored = await storedWorkflows(input.connection, submissions);
|
|
131
|
+
return new Map([
|
|
132
|
+
...previous,
|
|
133
|
+
...submissions.map((submission) => [submission, stored.get(submission) ?? null]),
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
function withProjection(parts) {
|
|
137
|
+
return { ...parts, projection: projection(parts) };
|
|
138
|
+
}
|
|
139
|
+
function remembered(compute) {
|
|
140
|
+
let value;
|
|
141
|
+
return () => {
|
|
142
|
+
value ??= { current: compute() };
|
|
143
|
+
return value.current;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function projection(parts) {
|
|
147
|
+
const { snapshot, times, workflows } = parts;
|
|
148
|
+
const state = remembered(() => currentState(snapshot));
|
|
149
|
+
return {
|
|
150
|
+
snapshot,
|
|
151
|
+
state,
|
|
152
|
+
eligible: remembered(() => eligible({ snapshot, state: state() })),
|
|
153
|
+
operations: remembered(() => operationRecords(snapshot)),
|
|
154
|
+
sequences: remembered(() => sequences({ snapshot, times })),
|
|
155
|
+
workflowIds: (runIds) => workflowIds({ snapshot, workflows, runIds }),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function sequences(parts) {
|
|
159
|
+
const timed = parts.snapshot.runs.flatMap((row) => {
|
|
160
|
+
const time = parts.times.get(row.runId);
|
|
161
|
+
return time === undefined ? [] : [{ row, time }];
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
latest: parts.snapshot.sequence,
|
|
165
|
+
runs: Object.fromEntries(timed.map(({ row }) => [row.runId, row.sequence])),
|
|
166
|
+
times: Object.fromEntries(timed.map(({ row, time }) => [row.runId, time])),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function workflowIds(parts) {
|
|
170
|
+
const wanted = parts.runIds === undefined ? null : new Set(parts.runIds);
|
|
171
|
+
return Object.fromEntries(parts.snapshot.details.flatMap((row) => {
|
|
172
|
+
const workflowId = row.submission === null ? null : parts.workflows.get(row.submission);
|
|
173
|
+
if (typeof workflowId !== "string")
|
|
174
|
+
return [];
|
|
175
|
+
return wanted === null || wanted.has(row.runId)
|
|
176
|
+
? [[row.runId, workflowId]]
|
|
177
|
+
: [];
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
export function operationRecords(snapshot) {
|
|
181
|
+
const deliveries = Map.groupBy(snapshot.deliveries, (row) => row.operationId);
|
|
182
|
+
return snapshot.outbox.map((row) => ({
|
|
183
|
+
operationId: row.operationId,
|
|
184
|
+
kind: intentKind(row.kind),
|
|
185
|
+
runId: row.runId,
|
|
186
|
+
createdSequence: row.createdSequence,
|
|
187
|
+
releasedSequence: row.releasedSequence,
|
|
188
|
+
deliveries: (deliveries.get(row.operationId) ?? []).map(({ effectId, outcome, receipt, purpose, epoch }) => ({
|
|
189
|
+
effectId,
|
|
190
|
+
outcome,
|
|
191
|
+
receipt,
|
|
192
|
+
purpose,
|
|
193
|
+
epoch,
|
|
194
|
+
})),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
function eligible(parts) {
|
|
198
|
+
return eligibleIntents({
|
|
199
|
+
...parts.state,
|
|
200
|
+
effects: {
|
|
201
|
+
operations: Object.fromEntries(parts.snapshot.pending.map((operationId) => [
|
|
202
|
+
operationId,
|
|
203
|
+
{ status: "dispatch-pending" },
|
|
204
|
+
])),
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ActorClass, type Database, type InstrumentationEvent, type SolidObjectsConfiguration, type SolidObjectsRuntime } from "solid-objects";
|
|
2
|
+
import { type DomainChange } from "../../storage/domain-changes.ts";
|
|
2
3
|
import { type AuthorityTiming } from "./authority-settings.ts";
|
|
3
4
|
import type { StrandingWatchdog } from "./authority-watchdog.ts";
|
|
4
5
|
export interface FailedDelivery {
|
|
@@ -20,4 +21,8 @@ export declare function registeredRuntime(registration: {
|
|
|
20
21
|
readonly database: Database;
|
|
21
22
|
readonly actor: ActorClass;
|
|
22
23
|
readonly settings: Partial<SolidObjectsConfiguration>;
|
|
24
|
+
readonly record: (recorded: {
|
|
25
|
+
readonly messageId: string;
|
|
26
|
+
readonly change: DomainChange;
|
|
27
|
+
}) => void;
|
|
23
28
|
}): SolidObjectsRuntime;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRuntime, } from "solid-objects";
|
|
2
|
-
import { persistChange } from "../../storage/domain-changes.js";
|
|
2
|
+
import { persistChange, } from "../../storage/domain-changes.js";
|
|
3
3
|
import { persistAction } from "./authority-actor.js";
|
|
4
4
|
import { brokerAuthorization } from "./authority-policy.js";
|
|
5
5
|
import { librarySettings } from "./authority-settings.js";
|
|
@@ -26,9 +26,14 @@ export function runtimeSettings(parts) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
export function registeredRuntime(registration) {
|
|
29
|
-
const { database, actor, settings } = registration;
|
|
29
|
+
const { database, actor, settings, record } = registration;
|
|
30
30
|
const runtime = createRuntime({ database, ...settings });
|
|
31
31
|
runtime.register(actor);
|
|
32
|
-
runtime.registerCommitAction(persistAction, (change, context) =>
|
|
32
|
+
runtime.registerCommitAction(persistAction, async (change, context) => {
|
|
33
|
+
record({
|
|
34
|
+
messageId: context.messageId,
|
|
35
|
+
change: await persistChange(change, context.connection),
|
|
36
|
+
});
|
|
37
|
+
});
|
|
33
38
|
return runtime;
|
|
34
39
|
}
|
|
@@ -133,7 +133,7 @@ export function currentState(snapshot) {
|
|
|
133
133
|
return {
|
|
134
134
|
...projectedState(snapshot),
|
|
135
135
|
...decisionState(snapshot),
|
|
136
|
-
...commandWaitState(snapshot.waits
|
|
136
|
+
...commandWaitState(snapshot.waits),
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
export function projectedState(snapshot) {
|
|
@@ -3,3 +3,4 @@ import type { AuthorityState } from "../authority/reducer.ts";
|
|
|
3
3
|
export declare function upgradeEffects(database: Database, state: AuthorityState): Promise<void>;
|
|
4
4
|
export declare function upgradeSpendingIndexes(database: Database): Promise<void>;
|
|
5
5
|
export declare function upgradeEventIndexes(database: Database): Promise<void>;
|
|
6
|
+
export declare function upgradeRunIndexes(database: Database): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { currentVersion, effectedVersion, spendingVersion, upgradeSchema, } from "../storage/domain-schema.js";
|
|
1
|
+
import { currentVersion, effectedVersion, eventIndexVersion, spendingVersion, upgradeSchema, } from "../storage/domain-schema.js";
|
|
2
2
|
import { persistEffects } from "../storage/effect-changes.js";
|
|
3
3
|
import { effectRows } from "./effect-restore.js";
|
|
4
4
|
export async function upgradeEffects(database, state) {
|
|
@@ -11,5 +11,8 @@ export async function upgradeSpendingIndexes(database) {
|
|
|
11
11
|
await database.transaction((connection) => upgradeSchema(connection, spendingVersion));
|
|
12
12
|
}
|
|
13
13
|
export async function upgradeEventIndexes(database) {
|
|
14
|
+
await database.transaction((connection) => upgradeSchema(connection, eventIndexVersion));
|
|
15
|
+
}
|
|
16
|
+
export async function upgradeRunIndexes(database) {
|
|
14
17
|
await database.transaction((connection) => upgradeSchema(connection, currentVersion));
|
|
15
18
|
}
|
package/dist/src/cli/client.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ export interface ClientRequest extends ServiceRequest {
|
|
|
7
7
|
readonly signal?: AbortSignal;
|
|
8
8
|
}
|
|
9
9
|
export declare function send(runtimeDirectory: string, input: ClientRequest): Promise<Reply>;
|
|
10
|
-
export declare function listRuns(runtimeDirectory: string
|
|
10
|
+
export declare function listRuns(runtimeDirectory: string, selection?: {
|
|
11
|
+
readonly active?: true;
|
|
12
|
+
}): Promise<Reply>;
|
|
11
13
|
export declare function mutate(runtimeDirectory: string, mutation: {
|
|
12
14
|
readonly request: ServiceRequest;
|
|
13
15
|
readonly signal: Promise<void>;
|
package/dist/src/cli/client.js
CHANGED
|
@@ -2,7 +2,7 @@ import net from "node:net";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { socketAddress } from "../broker/socket-address.js";
|
|
4
4
|
import { parseReplyLine, record, requestLine, } from "../protocol.js";
|
|
5
|
-
const mutations = ["run", "decision", "run-command"];
|
|
5
|
+
const mutations = ["run", "decision", "run-command", "queue-command"];
|
|
6
6
|
function unavailable(runtimeDirectory) {
|
|
7
7
|
return {
|
|
8
8
|
kind: "error",
|
|
@@ -101,10 +101,10 @@ function listedRuns(result) {
|
|
|
101
101
|
? runs.map(record).filter((run) => run !== undefined)
|
|
102
102
|
: [];
|
|
103
103
|
}
|
|
104
|
-
export async function listRuns(runtimeDirectory) {
|
|
104
|
+
export async function listRuns(runtimeDirectory, selection = {}) {
|
|
105
105
|
const first = await send(runtimeDirectory, {
|
|
106
106
|
operation: "status",
|
|
107
|
-
parameters: { limit: 500 },
|
|
107
|
+
parameters: { ...selection, limit: 500 },
|
|
108
108
|
});
|
|
109
109
|
if (first.kind === "error")
|
|
110
110
|
return first;
|
|
@@ -123,7 +123,7 @@ export async function listRuns(runtimeDirectory) {
|
|
|
123
123
|
after = next;
|
|
124
124
|
const page = await send(runtimeDirectory, {
|
|
125
125
|
operation: "status",
|
|
126
|
-
parameters: { limit: 500, after },
|
|
126
|
+
parameters: { ...selection, limit: 500, after },
|
|
127
127
|
});
|
|
128
128
|
if (page.kind === "error")
|
|
129
129
|
return page;
|
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
import { type Fields, type Reply, type ServiceError, type ServiceRequest } from "../protocol.ts";
|
|
2
2
|
import { type ClientRequest } from "./client.ts";
|
|
3
|
+
import type { DecisionCommand, RunCommand, RunSource } from "./runtime-arguments.ts";
|
|
3
4
|
export interface ReadCommand {
|
|
4
|
-
readonly kind: "status" | "questions" | "events";
|
|
5
|
+
readonly kind: "status" | "questions" | "events" | "reload";
|
|
5
6
|
readonly runId?: string | null;
|
|
6
7
|
readonly limit?: number | null;
|
|
7
8
|
readonly after?: string | number | null;
|
|
9
|
+
readonly active?: boolean;
|
|
8
10
|
readonly follow?: boolean;
|
|
9
11
|
}
|
|
12
|
+
export type SubmittedSource = Exclude<RunSource, {
|
|
13
|
+
readonly kind: "input";
|
|
14
|
+
}> | {
|
|
15
|
+
readonly kind: "input";
|
|
16
|
+
readonly submission: Fields;
|
|
17
|
+
};
|
|
18
|
+
export declare function runRequest(run: {
|
|
19
|
+
readonly workflowId: string;
|
|
20
|
+
readonly source: SubmittedSource;
|
|
21
|
+
readonly commandId: string;
|
|
22
|
+
}): ServiceRequest;
|
|
23
|
+
export interface DecisionRequest {
|
|
24
|
+
readonly command: DecisionCommand;
|
|
25
|
+
readonly reference: string;
|
|
26
|
+
readonly text: string | null;
|
|
27
|
+
readonly expectedRevision: number;
|
|
28
|
+
readonly generation: number;
|
|
29
|
+
readonly subjectDigest: string;
|
|
30
|
+
readonly commandId: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function decisionRequest(decision: DecisionRequest): ServiceRequest;
|
|
33
|
+
export declare function runCommandRequest(request: {
|
|
34
|
+
readonly command: RunCommand;
|
|
35
|
+
readonly runId: string;
|
|
36
|
+
readonly expectedRevision: number;
|
|
37
|
+
readonly commandId: string;
|
|
38
|
+
}): ServiceRequest;
|
|
10
39
|
export declare function readCommandRequest(command: ReadCommand): ServiceRequest;
|
|
11
40
|
export declare function readCommandReply(runtimeDirectory: string, request: ClientRequest): Promise<Reply>;
|
|
12
41
|
export declare function errorObject(error: ServiceError, format?: {
|
|
@@ -2,11 +2,52 @@ import {} from "../protocol.js";
|
|
|
2
2
|
import { send } from "./client.js";
|
|
3
3
|
import { withCiReruns } from "./github-ci-progress.js";
|
|
4
4
|
import { statusJson } from "./render.js";
|
|
5
|
+
function submission(source) {
|
|
6
|
+
if (source.kind === "input")
|
|
7
|
+
return source.submission;
|
|
8
|
+
return source.kind === "prompt"
|
|
9
|
+
? { prompt: source.text }
|
|
10
|
+
: { ticket: source.id };
|
|
11
|
+
}
|
|
12
|
+
export function runRequest(run) {
|
|
13
|
+
return {
|
|
14
|
+
operation: "run",
|
|
15
|
+
parameters: {
|
|
16
|
+
workflowId: run.workflowId,
|
|
17
|
+
source: run.source.kind,
|
|
18
|
+
submission: submission(run.source),
|
|
19
|
+
commandId: run.commandId,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function decisionRequest(decision) {
|
|
24
|
+
return {
|
|
25
|
+
operation: "decision",
|
|
26
|
+
parameters: Object.fromEntries(Object.entries(decision).filter(([, value]) => value !== null)),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function runCommandRequest(request) {
|
|
30
|
+
return {
|
|
31
|
+
operation: "run-command",
|
|
32
|
+
parameters: {
|
|
33
|
+
command: request.command,
|
|
34
|
+
runId: request.runId,
|
|
35
|
+
expectedRevision: request.expectedRevision,
|
|
36
|
+
commandId: request.commandId,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
5
40
|
export function readCommandRequest(command) {
|
|
6
|
-
const { kind, runId, limit, after, follow } = command;
|
|
41
|
+
const { kind, runId, limit, after, active, follow } = command;
|
|
7
42
|
const parameters = kind === "questions"
|
|
8
43
|
? { runId }
|
|
9
|
-
: {
|
|
44
|
+
: {
|
|
45
|
+
runId,
|
|
46
|
+
limit,
|
|
47
|
+
after,
|
|
48
|
+
...(active === true ? { active } : {}),
|
|
49
|
+
...(follow === undefined ? {} : { follow }),
|
|
50
|
+
};
|
|
10
51
|
return {
|
|
11
52
|
operation: kind,
|
|
12
53
|
parameters: Object.fromEntries(Object.entries(parameters).filter(([, value]) => value !== null && value !== undefined)),
|
package/dist/src/cli/commands.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { commandJson, errorObject, readCommandReply, readCommandRequest, } from "./command-requests.js";
|
|
1
|
+
import { commandJson, decisionRequest, errorObject, readCommandReply, readCommandRequest, runCommandRequest, runRequest, } from "./command-requests.js";
|
|
2
2
|
import { reportCommand } from "./report.js";
|
|
3
3
|
import { scheduleServiceRequest } from "./schedule-arguments.js";
|
|
4
|
+
import { queueServiceRequest } from "./queue-arguments.js";
|
|
4
5
|
import { withCiReruns } from "./github-ci-progress.js";
|
|
5
6
|
import { readFile } from "node:fs/promises";
|
|
6
7
|
import { resolve } from "node:path";
|
|
@@ -55,7 +56,8 @@ function identified(environment, invocation) {
|
|
|
55
56
|
environment.terminal.error(`Command ID: ${commandId}\n`);
|
|
56
57
|
return { json, commandId, announced: !json };
|
|
57
58
|
}
|
|
58
|
-
async function inputFile(environment,
|
|
59
|
+
async function inputFile(environment, input) {
|
|
60
|
+
const { file, commandId } = input;
|
|
59
61
|
let text;
|
|
60
62
|
try {
|
|
61
63
|
text = await readFile(resolve(environment.workingDirectory, file), "utf8");
|
|
@@ -69,8 +71,9 @@ async function inputFile(environment, file) {
|
|
|
69
71
|
},
|
|
70
72
|
};
|
|
71
73
|
}
|
|
74
|
+
let submission;
|
|
72
75
|
try {
|
|
73
|
-
|
|
76
|
+
submission = record(JSON.parse(text));
|
|
74
77
|
}
|
|
75
78
|
catch {
|
|
76
79
|
return {
|
|
@@ -81,37 +84,37 @@ async function inputFile(environment, file) {
|
|
|
81
84
|
},
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if (submission === undefined)
|
|
88
|
+
return {
|
|
89
|
+
kind: "error",
|
|
90
|
+
error: {
|
|
91
|
+
commandId,
|
|
92
|
+
detail: "The submission is not an object.",
|
|
93
|
+
code: "invalid-submission",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
return { kind: "read", source: { kind: "input", submission } };
|
|
89
97
|
}
|
|
90
98
|
async function run(environment, invocation) {
|
|
91
99
|
const format = identified(environment, invocation);
|
|
92
|
-
const { commandId } = format;
|
|
93
100
|
const { source } = invocation;
|
|
94
101
|
const read = source.kind === "input"
|
|
95
|
-
? await inputFile(environment,
|
|
96
|
-
|
|
102
|
+
? await inputFile(environment, {
|
|
103
|
+
file: source.file,
|
|
104
|
+
commandId: format.commandId,
|
|
105
|
+
})
|
|
106
|
+
: { kind: "read", source };
|
|
97
107
|
if (read.kind === "error")
|
|
98
108
|
return failed(environment, { error: read.error, format });
|
|
99
109
|
return mutated(environment, {
|
|
100
110
|
format,
|
|
101
|
-
request: {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
submission: read.submission,
|
|
107
|
-
commandId,
|
|
108
|
-
},
|
|
109
|
-
},
|
|
111
|
+
request: runRequest({
|
|
112
|
+
workflowId: invocation.workflowId,
|
|
113
|
+
source: read.source,
|
|
114
|
+
commandId: format.commandId,
|
|
115
|
+
}),
|
|
110
116
|
});
|
|
111
117
|
}
|
|
112
|
-
function present(values) {
|
|
113
|
-
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== null));
|
|
114
|
-
}
|
|
115
118
|
async function summarized(environment) {
|
|
116
119
|
const pending = await send(environment.runtimeDirectory, {
|
|
117
120
|
operation: "questions",
|
|
@@ -121,11 +124,11 @@ async function summarized(environment) {
|
|
|
121
124
|
environment.terminal.out(pendingSummary(pending.result));
|
|
122
125
|
}
|
|
123
126
|
async function status(environment, invocation) {
|
|
124
|
-
const { runId, limit, after, json } = invocation;
|
|
127
|
+
const { runId, limit, after, active, json } = invocation;
|
|
125
128
|
if (!json && runId === null)
|
|
126
129
|
await summarized(environment);
|
|
127
130
|
if (!json && runId === null && limit === null && after === null) {
|
|
128
|
-
const reply = await withCiReruns(environment.runtimeDirectory, await listRuns(environment.runtimeDirectory));
|
|
131
|
+
const reply = await withCiReruns(environment.runtimeDirectory, await listRuns(environment.runtimeDirectory, active ? { active } : {}));
|
|
129
132
|
return presented(environment, {
|
|
130
133
|
format: { json },
|
|
131
134
|
reply: reply.kind === "result"
|
|
@@ -140,37 +143,29 @@ async function status(environment, invocation) {
|
|
|
140
143
|
}
|
|
141
144
|
function decision(environment, invocation) {
|
|
142
145
|
const format = identified(environment, invocation);
|
|
143
|
-
const { commandId } = format;
|
|
144
146
|
return mutated(environment, {
|
|
145
147
|
format,
|
|
146
|
-
request: {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
commandId,
|
|
156
|
-
}),
|
|
157
|
-
},
|
|
148
|
+
request: decisionRequest({
|
|
149
|
+
command: invocation.command,
|
|
150
|
+
reference: invocation.reference,
|
|
151
|
+
text: invocation.text,
|
|
152
|
+
expectedRevision: invocation.expectedRevision,
|
|
153
|
+
generation: invocation.generation,
|
|
154
|
+
subjectDigest: invocation.subjectDigest,
|
|
155
|
+
commandId: format.commandId,
|
|
156
|
+
}),
|
|
158
157
|
});
|
|
159
158
|
}
|
|
160
159
|
function runCommand(environment, invocation) {
|
|
161
160
|
const format = identified(environment, invocation);
|
|
162
|
-
const { commandId } = format;
|
|
163
161
|
return mutated(environment, {
|
|
164
162
|
format,
|
|
165
|
-
request: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
commandId,
|
|
172
|
-
},
|
|
173
|
-
},
|
|
163
|
+
request: runCommandRequest({
|
|
164
|
+
command: invocation.command,
|
|
165
|
+
runId: invocation.runId,
|
|
166
|
+
expectedRevision: invocation.expectedRevision,
|
|
167
|
+
commandId: format.commandId,
|
|
168
|
+
}),
|
|
174
169
|
});
|
|
175
170
|
}
|
|
176
171
|
async function events(environment, invocation) {
|
|
@@ -216,6 +211,8 @@ function located(invocation, environment) {
|
|
|
216
211
|
format: { json: invocation.json },
|
|
217
212
|
request: scheduleServiceRequest(invocation),
|
|
218
213
|
});
|
|
214
|
+
if (invocation.kind === "queue")
|
|
215
|
+
return queue(environment, invocation);
|
|
219
216
|
if (invocation.kind === "report")
|
|
220
217
|
return reportCommand(invocation, environment);
|
|
221
218
|
if (invocation.kind === "run")
|
|
@@ -235,6 +232,18 @@ function located(invocation, environment) {
|
|
|
235
232
|
request: readCommandRequest(invocation),
|
|
236
233
|
});
|
|
237
234
|
}
|
|
235
|
+
function queue(environment, invocation) {
|
|
236
|
+
if (invocation.action === "show")
|
|
237
|
+
return requested(environment, {
|
|
238
|
+
format: { json: invocation.json },
|
|
239
|
+
request: { operation: "queue", parameters: {} },
|
|
240
|
+
});
|
|
241
|
+
const format = identified(environment, invocation);
|
|
242
|
+
return mutated(environment, {
|
|
243
|
+
format,
|
|
244
|
+
request: queueServiceRequest(invocation, format.commandId),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
238
247
|
function watch(environment, invocation) {
|
|
239
248
|
return watchServer({
|
|
240
249
|
actions: environment.watchActions ?? "on",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { socketAddress } from "../broker/socket-address.js";
|
|
4
|
+
import { reloadedSettingFields } from "../project-settings.js";
|
|
4
5
|
import { runtimeDirectory } from "./runtime-directory.js";
|
|
5
6
|
function serviceDirectory(repository) {
|
|
6
7
|
try {
|
|
@@ -40,7 +41,9 @@ export async function configNotice(repository, input) {
|
|
|
40
41
|
const source = input.entry?.source;
|
|
41
42
|
const hidden = source === "environment" ||
|
|
42
43
|
(input.scope === "project" && source === "private");
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
if (hidden)
|
|
45
|
+
return `The ${source} source hides this file value. A restart keeps the value of that source.`;
|
|
46
|
+
return reloadedSettingFields.includes(String(input.entry?.field))
|
|
47
|
+
? "Run cloving reload to use the new value."
|
|
45
48
|
: "Restart the service to use the new value.";
|
|
46
49
|
}
|