@pikku/core 0.12.13 → 0.12.15
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 +46 -0
- package/dist/errors/errors.d.ts +18 -0
- package/dist/errors/errors.js +33 -0
- package/dist/function/function-runner.d.ts +1 -1
- package/dist/function/function-runner.js +10 -5
- package/dist/function/functions.types.js +1 -0
- package/dist/function/index.d.ts +2 -1
- package/dist/function/index.js +1 -0
- package/dist/handle-error.d.ts +2 -2
- package/dist/handle-error.js +8 -8
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/remote-auth.js +5 -2
- package/dist/permissions.d.ts +13 -1
- package/dist/permissions.js +51 -0
- package/dist/pikku-state.d.ts +0 -1
- package/dist/pikku-state.js +2 -4
- package/dist/remote.d.ts +10 -0
- package/dist/remote.js +32 -0
- package/dist/schema.js +2 -0
- package/dist/services/credential-service.d.ts +11 -0
- package/dist/services/credential-wire-service.d.ts +14 -3
- package/dist/services/credential-wire-service.js +49 -6
- package/dist/services/deployment-service.d.ts +12 -1
- package/dist/services/in-memory-queue-service.d.ts +7 -0
- package/dist/services/in-memory-queue-service.js +28 -0
- package/dist/services/index.d.ts +4 -1
- package/dist/services/index.js +2 -1
- package/dist/services/local-credential-service.d.ts +2 -0
- package/dist/services/local-credential-service.js +20 -0
- package/dist/services/logger-console.d.ts +26 -40
- package/dist/services/logger-console.js +102 -46
- package/dist/services/logger.d.ts +5 -0
- package/dist/services/meta-service.d.ts +175 -0
- package/dist/services/meta-service.js +310 -0
- package/dist/services/pikku-user-id.d.ts +3 -0
- package/dist/services/pikku-user-id.js +16 -0
- package/dist/services/typed-credential-service.d.ts +2 -0
- package/dist/services/typed-credential-service.js +6 -0
- package/dist/services/workflow-service.d.ts +6 -1
- package/dist/testing/service-tests.js +0 -8
- package/dist/types/core.types.d.ts +14 -2
- package/dist/types/state.types.d.ts +9 -2
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +21 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +90 -21
- package/dist/wirings/ai-agent/ai-agent-registry.js +2 -2
- package/dist/wirings/ai-agent/ai-agent-runner.js +24 -2
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +61 -3
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +25 -4
- package/dist/wirings/ai-agent/index.d.ts +1 -1
- package/dist/wirings/ai-agent/index.js +1 -1
- package/dist/wirings/channel/channel-runner.js +3 -3
- package/dist/wirings/cli/cli-runner.js +4 -3
- package/dist/wirings/cli/command-parser.js +7 -3
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +23 -15
- package/dist/wirings/http/http.types.d.ts +2 -0
- package/dist/wirings/mcp/mcp-runner.js +5 -3
- package/dist/wirings/queue/queue-runner.d.ts +3 -1
- package/dist/wirings/queue/queue-runner.js +8 -3
- package/dist/wirings/queue/queue.types.d.ts +6 -0
- package/dist/wirings/rpc/rpc-runner.js +57 -90
- package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -1
- package/dist/wirings/scheduler/scheduler-runner.js +9 -5
- package/dist/wirings/trigger/trigger-runner.js +4 -2
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +2 -0
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +1 -1
- package/dist/wirings/workflow/dsl/workflow-runner.js +6 -9
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
- package/dist/wirings/workflow/graph/graph-runner.js +18 -4
- package/dist/wirings/workflow/index.d.ts +4 -2
- package/dist/wirings/workflow/index.js +4 -2
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +16 -4
- package/dist/wirings/workflow/pikku-workflow-service.js +218 -24
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +40 -0
- package/dist/wirings/workflow/workflow-queue-workers.js +41 -0
- package/dist/wirings/workflow/workflow.types.d.ts +19 -1
- package/package.json +4 -1
- package/src/errors/errors.ts +44 -0
- package/src/function/function-runner.ts +24 -13
- package/src/function/functions.types.ts +1 -0
- package/src/function/index.ts +10 -0
- package/src/handle-error.test.ts +2 -2
- package/src/handle-error.ts +8 -8
- package/src/index.ts +1 -2
- package/src/middleware/index.ts +2 -0
- package/src/middleware/remote-auth.test.ts +4 -4
- package/src/middleware/remote-auth.ts +7 -2
- package/src/permissions.ts +70 -0
- package/src/pikku-state.test.ts +0 -26
- package/src/pikku-state.ts +2 -5
- package/src/remote.ts +46 -0
- package/src/schema.ts +1 -0
- package/src/services/credential-service.ts +13 -0
- package/src/services/credential-wire-service.test.ts +174 -0
- package/src/services/credential-wire-service.ts +52 -8
- package/src/services/deployment-service.ts +17 -1
- package/src/services/in-memory-queue-service.ts +46 -0
- package/src/services/index.ts +21 -1
- package/src/services/local-credential-service.ts +22 -0
- package/src/services/logger-console.ts +127 -47
- package/src/services/logger.ts +6 -0
- package/src/services/meta-service.ts +523 -0
- package/src/services/pikku-user-id.test.ts +62 -0
- package/src/services/pikku-user-id.ts +17 -0
- package/src/services/typed-credential-service.ts +8 -0
- package/src/services/workflow-service.ts +8 -0
- package/src/testing/service-tests.ts +0 -10
- package/src/types/core.types.ts +16 -2
- package/src/types/state.types.ts +7 -2
- package/src/wirings/ai-agent/ai-agent-prepare.ts +122 -41
- package/src/wirings/ai-agent/ai-agent-registry.ts +3 -3
- package/src/wirings/ai-agent/ai-agent-runner.ts +22 -3
- package/src/wirings/ai-agent/ai-agent-stream.ts +115 -3
- package/src/wirings/ai-agent/ai-agent.types.ts +27 -4
- package/src/wirings/ai-agent/index.ts +1 -0
- package/src/wirings/channel/channel-handler.ts +0 -1
- package/src/wirings/channel/channel-runner.ts +4 -5
- package/src/wirings/cli/cli-runner.test.ts +8 -7
- package/src/wirings/cli/cli-runner.ts +5 -4
- package/src/wirings/cli/command-parser.ts +8 -3
- package/src/wirings/http/http-runner.ts +27 -18
- package/src/wirings/http/http.types.ts +2 -0
- package/src/wirings/mcp/mcp-runner.ts +7 -10
- package/src/wirings/queue/queue-runner.test.ts +5 -11
- package/src/wirings/queue/queue-runner.ts +12 -4
- package/src/wirings/queue/queue.types.ts +6 -0
- package/src/wirings/rpc/rpc-runner.ts +91 -118
- package/src/wirings/scheduler/scheduler-runner.test.ts +5 -11
- package/src/wirings/scheduler/scheduler-runner.ts +13 -5
- package/src/wirings/trigger/trigger-runner.test.ts +10 -11
- package/src/wirings/trigger/trigger-runner.ts +6 -4
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +2 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +11 -10
- package/src/wirings/workflow/graph/graph-runner.ts +19 -4
- package/src/wirings/workflow/index.ts +17 -6
- package/src/wirings/workflow/pikku-workflow-service.ts +286 -24
- package/src/wirings/workflow/workflow-queue-workers.ts +85 -0
- package/src/wirings/workflow/workflow.types.ts +18 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +0 -6
- package/dist/wirings/ai-agent/agent-dynamic-workflow.js +0 -468
- package/dist/wirings/workflow/workflow-helpers.d.ts +0 -36
- package/dist/wirings/workflow/workflow-helpers.js +0 -38
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +0 -610
- package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
- package/src/wirings/workflow/workflow-helpers.ts +0 -79
|
@@ -1,16 +1,56 @@
|
|
|
1
|
-
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
1
|
+
import { runPikkuFunc, addFunction } from '../../function/function-runner.js'
|
|
2
|
+
import {
|
|
3
|
+
pikkuWorkflowWorkerFunc,
|
|
4
|
+
pikkuWorkflowOrchestratorFunc,
|
|
5
|
+
pikkuWorkflowSleeperFunc,
|
|
6
|
+
} from './workflow-queue-workers.js'
|
|
7
|
+
import { wireQueueWorker } from '../queue/queue-runner.js'
|
|
2
8
|
import {
|
|
3
9
|
getSingletonServices,
|
|
4
10
|
getCreateWireServices,
|
|
5
11
|
pikkuState,
|
|
6
12
|
} from '../../pikku-state.js'
|
|
7
13
|
import { getDurationInMilliseconds } from '../../time-utils.js'
|
|
14
|
+
|
|
15
|
+
const resolveWorkflowMeta = (
|
|
16
|
+
name: string
|
|
17
|
+
): { meta: any; packageName: string | null; resolvedName: string } | null => {
|
|
18
|
+
const rootMeta = pikkuState(null, 'workflows', 'meta')
|
|
19
|
+
if (rootMeta[name]) {
|
|
20
|
+
return { meta: rootMeta[name], packageName: null, resolvedName: name }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const colonIndex = name.indexOf(':')
|
|
24
|
+
if (colonIndex !== -1) {
|
|
25
|
+
const namespace = name.substring(0, colonIndex)
|
|
26
|
+
const localName = name.substring(colonIndex + 1)
|
|
27
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
28
|
+
const pkgConfig = addons?.get(namespace)
|
|
29
|
+
if (pkgConfig) {
|
|
30
|
+
const addonMeta = pikkuState(pkgConfig.package, 'workflows', 'meta')
|
|
31
|
+
if (addonMeta?.[localName]) {
|
|
32
|
+
return {
|
|
33
|
+
meta: addonMeta[localName],
|
|
34
|
+
packageName: pkgConfig.package,
|
|
35
|
+
resolvedName: localName,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const toKebab = (s: string) =>
|
|
45
|
+
s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
|
|
8
46
|
import type { PikkuWire, SerializedError } from '../../types/core.types.js'
|
|
9
47
|
import type { QueueService } from '../queue/queue.types.js'
|
|
10
48
|
import type {
|
|
11
49
|
PikkuWorkflowWire,
|
|
12
50
|
StepState,
|
|
51
|
+
StepStatus,
|
|
13
52
|
WorkflowRun,
|
|
53
|
+
WorkflowRunStatus,
|
|
14
54
|
WorkflowRunWire,
|
|
15
55
|
WorkflowStatus,
|
|
16
56
|
WorkflowVersionStatus,
|
|
@@ -116,7 +156,87 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
116
156
|
return getSingletonServices()?.logger
|
|
117
157
|
}
|
|
118
158
|
|
|
119
|
-
constructor() {
|
|
159
|
+
constructor() {
|
|
160
|
+
const functions = pikkuState(null, 'function', 'functions')
|
|
161
|
+
const functionsMeta = pikkuState(null, 'function', 'meta')
|
|
162
|
+
|
|
163
|
+
// Minimal meta for internal workflow functions (satisfies FunctionMeta)
|
|
164
|
+
const mkMeta = (funcId: string) => ({
|
|
165
|
+
pikkuFuncId: funcId,
|
|
166
|
+
sessionless: true,
|
|
167
|
+
functionType: 'helper' as const,
|
|
168
|
+
inputSchemaName: null,
|
|
169
|
+
outputSchemaName: null,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const queueMeta = pikkuState(null, 'queue', 'meta')
|
|
173
|
+
|
|
174
|
+
const registerWorkflowFunc = (
|
|
175
|
+
funcId: string,
|
|
176
|
+
func: { func: unknown },
|
|
177
|
+
queueName: string
|
|
178
|
+
) => {
|
|
179
|
+
if (functions.has(funcId)) return
|
|
180
|
+
addFunction(funcId, func as never)
|
|
181
|
+
if (!queueMeta[queueName]) {
|
|
182
|
+
queueMeta[queueName] = { pikkuFuncId: funcId, name: queueName }
|
|
183
|
+
}
|
|
184
|
+
wireQueueWorker({ name: queueName, func } as never)
|
|
185
|
+
if (!functionsMeta[funcId]) {
|
|
186
|
+
functionsMeta[funcId] = mkMeta(funcId)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Register shared queue workers for monolith deployments
|
|
191
|
+
registerWorkflowFunc(
|
|
192
|
+
'pikkuWorkflowOrchestrator',
|
|
193
|
+
{ func: pikkuWorkflowOrchestratorFunc },
|
|
194
|
+
'pikku-workflow-orchestrator'
|
|
195
|
+
)
|
|
196
|
+
registerWorkflowFunc(
|
|
197
|
+
'pikkuWorkflowStepWorker',
|
|
198
|
+
{ func: pikkuWorkflowWorkerFunc },
|
|
199
|
+
'pikku-workflow-step-worker'
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// Register per-workflow queue workers (root + addon packages)
|
|
203
|
+
const registerQueueWorkers = (queueMeta: Record<string, any>) => {
|
|
204
|
+
for (const [queueName, meta] of Object.entries(queueMeta)) {
|
|
205
|
+
if (functions.has(meta.pikkuFuncId)) continue
|
|
206
|
+
if (queueName.startsWith('wf-orchestrator-')) {
|
|
207
|
+
registerWorkflowFunc(
|
|
208
|
+
meta.pikkuFuncId,
|
|
209
|
+
{ func: pikkuWorkflowOrchestratorFunc },
|
|
210
|
+
queueName
|
|
211
|
+
)
|
|
212
|
+
} else if (queueName.startsWith('wf-step-')) {
|
|
213
|
+
registerWorkflowFunc(
|
|
214
|
+
meta.pikkuFuncId,
|
|
215
|
+
{ func: pikkuWorkflowWorkerFunc },
|
|
216
|
+
queueName
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
registerQueueWorkers(pikkuState(null, 'queue', 'meta'))
|
|
223
|
+
|
|
224
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
225
|
+
if (addons) {
|
|
226
|
+
for (const [, addon] of addons) {
|
|
227
|
+
const addonQueueMeta = pikkuState(addon.package, 'queue', 'meta')
|
|
228
|
+
if (addonQueueMeta) {
|
|
229
|
+
registerQueueWorkers(addonQueueMeta)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!functions.has('pikkuWorkflowSleeper')) {
|
|
235
|
+
addFunction('pikkuWorkflowSleeper', {
|
|
236
|
+
func: pikkuWorkflowSleeperFunc,
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
}
|
|
120
240
|
|
|
121
241
|
/**
|
|
122
242
|
* Check if a run is executing inline (without queues)
|
|
@@ -162,6 +282,55 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
162
282
|
*/
|
|
163
283
|
abstract getRun(id: string): Promise<WorkflowRun | null>
|
|
164
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Get minimal workflow run status with step summaries.
|
|
287
|
+
* Used by the public API — the console addon provides the full verbose view.
|
|
288
|
+
*/
|
|
289
|
+
async getRunStatus(id: string): Promise<WorkflowRunStatus | null> {
|
|
290
|
+
const run = await this.getRun(id)
|
|
291
|
+
if (!run) return null
|
|
292
|
+
|
|
293
|
+
const history = await this.getRunHistory(id)
|
|
294
|
+
const terminalStatuses = new Set(['completed', 'failed', 'cancelled'])
|
|
295
|
+
|
|
296
|
+
// Build step summaries from history (latest attempt per step)
|
|
297
|
+
const stepMap = new Map<
|
|
298
|
+
string,
|
|
299
|
+
{ status: StepStatus; startedAt?: Date; completedAt?: Date }
|
|
300
|
+
>()
|
|
301
|
+
for (const step of history) {
|
|
302
|
+
const existing = stepMap.get(step.stepName)
|
|
303
|
+
if (!existing || step.updatedAt > existing.completedAt!) {
|
|
304
|
+
stepMap.set(step.stepName, {
|
|
305
|
+
status: step.status,
|
|
306
|
+
startedAt: step.runningAt ?? step.createdAt,
|
|
307
|
+
completedAt: step.succeededAt ?? step.failedAt,
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const steps = [...stepMap.entries()].map(([name, s]) => ({
|
|
313
|
+
name,
|
|
314
|
+
status: s.status,
|
|
315
|
+
duration:
|
|
316
|
+
s.startedAt && s.completedAt
|
|
317
|
+
? s.completedAt.getTime() - s.startedAt.getTime()
|
|
318
|
+
: undefined,
|
|
319
|
+
}))
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
id: run.id,
|
|
323
|
+
status: run.status,
|
|
324
|
+
startedAt: run.createdAt,
|
|
325
|
+
completedAt: terminalStatuses.has(run.status) ? run.updatedAt : undefined,
|
|
326
|
+
steps,
|
|
327
|
+
output: run.status === 'completed' ? run.output : undefined,
|
|
328
|
+
error: run.error
|
|
329
|
+
? { message: run.error.message ?? 'Unknown error' }
|
|
330
|
+
: undefined,
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
165
334
|
/**
|
|
166
335
|
* Get workflow run history (all step attempts in chronological order)
|
|
167
336
|
* @param runId - Run ID
|
|
@@ -379,9 +548,18 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
379
548
|
* Resume a paused workflow by triggering the orchestrator
|
|
380
549
|
* @param runId - Run ID
|
|
381
550
|
*/
|
|
382
|
-
public async resumeWorkflow(
|
|
551
|
+
public async resumeWorkflow(
|
|
552
|
+
runId: string,
|
|
553
|
+
workflowName?: string
|
|
554
|
+
): Promise<void> {
|
|
383
555
|
const queueService = this.verifyQueueService()
|
|
384
|
-
|
|
556
|
+
if (!workflowName) {
|
|
557
|
+
const run = await this.getRun(runId)
|
|
558
|
+
workflowName = run?.workflow
|
|
559
|
+
}
|
|
560
|
+
await queueService.add(this.getOrchestratorQueueName(workflowName), {
|
|
561
|
+
runId,
|
|
562
|
+
})
|
|
385
563
|
}
|
|
386
564
|
|
|
387
565
|
public async queueStepWorker(
|
|
@@ -392,7 +570,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
392
570
|
): Promise<void> {
|
|
393
571
|
const queueService = this.verifyQueueService()
|
|
394
572
|
await queueService.add(
|
|
395
|
-
this.
|
|
573
|
+
this.getStepWorkerQueueName(rpcName),
|
|
396
574
|
JSON.parse(
|
|
397
575
|
JSON.stringify({
|
|
398
576
|
runId,
|
|
@@ -424,11 +602,16 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
424
602
|
*/
|
|
425
603
|
protected async scheduleOrchestratorRetry(
|
|
426
604
|
runId: string,
|
|
427
|
-
retryDelay?: number | string
|
|
605
|
+
retryDelay?: number | string,
|
|
606
|
+
workflowName?: string
|
|
428
607
|
): Promise<void> {
|
|
429
608
|
const queueService = this.verifyQueueService()
|
|
609
|
+
if (!workflowName) {
|
|
610
|
+
const run = await this.getRun(runId)
|
|
611
|
+
workflowName = run?.workflow
|
|
612
|
+
}
|
|
430
613
|
await queueService.add(
|
|
431
|
-
this.
|
|
614
|
+
this.getOrchestratorQueueName(workflowName),
|
|
432
615
|
{ runId },
|
|
433
616
|
retryDelay ? { delay: getDurationInMilliseconds(retryDelay) } : undefined
|
|
434
617
|
)
|
|
@@ -447,15 +630,27 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
447
630
|
rpcService: any,
|
|
448
631
|
options?: { inline?: boolean; startNode?: string }
|
|
449
632
|
): Promise<{ runId: string }> {
|
|
450
|
-
//
|
|
451
|
-
const
|
|
452
|
-
|
|
633
|
+
// Resolve workflow from static meta (root or addon namespace), then dynamic DB
|
|
634
|
+
const resolved = resolveWorkflowMeta(name)
|
|
635
|
+
let workflowMeta = resolved?.meta
|
|
636
|
+
const packageName = resolved?.packageName ?? null
|
|
637
|
+
|
|
638
|
+
if (!workflowMeta) {
|
|
639
|
+
const dynamicWorkflows = await this.getAIGeneratedWorkflows()
|
|
640
|
+
const match = dynamicWorkflows.find((w) => w.workflowName === name)
|
|
641
|
+
if (match?.graph) {
|
|
642
|
+
workflowMeta = match.graph
|
|
643
|
+
}
|
|
644
|
+
}
|
|
453
645
|
|
|
454
646
|
if (!workflowMeta) {
|
|
455
647
|
throw new WorkflowNotFoundError(name)
|
|
456
648
|
}
|
|
457
649
|
|
|
458
|
-
if (
|
|
650
|
+
if (
|
|
651
|
+
workflowMeta.source === 'graph' ||
|
|
652
|
+
workflowMeta.source === 'dynamic-workflow'
|
|
653
|
+
) {
|
|
459
654
|
const shouldInline =
|
|
460
655
|
options?.inline ||
|
|
461
656
|
workflowMeta.inline ||
|
|
@@ -467,13 +662,14 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
467
662
|
rpcService,
|
|
468
663
|
shouldInline,
|
|
469
664
|
options?.startNode,
|
|
470
|
-
wire
|
|
665
|
+
wire,
|
|
666
|
+
workflowMeta
|
|
471
667
|
)
|
|
472
668
|
}
|
|
473
669
|
|
|
474
670
|
// DSL workflow - check registration exists
|
|
475
|
-
const registrations = pikkuState(
|
|
476
|
-
const workflow = registrations.get(name)
|
|
671
|
+
const registrations = pikkuState(packageName, 'workflows', 'registrations')
|
|
672
|
+
const workflow = registrations.get(resolved?.resolvedName ?? name)
|
|
477
673
|
|
|
478
674
|
if (!workflow) {
|
|
479
675
|
throw new WorkflowNotFoundError(name)
|
|
@@ -559,8 +755,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
559
755
|
throw new WorkflowRunNotFoundError(runId)
|
|
560
756
|
}
|
|
561
757
|
|
|
562
|
-
const
|
|
563
|
-
const workflowMeta = meta
|
|
758
|
+
const resolved = resolveWorkflowMeta(run.workflow)
|
|
759
|
+
const workflowMeta = resolved?.meta
|
|
760
|
+
const pkgName = resolved?.packageName ?? null
|
|
564
761
|
|
|
565
762
|
if (
|
|
566
763
|
run.graphHash &&
|
|
@@ -571,7 +768,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
571
768
|
return
|
|
572
769
|
}
|
|
573
770
|
|
|
574
|
-
if (
|
|
771
|
+
if (
|
|
772
|
+
workflowMeta?.source === 'graph' ||
|
|
773
|
+
workflowMeta?.source === 'dynamic-workflow'
|
|
774
|
+
) {
|
|
575
775
|
await continueGraph(this, runId, run.workflow)
|
|
576
776
|
const updatedRun = await this.getRun(runId)
|
|
577
777
|
if (updatedRun?.status === 'completed') {
|
|
@@ -588,20 +788,49 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
588
788
|
return
|
|
589
789
|
}
|
|
590
790
|
|
|
591
|
-
|
|
592
|
-
|
|
791
|
+
if (!workflowMeta) {
|
|
792
|
+
const dynamicWorkflows = await this.getAIGeneratedWorkflows()
|
|
793
|
+
const match = dynamicWorkflows.find(
|
|
794
|
+
(w) => w.workflowName === run.workflow
|
|
795
|
+
)
|
|
796
|
+
if (match?.graph) {
|
|
797
|
+
await continueGraph(this, runId, run.workflow, match.graph)
|
|
798
|
+
const updatedRun = await this.getRun(runId)
|
|
799
|
+
if (updatedRun?.status === 'completed') {
|
|
800
|
+
await this.onChildWorkflowCompleted(updatedRun, updatedRun.output)
|
|
801
|
+
} else if (
|
|
802
|
+
updatedRun?.status === 'failed' ||
|
|
803
|
+
updatedRun?.status === 'cancelled'
|
|
804
|
+
) {
|
|
805
|
+
await this.onChildWorkflowFailed(
|
|
806
|
+
updatedRun,
|
|
807
|
+
new Error(updatedRun.error?.message || 'Child workflow failed')
|
|
808
|
+
)
|
|
809
|
+
}
|
|
810
|
+
return
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const registrations = pikkuState(pkgName, 'workflows', 'registrations')
|
|
815
|
+
const workflow = registrations.get(resolved?.resolvedName ?? run.workflow)
|
|
593
816
|
if (!workflow) {
|
|
594
817
|
throw new WorkflowNotFoundError(run.workflow)
|
|
595
818
|
}
|
|
596
819
|
|
|
597
820
|
await this.withRunLock(runId, async () => {
|
|
821
|
+
const addonNs = run.workflow.includes(':')
|
|
822
|
+
? run.workflow.substring(0, run.workflow.indexOf(':'))
|
|
823
|
+
: null
|
|
598
824
|
const workflowWire = this.createWorkflowWire(
|
|
599
825
|
run.workflow,
|
|
600
826
|
runId,
|
|
601
|
-
rpcService
|
|
827
|
+
rpcService,
|
|
828
|
+
addonNs
|
|
602
829
|
)
|
|
830
|
+
workflowWire.pikkuUserId = run.wire?.pikkuUserId
|
|
603
831
|
const wire: PikkuWire = {
|
|
604
832
|
workflow: workflowWire,
|
|
833
|
+
pikkuUserId: run.wire?.pikkuUserId,
|
|
605
834
|
session: rpcService.wire?.session,
|
|
606
835
|
rpc: rpcService.wire?.rpc,
|
|
607
836
|
}
|
|
@@ -615,6 +844,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
615
844
|
wire,
|
|
616
845
|
createWireServices: getCreateWireServices(),
|
|
617
846
|
data: () => run.input,
|
|
847
|
+
packageName: pkgName ?? undefined,
|
|
618
848
|
}
|
|
619
849
|
)
|
|
620
850
|
|
|
@@ -761,7 +991,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
761
991
|
|
|
762
992
|
const isGraphWorkflow =
|
|
763
993
|
workflowMeta?.source === 'graph' ||
|
|
764
|
-
workflowMeta?.source === '
|
|
994
|
+
workflowMeta?.source === 'dynamic-workflow'
|
|
765
995
|
if (
|
|
766
996
|
isGraphWorkflow &&
|
|
767
997
|
workflowMeta?.nodes &&
|
|
@@ -961,7 +1191,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
961
1191
|
const retryDelay = stepOptions?.retryDelay
|
|
962
1192
|
|
|
963
1193
|
await getSingletonServices()!.queueService!.add(
|
|
964
|
-
this.
|
|
1194
|
+
this.getStepWorkerQueueName(rpcName),
|
|
965
1195
|
JSON.parse(
|
|
966
1196
|
JSON.stringify({
|
|
967
1197
|
runId,
|
|
@@ -1272,7 +1502,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1272
1502
|
public createWorkflowWire(
|
|
1273
1503
|
name: string,
|
|
1274
1504
|
runId: string,
|
|
1275
|
-
rpcService: any
|
|
1505
|
+
rpcService: any,
|
|
1506
|
+
addonNamespace?: string | null
|
|
1276
1507
|
): PikkuWorkflowWire {
|
|
1277
1508
|
const workflowWire: PikkuWorkflowWire = {
|
|
1278
1509
|
name,
|
|
@@ -1288,10 +1519,14 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1288
1519
|
) => {
|
|
1289
1520
|
this.verifyStepName(stepName)
|
|
1290
1521
|
if (typeof rpcNameOrFn === 'string') {
|
|
1522
|
+
const resolvedRpcName =
|
|
1523
|
+
addonNamespace && !rpcNameOrFn.includes(':')
|
|
1524
|
+
? `${addonNamespace}:${rpcNameOrFn}`
|
|
1525
|
+
: rpcNameOrFn
|
|
1291
1526
|
return await this.rpcStep(
|
|
1292
1527
|
runId,
|
|
1293
1528
|
stepName,
|
|
1294
|
-
|
|
1529
|
+
resolvedRpcName,
|
|
1295
1530
|
dataOrOptions,
|
|
1296
1531
|
rpcService,
|
|
1297
1532
|
options
|
|
@@ -1342,4 +1577,31 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1342
1577
|
sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
|
|
1343
1578
|
}
|
|
1344
1579
|
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Get the orchestrator queue name for a specific workflow.
|
|
1583
|
+
* Checks queue meta for a per-workflow queue first (e.g. wf-orchestrator-{name}),
|
|
1584
|
+
* falls back to the shared orchestrator queue.
|
|
1585
|
+
*/
|
|
1586
|
+
protected getOrchestratorQueueName(workflowName?: string): string {
|
|
1587
|
+
if (workflowName) {
|
|
1588
|
+
const perWorkflow = `wf-orchestrator-${toKebab(workflowName)}`
|
|
1589
|
+
const registrations = pikkuState(null, 'queue', 'registrations')
|
|
1590
|
+
if (registrations.has(perWorkflow)) {
|
|
1591
|
+
return perWorkflow
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return this.getConfig().orchestratorQueueName
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
protected getStepWorkerQueueName(rpcName?: string): string {
|
|
1598
|
+
if (rpcName) {
|
|
1599
|
+
const perStep = `wf-step-${toKebab(rpcName)}`
|
|
1600
|
+
const registrations = pikkuState(null, 'queue', 'registrations')
|
|
1601
|
+
if (registrations.has(perStep)) {
|
|
1602
|
+
return perStep
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return this.getConfig().stepWorkerQueueName
|
|
1606
|
+
}
|
|
1345
1607
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue worker functions for workflow orchestration.
|
|
3
|
+
*
|
|
4
|
+
* These are registered as queue consumers by the codegen pipeline
|
|
5
|
+
* (injected into queue meta when workflows exist). They provide
|
|
6
|
+
* the runtime implementations that process queued workflow jobs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { getSingletonServices } from '../../pikku-state.js'
|
|
10
|
+
import type { PikkuRPC } from '../rpc/rpc-types.js'
|
|
11
|
+
|
|
12
|
+
export interface WorkflowStepInput {
|
|
13
|
+
runId: string
|
|
14
|
+
stepName: string
|
|
15
|
+
rpcName: string
|
|
16
|
+
data: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PikkuWorkflowOrchestratorInput {
|
|
20
|
+
runId: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PikkuWorkflowSleeperInput {
|
|
24
|
+
runId: string
|
|
25
|
+
stepId: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Step worker — executes individual workflow steps dispatched via queue.
|
|
30
|
+
* The orchestrator queues steps here when they're async (not inline).
|
|
31
|
+
*/
|
|
32
|
+
export async function pikkuWorkflowWorkerFunc(
|
|
33
|
+
_services: Record<string, unknown>,
|
|
34
|
+
{ runId, stepName, rpcName, data }: WorkflowStepInput,
|
|
35
|
+
{ rpc }: { rpc: PikkuRPC }
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
const services = getSingletonServices()
|
|
38
|
+
if (!services?.workflowService) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Workflow service not initialized: cannot execute workflow step for runId ${runId}, stepName ${stepName}`
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
await services.workflowService.executeWorkflowStep(
|
|
44
|
+
runId,
|
|
45
|
+
stepName,
|
|
46
|
+
rpcName,
|
|
47
|
+
data,
|
|
48
|
+
rpc
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Orchestrator — resumes workflow execution after an async step completes.
|
|
54
|
+
* Called when a step worker finishes and the workflow needs to continue.
|
|
55
|
+
*/
|
|
56
|
+
export async function pikkuWorkflowOrchestratorFunc(
|
|
57
|
+
_services: Record<string, unknown>,
|
|
58
|
+
{ runId }: PikkuWorkflowOrchestratorInput,
|
|
59
|
+
{ rpc }: { rpc: PikkuRPC }
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
const services = getSingletonServices()
|
|
62
|
+
if (!services?.workflowService) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Workflow service not initialized: cannot orchestrate workflow for runId ${runId}`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
await services.workflowService.orchestrateWorkflow(runId, rpc)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Sleeper — wakes a workflow after a workflow.sleep() duration expires.
|
|
72
|
+
* Triggered by a delayed queue message or scheduler callback.
|
|
73
|
+
*/
|
|
74
|
+
export async function pikkuWorkflowSleeperFunc(
|
|
75
|
+
_services: Record<string, unknown>,
|
|
76
|
+
{ runId, stepId }: PikkuWorkflowSleeperInput
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
const services = getSingletonServices()
|
|
79
|
+
if (!services?.workflowService) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Workflow service not initialized: cannot execute workflow sleep completed for runId ${runId}, stepId ${stepId}`
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
await services.workflowService.executeWorkflowSleepCompleted(runId, stepId)
|
|
85
|
+
}
|
|
@@ -41,6 +41,8 @@ export interface WorkflowRunWire {
|
|
|
41
41
|
id?: string
|
|
42
42
|
parentRunId?: string
|
|
43
43
|
parentStepId?: string
|
|
44
|
+
/** Pikku user ID propagated from the originating request for credential resolution */
|
|
45
|
+
pikkuUserId?: string
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export interface WorkflowServiceConfig {
|
|
@@ -139,6 +141,20 @@ export interface StepState {
|
|
|
139
141
|
failedAt?: Date
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
export interface WorkflowRunStatus {
|
|
145
|
+
id: string
|
|
146
|
+
status: WorkflowStatus
|
|
147
|
+
startedAt: Date
|
|
148
|
+
completedAt?: Date
|
|
149
|
+
steps: Array<{
|
|
150
|
+
name: string
|
|
151
|
+
status: StepStatus
|
|
152
|
+
duration?: number
|
|
153
|
+
}>
|
|
154
|
+
output?: unknown
|
|
155
|
+
error?: { message: string }
|
|
156
|
+
}
|
|
157
|
+
|
|
142
158
|
export interface WorkflowRunService {
|
|
143
159
|
listRuns(options?: {
|
|
144
160
|
workflowName?: string
|
|
@@ -226,6 +242,7 @@ export type WorkflowsMeta = Record<
|
|
|
226
242
|
context?: WorkflowContext
|
|
227
243
|
dsl?: boolean
|
|
228
244
|
inline?: boolean
|
|
245
|
+
expose?: boolean
|
|
229
246
|
}
|
|
230
247
|
>
|
|
231
248
|
|
|
@@ -240,7 +257,7 @@ export interface WorkflowRuntimeMeta {
|
|
|
240
257
|
/** Pikku function name (for execution) */
|
|
241
258
|
pikkuFuncId: string
|
|
242
259
|
/** Source type: 'dsl' (serializable), 'complex' (has inline steps), 'graph' */
|
|
243
|
-
source: 'dsl' | 'complex' | 'graph' | '
|
|
260
|
+
source: 'dsl' | 'complex' | 'graph' | 'dynamic-workflow'
|
|
244
261
|
/** Optional description */
|
|
245
262
|
description?: string
|
|
246
263
|
/** Tags for organization */
|