@osolmaz/pi-workflows 0.16.2 → 0.16.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/README.md +41 -41
- package/dist/builtins/monitor.workflow.d.ts +1 -1
- package/dist/builtins/monitor.workflow.js +8 -8
- package/dist/builtins/pi-agent-group.js +3 -3
- package/dist/builtins/pi-agent-group.js.map +1 -1
- package/dist/channels/adapter-entry.js +14 -14
- package/dist/channels/adapter-entry.js.map +1 -1
- package/dist/client/client.d.ts +6 -6
- package/dist/client/client.js +27 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +4 -1
- package/dist/client/protocol.js +17 -11
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/resolver.d.ts +4 -4
- package/dist/client/view.d.ts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +32 -32
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +1 -1
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/remote-recorder-store.d.ts +1 -1
- package/dist/extension/remote-recorder-store.js +2 -2
- package/dist/extension/remote-recorder-store.js.map +1 -1
- package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
- package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
- package/dist/extension/resource-manager-command.js.map +1 -0
- package/dist/extension/session-delivery.d.ts +1 -1
- package/dist/extension/session-delivery.js +2 -2
- package/dist/extension/session-delivery.js.map +1 -1
- package/dist/extension/session-view.d.ts +1 -1
- package/dist/extension/session-view.js +1 -1
- package/dist/extension/session-view.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +1 -1
- package/dist/extension/workflow-message-coordinator.js +6 -6
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/conditions.d.ts +6 -0
- package/dist/{controllers → resource-managers}/conditions.js +3 -3
- package/dist/resource-managers/conditions.js.map +1 -0
- package/dist/resource-managers/definition.d.ts +6 -0
- package/dist/resource-managers/definition.js +45 -0
- package/dist/resource-managers/definition.js.map +1 -0
- package/dist/resource-managers/effects.d.ts +15 -0
- package/dist/{controllers → resource-managers}/effects.js +2 -2
- package/dist/resource-managers/effects.js.map +1 -0
- package/dist/resource-managers/errors.d.ts +12 -0
- package/dist/resource-managers/errors.js +25 -0
- package/dist/resource-managers/errors.js.map +1 -0
- package/dist/resource-managers/index.d.ts +12 -0
- package/dist/resource-managers/index.js +12 -0
- package/dist/resource-managers/index.js.map +1 -0
- package/dist/resource-managers/json.js.map +1 -0
- package/dist/resource-managers/loader.d.ts +23 -0
- package/dist/resource-managers/loader.js +75 -0
- package/dist/resource-managers/loader.js.map +1 -0
- package/dist/resource-managers/results.d.ts +5 -0
- package/dist/resource-managers/results.js.map +1 -0
- package/dist/resource-managers/runtime.d.ts +59 -0
- package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
- package/dist/resource-managers/runtime.js.map +1 -0
- package/dist/{controllers → resource-managers}/sqlite.d.ts +44 -44
- package/dist/{controllers → resource-managers}/sqlite.js +139 -130
- package/dist/resource-managers/sqlite.js.map +1 -0
- package/dist/{controllers → resource-managers}/store.d.ts +33 -33
- package/dist/{controllers → resource-managers}/store.js.map +1 -1
- package/dist/{controllers → resource-managers}/types.d.ts +41 -41
- package/dist/{controllers → resource-managers}/types.js.map +1 -1
- package/dist/resource-managers/workflows.d.ts +34 -0
- package/dist/{controllers → resource-managers}/workflows.js +15 -15
- package/dist/resource-managers/workflows.js.map +1 -0
- package/dist/server/channel-effects.js.map +1 -0
- package/dist/{host → server}/channel-supervisor.d.ts +6 -6
- package/dist/{host → server}/channel-supervisor.js +3 -3
- package/dist/server/channel-supervisor.js.map +1 -0
- package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
- package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
- package/dist/server/child-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/processes.d.ts +3 -3
- package/dist/{host → server}/processes.js +3 -3
- package/dist/server/processes.js.map +1 -0
- package/dist/{host → server}/resolver-entry.d.ts +6 -6
- package/dist/{host → server}/resolver-entry.js +14 -14
- package/dist/server/resolver-entry.js.map +1 -0
- package/dist/server/resource-runner-entry.d.ts +2 -0
- package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
- package/dist/server/resource-runner-entry.js.map +1 -0
- package/dist/server/resource-runner-protocol.d.ts +36 -0
- package/dist/server/resource-runner-protocol.js +49 -0
- package/dist/server/resource-runner-protocol.js.map +1 -0
- package/dist/server/resource-runner-supervisor.d.ts +21 -0
- package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
- package/dist/server/resource-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/rpc-bridge.d.ts +3 -3
- package/dist/{host → server}/rpc-bridge.js +3 -3
- package/dist/server/rpc-bridge.js.map +1 -0
- package/dist/{host → server}/rpc-executor.d.ts +1 -1
- package/dist/{host → server}/rpc-executor.js +3 -3
- package/dist/server/rpc-executor.js.map +1 -0
- package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
- package/dist/server/server-entry.js.map +1 -0
- package/dist/{host/runner.d.ts → server/server.d.ts} +39 -38
- package/dist/{host/runner.js → server/server.js} +408 -374
- package/dist/server/server.js.map +1 -0
- package/dist/{host → server}/state.d.ts +25 -25
- package/dist/{host → server}/state.js +49 -49
- package/dist/server/state.js.map +1 -0
- package/dist/{host → server}/view.d.ts +7 -7
- package/dist/{host → server}/view.js +13 -13
- package/dist/server/view.js.map +1 -0
- package/dist/server/workflow-runner-content.d.ts +6 -0
- package/dist/server/workflow-runner-content.js +96 -0
- package/dist/server/workflow-runner-content.js.map +1 -0
- package/dist/{host/worker-entry.d.ts → server/workflow-runner-entry.d.ts} +3 -3
- package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +68 -35
- package/dist/server/workflow-runner-entry.js.map +1 -0
- package/dist/server/workflow-runner-protocol.d.ts +69 -0
- package/dist/server/workflow-runner-protocol.js +176 -0
- package/dist/server/workflow-runner-protocol.js.map +1 -0
- package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
- package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
- package/dist/server/workflow-runner-store.js.map +1 -0
- package/dist/server/workflow-runner-supervisor.d.ts +22 -0
- package/dist/server/workflow-runner-supervisor.js +56 -0
- package/dist/server/workflow-runner-supervisor.js.map +1 -0
- package/dist/state/prune.d.ts +1 -1
- package/dist/state/prune.js +17 -10
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +2 -2
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +1 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +1 -1
- package/dist/viewer/backup.js.map +1 -1
- package/dist/viewer/cli.d.ts +2 -2
- package/dist/viewer/cli.js +46 -32
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/tui.d.ts +1 -1
- package/dist/viewer/tui.js +1 -1
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/command-batch.js +2 -2
- package/dist/workflows/engine.js +14 -15
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/store.d.ts +12 -11
- package/dist/workflows/store.js +24 -18
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +2 -2
- package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
- package/docs/2026-08-25-workflow-follow-ups.md +7 -7
- package/docs/2026-08-25-workflow-settings.md +3 -3
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
- package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
- package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
- package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
- package/docs/2026-09-04-workflow-run-state-plan.md +6 -6
- package/docs/DEFERRED_TURNS.md +7 -7
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/HUMAN_DECISIONS.md +11 -11
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
- package/docs/MONITOR.md +1 -1
- package/docs/RESOURCE_MANAGERS.md +221 -0
- package/docs/SQLITE_STATE.md +35 -33
- package/docs/WORKFLOW_COMPOSITION.md +1 -1
- package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +125 -119
- package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
- package/docs/WORKFLOW_UPDATES.md +16 -16
- package/docs/development.md +15 -14
- package/docs/live-replay-protocol.md +20 -20
- package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
- package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
- package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
- package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
- package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
- package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
- package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
- package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
- package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
- package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
- package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
- package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
- package/docs/tui-viewer.md +9 -9
- package/docs/workflows.md +72 -66
- package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
- package/herdr-plugin.toml +1 -1
- package/package.json +12 -11
- package/protocol/client.v1.schema.json +7 -7
- package/protocol/fixtures/client-v1.json +1 -1
- package/src/builtins/monitor.workflow.ts +9 -9
- package/src/builtins/pi-agent-group.ts +3 -3
- package/src/channels/adapter-entry.ts +14 -14
- package/src/client/client.ts +33 -32
- package/src/client/protocol.ts +16 -11
- package/src/client/resolver.ts +4 -4
- package/src/client/view.ts +1 -1
- package/src/extension/index.ts +44 -34
- package/src/extension/recorder.ts +1 -1
- package/src/extension/remote-recorder-store.ts +2 -2
- package/src/extension/resource-manager-command.ts +45 -0
- package/src/extension/session-delivery.ts +2 -2
- package/src/extension/session-view.ts +1 -1
- package/src/extension/workflow-message-coordinator.ts +6 -6
- package/src/{controllers → resource-managers}/conditions.ts +23 -23
- package/src/resource-managers/definition.ts +71 -0
- package/src/{controllers → resource-managers}/effects.ts +9 -9
- package/src/resource-managers/errors.ts +27 -0
- package/src/resource-managers/index.ts +88 -0
- package/src/resource-managers/loader.ts +111 -0
- package/src/{controllers → resource-managers}/results.ts +4 -4
- package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
- package/src/{controllers → resource-managers}/sqlite.ts +198 -186
- package/src/{controllers → resource-managers}/store.ts +46 -38
- package/src/{controllers → resource-managers}/types.ts +50 -41
- package/src/{controllers → resource-managers}/workflows.ts +43 -41
- package/src/{host → server}/channel-supervisor.ts +8 -8
- package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
- package/src/{host → server}/processes.ts +3 -3
- package/src/{host → server}/resolver-entry.ts +28 -25
- package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
- package/src/server/resource-runner-protocol.ts +103 -0
- package/src/server/resource-runner-supervisor.ts +76 -0
- package/src/{host → server}/rpc-bridge.ts +3 -3
- package/src/{host → server}/rpc-executor.ts +4 -4
- package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
- package/src/{host/runner.ts → server/server.ts} +526 -461
- package/src/{host → server}/state.ts +78 -67
- package/src/{host → server}/view.ts +15 -15
- package/src/server/workflow-runner-content.ts +119 -0
- package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +98 -57
- package/src/server/workflow-runner-protocol.ts +241 -0
- package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
- package/src/server/workflow-runner-supervisor.ts +79 -0
- package/src/state/prune.ts +27 -9
- package/src/state/schema.ts +2 -2
- package/src/state/workflow-messages.ts +1 -1
- package/src/viewer/backup.ts +1 -1
- package/src/viewer/cli.ts +50 -36
- package/src/viewer/tui.ts +1 -1
- package/src/workflows/command-batch.ts +2 -2
- package/src/workflows/engine.ts +14 -15
- package/src/workflows/store.ts +31 -28
- package/src/workflows/types.ts +2 -2
- package/dist/controllers/conditions.d.ts +0 -6
- package/dist/controllers/conditions.js.map +0 -1
- package/dist/controllers/definition.d.ts +0 -6
- package/dist/controllers/definition.js +0 -45
- package/dist/controllers/definition.js.map +0 -1
- package/dist/controllers/effects.d.ts +0 -15
- package/dist/controllers/effects.js.map +0 -1
- package/dist/controllers/errors.d.ts +0 -12
- package/dist/controllers/errors.js +0 -25
- package/dist/controllers/errors.js.map +0 -1
- package/dist/controllers/index.d.ts +0 -12
- package/dist/controllers/index.js +0 -12
- package/dist/controllers/index.js.map +0 -1
- package/dist/controllers/json.js.map +0 -1
- package/dist/controllers/loader.d.ts +0 -23
- package/dist/controllers/loader.js +0 -74
- package/dist/controllers/loader.js.map +0 -1
- package/dist/controllers/manager.d.ts +0 -59
- package/dist/controllers/manager.js.map +0 -1
- package/dist/controllers/results.d.ts +0 -5
- package/dist/controllers/results.js.map +0 -1
- package/dist/controllers/sqlite.js.map +0 -1
- package/dist/controllers/workflows.d.ts +0 -34
- package/dist/controllers/workflows.js.map +0 -1
- package/dist/extension/controller-command.js.map +0 -1
- package/dist/host/channel-effects.js.map +0 -1
- package/dist/host/channel-supervisor.js.map +0 -1
- package/dist/host/child-worker-supervisor.js.map +0 -1
- package/dist/host/controller-worker-entry.d.ts +0 -2
- package/dist/host/controller-worker-entry.js.map +0 -1
- package/dist/host/controller-worker-protocol.d.ts +0 -36
- package/dist/host/controller-worker-protocol.js +0 -49
- package/dist/host/controller-worker-protocol.js.map +0 -1
- package/dist/host/controller-worker-supervisor.d.ts +0 -21
- package/dist/host/controller-worker-supervisor.js +0 -54
- package/dist/host/controller-worker-supervisor.js.map +0 -1
- package/dist/host/host-entry.js.map +0 -1
- package/dist/host/processes.js.map +0 -1
- package/dist/host/resolver-entry.js.map +0 -1
- package/dist/host/rpc-bridge.js.map +0 -1
- package/dist/host/rpc-executor.js.map +0 -1
- package/dist/host/runner.js.map +0 -1
- package/dist/host/state.js.map +0 -1
- package/dist/host/view.js.map +0 -1
- package/dist/host/worker-entry.js.map +0 -1
- package/dist/host/worker-protocol.d.ts +0 -46
- package/dist/host/worker-protocol.js +0 -122
- package/dist/host/worker-protocol.js.map +0 -1
- package/dist/host/worker-store.js.map +0 -1
- package/dist/host/worker-supervisor.d.ts +0 -22
- package/dist/host/worker-supervisor.js.map +0 -1
- package/docs/CONTROLLERS.md +0 -217
- package/src/controllers/definition.ts +0 -65
- package/src/controllers/errors.ts +0 -27
- package/src/controllers/index.ts +0 -88
- package/src/controllers/loader.ts +0 -104
- package/src/extension/controller-command.ts +0 -45
- package/src/host/controller-worker-protocol.ts +0 -104
- package/src/host/controller-worker-supervisor.ts +0 -79
- package/src/host/worker-protocol.ts +0 -176
- package/src/host/worker-supervisor.ts +0 -74
- /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
- /package/dist/{controllers → resource-managers}/json.js +0 -0
- /package/dist/{controllers → resource-managers}/results.js +0 -0
- /package/dist/{controllers → resource-managers}/store.js +0 -0
- /package/dist/{controllers → resource-managers}/types.js +0 -0
- /package/dist/{host → server}/channel-effects.d.ts +0 -0
- /package/dist/{host → server}/channel-effects.js +0 -0
- /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
- /package/src/{controllers → resource-managers}/json.ts +0 -0
- /package/src/{host → server}/channel-effects.ts +0 -0
|
@@ -8,12 +8,12 @@ import { initializeViewerRun, recordViewerDeltas } from "../state/viewer.js";
|
|
|
8
8
|
import { WorkflowMessageStore } from "../state/workflow-messages.js";
|
|
9
9
|
import {
|
|
10
10
|
EffectRequestConflictError,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
ManagedResourceConflictError,
|
|
12
|
+
ManagedResourceNotFoundError,
|
|
13
13
|
WorkflowRequestConflictError,
|
|
14
14
|
} from "./errors.js";
|
|
15
15
|
import type {
|
|
16
|
-
|
|
16
|
+
ResourceManagerStore,
|
|
17
17
|
EffectReservation,
|
|
18
18
|
QueueItem,
|
|
19
19
|
QueueRequeueOptions,
|
|
@@ -22,11 +22,11 @@ import type {
|
|
|
22
22
|
} from "./store.js";
|
|
23
23
|
import type {
|
|
24
24
|
ChildWorkflowRecord,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
ResourceManagerEvent,
|
|
26
|
+
ResourceManagerQueueClaim,
|
|
27
|
+
ManagedResource,
|
|
28
|
+
ManagedResourceRef,
|
|
29
|
+
ManagedResourceStatus,
|
|
30
30
|
EffectRecord,
|
|
31
31
|
JsonObject,
|
|
32
32
|
} from "./types.js";
|
|
@@ -139,11 +139,11 @@ export type RunEventRecord = {
|
|
|
139
139
|
payload: JsonObject;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
type
|
|
143
|
-
|
|
142
|
+
type ResourceManagerRow = {
|
|
143
|
+
managedResourceId: string;
|
|
144
144
|
resourceId: string;
|
|
145
145
|
resourceVersion: number;
|
|
146
|
-
|
|
146
|
+
resourceManagerName: string;
|
|
147
147
|
resourceKey: string;
|
|
148
148
|
uid: string;
|
|
149
149
|
generation: number;
|
|
@@ -268,7 +268,7 @@ type WorkflowRow = {
|
|
|
268
268
|
errorHash: Buffer | null;
|
|
269
269
|
};
|
|
270
270
|
|
|
271
|
-
export class
|
|
271
|
+
export class SqliteResourceManagerStore implements ResourceManagerStore {
|
|
272
272
|
readonly filePath: string;
|
|
273
273
|
readonly state: StateDatabase;
|
|
274
274
|
private readonly workflowMessages: WorkflowMessageStore;
|
|
@@ -282,7 +282,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
282
282
|
readOnly?: boolean;
|
|
283
283
|
projectPath?: string;
|
|
284
284
|
state?: StateDatabase;
|
|
285
|
-
/**
|
|
285
|
+
/** Server-only global view. Project-scoped mutations still require projectPath. */
|
|
286
286
|
global?: boolean;
|
|
287
287
|
} = {},
|
|
288
288
|
) {
|
|
@@ -312,31 +312,34 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
putResource<TSpec, TStatus>(options: {
|
|
315
|
-
|
|
315
|
+
resourceManager: string;
|
|
316
316
|
key: string;
|
|
317
317
|
spec: TSpec;
|
|
318
318
|
initialStatus: TStatus;
|
|
319
319
|
now?: string;
|
|
320
|
-
}):
|
|
320
|
+
}): ManagedResource<TSpec, TStatus> {
|
|
321
321
|
this.assertWritable();
|
|
322
|
-
validateName(options.
|
|
322
|
+
validateName(options.resourceManager, "resource manager");
|
|
323
323
|
validateKey(options.key, "resource key");
|
|
324
324
|
const now = epoch(validTimestamp(options.now));
|
|
325
325
|
const specHash = this.state.putJson(options.spec, now);
|
|
326
|
-
const initialStatus:
|
|
326
|
+
const initialStatus: ManagedResourceStatus<TStatus> = {
|
|
327
327
|
observedGeneration: 0,
|
|
328
328
|
conditions: [],
|
|
329
|
-
|
|
329
|
+
resourceManagerStatus: options.initialStatus,
|
|
330
330
|
};
|
|
331
331
|
const statusHash = this.state.putJson(initialStatus, now);
|
|
332
332
|
return this.state.transaction(() => {
|
|
333
|
-
const existing = this.
|
|
333
|
+
const existing = this.resourceManagerRow({
|
|
334
|
+
resourceManager: options.resourceManager,
|
|
335
|
+
key: options.key,
|
|
336
|
+
});
|
|
334
337
|
if (existing === undefined) {
|
|
335
338
|
const uid = randomUUID();
|
|
336
|
-
const
|
|
339
|
+
const managedResourceId = `controller-resource-${randomUUID()}`;
|
|
337
340
|
const resourceId = resourceIdFor(
|
|
338
341
|
"controller",
|
|
339
|
-
`${this.requireProjectId()}:${options.
|
|
342
|
+
`${this.requireProjectId()}:${options.resourceManager}:${options.key}`,
|
|
340
343
|
);
|
|
341
344
|
this.state.connection
|
|
342
345
|
.prepare(
|
|
@@ -344,7 +347,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
344
347
|
resource_id, resource_type, aggregate_key, revision, created_at, updated_at
|
|
345
348
|
) VALUES (?, 'controller', ?, 1, ?, ?)`,
|
|
346
349
|
)
|
|
347
|
-
.run(resourceId,
|
|
350
|
+
.run(resourceId, managedResourceId, now, now);
|
|
348
351
|
this.state.connection
|
|
349
352
|
.prepare("INSERT INTO leases(resource_id, generation) VALUES (?, 0)")
|
|
350
353
|
.run(resourceId);
|
|
@@ -356,10 +359,10 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
356
359
|
) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)`,
|
|
357
360
|
)
|
|
358
361
|
.run(
|
|
359
|
-
|
|
362
|
+
managedResourceId,
|
|
360
363
|
resourceId,
|
|
361
364
|
this.requireProjectId(),
|
|
362
|
-
options.
|
|
365
|
+
options.resourceManager,
|
|
363
366
|
options.key,
|
|
364
367
|
uid,
|
|
365
368
|
specHash,
|
|
@@ -367,7 +370,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
367
370
|
now,
|
|
368
371
|
now,
|
|
369
372
|
);
|
|
370
|
-
this.
|
|
373
|
+
this.enqueueResourceManagerRow(managedResourceId, now, now);
|
|
371
374
|
this.insertEvent(
|
|
372
375
|
resourceId,
|
|
373
376
|
1,
|
|
@@ -385,9 +388,9 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
385
388
|
SET spec_hash = ?, generation = generation + 1, updated_at = ?
|
|
386
389
|
WHERE controller_resource_id = ?`,
|
|
387
390
|
)
|
|
388
|
-
.run(specHash, now, existing.
|
|
391
|
+
.run(specHash, now, existing.managedResourceId);
|
|
389
392
|
this.bumpResource(existing.resourceId, existing.resourceVersion, now);
|
|
390
|
-
this.
|
|
393
|
+
this.enqueueResourceManagerRow(existing.managedResourceId, now, now);
|
|
391
394
|
this.insertEvent(
|
|
392
395
|
existing.resourceId,
|
|
393
396
|
revision,
|
|
@@ -399,74 +402,74 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
399
402
|
);
|
|
400
403
|
}
|
|
401
404
|
return this.requireResource({
|
|
402
|
-
|
|
405
|
+
resourceManager: options.resourceManager,
|
|
403
406
|
key: options.key,
|
|
404
|
-
}) as
|
|
407
|
+
}) as ManagedResource<TSpec, TStatus>;
|
|
405
408
|
});
|
|
406
409
|
}
|
|
407
410
|
|
|
408
411
|
getResource<TSpec = unknown, TStatus = unknown>(
|
|
409
|
-
ref:
|
|
410
|
-
):
|
|
411
|
-
const row = this.
|
|
412
|
+
ref: ManagedResourceRef,
|
|
413
|
+
): ManagedResource<TSpec, TStatus> | undefined {
|
|
414
|
+
const row = this.resourceManagerRow(ref);
|
|
412
415
|
return row === undefined
|
|
413
416
|
? undefined
|
|
414
|
-
: (this.
|
|
417
|
+
: (this.mapManagedResource(row) as ManagedResource<TSpec, TStatus>);
|
|
415
418
|
}
|
|
416
419
|
|
|
417
|
-
getResourceByUid(uid: string):
|
|
418
|
-
const row = this.state.connection.prepare(
|
|
419
|
-
return
|
|
420
|
+
getResourceByUid(uid: string): ManagedResource | undefined {
|
|
421
|
+
const row = this.state.connection.prepare(resourceManagerSelect("WHERE c.uid = ?")).get(uid);
|
|
422
|
+
return isResourceManagerRow(row) ? this.mapManagedResource(row) : undefined;
|
|
420
423
|
}
|
|
421
424
|
|
|
422
425
|
listResources<TSpec = unknown, TStatus = unknown>(
|
|
423
|
-
options: {
|
|
424
|
-
):
|
|
426
|
+
options: { resourceManager?: string } = {},
|
|
427
|
+
): ManagedResource<TSpec, TStatus>[] {
|
|
425
428
|
const clauses = ["1 = 1"];
|
|
426
429
|
const params: unknown[] = [];
|
|
427
430
|
if (this.projectId !== null) {
|
|
428
431
|
clauses.push("c.project_id = ?");
|
|
429
432
|
params.push(this.projectId);
|
|
430
433
|
}
|
|
431
|
-
if (options.
|
|
434
|
+
if (options.resourceManager !== undefined) {
|
|
432
435
|
clauses.push("c.controller_name = ?");
|
|
433
|
-
params.push(options.
|
|
436
|
+
params.push(options.resourceManager);
|
|
434
437
|
}
|
|
435
438
|
const rows = this.state.connection
|
|
436
439
|
.prepare(
|
|
437
|
-
|
|
440
|
+
resourceManagerSelect(
|
|
438
441
|
`WHERE ${clauses.join(" AND ")} ORDER BY c.controller_name, c.resource_key`,
|
|
439
442
|
),
|
|
440
443
|
)
|
|
441
444
|
.all(...params);
|
|
442
445
|
return rows
|
|
443
|
-
.filter(
|
|
444
|
-
.map((row) => this.
|
|
446
|
+
.filter(isResourceManagerRow)
|
|
447
|
+
.map((row) => this.mapManagedResource(row) as ManagedResource<TSpec, TStatus>);
|
|
445
448
|
}
|
|
446
449
|
|
|
447
450
|
updateStatus<TStatus>(options: {
|
|
448
|
-
ref:
|
|
451
|
+
ref: ManagedResourceRef;
|
|
449
452
|
expectedResourceVersion: number;
|
|
450
|
-
claim:
|
|
451
|
-
status:
|
|
453
|
+
claim: ResourceManagerQueueClaim;
|
|
454
|
+
status: ManagedResourceStatus<TStatus>;
|
|
452
455
|
finalizers?: string[];
|
|
453
456
|
now?: string;
|
|
454
|
-
}):
|
|
457
|
+
}): ManagedResource<unknown, TStatus> {
|
|
455
458
|
const now = epoch(validTimestamp(options.now));
|
|
456
459
|
return this.state.transaction(() => {
|
|
457
|
-
const row = this.
|
|
458
|
-
this.
|
|
460
|
+
const row = this.requireResourceManagerRow(options.ref);
|
|
461
|
+
this.assertResourceManagerClaim(row, options.claim, now);
|
|
459
462
|
if (row.resourceVersion !== options.expectedResourceVersion) {
|
|
460
|
-
throw new
|
|
463
|
+
throw new ManagedResourceConflictError(options.ref.resourceManager, options.ref.key);
|
|
461
464
|
}
|
|
462
465
|
const statusHash = this.state.putJson(options.status, now);
|
|
463
466
|
this.state.connection
|
|
464
467
|
.prepare(
|
|
465
468
|
"UPDATE controller_resources SET status_hash = ?, updated_at = ? WHERE controller_resource_id = ?",
|
|
466
469
|
)
|
|
467
|
-
.run(statusHash, now, row.
|
|
470
|
+
.run(statusHash, now, row.managedResourceId);
|
|
468
471
|
if (options.finalizers !== undefined)
|
|
469
|
-
this.replaceFinalizers(row.
|
|
472
|
+
this.replaceFinalizers(row.managedResourceId, options.finalizers);
|
|
470
473
|
this.bumpResource(row.resourceId, row.resourceVersion, now);
|
|
471
474
|
this.insertEvent(
|
|
472
475
|
row.resourceId,
|
|
@@ -479,22 +482,22 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
479
482
|
options.claim.generation,
|
|
480
483
|
);
|
|
481
484
|
options.claim.resourceVersion = row.resourceVersion + 1;
|
|
482
|
-
return this.requireResource(options.ref) as
|
|
485
|
+
return this.requireResource(options.ref) as ManagedResource<unknown, TStatus>;
|
|
483
486
|
});
|
|
484
487
|
}
|
|
485
488
|
|
|
486
|
-
requestDeletion(ref:
|
|
489
|
+
requestDeletion(ref: ManagedResourceRef, nowValue?: string): ManagedResource {
|
|
487
490
|
const now = epoch(validTimestamp(nowValue));
|
|
488
491
|
return this.state.transaction(() => {
|
|
489
|
-
const row = this.
|
|
492
|
+
const row = this.requireResourceManagerRow(ref);
|
|
490
493
|
if (row.deletionRequestedAt === null) {
|
|
491
494
|
this.state.connection
|
|
492
495
|
.prepare(
|
|
493
496
|
"UPDATE controller_resources SET deletion_requested_at = ?, updated_at = ? WHERE controller_resource_id = ?",
|
|
494
497
|
)
|
|
495
|
-
.run(now, now, row.
|
|
498
|
+
.run(now, now, row.managedResourceId);
|
|
496
499
|
this.bumpResource(row.resourceId, row.resourceVersion, now);
|
|
497
|
-
this.
|
|
500
|
+
this.enqueueResourceManagerRow(row.managedResourceId, now, now);
|
|
498
501
|
this.insertEvent(
|
|
499
502
|
row.resourceId,
|
|
500
503
|
row.resourceVersion + 1,
|
|
@@ -510,21 +513,21 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
510
513
|
}
|
|
511
514
|
|
|
512
515
|
updateFinalizers(options: {
|
|
513
|
-
ref:
|
|
516
|
+
ref: ManagedResourceRef;
|
|
514
517
|
expectedResourceVersion: number;
|
|
515
518
|
finalizers: string[];
|
|
516
519
|
now?: string;
|
|
517
|
-
}):
|
|
520
|
+
}): ManagedResource {
|
|
518
521
|
const now = epoch(validTimestamp(options.now));
|
|
519
522
|
return this.state.transaction(() => {
|
|
520
|
-
const row = this.
|
|
523
|
+
const row = this.requireResourceManagerRow(options.ref);
|
|
521
524
|
if (row.resourceVersion !== options.expectedResourceVersion) {
|
|
522
|
-
throw new
|
|
525
|
+
throw new ManagedResourceConflictError(options.ref.resourceManager, options.ref.key);
|
|
523
526
|
}
|
|
524
|
-
this.replaceFinalizers(row.
|
|
527
|
+
this.replaceFinalizers(row.managedResourceId, options.finalizers);
|
|
525
528
|
this.state.connection
|
|
526
529
|
.prepare("UPDATE controller_resources SET updated_at = ? WHERE controller_resource_id = ?")
|
|
527
|
-
.run(now, row.
|
|
530
|
+
.run(now, row.managedResourceId);
|
|
528
531
|
this.bumpResource(row.resourceId, row.resourceVersion, now);
|
|
529
532
|
this.insertEvent(
|
|
530
533
|
row.resourceId,
|
|
@@ -540,50 +543,50 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
540
543
|
}
|
|
541
544
|
|
|
542
545
|
deleteResource(
|
|
543
|
-
ref:
|
|
546
|
+
ref: ManagedResourceRef,
|
|
544
547
|
expectedResourceVersion: number,
|
|
545
|
-
claim:
|
|
548
|
+
claim: ResourceManagerQueueClaim,
|
|
546
549
|
): boolean {
|
|
547
550
|
return this.state.transaction(() => {
|
|
548
|
-
const row = this.
|
|
549
|
-
this.
|
|
551
|
+
const row = this.requireResourceManagerRow(ref);
|
|
552
|
+
this.assertResourceManagerClaim(row, claim, Date.now());
|
|
550
553
|
if (row.resourceVersion !== expectedResourceVersion) {
|
|
551
|
-
throw new
|
|
554
|
+
throw new ManagedResourceConflictError(ref.resourceManager, ref.key);
|
|
552
555
|
}
|
|
553
|
-
if (row.deletionRequestedAt === null || this.finalizers(row.
|
|
556
|
+
if (row.deletionRequestedAt === null || this.finalizers(row.managedResourceId).length > 0)
|
|
554
557
|
return false;
|
|
555
558
|
this.state.connection
|
|
556
559
|
.prepare("DELETE FROM controller_resources WHERE controller_resource_id = ?")
|
|
557
|
-
.run(row.
|
|
560
|
+
.run(row.managedResourceId);
|
|
558
561
|
return true;
|
|
559
562
|
});
|
|
560
563
|
}
|
|
561
564
|
|
|
562
|
-
enqueue(ref:
|
|
563
|
-
const row = this.
|
|
565
|
+
enqueue(ref: ManagedResourceRef, availableAt?: string): void {
|
|
566
|
+
const row = this.requireResourceManagerRow(ref);
|
|
564
567
|
const now = Date.now();
|
|
565
|
-
this.
|
|
568
|
+
this.enqueueResourceManagerRow(row.managedResourceId, epoch(validTimestamp(availableAt)), now);
|
|
566
569
|
}
|
|
567
570
|
|
|
568
571
|
claimNext(options: {
|
|
569
|
-
|
|
572
|
+
resourceManagers: string[];
|
|
570
573
|
ownerId: string;
|
|
571
574
|
leaseMs: number;
|
|
572
575
|
now?: string;
|
|
573
|
-
exclude?:
|
|
574
|
-
}):
|
|
575
|
-
if (options.
|
|
576
|
+
exclude?: ManagedResourceRef[];
|
|
577
|
+
}): ResourceManagerQueueClaim | undefined {
|
|
578
|
+
if (options.resourceManagers.length === 0) return undefined;
|
|
576
579
|
const now = epoch(validTimestamp(options.now));
|
|
577
|
-
const placeholders = options.
|
|
580
|
+
const placeholders = options.resourceManagers.map(() => "?").join(", ");
|
|
578
581
|
const exclusions = options.exclude ?? [];
|
|
579
582
|
const exclusionSql = exclusions
|
|
580
583
|
.map(() => "AND NOT (c.controller_name = ? AND c.resource_key = ?)")
|
|
581
584
|
.join("\n ");
|
|
582
|
-
const exclusionParams = exclusions.flatMap((ref) => [ref.
|
|
585
|
+
const exclusionParams = exclusions.flatMap((ref) => [ref.resourceManager, ref.key]);
|
|
583
586
|
return this.state.transaction(() => {
|
|
584
587
|
const row = this.state.connection
|
|
585
588
|
.prepare(
|
|
586
|
-
`${
|
|
589
|
+
`${resourceManagerSelect(`
|
|
587
590
|
JOIN controller_queue q ON q.controller_resource_id = c.controller_resource_id
|
|
588
591
|
JOIN leases l ON l.resource_id = c.resource_id
|
|
589
592
|
WHERE c.project_id = ?
|
|
@@ -594,8 +597,8 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
594
597
|
ORDER BY q.available_at, c.controller_name, c.resource_key
|
|
595
598
|
LIMIT 1`)}`,
|
|
596
599
|
)
|
|
597
|
-
.get(this.requireProjectId(), ...options.
|
|
598
|
-
if (!
|
|
600
|
+
.get(this.requireProjectId(), ...options.resourceManagers, ...exclusionParams, now, now);
|
|
601
|
+
if (!isResourceManagerRow(row)) return undefined;
|
|
599
602
|
const lease = this.requireLease(row.resourceId);
|
|
600
603
|
const token = randomBytes(32).toString("base64url");
|
|
601
604
|
const generation = lease.generation + 1;
|
|
@@ -633,23 +636,23 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
633
636
|
generation,
|
|
634
637
|
);
|
|
635
638
|
return {
|
|
636
|
-
|
|
639
|
+
resourceManager: row.resourceManagerName,
|
|
637
640
|
key: row.resourceKey,
|
|
638
641
|
ownerId: options.ownerId,
|
|
639
642
|
token,
|
|
640
643
|
generation,
|
|
641
|
-
queueVersion: this.queueVersion(row.
|
|
644
|
+
queueVersion: this.queueVersion(row.managedResourceId),
|
|
642
645
|
resourceVersion: row.resourceVersion + 1,
|
|
643
|
-
consecutiveErrors: this.queueErrors(row.
|
|
646
|
+
consecutiveErrors: this.queueErrors(row.managedResourceId),
|
|
644
647
|
expiresAt: new Date(expiresAt).toISOString(),
|
|
645
648
|
};
|
|
646
649
|
});
|
|
647
650
|
}
|
|
648
651
|
|
|
649
|
-
renewClaim(claim:
|
|
652
|
+
renewClaim(claim: ResourceManagerQueueClaim, leaseMs: number, nowValue?: string): boolean {
|
|
650
653
|
const now = epoch(validTimestamp(nowValue));
|
|
651
654
|
const expiresAt = now + leaseMs;
|
|
652
|
-
const row = this.
|
|
655
|
+
const row = this.resourceManagerRow({ resourceManager: claim.resourceManager, key: claim.key });
|
|
653
656
|
if (row === undefined) return false;
|
|
654
657
|
const result = this.state.connection
|
|
655
658
|
.prepare(
|
|
@@ -670,22 +673,22 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
670
673
|
return result.changes === 1;
|
|
671
674
|
}
|
|
672
675
|
|
|
673
|
-
settleClaim(claim:
|
|
674
|
-
return this.
|
|
676
|
+
settleClaim(claim: ResourceManagerQueueClaim, nowValue?: string): boolean {
|
|
677
|
+
return this.settleResourceManagerClaim(claim, undefined, nowValue);
|
|
675
678
|
}
|
|
676
679
|
|
|
677
680
|
requeueClaim(
|
|
678
|
-
claim:
|
|
681
|
+
claim: ResourceManagerQueueClaim,
|
|
679
682
|
options: QueueRequeueOptions,
|
|
680
683
|
nowValue?: string,
|
|
681
684
|
): boolean {
|
|
682
|
-
return this.
|
|
685
|
+
return this.settleResourceManagerClaim(claim, options, nowValue);
|
|
683
686
|
}
|
|
684
687
|
|
|
685
688
|
listQueue(): QueueItem[] {
|
|
686
689
|
const rows = this.state.connection
|
|
687
690
|
.prepare(
|
|
688
|
-
`SELECT c.controller_name AS
|
|
691
|
+
`SELECT c.controller_name AS resourceManager, c.resource_key AS resourceKey,
|
|
689
692
|
q.available_at AS availableAt, q.consecutive_errors AS consecutiveErrors,
|
|
690
693
|
l.expires_at AS claimExpiresAt
|
|
691
694
|
FROM controller_queue q
|
|
@@ -695,7 +698,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
695
698
|
)
|
|
696
699
|
.all(this.requireProjectId());
|
|
697
700
|
return rows.filter(isQueueListRow).map((row) => ({
|
|
698
|
-
|
|
701
|
+
resourceManager: row.resourceManager,
|
|
699
702
|
key: row.resourceKey,
|
|
700
703
|
availableAt: new Date(row.availableAt).toISOString(),
|
|
701
704
|
consecutiveErrors: row.consecutiveErrors,
|
|
@@ -708,7 +711,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
708
711
|
reserveEffect(options: {
|
|
709
712
|
key: string;
|
|
710
713
|
resourceUid: string;
|
|
711
|
-
claim:
|
|
714
|
+
claim: ResourceManagerQueueClaim;
|
|
712
715
|
generation: number;
|
|
713
716
|
kind: string;
|
|
714
717
|
requestFingerprint: string;
|
|
@@ -716,8 +719,8 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
716
719
|
}): EffectReservation {
|
|
717
720
|
const now = epoch(validTimestamp(options.now));
|
|
718
721
|
return this.state.transaction(() => {
|
|
719
|
-
const source = this.
|
|
720
|
-
this.
|
|
722
|
+
const source = this.requireResourceManagerRowByUid(options.resourceUid);
|
|
723
|
+
this.assertResourceManagerClaim(source, options.claim, now);
|
|
721
724
|
const effectId = effectIdFor(source.resourceId, options.key);
|
|
722
725
|
const existing = this.effectRow(options.resourceUid, options.key);
|
|
723
726
|
if (existing !== undefined) {
|
|
@@ -794,7 +797,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
794
797
|
updateEffect(options: {
|
|
795
798
|
resourceUid: string;
|
|
796
799
|
key: string;
|
|
797
|
-
claim:
|
|
800
|
+
claim: ResourceManagerQueueClaim;
|
|
798
801
|
state: EffectRecord["state"];
|
|
799
802
|
externalRef?: string;
|
|
800
803
|
error?: string;
|
|
@@ -802,8 +805,8 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
802
805
|
}): EffectRecord {
|
|
803
806
|
const now = epoch(validTimestamp(options.now));
|
|
804
807
|
return this.state.transaction(() => {
|
|
805
|
-
const source = this.
|
|
806
|
-
this.
|
|
808
|
+
const source = this.requireResourceManagerRowByUid(options.resourceUid);
|
|
809
|
+
this.assertResourceManagerClaim(source, options.claim, now);
|
|
807
810
|
const row = this.requireEffectRow(options.resourceUid, options.key);
|
|
808
811
|
const effectResourceId = resourceIdFor("effect", row.effectId);
|
|
809
812
|
const revision = this.resourceRevision(effectResourceId);
|
|
@@ -846,15 +849,15 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
846
849
|
|
|
847
850
|
reserveWorkflow(options: {
|
|
848
851
|
resourceUid: string;
|
|
849
|
-
claim:
|
|
852
|
+
claim: ResourceManagerQueueClaim;
|
|
850
853
|
requestKey: string;
|
|
851
854
|
workflow: string;
|
|
852
855
|
inputFingerprint: string;
|
|
853
856
|
}): WorkflowReservation {
|
|
854
857
|
const now = Date.now();
|
|
855
858
|
return this.state.transaction(() => {
|
|
856
|
-
const source = this.
|
|
857
|
-
this.
|
|
859
|
+
const source = this.requireResourceManagerRowByUid(options.resourceUid);
|
|
860
|
+
this.assertResourceManagerClaim(source, options.claim, now);
|
|
858
861
|
const existing = this.workflowRow(options.resourceUid, options.requestKey);
|
|
859
862
|
if (existing !== undefined) {
|
|
860
863
|
if (
|
|
@@ -875,7 +878,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
875
878
|
)
|
|
876
879
|
.run(
|
|
877
880
|
requestId,
|
|
878
|
-
source.
|
|
881
|
+
source.managedResourceId,
|
|
879
882
|
options.requestKey,
|
|
880
883
|
options.workflow,
|
|
881
884
|
Buffer.from(options.inputFingerprint, "hex"),
|
|
@@ -899,13 +902,13 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
899
902
|
updateWorkflow(
|
|
900
903
|
requestId: string,
|
|
901
904
|
update: WorkflowRecordUpdate,
|
|
902
|
-
claim:
|
|
905
|
+
claim: ResourceManagerQueueClaim,
|
|
903
906
|
): ChildWorkflowRecord {
|
|
904
907
|
const now = Date.now();
|
|
905
908
|
return this.state.transaction(() => {
|
|
906
909
|
const row = this.requireWorkflowRow(requestId);
|
|
907
|
-
const source = this.
|
|
908
|
-
this.
|
|
910
|
+
const source = this.requireResourceManagerRowByUid(row.resourceUid);
|
|
911
|
+
this.assertResourceManagerClaim(source, claim, now);
|
|
909
912
|
const errorHash =
|
|
910
913
|
update.error === undefined || update.error === null
|
|
911
914
|
? null
|
|
@@ -945,7 +948,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
945
948
|
if (update.runId !== undefined && row.runId !== null && update.runId !== row.runId) {
|
|
946
949
|
throw new Error("Scheduler completion run ID does not match the reserved child run");
|
|
947
950
|
}
|
|
948
|
-
const source = this.
|
|
951
|
+
const source = this.requireResourceManagerRowByUid(row.resourceUid);
|
|
949
952
|
const now = Date.now();
|
|
950
953
|
return this.state.transaction(() => {
|
|
951
954
|
const errorHash =
|
|
@@ -996,17 +999,20 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
996
999
|
}
|
|
997
1000
|
|
|
998
1001
|
recordEvent(options: {
|
|
999
|
-
|
|
1002
|
+
resourceManager: string;
|
|
1000
1003
|
key: string;
|
|
1001
|
-
claim?:
|
|
1004
|
+
claim?: ResourceManagerQueueClaim;
|
|
1002
1005
|
type: string;
|
|
1003
1006
|
payload?: JsonObject;
|
|
1004
1007
|
now?: string;
|
|
1005
|
-
}):
|
|
1008
|
+
}): ResourceManagerEvent {
|
|
1006
1009
|
const now = epoch(validTimestamp(options.now));
|
|
1007
1010
|
return this.state.transaction(() => {
|
|
1008
|
-
const row = this.
|
|
1009
|
-
|
|
1011
|
+
const row = this.requireResourceManagerRow({
|
|
1012
|
+
resourceManager: options.resourceManager,
|
|
1013
|
+
key: options.key,
|
|
1014
|
+
});
|
|
1015
|
+
if (options.claim !== undefined) this.assertResourceManagerClaim(row, options.claim, now);
|
|
1010
1016
|
const revision = this.resourceRevision(row.resourceId) + 1;
|
|
1011
1017
|
this.bumpResource(row.resourceId, revision - 1, now);
|
|
1012
1018
|
const eventId = this.insertEvent(
|
|
@@ -1024,11 +1030,11 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1024
1030
|
.prepare("SELECT event_seq AS seq FROM events WHERE event_id = ?")
|
|
1025
1031
|
.get(eventId);
|
|
1026
1032
|
/* istanbul ignore if -- exact schema and internal query shape */
|
|
1027
|
-
if (!isSequenceRow(event)) throw new Error("
|
|
1033
|
+
if (!isSequenceRow(event)) throw new Error("ResourceManager event was not recorded");
|
|
1028
1034
|
return {
|
|
1029
1035
|
seq: event.seq,
|
|
1030
1036
|
recordedAt: new Date(now).toISOString(),
|
|
1031
|
-
|
|
1037
|
+
resourceManager: options.resourceManager,
|
|
1032
1038
|
key: options.key,
|
|
1033
1039
|
type: options.type,
|
|
1034
1040
|
payload: options.payload ?? {},
|
|
@@ -1037,17 +1043,17 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1037
1043
|
}
|
|
1038
1044
|
|
|
1039
1045
|
listEvents(
|
|
1040
|
-
options: {
|
|
1041
|
-
):
|
|
1046
|
+
options: { resourceManager?: string; key?: string; limit?: number } = {},
|
|
1047
|
+
): ResourceManagerEvent[] {
|
|
1042
1048
|
const clauses = ["r.resource_type = 'controller'"];
|
|
1043
1049
|
const params: unknown[] = [];
|
|
1044
1050
|
if (this.projectId !== null) {
|
|
1045
1051
|
clauses.push("c.project_id = ?");
|
|
1046
1052
|
params.push(this.projectId);
|
|
1047
1053
|
}
|
|
1048
|
-
if (options.
|
|
1054
|
+
if (options.resourceManager !== undefined) {
|
|
1049
1055
|
clauses.push("c.controller_name = ?");
|
|
1050
|
-
params.push(options.
|
|
1056
|
+
params.push(options.resourceManager);
|
|
1051
1057
|
}
|
|
1052
1058
|
if (options.key !== undefined) {
|
|
1053
1059
|
clauses.push("c.resource_key = ?");
|
|
@@ -1057,7 +1063,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1057
1063
|
const rows = this.state.connection
|
|
1058
1064
|
.prepare(
|
|
1059
1065
|
`SELECT e.event_seq AS seq, e.recorded_at AS recordedAt,
|
|
1060
|
-
c.controller_name AS
|
|
1066
|
+
c.controller_name AS resourceManager, c.resource_key AS resourceKey,
|
|
1061
1067
|
e.event_type AS eventType, e.payload_hash AS payloadHash
|
|
1062
1068
|
FROM events e
|
|
1063
1069
|
JOIN resources r ON r.resource_id = e.resource_id
|
|
@@ -1066,10 +1072,10 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1066
1072
|
ORDER BY e.event_seq DESC LIMIT ?`,
|
|
1067
1073
|
)
|
|
1068
1074
|
.all(...params);
|
|
1069
|
-
return rows.filter(
|
|
1075
|
+
return rows.filter(isResourceManagerEventRow).map((row) => ({
|
|
1070
1076
|
seq: row.seq,
|
|
1071
1077
|
recordedAt: new Date(row.recordedAt).toISOString(),
|
|
1072
|
-
|
|
1078
|
+
resourceManager: row.resourceManager,
|
|
1073
1079
|
key: row.resourceKey,
|
|
1074
1080
|
type: row.eventType,
|
|
1075
1081
|
payload: row.payloadHash === null ? {} : (this.state.readJson(row.payloadHash) as JsonObject),
|
|
@@ -1406,7 +1412,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1406
1412
|
return this.claimRun(options.runId, options.runnerId, options.claimToken, options.leaseMs, now);
|
|
1407
1413
|
}
|
|
1408
1414
|
|
|
1409
|
-
/** Schedule a
|
|
1415
|
+
/** Schedule a runner that validates one pending interactive submission. */
|
|
1410
1416
|
claimWorkflowRunForInteractionValidation(options: {
|
|
1411
1417
|
runId: string;
|
|
1412
1418
|
runnerId: string;
|
|
@@ -1427,7 +1433,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1427
1433
|
);
|
|
1428
1434
|
}
|
|
1429
1435
|
|
|
1430
|
-
/** Take a short
|
|
1436
|
+
/** Take a short server claim without scheduling a parked interactive run. */
|
|
1431
1437
|
claimWorkflowRunForControl(options: {
|
|
1432
1438
|
runId: string;
|
|
1433
1439
|
runnerId: string;
|
|
@@ -1559,7 +1565,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1559
1565
|
"(q.affinity_runner_id IS NULL OR q.affinity_runner_id = ? OR q.status IN ('parked', 'starting', 'running'))",
|
|
1560
1566
|
"NOT (q.status = 'parked' AND (r.status = 'waiting' OR r.paused = 1))",
|
|
1561
1567
|
"NOT EXISTS (SELECT 1 FROM interactive_requests i WHERE i.run_id = r.run_id AND i.status = 'pending')",
|
|
1562
|
-
"(q.error_code IS NULL OR q.error_code NOT IN ('workflowSourceChanged', '
|
|
1568
|
+
"(q.error_code IS NULL OR q.error_code NOT IN ('workflowSourceChanged', 'runnerNoProgress'))",
|
|
1563
1569
|
];
|
|
1564
1570
|
const params: unknown[] = [now, now, options.runnerId, options.runnerId];
|
|
1565
1571
|
if (this.projectId !== null) {
|
|
@@ -1672,7 +1678,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1672
1678
|
return this.releaseRunClaim(options.runId, options.claimToken, "parked", options.now);
|
|
1673
1679
|
}
|
|
1674
1680
|
|
|
1675
|
-
|
|
1681
|
+
parkWorkflowRunForRunnerNoProgress(options: {
|
|
1676
1682
|
runId: string;
|
|
1677
1683
|
claimToken: string;
|
|
1678
1684
|
detail: string;
|
|
@@ -1702,13 +1708,13 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1702
1708
|
const queue = this.state.connection
|
|
1703
1709
|
.prepare(
|
|
1704
1710
|
`UPDATE run_queue
|
|
1705
|
-
SET status = 'parked', error_code = '
|
|
1711
|
+
SET status = 'parked', error_code = 'runnerNoProgress', error_hash = ?,
|
|
1706
1712
|
available_at = ?, updated_at = ?, finished_at = NULL
|
|
1707
1713
|
WHERE run_id = ? AND status IN ('queued', 'starting', 'running', 'parked')`,
|
|
1708
1714
|
)
|
|
1709
1715
|
.run(errorHash, now, now, options.runId);
|
|
1710
1716
|
if (run.changes !== 1 || queue.changes !== 1) {
|
|
1711
|
-
throw new Error(`Workflow run ${options.runId} changed during
|
|
1717
|
+
throw new Error(`Workflow run ${options.runId} changed during runner recovery`);
|
|
1712
1718
|
}
|
|
1713
1719
|
const released = this.state.connection
|
|
1714
1720
|
.prepare(
|
|
@@ -1720,7 +1726,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1720
1726
|
.run(row.resourceId, tokenHash(options.claimToken), lease.generation, now);
|
|
1721
1727
|
/* istanbul ignore if -- the exact live claim is stable in this transaction */
|
|
1722
1728
|
if (released.changes !== 1) {
|
|
1723
|
-
throw new Error(`Workflow run ${options.runId} claim changed during
|
|
1729
|
+
throw new Error(`Workflow run ${options.runId} claim changed during runner recovery`);
|
|
1724
1730
|
}
|
|
1725
1731
|
const revision = this.resourceRevision(row.resourceId);
|
|
1726
1732
|
this.bumpResource(row.resourceId, revision, now);
|
|
@@ -1730,7 +1736,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
1730
1736
|
"run.worker_no_progress",
|
|
1731
1737
|
lease.ownerType ?? "system",
|
|
1732
1738
|
lease.ownerId,
|
|
1733
|
-
{ status: "parked", code: "
|
|
1739
|
+
{ status: "parked", code: "runnerNoProgress" },
|
|
1734
1740
|
now,
|
|
1735
1741
|
lease.generation || undefined,
|
|
1736
1742
|
);
|
|
@@ -2292,84 +2298,87 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2292
2298
|
|
|
2293
2299
|
private requireProjectId(): string {
|
|
2294
2300
|
if (this.projectId === null)
|
|
2295
|
-
throw new Error("
|
|
2301
|
+
throw new Error("ResourceManager project is not registered in the state database");
|
|
2296
2302
|
return this.projectId;
|
|
2297
2303
|
}
|
|
2298
2304
|
|
|
2299
|
-
private
|
|
2305
|
+
private resourceManagerRow(ref: ManagedResourceRef): ResourceManagerRow | undefined {
|
|
2300
2306
|
if (this.projectId === null) return undefined;
|
|
2301
2307
|
const row = this.state.connection
|
|
2302
2308
|
.prepare(
|
|
2303
|
-
|
|
2309
|
+
resourceManagerSelect(
|
|
2310
|
+
"WHERE c.project_id = ? AND c.controller_name = ? AND c.resource_key = ?",
|
|
2311
|
+
),
|
|
2304
2312
|
)
|
|
2305
|
-
.get(this.projectId, ref.
|
|
2306
|
-
return
|
|
2313
|
+
.get(this.projectId, ref.resourceManager, ref.key);
|
|
2314
|
+
return isResourceManagerRow(row) ? row : undefined;
|
|
2307
2315
|
}
|
|
2308
2316
|
|
|
2309
|
-
private
|
|
2310
|
-
const row = this.
|
|
2311
|
-
if (row === undefined) throw new
|
|
2317
|
+
private requireResourceManagerRow(ref: ManagedResourceRef): ResourceManagerRow {
|
|
2318
|
+
const row = this.resourceManagerRow(ref);
|
|
2319
|
+
if (row === undefined) throw new ManagedResourceNotFoundError(ref.resourceManager, ref.key);
|
|
2312
2320
|
return row;
|
|
2313
2321
|
}
|
|
2314
2322
|
|
|
2315
|
-
private
|
|
2316
|
-
const row = this.state.connection.prepare(
|
|
2323
|
+
private requireResourceManagerRowByUid(uid: string): ResourceManagerRow {
|
|
2324
|
+
const row = this.state.connection.prepare(resourceManagerSelect("WHERE c.uid = ?")).get(uid);
|
|
2317
2325
|
/* istanbul ignore if -- impossible after exact schema and transaction checks */
|
|
2318
|
-
if (!
|
|
2326
|
+
if (!isResourceManagerRow(row)) throw new Error(`Managed resource not found: ${uid}`);
|
|
2319
2327
|
return row;
|
|
2320
2328
|
}
|
|
2321
2329
|
|
|
2322
|
-
private requireResource(ref:
|
|
2330
|
+
private requireResource(ref: ManagedResourceRef): ManagedResource {
|
|
2323
2331
|
const resource = this.getResource(ref);
|
|
2324
|
-
if (resource === undefined)
|
|
2332
|
+
if (resource === undefined)
|
|
2333
|
+
throw new ManagedResourceNotFoundError(ref.resourceManager, ref.key);
|
|
2325
2334
|
return resource;
|
|
2326
2335
|
}
|
|
2327
2336
|
|
|
2328
2337
|
/* istanbul ignore next -- pure projection covered by integration tests */
|
|
2329
|
-
private
|
|
2338
|
+
private mapManagedResource(row: ResourceManagerRow): ManagedResource {
|
|
2330
2339
|
const spec = this.state.readJson(row.specHash);
|
|
2331
|
-
const status = this.state.readJson(row.statusHash) as
|
|
2340
|
+
const status = this.state.readJson(row.statusHash) as ManagedResourceStatus<unknown>;
|
|
2332
2341
|
return {
|
|
2333
2342
|
metadata: {
|
|
2334
2343
|
uid: row.uid,
|
|
2335
|
-
|
|
2344
|
+
resourceManager: row.resourceManagerName,
|
|
2336
2345
|
key: row.resourceKey,
|
|
2337
2346
|
resourceVersion: row.resourceVersion,
|
|
2338
2347
|
generation: row.generation,
|
|
2339
2348
|
...(row.deletionRequestedAt === null
|
|
2340
2349
|
? {}
|
|
2341
2350
|
: { deletionTimestamp: new Date(row.deletionRequestedAt).toISOString() }),
|
|
2342
|
-
finalizers: this.finalizers(row.
|
|
2351
|
+
finalizers: this.finalizers(row.managedResourceId),
|
|
2343
2352
|
},
|
|
2344
2353
|
spec,
|
|
2345
2354
|
status,
|
|
2346
2355
|
};
|
|
2347
2356
|
}
|
|
2348
2357
|
|
|
2349
|
-
private finalizers(
|
|
2358
|
+
private finalizers(managedResourceId: string): string[] {
|
|
2350
2359
|
const rows = this.state.connection
|
|
2351
2360
|
.prepare(
|
|
2352
2361
|
"SELECT finalizer FROM controller_finalizers WHERE controller_resource_id = ? ORDER BY position",
|
|
2353
2362
|
)
|
|
2354
|
-
.all(
|
|
2363
|
+
.all(managedResourceId);
|
|
2355
2364
|
return rows.flatMap((row) => (isFinalizerRow(row) ? [row.finalizer] : []));
|
|
2356
2365
|
}
|
|
2357
2366
|
|
|
2358
|
-
private replaceFinalizers(
|
|
2367
|
+
private replaceFinalizers(managedResourceId: string, finalizers: string[]): void {
|
|
2359
2368
|
const unique = [...new Set(finalizers)];
|
|
2360
2369
|
if (unique.length !== finalizers.length)
|
|
2361
|
-
throw new Error("
|
|
2370
|
+
throw new Error("ResourceManager finalizers must be unique");
|
|
2362
2371
|
this.state.connection
|
|
2363
2372
|
.prepare("DELETE FROM controller_finalizers WHERE controller_resource_id = ?")
|
|
2364
|
-
.run(
|
|
2373
|
+
.run(managedResourceId);
|
|
2365
2374
|
const insert = this.state.connection.prepare(
|
|
2366
2375
|
"INSERT INTO controller_finalizers(controller_resource_id, finalizer, position) VALUES (?, ?, ?)",
|
|
2367
2376
|
);
|
|
2368
|
-
unique.forEach((finalizer, index) => insert.run(
|
|
2377
|
+
unique.forEach((finalizer, index) => insert.run(managedResourceId, finalizer, index));
|
|
2369
2378
|
}
|
|
2370
2379
|
|
|
2371
|
-
private
|
|
2372
|
-
|
|
2380
|
+
private enqueueResourceManagerRow(
|
|
2381
|
+
managedResourceId: string,
|
|
2373
2382
|
availableAt: number,
|
|
2374
2383
|
now: number,
|
|
2375
2384
|
): void {
|
|
@@ -2384,50 +2393,53 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2384
2393
|
queue_version = controller_queue.queue_version + 1,
|
|
2385
2394
|
updated_at = excluded.updated_at`,
|
|
2386
2395
|
)
|
|
2387
|
-
.run(
|
|
2396
|
+
.run(managedResourceId, availableAt, now, now);
|
|
2388
2397
|
}
|
|
2389
2398
|
|
|
2390
|
-
private queueErrors(
|
|
2399
|
+
private queueErrors(managedResourceId: string): number {
|
|
2391
2400
|
const row = this.state.connection
|
|
2392
2401
|
.prepare(
|
|
2393
2402
|
"SELECT consecutive_errors AS consecutiveErrors FROM controller_queue WHERE controller_resource_id = ?",
|
|
2394
2403
|
)
|
|
2395
|
-
.get(
|
|
2404
|
+
.get(managedResourceId);
|
|
2396
2405
|
return isErrorCountRow(row) ? row.consecutiveErrors : 0;
|
|
2397
2406
|
}
|
|
2398
2407
|
|
|
2399
|
-
private queueVersion(
|
|
2408
|
+
private queueVersion(managedResourceId: string): number {
|
|
2400
2409
|
const row = this.state.connection
|
|
2401
2410
|
.prepare(
|
|
2402
2411
|
"SELECT queue_version AS queueVersion FROM controller_queue WHERE controller_resource_id = ?",
|
|
2403
2412
|
)
|
|
2404
|
-
.get(
|
|
2413
|
+
.get(managedResourceId);
|
|
2405
2414
|
/* istanbul ignore if -- exact schema and internal query shape */
|
|
2406
2415
|
/* istanbul ignore if -- impossible after exact schema and transaction checks */
|
|
2407
|
-
if (!isQueueVersionRow(row)) throw new Error("
|
|
2416
|
+
if (!isQueueVersionRow(row)) throw new Error("ResourceManager queue item is missing");
|
|
2408
2417
|
return row.queueVersion;
|
|
2409
2418
|
}
|
|
2410
2419
|
|
|
2411
|
-
private
|
|
2412
|
-
claim:
|
|
2420
|
+
private settleResourceManagerClaim(
|
|
2421
|
+
claim: ResourceManagerQueueClaim,
|
|
2413
2422
|
requeue: QueueRequeueOptions | undefined,
|
|
2414
2423
|
nowValue?: string,
|
|
2415
2424
|
): boolean {
|
|
2416
2425
|
const now = epoch(validTimestamp(nowValue));
|
|
2417
2426
|
return this.state.transaction(() => {
|
|
2418
|
-
const row = this.
|
|
2427
|
+
const row = this.resourceManagerRow({
|
|
2428
|
+
resourceManager: claim.resourceManager,
|
|
2429
|
+
key: claim.key,
|
|
2430
|
+
});
|
|
2419
2431
|
if (row === undefined) return false;
|
|
2420
2432
|
try {
|
|
2421
|
-
this.
|
|
2433
|
+
this.assertResourceManagerClaim(row, claim, now, false);
|
|
2422
2434
|
} catch {
|
|
2423
2435
|
return false;
|
|
2424
2436
|
}
|
|
2425
|
-
const currentQueueVersion = this.queueVersion(row.
|
|
2437
|
+
const currentQueueVersion = this.queueVersion(row.managedResourceId);
|
|
2426
2438
|
if (requeue === undefined) {
|
|
2427
2439
|
if (currentQueueVersion === claim.queueVersion) {
|
|
2428
2440
|
this.state.connection
|
|
2429
2441
|
.prepare("DELETE FROM controller_queue WHERE controller_resource_id = ?")
|
|
2430
|
-
.run(row.
|
|
2442
|
+
.run(row.managedResourceId);
|
|
2431
2443
|
}
|
|
2432
2444
|
} else {
|
|
2433
2445
|
const errorHash =
|
|
@@ -2439,21 +2451,21 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2439
2451
|
consecutive_errors = consecutive_errors + 1,
|
|
2440
2452
|
last_error_hash = ?, updated_at = ? WHERE controller_resource_id = ?`,
|
|
2441
2453
|
)
|
|
2442
|
-
.run(epoch(requeue.availableAt), errorHash, now, row.
|
|
2454
|
+
.run(epoch(requeue.availableAt), errorHash, now, row.managedResourceId);
|
|
2443
2455
|
}
|
|
2444
2456
|
this.releaseLease(row.resourceId, claim, now);
|
|
2445
2457
|
return true;
|
|
2446
2458
|
});
|
|
2447
2459
|
}
|
|
2448
2460
|
|
|
2449
|
-
private
|
|
2450
|
-
row:
|
|
2451
|
-
claim:
|
|
2461
|
+
private assertResourceManagerClaim(
|
|
2462
|
+
row: ResourceManagerRow,
|
|
2463
|
+
claim: ResourceManagerQueueClaim,
|
|
2452
2464
|
now: number,
|
|
2453
2465
|
requireUnexpired = true,
|
|
2454
2466
|
): void {
|
|
2455
|
-
if (row.
|
|
2456
|
-
throw new Error("
|
|
2467
|
+
if (row.resourceManagerName !== claim.resourceManager || row.resourceKey !== claim.key)
|
|
2468
|
+
throw new Error("ResourceManager claim targets another resource");
|
|
2457
2469
|
const lease = this.requireLease(row.resourceId);
|
|
2458
2470
|
if (
|
|
2459
2471
|
lease.ownerType !== "controller" ||
|
|
@@ -2463,7 +2475,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2463
2475
|
!lease.tokenHash.equals(tokenHash(claim.token)) ||
|
|
2464
2476
|
(requireUnexpired && (lease.expiresAt === null || lease.expiresAt <= now))
|
|
2465
2477
|
) {
|
|
2466
|
-
throw new Error("
|
|
2478
|
+
throw new Error("ResourceManager claim is stale");
|
|
2467
2479
|
}
|
|
2468
2480
|
}
|
|
2469
2481
|
|
|
@@ -2481,8 +2493,8 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2481
2493
|
return row;
|
|
2482
2494
|
}
|
|
2483
2495
|
|
|
2484
|
-
private releaseLease(resourceId: string, claim:
|
|
2485
|
-
const row = this.
|
|
2496
|
+
private releaseLease(resourceId: string, claim: ResourceManagerQueueClaim, now: number): void {
|
|
2497
|
+
const row = this.resourceManagerRow({ resourceManager: claim.resourceManager, key: claim.key });
|
|
2486
2498
|
if (row === undefined) return;
|
|
2487
2499
|
const revision = this.resourceRevision(resourceId);
|
|
2488
2500
|
const result = this.state.connection
|
|
@@ -2600,7 +2612,7 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
2600
2612
|
resourceUid: row.resourceUid,
|
|
2601
2613
|
generation: row.generation,
|
|
2602
2614
|
kind: row.kind,
|
|
2603
|
-
state:
|
|
2615
|
+
state: resourceManagerEffectState(row.state),
|
|
2604
2616
|
requestFingerprint: row.requestFingerprint,
|
|
2605
2617
|
startedAt: new Date(row.startedAt).toISOString(),
|
|
2606
2618
|
...(row.completedAt === null ? {} : { completedAt: new Date(row.completedAt).toISOString() }),
|
|
@@ -3129,14 +3141,14 @@ export class SqliteControllerStore implements ControllerStore {
|
|
|
3129
3141
|
}
|
|
3130
3142
|
|
|
3131
3143
|
private assertWritable(): void {
|
|
3132
|
-
if (this.state.mode === "read-only") throw new Error("
|
|
3144
|
+
if (this.state.mode === "read-only") throw new Error("ResourceManager store is read-only");
|
|
3133
3145
|
}
|
|
3134
3146
|
}
|
|
3135
3147
|
|
|
3136
|
-
function
|
|
3137
|
-
return `SELECT c.controller_resource_id AS
|
|
3148
|
+
function resourceManagerSelect(clause: string): string {
|
|
3149
|
+
return `SELECT c.controller_resource_id AS managedResourceId,
|
|
3138
3150
|
c.resource_id AS resourceId, r.revision AS resourceVersion,
|
|
3139
|
-
c.controller_name AS
|
|
3151
|
+
c.controller_name AS resourceManagerName, c.resource_key AS resourceKey,
|
|
3140
3152
|
c.uid, c.generation, c.spec_hash AS specHash, c.status_hash AS statusHash,
|
|
3141
3153
|
c.deletion_requested_at AS deletionRequestedAt
|
|
3142
3154
|
FROM controller_resources c JOIN resources r ON r.resource_id = c.resource_id ${clause}`;
|
|
@@ -3205,16 +3217,16 @@ function workflowRunViewSelect(clause: string): string {
|
|
|
3205
3217
|
}
|
|
3206
3218
|
|
|
3207
3219
|
type QueueListRow = {
|
|
3208
|
-
|
|
3220
|
+
resourceManager: string;
|
|
3209
3221
|
resourceKey: string;
|
|
3210
3222
|
availableAt: number;
|
|
3211
3223
|
consecutiveErrors: number;
|
|
3212
3224
|
claimExpiresAt: number | null;
|
|
3213
3225
|
};
|
|
3214
|
-
type
|
|
3226
|
+
type ResourceManagerEventRow = {
|
|
3215
3227
|
seq: number;
|
|
3216
3228
|
recordedAt: number;
|
|
3217
|
-
|
|
3229
|
+
resourceManager: string;
|
|
3218
3230
|
resourceKey: string;
|
|
3219
3231
|
eventType: string;
|
|
3220
3232
|
payloadHash: Buffer | null;
|
|
@@ -3231,7 +3243,7 @@ type RunEventRow = {
|
|
|
3231
3243
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
3232
3244
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3233
3245
|
}
|
|
3234
|
-
function
|
|
3246
|
+
function isResourceManagerRow(value: unknown): value is ResourceManagerRow {
|
|
3235
3247
|
return isRecord(value);
|
|
3236
3248
|
}
|
|
3237
3249
|
function isLeaseRow(value: unknown): value is LeaseRow {
|
|
@@ -3338,7 +3350,7 @@ function isWorkflowRow(value: unknown): value is WorkflowRow {
|
|
|
3338
3350
|
function isQueueListRow(value: unknown): value is QueueListRow {
|
|
3339
3351
|
return isRecord(value);
|
|
3340
3352
|
}
|
|
3341
|
-
function
|
|
3353
|
+
function isResourceManagerEventRow(value: unknown): value is ResourceManagerEventRow {
|
|
3342
3354
|
return isRecord(value);
|
|
3343
3355
|
}
|
|
3344
3356
|
function isRunEventRow(value: unknown): value is RunEventRow {
|
|
@@ -3518,7 +3530,7 @@ function rowToSource(row: RunSourceIdentityRow): QueuedSource["root"] {
|
|
|
3518
3530
|
function effectStatus(value: EffectRecord["state"]): string {
|
|
3519
3531
|
return value === "indeterminate" ? "ambiguous" : value;
|
|
3520
3532
|
}
|
|
3521
|
-
function
|
|
3533
|
+
function resourceManagerEffectState(value: string): EffectRecord["state"] {
|
|
3522
3534
|
return value === "ambiguous" ? "indeterminate" : (value as EffectRecord["state"]);
|
|
3523
3535
|
}
|
|
3524
3536
|
function validTimestamp(value?: string): string {
|