@actiondock/core 2.5.1 → 2.6.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.
Files changed (134) hide show
  1. package/dist/app/app.d.ts +14 -1
  2. package/dist/app/app.js +88 -222
  3. package/dist/app/static-index.d.ts +39 -0
  4. package/dist/app/static-index.js +164 -0
  5. package/dist/app/types.d.ts +5 -0
  6. package/dist/catalog/action-index.js +6 -4
  7. package/dist/catalog/package-catalog.js +2 -1
  8. package/dist/doctor/checks/index.d.ts +5 -0
  9. package/dist/doctor/checks/index.js +5 -0
  10. package/dist/doctor/checks/project-locate.d.ts +6 -0
  11. package/dist/doctor/checks/project-locate.js +36 -0
  12. package/dist/doctor/checks/project.d.ts +6 -0
  13. package/dist/doctor/checks/project.js +383 -0
  14. package/dist/doctor/checks/registry.d.ts +13 -0
  15. package/dist/doctor/checks/registry.js +207 -0
  16. package/dist/doctor/checks/runtime.d.ts +13 -0
  17. package/dist/doctor/checks/runtime.js +90 -0
  18. package/dist/doctor/checks/storage.d.ts +5 -0
  19. package/dist/doctor/checks/storage.js +34 -0
  20. package/dist/doctor/context.d.ts +35 -0
  21. package/dist/doctor/context.js +11 -0
  22. package/dist/doctor/doctor.d.ts +5 -0
  23. package/dist/doctor/doctor.js +33 -602
  24. package/dist/errors.d.ts +2 -0
  25. package/dist/errors.js +2 -0
  26. package/dist/execution/service.js +14 -9
  27. package/dist/host/host.d.ts +33 -0
  28. package/dist/host/host.js +222 -362
  29. package/dist/host/routing.d.ts +92 -0
  30. package/dist/host/routing.js +238 -0
  31. package/dist/host/types.d.ts +8 -0
  32. package/dist/ipc/host.js +38 -0
  33. package/dist/platform/types.d.ts +7 -0
  34. package/dist/process/control-arbiter.d.ts +107 -0
  35. package/dist/process/control-arbiter.js +251 -0
  36. package/dist/process/diagnostics-sink.d.ts +26 -0
  37. package/dist/process/diagnostics-sink.js +35 -0
  38. package/dist/process/index.d.ts +7 -0
  39. package/dist/process/index.js +7 -0
  40. package/dist/process/input-dispatcher.d.ts +88 -0
  41. package/dist/process/input-dispatcher.js +240 -0
  42. package/dist/process/managed-record.d.ts +78 -0
  43. package/dist/process/managed-record.js +1 -0
  44. package/dist/process/process-manager.d.ts +15 -56
  45. package/dist/process/process-manager.js +119 -707
  46. package/dist/process/reservation-table.d.ts +39 -0
  47. package/dist/process/reservation-table.js +80 -0
  48. package/dist/process/run-executor.d.ts +16 -0
  49. package/dist/process/run-executor.js +155 -0
  50. package/dist/process/terminal-output-cache.d.ts +66 -0
  51. package/dist/process/terminal-output-cache.js +132 -0
  52. package/dist/profile/client-actions.d.ts +56 -0
  53. package/dist/profile/client-actions.js +215 -0
  54. package/dist/profile/client-config.d.ts +20 -0
  55. package/dist/profile/client-config.js +51 -0
  56. package/dist/profile/client-health.d.ts +14 -0
  57. package/dist/profile/client-health.js +68 -0
  58. package/dist/profile/client-playbooks.d.ts +19 -0
  59. package/dist/profile/client-playbooks.js +36 -0
  60. package/dist/profile/client-query.d.ts +30 -0
  61. package/dist/profile/client-query.js +70 -0
  62. package/dist/profile/client-runs.d.ts +42 -0
  63. package/dist/profile/client-runs.js +58 -0
  64. package/dist/profile/client-state.d.ts +56 -0
  65. package/dist/profile/client-state.js +83 -0
  66. package/dist/profile/client-transport.d.ts +61 -0
  67. package/dist/profile/client-transport.js +155 -0
  68. package/dist/profile/client.d.ts +23 -142
  69. package/dist/profile/client.js +28 -588
  70. package/dist/profile/manager.js +1 -1
  71. package/dist/project/closure.d.ts +4 -4
  72. package/dist/project/closure.js +11 -5
  73. package/dist/project/init.js +2 -2
  74. package/dist/project/loader.d.ts +0 -25
  75. package/dist/project/loader.js +1 -154
  76. package/dist/project/transactions.d.ts +12 -14
  77. package/dist/project/transactions.js +60 -836
  78. package/dist/registry/registry.js +6 -4
  79. package/dist/registry/resolve.js +1 -1
  80. package/dist/runtime/action-registry.d.ts +45 -0
  81. package/dist/runtime/action-registry.js +108 -0
  82. package/dist/runtime/clock.d.ts +5 -17
  83. package/dist/runtime/clock.js +5 -15
  84. package/dist/runtime/context.d.ts +3 -0
  85. package/dist/runtime/context.js +17 -56
  86. package/dist/runtime/index.d.ts +3 -0
  87. package/dist/runtime/index.js +3 -0
  88. package/dist/runtime/package-runner-factory.d.ts +82 -0
  89. package/dist/runtime/package-runner-factory.js +186 -0
  90. package/dist/runtime/run-persistence.d.ts +85 -0
  91. package/dist/runtime/run-persistence.js +111 -0
  92. package/dist/runtime/runner.d.ts +23 -33
  93. package/dist/runtime/runner.js +92 -286
  94. package/dist/runtime/standalone.d.ts +11 -1
  95. package/dist/runtime/standalone.js +137 -25
  96. package/dist/server/dispatcher.d.ts +8 -3
  97. package/dist/server/dispatcher.js +16 -40
  98. package/dist/server/routes/runs.js +6 -4
  99. package/dist/server/security.d.ts +1 -7
  100. package/dist/server/security.js +2 -13
  101. package/dist/storage/clock.d.ts +25 -0
  102. package/dist/storage/clock.js +23 -0
  103. package/dist/storage/data-dir-lock.d.ts +3 -36
  104. package/dist/storage/data-dir-lock.js +48 -881
  105. package/dist/storage/index.d.ts +8 -1
  106. package/dist/storage/index.js +8 -2
  107. package/dist/storage/lazy.js +1 -0
  108. package/dist/storage/lock-core.d.ts +162 -0
  109. package/dist/storage/lock-core.js +885 -0
  110. package/dist/storage/sqlite.d.ts +19 -6
  111. package/dist/storage/sqlite.js +81 -89
  112. package/dist/storage/types.d.ts +27 -5
  113. package/dist/storage/types.js +21 -0
  114. package/dist/storage/utils.d.ts +22 -0
  115. package/dist/storage/utils.js +34 -0
  116. package/dist/target/local.d.ts +1 -0
  117. package/dist/target/local.js +23 -17
  118. package/dist/target/remote-errors.d.ts +21 -0
  119. package/dist/target/remote-errors.js +51 -0
  120. package/dist/target/remote-polling.d.ts +35 -0
  121. package/dist/target/remote-polling.js +104 -0
  122. package/dist/target/remote.d.ts +4 -0
  123. package/dist/target/remote.js +108 -301
  124. package/dist/target/sse-parser.d.ts +35 -0
  125. package/dist/target/sse-parser.js +163 -0
  126. package/dist/target/target.js +2 -0
  127. package/dist/target/types.d.ts +6 -0
  128. package/dist/utils/index.d.ts +1 -0
  129. package/dist/utils/index.js +1 -0
  130. package/dist/utils/net.d.ts +10 -0
  131. package/dist/utils/net.js +16 -0
  132. package/dist/version.d.ts +1 -1
  133. package/dist/version.js +1 -1
  134. package/package.json +2 -2
@@ -0,0 +1,92 @@
1
+ import type { ActionRef, RuntimeError } from "@actiondock/sdk";
2
+ import type { ActionDockApp, ActionSpec, PackageInfo, PlaybookSpec, PlaybookSummary } from "../app/types.js";
3
+ import type { ActionPackageResolver } from "../project/resolver.js";
4
+ /**
5
+ * 判定异常是否为 not-found 语义(包内确实不存在该 Action)。
6
+ *
7
+ * 遍历匹配短标识符时仅跳过此类错误;存储损坏、模块加载失败等内部错误必须向调用方透传,
8
+ * 严禁被吞没后伪装成 ACTION_NOT_FOUND。
9
+ */
10
+ export declare function isActionNotFoundLikeError(err: any): boolean;
11
+ /**
12
+ * 宽松解析引用:优先结构化解析,失败时按对象或裸短标识符回退。
13
+ */
14
+ export declare function parseRefLoose(ref: ActionRef | string): ActionRef;
15
+ /**
16
+ * 根调用可见性判定:非公开包且存在依赖解析器时,交由解析器委托规则裁决。
17
+ */
18
+ export declare function isRootCallVisible(packageId: string, actionId: string, hostPublicPackageIds: ReadonlySet<string>, resolver?: ActionPackageResolver): boolean;
19
+ /**
20
+ * 跨包短标识符歧义消解结果:唯一匹配、歧义多匹配与零匹配三态。
21
+ */
22
+ export type ShortRefResolution = {
23
+ kind: "unique";
24
+ app: ActionDockApp;
25
+ } | {
26
+ kind: "ambiguous";
27
+ candidates: string[];
28
+ } | {
29
+ kind: "not_found";
30
+ };
31
+ /**
32
+ * 在全部已注册包中按短标识符解析唯一提供方。
33
+ *
34
+ * 可见性过滤遵循根调用规则(公开包直接可见;传递依赖包仅可见 Playbook 委托项);
35
+ * 仅 not-found 语义错误被跳过,其余异常向调用方透传。
36
+ */
37
+ export declare function resolveShortRef(apps: readonly ActionDockApp[], actionId: string, visibility: {
38
+ hostPublicPackageIds: ReadonlySet<string>;
39
+ resolver?: ActionPackageResolver;
40
+ }): Promise<ShortRefResolution>;
41
+ /**
42
+ * 按包标识构造未找到错误信息:优先透传链接包加载失败的精准诊断。
43
+ */
44
+ export declare function packageNotFoundMessage(packageId: string, failedLinkedPackages: ReadonlyMap<string, {
45
+ path: string;
46
+ error: string;
47
+ }>): string;
48
+ /**
49
+ * 静态查询并聚合可见包的 Playbook 规程摘要(带包前缀限定)。
50
+ */
51
+ export declare function listVisiblePlaybooks(apps: readonly ActionDockApp[], visibility: {
52
+ hostPublicPackageIds: ReadonlySet<string>;
53
+ resolver?: ActionPackageResolver;
54
+ }): Promise<PlaybookSummary[]>;
55
+ /**
56
+ * 静态查询指定 Playbook 规范:支持「包/规程」限定引用与裸短标识符两级解析。
57
+ * 未声明依赖的传递包、不存在的规程与空匹配分别抛出对应语义错误。
58
+ */
59
+ export declare function describeVisiblePlaybook(apps: readonly ActionDockApp[], id: string, visibility: {
60
+ hostPublicPackageIds: ReadonlySet<string>;
61
+ resolver?: ActionPackageResolver;
62
+ }): Promise<PlaybookSpec>;
63
+ /**
64
+ * 聚合全部已注册包的元数据信息。
65
+ */
66
+ export declare function collectPackageInfos(apps: readonly ActionDockApp[]): Promise<PackageInfo[]>;
67
+ /**
68
+ * 构造失配类错误响应载荷(ACTION_NOT_FOUND / AMBIGUOUS_ACTION_REF 等)。
69
+ */
70
+ export declare function buildRuntimeError(code: string, message: string, details?: Record<string, unknown>): RuntimeError;
71
+ /**
72
+ * 构造短标识符歧义错误消息(供静态查询与启动执行链路复用统一文案)。
73
+ */
74
+ export declare function ambiguousActionMessage(actionId: string, candidates: readonly string[]): string;
75
+ /**
76
+ * 静态查询指定 Action 规范:包限定引用直查,裸短标识符跨包消歧后返回。
77
+ */
78
+ export declare function describeActionAcrossApps(ref: ActionRef | string, apps: readonly ActionDockApp[], visibility: {
79
+ hostPublicPackageIds: ReadonlySet<string>;
80
+ resolver?: ActionPackageResolver;
81
+ }, failedLinkedPackages: ReadonlyMap<string, {
82
+ path: string;
83
+ error: string;
84
+ }>): Promise<ActionSpec>;
85
+ /** existsSync 的可注入探测依赖(纯函数模块保持可测试性) */
86
+ export type ExistsProbe = (path: string) => boolean;
87
+ /**
88
+ * 解析宿主自动加载工程根目录:显式指定优先,其次当前进程工作目录探测。
89
+ */
90
+ export declare function resolveProjectRoot(options: {
91
+ projectRoot?: string;
92
+ }, detect: () => string | null, exists?: ExistsProbe): string | null;
@@ -0,0 +1,238 @@
1
+ import { existsSync } from "node:fs";
2
+ import { ActionResolver } from "../catalog/action-resolver.js";
3
+ /**
4
+ * 判定异常是否为 not-found 语义(包内确实不存在该 Action)。
5
+ *
6
+ * 遍历匹配短标识符时仅跳过此类错误;存储损坏、模块加载失败等内部错误必须向调用方透传,
7
+ * 严禁被吞没后伪装成 ACTION_NOT_FOUND。
8
+ */
9
+ export function isActionNotFoundLikeError(err) {
10
+ if (!err) {
11
+ return false;
12
+ }
13
+ if (err.code === "ACTION_NOT_FOUND" || err.code === "NOT_FOUND") {
14
+ return true;
15
+ }
16
+ const message = typeof err.message === "string" ? err.message : String(err);
17
+ return (message.startsWith("ACTION_NOT_FOUND:") ||
18
+ /Action '[^']*' not found in package/.test(message));
19
+ }
20
+ /**
21
+ * 宽松解析引用:优先结构化解析,失败时按对象或裸短标识符回退。
22
+ */
23
+ export function parseRefLoose(ref) {
24
+ try {
25
+ return ActionResolver.parseRef(ref);
26
+ }
27
+ catch {
28
+ return typeof ref === "object" ? ref : { actionId: ref };
29
+ }
30
+ }
31
+ /**
32
+ * 根调用可见性判定:非公开包且存在依赖解析器时,交由解析器委托规则裁决。
33
+ */
34
+ export function isRootCallVisible(packageId, actionId, hostPublicPackageIds, resolver) {
35
+ const isPublic = hostPublicPackageIds.has(packageId);
36
+ if (isPublic) {
37
+ return true;
38
+ }
39
+ if (resolver && !resolver.canRootCall(packageId, actionId)) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ /**
45
+ * 在全部已注册包中按短标识符解析唯一提供方。
46
+ *
47
+ * 可见性过滤遵循根调用规则(公开包直接可见;传递依赖包仅可见 Playbook 委托项);
48
+ * 仅 not-found 语义错误被跳过,其余异常向调用方透传。
49
+ */
50
+ export async function resolveShortRef(apps, actionId, visibility) {
51
+ const matches = [];
52
+ for (const app of apps) {
53
+ try {
54
+ if (!isRootCallVisible(app.packageId, actionId, visibility.hostPublicPackageIds, visibility.resolver)) {
55
+ continue;
56
+ }
57
+ await app.describeAction(actionId);
58
+ matches.push(app);
59
+ }
60
+ catch (err) {
61
+ // 仅将 not-found 语义视为「包内无此 Action」;其余异常必须透传,避免内部错误伪装成 ACTION_NOT_FOUND
62
+ if (!isActionNotFoundLikeError(err)) {
63
+ throw err;
64
+ }
65
+ }
66
+ }
67
+ if (matches.length === 1) {
68
+ return { kind: "unique", app: matches[0] };
69
+ }
70
+ if (matches.length > 1) {
71
+ return { kind: "ambiguous", candidates: matches.map((m) => m.packageId) };
72
+ }
73
+ return { kind: "not_found" };
74
+ }
75
+ /**
76
+ * 按包标识构造未找到错误信息:优先透传链接包加载失败的精准诊断。
77
+ */
78
+ export function packageNotFoundMessage(packageId, failedLinkedPackages) {
79
+ const failed = failedLinkedPackages.get(packageId);
80
+ if (failed) {
81
+ return `Package '${packageId}' not found in host (failed to load from '${failed.path}': ${failed.error})`;
82
+ }
83
+ return `Package '${packageId}' not found in host`;
84
+ }
85
+ /**
86
+ * 静态查询并聚合可见包的 Playbook 规程摘要(带包前缀限定)。
87
+ */
88
+ export async function listVisiblePlaybooks(apps, visibility) {
89
+ const results = [];
90
+ for (const app of apps) {
91
+ const isPublic = visibility.hostPublicPackageIds.has(app.packageId);
92
+ if (!isPublic && visibility.resolver) {
93
+ const graph = visibility.resolver.resolveSync();
94
+ if (!graph.directDependencyIds.has(app.packageId)) {
95
+ continue;
96
+ }
97
+ }
98
+ const appPlaybooks = await app.listPlaybooks();
99
+ for (const item of appPlaybooks) {
100
+ const qualifiedId = apps.length > 1 && !item.id.includes("/")
101
+ ? `${app.packageId}/${item.id}`
102
+ : item.id;
103
+ results.push({
104
+ ...item,
105
+ id: qualifiedId,
106
+ packageId: app.packageId,
107
+ });
108
+ }
109
+ }
110
+ return results;
111
+ }
112
+ /**
113
+ * 静态查询指定 Playbook 规范:支持「包/规程」限定引用与裸短标识符两级解析。
114
+ * 未声明依赖的传递包、不存在的规程与空匹配分别抛出对应语义错误。
115
+ */
116
+ export async function describeVisiblePlaybook(apps, id, visibility) {
117
+ if (id.includes("/")) {
118
+ const lastSlashIndex = id.lastIndexOf("/");
119
+ const packageId = id.slice(0, lastSlashIndex);
120
+ const playbookId = id.slice(lastSlashIndex + 1);
121
+ const isPublic = visibility.hostPublicPackageIds.has(packageId);
122
+ if (!isPublic && visibility.resolver) {
123
+ const graph = visibility.resolver.resolveSync();
124
+ if (!graph.directDependencyIds.has(packageId)) {
125
+ throw new Error(`UNDECLARED_ACTION_DEPENDENCY: Playbook '${id}' belongs to undeclared transitive package '${packageId}'`);
126
+ }
127
+ }
128
+ const app = apps.find((a) => a.packageId === packageId);
129
+ if (!app) {
130
+ throw new Error(`Package '${packageId}' not found in host`);
131
+ }
132
+ return app.describePlaybook(playbookId);
133
+ }
134
+ for (const app of apps) {
135
+ try {
136
+ const isPublic = visibility.hostPublicPackageIds.has(app.packageId);
137
+ if (!isPublic && visibility.resolver) {
138
+ const graph = visibility.resolver.resolveSync();
139
+ if (!graph.directDependencyIds.has(app.packageId)) {
140
+ continue;
141
+ }
142
+ }
143
+ return await app.describePlaybook(id);
144
+ }
145
+ catch {
146
+ // 忽略未匹配的包
147
+ }
148
+ }
149
+ throw new Error(`Playbook '${id}' not found in any registered package`);
150
+ }
151
+ /**
152
+ * 聚合全部已注册包的元数据信息。
153
+ */
154
+ export async function collectPackageInfos(apps) {
155
+ return Promise.all(apps.map((app) => app.info()));
156
+ }
157
+ /**
158
+ * 构造失配类错误响应载荷(ACTION_NOT_FOUND / AMBIGUOUS_ACTION_REF 等)。
159
+ */
160
+ export function buildRuntimeError(code, message, details) {
161
+ const error = { code, message };
162
+ if (details !== undefined) {
163
+ error.details = details;
164
+ }
165
+ return error;
166
+ }
167
+ /**
168
+ * 构造短标识符歧义错误消息(供静态查询与启动执行链路复用统一文案)。
169
+ */
170
+ export function ambiguousActionMessage(actionId, candidates) {
171
+ const joined = candidates.map((c) => `${c}/${actionId}`).join(", ");
172
+ return `Action '${actionId}' is ambiguous and provided by multiple packages: ${joined}. Please specify '<package-id>/${actionId}'.`;
173
+ }
174
+ /**
175
+ * 静态查询指定 Action 规范:包限定引用直查,裸短标识符跨包消歧后返回。
176
+ */
177
+ export async function describeActionAcrossApps(ref, apps, visibility, failedLinkedPackages) {
178
+ const parsed = parseRefLoose(ref);
179
+ if (parsed.packageId) {
180
+ if (!isRootCallVisible(parsed.packageId, parsed.actionId, visibility.hostPublicPackageIds, visibility.resolver)) {
181
+ throw new Error(`UNDECLARED_ACTION_DEPENDENCY: Action '${parsed.packageId}/${parsed.actionId}' is not declared as a direct dependency in actiondock.json and is not delegated by a visible playbook`);
182
+ }
183
+ const app = apps.find((a) => a.packageId === parsed.packageId);
184
+ if (!app) {
185
+ throw new Error(packageNotFoundMessage(parsed.packageId, failedLinkedPackages));
186
+ }
187
+ const spec = await app.describeAction(parsed.actionId);
188
+ return {
189
+ ...spec,
190
+ packageId: app.packageId,
191
+ };
192
+ }
193
+ const matches = [];
194
+ for (const app of apps) {
195
+ try {
196
+ if (!isRootCallVisible(app.packageId, parsed.actionId, visibility.hostPublicPackageIds, visibility.resolver)) {
197
+ continue;
198
+ }
199
+ const spec = await app.describeAction(parsed.actionId);
200
+ matches.push({ app, spec: { ...spec, packageId: app.packageId } });
201
+ }
202
+ catch (err) {
203
+ // 仅将 not-found 语义视为「包内无此 Action」;其余异常必须透传,避免内部错误伪装成 ACTION_NOT_FOUND
204
+ if (!isActionNotFoundLikeError(err)) {
205
+ throw err;
206
+ }
207
+ }
208
+ }
209
+ if (matches.length === 1) {
210
+ return {
211
+ ...matches[0].spec,
212
+ packageId: matches[0].app.packageId,
213
+ };
214
+ }
215
+ if (matches.length > 1) {
216
+ const err = new Error(`INVALID_ACTION_REF: ${ambiguousActionMessage(parsed.actionId, matches.map((m) => m.app.packageId))} (AMBIGUOUS_ACTION_REF)`);
217
+ err.code = "INVALID_ACTION_REF";
218
+ err.details = { alias: "AMBIGUOUS_ACTION_REF", candidates: matches.map((m) => m.app.packageId) };
219
+ throw err;
220
+ }
221
+ throw new Error(`ACTION_NOT_FOUND: Action '${parsed.actionId}' not found in any registered package`);
222
+ }
223
+ /**
224
+ * 解析宿主自动加载工程根目录:显式指定优先,其次当前进程工作目录探测。
225
+ */
226
+ export function resolveProjectRoot(options, detect, exists = existsSync) {
227
+ let root = options.projectRoot;
228
+ if (!root) {
229
+ const detected = detect();
230
+ if (detected) {
231
+ root = detected;
232
+ }
233
+ }
234
+ if (!root || !exists(root)) {
235
+ return null;
236
+ }
237
+ return root;
238
+ }
@@ -16,6 +16,12 @@ export interface ActionDockHostOptions {
16
16
  autoLoadCurrentProject?: boolean;
17
17
  /** 是否扫描已软链接的外部包(通过 listLinkedPackages,默认为 false) */
18
18
  scanLinkedPackages?: boolean;
19
+ /**
20
+ * 是否以数据目录持有者身份打开包存储:true 时内部创建的 App 存储在打开阶段
21
+ * 收割遗留非终态运行记录。默认 true(Host 本身即持有者);
22
+ * CLI 查询命令创建旁观 Host 时显式置 false,避免误收割并发 serve 进程的在途运行。
23
+ */
24
+ recoverOrphans?: boolean;
19
25
  /** 显式注入的运行时平台适配 */
20
26
  platform?: RuntimePlatform;
21
27
  /** 全局最大并发活跃运行数 */
@@ -44,6 +50,8 @@ export interface ActionDockHostOptions {
44
50
  * 作为多包环境下的全局协调中枢,负责多包生命周期、完全限定引用路由、依赖校验与配额管理。
45
51
  */
46
52
  export interface ActionDockHost {
53
+ /** 宿主容器初始化配置项(只读) */
54
+ readonly options?: ActionDockHostOptions;
47
55
  /** 获取所有已注册包的元数据信息列表 */
48
56
  info(): Promise<PackageInfo[]>;
49
57
  /** 静态列出所有已注册包中可用的 Action 摘要 */
package/dist/ipc/host.js CHANGED
@@ -1,4 +1,36 @@
1
+ import { TARGET_CAPABILITY_UNAVAILABLE } from "../target/types.js";
1
2
  import { hasIpcSignalMarker, IPC_SIGNAL_MARKER } from "./target.js";
3
+ /**
4
+ * IPC 通道允许反射调用的目标方法白名单。
5
+ *
6
+ * 严格对齐 ActionDockTarget 公共接口方法名集合:白名单之外的方法一律拒绝,
7
+ * 防止 IPC 消息通道触达任意内部属性或危险方法(如 close 之外的私有能力)。
8
+ */
9
+ const IPC_ALLOWED_METHODS = new Set([
10
+ "info",
11
+ "listPackages",
12
+ "listActions",
13
+ "describeAction",
14
+ "listPlaybooks",
15
+ "describePlaybook",
16
+ "runAction",
17
+ "startAction",
18
+ "listRuns",
19
+ "getRun",
20
+ "cancelRun",
21
+ "clearRuns",
22
+ "events",
23
+ "getConfig",
24
+ "setConfig",
25
+ "deleteConfig",
26
+ "listConfig",
27
+ "getState",
28
+ "setState",
29
+ "deleteState",
30
+ "listStateKeys",
31
+ "clearState",
32
+ "listStateEntries",
33
+ ]);
2
34
  /**
3
35
  * 在 Host 子进程中启动 Node IPC 服务,向父进程暴露 ActionDockTarget 门面能力。
4
36
  *
@@ -65,6 +97,12 @@ export async function serveParentIpc(target) {
65
97
  const callMsg = msg;
66
98
  const { id, method, args } = callMsg;
67
99
  try {
100
+ // 白名单校验:仅允许 ActionDockTarget 公共接口方法,阻止任意方法反射调用
101
+ if (typeof method !== "string" || !IPC_ALLOWED_METHODS.has(method)) {
102
+ const err = new Error(`Target method '${method}' is not allowed over IPC (not in ActionDockTarget public interface)`);
103
+ err.code = TARGET_CAPABILITY_UNAVAILABLE;
104
+ throw err;
105
+ }
68
106
  const fn = target[method];
69
107
  if (typeof fn !== "function") {
70
108
  throw new Error(`Target method '${method}' not found`);
@@ -53,6 +53,11 @@ export interface StorageFactoryOptions {
53
53
  dataDir?: string;
54
54
  inMemory?: boolean;
55
55
  customHome?: string;
56
+ /**
57
+ * 是否以数据目录持有者身份打开:true 时构造阶段收割死亡会话遗留的非终态运行记录。
58
+ * 默认 false(旁观查询打开,不触碰在途记录),供 CLI 查询命令与执行宿主并发共存。
59
+ */
60
+ recoverOrphans?: boolean;
56
61
  }
57
62
  /**
58
63
  * 跨运行时全局存储工厂配置选项。
@@ -61,6 +66,8 @@ export interface GlobalStorageFactoryOptions {
61
66
  dataDir?: string;
62
67
  inMemory?: boolean;
63
68
  customHome?: string;
69
+ /** 是否以持有者身份打开并收割遗留非终态运行记录(默认 false,旁观语义) */
70
+ recoverOrphans?: boolean;
64
71
  }
65
72
  /**
66
73
  * 跨运行时持久化存储工厂契约。
@@ -0,0 +1,107 @@
1
+ import type { ControlGrant } from "@actiondock/sdk";
2
+ import type { ManagedProcessRecord, ProcessOwner } from "./managed-record.js";
3
+ import type { StoredProcessRecord } from "./metadata-store.js";
4
+ /**
5
+ * 控制权仲裁器宿主协作回调集合。
6
+ *
7
+ * 持有者始终为 ProcessManager:仲裁器仅经回调透传副作用,不反向依赖管理器。
8
+ */
9
+ export interface ControlArbiterHost {
10
+ /** 控制权状态落盘(异步尽力而为) */
11
+ persistState(processId: string, patch: Partial<StoredProcessRecord>): unknown;
12
+ /** 排队路径授予成功时的凭据落盘(异步尽力而为) */
13
+ persistGrantReceipt(grant: ControlGrant, wait: {
14
+ requestId: string;
15
+ waitMs: number;
16
+ ttlMs: number;
17
+ }, proc: ManagedProcessRecord): unknown;
18
+ /** 控制权事件后的空闲定时器刷新(授予、续租与成功输入均触发) */
19
+ refreshIdleTimer(proc: ManagedProcessRecord): void;
20
+ /** 到期复核通过后的隔离处置(隔离而非释放) */
21
+ quarantineProcess(owner: ProcessOwner, processId: string, token?: string, reason?: string): unknown;
22
+ /** 宿主级等待者配额统计(含全部进程的排队等待者) */
23
+ countHostAcquireWaiters(): number;
24
+ /** 宿主等待者配额上限 */
25
+ readonly maxWaitersPerProcess: number;
26
+ /** 单进程等待者配额上限 */
27
+ readonly maxWaitersPerHost: number;
28
+ }
29
+ /**
30
+ * 控制权仲裁器。
31
+ *
32
+ * 单一职责:独占控制权状态机的全部推进路径——
33
+ * - 直接授予与 FIFO 等待队列(唯一唤醒)
34
+ * - TTL 定时器生命周期(设置、续租换新、清理)
35
+ * - 到期复核(复合控制态与凭据双重校验,转隔离而非释放)
36
+ * - 操作授权校验(write/control 提交前与调度复核共用)
37
+ * - 终态等待者单一拒绝入口
38
+ */
39
+ export declare class ControlArbiter {
40
+ private readonly host;
41
+ constructor(host: ControlArbiterHost);
42
+ /**
43
+ * 尝试立即授予控制权:控制权空闲且无等待者时直接授予,
44
+ * 否则按 FIFO 进入等待队列并返回等待 Promise。
45
+ *
46
+ * 调用方(原 acquire 请求)在排队路径 resolve 后负责预约结算;
47
+ * 唤醒方负责凭据落盘,保持两阶段时序不变。
48
+ */
49
+ tryAcquire(proc: ManagedProcessRecord, input: {
50
+ requestId: string;
51
+ waitMs: number;
52
+ ttlMs: number;
53
+ }, call?: {
54
+ signal?: AbortSignal;
55
+ }, runId?: string): {
56
+ granted: boolean;
57
+ waiter?: Promise<ControlGrant>;
58
+ };
59
+ /**
60
+ * FIFO 入队单个等待者:注册超时定时器、中止监听与自清理钩子。
61
+ */
62
+ private enqueueWaiter;
63
+ /**
64
+ * 授予指定受管进程控制令牌:递增控制纪元、登记凭据并设置 TTL 定时器。
65
+ */
66
+ grantControl(proc: ManagedProcessRecord, requestId: string, ttlMs: number, runId?: string): ControlGrant;
67
+ /**
68
+ * 续租当前有效控制令牌:先清理旧 TTL 定时器再设置新定时器,
69
+ * 刷新到期时刻并联动空闲定时器。
70
+ */
71
+ renewGrant(proc: ManagedProcessRecord, ttlMs: number): ControlGrant;
72
+ /**
73
+ * 显式释放控制令牌:清理 TTL 定时器、凭据置空并唤醒下一个等待者。
74
+ */
75
+ releaseGrant(proc: ManagedProcessRecord): void;
76
+ /**
77
+ * 唤醒排队等待控制权的下一个调用者(唯一唤醒入口):
78
+ * 授予令牌、由唤醒方落盘排队凭据并 resolve 原等待 Promise。
79
+ */
80
+ private wakeNextAcquireWaiter;
81
+ /**
82
+ * 控制权到期未释放时的复核:仍处于 held 且凭据有效时转隔离(隔离而非释放)。
83
+ */
84
+ handleGrantTtlExpired(proc: ManagedProcessRecord): void;
85
+ /**
86
+ * 操作提交前校验持有者令牌与授权(write/control 入队前与调度链路共用)。
87
+ */
88
+ validateOperation(proc: ManagedProcessRecord, token: string): void;
89
+ /**
90
+ * 校验当前持有凭据有效性(token 匹配且未过期),供 renew/release 提交前判定。
91
+ */
92
+ validateHeldGrant(proc: ManagedProcessRecord, token: string): void;
93
+ /**
94
+ * 终态等待者单一拒绝入口:按 FIFO 拒绝全部排队等待者。
95
+ *
96
+ * 终态转移(exit/error/stop/quarantine)必须经由此方法清空等待队列,
97
+ * 禁止直接操作 acquireWaiters 数组。
98
+ */
99
+ revokeAllWaiters(proc: ManagedProcessRecord, reason: {
100
+ code: string;
101
+ message: string;
102
+ }): void;
103
+ /**
104
+ * 清理进程全部控制权相关定时器与等待队列(stop/退出/故障路径统一入口)。
105
+ */
106
+ disposeProcess(proc: ManagedProcessRecord): void;
107
+ }