@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
package/dist/app/app.d.ts CHANGED
@@ -22,14 +22,19 @@ export declare class DefaultActionDockApp implements ActionDockApp {
22
22
  private readonly options;
23
23
  private runtimeConfig;
24
24
  private isClosed;
25
+ /** 静态清单索引缓存:包静态事实(根目录、配置、内存注入集合)在实例生命周期内不变,解析结果同实例内复用 */
26
+ private staticActionIndex?;
27
+ private staticPlaybookIndex?;
25
28
  constructor(options?: ActionDockAppOptions);
26
29
  /**
27
30
  * 静态读取并聚合当前包的 Action 规范索引。
28
- * 不产生全量模块导入与执行副作用。
31
+ * 聚合逻辑委托 static-index 单一事实源;同实例内复用首次解析结果,
32
+ * 消除重复查询单次调用内的重复读盘(磁盘清单在实例生命周期内视为静态事实)。
29
33
  */
30
34
  private getStaticActionMap;
31
35
  /**
32
36
  * 静态读取并聚合当前包的 Playbook 规范索引。
37
+ * 聚合逻辑委托 static-index 单一事实源;同实例内复用首次解析结果。
33
38
  */
34
39
  private getStaticPlaybookMap;
35
40
  info(options?: {
@@ -52,6 +57,14 @@ export declare class DefaultActionDockApp implements ActionDockApp {
52
57
  getConfig(key: string): Promise<ConfigValueView>;
53
58
  setConfig(key: string, value: JsonValue): Promise<void>;
54
59
  deleteConfig(key: string): Promise<boolean>;
60
+ /**
61
+ * 状态作用域统一解析辅助函数(单一事实源)。
62
+ *
63
+ * 完成重载消歧与 actionId 冲突校验,返回最终生效的命名空间:
64
+ * - actionId 存在时拼 接 `${actionId}:${namespace}` 或直接 actionId;
65
+ * - 无 actionId 时返回显式 namespace 或空字符串(包级扁平状态)。
66
+ */
67
+ private resolveStateScope;
55
68
  getState<T extends JsonValue = JsonValue>(key: string, options?: StateScopeOptions): Promise<T | undefined>;
56
69
  getState<T extends JsonValue = JsonValue>(actionId: string, key: string, options?: StateScopeOptions): Promise<T | undefined>;
57
70
  setState<T extends JsonValue = JsonValue>(key: string, value: T, options?: StateScopeOptions): Promise<void>;
package/dist/app/app.js CHANGED
@@ -1,14 +1,14 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { join, resolve } from "node:path";
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
3
  import { DefaultExecutionService } from "../execution/service.js";
4
4
  import { createDefaultPlatform } from "../platform/index.js";
5
- import { findProjectRoot, loadPlaybooks, loadProjectConfig } from "../project/loader.js";
6
- import { loadManifest } from "../project/manifest.js";
5
+ import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
7
6
  import { RuntimeConfig } from "../runtime/context.js";
8
7
  import { normalizeActionCollection } from "../runtime/action-collection.js";
9
8
  import { createGlobalStorage, createLazyStorage, createStorage } from "../storage/index.js";
10
9
  import { isSecretConfigKey, sanitizeConfigDefinitions } from "../storage/mask.js";
11
10
  import { decodeStateKey, SqliteRuntimeStorage } from "../storage/sqlite.js";
11
+ import { buildStaticActionMap, buildStaticPlaybookMap } from "./static-index.js";
12
12
  /**
13
13
  * ActionDock 统一应用默认实现。
14
14
  * 封装并管理单个 Action Package 的执行引擎、静态元数据索引、配置与状态存储生命周期。
@@ -27,6 +27,9 @@ export class DefaultActionDockApp {
27
27
  options;
28
28
  runtimeConfig;
29
29
  isClosed = false;
30
+ /** 静态清单索引缓存:包静态事实(根目录、配置、内存注入集合)在实例生命周期内不变,解析结果同实例内复用 */
31
+ staticActionIndex;
32
+ staticPlaybookIndex;
30
33
  constructor(options = {}) {
31
34
  this.options = options;
32
35
  // 1. 确定项目根路径与配置对象
@@ -41,12 +44,9 @@ export class DefaultActionDockApp {
41
44
  if (!projectConfig && packageRoot) {
42
45
  const configPath = join(packageRoot, "actiondock.json");
43
46
  if (existsSync(configPath)) {
44
- try {
45
- projectConfig = loadProjectConfig(packageRoot);
46
- }
47
- catch {
48
- // 忽略解析失败,使用回退配置
49
- }
47
+ // 文件存在但解析失败(损坏 JSON、校验失败)时直接向上抛出:
48
+ // 仅当确实不存在 actiondock.json 文件时才允许回退默认配置,杜绝幽灵包配置
49
+ projectConfig = loadProjectConfig(packageRoot);
50
50
  }
51
51
  }
52
52
  if (!projectConfig) {
@@ -75,6 +75,9 @@ export class DefaultActionDockApp {
75
75
  dataDir: options.dataDir,
76
76
  customHome: options.customHome,
77
77
  inMemory: options.inMemory,
78
+ // 默认持有者语义:App 主路径打开时收割死亡会话遗留非终态运行记录;
79
+ // CLI 查询旁观视图显式置 recoverOrphans: false 跳过收割
80
+ recoverOrphans: options.recoverOrphans !== false,
78
81
  };
79
82
  const storageFactory = this.platform.storage;
80
83
  const initStorage = () => {
@@ -113,6 +116,8 @@ export class DefaultActionDockApp {
113
116
  customHome: options.customHome,
114
117
  dataDir: options.dataDir,
115
118
  inMemory: options.inMemory,
119
+ // 与主存储保持同侧收割语义(全局库 runs 表为空集,收割无实际副作用)
120
+ recoverOrphans: options.recoverOrphans !== false,
116
121
  };
117
122
  const storageFactory = this.platform.storage;
118
123
  const initGlobalStorage = () => {
@@ -153,135 +158,33 @@ export class DefaultActionDockApp {
153
158
  }
154
159
  /**
155
160
  * 静态读取并聚合当前包的 Action 规范索引。
156
- * 不产生全量模块导入与执行副作用。
161
+ * 聚合逻辑委托 static-index 单一事实源;同实例内复用首次解析结果,
162
+ * 消除重复查询单次调用内的重复读盘(磁盘清单在实例生命周期内视为静态事实)。
157
163
  */
158
164
  getStaticActionMap() {
159
- const map = new Map();
160
- // 1. 读取声明式清单文件 (actiondock.json)
161
- if (this.packageRoot) {
162
- try {
163
- const manifest = loadManifest(this.packageRoot);
164
- if (manifest?.actions) {
165
- for (const [id, item] of Object.entries(manifest.actions)) {
166
- map.set(id, {
167
- id,
168
- packageId: this.packageId,
169
- description: item.description,
170
- inputSchema: item.inputSchema,
171
- outputSchema: item.outputSchema,
172
- tags: item.tags ? [...item.tags] : [],
173
- annotations: item.annotations,
174
- uses: item.uses ? [...item.uses] : [],
175
- entry: item.entry,
176
- filePath: item.entry ? resolve(this.packageRoot, item.entry) : undefined,
177
- });
178
- }
179
- }
180
- }
181
- catch {
182
- // 忽略清单缺失或解析异常
183
- }
184
- }
185
- // 2. 读取项目配置文件中声明的 actions (Manifest v2 格式)
186
- if (this.projectConfig && this.projectConfig.actions) {
187
- const rawActions = this.projectConfig.actions;
188
- if (typeof rawActions === "object" && rawActions !== null) {
189
- for (const [id, item] of Object.entries(rawActions)) {
190
- const existing = map.get(id);
191
- map.set(id, {
192
- id,
193
- packageId: this.packageId,
194
- description: item.description ?? existing?.description,
195
- inputSchema: item.inputSchema ?? existing?.inputSchema,
196
- outputSchema: item.outputSchema ?? existing?.outputSchema,
197
- tags: item.tags ?? existing?.tags,
198
- annotations: item.annotations ?? existing?.annotations,
199
- uses: item.uses ?? existing?.uses,
200
- entry: item.entry ?? existing?.entry,
201
- filePath: item.entry && this.packageRoot
202
- ? resolve(this.packageRoot, item.entry)
203
- : existing?.filePath,
204
- });
205
- }
206
- }
207
- }
208
- // 3. 读取内存显式注入的 Action 定义
209
- for (const [id, act] of this.actionsMap) {
210
- const existing = map.get(id);
211
- const actObj = act;
212
- map.set(id, {
213
- id,
165
+ if (!this.staticActionIndex) {
166
+ this.staticActionIndex = buildStaticActionMap({
167
+ packageRoot: this.packageRoot,
214
168
  packageId: this.packageId,
215
- description: actObj.description ?? existing?.description,
216
- inputSchema: actObj.inputSchema ?? existing?.inputSchema,
217
- outputSchema: actObj.outputSchema ?? existing?.outputSchema,
218
- tags: actObj.tags ? [...actObj.tags] : existing?.tags,
219
- annotations: actObj.annotations ?? existing?.annotations,
220
- uses: actObj.uses ? [...actObj.uses] : existing?.uses,
221
- entry: existing?.entry,
222
- filePath: existing?.filePath,
169
+ projectConfig: this.projectConfig,
170
+ actionsMap: this.actionsMap,
223
171
  });
224
172
  }
225
- return map;
173
+ return this.staticActionIndex;
226
174
  }
227
175
  /**
228
176
  * 静态读取并聚合当前包的 Playbook 规范索引。
177
+ * 聚合逻辑委托 static-index 单一事实源;同实例内复用首次解析结果。
229
178
  */
230
179
  getStaticPlaybookMap() {
231
- const map = new Map();
232
- // 1. 扫描磁盘规程文件
233
- if (this.packageRoot) {
234
- const playbooksDir = this.projectConfig?.playbooksDir || "playbooks";
235
- const dirPath = join(this.packageRoot, playbooksDir);
236
- if (existsSync(dirPath)) {
237
- try {
238
- const loaded = loadPlaybooks(this.packageRoot, playbooksDir);
239
- for (const [id, def] of loaded) {
240
- map.set(id, {
241
- id: def.id,
242
- packageId: this.packageId,
243
- description: def.description,
244
- actions: def.actions,
245
- content: def.content,
246
- filePath: def.filePath,
247
- });
248
- }
249
- }
250
- catch {
251
- // 忽略规程加载异常
252
- }
253
- }
254
- }
255
- // 2. 读取项目配置文件中的 playbooks
256
- if (this.projectConfig && this.projectConfig.playbooks) {
257
- const rawPlaybooks = this.projectConfig.playbooks;
258
- if (typeof rawPlaybooks === "object" && rawPlaybooks !== null) {
259
- for (const [id, item] of Object.entries(rawPlaybooks)) {
260
- const existing = map.get(id);
261
- let content = item.content ?? existing?.content ?? "";
262
- let filePath = item.entry && this.packageRoot
263
- ? resolve(this.packageRoot, item.entry)
264
- : existing?.filePath;
265
- if (!content && filePath && existsSync(filePath)) {
266
- try {
267
- content = readFileSync(filePath, "utf-8");
268
- }
269
- catch {
270
- // 忽略读取异常
271
- }
272
- }
273
- map.set(id, {
274
- id,
275
- packageId: this.packageId,
276
- description: item.description ?? existing?.description,
277
- actions: item.actions ?? existing?.actions,
278
- content,
279
- filePath,
280
- });
281
- }
282
- }
180
+ if (!this.staticPlaybookIndex) {
181
+ this.staticPlaybookIndex = buildStaticPlaybookMap({
182
+ packageRoot: this.packageRoot,
183
+ packageId: this.packageId,
184
+ projectConfig: this.projectConfig,
185
+ });
283
186
  }
284
- return map;
187
+ return this.staticPlaybookIndex;
285
188
  }
286
189
  async info(options) {
287
190
  const actionsMap = this.getStaticActionMap();
@@ -477,36 +380,47 @@ export class DefaultActionDockApp {
477
380
  async deleteConfig(key) {
478
381
  return await this.storage.deleteConfig(key);
479
382
  }
480
- async getState(actionIdOrKey, keyOrOptions, options) {
383
+ /**
384
+ * 状态作用域统一解析辅助函数(单一事实源)。
385
+ *
386
+ * 完成重载消歧与 actionId 冲突校验,返回最终生效的命名空间:
387
+ * - actionId 存在时拼 接 `${actionId}:${namespace}` 或直接 actionId;
388
+ * - 无 actionId 时返回显式 namespace 或空字符串(包级扁平状态)。
389
+ */
390
+ resolveStateScope(actionIdOrKey, keyOrOptions, options) {
481
391
  let actionId;
482
- let key;
483
- let opts;
392
+ let resolvedOpts;
393
+ // 消歧规则(与原始重载语义严格一致):仅当第二参为字符串时认定为
394
+ // (actionId, key, options) 形态;否则(undefined 或选项对象)认定为
395
+ // (key, options) 扁平调用形态,首参是状态键而非 actionId
484
396
  if (typeof keyOrOptions === "string") {
485
397
  actionId = actionIdOrKey;
486
- key = keyOrOptions;
487
- opts = options;
488
- if (opts?.actionId && opts.actionId !== actionId) {
489
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
398
+ resolvedOpts = options;
399
+ if (resolvedOpts?.actionId && resolvedOpts.actionId !== actionId) {
400
+ throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${resolvedOpts.actionId}'`);
490
401
  }
491
402
  }
492
403
  else {
404
+ // (key, options) 扁平调用:首参是状态键,无位置 actionId;
405
+ // options.actionId 属于合法的显式指定,直接采纳而非判为冲突
493
406
  actionId = "";
494
- key = actionIdOrKey;
495
- opts = keyOrOptions;
407
+ resolvedOpts = keyOrOptions;
496
408
  }
497
- if (!actionId && opts?.actionId) {
498
- actionId = opts.actionId;
409
+ if (!actionId && resolvedOpts?.actionId) {
410
+ actionId = resolvedOpts.actionId;
499
411
  }
500
412
  const ns = actionId
501
- ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
502
- : (opts?.namespace ?? "");
413
+ ? (resolvedOpts?.namespace ? `${actionId}:${resolvedOpts.namespace}` : actionId)
414
+ : (resolvedOpts?.namespace ?? "");
415
+ return { ns, opts: resolvedOpts };
416
+ }
417
+ async getState(actionIdOrKey, keyOrOptions, options) {
418
+ const { ns, opts } = this.resolveStateScope(actionIdOrKey, keyOrOptions, options);
419
+ const key = typeof keyOrOptions === "string" ? keyOrOptions : actionIdOrKey;
503
420
  if (opts?.detail) {
504
421
  const entry = await this.storage.findState(key, ns || undefined);
505
422
  return entry;
506
423
  }
507
- if (actionId) {
508
- return await this.storage.getState(ns, key);
509
- }
510
424
  if (ns) {
511
425
  return await this.storage.getState(ns, key);
512
426
  }
@@ -514,10 +428,10 @@ export class DefaultActionDockApp {
514
428
  return entry?.value;
515
429
  }
516
430
  async setState(actionIdOrKey, keyOrValue, valueOrOptions, options) {
517
- let actionId;
518
431
  let key;
519
432
  let value;
520
433
  let opts;
434
+ let actionId;
521
435
  if (arguments.length >= 4) {
522
436
  if (options?.actionId && options.actionId !== actionIdOrKey) {
523
437
  throw new Error(`Conflicting actionId specified: positional '${actionIdOrKey}' vs options.actionId '${options.actionId}'`);
@@ -563,28 +477,8 @@ export class DefaultActionDockApp {
563
477
  await this.storage.setState(targetNs, targetKey, value, opts?.ttl);
564
478
  }
565
479
  async deleteState(actionIdOrKey, keyOrOptions, options) {
566
- let actionId;
567
- let key;
568
- let opts;
569
- if (typeof keyOrOptions === "string") {
570
- actionId = actionIdOrKey;
571
- key = keyOrOptions;
572
- opts = options;
573
- if (opts?.actionId && opts.actionId !== actionId) {
574
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
575
- }
576
- }
577
- else {
578
- actionId = "";
579
- key = actionIdOrKey;
580
- opts = keyOrOptions;
581
- }
582
- if (!actionId && opts?.actionId) {
583
- actionId = opts.actionId;
584
- }
585
- const ns = actionId
586
- ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
587
- : (opts?.namespace ?? "");
480
+ const { ns } = this.resolveStateScope(actionIdOrKey, keyOrOptions, options);
481
+ const key = typeof keyOrOptions === "string" ? keyOrOptions : actionIdOrKey;
588
482
  if (ns) {
589
483
  return await this.storage.deleteState(ns, key);
590
484
  }
@@ -601,74 +495,53 @@ export class DefaultActionDockApp {
601
495
  return await this.storage.deleteState(targetNs, targetKey);
602
496
  }
603
497
  async getActionState(actionId, key, options) {
604
- if (options?.actionId && options.actionId !== actionId) {
605
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
606
- }
607
- const ns = options?.namespace ? `${actionId}:${options.namespace}` : actionId;
608
- if (options?.detail) {
498
+ const { ns, opts } = this.resolveStateScope(actionId, key, options);
499
+ if (opts?.detail) {
609
500
  const entry = await this.storage.findState(key, ns || undefined);
610
501
  return entry;
611
502
  }
612
503
  return await this.storage.getState(ns, key);
613
504
  }
614
505
  async setActionState(actionId, key, value, options) {
615
- if (options?.actionId && options.actionId !== actionId) {
616
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
617
- }
618
- const ns = options?.namespace ? `${actionId}:${options.namespace}` : actionId;
619
- await this.storage.setState(ns, key, value, options?.ttl);
506
+ const { ns, opts } = this.resolveStateScope(actionId, key, options);
507
+ await this.storage.setState(ns, key, value, opts?.ttl);
620
508
  }
621
509
  async deleteActionState(actionId, key, options) {
622
- if (options?.actionId && options.actionId !== actionId) {
623
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${options.actionId}'`);
624
- }
625
- const ns = options?.namespace ? `${actionId}:${options.namespace}` : actionId;
510
+ const { ns } = this.resolveStateScope(actionId, key, options);
626
511
  return await this.storage.deleteState(ns, key);
627
512
  }
628
513
  async listStateKeys(actionIdOrOptions, options) {
629
- let actionId;
630
- let opts;
514
+ // 重载消歧:首参为字符串时是 (actionId, options) 调用(完成冲突校验后取其域);
515
+ // 否则首参本身就是选项对象(或未传),取 options.actionId 或 namespace 扁平域
516
+ let ns;
631
517
  if (typeof actionIdOrOptions === "string") {
632
- actionId = actionIdOrOptions;
633
- opts = options;
634
- if (opts?.actionId && opts.actionId !== actionId) {
635
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
636
- }
518
+ ns = this.resolveStateScope(actionIdOrOptions, "", options).ns;
637
519
  }
638
520
  else {
639
- actionId = "";
640
- opts = actionIdOrOptions;
521
+ const opts = actionIdOrOptions;
522
+ ns = opts?.actionId
523
+ ? (opts.namespace ? `${opts.actionId}:${opts.namespace}` : opts.actionId)
524
+ : (opts?.namespace ?? "");
641
525
  }
642
- if (!actionId && opts?.actionId) {
643
- actionId = opts.actionId;
644
- }
645
- const ns = actionId
646
- ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
647
- : (opts?.namespace ?? null);
648
- return this.storage.listStateKeys(ns, opts?.prefix);
526
+ const opts = typeof actionIdOrOptions === "string" ? options : actionIdOrOptions;
527
+ return this.storage.listStateKeys(ns ? ns : null, opts?.prefix);
649
528
  }
650
529
  async clearState(actionIdOrOptions, options) {
651
- let actionId;
652
- let opts;
530
+ // 首参为字符串时是 (actionId, options) 调用:以伪 key 消费冲突校验后取其 actionId 域;
531
+ // 否则首参本身就是选项对象(或未传),直接走扁平分支
653
532
  if (typeof actionIdOrOptions === "string") {
654
- actionId = actionIdOrOptions;
655
- opts = options;
656
- if (opts?.actionId && opts.actionId !== actionId) {
657
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
658
- }
659
- }
660
- else {
661
- actionId = "";
662
- opts = actionIdOrOptions;
663
- }
664
- if (!actionId && opts?.actionId) {
665
- actionId = opts.actionId;
533
+ const { ns } = this.resolveStateScope(actionIdOrOptions, "", options);
534
+ const finalNs = ns || (options?.namespace ?? "");
535
+ return this.storage.clearState({
536
+ namespace: finalNs ? finalNs : undefined,
537
+ prefix: options?.prefix,
538
+ all: options?.all,
539
+ });
666
540
  }
667
- const ns = actionId
668
- ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
669
- : (opts?.namespace ?? undefined);
541
+ const opts = actionIdOrOptions;
542
+ const ns = opts?.namespace;
670
543
  return this.storage.clearState({
671
- namespace: ns,
544
+ namespace: ns ? ns : undefined,
672
545
  prefix: opts?.prefix,
673
546
  all: opts?.all,
674
547
  });
@@ -0,0 +1,39 @@
1
+ import type { ActionDefinition } from "@actiondock/sdk";
2
+ import type { ProjectConfig } from "../project/types.js";
3
+ import type { ActionSpec, PlaybookSpec } from "./types.js";
4
+ /**
5
+ * 静态清单索引构建入参:同一包的全部静态事实来源。
6
+ * 入参不可变(同实例内复用解析结果的前提),字段与 App 实例的运行时只读状态一一对应。
7
+ */
8
+ export interface StaticIndexInput {
9
+ /** 包根目录(磁盘清单来源;缺省时仅聚合配置与内存注入来源) */
10
+ packageRoot?: string;
11
+ /** 包唯一标识 */
12
+ packageId: string;
13
+ /** 项目配置(Manifest v2 声明来源) */
14
+ projectConfig: ProjectConfig;
15
+ }
16
+ /** Action 索引构建入参:静态事实来源之上叠加内存注入的 Action 定义集合 */
17
+ export interface StaticActionIndexInput extends StaticIndexInput {
18
+ /** 内存显式注入的 Action 定义集合 */
19
+ actionsMap: ReadonlyMap<string, ActionDefinition>;
20
+ }
21
+ /**
22
+ * 静态读取并聚合指定包的 Action 规范索引。
23
+ * 不产生全量模块导入与执行副作用。
24
+ *
25
+ * 聚合优先级(后者覆盖前者同名字段,缺省字段回退前者):
26
+ * - 磁盘声明式清单文件(actiondock.json);
27
+ * - 项目配置中声明的 actions(Manifest v2 格式);
28
+ * - 内存显式注入的 Action 定义。
29
+ *
30
+ * 清单文件不存在属于合法空态;解析失败(损坏 JSON 等)输出告警并跳过清单部分。
31
+ */
32
+ export declare function buildStaticActionMap(input: StaticActionIndexInput): Map<string, ActionSpec>;
33
+ /**
34
+ * 静态读取并聚合指定包的 Playbook 规范索引。
35
+ *
36
+ * 聚合来源:磁盘规程目录扫描(以 actiondock.json 声明为事实源)与
37
+ * 项目配置中声明的 playbooks;同键时配置声明覆盖磁盘扫描结果。
38
+ */
39
+ export declare function buildStaticPlaybookMap(input: StaticIndexInput): Map<string, PlaybookSpec>;
@@ -0,0 +1,164 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { loadPlaybooks } from "../project/loader.js";
4
+ import { loadManifest, MANIFEST_FILE_NAME } from "../project/manifest.js";
5
+ /**
6
+ * 静态读取并聚合指定包的 Action 规范索引。
7
+ * 不产生全量模块导入与执行副作用。
8
+ *
9
+ * 聚合优先级(后者覆盖前者同名字段,缺省字段回退前者):
10
+ * - 磁盘声明式清单文件(actiondock.json);
11
+ * - 项目配置中声明的 actions(Manifest v2 格式);
12
+ * - 内存显式注入的 Action 定义。
13
+ *
14
+ * 清单文件不存在属于合法空态;解析失败(损坏 JSON 等)输出告警并跳过清单部分。
15
+ */
16
+ export function buildStaticActionMap(input) {
17
+ const { packageRoot, packageId, projectConfig, actionsMap } = input;
18
+ const map = new Map();
19
+ // 读取声明式清单文件 (actiondock.json)
20
+ if (packageRoot) {
21
+ const manifestPath = join(packageRoot, MANIFEST_FILE_NAME);
22
+ // 文件不存在属于合法空态(无清单包);解析失败(损坏 JSON 等)则输出告警并跳过清单部分
23
+ if (!existsSync(manifestPath)) {
24
+ // 合法空态:无清单文件,仅依赖后续配置与内存注入来源
25
+ }
26
+ else {
27
+ try {
28
+ const manifest = loadManifest(packageRoot);
29
+ if (manifest?.actions) {
30
+ for (const [id, item] of Object.entries(manifest.actions)) {
31
+ map.set(id, {
32
+ id,
33
+ packageId,
34
+ description: item.description,
35
+ inputSchema: item.inputSchema,
36
+ outputSchema: item.outputSchema,
37
+ tags: item.tags ? [...item.tags] : [],
38
+ annotations: item.annotations,
39
+ uses: item.uses ? [...item.uses] : [],
40
+ entry: item.entry,
41
+ filePath: item.entry ? resolve(packageRoot, item.entry) : undefined,
42
+ });
43
+ }
44
+ }
45
+ }
46
+ catch (err) {
47
+ console.warn(`[App] Failed to load manifest for package '${packageId}' from '${manifestPath}': ${err?.message || String(err)}`);
48
+ }
49
+ }
50
+ }
51
+ // 读取项目配置文件中声明的 actions (Manifest v2 格式)
52
+ if (projectConfig && projectConfig.actions) {
53
+ const rawActions = projectConfig.actions;
54
+ if (typeof rawActions === "object" && rawActions !== null) {
55
+ for (const [id, item] of Object.entries(rawActions)) {
56
+ const existing = map.get(id);
57
+ map.set(id, {
58
+ id,
59
+ packageId,
60
+ description: item.description ?? existing?.description,
61
+ inputSchema: item.inputSchema ?? existing?.inputSchema,
62
+ outputSchema: item.outputSchema ?? existing?.outputSchema,
63
+ tags: item.tags ?? existing?.tags,
64
+ annotations: item.annotations ?? existing?.annotations,
65
+ uses: item.uses ?? existing?.uses,
66
+ entry: item.entry ?? existing?.entry,
67
+ filePath: item.entry && packageRoot
68
+ ? resolve(packageRoot, item.entry)
69
+ : existing?.filePath,
70
+ });
71
+ }
72
+ }
73
+ }
74
+ // 读取内存显式注入的 Action 定义
75
+ for (const [id, act] of actionsMap) {
76
+ // 若为当前包完全限定名别名(例如 "pkgId/actionId"),跳过以防与短名 action 重复
77
+ if (id.startsWith(`${packageId}/`)) {
78
+ const shortId = id.slice(packageId.length + 1);
79
+ if (actionsMap.has(shortId) || map.has(shortId)) {
80
+ continue;
81
+ }
82
+ }
83
+ const existing = map.get(id);
84
+ const actObj = act;
85
+ map.set(id, {
86
+ id,
87
+ packageId,
88
+ description: actObj.description ?? existing?.description,
89
+ inputSchema: actObj.inputSchema ?? existing?.inputSchema,
90
+ outputSchema: actObj.outputSchema ?? existing?.outputSchema,
91
+ tags: actObj.tags ? [...actObj.tags] : existing?.tags,
92
+ annotations: actObj.annotations ?? existing?.annotations,
93
+ uses: actObj.uses ? [...actObj.uses] : existing?.uses,
94
+ entry: existing?.entry,
95
+ filePath: existing?.filePath,
96
+ });
97
+ }
98
+ return map;
99
+ }
100
+ /**
101
+ * 静态读取并聚合指定包的 Playbook 规范索引。
102
+ *
103
+ * 聚合来源:磁盘规程目录扫描(以 actiondock.json 声明为事实源)与
104
+ * 项目配置中声明的 playbooks;同键时配置声明覆盖磁盘扫描结果。
105
+ */
106
+ export function buildStaticPlaybookMap(input) {
107
+ const { packageRoot, packageId, projectConfig } = input;
108
+ const map = new Map();
109
+ // 扫描磁盘规程文件
110
+ if (packageRoot) {
111
+ const playbooksDir = projectConfig?.playbooksDir || "playbooks";
112
+ const dirPath = join(packageRoot, playbooksDir);
113
+ if (existsSync(dirPath)) {
114
+ try {
115
+ const loaded = loadPlaybooks(packageRoot, playbooksDir);
116
+ for (const [id, def] of loaded) {
117
+ map.set(id, {
118
+ id: def.id,
119
+ packageId,
120
+ description: def.description,
121
+ actions: def.actions,
122
+ content: def.content,
123
+ filePath: def.filePath,
124
+ });
125
+ }
126
+ }
127
+ catch (err) {
128
+ // 规程加载失败(清单声明非法、规程文件损坏等)输出告警并跳过磁盘部分,保持返回可用列表
129
+ console.warn(`[App] Failed to load playbooks for package '${packageId}' from '${dirPath}': ${err?.message || String(err)}`);
130
+ }
131
+ }
132
+ }
133
+ // 读取项目配置文件中的 playbooks
134
+ if (projectConfig && projectConfig.playbooks) {
135
+ const rawPlaybooks = projectConfig.playbooks;
136
+ if (typeof rawPlaybooks === "object" && rawPlaybooks !== null) {
137
+ for (const [id, item] of Object.entries(rawPlaybooks)) {
138
+ const existing = map.get(id);
139
+ let content = item.content ?? existing?.content ?? "";
140
+ let filePath = item.entry && packageRoot
141
+ ? resolve(packageRoot, item.entry)
142
+ : existing?.filePath;
143
+ if (!content && filePath && existsSync(filePath)) {
144
+ try {
145
+ content = readFileSync(filePath, "utf-8");
146
+ }
147
+ catch (err) {
148
+ // 读取失败(权限、IO 错误等)输出告警并保持空正文,不再无声吞没
149
+ console.warn(`[App] Failed to read playbook '${id}' content from '${filePath}' in package '${packageId}': ${err?.message || String(err)}`);
150
+ }
151
+ }
152
+ map.set(id, {
153
+ id,
154
+ packageId,
155
+ description: item.description ?? existing?.description,
156
+ actions: item.actions ?? existing?.actions,
157
+ content,
158
+ filePath,
159
+ });
160
+ }
161
+ }
162
+ }
163
+ return map;
164
+ }