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.
- package/CHANGELOG.md +18 -0
- package/README.md +47 -64
- package/dist/bin/gd-cli.js +2 -2
- package/dist/src/api/app-runner.d.ts +1 -4
- package/dist/src/api/app-runner.js +10 -42
- package/dist/src/api/dam-client.d.ts +0 -7
- package/dist/src/api/dam-client.js +0 -17
- package/dist/src/commands/auth.js +5 -41
- package/dist/src/commands/creative.js +37 -45
- package/dist/src/commands/dam.js +248 -110
- package/dist/src/commands/org.js +17 -39
- package/dist/src/commands/skills.js +24 -114
- package/dist/src/commands/update.js +10 -85
- package/dist/src/commands/usage.js +4 -17
- package/dist/src/commands/workflows.d.ts +1 -1
- package/dist/src/commands/workflows.js +283 -199
- package/dist/src/core/output/envelope.d.ts +1 -0
- package/dist/src/core/output/envelope.js +76 -6
- package/dist/src/creative/contract.d.ts +28 -80
- package/dist/src/creative/contract.js +23 -44
- package/dist/src/creative/protocol.js +6 -6
- package/dist/src/io.d.ts +0 -2
- package/dist/src/io.js +0 -8
- package/dist/src/middleware/error-handler.d.ts +7 -1
- package/dist/src/middleware/error-handler.js +59 -54
- package/dist/src/program.d.ts +1 -0
- package/dist/src/program.js +138 -41
- package/dist/src/registry/agent-contracts.d.ts +14 -26
- package/dist/src/registry/agent-contracts.js +214 -64
- package/dist/src/utils/capability-presenter.js +6 -29
- package/dist/src/utils/config.d.ts +2 -2
- package/dist/src/utils/config.js +1 -1
- package/dist/src/utils/sensitive-path.js +1 -4
- package/package.json +2 -1
- package/skills/gd-cli/SKILL.md +6 -6
- package/skills/gd-cli/references/auth.md +3 -10
- package/skills/gd-cli/references/creative.md +11 -11
- package/skills/gd-cli/references/entitlement.md +2 -2
- package/skills/gd-cli/references/errors.md +4 -4
- package/skills/gd-cli/references/org.md +3 -3
- package/skills/gd-cli/references/sop.md +5 -5
- package/skills/gd-cli/references/update.md +4 -3
- package/skills/gd-cli/references/workflows.md +6 -5
- package/dist/src/utils/list-output-format.d.ts +0 -10
- 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,
|
|
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,
|
|
12
|
-
import {
|
|
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 {
|
|
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
|
|
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(
|
|
43
|
-
|
|
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),
|
|
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
|
-
|
|
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
|
|
92
|
-
const cmd = command;
|
|
56
|
+
.action(withErrorHandler(async (appId) => {
|
|
93
57
|
const payload = buildWorkflowExamples(String(appId));
|
|
94
|
-
|
|
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>
|
|
114
|
-
.usage("<workflow-id> [
|
|
62
|
+
.command("run <workflow-id>")
|
|
63
|
+
.usage("<workflow-id> [options]")
|
|
115
64
|
.description("运行 Workflow(场景编排)")
|
|
116
65
|
.option("--image <path>", "输入图片路径")
|
|
117
|
-
.option("--
|
|
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("--
|
|
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("--
|
|
138
|
-
.option("--timeout <seconds>", "同步等待超时", "600")
|
|
139
|
-
.allowUnknownOption(true)
|
|
75
|
+
.option("--json", "输出机器可读 JSON")
|
|
140
76
|
.allowExcessArguments(false);
|
|
141
|
-
callCmd.action(withErrorHandler(async (appId,
|
|
142
|
-
await runAppCall(appId,
|
|
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,
|
|
82
|
+
export async function runAppCall(appId, opts) {
|
|
149
83
|
const spec = resolveAppSpec(appId);
|
|
150
84
|
if (!spec)
|
|
151
|
-
throw new Error(
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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,
|
|
102
|
+
formatOutput(payload, "human");
|
|
180
103
|
}
|
|
181
104
|
return;
|
|
182
105
|
}
|
|
183
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
260
|
-
|
|
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
|
-
|
|
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
|
|
274
|
-
hasReferenceImage: !appNeedsReferenceImage(spec) || Boolean(firstReferenceImage(params.image
|
|
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
|
|
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(
|
|
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
|
|
439
|
+
function workflowFlagParams(opts) {
|
|
329
440
|
const params = {};
|
|
330
|
-
for (const [
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
params[
|
|
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
|
|
346
|
-
|
|
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
|
|
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
|
-
|
|
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} --
|
|
383
|
-
examples_command: `gd-cli workflows examples ${app.id}
|
|
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;
|