@actiondock/core 2.0.9 → 2.0.11-beta.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/package.json +2 -2
- package/src/build/index.ts +0 -1
- package/src/build/templates.ts +1 -1
- package/src/catalog/types.ts +1 -9
- package/src/doctor/doctor.ts +99 -38
- package/src/execution/service.ts +47 -7
- package/src/export/index.ts +0 -1
- package/src/export/templates.ts +348 -59
- package/src/index.ts +1 -0
- package/src/project/init.ts +3 -2
- package/src/project/loader.ts +46 -7
- package/src/project/manifest.ts +205 -2
- package/src/project/types.ts +49 -0
- package/src/registry/registry.ts +271 -34
- package/src/runtime/context.ts +8 -3
- package/src/runtime/index.ts +1 -0
- package/src/runtime/module-loader.ts +67 -0
- package/src/runtime/process.ts +12 -11
- package/src/runtime/runner.ts +193 -68
- package/src/server/index.ts +1 -0
- package/src/server/routes/actions.ts +262 -0
- package/src/server/routes/common.ts +126 -0
- package/src/server/routes/config.ts +147 -0
- package/src/server/routes/doctor.ts +29 -0
- package/src/server/routes/health.ts +40 -0
- package/src/server/routes/index.ts +9 -0
- package/src/server/routes/info.ts +221 -0
- package/src/server/routes/playbooks.ts +109 -0
- package/src/server/routes/runs.ts +301 -0
- package/src/server/routes/state.ts +133 -0
- package/src/server/server.ts +82 -1323
- package/src/storage/index.ts +20 -4
- package/src/storage/sqlite.ts +60 -31
- package/src/storage/types.ts +8 -0
- package/src/utils/index.ts +2 -0
- package/src/build/builder.ts +0 -218
- package/src/export/skill.ts +0 -350
package/src/runtime/index.ts
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 统一源码模块加载器接口。
|
|
5
|
+
* 解耦 Action 与各类扩展模块的具体加载机制(如 ECMAScript 原生 import、tsx 动态转译加载等)。
|
|
6
|
+
*/
|
|
7
|
+
export interface ModuleLoader {
|
|
8
|
+
/**
|
|
9
|
+
* 解析模块标识符为绝对路径或完整 URL。
|
|
10
|
+
*
|
|
11
|
+
* @param specifier 模块规范说明符或物理路径
|
|
12
|
+
* @param parentPath 发起解析的父级文件或目录路径
|
|
13
|
+
*/
|
|
14
|
+
resolve?(specifier: string, parentPath?: string): string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 动态加载模块并返回命名空间全量导出对象。
|
|
18
|
+
*
|
|
19
|
+
* @param specifier 模块规范说明符或物理路径
|
|
20
|
+
* @param parentPath 发起加载的父级文件或目录路径
|
|
21
|
+
*/
|
|
22
|
+
load<T = any>(specifier: string, parentPath?: string): Promise<T>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 加载模块并解包其默认导出(default 或 action 属性)。
|
|
26
|
+
*
|
|
27
|
+
* @param specifier 模块规范说明符或物理路径
|
|
28
|
+
* @param parentPath 发起加载的父级文件或目录路径
|
|
29
|
+
*/
|
|
30
|
+
loadDefault?<T = any>(specifier: string, parentPath?: string): Promise<T>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 基于标准 ECMAScript 动态 import 的默认模块加载器。
|
|
35
|
+
*/
|
|
36
|
+
export class DefaultModuleLoader implements ModuleLoader {
|
|
37
|
+
async load<T = any>(specifier: string, _parentPath?: string): Promise<T> {
|
|
38
|
+
const importSpecifier = specifier.startsWith("file://")
|
|
39
|
+
? specifier
|
|
40
|
+
: pathToFileURL(specifier).href;
|
|
41
|
+
return (await import(importSpecifier)) as T;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async loadDefault<T = any>(specifier: string, parentPath?: string): Promise<T> {
|
|
45
|
+
const mod = await this.load<any>(specifier, parentPath);
|
|
46
|
+
return (mod?.default !== undefined ? mod.default : mod?.action !== undefined ? mod.action : mod) as T;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let globalModuleLoader: ModuleLoader | undefined;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 注册全局模块加载器实现。
|
|
54
|
+
*/
|
|
55
|
+
export function setModuleLoader(loader: ModuleLoader): void {
|
|
56
|
+
globalModuleLoader = loader;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取当前全局模块加载器,若未显式注册则回退使用 DefaultModuleLoader。
|
|
61
|
+
*/
|
|
62
|
+
export function getModuleLoader(): ModuleLoader {
|
|
63
|
+
if (!globalModuleLoader) {
|
|
64
|
+
globalModuleLoader = new DefaultModuleLoader();
|
|
65
|
+
}
|
|
66
|
+
return globalModuleLoader;
|
|
67
|
+
}
|
package/src/runtime/process.ts
CHANGED
|
@@ -200,23 +200,24 @@ export class DefaultProcessExecutor implements ProcessExecutor {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
try {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
const res: DetachedProcessResult = {
|
|
204
|
+
ok: true,
|
|
205
|
+
pid: child.pid,
|
|
206
|
+
ready: true,
|
|
207
|
+
durationMs: Date.now() - startTime,
|
|
208
|
+
};
|
|
209
|
+
const isReady = await options.probe(res as any);
|
|
207
210
|
if (isReady) {
|
|
208
|
-
return
|
|
209
|
-
ok: true,
|
|
210
|
-
pid: child.pid,
|
|
211
|
-
ready: true,
|
|
212
|
-
durationMs: Date.now() - startTime,
|
|
213
|
-
};
|
|
211
|
+
return res;
|
|
214
212
|
}
|
|
215
213
|
} catch {
|
|
216
214
|
// 探测失败继续轮询
|
|
217
215
|
}
|
|
218
216
|
|
|
219
|
-
|
|
217
|
+
const remaining = deadline - Date.now();
|
|
218
|
+
if (remaining <= 0) break;
|
|
219
|
+
const sleepTime = Math.min(probeInterval, remaining);
|
|
220
|
+
await new Promise((r) => setTimeout(r, sleepTime));
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
return {
|
package/src/runtime/runner.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import type {
|
|
3
4
|
ActionContext,
|
|
4
5
|
ActionDefinition,
|
|
6
|
+
ActionRef,
|
|
5
7
|
ExecutionResult,
|
|
6
8
|
JsonValue,
|
|
9
|
+
Logger,
|
|
7
10
|
ProcessAPI,
|
|
8
11
|
ProgressReporter,
|
|
9
12
|
RuntimeError,
|
|
10
13
|
RunRecord,
|
|
11
14
|
} from "@actiondock/sdk";
|
|
15
|
+
import { ActionResolver } from "../catalog/action-resolver";
|
|
16
|
+
import { loadActions, loadProjectConfig } from "../project/loader";
|
|
12
17
|
import type { ProjectConfig } from "../project/types";
|
|
18
|
+
import { resolveActionProject } from "../registry/registry";
|
|
13
19
|
import { validateSchema } from "../schema/validator";
|
|
14
20
|
import type { RuntimeStorage, TerminalRunStatus } from "../storage/types";
|
|
15
|
-
import {
|
|
16
|
-
import { getProcessExecutor } from "./process";
|
|
21
|
+
import { createActionContext, StderrLogger } from "./context";
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
24
|
* ActionRunner 初始化配置选项。
|
|
@@ -31,12 +36,19 @@ export interface RunnerOptions {
|
|
|
31
36
|
actions?: Map<string, ActionDefinition>;
|
|
32
37
|
/** 外部注入的进程执行器 */
|
|
33
38
|
process?: ProcessAPI;
|
|
39
|
+
/** 动态解析跨包或未注册 Action 的委托函数 */
|
|
40
|
+
actionResolver?: (
|
|
41
|
+
ref: ActionRef | string,
|
|
42
|
+
currentPackageId?: string
|
|
43
|
+
) => ActionDefinition | undefined | Promise<ActionDefinition | undefined>;
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
/**
|
|
37
47
|
* 启动 Action 执行时的可选控制参数。
|
|
38
48
|
*/
|
|
39
49
|
export interface ExecutionStartOptions {
|
|
50
|
+
/** 显式指定的运行 ID */
|
|
51
|
+
runId?: string;
|
|
40
52
|
/** 根运行 ID */
|
|
41
53
|
rootRunId?: string;
|
|
42
54
|
/** 父级运行 ID(嵌套调用场景下建立调用链树) */
|
|
@@ -57,6 +69,8 @@ export interface ExecutionStartOptions {
|
|
|
57
69
|
process?: ProcessAPI;
|
|
58
70
|
/** 外部注入的进度报告器 */
|
|
59
71
|
progress?: ProgressReporter;
|
|
72
|
+
/** 外部注入的日志记录器 */
|
|
73
|
+
logger?: Logger;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
/**
|
|
@@ -91,6 +105,10 @@ export class ActionRunner {
|
|
|
91
105
|
private projectConfig?: ProjectConfig;
|
|
92
106
|
private configOverrides: Record<string, unknown>;
|
|
93
107
|
private actions: Map<string, ActionDefinition>;
|
|
108
|
+
private actionResolver?: (
|
|
109
|
+
ref: ActionRef | string,
|
|
110
|
+
currentPackageId?: string
|
|
111
|
+
) => ActionDefinition | undefined | Promise<ActionDefinition | undefined>;
|
|
94
112
|
|
|
95
113
|
constructor(options: RunnerOptions) {
|
|
96
114
|
this.packageId = options.packageId;
|
|
@@ -98,6 +116,7 @@ export class ActionRunner {
|
|
|
98
116
|
this.projectConfig = options.projectConfig;
|
|
99
117
|
this.configOverrides = options.configOverrides || {};
|
|
100
118
|
this.actions = options.actions || new Map();
|
|
119
|
+
this.actionResolver = options.actionResolver;
|
|
101
120
|
}
|
|
102
121
|
|
|
103
122
|
/**
|
|
@@ -114,6 +133,86 @@ export class ActionRunner {
|
|
|
114
133
|
return this.actions.get(id);
|
|
115
134
|
}
|
|
116
135
|
|
|
136
|
+
/**
|
|
137
|
+
* 动态解析 Action(支持本地注册表、自定义解析器委托与已链接包目录索引检索)。
|
|
138
|
+
*
|
|
139
|
+
* @param actionOrRef Action 定义对象、引用或标识符
|
|
140
|
+
* @returns 解析出的 ActionDefinition,若未找到则返回 undefined
|
|
141
|
+
*/
|
|
142
|
+
public async resolveAction(
|
|
143
|
+
actionOrRef: ActionDefinition | ActionRef | string
|
|
144
|
+
): Promise<ActionDefinition | undefined> {
|
|
145
|
+
if (
|
|
146
|
+
typeof actionOrRef === "object" &&
|
|
147
|
+
"run" in actionOrRef &&
|
|
148
|
+
typeof (actionOrRef as any).run === "function"
|
|
149
|
+
) {
|
|
150
|
+
return actionOrRef as ActionDefinition;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const ref = actionOrRef as ActionRef | string;
|
|
154
|
+
const parsed = ActionResolver.parseRef(ref);
|
|
155
|
+
const targetActionId = parsed.actionId;
|
|
156
|
+
const targetPackageId = parsed.packageId;
|
|
157
|
+
|
|
158
|
+
// 1. 本地 actions 映射表优先检索
|
|
159
|
+
if (targetPackageId && targetPackageId !== this.packageId) {
|
|
160
|
+
if (this.actions.has(`${targetPackageId}/${targetActionId}`)) {
|
|
161
|
+
return this.actions.get(`${targetPackageId}/${targetActionId}`);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
if (this.actions.has(targetActionId)) {
|
|
165
|
+
return this.actions.get(targetActionId);
|
|
166
|
+
}
|
|
167
|
+
if (this.packageId && this.actions.has(`${this.packageId}/${targetActionId}`)) {
|
|
168
|
+
return this.actions.get(`${this.packageId}/${targetActionId}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 2. 外部注入的自定义 actionResolver 调度
|
|
173
|
+
if (this.actionResolver) {
|
|
174
|
+
const customResolved = await this.actionResolver(ref, this.packageId);
|
|
175
|
+
if (customResolved) {
|
|
176
|
+
if (targetPackageId && targetPackageId !== this.packageId) {
|
|
177
|
+
this.actions.set(`${targetPackageId}/${targetActionId}`, customResolved);
|
|
178
|
+
} else {
|
|
179
|
+
this.actions.set(targetActionId, customResolved);
|
|
180
|
+
if (this.packageId) {
|
|
181
|
+
this.actions.set(`${this.packageId}/${targetActionId}`, customResolved);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return customResolved;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 3. 基于全局链接注册表与目录索引的动态寻址与按需加载
|
|
189
|
+
try {
|
|
190
|
+
const identifier = targetPackageId
|
|
191
|
+
? `${targetPackageId}/${targetActionId}`
|
|
192
|
+
: targetActionId;
|
|
193
|
+
const resolved = await resolveActionProject(identifier);
|
|
194
|
+
if (resolved && existsSync(resolved.projectRoot)) {
|
|
195
|
+
const config = loadProjectConfig(resolved.projectRoot);
|
|
196
|
+
const actionsMap = await loadActions(resolved.projectRoot, config.actionsDir, {
|
|
197
|
+
autoInstall: false,
|
|
198
|
+
});
|
|
199
|
+
const matched = actionsMap.get(resolved.actionId);
|
|
200
|
+
if (matched) {
|
|
201
|
+
this.actions.set(`${resolved.packageId}/${resolved.actionId}`, matched);
|
|
202
|
+
// 仅当目标包就是当前项目时才注册短标识符,避免跨包动态载入污染全局短标识符
|
|
203
|
+
if (!targetPackageId || resolved.packageId === this.packageId) {
|
|
204
|
+
this.actions.set(resolved.actionId, matched);
|
|
205
|
+
}
|
|
206
|
+
return matched;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// 忽略寻址异常并返回 undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
117
216
|
/**
|
|
118
217
|
* 获取当前 Runner 已注册的所有 Action 列表。
|
|
119
218
|
*/
|
|
@@ -124,44 +223,61 @@ export class ActionRunner {
|
|
|
124
223
|
/**
|
|
125
224
|
* 异步启动 Action 的执行并立即返回 ExecutionHandle 句柄。
|
|
126
225
|
*
|
|
127
|
-
* @param actionOrId Action
|
|
226
|
+
* @param actionOrId Action 定义对象、引用或标识符
|
|
128
227
|
* @param input 传递给 Action 的输入数据
|
|
129
228
|
* @param options 执行控制选项(超时、取消信号、父运行 ID 等)
|
|
130
229
|
* @returns 包含 runId、result Promise 和 cancel 方法的执行句柄
|
|
131
230
|
*/
|
|
132
231
|
start(
|
|
133
|
-
actionOrId: ActionDefinition | string,
|
|
232
|
+
actionOrId: ActionDefinition | ActionRef | string,
|
|
134
233
|
input: unknown = {},
|
|
135
234
|
options: ExecutionStartOptions = {}
|
|
136
235
|
): ExecutionHandle {
|
|
137
|
-
const runId = randomUUID();
|
|
236
|
+
const runId = options.runId || randomUUID();
|
|
138
237
|
const startedAt = new Date().toISOString();
|
|
139
238
|
const callStack = [...(options.callStack || [])];
|
|
140
239
|
|
|
141
|
-
let action: ActionDefinition;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
cancel: () => false,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
action = found;
|
|
240
|
+
let action: ActionDefinition | undefined;
|
|
241
|
+
let targetActionId: string;
|
|
242
|
+
let targetPackageId: string = this.packageId;
|
|
243
|
+
|
|
244
|
+
if (
|
|
245
|
+
typeof actionOrId === "object" &&
|
|
246
|
+
"run" in actionOrId &&
|
|
247
|
+
typeof (actionOrId as any).run === "function"
|
|
248
|
+
) {
|
|
249
|
+
action = actionOrId as ActionDefinition;
|
|
250
|
+
targetActionId = action.id;
|
|
156
251
|
} else {
|
|
157
|
-
|
|
252
|
+
const parsed = ActionResolver.parseRef(actionOrId as ActionRef | string);
|
|
253
|
+
targetActionId = parsed.actionId;
|
|
254
|
+
if (parsed.packageId) {
|
|
255
|
+
targetPackageId = parsed.packageId;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (parsed.packageId && parsed.packageId !== this.packageId) {
|
|
259
|
+
action = this.actions.get(`${parsed.packageId}/${targetActionId}`);
|
|
260
|
+
} else {
|
|
261
|
+
action =
|
|
262
|
+
this.actions.get(targetActionId) ||
|
|
263
|
+
(this.packageId ? this.actions.get(`${this.packageId}/${targetActionId}`) : undefined);
|
|
264
|
+
}
|
|
158
265
|
}
|
|
159
266
|
|
|
160
267
|
// 1. 环路死锁检测 (Cycle Detection)
|
|
161
|
-
|
|
268
|
+
const isExternal = Boolean(targetPackageId && targetPackageId !== this.packageId);
|
|
269
|
+
const callKey = isExternal
|
|
270
|
+
? `${targetPackageId}/${targetActionId}`
|
|
271
|
+
: targetActionId;
|
|
272
|
+
|
|
273
|
+
const hasCycle = isExternal
|
|
274
|
+
? callStack.includes(callKey)
|
|
275
|
+
: (callStack.includes(callKey) || (this.packageId ? callStack.includes(`${this.packageId}/${targetActionId}`) : false));
|
|
276
|
+
|
|
277
|
+
if (hasCycle) {
|
|
162
278
|
const error: RuntimeError = {
|
|
163
279
|
code: "ACTION_CYCLE_DETECTED",
|
|
164
|
-
message: `Cycle detected in action invocation: ${callStack.join(" -> ")} -> ${
|
|
280
|
+
message: `Cycle detected in action invocation: ${callStack.join(" -> ")} -> ${callKey}`,
|
|
165
281
|
};
|
|
166
282
|
return {
|
|
167
283
|
runId,
|
|
@@ -169,10 +285,10 @@ export class ActionRunner {
|
|
|
169
285
|
cancel: () => false,
|
|
170
286
|
};
|
|
171
287
|
}
|
|
172
|
-
callStack.push(
|
|
288
|
+
callStack.push(callKey);
|
|
173
289
|
|
|
174
|
-
// 2. 输入参数 JSON Schema
|
|
175
|
-
if (action
|
|
290
|
+
// 2. 输入参数 JSON Schema 校验(若 action 已就绪)
|
|
291
|
+
if (action?.inputSchema) {
|
|
176
292
|
const val = validateSchema(action.inputSchema, input);
|
|
177
293
|
if (!val.valid) {
|
|
178
294
|
const error: RuntimeError = {
|
|
@@ -193,9 +309,9 @@ export class ActionRunner {
|
|
|
193
309
|
id: runId,
|
|
194
310
|
rootRunId: options.rootRunId || options.parentRunId || runId,
|
|
195
311
|
parentRunId: options.parentRunId,
|
|
196
|
-
packageId:
|
|
197
|
-
packageInstanceId: options.packageInstanceId ||
|
|
198
|
-
actionId:
|
|
312
|
+
packageId: targetPackageId,
|
|
313
|
+
packageInstanceId: options.packageInstanceId || targetPackageId,
|
|
314
|
+
actionId: targetActionId,
|
|
199
315
|
generationId: options.generationId || "1",
|
|
200
316
|
ownerId: options.ownerId || "local",
|
|
201
317
|
status: "running",
|
|
@@ -243,26 +359,26 @@ export class ActionRunner {
|
|
|
243
359
|
};
|
|
244
360
|
|
|
245
361
|
// 5. 构建 ActionContext 运行时上下文
|
|
246
|
-
const
|
|
247
|
-
this.storage,
|
|
248
|
-
this.configOverrides,
|
|
249
|
-
this.projectConfig
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
): Promise<O> => {
|
|
362
|
+
const ctx = createActionContext({
|
|
363
|
+
storage: this.storage,
|
|
364
|
+
overrides: this.configOverrides,
|
|
365
|
+
projectConfig: this.projectConfig,
|
|
366
|
+
runId,
|
|
367
|
+
rootRunId: initialRun.rootRunId,
|
|
368
|
+
parentRunId: options.parentRunId,
|
|
369
|
+
signal: controller.signal,
|
|
370
|
+
process: options.process,
|
|
371
|
+
progress: options.progress,
|
|
372
|
+
logger: options.logger || new StderrLogger(action?.id || targetActionId),
|
|
373
|
+
onActionInvoke: async (childAction, childInput, parentRunId) => {
|
|
259
374
|
const childResult = await this.execute(childAction, childInput, {
|
|
260
375
|
rootRunId: initialRun.rootRunId,
|
|
261
|
-
parentRunId
|
|
376
|
+
parentRunId,
|
|
262
377
|
callStack,
|
|
263
378
|
signal: controller.signal,
|
|
264
379
|
process: options.process,
|
|
265
380
|
progress: options.progress,
|
|
381
|
+
logger: options.logger,
|
|
266
382
|
});
|
|
267
383
|
if (!childResult.ok) {
|
|
268
384
|
const err = new Error(childResult.error.message);
|
|
@@ -270,26 +386,9 @@ export class ActionRunner {
|
|
|
270
386
|
(err as any).details = childResult.error.details;
|
|
271
387
|
throw err;
|
|
272
388
|
}
|
|
273
|
-
return childResult.data
|
|
389
|
+
return childResult.data;
|
|
274
390
|
},
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const ctx: ActionContext = {
|
|
278
|
-
config,
|
|
279
|
-
state,
|
|
280
|
-
actions: invoker,
|
|
281
|
-
process: options.process || getProcessExecutor(),
|
|
282
|
-
log,
|
|
283
|
-
progress: options.progress || {
|
|
284
|
-
report() {},
|
|
285
|
-
},
|
|
286
|
-
signal: controller.signal,
|
|
287
|
-
run: {
|
|
288
|
-
id: runId,
|
|
289
|
-
rootId: initialRun.rootRunId,
|
|
290
|
-
parentId: options.parentRunId,
|
|
291
|
-
},
|
|
292
|
-
};
|
|
391
|
+
});
|
|
293
392
|
|
|
294
393
|
// 6. 执行 Action 业务逻辑并与取消/超时信号进行竞态
|
|
295
394
|
const abortPromise = new Promise<never>((_, reject) => {
|
|
@@ -306,18 +405,44 @@ export class ActionRunner {
|
|
|
306
405
|
|
|
307
406
|
const executionPromise = (async (): Promise<ExecutionResult> => {
|
|
308
407
|
try {
|
|
408
|
+
let currentAction = action;
|
|
409
|
+
if (!currentAction) {
|
|
410
|
+
currentAction = await this.resolveAction(actionOrId);
|
|
411
|
+
if (!currentAction) {
|
|
412
|
+
const error: RuntimeError = {
|
|
413
|
+
code: "ACTION_NOT_FOUND",
|
|
414
|
+
message: `Action '${targetActionId}' not found in registry or linked packages`,
|
|
415
|
+
};
|
|
416
|
+
finalizeRun("failed", undefined, error);
|
|
417
|
+
return { ok: false, runId, error };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (currentAction.inputSchema) {
|
|
421
|
+
const val = validateSchema(currentAction.inputSchema, input);
|
|
422
|
+
if (!val.valid) {
|
|
423
|
+
const error: RuntimeError = {
|
|
424
|
+
code: "INPUT_VALIDATION_FAILED",
|
|
425
|
+
message: `Input schema validation failed for action '${currentAction.id}'`,
|
|
426
|
+
details: val.errors,
|
|
427
|
+
};
|
|
428
|
+
finalizeRun("failed", undefined, error);
|
|
429
|
+
return { ok: false, runId, error };
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
309
434
|
const rawOutput = await Promise.race([
|
|
310
|
-
Promise.resolve().then(() =>
|
|
435
|
+
Promise.resolve().then(() => currentAction!.run(input, ctx)),
|
|
311
436
|
abortPromise,
|
|
312
437
|
]);
|
|
313
438
|
|
|
314
439
|
// 输出结果 Schema 校验
|
|
315
|
-
if (
|
|
316
|
-
const outVal = validateSchema(
|
|
440
|
+
if (currentAction.outputSchema) {
|
|
441
|
+
const outVal = validateSchema(currentAction.outputSchema, rawOutput);
|
|
317
442
|
if (!outVal.valid) {
|
|
318
443
|
const error: RuntimeError = {
|
|
319
444
|
code: "OUTPUT_VALIDATION_FAILED",
|
|
320
|
-
message: `Output schema validation failed for action '${
|
|
445
|
+
message: `Output schema validation failed for action '${currentAction.id}'`,
|
|
321
446
|
details: outVal.errors,
|
|
322
447
|
};
|
|
323
448
|
finalizeRun("failed", undefined, error);
|
|
@@ -388,12 +513,12 @@ export class ActionRunner {
|
|
|
388
513
|
/**
|
|
389
514
|
* 同步等待方式执行指定 Action,直接返回 ExecutionResult 信封结果。
|
|
390
515
|
*
|
|
391
|
-
* @param actionOrId Action
|
|
516
|
+
* @param actionOrId Action 定义对象、引用或标识符
|
|
392
517
|
* @param input 输入参数
|
|
393
518
|
* @param options 执行控制选项
|
|
394
519
|
*/
|
|
395
520
|
async execute(
|
|
396
|
-
actionOrId: ActionDefinition | string,
|
|
521
|
+
actionOrId: ActionDefinition | ActionRef | string,
|
|
397
522
|
input: unknown = {},
|
|
398
523
|
options: ExecutionStartOptions = {}
|
|
399
524
|
): Promise<ExecutionResult> {
|