@actiondock/core 2.13.0 → 2.15.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 (40) hide show
  1. package/dist/catalog/discovery.d.ts +3 -3
  2. package/dist/catalog/graph.d.ts +6 -8
  3. package/dist/catalog/graph.js +197 -62
  4. package/dist/catalog/resolve-playbook.d.ts +2 -2
  5. package/dist/catalog/resolve-playbook.js +12 -53
  6. package/dist/execution/types.d.ts +2 -2
  7. package/dist/host/routing.d.ts +1 -1
  8. package/dist/host/routing.js +1 -1
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.js +1 -1
  11. package/dist/package/runtime.d.ts +2 -2
  12. package/dist/package/runtime.js +3 -1
  13. package/dist/package/static-index.d.ts +4 -6
  14. package/dist/package/static-index.js +12 -60
  15. package/dist/package/types.d.ts +5 -5
  16. package/dist/project/index.d.ts +1 -0
  17. package/dist/project/index.js +1 -0
  18. package/dist/project/init.js +2 -2
  19. package/dist/project/loader.d.ts +4 -2
  20. package/dist/project/loader.js +38 -34
  21. package/dist/project/manifest.d.ts +2 -0
  22. package/dist/project/manifest.js +17 -1
  23. package/dist/project/skill-dir.d.ts +72 -0
  24. package/dist/project/skill-dir.js +244 -0
  25. package/dist/project/types.d.ts +31 -0
  26. package/dist/registry/registry.js +27 -15
  27. package/dist/runtime/context.d.ts +3 -3
  28. package/dist/runtime/standalone-commands.js +8 -2
  29. package/dist/runtime/standalone.d.ts +2 -0
  30. package/dist/storage/sqlite-driver.d.ts +0 -1
  31. package/dist/storage/sqlite-driver.js +0 -1
  32. package/dist/utils/index.d.ts +0 -1
  33. package/dist/utils/index.js +0 -1
  34. package/dist/utils/semver.d.ts +2 -1
  35. package/dist/utils/semver.js +11 -5
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +2 -7
  39. package/dist/utils/warning.d.ts +0 -1
  40. package/dist/utils/warning.js +0 -52
@@ -1,4 +1,4 @@
1
- import type { ActionDockManifest } from "../project/types.js";
1
+ import type { ActionDockManifest, PlaybookSourceManifest } from "../project/types.js";
2
2
  import type { PackageIdentity } from "../runtime/identity.js";
3
3
  /**
4
4
  * 发现的包基本信息规范。
@@ -8,8 +8,8 @@ export interface DiscoveredPackage {
8
8
  readonly id: string;
9
9
  /** 包物理根目录绝对路径 */
10
10
  readonly root: string;
11
- /** 包清单定义 */
12
- readonly manifest: ActionDockManifest;
11
+ /** 包清单定义(支持磁盘声明式清单与运行时规程来源清单) */
12
+ readonly manifest: ActionDockManifest | PlaybookSourceManifest;
13
13
  /** 包物理与快照身份凭证(可选,由已有运行时传入) */
14
14
  readonly identity?: PackageIdentity;
15
15
  /** 是否来自工作区目录扫描发现 */
@@ -1,15 +1,11 @@
1
1
  import { ActionDockError } from "../errors.js";
2
2
  import { type ActionDockLockfile } from "../project/lockfile.js";
3
- import type { ActionDockManifest, PlaybookDefinition } from "../project/types.js";
3
+ import type { ActionDockManifest, PlaybookDefinition, PlaybookSourceManifest } from "../project/types.js";
4
4
  import { type PackageIdentity } from "../runtime/identity.js";
5
5
  import { parseSemVer, type SemVer } from "../utils/index.js";
6
6
  import { type DiscoveredPackage, type PackageDiscoveryOptions } from "./discovery.js";
7
7
  export type { SemVer };
8
8
  export { parseSemVer };
9
- /**
10
- * 校验两个语义化版本范围是否兼容并能收敛为单一解析版本。
11
- * 遵循设计规范:直接或传递版本范围无法收敛时拒绝并抛出 ACTION_PACKAGE_VERSION_CONFLICT。
12
- */
13
9
  export declare function areVersionsCompatible(v1: string, v2: string): boolean;
14
10
  /**
15
11
  * 版本冲突错误。
@@ -55,8 +51,8 @@ export interface PackageNode {
55
51
  readonly packageId: string;
56
52
  /** 包根目录绝对物理路径 */
57
53
  readonly root: string;
58
- /** 包清单规范 */
59
- readonly manifest: ActionDockManifest;
54
+ /** 包清单规范(支持磁盘声明式清单与运行时规程来源清单) */
55
+ readonly manifest: ActionDockManifest | PlaybookSourceManifest;
60
56
  /** 包清单摘要哈希 */
61
57
  readonly manifestDigest: string;
62
58
  /** 包版本号 */
@@ -145,12 +141,14 @@ export declare class DefaultPackageGraph implements PackageGraph {
145
141
  export interface PackageGraphBuilderOptions {
146
142
  /** 预先发现的包集合,若未提供则通过 PackageDiscovery 自动发现 */
147
143
  packages?: DiscoveredPackage[];
144
+ /** 完整显式集合的依赖存在性与选定版本校验(Host 增量注册时不启用)。 */
145
+ validateDependencies?: boolean;
148
146
  /** 根工程根目录或根包标识(与 projectRoot 互为别名) */
149
147
  root?: string;
150
148
  /** 当前工程根目录绝对或相对物理路径 */
151
149
  projectRoot?: string;
152
150
  /** 显式根清单 */
153
- manifest?: ActionDockManifest;
151
+ manifest?: ActionDockManifest | PlaybookSourceManifest;
154
152
  /** 锁文件规范对象(若未显式提供且 projectRoot 存在,则尝试自动读取 actiondock.lock.json) */
155
153
  lockfile?: ActionDockLockfile;
156
154
  /** 是否允许扫描与链接开发软链接包(默认根据 discoveryOptions 判定) */
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
- import { ActionDockError, ACTION_PACKAGE_VERSION_CONFLICT, MANIFEST_DIGEST_MISMATCH, MANIFEST_NOT_FOUND, MANIFEST_READ_FAILED, PACKAGE_NOT_FOUND, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
3
+ import { ActionDockError, ACTION_PACKAGE_VERSION_CONFLICT, MANIFEST_DIGEST_MISMATCH, MANIFEST_NOT_FOUND, MANIFEST_READ_FAILED, PACKAGE_NOT_FOUND, PACKAGE_ID_CONFLICT, INVALID_ARGUMENT, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
4
4
  import { computeManifestDigest, parseJsonWithoutDuplicates } from "../project/digest.js";
5
5
  import { loadLockfile } from "../project/lockfile.js";
6
6
  import { loadPlaybooks, loadProjectConfig } from "../project/loader.js";
@@ -10,30 +10,124 @@ import { createPackageIdentity } from "../runtime/identity.js";
10
10
  import { parseSemVer } from "../utils/index.js";
11
11
  import { PackageDiscovery } from "./discovery.js";
12
12
  export { parseSemVer };
13
- /**
14
- * 校验两个语义化版本范围是否兼容并能收敛为单一解析版本。
15
- * 遵循设计规范:直接或传递版本范围无法收敛时拒绝并抛出 ACTION_PACKAGE_VERSION_CONFLICT。
16
- */
17
- export function areVersionsCompatible(v1, v2) {
18
- if (v1 === v2 || v1 === "*" || v2 === "*" || !v1 || !v2) {
19
- return true;
13
+ /** 有限版本契约:严格三段版本(允许 v/= 前缀)、^、~、*;不猜测其他范围。 */
14
+ function parseVersionRange(value) {
15
+ const text = value.trim();
16
+ if (!text || text === "*")
17
+ return {};
18
+ const prefix = text[0];
19
+ const minimum = parseSemVer(prefix === "^" || prefix === "~" ? text.slice(1) : text);
20
+ if (!minimum)
21
+ return undefined;
22
+ if (prefix !== "^" && prefix !== "~")
23
+ return { minimum, exact: true };
24
+ const maximum = prefix === "~"
25
+ ? { major: minimum.major, minor: minimum.minor + 1, patch: 0 }
26
+ : minimum.major > 0 ? { major: minimum.major + 1, minor: 0, patch: 0 }
27
+ : minimum.minor > 0 ? { major: 0, minor: minimum.minor + 1, patch: 0 }
28
+ : { major: 0, minor: 0, patch: minimum.patch + 1 };
29
+ return { minimum, maximum };
30
+ }
31
+ function compareVersions(a, b) {
32
+ const core = a.major - b.major || a.minor - b.minor || a.patch - b.patch;
33
+ if (core)
34
+ return core;
35
+ if (a.prerelease === b.prerelease)
36
+ return 0;
37
+ if (a.prerelease === undefined)
38
+ return 1;
39
+ if (b.prerelease === undefined)
40
+ return -1;
41
+ const left = a.prerelease.split(".");
42
+ const right = b.prerelease.split(".");
43
+ for (let i = 0; i < Math.max(left.length, right.length); i++) {
44
+ if (left[i] === undefined)
45
+ return -1;
46
+ if (right[i] === undefined)
47
+ return 1;
48
+ if (left[i] === right[i])
49
+ continue;
50
+ const numericLeft = /^\d+$/.test(left[i]);
51
+ const numericRight = /^\d+$/.test(right[i]);
52
+ if (numericLeft && numericRight)
53
+ return BigInt(left[i]) < BigInt(right[i]) ? -1 : 1;
54
+ if (numericLeft !== numericRight)
55
+ return numericLeft ? -1 : 1;
56
+ return left[i] < right[i] ? -1 : 1;
20
57
  }
21
- const clean1 = v1.trim().replace(/^[\^~=]/, "");
22
- const clean2 = v2.trim().replace(/^[\^~=]/, "");
23
- const sem1 = parseSemVer(clean1);
24
- const sem2 = parseSemVer(clean2);
25
- if (!sem1 || !sem2) {
26
- return v1 === v2;
58
+ return 0;
59
+ }
60
+ function satisfiesRange(version, range) {
61
+ if (range.exact)
62
+ return compareVersions(version, range.minimum) === 0;
63
+ if (range.minimum && compareVersions(version, range.minimum) < 0)
64
+ return false;
65
+ if (range.maximum && compareVersions(version, range.maximum) >= 0)
66
+ return false;
67
+ // 预发布版本仅在范围下界显式点名同一核心版本的预发布时纳入。
68
+ if (version.prerelease !== undefined) {
69
+ const min = range.minimum;
70
+ return Boolean(min?.prerelease && version.major === min.major && version.minor === min.minor && version.patch === min.patch);
27
71
  }
28
- if (sem1.major !== sem2.major) {
72
+ return true;
73
+ }
74
+ export function areVersionsCompatible(v1, v2) {
75
+ const left = parseVersionRange(v1);
76
+ const right = parseVersionRange(v2);
77
+ if (!left || !right)
29
78
  return false;
79
+ const candidate = !left.minimum ? right.minimum : !right.minimum ? left.minimum
80
+ : compareVersions(left.minimum, right.minimum) >= 0 ? left.minimum : right.minimum;
81
+ if (!candidate)
82
+ return true;
83
+ if (satisfiesRange(candidate, left) && satisfiesRange(candidate, right))
84
+ return true;
85
+ // 两个范围可以在预发布下界之后的稳定版本交汇,但不能改变精确版本。
86
+ const stable = { major: candidate.major, minor: candidate.minor, patch: candidate.patch };
87
+ return satisfiesRange(stable, left) && satisfiesRange(stable, right);
88
+ }
89
+ /**
90
+ * 判定给定的依赖规格是否为不支持的语义化版本范围形态(而非获取描述)。
91
+ *
92
+ * 规则:
93
+ * - 范围操作符开头:^, ~, <, >, =, *;
94
+ * - 复合范围操作符:|| 或 - 范围;
95
+ * - X 范围与通配:如 1.x, 1.2.x, 1.*, 1.2.*, v1.x 等;
96
+ * - 不完整两段或一段版本:如 1, 1.2, v1, v1.2 等;
97
+ * - 版本三段式开头但未被严格解析器支持的非法形态。
98
+ */
99
+ function isUnsupportedVersionRange(spec) {
100
+ const text = spec.trim();
101
+ if (!text)
102
+ return false;
103
+ if (/^[~^<>=*]/.test(text))
104
+ return true;
105
+ if (/\|\||\s+-\s+/.test(text))
106
+ return true;
107
+ if (/^v?\d+\.(?:[xX*]|\d+\.[xX*])(?:$|[-+])/.test(text))
108
+ return true;
109
+ if (/^v?\d+(?:\.\d+)?$/.test(text))
110
+ return true;
111
+ if (/^v?\d+\.\d+\.\d+/.test(text))
112
+ return true;
113
+ return false;
114
+ }
115
+ /** 获取描述不是版本约束;仅真实范围及锁定版本参与版本兼容校验。 */
116
+ function dependencyVersionRequirements(spec, locked) {
117
+ const requirements = [];
118
+ const range = spec === undefined ? undefined : parseVersionRange(spec);
119
+ if (spec !== undefined && range)
120
+ requirements.push(spec);
121
+ else if (spec !== undefined && isUnsupportedVersionRange(spec)) {
122
+ throw new ActionDockError(INVALID_ARGUMENT, `Unsupported Action package version range '${spec}'; supported forms are exact versions, ^, ~ and *`);
30
123
  }
31
- if (v1.startsWith("~") || v2.startsWith("~")) {
32
- if (sem1.minor !== sem2.minor) {
33
- return false;
34
- }
124
+ const lockedVersion = locked?.resolved || locked?.version;
125
+ if (lockedVersion && parseSemVer(lockedVersion))
126
+ requirements.push(lockedVersion);
127
+ else if (lockedVersion) {
128
+ throw new ActionDockError(INVALID_ARGUMENT, `Invalid locked Action package version '${lockedVersion}'`);
35
129
  }
36
- return true;
130
+ return requirements;
37
131
  }
38
132
  /**
39
133
  * 版本冲突错误。
@@ -227,30 +321,20 @@ function computeTransitiveDependencies(nodes) {
227
321
  function collectVisiblePlaybooks(rootNode, nodes) {
228
322
  const visiblePlaybooks = new Map();
229
323
  const delegatedActions = new Set();
230
- if (rootNode && rootNode.root && existsSync(rootNode.root)) {
231
- try {
232
- const rootPbs = loadPlaybooks(rootNode.root, undefined, rootNode.manifest);
233
- for (const [id, pb] of rootPbs.entries()) {
234
- visiblePlaybooks.set(id, pb);
235
- visiblePlaybooks.set(`${rootNode.packageId}/${id}`, pb);
236
- }
237
- }
238
- catch {
239
- // 忽略根包 Playbook 加载异常
324
+ if (rootNode) {
325
+ const rootPbs = loadPlaybooks(rootNode.root || "", undefined, rootNode.manifest);
326
+ for (const [id, pb] of rootPbs.entries()) {
327
+ visiblePlaybooks.set(id, pb);
328
+ visiblePlaybooks.set(`${rootNode.packageId}/${id}`, pb);
240
329
  }
241
330
  for (const depId of rootNode.directDependencies) {
242
331
  if (depId === rootNode.packageId)
243
332
  continue;
244
333
  const depNode = nodes.get(depId);
245
- if (depNode && depNode.root && existsSync(depNode.root)) {
246
- try {
247
- const depPbs = loadPlaybooks(depNode.root, undefined, depNode.manifest);
248
- for (const [id, pb] of depPbs.entries()) {
249
- visiblePlaybooks.set(`${depId}/${id}`, pb);
250
- }
251
- }
252
- catch {
253
- // 忽略依赖包 Playbook 加载异常
334
+ if (depNode) {
335
+ const depPbs = loadPlaybooks(depNode.root || "", undefined, depNode.manifest);
336
+ for (const [id, pb] of depPbs.entries()) {
337
+ visiblePlaybooks.set(`${depId}/${id}`, pb);
254
338
  }
255
339
  }
256
340
  }
@@ -280,13 +364,16 @@ export class PackageGraphBuilder {
280
364
  */
281
365
  buildFromPackages(nodes, generation) {
282
366
  for (const pkg of this.options.packages) {
367
+ if (nodes.has(pkg.id)) {
368
+ throw new ActionDockError(PACKAGE_ID_CONFLICT, `Package ID '${pkg.id}' occurs more than once in explicit package graph`);
369
+ }
283
370
  const fields = createPackageNodeFields({
284
371
  id: pkg.id,
285
372
  root: pkg.root,
286
373
  manifest: pkg.manifest,
287
374
  npmPackage: pkg.id,
288
375
  isDirect: Boolean(pkg.isCurrentProject),
289
- isRoot: Boolean(pkg.isCurrentProject),
376
+ isRoot: false,
290
377
  identity: pkg.identity,
291
378
  generation,
292
379
  });
@@ -308,18 +395,46 @@ export class PackageGraphBuilder {
308
395
  }
309
396
  }
310
397
  if (!rootIdentity) {
311
- for (const node of nodes.values()) {
312
- if (node.isRoot) {
313
- rootIdentity = node.identity;
314
- break;
315
- }
398
+ const currentProjectNodes = Array.from(nodes.values()).filter((n) => n.isDirect);
399
+ if (currentProjectNodes.length === 1) {
400
+ rootIdentity = currentProjectNodes[0].identity;
316
401
  }
317
402
  }
318
- if (rootIdentity) {
319
- const rootNode = nodes.get(rootIdentity.id);
320
- if (rootNode) {
321
- rootNode.isRoot = true;
322
- rootNode.isDirect = true;
403
+ for (const [id, node] of nodes) {
404
+ const isTheRoot = Boolean(rootIdentity && node.identity.id === rootIdentity.id);
405
+ nodes.set(id, { ...node, isRoot: isTheRoot, isDirect: isTheRoot || Boolean(node.isDirect) });
406
+ }
407
+ if (this.options.validateDependencies) {
408
+ for (const node of nodes.values()) {
409
+ const lockfile = node.root ? loadLockfile(node.root) : null;
410
+ for (const [depId, spec] of Object.entries(node.manifest.dependencies || {})) {
411
+ const dep = nodes.get(depId);
412
+ if (!dep) {
413
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package '${depId}' required by '${node.packageId}' is missing from explicit graph`);
414
+ }
415
+ for (const requirement of dependencyVersionRequirements(spec)) {
416
+ if (!areVersionsCompatible(requirement, dep.version)) {
417
+ throw new ActionPackageVersionConflictError(depId, [
418
+ { requester: node.packageId, rangeOrVersion: requirement },
419
+ { requester: depId, rangeOrVersion: dep.version },
420
+ ]);
421
+ }
422
+ }
423
+ }
424
+ // 根锁文件也可能锁定传递依赖,不能只检查直接 dependencies。
425
+ for (const [depId, locked] of Object.entries(lockfile?.packages || {})) {
426
+ const dep = nodes.get(depId);
427
+ if (!dep)
428
+ continue;
429
+ for (const requirement of dependencyVersionRequirements(undefined, locked)) {
430
+ if (!areVersionsCompatible(requirement, dep.version)) {
431
+ throw new ActionPackageVersionConflictError(depId, [
432
+ { requester: node.packageId, rangeOrVersion: requirement },
433
+ { requester: depId, rangeOrVersion: dep.version },
434
+ ]);
435
+ }
436
+ }
437
+ }
323
438
  }
324
439
  }
325
440
  linkDirectDependencies(nodes);
@@ -415,6 +530,13 @@ export class PackageGraphBuilder {
415
530
  // 忽略扫描外部链接包异常
416
531
  }
417
532
  }
533
+ const requesterLocks = new Map([[rootPackageId, lockfile]]);
534
+ const getRequesterLock = (requester) => {
535
+ if (!requesterLocks.has(requester)) {
536
+ requesterLocks.set(requester, loadLockfile(nodes.get(requester).root));
537
+ }
538
+ return requesterLocks.get(requester);
539
+ };
418
540
  const versionRequirements = new Map();
419
541
  function recordRequirement(pkgId, requester, rangeOrVersion) {
420
542
  const existing = versionRequirements.get(pkgId) || [];
@@ -429,10 +551,22 @@ export class PackageGraphBuilder {
429
551
  existing.push({ requester, rangeOrVersion });
430
552
  versionRequirements.set(pkgId, existing);
431
553
  }
554
+ const recordDependency = (pkgId, requester, spec) => {
555
+ const requesterLocked = getRequesterLock(requester)?.packages[pkgId];
556
+ for (const requirement of dependencyVersionRequirements(spec, requesterLocked)) {
557
+ recordRequirement(pkgId, requester, requirement);
558
+ }
559
+ const rootLocked = lockfile?.packages[pkgId];
560
+ if (rootLocked && rootLocked !== requesterLocked) {
561
+ for (const requirement of dependencyVersionRequirements(undefined, rootLocked)) {
562
+ recordRequirement(pkgId, rootPackageId, requirement);
563
+ }
564
+ }
565
+ };
432
566
  const queue = [];
433
567
  for (const [depId, spec] of Object.entries(rootManifest.dependencies || {})) {
434
568
  rootNode.directDependencies.add(depId);
435
- recordRequirement(depId, rootPackageId, spec);
569
+ recordDependency(depId, rootPackageId, spec);
436
570
  queue.push({
437
571
  pkgId: depId,
438
572
  requester: rootPackageId,
@@ -460,7 +594,7 @@ export class PackageGraphBuilder {
460
594
  continue;
461
595
  }
462
596
  let pkgDir = null;
463
- const locked = lockfile?.packages?.[item.pkgId];
597
+ const locked = getRequesterLock(item.requester)?.packages[item.pkgId] || lockfile?.packages[item.pkgId];
464
598
  if (packageRootsRecord?.[item.pkgId]) {
465
599
  pkgDir = packageRootsRecord[item.pkgId];
466
600
  }
@@ -474,7 +608,7 @@ export class PackageGraphBuilder {
474
608
  }
475
609
  candidateNames.push(item.rangeOrSpec);
476
610
  candidateNames.push(item.pkgId);
477
- let cur = absProjectRoot;
611
+ let cur = nodes.get(item.requester)?.root || absProjectRoot;
478
612
  while (true) {
479
613
  for (const candName of candidateNames) {
480
614
  const candPath = join(cur, "node_modules", candName);
@@ -506,6 +640,9 @@ export class PackageGraphBuilder {
506
640
  throw new ActionDockError(MANIFEST_READ_FAILED, `Failed to read manifest for '${item.pkgId}': ${err.message}`);
507
641
  }
508
642
  const depManifest = parseJsonWithoutDuplicates(manifestRaw);
643
+ if (depManifest.id !== item.pkgId) {
644
+ throw new ActionDockError(PACKAGE_ID_CONFLICT, `Expected package '${item.pkgId}' at '${pkgDir}', found '${depManifest.id}'`);
645
+ }
509
646
  const actualDigest = computeManifestDigest(depManifest);
510
647
  if (locked?.manifestDigest && locked.manifestDigest !== actualDigest) {
511
648
  throw new ActionDockError(MANIFEST_DIGEST_MISMATCH, `Manifest digest mismatch for package '${item.pkgId}': expected '${locked.manifestDigest}', got '${actualDigest}'. Re-resolution required.`);
@@ -538,15 +675,13 @@ export class PackageGraphBuilder {
538
675
  };
539
676
  nodes.set(item.pkgId, newNode);
540
677
  for (const [subDepId, subSpec] of Object.entries(depManifest.dependencies || {})) {
541
- recordRequirement(subDepId, item.pkgId, subSpec);
542
- if (!nodes.has(subDepId)) {
543
- queue.push({
544
- pkgId: subDepId,
545
- requester: item.pkgId,
546
- rangeOrSpec: subSpec,
547
- isDirect: false,
548
- });
549
- }
678
+ recordDependency(subDepId, item.pkgId, subSpec);
679
+ queue.push({
680
+ pkgId: subDepId,
681
+ requester: item.pkgId,
682
+ rangeOrSpec: subSpec,
683
+ isDirect: false,
684
+ });
550
685
  }
551
686
  }
552
687
  if (scanLinked) {
@@ -20,7 +20,7 @@ export interface ResolvePlaybookContext {
20
20
  readonly caller?: string;
21
21
  }
22
22
  /**
23
- * 纯领域规程解析函数 resolvePlaybook。
24
- * 基于 PackageGraph 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
23
+ * 基于 PackageGraph 的规程寻址函数,正文统一由规程加载层提供。
24
+ * 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
25
25
  */
26
26
  export declare function resolvePlaybook(identifier: string, context: ResolvePlaybookContext): ResolvedPlaybook;
@@ -1,48 +1,12 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
1
  import { loadPlaybooks } from "../project/loader.js";
4
2
  import { getPackageSlug } from "../utils/index.js";
5
3
  import { ActionDockError, INVALID_ACTION_REF, NOT_FOUND, PACKAGE_NOT_FOUND } from "../errors.js";
6
4
  function getNodePlaybooks(node) {
7
- const map = new Map();
8
- if (node.root && existsSync(node.root)) {
9
- try {
10
- const diskPlaybooks = loadPlaybooks(node.root, node.manifest?.playbooksDir);
11
- for (const [k, v] of diskPlaybooks) {
12
- map.set(k, v);
13
- }
14
- }
15
- catch {
16
- // 忽略磁盘规程目录扫描异常
17
- }
18
- }
19
- if (node.manifest?.playbooks && typeof node.manifest.playbooks === "object") {
20
- for (const [id, item] of Object.entries(node.manifest.playbooks)) {
21
- const existing = map.get(id);
22
- let content = item.content ?? existing?.content ?? "";
23
- let filePath = item.entry && node.root
24
- ? resolve(node.root, item.entry)
25
- : existing?.filePath;
26
- if (!content && filePath && existsSync(filePath)) {
27
- try {
28
- content = readFileSync(filePath, "utf-8");
29
- }
30
- catch { }
31
- }
32
- map.set(id, {
33
- id,
34
- description: item.description ?? existing?.description,
35
- actions: item.actions ?? existing?.actions ?? [],
36
- content,
37
- filePath: filePath || "",
38
- });
39
- }
40
- }
41
- return map;
5
+ return loadPlaybooks(node.root, node.manifest.playbooksDir, node.manifest);
42
6
  }
43
7
  /**
44
- * 纯领域规程解析函数 resolvePlaybook。
45
- * 基于 PackageGraph 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
8
+ * 基于 PackageGraph 的规程寻址函数,正文统一由规程加载层提供。
9
+ * 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
46
10
  */
47
11
  export function resolvePlaybook(identifier, context) {
48
12
  const str = identifier.trim();
@@ -87,20 +51,15 @@ export function resolvePlaybook(identifier, context) {
87
51
  // 3. 全局搜索
88
52
  const matches = [];
89
53
  for (const node of context.graph.packages.values()) {
90
- try {
91
- const playbooks = getNodePlaybooks(node);
92
- const pb = playbooks.get(str);
93
- if (pb) {
94
- matches.push({
95
- packageId: node.identity.id,
96
- projectRoot: node.root,
97
- playbookId: str,
98
- playbook: pb,
99
- });
100
- }
101
- }
102
- catch {
103
- // 忽略单个包加载异常
54
+ const playbooks = getNodePlaybooks(node);
55
+ const pb = playbooks.get(str);
56
+ if (pb) {
57
+ matches.push({
58
+ packageId: node.identity.id,
59
+ projectRoot: node.root,
60
+ playbookId: str,
61
+ playbook: pb,
62
+ });
104
63
  }
105
64
  }
106
65
  if (matches.length === 1) {
@@ -1,5 +1,5 @@
1
1
  import type { ActionDefinition, ActionRef, ExecutionEvent, ExecutionResult, JsonValue, Logger, ProcessAPI, RunRecord, RunStatus } from "@actiondock/sdk";
2
- import type { ProjectConfig } from "../project/types.js";
2
+ import type { ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
3
3
  import type { Clock } from "../storage/clock.js";
4
4
  import type { EventSink } from "../runtime/events.js";
5
5
  import type { RuntimeStorage } from "../storage/types.js";
@@ -40,7 +40,7 @@ export interface ExecutionServiceOptions {
40
40
  storage: RuntimeStorage;
41
41
  globalStorage?: RuntimeStorage;
42
42
  projectRoot?: string;
43
- projectConfig?: ProjectConfig;
43
+ projectConfig?: ProjectConfig | RuntimeProjectConfig;
44
44
  configOverrides?: Record<string, unknown>;
45
45
  actions?: Map<string, ActionDefinition>;
46
46
  process?: ProcessAPI;
@@ -25,7 +25,7 @@ export declare function listVisiblePlaybooks(runtimes: readonly PackageRuntime[]
25
25
  graph?: PackageGraph;
26
26
  }): Promise<PlaybookSummary[]>;
27
27
  /**
28
- * 静态查询指定 Playbook 规范:基于 resolvePlaybook 统一纯领域解析。
28
+ * 静态查询指定 Playbook 规范:委托 resolvePlaybook 统一寻址与来源加载。
29
29
  */
30
30
  export declare function describeVisiblePlaybook(runtimes: readonly PackageRuntime[], id: string, visibility: {
31
31
  hostPublicPackageIds: ReadonlySet<string>;
@@ -83,7 +83,7 @@ function buildFallbackGraph(runtimes) {
83
83
  ])));
84
84
  }
85
85
  /**
86
- * 静态查询指定 Playbook 规范:基于 resolvePlaybook 统一纯领域解析。
86
+ * 静态查询指定 Playbook 规范:委托 resolvePlaybook 统一寻址与来源加载。
87
87
  */
88
88
  export async function describeVisiblePlaybook(runtimes, id, visibility) {
89
89
  const graph = visibility.graph || buildFallbackGraph(runtimes);
package/dist/index.d.ts CHANGED
@@ -5,7 +5,6 @@
5
5
  * 平台大对象驱动与解析器工具移至特定子路径导出(./server, ./project, ./registry,
6
6
  * ./profile, ./graph, ./package),严格杜绝在根导出泄露内部执行与平台上下文机制。
7
7
  */
8
- export { suppressExperimentalWarnings } from "./utils/warning.js";
9
8
  export { ACTIONDOCK_VERSION } from "./version.js";
10
9
  export { createActionDock, connectActionDock, } from "./service/factory.js";
11
10
  export type { ActionDockService, ActionDockService as ActionDock, DiscoveryPort, ExecutionPort, RunsPort, EventsPort, RunEventSubscriptionOptions, ConfigPort, StatePort, CreateActionDockOptions, ConnectActionDockOptions, RemoteServiceOptions, ConfigValueView, ListRunsOptions, RunReadOptions, StateScopeOptions, } from "./service/types.js";
@@ -23,6 +22,8 @@ export { startActionDockServer, } from "./server/server.js";
23
22
  export type { ServerOptions, ServerOptions as ActionDockServerOptions, ActionDockServerInstance, } from "./server/types.js";
24
23
  export { initProject } from "./project/init.js";
25
24
  export { loadProjectConfig, findProjectRoot, } from "./project/loader.js";
26
- export type { ProjectConfig, } from "./project/types.js";
25
+ export { discoverLocalSkillDirectory, inspectLocalPackageDirectory, queryLocalPackageDirectory, validateLocalSkillPackages, isExplicitPath, } from "./project/skill-dir.js";
26
+ export type { ProjectConfig, PlaybookSourceManifest, RuntimeProjectConfig, } from "./project/types.js";
27
+ export type { LocalSkillDirectory, LocalPackageEntry, } from "./project/skill-dir.js";
27
28
  export type { PackageGraph, } from "./catalog/graph.js";
28
29
  export { ActionDockError, ProcessError, PACKAGE_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_NOT_FOUND, ACTION_LOAD_FAILED, ACTION_FAILED, ACTION_TIMEOUT, ACTION_CANCELLED, INPUT_VALIDATION_FAILED, OUTPUT_VALIDATION_FAILED, INPUT_NOT_JSON, OUTPUT_NOT_JSON, ACTION_SUBRUN_LIMIT, ACTION_CALL_CYCLE, UNDECLARED_ACTION_DEPENDENCY, INVALID_ACTION_REF, IDEMPOTENCY_CONFLICT, EXECUTION_FAILED, UNHANDLED_EXECUTION_ERROR, RUN_REPOSITORY_UNAVAILABLE, RUN_PERSISTENCE_FAILED, STANDALONE_ASYNC_UNSUPPORTED, HOST_PROCESS_EXITED, EXECUTION_ABORTED, TIMEOUT, NETWORK_ERROR, CAPABILITY_UNAVAILABLE, STATE_KEY_NOT_FOUND, STORAGE_BUSY, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED, PROCESS_OUTPUT_LIMIT, PROCESS_SPAWN_ERROR, PROCESS_CANCELLED, PROCESS_TIMEOUT, UNAUTHORIZED, NOT_FOUND, SERVER_ERROR, PACKAGE_FORBIDDEN, ACTION_FORBIDDEN, PLAYBOOK_NOT_FOUND, REQUEST_TOO_LARGE, ACTION_START_FAILED, ACTION_EXECUTION_ERROR, DATA_DIR_IN_USE, DATA_DIR_RECOVERY_REQUIRED, PROCESS_FAILED, MCP_TOOL_NAME_COLLISION, ACCESS_DENIED, INVALID_FLAT_ARGUMENT, INVALID_JSON, INVALID_JSON_LITERAL, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, INVALID_ARGUMENT, PACKAGE_NOT_ALLOWED, INVALID_PACKAGE_ID, PATH_TRAVERSAL, AMBIGUOUS_STATE_KEY, RUN_NOT_FOUND, RUN_ALREADY_FINISHED, PROJECT_ROOT_NOT_FOUND, describeActionLoadFailure, createErrorEnvelope, resolveExecutionHint, } from "./errors.js";
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@
5
5
  * 平台大对象驱动与解析器工具移至特定子路径导出(./server, ./project, ./registry,
6
6
  * ./profile, ./graph, ./package),严格杜绝在根导出泄露内部执行与平台上下文机制。
7
7
  */
8
- export { suppressExperimentalWarnings } from "./utils/warning.js";
9
8
  // 1. 核心版本号单一事实源
10
9
  export { ACTIONDOCK_VERSION } from "./version.js";
11
10
  // 2. 统一服务门面与聚合契约接口
@@ -20,5 +19,6 @@ export { startActionDockServer, } from "./server/server.js";
20
19
  // 6. 核心工程辅助
21
20
  export { initProject } from "./project/init.js";
22
21
  export { loadProjectConfig, findProjectRoot, } from "./project/loader.js";
22
+ export { discoverLocalSkillDirectory, inspectLocalPackageDirectory, queryLocalPackageDirectory, validateLocalSkillPackages, isExplicitPath, } from "./project/skill-dir.js";
23
23
  // 8. 统一错误模型与核心标准错误码常量
24
24
  export { ActionDockError, ProcessError, PACKAGE_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_NOT_FOUND, ACTION_LOAD_FAILED, ACTION_FAILED, ACTION_TIMEOUT, ACTION_CANCELLED, INPUT_VALIDATION_FAILED, OUTPUT_VALIDATION_FAILED, INPUT_NOT_JSON, OUTPUT_NOT_JSON, ACTION_SUBRUN_LIMIT, ACTION_CALL_CYCLE, UNDECLARED_ACTION_DEPENDENCY, INVALID_ACTION_REF, IDEMPOTENCY_CONFLICT, EXECUTION_FAILED, UNHANDLED_EXECUTION_ERROR, RUN_REPOSITORY_UNAVAILABLE, RUN_PERSISTENCE_FAILED, STANDALONE_ASYNC_UNSUPPORTED, HOST_PROCESS_EXITED, EXECUTION_ABORTED, TIMEOUT, NETWORK_ERROR, CAPABILITY_UNAVAILABLE, STATE_KEY_NOT_FOUND, STORAGE_BUSY, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED, PROCESS_OUTPUT_LIMIT, PROCESS_SPAWN_ERROR, PROCESS_CANCELLED, PROCESS_TIMEOUT, UNAUTHORIZED, NOT_FOUND, SERVER_ERROR, PACKAGE_FORBIDDEN, ACTION_FORBIDDEN, PLAYBOOK_NOT_FOUND, REQUEST_TOO_LARGE, ACTION_START_FAILED, ACTION_EXECUTION_ERROR, DATA_DIR_IN_USE, DATA_DIR_RECOVERY_REQUIRED, PROCESS_FAILED, MCP_TOOL_NAME_COLLISION, ACCESS_DENIED, INVALID_FLAT_ARGUMENT, INVALID_JSON, INVALID_JSON_LITERAL, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, INVALID_ARGUMENT, PACKAGE_NOT_ALLOWED, INVALID_PACKAGE_ID, PATH_TRAVERSAL, AMBIGUOUS_STATE_KEY, RUN_NOT_FOUND, RUN_ALREADY_FINISHED, PROJECT_ROOT_NOT_FOUND, describeActionLoadFailure, createErrorEnvelope, resolveExecutionHint, } from "./errors.js";
@@ -1,6 +1,6 @@
1
1
  import type { ExecutionEvent, ExecutionResult, JsonValue, RunRecord } from "@actiondock/sdk";
2
2
  import type { ActionInvoker, CancelResult, ExecutionTicket } from "../execution/types.js";
3
- import type { ProjectConfig } from "../project/types.js";
3
+ import type { ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
4
4
  import { type PackageIdentity } from "../runtime/identity.js";
5
5
  import { type InvocationContext, type RunOptions } from "../invocation/types.js";
6
6
  import type { ActionSpec, ActionSummary, ConfigValueView, HostManagedPackageRuntime, ListActionsOptions, ListRunsOptions, PackageInfo, PackageRuntime, PackageRuntimeOptions, PackageRuntimeInternalOptions, PlaybookSpec, PlaybookSummary, StateScopeOptions } from "./types.js";
@@ -21,7 +21,7 @@ export declare class DefaultPackageRuntime implements HostManagedPackageRuntime
21
21
  readonly packageInstanceId: string;
22
22
  readonly generationId: string;
23
23
  readonly packageRoot?: string;
24
- readonly projectConfig: ProjectConfig;
24
+ readonly projectConfig: ProjectConfig | RuntimeProjectConfig;
25
25
  readonly inMemory: boolean;
26
26
  private readonly platform;
27
27
  private readonly actionsMap;
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { DefaultExecutionService } from "../execution/service.js";
4
4
  import { createNodePlatform } from "../platform/index.js";
5
- import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
5
+ import { completeRuntimeProjectConfig, findProjectRoot, loadProjectConfig } from "../project/loader.js";
6
6
  import { RuntimeConfig } from "../runtime/context.js";
7
7
  import { normalizeActionCollection } from "../runtime/action-collection.js";
8
8
  import { isSecretConfigKey, sanitizeConfigDefinitions } from "../storage/mask.js";
@@ -125,6 +125,8 @@ export class DefaultPackageRuntime {
125
125
  version: "0.1.0",
126
126
  };
127
127
  }
128
+ // 在唯一装配边界确定规程来源,索引与包图共享同一份完整配置快照。
129
+ projectConfig = completeRuntimeProjectConfig(packageRoot, projectConfig);
128
130
  this.packageRoot = packageRoot;
129
131
  this.projectConfig = projectConfig;
130
132
  // 兼容未在公开选项契约中显式声明的包实例标识与代系标识内部参数透传
@@ -1,5 +1,5 @@
1
1
  import type { ActionDefinition } from "@actiondock/sdk";
2
- import type { ProjectConfig } from "../project/types.js";
2
+ import type { ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
3
3
  import type { ActionSpec, PlaybookSpec } from "./types.js";
4
4
  /**
5
5
  * 静态清单索引构建入参:同一包的全部静态事实来源。
@@ -10,8 +10,8 @@ export interface StaticIndexInput {
10
10
  packageRoot?: string;
11
11
  /** 包唯一标识 */
12
12
  packageId: string;
13
- /** 项目配置(规范化清单配置快照,单一事实源) */
14
- projectConfig: ProjectConfig;
13
+ /** 项目配置(规范化清单配置快照或运行时来源,单一事实源) */
14
+ projectConfig: ProjectConfig | RuntimeProjectConfig;
15
15
  }
16
16
  /** Action 索引构建入参:静态事实来源之上叠加内存注入的 Action 定义集合 */
17
17
  export interface StaticActionIndexInput extends StaticIndexInput {
@@ -29,8 +29,6 @@ export interface StaticActionIndexInput extends StaticIndexInput {
29
29
  export declare function buildStaticActionMap(input: StaticActionIndexInput): Map<string, ActionSpec>;
30
30
  /**
31
31
  * 静态读取并聚合指定包的 Playbook 规范索引。
32
- *
33
- * 聚合来源:磁盘规程目录扫描(以 actiondock.json 声明为事实源)与
34
- * 项目配置中声明的 playbooks;同键时配置声明覆盖磁盘扫描结果。
32
+ * 统一复用 project/loader 的 loadPlaybooks(单一事实源),不再自行读文件或吞异常。
35
33
  */
36
34
  export declare function buildStaticPlaybookMap(input: StaticIndexInput): Map<string, PlaybookSpec>;