bukesiyi-cli 0.3.0 → 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.
Files changed (3) hide show
  1. package/README.md +5 -3
  2. package/dist/main.js +128 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,7 +26,7 @@ npm install -g bukesiyi-cli
26
26
 
27
27
  ```console
28
28
  $ buke --version
29
- bukesiyi-cli/0.3.0
29
+ bukesiyi-cli/0.3.1
30
30
  ```
31
31
 
32
32
  不想全局安装时,可以用 npx 临时运行,写法是 `npx bukesiyi-cli <命令>`,例如 `npx bukesiyi-cli whoami`。下面的例子都直接写 `buke`。
@@ -107,6 +107,8 @@ $ buke login bk_test_0123456789abcdef_……
107
107
 
108
108
  短剧的确认开拍(`buke drama confirm`)要先取一次当前报价摆出来给人看:不加 `--yes` 只看,退出码 2;加了 `--yes` 才提交这笔布支出,并且用服务端认可的报价哈希核对,报价变过就不会扣费。`buke drama status --wait` 会在终态或停靠时停下:拿到成片退出 0;失败、取消退出 9;停在「要人处理」或「有镜没拍成、可补拍」时退出 0,输出里带原因、缺口与下一步。
109
109
 
110
+ 等待失败、超时或轮询断网时,先查询原任务。`--json` 错误文档的 `recovery` 保留任务编号、最后状态及查询命令;`shorts order` 还保留本次幂等键。自定义服务的查询命令会带回原来的 `--base-url`。若提交结果还无法确认,`recovery.acceptance` 为 `unknown`,只保留请求的键和服务地址,不虚构已受理或任务编号。先在 Studio 核对原请求,不要换键重新下单;CLI 不会自动重试。
111
+
110
112
  通用选项:
111
113
 
112
114
  | 选项 | 作用 |
@@ -213,8 +215,8 @@ export NO_PROXY='localhost,127.0.0.1,[::1]'
213
215
  | `CLI_TIMEOUT` | 5 | 等了 15 秒,服务端没有回完。稍后重试;总是超时就检查网络,或者中间有没有代理、防火墙拦着 |
214
216
  | `CLI_REDIRECT` | 5 | 服务器要求跳转到别处;为了不把密钥带过去,没有跟随。核对服务地址(报错的括号里是跳转指向的地方),用正确的地址重新登录 |
215
217
  | `CLI_BAD_RESPONSE` | 5 | 收到的回应不是约定的格式,多半是中间的网关或别的网站回的,括号里是 HTTP 状态码。状态码是 429 或 5xx 时稍后重试;其余的先核对服务地址,重试多半没有用 |
216
- | `CLI_WAIT_TIMEOUT` | 8 | `--wait` 等到时限任务还在跑,任务本身没有失败。话里带当前状态;用 `status` 再查,或加 `--timeout` 延长等待 |
217
- | `CLI_TASK_FAILED` | 9 | 等的那张单或那个任务到了失败的终态,话里是服务端记的原因。改好原因后重新下单(新订单、新幂等键) |
218
+ | `CLI_WAIT_TIMEOUT` | 8 | `--wait` 等到时限任务还在跑,任务本身没有失败。按恢复信息查询原任务,给查询加 `--wait --timeout <秒数>` 继续等待,不要重新下单 |
219
+ | `CLI_TASK_FAILED` | 9 | 原任务到了失败的终态,话里是服务端记的原因。先按恢复信息查询原任务并核对原因、已完成内容和账目;新制作需要另行报价与授权 |
218
220
  | `CLI_INTERRUPTED` | 130 | 命令被打断了:按了 Ctrl-C,或者收到了 SIGTERM、SIGHUP(这两种的退出码是 143、129)。需要时重新运行 |
219
221
  | `CLI_INTERNAL` | 5 | 工具自己出了没有料到的错,正常使用不应该遇到。把完整的报错和运行的命令(不要带密钥)发给客服,联系方式见文末「联系我们」 |
220
222
 
package/dist/main.js CHANGED
@@ -89,6 +89,7 @@ var CliError = class _CliError extends Error {
89
89
  retryAfterSeconds;
90
90
  httpStatus;
91
91
  hints;
92
+ recovery;
92
93
  constructor(init) {
93
94
  super(redactKeys(init.message));
94
95
  this.name = "CliError";
@@ -99,6 +100,7 @@ var CliError = class _CliError extends Error {
99
100
  if (init.retryAfterSeconds !== void 0) this.retryAfterSeconds = init.retryAfterSeconds;
100
101
  if (init.httpStatus !== void 0) this.httpStatus = init.httpStatus;
101
102
  this.hints = init.hints ?? [];
103
+ if (init.recovery !== void 0) this.recovery = init.recovery;
102
104
  }
103
105
  withHint(hint) {
104
106
  return new _CliError({
@@ -109,7 +111,21 @@ var CliError = class _CliError extends Error {
109
111
  traceId: this.traceId,
110
112
  retryAfterSeconds: this.retryAfterSeconds,
111
113
  httpStatus: this.httpStatus,
112
- 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
113
129
  });
114
130
  }
115
131
  };
@@ -396,12 +412,58 @@ async function readNewKey(io) {
396
412
 
397
413
  // package.json
398
414
  var name = "bukesiyi-cli";
399
- var version = "0.3.0";
415
+ var version = "0.3.1";
400
416
 
401
417
  // src/version.ts
402
418
  var PACKAGE_NAME = name;
403
419
  var VERSION = version;
404
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
+
405
467
  // src/client.ts
406
468
  var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
407
469
  var DEFAULT_DOWNLOAD_TIMEOUT_MS = 12e4;
@@ -441,6 +503,7 @@ function unreadableResponse(status4) {
441
503
  code: "CLI_BAD_RESPONSE",
442
504
  exitCode: EXIT.SERVER,
443
505
  retryable,
506
+ httpStatus: status4,
444
507
  message: `\u670D\u52A1\u5668\u56DE\u4E86\u4E00\u4E2A\u770B\u4E0D\u61C2\u7684\u56DE\u5E94\uFF08HTTP ${status4}\uFF09\uFF0C${advice}`,
445
508
  hints: status4 === 404 ? [NOT_FOUND_HINT] : []
446
509
  });
@@ -1215,9 +1278,11 @@ async function awaitTemplateShortOrder(baseUrl, apiKey, start, deps, wait) {
1215
1278
  for (; ; ) {
1216
1279
  const remaining = deadline - wait.now().getTime();
1217
1280
  if (remaining <= 0) return { value: current, timedOut: true };
1218
- await wait.sleep(Math.max(Math.min(interval, remaining), 1));
1219
- interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
1220
- current = await getTemplateShortOrder(baseUrl, apiKey, current.view.id, deps);
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
+ });
1221
1286
  if (current.view.status !== said) {
1222
1287
  wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
1223
1288
  said = current.view.status;
@@ -1235,9 +1300,11 @@ async function awaitVideoRun(baseUrl, apiKey, start, deps, wait) {
1235
1300
  const remaining = deadline - wait.now().getTime();
1236
1301
  if (remaining <= 0) return { value: current, timedOut: true };
1237
1302
  const preferred = current.view.pollAfterMs !== null && current.view.pollAfterMs > 0 ? current.view.pollAfterMs : interval;
1238
- await wait.sleep(Math.max(Math.min(preferred, remaining), 1));
1239
- interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
1240
- current = await getVideoRun(baseUrl, apiKey, current.view.rootRunId, deps);
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
+ });
1241
1308
  if (current.view.status !== said) {
1242
1309
  wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
1243
1310
  said = current.view.status;
@@ -1257,9 +1324,11 @@ async function awaitDramaOrder(baseUrl, apiKey, start, deps, wait) {
1257
1324
  for (; ; ) {
1258
1325
  const remaining = deadline - wait.now().getTime();
1259
1326
  if (remaining <= 0) return { value: current, timedOut: true };
1260
- await wait.sleep(Math.max(Math.min(interval, remaining), 1));
1261
- interval = Math.min(Math.ceil(interval * 1.5), MAX_POLL_INTERVAL_MS);
1262
- current = await getDramaOrder(baseUrl, apiKey, current.view.id, deps);
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
+ });
1263
1332
  if (current.view.status !== said) {
1264
1333
  wait.note?.(`\u72B6\u6001\u53D8\u6210 ${current.view.status}`);
1265
1334
  said = current.view.status;
@@ -1732,20 +1801,23 @@ function waitControls(io, values, note) {
1732
1801
  note
1733
1802
  };
1734
1803
  }
1735
- function settleOrder(outcome) {
1804
+ function settleOrder(outcome, baseUrl) {
1736
1805
  const order2 = outcome.value.view;
1806
+ const recovery = taskRecovery("shorts", order2.id, order2.status, baseUrl);
1737
1807
  if (outcome.timedOut) {
1738
1808
  throw new CliError({
1739
1809
  code: "CLI_WAIT_TIMEOUT",
1740
1810
  exitCode: EXIT.WAIT_TIMEOUT,
1741
- message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${order2.status}\uFF1A\u518D\u67E5\u7528 buke shorts status ${order2.id}\uFF0C\u6216\u52A0 --timeout \u5EF6\u957F\u7B49\u5F85`
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
1742
1813
  });
1743
1814
  }
1744
1815
  if (order2.status === "failed" || order2.status === "canceled") {
1745
1816
  throw new CliError({
1746
1817
  code: "CLI_TASK_FAILED",
1747
1818
  exitCode: EXIT.TASK_FAILED,
1748
- message: order2.error === void 0 ? `\u8FD9\u6761\u77ED\u7247${order2.status === "canceled" ? "\u5DF2\u53D6\u6D88" : "\u6CA1\u6709\u6E32\u67D3\u6210\u529F"}` : singleLine(order2.error)
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
1749
1821
  });
1750
1822
  }
1751
1823
  return outcome.value;
@@ -1822,8 +1894,16 @@ async function order(context) {
1822
1894
  const shouldWait = values.wait === true;
1823
1895
  const controls = shouldWait ? waitControls(io, values, (line) => out.notice(line)) : void 0;
1824
1896
  const { apiKey, baseUrl } = await resolveApiKey(context);
1825
- const accepted = await createTemplateShortOrder(baseUrl, apiKey, templateId, props, quoteHash, idempotencyKey, io);
1826
- const final = shouldWait ? settleOrder(await awaitTemplateShortOrder(baseUrl, apiKey, accepted, io, controls)) : accepted;
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;
1827
1907
  out.result([...orderLines(final.view), `\u5E42\u7B49\u952E\uFF1A${idempotencyKey}`], {
1828
1908
  order: final.raw.order,
1829
1909
  idempotencyKey,
@@ -1837,7 +1917,7 @@ async function status(context, rawId) {
1837
1917
  const controls = shouldWait ? waitControls(io, values, (line) => out.notice(line)) : void 0;
1838
1918
  const { apiKey, baseUrl } = await resolveApiKey(context);
1839
1919
  const current = await getTemplateShortOrder(baseUrl, apiKey, orderId, io);
1840
- const final = shouldWait ? settleOrder(await awaitTemplateShortOrder(baseUrl, apiKey, current, io, controls)) : current;
1920
+ const final = shouldWait ? settleOrder(await awaitTemplateShortOrder(baseUrl, apiKey, current, io, controls), baseUrl) : current;
1841
1921
  out.result(orderLines(final.view), { order: final.raw.order, ...shouldWait ? { waited: true } : {} });
1842
1922
  }
1843
1923
  async function cancel(context, rawId) {
@@ -1905,27 +1985,31 @@ function waitControls2(io, values, note) {
1905
1985
  note
1906
1986
  };
1907
1987
  }
1908
- function settleOrder2(outcome) {
1988
+ function settleOrder2(outcome, baseUrl) {
1909
1989
  const order2 = outcome.value.view;
1990
+ const recovery = taskRecovery("drama", order2.id, order2.status, baseUrl);
1910
1991
  if (outcome.timedOut) {
1911
1992
  throw new CliError({
1912
1993
  code: "CLI_WAIT_TIMEOUT",
1913
1994
  exitCode: EXIT.WAIT_TIMEOUT,
1914
- message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${order2.status}\uFF1A\u518D\u67E5\u7528 buke drama status ${order2.id}\uFF0C\u6216\u52A0 --timeout \u5EF6\u957F\u7B49\u5F85`
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
1915
1997
  });
1916
1998
  }
1917
1999
  if (order2.status === "failed") {
1918
2000
  throw new CliError({
1919
2001
  code: "CLI_TASK_FAILED",
1920
2002
  exitCode: EXIT.TASK_FAILED,
1921
- message: order2.reason === void 0 || order2.reason.trim() === "" ? "\u8FD9\u5355\u77ED\u5267\u6CA1\u6709\u62CD\u6210" : singleLine(order2.reason)
2003
+ message: order2.reason === void 0 || order2.reason.trim() === "" ? "\u8FD9\u5355\u77ED\u5267\u6CA1\u6709\u62CD\u6210" : singleLine(order2.reason),
2004
+ recovery
1922
2005
  });
1923
2006
  }
1924
2007
  if (order2.status === "canceled") {
1925
2008
  throw new CliError({
1926
2009
  code: "CLI_TASK_FAILED",
1927
2010
  exitCode: EXIT.TASK_FAILED,
1928
- 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"
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
1929
2013
  });
1930
2014
  }
1931
2015
  return outcome.value;
@@ -2033,7 +2117,7 @@ async function status2(context, rawId) {
2033
2117
  const controls = shouldWait ? waitControls2(io, values, (line) => out.notice(line)) : void 0;
2034
2118
  const { apiKey, baseUrl } = await resolveApiKey(context);
2035
2119
  const current = await getDramaOrder(baseUrl, apiKey, orderId, io);
2036
- const final = shouldWait ? settleOrder2(await awaitDramaOrder(baseUrl, apiKey, current, io, controls)) : current;
2120
+ const final = shouldWait ? settleOrder2(await awaitDramaOrder(baseUrl, apiKey, current, io, controls), baseUrl) : current;
2037
2121
  out.result([...orderLines2(final.view), ...guidanceLines(final.view)], {
2038
2122
  order: final.raw.order,
2039
2123
  ...shouldWait ? { waited: true } : {}
@@ -2124,13 +2208,27 @@ function errorDocument(error) {
2124
2208
  };
2125
2209
  if (error.retryAfterSeconds !== void 0) document.error.retryAfterSeconds = error.retryAfterSeconds;
2126
2210
  if (error.httpStatus !== void 0) document.error.httpStatus = error.httpStatus;
2211
+ if (error.recovery !== void 0) document.recovery = error.recovery;
2127
2212
  return document;
2128
2213
  }
2129
2214
  function errorLines(error) {
2130
2215
  const details = [`\u9519\u8BEF\u7801 ${singleLine(error.code)}`];
2131
2216
  if (error.traceId) details.push(`\u8BF7\u6C42\u7F16\u53F7 ${singleLine(error.traceId)}`);
2132
2217
  if (error.retryAfterSeconds !== void 0) details.push(`${error.retryAfterSeconds} \u79D2\u540E\u53EF\u4EE5\u91CD\u8BD5`);
2133
- return [`\u9519\u8BEF\uFF1A${singleLine(error.message)}`, `\uFF08${details.join("\uFF1B")}\uFF09`, ...error.hints];
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
+ ];
2134
2232
  }
2135
2233
  function createOutput(streams, json) {
2136
2234
  const secrets = [];
@@ -2393,20 +2491,23 @@ function waitControls3(io, values) {
2393
2491
  sleep: io.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))
2394
2492
  };
2395
2493
  }
2396
- function settleRun(outcome) {
2494
+ function settleRun(outcome, baseUrl) {
2397
2495
  const run2 = outcome.value.view;
2496
+ const recovery = taskRecovery("videos", run2.rootRunId, run2.status, baseUrl, run2.failure?.code);
2398
2497
  if (outcome.timedOut) {
2399
2498
  throw new CliError({
2400
2499
  code: "CLI_WAIT_TIMEOUT",
2401
2500
  exitCode: EXIT.WAIT_TIMEOUT,
2402
- message: `\u7B49\u4E86\u8FD9\u4E48\u4E45\u8FD8\u5728 ${run2.status}\uFF08${run2.stage}\uFF09\uFF1A\u518D\u67E5\u7528 buke videos status ${run2.rootRunId}\uFF0C\u6216\u52A0 --timeout \u5EF6\u957F\u7B49\u5F85`
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
2403
2503
  });
2404
2504
  }
2405
2505
  if (run2.status === "failed" || run2.status === "canceled") {
2406
2506
  throw new CliError({
2407
2507
  code: "CLI_TASK_FAILED",
2408
2508
  exitCode: EXIT.TASK_FAILED,
2409
- 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)}`)
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
2410
2511
  });
2411
2512
  }
2412
2513
  return outcome.value;
@@ -2483,7 +2584,7 @@ async function status3(context, rawId) {
2483
2584
  const controls = shouldWait ? waitControls3(io, values) : void 0;
2484
2585
  const { apiKey, baseUrl } = await resolveApiKey(context);
2485
2586
  const current = await getVideoRun(baseUrl, apiKey, runId, io);
2486
- const final = shouldWait ? settleRun(await awaitVideoRun(baseUrl, apiKey, current, io, controls)) : current;
2587
+ const final = shouldWait ? settleRun(await awaitVideoRun(baseUrl, apiKey, current, io, controls), baseUrl) : current;
2487
2588
  const lastDirectorLine = [...final.view.messages].reverse().find((message2) => message2.role === "assistant" && message2.text.trim() !== "");
2488
2589
  out.result(
2489
2590
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bukesiyi-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "《不可思议》的命令行工具:用 API 密钥调用(命令名 buke)",
5
5
  "type": "module",
6
6
  "bin": {