@sapiom/orchestration-core 0.1.1 → 0.3.0
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 +42 -0
- package/dist/cjs/index.d.ts +29 -29
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/inspect.d.ts +18 -1
- package/dist/cjs/inspect.js +39 -1
- package/dist/cjs/local/dispatcher.d.ts +7 -5
- package/dist/cjs/local/dispatcher.js +49 -15
- package/dist/cjs/local/run-local.d.ts +10 -4
- package/dist/cjs/local/run-local.js +31 -9
- package/dist/cjs/scaffold.d.ts +1 -0
- package/dist/cjs/scaffold.js +52 -22
- package/dist/esm/index.d.ts +29 -29
- package/dist/esm/index.js +15 -15
- package/dist/esm/inspect.d.ts +18 -1
- package/dist/esm/inspect.js +37 -1
- package/dist/esm/local/dispatcher.d.ts +7 -5
- package/dist/esm/local/dispatcher.js +52 -18
- package/dist/esm/local/run-local.d.ts +10 -4
- package/dist/esm/local/run-local.js +38 -16
- package/dist/esm/scaffold.d.ts +1 -0
- package/dist/esm/scaffold.js +56 -26
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/templates/coding-pause/AGENTS.md +48 -0
- package/templates/coding-pause/CLAUDE.md +7 -0
- package/templates/coding-pause/README.md +53 -0
- package/templates/coding-pause/_gitignore +3 -0
- package/templates/coding-pause/index.ts +109 -0
- package/templates/coding-pause/package.json +21 -0
- package/templates/coding-pause/tsconfig.json +12 -0
- package/templates/default/AGENTS.md +21 -2
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
export { OrchestrationError } from
|
|
2
|
-
export type { StructuredError } from
|
|
3
|
-
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST } from
|
|
4
|
-
export type { ClientOptions, GatewayErrorBody } from
|
|
5
|
-
export { readConfig, requireConfig, writeConfig, CONFIG_FILE } from
|
|
6
|
-
export type { SapiomConfig } from
|
|
7
|
-
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE } from
|
|
8
|
-
export type { ScaffoldOptions, ScaffoldResult, ResolvedVersions } from
|
|
9
|
-
export { check } from
|
|
10
|
-
export type { CheckOptions, CheckResult } from
|
|
11
|
-
export { link } from
|
|
12
|
-
export type { LinkOptions, LinkResult, DefinitionSummary } from
|
|
13
|
-
export { deploy } from
|
|
14
|
-
export type { DeployOptions, DeployResult } from
|
|
15
|
-
export { run, parseJsonInput } from
|
|
16
|
-
export type { RunOptions, RunResult } from
|
|
17
|
-
export { inspect, listExecutions, inspectBuild } from
|
|
18
|
-
export type { InspectOptions, InspectResult, ListExecutionsResult, InspectBuildOptions, InspectBuildResult, ExecutionDetail, StepRecord, BuildDetail, } from
|
|
19
|
-
export { signal, parseSignalPayload } from
|
|
20
|
-
export type { SignalOptions, SignalResult } from
|
|
21
|
-
export { assertDeployable, pushHead } from
|
|
22
|
-
export { parseStubFile, STUB_FILE_VERSION } from
|
|
23
|
-
export type { StubFile, StepStubs, StubResponse } from
|
|
24
|
-
export { runLocal, runLocalFromDir, STUBS_FILE } from
|
|
25
|
-
export type { RunLocalOptions, LocalRunResult, LocalRunOutcome } from
|
|
26
|
-
export { loadDefinition } from
|
|
27
|
-
export type { LoadedDefinition } from
|
|
28
|
-
export { LocalStubDispatcher } from
|
|
29
|
-
export type { LocalStepTrace, LogEntry } from
|
|
1
|
+
export { OrchestrationError } from "./errors.js";
|
|
2
|
+
export type { StructuredError } from "./errors.js";
|
|
3
|
+
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST, } from "./client.js";
|
|
4
|
+
export type { ClientOptions, GatewayErrorBody } from "./client.js";
|
|
5
|
+
export { readConfig, requireConfig, writeConfig, CONFIG_FILE, } from "./config.js";
|
|
6
|
+
export type { SapiomConfig } from "./config.js";
|
|
7
|
+
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE, } from "./scaffold.js";
|
|
8
|
+
export type { ScaffoldOptions, ScaffoldResult, ResolvedVersions, } from "./scaffold.js";
|
|
9
|
+
export { check } from "./check.js";
|
|
10
|
+
export type { CheckOptions, CheckResult } from "./check.js";
|
|
11
|
+
export { link } from "./link.js";
|
|
12
|
+
export type { LinkOptions, LinkResult, DefinitionSummary } from "./link.js";
|
|
13
|
+
export { deploy } from "./deploy.js";
|
|
14
|
+
export type { DeployOptions, DeployResult } from "./deploy.js";
|
|
15
|
+
export { run, parseJsonInput } from "./run.js";
|
|
16
|
+
export type { RunOptions, RunResult } from "./run.js";
|
|
17
|
+
export { inspect, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js";
|
|
18
|
+
export type { InspectOptions, InspectResult, ListExecutionsResult, InspectBuildOptions, InspectBuildResult, ExecutionDetail, StepRecord, BuildDetail, WaitForExecutionOptions, WaitForExecutionResult, WaitStopReason, } from "./inspect.js";
|
|
19
|
+
export { signal, parseSignalPayload } from "./signal.js";
|
|
20
|
+
export type { SignalOptions, SignalResult } from "./signal.js";
|
|
21
|
+
export { assertDeployable, pushHead } from "./git.js";
|
|
22
|
+
export { parseStubFile, STUB_FILE_VERSION } from "./local/stubs.js";
|
|
23
|
+
export type { StubFile, StepStubs, StubResponse } from "./local/stubs.js";
|
|
24
|
+
export { runLocal, runLocalFromDir, STUBS_FILE } from "./local/run-local.js";
|
|
25
|
+
export type { RunLocalOptions, LocalRunResult, LocalRunOutcome, } from "./local/run-local.js";
|
|
26
|
+
export { loadDefinition } from "./local/load.js";
|
|
27
|
+
export type { LoadedDefinition } from "./local/load.js";
|
|
28
|
+
export { LocalStubDispatcher } from "./local/dispatcher.js";
|
|
29
|
+
export type { LocalStepTrace, LogEntry } from "./local/dispatcher.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export { OrchestrationError } from
|
|
2
|
-
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST } from
|
|
3
|
-
export { readConfig, requireConfig, writeConfig, CONFIG_FILE } from
|
|
4
|
-
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE } from
|
|
5
|
-
export { check } from
|
|
6
|
-
export { link } from
|
|
7
|
-
export { deploy } from
|
|
8
|
-
export { run, parseJsonInput } from
|
|
9
|
-
export { inspect, listExecutions, inspectBuild } from
|
|
10
|
-
export { signal, parseSignalPayload } from
|
|
11
|
-
export { assertDeployable, pushHead } from
|
|
12
|
-
export { parseStubFile, STUB_FILE_VERSION } from
|
|
13
|
-
export { runLocal, runLocalFromDir, STUBS_FILE } from
|
|
14
|
-
export { loadDefinition } from
|
|
15
|
-
export { LocalStubDispatcher } from
|
|
1
|
+
export { OrchestrationError } from "./errors.js";
|
|
2
|
+
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST, } from "./client.js";
|
|
3
|
+
export { readConfig, requireConfig, writeConfig, CONFIG_FILE, } from "./config.js";
|
|
4
|
+
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE, } from "./scaffold.js";
|
|
5
|
+
export { check } from "./check.js";
|
|
6
|
+
export { link } from "./link.js";
|
|
7
|
+
export { deploy } from "./deploy.js";
|
|
8
|
+
export { run, parseJsonInput } from "./run.js";
|
|
9
|
+
export { inspect, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js";
|
|
10
|
+
export { signal, parseSignalPayload } from "./signal.js";
|
|
11
|
+
export { assertDeployable, pushHead } from "./git.js";
|
|
12
|
+
export { parseStubFile, STUB_FILE_VERSION } from "./local/stubs.js";
|
|
13
|
+
export { runLocal, runLocalFromDir, STUBS_FILE } from "./local/run-local.js";
|
|
14
|
+
export { loadDefinition } from "./local/load.js";
|
|
15
|
+
export { LocalStubDispatcher } from "./local/dispatcher.js";
|
package/dist/esm/inspect.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GatewayClient } from
|
|
1
|
+
import { GatewayClient } from "./client.js";
|
|
2
2
|
export interface StepRecord {
|
|
3
3
|
stepName: string;
|
|
4
4
|
attempt: number;
|
|
@@ -12,6 +12,7 @@ export interface ExecutionDetail {
|
|
|
12
12
|
id: string;
|
|
13
13
|
status: string;
|
|
14
14
|
currentStep?: string | null;
|
|
15
|
+
pausedSignalName?: string | null;
|
|
15
16
|
error?: unknown;
|
|
16
17
|
steps?: StepRecord[];
|
|
17
18
|
}
|
|
@@ -27,6 +28,22 @@ export interface InspectResult {
|
|
|
27
28
|
execution: ExecutionDetail;
|
|
28
29
|
}
|
|
29
30
|
export declare function inspect(opts: InspectOptions, client: GatewayClient): Promise<InspectResult>;
|
|
31
|
+
export declare function isExecutionTerminal(status: string): boolean;
|
|
32
|
+
export type WaitStopReason = "terminal" | "needs-signal" | "timeout";
|
|
33
|
+
export interface WaitForExecutionOptions {
|
|
34
|
+
executionId: string;
|
|
35
|
+
maxWaitMs?: number;
|
|
36
|
+
pollMs?: number;
|
|
37
|
+
autoResumeSignals?: string[];
|
|
38
|
+
sleep?: (ms: number) => Promise<void>;
|
|
39
|
+
now?: () => number;
|
|
40
|
+
}
|
|
41
|
+
export interface WaitForExecutionResult {
|
|
42
|
+
execution: ExecutionDetail;
|
|
43
|
+
reason: WaitStopReason;
|
|
44
|
+
done: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function waitForExecution(opts: WaitForExecutionOptions, client: GatewayClient): Promise<WaitForExecutionResult>;
|
|
30
47
|
export interface ListExecutionsResult {
|
|
31
48
|
executions: ExecutionDetail[];
|
|
32
49
|
}
|
package/dist/esm/inspect.js
CHANGED
|
@@ -2,8 +2,44 @@ export async function inspect(opts, client) {
|
|
|
2
2
|
const execution = await client.get(`/executions/${opts.executionId}`);
|
|
3
3
|
return { execution };
|
|
4
4
|
}
|
|
5
|
+
const TERMINAL_STATUSES = new Set([
|
|
6
|
+
"completed",
|
|
7
|
+
"failed",
|
|
8
|
+
"cancelled",
|
|
9
|
+
"canceled",
|
|
10
|
+
]);
|
|
11
|
+
export function isExecutionTerminal(status) {
|
|
12
|
+
return TERMINAL_STATUSES.has(status);
|
|
13
|
+
}
|
|
14
|
+
const AUTO_RESUME_PAUSE_SIGNALS = ["agent.coding.result"];
|
|
15
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
export async function waitForExecution(opts, client) {
|
|
17
|
+
const maxWaitMs = opts.maxWaitMs ?? 45000;
|
|
18
|
+
const autoResume = opts.autoResumeSignals ?? AUTO_RESUME_PAUSE_SIGNALS;
|
|
19
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
20
|
+
const now = opts.now ?? Date.now;
|
|
21
|
+
const deadline = now() + maxWaitMs;
|
|
22
|
+
let interval = opts.pollMs ?? 1000;
|
|
23
|
+
for (;;) {
|
|
24
|
+
const { execution } = await inspect({ executionId: opts.executionId }, client);
|
|
25
|
+
if (isExecutionTerminal(execution.status)) {
|
|
26
|
+
return { execution, reason: "terminal", done: true };
|
|
27
|
+
}
|
|
28
|
+
if (execution.status === "paused") {
|
|
29
|
+
const signal = execution.pausedSignalName ?? null;
|
|
30
|
+
if (!signal || !autoResume.includes(signal)) {
|
|
31
|
+
return { execution, reason: "needs-signal", done: false };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const remaining = deadline - now();
|
|
35
|
+
if (remaining <= 0)
|
|
36
|
+
return { execution, reason: "timeout", done: false };
|
|
37
|
+
await sleep(Math.min(interval, remaining));
|
|
38
|
+
interval = Math.min(interval * 1.5, 5000);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
5
41
|
export async function listExecutions(client) {
|
|
6
|
-
const executions = await client.get(
|
|
42
|
+
const executions = await client.get("/executions");
|
|
7
43
|
return { executions };
|
|
8
44
|
}
|
|
9
45
|
export async function inspectBuild(opts, client) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type NextStepDirective, type OrchestrationDefinition } from
|
|
2
|
-
import { type StepDispatcher, type StepDispatchRequest, type WorkflowRunnerCore } from
|
|
3
|
-
import type { StubFile } from
|
|
1
|
+
import { type NextStepDirective, type OrchestrationDefinition } from "@sapiom/orchestration";
|
|
2
|
+
import { type StepDispatcher, type StepDispatchRequest, type WorkflowRunnerCore } from "@sapiom/orchestration-runtime";
|
|
3
|
+
import type { StubFile } from "./stubs.js";
|
|
4
4
|
export interface LogEntry {
|
|
5
|
-
level:
|
|
5
|
+
level: "info" | "warn" | "error" | "debug";
|
|
6
6
|
msg: string;
|
|
7
7
|
meta?: Record<string, unknown>;
|
|
8
8
|
}
|
|
@@ -10,7 +10,7 @@ export interface LocalStepTrace {
|
|
|
10
10
|
step: string;
|
|
11
11
|
attempt: number;
|
|
12
12
|
input: unknown;
|
|
13
|
-
status:
|
|
13
|
+
status: "succeeded" | "threw";
|
|
14
14
|
output?: unknown;
|
|
15
15
|
directive?: NextStepDirective;
|
|
16
16
|
error?: {
|
|
@@ -27,6 +27,8 @@ export declare class LocalStubDispatcher implements StepDispatcher {
|
|
|
27
27
|
private maxAttemptsPerStep;
|
|
28
28
|
private signals;
|
|
29
29
|
readonly trace: LocalStepTrace[];
|
|
30
|
+
readonly usedKeysByStep: Map<string, Set<string>>;
|
|
31
|
+
readonly stubWarnings: Set<string>;
|
|
30
32
|
constructor(definition: OrchestrationDefinition, stubs: StubFile);
|
|
31
33
|
setCore(core: WorkflowRunnerCore): void;
|
|
32
34
|
setMaxAttempts(max: number): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { InMemoryContextStore, } from
|
|
2
|
-
import { parseCorrelationId, STEP_COMPLETION_OUTCOME, } from
|
|
3
|
-
import { createStubClient } from
|
|
1
|
+
import { InMemoryContextStore, } from "@sapiom/orchestration";
|
|
2
|
+
import { parseCorrelationId, STEP_COMPLETION_OUTCOME, } from "@sapiom/orchestration-runtime";
|
|
3
|
+
import { createStubClient } from "@sapiom/tools/stub";
|
|
4
4
|
export class LocalStubDispatcher {
|
|
5
5
|
constructor(definition, stubs) {
|
|
6
6
|
this.definition = definition;
|
|
@@ -8,6 +8,8 @@ export class LocalStubDispatcher {
|
|
|
8
8
|
this.core = null;
|
|
9
9
|
this.signals = new Map();
|
|
10
10
|
this.trace = [];
|
|
11
|
+
this.usedKeysByStep = new Map();
|
|
12
|
+
this.stubWarnings = new Set();
|
|
11
13
|
}
|
|
12
14
|
setCore(core) {
|
|
13
15
|
this.core = core;
|
|
@@ -20,7 +22,7 @@ export class LocalStubDispatcher {
|
|
|
20
22
|
}
|
|
21
23
|
async dispatch(request) {
|
|
22
24
|
if (!this.core)
|
|
23
|
-
throw new Error(
|
|
25
|
+
throw new Error("LocalStubDispatcher: setCore() was not called");
|
|
24
26
|
const step = this.definition.steps[request.stepName];
|
|
25
27
|
if (!step)
|
|
26
28
|
throw new Error(`LocalStubDispatcher: no step '${request.stepName}' in the definition`);
|
|
@@ -30,7 +32,17 @@ export class LocalStubDispatcher {
|
|
|
30
32
|
const logs = [];
|
|
31
33
|
const sharedStore = new InMemoryContextStore(request.shared);
|
|
32
34
|
const overrides = (this.stubs.steps[request.stepName] ?? {});
|
|
33
|
-
|
|
35
|
+
let usedKeys = this.usedKeysByStep.get(request.stepName);
|
|
36
|
+
if (!usedKeys) {
|
|
37
|
+
usedKeys = new Set();
|
|
38
|
+
this.usedKeysByStep.set(request.stepName, usedKeys);
|
|
39
|
+
}
|
|
40
|
+
const sapiom = createStubClient({
|
|
41
|
+
overrides,
|
|
42
|
+
signals: this.signals,
|
|
43
|
+
usedKeys,
|
|
44
|
+
warnings: this.stubWarnings,
|
|
45
|
+
});
|
|
34
46
|
const ctx = {
|
|
35
47
|
executionId: request.executionId,
|
|
36
48
|
workflowName: request.workflowName,
|
|
@@ -53,7 +65,7 @@ export class LocalStubDispatcher {
|
|
|
53
65
|
step: request.stepName,
|
|
54
66
|
attempt: request.attempt,
|
|
55
67
|
input: request.input,
|
|
56
|
-
status:
|
|
68
|
+
status: "threw",
|
|
57
69
|
error: { name: e.name, message: e.message, stack: e.stack },
|
|
58
70
|
logs,
|
|
59
71
|
});
|
|
@@ -71,7 +83,7 @@ export class LocalStubDispatcher {
|
|
|
71
83
|
step: request.stepName,
|
|
72
84
|
attempt: request.attempt,
|
|
73
85
|
input: request.input,
|
|
74
|
-
status:
|
|
86
|
+
status: "succeeded",
|
|
75
87
|
output,
|
|
76
88
|
directive,
|
|
77
89
|
logs,
|
|
@@ -90,22 +102,44 @@ function makeLogger(sink) {
|
|
|
90
102
|
const at = (level) => (msg, meta) => {
|
|
91
103
|
sink.push({ level, msg, ...(meta ? { meta } : {}) });
|
|
92
104
|
};
|
|
93
|
-
return {
|
|
105
|
+
return {
|
|
106
|
+
info: at("info"),
|
|
107
|
+
warn: at("warn"),
|
|
108
|
+
error: at("error"),
|
|
109
|
+
debug: at("debug"),
|
|
110
|
+
};
|
|
94
111
|
}
|
|
95
112
|
function splitDirective(d) {
|
|
96
113
|
switch (d.kind) {
|
|
97
|
-
case
|
|
98
|
-
return {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
case 'pause_until_signal':
|
|
114
|
+
case "continue":
|
|
115
|
+
return {
|
|
116
|
+
output: d.input,
|
|
117
|
+
wire: { kind: "continue", stepName: d.stepName, input: d.input },
|
|
118
|
+
};
|
|
119
|
+
case "terminate":
|
|
104
120
|
return {
|
|
105
121
|
output: d.output,
|
|
106
|
-
wire: { kind:
|
|
122
|
+
wire: { kind: "terminate", reason: d.reason },
|
|
123
|
+
};
|
|
124
|
+
case "fail":
|
|
125
|
+
return {
|
|
126
|
+
output: d.output,
|
|
127
|
+
wire: { kind: "fail", reason: d.reason },
|
|
128
|
+
};
|
|
129
|
+
case "pause_until_signal":
|
|
130
|
+
return {
|
|
131
|
+
output: d.output,
|
|
132
|
+
wire: {
|
|
133
|
+
kind: "pause_until_signal",
|
|
134
|
+
signal: d.signal,
|
|
135
|
+
timeoutMs: d.timeoutMs,
|
|
136
|
+
resumeStep: d.resumeStep,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
case "retry":
|
|
140
|
+
return {
|
|
141
|
+
output: undefined,
|
|
142
|
+
wire: { kind: "retry", delayMs: d.delayMs, reason: d.reason },
|
|
107
143
|
};
|
|
108
|
-
case 'retry':
|
|
109
|
-
return { output: undefined, wire: { kind: 'retry', delayMs: d.delayMs, reason: d.reason } };
|
|
110
144
|
}
|
|
111
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OrchestrationDefinition, WorkflowManifest } from
|
|
2
|
-
import { type LocalStepTrace } from
|
|
3
|
-
import { type StubFile } from
|
|
1
|
+
import type { OrchestrationDefinition, WorkflowManifest } from "@sapiom/orchestration";
|
|
2
|
+
import { type LocalStepTrace } from "./dispatcher.js";
|
|
3
|
+
import { type StubFile } from "./stubs.js";
|
|
4
4
|
export declare const STUBS_FILE: string;
|
|
5
5
|
export interface RunLocalOptions {
|
|
6
6
|
definition: OrchestrationDefinition;
|
|
@@ -9,13 +9,19 @@ export interface RunLocalOptions {
|
|
|
9
9
|
stubs?: StubFile;
|
|
10
10
|
maxAttemptsPerStep?: number;
|
|
11
11
|
}
|
|
12
|
-
export type LocalRunOutcome =
|
|
12
|
+
export type LocalRunOutcome = "completed" | "failed" | "paused" | "running";
|
|
13
|
+
export interface UnusedStub {
|
|
14
|
+
step: string;
|
|
15
|
+
key: string;
|
|
16
|
+
}
|
|
13
17
|
export interface LocalRunResult {
|
|
14
18
|
outcome: LocalRunOutcome;
|
|
15
19
|
executionId: string;
|
|
16
20
|
output?: unknown;
|
|
17
21
|
error?: unknown;
|
|
18
22
|
steps: LocalStepTrace[];
|
|
23
|
+
unusedStubs: UnusedStub[];
|
|
24
|
+
stubWarnings: string[];
|
|
19
25
|
}
|
|
20
26
|
export declare function runLocal(opts: RunLocalOptions): Promise<LocalRunResult>;
|
|
21
27
|
export declare function runLocalFromDir(opts: {
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from
|
|
2
|
-
import path from
|
|
3
|
-
import { DEFAULT_MAX_ATTEMPTS_PER_STEP, InMemoryExecutionStore, NOOP_OBSERVER, WorkflowRunnerCore, } from
|
|
4
|
-
import { OrchestrationError } from
|
|
5
|
-
import { LocalStubDispatcher } from
|
|
6
|
-
import { loadDefinition } from
|
|
7
|
-
import { parseStubFile } from
|
|
8
|
-
export const STUBS_FILE = path.join(
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_MAX_ATTEMPTS_PER_STEP, InMemoryExecutionStore, NOOP_OBSERVER, WorkflowRunnerCore, } from "@sapiom/orchestration-runtime";
|
|
4
|
+
import { OrchestrationError } from "../errors.js";
|
|
5
|
+
import { LocalStubDispatcher } from "./dispatcher.js";
|
|
6
|
+
import { loadDefinition } from "./load.js";
|
|
7
|
+
import { parseStubFile } from "./stubs.js";
|
|
8
|
+
export const STUBS_FILE = path.join(".sapiom-dev", "stubs.json");
|
|
9
9
|
function loadStubsFile(sourceDir) {
|
|
10
10
|
const file = path.join(sourceDir, STUBS_FILE);
|
|
11
11
|
if (!existsSync(file))
|
|
12
12
|
return undefined;
|
|
13
13
|
let raw;
|
|
14
14
|
try {
|
|
15
|
-
raw = JSON.parse(readFileSync(file,
|
|
15
|
+
raw = JSON.parse(readFileSync(file, "utf8"));
|
|
16
16
|
}
|
|
17
17
|
catch (err) {
|
|
18
18
|
throw new OrchestrationError({
|
|
19
|
-
code:
|
|
19
|
+
code: "STUBS_INVALID",
|
|
20
20
|
message: `${STUBS_FILE} is not valid JSON.`,
|
|
21
21
|
hint: err instanceof Error ? err.message : String(err),
|
|
22
22
|
});
|
|
@@ -31,7 +31,11 @@ export async function runLocal(opts) {
|
|
|
31
31
|
const dispatcher = new LocalStubDispatcher(opts.definition, stubs);
|
|
32
32
|
const signals = new Map();
|
|
33
33
|
dispatcher.setSignals(signals);
|
|
34
|
-
const core = new WorkflowRunnerCore({
|
|
34
|
+
const core = new WorkflowRunnerCore({
|
|
35
|
+
store,
|
|
36
|
+
dispatcher,
|
|
37
|
+
observer: NOOP_OBSERVER,
|
|
38
|
+
});
|
|
35
39
|
dispatcher.setCore(core);
|
|
36
40
|
dispatcher.setMaxAttempts(max);
|
|
37
41
|
const executionId = await core.createExecution(opts.definition.name, opts.definition.entry, opts.input, {
|
|
@@ -41,25 +45,43 @@ export async function runLocal(opts) {
|
|
|
41
45
|
while (guard++ < MAX_ADVANCES) {
|
|
42
46
|
await core.advance(executionId, max);
|
|
43
47
|
const row = await store.loadExecution(executionId);
|
|
44
|
-
if (!row ||
|
|
48
|
+
if (!row ||
|
|
49
|
+
row.status === "completed" ||
|
|
50
|
+
row.status === "failed" ||
|
|
51
|
+
row.status === "cancelled")
|
|
45
52
|
break;
|
|
46
|
-
if (row.status ===
|
|
47
|
-
const payload = signals.get(row.pausedSignalCorrelationId ??
|
|
53
|
+
if (row.status === "paused") {
|
|
54
|
+
const payload = signals.get(row.pausedSignalCorrelationId ?? "") ?? {};
|
|
48
55
|
await core.resetForResume(executionId, { fromStepInput: payload });
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
const final = await store.loadExecution(executionId);
|
|
52
|
-
const outcome = final?.status ===
|
|
59
|
+
const outcome = final?.status === "cancelled" ? "failed" : (final?.status ?? "running");
|
|
60
|
+
const unusedStubs = [];
|
|
61
|
+
for (const [step, used] of dispatcher.usedKeysByStep) {
|
|
62
|
+
for (const key of Object.keys(stubs.steps[step] ?? {})) {
|
|
63
|
+
if (!used.has(key))
|
|
64
|
+
unusedStubs.push({ step, key });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
53
67
|
return {
|
|
54
68
|
outcome,
|
|
55
69
|
executionId,
|
|
56
70
|
output: final?.output,
|
|
57
71
|
error: final?.error,
|
|
58
72
|
steps: dispatcher.trace,
|
|
73
|
+
unusedStubs,
|
|
74
|
+
stubWarnings: [...dispatcher.stubWarnings],
|
|
59
75
|
};
|
|
60
76
|
}
|
|
61
77
|
export async function runLocalFromDir(opts) {
|
|
62
78
|
const { definition, manifest } = await loadDefinition(opts.sourceDir);
|
|
63
79
|
const stubs = opts.stubs ?? loadStubsFile(opts.sourceDir);
|
|
64
|
-
return runLocal({
|
|
80
|
+
return runLocal({
|
|
81
|
+
definition,
|
|
82
|
+
manifest,
|
|
83
|
+
input: opts.input,
|
|
84
|
+
stubs,
|
|
85
|
+
maxAttemptsPerStep: opts.maxAttemptsPerStep,
|
|
86
|
+
});
|
|
65
87
|
}
|
package/dist/esm/scaffold.d.ts
CHANGED
package/dist/esm/scaffold.js
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { OrchestrationError } from "./errors.js";
|
|
5
6
|
function resolveModuleDir() {
|
|
6
|
-
if (typeof __dirname !==
|
|
7
|
+
if (typeof __dirname !== "undefined")
|
|
7
8
|
return __dirname;
|
|
8
9
|
try {
|
|
9
|
-
const metaUrl = eval(
|
|
10
|
-
if (typeof metaUrl ===
|
|
10
|
+
const metaUrl = eval("import.meta.url");
|
|
11
|
+
if (typeof metaUrl === "string")
|
|
11
12
|
return path.dirname(fileURLToPath(metaUrl));
|
|
12
13
|
}
|
|
13
14
|
catch {
|
|
14
15
|
}
|
|
15
|
-
return
|
|
16
|
+
return "";
|
|
16
17
|
}
|
|
17
18
|
const moduleDir = resolveModuleDir();
|
|
18
19
|
function getTemplatesDir(override) {
|
|
19
|
-
return override ??
|
|
20
|
+
return (override ??
|
|
21
|
+
process.env.SAPIOM_TEMPLATES_DIR ??
|
|
22
|
+
path.resolve(moduleDir, "..", "..", "templates"));
|
|
20
23
|
}
|
|
21
|
-
export const DEFAULT_TEMPLATE =
|
|
22
|
-
const DOTFILE_NAMES = new Set([
|
|
23
|
-
const REGISTRY =
|
|
24
|
+
export const DEFAULT_TEMPLATE = "default";
|
|
25
|
+
const DOTFILE_NAMES = new Set(["_gitignore", "_npmrc"]);
|
|
26
|
+
const REGISTRY = "https://registry.npmjs.org";
|
|
24
27
|
const VERSION_FALLBACK = {
|
|
25
|
-
orchestration:
|
|
26
|
-
tools:
|
|
28
|
+
orchestration: "0.1.1",
|
|
29
|
+
tools: "0.1.1",
|
|
27
30
|
};
|
|
28
|
-
const ZOD_VERSION =
|
|
31
|
+
const ZOD_VERSION = "3.25.76";
|
|
29
32
|
async function latestNpmVersion(pkg) {
|
|
30
33
|
try {
|
|
31
34
|
const res = await fetch(`${REGISTRY}/${encodeURIComponent(pkg)}/latest`, {
|
|
@@ -34,7 +37,7 @@ async function latestNpmVersion(pkg) {
|
|
|
34
37
|
if (!res.ok)
|
|
35
38
|
return null;
|
|
36
39
|
const json = (await res.json());
|
|
37
|
-
return typeof json.version ===
|
|
40
|
+
return typeof json.version === "string" ? json.version : null;
|
|
38
41
|
}
|
|
39
42
|
catch {
|
|
40
43
|
return null;
|
|
@@ -42,8 +45,8 @@ async function latestNpmVersion(pkg) {
|
|
|
42
45
|
}
|
|
43
46
|
export async function resolveVersions() {
|
|
44
47
|
const [orchestration, tools] = await Promise.all([
|
|
45
|
-
latestNpmVersion(
|
|
46
|
-
latestNpmVersion(
|
|
48
|
+
latestNpmVersion("@sapiom/orchestration"),
|
|
49
|
+
latestNpmVersion("@sapiom/tools"),
|
|
47
50
|
]);
|
|
48
51
|
return {
|
|
49
52
|
orchestration: orchestration ?? VERSION_FALLBACK.orchestration,
|
|
@@ -62,9 +65,11 @@ export function resolveTemplate(name, templatesDir) {
|
|
|
62
65
|
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
|
|
63
66
|
const available = listTemplates(templatesDir);
|
|
64
67
|
throw new OrchestrationError({
|
|
65
|
-
code:
|
|
68
|
+
code: "UNKNOWN_TEMPLATE",
|
|
66
69
|
message: `Unknown template '${name}'.` +
|
|
67
|
-
(available.length
|
|
70
|
+
(available.length
|
|
71
|
+
? ` Available: ${available.join(", ")}.`
|
|
72
|
+
: " No templates are bundled."),
|
|
68
73
|
});
|
|
69
74
|
}
|
|
70
75
|
return dir;
|
|
@@ -72,7 +77,7 @@ export function resolveTemplate(name, templatesDir) {
|
|
|
72
77
|
function applyReplacements(file, replacements) {
|
|
73
78
|
let content;
|
|
74
79
|
try {
|
|
75
|
-
content = readFileSync(file,
|
|
80
|
+
content = readFileSync(file, "utf8");
|
|
76
81
|
}
|
|
77
82
|
catch {
|
|
78
83
|
return;
|
|
@@ -101,7 +106,7 @@ function copyTemplate(templateDir, targetDir, replacements) {
|
|
|
101
106
|
walk(targetDir, (file) => {
|
|
102
107
|
const base = path.basename(file);
|
|
103
108
|
if (DOTFILE_NAMES.has(base)) {
|
|
104
|
-
const dotted = path.join(path.dirname(file),
|
|
109
|
+
const dotted = path.join(path.dirname(file), "." + base.slice(1));
|
|
105
110
|
renameSync(file, dotted);
|
|
106
111
|
applyReplacements(dotted, replacements);
|
|
107
112
|
return;
|
|
@@ -109,13 +114,37 @@ function copyTemplate(templateDir, targetDir, replacements) {
|
|
|
109
114
|
applyReplacements(file, replacements);
|
|
110
115
|
});
|
|
111
116
|
}
|
|
117
|
+
function initGitRepo(dir) {
|
|
118
|
+
const tryGit = (args) => {
|
|
119
|
+
try {
|
|
120
|
+
execFileSync("git", args, { cwd: dir, stdio: "ignore" });
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
if (!tryGit(["init"]))
|
|
128
|
+
return false;
|
|
129
|
+
tryGit(["add", "-A"]);
|
|
130
|
+
return (tryGit(["commit", "-m", "Initial commit"]) ||
|
|
131
|
+
tryGit([
|
|
132
|
+
"-c",
|
|
133
|
+
"user.name=Sapiom",
|
|
134
|
+
"-c",
|
|
135
|
+
"user.email=noreply@sapiom.ai",
|
|
136
|
+
"commit",
|
|
137
|
+
"-m",
|
|
138
|
+
"Initial commit",
|
|
139
|
+
]));
|
|
140
|
+
}
|
|
112
141
|
export async function scaffold(opts) {
|
|
113
142
|
const { targetDir } = opts;
|
|
114
143
|
const template = opts.template ?? DEFAULT_TEMPLATE;
|
|
115
144
|
const projectName = opts.projectName ?? path.basename(targetDir);
|
|
116
145
|
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
117
146
|
throw new OrchestrationError({
|
|
118
|
-
code:
|
|
147
|
+
code: "DIR_NOT_EMPTY",
|
|
119
148
|
message: `Target directory '${targetDir}' already exists and is not empty.`,
|
|
120
149
|
});
|
|
121
150
|
}
|
|
@@ -128,8 +157,9 @@ export async function scaffold(opts) {
|
|
|
128
157
|
__TOOLS_VERSION__: versions.tools,
|
|
129
158
|
__ZOD_VERSION__: versions.zod,
|
|
130
159
|
});
|
|
131
|
-
const devDir = path.join(targetDir,
|
|
160
|
+
const devDir = path.join(targetDir, ".sapiom-dev");
|
|
132
161
|
mkdirSync(devDir, { recursive: true });
|
|
133
|
-
writeFileSync(path.join(devDir,
|
|
134
|
-
|
|
162
|
+
writeFileSync(path.join(devDir, "stubs.json"), JSON.stringify({ version: 1, steps: {} }, null, 2) + "\n");
|
|
163
|
+
const gitInitialized = initGitRepo(targetDir);
|
|
164
|
+
return { targetDir, template, projectName, gitInitialized };
|
|
135
165
|
}
|