@actiondock/core 2.12.0 → 2.14.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 (38) hide show
  1. package/dist/catalog/graph.d.ts +2 -4
  2. package/dist/catalog/graph.js +182 -32
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +1 -1
  5. package/dist/input/describe.d.ts +12 -1
  6. package/dist/input/describe.js +55 -1
  7. package/dist/input/flat-materializer.d.ts +24 -0
  8. package/dist/input/flat-materializer.js +51 -14
  9. package/dist/input/index.d.ts +1 -0
  10. package/dist/input/index.js +1 -0
  11. package/dist/input/input-resolver.d.ts +10 -2
  12. package/dist/input/input-resolver.js +123 -4
  13. package/dist/input/output-contract.d.ts +72 -0
  14. package/dist/input/output-contract.js +274 -0
  15. package/dist/input/stdin-input.d.ts +11 -1
  16. package/dist/input/stdin-input.js +15 -3
  17. package/dist/project/index.d.ts +2 -1
  18. package/dist/project/index.js +3 -2
  19. package/dist/project/init.js +2 -2
  20. package/dist/project/skill-dir.d.ts +56 -0
  21. package/dist/project/skill-dir.js +214 -0
  22. package/dist/project/types.d.ts +2 -0
  23. package/dist/registry/registry.js +27 -15
  24. package/dist/runtime/action-collection.js +3 -0
  25. package/dist/runtime/standalone-commands.js +18 -3
  26. package/dist/runtime/standalone-help.js +2 -0
  27. package/dist/runtime/standalone.d.ts +2 -0
  28. package/dist/storage/sqlite-driver.d.ts +0 -1
  29. package/dist/storage/sqlite-driver.js +0 -1
  30. package/dist/utils/index.d.ts +0 -1
  31. package/dist/utils/index.js +0 -1
  32. package/dist/utils/semver.d.ts +2 -1
  33. package/dist/utils/semver.js +11 -5
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +2 -7
  37. package/dist/utils/warning.d.ts +0 -1
  38. package/dist/utils/warning.js +0 -52
@@ -0,0 +1,56 @@
1
+ import type { ProjectConfig } from "./types.js";
2
+ /**
3
+ * 本地封闭包条目。
4
+ */
5
+ export interface LocalPackageEntry {
6
+ packageId: string;
7
+ packageRoot: string;
8
+ projectConfig: ProjectConfig;
9
+ }
10
+ /**
11
+ * 本地技能 / 包目录发现视图。
12
+ */
13
+ export interface LocalSkillDirectory {
14
+ type: "single" | "composite";
15
+ root: string;
16
+ packages: LocalPackageEntry[];
17
+ }
18
+ /**
19
+ * 判定路径字符串是否为显式文件/目录路径形态(而非包标识符)。
20
+ *
21
+ * 规则:
22
+ * - 以 .、/、~ 开头;
23
+ * - 包含 / 或 \\,且不是 scoped 包(@scope/name 仅含一个 /);
24
+ * - scoped 包若包含反斜杠或多于一个 /,亦视作路径。
25
+ */
26
+ export declare function isExplicitPath(p: string): boolean;
27
+ /**
28
+ * 对明确的本地封闭包集合实施一致性校验。
29
+ *
30
+ * 校验规则:
31
+ * - 重复包标识防护:集合内不得存在相同 packageId;
32
+ * - 依赖存在性与版本兼容性:声明的直接依赖必须在集合内存在且满足语义版本约束(复用 PackageGraphBuilder);
33
+ * - 跨包 uses 目标完整性:uses 声明引用的目标包与目标 Action 必须在集合内存在。
34
+ *
35
+ * @param packages 封闭包条目集合
36
+ */
37
+ export declare function validateLocalSkillPackages(packages: Array<{
38
+ packageId: string;
39
+ packageRoot: string;
40
+ projectConfig: ProjectConfig;
41
+ }>): void;
42
+ /**
43
+ * 探测指定路径是否为本地 Skill / 包目录(支持单包根目录与 packages/* 复合套件布局)。
44
+ *
45
+ * 封闭包装配与校验规则:
46
+ * - 边界安全防护:packages 目录、各子包目录与 actiondock.json 清单真实路径必须在 Skill 根边界内,禁止越界符号链接;
47
+ * - 单包布局:目录根部存在 actiondock.json,解析为包含单一包的 LocalSkillDirectory;
48
+ * - 复合布局:目录根部存在 packages/ 目录,遍历其直接子目录,收集包含 actiondock.json 的子包;
49
+ * - 集合一致性校验:通过 validateLocalSkillPackages 严格核查依赖闭包、版本兼容与 uses 目标;
50
+ * - 异常透传:坏清单或权限异常原样抛出,严禁静默回退 Registry;
51
+ * - 若非候选技能目录,返回 null。
52
+ *
53
+ * @param directoryPath 待检查的目录路径(相对路径将基于 cwd 解析)
54
+ * @param cwd 解析基准工作目录(默认为 process.cwd())
55
+ */
56
+ export declare function inspectLocalPackageDirectory(directoryPath: string, cwd?: string): LocalSkillDirectory | null;
@@ -0,0 +1,214 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { ActionDockError, ACTION_NOT_FOUND, PACKAGE_NOT_FOUND, PACKAGE_ID_CONFLICT, } from "../errors.js";
4
+ import { loadProjectConfig } from "./loader.js";
5
+ import { PackageGraphBuilder } from "../catalog/graph.js";
6
+ import { parseActionRef } from "../catalog/resolve-action.js";
7
+ import { createPackageIdentity } from "../runtime/identity.js";
8
+ /**
9
+ * 判定路径字符串是否为显式文件/目录路径形态(而非包标识符)。
10
+ *
11
+ * 规则:
12
+ * - 以 .、/、~ 开头;
13
+ * - 包含 / 或 \\,且不是 scoped 包(@scope/name 仅含一个 /);
14
+ * - scoped 包若包含反斜杠或多于一个 /,亦视作路径。
15
+ */
16
+ export function isExplicitPath(p) {
17
+ if (!p)
18
+ return false;
19
+ if (p.startsWith("@")) {
20
+ const slashCount = (p.match(/\//g) || []).length;
21
+ return slashCount > 1 || p.includes("\\");
22
+ }
23
+ return (p.startsWith(".") ||
24
+ p.startsWith("/") ||
25
+ p.startsWith("~") ||
26
+ p.includes("/") ||
27
+ p.includes("\\"));
28
+ }
29
+ /**
30
+ * 对明确的本地封闭包集合实施一致性校验。
31
+ *
32
+ * 校验规则:
33
+ * - 重复包标识防护:集合内不得存在相同 packageId;
34
+ * - 依赖存在性与版本兼容性:声明的直接依赖必须在集合内存在且满足语义版本约束(复用 PackageGraphBuilder);
35
+ * - 跨包 uses 目标完整性:uses 声明引用的目标包与目标 Action 必须在集合内存在。
36
+ *
37
+ * @param packages 封闭包条目集合
38
+ */
39
+ export function validateLocalSkillPackages(packages) {
40
+ if (!packages || packages.length === 0)
41
+ return;
42
+ // 1. 重复 packageId 校验
43
+ const seenIds = new Set();
44
+ for (const pkg of packages) {
45
+ if (seenIds.has(pkg.packageId)) {
46
+ throw new ActionDockError(PACKAGE_ID_CONFLICT, `Package ID conflict: package '${pkg.packageId}' is declared more than once in skill packages`);
47
+ }
48
+ seenIds.add(pkg.packageId);
49
+ }
50
+ // 2. 构造包发现视图并交由 PackageGraphBuilder 校验依赖与版本
51
+ const discovered = packages.map((pkg) => ({
52
+ id: pkg.packageId,
53
+ root: pkg.packageRoot,
54
+ manifest: pkg.projectConfig,
55
+ identity: createPackageIdentity({
56
+ id: pkg.packageId,
57
+ instanceId: `${pkg.packageId}:${pkg.packageRoot}`,
58
+ generation: "skill-local",
59
+ }),
60
+ isCurrentProject: true,
61
+ }));
62
+ const builder = new PackageGraphBuilder({
63
+ packages: discovered,
64
+ validateDependencies: true,
65
+ });
66
+ builder.buildSync();
67
+ // 3. uses 声明目标存在性校验
68
+ const pkgMap = new Map();
69
+ for (const pkg of packages) {
70
+ pkgMap.set(pkg.packageId, pkg.projectConfig);
71
+ }
72
+ const validateUseRef = (rawRef, caller, currentPkg) => {
73
+ if (typeof rawRef !== "string" || !rawRef.trim())
74
+ return;
75
+ const trimmed = rawRef.trim();
76
+ // 先按完整包身份识别裸包引用,包括 scoped 包。
77
+ if (pkgMap.has(trimmed)) {
78
+ return;
79
+ }
80
+ // 裸 scoped 包只有一个斜杠,不能按限定动作拆分。
81
+ if (trimmed.startsWith("@") && (trimmed.match(/\//g) || []).length === 1) {
82
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package '${trimmed}' referenced by uses in '${caller}' was not found in skill directory`);
83
+ }
84
+ // 限定动作与包级通配符复用统一引用解析器。
85
+ if (trimmed.includes("/")) {
86
+ const ref = parseActionRef(trimmed);
87
+ const targetPkgId = ref.packageId;
88
+ const targetActionId = ref.actionId;
89
+ const targetPkg = pkgMap.get(targetPkgId);
90
+ if (!targetPkg) {
91
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package '${targetPkgId}' referenced by uses in '${caller}' was not found in skill directory`);
92
+ }
93
+ if (targetActionId !== "*") {
94
+ const hasAction = Boolean(targetPkg.actions && targetPkg.actions[targetActionId]);
95
+ if (!hasAction) {
96
+ throw new ActionDockError(ACTION_NOT_FOUND, `Action '${targetActionId}' referenced by uses in '${caller}' was not found in package '${targetPkgId}'`);
97
+ }
98
+ }
99
+ return;
100
+ }
101
+ // 本包短动作引用。
102
+ if (currentPkg.actions && currentPkg.actions[trimmed]) {
103
+ return;
104
+ }
105
+ // 目标不存在。
106
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package or action '${trimmed}' referenced by uses in '${caller}' was not found in skill directory`);
107
+ };
108
+ for (const pkg of packages) {
109
+ const pkgUses = pkg.projectConfig.uses;
110
+ if (Array.isArray(pkgUses)) {
111
+ for (const ref of pkgUses) {
112
+ validateUseRef(ref, `package '${pkg.packageId}'`, pkg.projectConfig);
113
+ }
114
+ }
115
+ if (pkg.projectConfig.actions && typeof pkg.projectConfig.actions === "object") {
116
+ for (const [actionId, actionEntry] of Object.entries(pkg.projectConfig.actions)) {
117
+ if (actionEntry && Array.isArray(actionEntry.uses)) {
118
+ for (const ref of actionEntry.uses) {
119
+ validateUseRef(ref, `action '${pkg.packageId}/${actionId}'`, pkg.projectConfig);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * 探测指定路径是否为本地 Skill / 包目录(支持单包根目录与 packages/* 复合套件布局)。
128
+ *
129
+ * 封闭包装配与校验规则:
130
+ * - 边界安全防护:packages 目录、各子包目录与 actiondock.json 清单真实路径必须在 Skill 根边界内,禁止越界符号链接;
131
+ * - 单包布局:目录根部存在 actiondock.json,解析为包含单一包的 LocalSkillDirectory;
132
+ * - 复合布局:目录根部存在 packages/ 目录,遍历其直接子目录,收集包含 actiondock.json 的子包;
133
+ * - 集合一致性校验:通过 validateLocalSkillPackages 严格核查依赖闭包、版本兼容与 uses 目标;
134
+ * - 异常透传:坏清单或权限异常原样抛出,严禁静默回退 Registry;
135
+ * - 若非候选技能目录,返回 null。
136
+ *
137
+ * @param directoryPath 待检查的目录路径(相对路径将基于 cwd 解析)
138
+ * @param cwd 解析基准工作目录(默认为 process.cwd())
139
+ */
140
+ export function inspectLocalPackageDirectory(directoryPath, cwd) {
141
+ if (!directoryPath || typeof directoryPath !== "string") {
142
+ return null;
143
+ }
144
+ const absDir = resolve(cwd || process.cwd(), directoryPath);
145
+ if (!existsSync(absDir)) {
146
+ return null;
147
+ }
148
+ try {
149
+ const stat = statSync(absDir);
150
+ if (!stat.isDirectory()) {
151
+ return null;
152
+ }
153
+ }
154
+ catch {
155
+ return null;
156
+ }
157
+ // 单包布局:根目录包含 actiondock.json
158
+ const rootManifest = join(absDir, "actiondock.json");
159
+ if (existsSync(rootManifest)) {
160
+ const config = loadProjectConfig(absDir);
161
+ const packages = [
162
+ {
163
+ packageId: config.id,
164
+ packageRoot: absDir,
165
+ projectConfig: config,
166
+ },
167
+ ];
168
+ validateLocalSkillPackages(packages);
169
+ return {
170
+ type: "single",
171
+ root: absDir,
172
+ packages,
173
+ };
174
+ }
175
+ // 复合 Skill 布局:存在 packages/ 目录且包含至少一个有效子包
176
+ const packagesDir = join(absDir, "packages");
177
+ if (existsSync(packagesDir)) {
178
+ const pStat = statSync(packagesDir);
179
+ if (pStat.isDirectory()) {
180
+ const entries = readdirSync(packagesDir, { withFileTypes: true });
181
+ const packages = [];
182
+ const seenIds = new Set();
183
+ entries.sort((a, b) => a.name.localeCompare(b.name));
184
+ for (const entry of entries) {
185
+ const subDir = join(packagesDir, entry.name);
186
+ const sStat = statSync(subDir);
187
+ if (sStat.isDirectory()) {
188
+ const subManifest = join(subDir, "actiondock.json");
189
+ if (existsSync(subManifest)) {
190
+ const childConfig = loadProjectConfig(subDir);
191
+ if (seenIds.has(childConfig.id)) {
192
+ throw new ActionDockError(PACKAGE_ID_CONFLICT, `Package ID conflict: package '${childConfig.id}' is declared more than once in skill directory '${absDir}'`);
193
+ }
194
+ seenIds.add(childConfig.id);
195
+ packages.push({
196
+ packageId: childConfig.id,
197
+ packageRoot: subDir,
198
+ projectConfig: childConfig,
199
+ });
200
+ }
201
+ }
202
+ }
203
+ if (packages.length > 0) {
204
+ validateLocalSkillPackages(packages);
205
+ return {
206
+ type: "composite",
207
+ root: absDir,
208
+ packages,
209
+ };
210
+ }
211
+ }
212
+ }
213
+ return null;
214
+ }
@@ -75,6 +75,8 @@ export interface ActionDockManifest {
75
75
  actions?: Record<string, ActionManifestEntry>;
76
76
  /** 声明的 Playbooks 集合(唯一事实源) */
77
77
  playbooks?: Record<string, PlaybookManifestEntry>;
78
+ /** 包级静态依赖引用,与 Action 的 uses 共用引用语义。 */
79
+ uses?: string[];
78
80
  /** 跨包外部依赖映射(逻辑包 ID 到 npm 包名) */
79
81
  dependencies?: Record<string, string>;
80
82
  /** 框架导出与分发边界文件列表 */
@@ -2,6 +2,7 @@ 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";
5
6
  import { getActionDockHome, getPackageSlug } from "../utils/index.js";
6
7
  import { ActionDockError, MANIFEST_NOT_FOUND, REGISTRY_CORRUPTED } from "../errors.js";
7
8
  import { withRegistryLock } from "./lock.js";
@@ -399,31 +400,42 @@ export function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
399
400
  const resolvedPath = resolve(baseDir, packageIdOrPath);
400
401
  // 检查 packageIdOrPath 是否为磁盘上现存的目录或文件路径
401
402
  if (existsSync(resolvedPath)) {
403
+ let info;
402
404
  try {
403
- const info = statSync(resolvedPath);
405
+ info = statSync(resolvedPath);
406
+ }
407
+ catch (err) {
408
+ if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
409
+ throw err;
410
+ }
411
+ }
412
+ if (info) {
404
413
  const targetDir = info.isDirectory() ? resolvedPath : dirname(resolvedPath);
414
+ // 普通开发包根目录:直接包含 actiondock.json 清单。
415
+ // 普通清单根目录定位与封闭 Skill 执行校验分离,不能被当成只含一个包的 Skill 闭包拒绝。
405
416
  if (existsSync(join(targetDir, "actiondock.json"))) {
417
+ loadProjectConfig(targetDir);
406
418
  return targetDir;
407
419
  }
408
- const parentRoot = findProjectRoot(targetDir);
409
- if (parentRoot) {
410
- return parentRoot;
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
+ }
411
433
  }
412
- }
413
- catch {
414
- // stat 失败(如并发删除):忽略并继续走标识符解析路径
415
434
  }
416
435
  }
417
436
  // 显式路径形态但未解析成功的场景:
418
437
  // scoped package 标识符(如 @team/tools)虽含 '/' 但属于包 ID 而非文件路径
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) {
438
+ if (isExplicitPath(packageIdOrPath)) {
427
439
  // 显式路径不存在或不是 ActionDock 项目时必须失败,禁止静默回退
428
440
  return null;
429
441
  }
@@ -22,6 +22,7 @@ export function normalizeActionCollection(rawActions) {
22
22
  description: v.description,
23
23
  inputSchema: v.inputSchema,
24
24
  outputSchema: v.outputSchema,
25
+ annotations: v.annotations,
25
26
  };
26
27
  }
27
28
  return { actionsMap, actionSpecs };
@@ -37,6 +38,7 @@ export function normalizeActionCollection(rawActions) {
37
38
  description: item.description ?? act.description,
38
39
  inputSchema: item.inputSchema ?? act.inputSchema,
39
40
  outputSchema: item.outputSchema ?? act.outputSchema,
41
+ annotations: item.annotations ?? act.annotations,
40
42
  };
41
43
  }
42
44
  }
@@ -50,6 +52,7 @@ export function normalizeActionCollection(rawActions) {
50
52
  description: v.description,
51
53
  inputSchema: v.inputSchema,
52
54
  outputSchema: v.outputSchema,
55
+ annotations: v.annotations,
53
56
  };
54
57
  }
55
58
  }
@@ -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: a.id,
32
+ id: ctx.options.multiPackage && a.packageId && !a.id.includes("/") ? `${a.packageId}/${a.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,13 +66,13 @@ export async function handleDescribe(ctx, subArgs) {
66
66
  });
67
67
  }
68
68
  const payload = buildActionDescribePayload(action, {
69
- packageId: ctx.options.packageId,
69
+ packageId: action.packageId || ctx.options.packageId,
70
70
  });
71
71
  if (isJson) {
72
72
  ctx.writeOut(JSON.stringify(payload, null, 2));
73
73
  }
74
74
  else {
75
- ctx.writeOut(formatActionDetail(payload));
75
+ ctx.writeOut(formatActionDetail(payload, { supportsTextField: false }));
76
76
  }
77
77
  return ExitCode.SUCCESS;
78
78
  }
@@ -84,6 +84,7 @@ export async function handleRun(ctx, subArgs, actionArgs, signal) {
84
84
  }
85
85
  let inputStr;
86
86
  let inputFile;
87
+ let stdinField;
87
88
  let timeoutMs;
88
89
  const recognizedFlags = new Set(["--json", "--async"]);
89
90
  for (let i = 0; i < subArgs.length; i++) {
@@ -131,6 +132,12 @@ export async function handleRun(ctx, subArgs, actionArgs, signal) {
131
132
  else if (arg.startsWith("-f=")) {
132
133
  inputFile = arg.slice(3);
133
134
  }
135
+ else if (arg === "--stdin-field" && i + 1 < subArgs.length) {
136
+ stdinField = subArgs[++i];
137
+ }
138
+ else if (arg.startsWith("--stdin-field=")) {
139
+ stdinField = arg.slice(14);
140
+ }
134
141
  else if (arg.startsWith("-")) {
135
142
  const hint = "Hint: Separate action inputs from CLI options using '--', e.g.: ad run <id> [options] -- <param>=<val> or <param>:=<json>.";
136
143
  return ctx.emitError(isJson, "INVALID_ARGUMENT", `error: unknown option '${arg}'`, ExitCode.INVALID_ARGUMENT, {
@@ -144,8 +151,10 @@ export async function handleRun(ctx, subArgs, actionArgs, signal) {
144
151
  input = await resolveActionInput({
145
152
  input: inputStr,
146
153
  inputFile,
154
+ stdinField,
147
155
  flatArgs: actionArgs.length > 0 ? actionArgs : undefined,
148
156
  stdin: process.stdin,
157
+ signal,
149
158
  });
150
159
  const check = validateActionInputValue(input);
151
160
  if (!check.valid) {
@@ -265,6 +274,9 @@ function isServiceCapabilityError(err) {
265
274
  code === "TARGET_CAPABILITY_UNAVAILABLE");
266
275
  }
267
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
+ }
268
280
  const sub = subArgs[0] || "list";
269
281
  if (!ctx.service.management) {
270
282
  ctx.writeErr("Error: management port is not enabled on this service");
@@ -341,6 +353,9 @@ function decodeStateKeyArg(key) {
341
353
  }
342
354
  }
343
355
  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
+ }
344
359
  const sub = subArgs[0] || "list";
345
360
  let namespace;
346
361
  let isAll = false;
@@ -42,6 +42,8 @@ export function printHelp(writeOut, options) {
42
42
  writeOut(" <cmd> describe <id> [--json] Show action details and schemas");
43
43
  writeOut(" <cmd> run <id> [--input '<json>'] Execute action (raw text output by default)");
44
44
  writeOut(" <cmd> run <id> [--json] Output standard execution result in JSON format");
45
+ writeOut(" <cmd> run <id> [--stdin-field <field>] [-- <assignments...>]");
46
+ writeOut(" Bind raw stdin text to an input field");
45
47
  writeOut(" <cmd> config list/get/set/delete Manage package configuration");
46
48
  writeOut(" <cmd> state list/get/set/delete Manage shared state store");
47
49
  writeOut("\nGlobal options:");
@@ -24,6 +24,8 @@ export interface InvocationControl {
24
24
  export interface StandaloneDispatcherOptions {
25
25
  /** 所属 Package ID */
26
26
  packageId: string;
27
+ /** 多包入口使用完全限定发现标识,不以展示名操作配置和状态。 */
28
+ multiPackage?: boolean;
27
29
  /** 版本号 */
28
30
  version: string;
29
31
  /** 描述信息 */
@@ -1,4 +1,3 @@
1
- import "../utils/warning.js";
2
1
  import type { DatabaseSync } from "node:sqlite";
3
2
  import { normalizeSqliteParams } from "./params.js";
4
3
  import type { SqliteDriver, SqliteStatement } from "./types.js";
@@ -1,4 +1,3 @@
1
- import "../utils/warning.js";
2
1
  import { normalizeSqliteParams } from "./params.js";
3
2
  import { ActionDockError, ASYNC_TRANSACTION_UNSUPPORTED, STORAGE_CLOSED } from "../errors.js";
4
3
  // 基于原生 ESM Top-Level Await 在模块评估期动态加载,确保警告静默拦截器优先就绪且保持纯正 ESM 架构
@@ -104,4 +104,3 @@ export declare function assertPathWithinRoot(rootDir: string, targetPath: string
104
104
  * Backwards-compatible alias for assertPathWithinRoot targeting project roots.
105
105
  */
106
106
  export declare function assertWithinProjectRoot(projectRoot: string, subDir: string, fieldName: string): void;
107
- export { suppressExperimentalWarnings } from "./warning.js";
@@ -281,4 +281,3 @@ export function assertWithinProjectRoot(projectRoot, subDir, fieldName) {
281
281
  }
282
282
  assertPathWithinRoot(projectRoot, subDir, fieldName);
283
283
  }
284
- export { suppressExperimentalWarnings } from "./warning.js";
@@ -6,10 +6,11 @@ export interface SemVer {
6
6
  minor: number;
7
7
  patch: number;
8
8
  prerelease?: string;
9
+ build?: string;
9
10
  }
10
11
  /**
11
12
  * 解析基础 SemVer 版本字符串(单一事实源)。
12
- * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
13
+ * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀与构建元数据;
13
14
  * 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
14
15
  *
15
16
  * 本模块不依赖任何 Node 内建与 core 领域模块,可在无 loader 的原生
@@ -1,19 +1,25 @@
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(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
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-]+)*))?$/);
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;
13
18
  return {
14
- major: parseInt(match[1], 10),
15
- minor: parseInt(match[2], 10),
16
- patch: parseInt(match[3], 10),
19
+ major,
20
+ minor,
21
+ patch,
17
22
  prerelease: match[4],
23
+ ...(match[5] === undefined ? {} : { build: match[5] }),
18
24
  };
19
25
  }
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export declare const ACTIONDOCK_VERSION = "2.12.0";
4
+ export declare const ACTIONDOCK_VERSION = "2.14.0";
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export const ACTIONDOCK_VERSION = "2.12.0";
4
+ export const ACTIONDOCK_VERSION = "2.14.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.12.0",
3
+ "version": "2.14.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",
@@ -41,11 +41,6 @@
41
41
  "types": "./dist/package/index.d.ts",
42
42
  "import": "./dist/package/index.js",
43
43
  "default": "./dist/package/index.js"
44
- },
45
- "./warning": {
46
- "types": "./dist/utils/warning.d.ts",
47
- "import": "./dist/utils/warning.js",
48
- "default": "./dist/utils/warning.js"
49
44
  }
50
45
  },
51
46
  "files": [
@@ -64,7 +59,7 @@
64
59
  "test": "node ../../scripts/run-tests.ts"
65
60
  },
66
61
  "dependencies": {
67
- "@actiondock/sdk": "^2.12.0",
62
+ "@actiondock/sdk": "^2.14.0",
68
63
  "ajv": "^8.17.1",
69
64
  "ajv-formats": "^3.0.1",
70
65
  "undici": "^7.29.1"
@@ -1 +0,0 @@
1
- export declare function suppressExperimentalWarnings(): void;
@@ -1,52 +0,0 @@
1
- /**
2
- * 全局禁用 Node.js 所有的 ExperimentalWarning 实验性特性警告。
3
- * 跨所有 Node.js 运行版本,不区分具体特性(如 SQLite、类型擦除等),
4
- * 在进程级别统一静默全部实验性警告,同时完整保留废弃警告与运行期异常。
5
- *
6
- * 逃生通道:当环境变量 ACTIONDOCK_SILENCE_WARNINGS 显式设置为 "0" 时,放弃静默以供深度调试排查。
7
- */
8
- const kExperimentalWarningSuppressed = Symbol.for("actiondock.experimental_warning_suppressed");
9
- export function suppressExperimentalWarnings() {
10
- // 逃生通道:支持开发者显式禁用静默逻辑,确保排障透明度
11
- if (process.env.ACTIONDOCK_SILENCE_WARNINGS === "0") {
12
- return;
13
- }
14
- const globalObj = globalThis;
15
- if (globalObj[kExperimentalWarningSuppressed]) {
16
- return;
17
- }
18
- globalObj[kExperimentalWarningSuppressed] = true;
19
- // 1. 拦截 process.emitWarning 源头
20
- const originalEmitWarning = process.emitWarning;
21
- if (typeof originalEmitWarning === "function") {
22
- process.emitWarning = function (warning, ...args) {
23
- if (typeof warning === "string") {
24
- const type = typeof args[0] === "string" ? args[0] : args[0]?.type || args[1];
25
- if (type === "ExperimentalWarning") {
26
- return;
27
- }
28
- }
29
- else if (warning) {
30
- const warnObj = warning;
31
- if (warnObj.name === "ExperimentalWarning" || warnObj.type === "ExperimentalWarning") {
32
- return;
33
- }
34
- }
35
- return Reflect.apply(originalEmitWarning, process, [warning, ...args]);
36
- };
37
- }
38
- // 2. 包装进程级 warning 事件分发器,作为双层防护
39
- const originalListeners = process.listeners("warning");
40
- process.removeAllListeners("warning");
41
- process.on("warning", (warning) => {
42
- const warnObj = warning;
43
- if (warnObj.name === "ExperimentalWarning" || warnObj.type === "ExperimentalWarning") {
44
- return;
45
- }
46
- for (const listener of originalListeners) {
47
- listener.call(process, warning);
48
- }
49
- });
50
- }
51
- // 模块被引入时立即自执行
52
- suppressExperimentalWarnings();