@gaoding/cli 0.0.0 → 1.0.0-alpha.25
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/LICENSE +21 -0
- package/README.md +40 -3
- package/contracts/operations/agent.send/input.schema.json +353 -0
- package/contracts/operations/agent.send/output.schema.json +271 -0
- package/contracts/operations/auth.status/input.schema.json +7 -0
- package/contracts/operations/auth.status/output.schema.json +132 -0
- package/contracts/operations/dam.copy/input.schema.json +19 -0
- package/contracts/operations/dam.copy/output.schema.json +19 -0
- package/contracts/operations/dam.delete/input.schema.json +23 -0
- package/contracts/operations/dam.delete/output.schema.json +6 -0
- package/contracts/operations/dam.download/input.schema.json +14 -0
- package/contracts/operations/dam.download/output.schema.json +25 -0
- package/contracts/operations/dam.folder.list/input.schema.json +15 -0
- package/contracts/operations/dam.folder.list/output.schema.json +37 -0
- package/contracts/operations/dam.get/input.schema.json +16 -0
- package/contracts/operations/dam.get/output.schema.json +46 -0
- package/contracts/operations/dam.list/input.schema.json +44 -0
- package/contracts/operations/dam.list/output.schema.json +71 -0
- package/contracts/operations/dam.move/input.schema.json +19 -0
- package/contracts/operations/dam.move/output.schema.json +18 -0
- package/contracts/operations/dam.recycle.list/input.schema.json +13 -0
- package/contracts/operations/dam.recycle.list/output.schema.json +34 -0
- package/contracts/operations/dam.recycle.restore/input.schema.json +14 -0
- package/contracts/operations/dam.recycle.restore/output.schema.json +6 -0
- package/contracts/operations/dam.rename/input.schema.json +18 -0
- package/contracts/operations/dam.rename/output.schema.json +18 -0
- package/contracts/operations/dam.repository.list/input.schema.json +8 -0
- package/contracts/operations/dam.repository.list/output.schema.json +52 -0
- package/contracts/operations/dam.search/input.schema.json +46 -0
- package/contracts/operations/dam.search/output.schema.json +71 -0
- package/contracts/operations/dam.tag.list/input.schema.json +12 -0
- package/contracts/operations/dam.tag.list/output.schema.json +26 -0
- package/contracts/operations/dam.upload/input.schema.json +31 -0
- package/contracts/operations/dam.upload/output.schema.json +46 -0
- package/contracts/operations/editor.apply/input.schema.json +134 -0
- package/contracts/operations/editor.apply/output.schema.json +15 -0
- package/contracts/operations/editor.connect/input.schema.json +15 -0
- package/contracts/operations/editor.connect/output.schema.json +17 -0
- package/contracts/operations/editor.disconnect/input.schema.json +8 -0
- package/contracts/operations/editor.disconnect/output.schema.json +14 -0
- package/contracts/operations/editor.save/input.schema.json +8 -0
- package/contracts/operations/editor.save/output.schema.json +23 -0
- package/contracts/operations/editor.screenshot/input.schema.json +8 -0
- package/contracts/operations/editor.screenshot/output.schema.json +16 -0
- package/contracts/operations/editor.snapshot/input.schema.json +8 -0
- package/contracts/operations/editor.snapshot/output.schema.json +113 -0
- package/contracts/operations/model.get/input.schema.json +18 -0
- package/contracts/operations/model.get/output.schema.json +191 -0
- package/contracts/operations/model.list/input.schema.json +15 -0
- package/contracts/operations/model.list/output.schema.json +111 -0
- package/contracts/operations/org.current/input.schema.json +7 -0
- package/contracts/operations/org.current/output.schema.json +49 -0
- package/contracts/operations/org.list/input.schema.json +7 -0
- package/contracts/operations/org.list/output.schema.json +71 -0
- package/contracts/operations/tool.call/input.schema.json +35 -0
- package/contracts/operations/tool.call/output.schema.json +129 -0
- package/contracts/operations/tool.list/input.schema.json +7 -0
- package/contracts/operations/tool.list/output.schema.json +45 -0
- package/dist/bin/gd-cli.js +60 -0
- package/dist/bin/postinstall.js +42 -0
- package/dist/src/bootstrap/create-cli.js +112 -0
- package/dist/src/bootstrap/create-runtime.js +290 -0
- package/dist/src/bootstrap/validators.js +157 -0
- package/dist/src/cli/action-binding.js +109 -0
- package/dist/src/cli/agent-commands.js +49 -0
- package/dist/src/cli/auth-commands.js +55 -0
- package/dist/src/cli/dam-commands.js +469 -0
- package/dist/src/cli/editor-commands.js +73 -0
- package/dist/src/cli/errors.js +157 -0
- package/dist/src/cli/model-commands.js +61 -0
- package/dist/src/cli/org-commands.js +58 -0
- package/dist/src/cli/presenter.js +105 -0
- package/dist/src/cli/prompt.js +30 -0
- package/dist/src/cli/tool-commands.js +77 -0
- package/dist/src/cli/update-command.js +14 -0
- package/dist/src/contracts/schema.js +4 -0
- package/dist/src/features/agent/creative-agent-adapter.js +36 -0
- package/dist/src/features/agent/creative-protocol.js +432 -0
- package/dist/src/features/agent/creative-stream.js +127 -0
- package/dist/src/features/agent/use-cases.js +150 -0
- package/dist/src/features/auth/access-policy.js +101 -0
- package/dist/src/features/auth/credential-store.js +62 -0
- package/dist/src/features/auth/sso-service.js +194 -0
- package/dist/src/features/auth/state.js +129 -0
- package/dist/src/features/auth/use-cases.js +136 -0
- package/dist/src/features/dam/asset-projection.js +278 -0
- package/dist/src/features/dam/dam-api-adapter.js +562 -0
- package/dist/src/features/dam/file-downloader.js +242 -0
- package/dist/src/features/dam/object-storage.js +193 -0
- package/dist/src/features/dam/operation-executor.js +72 -0
- package/dist/src/features/dam/registered-uploader.js +247 -0
- package/dist/src/features/dam/repository-catalog.js +127 -0
- package/dist/src/features/dam/storage-upload.js +141 -0
- package/dist/src/features/dam/transient-uploader.js +17 -0
- package/dist/src/features/dam/use-cases.js +470 -0
- package/dist/src/features/editor/bridge-client.js +109 -0
- package/dist/src/features/editor/bridge-process.js +222 -0
- package/dist/src/features/editor/bridge-server.js +337 -0
- package/dist/src/features/editor/protocol.js +1 -0
- package/dist/src/features/editor/public-url.js +36 -0
- package/dist/src/features/editor/session-state.js +78 -0
- package/dist/src/features/editor/session.js +207 -0
- package/dist/src/features/editor/use-cases.js +88 -0
- package/dist/src/features/org/org-service.js +76 -0
- package/dist/src/features/org/use-cases.js +156 -0
- package/dist/src/features/skill/bundled-skills.js +55 -0
- package/dist/src/features/skill/installer.js +271 -0
- package/dist/src/features/tool/catalog.js +83 -0
- package/dist/src/features/tool/dynamic-schema.js +128 -0
- package/dist/src/features/tool/mns-catalog-adapter.js +265 -0
- package/dist/src/features/tool/tool-api-adapter.js +352 -0
- package/dist/src/features/tool/use-cases.js +177 -0
- package/dist/src/features/update/latest-version.js +37 -0
- package/dist/src/features/update/update-notification.js +262 -0
- package/dist/src/features/update/update-service.js +183 -0
- package/dist/src/platform/json-input.js +67 -0
- package/dist/src/platform/local-json-file.js +59 -0
- package/dist/src/platform/open-browser.js +8 -0
- package/dist/src/platform/redact.js +80 -0
- package/dist/src/platform/remote-error-evidence.js +80 -0
- package/dist/src/platform/remote-protocol.js +10 -0
- package/dist/src/platform/safe-upload-file.js +146 -0
- package/dist/src/platform/signature.js +20 -0
- package/dist/src/platform/signed-http-transport.js +143 -0
- package/dist/src/platform/url-safety.js +100 -0
- package/dist/src/telemetry/invocation.js +156 -0
- package/dist/src/telemetry/sls-sink.js +31 -0
- package/package.json +54 -8
- package/skills/gd-cli/SKILL.md +15 -0
- package/skills/gd-cli/references/auth-org.md +9 -0
- package/skills/gd-cli/references/creation.md +115 -0
- package/skills/gd-cli/references/dam.md +52 -0
- package/skills/gd-cli/references/editor.md +254 -0
- package/skills/gd-cli/references/errors.md +13 -0
- package/skills/gd-cli/references/update.md +13 -0
- package/index.js +0 -3
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
4
|
+
import sharp from "sharp";
|
|
5
|
+
import { RemoteRequestError } from "../../platform/signed-http-transport.js";
|
|
6
|
+
import { projectDamAssetDetail } from "./asset-projection.js";
|
|
7
|
+
const imageFormats = new Set([
|
|
8
|
+
"png", "jpg", "jpeg", "gif", "svg", "webp", "jfif", "bmp", "tiff", "tif", "heic", "avif"
|
|
9
|
+
]);
|
|
10
|
+
const videoFormats = new Set([
|
|
11
|
+
"mp4", "avi", "flv", "mov", "3gp", "m3u8", "mpg", "asf", "wmv", "mkv", "ts", "webm", "mxf"
|
|
12
|
+
]);
|
|
13
|
+
const audioFormats = new Set(["mp3", "wav", "ogg", "flac"]);
|
|
14
|
+
const fontFormats = new Set(["ttf", "otf"]);
|
|
15
|
+
const designFormats = new Set(["psd", "ai", "sketch", "pdf"]);
|
|
16
|
+
const slideFormats = new Set(["ppt", "pptx"]);
|
|
17
|
+
function storageFormat(format) {
|
|
18
|
+
const normalized = format.toLowerCase();
|
|
19
|
+
if (imageFormats.has(normalized))
|
|
20
|
+
return "gdpic";
|
|
21
|
+
if (videoFormats.has(normalized))
|
|
22
|
+
return "gdvideo";
|
|
23
|
+
if (audioFormats.has(normalized))
|
|
24
|
+
return "gdaudio";
|
|
25
|
+
if (fontFormats.has(normalized))
|
|
26
|
+
return "gdfont";
|
|
27
|
+
if (designFormats.has(normalized))
|
|
28
|
+
return "gdimage";
|
|
29
|
+
if (slideFormats.has(normalized))
|
|
30
|
+
return "gdslide";
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
function publicType(format) {
|
|
34
|
+
if (format === "gdpic" || format === "gdimage")
|
|
35
|
+
return "image";
|
|
36
|
+
if (format === "gdvideo")
|
|
37
|
+
return "video";
|
|
38
|
+
if (format === "gdaudio")
|
|
39
|
+
return "audio";
|
|
40
|
+
if (format === "gdfont")
|
|
41
|
+
return "font";
|
|
42
|
+
if (format === "gdtemplate")
|
|
43
|
+
return "template";
|
|
44
|
+
return "file";
|
|
45
|
+
}
|
|
46
|
+
async function hashes(filePath, signal) {
|
|
47
|
+
signal.throwIfAborted();
|
|
48
|
+
const md5 = createHash("md5");
|
|
49
|
+
const sha1 = createHash("sha1");
|
|
50
|
+
const stream = createReadStream(filePath, { signal });
|
|
51
|
+
try {
|
|
52
|
+
for await (const chunk of stream) {
|
|
53
|
+
signal.throwIfAborted();
|
|
54
|
+
md5.update(chunk);
|
|
55
|
+
sha1.update(chunk);
|
|
56
|
+
}
|
|
57
|
+
signal.throwIfAborted();
|
|
58
|
+
return { md5: md5.digest("hex"), sha1: sha1.digest("hex") };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (signal.aborted)
|
|
62
|
+
throw signal.reason;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function filenameTitle(filename) {
|
|
67
|
+
const withoutExtension = filename.replace(/\.[^.]+$/u, "");
|
|
68
|
+
return withoutExtension === "" ? filename : withoutExtension;
|
|
69
|
+
}
|
|
70
|
+
function terminalAnalysisStatus(value) {
|
|
71
|
+
const normalized = typeof value === "string" ? Number(value) : value;
|
|
72
|
+
return normalized === 2 || normalized === 3 || normalized === 4;
|
|
73
|
+
}
|
|
74
|
+
async function defaultSleep(milliseconds, signal) {
|
|
75
|
+
signal.throwIfAborted();
|
|
76
|
+
try {
|
|
77
|
+
await delay(milliseconds, undefined, { signal });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (signal.aborted)
|
|
81
|
+
throw signal.reason;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function createDamRegisteredUploader(dependencies) {
|
|
86
|
+
const now = dependencies.now ?? (() => new Date());
|
|
87
|
+
const sleep = dependencies.sleep ?? defaultSleep;
|
|
88
|
+
const imageMetadata = dependencies.imageMetadata
|
|
89
|
+
?? (async (filePath) => {
|
|
90
|
+
const metadata = await sharp(filePath).metadata();
|
|
91
|
+
return { width: metadata.width, height: metadata.height };
|
|
92
|
+
});
|
|
93
|
+
async function localMetadata(file, signal) {
|
|
94
|
+
if (!file.mediaType.startsWith("image/"))
|
|
95
|
+
return {};
|
|
96
|
+
try {
|
|
97
|
+
const metadata = await imageMetadata(file.filePath);
|
|
98
|
+
signal.throwIfAborted();
|
|
99
|
+
return {
|
|
100
|
+
...(typeof metadata.width === "number" && Number.isFinite(metadata.width)
|
|
101
|
+
? { width: metadata.width }
|
|
102
|
+
: {}),
|
|
103
|
+
...(typeof metadata.height === "number" && Number.isFinite(metadata.height)
|
|
104
|
+
? { height: metadata.height }
|
|
105
|
+
: {})
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
if (signal.aborted)
|
|
110
|
+
throw signal.reason;
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
async upload(input) {
|
|
116
|
+
try {
|
|
117
|
+
const stage = (name, run) => (dependencies.telemetry.stage(name, async () => {
|
|
118
|
+
try {
|
|
119
|
+
return await run();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (input.signal.aborted)
|
|
123
|
+
throw input.signal.reason;
|
|
124
|
+
if (error instanceof RemoteRequestError)
|
|
125
|
+
throw error;
|
|
126
|
+
throw new RemoteRequestError();
|
|
127
|
+
}
|
|
128
|
+
}));
|
|
129
|
+
input.signal.throwIfAborted();
|
|
130
|
+
const prepared = await stage("prepare", async () => {
|
|
131
|
+
const [digest, metadata] = await Promise.all([
|
|
132
|
+
hashes(input.file.filePath, input.signal),
|
|
133
|
+
localMetadata(input.file, input.signal)
|
|
134
|
+
]);
|
|
135
|
+
input.signal.throwIfAborted();
|
|
136
|
+
const contentId = await dependencies.api.createAssetId({
|
|
137
|
+
access: input.access,
|
|
138
|
+
signal: input.signal
|
|
139
|
+
});
|
|
140
|
+
const format = input.file.format.toLowerCase();
|
|
141
|
+
return {
|
|
142
|
+
digest,
|
|
143
|
+
metadata,
|
|
144
|
+
contentId,
|
|
145
|
+
format,
|
|
146
|
+
damFormat: storageFormat(format),
|
|
147
|
+
title: input.input.title ?? filenameTitle(input.file.filename),
|
|
148
|
+
folderId: input.input.folderId ?? "0"
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
const { digest, metadata, contentId, format, damFormat, title, folderId } = prepared;
|
|
152
|
+
const stored = await stage("upload", () => dependencies.storage.upload({
|
|
153
|
+
file: input.file,
|
|
154
|
+
contentId
|
|
155
|
+
}, input.access, input.signal));
|
|
156
|
+
const preview = input.file.mediaType.startsWith("image/")
|
|
157
|
+
? {
|
|
158
|
+
url: stored.url,
|
|
159
|
+
...metadata,
|
|
160
|
+
format
|
|
161
|
+
}
|
|
162
|
+
: {};
|
|
163
|
+
const body = {
|
|
164
|
+
folder_id: folderId,
|
|
165
|
+
repository_id: input.repository.id,
|
|
166
|
+
second_transmission: false,
|
|
167
|
+
storage_format: damFormat,
|
|
168
|
+
category_ids: [],
|
|
169
|
+
asset_info: {
|
|
170
|
+
id: contentId,
|
|
171
|
+
origin_url: stored.url,
|
|
172
|
+
size: input.file.size,
|
|
173
|
+
title,
|
|
174
|
+
md5: digest.md5,
|
|
175
|
+
sha1: digest.sha1,
|
|
176
|
+
mime_type: input.file.mediaType,
|
|
177
|
+
format,
|
|
178
|
+
preview
|
|
179
|
+
},
|
|
180
|
+
...(input.input.tagIds === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { tag_ids: [...input.input.tagIds] })
|
|
183
|
+
};
|
|
184
|
+
const fallback = projectDamAssetDetail({
|
|
185
|
+
id: contentId,
|
|
186
|
+
title,
|
|
187
|
+
format,
|
|
188
|
+
type: publicType(damFormat),
|
|
189
|
+
origin_info: {
|
|
190
|
+
url: stored.url,
|
|
191
|
+
size: input.file.size,
|
|
192
|
+
format
|
|
193
|
+
},
|
|
194
|
+
preview_info: preview,
|
|
195
|
+
repository_info: {
|
|
196
|
+
id: input.repository.id,
|
|
197
|
+
...(input.repository.name === undefined ? {} : { name: input.repository.name })
|
|
198
|
+
},
|
|
199
|
+
folder_info: { id: folderId },
|
|
200
|
+
...(input.input.tagIds === undefined
|
|
201
|
+
? {}
|
|
202
|
+
: { tag_ids: [...input.input.tagIds] })
|
|
203
|
+
});
|
|
204
|
+
if (fallback === undefined)
|
|
205
|
+
throw new RemoteRequestError();
|
|
206
|
+
let latest = await stage("persist", () => dependencies.api.createMaterial({
|
|
207
|
+
body,
|
|
208
|
+
fallback,
|
|
209
|
+
access: input.access,
|
|
210
|
+
signal: input.signal
|
|
211
|
+
}));
|
|
212
|
+
if (input.input.waitAnalysis !== true || terminalAnalysisStatus(latest.analysis_status)) {
|
|
213
|
+
return latest;
|
|
214
|
+
}
|
|
215
|
+
return stage("wait", async () => {
|
|
216
|
+
const deadline = now().getTime() + 180_000;
|
|
217
|
+
while (true) {
|
|
218
|
+
input.signal.throwIfAborted();
|
|
219
|
+
const current = await dependencies.api.getBatchStatus({
|
|
220
|
+
assetId: latest.asset_id,
|
|
221
|
+
fallback: latest,
|
|
222
|
+
access: input.access,
|
|
223
|
+
signal: input.signal
|
|
224
|
+
});
|
|
225
|
+
if (current !== null)
|
|
226
|
+
latest = current;
|
|
227
|
+
if (terminalAnalysisStatus(latest.analysis_status))
|
|
228
|
+
return latest;
|
|
229
|
+
const remaining = deadline - now().getTime();
|
|
230
|
+
if (remaining <= 0) {
|
|
231
|
+
dependencies.warn("素材分析仍在处理中,已返回当前结果。");
|
|
232
|
+
return latest;
|
|
233
|
+
}
|
|
234
|
+
await sleep(Math.min(3000, remaining), input.signal);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
if (input.signal.aborted)
|
|
240
|
+
throw input.signal.reason;
|
|
241
|
+
if (error instanceof RemoteRequestError)
|
|
242
|
+
throw error;
|
|
243
|
+
throw new RemoteRequestError();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { RemoteProtocolError } from "../../platform/remote-protocol.js";
|
|
2
|
+
const permissions = new Set([
|
|
3
|
+
"OWNER",
|
|
4
|
+
"MANAGE",
|
|
5
|
+
"EDIT",
|
|
6
|
+
"USE",
|
|
7
|
+
"READ"
|
|
8
|
+
]);
|
|
9
|
+
function record(value) {
|
|
10
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
11
|
+
? value
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
function rows(value) {
|
|
15
|
+
if (Array.isArray(value))
|
|
16
|
+
return value;
|
|
17
|
+
const nested = record(value)?.data;
|
|
18
|
+
if (Array.isArray(nested))
|
|
19
|
+
return nested;
|
|
20
|
+
throw new RemoteProtocolError();
|
|
21
|
+
}
|
|
22
|
+
function nonblank(value) {
|
|
23
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
24
|
+
}
|
|
25
|
+
function permission(value) {
|
|
26
|
+
return typeof value === "string"
|
|
27
|
+
&& permissions.has(value)
|
|
28
|
+
? value
|
|
29
|
+
: undefined;
|
|
30
|
+
}
|
|
31
|
+
function privateRepositories(value) {
|
|
32
|
+
return rows(value).flatMap((candidate) => {
|
|
33
|
+
const item = record(candidate);
|
|
34
|
+
const repositoryId = nonblank(item?.repository_id);
|
|
35
|
+
const name = nonblank(item?.name) ?? nonblank(item?.title);
|
|
36
|
+
const type = item?.type === 1
|
|
37
|
+
? "personal"
|
|
38
|
+
: item?.type === 6
|
|
39
|
+
? "draft"
|
|
40
|
+
: undefined;
|
|
41
|
+
if (repositoryId === undefined || name === undefined || type === undefined)
|
|
42
|
+
return [];
|
|
43
|
+
return [{ repository_id: repositoryId, name, type, is_default: false }];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function teamRepositories(value) {
|
|
47
|
+
return rows(value).flatMap((candidate) => {
|
|
48
|
+
const item = record(candidate);
|
|
49
|
+
const repositoryId = nonblank(item?.repository_id);
|
|
50
|
+
const name = nonblank(item?.name) ?? nonblank(item?.title);
|
|
51
|
+
if (repositoryId === undefined || name === undefined)
|
|
52
|
+
return [];
|
|
53
|
+
const permissionCode = permission(item?.permission_code);
|
|
54
|
+
return [{
|
|
55
|
+
repository_id: repositoryId,
|
|
56
|
+
name,
|
|
57
|
+
type: "team",
|
|
58
|
+
...(permissionCode === undefined ? {} : { permission_code: permissionCode }),
|
|
59
|
+
is_default: false
|
|
60
|
+
}];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function enterpriseRepositories(value) {
|
|
64
|
+
return rows(value).flatMap((candidate) => {
|
|
65
|
+
const item = record(candidate);
|
|
66
|
+
const repositoryId = nonblank(item?.id);
|
|
67
|
+
const name = nonblank(item?.name) ?? nonblank(item?.title);
|
|
68
|
+
if (repositoryId === undefined || name === undefined)
|
|
69
|
+
return [];
|
|
70
|
+
const permissionCode = permission(item?.permission_code);
|
|
71
|
+
return [{
|
|
72
|
+
repository_id: repositoryId,
|
|
73
|
+
name,
|
|
74
|
+
type: "enterprise",
|
|
75
|
+
...(permissionCode === undefined ? {} : { permission_code: permissionCode }),
|
|
76
|
+
is_default: false
|
|
77
|
+
}];
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export function normalizeRepositoryCatalog(privateRows, teamRows, enterpriseRows) {
|
|
81
|
+
const merged = [
|
|
82
|
+
...privateRepositories(privateRows),
|
|
83
|
+
...teamRepositories(teamRows),
|
|
84
|
+
...enterpriseRepositories(enterpriseRows)
|
|
85
|
+
];
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
const unique = merged.filter((repository) => {
|
|
88
|
+
if (seen.has(repository.repository_id))
|
|
89
|
+
return false;
|
|
90
|
+
seen.add(repository.repository_id);
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
const defaultIndex = unique.findIndex((repository) => repository.type === "personal");
|
|
94
|
+
const selectedIndex = defaultIndex === -1 && unique.length > 0 ? 0 : defaultIndex;
|
|
95
|
+
return unique.map((repository, index) => ({
|
|
96
|
+
...repository,
|
|
97
|
+
is_default: index === selectedIndex
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
function requestContext(input) {
|
|
101
|
+
return {
|
|
102
|
+
credential: input.access.credential,
|
|
103
|
+
organizationId: input.access.organizationId,
|
|
104
|
+
signal: input.signal
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function createDamRepositoryCatalog(dependencies) {
|
|
108
|
+
return {
|
|
109
|
+
async list(input) {
|
|
110
|
+
const [privateRows, teamRows, enterpriseRows] = await Promise.all([
|
|
111
|
+
dependencies.transport.getJson({
|
|
112
|
+
path: "/tb-dam/repositories",
|
|
113
|
+
...requestContext(input)
|
|
114
|
+
}),
|
|
115
|
+
dependencies.transport.getJson({
|
|
116
|
+
path: "/tb-dam/teams",
|
|
117
|
+
...requestContext(input)
|
|
118
|
+
}),
|
|
119
|
+
dependencies.transport.getJson({
|
|
120
|
+
path: "/dam/org-repositories",
|
|
121
|
+
...requestContext(input)
|
|
122
|
+
})
|
|
123
|
+
]);
|
|
124
|
+
return normalizeRepositoryCatalog(privateRows, teamRows, enterpriseRows);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { RemoteRequestError } from "../../platform/signed-http-transport.js";
|
|
2
|
+
import { inspectUploadFile } from "../../platform/safe-upload-file.js";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function requiredString(record, key) {
|
|
7
|
+
const value = record[key];
|
|
8
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
9
|
+
throw new RemoteRequestError();
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function optionalString(record, key) {
|
|
13
|
+
const value = record[key];
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
17
|
+
throw new RemoteRequestError();
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function parseToken(response) {
|
|
21
|
+
if (!isRecord(response))
|
|
22
|
+
throw new RemoteRequestError();
|
|
23
|
+
const record = "data" in response ? response.data : response;
|
|
24
|
+
if (!isRecord(record))
|
|
25
|
+
throw new RemoteRequestError();
|
|
26
|
+
const standbyEndpoint = optionalString(record, "standby_endpoint");
|
|
27
|
+
const accelerateEndpoint = optionalString(record, "accelerate_endpoint");
|
|
28
|
+
const authKey = optionalString(record, "auth_key");
|
|
29
|
+
return {
|
|
30
|
+
securityToken: requiredString(record, "security_token"),
|
|
31
|
+
accessKeyId: requiredString(record, "access_key_id"),
|
|
32
|
+
accessKeySecret: requiredString(record, "access_key_secret"),
|
|
33
|
+
endpoint: requiredString(record, "endpoint"),
|
|
34
|
+
...(standbyEndpoint === undefined ? {} : { standbyEndpoint }),
|
|
35
|
+
...(accelerateEndpoint === undefined ? {} : { accelerateEndpoint }),
|
|
36
|
+
region: requiredString(record, "region"),
|
|
37
|
+
bucketName: requiredString(record, "bucket_name"),
|
|
38
|
+
objectName: requiredString(record, "object_name"),
|
|
39
|
+
host: requiredString(record, "host"),
|
|
40
|
+
...(authKey === undefined ? {} : { authKey })
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function publicAssetUrl(token) {
|
|
44
|
+
try {
|
|
45
|
+
const normalizedHost = token.host.includes("://") ? token.host : `https://${token.host}`;
|
|
46
|
+
const url = new URL(normalizedHost);
|
|
47
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:")
|
|
48
|
+
|| url.username !== ""
|
|
49
|
+
|| url.password !== "") {
|
|
50
|
+
throw new RemoteRequestError();
|
|
51
|
+
}
|
|
52
|
+
url.search = "";
|
|
53
|
+
url.hash = "";
|
|
54
|
+
if (!url.pathname.endsWith("/"))
|
|
55
|
+
url.pathname += "/";
|
|
56
|
+
url.pathname += token.objectName
|
|
57
|
+
.replace(/^\/+/, "")
|
|
58
|
+
.split("/")
|
|
59
|
+
.map(encodeURIComponent)
|
|
60
|
+
.join("/");
|
|
61
|
+
if (token.authKey !== undefined)
|
|
62
|
+
url.searchParams.set("auth_key", token.authKey);
|
|
63
|
+
return url.href;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof RemoteRequestError)
|
|
67
|
+
throw error;
|
|
68
|
+
throw new RemoteRequestError();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function storageToken(token) {
|
|
72
|
+
return {
|
|
73
|
+
securityToken: token.securityToken,
|
|
74
|
+
accessKeyId: token.accessKeyId,
|
|
75
|
+
accessKeySecret: token.accessKeySecret,
|
|
76
|
+
endpoint: token.endpoint,
|
|
77
|
+
...(token.standbyEndpoint === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { standbyEndpoint: token.standbyEndpoint }),
|
|
80
|
+
...(token.accelerateEndpoint === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { accelerateEndpoint: token.accelerateEndpoint }),
|
|
83
|
+
region: token.region,
|
|
84
|
+
bucketName: token.bucketName,
|
|
85
|
+
objectName: token.objectName
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function createDamStorageUploader(dependencies) {
|
|
89
|
+
return {
|
|
90
|
+
inspect(input, signal) {
|
|
91
|
+
return inspectUploadFile({ ...input, signal });
|
|
92
|
+
},
|
|
93
|
+
async upload(input, access, signal) {
|
|
94
|
+
signal.throwIfAborted();
|
|
95
|
+
let token;
|
|
96
|
+
let url;
|
|
97
|
+
try {
|
|
98
|
+
const response = await dependencies.transport.postJson({
|
|
99
|
+
path: "/tb-dam/asset/upload/tokens",
|
|
100
|
+
body: {
|
|
101
|
+
...(input.contentId === undefined ? {} : { content_id: input.contentId }),
|
|
102
|
+
format: input.file.format
|
|
103
|
+
},
|
|
104
|
+
credential: access.credential,
|
|
105
|
+
organizationId: access.organizationId,
|
|
106
|
+
signal
|
|
107
|
+
});
|
|
108
|
+
signal.throwIfAborted();
|
|
109
|
+
token = parseToken(response);
|
|
110
|
+
url = publicAssetUrl(token);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (signal.aborted)
|
|
114
|
+
throw signal.reason;
|
|
115
|
+
if (error instanceof RemoteRequestError)
|
|
116
|
+
throw error;
|
|
117
|
+
throw new RemoteRequestError();
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await dependencies.storage.upload({
|
|
121
|
+
token: storageToken(token),
|
|
122
|
+
filePath: input.file.filePath,
|
|
123
|
+
mediaType: input.file.mediaType,
|
|
124
|
+
signal
|
|
125
|
+
});
|
|
126
|
+
signal.throwIfAborted();
|
|
127
|
+
return {
|
|
128
|
+
...input.file,
|
|
129
|
+
url
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (signal.aborted)
|
|
134
|
+
throw signal.reason;
|
|
135
|
+
if (error instanceof RemoteRequestError)
|
|
136
|
+
throw error;
|
|
137
|
+
throw new RemoteRequestError();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createDamTransientUploader(dependencies) {
|
|
2
|
+
return {
|
|
3
|
+
async upload(input, access, signal) {
|
|
4
|
+
const file = await dependencies.storageUpload.inspect({
|
|
5
|
+
url: input.url,
|
|
6
|
+
...(input.mediaType === undefined ? {} : { claimedMediaType: input.mediaType })
|
|
7
|
+
}, signal);
|
|
8
|
+
const stored = await dependencies.storageUpload.upload({ file }, access, signal);
|
|
9
|
+
return {
|
|
10
|
+
url: stored.url,
|
|
11
|
+
mediaType: stored.mediaType,
|
|
12
|
+
filename: input.filename ?? stored.filename,
|
|
13
|
+
fromUserUpload: true
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|