bukesiyi-cli 0.1.3 → 0.3.1
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/README.md +21 -8
- package/dist/main.js +1963 -99
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -49,11 +49,13 @@ var EXIT = {
|
|
|
49
49
|
NOT_FOUND: 10,
|
|
50
50
|
INTERRUPTED: 130
|
|
51
51
|
};
|
|
52
|
-
function exitCodeForHttpStatus(
|
|
53
|
-
if (
|
|
54
|
-
if (
|
|
55
|
-
if (
|
|
56
|
-
if (
|
|
52
|
+
function exitCodeForHttpStatus(status4) {
|
|
53
|
+
if (status4 === 401 || status4 === 403) return EXIT.AUTH;
|
|
54
|
+
if (status4 === 402) return EXIT.FUNDS;
|
|
55
|
+
if (status4 === 404) return EXIT.NOT_FOUND;
|
|
56
|
+
if (status4 === 409) return EXIT.QUOTE_CHANGED;
|
|
57
|
+
if (status4 === 429) return EXIT.RETRY_LATER;
|
|
58
|
+
if (status4 >= 400 && status4 < 500) return EXIT.USAGE;
|
|
57
59
|
return EXIT.SERVER;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -87,6 +89,7 @@ var CliError = class _CliError extends Error {
|
|
|
87
89
|
retryAfterSeconds;
|
|
88
90
|
httpStatus;
|
|
89
91
|
hints;
|
|
92
|
+
recovery;
|
|
90
93
|
constructor(init) {
|
|
91
94
|
super(redactKeys(init.message));
|
|
92
95
|
this.name = "CliError";
|
|
@@ -97,6 +100,7 @@ var CliError = class _CliError extends Error {
|
|
|
97
100
|
if (init.retryAfterSeconds !== void 0) this.retryAfterSeconds = init.retryAfterSeconds;
|
|
98
101
|
if (init.httpStatus !== void 0) this.httpStatus = init.httpStatus;
|
|
99
102
|
this.hints = init.hints ?? [];
|
|
103
|
+
if (init.recovery !== void 0) this.recovery = init.recovery;
|
|
100
104
|
}
|
|
101
105
|
withHint(hint) {
|
|
102
106
|
return new _CliError({
|
|
@@ -107,15 +111,29 @@ var CliError = class _CliError extends Error {
|
|
|
107
111
|
traceId: this.traceId,
|
|
108
112
|
retryAfterSeconds: this.retryAfterSeconds,
|
|
109
113
|
httpStatus: this.httpStatus,
|
|
110
|
-
hints: [...this.hints, hint]
|
|
114
|
+
hints: [...this.hints, hint],
|
|
115
|
+
recovery: this.recovery
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
withRecovery(recovery) {
|
|
119
|
+
return new _CliError({
|
|
120
|
+
code: this.code,
|
|
121
|
+
message: this.message,
|
|
122
|
+
exitCode: this.exitCode,
|
|
123
|
+
retryable: this.retryable,
|
|
124
|
+
traceId: this.traceId,
|
|
125
|
+
retryAfterSeconds: this.retryAfterSeconds,
|
|
126
|
+
httpStatus: this.httpStatus,
|
|
127
|
+
hints: this.hints,
|
|
128
|
+
recovery
|
|
111
129
|
});
|
|
112
130
|
}
|
|
113
131
|
};
|
|
114
|
-
function usageError(
|
|
115
|
-
return new CliError({ code: "CLI_USAGE", exitCode: EXIT.USAGE, message: `${
|
|
132
|
+
function usageError(message2) {
|
|
133
|
+
return new CliError({ code: "CLI_USAGE", exitCode: EXIT.USAGE, message: `${message2}\u3002\u8FD0\u884C buke --help \u770B\u7528\u6CD5` });
|
|
116
134
|
}
|
|
117
|
-
function interruptedError(
|
|
118
|
-
return new CliError({ code: "CLI_INTERRUPTED", exitCode: EXIT.INTERRUPTED, message });
|
|
135
|
+
function interruptedError(message2 = "\u5DF2\u4E2D\u65AD") {
|
|
136
|
+
return new CliError({ code: "CLI_INTERRUPTED", exitCode: EXIT.INTERRUPTED, message: message2 });
|
|
119
137
|
}
|
|
120
138
|
function isRecord(value) {
|
|
121
139
|
return typeof value === "object" && value !== null;
|
|
@@ -215,8 +233,8 @@ function malformedKey() {
|
|
|
215
233
|
message: "\u8FD9\u4E0D\u50CF\u4E00\u628A API \u5BC6\u94A5\uFF1A\u5E94\u4EE5 bk_ \u5F00\u5934\uFF0C\u4E2D\u95F4\u6CA1\u6709\u7A7A\u683C\u6216\u6362\u884C"
|
|
216
234
|
});
|
|
217
235
|
}
|
|
218
|
-
function missingKey(
|
|
219
|
-
return new CliError({ code: "CLI_KEY_MISSING", exitCode: EXIT.USAGE, message });
|
|
236
|
+
function missingKey(message2) {
|
|
237
|
+
return new CliError({ code: "CLI_KEY_MISSING", exitCode: EXIT.USAGE, message: message2 });
|
|
220
238
|
}
|
|
221
239
|
var NO_KEY_READ = "\u6CA1\u6709\u8BFB\u5230\u5BC6\u94A5\u3002\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF BUKE_API_KEY\uFF0C\u6216\u8005\u901A\u8FC7\u7BA1\u9053\u628A\u5BC6\u94A5\u4F20\u7ED9 buke login";
|
|
222
240
|
function assertKeyFormat(key) {
|
|
@@ -239,10 +257,10 @@ function toBuffer(chunk) {
|
|
|
239
257
|
return Buffer.from(chunk);
|
|
240
258
|
}
|
|
241
259
|
function readToEnd(stdin, timeoutMs, signal) {
|
|
242
|
-
return new Promise((
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
243
261
|
const state = stdin;
|
|
244
262
|
if (state.readableEnded === true || state.destroyed === true) {
|
|
245
|
-
|
|
263
|
+
resolve("");
|
|
246
264
|
return;
|
|
247
265
|
}
|
|
248
266
|
const chunks = [];
|
|
@@ -270,7 +288,7 @@ function readToEnd(stdin, timeoutMs, signal) {
|
|
|
270
288
|
}
|
|
271
289
|
chunks.push(piece);
|
|
272
290
|
};
|
|
273
|
-
const onEnd = () => settle(() =>
|
|
291
|
+
const onEnd = () => settle(() => resolve(Buffer.concat(chunks).toString("utf8").trim()));
|
|
274
292
|
const onError = (error) => settle(() => reject(missingKey(`\u6CA1\u6709\u8BFB\u5230\u5BC6\u94A5\uFF1A\u6807\u51C6\u8F93\u5165\u51FA\u9519\uFF08${briefReason(error)}\uFF09`)));
|
|
275
293
|
const timer = setTimeout(() => {
|
|
276
294
|
settle(
|
|
@@ -290,7 +308,7 @@ function readToEnd(stdin, timeoutMs, signal) {
|
|
|
290
308
|
});
|
|
291
309
|
}
|
|
292
310
|
function readHiddenLine(stdin, signal) {
|
|
293
|
-
return new Promise((
|
|
311
|
+
return new Promise((resolve, reject) => {
|
|
294
312
|
let typed = "";
|
|
295
313
|
let escape = "none";
|
|
296
314
|
let settled = false;
|
|
@@ -321,7 +339,7 @@ function readHiddenLine(stdin, signal) {
|
|
|
321
339
|
continue;
|
|
322
340
|
}
|
|
323
341
|
if (char === "\r" || char === "\n") {
|
|
324
|
-
settle(() =>
|
|
342
|
+
settle(() => resolve(typed));
|
|
325
343
|
return;
|
|
326
344
|
}
|
|
327
345
|
if (char === CTRL_C) {
|
|
@@ -329,7 +347,7 @@ function readHiddenLine(stdin, signal) {
|
|
|
329
347
|
return;
|
|
330
348
|
}
|
|
331
349
|
if (char === CTRL_D) {
|
|
332
|
-
settle(() => typed === "" ? reject(missingKey(NO_KEY_READ)) :
|
|
350
|
+
settle(() => typed === "" ? reject(missingKey(NO_KEY_READ)) : resolve(typed));
|
|
333
351
|
return;
|
|
334
352
|
}
|
|
335
353
|
if (char === DELETE || char === BACKSPACE) {
|
|
@@ -349,7 +367,7 @@ function readHiddenLine(stdin, signal) {
|
|
|
349
367
|
}
|
|
350
368
|
if (escape === "started") escape = "carried";
|
|
351
369
|
};
|
|
352
|
-
const onEnd = () => settle(() =>
|
|
370
|
+
const onEnd = () => settle(() => resolve(typed));
|
|
353
371
|
const onError = (error) => settle(() => reject(missingKey(`\u6CA1\u6709\u8BFB\u5230\u5BC6\u94A5\uFF1A\u6807\u51C6\u8F93\u5165\u51FA\u9519\uFF08${briefReason(error)}\uFF09`)));
|
|
354
372
|
const onAbort = () => settle(() => reject(interruptedError(CANCELLED)));
|
|
355
373
|
stdin.on("data", onData);
|
|
@@ -394,35 +412,100 @@ async function readNewKey(io) {
|
|
|
394
412
|
|
|
395
413
|
// package.json
|
|
396
414
|
var name = "bukesiyi-cli";
|
|
397
|
-
var version = "0.1
|
|
415
|
+
var version = "0.3.1";
|
|
398
416
|
|
|
399
417
|
// src/version.ts
|
|
400
418
|
var PACKAGE_NAME = name;
|
|
401
419
|
var VERSION = version;
|
|
402
420
|
|
|
421
|
+
// src/task-recovery.ts
|
|
422
|
+
function shellArgument(value) {
|
|
423
|
+
return /^[A-Za-z0-9_][A-Za-z0-9_.:-]*$/.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
424
|
+
}
|
|
425
|
+
function taskRecovery(taskType, taskId, status4, baseUrl, failureCode) {
|
|
426
|
+
const customOrigin = baseUrl !== DEFAULT_BASE_URL;
|
|
427
|
+
return {
|
|
428
|
+
taskType,
|
|
429
|
+
acceptance: "confirmed",
|
|
430
|
+
taskId,
|
|
431
|
+
status: status4,
|
|
432
|
+
...customOrigin ? { baseUrl } : {},
|
|
433
|
+
...failureCode === void 0 || failureCode === "" ? {} : { failureCode },
|
|
434
|
+
nextAction: "query",
|
|
435
|
+
statusCommand: `buke ${taskType} status ${shellArgument(taskId)}` + (customOrigin ? ` --base-url ${shellArgument(baseUrl)}` : "")
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
async function retainTaskOnError(recovery, operation) {
|
|
439
|
+
try {
|
|
440
|
+
return await operation();
|
|
441
|
+
} catch (error) {
|
|
442
|
+
const failure = toCliError(error);
|
|
443
|
+
throw failure.withRecovery({ ...recovery(), ...failure.recovery });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async function retainSubmissionOnError(taskType, idempotencyKey, baseUrl, operation) {
|
|
447
|
+
try {
|
|
448
|
+
return await operation();
|
|
449
|
+
} catch (error) {
|
|
450
|
+
const failure = toCliError(error);
|
|
451
|
+
const rejectedResponse = failure.httpStatus !== void 0 && failure.httpStatus >= 400 && failure.httpStatus < 500 && failure.httpStatus !== 408;
|
|
452
|
+
const uncertainResponse = failure.httpStatus === 408 || failure.httpStatus !== void 0 && failure.httpStatus >= 500 && failure.httpStatus < 600;
|
|
453
|
+
const uncertainTransport = ["CLI_NETWORK", "CLI_TIMEOUT", "CLI_BAD_RESPONSE", "CLI_INTERRUPTED"].includes(failure.code);
|
|
454
|
+
if (!rejectedResponse && (uncertainResponse || uncertainTransport)) {
|
|
455
|
+
throw failure.withRecovery({
|
|
456
|
+
taskType,
|
|
457
|
+
acceptance: "unknown",
|
|
458
|
+
idempotencyKey,
|
|
459
|
+
nextAction: "query",
|
|
460
|
+
...baseUrl !== DEFAULT_BASE_URL ? { baseUrl } : {}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
throw failure;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
403
467
|
// src/client.ts
|
|
404
468
|
var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
469
|
+
var DEFAULT_DOWNLOAD_TIMEOUT_MS = 12e4;
|
|
470
|
+
var MAX_DOWNLOAD_BYTES = 1024 * 1024 * 1024;
|
|
405
471
|
var ME_PATH = "/api/open/v1/me";
|
|
406
472
|
var MAX_BODY_BYTES = 1024 * 1024;
|
|
407
473
|
var NOT_FOUND_HINT = "\u8FD9\u4E2A\u5730\u5740\u4E0D\u63D0\u4F9B API \u8C03\u7528\uFF1A\u53EF\u80FD\u8FD8\u6CA1\u6709\u5BF9\u5916\u5F00\u901A\uFF0C\u6216\u8005 --base-url \u5199\u9519\u4E86";
|
|
474
|
+
var EXIT_BY_SERVER_CODE = {
|
|
475
|
+
OPEN_API_INSUFFICIENT_BALANCE: EXIT.FUNDS,
|
|
476
|
+
OPEN_API_QUOTE_CHANGED: EXIT.QUOTE_CHANGED,
|
|
477
|
+
OPEN_API_QUOTE_REQUIRED: EXIT.QUOTE_CHANGED,
|
|
478
|
+
OPEN_API_ORDER_NOT_READY: EXIT.RETRY_LATER,
|
|
479
|
+
OPEN_API_QUOTA_EXCEEDED: EXIT.RETRY_LATER,
|
|
480
|
+
OPEN_API_ORDER_NOT_FOUND: EXIT.NOT_FOUND,
|
|
481
|
+
OPEN_API_PRODUCT_NOT_FOUND: EXIT.NOT_FOUND,
|
|
482
|
+
OPEN_API_TEMPLATE_NOT_FOUND: EXIT.NOT_FOUND,
|
|
483
|
+
// 「这单已交付/已失败,不能取消」也是 409,按状态码会被当成「报价变了」(6);
|
|
484
|
+
// 它说的是这个动作对当前状态不成立,重试也不会变,按「用法不对」(2)定。
|
|
485
|
+
OPEN_API_ORDER_NOT_CANCELLABLE: EXIT.USAGE
|
|
486
|
+
};
|
|
408
487
|
function userAgent() {
|
|
409
488
|
return `${PACKAGE_NAME}/${VERSION} node/${process.versions.node}`;
|
|
410
489
|
}
|
|
411
490
|
function isRecord2(value) {
|
|
412
491
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
413
492
|
}
|
|
414
|
-
function
|
|
415
|
-
return
|
|
493
|
+
function numberOrUndefined(value) {
|
|
494
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
416
495
|
}
|
|
417
|
-
function
|
|
418
|
-
|
|
496
|
+
function retryableStatus(status4) {
|
|
497
|
+
return status4 === 429 || status4 >= 500;
|
|
498
|
+
}
|
|
499
|
+
function unreadableResponse(status4) {
|
|
500
|
+
const retryable = retryableStatus(status4);
|
|
419
501
|
const advice = retryable ? "\u53EF\u80FD\u662F\u7F51\u5173\u6216\u4E2D\u95F4\u5C42\u51FA\u7684\u9519\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : "\u53EF\u80FD\u662F\u7F51\u5173\u6216\u4E2D\u95F4\u5C42\u51FA\u7684\u9519\uFF0C\u4E5F\u53EF\u80FD\u662F\u670D\u52A1\u5730\u5740\u6307\u5230\u4E86\u522B\u5904\uFF1B\u91CD\u8BD5\u591A\u534A\u6CA1\u6709\u7528\uFF0C\u8BF7\u5148\u6838\u5BF9\u670D\u52A1\u5730\u5740";
|
|
420
502
|
return new CliError({
|
|
421
503
|
code: "CLI_BAD_RESPONSE",
|
|
422
504
|
exitCode: EXIT.SERVER,
|
|
423
505
|
retryable,
|
|
424
|
-
|
|
425
|
-
|
|
506
|
+
httpStatus: status4,
|
|
507
|
+
message: `\u670D\u52A1\u5668\u56DE\u4E86\u4E00\u4E2A\u770B\u4E0D\u61C2\u7684\u56DE\u5E94\uFF08HTTP ${status4}\uFF09\uFF0C${advice}`,
|
|
508
|
+
hints: status4 === 404 ? [NOT_FOUND_HINT] : []
|
|
426
509
|
});
|
|
427
510
|
}
|
|
428
511
|
function redirectTarget(response, requestUrl) {
|
|
@@ -442,11 +525,15 @@ function discardBody(response) {
|
|
|
442
525
|
}
|
|
443
526
|
}
|
|
444
527
|
async function readBody(response, abandoned) {
|
|
528
|
+
const bytes = await readBodyBytes(response, abandoned, MAX_BODY_BYTES);
|
|
529
|
+
return bytes === null ? null : bytes.toString("utf8");
|
|
530
|
+
}
|
|
531
|
+
async function readBodyBytes(response, abandoned, maxBytes) {
|
|
445
532
|
const body = response.body;
|
|
446
|
-
if (body === null) return
|
|
533
|
+
if (body === null) return Buffer.alloc(0);
|
|
447
534
|
if (body === void 0) {
|
|
448
535
|
const text = await response.text();
|
|
449
|
-
return Buffer.byteLength(text, "utf8") >
|
|
536
|
+
return Buffer.byteLength(text, "utf8") > maxBytes ? null : Buffer.from(text, "utf8");
|
|
450
537
|
}
|
|
451
538
|
const reader = body.getReader();
|
|
452
539
|
const release2 = () => void reader.cancel().catch(() => void 0);
|
|
@@ -459,7 +546,7 @@ async function readBody(response, abandoned) {
|
|
|
459
546
|
const { done, value } = await reader.read();
|
|
460
547
|
if (done) break;
|
|
461
548
|
size += value.byteLength;
|
|
462
|
-
if (size >
|
|
549
|
+
if (size > maxBytes) {
|
|
463
550
|
await reader.cancel().catch(() => void 0);
|
|
464
551
|
return null;
|
|
465
552
|
}
|
|
@@ -468,7 +555,7 @@ async function readBody(response, abandoned) {
|
|
|
468
555
|
} finally {
|
|
469
556
|
abandoned.removeEventListener("abort", release2);
|
|
470
557
|
}
|
|
471
|
-
return Buffer.concat(chunks)
|
|
558
|
+
return Buffer.concat(chunks);
|
|
472
559
|
}
|
|
473
560
|
function isTimeout(error) {
|
|
474
561
|
const name2 = typeof error === "object" && error !== null ? error.name : void 0;
|
|
@@ -478,9 +565,9 @@ function proxyHints(origin, deps) {
|
|
|
478
565
|
const variable = deps.env ? envProxyFor(origin, deps.env) : null;
|
|
479
566
|
return variable === null ? [] : [`\u73AF\u5883\u91CC\u5F00\u7740\u4EE3\u7406\uFF08${variable}\uFF09\uFF0C\u8FD9\u6B21\u8BF7\u6C42\u53EF\u80FD\u662F\u7ECF\u5B83\u53D1\u51FA\u7684\uFF1A\u51FA\u95EE\u9898\u7684\u4E5F\u53EF\u80FD\u662F\u4EE3\u7406`];
|
|
480
567
|
}
|
|
481
|
-
async function exchange(url, apiKey, deps, origin) {
|
|
568
|
+
async function exchange(url, apiKey, plan, deps, origin) {
|
|
482
569
|
if (deps.signal?.aborted) throw interruptedError();
|
|
483
|
-
const timeoutMs = deps.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
570
|
+
const timeoutMs = plan.timeoutMs ?? deps.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
484
571
|
const controller = new AbortController();
|
|
485
572
|
let timedOut = false;
|
|
486
573
|
let timer;
|
|
@@ -500,15 +587,20 @@ async function exchange(url, apiKey, deps, origin) {
|
|
|
500
587
|
};
|
|
501
588
|
});
|
|
502
589
|
deps.signal?.addEventListener("abort", stop, { once: true });
|
|
590
|
+
const headers = {
|
|
591
|
+
Authorization: `Bearer ${apiKey}`,
|
|
592
|
+
Accept: "application/json",
|
|
593
|
+
"User-Agent": userAgent()
|
|
594
|
+
};
|
|
595
|
+
if (plan.contentType !== void 0) headers["Content-Type"] = plan.contentType;
|
|
596
|
+
if (plan.idempotencyKey !== void 0) headers["Idempotency-Key"] = plan.idempotencyKey;
|
|
597
|
+
const asBytes = plan.asBytes === true;
|
|
503
598
|
const send = deps.fetch;
|
|
504
599
|
const work = (async () => {
|
|
505
600
|
const response = await send(url, {
|
|
506
|
-
method:
|
|
507
|
-
headers
|
|
508
|
-
|
|
509
|
-
Accept: "application/json",
|
|
510
|
-
"User-Agent": userAgent()
|
|
511
|
-
},
|
|
601
|
+
method: plan.method,
|
|
602
|
+
headers,
|
|
603
|
+
...plan.body === void 0 ? {} : { body: plan.body },
|
|
512
604
|
// 跟着跳转走,密钥就会被带到一个使用者没有点头的地方。
|
|
513
605
|
redirect: "manual",
|
|
514
606
|
signal: controller.signal
|
|
@@ -517,6 +609,11 @@ async function exchange(url, apiKey, deps, origin) {
|
|
|
517
609
|
discardBody(response);
|
|
518
610
|
return { status: response.status, redirectedTo: redirectTarget(response, url) };
|
|
519
611
|
}
|
|
612
|
+
if (asBytes) {
|
|
613
|
+
const bytes = await readBodyBytes(response, controller.signal, MAX_DOWNLOAD_BYTES);
|
|
614
|
+
if (bytes === null) throw unreadableResponse(response.status);
|
|
615
|
+
return { status: response.status, bytes, headers: response.headers };
|
|
616
|
+
}
|
|
520
617
|
return { status: response.status, text: await readBody(response, controller.signal) };
|
|
521
618
|
})();
|
|
522
619
|
work.catch(() => void 0);
|
|
@@ -554,36 +651,40 @@ function parseJson(text) {
|
|
|
554
651
|
return null;
|
|
555
652
|
}
|
|
556
653
|
}
|
|
557
|
-
function serverError(
|
|
654
|
+
function serverError(status4, body, apiKey) {
|
|
558
655
|
const error = isRecord2(body) ? body.error : void 0;
|
|
559
656
|
if (!isRecord2(error) || typeof error.code !== "string" || typeof error.message !== "string") return null;
|
|
560
657
|
const retryAfter = error.retryAfterSeconds;
|
|
561
658
|
return new CliError({
|
|
562
659
|
code: error.code,
|
|
563
660
|
// 对面把收到的密钥抄回话里时,在这一层就抹掉。
|
|
564
|
-
message: error.message.trim() === "" ? `\u670D\u52A1\u5668\u62D2\u7EDD\u4E86\u8FD9\u6B21\u8BF7\u6C42\uFF08HTTP ${
|
|
565
|
-
|
|
566
|
-
|
|
661
|
+
message: error.message.trim() === "" ? `\u670D\u52A1\u5668\u62D2\u7EDD\u4E86\u8FD9\u6B21\u8BF7\u6C42\uFF08HTTP ${status4}\uFF09` : redactSecret(error.message, apiKey),
|
|
662
|
+
// 列进显式表的码按表定(409 在业务里分「报价变了」和「还没好」两种),其余按状态码。
|
|
663
|
+
exitCode: EXIT_BY_SERVER_CODE[error.code] ?? exitCodeForHttpStatus(status4),
|
|
664
|
+
retryable: typeof error.retryable === "boolean" ? error.retryable : retryableStatus(status4),
|
|
567
665
|
traceId: typeof error.traceId === "string" ? error.traceId : null,
|
|
568
666
|
retryAfterSeconds: typeof retryAfter === "number" && Number.isFinite(retryAfter) ? retryAfter : void 0,
|
|
569
|
-
httpStatus:
|
|
570
|
-
|
|
667
|
+
httpStatus: status4,
|
|
668
|
+
// 那句「这个地址不提供 API 调用」只对端点级的 404 有意义:业务里的 404(单不存在、产品不存在)
|
|
669
|
+
// 说的是东西没找到,不是地址写错。
|
|
670
|
+
hints: status4 === 404 && error.code === "OPEN_API_NOT_FOUND" ? [NOT_FOUND_HINT] : []
|
|
571
671
|
});
|
|
572
672
|
}
|
|
573
|
-
|
|
673
|
+
function redirectError(redirectedTo) {
|
|
674
|
+
const where = redirectedTo ? `\uFF08${redirectedTo}\uFF09` : "";
|
|
675
|
+
return new CliError({
|
|
676
|
+
code: "CLI_REDIRECT",
|
|
677
|
+
exitCode: EXIT.SERVER,
|
|
678
|
+
retryable: false,
|
|
679
|
+
message: `\u670D\u52A1\u5668\u628A\u8BF7\u6C42\u8F6C\u5230\u4E86\u522B\u5904${where}\uFF0C\u4E3A\u4E86\u4E0D\u628A\u5BC6\u94A5\u5E26\u8FC7\u53BB\u6CA1\u6709\u8DDF\u968F\u3002\u8BF7\u6838\u5BF9 --base-url`
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
async function requestJson(baseUrl, apiKey, plan, deps) {
|
|
574
683
|
const origin = normalizeBaseUrl(baseUrl);
|
|
575
684
|
assertKeyFormat(apiKey);
|
|
576
685
|
assertReachedDirectly(origin, deps.env);
|
|
577
|
-
const result = await exchange(`${origin}${path}`, apiKey, deps, origin);
|
|
578
|
-
if ("redirectedTo" in result)
|
|
579
|
-
const where = result.redirectedTo ? `\uFF08${result.redirectedTo}\uFF09` : "";
|
|
580
|
-
throw new CliError({
|
|
581
|
-
code: "CLI_REDIRECT",
|
|
582
|
-
exitCode: EXIT.SERVER,
|
|
583
|
-
retryable: false,
|
|
584
|
-
message: `\u670D\u52A1\u5668\u628A\u8BF7\u6C42\u8F6C\u5230\u4E86\u522B\u5904${where}\uFF0C\u4E3A\u4E86\u4E0D\u628A\u5BC6\u94A5\u5E26\u8FC7\u53BB\u6CA1\u6709\u8DDF\u968F\u3002\u8BF7\u6838\u5BF9 --base-url`
|
|
585
|
-
});
|
|
586
|
-
}
|
|
686
|
+
const result = await exchange(`${origin}${plan.path}`, apiKey, plan, deps, origin);
|
|
687
|
+
if ("redirectedTo" in result) throw redirectError(result.redirectedTo);
|
|
587
688
|
const parsed = parseJson(result.text);
|
|
588
689
|
if (result.status >= 200 && result.status < 300) {
|
|
589
690
|
if (!parsed) throw unreadableResponse(result.status);
|
|
@@ -591,6 +692,357 @@ async function getJson(baseUrl, apiKey, path, deps) {
|
|
|
591
692
|
}
|
|
592
693
|
throw (parsed && serverError(result.status, parsed.value, apiKey)) ?? unreadableResponse(result.status);
|
|
593
694
|
}
|
|
695
|
+
async function getJson(baseUrl, apiKey, path, deps) {
|
|
696
|
+
return requestJson(baseUrl, apiKey, { method: "GET", path }, deps);
|
|
697
|
+
}
|
|
698
|
+
async function postJson(baseUrl, apiKey, path, body, idempotencyKey, deps) {
|
|
699
|
+
return requestJson(
|
|
700
|
+
baseUrl,
|
|
701
|
+
apiKey,
|
|
702
|
+
{ method: "POST", path, body: JSON.stringify(body), contentType: "application/json", idempotencyKey },
|
|
703
|
+
deps
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
async function putRaw(baseUrl, apiKey, path, bytes, contentType, idempotencyKey, deps) {
|
|
707
|
+
return requestJson(baseUrl, apiKey, { method: "PUT", path, body: bytes, contentType, idempotencyKey }, deps);
|
|
708
|
+
}
|
|
709
|
+
async function postRaw(baseUrl, apiKey, path, bytes, contentType, idempotencyKey, deps) {
|
|
710
|
+
return requestJson(baseUrl, apiKey, { method: "POST", path, body: bytes, contentType, idempotencyKey }, deps);
|
|
711
|
+
}
|
|
712
|
+
async function downloadFile(baseUrl, apiKey, path, deps) {
|
|
713
|
+
const origin = normalizeBaseUrl(baseUrl);
|
|
714
|
+
assertKeyFormat(apiKey);
|
|
715
|
+
assertReachedDirectly(origin, deps.env);
|
|
716
|
+
const result = await exchange(`${origin}${path}`, apiKey, downloadPlan(path, deps), deps, origin);
|
|
717
|
+
if ("redirectedTo" in result) throw redirectError(result.redirectedTo);
|
|
718
|
+
if (result.status < 200 || result.status >= 300) {
|
|
719
|
+
const text = result.bytes.toString("utf8");
|
|
720
|
+
const parsed = parseJson(text);
|
|
721
|
+
throw (parsed && serverError(result.status, parsed.value, apiKey)) ?? unreadableResponse(result.status);
|
|
722
|
+
}
|
|
723
|
+
return { bytes: result.bytes, filename: filenameFromDisposition(result.headers.get("content-disposition")) };
|
|
724
|
+
}
|
|
725
|
+
function downloadPlan(path, deps) {
|
|
726
|
+
return {
|
|
727
|
+
method: "GET",
|
|
728
|
+
path,
|
|
729
|
+
// 下载不共用 JSON 请求 15 秒的默认时限;调用方在 deps 里给了更短的,照他的。
|
|
730
|
+
timeoutMs: deps.requestTimeoutMs !== void 0 ? Math.max(deps.requestTimeoutMs, DEFAULT_REQUEST_TIMEOUT_MS) : DEFAULT_DOWNLOAD_TIMEOUT_MS,
|
|
731
|
+
asBytes: true
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function filenameFromDisposition(value) {
|
|
735
|
+
if (typeof value !== "string" || value === "") return null;
|
|
736
|
+
const star = /filename\*=(?:UTF-8|utf-8)''([^;]+)/.exec(value);
|
|
737
|
+
if (star !== null) {
|
|
738
|
+
try {
|
|
739
|
+
const decoded = decodeURIComponent(star[1]);
|
|
740
|
+
if (decoded !== "") return decoded;
|
|
741
|
+
} catch {
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const plain = /filename="?([^";]+)"?/.exec(value);
|
|
745
|
+
return plain !== null ? plain[1] : null;
|
|
746
|
+
}
|
|
747
|
+
function required(report) {
|
|
748
|
+
return (value) => {
|
|
749
|
+
if (typeof value !== "string" || value === "") {
|
|
750
|
+
report.push("\u5B57\u6BB5\u5F62\u72B6\u4E0D\u5BF9");
|
|
751
|
+
return "";
|
|
752
|
+
}
|
|
753
|
+
return value;
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function parseBalance(body) {
|
|
757
|
+
const raw = isRecord2(body) ? body.balance : void 0;
|
|
758
|
+
if (!isRecord2(raw)) return null;
|
|
759
|
+
const problems = [];
|
|
760
|
+
const text = required(problems);
|
|
761
|
+
const currency = text(raw.currency);
|
|
762
|
+
const updatedAt = text(raw.updatedAt);
|
|
763
|
+
const available = numberOrUndefined(raw.available);
|
|
764
|
+
const held = numberOrUndefined(raw.held);
|
|
765
|
+
const spent = numberOrUndefined(raw.spent);
|
|
766
|
+
if (problems.length > 0 || available === void 0 || held === void 0 || spent === void 0) return null;
|
|
767
|
+
return { currency, available, held, spent, updatedAt };
|
|
768
|
+
}
|
|
769
|
+
function parseProductImages(value) {
|
|
770
|
+
if (!Array.isArray(value)) return null;
|
|
771
|
+
const images = [];
|
|
772
|
+
for (const item of value) {
|
|
773
|
+
if (!isRecord2(item)) return null;
|
|
774
|
+
const problems = [];
|
|
775
|
+
const text = required(problems);
|
|
776
|
+
const image = {
|
|
777
|
+
id: text(item.id),
|
|
778
|
+
angle: typeof item.angle === "string" ? item.angle : "",
|
|
779
|
+
url: text(item.url),
|
|
780
|
+
mime: text(item.mime),
|
|
781
|
+
bytes: numberOrUndefined(item.bytes) ?? -1,
|
|
782
|
+
sha256: text(item.sha256)
|
|
783
|
+
};
|
|
784
|
+
if (problems.length > 0 || image.bytes < 0) return null;
|
|
785
|
+
images.push(image);
|
|
786
|
+
}
|
|
787
|
+
return images;
|
|
788
|
+
}
|
|
789
|
+
function parseProduct(body) {
|
|
790
|
+
const product = isRecord2(body) ? body.product : void 0;
|
|
791
|
+
if (!isRecord2(product)) return null;
|
|
792
|
+
const problems = [];
|
|
793
|
+
const text = required(problems);
|
|
794
|
+
const images = parseProductImages(product.images);
|
|
795
|
+
const sku = typeof product.sku === "string" ? product.sku : null;
|
|
796
|
+
const parsed = {
|
|
797
|
+
id: text(product.id),
|
|
798
|
+
name: text(product.name),
|
|
799
|
+
category: text(product.category),
|
|
800
|
+
sku,
|
|
801
|
+
createdAt: text(product.createdAt),
|
|
802
|
+
images: images ?? []
|
|
803
|
+
};
|
|
804
|
+
if (problems.length > 0 || images === null) return null;
|
|
805
|
+
return parsed;
|
|
806
|
+
}
|
|
807
|
+
function parseProductLock(body) {
|
|
808
|
+
const lock = isRecord2(body) ? body.productLock : void 0;
|
|
809
|
+
if (!isRecord2(lock)) return null;
|
|
810
|
+
const problems = [];
|
|
811
|
+
const text = required(problems);
|
|
812
|
+
const parsed = { colorName: text(lock.colorName), silhouette: text(lock.silhouette), material: text(lock.material) };
|
|
813
|
+
return problems.length > 0 ? null : parsed;
|
|
814
|
+
}
|
|
815
|
+
function parseCatalogTemplate(item) {
|
|
816
|
+
if (!isRecord2(item)) return null;
|
|
817
|
+
const problems = [];
|
|
818
|
+
const text = required(problems);
|
|
819
|
+
const durationSeconds = numberOrUndefined(item.durationSeconds);
|
|
820
|
+
const needsList = Array.isArray(item.needs) && item.needs.every((need) => typeof need === "string") ? item.needs : null;
|
|
821
|
+
if (needsList === null) problems.push("needs");
|
|
822
|
+
const parsed = {
|
|
823
|
+
id: text(item.id),
|
|
824
|
+
title: text(item.title),
|
|
825
|
+
tier: text(item.tier),
|
|
826
|
+
summary: typeof item.summary === "string" ? item.summary : "",
|
|
827
|
+
durationSeconds: durationSeconds ?? -1,
|
|
828
|
+
needs: needsList ?? [],
|
|
829
|
+
defaultProps: isRecord2(item.defaultProps) ? item.defaultProps : {}
|
|
830
|
+
};
|
|
831
|
+
if (problems.length > 0 || durationSeconds === void 0) return null;
|
|
832
|
+
return parsed;
|
|
833
|
+
}
|
|
834
|
+
function parseCatalog(body) {
|
|
835
|
+
const catalog2 = isRecord2(body) ? body.catalog : void 0;
|
|
836
|
+
if (!isRecord2(catalog2)) return null;
|
|
837
|
+
const category = isRecord2(catalog2.category) ? catalog2.category : null;
|
|
838
|
+
const templates = Array.isArray(catalog2.templates) ? catalog2.templates : null;
|
|
839
|
+
if (!category || templates === null) return null;
|
|
840
|
+
const problems = [];
|
|
841
|
+
const text = required(problems);
|
|
842
|
+
const parsedTemplates = [];
|
|
843
|
+
for (const item of templates) {
|
|
844
|
+
const parsed2 = parseCatalogTemplate(item);
|
|
845
|
+
if (parsed2 === null) problems.push("template");
|
|
846
|
+
else parsedTemplates.push(parsed2);
|
|
847
|
+
}
|
|
848
|
+
const parsed = {
|
|
849
|
+
category: { id: text(category.id), name: text(category.name), badge: text(category.badge) },
|
|
850
|
+
templates: parsedTemplates
|
|
851
|
+
};
|
|
852
|
+
return problems.length > 0 ? null : parsed;
|
|
853
|
+
}
|
|
854
|
+
function parseUpload(body) {
|
|
855
|
+
if (!isRecord2(body)) return null;
|
|
856
|
+
const problems = [];
|
|
857
|
+
const text = required(problems);
|
|
858
|
+
const bytes = numberOrUndefined(body.bytes);
|
|
859
|
+
const parsed = { url: text(body.url), sha256: text(body.sha256), bytes: bytes ?? -1 };
|
|
860
|
+
if (problems.length > 0 || bytes === void 0) return null;
|
|
861
|
+
return parsed;
|
|
862
|
+
}
|
|
863
|
+
function parseQuoteValue(value) {
|
|
864
|
+
if (!isRecord2(value)) return null;
|
|
865
|
+
const problems = [];
|
|
866
|
+
const text = required(problems);
|
|
867
|
+
const numbers = ["durationMilliseconds", "memberMilliseconds", "excessMilliseconds", "credits"].map(
|
|
868
|
+
(field) => numberOrUndefined(value[field])
|
|
869
|
+
);
|
|
870
|
+
if (problems.length > 0 || numbers.some((entry) => entry === void 0)) return null;
|
|
871
|
+
const [durationMilliseconds, memberMilliseconds, excessMilliseconds, credits] = numbers;
|
|
872
|
+
return {
|
|
873
|
+
quoteHash: text(value.quoteHash),
|
|
874
|
+
templateId: text(value.templateId),
|
|
875
|
+
durationMilliseconds,
|
|
876
|
+
memberMilliseconds,
|
|
877
|
+
excessMilliseconds,
|
|
878
|
+
credits
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function parseQuote(body) {
|
|
882
|
+
return parseQuoteValue(isRecord2(body) ? body.quote : void 0);
|
|
883
|
+
}
|
|
884
|
+
function parseOrder(body) {
|
|
885
|
+
const order2 = isRecord2(body) ? body.order : void 0;
|
|
886
|
+
if (!isRecord2(order2)) return null;
|
|
887
|
+
const problems = [];
|
|
888
|
+
const text = required(problems);
|
|
889
|
+
const durationSeconds = numberOrUndefined(order2.durationSeconds);
|
|
890
|
+
const bytes = numberOrUndefined(order2.bytes);
|
|
891
|
+
const quoteValue = order2.quote === void 0 ? void 0 : parseQuoteValue(order2.quote);
|
|
892
|
+
if (order2.quote !== void 0 && quoteValue === null) problems.push("quote");
|
|
893
|
+
const parsed = {
|
|
894
|
+
id: text(order2.id),
|
|
895
|
+
status: text(order2.status),
|
|
896
|
+
templateId: text(order2.templateId),
|
|
897
|
+
templateTitle: text(order2.templateTitle),
|
|
898
|
+
createdAt: text(order2.createdAt),
|
|
899
|
+
...typeof order2.finishedAt === "string" ? { finishedAt: order2.finishedAt } : {},
|
|
900
|
+
...durationSeconds !== void 0 ? { durationSeconds } : {},
|
|
901
|
+
...typeof order2.error === "string" ? { error: order2.error } : {},
|
|
902
|
+
...typeof order2.videoUrl === "string" ? { videoUrl: order2.videoUrl } : {},
|
|
903
|
+
...bytes !== void 0 ? { bytes } : {},
|
|
904
|
+
...quoteValue !== void 0 && quoteValue !== null ? { quote: quoteValue } : {}
|
|
905
|
+
};
|
|
906
|
+
return problems.length > 0 ? null : parsed;
|
|
907
|
+
}
|
|
908
|
+
function parseDramaMoney(value) {
|
|
909
|
+
if (!isRecord2(value)) return null;
|
|
910
|
+
const problems = [];
|
|
911
|
+
const text = required(problems);
|
|
912
|
+
const numbers = ["held", "settled", "released", "refunded"].map((field) => numberOrUndefined(value[field]));
|
|
913
|
+
if (numbers.some((entry) => entry === void 0)) return null;
|
|
914
|
+
const [held, settled, released, refunded] = numbers;
|
|
915
|
+
const currency = text(value.currency);
|
|
916
|
+
return problems.length > 0 ? null : { currency, held, settled, released, refunded };
|
|
917
|
+
}
|
|
918
|
+
function parseDramaGap(value) {
|
|
919
|
+
if (!isRecord2(value)) return null;
|
|
920
|
+
const problems = [];
|
|
921
|
+
const text = required(problems);
|
|
922
|
+
const sceneNos = Array.isArray(value.sceneNos) && value.sceneNos.every((no) => numberOrUndefined(no) !== void 0) ? value.sceneNos : null;
|
|
923
|
+
if (sceneNos === null) problems.push("sceneNos");
|
|
924
|
+
const label = typeof value.label === "string" ? value.label : "";
|
|
925
|
+
const parsed = { kind: text(value.kind), key: text(value.key), label, sceneNos: sceneNos ?? [] };
|
|
926
|
+
return problems.length > 0 ? null : parsed;
|
|
927
|
+
}
|
|
928
|
+
function parseDramaOrder(body) {
|
|
929
|
+
const order2 = isRecord2(body) ? body.order : void 0;
|
|
930
|
+
if (!isRecord2(order2)) return null;
|
|
931
|
+
const problems = [];
|
|
932
|
+
const text = required(problems);
|
|
933
|
+
const money = parseDramaMoney(order2.money);
|
|
934
|
+
if (money === null) problems.push("money");
|
|
935
|
+
const actionsValue = Array.isArray(order2.nextActions) ? order2.nextActions : null;
|
|
936
|
+
const actions = actionsValue === null ? [] : actionsValue.filter((action) => typeof action === "string");
|
|
937
|
+
if (actionsValue === null || actions.length !== actionsValue.length) problems.push("nextActions");
|
|
938
|
+
const gapsValue = order2.gaps === void 0 ? void 0 : Array.isArray(order2.gaps) ? order2.gaps : null;
|
|
939
|
+
if (order2.gaps !== void 0 && gapsValue === null) problems.push("gaps");
|
|
940
|
+
const gaps = gapsValue === void 0 || gapsValue === null ? void 0 : gapsValue.map((item) => {
|
|
941
|
+
const parsed2 = parseDramaGap(item);
|
|
942
|
+
if (parsed2 === null) problems.push("gap");
|
|
943
|
+
return parsed2;
|
|
944
|
+
});
|
|
945
|
+
if (gaps !== void 0 && gaps.some((gap) => gap === null)) problems.push("gap");
|
|
946
|
+
const parsed = {
|
|
947
|
+
id: text(order2.id),
|
|
948
|
+
projectId: text(order2.projectId),
|
|
949
|
+
status: text(order2.status),
|
|
950
|
+
productId: text(order2.productId),
|
|
951
|
+
genreId: text(order2.genreId),
|
|
952
|
+
durationKey: text(order2.durationKey),
|
|
953
|
+
videoModel: typeof order2.videoModel === "string" ? order2.videoModel : null,
|
|
954
|
+
resolution: typeof order2.resolution === "string" ? order2.resolution : null,
|
|
955
|
+
money: money ?? { currency: "", held: 0, settled: 0, released: 0, refunded: 0 },
|
|
956
|
+
nextActions: actions,
|
|
957
|
+
...typeof order2.reason === "string" ? { reason: order2.reason } : {},
|
|
958
|
+
...gaps === void 0 ? {} : { gaps },
|
|
959
|
+
createdAt: text(order2.createdAt),
|
|
960
|
+
updatedAt: text(order2.updatedAt)
|
|
961
|
+
};
|
|
962
|
+
return problems.length > 0 ? null : parsed;
|
|
963
|
+
}
|
|
964
|
+
function parseDramaQuote(body) {
|
|
965
|
+
if (!isRecord2(body)) return null;
|
|
966
|
+
const problems = [];
|
|
967
|
+
const text = required(problems);
|
|
968
|
+
const quote3 = isRecord2(body.quote) ? body.quote : null;
|
|
969
|
+
if (quote3 === null) problems.push("quote");
|
|
970
|
+
const totalCredits = quote3 === null ? void 0 : numberOrUndefined(quote3.totalCredits);
|
|
971
|
+
const shotCount = quote3 === null ? void 0 : numberOrUndefined(quote3.shotCount);
|
|
972
|
+
if (totalCredits === void 0 || shotCount === void 0) problems.push("quote");
|
|
973
|
+
const shotCreditCost = quote3 === null ? void 0 : numberOrUndefined(quote3.shotCreditCost);
|
|
974
|
+
const videoModel = quote3 !== null && typeof quote3.videoModel === "string" ? quote3.videoModel : void 0;
|
|
975
|
+
const resolution = quote3 !== null && typeof quote3.resolution === "string" ? quote3.resolution : void 0;
|
|
976
|
+
const parsed = {
|
|
977
|
+
quoteHash: text(body.quoteHash),
|
|
978
|
+
sufficient: typeof body.sufficient === "boolean" ? body.sufficient : null,
|
|
979
|
+
totalCredits: totalCredits ?? -1,
|
|
980
|
+
shotCount: shotCount ?? -1,
|
|
981
|
+
...shotCreditCost !== void 0 ? { shotCreditCost } : {},
|
|
982
|
+
...videoModel !== void 0 ? { videoModel } : {},
|
|
983
|
+
...resolution !== void 0 ? { resolution } : {}
|
|
984
|
+
};
|
|
985
|
+
return problems.length > 0 ? null : parsed;
|
|
986
|
+
}
|
|
987
|
+
function parseRunAction(value) {
|
|
988
|
+
if (!isRecord2(value) || typeof value.type !== "string" || value.type === "") return null;
|
|
989
|
+
return {
|
|
990
|
+
type: value.type,
|
|
991
|
+
...numberOrUndefined(value.credits) !== void 0 ? { credits: numberOrUndefined(value.credits) } : {},
|
|
992
|
+
...typeof value.confirmKind === "string" ? { confirmKind: value.confirmKind } : {},
|
|
993
|
+
...value.confirmation !== void 0 ? { confirmation: value.confirmation } : {}
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
function parseRun(body) {
|
|
997
|
+
const run2 = isRecord2(body) ? body.run : void 0;
|
|
998
|
+
if (!isRecord2(run2)) return null;
|
|
999
|
+
const problems = [];
|
|
1000
|
+
const text = required(problems);
|
|
1001
|
+
const version2 = numberOrUndefined(run2.version);
|
|
1002
|
+
const pollAfterMs = numberOrUndefined(run2.pollAfterMs);
|
|
1003
|
+
const product = isRecord2(run2.product) ? run2.product : null;
|
|
1004
|
+
if (!product) problems.push("product");
|
|
1005
|
+
const actions = Array.isArray(run2.nextActions) ? run2.nextActions : null;
|
|
1006
|
+
if (actions === null) problems.push("nextActions");
|
|
1007
|
+
const messagesValue = Array.isArray(run2.messages) ? run2.messages : null;
|
|
1008
|
+
if (messagesValue === null) problems.push("messages");
|
|
1009
|
+
const messages = messagesValue === null ? [] : messagesValue.map((message2) => {
|
|
1010
|
+
if (!isRecord2(message2) || typeof message2.text !== "string") {
|
|
1011
|
+
problems.push("message");
|
|
1012
|
+
return { role: "", text: "" };
|
|
1013
|
+
}
|
|
1014
|
+
return { role: typeof message2.role === "string" ? message2.role : "", text: message2.text };
|
|
1015
|
+
});
|
|
1016
|
+
const failureValue = isRecord2(run2.failure) ? run2.failure : null;
|
|
1017
|
+
const failure = failureValue ? {
|
|
1018
|
+
code: text(failureValue.code),
|
|
1019
|
+
title: text(failureValue.title),
|
|
1020
|
+
detail: typeof failureValue.detail === "string" ? failureValue.detail : "",
|
|
1021
|
+
retryable: failureValue.retryable === true
|
|
1022
|
+
} : null;
|
|
1023
|
+
const currentProductionCredits = numberOrUndefined(run2.currentProductionCredits);
|
|
1024
|
+
const parsed = {
|
|
1025
|
+
rootRunId: text(run2.rootRunId),
|
|
1026
|
+
version: version2 ?? -1,
|
|
1027
|
+
status: text(run2.status),
|
|
1028
|
+
stage: text(run2.stage),
|
|
1029
|
+
label: text(run2.label),
|
|
1030
|
+
updatedAt: text(run2.updatedAt),
|
|
1031
|
+
pollAfterMs: pollAfterMs ?? null,
|
|
1032
|
+
productionConfirmed: run2.productionConfirmed === true,
|
|
1033
|
+
...currentProductionCredits !== void 0 ? { currentProductionCredits } : {},
|
|
1034
|
+
product: product ? { id: text(product.id), name: text(product.name) } : { id: "", name: "" },
|
|
1035
|
+
nextActions: actions === null ? [] : actions.map((action) => {
|
|
1036
|
+
const parsed2 = parseRunAction(action);
|
|
1037
|
+
if (parsed2 === null) problems.push("action");
|
|
1038
|
+
return parsed2 ?? { type: "" };
|
|
1039
|
+
}),
|
|
1040
|
+
messages,
|
|
1041
|
+
failure
|
|
1042
|
+
};
|
|
1043
|
+
if (problems.length > 0 || version2 === void 0) return null;
|
|
1044
|
+
return parsed;
|
|
1045
|
+
}
|
|
594
1046
|
function parseMe(body) {
|
|
595
1047
|
if (!isRecord2(body)) return null;
|
|
596
1048
|
const { key, account, workspace } = body;
|
|
@@ -613,6 +1065,280 @@ async function getMe(baseUrl, apiKey, deps) {
|
|
|
613
1065
|
if (!me) throw unreadableResponse(reply.status);
|
|
614
1066
|
return me;
|
|
615
1067
|
}
|
|
1068
|
+
async function getBalance(baseUrl, apiKey, deps) {
|
|
1069
|
+
const reply = await getJson(baseUrl, apiKey, "/api/open/v1/balance", deps);
|
|
1070
|
+
const balance2 = parseBalance(reply.body);
|
|
1071
|
+
if (!balance2 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1072
|
+
return { view: balance2, raw: reply.body };
|
|
1073
|
+
}
|
|
1074
|
+
async function createProduct(baseUrl, apiKey, name2, category, idempotencyKey, deps) {
|
|
1075
|
+
const reply = await postJson(
|
|
1076
|
+
baseUrl,
|
|
1077
|
+
apiKey,
|
|
1078
|
+
"/api/open/v1/products",
|
|
1079
|
+
{ name: name2, ...category === void 0 ? {} : { category } },
|
|
1080
|
+
idempotencyKey,
|
|
1081
|
+
deps
|
|
1082
|
+
);
|
|
1083
|
+
const product = parseProduct(reply.body);
|
|
1084
|
+
if (!product || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1085
|
+
return { view: product, raw: reply.body, replayed: reply.status === 200 };
|
|
1086
|
+
}
|
|
1087
|
+
async function putProductImage(baseUrl, apiKey, productId, angle, bytes, contentType, idempotencyKey, deps) {
|
|
1088
|
+
const reply = await putRaw(
|
|
1089
|
+
baseUrl,
|
|
1090
|
+
apiKey,
|
|
1091
|
+
`/api/open/v1/products/${encodeURIComponent(productId)}/images?angle=${angle}`,
|
|
1092
|
+
bytes,
|
|
1093
|
+
contentType,
|
|
1094
|
+
idempotencyKey,
|
|
1095
|
+
deps
|
|
1096
|
+
);
|
|
1097
|
+
const product = parseProduct(reply.body);
|
|
1098
|
+
if (!product || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1099
|
+
return { view: product, raw: reply.body, replayed: reply.status === 200 };
|
|
1100
|
+
}
|
|
1101
|
+
async function confirmProductLock(baseUrl, apiKey, productId, lock, idempotencyKey, deps) {
|
|
1102
|
+
const reply = await postJson(
|
|
1103
|
+
baseUrl,
|
|
1104
|
+
apiKey,
|
|
1105
|
+
`/api/open/v1/products/${encodeURIComponent(productId)}/lock/confirm`,
|
|
1106
|
+
{ productLock: lock },
|
|
1107
|
+
idempotencyKey,
|
|
1108
|
+
deps
|
|
1109
|
+
);
|
|
1110
|
+
const productLock = parseProductLock(reply.body);
|
|
1111
|
+
const product = parseProduct(reply.body);
|
|
1112
|
+
if (!productLock || !product || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1113
|
+
return { view: { productLock, product }, raw: reply.body };
|
|
1114
|
+
}
|
|
1115
|
+
async function getTemplateShortCatalog(baseUrl, apiKey, deps) {
|
|
1116
|
+
const reply = await getJson(baseUrl, apiKey, "/api/open/v1/template-shorts", deps);
|
|
1117
|
+
const catalog2 = parseCatalog(reply.body);
|
|
1118
|
+
if (!catalog2 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1119
|
+
return { view: catalog2, raw: reply.body };
|
|
1120
|
+
}
|
|
1121
|
+
async function uploadTemplateShortAsset(baseUrl, apiKey, bytes, contentType, idempotencyKey, deps) {
|
|
1122
|
+
const reply = await postRaw(baseUrl, apiKey, "/api/open/v1/template-shorts/uploads", bytes, contentType, idempotencyKey, deps);
|
|
1123
|
+
const upload2 = parseUpload(reply.body);
|
|
1124
|
+
if (!upload2 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1125
|
+
return { view: upload2, raw: reply.body };
|
|
1126
|
+
}
|
|
1127
|
+
async function quoteTemplateShort(baseUrl, apiKey, templateId, props, idempotencyKey, deps) {
|
|
1128
|
+
const reply = await postJson(
|
|
1129
|
+
baseUrl,
|
|
1130
|
+
apiKey,
|
|
1131
|
+
"/api/open/v1/template-shorts/quote",
|
|
1132
|
+
{ templateId, props },
|
|
1133
|
+
idempotencyKey,
|
|
1134
|
+
deps
|
|
1135
|
+
);
|
|
1136
|
+
const quote3 = parseQuote(reply.body);
|
|
1137
|
+
if (!quote3 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1138
|
+
return { view: quote3, raw: reply.body };
|
|
1139
|
+
}
|
|
1140
|
+
async function createTemplateShortOrder(baseUrl, apiKey, templateId, props, expectedQuoteHash, idempotencyKey, deps) {
|
|
1141
|
+
return orderReplyOf(
|
|
1142
|
+
await postJson(
|
|
1143
|
+
baseUrl,
|
|
1144
|
+
apiKey,
|
|
1145
|
+
"/api/open/v1/template-shorts/orders",
|
|
1146
|
+
{ templateId, props, ...expectedQuoteHash === void 0 ? {} : { expectedQuoteHash } },
|
|
1147
|
+
idempotencyKey,
|
|
1148
|
+
deps
|
|
1149
|
+
)
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
async function getTemplateShortOrder(baseUrl, apiKey, orderId, deps) {
|
|
1153
|
+
return orderReplyOf(
|
|
1154
|
+
await getJson(baseUrl, apiKey, `/api/open/v1/template-shorts/orders/${encodeURIComponent(orderId)}`, deps)
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
async function cancelTemplateShortOrder(baseUrl, apiKey, orderId, idempotencyKey, deps) {
|
|
1158
|
+
return orderReplyOf(
|
|
1159
|
+
await postJson(
|
|
1160
|
+
baseUrl,
|
|
1161
|
+
apiKey,
|
|
1162
|
+
`/api/open/v1/template-shorts/orders/${encodeURIComponent(orderId)}/cancel`,
|
|
1163
|
+
{},
|
|
1164
|
+
idempotencyKey,
|
|
1165
|
+
deps
|
|
1166
|
+
)
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
async function orderReplyOf(reply) {
|
|
1170
|
+
const order2 = parseOrder(reply.body);
|
|
1171
|
+
if (!order2 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1172
|
+
return { view: order2, raw: reply.body };
|
|
1173
|
+
}
|
|
1174
|
+
async function downloadTemplateShort(baseUrl, apiKey, orderId, deps) {
|
|
1175
|
+
return downloadFile(baseUrl, apiKey, `/api/open/v1/template-shorts/orders/${encodeURIComponent(orderId)}/download`, deps);
|
|
1176
|
+
}
|
|
1177
|
+
async function runReplyOf(reply) {
|
|
1178
|
+
const run2 = parseRun(reply.body);
|
|
1179
|
+
if (!run2 || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1180
|
+
return { view: run2, raw: reply.body };
|
|
1181
|
+
}
|
|
1182
|
+
async function createVideoRun(baseUrl, apiKey, body, idempotencyKey, deps) {
|
|
1183
|
+
return runReplyOf(await postJson(baseUrl, apiKey, "/api/open/v1/videos", body, idempotencyKey, deps));
|
|
1184
|
+
}
|
|
1185
|
+
async function getVideoRun(baseUrl, apiKey, runId, deps) {
|
|
1186
|
+
return runReplyOf(await getJson(baseUrl, apiKey, `/api/open/v1/videos/${encodeURIComponent(runId)}`, deps));
|
|
1187
|
+
}
|
|
1188
|
+
async function postVideoMessage(baseUrl, apiKey, runId, text, idempotencyKey, deps) {
|
|
1189
|
+
return runReplyOf(
|
|
1190
|
+
await postJson(
|
|
1191
|
+
baseUrl,
|
|
1192
|
+
apiKey,
|
|
1193
|
+
`/api/open/v1/videos/${encodeURIComponent(runId)}/messages`,
|
|
1194
|
+
{ text },
|
|
1195
|
+
idempotencyKey,
|
|
1196
|
+
deps
|
|
1197
|
+
)
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
async function confirmVideoProduction(baseUrl, apiKey, runId, expectedVersion, confirmation, idempotencyKey, deps) {
|
|
1201
|
+
return runReplyOf(
|
|
1202
|
+
await postJson(
|
|
1203
|
+
baseUrl,
|
|
1204
|
+
apiKey,
|
|
1205
|
+
`/api/open/v1/videos/${encodeURIComponent(runId)}/confirm`,
|
|
1206
|
+
{ expectedVersion, confirmation },
|
|
1207
|
+
idempotencyKey,
|
|
1208
|
+
deps
|
|
1209
|
+
)
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
async function downloadVideo(baseUrl, apiKey, runId, deps) {
|
|
1213
|
+
return downloadFile(baseUrl, apiKey, `/api/open/v1/videos/${encodeURIComponent(runId)}/download`, deps);
|
|
1214
|
+
}
|
|
1215
|
+
async function createDramaOrder(baseUrl, apiKey, body, idempotencyKey, deps) {
|
|
1216
|
+
const reply = await postJson(baseUrl, apiKey, "/api/open/v1/drama/orders", body, idempotencyKey, deps);
|
|
1217
|
+
const view = parseDramaOrder(reply.body);
|
|
1218
|
+
if (!view || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1219
|
+
return { view, raw: reply.body, replayed: reply.status === 200 };
|
|
1220
|
+
}
|
|
1221
|
+
async function getDramaOrder(baseUrl, apiKey, orderId, deps) {
|
|
1222
|
+
const reply = await getJson(baseUrl, apiKey, `/api/open/v1/drama/orders/${encodeURIComponent(orderId)}`, deps);
|
|
1223
|
+
const view = parseDramaOrder(reply.body);
|
|
1224
|
+
if (!view || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1225
|
+
return { view, raw: reply.body, replayed: false };
|
|
1226
|
+
}
|
|
1227
|
+
async function getDramaQuote(baseUrl, apiKey, orderId, deps) {
|
|
1228
|
+
const reply = await getJson(baseUrl, apiKey, `/api/open/v1/drama/orders/${encodeURIComponent(orderId)}/quote`, deps);
|
|
1229
|
+
const view = parseDramaQuote(reply.body);
|
|
1230
|
+
if (!view || !isRecord2(reply.body)) throw unreadableResponse(reply.status);
|
|
1231
|
+
return { view, raw: reply.body };
|
|
1232
|
+
}
|
|
1233
|
+
async function confirmDramaOrder(baseUrl, apiKey, orderId, expectedQuoteHash, idempotencyKey, deps) {
|
|
1234
|
+
const reply = await postJson(
|
|
1235
|
+
baseUrl,
|
|
1236
|
+
apiKey,
|
|
1237
|
+
`/api/open/v1/drama/orders/${encodeURIComponent(orderId)}/confirm`,
|
|
1238
|
+
{ expectedQuoteHash },
|
|
1239
|
+
idempotencyKey,
|
|
1240
|
+
deps
|
|
1241
|
+
);
|
|
1242
|
+
const view = parseDramaOrder(reply.body);
|
|
1243
|
+
const started = isRecord2(reply.body) ? reply.body.productionStarted : void 0;
|
|
1244
|
+
if (!view || !isRecord2(reply.body) || typeof started !== "boolean") throw unreadableResponse(reply.status);
|
|
1245
|
+
return { view, productionStarted: started, raw: reply.body };
|
|
1246
|
+
}
|
|
1247
|
+
async function cancelDramaOrder(baseUrl, apiKey, orderId, idempotencyKey, deps) {
|
|
1248
|
+
const reply = await postJson(
|
|
1249
|
+
baseUrl,
|
|
1250
|
+
apiKey,
|
|
1251
|
+
`/api/open/v1/drama/orders/${encodeURIComponent(orderId)}/cancel`,
|
|
1252
|
+
{},
|
|
1253
|
+
idempotencyKey,
|
|
1254
|
+
deps
|
|
1255
|
+
);
|
|
1256
|
+
const view = parseDramaOrder(reply.body);
|
|
1257
|
+
const canceled = isRecord2(reply.body) ? reply.body.canceled : void 0;
|
|
1258
|
+
if (!view || !isRecord2(reply.body) || typeof canceled !== "boolean") throw unreadableResponse(reply.status);
|
|
1259
|
+
return { view, canceled, raw: reply.body };
|
|
1260
|
+
}
|
|
1261
|
+
async function downloadDramaFilm(baseUrl, apiKey, orderId, deps) {
|
|
1262
|
+
return downloadFile(baseUrl, apiKey, `/api/open/v1/drama/orders/${encodeURIComponent(orderId)}/download`, deps);
|
|
1263
|
+
}
|
|
1264
|
+
var DEFAULT_WAIT_TIMEOUT_MS = 6e5;
|
|
1265
|
+
var FIRST_POLL_INTERVAL_MS = 3e3;
|
|
1266
|
+
var MAX_POLL_INTERVAL_MS = 12e3;
|
|
1267
|
+
var ORDER_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled"]);
|
|
1268
|
+
var RUN_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled"]);
|
|
1269
|
+
var RUN_WAITING_FOR_USER_STATUSES = /* @__PURE__ */ new Set(["needs_input", "awaiting_reply", "awaiting_confirmation"]);
|
|
1270
|
+
var DRAMA_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["delivered", "failed", "canceled", "partial"]);
|
|
1271
|
+
var DRAMA_WAITING_FOR_USER_STATUSES = /* @__PURE__ */ new Set(["needs_attention"]);
|
|
1272
|
+
async function awaitTemplateShortOrder(baseUrl, apiKey, start, deps, wait) {
|
|
1273
|
+
let current = start;
|
|
1274
|
+
if (ORDER_TERMINAL_STATUSES.has(current.view.status)) return { value: current, timedOut: false };
|
|
1275
|
+
const deadline = wait.now().getTime() + wait.timeoutMs;
|
|
1276
|
+
let interval = FIRST_POLL_INTERVAL_MS;
|
|
1277
|
+
let said = current.view.status;
|
|
1278
|
+
for (; ; ) {
|
|
1279
|
+
const remaining = deadline - wait.now().getTime();
|
|
1280
|
+
if (remaining <= 0) return { value: current, timedOut: true };
|
|
1281
|
+
current = await retainTaskOnError(() => taskRecovery("shorts", current.view.id, current.view.status, baseUrl), async () => {
|
|
1282
|
+
await wait.sleep(Math.max(Math.min(interval, remaining), 1));
|
|
1283
|
+
interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
|
|
1284
|
+
return getTemplateShortOrder(baseUrl, apiKey, current.view.id, deps);
|
|
1285
|
+
});
|
|
1286
|
+
if (current.view.status !== said) {
|
|
1287
|
+
wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
|
|
1288
|
+
said = current.view.status;
|
|
1289
|
+
}
|
|
1290
|
+
if (ORDER_TERMINAL_STATUSES.has(current.view.status)) return { value: current, timedOut: false };
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
async function awaitVideoRun(baseUrl, apiKey, start, deps, wait) {
|
|
1294
|
+
let current = start;
|
|
1295
|
+
if (runStopped(current.view.status)) return { value: current, timedOut: false };
|
|
1296
|
+
const deadline = wait.now().getTime() + wait.timeoutMs;
|
|
1297
|
+
let interval = FIRST_POLL_INTERVAL_MS;
|
|
1298
|
+
let said = current.view.status;
|
|
1299
|
+
for (; ; ) {
|
|
1300
|
+
const remaining = deadline - wait.now().getTime();
|
|
1301
|
+
if (remaining <= 0) return { value: current, timedOut: true };
|
|
1302
|
+
const preferred = current.view.pollAfterMs !== null && current.view.pollAfterMs > 0 ? current.view.pollAfterMs : interval;
|
|
1303
|
+
current = await retainTaskOnError(() => taskRecovery("videos", current.view.rootRunId, current.view.status, baseUrl, current.view.failure?.code), async () => {
|
|
1304
|
+
await wait.sleep(Math.max(Math.min(preferred, remaining), 1));
|
|
1305
|
+
interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
|
|
1306
|
+
return getVideoRun(baseUrl, apiKey, current.view.rootRunId, deps);
|
|
1307
|
+
});
|
|
1308
|
+
if (current.view.status !== said) {
|
|
1309
|
+
wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
|
|
1310
|
+
said = current.view.status;
|
|
1311
|
+
}
|
|
1312
|
+
if (runStopped(current.view.status)) return { value: current, timedOut: false };
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
function runStopped(status4) {
|
|
1316
|
+
return RUN_TERMINAL_STATUSES.has(status4) || RUN_WAITING_FOR_USER_STATUSES.has(status4);
|
|
1317
|
+
}
|
|
1318
|
+
async function awaitDramaOrder(baseUrl, apiKey, start, deps, wait) {
|
|
1319
|
+
let current = start;
|
|
1320
|
+
if (dramaStopped(current.view.status)) return { value: current, timedOut: false };
|
|
1321
|
+
const deadline = wait.now().getTime() + wait.timeoutMs;
|
|
1322
|
+
let interval = FIRST_POLL_INTERVAL_MS;
|
|
1323
|
+
let said = current.view.status;
|
|
1324
|
+
for (; ; ) {
|
|
1325
|
+
const remaining = deadline - wait.now().getTime();
|
|
1326
|
+
if (remaining <= 0) return { value: current, timedOut: true };
|
|
1327
|
+
current = await retainTaskOnError(() => taskRecovery("drama", current.view.id, current.view.status, baseUrl), async () => {
|
|
1328
|
+
await wait.sleep(Math.max(Math.min(interval, remaining), 1));
|
|
1329
|
+
interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
|
|
1330
|
+
return getDramaOrder(baseUrl, apiKey, current.view.id, deps);
|
|
1331
|
+
});
|
|
1332
|
+
if (current.view.status !== said) {
|
|
1333
|
+
wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
|
|
1334
|
+
said = current.view.status;
|
|
1335
|
+
}
|
|
1336
|
+
if (dramaStopped(current.view.status)) return { value: current, timedOut: false };
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
function dramaStopped(status4) {
|
|
1340
|
+
return DRAMA_TERMINAL_STATUSES.has(status4) || DRAMA_WAITING_FOR_USER_STATUSES.has(status4);
|
|
1341
|
+
}
|
|
616
1342
|
|
|
617
1343
|
// src/credentials.ts
|
|
618
1344
|
import { randomBytes } from "node:crypto";
|
|
@@ -636,8 +1362,8 @@ function nonEmpty3(value) {
|
|
|
636
1362
|
function absoluteOnly(value) {
|
|
637
1363
|
return value !== void 0 && isAbsolute(value) ? value : void 0;
|
|
638
1364
|
}
|
|
639
|
-
function configDirError(
|
|
640
|
-
return new CliError({ code: "CLI_CONFIG_DIR_INVALID", exitCode: EXIT.USAGE, message });
|
|
1365
|
+
function configDirError(message2) {
|
|
1366
|
+
return new CliError({ code: "CLI_CONFIG_DIR_INVALID", exitCode: EXIT.USAGE, message: message2 });
|
|
641
1367
|
}
|
|
642
1368
|
function expandHome(value, io) {
|
|
643
1369
|
if (value === "~") return io.homedir();
|
|
@@ -865,6 +1591,616 @@ async function deleteCredentials(io, warn = SILENT) {
|
|
|
865
1591
|
return removed;
|
|
866
1592
|
}
|
|
867
1593
|
|
|
1594
|
+
// src/resolve.ts
|
|
1595
|
+
async function resolveApiKey({ io, out, baseUrl: flag }) {
|
|
1596
|
+
const explicit = explicitBaseUrl(flag, io.env);
|
|
1597
|
+
const fromEnv = keyFromEnv(io.env);
|
|
1598
|
+
if (fromEnv !== null) {
|
|
1599
|
+
assertKeyFormat(fromEnv);
|
|
1600
|
+
out.protect(fromEnv);
|
|
1601
|
+
return { apiKey: fromEnv, baseUrl: explicit ?? DEFAULT_BASE_URL, source: "env" };
|
|
1602
|
+
}
|
|
1603
|
+
const stored = await readCredentials(io, (line) => out.notice(line));
|
|
1604
|
+
if (!stored) {
|
|
1605
|
+
throw new CliError({
|
|
1606
|
+
code: "CLI_NOT_LOGGED_IN",
|
|
1607
|
+
exitCode: EXIT.AUTH,
|
|
1608
|
+
message: "\u8FD8\u6CA1\u6709\u767B\u5F55\u3002\u5148\u8FD0\u884C buke login\uFF0C\u6216\u8005\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF BUKE_API_KEY"
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
out.protect(stored.apiKey);
|
|
1612
|
+
if (explicit !== null && explicit !== stored.baseUrl) {
|
|
1613
|
+
throw new CliError({
|
|
1614
|
+
code: "CLI_BASE_URL_MISMATCH",
|
|
1615
|
+
exitCode: EXIT.USAGE,
|
|
1616
|
+
message: `\u672C\u673A\u4FDD\u5B58\u7684\u5BC6\u94A5\u662F\u7ED9 ${stored.baseUrl} \u7528\u7684\uFF0C\u4E0D\u4F1A\u53D1\u5230 ${explicit}\u3002\u8981\u6362\u5730\u5740\u8BF7\u91CD\u65B0\u8FD0\u884C buke login --base-url ${explicit}`
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
return { apiKey: stored.apiKey, baseUrl: stored.baseUrl, source: "file", credentialsPath: credentialsPath(io) };
|
|
1620
|
+
}
|
|
1621
|
+
function keySourceLine(resolved) {
|
|
1622
|
+
return resolved.source === "env" ? "\u5BC6\u94A5\u6765\u81EA\uFF1A\u73AF\u5883\u53D8\u91CF BUKE_API_KEY" : `\u5BC6\u94A5\u6765\u81EA\uFF1A${resolved.credentialsPath}`;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// src/commands/balance.ts
|
|
1626
|
+
async function balance(context) {
|
|
1627
|
+
const { io, out } = context;
|
|
1628
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1629
|
+
const { view, raw } = await getBalance(baseUrl, apiKey, io);
|
|
1630
|
+
out.result(
|
|
1631
|
+
[
|
|
1632
|
+
`\u53EF\u7528\u4F59\u989D\uFF1A${view.available} ${singleLine(view.currency)}`,
|
|
1633
|
+
`\u51BB\u7ED3\u4E2D\uFF1A${view.held} ${singleLine(view.currency)}`,
|
|
1634
|
+
`\u5DF2\u652F\u51FA\uFF1A${view.spent} ${singleLine(view.currency)}`,
|
|
1635
|
+
`\u66F4\u65B0\u65F6\u95F4\uFF1A${singleLine(view.updatedAt)}`
|
|
1636
|
+
],
|
|
1637
|
+
{ balance: raw.balance }
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// src/commands/shorts.ts
|
|
1642
|
+
import { createHash } from "node:crypto";
|
|
1643
|
+
import { writeFile } from "node:fs/promises";
|
|
1644
|
+
|
|
1645
|
+
// src/commands/shared.ts
|
|
1646
|
+
import { randomUUID } from "node:crypto";
|
|
1647
|
+
import { readFile } from "node:fs/promises";
|
|
1648
|
+
var MAX_PROPS_BYTES = 1024 * 1024;
|
|
1649
|
+
var MAX_IMAGE_BYTES = 64 * 1024 * 1024;
|
|
1650
|
+
function idempotencyKeyOf(values) {
|
|
1651
|
+
const supplied = values["idempotency-key"];
|
|
1652
|
+
if (typeof supplied === "string" && supplied.trim() !== "") return supplied.trim();
|
|
1653
|
+
return randomUUID();
|
|
1654
|
+
}
|
|
1655
|
+
function textOption(values, name2) {
|
|
1656
|
+
const value = values[name2];
|
|
1657
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
1658
|
+
}
|
|
1659
|
+
function timeoutSecondsOf(values, fallbackSeconds) {
|
|
1660
|
+
const raw = values.timeout;
|
|
1661
|
+
if (raw === void 0) return fallbackSeconds;
|
|
1662
|
+
if (typeof raw !== "string") return fallbackSeconds;
|
|
1663
|
+
const parsed = Number(raw);
|
|
1664
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
1665
|
+
throw usageError(`--timeout \u8981\u662F\u6B63\u6574\u6570\u79D2\u6570\uFF0C\u5199\u7684\u662F ${raw}`);
|
|
1666
|
+
}
|
|
1667
|
+
return parsed;
|
|
1668
|
+
}
|
|
1669
|
+
async function readPropsArgument(io, ref, what) {
|
|
1670
|
+
if (ref === void 0) {
|
|
1671
|
+
throw usageError("\u8981\u7ED9 --props\uFF1A\u4E00\u4E2A JSON \u6587\u4EF6\uFF0C\u6216 - \u8868\u793A\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB");
|
|
1672
|
+
}
|
|
1673
|
+
let text;
|
|
1674
|
+
if (ref === "-") {
|
|
1675
|
+
text = await readStdinAll(io, MAX_PROPS_BYTES, what);
|
|
1676
|
+
} else {
|
|
1677
|
+
let bytes;
|
|
1678
|
+
try {
|
|
1679
|
+
bytes = await readFile(ref);
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
throw new CliError({
|
|
1682
|
+
code: "CLI_USAGE",
|
|
1683
|
+
exitCode: EXIT.USAGE,
|
|
1684
|
+
message: `${what}\u6587\u4EF6\u8BFB\u4E0D\u4E86\uFF1A${ref}\uFF08${briefReason(error)}\uFF09`
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
if (bytes.byteLength > MAX_PROPS_BYTES) {
|
|
1688
|
+
throw new CliError({ code: "CLI_USAGE", exitCode: EXIT.USAGE, message: `${what}\u6587\u4EF6\u592A\u5927\uFF08\u8D85\u8FC7 1 MiB\uFF09\uFF1A${ref}` });
|
|
1689
|
+
}
|
|
1690
|
+
text = bytes.toString("utf8");
|
|
1691
|
+
}
|
|
1692
|
+
let parsed;
|
|
1693
|
+
try {
|
|
1694
|
+
parsed = JSON.parse(text);
|
|
1695
|
+
} catch {
|
|
1696
|
+
throw usageError(`${what}\u4E0D\u662F\u5408\u6CD5\u7684 JSON`);
|
|
1697
|
+
}
|
|
1698
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1699
|
+
throw usageError(`${what}\u5E94\u662F\u4E00\u4E2A JSON \u5BF9\u8C61`);
|
|
1700
|
+
}
|
|
1701
|
+
return parsed;
|
|
1702
|
+
}
|
|
1703
|
+
async function readStdinAll(io, maxBytes, what) {
|
|
1704
|
+
const stdin = io.stdin;
|
|
1705
|
+
if (stdin.readableEnded === true) return "";
|
|
1706
|
+
const chunks = [];
|
|
1707
|
+
let size = 0;
|
|
1708
|
+
await new Promise((resolve, reject) => {
|
|
1709
|
+
const toBuffer2 = (chunk) => typeof chunk === "string" ? Buffer.from(chunk, "utf8") : Buffer.from(chunk);
|
|
1710
|
+
const onData = (chunk) => {
|
|
1711
|
+
const piece = toBuffer2(chunk);
|
|
1712
|
+
size += piece.byteLength;
|
|
1713
|
+
if (size > maxBytes) {
|
|
1714
|
+
cleanup();
|
|
1715
|
+
reject(usageError(`${what}\u592A\u5927\uFF08\u8D85\u8FC7 1 MiB\uFF09`));
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
chunks.push(piece);
|
|
1719
|
+
};
|
|
1720
|
+
const onEnd = () => {
|
|
1721
|
+
cleanup();
|
|
1722
|
+
resolve();
|
|
1723
|
+
};
|
|
1724
|
+
const onError = (error) => {
|
|
1725
|
+
cleanup();
|
|
1726
|
+
reject(new CliError({ code: "CLI_USAGE", exitCode: EXIT.USAGE, message: `\u6807\u51C6\u8F93\u5165\u8BFB\u4E0D\u4E86\uFF1A${briefReason(error)}` }));
|
|
1727
|
+
};
|
|
1728
|
+
const cleanup = () => {
|
|
1729
|
+
stdin.removeListener("data", onData);
|
|
1730
|
+
stdin.removeListener("end", onEnd);
|
|
1731
|
+
stdin.removeListener("error", onError);
|
|
1732
|
+
stdin.pause();
|
|
1733
|
+
};
|
|
1734
|
+
stdin.on("data", onData);
|
|
1735
|
+
stdin.on("end", onEnd);
|
|
1736
|
+
stdin.on("error", onError);
|
|
1737
|
+
stdin.resume();
|
|
1738
|
+
});
|
|
1739
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1740
|
+
}
|
|
1741
|
+
async function readImageFile(path) {
|
|
1742
|
+
let bytes;
|
|
1743
|
+
try {
|
|
1744
|
+
bytes = await readFile(path);
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
throw new CliError({
|
|
1747
|
+
code: "CLI_USAGE",
|
|
1748
|
+
exitCode: EXIT.USAGE,
|
|
1749
|
+
message: `\u56FE\u7247\u6587\u4EF6\u8BFB\u4E0D\u4E86\uFF1A${path}\uFF08${briefReason(error)}\uFF09`
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
if (bytes.byteLength > MAX_IMAGE_BYTES) {
|
|
1753
|
+
throw new CliError({ code: "CLI_USAGE", exitCode: EXIT.USAGE, message: `\u56FE\u7247\u592A\u5927\uFF08\u8D85\u8FC7 64 MiB\uFF09\uFF1A${path}` });
|
|
1754
|
+
}
|
|
1755
|
+
return { bytes, contentType: sniffImageContentType(bytes) };
|
|
1756
|
+
}
|
|
1757
|
+
function sniffImageContentType(bytes) {
|
|
1758
|
+
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
1759
|
+
if (bytes.length >= 4 && bytes.subarray(0, 4).toString("hex") === "89504e47") return "image/png";
|
|
1760
|
+
if (bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
1761
|
+
return "image/webp";
|
|
1762
|
+
}
|
|
1763
|
+
return void 0;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/commands/shorts.ts
|
|
1767
|
+
var SUBCOMMANDS = "catalog\u3001upload\u3001quote\u3001order\u3001status\u3001download \u548C cancel";
|
|
1768
|
+
async function shorts(context) {
|
|
1769
|
+
const { args } = context;
|
|
1770
|
+
const [subcommand] = args;
|
|
1771
|
+
if (subcommand === void 0) {
|
|
1772
|
+
throw usageError(`shorts \u8981\u8DDF\u4E00\u4E2A\u5B50\u547D\u4EE4\uFF1A${SUBCOMMANDS}`);
|
|
1773
|
+
}
|
|
1774
|
+
if (subcommand === "catalog") return catalog(context);
|
|
1775
|
+
if (subcommand === "upload") return upload(context, args[1]);
|
|
1776
|
+
if (subcommand === "quote") return quote(context);
|
|
1777
|
+
if (subcommand === "order") return order(context);
|
|
1778
|
+
if (subcommand === "status") return status(context, args[1]);
|
|
1779
|
+
if (subcommand === "download") return download(context, args[1]);
|
|
1780
|
+
if (subcommand === "cancel") return cancel(context, args[1]);
|
|
1781
|
+
throw usageError(`\u4E0D\u8BA4\u8BC6\u7684\u5B50\u547D\u4EE4 ${subcommand}\uFF1Ashorts \u7684\u5B50\u547D\u4EE4\u662F ${SUBCOMMANDS}`);
|
|
1782
|
+
}
|
|
1783
|
+
function orderIdOf(raw) {
|
|
1784
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
1785
|
+
throw usageError("\u8981\u7ED9\u8BA2\u5355 id\uFF08shorts order \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
1786
|
+
}
|
|
1787
|
+
return raw.trim();
|
|
1788
|
+
}
|
|
1789
|
+
function templateOf(values) {
|
|
1790
|
+
const templateId = textOption(values, "template");
|
|
1791
|
+
if (templateId === void 0) {
|
|
1792
|
+
throw usageError("\u8981\u7ED9 --template <\u6A21\u677Fid>\uFF08shorts catalog \u5217\u51FA\u7684\uFF09");
|
|
1793
|
+
}
|
|
1794
|
+
return templateId;
|
|
1795
|
+
}
|
|
1796
|
+
function waitControls(io, values, note) {
|
|
1797
|
+
return {
|
|
1798
|
+
timeoutMs: timeoutSecondsOf(values, DEFAULT_WAIT_TIMEOUT_MS / 1e3) * 1e3,
|
|
1799
|
+
now: io.now ?? (() => /* @__PURE__ */ new Date()),
|
|
1800
|
+
sleep: io.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
1801
|
+
note
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
function settleOrder(outcome, baseUrl) {
|
|
1805
|
+
const order2 = outcome.value.view;
|
|
1806
|
+
const recovery = taskRecovery("shorts", order2.id, order2.status, baseUrl);
|
|
1807
|
+
if (outcome.timedOut) {
|
|
1808
|
+
throw new CliError({
|
|
1809
|
+
code: "CLI_WAIT_TIMEOUT",
|
|
1810
|
+
exitCode: EXIT.WAIT_TIMEOUT,
|
|
1811
|
+
message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${order2.status}\uFF1A\u518D\u67E5\u7528 ${recovery.statusCommand}\uFF0C\u7ED9\u8BE5\u67E5\u8BE2\u52A0 --wait --timeout <\u79D2\u6570> \u53EF\u7EE7\u7EED\u7B49\u5F85`,
|
|
1812
|
+
recovery
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
if (order2.status === "failed" || order2.status === "canceled") {
|
|
1816
|
+
throw new CliError({
|
|
1817
|
+
code: "CLI_TASK_FAILED",
|
|
1818
|
+
exitCode: EXIT.TASK_FAILED,
|
|
1819
|
+
message: order2.error === void 0 ? `\u8FD9\u6761\u77ED\u7247${order2.status === "canceled" ? "\u5DF2\u53D6\u6D88" : "\u6CA1\u6709\u6E32\u67D3\u6210\u529F"}` : singleLine(order2.error),
|
|
1820
|
+
recovery
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
return outcome.value;
|
|
1824
|
+
}
|
|
1825
|
+
function orderLines(order2) {
|
|
1826
|
+
return [
|
|
1827
|
+
`\u8BA2\u5355\uFF1A${singleLine(order2.id)}`,
|
|
1828
|
+
`\u72B6\u6001\uFF1A${singleLine(order2.status)}`,
|
|
1829
|
+
`\u6A21\u677F\uFF1A${singleLine(order2.templateTitle)}\uFF08${singleLine(order2.templateId)}\uFF09`,
|
|
1830
|
+
`\u521B\u5EFA\u4E8E\uFF1A${singleLine(order2.createdAt)}`,
|
|
1831
|
+
...order2.durationSeconds !== void 0 ? [`\u65F6\u957F\uFF1A${order2.durationSeconds} \u79D2`] : [],
|
|
1832
|
+
...order2.status === "succeeded" && order2.videoUrl !== void 0 ? [`\u6210\u7247\u5730\u5740\uFF1A${singleLine(order2.videoUrl)}`] : [],
|
|
1833
|
+
...order2.bytes !== void 0 ? [`\u5927\u5C0F\uFF1A${order2.bytes} \u5B57\u8282`] : []
|
|
1834
|
+
];
|
|
1835
|
+
}
|
|
1836
|
+
async function catalog(context) {
|
|
1837
|
+
const { io, out } = context;
|
|
1838
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1839
|
+
const { view, raw } = await getTemplateShortCatalog(baseUrl, apiKey, io);
|
|
1840
|
+
out.result(
|
|
1841
|
+
[
|
|
1842
|
+
`${singleLine(view.category.name)}\uFF08${singleLine(view.category.badge)}\uFF0C\u5171 ${view.templates.length} \u4E2A\u6A21\u677F\uFF09`,
|
|
1843
|
+
...view.templates.map(
|
|
1844
|
+
(template) => `${template.id} ${singleLine(template.title)}\uFF08${singleLine(template.tier)}\uFF0C${template.durationSeconds} \u79D2\uFF09
|
|
1845
|
+
${template.summary}${template.needs.length > 0 ? `\uFF1B\u9700\u8981\uFF1A${template.needs.join("\u3001")}` : ""}`
|
|
1846
|
+
)
|
|
1847
|
+
],
|
|
1848
|
+
// --json 按文档回服务端那份完整的 catalog:slots、defaultProps 都在内。
|
|
1849
|
+
{ catalog: raw.catalog }
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
async function upload(context, rawPath) {
|
|
1853
|
+
const { io, out } = context;
|
|
1854
|
+
if (rawPath === void 0 || rawPath.trim() === "") {
|
|
1855
|
+
throw usageError("\u8981\u7ED9\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84");
|
|
1856
|
+
}
|
|
1857
|
+
const image = await readImageFile(rawPath);
|
|
1858
|
+
const idempotencyKey = idempotencyKeyOf(context.values);
|
|
1859
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1860
|
+
const { view, raw } = await uploadTemplateShortAsset(baseUrl, apiKey, image.bytes, image.contentType, idempotencyKey, io);
|
|
1861
|
+
out.result(
|
|
1862
|
+
[
|
|
1863
|
+
`\u5DF2\u4E0A\u4F20\uFF1A${singleLine(view.url)}`,
|
|
1864
|
+
`\u6821\u9A8C\uFF1Asha256 ${view.sha256}\uFF08${view.bytes} \u5B57\u8282\uFF09`,
|
|
1865
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
1866
|
+
],
|
|
1867
|
+
{ upload: raw, idempotencyKey }
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
async function quote(context) {
|
|
1871
|
+
const { io, out, values } = context;
|
|
1872
|
+
const templateId = templateOf(values);
|
|
1873
|
+
const props = await readPropsArgument(io, textOption(values, "props"), "\u6A21\u677F\u53C2\u6570\uFF08--props\uFF09");
|
|
1874
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
1875
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1876
|
+
const { view, raw } = await quoteTemplateShort(baseUrl, apiKey, templateId, props, idempotencyKey, io);
|
|
1877
|
+
out.result(
|
|
1878
|
+
[
|
|
1879
|
+
`\u6A21\u677F\uFF1A${singleLine(view.templateId)}\uFF0C\u65F6\u957F ${Math.round(view.durationMilliseconds / 100) / 10} \u79D2`,
|
|
1880
|
+
`\u62A5\u4EF7\uFF1A${view.credits} \u5E03\uFF08\u4F1A\u5458\u65F6\u957F\u5185 ${Math.round(view.memberMilliseconds / 100) / 10} \u79D2\uFF0C\u8D85\u51FA ${Math.round(view.excessMilliseconds / 100) / 10} \u79D2\uFF09`,
|
|
1881
|
+
`\u62A5\u4EF7\u54C8\u5E0C\uFF1A${view.quoteHash}`,
|
|
1882
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`,
|
|
1883
|
+
`\u4E0B\u5355\u65F6\u5E26\u4E0A --quote-hash ${view.quoteHash} \u548C\u540C\u4E00\u628A --idempotency-key ${idempotencyKey}\uFF08\u62A5\u4EF7\u54C8\u5E0C\u548C\u5E42\u7B49\u952E\u7ED1\u5B9A\uFF0C\u6362\u4E00\u628A\u5C31\u5BF9\u4E0D\u4E0A\uFF09`
|
|
1884
|
+
],
|
|
1885
|
+
{ quote: raw.quote, idempotencyKey }
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
async function order(context) {
|
|
1889
|
+
const { io, out, values } = context;
|
|
1890
|
+
const templateId = templateOf(values);
|
|
1891
|
+
const props = await readPropsArgument(io, textOption(values, "props"), "\u6A21\u677F\u53C2\u6570\uFF08--props\uFF09");
|
|
1892
|
+
const quoteHash = textOption(values, "quote-hash");
|
|
1893
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
1894
|
+
const shouldWait = values.wait === true;
|
|
1895
|
+
const controls = shouldWait ? waitControls(io, values, (line) => out.notice(line)) : void 0;
|
|
1896
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1897
|
+
const accepted = await retainSubmissionOnError(
|
|
1898
|
+
"shorts",
|
|
1899
|
+
idempotencyKey,
|
|
1900
|
+
baseUrl,
|
|
1901
|
+
() => createTemplateShortOrder(baseUrl, apiKey, templateId, props, quoteHash, idempotencyKey, io)
|
|
1902
|
+
);
|
|
1903
|
+
const final = shouldWait ? await retainTaskOnError(
|
|
1904
|
+
() => ({ ...taskRecovery("shorts", accepted.view.id, accepted.view.status, baseUrl), idempotencyKey }),
|
|
1905
|
+
async () => settleOrder(await awaitTemplateShortOrder(baseUrl, apiKey, accepted, io, controls), baseUrl)
|
|
1906
|
+
) : accepted;
|
|
1907
|
+
out.result([...orderLines(final.view), `\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`], {
|
|
1908
|
+
order: final.raw.order,
|
|
1909
|
+
idempotencyKey,
|
|
1910
|
+
...shouldWait ? { waited: true } : {}
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
async function status(context, rawId) {
|
|
1914
|
+
const { io, out, values } = context;
|
|
1915
|
+
const orderId = orderIdOf(rawId);
|
|
1916
|
+
const shouldWait = values.wait === true;
|
|
1917
|
+
const controls = shouldWait ? waitControls(io, values, (line) => out.notice(line)) : void 0;
|
|
1918
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1919
|
+
const current = await getTemplateShortOrder(baseUrl, apiKey, orderId, io);
|
|
1920
|
+
const final = shouldWait ? settleOrder(await awaitTemplateShortOrder(baseUrl, apiKey, current, io, controls), baseUrl) : current;
|
|
1921
|
+
out.result(orderLines(final.view), { order: final.raw.order, ...shouldWait ? { waited: true } : {} });
|
|
1922
|
+
}
|
|
1923
|
+
async function cancel(context, rawId) {
|
|
1924
|
+
const { io, out, values } = context;
|
|
1925
|
+
const orderId = orderIdOf(rawId);
|
|
1926
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
1927
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1928
|
+
const canceled = await cancelTemplateShortOrder(baseUrl, apiKey, orderId, idempotencyKey, io);
|
|
1929
|
+
out.result([...orderLines(canceled.view), `\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`], { order: canceled.raw.order, idempotencyKey });
|
|
1930
|
+
}
|
|
1931
|
+
async function download(context, rawId) {
|
|
1932
|
+
const { io, out, values } = context;
|
|
1933
|
+
const orderId = orderIdOf(rawId);
|
|
1934
|
+
const defaultName = `template-short-${orderId}.mp4`;
|
|
1935
|
+
const target = textOption(values, "out") ?? defaultName;
|
|
1936
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
1937
|
+
const result = await downloadTemplateShort(baseUrl, apiKey, orderId, io);
|
|
1938
|
+
const saved = await saveDownload(target, result.bytes);
|
|
1939
|
+
out.result(
|
|
1940
|
+
[`\u5DF2\u4E0B\u8F7D\uFF1A${saved.path}`, `\u5927\u5C0F\uFF1A${saved.bytes} \u5B57\u8282`, `\u6821\u9A8C\uFF1Asha256 ${saved.sha256}`],
|
|
1941
|
+
{ download: { ...saved, filename: result.filename ?? defaultName } }
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
async function saveDownload(target, bytes) {
|
|
1945
|
+
try {
|
|
1946
|
+
await writeFile(target, bytes);
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
throw new CliError({
|
|
1949
|
+
code: "CLI_INTERNAL",
|
|
1950
|
+
exitCode: EXIT.SERVER,
|
|
1951
|
+
message: `\u6210\u7247\u5199\u4E0D\u8FDB ${target}\uFF08${briefReason(error)}\uFF09`
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
return { path: target, bytes: bytes.byteLength, sha256: createHash("sha256").update(bytes).digest("hex") };
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// src/commands/drama.ts
|
|
1958
|
+
var SUBCOMMANDS2 = "create\u3001status\u3001quote\u3001confirm\u3001cancel \u548C download";
|
|
1959
|
+
var DURATION_KEYS = ["30s", "60s", "90s"];
|
|
1960
|
+
async function drama(context) {
|
|
1961
|
+
const { args } = context;
|
|
1962
|
+
const [subcommand] = args;
|
|
1963
|
+
if (subcommand === void 0) {
|
|
1964
|
+
throw usageError(`drama \u8981\u8DDF\u4E00\u4E2A\u5B50\u547D\u4EE4\uFF1A${SUBCOMMANDS2}`);
|
|
1965
|
+
}
|
|
1966
|
+
if (subcommand === "create") return create(context);
|
|
1967
|
+
if (subcommand === "status") return status2(context, args[1]);
|
|
1968
|
+
if (subcommand === "quote") return quote2(context, args[1]);
|
|
1969
|
+
if (subcommand === "confirm") return confirm(context, args[1]);
|
|
1970
|
+
if (subcommand === "cancel") return cancel2(context, args[1]);
|
|
1971
|
+
if (subcommand === "download") return download2(context, args[1]);
|
|
1972
|
+
throw usageError(`\u4E0D\u8BA4\u8BC6\u7684\u5B50\u547D\u4EE4 ${subcommand}\uFF1Adrama \u7684\u5B50\u547D\u4EE4\u662F ${SUBCOMMANDS2}`);
|
|
1973
|
+
}
|
|
1974
|
+
function orderIdOf2(raw) {
|
|
1975
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
1976
|
+
throw usageError("\u8981\u7ED9\u8BA2\u5355 id\uFF08drama create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
1977
|
+
}
|
|
1978
|
+
return raw.trim();
|
|
1979
|
+
}
|
|
1980
|
+
function waitControls2(io, values, note) {
|
|
1981
|
+
return {
|
|
1982
|
+
timeoutMs: timeoutSecondsOf(values, DEFAULT_WAIT_TIMEOUT_MS / 1e3) * 1e3,
|
|
1983
|
+
now: io.now ?? (() => /* @__PURE__ */ new Date()),
|
|
1984
|
+
sleep: io.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
1985
|
+
note
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
function settleOrder2(outcome, baseUrl) {
|
|
1989
|
+
const order2 = outcome.value.view;
|
|
1990
|
+
const recovery = taskRecovery("drama", order2.id, order2.status, baseUrl);
|
|
1991
|
+
if (outcome.timedOut) {
|
|
1992
|
+
throw new CliError({
|
|
1993
|
+
code: "CLI_WAIT_TIMEOUT",
|
|
1994
|
+
exitCode: EXIT.WAIT_TIMEOUT,
|
|
1995
|
+
message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${order2.status}\uFF1A\u518D\u67E5\u7528 ${recovery.statusCommand}\uFF0C\u7ED9\u8BE5\u67E5\u8BE2\u52A0 --wait --timeout <\u79D2\u6570> \u53EF\u7EE7\u7EED\u7B49\u5F85`,
|
|
1996
|
+
recovery
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
if (order2.status === "failed") {
|
|
2000
|
+
throw new CliError({
|
|
2001
|
+
code: "CLI_TASK_FAILED",
|
|
2002
|
+
exitCode: EXIT.TASK_FAILED,
|
|
2003
|
+
message: order2.reason === void 0 || order2.reason.trim() === "" ? "\u8FD9\u5355\u77ED\u5267\u6CA1\u6709\u62CD\u6210" : singleLine(order2.reason),
|
|
2004
|
+
recovery
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
if (order2.status === "canceled") {
|
|
2008
|
+
throw new CliError({
|
|
2009
|
+
code: "CLI_TASK_FAILED",
|
|
2010
|
+
exitCode: EXIT.TASK_FAILED,
|
|
2011
|
+
message: "\u8FD9\u5355\u77ED\u5267\u5DF2\u53D6\u6D88\uFF1A\u6CA1\u62CD\u7684\u955C\u5934\u9000\u56DE\u51BB\u7ED3\u989D\u5EA6\uFF0C\u5DF2\u7ECF\u62CD\u6210\u7684\u4FDD\u7559\u7167\u6263",
|
|
2012
|
+
recovery
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
return outcome.value;
|
|
2016
|
+
}
|
|
2017
|
+
function orderLines2(order2) {
|
|
2018
|
+
const money = order2.money;
|
|
2019
|
+
const extra = [];
|
|
2020
|
+
if (money.released > 0) extra.push(`\u5DF2\u91CA\u653E ${money.released} ${money.currency}`);
|
|
2021
|
+
if (money.refunded > 0) extra.push(`\u5DF2\u9000\u6B3E ${money.refunded} ${money.currency}`);
|
|
2022
|
+
return [
|
|
2023
|
+
`\u8BA2\u5355\uFF1A${singleLine(order2.id)}`,
|
|
2024
|
+
`\u72B6\u6001\uFF1A${singleLine(order2.status)}`,
|
|
2025
|
+
`\u4EA7\u54C1\uFF1A${singleLine(order2.productId)}\uFF08\u9898\u6750 ${singleLine(order2.genreId)}\uFF0C\u4F53\u91CF ${singleLine(order2.durationKey)}\uFF09`,
|
|
2026
|
+
`\u66F4\u65B0\u65F6\u95F4\uFF1A${singleLine(order2.updatedAt)}`,
|
|
2027
|
+
`\u8D26\uFF1A\u51BB\u7ED3 ${money.held} ${money.currency}\uFF0C\u5DF2\u7ED3\u7B97 ${money.settled} ${money.currency}` + (extra.length > 0 ? `\uFF08${extra.join("\uFF0C")}\uFF09` : "")
|
|
2028
|
+
];
|
|
2029
|
+
}
|
|
2030
|
+
function guidanceLines(order2) {
|
|
2031
|
+
const lines = [];
|
|
2032
|
+
if (order2.reason !== void 0 && order2.reason.trim() !== "") {
|
|
2033
|
+
const label = order2.status === "failed" ? "\u5931\u8D25\u539F\u56E0" : order2.status === "partial" ? "\u6CA1\u62CD\u5B8C\u7684\u90E8\u5206" : "\u505C\u9760\u539F\u56E0";
|
|
2034
|
+
lines.push(`${label}\uFF1A${singleLine(order2.reason)}`);
|
|
2035
|
+
}
|
|
2036
|
+
for (const gap of order2.gaps ?? []) {
|
|
2037
|
+
const kind = gap.kind === "character" ? "\u89D2\u8272" : gap.kind === "scene" ? "\u573A\u666F" : "\u7D20\u6750";
|
|
2038
|
+
const scenes = gap.sceneNos.length > 0 ? `\uFF0C\u51FA\u73B0\u5728\u7B2C ${gap.sceneNos.join("\u3001")} \u955C` : "";
|
|
2039
|
+
lines.push(`\u7F3A\u53E3\uFF1A${kind}\u300C${singleLine(gap.label !== "" ? gap.label : gap.key)}\u300D\u8FD8\u6CA1\u7ED1\u5B9A${scenes}`);
|
|
2040
|
+
}
|
|
2041
|
+
const next = nextStepOf(order2);
|
|
2042
|
+
return next === null ? lines : [...lines, next];
|
|
2043
|
+
}
|
|
2044
|
+
function nextStepOf(order2) {
|
|
2045
|
+
switch (order2.status) {
|
|
2046
|
+
case "preparing":
|
|
2047
|
+
return `\u4E0B\u4E00\u6B65\uFF1A\u7B49\u5199\u7A3F\u4E0E\u6392\u7248\u8DD1\u5B8C\uFF0C\u91CD\u65B0\u67E5\u8BE2\uFF1Abuke drama status ${order2.id}`;
|
|
2048
|
+
case "needs_attention":
|
|
2049
|
+
return `\u4E0B\u4E00\u6B65\uFF1A\u6309\u4E0A\u9762\u7684\u63D0\u793A\u5728 Studio \u6253\u5F00\u8FD9\u90E8\u77ED\u5267\u5904\u7406\uFF0C\u5904\u7406\u5B8C\u91CD\u65B0\u67E5\u8BE2\uFF1Abuke drama status ${order2.id}`;
|
|
2050
|
+
case "quotable":
|
|
2051
|
+
return `\u4E0B\u4E00\u6B65\uFF1A\u770B\u62A5\u4EF7\uFF1Abuke drama quote ${order2.id}\uFF0C\u518D\u786E\u8BA4\u5F00\u62CD\uFF1Abuke drama confirm ${order2.id} --quote-hash <\u62A5\u4EF7\u54C8\u5E0C>`;
|
|
2052
|
+
case "producing":
|
|
2053
|
+
case "stitching":
|
|
2054
|
+
return `\u4E0B\u4E00\u6B65\uFF1A\u7B49\u5B83\u62CD\u5B8C\uFF1Abuke drama status ${order2.id} --wait`;
|
|
2055
|
+
case "partial":
|
|
2056
|
+
return `\u4E0B\u4E00\u6B65\uFF1A\u53EA\u8865\u7F3A\u7684\u955C\u5934\uFF1Abuke drama confirm ${order2.id} --quote-hash <\u62A5\u4EF7\u54C8\u5E0C>\uFF1B\u4E0D\u8981\u4E86\uFF1Abuke drama cancel ${order2.id}`;
|
|
2057
|
+
case "delivered":
|
|
2058
|
+
return `\u4E0B\u8F7D\uFF1Abuke drama download ${order2.id}`;
|
|
2059
|
+
default:
|
|
2060
|
+
return null;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
function sufficientLine(sufficient) {
|
|
2064
|
+
if (sufficient === true) return "\u591F\u8FD9\u4E00\u5355";
|
|
2065
|
+
if (sufficient === false) return "\u4E0D\u591F\u8FD9\u4E00\u5355";
|
|
2066
|
+
return "\u6682\u65F6\u7B97\u4E0D\u51FA\u6765";
|
|
2067
|
+
}
|
|
2068
|
+
function quoteLines(orderId, quote3) {
|
|
2069
|
+
const price = [`\u603B\u4EF7 ${quote3.totalCredits} \u5E03`, `${quote3.shotCount} \u4E2A\u955C\u5934`];
|
|
2070
|
+
if (quote3.shotCreditCost !== void 0) price.push(`\u6BCF\u955C ${quote3.shotCreditCost} \u5E03`);
|
|
2071
|
+
const spec = [quote3.videoModel, quote3.resolution].filter((entry) => entry !== void 0);
|
|
2072
|
+
return [
|
|
2073
|
+
`\u62A5\u4EF7\uFF1A${price.join("\uFF0C")}`,
|
|
2074
|
+
...spec.length > 0 ? [`\u6863\u4F4D\uFF1A${spec.join(" / ")}`] : [],
|
|
2075
|
+
`\u62A5\u4EF7\u54C8\u5E0C\uFF1A${quote3.quoteHash}`,
|
|
2076
|
+
`\u4F59\u989D\uFF1A${sufficientLine(quote3.sufficient)}`,
|
|
2077
|
+
`\u786E\u8BA4\u5F00\u62CD\uFF1Abuke drama confirm ${orderId} --quote-hash ${quote3.quoteHash}`
|
|
2078
|
+
];
|
|
2079
|
+
}
|
|
2080
|
+
async function create(context) {
|
|
2081
|
+
const { io, out, values } = context;
|
|
2082
|
+
const productId = textOption(values, "product");
|
|
2083
|
+
if (productId === void 0) {
|
|
2084
|
+
throw usageError("\u8981\u7ED9 --product <\u4EA7\u54C1id>\uFF08buke products create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2085
|
+
}
|
|
2086
|
+
const genreId = textOption(values, "genre");
|
|
2087
|
+
const durationKey = textOption(values, "duration");
|
|
2088
|
+
if (durationKey !== void 0 && !DURATION_KEYS.includes(durationKey)) {
|
|
2089
|
+
throw usageError(`--duration \u53EA\u80FD\u662F ${DURATION_KEYS.join("\u3001")}\uFF0C\u5199\u7684\u662F ${durationKey}`);
|
|
2090
|
+
}
|
|
2091
|
+
const briefRef = textOption(values, "brief");
|
|
2092
|
+
const brief = briefRef === void 0 ? void 0 : await readPropsArgument(io, briefRef, "\u6545\u4E8B\u8BBE\u5B9A\uFF08--brief\uFF09");
|
|
2093
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2094
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2095
|
+
const created = await createDramaOrder(
|
|
2096
|
+
baseUrl,
|
|
2097
|
+
apiKey,
|
|
2098
|
+
{
|
|
2099
|
+
productId,
|
|
2100
|
+
...genreId === void 0 ? {} : { genreId },
|
|
2101
|
+
...durationKey === void 0 ? {} : { durationKey },
|
|
2102
|
+
...brief === void 0 ? {} : { brief }
|
|
2103
|
+
},
|
|
2104
|
+
idempotencyKey,
|
|
2105
|
+
io
|
|
2106
|
+
);
|
|
2107
|
+
out.result(
|
|
2108
|
+
[...orderLines2(created.view), `\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`, `\u67E5\u8FDB\u5EA6\uFF1Abuke drama status ${created.view.id}`],
|
|
2109
|
+
// --json 按文档回服务端那份完整的 order;replayed 说明这次是新建还是同键重放。
|
|
2110
|
+
{ order: created.raw.order, replayed: created.replayed, idempotencyKey }
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
async function status2(context, rawId) {
|
|
2114
|
+
const { io, out, values } = context;
|
|
2115
|
+
const orderId = orderIdOf2(rawId);
|
|
2116
|
+
const shouldWait = values.wait === true;
|
|
2117
|
+
const controls = shouldWait ? waitControls2(io, values, (line) => out.notice(line)) : void 0;
|
|
2118
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2119
|
+
const current = await getDramaOrder(baseUrl, apiKey, orderId, io);
|
|
2120
|
+
const final = shouldWait ? settleOrder2(await awaitDramaOrder(baseUrl, apiKey, current, io, controls), baseUrl) : current;
|
|
2121
|
+
out.result([...orderLines2(final.view), ...guidanceLines(final.view)], {
|
|
2122
|
+
order: final.raw.order,
|
|
2123
|
+
...shouldWait ? { waited: true } : {}
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
async function quote2(context, rawId) {
|
|
2127
|
+
const { io, out } = context;
|
|
2128
|
+
const orderId = orderIdOf2(rawId);
|
|
2129
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2130
|
+
const { view, raw } = await getDramaQuote(baseUrl, apiKey, orderId, io);
|
|
2131
|
+
out.result(quoteLines(orderId, view), { quote: raw.quote, quoteHash: view.quoteHash, sufficient: view.sufficient });
|
|
2132
|
+
}
|
|
2133
|
+
async function confirm(context, rawId) {
|
|
2134
|
+
const { io, out, values } = context;
|
|
2135
|
+
const orderId = orderIdOf2(rawId);
|
|
2136
|
+
const quoteHash = textOption(values, "quote-hash");
|
|
2137
|
+
if (quoteHash === void 0) {
|
|
2138
|
+
throw usageError("\u8981\u7ED9 --quote-hash <\u62A5\u4EF7\u54C8\u5E0C>\uFF08buke drama quote \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2139
|
+
}
|
|
2140
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2141
|
+
const current = await getDramaOrder(baseUrl, apiKey, orderId, io);
|
|
2142
|
+
const quoted = current.view.status === "quotable" ? await getDramaQuote(baseUrl, apiKey, orderId, io) : null;
|
|
2143
|
+
if (values.yes !== true) {
|
|
2144
|
+
if (!out.json) {
|
|
2145
|
+
out.result(
|
|
2146
|
+
quoted === null ? [`\u8FD9\u5355\u73B0\u5728\u4E0D\u662F\u53EF\u62A5\u4EF7\u72B6\u6001\uFF08${singleLine(current.view.status)}\uFF09\uFF1A\u6CA1\u6709\u5F53\u524D\u62A5\u4EF7\u53EF\u770B\uFF0C\u786E\u8BA4\u4F1A\u6309\u4F60\u5E26\u6765\u7684\u62A5\u4EF7\u54C8\u5E0C\u63D0\u4EA4`] : quoteLines(orderId, quoted.view),
|
|
2147
|
+
void 0
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
throw new CliError({
|
|
2151
|
+
code: "CLI_USAGE",
|
|
2152
|
+
exitCode: EXIT.USAGE,
|
|
2153
|
+
message: quoted === null ? "\u52A0 --yes \u6309\u4F60\u5E26\u6765\u7684\u62A5\u4EF7\u54C8\u5E0C\u63D0\u4EA4\u8FD9\u6B21\u786E\u8BA4" : `\u8FD9\u4E00\u5355\u73B0\u5728\u62A5\u4EF7 ${quoted.view.totalCredits} \u5E03\u3002\u52A0 --yes \u786E\u8BA4\u8FD9\u7B14\u5E03\u652F\u51FA`,
|
|
2154
|
+
hints: [`\u770B\u8FC7\u518D\u786E\u8BA4\uFF1Abuke drama status ${orderId}`]
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2158
|
+
const confirmed = await confirmDramaOrder(baseUrl, apiKey, orderId, quoteHash, idempotencyKey, io);
|
|
2159
|
+
out.result(
|
|
2160
|
+
[
|
|
2161
|
+
...orderLines2(confirmed.view),
|
|
2162
|
+
confirmed.productionStarted ? quoted === null ? "\u5DF2\u786E\u8BA4\uFF1A\u6309\u539F\u6765\u6536\u4E0B\u7684\u62A5\u4EF7\u8865\u62CD\u7F3A\u7684\u955C\u5934" : `\u5DF2\u786E\u8BA4\uFF1A\u8FD9\u7B14\u6700\u591A ${quoted.view.totalCredits} \u5E03` : "\u5DF2\u786E\u8BA4\uFF1A\u8FD9\u6B21\u6CA1\u6709\u5F00\u65B0\u4EFB\u52A1\uFF08\u5B83\u53EF\u80FD\u5DF2\u7ECF\u5728\u62CD\uFF0C\u6216\u5DF2\u7ECF\u62CD\u597D\u7B49\u62FC\u63A5\uFF09",
|
|
2163
|
+
...guidanceLines(confirmed.view),
|
|
2164
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2165
|
+
],
|
|
2166
|
+
{
|
|
2167
|
+
order: confirmed.raw.order,
|
|
2168
|
+
productionStarted: confirmed.productionStarted,
|
|
2169
|
+
// 计价的那一份:可报价时是刚取回的报价;续拍按原签字走,没有当前报价。
|
|
2170
|
+
confirmed: { totalCredits: quoted === null ? null : quoted.view.totalCredits, quoteHash },
|
|
2171
|
+
idempotencyKey
|
|
2172
|
+
}
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
async function cancel2(context, rawId) {
|
|
2176
|
+
const { io, out, values } = context;
|
|
2177
|
+
const orderId = orderIdOf2(rawId);
|
|
2178
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2179
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2180
|
+
const canceled = await cancelDramaOrder(baseUrl, apiKey, orderId, idempotencyKey, io);
|
|
2181
|
+
out.result(
|
|
2182
|
+
[
|
|
2183
|
+
...orderLines2(canceled.view),
|
|
2184
|
+
canceled.canceled ? "\u5DF2\u53D6\u6D88\uFF1A\u6CA1\u62CD\u7684\u955C\u5934\u9000\u56DE\u51BB\u7ED3\u989D\u5EA6\uFF0C\u5DF2\u7ECF\u62CD\u6210\u7684\u4FDD\u7559\u7167\u6263" : "\u8FD9\u5355\u4E4B\u524D\u5DF2\u7ECF\u53D6\u6D88\u8FC7\uFF0C\u6CA1\u6709\u518D\u53D6\u6D88\u4E00\u6B21",
|
|
2185
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2186
|
+
],
|
|
2187
|
+
{ order: canceled.raw.order, canceled: canceled.canceled, idempotencyKey }
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
async function download2(context, rawId) {
|
|
2191
|
+
const { io, out, values } = context;
|
|
2192
|
+
const orderId = orderIdOf2(rawId);
|
|
2193
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2194
|
+
const result = await downloadDramaFilm(baseUrl, apiKey, orderId, io);
|
|
2195
|
+
const defaultName = result.filename ?? `drama-${orderId}.mp4`;
|
|
2196
|
+
const target = textOption(values, "out") ?? defaultName;
|
|
2197
|
+
const saved = await saveDownload(target, result.bytes);
|
|
2198
|
+
out.result(
|
|
2199
|
+
[`\u5DF2\u4E0B\u8F7D\uFF1A${saved.path}`, `\u5927\u5C0F\uFF1A${saved.bytes} \u5B57\u8282`, `\u6821\u9A8C\uFF1Asha256 ${saved.sha256}`],
|
|
2200
|
+
{ download: { ...saved, filename: defaultName } }
|
|
2201
|
+
);
|
|
2202
|
+
}
|
|
2203
|
+
|
|
868
2204
|
// src/output.ts
|
|
869
2205
|
function errorDocument(error) {
|
|
870
2206
|
const document = {
|
|
@@ -872,13 +2208,27 @@ function errorDocument(error) {
|
|
|
872
2208
|
};
|
|
873
2209
|
if (error.retryAfterSeconds !== void 0) document.error.retryAfterSeconds = error.retryAfterSeconds;
|
|
874
2210
|
if (error.httpStatus !== void 0) document.error.httpStatus = error.httpStatus;
|
|
2211
|
+
if (error.recovery !== void 0) document.recovery = error.recovery;
|
|
875
2212
|
return document;
|
|
876
2213
|
}
|
|
877
2214
|
function errorLines(error) {
|
|
878
2215
|
const details = [`\u9519\u8BEF\u7801 ${singleLine(error.code)}`];
|
|
879
2216
|
if (error.traceId) details.push(`\u8BF7\u6C42\u7F16\u53F7 ${singleLine(error.traceId)}`);
|
|
880
2217
|
if (error.retryAfterSeconds !== void 0) details.push(`${error.retryAfterSeconds} \u79D2\u540E\u53EF\u4EE5\u91CD\u8BD5`);
|
|
881
|
-
|
|
2218
|
+
const recovery = error.recovery;
|
|
2219
|
+
return [
|
|
2220
|
+
`\u9519\u8BEF\uFF1A${singleLine(error.message)}`,
|
|
2221
|
+
`\uFF08${details.join("\uFF1B")}\uFF09`,
|
|
2222
|
+
...error.hints,
|
|
2223
|
+
...recovery === void 0 ? [] : [
|
|
2224
|
+
recovery.acceptance === "confirmed" ? `\u5DF2\u53D7\u7406\u4EFB\u52A1\uFF1A${singleLine(recovery.taskId ?? "")}` : "\u63D0\u4EA4\u7ED3\u679C\u5C1A\u672A\u786E\u8BA4\uFF0C\u8BF7\u5148\u5728 Studio \u6838\u5BF9\u539F\u8BF7\u6C42\uFF1B\u4E0D\u8981\u91CD\u65B0\u4E0B\u5355",
|
|
2225
|
+
...recovery.baseUrl === void 0 ? [] : [`\u670D\u52A1\u5730\u5740\uFF1A${singleLine(recovery.baseUrl)}`],
|
|
2226
|
+
...recovery.status === void 0 ? [] : [`\u6700\u540E\u72B6\u6001\uFF1A${singleLine(recovery.status)}`],
|
|
2227
|
+
...recovery.failureCode === void 0 ? [] : [`\u4EFB\u52A1\u5931\u8D25\u7801\uFF1A${singleLine(recovery.failureCode)}`],
|
|
2228
|
+
...recovery.idempotencyKey === void 0 ? [] : [`\u5E42\u7B49\u952E\uFF1A${singleLine(recovery.idempotencyKey)}`],
|
|
2229
|
+
...recovery.statusCommand === void 0 ? [] : [`\u53EA\u67E5\u8BE2\u539F\u4EFB\u52A1\uFF1A${singleLine(recovery.statusCommand)}`]
|
|
2230
|
+
]
|
|
2231
|
+
];
|
|
882
2232
|
}
|
|
883
2233
|
function createOutput(streams, json) {
|
|
884
2234
|
const secrets = [];
|
|
@@ -996,36 +2346,333 @@ async function logout({ io, out }) {
|
|
|
996
2346
|
}
|
|
997
2347
|
}
|
|
998
2348
|
|
|
999
|
-
// src/commands/
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
return { apiKey: fromEnv, baseUrl: explicit ?? DEFAULT_BASE_URL, source: "env" };
|
|
2349
|
+
// src/commands/products.ts
|
|
2350
|
+
var PRODUCT_ANGLES = ["front", "side", "back", "detail"];
|
|
2351
|
+
async function products(context) {
|
|
2352
|
+
const { args, values } = context;
|
|
2353
|
+
const [subcommand] = args;
|
|
2354
|
+
if (subcommand === void 0) {
|
|
2355
|
+
throw usageError("products \u8981\u8DDF\u4E00\u4E2A\u5B50\u547D\u4EE4\uFF1Acreate\u3001set-images \u6216 confirm-lock");
|
|
1007
2356
|
}
|
|
1008
|
-
|
|
1009
|
-
if (
|
|
2357
|
+
if (subcommand === "create") return create2(context, values);
|
|
2358
|
+
if (subcommand === "set-images") return setImages(context, values, args[1]);
|
|
2359
|
+
if (subcommand === "confirm-lock") return confirmLock(context, values, args[1]);
|
|
2360
|
+
throw usageError(`\u4E0D\u8BA4\u8BC6\u7684\u5B50\u547D\u4EE4 ${subcommand}\uFF1Aproducts \u7684\u5B50\u547D\u4EE4\u662F create\u3001set-images \u548C confirm-lock`);
|
|
2361
|
+
}
|
|
2362
|
+
function productIdOf(raw, what) {
|
|
2363
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
2364
|
+
throw usageError(`\u8981\u7ED9${what}`);
|
|
2365
|
+
}
|
|
2366
|
+
return raw.trim();
|
|
2367
|
+
}
|
|
2368
|
+
async function create2(context, values) {
|
|
2369
|
+
const { io, out } = context;
|
|
2370
|
+
const name2 = textOption(values, "name");
|
|
2371
|
+
if (name2 === void 0) {
|
|
2372
|
+
throw usageError("\u8981\u7ED9 --name <\u540D\u79F0>");
|
|
2373
|
+
}
|
|
2374
|
+
const category = textOption(values, "category");
|
|
2375
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2376
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2377
|
+
const { view, raw, replayed } = await createProduct(baseUrl, apiKey, name2, category, idempotencyKey, io);
|
|
2378
|
+
out.result(
|
|
2379
|
+
[
|
|
2380
|
+
replayed ? `\u4EA7\u54C1\u5DF2\u5B58\u5728\uFF1A${singleLine(view.id)}\uFF08\u540C\u952E\u91CD\u653E\uFF09` : `\u5DF2\u521B\u5EFA\u4EA7\u54C1\uFF1A${singleLine(view.id)}`,
|
|
2381
|
+
`\u540D\u79F0\uFF1A${singleLine(view.name)}`,
|
|
2382
|
+
`\u7C7B\u76EE\uFF1A${singleLine(view.category)}`,
|
|
2383
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2384
|
+
],
|
|
2385
|
+
{ product: raw.product, replayed, idempotencyKey }
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
async function setImages(context, values, rawId) {
|
|
2389
|
+
const { io, out } = context;
|
|
2390
|
+
const productId = productIdOf(rawId, "\u4EA7\u54C1 id\uFF08buke products create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2391
|
+
const planned = [];
|
|
2392
|
+
for (const angle of PRODUCT_ANGLES) {
|
|
2393
|
+
const path = textOption(values, angle);
|
|
2394
|
+
if (path !== void 0) planned.push({ angle, path });
|
|
2395
|
+
}
|
|
2396
|
+
if (planned.length === 0) {
|
|
2397
|
+
throw usageError("\u81F3\u5C11\u7ED9\u4E00\u4E2A\u89D2\u5EA6\u7684\u56FE\uFF1A--front\u3001--side\u3001--back \u6216 --detail");
|
|
2398
|
+
}
|
|
2399
|
+
const images = await Promise.all(planned.map(async ({ angle, path }) => ({ angle, ...await readImageFile(path) })));
|
|
2400
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2401
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2402
|
+
let latest;
|
|
2403
|
+
const uploaded = [];
|
|
2404
|
+
for (const image of images) {
|
|
2405
|
+
const result = await putProductImage(
|
|
2406
|
+
baseUrl,
|
|
2407
|
+
apiKey,
|
|
2408
|
+
productId,
|
|
2409
|
+
image.angle,
|
|
2410
|
+
image.bytes,
|
|
2411
|
+
image.contentType,
|
|
2412
|
+
idempotencyKey,
|
|
2413
|
+
io
|
|
2414
|
+
);
|
|
2415
|
+
latest = result;
|
|
2416
|
+
uploaded.push({ angle: image.angle, replayed: result.replayed });
|
|
2417
|
+
}
|
|
2418
|
+
out.result(
|
|
2419
|
+
[
|
|
2420
|
+
...uploaded.map(
|
|
2421
|
+
({ angle, replayed }) => replayed ? `${angle}\uFF1A\u8FD9\u5F20\u5DF2\u7ECF\u4F20\u8FC7\u4E86\uFF08\u540C\u5B57\u8282\u91CD\u653E\uFF09` : `${angle}\uFF1A\u5DF2\u4E0A\u4F20`
|
|
2422
|
+
),
|
|
2423
|
+
`\u4EA7\u54C1\uFF1A${singleLine(latest?.view.id ?? productId)}\uFF08\u73B0\u5728 ${latest?.view.images.length ?? 0} \u5F20\u56FE\uFF09`,
|
|
2424
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2425
|
+
],
|
|
2426
|
+
{ product: latest?.raw.product, uploaded, idempotencyKey }
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
async function confirmLock(context, values, rawId) {
|
|
2430
|
+
const { io, out } = context;
|
|
2431
|
+
const productId = productIdOf(rawId, "\u4EA7\u54C1 id\uFF08buke products create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2432
|
+
const colorName = textOption(values, "color");
|
|
2433
|
+
const silhouette = textOption(values, "silhouette");
|
|
2434
|
+
const material = textOption(values, "material");
|
|
2435
|
+
for (const [option, value] of [
|
|
2436
|
+
["--color", colorName],
|
|
2437
|
+
["--silhouette", silhouette],
|
|
2438
|
+
["--material", material]
|
|
2439
|
+
]) {
|
|
2440
|
+
if (value === void 0) {
|
|
2441
|
+
throw usageError(`\u5916\u89C2\u9501\u8981\u7ED9\u5168\uFF1A\u7F3A ${option}`);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2445
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2446
|
+
const { view, raw } = await confirmProductLock(
|
|
2447
|
+
baseUrl,
|
|
2448
|
+
apiKey,
|
|
2449
|
+
productId,
|
|
2450
|
+
{ colorName, silhouette, material },
|
|
2451
|
+
idempotencyKey,
|
|
2452
|
+
io
|
|
2453
|
+
);
|
|
2454
|
+
out.result(
|
|
2455
|
+
[
|
|
2456
|
+
`\u5916\u89C2\u9501\u5DF2\u786E\u8BA4\uFF1A\u4EA7\u54C1 ${singleLine(view.product.id)}`,
|
|
2457
|
+
`\u989C\u8272\uFF1A${singleLine(view.productLock.colorName)}`,
|
|
2458
|
+
`\u5ED3\u5F62\uFF1A${singleLine(view.productLock.silhouette)}`,
|
|
2459
|
+
`\u6750\u8D28\uFF1A${singleLine(view.productLock.material)}`,
|
|
2460
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2461
|
+
],
|
|
2462
|
+
{ productLock: raw.productLock, product: raw.product, idempotencyKey }
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
// src/commands/videos.ts
|
|
2467
|
+
var SUBCOMMANDS3 = "create\u3001status\u3001message\u3001confirm \u548C download";
|
|
2468
|
+
async function videos(context) {
|
|
2469
|
+
const { args } = context;
|
|
2470
|
+
const [subcommand] = args;
|
|
2471
|
+
if (subcommand === void 0) {
|
|
2472
|
+
throw usageError(`videos \u8981\u8DDF\u4E00\u4E2A\u5B50\u547D\u4EE4\uFF1A${SUBCOMMANDS3}`);
|
|
2473
|
+
}
|
|
2474
|
+
if (subcommand === "create") return create3(context);
|
|
2475
|
+
if (subcommand === "status") return status3(context, args[1]);
|
|
2476
|
+
if (subcommand === "message") return message(context, args[1]);
|
|
2477
|
+
if (subcommand === "confirm") return confirm2(context, args[1]);
|
|
2478
|
+
if (subcommand === "download") return download3(context, args[1]);
|
|
2479
|
+
throw usageError(`\u4E0D\u8BA4\u8BC6\u7684\u5B50\u547D\u4EE4 ${subcommand}\uFF1Avideos \u7684\u5B50\u547D\u4EE4\u662F ${SUBCOMMANDS3}`);
|
|
2480
|
+
}
|
|
2481
|
+
function runIdOf(raw) {
|
|
2482
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
2483
|
+
throw usageError("\u8981\u7ED9\u89C6\u9891\u4EFB\u52A1 id\uFF08videos create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2484
|
+
}
|
|
2485
|
+
return raw.trim();
|
|
2486
|
+
}
|
|
2487
|
+
function waitControls3(io, values) {
|
|
2488
|
+
return {
|
|
2489
|
+
timeoutMs: timeoutSecondsOf(values, DEFAULT_WAIT_TIMEOUT_MS / 1e3) * 1e3,
|
|
2490
|
+
now: io.now ?? (() => /* @__PURE__ */ new Date()),
|
|
2491
|
+
sleep: io.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
function settleRun(outcome, baseUrl) {
|
|
2495
|
+
const run2 = outcome.value.view;
|
|
2496
|
+
const recovery = taskRecovery("videos", run2.rootRunId, run2.status, baseUrl, run2.failure?.code);
|
|
2497
|
+
if (outcome.timedOut) {
|
|
1010
2498
|
throw new CliError({
|
|
1011
|
-
code: "
|
|
1012
|
-
exitCode: EXIT.
|
|
1013
|
-
message:
|
|
2499
|
+
code: "CLI_WAIT_TIMEOUT",
|
|
2500
|
+
exitCode: EXIT.WAIT_TIMEOUT,
|
|
2501
|
+
message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${run2.status}\uFF08${run2.stage}\uFF09\uFF1A\u518D\u67E5\u7528 ${recovery.statusCommand}\uFF0C\u7ED9\u8BE5\u67E5\u8BE2\u52A0 --wait --timeout <\u79D2\u6570> \u53EF\u7EE7\u7EED\u7B49\u5F85`,
|
|
2502
|
+
recovery
|
|
1014
2503
|
});
|
|
1015
2504
|
}
|
|
1016
|
-
|
|
1017
|
-
if (explicit !== null && explicit !== stored.baseUrl) {
|
|
2505
|
+
if (run2.status === "failed" || run2.status === "canceled") {
|
|
1018
2506
|
throw new CliError({
|
|
1019
|
-
code: "
|
|
2507
|
+
code: "CLI_TASK_FAILED",
|
|
2508
|
+
exitCode: EXIT.TASK_FAILED,
|
|
2509
|
+
message: run2.failure === null ? `\u8FD9\u6761\u89C6\u9891${run2.status === "canceled" ? "\u5DF2\u53D6\u6D88" : "\u6CA1\u6709\u505A\u6210"}` : singleLine(run2.failure.title) + (run2.failure.detail === "" ? "" : `\uFF1A${singleLine(run2.failure.detail)}`),
|
|
2510
|
+
recovery
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
return outcome.value;
|
|
2514
|
+
}
|
|
2515
|
+
function runLines(run2) {
|
|
2516
|
+
return [
|
|
2517
|
+
`\u89C6\u9891\u4EFB\u52A1\uFF1A${singleLine(run2.rootRunId)}`,
|
|
2518
|
+
`\u72B6\u6001\uFF1A${singleLine(run2.status)}\uFF08${singleLine(run2.stage)}\uFF09`,
|
|
2519
|
+
`\u4EA7\u54C1\uFF1A${singleLine(run2.product.name)}`,
|
|
2520
|
+
`\u66F4\u65B0\u65F6\u95F4\uFF1A${singleLine(run2.updatedAt)}`
|
|
2521
|
+
];
|
|
2522
|
+
}
|
|
2523
|
+
function waitingLines(run2) {
|
|
2524
|
+
if (run2.status === "awaiting_confirmation") return [`\u7B49\u4F60\u786E\u8BA4\uFF1Abuke videos confirm ${run2.rootRunId}`];
|
|
2525
|
+
if (run2.status === "awaiting_reply" || run2.status === "needs_input") {
|
|
2526
|
+
return [`\u7B49\u4F60\u56DE\u7B54\uFF1Abuke videos message ${run2.rootRunId} --text <\u5185\u5BB9>`];
|
|
2527
|
+
}
|
|
2528
|
+
return [];
|
|
2529
|
+
}
|
|
2530
|
+
async function create3(context) {
|
|
2531
|
+
const { io, out, values } = context;
|
|
2532
|
+
const productId = textOption(values, "product");
|
|
2533
|
+
if (productId === void 0) {
|
|
2534
|
+
throw usageError("\u8981\u7ED9 --product <\u4EA7\u54C1id>\uFF08buke products create \u56DE\u7684\u90A3\u4E2A\uFF09");
|
|
2535
|
+
}
|
|
2536
|
+
const intent = textOption(values, "intent");
|
|
2537
|
+
const durationRaw = textOption(values, "duration");
|
|
2538
|
+
let durationSeconds;
|
|
2539
|
+
if (durationRaw !== void 0) {
|
|
2540
|
+
const parsed = Number(durationRaw);
|
|
2541
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
2542
|
+
throw usageError(`--duration \u8981\u662F\u6B63\u6574\u6570\u79D2\u6570\uFF0C\u5199\u7684\u662F ${durationRaw}`);
|
|
2543
|
+
}
|
|
2544
|
+
durationSeconds = parsed;
|
|
2545
|
+
}
|
|
2546
|
+
const preferences = {};
|
|
2547
|
+
if (durationSeconds !== void 0) preferences.durationSeconds = durationSeconds;
|
|
2548
|
+
for (const [option, field] of [
|
|
2549
|
+
["resolution", "resolution"],
|
|
2550
|
+
["speed", "speed"],
|
|
2551
|
+
["aspect-ratio", "aspectRatio"],
|
|
2552
|
+
["style-preset", "stylePreset"]
|
|
2553
|
+
]) {
|
|
2554
|
+
const value = textOption(values, option);
|
|
2555
|
+
if (value !== void 0) preferences[field] = value;
|
|
2556
|
+
}
|
|
2557
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2558
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2559
|
+
const { view, raw } = await createVideoRun(
|
|
2560
|
+
baseUrl,
|
|
2561
|
+
apiKey,
|
|
2562
|
+
{
|
|
2563
|
+
productId,
|
|
2564
|
+
...intent === void 0 ? {} : { intent },
|
|
2565
|
+
...Object.keys(preferences).length > 0 ? { preferences } : {}
|
|
2566
|
+
},
|
|
2567
|
+
idempotencyKey,
|
|
2568
|
+
io
|
|
2569
|
+
);
|
|
2570
|
+
out.result(
|
|
2571
|
+
[
|
|
2572
|
+
...runLines(view),
|
|
2573
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`,
|
|
2574
|
+
`\u67E5\u8FDB\u5EA6\uFF1Abuke videos status ${view.rootRunId}`
|
|
2575
|
+
],
|
|
2576
|
+
// --json 按文档回服务端那份完整的 run 视图。
|
|
2577
|
+
{ run: raw.run, idempotencyKey }
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
async function status3(context, rawId) {
|
|
2581
|
+
const { io, out, values } = context;
|
|
2582
|
+
const runId = runIdOf(rawId);
|
|
2583
|
+
const shouldWait = values.wait === true;
|
|
2584
|
+
const controls = shouldWait ? waitControls3(io, values) : void 0;
|
|
2585
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2586
|
+
const current = await getVideoRun(baseUrl, apiKey, runId, io);
|
|
2587
|
+
const final = shouldWait ? settleRun(await awaitVideoRun(baseUrl, apiKey, current, io, controls), baseUrl) : current;
|
|
2588
|
+
const lastDirectorLine = [...final.view.messages].reverse().find((message2) => message2.role === "assistant" && message2.text.trim() !== "");
|
|
2589
|
+
out.result(
|
|
2590
|
+
[
|
|
2591
|
+
...runLines(final.view),
|
|
2592
|
+
...lastDirectorLine === void 0 ? [] : [`\u5BFC\u6F14\u8BF4\uFF1A${singleLine(lastDirectorLine.text)}`],
|
|
2593
|
+
...waitingLines(final.view),
|
|
2594
|
+
...final.view.status === "succeeded" ? [`\u4E0B\u8F7D\uFF1Abuke videos download ${final.view.rootRunId}`] : []
|
|
2595
|
+
],
|
|
2596
|
+
{ run: final.raw.run, ...shouldWait ? { waited: true } : {} }
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2599
|
+
async function message(context, rawId) {
|
|
2600
|
+
const { io, out, values } = context;
|
|
2601
|
+
const runId = runIdOf(rawId);
|
|
2602
|
+
const text = textOption(values, "text");
|
|
2603
|
+
if (text === void 0) {
|
|
2604
|
+
throw usageError("\u8981\u7ED9 --text <\u5185\u5BB9>\uFF1A\u60F3\u5BF9\u5BFC\u6F14\u8BF4\u7684\u90A3\u53E5\u8BDD");
|
|
2605
|
+
}
|
|
2606
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2607
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2608
|
+
const { view, raw } = await postVideoMessage(baseUrl, apiKey, runId, text, idempotencyKey, io);
|
|
2609
|
+
const lastDirectorLine = [...view.messages].reverse().find((entry) => entry.role === "assistant" && entry.text.trim() !== "");
|
|
2610
|
+
out.result(
|
|
2611
|
+
[
|
|
2612
|
+
...runLines(view),
|
|
2613
|
+
...lastDirectorLine === void 0 ? [] : [`\u5BFC\u6F14\u8BF4\uFF1A${singleLine(lastDirectorLine.text)}`],
|
|
2614
|
+
...waitingLines(view),
|
|
2615
|
+
`\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`
|
|
2616
|
+
],
|
|
2617
|
+
{ run: raw.run, idempotencyKey }
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
async function confirm2(context, rawId) {
|
|
2621
|
+
const { io, out, values } = context;
|
|
2622
|
+
const runId = runIdOf(rawId);
|
|
2623
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2624
|
+
const fetched = await getVideoRun(baseUrl, apiKey, runId, io);
|
|
2625
|
+
const run2 = fetched.view;
|
|
2626
|
+
const card = run2.nextActions.find((action) => action.type === "confirm_production");
|
|
2627
|
+
if (card === void 0) {
|
|
2628
|
+
throw new CliError({
|
|
2629
|
+
code: "CLI_USAGE",
|
|
1020
2630
|
exitCode: EXIT.USAGE,
|
|
1021
|
-
message: `\
|
|
2631
|
+
message: `\u73B0\u5728\u6CA1\u6709\u7B49\u7740\u786E\u8BA4\u7684\u5361\uFF08\u5F53\u524D\u72B6\u6001\uFF1A${run2.status}\uFF09\u3002\u5BFC\u6F14\u505C\u4E0B\u6765\u8981\u786E\u8BA4\u65F6\u518D\u8FD0\u884C\u8FD9\u6761\u547D\u4EE4`
|
|
1022
2632
|
});
|
|
1023
2633
|
}
|
|
1024
|
-
|
|
2634
|
+
if (card.confirmation === void 0) {
|
|
2635
|
+
throw new CliError({
|
|
2636
|
+
code: "CLI_USAGE",
|
|
2637
|
+
exitCode: EXIT.USAGE,
|
|
2638
|
+
message: "\u670D\u52A1\u7AEF\u6302\u51FA\u7684\u8FD9\u5F20\u5361\u4E0D\u5E26\u786E\u8BA4\u8EAB\u4EFD\uFF0C\u786E\u8BA4\u4E0D\u4E86\uFF1B\u7A0D\u540E\u91CD\u65B0\u8FD0\u884C buke videos status \u770B\u6700\u65B0\u72B6\u6001"
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2641
|
+
const credits = card.credits ?? 0;
|
|
2642
|
+
const kind = card.confirmKind === void 0 ? "\u786E\u8BA4" : `\u786E\u8BA4\uFF08${card.confirmKind}\uFF09`;
|
|
2643
|
+
if (values.yes !== true) {
|
|
2644
|
+
throw new CliError({
|
|
2645
|
+
code: "CLI_USAGE",
|
|
2646
|
+
exitCode: EXIT.USAGE,
|
|
2647
|
+
message: `\u8FD9\u5F20\u5361\u8981\u82B1 ${credits} \u5E03\u3002\u52A0 --yes \u786E\u8BA4\u8FD9\u7B14\u5E03\u652F\u51FA`,
|
|
2648
|
+
hints: [`${kind}\uFF1A${singleLine(run2.label)}`, `\u770B\u8FC7\u518D\u786E\u8BA4\uFF1Abuke videos status ${run2.rootRunId}`]
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
const idempotencyKey = idempotencyKeyOf(values);
|
|
2652
|
+
const outcome = await confirmVideoProduction(baseUrl, apiKey, runId, run2.version, card.confirmation, idempotencyKey, io);
|
|
2653
|
+
out.result(
|
|
2654
|
+
[...runLines(outcome.view), `\u5DF2\u786E\u8BA4\uFF1A\u8FD9\u7B14\u6700\u591A ${credits} \u5E03`, ...waitingLines(outcome.view), `\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`],
|
|
2655
|
+
{ run: outcome.raw.run, confirmed: { credits, confirmKind: card.confirmKind ?? null }, idempotencyKey }
|
|
2656
|
+
);
|
|
1025
2657
|
}
|
|
2658
|
+
async function download3(context, rawId) {
|
|
2659
|
+
const { io, out, values } = context;
|
|
2660
|
+
const runId = runIdOf(rawId);
|
|
2661
|
+
const { apiKey, baseUrl } = await resolveApiKey(context);
|
|
2662
|
+
const result = await downloadVideo(baseUrl, apiKey, runId, io);
|
|
2663
|
+
const defaultName = result.filename ?? `video-${runId}.mp4`;
|
|
2664
|
+
const target = textOption(values, "out") ?? defaultName;
|
|
2665
|
+
const saved = await saveDownload(target, result.bytes);
|
|
2666
|
+
out.result(
|
|
2667
|
+
[`\u5DF2\u4E0B\u8F7D\uFF1A${saved.path}`, `\u5927\u5C0F\uFF1A${saved.bytes} \u5B57\u8282`, `\u6821\u9A8C\uFF1Asha256 ${saved.sha256}`],
|
|
2668
|
+
{ download: { ...saved, filename: defaultName } }
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/commands/whoami.ts
|
|
1026
2673
|
async function whoami(context) {
|
|
1027
2674
|
const { io, out } = context;
|
|
1028
|
-
const resolved = await
|
|
2675
|
+
const resolved = await resolveApiKey(context);
|
|
1029
2676
|
const { apiKey, baseUrl } = resolved;
|
|
1030
2677
|
let me;
|
|
1031
2678
|
try {
|
|
@@ -1041,11 +2688,7 @@ async function whoami(context) {
|
|
|
1041
2688
|
const displayPrefix = displayPrefixOf(apiKey, me.key.id);
|
|
1042
2689
|
const identity = identityDocument(baseUrl, me, displayPrefix);
|
|
1043
2690
|
out.result(
|
|
1044
|
-
[
|
|
1045
|
-
`\u5F53\u524D\u767B\u5F55 ${baseUrl}`,
|
|
1046
|
-
...identityLines(me, displayPrefix),
|
|
1047
|
-
resolved.source === "env" ? "\u5BC6\u94A5\u6765\u81EA\uFF1A\u73AF\u5883\u53D8\u91CF BUKE_API_KEY" : `\u5BC6\u94A5\u6765\u81EA\uFF1A${resolved.credentialsPath}`
|
|
1048
|
-
],
|
|
2691
|
+
[`\u5F53\u524D\u767B\u5F55 ${baseUrl}`, ...identityLines(me, displayPrefix), keySourceLine(resolved)],
|
|
1049
2692
|
resolved.source === "env" ? { ...identity, source: resolved.source } : { ...identity, source: resolved.source, credentialsPath: resolved.credentialsPath },
|
|
1050
2693
|
[displayPrefix]
|
|
1051
2694
|
);
|
|
@@ -1062,6 +2705,8 @@ var COMMANDS = {
|
|
|
1062
2705
|
login: {
|
|
1063
2706
|
run: login,
|
|
1064
2707
|
acceptsBaseUrl: true,
|
|
2708
|
+
options: [],
|
|
2709
|
+
positionals: [],
|
|
1065
2710
|
help: [
|
|
1066
2711
|
"\u7528\u6CD5\uFF1Abuke login [--base-url <\u5730\u5740>] [--json]",
|
|
1067
2712
|
"",
|
|
@@ -1078,6 +2723,8 @@ var COMMANDS = {
|
|
|
1078
2723
|
whoami: {
|
|
1079
2724
|
run: whoami,
|
|
1080
2725
|
acceptsBaseUrl: true,
|
|
2726
|
+
options: [],
|
|
2727
|
+
positionals: [],
|
|
1081
2728
|
help: [
|
|
1082
2729
|
"\u7528\u6CD5\uFF1Abuke whoami [--base-url <\u5730\u5740>] [--json]",
|
|
1083
2730
|
"",
|
|
@@ -1093,6 +2740,8 @@ var COMMANDS = {
|
|
|
1093
2740
|
logout: {
|
|
1094
2741
|
run: logout,
|
|
1095
2742
|
acceptsBaseUrl: false,
|
|
2743
|
+
options: [],
|
|
2744
|
+
positionals: [],
|
|
1096
2745
|
help: [
|
|
1097
2746
|
"\u7528\u6CD5\uFF1Abuke logout [--json]",
|
|
1098
2747
|
"",
|
|
@@ -1101,15 +2750,189 @@ var COMMANDS = {
|
|
|
1101
2750
|
"\u9009\u9879\uFF1A",
|
|
1102
2751
|
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
1103
2752
|
].join("\n")
|
|
2753
|
+
},
|
|
2754
|
+
balance: {
|
|
2755
|
+
run: balance,
|
|
2756
|
+
acceptsBaseUrl: true,
|
|
2757
|
+
options: [],
|
|
2758
|
+
positionals: [],
|
|
2759
|
+
help: [
|
|
2760
|
+
"\u7528\u6CD5\uFF1Abuke balance [--json]",
|
|
2761
|
+
"",
|
|
2762
|
+
"\u67E5\u8BE2\u8FD9\u4E2A\u8D26\u53F7\u7684\u4F59\u989D\uFF1A\u53EF\u7528\u591A\u5C11\u3001\u51BB\u7740\u591A\u5C11\u3001\u5DF2\u7ECF\u82B1\u4E86\u591A\u5C11\u3002",
|
|
2763
|
+
"",
|
|
2764
|
+
"\u9009\u9879\uFF1A",
|
|
2765
|
+
" --base-url <\u5730\u5740> \u670D\u52A1\u5730\u5740\uFF1B\u4E0D\u7ED9\u65F6\u7528\u767B\u5F55\u65F6\u7684\u5730\u5740",
|
|
2766
|
+
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
2767
|
+
].join("\n")
|
|
2768
|
+
},
|
|
2769
|
+
products: {
|
|
2770
|
+
run: products,
|
|
2771
|
+
acceptsBaseUrl: true,
|
|
2772
|
+
options: ["name", "category", "front", "side", "back", "detail", "color", "silhouette", "material", "idempotency-key"],
|
|
2773
|
+
positionals: ["\u5B50\u547D\u4EE4", "\u4EA7\u54C1id"],
|
|
2774
|
+
help: [
|
|
2775
|
+
"\u7528\u6CD5\uFF1A",
|
|
2776
|
+
" buke products create --name <\u540D\u79F0> [--category <\u7C7B\u76EE>] [--json]",
|
|
2777
|
+
" buke products set-images <\u4EA7\u54C1id> --front <\u56FE> [--side <\u56FE>] [--back <\u56FE>] [--detail <\u56FE>]",
|
|
2778
|
+
" buke products confirm-lock <\u4EA7\u54C1id> --color <\u8272> --silhouette <\u5ED3\u5F62> --material <\u6750\u8D28>",
|
|
2779
|
+
"",
|
|
2780
|
+
"\u4EA7\u54C1\u5EFA\u6863\uFF0C\u662F\u5546\u54C1\u89C6\u9891\u4E0B\u5355\u7684\u524D\u7F6E\uFF1A",
|
|
2781
|
+
" create \u5EFA\u4E00\u4EF6\u4EA7\u54C1\uFF08\u540D\u79F0\u5FC5\u586B\uFF1B\u7C7B\u76EE\u53EF\u7ED9\u53EF\u4E0D\u7ED9\uFF0C\u4E0D\u7ED9\u65F6\u7531\u670D\u52A1\u7AEF\u6309\u540D\u79F0\u8BC6\u522B\uFF09",
|
|
2782
|
+
" set-images \u4F20\u4EA7\u54C1\u56FE\uFF08JPEG/PNG/WebP \u7684\u6587\u4EF6\u8DEF\u5F84\uFF1B\u81F3\u5C11\u7ED9\u4E00\u4E2A\u89D2\u5EA6\uFF09",
|
|
2783
|
+
" confirm-lock \u786E\u8BA4\u5916\u89C2\u9501\uFF1A\u989C\u8272\u3001\u5ED3\u5F62\u3001\u6750\u8D28\u4E09\u6837\u90FD\u8981\u7ED9",
|
|
2784
|
+
"",
|
|
2785
|
+
"\u9009\u9879\uFF1A",
|
|
2786
|
+
" --name <\u540D\u79F0> \u4EA7\u54C1\u540D\u79F0",
|
|
2787
|
+
" --category <\u7C7B\u76EE> \u7C7B\u76EE\uFF1B\u4E0D\u7ED9\u65F6\u670D\u52A1\u7AEF\u6309\u540D\u79F0\u548C\u4ECB\u7ECD\u8BC6\u522B",
|
|
2788
|
+
" --front/--side/--back/--detail <\u56FE> \u4EA7\u54C1\u56FE\u7684\u89D2\u5EA6\u4E0E\u6587\u4EF6\u8DEF\u5F84",
|
|
2789
|
+
" --color <\u8272> \u5916\u89C2\u9501\uFF1A\u989C\u8272",
|
|
2790
|
+
" --silhouette <\u5ED3\u5F62> \u5916\u89C2\u9501\uFF1A\u5ED3\u5F62",
|
|
2791
|
+
" --material <\u6750\u8D28> \u5916\u89C2\u9501\uFF1A\u6750\u8D28",
|
|
2792
|
+
" --idempotency-key <\u952E> \u5E42\u7B49\u952E\uFF1B\u4E0D\u7ED9\u65F6\u81EA\u52A8\u751F\u6210\u5E76\u5728\u8F93\u51FA\u91CC\u56DE\u663E\uFF0C\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u539F\u6837\u590D\u7528",
|
|
2793
|
+
" --base-url <\u5730\u5740> \u670D\u52A1\u5730\u5740\uFF1B\u4E0D\u7ED9\u65F6\u7528\u767B\u5F55\u65F6\u7684\u5730\u5740",
|
|
2794
|
+
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
2795
|
+
].join("\n")
|
|
2796
|
+
},
|
|
2797
|
+
shorts: {
|
|
2798
|
+
run: shorts,
|
|
2799
|
+
acceptsBaseUrl: true,
|
|
2800
|
+
options: ["template", "props", "quote-hash", "wait", "timeout", "out", "idempotency-key"],
|
|
2801
|
+
positionals: ["\u5B50\u547D\u4EE4", "\u8BA2\u5355id"],
|
|
2802
|
+
help: [
|
|
2803
|
+
"\u7528\u6CD5\uFF1A",
|
|
2804
|
+
" buke shorts catalog [--json]",
|
|
2805
|
+
" buke shorts upload <\u56FE\u7247\u6587\u4EF6>",
|
|
2806
|
+
" buke shorts quote --template <\u6A21\u677Fid> --props <props.json|->",
|
|
2807
|
+
" buke shorts order --template <\u6A21\u677Fid> --props <props.json|-> [--quote-hash <hash>] [--wait] [--timeout <\u79D2>]",
|
|
2808
|
+
" buke shorts status <\u8BA2\u5355id> [--wait] [--timeout <\u79D2>]",
|
|
2809
|
+
" buke shorts download <\u8BA2\u5355id> [--out <\u8DEF\u5F84>]",
|
|
2810
|
+
" buke shorts cancel <\u8BA2\u5355id>",
|
|
2811
|
+
"",
|
|
2812
|
+
"\u6E32\u67D3\u77ED\u7247\uFF08\u6A21\u677F\u76F4\u51FA\u7684\u7AD6\u5C4F\u5546\u54C1\u77ED\u7247\uFF09\uFF1A",
|
|
2813
|
+
" catalog \u5217\u51FA\u5168\u90E8\u6A21\u677F\uFF08id\u3001\u65F6\u957F\u3001\u8981\u54EA\u4E9B\u7D20\u6750\uFF09",
|
|
2814
|
+
" upload \u6258\u7BA1\u4E00\u5F20\u7D20\u6750\u56FE\uFF0C\u6362\u56DE\u53EF\u4EE5\u76F4\u63A5\u586B\u8FDB\u6A21\u677F\u56FE\u7247\u69FD\u7684\u5730\u5740",
|
|
2815
|
+
" quote \u53EA\u8BFB\u62A5\u4EF7\uFF1B\u6536\u8D39\u7684\u6A21\u677F\u4E0B\u5355\u524D\u5FC5\u987B\u5148\u62FF quoteHash",
|
|
2816
|
+
" order \u4E0B\u5355\uFF1B\u540C\u952E\u540C\u5185\u5BB9\u91CD\u8BD5\u56DE\u540C\u4E00\u5F20\u5355",
|
|
2817
|
+
" status \u67E5\u8FDB\u5EA6\uFF1B--wait \u4E00\u76F4\u7B49\u5230\u51FA\u7ED3\u679C",
|
|
2818
|
+
" download \u4E0B\u8F7D\u6E32\u67D3\u597D\u7684\u6210\u7247\uFF08mp4\uFF09",
|
|
2819
|
+
" cancel \u53D6\u6D88\u8FD8\u6CA1\u5F00\u59CB\u7684\u8BA2\u5355",
|
|
2820
|
+
"",
|
|
2821
|
+
"\u9009\u9879\uFF1A",
|
|
2822
|
+
" --template <\u6A21\u677Fid> \u6A21\u677F id\uFF08catalog \u91CC\u5217\u51FA\u7684\uFF09",
|
|
2823
|
+
" --props <props.json|-> \u6A21\u677F\u53C2\u6570\uFF1A\u4E00\u4E2A JSON \u6587\u4EF6\uFF0C\u6216 - \u8868\u793A\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB",
|
|
2824
|
+
" --quote-hash <hash> \u62A5\u4EF7\u54C8\u5E0C\uFF08quote \u547D\u4EE4\u56DE\u7684\uFF09",
|
|
2825
|
+
" --wait \u4E00\u76F4\u7B49\u5230\u7EC8\u6001\uFF1A\u6210\u529F\u9000\u51FA 0\uFF0C\u4EFB\u52A1\u5931\u8D25\u9000\u51FA 9\uFF0C\u5230\u65F6\u9650\u9000\u51FA 8",
|
|
2826
|
+
" --timeout <\u79D2> --wait \u7684\u65F6\u9650\uFF0C\u9ED8\u8BA4 600",
|
|
2827
|
+
" --out <\u8DEF\u5F84> \u4E0B\u8F7D\u4FDD\u5B58\u7684\u4F4D\u7F6E\uFF1B\u4E0D\u7ED9\u65F6\u5B58\u5230\u5F53\u524D\u76EE\u5F55",
|
|
2828
|
+
" --idempotency-key <\u952E> \u5E42\u7B49\u952E\uFF1B\u4E0D\u7ED9\u65F6\u81EA\u52A8\u751F\u6210\u5E76\u5728\u8F93\u51FA\u91CC\u56DE\u663E\uFF0C\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u539F\u6837\u590D\u7528",
|
|
2829
|
+
" --base-url <\u5730\u5740> \u670D\u52A1\u5730\u5740\uFF1B\u4E0D\u7ED9\u65F6\u7528\u767B\u5F55\u65F6\u7684\u5730\u5740",
|
|
2830
|
+
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
2831
|
+
].join("\n")
|
|
2832
|
+
},
|
|
2833
|
+
videos: {
|
|
2834
|
+
run: videos,
|
|
2835
|
+
acceptsBaseUrl: true,
|
|
2836
|
+
options: [
|
|
2837
|
+
"product",
|
|
2838
|
+
"intent",
|
|
2839
|
+
"duration",
|
|
2840
|
+
"resolution",
|
|
2841
|
+
"speed",
|
|
2842
|
+
"aspect-ratio",
|
|
2843
|
+
"style-preset",
|
|
2844
|
+
"text",
|
|
2845
|
+
"yes",
|
|
2846
|
+
"wait",
|
|
2847
|
+
"timeout",
|
|
2848
|
+
"out",
|
|
2849
|
+
"idempotency-key"
|
|
2850
|
+
],
|
|
2851
|
+
positionals: ["\u5B50\u547D\u4EE4", "\u89C6\u9891id"],
|
|
2852
|
+
help: [
|
|
2853
|
+
"\u7528\u6CD5\uFF1A",
|
|
2854
|
+
" buke videos create --product <\u4EA7\u54C1id> [--intent <\u4E00\u53E5\u8BDD>] [--duration <\u79D2>] [--resolution <r>]",
|
|
2855
|
+
" [--speed <\u6863>] [--aspect-ratio <a>] [--style-preset <p>] [--json]",
|
|
2856
|
+
" buke videos status <\u89C6\u9891id> [--wait] [--timeout <\u79D2>]",
|
|
2857
|
+
" buke videos message <\u89C6\u9891id> --text <\u5185\u5BB9>",
|
|
2858
|
+
" buke videos confirm <\u89C6\u9891id> [--yes]",
|
|
2859
|
+
" buke videos download <\u89C6\u9891id> [--out <\u8DEF\u5F84>]",
|
|
2860
|
+
"",
|
|
2861
|
+
"\u5546\u54C1\u89C6\u9891\uFF08\u5BFC\u6F14\u5168\u7A0B\u7F16\u6392\u7684\u90A3\u6761\u4EA7\u7EBF\uFF09\uFF1A",
|
|
2862
|
+
" create \u4E0B\u5355\uFF1B\u4EA7\u54C1\u5148\u7528 buke products \u5EFA\u597D\u3002\u53D7\u7406\u540E\u8F6E\u8BE2 status \u53D6\u8FDB\u5EA6",
|
|
2863
|
+
" status \u67E5\u8FDB\u5EA6\uFF1B--wait \u4E00\u76F4\u7B49\u5230\u7EC8\u6001",
|
|
2864
|
+
" message \u7ED9\u5BFC\u6F14\u56DE\u4E00\u53E5\u8BDD\uFF08\u8865\u610F\u56FE\u3001\u56DE\u7B54\u63D0\u95EE\uFF09",
|
|
2865
|
+
" confirm \u786E\u8BA4\u5F00\u62CD\uFF1A\u628A\u670D\u52A1\u7AEF\u6302\u51FA\u7684\u90A3\u5F20\u5361\u539F\u6837\u56DE\u4F20\uFF0C\u94B1\u5728\u505C\u70B9\u5F53\u9762\u8981\u6388\u6743",
|
|
2866
|
+
" download \u4E0B\u8F7D\u6210\u7247\uFF08mp4\uFF09",
|
|
2867
|
+
"",
|
|
2868
|
+
"\u9009\u9879\uFF1A",
|
|
2869
|
+
" --product <\u4EA7\u54C1id> \u4EA7\u54C1 id\uFF08buke products create \u56DE\u7684\uFF09",
|
|
2870
|
+
" --intent <\u4E00\u53E5\u8BDD> \u8FD9\u6761\u7247\u5B50\u60F3\u8BB2\u4EC0\u4E48\uFF1B\u4E0D\u7ED9\u65F6\u7531\u5BFC\u6F14\u6309\u4EA7\u54C1\u81EA\u5DF1\u60F3",
|
|
2871
|
+
" --duration <\u79D2> \u671F\u671B\u65F6\u957F",
|
|
2872
|
+
" --resolution <r> \u5206\u8FA8\u7387\u6863\u4F4D",
|
|
2873
|
+
" --speed <\u6863> \u751F\u6210\u901F\u5EA6\u6863\u4F4D",
|
|
2874
|
+
" --aspect-ratio <a> \u753B\u5E45\uFF08\u5982 9:16\uFF09",
|
|
2875
|
+
" --style-preset <p> \u753B\u9762\u98CE\u683C",
|
|
2876
|
+
" --text <\u5185\u5BB9> \u8981\u5E26\u7ED9\u5BFC\u6F14\u7684\u90A3\u53E5\u8BDD",
|
|
2877
|
+
" --yes confirm \u65F6\u771F\u7684\u63D0\u4EA4\uFF1B\u4E0D\u7ED9\u65F6\u53EA\u663E\u793A\u90A3\u5F20\u5361",
|
|
2878
|
+
" --wait \u4E00\u76F4\u7B49\u5230\u7EC8\u6001\uFF1A\u6210\u529F\u9000\u51FA 0\uFF0C\u4EFB\u52A1\u5931\u8D25\u9000\u51FA 9\uFF0C\u5230\u65F6\u9650\u9000\u51FA 8",
|
|
2879
|
+
" --timeout <\u79D2> --wait \u7684\u65F6\u9650\uFF0C\u9ED8\u8BA4 600",
|
|
2880
|
+
" --out <\u8DEF\u5F84> \u4E0B\u8F7D\u4FDD\u5B58\u7684\u4F4D\u7F6E\uFF1B\u4E0D\u7ED9\u65F6\u5B58\u5230\u5F53\u524D\u76EE\u5F55",
|
|
2881
|
+
" --idempotency-key <\u952E> \u5E42\u7B49\u952E\uFF1B\u4E0D\u7ED9\u65F6\u81EA\u52A8\u751F\u6210\u5E76\u5728\u8F93\u51FA\u91CC\u56DE\u663E\uFF0C\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u539F\u6837\u590D\u7528",
|
|
2882
|
+
" --base-url <\u5730\u5740> \u670D\u52A1\u5730\u5740\uFF1B\u4E0D\u7ED9\u65F6\u7528\u767B\u5F55\u65F6\u7684\u5730\u5740",
|
|
2883
|
+
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
2884
|
+
].join("\n")
|
|
2885
|
+
},
|
|
2886
|
+
drama: {
|
|
2887
|
+
run: drama,
|
|
2888
|
+
acceptsBaseUrl: true,
|
|
2889
|
+
options: ["product", "genre", "duration", "brief", "quote-hash", "yes", "wait", "timeout", "out", "idempotency-key"],
|
|
2890
|
+
positionals: ["\u5B50\u547D\u4EE4", "\u8BA2\u5355id"],
|
|
2891
|
+
help: [
|
|
2892
|
+
"\u7528\u6CD5\uFF1A",
|
|
2893
|
+
" buke drama create --product <\u4EA7\u54C1id> [--genre <\u9898\u6750id>] [--duration 30s|60s|90s] [--brief <JSON \u6587\u4EF6|->] [--json]",
|
|
2894
|
+
" buke drama status <\u8BA2\u5355id> [--wait] [--timeout <\u79D2>]",
|
|
2895
|
+
" buke drama quote <\u8BA2\u5355id>",
|
|
2896
|
+
" buke drama confirm <\u8BA2\u5355id> --quote-hash <\u62A5\u4EF7\u54C8\u5E0C> [--yes]",
|
|
2897
|
+
" buke drama cancel <\u8BA2\u5355id>",
|
|
2898
|
+
" buke drama download <\u8BA2\u5355id> [--out <\u8DEF\u5F84>]",
|
|
2899
|
+
"",
|
|
2900
|
+
"\u77ED\u5267\uFF08\u65E0\u4EBA\u503C\u5B88\u7F16\u6392\u5668\uFF09\uFF1A\u5E73\u53F0\u5148\u8DD1\u5199\u7A3F\u3001\u7ED1\u4EBA\u4E0E\u6392\u7248\u5206\u955C\uFF0C\u5230\u53EF\u62A5\u4EF7\u505C\u4E0B\u7B49\u4F60\u786E\u8BA4\uFF1B\u786E\u8BA4\u5F00\u62CD\u662F\u552F\u4E00\u6263\u8D39\u5165\u53E3\u3002",
|
|
2901
|
+
" create \u4E0B\u5355\uFF1B\u4EA7\u54C1\u5148\u7528 buke products \u5EFA\u597D\u3002\u53D7\u7406\u540E\u8F6E\u8BE2 status \u53D6\u8FDB\u5EA6",
|
|
2902
|
+
" status \u67E5\u8FDB\u5EA6\uFF08\u67E5\u8BE2\u672C\u8EAB\u4F1A\u63A8\u8FDB\u6392\u7248\uFF09\uFF1B--wait \u4E00\u76F4\u7B49\u5230\u7EC8\u6001\u6216\u505C\u9760",
|
|
2903
|
+
" quote \u53EA\u8BFB\u62A5\u4EF7\uFF1A\u62A5\u4EF7\u8981\u7D20\u4E0E\u62A5\u4EF7\u54C8\u5E0C\uFF0C\u786E\u8BA4\u5F00\u62CD\u65F6\u539F\u6837\u5E26\u56DE",
|
|
2904
|
+
" confirm \u786E\u8BA4\u5F00\u62CD\uFF1A\u5148\u53D6\u4E00\u6B21\u5F53\u524D\u62A5\u4EF7\u7ED9\u4EBA\u770B\uFF0C\u52A0 --yes \u624D\u63D0\u4EA4\u8FD9\u7B14\u5E03\u652F\u51FA",
|
|
2905
|
+
" cancel \u53D6\u6D88\uFF1A\u6CA1\u62CD\u7684\u955C\u5934\u9000\u56DE\u51BB\u7ED3\u989D\u5EA6\uFF0C\u5DF2\u7ECF\u62CD\u6210\u7684\u4FDD\u7559\u7167\u6263",
|
|
2906
|
+
" download \u4E0B\u8F7D\u76F4\u62FC\u597D\u7684\u6210\u7247\uFF08mp4\uFF09",
|
|
2907
|
+
"",
|
|
2908
|
+
"\u9009\u9879\uFF1A",
|
|
2909
|
+
" --product <\u4EA7\u54C1id> \u4EA7\u54C1 id\uFF08buke products create \u56DE\u7684\uFF09",
|
|
2910
|
+
" --genre <\u9898\u6750id> \u9898\u6750 id\uFF08\u5982 counterattack\u3001urban-ceo\u3001family\uFF09",
|
|
2911
|
+
" --duration <\u4F53\u91CF> \u6210\u7247\u4F53\u91CF\uFF1A30s\u300160s \u6216 90s\uFF1B\u4E0D\u7ED9\u65F6 30s",
|
|
2912
|
+
" --brief <JSON \u6587\u4EF6|-> \u6545\u4E8B\u8BBE\u5B9A\uFF1Adescription\u3001forbidden\u3001storyTone \u4E09\u9879\uFF0C\u4E00\u4E2A JSON \u6587\u4EF6\u6216 - \u8868\u793A\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB",
|
|
2913
|
+
" --quote-hash <\u54C8\u5E0C> \u62A5\u4EF7\u54C8\u5E0C\uFF08buke drama quote \u56DE\u7684\uFF09\uFF0C\u786E\u8BA4\u5F00\u62CD\u65F6\u539F\u6837\u5E26\u56DE",
|
|
2914
|
+
" --yes confirm \u65F6\u771F\u7684\u63D0\u4EA4\u5E76\u5F00\u62CD\uFF1B\u4E0D\u7ED9\u65F6\u53EA\u770B\u5F53\u524D\u62A5\u4EF7",
|
|
2915
|
+
" --wait \u4E00\u76F4\u7B49\u5230\u7EC8\u6001\u6216\u505C\u9760\uFF1A\u6210\u529F\u9000\u51FA 0\uFF0C\u5931\u8D25\u6216\u53D6\u6D88\u9000\u51FA 9\uFF0C\u5230\u65F6\u9650\u9000\u51FA 8",
|
|
2916
|
+
" --timeout <\u79D2> --wait \u7684\u65F6\u9650\uFF0C\u9ED8\u8BA4 600",
|
|
2917
|
+
" --out <\u8DEF\u5F84> \u4E0B\u8F7D\u4FDD\u5B58\u7684\u4F4D\u7F6E\uFF1B\u4E0D\u7ED9\u65F6\u5B58\u5230\u5F53\u524D\u76EE\u5F55",
|
|
2918
|
+
" --idempotency-key <\u952E> \u5E42\u7B49\u952E\uFF1B\u4E0D\u7ED9\u65F6\u81EA\u52A8\u751F\u6210\u5E76\u5728\u8F93\u51FA\u91CC\u56DE\u663E\uFF0C\u91CD\u8BD5\u540C\u4E00\u6B21\u8BF7\u6C42\u65F6\u539F\u6837\u590D\u7528",
|
|
2919
|
+
" --base-url <\u5730\u5740> \u670D\u52A1\u5730\u5740\uFF1B\u4E0D\u7ED9\u65F6\u7528\u767B\u5F55\u65F6\u7684\u5730\u5740",
|
|
2920
|
+
" --json \u53EA\u5728\u6807\u51C6\u8F93\u51FA\u6253\u5370\u4E00\u4E2A JSON \u6587\u6863\uFF0C\u65B9\u4FBF\u811A\u672C\u8BFB\u53D6"
|
|
2921
|
+
].join("\n")
|
|
1104
2922
|
}
|
|
1105
2923
|
};
|
|
1106
2924
|
var GENERAL_HELP = [
|
|
1107
2925
|
"buke\uFF1A\u300A\u4E0D\u53EF\u601D\u8BAE\u300B\u7684\u547D\u4EE4\u884C\u5DE5\u5177",
|
|
1108
2926
|
"",
|
|
1109
2927
|
"\u7528\u6CD5\uFF1A",
|
|
1110
|
-
" buke login
|
|
1111
|
-
" buke whoami
|
|
1112
|
-
" buke logout
|
|
2928
|
+
" buke login [--base-url <\u5730\u5740>] [--json] \u7528\u4E00\u628A API \u5BC6\u94A5\u767B\u5F55\uFF0C\u5E76\u628A\u5B83\u4FDD\u5B58\u5728\u672C\u673A",
|
|
2929
|
+
" buke whoami [--base-url <\u5730\u5740>] [--json] \u67E5\u770B\u5F53\u524D\u7528\u7684\u662F\u54EA\u628A\u5BC6\u94A5\u3001\u5C5E\u4E8E\u54EA\u4E2A\u56E2\u961F",
|
|
2930
|
+
" buke logout [--json] \u9000\u51FA\u767B\u5F55\uFF0C\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5BC6\u94A5",
|
|
2931
|
+
" buke balance [--json] \u67E5\u8BE2\u8D26\u6237\u4F59\u989D",
|
|
2932
|
+
" buke products <\u5B50\u547D\u4EE4> [\u53C2\u6570] \u4EA7\u54C1\u5EFA\u6863\uFF1Acreate\u3001set-images\u3001confirm-lock",
|
|
2933
|
+
" buke shorts <\u5B50\u547D\u4EE4> [\u53C2\u6570] \u6E32\u67D3\u77ED\u7247\uFF1Acatalog\u3001upload\u3001quote\u3001order\u3001status\u3001download\u3001cancel",
|
|
2934
|
+
" buke videos <\u5B50\u547D\u4EE4> [\u53C2\u6570] \u5546\u54C1\u89C6\u9891\uFF1Acreate\u3001status\u3001message\u3001confirm\u3001download",
|
|
2935
|
+
" buke drama <\u5B50\u547D\u4EE4> [\u53C2\u6570] \u77ED\u5267\uFF1Acreate\u3001status\u3001quote\u3001confirm\u3001cancel\u3001download",
|
|
1113
2936
|
" buke --help | -h \u663E\u793A\u8FD9\u4EFD\u8BF4\u660E\uFF1Bbuke <\u547D\u4EE4> --help \u770B\u5355\u6761\u547D\u4EE4",
|
|
1114
2937
|
" buke --version | -v \u663E\u793A\u7248\u672C\u53F7",
|
|
1115
2938
|
"",
|
|
@@ -1122,7 +2945,7 @@ var GENERAL_HELP = [
|
|
|
1122
2945
|
...KEY_HOWTO,
|
|
1123
2946
|
"",
|
|
1124
2947
|
"\u73AF\u5883\u53D8\u91CF\uFF1A",
|
|
1125
|
-
" BUKE_API_KEY API \u5BC6\u94A5\uFF1B\u8BBE\u4E86\u5B83\uFF0Clogin \u548C
|
|
2948
|
+
" BUKE_API_KEY API \u5BC6\u94A5\uFF1B\u8BBE\u4E86\u5B83\uFF0Clogin \u548C\u522B\u7684\u547D\u4EE4\u90FD\u76F4\u63A5\u7528\u5B83",
|
|
1126
2949
|
" BUKE_BASE_URL \u670D\u52A1\u5730\u5740\uFF0C\u4F5C\u7528\u540C --base-url",
|
|
1127
2950
|
" BUKE_CONFIG_DIR \u4FDD\u5B58\u51ED\u636E\u7684\u76EE\u5F55\uFF0C\u8981\u5199\u7EDD\u5BF9\u8DEF\u5F84\u3002\u4E0D\u8BBE\u65F6\u662F $XDG_CONFIG_HOME/buke\uFF1B",
|
|
1128
2951
|
" \u8FD9\u4E2A\u4E5F\u6CA1\u8BBE\u5C31\u662F ~/.config/buke\uFF08Windows \u4E0A\u662F %APPDATA%\\buke\uFF09",
|
|
@@ -1134,34 +2957,63 @@ var GENERAL_HELP = [
|
|
|
1134
2957
|
" 4 \u4F59\u989D\u4E0D\u8DB3",
|
|
1135
2958
|
" 5 \u670D\u52A1\u7AEF\u6216\u7F51\u7EDC\u51FA\u9519\uFF1B\u672C\u673A\u7684\u51ED\u636E\u6587\u4EF6\u8BFB\u4E0D\u4E86\u3001\u5199\u4E0D\u4E86\u4E5F\u662F\u8FD9\u4E2A\u7801",
|
|
1136
2959
|
" 6 \u62A5\u4EF7\u53D8\u4E86",
|
|
1137
|
-
" 7 \u8BF7\u6C42\u592A\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",
|
|
2960
|
+
" 7 \u8BF7\u6C42\u592A\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF1B\u6216\u8FD9\u4E00\u6B65\u8FD8\u6CA1\u5230\u65F6\u5019\uFF0C\u7A0D\u540E\u518D\u6765",
|
|
1138
2961
|
" 8 \u7B49\u5F85\u8D85\u65F6",
|
|
1139
2962
|
" 9 \u4EFB\u52A1\u5931\u8D25",
|
|
1140
2963
|
" 10 \u8981\u627E\u7684\u4E1C\u897F\u4E0D\u5B58\u5728",
|
|
1141
2964
|
" 130 \u88AB Ctrl-C \u6253\u65AD",
|
|
1142
2965
|
"",
|
|
1143
|
-
"\
|
|
2966
|
+
"\u8FD9\u4E00\u7248\u80FD\u4E0B\u5355\u3001\u67E5\u8FDB\u5EA6\u3001\u4E0B\u8F7D\u6210\u7247\uFF08\u6E32\u67D3\u77ED\u7247\u3001\u5546\u54C1\u89C6\u9891\u4E0E\u77ED\u5267\u4E09\u6761\u7EBF\uFF09\u3002"
|
|
1144
2967
|
].join("\n");
|
|
1145
2968
|
var MAX_ECHO_LENGTH = 40;
|
|
1146
2969
|
function echo(item) {
|
|
1147
2970
|
return item.length > MAX_ECHO_LENGTH ? `${item.slice(0, MAX_ECHO_LENGTH)}\u2026` : item;
|
|
1148
2971
|
}
|
|
2972
|
+
var GLOBAL_OPTIONS = /* @__PURE__ */ new Set(["help", "version", "json", "base-url"]);
|
|
2973
|
+
var STRING_OPTIONS = /* @__PURE__ */ new Set([
|
|
2974
|
+
"base-url",
|
|
2975
|
+
"idempotency-key",
|
|
2976
|
+
"name",
|
|
2977
|
+
"category",
|
|
2978
|
+
"front",
|
|
2979
|
+
"side",
|
|
2980
|
+
"back",
|
|
2981
|
+
"detail",
|
|
2982
|
+
"color",
|
|
2983
|
+
"silhouette",
|
|
2984
|
+
"material",
|
|
2985
|
+
"template",
|
|
2986
|
+
"props",
|
|
2987
|
+
"quote-hash",
|
|
2988
|
+
"timeout",
|
|
2989
|
+
"out",
|
|
2990
|
+
"product",
|
|
2991
|
+
"intent",
|
|
2992
|
+
"duration",
|
|
2993
|
+
"resolution",
|
|
2994
|
+
"speed",
|
|
2995
|
+
"aspect-ratio",
|
|
2996
|
+
"style-preset",
|
|
2997
|
+
"text",
|
|
2998
|
+
"genre",
|
|
2999
|
+
"brief"
|
|
3000
|
+
]);
|
|
3001
|
+
var BOOLEAN_OPTIONS = /* @__PURE__ */ new Set(["help", "version", "json", "wait", "yes"]);
|
|
1149
3002
|
function parse(argv) {
|
|
1150
3003
|
try {
|
|
3004
|
+
const options = {};
|
|
3005
|
+
for (const name2 of STRING_OPTIONS) options[name2] = { type: "string" };
|
|
3006
|
+
for (const name2 of BOOLEAN_OPTIONS) options[name2] = { type: "boolean", ...name2 === "help" ? { short: "h" } : name2 === "version" ? { short: "v" } : {} };
|
|
1151
3007
|
const { values, positionals } = parseArgs({
|
|
1152
3008
|
args: argv,
|
|
1153
|
-
options
|
|
1154
|
-
help: { type: "boolean", short: "h" },
|
|
1155
|
-
version: { type: "boolean", short: "v" },
|
|
1156
|
-
json: { type: "boolean" },
|
|
1157
|
-
"base-url": { type: "string" }
|
|
1158
|
-
},
|
|
3009
|
+
options,
|
|
1159
3010
|
strict: true,
|
|
1160
3011
|
allowPositionals: true
|
|
1161
3012
|
});
|
|
1162
3013
|
return {
|
|
1163
3014
|
command: positionals[0],
|
|
1164
|
-
|
|
3015
|
+
positionals,
|
|
3016
|
+
values,
|
|
1165
3017
|
help: values.help === true,
|
|
1166
3018
|
version: values.version === true,
|
|
1167
3019
|
baseUrl: values["base-url"]
|
|
@@ -1193,9 +3045,21 @@ async function dispatch(argv, io, out) {
|
|
|
1193
3045
|
const parsed = parse(argv);
|
|
1194
3046
|
const command = parsed.command !== void 0 && Object.hasOwn(COMMANDS, parsed.command) ? COMMANDS[parsed.command] : void 0;
|
|
1195
3047
|
if (parsed.command !== void 0 && !command) throw usageError(`\u4E0D\u8BA4\u8BC6\u7684\u547D\u4EE4 ${echo(parsed.command)}`);
|
|
1196
|
-
|
|
1197
|
-
if (command
|
|
1198
|
-
|
|
3048
|
+
const args = parsed.positionals.slice(1);
|
|
3049
|
+
if (command) {
|
|
3050
|
+
if (command.positionals.length === 0 && args.length > 0) {
|
|
3051
|
+
throw usageError(`${parsed.command} \u540E\u9762\u4E0D\u63A5\u522B\u7684\u53C2\u6570`);
|
|
3052
|
+
}
|
|
3053
|
+
if (args.length > command.positionals.length) {
|
|
3054
|
+
throw usageError(`${parsed.command} \u540E\u9762\u7684\u53C2\u6570\u591A\u4E86\uFF08${echo(args[command.positionals.length])}\uFF09`);
|
|
3055
|
+
}
|
|
3056
|
+
if (!command.acceptsBaseUrl && parsed.baseUrl !== void 0) {
|
|
3057
|
+
throw usageError(`${parsed.command} \u4E0D\u8BA4\u8BC6\u9009\u9879 --base-url`);
|
|
3058
|
+
}
|
|
3059
|
+
for (const [name2, value] of Object.entries(parsed.values)) {
|
|
3060
|
+
if (value === void 0 || GLOBAL_OPTIONS.has(name2)) continue;
|
|
3061
|
+
if (!command.options.includes(name2)) throw usageError(`${parsed.command} \u4E0D\u8BA4\u8BC6\u9009\u9879 --${name2}`);
|
|
3062
|
+
}
|
|
1199
3063
|
}
|
|
1200
3064
|
if (parsed.help || !command && !parsed.version) {
|
|
1201
3065
|
const text = command ? command.help : GENERAL_HELP;
|
|
@@ -1206,7 +3070,7 @@ async function dispatch(argv, io, out) {
|
|
|
1206
3070
|
out.result([`${PACKAGE_NAME}/${VERSION}`], { name: PACKAGE_NAME, version: VERSION });
|
|
1207
3071
|
return;
|
|
1208
3072
|
}
|
|
1209
|
-
if (command) await command.run({ io, out, baseUrl: parsed.baseUrl });
|
|
3073
|
+
if (command) await command.run({ io, out, baseUrl: parsed.baseUrl, args, values: parsed.values });
|
|
1210
3074
|
}
|
|
1211
3075
|
async function run(argv, io) {
|
|
1212
3076
|
let out;
|