@forestadmin/workflow-executor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +141 -0
- package/dist/adapters/activity-log-drainer.d.ts +6 -0
- package/dist/adapters/activity-log-drainer.js +21 -0
- package/dist/adapters/agent-client-agent-port.d.ts +27 -0
- package/dist/adapters/agent-client-agent-port.js +211 -0
- package/dist/adapters/ai-client-adapter.d.ts +11 -0
- package/dist/adapters/ai-client-adapter.js +38 -0
- package/dist/adapters/always-error-ai-model-port.d.ts +8 -0
- package/dist/adapters/always-error-ai-model-port.js +23 -0
- package/dist/adapters/console-logger.d.ts +7 -0
- package/dist/adapters/console-logger.js +15 -0
- package/dist/adapters/forest-server-workflow-port.d.ts +25 -0
- package/dist/adapters/forest-server-workflow-port.js +163 -0
- package/dist/adapters/forestadmin-client-activity-log-port-factory.d.ts +12 -0
- package/dist/adapters/forestadmin-client-activity-log-port-factory.js +22 -0
- package/dist/adapters/forestadmin-client-activity-log-port.d.ts +15 -0
- package/dist/adapters/forestadmin-client-activity-log-port.js +78 -0
- package/dist/adapters/pretty-logger.d.ts +9 -0
- package/dist/adapters/pretty-logger.js +37 -0
- package/dist/adapters/record-id-serializer.d.ts +4 -0
- package/dist/adapters/record-id-serializer.js +20 -0
- package/dist/adapters/run-to-available-step-mapper.d.ts +4 -0
- package/dist/adapters/run-to-available-step-mapper.js +137 -0
- package/dist/adapters/server-ai-adapter.d.ts +16 -0
- package/dist/adapters/server-ai-adapter.js +60 -0
- package/dist/adapters/server-types.d.ts +181 -0
- package/dist/adapters/server-types.js +35 -0
- package/dist/adapters/step-definition-mapper.d.ts +4 -0
- package/dist/adapters/step-definition-mapper.js +68 -0
- package/dist/adapters/step-outcome-to-update-step-mapper.d.ts +4 -0
- package/dist/adapters/step-outcome-to-update-step-mapper.js +34 -0
- package/dist/adapters/with-retry.d.ts +6 -0
- package/dist/adapters/with-retry.js +40 -0
- package/dist/build-workflow-executor.d.ts +35 -0
- package/dist/build-workflow-executor.js +175 -0
- package/dist/cli-core.d.ts +26 -0
- package/dist/cli-core.js +228 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +17 -0
- package/dist/defaults.d.ts +9 -0
- package/dist/defaults.js +12 -0
- package/dist/errors.d.ts +153 -0
- package/dist/errors.js +327 -0
- package/dist/executors/activity-log.d.ts +14 -0
- package/dist/executors/activity-log.js +33 -0
- package/dist/executors/agent-with-log.d.ts +33 -0
- package/dist/executors/agent-with-log.js +76 -0
- package/dist/executors/base-step-executor.d.ts +40 -0
- package/dist/executors/base-step-executor.js +267 -0
- package/dist/executors/condition-step-executor.d.ts +15 -0
- package/dist/executors/condition-step-executor.js +108 -0
- package/dist/executors/guidance-step-executor.d.ts +12 -0
- package/dist/executors/guidance-step-executor.js +39 -0
- package/dist/executors/load-related-record-step-executor.d.ts +38 -0
- package/dist/executors/load-related-record-step-executor.js +478 -0
- package/dist/executors/mcp-step-executor.d.ts +22 -0
- package/dist/executors/mcp-step-executor.js +188 -0
- package/dist/executors/read-record-step-executor.d.ts +10 -0
- package/dist/executors/read-record-step-executor.js +100 -0
- package/dist/executors/record-step-executor.d.ts +19 -0
- package/dist/executors/record-step-executor.js +108 -0
- package/dist/executors/step-executor-factory.d.ts +24 -0
- package/dist/executors/step-executor-factory.js +99 -0
- package/dist/executors/summary/step-execution-formatters.d.ts +8 -0
- package/dist/executors/summary/step-execution-formatters.js +49 -0
- package/dist/executors/summary/step-summary-builder.d.ts +7 -0
- package/dist/executors/summary/step-summary-builder.js +52 -0
- package/dist/executors/trigger-record-action-step-executor.d.ts +17 -0
- package/dist/executors/trigger-record-action-step-executor.js +169 -0
- package/dist/executors/update-record-step-executor.d.ts +13 -0
- package/dist/executors/update-record-step-executor.js +245 -0
- package/dist/http/executor-http-server.d.ts +25 -0
- package/dist/http/executor-http-server.js +170 -0
- package/dist/http/pending-data-validators.d.ts +25 -0
- package/dist/http/pending-data-validators.js +79 -0
- package/dist/http/step-serializer.d.ts +3 -0
- package/dist/http/step-serializer.js +47 -0
- package/dist/in-flight-run-registry.d.ts +9 -0
- package/dist/in-flight-run-registry.js +30 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +88 -0
- package/dist/ports/activity-log-port.d.ts +24 -0
- package/dist/ports/activity-log-port.js +3 -0
- package/dist/ports/agent-port.d.ts +54 -0
- package/dist/ports/agent-port.js +3 -0
- package/dist/ports/ai-model-port.d.ts +7 -0
- package/dist/ports/ai-model-port.js +3 -0
- package/dist/ports/logger-port.d.ts +6 -0
- package/dist/ports/logger-port.js +3 -0
- package/dist/ports/run-store.d.ts +9 -0
- package/dist/ports/run-store.js +3 -0
- package/dist/ports/workflow-port.d.ts +30 -0
- package/dist/ports/workflow-port.js +3 -0
- package/dist/remote-tool-fetcher.d.ts +19 -0
- package/dist/remote-tool-fetcher.js +56 -0
- package/dist/runner.d.ts +50 -0
- package/dist/runner.js +317 -0
- package/dist/schema-cache.d.ts +11 -0
- package/dist/schema-cache.js +37 -0
- package/dist/schema-resolver.d.ts +11 -0
- package/dist/schema-resolver.js +24 -0
- package/dist/stores/build-run-store.d.ts +5 -0
- package/dist/stores/build-run-store.js +28 -0
- package/dist/stores/database-store.d.ts +17 -0
- package/dist/stores/database-store.js +119 -0
- package/dist/stores/in-memory-store.d.ts +11 -0
- package/dist/stores/in-memory-store.js +48 -0
- package/dist/types/execution-context.d.ts +37 -0
- package/dist/types/execution-context.js +3 -0
- package/dist/types/step-execution-data.d.ts +137 -0
- package/dist/types/step-execution-data.js +3 -0
- package/dist/types/validated/collection.d.ts +126 -0
- package/dist/types/validated/collection.js +96 -0
- package/dist/types/validated/execution.d.ts +362 -0
- package/dist/types/validated/execution.js +43 -0
- package/dist/types/validated/step-definition.d.ts +243 -0
- package/dist/types/validated/step-definition.js +128 -0
- package/dist/types/validated/step-outcome.d.ts +108 -0
- package/dist/types/validated/step-outcome.js +66 -0
- package/dist/validate-secrets.d.ts +5 -0
- package/dist/validate-secrets.js +14 -0
- package/package.json +50 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class StepExecutionFormatters {
|
|
4
|
+
// Returns null when no custom format is defined for the step type or when execution data
|
|
5
|
+
// doesn't satisfy formatter preconditions — caller falls back to generic Input:/Output:.
|
|
6
|
+
static format(execution) {
|
|
7
|
+
switch (execution.type) {
|
|
8
|
+
case 'load-related-record':
|
|
9
|
+
return StepExecutionFormatters.formatLoadRelatedRecord(execution);
|
|
10
|
+
case 'mcp':
|
|
11
|
+
return StepExecutionFormatters.formatMcp(execution);
|
|
12
|
+
case 'guidance':
|
|
13
|
+
return StepExecutionFormatters.formatGuidance(execution);
|
|
14
|
+
default:
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
static formatMcp(execution) {
|
|
19
|
+
const { executionResult } = execution;
|
|
20
|
+
if (!executionResult)
|
|
21
|
+
return null;
|
|
22
|
+
if ('skipped' in executionResult)
|
|
23
|
+
return null;
|
|
24
|
+
if (executionResult.formattedResponse) {
|
|
25
|
+
return ` Result: ${executionResult.formattedResponse}`;
|
|
26
|
+
}
|
|
27
|
+
const toolName = execution.executionParams?.name ?? 'unknown tool';
|
|
28
|
+
return ` Executed: ${toolName} (result not summarized)`;
|
|
29
|
+
}
|
|
30
|
+
static formatGuidance(execution) {
|
|
31
|
+
if (!execution.executionResult?.userInput)
|
|
32
|
+
return null;
|
|
33
|
+
return ` The user provided the following input: "${execution.executionResult.userInput}"`;
|
|
34
|
+
}
|
|
35
|
+
static formatLoadRelatedRecord(execution) {
|
|
36
|
+
const { executionResult } = execution;
|
|
37
|
+
if (!executionResult)
|
|
38
|
+
return null; // pending phase — no result yet
|
|
39
|
+
if ('skipped' in executionResult)
|
|
40
|
+
return null; // user skipped — generic fallback
|
|
41
|
+
const { selectedRecordRef } = execution;
|
|
42
|
+
const { relation, record } = executionResult;
|
|
43
|
+
const sourceId = selectedRecordRef.recordId.join(', ');
|
|
44
|
+
const recordId = record.recordId.join(', ');
|
|
45
|
+
return ` Loaded: ${selectedRecordRef.collectionName} #${sourceId} → [${relation.displayName}] → ${record.collectionName} #${recordId} (step ${record.stepIndex})`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.default = StepExecutionFormatters;
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC1leGVjdXRpb24tZm9ybWF0dGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leGVjdXRvcnMvc3VtbWFyeS9zdGVwLWV4ZWN1dGlvbi1mb3JtYXR0ZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBT0EsTUFBcUIsdUJBQXVCO0lBQzFDLHlGQUF5RjtJQUN6Rix5RkFBeUY7SUFDekYsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUE0QjtRQUN4QyxRQUFRLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN2QixLQUFLLHFCQUFxQjtnQkFDeEIsT0FBTyx1QkFBdUIsQ0FBQyx1QkFBdUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNwRSxLQUFLLEtBQUs7Z0JBQ1IsT0FBTyx1QkFBdUIsQ0FBQyxTQUFTLENBQUMsU0FBaUMsQ0FBQyxDQUFDO1lBQzlFLEtBQUssVUFBVTtnQkFDYixPQUFPLHVCQUF1QixDQUFDLGNBQWMsQ0FBQyxTQUFzQyxDQUFDLENBQUM7WUFDeEY7Z0JBQ0UsT0FBTyxJQUFJLENBQUM7UUFDaEIsQ0FBQztJQUNILENBQUM7SUFFTyxNQUFNLENBQUMsU0FBUyxDQUFDLFNBQStCO1FBQ3RELE1BQU0sRUFBRSxlQUFlLEVBQUUsR0FBRyxTQUFTLENBQUM7UUFDdEMsSUFBSSxDQUFDLGVBQWU7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNsQyxJQUFJLFNBQVMsSUFBSSxlQUFlO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFOUMsSUFBSSxlQUFlLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUN0QyxPQUFPLGFBQWEsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDMUQsQ0FBQztRQUVELE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxlQUFlLEVBQUUsSUFBSSxJQUFJLGNBQWMsQ0FBQztRQUVuRSxPQUFPLGVBQWUsUUFBUSwwQkFBMEIsQ0FBQztJQUMzRCxDQUFDO0lBRU8sTUFBTSxDQUFDLGNBQWMsQ0FBQyxTQUFvQztRQUNoRSxJQUFJLENBQUMsU0FBUyxDQUFDLGVBQWUsRUFBRSxTQUFTO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFdkQsT0FBTyw2Q0FBNkMsU0FBUyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEdBQUcsQ0FBQztJQUM3RixDQUFDO0lBRU8sTUFBTSxDQUFDLHVCQUF1QixDQUNwQyxTQUE2QztRQUU3QyxNQUFNLEVBQUUsZUFBZSxFQUFFLEdBQUcsU0FBUyxDQUFDO1FBRXRDLElBQUksQ0FBQyxlQUFlO1lBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQyxnQ0FBZ0M7UUFDbkUsSUFBSSxTQUFTLElBQUksZUFBZTtZQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsa0NBQWtDO1FBRWpGLE1BQU0sRUFBRSxpQkFBaUIsRUFBRSxHQUFHLFNBQVMsQ0FBQztRQUN4QyxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxHQUFHLGVBQWUsQ0FBQztRQUM3QyxNQUFNLFFBQVEsR0FBRyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZELE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRTVDLE9BQU8sYUFBYSxpQkFBaUIsQ0FBQyxjQUFjLEtBQUssUUFBUSxPQUFPLFFBQVEsQ0FBQyxXQUFXLE9BQU8sTUFBTSxDQUFDLGNBQWMsS0FBSyxRQUFRLFVBQVUsTUFBTSxDQUFDLFNBQVMsR0FBRyxDQUFDO0lBQ3JLLENBQUM7Q0FDRjtBQW5ERCwwQ0FtREMifQ==
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StepExecutionData } from '../../types/step-execution-data';
|
|
2
|
+
import type { StepDefinition } from '../../types/validated/step-definition';
|
|
3
|
+
import type { StepOutcome } from '../../types/validated/step-outcome';
|
|
4
|
+
export default class StepSummaryBuilder {
|
|
5
|
+
static build(step: StepDefinition, stepOutcome: StepOutcome, execution: StepExecutionData | undefined): string;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=step-summary-builder.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const step_execution_formatters_1 = __importDefault(require("./step-execution-formatters"));
|
|
7
|
+
class StepSummaryBuilder {
|
|
8
|
+
static build(step, stepOutcome, execution) {
|
|
9
|
+
const prompt = step.prompt ?? '(no prompt)';
|
|
10
|
+
const header = `Step "${stepOutcome.stepId}" (index ${stepOutcome.stepIndex}):`;
|
|
11
|
+
const lines = [header, ` Prompt: ${prompt}`];
|
|
12
|
+
if (execution !== undefined) {
|
|
13
|
+
// Detect "handled manually": executor proposed an action (pendingData) but the user
|
|
14
|
+
// completed the step on the frontend without going through the trigger endpoint, so the
|
|
15
|
+
// executor never wrote executionResult. Normal completions (confirmation flow, skip, Branch B)
|
|
16
|
+
// always set executionResult before the step is marked done.
|
|
17
|
+
if (stepOutcome.status === 'success' &&
|
|
18
|
+
'pendingData' in execution &&
|
|
19
|
+
execution.pendingData !== undefined &&
|
|
20
|
+
execution.executionResult === undefined) {
|
|
21
|
+
lines.push(` Proposed: ${JSON.stringify(execution.pendingData)}`);
|
|
22
|
+
lines.push(` Note: the user handled this step manually — the actual outcome may differ from the proposal above.`);
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
// Try custom formatting — if it fires, it owns the entire output section (no Input: line)
|
|
26
|
+
const customLine = execution.executionResult
|
|
27
|
+
? step_execution_formatters_1.default.format(execution)
|
|
28
|
+
: null;
|
|
29
|
+
if (customLine !== null) {
|
|
30
|
+
lines.push(customLine);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if ('executionParams' in execution && execution.executionParams !== undefined) {
|
|
34
|
+
lines.push(` Input: ${JSON.stringify(execution.executionParams)}`);
|
|
35
|
+
}
|
|
36
|
+
else if ('pendingData' in execution && execution.pendingData !== undefined) {
|
|
37
|
+
lines.push(` Pending: ${JSON.stringify(execution.pendingData)}`);
|
|
38
|
+
}
|
|
39
|
+
if (execution.executionResult) {
|
|
40
|
+
lines.push(` Output: ${JSON.stringify(execution.executionResult)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const { stepId, stepIndex, type, ...historyDetails } = stepOutcome;
|
|
46
|
+
lines.push(` History: ${JSON.stringify(historyDetails)}`);
|
|
47
|
+
}
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.default = StepSummaryBuilder;
|
|
52
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC1zdW1tYXJ5LWJ1aWxkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXhlY3V0b3JzL3N1bW1hcnkvc3RlcC1zdW1tYXJ5LWJ1aWxkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFJQSw0RkFBa0U7QUFFbEUsTUFBcUIsa0JBQWtCO0lBQ3JDLE1BQU0sQ0FBQyxLQUFLLENBQ1YsSUFBb0IsRUFDcEIsV0FBd0IsRUFDeEIsU0FBd0M7UUFFeEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sSUFBSSxhQUFhLENBQUM7UUFDNUMsTUFBTSxNQUFNLEdBQUcsU0FBUyxXQUFXLENBQUMsTUFBTSxZQUFZLFdBQVcsQ0FBQyxTQUFTLElBQUksQ0FBQztRQUNoRixNQUFNLEtBQUssR0FBRyxDQUFDLE1BQU0sRUFBRSxhQUFhLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFFOUMsSUFBSSxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDNUIsb0ZBQW9GO1lBQ3BGLHdGQUF3RjtZQUN4RiwrRkFBK0Y7WUFDL0YsNkRBQTZEO1lBQzdELElBQ0UsV0FBVyxDQUFDLE1BQU0sS0FBSyxTQUFTO2dCQUNoQyxhQUFhLElBQUksU0FBUztnQkFDMUIsU0FBUyxDQUFDLFdBQVcsS0FBSyxTQUFTO2dCQUNuQyxTQUFTLENBQUMsZUFBZSxLQUFLLFNBQVMsRUFDdkMsQ0FBQztnQkFDRCxLQUFLLENBQUMsSUFBSSxDQUFDLGVBQWUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNuRSxLQUFLLENBQUMsSUFBSSxDQUNSLHNHQUFzRyxDQUN2RyxDQUFDO2dCQUVGLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMxQixDQUFDO1lBRUQsMEZBQTBGO1lBQzFGLE1BQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQyxlQUFlO2dCQUMxQyxDQUFDLENBQUMsbUNBQXVCLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQztnQkFDM0MsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUVULElBQUksVUFBVSxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUN4QixLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ3pCLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLGlCQUFpQixJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsZUFBZSxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUM5RSxLQUFLLENBQUMsSUFBSSxDQUFDLFlBQVksSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0RSxDQUFDO3FCQUFNLElBQUksYUFBYSxJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUM3RSxLQUFLLENBQUMsSUFBSSxDQUFDLGNBQWMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRSxDQUFDO2dCQUVELElBQUksU0FBUyxDQUFDLGVBQWUsRUFBRSxDQUFDO29CQUM5QixLQUFLLENBQUMsSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN2RSxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLEdBQUcsY0FBYyxFQUFFLEdBQUcsV0FBVyxDQUFDO1lBQ25FLEtBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxJQUFJLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM3RCxDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUM7Q0FDRjtBQXRERCxxQ0FzREMifQ==
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StepExecutionResult } from '../types/execution-context';
|
|
2
|
+
import type { TriggerActionStepDefinition } from '../types/validated/step-definition';
|
|
3
|
+
import RecordStepExecutor from './record-step-executor';
|
|
4
|
+
export default class TriggerRecordActionStepExecutor extends RecordStepExecutor<TriggerActionStepDefinition> {
|
|
5
|
+
protected checkIdempotency(): Promise<StepExecutionResult | null>;
|
|
6
|
+
protected doExecute(): Promise<StepExecutionResult>;
|
|
7
|
+
private handleFirstCall;
|
|
8
|
+
/** Branch B — executor runs the action via the audited agent, then persists the result. */
|
|
9
|
+
private executeOnExecutor;
|
|
10
|
+
/** Branch A — the frontend executed the action; executor only persists the result it sent. */
|
|
11
|
+
private saveFrontendResult;
|
|
12
|
+
private selectAction;
|
|
13
|
+
private buildSelectActionTool;
|
|
14
|
+
private findActionByTechnicalName;
|
|
15
|
+
private findAction;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=trigger-record-action-step-executor.d.ts.map
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const ai_proxy_1 = require("@forestadmin/ai-proxy");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const record_step_executor_1 = __importDefault(require("./record-step-executor"));
|
|
10
|
+
const step_definition_1 = require("../types/validated/step-definition");
|
|
11
|
+
const TRIGGER_ACTION_SYSTEM_PROMPT = `You are an AI agent triggering an action on a record based on a user request.
|
|
12
|
+
Select the action to trigger.
|
|
13
|
+
|
|
14
|
+
Important rules:
|
|
15
|
+
- Be precise: only trigger the action directly relevant to the request.
|
|
16
|
+
- Final answer is definitive, you won't receive any other input from the user.
|
|
17
|
+
- Do not refer to yourself as "I" in the response, use a passive formulation instead.`;
|
|
18
|
+
class TriggerRecordActionStepExecutor extends record_step_executor_1.default {
|
|
19
|
+
async checkIdempotency() {
|
|
20
|
+
const existing = await this.findPendingExecution('trigger-action');
|
|
21
|
+
if (existing?.idempotencyPhase === 'done') {
|
|
22
|
+
return this.buildOutcomeResult({ status: 'success' });
|
|
23
|
+
}
|
|
24
|
+
if (existing?.idempotencyPhase === 'executing') {
|
|
25
|
+
throw new errors_1.StepStateError('Step execution was interrupted. Please retry the step manually.');
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
async doExecute() {
|
|
30
|
+
// Branch A -- Re-entry after pending execution found in RunStore
|
|
31
|
+
const pending = await this.patchAndReloadPendingData(this.context.incomingPendingData);
|
|
32
|
+
if (pending) {
|
|
33
|
+
return this.handleConfirmationFlow(pending, async (exec) => {
|
|
34
|
+
const { selectedRecordRef, pendingData, userConfirmation } = exec;
|
|
35
|
+
// The frontend executes the action itself and posts the result back.
|
|
36
|
+
// A confirmed step without actionResult is a broken frontend contract.
|
|
37
|
+
if (!pendingData || !userConfirmation || !('actionResult' in userConfirmation)) {
|
|
38
|
+
throw new errors_1.StepStateError(`Frontend confirmed action but did not provide actionResult ` +
|
|
39
|
+
`(run "${this.context.runId}", step ${this.context.stepIndex})`);
|
|
40
|
+
}
|
|
41
|
+
const target = {
|
|
42
|
+
selectedRecordRef,
|
|
43
|
+
displayName: pendingData.displayName,
|
|
44
|
+
name: pendingData.name,
|
|
45
|
+
};
|
|
46
|
+
return this.saveFrontendResult(target, userConfirmation.actionResult, exec);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Branches B & C -- First call
|
|
50
|
+
return this.handleFirstCall();
|
|
51
|
+
}
|
|
52
|
+
async handleFirstCall() {
|
|
53
|
+
const { stepDefinition: step } = this.context;
|
|
54
|
+
const { preRecordedArgs } = step;
|
|
55
|
+
const records = await this.getAvailableRecordRefs();
|
|
56
|
+
const selectedRecordRef = await this.resolveRecordRef(records, step.prompt, preRecordedArgs?.selectedRecordStepIndex);
|
|
57
|
+
const schema = await this.getCollectionSchema(selectedRecordRef.collectionName);
|
|
58
|
+
const recordedAction = preRecordedArgs?.actionName;
|
|
59
|
+
const actionName = recordedAction ?? (await this.selectAction(schema, step.prompt)).actionName;
|
|
60
|
+
const action = this.findActionByTechnicalName(schema, actionName);
|
|
61
|
+
if (!action) {
|
|
62
|
+
throw new errors_1.ActionNotFoundError(actionName, schema.collectionName);
|
|
63
|
+
}
|
|
64
|
+
const target = {
|
|
65
|
+
selectedRecordRef,
|
|
66
|
+
displayName: action.displayName,
|
|
67
|
+
name: action.name,
|
|
68
|
+
};
|
|
69
|
+
// Branch B -- fully automated: executor runs the action itself, so it cannot
|
|
70
|
+
// handle forms (no UI to fill them). Reject form-bearing actions here. When the
|
|
71
|
+
// frontend is in the loop (Branch C), it handles the form natively so no check.
|
|
72
|
+
if (step.executionType === step_definition_1.StepExecutionMode.FullyAutomated) {
|
|
73
|
+
const { hasForm } = await this.context.agent.getActionFormInfo({
|
|
74
|
+
collection: selectedRecordRef.collectionName,
|
|
75
|
+
action: target.name,
|
|
76
|
+
id: selectedRecordRef.recordId,
|
|
77
|
+
});
|
|
78
|
+
if (hasForm)
|
|
79
|
+
throw new errors_1.UnsupportedActionFormError(target.displayName);
|
|
80
|
+
return this.executeOnExecutor(target);
|
|
81
|
+
}
|
|
82
|
+
// Branch C -- Awaiting confirmation (frontend executes the action, including forms)
|
|
83
|
+
await this.context.runStore.saveStepExecution(this.context.runId, {
|
|
84
|
+
type: 'trigger-action',
|
|
85
|
+
stepIndex: this.context.stepIndex,
|
|
86
|
+
pendingData: { displayName: target.displayName, name: target.name },
|
|
87
|
+
selectedRecordRef: target.selectedRecordRef,
|
|
88
|
+
});
|
|
89
|
+
return this.buildOutcomeResult({ status: 'awaiting-input' });
|
|
90
|
+
}
|
|
91
|
+
/** Branch B — executor runs the action via the audited agent, then persists the result. */
|
|
92
|
+
async executeOnExecutor(target) {
|
|
93
|
+
const { selectedRecordRef, displayName, name } = target;
|
|
94
|
+
const actionResult = await this.context.agent.executeAction({
|
|
95
|
+
collection: selectedRecordRef.collectionName,
|
|
96
|
+
action: name,
|
|
97
|
+
id: selectedRecordRef.recordId,
|
|
98
|
+
}, {
|
|
99
|
+
beforeCall: () => this.context.runStore.saveStepExecution(this.context.runId, {
|
|
100
|
+
type: 'trigger-action',
|
|
101
|
+
stepIndex: this.context.stepIndex,
|
|
102
|
+
selectedRecordRef,
|
|
103
|
+
idempotencyPhase: 'executing',
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
await this.context.runStore.saveStepExecution(this.context.runId, {
|
|
107
|
+
type: 'trigger-action',
|
|
108
|
+
stepIndex: this.context.stepIndex,
|
|
109
|
+
executionParams: { displayName, name },
|
|
110
|
+
executionResult: { success: true, actionResult },
|
|
111
|
+
selectedRecordRef,
|
|
112
|
+
idempotencyPhase: 'done',
|
|
113
|
+
});
|
|
114
|
+
return this.buildOutcomeResult({ status: 'success' });
|
|
115
|
+
}
|
|
116
|
+
/** Branch A — the frontend executed the action; executor only persists the result it sent. */
|
|
117
|
+
async saveFrontendResult(target, actionResult, existingExecution) {
|
|
118
|
+
const { selectedRecordRef, displayName, name } = target;
|
|
119
|
+
await this.context.runStore.saveStepExecution(this.context.runId, {
|
|
120
|
+
...existingExecution,
|
|
121
|
+
type: 'trigger-action',
|
|
122
|
+
stepIndex: this.context.stepIndex,
|
|
123
|
+
executionParams: { displayName, name },
|
|
124
|
+
executionResult: { success: true, actionResult },
|
|
125
|
+
selectedRecordRef,
|
|
126
|
+
});
|
|
127
|
+
return this.buildOutcomeResult({ status: 'success' });
|
|
128
|
+
}
|
|
129
|
+
async selectAction(schema, prompt) {
|
|
130
|
+
const tool = this.buildSelectActionTool(schema);
|
|
131
|
+
const messages = [
|
|
132
|
+
this.buildContextMessage(),
|
|
133
|
+
...(await this.buildPreviousStepsMessages()),
|
|
134
|
+
new ai_proxy_1.SystemMessage(TRIGGER_ACTION_SYSTEM_PROMPT),
|
|
135
|
+
new ai_proxy_1.SystemMessage(`The selected record belongs to the "${schema.collectionDisplayName}" collection.`),
|
|
136
|
+
new ai_proxy_1.HumanMessage(`**Request**: ${prompt ?? 'Trigger the relevant action.'}`),
|
|
137
|
+
];
|
|
138
|
+
const { actionName } = await this.invokeWithTool(messages, tool);
|
|
139
|
+
return { actionName: this.findAction(schema, actionName)?.name ?? actionName };
|
|
140
|
+
}
|
|
141
|
+
buildSelectActionTool(schema) {
|
|
142
|
+
if (schema.actions.length === 0) {
|
|
143
|
+
throw new errors_1.NoActionsError(schema.collectionName);
|
|
144
|
+
}
|
|
145
|
+
const displayNames = schema.actions.map(a => a.displayName);
|
|
146
|
+
const technicalNames = schema.actions
|
|
147
|
+
.map(a => `${a.displayName} (technical name: ${a.name})`)
|
|
148
|
+
.join(', ');
|
|
149
|
+
return new ai_proxy_1.DynamicStructuredTool({
|
|
150
|
+
name: 'select-action',
|
|
151
|
+
description: 'Select the action to trigger on the record.',
|
|
152
|
+
schema: zod_1.z.object({
|
|
153
|
+
actionName: zod_1.z
|
|
154
|
+
.enum(displayNames)
|
|
155
|
+
.describe(`The name of the action to trigger. Available: ${technicalNames}`),
|
|
156
|
+
reasoning: zod_1.z.string().describe('Why this action was chosen'),
|
|
157
|
+
}),
|
|
158
|
+
func: undefined,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
findActionByTechnicalName(schema, name) {
|
|
162
|
+
return schema.actions.find(a => a.name === name);
|
|
163
|
+
}
|
|
164
|
+
findAction(schema, name) {
|
|
165
|
+
return (schema.actions.find(a => a.displayName === name) ?? schema.actions.find(a => a.name === name));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.default = TriggerRecordActionStepExecutor;
|
|
169
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJpZ2dlci1yZWNvcmQtYWN0aW9uLXN0ZXAtZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXhlY3V0b3JzL3RyaWdnZXItcmVjb3JkLWFjdGlvbi1zdGVwLWV4ZWN1dG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0Esb0RBQTJGO0FBQzNGLDZCQUF3QjtBQUV4QixzQ0FLbUI7QUFDbkIsa0ZBQXdEO0FBQ3hELHdFQUF1RTtBQUV2RSxNQUFNLDRCQUE0QixHQUFHOzs7Ozs7c0ZBTWlELENBQUM7QUFNdkYsTUFBcUIsK0JBQWdDLFNBQVEsOEJBQStDO0lBQ3ZGLEtBQUssQ0FBQyxnQkFBZ0I7UUFDdkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQzlDLGdCQUFnQixDQUNqQixDQUFDO1FBRUYsSUFBSSxRQUFRLEVBQUUsZ0JBQWdCLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDMUMsT0FBTyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBRUQsSUFBSSxRQUFRLEVBQUUsZ0JBQWdCLEtBQUssV0FBVyxFQUFFLENBQUM7WUFDL0MsTUFBTSxJQUFJLHVCQUFjLENBQUMsaUVBQWlFLENBQUMsQ0FBQztRQUM5RixDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRVMsS0FBSyxDQUFDLFNBQVM7UUFDdkIsaUVBQWlFO1FBQ2pFLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLHlCQUF5QixDQUNsRCxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUNqQyxDQUFDO1FBRUYsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUNoQyxPQUFPLEVBQ1AsS0FBSyxFQUFDLElBQUksRUFBQyxFQUFFO2dCQUNYLE1BQU0sRUFBRSxpQkFBaUIsRUFB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
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { StepExecutionResult } from '../types/execution-context';
|
|
2
|
+
import type { UpdateRecordStepDefinition } from '../types/validated/step-definition';
|
|
3
|
+
import RecordStepExecutor from './record-step-executor';
|
|
4
|
+
export default class UpdateRecordStepExecutor extends RecordStepExecutor<UpdateRecordStepDefinition> {
|
|
5
|
+
protected checkIdempotency(): Promise<StepExecutionResult | null>;
|
|
6
|
+
protected doExecute(): Promise<StepExecutionResult>;
|
|
7
|
+
private coerceOverride;
|
|
8
|
+
private handleFirstCall;
|
|
9
|
+
private resolveAndUpdate;
|
|
10
|
+
private selectFieldAndValue;
|
|
11
|
+
private buildUpdateFieldTool;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=update-record-step-executor.d.ts.map
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const ai_proxy_1 = require("@forestadmin/ai-proxy");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const record_step_executor_1 = __importDefault(require("./record-step-executor"));
|
|
10
|
+
const step_definition_1 = require("../types/validated/step-definition");
|
|
11
|
+
const UPDATE_RECORD_SYSTEM_PROMPT = `You are an AI agent updating a field on a record based on a user request.
|
|
12
|
+
Select the field to update and provide the new value.
|
|
13
|
+
|
|
14
|
+
Important rules:
|
|
15
|
+
- Be precise: only update the field that is directly relevant to the request.
|
|
16
|
+
- Final answer is definitive, you won't receive any other input from the user.
|
|
17
|
+
- Do not refer to yourself as "I" in the response, use a passive formulation instead.`;
|
|
18
|
+
const jsonStringSchema = zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.refine(val => {
|
|
21
|
+
try {
|
|
22
|
+
JSON.parse(val);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}, { message: 'Must be a valid JSON string' })
|
|
29
|
+
.describe('JSON content as a valid JSON string');
|
|
30
|
+
function buildZodSchemaForPrimitive(type, enumValues) {
|
|
31
|
+
switch (type) {
|
|
32
|
+
case 'Boolean':
|
|
33
|
+
return zod_1.z.preprocess(val => {
|
|
34
|
+
if (typeof val !== 'string')
|
|
35
|
+
return val;
|
|
36
|
+
if (val === 'true')
|
|
37
|
+
return true;
|
|
38
|
+
if (val === 'false')
|
|
39
|
+
return false;
|
|
40
|
+
return val;
|
|
41
|
+
}, zod_1.z.boolean());
|
|
42
|
+
case 'Date':
|
|
43
|
+
return zod_1.z.iso.datetime().describe('ISO 8601 datetime, e.g. 2024-06-01T00:00:00Z');
|
|
44
|
+
case 'Dateonly':
|
|
45
|
+
return zod_1.z.iso.date().describe('ISO 8601 date, e.g. 2024-06-01');
|
|
46
|
+
case 'Number':
|
|
47
|
+
return zod_1.z.coerce.number();
|
|
48
|
+
case 'Enum':
|
|
49
|
+
if (enumValues && enumValues.length >= 2) {
|
|
50
|
+
return zod_1.z.enum(enumValues);
|
|
51
|
+
}
|
|
52
|
+
if (enumValues?.length === 1)
|
|
53
|
+
return zod_1.z.literal(enumValues[0]);
|
|
54
|
+
return zod_1.z.string();
|
|
55
|
+
case 'Json':
|
|
56
|
+
return jsonStringSchema;
|
|
57
|
+
case 'Point':
|
|
58
|
+
return zod_1.z.array(zod_1.z.number()).length(2).describe('[longitude, latitude]');
|
|
59
|
+
// String, Uuid, Time, Binary, Timeonly, File → plain string
|
|
60
|
+
default:
|
|
61
|
+
return zod_1.z.string();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function buildZodSchemaForField(field, collectionName) {
|
|
65
|
+
const { type, enumValues } = field;
|
|
66
|
+
// A writable (non-relationship) field with no column type is a malformed schema for this update:
|
|
67
|
+
// we'd otherwise fall through to z.string() and silently write the wrong type. Fail visibly.
|
|
68
|
+
if (type == null) {
|
|
69
|
+
throw new errors_1.FieldTypeMissingError(field.displayName, collectionName);
|
|
70
|
+
}
|
|
71
|
+
if (Array.isArray(type)) {
|
|
72
|
+
// Nested array (e.g. [['String']]) → treat as opaque JSON.
|
|
73
|
+
if (Array.isArray(type[0]))
|
|
74
|
+
return zod_1.z.array(jsonStringSchema);
|
|
75
|
+
return zod_1.z.array(buildZodSchemaForPrimitive(type[0], enumValues));
|
|
76
|
+
}
|
|
77
|
+
if (typeof type === 'object' && type !== null) {
|
|
78
|
+
return jsonStringSchema;
|
|
79
|
+
}
|
|
80
|
+
return buildZodSchemaForPrimitive(type, enumValues);
|
|
81
|
+
}
|
|
82
|
+
// Coerce a user-overridden value to the field's native type before updating the record.
|
|
83
|
+
// The HTTP schema accepts `unknown`, so the override may be a boolean or an array; this turns
|
|
84
|
+
// it into the type the datasource expects, and throws a StepStateError on mismatch.
|
|
85
|
+
function coerceFieldValue(fieldSchema, value, collectionName) {
|
|
86
|
+
// Field not found, relationship (type intentionally null), or explicit null → nothing to coerce.
|
|
87
|
+
if (!fieldSchema || fieldSchema.isRelationship || value === null)
|
|
88
|
+
return value;
|
|
89
|
+
const parsed = buildZodSchemaForField(fieldSchema, collectionName).safeParse(value);
|
|
90
|
+
if (!parsed.success) {
|
|
91
|
+
throw new errors_1.StepStateError(`Invalid value for field "${fieldSchema.displayName}": ${parsed.error.issues
|
|
92
|
+
.map(issue => issue.message)
|
|
93
|
+
.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
return parsed.data;
|
|
96
|
+
}
|
|
97
|
+
class UpdateRecordStepExecutor extends record_step_executor_1.default {
|
|
98
|
+
async checkIdempotency() {
|
|
99
|
+
const existing = await this.findPendingExecution('update-record');
|
|
100
|
+
if (existing?.idempotencyPhase === 'done') {
|
|
101
|
+
return this.buildOutcomeResult({ status: 'success' });
|
|
102
|
+
}
|
|
103
|
+
if (existing?.idempotencyPhase === 'executing') {
|
|
104
|
+
throw new errors_1.StepStateError('Step execution was interrupted. Please retry the step manually.');
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
async doExecute() {
|
|
109
|
+
// Branch A -- Re-entry after pending execution found in RunStore
|
|
110
|
+
const pending = await this.patchAndReloadPendingData(this.context.incomingPendingData);
|
|
111
|
+
if (pending) {
|
|
112
|
+
return this.handleConfirmationFlow(pending, async (exec) => {
|
|
113
|
+
const { selectedRecordRef, pendingData, userConfirmation } = exec;
|
|
114
|
+
// A user override of `null` (clearing the field) must win over the AI suggestion, so
|
|
115
|
+
// distinguish "no override" (undefined) from "override to null".
|
|
116
|
+
const rawValue = userConfirmation?.value !== undefined ? userConfirmation.value : pendingData.value;
|
|
117
|
+
const target = {
|
|
118
|
+
selectedRecordRef,
|
|
119
|
+
...pendingData,
|
|
120
|
+
// The value comes from an `unknown` HTTP value (may be a boolean or array), so coerce
|
|
121
|
+
// it to the field's native type before updating. Idempotent on already-typed values.
|
|
122
|
+
value: await this.coerceOverride(selectedRecordRef, pendingData, rawValue),
|
|
123
|
+
};
|
|
124
|
+
return this.resolveAndUpdate(target, exec);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Branches B & C -- First call
|
|
128
|
+
return this.handleFirstCall();
|
|
129
|
+
}
|
|
130
|
+
async coerceOverride(selectedRecordRef, pendingData, value) {
|
|
131
|
+
const schema = await this.getCollectionSchema(selectedRecordRef.collectionName);
|
|
132
|
+
const fieldSchema = this.findFieldByTechnicalName(schema, pendingData?.name);
|
|
133
|
+
return coerceFieldValue(fieldSchema, value, selectedRecordRef.collectionName);
|
|
134
|
+
}
|
|
135
|
+
async handleFirstCall() {
|
|
136
|
+
const { stepDefinition: step } = this.context;
|
|
137
|
+
const { preRecordedArgs } = step;
|
|
138
|
+
const records = await this.getAvailableRecordRefs();
|
|
139
|
+
const selectedRecordRef = await this.resolveRecordRef(records, step.prompt, preRecordedArgs?.selectedRecordStepIndex);
|
|
140
|
+
const schema = await this.getCollectionSchema(selectedRecordRef.collectionName);
|
|
141
|
+
if (preRecordedArgs?.fieldName !== undefined && preRecordedArgs?.value === undefined) {
|
|
142
|
+
throw new errors_1.InvalidPreRecordedArgsError('fieldName and value must both be provided or both omitted');
|
|
143
|
+
}
|
|
144
|
+
if (preRecordedArgs?.value !== undefined && preRecordedArgs?.fieldName === undefined) {
|
|
145
|
+
throw new errors_1.InvalidPreRecordedArgsError('fieldName and value must both be provided or both omitted');
|
|
146
|
+
}
|
|
147
|
+
const recordedField = preRecordedArgs?.fieldName;
|
|
148
|
+
const { fieldName, value } = recordedField !== undefined
|
|
149
|
+
? { fieldName: recordedField, value: preRecordedArgs?.value }
|
|
150
|
+
: await this.selectFieldAndValue(schema, step.prompt);
|
|
151
|
+
const field = this.findFieldByTechnicalName(schema, fieldName);
|
|
152
|
+
if (!field) {
|
|
153
|
+
throw new errors_1.FieldNotFoundError(fieldName, schema.collectionName);
|
|
154
|
+
}
|
|
155
|
+
const target = {
|
|
156
|
+
selectedRecordRef,
|
|
157
|
+
displayName: field.displayName,
|
|
158
|
+
name: field.fieldName,
|
|
159
|
+
value,
|
|
160
|
+
};
|
|
161
|
+
// Branch B -- fully automated execution
|
|
162
|
+
if (step.executionType === step_definition_1.StepExecutionMode.FullyAutomated) {
|
|
163
|
+
return this.resolveAndUpdate(target);
|
|
164
|
+
}
|
|
165
|
+
// Branch C -- Awaiting confirmation
|
|
166
|
+
await this.context.runStore.saveStepExecution(this.context.runId, {
|
|
167
|
+
type: 'update-record',
|
|
168
|
+
stepIndex: this.context.stepIndex,
|
|
169
|
+
pendingData: {
|
|
170
|
+
displayName: target.displayName,
|
|
171
|
+
name: target.name,
|
|
172
|
+
value: target.value,
|
|
173
|
+
},
|
|
174
|
+
selectedRecordRef: target.selectedRecordRef,
|
|
175
|
+
});
|
|
176
|
+
return this.buildOutcomeResult({ status: 'awaiting-input' });
|
|
177
|
+
}
|
|
178
|
+
// existingExecution (confirmation flow) is spread into the saved execution to preserve pendingData.
|
|
179
|
+
async resolveAndUpdate(target, existingExecution) {
|
|
180
|
+
const { selectedRecordRef, displayName, name, value } = target;
|
|
181
|
+
const updated = await this.context.agent.updateRecord({
|
|
182
|
+
collection: selectedRecordRef.collectionName,
|
|
183
|
+
id: selectedRecordRef.recordId,
|
|
184
|
+
values: { [name]: value },
|
|
185
|
+
}, {
|
|
186
|
+
beforeCall: () => this.context.runStore.saveStepExecution(this.context.runId, {
|
|
187
|
+
...existingExecution,
|
|
188
|
+
type: 'update-record',
|
|
189
|
+
stepIndex: this.context.stepIndex,
|
|
190
|
+
selectedRecordRef,
|
|
191
|
+
idempotencyPhase: 'executing',
|
|
192
|
+
}),
|
|
193
|
+
});
|
|
194
|
+
await this.context.runStore.saveStepExecution(this.context.runId, {
|
|
195
|
+
...existingExecution,
|
|
196
|
+
type: 'update-record',
|
|
197
|
+
stepIndex: this.context.stepIndex,
|
|
198
|
+
executionParams: { displayName, name, value },
|
|
199
|
+
executionResult: { updatedValues: updated.values },
|
|
200
|
+
selectedRecordRef,
|
|
201
|
+
idempotencyPhase: 'done',
|
|
202
|
+
});
|
|
203
|
+
return this.buildOutcomeResult({ status: 'success' });
|
|
204
|
+
}
|
|
205
|
+
async selectFieldAndValue(schema, prompt) {
|
|
206
|
+
const tool = this.buildUpdateFieldTool(schema);
|
|
207
|
+
const messages = [
|
|
208
|
+
this.buildContextMessage(),
|
|
209
|
+
...(await this.buildPreviousStepsMessages()),
|
|
210
|
+
new ai_proxy_1.SystemMessage(UPDATE_RECORD_SYSTEM_PROMPT),
|
|
211
|
+
new ai_proxy_1.SystemMessage(`The selected record belongs to the "${schema.collectionDisplayName}" collection.`),
|
|
212
|
+
new ai_proxy_1.HumanMessage(`**Request**: ${prompt ?? 'Update the relevant field.'}`),
|
|
213
|
+
];
|
|
214
|
+
const { input } = await this.invokeWithTool(messages, tool);
|
|
215
|
+
return {
|
|
216
|
+
fieldName: this.resolveAiFieldName(schema, input.fieldName),
|
|
217
|
+
value: input.value,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
buildUpdateFieldTool(schema) {
|
|
221
|
+
// Exclude type-less fields: they can't be coerced/written, so offering them to the AI would
|
|
222
|
+
// let a single drifted field fail the whole step. The override path still rejects an explicit
|
|
223
|
+
// type-less target via FieldTypeMissingError.
|
|
224
|
+
const nonRelationFields = schema.fields.filter(f => !f.isRelationship && f.type != null);
|
|
225
|
+
if (nonRelationFields.length === 0) {
|
|
226
|
+
throw new errors_1.NoWritableFieldsError(schema.collectionName);
|
|
227
|
+
}
|
|
228
|
+
const fieldObjects = nonRelationFields.map(f => zod_1.z.object({
|
|
229
|
+
fieldName: zod_1.z.literal(f.displayName),
|
|
230
|
+
value: buildZodSchemaForField(f, schema.collectionName).nullable(),
|
|
231
|
+
reasoning: zod_1.z.string().describe('Why this field and value were chosen'),
|
|
232
|
+
}));
|
|
233
|
+
const inputSchema = fieldObjects.length === 1
|
|
234
|
+
? fieldObjects[0]
|
|
235
|
+
: zod_1.z.union(fieldObjects);
|
|
236
|
+
return new ai_proxy_1.DynamicStructuredTool({
|
|
237
|
+
name: 'update-record-field',
|
|
238
|
+
description: 'Update a field on the selected record.',
|
|
239
|
+
schema: zod_1.z.object({ input: inputSchema }),
|
|
240
|
+
func: undefined,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
exports.default = UpdateRecordStepExecutor;
|
|
245
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLXJlY29yZC1zdGVwLWV4ZWN1dG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2V4ZWN1dG9ycy91cGRhdGUtcmVjb3JkLXN0ZXAtZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFLQSxvREFBMkY7QUFDM0YsNkJBQXdCO0FBRXhCLHNDQU1tQjtBQUNuQixrRkFBd0Q7QUFDeEQsd0VBQXVFO0FBRXZFLE1BQU0sMkJBQTJCLEdBQUc7Ozs7OztzRkFNa0QsQ0FBQztBQUV2RixNQUFNLGdCQUFnQixHQUFHLE9BQUM7S0FDdkIsTUFBTSxFQUFFO0tBQ1IsTUFBTSxDQUNMLEdBQUcsQ0FBQyxFQUFFO0lBQ0osSUFBSSxDQUFDO1FBQ0gsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUVoQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7QUFDSCxDQUFDLEVBQ0QsRUFBRSxPQUFPLEVBQUUsNkJBQTZCLEVBQUUsQ0FDM0M7S0FDQSxRQUFRLENBQUMscUNBQXFDLENBQUMsQ0FBQztBQUVuRCxTQUFTLDBCQUEwQixDQUFDLElBQVksRUFBRSxVQUFxQjtJQUNyRSxRQUFRLElBQUksRUFBRSxDQUFDO1FBQ2IsS0FBSyxTQUFTO1lBQ1osT0FBTyxPQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUN4QixJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVE7b0JBQUUsT0FBTyxHQUFHLENBQUM7Z0JBQ3hDLElBQUksR0FBRyxLQUFLLE1BQU07b0JBQUUsT0FBTyxJQUFJLENBQUM7Z0JBQ2hDLElBQUksR0FBRyxLQUFLLE9BQU87b0JBQUUsT0FBTyxLQUFLLENBQUM7Z0JBRWxDLE9BQU8sR0FBRyxDQUFDO1lBQ2IsQ0FBQyxFQUFFLE9BQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ2xCLEtBQUssTUFBTTtZQUNULE9BQU8sT0FBQyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsOENBQThDLENBQUMsQ0FBQztRQUNuRixLQUFLLFVBQVU7WUFDYixPQUFPLE9BQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxDQUFDLGdDQUFnQyxDQUFDLENBQUM7UUFDakUsS0FBSyxRQUFRO1lBQ1gsT0FBTyxPQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzNCLEtBQUssTUFBTTtZQUNULElBQUksVUFBVSxJQUFJLFVBQVUsQ0FBQyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3pDLE9BQU8sT0FBQyxDQUFDLElBQUksQ0FBQyxVQUEyQyxDQUFDLENBQUM7WUFDN0QsQ0FBQztZQUVELElBQUksVUFBVSxFQUFFLE1BQU0sS0FBSyxDQUFDO2dCQUFFLE9BQU8sT0FBQyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUU5RCxPQUFPLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNwQixLQUFLLE1BQU07WUFDVCxPQUFPLGdCQUFnQixDQUFDO1FBQzFCLEtBQUssT0FBTztZQUNWLE9BQU8sT0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFDekUsNERBQTREO1FBQzVEO1lBQ0UsT0FBTyxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDdEIsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLHNCQUFzQixDQUFDLEtBQWtCLEVBQUUsY0FBc0I7SUFDeEUsTUFBTSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFFbkMsaUdBQWlHO0lBQ2pHLDZGQUE2RjtJQUM3RixJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUNqQixNQUFNLElBQUksOEJBQXFCLENBQUMsS0FBSyxDQUFDLFdBQVcsRUFBRSxjQUFjLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRUQsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDeEIsMkRBQTJEO1FBQzNELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFBRSxPQUFPLE9BQUMsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUU3RCxPQUFPLE9BQUMsQ0FBQyxLQUFLLENBQUMsMEJBQTBCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBVyxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVELElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUM5QyxPQUFPLGdCQUFnQixDQUFDO0lBQzFCLENBQUM7SUFFRCxPQUFPLDBCQUEwQixDQUFDLElBQWMsRUFBRSxVQUFVLENBQUMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsd0ZBQXdGO0FBQ3hGLDhGQUE4RjtBQUM5RixvRkFBb0Y7QUFDcEYsU0FBUyxnQkFBZ0IsQ0FDdkIsV0FBb0MsRUFDcEMsS0FBYyxFQUNkLGNBQXNCO0lBRXRCLGlHQUFpRztJQUNqRyxJQUFJLENBQUMsV0FBVyxJQUFJLFdBQVcsQ0FBQyxjQUFjLElBQUksS0FBSyxLQUFLLElBQUk7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUUvRSxNQUFNLE1BQU0sR0FBRyxzQkFBc0IsQ0FBQyxXQUFXLEVBQUUsY0FBYyxDQUFDLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRXBGLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDcEIsTUFBTSxJQUFJLHVCQUFjLENBQ3RCLDRCQUE0QixXQUFXLENBQUMsV0FBVyxNQUFNLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTTthQUN6RSxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDO2FBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUNoQixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQztBQUNyQixDQUFDO0FBTUQsTUFBcUIsd0JBQXlCLFNBQVEsOEJBQThDO0lBQy9FLEtBQUssQ0FBQyxnQkFBZ0I7UUFDdkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQzlDLGVBQWUsQ0FDaEIsQ0FBQztRQUVGLElBQUksUUFBUSxFQUFFLGdCQUFnQixLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQzFDLE9BQU8sSUFBSSxDQUFDLGtCQUFrQixDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7UUFDeEQsQ0FBQztRQUVELElBQUksUUFBUSxFQUFFLGdCQUFnQixLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQy9DLE1BQU0sSUFBSSx1QkFBYyxDQUFDLGlFQUFpRSxDQUFDLENBQUM7UUFDOUYsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVTLEtBQUssQ0FBQyxTQUFTO1FBQ3ZCLGlFQUFpRTtRQUNqRSxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyx5QkFBeUIsQ0FDbEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FDakMsQ0FBQztRQUVGLElBQUksT0FBTyxFQUFFLENBQUM7WUFDWixPQUFPLElBQUksQ0FBQyxzQkFBc0IsQ0FBZ0MsT0FBTyxFQUFFLEtBQUssRUFBQyxJQUFJLEVBQUMsRUFBRTtnQkFDdEYsTUFBTSxFQUFFLGlCQUFpQixFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxHQUFHLElBQUksQ0FBQztnQkFDbEUscUZBQXFGO2dCQUNyRixpRUFBaUU7Z0JBQ2pFLE1BQU0sUUFBUSxHQUNaLGdCQUFnQixFQUFFLEtBQUssS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsV0FBWSxDQUFDLEtBQUssQ0FBQztnQkFFdEYsTUFBTSxNQUFNLEdBQWlCO29CQUMzQixpQkFBaUI7b0JBQ2pCLEdBQUcsV0FBWTtvQkFDZixzRkFBc0Y7b0JBQ3RGLHFGQUFxRjtvQkFDckYsS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsUUFBUSxDQUFDO2lCQUMzRSxDQUFDO2dCQUVGLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztZQUM3QyxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCwrQkFBK0I7UUFDL0IsT0FBTyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDaEMsQ0FBQztJQUVPLEtBQUssQ0FBQyxjQUFjLENBQzFCLGlCQUE0QixFQUM1QixXQUF1QyxFQUN2QyxLQUFjO1FBRWQsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsaUJBQWlCLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEYsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFN0UsT0FBTyxnQkFBZ0IsQ0FBQyxXQUFXLEVBQUUsS0FBSyxFQUFFLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQ2hGLENBQUM7SUFFTyxLQUFLLENBQUMsZUFBZTtRQUMzQixNQUFNLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDOUMsTUFBTSxFQUFFLGVBQWUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUNqQyxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1FBRXBELE1BQU0saUJBQWlCLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQ25ELE9BQU8sRUFDUCxJQUFJLENBQUMsTUFBTSxFQUNYLGVBQWUsRUFBRSx1QkFBdUIsQ0FDekMsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBRWhGLElBQUksZUFBZSxFQUFFLFNBQVMsS0FBSyxTQUFTLElBQUksZUFBZSxFQUFFLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNyRixNQUFNLElBQUksb0NBQTJCLENBQ25DLDJEQUEyRCxDQUM1RCxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksZUFBZSxFQUFFLEtBQUssS0FBSyxTQUFTLElBQUksZUFBZSxFQUFFLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNyRixNQUFNLElBQUksb0NBQTJCLENBQ25DLDJEQUEyRCxDQUM1RCxDQUFDO1FBQ0osQ0FBQztRQUVELE1BQU0sYUFBYSxHQUFHLGVBQWUsRUFBRSxTQUFTLENBQUM7UUFDakQsTUFBTSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsR0FDeEIsYUFBYSxLQUFLLFNBQVM7WUFDekIsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRTtZQUM3RCxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRS9ELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE1BQU0sSUFBSSwyQkFBa0IsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ2pFLENBQUM7UUFFRCxNQUFNLE1BQU0sR0FBaUI7WUFDM0IsaUJBQWlCO1lBQ2pCLFdBQVcsRUFBRSxLQUFLLENBQUMsV0FBVztZQUM5QixJQUFJLEVBQUUsS0FBSyxDQUFDLFNBQVM7WUFDckIsS0FBSztTQUNOLENBQUM7UUFFRix3Q0FBd0M7UUFDeEMsSUFBSSxJQUFJLENBQUMsYUFBYSxLQUFLLG1DQUFpQixDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQzVELE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZDLENBQUM7UUFFRCxvQ0FBb0M7UUFDcEMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRTtZQUNoRSxJQUFJLEVBQUUsZUFBZTtZQUNyQixTQUFTLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTO1lBQ2pDLFdBQVcsRUFBRTtnQkFDWCxXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVc7Z0JBQy9CLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtnQkFDakIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLO2FBQ3BCO1lBQ0QsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLGlCQUFpQjtTQUM1QyxDQUFDLENBQUM7UUFFSCxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUVELG9HQUFvRztJQUM1RixLQUFLLENBQUMsZ0JBQWdCLENBQzVCLE1BQW9CLEVBQ3BCLGlCQUFpRDtRQUVqRCxNQUFNLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxNQUFNLENBQUM7UUFFL0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQ25EO1lBQ0UsVUFBVSxFQUFFLGlCQUFpQixDQUFDLGNBQWM7WUFDNUMsRUFBRSxFQUFFLGlCQUFpQixDQUFDLFFBQVE7WUFDOUIsTUFBTSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUU7U0FDMUIsRUFDRDtZQUNFLFVBQVUsRUFBRSxHQUFHLEVBQUUsQ0FDZixJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRTtnQkFDMUQsR0FBRyxpQkFBaUI7Z0JBQ3BCLElBQUksRUFBRSxlQUFlO2dCQUNyQixTQUFTLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTO2dCQUNqQyxpQkFBaUI7Z0JBQ2pCLGdCQUFnQixFQUFFLFdBQVc7YUFDOUIsQ0FBQztTQUNMLENBQ0YsQ0FBQztRQUVGLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUU7WUFDaEUsR0FBRyxpQkFBaUI7WUFDcEIsSUFBSSxFQUFFLGVBQWU7WUFDckIsU0FBUyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUztZQUNqQyxlQUFlLEVBQUUsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRTtZQUM3QyxlQUFlLEVBQUUsRUFBRSxhQUFhLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRTtZQUNsRCxpQkFBaUI7WUFDakIsZ0JBQWdCLEVBQUUsTUFBTTtTQUN6QixDQUFDLENBQUM7UUFFSCxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFFTyxLQUFLLENBQUMsbUJBQW1CLENBQy9CLE1BQXdCLEVBQ3hCLE1BQTBCO1FBRTFCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMvQyxNQUFNLFFBQVEsR0FBRztZQUNmLElBQUksQ0FBQyxtQkFBbUIsRUFBRTtZQUMxQixHQUFHLENBQUMsTUFBTSxJQUFJLENBQUMsMEJBQTBCLEVBQUUsQ0FBQztZQUM1QyxJQUFJLHdCQUFhLENBQUMsMkJBQTJCLENBQUM7WUFDOUMsSUFBSSx3QkFBYSxDQUNmLHVDQUF1QyxNQUFNLENBQUMscUJBQXFCLGVBQWUsQ0FDbkY7WUFDRCxJQUFJLHVCQUFZLENBQUMsZ0JBQWdCLE1BQU0sSUFBSSw0QkFBNEIsRUFBRSxDQUFDO1NBQzNFLENBQUM7UUFFRixNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUV4QyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFbkIsT0FBTztZQUNMLFNBQVMsRUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxTQUFTLENBQUM7WUFDM0QsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO1NBQ25CLENBQUM7SUFDSixDQUFDO0lBRU8sb0JBQW9CLENBQUMsTUFBd0I7UUFDbkQsNEZBQTRGO1FBQzVGLDhGQUE4RjtRQUM5Riw4Q0FBOEM7UUFDOUMsTUFBTSxpQkFBaUIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLGNBQWMsSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxDQUFDO1FBRXpGLElBQUksaUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ25DLE1BQU0sSUFBSSw4QkFBcUIsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDekQsQ0FBQztRQVFELE1BQU0sWUFBWSxHQUFHLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUM3QyxPQUFDLENBQUMsTUFBTSxDQUFDO1lBQ1AsU0FBUyxFQUFFLE9BQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQztZQUNuQyxLQUFLLEVBQUUsc0JBQXNCLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxRQUFRLEVBQUU7WUFDbEUsU0FBUyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsc0NBQXNDLENBQUM7U0FDdkUsQ0FBQyxDQUNjLENBQUM7UUFFbkIsTUFBTSxXQUFXLEdBQ2YsWUFBWSxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQ3ZCLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO1lBQ2pCLENBQUMsQ0FBQyxPQUFDLENBQUMsS0FBSyxDQUFDLFlBQTRELENBQUMsQ0FBQztRQUU1RSxPQUFPLElBQUksZ0NBQXFCLENBQUM7WUFDL0IsSUFBSSxFQUFFLHFCQUFxQjtZQUMzQixXQUFXLEVBQUUsd0NBQXdDO1lBQ3JELE1BQU0sRUFBRSxPQUFDLENBQUMsTUFBTSxDQUFDLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxDQUFDO1lBQ3hDLElBQUksRUFBRSxTQUFTO1NBQ2hCLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQTNORCwyQ0EyTkMifQ==
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Logger } from '../ports/logger-port';
|
|
2
|
+
import type { WorkflowPort } from '../ports/workflow-port';
|
|
3
|
+
import type Runner from '../runner';
|
|
4
|
+
import http from 'http';
|
|
5
|
+
export interface ExecutorHttpServerOptions {
|
|
6
|
+
port: number;
|
|
7
|
+
runner: Runner;
|
|
8
|
+
authSecret: string;
|
|
9
|
+
workflowPort: WorkflowPort;
|
|
10
|
+
logger?: Logger;
|
|
11
|
+
}
|
|
12
|
+
export default class ExecutorHttpServer {
|
|
13
|
+
private readonly app;
|
|
14
|
+
private readonly options;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private server;
|
|
17
|
+
constructor(options: ExecutorHttpServerOptions);
|
|
18
|
+
start(): Promise<void>;
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
get callback(): (req: http.IncomingMessage | import("http2").Http2ServerRequest, res: http.ServerResponse | import("http2").Http2ServerResponse) => Promise<void>;
|
|
21
|
+
private hasRunAccessMiddleware;
|
|
22
|
+
private handleGetRun;
|
|
23
|
+
private handleTrigger;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=executor-http-server.d.ts.map
|