@actiondock/cli 2.12.0 → 2.13.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.
package/README.md CHANGED
@@ -59,6 +59,10 @@ ad run sample.greet -- name=Alice count:=1
59
59
  # 复杂参数或对象从文件读取(与扁平参数互斥)
60
60
  ad run sample.greet --input-file input.json
61
61
 
62
+ # 原始文本管道绑定(假定已注册 summarize 动作且 text 是字符串入参)
63
+ # 可与扁平赋值组合,与 --input / --input-file 互斥
64
+ printf '这是一段正文' | ad run summarize --stdin-field text -- style=brief
65
+
62
66
  # 自动化与脚本通过标准输入传递
63
67
  cat input.json | ad run sample.greet --input-file -
64
68
  ```
@@ -126,11 +130,12 @@ ad pack
126
130
  - 路径冲突(叶节点与容器冲突、对象与数组冲突、重复赋值)严格拒绝(`INPUT_PATH_CONFLICT`)。
127
131
  - 拦截原型污染敏感属性(`__proto__`、`constructor`、`prototype`)。
128
132
  - 三种输入模式互斥:扁平参数、`--input` 与 `--input-file` 严格互斥,不可混用(`INPUT_CONFLICT`);未指定输入时默认为 `{}`。
133
+ - 原始文本管道绑定:`printf '正文' | ad run <action> --stdin-field <field> [-- <assignments...>]` 将 stdin 完整正文原样绑定为指定顶层字段的字符串值(不解析类型、不剥 BOM、不修剪空白、空输入为空字符串);可与扁平赋值组合,与 `--input` / `--input-file` 互斥;stdin 源字节与绑定后的最终入参序列化大小各自受默认 10MiB 上限约束,分别超限报 `INPUT_LIMIT_EXCEEDED` 与 `FLAT_INPUT_LIMIT_EXCEEDED`,JSON 转义放大也计入最终大小;严格 UTF-8 校验失败报读取错误。管道非事务,高风险写入先完整生成并校验再执行。
129
134
  - 传统输入选项:
130
135
  - 简单内联参数:使用 `--input <json>` 直接解析 JSON 字符串。
131
136
  - 复杂对象文件:使用 `--input-file <path>` 读取文件并解析 JSON,避免各类终端的引号转义损坏。
132
137
  - 标准输入管道:使用 `--input-file -` 从标准输入读取全部数据并解析 JSON。
133
- - 统一解析:解析前均自动剔除 UTF-8 BOM 标记,且不设人为大小上限。
138
+ - 统一解析:JSON 输入在解析前剔除 UTF-8 BOM,默认大小上限为 10MiB;改用文件不会解除限制。原始文本绑定保留正文 BOM。
134
139
 
135
140
  ### Windows 与多终端传参建议
136
141
 
@@ -156,7 +161,7 @@ $data | ConvertTo-Json -Depth 100 | ad run complex-action --input-file -
156
161
  - **显式正文输出与默认注解**:
157
162
  - 显式指定正文字段:通过 `ad run <id> --text-field <field> -- ...` 提取结果中特定顶层自有字符串字段输出至 stdout(保留真实换行且无 JSON 转义),其余字段以 JSON 对象形式输出至 stderr(元数据为空时不输出)。
158
163
  - 声明式默认正文:Action 清单中声明 `annotations["actiondock.cli"] = { "textField": "<field>" }` 时,未指定 `--text-field` 时自动按声明提取正文与元数据;显式 `--text-field` 优先于默认注解。
159
- - 严格校验:正文字段缺失、非字符串或返回数据非对象时,明确输出错误并以退出码 1 退出,不静默回退,不改写运行记录。
164
+ - 严格校验与契约闭环:正文字段缺失、非字符串或返回数据非对象时,明确输出错误并以退出码 1 退出,不静默回退,不改写运行记录。契约通过 `ad describe` 呈现通道语义与覆盖指引,并通过 `ad validate` 进行纯静态元数据合规校验。
160
165
  - 参数互斥:`--text-field` 与 `--json`、`--async` 严格互斥,在执行前校验并以退出码 2 退出。
161
166
  - **机器模式**(`--json`):当需要以程序化方式消费、获取完整结构化数据或被外部系统集成时,传入 `--json`。此时始终输出完整执行信封(包含 `ok`、`runId`、`data` 或 `error`),忽略默认正文注解;若参数解析出错输出标准错误信封并以退出码 2 退出。
162
167
 
@@ -171,9 +176,9 @@ $data | ConvertTo-Json -Depth 100 | ad run complex-action --input-file -
171
176
  | `ad remove <package>` | 卸载并更新锁定依赖,提供原子回滚保护 |
172
177
  | `ad info [patterns...]` | 检索包元数据与能力清单,支持模式匹配与树形展示 |
173
178
  | `ad list [patterns...]` | 列出包内所有已注册的 Action |
174
- | `ad describe <id>` | 编码顾问:查看 Action 的详情、模式字段明细、Flat 编码指引与建议赋值 |
175
- | `ad run <id>` | 本地或远程执行指定 Action(规范语法 `ad run <id> [options] -- <assignments...>`,支持 `--text-field` 提取正文与 `--json` 输出标准信封) |
176
- | `ad validate [id]` | 校验 Action 规范与模式规范 |
179
+ | `ad describe <id>` | 编码顾问:查看 Action 的详情、模式字段明细、Flat 编码指引、建议赋值与输出选择契约 |
180
+ | `ad run <id>` | 本地或远程执行指定 Action(规范语法 `ad run <id> [options] -- <assignments...>`,支持 `--stdin-field` 绑定原始文本、`--text-field` 提取正文与 `--json` 输出标准信封) |
181
+ | `ad validate [id]` | 校验 Action 规范、模式规范与输出选择契约 |
177
182
  | `ad doctor` | 执行运行环境与项目结构健康诊断 |
178
183
  | `ad action create <id>` | 创建新 Action 源码 |
179
184
  | `ad playbook list` / `show` | 查看智能体操作规程 Playbook |
@@ -78,6 +78,13 @@ export async function executeAction(id, options, context, flatArgs, control) {
78
78
  throw new ArgumentError("Cannot specify both --text-field and --async.");
79
79
  }
80
80
  }
81
+ // --stdin-field 与 --text-field 分属输入绑定与输出选择,可同时使用;
82
+ // 与完整 JSON 输入来源的互斥由共享 resolver 统一裁决(INPUT_CONFLICT)
83
+ if (options.stdinField !== undefined) {
84
+ if (typeof options.stdinField !== "string") {
85
+ throw new ArgumentError("The --stdin-field option requires a field name.");
86
+ }
87
+ }
81
88
  const effectiveControl = control ?? context?.control;
82
89
  const effectiveSignal = effectiveControl?.signal;
83
90
  const effectiveFlatArgs = flatArgs ?? options.flatArgs;
@@ -85,7 +92,9 @@ export async function executeAction(id, options, context, flatArgs, control) {
85
92
  input: options.input,
86
93
  inputFile: options.inputFile,
87
94
  flatArgs: effectiveFlatArgs && effectiveFlatArgs.length > 0 ? effectiveFlatArgs : undefined,
95
+ stdinField: options.stdinField,
88
96
  stdin: context?.stdin,
97
+ signal: effectiveSignal,
89
98
  });
90
99
  const check = validateActionInputValue(input);
91
100
  if (!check.valid) {
@@ -242,6 +251,11 @@ Flat Input Syntax & Examples:
242
251
  # Extract specific text field to stdout (remaining fields as JSON to stderr)
243
252
  ad run <id> --text-field content -- path="README.md"
244
253
 
254
+ Raw Text Input from stdin:
255
+ # Bind entire stdin content as a string to the given input field
256
+ printf 'body text' | ad run <id> --stdin-field text -- style=brief
257
+ cat report.md | ad run <id> --stdin-field content
258
+
245
259
  Complex or multiline inputs:
246
260
  # Pass via JSON file with --input-file to avoid shell escaping issues
247
261
  ad run <id> --input-file input.json`;
@@ -258,7 +272,8 @@ export function attachRunCommand(parent, context) {
258
272
  .option("-P, --package <id>", "Target package ID or path")
259
273
  .option("-i, --input <json>", "Action input as inline JSON")
260
274
  .option("-f, --input-file <path>", "Action input from JSON file, or '-' for stdin")
261
- .option("-c, --config <key=value...>", "Temporary config override (repeatable)");
275
+ .option("-c, --config <key=value...>", "Temporary config override (repeatable)")
276
+ .option("--stdin-field <field>", "Bind raw stdin text to a top-level action input field");
262
277
  cmd.unknownOption = function (flag) {
263
278
  this.error(`unknown option '${flag}'\nHint: Separate action inputs from CLI options using '--', e.g.: ad run <id> [options] -- <param>=<val> or <param>:=<json>.`, { code: "commander.unknownOption" });
264
279
  };
@@ -1,4 +1,4 @@
1
- import { checkGeneratedTypes, GENERATED_TYPES_OUTDATED_CODE, loadActions, } from "@actiondock/core/project";
1
+ import { checkGeneratedTypes, GENERATED_TYPES_OUTDATED_CODE, loadActions, analyzeActionOutputContract, } from "@actiondock/core/project";
2
2
  import { ArgumentError, ExecutionError } from "../errors.js";
3
3
  import { renderActionValidation, renderResult } from "../renderer.js";
4
4
  import { getEffectiveOptions, requirePackageRoot } from "../utils/index.js";
@@ -49,10 +49,22 @@ export function attachValidateCommand(parent, context) {
49
49
  if (outSchema && typeof outSchema !== "object") {
50
50
  errors.push("Invalid outputSchema object");
51
51
  }
52
+ const annotations = item.spec?.annotations ?? item.act?.annotations;
53
+ const contractAnalysis = analyzeActionOutputContract({
54
+ annotations,
55
+ outputSchema: outSchema,
56
+ });
57
+ if (contractAnalysis.errors.length > 0) {
58
+ for (const err of contractAnalysis.errors) {
59
+ errors.push(err);
60
+ }
61
+ }
62
+ const warnings = contractAnalysis.warnings.slice();
52
63
  results.push({
53
64
  id: item.id,
54
65
  valid: errors.length === 0,
55
66
  errors,
67
+ ...(warnings.length > 0 ? { warnings } : {}),
56
68
  });
57
69
  }
58
70
  const allValid = results.every((r) => r.valid);
@@ -1,5 +1,5 @@
1
- import type { ActionSpec } from "@actiondock/core";
2
- import { buildActionDescribePayload } from "@actiondock/core/project";
1
+ import { formatActionDetail, type FormatActionDetailOptions } from "@actiondock/core/project";
2
+ export { formatActionDetail, type FormatActionDetailOptions, };
3
3
  /**
4
4
  * 解析 Action 注解中的默认正文输出字段声明。
5
5
  *
@@ -7,7 +7,7 @@ import { buildActionDescribePayload } from "@actiondock/core/project";
7
7
  * - 注解键名固定为 actiondock.cli,值必须为非空非数组对象。
8
8
  * - 正文字段键名为 textField,值必须为非空字符串。
9
9
  * - 无相关注解或未声明 textField 时返回 undefined。
10
- * - 注解存在但类型非法时抛出 ExecutionError。
10
+ * - 注解存在但类型非法时抛出 ExecutionError (INVALID_ANNOTATION)。
11
11
  */
12
12
  export declare function resolveActionDefaultTextField(annotations?: Record<string, unknown>): string | undefined;
13
13
  /**
@@ -35,30 +35,9 @@ export declare function renderActionList(items: Array<{
35
35
  description: string;
36
36
  packageId?: string;
37
37
  }>, title?: string, isFallback?: boolean, intent?: string): string;
38
- /**
39
- * 格式化渲染 Action 详情人类可读文本。
40
- *
41
- * @param input Action 描述载荷或 Action 规范
42
- * @returns 格式化后的说明文本
43
- */
44
- export declare function formatActionDetail(input: ReturnType<typeof buildActionDescribePayload> | (Partial<ActionSpec> & {
45
- id: string;
46
- }) | {
47
- id: string;
48
- packageId?: string;
49
- projectRoot?: string;
50
- description?: string;
51
- inputSchema?: unknown;
52
- outputSchema?: unknown;
53
- tags?: string[];
54
- annotations?: Record<string, unknown>;
55
- uses?: string[];
56
- entry?: string;
57
- [key: string]: any;
58
- }): string;
59
38
  /**
60
39
  * 格式化渲染 Action 详情(编码顾问 Encoding Advisor)。
61
- * 统一复用 formatActionDetail 实现。
40
+ * 统一复用核心 formatActionDetail 实现。
62
41
  */
63
42
  export declare function renderActionDetail(action: {
64
43
  id: string;
@@ -75,4 +54,5 @@ export declare function renderActionValidation(results: Array<{
75
54
  id: string;
76
55
  valid: boolean;
77
56
  errors: string[];
57
+ warnings?: string[];
78
58
  }>): string;
@@ -1,4 +1,5 @@
1
- import { buildActionDescribePayload, ACTION_DESCRIBE_SYNTAX_REFERENCE, } from "@actiondock/core/project";
1
+ import { inspectActionTextFieldAnnotation, formatActionDetail, } from "@actiondock/core/project";
2
+ export { formatActionDetail, };
2
3
  import { ExecutionError } from "../errors.js";
3
4
  /**
4
5
  * 解析 Action 注解中的默认正文输出字段声明。
@@ -7,33 +8,14 @@ import { ExecutionError } from "../errors.js";
7
8
  * - 注解键名固定为 actiondock.cli,值必须为非空非数组对象。
8
9
  * - 正文字段键名为 textField,值必须为非空字符串。
9
10
  * - 无相关注解或未声明 textField 时返回 undefined。
10
- * - 注解存在但类型非法时抛出 ExecutionError。
11
+ * - 注解存在但类型非法时抛出 ExecutionError (INVALID_ANNOTATION)。
11
12
  */
12
13
  export function resolveActionDefaultTextField(annotations) {
13
- if (!annotations || typeof annotations !== "object") {
14
- return undefined;
14
+ const inspection = inspectActionTextFieldAnnotation(annotations);
15
+ if (!inspection.valid) {
16
+ throw new ExecutionError(inspection.error, undefined, "INVALID_ANNOTATION");
15
17
  }
16
- if (!Object.hasOwn(annotations, "actiondock.cli")) {
17
- return undefined;
18
- }
19
- const cli = annotations["actiondock.cli"];
20
- if (cli === undefined) {
21
- return undefined;
22
- }
23
- if (typeof cli !== "object" || cli === null || Array.isArray(cli)) {
24
- throw new ExecutionError(`Invalid 'actiondock.cli' annotation: expected an object, but received ${cli === null ? "null" : Array.isArray(cli) ? "an array" : typeof cli}.`, undefined, "INVALID_ANNOTATION");
25
- }
26
- if (!Object.hasOwn(cli, "textField") || cli.textField === undefined) {
27
- return undefined;
28
- }
29
- const textField = cli.textField;
30
- if (typeof textField !== "string") {
31
- throw new ExecutionError(`Invalid 'actiondock.cli.textField' annotation: expected a string, but received ${textField === null ? "null" : Array.isArray(textField) ? "an array" : typeof textField}.`, undefined, "INVALID_ANNOTATION");
32
- }
33
- if (textField.trim() === "") {
34
- throw new ExecutionError("Invalid 'actiondock.cli.textField' annotation: cannot be an empty string.", undefined, "INVALID_ANNOTATION");
35
- }
36
- return textField;
18
+ return inspection.textField;
37
19
  }
38
20
  /**
39
21
  * 从 Action 执行数据中抽取指定自有顶层正文字段及剩余元数据。
@@ -94,78 +76,9 @@ export function renderActionList(items, title = "Actions", isFallback = false, i
94
76
  lines.push("\nTip: For composite or multi-step tasks, check 'ad playbook list' for standard operating procedures.");
95
77
  return lines.join("\n");
96
78
  }
97
- /**
98
- * 格式化渲染 Action 详情人类可读文本。
99
- *
100
- * @param input Action 描述载荷或 Action 规范
101
- * @returns 格式化后的说明文本
102
- */
103
- export function formatActionDetail(input) {
104
- const payload = "inputAdvice" in input && input.inputAdvice
105
- ? input
106
- : buildActionDescribePayload(input);
107
- const lines = [];
108
- lines.push(`Action: ${payload.id}`);
109
- if (payload.packageId) {
110
- lines.push(`Package: ${payload.packageId}`);
111
- }
112
- if (payload.description) {
113
- lines.push(`Description: ${payload.description}`);
114
- }
115
- if (payload.tags && payload.tags.length > 0) {
116
- lines.push(`Tags: ${payload.tags.join(", ")}`);
117
- }
118
- if (payload.uses && payload.uses.length > 0) {
119
- lines.push(`Uses: ${payload.uses.join(", ")}`);
120
- }
121
- if (payload.entry) {
122
- lines.push(`Entry: ${payload.entry}`);
123
- }
124
- if (payload.inputSchema !== undefined) {
125
- lines.push("");
126
- lines.push("Input Schema:");
127
- lines.push(typeof payload.inputSchema === "string"
128
- ? payload.inputSchema
129
- : JSON.stringify(payload.inputSchema, null, 2));
130
- }
131
- if (payload.outputSchema !== undefined) {
132
- lines.push("");
133
- lines.push("Output Schema:");
134
- lines.push(typeof payload.outputSchema === "string"
135
- ? payload.outputSchema
136
- : JSON.stringify(payload.outputSchema, null, 2));
137
- }
138
- lines.push("");
139
- lines.push(`Recommended Input: ${payload.inputAdvice.recommendedMode}`);
140
- if (payload.inputAdvice.reason) {
141
- lines.push(`Reason: ${payload.inputAdvice.reason}`);
142
- }
143
- const hasAssignments = Boolean(payload.inputAdvice.assignments &&
144
- Object.keys(payload.inputAdvice.assignments).length > 0);
145
- if (hasAssignments) {
146
- lines.push("");
147
- lines.push("Assignments:");
148
- for (const [key, op] of Object.entries(payload.inputAdvice.assignments)) {
149
- lines.push(` ${key}${op}`);
150
- }
151
- }
152
- if (payload.inputAdvice.recommendedMode === "flat" || hasAssignments) {
153
- lines.push("");
154
- lines.push("Syntax Reference:");
155
- lines.push(...(payload.syntaxReference || ACTION_DESCRIBE_SYNTAX_REFERENCE));
156
- }
157
- if (payload.inputAdvice.issues && payload.inputAdvice.issues.length > 0) {
158
- lines.push("");
159
- lines.push("Issues:");
160
- for (const issue of payload.inputAdvice.issues) {
161
- lines.push(` - ${issue.path}: ${issue.code}`);
162
- }
163
- }
164
- return lines.join("\n");
165
- }
166
79
  /**
167
80
  * 格式化渲染 Action 详情(编码顾问 Encoding Advisor)。
168
- * 统一复用 formatActionDetail 实现。
81
+ * 统一复用核心 formatActionDetail 实现。
169
82
  */
170
83
  export function renderActionDetail(action) {
171
84
  return formatActionDetail(action);
@@ -177,10 +90,19 @@ export function renderActionValidation(results) {
177
90
  const lines = [];
178
91
  for (const r of results) {
179
92
  if (r.valid) {
180
- lines.push(`[OK] ${r.id}: Valid`);
93
+ if (r.warnings && r.warnings.length > 0) {
94
+ lines.push(`[OK] ${r.id}: Valid (warnings: ${r.warnings.join("; ")})`);
95
+ }
96
+ else {
97
+ lines.push(`[OK] ${r.id}: Valid`);
98
+ }
181
99
  }
182
100
  else {
183
- lines.push(`[FAIL] ${r.id}: ${r.errors.join(", ")}`);
101
+ let msg = `[FAIL] ${r.id}: ${r.errors.join(", ")}`;
102
+ if (r.warnings && r.warnings.length > 0) {
103
+ msg += ` (warnings: ${r.warnings.join("; ")})`;
104
+ }
105
+ lines.push(msg);
184
106
  }
185
107
  }
186
108
  return lines.join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/cli",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "description": "ActionDock CLI facade for developing, testing, building, and exporting standalone Agent Actions and Skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,10 +34,10 @@
34
34
  "test": "node ../../scripts/run-tests.ts"
35
35
  },
36
36
  "dependencies": {
37
- "@actiondock/builder": "^2.12.0",
38
- "@actiondock/core": "^2.12.0",
39
- "@actiondock/mcp": "^2.12.0",
40
- "@actiondock/sdk": "^2.12.0",
37
+ "@actiondock/builder": "^2.13.0",
38
+ "@actiondock/core": "^2.13.0",
39
+ "@actiondock/mcp": "^2.13.0",
40
+ "@actiondock/sdk": "^2.13.0",
41
41
  "commander": "^13.1.0",
42
42
  "selfsigned": "^5.5.0"
43
43
  },