@pikku/core 0.12.20 → 0.12.22
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 +17 -0
- package/dist/dev/hot-reload.js +20 -6
- package/dist/errors/error-handler.d.ts +1 -1
- package/dist/errors/error-handler.js +2 -2
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/handle-error.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.js +2 -2
- package/dist/middleware/telemetry.d.ts +2 -2
- package/dist/middleware/telemetry.js +2 -2
- package/dist/middleware-runner.d.ts +15 -27
- package/dist/middleware-runner.js +25 -30
- package/dist/permissions.d.ts +15 -22
- package/dist/permissions.js +30 -25
- package/dist/pikku-request.js +1 -1
- package/dist/pikku-state.js +2 -3
- package/dist/services/ai-agent-runner-service.d.ts +1 -0
- package/dist/services/in-memory-queue-service.js +1 -1
- package/dist/services/in-memory-workflow-service.d.ts +15 -13
- package/dist/services/in-memory-workflow-service.js +31 -13
- package/dist/services/local-content.js +8 -1
- package/dist/services/user-session-service.d.ts +1 -1
- package/dist/testing/service-tests.js +24 -0
- package/dist/types/core.types.d.ts +4 -0
- package/dist/types/state.types.d.ts +2 -18
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +24 -5
- package/dist/wirings/ai-agent/ai-agent-memory.js +128 -23
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +10 -1
- package/dist/wirings/ai-agent/ai-agent-model-config.js +15 -35
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +45 -31
- package/dist/wirings/ai-agent/ai-agent-stream.js +63 -34
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -0
- package/dist/wirings/channel/channel-handler.js +1 -1
- package/dist/wirings/channel/channel-store.d.ts +13 -0
- package/dist/wirings/channel/channel.types.d.ts +3 -0
- package/dist/wirings/channel/local/local-channel-runner.js +23 -5
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +8 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +9 -0
- package/dist/wirings/cli/cli-runner.js +24 -5
- package/dist/wirings/cli/command-parser.js +19 -4
- package/dist/wirings/http/http-runner.js +72 -36
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -1
- package/dist/wirings/http/web-request.js +32 -0
- package/dist/wirings/rpc/rpc-runner.js +13 -3
- package/dist/wirings/workflow/graph/graph-node.d.ts +8 -0
- package/dist/wirings/workflow/graph/graph-node.js +4 -0
- package/dist/wirings/workflow/graph/graph-runner.js +12 -10
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +4 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +81 -16
- package/dist/wirings/workflow/pikku-workflow-service.js +266 -45
- package/dist/wirings/workflow/workflow.types.d.ts +34 -0
- package/package.json +1 -1
- package/run-tests.sh +4 -1
- package/src/dev/hot-reload.test.ts +62 -11
- package/src/dev/hot-reload.ts +28 -7
- package/src/errors/error-handler.ts +8 -2
- package/src/errors/error.test.ts +2 -0
- package/src/function/function-runner.test.ts +500 -10
- package/src/function/functions.types.ts +3 -2
- package/src/handle-error.test.ts +1 -1
- package/src/handle-error.ts +4 -1
- package/src/index.ts +12 -2
- package/src/middleware/index.ts +11 -2
- package/src/middleware/telemetry.ts +2 -2
- package/src/middleware-runner.test.ts +16 -16
- package/src/middleware-runner.ts +42 -30
- package/src/permissions.test.ts +27 -24
- package/src/permissions.ts +41 -25
- package/src/pikku-request.test.ts +35 -0
- package/src/pikku-request.ts +1 -1
- package/src/pikku-state.ts +2 -3
- package/src/run-tests-script.test.ts +18 -0
- package/src/services/ai-agent-runner-service.ts +1 -0
- package/src/services/in-memory-queue-service.ts +1 -1
- package/src/services/in-memory-session-store.ts +1 -2
- package/src/services/in-memory-workflow-service.test.ts +33 -11
- package/src/services/in-memory-workflow-service.ts +49 -13
- package/src/services/local-content.test.ts +54 -0
- package/src/services/local-content.ts +12 -1
- package/src/services/typed-credential-service.ts +3 -3
- package/src/services/typed-secret-service.ts +3 -3
- package/src/services/typed-variables-service.ts +3 -3
- package/src/services/user-session-service.ts +4 -4
- package/src/testing/service-tests.ts +30 -0
- package/src/types/core.types.ts +6 -2
- package/src/types/state.types.ts +2 -13
- package/src/wirings/ai-agent/ai-agent-memory.test.ts +324 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +187 -36
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +12 -90
- package/src/wirings/ai-agent/ai-agent-model-config.ts +14 -38
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +292 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -1
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +230 -3
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +625 -6
- package/src/wirings/ai-agent/ai-agent-runner.ts +65 -50
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +544 -5
- package/src/wirings/ai-agent/ai-agent-stream.ts +71 -69
- package/src/wirings/ai-agent/ai-agent.types.ts +24 -0
- package/src/wirings/channel/channel-handler.test.ts +272 -0
- package/src/wirings/channel/channel-handler.ts +1 -1
- package/src/wirings/channel/channel-middleware-runner.test.ts +163 -0
- package/src/wirings/channel/channel-store.ts +19 -0
- package/src/wirings/channel/channel.types.ts +4 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +63 -0
- package/src/wirings/channel/local/local-channel-runner.ts +41 -5
- package/src/wirings/channel/local/local-eventhub-service.ts +3 -3
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +9 -2
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +23 -5
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +7 -2
- package/src/wirings/cli/cli-runner.test.ts +255 -2
- package/src/wirings/cli/cli-runner.ts +31 -10
- package/src/wirings/cli/cli.types.ts +4 -8
- package/src/wirings/cli/command-parser.test.ts +83 -0
- package/src/wirings/cli/command-parser.ts +23 -4
- package/src/wirings/http/http-runner.test.ts +296 -1
- package/src/wirings/http/http-runner.ts +87 -57
- package/src/wirings/http/http.types.ts +11 -26
- package/src/wirings/http/pikku-fetch-http-request.ts +8 -4
- package/src/wirings/http/pikku-fetch-http-response.test.ts +41 -0
- package/src/wirings/http/web-request.test.ts +115 -0
- package/src/wirings/http/web-request.ts +43 -5
- package/src/wirings/mcp/mcp-runner.test.ts +367 -0
- package/src/wirings/queue/queue.types.ts +2 -5
- package/src/wirings/rpc/rpc-runner.test.ts +511 -0
- package/src/wirings/rpc/rpc-runner.ts +15 -3
- package/src/wirings/rpc/wire-addon.test.ts +57 -0
- package/src/wirings/workflow/graph/graph-node.ts +12 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +98 -0
- package/src/wirings/workflow/graph/graph-runner.ts +28 -10
- package/src/wirings/workflow/graph/workflow-graph.types.ts +4 -0
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +19 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +370 -71
- package/src/wirings/workflow/workflow-queue-workers.test.ts +131 -0
- package/src/wirings/workflow/workflow.types.ts +68 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
function toCamelCase(str) {
|
|
3
3
|
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4
4
|
}
|
|
5
|
+
/** Convert camelCase to kebab-case for display: "autoApply" → "auto-apply".
|
|
6
|
+
* Flags are stored camelCase (matching the function input field) but shown
|
|
7
|
+
* kebab; the parser accepts both forms via toCamelCase. */
|
|
8
|
+
function toKebabCase(str) {
|
|
9
|
+
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
10
|
+
}
|
|
5
11
|
/**
|
|
6
12
|
* Parses raw CLI arguments into structured data for a specific program
|
|
7
13
|
*/
|
|
@@ -77,6 +83,15 @@ export function parseCLIArguments(args, programName, allMeta) {
|
|
|
77
83
|
}
|
|
78
84
|
return result;
|
|
79
85
|
}
|
|
86
|
+
// A group/parent command — has subcommands but no runnable function of its
|
|
87
|
+
// own — cannot be executed directly. Surface a routable error so the runner
|
|
88
|
+
// shows its subcommand help instead of attempting to run a missing function.
|
|
89
|
+
if (!commandMeta.pikkuFuncId &&
|
|
90
|
+
commandMeta.subcommands &&
|
|
91
|
+
Object.keys(commandMeta.subcommands).length > 0) {
|
|
92
|
+
result.errors.push(`Missing subcommand: ${result.commandPath.join(' ')}`);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
80
95
|
// Collect all available options (global + inherited)
|
|
81
96
|
const availableOptions = collectAvailableOptions(meta, result.commandPath);
|
|
82
97
|
// Parse remaining arguments as positionals and options
|
|
@@ -289,7 +304,7 @@ function applyOptionDefaults(optionDefs, options, result) {
|
|
|
289
304
|
}
|
|
290
305
|
// Check required
|
|
291
306
|
if (def.required && !(name in options)) {
|
|
292
|
-
result.errors.push(`Missing required option: --${name}`);
|
|
307
|
+
result.errors.push(`Missing required option: --${toKebabCase(name)}`);
|
|
293
308
|
}
|
|
294
309
|
// Validate choices
|
|
295
310
|
if (def.choices && name in options) {
|
|
@@ -297,12 +312,12 @@ function applyOptionDefaults(optionDefs, options, result) {
|
|
|
297
312
|
if (Array.isArray(value)) {
|
|
298
313
|
for (const v of value) {
|
|
299
314
|
if (!def.choices.includes(v)) {
|
|
300
|
-
result.errors.push(`Invalid value for --${name}: ${v}. Valid choices: ${def.choices.join(', ')}`);
|
|
315
|
+
result.errors.push(`Invalid value for --${toKebabCase(name)}: ${v}. Valid choices: ${def.choices.join(', ')}`);
|
|
301
316
|
}
|
|
302
317
|
}
|
|
303
318
|
}
|
|
304
319
|
else if (!def.choices.includes(value)) {
|
|
305
|
-
result.errors.push(`Invalid value for --${name}: ${value}. Valid choices: ${def.choices.join(', ')}`);
|
|
320
|
+
result.errors.push(`Invalid value for --${toKebabCase(name)}: ${value}. Valid choices: ${def.choices.join(', ')}`);
|
|
306
321
|
}
|
|
307
322
|
}
|
|
308
323
|
}
|
|
@@ -390,7 +405,7 @@ function formatOptions(options, lines) {
|
|
|
390
405
|
else {
|
|
391
406
|
line += ' ';
|
|
392
407
|
}
|
|
393
|
-
line += `--${name}`;
|
|
408
|
+
line += `--${toKebabCase(name)}`;
|
|
394
409
|
if (opt.default !== undefined && typeof opt.default !== 'boolean') {
|
|
395
410
|
line += ` <value>`;
|
|
396
411
|
}
|
|
@@ -239,8 +239,10 @@ const executeRoute = async (services, matchedRoute, http, options) => {
|
|
|
239
239
|
response.setMode('stream');
|
|
240
240
|
response.header('Content-Type', 'text/event-stream');
|
|
241
241
|
response.header('Cache-Control', 'no-cache');
|
|
242
|
+
let sseState;
|
|
243
|
+
const channelId = createWeakUID();
|
|
242
244
|
channel = {
|
|
243
|
-
channelId
|
|
245
|
+
channelId,
|
|
244
246
|
openingData: await data(),
|
|
245
247
|
send: (data) => {
|
|
246
248
|
response.arrayBuffer(isSerializable(data) ? JSON.stringify(data) : data);
|
|
@@ -253,7 +255,36 @@ const executeRoute = async (services, matchedRoute, http, options) => {
|
|
|
253
255
|
response.close?.();
|
|
254
256
|
},
|
|
255
257
|
state: 'open',
|
|
258
|
+
setState: (s) => {
|
|
259
|
+
sseState = s;
|
|
260
|
+
},
|
|
261
|
+
getState: () => sseState,
|
|
262
|
+
clearState: () => {
|
|
263
|
+
sseState = undefined;
|
|
264
|
+
},
|
|
256
265
|
};
|
|
266
|
+
// Register the SSE channel with the eventHub (if present) so that
|
|
267
|
+
// eventHub.publish() can deliver messages to SSE subscribers.
|
|
268
|
+
// The channel handler wraps the SSE channel to match PikkuChannelHandler.
|
|
269
|
+
if (singletonServices.eventHub?.onChannelOpened) {
|
|
270
|
+
const channelRef = channel;
|
|
271
|
+
const channelHandler = {
|
|
272
|
+
getChannel: () => channelRef,
|
|
273
|
+
send: (data, isBinary) => {
|
|
274
|
+
if (isBinary)
|
|
275
|
+
channelRef.sendBinary(data);
|
|
276
|
+
else
|
|
277
|
+
channelRef.send(data);
|
|
278
|
+
},
|
|
279
|
+
sendBinary: (data) => channelRef.sendBinary(data),
|
|
280
|
+
};
|
|
281
|
+
singletonServices.eventHub.onChannelOpened(channelHandler);
|
|
282
|
+
const originalClose = channel.close;
|
|
283
|
+
channel.close = () => {
|
|
284
|
+
singletonServices.eventHub.onChannelClosed(channelId);
|
|
285
|
+
originalClose();
|
|
286
|
+
};
|
|
287
|
+
}
|
|
257
288
|
}
|
|
258
289
|
const wire = {
|
|
259
290
|
traceId: requestId,
|
|
@@ -264,22 +295,45 @@ const executeRoute = async (services, matchedRoute, http, options) => {
|
|
|
264
295
|
getSession: () => userSession.get(),
|
|
265
296
|
hasSessionChanged: () => userSession.sessionChanged,
|
|
266
297
|
};
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
try {
|
|
299
|
+
result = await runPikkuFunc('http', `${meta.method}:${meta.route}`, meta.pikkuFuncId, {
|
|
300
|
+
singletonServices,
|
|
301
|
+
createWireServices,
|
|
302
|
+
auth: route.auth !== false,
|
|
303
|
+
data,
|
|
304
|
+
inheritedMiddleware: meta.middleware,
|
|
305
|
+
wireMiddleware: route.middleware,
|
|
306
|
+
inheritedPermissions: meta.permissions,
|
|
307
|
+
wirePermissions: route.permissions,
|
|
308
|
+
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
309
|
+
tags: route.tags,
|
|
310
|
+
wire,
|
|
311
|
+
sessionService: userSession,
|
|
312
|
+
packageName: meta.packageName,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
if (matchedRoute.route.sse) {
|
|
317
|
+
singletonServices.logger.error(e instanceof Error ? e.message : e);
|
|
318
|
+
try {
|
|
319
|
+
const errorResponse = getErrorResponse(e);
|
|
320
|
+
http?.response?.arrayBuffer(JSON.stringify({
|
|
321
|
+
type: 'error',
|
|
322
|
+
errorText: errorResponse?.message ?? 'Internal server error',
|
|
323
|
+
}));
|
|
324
|
+
http?.response?.arrayBuffer(JSON.stringify({ type: 'done' }));
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
327
|
+
channel?.close();
|
|
328
|
+
return wireServices ? { result, wireServices } : { result };
|
|
329
|
+
}
|
|
330
|
+
throw e;
|
|
331
|
+
}
|
|
332
|
+
if (matchedRoute.route.sse) {
|
|
333
|
+
// Flush headers after middleware has run so CORS/auth headers are included
|
|
334
|
+
http?.response?.flushHeaders?.();
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
283
337
|
if (result instanceof Response) {
|
|
284
338
|
await applyWebResponse(http.response, result);
|
|
285
339
|
}
|
|
@@ -405,25 +459,7 @@ export const fetchData = async (request, response, { skipUserSession = false, re
|
|
|
405
459
|
return result;
|
|
406
460
|
}
|
|
407
461
|
catch (e) {
|
|
408
|
-
|
|
409
|
-
// For SSE routes, send error through the stream since the response is already in stream mode
|
|
410
|
-
scopedLogger.error(e instanceof Error ? e.message : e);
|
|
411
|
-
try {
|
|
412
|
-
const errorResponse = getErrorResponse(e);
|
|
413
|
-
response.arrayBuffer(JSON.stringify({
|
|
414
|
-
type: 'error',
|
|
415
|
-
errorText: errorResponse?.message ?? 'Internal server error',
|
|
416
|
-
}));
|
|
417
|
-
response.arrayBuffer(JSON.stringify({ type: 'done' }));
|
|
418
|
-
}
|
|
419
|
-
catch (streamErr) {
|
|
420
|
-
scopedLogger.error(`SSE error while sending error payload: ${streamErr instanceof Error ? streamErr.message : String(streamErr)}`);
|
|
421
|
-
}
|
|
422
|
-
response.close?.();
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
handleHTTPError(e, http, requestId, scopedLogger, logWarningsForStatusCodes, respondWith404, bubbleErrors, exposeErrors);
|
|
426
|
-
}
|
|
462
|
+
handleHTTPError(e, http, requestId, scopedLogger, logWarningsForStatusCodes, respondWith404, bubbleErrors, exposeErrors);
|
|
427
463
|
}
|
|
428
464
|
finally {
|
|
429
465
|
// Clean up any session-specific services created during processing
|
|
@@ -177,6 +177,7 @@ export interface PikkuHTTPResponse<Out = unknown> {
|
|
|
177
177
|
redirect(location: string, status?: number): this;
|
|
178
178
|
close?: () => void;
|
|
179
179
|
setMode?: (mode: 'stream') => void;
|
|
180
|
+
flushHeaders?: () => void;
|
|
180
181
|
}
|
|
181
182
|
/**
|
|
182
183
|
* Single route configuration - supports all wireHTTP options
|
|
@@ -89,7 +89,9 @@ export class PikkuFetchHTTPRequest {
|
|
|
89
89
|
const merged = {};
|
|
90
90
|
for (const part of parts) {
|
|
91
91
|
for (const [key, value] of Object.entries(part)) {
|
|
92
|
-
if (key === '__proto__' ||
|
|
92
|
+
if (key === '__proto__' ||
|
|
93
|
+
key === 'constructor' ||
|
|
94
|
+
key === 'prototype') {
|
|
93
95
|
continue;
|
|
94
96
|
}
|
|
95
97
|
if (key in merged && !valuesAreEquivalent(merged[key], value)) {
|
|
@@ -57,17 +57,46 @@ export function toWebRequest(req, baseUrl) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
const SKIP_RESPONSE_HEADERS = new Set(['content-length', 'transfer-encoding']);
|
|
60
|
+
function collectSetCookieHeaders(webResponse) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const values = [];
|
|
63
|
+
const add = (cookie) => {
|
|
64
|
+
if (!cookie || seen.has(cookie)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
seen.add(cookie);
|
|
68
|
+
values.push(cookie);
|
|
69
|
+
};
|
|
70
|
+
const headersWithGetSetCookie = webResponse.headers;
|
|
71
|
+
if (typeof headersWithGetSetCookie.getSetCookie === 'function') {
|
|
72
|
+
for (const cookie of headersWithGetSetCookie.getSetCookie()) {
|
|
73
|
+
add(cookie);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
webResponse.headers.forEach((value, name) => {
|
|
78
|
+
if (name.toLowerCase() === 'set-cookie') {
|
|
79
|
+
add(value);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return values;
|
|
84
|
+
}
|
|
60
85
|
/**
|
|
61
86
|
* Applies a Web API Response to a PikkuHTTPResponse.
|
|
62
87
|
* Copies status, headers (including Set-Cookie), redirects, and body.
|
|
63
88
|
*/
|
|
64
89
|
export async function applyWebResponse(res, webResponse) {
|
|
65
90
|
res.status(webResponse.status);
|
|
91
|
+
const setCookieValues = collectSetCookieHeaders(webResponse);
|
|
66
92
|
webResponse.headers.forEach((value, name) => {
|
|
67
93
|
const lower = name.toLowerCase();
|
|
68
94
|
if (SKIP_RESPONSE_HEADERS.has(lower)) {
|
|
69
95
|
return;
|
|
70
96
|
}
|
|
97
|
+
if (lower === 'set-cookie') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
71
100
|
if (lower === 'location') {
|
|
72
101
|
res.redirect(value, webResponse.status);
|
|
73
102
|
}
|
|
@@ -75,6 +104,9 @@ export async function applyWebResponse(res, webResponse) {
|
|
|
75
104
|
res.header(name, value);
|
|
76
105
|
}
|
|
77
106
|
});
|
|
107
|
+
if (setCookieValues.length > 0) {
|
|
108
|
+
res.header('Set-Cookie', setCookieValues);
|
|
109
|
+
}
|
|
78
110
|
const body = await webResponse.text();
|
|
79
111
|
if (body) {
|
|
80
112
|
if (res.send) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runPikkuFunc } from '../../function/function-runner.js';
|
|
2
2
|
import { pikkuState } from '../../pikku-state.js';
|
|
3
|
-
import { ForbiddenError } from '../../errors/errors.js';
|
|
4
3
|
import { PikkuError, addError } from '../../errors/error-handler.js';
|
|
5
4
|
import { parseVersionedId } from '../../version.js';
|
|
6
5
|
export class RPCNotFoundError extends PikkuError {
|
|
@@ -63,6 +62,16 @@ const resolvePikkuFunction = (rpcName, packageName = null) => {
|
|
|
63
62
|
rpcMeta = rpc[baseName];
|
|
64
63
|
}
|
|
65
64
|
}
|
|
65
|
+
if (!rpcMeta) {
|
|
66
|
+
const rootFunctions = pikkuState(null, 'function', 'meta');
|
|
67
|
+
const rootFunctionMeta = rootFunctions?.[rpcName];
|
|
68
|
+
if (rootFunctionMeta) {
|
|
69
|
+
return {
|
|
70
|
+
pikkuFuncId: rootFunctionMeta.pikkuFuncId || rpcName,
|
|
71
|
+
packageName: null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
66
75
|
if (!rpcMeta) {
|
|
67
76
|
throw new RPCNotFoundError(rpcName);
|
|
68
77
|
}
|
|
@@ -89,13 +98,14 @@ export class ContextAwareRPCService {
|
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
100
|
else {
|
|
92
|
-
|
|
101
|
+
const resolved = resolvePikkuFunction(funcName, this.packageName);
|
|
102
|
+
functionMeta = pikkuState(resolved.packageName, 'function', 'meta')[resolved.pikkuFuncId];
|
|
93
103
|
}
|
|
94
104
|
if (!functionMeta) {
|
|
95
105
|
throw new RPCNotFoundError(funcName);
|
|
96
106
|
}
|
|
97
107
|
if (!functionMeta.expose) {
|
|
98
|
-
throw new
|
|
108
|
+
throw new RPCNotFoundError(funcName);
|
|
99
109
|
}
|
|
100
110
|
return await this.rpc(funcName, data);
|
|
101
111
|
}
|
|
@@ -26,6 +26,10 @@ export interface GraphNodeDef<RPCMap extends Record<string, RPCHandler>, NodeIds
|
|
|
26
26
|
next?: NextConfig<NodeIds>;
|
|
27
27
|
/** Error handling - node(s) to execute on error */
|
|
28
28
|
onError?: NodeIds | NodeIds[];
|
|
29
|
+
/** Maximum retry attempts allowed (excludes the initial attempt) */
|
|
30
|
+
retries?: number;
|
|
31
|
+
/** Delay between retries — milliseconds, duration string, or 'exponential' */
|
|
32
|
+
retryDelay?: string | number;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* Helper to create a type-safe graph definition with RPC autocomplete.
|
|
@@ -87,6 +91,8 @@ type GraphNodeConfigMap<FuncMap extends Record<string, string>, RPCMap extends R
|
|
|
87
91
|
next?: NextConfig<Extract<keyof FuncMap, string>>;
|
|
88
92
|
input?: (ref: <N extends Extract<keyof FuncMap, string>, P extends keyof ComputeNodeOutputs<FuncMap, RPCMap>[N] & string>(nodeId: N, path: P) => TypedRef<ComputeNodeOutputs<FuncMap, RPCMap>[N][P]>) => InputWithRefs<ComputeNodeInputs<FuncMap, RPCMap>[K]>;
|
|
89
93
|
onError?: Extract<keyof FuncMap, string> | Extract<keyof FuncMap, string>[];
|
|
94
|
+
retries?: number;
|
|
95
|
+
retryDelay?: string | number;
|
|
90
96
|
};
|
|
91
97
|
};
|
|
92
98
|
/**
|
|
@@ -113,5 +119,7 @@ export declare function graph<NodeIds extends string = string>(nodes: Record<Nod
|
|
|
113
119
|
next?: NextConfig<NodeIds>;
|
|
114
120
|
input?: (ref: (nodeId: NodeIds, path: string) => RefValue) => Record<string, unknown>;
|
|
115
121
|
onError?: NodeIds | NodeIds[];
|
|
122
|
+
retries?: number;
|
|
123
|
+
retryDelay?: string | number;
|
|
116
124
|
}>): Record<NodeIds, GraphNodeConfig<NodeIds>>;
|
|
117
125
|
export {};
|
|
@@ -20,6 +20,8 @@ export function createGraph() {
|
|
|
20
20
|
input: def?.input,
|
|
21
21
|
next: def?.next,
|
|
22
22
|
onError: def?.onError,
|
|
23
|
+
retries: def?.retries,
|
|
24
|
+
retryDelay: def?.retryDelay,
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
return result;
|
|
@@ -52,6 +54,8 @@ export function graph(nodes) {
|
|
|
52
54
|
input: def.input,
|
|
53
55
|
next: def.next,
|
|
54
56
|
onError: def.onError,
|
|
57
|
+
retries: def.retries,
|
|
58
|
+
retryDelay: def.retryDelay,
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
61
|
return result;
|
|
@@ -257,11 +257,13 @@ function areDependenciesSatisfied(node, completedNodeIds) {
|
|
|
257
257
|
const deps = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
|
|
258
258
|
return deps.every((dep) => completedNodeIds.has(dep));
|
|
259
259
|
}
|
|
260
|
-
async function queueGraphNode(workflowService, runId, _graphName, nodeId, rpcName, input) {
|
|
261
|
-
|
|
262
|
-
retries: 0,
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
async function queueGraphNode(workflowService, runId, _graphName, nodeId, rpcName, input, nodeConfig) {
|
|
261
|
+
const stepOptions = {
|
|
262
|
+
retries: nodeConfig?.retries ?? 0,
|
|
263
|
+
retryDelay: nodeConfig?.retryDelay,
|
|
264
|
+
};
|
|
265
|
+
await workflowService.insertStepState(runId, nodeId, rpcName, input, stepOptions);
|
|
266
|
+
await workflowService.queueStepWorker(runId, nodeId, rpcName, input, stepOptions);
|
|
265
267
|
}
|
|
266
268
|
export async function continueGraph(workflowService, runId, graphName, overrideMeta) {
|
|
267
269
|
const meta = overrideMeta ?? getWorkflowMeta(graphName);
|
|
@@ -332,7 +334,7 @@ export async function continueGraph(workflowService, runId, graphName, overrideM
|
|
|
332
334
|
const fetchedResults = await workflowService.getNodeResults(runId, referencedNodeIds);
|
|
333
335
|
const nodeResults = { trigger: triggerInput, ...fetchedResults };
|
|
334
336
|
const resolvedInput = resolveSerializedInput(node.input, nodeResults);
|
|
335
|
-
await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput);
|
|
337
|
+
await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput, node);
|
|
336
338
|
}
|
|
337
339
|
}
|
|
338
340
|
export async function executeGraphStep(workflowService, rpcService, runId, stepId, nodeId, rpcName, data, graphName) {
|
|
@@ -410,7 +412,7 @@ export async function executeGraphStep(workflowService, rpcService, runId, stepI
|
|
|
410
412
|
for (const errorNodeId of errorNodes) {
|
|
411
413
|
const errorNode = meta.nodes[errorNodeId];
|
|
412
414
|
if (errorNode) {
|
|
413
|
-
await queueGraphNode(workflowService, runId, graphName, errorNodeId, errorNode.rpcName, { error: { message: error.message } });
|
|
415
|
+
await queueGraphNode(workflowService, runId, graphName, errorNodeId, errorNode.rpcName, { error: { message: error.message } }, errorNode);
|
|
414
416
|
}
|
|
415
417
|
}
|
|
416
418
|
throw error;
|
|
@@ -430,7 +432,7 @@ async function executeGraphNodeInline(workflowService, rpcService, runId, graphN
|
|
|
430
432
|
if (!node)
|
|
431
433
|
return;
|
|
432
434
|
const rpcName = node.rpcName;
|
|
433
|
-
const stepState = await workflowService.insertStepState(runId, nodeId, rpcName, input, { retries:
|
|
435
|
+
const stepState = await workflowService.insertStepState(runId, nodeId, rpcName, input, { retries: node.retries ?? 0, retryDelay: node.retryDelay });
|
|
434
436
|
await workflowService.setStepRunning(stepState.stepId);
|
|
435
437
|
const wireState = {};
|
|
436
438
|
const graphWire = {
|
|
@@ -486,7 +488,7 @@ async function executeGraphNodeInline(workflowService, rpcService, runId, graphN
|
|
|
486
488
|
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
487
489
|
code: 'RPC_NOT_FOUND',
|
|
488
490
|
});
|
|
489
|
-
|
|
491
|
+
throw new WorkflowSuspendedException(runId, 'RPC_NOT_FOUND');
|
|
490
492
|
}
|
|
491
493
|
await workflowService.setStepError(stepState.stepId, error);
|
|
492
494
|
if (node?.onError) {
|
|
@@ -627,7 +629,7 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
|
|
|
627
629
|
const resolvedInput = node.input && Object.keys(node.input).length > 0
|
|
628
630
|
? resolveSerializedInput(node.input, triggerNodeResults)
|
|
629
631
|
: triggerInput;
|
|
630
|
-
await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput);
|
|
632
|
+
await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput, node);
|
|
631
633
|
}
|
|
632
634
|
if (inline) {
|
|
633
635
|
workflowService.unregisterInlineRun(runId);
|
|
@@ -45,6 +45,10 @@ export interface GraphNodeConfig<NodeIds extends string = string> {
|
|
|
45
45
|
next?: NextConfig<NodeIds>;
|
|
46
46
|
/** Error routing - node(s) to execute on error */
|
|
47
47
|
onError?: NodeIds | NodeIds[];
|
|
48
|
+
/** Maximum retry attempts allowed (excludes the initial attempt) */
|
|
49
|
+
retries?: number;
|
|
50
|
+
/** Delay between retries — milliseconds, duration string, or 'exponential' */
|
|
51
|
+
retryDelay?: string | number;
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
50
54
|
* Graph wire context - available to functions running in a workflow graph
|
|
@@ -8,5 +8,5 @@ export { pikkuWorkflowGraph, type PikkuWorkflowGraphConfig, type PikkuWorkflowGr
|
|
|
8
8
|
export { validateWorkflowWiring, computeEntryNodeIds, } from './graph/graph-validation.js';
|
|
9
9
|
export { pikkuWorkflowWorkerFunc, pikkuWorkflowOrchestratorFunc, pikkuWorkflowSleeperFunc, } from './workflow-queue-workers.js';
|
|
10
10
|
export type { WorkflowStepInput as WorkflowStepQueueInput, PikkuWorkflowOrchestratorInput, PikkuWorkflowSleeperInput, } from './workflow-queue-workers.js';
|
|
11
|
-
export type { WorkflowService, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
|
|
11
|
+
export type { WorkflowService, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, WorkflowRunMirror, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
|
|
12
12
|
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow.types.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SerializedError } from '../../types/core.types.js';
|
|
2
|
-
import type { PikkuWorkflowWire, StepState, WorkflowPlannedStep, WorkflowRun, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
|
|
2
|
+
import type { PikkuWorkflowWire, StepState, WorkflowPlannedStep, WorkflowRun, WorkflowRunMirror, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
|
|
3
3
|
import type { WorkflowService } from '../../services/workflow-service.js';
|
|
4
4
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
5
5
|
/**
|
|
@@ -35,6 +35,12 @@ export declare class WorkflowNotFoundError extends PikkuError {
|
|
|
35
35
|
export declare class WorkflowRunNotFoundError extends PikkuError {
|
|
36
36
|
constructor(runId: string);
|
|
37
37
|
}
|
|
38
|
+
export declare class WorkflowRunFailedError extends PikkuError {
|
|
39
|
+
payload: {
|
|
40
|
+
message?: string;
|
|
41
|
+
};
|
|
42
|
+
constructor(message?: string);
|
|
43
|
+
}
|
|
38
44
|
export declare class WorkflowServiceNotInitialized extends Error {
|
|
39
45
|
}
|
|
40
46
|
export declare class WorkflowStepNameNotString extends Error {
|
|
@@ -48,7 +54,19 @@ export declare class WorkflowStepNameNotString extends Error {
|
|
|
48
54
|
export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
49
55
|
private inlineRuns;
|
|
50
56
|
protected get logger(): import("../../services/logger.js").Logger;
|
|
51
|
-
|
|
57
|
+
protected mirror?: WorkflowRunMirror;
|
|
58
|
+
constructor(options?: {
|
|
59
|
+
wireQueues?: boolean;
|
|
60
|
+
mirror?: WorkflowRunMirror;
|
|
61
|
+
});
|
|
62
|
+
private safeMirror;
|
|
63
|
+
rewireQueueWorkers(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Wire the queue-based orchestrator/step/sleeper workers.
|
|
66
|
+
* Subclasses that orchestrate without queues (e.g. Durable Objects) should
|
|
67
|
+
* pass `wireQueues: false` to the base constructor and skip this entirely.
|
|
68
|
+
*/
|
|
69
|
+
protected wireQueueWorkers(): void;
|
|
52
70
|
/**
|
|
53
71
|
* Check if a run is executing inline (without queues)
|
|
54
72
|
*/
|
|
@@ -62,7 +80,11 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
62
80
|
*/
|
|
63
81
|
unregisterInlineRun(runId: string): void;
|
|
64
82
|
registerWorkflowVersions(): Promise<void>;
|
|
65
|
-
|
|
83
|
+
createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
|
|
84
|
+
deterministic?: boolean;
|
|
85
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
86
|
+
}): Promise<string>;
|
|
87
|
+
protected abstract createRunImpl(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
|
|
66
88
|
deterministic?: boolean;
|
|
67
89
|
plannedSteps?: WorkflowPlannedStep[];
|
|
68
90
|
}): Promise<string>;
|
|
@@ -90,7 +112,8 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
90
112
|
* @param id - Run ID
|
|
91
113
|
* @param status - New status
|
|
92
114
|
*/
|
|
93
|
-
|
|
115
|
+
updateRunStatus(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
|
|
116
|
+
protected abstract updateRunStatusImpl(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
|
|
94
117
|
/**
|
|
95
118
|
* Insert initial step state (called by orchestrator)
|
|
96
119
|
* Creates pending step in both workflow_step and workflow_step_history
|
|
@@ -101,7 +124,8 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
101
124
|
* @param stepOptions - Step options (retries, retryDelay)
|
|
102
125
|
* @returns Step state with generated stepId
|
|
103
126
|
*/
|
|
104
|
-
|
|
127
|
+
insertStepState(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: WorkflowStepOptions): Promise<StepState>;
|
|
128
|
+
protected abstract insertStepStateImpl(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: WorkflowStepOptions): Promise<StepState>;
|
|
105
129
|
/**
|
|
106
130
|
* Get step state by cache key (read-only)
|
|
107
131
|
* @param runId - Run ID
|
|
@@ -114,33 +138,38 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
114
138
|
* Updates both workflow_step and workflow_step_history
|
|
115
139
|
* @param stepId - Step ID
|
|
116
140
|
*/
|
|
117
|
-
|
|
141
|
+
setStepRunning(stepId: string): Promise<void>;
|
|
142
|
+
protected abstract setStepRunningImpl(stepId: string): Promise<void>;
|
|
118
143
|
/**
|
|
119
144
|
* Mark step as scheduled (queued for execution)
|
|
120
145
|
* Updates both workflow_step and workflow_step_history
|
|
121
146
|
* @param stepId - Step ID
|
|
122
147
|
*/
|
|
123
|
-
|
|
148
|
+
setStepScheduled(stepId: string): Promise<void>;
|
|
149
|
+
protected abstract setStepScheduledImpl(stepId: string): Promise<void>;
|
|
124
150
|
/**
|
|
125
151
|
* Store step result and mark as succeeded
|
|
126
152
|
* Updates both workflow_step and workflow_step_history
|
|
127
153
|
* @param stepId - Step ID
|
|
128
154
|
* @param result - Step result
|
|
129
155
|
*/
|
|
130
|
-
|
|
156
|
+
setStepResult(stepId: string, result: any): Promise<void>;
|
|
157
|
+
protected abstract setStepResultImpl(stepId: string, result: any): Promise<void>;
|
|
131
158
|
/**
|
|
132
159
|
* Set the child workflow run ID on a step
|
|
133
160
|
* @param stepId - Step ID
|
|
134
161
|
* @param childRunId - Child workflow run ID
|
|
135
162
|
*/
|
|
136
|
-
|
|
163
|
+
setStepChildRunId(stepId: string, childRunId: string): Promise<void>;
|
|
164
|
+
protected abstract setStepChildRunIdImpl(stepId: string, childRunId: string): Promise<void>;
|
|
137
165
|
/**
|
|
138
166
|
* Store step error and mark as failed
|
|
139
167
|
* Updates both workflow_step and workflow_step_history
|
|
140
168
|
* @param stepId - Step ID
|
|
141
169
|
* @param error - Error object
|
|
142
170
|
*/
|
|
143
|
-
|
|
171
|
+
setStepError(stepId: string, error: Error): Promise<void>;
|
|
172
|
+
protected abstract setStepErrorImpl(stepId: string, error: Error): Promise<void>;
|
|
144
173
|
/**
|
|
145
174
|
* Create a new retry attempt for a failed step
|
|
146
175
|
* Inserts new pending step in both workflow_step and workflow_step_history
|
|
@@ -149,7 +178,8 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
149
178
|
* @param failedStepId - Failed step ID to copy from
|
|
150
179
|
* @returns New step state for the retry attempt
|
|
151
180
|
*/
|
|
152
|
-
|
|
181
|
+
createRetryAttempt(failedStepId: string, status: 'pending' | 'running'): Promise<StepState>;
|
|
182
|
+
protected abstract createRetryAttemptImpl(failedStepId: string, status: 'pending' | 'running'): Promise<StepState>;
|
|
153
183
|
/**
|
|
154
184
|
* Execute function within a run lock to prevent concurrent modifications
|
|
155
185
|
* @param id - Run ID
|
|
@@ -198,22 +228,26 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
198
228
|
* @param stepId - Step ID
|
|
199
229
|
* @param branchKey - Branch key selected by graph.branch()
|
|
200
230
|
*/
|
|
201
|
-
|
|
231
|
+
setBranchTaken(stepId: string, branchKey: string): Promise<void>;
|
|
232
|
+
protected abstract setBranchTakenImpl(stepId: string, branchKey: string): Promise<void>;
|
|
202
233
|
/**
|
|
203
234
|
* Update a state variable in the workflow run's state
|
|
204
235
|
* @param runId - Run ID
|
|
205
236
|
* @param name - Variable name
|
|
206
237
|
* @param value - Value to store
|
|
207
238
|
*/
|
|
208
|
-
|
|
239
|
+
updateRunState(runId: string, name: string, value: unknown): Promise<void>;
|
|
240
|
+
protected abstract updateRunStateImpl(runId: string, name: string, value: unknown): Promise<void>;
|
|
209
241
|
/**
|
|
210
242
|
* Get the entire state object for a workflow run
|
|
211
243
|
* @param runId - Run ID
|
|
212
244
|
* @returns The state object with all variables
|
|
213
245
|
*/
|
|
214
246
|
abstract getRunState(runId: string): Promise<Record<string, unknown>>;
|
|
215
|
-
|
|
216
|
-
abstract
|
|
247
|
+
upsertWorkflowVersion(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
|
|
248
|
+
protected abstract upsertWorkflowVersionImpl(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
|
|
249
|
+
updateWorkflowVersionStatus(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
|
|
250
|
+
protected abstract updateWorkflowVersionStatusImpl(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
|
|
217
251
|
abstract getWorkflowVersion(name: string, graphHash: string): Promise<{
|
|
218
252
|
graph: any;
|
|
219
253
|
source: string;
|
|
@@ -228,7 +262,7 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
228
262
|
* @param runId - Run ID
|
|
229
263
|
*/
|
|
230
264
|
resumeWorkflow(runId: string, workflowName?: string): Promise<void>;
|
|
231
|
-
queueStepWorker(runId: string, stepName: string, rpcName: string, data: any): Promise<void>;
|
|
265
|
+
queueStepWorker(runId: string, stepName: string, rpcName: string, data: any, stepOptions?: WorkflowStepOptions): Promise<void>;
|
|
232
266
|
/**
|
|
233
267
|
* Execute a workflow sleep step completion
|
|
234
268
|
* Sets the step result to null and resumes the workflow
|
|
@@ -241,6 +275,32 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
241
275
|
* @param retryDelay - Delay in milliseconds or duration string (optional)
|
|
242
276
|
*/
|
|
243
277
|
protected scheduleOrchestratorRetry(runId: string, retryDelay?: number | string, workflowName?: string): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Dispatch a workflow step to be executed asynchronously.
|
|
280
|
+
*
|
|
281
|
+
* Default implementation enqueues a step worker job via the queue service.
|
|
282
|
+
* Subclasses with non-queue transports (e.g. Durable Objects) override this
|
|
283
|
+
* to dispatch via their own mechanism (RPC to a step worker, etc.).
|
|
284
|
+
*
|
|
285
|
+
* On return, the workflow is paused via `WorkflowAsyncException` thrown by
|
|
286
|
+
* the caller; the step transport is responsible for calling back into the
|
|
287
|
+
* orchestrator (via `resumeWorkflow` or equivalent) when the step completes.
|
|
288
|
+
*
|
|
289
|
+
* @returns true if dispatch was async (caller should pause), false to fall
|
|
290
|
+
* through to the inline execution path.
|
|
291
|
+
*/
|
|
292
|
+
protected dispatchStep(runId: string, stepName: string, rpcName: string, data: unknown, stepOptions?: WorkflowStepOptions): Promise<boolean>;
|
|
293
|
+
/**
|
|
294
|
+
* Schedule a workflow sleep wakeup at the given duration.
|
|
295
|
+
*
|
|
296
|
+
* Default implementation uses the scheduler service to enqueue a delayed
|
|
297
|
+
* sleeper RPC. Subclasses with native timer primitives (e.g. Durable Object
|
|
298
|
+
* alarms) override this to schedule directly without going through queues.
|
|
299
|
+
*
|
|
300
|
+
* @returns true if the wakeup was scheduled remotely (caller should pause),
|
|
301
|
+
* false to fall through to inline `setTimeout` behavior.
|
|
302
|
+
*/
|
|
303
|
+
protected scheduleSleep(runId: string, stepId: string, duration: number | string): Promise<boolean>;
|
|
244
304
|
/**
|
|
245
305
|
* Start a new workflow run
|
|
246
306
|
* Automatically detects workflow type (DSL or graph) from meta and executes accordingly
|
|
@@ -284,6 +344,11 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
284
344
|
* Get the orchestrator queue name for a specific workflow.
|
|
285
345
|
* Checks queue meta for a per-workflow queue first (e.g. wf-orchestrator-{name}),
|
|
286
346
|
* falls back to the shared orchestrator queue.
|
|
347
|
+
*
|
|
348
|
+
* Reads from `queue.meta` (always populated globally) rather than
|
|
349
|
+
* `queue.registrations` (only populated for queues this unit consumes).
|
|
350
|
+
* In a per-unit deploy the orchestrator unit doesn't consume per-step
|
|
351
|
+
* queues — but it produces to them — so registrations would miss them.
|
|
287
352
|
*/
|
|
288
353
|
protected getOrchestratorQueueName(workflowName?: string): string;
|
|
289
354
|
protected getStepWorkerQueueName(rpcName?: string): string;
|