@pikku/core 0.12.150 → 0.12.151
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/dist/function/function-runner.js +23 -0
- package/dist/function/trace-queue-service.d.ts +26 -0
- package/dist/function/trace-queue-service.js +48 -0
- package/dist/mesh.d.ts +2 -2
- package/dist/mesh.js +3 -3
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/mesh-auth.d.ts +3 -2
- package/dist/middleware/mesh-auth.js +7 -2
- package/dist/middleware/remote-jobs-secret.d.ts +1 -1
- package/dist/middleware/require-origin.d.ts +19 -0
- package/dist/middleware/require-origin.js +36 -2
- package/dist/services/deployment-service.d.ts +1 -1
- package/dist/services/incoming-webhook-service.d.ts +4 -2
- package/dist/services/incoming-webhook-service.js +4 -3
- package/dist/services/queue-webhook-service.js +7 -1
- package/dist/services/remote-jobs.d.ts +6 -1
- package/dist/services/scheduler-service.d.ts +3 -1
- package/dist/services/webhook-service.d.ts +2 -0
- package/dist/services/workflow-service.d.ts +4 -1
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/queue-identity-transport-tests.d.ts +18 -0
- package/dist/testing/queue-identity-transport-tests.js +162 -0
- package/dist/testing/queue-trace-conformance.d.ts +26 -0
- package/dist/testing/queue-trace-conformance.js +259 -0
- package/dist/testing/service-tests/channel-store-tests.js +11 -0
- package/dist/testing/service-tests/queued-workflow-harness.d.ts +18 -1
- package/dist/testing/service-tests/queued-workflow-harness.js +19 -3
- package/dist/testing/service-tests/workflow-service-tests.js +12 -0
- package/dist/testing/trace-matrix/assertions.d.ts +3 -0
- package/dist/testing/trace-matrix/assertions.js +190 -0
- package/dist/testing/trace-matrix/chain-harness.d.ts +119 -0
- package/dist/testing/trace-matrix/chain-harness.js +777 -0
- package/dist/testing/trace-matrix/dimensions.d.ts +55 -0
- package/dist/testing/trace-matrix/dimensions.js +305 -0
- package/dist/testing/trace-matrix/pairwise.d.ts +20 -0
- package/dist/testing/trace-matrix/pairwise.js +75 -0
- package/dist/testing/trace-matrix/report.d.ts +1 -0
- package/dist/testing/trace-matrix/report.js +16 -0
- package/dist/testing/trace-matrix/transports.d.ts +19 -0
- package/dist/testing/trace-matrix/transports.js +27 -0
- package/dist/testing/trace-matrix/workflow-cases.d.ts +24 -0
- package/dist/testing/trace-matrix/workflow-cases.js +147 -0
- package/dist/trace.d.ts +66 -0
- package/dist/trace.js +97 -0
- package/dist/types/core.types.d.ts +6 -2
- package/dist/wirings/addon/addon-reach.d.ts +3 -0
- package/dist/wirings/addon/addon-reach.js +9 -0
- package/dist/wirings/addon/define-plugin.d.ts +6 -0
- package/dist/wirings/addon/wire-addon.js +5 -0
- package/dist/wirings/addon/wire-remote-addon.d.ts +11 -0
- package/dist/wirings/agent/agent-finalize.d.ts +2 -0
- package/dist/wirings/agent/agent-finalize.js +1 -1
- package/dist/wirings/agent/agent-prepare.d.ts +3 -0
- package/dist/wirings/agent/agent-prepare.js +24 -8
- package/dist/wirings/agent/agent-rpc.js +1 -0
- package/dist/wirings/agent/agent-runner.js +3 -0
- package/dist/wirings/agent/agent-stream.js +4 -0
- package/dist/wirings/agent/voice-input.d.ts +1 -1
- package/dist/wirings/agent/voice-output.d.ts +1 -1
- package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +4 -2
- package/dist/wirings/agent-scorer/agent-scorer-live.js +2 -2
- package/dist/wirings/channel/channel-common.d.ts +4 -1
- package/dist/wirings/channel/channel-common.js +3 -1
- package/dist/wirings/channel/channel-handler.js +2 -0
- package/dist/wirings/channel/channel-host-rpc.d.ts +1 -1
- package/dist/wirings/channel/channel-host-rpc.js +2 -2
- package/dist/wirings/channel/channel-rpc-responder.d.ts +7 -1
- package/dist/wirings/channel/channel-rpc-responder.js +2 -2
- package/dist/wirings/channel/channel-rpc-service.d.ts +1 -1
- package/dist/wirings/channel/channel-rpc-service.js +2 -2
- package/dist/wirings/channel/channel-rpc.types.d.ts +5 -2
- package/dist/wirings/channel/channel-rpc.types.js +1 -1
- package/dist/wirings/channel/channel-store.d.ts +4 -2
- package/dist/wirings/channel/channel-store.js +2 -2
- package/dist/wirings/channel/channel.types.d.ts +3 -0
- package/dist/wirings/channel/local/local-channel-runner.js +16 -5
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -0
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +3 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +27 -4
- package/dist/wirings/gateway/gateway-runner.js +3 -1
- package/dist/wirings/http/http-runner.d.ts +4 -4
- package/dist/wirings/http/http-runner.js +14 -18
- package/dist/wirings/http/http.types.d.ts +3 -3
- package/dist/wirings/mcp/mcp-runner.js +15 -5
- package/dist/wirings/queue/in-memory-queue-service.d.ts +1 -1
- package/dist/wirings/queue/in-memory-queue-service.js +8 -2
- package/dist/wirings/queue/index.d.ts +1 -0
- package/dist/wirings/queue/index.js +1 -0
- package/dist/wirings/queue/queue-runner.d.ts +3 -4
- package/dist/wirings/queue/queue-runner.js +8 -7
- package/dist/wirings/queue/queue-trace.d.ts +18 -0
- package/dist/wirings/queue/queue-trace.js +42 -0
- package/dist/wirings/queue/queue.types.d.ts +7 -0
- package/dist/wirings/queue/remote-queue-job.d.ts +1 -1
- package/dist/wirings/queue/remote-queue-job.js +5 -3
- package/dist/wirings/rpc/rpc-runner.d.ts +7 -1
- package/dist/wirings/rpc/rpc-runner.js +43 -21
- package/dist/wirings/rpc/rpc-types.d.ts +2 -0
- package/dist/wirings/scheduler/remote-scheduled-job.d.ts +1 -1
- package/dist/wirings/scheduler/remote-scheduled-job.js +2 -2
- package/dist/wirings/scheduler/scheduler-runner.d.ts +4 -4
- package/dist/wirings/scheduler/scheduler-runner.js +7 -6
- package/dist/wirings/trigger/pikku-trigger-service.js +7 -4
- package/dist/wirings/trigger/webhook-source-runner.d.ts +7 -1
- package/dist/wirings/trigger/webhook-source-runner.js +11 -3
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
- package/dist/wirings/workflow/graph/graph-runner.js +49 -30
- package/dist/wirings/workflow/index.d.ts +3 -1
- package/dist/wirings/workflow/index.js +1 -0
- package/dist/wirings/workflow/pikku-scenario-service.js +2 -0
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +37 -40
- package/dist/wirings/workflow/pikku-workflow-service.js +122 -133
- package/dist/wirings/workflow/workflow-child-step.d.ts +2 -2
- package/dist/wirings/workflow/workflow-child-step.js +11 -4
- package/dist/wirings/workflow/workflow-compensation.d.ts +6 -0
- package/dist/wirings/workflow/workflow-compensation.js +15 -1
- package/dist/wirings/workflow/workflow-constants.d.ts +13 -0
- package/dist/wirings/workflow/workflow-constants.js +13 -0
- package/dist/wirings/workflow/workflow-dsl-pass.d.ts +2 -0
- package/dist/wirings/workflow/workflow-dsl-pass.js +27 -11
- package/dist/wirings/workflow/workflow-recovery.d.ts +131 -3
- package/dist/wirings/workflow/workflow-recovery.js +233 -7
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +1 -0
- package/dist/wirings/workflow/workflow-run-mirror.d.ts +4 -0
- package/dist/wirings/workflow/workflow-run-mirror.js +16 -0
- package/dist/wirings/workflow/workflow-run-status.d.ts +2 -0
- package/dist/wirings/workflow/workflow-run-status.js +13 -0
- package/dist/wirings/workflow/workflow-run-trace.d.ts +22 -0
- package/dist/wirings/workflow/workflow-run-trace.js +24 -0
- package/dist/wirings/workflow/workflow-step-claim.d.ts +18 -0
- package/dist/wirings/workflow/workflow-step-claim.js +47 -0
- package/dist/wirings/workflow/workflow-step-retry.d.ts +6 -2
- package/dist/wirings/workflow/workflow-step-retry.js +8 -3
- package/dist/wirings/workflow/workflow-wire.d.ts +12 -0
- package/dist/wirings/workflow/workflow-wire.js +33 -0
- package/dist/wirings/workflow/workflow.types.d.ts +16 -0
- package/package.json +2 -1
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
import { DEFAULT_STEP_RETRIES, isStepLeaseLive } from './workflow-constants.js';
|
|
2
2
|
import { WorkflowStepFunctionMismatchError, WorkflowStepLeaseExpiredError, } from './workflow-errors.js';
|
|
3
|
+
import { deriveInvocationId } from './workflow-invocation-id.js';
|
|
4
|
+
import { parseTraceparent, tracedLogger } from '../../trace.js';
|
|
5
|
+
const warnLostStep = async (log, runId, stepName, attempt, message) => {
|
|
6
|
+
// The step has already moved on, so no failure here may reach the claim.
|
|
7
|
+
try {
|
|
8
|
+
const logger = log.logger();
|
|
9
|
+
if (!logger)
|
|
10
|
+
return;
|
|
11
|
+
const traceparent = await log.traceparent(runId).catch(() => undefined);
|
|
12
|
+
tracedLogger(logger, parseTraceparent(traceparent)?.traceId).warn({
|
|
13
|
+
message,
|
|
14
|
+
runId,
|
|
15
|
+
stepName,
|
|
16
|
+
attempt,
|
|
17
|
+
invocationId: deriveInvocationId(runId, stepName),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch { }
|
|
21
|
+
};
|
|
22
|
+
export const warnStepRerun = (log, runId, stepName, attempt) => warnLostStep(log, runId, stepName, attempt, `Workflow step '${stepName}' (run ${runId}) lost its worker and is running again as attempt ${attempt}`);
|
|
23
|
+
export const warnStepAttemptsExhausted = (log, runId, stepName, attempts) => warnLostStep(log, runId, stepName, attempts, `Workflow step '${stepName}' (run ${runId}) lost its worker on attempt ${attempts} with no attempts left, so it failed`);
|
|
3
24
|
/**
|
|
4
25
|
* Whether a step whose worker vanished may be handed to another one, or has
|
|
5
26
|
* run out of attempts to spend on that.
|
|
6
27
|
*/
|
|
7
28
|
export const leaseAttemptsExhausted = (stepState) => stepState.attemptCount >= (stepState.retries ?? DEFAULT_STEP_RETRIES) + 1;
|
|
29
|
+
/**
|
|
30
|
+
* A pass holds its run's lease for as long as it works a step inline, so one
|
|
31
|
+
* that meets its own step already `running` is looking at a pass that died
|
|
32
|
+
* mid-step. Running it again is a new attempt, counted as one so a step that
|
|
33
|
+
* takes its process down every time runs out instead of looping forever. The
|
|
34
|
+
* invocation id comes from the step's name, so the attempt keeps it.
|
|
35
|
+
*/
|
|
36
|
+
export const takeBackLostInlineStep = async (store, runId, stepName, stepState) => {
|
|
37
|
+
if (stepState.status !== 'running') {
|
|
38
|
+
return stepState;
|
|
39
|
+
}
|
|
40
|
+
if (leaseAttemptsExhausted(stepState)) {
|
|
41
|
+
const error = new WorkflowStepLeaseExpiredError(runId, stepName, stepState.attemptCount);
|
|
42
|
+
await store.setStepError(stepState.stepId, error, stepState.attemptCount);
|
|
43
|
+
await store.logStepAttemptsExhausted?.(runId, stepName, stepState.attemptCount);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
const attempt = await store.createRetryAttempt(stepState.stepId, 'pending');
|
|
47
|
+
await store.logStepRerun?.(runId, stepName, attempt.attemptCount);
|
|
48
|
+
return attempt;
|
|
49
|
+
};
|
|
8
50
|
/**
|
|
9
51
|
* Decide whether this dispatch owns the step, by reading its state and then
|
|
10
52
|
* writing it — which only excludes a concurrent dispatch when the caller holds
|
|
@@ -29,6 +71,7 @@ export const claimStepByReadThenWrite = async (store, runId, stepName, rpcName,
|
|
|
29
71
|
}
|
|
30
72
|
if (leaseAttemptsExhausted(stepState)) {
|
|
31
73
|
await store.setStepError(stepState.stepId, new WorkflowStepLeaseExpiredError(runId, stepName, stepState.attemptCount), stepState.attemptCount);
|
|
74
|
+
await store.logStepAttemptsExhausted?.(runId, stepName, stepState.attemptCount);
|
|
32
75
|
await store.resumeWorkflow?.(runId);
|
|
33
76
|
return null;
|
|
34
77
|
}
|
|
@@ -41,6 +84,10 @@ export const claimStepByReadThenWrite = async (store, runId, stepName, rpcName,
|
|
|
41
84
|
if (stepState.status === 'failed' || stepState.status === 'running') {
|
|
42
85
|
const attempt = await store.createRetryAttempt(stepState.stepId, 'running');
|
|
43
86
|
await store.refreshStepLease(attempt.stepId, leaseMs);
|
|
87
|
+
// A `failed` step threw, and this is its ordinary retry.
|
|
88
|
+
if (stepState.status === 'running') {
|
|
89
|
+
await store.logStepRerun?.(runId, stepName, attempt.attemptCount);
|
|
90
|
+
}
|
|
44
91
|
return attempt;
|
|
45
92
|
}
|
|
46
93
|
if (stepState.status === 'pending' || stepState.status === 'scheduled') {
|
|
@@ -9,6 +9,10 @@ export type StepRetryStore = {
|
|
|
9
9
|
/**
|
|
10
10
|
* Spend a step's retry budget here and now, rather than by handing the run back
|
|
11
11
|
* to the orchestrator between attempts. For an inline run there is nothing to
|
|
12
|
-
* hand it back to.
|
|
12
|
+
* hand it back to. Nothing ends the run between attempts either, so a forward
|
|
13
|
+
* step passes `beforeAttempt` to stop once the run has been cancelled.
|
|
14
|
+
*
|
|
15
|
+
* `retries` counts what follows the first attempt, as it does on the queue, so
|
|
16
|
+
* a step gets `retries + 1` attempts.
|
|
13
17
|
*/
|
|
14
|
-
export declare const runInlineRetryLoop: (store: StepRetryStore, stepState: StepState, retries: number, retryDelay: WorkflowStepOptions['retryDelay'], doWork: (currentStepState: StepState) => Promise<any>, onError?: (error: any) => Promise<void>) => Promise<any>;
|
|
18
|
+
export declare const runInlineRetryLoop: (store: StepRetryStore, stepState: StepState, retries: number, retryDelay: WorkflowStepOptions['retryDelay'], doWork: (currentStepState: StepState) => Promise<any>, onError?: (error: any) => Promise<void>, beforeAttempt?: () => Promise<void>) => Promise<any>;
|
|
@@ -2,11 +2,16 @@ import { getDurationInMilliseconds } from '../../time-utils.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Spend a step's retry budget here and now, rather than by handing the run back
|
|
4
4
|
* to the orchestrator between attempts. For an inline run there is nothing to
|
|
5
|
-
* hand it back to.
|
|
5
|
+
* hand it back to. Nothing ends the run between attempts either, so a forward
|
|
6
|
+
* step passes `beforeAttempt` to stop once the run has been cancelled.
|
|
7
|
+
*
|
|
8
|
+
* `retries` counts what follows the first attempt, as it does on the queue, so
|
|
9
|
+
* a step gets `retries + 1` attempts.
|
|
6
10
|
*/
|
|
7
|
-
export const runInlineRetryLoop = async (store, stepState, retries, retryDelay, doWork, onError) => {
|
|
11
|
+
export const runInlineRetryLoop = async (store, stepState, retries, retryDelay, doWork, onError, beforeAttempt) => {
|
|
8
12
|
let currentStepState = stepState;
|
|
9
13
|
while (true) {
|
|
14
|
+
await beforeAttempt?.();
|
|
10
15
|
let result;
|
|
11
16
|
try {
|
|
12
17
|
await store.setStepRunning(currentStepState.stepId);
|
|
@@ -16,7 +21,7 @@ export const runInlineRetryLoop = async (store, stepState, retries, retryDelay,
|
|
|
16
21
|
if (onError)
|
|
17
22
|
await onError(error);
|
|
18
23
|
await store.setStepError(currentStepState.stepId, error);
|
|
19
|
-
if (currentStepState.attemptCount
|
|
24
|
+
if (currentStepState.attemptCount > retries) {
|
|
20
25
|
throw error;
|
|
21
26
|
}
|
|
22
27
|
currentStepState = await store.createRetryAttempt(currentStepState.stepId, 'pending');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PikkuWorkflowWire, WorkflowApprovalOptions, WorkflowRun } from './workflow.types.js';
|
|
2
|
+
/** The steps a wire drives, already bound to its run. */
|
|
3
|
+
export type WorkflowWireSteps = {
|
|
4
|
+
getRun: () => Promise<WorkflowRun | null | undefined>;
|
|
5
|
+
verifyStepName: (stepName: string) => void;
|
|
6
|
+
rpcStep: (stepName: string, rpcName: string, data: any, options?: any) => Promise<any>;
|
|
7
|
+
inlineStep: (stepName: string, fn: any, options?: any) => Promise<any>;
|
|
8
|
+
sleepStep: (stepName: string, ms: number) => Promise<void>;
|
|
9
|
+
suspendStep: (reason: string) => Promise<void>;
|
|
10
|
+
approvalStep: (reason: string, options: WorkflowApprovalOptions) => Promise<any>;
|
|
11
|
+
};
|
|
12
|
+
export declare const buildWorkflowWire: (name: string, runId: string, addonNamespace: string | null | undefined, steps: WorkflowWireSteps) => PikkuWorkflowWire;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getDurationInMilliseconds } from '../../time-utils.js';
|
|
2
|
+
import { milestoneStepName } from './workflow-constants.js';
|
|
3
|
+
export const buildWorkflowWire = (name, runId, addonNamespace, steps) => ({
|
|
4
|
+
name,
|
|
5
|
+
runId,
|
|
6
|
+
getRun: async () => (await steps.getRun()),
|
|
7
|
+
do: async (stepName, rpcNameOrFn, dataOrOptions, options) => {
|
|
8
|
+
steps.verifyStepName(stepName);
|
|
9
|
+
if (typeof rpcNameOrFn === 'string') {
|
|
10
|
+
const resolvedRpcName = addonNamespace && !rpcNameOrFn.includes(':')
|
|
11
|
+
? `${addonNamespace}:${rpcNameOrFn}`
|
|
12
|
+
: rpcNameOrFn;
|
|
13
|
+
return await steps.rpcStep(stepName, resolvedRpcName, dataOrOptions, options);
|
|
14
|
+
}
|
|
15
|
+
return await steps.inlineStep(stepName, rpcNameOrFn, dataOrOptions);
|
|
16
|
+
},
|
|
17
|
+
sleep: async (stepName, duration) => {
|
|
18
|
+
steps.verifyStepName(stepName);
|
|
19
|
+
await steps.sleepStep(stepName, getDurationInMilliseconds(duration));
|
|
20
|
+
},
|
|
21
|
+
suspend: async (reason) => {
|
|
22
|
+
steps.verifyStepName(reason);
|
|
23
|
+
await steps.suspendStep(reason);
|
|
24
|
+
},
|
|
25
|
+
milestone: async (name) => {
|
|
26
|
+
steps.verifyStepName(name);
|
|
27
|
+
await steps.inlineStep(milestoneStepName(name), async () => ({ name }));
|
|
28
|
+
},
|
|
29
|
+
approval: (async (reason, options) => {
|
|
30
|
+
steps.verifyStepName(reason);
|
|
31
|
+
return await steps.approvalStep(reason, options);
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
@@ -16,6 +16,8 @@ export interface WorkflowRunWire {
|
|
|
16
16
|
parentRunId?: string;
|
|
17
17
|
parentStepId?: string;
|
|
18
18
|
pikkuUserId?: string;
|
|
19
|
+
/** The W3C trace every hop of the run stays on. Set when the run is created. */
|
|
20
|
+
traceparent?: string;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* The resolved `workflow` section of the app config: default retries and the queue names the engine uses.
|
|
@@ -26,6 +28,20 @@ export interface WorkflowServiceConfig {
|
|
|
26
28
|
orchestratorQueueName: string;
|
|
27
29
|
stepWorkerQueueName: string;
|
|
28
30
|
sleeperRPCName: string;
|
|
31
|
+
/**
|
|
32
|
+
* How `pikku dev` and `pikku serve` sweep for work a dead process left
|
|
33
|
+
* behind. They do by default; `false` turns it off.
|
|
34
|
+
*/
|
|
35
|
+
recovery?: WorkflowRecoveryOptions | false;
|
|
36
|
+
}
|
|
37
|
+
/** Tuning for `startRecovery`. */
|
|
38
|
+
export interface WorkflowRecoveryOptions {
|
|
39
|
+
/** How often to sweep after the startup pass. Default 30s. */
|
|
40
|
+
intervalMs?: number;
|
|
41
|
+
/** Idle time before a `running` run with nothing in flight is re-driven. Default 30s. */
|
|
42
|
+
stalledAfterMs?: number;
|
|
43
|
+
/** Age at which a dispatched step's job is presumed lost. Default 30s. */
|
|
44
|
+
undispatchedAfterMs?: number;
|
|
29
45
|
}
|
|
30
46
|
/**
|
|
31
47
|
* How a workflow service routes its jobs. `per-workflow` (the default) uses a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.151",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"./oauth2": "./dist/wirings/oauth2/index.js",
|
|
77
77
|
"./errors": "./dist/errors/index.js",
|
|
78
78
|
"./services": "./dist/services/index.js",
|
|
79
|
+
"./trace": "./dist/trace.js",
|
|
79
80
|
"./flag": "./dist/wirings/flag/index.js",
|
|
80
81
|
"./services/local-meta": "./dist/services/meta-service.js",
|
|
81
82
|
"./services/in-memory-queue": "./dist/wirings/queue/in-memory-queue-service.js",
|