@pikku/core 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/dist/crypto-utils.d.ts +2 -0
- package/dist/crypto-utils.js +78 -0
- package/dist/errors/error-handler.js +3 -3
- package/dist/errors/errors.d.ts +8 -0
- package/dist/errors/errors.js +16 -0
- package/dist/errors/index.d.ts +0 -1
- package/dist/errors/index.js +0 -1
- package/dist/function/function-runner.d.ts +10 -3
- package/dist/function/function-runner.js +136 -31
- package/dist/function/functions.types.d.ts +35 -12
- package/dist/function/functions.types.js +16 -6
- package/dist/function/index.d.ts +2 -2
- package/dist/function/index.js +1 -2
- package/dist/handle-error.js +1 -10
- package/dist/index.d.ts +30 -24
- package/dist/index.js +19 -27
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.d.ts +1 -1
- package/dist/middleware/auth-bearer.js +3 -6
- package/dist/middleware/auth-cookie.d.ts +1 -1
- package/dist/middleware/auth-cookie.js +6 -8
- package/dist/middleware/cors.d.ts +46 -0
- package/dist/middleware/cors.js +73 -0
- package/dist/middleware/index.d.ts +5 -3
- package/dist/middleware/index.js +5 -3
- package/dist/middleware/remote-auth.d.ts +5 -0
- package/dist/middleware/remote-auth.js +55 -0
- package/dist/middleware/timeout.d.ts +1 -1
- package/dist/middleware-runner.d.ts +3 -14
- package/dist/middleware-runner.js +14 -16
- package/dist/permissions.d.ts +3 -86
- package/dist/permissions.js +20 -80
- package/dist/pikku-state.d.ts +33 -104
- package/dist/pikku-state.js +150 -66
- package/dist/schema.d.ts +8 -6
- package/dist/schema.js +31 -18
- package/dist/services/ai-agent-runner-service.d.ts +24 -0
- package/dist/services/ai-agent-runner-service.js +1 -0
- package/dist/services/ai-run-state-service.d.ts +13 -0
- package/dist/services/ai-run-state-service.js +1 -0
- package/dist/services/ai-storage-service.d.ts +18 -0
- package/dist/services/ai-storage-service.js +1 -0
- package/dist/services/deployment-service.d.ts +19 -0
- package/dist/services/deployment-service.js +1 -0
- package/dist/services/gopass-secrets.d.ts +47 -0
- package/dist/services/gopass-secrets.js +94 -0
- package/dist/services/in-memory-trigger-service.d.ts +23 -0
- package/dist/services/in-memory-trigger-service.js +50 -0
- package/dist/services/in-memory-workflow-service.d.ts +59 -0
- package/dist/services/in-memory-workflow-service.js +287 -0
- package/dist/services/index.d.ts +25 -11
- package/dist/services/index.js +10 -13
- package/dist/services/local-secrets.d.ts +23 -0
- package/dist/services/local-secrets.js +45 -0
- package/dist/services/local-variables.d.ts +5 -0
- package/dist/services/local-variables.js +18 -0
- package/dist/services/scheduler-service.d.ts +21 -1
- package/dist/services/scheduler-service.js +14 -0
- package/dist/services/schema-service.d.ts +6 -0
- package/dist/services/scoped-secret-service.d.ts +16 -0
- package/dist/services/scoped-secret-service.js +35 -0
- package/dist/services/secret-service.d.ts +25 -4
- package/dist/services/trigger-service.d.ts +23 -0
- package/dist/services/trigger-service.js +1 -0
- package/dist/services/typed-secret-service.d.ts +30 -0
- package/dist/services/typed-secret-service.js +40 -0
- package/dist/services/typed-variables-service.d.ts +27 -0
- package/dist/services/typed-variables-service.js +46 -0
- package/dist/services/user-session-service.d.ts +13 -0
- package/dist/services/user-session-service.js +17 -0
- package/dist/services/variables-service.d.ts +5 -0
- package/dist/services/workflow-service.d.ts +43 -0
- package/dist/services/workflow-service.js +1 -0
- package/dist/types/core.types.d.ts +54 -10
- package/dist/types/core.types.js +7 -0
- package/dist/types/state.types.d.ts +127 -0
- package/dist/types/state.types.js +1 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +60 -3
- package/dist/version.d.ts +6 -0
- package/dist/version.js +19 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
- package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
- package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
- package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
- package/dist/wirings/ai-agent/index.d.ts +5 -0
- package/dist/wirings/ai-agent/index.js +4 -0
- package/dist/wirings/channel/channel-common.d.ts +7 -4
- package/dist/wirings/channel/channel-common.js +15 -8
- package/dist/wirings/channel/channel-handler.js +13 -4
- package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
- package/dist/wirings/channel/channel-middleware-runner.js +73 -0
- package/dist/wirings/channel/channel-runner.js +12 -12
- package/dist/wirings/channel/channel.types.d.ts +5 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
- package/dist/wirings/channel/define-channel-routes.js +23 -0
- package/dist/wirings/channel/index.d.ts +10 -8
- package/dist/wirings/channel/index.js +7 -8
- package/dist/wirings/channel/local/index.d.ts +3 -3
- package/dist/wirings/channel/local/index.js +3 -3
- package/dist/wirings/channel/local/local-channel-runner.js +18 -6
- package/dist/wirings/channel/log-channels.js +1 -1
- package/dist/wirings/channel/serverless/index.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.js +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +17 -6
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
- package/dist/wirings/cli/channel/index.d.ts +1 -1
- package/dist/wirings/cli/channel/index.js +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +1 -1
- package/dist/wirings/cli/cli-runner.js +22 -18
- package/dist/wirings/cli/cli.types.d.ts +7 -6
- package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
- package/dist/wirings/cli/define-cli-commands.js +20 -0
- package/dist/wirings/cli/index.d.ts +2 -3
- package/dist/wirings/cli/index.js +1 -3
- package/dist/wirings/http/http-routes.d.ts +69 -0
- package/dist/wirings/http/http-routes.js +139 -0
- package/dist/wirings/http/http-runner.d.ts +2 -2
- package/dist/wirings/http/http-runner.js +62 -27
- package/dist/wirings/http/http.types.d.ts +77 -24
- package/dist/wirings/http/index.d.ts +5 -4
- package/dist/wirings/http/index.js +4 -3
- package/dist/wirings/http/log-http-routes.js +1 -1
- package/dist/wirings/http/routers/path-to-regex.js +2 -2
- package/dist/wirings/mcp/index.d.ts +4 -3
- package/dist/wirings/mcp/index.js +3 -3
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
- package/dist/wirings/mcp/mcp-runner.js +42 -56
- package/dist/wirings/mcp/mcp.types.d.ts +3 -3
- package/dist/wirings/node/index.d.ts +2 -0
- package/dist/wirings/node/index.js +1 -0
- package/dist/wirings/node/node.types.d.ts +20 -0
- package/dist/wirings/node/node.types.js +1 -0
- package/dist/wirings/oauth2/index.d.ts +3 -0
- package/dist/wirings/oauth2/index.js +2 -0
- package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
- package/dist/wirings/oauth2/oauth2-client.js +267 -0
- package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
- package/dist/wirings/oauth2/oauth2.types.js +4 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
- package/dist/wirings/queue/index.d.ts +2 -2
- package/dist/wirings/queue/index.js +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +7 -0
- package/dist/wirings/queue/queue-runner.js +23 -12
- package/dist/wirings/queue/queue.types.d.ts +8 -2
- package/dist/wirings/rpc/index.d.ts +1 -1
- package/dist/wirings/rpc/index.js +1 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +40 -13
- package/dist/wirings/rpc/rpc-runner.js +191 -25
- package/dist/wirings/rpc/rpc-types.d.ts +15 -4
- package/dist/wirings/scheduler/index.d.ts +3 -3
- package/dist/wirings/scheduler/index.js +2 -3
- package/dist/wirings/scheduler/log-schedulers.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
- package/dist/wirings/scheduler/scheduler-runner.js +28 -10
- package/dist/wirings/secret/index.d.ts +3 -0
- package/dist/wirings/secret/index.js +2 -0
- package/dist/wirings/secret/secret.types.d.ts +27 -0
- package/dist/wirings/secret/secret.types.js +1 -0
- package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
- package/dist/wirings/secret/validate-secret-definitions.js +55 -0
- package/dist/wirings/trigger/index.d.ts +3 -0
- package/dist/wirings/trigger/index.js +2 -0
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
- package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
- package/dist/wirings/trigger/trigger-runner.d.ts +42 -0
- package/dist/wirings/trigger/trigger-runner.js +85 -0
- package/dist/wirings/trigger/trigger.types.d.ts +130 -0
- package/dist/wirings/trigger/trigger.types.js +40 -0
- package/dist/wirings/variable/index.d.ts +3 -0
- package/dist/wirings/variable/index.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
- package/dist/wirings/variable/validate-variable-definitions.js +45 -0
- package/dist/wirings/variable/variable.types.d.ts +18 -0
- package/dist/wirings/variable/variable.types.js +1 -0
- package/dist/wirings/workflow/dsl/index.d.ts +5 -0
- package/dist/wirings/workflow/dsl/index.js +4 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +292 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.js +23 -0
- package/dist/wirings/workflow/graph/graph-node.d.ts +117 -0
- package/dist/wirings/workflow/graph/graph-node.js +58 -0
- package/dist/wirings/workflow/graph/graph-runner.d.ts +9 -0
- package/dist/wirings/workflow/graph/graph-runner.js +540 -0
- package/dist/wirings/workflow/graph/index.d.ts +4 -0
- package/dist/wirings/workflow/graph/index.js +3 -0
- package/dist/wirings/workflow/graph/template.d.ts +15 -0
- package/dist/wirings/workflow/graph/template.js +27 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +82 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
- package/dist/wirings/workflow/index.d.ts +7 -12
- package/dist/wirings/workflow/index.js +8 -11
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +114 -10
- package/dist/wirings/workflow/pikku-workflow-service.js +386 -115
- package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
- package/dist/wirings/workflow/workflow-helpers.js +38 -0
- package/dist/wirings/workflow/workflow-utils.d.ts +1 -0
- package/dist/wirings/workflow/workflow-utils.js +1 -0
- package/dist/wirings/workflow/workflow.types.d.ts +83 -214
- package/package.json +13 -6
- package/run-tests.sh +4 -1
- package/src/crypto-utils.ts +99 -0
- package/src/errors/error-handler.ts +3 -3
- package/src/errors/errors.ts +17 -0
- package/src/errors/index.ts +0 -1
- package/src/factory-functions.test.ts +42 -2
- package/src/function/function-runner.test.ts +5 -12
- package/src/function/function-runner.ts +195 -40
- package/src/function/functions.types.ts +66 -14
- package/src/function/index.ts +5 -2
- package/src/handle-error.ts +2 -10
- package/src/index.ts +106 -27
- package/src/middleware/auth-apikey.test.ts +15 -12
- package/src/middleware/auth-apikey.ts +3 -3
- package/src/middleware/auth-bearer.test.ts +17 -14
- package/src/middleware/auth-bearer.ts +25 -27
- package/src/middleware/auth-cookie.test.ts +16 -13
- package/src/middleware/auth-cookie.ts +6 -8
- package/src/middleware/cors.ts +96 -0
- package/src/middleware/index.ts +5 -3
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware-runner.test.ts +122 -0
- package/src/middleware-runner.ts +24 -17
- package/src/permissions.test.ts +102 -86
- package/src/permissions.ts +32 -93
- package/src/pikku-state.ts +168 -201
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.ts +50 -18
- package/src/services/ai-agent-runner-service.ts +29 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gopass-secrets.ts +95 -0
- package/src/services/in-memory-trigger-service.ts +68 -0
- package/src/services/in-memory-workflow-service.ts +395 -0
- package/src/services/index.ts +49 -14
- package/src/services/local-secrets.ts +51 -0
- package/src/services/local-variables.ts +22 -0
- package/src/services/scheduler-service.ts +29 -1
- package/src/services/schema-service.ts +7 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +25 -4
- package/src/services/trigger-service.ts +38 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.ts +23 -0
- package/src/services/variables-service.ts +5 -0
- package/src/services/workflow-service.ts +94 -0
- package/src/types/core.types.ts +82 -8
- package/src/types/state.types.ts +169 -0
- package/src/utils.ts +74 -5
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
- package/src/wirings/ai-agent/index.ts +28 -0
- package/src/wirings/channel/channel-common.ts +32 -19
- package/src/wirings/channel/channel-handler.ts +18 -5
- package/src/wirings/channel/channel-middleware-runner.ts +119 -0
- package/src/wirings/channel/channel-runner.ts +14 -15
- package/src/wirings/channel/channel.types.ts +17 -1
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/index.ts +23 -8
- package/src/wirings/channel/local/index.ts +3 -3
- package/src/wirings/channel/local/local-channel-runner.test.ts +47 -2
- package/src/wirings/channel/local/local-channel-runner.ts +22 -6
- package/src/wirings/channel/log-channels.ts +1 -1
- package/src/wirings/channel/serverless/index.ts +5 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +22 -6
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
- package/src/wirings/cli/channel/index.ts +1 -1
- package/src/wirings/cli/cli-runner.test.ts +28 -28
- package/src/wirings/cli/cli-runner.ts +27 -19
- package/src/wirings/cli/cli.types.ts +12 -11
- package/src/wirings/cli/command-parser.test.ts +8 -8
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +10 -4
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +198 -0
- package/src/wirings/http/http-runner.test.ts +5 -5
- package/src/wirings/http/http-runner.ts +80 -29
- package/src/wirings/http/http.types.ts +121 -23
- package/src/wirings/http/index.ts +16 -4
- package/src/wirings/http/log-http-routes.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
- package/src/wirings/http/routers/path-to-regex.ts +2 -2
- package/src/wirings/mcp/index.ts +27 -3
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
- package/src/wirings/mcp/mcp-runner.ts +56 -67
- package/src/wirings/mcp/mcp.types.ts +3 -3
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +14 -2
- package/src/wirings/queue/queue-runner.test.ts +115 -73
- package/src/wirings/queue/queue-runner.ts +39 -13
- package/src/wirings/queue/queue.types.ts +12 -2
- package/src/wirings/rpc/index.ts +1 -1
- package/src/wirings/rpc/rpc-runner.ts +270 -34
- package/src/wirings/rpc/rpc-types.ts +21 -4
- package/src/wirings/scheduler/index.ts +7 -4
- package/src/wirings/scheduler/log-schedulers.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.test.ts +97 -53
- package/src/wirings/scheduler/scheduler-runner.ts +50 -10
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
- package/src/wirings/trigger/trigger-runner.ts +138 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/index.ts +14 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +64 -26
- package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +571 -136
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow-utils.ts +0 -0
- package/src/wirings/workflow/workflow.types.ts +123 -279
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
- package/dist/wirings/workflow/workflow-runner.js +0 -65
- package/src/function/function-runner.ts.bak +0 -188
- package/src/wirings/workflow/workflow-runner.ts +0 -72
|
@@ -9,21 +9,86 @@ import {
|
|
|
9
9
|
SerializedError,
|
|
10
10
|
} from '../../types/core.types.js'
|
|
11
11
|
import { QueueService } from '../queue/queue.types.js'
|
|
12
|
-
import {
|
|
13
|
-
WorkflowAsyncException,
|
|
14
|
-
WorkflowCancelledException,
|
|
15
|
-
WorkflowNotFoundError,
|
|
16
|
-
WorkflowRunNotFound,
|
|
17
|
-
} from './workflow-runner.js'
|
|
18
12
|
import type {
|
|
19
13
|
PikkuWorkflowWire,
|
|
20
14
|
StepState,
|
|
21
15
|
WorkflowRun,
|
|
16
|
+
WorkflowRunWire,
|
|
22
17
|
WorkflowStatus,
|
|
23
|
-
WorkflowService,
|
|
24
18
|
WorkflowServiceConfig,
|
|
25
19
|
WorkflowStepOptions,
|
|
26
20
|
} from './workflow.types.js'
|
|
21
|
+
import {
|
|
22
|
+
continueGraph,
|
|
23
|
+
executeGraphStep,
|
|
24
|
+
runWorkflowGraph,
|
|
25
|
+
runFromMeta,
|
|
26
|
+
} from './graph/graph-runner.js'
|
|
27
|
+
import { WorkflowService } from '../../services/workflow-service.js'
|
|
28
|
+
import { PikkuError, addError } from '../../errors/error-handler.js'
|
|
29
|
+
import { RPCNotFoundError } from '../rpc/rpc-runner.js'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Exception thrown when workflow needs to pause for async step
|
|
33
|
+
*/
|
|
34
|
+
export class WorkflowAsyncException extends Error {
|
|
35
|
+
constructor(
|
|
36
|
+
public readonly runId: string,
|
|
37
|
+
public readonly stepName: string
|
|
38
|
+
) {
|
|
39
|
+
super(`Workflow paused at step: ${stepName}`)
|
|
40
|
+
this.name = 'WorkflowAsyncException'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Exception thrown when workflow is cancelled
|
|
46
|
+
*/
|
|
47
|
+
export class WorkflowCancelledException extends Error {
|
|
48
|
+
constructor(
|
|
49
|
+
public readonly runId: string,
|
|
50
|
+
public readonly reason?: string
|
|
51
|
+
) {
|
|
52
|
+
super(reason || 'Workflow cancelled')
|
|
53
|
+
this.name = 'WorkflowCancelledException'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Exception thrown when workflow is suspended
|
|
59
|
+
*/
|
|
60
|
+
export class WorkflowSuspendedException extends Error {
|
|
61
|
+
constructor(
|
|
62
|
+
public readonly runId: string,
|
|
63
|
+
public readonly reason: string
|
|
64
|
+
) {
|
|
65
|
+
super(reason || 'Workflow suspended')
|
|
66
|
+
this.name = 'WorkflowSuspendedException'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Error class for workflow not found
|
|
72
|
+
*/
|
|
73
|
+
export class WorkflowNotFoundError extends PikkuError {
|
|
74
|
+
constructor(name: string) {
|
|
75
|
+
super(`Workflow not found: ${name}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
addError(WorkflowNotFoundError, {
|
|
79
|
+
status: 404,
|
|
80
|
+
message: 'Workflow not found.',
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export class WorkflowRunNotFoundError extends PikkuError {
|
|
84
|
+
constructor(runId: string) {
|
|
85
|
+
super(`Workflow run not found: ${runId}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
addError(WorkflowRunNotFoundError, {
|
|
89
|
+
status: 404,
|
|
90
|
+
message: 'Workflow run not found.',
|
|
91
|
+
})
|
|
27
92
|
|
|
28
93
|
export class WorkflowServiceNotInitialized extends Error {}
|
|
29
94
|
export class WorkflowStepNameNotString extends Error {
|
|
@@ -32,6 +97,13 @@ export class WorkflowStepNameNotString extends Error {
|
|
|
32
97
|
}
|
|
33
98
|
}
|
|
34
99
|
|
|
100
|
+
const WORKFLOW_END_STATES: ReadonlySet<string> = new Set([
|
|
101
|
+
'completed',
|
|
102
|
+
'failed',
|
|
103
|
+
'cancelled',
|
|
104
|
+
'suspended',
|
|
105
|
+
])
|
|
106
|
+
|
|
35
107
|
/**
|
|
36
108
|
* Abstract workflow state service
|
|
37
109
|
* Implementations provide pluggable storage backends (SQLite, PostgreSQL, etc.)
|
|
@@ -41,12 +113,34 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
41
113
|
private config: WorkflowServiceConfig | undefined
|
|
42
114
|
private singletonServices: CoreSingletonServices | undefined
|
|
43
115
|
private createWireServices: CreateWireServices | undefined
|
|
116
|
+
private inlineRuns = new Set<string>()
|
|
44
117
|
|
|
45
118
|
constructor() {}
|
|
46
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Check if a run is executing inline (without queues)
|
|
122
|
+
*/
|
|
123
|
+
protected isInline(runId: string): boolean {
|
|
124
|
+
return this.inlineRuns.has(runId)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Register a run as inline (for graph-runner to use)
|
|
129
|
+
*/
|
|
130
|
+
public registerInlineRun(runId: string): void {
|
|
131
|
+
this.inlineRuns.add(runId)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Unregister a run from inline tracking
|
|
136
|
+
*/
|
|
137
|
+
public unregisterInlineRun(runId: string): void {
|
|
138
|
+
this.inlineRuns.delete(runId)
|
|
139
|
+
}
|
|
140
|
+
|
|
47
141
|
public setServices(
|
|
48
142
|
singletonServices: CoreSingletonServices,
|
|
49
|
-
createWireServices: CreateWireServices,
|
|
143
|
+
createWireServices: CreateWireServices | undefined,
|
|
50
144
|
{ workflow }: CoreConfig
|
|
51
145
|
) {
|
|
52
146
|
this.singletonServices = singletonServices
|
|
@@ -58,17 +152,25 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
58
152
|
workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
|
|
59
153
|
stepWorkerQueueName:
|
|
60
154
|
workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
|
|
61
|
-
sleeperRPCName: workflow?.sleeperRPCName ?? '
|
|
155
|
+
sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
|
|
62
156
|
}
|
|
63
157
|
}
|
|
64
158
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
159
|
+
public async registerWorkflowVersions(): Promise<void> {
|
|
160
|
+
const allMeta = pikkuState(null, 'workflows', 'meta')
|
|
161
|
+
for (const [name, meta] of Object.entries(allMeta)) {
|
|
162
|
+
if (!meta.graphHash) continue
|
|
163
|
+
await this.upsertWorkflowVersion(name, meta.graphHash, meta, meta.source)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
abstract createRun(
|
|
168
|
+
workflowName: string,
|
|
169
|
+
input: any,
|
|
170
|
+
inline: boolean,
|
|
171
|
+
graphHash: string,
|
|
172
|
+
wire: WorkflowRunWire
|
|
173
|
+
): Promise<string>
|
|
72
174
|
|
|
73
175
|
/**
|
|
74
176
|
* Get a workflow run by ID
|
|
@@ -193,6 +295,81 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
193
295
|
*/
|
|
194
296
|
abstract close(): Promise<void>
|
|
195
297
|
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// Workflow Graph Methods
|
|
300
|
+
// ============================================================================
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get completed graph state (lightweight - no results)
|
|
304
|
+
* @param runId - Run ID
|
|
305
|
+
* @returns Completed node IDs and their branch keys
|
|
306
|
+
*/
|
|
307
|
+
abstract getCompletedGraphState(runId: string): Promise<{
|
|
308
|
+
completedNodeIds: string[]
|
|
309
|
+
failedNodeIds: string[]
|
|
310
|
+
branchKeys: Record<string, string>
|
|
311
|
+
}>
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Filter candidate nodes to only those without existing steps
|
|
315
|
+
* @param runId - Run ID
|
|
316
|
+
* @param nodeIds - Candidate node IDs to check
|
|
317
|
+
* @returns Node IDs that don't have a step yet
|
|
318
|
+
*/
|
|
319
|
+
abstract getNodesWithoutSteps(
|
|
320
|
+
runId: string,
|
|
321
|
+
nodeIds: string[]
|
|
322
|
+
): Promise<string[]>
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get results for specific nodes
|
|
326
|
+
* @param runId - Run ID
|
|
327
|
+
* @param nodeIds - Node IDs to fetch results for
|
|
328
|
+
* @returns Map of nodeId to result
|
|
329
|
+
*/
|
|
330
|
+
abstract getNodeResults(
|
|
331
|
+
runId: string,
|
|
332
|
+
nodeIds: string[]
|
|
333
|
+
): Promise<Record<string, any>>
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Set the branch key for a graph node step
|
|
337
|
+
* @param stepId - Step ID
|
|
338
|
+
* @param branchKey - Branch key selected by graph.branch()
|
|
339
|
+
*/
|
|
340
|
+
abstract setBranchTaken(stepId: string, branchKey: string): Promise<void>
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Update a state variable in the workflow run's state
|
|
344
|
+
* @param runId - Run ID
|
|
345
|
+
* @param name - Variable name
|
|
346
|
+
* @param value - Value to store
|
|
347
|
+
*/
|
|
348
|
+
abstract updateRunState(
|
|
349
|
+
runId: string,
|
|
350
|
+
name: string,
|
|
351
|
+
value: unknown
|
|
352
|
+
): Promise<void>
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get the entire state object for a workflow run
|
|
356
|
+
* @param runId - Run ID
|
|
357
|
+
* @returns The state object with all variables
|
|
358
|
+
*/
|
|
359
|
+
abstract getRunState(runId: string): Promise<Record<string, unknown>>
|
|
360
|
+
|
|
361
|
+
abstract upsertWorkflowVersion(
|
|
362
|
+
name: string,
|
|
363
|
+
graphHash: string,
|
|
364
|
+
graph: any,
|
|
365
|
+
source: string
|
|
366
|
+
): Promise<void>
|
|
367
|
+
|
|
368
|
+
abstract getWorkflowVersion(
|
|
369
|
+
name: string,
|
|
370
|
+
graphHash: string
|
|
371
|
+
): Promise<{ graph: any; source: string } | null>
|
|
372
|
+
|
|
196
373
|
// ============================================================================
|
|
197
374
|
// Workflow Lifecycle Methods
|
|
198
375
|
// ============================================================================
|
|
@@ -206,12 +383,27 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
206
383
|
await queueService.add(this.getConfig().orchestratorQueueName, { runId })
|
|
207
384
|
}
|
|
208
385
|
|
|
386
|
+
public async queueStepWorker(
|
|
387
|
+
runId: string,
|
|
388
|
+
stepName: string,
|
|
389
|
+
rpcName: string,
|
|
390
|
+
data: any
|
|
391
|
+
): Promise<void> {
|
|
392
|
+
const queueService = this.verifyQueueService()
|
|
393
|
+
await queueService.add(this.getConfig().stepWorkerQueueName, {
|
|
394
|
+
runId,
|
|
395
|
+
stepName,
|
|
396
|
+
rpcName,
|
|
397
|
+
data,
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
209
401
|
/**
|
|
210
402
|
* Execute a workflow sleep step completion
|
|
211
403
|
* Sets the step result to null and resumes the workflow
|
|
212
404
|
* @param data - Sleeper input data
|
|
213
405
|
*/
|
|
214
|
-
public async
|
|
406
|
+
public async executeWorkflowSleepCompleted(
|
|
215
407
|
runId: string,
|
|
216
408
|
stepId: string
|
|
217
409
|
): Promise<void> {
|
|
@@ -238,51 +430,141 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
238
430
|
|
|
239
431
|
/**
|
|
240
432
|
* Start a new workflow run
|
|
433
|
+
* Automatically detects workflow type (DSL or graph) from meta and executes accordingly
|
|
434
|
+
* @param options.inline - If true, execute workflow directly without queue service
|
|
435
|
+
* @param options.startNode - Starting node ID for graph workflows (from wire config)
|
|
241
436
|
*/
|
|
242
437
|
public async startWorkflow<I>(
|
|
243
438
|
name: string,
|
|
244
439
|
input: I,
|
|
245
|
-
|
|
440
|
+
wire: WorkflowRunWire,
|
|
441
|
+
rpcService: any,
|
|
442
|
+
options?: { inline?: boolean; startNode?: string }
|
|
246
443
|
): Promise<{ runId: string }> {
|
|
247
|
-
|
|
444
|
+
// Check meta to determine workflow type
|
|
445
|
+
const meta = pikkuState(null, 'workflows', 'meta')
|
|
446
|
+
const workflowMeta = meta[name]
|
|
447
|
+
|
|
448
|
+
if (!workflowMeta) {
|
|
449
|
+
throw new WorkflowNotFoundError(name)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Check if this is a graph workflow (source === 'graph')
|
|
453
|
+
if (workflowMeta.source === 'graph') {
|
|
454
|
+
const shouldInline =
|
|
455
|
+
options?.inline || !this.singletonServices?.queueService
|
|
456
|
+
return runWorkflowGraph(
|
|
457
|
+
this,
|
|
458
|
+
name,
|
|
459
|
+
input,
|
|
460
|
+
rpcService,
|
|
461
|
+
shouldInline,
|
|
462
|
+
options?.startNode,
|
|
463
|
+
wire
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// DSL workflow - check registration exists
|
|
468
|
+
const registrations = pikkuState(null, 'workflows', 'registrations')
|
|
248
469
|
const workflow = registrations.get(name)
|
|
249
470
|
|
|
250
471
|
if (!workflow) {
|
|
251
472
|
throw new WorkflowNotFoundError(name)
|
|
252
473
|
}
|
|
253
474
|
|
|
254
|
-
|
|
255
|
-
|
|
475
|
+
if (!workflowMeta.graphHash) {
|
|
476
|
+
throw new Error(`Missing workflow graphHash for '${name}'`)
|
|
477
|
+
}
|
|
256
478
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
479
|
+
const runId = await this.createRun(
|
|
480
|
+
name,
|
|
481
|
+
input,
|
|
482
|
+
options?.inline ?? false,
|
|
483
|
+
workflowMeta.graphHash,
|
|
484
|
+
wire
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
if (options?.inline) {
|
|
488
|
+
this.inlineRuns.add(runId)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (options?.inline || !this.singletonServices?.queueService) {
|
|
492
|
+
this.runWorkflowJob(runId, rpcService)
|
|
493
|
+
.catch(() => {})
|
|
494
|
+
.finally(() => {
|
|
495
|
+
if (options?.inline) {
|
|
496
|
+
this.inlineRuns.delete(runId)
|
|
497
|
+
}
|
|
498
|
+
})
|
|
262
499
|
} else {
|
|
263
|
-
|
|
264
|
-
await this.runWorkflowJob(runId, rpcService)
|
|
500
|
+
await this.resumeWorkflow(runId)
|
|
265
501
|
}
|
|
266
502
|
|
|
267
503
|
return { runId }
|
|
268
504
|
}
|
|
269
505
|
|
|
506
|
+
public async runToCompletion<I>(
|
|
507
|
+
name: string,
|
|
508
|
+
input: I,
|
|
509
|
+
rpcService: any,
|
|
510
|
+
options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
|
|
511
|
+
): Promise<any> {
|
|
512
|
+
const pollInterval = options?.pollIntervalMs ?? 1000
|
|
513
|
+
const { runId } = await this.startWorkflow(
|
|
514
|
+
name,
|
|
515
|
+
input,
|
|
516
|
+
options?.wire ?? { type: 'internal' },
|
|
517
|
+
rpcService,
|
|
518
|
+
{ inline: true }
|
|
519
|
+
)
|
|
520
|
+
while (true) {
|
|
521
|
+
const run = await this.getRun(runId)
|
|
522
|
+
if (!run) {
|
|
523
|
+
throw new WorkflowRunNotFoundError(runId)
|
|
524
|
+
}
|
|
525
|
+
if (WORKFLOW_END_STATES.has(run.status)) {
|
|
526
|
+
if (run.status === 'failed') {
|
|
527
|
+
throw new Error(run.error?.message || 'Workflow failed')
|
|
528
|
+
}
|
|
529
|
+
if (run.status === 'cancelled') {
|
|
530
|
+
throw new Error('Workflow was cancelled')
|
|
531
|
+
}
|
|
532
|
+
return run.output
|
|
533
|
+
}
|
|
534
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval))
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
270
538
|
public async runWorkflowJob(runId: string, rpcService: any): Promise<void> {
|
|
271
539
|
const run = await this.getRun(runId)
|
|
272
540
|
if (!run) {
|
|
273
|
-
throw new
|
|
541
|
+
throw new WorkflowRunNotFoundError(runId)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const meta = pikkuState(null, 'workflows', 'meta')
|
|
545
|
+
const workflowMeta = meta[run.workflow]
|
|
546
|
+
|
|
547
|
+
if (
|
|
548
|
+
run.graphHash &&
|
|
549
|
+
workflowMeta?.graphHash &&
|
|
550
|
+
run.graphHash !== workflowMeta.graphHash
|
|
551
|
+
) {
|
|
552
|
+
await this.runVersionMismatchFallback(run, workflowMeta, rpcService)
|
|
553
|
+
return
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (workflowMeta?.source === 'graph') {
|
|
557
|
+
await continueGraph(this, runId, run.workflow)
|
|
558
|
+
return
|
|
274
559
|
}
|
|
275
560
|
|
|
276
|
-
const registrations = pikkuState('workflows', 'registrations')
|
|
561
|
+
const registrations = pikkuState(null, 'workflows', 'registrations')
|
|
277
562
|
const workflow = registrations.get(run.workflow)
|
|
278
563
|
if (!workflow) {
|
|
279
564
|
throw new WorkflowNotFoundError(run.workflow)
|
|
280
565
|
}
|
|
281
566
|
|
|
282
567
|
await this.withRunLock(runId, async () => {
|
|
283
|
-
const meta = pikkuState('workflows', 'meta')
|
|
284
|
-
const workflowMeta = meta[run.workflow]
|
|
285
|
-
|
|
286
568
|
const workflowWire = this.createWorkflowWire(
|
|
287
569
|
run.workflow,
|
|
288
570
|
runId,
|
|
@@ -292,8 +574,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
292
574
|
try {
|
|
293
575
|
const result = await runPikkuFunc(
|
|
294
576
|
'workflow',
|
|
295
|
-
workflowMeta.
|
|
296
|
-
workflowMeta.
|
|
577
|
+
workflowMeta.name,
|
|
578
|
+
workflowMeta.pikkuFuncId,
|
|
297
579
|
{
|
|
298
580
|
singletonServices: this.singletonServices!,
|
|
299
581
|
wire,
|
|
@@ -305,13 +587,24 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
305
587
|
await this.updateRunStatus(runId, 'completed', result)
|
|
306
588
|
} catch (error: any) {
|
|
307
589
|
if (error instanceof WorkflowAsyncException) {
|
|
308
|
-
// Normal - workflow paused for step execution
|
|
309
590
|
throw error
|
|
310
591
|
}
|
|
311
592
|
|
|
312
|
-
// Check if it's a WorkflowCancelledException
|
|
313
593
|
if (error instanceof WorkflowCancelledException) {
|
|
314
|
-
|
|
594
|
+
await this.updateRunStatus(runId, 'cancelled', undefined, {
|
|
595
|
+
message: error.message || 'Workflow cancelled',
|
|
596
|
+
stack: '',
|
|
597
|
+
code: 'WORKFLOW_CANCELLED',
|
|
598
|
+
})
|
|
599
|
+
throw error
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (error instanceof WorkflowSuspendedException) {
|
|
603
|
+
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
604
|
+
message: error.message || 'Workflow suspended',
|
|
605
|
+
stack: '',
|
|
606
|
+
code: 'WORKFLOW_SUSPENDED',
|
|
607
|
+
})
|
|
315
608
|
throw error
|
|
316
609
|
}
|
|
317
610
|
|
|
@@ -326,6 +619,35 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
326
619
|
})
|
|
327
620
|
}
|
|
328
621
|
|
|
622
|
+
private async runVersionMismatchFallback(
|
|
623
|
+
run: WorkflowRun,
|
|
624
|
+
currentMeta: { source: string },
|
|
625
|
+
rpcService: any
|
|
626
|
+
): Promise<void> {
|
|
627
|
+
const source = currentMeta.source
|
|
628
|
+
|
|
629
|
+
if (source === 'complex') {
|
|
630
|
+
await this.updateRunStatus(run.id, 'failed', undefined, {
|
|
631
|
+
message: `Workflow '${run.workflow}' definition changed. Complex workflows with inline steps cannot be migrated.`,
|
|
632
|
+
stack: '',
|
|
633
|
+
code: 'VERSION_CONFLICT',
|
|
634
|
+
})
|
|
635
|
+
return
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const version = await this.getWorkflowVersion(run.workflow, run.graphHash!)
|
|
639
|
+
if (!version) {
|
|
640
|
+
await this.updateRunStatus(run.id, 'failed', undefined, {
|
|
641
|
+
message: `Workflow '${run.workflow}' version '${run.graphHash}' not found. Cannot resume with changed definition.`,
|
|
642
|
+
stack: '',
|
|
643
|
+
code: 'VERSION_NOT_FOUND',
|
|
644
|
+
})
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
await runFromMeta(this, run.id, version.graph, rpcService)
|
|
649
|
+
}
|
|
650
|
+
|
|
329
651
|
/**
|
|
330
652
|
* Execute a single workflow step (called by worker)
|
|
331
653
|
* Handles idempotency, RPC execution, result storage, retry logic, and orchestrator triggering
|
|
@@ -358,31 +680,58 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
358
680
|
stepState = await this.createRetryAttempt(stepState.stepId, 'running')
|
|
359
681
|
}
|
|
360
682
|
|
|
361
|
-
if (stepState.status
|
|
362
|
-
// Mark step as running
|
|
683
|
+
if (stepState.status === 'pending') {
|
|
684
|
+
// Mark pending step as running before execution
|
|
363
685
|
await this.setStepRunning(stepState.stepId)
|
|
364
686
|
}
|
|
365
687
|
|
|
366
688
|
try {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
689
|
+
let result: any
|
|
690
|
+
|
|
691
|
+
const run = await this.getRun(runId)
|
|
692
|
+
if (!run) {
|
|
693
|
+
throw new Error(`Workflow run not found: ${runId}`)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const meta = pikkuState(null, 'workflows', 'meta')
|
|
697
|
+
const workflowMeta = meta[run.workflow]
|
|
698
|
+
|
|
699
|
+
if (workflowMeta?.nodes && stepName in workflowMeta.nodes) {
|
|
700
|
+
result = await executeGraphStep(
|
|
701
|
+
this,
|
|
702
|
+
rpcService,
|
|
370
703
|
runId,
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
704
|
+
stepState.stepId,
|
|
705
|
+
stepName,
|
|
706
|
+
rpcName,
|
|
707
|
+
data,
|
|
708
|
+
run.workflow
|
|
709
|
+
)
|
|
710
|
+
} else {
|
|
711
|
+
result = await rpcService.rpcWithWire(rpcName, data, {
|
|
712
|
+
workflowStep: {
|
|
713
|
+
runId,
|
|
714
|
+
stepId: stepState.stepId,
|
|
715
|
+
attemptCount: stepState.attemptCount,
|
|
716
|
+
},
|
|
717
|
+
})
|
|
718
|
+
}
|
|
375
719
|
|
|
376
720
|
// Store result and mark succeeded
|
|
377
721
|
await this.setStepResult(stepState.stepId, result)
|
|
378
722
|
|
|
379
723
|
// Resume orchestrator to continue workflow
|
|
380
|
-
|
|
381
|
-
await this.resumeWorkflow(runId)
|
|
382
|
-
} catch (resumeError: any) {
|
|
383
|
-
throw resumeError
|
|
384
|
-
}
|
|
724
|
+
await this.resumeWorkflow(runId)
|
|
385
725
|
} catch (error: any) {
|
|
726
|
+
if (error instanceof RPCNotFoundError) {
|
|
727
|
+
await this.setStepError(stepState.stepId, error)
|
|
728
|
+
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
729
|
+
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
730
|
+
code: 'RPC_NOT_FOUND',
|
|
731
|
+
})
|
|
732
|
+
return
|
|
733
|
+
}
|
|
734
|
+
|
|
386
735
|
// Store error and mark failed
|
|
387
736
|
await this.setStepError(stepState.stepId, error)
|
|
388
737
|
|
|
@@ -414,7 +763,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
414
763
|
} catch (error: any) {
|
|
415
764
|
if (
|
|
416
765
|
error.name === 'WorkflowAsyncException' ||
|
|
417
|
-
error.name === 'WorkflowCancelledException'
|
|
766
|
+
error.name === 'WorkflowCancelledException' ||
|
|
767
|
+
error.name === 'WorkflowSuspendedException'
|
|
418
768
|
) {
|
|
419
769
|
return
|
|
420
770
|
}
|
|
@@ -451,7 +801,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
451
801
|
let stepState: StepState
|
|
452
802
|
try {
|
|
453
803
|
stepState = await this.getStepState(runId, stepName)
|
|
454
|
-
} catch
|
|
804
|
+
} catch {
|
|
455
805
|
// Step doesn't exist - create it
|
|
456
806
|
stepState = await this.insertStepState(
|
|
457
807
|
runId,
|
|
@@ -488,8 +838,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
488
838
|
// Step is pending - schedule it
|
|
489
839
|
await this.setStepScheduled(stepState.stepId)
|
|
490
840
|
|
|
491
|
-
// Enqueue step worker
|
|
492
|
-
if (this.singletonServices!.queueService) {
|
|
841
|
+
// Enqueue step worker (unless inline mode)
|
|
842
|
+
if (!this.isInline(runId) && this.singletonServices!.queueService) {
|
|
493
843
|
// Map step retry options to queue job options
|
|
494
844
|
const retries = stepOptions?.retries ?? 0
|
|
495
845
|
const retryDelay = stepOptions?.retryDelay
|
|
@@ -514,45 +864,60 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
514
864
|
: undefined,
|
|
515
865
|
}
|
|
516
866
|
)
|
|
867
|
+
// Pause workflow - step will callback when done
|
|
868
|
+
throw new WorkflowAsyncException(runId, stepName)
|
|
517
869
|
} else {
|
|
518
|
-
//
|
|
870
|
+
// Inline or no queue service - execute locally with retry loop
|
|
519
871
|
const retries = stepOptions?.retries ?? 0
|
|
520
872
|
const retryDelay = stepOptions?.retryDelay
|
|
521
|
-
|
|
873
|
+
let currentStepState = stepState
|
|
522
874
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
await this.scheduleOrchestratorRetry(runId, retryDelay)
|
|
875
|
+
while (true) {
|
|
876
|
+
try {
|
|
877
|
+
await this.setStepRunning(currentStepState.stepId)
|
|
878
|
+
const result = await rpcService.rpcWithWire(rpcName, data, {
|
|
879
|
+
workflowStep: {
|
|
880
|
+
runId,
|
|
881
|
+
stepId: currentStepState.stepId,
|
|
882
|
+
attemptCount: currentStepState.attemptCount,
|
|
883
|
+
},
|
|
884
|
+
})
|
|
885
|
+
await this.setStepResult(currentStepState.stepId, result)
|
|
886
|
+
return result
|
|
887
|
+
} catch (error: any) {
|
|
888
|
+
if (error instanceof RPCNotFoundError) {
|
|
889
|
+
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
890
|
+
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
891
|
+
code: 'RPC_NOT_FOUND',
|
|
892
|
+
})
|
|
893
|
+
throw error
|
|
894
|
+
}
|
|
544
895
|
|
|
545
|
-
//
|
|
546
|
-
|
|
896
|
+
// Record the error (marks step as failed)
|
|
897
|
+
await this.setStepError(currentStepState.stepId, error)
|
|
898
|
+
|
|
899
|
+
// Check if we should retry
|
|
900
|
+
if (currentStepState.attemptCount < retries) {
|
|
901
|
+
// Create a new pending retry attempt
|
|
902
|
+
currentStepState = await this.createRetryAttempt(
|
|
903
|
+
currentStepState.stepId,
|
|
904
|
+
'pending'
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
// Wait for retry delay if specified
|
|
908
|
+
if (retryDelay) {
|
|
909
|
+
await new Promise((resolve) =>
|
|
910
|
+
setTimeout(resolve, getDurationInMilliseconds(retryDelay))
|
|
911
|
+
)
|
|
912
|
+
}
|
|
913
|
+
// Continue loop to retry
|
|
914
|
+
} else {
|
|
915
|
+
// No more retries, fail the workflow
|
|
916
|
+
throw error
|
|
917
|
+
}
|
|
547
918
|
}
|
|
548
|
-
|
|
549
|
-
// No more retries, fail the workflow
|
|
550
|
-
throw error
|
|
551
919
|
}
|
|
552
920
|
}
|
|
553
|
-
|
|
554
|
-
// Pause workflow - step will callback when done
|
|
555
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
556
921
|
}
|
|
557
922
|
|
|
558
923
|
private async inlineStep(
|
|
@@ -565,13 +930,13 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
565
930
|
let stepState: StepState
|
|
566
931
|
try {
|
|
567
932
|
stepState = await this.getStepState(runId, stepName)
|
|
568
|
-
} catch
|
|
933
|
+
} catch {
|
|
569
934
|
// Step doesn't exist - create it (inline, no RPC)
|
|
570
935
|
stepState = await this.insertStepState(
|
|
571
936
|
runId,
|
|
572
937
|
stepName,
|
|
573
|
-
null,
|
|
574
|
-
null,
|
|
938
|
+
null,
|
|
939
|
+
null,
|
|
575
940
|
stepOptions
|
|
576
941
|
)
|
|
577
942
|
}
|
|
@@ -584,30 +949,68 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
584
949
|
// Execute inline function
|
|
585
950
|
const retries = stepOptions?.retries ?? this.getConfig().retries
|
|
586
951
|
const retryDelay = stepOptions?.retryDelay ?? this.getConfig().retryDelay
|
|
587
|
-
|
|
952
|
+
let currentStepState = stepState
|
|
588
953
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
954
|
+
// Check if we're running inline (in-memory) or remote (queue-based)
|
|
955
|
+
if (this.isInline(runId)) {
|
|
956
|
+
// Inline mode - execute with retry loop
|
|
957
|
+
while (true) {
|
|
958
|
+
try {
|
|
959
|
+
await this.setStepRunning(currentStepState.stepId)
|
|
960
|
+
const result = await fn()
|
|
961
|
+
await this.setStepResult(currentStepState.stepId, result)
|
|
962
|
+
return result
|
|
963
|
+
} catch (error: any) {
|
|
964
|
+
// Record the error (marks step as failed)
|
|
965
|
+
await this.setStepError(currentStepState.stepId, error)
|
|
966
|
+
|
|
967
|
+
// Check if we should retry
|
|
968
|
+
if (currentStepState.attemptCount < retries) {
|
|
969
|
+
// Create a new pending retry attempt
|
|
970
|
+
currentStepState = await this.createRetryAttempt(
|
|
971
|
+
currentStepState.stepId,
|
|
972
|
+
'pending'
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
// Wait for retry delay if specified
|
|
976
|
+
if (retryDelay) {
|
|
977
|
+
await new Promise((resolve) =>
|
|
978
|
+
setTimeout(resolve, getDurationInMilliseconds(retryDelay))
|
|
979
|
+
)
|
|
980
|
+
}
|
|
981
|
+
// Continue loop to retry
|
|
982
|
+
} else {
|
|
983
|
+
// No more retries, fail the workflow
|
|
984
|
+
throw error
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
// Remote mode - use queue-based retry
|
|
990
|
+
try {
|
|
991
|
+
await this.setStepRunning(currentStepState.stepId)
|
|
992
|
+
const result = await fn()
|
|
993
|
+
await this.setStepResult(currentStepState.stepId, result)
|
|
994
|
+
return result
|
|
995
|
+
} catch (error: any) {
|
|
996
|
+
// Record the error (marks step as failed)
|
|
997
|
+
await this.setStepError(currentStepState.stepId, error)
|
|
596
998
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
999
|
+
// Check if we should retry
|
|
1000
|
+
if (currentStepState.attemptCount < retries) {
|
|
1001
|
+
// Create a new pending retry attempt (copies metadata from failed step)
|
|
1002
|
+
await this.createRetryAttempt(currentStepState.stepId, 'pending')
|
|
601
1003
|
|
|
602
|
-
|
|
603
|
-
|
|
1004
|
+
// Schedule orchestrator to retry after delay
|
|
1005
|
+
await this.scheduleOrchestratorRetry(runId, retryDelay)
|
|
604
1006
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
1007
|
+
// Pause workflow - orchestrator will replay and pick up new attempt
|
|
1008
|
+
throw new WorkflowAsyncException(runId, stepName)
|
|
1009
|
+
}
|
|
608
1010
|
|
|
609
|
-
|
|
610
|
-
|
|
1011
|
+
// No more retries, fail the workflow
|
|
1012
|
+
throw error
|
|
1013
|
+
}
|
|
611
1014
|
}
|
|
612
1015
|
}
|
|
613
1016
|
|
|
@@ -616,15 +1019,11 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
616
1019
|
let stepState: StepState
|
|
617
1020
|
try {
|
|
618
1021
|
stepState = await this.getStepState(runId, stepName)
|
|
619
|
-
} catch
|
|
1022
|
+
} catch {
|
|
620
1023
|
// Step doesn't exist - create it (sleep step, no RPC)
|
|
621
|
-
stepState = await this.insertStepState(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
null, // No RPC for sleep steps
|
|
625
|
-
{ duration }, // Store duration as data
|
|
626
|
-
undefined // No retry options for sleep
|
|
627
|
-
)
|
|
1024
|
+
stepState = await this.insertStepState(runId, stepName, null, {
|
|
1025
|
+
duration,
|
|
1026
|
+
})
|
|
628
1027
|
}
|
|
629
1028
|
|
|
630
1029
|
if (stepState.status === 'succeeded') {
|
|
@@ -640,9 +1039,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
640
1039
|
// Step is pending - schedule it
|
|
641
1040
|
await this.setStepScheduled(stepState.stepId)
|
|
642
1041
|
|
|
643
|
-
// Check if
|
|
644
|
-
if (this.singletonServices!.schedulerService) {
|
|
645
|
-
// Remote mode -
|
|
1042
|
+
// Check if inline mode or no scheduler service
|
|
1043
|
+
if (!this.isInline(runId) && this.singletonServices!.schedulerService) {
|
|
1044
|
+
// Remote mode - schedule sleep via scheduler service
|
|
646
1045
|
await this.singletonServices!.schedulerService.scheduleRPC(
|
|
647
1046
|
duration,
|
|
648
1047
|
this.getConfig().sleeperRPCName,
|
|
@@ -651,6 +1050,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
651
1050
|
stepId: stepState.stepId,
|
|
652
1051
|
}
|
|
653
1052
|
)
|
|
1053
|
+
// Pause workflow - sleep will callback when done
|
|
1054
|
+
throw new WorkflowAsyncException(runId, stepName)
|
|
654
1055
|
} else {
|
|
655
1056
|
// Inline mode - use setTimeout with actual duration
|
|
656
1057
|
await new Promise((resolve) =>
|
|
@@ -659,30 +1060,65 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
659
1060
|
await this.setStepResult(stepState.stepId, null)
|
|
660
1061
|
return
|
|
661
1062
|
}
|
|
1063
|
+
}
|
|
662
1064
|
|
|
663
|
-
|
|
664
|
-
|
|
1065
|
+
private getSuspendStepName(reason: string): string {
|
|
1066
|
+
if (!reason) {
|
|
1067
|
+
return '__workflow_suspend'
|
|
1068
|
+
}
|
|
1069
|
+
return '__workflow_suspend'
|
|
665
1070
|
}
|
|
666
1071
|
|
|
667
|
-
private async
|
|
668
|
-
|
|
669
|
-
await this.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
1072
|
+
private async suspendStep(runId: string, reason: string): Promise<void> {
|
|
1073
|
+
const stepName = this.getSuspendStepName(reason)
|
|
1074
|
+
await this.withStepLock(runId, stepName, async () => {
|
|
1075
|
+
let stepState: StepState
|
|
1076
|
+
try {
|
|
1077
|
+
stepState = await this.getStepState(runId, stepName)
|
|
1078
|
+
} catch {
|
|
1079
|
+
stepState = await this.insertStepState(
|
|
1080
|
+
runId,
|
|
1081
|
+
stepName,
|
|
1082
|
+
'pikkuWorkflowSuspend',
|
|
1083
|
+
{
|
|
1084
|
+
reason,
|
|
1085
|
+
}
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1088
|
+
if (!stepState.stepId) {
|
|
1089
|
+
stepState = await this.insertStepState(
|
|
1090
|
+
runId,
|
|
1091
|
+
stepName,
|
|
1092
|
+
'pikkuWorkflowSuspend',
|
|
1093
|
+
{
|
|
1094
|
+
reason,
|
|
1095
|
+
}
|
|
1096
|
+
)
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
if (stepState.status === 'succeeded') {
|
|
1100
|
+
return
|
|
1101
|
+
}
|
|
674
1102
|
|
|
675
|
-
|
|
676
|
-
|
|
1103
|
+
if (stepState.status === 'pending') {
|
|
1104
|
+
await this.setStepRunning(stepState.stepId)
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
await this.setStepResult(stepState.stepId, {
|
|
1108
|
+
reason,
|
|
1109
|
+
suspendedAt: new Date().toISOString(),
|
|
1110
|
+
})
|
|
1111
|
+
throw new WorkflowSuspendedException(runId, reason)
|
|
1112
|
+
})
|
|
677
1113
|
}
|
|
678
1114
|
|
|
679
1115
|
private createWorkflowWire(
|
|
680
|
-
|
|
1116
|
+
name: string,
|
|
681
1117
|
runId: string,
|
|
682
1118
|
rpcService: any
|
|
683
1119
|
): PikkuWorkflowWire {
|
|
684
1120
|
const workflowWire: PikkuWorkflowWire = {
|
|
685
|
-
|
|
1121
|
+
name,
|
|
686
1122
|
runId,
|
|
687
1123
|
getRun: async () => (await this.getRun(runId)) as WorkflowRun,
|
|
688
1124
|
|
|
@@ -723,9 +1159,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
723
1159
|
)
|
|
724
1160
|
},
|
|
725
1161
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
await this.cancelWorkflow(runId, reason)
|
|
1162
|
+
suspend: async (reason: string) => {
|
|
1163
|
+
await this.suspendStep(runId, reason || 'Workflow suspended')
|
|
729
1164
|
},
|
|
730
1165
|
}
|
|
731
1166
|
return workflowWire
|