@qmuse/qmuse-cli 1.0.12 → 1.0.13

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 发布域(C10):POST /api/deploy/create + update(触发执行),GET /api/deploy/query | list | last。
2
+ * 发布域(C10):POST /api/deploy/create,GET /api/deploy/query | list | last。
3
3
  * 部署单状态机:INIT → INITED(安全检查通过)→ DEPLOYING → DEPLOYED / DEPLOY_FAILED / CANCEL。
4
4
  * query 返回 content(infosec/mosec/域名发布状态/发布地址),供 release status 汇总。
5
5
  */
@@ -18,10 +18,6 @@ export function parseDeployTicketContent(ticket) {
18
18
  export function createDeployTicket(appId, versionId, cfg) {
19
19
  return apiCall('POST', '/api/deploy/create', { appId, versionId }, cfg);
20
20
  }
21
- /** 触发已就绪(INITED)部署单执行发布;服务端异步执行 DEPLOYING → 终态。 */
22
- export function triggerDeployTicket(deployId, cfg) {
23
- return apiCall('POST', '/api/deploy/update', { id: deployId }, cfg);
24
- }
25
21
  /** C10.03 查询发布结果:同一查询返回任务详情、产物/安全检查、地址与失败原因。 */
26
22
  export function queryDeployTicket(deployId, cfg) {
27
23
  return apiCall('GET', `/api/deploy/query?id=${encodeURIComponent(deployId)}`, undefined, cfg);
@@ -38,7 +34,7 @@ export function sleep(ms) {
38
34
  return new Promise(resolve => setTimeout(resolve, ms));
39
35
  }
40
36
  /**
41
- * 发布等待循环:CREATE 后轮询 query;INITED 时触发 update(复用现有发布流程);
37
+ * 发布等待循环:CREATE 后轮询 query;服务端 query 将 INITED 推进为 DEPLOYING 并执行发布;
42
38
  * 终态、--no-wait 受理(DEPLOYING)或超时返回最近一次 ticket。
43
39
  * 温和退避(5s→15s 封顶)降低轮询请求量;连续网络错误快速失败。
44
40
  */
@@ -47,7 +43,6 @@ export async function waitDeployResult(deployId, cfg, options = {}) {
47
43
  const timeoutMs = options.timeoutMs ?? (noWait ? 5 * 60 * 1000 : 8 * 60 * 1000);
48
44
  const baseIntervalMs = options.intervalMs ?? 5000;
49
45
  const startedAt = Date.now();
50
- let triggered = false;
51
46
  let lastError;
52
47
  let polls = 0;
53
48
  let netErrors = 0;
@@ -59,7 +54,7 @@ export async function waitDeployResult(deployId, cfg, options = {}) {
59
54
  netErrors += 1;
60
55
  if (netErrors >= 3) {
61
56
  lastError = `连续 ${netErrors} 次网络错误:${r.errorMessage ?? 'network'}`;
62
- return { ticket: undefined, triggered, timedOut: false, lastError };
57
+ return { ticket: undefined, timedOut: false, lastError };
63
58
  }
64
59
  }
65
60
  else {
@@ -67,37 +62,27 @@ export async function waitDeployResult(deployId, cfg, options = {}) {
67
62
  }
68
63
  const ticket = r.ok ? r.data : undefined;
69
64
  const status = typeof ticket?.['status'] === 'string' ? ticket['status'] : undefined;
70
- if (!r.ok) {
65
+ if (r.ok) {
66
+ lastError = undefined;
67
+ }
68
+ else {
71
69
  lastError = r.errorMessage ?? r.errorCode ?? `HTTP ${r.status}`;
72
70
  // 快速失败:单据不存在/无权限时继续轮询没有意义(避免长达数分钟的空转)。
73
71
  const notFound = r.status === 404
74
72
  || r.errorCode === 'NOT_FOUND'
75
73
  || /not found|不存在|无权限|forbidden/iu.test(String(lastError));
76
74
  if (notFound) {
77
- return { ticket: ticket ?? undefined, triggered, timedOut: false, lastError };
75
+ return { ticket: ticket ?? undefined, timedOut: false, lastError };
78
76
  }
79
77
  }
80
78
  if (status && APP_DEPLOY_TERMINAL_STATUSES.has(status)) {
81
- return { ticket: ticket ?? undefined, triggered, timedOut: false, lastError };
82
- }
83
- if (status === 'INITED' && !triggered) {
84
- // 安全门禁通过:触发现有发布流程(INITED → DEPLOYING,服务端异步执行)。
85
- const up = await triggerDeployTicket(deployId, cfg);
86
- triggered = true;
87
- if (!up.ok)
88
- lastError = up.errorMessage ?? up.errorCode ?? `HTTP ${up.status}`; // 触发失败语义保留(不重触发)
89
- const updated = up.ok ? up.data : undefined;
90
- const next = typeof updated?.['status'] === 'string' ? updated['status'] : undefined;
91
- if (next && APP_DEPLOY_TERMINAL_STATUSES.has(next))
92
- return { ticket: updated, triggered, timedOut: false };
93
- if (updated && noWait)
94
- return { ticket: updated, triggered, timedOut: false };
79
+ return { ticket: ticket ?? undefined, timedOut: false, lastError };
95
80
  }
96
- if (noWait && (triggered || (status && status !== 'INIT'))) {
97
- return { ticket: ticket ?? undefined, triggered, timedOut: false, lastError };
81
+ if (noWait && status === 'DEPLOYING') {
82
+ return { ticket: ticket ?? undefined, timedOut: false, lastError };
98
83
  }
99
84
  if (Date.now() - startedAt > timeoutMs)
100
- return { ticket: ticket ?? undefined, triggered, timedOut: true, lastError };
85
+ return { ticket: ticket ?? undefined, timedOut: true, lastError };
101
86
  // 温和退避:每 4 轮 ×1.6,封顶 15s(8 分钟窗口轮询请求减半以上)。
102
87
  const intervalMs = Math.min(baseIntervalMs * Math.pow(1.6, Math.floor(polls / 4)), 15000);
103
88
  await sleep(Math.round(intervalMs));
package/dist/cli.js CHANGED
@@ -876,6 +876,22 @@ function summarizeDeployTicket(ticket) {
876
876
  updatedAt: ticket.updatedAt ?? ticket.gmtModified ?? undefined,
877
877
  };
878
878
  }
879
+ /** 同一应用的发布结果共用一次详情查询,补齐发布单未携带的正式访问地址。 */
880
+ async function summarizeDeployTickets(tickets, cfg) {
881
+ const summaries = tickets.map(summarizeDeployTicket);
882
+ const published = summaries.find(item => item.status === 'DEPLOYED' && !item.publishUrl);
883
+ if (typeof published?.appId === 'string') {
884
+ const app = await queryAppInfo(published.appId, cfg);
885
+ if (app.ok && app.data) {
886
+ const publishUrl = app.data.domain ? `https://${app.data.domain}` : app.data.url;
887
+ for (const summary of summaries) {
888
+ if (summary.status === 'DEPLOYED' && !summary.publishUrl)
889
+ summary.publishUrl = publishUrl;
890
+ }
891
+ }
892
+ }
893
+ return summaries;
894
+ }
879
895
  async function cmdDeploy(command, flags) {
880
896
  const cfg = resolveCfg(flags);
881
897
  requireAuth(cfg);
@@ -900,6 +916,7 @@ async function cmdDeploy(command, flags) {
900
916
  const success = !wait.timedOut && !!wait.ticket
901
917
  && wait.ticket.status !== 'DEPLOY_FAILED' && wait.ticket.status !== 'CANCEL'
902
918
  && (!wait.lastError || wait.ticket.status === 'DEPLOYED');
919
+ const [summary] = await summarizeDeployTickets([wait.ticket ?? seed], cfg);
903
920
  const payload = {
904
921
  success,
905
922
  appId,
@@ -907,7 +924,7 @@ async function cmdDeploy(command, flags) {
907
924
  deployId: deployId || undefined,
908
925
  timedOut: wait.timedOut || undefined,
909
926
  error: wait.lastError,
910
- ...summarizeDeployTicket(wait.ticket ?? seed),
927
+ ...summary,
911
928
  };
912
929
  if (flags['--json'] === true) {
913
930
  output(payload, flags);
@@ -939,7 +956,7 @@ async function cmdRelease(command, flags) {
939
956
  if (!releaseId)
940
957
  fail('用法: qmuse release status <releaseId> [--wait] 或 qmuse release status --last --app-id <appId>');
941
958
  const wait = await waitDeployResult(releaseId, cfg, { noWait: flags['--wait'] !== true });
942
- const summary = summarizeDeployTicket(wait.ticket);
959
+ const [summary] = await summarizeDeployTickets([wait.ticket], cfg);
943
960
  const lookupFailed = !wait.ticket;
944
961
  const success = !wait.timedOut && !lookupFailed
945
962
  && summary.status !== 'DEPLOY_FAILED' && summary.status !== 'CANCEL'
@@ -962,7 +979,8 @@ async function cmdRelease(command, flags) {
962
979
  const r = await lastDeployTicket(appId, cfg);
963
980
  if (!r.ok)
964
981
  failApi(r, `查询最近发布失败`);
965
- output(summarizeDeployTicket(r.data), flags);
982
+ const [summary] = await summarizeDeployTickets([r.data], cfg);
983
+ output(summary, flags);
966
984
  return;
967
985
  }
968
986
  const r = await listDeployTickets(appId, cfg);
@@ -971,7 +989,7 @@ async function cmdRelease(command, flags) {
971
989
  // 防御性兼容:老网关可能返回包装对象而非数组,运行时仍做数组判定。
972
990
  const payload = r.data;
973
991
  const list = Array.isArray(payload)
974
- ? payload.map(item => summarizeDeployTicket(item))
992
+ ? await summarizeDeployTickets(payload, cfg)
975
993
  : payload;
976
994
  output({ appId, releases: list }, flags);
977
995
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qmuse/qmuse-cli",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "QMuse 命令行客户端:在本地创建/导入 React 项目、与 QMuse 对话、拉取发布与云端调试",
5
5
  "type": "module",
6
6
  "bin": {