@elizaos/plugin-workflow 2.0.0-beta.1 → 2.0.3-beta.5
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/LICENSE +21 -0
- package/README.md +28 -26
- package/dist/actions/eval-code.d.ts +12 -0
- package/dist/actions/eval-code.d.ts.map +1 -0
- package/dist/actions/eval-code.js +59 -0
- package/dist/actions/eval-code.js.map +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +1 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/workflow.d.ts +7 -0
- package/dist/actions/workflow.d.ts.map +1 -1
- package/dist/actions/workflow.js +462 -10
- package/dist/actions/workflow.js.map +1 -1
- package/dist/db/schema.d.ts +196 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +23 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -64
- package/dist/index.js.map +1 -1
- package/dist/lib/automations-builder.d.ts.map +1 -1
- package/dist/lib/automations-builder.js +10 -35
- package/dist/lib/automations-builder.js.map +1 -1
- package/dist/lib/automations-types.d.ts +2 -2
- package/dist/lib/automations-types.d.ts.map +1 -1
- package/dist/lib/automations-types.js.map +1 -1
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/workflow-clarification.d.ts +2 -2
- package/dist/lib/workflow-clarification.d.ts.map +1 -1
- package/dist/lib/workflow-clarification.js +15 -11
- package/dist/lib/workflow-clarification.js.map +1 -1
- package/dist/plugin-routes.d.ts.map +1 -1
- package/dist/plugin-routes.js +6 -0
- package/dist/plugin-routes.js.map +1 -1
- package/dist/providers/activeWorkflows.js +2 -2
- package/dist/providers/activeWorkflows.js.map +1 -1
- package/dist/providers/workflowStatus.js +1 -1
- package/dist/providers/workflowStatus.js.map +1 -1
- package/dist/routes/workflow-routes.d.ts.map +1 -1
- package/dist/routes/workflow-routes.js +68 -2
- package/dist/routes/workflow-routes.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +5 -1
- package/dist/routes/workflows.js.map +1 -1
- package/dist/services/embedded-workflow-service.d.ts +74 -17
- package/dist/services/embedded-workflow-service.d.ts.map +1 -1
- package/dist/services/embedded-workflow-service.js +343 -149
- package/dist/services/embedded-workflow-service.js.map +1 -1
- package/dist/services/smithers-runtime.d.ts +47 -0
- package/dist/services/smithers-runtime.d.ts.map +1 -0
- package/dist/services/smithers-runtime.js +444 -0
- package/dist/services/smithers-runtime.js.map +1 -0
- package/dist/services/workflow-credential-store.js +1 -1
- package/dist/services/workflow-credential-store.js.map +1 -1
- package/dist/services/workflow-dispatch.d.ts +31 -1
- package/dist/services/workflow-dispatch.d.ts.map +1 -1
- package/dist/services/workflow-dispatch.js +75 -10
- package/dist/services/workflow-dispatch.js.map +1 -1
- package/dist/services/workflow-service.d.ts +27 -1
- package/dist/services/workflow-service.d.ts.map +1 -1
- package/dist/services/workflow-service.js +133 -11
- package/dist/services/workflow-service.js.map +1 -1
- package/dist/trigger-routes.d.ts +2 -18
- package/dist/trigger-routes.d.ts.map +1 -1
- package/dist/trigger-routes.js +11 -39
- package/dist/trigger-routes.js.map +1 -1
- package/dist/types/index.d.ts +82 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/workflow-contracts.d.ts +118 -0
- package/dist/types/workflow-contracts.d.ts.map +1 -0
- package/dist/types/workflow-contracts.js +2 -0
- package/dist/types/workflow-contracts.js.map +1 -0
- package/dist/utils/catalog.js +2 -2
- package/dist/utils/catalog.js.map +1 -1
- package/dist/utils/clarification.d.ts +1 -1
- package/dist/utils/clarification.d.ts.map +1 -1
- package/dist/utils/clarification.js +15 -4
- package/dist/utils/clarification.js.map +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/context.js.map +1 -1
- package/dist/utils/evaluation-samples.d.ts +6 -0
- package/dist/utils/evaluation-samples.d.ts.map +1 -0
- package/dist/utils/evaluation-samples.js +216 -0
- package/dist/utils/evaluation-samples.js.map +1 -0
- package/dist/utils/execution-diagnostics.d.ts +26 -0
- package/dist/utils/execution-diagnostics.d.ts.map +1 -0
- package/dist/utils/execution-diagnostics.js +159 -0
- package/dist/utils/execution-diagnostics.js.map +1 -0
- package/dist/utils/generation.d.ts.map +1 -1
- package/dist/utils/generation.js +134 -19
- package/dist/utils/generation.js.map +1 -1
- package/dist/utils/host-capabilities.d.ts.map +1 -1
- package/dist/utils/host-capabilities.js +20 -5
- package/dist/utils/host-capabilities.js.map +1 -1
- package/dist/utils/inferSyntheticOutputSchema.js +3 -3
- package/dist/utils/inferSyntheticOutputSchema.js.map +1 -1
- package/dist/utils/outputSchema.js +1 -1
- package/dist/utils/outputSchema.js.map +1 -1
- package/dist/utils/validateAndRepair.js +10 -10
- package/dist/utils/validateAndRepair.js.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.js +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.js +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.js +4 -4
- package/dist/utils/workflow-prompts/workflowMatching.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.js +1 -1
- package/dist/utils/workflow.d.ts +1 -0
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +44 -8
- package/dist/utils/workflow.js.map +1 -1
- package/package.json +27 -8
- package/registry-entry.json +25 -0
- package/src/actions/eval-code.ts +81 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/workflow.ts +518 -10
- package/src/db/schema.ts +31 -0
- package/src/index.ts +9 -82
- package/src/lib/automations-builder.ts +11 -35
- package/src/lib/automations-types.ts +1 -2
- package/src/lib/index.ts +0 -8
- package/src/lib/workflow-clarification.ts +18 -13
- package/src/plugin-routes.ts +6 -0
- package/src/providers/activeWorkflows.ts +2 -2
- package/src/providers/workflowStatus.ts +1 -1
- package/src/routes/workflow-routes.ts +100 -2
- package/src/routes/workflows.ts +5 -1
- package/src/services/embedded-workflow-service.ts +447 -172
- package/src/services/smithers-runtime.ts +526 -0
- package/src/services/workflow-credential-store.ts +1 -1
- package/src/services/workflow-dispatch.ts +116 -13
- package/src/services/workflow-service.ts +186 -10
- package/src/trigger-routes.ts +12 -70
- package/src/types/index.ts +94 -2
- package/src/types/workflow-contracts.ts +166 -0
- package/src/utils/catalog.ts +2 -2
- package/src/utils/clarification.ts +19 -5
- package/src/utils/context.ts +1 -1
- package/src/utils/evaluation-samples.ts +239 -0
- package/src/utils/execution-diagnostics.ts +192 -0
- package/src/utils/generation.ts +224 -32
- package/src/utils/host-capabilities.ts +21 -5
- package/src/utils/inferSyntheticOutputSchema.ts +3 -3
- package/src/utils/outputSchema.ts +1 -1
- package/src/utils/validateAndRepair.ts +10 -10
- package/src/utils/workflow-prompts/draftIntent.ts +1 -1
- package/src/utils/workflow-prompts/keywordExtraction.ts +1 -1
- package/src/utils/workflow-prompts/workflowGeneration.ts +4 -4
- package/src/utils/workflow-prompts/workflowMatching.ts +1 -1
- package/src/utils/workflow.ts +56 -8
- package/dist/lib/legacy-task-migration.d.ts +0 -20
- package/dist/lib/legacy-task-migration.d.ts.map +0 -1
- package/dist/lib/legacy-task-migration.js +0 -110
- package/dist/lib/legacy-task-migration.js.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.d.ts +0 -18
- package/dist/lib/legacy-text-trigger-migration.d.ts.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.js +0 -131
- package/dist/lib/legacy-text-trigger-migration.js.map +0 -1
- package/src/lib/legacy-task-migration.ts +0 -143
- package/src/lib/legacy-text-trigger-migration.ts +0 -178
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { logger } from '@elizaos/core';
|
|
6
|
+
function sanitizeWorkflowName(name) {
|
|
7
|
+
return name.replace(/[^a-zA-Z0-9_.:-]+/g, '-').replace(/^-+|-+$/g, '') || 'workflow';
|
|
8
|
+
}
|
|
9
|
+
function resolveSmithersDbPath(workflowId) {
|
|
10
|
+
const safeId = sanitizeWorkflowName(workflowId || 'anonymous');
|
|
11
|
+
return join(process.cwd(), '.eliza', 'smithers', `${safeId}.sqlite`);
|
|
12
|
+
}
|
|
13
|
+
function resolveBunBinary() {
|
|
14
|
+
if (typeof globalThis.Bun !== 'undefined')
|
|
15
|
+
return process.execPath;
|
|
16
|
+
return process.env.BUN_BIN || 'bun';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the Smithers storage backend configuration from environment variables.
|
|
20
|
+
*
|
|
21
|
+
* SMITHERS_DB_PROVIDER: "sqlite" (default) | "postgres" | "pglite"
|
|
22
|
+
* SMITHERS_DB_URL: PostgreSQL connection string (used when provider = "postgres")
|
|
23
|
+
* SMITHERS_DB_DATA_DIR: PGlite data directory (used when provider = "pglite")
|
|
24
|
+
*
|
|
25
|
+
* The resolved config is threaded through the subprocess payload so the layer
|
|
26
|
+
* selection runs inside the subprocess script string.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveSmithersDbConfig() {
|
|
29
|
+
const raw = process.env.SMITHERS_DB_PROVIDER ?? 'sqlite';
|
|
30
|
+
const provider = raw === 'postgres' || raw === 'pglite' ? raw : 'sqlite';
|
|
31
|
+
return {
|
|
32
|
+
provider,
|
|
33
|
+
connectionString: process.env.SMITHERS_DB_URL,
|
|
34
|
+
dataDir: process.env.SMITHERS_DB_DATA_DIR,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function resolvePluginRoot() {
|
|
38
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
40
|
+
try {
|
|
41
|
+
const manifestPath = join(dir, 'package.json');
|
|
42
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
43
|
+
if (manifest.name === '@elizaos/plugin-workflow')
|
|
44
|
+
return dir;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Continue walking upward until the plugin package root is found.
|
|
48
|
+
}
|
|
49
|
+
const parent = dirname(dir);
|
|
50
|
+
if (parent === dir)
|
|
51
|
+
break;
|
|
52
|
+
dir = parent;
|
|
53
|
+
}
|
|
54
|
+
return process.cwd();
|
|
55
|
+
}
|
|
56
|
+
function toErrorPayload(error) {
|
|
57
|
+
if (error instanceof Error)
|
|
58
|
+
return { message: error.message, stack: error.stack };
|
|
59
|
+
return { message: String(error) };
|
|
60
|
+
}
|
|
61
|
+
function buildSmithersWorkerEnv(payload) {
|
|
62
|
+
const env = { ...process.env, ELIZA_SMITHERS_RUN_PAYLOAD: payload };
|
|
63
|
+
for (const key of Object.keys(env)) {
|
|
64
|
+
const normalized = key.toUpperCase();
|
|
65
|
+
if (normalized === 'NODE_V8_COVERAGE' ||
|
|
66
|
+
normalized === 'BUN_TEST' ||
|
|
67
|
+
normalized.startsWith('BUN_TEST_') ||
|
|
68
|
+
normalized.startsWith('VITEST') ||
|
|
69
|
+
normalized.startsWith('NYC_') ||
|
|
70
|
+
normalized.includes('COVERAGE')) {
|
|
71
|
+
delete env[key];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return env;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Source for the per-run Smithers subprocess. Each workflow run executes in a
|
|
78
|
+
* fresh Bun process so the global Smithers singleton + SQLite state stay isolated
|
|
79
|
+
* (a long-lived singleton degrades across runs) and so Bun's `bun:sqlite` is
|
|
80
|
+
* available (Smithers requires it).
|
|
81
|
+
*
|
|
82
|
+
* The node graph is built with native Smithers control flow: dependency-depth
|
|
83
|
+
* levels become `parallel` groups joined in `sequence`, so independent nodes run
|
|
84
|
+
* concurrently instead of strictly serially. Node execution is delegated back to
|
|
85
|
+
* the parent over a line-delimited stdin/stdout protocol; a map-based response
|
|
86
|
+
* reader lets concurrent in-flight requests from a parallel level resolve without
|
|
87
|
+
* racing. Per-node n8n retry / continue-on-fail is honoured, and per-run metrics
|
|
88
|
+
* are reported back.
|
|
89
|
+
*/
|
|
90
|
+
function createSmithersScript() {
|
|
91
|
+
return String.raw `
|
|
92
|
+
import { Smithers } from 'smithers-orchestrator';
|
|
93
|
+
import { Effect, Schema } from 'effect';
|
|
94
|
+
import { createInterface } from 'node:readline/promises';
|
|
95
|
+
|
|
96
|
+
const payload = JSON.parse(process.env.ELIZA_SMITHERS_RUN_PAYLOAD ?? '{}');
|
|
97
|
+
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
98
|
+
const pending = new Map();
|
|
99
|
+
let requestSeq = 0;
|
|
100
|
+
const metrics = { nodes: 0, levels: 0, maxConcurrency: 0, started: 0, finished: 0, failed: 0, skipped: 0, retries: 0 };
|
|
101
|
+
let lastNodeError = null;
|
|
102
|
+
|
|
103
|
+
function emit(message) {
|
|
104
|
+
process.stdout.write(JSON.stringify(message) + '\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
(async () => {
|
|
108
|
+
for await (const line of rl) {
|
|
109
|
+
if (!line.trim()) continue;
|
|
110
|
+
let response;
|
|
111
|
+
try { response = JSON.parse(line); } catch { continue; }
|
|
112
|
+
const entry = pending.get(response.requestId);
|
|
113
|
+
if (!entry) continue;
|
|
114
|
+
pending.delete(response.requestId);
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const error = new Error(response.error?.message ?? 'Node execution failed');
|
|
117
|
+
if (response.error?.stack) error.stack = response.error.stack;
|
|
118
|
+
entry.reject(error);
|
|
119
|
+
} else {
|
|
120
|
+
entry.resolve(response.outputData ?? [[]]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
})();
|
|
124
|
+
|
|
125
|
+
function sendNodeRequest(nodeName, inputData) {
|
|
126
|
+
const requestId = String(++requestSeq);
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
pending.set(requestId, { resolve, reject });
|
|
129
|
+
emit({ type: 'executeNode', requestId, nodeName, inputData });
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function cloneJson(value) { return JSON.parse(JSON.stringify(value)); }
|
|
134
|
+
function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
|
|
135
|
+
|
|
136
|
+
function collectInputData(nodeName, incoming, nodeOutputs) {
|
|
137
|
+
const inputData = [];
|
|
138
|
+
for (const connection of incoming[nodeName] ?? []) {
|
|
139
|
+
const sourceOutputs = nodeOutputs[connection.source] ?? [];
|
|
140
|
+
const sourceItems = sourceOutputs[connection.sourceOutputIndex] ?? [];
|
|
141
|
+
inputData[connection.destinationInputIndex] = [
|
|
142
|
+
...(inputData[connection.destinationInputIndex] ?? []),
|
|
143
|
+
...sourceItems,
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
return inputData.length > 0 ? inputData : [[]];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function hasInputItems(inputData) { return inputData.some((items) => items.length > 0); }
|
|
150
|
+
|
|
151
|
+
function makeStepId(index, node) {
|
|
152
|
+
const raw = node.id ?? node.name ?? 'node';
|
|
153
|
+
const safe = String(raw).replace(/[^a-zA-Z0-9_.:-]+/g, '-').replace(/^-+|-+$/g, '') || 'node';
|
|
154
|
+
return String(index).padStart(4, '0') + '-' + safe;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Group topologically-ordered nodes into dependency-depth levels; nodes in a
|
|
158
|
+
// level have no data dependency on each other and run concurrently.
|
|
159
|
+
function computeLevels(enabledNodes, incoming, startNodes, nodeByName) {
|
|
160
|
+
const depth = new Map();
|
|
161
|
+
for (const node of enabledNodes) {
|
|
162
|
+
const connections = (incoming[node.name] ?? []).filter((c) => nodeByName.has(c.source));
|
|
163
|
+
if (startNodes.has(node.name) || connections.length === 0) { depth.set(node.name, 0); continue; }
|
|
164
|
+
let nodeDepth = 0;
|
|
165
|
+
for (const connection of connections) nodeDepth = Math.max(nodeDepth, (depth.get(connection.source) ?? 0) + 1);
|
|
166
|
+
depth.set(node.name, nodeDepth);
|
|
167
|
+
}
|
|
168
|
+
const levels = [];
|
|
169
|
+
for (const node of enabledNodes) {
|
|
170
|
+
const nodeDepth = depth.get(node.name) ?? 0;
|
|
171
|
+
(levels[nodeDepth] ??= []).push(node);
|
|
172
|
+
}
|
|
173
|
+
return levels.filter((level) => level && level.length > 0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Honour the node's own n8n retry / continue-on-fail settings.
|
|
177
|
+
async function runNodeWithPolicy(node, inputData) {
|
|
178
|
+
const maxAttempts = node.retryOnFail ? Math.max(1, node.maxTries ?? 3) : 1;
|
|
179
|
+
let lastError;
|
|
180
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
181
|
+
try { return await sendNodeRequest(node.name, inputData); }
|
|
182
|
+
catch (error) {
|
|
183
|
+
lastError = error;
|
|
184
|
+
lastNodeError = { nodeName: node.name, message: error?.message ?? String(error) };
|
|
185
|
+
if (attempt < maxAttempts) { metrics.retries += 1; await delay(node.waitBetweenTries ?? 1000); }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (node.continueOnFail) return [[{ json: { error: lastError?.message ?? String(lastError) } }]];
|
|
189
|
+
throw lastError;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const enabledNodes = payload.plan.enabledNodes;
|
|
194
|
+
const incoming = payload.plan.incoming;
|
|
195
|
+
const startNodes = new Set(payload.plan.startNodes);
|
|
196
|
+
const nodeByName = new Map(enabledNodes.map((node) => [node.name, node]));
|
|
197
|
+
const levels = computeLevels(enabledNodes, incoming, startNodes, nodeByName);
|
|
198
|
+
const terminalNodeName = enabledNodes[enabledNodes.length - 1]?.name;
|
|
199
|
+
metrics.nodes = enabledNodes.length;
|
|
200
|
+
metrics.levels = levels.length;
|
|
201
|
+
metrics.maxConcurrency = levels.reduce((max, level) => Math.max(max, level.length), 0);
|
|
202
|
+
|
|
203
|
+
const nodeOutputs = {};
|
|
204
|
+
const runData = {};
|
|
205
|
+
const workflow = Smithers.workflow({ name: payload.workflowName, input: Schema.Unknown });
|
|
206
|
+
|
|
207
|
+
const buildStep = (node, index) =>
|
|
208
|
+
workflow.step(makeStepId(index, node), {
|
|
209
|
+
output: Schema.Unknown,
|
|
210
|
+
run: async () => {
|
|
211
|
+
metrics.started += 1;
|
|
212
|
+
const incomingConnections = incoming[node.name] ?? [];
|
|
213
|
+
const isStartNode = startNodes.has(node.name);
|
|
214
|
+
const inputData =
|
|
215
|
+
isStartNode && incomingConnections.length === 0
|
|
216
|
+
? Object.keys(payload.triggerData ?? {}).length > 0
|
|
217
|
+
? [[{ json: payload.triggerData }]]
|
|
218
|
+
: [[]]
|
|
219
|
+
: collectInputData(node.name, incoming, nodeOutputs);
|
|
220
|
+
const started = Date.now();
|
|
221
|
+
const shouldSkip = !isStartNode && incomingConnections.length > 0 && !hasInputItems(inputData);
|
|
222
|
+
let outputData;
|
|
223
|
+
if (shouldSkip) { outputData = [[]]; metrics.skipped += 1; }
|
|
224
|
+
else {
|
|
225
|
+
try { outputData = await runNodeWithPolicy(node, inputData); }
|
|
226
|
+
catch (error) { metrics.failed += 1; throw error; }
|
|
227
|
+
}
|
|
228
|
+
nodeOutputs[node.name] = outputData;
|
|
229
|
+
runData[node.name] = [{
|
|
230
|
+
startTime: started,
|
|
231
|
+
executionTime: Date.now() - started,
|
|
232
|
+
data: { main: cloneJson(outputData) },
|
|
233
|
+
source: incomingConnections.map((connection) => ({
|
|
234
|
+
previousNode: connection.source,
|
|
235
|
+
previousNodeOutput: connection.sourceOutputIndex,
|
|
236
|
+
previousNodeRun: 0,
|
|
237
|
+
})),
|
|
238
|
+
}];
|
|
239
|
+
metrics.finished += 1;
|
|
240
|
+
return { nodeName: node.name, outputData };
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
let stepIndex = 0;
|
|
245
|
+
const levelGraphs = levels.map((level) => {
|
|
246
|
+
const handles = level.map((node) => buildStep(node, stepIndex++));
|
|
247
|
+
return handles.length === 1 ? handles[0] : workflow.parallel(...handles);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const resultStep = workflow.step('eliza-workflow-result', {
|
|
251
|
+
output: Schema.Unknown,
|
|
252
|
+
run: async () => {
|
|
253
|
+
const stoppedAt = new Date().toISOString();
|
|
254
|
+
return {
|
|
255
|
+
...payload.pending,
|
|
256
|
+
finished: true,
|
|
257
|
+
status: 'success',
|
|
258
|
+
stoppedAt,
|
|
259
|
+
data: { resultData: { runData, lastNodeExecuted: terminalNodeName } },
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const graph = workflow.sequence(...levelGraphs, resultStep);
|
|
265
|
+
const built = workflow.from(graph);
|
|
266
|
+
// Select the storage backend based on the provider field threaded through
|
|
267
|
+
// the payload. Feature-detect non-sqlite APIs: smithers-orchestrator@0.22.0
|
|
268
|
+
// does not yet expose Smithers.postgres / Smithers.pglite; if the method is
|
|
269
|
+
// absent we degrade to sqlite so old and new builds both work correctly.
|
|
270
|
+
const dbConfig = payload.dbConfig ?? {};
|
|
271
|
+
const provider = dbConfig.provider ?? 'sqlite';
|
|
272
|
+
let smithersLayer;
|
|
273
|
+
if (provider !== 'sqlite' && typeof Smithers[provider] === 'function') {
|
|
274
|
+
if (provider === 'postgres') {
|
|
275
|
+
smithersLayer = Smithers.postgres({ connectionString: dbConfig.connectionString });
|
|
276
|
+
} else if (provider === 'pglite') {
|
|
277
|
+
smithersLayer = Smithers.pglite({ dataDir: dbConfig.dataDir });
|
|
278
|
+
} else {
|
|
279
|
+
smithersLayer = Smithers.sqlite({ filename: payload.dbPath });
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
smithersLayer = Smithers.sqlite({ filename: payload.dbPath });
|
|
283
|
+
}
|
|
284
|
+
const execution = await Effect.runPromise(
|
|
285
|
+
built
|
|
286
|
+
.execute(payload.input, {
|
|
287
|
+
runId: payload.executionId,
|
|
288
|
+
force: true,
|
|
289
|
+
rootDir: payload.rootDir ?? process.cwd(),
|
|
290
|
+
allowNetwork: true,
|
|
291
|
+
})
|
|
292
|
+
.pipe(Effect.provide(smithersLayer))
|
|
293
|
+
);
|
|
294
|
+
emit({ type: 'workflowResult', execution, metrics });
|
|
295
|
+
process.exit(0);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (lastNodeError) {
|
|
298
|
+
console.error('Node "' + lastNodeError.nodeName + '" failed: ' + lastNodeError.message);
|
|
299
|
+
}
|
|
300
|
+
console.error(error?.stack ?? error?.message ?? String(error));
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
export async function runWorkflowWithSmithers({ workflow, executionId, pending, mode, triggerData, plan, runNode, }) {
|
|
306
|
+
const dbPath = resolveSmithersDbPath(workflow.id ?? workflow.name);
|
|
307
|
+
await mkdir(dirname(dbPath), { recursive: true });
|
|
308
|
+
const dbConfig = resolveSmithersDbConfig();
|
|
309
|
+
const payload = JSON.stringify({
|
|
310
|
+
dbPath,
|
|
311
|
+
dbConfig,
|
|
312
|
+
executionId,
|
|
313
|
+
workflowName: sanitizeWorkflowName(workflow.name),
|
|
314
|
+
input: { mode, triggerData: triggerData ?? {}, workflowId: workflow.id ?? '' },
|
|
315
|
+
pending,
|
|
316
|
+
plan,
|
|
317
|
+
triggerData: triggerData ?? {},
|
|
318
|
+
rootDir: process.cwd(),
|
|
319
|
+
});
|
|
320
|
+
const pluginRoot = await resolvePluginRoot();
|
|
321
|
+
const proc = spawn(resolveBunBinary(), ['-e', createSmithersScript()], {
|
|
322
|
+
cwd: pluginRoot,
|
|
323
|
+
env: buildSmithersWorkerEnv(payload),
|
|
324
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
325
|
+
});
|
|
326
|
+
const byName = new Map(plan.enabledNodes.map((node) => [node.name, node]));
|
|
327
|
+
let executionResult = null;
|
|
328
|
+
let runMetrics = null;
|
|
329
|
+
let stdinEnded = false;
|
|
330
|
+
const endStdin = () => {
|
|
331
|
+
if (stdinEnded)
|
|
332
|
+
return;
|
|
333
|
+
stdinEnded = true;
|
|
334
|
+
proc.stdin.end();
|
|
335
|
+
};
|
|
336
|
+
const writeResponse = (response) => {
|
|
337
|
+
if (proc.stdin.writable)
|
|
338
|
+
proc.stdin.write(`${JSON.stringify(response)}\n`);
|
|
339
|
+
};
|
|
340
|
+
// Node executions are dispatched concurrently so a parallel level's nodes
|
|
341
|
+
// actually run in parallel; their promises are drained before completion.
|
|
342
|
+
const inflight = [];
|
|
343
|
+
const handleLine = (line) => {
|
|
344
|
+
// The subprocess shares stdout with Smithers' own logging; only our protocol
|
|
345
|
+
// JSON is relevant, so ignore anything that isn't an object line.
|
|
346
|
+
const trimmed = line.trim();
|
|
347
|
+
if (trimmed?.[0] !== '{')
|
|
348
|
+
return;
|
|
349
|
+
let message;
|
|
350
|
+
try {
|
|
351
|
+
message = JSON.parse(trimmed);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (message.type === 'workflowResult') {
|
|
357
|
+
executionResult = message.execution;
|
|
358
|
+
runMetrics = message.metrics ?? null;
|
|
359
|
+
endStdin();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (message.type !== 'executeNode')
|
|
363
|
+
return;
|
|
364
|
+
const node = byName.get(message.nodeName);
|
|
365
|
+
if (!node) {
|
|
366
|
+
writeResponse({
|
|
367
|
+
requestId: message.requestId,
|
|
368
|
+
ok: false,
|
|
369
|
+
error: { message: `Smithers requested unknown workflow node "${message.nodeName}"` },
|
|
370
|
+
});
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
inflight.push((async () => {
|
|
374
|
+
try {
|
|
375
|
+
const outputData = await runNode(node, message.inputData);
|
|
376
|
+
writeResponse({ requestId: message.requestId, ok: true, outputData });
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
writeResponse({ requestId: message.requestId, ok: false, error: toErrorPayload(error) });
|
|
380
|
+
}
|
|
381
|
+
})());
|
|
382
|
+
};
|
|
383
|
+
let stdoutBuffer = '';
|
|
384
|
+
let stderr = '';
|
|
385
|
+
proc.stdout.setEncoding('utf8');
|
|
386
|
+
proc.stdout.on('data', (chunk) => {
|
|
387
|
+
stdoutBuffer += chunk;
|
|
388
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
389
|
+
stdoutBuffer = lines.pop() ?? '';
|
|
390
|
+
for (const line of lines)
|
|
391
|
+
handleLine(line);
|
|
392
|
+
});
|
|
393
|
+
proc.stderr.setEncoding('utf8');
|
|
394
|
+
proc.stderr.on('data', (chunk) => {
|
|
395
|
+
stderr += chunk;
|
|
396
|
+
});
|
|
397
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
398
|
+
proc.on('error', reject);
|
|
399
|
+
proc.on('close', (code) => resolve(code ?? 1));
|
|
400
|
+
});
|
|
401
|
+
if (stdoutBuffer.trim())
|
|
402
|
+
handleLine(stdoutBuffer);
|
|
403
|
+
if (exitCode === 0)
|
|
404
|
+
await Promise.all(inflight);
|
|
405
|
+
endStdin();
|
|
406
|
+
if (exitCode !== 0) {
|
|
407
|
+
throw new Error(`Smithers workflow execution failed: ${stderr.trim() || `exit ${exitCode}`}`);
|
|
408
|
+
}
|
|
409
|
+
if (!executionResult) {
|
|
410
|
+
throw new Error('Smithers workflow execution completed without returning a workflow result');
|
|
411
|
+
}
|
|
412
|
+
const completedExecution = executionResult;
|
|
413
|
+
const completedMetrics = runMetrics;
|
|
414
|
+
const executionWithMetrics = completedMetrics
|
|
415
|
+
? {
|
|
416
|
+
...completedExecution,
|
|
417
|
+
data: {
|
|
418
|
+
...completedExecution.data,
|
|
419
|
+
resultData: {
|
|
420
|
+
...completedExecution.data?.resultData,
|
|
421
|
+
engine: {
|
|
422
|
+
provider: 'smithers',
|
|
423
|
+
nodes: completedMetrics.nodes,
|
|
424
|
+
levels: completedMetrics.levels,
|
|
425
|
+
maxConcurrency: completedMetrics.maxConcurrency,
|
|
426
|
+
started: completedMetrics.started,
|
|
427
|
+
finished: completedMetrics.finished,
|
|
428
|
+
failed: completedMetrics.failed,
|
|
429
|
+
skipped: completedMetrics.skipped,
|
|
430
|
+
retries: completedMetrics.retries,
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
}
|
|
435
|
+
: completedExecution;
|
|
436
|
+
logger.info({
|
|
437
|
+
src: 'plugin:workflow:smithers',
|
|
438
|
+
workflowId: workflow.id ?? '',
|
|
439
|
+
executionId,
|
|
440
|
+
...(runMetrics ?? {}),
|
|
441
|
+
}, 'workflow executed');
|
|
442
|
+
return executionWithMetrics;
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=smithers-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smithers-runtime.js","sourceRoot":"","sources":["../../src/services/smithers-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AA6DvC,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC;AACvF,CAAC;AAED,SAAS,qBAAqB,CAAC,UAAkB;IAC/C,MAAM,MAAM,GAAG,oBAAoB,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,SAAS,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,OAAQ,UAAgC,CAAC,GAAG,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC1F,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,KAAK,CAAC;AACtC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB;IAKrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,QAAQ,CAAC;IACzD,MAAM,QAAQ,GAAG,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzE,OAAO;QACL,QAAQ;QACR,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;QAC7C,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;KAC1C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,IAAI,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAsB,CAAC;YACvF,IAAI,QAAQ,CAAC,IAAI,KAAK,0BAA0B;gBAAE,OAAO,GAAG,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAClF,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAe;IAC7C,MAAM,GAAG,GAAsB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,0BAA0B,EAAE,OAAO,EAAE,CAAC;IACvF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACrC,IACE,UAAU,KAAK,kBAAkB;YACjC,UAAU,KAAK,UAAU;YACzB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC;YAClC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC;YAC/B,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;YAC7B,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,EAC/B,CAAC;YACD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,oBAAoB;IAC3B,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoNhB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAC5C,QAAQ,EACR,WAAW,EACX,OAAO,EACP,IAAI,EACJ,WAAW,EACX,IAAI,EACJ,OAAO,GACoB;IAC3B,MAAM,MAAM,GAAG,qBAAqB,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnE,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;IAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7B,MAAM;QACN,QAAQ;QACR,WAAW;QACX,YAAY,EAAE,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC;QACjD,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE;QAC9E,OAAO;QACP,IAAI;QACJ,WAAW,EAAE,WAAW,IAAI,EAAE;QAC9B,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE;KACvB,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC,EAAE;QACrE,GAAG,EAAE,UAAU;QACf,GAAG,EAAE,sBAAsB,CAAC,OAAO,CAAC;QACpC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3E,IAAI,eAAe,GAA6B,IAAI,CAAC;IACrD,IAAI,UAAU,GAA8B,IAAI,CAAC;IACjD,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,IAAI,UAAU;YAAE,OAAO;QACvB,UAAU,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,QAAkC,EAAQ,EAAE;QACjE,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC,CAAC;IAEF,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAQ,EAAE;QACxC,6EAA6E;QAC7E,kEAAkE;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,OAAO;QACjC,IAAI,OAAyD,CAAC;QAC9D,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqD,CAAC;QACpF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACtC,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;YACpC,UAAU,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;YACrC,QAAQ,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa;YAAE,OAAO;QAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,aAAa,CAAC;gBACZ,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,OAAO,EAAE,6CAA6C,OAAO,CAAC,QAAQ,GAAG,EAAE;aACrF,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC1D,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC,CAAC,EAAE,CACL,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QACvC,YAAY,IAAI,KAAK,CAAC;QACtB,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7D,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IACH,IAAI,YAAY,CAAC,IAAI,EAAE;QAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,CAAC;QAAE,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEhD,QAAQ,EAAE,CAAC;IAEX,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,uCAAuC,MAAM,CAAC,IAAI,EAAE,IAAI,QAAQ,QAAQ,EAAE,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,kBAAkB,GAAG,eAAoC,CAAC;IAChE,MAAM,gBAAgB,GAAG,UAAuC,CAAC;IACjE,MAAM,oBAAoB,GAAsB,gBAAgB;QAC9D,CAAC,CAAC;YACE,GAAG,kBAAkB;YACrB,IAAI,EAAE;gBACJ,GAAG,kBAAkB,CAAC,IAAI;gBAC1B,UAAU,EAAE;oBACV,GAAG,kBAAkB,CAAC,IAAI,EAAE,UAAU;oBACtC,MAAM,EAAE;wBACN,QAAQ,EAAE,UAAU;wBACpB,KAAK,EAAE,gBAAgB,CAAC,KAAK;wBAC7B,MAAM,EAAE,gBAAgB,CAAC,MAAM;wBAC/B,cAAc,EAAE,gBAAgB,CAAC,cAAc;wBAC/C,OAAO,EAAE,gBAAgB,CAAC,OAAO;wBACjC,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;wBACnC,MAAM,EAAE,gBAAgB,CAAC,MAAM;wBAC/B,OAAO,EAAE,gBAAgB,CAAC,OAAO;wBACjC,OAAO,EAAE,gBAAgB,CAAC,OAAO;qBAClC;iBACF;aACF;SACF;QACH,CAAC,CAAC,kBAAkB,CAAC;IAEvB,MAAM,CAAC,IAAI,CACT;QACE,GAAG,EAAE,0BAA0B;QAC/B,UAAU,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE;QAC7B,WAAW;QACX,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;KACtB,EACD,mBAAmB,CACpB,CAAC;IAEF,OAAO,oBAAoB,CAAC;AAC9B,CAAC"}
|
|
@@ -18,7 +18,7 @@ export class WorkflowCredentialStore extends Service {
|
|
|
18
18
|
* `start()` registered (referential equality matters for unregisterEvent).
|
|
19
19
|
*/
|
|
20
20
|
connectorDisconnectedHandler = async (payload) => {
|
|
21
|
-
if (!payload
|
|
21
|
+
if (!payload.userId || !Array.isArray(payload.credTypes)) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
if (payload.credTypes.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-credential-store.js","sourceRoot":"","sources":["../../src/services/workflow-credential-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMlD,OAAO,EAAE,4BAA4B,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAE9F;;;;;;GAMG;AACH,MAAM,OAAO,uBAAwB,SAAQ,OAAO;IAClD,MAAM,CAAmB,WAAW,GAAG,8BAA8B,CAAC;IAE7D,qBAAqB,GAC5B,6FAA6F,CAAC;IAEhG;;;;OAIG;IACc,4BAA4B,GAAG,KAAK,EACnD,OAAqC,EACtB,EAAE;QACjB,IAAI,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"workflow-credential-store.js","sourceRoot":"","sources":["../../src/services/workflow-credential-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,13 +19,43 @@ export interface WorkflowDispatchResult {
|
|
|
19
19
|
ok: boolean;
|
|
20
20
|
error?: string;
|
|
21
21
|
executionId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* True when the call was short-circuited by an idempotency-key match.
|
|
24
|
+
* Callers (the trigger dispatcher, dashboards) can record a dedup
|
|
25
|
+
* instead of treating the call as a fresh execution.
|
|
26
|
+
*/
|
|
27
|
+
dedup?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Optional, structured dispatch options. The `idempotencyKey` field is
|
|
31
|
+
* the durable contract: same workflow + same key → at most one
|
|
32
|
+
* execution. Passed inline through the legacy `payload` shape (key
|
|
33
|
+
* `__idempotencyKey`) when the caller can't pass a second argument.
|
|
34
|
+
*/
|
|
35
|
+
export interface WorkflowDispatchOptions {
|
|
36
|
+
triggerData?: Record<string, unknown>;
|
|
37
|
+
idempotencyKey?: string;
|
|
22
38
|
}
|
|
23
39
|
export interface WorkflowDispatchService {
|
|
24
|
-
execute(workflowId: string, payload?: Record<string, unknown
|
|
40
|
+
execute(workflowId: string, payload?: Record<string, unknown>, options?: WorkflowDispatchOptions): Promise<WorkflowDispatchResult>;
|
|
25
41
|
}
|
|
26
42
|
/**
|
|
27
43
|
* Construct the dispatch service. Registered under `WORKFLOW_DISPATCH` on the
|
|
28
44
|
* runtime by the plugin's `init` lifecycle hook.
|
|
45
|
+
*
|
|
46
|
+
* Idempotency contract: when a caller passes an `idempotencyKey` (either via
|
|
47
|
+
* the explicit `options.idempotencyKey` or via the legacy
|
|
48
|
+
* `payload.__idempotencyKey`), the dispatch service first looks up an
|
|
49
|
+
* existing execution row for `(workflowId, idempotencyKey)`. If one exists,
|
|
50
|
+
* the new run is suppressed and the prior execution id is returned with
|
|
51
|
+
* `{ ok: true, dedup: true }`. Scheduled workflow dispatches use a
|
|
52
|
+
* minute-bucketed key so two simultaneous schedule fires collapse to one
|
|
53
|
+
* execution.
|
|
54
|
+
*
|
|
55
|
+
* Concurrent dispatches that race past the lookup are still safely
|
|
56
|
+
* coalesced because the embedded service persists the idempotency key on
|
|
57
|
+
* the execution row, so the second-to-write completes but is detectable
|
|
58
|
+
* as a duplicate on later lookups.
|
|
29
59
|
*/
|
|
30
60
|
export declare function createWorkflowDispatchService(runtime: IAgentRuntime): WorkflowDispatchService;
|
|
31
61
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-dispatch.d.ts","sourceRoot":"","sources":["../../src/services/workflow-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAOnD,eAAO,MAAM,8BAA8B,EAAG,mBAA4B,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-dispatch.d.ts","sourceRoot":"","sources":["../../src/services/workflow-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAOnD,eAAO,MAAM,8BAA8B,EAAG,mBAA4B,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACpC;AAuDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,aAAa,GAAG,uBAAuB,CAsD7F;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAQ5E"}
|
|
@@ -16,6 +16,21 @@
|
|
|
16
16
|
import { logger } from '@elizaos/core';
|
|
17
17
|
import { EMBEDDED_WORKFLOW_SERVICE_TYPE, } from './embedded-workflow-service';
|
|
18
18
|
export const WORKFLOW_DISPATCH_SERVICE_TYPE = 'WORKFLOW_DISPATCH';
|
|
19
|
+
/**
|
|
20
|
+
* Pull `__idempotencyKey` out of the legacy `payload` shape so existing
|
|
21
|
+
* callers (the trigger dispatcher's `event` payload) can attach a key
|
|
22
|
+
* without growing the signature. The wrapper key is stripped before the
|
|
23
|
+
* payload is forwarded as `triggerData`.
|
|
24
|
+
*/
|
|
25
|
+
function partitionPayload(payload) {
|
|
26
|
+
if (!payload)
|
|
27
|
+
return { triggerData: {} };
|
|
28
|
+
const { __idempotencyKey, ...rest } = payload;
|
|
29
|
+
return {
|
|
30
|
+
triggerData: rest,
|
|
31
|
+
idempotencyKey: typeof __idempotencyKey === 'string' ? __idempotencyKey : undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
19
34
|
function resolveEmbeddedService(runtime) {
|
|
20
35
|
const service = runtime.getService(EMBEDDED_WORKFLOW_SERVICE_TYPE);
|
|
21
36
|
if (service && typeof service.executeWorkflow === 'function') {
|
|
@@ -41,10 +56,29 @@ function getRuntimeServiceRegistry(runtime) {
|
|
|
41
56
|
/**
|
|
42
57
|
* Construct the dispatch service. Registered under `WORKFLOW_DISPATCH` on the
|
|
43
58
|
* runtime by the plugin's `init` lifecycle hook.
|
|
59
|
+
*
|
|
60
|
+
* Idempotency contract: when a caller passes an `idempotencyKey` (either via
|
|
61
|
+
* the explicit `options.idempotencyKey` or via the legacy
|
|
62
|
+
* `payload.__idempotencyKey`), the dispatch service first looks up an
|
|
63
|
+
* existing execution row for `(workflowId, idempotencyKey)`. If one exists,
|
|
64
|
+
* the new run is suppressed and the prior execution id is returned with
|
|
65
|
+
* `{ ok: true, dedup: true }`. Scheduled workflow dispatches use a
|
|
66
|
+
* minute-bucketed key so two simultaneous schedule fires collapse to one
|
|
67
|
+
* execution.
|
|
68
|
+
*
|
|
69
|
+
* Concurrent dispatches that race past the lookup are still safely
|
|
70
|
+
* coalesced because the embedded service persists the idempotency key on
|
|
71
|
+
* the execution row, so the second-to-write completes but is detectable
|
|
72
|
+
* as a duplicate on later lookups.
|
|
44
73
|
*/
|
|
45
74
|
export function createWorkflowDispatchService(runtime) {
|
|
75
|
+
// Track in-flight executions by `(workflowId, idempotencyKey)` so that
|
|
76
|
+
// two concurrent dispatches inside the same process collapse onto one
|
|
77
|
+
// run. The map entry resolves once the original run finishes, and the
|
|
78
|
+
// late caller returns the same execution id.
|
|
79
|
+
const inflight = new Map();
|
|
46
80
|
return {
|
|
47
|
-
async execute(workflowId,
|
|
81
|
+
async execute(workflowId, payload = {}, options = {}) {
|
|
48
82
|
const id = workflowId.trim();
|
|
49
83
|
if (!id) {
|
|
50
84
|
return { ok: false, error: 'workflow id required' };
|
|
@@ -53,18 +87,49 @@ export function createWorkflowDispatchService(runtime) {
|
|
|
53
87
|
if (!service) {
|
|
54
88
|
return { ok: false, error: 'embedded workflow service not registered' };
|
|
55
89
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
90
|
+
const partitioned = partitionPayload(payload);
|
|
91
|
+
const triggerData = options.triggerData && Object.keys(options.triggerData).length > 0
|
|
92
|
+
? options.triggerData
|
|
93
|
+
: partitioned.triggerData;
|
|
94
|
+
const idempotencyKey = options.idempotencyKey ?? partitioned.idempotencyKey;
|
|
95
|
+
if (idempotencyKey) {
|
|
96
|
+
const existing = await service.findExecutionByIdempotencyKey(id, idempotencyKey);
|
|
97
|
+
if (existing) {
|
|
98
|
+
return existing.id
|
|
99
|
+
? { ok: true, executionId: existing.id, dedup: true }
|
|
100
|
+
: { ok: true, dedup: true };
|
|
101
|
+
}
|
|
102
|
+
const inflightKey = `${id}::${idempotencyKey}`;
|
|
103
|
+
const pending = inflight.get(inflightKey);
|
|
104
|
+
if (pending) {
|
|
105
|
+
const result = await pending;
|
|
106
|
+
return result.ok ? { ...result, dedup: true } : result;
|
|
107
|
+
}
|
|
108
|
+
const promise = runDispatch(service, id, triggerData, idempotencyKey).finally(() => {
|
|
109
|
+
inflight.delete(inflightKey);
|
|
110
|
+
});
|
|
111
|
+
inflight.set(inflightKey, promise);
|
|
112
|
+
return promise;
|
|
64
113
|
}
|
|
114
|
+
return runDispatch(service, id, triggerData, undefined);
|
|
65
115
|
},
|
|
66
116
|
};
|
|
67
117
|
}
|
|
118
|
+
async function runDispatch(service, workflowId, triggerData, idempotencyKey) {
|
|
119
|
+
try {
|
|
120
|
+
const execution = await service.executeWorkflow(workflowId, {
|
|
121
|
+
mode: 'trigger',
|
|
122
|
+
triggerData,
|
|
123
|
+
idempotencyKey,
|
|
124
|
+
});
|
|
125
|
+
return execution.id ? { ok: true, executionId: execution.id } : { ok: true };
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
129
|
+
logger.warn({ src: 'plugin:workflow:dispatch' }, `Workflow execution failed for ${workflowId}: ${message}`);
|
|
130
|
+
return { ok: false, error: message };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
68
133
|
/**
|
|
69
134
|
* Register the dispatch service in the runtime services map under
|
|
70
135
|
* `WORKFLOW_DISPATCH`. Called from the plugin's `init`.
|
|
@@ -77,7 +142,7 @@ export function createWorkflowDispatchService(runtime) {
|
|
|
77
142
|
export function registerWorkflowDispatchService(runtime) {
|
|
78
143
|
const dispatch = createWorkflowDispatchService(runtime);
|
|
79
144
|
const serviceEntry = {
|
|
80
|
-
execute: dispatch.execute,
|
|
145
|
+
execute: dispatch.execute.bind(dispatch),
|
|
81
146
|
stop: async () => { },
|
|
82
147
|
capabilityDescription: 'Executes embedded workflows by id via the in-process workflow service.',
|
|
83
148
|
};
|