gaoding-cli 1.0.0-alpha.8 → 1.0.0-alpha.9

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 (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +47 -64
  3. package/dist/bin/gd-cli.js +2 -2
  4. package/dist/src/api/app-runner.d.ts +1 -4
  5. package/dist/src/api/app-runner.js +10 -42
  6. package/dist/src/api/dam-client.d.ts +0 -7
  7. package/dist/src/api/dam-client.js +0 -17
  8. package/dist/src/commands/auth.js +5 -41
  9. package/dist/src/commands/creative.js +37 -45
  10. package/dist/src/commands/dam.js +248 -110
  11. package/dist/src/commands/org.js +17 -39
  12. package/dist/src/commands/skills.js +24 -114
  13. package/dist/src/commands/update.js +10 -85
  14. package/dist/src/commands/usage.js +4 -17
  15. package/dist/src/commands/workflows.d.ts +1 -1
  16. package/dist/src/commands/workflows.js +283 -199
  17. package/dist/src/core/output/envelope.d.ts +1 -0
  18. package/dist/src/core/output/envelope.js +76 -6
  19. package/dist/src/creative/contract.d.ts +28 -80
  20. package/dist/src/creative/contract.js +23 -44
  21. package/dist/src/creative/protocol.js +6 -6
  22. package/dist/src/io.d.ts +0 -2
  23. package/dist/src/io.js +0 -8
  24. package/dist/src/middleware/error-handler.d.ts +7 -1
  25. package/dist/src/middleware/error-handler.js +59 -54
  26. package/dist/src/program.d.ts +1 -0
  27. package/dist/src/program.js +138 -41
  28. package/dist/src/registry/agent-contracts.d.ts +14 -26
  29. package/dist/src/registry/agent-contracts.js +214 -64
  30. package/dist/src/utils/capability-presenter.js +6 -29
  31. package/dist/src/utils/config.d.ts +2 -2
  32. package/dist/src/utils/config.js +1 -1
  33. package/dist/src/utils/sensitive-path.js +1 -4
  34. package/package.json +2 -1
  35. package/skills/gd-cli/SKILL.md +6 -6
  36. package/skills/gd-cli/references/auth.md +3 -10
  37. package/skills/gd-cli/references/creative.md +11 -11
  38. package/skills/gd-cli/references/entitlement.md +2 -2
  39. package/skills/gd-cli/references/errors.md +4 -4
  40. package/skills/gd-cli/references/org.md +3 -3
  41. package/skills/gd-cli/references/sop.md +5 -5
  42. package/skills/gd-cli/references/update.md +4 -3
  43. package/skills/gd-cli/references/workflows.md +6 -5
  44. package/dist/src/utils/list-output-format.d.ts +0 -10
  45. package/dist/src/utils/list-output-format.js +0 -34
@@ -1,83 +1,48 @@
1
1
  import { AppRunner } from "../api/app-runner.js";
2
2
  import { withErrorHandler } from "../middleware/error-handler.js";
3
3
  import { formatOutput, rowsForAppList } from "../middleware/output.js";
4
- import { resolveInvokeOutputFormat, resolveListOutputFormat, } from "../utils/list-output-format.js";
5
4
  import { resolveAppActivityLabel } from "../utils/tool-display.js";
6
5
  import { createSpinner } from "../utils/spinner.js";
7
- import { appendAppRunQuickExample } from "../utils/help-verify.js";
8
6
  import { writeAppDeliverables } from "../utils/write-app-deliverables.js";
9
- import { emitJson, wrapEnvelope, shouldEmitEnvelope } from "../core/output/envelope.js";
7
+ import { emitJson, failedEnvelope, publicCliErrorBody, wrapEnvelope, } from "../core/output/envelope.js";
10
8
  import { DIRECT_APPS, resolveAppSpec, } from "../registry/direct-registry.js";
11
- import { buildWorkflowExamples, buildWorkflowSchema, buildWorkflowManifest, rowsForWorkflowManifest, } from "../registry/agent-contracts.js";
12
- import { applySetEntries, collectArg, readJsonInput } from "../utils/input-json.js";
13
- import { setStaticHelp } from "../utils/static-help.js";
9
+ import { buildWorkflowExamples, buildWorkflowSchema, PUBLIC_WORKFLOW_MEDIA_PATTERN, resolvePublicWorkflowOutputDefinition, } from "../registry/agent-contracts.js";
10
+ import { readJsonInput } from "../utils/input-json.js";
14
11
  import { printCapabilityExamples, printCapabilitySchema, } from "../utils/capability-presenter.js";
15
- import { capabilityMetadata } from "../utils/capability-metadata.js";
12
+ import { CliUsageError } from "../core/output/cli-error.js";
16
13
  import { assertReferenceImageAvailable, firstReferenceImage, } from "../utils/reference-image.js";
17
14
  import { buildEcommerceInputGuidance } from "../utils/ecommerce-input-guidance.js";
18
15
  import { resolveSafeOutputDir } from "../utils/output-path.js";
19
16
  import { setAuthRequirement } from "../middleware/auth.js";
20
- const INTERNAL_OPTS = new Set([
21
- "outputDir",
22
- "save",
23
- "async",
24
- "timeout",
25
- "dryRun",
26
- "set",
27
- "asset",
28
- ]);
17
+ const PUBLIC_WORKFLOW_MEDIA_KEY = new RegExp(PUBLIC_WORKFLOW_MEDIA_PATTERN);
29
18
  export function registerWorkflowsCommand(program) {
30
19
  const workflows = program
31
20
  .command("workflows")
32
21
  .usage("<command> [options]")
33
- .description("完整创意场景 Workflows")
34
- .configureHelp({ showGlobalOptions: true });
35
- setStaticHelp(workflows, WORKFLOWS_HELP);
22
+ .description("完整创意场景 Workflows");
36
23
  const list = workflows
37
24
  .command("list")
38
- .alias("ls")
39
25
  .description("列出内置 Workflows")
26
+ .option("--json", "输出机器可读 JSON")
40
27
  .action(withErrorHandler(async (_options, command) => {
41
28
  const cmd = command;
42
- const publicItems = DIRECT_APPS.map(toPublicAppPayload);
43
- const format = resolveListOutputFormat(cmd);
44
- if (shouldEmitEnvelope(format)) {
29
+ const publicItems = DIRECT_APPS.map(toPublicWorkflowPayload);
30
+ if (cmd.opts().json === true) {
45
31
  emitJson(wrapEnvelope({ workflows: publicItems }, { command: "workflows list" }));
46
32
  }
47
- else if (format === "json") {
48
- formatOutput(publicItems, format);
49
- }
50
33
  else {
51
- formatOutput(rowsForAppList(publicItems), format);
34
+ formatOutput(rowsForAppList(publicItems), "table");
52
35
  }
53
36
  }));
54
37
  setAuthRequirement(list, "none");
55
- const info = workflows
56
- .command("info <workflow-id>")
57
- .description("查看 Workflow 详情")
58
- .action(withErrorHandler(async (appId, _opts, command) => {
59
- const cmd = command;
60
- const detail = resolveAppSpec(String(appId));
61
- if (!detail)
62
- throw new Error(`未找到应用: ${String(appId)}`);
63
- const payload = toPublicAppPayload(detail);
64
- const format = cmd.parent?.parent?.opts().format ?? "json";
65
- if (shouldEmitEnvelope(format)) {
66
- emitJson(wrapEnvelope(payload, { command: "workflows info", workflow_id: appId }));
67
- }
68
- else {
69
- formatOutput(payload, format);
70
- }
71
- }));
72
- setAuthRequirement(info, "none");
73
38
  const schema = workflows
74
39
  .command("schema <workflow-id>")
75
40
  .description("查看 Workflow 输入/输出 JSON Schema")
41
+ .option("--json", "输出完整 JSON Schema")
76
42
  .action(withErrorHandler(async (appId, _opts, command) => {
77
43
  const cmd = command;
78
44
  const payload = buildWorkflowSchema(String(appId));
79
- const format = resolveListOutputFormat(cmd);
80
- if (format === "json") {
45
+ if (cmd.opts().json === true) {
81
46
  formatOutput(payload, "json");
82
47
  }
83
48
  else {
@@ -88,101 +53,57 @@ export function registerWorkflowsCommand(program) {
88
53
  const examples = workflows
89
54
  .command("examples <workflow-id>")
90
55
  .description("查看 Workflow 调用示例")
91
- .action(withErrorHandler(async (appId, _opts, command) => {
92
- const cmd = command;
56
+ .action(withErrorHandler(async (appId) => {
93
57
  const payload = buildWorkflowExamples(String(appId));
94
- const format = resolveListOutputFormat(cmd);
95
- if (format === "json") {
96
- formatOutput(payload, "json");
97
- }
98
- else {
99
- printCapabilityExamples(payload);
100
- }
58
+ printCapabilityExamples(payload);
101
59
  }));
102
60
  setAuthRequirement(examples, "none");
103
- const manifest = workflows
104
- .command("manifest")
105
- .description("输出 Workflows 机器可读能力清单")
106
- .action((_opts, command) => {
107
- const manifest = buildWorkflowManifest();
108
- const format = resolveListOutputFormat(command);
109
- formatOutput(format === "json" ? manifest : rowsForWorkflowManifest(manifest), format);
110
- });
111
- setAuthRequirement(manifest, "none");
112
61
  const callCmd = workflows
113
- .command("run <workflow-id> [input]")
114
- .usage("<workflow-id> [input] [options]")
62
+ .command("run <workflow-id>")
63
+ .usage("<workflow-id> [options]")
115
64
  .description("运行 Workflow(场景编排)")
116
65
  .option("--image <path>", "输入图片路径")
117
- .option("--images <paths>", "多张图片(逗号分隔)")
118
- .option("--prompt <text>", "文字描述 / brief")
119
- .option("--brief <text>", "同 --prompt")
120
- .option("--platforms <list>", "目标平台(逗号分隔)")
121
- .option("--style <style>", "风格")
66
+ .option("--brief <text>", "创作需求说明")
122
67
  .option("--ratio <ratio>", "画面比例")
123
68
  .option("--ratios <list>", "多比例输出列表,如 16:9,1:1,4:5")
124
69
  .option("--copy-mode <mode>", "文案模式: auto / render-text / layout-copy / no-text / ask")
125
70
  .option("--model <code-or-name>", "生图模型 code / 名称 / auto")
126
- .option("--duration <value>", "视频时长参数")
127
- .option("--input <path>", "从 JSON 文件读取输入;非 .json 文件作为 Workflow input 参数")
128
- .option("--set <key=value>", "直接传入单个字段,可重复", collectArg)
129
- .option("--asset <id-or-url>", "传入 DAM asset_id 或 CDN URL,可重复", collectArg)
130
- .option("--assets-dir <dir>", "商品图片目录")
131
- .option("--count <n>", "目标产出数量", parseInt)
132
- .option("--concurrency <n>", "并发数", parseInt)
71
+ .option("--input <path>", "从 JSON 文件读取完整输入;传 - 表示读取 stdin")
133
72
  .option("--dry-run", "只输出计划,不实际调用业务接口", false)
134
- .option("--save <dir>", "同 --output-dir")
135
73
  .option("-o, --output-dir <dir>", "结果输出目录", "./output")
136
74
  .option("--allow-sensitive-path", "确认上传默认受保护的本地路径", false)
137
- .option("--async", "异步模式", false)
138
- .option("--timeout <seconds>", "同步等待超时", "600")
139
- .allowUnknownOption(true)
75
+ .option("--json", "输出机器可读 JSON")
140
76
  .allowExcessArguments(false);
141
- callCmd.action(withErrorHandler(async (appId, input, options, command) => {
142
- await runAppCall(appId, input, options, command);
77
+ callCmd.action(withErrorHandler(async (appId, options) => {
78
+ await runAppCall(appId, options);
143
79
  }));
144
80
  setAuthRequirement(callCmd, (command) => command.opts().dryRun === true ? "none" : "current-org");
145
- appendAppRunQuickExample(callCmd, "gd-cli workflows run");
146
- callCmd.addHelpText("afterAll", WORKFLOW_RUN_HELP);
147
81
  }
148
- export async function runAppCall(appId, inputRef, opts, cmd) {
82
+ export async function runAppCall(appId, opts) {
149
83
  const spec = resolveAppSpec(appId);
150
84
  if (!spec)
151
- throw new Error(`未找到应用: ${appId}`);
152
- const inputOption = optionalString(opts.input);
153
- const inputOptionIsJson = inputOption ? shouldReadJsonInput(inputOption) : false;
154
- const inputArgIsJson = inputRef ? shouldReadJsonInput(inputRef) : false;
155
- let params = {};
156
- if (inputArgIsJson && inputRef) {
157
- params = { ...params, ...(await readJsonInput(inputRef)) };
158
- }
159
- if (inputOptionIsJson && inputOption) {
160
- params = { ...params, ...(await readJsonInput(inputOption)) };
161
- }
162
- params = {
163
- ...params,
164
- ...extractAppParams(cmd, opts, { skipInput: inputOptionIsJson }),
85
+ throw new Error(`未找到 Workflow: ${appId}`);
86
+ const inputRef = optionalString(opts.input);
87
+ const inputParams = inputRef
88
+ ? stripSensitiveWorkflowInput(await readJsonInput(inputRef))
89
+ : {};
90
+ const params = {
91
+ ...inputParams,
92
+ ...workflowFlagParams(opts),
165
93
  };
166
- if (inputRef && !inputArgIsJson)
167
- params.input = inputRef;
168
- applySetEntries(params, opts.set);
169
- applyAssetEntries(params, opts.asset);
170
- if (opts.save)
171
- opts.outputDir = opts.save;
94
+ validateWorkflowInput(spec, params);
95
+ const json = opts.json === true;
172
96
  if (opts.dryRun) {
173
97
  const payload = buildAppDryRunPlan(spec.id, params, opts);
174
- const format = resolveInvokeOutputFormat(cmd);
175
- if (shouldEmitEnvelope(format)) {
98
+ if (json) {
176
99
  emitJson(wrapEnvelope(payload, { command: "workflows run", workflow_id: spec.id }));
177
100
  }
178
101
  else {
179
- formatOutput(payload, format);
102
+ formatOutput(payload, "human");
180
103
  }
181
104
  return;
182
105
  }
183
- if (!opts.async) {
184
- resolveSafeOutputDir(String(opts.outputDir ?? "./output"));
185
- }
106
+ resolveSafeOutputDir(String(opts.outputDir ?? "./output"));
186
107
  assertAppReferenceImageAvailable(spec, params);
187
108
  const label = await resolveAppActivityLabel(spec.id);
188
109
  const spinner = createSpinner(`${label} · 处理中…`);
@@ -192,10 +113,7 @@ export async function runAppCall(appId, inputRef, opts, cmd) {
192
113
  let result;
193
114
  try {
194
115
  result = await runner.run(spec.id, params, {
195
- async: !!opts.async,
196
- timeout: parseInt(String(opts.timeout)),
197
- outputDir: opts.outputDir,
198
- concurrency: typeof opts.concurrency === "number" ? opts.concurrency : undefined,
116
+ allowSensitivePath: opts.allowSensitivePath === true,
199
117
  onStepStart: (step) => {
200
118
  if (process.stderr.isTTY)
201
119
  spinner.text = `${label} · ${step.step_name}…`;
@@ -228,7 +146,7 @@ export async function runAppCall(appId, inputRef, opts, cmd) {
228
146
  spinner.fail(`${label} · 失败`);
229
147
  throw error;
230
148
  }
231
- if (result.status === "completed" && result.outputs && !opts.async) {
149
+ if (result.status === "completed" && result.outputs) {
232
150
  try {
233
151
  const pack = await writeAppDeliverables({
234
152
  appId: spec.id,
@@ -256,13 +174,205 @@ export async function runAppCall(appId, inputRef, opts, cmd) {
256
174
  else
257
175
  spinner.stop();
258
176
  }
259
- const format = resolveInvokeOutputFormat(cmd);
260
- if (shouldEmitEnvelope(format)) {
261
- emitJson(wrapEnvelope(result, { command: "workflows run", workflow_id: spec.id }));
177
+ if (json) {
178
+ emitPublicWorkflowJson(spec.id, result);
262
179
  }
263
180
  else {
264
- formatOutput(result, format, { activityLabel: label });
181
+ formatPublicWorkflowHuman(spec.id, result, label);
265
182
  }
183
+ if (result.status === "failed") {
184
+ process.exitCode = 1;
185
+ }
186
+ }
187
+ function emitPublicWorkflowJson(workflowId, result) {
188
+ const meta = { command: "workflows run", workflow_id: workflowId };
189
+ if (result.status === "failed") {
190
+ const envelope = failedEnvelope(projectPublicWorkflowError(result.error), meta);
191
+ envelope.task_id = result.task_id;
192
+ emitJson(envelope);
193
+ return;
194
+ }
195
+ const envelope = wrapEnvelope(projectPublicWorkflowResult(workflowId, result), meta);
196
+ envelope.task_id = result.task_id;
197
+ emitJson(envelope);
198
+ }
199
+ function formatPublicWorkflowHuman(workflowId, result, activityLabel) {
200
+ if (result.status === "failed") {
201
+ formatOutput({
202
+ status: "failed",
203
+ task_id: result.task_id,
204
+ error: projectPublicWorkflowError(result.error),
205
+ }, "human", { activityLabel });
206
+ return;
207
+ }
208
+ const projected = projectPublicWorkflowResult(workflowId, result);
209
+ formatOutput({
210
+ status: "completed",
211
+ task_id: result.task_id,
212
+ outputs: projected.outputs,
213
+ deliverable_dir: projected.deliverable_dir,
214
+ deliverable_zip: projected.deliverable_zip,
215
+ deliverable_readme: projected.deliverable_readme,
216
+ }, "human", { activityLabel });
217
+ }
218
+ function projectPublicWorkflowResult(workflowId, result) {
219
+ return {
220
+ workflow_id: workflowId,
221
+ outputs: projectPublicWorkflowOutputs(workflowId, result.outputs),
222
+ ...(result.deliverable_dir ? { deliverable_dir: result.deliverable_dir } : {}),
223
+ ...(result.deliverable_zip ? { deliverable_zip: result.deliverable_zip } : {}),
224
+ ...(result.deliverable_readme ? { deliverable_readme: result.deliverable_readme } : {}),
225
+ };
226
+ }
227
+ function projectPublicWorkflowOutputs(workflowId, value) {
228
+ if (!value || typeof value !== "object" || Array.isArray(value))
229
+ return {};
230
+ const definition = resolvePublicWorkflowOutputDefinition(workflowId);
231
+ return Object.fromEntries(Object.entries(value)
232
+ .flatMap(([key, item]) => {
233
+ const projected = projectPublicWorkflowOutput(definition, key, item);
234
+ return projected === undefined ? [] : [[key, projected]];
235
+ }));
236
+ }
237
+ function projectPublicWorkflowOutput(definition, key, value) {
238
+ if (definition.textFields.includes(key))
239
+ return publicString(value);
240
+ if (definition.mediaFields.includes(key) ||
241
+ (definition.mediaPattern !== undefined && isPublicKvKey(key))) {
242
+ return projectPublicWorkflowDeliverable(value);
243
+ }
244
+ if (key === "assets")
245
+ return projectPublicWorkflowAssets(value);
246
+ if (key === "copy_plan")
247
+ return projectPublicCopyPlan(value);
248
+ if (key === "overlay_copy") {
249
+ return projectPublicNamedStringRecord(value, definition.overlayFields, definition.mediaPattern !== undefined);
250
+ }
251
+ if (key === "category_profile")
252
+ return projectPublicCategoryProfile(value);
253
+ if (key === "ratios")
254
+ return projectPublicWorkflowRatios(definition, value);
255
+ if (key === "selling_points" && definition.sellingPoints) {
256
+ return publicStringArray(value);
257
+ }
258
+ return undefined;
259
+ }
260
+ function projectPublicWorkflowAssets(value) {
261
+ if (!Array.isArray(value))
262
+ return [];
263
+ return value
264
+ .filter((item) => item && typeof item === "object" && !Array.isArray(item))
265
+ .map((item) => {
266
+ const asset = item;
267
+ return compactPublicRecord({
268
+ label: publicString(asset.label),
269
+ role: publicString(asset.role),
270
+ type: publicString(asset.type),
271
+ url: publicString(asset.url),
272
+ });
273
+ });
274
+ }
275
+ function projectPublicWorkflowDeliverable(value) {
276
+ if (!value || typeof value !== "object" || Array.isArray(value))
277
+ return undefined;
278
+ const deliverable = value;
279
+ return compactPublicRecord({
280
+ url: publicString(deliverable.url),
281
+ mime_type: publicString(deliverable.mime_type),
282
+ urls: publicStringArray(deliverable.urls),
283
+ });
284
+ }
285
+ function projectPublicCopyPlan(value) {
286
+ if (!value || typeof value !== "object" || Array.isArray(value))
287
+ return undefined;
288
+ const plan = value;
289
+ return compactPublicRecord({
290
+ mode: publicString(plan.mode),
291
+ render_text: typeof plan.render_text === "boolean" ? plan.render_text : undefined,
292
+ image_model: publicString(plan.image_model),
293
+ });
294
+ }
295
+ function projectPublicCategoryProfile(value) {
296
+ if (!value || typeof value !== "object" || Array.isArray(value))
297
+ return undefined;
298
+ const profile = value;
299
+ return compactPublicRecord({
300
+ id: publicString(profile.id),
301
+ label: publicString(profile.label),
302
+ guidance: publicString(profile.guidance),
303
+ });
304
+ }
305
+ function projectPublicWorkflowRatios(definition, value) {
306
+ if (definition.ratios === "array") {
307
+ return Array.isArray(value) ? publicStringArray(value) : undefined;
308
+ }
309
+ return projectPublicNamedStringRecord(value, definition.ratioFields);
310
+ }
311
+ function projectPublicNamedStringRecord(value, allowedKeys, allowDynamicKv = false) {
312
+ if (!value || typeof value !== "object" || Array.isArray(value))
313
+ return undefined;
314
+ const entries = Object.entries(value)
315
+ .flatMap(([key, item]) => (allowedKeys.includes(key) || (allowDynamicKv && isPublicKvKey(key))) &&
316
+ typeof item === "string"
317
+ ? [[key, item]]
318
+ : []);
319
+ return Object.fromEntries(entries);
320
+ }
321
+ function isPublicKvKey(key) {
322
+ return PUBLIC_WORKFLOW_MEDIA_KEY.test(key);
323
+ }
324
+ function projectPublicWorkflowError(value) {
325
+ if (typeof value === "string") {
326
+ return { code: "WORKFLOW_FAILED", type: "business", message: value };
327
+ }
328
+ const error = value && typeof value === "object" && !Array.isArray(value)
329
+ ? value
330
+ : {};
331
+ const type = publicErrorType(error.type);
332
+ return publicCliErrorBody({
333
+ code: publicString(error.code) ?? "WORKFLOW_FAILED",
334
+ type,
335
+ message: publicString(error.message) ?? "Workflow 执行失败",
336
+ reason: publicString(error.reason),
337
+ hint: publicString(error.hint),
338
+ retryable: typeof error.retryable === "boolean" ? error.retryable : undefined,
339
+ diagnostic_context: error.diagnostic_context && typeof error.diagnostic_context === "object" &&
340
+ !Array.isArray(error.diagnostic_context)
341
+ ? error.diagnostic_context
342
+ : undefined,
343
+ next_command: publicString(error.next_command),
344
+ next_steps: publicStringArray(error.next_steps),
345
+ pricing_url: publicString(error.pricing_url),
346
+ user_action_required: publicString(error.user_action_required),
347
+ forbidden_actions: publicStringArray(error.forbidden_actions),
348
+ allow_org_check: typeof error.allow_org_check === "boolean" ? error.allow_org_check : undefined,
349
+ agent_message_template: publicString(error.agent_message_template),
350
+ });
351
+ }
352
+ function publicErrorType(value) {
353
+ return value === "business" ||
354
+ value === "auth" ||
355
+ value === "network" ||
356
+ value === "validation" ||
357
+ value === "unknown"
358
+ ? value
359
+ : "unknown";
360
+ }
361
+ function publicString(value) {
362
+ return typeof value === "string" && value ? value : undefined;
363
+ }
364
+ function publicStringArray(value) {
365
+ if (!Array.isArray(value))
366
+ return undefined;
367
+ const items = value.filter((item) => typeof item === "string");
368
+ return items.length > 0 ? items : undefined;
369
+ }
370
+ function compactPublicRecord(value) {
371
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
372
+ }
373
+ function stripSensitiveWorkflowInput(input) {
374
+ const { allow_sensitive_path: _allowSensitivePath, "allow-sensitive-path": _allowSensitivePathKebab, ...safeInput } = input;
375
+ return safeInput;
266
376
  }
267
377
  function buildAppDryRunPlan(appId, params, opts) {
268
378
  const spec = resolveAppSpec(appId);
@@ -270,16 +380,14 @@ function buildAppDryRunPlan(appId, params, opts) {
270
380
  throw new Error(`未找到应用: ${appId}`);
271
381
  const inputGuidance = buildEcommerceInputGuidance({
272
382
  appId: spec.id,
273
- brief: params.brief ?? params.prompt,
274
- hasReferenceImage: !appNeedsReferenceImage(spec) || Boolean(firstReferenceImage(params.image ?? params.images)),
383
+ brief: params.brief,
384
+ hasReferenceImage: !appNeedsReferenceImage(spec) || Boolean(firstReferenceImage(params.image)),
275
385
  });
276
386
  return {
277
387
  workflow: spec.id,
278
388
  dry_run: true,
279
389
  description: spec.description,
280
390
  inputs: params,
281
- required_tools: spec.required_tools,
282
- asset_operations: spec.asset_operations ?? [],
283
391
  outputs: spec.outputs ?? ["manifest.json", "README.md", "assets/"],
284
392
  estimated_duration: spec.estimated_duration,
285
393
  supports_batch: spec.supports_batch ?? false,
@@ -294,7 +402,7 @@ function buildAppDryRunPlan(appId, params, opts) {
294
402
  function assertAppReferenceImageAvailable(spec, params) {
295
403
  if (!appNeedsReferenceImage(spec))
296
404
  return;
297
- assertReferenceImageAvailable(params.image ?? params.images, {
405
+ assertReferenceImageAvailable(params.image, {
298
406
  context: `${spec.id} / ${spec.name}`,
299
407
  });
300
408
  }
@@ -312,11 +420,14 @@ function stepResultUrl(stepResult) {
312
420
  }
313
421
  function buildNextAppCommand(appId, params, opts) {
314
422
  const pieces = ["gd-cli", "workflows", "run", appId];
315
- for (const [key, value] of Object.entries({ ...params, ...opts })) {
316
- if (INTERNAL_OPTS.has(key) || value === undefined || value === false)
317
- continue;
423
+ for (const [key, value] of Object.entries(params)) {
318
424
  pieces.push(`--${kebabCase(key)}`, quoteShell(String(value)));
319
425
  }
426
+ if (opts.allowSensitivePath === true)
427
+ pieces.push("--allow-sensitive-path");
428
+ if (opts.outputDir && opts.outputDir !== "./output") {
429
+ pieces.push("--output-dir", quoteShell(String(opts.outputDir)));
430
+ }
320
431
  return pieces.join(" ");
321
432
  }
322
433
  function kebabCase(key) {
@@ -325,25 +436,47 @@ function kebabCase(key) {
325
436
  function quoteShell(value) {
326
437
  return /^[\w./:=,-]+$/.test(value) ? value : `"${value.replace(/"/g, '\\"')}"`;
327
438
  }
328
- function extractAppParams(command, opts, options = {}) {
439
+ function workflowFlagParams(opts) {
329
440
  const params = {};
330
- for (const [key, value] of Object.entries(opts)) {
331
- if (key === "input" && options.skipInput)
332
- continue;
333
- if (!INTERNAL_OPTS.has(key) && value !== undefined && value !== false) {
334
- params[key] = value;
335
- }
336
- }
337
- const rawArgs = command.args.slice(1);
338
- for (let i = 0; i < rawArgs.length; i += 2) {
339
- if (rawArgs[i]?.startsWith("--")) {
340
- params[rawArgs[i].replace(/^--/, "")] = rawArgs[i + 1];
341
- }
441
+ for (const [option, field] of [
442
+ ["image", "image"],
443
+ ["brief", "brief"],
444
+ ["ratio", "ratio"],
445
+ ["ratios", "ratios"],
446
+ ["copyMode", "copy_mode"],
447
+ ["model", "model"],
448
+ ]) {
449
+ const value = optionalString(opts[option]);
450
+ if (value !== undefined)
451
+ params[field] = value;
342
452
  }
343
453
  return params;
344
454
  }
345
- function shouldReadJsonInput(value) {
346
- return value === "-" || /\.json$/i.test(value);
455
+ function validateWorkflowInput(spec, params) {
456
+ const known = new Set(spec.input.map((param) => param.name));
457
+ const unknown = Object.keys(params).filter((key) => !known.has(key));
458
+ if (unknown.length > 0) {
459
+ throw new CliUsageError({
460
+ code: "PARAM_INVALID",
461
+ type: "validation",
462
+ message: `Workflow 输入不支持字段: ${unknown.join(", ")}`,
463
+ hint: `运行 gd-cli workflows schema ${spec.id} --json 查看完整输入结构。`,
464
+ retryable: false,
465
+ });
466
+ }
467
+ const missing = spec.input
468
+ .filter((param) => param.required)
469
+ .map((param) => param.name)
470
+ .filter((name) => typeof params[name] !== "string" || !String(params[name]).trim());
471
+ if (missing.length > 0) {
472
+ throw new CliUsageError({
473
+ code: "PARAM_MISSING",
474
+ type: "validation",
475
+ message: `Workflow 缺少必填输入: ${missing.join(", ")}`,
476
+ hint: `运行 gd-cli workflows schema ${spec.id} 查看输入说明。`,
477
+ retryable: false,
478
+ });
479
+ }
347
480
  }
348
481
  function optionalString(value) {
349
482
  if (typeof value !== "string")
@@ -351,66 +484,17 @@ function optionalString(value) {
351
484
  const trimmed = value.trim();
352
485
  return trimmed ? trimmed : undefined;
353
486
  }
354
- function applyAssetEntries(target, entries) {
355
- if (!Array.isArray(entries) || entries.length === 0)
356
- return;
357
- const assets = entries.map(String).filter(Boolean);
358
- if (assets.length === 0)
359
- return;
360
- target.assets = assets;
361
- if (!target.image && assets.length === 1)
362
- target.image = assets[0];
363
- if (!target.images && assets.length > 1)
364
- target.images = assets;
365
- }
366
- function toPublicAppPayload(app) {
487
+ function toPublicWorkflowPayload(app) {
367
488
  return {
368
489
  id: app.id,
369
- aliases: app.aliases ?? [],
370
490
  name: app.name,
371
491
  description: app.description,
372
492
  category: app.category,
373
493
  estimated_duration: app.estimated_duration,
374
- input: app.input,
375
- output: app.output,
376
- outputs: app.outputs ?? [],
377
- examples: app.examples ?? [],
378
- supports_batch: app.supports_batch ?? false,
494
+ deliverables: app.outputs ?? [],
379
495
  consumes_quota: app.consumes_quota ?? false,
380
- ...capabilityMetadata("app", app.id),
381
496
  canonical_command: `gd-cli workflows run ${app.id}`,
382
- schema_command: `gd-cli workflows schema ${app.id} --format json`,
383
- examples_command: `gd-cli workflows examples ${app.id} --format json`,
497
+ schema_command: `gd-cli workflows schema ${app.id} --json`,
498
+ examples_command: `gd-cli workflows examples ${app.id}`,
384
499
  };
385
500
  }
386
- const WORKFLOWS_HELP = `
387
- gd-cli workflows <command> [options]
388
-
389
- 用于运行完整业务场景,例如商品图库素材包、详情页/A+模块素材包、小红书种草图文包。
390
- 一个 Workflow 通常会编排多个内部能力,并输出一组可交付物料。
391
-
392
- 常用命令:
393
- list/ls 列出内置 Workflows
394
- info <workflow-id> 查看 Workflow 详情
395
- run <workflow-id> 运行 Workflow,适合 Agent / 脚本
396
- manifest 输出 Workflows 能力清单
397
- schema <workflow-id> 查看 Workflow 输入/输出 Schema
398
- examples <workflow-id> 查看 Workflow 调用示例
399
-
400
- 示例:
401
- gd-cli workflows list
402
- gd-cli workflows info listing-gallery
403
- gd-cli workflows schema pdp-a-plus
404
- gd-cli workflows run social-seeding-pack --input brief.json --format json
405
- `;
406
- const WORKFLOW_RUN_HELP = `
407
-
408
- JSON 输入:
409
- gd-cli workflows run listing-gallery --input brief.json --format json
410
- cat brief.json | gd-cli workflows run social-seeding-pack - --format json
411
- gd-cli workflows run campaign-kv-pack --set brief="城市跑鞋新品 Campaign" --set image=./sku.png --format json
412
-
413
- 说明:
414
- --input <file> 传入 .json 时作为参数对象读取;其他文件类型会作为 Workflow 的 input 字段。
415
- --dry-run 只校验输入和执行计划,不真正生成。
416
- `;
@@ -36,5 +36,6 @@ export interface ResultEnvelope<T = unknown> {
36
36
  }
37
37
  export declare function wrapEnvelope<T>(payload: T, meta?: Record<string, unknown>): ResultEnvelope<T>;
38
38
  export declare function failedEnvelope(error: CliErrorBody, meta?: Record<string, unknown>): ResultEnvelope<null>;
39
+ export declare function publicCliErrorBody(error: CliErrorBody): CliErrorBody;
39
40
  export declare function emitJson(data: unknown): void;
40
41
  export declare function shouldEmitEnvelope(format: string, rootFormat?: string): boolean;