@pikku/core 0.12.106 → 0.12.111
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 +358 -0
- package/dist/analytics/analytics.d.ts +6 -0
- package/dist/analytics/analytics.js +58 -0
- package/dist/analytics/analytics.types.d.ts +156 -0
- package/dist/analytics/analytics.types.js +1 -0
- package/dist/analytics/anonymous-analytics-identity.d.ts +23 -0
- package/dist/analytics/anonymous-analytics-identity.js +27 -0
- package/dist/analytics/compose-analytics-identity.d.ts +14 -0
- package/dist/analytics/compose-analytics-identity.js +33 -0
- package/dist/analytics/cookie-analytics-identity.d.ts +24 -0
- package/dist/analytics/cookie-analytics-identity.js +37 -0
- package/dist/analytics/define-analytics-events.d.ts +22 -0
- package/dist/analytics/define-analytics-events.js +9 -0
- package/dist/analytics/fan-out-analytics.d.ts +15 -0
- package/dist/analytics/fan-out-analytics.js +36 -0
- package/dist/analytics/index.d.ts +13 -0
- package/dist/analytics/index.js +8 -0
- package/dist/analytics/logger-analytics-service.d.ts +14 -0
- package/dist/analytics/logger-analytics-service.js +23 -0
- package/dist/analytics/mint-cookie.d.ts +44 -0
- package/dist/analytics/mint-cookie.js +55 -0
- package/dist/errors/errors.d.ts +7 -0
- package/dist/errors/errors.js +17 -0
- package/dist/function/function-meta.types.d.ts +2 -0
- package/dist/function/function-runner.js +29 -0
- package/dist/function/functions.types.d.ts +22 -3
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/pikku-state.d.ts +1 -1
- package/dist/scopes.d.ts +11 -0
- package/dist/scopes.js +17 -0
- package/dist/services/credential-wire-service.d.ts +1 -1
- package/dist/services/feature-flag-service.d.ts +100 -0
- package/dist/services/feature-flag-service.js +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/meta-service.d.ts +16 -0
- package/dist/services/meta-service.js +19 -0
- package/dist/services/webhook-service.d.ts +7 -2
- package/dist/services/webhook-service.js +11 -6
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +2 -2
- package/dist/testing/service-tests/agent-storage-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/channel-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/credential-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/deployment-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/event-hub-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/secret-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/session-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/workflow-run-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/workflow-service-tests.d.ts +1 -1
- package/dist/types/core.types.d.ts +23 -0
- package/dist/wirings/agent/agent-rpc.d.ts +1 -1
- package/dist/wirings/agent/agent-turn.d.ts +1 -1
- package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +1 -1
- package/dist/wirings/channel/channel-common.d.ts +1 -1
- package/dist/wirings/channel/channel-rpc-validators.d.ts +2 -2
- package/dist/wirings/channel/channel-runner.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
- package/dist/wirings/cli/channel/cli-approval.d.ts +2 -2
- package/dist/wirings/flag/assert-feature-available.d.ts +15 -0
- package/dist/wirings/flag/assert-feature-available.js +23 -0
- package/dist/wirings/flag/cached-flag-source.d.ts +73 -0
- package/dist/wirings/flag/cached-flag-source.js +96 -0
- package/dist/wirings/flag/define-feature-flags.d.ts +33 -0
- package/dist/wirings/flag/define-feature-flags.js +32 -0
- package/dist/wirings/flag/flag.types.d.ts +97 -0
- package/dist/wirings/flag/flag.types.js +1 -0
- package/dist/wirings/flag/index.d.ts +7 -0
- package/dist/wirings/flag/index.js +5 -0
- package/dist/wirings/flag/resolve-flag.d.ts +52 -0
- package/dist/wirings/flag/resolve-flag.js +90 -0
- package/dist/wirings/flag/validate-flag-definitions.d.ts +26 -0
- package/dist/wirings/flag/validate-flag-definitions.js +103 -0
- package/dist/wirings/rpc/rpc-runner.js +3 -0
- package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +8 -8
- package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +1 -1
- package/dist/wirings/workflow/workflow-queue-routing.d.ts +1 -1
- package/package.json +11 -5
- package/run-tests.sh +17 -9
- package/src/public-surface.json +26 -0
- package/src/agent-naming.test.ts +0 -68
- package/src/api-report.test.ts +0 -138
- package/src/app-alias-surface.test.ts +0 -179
- package/src/app-leaf-surface.test.ts +0 -222
- package/src/bootstrap-compat/root.ts +0 -20
- package/src/classification/column-form.test.ts +0 -97
- package/src/classification/column-form.ts +0 -55
- package/src/classification/data-classification.ts +0 -73
- package/src/classification/index.ts +0 -40
- package/src/classification/key-ids.ts +0 -2
- package/src/classification/secret-value.test.ts +0 -204
- package/src/classification/secret-value.ts +0 -106
- package/src/crypto-utils.test.ts +0 -668
- package/src/crypto-utils.ts +0 -460
- package/src/dev/hot-reload.test.ts +0 -587
- package/src/dev/hot-reload.ts +0 -225
- package/src/dev/module-runner.test.ts +0 -235
- package/src/dev/module-runner.ts +0 -138
- package/src/dev/reload-meta.test.ts +0 -183
- package/src/dev/reload-meta.ts +0 -144
- package/src/ecosystem-tier-removed.test.ts +0 -72
- package/src/env.ts +0 -1
- package/src/errors/error-handler.ts +0 -77
- package/src/errors/error.test.ts +0 -229
- package/src/errors/errors.ts +0 -393
- package/src/errors/index.ts +0 -11
- package/src/errors/serialized-error.ts +0 -12
- package/src/factory-functions.test.ts +0 -111
- package/src/function/abort-scope.test.ts +0 -97
- package/src/function/abort-scope.ts +0 -80
- package/src/function/function-meta.types.ts +0 -151
- package/src/function/function-runner.test.ts +0 -1619
- package/src/function/function-runner.ts +0 -499
- package/src/function/functions.types.ts +0 -319
- package/src/function/index.ts +0 -40
- package/src/function/list.types.test.ts +0 -100
- package/src/function/list.types.ts +0 -69
- package/src/function/pikku-request.test.ts +0 -35
- package/src/function/pikku-request.ts +0 -17
- package/src/gopass-secrets-removed.test.ts +0 -51
- package/src/handle-error.test.ts +0 -532
- package/src/handle-error.ts +0 -64
- package/src/middleware/auth-apikey.test.ts +0 -362
- package/src/middleware/auth-apikey.ts +0 -39
- package/src/middleware/auth-bearer.test.ts +0 -566
- package/src/middleware/auth-bearer.ts +0 -83
- package/src/middleware/auth-cookie.test.ts +0 -572
- package/src/middleware/auth-cookie.ts +0 -107
- package/src/middleware/cors.test.ts +0 -579
- package/src/middleware/cors.ts +0 -103
- package/src/middleware/index.ts +0 -30
- package/src/middleware/middleware-factories.ts +0 -76
- package/src/middleware/middleware.types.ts +0 -61
- package/src/middleware/remote-auth.test.ts +0 -547
- package/src/middleware/remote-auth.ts +0 -95
- package/src/middleware/remote-jobs-secret.ts +0 -35
- package/src/middleware/require-origin.test.ts +0 -115
- package/src/middleware/require-origin.ts +0 -79
- package/src/middleware/telemetry.ts +0 -83
- package/src/middleware-runner.test.ts +0 -578
- package/src/middleware-runner.ts +0 -232
- package/src/no-any-casts.test.ts +0 -57
- package/src/no-root-barrel.test.ts +0 -122
- package/src/permissions.test.ts +0 -256
- package/src/permissions.ts +0 -217
- package/src/pikku-state.test.ts +0 -198
- package/src/pikku-state.ts +0 -206
- package/src/production-barrels-stay-lean.test.ts +0 -110
- package/src/public-surface.test.ts +0 -133
- package/src/remote.test.ts +0 -173
- package/src/remote.ts +0 -56
- package/src/removed-legacy-exports.test.ts +0 -66
- package/src/run-tests-script.test.ts +0 -67
- package/src/schema.test.ts +0 -430
- package/src/schema.ts +0 -204
- package/src/scopes.test.ts +0 -203
- package/src/scopes.ts +0 -67
- package/src/services/agent-run-state-service.ts +0 -37
- package/src/services/agent-runner-service.ts +0 -238
- package/src/services/agent-storage-service.ts +0 -39
- package/src/services/ai-embedding-service.ts +0 -9
- package/src/services/audit-service.test.ts +0 -44
- package/src/services/audit-service.ts +0 -280
- package/src/services/content-service.ts +0 -65
- package/src/services/credential-service.ts +0 -20
- package/src/services/credential-wire-service.test.ts +0 -235
- package/src/services/credential-wire-service.ts +0 -120
- package/src/services/deployment-service.ts +0 -32
- package/src/services/email-service.test.ts +0 -100
- package/src/services/email-service.ts +0 -60
- package/src/services/email-template.test.ts +0 -311
- package/src/services/email-template.ts +0 -254
- package/src/services/file-scenario-run-store.test.ts +0 -349
- package/src/services/file-scenario-run-store.ts +0 -282
- package/src/services/gateway-service.ts +0 -5
- package/src/services/http-personas-converse.test.ts +0 -274
- package/src/services/http-personas.test.ts +0 -379
- package/src/services/http-personas.ts +0 -508
- package/src/services/in-memory-agent-run-state-service.ts +0 -87
- package/src/services/in-memory-queue-service.test.ts +0 -162
- package/src/services/in-memory-queue-service.ts +0 -78
- package/src/services/in-memory-session-store.test.ts +0 -58
- package/src/services/in-memory-session-store.ts +0 -20
- package/src/services/in-memory-trigger-service.ts +0 -47
- package/src/services/in-memory-workflow-service.test.ts +0 -430
- package/src/services/in-memory-workflow-service.ts +0 -595
- package/src/services/index.ts +0 -192
- package/src/services/istanbul-coverage-service.test.ts +0 -66
- package/src/services/istanbul-coverage-service.ts +0 -59
- package/src/services/jwt-service.ts +0 -15
- package/src/services/local-content-request-handler.test.ts +0 -240
- package/src/services/local-content-request-handler.ts +0 -295
- package/src/services/local-content.test.ts +0 -186
- package/src/services/local-content.ts +0 -242
- package/src/services/local-credential-service.ts +0 -63
- package/src/services/local-email-service.test.ts +0 -182
- package/src/services/local-email-service.ts +0 -43
- package/src/services/local-gateway-service.ts +0 -51
- package/src/services/local-secrets.test.ts +0 -95
- package/src/services/local-secrets.ts +0 -71
- package/src/services/local-variables.test.ts +0 -61
- package/src/services/local-variables.ts +0 -45
- package/src/services/logger-console.test.ts +0 -117
- package/src/services/logger-console.ts +0 -174
- package/src/services/logger.ts +0 -48
- package/src/services/meta-service.test.ts +0 -75
- package/src/services/meta-service.ts +0 -767
- package/src/services/persona-actor-secret.test.ts +0 -68
- package/src/services/persona-actor-secret.ts +0 -70
- package/src/services/persona-sign-in.test.ts +0 -219
- package/src/services/persona-sign-in.ts +0 -238
- package/src/services/personas-service.ts +0 -191
- package/src/services/pikku-user-id.test.ts +0 -62
- package/src/services/pikku-user-id.ts +0 -13
- package/src/services/queue-webhook-service.test.ts +0 -409
- package/src/services/queue-webhook-service.ts +0 -150
- package/src/services/remote-jobs.test.ts +0 -106
- package/src/services/remote-jobs.ts +0 -92
- package/src/services/scheduler-service.ts +0 -37
- package/src/services/schema-service.ts +0 -10
- package/src/services/scope-service.ts +0 -91
- package/src/services/scoped-credential-service.test.ts +0 -86
- package/src/services/scoped-credential-service.ts +0 -63
- package/src/services/scoped-secret-service.test.ts +0 -93
- package/src/services/scoped-secret-service.ts +0 -40
- package/src/services/secret-host-binding.test.ts +0 -138
- package/src/services/secret-host-binding.ts +0 -51
- package/src/services/secret-service.ts +0 -25
- package/src/services/secretless.test.ts +0 -54
- package/src/services/secretless.ts +0 -29
- package/src/services/session-store.ts +0 -9
- package/src/services/stub-tracker.test.ts +0 -104
- package/src/services/stub-tracker.ts +0 -175
- package/src/services/system-role-guard.test.ts +0 -96
- package/src/services/system-role-guard.ts +0 -71
- package/src/services/temporary-file-service.ts +0 -76
- package/src/services/trigger-service.ts +0 -5
- package/src/services/typed-credential-service.ts +0 -83
- package/src/services/typed-secret-service.test.ts +0 -204
- package/src/services/typed-secret-service.ts +0 -132
- package/src/services/typed-variables-service.test.ts +0 -166
- package/src/services/typed-variables-service.ts +0 -166
- package/src/services/user-session-service.test.ts +0 -147
- package/src/services/user-session-service.ts +0 -85
- package/src/services/v8-coverage-service.test.ts +0 -69
- package/src/services/v8-coverage-service.ts +0 -163
- package/src/services/variables-service.ts +0 -13
- package/src/services/webhook-service.ts +0 -147
- package/src/services/workflow-service.ts +0 -120
- package/src/side-effects-are-declared.test.ts +0 -84
- package/src/source-files-stay-composable.test.ts +0 -41
- package/src/template-alias-surface.test.ts +0 -240
- package/src/test-utils.ts +0 -38
- package/src/testing/index.ts +0 -3
- package/src/testing/service-tests/agent-run-service-tests.ts +0 -98
- package/src/testing/service-tests/agent-storage-service-tests.ts +0 -379
- package/src/testing/service-tests/channel-store-tests.ts +0 -98
- package/src/testing/service-tests/credential-service-tests.ts +0 -143
- package/src/testing/service-tests/deployment-service-tests.ts +0 -33
- package/src/testing/service-tests/event-hub-store-tests.ts +0 -48
- package/src/testing/service-tests/secret-service-tests.ts +0 -105
- package/src/testing/service-tests/session-store-tests.ts +0 -62
- package/src/testing/service-tests/workflow-run-service-tests.ts +0 -59
- package/src/testing/service-tests/workflow-service-tests.ts +0 -308
- package/src/testing/service-tests.ts +0 -95
- package/src/time-utils.test.ts +0 -56
- package/src/time-utils.ts +0 -89
- package/src/types/core.types.ts +0 -446
- package/src/types/index.ts +0 -25
- package/src/types/state.types.ts +0 -226
- package/src/types/typed-credentials.test.ts +0 -72
- package/src/utils/hash.test.ts +0 -68
- package/src/utils/hash.ts +0 -26
- package/src/utils/hmac.ts +0 -21
- package/src/utils/node-host-resolver.ts +0 -20
- package/src/utils/safe-fetch.test.ts +0 -515
- package/src/utils/safe-fetch.ts +0 -347
- package/src/utils.test.ts +0 -359
- package/src/utils.ts +0 -165
- package/src/version.test.ts +0 -80
- package/src/version.ts +0 -25
- package/src/wirings/actor-flow/actor-flow.types.ts +0 -40
- package/src/wirings/actor-flow/index.ts +0 -10
- package/src/wirings/actor-flow/run-conversation.test.ts +0 -187
- package/src/wirings/actor-flow/run-conversation.ts +0 -280
- package/src/wirings/addon/addon-auth-tags.test.ts +0 -219
- package/src/wirings/addon/addon-runner.ts +0 -236
- package/src/wirings/addon/addon-scopes.test.ts +0 -225
- package/src/wirings/addon/addon-secrets.test.ts +0 -391
- package/src/wirings/addon/index.ts +0 -12
- package/src/wirings/addon/remote-addon-auth.ts +0 -65
- package/src/wirings/addon/wire-addon.test.ts +0 -102
- package/src/wirings/addon/wire-addon.ts +0 -202
- package/src/wirings/addon/wire-remote-addon.ts +0 -41
- package/src/wirings/agent/agent-agui.test.ts +0 -1213
- package/src/wirings/agent/agent-agui.ts +0 -405
- package/src/wirings/agent/agent-authorization.test.ts +0 -208
- package/src/wirings/agent/agent-finalize.test.ts +0 -187
- package/src/wirings/agent/agent-finalize.ts +0 -197
- package/src/wirings/agent/agent-helpers.test.ts +0 -215
- package/src/wirings/agent/agent-helpers.ts +0 -96
- package/src/wirings/agent/agent-interrupt.test.ts +0 -833
- package/src/wirings/agent/agent-interrupt.ts +0 -400
- package/src/wirings/agent/agent-memory.test.ts +0 -397
- package/src/wirings/agent/agent-memory.ts +0 -496
- package/src/wirings/agent/agent-middleware.types.test.ts +0 -41
- package/src/wirings/agent/agent-model-config.test.ts +0 -103
- package/src/wirings/agent/agent-model-config.ts +0 -59
- package/src/wirings/agent/agent-prepare.test.ts +0 -977
- package/src/wirings/agent/agent-prepare.ts +0 -1083
- package/src/wirings/agent/agent-registry.test.ts +0 -455
- package/src/wirings/agent/agent-registry.ts +0 -99
- package/src/wirings/agent/agent-resume-authorization.test.ts +0 -206
- package/src/wirings/agent/agent-rpc.ts +0 -116
- package/src/wirings/agent/agent-runner.test.ts +0 -1229
- package/src/wirings/agent/agent-runner.ts +0 -858
- package/src/wirings/agent/agent-stream-delegate.test.ts +0 -381
- package/src/wirings/agent/agent-stream-output-hooks.test.ts +0 -353
- package/src/wirings/agent/agent-stream.test.ts +0 -1800
- package/src/wirings/agent/agent-stream.ts +0 -1639
- package/src/wirings/agent/agent-thread-ownership.test.ts +0 -297
- package/src/wirings/agent/agent-turn.test.ts +0 -65
- package/src/wirings/agent/agent-turn.ts +0 -122
- package/src/wirings/agent/agent-utils.ts +0 -20
- package/src/wirings/agent/agent.types.ts +0 -676
- package/src/wirings/agent/index.ts +0 -58
- package/src/wirings/agent/voice-input.test.ts +0 -220
- package/src/wirings/agent/voice-input.ts +0 -180
- package/src/wirings/agent/voice-output.test.ts +0 -510
- package/src/wirings/agent/voice-output.ts +0 -311
- package/src/wirings/agent-scorer/agent-scorer-grade.test.ts +0 -106
- package/src/wirings/agent-scorer/agent-scorer-grade.ts +0 -55
- package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +0 -285
- package/src/wirings/agent-scorer/agent-scorer-judge.ts +0 -180
- package/src/wirings/agent-scorer/agent-scorer-live.test.ts +0 -174
- package/src/wirings/agent-scorer/agent-scorer-live.ts +0 -56
- package/src/wirings/agent-scorer/agent-scorer-registry.ts +0 -66
- package/src/wirings/agent-scorer/agent-scorer-sampling.test.ts +0 -34
- package/src/wirings/agent-scorer/agent-scorer-sampling.ts +0 -36
- package/src/wirings/agent-scorer/agent-scorer-snapshots.test.ts +0 -49
- package/src/wirings/agent-scorer/agent-scorer-snapshots.ts +0 -46
- package/src/wirings/agent-scorer/agent-scorer-worker.test.ts +0 -122
- package/src/wirings/agent-scorer/agent-scorer-worker.ts +0 -71
- package/src/wirings/agent-scorer/agent-scorer.ts +0 -102
- package/src/wirings/agent-scorer/agent-scorer.types.ts +0 -128
- package/src/wirings/agent-scorer/index.ts +0 -23
- package/src/wirings/channel/channel-common.ts +0 -102
- package/src/wirings/channel/channel-handler-shapes.test.ts +0 -72
- package/src/wirings/channel/channel-handler.test.ts +0 -322
- package/src/wirings/channel/channel-handler.ts +0 -259
- package/src/wirings/channel/channel-host-rpc.test.ts +0 -150
- package/src/wirings/channel/channel-host-rpc.ts +0 -69
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -188
- package/src/wirings/channel/channel-middleware-runner.ts +0 -126
- package/src/wirings/channel/channel-rpc-registry.ts +0 -116
- package/src/wirings/channel/channel-rpc-responder.ts +0 -117
- package/src/wirings/channel/channel-rpc-service.ts +0 -132
- package/src/wirings/channel/channel-rpc-validators.ts +0 -65
- package/src/wirings/channel/channel-rpc.test.ts +0 -841
- package/src/wirings/channel/channel-rpc.ts +0 -5
- package/src/wirings/channel/channel-rpc.types.ts +0 -160
- package/src/wirings/channel/channel-runner.ts +0 -164
- package/src/wirings/channel/channel-store.ts +0 -35
- package/src/wirings/channel/channel.types.ts +0 -193
- package/src/wirings/channel/define-channel-routes.ts +0 -5
- package/src/wirings/channel/eventhub-service.ts +0 -16
- package/src/wirings/channel/eventhub-store.ts +0 -13
- package/src/wirings/channel/index.ts +0 -40
- package/src/wirings/channel/local/index.ts +0 -3
- package/src/wirings/channel/local/local-channel-handler.ts +0 -98
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -339
- package/src/wirings/channel/local/local-channel-runner.ts +0 -271
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -77
- package/src/wirings/channel/local/local-eventhub-service.ts +0 -69
- package/src/wirings/channel/log-channels.ts +0 -16
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -135
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -60
- package/src/wirings/channel/serverless/index.ts +0 -6
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -328
- package/src/wirings/cli/channel/cli-approval.test.ts +0 -177
- package/src/wirings/cli/channel/cli-approval.ts +0 -135
- package/src/wirings/cli/channel/cli-channel-runner.ts +0 -118
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +0 -192
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +0 -148
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +0 -480
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +0 -188
- package/src/wirings/cli/channel/index.ts +0 -6
- package/src/wirings/cli/cli-runner.test.ts +0 -969
- package/src/wirings/cli/cli-runner.ts +0 -538
- package/src/wirings/cli/cli.types.ts +0 -388
- package/src/wirings/cli/command-parser.test.ts +0 -1120
- package/src/wirings/cli/command-parser.ts +0 -713
- package/src/wirings/cli/define-cli-commands.ts +0 -13
- package/src/wirings/cli/format-cli-error.test.ts +0 -91
- package/src/wirings/cli/format-cli-error.ts +0 -96
- package/src/wirings/cli/index.ts +0 -19
- package/src/wirings/credential/credential.types.ts +0 -37
- package/src/wirings/credential/define-credential.ts +0 -49
- package/src/wirings/credential/index.ts +0 -7
- package/src/wirings/credential/validate-credential-definitions.ts +0 -63
- package/src/wirings/gateway/gateway-authorization.test.ts +0 -575
- package/src/wirings/gateway/gateway-channel-meta.test.ts +0 -44
- package/src/wirings/gateway/gateway-runner.test.ts +0 -581
- package/src/wirings/gateway/gateway-runner.ts +0 -411
- package/src/wirings/gateway/gateway.types.ts +0 -137
- package/src/wirings/gateway/index.ts +0 -15
- package/src/wirings/http/http-routes.test.ts +0 -318
- package/src/wirings/http/http-routes.ts +0 -111
- package/src/wirings/http/http-runner-addon-ref.test.ts +0 -238
- package/src/wirings/http/http-runner.test.ts +0 -503
- package/src/wirings/http/http-runner.ts +0 -462
- package/src/wirings/http/http.types.ts +0 -307
- package/src/wirings/http/index.ts +0 -27
- package/src/wirings/http/log-http-routes.ts +0 -18
- package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -318
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -260
- package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -123
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -149
- package/src/wirings/http/routers/http-router.ts +0 -13
- package/src/wirings/http/routers/path-to-regex.test.ts +0 -306
- package/src/wirings/http/routers/path-to-regex.ts +0 -115
- package/src/wirings/http/web-request.test.ts +0 -382
- package/src/wirings/http/web-request.ts +0 -162
- package/src/wirings/mcp/index.ts +0 -26
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -388
- package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
- package/src/wirings/mcp/mcp-runner.test.ts +0 -588
- package/src/wirings/mcp/mcp-runner.ts +0 -330
- package/src/wirings/mcp/mcp.types.ts +0 -199
- package/src/wirings/node/index.ts +0 -2
- package/src/wirings/node/node.types.ts +0 -23
- package/src/wirings/oauth2/index.ts +0 -1
- package/src/wirings/oauth2/oauth2.types.ts +0 -12
- package/src/wirings/persona/define-personas.ts +0 -33
- package/src/wirings/persona/index.ts +0 -75
- package/src/wirings/persona/persona-app-scopes.test.ts +0 -47
- package/src/wirings/persona/persona-app-scopes.ts +0 -74
- package/src/wirings/persona/persona-email.ts +0 -87
- package/src/wirings/persona/persona-environments.test.ts +0 -194
- package/src/wirings/persona/persona-environments.ts +0 -138
- package/src/wirings/persona/persona-mailbox.ts +0 -156
- package/src/wirings/persona/persona.test.ts +0 -230
- package/src/wirings/persona/persona.types.ts +0 -156
- package/src/wirings/persona/validate-personas.ts +0 -137
- package/src/wirings/queue/index.ts +0 -29
- package/src/wirings/queue/queue-identity.test.ts +0 -454
- package/src/wirings/queue/queue-identity.ts +0 -176
- package/src/wirings/queue/queue-runner.test.ts +0 -705
- package/src/wirings/queue/queue-runner.ts +0 -181
- package/src/wirings/queue/queue.types.ts +0 -161
- package/src/wirings/queue/register-queue-helper.ts +0 -46
- package/src/wirings/queue/signed-queue-service.ts +0 -60
- package/src/wirings/queue/validate-worker-config.test.ts +0 -108
- package/src/wirings/queue/validate-worker-config.ts +0 -90
- package/src/wirings/role/define-system-role.ts +0 -33
- package/src/wirings/role/index.ts +0 -13
- package/src/wirings/role/role.test.ts +0 -104
- package/src/wirings/role/role.types.ts +0 -47
- package/src/wirings/role/validate-role-definitions.ts +0 -93
- package/src/wirings/rpc/index.ts +0 -8
- package/src/wirings/rpc/rpc-runner.test.ts +0 -1130
- package/src/wirings/rpc/rpc-runner.ts +0 -566
- package/src/wirings/rpc/rpc-types.ts +0 -87
- package/src/wirings/scheduler/index.ts +0 -11
- package/src/wirings/scheduler/log-schedulers.ts +0 -16
- package/src/wirings/scheduler/scheduler-runner.test.ts +0 -835
- package/src/wirings/scheduler/scheduler-runner.ts +0 -138
- package/src/wirings/scheduler/scheduler.types.ts +0 -42
- package/src/wirings/scope/define-scope.ts +0 -33
- package/src/wirings/scope/index.ts +0 -15
- package/src/wirings/scope/scope.test.ts +0 -159
- package/src/wirings/scope/scope.types.ts +0 -42
- package/src/wirings/scope/validate-scope-definitions.ts +0 -107
- package/src/wirings/secret/index.ts +0 -9
- package/src/wirings/secret/secret.types.ts +0 -70
- package/src/wirings/secret/validate-secret-definitions.test.ts +0 -232
- package/src/wirings/secret/validate-secret-definitions.ts +0 -88
- package/src/wirings/trigger/index.ts +0 -9
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -107
- package/src/wirings/trigger/trigger-runner.test.ts +0 -78
- package/src/wirings/trigger/trigger-runner.ts +0 -113
- package/src/wirings/trigger/trigger.types.ts +0 -100
- package/src/wirings/variable/index.ts +0 -8
- package/src/wirings/variable/validate-variable-definitions.test.ts +0 -115
- package/src/wirings/variable/validate-variable-definitions.ts +0 -71
- package/src/wirings/variable/variable.types.ts +0 -40
- package/src/wirings/virtual-user/index.ts +0 -107
- package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +0 -115
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +0 -95
- package/src/wirings/virtual-user/run-virtual-user.test.ts +0 -774
- package/src/wirings/virtual-user/run-virtual-user.ts +0 -684
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +0 -72
- package/src/wirings/virtual-user/virtual-user-agents.ts +0 -62
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +0 -215
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +0 -184
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +0 -426
- package/src/wirings/virtual-user/virtual-user-derive.ts +0 -161
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +0 -68
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +0 -210
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +0 -218
- package/src/wirings/virtual-user/virtual-user-intents.ts +0 -185
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +0 -72
- package/src/wirings/virtual-user/virtual-user-rng.ts +0 -50
- package/src/wirings/virtual-user/virtual-user-run-store.ts +0 -131
- package/src/wirings/virtual-user/virtual-user-scaffold.test.ts +0 -854
- package/src/wirings/virtual-user/virtual-user-scaffold.ts +0 -664
- package/src/wirings/virtual-user/virtual-user-schedule-store.ts +0 -93
- package/src/wirings/virtual-user/virtual-user-schedule.test.ts +0 -280
- package/src/wirings/virtual-user/virtual-user-schedule.ts +0 -156
- package/src/wirings/virtual-user/virtual-user-target.ts +0 -47
- package/src/wirings/virtual-user/virtual-user.types.ts +0 -215
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -709
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -25
- package/src/wirings/workflow/feature.test.ts +0 -131
- package/src/wirings/workflow/feature.ts +0 -59
- package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +0 -569
- package/src/wirings/workflow/graph/graph-foreach.test.ts +0 -626
- package/src/wirings/workflow/graph/graph-node.ts +0 -115
- package/src/wirings/workflow/graph/graph-runner.test.ts +0 -1049
- package/src/wirings/workflow/graph/graph-runner.ts +0 -1376
- package/src/wirings/workflow/graph/graph-validation.test.ts +0 -27
- package/src/wirings/workflow/graph/graph-validation.ts +0 -41
- 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 -39
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -75
- package/src/wirings/workflow/index.ts +0 -107
- package/src/wirings/workflow/pikku-scenario-service.ts +0 -1013
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -963
- package/src/wirings/workflow/pikku-workflow-service.ts +0 -1993
- package/src/wirings/workflow/run-timeline.test.ts +0 -199
- package/src/wirings/workflow/run-timeline.ts +0 -185
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -107
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -57
- package/src/wirings/workflow/scenario-expectations.test.ts +0 -238
- package/src/wirings/workflow/scenario-hooks.test.ts +0 -259
- package/src/wirings/workflow/scenario-poll.test.ts +0 -64
- package/src/wirings/workflow/scenario-poll.ts +0 -27
- package/src/wirings/workflow/scenario-prose.test.ts +0 -275
- package/src/wirings/workflow/scenario-prose.ts +0 -86
- package/src/wirings/workflow/scenario-run.types.ts +0 -193
- package/src/wirings/workflow/scenario-service.test.ts +0 -147
- package/src/wirings/workflow/scenario-step-guards.ts +0 -23
- package/src/wirings/workflow/scenario-step.test.ts +0 -800
- package/src/wirings/workflow/scenario-step.types.ts +0 -262
- package/src/wirings/workflow/scenario-surface.test.ts +0 -146
- package/src/wirings/workflow/scenario-surface.ts +0 -71
- package/src/wirings/workflow/scenario.types.ts +0 -63
- package/src/wirings/workflow/workflow-approval-audit.ts +0 -47
- package/src/wirings/workflow/workflow-approval-policy.test.ts +0 -594
- package/src/wirings/workflow/workflow-approval-policy.ts +0 -68
- package/src/wirings/workflow/workflow-approval.ts +0 -290
- package/src/wirings/workflow/workflow-child-run-session.test.ts +0 -168
- package/src/wirings/workflow/workflow-constants.ts +0 -66
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +0 -116
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -55
- package/src/wirings/workflow/workflow-dispatch-relay.test.ts +0 -128
- package/src/wirings/workflow/workflow-errors.ts +0 -132
- package/src/wirings/workflow/workflow-inline-authority.test.ts +0 -175
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -51
- package/src/wirings/workflow/workflow-invocation-id.ts +0 -28
- package/src/wirings/workflow/workflow-meta-resolver.ts +0 -45
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -171
- package/src/wirings/workflow/workflow-missing-meta.test.ts +0 -92
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -145
- package/src/wirings/workflow/workflow-queue-routing.ts +0 -129
- package/src/wirings/workflow/workflow-queue-wiring.ts +0 -149
- package/src/wirings/workflow/workflow-queue-workers.test.ts +0 -131
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -66
- package/src/wirings/workflow/workflow-recovery.ts +0 -192
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +0 -140
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -113
- package/src/wirings/workflow/workflow-run-authority.test.ts +0 -212
- package/src/wirings/workflow/workflow-run-context.test.ts +0 -172
- package/src/wirings/workflow/workflow-run-engine.types.ts +0 -92
- package/src/wirings/workflow/workflow-run-ownership.ts +0 -37
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -127
- package/src/wirings/workflow/workflow-stalled-recovery.test.ts +0 -152
- package/src/wirings/workflow/workflow-status-stream.test.ts +0 -354
- package/src/wirings/workflow/workflow-status-stream.ts +0 -144
- package/src/wirings/workflow/workflow-step-claim.ts +0 -46
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +0 -94
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -87
- package/src/wirings/workflow/workflow-suspend.ts +0 -61
- package/src/wirings/workflow/workflow-terminal-run-guard.test.ts +0 -105
- package/src/wirings/workflow/workflow.types.ts +0 -268
- package/src/wirings-stay-decoupled.test.ts +0 -130
- package/tsconfig.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.type-tests.json +0 -15
|
@@ -1,1993 +0,0 @@
|
|
|
1
|
-
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
2
|
-
import {
|
|
3
|
-
getSingletonServices,
|
|
4
|
-
getCreateWireServices,
|
|
5
|
-
pikkuState,
|
|
6
|
-
} from '../../pikku-state.js'
|
|
7
|
-
import { getDurationInMilliseconds } from '../../time-utils.js'
|
|
8
|
-
import type { CoreUserSession, PikkuRawWire } from '../../types/core.types.js'
|
|
9
|
-
import type { SerializedError } from '../../errors/serialized-error.js'
|
|
10
|
-
import type {
|
|
11
|
-
GroupConcurrencyConfig,
|
|
12
|
-
JobGroup,
|
|
13
|
-
JobOptions,
|
|
14
|
-
QueueService,
|
|
15
|
-
} from '../queue/queue.types.js'
|
|
16
|
-
import type {
|
|
17
|
-
ApprovalOutcome,
|
|
18
|
-
PikkuWorkflowWire,
|
|
19
|
-
StepState,
|
|
20
|
-
StepStatus,
|
|
21
|
-
WorkflowApprovalOptions,
|
|
22
|
-
WorkflowPlannedStep,
|
|
23
|
-
WorkflowRun,
|
|
24
|
-
WorkflowRunMirror,
|
|
25
|
-
WorkflowRunStatus,
|
|
26
|
-
WorkflowRunWire,
|
|
27
|
-
WorkflowStatus,
|
|
28
|
-
WorkflowVersionStatus,
|
|
29
|
-
WorkflowQueueOptions,
|
|
30
|
-
WorkflowServiceConfig,
|
|
31
|
-
WorkflowStepOptions,
|
|
32
|
-
} from './workflow.types.js'
|
|
33
|
-
import {
|
|
34
|
-
ChildWorkflowStartedException,
|
|
35
|
-
continueGraph,
|
|
36
|
-
executeGraphStep,
|
|
37
|
-
runWorkflowGraph,
|
|
38
|
-
runFromMeta,
|
|
39
|
-
stripInstanceOrdinal,
|
|
40
|
-
} from './graph/graph-runner.js'
|
|
41
|
-
import type { WorkflowService } from '../../services/workflow-service.js'
|
|
42
|
-
import type { ScenarioPersonas } from '../../services/personas-service.js'
|
|
43
|
-
import { isExpectedError } from '../../errors/error-handler.js'
|
|
44
|
-
import { PikkuMissingMetaError } from '../../errors/errors.js'
|
|
45
|
-
import { RPCNotFoundError } from '../rpc/rpc-runner.js'
|
|
46
|
-
import type { PikkuRPC } from '../rpc/rpc-types.js'
|
|
47
|
-
import { deriveInvocationId } from './workflow-invocation-id.js'
|
|
48
|
-
import { approvalDeciderFrom } from './workflow-approval-policy.js'
|
|
49
|
-
import {
|
|
50
|
-
buildRunTimeline,
|
|
51
|
-
reconstructStateAt,
|
|
52
|
-
type RunTimeline,
|
|
53
|
-
type ReconstructedRunState,
|
|
54
|
-
} from './run-timeline.js'
|
|
55
|
-
import {
|
|
56
|
-
DEFAULT_STEP_RETRIES,
|
|
57
|
-
WORKFLOW_CHILD_POLL_MAX_MS,
|
|
58
|
-
WORKFLOW_END_STATES,
|
|
59
|
-
WORKFLOW_POLL_FACTOR,
|
|
60
|
-
WORKFLOW_POLL_MIN_MS,
|
|
61
|
-
WORKFLOW_TERMINAL_STATES,
|
|
62
|
-
isRunSettled,
|
|
63
|
-
} from './workflow-constants.js'
|
|
64
|
-
import {
|
|
65
|
-
WorkflowAsyncException,
|
|
66
|
-
WorkflowCancelledException,
|
|
67
|
-
WorkflowDispatchException,
|
|
68
|
-
WorkflowNotFoundError,
|
|
69
|
-
WorkflowRunCancelledError,
|
|
70
|
-
WorkflowRunFailedError,
|
|
71
|
-
WorkflowRunNotFoundError,
|
|
72
|
-
WorkflowStepNameNotString,
|
|
73
|
-
WorkflowSuspendedException,
|
|
74
|
-
} from './workflow-errors.js'
|
|
75
|
-
import type {
|
|
76
|
-
RunContext,
|
|
77
|
-
RunLifecycleContext,
|
|
78
|
-
WorkflowRunEngine,
|
|
79
|
-
WorkflowRunExtension,
|
|
80
|
-
} from './workflow-run-engine.types.js'
|
|
81
|
-
import { resolveWorkflowMeta } from './workflow-meta-resolver.js'
|
|
82
|
-
import {
|
|
83
|
-
jobGroupFor,
|
|
84
|
-
orchestratorQueueName,
|
|
85
|
-
resolveWorkflowConfig,
|
|
86
|
-
stepDispatchTarget,
|
|
87
|
-
stepJobOptions,
|
|
88
|
-
stepWorkerQueueName,
|
|
89
|
-
} from './workflow-queue-routing.js'
|
|
90
|
-
import { wireWorkflowQueueWorkers } from './workflow-queue-wiring.js'
|
|
91
|
-
import {
|
|
92
|
-
approvalStepNameFor,
|
|
93
|
-
evaluateApprovalStep,
|
|
94
|
-
recordApprovalDecision,
|
|
95
|
-
type ApprovalStore,
|
|
96
|
-
} from './workflow-approval.js'
|
|
97
|
-
import { auditApprovalDecision } from './workflow-approval-audit.js'
|
|
98
|
-
import { recordSuspension, suspendStepNameFor } from './workflow-suspend.js'
|
|
99
|
-
import { claimStepByReadThenWrite } from './workflow-step-claim.js'
|
|
100
|
-
import {
|
|
101
|
-
RedispatchBackoff,
|
|
102
|
-
sweepStalledRuns,
|
|
103
|
-
sweepUndispatchedSteps,
|
|
104
|
-
} from './workflow-recovery.js'
|
|
105
|
-
|
|
106
|
-
export abstract class PikkuWorkflowService implements WorkflowService {
|
|
107
|
-
private runExtension?: WorkflowRunExtension
|
|
108
|
-
|
|
109
|
-
private runContexts = new Map<string, RunContext>()
|
|
110
|
-
|
|
111
|
-
private contextFor(runId: string): RunContext {
|
|
112
|
-
let context = this.runContexts.get(runId)
|
|
113
|
-
if (!context) {
|
|
114
|
-
context = { activeExecutions: 0, ordinals: new Map() }
|
|
115
|
-
this.runContexts.set(runId, context)
|
|
116
|
-
}
|
|
117
|
-
return context
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private releaseContext(runId: string): void {
|
|
121
|
-
const context = this.runContexts.get(runId)
|
|
122
|
-
if (!context) return
|
|
123
|
-
if (context.activeExecutions > 0) return
|
|
124
|
-
this.runContexts.delete(runId)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
private enterExecution(runId: string): RunContext {
|
|
128
|
-
const context = this.contextFor(runId)
|
|
129
|
-
context.activeExecutions++
|
|
130
|
-
return context
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private exitExecution(runId: string): void {
|
|
134
|
-
const context = this.runContexts.get(runId)
|
|
135
|
-
if (!context) return
|
|
136
|
-
if (context.activeExecutions > 0) {
|
|
137
|
-
context.activeExecutions--
|
|
138
|
-
}
|
|
139
|
-
if (context.activeExecutions === 0) {
|
|
140
|
-
context.replay = undefined
|
|
141
|
-
context.ordinals = new Map()
|
|
142
|
-
context.lastStep = undefined
|
|
143
|
-
}
|
|
144
|
-
this.releaseContext(runId)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
protected get logger() {
|
|
148
|
-
return getSingletonServices()?.logger
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
protected mirror?: WorkflowRunMirror
|
|
152
|
-
|
|
153
|
-
protected readonly queueStrategy: 'per-workflow' | 'shared-groups'
|
|
154
|
-
protected readonly queueConcurrency: number
|
|
155
|
-
protected readonly queueGroupConcurrency: number | GroupConcurrencyConfig
|
|
156
|
-
|
|
157
|
-
constructor(
|
|
158
|
-
options: {
|
|
159
|
-
wireQueues?: boolean
|
|
160
|
-
mirror?: WorkflowRunMirror
|
|
161
|
-
} & WorkflowQueueOptions = {}
|
|
162
|
-
) {
|
|
163
|
-
const wireQueues = options.wireQueues ?? true
|
|
164
|
-
this.mirror = options.mirror
|
|
165
|
-
this.queueStrategy = options.queueStrategy ?? 'per-workflow'
|
|
166
|
-
this.queueConcurrency = options.queueConcurrency ?? 20
|
|
167
|
-
this.queueGroupConcurrency = options.queueGroupConcurrency ?? 2
|
|
168
|
-
if (wireQueues) {
|
|
169
|
-
this.wireQueueWorkers()
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private async mirrored<T>(
|
|
174
|
-
write: () => Promise<T>,
|
|
175
|
-
mirror: (mirror: WorkflowRunMirror, written: T) => Promise<void>
|
|
176
|
-
): Promise<T> {
|
|
177
|
-
const written = await write()
|
|
178
|
-
if (this.mirror) {
|
|
179
|
-
try {
|
|
180
|
-
await mirror(this.mirror, written)
|
|
181
|
-
} catch (err: any) {
|
|
182
|
-
try {
|
|
183
|
-
this.logger?.warn?.(
|
|
184
|
-
`[pikku] WorkflowRunMirror write failed: ${err?.message ?? err}`
|
|
185
|
-
)
|
|
186
|
-
} catch {
|
|
187
|
-
// knowledge: decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return written
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
public wireQueueWorkers(): void {
|
|
195
|
-
wireWorkflowQueueWorkers({
|
|
196
|
-
strategy: this.queueStrategy,
|
|
197
|
-
concurrency: this.queueConcurrency,
|
|
198
|
-
groupConcurrency: this.queueGroupConcurrency,
|
|
199
|
-
getLogger: () => this.logger,
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
protected async isInline(runId: string): Promise<boolean> {
|
|
204
|
-
const context = this.runContexts.get(runId)
|
|
205
|
-
if (context?.inline !== undefined) {
|
|
206
|
-
return context.inline
|
|
207
|
-
}
|
|
208
|
-
const inline = (await this.getRunIdentity(runId))?.inline === true
|
|
209
|
-
if (context) {
|
|
210
|
-
context.inline = inline
|
|
211
|
-
}
|
|
212
|
-
return inline
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
public registerInlineRun(runId: string): void {
|
|
216
|
-
this.contextFor(runId).inline = true
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
public unregisterInlineRun(runId: string): void {
|
|
220
|
-
const context = this.runContexts.get(runId)
|
|
221
|
-
if (!context) return
|
|
222
|
-
context.inline = undefined
|
|
223
|
-
this.releaseContext(runId)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
public async registerWorkflowVersions(): Promise<void> {
|
|
227
|
-
const allMeta = pikkuState(null, 'workflows', 'meta')
|
|
228
|
-
for (const [name, meta] of Object.entries(allMeta)) {
|
|
229
|
-
if (!meta.graphHash) continue
|
|
230
|
-
await this.upsertWorkflowVersion(name, meta.graphHash, meta, meta.source)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
public async createRun(
|
|
235
|
-
workflowName: string,
|
|
236
|
-
input: any,
|
|
237
|
-
inline: boolean,
|
|
238
|
-
graphHash: string,
|
|
239
|
-
wire: WorkflowRunWire,
|
|
240
|
-
options?: {
|
|
241
|
-
deterministic?: boolean
|
|
242
|
-
plannedSteps?: WorkflowPlannedStep[]
|
|
243
|
-
}
|
|
244
|
-
): Promise<string> {
|
|
245
|
-
return this.mirrored(
|
|
246
|
-
() =>
|
|
247
|
-
this.createRunImpl(
|
|
248
|
-
workflowName,
|
|
249
|
-
input,
|
|
250
|
-
inline,
|
|
251
|
-
graphHash,
|
|
252
|
-
wire,
|
|
253
|
-
options
|
|
254
|
-
),
|
|
255
|
-
(mirror, runId) =>
|
|
256
|
-
mirror.createRun(
|
|
257
|
-
runId,
|
|
258
|
-
workflowName,
|
|
259
|
-
input,
|
|
260
|
-
inline,
|
|
261
|
-
graphHash,
|
|
262
|
-
wire,
|
|
263
|
-
options
|
|
264
|
-
)
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
protected abstract createRunImpl(
|
|
269
|
-
workflowName: string,
|
|
270
|
-
input: any,
|
|
271
|
-
inline: boolean,
|
|
272
|
-
graphHash: string,
|
|
273
|
-
wire: WorkflowRunWire,
|
|
274
|
-
options?: {
|
|
275
|
-
deterministic?: boolean
|
|
276
|
-
plannedSteps?: WorkflowPlannedStep[]
|
|
277
|
-
}
|
|
278
|
-
): Promise<string>
|
|
279
|
-
|
|
280
|
-
abstract getRun(id: string): Promise<WorkflowRun | null>
|
|
281
|
-
|
|
282
|
-
async getRunStatus(id: string): Promise<WorkflowRunStatus | null> {
|
|
283
|
-
const run = await this.getRun(id)
|
|
284
|
-
if (!run) return null
|
|
285
|
-
|
|
286
|
-
const history = await this.getRunHistory(id)
|
|
287
|
-
const terminalStatuses = new Set(['completed', 'failed', 'cancelled'])
|
|
288
|
-
|
|
289
|
-
const stepMap = new Map<
|
|
290
|
-
string,
|
|
291
|
-
{
|
|
292
|
-
status: StepStatus
|
|
293
|
-
startedAt?: Date
|
|
294
|
-
completedAt?: Date
|
|
295
|
-
attempts: number
|
|
296
|
-
}
|
|
297
|
-
>()
|
|
298
|
-
for (const step of history) {
|
|
299
|
-
const existing = stepMap.get(step.stepName)
|
|
300
|
-
if (!existing || step.updatedAt > existing.completedAt!) {
|
|
301
|
-
stepMap.set(step.stepName, {
|
|
302
|
-
status: step.status,
|
|
303
|
-
startedAt: step.runningAt ?? step.createdAt,
|
|
304
|
-
completedAt: step.succeededAt ?? step.failedAt,
|
|
305
|
-
attempts: step.attemptCount,
|
|
306
|
-
})
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const steps = [...stepMap.entries()].map(([name, s]) => ({
|
|
311
|
-
name,
|
|
312
|
-
status: s.status,
|
|
313
|
-
duration:
|
|
314
|
-
s.startedAt && s.completedAt
|
|
315
|
-
? s.completedAt.getTime() - s.startedAt.getTime()
|
|
316
|
-
: undefined,
|
|
317
|
-
attempts: s.attempts,
|
|
318
|
-
}))
|
|
319
|
-
|
|
320
|
-
return {
|
|
321
|
-
id: run.id,
|
|
322
|
-
status: run.status,
|
|
323
|
-
startedAt: run.createdAt,
|
|
324
|
-
completedAt: terminalStatuses.has(run.status) ? run.updatedAt : undefined,
|
|
325
|
-
deterministic: run.deterministic,
|
|
326
|
-
plannedSteps: run.plannedSteps,
|
|
327
|
-
steps,
|
|
328
|
-
output: run.status === 'completed' ? run.output : undefined,
|
|
329
|
-
error: run.error
|
|
330
|
-
? { message: run.error.message ?? 'Unknown error' }
|
|
331
|
-
: undefined,
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
public async getRunTimeline(id: string): Promise<RunTimeline | null> {
|
|
336
|
-
const run = await this.getRun(id)
|
|
337
|
-
if (!run) return null
|
|
338
|
-
return buildRunTimeline(await this.getRunHistory(id))
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
public async reconstructRunStateAt(
|
|
342
|
-
id: string,
|
|
343
|
-
at?: number | Date
|
|
344
|
-
): Promise<ReconstructedRunState | null> {
|
|
345
|
-
const timeline = await this.getRunTimeline(id)
|
|
346
|
-
if (!timeline) return null
|
|
347
|
-
return reconstructStateAt(timeline, at ?? timeline.length - 1)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
abstract getRunHistory(
|
|
351
|
-
runId: string
|
|
352
|
-
): Promise<Array<StepState & { stepName: string }>>
|
|
353
|
-
|
|
354
|
-
public async updateRunStatus(
|
|
355
|
-
id: string,
|
|
356
|
-
status: WorkflowStatus,
|
|
357
|
-
output?: any,
|
|
358
|
-
error?: SerializedError
|
|
359
|
-
): Promise<void> {
|
|
360
|
-
await this.mirrored(
|
|
361
|
-
() => this.updateRunStatusImpl(id, status, output, error),
|
|
362
|
-
(mirror) => mirror.updateRunStatus(id, status, output, error)
|
|
363
|
-
)
|
|
364
|
-
if (WORKFLOW_TERMINAL_STATES.has(status)) {
|
|
365
|
-
this.runExtension?.detachRunContext(id)
|
|
366
|
-
this.releaseContext(id)
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
protected abstract updateRunStatusImpl(
|
|
371
|
-
id: string,
|
|
372
|
-
status: WorkflowStatus,
|
|
373
|
-
output?: any,
|
|
374
|
-
error?: SerializedError
|
|
375
|
-
): Promise<void>
|
|
376
|
-
|
|
377
|
-
public async insertStepState(
|
|
378
|
-
runId: string,
|
|
379
|
-
stepName: string,
|
|
380
|
-
rpcName: string | null,
|
|
381
|
-
data: any,
|
|
382
|
-
stepOptions?: WorkflowStepOptions,
|
|
383
|
-
fromStepName?: string
|
|
384
|
-
): Promise<StepState> {
|
|
385
|
-
return this.mirrored(
|
|
386
|
-
() =>
|
|
387
|
-
this.insertStepStateImpl(
|
|
388
|
-
runId,
|
|
389
|
-
stepName,
|
|
390
|
-
rpcName,
|
|
391
|
-
data,
|
|
392
|
-
stepOptions,
|
|
393
|
-
fromStepName
|
|
394
|
-
),
|
|
395
|
-
(mirror, step) =>
|
|
396
|
-
mirror.insertStepState(runId, { ...step, stepName, rpcName, data })
|
|
397
|
-
)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
protected abstract insertStepStateImpl(
|
|
401
|
-
runId: string,
|
|
402
|
-
stepName: string,
|
|
403
|
-
rpcName: string | null,
|
|
404
|
-
data: any,
|
|
405
|
-
stepOptions?: WorkflowStepOptions,
|
|
406
|
-
fromStepName?: string
|
|
407
|
-
): Promise<StepState>
|
|
408
|
-
|
|
409
|
-
abstract getStepState(runId: string, stepName: string): Promise<StepState>
|
|
410
|
-
|
|
411
|
-
public async setStepRunning(stepId: string): Promise<void> {
|
|
412
|
-
await this.mirrored(
|
|
413
|
-
() => this.setStepRunningImpl(stepId),
|
|
414
|
-
(mirror) => mirror.setStepRunning(stepId)
|
|
415
|
-
)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
protected abstract setStepRunningImpl(stepId: string): Promise<void>
|
|
419
|
-
|
|
420
|
-
public async setStepScheduled(stepId: string): Promise<void> {
|
|
421
|
-
await this.mirrored(
|
|
422
|
-
() => this.setStepScheduledImpl(stepId),
|
|
423
|
-
(mirror) => mirror.setStepScheduled(stepId)
|
|
424
|
-
)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
protected abstract setStepScheduledImpl(stepId: string): Promise<void>
|
|
428
|
-
|
|
429
|
-
public async setStepResult(stepId: string, result: any): Promise<void> {
|
|
430
|
-
await this.mirrored(
|
|
431
|
-
() => this.setStepResultImpl(stepId, result),
|
|
432
|
-
(mirror) => mirror.setStepResult(stepId, result)
|
|
433
|
-
)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
protected abstract setStepResultImpl(
|
|
437
|
-
stepId: string,
|
|
438
|
-
result: any
|
|
439
|
-
): Promise<void>
|
|
440
|
-
|
|
441
|
-
public async setStepChildRunId(
|
|
442
|
-
stepId: string,
|
|
443
|
-
childRunId: string
|
|
444
|
-
): Promise<void> {
|
|
445
|
-
await this.mirrored(
|
|
446
|
-
() => this.setStepChildRunIdImpl(stepId, childRunId),
|
|
447
|
-
(mirror) => mirror.setStepChildRunId(stepId, childRunId)
|
|
448
|
-
)
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
protected abstract setStepChildRunIdImpl(
|
|
452
|
-
stepId: string,
|
|
453
|
-
childRunId: string
|
|
454
|
-
): Promise<void>
|
|
455
|
-
|
|
456
|
-
public async setStepError(stepId: string, error: Error): Promise<void> {
|
|
457
|
-
await this.mirrored(
|
|
458
|
-
() => this.setStepErrorImpl(stepId, error),
|
|
459
|
-
(mirror) => {
|
|
460
|
-
const serialized: SerializedError = {
|
|
461
|
-
message: error.message,
|
|
462
|
-
stack: error.stack,
|
|
463
|
-
code: (error as { code?: string }).code,
|
|
464
|
-
expected: isExpectedError(error),
|
|
465
|
-
}
|
|
466
|
-
return mirror.setStepError(stepId, serialized)
|
|
467
|
-
}
|
|
468
|
-
)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
protected abstract setStepErrorImpl(
|
|
472
|
-
stepId: string,
|
|
473
|
-
error: Error
|
|
474
|
-
): Promise<void>
|
|
475
|
-
|
|
476
|
-
public async createRetryAttempt(
|
|
477
|
-
failedStepId: string,
|
|
478
|
-
status: 'pending' | 'running'
|
|
479
|
-
): Promise<StepState> {
|
|
480
|
-
return this.mirrored(
|
|
481
|
-
() => this.createRetryAttemptImpl(failedStepId, status),
|
|
482
|
-
(mirror, newStep) =>
|
|
483
|
-
mirror.createRetryAttempt(failedStepId, {
|
|
484
|
-
...newStep,
|
|
485
|
-
stepName: (newStep as { stepName?: string }).stepName ?? '',
|
|
486
|
-
})
|
|
487
|
-
)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
protected abstract createRetryAttemptImpl(
|
|
491
|
-
failedStepId: string,
|
|
492
|
-
status: 'pending' | 'running'
|
|
493
|
-
): Promise<StepState>
|
|
494
|
-
|
|
495
|
-
abstract withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>
|
|
496
|
-
|
|
497
|
-
abstract withStepLock<T>(
|
|
498
|
-
runId: string,
|
|
499
|
-
stepName: string,
|
|
500
|
-
fn: () => Promise<T>
|
|
501
|
-
): Promise<T>
|
|
502
|
-
|
|
503
|
-
abstract close(): Promise<void>
|
|
504
|
-
|
|
505
|
-
abstract getCompletedGraphState(runId: string): Promise<{
|
|
506
|
-
completedNodeIds: string[]
|
|
507
|
-
failedNodeIds: string[]
|
|
508
|
-
branchKeys: Record<string, string>
|
|
509
|
-
}>
|
|
510
|
-
|
|
511
|
-
abstract getStepInstances(runId: string): Promise<
|
|
512
|
-
Array<{
|
|
513
|
-
stepName: string
|
|
514
|
-
status: StepStatus
|
|
515
|
-
fromStepName?: string
|
|
516
|
-
}>
|
|
517
|
-
>
|
|
518
|
-
|
|
519
|
-
abstract getNodeResults(
|
|
520
|
-
runId: string,
|
|
521
|
-
nodeIds: string[]
|
|
522
|
-
): Promise<Record<string, any>>
|
|
523
|
-
|
|
524
|
-
public async setBranchTaken(
|
|
525
|
-
stepId: string,
|
|
526
|
-
branchKey: string
|
|
527
|
-
): Promise<void> {
|
|
528
|
-
await this.mirrored(
|
|
529
|
-
() => this.setBranchTakenImpl(stepId, branchKey),
|
|
530
|
-
(mirror) => mirror.setBranchTaken(stepId, branchKey)
|
|
531
|
-
)
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
protected abstract setBranchTakenImpl(
|
|
535
|
-
stepId: string,
|
|
536
|
-
branchKey: string
|
|
537
|
-
): Promise<void>
|
|
538
|
-
|
|
539
|
-
public async updateRunState(
|
|
540
|
-
runId: string,
|
|
541
|
-
name: string,
|
|
542
|
-
value: unknown
|
|
543
|
-
): Promise<void> {
|
|
544
|
-
await this.mirrored(
|
|
545
|
-
() => this.updateRunStateImpl(runId, name, value),
|
|
546
|
-
(mirror) => mirror.updateRunState(runId, name, value)
|
|
547
|
-
)
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
protected abstract updateRunStateImpl(
|
|
551
|
-
runId: string,
|
|
552
|
-
name: string,
|
|
553
|
-
value: unknown
|
|
554
|
-
): Promise<void>
|
|
555
|
-
|
|
556
|
-
abstract getRunState(runId: string): Promise<Record<string, unknown>>
|
|
557
|
-
|
|
558
|
-
public async upsertWorkflowVersion(
|
|
559
|
-
name: string,
|
|
560
|
-
graphHash: string,
|
|
561
|
-
graph: any,
|
|
562
|
-
source: string,
|
|
563
|
-
status?: WorkflowVersionStatus
|
|
564
|
-
): Promise<void> {
|
|
565
|
-
await this.mirrored(
|
|
566
|
-
() =>
|
|
567
|
-
this.upsertWorkflowVersionImpl(name, graphHash, graph, source, status),
|
|
568
|
-
(mirror) =>
|
|
569
|
-
mirror.upsertWorkflowVersion(name, graphHash, graph, source, status)
|
|
570
|
-
)
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
protected abstract upsertWorkflowVersionImpl(
|
|
574
|
-
name: string,
|
|
575
|
-
graphHash: string,
|
|
576
|
-
graph: any,
|
|
577
|
-
source: string,
|
|
578
|
-
status?: WorkflowVersionStatus
|
|
579
|
-
): Promise<void>
|
|
580
|
-
|
|
581
|
-
public async updateWorkflowVersionStatus(
|
|
582
|
-
name: string,
|
|
583
|
-
graphHash: string,
|
|
584
|
-
status: WorkflowVersionStatus
|
|
585
|
-
): Promise<void> {
|
|
586
|
-
await this.mirrored(
|
|
587
|
-
() => this.updateWorkflowVersionStatusImpl(name, graphHash, status),
|
|
588
|
-
(mirror) => mirror.updateWorkflowVersionStatus(name, graphHash, status)
|
|
589
|
-
)
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
protected abstract updateWorkflowVersionStatusImpl(
|
|
593
|
-
name: string,
|
|
594
|
-
graphHash: string,
|
|
595
|
-
status: WorkflowVersionStatus
|
|
596
|
-
): Promise<void>
|
|
597
|
-
|
|
598
|
-
abstract getWorkflowVersion(
|
|
599
|
-
name: string,
|
|
600
|
-
graphHash: string
|
|
601
|
-
): Promise<{ graph: any; source: string } | null>
|
|
602
|
-
|
|
603
|
-
public async resumeWorkflow(
|
|
604
|
-
runId: string,
|
|
605
|
-
workflowName?: string
|
|
606
|
-
): Promise<void> {
|
|
607
|
-
const queueService = this.verifyQueueService()
|
|
608
|
-
if (!workflowName) {
|
|
609
|
-
const run = await this.getRun(runId)
|
|
610
|
-
workflowName = run?.workflow
|
|
611
|
-
}
|
|
612
|
-
await queueService.add(
|
|
613
|
-
this.getOrchestratorQueueName(workflowName),
|
|
614
|
-
{ runId },
|
|
615
|
-
{
|
|
616
|
-
...this.resolveStepJobOptions(),
|
|
617
|
-
group: this.getJobGroup(workflowName),
|
|
618
|
-
}
|
|
619
|
-
)
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Ids of runs that are stalled: still `running`, with no step in a state that
|
|
624
|
-
* something is expected to complete (`running`, `scheduled`, `suspended`),
|
|
625
|
-
* and no step activity since `before`.
|
|
626
|
-
*
|
|
627
|
-
* Returns nothing by default so a store that cannot express the query keeps
|
|
628
|
-
* working unchanged; a store that overrides it gains crash recovery through
|
|
629
|
-
* `recoverStalledRuns`.
|
|
630
|
-
*/
|
|
631
|
-
protected async findStalledRunIds(
|
|
632
|
-
_before: Date,
|
|
633
|
-
_limit: number
|
|
634
|
-
): Promise<string[]> {
|
|
635
|
-
return []
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Runs holding a step that has sat `pending` since before `before`, paired
|
|
640
|
-
* with the step that flagged them.
|
|
641
|
-
*
|
|
642
|
-
* Returns nothing by default so a store that cannot express the query keeps
|
|
643
|
-
* working unchanged — and, because it does not opt in, gains no re-dispatches
|
|
644
|
-
* either. A store must have an atomic `claimStepForExecution` before
|
|
645
|
-
* overriding this, or no concurrency for one to exclude: the relay makes
|
|
646
|
-
* duplicate dispatch routine, and the claim is what keeps a duplicate from
|
|
647
|
-
* becoming a second execution. Every `@pikku/kysely` dialect qualifies on its
|
|
648
|
-
* status-guarded claim, `mongodb` on the same claim expressed as a
|
|
649
|
-
* single-document update, `in-memory` on being inline and single-process —
|
|
650
|
-
* none of them overrides this yet.
|
|
651
|
-
*/
|
|
652
|
-
protected async findUndispatchedSteps(
|
|
653
|
-
_before: Date,
|
|
654
|
-
_limit: number
|
|
655
|
-
): Promise<Array<{ runId: string; stepId: string }>> {
|
|
656
|
-
return []
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
private readonly redispatchBackoff = new RedispatchBackoff()
|
|
660
|
-
|
|
661
|
-
private get sweepDeps() {
|
|
662
|
-
return {
|
|
663
|
-
resume: (runId: string) => this.resumeWorkflow(runId),
|
|
664
|
-
logger: getSingletonServices()?.logger,
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Re-drive runs whose next move was lost. Not self-starting — call it from a
|
|
670
|
-
* scheduled task at whatever interval suits the workload.
|
|
671
|
-
*/
|
|
672
|
-
public async recoverStalledRuns(options?: {
|
|
673
|
-
stalledAfterMs?: number
|
|
674
|
-
limit?: number
|
|
675
|
-
}): Promise<{ resumed: string[] }> {
|
|
676
|
-
return sweepStalledRuns(
|
|
677
|
-
(before, limit) => this.findStalledRunIds(before, limit),
|
|
678
|
-
this.redispatchBackoff,
|
|
679
|
-
options,
|
|
680
|
-
this.sweepDeps
|
|
681
|
-
)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Re-drive steps whose dispatch was lost. Not self-starting — call it from a
|
|
686
|
-
* scheduled task; ~30s suits a queue whose `pending`→`running` latency is
|
|
687
|
-
* well under that.
|
|
688
|
-
*/
|
|
689
|
-
public async relayUndispatchedSteps(options?: {
|
|
690
|
-
undispatchedAfterMs?: number
|
|
691
|
-
limit?: number
|
|
692
|
-
}): Promise<{ redispatched: string[] }> {
|
|
693
|
-
return sweepUndispatchedSteps(
|
|
694
|
-
(before, limit) => this.findUndispatchedSteps(before, limit),
|
|
695
|
-
this.redispatchBackoff,
|
|
696
|
-
options,
|
|
697
|
-
this.sweepDeps
|
|
698
|
-
)
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
protected resolveStepJobOptions(
|
|
702
|
-
stepOptions?: WorkflowStepOptions
|
|
703
|
-
): JobOptions {
|
|
704
|
-
return stepJobOptions(stepOptions)
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
public async queueStepWorker(
|
|
708
|
-
runId: string,
|
|
709
|
-
stepName: string,
|
|
710
|
-
rpcName: string,
|
|
711
|
-
data: any,
|
|
712
|
-
stepOptions?: WorkflowStepOptions,
|
|
713
|
-
fromStepName?: string
|
|
714
|
-
): Promise<void> {
|
|
715
|
-
const queueService = this.verifyQueueService()
|
|
716
|
-
await queueService.add(
|
|
717
|
-
this.getStepWorkerQueueName(rpcName),
|
|
718
|
-
{ runId, stepName, rpcName, data, fromStepName },
|
|
719
|
-
{
|
|
720
|
-
...this.resolveStepJobOptions(stepOptions),
|
|
721
|
-
group: this.getJobGroup(rpcName),
|
|
722
|
-
}
|
|
723
|
-
)
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
public async executeWorkflowSleepCompleted(
|
|
727
|
-
runId: string,
|
|
728
|
-
stepId: string
|
|
729
|
-
): Promise<void> {
|
|
730
|
-
await this.setStepResult(stepId, null)
|
|
731
|
-
await this.resumeWorkflow(runId)
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
protected async scheduleOrchestratorRetry(
|
|
735
|
-
runId: string,
|
|
736
|
-
retryDelay?: number | string,
|
|
737
|
-
workflowName?: string
|
|
738
|
-
): Promise<void> {
|
|
739
|
-
const queueService = this.verifyQueueService()
|
|
740
|
-
if (!workflowName) {
|
|
741
|
-
const run = await this.getRun(runId)
|
|
742
|
-
workflowName = run?.workflow
|
|
743
|
-
}
|
|
744
|
-
await queueService.add(
|
|
745
|
-
this.getOrchestratorQueueName(workflowName),
|
|
746
|
-
{ runId },
|
|
747
|
-
{
|
|
748
|
-
...(retryDelay
|
|
749
|
-
? { delay: getDurationInMilliseconds(retryDelay) }
|
|
750
|
-
: undefined),
|
|
751
|
-
group: this.getJobGroup(workflowName),
|
|
752
|
-
}
|
|
753
|
-
)
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
protected async dispatchStep(
|
|
757
|
-
runId: string,
|
|
758
|
-
stepName: string,
|
|
759
|
-
rpcName: string,
|
|
760
|
-
data: unknown,
|
|
761
|
-
stepOptions?: WorkflowStepOptions,
|
|
762
|
-
fromStepName?: string
|
|
763
|
-
): Promise<boolean> {
|
|
764
|
-
const target = await stepDispatchTarget(rpcName, stepName, () =>
|
|
765
|
-
this.isInline(runId)
|
|
766
|
-
)
|
|
767
|
-
if (target === 'inline') {
|
|
768
|
-
return false
|
|
769
|
-
}
|
|
770
|
-
try {
|
|
771
|
-
await getSingletonServices()!.queueService!.add(
|
|
772
|
-
this.getStepWorkerQueueName(rpcName),
|
|
773
|
-
{ runId, stepName, rpcName, data, fromStepName },
|
|
774
|
-
{
|
|
775
|
-
...this.resolveStepJobOptions(stepOptions),
|
|
776
|
-
group: this.getJobGroup(rpcName),
|
|
777
|
-
}
|
|
778
|
-
)
|
|
779
|
-
} catch (cause) {
|
|
780
|
-
throw new WorkflowDispatchException(runId, stepName, { cause })
|
|
781
|
-
}
|
|
782
|
-
return true
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
protected async scheduleSleep(
|
|
786
|
-
runId: string,
|
|
787
|
-
stepId: string,
|
|
788
|
-
duration: number | string
|
|
789
|
-
): Promise<boolean> {
|
|
790
|
-
if (
|
|
791
|
-
(await this.isInline(runId)) ||
|
|
792
|
-
!getSingletonServices()?.schedulerService
|
|
793
|
-
) {
|
|
794
|
-
return false
|
|
795
|
-
}
|
|
796
|
-
await getSingletonServices()!.schedulerService!.scheduleRPC(
|
|
797
|
-
duration,
|
|
798
|
-
this.getConfig().sleeperRPCName,
|
|
799
|
-
{ runId, stepId }
|
|
800
|
-
)
|
|
801
|
-
return true
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
public setRunExtension<T extends WorkflowRunExtension>(
|
|
805
|
-
create: (engine: WorkflowRunEngine) => T
|
|
806
|
-
): T {
|
|
807
|
-
const engine: WorkflowRunEngine = {
|
|
808
|
-
inlineStep: this.inlineStep.bind(this),
|
|
809
|
-
updateRunStatus: this.updateRunStatus.bind(this),
|
|
810
|
-
onChildWorkflowFailed: this.onChildWorkflowFailed.bind(this),
|
|
811
|
-
verifyStepName: this.verifyStepName.bind(this),
|
|
812
|
-
}
|
|
813
|
-
const extension = create(engine)
|
|
814
|
-
this.runExtension = extension
|
|
815
|
-
return extension
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
public getRunExtension(): WorkflowRunExtension | undefined {
|
|
819
|
-
return this.runExtension
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
public async startWorkflow<I>(
|
|
823
|
-
name: string,
|
|
824
|
-
input: I,
|
|
825
|
-
wire: WorkflowRunWire,
|
|
826
|
-
rpcService: PikkuRPC,
|
|
827
|
-
options?: {
|
|
828
|
-
inline?: boolean
|
|
829
|
-
startNode?: string
|
|
830
|
-
actors?: ScenarioPersonas
|
|
831
|
-
onRunCreated?: (runId: string) => void
|
|
832
|
-
}
|
|
833
|
-
): Promise<{ runId: string }> {
|
|
834
|
-
const resolved = resolveWorkflowMeta(name)
|
|
835
|
-
const workflowMeta = resolved?.meta
|
|
836
|
-
const packageName = resolved?.packageName ?? null
|
|
837
|
-
|
|
838
|
-
if (!workflowMeta) {
|
|
839
|
-
throw new WorkflowNotFoundError(name)
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (workflowMeta.source === 'graph') {
|
|
843
|
-
// A caller-supplied startNode must be one of the graph's declared entry
|
|
844
|
-
// nodes. startWorkflow is the boundary the public
|
|
845
|
-
// `/workflow/:name/graph/:nodeId` route and triggers enter through, so
|
|
846
|
-
// without this a request could name any dependency-free node — one whose
|
|
847
|
-
// input reads only `trigger` — and fire its RPC directly with
|
|
848
|
-
// attacker-chosen data, skipping every upstream eligibility, validation or
|
|
849
|
-
// approval node. (Internal resume/replay drives runWorkflowGraph directly
|
|
850
|
-
// and is unaffected.)
|
|
851
|
-
if (
|
|
852
|
-
options?.startNode &&
|
|
853
|
-
!(workflowMeta.entryNodeIds ?? []).includes(options.startNode)
|
|
854
|
-
) {
|
|
855
|
-
throw new Error(
|
|
856
|
-
`Workflow graph '${name}': '${options.startNode}' is not a declared entry node`
|
|
857
|
-
)
|
|
858
|
-
}
|
|
859
|
-
const shouldInline =
|
|
860
|
-
options?.inline || !getSingletonServices()?.queueService
|
|
861
|
-
return runWorkflowGraph(
|
|
862
|
-
this,
|
|
863
|
-
name,
|
|
864
|
-
input,
|
|
865
|
-
rpcService,
|
|
866
|
-
shouldInline,
|
|
867
|
-
options?.startNode,
|
|
868
|
-
wire,
|
|
869
|
-
workflowMeta
|
|
870
|
-
)
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const registrations = pikkuState(packageName, 'workflows', 'registrations')
|
|
874
|
-
const workflow = registrations.get(resolved?.resolvedName ?? name)
|
|
875
|
-
|
|
876
|
-
if (!workflow) {
|
|
877
|
-
throw new WorkflowNotFoundError(name)
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (!workflowMeta.graphHash) {
|
|
881
|
-
throw new Error(`Missing workflow graphHash for '${name}'`)
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const shouldInline =
|
|
885
|
-
options?.inline || !getSingletonServices()?.queueService
|
|
886
|
-
|
|
887
|
-
const runId = await this.createRun(
|
|
888
|
-
name,
|
|
889
|
-
input,
|
|
890
|
-
shouldInline,
|
|
891
|
-
workflowMeta.graphHash,
|
|
892
|
-
wire,
|
|
893
|
-
{
|
|
894
|
-
deterministic: workflowMeta.deterministic,
|
|
895
|
-
plannedSteps: workflowMeta.plannedSteps,
|
|
896
|
-
}
|
|
897
|
-
)
|
|
898
|
-
|
|
899
|
-
options?.onRunCreated?.(runId)
|
|
900
|
-
|
|
901
|
-
await this.runExtension?.attachRunContext(runId, workflowMeta, options)
|
|
902
|
-
|
|
903
|
-
if (shouldInline) {
|
|
904
|
-
this.registerInlineRun(runId)
|
|
905
|
-
try {
|
|
906
|
-
await this.runWorkflowJob(runId, rpcService)
|
|
907
|
-
} catch (error: any) {
|
|
908
|
-
if (
|
|
909
|
-
error.name !== 'WorkflowAsyncException' &&
|
|
910
|
-
error.name !== 'WorkflowCancelledException' &&
|
|
911
|
-
error.name !== 'WorkflowSuspendedException' &&
|
|
912
|
-
error.name !== 'WorkflowDispatchException'
|
|
913
|
-
) {
|
|
914
|
-
await this.updateRunStatus(runId, 'failed', undefined, {
|
|
915
|
-
name: error.name,
|
|
916
|
-
message: error.message,
|
|
917
|
-
stack: error.stack,
|
|
918
|
-
})
|
|
919
|
-
getSingletonServices()!.logger.error(
|
|
920
|
-
`Workflow ${name} (run ${runId}) failed:`,
|
|
921
|
-
isExpectedError(error) ? error.message : error
|
|
922
|
-
)
|
|
923
|
-
throw error
|
|
924
|
-
}
|
|
925
|
-
if (error.name === 'WorkflowDispatchException') {
|
|
926
|
-
throw error
|
|
927
|
-
}
|
|
928
|
-
} finally {
|
|
929
|
-
this.unregisterInlineRun(runId)
|
|
930
|
-
this.runExtension?.detachRunContext(runId)
|
|
931
|
-
}
|
|
932
|
-
} else {
|
|
933
|
-
await this.resumeWorkflow(runId)
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
return { runId }
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
public async runToCompletion<I>(
|
|
940
|
-
name: string,
|
|
941
|
-
input: I,
|
|
942
|
-
rpcService: PikkuRPC,
|
|
943
|
-
options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
|
|
944
|
-
): Promise<any> {
|
|
945
|
-
const { runId } = await this.startWorkflow(
|
|
946
|
-
name,
|
|
947
|
-
input,
|
|
948
|
-
options?.wire ?? { type: 'internal' },
|
|
949
|
-
rpcService,
|
|
950
|
-
{ inline: true }
|
|
951
|
-
)
|
|
952
|
-
const run = await this.awaitRunEnd(runId, options?.pollIntervalMs ?? 1000)
|
|
953
|
-
if (run.status === 'failed') {
|
|
954
|
-
throw new WorkflowRunFailedError(run.error?.message)
|
|
955
|
-
}
|
|
956
|
-
if (run.status === 'cancelled') {
|
|
957
|
-
throw new WorkflowRunCancelledError()
|
|
958
|
-
}
|
|
959
|
-
return run.output
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
protected async awaitRunEnd(
|
|
963
|
-
runId: string,
|
|
964
|
-
maxIntervalMs: number
|
|
965
|
-
): Promise<WorkflowRun> {
|
|
966
|
-
let interval = Math.min(WORKFLOW_POLL_MIN_MS, maxIntervalMs)
|
|
967
|
-
while (true) {
|
|
968
|
-
const run = await this.getRun(runId)
|
|
969
|
-
if (!run) {
|
|
970
|
-
throw new WorkflowRunNotFoundError(runId)
|
|
971
|
-
}
|
|
972
|
-
if (WORKFLOW_END_STATES.has(run.status)) {
|
|
973
|
-
return run
|
|
974
|
-
}
|
|
975
|
-
await this.waitBeforeNextRead(interval)
|
|
976
|
-
interval = Math.min(interval * WORKFLOW_POLL_FACTOR, maxIntervalMs)
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
protected async waitBeforeNextRead(ms: number): Promise<void> {
|
|
981
|
-
await new Promise((resolve) => setTimeout(resolve, ms))
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
protected async listStepStates(
|
|
985
|
-
_runId: string
|
|
986
|
-
): Promise<Array<StepState & { stepName: string }> | null> {
|
|
987
|
-
return null
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
private async beginReplay(runId: string): Promise<void> {
|
|
991
|
-
const context = this.enterExecution(runId)
|
|
992
|
-
context.ordinals = new Map()
|
|
993
|
-
context.lastStep = undefined
|
|
994
|
-
context.replay = {}
|
|
995
|
-
const steps = await this.listStepStates(runId)
|
|
996
|
-
if (steps) {
|
|
997
|
-
context.replay.steps = new Map(steps.map((step) => [step.stepName, step]))
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
private endReplay(runId: string): void {
|
|
1002
|
-
this.exitExecution(runId)
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
private async loadOrCreateStep(
|
|
1006
|
-
runId: string,
|
|
1007
|
-
stepName: string,
|
|
1008
|
-
create: () => Promise<StepState>
|
|
1009
|
-
): Promise<StepState> {
|
|
1010
|
-
const snapshot = this.runContexts.get(runId)?.replay?.steps
|
|
1011
|
-
if (snapshot) {
|
|
1012
|
-
const cached = snapshot.get(stepName)
|
|
1013
|
-
if (cached) {
|
|
1014
|
-
return cached
|
|
1015
|
-
}
|
|
1016
|
-
} else {
|
|
1017
|
-
try {
|
|
1018
|
-
return await this.getStepState(runId, stepName)
|
|
1019
|
-
} catch {
|
|
1020
|
-
// knowledge: decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
let step: StepState
|
|
1025
|
-
try {
|
|
1026
|
-
step = await create()
|
|
1027
|
-
} catch (error) {
|
|
1028
|
-
try {
|
|
1029
|
-
step = await this.getStepState(runId, stepName)
|
|
1030
|
-
} catch {
|
|
1031
|
-
throw error
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
snapshot?.set(stepName, step)
|
|
1035
|
-
return step
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
private async getRunIdentity(runId: string): Promise<WorkflowRun | null> {
|
|
1039
|
-
const replay = this.runContexts.get(runId)?.replay
|
|
1040
|
-
if (replay?.run) {
|
|
1041
|
-
return replay.run
|
|
1042
|
-
}
|
|
1043
|
-
const run = await this.getRun(runId)
|
|
1044
|
-
if (run && replay) {
|
|
1045
|
-
replay.run = run
|
|
1046
|
-
}
|
|
1047
|
-
return run
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
private lastStepName(runId: string): string | undefined {
|
|
1051
|
-
return this.runContexts.get(runId)?.lastStep
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
private nextStepKey(runId: string, logicalStepName: string): string {
|
|
1055
|
-
const context = this.contextFor(runId)
|
|
1056
|
-
const ordinal = context.ordinals.get(logicalStepName) ?? 0
|
|
1057
|
-
context.ordinals.set(logicalStepName, ordinal + 1)
|
|
1058
|
-
const stepName =
|
|
1059
|
-
ordinal === 0 ? logicalStepName : `${logicalStepName}#${ordinal}`
|
|
1060
|
-
context.lastStep = stepName
|
|
1061
|
-
return stepName
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
public async runWorkflowJob(
|
|
1065
|
-
runId: string,
|
|
1066
|
-
rpcService: PikkuRPC
|
|
1067
|
-
): Promise<void> {
|
|
1068
|
-
await this.beginReplay(runId)
|
|
1069
|
-
try {
|
|
1070
|
-
await this.runWorkflowJobInner(runId, rpcService)
|
|
1071
|
-
} finally {
|
|
1072
|
-
this.endReplay(runId)
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
private async runWorkflowJobInner(
|
|
1077
|
-
runId: string,
|
|
1078
|
-
rpcService: PikkuRPC
|
|
1079
|
-
): Promise<void> {
|
|
1080
|
-
const run = await this.getRunIdentity(runId)
|
|
1081
|
-
if (!run) {
|
|
1082
|
-
throw new WorkflowRunNotFoundError(runId)
|
|
1083
|
-
}
|
|
1084
|
-
if (isRunSettled(run.status)) return
|
|
1085
|
-
|
|
1086
|
-
const resolved = resolveWorkflowMeta(run.workflow)
|
|
1087
|
-
const workflowMeta = resolved?.meta
|
|
1088
|
-
const pkgName = resolved?.packageName ?? null
|
|
1089
|
-
|
|
1090
|
-
if (
|
|
1091
|
-
run.graphHash &&
|
|
1092
|
-
workflowMeta?.graphHash &&
|
|
1093
|
-
run.graphHash !== workflowMeta.graphHash
|
|
1094
|
-
) {
|
|
1095
|
-
await this.runVersionMismatchFallback(run, workflowMeta, rpcService)
|
|
1096
|
-
return
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
if (workflowMeta?.source === 'graph') {
|
|
1100
|
-
await continueGraph(this, runId, run.workflow)
|
|
1101
|
-
const updatedRun = await this.getRun(runId)
|
|
1102
|
-
if (updatedRun?.status === 'completed') {
|
|
1103
|
-
await this.onChildWorkflowCompleted(updatedRun, updatedRun.output)
|
|
1104
|
-
} else if (
|
|
1105
|
-
updatedRun?.status === 'failed' ||
|
|
1106
|
-
updatedRun?.status === 'cancelled'
|
|
1107
|
-
) {
|
|
1108
|
-
await this.onChildWorkflowFailed(
|
|
1109
|
-
updatedRun,
|
|
1110
|
-
new Error(updatedRun.error?.message || 'Child workflow failed')
|
|
1111
|
-
)
|
|
1112
|
-
}
|
|
1113
|
-
return
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
const registrations = pikkuState(pkgName, 'workflows', 'registrations')
|
|
1117
|
-
if (!workflowMeta) {
|
|
1118
|
-
throw new PikkuMissingMetaError(
|
|
1119
|
-
`Missing generated metadata for workflow '${run.workflow}'`
|
|
1120
|
-
)
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
const workflow = registrations.get(resolved?.resolvedName ?? run.workflow)
|
|
1124
|
-
if (!workflow) {
|
|
1125
|
-
throw new WorkflowNotFoundError(run.workflow)
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
await this.withRunLock(runId, async () => {
|
|
1129
|
-
const addonNs = run.workflow.includes(':')
|
|
1130
|
-
? run.workflow.substring(0, run.workflow.indexOf(':'))
|
|
1131
|
-
: null
|
|
1132
|
-
const workflowWire = this.createWorkflowWire(
|
|
1133
|
-
run.workflow,
|
|
1134
|
-
runId,
|
|
1135
|
-
rpcService,
|
|
1136
|
-
addonNs
|
|
1137
|
-
)
|
|
1138
|
-
workflowWire.pikkuUserId = run.wire?.pikkuUserId
|
|
1139
|
-
// No `rpc` yet — runPikkuFunc attaches it lazily for the invocation.
|
|
1140
|
-
const wire: PikkuRawWire = {
|
|
1141
|
-
workflow: workflowWire,
|
|
1142
|
-
pikkuUserId: run.wire?.pikkuUserId,
|
|
1143
|
-
}
|
|
1144
|
-
this.runExtension?.decorateRunWire(wire, {
|
|
1145
|
-
runId,
|
|
1146
|
-
workflowMeta,
|
|
1147
|
-
workflowWire,
|
|
1148
|
-
})
|
|
1149
|
-
|
|
1150
|
-
const lifecycle: RunLifecycleContext = {
|
|
1151
|
-
runId,
|
|
1152
|
-
run,
|
|
1153
|
-
workflowMeta,
|
|
1154
|
-
workflow,
|
|
1155
|
-
wire,
|
|
1156
|
-
packageName: pkgName,
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
let outcome: 'completed' | 'failed' | 'interrupted' = 'completed'
|
|
1160
|
-
let failure: any
|
|
1161
|
-
try {
|
|
1162
|
-
await this.runExtension?.onBeforeRunFunc(lifecycle)
|
|
1163
|
-
|
|
1164
|
-
const result = await runPikkuFunc(
|
|
1165
|
-
'workflow',
|
|
1166
|
-
workflowMeta.name,
|
|
1167
|
-
workflowMeta.pikkuFuncId,
|
|
1168
|
-
{
|
|
1169
|
-
singletonServices: getSingletonServices()!,
|
|
1170
|
-
wire,
|
|
1171
|
-
createWireServices: getCreateWireServices(),
|
|
1172
|
-
data: () => run.input,
|
|
1173
|
-
packageName: pkgName ?? undefined,
|
|
1174
|
-
}
|
|
1175
|
-
)
|
|
1176
|
-
|
|
1177
|
-
await this.updateRunStatus(runId, 'completed', result)
|
|
1178
|
-
await this.onChildWorkflowCompleted(run, result)
|
|
1179
|
-
} catch (error: any) {
|
|
1180
|
-
failure = error
|
|
1181
|
-
|
|
1182
|
-
if (error instanceof WorkflowAsyncException) {
|
|
1183
|
-
outcome = 'interrupted'
|
|
1184
|
-
throw error
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
if (error instanceof WorkflowCancelledException) {
|
|
1188
|
-
outcome = 'failed'
|
|
1189
|
-
await this.updateRunStatus(runId, 'cancelled', undefined, {
|
|
1190
|
-
message: error.message || 'Workflow cancelled',
|
|
1191
|
-
stack: '',
|
|
1192
|
-
code: 'WORKFLOW_CANCELLED',
|
|
1193
|
-
})
|
|
1194
|
-
await this.onChildWorkflowFailed(run, error)
|
|
1195
|
-
throw error
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
if (error instanceof WorkflowSuspendedException) {
|
|
1199
|
-
outcome = 'interrupted'
|
|
1200
|
-
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
1201
|
-
message: error.message || 'Workflow suspended',
|
|
1202
|
-
stack: '',
|
|
1203
|
-
code: 'WORKFLOW_SUSPENDED',
|
|
1204
|
-
})
|
|
1205
|
-
throw error
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
outcome = 'failed'
|
|
1209
|
-
await this.updateRunStatus(runId, 'failed', undefined, {
|
|
1210
|
-
message: error.message,
|
|
1211
|
-
stack: error.stack,
|
|
1212
|
-
code: error.code,
|
|
1213
|
-
})
|
|
1214
|
-
await this.onChildWorkflowFailed(run, error)
|
|
1215
|
-
|
|
1216
|
-
throw error
|
|
1217
|
-
} finally {
|
|
1218
|
-
await this.runExtension?.onAfterRunFunc(lifecycle, outcome, failure)
|
|
1219
|
-
}
|
|
1220
|
-
})
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
private async onChildWorkflowCompleted(
|
|
1224
|
-
childRun: WorkflowRun,
|
|
1225
|
-
result: any
|
|
1226
|
-
): Promise<void> {
|
|
1227
|
-
const { parentRunId, parentStepId } = childRun.wire ?? {}
|
|
1228
|
-
if (!parentRunId || !parentStepId) return
|
|
1229
|
-
|
|
1230
|
-
this.logger?.debug(
|
|
1231
|
-
`Child workflow ${childRun.id} completed, updating parent step ${parentStepId}`
|
|
1232
|
-
)
|
|
1233
|
-
await this.setStepResult(parentStepId, result)
|
|
1234
|
-
await this.resumeWorkflow(parentRunId)
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
protected async onChildWorkflowFailed(
|
|
1238
|
-
childRun: WorkflowRun,
|
|
1239
|
-
error: Error
|
|
1240
|
-
): Promise<void> {
|
|
1241
|
-
const { parentRunId, parentStepId } = childRun.wire ?? {}
|
|
1242
|
-
if (!parentRunId || !parentStepId) return
|
|
1243
|
-
|
|
1244
|
-
this.logger?.debug(
|
|
1245
|
-
`Child workflow ${childRun.id} failed, updating parent step ${parentStepId}`
|
|
1246
|
-
)
|
|
1247
|
-
await this.setStepError(parentStepId, error)
|
|
1248
|
-
await this.resumeWorkflow(parentRunId)
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
private async runVersionMismatchFallback(
|
|
1252
|
-
run: WorkflowRun,
|
|
1253
|
-
currentMeta: { source: string },
|
|
1254
|
-
rpcService: PikkuRPC
|
|
1255
|
-
): Promise<void> {
|
|
1256
|
-
const source = currentMeta.source
|
|
1257
|
-
|
|
1258
|
-
if (source === 'complex') {
|
|
1259
|
-
await this.updateRunStatus(run.id, 'failed', undefined, {
|
|
1260
|
-
message: `Workflow '${run.workflow}' definition changed. Complex workflows with inline steps cannot be migrated.`,
|
|
1261
|
-
stack: '',
|
|
1262
|
-
code: 'VERSION_CONFLICT',
|
|
1263
|
-
})
|
|
1264
|
-
return
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
const version = await this.getWorkflowVersion(run.workflow, run.graphHash!)
|
|
1268
|
-
if (!version) {
|
|
1269
|
-
await this.updateRunStatus(run.id, 'failed', undefined, {
|
|
1270
|
-
message: `Workflow '${run.workflow}' version '${run.graphHash}' not found. Cannot resume with changed definition.`,
|
|
1271
|
-
stack: '',
|
|
1272
|
-
code: 'VERSION_NOT_FOUND',
|
|
1273
|
-
})
|
|
1274
|
-
return
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
await runFromMeta(this, run.id, version.graph, rpcService)
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
public async executeWorkflowStep(
|
|
1281
|
-
runId: string,
|
|
1282
|
-
stepName: string,
|
|
1283
|
-
rpcName: string,
|
|
1284
|
-
data: any,
|
|
1285
|
-
rpcService: PikkuRPC
|
|
1286
|
-
): Promise<void> {
|
|
1287
|
-
this.enterExecution(runId)
|
|
1288
|
-
try {
|
|
1289
|
-
await this.executeWorkflowStepInner(
|
|
1290
|
-
runId,
|
|
1291
|
-
stepName,
|
|
1292
|
-
rpcName,
|
|
1293
|
-
data,
|
|
1294
|
-
rpcService
|
|
1295
|
-
)
|
|
1296
|
-
} finally {
|
|
1297
|
-
this.exitExecution(runId)
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
/**
|
|
1302
|
-
* Take sole ownership of a step before it runs, returning the state to run
|
|
1303
|
-
* under — or `null` when another dispatch already owns it.
|
|
1304
|
-
*
|
|
1305
|
-
* Dispatch is at-least-once by design: the relay re-dispatches steps it
|
|
1306
|
-
* believes were dropped, and a queue can redeliver a job it already handed
|
|
1307
|
-
* out. This is the one place that keeps a duplicate dispatch from becoming a
|
|
1308
|
-
* second execution of a side-effecting step, so it is only as strong as the
|
|
1309
|
-
* exclusion it is built on — and `withStepLock` excludes nothing unless the
|
|
1310
|
-
* store backs it with a real primitive. A store able to express the decision
|
|
1311
|
-
* as one conditional write should override this rather than reach for a lock,
|
|
1312
|
-
* which is what `@pikku/kysely` does with a status-guarded `UPDATE`.
|
|
1313
|
-
*/
|
|
1314
|
-
protected async claimStepForExecution(
|
|
1315
|
-
runId: string,
|
|
1316
|
-
stepName: string,
|
|
1317
|
-
rpcName: string
|
|
1318
|
-
): Promise<StepState | null> {
|
|
1319
|
-
return this.withStepLock(runId, stepName, () =>
|
|
1320
|
-
claimStepByReadThenWrite(this, runId, stepName, rpcName)
|
|
1321
|
-
)
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
private async executeWorkflowStepInner(
|
|
1325
|
-
runId: string,
|
|
1326
|
-
stepName: string,
|
|
1327
|
-
rpcName: string,
|
|
1328
|
-
data: any,
|
|
1329
|
-
rpcService: PikkuRPC
|
|
1330
|
-
): Promise<void> {
|
|
1331
|
-
const claimed = await this.claimStepForExecution(runId, stepName, rpcName)
|
|
1332
|
-
|
|
1333
|
-
if (!claimed) {
|
|
1334
|
-
return
|
|
1335
|
-
}
|
|
1336
|
-
const stepState = claimed
|
|
1337
|
-
|
|
1338
|
-
try {
|
|
1339
|
-
let result: any
|
|
1340
|
-
|
|
1341
|
-
const run = await this.getRun(runId)
|
|
1342
|
-
if (!run) {
|
|
1343
|
-
throw new Error(`Workflow run not found: ${runId}`)
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
const meta = pikkuState(null, 'workflows', 'meta')
|
|
1347
|
-
const workflowMeta = meta[run.workflow]
|
|
1348
|
-
|
|
1349
|
-
const isGraphWorkflow = workflowMeta?.source === 'graph'
|
|
1350
|
-
let graphNodeId: string | undefined
|
|
1351
|
-
if (isGraphWorkflow && workflowMeta?.nodes) {
|
|
1352
|
-
if (stepName in workflowMeta.nodes) {
|
|
1353
|
-
graphNodeId = stepName
|
|
1354
|
-
} else {
|
|
1355
|
-
const base = stripInstanceOrdinal(stepName)
|
|
1356
|
-
if (base !== stepName && base in workflowMeta.nodes)
|
|
1357
|
-
graphNodeId = base
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
if (graphNodeId) {
|
|
1361
|
-
result = await executeGraphStep(
|
|
1362
|
-
this,
|
|
1363
|
-
rpcService,
|
|
1364
|
-
runId,
|
|
1365
|
-
stepState.stepId,
|
|
1366
|
-
graphNodeId,
|
|
1367
|
-
workflowMeta!.nodes![graphNodeId].rpcName,
|
|
1368
|
-
data,
|
|
1369
|
-
run.workflow
|
|
1370
|
-
)
|
|
1371
|
-
} else {
|
|
1372
|
-
const subWorkflowMeta = meta[rpcName]
|
|
1373
|
-
if (subWorkflowMeta) {
|
|
1374
|
-
const childWire: WorkflowRunWire = {
|
|
1375
|
-
type: 'workflow',
|
|
1376
|
-
id: rpcName,
|
|
1377
|
-
parentRunId: runId,
|
|
1378
|
-
parentStepId: stepState.stepId,
|
|
1379
|
-
pikkuUserId: run.wire?.pikkuUserId,
|
|
1380
|
-
}
|
|
1381
|
-
const shouldInline = !getSingletonServices()?.queueService
|
|
1382
|
-
const { runId: childRunId } = await this.startWorkflow(
|
|
1383
|
-
rpcName,
|
|
1384
|
-
data,
|
|
1385
|
-
childWire,
|
|
1386
|
-
rpcService,
|
|
1387
|
-
{ inline: shouldInline }
|
|
1388
|
-
)
|
|
1389
|
-
await this.setStepChildRunId(stepState.stepId, childRunId)
|
|
1390
|
-
if (shouldInline) {
|
|
1391
|
-
const childRun = await this.getRun(childRunId)
|
|
1392
|
-
if (childRun?.status === 'failed') {
|
|
1393
|
-
throw new Error(childRun.error?.message || 'Sub-workflow failed')
|
|
1394
|
-
}
|
|
1395
|
-
if (childRun?.status === 'cancelled') {
|
|
1396
|
-
throw new Error('Sub-workflow was cancelled')
|
|
1397
|
-
}
|
|
1398
|
-
result = childRun?.output
|
|
1399
|
-
} else {
|
|
1400
|
-
throw new ChildWorkflowStartedException(
|
|
1401
|
-
runId,
|
|
1402
|
-
stepState.stepId,
|
|
1403
|
-
childRunId
|
|
1404
|
-
)
|
|
1405
|
-
}
|
|
1406
|
-
} else {
|
|
1407
|
-
result = await this.invokeStepRpc(
|
|
1408
|
-
runId,
|
|
1409
|
-
stepName,
|
|
1410
|
-
stepState,
|
|
1411
|
-
rpcName,
|
|
1412
|
-
data,
|
|
1413
|
-
rpcService,
|
|
1414
|
-
run
|
|
1415
|
-
)
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
await this.setStepResult(stepState.stepId, result)
|
|
1420
|
-
|
|
1421
|
-
await this.resumeWorkflow(runId)
|
|
1422
|
-
} catch (error: any) {
|
|
1423
|
-
if (error instanceof ChildWorkflowStartedException) {
|
|
1424
|
-
this.logger?.debug(
|
|
1425
|
-
`Workflow step '${stepName}': child workflow ${error.childRunId} started, waiting for completion`
|
|
1426
|
-
)
|
|
1427
|
-
return
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
if (error instanceof RPCNotFoundError) {
|
|
1431
|
-
await this.setStepError(stepState.stepId, error)
|
|
1432
|
-
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
1433
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
1434
|
-
code: 'RPC_NOT_FOUND',
|
|
1435
|
-
})
|
|
1436
|
-
return
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
await this.setStepError(stepState.stepId, error)
|
|
1440
|
-
|
|
1441
|
-
const maxAttempts = (stepState.retries ?? DEFAULT_STEP_RETRIES) + 1
|
|
1442
|
-
const retriesExhausted = stepState.attemptCount >= maxAttempts
|
|
1443
|
-
|
|
1444
|
-
if (retriesExhausted) {
|
|
1445
|
-
await this.resumeWorkflow(runId)
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
throw error
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
public async orchestrateWorkflow(
|
|
1453
|
-
runId: string,
|
|
1454
|
-
rpcService: PikkuRPC
|
|
1455
|
-
): Promise<void> {
|
|
1456
|
-
try {
|
|
1457
|
-
await this.runWorkflowJob(runId, rpcService)
|
|
1458
|
-
} catch (error: any) {
|
|
1459
|
-
if (
|
|
1460
|
-
error.name === 'WorkflowAsyncException' ||
|
|
1461
|
-
error.name === 'WorkflowCancelledException' ||
|
|
1462
|
-
error.name === 'WorkflowSuspendedException'
|
|
1463
|
-
) {
|
|
1464
|
-
return
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
if (error.name === 'WorkflowDispatchException') {
|
|
1468
|
-
getSingletonServices()!.logger.warn(
|
|
1469
|
-
`Workflow run ${runId} could not dispatch a step (queue unavailable); leaving run for orchestrator retry`,
|
|
1470
|
-
error
|
|
1471
|
-
)
|
|
1472
|
-
throw error
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
await this.updateRunStatus(runId, 'failed', undefined, {
|
|
1476
|
-
message: error.message,
|
|
1477
|
-
stack: error.stack,
|
|
1478
|
-
code: error.code,
|
|
1479
|
-
})
|
|
1480
|
-
|
|
1481
|
-
throw error
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
private verifyQueueService(): QueueService {
|
|
1486
|
-
if (!getSingletonServices()?.queueService) {
|
|
1487
|
-
throw new Error(
|
|
1488
|
-
'QueueService not configured. Remote workflows require a queue service.'
|
|
1489
|
-
)
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
return getSingletonServices()!.queueService!
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
private async invokeStepRpc(
|
|
1496
|
-
runId: string,
|
|
1497
|
-
stepName: string,
|
|
1498
|
-
stepState: StepState,
|
|
1499
|
-
rpcName: string,
|
|
1500
|
-
data: any,
|
|
1501
|
-
rpcService: PikkuRPC,
|
|
1502
|
-
knownRun?: WorkflowRun | null
|
|
1503
|
-
): Promise<any> {
|
|
1504
|
-
const run = knownRun ?? (await this.getRunIdentity(runId))
|
|
1505
|
-
return rpcService.rpcWithWire(rpcName, data, {
|
|
1506
|
-
...(run?.wire?.pikkuUserId ? { pikkuUserId: run.wire.pikkuUserId } : {}),
|
|
1507
|
-
workflowStep: {
|
|
1508
|
-
runId,
|
|
1509
|
-
stepId: stepState.stepId,
|
|
1510
|
-
invocationId: deriveInvocationId(runId, stepName),
|
|
1511
|
-
attemptCount: stepState.attemptCount,
|
|
1512
|
-
fromInvocationId: stepState.fromStepName
|
|
1513
|
-
? deriveInvocationId(runId, stepState.fromStepName)
|
|
1514
|
-
: undefined,
|
|
1515
|
-
},
|
|
1516
|
-
})
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
private async runInlineRetryLoop(
|
|
1520
|
-
stepState: StepState,
|
|
1521
|
-
retries: number,
|
|
1522
|
-
retryDelay: WorkflowStepOptions['retryDelay'],
|
|
1523
|
-
doWork: (currentStepState: StepState) => Promise<any>,
|
|
1524
|
-
onError?: (error: any) => Promise<void>
|
|
1525
|
-
): Promise<any> {
|
|
1526
|
-
let currentStepState = stepState
|
|
1527
|
-
while (true) {
|
|
1528
|
-
try {
|
|
1529
|
-
await this.setStepRunning(currentStepState.stepId)
|
|
1530
|
-
const result = await doWork(currentStepState)
|
|
1531
|
-
await this.setStepResult(currentStepState.stepId, result)
|
|
1532
|
-
return result
|
|
1533
|
-
} catch (error: any) {
|
|
1534
|
-
if (onError) await onError(error)
|
|
1535
|
-
|
|
1536
|
-
await this.setStepError(currentStepState.stepId, error)
|
|
1537
|
-
|
|
1538
|
-
if (currentStepState.attemptCount < retries) {
|
|
1539
|
-
currentStepState = await this.createRetryAttempt(
|
|
1540
|
-
currentStepState.stepId,
|
|
1541
|
-
'pending'
|
|
1542
|
-
)
|
|
1543
|
-
if (retryDelay) {
|
|
1544
|
-
await new Promise((resolve) =>
|
|
1545
|
-
setTimeout(resolve, getDurationInMilliseconds(retryDelay))
|
|
1546
|
-
)
|
|
1547
|
-
}
|
|
1548
|
-
} else {
|
|
1549
|
-
throw error
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
private async runStepCompensation(
|
|
1556
|
-
runId: string,
|
|
1557
|
-
stepName: string,
|
|
1558
|
-
onErrorRpcName: string,
|
|
1559
|
-
rpcService: PikkuRPC,
|
|
1560
|
-
error: Error
|
|
1561
|
-
): Promise<void> {
|
|
1562
|
-
await this.rpcStep(
|
|
1563
|
-
runId,
|
|
1564
|
-
`${stepName}:onError`,
|
|
1565
|
-
onErrorRpcName,
|
|
1566
|
-
{ error: { message: error.message } },
|
|
1567
|
-
rpcService,
|
|
1568
|
-
{ retries: 0 }
|
|
1569
|
-
)
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
private async rpcStep(
|
|
1573
|
-
runId: string,
|
|
1574
|
-
logicalStepName: string,
|
|
1575
|
-
rpcName: string,
|
|
1576
|
-
data: any,
|
|
1577
|
-
rpcService: PikkuRPC,
|
|
1578
|
-
stepOptions?: WorkflowStepOptions
|
|
1579
|
-
): Promise<any> {
|
|
1580
|
-
const fromStepName = this.lastStepName(runId)
|
|
1581
|
-
const stepName = this.nextStepKey(runId, logicalStepName)
|
|
1582
|
-
const resolvedStepOptions: WorkflowStepOptions = {
|
|
1583
|
-
retries: stepOptions?.retries ?? DEFAULT_STEP_RETRIES,
|
|
1584
|
-
retryDelay: stepOptions?.retryDelay,
|
|
1585
|
-
actor: stepOptions?.actor,
|
|
1586
|
-
onError: stepOptions?.onError,
|
|
1587
|
-
}
|
|
1588
|
-
const stepState = await this.loadOrCreateStep(runId, stepName, () =>
|
|
1589
|
-
this.insertStepState(
|
|
1590
|
-
runId,
|
|
1591
|
-
stepName,
|
|
1592
|
-
rpcName,
|
|
1593
|
-
data,
|
|
1594
|
-
resolvedStepOptions,
|
|
1595
|
-
fromStepName
|
|
1596
|
-
)
|
|
1597
|
-
)
|
|
1598
|
-
|
|
1599
|
-
if (stepState.status === 'succeeded') {
|
|
1600
|
-
return stepState.result
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
if (stepState.status === 'failed') {
|
|
1604
|
-
const error = new Error(
|
|
1605
|
-
stepState.error?.message ||
|
|
1606
|
-
`Step '${stepName}' failed after exhausting all retries`
|
|
1607
|
-
)
|
|
1608
|
-
if (resolvedStepOptions.onError) {
|
|
1609
|
-
await this.runStepCompensation(
|
|
1610
|
-
runId,
|
|
1611
|
-
stepName,
|
|
1612
|
-
resolvedStepOptions.onError,
|
|
1613
|
-
rpcService,
|
|
1614
|
-
error
|
|
1615
|
-
)
|
|
1616
|
-
}
|
|
1617
|
-
if (stepState.error) {
|
|
1618
|
-
Object.assign(error, stepState.error)
|
|
1619
|
-
}
|
|
1620
|
-
throw error
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
if (stepState.status === 'scheduled') {
|
|
1624
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
const dispatched = resolvedStepOptions.actor
|
|
1628
|
-
? false
|
|
1629
|
-
: await this.dispatchStep(
|
|
1630
|
-
runId,
|
|
1631
|
-
stepName,
|
|
1632
|
-
rpcName,
|
|
1633
|
-
data,
|
|
1634
|
-
resolvedStepOptions,
|
|
1635
|
-
fromStepName
|
|
1636
|
-
)
|
|
1637
|
-
if (dispatched) {
|
|
1638
|
-
await this.setStepScheduled(stepState.stepId)
|
|
1639
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
const retries = resolvedStepOptions.retries ?? this.getConfig().retries
|
|
1643
|
-
const retryDelay = resolvedStepOptions.retryDelay
|
|
1644
|
-
|
|
1645
|
-
return this.runInlineRetryLoop(
|
|
1646
|
-
stepState,
|
|
1647
|
-
retries,
|
|
1648
|
-
retryDelay,
|
|
1649
|
-
async (currentStepState) => {
|
|
1650
|
-
if (resolvedStepOptions.actor) {
|
|
1651
|
-
return resolvedStepOptions.actor.invoke(rpcName, data)
|
|
1652
|
-
}
|
|
1653
|
-
const workflowMeta = pikkuState(null, 'workflows', 'meta')[rpcName]
|
|
1654
|
-
if (workflowMeta) {
|
|
1655
|
-
const childWire = {
|
|
1656
|
-
type: 'workflow',
|
|
1657
|
-
id: rpcName,
|
|
1658
|
-
parentRunId: runId,
|
|
1659
|
-
pikkuUserId: (await this.getRunIdentity(runId))?.wire?.pikkuUserId,
|
|
1660
|
-
}
|
|
1661
|
-
const { runId: childRunId } = await this.startWorkflow(
|
|
1662
|
-
rpcName,
|
|
1663
|
-
data,
|
|
1664
|
-
childWire,
|
|
1665
|
-
rpcService,
|
|
1666
|
-
{ inline: true }
|
|
1667
|
-
)
|
|
1668
|
-
await this.setStepChildRunId(currentStepState.stepId, childRunId)
|
|
1669
|
-
const childRun = await this.awaitRunEnd(
|
|
1670
|
-
childRunId,
|
|
1671
|
-
WORKFLOW_CHILD_POLL_MAX_MS
|
|
1672
|
-
)
|
|
1673
|
-
if (childRun.status === 'failed') {
|
|
1674
|
-
throw new Error(childRun.error?.message || 'Sub-workflow failed')
|
|
1675
|
-
}
|
|
1676
|
-
if (childRun.status === 'cancelled') {
|
|
1677
|
-
throw new Error('Sub-workflow was cancelled')
|
|
1678
|
-
}
|
|
1679
|
-
return childRun.output
|
|
1680
|
-
}
|
|
1681
|
-
return this.invokeStepRpc(
|
|
1682
|
-
runId,
|
|
1683
|
-
stepName,
|
|
1684
|
-
currentStepState,
|
|
1685
|
-
rpcName,
|
|
1686
|
-
data,
|
|
1687
|
-
rpcService
|
|
1688
|
-
)
|
|
1689
|
-
},
|
|
1690
|
-
async (error) => {
|
|
1691
|
-
if (error instanceof RPCNotFoundError) {
|
|
1692
|
-
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
1693
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
1694
|
-
code: 'RPC_NOT_FOUND',
|
|
1695
|
-
})
|
|
1696
|
-
throw error
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
)
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
protected async inlineStep(
|
|
1703
|
-
runId: string,
|
|
1704
|
-
logicalStepName: string,
|
|
1705
|
-
fn: Function,
|
|
1706
|
-
stepOptions?: WorkflowStepOptions,
|
|
1707
|
-
data: any = null,
|
|
1708
|
-
rpcName: string | null = null
|
|
1709
|
-
): Promise<any> {
|
|
1710
|
-
const fromStepName = this.lastStepName(runId)
|
|
1711
|
-
const stepName = this.nextStepKey(runId, logicalStepName)
|
|
1712
|
-
const stepState = await this.loadOrCreateStep(runId, stepName, () =>
|
|
1713
|
-
this.insertStepState(
|
|
1714
|
-
runId,
|
|
1715
|
-
stepName,
|
|
1716
|
-
rpcName,
|
|
1717
|
-
data,
|
|
1718
|
-
stepOptions,
|
|
1719
|
-
fromStepName
|
|
1720
|
-
)
|
|
1721
|
-
)
|
|
1722
|
-
|
|
1723
|
-
if (stepState.status === 'succeeded') {
|
|
1724
|
-
return stepState.result
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
const retries = stepOptions?.retries ?? this.getConfig().retries
|
|
1728
|
-
const retryDelay = stepOptions?.retryDelay ?? this.getConfig().retryDelay
|
|
1729
|
-
|
|
1730
|
-
if (await this.isInline(runId)) {
|
|
1731
|
-
return this.runInlineRetryLoop(stepState, retries, retryDelay, () => fn())
|
|
1732
|
-
} else {
|
|
1733
|
-
let currentStepState = stepState
|
|
1734
|
-
try {
|
|
1735
|
-
await this.setStepRunning(currentStepState.stepId)
|
|
1736
|
-
const result = await fn()
|
|
1737
|
-
await this.setStepResult(currentStepState.stepId, result)
|
|
1738
|
-
return result
|
|
1739
|
-
} catch (error: any) {
|
|
1740
|
-
await this.setStepError(currentStepState.stepId, error)
|
|
1741
|
-
|
|
1742
|
-
if (currentStepState.attemptCount < retries) {
|
|
1743
|
-
await this.createRetryAttempt(currentStepState.stepId, 'pending')
|
|
1744
|
-
|
|
1745
|
-
await this.scheduleOrchestratorRetry(runId, retryDelay)
|
|
1746
|
-
|
|
1747
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
throw error
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
private async sleepStep(
|
|
1756
|
-
runId: string,
|
|
1757
|
-
logicalStepName: string,
|
|
1758
|
-
duration: number
|
|
1759
|
-
) {
|
|
1760
|
-
const fromStepName = this.lastStepName(runId)
|
|
1761
|
-
const stepName = this.nextStepKey(runId, logicalStepName)
|
|
1762
|
-
const stepState = await this.loadOrCreateStep(runId, stepName, () =>
|
|
1763
|
-
this.insertStepState(
|
|
1764
|
-
runId,
|
|
1765
|
-
stepName,
|
|
1766
|
-
null,
|
|
1767
|
-
{ duration },
|
|
1768
|
-
undefined,
|
|
1769
|
-
fromStepName
|
|
1770
|
-
)
|
|
1771
|
-
)
|
|
1772
|
-
|
|
1773
|
-
if (stepState.status === 'succeeded') {
|
|
1774
|
-
return
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
if (stepState.status === 'scheduled') {
|
|
1778
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
let scheduled: boolean
|
|
1782
|
-
try {
|
|
1783
|
-
scheduled = await this.scheduleSleep(runId, stepState.stepId, duration)
|
|
1784
|
-
} catch (cause) {
|
|
1785
|
-
throw new WorkflowDispatchException(runId, stepName, { cause })
|
|
1786
|
-
}
|
|
1787
|
-
if (scheduled) {
|
|
1788
|
-
await this.setStepScheduled(stepState.stepId)
|
|
1789
|
-
throw new WorkflowAsyncException(runId, stepName)
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
await new Promise((resolve) =>
|
|
1793
|
-
setTimeout(resolve, getDurationInMilliseconds(duration))
|
|
1794
|
-
)
|
|
1795
|
-
await this.setStepResult(stepState.stepId, null)
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
private async suspendStep(runId: string, reason: string): Promise<void> {
|
|
1799
|
-
const fromStepName = this.lastStepName(runId)
|
|
1800
|
-
const suspendStepName = this.nextStepKey(runId, suspendStepNameFor(reason))
|
|
1801
|
-
await this.withStepLock(runId, suspendStepName, () =>
|
|
1802
|
-
recordSuspension(
|
|
1803
|
-
this.approvalStore,
|
|
1804
|
-
runId,
|
|
1805
|
-
reason,
|
|
1806
|
-
suspendStepName,
|
|
1807
|
-
fromStepName
|
|
1808
|
-
)
|
|
1809
|
-
)
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
private async scheduleRunWake(runId: string, delay: number): Promise<void> {
|
|
1813
|
-
try {
|
|
1814
|
-
const queueService = this.verifyQueueService()
|
|
1815
|
-
const run = await this.getRun(runId)
|
|
1816
|
-
if (!run?.workflow) return
|
|
1817
|
-
await queueService.add(
|
|
1818
|
-
this.getOrchestratorQueueName(run.workflow),
|
|
1819
|
-
{ runId },
|
|
1820
|
-
{
|
|
1821
|
-
...this.resolveStepJobOptions(),
|
|
1822
|
-
delay,
|
|
1823
|
-
group: this.getJobGroup(run.workflow),
|
|
1824
|
-
}
|
|
1825
|
-
)
|
|
1826
|
-
} catch (error) {
|
|
1827
|
-
this.logger?.warn(
|
|
1828
|
-
`Failed to schedule approval expiry wake for run ${runId}; expiry will still resolve on the next replay`,
|
|
1829
|
-
error
|
|
1830
|
-
)
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
private get approvalStore(): ApprovalStore {
|
|
1835
|
-
return {
|
|
1836
|
-
getStepState: (runId, stepName) => this.getStepState(runId, stepName),
|
|
1837
|
-
insertStepState: (
|
|
1838
|
-
runId,
|
|
1839
|
-
stepName,
|
|
1840
|
-
rpcName,
|
|
1841
|
-
input,
|
|
1842
|
-
output,
|
|
1843
|
-
fromStepName
|
|
1844
|
-
) =>
|
|
1845
|
-
this.insertStepState(
|
|
1846
|
-
runId,
|
|
1847
|
-
stepName,
|
|
1848
|
-
rpcName,
|
|
1849
|
-
input,
|
|
1850
|
-
output,
|
|
1851
|
-
fromStepName
|
|
1852
|
-
),
|
|
1853
|
-
setStepRunning: (stepId) => this.setStepRunning(stepId),
|
|
1854
|
-
setStepResult: (stepId, result) => this.setStepResult(stepId, result),
|
|
1855
|
-
getRunState: (runId) => this.getRunState(runId),
|
|
1856
|
-
updateRunState: (runId, key, value) =>
|
|
1857
|
-
this.updateRunState(runId, key, value),
|
|
1858
|
-
resumeWorkflow: (runId) => this.resumeWorkflow(runId),
|
|
1859
|
-
scheduleRunWake: (runId, delay) => this.scheduleRunWake(runId, delay),
|
|
1860
|
-
getRunOwner: async (runId) =>
|
|
1861
|
-
(await this.getRunIdentity(runId))?.wire?.pikkuUserId,
|
|
1862
|
-
auditApproval: (event) => auditApprovalDecision(event),
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
public async approveStep(
|
|
1867
|
-
runId: string,
|
|
1868
|
-
reason: string,
|
|
1869
|
-
decision: unknown,
|
|
1870
|
-
session?: CoreUserSession
|
|
1871
|
-
): Promise<void> {
|
|
1872
|
-
return recordApprovalDecision(
|
|
1873
|
-
this.approvalStore,
|
|
1874
|
-
runId,
|
|
1875
|
-
reason,
|
|
1876
|
-
decision,
|
|
1877
|
-
approvalDeciderFrom(session)
|
|
1878
|
-
)
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
private async approvalStep(
|
|
1882
|
-
runId: string,
|
|
1883
|
-
reason: string,
|
|
1884
|
-
options: WorkflowApprovalOptions
|
|
1885
|
-
): Promise<ApprovalOutcome<unknown>> {
|
|
1886
|
-
const fromStepName = this.lastStepName(runId)
|
|
1887
|
-
const approvalStepName = this.nextStepKey(
|
|
1888
|
-
runId,
|
|
1889
|
-
approvalStepNameFor(reason)
|
|
1890
|
-
)
|
|
1891
|
-
return await this.withStepLock(runId, approvalStepName, () =>
|
|
1892
|
-
evaluateApprovalStep(
|
|
1893
|
-
this.approvalStore,
|
|
1894
|
-
runId,
|
|
1895
|
-
reason,
|
|
1896
|
-
approvalStepName,
|
|
1897
|
-
fromStepName,
|
|
1898
|
-
options
|
|
1899
|
-
)
|
|
1900
|
-
)
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
public createWorkflowWire(
|
|
1904
|
-
name: string,
|
|
1905
|
-
runId: string,
|
|
1906
|
-
rpcService: PikkuRPC,
|
|
1907
|
-
addonNamespace?: string | null
|
|
1908
|
-
): PikkuWorkflowWire {
|
|
1909
|
-
const workflowWire: PikkuWorkflowWire = {
|
|
1910
|
-
name,
|
|
1911
|
-
runId,
|
|
1912
|
-
getRun: async () => (await this.getRun(runId)) as WorkflowRun,
|
|
1913
|
-
|
|
1914
|
-
do: async (
|
|
1915
|
-
stepName: string,
|
|
1916
|
-
rpcNameOrFn: any,
|
|
1917
|
-
dataOrOptions?: any,
|
|
1918
|
-
options?: any
|
|
1919
|
-
) => {
|
|
1920
|
-
this.verifyStepName(stepName)
|
|
1921
|
-
if (typeof rpcNameOrFn === 'string') {
|
|
1922
|
-
const resolvedRpcName =
|
|
1923
|
-
addonNamespace && !rpcNameOrFn.includes(':')
|
|
1924
|
-
? `${addonNamespace}:${rpcNameOrFn}`
|
|
1925
|
-
: rpcNameOrFn
|
|
1926
|
-
return await this.rpcStep(
|
|
1927
|
-
runId,
|
|
1928
|
-
stepName,
|
|
1929
|
-
resolvedRpcName,
|
|
1930
|
-
dataOrOptions,
|
|
1931
|
-
rpcService,
|
|
1932
|
-
options
|
|
1933
|
-
)
|
|
1934
|
-
} else {
|
|
1935
|
-
return await this.inlineStep(
|
|
1936
|
-
runId,
|
|
1937
|
-
stepName,
|
|
1938
|
-
rpcNameOrFn,
|
|
1939
|
-
dataOrOptions
|
|
1940
|
-
)
|
|
1941
|
-
}
|
|
1942
|
-
},
|
|
1943
|
-
|
|
1944
|
-
sleep: async (stepName: string, duration: string | number) => {
|
|
1945
|
-
this.verifyStepName(stepName)
|
|
1946
|
-
await this.sleepStep(
|
|
1947
|
-
runId,
|
|
1948
|
-
stepName,
|
|
1949
|
-
getDurationInMilliseconds(duration)
|
|
1950
|
-
)
|
|
1951
|
-
},
|
|
1952
|
-
|
|
1953
|
-
suspend: async (reason: string) => {
|
|
1954
|
-
this.verifyStepName(reason)
|
|
1955
|
-
await this.suspendStep(runId, reason)
|
|
1956
|
-
},
|
|
1957
|
-
|
|
1958
|
-
approval: (async (reason: string, options: WorkflowApprovalOptions) => {
|
|
1959
|
-
this.verifyStepName(reason)
|
|
1960
|
-
return await this.approvalStep(runId, reason, options)
|
|
1961
|
-
}) as PikkuWorkflowWire['approval'],
|
|
1962
|
-
}
|
|
1963
|
-
this.runExtension?.decorateWorkflowWire(workflowWire, {
|
|
1964
|
-
name,
|
|
1965
|
-
runId,
|
|
1966
|
-
rpcService,
|
|
1967
|
-
addonNamespace,
|
|
1968
|
-
})
|
|
1969
|
-
return workflowWire
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
protected verifyStepName(stepName: string) {
|
|
1973
|
-
if (typeof stepName !== 'string') {
|
|
1974
|
-
throw new WorkflowStepNameNotString(stepName)
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
private getConfig(): WorkflowServiceConfig {
|
|
1979
|
-
return resolveWorkflowConfig()
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
protected getOrchestratorQueueName(workflowName?: string): string {
|
|
1983
|
-
return orchestratorQueueName(this.queueStrategy, workflowName)
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
protected getStepWorkerQueueName(rpcName?: string): string {
|
|
1987
|
-
return stepWorkerQueueName(this.queueStrategy, rpcName)
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
protected getJobGroup(id?: string): JobGroup | undefined {
|
|
1991
|
-
return jobGroupFor(this.queueStrategy, id)
|
|
1992
|
-
}
|
|
1993
|
-
}
|