@actiondock/core 2.15.0 → 2.17.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 (52) hide show
  1. package/dist/catalog/discovery.d.ts +3 -3
  2. package/dist/catalog/graph.d.ts +8 -6
  3. package/dist/catalog/graph.js +62 -197
  4. package/dist/catalog/resolve-playbook.d.ts +2 -2
  5. package/dist/catalog/resolve-playbook.js +53 -12
  6. package/dist/execution/service.js +6 -0
  7. package/dist/execution/types.d.ts +2 -2
  8. package/dist/host/host.js +16 -5
  9. package/dist/host/routing.d.ts +1 -1
  10. package/dist/host/routing.js +1 -1
  11. package/dist/index.d.ts +2 -4
  12. package/dist/index.js +0 -1
  13. package/dist/invocation/types.d.ts +5 -1
  14. package/dist/invocation/types.js +5 -0
  15. package/dist/package/runtime.d.ts +2 -2
  16. package/dist/package/runtime.js +9 -5
  17. package/dist/package/static-index.d.ts +6 -4
  18. package/dist/package/static-index.js +60 -12
  19. package/dist/package/types.d.ts +5 -5
  20. package/dist/project/index.d.ts +0 -1
  21. package/dist/project/index.js +0 -1
  22. package/dist/project/init.js +2 -2
  23. package/dist/project/loader.d.ts +2 -4
  24. package/dist/project/loader.js +34 -38
  25. package/dist/project/manifest.d.ts +0 -2
  26. package/dist/project/manifest.js +1 -17
  27. package/dist/project/types.d.ts +0 -31
  28. package/dist/registry/registry.js +15 -27
  29. package/dist/runtime/context.d.ts +3 -3
  30. package/dist/runtime/index.d.ts +1 -0
  31. package/dist/runtime/index.js +1 -0
  32. package/dist/runtime/run-origin.d.ts +9 -0
  33. package/dist/runtime/run-origin.js +33 -0
  34. package/dist/runtime/run-persistence.d.ts +3 -1
  35. package/dist/runtime/run-persistence.js +3 -0
  36. package/dist/runtime/standalone-commands.js +2 -8
  37. package/dist/runtime/standalone.d.ts +0 -2
  38. package/dist/server/policy.d.ts +8 -0
  39. package/dist/server/policy.js +14 -0
  40. package/dist/server/routes/actions.js +5 -0
  41. package/dist/service/types.d.ts +3 -1
  42. package/dist/storage/sqlite-runs.js +9 -3
  43. package/dist/storage/sqlite-schema.js +5 -1
  44. package/dist/storage/utils.d.ts +5 -2
  45. package/dist/storage/utils.js +7 -2
  46. package/dist/utils/semver.d.ts +1 -2
  47. package/dist/utils/semver.js +5 -11
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +2 -2
  51. package/dist/project/skill-dir.d.ts +0 -72
  52. package/dist/project/skill-dir.js +0 -244
@@ -1,4 +1,4 @@
1
- import type { ActionDockManifest, PlaybookSourceManifest } from "../project/types.js";
1
+ import type { ActionDockManifest } 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 | PlaybookSourceManifest;
11
+ /** 包清单定义 */
12
+ readonly manifest: ActionDockManifest;
13
13
  /** 包物理与快照身份凭证(可选,由已有运行时传入) */
14
14
  readonly identity?: PackageIdentity;
15
15
  /** 是否来自工作区目录扫描发现 */
@@ -1,11 +1,15 @@
1
1
  import { ActionDockError } from "../errors.js";
2
2
  import { type ActionDockLockfile } from "../project/lockfile.js";
3
- import type { ActionDockManifest, PlaybookDefinition, PlaybookSourceManifest } from "../project/types.js";
3
+ import type { ActionDockManifest, PlaybookDefinition } 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
+ */
9
13
  export declare function areVersionsCompatible(v1: string, v2: string): boolean;
10
14
  /**
11
15
  * 版本冲突错误。
@@ -51,8 +55,8 @@ export interface PackageNode {
51
55
  readonly packageId: string;
52
56
  /** 包根目录绝对物理路径 */
53
57
  readonly root: string;
54
- /** 包清单规范(支持磁盘声明式清单与运行时规程来源清单) */
55
- readonly manifest: ActionDockManifest | PlaybookSourceManifest;
58
+ /** 包清单规范 */
59
+ readonly manifest: ActionDockManifest;
56
60
  /** 包清单摘要哈希 */
57
61
  readonly manifestDigest: string;
58
62
  /** 包版本号 */
@@ -141,14 +145,12 @@ export declare class DefaultPackageGraph implements PackageGraph {
141
145
  export interface PackageGraphBuilderOptions {
142
146
  /** 预先发现的包集合,若未提供则通过 PackageDiscovery 自动发现 */
143
147
  packages?: DiscoveredPackage[];
144
- /** 完整显式集合的依赖存在性与选定版本校验(Host 增量注册时不启用)。 */
145
- validateDependencies?: boolean;
146
148
  /** 根工程根目录或根包标识(与 projectRoot 互为别名) */
147
149
  root?: string;
148
150
  /** 当前工程根目录绝对或相对物理路径 */
149
151
  projectRoot?: string;
150
152
  /** 显式根清单 */
151
- manifest?: ActionDockManifest | PlaybookSourceManifest;
153
+ manifest?: ActionDockManifest;
152
154
  /** 锁文件规范对象(若未显式提供且 projectRoot 存在,则尝试自动读取 actiondock.lock.json) */
153
155
  lockfile?: ActionDockLockfile;
154
156
  /** 是否允许扫描与链接开发软链接包(默认根据 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, PACKAGE_ID_CONFLICT, INVALID_ARGUMENT, 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, 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,124 +10,30 @@ 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
- /** 有限版本契约:严格三段版本(允许 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;
57
- }
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);
71
- }
72
- return true;
73
- }
74
- export function areVersionsCompatible(v1, v2) {
75
- const left = parseVersionRange(v1);
76
- const right = parseVersionRange(v2);
77
- if (!left || !right)
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
13
  /**
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
- * - 版本三段式开头但未被严格解析器支持的非法形态。
14
+ * 校验两个语义化版本范围是否兼容并能收敛为单一解析版本。
15
+ * 遵循设计规范:直接或传递版本范围无法收敛时拒绝并抛出 ACTION_PACKAGE_VERSION_CONFLICT。
98
16
  */
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))
17
+ export function areVersionsCompatible(v1, v2) {
18
+ if (v1 === v2 || v1 === "*" || v2 === "*" || !v1 || !v2) {
112
19
  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 *`);
123
20
  }
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}'`);
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;
27
+ }
28
+ if (sem1.major !== sem2.major) {
29
+ return false;
30
+ }
31
+ if (v1.startsWith("~") || v2.startsWith("~")) {
32
+ if (sem1.minor !== sem2.minor) {
33
+ return false;
34
+ }
129
35
  }
130
- return requirements;
36
+ return true;
131
37
  }
132
38
  /**
133
39
  * 版本冲突错误。
@@ -321,20 +227,30 @@ function computeTransitiveDependencies(nodes) {
321
227
  function collectVisiblePlaybooks(rootNode, nodes) {
322
228
  const visiblePlaybooks = new Map();
323
229
  const delegatedActions = new Set();
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);
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 加载异常
329
240
  }
330
241
  for (const depId of rootNode.directDependencies) {
331
242
  if (depId === rootNode.packageId)
332
243
  continue;
333
244
  const depNode = nodes.get(depId);
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);
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 加载异常
338
254
  }
339
255
  }
340
256
  }
@@ -364,16 +280,13 @@ export class PackageGraphBuilder {
364
280
  */
365
281
  buildFromPackages(nodes, generation) {
366
282
  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
- }
370
283
  const fields = createPackageNodeFields({
371
284
  id: pkg.id,
372
285
  root: pkg.root,
373
286
  manifest: pkg.manifest,
374
287
  npmPackage: pkg.id,
375
288
  isDirect: Boolean(pkg.isCurrentProject),
376
- isRoot: false,
289
+ isRoot: Boolean(pkg.isCurrentProject),
377
290
  identity: pkg.identity,
378
291
  generation,
379
292
  });
@@ -395,48 +308,20 @@ export class PackageGraphBuilder {
395
308
  }
396
309
  }
397
310
  if (!rootIdentity) {
398
- const currentProjectNodes = Array.from(nodes.values()).filter((n) => n.isDirect);
399
- if (currentProjectNodes.length === 1) {
400
- rootIdentity = currentProjectNodes[0].identity;
401
- }
402
- }
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
311
  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
- }
312
+ if (node.isRoot) {
313
+ rootIdentity = node.identity;
314
+ break;
437
315
  }
438
316
  }
439
317
  }
318
+ if (rootIdentity) {
319
+ const rootNode = nodes.get(rootIdentity.id);
320
+ if (rootNode) {
321
+ rootNode.isRoot = true;
322
+ rootNode.isDirect = true;
323
+ }
324
+ }
440
325
  linkDirectDependencies(nodes);
441
326
  computeTransitiveDependencies(nodes);
442
327
  const effectiveRootNode = rootIdentity ? nodes.get(rootIdentity.id) : undefined;
@@ -530,13 +415,6 @@ export class PackageGraphBuilder {
530
415
  // 忽略扫描外部链接包异常
531
416
  }
532
417
  }
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
- };
540
418
  const versionRequirements = new Map();
541
419
  function recordRequirement(pkgId, requester, rangeOrVersion) {
542
420
  const existing = versionRequirements.get(pkgId) || [];
@@ -551,22 +429,10 @@ export class PackageGraphBuilder {
551
429
  existing.push({ requester, rangeOrVersion });
552
430
  versionRequirements.set(pkgId, existing);
553
431
  }
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
- };
566
432
  const queue = [];
567
433
  for (const [depId, spec] of Object.entries(rootManifest.dependencies || {})) {
568
434
  rootNode.directDependencies.add(depId);
569
- recordDependency(depId, rootPackageId, spec);
435
+ recordRequirement(depId, rootPackageId, spec);
570
436
  queue.push({
571
437
  pkgId: depId,
572
438
  requester: rootPackageId,
@@ -594,7 +460,7 @@ export class PackageGraphBuilder {
594
460
  continue;
595
461
  }
596
462
  let pkgDir = null;
597
- const locked = getRequesterLock(item.requester)?.packages[item.pkgId] || lockfile?.packages[item.pkgId];
463
+ const locked = lockfile?.packages?.[item.pkgId];
598
464
  if (packageRootsRecord?.[item.pkgId]) {
599
465
  pkgDir = packageRootsRecord[item.pkgId];
600
466
  }
@@ -608,7 +474,7 @@ export class PackageGraphBuilder {
608
474
  }
609
475
  candidateNames.push(item.rangeOrSpec);
610
476
  candidateNames.push(item.pkgId);
611
- let cur = nodes.get(item.requester)?.root || absProjectRoot;
477
+ let cur = absProjectRoot;
612
478
  while (true) {
613
479
  for (const candName of candidateNames) {
614
480
  const candPath = join(cur, "node_modules", candName);
@@ -640,9 +506,6 @@ export class PackageGraphBuilder {
640
506
  throw new ActionDockError(MANIFEST_READ_FAILED, `Failed to read manifest for '${item.pkgId}': ${err.message}`);
641
507
  }
642
508
  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
- }
646
509
  const actualDigest = computeManifestDigest(depManifest);
647
510
  if (locked?.manifestDigest && locked.manifestDigest !== actualDigest) {
648
511
  throw new ActionDockError(MANIFEST_DIGEST_MISMATCH, `Manifest digest mismatch for package '${item.pkgId}': expected '${locked.manifestDigest}', got '${actualDigest}'. Re-resolution required.`);
@@ -675,13 +538,15 @@ export class PackageGraphBuilder {
675
538
  };
676
539
  nodes.set(item.pkgId, newNode);
677
540
  for (const [subDepId, subSpec] of Object.entries(depManifest.dependencies || {})) {
678
- recordDependency(subDepId, item.pkgId, subSpec);
679
- queue.push({
680
- pkgId: subDepId,
681
- requester: item.pkgId,
682
- rangeOrSpec: subSpec,
683
- isDirect: false,
684
- });
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
+ }
685
550
  }
686
551
  }
687
552
  if (scanLinked) {
@@ -20,7 +20,7 @@ export interface ResolvePlaybookContext {
20
20
  readonly caller?: string;
21
21
  }
22
22
  /**
23
- * 基于 PackageGraph 的规程寻址函数,正文统一由规程加载层提供。
24
- * 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
23
+ * 纯领域规程解析函数 resolvePlaybook。
24
+ * 基于 PackageGraph 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
25
25
  */
26
26
  export declare function resolvePlaybook(identifier: string, context: ResolvePlaybookContext): ResolvedPlaybook;
@@ -1,12 +1,48 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
1
3
  import { loadPlaybooks } from "../project/loader.js";
2
4
  import { getPackageSlug } from "../utils/index.js";
3
5
  import { ActionDockError, INVALID_ACTION_REF, NOT_FOUND, PACKAGE_NOT_FOUND } from "../errors.js";
4
6
  function getNodePlaybooks(node) {
5
- return loadPlaybooks(node.root, node.manifest.playbooksDir, node.manifest);
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;
6
42
  }
7
43
  /**
8
- * 基于 PackageGraph 的规程寻址函数,正文统一由规程加载层提供。
9
- * 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
44
+ * 纯领域规程解析函数 resolvePlaybook。
45
+ * 基于 PackageGraph 替代原 registry 内部的 resolvePlaybookProject 启发式搜索。
10
46
  */
11
47
  export function resolvePlaybook(identifier, context) {
12
48
  const str = identifier.trim();
@@ -51,15 +87,20 @@ export function resolvePlaybook(identifier, context) {
51
87
  // 3. 全局搜索
52
88
  const matches = [];
53
89
  for (const node of context.graph.packages.values()) {
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
- });
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
+ // 忽略单个包加载异常
63
104
  }
64
105
  }
65
106
  if (matches.length === 1) {
@@ -7,6 +7,7 @@ import { InMemoryEventSink } from "../runtime/events.js";
7
7
  import { ActionDockError, ACTION_CANCELLED, ACTION_FAILED, ACTION_NOT_FOUND, ACTION_TIMEOUT, EXECUTION_CONCURRENCY_LIMIT, EXECUTION_FAILED, IDEMPOTENCY_CONFLICT, INPUT_NOT_JSON, INPUT_VALIDATION_FAILED, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, OUTPUT_NOT_JSON, OUTPUT_VALIDATION_FAILED, RUN_REPOSITORY_UNAVAILABLE, SERVICE_CLOSED, UNHANDLED_EXECUTION_ERROR, describeActionLoadFailure, } from "../errors.js";
8
8
  import { resultStatusToRunStatus } from "../storage/types.js";
9
9
  import { createDefaultProcessOwner, } from "../invocation/types.js";
10
+ import { snapshotRunOrigin } from "../runtime/run-origin.js";
10
11
  import { ActionRegistry, isActionDefinitionObject, resolveAnonymousActionId, } from "../runtime/action-registry.js";
11
12
  import { buildInitialRunRecord, createRunFinalizer, createRunOrThrow, tryCreateRun, } from "../runtime/run-persistence.js";
12
13
  import { createActionContext } from "../runtime/context.js";
@@ -432,6 +433,7 @@ export class DefaultExecutionService {
432
433
  process: opts.process || this.process,
433
434
  owner: opts.owner,
434
435
  requestId: opts.requestId,
436
+ origin: snapshotRunOrigin(opts.origin),
435
437
  };
436
438
  }
437
439
  async raceExecution(args) {
@@ -548,6 +550,7 @@ export class DefaultExecutionService {
548
550
  progress: context.progress,
549
551
  process: effectiveProcess,
550
552
  owner: effectiveOwner,
553
+ origin: context.origin,
551
554
  };
552
555
  return await invoker(childAction, childInput, invocationContext);
553
556
  }
@@ -712,8 +715,10 @@ export class DefaultExecutionService {
712
715
  message: `Action '${targetActionId}' not found in package '${targetPackageId}'`,
713
716
  };
714
717
  const rootRunId = context.rootRunId || context.parentRunId || runId;
718
+ const origin = snapshotRunOrigin(context.origin);
715
719
  const initialRun = {
716
720
  id: runId,
721
+ ...(origin !== undefined ? { origin } : {}),
717
722
  rootRunId,
718
723
  parentRunId: context.parentRunId,
719
724
  packageId: targetPackageId,
@@ -777,6 +782,7 @@ export class DefaultExecutionService {
777
782
  runId,
778
783
  rootRunId,
779
784
  parentRunId: context.parentRunId,
785
+ origin: context.origin,
780
786
  ownerId: context.principalId || context.tenantId || this.ownerId,
781
787
  hostSessionId: context.hostSessionId || this.hostSessionId,
782
788
  hostPid: this.hostPid,
@@ -1,5 +1,5 @@
1
1
  import type { ActionDefinition, ActionRef, ExecutionEvent, ExecutionResult, JsonValue, Logger, ProcessAPI, RunRecord, RunStatus } from "@actiondock/sdk";
2
- import type { ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
2
+ import type { ProjectConfig } 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 | RuntimeProjectConfig;
43
+ projectConfig?: ProjectConfig;
44
44
  configOverrides?: Record<string, unknown>;
45
45
  actions?: Map<string, ActionDefinition>;
46
46
  process?: ProcessAPI;
package/dist/host/host.js CHANGED
@@ -7,6 +7,7 @@ import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
7
7
  import { hasPendingTransactions, isProjectLockHeld, recoverPendingTransactions } from "../project/transactions.js";
8
8
  import { listLinkedPackages } from "../registry/registry.js";
9
9
  import { InMemoryEventSink } from "../runtime/events.js";
10
+ import { snapshotRunOrigin } from "../runtime/run-origin.js";
10
11
  import { ActionDockError, ACTION_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_SUBRUN_LIMIT, EXECUTION_FAILED, INVALID_ACTION_REF, PACKAGE_ID_CONFLICT, PACKAGE_NOT_FOUND, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, SERVICE_CLOSED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
11
12
  import { InvocationPolicy } from "../invocation/policy.js";
12
13
  import { createAbortError } from "../ipc/service.js";
@@ -135,6 +136,7 @@ export class DefaultActionDockHost {
135
136
  };
136
137
  this.execution = {
137
138
  async run(ref, input, opts) {
139
+ const origin = snapshotRunOrigin(opts?.origin);
138
140
  await self.ensureInitialized();
139
141
  const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
140
142
  const cleanOpts = {
@@ -142,10 +144,12 @@ export class DefaultActionDockHost {
142
144
  timeoutMs: opts?.timeoutMs,
143
145
  config: opts?.config,
144
146
  requestId: opts?.requestId,
147
+ origin,
145
148
  };
146
149
  return self.runAction(actionRef, (input ?? {}), cleanOpts);
147
150
  },
148
151
  async start(ref, input, opts) {
152
+ const origin = snapshotRunOrigin(opts?.origin);
149
153
  await self.ensureInitialized();
150
154
  const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
151
155
  const cleanOpts = {
@@ -153,6 +157,7 @@ export class DefaultActionDockHost {
153
157
  timeoutMs: opts?.timeoutMs,
154
158
  config: opts?.config,
155
159
  requestId: opts?.requestId,
160
+ origin,
156
161
  };
157
162
  return self.startAction(actionRef, (input ?? {}), cleanOpts);
158
163
  },
@@ -634,6 +639,7 @@ export class DefaultActionDockHost {
634
639
  progress: context.progress,
635
640
  process: context.process,
636
641
  owner: targetOwner,
642
+ origin: context.origin,
637
643
  };
638
644
  const ticket = await targetRuntime.startInvocation(targetActionId, childInput, subInvocationContext);
639
645
  if (!ticket.result) {
@@ -752,14 +758,18 @@ export class DefaultActionDockHost {
752
758
  return describeVisiblePlaybook(this.listRuntimes(), id, this.visibility());
753
759
  }
754
760
  async runAction(ref, input, options = {}) {
761
+ const origin = snapshotRunOrigin(options.origin);
762
+ const effectiveOptions = { ...options, origin };
755
763
  await this.ensureInitialized();
756
- const ticket = await this.startAction(ref, input, options);
764
+ const ticket = await this.startAction(ref, input, effectiveOptions);
757
765
  if (!ticket.result) {
758
766
  throw new ActionDockError(EXECUTION_FAILED, `Execution ticket for run '${ticket.runId}' has no result Promise`);
759
767
  }
760
768
  return ticket.result;
761
769
  }
762
770
  async startAction(ref, input, options = {}) {
771
+ const origin = snapshotRunOrigin(options.origin);
772
+ const effectiveOptions = { ...options, origin };
763
773
  await this.ensureInitialized();
764
774
  if (this.isClosed) {
765
775
  throw new ActionDockError(SERVICE_CLOSED, "ActionDockHost is closed: new tasks rejected");
@@ -837,13 +847,14 @@ export class DefaultActionDockHost {
837
847
  const rootContext = createRootInvocationContext({
838
848
  targetPackage: targetRuntime.identity,
839
849
  callStack: [rootKey],
840
- signal: options.signal,
841
- timeoutMs: options.timeoutMs,
842
- config: options.config,
843
- requestId: options.requestId,
850
+ signal: effectiveOptions.signal,
851
+ timeoutMs: effectiveOptions.timeoutMs,
852
+ config: effectiveOptions.config,
853
+ requestId: effectiveOptions.requestId,
844
854
  hostSessionId: this.hostSessionId,
845
855
  maxCallDepth: this.maxCallDepth,
846
856
  process: this.options.process,
857
+ origin: effectiveOptions.origin,
847
858
  });
848
859
  // 调度目标包执行服务
849
860
  return targetRuntime.startInvocation(targetActionId, input, rootContext);
@@ -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>;