@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,3 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLXBvcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydHMvbG9nZ2VyLXBvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Logger } from './logger-port';
|
|
2
|
+
import type { StepExecutionData } from '../types/step-execution-data';
|
|
3
|
+
export interface RunStore {
|
|
4
|
+
init(logger?: Logger): Promise<void>;
|
|
5
|
+
close(logger?: Logger): Promise<void>;
|
|
6
|
+
getStepExecutions(runId: string): Promise<StepExecutionData[]>;
|
|
7
|
+
saveStepExecution(runId: string, stepExecution: StepExecutionData): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=run-store.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVuLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BvcnRzL3J1bi1zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AvailableStepExecution, StepUser } from '../types/execution-context';
|
|
2
|
+
import type { CollectionSchema } from '../types/validated/collection';
|
|
3
|
+
import type { StepOutcome } from '../types/validated/step-outcome';
|
|
4
|
+
import type { ToolConfig } from '@forestadmin/ai-proxy';
|
|
5
|
+
export interface MalformedRunInfo {
|
|
6
|
+
runId: string;
|
|
7
|
+
stepId: string | null;
|
|
8
|
+
stepIndex: number | null;
|
|
9
|
+
userMessage: string;
|
|
10
|
+
technicalMessage: string;
|
|
11
|
+
}
|
|
12
|
+
export interface AvailableRunDispatch {
|
|
13
|
+
step: AvailableStepExecution;
|
|
14
|
+
auth: {
|
|
15
|
+
forestServerToken: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface AvailableRunsBatch {
|
|
19
|
+
pending: AvailableRunDispatch[];
|
|
20
|
+
malformed: MalformedRunInfo[];
|
|
21
|
+
}
|
|
22
|
+
export interface WorkflowPort {
|
|
23
|
+
getAvailableRuns(): Promise<AvailableRunsBatch>;
|
|
24
|
+
getAvailableRun(runId: string): Promise<AvailableRunDispatch | null>;
|
|
25
|
+
updateStepExecution(runId: string, stepOutcome: StepOutcome): Promise<AvailableRunDispatch | null>;
|
|
26
|
+
getCollectionSchema(collectionName: string, runId: string): Promise<CollectionSchema>;
|
|
27
|
+
getMcpServerConfigs(): Promise<Record<string, ToolConfig>>;
|
|
28
|
+
hasRunAccess(runId: string, user: StepUser): Promise<boolean>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=workflow-port.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2Zsb3ctcG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wb3J0cy93b3JrZmxvdy1wb3J0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AiModelPort } from './ports/ai-model-port';
|
|
2
|
+
import type { Logger } from './ports/logger-port';
|
|
3
|
+
import type { WorkflowPort } from './ports/workflow-port';
|
|
4
|
+
import type { RemoteTool, ToolConfig } from '@forestadmin/ai-proxy';
|
|
5
|
+
export declare function scopeConfigsToServer(configs: Record<string, ToolConfig>, mcpServerId: string): Record<string, ToolConfig>;
|
|
6
|
+
export interface FetchRemoteToolsResult {
|
|
7
|
+
tools: RemoteTool[];
|
|
8
|
+
mcpServerName?: string;
|
|
9
|
+
}
|
|
10
|
+
export default class RemoteToolFetcher {
|
|
11
|
+
private readonly workflowPort;
|
|
12
|
+
private readonly aiModelPort;
|
|
13
|
+
private readonly logger;
|
|
14
|
+
constructor(workflowPort: WorkflowPort, aiModelPort: AiModelPort, logger: Logger);
|
|
15
|
+
fetch(mcpServerId: string): Promise<FetchRemoteToolsResult>;
|
|
16
|
+
private warnMissingTargetServer;
|
|
17
|
+
private errorOnPartialLoadFailure;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=remote-tool-fetcher.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scopeConfigsToServer = scopeConfigsToServer;
|
|
4
|
+
// Match by config.id, not by Record key: server names can collide across configs.
|
|
5
|
+
function scopeConfigsToServer(configs, mcpServerId) {
|
|
6
|
+
return Object.fromEntries(Object.entries(configs).filter(([, cfg]) => cfg.id === mcpServerId));
|
|
7
|
+
}
|
|
8
|
+
class RemoteToolFetcher {
|
|
9
|
+
constructor(workflowPort, aiModelPort, logger) {
|
|
10
|
+
this.workflowPort = workflowPort;
|
|
11
|
+
this.aiModelPort = aiModelPort;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
}
|
|
14
|
+
async fetch(mcpServerId) {
|
|
15
|
+
const configs = await this.workflowPort.getMcpServerConfigs();
|
|
16
|
+
const scoped = scopeConfigsToServer(configs, mcpServerId);
|
|
17
|
+
const [mcpServerName] = Object.keys(scoped);
|
|
18
|
+
this.warnMissingTargetServer(configs, scoped, mcpServerId, mcpServerName);
|
|
19
|
+
if (Object.keys(scoped).length === 0)
|
|
20
|
+
return { tools: [], mcpServerName };
|
|
21
|
+
const tools = await this.aiModelPort.loadRemoteTools(scoped);
|
|
22
|
+
this.errorOnPartialLoadFailure(scoped, tools, mcpServerId, mcpServerName);
|
|
23
|
+
return { tools, mcpServerName };
|
|
24
|
+
}
|
|
25
|
+
// Distinguish "no configs at all" (deployment misconfig) from "configs exist but none match"
|
|
26
|
+
// (orchestrator/executor drift on server id) — both yield zero tools, but ops need to know
|
|
27
|
+
// which one to fix.
|
|
28
|
+
warnMissingTargetServer(configs, scoped, mcpServerId, mcpServerName) {
|
|
29
|
+
if (Object.keys(scoped).length > 0)
|
|
30
|
+
return;
|
|
31
|
+
const availableMcpServerIds = Object.values(configs)
|
|
32
|
+
.map(cfg => cfg.id)
|
|
33
|
+
.filter((id) => Boolean(id));
|
|
34
|
+
this.logger.warn(Object.keys(configs).length === 0
|
|
35
|
+
? 'MCP step targets a server but orchestrator returned no MCP configs'
|
|
36
|
+
: 'MCP step targets a server not advertised by the orchestrator', { requestedMcpServerId: mcpServerId, mcpServerName, availableMcpServerIds });
|
|
37
|
+
}
|
|
38
|
+
// Partial-failure detection: McpClient swallows per-server load errors and returns whatever
|
|
39
|
+
// succeeded. Match config.id against tool.mcpServerId — both providers populate it from the
|
|
40
|
+
// orchestrator's persisted id, so the check is uniform across MCP and Forest connectors.
|
|
41
|
+
errorOnPartialLoadFailure(scoped, tools, mcpServerId, mcpServerName) {
|
|
42
|
+
const loadedMcpServerIds = new Set(tools.map(t => t.mcpServerId));
|
|
43
|
+
const failedConfigNames = Object.entries(scoped)
|
|
44
|
+
.filter(([, cfg]) => !loadedMcpServerIds.has(cfg.id))
|
|
45
|
+
.map(([name]) => name);
|
|
46
|
+
if (failedConfigNames.length === 0)
|
|
47
|
+
return;
|
|
48
|
+
this.logger.error('MCP servers failed to load tools', {
|
|
49
|
+
requestedMcpServerId: mcpServerId,
|
|
50
|
+
mcpServerName,
|
|
51
|
+
failedConfigNames,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.default = RemoteToolFetcher;
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3RlLXRvb2wtZmV0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9yZW1vdGUtdG9vbC1mZXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBTUEsb0RBS0M7QUFORCxrRkFBa0Y7QUFDbEYsU0FBZ0Isb0JBQW9CLENBQ2xDLE9BQW1DLEVBQ25DLFdBQW1CO0lBRW5CLE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBQ2pHLENBQUM7QUFPRCxNQUFxQixpQkFBaUI7SUFLcEMsWUFBWSxZQUEwQixFQUFFLFdBQXdCLEVBQUUsTUFBYztRQUM5RSxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQUssQ0FBQyxXQUFtQjtRQUM3QixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5RCxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUQsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFNUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLGFBQWEsRUFBRSxDQUFDO1FBRTFFLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLHlCQUF5QixDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVELDZGQUE2RjtJQUM3RiwyRkFBMkY7SUFDM0Ysb0JBQW9CO0lBQ1osdUJBQXVCLENBQzdCLE9BQW1DLEVBQ25DLE1BQWtDLEVBQ2xDLFdBQW1CLEVBQ25CLGFBQWlDO1FBRWpDLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQztZQUFFLE9BQU87UUFFM0MsTUFBTSxxQkFBcUIsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQzthQUNqRCxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2FBQ2xCLE1BQU0sQ0FBQyxDQUFDLEVBQUUsRUFBZ0IsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRTdDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUNkLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDL0IsQ0FBQyxDQUFDLG9FQUFvRTtZQUN0RSxDQUFDLENBQUMsOERBQThELEVBQ2xFLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxxQkFBcUIsRUFBRSxDQUM1RSxDQUFDO0lBQ0osQ0FBQztJQUVELDRGQUE0RjtJQUM1Riw0RkFBNEY7SUFDNUYseUZBQXlGO0lBQ2pGLHlCQUF5QixDQUMvQixNQUFrQyxFQUNsQyxLQUFtQixFQUNuQixXQUFtQixFQUNuQixhQUFpQztRQUVqQyxNQUFNLGtCQUFrQixHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztRQUNsRSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO2FBQzdDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQ3BELEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXpCLElBQUksaUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPO1FBRTNDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLGtDQUFrQyxFQUFFO1lBQ3BELG9CQUFvQixFQUFFLFdBQVc7WUFDakMsYUFBYTtZQUNiLGlCQUFpQjtTQUNsQixDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUF4RUQsb0NBd0VDIn0=
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ActivityLogPortFactory } from './ports/activity-log-port';
|
|
2
|
+
import type { AgentPort } from './ports/agent-port';
|
|
3
|
+
import type { AiModelPort } from './ports/ai-model-port';
|
|
4
|
+
import type { Logger } from './ports/logger-port';
|
|
5
|
+
import type { RunStore } from './ports/run-store';
|
|
6
|
+
import type { WorkflowPort } from './ports/workflow-port';
|
|
7
|
+
import type SchemaCache from './schema-cache';
|
|
8
|
+
import type { StepExecutionData } from './types/step-execution-data';
|
|
9
|
+
export type RunnerState = 'idle' | 'running' | 'draining' | 'stopped';
|
|
10
|
+
export interface RunnerConfig {
|
|
11
|
+
agentPort: AgentPort;
|
|
12
|
+
workflowPort: WorkflowPort;
|
|
13
|
+
runStore: RunStore;
|
|
14
|
+
schemaCache: SchemaCache;
|
|
15
|
+
pollingIntervalMs: number;
|
|
16
|
+
aiModelPort: AiModelPort;
|
|
17
|
+
activityLogPortFactory: ActivityLogPortFactory;
|
|
18
|
+
envSecret: string;
|
|
19
|
+
authSecret: string;
|
|
20
|
+
logger?: Logger;
|
|
21
|
+
stopTimeoutMs?: number;
|
|
22
|
+
stepTimeoutMs?: number;
|
|
23
|
+
aiInvokeTimeoutMs?: number;
|
|
24
|
+
maxChainDepth?: number;
|
|
25
|
+
}
|
|
26
|
+
export default class Runner {
|
|
27
|
+
private readonly config;
|
|
28
|
+
private pollingTimer;
|
|
29
|
+
private readonly inFlightRuns;
|
|
30
|
+
private readonly logger;
|
|
31
|
+
private readonly remoteToolFetcher;
|
|
32
|
+
private _state;
|
|
33
|
+
constructor(config: RunnerConfig);
|
|
34
|
+
get state(): RunnerState;
|
|
35
|
+
start(): Promise<void>;
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
getRunStepExecutions(runId: string): Promise<StepExecutionData[]>;
|
|
38
|
+
triggerPoll(runId: string, options?: {
|
|
39
|
+
pendingData?: unknown;
|
|
40
|
+
bearerUserId?: number;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
private assertRunNotInFlight;
|
|
43
|
+
private schedulePoll;
|
|
44
|
+
private runPollCycle;
|
|
45
|
+
private reportMalformedRun;
|
|
46
|
+
private executeStep;
|
|
47
|
+
private doExecuteStep;
|
|
48
|
+
private get contextConfig();
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=runner.d.ts.map
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
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 console_logger_1 = __importDefault(require("./adapters/console-logger"));
|
|
7
|
+
const defaults_1 = require("./defaults");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const step_executor_factory_1 = __importDefault(require("./executors/step-executor-factory"));
|
|
10
|
+
const in_flight_run_registry_1 = __importDefault(require("./in-flight-run-registry"));
|
|
11
|
+
const remote_tool_fetcher_1 = __importDefault(require("./remote-tool-fetcher"));
|
|
12
|
+
const step_outcome_1 = require("./types/validated/step-outcome");
|
|
13
|
+
const validate_secrets_1 = __importDefault(require("./validate-secrets"));
|
|
14
|
+
class Runner {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.pollingTimer = null;
|
|
17
|
+
this.inFlightRuns = new in_flight_run_registry_1.default();
|
|
18
|
+
this._state = 'idle';
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.logger = config.logger ?? new console_logger_1.default();
|
|
21
|
+
this.remoteToolFetcher = new remote_tool_fetcher_1.default(config.workflowPort, config.aiModelPort, this.logger);
|
|
22
|
+
}
|
|
23
|
+
get state() {
|
|
24
|
+
return this._state;
|
|
25
|
+
}
|
|
26
|
+
async start() {
|
|
27
|
+
if (this._state === 'stopped' || this._state === 'draining') {
|
|
28
|
+
throw new Error('Runner has been stopped and cannot be restarted');
|
|
29
|
+
}
|
|
30
|
+
if (this._state === 'running')
|
|
31
|
+
return;
|
|
32
|
+
(0, validate_secrets_1.default)({ envSecret: this.config.envSecret, authSecret: this.config.authSecret });
|
|
33
|
+
// Probe the agent first so we fail fast without opening DB connections when unreachable.
|
|
34
|
+
await this.config.agentPort.probe();
|
|
35
|
+
this.logger.info('Agent probe passed', {});
|
|
36
|
+
await this.config.runStore.init(this.logger);
|
|
37
|
+
this._state = 'running';
|
|
38
|
+
this.schedulePoll();
|
|
39
|
+
}
|
|
40
|
+
async stop() {
|
|
41
|
+
if (this._state === 'idle' || this._state === 'stopped' || this._state === 'draining')
|
|
42
|
+
return;
|
|
43
|
+
this._state = 'draining';
|
|
44
|
+
this.logger.info('Graceful shutdown initiated', { inFlightRuns: this.inFlightRuns.size });
|
|
45
|
+
if (this.pollingTimer !== null) {
|
|
46
|
+
clearTimeout(this.pollingTimer);
|
|
47
|
+
this.pollingTimer = null;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
// Drain in-flight runs (each entry may cover a whole auto-chain).
|
|
51
|
+
if (this.inFlightRuns.size > 0) {
|
|
52
|
+
this.logger.info('Draining in-flight runs', {
|
|
53
|
+
count: this.inFlightRuns.size,
|
|
54
|
+
runs: [...this.inFlightRuns.keys()],
|
|
55
|
+
});
|
|
56
|
+
const timeout = this.config.stopTimeoutMs ?? defaults_1.DEFAULT_STOP_TIMEOUT_MS;
|
|
57
|
+
let drainTimer;
|
|
58
|
+
const drainResult = await Promise.race([
|
|
59
|
+
this.inFlightRuns.drain().then(() => {
|
|
60
|
+
if (drainTimer)
|
|
61
|
+
clearTimeout(drainTimer);
|
|
62
|
+
return 'drained';
|
|
63
|
+
}),
|
|
64
|
+
new Promise(resolve => {
|
|
65
|
+
drainTimer = setTimeout(() => resolve('timeout'), timeout);
|
|
66
|
+
}),
|
|
67
|
+
]);
|
|
68
|
+
if (drainResult === 'timeout') {
|
|
69
|
+
this.logger.error('Drain timeout — runs still in flight', {
|
|
70
|
+
remainingRuns: [...this.inFlightRuns.keys()],
|
|
71
|
+
timeoutMs: timeout,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.logger.info('All in-flight runs drained', {});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Wait for fire-and-forget activity-log transitions to settle before closing resources —
|
|
79
|
+
// otherwise audit-trail rows can be left stuck in Pending.
|
|
80
|
+
await this.config.activityLogPortFactory.drain();
|
|
81
|
+
const results = await Promise.allSettled([
|
|
82
|
+
this.config.aiModelPort.closeConnections(),
|
|
83
|
+
this.config.runStore.close(this.logger),
|
|
84
|
+
]);
|
|
85
|
+
for (const result of results) {
|
|
86
|
+
if (result.status === 'rejected') {
|
|
87
|
+
this.logger.error('Resource cleanup failed during shutdown', {
|
|
88
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
this._state = 'stopped';
|
|
95
|
+
this.logger.info('Workflow executor stopped', {});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async getRunStepExecutions(runId) {
|
|
99
|
+
return this.config.runStore.getStepExecutions(runId);
|
|
100
|
+
}
|
|
101
|
+
async triggerPoll(runId, options) {
|
|
102
|
+
// Best-effort local short-circuit: skip the orchestrator round-trip when THIS instance is
|
|
103
|
+
// already running the run. It is NOT a concurrency guard — inFlightRuns is per-instance and a
|
|
104
|
+
// deployment can run several executors, so genuine duplicate-execution prevention is the
|
|
105
|
+
// orchestrator's job (it atomically claims a pending run; concurrent triggers get nothing).
|
|
106
|
+
this.assertRunNotInFlight(runId);
|
|
107
|
+
let dispatch;
|
|
108
|
+
try {
|
|
109
|
+
dispatch = await this.config.workflowPort.getAvailableRun(runId);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (err instanceof errors_1.MalformedRunError) {
|
|
113
|
+
await this.reportMalformedRun(err.info);
|
|
114
|
+
}
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
if (!dispatch)
|
|
118
|
+
throw new errors_1.RunNotFoundError(runId);
|
|
119
|
+
const { step, auth } = dispatch;
|
|
120
|
+
if (options?.bearerUserId !== undefined && step.user.id !== options.bearerUserId) {
|
|
121
|
+
throw new errors_1.UserMismatchError(runId);
|
|
122
|
+
}
|
|
123
|
+
await this.executeStep(step, auth.forestServerToken, options?.pendingData);
|
|
124
|
+
}
|
|
125
|
+
assertRunNotInFlight(runId) {
|
|
126
|
+
if (this.inFlightRuns.has(runId)) {
|
|
127
|
+
this.logger.info('Trigger ignored — run already in flight', { runId });
|
|
128
|
+
throw new errors_1.RunAlreadyInFlightError(runId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
schedulePoll() {
|
|
132
|
+
if (this._state !== 'running')
|
|
133
|
+
return;
|
|
134
|
+
this.pollingTimer = setTimeout(() => this.runPollCycle(), this.config.pollingIntervalMs);
|
|
135
|
+
}
|
|
136
|
+
async runPollCycle() {
|
|
137
|
+
try {
|
|
138
|
+
const { pending, malformed } = await this.config.workflowPort.getAvailableRuns();
|
|
139
|
+
// Each reportMalformedRun has its own try/catch, no individual failure poisons the cycle.
|
|
140
|
+
await Promise.allSettled(malformed.map(info => this.reportMalformedRun(info)));
|
|
141
|
+
const dispatchable = pending.filter(d => !this.inFlightRuns.has(d.step.runId));
|
|
142
|
+
this.logger.info('Poll cycle completed', {
|
|
143
|
+
fetched: pending.length,
|
|
144
|
+
dispatching: dispatchable.length,
|
|
145
|
+
malformed: malformed.length,
|
|
146
|
+
});
|
|
147
|
+
await Promise.allSettled(dispatchable.map(d => this.executeStep(d.step, d.auth.forestServerToken)));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
this.logger.error('Poll cycle failed', {
|
|
151
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
152
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
this.schedulePoll();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Posts an error outcome so the orchestrator marks the run failed and stops re-dispatching it.
|
|
160
|
+
// Idempotent server-side. If stepIndex is null (empty/corrupt history), log loudly and skip —
|
|
161
|
+
// ops has to clean up manually.
|
|
162
|
+
async reportMalformedRun(info) {
|
|
163
|
+
if (info.stepId === null || info.stepIndex === null) {
|
|
164
|
+
this.logger.error('Malformed run cannot be reported — no available step identified', {
|
|
165
|
+
runId: info.runId,
|
|
166
|
+
error: info.technicalMessage,
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await this.config.workflowPort.updateStepExecution(info.runId, {
|
|
172
|
+
type: 'record',
|
|
173
|
+
stepId: info.stepId,
|
|
174
|
+
stepIndex: info.stepIndex,
|
|
175
|
+
status: 'error',
|
|
176
|
+
error: info.userMessage,
|
|
177
|
+
});
|
|
178
|
+
this.logger.error('Malformed run reported as error', {
|
|
179
|
+
runId: info.runId,
|
|
180
|
+
stepIndex: info.stepIndex,
|
|
181
|
+
error: info.technicalMessage,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (reportErr) {
|
|
185
|
+
this.logger.error('Malformed run — also failed to report', {
|
|
186
|
+
runId: info.runId,
|
|
187
|
+
mappingError: info.technicalMessage,
|
|
188
|
+
reportError: (0, errors_1.extractErrorMessage)(reportErr),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
executeStep(step, forestServerToken, incomingPendingData) {
|
|
193
|
+
// The tracked promise covers the entire auto-chain for this run plus the map cleanup —
|
|
194
|
+
// register it once, clean up once. Storing per-step entries (or Promise.resolve()) would
|
|
195
|
+
// break drain: Promise.allSettled would see already-resolved entries and stop waiting while
|
|
196
|
+
// the chain is still running.
|
|
197
|
+
return this.inFlightRuns.track(step.runId, this.doExecuteStep(step, forestServerToken, incomingPendingData));
|
|
198
|
+
}
|
|
199
|
+
async doExecuteStep(step, forestServerToken, incomingPendingData) {
|
|
200
|
+
let currentStep = step;
|
|
201
|
+
let currentToken = forestServerToken;
|
|
202
|
+
let currentIncomingData = incomingPendingData;
|
|
203
|
+
let chainedCount = 0; // additional steps chained after the initial one
|
|
204
|
+
const maxDepth = this.config.maxChainDepth ?? defaults_1.DEFAULT_MAX_CHAIN_DEPTH;
|
|
205
|
+
// Sequential by design: each step's outcome drives the next dispatch; steps within one run
|
|
206
|
+
// cannot overlap. The no-await-in-loop rule doesn't apply here.
|
|
207
|
+
/* eslint-disable no-await-in-loop, no-constant-condition */
|
|
208
|
+
while (true) {
|
|
209
|
+
let result;
|
|
210
|
+
try {
|
|
211
|
+
const executor = await step_executor_factory_1.default.create(currentStep, this.contextConfig, this.config.activityLogPortFactory.forRun(currentToken), mcpServerId => this.remoteToolFetcher.fetch(mcpServerId), currentIncomingData);
|
|
212
|
+
result = await executor.execute();
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
this.logger.error('FATAL: executor contract violated — reporting synthetic error outcome', {
|
|
216
|
+
runId: currentStep.runId,
|
|
217
|
+
stepId: currentStep.stepId,
|
|
218
|
+
stepIndex: currentStep.stepIndex,
|
|
219
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
220
|
+
});
|
|
221
|
+
// Report a synthetic error outcome so the orchestrator marks the run failed and stops
|
|
222
|
+
// re-dispatching — without this, the contract-violating step loops forever.
|
|
223
|
+
const syntheticOutcome = {
|
|
224
|
+
type: (0, step_outcome_1.stepTypeToOutcomeType)(currentStep.stepDefinition.type),
|
|
225
|
+
stepId: currentStep.stepId,
|
|
226
|
+
stepIndex: currentStep.stepIndex,
|
|
227
|
+
status: 'error',
|
|
228
|
+
error: 'An unexpected error occurred.',
|
|
229
|
+
};
|
|
230
|
+
try {
|
|
231
|
+
await this.config.workflowPort.updateStepExecution(currentStep.runId, syntheticOutcome);
|
|
232
|
+
}
|
|
233
|
+
catch (reportErr) {
|
|
234
|
+
this.logger.error('FATAL: also failed to report synthetic error outcome', {
|
|
235
|
+
runId: currentStep.runId,
|
|
236
|
+
stepId: currentStep.stepId,
|
|
237
|
+
reportError: (0, errors_1.extractErrorMessage)(reportErr),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
let nextDispatch;
|
|
243
|
+
try {
|
|
244
|
+
nextDispatch = await this.config.workflowPort.updateStepExecution(currentStep.runId, result.stepOutcome);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
this.logger.error('Failed to report step outcome', {
|
|
248
|
+
runId: currentStep.runId,
|
|
249
|
+
stepId: currentStep.stepId,
|
|
250
|
+
stepIndex: currentStep.stepIndex,
|
|
251
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
252
|
+
cause: (0, errors_1.causeMessage)(error),
|
|
253
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (nextDispatch === null) {
|
|
258
|
+
this.logger.info('Chain completed — orchestrator returned no further step', {
|
|
259
|
+
runId: currentStep.runId,
|
|
260
|
+
stepIndex: currentStep.stepIndex,
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
// Progression safety: the server must advance the workflow within the same run. A cross-run
|
|
265
|
+
// dispatch would execute under the initial run's inFlightRuns key (and leak the map entry
|
|
266
|
+
// on cleanup); a non-progressing stepIndex would loop forever. Both are server bugs —
|
|
267
|
+
// exit the chain and let the next poll re-fetch authoritative state.
|
|
268
|
+
if (nextDispatch.step.runId !== currentStep.runId ||
|
|
269
|
+
nextDispatch.step.stepIndex <= currentStep.stepIndex) {
|
|
270
|
+
this.logger.error('Server returned non-progressing next step — exiting chain', {
|
|
271
|
+
runId: currentStep.runId,
|
|
272
|
+
currentStepIndex: currentStep.stepIndex,
|
|
273
|
+
returnedRunId: nextDispatch.step.runId,
|
|
274
|
+
returnedStepIndex: nextDispatch.step.stepIndex,
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Cap check BEFORE incrementing: chainedCount counts chained steps we've already executed.
|
|
279
|
+
// maxDepth=2 means "run up to 2 chained steps after the initial one" (3 total).
|
|
280
|
+
if (chainedCount >= maxDepth) {
|
|
281
|
+
this.logger.info('Chain depth cap reached — yielding to next poll', {
|
|
282
|
+
runId: currentStep.runId,
|
|
283
|
+
stepIndex: currentStep.stepIndex,
|
|
284
|
+
maxDepth,
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
// Graceful stop: finish the current step, then yield instead of chaining further.
|
|
289
|
+
if (this._state === 'draining') {
|
|
290
|
+
this.logger.info('Chain interrupted by stop() — yielding', {
|
|
291
|
+
runId: currentStep.runId,
|
|
292
|
+
stepIndex: currentStep.stepIndex,
|
|
293
|
+
});
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
chainedCount += 1;
|
|
297
|
+
currentStep = nextDispatch.step;
|
|
298
|
+
currentToken = nextDispatch.auth.forestServerToken;
|
|
299
|
+
currentIncomingData = undefined; // chained steps never carry pending data
|
|
300
|
+
}
|
|
301
|
+
/* eslint-enable no-await-in-loop, no-constant-condition */
|
|
302
|
+
}
|
|
303
|
+
get contextConfig() {
|
|
304
|
+
return {
|
|
305
|
+
aiModelPort: this.config.aiModelPort,
|
|
306
|
+
agentPort: this.config.agentPort,
|
|
307
|
+
workflowPort: this.config.workflowPort,
|
|
308
|
+
runStore: this.config.runStore,
|
|
309
|
+
schemaCache: this.config.schemaCache,
|
|
310
|
+
logger: this.logger,
|
|
311
|
+
stepTimeoutMs: this.config.stepTimeoutMs,
|
|
312
|
+
aiInvokeTimeoutMs: this.config.aiInvokeTimeoutMs,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.default = Runner;
|
|
317
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVubmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3J1bm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVlBLCtFQUFzRDtBQUN0RCx5Q0FBOEU7QUFDOUUscUNBT2tCO0FBQ2xCLDhGQUFvRTtBQUNwRSxzRkFBMkQ7QUFDM0QsZ0ZBQXNEO0FBQ3RELGlFQUF1RTtBQUN2RSwwRUFBaUQ7QUE0QmpELE1BQXFCLE1BQU07SUFRekIsWUFBWSxNQUFvQjtRQU54QixpQkFBWSxHQUEwQixJQUFJLENBQUM7UUFDbEMsaUJBQVksR0FBRyxJQUFJLGdDQUFtQixFQUFFLENBQUM7UUFHbEQsV0FBTSxHQUFnQixNQUFNLENBQUM7UUFHbkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLElBQUksd0JBQWEsRUFBRSxDQUFDO1FBQ25ELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLDZCQUFpQixDQUM1QyxNQUFNLENBQUMsWUFBWSxFQUNuQixNQUFNLENBQUMsV0FBVyxFQUNsQixJQUFJLENBQUMsTUFBTSxDQUNaLENBQUM7SUFDSixDQUFDO0lBRUQsSUFBSSxLQUFLO1FBQ1AsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSztRQUNULElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUM1RCxNQUFNLElBQUksS0FBSyxDQUFDLGlEQUFpRCxDQUFDLENBQUM7UUFDckUsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTO1lBQUUsT0FBTztRQUV0QyxJQUFBLDBCQUFlLEVBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLElBQUksQ0FB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
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CollectionSchema } from './types/validated/collection';
|
|
2
|
+
export default class SchemaCache {
|
|
3
|
+
private readonly store;
|
|
4
|
+
private readonly ttlMs;
|
|
5
|
+
private readonly now;
|
|
6
|
+
constructor(ttlMs?: number, now?: () => number);
|
|
7
|
+
get(collectionName: string): CollectionSchema | undefined;
|
|
8
|
+
set(collectionName: string, schema: CollectionSchema): void;
|
|
9
|
+
[Symbol.iterator](): IterableIterator<[string, CollectionSchema]>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=schema-cache.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const defaults_1 = require("./defaults");
|
|
4
|
+
class SchemaCache {
|
|
5
|
+
constructor(ttlMs = defaults_1.DEFAULT_SCHEMA_CACHE_TTL_MS, now = Date.now) {
|
|
6
|
+
this.store = new Map();
|
|
7
|
+
this.ttlMs = ttlMs;
|
|
8
|
+
this.now = now;
|
|
9
|
+
}
|
|
10
|
+
get(collectionName) {
|
|
11
|
+
const entry = this.store.get(collectionName);
|
|
12
|
+
if (!entry)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (this.now() - entry.fetchedAt > this.ttlMs) {
|
|
15
|
+
this.store.delete(collectionName);
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return entry.schema;
|
|
19
|
+
}
|
|
20
|
+
set(collectionName, schema) {
|
|
21
|
+
this.store.set(collectionName, { schema, fetchedAt: this.now() });
|
|
22
|
+
}
|
|
23
|
+
// Yields non-expired entries; deletes stale ones along the way.
|
|
24
|
+
*[Symbol.iterator]() {
|
|
25
|
+
const now = this.now();
|
|
26
|
+
for (const [key, entry] of this.store) {
|
|
27
|
+
if (now - entry.fetchedAt <= this.ttlMs) {
|
|
28
|
+
yield [key, entry.schema];
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
this.store.delete(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = SchemaCache;
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NjaGVtYS1jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLHlDQUF5RDtBQUV6RCxNQUFxQixXQUFXO0lBSzlCLFlBQVksUUFBZ0Isc0NBQTJCLEVBQUUsTUFBb0IsSUFBSSxDQUFDLEdBQUc7UUFKcEUsVUFBSyxHQUFHLElBQUksR0FBRyxFQUEyRCxDQUFDO1FBSzFGLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxHQUFHLENBQUMsY0FBc0I7UUFDeEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7UUFFN0MsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUU3QixJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUM5QyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUVsQyxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxHQUFHLENBQUMsY0FBc0IsRUFBRSxNQUF3QjtRQUNsRCxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUVELGdFQUFnRTtJQUNoRSxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztRQUNoQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFdkIsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUN0QyxJQUFJLEdBQUcsR0FBRyxLQUFLLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDeEMsTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDNUIsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3pCLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBeENELDhCQXdDQyJ9
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WorkflowPort } from './ports/workflow-port';
|
|
2
|
+
import type SchemaCache from './schema-cache';
|
|
3
|
+
import type { CollectionSchema } from './types/validated/collection';
|
|
4
|
+
export default class SchemaResolver {
|
|
5
|
+
private readonly cache;
|
|
6
|
+
private readonly workflowPort;
|
|
7
|
+
private readonly runId;
|
|
8
|
+
constructor(cache: SchemaCache, workflowPort: WorkflowPort, runId: string);
|
|
9
|
+
resolve(collectionName: string): Promise<CollectionSchema>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=schema-resolver.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Per-run schema resolution: binds the shared SchemaCache, the orchestrator port and the
|
|
4
|
+
// current runId once, so callers never thread a loader. Writes into the SAME SchemaCache
|
|
5
|
+
// instance AgentClientAgentPort reads from (get/iterate): the resolver always populates an
|
|
6
|
+
// entry before the agent-port reads it, so the agent-port's SchemaNotCachedError guard does
|
|
7
|
+
// not fire under normal TTLs (a TTL shorter than a single step's round-trip could still evict).
|
|
8
|
+
class SchemaResolver {
|
|
9
|
+
constructor(cache, workflowPort, runId) {
|
|
10
|
+
this.cache = cache;
|
|
11
|
+
this.workflowPort = workflowPort;
|
|
12
|
+
this.runId = runId;
|
|
13
|
+
}
|
|
14
|
+
async resolve(collectionName) {
|
|
15
|
+
const cached = this.cache.get(collectionName);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
const schema = await this.workflowPort.getCollectionSchema(collectionName, this.runId);
|
|
19
|
+
this.cache.set(collectionName, schema);
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.default = SchemaResolver;
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLXJlc29sdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NjaGVtYS1yZXNvbHZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUlBLHlGQUF5RjtBQUN6Rix5RkFBeUY7QUFDekYsMkZBQTJGO0FBQzNGLDRGQUE0RjtBQUM1RixnR0FBZ0c7QUFDaEcsTUFBcUIsY0FBYztJQUtqQyxZQUFZLEtBQWtCLEVBQUUsWUFBMEIsRUFBRSxLQUFhO1FBQ3ZFLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTyxDQUFDLGNBQXNCO1FBQ2xDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzlDLElBQUksTUFBTTtZQUFFLE9BQU8sTUFBTSxDQUFDO1FBRTFCLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3ZGLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUV2QyxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0NBQ0Y7QUFwQkQsaUNBb0JDIn0=
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RunStore } from '../ports/run-store';
|
|
2
|
+
import type { Options } from 'sequelize';
|
|
3
|
+
export declare function buildDatabaseRunStore(options: Options): Promise<RunStore>;
|
|
4
|
+
export declare function buildInMemoryRunStore(): Promise<RunStore>;
|
|
5
|
+
//# sourceMappingURL=build-run-store.d.ts.map
|