@pikku/core 0.11.0 → 0.11.1
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 +11 -1
- package/dist/errors/errors.d.ts +1 -1
- package/dist/errors/errors.js +3 -3
- package/dist/function/function-runner.d.ts +6 -7
- package/dist/function/function-runner.js +35 -16
- package/dist/function/functions.types.d.ts +22 -30
- package/dist/function/functions.types.js +9 -6
- package/dist/handle-error.d.ts +1 -1
- package/dist/handle-error.js +1 -1
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.js +3 -3
- package/dist/middleware/auth-cookie.js +5 -5
- package/dist/middleware/timeout.js +3 -2
- package/dist/middleware-runner.d.ts +3 -6
- package/dist/middleware-runner.js +11 -12
- package/dist/permissions.d.ts +6 -6
- package/dist/permissions.js +15 -16
- package/dist/services/user-session-service.d.ts +7 -3
- package/dist/services/user-session-service.js +8 -1
- package/dist/types/core.types.d.ts +29 -38
- package/dist/types/core.types.js +3 -14
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/dist/wirings/channel/channel-common.js +2 -3
- package/dist/wirings/channel/channel-handler.d.ts +3 -2
- package/dist/wirings/channel/channel-handler.js +6 -9
- package/dist/wirings/channel/channel-runner.d.ts +2 -2
- package/dist/wirings/channel/channel.types.d.ts +10 -5
- package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-runner.js +19 -26
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +41 -46
- package/dist/wirings/cli/cli-runner.d.ts +5 -5
- package/dist/wirings/cli/cli-runner.js +10 -28
- package/dist/wirings/cli/cli.types.d.ts +15 -11
- package/dist/wirings/http/http-runner.d.ts +6 -6
- package/dist/wirings/http/http-runner.js +25 -39
- package/dist/wirings/http/http.types.d.ts +5 -10
- package/dist/wirings/mcp/mcp-runner.d.ts +2 -2
- package/dist/wirings/mcp/mcp-runner.js +12 -19
- package/dist/wirings/mcp/mcp.types.d.ts +6 -0
- package/dist/wirings/queue/queue-runner.d.ts +3 -3
- package/dist/wirings/queue/queue-runner.js +7 -17
- package/dist/wirings/queue/queue.types.d.ts +4 -11
- package/dist/wirings/rpc/rpc-runner.d.ts +10 -8
- package/dist/wirings/rpc/rpc-runner.js +36 -47
- package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -3
- package/dist/wirings/scheduler/scheduler-runner.js +13 -24
- package/dist/wirings/scheduler/scheduler.types.d.ts +3 -8
- package/dist/wirings/workflow/index.d.ts +11 -2
- package/dist/wirings/workflow/index.js +10 -2
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +12 -4
- package/dist/wirings/workflow/pikku-workflow-service.js +61 -79
- package/dist/wirings/workflow/workflow-runner.d.ts +3 -4
- package/dist/wirings/workflow/workflow-runner.js +11 -14
- package/dist/wirings/workflow/workflow.types.d.ts +114 -29
- package/package.json +2 -2
- package/src/errors/errors.ts +3 -3
- package/src/factory-functions.test.ts +9 -36
- package/src/function/function-runner.test.ts +57 -68
- package/src/function/function-runner.ts +51 -28
- package/src/function/function-runner.ts.bak +188 -0
- package/src/function/functions.types.ts +68 -51
- package/src/handle-error.ts +1 -1
- package/src/middleware/auth-apikey.test.ts +360 -0
- package/src/middleware/auth-apikey.ts +3 -7
- package/src/middleware/auth-bearer.test.ts +447 -0
- package/src/middleware/auth-bearer.ts +27 -33
- package/src/middleware/auth-cookie.test.ts +525 -0
- package/src/middleware/auth-cookie.ts +6 -6
- package/src/middleware/timeout.ts +4 -2
- package/src/middleware-runner.test.ts +58 -127
- package/src/middleware-runner.ts +13 -16
- package/src/permissions.test.ts +41 -45
- package/src/permissions.ts +21 -21
- package/src/services/user-session-service.ts +14 -4
- package/src/types/core.types.ts +67 -55
- package/src/utils.ts +4 -4
- package/src/wirings/channel/channel-common.ts +4 -7
- package/src/wirings/channel/channel-handler.ts +16 -22
- package/src/wirings/channel/channel-runner.ts +2 -2
- package/src/wirings/channel/channel.types.ts +14 -11
- package/src/wirings/channel/local/local-channel-runner.test.ts +5 -3
- package/src/wirings/channel/local/local-channel-runner.ts +23 -38
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +51 -77
- package/src/wirings/cli/cli-runner.test.ts +7 -7
- package/src/wirings/cli/cli-runner.ts +24 -62
- package/src/wirings/cli/cli.types.ts +22 -16
- package/src/wirings/http/http-runner.test.ts +8 -8
- package/src/wirings/http/http-runner.ts +29 -56
- package/src/wirings/http/http.types.ts +5 -14
- package/src/wirings/mcp/mcp-runner.ts +23 -48
- package/src/wirings/mcp/mcp.types.ts +6 -0
- package/src/wirings/queue/queue-runner.test.ts +641 -0
- package/src/wirings/queue/queue-runner.ts +10 -32
- package/src/wirings/queue/queue.types.ts +4 -11
- package/src/wirings/rpc/rpc-runner.ts +41 -55
- package/src/wirings/scheduler/scheduler-runner.test.ts +627 -0
- package/src/wirings/scheduler/scheduler-runner.ts +24 -50
- package/src/wirings/scheduler/scheduler.types.ts +3 -9
- package/src/wirings/workflow/index.ts +24 -3
- package/src/wirings/workflow/pikku-workflow-service.ts +83 -93
- package/src/wirings/workflow/workflow-runner.ts +12 -25
- package/src/wirings/workflow/workflow.types.ts +144 -54
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
PikkuWiringTypes,
|
|
2
|
+
PikkuWire,
|
|
4
3
|
type CoreServices,
|
|
5
4
|
type CoreSingletonServices,
|
|
6
5
|
type CoreUserSession,
|
|
7
|
-
type
|
|
6
|
+
type CreateWireServices,
|
|
8
7
|
} from '../../types/core.types.js'
|
|
9
8
|
import type { CoreScheduledTask } from './scheduler.types.js'
|
|
10
9
|
import { getErrorResponse, PikkuError } from '../../errors/error-handler.js'
|
|
11
|
-
import {
|
|
10
|
+
import { closeWireServices } from '../../utils.js'
|
|
12
11
|
import { pikkuState } from '../../pikku-state.js'
|
|
13
12
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
14
|
-
import {
|
|
15
|
-
import { PikkuUserSessionService } from '../../services/user-session-service.js'
|
|
13
|
+
import { PikkuSessionService } from '../../services/user-session-service.js'
|
|
16
14
|
import {
|
|
17
15
|
CorePikkuFunctionConfig,
|
|
18
16
|
CorePikkuFunctionSessionless,
|
|
@@ -22,7 +20,7 @@ export type RunScheduledTasksParams = {
|
|
|
22
20
|
name: string
|
|
23
21
|
session?: CoreUserSession
|
|
24
22
|
singletonServices: CoreSingletonServices
|
|
25
|
-
|
|
23
|
+
createWireServices?: CreateWireServices<
|
|
26
24
|
CoreSingletonServices,
|
|
27
25
|
CoreServices<CoreSingletonServices>,
|
|
28
26
|
CoreUserSession
|
|
@@ -45,9 +43,8 @@ export const wireScheduler = <
|
|
|
45
43
|
func: scheduledTask.func.func,
|
|
46
44
|
auth: scheduledTask.func.auth,
|
|
47
45
|
permissions: scheduledTask.func.permissions,
|
|
48
|
-
middleware: scheduledTask.func.middleware
|
|
46
|
+
middleware: scheduledTask.func.middleware,
|
|
49
47
|
tags: scheduledTask.func.tags,
|
|
50
|
-
docs: scheduledTask.func.docs as any,
|
|
51
48
|
})
|
|
52
49
|
|
|
53
50
|
const tasks = pikkuState('scheduler', 'tasks')
|
|
@@ -76,13 +73,13 @@ export async function runScheduledTask({
|
|
|
76
73
|
name,
|
|
77
74
|
session,
|
|
78
75
|
singletonServices,
|
|
79
|
-
|
|
76
|
+
createWireServices,
|
|
80
77
|
}: RunScheduledTasksParams): Promise<void> {
|
|
81
|
-
let
|
|
78
|
+
let wireServices: any
|
|
82
79
|
const task = pikkuState('scheduler', 'tasks').get(name)
|
|
83
80
|
const meta = pikkuState('scheduler', 'meta')[name]
|
|
84
81
|
|
|
85
|
-
const userSession = new
|
|
82
|
+
const userSession = new PikkuSessionService()
|
|
86
83
|
if (session) {
|
|
87
84
|
userSession.set(session)
|
|
88
85
|
}
|
|
@@ -96,8 +93,8 @@ export async function runScheduledTask({
|
|
|
96
93
|
)
|
|
97
94
|
}
|
|
98
95
|
|
|
99
|
-
// Create the scheduled task
|
|
100
|
-
const
|
|
96
|
+
// Create the scheduled task wire object
|
|
97
|
+
const wire: PikkuWire = {
|
|
101
98
|
scheduledTask: {
|
|
102
99
|
name,
|
|
103
100
|
schedule: task.schedule,
|
|
@@ -106,6 +103,7 @@ export async function runScheduledTask({
|
|
|
106
103
|
throw new ScheduledTaskSkippedError(name, reason)
|
|
107
104
|
},
|
|
108
105
|
},
|
|
106
|
+
session: userSession,
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
try {
|
|
@@ -113,40 +111,16 @@ export async function runScheduledTask({
|
|
|
113
111
|
`Running schedule task: ${name} | schedule: ${task.schedule}`
|
|
114
112
|
)
|
|
115
113
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
...sessionServices,
|
|
127
|
-
},
|
|
128
|
-
interaction
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const result = await runPikkuFunc(
|
|
133
|
-
PikkuWiringTypes.scheduler,
|
|
134
|
-
meta.name,
|
|
135
|
-
meta.pikkuFuncName,
|
|
136
|
-
{
|
|
137
|
-
singletonServices,
|
|
138
|
-
getAllServices,
|
|
139
|
-
userSession,
|
|
140
|
-
auth: false,
|
|
141
|
-
data: () => undefined,
|
|
142
|
-
inheritedMiddleware: meta.middleware,
|
|
143
|
-
wireMiddleware: task.middleware,
|
|
144
|
-
tags: task.tags,
|
|
145
|
-
interaction,
|
|
146
|
-
}
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
return result
|
|
114
|
+
await runPikkuFunc('scheduler', meta.name, meta.pikkuFuncName, {
|
|
115
|
+
singletonServices,
|
|
116
|
+
createWireServices,
|
|
117
|
+
auth: false,
|
|
118
|
+
data: () => undefined,
|
|
119
|
+
inheritedMiddleware: meta.middleware,
|
|
120
|
+
wireMiddleware: task.middleware,
|
|
121
|
+
tags: task.tags,
|
|
122
|
+
wire,
|
|
123
|
+
})
|
|
150
124
|
} catch (e: any) {
|
|
151
125
|
const errorResponse = getErrorResponse(e)
|
|
152
126
|
if (errorResponse != null) {
|
|
@@ -154,8 +128,8 @@ export async function runScheduledTask({
|
|
|
154
128
|
}
|
|
155
129
|
throw e
|
|
156
130
|
} finally {
|
|
157
|
-
if (
|
|
158
|
-
await
|
|
131
|
+
if (wireServices) {
|
|
132
|
+
await closeWireServices(singletonServices.logger, wireServices)
|
|
159
133
|
}
|
|
160
134
|
}
|
|
161
135
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PikkuDocs,
|
|
3
2
|
CoreUserSession,
|
|
4
3
|
CorePikkuMiddleware,
|
|
5
|
-
|
|
4
|
+
CommonWireMeta,
|
|
6
5
|
} from '../../types/core.types.js'
|
|
7
6
|
import {
|
|
8
7
|
CorePikkuFunctionConfig,
|
|
@@ -15,14 +14,10 @@ import {
|
|
|
15
14
|
export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> =
|
|
16
15
|
Record<
|
|
17
16
|
string,
|
|
18
|
-
{
|
|
19
|
-
pikkuFuncName: string
|
|
17
|
+
CommonWireMeta & {
|
|
20
18
|
name: string
|
|
21
19
|
schedule: string
|
|
22
20
|
session?: UserSession
|
|
23
|
-
docs?: PikkuDocs
|
|
24
|
-
tags?: string[]
|
|
25
|
-
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
26
21
|
}
|
|
27
22
|
>
|
|
28
23
|
|
|
@@ -38,13 +33,12 @@ export type CoreScheduledTask<
|
|
|
38
33
|
name: string
|
|
39
34
|
schedule: string
|
|
40
35
|
func: PikkuFunctionConfig
|
|
41
|
-
docs?: PikkuDocs
|
|
42
36
|
tags?: string[]
|
|
43
37
|
middleware?: PikkuMiddleware[]
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
/**
|
|
47
|
-
* Represents a scheduled task
|
|
41
|
+
* Represents a scheduled task wire object for middleware
|
|
48
42
|
* Provides information about the current scheduled task execution
|
|
49
43
|
*/
|
|
50
44
|
export interface PikkuScheduledTask {
|
|
@@ -7,7 +7,17 @@ export type {
|
|
|
7
7
|
CoreWorkflow,
|
|
8
8
|
WorkflowStepOptions,
|
|
9
9
|
WorkflowStepMeta,
|
|
10
|
-
|
|
10
|
+
RpcStepMeta,
|
|
11
|
+
BranchStepMeta,
|
|
12
|
+
ParallelGroupStepMeta,
|
|
13
|
+
FanoutStepMeta,
|
|
14
|
+
ReturnStepMeta,
|
|
15
|
+
InlineStepMeta,
|
|
16
|
+
SleepStepMeta,
|
|
17
|
+
CancelStepMeta,
|
|
18
|
+
InputSource,
|
|
19
|
+
OutputBinding,
|
|
20
|
+
PikkuWorkflowWire,
|
|
11
21
|
PikkuWorkflow,
|
|
12
22
|
WorkflowsMeta,
|
|
13
23
|
WorkflowRun,
|
|
@@ -18,5 +28,16 @@ export type {
|
|
|
18
28
|
|
|
19
29
|
export { PikkuWorkflowService } from './pikku-workflow-service.js'
|
|
20
30
|
|
|
21
|
-
//
|
|
22
|
-
export {
|
|
31
|
+
// Internal registration function (used by generated code)
|
|
32
|
+
export { addWorkflow } from './workflow-runner.js'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated This function is no longer used and will be removed in a future release.
|
|
36
|
+
* It exists only for backwards compatibility with generated code.
|
|
37
|
+
* TODO: Remove this export in a future release after updating code generation
|
|
38
|
+
*/
|
|
39
|
+
export const wireWorkflow = <T extends { name: string; func: any }>(
|
|
40
|
+
workflow: T
|
|
41
|
+
): void => {
|
|
42
|
+
// Empty function - no longer used, kept for backwards compatibility
|
|
43
|
+
}
|
|
@@ -3,10 +3,9 @@ import { pikkuState } from '../../pikku-state.js'
|
|
|
3
3
|
import { getDurationInMilliseconds } from '../../time-utils.js'
|
|
4
4
|
import {
|
|
5
5
|
CoreConfig,
|
|
6
|
-
CoreServices,
|
|
7
6
|
CoreSingletonServices,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
CreateWireServices,
|
|
8
|
+
PikkuWire,
|
|
10
9
|
SerializedError,
|
|
11
10
|
} from '../../types/core.types.js'
|
|
12
11
|
import { QueueService } from '../queue/queue.types.js'
|
|
@@ -17,7 +16,7 @@ import {
|
|
|
17
16
|
WorkflowRunNotFound,
|
|
18
17
|
} from './workflow-runner.js'
|
|
19
18
|
import type {
|
|
20
|
-
|
|
19
|
+
PikkuWorkflowWire,
|
|
21
20
|
StepState,
|
|
22
21
|
WorkflowRun,
|
|
23
22
|
WorkflowStatus,
|
|
@@ -41,17 +40,17 @@ export class WorkflowStepNameNotString extends Error {
|
|
|
41
40
|
export abstract class PikkuWorkflowService implements WorkflowService {
|
|
42
41
|
private config: WorkflowServiceConfig | undefined
|
|
43
42
|
private singletonServices: CoreSingletonServices | undefined
|
|
44
|
-
private
|
|
43
|
+
private createWireServices: CreateWireServices | undefined
|
|
45
44
|
|
|
46
45
|
constructor() {}
|
|
47
46
|
|
|
48
47
|
public setServices(
|
|
49
48
|
singletonServices: CoreSingletonServices,
|
|
50
|
-
|
|
49
|
+
createWireServices: CreateWireServices,
|
|
51
50
|
{ workflow }: CoreConfig
|
|
52
51
|
) {
|
|
53
52
|
this.singletonServices = singletonServices
|
|
54
|
-
this.
|
|
53
|
+
this.createWireServices = createWireServices
|
|
55
54
|
this.config = {
|
|
56
55
|
retries: workflow?.retries ?? 0,
|
|
57
56
|
retryDelay: workflow?.retryDelay ?? 0,
|
|
@@ -176,6 +175,19 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
176
175
|
*/
|
|
177
176
|
abstract withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>
|
|
178
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Execute function within a step lock to prevent concurrent step execution
|
|
180
|
+
* @param runId - Run ID
|
|
181
|
+
* @param stepName - Step name
|
|
182
|
+
* @param fn - Function to execute
|
|
183
|
+
* @returns Function result
|
|
184
|
+
*/
|
|
185
|
+
abstract withStepLock<T>(
|
|
186
|
+
runId: string,
|
|
187
|
+
stepName: string,
|
|
188
|
+
fn: () => Promise<T>
|
|
189
|
+
): Promise<T>
|
|
190
|
+
|
|
179
191
|
/**
|
|
180
192
|
* Close any open connections
|
|
181
193
|
*/
|
|
@@ -256,66 +268,42 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
256
268
|
}
|
|
257
269
|
|
|
258
270
|
public async runWorkflowJob(runId: string, rpcService: any): Promise<void> {
|
|
259
|
-
// Get the run
|
|
260
271
|
const run = await this.getRun(runId)
|
|
261
272
|
if (!run) {
|
|
262
273
|
throw new WorkflowRunNotFound(runId)
|
|
263
274
|
}
|
|
264
275
|
|
|
265
|
-
// Get workflow registration
|
|
266
276
|
const registrations = pikkuState('workflows', 'registrations')
|
|
267
277
|
const workflow = registrations.get(run.workflow)
|
|
268
278
|
if (!workflow) {
|
|
269
279
|
throw new WorkflowNotFoundError(run.workflow)
|
|
270
280
|
}
|
|
271
281
|
|
|
272
|
-
// Use lock to prevent concurrent execution
|
|
273
282
|
await this.withRunLock(runId, async () => {
|
|
274
|
-
|
|
275
|
-
const
|
|
283
|
+
const meta = pikkuState('workflows', 'meta')
|
|
284
|
+
const workflowMeta = meta[run.workflow]
|
|
285
|
+
|
|
286
|
+
const workflowWire = this.createWorkflowWire(
|
|
276
287
|
run.workflow,
|
|
277
288
|
runId,
|
|
278
289
|
rpcService
|
|
279
290
|
)
|
|
280
|
-
|
|
281
|
-
// Get function metadata
|
|
282
|
-
const meta = pikkuState('workflows', 'meta')
|
|
283
|
-
const workflowMeta = meta[run.workflow]
|
|
284
|
-
|
|
285
|
-
// Execute workflow function with workflow interaction
|
|
291
|
+
const wire: PikkuWire = { workflow: workflowWire }
|
|
286
292
|
try {
|
|
287
|
-
const getAllServices = () => {
|
|
288
|
-
let sessionServices = {}
|
|
289
|
-
const interaction = { workflow: workflowInteraction }
|
|
290
|
-
if (this.createSessionServices) {
|
|
291
|
-
sessionServices = this.createSessionServices(
|
|
292
|
-
this.singletonServices!,
|
|
293
|
-
interaction,
|
|
294
|
-
undefined
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
...this.singletonServices,
|
|
299
|
-
...sessionServices,
|
|
300
|
-
...interaction,
|
|
301
|
-
} as CoreServices
|
|
302
|
-
}
|
|
303
293
|
const result = await runPikkuFunc(
|
|
304
|
-
|
|
294
|
+
'workflow',
|
|
305
295
|
workflowMeta.workflowName,
|
|
306
296
|
workflowMeta.pikkuFuncName,
|
|
307
297
|
{
|
|
308
298
|
singletonServices: this.singletonServices!,
|
|
309
|
-
|
|
310
|
-
|
|
299
|
+
wire,
|
|
300
|
+
createWireServices: this.createWireServices,
|
|
311
301
|
data: () => run.input,
|
|
312
302
|
}
|
|
313
303
|
)
|
|
314
304
|
|
|
315
|
-
// Workflow completed successfully
|
|
316
305
|
await this.updateRunStatus(runId, 'completed', result)
|
|
317
306
|
} catch (error: any) {
|
|
318
|
-
// Check if it's a WorkflowAsyncException
|
|
319
307
|
if (error instanceof WorkflowAsyncException) {
|
|
320
308
|
// Normal - workflow paused for step execution
|
|
321
309
|
throw error
|
|
@@ -327,7 +315,6 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
327
315
|
throw error
|
|
328
316
|
}
|
|
329
317
|
|
|
330
|
-
// Real error - mark as failed
|
|
331
318
|
await this.updateRunStatus(runId, 'failed', undefined, {
|
|
332
319
|
message: error.message,
|
|
333
320
|
stack: error.stack,
|
|
@@ -350,64 +337,67 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
350
337
|
data: any,
|
|
351
338
|
rpcService: any
|
|
352
339
|
): Promise<void> {
|
|
353
|
-
//
|
|
354
|
-
|
|
340
|
+
// Use step-level lock to prevent concurrent execution of same step
|
|
341
|
+
await this.withStepLock(runId, stepName, async () => {
|
|
342
|
+
// Get step state
|
|
343
|
+
let stepState = await this.getStepState(runId, stepName)
|
|
355
344
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
345
|
+
// Idempotency - if already succeeded, resume orchestrator and return
|
|
346
|
+
if (stepState.status === 'succeeded') {
|
|
347
|
+
await this.resumeWorkflow(runId)
|
|
348
|
+
return
|
|
349
|
+
}
|
|
361
350
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
351
|
+
// Log warning if already running (race condition)
|
|
352
|
+
if (stepState.status === 'running') {
|
|
353
|
+
return
|
|
354
|
+
}
|
|
366
355
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
356
|
+
// If status is 'failed', this is a retry - create new attempt history
|
|
357
|
+
if (stepState.status === 'failed') {
|
|
358
|
+
stepState = await this.createRetryAttempt(stepState.stepId, 'running')
|
|
359
|
+
}
|
|
371
360
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
361
|
+
if (stepState.status !== 'pending') {
|
|
362
|
+
// Mark step as running (pending or failed status)
|
|
363
|
+
await this.setStepRunning(stepState.stepId)
|
|
364
|
+
}
|
|
376
365
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
366
|
+
try {
|
|
367
|
+
// Execute RPC with workflow step context
|
|
368
|
+
const result = await rpcService.rpcWithWire(rpcName, data, {
|
|
369
|
+
workflowStep: {
|
|
370
|
+
runId,
|
|
371
|
+
stepId: stepState.stepId,
|
|
372
|
+
attemptCount: stepState.attemptCount,
|
|
373
|
+
},
|
|
374
|
+
})
|
|
386
375
|
|
|
387
|
-
|
|
388
|
-
|
|
376
|
+
// Store result and mark succeeded
|
|
377
|
+
await this.setStepResult(stepState.stepId, result)
|
|
389
378
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
379
|
+
// Resume orchestrator to continue workflow
|
|
380
|
+
try {
|
|
381
|
+
await this.resumeWorkflow(runId)
|
|
382
|
+
} catch (resumeError: any) {
|
|
383
|
+
throw resumeError
|
|
384
|
+
}
|
|
385
|
+
} catch (error: any) {
|
|
386
|
+
// Store error and mark failed
|
|
387
|
+
await this.setStepError(stepState.stepId, error)
|
|
399
388
|
|
|
400
|
-
|
|
401
|
-
|
|
389
|
+
const maxAttempts = (stepState.retries ?? 0) + 1
|
|
390
|
+
const retriesExhausted = stepState.attemptCount >= maxAttempts
|
|
402
391
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
392
|
+
if (retriesExhausted) {
|
|
393
|
+
// No more retries - resume orchestrator to mark workflow as failed
|
|
394
|
+
await this.resumeWorkflow(runId)
|
|
395
|
+
}
|
|
407
396
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
397
|
+
// Always throw so queue knows the job failed and can retry if needed
|
|
398
|
+
throw error
|
|
399
|
+
}
|
|
400
|
+
})
|
|
411
401
|
}
|
|
412
402
|
|
|
413
403
|
/**
|
|
@@ -531,7 +521,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
531
521
|
const attemptCount = stepState.attemptCount
|
|
532
522
|
|
|
533
523
|
try {
|
|
534
|
-
const result = await rpcService.
|
|
524
|
+
const result = await rpcService.rpcWithWire(rpcName, data, {
|
|
535
525
|
workflowStep: {
|
|
536
526
|
runId,
|
|
537
527
|
stepId: stepState.stepId,
|
|
@@ -686,12 +676,12 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
686
676
|
throw new WorkflowCancelledException(runId, reason)
|
|
687
677
|
}
|
|
688
678
|
|
|
689
|
-
private
|
|
679
|
+
private createWorkflowWire(
|
|
690
680
|
workflowName: string,
|
|
691
681
|
runId: string,
|
|
692
682
|
rpcService: any
|
|
693
|
-
):
|
|
694
|
-
const
|
|
683
|
+
): PikkuWorkflowWire {
|
|
684
|
+
const workflowWire: PikkuWorkflowWire = {
|
|
695
685
|
workflowName,
|
|
696
686
|
runId,
|
|
697
687
|
getRun: async () => (await this.getRun(runId)) as WorkflowRun,
|
|
@@ -738,7 +728,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
738
728
|
await this.cancelWorkflow(runId, reason)
|
|
739
729
|
},
|
|
740
730
|
}
|
|
741
|
-
return
|
|
731
|
+
return workflowWire
|
|
742
732
|
}
|
|
743
733
|
|
|
744
734
|
private verifyStepName(stepName: string) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { CoreWorkflow } from './workflow.types.js'
|
|
2
|
-
import type { CorePikkuFunctionConfig } from '../../function/functions.types.js'
|
|
3
1
|
import { pikkuState } from '../../pikku-state.js'
|
|
4
2
|
import { PikkuError } from '../../errors/error-handler.js'
|
|
5
3
|
import { addFunction } from '../../function/function-runner.js'
|
|
@@ -49,37 +47,26 @@ export class WorkflowRunNotFound extends PikkuError {
|
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
/**
|
|
52
|
-
*
|
|
50
|
+
* Add a workflow to the system
|
|
51
|
+
* This is called by the generated workflow wirings
|
|
53
52
|
*/
|
|
54
|
-
export const
|
|
55
|
-
PikkuFunctionConfig extends CorePikkuFunctionConfig<
|
|
56
|
-
any,
|
|
57
|
-
any,
|
|
58
|
-
any
|
|
59
|
-
> = CorePikkuFunctionConfig<any, any, any>,
|
|
60
|
-
>(
|
|
61
|
-
workflow: CoreWorkflow<PikkuFunctionConfig>
|
|
62
|
-
) => {
|
|
53
|
+
export const addWorkflow = (workflowName: string, workflowFunc: any) => {
|
|
63
54
|
// Get workflow metadata from inspector
|
|
64
55
|
const meta = pikkuState('workflows', 'meta')
|
|
65
|
-
const workflowMeta = meta[
|
|
56
|
+
const workflowMeta = meta[workflowName]
|
|
66
57
|
if (!workflowMeta) {
|
|
67
58
|
throw new Error(
|
|
68
|
-
`Workflow metadata not found for '${
|
|
59
|
+
`Workflow metadata not found for '${workflowName}'. Make sure to run the CLI to generate metadata.`
|
|
69
60
|
)
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
// Register the function with pikku
|
|
73
|
-
addFunction(workflowMeta.pikkuFuncName, {
|
|
74
|
-
func: workflow.func.func,
|
|
75
|
-
auth: workflow.func.auth,
|
|
76
|
-
permissions: workflow.func.permissions,
|
|
77
|
-
middleware: workflow.func.middleware as any,
|
|
78
|
-
tags: workflow.func.tags,
|
|
79
|
-
docs: workflow.func.docs as any,
|
|
80
|
-
})
|
|
81
|
-
|
|
82
63
|
// Store workflow definition in state
|
|
83
64
|
const registrations = pikkuState('workflows', 'registrations')
|
|
84
|
-
registrations.set(
|
|
65
|
+
registrations.set(workflowName, {
|
|
66
|
+
name: workflowName,
|
|
67
|
+
func: workflowFunc,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Register the function with pikku
|
|
71
|
+
addFunction(workflowMeta.pikkuFuncName, workflowFunc)
|
|
85
72
|
}
|