@pikku/core 0.12.2 → 0.12.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/CHANGELOG.md +24 -0
- package/dist/crypto-utils.d.ts +10 -0
- package/dist/crypto-utils.js +62 -0
- package/dist/function/function-runner.js +4 -4
- package/dist/function/functions.types.d.ts +26 -1
- package/dist/function/functions.types.js +16 -0
- package/dist/handle-error.d.ts +1 -1
- package/dist/handle-error.js +7 -3
- package/dist/index.d.ts +14 -2
- package/dist/index.js +1 -1
- package/dist/pikku-state.js +3 -1
- package/dist/schema.js +1 -1
- package/dist/services/in-memory-workflow-service.d.ts +22 -3
- package/dist/services/in-memory-workflow-service.js +77 -1
- package/dist/services/workflow-service.d.ts +8 -2
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/service-tests.d.ts +31 -0
- package/dist/testing/service-tests.js +598 -0
- package/dist/types/core.types.d.ts +9 -1
- package/dist/types/state.types.d.ts +4 -2
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-memory.js +6 -5
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
- package/dist/wirings/ai-agent/ai-agent-prepare.js +127 -47
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +109 -46
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +22 -1
- package/dist/wirings/ai-agent/index.d.ts +1 -2
- package/dist/wirings/ai-agent/index.js +1 -2
- package/dist/wirings/channel/channel-handler.js +5 -1
- package/dist/wirings/channel/channel.types.d.ts +1 -7
- package/dist/wirings/channel/local/local-channel-handler.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-handler.js +5 -3
- package/dist/wirings/channel/local/local-channel-runner.js +14 -4
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -1
- package/dist/wirings/cli/cli-runner.js +10 -2
- package/dist/wirings/cli/cli.types.d.ts +1 -0
- package/dist/wirings/http/http-routes.js +0 -1
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +25 -3
- package/dist/wirings/http/http.types.d.ts +3 -11
- package/dist/wirings/http/index.d.ts +2 -1
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
- package/dist/wirings/http/web-request.d.ts +12 -0
- package/dist/wirings/http/web-request.js +85 -0
- package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
- package/dist/wirings/mcp/mcp-runner.js +13 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
- package/dist/wirings/rpc/rpc-runner.js +36 -14
- package/dist/wirings/rpc/rpc-types.d.ts +6 -1
- package/dist/wirings/rpc/wire-addon.d.ts +1 -0
- package/dist/wirings/rpc/wire-addon.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +1 -0
- package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
- package/dist/wirings/workflow/graph/graph-validation.js +175 -0
- package/dist/wirings/workflow/graph/index.d.ts +1 -0
- package/dist/wirings/workflow/graph/index.js +1 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
- package/dist/wirings/workflow/workflow.types.d.ts +10 -1
- package/lcov.info +18891 -0
- package/package.json +4 -2
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -168
- package/src/crypto-utils.test.ts +0 -116
- package/src/crypto-utils.ts +0 -99
- package/src/errors/error-handler.ts +0 -62
- package/src/errors/error.test.ts +0 -195
- package/src/errors/errors.ts +0 -374
- package/src/errors/index.ts +0 -2
- package/src/factory-functions.test.ts +0 -109
- package/src/function/function-runner.test.ts +0 -536
- package/src/function/function-runner.ts +0 -365
- package/src/function/functions.types.ts +0 -267
- package/src/function/index.ts +0 -5
- package/src/handle-error.test.ts +0 -361
- package/src/handle-error.ts +0 -65
- package/src/index.ts +0 -104
- package/src/internal.ts +0 -6
- package/src/middleware/auth-apikey.test.ts +0 -363
- package/src/middleware/auth-apikey.ts +0 -47
- package/src/middleware/auth-bearer.test.ts +0 -450
- package/src/middleware/auth-bearer.ts +0 -72
- package/src/middleware/auth-cookie.test.ts +0 -528
- package/src/middleware/auth-cookie.ts +0 -80
- package/src/middleware/cors.test.ts +0 -424
- package/src/middleware/cors.ts +0 -104
- package/src/middleware/index.ts +0 -5
- package/src/middleware/remote-auth.test.ts +0 -488
- package/src/middleware/remote-auth.ts +0 -68
- package/src/middleware/timeout.ts +0 -15
- package/src/middleware-runner.test.ts +0 -418
- package/src/middleware-runner.ts +0 -234
- package/src/permissions.test.ts +0 -434
- package/src/permissions.ts +0 -319
- package/src/pikku-request.ts +0 -23
- package/src/pikku-response.ts +0 -5
- package/src/pikku-state.test.ts +0 -224
- package/src/pikku-state.ts +0 -214
- package/src/run-tests-script.test.ts +0 -49
- package/src/schema.test.ts +0 -249
- package/src/schema.ts +0 -151
- package/src/services/ai-agent-runner-service.ts +0 -41
- package/src/services/ai-run-state-service.ts +0 -20
- package/src/services/ai-storage-service.ts +0 -39
- package/src/services/content-service.ts +0 -76
- package/src/services/deployment-service.ts +0 -22
- package/src/services/gateway-service.ts +0 -20
- package/src/services/gopass-secrets.ts +0 -98
- package/src/services/in-memory-ai-run-state-service.ts +0 -73
- package/src/services/in-memory-trigger-service.ts +0 -64
- package/src/services/in-memory-workflow-service.test.ts +0 -351
- package/src/services/in-memory-workflow-service.ts +0 -395
- package/src/services/index.ts +0 -56
- package/src/services/jwt-service.ts +0 -30
- package/src/services/local-content.ts +0 -120
- package/src/services/local-gateway-service.ts +0 -62
- package/src/services/local-secrets.test.ts +0 -80
- package/src/services/local-secrets.ts +0 -94
- package/src/services/local-variables.test.ts +0 -61
- package/src/services/local-variables.ts +0 -39
- package/src/services/logger-console.test.ts +0 -118
- package/src/services/logger-console.ts +0 -98
- package/src/services/logger.ts +0 -57
- package/src/services/scheduler-service.ts +0 -86
- package/src/services/schema-service.ts +0 -33
- package/src/services/scoped-secret-service.test.ts +0 -74
- package/src/services/scoped-secret-service.ts +0 -41
- package/src/services/secret-service.ts +0 -38
- package/src/services/trigger-service.ts +0 -17
- package/src/services/typed-secret-service.test.ts +0 -93
- package/src/services/typed-secret-service.ts +0 -70
- package/src/services/typed-variables-service.test.ts +0 -73
- package/src/services/typed-variables-service.ts +0 -81
- package/src/services/user-session-service.test.ts +0 -113
- package/src/services/user-session-service.ts +0 -86
- package/src/services/variables-service.ts +0 -11
- package/src/services/workflow-service.ts +0 -84
- package/src/time-utils.test.ts +0 -56
- package/src/time-utils.ts +0 -107
- package/src/types/core.types.ts +0 -470
- package/src/types/state.types.ts +0 -186
- package/src/utils.test.ts +0 -350
- package/src/utils.ts +0 -129
- package/src/version.test.ts +0 -80
- package/src/version.ts +0 -25
- package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -238
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +0 -152
- package/src/wirings/ai-agent/ai-agent-helpers.ts +0 -76
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -309
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +0 -115
- package/src/wirings/ai-agent/ai-agent-model-config.ts +0 -43
- package/src/wirings/ai-agent/ai-agent-prepare.ts +0 -522
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +0 -37
- package/src/wirings/ai-agent/ai-agent-registry.ts +0 -82
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +0 -317
- package/src/wirings/ai-agent/ai-agent-runner.ts +0 -251
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +0 -652
- package/src/wirings/ai-agent/ai-agent-stream.ts +0 -1060
- package/src/wirings/ai-agent/ai-agent.types.ts +0 -360
- package/src/wirings/ai-agent/index.ts +0 -41
- package/src/wirings/channel/channel-common.ts +0 -90
- package/src/wirings/channel/channel-handler.ts +0 -241
- package/src/wirings/channel/channel-middleware-runner.ts +0 -120
- package/src/wirings/channel/channel-runner.ts +0 -166
- package/src/wirings/channel/channel-store.ts +0 -29
- package/src/wirings/channel/channel.types.ts +0 -178
- package/src/wirings/channel/define-channel-routes.ts +0 -25
- package/src/wirings/channel/eventhub-service.ts +0 -34
- package/src/wirings/channel/eventhub-store.ts +0 -13
- package/src/wirings/channel/index.ts +0 -24
- package/src/wirings/channel/local/index.ts +0 -3
- package/src/wirings/channel/local/local-channel-handler.ts +0 -79
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -215
- package/src/wirings/channel/local/local-channel-runner.ts +0 -200
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -95
- package/src/wirings/channel/local/local-eventhub-service.ts +0 -101
- package/src/wirings/channel/log-channels.ts +0 -20
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -54
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -45
- package/src/wirings/channel/serverless/index.ts +0 -5
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -286
- package/src/wirings/cli/channel/cli-channel-runner.ts +0 -136
- package/src/wirings/cli/channel/index.ts +0 -1
- package/src/wirings/cli/cli-runner.test.ts +0 -403
- package/src/wirings/cli/cli-runner.ts +0 -519
- package/src/wirings/cli/cli.types.ts +0 -357
- package/src/wirings/cli/command-parser.test.ts +0 -445
- package/src/wirings/cli/command-parser.ts +0 -504
- package/src/wirings/cli/define-cli-commands.ts +0 -24
- package/src/wirings/cli/index.ts +0 -19
- package/src/wirings/gateway/gateway-runner.test.ts +0 -474
- package/src/wirings/gateway/gateway-runner.ts +0 -411
- package/src/wirings/gateway/gateway.types.ts +0 -149
- package/src/wirings/gateway/index.ts +0 -13
- package/src/wirings/http/http-routes.test.ts +0 -322
- package/src/wirings/http/http-routes.ts +0 -198
- package/src/wirings/http/http-runner.test.ts +0 -144
- package/src/wirings/http/http-runner.ts +0 -563
- package/src/wirings/http/http.types.ts +0 -378
- package/src/wirings/http/index.ts +0 -25
- package/src/wirings/http/log-http-routes.ts +0 -22
- package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -237
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -166
- package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -82
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -147
- package/src/wirings/http/routers/http-router.ts +0 -13
- package/src/wirings/http/routers/path-to-regex.test.ts +0 -319
- package/src/wirings/http/routers/path-to-regex.ts +0 -126
- package/src/wirings/mcp/index.ts +0 -27
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -389
- package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
- package/src/wirings/mcp/mcp-runner.ts +0 -305
- package/src/wirings/mcp/mcp.types.ts +0 -216
- package/src/wirings/node/index.ts +0 -2
- package/src/wirings/node/node.types.ts +0 -23
- package/src/wirings/oauth2/index.ts +0 -3
- package/src/wirings/oauth2/oauth2-client.test.ts +0 -929
- package/src/wirings/oauth2/oauth2-client.ts +0 -335
- package/src/wirings/oauth2/oauth2.types.ts +0 -69
- package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
- package/src/wirings/queue/index.ts +0 -31
- package/src/wirings/queue/queue-runner.test.ts +0 -710
- package/src/wirings/queue/queue-runner.ts +0 -192
- package/src/wirings/queue/queue.types.ts +0 -189
- package/src/wirings/queue/register-queue-helper.ts +0 -60
- package/src/wirings/queue/validate-worker-config.test.ts +0 -108
- package/src/wirings/queue/validate-worker-config.ts +0 -116
- package/src/wirings/rpc/index.ts +0 -4
- package/src/wirings/rpc/rpc-runner.ts +0 -439
- package/src/wirings/rpc/rpc-types.ts +0 -51
- package/src/wirings/rpc/wire-addon.ts +0 -20
- package/src/wirings/scheduler/index.ts +0 -11
- package/src/wirings/scheduler/log-schedulers.ts +0 -20
- package/src/wirings/scheduler/scheduler-runner.test.ts +0 -660
- package/src/wirings/scheduler/scheduler-runner.ts +0 -132
- package/src/wirings/scheduler/scheduler.types.ts +0 -53
- package/src/wirings/secret/index.ts +0 -9
- package/src/wirings/secret/secret.types.ts +0 -32
- package/src/wirings/secret/validate-secret-definitions.test.ts +0 -140
- package/src/wirings/secret/validate-secret-definitions.ts +0 -82
- package/src/wirings/trigger/index.ts +0 -10
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -112
- package/src/wirings/trigger/trigger-runner.test.ts +0 -79
- package/src/wirings/trigger/trigger-runner.ts +0 -135
- package/src/wirings/trigger/trigger.types.ts +0 -178
- package/src/wirings/variable/index.ts +0 -8
- package/src/wirings/variable/validate-variable-definitions.test.ts +0 -91
- package/src/wirings/variable/validate-variable-definitions.ts +0 -69
- package/src/wirings/variable/variable.types.ts +0 -22
- package/src/wirings/workflow/dsl/index.ts +0 -31
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -320
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -28
- package/src/wirings/workflow/graph/graph-node.ts +0 -222
- package/src/wirings/workflow/graph/graph-runner.test.ts +0 -487
- package/src/wirings/workflow/graph/graph-runner.ts +0 -816
- package/src/wirings/workflow/graph/index.ts +0 -14
- package/src/wirings/workflow/graph/template.test.ts +0 -49
- package/src/wirings/workflow/graph/template.ts +0 -42
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -29
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -104
- package/src/wirings/workflow/index.ts +0 -81
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -200
- package/src/wirings/workflow/pikku-workflow-service.ts +0 -1165
- package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
- package/src/wirings/workflow/workflow-helpers.ts +0 -79
- package/src/wirings/workflow/workflow.types.ts +0 -266
- package/tsconfig.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,816 +0,0 @@
|
|
|
1
|
-
import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
|
|
2
|
-
import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
|
|
3
|
-
import { pikkuState } from '../../../pikku-state.js'
|
|
4
|
-
import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
|
|
5
|
-
import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
|
|
6
|
-
|
|
7
|
-
function buildTemplateRegex(nodeId: string): RegExp | null {
|
|
8
|
-
if (!nodeId.includes('${')) return null
|
|
9
|
-
const escaped = nodeId
|
|
10
|
-
.split(/\$\{[^}]+\}/)
|
|
11
|
-
.map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
12
|
-
.join('.+')
|
|
13
|
-
return new RegExp(`^${escaped}$`)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function remapStepNamesToNodeIds(
|
|
17
|
-
stepNames: string[],
|
|
18
|
-
nodes: Record<string, any>,
|
|
19
|
-
graphName: string
|
|
20
|
-
): string[] {
|
|
21
|
-
const templatePatterns = new Map<string, RegExp>()
|
|
22
|
-
for (const nodeId of Object.keys(nodes)) {
|
|
23
|
-
const regex = buildTemplateRegex(nodeId)
|
|
24
|
-
if (regex) templatePatterns.set(nodeId, regex)
|
|
25
|
-
}
|
|
26
|
-
if (templatePatterns.size === 0) return stepNames
|
|
27
|
-
return stepNames.map((name) => {
|
|
28
|
-
if (nodes[name]) return name
|
|
29
|
-
const matches: string[] = []
|
|
30
|
-
for (const [nodeId, regex] of templatePatterns) {
|
|
31
|
-
if (regex.test(name)) matches.push(nodeId)
|
|
32
|
-
}
|
|
33
|
-
if (matches.length > 1) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
`Workflow graph '${graphName}': ambiguous template node match for '${name}' (${matches.join(', ')})`
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
if (matches.length === 1) {
|
|
39
|
-
return matches[0]!
|
|
40
|
-
}
|
|
41
|
-
return name
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function remapBranchKeys(
|
|
46
|
-
branchKeys: Record<string, string>,
|
|
47
|
-
nodes: Record<string, any>,
|
|
48
|
-
graphName: string
|
|
49
|
-
): Record<string, string> {
|
|
50
|
-
const templatePatterns = new Map<string, RegExp>()
|
|
51
|
-
for (const nodeId of Object.keys(nodes)) {
|
|
52
|
-
const regex = buildTemplateRegex(nodeId)
|
|
53
|
-
if (regex) templatePatterns.set(nodeId, regex)
|
|
54
|
-
}
|
|
55
|
-
if (templatePatterns.size === 0) return branchKeys
|
|
56
|
-
const remapped: Record<string, string> = {}
|
|
57
|
-
for (const [key, value] of Object.entries(branchKeys)) {
|
|
58
|
-
let mappedKey = key
|
|
59
|
-
if (!nodes[key]) {
|
|
60
|
-
const matches: string[] = []
|
|
61
|
-
for (const [nodeId, regex] of templatePatterns) {
|
|
62
|
-
if (regex.test(key)) matches.push(nodeId)
|
|
63
|
-
}
|
|
64
|
-
if (matches.length > 1) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Workflow graph '${graphName}': ambiguous template branch key match for '${key}' (${matches.join(', ')})`
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
if (matches.length === 1) {
|
|
70
|
-
mappedKey = matches[0]!
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
remapped[mappedKey] = value
|
|
74
|
-
}
|
|
75
|
-
return remapped
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function isDataRef(value: unknown): value is { $ref: string; path?: string } {
|
|
79
|
-
return (
|
|
80
|
-
typeof value === 'object' &&
|
|
81
|
-
value !== null &&
|
|
82
|
-
'$ref' in value &&
|
|
83
|
-
typeof (value as any).$ref === 'string'
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface TemplateValue {
|
|
88
|
-
$template: {
|
|
89
|
-
parts: string[]
|
|
90
|
-
expressions: Array<{ $ref: string; path?: string }>
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function isTemplate(value: unknown): value is TemplateValue {
|
|
95
|
-
return (
|
|
96
|
-
typeof value === 'object' &&
|
|
97
|
-
value !== null &&
|
|
98
|
-
'$template' in value &&
|
|
99
|
-
typeof (value as any).$template === 'object'
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function getWorkflowMeta(name: string): WorkflowRuntimeMeta | undefined {
|
|
104
|
-
const meta = pikkuState(null, 'workflows', 'meta')
|
|
105
|
-
return meta[name]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function resolveNextFromConfig(next: unknown, branchKey?: string): string[] {
|
|
109
|
-
if (!next) return []
|
|
110
|
-
|
|
111
|
-
if (typeof next === 'string') return [next]
|
|
112
|
-
if (Array.isArray(next)) return next
|
|
113
|
-
|
|
114
|
-
if (typeof next === 'object' && next !== null) {
|
|
115
|
-
if (!branchKey || !(branchKey in next)) return []
|
|
116
|
-
const branchNext = (next as Record<string, string | string[]>)[branchKey]!
|
|
117
|
-
return Array.isArray(branchNext) ? branchNext : [branchNext]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return []
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function getValueAtPath(obj: any, path: string): any {
|
|
124
|
-
if (!path) return obj
|
|
125
|
-
const parts = path.split('.')
|
|
126
|
-
let current = obj
|
|
127
|
-
for (const part of parts) {
|
|
128
|
-
if (current == null) return undefined
|
|
129
|
-
current = current[part]
|
|
130
|
-
}
|
|
131
|
-
return current
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function resolveTemplate(
|
|
135
|
-
template: TemplateValue,
|
|
136
|
-
nodeResults: Record<string, any>
|
|
137
|
-
): string {
|
|
138
|
-
const { parts, expressions } = template.$template
|
|
139
|
-
let result = ''
|
|
140
|
-
for (let i = 0; i < parts.length; i++) {
|
|
141
|
-
result += parts[i]
|
|
142
|
-
if (i < expressions.length) {
|
|
143
|
-
const expr = expressions[i]!
|
|
144
|
-
const nodeResult = nodeResults[expr.$ref]
|
|
145
|
-
const value = expr.path
|
|
146
|
-
? getValueAtPath(nodeResult, expr.path)
|
|
147
|
-
: nodeResult
|
|
148
|
-
result += String(value ?? '')
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return result
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function resolveValue(value: unknown, nodeResults: Record<string, any>): any {
|
|
155
|
-
if (isDataRef(value)) {
|
|
156
|
-
const source = nodeResults[value.$ref]
|
|
157
|
-
return value.path ? getValueAtPath(source, value.path) : source
|
|
158
|
-
}
|
|
159
|
-
if (isTemplate(value)) {
|
|
160
|
-
return resolveTemplate(value, nodeResults)
|
|
161
|
-
}
|
|
162
|
-
if (Array.isArray(value)) {
|
|
163
|
-
return value.map((item) => resolveValue(item, nodeResults))
|
|
164
|
-
}
|
|
165
|
-
if (typeof value === 'object' && value !== null) {
|
|
166
|
-
const resolved: Record<string, any> = {}
|
|
167
|
-
for (const [k, v] of Object.entries(value)) {
|
|
168
|
-
resolved[k] = resolveValue(v, nodeResults)
|
|
169
|
-
}
|
|
170
|
-
return resolved
|
|
171
|
-
}
|
|
172
|
-
return value
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function resolveSerializedInput(
|
|
176
|
-
input: Record<string, unknown> | undefined,
|
|
177
|
-
nodeResults: Record<string, any>
|
|
178
|
-
): Record<string, any> {
|
|
179
|
-
if (!input || Object.keys(input).length === 0) return {}
|
|
180
|
-
|
|
181
|
-
const resolved: Record<string, any> = {}
|
|
182
|
-
for (const [key, value] of Object.entries(input)) {
|
|
183
|
-
resolved[key] = resolveValue(value, nodeResults)
|
|
184
|
-
}
|
|
185
|
-
return resolved
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function collectReferencedNodeIds(value: unknown, nodeIds: string[]): void {
|
|
189
|
-
if (isDataRef(value)) {
|
|
190
|
-
nodeIds.push(value.$ref)
|
|
191
|
-
} else if (isTemplate(value)) {
|
|
192
|
-
for (const expr of value.$template.expressions) {
|
|
193
|
-
nodeIds.push(expr.$ref)
|
|
194
|
-
}
|
|
195
|
-
} else if (Array.isArray(value)) {
|
|
196
|
-
for (const item of value) {
|
|
197
|
-
collectReferencedNodeIds(item, nodeIds)
|
|
198
|
-
}
|
|
199
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
200
|
-
for (const v of Object.values(value)) {
|
|
201
|
-
collectReferencedNodeIds(v, nodeIds)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function extractReferencedNodeIds(
|
|
207
|
-
input: Record<string, unknown> | undefined
|
|
208
|
-
): string[] {
|
|
209
|
-
if (!input) return []
|
|
210
|
-
const nodeIds: string[] = []
|
|
211
|
-
for (const value of Object.values(input)) {
|
|
212
|
-
collectReferencedNodeIds(value, nodeIds)
|
|
213
|
-
}
|
|
214
|
-
return [...new Set(nodeIds)]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const IGNORED_REFS = new Set(['trigger', '$item', 'unknown'])
|
|
218
|
-
|
|
219
|
-
function normalizeNodeTargets(value: unknown): string[] {
|
|
220
|
-
if (!value) return []
|
|
221
|
-
if (typeof value === 'string') return [value]
|
|
222
|
-
if (Array.isArray(value)) return value.filter((v) => typeof v === 'string')
|
|
223
|
-
if (typeof value === 'object') {
|
|
224
|
-
const targets: string[] = []
|
|
225
|
-
for (const branchTarget of Object.values(
|
|
226
|
-
value as Record<string, unknown>
|
|
227
|
-
)) {
|
|
228
|
-
targets.push(...normalizeNodeTargets(branchTarget))
|
|
229
|
-
}
|
|
230
|
-
return targets
|
|
231
|
-
}
|
|
232
|
-
return []
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function validateGraphReferences(
|
|
236
|
-
graphName: string,
|
|
237
|
-
nodes: Record<string, any>,
|
|
238
|
-
entryNodes: string[]
|
|
239
|
-
): void {
|
|
240
|
-
const nodeIds = new Set(Object.keys(nodes))
|
|
241
|
-
|
|
242
|
-
for (const entryId of entryNodes) {
|
|
243
|
-
if (!nodeIds.has(entryId)) {
|
|
244
|
-
throw new Error(
|
|
245
|
-
`Workflow graph '${graphName}': entry node '${entryId}' is not defined`
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
251
|
-
const inputRefs = extractReferencedNodeIds(node.input).filter(
|
|
252
|
-
(id) => !IGNORED_REFS.has(id)
|
|
253
|
-
)
|
|
254
|
-
for (const refId of inputRefs) {
|
|
255
|
-
if (!nodeIds.has(refId)) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const nextTargets = normalizeNodeTargets(node.next)
|
|
263
|
-
for (const nextId of nextTargets) {
|
|
264
|
-
if (!nodeIds.has(nextId)) {
|
|
265
|
-
throw new Error(
|
|
266
|
-
`Workflow graph '${graphName}': node '${nodeId}' routes to unknown node '${nextId}'`
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const errorTargets = normalizeNodeTargets(node.onError)
|
|
272
|
-
for (const errorId of errorTargets) {
|
|
273
|
-
if (!nodeIds.has(errorId)) {
|
|
274
|
-
throw new Error(
|
|
275
|
-
`Workflow graph '${graphName}': node '${nodeId}' onError targets unknown node '${errorId}'`
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function areDependenciesSatisfied(
|
|
283
|
-
node: { input?: Record<string, unknown> },
|
|
284
|
-
completedNodeIds: Set<string>
|
|
285
|
-
): boolean {
|
|
286
|
-
const deps = extractReferencedNodeIds(node.input).filter(
|
|
287
|
-
(id) => !IGNORED_REFS.has(id)
|
|
288
|
-
)
|
|
289
|
-
return deps.every((dep) => completedNodeIds.has(dep))
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async function queueGraphNode(
|
|
293
|
-
workflowService: PikkuWorkflowService,
|
|
294
|
-
runId: string,
|
|
295
|
-
_graphName: string,
|
|
296
|
-
nodeId: string,
|
|
297
|
-
rpcName: string,
|
|
298
|
-
input: any
|
|
299
|
-
): Promise<void> {
|
|
300
|
-
await workflowService.insertStepState(runId, nodeId, rpcName, input, {
|
|
301
|
-
retries: 0,
|
|
302
|
-
})
|
|
303
|
-
await workflowService.queueStepWorker(runId, nodeId, rpcName, input)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export async function continueGraph(
|
|
307
|
-
workflowService: PikkuWorkflowService,
|
|
308
|
-
runId: string,
|
|
309
|
-
graphName: string,
|
|
310
|
-
overrideMeta?: WorkflowRuntimeMeta
|
|
311
|
-
): Promise<void> {
|
|
312
|
-
const meta = overrideMeta ?? getWorkflowMeta(graphName)
|
|
313
|
-
if (!meta?.nodes) {
|
|
314
|
-
throw new Error(`Workflow graph meta '${graphName}' not found`)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const nodes = meta.nodes
|
|
318
|
-
validateGraphReferences(graphName, nodes, meta.entryNodeIds ?? [])
|
|
319
|
-
|
|
320
|
-
const {
|
|
321
|
-
completedNodeIds: rawCompleted,
|
|
322
|
-
failedNodeIds: rawFailed,
|
|
323
|
-
branchKeys: rawBranch,
|
|
324
|
-
} = await workflowService.getCompletedGraphState(runId)
|
|
325
|
-
const completedNodeIds = remapStepNamesToNodeIds(
|
|
326
|
-
rawCompleted,
|
|
327
|
-
nodes,
|
|
328
|
-
graphName
|
|
329
|
-
)
|
|
330
|
-
const completedNodeIdSet = new Set(completedNodeIds)
|
|
331
|
-
const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
|
|
332
|
-
const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
|
|
333
|
-
|
|
334
|
-
if (failedNodeIds.length > 0) {
|
|
335
|
-
const failedNode = failedNodeIds[0]!
|
|
336
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
337
|
-
message: `Graph node '${failedNode}' failed after exhausting retries`,
|
|
338
|
-
stack: '',
|
|
339
|
-
code: 'GRAPH_NODE_FAILED',
|
|
340
|
-
})
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const currentRun = await workflowService.getRun(runId)
|
|
345
|
-
if (currentRun?.status === 'suspended') {
|
|
346
|
-
return
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const candidateNodes = new Set<string>()
|
|
350
|
-
|
|
351
|
-
for (const nodeId of completedNodeIds) {
|
|
352
|
-
const node = nodes[nodeId]
|
|
353
|
-
if (!node?.next) continue
|
|
354
|
-
|
|
355
|
-
const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
|
|
356
|
-
for (const nextNode of nextNodes) {
|
|
357
|
-
candidateNodes.add(nextNode)
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
for (const entryId of meta.entryNodeIds ?? []) {
|
|
362
|
-
candidateNodes.add(entryId)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
|
|
366
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
367
|
-
return
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
|
|
371
|
-
...candidateNodes,
|
|
372
|
-
])
|
|
373
|
-
|
|
374
|
-
const nodesToQueue = unstartedNodes.filter((nodeId) => {
|
|
375
|
-
const node = nodes[nodeId]
|
|
376
|
-
return node && areDependenciesSatisfied(node, completedNodeIdSet)
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
if (nodesToQueue.length === 0) {
|
|
380
|
-
const allRpcNodes = Object.entries(nodes)
|
|
381
|
-
.filter(([_, n]) => n.rpcName)
|
|
382
|
-
.map(([id]) => id)
|
|
383
|
-
const allRpcCompleted = allRpcNodes.every((id) =>
|
|
384
|
-
completedNodeIdSet.has(id)
|
|
385
|
-
)
|
|
386
|
-
if (allRpcCompleted) {
|
|
387
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
388
|
-
}
|
|
389
|
-
return
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const run = await workflowService.getRun(runId)
|
|
393
|
-
const triggerInput = run?.input
|
|
394
|
-
|
|
395
|
-
for (const nodeId of nodesToQueue) {
|
|
396
|
-
const node = nodes[nodeId]
|
|
397
|
-
if (!node?.rpcName) continue
|
|
398
|
-
|
|
399
|
-
const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
|
|
400
|
-
(id) => !IGNORED_REFS.has(id)
|
|
401
|
-
)
|
|
402
|
-
const fetchedResults = await workflowService.getNodeResults(
|
|
403
|
-
runId,
|
|
404
|
-
referencedNodeIds
|
|
405
|
-
)
|
|
406
|
-
|
|
407
|
-
const nodeResults = { trigger: triggerInput, ...fetchedResults }
|
|
408
|
-
const resolvedInput = resolveSerializedInput(node.input, nodeResults)
|
|
409
|
-
|
|
410
|
-
await queueGraphNode(
|
|
411
|
-
workflowService,
|
|
412
|
-
runId,
|
|
413
|
-
graphName,
|
|
414
|
-
nodeId,
|
|
415
|
-
node.rpcName,
|
|
416
|
-
resolvedInput
|
|
417
|
-
)
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export async function executeGraphStep(
|
|
422
|
-
workflowService: PikkuWorkflowService,
|
|
423
|
-
rpcService: any,
|
|
424
|
-
runId: string,
|
|
425
|
-
stepId: string,
|
|
426
|
-
nodeId: string,
|
|
427
|
-
rpcName: string,
|
|
428
|
-
data: any,
|
|
429
|
-
graphName: string
|
|
430
|
-
): Promise<any> {
|
|
431
|
-
const wireState: GraphWireState = {}
|
|
432
|
-
const graphWire: PikkuGraphWire = {
|
|
433
|
-
runId,
|
|
434
|
-
graphName,
|
|
435
|
-
nodeId,
|
|
436
|
-
branch: (key: string) => {
|
|
437
|
-
wireState.branchKey = key
|
|
438
|
-
},
|
|
439
|
-
setState: (name: string, value: unknown) =>
|
|
440
|
-
workflowService.updateRunState(runId, name, value),
|
|
441
|
-
getState: () => workflowService.getRunState(runId),
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
try {
|
|
445
|
-
const result = await rpcService.rpcWithWire(rpcName, data, {
|
|
446
|
-
graph: graphWire,
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
if (wireState.branchKey) {
|
|
450
|
-
await workflowService.setBranchTaken(stepId, wireState.branchKey)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return result
|
|
454
|
-
} catch (error) {
|
|
455
|
-
if (error instanceof RPCNotFoundError) {
|
|
456
|
-
await workflowService.updateRunStatus(runId, 'suspended', undefined, {
|
|
457
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
458
|
-
code: 'RPC_NOT_FOUND',
|
|
459
|
-
})
|
|
460
|
-
throw error
|
|
461
|
-
}
|
|
462
|
-
const meta = getWorkflowMeta(graphName)
|
|
463
|
-
if (meta?.nodes) {
|
|
464
|
-
const node = meta.nodes[nodeId]
|
|
465
|
-
if (node?.onError) {
|
|
466
|
-
const errorNodes = Array.isArray(node.onError)
|
|
467
|
-
? node.onError
|
|
468
|
-
: [node.onError]
|
|
469
|
-
for (const errorNodeId of errorNodes) {
|
|
470
|
-
const errorNode = meta.nodes[errorNodeId]
|
|
471
|
-
if (errorNode) {
|
|
472
|
-
await queueGraphNode(
|
|
473
|
-
workflowService,
|
|
474
|
-
runId,
|
|
475
|
-
graphName,
|
|
476
|
-
errorNodeId,
|
|
477
|
-
errorNode.rpcName,
|
|
478
|
-
{ error: { message: (error as Error).message } }
|
|
479
|
-
)
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
throw error
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
throw error
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
export async function onGraphNodeComplete(
|
|
490
|
-
workflowService: PikkuWorkflowService,
|
|
491
|
-
runId: string,
|
|
492
|
-
graphName: string
|
|
493
|
-
): Promise<void> {
|
|
494
|
-
await continueGraph(workflowService, runId, graphName)
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
export async function runFromMeta(
|
|
498
|
-
workflowService: PikkuWorkflowService,
|
|
499
|
-
runId: string,
|
|
500
|
-
meta: WorkflowRuntimeMeta,
|
|
501
|
-
_rpcService: any
|
|
502
|
-
): Promise<void> {
|
|
503
|
-
await continueGraph(workflowService, runId, meta.name, meta)
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
async function executeGraphNodeInline(
|
|
507
|
-
workflowService: PikkuWorkflowService,
|
|
508
|
-
rpcService: any,
|
|
509
|
-
runId: string,
|
|
510
|
-
graphName: string,
|
|
511
|
-
nodeId: string,
|
|
512
|
-
input: any,
|
|
513
|
-
nodes: Record<string, any>
|
|
514
|
-
): Promise<void> {
|
|
515
|
-
const node = nodes[nodeId]
|
|
516
|
-
if (!node) return
|
|
517
|
-
|
|
518
|
-
const rpcName = node.rpcName
|
|
519
|
-
|
|
520
|
-
const stepState = await workflowService.insertStepState(
|
|
521
|
-
runId,
|
|
522
|
-
nodeId,
|
|
523
|
-
rpcName,
|
|
524
|
-
input,
|
|
525
|
-
{ retries: 3 }
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
await workflowService.setStepRunning(stepState.stepId)
|
|
529
|
-
|
|
530
|
-
const wireState: GraphWireState = {}
|
|
531
|
-
const graphWire: PikkuGraphWire = {
|
|
532
|
-
runId,
|
|
533
|
-
graphName,
|
|
534
|
-
nodeId,
|
|
535
|
-
branch: (key: string) => {
|
|
536
|
-
wireState.branchKey = key
|
|
537
|
-
},
|
|
538
|
-
setState: (name: string, value: unknown) =>
|
|
539
|
-
workflowService.updateRunState(runId, name, value),
|
|
540
|
-
getState: () => workflowService.getRunState(runId),
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
try {
|
|
544
|
-
const result = await rpcService.rpcWithWire(rpcName, input, {
|
|
545
|
-
graph: graphWire,
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
if (wireState.branchKey) {
|
|
549
|
-
await workflowService.setBranchTaken(
|
|
550
|
-
stepState.stepId,
|
|
551
|
-
wireState.branchKey
|
|
552
|
-
)
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
await workflowService.setStepResult(stepState.stepId, result)
|
|
556
|
-
} catch (error) {
|
|
557
|
-
if (error instanceof RPCNotFoundError) {
|
|
558
|
-
await workflowService.setStepError(stepState.stepId, error as Error)
|
|
559
|
-
await workflowService.updateRunStatus(runId, 'suspended', undefined, {
|
|
560
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
561
|
-
code: 'RPC_NOT_FOUND',
|
|
562
|
-
})
|
|
563
|
-
return
|
|
564
|
-
}
|
|
565
|
-
await workflowService.setStepError(stepState.stepId, error as Error)
|
|
566
|
-
|
|
567
|
-
if (node?.onError) {
|
|
568
|
-
const errorNodes = Array.isArray(node.onError)
|
|
569
|
-
? node.onError
|
|
570
|
-
: [node.onError]
|
|
571
|
-
await Promise.all(
|
|
572
|
-
errorNodes.map((errorNodeId: string) =>
|
|
573
|
-
executeGraphNodeInline(
|
|
574
|
-
workflowService,
|
|
575
|
-
rpcService,
|
|
576
|
-
runId,
|
|
577
|
-
graphName,
|
|
578
|
-
errorNodeId,
|
|
579
|
-
{ error: { message: (error as Error).message } },
|
|
580
|
-
nodes
|
|
581
|
-
)
|
|
582
|
-
)
|
|
583
|
-
)
|
|
584
|
-
return
|
|
585
|
-
}
|
|
586
|
-
throw error
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
async function continueGraphInline(
|
|
591
|
-
workflowService: PikkuWorkflowService,
|
|
592
|
-
rpcService: any,
|
|
593
|
-
runId: string,
|
|
594
|
-
graphName: string,
|
|
595
|
-
nodes: Record<string, any>,
|
|
596
|
-
triggerInput: any,
|
|
597
|
-
entryNodeIds: string[]
|
|
598
|
-
): Promise<void> {
|
|
599
|
-
while (true) {
|
|
600
|
-
const {
|
|
601
|
-
completedNodeIds: rawCompleted,
|
|
602
|
-
failedNodeIds: rawFailed,
|
|
603
|
-
branchKeys: rawBranch,
|
|
604
|
-
} = await workflowService.getCompletedGraphState(runId)
|
|
605
|
-
const completedNodeIds = remapStepNamesToNodeIds(
|
|
606
|
-
rawCompleted,
|
|
607
|
-
nodes,
|
|
608
|
-
graphName
|
|
609
|
-
)
|
|
610
|
-
const completedNodeIdSet = new Set(completedNodeIds)
|
|
611
|
-
const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
|
|
612
|
-
const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
|
|
613
|
-
|
|
614
|
-
if (failedNodeIds.length > 0) {
|
|
615
|
-
const failedNode = failedNodeIds[0]!
|
|
616
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
617
|
-
message: `Graph node '${failedNode}' failed after exhausting retries`,
|
|
618
|
-
stack: '',
|
|
619
|
-
code: 'GRAPH_NODE_FAILED',
|
|
620
|
-
})
|
|
621
|
-
return
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const candidateNodes = new Set<string>()
|
|
625
|
-
|
|
626
|
-
for (const nodeId of completedNodeIds) {
|
|
627
|
-
const node = nodes[nodeId]
|
|
628
|
-
if (!node?.next) continue
|
|
629
|
-
|
|
630
|
-
const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
|
|
631
|
-
for (const nextNode of nextNodes) {
|
|
632
|
-
candidateNodes.add(nextNode)
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
for (const entryId of entryNodeIds) {
|
|
637
|
-
candidateNodes.add(entryId)
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
|
|
641
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
642
|
-
return
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
|
|
646
|
-
...candidateNodes,
|
|
647
|
-
])
|
|
648
|
-
|
|
649
|
-
const nodesToExecute = unstartedNodes.filter((nodeId) => {
|
|
650
|
-
const node = nodes[nodeId]
|
|
651
|
-
return node && areDependenciesSatisfied(node, completedNodeIdSet)
|
|
652
|
-
})
|
|
653
|
-
|
|
654
|
-
if (nodesToExecute.length === 0) {
|
|
655
|
-
if (completedNodeIds.length > 0 && unstartedNodes.length === 0) {
|
|
656
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
657
|
-
}
|
|
658
|
-
return
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
await Promise.all(
|
|
662
|
-
nodesToExecute.map(async (nodeId) => {
|
|
663
|
-
const node = nodes[nodeId]
|
|
664
|
-
if (!node?.rpcName) return
|
|
665
|
-
|
|
666
|
-
const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
|
|
667
|
-
(id) => !IGNORED_REFS.has(id)
|
|
668
|
-
)
|
|
669
|
-
const fetchedResults = await workflowService.getNodeResults(
|
|
670
|
-
runId,
|
|
671
|
-
referencedNodeIds
|
|
672
|
-
)
|
|
673
|
-
|
|
674
|
-
const nodeResults = { trigger: triggerInput, ...fetchedResults }
|
|
675
|
-
const resolvedInput = resolveSerializedInput(node.input, nodeResults)
|
|
676
|
-
|
|
677
|
-
await executeGraphNodeInline(
|
|
678
|
-
workflowService,
|
|
679
|
-
rpcService,
|
|
680
|
-
runId,
|
|
681
|
-
graphName,
|
|
682
|
-
nodeId,
|
|
683
|
-
resolvedInput,
|
|
684
|
-
nodes
|
|
685
|
-
)
|
|
686
|
-
})
|
|
687
|
-
)
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export async function runWorkflowGraph(
|
|
692
|
-
workflowService: PikkuWorkflowService,
|
|
693
|
-
graphName: string,
|
|
694
|
-
triggerInput: any,
|
|
695
|
-
rpcService?: any,
|
|
696
|
-
inline?: boolean,
|
|
697
|
-
startNode?: string,
|
|
698
|
-
wire?: WorkflowRunWire
|
|
699
|
-
): Promise<{ runId: string }> {
|
|
700
|
-
const meta = getWorkflowMeta(graphName)
|
|
701
|
-
if (!meta?.nodes) {
|
|
702
|
-
throw new Error(`Workflow graph '${graphName}' not found`)
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
const nodes = meta.nodes
|
|
706
|
-
const entryNodes: string[] = startNode
|
|
707
|
-
? [startNode]
|
|
708
|
-
: (meta.entryNodeIds ?? [])
|
|
709
|
-
validateGraphReferences(graphName, nodes, entryNodes)
|
|
710
|
-
|
|
711
|
-
if (entryNodes.length === 0) {
|
|
712
|
-
throw new Error(
|
|
713
|
-
`Workflow graph '${graphName}': no entry nodes found in meta or startNode`
|
|
714
|
-
)
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
const readyEntryNodes = entryNodes.filter((nodeId) => {
|
|
718
|
-
const node = nodes[nodeId]
|
|
719
|
-
return node && areDependenciesSatisfied(node, new Set())
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
if (readyEntryNodes.length === 0) {
|
|
723
|
-
throw new Error(
|
|
724
|
-
`Workflow graph '${graphName}': no entry nodes have satisfied dependencies`
|
|
725
|
-
)
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (!meta.graphHash) {
|
|
729
|
-
throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`)
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
const runId = await workflowService.createRun(
|
|
733
|
-
graphName,
|
|
734
|
-
triggerInput,
|
|
735
|
-
inline ?? false,
|
|
736
|
-
meta.graphHash,
|
|
737
|
-
wire ?? { type: 'unknown' }
|
|
738
|
-
)
|
|
739
|
-
|
|
740
|
-
if (inline) {
|
|
741
|
-
workflowService.registerInlineRun(runId)
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
const triggerNodeResults = { trigger: triggerInput }
|
|
745
|
-
|
|
746
|
-
if (inline && rpcService) {
|
|
747
|
-
const executeInline = async () => {
|
|
748
|
-
try {
|
|
749
|
-
await Promise.all(
|
|
750
|
-
readyEntryNodes.map(async (nodeId) => {
|
|
751
|
-
const node = nodes[nodeId]
|
|
752
|
-
if (!node?.rpcName) return
|
|
753
|
-
|
|
754
|
-
const resolvedInput =
|
|
755
|
-
node.input && Object.keys(node.input).length > 0
|
|
756
|
-
? resolveSerializedInput(node.input, triggerNodeResults)
|
|
757
|
-
: triggerInput
|
|
758
|
-
|
|
759
|
-
await executeGraphNodeInline(
|
|
760
|
-
workflowService,
|
|
761
|
-
rpcService,
|
|
762
|
-
runId,
|
|
763
|
-
graphName,
|
|
764
|
-
nodeId,
|
|
765
|
-
resolvedInput,
|
|
766
|
-
nodes
|
|
767
|
-
)
|
|
768
|
-
})
|
|
769
|
-
)
|
|
770
|
-
|
|
771
|
-
await continueGraphInline(
|
|
772
|
-
workflowService,
|
|
773
|
-
rpcService,
|
|
774
|
-
runId,
|
|
775
|
-
graphName,
|
|
776
|
-
nodes,
|
|
777
|
-
triggerInput,
|
|
778
|
-
entryNodes
|
|
779
|
-
)
|
|
780
|
-
} catch (error) {
|
|
781
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
782
|
-
message: (error as Error).message,
|
|
783
|
-
stack: (error as Error).stack || '',
|
|
784
|
-
code: 'GRAPH_NODE_FAILED',
|
|
785
|
-
})
|
|
786
|
-
} finally {
|
|
787
|
-
workflowService.unregisterInlineRun(runId)
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
await executeInline()
|
|
791
|
-
} else {
|
|
792
|
-
for (const nodeId of readyEntryNodes) {
|
|
793
|
-
const node = nodes[nodeId]
|
|
794
|
-
if (!node?.rpcName) continue
|
|
795
|
-
|
|
796
|
-
const resolvedInput =
|
|
797
|
-
node.input && Object.keys(node.input).length > 0
|
|
798
|
-
? resolveSerializedInput(node.input, triggerNodeResults)
|
|
799
|
-
: triggerInput
|
|
800
|
-
|
|
801
|
-
await queueGraphNode(
|
|
802
|
-
workflowService,
|
|
803
|
-
runId,
|
|
804
|
-
graphName,
|
|
805
|
-
nodeId,
|
|
806
|
-
node.rpcName,
|
|
807
|
-
resolvedInput
|
|
808
|
-
)
|
|
809
|
-
}
|
|
810
|
-
if (inline) {
|
|
811
|
-
workflowService.unregisterInlineRun(runId)
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
return { runId }
|
|
816
|
-
}
|