gaoding-cli 1.0.0-alpha.7 → 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 +38 -0
- package/README.md +47 -60
- 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 +11 -44
- package/dist/src/api/creative-client.d.ts +34 -0
- package/dist/src/api/creative-client.js +184 -0
- package/dist/src/api/dam-client.d.ts +0 -7
- package/dist/src/api/dam-client.js +0 -17
- package/dist/src/api/direct-client.d.ts +9 -2
- package/dist/src/api/direct-client.js +45 -18
- package/dist/src/api/hub-client.d.ts +11 -8
- package/dist/src/api/hub-client.js +17 -18
- package/dist/src/api/usage-client.d.ts +4 -4
- package/dist/src/api/usage-client.js +9 -49
- package/dist/src/auth/credential-store-v2.d.ts +0 -1
- package/dist/src/auth/credential-store-v2.js +0 -6
- package/dist/src/commands/auth.js +17 -53
- package/dist/src/commands/creative.d.ts +8 -0
- package/dist/src/commands/creative.js +178 -0
- package/dist/src/commands/dam.js +270 -127
- package/dist/src/commands/org.js +31 -47
- package/dist/src/commands/skills.js +33 -118
- package/dist/src/commands/update.js +11 -82
- package/dist/src/commands/usage.d.ts +1 -1
- package/dist/src/commands/usage.js +10 -21
- package/dist/src/commands/workflows.d.ts +1 -1
- package/dist/src/commands/workflows.js +299 -197
- package/dist/src/core/auth/auth-guard.d.ts +21 -0
- package/dist/src/core/auth/auth-guard.js +121 -0
- package/dist/src/core/auth/org-manager.d.ts +2 -1
- package/dist/src/core/auth/org-manager.js +61 -6
- 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 +1060 -0
- package/dist/src/creative/contract.js +722 -0
- package/dist/src/creative/media.d.ts +19 -0
- package/dist/src/creative/media.js +83 -0
- package/dist/src/creative/protocol.d.ts +60 -0
- package/dist/src/creative/protocol.js +337 -0
- package/dist/src/io.d.ts +0 -2
- package/dist/src/io.js +0 -8
- package/dist/src/middleware/auth.d.ts +6 -2
- package/dist/src/middleware/auth.js +25 -63
- package/dist/src/middleware/error-handler.d.ts +8 -0
- package/dist/src/middleware/error-handler.js +61 -57
- package/dist/src/program.d.ts +1 -0
- package/dist/src/program.js +137 -122
- package/dist/src/registry/agent-contracts.d.ts +14 -34
- package/dist/src/registry/agent-contracts.js +209 -297
- package/dist/src/registry/direct-registry.d.ts +0 -5
- package/dist/src/registry/direct-registry.js +0 -19
- package/dist/src/registry/registry-client.d.ts +0 -21
- package/dist/src/registry/registry-client.js +1 -40
- 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/help-verify.d.ts +1 -3
- package/dist/src/utils/help-verify.js +1 -25
- package/dist/src/utils/sensitive-path.js +1 -4
- package/package.json +4 -3
- package/skills/gd-cli/SKILL.md +13 -12
- package/skills/gd-cli/references/auth.md +9 -10
- package/skills/gd-cli/references/creative.md +47 -0
- package/skills/gd-cli/references/entitlement.md +3 -3
- package/skills/gd-cli/references/errors.md +10 -3
- package/skills/gd-cli/references/org.md +3 -3
- package/skills/gd-cli/references/sop.md +8 -8
- package/skills/gd-cli/references/update.md +4 -3
- package/skills/gd-cli/references/workflows.md +7 -6
- package/dist/src/commands/models.d.ts +0 -2
- package/dist/src/commands/models.js +0 -78
- package/dist/src/commands/tools.d.ts +0 -4
- package/dist/src/commands/tools.js +0 -383
- package/dist/src/utils/list-output-format.d.ts +0 -10
- package/dist/src/utils/list-output-format.js +0 -34
- package/skills/gd-cli/references/tools.md +0 -29
package/dist/src/commands/dam.js
CHANGED
|
@@ -1,35 +1,31 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { basename, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { setAuthRequirement } from "../middleware/auth.js";
|
|
4
4
|
import { withErrorHandler } from "../middleware/error-handler.js";
|
|
5
5
|
import { DamClient } from "../api/dam-client.js";
|
|
6
6
|
import { formatOutput } from "../middleware/output.js";
|
|
7
|
-
import { resolveInvokeOutputFormat, resolveListOutputFormat, } from "../utils/list-output-format.js";
|
|
8
7
|
import { createSpinner } from "../utils/spinner.js";
|
|
9
|
-
import { emitJson, wrapEnvelope
|
|
10
|
-
import {
|
|
8
|
+
import { emitJson, wrapEnvelope } from "../core/output/envelope.js";
|
|
9
|
+
import { CliUsageError } from "../core/output/cli-error.js";
|
|
10
|
+
import { isSensitiveKey } from "../utils/redact.js";
|
|
11
|
+
import { assertSafeHttpUrl, isBlockedHost } from "../utils/url-safety.js";
|
|
12
|
+
const DAM_ASSET_TYPES = new Set(["image", "video", "audio", "font", "template", "file"]);
|
|
11
13
|
export function registerDamCommand(program) {
|
|
12
14
|
const dam = program
|
|
13
15
|
.command("dam")
|
|
14
16
|
.usage("<command> [options]")
|
|
15
|
-
.description("DAM 素材管理")
|
|
16
|
-
|
|
17
|
-
setStaticHelp(dam, DAM_HELP);
|
|
18
|
-
dam
|
|
17
|
+
.description("DAM 素材管理");
|
|
18
|
+
const upload = dam
|
|
19
19
|
.command("upload <file>")
|
|
20
20
|
.description("上传本地文件并提交 DAM 入库,返回 DAM asset_id 与 CDN URL")
|
|
21
21
|
.option("--repository-id <id>", "DAM 资源库 ID,默认取个人库或 GD_DAM_REPOSITORY_ID")
|
|
22
22
|
.option("--name <name>", "指定素材名称")
|
|
23
23
|
.option("--folder-id <id>", "DAM 文件夹 ID,MVP 默认 0", "0")
|
|
24
|
-
.option("--folder <name>", "文件夹名称")
|
|
25
|
-
.option("--tags <list>", "标签,逗号分隔")
|
|
26
24
|
.option("--tag-ids <list>", "标签 ID,逗号分隔")
|
|
27
|
-
.option("--storage-only", "只上传对象存储,不提交 DAM 入库", false)
|
|
28
25
|
.option("--allow-sensitive-path", "确认上传默认受保护的本地路径", false)
|
|
29
26
|
.option("--wait-analysis", "等待 DAM 后端分析完成并返回封面", false)
|
|
30
|
-
.option("--
|
|
31
|
-
.
|
|
32
|
-
.action(withErrorHandler(withAuth(async (file, options, command) => {
|
|
27
|
+
.option("--json", "输出机器可读 JSON")
|
|
28
|
+
.action(withErrorHandler(async (file, options, command) => {
|
|
33
29
|
const opts = options;
|
|
34
30
|
const cmd = command;
|
|
35
31
|
const p = resolve(process.cwd(), String(file));
|
|
@@ -41,74 +37,54 @@ export function registerDamCommand(program) {
|
|
|
41
37
|
spinner.start();
|
|
42
38
|
const damClient = new DamClient();
|
|
43
39
|
const uploaded = await damClient.uploadLocalAsset(p, {
|
|
44
|
-
register:
|
|
40
|
+
register: true,
|
|
45
41
|
allowSensitivePath: opts.allowSensitivePath === true,
|
|
46
42
|
repositoryId: optionalString(opts.repositoryId),
|
|
47
43
|
folderId: optionalString(opts.folderId) ?? "0",
|
|
48
44
|
title: optionalString(opts.name),
|
|
49
45
|
tagIds: splitList(opts.tagIds),
|
|
50
|
-
waitAnalysis: Boolean(opts.waitAnalysis)
|
|
51
|
-
analysisTimeoutMs: numberOption(opts.analysisTimeout),
|
|
52
|
-
analysisIntervalMs: numberOption(opts.analysisInterval),
|
|
46
|
+
waitAnalysis: Boolean(opts.waitAnalysis),
|
|
53
47
|
});
|
|
54
48
|
if (process.stderr.isTTY)
|
|
55
49
|
spinner.succeed("上传完成");
|
|
56
|
-
const asset = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
type: uploaded.mime_type,
|
|
50
|
+
const asset = projectPublicDamAsset({
|
|
51
|
+
...uploaded,
|
|
52
|
+
asset_id: uploaded.id,
|
|
60
53
|
filename: basename(p),
|
|
61
|
-
|
|
62
|
-
cover_url: uploaded.cover_url,
|
|
63
|
-
};
|
|
54
|
+
});
|
|
64
55
|
const payload = {
|
|
65
56
|
status: "completed",
|
|
66
57
|
asset,
|
|
67
58
|
outputs: {
|
|
68
|
-
asset_id:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
url: uploaded.url,
|
|
74
|
-
cover_url: uploaded.cover_url,
|
|
75
|
-
cover: uploaded.cover,
|
|
76
|
-
asset_info: uploaded.asset_info,
|
|
77
|
-
analysis_status: uploaded.analysis_status,
|
|
78
|
-
analysis_fail_msg: uploaded.analysis_fail_msg,
|
|
79
|
-
analysis_timed_out: uploaded.analysis_timed_out,
|
|
80
|
-
filename: uploaded.filename,
|
|
81
|
-
format: uploaded.format,
|
|
82
|
-
mime_type: uploaded.mime_type,
|
|
83
|
-
object_name: uploaded.object_name,
|
|
84
|
-
auth_key: uploaded.auth_key,
|
|
85
|
-
registration: uploaded.registration,
|
|
59
|
+
asset_id: asset.asset_id,
|
|
60
|
+
url: asset.url,
|
|
61
|
+
cover_url: asset.cover_url,
|
|
62
|
+
filename: asset.filename,
|
|
63
|
+
type: asset.mime_type ?? asset.type,
|
|
86
64
|
},
|
|
87
65
|
};
|
|
88
|
-
|
|
89
|
-
if (shouldEmitEnvelope(format)) {
|
|
66
|
+
if (isJson(cmd)) {
|
|
90
67
|
emitJson(wrapEnvelope(payload, { command: "dam upload" }));
|
|
91
68
|
}
|
|
92
69
|
else {
|
|
93
|
-
formatOutput(payload,
|
|
70
|
+
formatOutput(payload, "human");
|
|
94
71
|
}
|
|
95
|
-
}))
|
|
96
|
-
|
|
72
|
+
}));
|
|
73
|
+
setAuthRequirement(upload, "current-org");
|
|
74
|
+
const list = dam
|
|
97
75
|
.command("list")
|
|
98
76
|
.description("查看最近上传素材")
|
|
99
77
|
.option("--repository-id <id>", "DAM 资源库 ID,默认取个人库或 GD_DAM_REPOSITORY_ID")
|
|
100
78
|
.option("--all-repositories", "搜索当前组织下所有资源库", false)
|
|
101
79
|
.option("--folder-id <id>", "限定文件夹")
|
|
102
|
-
.option("--include-child-folder", "包含子文件夹", true)
|
|
103
80
|
.option("--no-include-child-folder", "不包含子文件夹")
|
|
104
81
|
.option("--type <type>", "image/video/audio/font/template/file")
|
|
105
|
-
.option("--format <list>", "素材格式,逗号分隔,例如 png,jpg,mp4")
|
|
82
|
+
.option("--asset-format <list>", "素材格式,逗号分隔,例如 png,jpg,mp4")
|
|
106
83
|
.option("--tag-ids <list>", "标签 ID,逗号分隔")
|
|
107
84
|
.option("--page-size <n>", "每页数量,最大 100", parsePositiveInteger, 20)
|
|
108
|
-
.option("--page <n>", "页码", parsePositiveInteger, 1)
|
|
109
85
|
.option("--cursor <id>", "分页 cursor/query_id")
|
|
110
|
-
.option("--
|
|
111
|
-
.action(withErrorHandler(
|
|
86
|
+
.option("--json", "输出机器可读 JSON")
|
|
87
|
+
.action(withErrorHandler(async (options, command) => {
|
|
112
88
|
const opts = options;
|
|
113
89
|
const cmd = command;
|
|
114
90
|
const damClient = new DamClient();
|
|
@@ -116,53 +92,57 @@ export function registerDamCommand(program) {
|
|
|
116
92
|
...buildDamSearchOptions(opts),
|
|
117
93
|
allowEmptyKeyword: true,
|
|
118
94
|
});
|
|
119
|
-
outputDamSearch(data,
|
|
120
|
-
}))
|
|
121
|
-
|
|
95
|
+
outputDamSearch(data, isJson(cmd), "list");
|
|
96
|
+
}));
|
|
97
|
+
setAuthRequirement(list, "current-org");
|
|
98
|
+
const search = dam
|
|
122
99
|
.command("search <keyword>")
|
|
123
100
|
.description("搜索素材")
|
|
124
101
|
.option("--repository-id <id>", "DAM 资源库 ID,默认取个人库或 GD_DAM_REPOSITORY_ID")
|
|
125
102
|
.option("--all-repositories", "搜索当前组织下所有资源库", false)
|
|
126
103
|
.option("--folder-id <id>", "限定文件夹")
|
|
127
|
-
.option("--include-child-folder", "包含子文件夹", true)
|
|
128
104
|
.option("--no-include-child-folder", "不包含子文件夹")
|
|
129
105
|
.option("--type <type>", "image/video/audio/font/template/file")
|
|
130
|
-
.option("--format <list>", "素材格式,逗号分隔,例如 png,jpg,mp4")
|
|
106
|
+
.option("--asset-format <list>", "素材格式,逗号分隔,例如 png,jpg,mp4")
|
|
131
107
|
.option("--tag-ids <list>", "标签 ID,逗号分隔")
|
|
132
108
|
.option("--page-size <n>", "每页数量,最大 100", parsePositiveInteger, 20)
|
|
133
|
-
.option("--page <n>", "页码", parsePositiveInteger, 1)
|
|
134
109
|
.option("--cursor <id>", "分页 cursor/query_id")
|
|
135
|
-
.option("--
|
|
136
|
-
.action(withErrorHandler(
|
|
110
|
+
.option("--json", "输出机器可读 JSON")
|
|
111
|
+
.action(withErrorHandler(async (keyword, options, command) => {
|
|
137
112
|
const opts = options;
|
|
138
113
|
const cmd = command;
|
|
139
114
|
const damClient = new DamClient();
|
|
140
115
|
const data = await damClient.searchAssets(String(keyword), buildDamSearchOptions(opts));
|
|
141
|
-
outputDamSearch(data,
|
|
142
|
-
}))
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
.
|
|
146
|
-
.
|
|
116
|
+
outputDamSearch(data, isJson(cmd), "search");
|
|
117
|
+
}));
|
|
118
|
+
setAuthRequirement(search, "current-org");
|
|
119
|
+
const get = dam
|
|
120
|
+
.command("get <asset-id>")
|
|
121
|
+
.description("按 asset_id 查询素材")
|
|
122
|
+
.option("--json", "输出机器可读 JSON")
|
|
123
|
+
.action(withErrorHandler(async (assetId, _opts, command) => {
|
|
147
124
|
const cmd = command;
|
|
148
|
-
const key =
|
|
125
|
+
const key = requireAssetId(assetId);
|
|
149
126
|
const damClient = new DamClient();
|
|
150
127
|
const data = await damClient.getAsset(key);
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
128
|
+
const payload = {
|
|
129
|
+
status: "completed",
|
|
130
|
+
asset: projectPublicDamAsset(data, key),
|
|
131
|
+
};
|
|
132
|
+
if (isJson(cmd)) {
|
|
133
|
+
emitJson(wrapEnvelope(payload, { command: "dam get" }));
|
|
154
134
|
}
|
|
155
135
|
else {
|
|
156
|
-
formatOutput(
|
|
136
|
+
formatOutput(payload, "human");
|
|
157
137
|
}
|
|
158
|
-
}))
|
|
159
|
-
|
|
138
|
+
}));
|
|
139
|
+
setAuthRequirement(get, "current-org");
|
|
140
|
+
const deleteCommand = dam
|
|
160
141
|
.command("delete <asset-id>")
|
|
161
142
|
.description("删除 DAM 素材")
|
|
162
143
|
.option("--repository-id <id>", "DAM 资源库 ID,默认取个人库或 GD_DAM_REPOSITORY_ID")
|
|
163
144
|
.option("--permanent", "进入回收站后继续永久删除", false)
|
|
164
|
-
.action(withErrorHandler(
|
|
165
|
-
const cmd = command;
|
|
145
|
+
.action(withErrorHandler(async (assetId, options) => {
|
|
166
146
|
const opts = options;
|
|
167
147
|
const id = String(assetId);
|
|
168
148
|
const damClient = new DamClient();
|
|
@@ -170,52 +150,55 @@ export function registerDamCommand(program) {
|
|
|
170
150
|
repositoryId: optionalString(opts.repositoryId),
|
|
171
151
|
permanent: Boolean(opts.permanent),
|
|
172
152
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
formatOutput(data, format);
|
|
179
|
-
}
|
|
180
|
-
})));
|
|
153
|
+
formatOutput(data, "human");
|
|
154
|
+
}));
|
|
155
|
+
setAuthRequirement(deleteCommand, "current-org");
|
|
181
156
|
}
|
|
182
157
|
function buildDamSearchOptions(opts) {
|
|
158
|
+
const repositoryId = optionalString(opts.repositoryId);
|
|
159
|
+
const allRepositories = Boolean(opts.allRepositories);
|
|
160
|
+
if (repositoryId && allRepositories) {
|
|
161
|
+
invalidDamOption("--repository-id 与 --all-repositories 不能同时使用");
|
|
162
|
+
}
|
|
163
|
+
const type = optionalString(opts.type)?.toLowerCase();
|
|
164
|
+
if (type && !DAM_ASSET_TYPES.has(type)) {
|
|
165
|
+
invalidDamOption("--type 必须是 image、video、audio、font、template 或 file");
|
|
166
|
+
}
|
|
183
167
|
return {
|
|
184
|
-
repositoryId
|
|
185
|
-
allRepositories
|
|
168
|
+
repositoryId,
|
|
169
|
+
allRepositories,
|
|
186
170
|
folderId: optionalString(opts.folderId),
|
|
187
171
|
includeChildFolder: opts.includeChildFolder !== false,
|
|
188
|
-
type
|
|
189
|
-
format: splitList(opts.
|
|
172
|
+
type,
|
|
173
|
+
format: splitList(opts.assetFormat),
|
|
190
174
|
tagIds: splitList(opts.tagIds),
|
|
191
175
|
pageSize: numberOption(opts.pageSize),
|
|
192
|
-
page: numberOption(opts.page),
|
|
193
176
|
cursor: optionalString(opts.cursor),
|
|
194
|
-
raw: Boolean(opts.raw),
|
|
195
177
|
};
|
|
196
178
|
}
|
|
197
|
-
function outputDamSearch(data,
|
|
198
|
-
|
|
199
|
-
|
|
179
|
+
function outputDamSearch(data, json, mode) {
|
|
180
|
+
const payload = projectPublicDamSearchResult(data);
|
|
181
|
+
if (json) {
|
|
182
|
+
emitJson(wrapEnvelope(payload, { command: `dam ${mode}` }));
|
|
200
183
|
return;
|
|
201
184
|
}
|
|
202
|
-
const repo =
|
|
185
|
+
const repo = payload.repository_ids.join(", ") || "default";
|
|
203
186
|
if (mode === "list") {
|
|
204
|
-
console.log(`Found ${
|
|
187
|
+
console.log(`Found ${payload.total} recent assets in repo ${repo}`);
|
|
205
188
|
}
|
|
206
189
|
else {
|
|
207
|
-
console.log(`Found ${
|
|
190
|
+
console.log(`Found ${payload.total} assets for "${payload.query}" in repo ${repo}`);
|
|
208
191
|
}
|
|
209
192
|
console.log("");
|
|
210
|
-
formatOutput(rowsForDamSearch(
|
|
211
|
-
if (
|
|
193
|
+
formatOutput(rowsForDamSearch(payload), "table");
|
|
194
|
+
if (payload.next_cursor) {
|
|
212
195
|
const base = mode === "list"
|
|
213
196
|
? "gd-cli dam list"
|
|
214
|
-
: `gd-cli dam search ${quoteShell(
|
|
215
|
-
console.log(`\nNext:\n ${base} --cursor ${
|
|
197
|
+
: `gd-cli dam search ${quoteShell(payload.query)}`;
|
|
198
|
+
console.log(`\nNext:\n ${base} --cursor ${payload.next_cursor}`);
|
|
216
199
|
}
|
|
217
|
-
if (
|
|
218
|
-
console.log(`\nUse:\n gd-cli dam get ${
|
|
200
|
+
if (payload.assets.length === 1) {
|
|
201
|
+
console.log(`\nUse:\n gd-cli dam get ${payload.assets[0].asset_id}`);
|
|
219
202
|
}
|
|
220
203
|
}
|
|
221
204
|
function rowsForDamSearch(data) {
|
|
@@ -252,6 +235,191 @@ function parsePositiveInteger(value) {
|
|
|
252
235
|
function numberOption(value) {
|
|
253
236
|
return typeof value === "number" ? value : undefined;
|
|
254
237
|
}
|
|
238
|
+
function requireAssetId(value) {
|
|
239
|
+
const assetId = optionalString(value);
|
|
240
|
+
if (!assetId)
|
|
241
|
+
invalidDamOption("asset_id 不能为空");
|
|
242
|
+
if (/^https?:\/\//i.test(assetId))
|
|
243
|
+
invalidDamOption("asset_id 不能是 URL");
|
|
244
|
+
return assetId;
|
|
245
|
+
}
|
|
246
|
+
function invalidDamOption(message) {
|
|
247
|
+
throw new CliUsageError({
|
|
248
|
+
code: "PARAM_INVALID",
|
|
249
|
+
type: "validation",
|
|
250
|
+
message,
|
|
251
|
+
retryable: false,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function isJson(command) {
|
|
255
|
+
return command.opts().json === true;
|
|
256
|
+
}
|
|
257
|
+
function projectPublicDamSearchResult(data) {
|
|
258
|
+
return {
|
|
259
|
+
status: "completed",
|
|
260
|
+
query: data.query,
|
|
261
|
+
repository_ids: [...data.repository_ids],
|
|
262
|
+
page: data.page,
|
|
263
|
+
page_size: data.page_size,
|
|
264
|
+
total: data.total,
|
|
265
|
+
finish: data.finish,
|
|
266
|
+
...(data.next_cursor ? { next_cursor: data.next_cursor } : {}),
|
|
267
|
+
assets: data.assets.map((asset) => projectPublicDamAsset(asset)),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function projectPublicDamAsset(value, fallbackAssetId = "") {
|
|
271
|
+
const item = publicDamAssetRecord(value);
|
|
272
|
+
const origin = recordValue(item.origin_info) ?? recordValue(item.originInfo);
|
|
273
|
+
const preview = recordValue(item.preview) ??
|
|
274
|
+
recordValue(item.preview_info) ??
|
|
275
|
+
recordValue(item.previewInfo) ??
|
|
276
|
+
recordValue(item.cover);
|
|
277
|
+
const folder = recordValue(item.folder_info) ?? recordValue(item.folderInfo);
|
|
278
|
+
const format = stringValue(origin?.format) ?? stringValue(item.format);
|
|
279
|
+
const mimeType = stringValue(item.mime_type) ?? stringValue(item.mimeType);
|
|
280
|
+
const url = stringValue(origin?.url) ?? stringValue(item.origin_url) ?? stringValue(item.url);
|
|
281
|
+
const coverUrl = stringValue(preview?.url) ??
|
|
282
|
+
stringValue(item.cover_url) ??
|
|
283
|
+
stringValue(item.preview_url);
|
|
284
|
+
const assetId = stringValue(item.asset_id) ??
|
|
285
|
+
stringValue(item.id) ??
|
|
286
|
+
stringValue(item.content_id) ??
|
|
287
|
+
fallbackAssetId;
|
|
288
|
+
const tags = publicTags(item.tags);
|
|
289
|
+
const tagIds = publicTagIds(item.tag_ids, item.tags);
|
|
290
|
+
return compactRecord({
|
|
291
|
+
asset_id: assetId,
|
|
292
|
+
title: stringValue(item.title) ?? stringValue(item.name),
|
|
293
|
+
filename: stringValue(item.filename),
|
|
294
|
+
format,
|
|
295
|
+
mime_type: mimeType,
|
|
296
|
+
type: publicDamAssetType(item, format, mimeType),
|
|
297
|
+
size: numberValue(origin?.size) ?? numberValue(item.size),
|
|
298
|
+
url: url ? publicAssetUrl(url) : undefined,
|
|
299
|
+
cover_url: coverUrl ? publicAssetUrl(coverUrl) : undefined,
|
|
300
|
+
width: numberValue(preview?.width) ?? numberValue(item.width),
|
|
301
|
+
height: numberValue(preview?.height) ?? numberValue(item.height),
|
|
302
|
+
repository_id: stringValue(item.repository_id),
|
|
303
|
+
folder_id: stringValue(item.folder_id) ?? stringValue(folder?.id),
|
|
304
|
+
folder_name: stringValue(folder?.title) ?? stringValue(folder?.name),
|
|
305
|
+
tags,
|
|
306
|
+
tag_ids: tagIds,
|
|
307
|
+
analysis_status: numberValue(item.analysis_result) ??
|
|
308
|
+
numberValue(item.analysis_status) ??
|
|
309
|
+
stringValue(item.analysis_result) ??
|
|
310
|
+
stringValue(item.analysis_status),
|
|
311
|
+
created_at: stringValue(item.created_at),
|
|
312
|
+
updated_at: stringValue(item.updated_at),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function publicDamAssetRecord(value) {
|
|
316
|
+
if (!recordValue(value))
|
|
317
|
+
return {};
|
|
318
|
+
const queue = [value];
|
|
319
|
+
const seen = new Set();
|
|
320
|
+
while (queue.length > 0) {
|
|
321
|
+
const candidate = queue.shift();
|
|
322
|
+
const record = recordValue(candidate);
|
|
323
|
+
if (!record || seen.has(record))
|
|
324
|
+
continue;
|
|
325
|
+
seen.add(record);
|
|
326
|
+
if (isDamAssetRecord(record))
|
|
327
|
+
return record;
|
|
328
|
+
for (const key of ["data", "asset", "asset_info"]) {
|
|
329
|
+
if (record[key] !== undefined)
|
|
330
|
+
queue.push(record[key]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return {};
|
|
334
|
+
}
|
|
335
|
+
function isDamAssetRecord(value) {
|
|
336
|
+
return [
|
|
337
|
+
"asset_id",
|
|
338
|
+
"id",
|
|
339
|
+
"content_id",
|
|
340
|
+
"origin_info",
|
|
341
|
+
"originInfo",
|
|
342
|
+
"origin_url",
|
|
343
|
+
"url",
|
|
344
|
+
].some((key) => value[key] !== undefined);
|
|
345
|
+
}
|
|
346
|
+
function publicDamAssetType(item, format, mimeType) {
|
|
347
|
+
const explicit = stringValue(item.type)?.toLowerCase();
|
|
348
|
+
if (explicit && DAM_ASSET_TYPES.has(explicit))
|
|
349
|
+
return explicit;
|
|
350
|
+
const contentFormat = stringValue(item.content_format)?.toLowerCase() ??
|
|
351
|
+
stringValue(item.storage_format)?.toLowerCase();
|
|
352
|
+
if (contentFormat === "gdpic" || contentFormat === "gdimage")
|
|
353
|
+
return "image";
|
|
354
|
+
if (contentFormat === "gdvideo")
|
|
355
|
+
return "video";
|
|
356
|
+
if (contentFormat === "gdaudio")
|
|
357
|
+
return "audio";
|
|
358
|
+
if (contentFormat === "gdfont")
|
|
359
|
+
return "font";
|
|
360
|
+
if (contentFormat === "gdtemplate")
|
|
361
|
+
return "template";
|
|
362
|
+
const mimeCategory = mimeType?.split("/", 1)[0]?.toLowerCase();
|
|
363
|
+
if (mimeCategory && DAM_ASSET_TYPES.has(mimeCategory))
|
|
364
|
+
return mimeCategory;
|
|
365
|
+
const normalizedFormat = format?.toLowerCase();
|
|
366
|
+
if (normalizedFormat && ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(normalizedFormat)) {
|
|
367
|
+
return "image";
|
|
368
|
+
}
|
|
369
|
+
if (normalizedFormat && ["mp4", "mov", "webm", "mkv"].includes(normalizedFormat)) {
|
|
370
|
+
return "video";
|
|
371
|
+
}
|
|
372
|
+
if (normalizedFormat && ["mp3", "wav", "m4a", "aac"].includes(normalizedFormat)) {
|
|
373
|
+
return "audio";
|
|
374
|
+
}
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
function recordValue(value) {
|
|
378
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
379
|
+
? value
|
|
380
|
+
: undefined;
|
|
381
|
+
}
|
|
382
|
+
function stringValue(value) {
|
|
383
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
384
|
+
}
|
|
385
|
+
function numberValue(value) {
|
|
386
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
387
|
+
}
|
|
388
|
+
function publicTags(value) {
|
|
389
|
+
if (!Array.isArray(value))
|
|
390
|
+
return undefined;
|
|
391
|
+
const tags = value
|
|
392
|
+
.map((item) => typeof item === "string" ? item : stringValue(recordValue(item)?.name))
|
|
393
|
+
.filter((item) => Boolean(item));
|
|
394
|
+
return tags.length > 0 ? tags : undefined;
|
|
395
|
+
}
|
|
396
|
+
function publicTagIds(tagIds, tags) {
|
|
397
|
+
const values = Array.isArray(tagIds)
|
|
398
|
+
? tagIds
|
|
399
|
+
: Array.isArray(tags)
|
|
400
|
+
? tags.map((item) => recordValue(item)?.id)
|
|
401
|
+
: [];
|
|
402
|
+
const ids = values.filter((item) => typeof item === "string" || typeof item === "number");
|
|
403
|
+
return ids.length > 0 ? ids : undefined;
|
|
404
|
+
}
|
|
405
|
+
function compactRecord(value) {
|
|
406
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
407
|
+
}
|
|
408
|
+
function publicAssetUrl(value) {
|
|
409
|
+
try {
|
|
410
|
+
const url = new URL(assertSafeHttpUrl(value, { context: "DAM 素材 URL" }));
|
|
411
|
+
if (isBlockedHost(url.hostname))
|
|
412
|
+
return undefined;
|
|
413
|
+
for (const key of [...url.searchParams.keys()]) {
|
|
414
|
+
if (isSensitiveKey(key))
|
|
415
|
+
url.searchParams.delete(key);
|
|
416
|
+
}
|
|
417
|
+
return url.toString();
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return undefined;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
255
423
|
function formatBytes(value) {
|
|
256
424
|
if (!Number.isFinite(value))
|
|
257
425
|
return "";
|
|
@@ -266,28 +434,3 @@ function formatBytes(value) {
|
|
|
266
434
|
function quoteShell(value) {
|
|
267
435
|
return /^[\w./:=,-]+$/.test(value) ? value : `"${value.replace(/"/g, '\\"')}"`;
|
|
268
436
|
}
|
|
269
|
-
const DAM_HELP = `Usage: gd-cli dam <command> [options]
|
|
270
|
-
|
|
271
|
-
用于把本地文件上传到稿定素材库,获得可复用的 asset_id 和 CDN URL。
|
|
272
|
-
适合在 Workflows / Tools / Agent 工作流中反复引用同一批商品图、Logo、参考图和交付文件。
|
|
273
|
-
|
|
274
|
-
常用命令:
|
|
275
|
-
upload <file> 上传文件并入库,返回 asset_id 与 CDN URL
|
|
276
|
-
get <id-or-url> 按 asset_id、CDN URL 或文件名查询素材
|
|
277
|
-
list 查看最近上传素材
|
|
278
|
-
search <keyword> 搜索素材
|
|
279
|
-
delete <asset-id> 删除素材
|
|
280
|
-
|
|
281
|
-
常用选项:
|
|
282
|
-
--name <name> 指定素材名称
|
|
283
|
-
--folder <folder> 指定 DAM 文件夹
|
|
284
|
-
--tags <tags> 添加标签,逗号分隔
|
|
285
|
-
--format <list> 在 dam search/list 中表示素材格式过滤,例如 png,jpg
|
|
286
|
-
--json 输出机器可读 JSON
|
|
287
|
-
|
|
288
|
-
示例:
|
|
289
|
-
gd-cli dam upload ./sku-001.png --tags product,shoe
|
|
290
|
-
gd-cli dam get ast_123
|
|
291
|
-
gd-cli dam search "white sneaker" --type image --format png,jpg
|
|
292
|
-
gd-cli dam delete ast_123 --yes
|
|
293
|
-
`;
|
package/dist/src/commands/org.js
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { withErrorHandler } from "../middleware/error-handler.js";
|
|
3
|
-
import {
|
|
3
|
+
import { setAuthRequirement } from "../middleware/auth.js";
|
|
4
4
|
import { OrgManager } from "../core/auth/org-manager.js";
|
|
5
5
|
import { formatOutput } from "../middleware/output.js";
|
|
6
|
-
import { resolveInvokeOutputFormat, resolveListOutputFormat, } from "../utils/list-output-format.js";
|
|
7
6
|
import { getIO } from "../io.js";
|
|
8
7
|
import { emitJson, wrapEnvelope } from "../core/output/envelope.js";
|
|
9
|
-
import {
|
|
8
|
+
import { CliUsageError } from "../core/output/cli-error.js";
|
|
9
|
+
import { registerUsageCommand } from "./usage.js";
|
|
10
10
|
export function registerOrgCommand(program) {
|
|
11
11
|
const org = program
|
|
12
12
|
.command("org")
|
|
13
13
|
.usage("<command> [options]")
|
|
14
|
-
.description("
|
|
15
|
-
|
|
16
|
-
setStaticHelp(org, ORG_HELP);
|
|
17
|
-
org
|
|
14
|
+
.description("查看与切换当前组织");
|
|
15
|
+
const list = org
|
|
18
16
|
.command("list")
|
|
19
|
-
.
|
|
20
|
-
.
|
|
21
|
-
.action(withErrorHandler(
|
|
17
|
+
.description("列出可用组织")
|
|
18
|
+
.option("--json", "输出机器可读 JSON")
|
|
19
|
+
.action(withErrorHandler(async (_opts, command) => {
|
|
22
20
|
const cmd = command;
|
|
23
21
|
const manager = new OrgManager();
|
|
24
22
|
const orgs = await manager.listOrgs();
|
|
25
23
|
const current = manager.getCurrentOrgId();
|
|
26
|
-
const
|
|
24
|
+
const json = cmd.opts().json === true;
|
|
27
25
|
const rows = orgs.map((o) => ({
|
|
28
26
|
current: o.id === current ? "yes" : "",
|
|
29
27
|
name: o.name,
|
|
@@ -33,20 +31,17 @@ export function registerOrgCommand(program) {
|
|
|
33
31
|
status: o.status ?? "",
|
|
34
32
|
expires: formatExpireDate(o.expire_date),
|
|
35
33
|
}));
|
|
36
|
-
if (
|
|
34
|
+
if (json) {
|
|
37
35
|
emitJson(wrapEnvelope({ orgs, current_org_id: current }, { command: "org list" }));
|
|
38
36
|
return;
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
formatOutput(rows, format === "table" ? "table" : "text");
|
|
45
|
-
}
|
|
46
|
-
})));
|
|
47
|
-
org
|
|
38
|
+
formatOutput(rows, "table");
|
|
39
|
+
}));
|
|
40
|
+
setAuthRequirement(list, "credential");
|
|
41
|
+
const current = org
|
|
48
42
|
.command("current")
|
|
49
43
|
.description("快速查询当前组织")
|
|
44
|
+
.option("--json", "输出机器可读 JSON")
|
|
50
45
|
.action(withErrorHandler(async (_opts, command) => {
|
|
51
46
|
const cmd = command;
|
|
52
47
|
const manager = new OrgManager();
|
|
@@ -57,25 +52,23 @@ export function registerOrgCommand(program) {
|
|
|
57
52
|
current_org: current ?? null,
|
|
58
53
|
next_command: current ? undefined : "gd-cli org list",
|
|
59
54
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
formatOutput(payload, "json");
|
|
55
|
+
if (cmd.opts().json === true) {
|
|
56
|
+
emitJson(wrapEnvelope(payload, { command: "org current" }));
|
|
63
57
|
return;
|
|
64
58
|
}
|
|
65
59
|
if (!current) {
|
|
66
|
-
console.log(chalk.yellow("
|
|
60
|
+
console.log(chalk.yellow("当前未选择组织"));
|
|
67
61
|
console.log(" gd-cli org list");
|
|
68
62
|
return;
|
|
69
63
|
}
|
|
70
64
|
console.log(`当前组织: ${current.name} (${current.id})`);
|
|
71
65
|
}));
|
|
72
|
-
|
|
66
|
+
setAuthRequirement(current, "none");
|
|
67
|
+
const switchCommand = org
|
|
73
68
|
.command("switch")
|
|
74
69
|
.description("切换当前 CLI 组织(TTY 下可交互选择)")
|
|
75
70
|
.option("--org <org-id>", "组织 ID")
|
|
76
|
-
.action(withErrorHandler(
|
|
77
|
-
const cmd = command;
|
|
78
|
-
const format = resolveListOutputFormat(cmd);
|
|
71
|
+
.action(withErrorHandler(async (options) => {
|
|
79
72
|
const manager = new OrgManager();
|
|
80
73
|
let orgId = options.org;
|
|
81
74
|
if (!orgId) {
|
|
@@ -97,31 +90,22 @@ export function registerOrgCommand(program) {
|
|
|
97
90
|
});
|
|
98
91
|
}
|
|
99
92
|
else {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
throw new CliUsageError({
|
|
94
|
+
code: "ORG_SELECTION_REQUIRED",
|
|
95
|
+
type: "validation",
|
|
96
|
+
message: "非交互环境请通过 --org <org-id> 指定要切换的组织。",
|
|
97
|
+
hint: "先执行 gd-cli org list 查看可用组织。",
|
|
98
|
+
retryable: false,
|
|
99
|
+
});
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
const result = await manager.switchOrg(orgId);
|
|
107
|
-
if (getIO().prefersJsonOutput(format)) {
|
|
108
|
-
emitJson(wrapEnvelope({ org: result }, { command: "org switch" }));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
103
|
console.log(chalk.green(`\n✓ 已切换到组织: ${result.name} (${result.id})`));
|
|
112
104
|
console.log(chalk.yellow("\n请 60s 后再调用能力,即可按新组织的权益执行~\n"));
|
|
113
|
-
}))
|
|
105
|
+
}));
|
|
106
|
+
setAuthRequirement(switchCommand, "credential");
|
|
107
|
+
registerUsageCommand(org);
|
|
114
108
|
}
|
|
115
|
-
const ORG_HELP = `
|
|
116
|
-
Usage: gd-cli org <command> [options]
|
|
117
|
-
|
|
118
|
-
用于查看和切换当前组织
|
|
119
|
-
|
|
120
|
-
常用命令:
|
|
121
|
-
list/ls 列出可绑定的组织
|
|
122
|
-
current 查询当前绑定的组织
|
|
123
|
-
switch 切换绑定的组织
|
|
124
|
-
`;
|
|
125
109
|
function formatOrgChoice(org, currentOrgId) {
|
|
126
110
|
const product = org.product_name ? ` · ${org.product_name}` : "";
|
|
127
111
|
const current = org.id === currentOrgId ? " ← 当前" : "";
|