@elizaos/plugin-workflow 2.0.0-beta.1
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/README.md +71 -0
- package/auto-enable.ts +18 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/workflow.d.ts +23 -0
- package/dist/actions/workflow.d.ts.map +1 -0
- package/dist/actions/workflow.js +425 -0
- package/dist/actions/workflow.js.map +1 -0
- package/dist/data/defaultNodes.json +9887 -0
- package/dist/data/schemaIndex.json +1 -0
- package/dist/data/triggerSchemaIndex.json +1 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +2 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +588 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +59 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/automations-builder.d.ts +21 -0
- package/dist/lib/automations-builder.d.ts.map +1 -0
- package/dist/lib/automations-builder.js +557 -0
- package/dist/lib/automations-builder.js.map +1 -0
- package/dist/lib/automations-types.d.ts +153 -0
- package/dist/lib/automations-types.d.ts.map +1 -0
- package/dist/lib/automations-types.js +191 -0
- package/dist/lib/automations-types.js.map +1 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/legacy-task-migration.d.ts +20 -0
- package/dist/lib/legacy-task-migration.d.ts.map +1 -0
- package/dist/lib/legacy-task-migration.js +110 -0
- package/dist/lib/legacy-task-migration.js.map +1 -0
- package/dist/lib/legacy-text-trigger-migration.d.ts +18 -0
- package/dist/lib/legacy-text-trigger-migration.d.ts.map +1 -0
- package/dist/lib/legacy-text-trigger-migration.js +131 -0
- package/dist/lib/legacy-text-trigger-migration.js.map +1 -0
- package/dist/lib/workflow-clarification.d.ts +113 -0
- package/dist/lib/workflow-clarification.d.ts.map +1 -0
- package/dist/lib/workflow-clarification.js +425 -0
- package/dist/lib/workflow-clarification.js.map +1 -0
- package/dist/plugin-routes.d.ts +9 -0
- package/dist/plugin-routes.d.ts.map +1 -0
- package/dist/plugin-routes.js +147 -0
- package/dist/plugin-routes.js.map +1 -0
- package/dist/providers/activeWorkflows.d.ts +11 -0
- package/dist/providers/activeWorkflows.d.ts.map +1 -0
- package/dist/providers/activeWorkflows.js +72 -0
- package/dist/providers/activeWorkflows.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/pendingDraft.d.ts +9 -0
- package/dist/providers/pendingDraft.d.ts.map +1 -0
- package/dist/providers/pendingDraft.js +48 -0
- package/dist/providers/pendingDraft.js.map +1 -0
- package/dist/providers/workflowStatus.d.ts +3 -0
- package/dist/providers/workflowStatus.d.ts.map +1 -0
- package/dist/providers/workflowStatus.js +69 -0
- package/dist/providers/workflowStatus.js.map +1 -0
- package/dist/register-routes.d.ts +2 -0
- package/dist/register-routes.d.ts.map +1 -0
- package/dist/register-routes.js +6 -0
- package/dist/register-routes.js.map +1 -0
- package/dist/routes/_helpers.d.ts +11 -0
- package/dist/routes/_helpers.d.ts.map +1 -0
- package/dist/routes/_helpers.js +22 -0
- package/dist/routes/_helpers.js.map +1 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/automations.d.ts.map +1 -0
- package/dist/routes/automations.js +32 -0
- package/dist/routes/automations.js.map +1 -0
- package/dist/routes/embedded-webhooks.d.ts +3 -0
- package/dist/routes/embedded-webhooks.d.ts.map +1 -0
- package/dist/routes/embedded-webhooks.js +47 -0
- package/dist/routes/embedded-webhooks.js.map +1 -0
- package/dist/routes/executions.d.ts +3 -0
- package/dist/routes/executions.d.ts.map +1 -0
- package/dist/routes/executions.js +58 -0
- package/dist/routes/executions.js.map +1 -0
- package/dist/routes/index.d.ts +4 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +14 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/nodes.d.ts +3 -0
- package/dist/routes/nodes.d.ts.map +1 -0
- package/dist/routes/nodes.js +168 -0
- package/dist/routes/nodes.js.map +1 -0
- package/dist/routes/validation.d.ts +3 -0
- package/dist/routes/validation.d.ts.map +1 -0
- package/dist/routes/validation.js +41 -0
- package/dist/routes/validation.js.map +1 -0
- package/dist/routes/workflow-routes.d.ts +27 -0
- package/dist/routes/workflow-routes.d.ts.map +1 -0
- package/dist/routes/workflow-routes.js +326 -0
- package/dist/routes/workflow-routes.js.map +1 -0
- package/dist/routes/workflows.d.ts +3 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +252 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/schemas/draftIntent.d.ts +22 -0
- package/dist/schemas/draftIntent.d.ts.map +1 -0
- package/dist/schemas/draftIntent.js +22 -0
- package/dist/schemas/draftIntent.js.map +1 -0
- package/dist/schemas/feasibility.d.ts +13 -0
- package/dist/schemas/feasibility.d.ts.map +1 -0
- package/dist/schemas/feasibility.js +9 -0
- package/dist/schemas/feasibility.js.map +1 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/keywordExtraction.d.ts +14 -0
- package/dist/schemas/keywordExtraction.d.ts.map +1 -0
- package/dist/schemas/keywordExtraction.js +12 -0
- package/dist/schemas/keywordExtraction.js.map +1 -0
- package/dist/schemas/workflowMatching.d.ts +36 -0
- package/dist/schemas/workflowMatching.d.ts.map +1 -0
- package/dist/schemas/workflowMatching.js +30 -0
- package/dist/schemas/workflowMatching.js.map +1 -0
- package/dist/services/embedded-workflow-service.d.ts +106 -0
- package/dist/services/embedded-workflow-service.d.ts.map +1 -0
- package/dist/services/embedded-workflow-service.js +1900 -0
- package/dist/services/embedded-workflow-service.js.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +5 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/workflow-credential-store.d.ts +27 -0
- package/dist/services/workflow-credential-store.d.ts.map +1 -0
- package/dist/services/workflow-credential-store.js +92 -0
- package/dist/services/workflow-credential-store.js.map +1 -0
- package/dist/services/workflow-dispatch.d.ts +41 -0
- package/dist/services/workflow-dispatch.d.ts.map +1 -0
- package/dist/services/workflow-dispatch.js +86 -0
- package/dist/services/workflow-dispatch.js.map +1 -0
- package/dist/services/workflow-service.d.ts +63 -0
- package/dist/services/workflow-service.d.ts.map +1 -0
- package/dist/services/workflow-service.js +492 -0
- package/dist/services/workflow-service.js.map +1 -0
- package/dist/trigger-routes.d.ts +153 -0
- package/dist/trigger-routes.d.ts.map +1 -0
- package/dist/trigger-routes.js +424 -0
- package/dist/trigger-routes.js.map +1 -0
- package/dist/types/index.d.ts +457 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +59 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/catalog.d.ts +16 -0
- package/dist/utils/catalog.d.ts.map +1 -0
- package/dist/utils/catalog.js +211 -0
- package/dist/utils/catalog.js.map +1 -0
- package/dist/utils/clarification.d.ts +17 -0
- package/dist/utils/clarification.d.ts.map +1 -0
- package/dist/utils/clarification.js +46 -0
- package/dist/utils/clarification.js.map +1 -0
- package/dist/utils/context.d.ts +4 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +18 -0
- package/dist/utils/context.js.map +1 -0
- package/dist/utils/credentialResolver.d.ts +22 -0
- package/dist/utils/credentialResolver.d.ts.map +1 -0
- package/dist/utils/credentialResolver.js +146 -0
- package/dist/utils/credentialResolver.js.map +1 -0
- package/dist/utils/generation.d.ts +36 -0
- package/dist/utils/generation.d.ts.map +1 -0
- package/dist/utils/generation.js +701 -0
- package/dist/utils/generation.js.map +1 -0
- package/dist/utils/host-capabilities.d.ts +27 -0
- package/dist/utils/host-capabilities.d.ts.map +1 -0
- package/dist/utils/host-capabilities.js +59 -0
- package/dist/utils/host-capabilities.js.map +1 -0
- package/dist/utils/inferSyntheticOutputSchema.d.ts +20 -0
- package/dist/utils/inferSyntheticOutputSchema.d.ts.map +1 -0
- package/dist/utils/inferSyntheticOutputSchema.js +151 -0
- package/dist/utils/inferSyntheticOutputSchema.js.map +1 -0
- package/dist/utils/outputSchema.d.ts +26 -0
- package/dist/utils/outputSchema.d.ts.map +1 -0
- package/dist/utils/outputSchema.js +297 -0
- package/dist/utils/outputSchema.js.map +1 -0
- package/dist/utils/validateAndRepair.d.ts +41 -0
- package/dist/utils/validateAndRepair.d.ts.map +1 -0
- package/dist/utils/validateAndRepair.js +483 -0
- package/dist/utils/validateAndRepair.js.map +1 -0
- package/dist/utils/workflow-prompts/actionResponse.d.ts +2 -0
- package/dist/utils/workflow-prompts/actionResponse.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/actionResponse.js +17 -0
- package/dist/utils/workflow-prompts/actionResponse.js.map +1 -0
- package/dist/utils/workflow-prompts/draftIntent.d.ts +2 -0
- package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/draftIntent.js +23 -0
- package/dist/utils/workflow-prompts/draftIntent.js.map +1 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.d.ts +2 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.js +21 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.js.map +1 -0
- package/dist/utils/workflow-prompts/fieldCorrection.d.ts +3 -0
- package/dist/utils/workflow-prompts/fieldCorrection.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/fieldCorrection.js +20 -0
- package/dist/utils/workflow-prompts/fieldCorrection.js.map +1 -0
- package/dist/utils/workflow-prompts/index.d.ts +8 -0
- package/dist/utils/workflow-prompts/index.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/index.js +8 -0
- package/dist/utils/workflow-prompts/index.js.map +1 -0
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts +2 -0
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/keywordExtraction.js +21 -0
- package/dist/utils/workflow-prompts/keywordExtraction.js.map +1 -0
- package/dist/utils/workflow-prompts/parameterCorrection.d.ts +3 -0
- package/dist/utils/workflow-prompts/parameterCorrection.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/parameterCorrection.js +29 -0
- package/dist/utils/workflow-prompts/parameterCorrection.js.map +1 -0
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts +2 -0
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/workflowGeneration.js +529 -0
- package/dist/utils/workflow-prompts/workflowGeneration.js.map +1 -0
- package/dist/utils/workflow-prompts/workflowMatching.d.ts +2 -0
- package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/workflowMatching.js +23 -0
- package/dist/utils/workflow-prompts/workflowMatching.js.map +1 -0
- package/dist/utils/workflow.d.ts +62 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +712 -0
- package/dist/utils/workflow.js.map +1 -0
- package/package.json +87 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/workflow.ts +494 -0
- package/src/data/defaultNodes.json +9887 -0
- package/src/data/schemaIndex.json +1 -0
- package/src/data/triggerSchemaIndex.json +1 -0
- package/src/db/index.ts +8 -0
- package/src/db/schema.ts +94 -0
- package/src/index.ts +179 -0
- package/src/lib/automations-builder.ts +679 -0
- package/src/lib/automations-types.ts +391 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/legacy-task-migration.ts +143 -0
- package/src/lib/legacy-text-trigger-migration.ts +178 -0
- package/src/lib/workflow-clarification.ts +497 -0
- package/src/plugin-routes.ts +164 -0
- package/src/providers/activeWorkflows.ts +81 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/pendingDraft.ts +55 -0
- package/src/providers/workflowStatus.ts +88 -0
- package/src/register-routes.ts +6 -0
- package/src/routes/_helpers.ts +27 -0
- package/src/routes/automations.ts +46 -0
- package/src/routes/embedded-webhooks.ts +64 -0
- package/src/routes/executions.ts +75 -0
- package/src/routes/index.ts +16 -0
- package/src/routes/nodes.ts +211 -0
- package/src/routes/validation.ts +51 -0
- package/src/routes/workflow-routes.ts +469 -0
- package/src/routes/workflows.ts +310 -0
- package/src/schemas/draftIntent.ts +21 -0
- package/src/schemas/feasibility.ts +8 -0
- package/src/schemas/index.ts +4 -0
- package/src/schemas/keywordExtraction.ts +11 -0
- package/src/schemas/workflowMatching.ts +29 -0
- package/src/services/embedded-workflow-service.ts +2224 -0
- package/src/services/index.ts +17 -0
- package/src/services/workflow-credential-store.ts +132 -0
- package/src/services/workflow-dispatch.ts +121 -0
- package/src/services/workflow-service.ts +839 -0
- package/src/trigger-routes.ts +714 -0
- package/src/types/index.ts +562 -0
- package/src/utils/catalog.ts +260 -0
- package/src/utils/clarification.ts +52 -0
- package/src/utils/context.ts +22 -0
- package/src/utils/credentialResolver.ts +234 -0
- package/src/utils/generation.ts +987 -0
- package/src/utils/host-capabilities.ts +81 -0
- package/src/utils/inferSyntheticOutputSchema.ts +163 -0
- package/src/utils/outputSchema.ts +372 -0
- package/src/utils/validateAndRepair.ts +610 -0
- package/src/utils/workflow-prompts/actionResponse.ts +16 -0
- package/src/utils/workflow-prompts/draftIntent.ts +22 -0
- package/src/utils/workflow-prompts/feasibilityCheck.ts +20 -0
- package/src/utils/workflow-prompts/fieldCorrection.ts +20 -0
- package/src/utils/workflow-prompts/index.ts +10 -0
- package/src/utils/workflow-prompts/keywordExtraction.ts +20 -0
- package/src/utils/workflow-prompts/parameterCorrection.ts +29 -0
- package/src/utils/workflow-prompts/workflowGeneration.ts +528 -0
- package/src/utils/workflow-prompts/workflowMatching.ts +22 -0
- package/src/utils/workflow.ts +895 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host capability detection for the embedded workflow engine.
|
|
3
|
+
*
|
|
4
|
+
* Different host environments have different ceilings: Cloudflare Workers
|
|
5
|
+
* have no fs/childProcess/long-running process; iOS/Android (Capacitor) have
|
|
6
|
+
* no fs/childProcess and no public inbound HTTP without a tunnel; browsers
|
|
7
|
+
* have neither fs nor inbound. The engine uses these to refuse activation
|
|
8
|
+
* of workflows whose nodes need capabilities the host can't provide, with
|
|
9
|
+
* an actionable message suggesting the right remediation (paired Eliza
|
|
10
|
+
* Cloud, plugin-tunnel, or running on a server agent).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface HostCapabilities {
|
|
14
|
+
/** Read/write filesystem via node:fs (or equivalent). */
|
|
15
|
+
fs: boolean;
|
|
16
|
+
/** Can receive inbound HTTP from the public internet (host a webhook). */
|
|
17
|
+
inbound: boolean;
|
|
18
|
+
/** Host process stays alive across schedule firings (vs short-lived). */
|
|
19
|
+
longRunning: boolean;
|
|
20
|
+
/** Spawns child processes via node:child_process. */
|
|
21
|
+
childProcess: boolean;
|
|
22
|
+
/** Raw TCP/UDP sockets via node:net (vs only fetch). */
|
|
23
|
+
net: boolean;
|
|
24
|
+
/** Human-readable host label for error messages. */
|
|
25
|
+
label: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const navigator: { userAgent?: string } | undefined;
|
|
29
|
+
|
|
30
|
+
export function detectHostCapabilities(): HostCapabilities {
|
|
31
|
+
// Cloudflare Workers — runtime exposes navigator.userAgent === 'Cloudflare-Workers'.
|
|
32
|
+
if (
|
|
33
|
+
typeof navigator !== 'undefined' &&
|
|
34
|
+
typeof navigator?.userAgent === 'string' &&
|
|
35
|
+
navigator.userAgent.includes('Cloudflare-Workers')
|
|
36
|
+
) {
|
|
37
|
+
return {
|
|
38
|
+
fs: false,
|
|
39
|
+
inbound: true, // Workers handle HTTP — webhook nodes can run if scheduling is the worker cron
|
|
40
|
+
longRunning: false,
|
|
41
|
+
childProcess: false,
|
|
42
|
+
net: false,
|
|
43
|
+
label: 'Cloudflare Worker',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Capacitor (iOS / Android). Capacitor exposes a global on the window/globalThis.
|
|
48
|
+
const capacitor: unknown = Reflect.get(globalThis, 'Capacitor');
|
|
49
|
+
if (capacitor && typeof capacitor === 'object') {
|
|
50
|
+
return {
|
|
51
|
+
fs: false,
|
|
52
|
+
inbound: false, // No public HTTP without plugin-tunnel
|
|
53
|
+
longRunning: true, // App stays alive while running; bg runner handles wake-ups
|
|
54
|
+
childProcess: false,
|
|
55
|
+
net: false,
|
|
56
|
+
label: 'Mobile (Capacitor)',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Browser without Capacitor — pure web.
|
|
61
|
+
if (typeof window !== 'undefined' && typeof process === 'undefined') {
|
|
62
|
+
return {
|
|
63
|
+
fs: false,
|
|
64
|
+
inbound: false,
|
|
65
|
+
longRunning: true,
|
|
66
|
+
childProcess: false,
|
|
67
|
+
net: false,
|
|
68
|
+
label: 'Browser',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Node — server / desktop. Full power.
|
|
73
|
+
return {
|
|
74
|
+
fs: true,
|
|
75
|
+
inbound: true,
|
|
76
|
+
longRunning: true,
|
|
77
|
+
childProcess: true,
|
|
78
|
+
net: true,
|
|
79
|
+
label: 'Node',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthetic output-schema inference for nodes whose output keys are
|
|
3
|
+
* deterministically derivable from their parameters (Summarize, Set, etc.).
|
|
4
|
+
* For nodes with arbitrary user-defined output (Code, Function), returns
|
|
5
|
+
* null — callers should skip downstream field validation rather than
|
|
6
|
+
* false-error.
|
|
7
|
+
*
|
|
8
|
+
* Returning null = "unknowable, do not validate". Returning an empty array
|
|
9
|
+
* also means "unknowable" but signals the caller can warn loudly. We use
|
|
10
|
+
* null (skip) for Code/Function and a populated array for Summarize/Set.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { WorkflowNode } from '../types/index';
|
|
14
|
+
|
|
15
|
+
/** workflows Summarize node aggregation → output prefix. Verified against actual
|
|
16
|
+
* workflows output during Session 20 dogfood (concatenate→concatenated_<field>,
|
|
17
|
+
* count→count_<field>). Update this map when new aggregations show up. */
|
|
18
|
+
const SUMMARIZE_AGG_PREFIX: Record<string, string> = {
|
|
19
|
+
concatenate: 'concatenated',
|
|
20
|
+
count: 'count',
|
|
21
|
+
countUnique: 'uniqueCount',
|
|
22
|
+
sum: 'sum',
|
|
23
|
+
average: 'average',
|
|
24
|
+
min: 'min',
|
|
25
|
+
max: 'max',
|
|
26
|
+
first: 'first',
|
|
27
|
+
last: 'last',
|
|
28
|
+
append: 'appended',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** Returns top-level output field names a Summarize node will emit, derived
|
|
32
|
+
* from its `fieldsToSummarize.values[]` parameter. */
|
|
33
|
+
function inferSummarizeFields(node: WorkflowNode): string[] | null {
|
|
34
|
+
const fields = (node.parameters as Record<string, unknown> | undefined)?.fieldsToSummarize as
|
|
35
|
+
| { values?: Array<{ aggregation?: string; field?: string }> }
|
|
36
|
+
| undefined;
|
|
37
|
+
if (!fields?.values || !Array.isArray(fields.values)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const out: string[] = [];
|
|
41
|
+
for (const entry of fields.values) {
|
|
42
|
+
if (typeof entry?.aggregation !== 'string' || typeof entry?.field !== 'string') {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const prefix = SUMMARIZE_AGG_PREFIX[entry.aggregation];
|
|
46
|
+
if (!prefix) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
out.push(`${prefix}_${entry.field}`);
|
|
50
|
+
}
|
|
51
|
+
return out.length > 0 ? out : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Returns field names a Set / EditFields node will emit, derived from
|
|
55
|
+
* `assignments.assignments[]` (modern Set node) or `values.<type>[]`
|
|
56
|
+
* (legacy Set node). */
|
|
57
|
+
function inferSetFields(node: WorkflowNode): string[] | null {
|
|
58
|
+
const params = node.parameters as Record<string, unknown> | undefined;
|
|
59
|
+
if (!params) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Modern Set / EditFields shape: assignments.assignments[i].name
|
|
64
|
+
const modern = params.assignments as { assignments?: Array<{ name?: string }> } | undefined;
|
|
65
|
+
if (modern?.assignments && Array.isArray(modern.assignments)) {
|
|
66
|
+
const names = modern.assignments
|
|
67
|
+
.map((a) => a?.name)
|
|
68
|
+
.filter((n): n is string => typeof n === 'string' && n.length > 0);
|
|
69
|
+
if (names.length > 0) {
|
|
70
|
+
return names;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Legacy Set shape: values.{string,number,boolean}[i].name
|
|
75
|
+
const legacy = params.values as Record<string, Array<{ name?: string }>> | undefined;
|
|
76
|
+
if (legacy && typeof legacy === 'object') {
|
|
77
|
+
const names: string[] = [];
|
|
78
|
+
for (const arr of Object.values(legacy)) {
|
|
79
|
+
if (!Array.isArray(arr)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (const v of arr) {
|
|
83
|
+
if (typeof v?.name === 'string' && v.name.length > 0) {
|
|
84
|
+
names.push(v.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (names.length > 0) {
|
|
89
|
+
return names;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Gmail's `simple: true` mode flattens the response with PascalCase header
|
|
98
|
+
* fields (`Subject`, `From`, `To`, `Date`, ...) — distinct from the static
|
|
99
|
+
* schema in `schemaIndex.json` which captures the non-simple shape with
|
|
100
|
+
* lowercase `subject` etc. Without this override, validateAndRepair sees
|
|
101
|
+
* the static schema, decides `field: "subject"` is valid, and fails to
|
|
102
|
+
* catch the LLM's lowercase pick — Summarize then aggregates 10 empty
|
|
103
|
+
* strings at runtime because Gmail actually emits `Subject` capitalized.
|
|
104
|
+
*
|
|
105
|
+
* Verified during Session 20 dogfood: execution result data on a real
|
|
106
|
+
* Gmail node with `simple: true` had `[id, threadId, snippet, payload,
|
|
107
|
+
* sizeEstimate, historyId, internalDate, labels, Subject, From, To]`.
|
|
108
|
+
*/
|
|
109
|
+
const GMAIL_SIMPLE_MODE_FIELDS = [
|
|
110
|
+
'id',
|
|
111
|
+
'threadId',
|
|
112
|
+
'snippet',
|
|
113
|
+
'payload',
|
|
114
|
+
'sizeEstimate',
|
|
115
|
+
'historyId',
|
|
116
|
+
'internalDate',
|
|
117
|
+
'labels',
|
|
118
|
+
'Subject',
|
|
119
|
+
'From',
|
|
120
|
+
'To',
|
|
121
|
+
'Date',
|
|
122
|
+
'Cc',
|
|
123
|
+
'Bcc',
|
|
124
|
+
'Reply-To',
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
function inferGmailSimpleFields(node: WorkflowNode): string[] | null {
|
|
128
|
+
const params = node.parameters as Record<string, unknown> | undefined;
|
|
129
|
+
// simple=true is the default in workflows's Gmail node. Treat both `true` and
|
|
130
|
+
// `undefined` as simple-mode unless explicitly disabled.
|
|
131
|
+
if (params?.simple === false) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return GMAIL_SIMPLE_MODE_FIELDS;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns top-level output field names this node will emit, when derivable
|
|
139
|
+
* from parameters alone. Returns `null` when the schema is unknowable
|
|
140
|
+
* (Code, Function, AI Agent, custom) — callers should treat null as
|
|
141
|
+
* "skip field validation against this node's output", NOT as "no fields".
|
|
142
|
+
*/
|
|
143
|
+
export function inferSyntheticOutputSchema(node: WorkflowNode): string[] | null {
|
|
144
|
+
switch (node.type) {
|
|
145
|
+
case 'workflows-nodes-base.summarize':
|
|
146
|
+
return inferSummarizeFields(node);
|
|
147
|
+
case 'workflows-nodes-base.set':
|
|
148
|
+
case 'workflows-nodes-base.editFields':
|
|
149
|
+
return inferSetFields(node);
|
|
150
|
+
case 'workflows-nodes-base.gmail':
|
|
151
|
+
// simple-mode override — only applies when simple !== false.
|
|
152
|
+
// For simple=false, return null so the caller falls back to the
|
|
153
|
+
// static schema (which captures the non-simple lowercase shape).
|
|
154
|
+
return inferGmailSimpleFields(node);
|
|
155
|
+
// Arbitrary user output — schema unknowable without execution.
|
|
156
|
+
case 'workflows-nodes-base.code':
|
|
157
|
+
case 'workflows-nodes-base.function':
|
|
158
|
+
case 'workflows-nodes-base.functionItem':
|
|
159
|
+
return null;
|
|
160
|
+
default:
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output schema utilities for validating expressions between nodes.
|
|
3
|
+
* Uses pre-crawled schemaIndex.json with full schema content.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import schemaIndex from '../data/schemaIndex.json';
|
|
7
|
+
import triggerSchemaIndex from '../data/triggerSchemaIndex.json';
|
|
8
|
+
import type { ExpressionRef, SchemaContent } from '../types/index';
|
|
9
|
+
|
|
10
|
+
type SchemasByResource = Record<string, Record<string, SchemaContent>>;
|
|
11
|
+
|
|
12
|
+
interface SchemaEntry {
|
|
13
|
+
folder: string;
|
|
14
|
+
schemas: SchemasByResource;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface SchemaIndex {
|
|
18
|
+
nodeTypes: Record<string, SchemaEntry>;
|
|
19
|
+
generatedAt: string;
|
|
20
|
+
version: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TriggerSchemaIndex {
|
|
24
|
+
triggers?: Record<string, { outputSchema: SchemaContent }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SCHEMA_INDEX = schemaIndex as Partial<SchemaIndex>;
|
|
28
|
+
const TRIGGER_SCHEMA_INDEX: TriggerSchemaIndex = triggerSchemaIndex;
|
|
29
|
+
const TRIGGER_SCHEMAS = TRIGGER_SCHEMA_INDEX.triggers ?? {};
|
|
30
|
+
|
|
31
|
+
const NODE_SCHEMAS = SCHEMA_INDEX.nodeTypes ?? {};
|
|
32
|
+
|
|
33
|
+
export interface OutputSchemaResult {
|
|
34
|
+
schema: SchemaContent;
|
|
35
|
+
fields: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function hasOutputSchema(nodeType: string): boolean {
|
|
39
|
+
return nodeType in NODE_SCHEMAS;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getAvailableResources(nodeType: string): string[] {
|
|
43
|
+
const entry = NODE_SCHEMAS[nodeType];
|
|
44
|
+
if (!entry) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return Object.keys(entry.schemas);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getAvailableOperations(nodeType: string, resource: string): string[] {
|
|
51
|
+
const entry = NODE_SCHEMAS[nodeType];
|
|
52
|
+
if (!entry) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const resourceSchemas = entry.schemas[resource];
|
|
56
|
+
if (!resourceSchemas) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return Object.keys(resourceSchemas);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function loadOutputSchema(
|
|
63
|
+
nodeType: string,
|
|
64
|
+
resource: string,
|
|
65
|
+
operation: string
|
|
66
|
+
): OutputSchemaResult | null {
|
|
67
|
+
const entry = NODE_SCHEMAS[nodeType];
|
|
68
|
+
if (!entry) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const resourceSchemas = entry.schemas[resource];
|
|
73
|
+
if (!resourceSchemas) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const schema = resourceSchemas[operation];
|
|
78
|
+
if (!schema) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
schema,
|
|
84
|
+
fields: getTopLevelFields(schema),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function loadTriggerOutputSchema(
|
|
89
|
+
nodeType: string,
|
|
90
|
+
parameters?: Record<string, unknown>
|
|
91
|
+
): OutputSchemaResult | null {
|
|
92
|
+
if (parameters?.simple === false) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const entry = TRIGGER_SCHEMAS[nodeType];
|
|
96
|
+
if (!entry?.outputSchema?.properties || Object.keys(entry.outputSchema.properties).length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
schema: entry.outputSchema,
|
|
101
|
+
fields: getTopLevelFields(entry.outputSchema),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getTopLevelFields(schema: SchemaContent): string[] {
|
|
106
|
+
if (!schema.properties) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
return Object.keys(schema.properties);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Returns all field paths including nested (e.g., "from.value[0].address") */
|
|
113
|
+
export function getAllFieldPaths(schema: SchemaContent, prefix = ''): string[] {
|
|
114
|
+
return getAllFieldPathsTyped(schema, prefix).map((f) => f.path);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Returns field paths with their types (e.g., "snippet: string", "payload: object"). */
|
|
118
|
+
export function getAllFieldPathsTyped(
|
|
119
|
+
schema: SchemaContent,
|
|
120
|
+
prefix = ''
|
|
121
|
+
): { path: string; type: string }[] {
|
|
122
|
+
const fields: { path: string; type: string }[] = [];
|
|
123
|
+
const properties = schema.properties;
|
|
124
|
+
|
|
125
|
+
if (!properties) {
|
|
126
|
+
return fields;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
130
|
+
const currentPath = prefix ? `${prefix}.${key}` : key;
|
|
131
|
+
|
|
132
|
+
if (typeof value === 'object' && value !== null) {
|
|
133
|
+
const propSchema = value as SchemaContent;
|
|
134
|
+
fields.push({ path: currentPath, type: propSchema.type || 'unknown' });
|
|
135
|
+
|
|
136
|
+
if (propSchema.type === 'object' && propSchema.properties) {
|
|
137
|
+
fields.push(...getAllFieldPathsTyped(propSchema, currentPath));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (propSchema.type === 'array' && propSchema.items) {
|
|
141
|
+
const items = propSchema.items as SchemaContent;
|
|
142
|
+
if (items.type === 'object' && items.properties) {
|
|
143
|
+
fields.push(...getAllFieldPathsTyped(items, `${currentPath}[0]`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return fields;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function parseExpressions(
|
|
153
|
+
parameters: Record<string, unknown>,
|
|
154
|
+
parentPath = ''
|
|
155
|
+
): ExpressionRef[] {
|
|
156
|
+
const refs: ExpressionRef[] = [];
|
|
157
|
+
|
|
158
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
159
|
+
const currentPath = parentPath ? `${parentPath}.${key}` : key;
|
|
160
|
+
|
|
161
|
+
if (typeof value === 'string') {
|
|
162
|
+
refs.push(...extractExpressionsFromString(value, currentPath));
|
|
163
|
+
} else if (Array.isArray(value)) {
|
|
164
|
+
for (let i = 0; i < value.length; i++) {
|
|
165
|
+
if (typeof value[i] === 'string') {
|
|
166
|
+
refs.push(...extractExpressionsFromString(value[i], `${currentPath}[${i}]`));
|
|
167
|
+
} else if (typeof value[i] === 'object' && value[i] !== null) {
|
|
168
|
+
refs.push(
|
|
169
|
+
...parseExpressions(value[i] as Record<string, unknown>, `${currentPath}[${i}]`)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
174
|
+
refs.push(...parseExpressions(value as Record<string, unknown>, currentPath));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return refs;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function extractExpressionsFromString(str: string, paramPath: string): ExpressionRef[] {
|
|
182
|
+
const refs: ExpressionRef[] = [];
|
|
183
|
+
|
|
184
|
+
// Match every $json.field reference, even inside compound expressions like {{ $json.a || $json.b }}
|
|
185
|
+
const simplePattern = /\$json\.([a-zA-Z0-9_.[\]'"-]{1,200})/g;
|
|
186
|
+
// Bracket-notation variant: $json["someField"] or $json['someField'] (Session 21
|
|
187
|
+
// follow-up). The LLM occasionally emits this when the field name has chars
|
|
188
|
+
// that wouldn't survive dot notation, OR when it's just "trying to be safe".
|
|
189
|
+
// Without this pattern, validateAndRepair walks past references like
|
|
190
|
+
// {{ $json["concatenate_subject"] }} silently — the typo never gets caught.
|
|
191
|
+
const bracketPattern = /\$json\[\s*(['"])([^'"]{1,200})\1\s*\]/g;
|
|
192
|
+
const namedNodePattern =
|
|
193
|
+
/\$\(['"]([^'"]{1,100})['"]\)\.item\.json\.([a-zA-Z0-9_.[\]'"-]{1,200})/g;
|
|
194
|
+
|
|
195
|
+
let match: RegExpExecArray | null = simplePattern.exec(str);
|
|
196
|
+
while (match !== null) {
|
|
197
|
+
const field = match[1];
|
|
198
|
+
refs.push({
|
|
199
|
+
fullExpression: match[0],
|
|
200
|
+
field,
|
|
201
|
+
path: parseFieldPath(field),
|
|
202
|
+
paramPath,
|
|
203
|
+
});
|
|
204
|
+
match = simplePattern.exec(str);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
match = bracketPattern.exec(str);
|
|
208
|
+
while (match !== null) {
|
|
209
|
+
const field = match[2];
|
|
210
|
+
refs.push({
|
|
211
|
+
fullExpression: match[0],
|
|
212
|
+
field,
|
|
213
|
+
path: parseFieldPath(field),
|
|
214
|
+
paramPath,
|
|
215
|
+
});
|
|
216
|
+
match = bracketPattern.exec(str);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
match = namedNodePattern.exec(str);
|
|
220
|
+
while (match !== null) {
|
|
221
|
+
const field = match[2];
|
|
222
|
+
refs.push({
|
|
223
|
+
fullExpression: match[0],
|
|
224
|
+
field,
|
|
225
|
+
path: parseFieldPath(field),
|
|
226
|
+
paramPath,
|
|
227
|
+
sourceNodeName: match[1],
|
|
228
|
+
});
|
|
229
|
+
match = namedNodePattern.exec(str);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return refs;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Parses "from.value[0].address" or "headers['content-type']" into path segments.
|
|
237
|
+
*/
|
|
238
|
+
function parseFieldPath(field: string): string[] {
|
|
239
|
+
const path: string[] = [];
|
|
240
|
+
let current = '';
|
|
241
|
+
let i = 0;
|
|
242
|
+
|
|
243
|
+
while (i < field.length) {
|
|
244
|
+
const char = field[i];
|
|
245
|
+
|
|
246
|
+
if (char === '.') {
|
|
247
|
+
if (current) {
|
|
248
|
+
path.push(current);
|
|
249
|
+
current = '';
|
|
250
|
+
}
|
|
251
|
+
i++;
|
|
252
|
+
} else if (char === '[') {
|
|
253
|
+
if (current) {
|
|
254
|
+
path.push(current);
|
|
255
|
+
current = '';
|
|
256
|
+
}
|
|
257
|
+
i++;
|
|
258
|
+
if (i >= field.length) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
if (field[i] === "'" || field[i] === '"') {
|
|
262
|
+
const quote = field[i];
|
|
263
|
+
i++;
|
|
264
|
+
while (i < field.length && field[i] !== quote) {
|
|
265
|
+
current += field[i];
|
|
266
|
+
i++;
|
|
267
|
+
}
|
|
268
|
+
i++;
|
|
269
|
+
} else {
|
|
270
|
+
while (i < field.length && field[i] !== ']') {
|
|
271
|
+
current += field[i];
|
|
272
|
+
i++;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (current) {
|
|
276
|
+
path.push(current);
|
|
277
|
+
current = '';
|
|
278
|
+
}
|
|
279
|
+
i++;
|
|
280
|
+
} else {
|
|
281
|
+
current += char;
|
|
282
|
+
i++;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (current) {
|
|
287
|
+
path.push(current);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return path;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function fieldExistsInSchema(path: string[], schema: SchemaContent): boolean {
|
|
294
|
+
if (path.length === 0) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
let current: SchemaContent | undefined = schema;
|
|
299
|
+
|
|
300
|
+
for (let i = 0; i < path.length; i++) {
|
|
301
|
+
const segment = path[i];
|
|
302
|
+
|
|
303
|
+
if (!current || typeof current !== 'object') {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const properties = current.properties;
|
|
308
|
+
if (!properties) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const prop = properties[segment] as SchemaContent | undefined;
|
|
313
|
+
if (!prop) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (i === path.length - 1) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (prop.type === 'object') {
|
|
322
|
+
current = prop;
|
|
323
|
+
} else if (prop.type === 'array' && prop.items) {
|
|
324
|
+
const nextSegment = path[i + 1];
|
|
325
|
+
if (/^\d+$/.test(nextSegment)) {
|
|
326
|
+
i++;
|
|
327
|
+
current = prop.items as SchemaContent;
|
|
328
|
+
} else {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export function formatSchemaForPrompt(schema: SchemaContent, maxDepth = 2): string {
|
|
340
|
+
const lines: string[] = [];
|
|
341
|
+
|
|
342
|
+
function format(obj: SchemaContent, depth: number, prefix: string) {
|
|
343
|
+
const properties = obj.properties;
|
|
344
|
+
if (!properties || depth > maxDepth) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
349
|
+
const prop = value as SchemaContent;
|
|
350
|
+
const type = prop.type as string;
|
|
351
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
352
|
+
|
|
353
|
+
if (type === 'object' && prop.properties) {
|
|
354
|
+
lines.push(`${path}: object`);
|
|
355
|
+
format(prop, depth + 1, path);
|
|
356
|
+
} else if (type === 'array' && prop.items) {
|
|
357
|
+
const items = prop.items as SchemaContent;
|
|
358
|
+
if (items.type === 'object' && items.properties) {
|
|
359
|
+
lines.push(`${path}: array of objects`);
|
|
360
|
+
format(items, depth + 1, `${path}[0]`);
|
|
361
|
+
} else {
|
|
362
|
+
lines.push(`${path}: array of ${items.type || 'unknown'}`);
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
lines.push(`${path}: ${type || 'unknown'}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
format(schema, 0, '');
|
|
371
|
+
return lines.join('\n');
|
|
372
|
+
}
|