@actiondock/core 2.15.0 → 2.16.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.
@@ -1,5 +1,7 @@
1
- import { resolve } from "node:path";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
2
3
  import { loadPlaybooks } from "../project/loader.js";
4
+ import { MANIFEST_FILE_NAME } from "../project/manifest.js";
3
5
  /**
4
6
  * 将增量声明合并到既有 Action 规范之上(纯函数,单一事实源)。
5
7
  *
@@ -84,21 +86,67 @@ export function buildStaticActionMap(input) {
84
86
  }
85
87
  /**
86
88
  * 静态读取并聚合指定包的 Playbook 规范索引。
87
- * 统一复用 project/loader 的 loadPlaybooks(单一事实源),不再自行读文件或吞异常。
89
+ *
90
+ * 聚合来源:磁盘规程目录扫描(以 actiondock.json 声明为事实源)与
91
+ * 项目配置中声明的 playbooks;同键时配置声明覆盖磁盘扫描结果。
88
92
  */
89
93
  export function buildStaticPlaybookMap(input) {
90
94
  const { packageRoot, packageId, projectConfig } = input;
91
95
  const map = new Map();
92
- const loaded = loadPlaybooks(packageRoot || "", projectConfig.playbooksDir, projectConfig);
93
- for (const [id, def] of loaded) {
94
- map.set(id, {
95
- id: def.id,
96
- packageId,
97
- description: def.description,
98
- actions: def.actions,
99
- content: def.content,
100
- filePath: def.filePath,
101
- });
96
+ // 扫描磁盘规程文件
97
+ if (packageRoot) {
98
+ const playbooksDir = projectConfig?.playbooksDir || "playbooks";
99
+ const dirPath = join(packageRoot, playbooksDir);
100
+ const manifestPath = join(packageRoot, MANIFEST_FILE_NAME);
101
+ if (existsSync(manifestPath) || existsSync(dirPath)) {
102
+ try {
103
+ const loaded = loadPlaybooks(packageRoot, playbooksDir);
104
+ for (const [id, def] of loaded) {
105
+ map.set(id, {
106
+ id: def.id,
107
+ packageId,
108
+ description: def.description,
109
+ actions: def.actions,
110
+ content: def.content,
111
+ filePath: def.filePath,
112
+ });
113
+ }
114
+ }
115
+ catch (err) {
116
+ // 规程加载失败(清单声明非法、规程文件损坏等)输出告警并跳过磁盘部分,保持返回可用列表
117
+ console.warn(`[App] Failed to load playbooks for package '${packageId}' from '${dirPath}': ${err?.message || String(err)}`);
118
+ }
119
+ }
120
+ }
121
+ // 读取项目配置文件中的 playbooks
122
+ if (projectConfig && projectConfig.playbooks) {
123
+ const rawPlaybooks = projectConfig.playbooks;
124
+ if (typeof rawPlaybooks === "object" && rawPlaybooks !== null) {
125
+ for (const [id, item] of Object.entries(rawPlaybooks)) {
126
+ const existing = map.get(id);
127
+ let content = item.content ?? existing?.content ?? "";
128
+ let filePath = item.entry && packageRoot
129
+ ? resolve(packageRoot, item.entry)
130
+ : existing?.filePath;
131
+ if (!content && filePath && existsSync(filePath)) {
132
+ try {
133
+ content = readFileSync(filePath, "utf-8");
134
+ }
135
+ catch (err) {
136
+ // 读取失败(权限、IO 错误等)输出告警并保持空正文,不再无声吞没
137
+ console.warn(`[App] Failed to read playbook '${id}' content from '${filePath}' in package '${packageId}': ${err?.message || String(err)}`);
138
+ }
139
+ }
140
+ map.set(id, {
141
+ id,
142
+ packageId,
143
+ description: item.description ?? existing?.description,
144
+ actions: item.actions ?? existing?.actions,
145
+ content,
146
+ filePath,
147
+ });
148
+ }
149
+ }
102
150
  }
103
151
  return map;
104
152
  }
@@ -1,7 +1,7 @@
1
1
  import type { ActionDefinition, ExecutionEvent, ExecutionResult, JsonValue, Logger, ProcessAPI, RunRecord } from "@actiondock/sdk";
2
2
  import type { ActionInvoker, CancelResult, ExecutionTicket, LocalActionResolver } from "../execution/types.js";
3
3
  import type { InvocationContext, RunOptions } from "../invocation/types.js";
4
- import type { ConfigItemDefinition, ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
4
+ import type { ConfigItemDefinition, ProjectConfig } from "../project/types.js";
5
5
  import type { EventSink } from "../runtime/events.js";
6
6
  import type { RuntimePlatform, StorageFactory, StorageFactoryOptions } from "../platform/types.js";
7
7
  import type { RuntimeStorage, StateEntry } from "../storage/types.js";
@@ -156,8 +156,8 @@ export interface StateOptions {
156
156
  export interface PackageRuntimeOptions {
157
157
  /** 项目根目录绝对物理路径 */
158
158
  packageRoot?: string;
159
- /** 项目配置文件 (actiondock.json) 对象或运行时规程来源配置 */
160
- projectConfig?: ProjectConfig | RuntimeProjectConfig;
159
+ /** 项目配置文件 (actiondock.json) 对象 */
160
+ projectConfig?: ProjectConfig;
161
161
  /** 数据持久化存储目录 */
162
162
  dataDir?: string;
163
163
  /** 自定义家目录绝对路径 */
@@ -241,8 +241,8 @@ export interface PackageRuntime {
241
241
  readonly generationId?: string;
242
242
  /** 包根目录绝对物理路径 */
243
243
  readonly packageRoot?: string;
244
- /** 项目配置对象(actiondock.json 解析结果或运行时配置) */
245
- readonly projectConfig: ProjectConfig | RuntimeProjectConfig;
244
+ /** 项目配置对象(actiondock.json 解析结果) */
245
+ readonly projectConfig: ProjectConfig;
246
246
  /** 是否采用纯内存运行模式 */
247
247
  readonly inMemory?: boolean;
248
248
  /**
@@ -3,7 +3,6 @@ export * from "./init.js";
3
3
  export * from "./loader.js";
4
4
  export * from "./lockfile.js";
5
5
  export * from "./manifest.js";
6
- export * from "./skill-dir.js";
7
6
  export { SNAPSHOT_TRACKED_FILES, type TransactionFileRecord, type TransactionMetadata, type ProjectTransaction, isProjectLockHeld, acquireProjectLock, runFrozenInstall, hasPendingTransactions, beginTransaction, recoverPendingTransactions, } from "./transactions.js";
8
7
  export * from "./types.js";
9
8
  export * from "./types-generator.js";
@@ -3,7 +3,6 @@ export * from "./init.js";
3
3
  export * from "./loader.js";
4
4
  export * from "./lockfile.js";
5
5
  export * from "./manifest.js";
6
- export * from "./skill-dir.js";
7
6
  export { SNAPSHOT_TRACKED_FILES, isProjectLockHeld, acquireProjectLock, runFrozenInstall, hasPendingTransactions, beginTransaction, recoverPendingTransactions, } from "./transactions.js";
8
7
  export * from "./types.js";
9
8
  export * from "./types-generator.js";
@@ -86,10 +86,10 @@ export function initProject(targetDir, options = {}) {
86
86
  node: ">=24.12.0",
87
87
  },
88
88
  dependencies: {
89
- "@actiondock/sdk": "^2.15.0",
89
+ "@actiondock/sdk": "^2.16.0",
90
90
  },
91
91
  devDependencies: {
92
- "@actiondock/testing": "^2.15.0",
92
+ "@actiondock/testing": "^2.16.0",
93
93
  "@types/node": "^22.13.0",
94
94
  "tsx": "^4.19.0",
95
95
  "typescript": "^5.7.0",
@@ -1,6 +1,6 @@
1
1
  import type { ActionDefinition } from "@actiondock/sdk";
2
2
  import { type ModuleLoader } from "../platform/module-loader.js";
3
- import type { ActionDockManifest, PlaybookDefinition, PlaybookManifestEntry, PlaybookSourceManifest, ProjectConfig, RuntimeProjectConfig } from "./types.js";
3
+ import type { ActionDockManifest, PlaybookDefinition, PlaybookManifestEntry, ProjectConfig } from "./types.js";
4
4
  /**
5
5
  * 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
6
6
  *
@@ -16,8 +16,6 @@ export declare function findProjectRoot(cwd?: string): string | null;
16
16
  * @throws {Error} 若文件不存在或 JSON 格式错误、缺失必要字段
17
17
  */
18
18
  export declare function loadProjectConfig(projectRoot: string): ProjectConfig;
19
- /** 在运行时装配边界补全未声明的磁盘规程;显式规程来源(包括空集合)始终优先。 */
20
- export declare function completeRuntimeProjectConfig(projectRoot: string | undefined, config: RuntimeProjectConfig): RuntimeProjectConfig;
21
19
  export declare const ALLOWED_INSTALLERS: Set<string>;
22
20
  /**
23
21
  * 探测宿主系统中可用的包管理工具。
@@ -73,4 +71,4 @@ export declare function parsePlaybookContent(content: string, filePath: string,
73
71
  * @param customManifest 可选的显式清单对象(若未提供则从磁盘加载)
74
72
  * @returns Map<PlaybookId, PlaybookDefinition> 映射
75
73
  */
76
- export declare function loadPlaybooks(projectRoot: string, _playbooksDir?: string, customManifest?: ActionDockManifest | PlaybookSourceManifest | null): Map<string, PlaybookDefinition>;
74
+ export declare function loadPlaybooks(projectRoot: string, _playbooksDir?: string, customManifest?: ActionDockManifest | null): Map<string, PlaybookDefinition>;
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { basename, dirname, join, resolve } from "node:path";
4
4
  import { NodeModuleLoader, unwrapDefaultExport } from "../platform/module-loader.js";
5
5
  import { ActionDockError, ACTION_LOAD_FAILED, describeActionLoadFailure, INVALID_ACTION_ID, INVALID_JSON, INVALID_PACKAGE_ID, INVALID_PLAYBOOK_ID, MANIFEST_LOAD_FAILED, PLAYBOOK_NOT_FOUND, PROJECT_CONFIG_NOT_FOUND, } from "../errors.js";
6
- import { loadManifest, normalizeFilePlaybookEntries, ACTION_ID_REGEX, PLAYBOOK_ID_REGEX } from "./manifest.js";
6
+ import { loadManifest, ACTION_ID_REGEX, PLAYBOOK_ID_REGEX } from "./manifest.js";
7
7
  /**
8
8
  * 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
9
9
  *
@@ -68,7 +68,7 @@ export function loadProjectConfig(projectRoot) {
68
68
  if (parsed.playbooksDir) {
69
69
  assertWithinProjectRoot(projectRoot, parsed.playbooksDir, "playbooksDir");
70
70
  }
71
- return normalizeFilePlaybookEntries(parsed);
71
+ return parsed;
72
72
  }
73
73
  catch (err) {
74
74
  if (err instanceof ActionDockError) {
@@ -77,15 +77,6 @@ export function loadProjectConfig(projectRoot) {
77
77
  throw new ActionDockError(INVALID_JSON, `Failed to parse actiondock.json: ${err.message}`);
78
78
  }
79
79
  }
80
- /** 在运行时装配边界补全未声明的磁盘规程;显式规程来源(包括空集合)始终优先。 */
81
- export function completeRuntimeProjectConfig(projectRoot, config) {
82
- if (!projectRoot || config.playbooks !== undefined)
83
- return config;
84
- const diskManifest = loadManifest(projectRoot);
85
- return diskManifest?.playbooks === undefined
86
- ? config
87
- : { ...config, playbooks: diskManifest.playbooks };
88
- }
89
80
  export const ALLOWED_INSTALLERS = new Set(["npm", "bun"]);
90
81
  /**
91
82
  * 异步探测包管理器可用性(通过执行 --version 并检查退出码)。
@@ -317,38 +308,43 @@ export function parsePlaybookContent(content, filePath, metadata) {
317
308
  */
318
309
  export function loadPlaybooks(projectRoot, _playbooksDir = "playbooks", customManifest) {
319
310
  const playbooks = new Map();
320
- const isExplicitSource = customManifest !== undefined;
321
- const manifest = isExplicitSource
322
- ? customManifest
323
- : projectRoot ? loadManifest(projectRoot) : null;
324
- for (const [playbookId, entry] of Object.entries(manifest?.playbooks || {})) {
325
- if (!PLAYBOOK_ID_REGEX.test(playbookId)) {
326
- throw new ActionDockError(INVALID_PLAYBOOK_ID, `Invalid playbook ID '${playbookId}' in manifest. Playbook IDs must match ${PLAYBOOK_ID_REGEX}`);
311
+ let manifest = null;
312
+ if (customManifest !== undefined) {
313
+ manifest = customManifest;
314
+ }
315
+ else {
316
+ try {
317
+ manifest = loadManifest(projectRoot);
327
318
  }
328
- let content;
329
- let filePath;
330
- if (isExplicitSource && "content" in entry && typeof entry.content === "string") {
331
- // 真实内联来源(包括空字符串)无需访问磁盘。
332
- content = entry.content;
333
- filePath = entry.entry ? (projectRoot ? resolve(projectRoot, entry.entry) : entry.entry) : "";
319
+ catch (err) {
320
+ // 容错与可观测性取舍说明:
321
+ // - 工程无清单时 loadManifest 返回 null,按空规程集处理属正常场景;
322
+ // - 清单文件存在但内容损坏或解析失败时,记录可观测警告后安全降级,避免静默吞没异常。
323
+ const reason = err instanceof Error ? err.message : String(err ?? "Unknown error");
324
+ process.stderr.write(`[actiondock] Warning: Failed to load manifest in ${projectRoot}: ${reason}\n`);
325
+ return playbooks;
334
326
  }
335
- else {
336
- if (!entry.entry || typeof entry.entry !== "string") {
337
- throw new ActionDockError(PLAYBOOK_NOT_FOUND, `Playbook entry for '${playbookId}' must specify a valid file path or explicit inline content`);
327
+ }
328
+ // 以 actiondock.json 为唯一事实源
329
+ if (manifest?.playbooks && Object.keys(manifest.playbooks).length > 0) {
330
+ for (const [playbookId, pbEntry] of Object.entries(manifest.playbooks)) {
331
+ if (!PLAYBOOK_ID_REGEX.test(playbookId)) {
332
+ throw new ActionDockError(INVALID_PLAYBOOK_ID, `Invalid playbook ID '${playbookId}' in actiondock.json. Playbook IDs must match ${PLAYBOOK_ID_REGEX}`);
338
333
  }
339
- filePath = resolve(projectRoot, entry.entry);
340
- assertPathWithinRoot(projectRoot, filePath, `playbook entry '${entry.entry}'`);
341
- if (!existsSync(filePath)) {
342
- throw new ActionDockError(PLAYBOOK_NOT_FOUND, `Playbook file '${entry.entry}' for '${playbookId}' not found in ${projectRoot}`);
334
+ const fullPath = resolve(projectRoot, pbEntry.entry);
335
+ assertPathWithinRoot(projectRoot, fullPath, `playbook entry '${pbEntry.entry}'`);
336
+ if (!existsSync(fullPath)) {
337
+ throw new ActionDockError(PLAYBOOK_NOT_FOUND, `Playbook file '${pbEntry.entry}' for '${playbookId}' not found in ${projectRoot}`);
343
338
  }
344
- content = readFileSync(filePath, "utf-8");
339
+ const content = readFileSync(fullPath, "utf-8");
340
+ const def = parsePlaybookContent(content, fullPath, {
341
+ id: playbookId,
342
+ name: pbEntry?.name,
343
+ description: pbEntry.description,
344
+ actions: pbEntry.actions,
345
+ });
346
+ playbooks.set(playbookId, def);
345
347
  }
346
- playbooks.set(playbookId, parsePlaybookContent(content, filePath, {
347
- id: playbookId,
348
- name: entry.name,
349
- description: entry.description,
350
- actions: entry.actions,
351
- }));
352
348
  }
353
349
  return playbooks;
354
350
  }
@@ -2,8 +2,6 @@ import type { ActionDockManifest } from "./types.js";
2
2
  export declare const MANIFEST_FILE_NAME = "actiondock.json";
3
3
  export declare const ACTION_ID_REGEX: RegExp;
4
4
  export declare const PLAYBOOK_ID_REGEX: RegExp;
5
- /** 磁盘规程始终采用文件声明,不能把非契约 content 字段带入运行时内联来源。 */
6
- export declare function normalizeFilePlaybookEntries(manifest: ActionDockManifest): ActionDockManifest;
7
5
  /**
8
6
  * 读取并解析项目的声明式清单文件(actiondock.json 为唯一事实源)。
9
7
  * 若文件不存在则返回 null。
@@ -5,22 +5,6 @@ import { ActionDockError, MANIFEST_CORRUPTED, MANIFEST_INVALID, MANIFEST_READ_FA
5
5
  export const MANIFEST_FILE_NAME = "actiondock.json";
6
6
  export const ACTION_ID_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
7
7
  export const PLAYBOOK_ID_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
8
- /** 磁盘规程始终采用文件声明,不能把非契约 content 字段带入运行时内联来源。 */
9
- export function normalizeFilePlaybookEntries(manifest) {
10
- if (!manifest.playbooks || typeof manifest.playbooks !== "object" || Array.isArray(manifest.playbooks)) {
11
- return manifest;
12
- }
13
- let changed = false;
14
- const playbooks = Object.fromEntries(Object.entries(manifest.playbooks).map(([id, entry]) => {
15
- if (entry && typeof entry === "object" && "content" in entry) {
16
- const { content: _inlineContent, ...fileEntry } = entry;
17
- changed = true;
18
- return [id, fileEntry];
19
- }
20
- return [id, entry];
21
- }));
22
- return changed ? { ...manifest, playbooks } : manifest;
23
- }
24
8
  /**
25
9
  * 读取并解析项目的声明式清单文件(actiondock.json 为唯一事实源)。
26
10
  * 若文件不存在则返回 null。
@@ -54,7 +38,7 @@ export function loadManifest(projectRoot) {
54
38
  if (!parsed.id) {
55
39
  parsed.id = basename(projectRoot);
56
40
  }
57
- return normalizeFilePlaybookEntries(parsed);
41
+ return parsed;
58
42
  }
59
43
  /**
60
44
  * 保存声明式清单文件至项目根目录(actiondock.json)。
@@ -53,25 +53,6 @@ export interface PlaybookManifestEntry {
53
53
  /** 该 Playbook 所依赖/调用的 Action ID 列表 */
54
54
  actions?: string[];
55
55
  }
56
- /**
57
- * 内存运行时或测试中使用的内联 Playbook 声明项。
58
- */
59
- export interface InlinePlaybookSourceEntry {
60
- /** Playbook Markdown 正文内容(纯 Markdown 规程内容,含空字符串) */
61
- content: string;
62
- /** 可选的虚拟入口相对路径(如 "playbooks/calc-sop.md") */
63
- entry?: string;
64
- /** Playbook 展示名称,缺省回落文件名派生 */
65
- name?: string;
66
- /** Playbook 任务描述 */
67
- description?: string;
68
- /** 该 Playbook 所依赖/调用的 Action ID 列表 */
69
- actions?: string[];
70
- }
71
- /**
72
- * Playbook 来源定义条目(支持磁盘清单声明项与内存内联正文声明项)。
73
- */
74
- export type PlaybookSourceEntry = PlaybookManifestEntry | InlinePlaybookSourceEntry;
75
56
  /**
76
57
  * ActionDock 声明式元数据清单契约(actiondock.json 为唯一事实源)。
77
58
  */
@@ -94,8 +75,6 @@ export interface ActionDockManifest {
94
75
  actions?: Record<string, ActionManifestEntry>;
95
76
  /** 声明的 Playbooks 集合(唯一事实源) */
96
77
  playbooks?: Record<string, PlaybookManifestEntry>;
97
- /** 包级静态依赖引用,与 Action 的 uses 共用引用语义。 */
98
- uses?: string[];
99
78
  /** 跨包外部依赖映射(逻辑包 ID 到 npm 包名) */
100
79
  dependencies?: Record<string, string>;
101
80
  /** 框架导出与分发边界文件列表 */
@@ -112,20 +91,10 @@ export interface ActionDockManifest {
112
91
  [key: string]: unknown;
113
92
  };
114
93
  }
115
- /**
116
- * 内存运行时或图模型中支持内联正文的规程来源清单契约。
117
- */
118
- export type PlaybookSourceManifest = Omit<ActionDockManifest, "playbooks"> & {
119
- playbooks?: Record<string, PlaybookSourceEntry>;
120
- };
121
94
  /**
122
95
  * ActionDock 项目根配置文件契约(等同于 ActionDockManifest,actiondock.json 为单一事实源)。
123
96
  */
124
97
  export type ProjectConfig = ActionDockManifest;
125
- /**
126
- * 内存运行时或图模型中支持内联正文的项目配置契约。
127
- */
128
- export type RuntimeProjectConfig = PlaybookSourceManifest;
129
98
  /**
130
99
  * 解析后的完整 Playbook 定义对象。
131
100
  */
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync, readFileSync, renameSync, statSync } from "node:
2
2
  import { readFile, rename, writeFile } from "node:fs/promises";
3
3
  import { basename, dirname, join, resolve, sep } from "node:path";
4
4
  import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
5
- import { inspectLocalPackageDirectory, isExplicitPath } from "../project/skill-dir.js";
6
5
  import { getActionDockHome, getPackageSlug } from "../utils/index.js";
7
6
  import { ActionDockError, MANIFEST_NOT_FOUND, REGISTRY_CORRUPTED } from "../errors.js";
8
7
  import { withRegistryLock } from "./lock.js";
@@ -400,42 +399,31 @@ export function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
400
399
  const resolvedPath = resolve(baseDir, packageIdOrPath);
401
400
  // 检查 packageIdOrPath 是否为磁盘上现存的目录或文件路径
402
401
  if (existsSync(resolvedPath)) {
403
- let info;
404
402
  try {
405
- info = statSync(resolvedPath);
406
- }
407
- catch (err) {
408
- if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
409
- throw err;
410
- }
411
- }
412
- if (info) {
403
+ const info = statSync(resolvedPath);
413
404
  const targetDir = info.isDirectory() ? resolvedPath : dirname(resolvedPath);
414
- // 普通开发包根目录:直接包含 actiondock.json 清单。
415
- // 普通清单根目录定位与封闭 Skill 执行校验分离,不能被当成只含一个包的 Skill 闭包拒绝。
416
405
  if (existsSync(join(targetDir, "actiondock.json"))) {
417
- loadProjectConfig(targetDir);
418
406
  return targetDir;
419
407
  }
420
- // 复合技能布局:根目录下存在 packages/ 目录且包含有效子包。
421
- const localSkill = inspectLocalPackageDirectory(targetDir);
422
- if (localSkill) {
423
- return localSkill.root;
424
- }
425
- // 具体文件查找所属工程根目录:仅当 packageIdOrPath 指向具体文件(非目录)时,
426
- // 才尝试向上查找所属工程根目录;目录形态绝不向上回退,防止显式目录越界命中父级工程。
427
- if (!info.isDirectory()) {
428
- const parentRoot = findProjectRoot(targetDir);
429
- if (parentRoot) {
430
- loadProjectConfig(parentRoot);
431
- return parentRoot;
432
- }
408
+ const parentRoot = findProjectRoot(targetDir);
409
+ if (parentRoot) {
410
+ return parentRoot;
433
411
  }
434
412
  }
413
+ catch {
414
+ // stat 失败(如并发删除):忽略并继续走标识符解析路径
415
+ }
435
416
  }
436
417
  // 显式路径形态但未解析成功的场景:
437
418
  // scoped package 标识符(如 @team/tools)虽含 '/' 但属于包 ID 而非文件路径
438
- if (isExplicitPath(packageIdOrPath)) {
419
+ const isScopedPackage = packageIdOrPath.startsWith("@");
420
+ const isExplicitPath = !isScopedPackage &&
421
+ (packageIdOrPath.startsWith(".") ||
422
+ packageIdOrPath.startsWith("/") ||
423
+ packageIdOrPath.startsWith("~") ||
424
+ packageIdOrPath.includes("/") ||
425
+ packageIdOrPath.includes("\\"));
426
+ if (isExplicitPath) {
439
427
  // 显式路径不存在或不是 ActionDock 项目时必须失败,禁止静默回退
440
428
  return null;
441
429
  }
@@ -1,5 +1,5 @@
1
1
  import type { ActionContext, ActionRef, Config, Logger, ProcessAPI, ProgressReporter, StateStore } from "@actiondock/sdk";
2
- import type { ProjectConfig, RuntimeProjectConfig } from "../project/types.js";
2
+ import type { ProjectConfig } from "../project/types.js";
3
3
  import type { RuntimeStorage } from "../storage/types.js";
4
4
  import { ProcessManager, type ProcessOwner } from "../process/index.js";
5
5
  /**
@@ -17,7 +17,7 @@ export declare class RuntimeConfig implements Config {
17
17
  private storage;
18
18
  private projectConfig?;
19
19
  private globalStorage?;
20
- constructor(storage: RuntimeStorage, overrides?: Record<string, unknown>, projectConfig?: ProjectConfig | RuntimeProjectConfig, globalStorage?: RuntimeStorage);
20
+ constructor(storage: RuntimeStorage, overrides?: Record<string, unknown>, projectConfig?: ProjectConfig, globalStorage?: RuntimeStorage);
21
21
  get<T = unknown>(key: string): T | undefined;
22
22
  get<T = unknown>(key: string, defaultValue: T): T;
23
23
  has(key: string): boolean;
@@ -70,7 +70,7 @@ export interface ContextOptions {
70
70
  storage: RuntimeStorage;
71
71
  globalStorage?: RuntimeStorage;
72
72
  overrides?: Record<string, unknown>;
73
- projectConfig?: ProjectConfig | RuntimeProjectConfig;
73
+ projectConfig?: ProjectConfig;
74
74
  parentRunId?: string;
75
75
  runId?: string;
76
76
  rootRunId?: string;
@@ -29,7 +29,7 @@ export async function handleList(ctx, subArgs) {
29
29
  const effectiveIntent = intent || (positionalPatterns.length > 0 ? positionalPatterns.join("|") : undefined);
30
30
  const actions = await ctx.service.discovery.listActions();
31
31
  const list = actions.map((a) => ({
32
- id: ctx.options.multiPackage && a.packageId && !a.id.includes("/") ? `${a.packageId}/${a.id}` : a.id,
32
+ id: a.id,
33
33
  description: a.description || "",
34
34
  }));
35
35
  const filterRes = filterWithFallbackInfo(list, effectiveIntent, [(a) => a.id, (a) => a.description], !noFallback);
@@ -66,7 +66,7 @@ export async function handleDescribe(ctx, subArgs) {
66
66
  });
67
67
  }
68
68
  const payload = buildActionDescribePayload(action, {
69
- packageId: action.packageId || ctx.options.packageId,
69
+ packageId: ctx.options.packageId,
70
70
  });
71
71
  if (isJson) {
72
72
  ctx.writeOut(JSON.stringify(payload, null, 2));
@@ -274,9 +274,6 @@ function isServiceCapabilityError(err) {
274
274
  code === "TARGET_CAPABILITY_UNAVAILABLE");
275
275
  }
276
276
  export async function handleConfig(ctx, subArgs) {
277
- if (ctx.options.multiPackage) {
278
- return ctx.emitError(subArgs.includes("--json"), "CAPABILITY_UNAVAILABLE", "Multi-package entries do not support config/state management; configure actions using --config and --data-dir.", ExitCode.INVALID_ARGUMENT);
279
- }
280
277
  const sub = subArgs[0] || "list";
281
278
  if (!ctx.service.management) {
282
279
  ctx.writeErr("Error: management port is not enabled on this service");
@@ -353,9 +350,6 @@ function decodeStateKeyArg(key) {
353
350
  }
354
351
  }
355
352
  export async function handleState(ctx, subArgs) {
356
- if (ctx.options.multiPackage) {
357
- return ctx.emitError(subArgs.includes("--json"), "CAPABILITY_UNAVAILABLE", "Multi-package entries do not support config/state management; configure actions using --config and --data-dir.", ExitCode.INVALID_ARGUMENT);
358
- }
359
353
  const sub = subArgs[0] || "list";
360
354
  let namespace;
361
355
  let isAll = false;
@@ -24,8 +24,6 @@ export interface InvocationControl {
24
24
  export interface StandaloneDispatcherOptions {
25
25
  /** 所属 Package ID */
26
26
  packageId: string;
27
- /** 多包入口使用完全限定发现标识,不以展示名操作配置和状态。 */
28
- multiPackage?: boolean;
29
27
  /** 版本号 */
30
28
  version: string;
31
29
  /** 描述信息 */
@@ -6,11 +6,10 @@ export interface SemVer {
6
6
  minor: number;
7
7
  patch: number;
8
8
  prerelease?: string;
9
- build?: string;
10
9
  }
11
10
  /**
12
11
  * 解析基础 SemVer 版本字符串(单一事实源)。
13
- * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀与构建元数据;
12
+ * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
14
13
  * 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
15
14
  *
16
15
  * 本模块不依赖任何 Node 内建与 core 领域模块,可在无 loader 的原生
@@ -1,25 +1,19 @@
1
1
  /**
2
2
  * 解析基础 SemVer 版本字符串(单一事实源)。
3
- * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀与构建元数据;
3
+ * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
4
4
  * 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
5
5
  *
6
6
  * 本模块不依赖任何 Node 内建与 core 领域模块,可在无 loader 的原生
7
7
  * Node 直接以 .ts 形态导入(工程脚本域复用此处单一事实源)。
8
8
  */
9
9
  export function parseSemVer(v) {
10
- const match = v.trim().replace(/^[v=]/, "").match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/);
10
+ const match = v.trim().replace(/^[v=]/, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
11
11
  if (!match)
12
12
  return null;
13
- const [major, minor, patch] = match.slice(1, 4).map(Number);
14
- if (![major, minor, patch].every(Number.isSafeInteger))
15
- return null;
16
- if (match[4]?.split(".").some((id) => /^\d+$/.test(id) && id.length > 1 && id.startsWith("0")))
17
- return null;
18
13
  return {
19
- major,
20
- minor,
21
- patch,
14
+ major: parseInt(match[1], 10),
15
+ minor: parseInt(match[2], 10),
16
+ patch: parseInt(match[3], 10),
22
17
  prerelease: match[4],
23
- ...(match[5] === undefined ? {} : { build: match[5] }),
24
18
  };
25
19
  }
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export declare const ACTIONDOCK_VERSION = "2.15.0";
4
+ export declare const ACTIONDOCK_VERSION = "2.16.0";
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export const ACTIONDOCK_VERSION = "2.15.0";
4
+ export const ACTIONDOCK_VERSION = "2.16.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,7 +59,7 @@
59
59
  "test": "node ../../scripts/run-tests.ts"
60
60
  },
61
61
  "dependencies": {
62
- "@actiondock/sdk": "^2.15.0",
62
+ "@actiondock/sdk": "^2.16.0",
63
63
  "ajv": "^8.17.1",
64
64
  "ajv-formats": "^3.0.1",
65
65
  "undici": "^7.29.1"