@particle-academy/fancy-flow 0.10.1 → 0.12.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/dist/{chunk-2DMBJN2O.js → chunk-D6W5FMCT.js} +6 -5
- package/dist/chunk-D6W5FMCT.js.map +1 -0
- package/dist/{chunk-NCPQDVUE.js → chunk-IK5DS5JP.js} +262 -29
- package/dist/chunk-IK5DS5JP.js.map +1 -0
- package/dist/{chunk-HNBO4HP3.js → chunk-L4AX73Q6.js} +12 -5
- package/dist/chunk-L4AX73Q6.js.map +1 -0
- package/dist/{chunk-VEI743ZX.js → chunk-MFMRTRPO.js} +3 -3
- package/dist/{chunk-VEI743ZX.js.map → chunk-MFMRTRPO.js.map} +1 -1
- package/dist/{chunk-CPSOC27D.js → chunk-U5F22BHV.js} +18 -4
- package/dist/chunk-U5F22BHV.js.map +1 -0
- package/dist/engine.cjs +20 -3
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +4 -2
- package/dist/engine.d.ts +4 -2
- package/dist/engine.js +2 -2
- package/dist/index.cjs +417 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -9
- package/dist/registry/index.d.cts +149 -5
- package/dist/registry/index.d.ts +149 -5
- package/dist/registry.cjs +475 -71
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -2
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime.cjs +20 -3
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema.cjs +11 -3
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/{types-BS3Gwnkq.d.cts → types-Dg2REAz5.d.cts} +6 -0
- package/dist/{types-BS3Gwnkq.d.ts → types-Dg2REAz5.d.ts} +6 -0
- package/dist/{types-BocBFh6l.d.ts → types-DnQ4TEHN.d.cts} +16 -2
- package/dist/{types-DKqaUjF_.d.cts → types-DtXOwXjA.d.ts} +16 -2
- package/dist/ux.cjs +5 -0
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +2 -2
- package/dist/ux.d.ts +2 -2
- package/dist/ux.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2DMBJN2O.js.map +0 -1
- package/dist/chunk-CPSOC27D.js.map +0 -1
- package/dist/chunk-HNBO4HP3.js.map +0 -1
- package/dist/chunk-NCPQDVUE.js.map +0 -1
package/dist/registry.cjs
CHANGED
|
@@ -30,82 +30,82 @@ function resolveNodePorts(node, kind) {
|
|
|
30
30
|
outputs: data?.outputs ?? resolvePortSpec(kind?.outputs, config)
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
|
|
34
|
+
// src/registry/capabilities.ts
|
|
35
|
+
var llmClient = null;
|
|
36
|
+
function registerLlmClient(client) {
|
|
37
|
+
llmClient = client;
|
|
38
38
|
return () => {
|
|
39
|
-
if (
|
|
40
|
-
adapter = null;
|
|
41
|
-
for (const l of listeners) l();
|
|
42
|
-
}
|
|
39
|
+
if (llmClient === client) llmClient = null;
|
|
43
40
|
};
|
|
44
41
|
}
|
|
45
|
-
function
|
|
46
|
-
return
|
|
42
|
+
function getLlmClient() {
|
|
43
|
+
return llmClient;
|
|
47
44
|
}
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
var workflowResolver = null;
|
|
46
|
+
function registerWorkflowResolver(resolver) {
|
|
47
|
+
workflowResolver = resolver;
|
|
48
|
+
return () => {
|
|
49
|
+
if (workflowResolver === resolver) workflowResolver = null;
|
|
50
|
+
};
|
|
50
51
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
return () => listeners.delete(fn);
|
|
52
|
+
function getWorkflowResolver() {
|
|
53
|
+
return workflowResolver;
|
|
54
54
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
62
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview__unavailable", children: [
|
|
63
|
-
"Add ",
|
|
64
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/fancy-cms-ui" }),
|
|
65
|
-
" +",
|
|
66
|
-
" ",
|
|
67
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/react-fancy" }),
|
|
68
|
-
", then",
|
|
69
|
-
" ",
|
|
70
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { children: 'import "@particle-academy/fancy-flow/rich-input"' }),
|
|
71
|
-
"."
|
|
72
|
-
] })
|
|
73
|
-
] });
|
|
55
|
+
function capabilityStatus() {
|
|
56
|
+
let documentReady = false;
|
|
57
|
+
try {
|
|
58
|
+
documentReady = Boolean(globalThis.__fancyFlowDocumentAdapter);
|
|
59
|
+
} catch {
|
|
60
|
+
documentReady = false;
|
|
74
61
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
] });
|
|
62
|
+
return {
|
|
63
|
+
llm: llmClient !== null,
|
|
64
|
+
workflow_resolver: workflowResolver !== null,
|
|
65
|
+
document: documentReady
|
|
66
|
+
};
|
|
81
67
|
}
|
|
82
68
|
|
|
83
69
|
// src/registry/registry.ts
|
|
84
70
|
var kinds = /* @__PURE__ */ new Map();
|
|
85
|
-
var
|
|
71
|
+
var aliases = /* @__PURE__ */ new Map();
|
|
72
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
86
73
|
function registerNodeKind(definition) {
|
|
87
74
|
kinds.set(definition.name, definition);
|
|
75
|
+
for (const alias of definition.aliases ?? []) aliases.set(alias, definition.name);
|
|
88
76
|
notify();
|
|
89
77
|
return () => {
|
|
90
78
|
if (kinds.get(definition.name) === definition) {
|
|
91
79
|
kinds.delete(definition.name);
|
|
80
|
+
for (const alias of definition.aliases ?? []) {
|
|
81
|
+
if (aliases.get(alias) === definition.name) aliases.delete(alias);
|
|
82
|
+
}
|
|
92
83
|
notify();
|
|
93
84
|
}
|
|
94
85
|
};
|
|
95
86
|
}
|
|
87
|
+
function resolveKindId(id2) {
|
|
88
|
+
if (kinds.has(id2)) return id2;
|
|
89
|
+
const canonical = aliases.get(id2);
|
|
90
|
+
return canonical && kinds.has(canonical) ? canonical : null;
|
|
91
|
+
}
|
|
96
92
|
function getNodeKind(name) {
|
|
97
|
-
|
|
93
|
+
const canonical = resolveKindId(name);
|
|
94
|
+
return canonical ? kinds.get(canonical) ?? null : null;
|
|
95
|
+
}
|
|
96
|
+
function kindIds(kind) {
|
|
97
|
+
return [kind.name, ...kind.aliases ?? []];
|
|
98
98
|
}
|
|
99
99
|
function listNodeKinds(category) {
|
|
100
100
|
const all = Array.from(kinds.values());
|
|
101
101
|
return category ? all.filter((k) => k.category === category) : all;
|
|
102
102
|
}
|
|
103
103
|
function onNodeKindsChanged(listener) {
|
|
104
|
-
|
|
105
|
-
return () =>
|
|
104
|
+
listeners.add(listener);
|
|
105
|
+
return () => listeners.delete(listener);
|
|
106
106
|
}
|
|
107
107
|
function notify() {
|
|
108
|
-
for (const l of
|
|
108
|
+
for (const l of listeners) l();
|
|
109
109
|
}
|
|
110
110
|
function defaultConfigFor(kind) {
|
|
111
111
|
const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
|
|
@@ -219,6 +219,315 @@ function categoryAccent(category) {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/runtime/run-flow.ts
|
|
223
|
+
async function runFlow(graph, executors, onEvent = () => {
|
|
224
|
+
}, options = {}) {
|
|
225
|
+
const { signal, initialInputs = {}, timeoutMs, depth = 0 } = options;
|
|
226
|
+
const outputs = {};
|
|
227
|
+
const portValues = /* @__PURE__ */ new Map();
|
|
228
|
+
const completed = /* @__PURE__ */ new Set();
|
|
229
|
+
const errors = [];
|
|
230
|
+
const order = topoSort(graph);
|
|
231
|
+
if (order === null) {
|
|
232
|
+
const msg = "Cycle detected in flow graph \u2014 aborting.";
|
|
233
|
+
onEvent({ type: "run-error", error: msg });
|
|
234
|
+
return { ok: false, outputs, error: msg };
|
|
235
|
+
}
|
|
236
|
+
const incomingByNode = indexIncoming(graph.edges);
|
|
237
|
+
const timer2 = timeoutMs ? setTimeout(() => errors.push(`Run timed out after ${timeoutMs}ms`), timeoutMs) : null;
|
|
238
|
+
onEvent({ type: "run-start" });
|
|
239
|
+
try {
|
|
240
|
+
for (const node of order) {
|
|
241
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
242
|
+
if (errors.length) break;
|
|
243
|
+
const incoming = incomingByNode.get(node.id) ?? [];
|
|
244
|
+
if (incoming.length > 0) {
|
|
245
|
+
const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
|
|
246
|
+
if (!anyActive) {
|
|
247
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "skipped" });
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (node.type === "note") {
|
|
252
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "annotation" });
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "running" });
|
|
256
|
+
const inputs = collectInputs(node, incoming, portValues, initialInputs);
|
|
257
|
+
const exec = pickExecutor(executors, node);
|
|
258
|
+
if (!exec) {
|
|
259
|
+
const msg = `No executor registered for kind=${node.type}`;
|
|
260
|
+
errors.push(msg);
|
|
261
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
262
|
+
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const result = await Promise.resolve(
|
|
267
|
+
exec({
|
|
268
|
+
node,
|
|
269
|
+
inputs,
|
|
270
|
+
abort: (reason) => {
|
|
271
|
+
throw new Error(reason ?? "aborted");
|
|
272
|
+
},
|
|
273
|
+
emit: onEvent,
|
|
274
|
+
depth
|
|
275
|
+
})
|
|
276
|
+
);
|
|
277
|
+
outputs[node.id] = result;
|
|
278
|
+
const activated = activatedPorts(node, result);
|
|
279
|
+
for (const portId of activated.ports) {
|
|
280
|
+
portValues.set(`${node.id}:${portId}`, activated.value);
|
|
281
|
+
onEvent({ type: "node-output", nodeId: node.id, portId, value: activated.value });
|
|
282
|
+
}
|
|
283
|
+
completed.add(node.id);
|
|
284
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "done" });
|
|
285
|
+
} catch (e) {
|
|
286
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
287
|
+
errors.push(msg);
|
|
288
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
289
|
+
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} finally {
|
|
294
|
+
if (timer2) clearTimeout(timer2);
|
|
295
|
+
}
|
|
296
|
+
const ok = errors.length === 0;
|
|
297
|
+
onEvent({ type: "run-end", ok });
|
|
298
|
+
return ok ? { ok, outputs } : { ok, outputs, error: errors[0] };
|
|
299
|
+
}
|
|
300
|
+
function indexIncoming(edges) {
|
|
301
|
+
const map = /* @__PURE__ */ new Map();
|
|
302
|
+
for (const e of edges) {
|
|
303
|
+
const list = map.get(e.target) ?? [];
|
|
304
|
+
list.push(e);
|
|
305
|
+
map.set(e.target, list);
|
|
306
|
+
}
|
|
307
|
+
return map;
|
|
308
|
+
}
|
|
309
|
+
function topoSort(graph) {
|
|
310
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
311
|
+
for (const n of graph.nodes) inDegree.set(n.id, 0);
|
|
312
|
+
for (const e of graph.edges) inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);
|
|
313
|
+
const queue = [];
|
|
314
|
+
for (const [id2, d] of inDegree) if (d === 0) queue.push(id2);
|
|
315
|
+
const ordered = [];
|
|
316
|
+
while (queue.length) {
|
|
317
|
+
const id2 = queue.shift();
|
|
318
|
+
ordered.push(id2);
|
|
319
|
+
for (const e of graph.edges) {
|
|
320
|
+
if (e.source !== id2) continue;
|
|
321
|
+
const next = (inDegree.get(e.target) ?? 0) - 1;
|
|
322
|
+
inDegree.set(e.target, next);
|
|
323
|
+
if (next === 0) queue.push(e.target);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (ordered.length !== graph.nodes.length) return null;
|
|
327
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
328
|
+
return ordered.map((id2) => byId.get(id2)).filter(Boolean);
|
|
329
|
+
}
|
|
330
|
+
function collectInputs(node, incoming, portValues, initial) {
|
|
331
|
+
const inputs = { ...initial[node.id] ?? {} };
|
|
332
|
+
for (const e of incoming) {
|
|
333
|
+
const portId = e.targetHandle ?? "in";
|
|
334
|
+
const val = portValues.get(`${e.source}:${e.sourceHandle ?? "out"}`);
|
|
335
|
+
inputs[portId] = val;
|
|
336
|
+
}
|
|
337
|
+
return inputs;
|
|
338
|
+
}
|
|
339
|
+
function pickExecutor(executors, node) {
|
|
340
|
+
if (executors[node.id]) return executors[node.id];
|
|
341
|
+
if (node.type && executors[node.type]) return executors[node.type];
|
|
342
|
+
const kind = node.type ? getNodeKind(node.type) : null;
|
|
343
|
+
if (kind) {
|
|
344
|
+
for (const id2 of kindIds(kind)) {
|
|
345
|
+
if (executors[id2]) return executors[id2];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return executors["*"];
|
|
349
|
+
}
|
|
350
|
+
function activatedPorts(node, result) {
|
|
351
|
+
if (result && typeof result === "object") {
|
|
352
|
+
const r = result;
|
|
353
|
+
if (typeof r.__port === "string") {
|
|
354
|
+
return { ports: [r.__port], value: r.value };
|
|
355
|
+
}
|
|
356
|
+
if (typeof r.branch === "string") {
|
|
357
|
+
return { ports: [r.branch], value: r.value ?? r };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const kind = getNodeKind(node.data?.kind ?? node.type ?? "") ?? void 0;
|
|
361
|
+
const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);
|
|
362
|
+
return { ports: declared?.length ? declared : ["out"], value: result };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/registry/subflow.ts
|
|
366
|
+
var DEFAULT_MAX_DEPTH = 8;
|
|
367
|
+
function subflowMode(config) {
|
|
368
|
+
const mode = config.mode;
|
|
369
|
+
return mode === "stream" || mode === "both" ? mode : "output";
|
|
370
|
+
}
|
|
371
|
+
function subflowPorts(config) {
|
|
372
|
+
const mode = subflowMode(config);
|
|
373
|
+
const ports = [{ id: "out", label: "result" }];
|
|
374
|
+
if (mode === "stream" || mode === "both") ports.unshift({ id: "stream", label: "stream" });
|
|
375
|
+
return ports;
|
|
376
|
+
}
|
|
377
|
+
var subflowExecutor = async (ctx) => {
|
|
378
|
+
const config = ctx.node.data?.config ?? {};
|
|
379
|
+
const ref = String(config.workflow ?? "").trim();
|
|
380
|
+
if (!ref) ctx.abort("subflow has no workflow reference configured");
|
|
381
|
+
const resolver = getWorkflowResolver();
|
|
382
|
+
if (!resolver) {
|
|
383
|
+
ctx.abort(
|
|
384
|
+
"No workflow resolver registered. Call registerWorkflowResolver() so subflow can find the workflow it references."
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const maxDepth = Number.isFinite(config.maxDepth) ? Number(config.maxDepth) : DEFAULT_MAX_DEPTH;
|
|
388
|
+
const depth = ctx.depth ?? 0;
|
|
389
|
+
if (depth + 1 > maxDepth) {
|
|
390
|
+
ctx.abort(
|
|
391
|
+
`subflow depth limit reached (${maxDepth}) at "${ref}" \u2014 a workflow is referencing itself, directly or through a chain.`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
const child = await resolver(ref);
|
|
395
|
+
if (!child) ctx.abort(`subflow could not resolve workflow "${ref}"`);
|
|
396
|
+
const mode = subflowMode(config);
|
|
397
|
+
const streaming = mode === "stream" || mode === "both";
|
|
398
|
+
const forward = (event) => {
|
|
399
|
+
if (!streaming) return;
|
|
400
|
+
const detail = event.type === "node-status" ? `${event.nodeId} ${event.status}` : event.type === "run-end" ? `finished (${event.ok ? "ok" : "failed"})` : event.type;
|
|
401
|
+
ctx.emit({
|
|
402
|
+
type: "log",
|
|
403
|
+
nodeId: ctx.node.id,
|
|
404
|
+
level: "info",
|
|
405
|
+
message: `[${ref}] ${detail}`
|
|
406
|
+
});
|
|
407
|
+
};
|
|
408
|
+
const result = await runFlow(
|
|
409
|
+
child,
|
|
410
|
+
config.executors ?? {},
|
|
411
|
+
forward,
|
|
412
|
+
{
|
|
413
|
+
initialInputs: config.inputs ?? {
|
|
414
|
+
// With no explicit mapping, hand the parent's inputs to the child's
|
|
415
|
+
// entry points — the obvious default, and it makes the simple case
|
|
416
|
+
// require no configuration at all.
|
|
417
|
+
__parent: ctx.inputs
|
|
418
|
+
},
|
|
419
|
+
depth: depth + 1
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
if (!result.ok) {
|
|
423
|
+
ctx.abort(`subflow "${ref}" failed: ${result.error ?? "unknown error"}`);
|
|
424
|
+
}
|
|
425
|
+
if (mode === "stream") {
|
|
426
|
+
return { __port: "stream", value: result.outputs };
|
|
427
|
+
}
|
|
428
|
+
if (mode === "both") {
|
|
429
|
+
return result.outputs;
|
|
430
|
+
}
|
|
431
|
+
return { __port: "out", value: result.outputs };
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// src/registry/llm-branch.ts
|
|
435
|
+
function declaredRoutes(config) {
|
|
436
|
+
const raw = config.routes;
|
|
437
|
+
if (!Array.isArray(raw)) return [];
|
|
438
|
+
return raw.map((r) => ({ port: String(r?.port ?? "").trim(), description: r?.description })).filter((r) => r.port !== "");
|
|
439
|
+
}
|
|
440
|
+
function resolveFallbackPort(routes, fallbackEnabled) {
|
|
441
|
+
if (fallbackEnabled) return "fallback";
|
|
442
|
+
return routes[0]?.port ?? "out";
|
|
443
|
+
}
|
|
444
|
+
var llmBranchExecutor = async (ctx) => {
|
|
445
|
+
const config = ctx.node.data?.config ?? {};
|
|
446
|
+
const routes = declaredRoutes(config);
|
|
447
|
+
if (routes.length === 0) {
|
|
448
|
+
ctx.abort("llm_branch has no routes configured");
|
|
449
|
+
}
|
|
450
|
+
const client = getLlmClient();
|
|
451
|
+
if (!client) {
|
|
452
|
+
ctx.abort(
|
|
453
|
+
"No LLM client registered. Call registerLlmClient() with your provider adapter \u2014 fancy-flow ships the routing, not the model call."
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
const fallbackEnabled = config.fallback !== false;
|
|
457
|
+
const choice = await client.chooseRoute({
|
|
458
|
+
system: typeof config.system === "string" ? config.system : void 0,
|
|
459
|
+
prompt: String(config.prompt ?? ctx.inputs ?? ""),
|
|
460
|
+
routes,
|
|
461
|
+
provider: typeof config.provider === "string" ? config.provider : void 0,
|
|
462
|
+
model: typeof config.model === "string" ? config.model : void 0,
|
|
463
|
+
credential: typeof config.credential === "string" ? config.credential : void 0
|
|
464
|
+
});
|
|
465
|
+
const offered = new Set(routes.map((r) => r.port));
|
|
466
|
+
let port = choice?.port ?? "";
|
|
467
|
+
let reason = choice?.reason;
|
|
468
|
+
if (!offered.has(port)) {
|
|
469
|
+
const safe = resolveFallbackPort(routes, fallbackEnabled);
|
|
470
|
+
ctx.emit({
|
|
471
|
+
type: "log",
|
|
472
|
+
nodeId: ctx.node.id,
|
|
473
|
+
level: "warn",
|
|
474
|
+
message: `llm_branch: model returned "${port || "(nothing)"}", which is not a declared route. Routing to "${safe}".`
|
|
475
|
+
});
|
|
476
|
+
reason = reason ?? `unrecognised route "${port}"`;
|
|
477
|
+
port = safe;
|
|
478
|
+
}
|
|
479
|
+
return { __port: port, value: { route: port, reason, input: ctx.inputs } };
|
|
480
|
+
};
|
|
481
|
+
var adapter = null;
|
|
482
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
483
|
+
function registerRichInputAdapter(next) {
|
|
484
|
+
adapter = next;
|
|
485
|
+
for (const l of listeners2) l();
|
|
486
|
+
return () => {
|
|
487
|
+
if (adapter === next) {
|
|
488
|
+
adapter = null;
|
|
489
|
+
for (const l of listeners2) l();
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function getRichInputAdapter() {
|
|
494
|
+
return adapter;
|
|
495
|
+
}
|
|
496
|
+
function isRichInputEnabled() {
|
|
497
|
+
return adapter !== null && (adapter.renderDocument !== void 0 || adapter.renderEditor !== void 0);
|
|
498
|
+
}
|
|
499
|
+
function onRichInputAdapterChanged(fn) {
|
|
500
|
+
listeners2.add(fn);
|
|
501
|
+
return () => listeners2.delete(fn);
|
|
502
|
+
}
|
|
503
|
+
function RichInputPreview({ config }) {
|
|
504
|
+
const a = getRichInputAdapter();
|
|
505
|
+
const title = typeof config.title === "string" && config.title.trim() !== "" ? config.title : "Untitled step";
|
|
506
|
+
const doc = config.document;
|
|
507
|
+
if (!a || !a.renderDocument && !a.renderEditor) {
|
|
508
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
510
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview__unavailable", children: [
|
|
511
|
+
"Add ",
|
|
512
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/fancy-cms-ui" }),
|
|
513
|
+
" +",
|
|
514
|
+
" ",
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/react-fancy" }),
|
|
516
|
+
", then",
|
|
517
|
+
" ",
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: 'import "@particle-academy/fancy-flow/rich-input"' }),
|
|
519
|
+
"."
|
|
520
|
+
] })
|
|
521
|
+
] });
|
|
522
|
+
}
|
|
523
|
+
const body = doc === void 0 || doc === null ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-rich-preview__unavailable", children: "Nothing authored yet." }) : a.renderDocument?.(doc) ?? null;
|
|
524
|
+
const Frame = a.FauxClient;
|
|
525
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
526
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
527
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-rich-preview__frame", children: Frame ? /* @__PURE__ */ jsxRuntime.jsx(Frame, { ...a.frameProps ?? { variant: "browser" }, children: body }) : body })
|
|
528
|
+
] });
|
|
529
|
+
}
|
|
530
|
+
|
|
222
531
|
// node_modules/classcat/index.js
|
|
223
532
|
function cc(names) {
|
|
224
533
|
if (typeof names === "string" || typeof names === "number") return "" + names;
|
|
@@ -9276,7 +9585,8 @@ var HTTP_METHODS = [
|
|
|
9276
9585
|
var KINDS = [
|
|
9277
9586
|
// ───────────── Triggers ─────────────
|
|
9278
9587
|
{
|
|
9279
|
-
name: "manual_trigger",
|
|
9588
|
+
name: "@particle-academy/manual_trigger",
|
|
9589
|
+
aliases: ["manual_trigger", "@fancy/manual_trigger"],
|
|
9280
9590
|
category: "trigger",
|
|
9281
9591
|
label: "Manual",
|
|
9282
9592
|
description: "Entry point fired when the user clicks Run.",
|
|
@@ -9285,7 +9595,8 @@ var KINDS = [
|
|
|
9285
9595
|
outputs: [{ id: "out" }]
|
|
9286
9596
|
},
|
|
9287
9597
|
{
|
|
9288
|
-
name: "webhook_trigger",
|
|
9598
|
+
name: "@particle-academy/webhook_trigger",
|
|
9599
|
+
aliases: ["webhook_trigger", "@fancy/webhook_trigger"],
|
|
9289
9600
|
category: "trigger",
|
|
9290
9601
|
label: "Webhook",
|
|
9291
9602
|
description: "Triggered by an inbound HTTP request to a host-provided URL.",
|
|
@@ -9302,7 +9613,8 @@ var KINDS = [
|
|
|
9302
9613
|
]
|
|
9303
9614
|
},
|
|
9304
9615
|
{
|
|
9305
|
-
name: "schedule_trigger",
|
|
9616
|
+
name: "@particle-academy/schedule_trigger",
|
|
9617
|
+
aliases: ["schedule_trigger", "@fancy/schedule_trigger"],
|
|
9306
9618
|
category: "trigger",
|
|
9307
9619
|
label: "Schedule",
|
|
9308
9620
|
description: "Fires on a cron schedule (host-implemented).",
|
|
@@ -9322,7 +9634,8 @@ var KINDS = [
|
|
|
9322
9634
|
]
|
|
9323
9635
|
},
|
|
9324
9636
|
{
|
|
9325
|
-
name: "user_input",
|
|
9637
|
+
name: "@particle-academy/user_input",
|
|
9638
|
+
aliases: ["user_input", "@fancy/user_input"],
|
|
9326
9639
|
category: "human",
|
|
9327
9640
|
label: "User Input",
|
|
9328
9641
|
description: "Pause the flow until the user submits the configured form.",
|
|
@@ -9362,7 +9675,8 @@ var KINDS = [
|
|
|
9362
9675
|
]
|
|
9363
9676
|
},
|
|
9364
9677
|
{
|
|
9365
|
-
name: "rich_user_input",
|
|
9678
|
+
name: "@particle-academy/rich_user_input",
|
|
9679
|
+
aliases: ["rich_user_input", "@fancy/rich_user_input"],
|
|
9366
9680
|
category: "human",
|
|
9367
9681
|
label: "Rich User Input",
|
|
9368
9682
|
description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
|
|
@@ -9385,9 +9699,61 @@ var KINDS = [
|
|
|
9385
9699
|
// what the person hitting this step will actually see.
|
|
9386
9700
|
renderBody: (ctx) => ReactExports.createElement(RichInputPreview, { config: ctx.config ?? {} })
|
|
9387
9701
|
},
|
|
9702
|
+
{
|
|
9703
|
+
name: "@particle-academy/subflow",
|
|
9704
|
+
aliases: ["subflow", "@fancy/subflow"],
|
|
9705
|
+
category: "logic",
|
|
9706
|
+
label: "SubFlow",
|
|
9707
|
+
description: "Run another workflow and bring its result \u2014 or its live progress \u2014 back into this one.",
|
|
9708
|
+
icon: "\u29C9",
|
|
9709
|
+
inputs: [{ id: "in" }],
|
|
9710
|
+
// The stream port only exists when something actually streams.
|
|
9711
|
+
outputs: (config) => subflowPorts(config ?? {}),
|
|
9712
|
+
// Core, not marketplace: it runs a child graph through this same engine and
|
|
9713
|
+
// needs nothing from outside except where workflows live.
|
|
9714
|
+
executor: subflowExecutor,
|
|
9715
|
+
configSchema: [
|
|
9716
|
+
{
|
|
9717
|
+
type: "text",
|
|
9718
|
+
key: "workflow",
|
|
9719
|
+
label: "Workflow",
|
|
9720
|
+
required: true,
|
|
9721
|
+
placeholder: "onboarding-v2",
|
|
9722
|
+
description: "Reference resolved by the host's registerWorkflowResolver()."
|
|
9723
|
+
},
|
|
9724
|
+
{
|
|
9725
|
+
type: "select",
|
|
9726
|
+
key: "mode",
|
|
9727
|
+
label: "Return",
|
|
9728
|
+
default: "output",
|
|
9729
|
+
options: [
|
|
9730
|
+
{ value: "output", label: "Output when it finishes" },
|
|
9731
|
+
{ value: "stream", label: "Stream progress as it runs" },
|
|
9732
|
+
{ value: "both", label: "Both \u2014 stream, then output" }
|
|
9733
|
+
],
|
|
9734
|
+
description: "Streaming adds a second port so a parent can show progress instead of a spinner."
|
|
9735
|
+
},
|
|
9736
|
+
{
|
|
9737
|
+
type: "json",
|
|
9738
|
+
key: "inputs",
|
|
9739
|
+
label: "Input mapping",
|
|
9740
|
+
description: "Entry-point inputs for the child run. Omit to pass this node's inputs straight through."
|
|
9741
|
+
},
|
|
9742
|
+
{
|
|
9743
|
+
type: "number",
|
|
9744
|
+
key: "maxDepth",
|
|
9745
|
+
label: "Max nesting depth",
|
|
9746
|
+
default: DEFAULT_MAX_DEPTH,
|
|
9747
|
+
min: 1,
|
|
9748
|
+
max: 32,
|
|
9749
|
+
description: "Guards against a workflow referencing itself."
|
|
9750
|
+
}
|
|
9751
|
+
]
|
|
9752
|
+
},
|
|
9388
9753
|
// ───────────── Logic ─────────────
|
|
9389
9754
|
{
|
|
9390
|
-
name: "branch",
|
|
9755
|
+
name: "@particle-academy/branch",
|
|
9756
|
+
aliases: ["branch", "@fancy/branch"],
|
|
9391
9757
|
category: "logic",
|
|
9392
9758
|
label: "Branch",
|
|
9393
9759
|
description: "Multi-way branch on a condition or value.",
|
|
@@ -9399,7 +9765,8 @@ var KINDS = [
|
|
|
9399
9765
|
]
|
|
9400
9766
|
},
|
|
9401
9767
|
{
|
|
9402
|
-
name: "switch_case",
|
|
9768
|
+
name: "@particle-academy/switch_case",
|
|
9769
|
+
aliases: ["switch_case", "@fancy/switch_case"],
|
|
9403
9770
|
category: "logic",
|
|
9404
9771
|
label: "Switch",
|
|
9405
9772
|
description: "Route to one of N labelled outputs based on a key.",
|
|
@@ -9426,7 +9793,8 @@ var KINDS = [
|
|
|
9426
9793
|
]
|
|
9427
9794
|
},
|
|
9428
9795
|
{
|
|
9429
|
-
name: "for_each",
|
|
9796
|
+
name: "@particle-academy/for_each",
|
|
9797
|
+
aliases: ["for_each", "@fancy/for_each"],
|
|
9430
9798
|
category: "logic",
|
|
9431
9799
|
label: "For Each",
|
|
9432
9800
|
description: "Iterate over a list, emitting each item on `item`.",
|
|
@@ -9439,7 +9807,8 @@ var KINDS = [
|
|
|
9439
9807
|
]
|
|
9440
9808
|
},
|
|
9441
9809
|
{
|
|
9442
|
-
name: "merge",
|
|
9810
|
+
name: "@particle-academy/merge",
|
|
9811
|
+
aliases: ["merge", "@fancy/merge"],
|
|
9443
9812
|
category: "logic",
|
|
9444
9813
|
label: "Merge",
|
|
9445
9814
|
description: "Combine multiple inputs into one object or array.",
|
|
@@ -9457,7 +9826,8 @@ var KINDS = [
|
|
|
9457
9826
|
]
|
|
9458
9827
|
},
|
|
9459
9828
|
{
|
|
9460
|
-
name: "wait",
|
|
9829
|
+
name: "@particle-academy/wait",
|
|
9830
|
+
aliases: ["wait", "@fancy/wait"],
|
|
9461
9831
|
category: "logic",
|
|
9462
9832
|
label: "Wait",
|
|
9463
9833
|
description: "Sleep or wait for an external event.",
|
|
@@ -9474,7 +9844,8 @@ var KINDS = [
|
|
|
9474
9844
|
]
|
|
9475
9845
|
},
|
|
9476
9846
|
{
|
|
9477
|
-
name: "transform",
|
|
9847
|
+
name: "@particle-academy/transform",
|
|
9848
|
+
aliases: ["transform", "@fancy/transform"],
|
|
9478
9849
|
category: "logic",
|
|
9479
9850
|
label: "Transform",
|
|
9480
9851
|
description: "Reshape data with an expression.",
|
|
@@ -9491,7 +9862,8 @@ var KINDS = [
|
|
|
9491
9862
|
},
|
|
9492
9863
|
// ───────────── Data ─────────────
|
|
9493
9864
|
{
|
|
9494
|
-
name: "memory_store",
|
|
9865
|
+
name: "@particle-academy/memory_store",
|
|
9866
|
+
aliases: ["memory_store", "@fancy/memory_store"],
|
|
9495
9867
|
category: "data",
|
|
9496
9868
|
label: "Memory Store",
|
|
9497
9869
|
description: "Read or write per-conversation memory.",
|
|
@@ -9511,7 +9883,8 @@ var KINDS = [
|
|
|
9511
9883
|
]
|
|
9512
9884
|
},
|
|
9513
9885
|
{
|
|
9514
|
-
name: "data_store",
|
|
9886
|
+
name: "@particle-academy/data_store",
|
|
9887
|
+
aliases: ["data_store", "@fancy/data_store"],
|
|
9515
9888
|
category: "data",
|
|
9516
9889
|
label: "Data Store",
|
|
9517
9890
|
description: "Key-value or table read/write against a host store.",
|
|
@@ -9539,7 +9912,8 @@ var KINDS = [
|
|
|
9539
9912
|
]
|
|
9540
9913
|
},
|
|
9541
9914
|
{
|
|
9542
|
-
name: "variable",
|
|
9915
|
+
name: "@particle-academy/variable",
|
|
9916
|
+
aliases: ["variable", "@fancy/variable"],
|
|
9543
9917
|
category: "data",
|
|
9544
9918
|
label: "Variable",
|
|
9545
9919
|
description: "Workflow-scoped value used by other nodes.",
|
|
@@ -9551,7 +9925,8 @@ var KINDS = [
|
|
|
9551
9925
|
},
|
|
9552
9926
|
// ───────────── AI ─────────────
|
|
9553
9927
|
{
|
|
9554
|
-
name: "llm_call",
|
|
9928
|
+
name: "@particle-academy/llm_call",
|
|
9929
|
+
aliases: ["llm_call", "@fancy/llm_call"],
|
|
9555
9930
|
category: "ai",
|
|
9556
9931
|
label: "LLM Call",
|
|
9557
9932
|
description: "Send a prompt + context to a model and receive a response.",
|
|
@@ -9578,7 +9953,8 @@ var KINDS = [
|
|
|
9578
9953
|
]
|
|
9579
9954
|
},
|
|
9580
9955
|
{
|
|
9581
|
-
name: "llm_branch",
|
|
9956
|
+
name: "@particle-academy/llm_branch",
|
|
9957
|
+
aliases: ["llm_branch", "@fancy/llm_branch"],
|
|
9582
9958
|
category: "ai",
|
|
9583
9959
|
label: "LLM Router",
|
|
9584
9960
|
description: "Let a model choose which route the flow takes.",
|
|
@@ -9587,6 +9963,10 @@ var KINDS = [
|
|
|
9587
9963
|
// Each declared route is a port. The executor returns `{ __port: id }`
|
|
9588
9964
|
// (or `Port.only(id)` on the PHP runtime) to pick one.
|
|
9589
9965
|
outputs: (config) => routePorts(config?.routes, config?.fallback),
|
|
9966
|
+
// A shuttle, not an engine: it carries the routes out to whatever LLM
|
|
9967
|
+
// client the host registered and carries the choice back. No provider SDK
|
|
9968
|
+
// reaches core, so this stays a builtin without adding a dependency.
|
|
9969
|
+
executor: llmBranchExecutor,
|
|
9590
9970
|
configSchema: [
|
|
9591
9971
|
{
|
|
9592
9972
|
type: "textarea",
|
|
@@ -9648,7 +10028,8 @@ var KINDS = [
|
|
|
9648
10028
|
]
|
|
9649
10029
|
},
|
|
9650
10030
|
{
|
|
9651
|
-
name: "tool_use",
|
|
10031
|
+
name: "@particle-academy/tool_use",
|
|
10032
|
+
aliases: ["tool_use", "@fancy/tool_use"],
|
|
9652
10033
|
category: "ai",
|
|
9653
10034
|
label: "Tool Use",
|
|
9654
10035
|
description: "Hand control to a host-registered tool by name.",
|
|
@@ -9659,7 +10040,8 @@ var KINDS = [
|
|
|
9659
10040
|
]
|
|
9660
10041
|
},
|
|
9661
10042
|
{
|
|
9662
|
-
name: "embed_search",
|
|
10043
|
+
name: "@particle-academy/embed_search",
|
|
10044
|
+
aliases: ["embed_search", "@fancy/embed_search"],
|
|
9663
10045
|
category: "ai",
|
|
9664
10046
|
label: "Embed & Search",
|
|
9665
10047
|
description: "Embed a query and search a vector store.",
|
|
@@ -9672,7 +10054,8 @@ var KINDS = [
|
|
|
9672
10054
|
},
|
|
9673
10055
|
// ───────────── IO ─────────────
|
|
9674
10056
|
{
|
|
9675
|
-
name: "api_request",
|
|
10057
|
+
name: "@particle-academy/api_request",
|
|
10058
|
+
aliases: ["api_request", "@fancy/api_request"],
|
|
9676
10059
|
category: "io",
|
|
9677
10060
|
label: "API Request",
|
|
9678
10061
|
description: "HTTP request to any URL.",
|
|
@@ -9686,7 +10069,8 @@ var KINDS = [
|
|
|
9686
10069
|
]
|
|
9687
10070
|
},
|
|
9688
10071
|
{
|
|
9689
|
-
name: "webhook_out",
|
|
10072
|
+
name: "@particle-academy/webhook_out",
|
|
10073
|
+
aliases: ["webhook_out", "@fancy/webhook_out"],
|
|
9690
10074
|
category: "io",
|
|
9691
10075
|
label: "Send Webhook",
|
|
9692
10076
|
description: "POST a payload to a configured URL.",
|
|
@@ -9699,7 +10083,8 @@ var KINDS = [
|
|
|
9699
10083
|
},
|
|
9700
10084
|
// ───────────── Human ─────────────
|
|
9701
10085
|
{
|
|
9702
|
-
name: "human_approval",
|
|
10086
|
+
name: "@particle-academy/human_approval",
|
|
10087
|
+
aliases: ["human_approval", "@fancy/human_approval"],
|
|
9703
10088
|
category: "human",
|
|
9704
10089
|
label: "Human Approval",
|
|
9705
10090
|
description: "Pause until a human approves or denies.",
|
|
@@ -9713,7 +10098,8 @@ var KINDS = [
|
|
|
9713
10098
|
]
|
|
9714
10099
|
},
|
|
9715
10100
|
{
|
|
9716
|
-
name: "notify",
|
|
10101
|
+
name: "@particle-academy/notify",
|
|
10102
|
+
aliases: ["notify", "@fancy/notify"],
|
|
9717
10103
|
category: "human",
|
|
9718
10104
|
label: "Notify",
|
|
9719
10105
|
description: "Send a message via Slack / email / SMS / etc.",
|
|
@@ -9737,7 +10123,8 @@ var KINDS = [
|
|
|
9737
10123
|
},
|
|
9738
10124
|
// ───────────── Output ─────────────
|
|
9739
10125
|
{
|
|
9740
|
-
name: "output",
|
|
10126
|
+
name: "@particle-academy/output",
|
|
10127
|
+
aliases: ["output", "@fancy/output"],
|
|
9741
10128
|
category: "output",
|
|
9742
10129
|
label: "Output",
|
|
9743
10130
|
description: "Terminal node \u2014 captures the workflow's result.",
|
|
@@ -9746,7 +10133,8 @@ var KINDS = [
|
|
|
9746
10133
|
outputs: []
|
|
9747
10134
|
},
|
|
9748
10135
|
{
|
|
9749
|
-
name: "log",
|
|
10136
|
+
name: "@particle-academy/log",
|
|
10137
|
+
aliases: ["log", "@fancy/log"],
|
|
9750
10138
|
category: "output",
|
|
9751
10139
|
label: "Log",
|
|
9752
10140
|
description: "Send to the run feed.",
|
|
@@ -9773,28 +10161,44 @@ var BUILTIN_KINDS = KINDS;
|
|
|
9773
10161
|
// src/registry/index.ts
|
|
9774
10162
|
function buildNodeTypes() {
|
|
9775
10163
|
const map = {};
|
|
9776
|
-
for (const k of listNodeKinds())
|
|
10164
|
+
for (const k of listNodeKinds()) {
|
|
10165
|
+
for (const id2 of kindIds(k)) map[id2] = RegistryNode;
|
|
10166
|
+
}
|
|
9777
10167
|
return map;
|
|
9778
10168
|
}
|
|
9779
10169
|
|
|
9780
10170
|
exports.BUILTIN_KINDS = BUILTIN_KINDS;
|
|
10171
|
+
exports.DEFAULT_MAX_DEPTH = DEFAULT_MAX_DEPTH;
|
|
9781
10172
|
exports.RegistryNode = RegistryNode;
|
|
9782
10173
|
exports.RichInputPreview = RichInputPreview;
|
|
9783
10174
|
exports.buildNodeTypes = buildNodeTypes;
|
|
10175
|
+
exports.capabilityStatus = capabilityStatus;
|
|
9784
10176
|
exports.categoryAccent = categoryAccent;
|
|
10177
|
+
exports.declaredRoutes = declaredRoutes;
|
|
9785
10178
|
exports.defaultConfigFor = defaultConfigFor;
|
|
10179
|
+
exports.getLlmClient = getLlmClient;
|
|
9786
10180
|
exports.getNodeKind = getNodeKind;
|
|
9787
10181
|
exports.getRichInputAdapter = getRichInputAdapter;
|
|
10182
|
+
exports.getWorkflowResolver = getWorkflowResolver;
|
|
9788
10183
|
exports.isRichInputEnabled = isRichInputEnabled;
|
|
10184
|
+
exports.kindIds = kindIds;
|
|
9789
10185
|
exports.listNodeKinds = listNodeKinds;
|
|
10186
|
+
exports.llmBranchExecutor = llmBranchExecutor;
|
|
9790
10187
|
exports.nodeConfig = nodeConfig;
|
|
9791
10188
|
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
9792
10189
|
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
9793
10190
|
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
10191
|
+
exports.registerLlmClient = registerLlmClient;
|
|
9794
10192
|
exports.registerNodeKind = registerNodeKind;
|
|
9795
10193
|
exports.registerRichInputAdapter = registerRichInputAdapter;
|
|
10194
|
+
exports.registerWorkflowResolver = registerWorkflowResolver;
|
|
10195
|
+
exports.resolveFallbackPort = resolveFallbackPort;
|
|
10196
|
+
exports.resolveKindId = resolveKindId;
|
|
9796
10197
|
exports.resolveNodePorts = resolveNodePorts;
|
|
9797
10198
|
exports.resolvePortSpec = resolvePortSpec;
|
|
10199
|
+
exports.subflowExecutor = subflowExecutor;
|
|
10200
|
+
exports.subflowMode = subflowMode;
|
|
10201
|
+
exports.subflowPorts = subflowPorts;
|
|
9798
10202
|
exports.validateConfig = validateConfig;
|
|
9799
10203
|
//# sourceMappingURL=registry.cjs.map
|
|
9800
10204
|
//# sourceMappingURL=registry.cjs.map
|