gaoding-cli 1.0.0-alpha.5
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 +88 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/SECURITY.md +34 -0
- package/dist/bin/gd-cli.d.ts +2 -0
- package/dist/bin/gd-cli.js +10 -0
- package/dist/src/api/app-runner.d.ts +33 -0
- package/dist/src/api/app-runner.js +1177 -0
- package/dist/src/api/dam-client.d.ts +111 -0
- package/dist/src/api/dam-client.js +897 -0
- package/dist/src/api/direct-auth.d.ts +3 -0
- package/dist/src/api/direct-auth.js +11 -0
- package/dist/src/api/direct-client.d.ts +27 -0
- package/dist/src/api/direct-client.js +151 -0
- package/dist/src/api/direct-tools.d.ts +43 -0
- package/dist/src/api/direct-tools.js +1155 -0
- package/dist/src/api/hub-client.d.ts +20 -0
- package/dist/src/api/hub-client.js +99 -0
- package/dist/src/api/sso-client.d.ts +60 -0
- package/dist/src/api/sso-client.js +140 -0
- package/dist/src/api/task-poller.d.ts +38 -0
- package/dist/src/api/task-poller.js +72 -0
- package/dist/src/api/tool-invoker.d.ts +12 -0
- package/dist/src/api/tool-invoker.js +11 -0
- package/dist/src/api/usage-client.d.ts +39 -0
- package/dist/src/api/usage-client.js +179 -0
- package/dist/src/auth/credential-store-v2.d.ts +12 -0
- package/dist/src/auth/credential-store-v2.js +59 -0
- package/dist/src/commands/auth.d.ts +2 -0
- package/dist/src/commands/auth.js +191 -0
- package/dist/src/commands/dam.d.ts +2 -0
- package/dist/src/commands/dam.js +293 -0
- package/dist/src/commands/models.d.ts +2 -0
- package/dist/src/commands/models.js +78 -0
- package/dist/src/commands/org.d.ts +2 -0
- package/dist/src/commands/org.js +137 -0
- package/dist/src/commands/skills.d.ts +2 -0
- package/dist/src/commands/skills.js +213 -0
- package/dist/src/commands/tools.d.ts +4 -0
- package/dist/src/commands/tools.js +383 -0
- package/dist/src/commands/update.d.ts +2 -0
- package/dist/src/commands/update.js +167 -0
- package/dist/src/commands/usage.d.ts +7 -0
- package/dist/src/commands/usage.js +43 -0
- package/dist/src/commands/workflows.d.ts +3 -0
- package/dist/src/commands/workflows.js +398 -0
- package/dist/src/core/auth/auth-provider.d.ts +8 -0
- package/dist/src/core/auth/auth-provider.js +12 -0
- package/dist/src/core/auth/device-oauth-provider.d.ts +12 -0
- package/dist/src/core/auth/device-oauth-provider.js +124 -0
- package/dist/src/core/auth/org-manager.d.ts +26 -0
- package/dist/src/core/auth/org-manager.js +224 -0
- package/dist/src/core/auth/signature.d.ts +19 -0
- package/dist/src/core/auth/signature.js +33 -0
- package/dist/src/core/auth/types.d.ts +52 -0
- package/dist/src/core/auth/types.js +3 -0
- package/dist/src/core/output/cli-error.d.ts +5 -0
- package/dist/src/core/output/cli-error.js +8 -0
- package/dist/src/core/output/detector.d.ts +5 -0
- package/dist/src/core/output/detector.js +14 -0
- package/dist/src/core/output/engine.d.ts +32 -0
- package/dist/src/core/output/engine.js +77 -0
- package/dist/src/core/output/envelope.d.ts +40 -0
- package/dist/src/core/output/envelope.js +119 -0
- package/dist/src/core/output/index.d.ts +4 -0
- package/dist/src/core/output/index.js +4 -0
- package/dist/src/core/output/live-display.d.ts +15 -0
- package/dist/src/core/output/live-display.js +111 -0
- package/dist/src/core/output/result-card.d.ts +18 -0
- package/dist/src/core/output/result-card.js +39 -0
- package/dist/src/core/prompt/index.d.ts +31 -0
- package/dist/src/core/prompt/index.js +36 -0
- package/dist/src/io.d.ts +14 -0
- package/dist/src/io.js +31 -0
- package/dist/src/middleware/auth.d.ts +3 -0
- package/dist/src/middleware/auth.js +74 -0
- package/dist/src/middleware/error-handler.d.ts +3 -0
- package/dist/src/middleware/error-handler.js +431 -0
- package/dist/src/middleware/interactive-params.d.ts +14 -0
- package/dist/src/middleware/interactive-params.js +53 -0
- package/dist/src/middleware/output.d.ts +14 -0
- package/dist/src/middleware/output.js +546 -0
- package/dist/src/models/model-catalog.d.ts +26 -0
- package/dist/src/models/model-catalog.js +216 -0
- package/dist/src/program.d.ts +2 -0
- package/dist/src/program.js +152 -0
- package/dist/src/registry/agent-contracts.d.ts +43 -0
- package/dist/src/registry/agent-contracts.js +483 -0
- package/dist/src/registry/app-loader.d.ts +2 -0
- package/dist/src/registry/app-loader.js +70 -0
- package/dist/src/registry/direct-registry.d.ts +11 -0
- package/dist/src/registry/direct-registry.js +234 -0
- package/dist/src/registry/registry-cache.d.ts +10 -0
- package/dist/src/registry/registry-cache.js +52 -0
- package/dist/src/registry/registry-client.d.ts +28 -0
- package/dist/src/registry/registry-client.js +50 -0
- package/dist/src/registry/types.d.ts +69 -0
- package/dist/src/registry/types.js +1 -0
- package/dist/src/tty/auth-presenter.d.ts +3 -0
- package/dist/src/tty/auth-presenter.js +21 -0
- package/dist/src/update/install-source.d.ts +19 -0
- package/dist/src/update/install-source.js +139 -0
- package/dist/src/update/notifier.d.ts +2 -0
- package/dist/src/update/notifier.js +59 -0
- package/dist/src/update/update-checker.d.ts +22 -0
- package/dist/src/update/update-checker.js +110 -0
- package/dist/src/update/version.d.ts +9 -0
- package/dist/src/update/version.js +52 -0
- package/dist/src/utils/agent-skill-installer.d.ts +57 -0
- package/dist/src/utils/agent-skill-installer.js +389 -0
- package/dist/src/utils/capability-metadata.d.ts +18 -0
- package/dist/src/utils/capability-metadata.js +88 -0
- package/dist/src/utils/capability-presenter.d.ts +4 -0
- package/dist/src/utils/capability-presenter.js +190 -0
- package/dist/src/utils/config.d.ts +16 -0
- package/dist/src/utils/config.js +54 -0
- package/dist/src/utils/credential-permissions.d.ts +2 -0
- package/dist/src/utils/credential-permissions.js +22 -0
- package/dist/src/utils/ecommerce-input-guidance.d.ts +15 -0
- package/dist/src/utils/ecommerce-input-guidance.js +85 -0
- package/dist/src/utils/endpoints.d.ts +52 -0
- package/dist/src/utils/endpoints.js +258 -0
- package/dist/src/utils/enrich-upload-params.d.ts +6 -0
- package/dist/src/utils/enrich-upload-params.js +11 -0
- package/dist/src/utils/entitlement.d.ts +17 -0
- package/dist/src/utils/entitlement.js +146 -0
- package/dist/src/utils/gd-home.d.ts +7 -0
- package/dist/src/utils/gd-home.js +26 -0
- package/dist/src/utils/help-verify.d.ts +9 -0
- package/dist/src/utils/help-verify.js +41 -0
- package/dist/src/utils/input-json.d.ts +3 -0
- package/dist/src/utils/input-json.js +61 -0
- package/dist/src/utils/list-output-format.d.ts +10 -0
- package/dist/src/utils/list-output-format.js +34 -0
- package/dist/src/utils/logger.d.ts +6 -0
- package/dist/src/utils/logger.js +19 -0
- package/dist/src/utils/matting-compose.d.ts +10 -0
- package/dist/src/utils/matting-compose.js +45 -0
- package/dist/src/utils/open-browser.d.ts +1 -0
- package/dist/src/utils/open-browser.js +4 -0
- package/dist/src/utils/output-path.d.ts +5 -0
- package/dist/src/utils/output-path.js +70 -0
- package/dist/src/utils/redact.d.ts +3 -0
- package/dist/src/utils/redact.js +96 -0
- package/dist/src/utils/reference-image.d.ts +8 -0
- package/dist/src/utils/reference-image.js +43 -0
- package/dist/src/utils/resolve-app-local-images.d.ts +8 -0
- package/dist/src/utils/resolve-app-local-images.js +84 -0
- package/dist/src/utils/sensitive-path.d.ts +6 -0
- package/dist/src/utils/sensitive-path.js +69 -0
- package/dist/src/utils/settings-store.d.ts +42 -0
- package/dist/src/utils/settings-store.js +50 -0
- package/dist/src/utils/sleep.d.ts +1 -0
- package/dist/src/utils/sleep.js +3 -0
- package/dist/src/utils/spinner.d.ts +2 -0
- package/dist/src/utils/spinner.js +4 -0
- package/dist/src/utils/static-help.d.ts +2 -0
- package/dist/src/utils/static-help.js +3 -0
- package/dist/src/utils/tool-display.d.ts +10 -0
- package/dist/src/utils/tool-display.js +55 -0
- package/dist/src/utils/transient-network-error.d.ts +4 -0
- package/dist/src/utils/transient-network-error.js +40 -0
- package/dist/src/utils/url-safety.d.ts +6 -0
- package/dist/src/utils/url-safety.js +67 -0
- package/dist/src/utils/write-app-deliverables.d.ts +18 -0
- package/dist/src/utils/write-app-deliverables.js +386 -0
- package/package.json +91 -0
- package/skills/gd-cli/SKILL.md +57 -0
- package/skills/gd-cli/references/auth.md +31 -0
- package/skills/gd-cli/references/deliverables.md +22 -0
- package/skills/gd-cli/references/entitlement.md +21 -0
- package/skills/gd-cli/references/errors.md +19 -0
- package/skills/gd-cli/references/org.md +18 -0
- package/skills/gd-cli/references/sop.md +25 -0
- package/skills/gd-cli/references/tools.md +29 -0
- package/skills/gd-cli/references/update.md +16 -0
- package/skills/gd-cli/references/workflows.md +22 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { AppRunner } from "../api/app-runner.js";
|
|
2
|
+
import { withErrorHandler } from "../middleware/error-handler.js";
|
|
3
|
+
import { formatOutput, rowsForAppList } from "../middleware/output.js";
|
|
4
|
+
import { resolveInvokeOutputFormat, resolveListOutputFormat, } from "../utils/list-output-format.js";
|
|
5
|
+
import { resolveAppActivityLabel } from "../utils/tool-display.js";
|
|
6
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
7
|
+
import { appendAppRunQuickExample } from "../utils/help-verify.js";
|
|
8
|
+
import { writeAppDeliverables } from "../utils/write-app-deliverables.js";
|
|
9
|
+
import { emitJson, wrapEnvelope, shouldEmitEnvelope } from "../core/output/envelope.js";
|
|
10
|
+
import { DIRECT_APPS, resolveAppSpec, } from "../registry/direct-registry.js";
|
|
11
|
+
import { buildWorkflowExamples, buildWorkflowSchema, buildManifest, rowsForManifest, } from "../registry/agent-contracts.js";
|
|
12
|
+
import { applySetEntries, collectArg, readJsonInput } from "../utils/input-json.js";
|
|
13
|
+
import { setStaticHelp } from "../utils/static-help.js";
|
|
14
|
+
import { printCapabilityExamples, printCapabilitySchema, } from "../utils/capability-presenter.js";
|
|
15
|
+
import { capabilityMetadata } from "../utils/capability-metadata.js";
|
|
16
|
+
import { assertReferenceImageAvailable, firstReferenceImage, } from "../utils/reference-image.js";
|
|
17
|
+
import { buildEcommerceInputGuidance } from "../utils/ecommerce-input-guidance.js";
|
|
18
|
+
import { resolveSafeOutputDir } from "../utils/output-path.js";
|
|
19
|
+
const INTERNAL_OPTS = new Set([
|
|
20
|
+
"outputDir",
|
|
21
|
+
"save",
|
|
22
|
+
"async",
|
|
23
|
+
"timeout",
|
|
24
|
+
"dryRun",
|
|
25
|
+
"set",
|
|
26
|
+
"asset",
|
|
27
|
+
]);
|
|
28
|
+
export function registerWorkflowsCommand(program) {
|
|
29
|
+
const apps = program
|
|
30
|
+
.command("workflows")
|
|
31
|
+
.usage("<command> [options]")
|
|
32
|
+
.description("完整创意场景 Workflows")
|
|
33
|
+
.configureHelp({ showGlobalOptions: true });
|
|
34
|
+
setStaticHelp(apps, WORKFLOWS_HELP);
|
|
35
|
+
apps
|
|
36
|
+
.command("list")
|
|
37
|
+
.alias("ls")
|
|
38
|
+
.description("列出内置 Workflows")
|
|
39
|
+
.action(withErrorHandler(async (_options, command) => {
|
|
40
|
+
const cmd = command;
|
|
41
|
+
const publicItems = DIRECT_APPS.map(toPublicAppPayload);
|
|
42
|
+
const format = resolveListOutputFormat(cmd);
|
|
43
|
+
if (shouldEmitEnvelope(format)) {
|
|
44
|
+
emitJson(wrapEnvelope({ workflows: publicItems }, { command: "workflows ls" }));
|
|
45
|
+
}
|
|
46
|
+
else if (format === "json") {
|
|
47
|
+
formatOutput(publicItems, format);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
formatOutput(rowsForAppList(publicItems), format);
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
apps
|
|
54
|
+
.command("info <workflow-id>")
|
|
55
|
+
.description("查看 Workflow 详情")
|
|
56
|
+
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
57
|
+
const cmd = command;
|
|
58
|
+
const detail = resolveAppSpec(String(appId));
|
|
59
|
+
if (!detail)
|
|
60
|
+
throw new Error(`未找到应用: ${String(appId)}`);
|
|
61
|
+
const payload = toPublicAppPayload(detail);
|
|
62
|
+
const format = cmd.parent?.parent?.opts().format ?? "json";
|
|
63
|
+
if (shouldEmitEnvelope(format)) {
|
|
64
|
+
emitJson(wrapEnvelope(payload, { command: "workflows info", workflow_id: appId }));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
formatOutput(payload, format);
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
apps
|
|
71
|
+
.command("schema <workflow-id>")
|
|
72
|
+
.description("查看 Workflow 输入/输出 JSON Schema")
|
|
73
|
+
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
74
|
+
const cmd = command;
|
|
75
|
+
const payload = buildWorkflowSchema(String(appId));
|
|
76
|
+
const format = resolveListOutputFormat(cmd);
|
|
77
|
+
if (format === "json") {
|
|
78
|
+
formatOutput(payload, "json");
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
printCapabilitySchema(payload);
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
apps
|
|
85
|
+
.command("examples <workflow-id>")
|
|
86
|
+
.description("查看 Workflow 调用示例")
|
|
87
|
+
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
88
|
+
const cmd = command;
|
|
89
|
+
const payload = buildWorkflowExamples(String(appId));
|
|
90
|
+
const format = resolveListOutputFormat(cmd);
|
|
91
|
+
if (format === "json") {
|
|
92
|
+
formatOutput(payload, "json");
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
printCapabilityExamples(payload);
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
apps
|
|
99
|
+
.command("manifest")
|
|
100
|
+
.description("输出 Workflows 机器可读能力清单")
|
|
101
|
+
.action((_opts, command) => {
|
|
102
|
+
const manifest = buildManifest("workflows");
|
|
103
|
+
const format = resolveListOutputFormat(command);
|
|
104
|
+
formatOutput(format === "json" ? manifest : rowsForManifest(manifest), format);
|
|
105
|
+
});
|
|
106
|
+
const callCmd = apps
|
|
107
|
+
.command("run <workflow-id> [input]")
|
|
108
|
+
.usage("<workflow-id> [input] [options]")
|
|
109
|
+
.description("运行 Workflow(场景编排)")
|
|
110
|
+
.option("--image <path>", "输入图片路径")
|
|
111
|
+
.option("--images <paths>", "多张图片(逗号分隔)")
|
|
112
|
+
.option("--prompt <text>", "文字描述 / brief")
|
|
113
|
+
.option("--brief <text>", "同 --prompt")
|
|
114
|
+
.option("--platforms <list>", "目标平台(逗号分隔)")
|
|
115
|
+
.option("--style <style>", "风格")
|
|
116
|
+
.option("--ratio <ratio>", "画面比例")
|
|
117
|
+
.option("--ratios <list>", "多比例输出列表,如 16:9,1:1,4:5")
|
|
118
|
+
.option("--copy-mode <mode>", "文案模式: auto / render-text / layout-copy / no-text / ask")
|
|
119
|
+
.option("--model <code-or-name>", "生图模型 code / 名称 / auto")
|
|
120
|
+
.option("--duration <value>", "视频时长参数")
|
|
121
|
+
.option("--input <path>", "从 JSON 文件读取输入;非 .json 文件作为 Workflow input 参数")
|
|
122
|
+
.option("--set <key=value>", "直接传入单个字段,可重复", collectArg)
|
|
123
|
+
.option("--asset <id-or-url>", "传入 DAM asset_id 或 CDN URL,可重复", collectArg)
|
|
124
|
+
.option("--assets-dir <dir>", "商品图片目录")
|
|
125
|
+
.option("--count <n>", "目标产出数量", parseInt)
|
|
126
|
+
.option("--concurrency <n>", "并发数", parseInt)
|
|
127
|
+
.option("--dry-run", "只输出计划,不实际调用业务接口", false)
|
|
128
|
+
.option("--save <dir>", "同 --output-dir")
|
|
129
|
+
.option("-o, --output-dir <dir>", "结果输出目录", "./output")
|
|
130
|
+
.option("--allow-sensitive-path", "确认上传默认受保护的本地路径", false)
|
|
131
|
+
.option("--async", "异步模式", false)
|
|
132
|
+
.option("--timeout <seconds>", "同步等待超时", "600")
|
|
133
|
+
.allowUnknownOption(true)
|
|
134
|
+
.allowExcessArguments(false);
|
|
135
|
+
callCmd.action(withErrorHandler(async (appId, input, options, command) => {
|
|
136
|
+
await runAppCall(appId, input, options, command);
|
|
137
|
+
}));
|
|
138
|
+
appendAppRunQuickExample(callCmd, "gd-cli workflows run");
|
|
139
|
+
callCmd.addHelpText("afterAll", WORKFLOW_RUN_HELP);
|
|
140
|
+
}
|
|
141
|
+
export async function runAppCall(appId, inputRef, opts, cmd) {
|
|
142
|
+
const spec = resolveAppSpec(appId);
|
|
143
|
+
if (!spec)
|
|
144
|
+
throw new Error(`未找到应用: ${appId}`);
|
|
145
|
+
const inputOption = optionalString(opts.input);
|
|
146
|
+
const inputOptionIsJson = inputOption ? shouldReadJsonInput(inputOption) : false;
|
|
147
|
+
const inputArgIsJson = inputRef ? shouldReadJsonInput(inputRef) : false;
|
|
148
|
+
let params = {};
|
|
149
|
+
if (inputArgIsJson && inputRef) {
|
|
150
|
+
params = { ...params, ...(await readJsonInput(inputRef)) };
|
|
151
|
+
}
|
|
152
|
+
if (inputOptionIsJson && inputOption) {
|
|
153
|
+
params = { ...params, ...(await readJsonInput(inputOption)) };
|
|
154
|
+
}
|
|
155
|
+
params = {
|
|
156
|
+
...params,
|
|
157
|
+
...extractAppParams(cmd, opts, { skipInput: inputOptionIsJson }),
|
|
158
|
+
};
|
|
159
|
+
if (inputRef && !inputArgIsJson)
|
|
160
|
+
params.input = inputRef;
|
|
161
|
+
applySetEntries(params, opts.set);
|
|
162
|
+
applyAssetEntries(params, opts.asset);
|
|
163
|
+
if (opts.save)
|
|
164
|
+
opts.outputDir = opts.save;
|
|
165
|
+
if (opts.dryRun) {
|
|
166
|
+
const payload = buildAppDryRunPlan(spec.id, params, opts);
|
|
167
|
+
const format = resolveInvokeOutputFormat(cmd);
|
|
168
|
+
if (shouldEmitEnvelope(format)) {
|
|
169
|
+
emitJson(wrapEnvelope(payload, { command: "workflows run", workflow_id: spec.id }));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
formatOutput(payload, format);
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!opts.async) {
|
|
177
|
+
resolveSafeOutputDir(String(opts.outputDir ?? "./output"));
|
|
178
|
+
}
|
|
179
|
+
assertAppReferenceImageAvailable(spec, params);
|
|
180
|
+
const label = await resolveAppActivityLabel(spec.id);
|
|
181
|
+
const spinner = createSpinner(`${label} · 处理中…`);
|
|
182
|
+
if (process.stderr.isTTY)
|
|
183
|
+
spinner.start();
|
|
184
|
+
const runner = new AppRunner();
|
|
185
|
+
let result;
|
|
186
|
+
try {
|
|
187
|
+
result = await runner.run(spec.id, params, {
|
|
188
|
+
async: !!opts.async,
|
|
189
|
+
timeout: parseInt(String(opts.timeout)),
|
|
190
|
+
outputDir: opts.outputDir,
|
|
191
|
+
concurrency: typeof opts.concurrency === "number" ? opts.concurrency : undefined,
|
|
192
|
+
onStepStart: (step) => {
|
|
193
|
+
if (process.stderr.isTTY)
|
|
194
|
+
spinner.text = `${label} · ${step.step_name}…`;
|
|
195
|
+
},
|
|
196
|
+
onStepComplete: (step, stepResult) => {
|
|
197
|
+
const url = stepResultUrl(stepResult);
|
|
198
|
+
if (process.stderr.isTTY) {
|
|
199
|
+
spinner.text = `${label} · ${step.status === "failed" ? "失败" : "已完成"} ${step.step_name}`;
|
|
200
|
+
}
|
|
201
|
+
if (step.status === "completed" && url) {
|
|
202
|
+
if (process.stderr.isTTY) {
|
|
203
|
+
spinner.stop();
|
|
204
|
+
}
|
|
205
|
+
console.error(`已生成 ${step.step_name}: ${url}`);
|
|
206
|
+
if (process.stderr.isTTY) {
|
|
207
|
+
spinner.start(`${label} · 继续处理…`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else if (!process.stderr.isTTY && step.status === "failed") {
|
|
211
|
+
console.error(`步骤失败 ${step.step_name}`);
|
|
212
|
+
}
|
|
213
|
+
else if (!process.stderr.isTTY && step.status === "completed") {
|
|
214
|
+
console.error(`步骤完成 ${step.step_name}`);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (process.stderr.isTTY)
|
|
221
|
+
spinner.fail(`${label} · 失败`);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
if (result.status === "completed" && result.outputs && !opts.async) {
|
|
225
|
+
try {
|
|
226
|
+
const pack = await writeAppDeliverables({
|
|
227
|
+
appId: spec.id,
|
|
228
|
+
taskId: result.task_id,
|
|
229
|
+
outputs: result.outputs,
|
|
230
|
+
outputDir: opts.outputDir,
|
|
231
|
+
activityLabel: label,
|
|
232
|
+
});
|
|
233
|
+
result = {
|
|
234
|
+
...result,
|
|
235
|
+
deliverable_dir: pack.dir,
|
|
236
|
+
deliverable_zip: pack.zipPath,
|
|
237
|
+
deliverable_readme: pack.readmePath,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
catch (packErr) {
|
|
241
|
+
console.error(`警告: 交付物打包失败: ${packErr instanceof Error ? packErr.message : String(packErr)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (process.stderr.isTTY) {
|
|
245
|
+
if (result.status === "completed")
|
|
246
|
+
spinner.succeed(`${label} · 已完成`);
|
|
247
|
+
else if (result.status === "failed")
|
|
248
|
+
spinner.fail(`${label} · 失败`);
|
|
249
|
+
else
|
|
250
|
+
spinner.stop();
|
|
251
|
+
}
|
|
252
|
+
const format = resolveInvokeOutputFormat(cmd);
|
|
253
|
+
if (shouldEmitEnvelope(format)) {
|
|
254
|
+
emitJson(wrapEnvelope(result, { command: "workflows run", workflow_id: spec.id }));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
formatOutput(result, format, { activityLabel: label });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function buildAppDryRunPlan(appId, params, opts) {
|
|
261
|
+
const spec = resolveAppSpec(appId);
|
|
262
|
+
if (!spec)
|
|
263
|
+
throw new Error(`未找到应用: ${appId}`);
|
|
264
|
+
const inputGuidance = buildEcommerceInputGuidance({
|
|
265
|
+
appId: spec.id,
|
|
266
|
+
brief: params.brief ?? params.prompt,
|
|
267
|
+
hasReferenceImage: !appNeedsReferenceImage(spec) || Boolean(firstReferenceImage(params.image ?? params.images)),
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
workflow: spec.id,
|
|
271
|
+
dry_run: true,
|
|
272
|
+
description: spec.description,
|
|
273
|
+
inputs: params,
|
|
274
|
+
required_tools: spec.required_tools,
|
|
275
|
+
asset_operations: spec.asset_operations ?? [],
|
|
276
|
+
outputs: spec.outputs ?? ["manifest.json", "README.md", "assets/"],
|
|
277
|
+
estimated_duration: spec.estimated_duration,
|
|
278
|
+
supports_batch: spec.supports_batch ?? false,
|
|
279
|
+
reference_image_policy: appNeedsReferenceImage(spec)
|
|
280
|
+
? "required-original-image"
|
|
281
|
+
: "optional",
|
|
282
|
+
clarification_required: inputGuidance.clarification_required,
|
|
283
|
+
input_guidance: inputGuidance,
|
|
284
|
+
next_command: buildNextAppCommand(spec.id, params, opts),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function assertAppReferenceImageAvailable(spec, params) {
|
|
288
|
+
if (!appNeedsReferenceImage(spec))
|
|
289
|
+
return;
|
|
290
|
+
assertReferenceImageAvailable(params.image ?? params.images, {
|
|
291
|
+
context: `${spec.id} / ${spec.name}`,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function appNeedsReferenceImage(spec) {
|
|
295
|
+
return Boolean(spec.input?.some((param) => param.name === "image" && param.required));
|
|
296
|
+
}
|
|
297
|
+
function stepResultUrl(stepResult) {
|
|
298
|
+
if (!stepResult || typeof stepResult !== "object")
|
|
299
|
+
return "";
|
|
300
|
+
const outputs = stepResult.outputs;
|
|
301
|
+
if (!outputs || typeof outputs !== "object")
|
|
302
|
+
return "";
|
|
303
|
+
const url = outputs.url;
|
|
304
|
+
return typeof url === "string" ? url : "";
|
|
305
|
+
}
|
|
306
|
+
function buildNextAppCommand(appId, params, opts) {
|
|
307
|
+
const pieces = ["gd-cli", "workflows", "run", appId];
|
|
308
|
+
for (const [key, value] of Object.entries({ ...params, ...opts })) {
|
|
309
|
+
if (INTERNAL_OPTS.has(key) || value === undefined || value === false)
|
|
310
|
+
continue;
|
|
311
|
+
pieces.push(`--${kebabCase(key)}`, quoteShell(String(value)));
|
|
312
|
+
}
|
|
313
|
+
return pieces.join(" ");
|
|
314
|
+
}
|
|
315
|
+
function kebabCase(key) {
|
|
316
|
+
return key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/_/g, "-");
|
|
317
|
+
}
|
|
318
|
+
function quoteShell(value) {
|
|
319
|
+
return /^[\w./:=,-]+$/.test(value) ? value : `"${value.replace(/"/g, '\\"')}"`;
|
|
320
|
+
}
|
|
321
|
+
function extractAppParams(command, opts, options = {}) {
|
|
322
|
+
const params = {};
|
|
323
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
324
|
+
if (key === "input" && options.skipInput)
|
|
325
|
+
continue;
|
|
326
|
+
if (!INTERNAL_OPTS.has(key) && value !== undefined && value !== false) {
|
|
327
|
+
params[key] = value;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const rawArgs = command.args.slice(1);
|
|
331
|
+
for (let i = 0; i < rawArgs.length; i += 2) {
|
|
332
|
+
if (rawArgs[i]?.startsWith("--")) {
|
|
333
|
+
params[rawArgs[i].replace(/^--/, "")] = rawArgs[i + 1];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return params;
|
|
337
|
+
}
|
|
338
|
+
function shouldReadJsonInput(value) {
|
|
339
|
+
return value === "-" || /\.json$/i.test(value);
|
|
340
|
+
}
|
|
341
|
+
function optionalString(value) {
|
|
342
|
+
if (typeof value !== "string")
|
|
343
|
+
return undefined;
|
|
344
|
+
const trimmed = value.trim();
|
|
345
|
+
return trimmed ? trimmed : undefined;
|
|
346
|
+
}
|
|
347
|
+
function applyAssetEntries(target, entries) {
|
|
348
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
349
|
+
return;
|
|
350
|
+
const assets = entries.map(String).filter(Boolean);
|
|
351
|
+
if (assets.length === 0)
|
|
352
|
+
return;
|
|
353
|
+
target.assets = assets;
|
|
354
|
+
if (!target.image && assets.length === 1)
|
|
355
|
+
target.image = assets[0];
|
|
356
|
+
if (!target.images && assets.length > 1)
|
|
357
|
+
target.images = assets;
|
|
358
|
+
}
|
|
359
|
+
function toPublicAppPayload(app) {
|
|
360
|
+
return {
|
|
361
|
+
...app,
|
|
362
|
+
...capabilityMetadata("app", app.id),
|
|
363
|
+
canonical_command: `gd-cli workflows run ${app.id}`,
|
|
364
|
+
schema_command: `gd-cli workflows schema ${app.id} --format json`,
|
|
365
|
+
examples_command: `gd-cli workflows examples ${app.id} --format json`,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const WORKFLOWS_HELP = `
|
|
369
|
+
gd-cli workflows <command> [options]
|
|
370
|
+
|
|
371
|
+
用于运行完整业务场景,例如商品图库素材包、详情页/A+模块素材包、小红书种草图文包。
|
|
372
|
+
一个 Workflow 通常会编排多个 Tools,并输出一组可交付物料。
|
|
373
|
+
|
|
374
|
+
常用命令:
|
|
375
|
+
list/ls 列出内置 Workflows
|
|
376
|
+
info <workflow-id> 查看 Workflow 详情
|
|
377
|
+
run <workflow-id> 运行 Workflow,适合 Agent / 脚本
|
|
378
|
+
manifest 输出 Workflows 能力清单
|
|
379
|
+
schema <workflow-id> 查看 Workflow 输入/输出 Schema
|
|
380
|
+
examples <workflow-id> 查看 Workflow 调用示例
|
|
381
|
+
|
|
382
|
+
示例:
|
|
383
|
+
gd-cli workflows list
|
|
384
|
+
gd-cli workflows info listing-gallery
|
|
385
|
+
gd-cli workflows schema pdp-a-plus
|
|
386
|
+
gd-cli workflows run social-seeding-pack --input brief.json --format json
|
|
387
|
+
`;
|
|
388
|
+
const WORKFLOW_RUN_HELP = `
|
|
389
|
+
|
|
390
|
+
JSON 输入:
|
|
391
|
+
gd-cli workflows run listing-gallery --input brief.json --format json
|
|
392
|
+
cat brief.json | gd-cli workflows run social-seeding-pack - --format json
|
|
393
|
+
gd-cli workflows run campaign-kv-pack --set brief="城市跑鞋新品 Campaign" --set image=./sku.png --format json
|
|
394
|
+
|
|
395
|
+
说明:
|
|
396
|
+
--input <file> 传入 .json 时作为参数对象读取;其他文件类型会作为 Workflow 的 input 字段。
|
|
397
|
+
--dry-run 只校验输入和执行计划,不真正生成。
|
|
398
|
+
`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LoginOptions, LoginResult, AuthMode } from "./types.js";
|
|
2
|
+
export interface AuthProvider {
|
|
3
|
+
readonly mode: AuthMode;
|
|
4
|
+
login(options: LoginOptions): Promise<LoginResult>;
|
|
5
|
+
logout(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveAuthMode(): AuthMode;
|
|
8
|
+
export declare function createAuthProvider(): Promise<AuthProvider>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function resolveAuthMode() {
|
|
2
|
+
const m = (process.env.GD_AUTH_MODE ?? process.env.GDHUB_AUTH_MODE)?.toLowerCase();
|
|
3
|
+
if (!m || m === "device" || m === "oauth" || m === "device-flow" || m === "aksk") {
|
|
4
|
+
return "aksk";
|
|
5
|
+
}
|
|
6
|
+
throw new Error("仅支持 AK/SK 认证模式,请执行:gd-cli auth login");
|
|
7
|
+
}
|
|
8
|
+
export async function createAuthProvider() {
|
|
9
|
+
resolveAuthMode();
|
|
10
|
+
const { DeviceOAuthAuthProvider } = await import("./device-oauth-provider.js");
|
|
11
|
+
return new DeviceOAuthAuthProvider();
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AuthProvider } from "./auth-provider.js";
|
|
2
|
+
import type { LoginOptions, LoginResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* SSO OAuth 2.0 Device Authorization Flow,登录后保存 AK/SK。
|
|
5
|
+
*/
|
|
6
|
+
export declare class DeviceOAuthAuthProvider implements AuthProvider {
|
|
7
|
+
readonly mode: "aksk";
|
|
8
|
+
private sso;
|
|
9
|
+
login(options: LoginOptions): Promise<LoginResult>;
|
|
10
|
+
logout(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export { signBindOrg, signApiRequest, canonicalQuery } from "./signature.js";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
2
|
+
import { SsoClient } from "../../api/sso-client.js";
|
|
3
|
+
import { openBrowser } from "../../utils/open-browser.js";
|
|
4
|
+
import { createSpinner } from "../../utils/spinner.js";
|
|
5
|
+
import { GDHUB_CLI_CLIENT_ID, DEFAULT_SCOPE, } from "../../utils/config.js";
|
|
6
|
+
import { getIO } from "../../io.js";
|
|
7
|
+
import { emitAuthEvent, printAuthQrAndLink, } from "../../tty/auth-presenter.js";
|
|
8
|
+
import { OrgManager } from "./org-manager.js";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { resolveActiveEnv } from "../../utils/endpoints.js";
|
|
11
|
+
/**
|
|
12
|
+
* SSO OAuth 2.0 Device Authorization Flow,登录后保存 AK/SK。
|
|
13
|
+
*/
|
|
14
|
+
export class DeviceOAuthAuthProvider {
|
|
15
|
+
mode = "aksk";
|
|
16
|
+
sso = new SsoClient();
|
|
17
|
+
async login(options) {
|
|
18
|
+
const store = new CredentialStoreV2();
|
|
19
|
+
if (store.read() && !process.env.GD_AUTH_FORCE) {
|
|
20
|
+
throw Error(chalk.yellow("已存在授权凭证!如需重新授权,请先 gd-cli auth logout 再执行 gd-cli auth login。 \n"));
|
|
21
|
+
}
|
|
22
|
+
const authResp = await this.sso.deviceAuthorization();
|
|
23
|
+
const uri = authResp.verification_uri_complete;
|
|
24
|
+
const io = getIO();
|
|
25
|
+
if (io.isInteractiveAuth()) {
|
|
26
|
+
await printAuthQrAndLink(uri);
|
|
27
|
+
// console.log(` ${chalk.dim("verification_uri:")} ${authResp.verification_uri}`);
|
|
28
|
+
console.log(`授权码有效期约 ${authResp.expires_in}s,轮询间隔 ${authResp.interval}s`);
|
|
29
|
+
if (!options.noBrowser)
|
|
30
|
+
await openBrowser(uri).catch(() => undefined);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
emitAuthEvent({
|
|
34
|
+
type: "device_authorization",
|
|
35
|
+
verification_uri_complete: uri,
|
|
36
|
+
device_code: authResp.device_code,
|
|
37
|
+
expires_in: authResp.expires_in,
|
|
38
|
+
interval: authResp.interval,
|
|
39
|
+
user_action_required: "open_authorization_url",
|
|
40
|
+
agent_message_template: `请点击完成稿定授权:<${uri}>。我会保持授权命令运行,等你完成后继续。`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const spinner = io.stderrIsTTY ? createSpinner("等待浏览器授权…\n") : null;
|
|
44
|
+
spinner?.start();
|
|
45
|
+
const token = await this.sso.pollDeviceToken(authResp.device_code, authResp.interval, authResp.expires_in, () => {
|
|
46
|
+
if (!io.isInteractiveAuth()) {
|
|
47
|
+
emitAuthEvent({ type: "authorization_pending" });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
spinner?.succeed("SSO 授权成功,已获取权限");
|
|
51
|
+
let user;
|
|
52
|
+
let authorizedOrgId = cleanString(token.orgId);
|
|
53
|
+
try {
|
|
54
|
+
const identity = await this.sso.getIdentity(token.ak);
|
|
55
|
+
user = {
|
|
56
|
+
id: String(identity.userId),
|
|
57
|
+
name: String(identity.userId),
|
|
58
|
+
};
|
|
59
|
+
authorizedOrgId = cleanString(identity.orgId) ?? authorizedOrgId;
|
|
60
|
+
if (!io.isInteractiveAuth()) {
|
|
61
|
+
emitAuthEvent({
|
|
62
|
+
type: "authorization_success",
|
|
63
|
+
ak_preview: `${token.ak.slice(0, 8)}***`,
|
|
64
|
+
user_id: identity.userId,
|
|
65
|
+
bound_org_id: authorizedOrgId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
if (!io.isInteractiveAuth()) {
|
|
71
|
+
emitAuthEvent({ type: "authorization_success" });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const creds = {
|
|
75
|
+
version: 2,
|
|
76
|
+
provider: "gdhub",
|
|
77
|
+
client_id: GDHUB_CLI_CLIENT_ID,
|
|
78
|
+
ak: token.ak,
|
|
79
|
+
sk: token.sk,
|
|
80
|
+
scope: normalizeScope(token.scope),
|
|
81
|
+
expires_at: token.expires_at,
|
|
82
|
+
issued_for_env: resolveActiveEnv(),
|
|
83
|
+
user,
|
|
84
|
+
created_at: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
store.write(creds);
|
|
87
|
+
const orgMgr = new OrgManager(store);
|
|
88
|
+
let org;
|
|
89
|
+
if (options.orgId) {
|
|
90
|
+
org = await orgMgr.switchOrg(options.orgId);
|
|
91
|
+
}
|
|
92
|
+
else if (authorizedOrgId) {
|
|
93
|
+
org = await orgMgr.bindAuthorizedOrg(authorizedOrgId);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
emitAuthEvent({
|
|
97
|
+
type: "org_not_bound",
|
|
98
|
+
message: "授权成功,但授权结果未返回组织 ID。请执行 gd-cli org switch 绑定团队。",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (org) {
|
|
102
|
+
emitAuthEvent({ type: "bind_org_success", org });
|
|
103
|
+
}
|
|
104
|
+
return { credentials: store.read(), org };
|
|
105
|
+
}
|
|
106
|
+
async logout() {
|
|
107
|
+
new CredentialStoreV2().delete();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function cleanString(value) {
|
|
111
|
+
if (value === null || value === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
const s = String(value).trim();
|
|
114
|
+
return s ? s : undefined;
|
|
115
|
+
}
|
|
116
|
+
function normalizeScope(scope) {
|
|
117
|
+
if (!scope)
|
|
118
|
+
return [...DEFAULT_SCOPE];
|
|
119
|
+
if (Array.isArray(scope))
|
|
120
|
+
return scope;
|
|
121
|
+
return scope.split(",").map((s) => s.trim()).filter(Boolean);
|
|
122
|
+
}
|
|
123
|
+
// Backward-compatible signature exports used by older internal imports.
|
|
124
|
+
export { signBindOrg, signApiRequest, canonicalQuery } from "./signature.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SsoClient } from "../../api/sso-client.js";
|
|
2
|
+
import { HubClient } from "../../api/hub-client.js";
|
|
3
|
+
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
4
|
+
import type { OrgSummary } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* 组织列表与 bind-org。
|
|
7
|
+
* Device 模式:组织列表走 Hub 网关代理的 /structure/user/orgs;
|
|
8
|
+
* 网关不可用时退回本地 current_org 与 SSO identity。
|
|
9
|
+
*/
|
|
10
|
+
export declare class OrgManager {
|
|
11
|
+
private v2Store;
|
|
12
|
+
private sso;
|
|
13
|
+
private hub;
|
|
14
|
+
constructor(v2Store?: CredentialStoreV2, sso?: SsoClient, hub?: HubClient);
|
|
15
|
+
listOrgs(): Promise<OrgSummary[]>;
|
|
16
|
+
/** TTY 下选择结构接口返回的组织;接口不可用时允许手输 orgId。 */
|
|
17
|
+
switchOrgInteractive(): Promise<OrgSummary | undefined>;
|
|
18
|
+
switchOrg(orgId: string): Promise<OrgSummary>;
|
|
19
|
+
bindAuthorizedOrg(orgId: string): Promise<OrgSummary>;
|
|
20
|
+
getCurrentOrgId(): string | undefined;
|
|
21
|
+
getCurrentOrg(): OrgSummary | undefined;
|
|
22
|
+
private listOrgsWithSource;
|
|
23
|
+
private listStructureOrgs;
|
|
24
|
+
private listOrgsDevice;
|
|
25
|
+
private bindOrgDevice;
|
|
26
|
+
}
|