@actiondock/core 2.9.1 → 2.11.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 +1 -2
- package/dist/catalog/action-catalog.js +3 -33
- package/dist/catalog/discovery.d.ts +0 -3
- package/dist/catalog/discovery.js +4 -4
- package/dist/catalog/graph.d.ts +0 -4
- package/dist/catalog/graph.js +12 -15
- package/dist/{profile/client-actions.d.ts → client/actions.d.ts} +1 -1
- package/dist/{profile/client-actions.js → client/actions.js} +2 -8
- package/dist/{profile/client-config.d.ts → client/config.d.ts} +1 -1
- package/dist/{profile/client-config.js → client/config.js} +1 -1
- package/dist/client/dispatcher.d.ts +4 -0
- package/dist/client/dispatcher.js +21 -0
- package/dist/client/health.d.ts +38 -0
- package/dist/{profile/client-health.js → client/health.js} +9 -6
- package/dist/client/index.d.ts +16 -0
- package/dist/client/index.js +20 -0
- package/dist/{profile/client-playbooks.d.ts → client/playbooks.d.ts} +1 -1
- package/dist/{profile/client-playbooks.js → client/playbooks.js} +1 -1
- package/dist/{profile/client-runs.d.ts → client/runs.d.ts} +4 -2
- package/dist/{profile/client-runs.js → client/runs.js} +7 -1
- package/dist/{profile/client-state.d.ts → client/state.d.ts} +1 -1
- package/dist/{profile/client-state.js → client/state.js} +1 -1
- package/dist/{profile/client-transport.d.ts → client/transport.d.ts} +29 -10
- package/dist/{profile/client-transport.js → client/transport.js} +125 -35
- package/dist/doctor/checks/index.d.ts +1 -1
- package/dist/doctor/checks/index.js +1 -1
- package/dist/doctor/checks/project.js +24 -92
- package/dist/doctor/checks/registry.js +1 -1
- package/dist/doctor/checks/runtime.d.ts +0 -4
- package/dist/doctor/checks/runtime.js +0 -28
- package/dist/doctor/doctor.js +1 -2
- package/dist/errors.d.ts +85 -4
- package/dist/errors.js +114 -4
- package/dist/execution/service.d.ts +55 -37
- package/dist/execution/service.js +601 -212
- package/dist/execution/types.d.ts +24 -5
- package/dist/host/host.d.ts +42 -22
- package/dist/host/host.js +565 -150
- package/dist/host/routing.js +6 -6
- package/dist/host/types.d.ts +25 -13
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/input/advice.d.ts +0 -44
- package/dist/input/advice.js +7 -141
- package/dist/input/describe.d.ts +6 -0
- package/dist/input/describe.js +26 -1
- package/dist/input/flat-materializer.js +1 -1
- package/dist/input/flat-parser.js +1 -1
- package/dist/input/index.d.ts +0 -1
- package/dist/input/index.js +0 -1
- package/dist/input/input-resolver.js +2 -2
- package/dist/input/validation-mapper.d.ts +1 -1
- package/dist/invocation/policy.js +2 -0
- package/dist/ipc/diagnostic.d.ts +8 -21
- package/dist/ipc/diagnostic.js +24 -57
- package/dist/ipc/host.js +21 -7
- package/dist/ipc/service.d.ts +14 -0
- package/dist/ipc/service.js +132 -26
- package/dist/ipc/types.d.ts +0 -4
- package/dist/package/index.d.ts +4 -5
- package/dist/package/index.js +1 -2
- package/dist/package/runtime.d.ts +94 -23
- package/dist/package/runtime.js +628 -274
- package/dist/package/static-index.d.ts +3 -6
- package/dist/package/static-index.js +11 -40
- package/dist/package/types.d.ts +19 -4
- package/dist/platform/index.d.ts +2 -1
- package/dist/platform/index.js +2 -1
- package/dist/platform/loader-hook.d.ts +32 -0
- package/dist/platform/loader-hook.js +90 -0
- package/dist/{node → platform}/module-loader.d.ts +13 -0
- package/dist/{node → platform}/module-loader.js +51 -4
- package/dist/platform/node.d.ts +19 -8
- package/dist/platform/node.js +50 -20
- package/dist/platform/types.d.ts +7 -47
- package/dist/process/context-process.d.ts +3 -18
- package/dist/process/context-process.js +3 -44
- package/dist/process/control.d.ts +21 -0
- package/dist/process/control.js +34 -0
- package/dist/process/converters.d.ts +23 -0
- package/dist/process/converters.js +92 -0
- package/dist/process/cursor.js +2 -22
- package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
- package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
- package/dist/process/index.d.ts +1 -3
- package/dist/process/index.js +1 -3
- package/dist/process/input-dispatcher.d.ts +4 -13
- package/dist/process/input-dispatcher.js +48 -69
- package/dist/process/managed-record.d.ts +3 -31
- package/dist/process/metadata-store.d.ts +0 -41
- package/dist/process/metadata-store.js +2 -341
- package/dist/process/process-driver.js +2 -2
- package/dist/process/process-manager.d.ts +8 -50
- package/dist/process/process-manager.js +28 -417
- package/dist/process/quotas.d.ts +14 -0
- package/dist/process/quotas.js +29 -0
- package/dist/process/run-executor.js +0 -1
- package/dist/process/terminal-output-cache.d.ts +20 -34
- package/dist/process/terminal-output-cache.js +43 -64
- package/dist/profile/index.d.ts +1 -2
- package/dist/profile/index.js +1 -2
- package/dist/profile/manager.d.ts +2 -4
- package/dist/profile/manager.js +10 -40
- package/dist/profile/types.d.ts +1 -17
- package/dist/project/digest.d.ts +5 -1
- package/dist/project/digest.js +81 -219
- package/dist/project/index.d.ts +4 -5
- package/dist/project/index.js +4 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +3 -3
- package/dist/project/loader.js +72 -50
- package/dist/project/lockfile.js +6 -5
- package/dist/project/manifest.js +5 -4
- package/dist/project/transactions.d.ts +4 -42
- package/dist/project/transactions.js +83 -138
- package/dist/project/types-generator.js +2 -1
- package/dist/project/types.d.ts +2 -0
- package/dist/registry/index.d.ts +0 -1
- package/dist/registry/index.js +0 -1
- package/dist/registry/lock.d.ts +2 -11
- package/dist/registry/lock.js +14 -183
- package/dist/registry/registry.d.ts +1 -6
- package/dist/registry/registry.js +5 -17
- package/dist/runtime/action-registry.d.ts +1 -5
- package/dist/runtime/action-registry.js +0 -12
- package/dist/runtime/context.js +2 -2
- package/dist/runtime/events.d.ts +11 -50
- package/dist/runtime/events.js +88 -368
- package/dist/runtime/index.d.ts +0 -4
- package/dist/runtime/index.js +0 -4
- package/dist/runtime/run-persistence.d.ts +2 -0
- package/dist/runtime/run-persistence.js +2 -1
- package/dist/runtime/standalone-commands.d.ts +18 -0
- package/dist/runtime/standalone-commands.js +466 -0
- package/dist/runtime/standalone-help.d.ts +20 -0
- package/dist/runtime/standalone-help.js +72 -0
- package/dist/runtime/standalone.d.ts +1 -44
- package/dist/runtime/standalone.js +31 -526
- package/dist/schema/validator.d.ts +2 -2
- package/dist/schema/validator.js +2 -9
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +0 -2
- package/dist/server/routes/actions.js +1 -1
- package/dist/server/routes/info.js +1 -1
- package/dist/server/routes/playbooks.js +1 -1
- package/dist/server/routes/runs.js +123 -16
- package/dist/server/routes/state.js +43 -18
- package/dist/server/security.d.ts +1 -1
- package/dist/server/security.js +2 -2
- package/dist/server/server.js +3 -7
- package/dist/service/factory.js +4 -3
- package/dist/service/index.d.ts +1 -1
- package/dist/service/index.js +1 -1
- package/dist/service/remote-errors.d.ts +1 -1
- package/dist/service/remote-errors.js +6 -6
- package/dist/service/remote-polling.d.ts +3 -0
- package/dist/service/remote-polling.js +10 -5
- package/dist/service/remote.d.ts +5 -1
- package/dist/service/remote.js +108 -17
- package/dist/service/sse-stream.js +2 -2
- package/dist/service/types.d.ts +22 -19
- package/dist/service/types.js +1 -24
- package/dist/storage/clock.d.ts +1 -1
- package/dist/storage/clock.js +1 -1
- package/dist/storage/data-dir-lock.d.ts +10 -22
- package/dist/storage/data-dir-lock.js +28 -56
- package/dist/storage/file-lock.d.ts +64 -0
- package/dist/storage/file-lock.js +447 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +5 -4
- package/dist/storage/run-liveness.d.ts +48 -0
- package/dist/storage/run-liveness.js +44 -0
- package/dist/storage/sqlite-config.d.ts +16 -0
- package/dist/storage/sqlite-config.js +59 -0
- package/dist/storage/sqlite-driver.d.ts +3 -1
- package/dist/storage/sqlite-driver.js +14 -4
- package/dist/storage/sqlite-events.d.ts +13 -0
- package/dist/storage/sqlite-events.js +48 -0
- package/dist/storage/sqlite-runs.d.ts +107 -0
- package/dist/storage/sqlite-runs.js +622 -0
- package/dist/storage/sqlite-schema.d.ts +5 -0
- package/dist/storage/sqlite-schema.js +143 -0
- package/dist/storage/sqlite-state.d.ts +33 -0
- package/dist/storage/sqlite-state.js +270 -0
- package/dist/storage/sqlite.d.ts +30 -57
- package/dist/storage/sqlite.js +84 -741
- package/dist/storage/types.d.ts +35 -2
- package/dist/utils/index.d.ts +18 -14
- package/dist/utils/index.js +37 -29
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +19 -0
- package/dist/utils/warning.d.ts +1 -0
- package/dist/utils/warning.js +52 -0
- package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
- package/dist/value-validator.js +330 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -12
- package/dist/execution/index.d.ts +0 -2
- package/dist/execution/index.js +0 -2
- package/dist/filter/index.d.ts +0 -1
- package/dist/filter/index.js +0 -1
- package/dist/graph/index.d.ts +0 -4
- package/dist/graph/index.js +0 -4
- package/dist/input/metadata.d.ts +0 -99
- package/dist/input/metadata.js +0 -99
- package/dist/invocation/index.d.ts +0 -2
- package/dist/invocation/index.js +0 -2
- package/dist/ipc/index.d.ts +0 -4
- package/dist/ipc/index.js +0 -4
- package/dist/json/index.d.ts +0 -1
- package/dist/json/index.js +0 -1
- package/dist/json/value-validator.js +0 -330
- package/dist/node/index.d.ts +0 -1
- package/dist/node/index.js +0 -1
- package/dist/platform/node-fs.d.ts +0 -37
- package/dist/platform/node-fs.js +0 -81
- package/dist/process/control-arbiter.d.ts +0 -108
- package/dist/process/control-arbiter.js +0 -250
- package/dist/process/process-executor.d.ts +0 -47
- package/dist/process/process-executor.js +0 -296
- package/dist/profile/client-health.d.ts +0 -14
- package/dist/profile/client-query.d.ts +0 -30
- package/dist/profile/client-query.js +0 -69
- package/dist/profile/client.d.ts +0 -26
- package/dist/profile/client.js +0 -31
- package/dist/project/closure.d.ts +0 -25
- package/dist/project/closure.js +0 -74
- package/dist/registry/store.d.ts +0 -42
- package/dist/registry/store.js +0 -43
- package/dist/runtime/clock.d.ts +0 -7
- package/dist/runtime/clock.js +0 -7
- package/dist/runtime/module-loader.d.ts +0 -2
- package/dist/runtime/module-loader.js +0 -1
- package/dist/runtime/process.d.ts +0 -8
- package/dist/runtime/process.js +0 -1
- package/dist/runtime/runner.d.ts +0 -234
- package/dist/runtime/runner.js +0 -609
- package/dist/schema/index.d.ts +0 -1
- package/dist/schema/index.js +0 -1
- package/dist/server/dispatcher.d.ts +0 -25
- package/dist/server/dispatcher.js +0 -54
- package/dist/service/local.d.ts +0 -27
- package/dist/service/local.js +0 -141
- package/dist/storage/driver.d.ts +0 -10
- package/dist/storage/driver.js +0 -10
- package/dist/storage/lazy.d.ts +0 -8
- package/dist/storage/lazy.js +0 -45
- package/dist/storage/lock-core.d.ts +0 -158
- package/dist/storage/lock-core.js +0 -872
- package/dist/utils/net.d.ts +0 -10
- package/dist/utils/net.js +0 -16
- package/dist/utils/process.d.ts +0 -10
- package/dist/utils/process.js +0 -21
- /package/dist/{filter → utils}/intent.d.ts +0 -0
- /package/dist/{filter → utils}/intent.js +0 -0
package/dist/package/runtime.js
CHANGED
|
@@ -5,17 +5,73 @@ import { createNodePlatform } from "../platform/index.js";
|
|
|
5
5
|
import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
|
|
6
6
|
import { RuntimeConfig } from "../runtime/context.js";
|
|
7
7
|
import { normalizeActionCollection } from "../runtime/action-collection.js";
|
|
8
|
-
import { createLazyStorage } from "../storage/index.js";
|
|
9
8
|
import { isSecretConfigKey, sanitizeConfigDefinitions } from "../storage/mask.js";
|
|
10
9
|
import { decodeStateKey, SqliteRuntimeStorage } from "../storage/sqlite.js";
|
|
11
10
|
import { createPackageIdentity } from "../runtime/identity.js";
|
|
12
|
-
import { ACTION_NOT_FOUND, ActionDockError, CAPABILITY_UNAVAILABLE, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, NOT_FOUND, } from "../errors.js";
|
|
11
|
+
import { ACTION_NOT_FOUND, ActionDockError, CAPABILITY_UNAVAILABLE, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, NOT_FOUND, SERVICE_CLOSED, } from "../errors.js";
|
|
12
|
+
import { createAbortError } from "../ipc/service.js";
|
|
13
13
|
import { createRootInvocationContext } from "../invocation/types.js";
|
|
14
14
|
import { buildStaticActionMap, buildStaticPlaybookMap } from "./static-index.js";
|
|
15
15
|
import { applyActionSummaryFilters } from "./types.js";
|
|
16
|
+
function normalizeStateReadArgs(arg1, arg2, arg3) {
|
|
17
|
+
if (typeof arg2 === "string") {
|
|
18
|
+
if (arg3?.actionId !== undefined && arg3.actionId !== arg1) {
|
|
19
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${arg1}' vs options.actionId '${arg3.actionId}'`);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
key: arg2,
|
|
23
|
+
options: { ...arg3, actionId: arg1 },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
key: arg1,
|
|
28
|
+
options: arg2,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function normalizeStateWriteArgs(arg1, arg2, arg3, arg4, argsCount) {
|
|
32
|
+
const isFourArgs = argsCount !== undefined ? argsCount >= 4 : arg4 !== undefined;
|
|
33
|
+
if (isFourArgs) {
|
|
34
|
+
if (arg4?.actionId !== undefined && arg4.actionId !== arg1) {
|
|
35
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${arg1}' vs options.actionId '${arg4.actionId}'`);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
key: String(arg2),
|
|
39
|
+
value: arg3,
|
|
40
|
+
options: { ...arg4, actionId: arg1 },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (arg3 !== undefined &&
|
|
44
|
+
(typeof arg3 !== "object" || arg3 === null || Array.isArray(arg3))) {
|
|
45
|
+
throw new ActionDockError(INVALID_ARGUMENT, "Invalid options provided to setState. Use setActionState(actionId, key, value, options) or 4-argument setState for action state.");
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
key: arg1,
|
|
49
|
+
value: arg2,
|
|
50
|
+
options: arg3,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function normalizeStateScopeArgs(actionIdOrOptions, options) {
|
|
54
|
+
if (typeof actionIdOrOptions === "string") {
|
|
55
|
+
if (options?.actionId !== undefined && options.actionId !== actionIdOrOptions) {
|
|
56
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionIdOrOptions}' vs options.actionId '${options.actionId}'`);
|
|
57
|
+
}
|
|
58
|
+
return { ...options, actionId: actionIdOrOptions };
|
|
59
|
+
}
|
|
60
|
+
if (actionIdOrOptions === undefined) {
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
return actionIdOrOptions;
|
|
64
|
+
}
|
|
16
65
|
/**
|
|
17
66
|
* ActionDock 统一包运行时默认实现。
|
|
18
67
|
* 封装并管理单个 Action Package 的执行引擎、静态元数据索引、配置与状态存储生命周期。
|
|
68
|
+
*
|
|
69
|
+
* 资源按需装配契约:
|
|
70
|
+
* - 构造函数只完成身份、清单与内存注入动作的归一化,不打开数据库、
|
|
71
|
+
* 不创建进程接口、不注册模块加载钩子、不实例化执行服务;
|
|
72
|
+
* - 包存储、执行服务、全局存储与配置解析器拆分为内部资源槽位,
|
|
73
|
+
* 首次实际调用时准备并缓存复用;
|
|
74
|
+
* - info / list / describe / 规程发现等静态查询全程零资源创建。
|
|
19
75
|
*/
|
|
20
76
|
export class DefaultPackageRuntime {
|
|
21
77
|
identity;
|
|
@@ -24,21 +80,28 @@ export class DefaultPackageRuntime {
|
|
|
24
80
|
generationId;
|
|
25
81
|
packageRoot;
|
|
26
82
|
projectConfig;
|
|
83
|
+
inMemory;
|
|
27
84
|
platform;
|
|
28
|
-
storage;
|
|
29
|
-
globalStorage;
|
|
30
|
-
executionService;
|
|
31
85
|
actionsMap;
|
|
32
86
|
options;
|
|
33
|
-
runtimeConfig;
|
|
34
87
|
isClosed = false;
|
|
35
|
-
|
|
88
|
+
// - 资源槽位(构造阶段全部为空,按需准备)
|
|
89
|
+
storageSlot = {};
|
|
90
|
+
executionSlot = {};
|
|
91
|
+
/** 全局存储:显式注入或首次准备后固定,配合 ownership 判定关闭归属 */
|
|
92
|
+
globalStorageRes;
|
|
93
|
+
runtimeConfig;
|
|
94
|
+
/** 执行委托暂存:执行服务尚未创建时只保存,不触发资源初始化 */
|
|
95
|
+
pendingInvoker;
|
|
96
|
+
/** Host 会话级恢复标记:存储准备阶段收敛执行一次,避免重复执行恢复链 */
|
|
97
|
+
recoveredForHostSession = false;
|
|
36
98
|
/** 静态清单索引缓存:包静态事实(根目录、配置、内存注入集合)在实例生命周期内不变,解析结果同实例内复用 */
|
|
37
99
|
staticActionIndex;
|
|
38
100
|
staticPlaybookIndex;
|
|
39
101
|
constructor(options = {}) {
|
|
40
102
|
this.options = options;
|
|
41
|
-
|
|
103
|
+
this.inMemory = Boolean(options.inMemory);
|
|
104
|
+
// - 确定项目根路径与配置对象
|
|
42
105
|
let packageRoot = options.packageRoot;
|
|
43
106
|
let projectConfig = options.projectConfig;
|
|
44
107
|
if (!packageRoot && !projectConfig) {
|
|
@@ -64,6 +127,7 @@ export class DefaultPackageRuntime {
|
|
|
64
127
|
}
|
|
65
128
|
this.packageRoot = packageRoot;
|
|
66
129
|
this.projectConfig = projectConfig;
|
|
130
|
+
// 兼容未在公开选项契约中显式声明的包实例标识与代系标识内部参数透传
|
|
67
131
|
this.identity = options.identity || createPackageIdentity({
|
|
68
132
|
id: projectConfig.id,
|
|
69
133
|
instanceId: options?.packageInstanceId || projectConfig.packageInstanceId,
|
|
@@ -72,89 +136,16 @@ export class DefaultPackageRuntime {
|
|
|
72
136
|
this.packageId = this.identity.id;
|
|
73
137
|
this.packageInstanceId = this.identity.instanceId;
|
|
74
138
|
this.generationId = this.identity.generation;
|
|
75
|
-
//
|
|
139
|
+
// - 转换 Action 集合:委托归一化单一入口
|
|
76
140
|
this.actionsMap = normalizeActionCollection(options.actions).actionsMap;
|
|
77
|
-
//
|
|
141
|
+
// - 确定平台适配层:默认平台对象自身不打开数据库、不注册加载钩子、不创建进程接口
|
|
78
142
|
this.platform = options.platform ?? createNodePlatform();
|
|
79
|
-
// 4. 确定并初始化存储实例
|
|
80
143
|
if (options.storage) {
|
|
81
|
-
this.
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
const storageOpts = {
|
|
85
|
-
projectRoot: this.packageRoot,
|
|
86
|
-
dataDir: options.dataDir,
|
|
87
|
-
customHome: options.customHome,
|
|
88
|
-
inMemory: options.inMemory,
|
|
89
|
-
// 默认持有者语义:App 主路径打开时收割死亡会话遗留非终态运行记录;
|
|
90
|
-
// CLI 查询旁观视图显式置 recoverOrphans: false 跳过收割
|
|
91
|
-
recoverOrphans: options.recoverOrphans !== false,
|
|
92
|
-
};
|
|
93
|
-
const initStorage = () => {
|
|
94
|
-
return this.platform.storage.createStorage(this.packageId, storageOpts);
|
|
95
|
-
};
|
|
96
|
-
if (options.inMemory) {
|
|
97
|
-
this.storage = initStorage();
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
this.storage = createLazyStorage(initStorage);
|
|
101
|
-
}
|
|
144
|
+
this.storageSlot.instance = options.storage;
|
|
102
145
|
}
|
|
103
|
-
// 5. 确定全局存储实例
|
|
104
146
|
if (options.globalStorage) {
|
|
105
|
-
this.
|
|
106
|
-
this.injectedGlobalStorage = true;
|
|
107
|
-
}
|
|
108
|
-
else if (options.inMemory) {
|
|
109
|
-
this.globalStorage = new SqliteRuntimeStorage({
|
|
110
|
-
dbPath: ":memory:",
|
|
111
|
-
packageId: "__global__",
|
|
112
|
-
});
|
|
113
|
-
this.injectedGlobalStorage = false;
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
const globalOpts = {
|
|
117
|
-
customHome: options.customHome,
|
|
118
|
-
dataDir: options.dataDir,
|
|
119
|
-
inMemory: options.inMemory,
|
|
120
|
-
// 与主存储保持同侧收割语义(全局库 runs 表为空集,收割无实际副作用)
|
|
121
|
-
recoverOrphans: options.recoverOrphans !== false,
|
|
122
|
-
};
|
|
123
|
-
const initGlobalStorage = () => {
|
|
124
|
-
return this.platform.storage.createGlobalStorage(globalOpts);
|
|
125
|
-
};
|
|
126
|
-
this.globalStorage = createLazyStorage(initGlobalStorage);
|
|
127
|
-
this.injectedGlobalStorage = false;
|
|
128
|
-
}
|
|
129
|
-
// 6. 初始化唯一执行协调服务
|
|
130
|
-
this.executionService = new DefaultExecutionService({
|
|
131
|
-
identity: this.identity,
|
|
132
|
-
hostSessionId: options.hostSessionId,
|
|
133
|
-
storage: this.storage,
|
|
134
|
-
globalStorage: this.globalStorage,
|
|
135
|
-
projectRoot: this.packageRoot,
|
|
136
|
-
projectConfig: this.projectConfig,
|
|
137
|
-
configOverrides: options.configOverrides,
|
|
138
|
-
actions: this.actionsMap,
|
|
139
|
-
process: options.process ?? this.platform.process,
|
|
140
|
-
clock: options.clock ?? this.platform.clock,
|
|
141
|
-
logger: options.logger,
|
|
142
|
-
eventSink: options.eventSink ?? this.platform?.eventSink,
|
|
143
|
-
maxActiveRuns: options.maxActiveRuns,
|
|
144
|
-
ownerId: options.ownerId,
|
|
145
|
-
actionResolver: options.actionResolver,
|
|
146
|
-
customHome: options.customHome,
|
|
147
|
-
moduleLoader: this.platform.modules,
|
|
148
|
-
actionInvoker: options.actionInvoker,
|
|
149
|
-
});
|
|
150
|
-
if (!options.actionInvoker) {
|
|
151
|
-
const unsupportedInvoker = async () => {
|
|
152
|
-
throw new ActionDockError(INVOCATION_UNSUPPORTED, "Cascaded action invocation (ctx.actions.invoke) is not supported in standalone PackageRuntime. Actions must be executed within an ActionDock Host.");
|
|
153
|
-
};
|
|
154
|
-
this.executionService.setActionInvoker?.(unsupportedInvoker);
|
|
147
|
+
this.globalStorageRes = { kind: "external", storage: options.globalStorage };
|
|
155
148
|
}
|
|
156
|
-
// 7. 初始化配置解析器
|
|
157
|
-
this.runtimeConfig = new RuntimeConfig(this.storage, options.configOverrides, this.projectConfig, this.globalStorage);
|
|
158
149
|
}
|
|
159
150
|
/**
|
|
160
151
|
* 静态读取并聚合当前包的 Action 规范索引。
|
|
@@ -186,7 +177,260 @@ export class DefaultPackageRuntime {
|
|
|
186
177
|
}
|
|
187
178
|
return this.staticPlaybookIndex;
|
|
188
179
|
}
|
|
180
|
+
assertOpen() {
|
|
181
|
+
if (this.isClosed) {
|
|
182
|
+
throw new ActionDockError(SERVICE_CLOSED, `PackageRuntime for package '${this.packageId}' is closed`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// -------------------------------------------------------------------------
|
|
186
|
+
// - 内部资源准备路径(存储 / 全局存储 / 执行服务 / 配置解析器)
|
|
187
|
+
// -------------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* 准备包存储:打开(或复用注入的)包数据库,等待可选的 ensureInitialized,
|
|
190
|
+
* 并在持有者模式下完成运行记录恢复(允许返回 Promise 的恢复接口必须被等待)。
|
|
191
|
+
* 并发首次调用共享同一在途 Promise;失败时释放新建资源、清空失败 Promise、透传原始错误。
|
|
192
|
+
*/
|
|
193
|
+
ensureStorage() {
|
|
194
|
+
if (this.storageSlot.instance) {
|
|
195
|
+
return Promise.resolve(this.storageSlot.instance);
|
|
196
|
+
}
|
|
197
|
+
if (!this.storageSlot.initPromise) {
|
|
198
|
+
const init = this.openStorageAsync();
|
|
199
|
+
this.storageSlot.initPromise = init;
|
|
200
|
+
init.catch(() => {
|
|
201
|
+
// 失败后清空在途 Promise 供后续显式调用重试;同时防止未处理拒绝告警
|
|
202
|
+
if (this.storageSlot.initPromise === init) {
|
|
203
|
+
this.storageSlot.initPromise = undefined;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return this.storageSlot.initPromise;
|
|
208
|
+
}
|
|
209
|
+
async openStorageAsync() {
|
|
210
|
+
let storage;
|
|
211
|
+
try {
|
|
212
|
+
storage = this.platform.storage.createStorage(this.packageId, this.buildStorageOptions());
|
|
213
|
+
if (typeof storage.ensureInitialized === "function") {
|
|
214
|
+
await storage.ensureInitialized();
|
|
215
|
+
}
|
|
216
|
+
// 存储打开阶段无会话参数的恢复(初始化链语义)已完成:
|
|
217
|
+
// 这里补充 Host 会话粒度的恢复(同一宿主会话内只收敛执行一次,返回 Promise 时被等待)
|
|
218
|
+
await this.recoverForHostSessionOnce(storage);
|
|
219
|
+
this.storageSlot.instance = storage;
|
|
220
|
+
return storage;
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
// 初始化失败:释放本次新建且由本层拥有的资源,透传原始错误
|
|
224
|
+
if (storage) {
|
|
225
|
+
try {
|
|
226
|
+
await storage.close();
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// 忽略清理阶段的副异常,确保原始错误优先透传
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** 构造包存储工厂选项(单一事实源,异步与同步打开路径共用) */
|
|
236
|
+
buildStorageOptions() {
|
|
237
|
+
return {
|
|
238
|
+
projectRoot: this.packageRoot,
|
|
239
|
+
dataDir: this.options.dataDir,
|
|
240
|
+
customHome: this.options.customHome,
|
|
241
|
+
inMemory: this.options.inMemory,
|
|
242
|
+
// 默认持有者语义:App 主路径打开时收割死亡会话遗留非终态运行记录;
|
|
243
|
+
// CLI 查询旁观视图显式置 recoverOrphans: false 跳过收割
|
|
244
|
+
recoverOrphans: this.options.recoverOrphans !== false,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Host 会话级运行记录恢复:同实例生命周期内最多执行一次。
|
|
249
|
+
* 允许返回 Promise 的恢复接口被等待:恢复完成(或失败透传)前存储不发布;
|
|
250
|
+
* 恢复失败沿用既有可观测处理(告警透传),不因异步化静默丢失。
|
|
251
|
+
*/
|
|
252
|
+
async recoverForHostSessionOnce(storage) {
|
|
253
|
+
if (this.recoveredForHostSession) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this.recoveredForHostSession = true;
|
|
257
|
+
if (this.options.recoverOrphans === false) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (typeof storage.recoverDeadSessionRuns !== "function") {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
await storage.recoverDeadSessionRuns(this.options.hostSessionId);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
// 单包恢复失败不阻断整体接管流程,但必须可观测:告警后透传,不静默丢失
|
|
268
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
269
|
+
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${this.packageId}': ${message}`);
|
|
270
|
+
throw err;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 准备全局存储:显式注入优先,其次 Host 共享提供函数,最后独立使用平台存储工厂。
|
|
275
|
+
* 同步契约:RuntimeConfig 与 ctx.config.get() 的读取是同步的,故全局库必须同步可得。
|
|
276
|
+
*/
|
|
277
|
+
ensureGlobalStorageSync() {
|
|
278
|
+
if (this.globalStorageRes) {
|
|
279
|
+
return this.globalStorageRes.storage;
|
|
280
|
+
}
|
|
281
|
+
const provider = this.options.globalStorageProvider;
|
|
282
|
+
if (provider) {
|
|
283
|
+
// Host 提供函数复用 Host 级缓存,同一 Host 不为每个包重复打开全局库
|
|
284
|
+
// (优先级高于独立内存自建路径,保证 Host 与包共享同一全局库实例)
|
|
285
|
+
const storage = provider();
|
|
286
|
+
this.globalStorageRes = { kind: "external", storage };
|
|
287
|
+
return storage;
|
|
288
|
+
}
|
|
289
|
+
if (this.options.inMemory) {
|
|
290
|
+
// 独立内存包的全局库:包自建自关(同步创建,无 IO)
|
|
291
|
+
const storage = new SqliteRuntimeStorage({
|
|
292
|
+
dbPath: ":memory:",
|
|
293
|
+
packageId: "__global__",
|
|
294
|
+
});
|
|
295
|
+
this.globalStorageRes = { kind: "own", storage };
|
|
296
|
+
return storage;
|
|
297
|
+
}
|
|
298
|
+
const globalOpts = {
|
|
299
|
+
customHome: this.options.customHome,
|
|
300
|
+
dataDir: this.options.dataDir,
|
|
301
|
+
inMemory: this.options.inMemory,
|
|
302
|
+
// 与主存储保持同侧收割语义(全局库 runs 表为空集,收割无实际副作用)
|
|
303
|
+
recoverOrphans: this.options.recoverOrphans !== false,
|
|
304
|
+
};
|
|
305
|
+
const storage = this.platform.storage.createGlobalStorage(globalOpts);
|
|
306
|
+
this.globalStorageRes = { kind: "own", storage };
|
|
307
|
+
return storage;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 异步准备全局存储:供执行服务装配前调用,保证可选的异步初始化完成。
|
|
311
|
+
* 同步路径(RuntimeConfig 读取)仍走 ensureGlobalStorageSync 复用同一实例。
|
|
312
|
+
*/
|
|
313
|
+
async ensureGlobalStorage() {
|
|
314
|
+
const storage = this.ensureGlobalStorageSync();
|
|
315
|
+
if (typeof storage.ensureInitialized === "function") {
|
|
316
|
+
await storage.ensureInitialized();
|
|
317
|
+
}
|
|
318
|
+
return storage;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 准备配置解析器:依赖包存储与全局存储(五层优先级链既有实现不变)。
|
|
322
|
+
* 同步契约:RuntimeConfig 与 ctx.config.get() 的读取是同步的,故同步打开包库;
|
|
323
|
+
* 若异步准备已在途,同步路径仅在实例已发布后复用,避免双打开竞态。
|
|
324
|
+
*/
|
|
325
|
+
ensureRuntimeConfig() {
|
|
326
|
+
if (!this.runtimeConfig) {
|
|
327
|
+
this.runtimeConfig = new RuntimeConfig(this.getStorageAlreadyPrepared(), this.options.configOverrides, this.projectConfig, this.ensureGlobalStorageSync());
|
|
328
|
+
}
|
|
329
|
+
return this.runtimeConfig;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 获取已就绪的包存储(同步契约专用):
|
|
333
|
+
* - 已发布实例直接复用;
|
|
334
|
+
* - 异步准备在途时等待其结算后复用同一实例;
|
|
335
|
+
* - 未准备时同步打开并登记在途 Promise 已完成状态。
|
|
336
|
+
* 单一事实源:同步打开也走 buildStorageOptions,并补齐会话恢复等待语义的同步近似。
|
|
337
|
+
*/
|
|
338
|
+
getStorageAlreadyPrepared() {
|
|
339
|
+
if (this.storageSlot.instance) {
|
|
340
|
+
return this.storageSlot.instance;
|
|
341
|
+
}
|
|
342
|
+
if (this.storageSlot.initPromise) {
|
|
343
|
+
// 异步准备在途:无法同步等待,抛出明确错误供调用方改用异步入口
|
|
344
|
+
throw new ActionDockError(SERVICE_CLOSED, `Package storage for '${this.packageId}' is still initializing; retry after initialization settles`);
|
|
345
|
+
}
|
|
346
|
+
return this.openStorageSync();
|
|
347
|
+
}
|
|
348
|
+
/** 同步打开包存储(仅当异步准备从未发起时使用) */
|
|
349
|
+
openStorageSync() {
|
|
350
|
+
this.assertOpen();
|
|
351
|
+
const storage = this.platform.storage.createStorage(this.packageId, this.buildStorageOptions());
|
|
352
|
+
this.storageSlot.instance = storage;
|
|
353
|
+
this.storageSlot.initPromise = Promise.resolve(storage);
|
|
354
|
+
return storage;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 准备执行服务:等待存储与全局存储就绪后创建唯一 DefaultExecutionService。
|
|
358
|
+
* 失败时只回收执行初始化独自新建的资源,不关闭仍被状态查询使用的包存储。
|
|
359
|
+
*/
|
|
360
|
+
ensureExecutionService() {
|
|
361
|
+
if (this.executionSlot.instance) {
|
|
362
|
+
return Promise.resolve(this.executionSlot.instance);
|
|
363
|
+
}
|
|
364
|
+
if (!this.executionSlot.initPromise) {
|
|
365
|
+
const init = this.buildExecutionService();
|
|
366
|
+
this.executionSlot.initPromise = init;
|
|
367
|
+
init.catch(() => {
|
|
368
|
+
// 失败后清空在途 Promise 供后续显式调用重试;同时防止未处理拒绝告警
|
|
369
|
+
if (this.executionSlot.initPromise === init) {
|
|
370
|
+
this.executionSlot.initPromise = undefined;
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return this.executionSlot.initPromise;
|
|
375
|
+
}
|
|
376
|
+
async buildExecutionService() {
|
|
377
|
+
try {
|
|
378
|
+
const storage = await this.prepareStorageAsync();
|
|
379
|
+
const globalStorage = await this.ensureGlobalStorage();
|
|
380
|
+
const service = new DefaultExecutionService({
|
|
381
|
+
identity: this.identity,
|
|
382
|
+
hostSessionId: this.options.hostSessionId,
|
|
383
|
+
storage,
|
|
384
|
+
globalStorage,
|
|
385
|
+
projectRoot: this.packageRoot,
|
|
386
|
+
projectConfig: this.projectConfig,
|
|
387
|
+
configOverrides: this.options.configOverrides,
|
|
388
|
+
actions: this.actionsMap,
|
|
389
|
+
process: this.options.process ?? this.platform.process,
|
|
390
|
+
clock: this.options.clock ?? this.platform.clock,
|
|
391
|
+
logger: this.options.logger,
|
|
392
|
+
eventSink: this.options.eventSink ?? this.platform.eventSink,
|
|
393
|
+
maxActiveRuns: this.options.maxActiveRuns,
|
|
394
|
+
ownerId: this.options.ownerId,
|
|
395
|
+
actionResolver: this.options.actionResolver,
|
|
396
|
+
customHome: this.options.customHome,
|
|
397
|
+
moduleLoader: this.platform.modules,
|
|
398
|
+
// 委托优先级:最后一次绑定的 pendingInvoker 优先,否则透传显式注入的构造选项委托
|
|
399
|
+
actionInvoker: this.pendingInvoker ?? this.options.actionInvoker,
|
|
400
|
+
});
|
|
401
|
+
// 服务创建后使用最后一次绑定的委托;单包独立使用保留 INVOCATION_UNSUPPORTED 限制
|
|
402
|
+
if (!this.pendingInvoker && !this.options.actionInvoker) {
|
|
403
|
+
const unsupportedInvoker = async () => {
|
|
404
|
+
throw new ActionDockError(INVOCATION_UNSUPPORTED, "Cascaded action invocation (ctx.actions.invoke) is not supported in standalone PackageRuntime. Actions must be executed within an ActionDock Host.");
|
|
405
|
+
};
|
|
406
|
+
service.setActionInvoker?.(unsupportedInvoker);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
service.setActionInvoker?.(this.pendingInvoker ?? this.options.actionInvoker);
|
|
410
|
+
}
|
|
411
|
+
this.executionSlot.instance = service;
|
|
412
|
+
return service;
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
// 执行准备失败:只回收执行初始化独自新建的资源,不关闭仍被状态查询使用的包存储;
|
|
416
|
+
// 在途 Promise 由 ensureExecutionService 的失败回调清空(不缓存永久失败,不后台轮询)
|
|
417
|
+
throw err;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/** 异步等待存储准备完成(供执行准备与异步入口复用;单一赋值入口) */
|
|
421
|
+
async prepareStorageAsync() {
|
|
422
|
+
this.assertOpen();
|
|
423
|
+
if (this.storageSlot.instance) {
|
|
424
|
+
return this.storageSlot.instance;
|
|
425
|
+
}
|
|
426
|
+
return this.ensureStorage();
|
|
427
|
+
}
|
|
428
|
+
/** 获取已创建的执行服务(若存在),不触发创建 */
|
|
429
|
+
peekExecutionService() {
|
|
430
|
+
return this.executionSlot.instance;
|
|
431
|
+
}
|
|
189
432
|
async info(options) {
|
|
433
|
+
this.assertOpen();
|
|
190
434
|
const actionsMap = this.getStaticActionMap();
|
|
191
435
|
const playbooksMap = this.getStaticPlaybookMap();
|
|
192
436
|
const actions = Array.from(actionsMap.keys());
|
|
@@ -208,6 +452,7 @@ export class DefaultPackageRuntime {
|
|
|
208
452
|
};
|
|
209
453
|
}
|
|
210
454
|
async listActions(options) {
|
|
455
|
+
this.assertOpen();
|
|
211
456
|
const map = this.getStaticActionMap();
|
|
212
457
|
const summaries = Array.from(map.values()).map((spec) => ({
|
|
213
458
|
id: spec.id,
|
|
@@ -223,6 +468,7 @@ export class DefaultPackageRuntime {
|
|
|
223
468
|
return applyActionSummaryFilters(summaries, options);
|
|
224
469
|
}
|
|
225
470
|
async describeAction(id) {
|
|
471
|
+
this.assertOpen();
|
|
226
472
|
const map = this.getStaticActionMap();
|
|
227
473
|
let spec = map.get(id);
|
|
228
474
|
if (!spec && id.startsWith(`${this.packageId}/`)) {
|
|
@@ -236,30 +482,18 @@ export class DefaultPackageRuntime {
|
|
|
236
482
|
}
|
|
237
483
|
}
|
|
238
484
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
// 仅在本包范围内解析动态动作:限定 packageId 前缀,避免全局兜底搜索把
|
|
248
|
-
// 其他包的动作误计为本包提供者(host 层据此统计候选导致 AMBIGUOUS_ACTION_REF 误报)。
|
|
249
|
-
const qualifiedId = id.includes("/") ? id : `${this.packageId}/${id}`;
|
|
250
|
-
const resolution = await this.executionService.resolveAction(qualifiedId);
|
|
251
|
-
if (resolution) {
|
|
252
|
-
liveAction = resolution;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// 忽略动态解析异常
|
|
257
|
-
}
|
|
258
|
-
}
|
|
485
|
+
// 仅读取内存注入动作与已存在(不触发创建)的执行服务缓存,不调用动态解析器
|
|
486
|
+
const cachedService = this.peekExecutionService();
|
|
487
|
+
const liveAction = this.actionsMap.get(id) ||
|
|
488
|
+
(spec ? this.actionsMap.get(spec.id) : undefined) ||
|
|
489
|
+
(cachedService?.getAction
|
|
490
|
+
? cachedService.getAction(id) || (spec ? cachedService.getAction(spec.id) : undefined)
|
|
491
|
+
: undefined);
|
|
259
492
|
if (liveAction) {
|
|
493
|
+
// 第三方 Action 实例或内存注册 Action 可能存在规范外的运行时元数据扩展字段
|
|
260
494
|
const actObj = liveAction;
|
|
261
495
|
return {
|
|
262
|
-
id: actObj.id || id,
|
|
496
|
+
id: actObj.id || spec?.id || id,
|
|
263
497
|
packageId: this.packageId,
|
|
264
498
|
description: actObj.description ?? spec?.description,
|
|
265
499
|
inputSchema: actObj.inputSchema ?? spec?.inputSchema,
|
|
@@ -280,6 +514,7 @@ export class DefaultPackageRuntime {
|
|
|
280
514
|
};
|
|
281
515
|
}
|
|
282
516
|
async listPlaybooks() {
|
|
517
|
+
this.assertOpen();
|
|
283
518
|
const map = this.getStaticPlaybookMap();
|
|
284
519
|
return Array.from(map.values()).map((spec) => ({
|
|
285
520
|
id: spec.id,
|
|
@@ -290,6 +525,7 @@ export class DefaultPackageRuntime {
|
|
|
290
525
|
}));
|
|
291
526
|
}
|
|
292
527
|
async describePlaybook(id) {
|
|
528
|
+
this.assertOpen();
|
|
293
529
|
const map = this.getStaticPlaybookMap();
|
|
294
530
|
const cleanId = id.replace(/\.md$/, "");
|
|
295
531
|
const spec = map.get(cleanId) || map.get(id);
|
|
@@ -317,6 +553,7 @@ export class DefaultPackageRuntime {
|
|
|
317
553
|
});
|
|
318
554
|
}
|
|
319
555
|
async runAction(id, input, options) {
|
|
556
|
+
const service = await this.prepareExecution();
|
|
320
557
|
let actionId = id;
|
|
321
558
|
if (actionId.includes("/")) {
|
|
322
559
|
if (actionId.startsWith(`${this.packageId}/`)) {
|
|
@@ -327,9 +564,10 @@ export class DefaultPackageRuntime {
|
|
|
327
564
|
}
|
|
328
565
|
}
|
|
329
566
|
const context = this.buildRootInvocationContext(actionId, options);
|
|
330
|
-
return
|
|
567
|
+
return service.execute(actionId, input, context);
|
|
331
568
|
}
|
|
332
569
|
async startAction(id, input, options) {
|
|
570
|
+
const service = await this.prepareExecution();
|
|
333
571
|
let actionId = id;
|
|
334
572
|
if (actionId.includes("/")) {
|
|
335
573
|
if (actionId.startsWith(`${this.packageId}/`)) {
|
|
@@ -340,37 +578,103 @@ export class DefaultPackageRuntime {
|
|
|
340
578
|
}
|
|
341
579
|
}
|
|
342
580
|
const context = this.buildRootInvocationContext(actionId, options);
|
|
343
|
-
return
|
|
581
|
+
return service.start(actionId, input, context);
|
|
344
582
|
}
|
|
345
583
|
async startInvocation(actionId, input, context) {
|
|
584
|
+
const service = await this.prepareExecution();
|
|
346
585
|
let targetId = actionId;
|
|
347
586
|
if (targetId.startsWith(`${this.packageId}/`)) {
|
|
348
587
|
targetId = targetId.slice(this.packageId.length + 1);
|
|
349
588
|
}
|
|
350
|
-
return
|
|
589
|
+
return service.start(targetId, input, context);
|
|
590
|
+
}
|
|
591
|
+
/** 执行前统一准备:关闭检查与执行服务就绪 */
|
|
592
|
+
async prepareExecution() {
|
|
593
|
+
this.assertOpen();
|
|
594
|
+
return this.ensureExecutionService();
|
|
351
595
|
}
|
|
352
596
|
async getRun(runId) {
|
|
353
|
-
|
|
597
|
+
this.assertOpen();
|
|
598
|
+
// 历史读取复用存储能力,不创建执行服务;详情路径补齐 requestId 关联
|
|
599
|
+
const storage = await this.prepareStorageAsync();
|
|
600
|
+
if (typeof storage.getRunWithRequestId === "function") {
|
|
601
|
+
return storage.getRunWithRequestId(runId) ?? undefined;
|
|
602
|
+
}
|
|
603
|
+
return storage.getRun(runId) ?? undefined;
|
|
354
604
|
}
|
|
355
605
|
async cancelRun(runId, reason) {
|
|
356
|
-
|
|
606
|
+
this.assertOpen();
|
|
607
|
+
// 包已有执行服务时交由该服务处理活动任务与取消信号;
|
|
608
|
+
// 没有服务时只读取历史记录,不为了取消不存在的本地活动任务创建执行服务
|
|
609
|
+
const service = this.peekExecutionService();
|
|
610
|
+
if (service) {
|
|
611
|
+
return service.cancel(runId, reason);
|
|
612
|
+
}
|
|
613
|
+
const record = (await this.prepareStorageAsync()).getRun(runId);
|
|
614
|
+
if (record) {
|
|
615
|
+
return { outcome: "already_terminal", runId, status: record.status };
|
|
616
|
+
}
|
|
617
|
+
return { outcome: "not_found", runId };
|
|
357
618
|
}
|
|
358
619
|
events(runId, options) {
|
|
359
|
-
|
|
620
|
+
this.assertOpen();
|
|
621
|
+
// 已有执行服务时沿用其事件源;没有服务时复用注入/平台事件源,不新建执行服务
|
|
622
|
+
const service = this.peekExecutionService();
|
|
623
|
+
if (service) {
|
|
624
|
+
return service.events(runId, options);
|
|
625
|
+
}
|
|
626
|
+
const sink = this.options.eventSink ?? this.platform.eventSink;
|
|
627
|
+
if (sink) {
|
|
628
|
+
return sink.subscribe(runId, options);
|
|
629
|
+
}
|
|
630
|
+
// 无任何事件源时返回空流(不新建执行服务)
|
|
631
|
+
return (async function* () { })();
|
|
360
632
|
}
|
|
361
633
|
setActionInvoker(invoker) {
|
|
362
|
-
|
|
363
|
-
|
|
634
|
+
// 执行服务尚未创建时只保存委托,不触发资源初始化;服务创建后使用最后一次绑定的委托
|
|
635
|
+
this.pendingInvoker = invoker;
|
|
636
|
+
const service = this.peekExecutionService();
|
|
637
|
+
if (service) {
|
|
638
|
+
service.setActionInvoker?.(invoker);
|
|
364
639
|
}
|
|
365
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Host 持有者路径预热入口:完成包存储准备(含运行记录恢复),不创建执行服务。
|
|
643
|
+
* 延迟装配下旁观路径零资源创建,持有者工厂在返回前调用本方法保持既有初始化时机。
|
|
644
|
+
*/
|
|
645
|
+
async prepareResources() {
|
|
646
|
+
this.assertOpen();
|
|
647
|
+
const storage = await this.prepareStorageAsync();
|
|
648
|
+
// 注入存储路径也应执行会话级恢复:持有者预热返回前恢复必须完成(含返回 Promise 的实现)
|
|
649
|
+
await this.recoverForHostSessionOnce(storage);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* 显式触发运行记录恢复(Host 持有者路径兼容入口)。
|
|
653
|
+
* 延迟装配下恢复已收敛到存储准备路径,本方法确保存储已就绪后按需补一次会话恢复。
|
|
654
|
+
*/
|
|
366
655
|
recoverDeadSessionRuns(sessionId) {
|
|
367
|
-
if (
|
|
368
|
-
this.
|
|
656
|
+
if (this.isClosed) {
|
|
657
|
+
throw new ActionDockError(SERVICE_CLOSED, `PackageRuntime for package '${this.packageId}' is closed`);
|
|
658
|
+
}
|
|
659
|
+
const storage = this.storageSlot.instance ?? this.openStorageSync();
|
|
660
|
+
if (typeof storage.recoverDeadSessionRuns === "function") {
|
|
661
|
+
try {
|
|
662
|
+
const result = storage.recoverDeadSessionRuns(sessionId ?? this.options.hostSessionId);
|
|
663
|
+
if (result instanceof Promise) {
|
|
664
|
+
void result.catch((err) => {
|
|
665
|
+
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${this.packageId}': ${err instanceof Error ? err.message : String(err)}`);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
catch (err) {
|
|
670
|
+
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${this.packageId}': ${err instanceof Error ? err.message : String(err)}`);
|
|
671
|
+
}
|
|
369
672
|
}
|
|
370
673
|
}
|
|
371
674
|
async listConfig() {
|
|
675
|
+
this.assertOpen();
|
|
372
676
|
const declared = this.projectConfig.config || {};
|
|
373
|
-
const stored = this.
|
|
677
|
+
const stored = (await this.prepareStorageAsync()).listConfig();
|
|
374
678
|
const allKeys = Array.from(new Set([...Object.keys(declared), ...Object.keys(stored)]));
|
|
375
679
|
const views = [];
|
|
376
680
|
for (const key of allKeys) {
|
|
@@ -382,10 +686,11 @@ export class DefaultPackageRuntime {
|
|
|
382
686
|
return views;
|
|
383
687
|
}
|
|
384
688
|
async getConfig(key) {
|
|
689
|
+
this.assertOpen();
|
|
385
690
|
const itemDef = this.projectConfig.config?.[key];
|
|
386
691
|
const isSecret = isSecretConfigKey(key, itemDef);
|
|
387
692
|
// 委托 RuntimeConfig 五层优先级链单一事实源,避免重复实现解析链
|
|
388
|
-
const resolved = this.
|
|
693
|
+
const resolved = this.ensureRuntimeConfig().describe(key);
|
|
389
694
|
// overrides 来源对外统一星现为包级覆盖视角
|
|
390
695
|
const source = resolved.source === "overrides" ? "package" : resolved.source;
|
|
391
696
|
const configured = resolved.source !== "default";
|
|
@@ -398,187 +703,220 @@ export class DefaultPackageRuntime {
|
|
|
398
703
|
};
|
|
399
704
|
}
|
|
400
705
|
async setConfig(key, value) {
|
|
401
|
-
|
|
706
|
+
this.assertOpen();
|
|
707
|
+
await (await this.prepareStorageAsync()).setConfig(key, value);
|
|
402
708
|
}
|
|
403
709
|
async deleteConfig(key) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* 状态作用域统一解析辅助函数(单一事实源)。
|
|
408
|
-
*
|
|
409
|
-
* 完成重载消歧后委托 mergeStateScope 计算最终生效的命名空间:
|
|
410
|
-
* - (actionId, key, options) 形态:位置 actionId 参与冲突校验;
|
|
411
|
-
* - (key, options) 扁平形态:首参是状态键,options.actionId 属合法显式指定。
|
|
412
|
-
*/
|
|
413
|
-
resolveStateScope(actionIdOrKey, keyOrOptions, options) {
|
|
414
|
-
// 消歧规则(与原始重载语义严格一致):仅当第二参为字符串时认定为
|
|
415
|
-
// (actionId, key, options) 形态;否则(undefined 或选项对象)认定为
|
|
416
|
-
// (key, options) 扁平调用形态,首参是状态键而非 actionId
|
|
417
|
-
if (typeof keyOrOptions === "string") {
|
|
418
|
-
return { ns: this.mergeStateScope(actionIdOrKey, options), opts: options };
|
|
419
|
-
}
|
|
420
|
-
return { ns: this.mergeStateScope(undefined, keyOrOptions), opts: keyOrOptions };
|
|
710
|
+
this.assertOpen();
|
|
711
|
+
return await (await this.prepareStorageAsync()).deleteConfig(key);
|
|
421
712
|
}
|
|
422
713
|
/**
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
* - positionalActionId 非 undefined:调用形态携带位置 actionId,
|
|
426
|
-
* options.actionId 与其冲突时抛出异常(空串同样参与冲突校验);
|
|
427
|
-
* - positionalActionId 为 undefined:无位置 actionId 的扁平调用形态,
|
|
428
|
-
* options.actionId 属合法显式指定,直接采纳。
|
|
714
|
+
* 将可能携带命名空间前缀的状态键解码为 (namespace, key)。
|
|
715
|
+
* 解码失败时按无命名空间处理。
|
|
429
716
|
*/
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (opts?.actionId && opts.actionId !== actionId) {
|
|
435
|
-
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
|
|
436
|
-
}
|
|
717
|
+
decodeStateKeyParts(key) {
|
|
718
|
+
try {
|
|
719
|
+
const decoded = decodeStateKey(key);
|
|
720
|
+
return { namespace: decoded.namespace, key: decoded.key };
|
|
437
721
|
}
|
|
438
|
-
|
|
439
|
-
|
|
722
|
+
catch {
|
|
723
|
+
return { namespace: "", key };
|
|
440
724
|
}
|
|
725
|
+
}
|
|
726
|
+
hasExplicitStateScope(options) {
|
|
727
|
+
return options?.actionId !== undefined || options?.namespace !== undefined;
|
|
728
|
+
}
|
|
729
|
+
computeStateNamespace(options) {
|
|
730
|
+
const actionId = options?.actionId ?? "";
|
|
441
731
|
return actionId
|
|
442
|
-
? (
|
|
443
|
-
: (
|
|
444
|
-
}
|
|
445
|
-
async
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
732
|
+
? (options?.namespace ? `${actionId}:${options.namespace}` : actionId)
|
|
733
|
+
: (options?.namespace ?? "");
|
|
734
|
+
}
|
|
735
|
+
async getStateInternal(key, options) {
|
|
736
|
+
const storage = await this.prepareStorageAsync();
|
|
737
|
+
const explicitScope = this.hasExplicitStateScope(options);
|
|
738
|
+
const ns = this.computeStateNamespace(options);
|
|
739
|
+
if (options?.detail) {
|
|
740
|
+
const entry = await storage.findState(key, explicitScope ? ns : undefined);
|
|
450
741
|
return entry;
|
|
451
742
|
}
|
|
452
|
-
if (ns) {
|
|
453
|
-
return await
|
|
743
|
+
if (explicitScope || ns) {
|
|
744
|
+
return await storage.getState(ns, key);
|
|
454
745
|
}
|
|
455
|
-
const entry = await
|
|
746
|
+
const entry = await storage.findState(key);
|
|
456
747
|
return entry?.value;
|
|
457
748
|
}
|
|
458
|
-
async
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// 消歧规则(与原始重载语义严格一致):按实参个数区分三形态
|
|
465
|
-
if (arguments.length >= 4) {
|
|
466
|
-
positionalActionId = actionIdOrKey;
|
|
467
|
-
key = keyOrValue;
|
|
468
|
-
value = valueOrOptions;
|
|
469
|
-
opts = options;
|
|
470
|
-
}
|
|
471
|
-
else if (arguments.length === 2) {
|
|
472
|
-
key = actionIdOrKey;
|
|
473
|
-
value = keyOrValue;
|
|
474
|
-
opts = undefined;
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
if (valueOrOptions !== undefined &&
|
|
478
|
-
(typeof valueOrOptions !== "object" || valueOrOptions === null || Array.isArray(valueOrOptions))) {
|
|
479
|
-
throw new ActionDockError(INVALID_ARGUMENT, "Invalid options provided to setState. Use setActionState(actionId, key, value, options) or 4-argument setState for action state.");
|
|
480
|
-
}
|
|
481
|
-
key = actionIdOrKey;
|
|
482
|
-
value = keyOrValue;
|
|
483
|
-
opts = valueOrOptions;
|
|
484
|
-
}
|
|
485
|
-
// 作用域解析与命名空间计算统一委托 mergeStateScope 单一事实源
|
|
486
|
-
const ns = this.mergeStateScope(positionalActionId, opts);
|
|
487
|
-
if (ns) {
|
|
488
|
-
await this.storage.setState(ns, key, value, opts?.ttl);
|
|
749
|
+
async setStateInternal(key, value, options) {
|
|
750
|
+
const storage = await this.prepareStorageAsync();
|
|
751
|
+
const explicitScope = this.hasExplicitStateScope(options);
|
|
752
|
+
const ns = this.computeStateNamespace(options);
|
|
753
|
+
if (explicitScope || ns) {
|
|
754
|
+
await storage.setState(ns, key, value, options?.ttl);
|
|
489
755
|
return;
|
|
490
756
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
|
|
757
|
+
const target = this.decodeStateKeyParts(key);
|
|
758
|
+
await storage.setState(target.namespace, target.key, value, options?.ttl);
|
|
759
|
+
}
|
|
760
|
+
async deleteStateInternal(key, options) {
|
|
761
|
+
const storage = await this.prepareStorageAsync();
|
|
762
|
+
const explicitScope = this.hasExplicitStateScope(options);
|
|
763
|
+
const ns = this.computeStateNamespace(options);
|
|
764
|
+
if (explicitScope || ns) {
|
|
765
|
+
return await storage.deleteState(ns, key);
|
|
766
|
+
}
|
|
767
|
+
const target = this.decodeStateKeyParts(key);
|
|
768
|
+
return await storage.deleteState(target.namespace, target.key);
|
|
769
|
+
}
|
|
770
|
+
async listStateKeysInternal(options) {
|
|
771
|
+
const storage = await this.prepareStorageAsync();
|
|
772
|
+
if (options?.namespace === null) {
|
|
773
|
+
return storage.listStateKeys(null, options?.prefix);
|
|
774
|
+
}
|
|
775
|
+
const explicitScope = this.hasExplicitStateScope(options);
|
|
776
|
+
const ns = this.computeStateNamespace(options);
|
|
777
|
+
const targetNs = explicitScope ? ns : (ns ? ns : null);
|
|
778
|
+
return storage.listStateKeys(targetNs, options?.prefix);
|
|
779
|
+
}
|
|
780
|
+
async clearStateInternal(options) {
|
|
781
|
+
const storage = await this.prepareStorageAsync();
|
|
782
|
+
const explicitScope = this.hasExplicitStateScope(options);
|
|
783
|
+
const ns = this.computeStateNamespace(options);
|
|
784
|
+
const targetNs = options?.all ? undefined : (explicitScope ? ns : (ns ? ns : undefined));
|
|
785
|
+
return storage.clearState({
|
|
786
|
+
namespace: targetNs,
|
|
787
|
+
prefix: options?.prefix,
|
|
788
|
+
all: options?.all,
|
|
789
|
+
});
|
|
502
790
|
}
|
|
503
|
-
async
|
|
504
|
-
|
|
505
|
-
const key
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
targetNs = "";
|
|
518
|
-
}
|
|
519
|
-
return await this.storage.deleteState(targetNs, targetKey);
|
|
791
|
+
async getState(arg1, arg2, arg3) {
|
|
792
|
+
this.assertOpen();
|
|
793
|
+
const { key, options } = normalizeStateReadArgs(arg1, arg2, arg3);
|
|
794
|
+
return this.getStateInternal(key, options);
|
|
795
|
+
}
|
|
796
|
+
async setState(arg1, arg2, arg3, arg4) {
|
|
797
|
+
this.assertOpen();
|
|
798
|
+
const { key, value, options } = normalizeStateWriteArgs(arg1, arg2, arg3, arg4, arguments.length);
|
|
799
|
+
return this.setStateInternal(key, value, options);
|
|
800
|
+
}
|
|
801
|
+
async deleteState(arg1, arg2, arg3) {
|
|
802
|
+
this.assertOpen();
|
|
803
|
+
const { key, options } = normalizeStateReadArgs(arg1, arg2, arg3);
|
|
804
|
+
return this.deleteStateInternal(key, options);
|
|
520
805
|
}
|
|
521
806
|
async getActionState(actionId, key, options) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const entry = await this.storage.findState(key, ns || undefined);
|
|
525
|
-
return entry;
|
|
807
|
+
if (options?.actionId !== undefined && options.actionId !== actionId) {
|
|
808
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
|
|
526
809
|
}
|
|
527
|
-
return
|
|
810
|
+
return this.getStateInternal(key, { ...options, actionId });
|
|
528
811
|
}
|
|
529
812
|
async setActionState(actionId, key, value, options) {
|
|
530
|
-
|
|
531
|
-
|
|
813
|
+
if (options?.actionId !== undefined && options.actionId !== actionId) {
|
|
814
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
|
|
815
|
+
}
|
|
816
|
+
return this.setStateInternal(key, value, { ...options, actionId });
|
|
532
817
|
}
|
|
533
818
|
async deleteActionState(actionId, key, options) {
|
|
534
|
-
|
|
535
|
-
|
|
819
|
+
if (options?.actionId !== undefined && options.actionId !== actionId) {
|
|
820
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
|
|
821
|
+
}
|
|
822
|
+
return this.deleteStateInternal(key, { ...options, actionId });
|
|
536
823
|
}
|
|
537
824
|
async listStateKeys(actionIdOrOptions, options) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (typeof actionIdOrOptions === "string") {
|
|
542
|
-
ns = this.resolveStateScope(actionIdOrOptions, "", options).ns;
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
const opts = actionIdOrOptions;
|
|
546
|
-
ns = opts?.actionId
|
|
547
|
-
? (opts.namespace ? `${opts.actionId}:${opts.namespace}` : opts.actionId)
|
|
548
|
-
: (opts?.namespace ?? "");
|
|
549
|
-
}
|
|
550
|
-
const opts = typeof actionIdOrOptions === "string" ? options : actionIdOrOptions;
|
|
551
|
-
return this.storage.listStateKeys(ns ? ns : null, opts?.prefix);
|
|
825
|
+
this.assertOpen();
|
|
826
|
+
const normalizedOptions = normalizeStateScopeArgs(actionIdOrOptions, options);
|
|
827
|
+
return this.listStateKeysInternal(normalizedOptions);
|
|
552
828
|
}
|
|
553
829
|
async clearState(actionIdOrOptions, options) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const { ns } = this.resolveStateScope(actionIdOrOptions, "", options);
|
|
558
|
-
const finalNs = ns || (options?.namespace ?? "");
|
|
559
|
-
return this.storage.clearState({
|
|
560
|
-
namespace: finalNs ? finalNs : undefined,
|
|
561
|
-
prefix: options?.prefix,
|
|
562
|
-
all: options?.all,
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
const opts = actionIdOrOptions;
|
|
566
|
-
const ns = opts?.namespace;
|
|
567
|
-
return this.storage.clearState({
|
|
568
|
-
namespace: ns ? ns : undefined,
|
|
569
|
-
prefix: opts?.prefix,
|
|
570
|
-
all: opts?.all,
|
|
571
|
-
});
|
|
830
|
+
this.assertOpen();
|
|
831
|
+
const normalizedOptions = normalizeStateScopeArgs(actionIdOrOptions, options);
|
|
832
|
+
return this.clearStateInternal(normalizedOptions);
|
|
572
833
|
}
|
|
573
834
|
async listRuns(options) {
|
|
574
|
-
|
|
835
|
+
this.assertOpen();
|
|
836
|
+
if (options?.signal?.aborted) {
|
|
837
|
+
throw createAbortError(options.signal.reason);
|
|
838
|
+
}
|
|
839
|
+
const storage = await this.prepareStorageAsync();
|
|
840
|
+
if (options?.signal?.aborted) {
|
|
841
|
+
throw createAbortError(options.signal.reason);
|
|
842
|
+
}
|
|
843
|
+
// requestId 反查路径:经 idempotency_keys 反查关联运行记录(存储可选能力)
|
|
844
|
+
if (options?.requestIds && options.requestIds.length > 0) {
|
|
845
|
+
if (typeof storage.listRunsByRequestIds !== "function") {
|
|
846
|
+
return [];
|
|
847
|
+
}
|
|
848
|
+
let records = storage.listRunsByRequestIds(options.requestIds);
|
|
849
|
+
if (options.actionId) {
|
|
850
|
+
records = records.filter((r) => r.actionId === options.actionId);
|
|
851
|
+
}
|
|
852
|
+
if (options.status) {
|
|
853
|
+
records = records.filter((r) => r.status === options.status);
|
|
854
|
+
}
|
|
855
|
+
records.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
|
|
856
|
+
if (typeof options.offset === "number" && options.offset > 0) {
|
|
857
|
+
records = records.slice(options.offset);
|
|
858
|
+
}
|
|
859
|
+
if (options.limit !== undefined && records.length > options.limit) {
|
|
860
|
+
records.length = options.limit;
|
|
861
|
+
}
|
|
862
|
+
return records;
|
|
863
|
+
}
|
|
864
|
+
const records = storage.listRuns({
|
|
575
865
|
actionId: options?.actionId,
|
|
576
866
|
status: options?.status,
|
|
577
867
|
limit: options?.limit,
|
|
868
|
+
offset: options?.offset,
|
|
869
|
+
});
|
|
870
|
+
// 常规路径补齐 requestId 关联(可选能力缺失时保持无关联,不阻断查询)
|
|
871
|
+
if (typeof storage.getRunRequestIds === "function" && records.length > 0) {
|
|
872
|
+
const requestIdByRunId = storage.getRunRequestIds(records.map((r) => r.id));
|
|
873
|
+
for (const record of records) {
|
|
874
|
+
const requestId = requestIdByRunId[record.id];
|
|
875
|
+
if (requestId)
|
|
876
|
+
record.requestId = requestId;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return records;
|
|
880
|
+
}
|
|
881
|
+
async countRuns(options) {
|
|
882
|
+
this.assertOpen();
|
|
883
|
+
if (options?.signal?.aborted) {
|
|
884
|
+
throw createAbortError(options.signal.reason);
|
|
885
|
+
}
|
|
886
|
+
const storage = await this.prepareStorageAsync();
|
|
887
|
+
if (options?.signal?.aborted) {
|
|
888
|
+
throw createAbortError(options.signal.reason);
|
|
889
|
+
}
|
|
890
|
+
if (typeof storage.countRuns === "function") {
|
|
891
|
+
return storage.countRuns({
|
|
892
|
+
actionId: options?.actionId,
|
|
893
|
+
status: options?.status,
|
|
894
|
+
requestIds: options?.requestIds,
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
if (options?.requestIds && options.requestIds.length > 0) {
|
|
898
|
+
if (typeof storage.listRunsByRequestIds === "function") {
|
|
899
|
+
let records = storage.listRunsByRequestIds(options.requestIds);
|
|
900
|
+
if (options.actionId) {
|
|
901
|
+
records = records.filter((r) => r.actionId === options.actionId);
|
|
902
|
+
}
|
|
903
|
+
if (options.status) {
|
|
904
|
+
records = records.filter((r) => r.status === options.status);
|
|
905
|
+
}
|
|
906
|
+
return records.length;
|
|
907
|
+
}
|
|
908
|
+
return 0;
|
|
909
|
+
}
|
|
910
|
+
const records = storage.listRuns({
|
|
911
|
+
actionId: options?.actionId,
|
|
912
|
+
status: options?.status,
|
|
913
|
+
limit: 100_000,
|
|
578
914
|
});
|
|
915
|
+
return records.length;
|
|
579
916
|
}
|
|
580
917
|
async clearRuns(options) {
|
|
581
|
-
|
|
918
|
+
this.assertOpen();
|
|
919
|
+
return (await this.prepareStorageAsync()).clearRuns({
|
|
582
920
|
actionId: options?.actionId,
|
|
583
921
|
status: options?.status,
|
|
584
922
|
olderThanMs: options?.olderThanMs,
|
|
@@ -586,14 +924,15 @@ export class DefaultPackageRuntime {
|
|
|
586
924
|
});
|
|
587
925
|
}
|
|
588
926
|
async cleanExpiredRuns(policy) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return 0;
|
|
927
|
+
this.assertOpen();
|
|
928
|
+
const storage = await this.prepareStorageAsync();
|
|
929
|
+
return storage.cleanExpiredRuns?.(policy) ?? 0;
|
|
593
930
|
}
|
|
594
931
|
async listStateEntries(options) {
|
|
595
|
-
|
|
596
|
-
|
|
932
|
+
this.assertOpen();
|
|
933
|
+
const storage = await this.prepareStorageAsync();
|
|
934
|
+
if (typeof storage.listStateEntries === "function") {
|
|
935
|
+
return storage.listStateEntries(options);
|
|
597
936
|
}
|
|
598
937
|
throw new ActionDockError(CAPABILITY_UNAVAILABLE, `CAPABILITY_UNAVAILABLE: listStateEntries is not supported by package '${this.packageId}' storage`);
|
|
599
938
|
}
|
|
@@ -601,20 +940,35 @@ export class DefaultPackageRuntime {
|
|
|
601
940
|
if (this.isClosed)
|
|
602
941
|
return;
|
|
603
942
|
this.isClosed = true;
|
|
943
|
+
// 等待已经开始的资源准备结算;初始化完成后不把实例发布给已关闭对象
|
|
944
|
+
const pendingStorage = this.storageSlot.initPromise?.catch(() => undefined);
|
|
945
|
+
const pendingExecution = this.executionSlot.initPromise?.catch(() => undefined);
|
|
946
|
+
await Promise.allSettled([pendingStorage, pendingExecution]);
|
|
947
|
+
const service = this.executionSlot.instance;
|
|
948
|
+
this.executionSlot.instance = undefined;
|
|
949
|
+
this.executionSlot.initPromise = undefined;
|
|
604
950
|
try {
|
|
605
|
-
|
|
951
|
+
if (service) {
|
|
952
|
+
await service.close(options);
|
|
953
|
+
}
|
|
606
954
|
}
|
|
607
955
|
finally {
|
|
956
|
+
const storage = this.storageSlot.instance;
|
|
957
|
+
this.storageSlot.instance = undefined;
|
|
958
|
+
this.storageSlot.initPromise = undefined;
|
|
608
959
|
try {
|
|
609
|
-
|
|
960
|
+
storage?.close();
|
|
610
961
|
}
|
|
611
962
|
catch {
|
|
612
963
|
// 忽略存储重复关闭异常
|
|
613
964
|
}
|
|
614
965
|
finally {
|
|
615
|
-
|
|
966
|
+
// 共享全局库由 Host 关闭;独立包自建的全局库由包关闭;外部注入存储不在此关闭
|
|
967
|
+
const globalRes = this.globalStorageRes;
|
|
968
|
+
this.globalStorageRes = undefined;
|
|
969
|
+
if (globalRes?.kind === "own") {
|
|
616
970
|
try {
|
|
617
|
-
|
|
971
|
+
globalRes.storage.close();
|
|
618
972
|
}
|
|
619
973
|
catch {
|
|
620
974
|
// 忽略全局存储关闭异常
|