@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
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { applyStatusPatch } from "./conditions.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { asAnyResourceManagerDefinition, asTypedResource } from "./definition.js";
|
|
4
|
+
import { ResourceManagerEffectService } from "./effects.js";
|
|
5
|
+
import { ManagedResourceConflictError } from "./errors.js";
|
|
6
6
|
import { createResultHelpers } from "./results.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type { ResourceManagerStore } from "./store.js";
|
|
8
8
|
import type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
AnyResourceManagerDefinition,
|
|
10
|
+
ResourceManagerDefinition,
|
|
11
|
+
ResourceManagerQueueClaim,
|
|
12
|
+
ManagedResource,
|
|
13
|
+
ManagedResourceRef,
|
|
14
14
|
JsonObject,
|
|
15
15
|
ReconcileContext,
|
|
16
16
|
ReconcileResult,
|
|
17
17
|
} from "./types.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
ResourceManagerWorkflowCoordinator,
|
|
20
|
+
type ResourceManagerWorkflowScheduler,
|
|
21
|
+
} from "./workflows.js";
|
|
19
22
|
|
|
20
23
|
const DEFAULT_MAX_CONCURRENT = 4;
|
|
21
24
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
@@ -26,12 +29,12 @@ const DEFAULT_MAX_BACKOFF_MS = 60_000;
|
|
|
26
29
|
const DEFAULT_JITTER_RATIO = 0.2;
|
|
27
30
|
const MIN_LEASE_MS = 300;
|
|
28
31
|
|
|
29
|
-
export type
|
|
30
|
-
store:
|
|
31
|
-
|
|
32
|
-
workflowScheduler?:
|
|
32
|
+
export type ResourceManagerRuntimeOptions = {
|
|
33
|
+
store: ResourceManagerStore;
|
|
34
|
+
resourceManagers: AnyResourceManagerDefinition[];
|
|
35
|
+
workflowScheduler?: ResourceManagerWorkflowScheduler;
|
|
33
36
|
maxConcurrent?: number;
|
|
34
|
-
|
|
37
|
+
resourceManagerConcurrency?: Record<string, number>;
|
|
35
38
|
defaultTimeoutMs?: number;
|
|
36
39
|
leaseMs?: number;
|
|
37
40
|
pollIntervalMs?: number;
|
|
@@ -42,13 +45,13 @@ export type ControllerManagerOptions = {
|
|
|
42
45
|
random?: () => number;
|
|
43
46
|
};
|
|
44
47
|
|
|
45
|
-
export class
|
|
46
|
-
readonly store:
|
|
47
|
-
private readonly ownerId = `
|
|
48
|
-
private readonly definitions = new Map<string,
|
|
49
|
-
private readonly workflowCoordinator:
|
|
48
|
+
export class ResourceManagerRuntime {
|
|
49
|
+
readonly store: ResourceManagerStore;
|
|
50
|
+
private readonly ownerId = `resource-manager-runtime-${randomUUID()}`;
|
|
51
|
+
private readonly definitions = new Map<string, AnyResourceManagerDefinition>();
|
|
52
|
+
private readonly workflowCoordinator: ResourceManagerWorkflowCoordinator;
|
|
50
53
|
private readonly maxConcurrent: number;
|
|
51
|
-
private readonly
|
|
54
|
+
private readonly resourceManagerConcurrency: Record<string, number>;
|
|
52
55
|
private readonly defaultTimeoutMs: number;
|
|
53
56
|
private readonly leaseMs: number;
|
|
54
57
|
private readonly pollIntervalMs: number;
|
|
@@ -57,20 +60,20 @@ export class ControllerManager {
|
|
|
57
60
|
private readonly jitterRatio: number;
|
|
58
61
|
private readonly now: () => Date;
|
|
59
62
|
private readonly random: () => number;
|
|
60
|
-
private readonly
|
|
63
|
+
private readonly activeByResourceManager = new Map<string, number>();
|
|
61
64
|
private readonly activeReconciles = new Map<string, AbortController>();
|
|
62
65
|
private runAbort: AbortController | null = null;
|
|
63
|
-
private
|
|
66
|
+
private runnerTasks: Promise<void>[] = [];
|
|
64
67
|
|
|
65
|
-
constructor(options:
|
|
68
|
+
constructor(options: ResourceManagerRuntimeOptions) {
|
|
66
69
|
this.store = options.store;
|
|
67
|
-
for (const definition of options.
|
|
70
|
+
for (const definition of options.resourceManagers) {
|
|
68
71
|
if (this.definitions.has(definition.name)) {
|
|
69
|
-
throw new Error(`Duplicate
|
|
72
|
+
throw new Error(`Duplicate resource manager definition: ${definition.name}`);
|
|
70
73
|
}
|
|
71
74
|
this.definitions.set(definition.name, definition);
|
|
72
75
|
}
|
|
73
|
-
this.workflowCoordinator = new
|
|
76
|
+
this.workflowCoordinator = new ResourceManagerWorkflowCoordinator(
|
|
74
77
|
options.store,
|
|
75
78
|
options.workflowScheduler,
|
|
76
79
|
);
|
|
@@ -78,12 +81,12 @@ export class ControllerManager {
|
|
|
78
81
|
options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT,
|
|
79
82
|
"maxConcurrent",
|
|
80
83
|
);
|
|
81
|
-
this.
|
|
82
|
-
for (const [name, value] of Object.entries(this.
|
|
84
|
+
this.resourceManagerConcurrency = options.resourceManagerConcurrency ?? {};
|
|
85
|
+
for (const [name, value] of Object.entries(this.resourceManagerConcurrency)) {
|
|
83
86
|
if (!this.definitions.has(name)) {
|
|
84
|
-
throw new Error(`Concurrency limit names an unknown
|
|
87
|
+
throw new Error(`Concurrency limit names an unknown resource manager: ${name}`);
|
|
85
88
|
}
|
|
86
|
-
positiveInteger(value, `
|
|
89
|
+
positiveInteger(value, `resourceManagerConcurrency.${name}`);
|
|
87
90
|
}
|
|
88
91
|
this.defaultTimeoutMs = positiveInteger(
|
|
89
92
|
options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -117,67 +120,67 @@ export class ControllerManager {
|
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
putResource<TSpec, TStatus>(
|
|
120
|
-
definition:
|
|
123
|
+
definition: ResourceManagerDefinition<TSpec, TStatus>,
|
|
121
124
|
key: string,
|
|
122
125
|
spec: TSpec,
|
|
123
|
-
):
|
|
126
|
+
): ManagedResource<TSpec, TStatus> {
|
|
124
127
|
const registered = this.definitions.get(definition.name);
|
|
125
|
-
if (registered !==
|
|
126
|
-
throw new Error(`
|
|
128
|
+
if (registered !== asAnyResourceManagerDefinition(definition)) {
|
|
129
|
+
throw new Error(`ResourceManager definition is not registered: ${definition.name}`);
|
|
127
130
|
}
|
|
128
131
|
const initialStatus = definition.initialStatus(spec);
|
|
129
132
|
const resource = this.store.putResource({
|
|
130
|
-
|
|
133
|
+
resourceManager: definition.name,
|
|
131
134
|
key,
|
|
132
135
|
spec,
|
|
133
136
|
initialStatus,
|
|
134
137
|
now: this.now().toISOString(),
|
|
135
138
|
});
|
|
136
139
|
this.recordEvent(
|
|
137
|
-
{
|
|
140
|
+
{ resourceManager: definition.name, key },
|
|
138
141
|
resource.metadata.generation === 1 ? "resource_applied" : "resource_updated",
|
|
139
142
|
{ generation: resource.metadata.generation },
|
|
140
143
|
);
|
|
141
144
|
const current = this.store.getResource<TSpec, TStatus>({
|
|
142
|
-
|
|
145
|
+
resourceManager: definition.name,
|
|
143
146
|
key,
|
|
144
147
|
});
|
|
145
|
-
if (current === undefined) throw new Error("
|
|
148
|
+
if (current === undefined) throw new Error("Managed resource disappeared after its event");
|
|
146
149
|
return current;
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
putResourceByName(
|
|
150
|
-
const definition = this.definitions.get(
|
|
152
|
+
putResourceByName(resourceManager: string, key: string, spec: unknown): ManagedResource {
|
|
153
|
+
const definition = this.definitions.get(resourceManager);
|
|
151
154
|
if (definition === undefined) {
|
|
152
|
-
throw new Error(`Unknown
|
|
155
|
+
throw new Error(`Unknown resource manager: ${resourceManager}`);
|
|
153
156
|
}
|
|
154
157
|
const initialStatus = definition.initialStatus(spec);
|
|
155
158
|
const resource = this.store.putResource({
|
|
156
|
-
|
|
159
|
+
resourceManager,
|
|
157
160
|
key,
|
|
158
161
|
spec,
|
|
159
162
|
initialStatus,
|
|
160
163
|
now: this.now().toISOString(),
|
|
161
164
|
});
|
|
162
165
|
this.recordEvent(
|
|
163
|
-
{
|
|
166
|
+
{ resourceManager, key },
|
|
164
167
|
resource.metadata.generation === 1 ? "resource_applied" : "resource_updated",
|
|
165
168
|
{ generation: resource.metadata.generation },
|
|
166
169
|
);
|
|
167
|
-
const current = this.store.getResource({
|
|
168
|
-
if (current === undefined) throw new Error("
|
|
170
|
+
const current = this.store.getResource({ resourceManager, key });
|
|
171
|
+
if (current === undefined) throw new Error("Managed resource disappeared after its event");
|
|
169
172
|
return current;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
|
-
requestDeletion(ref:
|
|
175
|
+
requestDeletion(ref: ManagedResourceRef): ManagedResource {
|
|
173
176
|
this.store.requestDeletion(ref, this.now().toISOString());
|
|
174
177
|
this.recordEvent(ref, "deletion_requested");
|
|
175
178
|
const current = this.store.getResource(ref);
|
|
176
|
-
if (current === undefined) throw new Error("
|
|
179
|
+
if (current === undefined) throw new Error("Managed resource disappeared after its event");
|
|
177
180
|
return current;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
enqueue(ref:
|
|
183
|
+
enqueue(ref: ManagedResourceRef, afterMs = 0): void {
|
|
181
184
|
if (!Number.isSafeInteger(afterMs) || afterMs < 0) {
|
|
182
185
|
throw new Error("afterMs must be a non-negative safe integer");
|
|
183
186
|
}
|
|
@@ -189,8 +192,8 @@ export class ControllerManager {
|
|
|
189
192
|
return;
|
|
190
193
|
}
|
|
191
194
|
this.runAbort = new AbortController();
|
|
192
|
-
this.
|
|
193
|
-
this.
|
|
195
|
+
this.runnerTasks = Array.from({ length: this.maxConcurrent }, () =>
|
|
196
|
+
this.runnerLoop(this.runAbort?.signal as AbortSignal),
|
|
194
197
|
);
|
|
195
198
|
}
|
|
196
199
|
|
|
@@ -200,12 +203,12 @@ export class ControllerManager {
|
|
|
200
203
|
return;
|
|
201
204
|
}
|
|
202
205
|
this.runAbort = null;
|
|
203
|
-
abort.abort(new Error("
|
|
204
|
-
for (const
|
|
205
|
-
|
|
206
|
+
abort.abort(new Error("Resource manager stopped"));
|
|
207
|
+
for (const abortController of this.activeReconciles.values()) {
|
|
208
|
+
abortController.abort(new Error("Resource manager stopped"));
|
|
206
209
|
}
|
|
207
|
-
await Promise.allSettled(this.
|
|
208
|
-
this.
|
|
210
|
+
await Promise.allSettled(this.runnerTasks);
|
|
211
|
+
this.runnerTasks = [];
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
async runOne(): Promise<boolean> {
|
|
@@ -224,16 +227,16 @@ export class ControllerManager {
|
|
|
224
227
|
count += 1;
|
|
225
228
|
}
|
|
226
229
|
if (count === maxReconciles && this.readyQueueExists()) {
|
|
227
|
-
throw new Error(`
|
|
230
|
+
throw new Error(`Resource manager exceeded ${maxReconciles} reconciliations`);
|
|
228
231
|
}
|
|
229
232
|
return count;
|
|
230
233
|
}
|
|
231
234
|
|
|
232
|
-
get workflowRequests():
|
|
235
|
+
get workflowRequests(): ResourceManagerWorkflowCoordinator {
|
|
233
236
|
return this.workflowCoordinator;
|
|
234
237
|
}
|
|
235
238
|
|
|
236
|
-
private async
|
|
239
|
+
private async runnerLoop(signal: AbortSignal): Promise<void> {
|
|
237
240
|
while (!signal.aborted) {
|
|
238
241
|
const claim = this.claimNext();
|
|
239
242
|
if (claim === undefined) {
|
|
@@ -245,54 +248,54 @@ export class ControllerManager {
|
|
|
245
248
|
} catch {
|
|
246
249
|
if (signal.aborted) return;
|
|
247
250
|
// A stale claim means another owner now controls that resource. Keep
|
|
248
|
-
// this
|
|
251
|
+
// this runner alive so it can reconcile other queued resources.
|
|
249
252
|
}
|
|
250
253
|
}
|
|
251
254
|
}
|
|
252
255
|
|
|
253
|
-
private claimNext():
|
|
256
|
+
private claimNext(): ResourceManagerQueueClaim | undefined {
|
|
254
257
|
return this.store.claimNext({
|
|
255
|
-
|
|
258
|
+
resourceManagers: this.eligibleResourceManagers(),
|
|
256
259
|
ownerId: this.ownerId,
|
|
257
260
|
leaseMs: this.leaseMs,
|
|
258
261
|
now: this.now().toISOString(),
|
|
259
262
|
});
|
|
260
263
|
}
|
|
261
264
|
|
|
262
|
-
private
|
|
265
|
+
private eligibleResourceManagers(): string[] {
|
|
263
266
|
return [...this.definitions.keys()].filter((name) => {
|
|
264
|
-
const active = this.
|
|
265
|
-
const limit = this.
|
|
267
|
+
const active = this.activeByResourceManager.get(name) ?? 0;
|
|
268
|
+
const limit = this.resourceManagerConcurrency[name] ?? this.maxConcurrent;
|
|
266
269
|
return active < limit;
|
|
267
270
|
});
|
|
268
271
|
}
|
|
269
272
|
|
|
270
|
-
private async processClaim(claim:
|
|
271
|
-
const definition = this.definitions.get(claim.
|
|
273
|
+
private async processClaim(claim: ResourceManagerQueueClaim): Promise<void> {
|
|
274
|
+
const definition = this.definitions.get(claim.resourceManager);
|
|
272
275
|
if (definition === undefined) {
|
|
273
276
|
return;
|
|
274
277
|
}
|
|
275
|
-
this.
|
|
276
|
-
claim.
|
|
277
|
-
(this.
|
|
278
|
+
this.activeByResourceManager.set(
|
|
279
|
+
claim.resourceManager,
|
|
280
|
+
(this.activeByResourceManager.get(claim.resourceManager) ?? 0) + 1,
|
|
278
281
|
);
|
|
279
282
|
try {
|
|
280
283
|
await this.reconcileClaim(definition, claim);
|
|
281
284
|
} finally {
|
|
282
|
-
const active = (this.
|
|
285
|
+
const active = (this.activeByResourceManager.get(claim.resourceManager) ?? 1) - 1;
|
|
283
286
|
if (active === 0) {
|
|
284
|
-
this.
|
|
287
|
+
this.activeByResourceManager.delete(claim.resourceManager);
|
|
285
288
|
} else {
|
|
286
|
-
this.
|
|
289
|
+
this.activeByResourceManager.set(claim.resourceManager, active);
|
|
287
290
|
}
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
|
|
291
294
|
private async reconcileClaim(
|
|
292
|
-
definition:
|
|
293
|
-
claim:
|
|
295
|
+
definition: AnyResourceManagerDefinition,
|
|
296
|
+
claim: ResourceManagerQueueClaim,
|
|
294
297
|
): Promise<void> {
|
|
295
|
-
const ref = {
|
|
298
|
+
const ref = { resourceManager: claim.resourceManager, key: claim.key };
|
|
296
299
|
const resource = this.store.getResource(ref);
|
|
297
300
|
if (resource === undefined) {
|
|
298
301
|
this.store.settleClaim(claim, this.now().toISOString());
|
|
@@ -314,7 +317,7 @@ export class ControllerManager {
|
|
|
314
317
|
() => {
|
|
315
318
|
const renewed = this.store.renewClaim(claim, this.leaseMs, this.now().toISOString());
|
|
316
319
|
if (!renewed) {
|
|
317
|
-
abort.abort(new Error("
|
|
320
|
+
abort.abort(new Error("ResourceManager queue claim was lost"));
|
|
318
321
|
}
|
|
319
322
|
},
|
|
320
323
|
Math.max(100, Math.floor(this.leaseMs / 3)),
|
|
@@ -350,14 +353,14 @@ export class ControllerManager {
|
|
|
350
353
|
}
|
|
351
354
|
|
|
352
355
|
private async callReconciler(
|
|
353
|
-
definition:
|
|
354
|
-
resource:
|
|
355
|
-
claim:
|
|
356
|
+
definition: AnyResourceManagerDefinition,
|
|
357
|
+
resource: ManagedResource,
|
|
358
|
+
claim: ResourceManagerQueueClaim,
|
|
356
359
|
signal: AbortSignal,
|
|
357
360
|
): Promise<ReconcileResult<unknown>> {
|
|
358
361
|
const context: ReconcileContext<unknown> = {
|
|
359
362
|
signal,
|
|
360
|
-
effects: new
|
|
363
|
+
effects: new ResourceManagerEffectService(this.store, resource, claim, signal),
|
|
361
364
|
workflows: this.workflowCoordinator.forResource(resource, claim, signal),
|
|
362
365
|
...createResultHelpers<unknown>(),
|
|
363
366
|
};
|
|
@@ -367,13 +370,13 @@ export class ControllerManager {
|
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
private async finishSuccessfulClaim(
|
|
370
|
-
claim:
|
|
371
|
-
resource:
|
|
373
|
+
claim: ResourceManagerQueueClaim,
|
|
374
|
+
resource: ManagedResource,
|
|
372
375
|
result: ReconcileResult<unknown>,
|
|
373
376
|
reconcileId: string,
|
|
374
377
|
startedAtMs: number,
|
|
375
378
|
): Promise<void> {
|
|
376
|
-
const ref = {
|
|
379
|
+
const ref = { resourceManager: claim.resourceManager, key: claim.key };
|
|
377
380
|
const now = this.now();
|
|
378
381
|
const status = applyStatusPatch(
|
|
379
382
|
resource.status,
|
|
@@ -416,7 +419,7 @@ export class ControllerManager {
|
|
|
416
419
|
return;
|
|
417
420
|
}
|
|
418
421
|
} catch (error) {
|
|
419
|
-
if (error instanceof
|
|
422
|
+
if (error instanceof ManagedResourceConflictError) {
|
|
420
423
|
this.recordEvent(ref, "reconcile_conflict", { reconcileId }, claim);
|
|
421
424
|
this.store.requeueClaim(claim, { availableAt: now.toISOString() }, now.toISOString());
|
|
422
425
|
return;
|
|
@@ -436,8 +439,8 @@ export class ControllerManager {
|
|
|
436
439
|
}
|
|
437
440
|
|
|
438
441
|
private async finishFailedClaim(
|
|
439
|
-
claim:
|
|
440
|
-
ref:
|
|
442
|
+
claim: ResourceManagerQueueClaim,
|
|
443
|
+
ref: ManagedResourceRef,
|
|
441
444
|
reconcileId: string,
|
|
442
445
|
error: unknown,
|
|
443
446
|
startedAtMs: number,
|
|
@@ -479,13 +482,13 @@ export class ControllerManager {
|
|
|
479
482
|
}
|
|
480
483
|
|
|
481
484
|
private recordEvent(
|
|
482
|
-
ref:
|
|
485
|
+
ref: ManagedResourceRef,
|
|
483
486
|
type: string,
|
|
484
487
|
payload: JsonObject = {},
|
|
485
|
-
claim?:
|
|
488
|
+
claim?: ResourceManagerQueueClaim,
|
|
486
489
|
): void {
|
|
487
490
|
this.store.recordEvent({
|
|
488
|
-
|
|
491
|
+
resourceManager: ref.resourceManager,
|
|
489
492
|
key: ref.key,
|
|
490
493
|
...(claim === undefined ? {} : { claim }),
|
|
491
494
|
type,
|
|
@@ -521,10 +524,10 @@ function raceWithAbort<T>(operation: Promise<T>, signal: AbortSignal): Promise<T
|
|
|
521
524
|
|
|
522
525
|
function validateResult(result: ReconcileResult<unknown>): void {
|
|
523
526
|
if (result === null || typeof result !== "object") {
|
|
524
|
-
throw new Error("
|
|
527
|
+
throw new Error("ResourceManager reconcile must return a result object");
|
|
525
528
|
}
|
|
526
529
|
if (result.kind !== "settled" && result.kind !== "requeue") {
|
|
527
|
-
throw new Error("
|
|
530
|
+
throw new Error("ResourceManager reconcile returned an unknown result kind");
|
|
528
531
|
}
|
|
529
532
|
if (result.kind === "settled" && "afterMs" in result) {
|
|
530
533
|
throw new Error("A settled reconcile result cannot specify afterMs");
|