@actiondock/core 2.5.0 → 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 -215
  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
@@ -60,8 +60,9 @@ export function parseRegistryContent(raw, filePath) {
60
60
  const config = loadProjectConfig(link.path);
61
61
  packages[config.id] = buildLinkedPackageEntry(config, resolve(link.path), link.linkedAt || new Date().toISOString());
62
62
  }
63
- catch {
64
- // 迁移时项目已损坏或配置非法:跳过该条 link,避免迁移整体失败
63
+ catch (err) {
64
+ // 迁移时项目已损坏或配置非法:跳过该条 link 避免迁移整体失败,但必须输出告警,杜绝链接无声消失
65
+ console.warn(`[Registry] Skipping corrupted package link during migration: '${link.path}' (${err?.message || String(err)})`);
65
66
  }
66
67
  }
67
68
  }
@@ -259,8 +260,9 @@ export async function linkPackage(targetPath = process.cwd(), customHome, option
259
260
  registry.packages[config.id] = entry;
260
261
  linkedEntries.push(entry);
261
262
  }
262
- catch {
263
- // 子项目配置损坏:跳过该子项目条目,不影响 workspace 注册本身
263
+ catch (err) {
264
+ // 子项目配置损坏:跳过该子项目条目不影响 workspace 注册本身,但必须输出告警含包路径,杜绝链接无声消失
265
+ console.warn(`[Registry] Skipping corrupted workspace child package during link: '${root}' (${err?.message || String(err)})`);
264
266
  }
265
267
  }
266
268
  await writeRegistryLocked(registry, customHome);
@@ -179,7 +179,7 @@ export async function probeActionAsync(projectRoot, config, actionId) {
179
179
  return true;
180
180
  }
181
181
  try {
182
- const actions = await loadActions(projectRoot, config.actionsDir, { autoInstall: false });
182
+ const actions = await loadActions(projectRoot, config.actionsDir);
183
183
  return actions.has(actionId) ? true : undefined;
184
184
  }
185
185
  catch {
@@ -0,0 +1,45 @@
1
+ import type { ActionDefinition, ActionRef } from "@actiondock/sdk";
2
+ /**
3
+ * 为匿名传入的 Action 定义对象解析或分配稳定标识。
4
+ */
5
+ export declare function resolveAnonymousActionId(actions: Map<string, ActionDefinition>, action: ActionDefinition): string;
6
+ /**
7
+ * Action 注册表:持有 Action 标识到定义的映射并提供注册与检索能力。
8
+ */
9
+ export declare class ActionRegistry {
10
+ private actions;
11
+ constructor(actions?: Map<string, ActionDefinition>);
12
+ /** 底层映射表(调用方只读使用,写入仅经注册入口) */
13
+ get map(): Map<string, ActionDefinition>;
14
+ /**
15
+ * 注册单个 Action。
16
+ */
17
+ registerAction(id: string, action: ActionDefinition): void;
18
+ registerAction(action: ({
19
+ id: string;
20
+ action?: ActionDefinition;
21
+ } & Partial<ActionDefinition>) | ActionDefinition): void;
22
+ /**
23
+ * 根据 ID 检索注册的 Action。
24
+ */
25
+ getAction(id: string): ActionDefinition | undefined;
26
+ /**
27
+ * 检索全部已注册的 Action 列表。
28
+ */
29
+ listActions(): ActionDefinition[];
30
+ }
31
+ /**
32
+ * 本地注册表阶梯检索:跨包引用查 "pkg/action",本包引用依次查短标识与限定标识。
33
+ */
34
+ export declare function findLocalAction(actions: Map<string, ActionDefinition>, parsed: ActionRef, currentPackageId: string): ActionDefinition | undefined;
35
+ /**
36
+ * 判定传入对象是否为可执行的 Action 定义对象(含 run 函数)。
37
+ *
38
+ * 判定语义与原内联实现完全一致:null 入参同样在 `in` 运算处抛出 TypeError,
39
+ * 不额外吞异或静默改道,保持边界行为零变化。
40
+ */
41
+ export declare function isActionDefinitionObject(value: unknown): value is ActionDefinition;
42
+ /**
43
+ * 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
44
+ */
45
+ export declare function parseActionRef(ref: ActionRef | string): ActionRef;
@@ -0,0 +1,108 @@
1
+ import { ActionResolver } from "../catalog/action-resolver.js";
2
+ /**
3
+ * Action 注册表(单一职责模块)。
4
+ *
5
+ * 收敛 ActionRunner 原内联的纯注册逻辑:Action 注册与检索、匿名 Action 稳定标识
6
+ * 分配、以及本地注册表阶梯检索(跨包限定标识优先、本包短标识回退)。动态解析
7
+ * (自定义 resolver 委托与全局链接注册表按需加载)属于 Runner 编排职责,仍留在
8
+ * runner.ts 中,仅通过本注册表完成注册副作用。
9
+ */
10
+ /** 匿名传入 Action 对象的稳定标识分配表(同一对象跨多次调用复用同一标识) */
11
+ const anonymousActionIds = new WeakMap();
12
+ let anonymousActionCounter = 0;
13
+ /**
14
+ * 为匿名传入的 Action 定义对象解析或分配稳定标识。
15
+ */
16
+ export function resolveAnonymousActionId(actions, action) {
17
+ const actObj = action;
18
+ let foundId;
19
+ for (const [id, a] of actions) {
20
+ if (a === action) {
21
+ foundId = id;
22
+ break;
23
+ }
24
+ }
25
+ if (foundId) {
26
+ return foundId;
27
+ }
28
+ let anonId = anonymousActionIds.get(action);
29
+ if (!anonId) {
30
+ anonymousActionCounter++;
31
+ anonId = `anonymous-action-${anonymousActionCounter}`;
32
+ anonymousActionIds.set(action, anonId);
33
+ }
34
+ try {
35
+ actObj.id = anonId;
36
+ }
37
+ catch { }
38
+ return anonId;
39
+ }
40
+ /**
41
+ * Action 注册表:持有 Action 标识到定义的映射并提供注册与检索能力。
42
+ */
43
+ export class ActionRegistry {
44
+ actions;
45
+ constructor(actions) {
46
+ this.actions = actions || new Map();
47
+ }
48
+ /** 底层映射表(调用方只读使用,写入仅经注册入口) */
49
+ get map() {
50
+ return this.actions;
51
+ }
52
+ registerAction(idOrAction, actionDef) {
53
+ if (typeof idOrAction === "string") {
54
+ if (actionDef) {
55
+ this.actions.set(idOrAction, actionDef);
56
+ }
57
+ }
58
+ else {
59
+ const actObj = idOrAction;
60
+ const id = actObj.id || "anonymous-action";
61
+ const act = actObj.action || (actObj.run ? actObj : undefined);
62
+ if (act) {
63
+ this.actions.set(id, act);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * 根据 ID 检索注册的 Action。
69
+ */
70
+ getAction(id) {
71
+ return this.actions.get(id);
72
+ }
73
+ /**
74
+ * 检索全部已注册的 Action 列表。
75
+ */
76
+ listActions() {
77
+ return Array.from(this.actions.values());
78
+ }
79
+ }
80
+ /**
81
+ * 本地注册表阶梯检索:跨包引用查 "pkg/action",本包引用依次查短标识与限定标识。
82
+ */
83
+ export function findLocalAction(actions, parsed, currentPackageId) {
84
+ const targetActionId = parsed.actionId;
85
+ const targetPackageId = parsed.packageId;
86
+ if (targetPackageId && targetPackageId !== currentPackageId) {
87
+ return actions.get(`${targetPackageId}/${targetActionId}`);
88
+ }
89
+ return (actions.get(targetActionId) ||
90
+ (currentPackageId ? actions.get(`${currentPackageId}/${targetActionId}`) : undefined));
91
+ }
92
+ /**
93
+ * 判定传入对象是否为可执行的 Action 定义对象(含 run 函数)。
94
+ *
95
+ * 判定语义与原内联实现完全一致:null 入参同样在 `in` 运算处抛出 TypeError,
96
+ * 不额外吞异或静默改道,保持边界行为零变化。
97
+ */
98
+ export function isActionDefinitionObject(value) {
99
+ return (typeof value === "object" &&
100
+ "run" in value &&
101
+ typeof value.run === "function");
102
+ }
103
+ /**
104
+ * 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
105
+ */
106
+ export function parseActionRef(ref) {
107
+ return ActionResolver.parseRef(ref);
108
+ }
@@ -1,19 +1,7 @@
1
1
  /**
2
- * 统一时间与时钟接口。
2
+ * 时钟契约单一事实源已下沉至 storage/clock.ts(基础契约层)。
3
+ *
4
+ * 此处 re-export 维持 `runtime/clock` 与 `@actiondock/core` 根入口的既有
5
+ * 导入路径兼容,外部消费方无需变更。
3
6
  */
4
- export interface Clock {
5
- /** 获取当前系统墙上时间 */
6
- now(): Date;
7
- /** 获取单调递增时间戳(单位:毫秒) */
8
- monotonic(): number;
9
- /** 异步休眠指定毫秒 */
10
- sleep(ms: number): Promise<void>;
11
- }
12
- /**
13
- * 生产环境系统时钟实现。
14
- */
15
- export declare class SystemClock implements Clock {
16
- now(): Date;
17
- monotonic(): number;
18
- sleep(ms: number): Promise<void>;
19
- }
7
+ export { type Clock, SystemClock } from "../storage/clock.js";
@@ -1,17 +1,7 @@
1
1
  /**
2
- * 生产环境系统时钟实现。
2
+ * 时钟契约单一事实源已下沉至 storage/clock.ts(基础契约层)。
3
+ *
4
+ * 此处 re-export 维持 `runtime/clock` 与 `@actiondock/core` 根入口的既有
5
+ * 导入路径兼容,外部消费方无需变更。
3
6
  */
4
- export class SystemClock {
5
- now() {
6
- return new Date();
7
- }
8
- monotonic() {
9
- if (typeof performance !== "undefined" && typeof performance.now === "function") {
10
- return performance.now();
11
- }
12
- return Date.now();
13
- }
14
- sleep(ms) {
15
- return new Promise((resolve) => setTimeout(resolve, ms));
16
- }
17
- }
7
+ export { SystemClock } from "../storage/clock.js";
@@ -34,6 +34,9 @@ export declare class RuntimeConfig implements Config {
34
34
  }
35
35
  /**
36
36
  * 生产级状态存储实现,将状态持久化到 SQLite 中,完整支持命名空间分段隔离与 TTL。
37
+ *
38
+ * 读取严格限定当前命名空间(根作用域即空命名空间),不做跨命名空间隐式回扫;
39
+ * 跨命名空间模糊检索由 RuntimeStorage.findState 显式公共 API 承担。
37
40
  */
38
41
  export declare class RuntimeStateStore implements StateStore {
39
42
  private storage;
@@ -23,53 +23,17 @@ export class RuntimeConfig {
23
23
  this.globalStorage = globalStorage;
24
24
  }
25
25
  get(key, defaultValue) {
26
- // 1. CLI 临时参数覆盖
27
- if (this.overrides.has(key)) {
28
- return this.overrides.get(key);
29
- }
30
- // 2. 本地项目 SQLite 存储
31
- const stored = this.storage.getConfig(key);
32
- if (stored !== undefined) {
33
- return stored;
26
+ // 委托 describe 五层解析链唯一事实源:整链未命中(source 为 default 且无值)时回退调用方默认值
27
+ const resolved = this.describe(key);
28
+ if (resolved.source === "default" && resolved.value === undefined) {
29
+ return defaultValue;
34
30
  }
35
- // 3. 全局 SQLite 存储 (~/.actiondock/global.db)
36
- if (this.globalStorage) {
37
- try {
38
- const globalStored = this.globalStorage.getConfig(key);
39
- if (globalStored !== undefined) {
40
- return globalStored;
41
- }
42
- }
43
- catch {
44
- // 忽略全局存储读取异常
45
- }
46
- }
47
- // 4. 环境变量(支持包名前缀、SNAKE_CASE 转换与类型自动推断)
48
- const itemDef = this.projectConfig?.config?.[key];
49
- const envResolved = resolveEnvValue(key, itemDef, this.projectConfig?.id);
50
- if (envResolved !== undefined) {
51
- return envResolved.value;
52
- }
53
- // 5. 项目声明的默认值 (actiondock.json)
54
- if (itemDef?.default !== undefined) {
55
- return itemDef.default;
56
- }
57
- // 6. 调用方传入的代码级回退默认值
58
- return defaultValue;
31
+ return resolved.value;
59
32
  }
60
33
  has(key) {
61
- if (this.overrides.has(key))
62
- return true;
63
- if (this.storage.getConfig(key) !== undefined)
64
- return true;
65
- if (this.globalStorage?.getConfig(key) !== undefined)
66
- return true;
67
- const itemDef = this.projectConfig?.config?.[key];
68
- if (resolveEnvValue(key, itemDef, this.projectConfig?.id) !== undefined)
69
- return true;
70
- if (itemDef?.default !== undefined)
71
- return true;
72
- return false;
34
+ // 委托 describe 五层解析链唯一事实源:default 来源且无值即视为未配置
35
+ const resolved = this.describe(key);
36
+ return !(resolved.source === "default" && resolved.value === undefined);
73
37
  }
74
38
  /**
75
39
  * 结构化解析指定配置项的来源与取值视图。
@@ -87,7 +51,7 @@ export class RuntimeConfig {
87
51
  if (stored !== undefined) {
88
52
  return { source: "package", value: stored };
89
53
  }
90
- // 3. 全局持久化存储
54
+ // 3. 全局持久化存储(读取异常时降级继续解析链,但记录警告确保不静默吞没)
91
55
  if (this.globalStorage) {
92
56
  try {
93
57
  const globalStored = this.globalStorage.getConfig(key);
@@ -95,8 +59,8 @@ export class RuntimeConfig {
95
59
  return { source: "global", value: globalStored };
96
60
  }
97
61
  }
98
- catch {
99
- // 忽略全局存储读取异常
62
+ catch (err) {
63
+ console.warn(`[actiondock] global config read failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
100
64
  }
101
65
  }
102
66
  // 4. 环境变量
@@ -111,6 +75,9 @@ export class RuntimeConfig {
111
75
  }
112
76
  /**
113
77
  * 生产级状态存储实现,将状态持久化到 SQLite 中,完整支持命名空间分段隔离与 TTL。
78
+ *
79
+ * 读取严格限定当前命名空间(根作用域即空命名空间),不做跨命名空间隐式回扫;
80
+ * 跨命名空间模糊检索由 RuntimeStorage.findState 显式公共 API 承担。
114
81
  */
115
82
  export class RuntimeStateStore {
116
83
  storage;
@@ -120,15 +87,9 @@ export class RuntimeStateStore {
120
87
  this.namespace = namespace;
121
88
  }
122
89
  async get(key) {
123
- if (this.namespace) {
124
- return this.storage.getState(this.namespace, key);
125
- }
126
- const val = await this.storage.getState("", key);
127
- if (val !== undefined) {
128
- return val;
129
- }
130
- const found = await this.storage.findState(key);
131
- return found?.value;
90
+ // 读取严格限定当前命名空间(根作用域即空命名空间),不做跨命名空间隐式回扫;
91
+ // 跨命名空间检索需求由 RuntimeStorage.findState 显式公共 API 承担
92
+ return this.storage.getState(this.namespace, key);
132
93
  }
133
94
  async set(key, value, ttl) {
134
95
  return this.storage.setState(this.namespace, key, value, ttl);
@@ -1,5 +1,8 @@
1
1
  export * from "./context.js";
2
2
  export * from "./action-collection.js";
3
+ export * from "./action-registry.js";
4
+ export * from "./run-persistence.js";
5
+ export * from "./package-runner-factory.js";
3
6
  export * from "./runner.js";
4
7
  export * from "./execution-manager.js";
5
8
  export * from "./standalone.js";
@@ -1,5 +1,8 @@
1
1
  export * from "./context.js";
2
2
  export * from "./action-collection.js";
3
+ export * from "./action-registry.js";
4
+ export * from "./run-persistence.js";
5
+ export * from "./package-runner-factory.js";
3
6
  export * from "./runner.js";
4
7
  export * from "./execution-manager.js";
5
8
  export * from "./standalone.js";
@@ -0,0 +1,82 @@
1
+ import type { ProcessAPI } from "@actiondock/sdk";
2
+ import type { RuntimeStorage } from "../storage/types.js";
3
+ import type { RuntimePlatform } from "../platform/types.js";
4
+ import type { Clock } from "./clock.js";
5
+ import type { ActionRunner, PackageContextResolver, RunnerOptions } from "./runner.js";
6
+ /**
7
+ * 跨包 Runner 构建工厂参数(Runner 编排层注入的宿主执行环境快照)。
8
+ */
9
+ export interface PackageRunnerFactoryOptions {
10
+ /** 全局共享持久化存储实例(传递给子包 Runner 复用) */
11
+ globalStorage?: RuntimeStorage;
12
+ /** 项目根目录绝对路径 */
13
+ projectRoot?: string;
14
+ /** 外部注入的进程执行器 */
15
+ process?: ProcessAPI;
16
+ /** 可选的时间与时钟源 */
17
+ clock?: Clock;
18
+ /** 可选的底层运行时平台契约 */
19
+ platform?: RuntimePlatform;
20
+ /** 最大调用嵌套深度限制 */
21
+ maxCallDepth: number;
22
+ /** 最大并发子任务数限制 */
23
+ maxSubRuns: number;
24
+ /** 动态解析 Action 的委托函数 */
25
+ actionResolver?: RunnerOptions["actionResolver"];
26
+ /** 跨包存储工厂 */
27
+ getStorageForPackage?: (packageId: string, projectRoot?: string) => RuntimeStorage;
28
+ /** 跨包运行上下文解析委托函数 */
29
+ packageContextResolver?: PackageContextResolver;
30
+ /** 自定义 ActionDock 用户家目录 */
31
+ customHome?: string;
32
+ }
33
+ /**
34
+ * 跨包 Runner 构建工厂与缓存(单一职责模块)。
35
+ *
36
+ * 收敛 ActionRunner 原内联的跨包执行域:已解析子包 Runner 缓存、构建中 in-flight
37
+ * Promise 去重(消除并发 check-then-act 竞态)、本工厂直接创建的子包存储清单与
38
+ * dispose 级联释放。子 Runner 实例构造委托 ActionRunner 公共构造器,形成单向
39
+ * 依赖:factory -> runner,不产生环。
40
+ */
41
+ export declare class PackageRunnerFactory {
42
+ private readonly options;
43
+ private packageRunners;
44
+ /** 跨包 Runner 构建中的 in-flight Promise:并发调用 await 同一构建任务,消除 check-then-act 竞态 */
45
+ private pendingPackageRunners;
46
+ /** 本工厂直接为子包创建的存储连接(dispose 时级联关闭;外部注入存储不在此列) */
47
+ private packageStorages;
48
+ /** dispose 幂等守卫:并发调用复用同一次释放任务 */
49
+ private disposePromise;
50
+ /** 子包 Runner 构造委托(由 runner.ts 注入,避免本模块反向依赖 runner.ts) */
51
+ private readonly createRunner;
52
+ constructor(options: PackageRunnerFactoryOptions, createRunner: (options: RunnerOptions) => ActionRunner);
53
+ /**
54
+ * 已解析子包 Runner 缓存视图(调用方只读遍历使用)。
55
+ */
56
+ get runners(): Map<string, ActionRunner>;
57
+ /**
58
+ * 注入或更新跨包运行上下文解析委托。
59
+ */
60
+ setPackageContextResolver(resolver: PackageContextResolver): void;
61
+ /**
62
+ * 跨包运行时解析与获取(确保跨包执行具备独立的配置、存储、状态与 Action 注册表)。
63
+ *
64
+ * 缓存 miss 后存在长异步窗口(加载配置、扫描 Action、打开存储),并发调用会重复构建
65
+ * Runner、重复打开 SQLite 连接且子任务限流计数分裂;故以 in-flight Promise 去重,
66
+ * 并发方 await 同一构建任务,构建失败时移除该 Promise 以便后续重试。
67
+ */
68
+ resolveTargetPackageRunner(targetPackageId: string): Promise<ActionRunner | undefined>;
69
+ /**
70
+ * 实际构建跨包 Runner(仅由 resolveTargetPackageRunner 串行调度)。
71
+ */
72
+ private buildTargetPackageRunner;
73
+ /**
74
+ * 释放跨包资源:遍历关闭全部子包 Runner 及其独立创建的存储连接。
75
+ *
76
+ * 幂等且并发安全:重复调用直接复用首次 Promise;子包存储异常不阻断其余释放;
77
+ * 仅关闭由本工厂构建子包 Runner 时独立创建的存储,外部注入的存储(如经
78
+ * packageContextResolver 传入的目标包自身存储)生命周期归所有者管理,不在此误关。
79
+ */
80
+ dispose(): Promise<void>;
81
+ private disposeInternal;
82
+ }
@@ -0,0 +1,186 @@
1
+ import { existsSync } from "node:fs";
2
+ import { loadActions, loadProjectConfig } from "../project/loader.js";
3
+ import { resolvePackageRoot } from "../registry/registry.js";
4
+ /**
5
+ * 跨包 Runner 构建工厂与缓存(单一职责模块)。
6
+ *
7
+ * 收敛 ActionRunner 原内联的跨包执行域:已解析子包 Runner 缓存、构建中 in-flight
8
+ * Promise 去重(消除并发 check-then-act 竞态)、本工厂直接创建的子包存储清单与
9
+ * dispose 级联释放。子 Runner 实例构造委托 ActionRunner 公共构造器,形成单向
10
+ * 依赖:factory -> runner,不产生环。
11
+ */
12
+ export class PackageRunnerFactory {
13
+ options;
14
+ packageRunners = new Map();
15
+ /** 跨包 Runner 构建中的 in-flight Promise:并发调用 await 同一构建任务,消除 check-then-act 竞态 */
16
+ pendingPackageRunners = new Map();
17
+ /** 本工厂直接为子包创建的存储连接(dispose 时级联关闭;外部注入存储不在此列) */
18
+ packageStorages = new Set();
19
+ /** dispose 幂等守卫:并发调用复用同一次释放任务 */
20
+ disposePromise;
21
+ /** 子包 Runner 构造委托(由 runner.ts 注入,避免本模块反向依赖 runner.ts) */
22
+ createRunner;
23
+ constructor(options, createRunner) {
24
+ this.options = options;
25
+ this.createRunner = createRunner;
26
+ }
27
+ /**
28
+ * 已解析子包 Runner 缓存视图(调用方只读遍历使用)。
29
+ */
30
+ get runners() {
31
+ return this.packageRunners;
32
+ }
33
+ /**
34
+ * 注入或更新跨包运行上下文解析委托。
35
+ */
36
+ setPackageContextResolver(resolver) {
37
+ this.options.packageContextResolver = resolver;
38
+ }
39
+ /**
40
+ * 跨包运行时解析与获取(确保跨包执行具备独立的配置、存储、状态与 Action 注册表)。
41
+ *
42
+ * 缓存 miss 后存在长异步窗口(加载配置、扫描 Action、打开存储),并发调用会重复构建
43
+ * Runner、重复打开 SQLite 连接且子任务限流计数分裂;故以 in-flight Promise 去重,
44
+ * 并发方 await 同一构建任务,构建失败时移除该 Promise 以便后续重试。
45
+ */
46
+ async resolveTargetPackageRunner(targetPackageId) {
47
+ const cached = this.packageRunners.get(targetPackageId);
48
+ if (cached) {
49
+ return cached;
50
+ }
51
+ const inFlight = this.pendingPackageRunners.get(targetPackageId);
52
+ if (inFlight) {
53
+ return inFlight;
54
+ }
55
+ const building = this.buildTargetPackageRunner(targetPackageId).finally(() => {
56
+ // 无论成败均移除 in-flight 记录:成功者已写入正式缓存,失败者允许重试
57
+ this.pendingPackageRunners.delete(targetPackageId);
58
+ });
59
+ this.pendingPackageRunners.set(targetPackageId, building);
60
+ return building;
61
+ }
62
+ /**
63
+ * 实际构建跨包 Runner(仅由 resolveTargetPackageRunner 串行调度)。
64
+ */
65
+ async buildTargetPackageRunner(targetPackageId) {
66
+ const opts = this.options;
67
+ if (opts.packageContextResolver) {
68
+ const resolved = await opts.packageContextResolver(targetPackageId);
69
+ if (resolved) {
70
+ const runner = this.createRunner({
71
+ packageId: targetPackageId,
72
+ packageInstanceId: resolved.packageInstanceId || resolved.projectConfig?.packageInstanceId || targetPackageId,
73
+ generationId: resolved.generationId || resolved.projectConfig?.generationId || "1",
74
+ storage: resolved.storage,
75
+ globalStorage: opts.globalStorage,
76
+ projectRoot: resolved.projectRoot,
77
+ projectConfig: resolved.projectConfig,
78
+ actions: resolved.actions,
79
+ process: opts.process,
80
+ clock: opts.clock,
81
+ platform: opts.platform,
82
+ maxCallDepth: opts.maxCallDepth,
83
+ maxSubRuns: opts.maxSubRuns,
84
+ actionResolver: opts.actionResolver,
85
+ getStorageForPackage: opts.getStorageForPackage,
86
+ packageContextResolver: opts.packageContextResolver,
87
+ });
88
+ this.packageRunners.set(targetPackageId, runner);
89
+ return runner;
90
+ }
91
+ }
92
+ const root = resolvePackageRoot(targetPackageId, opts.projectRoot, opts.customHome);
93
+ if (root && existsSync(root)) {
94
+ const config = loadProjectConfig(root);
95
+ let storage;
96
+ let ownsPackageStorage = false;
97
+ if (opts.getStorageForPackage) {
98
+ storage = opts.getStorageForPackage(targetPackageId, root);
99
+ }
100
+ else if (opts.platform) {
101
+ storage = opts.platform.storage.createStorage(targetPackageId, {
102
+ projectRoot: root,
103
+ customHome: opts.customHome,
104
+ // 跨包子包存储由当前执行宿主持有,打开时收割遗留孤儿运行
105
+ recoverOrphans: true,
106
+ });
107
+ ownsPackageStorage = true;
108
+ }
109
+ else {
110
+ // 回退分支与主路径共用 createStorage 单一事实源,确保 run 记录落在统一解析的库文件
111
+ const { createStorage } = await import("../storage/index.js");
112
+ storage = createStorage(targetPackageId, { projectRoot: root, customHome: opts.customHome, recoverOrphans: true });
113
+ ownsPackageStorage = true;
114
+ }
115
+ // 由本工厂直接创建的子包存储纳入级联释放清单(getStorageForPackage 注入方自管理生命周期)
116
+ if (ownsPackageStorage) {
117
+ this.packageStorages.add(storage);
118
+ }
119
+ const actionsMap = await loadActions(root, config.actionsDir, {
120
+ loader: opts.platform?.modules,
121
+ });
122
+ const runner = this.createRunner({
123
+ packageId: targetPackageId,
124
+ packageInstanceId: config.packageInstanceId || targetPackageId,
125
+ generationId: config.generationId || "1",
126
+ storage,
127
+ globalStorage: opts.globalStorage,
128
+ projectRoot: root,
129
+ projectConfig: config,
130
+ actions: actionsMap,
131
+ process: opts.process,
132
+ clock: opts.clock,
133
+ platform: opts.platform,
134
+ maxCallDepth: opts.maxCallDepth,
135
+ maxSubRuns: opts.maxSubRuns,
136
+ actionResolver: opts.actionResolver,
137
+ getStorageForPackage: opts.getStorageForPackage,
138
+ packageContextResolver: opts.packageContextResolver,
139
+ customHome: opts.customHome,
140
+ });
141
+ this.packageRunners.set(targetPackageId, runner);
142
+ return runner;
143
+ }
144
+ return undefined;
145
+ }
146
+ /**
147
+ * 释放跨包资源:遍历关闭全部子包 Runner 及其独立创建的存储连接。
148
+ *
149
+ * 幂等且并发安全:重复调用直接复用首次 Promise;子包存储异常不阻断其余释放;
150
+ * 仅关闭由本工厂构建子包 Runner 时独立创建的存储,外部注入的存储(如经
151
+ * packageContextResolver 传入的目标包自身存储)生命周期归所有者管理,不在此误关。
152
+ */
153
+ async dispose() {
154
+ if (this.disposePromise) {
155
+ return this.disposePromise;
156
+ }
157
+ this.disposePromise = this.disposeInternal();
158
+ return this.disposePromise;
159
+ }
160
+ async disposeInternal() {
161
+ // 深度优先递归释放子包 Runner(子 Runner 可能又构建了孙 Runner)
162
+ const children = Array.from(this.packageRunners.values());
163
+ this.packageRunners.clear();
164
+ await Promise.all(children.map(async (child) => {
165
+ try {
166
+ await child.dispose();
167
+ }
168
+ catch {
169
+ // 单个子 Runner 释放异常不阻断其余释放
170
+ }
171
+ }));
172
+ // 关闭由本工厂直接创建的子包存储(buildTargetPackageRunner 回退分支)
173
+ for (const storage of this.packageStorages) {
174
+ try {
175
+ const res = storage.close();
176
+ if (res && typeof res.then === "function") {
177
+ await res;
178
+ }
179
+ }
180
+ catch {
181
+ // 存储重复关闭或已失效时忽略,保持释放链路继续
182
+ }
183
+ }
184
+ this.packageStorages.clear();
185
+ }
186
+ }