@objectstack/service-automation 7.2.1 → 7.4.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/engine.ts","../src/plugin.ts","../src/plugins/template.ts","../src/plugins/crud-nodes-plugin.ts","../src/plugins/logic-nodes-plugin.ts","../src/plugins/http-connector-plugin.ts","../src/plugins/screen-nodes-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Core engine\nexport { AutomationEngine } from './engine.js';\nexport type { NodeExecutor, NodeExecutionResult, FlowTrigger } from './engine.js';\n\n// Kernel plugin\nexport { AutomationServicePlugin } from './plugin.js';\nexport type { AutomationServicePluginOptions } from './plugin.js';\n\n// Node plugins\nexport { CrudNodesPlugin } from './plugins/crud-nodes-plugin.js';\nexport { LogicNodesPlugin } from './plugins/logic-nodes-plugin.js';\nexport { HttpConnectorPlugin } from './plugins/http-connector-plugin.js';\nexport { ScreenNodesPlugin } from './plugins/screen-nodes-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, IAutomationService } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema } from '@objectstack/spec/automation';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Corresponds to FlowNodeAction enum value */\n readonly type: string;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(flowName: string, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Internal execution step log entry.\n */\ninterface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\nexport class AutomationEngine implements IAutomationService {\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private triggers = new Map<string, FlowTrigger>();\n private executionLogs: ExecutionLogEntry[] = [];\n private maxLogSize = 1000;\n private logger: Logger;\n private runCounter = 0;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n this.nodeExecutors.delete(type);\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n }\n\n unregisterFlow(name: string): void {\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(`Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed.`);\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n }\n\n // Find next nodes — separate conditional and unconditional edges\n const outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string | { dialect?: string; source?: string; ast?: unknown }, variables: Map<string, unknown>): boolean {\n // M9.5+ wiring: route Expression envelopes through @objectstack/formula\n // ExpressionEngine. CEL is the default; legacy `{var}` template syntax\n // is preserved as a fallback for back-compat.\n const isEnvelope = typeof expression === 'object' && expression != null && 'dialect' in expression;\n const dialect = isEnvelope ? (expression as { dialect?: string }).dialect : undefined;\n const exprStr = typeof expression === 'string' ? expression : ((expression as { source?: string })?.source ?? '');\n\n if (isEnvelope && dialect && dialect !== 'cel' && dialect !== 'flow' && dialect !== 'template') {\n // Other dialects (cron, js) are not boolean predicates here.\n return false;\n }\n\n // CEL path — bind `vars` scope for `{step.result}` style references via\n // the equivalent `vars.step.result` CEL identifier path.\n if (dialect === 'cel' || (isEnvelope && !dialect)) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ExpressionEngine } = require('@objectstack/formula') as typeof import('@objectstack/formula');\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n // Convert \"step.result\" keys into nested object paths.\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source: exprStr },\n { extra: { vars }, record: vars },\n );\n if (!result.ok) return false;\n return Boolean(result.value);\n } catch {\n return false;\n }\n }\n\n // Legacy template path: {varName} → value, then primitive compare.\n let resolved = exprStr;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { AutomationEngine } from './engine.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service\n * 2. start phase: Trigger 'automation:ready' hook for node plugin registration,\n * then pull flow definitions from the ObjectQL schema registry and register\n * them with the engine.\n * 3. destroy phase: Clean up resources\n *\n * Does NOT implement any specific nodes — nodes are registered by other plugins\n * via the engine's extension API.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n // Soft dependency on metadata: we look it up at start() and tolerate absence.\n // Do NOT declare a hard kernel dependency, so this plugin works in environments\n // where MetadataPlugin is not registered.\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger);\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n console.warn('[Automation:start] entering start()');\n if (!this.engine) {\n console.warn('[Automation:start] engine missing, bailing');\n return;\n }\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n\n // Pull flow definitions from the ObjectQL schema registry. AppPlugin.init()\n // calls manifest.register(payload), which routes to ql.registerApp() and\n // stores each inline flow under type 'flow'. By the time start() runs,\n // every init() phase has completed, so the registry is fully populated.\n try {\n const ql = ctx.getService<{\n registry?: { listItems?: (type: string) => unknown[] };\n }>('objectql');\n if (!ql) {\n console.warn('[Automation] objectql service not found at start()');\n } else if (!ql.registry) {\n console.warn('[Automation] objectql.registry is undefined at start()');\n } else if (typeof ql.registry.listItems !== 'function') {\n console.warn('[Automation] objectql.registry.listItems is not a function');\n }\n const flows = ql?.registry?.listItems?.('flow') ?? [];\n console.warn(`[Automation] flow pull: registry returned ${flows.length} flow(s)`);\n let registered = 0;\n for (const f of flows) {\n const def = f as { name?: string };\n if (!def?.name) continue;\n try {\n this.engine.registerFlow(def.name, def as never);\n registered++;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n ctx.logger.warn(`[Automation] failed to register flow ${def.name}: ${msg}`);\n }\n }\n if (registered > 0) {\n ctx.logger.info(`[Automation] Pulled ${registered} flow(s) from ObjectQL registry`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.logger.warn(`[Automation] flow pull from ObjectQL registry failed: ${msg}`);\n }\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Template interpolation helpers shared across node executors.\n *\n * Supported syntax (intentionally minimal — no full expression language):\n *\n * {variable} → variables.get('variable')\n * {variable.path.segment} → walks dotted path on the resolved value\n * {$User.Id} → reads from context.userId\n * {$User.Email} → reads from context.user?.email\n * {NOW()} → ISO timestamp at evaluation time\n * {TODAY()} → YYYY-MM-DD at evaluation time\n * {TODAY() + 90} → date + N days (days only, integer)\n *\n * Anything that fails to resolve becomes the literal `null` value (for\n * single-token templates) or the empty string (for embedded substitution),\n * matching the behavior of common low-code formula engines.\n *\n * The interpolator walks objects, arrays, and primitives recursively so it\n * can be applied wholesale to a node's `config.fields`/`config.filter` blocks.\n */\n\nimport type { AutomationContext } from '@objectstack/spec/contracts';\n\nexport type VariableMap = Map<string, unknown>;\n\n/**\n * Resolve a dotted path against a base value.\n * Returns `undefined` for any missing intermediate node.\n */\nfunction resolvePath(base: unknown, path: string[]): unknown {\n let cur: unknown = base;\n for (const seg of path) {\n if (cur == null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Resolve a single template token (without braces) to a value.\n * Returns `undefined` if the token cannot be resolved.\n */\nfunction resolveToken(token: string, variables: VariableMap, context: AutomationContext): unknown {\n const trimmed = token.trim();\n if (!trimmed) return undefined;\n\n // Built-in date helpers — `NOW()` / `TODAY()` with optional `+ N` day offset.\n // The offset may be a literal integer or any token resolvable from `variables`.\n const dateFnMatch = /^(NOW|TODAY)\\s*\\(\\s*\\)\\s*(?:([+\\-])\\s*(\\S+))?$/.exec(trimmed);\n if (dateFnMatch) {\n const fn = dateFnMatch[1];\n const sign = dateFnMatch[2] === '-' ? -1 : 1;\n const offsetRaw = dateFnMatch[3];\n let offset = 0;\n if (offsetRaw) {\n const asNum = Number(offsetRaw);\n if (!isNaN(asNum)) {\n offset = asNum;\n } else if (variables.has(offsetRaw)) {\n offset = Number(variables.get(offsetRaw)) || 0;\n }\n }\n const now = new Date();\n if (offset) now.setDate(now.getDate() + sign * offset);\n if (fn === 'NOW') return now.toISOString();\n return now.toISOString().slice(0, 10);\n }\n\n // $User.* shortcuts\n if (trimmed.startsWith('$User.')) {\n const path = trimmed.slice('$User.'.length).split('.');\n if (path[0] === 'Id') return context.userId;\n if (path[0] === 'Email') return resolvePath((context as any).user, ['email', ...path.slice(1)]) ?? undefined;\n return resolvePath((context as any).user, path);\n }\n\n // Direct variable / dotted path lookup (fast path, no arithmetic).\n if (/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*$/.test(trimmed)) {\n const segments = trimmed.split('.');\n const head = segments[0];\n if (variables.has(head)) {\n return resolvePath(variables.get(head), segments.slice(1));\n }\n if (variables.has(trimmed)) return variables.get(trimmed);\n return undefined;\n }\n\n // Arithmetic / mixed expression: substitute variable references (foo, foo.bar)\n // with their numeric/string literal forms, then evaluate via Function().\n // Restricted to a safe character set (digits, basic operators, parentheses,\n // dots and identifier characters) — never executed on raw user input.\n if (!/^[\\w\\s+\\-*/%().,?:<>=!&|\"'$]+$/.test(trimmed)) return undefined;\n let safe = trimmed;\n safe = safe.replace(/([A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*)/g, (match) => {\n // Don't substitute reserved literals\n if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match;\n const segs = match.split('.');\n const head = segs[0];\n let val: unknown;\n if (variables.has(head)) val = resolvePath(variables.get(head), segs.slice(1));\n else if (variables.has(match)) val = variables.get(match);\n if (val === undefined || val === null) return 'null';\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return JSON.stringify(String(val));\n });\n try {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const fn = new Function(`\"use strict\"; return (${safe});`);\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{...}` tokens in a string with resolved values.\n * - When the entire string is a single token, returns the raw value (preserving type).\n * - Otherwise concatenates string substitutions, with `null`/`undefined` rendered as ''.\n */\nexport function interpolateString(\n input: string,\n variables: VariableMap,\n context: AutomationContext,\n): unknown {\n if (!input.includes('{')) return input;\n const single = /^\\{([^{}]+)\\}$/.exec(input);\n if (single) {\n const value = resolveToken(single[1], variables, context);\n return value;\n }\n return input.replace(/\\{([^{}]+)\\}/g, (_match, expr) => {\n const value = resolveToken(expr, variables, context);\n if (value === undefined || value === null) return '';\n return String(value);\n });\n}\n\n/**\n * Recursively interpolate template tokens in arbitrary JSON-like values.\n */\nexport function interpolate<T = unknown>(\n value: T,\n variables: VariableMap,\n context: AutomationContext,\n): T {\n if (typeof value === 'string') {\n return interpolateString(value, variables, context) as unknown as T;\n }\n if (Array.isArray(value)) {\n return value.map(v => interpolate(v, variables, context)) as unknown as T;\n }\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = interpolate(v, variables, context);\n }\n return out as unknown as T;\n }\n return value;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * CRUD Node Plugin — wires `get_record` / `create_record` / `update_record` /\n * `delete_record` flow nodes to the runtime data layer (ObjectQL / IDataEngine).\n *\n * Each executor:\n * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in\n * `node.config` against the running flow's variable context.\n * 2. Calls the resolved data engine via `ctx.getService('data')`.\n * 3. Writes the result back to the variable context under `outputVariable`\n * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream\n * nodes can reference fields like `{leadRecord.company}`.\n *\n * If no data engine is registered, executors degrade to a no-op success so\n * test environments without ObjectQL still complete the flow without errors.\n */\nexport class CrudNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.crud-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n const getData = (): IDataEngine | undefined => {\n try {\n return ctx.getService<IDataEngine>('data') ?? ctx.getService<IDataEngine>('objectql');\n } catch {\n return undefined;\n }\n };\n\n // ── get_record ────────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'get_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'get_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = cfg.fields as string[] | undefined;\n const limit = typeof cfg.limit === 'number' ? cfg.limit : undefined;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[get_record] no data engine; skipping ${objectName}`);\n return { success: true, output: { records: [], object: objectName } };\n }\n\n try {\n if (limit && limit > 1) {\n const records = await data.find(objectName, { where: filter, fields, limit });\n if (outputVariable) variables.set(outputVariable, records);\n return { success: true, output: { records, object: objectName } };\n }\n const record = await data.findOne(objectName, { where: filter, fields });\n if (outputVariable) variables.set(outputVariable, record);\n return { success: true, output: { record, id: record?.id, object: objectName } };\n } catch (err) {\n return { success: false, error: `get_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── create_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'create_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'create_record: objectName required' };\n\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);\n if (outputVariable) variables.set(outputVariable, `mock-${objectName}-${Date.now()}`);\n return { success: true, output: { id: `mock-${objectName}-${Date.now()}`, object: objectName } };\n }\n\n try {\n const created = await data.insert(objectName, fields);\n const insertedId = Array.isArray(created) ? created[0]?.id : created?.id ?? created;\n if (outputVariable) variables.set(outputVariable, insertedId);\n return { success: true, output: { id: insertedId, record: created, object: objectName } };\n } catch (err) {\n return { success: false, error: `create_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── update_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'update_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'update_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[update_record] no data engine; skipping ${objectName}`);\n return { success: true };\n }\n\n try {\n const result = await data.update(objectName, fields, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `update_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── delete_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'delete_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'delete_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) return { success: true };\n\n try {\n const result = await data.delete(objectName, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `delete_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 node executors registered (data-backed)');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic Node Plugin — Provides decision / assignment / loop nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class LogicNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.logic-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n // loop node — iterate over a collection\n engine.registerNodeExecutor({\n type: 'loop',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const collectionName = config?.collection as string | undefined;\n if (collectionName) {\n const collection = variables.get(collectionName);\n if (Array.isArray(collection)) {\n variables.set('$loopItems', collection);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 3 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * HTTP + Connector Node Plugin — Provides http_request / connector_action nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class HttpConnectorPlugin implements Plugin {\n name = 'com.objectstack.automation.http-connector';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // http_request node executor\n engine.registerNodeExecutor({\n type: 'http_request',\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const url = config?.url as string | undefined;\n const method = (config?.method as string) ?? 'GET';\n const headers = config?.headers as Record<string, string> | undefined;\n const body = config?.body;\n\n if (!url) {\n return { success: false, error: 'http_request: url is required' };\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n const data = await response.json();\n\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n },\n });\n\n // connector_action node — calls a registered connector\n engine.registerNodeExecutor({\n type: 'connector_action',\n async execute(node, _variables, _context) {\n const connectorConfig = node.connectorConfig;\n if (!connectorConfig) {\n return { success: false, error: 'connector_action: connectorConfig is required' };\n }\n\n ctx.logger.info(\n `Connector action: ${connectorConfig.connectorId}.${connectorConfig.actionId}`,\n );\n\n // In production, this would look up the connector from a registry\n // and execute the specified action with the mapped inputs\n return { success: true, output: { connectorResult: {} } };\n },\n });\n\n ctx.logger.info('[HTTP Connector] 2 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Screen / Script Node Plugin — Provides 'screen' and 'script' executors.\n *\n * - 'screen' nodes are pass-through on the server. The engine already injects\n * `isInput: true` flow variables from `context.params` into the top-level\n * variables map before execution begins, so screen nodes have no remaining\n * server-side work.\n * - 'script' nodes dispatch by `config.actionType`. Currently only 'email'\n * has a (logger-backed) implementation; unknown action types still succeed\n * so flows can continue and downstream nodes can react.\n *\n * Dependencies: service-automation (engine)\n */\nexport class ScreenNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.screen-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // screen — server-side pass-through (input vars already injected by engine).\n engine.registerNodeExecutor({\n type: 'screen',\n async execute(_node, _variables, _context) {\n return { success: true };\n },\n });\n\n // script — dispatch by actionType.\n engine.registerNodeExecutor({\n type: 'script',\n async execute(node, _variables, _context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const actionType = (cfg.actionType as string | undefined) ?? 'noop';\n if (actionType === 'email') {\n ctx.logger.info(\n `[Script:email] template=${String(cfg.template)} ` +\n `recipients=${JSON.stringify(cfg.recipients)} ` +\n `vars=${JSON.stringify(cfg.variables)}`,\n );\n return {\n success: true,\n output: {\n actionType,\n template: cfg.template,\n recipients: cfg.recipients,\n },\n };\n }\n ctx.logger.info(`[Script:${actionType}] node=${node.id} executed (no-op handler)`);\n return { success: true, output: { actionType } };\n },\n });\n\n ctx.logger.info('[Screen/Script Nodes] 2 node executors registered');\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/engine.ts","../src/builtin/logic-nodes.ts","../src/builtin/crud-nodes.ts","../src/builtin/template.ts","../src/builtin/screen-nodes.ts","../src/builtin/http-nodes.ts","../src/builtin/connector-nodes.ts","../src/builtin/notify-node.ts","../src/builtin/index.ts","../src/plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Core engine\nexport { AutomationEngine } from './engine.js';\nexport type {\n NodeExecutor,\n NodeExecutionResult,\n FlowTrigger,\n FlowTriggerBinding,\n ConnectorActionHandler,\n ConnectorActionContext,\n RegisteredConnector,\n ConnectorDescriptor,\n ConnectorActionDescriptor,\n} from './engine.js';\n\n// Kernel plugin — seeds all built-in nodes; this is the only plugin needed for\n// a fully-functional automation capability.\nexport { AutomationServicePlugin } from './plugin.js';\nexport type { AutomationServicePluginOptions } from './plugin.js';\n\n// Built-in node executors (ADR-0018). These are seeded by AutomationServicePlugin\n// and exported for advanced hosts that build a custom engine. They are functions,\n// not plugins — the platform's foundational nodes are built in, not installed.\nexport {\n installBuiltinNodes,\n registerLogicNodes,\n registerCrudNodes,\n registerScreenNodes,\n registerHttpNodes,\n registerConnectorNodes,\n} from './builtin/index.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog, ActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, ResumeSignal, IAutomationService, ScreenSpec } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema, FLOW_STRUCTURAL_NODE_TYPES } from '@objectstack/spec/automation';\nimport type { Connector } from '@objectstack/spec/integration';\nimport { ConnectorSchema } from '@objectstack/spec/integration';\n// Static import (not a lazy `require`): the engine ships as ESM (\"type\":\"module\"),\n// where a CommonJS `require('@objectstack/formula')` resolves to tsup's throwing\n// `__require` stub. That threw on every CEL evaluation and the catch below\n// silently returned `false`, so EVERY start-node / edge condition (record-change\n// `previous.*`, `budget > 100000`, …) skipped its flow. A static import binds the\n// engine at module load in both ESM and CJS builds.\nimport { ExpressionEngine } from '@objectstack/formula';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Registry node type (built-in id or plugin-defined) */\n readonly type: string;\n\n /**\n * Optional ADR-0018 action descriptor. When present, it is published into\n * the engine's action registry and surfaced via {@link AutomationEngine.getActionDescriptors}\n * — feeding flow validation and the designer palette. Plugins SHOULD publish\n * one so their node appears in the palette and validates as a legal flow node.\n */\n readonly descriptor?: ActionDescriptor;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n /**\n * ADR-0019 durable pause. When `true`, the node has done its on-entry work\n * (e.g. opened an approval request) and the run should **suspend** here: the\n * engine persists a continuation, stops traversal, and `execute()` returns\n * `{ status: 'paused', runId }`. The run is continued later via\n * {@link AutomationEngine.resume}. Any `output` is written to variables\n * before suspending. The node reads its own run id from the `$runId`\n * flow variable so it can map the run to external state.\n */\n suspend?: boolean;\n /**\n * Optional correlation key surfaced on the suspended-run record (e.g. an\n * approval request id). For observability / lookup; not required to resume.\n */\n correlation?: string;\n /**\n * Screen to render — set by a `screen` node that suspends to collect input.\n * Surfaced on the paused {@link AutomationResult} so a UI runner can render\n * the form and `resume()` with the values.\n */\n screen?: ScreenSpec;\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * A normalized description of *what* fires a flow, derived by the engine from\n * the flow's `start` node and handed to the matching {@link FlowTrigger} when a\n * flow is activated. Concrete triggers (record-change, schedule, …) read the\n * fields they care about and ignore the rest.\n *\n * The engine — not the trigger — owns parsing the start node, so trigger\n * plugins stay decoupled from flow-definition internals (mirrors how\n * `connector_action` keeps connectors decoupled from node config).\n */\nexport interface FlowTriggerBinding {\n /** Flow this binding activates. */\n readonly flowName: string;\n /** record-change: the object whose mutations fire the flow. */\n readonly object?: string;\n /** record-change: the start node's `triggerType` (e.g. 'record-after-update'). */\n readonly event?: string;\n /**\n * Optional trigger predicate copied from the start node's `condition`. The\n * engine evaluates it before running the flow; triggers may ignore it.\n */\n readonly condition?: string | { dialect?: string; source?: string; ast?: unknown };\n /** schedule: cron/interval descriptor (parsed but not yet acted on here). */\n readonly schedule?: unknown;\n /** The raw start-node `config`, for trigger-specific fields not modeled above. */\n readonly config?: Record<string, unknown>;\n}\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n *\n * The engine completes the wiring: when a flow whose start node maps to this\n * trigger's {@link type} is registered (or when this trigger is registered\n * after such flows already exist), the engine calls {@link start} with the\n * parsed {@link FlowTriggerBinding} and a `callback` that runs the flow. The\n * trigger subscribes to its event source (e.g. an ObjectQL lifecycle hook) and\n * invokes `callback(ctx)` when it fires. {@link stop} tears that subscription\n * down when the flow is unregistered/disabled or the trigger is removed.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(binding: FlowTriggerBinding, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Connector Registry (Plugin Extension Point) ────────────────────\n\n/**\n * Context handed to a connector action handler. Carries the live flow variable\n * map and the trigger context so a handler can read prior-node output, plus a\n * logger. The platform ships the registry + the `connector_action` dispatch\n * node (baseline, ADR-0018 §Addendum); *concrete* connectors — `connector-rest`,\n * `connector-slack`, … — are plugins that register handlers here.\n */\nexport interface ConnectorActionContext {\n readonly variables: Map<string, unknown>;\n readonly automation: AutomationContext;\n readonly logger: Logger;\n}\n\n/**\n * A handler for one connector action. Receives the (already-resolved) input\n * mapped from the flow node and returns the action's output, which the\n * `connector_action` node writes back into flow variables.\n */\nexport type ConnectorActionHandler = (\n input: Record<string, unknown>,\n ctx: ConnectorActionContext,\n) => Promise<Record<string, unknown>>;\n\n/**\n * A connector registered on the engine: its validated {@link Connector}\n * definition plus the handler for each action it declares.\n */\nexport interface RegisteredConnector {\n readonly def: Connector;\n readonly handlers: Record<string, ConnectorActionHandler>;\n}\n\n/**\n * A designer-facing view of one connector action — identity + its JSON-Schema\n * input/output. The runtime handler is intentionally omitted; this is metadata.\n */\nexport interface ConnectorActionDescriptor {\n readonly key: string;\n readonly label: string;\n readonly description?: string;\n readonly inputSchema?: Record<string, unknown>;\n readonly outputSchema?: Record<string, unknown>;\n}\n\n/**\n * A designer-facing descriptor for a registered connector: its identity plus\n * the actions it exposes. Served by `GET /api/v1/automation/connectors` so the\n * flow designer can populate the `connector_action` node's connector → action\n * → input pickers (ADR-0018 §Addendum, ADR-0022). Mirrors `ActionDescriptor`'s\n * role for node types, but for the connector registry.\n */\nexport interface ConnectorDescriptor {\n readonly name: string;\n readonly label: string;\n readonly type: string;\n readonly description?: string;\n readonly icon?: string;\n readonly actions: ConnectorActionDescriptor[];\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Internal execution step log entry.\n */\ninterface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\n/**\n * Internal sentinel thrown by {@link AutomationEngine.executeNode} when a node\n * signals `suspend`. It unwinds the synchronous DAG recursion up to\n * `execute()` / `resume()`, which converts it into a persisted continuation\n * rather than a failed run. (Not exported — callers see `status: 'paused'`.)\n *\n * NOTE: suspend is supported on the serial / main execution path. A node that\n * suspends inside a `Promise.all` parallel branch will unwind that branch, but\n * sibling parallel branches already in flight are not cancelled — durable\n * pause across parallel gateways is out of scope for ADR-0019 M1.\n */\nclass FlowSuspendSignal {\n readonly __flowSuspend = true as const;\n constructor(readonly nodeId: string, readonly correlation?: string, readonly screen?: ScreenSpec) {}\n}\n\nfunction isSuspendSignal(err: unknown): err is FlowSuspendSignal {\n return typeof err === 'object' && err !== null && (err as FlowSuspendSignal).__flowSuspend === true;\n}\n\n/**\n * A run paused at a node, awaiting {@link AutomationEngine.resume}. Held\n * in-memory, matching the engine's existing in-memory run model — durable\n * persistence of suspended runs across process restart is a follow-up tracked\n * with run-state persistence generally (ADR-0019 §Consequences).\n */\ninterface SuspendedRun {\n runId: string;\n flowName: string;\n flowVersion?: number;\n /** The node the run paused at; resume continues from its out-edges. */\n nodeId: string;\n /** Snapshot of the flow variable map at suspend time. */\n variables: Record<string, unknown>;\n steps: StepLogEntry[];\n context: AutomationContext;\n startedAt: string;\n startTime: number;\n correlation?: string;\n /** Screen the run paused on (screen-flow runtime), for re-fetch + UI render. */\n screen?: ScreenSpec;\n}\n\nexport class AutomationEngine implements IAutomationService {\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private actionDescriptors = new Map<string, ActionDescriptor>();\n private triggers = new Map<string, FlowTrigger>();\n /**\n * Flows currently wired to a trigger, keyed by flow name → the trigger\n * `type` that owns the binding. Used to avoid double-binding and to know\n * which trigger to `stop()` when a flow is unregistered/disabled.\n */\n private boundFlowTriggers = new Map<string, string>();\n /** Connectors registered by integration plugins, keyed by connector name (ADR-0018 §Addendum). */\n private connectors = new Map<string, RegisteredConnector>();\n private executionLogs: ExecutionLogEntry[] = [];\n private maxLogSize = 1000;\n private logger: Logger;\n private runCounter = 0;\n /** Runs paused at a node, keyed by runId (ADR-0019). In-memory, see {@link SuspendedRun}. */\n private suspendedRuns = new Map<string, SuspendedRun>();\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n\n // Publish the ADR-0018 action descriptor into the registry, so the\n // type validates as a legal flow node and appears in the designer\n // palette. A descriptor's `type` should match the executor's; we key\n // on the descriptor's `type` and warn on mismatch rather than silently\n // diverging.\n if (executor.descriptor) {\n const descriptorType = executor.descriptor.type;\n if (descriptorType !== executor.type) {\n this.logger.warn(\n `Node executor '${executor.type}' publishes a descriptor for type '${descriptorType}' — registering under both.`,\n );\n }\n this.actionDescriptors.set(descriptorType, executor.descriptor);\n }\n\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n const executor = this.nodeExecutors.get(type);\n this.nodeExecutors.delete(type);\n // Drop the published descriptor (keyed by descriptor.type, which may\n // differ from the executor type).\n this.actionDescriptors.delete(type);\n if (executor?.descriptor) {\n this.actionDescriptors.delete(executor.descriptor.type);\n }\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n // A trigger may be registered *after* its flows (e.g. AutomationServicePlugin\n // pulls flows at start(); a trigger plugin wires up on kernel:ready, which\n // fires later). Activate any already-registered flow that maps to this type.\n for (const name of this.flows.keys()) {\n if (this.boundFlowTriggers.has(name)) continue;\n const resolved = this.resolveTriggerBinding(name);\n if (resolved?.triggerType === trigger.type) {\n this.activateFlowTrigger(name);\n }\n }\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n // Tear down every flow bound to this trigger before dropping it.\n for (const [name, boundType] of [...this.boundFlowTriggers]) {\n if (boundType !== type) continue;\n try {\n this.triggers.get(type)?.stop(name);\n } catch (err) {\n this.logger.warn(`Trigger '${type}' stop('${name}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(name);\n }\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /**\n * Derive a flow's trigger binding from its `start` node, or `undefined` if\n * the flow has no auto-trigger (manual / screen / api). The convention —\n * established by the showcase flows — is that the start node carries the\n * trigger details in its `config`: `{ objectName, triggerType, condition }`\n * for record-change, or a `schedule` descriptor for time-based flows.\n */\n private resolveTriggerBinding(\n flowName: string,\n ): { triggerType: string; binding: FlowTriggerBinding } | undefined {\n const flow = this.flows.get(flowName);\n if (!flow) return undefined;\n const startNode = flow.nodes.find(n => n.type === 'start');\n const config = (startNode?.config ?? {}) as Record<string, unknown>;\n const triggerType = typeof config.triggerType === 'string' ? config.triggerType : undefined;\n\n if (triggerType && triggerType.startsWith('record-')) {\n return {\n triggerType: 'record_change',\n binding: {\n flowName,\n object: typeof config.objectName === 'string' ? config.objectName : undefined,\n event: triggerType,\n condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined,\n config,\n },\n };\n }\n\n if (config.schedule != null || flow.type === 'schedule') {\n return {\n triggerType: 'schedule',\n binding: { flowName, schedule: config.schedule, condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined, config },\n };\n }\n\n return undefined;\n }\n\n /**\n * Bind a flow to its matching registered trigger (idempotent). No-op when\n * the flow has no trigger binding or no trigger is registered for its type\n * yet — {@link registerTrigger} re-attempts activation when one arrives.\n */\n private activateFlowTrigger(flowName: string): void {\n if (this.boundFlowTriggers.has(flowName)) return;\n const resolved = this.resolveTriggerBinding(flowName);\n if (!resolved) return;\n const trigger = this.triggers.get(resolved.triggerType);\n if (!trigger) return;\n try {\n trigger.start(resolved.binding, (ctx: AutomationContext) => this.execute(flowName, ctx).then(() => undefined));\n this.boundFlowTriggers.set(flowName, resolved.triggerType);\n this.logger.info(`Flow '${flowName}' bound to trigger '${resolved.triggerType}'`);\n } catch (err) {\n this.logger.warn(`Failed to bind flow '${flowName}' to trigger '${resolved.triggerType}': ${(err as Error).message}`);\n }\n }\n\n /** Unbind a flow from its trigger, if bound. */\n private deactivateFlowTrigger(flowName: string): void {\n const boundType = this.boundFlowTriggers.get(flowName);\n if (!boundType) return;\n try {\n this.triggers.get(boundType)?.stop(flowName);\n } catch (err) {\n this.logger.warn(`Trigger '${boundType}' stop('${flowName}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(flowName);\n }\n\n /** Active flow→trigger bindings (observability / tests). */\n getActiveTriggerBindings(): Array<{ flowName: string; triggerType: string }> {\n return [...this.boundFlowTriggers].map(([flowName, triggerType]) => ({ flowName, triggerType }));\n }\n\n /**\n * Register a connector (called by integration plugins, ADR-0018 §Addendum).\n * Validates the definition against {@link ConnectorSchema} and asserts every\n * declared action has a handler, so a half-wired connector fails loudly at\n * registration rather than silently at dispatch. Re-registering the same\n * name replaces (mirrors {@link registerNodeExecutor}).\n */\n registerConnector(def: Connector, handlers: Record<string, ConnectorActionHandler>): void {\n const parsed = ConnectorSchema.parse(def);\n for (const action of parsed.actions ?? []) {\n if (typeof handlers[action.key] !== 'function') {\n throw new Error(\n `Connector '${parsed.name}': action '${action.key}' is declared but no handler was provided`,\n );\n }\n }\n if (this.connectors.has(parsed.name)) {\n this.logger.warn(`Connector '${parsed.name}' replaced`);\n }\n this.connectors.set(parsed.name, { def: parsed, handlers });\n this.logger.info(\n `Connector registered: ${parsed.name} (${Object.keys(handlers).length} action handlers)`,\n );\n }\n\n /** Unregister a connector (hot-unplug). */\n unregisterConnector(name: string): void {\n this.connectors.delete(name);\n this.logger.info(`Connector unregistered: ${name}`);\n }\n\n /**\n * Resolve the handler for a connector action, used by the baseline\n * `connector_action` node. Returns `undefined` when the connector or action\n * is not registered, so the node can fail the step with a clear error.\n */\n resolveConnectorAction(connectorId: string, actionId: string): ConnectorActionHandler | undefined {\n return this.connectors.get(connectorId)?.handlers[actionId];\n }\n\n /** Get all registered connector names. */\n getRegisteredConnectors(): string[] {\n return [...this.connectors.keys()];\n }\n\n /**\n * Get a designer-facing descriptor for every registered connector — its\n * identity plus the actions it exposes (input/output JSON Schema). Backs\n * `GET /api/v1/automation/connectors` so the designer can fill the\n * `connector_action` node's connector / action / input pickers (ADR-0022).\n * Handlers are omitted — they are runtime code, not metadata.\n */\n getConnectorDescriptors(): ConnectorDescriptor[] {\n return [...this.connectors.values()].map(({ def }) => ({\n name: def.name,\n label: def.label,\n type: def.type,\n description: def.description,\n icon: def.icon,\n actions: (def.actions ?? []).map((a) => ({\n key: a.key,\n label: a.label,\n description: a.description,\n inputSchema: a.inputSchema,\n outputSchema: a.outputSchema,\n })),\n }));\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /**\n * Get all published action descriptors (ADR-0018). Backs both flow\n * validation and the designer palette (`GET /api/v1/automation/actions`).\n * Only executors that published a descriptor appear here.\n */\n getActionDescriptors(): ActionDescriptor[] {\n return [...this.actionDescriptors.values()];\n }\n\n /** Get the action descriptor for a single node type, if published. */\n getActionDescriptor(type: string): ActionDescriptor | undefined {\n return this.actionDescriptors.get(type);\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // ADR-0018 §M1 — validate node types against the live action registry.\n // The protocol no longer gates `type` with a closed enum; membership is\n // checked here instead. Soft-fail (warn, don't throw): a flow authored\n // against a plugin that is currently disabled should still register, and\n // executeNode() already throws NO_EXECUTOR at run time for unknown types.\n this.validateNodeTypes(name, parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n\n // Re-bind in case the definition changed its trigger, then (re)activate.\n this.deactivateFlowTrigger(name);\n if (this.flowEnabled.get(name) !== false) {\n this.activateFlowTrigger(name);\n }\n }\n\n unregisterFlow(name: string): void {\n this.deactivateFlowTrigger(name);\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n // A disabled flow should stop receiving trigger events; a re-enabled one\n // should resume. execute() also guards disabled flows, but unbinding\n // avoids firing the trigger (and its event-source subscription) at all.\n if (enabled) {\n this.activateFlowTrigger(name);\n } else {\n this.deactivateFlowTrigger(name);\n }\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record. `$record` is the canonical handle; `record` is a\n // friendlier alias so templates/conditions can write `{record.title}` and\n // `record.status`. We also flatten the record's own fields to top-level\n // variables (so bare references like `status`/`budget` resolve in start\n // conditions and edge predicates) WITHOUT clobbering flow inputs already\n // seeded above. `previous` exposes the pre-update row for transition gates.\n if (context?.record) {\n variables.set('$record', context.record);\n variables.set('record', context.record);\n for (const [k, v] of Object.entries(context.record)) {\n if (!variables.has(k)) variables.set(k, v);\n }\n }\n if (context?.previous) {\n variables.set('previous', context.previous);\n }\n\n const runId = `run_${++this.runCounter}`;\n // Expose the run id to executors (ADR-0019): a pausing node (e.g. Approval)\n // reads `$runId` to map its external state back to this run for resume.\n variables.set('$runId', runId);\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Trigger-condition gate. The start node's `condition` is the predicate\n // that decides whether the trigger event should launch this flow (e.g.\n // `status == \"done\" && previous.status != \"done\"`). The engine — not the\n // trigger — owns evaluating it, so every trigger type (record-change,\n // schedule, …) and a manual `execute()` share one gate. Plain-string\n // conditions are routed through CEL so bare field references resolve.\n const startCondition = (startNode.config as Record<string, unknown> | undefined)?.condition as\n | string\n | { dialect?: string; source?: string; ast?: unknown }\n | undefined;\n if (startCondition !== undefined && startCondition !== null && startCondition !== '') {\n const condExpr =\n typeof startCondition === 'string' ? { dialect: 'cel', source: startCondition } : startCondition;\n if (!this.evaluateCondition(condExpr, variables)) {\n this.logger.debug(`Flow '${flowName}' skipped: start condition not met`);\n return { success: true, output: { skipped: true, reason: 'condition_not_met' } };\n }\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n // A node asked to suspend the run (ADR-0019 durable pause). Snapshot\n // the live state, record a `paused` log, and return the run id so the\n // caller can later `resume()` it. This is NOT a failure.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - startTime;\n this.suspendedRuns.set(runId, {\n runId,\n flowName,\n flowVersion: flow.version,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n context: context ?? {},\n startedAt,\n startTime,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'paused',\n startedAt,\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n });\n return {\n success: true,\n status: 'paused',\n runId,\n durationMs,\n screen: err.screen,\n };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n /**\n * Resume a run suspended at a node (ADR-0019 durable pause). Restores the\n * snapshotted variables, merges `signal.output` under the suspended node's\n * id, and continues traversal from that node's out-edges — optionally\n * restricted to the edge labelled `signal.branchLabel` (e.g. the approval\n * decision). The continuation may itself suspend again, in which case this\n * returns `{ status: 'paused', runId }` afresh.\n */\n async resume(runId: string, signal?: ResumeSignal): Promise<AutomationResult> {\n const run = this.suspendedRuns.get(runId);\n if (!run) {\n return { success: false, error: `No suspended run '${runId}'` };\n }\n const flow = this.flows.get(run.flowName);\n if (!flow) {\n return { success: false, error: `Flow '${run.flowName}' not found for run '${runId}'` };\n }\n const node = flow.nodes.find(n => n.id === run.nodeId);\n if (!node) {\n return { success: false, error: `Suspended node '${run.nodeId}' no longer exists in flow '${run.flowName}'` };\n }\n // Consume the suspension — a run resumes exactly once per pause.\n this.suspendedRuns.delete(runId);\n\n // Restore variable context and apply the resume signal's output as if it\n // were the node's output, so downstream edges branch on it.\n const variables = new Map<string, unknown>(Object.entries(run.variables));\n if (signal?.output) {\n for (const [key, value] of Object.entries(signal.output)) {\n variables.set(`${run.nodeId}.${key}`, value);\n }\n }\n // Bare flow variables — a `screen` node's collected inputs land under\n // their plain names so downstream `{var}` interpolation / conditions\n // read them directly (e.g. `new_assignee` → update_record fields).\n if (signal?.variables) {\n for (const [key, value] of Object.entries(signal.variables)) {\n variables.set(key, value);\n }\n }\n\n const steps = run.steps;\n const context = run.context;\n\n try {\n await this.traverseNext(node, flow, variables, context, steps, signal?.branchLabel);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) output[v.name] = variables.get(v.name);\n }\n }\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'completed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n output,\n });\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n // Re-suspended at a downstream node: persist a fresh continuation.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - run.startTime;\n this.suspendedRuns.set(runId, {\n ...run,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'paused',\n startedAt: run.startedAt,\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n });\n return { success: true, status: 'paused', runId, durationMs, screen: err.screen };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'failed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n\n /**\n * List the runs currently suspended awaiting {@link resume} (ADR-0019).\n * Backs operability surfaces such as a \"pending approvals\" view.\n */\n listSuspendedRuns(): Array<{ runId: string; flowName: string; nodeId: string; correlation?: string }> {\n return [...this.suspendedRuns.values()].map(r => ({\n runId: r.runId,\n flowName: r.flowName,\n nodeId: r.nodeId,\n correlation: r.correlation,\n }));\n }\n\n /**\n * The screen a paused run is currently waiting on (screen-flow runtime), or\n * `null` if the run isn't suspended / didn't pause at a screen node. Lets a\n * UI flow-runner re-fetch the form after a refresh.\n */\n getSuspendedScreen(runId: string): ScreenSpec | null {\n return this.suspendedRuns.get(runId)?.screen ?? null;\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Validate each node's `type` against the live action registry (ADR-0018).\n * A type is known if it is structural (start/end), has a registered\n * executor, or has a published action descriptor. Unknown types are\n * warned about (not rejected) so flows authored against a temporarily\n * absent plugin still register; the runtime surfaces a hard NO_EXECUTOR\n * error if such a node is actually executed.\n */\n private validateNodeTypes(flowName: string, flow: FlowParsed): void {\n const known = new Set<string>([\n ...FLOW_STRUCTURAL_NODE_TYPES,\n ...this.nodeExecutors.keys(),\n ...this.actionDescriptors.keys(),\n ]);\n const unknown = [...new Set(\n flow.nodes.map(n => n.type).filter(t => !known.has(t)),\n )];\n if (unknown.length > 0) {\n this.logger.warn(\n `Flow '${flowName}' references node type(s) with no registered executor or descriptor: ` +\n `${unknown.join(', ')}. They will fail at execution time unless a plugin registers them. ` +\n `Registered types: ${[...known].join(', ') || '(none)'}`,\n );\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(`Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed.`);\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n\n // ADR-0019 durable pause: the node did its on-entry work and asked to\n // suspend here. Output is already written above; unwind the recursion\n // up to execute()/resume(), which persists a continuation. Traversal\n // of this node's out-edges happens on resume, not now.\n if (result.suspend) {\n throw new FlowSuspendSignal(node.id, result.correlation, result.screen);\n }\n }\n\n // Continue to the node's successors.\n await this.traverseNext(node, flow, variables, context, steps);\n }\n\n /**\n * Traverse a node's out-edges and execute its successors. Split out of\n * {@link executeNode} so {@link resume} can re-enter traversal from a\n * suspended node without re-running the node body.\n *\n * @param branchLabel - When set (e.g. from a resume signal), restrict\n * traversal to out-edges whose `label` matches — this is how an Approval\n * node's `approve`/`reject` decision selects its downstream branch. When\n * no edge carries the label, traversal falls back to the normal edge set.\n */\n private async traverseNext(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n branchLabel?: string,\n ): Promise<void> {\n // Find next nodes — separate conditional and unconditional edges\n let outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n // Branch selection (resume): prefer edges tagged with the decision label.\n if (branchLabel) {\n const labeled = outEdges.filter(e => e.label === branchLabel);\n if (labeled.length > 0) outEdges = labeled;\n }\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string | { dialect?: string; source?: string; ast?: unknown }, variables: Map<string, unknown>): boolean {\n // M9.5+ wiring: route Expression envelopes through @objectstack/formula\n // ExpressionEngine. CEL is the default; legacy `{var}` template syntax\n // is preserved as a fallback for back-compat.\n const isEnvelope = typeof expression === 'object' && expression != null && 'dialect' in expression;\n const dialect = isEnvelope ? (expression as { dialect?: string }).dialect : undefined;\n const exprStr = typeof expression === 'string' ? expression : ((expression as { source?: string })?.source ?? '');\n\n if (isEnvelope && dialect && dialect !== 'cel' && dialect !== 'flow' && dialect !== 'template') {\n // Other dialects (cron, js) are not boolean predicates here.\n return false;\n }\n\n // CEL path — bind `vars` scope for `{step.result}` style references via\n // the equivalent `vars.step.result` CEL identifier path.\n if (dialect === 'cel' || (isEnvelope && !dialect)) {\n try {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n // Convert \"step.result\" keys into nested object paths.\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n // Expose variables two ways under `extra`: as a `vars` namespace\n // (so `vars.step.result` keeps working) AND spread to top level (so\n // bare identifiers like `status` / `previous.status` resolve — the\n // natural authoring style for record-change start conditions).\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source: exprStr },\n { extra: { ...vars, vars }, record: vars },\n );\n if (!result.ok) return false;\n return Boolean(result.value);\n } catch {\n return false;\n }\n }\n\n // Legacy template path: {varName} → value, then primitive compare.\n let resolved = exprStr;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic built-in nodes — decision / assignment / loop.\n *\n * Part of the automation engine's foundational vocabulary, so the core\n * {@link AutomationServicePlugin} seeds them directly (ADR-0018). These are NOT\n * shipped as a separately installable plugin — \"plugins are plugins; the\n * platform's foundational capabilities are built in.\" Third-party node types\n * are still registered via `engine.registerNodeExecutor()`.\n */\nexport function registerLogicNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n descriptor: defineActionDescriptor({\n type: 'decision', version: '1.0.0', name: 'Decision',\n description: 'Branch execution based on conditions.',\n icon: 'git-branch', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n descriptor: defineActionDescriptor({\n type: 'assignment', version: '1.0.0', name: 'Assignment',\n description: 'Set flow variables.',\n icon: 'variable', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n // loop node — iterate over a collection\n engine.registerNodeExecutor({\n type: 'loop',\n descriptor: defineActionDescriptor({\n type: 'loop', version: '1.0.0', name: 'Loop',\n description: 'Iterate over a collection.',\n icon: 'repeat', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const collectionName = config?.collection as string | undefined;\n if (collectionName) {\n const collection = variables.get(collectionName);\n if (Array.isArray(collection)) {\n variables.set('$loopItems', collection);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 3 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * CRUD built-in nodes — `get_record` / `create_record` / `update_record` /\n * `delete_record`, wired to the runtime data layer (ObjectQL / IDataEngine).\n * Part of the platform baseline, so the core {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * Each executor:\n * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in\n * `node.config` against the running flow's variable context.\n * 2. Calls the resolved data engine via `ctx.getService('data')`.\n * 3. Writes the result back to the variable context under `outputVariable`\n * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream\n * nodes can reference fields like `{leadRecord.company}`.\n *\n * If no data engine is registered, executors degrade to a no-op success so\n * test environments without ObjectQL still complete the flow without errors.\n */\nexport function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext): void {\n const getData = (): IDataEngine | undefined => {\n try {\n return ctx.getService<IDataEngine>('data') ?? ctx.getService<IDataEngine>('objectql');\n } catch {\n return undefined;\n }\n };\n\n // ── get_record ────────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'get_record',\n descriptor: defineActionDescriptor({\n type: 'get_record', version: '1.0.0', name: 'Get Records',\n description: 'Query records from an object.',\n icon: 'search', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'get_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = cfg.fields as string[] | undefined;\n const limit = typeof cfg.limit === 'number' ? cfg.limit : undefined;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[get_record] no data engine; skipping ${objectName}`);\n return { success: true, output: { records: [], object: objectName } };\n }\n\n try {\n if (limit && limit > 1) {\n const records = await data.find(objectName, { where: filter, fields, limit });\n if (outputVariable) variables.set(outputVariable, records);\n return { success: true, output: { records, object: objectName } };\n }\n const record = await data.findOne(objectName, { where: filter, fields });\n if (outputVariable) variables.set(outputVariable, record);\n return { success: true, output: { record, id: record?.id, object: objectName } };\n } catch (err) {\n return { success: false, error: `get_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── create_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'create_record',\n descriptor: defineActionDescriptor({\n type: 'create_record', version: '1.0.0', name: 'Create Record',\n description: 'Insert a new record into an object.',\n icon: 'plus-circle', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'create_record: objectName required' };\n\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);\n if (outputVariable) variables.set(outputVariable, `mock-${objectName}-${Date.now()}`);\n return { success: true, output: { id: `mock-${objectName}-${Date.now()}`, object: objectName } };\n }\n\n try {\n const created = await data.insert(objectName, fields);\n const insertedId = Array.isArray(created) ? created[0]?.id : created?.id ?? created;\n if (outputVariable) variables.set(outputVariable, insertedId);\n return { success: true, output: { id: insertedId, record: created, object: objectName } };\n } catch (err) {\n return { success: false, error: `create_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── update_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'update_record',\n descriptor: defineActionDescriptor({\n type: 'update_record', version: '1.0.0', name: 'Update Records',\n description: 'Update records matching a filter.',\n icon: 'edit', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'update_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[update_record] no data engine; skipping ${objectName}`);\n return { success: true };\n }\n\n try {\n const result = await data.update(objectName, fields, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `update_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── delete_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'delete_record',\n descriptor: defineActionDescriptor({\n type: 'delete_record', version: '1.0.0', name: 'Delete Records',\n description: 'Delete records matching a filter.',\n icon: 'trash', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'delete_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) return { success: true };\n\n try {\n const result = await data.delete(objectName, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `delete_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 built-in node executors registered (data-backed)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Template interpolation helpers shared across node executors.\n *\n * Supported syntax (intentionally minimal — no full expression language):\n *\n * {variable} → variables.get('variable')\n * {variable.path.segment} → walks dotted path on the resolved value\n * {$User.Id} → reads from context.userId\n * {$User.Email} → reads from context.user?.email\n * {NOW()} → ISO timestamp at evaluation time\n * {TODAY()} → YYYY-MM-DD at evaluation time\n * {TODAY() + 90} → date + N days (days only, integer)\n *\n * Anything that fails to resolve becomes the literal `null` value (for\n * single-token templates) or the empty string (for embedded substitution),\n * matching the behavior of common low-code formula engines.\n *\n * The interpolator walks objects, arrays, and primitives recursively so it\n * can be applied wholesale to a node's `config.fields`/`config.filter` blocks.\n */\n\nimport type { AutomationContext } from '@objectstack/spec/contracts';\n\nexport type VariableMap = Map<string, unknown>;\n\n/**\n * Resolve a dotted path against a base value.\n * Returns `undefined` for any missing intermediate node.\n */\nfunction resolvePath(base: unknown, path: string[]): unknown {\n let cur: unknown = base;\n for (const seg of path) {\n if (cur == null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Resolve a single template token (without braces) to a value.\n * Returns `undefined` if the token cannot be resolved.\n */\nfunction resolveToken(token: string, variables: VariableMap, context: AutomationContext): unknown {\n const trimmed = token.trim();\n if (!trimmed) return undefined;\n\n // Built-in date helpers — `NOW()` / `TODAY()` with optional `+ N` day offset.\n // The offset may be a literal integer or any token resolvable from `variables`.\n const dateFnMatch = /^(NOW|TODAY)\\s*\\(\\s*\\)\\s*(?:([+\\-])\\s*(\\S+))?$/.exec(trimmed);\n if (dateFnMatch) {\n const fn = dateFnMatch[1];\n const sign = dateFnMatch[2] === '-' ? -1 : 1;\n const offsetRaw = dateFnMatch[3];\n let offset = 0;\n if (offsetRaw) {\n const asNum = Number(offsetRaw);\n if (!isNaN(asNum)) {\n offset = asNum;\n } else if (variables.has(offsetRaw)) {\n offset = Number(variables.get(offsetRaw)) || 0;\n }\n }\n const now = new Date();\n if (offset) now.setDate(now.getDate() + sign * offset);\n if (fn === 'NOW') return now.toISOString();\n return now.toISOString().slice(0, 10);\n }\n\n // $User.* shortcuts\n if (trimmed.startsWith('$User.')) {\n const path = trimmed.slice('$User.'.length).split('.');\n if (path[0] === 'Id') return context.userId;\n if (path[0] === 'Email') return resolvePath((context as any).user, ['email', ...path.slice(1)]) ?? undefined;\n return resolvePath((context as any).user, path);\n }\n\n // Direct variable / dotted path lookup (fast path, no arithmetic).\n if (/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*$/.test(trimmed)) {\n const segments = trimmed.split('.');\n const head = segments[0];\n if (variables.has(head)) {\n return resolvePath(variables.get(head), segments.slice(1));\n }\n if (variables.has(trimmed)) return variables.get(trimmed);\n return undefined;\n }\n\n // Arithmetic / mixed expression: substitute variable references (foo, foo.bar)\n // with their numeric/string literal forms, then evaluate via Function().\n // Restricted to a safe character set (digits, basic operators, parentheses,\n // dots and identifier characters) — never executed on raw user input.\n if (!/^[\\w\\s+\\-*/%().,?:<>=!&|\"'$]+$/.test(trimmed)) return undefined;\n let safe = trimmed;\n safe = safe.replace(/([A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*)/g, (match) => {\n // Don't substitute reserved literals\n if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match;\n const segs = match.split('.');\n const head = segs[0];\n let val: unknown;\n if (variables.has(head)) val = resolvePath(variables.get(head), segs.slice(1));\n else if (variables.has(match)) val = variables.get(match);\n if (val === undefined || val === null) return 'null';\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return JSON.stringify(String(val));\n });\n try {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const fn = new Function(`\"use strict\"; return (${safe});`);\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{...}` tokens in a string with resolved values.\n * - When the entire string is a single token, returns the raw value (preserving type).\n * - Otherwise concatenates string substitutions, with `null`/`undefined` rendered as ''.\n */\nexport function interpolateString(\n input: string,\n variables: VariableMap,\n context: AutomationContext,\n): unknown {\n if (!input.includes('{')) return input;\n const single = /^\\{([^{}]+)\\}$/.exec(input);\n if (single) {\n const value = resolveToken(single[1], variables, context);\n return value;\n }\n return input.replace(/\\{([^{}]+)\\}/g, (_match, expr) => {\n const value = resolveToken(expr, variables, context);\n if (value === undefined || value === null) return '';\n return String(value);\n });\n}\n\n/**\n * Recursively interpolate template tokens in arbitrary JSON-like values.\n */\nexport function interpolate<T = unknown>(\n value: T,\n variables: VariableMap,\n context: AutomationContext,\n): T {\n if (typeof value === 'string') {\n return interpolateString(value, variables, context) as unknown as T;\n }\n if (Array.isArray(value)) {\n return value.map(v => interpolate(v, variables, context)) as unknown as T;\n }\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = interpolate(v, variables, context);\n }\n return out as unknown as T;\n }\n return value;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Screen / Script built-in nodes — 'screen' and 'script' executors.\n * Part of the core flow capability, so the {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * - 'screen' nodes collect user input. A screen that declares `config.fields`\n * (or sets `config.waitForInput === true`) suspends the run on entry via the\n * engine's durable pause (ADR-0019), surfacing a `ScreenSpec` for the client\n * to render; the run continues via `resume()` with the collected values (set\n * as bare flow variables). A field-less screen — or one with\n * `waitForInput === false` — stays a server pass-through (input vars, if any,\n * are already injected from `context.params`).\n * - 'script' nodes dispatch by `config.actionType`. Currently only 'email'\n * has a (logger-backed) implementation; unknown action types still succeed\n * so flows can continue and downstream nodes can react.\n */\nexport function registerScreenNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // screen — server-side pass-through (input vars already injected by engine).\n engine.registerNodeExecutor({\n type: 'screen',\n descriptor: defineActionDescriptor({\n type: 'screen', version: '1.0.0', name: 'Screen',\n description: 'Collect user input via a screen (human-input element).',\n icon: 'window', category: 'human', source: 'builtin',\n // Human-input nodes suspend the flow awaiting input.\n supportsPause: true, isAsync: true,\n }),\n async execute(node, _variables, _context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const rawFields = Array.isArray(cfg.fields) ? (cfg.fields as Array<Record<string, unknown>>) : [];\n const hasFields = rawFields.length > 0;\n // Suspend to collect input when the screen declares fields, or opts in\n // explicitly. `waitForInput === false` forces a server pass-through.\n const shouldPause = cfg.waitForInput === true || (hasFields && cfg.waitForInput !== false);\n if (!shouldPause) {\n return { success: true };\n }\n const fields = rawFields.map((f) => ({\n name: String(f.name ?? ''),\n label: f.label != null ? String(f.label) : undefined,\n type: f.type != null ? String(f.type) : undefined,\n required: f.required === true,\n options: Array.isArray(f.options) ? (f.options as Array<{ value: unknown; label: string }>) : undefined,\n defaultValue: f.defaultValue,\n placeholder: f.placeholder != null ? String(f.placeholder) : undefined,\n })).filter((f) => f.name.length > 0);\n return {\n success: true,\n suspend: true,\n screen: {\n nodeId: node.id,\n title: (cfg.title as string | undefined) ?? node.label ?? 'Input',\n description: cfg.description as string | undefined,\n fields,\n },\n };\n },\n });\n\n // script — dispatch by actionType.\n engine.registerNodeExecutor({\n type: 'script',\n descriptor: defineActionDescriptor({\n type: 'script', version: '1.0.0', name: 'Script',\n description: 'Run a custom script action.',\n icon: 'code', category: 'logic', source: 'builtin',\n }),\n async execute(node, _variables, _context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const actionType = (cfg.actionType as string | undefined) ?? 'noop';\n if (actionType === 'email') {\n ctx.logger.info(\n `[Script:email] template=${String(cfg.template)} ` +\n `recipients=${JSON.stringify(cfg.recipients)} ` +\n `vars=${JSON.stringify(cfg.variables)}`,\n );\n return {\n success: true,\n output: {\n actionType,\n template: cfg.template,\n recipients: cfg.recipients,\n },\n };\n }\n ctx.logger.info(`[Script:${actionType}] node=${node.id} executed (no-op handler)`);\n return { success: true, output: { actionType } };\n },\n });\n\n ctx.logger.info('[Screen/Script Nodes] 2 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * HTTP built-in node — `http_request` (foundational outbound I/O).\n *\n * Part of the platform baseline, so the core {@link AutomationServicePlugin}\n * seeds it directly (ADR-0018). Its generic-dispatch sibling `connector_action`\n * (see {@link ./connector-nodes.ts}) is now also baseline: where `http_request`\n * calls a raw URL, `connector_action` invokes a registered connector's action,\n * with concrete connectors contributed by plugins via `engine.registerConnector()`\n * (ADR-0018 §Addendum).\n *\n * ADR-0018 §M3 target: route `http_request` through the service-messaging\n * outbox (retry / idempotency / dead-letter) under the canonical `http` type.\n * Today it is a bare `fetch()`.\n */\nexport function registerHttpNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // http_request node executor\n engine.registerNodeExecutor({\n type: 'http_request',\n descriptor: defineActionDescriptor({\n type: 'http_request', version: '1.0.0', name: 'HTTP Request',\n description: 'Call an external HTTP endpoint. (ADR-0018: migrates to outbox-backed `http`.)',\n icon: 'globe', category: 'io', source: 'builtin',\n // ADR-0018 §M3 target: route via service-messaging outbox for\n // retry/idempotency/dead-letter. Today this is a bare fetch().\n needsOutbox: false, supportsRetry: true,\n paradigms: ['flow', 'workflow_rule', 'approval'],\n }),\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const url = config?.url as string | undefined;\n const method = (config?.method as string) ?? 'GET';\n const headers = config?.headers as Record<string, string> | undefined;\n const body = config?.body;\n\n if (!url) {\n return { success: false, error: 'http_request: url is required' };\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n const data = await response.json();\n\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n },\n });\n\n ctx.logger.info('[HTTP] 1 built-in node executor registered (http_request)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine, ConnectorActionContext } from '../engine.js';\n\n/**\n * Connector built-in node — `connector_action` (generic integration dispatch).\n *\n * Part of the platform baseline alongside `http_request` (ADR-0018 §Addendum):\n * where `http_request` calls *any raw URL*, `connector_action` invokes *any\n * registered connector's action*. The platform ships the generic dispatch node\n * + an (initially empty) connector registry on the engine; concrete connectors\n * — `connector-rest`, `connector-slack`, `connector-salesforce`, … — populate\n * the registry at runtime via `engine.registerConnector()`.\n *\n * Because the registry starts empty, a flow referencing a connector that no\n * plugin has registered fails the *step* with a clear error rather than failing\n * to register — graceful degradation matching `http_request`'s fail-soft style.\n */\nexport function registerConnectorNodes(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'connector_action',\n descriptor: defineActionDescriptor({\n type: 'connector_action',\n version: '1.0.0',\n name: 'Connector Action',\n description:\n 'Invoke an action on a registered connector (Slack, Salesforce, a REST API, …). '\n + 'The connector itself is contributed by an integration plugin via registerConnector().',\n icon: 'plug',\n category: 'io',\n source: 'builtin',\n supportsRetry: true,\n // Present in all three authoring paradigms (ADR-0018 §registry table).\n paradigms: ['flow', 'workflow_rule', 'approval'],\n // Config contract — drives the Studio property form and flow validation.\n configSchema: {\n type: 'object',\n required: ['connectorId', 'actionId'],\n properties: {\n connectorId: { type: 'string', description: 'Registered connector name' },\n actionId: { type: 'string', description: 'Action key declared by the connector' },\n input: { type: 'object', description: 'Mapped inputs for the action' },\n },\n },\n }),\n async execute(node, variables, context) {\n const cfg = node.connectorConfig;\n if (!cfg?.connectorId || !cfg?.actionId) {\n return {\n success: false,\n error: `connector_action '${node.id}': connectorConfig.connectorId and .actionId are required`,\n };\n }\n\n const handler = engine.resolveConnectorAction(cfg.connectorId, cfg.actionId);\n if (!handler) {\n return {\n success: false,\n error:\n `connector_action '${node.id}': no handler for `\n + `'${cfg.connectorId}.${cfg.actionId}' — is the connector plugin registered?`,\n };\n }\n\n const handlerCtx: ConnectorActionContext = {\n variables,\n automation: context,\n logger: ctx.logger,\n };\n\n try {\n const output = await handler((cfg.input ?? {}) as Record<string, unknown>, handlerCtx);\n return { success: true, output };\n } catch (err) {\n return {\n success: false,\n error: `connector_action(${cfg.connectorId}.${cfg.actionId}) failed: ${(err as Error).message}`,\n };\n }\n },\n });\n\n ctx.logger.info('[Connector] 1 built-in node executor registered (connector_action)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * Structural view of `@objectstack/service-messaging`'s service (ADR-0012),\n * declared locally so service-automation does not take a runtime dependency on\n * it — mirrors the `ConnectorRegistrySurface` pattern. The `notify` node\n * resolves whatever object is registered under the `messaging` service and\n * dispatches through this shape; if no such service is present the node\n * degrades to a no-op success.\n */\nexport interface MessagingServiceSurface {\n emit(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n severity?: string;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n channels?: string[];\n }): Promise<{ notificationId: string; delivered: number; failed: number }>;\n}\n\n/** Coerce a config value (string | string[]) into a clean string[]. */\nfunction toStringList(value: unknown): string[] {\n if (Array.isArray(value)) return value.map((v) => String(v)).filter(Boolean);\n if (typeof value === 'string' && value.trim()) return [value.trim()];\n return [];\n}\n\n/**\n * `notify` built-in node (ADR-0012) — outbound notification.\n *\n * Baseline node and the human-notification counterpart to `http_request`\n * (\"raw call\") and `connector_action` (\"call a registered integration\"):\n * `notify` hands a topic + recipients + message to the platform's messaging\n * service, which fans it out across the user's channels (inbox by default).\n *\n * Like the CRUD nodes degrade without a data engine, `notify` degrades to a\n * warning + success when no `messaging` service is registered — the capability\n * simply isn't installed in that stack. Install `MessagingServicePlugin`\n * (`@objectstack/service-messaging`) and the same flow starts delivering, with\n * no flow edit. This is the seam that fixes the \"notify drops on the floor\"\n * gap (#1292) once messaging is present.\n */\nexport function registerNotifyNode(engine: AutomationEngine, ctx: PluginContext): void {\n const getMessaging = (): MessagingServiceSurface | undefined => {\n try {\n return ctx.getService<MessagingServiceSurface>('messaging');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: 'notify',\n descriptor: defineActionDescriptor({\n type: 'notify', version: '1.0.0', name: 'Notify',\n description: 'Send an outbound notification to users via the messaging service (inbox / email / push / …).',\n icon: 'bell', category: 'io', source: 'builtin',\n supportsRetry: true,\n paradigms: ['flow', 'workflow_rule', 'approval'],\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n\n const recipients = toStringList(interpolate(cfg.recipients ?? cfg.to ?? [], variables, context));\n const title = String(interpolate(cfg.title ?? cfg.subject ?? '', variables, context) ?? '');\n const body = String(interpolate(cfg.message ?? cfg.body ?? '', variables, context) ?? '');\n const channels = toStringList(cfg.channels);\n const topic = cfg.topic ? String(cfg.topic) : undefined;\n const severity = cfg.severity ? String(cfg.severity) : undefined;\n const actionUrl = cfg.actionUrl\n ? String(interpolate(cfg.actionUrl, variables, context) ?? '')\n : undefined;\n const payload = cfg.payload\n ? (interpolate(cfg.payload, variables, context) as Record<string, unknown>)\n : undefined;\n\n if (!title) return { success: false, error: 'notify: title (or subject) is required' };\n if (recipients.length === 0) {\n return { success: false, error: 'notify: at least one recipient is required' };\n }\n\n const messaging = getMessaging();\n if (!messaging) {\n ctx.logger.warn(\n `[notify] no messaging service registered; notification \"${title}\" not delivered`,\n );\n return {\n success: true,\n output: { delivered: 0, failed: 0, skipped: true },\n };\n }\n\n try {\n // ADR-0030 single ingress: hand the messaging service a topic +\n // audience + payload; it writes the L2 event and materializes\n // per channel. title/body/url ride in the payload (templates in\n // a later phase fall back to these).\n const result = await messaging.emit({\n topic: topic ?? 'notify',\n audience: recipients,\n payload: { ...(payload ?? {}), title, body, url: actionUrl },\n severity,\n channels: channels.length ? channels : undefined,\n });\n return {\n success: true,\n output: {\n notificationId: result.notificationId,\n delivered: result.delivered,\n failed: result.failed,\n },\n };\n } catch (err) {\n return { success: false, error: `notify failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[Notify] 1 built-in node executor registered (notify)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Built-in node executors — the automation engine's foundational vocabulary.\n *\n * Per ADR-0018 and the platform principle \"plugins are plugins; the platform's\n * foundational capabilities are built in,\" these node packs are seeded directly\n * by the core {@link AutomationServicePlugin} rather than shipped as separately\n * installable plugins. Each `register*Nodes(engine, ctx)` publishes its\n * descriptors with `source: 'builtin'`.\n *\n * Scope (built-in baseline):\n * - logic — decision / assignment / loop (engine core)\n * - data — get/create/update/delete_record (platform CRUD baseline)\n * - human — screen / script (core flow capability)\n * - io — http_request (foundational outbound I/O)\n * - io — connector_action (generic integration dispatch)\n * - io — notify (outbound notification via messaging service)\n *\n * `connector_action` is the *generic dispatch* counterpart to `http_request`\n * (ADR-0018 §Addendum): the platform ships the node + an (initially empty)\n * connector registry on the engine, and *concrete* connectors populate it at\n * runtime via `engine.registerConnector()`. Third-party node types continue to\n * extend the vocabulary via `engine.registerNodeExecutor()`, keeping the action\n * list open and marketplace-extensible.\n */\n\nimport type { PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\nimport { registerLogicNodes } from './logic-nodes.js';\nimport { registerCrudNodes } from './crud-nodes.js';\nimport { registerScreenNodes } from './screen-nodes.js';\nimport { registerHttpNodes } from './http-nodes.js';\nimport { registerConnectorNodes } from './connector-nodes.js';\nimport { registerNotifyNode } from './notify-node.js';\n\nexport { registerLogicNodes } from './logic-nodes.js';\nexport { registerCrudNodes } from './crud-nodes.js';\nexport { registerScreenNodes } from './screen-nodes.js';\nexport { registerHttpNodes } from './http-nodes.js';\nexport { registerConnectorNodes } from './connector-nodes.js';\nexport { registerNotifyNode } from './notify-node.js';\n\n/**\n * Seed every built-in node executor into the engine. Called by\n * {@link AutomationServicePlugin.init} so a bare `new AutomationServicePlugin()`\n * yields a fully-functional automation capability with no companion plugins.\n */\nexport function installBuiltinNodes(engine: AutomationEngine, ctx: PluginContext): void {\n registerLogicNodes(engine, ctx);\n registerCrudNodes(engine, ctx);\n registerScreenNodes(engine, ctx);\n registerHttpNodes(engine, ctx);\n registerConnectorNodes(engine, ctx);\n registerNotifyNode(engine, ctx);\n\n const types = engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] ${types.length} built-in node executors installed: ${types.join(', ')}`,\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { AutomationEngine } from './engine.js';\nimport { installBuiltinNodes } from './builtin/index.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service, and\n * seed the platform's built-in node executors (logic / CRUD / screen-script /\n * http_request) via {@link installBuiltinNodes}. Per ADR-0018, foundational\n * capabilities are built into the core, not packaged as optional plugins.\n * 2. start phase: Trigger 'automation:ready' hook so third-party plugins can\n * register additional node types, then pull flow definitions from the\n * ObjectQL schema registry and register them with the engine.\n * 3. destroy phase: Clean up resources\n *\n * The engine's `registerNodeExecutor()` stays open so plugins extend the\n * node/action vocabulary at runtime — the marketplace-extensibility contract.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n // Soft dependency on metadata: we look it up at start() and tolerate absence.\n // Do NOT declare a hard kernel dependency, so this plugin works in environments\n // where MetadataPlugin is not registered.\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger);\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n // Seed the platform's built-in node executors. A bare\n // `new AutomationServicePlugin()` is thus a self-contained automation\n // capability — no companion node-pack plugins required (ADR-0018).\n installBuiltinNodes(this.engine, ctx);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n console.warn('[Automation:start] entering start()');\n if (!this.engine) {\n console.warn('[Automation:start] engine missing, bailing');\n return;\n }\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n\n // Pull flow definitions from the ObjectQL schema registry. AppPlugin.init()\n // calls manifest.register(payload), which routes to ql.registerApp() and\n // stores each inline flow under type 'flow'. By the time start() runs,\n // every init() phase has completed, so the registry is fully populated.\n try {\n const ql = ctx.getService<{\n registry?: { listItems?: (type: string) => unknown[] };\n }>('objectql');\n if (!ql) {\n console.warn('[Automation] objectql service not found at start()');\n } else if (!ql.registry) {\n console.warn('[Automation] objectql.registry is undefined at start()');\n } else if (typeof ql.registry.listItems !== 'function') {\n console.warn('[Automation] objectql.registry.listItems is not a function');\n }\n const flows = ql?.registry?.listItems?.('flow') ?? [];\n console.warn(`[Automation] flow pull: registry returned ${flows.length} flow(s)`);\n let registered = 0;\n for (const f of flows) {\n const def = f as { name?: string };\n if (!def?.name) continue;\n try {\n this.engine.registerFlow(def.name, def as never);\n registered++;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n ctx.logger.warn(`[Automation] failed to register flow ${def.name}: ${msg}`);\n }\n }\n if (registered > 0) {\n ctx.logger.info(`[Automation] Pulled ${registered} flow(s) from ObjectQL registry`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.logger.warn(`[Automation] flow pull from ObjectQL registry failed: ${msg}`);\n }\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n"],"mappings":"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","names":["durationMs","import_automation","import_automation","import_automation","import_automation","import_automation","import_automation"]}