@tomflow/proflow-execution-runtime 0.1.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/README.md +5 -0
- package/conformance.json +4 -0
- package/dist/deployment/adapter.d.ts +458 -0
- package/dist/deployment/adapter.js +265 -0
- package/dist/deployment/descriptor.d.ts +105 -0
- package/dist/deployment/descriptor.js +123 -0
- package/dist/deployment/requirements.d.ts +5 -0
- package/dist/deployment/requirements.js +5 -0
- package/dist/deployment/verification.d.ts +3 -0
- package/dist/deployment/verification.js +7 -0
- package/dist/src/approval-owner.d.ts +40 -0
- package/dist/src/approval-owner.js +278 -0
- package/dist/src/browser-vision-client.d.ts +15 -0
- package/dist/src/browser-vision-client.js +79 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +50 -0
- package/dist/src/executor-port.d.ts +5 -0
- package/dist/src/executor-port.js +1 -0
- package/dist/src/executors/local-adapter.d.ts +4 -0
- package/dist/src/executors/local-adapter.js +55 -0
- package/dist/src/formal-process.d.ts +20 -0
- package/dist/src/formal-process.js +161 -0
- package/dist/src/index.d.ts +122 -0
- package/dist/src/index.js +983 -0
- package/dist/src/model-decision-client.d.ts +10 -0
- package/dist/src/model-decision-client.js +244 -0
- package/dist/src/service.d.ts +67 -0
- package/dist/src/service.js +473 -0
- package/package.json +56 -0
- package/proflow.module.json +139 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { createBrowserExecutorComposition, loadBrowserExecutorCompositionConfig, } from "@tomflow/proflow-execution-browser-extension/runtime-composition";
|
|
3
|
+
import { createExecutionBrowserVisionClient } from "./browser-vision-client.js";
|
|
4
|
+
import { createExecutionModelDecisionClient } from "./model-decision-client.js";
|
|
5
|
+
import { createExecutionRuntimeProcess, } from "./service.js";
|
|
6
|
+
async function readSecret(path, name) {
|
|
7
|
+
const info = await stat(path);
|
|
8
|
+
if (process.platform !== "win32" && (info.mode & 0o077) !== 0)
|
|
9
|
+
throw new TypeError(`${name} permissions must be owner-only`);
|
|
10
|
+
const value = (await readFile(path, "utf8")).trim();
|
|
11
|
+
if (value.length < 32)
|
|
12
|
+
throw new TypeError(`${name} must contain at least 32 characters`);
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
async function createIdentityClient(config) {
|
|
16
|
+
const token = await readSecret(config.tokenFile, "execution identity token");
|
|
17
|
+
let ready = false;
|
|
18
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
19
|
+
try {
|
|
20
|
+
ready = (await fetch(`${config.endpoint}/internal/execution/identity/ready`, {
|
|
21
|
+
headers,
|
|
22
|
+
signal: AbortSignal.timeout(2_000),
|
|
23
|
+
})).ok;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
ready = false;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
port: {
|
|
30
|
+
async authorize(request) {
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(`${config.endpoint}/internal/execution/authorize`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
35
|
+
body: JSON.stringify(request),
|
|
36
|
+
signal: AbortSignal.timeout(5_000),
|
|
37
|
+
});
|
|
38
|
+
ready = response.ok;
|
|
39
|
+
if (!response.ok)
|
|
40
|
+
return false;
|
|
41
|
+
const body = (await response.json());
|
|
42
|
+
return body.authorized === true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
ready = false;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
readiness: () => ready,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function assertFormalConfig(config) {
|
|
54
|
+
if (!config.identity)
|
|
55
|
+
throw new Error("formal execution-runtime requires identity configuration");
|
|
56
|
+
if (!config.transportCredentialFile)
|
|
57
|
+
throw new Error("formal execution-runtime requires transportCredentialFile");
|
|
58
|
+
if (!config.browserExecutorConfigPath)
|
|
59
|
+
throw new Error("formal execution-runtime requires browserExecutorConfigPath");
|
|
60
|
+
if (!config.modelDecision)
|
|
61
|
+
throw new Error("formal execution-runtime requires modelDecision configuration");
|
|
62
|
+
if (!config.modelDecision.credentialFile)
|
|
63
|
+
throw new Error("formal execution-runtime requires modelDecision.credentialFile");
|
|
64
|
+
return {
|
|
65
|
+
identity: config.identity,
|
|
66
|
+
transportCredentialFile: config.transportCredentialFile,
|
|
67
|
+
browserExecutorConfigPath: config.browserExecutorConfigPath,
|
|
68
|
+
modelDecision: {
|
|
69
|
+
...config.modelDecision,
|
|
70
|
+
credentialFile: config.modelDecision.credentialFile,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function createFormalExecutionRuntimeLifecycle(input) {
|
|
75
|
+
const required = assertFormalConfig(input.config);
|
|
76
|
+
let current;
|
|
77
|
+
const build = async () => {
|
|
78
|
+
const identityClient = await createIdentityClient(required.identity);
|
|
79
|
+
const transportCredential = await readSecret(required.transportCredentialFile, "execution transport credential");
|
|
80
|
+
const modelDecisionCredential = await readSecret(required.modelDecision.credentialFile, "model decision transport credential");
|
|
81
|
+
const modelDecisionClient = createExecutionModelDecisionClient({
|
|
82
|
+
endpoint: required.modelDecision.endpoint,
|
|
83
|
+
...(required.modelDecision.timeoutMs === undefined
|
|
84
|
+
? {}
|
|
85
|
+
: { timeoutMs: required.modelDecision.timeoutMs }),
|
|
86
|
+
credential: modelDecisionCredential,
|
|
87
|
+
});
|
|
88
|
+
await modelDecisionClient.probe();
|
|
89
|
+
const browserVisionClient = createExecutionBrowserVisionClient({
|
|
90
|
+
endpoint: required.modelDecision.endpoint,
|
|
91
|
+
...(required.modelDecision.timeoutMs === undefined
|
|
92
|
+
? {}
|
|
93
|
+
: { timeoutMs: required.modelDecision.timeoutMs }),
|
|
94
|
+
credential: modelDecisionCredential,
|
|
95
|
+
});
|
|
96
|
+
const browserComposition = await createBrowserExecutorComposition({
|
|
97
|
+
...(await loadBrowserExecutorCompositionConfig(required.browserExecutorConfigPath)),
|
|
98
|
+
vision: browserVisionClient.port,
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
const service = await createExecutionRuntimeProcess({
|
|
102
|
+
config: input.config,
|
|
103
|
+
identity: identityClient.port,
|
|
104
|
+
identityReadiness: identityClient.readiness,
|
|
105
|
+
transportCredential,
|
|
106
|
+
requireModelDecision: true,
|
|
107
|
+
modelDecision: modelDecisionClient.port,
|
|
108
|
+
modelDecisionReadiness: modelDecisionClient.readiness,
|
|
109
|
+
browserExecutor: browserComposition.browserExecutor,
|
|
110
|
+
browserReadiness: () => browserComposition.bridgeStatus().online,
|
|
111
|
+
carrierSummary: () => browserComposition.bridgeStatus(),
|
|
112
|
+
...(input.log === undefined ? {} : { log: input.log }),
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
service,
|
|
116
|
+
closeBrowser: () => browserComposition.close(),
|
|
117
|
+
browserBridgeEndpoint: browserComposition.bridgeEndpoint,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
await browserComposition.close();
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return Object.freeze({
|
|
126
|
+
status: () => current?.service.status() ?? {
|
|
127
|
+
readiness: "NOT_READY",
|
|
128
|
+
},
|
|
129
|
+
async start() {
|
|
130
|
+
if (current !== undefined)
|
|
131
|
+
throw new Error("formal execution-runtime lifecycle is already started");
|
|
132
|
+
current = await build();
|
|
133
|
+
try {
|
|
134
|
+
const address = await current.service.start();
|
|
135
|
+
return {
|
|
136
|
+
...address,
|
|
137
|
+
browserBridgeEndpoint: current.browserBridgeEndpoint,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
await current.closeBrowser();
|
|
142
|
+
current = undefined;
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
async stop() {
|
|
147
|
+
if (current === undefined)
|
|
148
|
+
return;
|
|
149
|
+
const closing = current;
|
|
150
|
+
current = undefined;
|
|
151
|
+
await Promise.allSettled([
|
|
152
|
+
closing.service.stop(),
|
|
153
|
+
closing.closeBrowser(),
|
|
154
|
+
]);
|
|
155
|
+
},
|
|
156
|
+
async restart() {
|
|
157
|
+
await this.stop();
|
|
158
|
+
return this.start();
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { type ExecuteCapabilityRequest, type ExecutionArtifactRecord, type ExecutionErrorCode, type ExecutionRecord, type ReadExecutionOutputResponse } from "@tomflow/proflow-execution-contracts";
|
|
2
|
+
import type { ExecutionExecutorPort, ExecutorPrecondition } from "./executor-port.ts";
|
|
3
|
+
export declare const moduleRef: "execution-runtime";
|
|
4
|
+
type MaybePromise<Value> = Value | Promise<Value>;
|
|
5
|
+
type AdmissionDecision = {
|
|
6
|
+
decision: "ALLOW" | "DENY" | "REVIEW";
|
|
7
|
+
decisionPath: "deterministic" | "fast" | "reason";
|
|
8
|
+
approvalRequired?: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
};
|
|
11
|
+
export interface ExecutionPolicyPort {
|
|
12
|
+
decide(request: ExecuteCapabilityRequest): MaybePromise<AdmissionDecision>;
|
|
13
|
+
}
|
|
14
|
+
export interface ExecutionModelDecisionPort {
|
|
15
|
+
decide(request: ExecuteCapabilityRequest, context?: {
|
|
16
|
+
executionRef: string;
|
|
17
|
+
inputFingerprint: string;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
decision: "ALLOW" | "DENY";
|
|
20
|
+
decisionPath: "fast" | "reason";
|
|
21
|
+
approvalRequired?: boolean;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface ExecutionApprovalPort {
|
|
26
|
+
validate(input: {
|
|
27
|
+
approvalRef: string;
|
|
28
|
+
executionRef: string;
|
|
29
|
+
callerRef: string;
|
|
30
|
+
taskId?: string;
|
|
31
|
+
capability: string;
|
|
32
|
+
inputFingerprint: string;
|
|
33
|
+
request: ExecuteCapabilityRequest;
|
|
34
|
+
projectRoot?: string;
|
|
35
|
+
precondition?: ExecutorPrecondition;
|
|
36
|
+
now: string;
|
|
37
|
+
}): MaybePromise<boolean>;
|
|
38
|
+
}
|
|
39
|
+
export interface ExecutionIdentityPort {
|
|
40
|
+
authorize(request: ExecuteCapabilityRequest): MaybePromise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
export type { ExecutionExecutorPort, ExecutorAdmission, ExecutorArtifact, ExecutorArtifactRead, ExecutorDecisionPath, ExecutorInvocation, ExecutorPrecondition, ExecutorReconciliation, ExecutorResult, } from "./executor-port.ts";
|
|
43
|
+
export interface ExecutionRuntimeOptions {
|
|
44
|
+
databasePath: string;
|
|
45
|
+
localExecutor: ExecutionExecutorPort;
|
|
46
|
+
browserExecutor?: ExecutionExecutorPort;
|
|
47
|
+
policy?: ExecutionPolicyPort;
|
|
48
|
+
modelDecision?: ExecutionModelDecisionPort;
|
|
49
|
+
approval?: ExecutionApprovalPort;
|
|
50
|
+
approvalDraftTtlMs?: number;
|
|
51
|
+
identity?: ExecutionIdentityPort;
|
|
52
|
+
maxConcurrent?: number;
|
|
53
|
+
maxQueued?: number;
|
|
54
|
+
now?: () => Date;
|
|
55
|
+
idFactory?: () => string;
|
|
56
|
+
}
|
|
57
|
+
export declare class ExecutionRuntimeError extends Error {
|
|
58
|
+
readonly code: ExecutionErrorCode;
|
|
59
|
+
readonly retryable: boolean;
|
|
60
|
+
constructor(code: ExecutionErrorCode, message: string, retryable?: boolean);
|
|
61
|
+
}
|
|
62
|
+
export declare function executionInputFingerprint(input: unknown): string;
|
|
63
|
+
export declare function createExecutionRuntime(options: ExecutionRuntimeOptions): Promise<Readonly<{
|
|
64
|
+
executeCapability: (input: unknown) => Promise<ExecutionRecord>;
|
|
65
|
+
lookupExecutionIntent: (input: unknown) => ExecutionRecord;
|
|
66
|
+
listExecutionObserverSignals: (limit?: number) => {
|
|
67
|
+
signalRef: string;
|
|
68
|
+
kind: "RECOVERY_RESUME" | "UNKNOWN_REALITY";
|
|
69
|
+
executionRef: string;
|
|
70
|
+
taskId: string;
|
|
71
|
+
nodeId: string;
|
|
72
|
+
runNo?: number;
|
|
73
|
+
roleRef?: string;
|
|
74
|
+
workerRef: string;
|
|
75
|
+
status: ExecutionRecord["status"];
|
|
76
|
+
sideEffectState: ExecutionRecord["sideEffectState"];
|
|
77
|
+
errorCode?: string;
|
|
78
|
+
createdAt: string;
|
|
79
|
+
}[];
|
|
80
|
+
acknowledgeExecutionObserverSignal: (signalRef: string) => {
|
|
81
|
+
acknowledged: boolean;
|
|
82
|
+
};
|
|
83
|
+
getExecution: (input: unknown) => ExecutionRecord;
|
|
84
|
+
getExecutionForCaller: (input: {
|
|
85
|
+
executionRef: unknown;
|
|
86
|
+
callerRef: string;
|
|
87
|
+
}) => ExecutionRecord;
|
|
88
|
+
readExecutionOutput: (input: unknown) => Promise<ReadExecutionOutputResponse>;
|
|
89
|
+
readExecutionOutputForCaller: (input: unknown, callerRef: string) => Promise<ReadExecutionOutputResponse>;
|
|
90
|
+
cancelExecution: (input: unknown) => Promise<ExecutionRecord>;
|
|
91
|
+
requestExecutionApproval: (value: unknown) => import("@tomflow/proflow-execution-contracts").ExecutionApprovalRecord;
|
|
92
|
+
decideExecutionApproval: (value: unknown) => import("@tomflow/proflow-execution-contracts").ExecutionApprovalRecord;
|
|
93
|
+
revokeExecutionApproval: (value: unknown) => import("@tomflow/proflow-execution-contracts").ExecutionApprovalRecord;
|
|
94
|
+
getExecutionApproval: (approvalRef: string) => import("@tomflow/proflow-execution-contracts").ExecutionApprovalRecord;
|
|
95
|
+
listExecutionApprovals: (filter?: {
|
|
96
|
+
executionRef?: string;
|
|
97
|
+
status?: string;
|
|
98
|
+
}) => import("@tomflow/proflow-execution-contracts").ExecutionApprovalRecord[];
|
|
99
|
+
registerArtifact: (input: {
|
|
100
|
+
record: unknown;
|
|
101
|
+
path: string;
|
|
102
|
+
}) => ExecutionArtifactRecord;
|
|
103
|
+
getArtifactRecord: (input: {
|
|
104
|
+
artifactRef: string;
|
|
105
|
+
callerRef: string;
|
|
106
|
+
taskId?: string;
|
|
107
|
+
nodeId?: string;
|
|
108
|
+
roleRef?: string;
|
|
109
|
+
workerRef?: string;
|
|
110
|
+
}) => ExecutionArtifactRecord;
|
|
111
|
+
getArtifactSummary: () => {
|
|
112
|
+
totalArtifacts: number;
|
|
113
|
+
byKind: Record<string, number>;
|
|
114
|
+
latestCreatedAt: string | null;
|
|
115
|
+
pendingObserverSignals: number;
|
|
116
|
+
};
|
|
117
|
+
recoverIncomplete: () => Promise<void>;
|
|
118
|
+
databasePath: string;
|
|
119
|
+
logPath: string;
|
|
120
|
+
close(): void;
|
|
121
|
+
}>>;
|
|
122
|
+
export { createExecutionApprovalOwner, ExecutionApprovalOwnerError, } from "./approval-owner.ts";
|