@lingjingai/lj-awb-cli-pre 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +335 -0
  2. package/build/_shared.mjs +130 -0
  3. package/build/build.mjs +50 -0
  4. package/build/pre-publish.mjs +57 -0
  5. package/build/pre.mjs +42 -0
  6. package/build/prod.mjs +52 -0
  7. package/install.mjs +53 -0
  8. package/package.json +44 -0
  9. package/packages/awb-cli/README.md +19 -0
  10. package/packages/awb-cli/bin/lj-awb +19 -0
  11. package/packages/awb-cli/bin/lj-awb.js +11 -0
  12. package/packages/awb-cli/package.json +18 -0
  13. package/packages/awb-core/README.md +12 -0
  14. package/packages/awb-core/package.json +21 -0
  15. package/packages/awb-core/src/api.js +349 -0
  16. package/packages/awb-core/src/artifact.js +936 -0
  17. package/packages/awb-core/src/auth.js +80 -0
  18. package/packages/awb-core/src/commands.js +1321 -0
  19. package/packages/awb-core/src/common.js +508 -0
  20. package/packages/awb-core/src/output.js +1189 -0
  21. package/packages/awb-core/src/services.js +3811 -0
  22. package/packages/awb-core/src/standalone.js +1213 -0
  23. package/skills/lj-awb/SKILL.md +160 -0
  24. package/skills/lj-awb/VERSION +1 -0
  25. package/skills/lj-awb/compat.json +6 -0
  26. package/skills/lj-awb/modules/account.md +30 -0
  27. package/skills/lj-awb/modules/artifact/asset.md +64 -0
  28. package/skills/lj-awb/modules/artifact/clip.md +65 -0
  29. package/skills/lj-awb/modules/artifact/script.md +37 -0
  30. package/skills/lj-awb/modules/artifact/video.md +65 -0
  31. package/skills/lj-awb/modules/artifact.md +65 -0
  32. package/skills/lj-awb/modules/asset.md +53 -0
  33. package/skills/lj-awb/modules/auth.md +30 -0
  34. package/skills/lj-awb/modules/create-contract.md +118 -0
  35. package/skills/lj-awb/modules/credits.md +28 -0
  36. package/skills/lj-awb/modules/evals.md +186 -0
  37. package/skills/lj-awb/modules/image.md +75 -0
  38. package/skills/lj-awb/modules/model.md +110 -0
  39. package/skills/lj-awb/modules/project.md +30 -0
  40. package/skills/lj-awb/modules/subject.md +32 -0
  41. package/skills/lj-awb/modules/task-manual.md +185 -0
  42. package/skills/lj-awb/modules/task.md +62 -0
  43. package/skills/lj-awb/modules/upload.md +33 -0
  44. package/skills/lj-awb/modules/video.md +102 -0
  45. package/skills/lj-awb/modules/workflows.md +482 -0
  46. package/skills/lj-awb/references/error-codes.md +102 -0
  47. package/skills/lj-awb/references/model-options-read.md +49 -0
  48. package/skills/lj-awb/references/output-fields.md +113 -0
  49. package/skills/lj-awb/scripts/resolve-lj-awb-cmd.sh +10 -0
package/install.mjs ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { basename, dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const log = (message) => process.stderr.write(`${message}\n`);
8
+
9
+ function copyDirectory(sourceDir, targetDir) {
10
+ mkdirSync(targetDir, { recursive: true });
11
+ for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
12
+ if (entry.name === '.DS_Store') continue;
13
+ const sourcePath = join(sourceDir, entry.name);
14
+ const targetPath = join(targetDir, entry.name);
15
+ if (entry.isDirectory()) {
16
+ copyDirectory(sourcePath, targetPath);
17
+ } else {
18
+ cpSync(sourcePath, targetPath);
19
+ }
20
+ }
21
+ }
22
+
23
+ const packageDir = dirname(fileURLToPath(import.meta.url));
24
+ const skillSourceDir = join(packageDir, 'skills', 'lj-awb');
25
+ const explicitSkillTargetDir = process.env.LINGJING_AWB_SKILL_INSTALL_DIR || process.env.ANIME_SKILL_INSTALL_DIR;
26
+ const codexHomeDir = process.env.CODEX_HOME || join(homedir(), '.codex');
27
+
28
+ function normalizeSkillTargetDir(targetDir) {
29
+ return basename(targetDir) === 'skills' ? join(targetDir, 'lj-awb') : targetDir;
30
+ }
31
+
32
+ const skillTargetDirs = explicitSkillTargetDir
33
+ ? [normalizeSkillTargetDir(explicitSkillTargetDir)]
34
+ : [
35
+ join(homedir(), '.cc-switch', 'skills', 'lj-awb'),
36
+ join(codexHomeDir, 'skills', 'lj-awb'),
37
+ ];
38
+
39
+ if (process.env.LINGJING_AWB_SKIP_SKILL_INSTALL === '1' || process.env.ANIME_SKIP_SKILL_INSTALL === '1') {
40
+ log('Skipped lj-awb skill install because LINGJING_AWB_SKIP_SKILL_INSTALL=1.');
41
+ process.exit(0);
42
+ }
43
+
44
+ if (!existsSync(skillSourceDir)) {
45
+ log(`No lj-awb skill bundle found at ${skillSourceDir}.`);
46
+ process.exit(0);
47
+ }
48
+
49
+ for (const skillTargetDir of [...new Set(skillTargetDirs)]) {
50
+ rmSync(skillTargetDir, { recursive: true, force: true });
51
+ copyDirectory(skillSourceDir, skillTargetDir);
52
+ log(`Installed lj-awb skill -> ${skillTargetDir}`);
53
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lingjingai/lj-awb-cli-pre",
3
+ "version": "0.3.15",
4
+ "description": "Lingjing AWB CLI monorepo with shared core, standalone CLI, and agent skills (pre-release build pointing to https://animeworkbench-pre.lingjingai.cn)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "lj-awb": "packages/awb-cli/bin/lj-awb"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "workspaces": [
14
+ "packages/*"
15
+ ],
16
+ "scripts": {
17
+ "build": "node build/build.mjs",
18
+ "build:pre": "node build/pre.mjs",
19
+ "build:pre-publish": "node build/pre-publish.mjs",
20
+ "build:prod": "node build/prod.mjs",
21
+ "check": "npm run check:local && npm run check:real",
22
+ "check:local": "node --check install.mjs && node --check packages/awb-core/src/common.js && node --check packages/awb-core/src/api.js && node --check packages/awb-core/src/artifact.js && node --check packages/awb-core/src/auth.js && node --check packages/awb-core/src/output.js && node --check packages/awb-core/src/services.js && node --check packages/awb-core/src/commands.js && node --check packages/awb-core/src/standalone.js && sh -n packages/awb-cli/bin/lj-awb && node --check packages/awb-cli/bin/lj-awb.js && node --check build/build.mjs && node --check build/_shared.mjs && node --check build/pre.mjs && node --check build/pre-publish.mjs && node --check build/prod.mjs && node --check scripts/run-openapi-cli-examples-real.mjs && node --check scripts/validate-cli-schema.mjs && node --check scripts/validate-cli-output-contract.mjs && node --check scripts/validate-cli-command-coverage.mjs && node scripts/validate-skill-meta.mjs && node scripts/validate-cli-schema.mjs && node scripts/validate-cli-command-coverage.mjs",
23
+ "check:real": "node scripts/validate-cli-output-contract.mjs && node scripts/validate-openapi-cli-examples.mjs",
24
+ "test:openapi-real": "node scripts/run-openapi-cli-examples-real.mjs",
25
+ "smoke": "packages/awb-cli/bin/lj-awb --help && packages/awb-cli/bin/lj-awb auth status -f json && packages/awb-cli/bin/lj-awb system && packages/awb-cli/bin/lj-awb auth && packages/awb-cli/bin/lj-awb account && packages/awb-cli/bin/lj-awb project && packages/awb-cli/bin/lj-awb credits && packages/awb-cli/bin/lj-awb upload && packages/awb-cli/bin/lj-awb model && packages/awb-cli/bin/lj-awb image && packages/awb-cli/bin/lj-awb video && packages/awb-cli/bin/lj-awb task && packages/awb-cli/bin/lj-awb asset && packages/awb-cli/bin/lj-awb subject && packages/awb-cli/bin/lj-awb artifact && packages/awb-cli/bin/lj-awb schema -f json >/dev/null",
26
+ "postinstall": "node install.mjs",
27
+ "version:sync": "node scripts/sync-versions.mjs"
28
+ },
29
+ "files": [
30
+ "build",
31
+ "install.mjs",
32
+ "packages/awb-core",
33
+ "packages/awb-cli",
34
+ "skills/lj-awb",
35
+ "README.md"
36
+ ],
37
+ "keywords": [
38
+ "awb",
39
+ "agent",
40
+ "cli",
41
+ "skill",
42
+ "lingjing"
43
+ ]
44
+ }
@@ -0,0 +1,19 @@
1
+ # @lingjingai/awb-cli-bin
2
+
3
+ 内部 standalone bin 包,命令名为 `lj-awb`。面向用户发布的包是仓库根目录的 `@lingjingai/lj-awb-cli`,它会同时分发 CLI 和 skill。
4
+
5
+ 本地源码运行:
6
+
7
+ ```bash
8
+ node bin/lj-awb.js --help
9
+ node bin/lj-awb.js auth status -f json
10
+ ```
11
+
12
+ 自动化环境推荐通过环境变量提供 access key:
13
+
14
+ ```bash
15
+ export LINGJING_AWB_ACCESS_KEY=<access_key>
16
+ # 兼容 AWB 既有变量:
17
+ export AWB_ACCESS_KEY=<access_key>
18
+ export AWB_CODE=<access_key>
19
+ ```
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ if [ -z "${NODE_EXTRA_CA_CERTS:-}" ] && [ -r /etc/ssl/cert.pem ]; then
5
+ export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem
6
+ fi
7
+
8
+ SOURCE="$0"
9
+ while [ -L "$SOURCE" ]; do
10
+ DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
11
+ SOURCE="$(readlink "$SOURCE")"
12
+ case "$SOURCE" in
13
+ /*) ;;
14
+ *) SOURCE="$DIR/$SOURCE" ;;
15
+ esac
16
+ done
17
+ DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
18
+
19
+ exec node "$DIR/lj-awb.js" "$@"
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ process.env.LINGJING_AWB_COMMAND_PREFIX ??= 'lj-awb';
3
+
4
+ let standaloneModule;
5
+ try {
6
+ standaloneModule = await import('@lingjingai/awb-core/standalone.js');
7
+ } catch {
8
+ standaloneModule = await import('../../awb-core/src/standalone.js');
9
+ }
10
+
11
+ await standaloneModule.runStandaloneCli(process.argv.slice(2));
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@lingjingai/awb-cli-bin",
3
+ "version": "0.3.15",
4
+ "description": "Standalone CLI for Lingjing AWB",
5
+ "private": true,
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "lj-awb": "bin/lj-awb"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "README.md"
14
+ ],
15
+ "dependencies": {
16
+ "@lingjingai/awb-core": "0.3.15"
17
+ }
18
+ }
@@ -0,0 +1,12 @@
1
+ # @lingjingai/awb-core
2
+
3
+ `@lingjingai/awb-core` 是 Lingjing AWB CLI 的共享核心层。
4
+
5
+ 它负责:
6
+
7
+ - 本地认证和状态文件读写
8
+ - 动漫平台 / AWB API 请求封装
9
+ - 命令注册定义
10
+ - 独立 CLI 运行时
11
+
12
+ `lj-awb` 和未来可能出现的其他 frontend 都应复用这一层,不重复写业务逻辑。
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@lingjingai/awb-core",
3
+ "version": "0.3.15",
4
+ "description": "Shared core runtime for Lingjing AWB CLI",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ "./common.js": "./src/common.js",
9
+ "./api.js": "./src/api.js",
10
+ "./artifact.js": "./src/artifact.js",
11
+ "./auth.js": "./src/auth.js",
12
+ "./output.js": "./src/output.js",
13
+ "./services.js": "./src/services.js",
14
+ "./commands.js": "./src/commands.js",
15
+ "./standalone.js": "./src/standalone.js"
16
+ },
17
+ "files": [
18
+ "src",
19
+ "README.md"
20
+ ]
21
+ }
@@ -0,0 +1,349 @@
1
+ import { API_ORIGIN, LingjingAwbCliError } from './common.js';
2
+ import { resolveAuthContext } from './auth.js';
3
+
4
+ export const ASSET_EDIT_ORIGIN = (
5
+ process.env.LINGJING_AWB_ASSET_EDIT_ORIGIN
6
+ || process.env.ASSET_EDIT_ORIGIN
7
+ || 'https://asset-edit.lingjingai.cn'
8
+ ).replace(/\/+$/, '');
9
+
10
+ function buildHeaders(authContext, extraHeaders = {}) {
11
+ const contextHeaders = {};
12
+ for (const [headerName, envNames] of Object.entries({
13
+ userId: ['LINGJING_AWB_USER_ID', 'AWB_USER_ID'],
14
+ groupId: ['LINGJING_AWB_GROUP_ID', 'AWB_GROUP_ID'],
15
+ userName: ['LINGJING_AWB_USER_NAME', 'AWB_USER_NAME'],
16
+ groupName: ['LINGJING_AWB_GROUP_NAME', 'AWB_GROUP_NAME'],
17
+ role: ['LINGJING_AWB_ROLE', 'AWB_ROLE'],
18
+ groupRole: ['LINGJING_AWB_GROUP_ROLE', 'AWB_GROUP_ROLE'],
19
+ })) {
20
+ const value = envNames.map((name) => process.env[name]).find((item) => item !== undefined && item !== '');
21
+ if (value !== undefined) contextHeaders[headerName] = value;
22
+ }
23
+ const headers = {
24
+ 'content-type': 'application/json',
25
+ productcode: '1004',
26
+ source: 'pc',
27
+ timestamp: String(Date.now()),
28
+ ...contextHeaders,
29
+ ...extraHeaders,
30
+ };
31
+ if (authContext?.accessKey) {
32
+ headers['X-Access-Key'] = authContext.accessKey;
33
+ }
34
+ return headers;
35
+ }
36
+
37
+ function stringifyJsonBody(body) {
38
+ return JSON.stringify(body).replace(/[<>&]/g, (char) => ({
39
+ '<': '\\u003c',
40
+ '>': '\\u003e',
41
+ '&': '\\u0026',
42
+ }[char]));
43
+ }
44
+
45
+ export async function apiFetch(pathname, options = {}) {
46
+ const {
47
+ method = 'POST',
48
+ query,
49
+ body,
50
+ auth = true,
51
+ headers = {},
52
+ } = options;
53
+
54
+ const authContext = auth ? await resolveAuthContext({ required: true }) : null;
55
+ const url = new URL(pathname, API_ORIGIN);
56
+ for (const [key, value] of Object.entries(query || {})) {
57
+ if (value === undefined || value === null || value === '') continue;
58
+ url.searchParams.set(key, String(value));
59
+ }
60
+
61
+ let response;
62
+ try {
63
+ response = await fetch(url, {
64
+ method,
65
+ headers: buildHeaders(authContext, headers),
66
+ body: method.toUpperCase() === 'GET' || body === undefined ? undefined : stringifyJsonBody(body),
67
+ });
68
+ } catch (error) {
69
+ const causeCode = error?.cause?.code;
70
+ const isTlsIssuerError = causeCode === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY';
71
+ throw new LingjingAwbCliError(isTlsIssuerError
72
+ ? '请求平台接口失败:本机 Node 无法验证平台 HTTPS 证书链'
73
+ : `请求平台接口失败:${error.message}`, {
74
+ type: 'network_error',
75
+ exitCode: 30,
76
+ hint: isTlsIssuerError
77
+ ? '请使用全局 lj-awb 命令运行,或设置 NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem 后重试。'
78
+ : '',
79
+ details: { url: url.toString(), causeCode },
80
+ });
81
+ }
82
+
83
+ const rawText = await response.text();
84
+ let payload = null;
85
+ try {
86
+ payload = rawText ? JSON.parse(rawText) : null;
87
+ } catch {
88
+ throw new LingjingAwbCliError('平台接口返回了无效 JSON', {
89
+ type: 'invalid_json',
90
+ exitCode: 30,
91
+ details: { status: response.status, body: rawText.slice(0, 500) },
92
+ });
93
+ }
94
+
95
+ if (!response.ok) {
96
+ throw new LingjingAwbCliError(payload?.msg || payload?.message || `${response.status} ${response.statusText}`, {
97
+ type: response.status === 401 ? 'auth_failed' : 'http_error',
98
+ exitCode: response.status === 401 ? 3 : 30,
99
+ details: { status: response.status, payload },
100
+ });
101
+ }
102
+
103
+ if (payload && typeof payload === 'object' && 'code' in payload && String(payload.code) !== '200') {
104
+ throw new LingjingAwbCliError(payload.msg || payload.message || '平台接口返回失败', {
105
+ type: String(payload.code) === '401' ? 'auth_failed' : 'api_error',
106
+ exitCode: String(payload.code) === '401' ? 3 : 1,
107
+ details: payload,
108
+ });
109
+ }
110
+
111
+ return payload && typeof payload === 'object' && 'data' in payload ? payload.data : payload;
112
+ }
113
+
114
+ export async function fetchUserInfo() {
115
+ return await apiFetch('/api/anime/user/account/userInfo', { body: {} });
116
+ }
117
+
118
+ export async function fetchTeams() {
119
+ return await apiFetch('/api/anime/user/group/getOwnGroupList', { body: {} });
120
+ }
121
+
122
+ export async function fetchProjectGroups() {
123
+ return await apiFetch('/api/anime/workbench/projectGroup/getProjectGroup', { method: 'GET' });
124
+ }
125
+
126
+ export async function fetchCurrentProjectGroup() {
127
+ return await apiFetch('/api/anime/workbench/projectGroup/getLastProjectGroup', { method: 'GET' });
128
+ }
129
+
130
+ export async function fetchPoints() {
131
+ return await apiFetch('/api/anime/member/benefits/queryGroupPoint', { body: {} });
132
+ }
133
+
134
+ export async function selectProjectGroup(projectGroupNo) {
135
+ return await apiFetch('/api/anime/workbench/projectGroup/setLastProjectGroup', {
136
+ body: { projectGroupNo },
137
+ });
138
+ }
139
+
140
+ export async function updateCurrentTeam(groupId) {
141
+ return await apiFetch('/api/anime/user/group/updateCurrentGroup', {
142
+ body: { groupId },
143
+ });
144
+ }
145
+
146
+ export async function fetchProjectGroupUsers() {
147
+ return await apiFetch('/api/anime/workbench/projectGroup/getGroupAllUser', { method: 'GET' });
148
+ }
149
+
150
+ export async function fetchProjectGroupIntegral(projectGroupNo) {
151
+ return await apiFetch('/api/anime/workbench/projectGroup/getProjectGroupIntegral', {
152
+ body: { projectGroupNo },
153
+ });
154
+ }
155
+
156
+ export async function createProjectGroup(payload) {
157
+ return await apiFetch('/api/anime/workbench/projectGroup/createProjectGroup', { body: payload });
158
+ }
159
+
160
+ export async function updateProjectGroup(payload) {
161
+ return await apiFetch('/api/anime/workbench/projectGroup/modify', { body: payload });
162
+ }
163
+
164
+ export async function fetchProjectManagementList(payload = {}) {
165
+ return await apiFetch('/api/anime/workbench/project/management/list', {
166
+ body: {
167
+ pageNo: 1,
168
+ pageSize: 20,
169
+ ...payload,
170
+ },
171
+ });
172
+ }
173
+
174
+ export async function fetchModelsByUsage(usage, body = {}) {
175
+ return await apiFetch(`/api/resource/model/list/usage/${encodeURIComponent(usage)}`, { body });
176
+ }
177
+
178
+ export async function fetchModelOptions(payload = {}) {
179
+ return await apiFetch('/api/resource/model/config/options', { body: payload });
180
+ }
181
+
182
+ export async function fetchModelGroupInfoAll(modelGroupCode) {
183
+ return await apiFetch(`/api/resource/model/info/group/${encodeURIComponent(modelGroupCode)}/all`, { method: 'GET' });
184
+ }
185
+
186
+ export async function fetchUploadSecret(payload = {}) {
187
+ return await apiFetch('/api/anime/workbench/TencentCloud/getSecret', { body: payload });
188
+ }
189
+
190
+ export async function fetchObjectSignUrl(objectName) {
191
+ return await apiFetch('/api/anime/workbench/TencentCloud/getObjectSignUrl', {
192
+ method: 'GET',
193
+ query: { objectName },
194
+ });
195
+ }
196
+
197
+ export async function fetchImageFee(payload = {}) {
198
+ return await apiFetch('/api/material/creation/imageCreateFeeCalc', { body: payload });
199
+ }
200
+
201
+ export async function createImageTask(payload = {}) {
202
+ return await apiFetch('/api/material/creation/imageCreate', { body: payload });
203
+ }
204
+
205
+ export async function fetchImageTask(taskId) {
206
+ return await apiFetch('/api/material/creation/imageCreateGet', {
207
+ method: 'GET',
208
+ query: { taskId },
209
+ });
210
+ }
211
+
212
+ export async function fetchVideoFee(payload = {}) {
213
+ return await apiFetch('/api/material/creation/videoCreateFeeCalc', { body: payload });
214
+ }
215
+
216
+ export async function createVideoTask(payload = {}) {
217
+ return await apiFetch('/api/material/creation/videoCreate', { body: payload });
218
+ }
219
+
220
+ export async function fetchVideoTask(taskId) {
221
+ return await apiFetch('/api/material/creation/videoCreateGet', {
222
+ method: 'GET',
223
+ query: { taskId },
224
+ });
225
+ }
226
+
227
+ export async function fetchTaskFeed(payload = {}) {
228
+ return await apiFetch('/api/material/creation/task/feedPull', {
229
+ query: payload,
230
+ body: payload,
231
+ });
232
+ }
233
+
234
+ export async function matchAssetMaterial(payload = {}) {
235
+ return await apiFetch('/api/admin/asset-material/match', { body: payload });
236
+ }
237
+
238
+ export async function listAssetGroups(payload = {}) {
239
+ return await apiFetch('/api/material/asset-groups/list', { body: payload });
240
+ }
241
+
242
+ export async function getAssetGroup(groupId) {
243
+ return await apiFetch(`/api/material/asset-groups/${encodeURIComponent(groupId)}`, { method: 'GET' });
244
+ }
245
+
246
+ export async function createAssetGroup(payload = {}) {
247
+ return await apiFetch('/api/material/asset-groups', { body: payload });
248
+ }
249
+
250
+ export async function updateAssetGroup(groupId, payload = {}) {
251
+ return await apiFetch(`/api/material/asset-groups/${encodeURIComponent(groupId)}`, {
252
+ method: 'PUT',
253
+ body: payload,
254
+ });
255
+ }
256
+
257
+ export async function registerAsset(payload = {}) {
258
+ return await apiFetch('/api/material/assets', { body: payload });
259
+ }
260
+
261
+ export async function listElements(query = {}) {
262
+ return await apiFetch('/api/material/creation/listElements', {
263
+ method: 'GET',
264
+ query,
265
+ });
266
+ }
267
+
268
+ export async function createElement(payload = {}) {
269
+ return await apiFetch('/api/material/creation/createElement', { body: payload });
270
+ }
271
+
272
+ export async function getElementByReqTaskId(reqTaskId) {
273
+ return await apiFetch('/api/material/creation/getElementByReqTaskId', {
274
+ method: 'GET',
275
+ query: { reqTaskId },
276
+ });
277
+ }
278
+
279
+ export async function listElementsByName(elementName) {
280
+ return await apiFetch('/api/material/creation/listElementsByName', {
281
+ method: 'GET',
282
+ query: { elementName },
283
+ });
284
+ }
285
+
286
+ export async function assetEditFetch(pathname, options = {}) {
287
+ const {
288
+ method = 'GET',
289
+ body,
290
+ query,
291
+ headers = {},
292
+ } = options;
293
+ const url = new URL(pathname, ASSET_EDIT_ORIGIN);
294
+ for (const [key, value] of Object.entries(query || {})) {
295
+ if (value === undefined || value === null || value === '') continue;
296
+ url.searchParams.set(key, String(value));
297
+ }
298
+ let response;
299
+ try {
300
+ response = await fetch(url, {
301
+ method,
302
+ headers: {
303
+ 'content-type': 'application/json',
304
+ ...headers,
305
+ },
306
+ body: method.toUpperCase() === 'GET' || body === undefined ? undefined : stringifyJsonBody(body),
307
+ });
308
+ } catch (error) {
309
+ throw new LingjingAwbCliError(`请求去字幕服务失败:${error.message}`, {
310
+ type: 'network_error',
311
+ exitCode: 30,
312
+ details: { url: url.toString() },
313
+ });
314
+ }
315
+ const rawText = await response.text();
316
+ let payload = null;
317
+ try {
318
+ payload = rawText ? JSON.parse(rawText) : null;
319
+ } catch {
320
+ throw new LingjingAwbCliError('去字幕服务返回了无效 JSON', {
321
+ type: 'invalid_json',
322
+ exitCode: 30,
323
+ details: { status: response.status, body: rawText.slice(0, 500) },
324
+ });
325
+ }
326
+ if (!response.ok) {
327
+ throw new LingjingAwbCliError(payload?.message || payload?.msg || `${response.status} ${response.statusText}`, {
328
+ type: 'http_error',
329
+ exitCode: 30,
330
+ details: { status: response.status, payload },
331
+ });
332
+ }
333
+ return payload && typeof payload === 'object' && 'data' in payload ? payload.data : payload;
334
+ }
335
+
336
+ export async function createSubtitleRemoveTask(payload = {}) {
337
+ return await assetEditFetch('/api/watermark/tasks', {
338
+ method: 'POST',
339
+ body: payload,
340
+ });
341
+ }
342
+
343
+ export async function fetchSubtitleTaskByPublicId(publicId) {
344
+ return await assetEditFetch(`/api/watermark/tasks/${encodeURIComponent(publicId)}`);
345
+ }
346
+
347
+ export async function fetchSubtitleTaskByRemoteId(remoteTaskId) {
348
+ return await assetEditFetch(`/api/watermark/remote/${encodeURIComponent(remoteTaskId)}`);
349
+ }