dsh-opendesign-corouter 0.1.5 → 0.1.7

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/lib/client.js CHANGED
@@ -19927,22 +19927,29 @@ function record2(value) {
19927
19927
  }
19928
19928
 
19929
19929
  // src/device-auth.ts
19930
- function validateActivationUrl(value, userCode) {
19930
+ function validateActivationUrl(value, userCode, deviceId) {
19931
19931
  if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
19932
19932
  try {
19933
19933
  const url2 = new URL(value);
19934
19934
  if (url2.origin !== "https://open-design.ai" || url2.pathname !== "/cloud/cli/activate" || url2.username || url2.password || url2.hash) return null;
19935
19935
  const entries = [...url2.searchParams];
19936
- if (entries.length > 1) return null;
19937
- if (entries.length) {
19936
+ if (entries.length === 2) {
19937
+ if (entries.some(([key]) => key !== "deviceId" && key !== "userCode")) return null;
19938
+ const id = url2.searchParams.get("deviceId");
19939
+ const code = url2.searchParams.get("userCode");
19940
+ if (!validDeviceId(id) || !validUserCode(code) || deviceId !== void 0 && id !== deviceId || userCode !== void 0 && code !== userCode) return null;
19941
+ } else if (entries.length === 1) {
19938
19942
  const entry = entries[0];
19939
19943
  if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
19940
- }
19944
+ } else if (entries.length > 2) return null;
19941
19945
  return url2.href;
19942
19946
  } catch {
19943
19947
  return null;
19944
19948
  }
19945
19949
  }
19950
+ function validDeviceId(value) {
19951
+ return typeof value === "string" && /^[a-z0-9_-]{1,200}$/iu.test(value);
19952
+ }
19946
19953
  function validUserCode(value) {
19947
19954
  return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
19948
19955
  }
@@ -20047,9 +20054,9 @@ function amount(value, label, negative = false) {
20047
20054
  if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
20048
20055
  return value;
20049
20056
  }
20050
- function positiveInteger(value, label, max) {
20057
+ function positiveInteger(value, label, max, min = 1) {
20051
20058
  const result = count(value, label);
20052
- if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
20059
+ if (result < min || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
20053
20060
  return result;
20054
20061
  }
20055
20062
  function parseEfforts(value) {
@@ -20152,9 +20159,9 @@ function parseStatusConfig(value) {
20152
20159
  apiKeyEnv,
20153
20160
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
20154
20161
  ...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
20155
- ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
20156
- ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
20157
- ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
20162
+ ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5, 1e3) },
20163
+ ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5, 1e3) },
20164
+ ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600, 5) }
20158
20165
  };
20159
20166
  }
20160
20167
  function parseStatus(value) {
@@ -20167,9 +20174,13 @@ function parseStatus(value) {
20167
20174
  if (ids.has(model.id)) throw new Error(`\u6A21\u578B\u5217\u8868\u91CD\u590D\u4E86 id "${model.id}"`);
20168
20175
  ids.add(model.id);
20169
20176
  }
20177
+ const apiKeyConfigured = flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA");
20178
+ const apiKeySource = value.apiKeySource === void 0 ? void 0 : member(value.apiKeySource, ["reference", "stored", "device"], "\u63A8\u7406\u51ED\u636E\u6765\u6E90");
20179
+ if (!apiKeyConfigured && apiKeySource !== void 0) throw new Error("\u672A\u914D\u7F6E\u51ED\u636E\u65F6\u4E0D\u80FD\u6307\u5B9A\u63A8\u7406\u51ED\u636E\u6765\u6E90");
20170
20180
  return {
20171
20181
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
20172
- apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
20182
+ apiKeyConfigured,
20183
+ ...apiKeySource === void 0 ? {} : { apiKeySource },
20173
20184
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
20174
20185
  quota: parseQuotaSnapshot(value.quota),
20175
20186
  login: parseLoginState(value.login),
@@ -20257,7 +20268,7 @@ var LOGIN_LABELS = {
20257
20268
  };
20258
20269
  var QUOTA_LABELS = {
20259
20270
  ok: "\u989D\u5EA6\u4FE1\u606F\u5DF2\u66F4\u65B0",
20260
- partial: "\u90E8\u5206\u989D\u5EA6\u6682\u4E0D\u53EF\u7528\uFF0C\u5176\u4F59\u6570\u636E\u4ECD\u53EF\u53C2\u8003",
20271
+ partial: "\u989D\u5EA6\u6570\u636E\u6682\u672A\u5B8C\u6574",
20261
20272
  "need-login": "\u9700\u8981\u767B\u5F55\u540E\u8BFB\u53D6\u989D\u5EA6",
20262
20273
  error: "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u989D\u5EA6"
20263
20274
  };
@@ -20271,7 +20282,9 @@ function installStyles() {
20271
20282
  .odc-tab{flex:none;border:1px solid transparent;border-radius:8px;padding:7px 13px;background:transparent;color:var(--dsw-alias-label-secondary,inherit);font:inherit;cursor:pointer}.odc-tab:hover{background:var(--dsw-alias-bg-layer-1,transparent)}.odc-tab[aria-selected=true]{color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-border-l3,transparent);background:var(--dsw-alias-bg-layer-1,transparent)}
20272
20283
  .odc-panel{display:grid;gap:14px}.odc-card{min-width:0;padding:15px;border:1px solid var(--dsw-alias-border-l4,transparent);border-radius:12px;background:var(--dsw-alias-bg-layer-1,transparent)}.odc-card>p{margin-top:6px}.odc-card-head,.odc-row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.odc-card-head{margin-bottom:12px}
20273
20284
  .odc-quota-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.odc-metric{display:grid;align-content:start;gap:5px;min-height:96px}.odc-metric-value{font-size:20px;font-weight:650;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.odc-metric-value.odc-metric-text{font-size:13px;font-weight:600;line-height:1.55}.odc-quota-grid+.odc-quota-grid{margin-top:10px}
20274
- .odc-label,.odc-hint,.odc-meta{color:var(--dsw-alias-label-secondary,inherit);font-size:11px;line-height:1.6}.odc-meta{margin-top:10px!important}.odc-window{display:grid;gap:7px;margin-top:10px}.odc-window progress{width:100%;height:7px;accent-color:var(--dsw-alias-brand-primary,Highlight)}
20285
+ .odc-label,.odc-hint,.odc-meta{color:var(--dsw-alias-label-secondary,inherit);font-size:11px;line-height:1.6}.odc-meta{margin-top:10px!important}
20286
+ .odc-window{display:grid;gap:10px;margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,transparent);min-width:0}.odc-window-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.odc-window h4{margin:0;font-size:13px;font-weight:600}.odc-window-remaining{display:flex;align-items:baseline;gap:6px;font-variant-numeric:tabular-nums;white-space:nowrap}.odc-window-remaining strong{font-size:22px;line-height:1.2}.odc-window progress{display:block;width:100%;height:8px;accent-color:var(--dsw-alias-brand-primary,Highlight)}
20287
+ .odc-window-usage{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0}.odc-window-usage>div{display:grid;gap:3px;min-width:0}.odc-window dt,.odc-quota-footer dt{color:var(--dsw-alias-label-secondary,inherit);font-size:11px}.odc-window dd,.odc-quota-footer dd{margin:0;min-width:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.odc-window-reset{display:grid;gap:3px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l4,transparent)}.odc-window-reset time{font-size:12px}.odc-window-details>summary{cursor:pointer;font-size:11px;color:var(--dsw-alias-label-secondary,inherit);min-height:32px;padding:6px 0}.odc-window-details dl{display:grid;gap:8px;margin:6px 0 0}.odc-window-details dl>div{display:grid;gap:3px}.odc-quota-footer{display:grid;gap:8px;margin:0;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l4,transparent)}.odc-quota-footer>div{display:grid;grid-template-columns:72px minmax(0,1fr);gap:10px}.odc-quota-footer dd{font-size:11px;color:var(--dsw-alias-label-secondary,inherit)}
20275
20288
  .odc-button{display:inline-flex;align-items:center;justify-content:center;min-height:32px;padding:6px 12px;border:1px solid var(--dsw-alias-border-l3,transparent);border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);font:inherit;font-size:12px;cursor:pointer}.odc-button:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary,Highlight)}.odc-button-primary{background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary,Highlight));color:var(--dsw-alias-label-primary-foreground,HighlightText);border-color:transparent}.odc-button-primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-brand-primary,Highlight))}.odc-button:disabled{opacity:.55;cursor:not-allowed}.odc-button-small{min-height:26px;padding:3px 9px;font-size:11px}.odc-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
20276
20289
  .odc-notice{padding:9px 11px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent);font-size:12px;line-height:1.6}.odc-notice[data-kind=error]{color:var(--dsw-alias-state-error-primary,inherit);color:color-mix(in srgb,var(--dsw-alias-state-error-primary,CanvasText) 85%,var(--dsw-alias-label-primary,CanvasText))}
20277
20290
  .odc-model-groups{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:12px}.odc-model-list{display:grid;gap:6px;margin:10px 0 0;padding:0;list-style:none}.odc-model-item{padding:9px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent);min-width:0}.odc-model-head{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;flex-wrap:wrap}.odc-model-switch{min-width:0;flex:1 1 190px}.odc-model-item strong,.odc-model-item code{display:block;overflow-wrap:anywhere}.odc-model-item strong{font-size:12px;font-weight:600}.odc-model-item code{margin-top:2px;color:var(--dsw-alias-label-secondary,inherit);font-size:11px}.odc-model-meta{margin-top:6px!important;color:var(--dsw-alias-label-secondary,inherit);font-size:11px;overflow-wrap:anywhere}.odc-model-count{color:var(--dsw-alias-label-secondary,inherit);font-size:11px;font-weight:400}.odc-chip{display:inline-flex;align-items:center;padding:1px 7px;border:1px solid var(--dsw-alias-border-l3,transparent);border-radius:999px;color:var(--dsw-alias-label-secondary,inherit);font-size:10px;line-height:1.6}
@@ -20464,19 +20477,49 @@ function QuotaView({ quota, nextAutoRefreshAt, refreshIntervalSeconds }) {
20464
20477
  React.Fragment,
20465
20478
  null,
20466
20479
  h("p", { className: "odc-metric-value" }, "\u2014"),
20467
- h("p", { className: "odc-hint" }, "\u989D\u5EA6\u7A97\u53E3\u6682\u4E0D\u53EF\u786E\u8BA4\uFF1B\u4E0D\u6309 0 \u586B\u5145\u3002")
20468
- ) : quota.windows.map((window, index) => h(
20469
- "div",
20470
- { className: "odc-window", key: `${window.durationSeconds}-${index}` },
20471
- h(
20472
- "div",
20473
- { className: "odc-row" },
20474
- h("span", null, windowLabel(window.durationSeconds)),
20475
- h("span", { className: "odc-label" }, `\u5DF2\u7528 ${window.percent.toFixed(1)}% \xB7 \u5269\u4F59 ${window.remaining ?? "\u2014"}`)
20476
- ),
20477
- h("progress", { max: 100, value: window.percent, "aria-label": `${windowLabel(window.durationSeconds)}\u5DF2\u7528\u989D\u5EA6` }),
20478
- h("span", { className: "odc-meta" }, `\u5DF2\u7528 ${window.used} / \u4E0A\u9650 ${window.limit} \xB7 \u6062\u590D ${formatTime(window.resetsAt)}${window.windowStart === void 0 ? "" : ` \xB7 \u7A97\u53E3\u5F00\u59CB ${formatTime(window.windowStart)}`}${window.resetMode === void 0 ? "" : ` \xB7 \u91CD\u7F6E\u65B9\u5F0F ${RESET_MODE_LABELS[window.resetMode] ?? window.resetMode}`}`)
20479
- ))
20480
+ h("p", { className: "odc-hint" }, "\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u989D\u5EA6\u7A97\u53E3\u3002")
20481
+ ) : quota.windows.map((window, index) => {
20482
+ const label = windowLabel(window.durationSeconds);
20483
+ const remainingPercent = Math.max(0, Math.min(100, 100 - window.percent));
20484
+ return h(
20485
+ "section",
20486
+ { className: "odc-window", key: `${window.durationSeconds}-${index}`, "aria-label": label },
20487
+ h(
20488
+ "div",
20489
+ { className: "odc-window-head" },
20490
+ h("h4", null, label),
20491
+ h(
20492
+ "div",
20493
+ { className: "odc-window-remaining" },
20494
+ h("strong", null, `${remainingPercent.toFixed(1)}%`),
20495
+ h("span", { className: "odc-label" }, "\u5269\u4F59")
20496
+ )
20497
+ ),
20498
+ h("progress", { max: 100, value: remainingPercent, "aria-label": `${label}\u5269\u4F59\u989D\u5EA6` }),
20499
+ h(
20500
+ "dl",
20501
+ { className: "odc-window-usage", "aria-label": "\u7A97\u53E3\u7528\u91CF\u660E\u7EC6" },
20502
+ [{ label: "\u5DF2\u7528", value: window.used }, { label: "\u4E0A\u9650", value: window.limit }, { label: "\u5269\u4F59", value: window.remaining ?? "\u2014" }].map((item) => h("div", { key: item.label }, h("dt", null, item.label), h("dd", null, item.value)))
20503
+ ),
20504
+ h(
20505
+ "div",
20506
+ { className: "odc-window-reset" },
20507
+ h("span", { className: "odc-label" }, "\u4E0B\u6B21\u6062\u590D"),
20508
+ window.resetsAt === void 0 ? h("span", null, "\u2014") : h("time", { dateTime: window.resetsAt }, formatTime(window.resetsAt))
20509
+ ),
20510
+ window.windowStart === void 0 && window.resetMode === void 0 ? null : h(
20511
+ "details",
20512
+ { className: "odc-window-details" },
20513
+ h("summary", null, "\u7A97\u53E3\u8BE6\u60C5"),
20514
+ h(
20515
+ "dl",
20516
+ null,
20517
+ window.windowStart === void 0 ? null : h("div", null, h("dt", null, "\u7A97\u53E3\u5F00\u59CB"), h("dd", null, h("time", { dateTime: window.windowStart }, formatTime(window.windowStart)))),
20518
+ window.resetMode === void 0 ? null : h("div", null, h("dt", null, "\u91CD\u7F6E\u65B9\u5F0F"), h("dd", null, RESET_MODE_LABELS[window.resetMode] ?? window.resetMode))
20519
+ )
20520
+ )
20521
+ );
20522
+ })
20480
20523
  ),
20481
20524
  h("div", { className: "odc-quota-grid" }, metrics.map((metric) => h(
20482
20525
  "div",
@@ -20492,12 +20535,17 @@ function QuotaView({ quota, nextAutoRefreshAt, refreshIntervalSeconds }) {
20492
20535
  h("strong", { className: "odc-metric-value odc-metric-text" }, metric.value),
20493
20536
  h("span", { className: "odc-hint" }, metric.hint)
20494
20537
  ))),
20495
- h("p", { className: "odc-meta" }, `\u66F4\u65B0\u4E8E ${formatTime(quota.updatedAt)}${quota.source !== void 0 ? ` \xB7 \u6765\u6E90\uFF1A${SOURCE_LABELS[quota.source]}\uFF08\u53EA\u8BFB\u5C55\u793A\uFF09` : ""}`),
20538
+ h(
20539
+ "dl",
20540
+ { className: "odc-quota-footer", "aria-label": "\u989D\u5EA6\u6570\u636E\u6765\u6E90\u4E0E\u66F4\u65B0\u65F6\u95F4" },
20541
+ h("div", null, h("dt", null, "\u6700\u8FD1\u66F4\u65B0"), h("dd", null, h("time", { dateTime: quota.updatedAt }, formatTime(quota.updatedAt)))),
20542
+ quota.source === void 0 ? null : h("div", null, h("dt", null, "\u6570\u636E\u6765\u6E90"), h("dd", null, SOURCE_LABELS[quota.source]))
20543
+ ),
20496
20544
  h(
20497
20545
  "details",
20498
20546
  { className: "odc-model-details" },
20499
20547
  h("summary", null, "\u5982\u4F55\u7406\u89E3\u8FD9\u4E9B\u989D\u5EA6\uFF1F"),
20500
- h("p", { className: "odc-hint" }, "\u8D60\u9001\u7A97\u53E3\u4E0E\u94B1\u5305\u4F59\u989D\u5206\u522B\u8BA1\u91CF\u3002\u5957\u9910\u4F59\u989D\u6765\u81EA\u8BA2\u9605\uFF0C\u5145\u503C / \u8D60\u9001\u91D1\u989D\u6765\u81EA\u94B1\u5305\uFF1B\u603B\u989D\u5EA6\u662F\u94B1\u5305\u5F53\u524D\u53EF\u7528\u603B\u989D\u3002\u5B9E\u9645\u6263\u6B3E\u7531\u670D\u52A1\u7AEF\u51B3\u5B9A\u3002\u989D\u5EA6\u6570\u636E\u53EA\u8BFB\u5C55\u793A\uFF0C\u672A\u77E5\u91D1\u989D\u663E\u793A \u2014\u3002")
20548
+ h("p", { className: "odc-hint" }, "\u8D60\u9001\u7A97\u53E3\u663E\u793A\u5468\u671F\u5185\u7684\u4F7F\u7528\u91CF\u3002\u5957\u9910\u4F59\u989D\u548C\u5145\u503C / \u8D60\u9001\u91D1\u989D\u662F\u94B1\u5305\u7684\u4E0D\u540C\u5206\u6C60\uFF0C\u603B\u989D\u5EA6\u662F\u94B1\u5305\u5408\u8BA1\u3002\u2014 \u8868\u793A\u6682\u65E0\u6570\u636E\u3002")
20501
20549
  ),
20502
20550
  quota.note !== void 0 ? h("p", { className: "odc-notice", "data-kind": quota.state === "error" ? "error" : "status", role: quota.state === "error" ? "alert" : "status" }, quota.note) : null
20503
20551
  );
@@ -20578,7 +20626,7 @@ function Overview({ status, busy, onRefresh, nextAutoRefreshAt, refreshIntervalS
20578
20626
  h(
20579
20627
  "div",
20580
20628
  { className: "odc-card-head" },
20581
- h("div", null, h("h3", null, "\u989D\u5EA6\u6982\u89C8"), h("p", { className: "odc-hint" }, "\u5957\u9910\u7A97\u53E3\u4E0E\u94B1\u5305\u4F59\u989D\u5206\u5F00\u8BA1\u91CF\uFF1B\u672A\u77E5\u6570\u636E\u4E0D\u505A\u63A8\u7B97\u3002")),
20629
+ h("div", null, h("h3", null, "\u989D\u5EA6\u6982\u89C8"), h("p", { className: "odc-hint" }, "\u67E5\u770B\u5957\u9910\u7528\u91CF\u3001\u8BA2\u9605\u4F59\u989D\u548C\u5145\u503C\u4F59\u989D\u3002")),
20582
20630
  h("button", { className: "odc-button", type: "button", disabled: busy, onClick: onRefresh }, busy ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0\u989D\u5EA6")
20583
20631
  ),
20584
20632
  h(QuotaView, { quota: status?.quota, nextAutoRefreshAt, refreshIntervalSeconds })
@@ -20852,7 +20900,7 @@ function Models({ status, formSnapshot, setFormField, pendingField, onRefreshMod
20852
20900
  saving.current = false;
20853
20901
  }
20854
20902
  };
20855
- const source = status?.modelsSource === "openai" ? "OpenAI /models \u53D1\u73B0" : status?.modelsSource === "builtin" ? "\u5185\u7F6E\u76EE\u5F55" : "\u5C1A\u672A\u786E\u8BA4";
20903
+ const source = status?.modelsSource === "openai" ? "\u5F53\u524D\u7F51\u5173" : status?.modelsSource === "builtin" ? "\u5185\u7F6E\u76EE\u5F55" : "\u5C1A\u672A\u786E\u8BA4";
20856
20904
  const updated = status?.modelsUpdatedAt === void 0 ? "" : ` \xB7 \u66F4\u65B0\u4E8E ${formatTime(status.modelsUpdatedAt)}`;
20857
20905
  const models = status?.models ?? [];
20858
20906
  return h(
@@ -20865,7 +20913,7 @@ function Models({ status, formSnapshot, setFormField, pendingField, onRefreshMod
20865
20913
  "div",
20866
20914
  null,
20867
20915
  h("h3", null, "\u53EF\u7528\u6A21\u578B"),
20868
- h("p", { className: "odc-hint" }, "\u4E24\u4E2A Provider \u72EC\u7ACB\u7B5B\u9009\u3002\u9010\u6A21\u578B\u7684\u542F\u7528\u4E0E\u53C2\u6570\u4F1A\u4FDD\u5B58\u5230\u63D2\u4EF6\u8BBE\u7F6E\uFF0C\u53EA\u5F71\u54CD\u672C\u63D2\u4EF6\u7684\u6A21\u578B\u9009\u62E9\u4E0E\u8BF7\u6C42\u3002")
20916
+ h("p", { className: "odc-hint" }, "\u6309\u5206\u7EC4\u641C\u7D22\u6A21\u578B\uFF0C\u8BBE\u7F6E\u542F\u7528\u72B6\u6001\u4E0E\u9ED8\u8BA4\u53C2\u6570\u3002")
20869
20917
  ),
20870
20918
  h(
20871
20919
  "div",
@@ -20875,8 +20923,9 @@ function Models({ status, formSnapshot, setFormField, pendingField, onRefreshMod
20875
20923
  className: "odc-button",
20876
20924
  type: "button",
20877
20925
  disabled: refreshingModels || status === void 0 || status.apiKeyConfigured === false,
20878
- onClick: onRefreshModels
20879
- }, refreshingModels ? "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u2026" : "\u4ECE OpenAI \u5237\u65B0\u6A21\u578B")
20926
+ onClick: onRefreshModels,
20927
+ title: "\u4ECE\u5F53\u524D\u914D\u7F6E\u7684\u7F51\u5173\u8BFB\u53D6\u53EF\u7528\u6A21\u578B\u76EE\u5F55"
20928
+ }, refreshingModels ? "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u2026" : "\u5237\u65B0\u6A21\u578B\u76EE\u5F55")
20880
20929
  )
20881
20930
  ),
20882
20931
  status?.apiKeyConfigured === false ? h("p", { className: "odc-hint" }, "\u5237\u65B0\u6A21\u578B\u9700\u8981\u53EF\u7528\u7684 API Key\uFF1B\u4FDD\u5B58 Key \u540E\u91CD\u8BD5\u3002") : null,
@@ -20885,8 +20934,8 @@ function Models({ status, formSnapshot, setFormField, pendingField, onRefreshMod
20885
20934
  models.length === 0 ? h("p", { className: "odc-hint", role: "status" }, "\u6682\u65E0\u53EF\u7528\u6A21\u578B\uFF1B\u53EF\u5237\u65B0\u6A21\u578B\u5217\u8868\u540E\u91CD\u8BD5\u3002") : h(
20886
20935
  "div",
20887
20936
  { className: "odc-model-groups" },
20888
- h(ModelGroup, { title: "\u5957\u9910\u8D60\u9001 Provider", group: "plan", models, settingsMap, authoritative, writable, pending, onCommit: commit }),
20889
- h(ModelGroup, { title: "\u4ED8\u8D39 Provider", group: "paid", models, settingsMap, authoritative, writable, pending, onCommit: commit })
20937
+ h(ModelGroup, { title: "\u5957\u9910\u8D60\u9001\u6A21\u578B", group: "plan", models, settingsMap, authoritative, writable, pending, onCommit: commit }),
20938
+ h(ModelGroup, { title: "\u4ED8\u8D39\u6A21\u578B", group: "paid", models, settingsMap, authoritative, writable, pending, onCommit: commit })
20890
20939
  )
20891
20940
  );
20892
20941
  }
@@ -20978,12 +21027,12 @@ function Advanced({ status, formSnapshot, setFormField, pendingField }) {
20978
21027
  "section",
20979
21028
  { className: "odc-card" },
20980
21029
  h("h3", null, "\u7A7A\u8F6E\u76D1\u63A7"),
20981
- h("p", { className: "odc-hint" }, "\u72EC\u7ACB\u76D1\u63A7\u5957\u9910\u8D60\u9001\u6A21\u578B\u7684\u7A7A\u8F6E\u6216\u534F\u8BAE\u6B8B\u6E23\uFF1B\u4E0D\u5F71\u54CD\u4ED8\u8D39 Provider \u7684\u5F00\u5173\u3002"),
21030
+ h("p", { className: "odc-hint" }, "\u68C0\u6D4B\u5957\u9910\u8D60\u9001\u6A21\u578B\u5728\u5DE5\u5177\u5BF9\u8BDD\u4E2D\u8FD4\u56DE\u7684\u7A7A\u56DE\u7B54\u3002"),
20982
21031
  h("div", { style: { marginTop: 14 } }, h(Switch, {
20983
21032
  checked: formSnapshot.value?.emptyTurnGuard ?? status?.config.emptyTurnGuard ?? true,
20984
21033
  disabled: !writable || pendingField.includes("emptyTurnGuard"),
20985
21034
  label: "\u542F\u7528\u7A7A\u8F6E\u76D1\u63A7",
20986
- hint: `\u672C\u6B21\u8FD0\u884C\u5DF2\u62E6\u622A ${status?.guardedTurns ?? "\u2014"} \u8F6E\u3002\u68C0\u6D4B\u540E\u7531\u5BBF\u4E3B\u91CD\u8BD5\u7B56\u7565\u5904\u7406\uFF0C\u5BBF\u4E3B\u91CD\u8BD5\u4F1A\u989D\u5916\u6D88\u8017\u989D\u5EA6\u3002`,
21035
+ hint: `\u5DF2\u68C0\u6D4B\u5230 ${status?.guardedTurns ?? "\u2014"} \u6B21\u7A7A\u56DE\u7B54\u3002\u81EA\u52A8\u91CD\u8BD5\u7531 DSH \u7684\u91CD\u8BD5\u8BBE\u7F6E\u63A7\u5236\uFF0C\u91CD\u8BD5\u53EF\u80FD\u989D\u5916\u6D88\u8017\u989D\u5EA6\u3002`,
20987
21036
  onChange: (checked) => {
20988
21037
  void setFormField("emptyTurnGuard", checked);
20989
21038
  }
@@ -20993,7 +21042,7 @@ function Advanced({ status, formSnapshot, setFormField, pendingField }) {
20993
21042
  "section",
20994
21043
  { className: "odc-card" },
20995
21044
  h("h3", null, "\u63A8\u7406\u534F\u8BAE"),
20996
- h("p", { className: "odc-hint" }, "\u63D2\u4EF6\u6309\u6240\u9009\u534F\u8BAE\u7EC4\u88C5\u8BF7\u6C42\uFF1B\u6A21\u578B\u53D1\u73B0\u59CB\u7EC8\u8BFB\u53D6 OpenAI /models\uFF0C\u4E0E\u8FD9\u91CC\u7684\u9009\u62E9\u65E0\u5173\u3002"),
21045
+ h("p", { className: "odc-hint" }, "\u9009\u62E9\u5F53\u524D\u7F51\u5173\u652F\u6301\u7684\u63A8\u7406\u534F\u8BAE\u3002\u6A21\u578B\u76EE\u5F55\u59CB\u7EC8\u4ECE\u8BE5\u7F51\u5173\u8BFB\u53D6\u3002"),
20997
21046
  h(
20998
21047
  "div",
20999
21048
  { className: "odc-field" },
@@ -21041,7 +21090,7 @@ function Advanced({ status, formSnapshot, setFormField, pendingField }) {
21041
21090
  className: "odc-input",
21042
21091
  type: "text",
21043
21092
  value: apiKeyEnv,
21044
- disabled: !writable,
21093
+ disabled: !writable || pendingField.includes("apiKeyEnv"),
21045
21094
  autoCapitalize: "off",
21046
21095
  spellCheck: false,
21047
21096
  onChange: (event) => setApiKeyEnv(event.target.value)
@@ -21061,7 +21110,7 @@ function Advanced({ status, formSnapshot, setFormField, pendingField }) {
21061
21110
  "section",
21062
21111
  { className: "odc-card" },
21063
21112
  h("h3", null, "\u8BF7\u6C42\u65F6\u9650\u4E0E\u81EA\u52A8\u5237\u65B0"),
21064
- h("p", { className: "odc-hint" }, "\u6570\u503C\u5728\u79BB\u5F00\u8F93\u5165\u6846\u65F6\u6821\u9A8C\u5E76\u4FDD\u5B58\uFF1B\u7559\u7A7A\u6216\u8D8A\u754C\u4E0D\u4F1A\u5199\u5165\u3002"),
21113
+ h("p", { className: "odc-hint" }, "\u8F93\u5165\u6709\u6548\u8303\u56F4\u5185\u7684\u6574\u6570\uFF0C\u79BB\u5F00\u8F93\u5165\u6846\u540E\u4FDD\u5B58\u3002"),
21065
21114
  h(NumberField, {
21066
21115
  id: "odc-request-timeout",
21067
21116
  label: "\u8BF7\u6C42\u603B\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",
@@ -21112,14 +21161,32 @@ function loginInFlight(state) {
21112
21161
  var ACTIVATION_OPEN_LABELS = {
21113
21162
  opened: "\u5DF2\u5728\u65B0\u7A97\u53E3\u6253\u5F00\u5B98\u65B9\u6388\u6743\u9875\uFF1B\u8BF7\u5728\u8BE5\u7A97\u53E3\u786E\u8BA4\u8BBE\u5907\u7801\u3002",
21114
21163
  "no-link": "\u6388\u6743\u7801\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u7533\u8BF7\u3002",
21115
- "invalid-url": "\u6388\u6743\u94FE\u63A5\u672A\u901A\u8FC7\u6821\u9A8C\uFF0C\u8BF7\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00\u3002",
21164
+ "invalid-url": "\u6388\u6743\u94FE\u63A5\u65E0\u6548\uFF0C\u8BF7\u53D6\u6D88\u5E76\u91CD\u65B0\u5F00\u59CB\u767B\u5F55\u3002",
21116
21165
  unavailable: "\u65E0\u6CD5\u542F\u52A8\u4E34\u65F6\u6D4F\u89C8\u5668\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u5B89\u88C5 Chrome\uFF1B\u4E5F\u53EF\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00\u3002",
21117
21166
  failed: "\u4E34\u65F6\u6D4F\u89C8\u5668\u7A97\u53E3\u672A\u80FD\u6253\u5F00\uFF0C\u8BF7\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00\u3002"
21118
21167
  };
21119
21168
  function LoginApproval({ login, onOpen, opening, notice }) {
21169
+ const mounted = useMounted();
21170
+ const copyVersion = React.useRef(0);
21171
+ const [copyState, setCopyState] = React.useState("idle");
21172
+ React.useEffect(() => {
21173
+ copyVersion.current += 1;
21174
+ setCopyState("idle");
21175
+ }, [login?.jobId, login?.state, login?.activationUrl]);
21120
21176
  if (login?.state !== "waiting" || login.userCode === void 0 || login.expiresAt === void 0) return null;
21121
21177
  const url2 = validateActivationUrl(login.activationUrl, login.userCode);
21122
21178
  if (url2 === null) return null;
21179
+ const copyLink = async () => {
21180
+ const version2 = ++copyVersion.current;
21181
+ setCopyState("copying");
21182
+ try {
21183
+ if (typeof navigator.clipboard?.writeText !== "function") throw new Error("clipboard-unavailable");
21184
+ await navigator.clipboard.writeText(url2);
21185
+ if (mounted.current && version2 === copyVersion.current) setCopyState("copied");
21186
+ } catch {
21187
+ if (mounted.current && version2 === copyVersion.current) setCopyState("manual");
21188
+ }
21189
+ };
21123
21190
  return h(
21124
21191
  "div",
21125
21192
  { className: "odc-readonly", "aria-label": "\u8BBE\u5907\u6388\u6743" },
@@ -21136,14 +21203,24 @@ function LoginApproval({ login, onOpen, opening, notice }) {
21136
21203
  onOpen?.();
21137
21204
  }
21138
21205
  }, opening === true ? "\u6B63\u5728\u6253\u5F00\u2026" : "\u5728\u65B0\u7A97\u53E3\u6253\u5F00\u6388\u6743\u9875"),
21139
- h("a", { className: "odc-button", href: url2, target: "_blank", rel: "noreferrer noopener" }, "\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00")
21206
+ h("button", { className: "odc-button", type: "button", disabled: copyState === "copying", onClick: () => {
21207
+ void copyLink();
21208
+ } }, copyState === "copying" ? "\u6B63\u5728\u590D\u5236\u2026" : "\u590D\u5236\u6388\u6743\u94FE\u63A5"),
21209
+ h("a", { className: "odc-link", href: url2, target: "_blank", rel: "noreferrer noopener" }, "\u5B98\u65B9\u6388\u6743\u9875")
21140
21210
  ),
21211
+ copyState === "copied" ? h("p", { className: "odc-hint", role: "status" }, "\u94FE\u63A5\u5DF2\u590D\u5236\uFF0C\u53EF\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u4E2D\u7C98\u8D34\u6253\u5F00\u3002") : null,
21212
+ copyState === "manual" ? h(
21213
+ "label",
21214
+ { className: "odc-field" },
21215
+ h("span", null, "\u8BF7\u9009\u62E9\u5E76\u590D\u5236\u6388\u6743\u94FE\u63A5"),
21216
+ h("input", { className: "odc-input", type: "text", readOnly: true, value: url2, onFocus: (event) => event.target.select() })
21217
+ ) : null,
21141
21218
  notice ? h(NoticeView, { notice }) : null,
21142
21219
  h("p", { className: "odc-hint" }, `\u6388\u6743\u7801\u6709\u6548\u81F3 ${formatTime(login.expiresAt)}\u3002\u5728\u6388\u6743\u7A97\u53E3\u786E\u8BA4\u540E\uFF0C\u8FD4\u56DE\u6B64\u9875\u67E5\u770B\u7ED3\u679C\u3002`),
21143
- h("p", { className: "odc-hint" }, "\u65B0\u7A97\u53E3\u662F\u72EC\u7ACB\u7684\u4E34\u65F6 Chrome\uFF0C\u4E0D\u4F1A\u8BFB\u53D6\u6216\u6539\u52A8\u4F60\u6B63\u5728\u4F7F\u7528\u7684\u6D4F\u89C8\u5668\u914D\u7F6E\uFF1B\u9700\u8981\u6CBF\u7528\u5DF2\u6709\u767B\u5F55\u6001\u65F6\uFF0C\u53F3\u952E\u590D\u5236\u94FE\u63A5\u5230\u5E38\u7528\u6D4F\u89C8\u5668\u3002\u4EC5\u786E\u8BA4\u4F60\u672C\u4EBA\u53D1\u8D77\u7684\u6388\u6743\u3002")
21220
+ h("p", { className: "odc-hint" }, "\u4E34\u65F6 Chrome \u7A97\u53E3\u9700\u8981\u5355\u72EC\u767B\u5F55\uFF1B\u4E5F\u53EF\u590D\u5236\u94FE\u63A5\u5230\u5DF2\u767B\u5F55\u7684\u5E38\u7528\u6D4F\u89C8\u5668\u3002\u4EC5\u786E\u8BA4\u4F60\u672C\u4EBA\u53D1\u8D77\u7684\u6388\u6743\u3002")
21144
21221
  );
21145
21222
  }
21146
- function Connection({ status, formSnapshot, setFormField, pendingField, caller, loadStatus, refreshAfterLogin }) {
21223
+ function Connection({ status, formSnapshot, setFormField, pendingField, caller, onLoginStatus, onStatus }) {
21147
21224
  const mounted = useMounted();
21148
21225
  const actionVersion = React.useRef(0);
21149
21226
  const [busy, setBusy] = React.useState(false);
@@ -21154,6 +21231,7 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21154
21231
  const [activationNotice, setActivationNotice] = React.useState(null);
21155
21232
  const activationJob = React.useRef(null);
21156
21233
  const activationToken = React.useRef(0);
21234
+ const autoOpenPending = React.useRef(false);
21157
21235
  React.useEffect(() => () => {
21158
21236
  actionVersion.current += 1;
21159
21237
  }, []);
@@ -21172,15 +21250,17 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21172
21250
  if (status === void 0 || busy || keyBusy) return;
21173
21251
  const version2 = ++actionVersion.current;
21174
21252
  setBusy(true);
21253
+ autoOpenPending.current = true;
21175
21254
  setNotice({ kind: "status", text: "\u6B63\u5728\u7533\u8BF7\u8BBE\u5907\u6388\u6743\u2026" });
21176
21255
  try {
21177
21256
  const next = await caller("startLogin", { expectedRevision: status.revision });
21178
21257
  if (!mounted.current || version2 !== actionVersion.current) return;
21258
+ onLoginStatus(next);
21179
21259
  setNotice(null);
21180
21260
  setActivationNotice(null);
21181
- await (next.state === "success" ? refreshAfterLogin() : loadStatus());
21182
21261
  } catch {
21183
- if (mounted.current && version2 === actionVersion.current) setNotice({ kind: "error", text: "\u65E0\u6CD5\u542F\u52A8\u767B\u5F55\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
21262
+ autoOpenPending.current = false;
21263
+ if (mounted.current && version2 === actionVersion.current) setNotice({ kind: "error", text: "\u767B\u5F55\u8BF7\u6C42\u6682\u672A\u786E\u8BA4\uFF0C\u8BF7\u91CD\u8BD5\u3002" });
21184
21264
  } finally {
21185
21265
  if (mounted.current && version2 === actionVersion.current) setBusy(false);
21186
21266
  }
@@ -21190,10 +21270,11 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21190
21270
  const version2 = ++actionVersion.current;
21191
21271
  setBusy(true);
21192
21272
  try {
21193
- await caller("cancelLogin", { jobId: login.jobId });
21273
+ const next = await caller("cancelLogin", { jobId: login.jobId });
21194
21274
  if (!mounted.current || version2 !== actionVersion.current) return;
21195
- await loadStatus();
21196
- if (mounted.current) setNotice({ kind: "status", text: "\u5DF2\u8BF7\u6C42\u53D6\u6D88\u767B\u5F55\u3002" });
21275
+ autoOpenPending.current = false;
21276
+ onLoginStatus(next);
21277
+ setNotice({ kind: "status", text: next.message ?? LOGIN_LABELS[next.state] });
21197
21278
  } catch {
21198
21279
  if (mounted.current && version2 === actionVersion.current) setNotice({ kind: "error", text: "\u53D6\u6D88\u672A\u5B8C\u6210\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
21199
21280
  } finally {
@@ -21210,15 +21291,15 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21210
21291
  setKeyBusy(true);
21211
21292
  setNotice(null);
21212
21293
  try {
21213
- await caller("saveApiKey", { value: apiKey, expectedRevision: status.revision });
21294
+ const next = await caller("saveApiKey", { value: apiKey, expectedRevision: status.revision });
21214
21295
  if (!mounted.current || version2 !== actionVersion.current) return;
21215
21296
  setApiKey("");
21216
- await loadStatus();
21217
- if (mounted.current) setNotice({ kind: "status", text: "API Key \u5DF2\u5B89\u5168\u4FDD\u5B58\u3002" });
21297
+ onStatus(next);
21298
+ setNotice({ kind: "status", text: next.apiKeySource === "reference" ? `API Key \u5DF2\u4FDD\u5B58\uFF1B\u5F53\u524D\u4ECD\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u6216\u51ED\u636E\u5F15\u7528 ${next.config.apiKeyEnv}\u3002` : next.apiKeySource === "stored" ? "API Key \u5DF2\u4FDD\u5B58\u5E76\u7528\u4E8E\u5F53\u524D\u7F51\u5173\u3002" : "API Key \u5DF2\u4FDD\u5B58\u3002" });
21218
21299
  } catch {
21219
21300
  if (!mounted.current || version2 !== actionVersion.current) return;
21220
21301
  setApiKey("");
21221
- setNotice({ kind: "error", text: "API Key \u672A\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002" });
21302
+ setNotice({ kind: "error", text: "\u4FDD\u5B58\u7ED3\u679C\u6682\u672A\u786E\u8BA4\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
21222
21303
  } finally {
21223
21304
  if (mounted.current && version2 === actionVersion.current) setKeyBusy(false);
21224
21305
  }
@@ -21240,7 +21321,17 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21240
21321
  if (mounted.current && activationToken.current === token) setOpening(false);
21241
21322
  }
21242
21323
  };
21324
+ React.useEffect(() => {
21325
+ if (!autoOpenPending.current || busy || keyBusy || opening || status === void 0) return;
21326
+ if (state === "waiting") {
21327
+ autoOpenPending.current = false;
21328
+ void openActivation();
21329
+ } else if (state === "error" || state === "cancelled" || state === "success") {
21330
+ autoOpenPending.current = false;
21331
+ }
21332
+ }, [state, login?.jobId, busy, keyBusy, opening, status]);
21243
21333
  const desktopChecked = formSnapshot.value?.useDesktopCredential ?? status?.config.useDesktopCredential ?? true;
21334
+ const keySource = status?.apiKeySource === "reference" ? `\u73AF\u5883\u6216\u51ED\u636E\u5F15\u7528\uFF08${status.config.apiKeyEnv}\uFF09` : status?.apiKeySource === "stored" ? "\u5DF2\u4FDD\u5B58\u7684 API Key" : status?.apiKeySource === "device" ? "\u8BBE\u5907\u6388\u6743" : status?.apiKeyConfigured ? "\u5C1A\u672A\u786E\u8BA4" : "\u672A\u914D\u7F6E";
21244
21335
  return h(
21245
21336
  React.Fragment,
21246
21337
  null,
@@ -21256,7 +21347,7 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21256
21347
  checked: desktopChecked,
21257
21348
  disabled: formSnapshot.status !== "ready" || formSnapshot.value === void 0 || !formSnapshot.writable || pendingField.includes("useDesktopCredential"),
21258
21349
  label: "\u4F18\u5148\u4F7F\u7528\u684C\u9762\u767B\u5F55\u6001",
21259
- hint: "\u4EC5\u63A7\u5236\u8BFB\u53D6\u4F18\u5148\u7EA7\uFF0C\u4E0D\u4F1A\u4FEE\u6539\u6216\u5199\u56DE\u684C\u9762\u7AEF\u767B\u5F55\u6587\u4EF6\u3002",
21350
+ hint: "\u4F18\u5148\u663E\u793A\u684C\u9762\u7AEF\u767B\u5F55\u8D26\u53F7\u7684\u989D\u5EA6\u3002",
21260
21351
  onChange: (checked) => {
21261
21352
  void setFormField("useDesktopCredential", checked);
21262
21353
  }
@@ -21289,8 +21380,9 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21289
21380
  h(
21290
21381
  "section",
21291
21382
  { className: "odc-card" },
21292
- h("h3", null, "API Key\uFF08\u53EF\u9009\u8986\u76D6\uFF09"),
21293
- h("p", { className: "odc-hint" }, status?.apiKeyConfigured === true ? "\u5F53\u524D\u7F51\u5173\u5DF2\u6709\u53EF\u7528\u63A8\u7406\u51ED\u636E\u3002\u4F18\u5148\u7EA7\uFF1A\u73AF\u5883\u6216\u51ED\u636E\u5F15\u7528 \u2192 \u624B\u52A8\u4FDD\u5B58\u7684 API Key \u2192 \u8BBE\u5907\u6388\u6743\u4EE4\u724C\u3002" : "\u5F53\u524D\u7F51\u5173\u5C1A\u65E0\u63A8\u7406\u51ED\u636E\u3002\u53EF\u5B8C\u6210\u8BBE\u5907\u6388\u6743\u4EE5\u4F7F\u7528\u5B98\u65B9\u7F51\u5173\uFF0C\u6216\u4E3A\u5F53\u524D\u7F51\u5173\u5355\u72EC\u4FDD\u5B58 API Key\u3002"),
21383
+ h("h3", null, "API Key"),
21384
+ h("p", { className: "odc-meta", "aria-label": "\u5F53\u524D\u751F\u6548\u7684\u63A8\u7406\u51ED\u636E\u6765\u6E90" }, `\u5F53\u524D\u751F\u6548\u6765\u6E90\uFF1A${keySource}`),
21385
+ h("p", { className: "odc-hint" }, status?.apiKeyConfigured === true ? "\u5F53\u524D\u7F51\u5173\u5DF2\u914D\u7F6E\u63A8\u7406\u51ED\u636E\u3002\u53EF\u5728\u6B64\u4FDD\u5B58\u63D2\u4EF6\u4E13\u7528\u7684 API Key\u3002" : "\u5F53\u524D\u7F51\u5173\u5C1A\u65E0\u63A8\u7406\u51ED\u636E\u3002\u53EF\u5B8C\u6210\u8BBE\u5907\u6388\u6743\u4EE5\u4F7F\u7528\u5B98\u65B9\u7F51\u5173\uFF0C\u6216\u4E3A\u5F53\u524D\u7F51\u5173\u5355\u72EC\u4FDD\u5B58 API Key\u3002"),
21294
21386
  h(
21295
21387
  "form",
21296
21388
  { onSubmit: (event) => {
@@ -21359,6 +21451,19 @@ function SettingsPage({ caller, form }) {
21359
21451
  ...acceptedFormSnapshot,
21360
21452
  value: acceptedFormSnapshot.value === void 0 ? void 0 : { ...acceptedFormSnapshot.value, ...pendingValues }
21361
21453
  }), [acceptedFormSnapshot, pendingValues]);
21454
+ const acceptLoginStatus = React.useCallback((login) => {
21455
+ statusVersion.current += 1;
21456
+ loginVersion.current += 1;
21457
+ setStatus((previous) => previous === void 0 ? previous : { ...previous, login });
21458
+ setError(void 0);
21459
+ }, []);
21460
+ const acceptStatus = React.useCallback((next) => {
21461
+ statusVersion.current += 1;
21462
+ loginVersion.current += 1;
21463
+ setStatus(next);
21464
+ setModelsNotice(null);
21465
+ setError(void 0);
21466
+ }, []);
21362
21467
  const loadStatus = React.useCallback(async () => {
21363
21468
  const version2 = ++statusVersion.current;
21364
21469
  try {
@@ -21461,7 +21566,7 @@ function SettingsPage({ caller, form }) {
21461
21566
  if (!mounted.current || version2 !== modelsVersion.current) return;
21462
21567
  setStatus(next);
21463
21568
  setError(void 0);
21464
- setModelsNotice(next.modelsError === void 0 ? { kind: "status", text: `\u5DF2\u4ECE OpenAI \u5237\u65B0\u6A21\u578B\u5217\u8868\uFF0C\u5171 ${next.models.length} \u4E2A\u6A21\u578B\u3002` } : null);
21569
+ setModelsNotice(next.modelsError === void 0 ? { kind: "status", text: `\u5DF2\u4ECE\u5F53\u524D\u7F51\u5173\u5237\u65B0\u6A21\u578B\u76EE\u5F55\uFF0C\u5171 ${next.models.length} \u4E2A\u6A21\u578B\u3002` } : null);
21465
21570
  } catch {
21466
21571
  if (mounted.current && version2 === modelsVersion.current) setModelsNotice({ kind: "error", text: "\u6A21\u578B\u5237\u65B0\u672A\u5B8C\u6210\uFF0C\u8BF7\u68C0\u67E5\u51ED\u636E\u3001\u7F51\u5173\u4E0E\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002" });
21467
21572
  } finally {
@@ -21482,10 +21587,7 @@ function SettingsPage({ caller, form }) {
21482
21587
  const login = await caller("loginStatus");
21483
21588
  if (stopped || !mounted.current || version2 !== loginVersion.current) return;
21484
21589
  setStatus((previous) => previous === void 0 ? previous : { ...previous, login });
21485
- if (!loginInFlight(login.state)) {
21486
- if (login.state === "success") void refresh();
21487
- else void loadStatus();
21488
- }
21590
+ if (!loginInFlight(login.state) && login.state !== "success") void loadStatus();
21489
21591
  } catch {
21490
21592
  }
21491
21593
  };
@@ -21499,6 +21601,11 @@ function SettingsPage({ caller, form }) {
21499
21601
  loginVersion.current += 1;
21500
21602
  };
21501
21603
  }, [caller, loadStatus, loginActive, mounted, refresh]);
21604
+ React.useEffect(() => {
21605
+ if (loginState !== "success") return;
21606
+ void loadStatus();
21607
+ void refresh(true);
21608
+ }, [loginState, status?.login.jobId, loadStatus, refresh]);
21502
21609
  const configuredSeconds = formSnapshot.value?.quotaRefreshSeconds ?? status?.config.quotaRefreshSeconds;
21503
21610
  const refreshIntervalSeconds = configuredSeconds === void 0 ? DEFAULT_QUOTA_REFRESH_SECONDS : normaliseQuotaRefreshSeconds(configuredSeconds);
21504
21611
  const quotaNextRefreshAt = status?.quota.nextRefreshAt;
@@ -21544,7 +21651,7 @@ function SettingsPage({ caller, form }) {
21544
21651
  notice: modelsNotice
21545
21652
  });
21546
21653
  } else if (tab === "connection") {
21547
- content = h(Connection, { status, formSnapshot, setFormField, pendingField, caller, loadStatus, refreshAfterLogin: refresh });
21654
+ content = h(Connection, { status, formSnapshot, setFormField, pendingField, caller, onLoginStatus: acceptLoginStatus, onStatus: acceptStatus });
21548
21655
  } else {
21549
21656
  content = h(Advanced, { status, formSnapshot, setFormField, pendingField });
21550
21657
  }
@@ -21552,7 +21659,7 @@ function SettingsPage({ caller, form }) {
21552
21659
  "div",
21553
21660
  { className: "odc-settings" },
21554
21661
  h("h2", null, "OpenDesign CoRouter"),
21555
- h("p", { className: "odc-intro" }, "\u5728\u4E00\u4E2A\u7B80\u6D01\u9762\u677F\u4E2D\u7BA1\u7406\u989D\u5EA6\u3001\u53CC Provider \u6A21\u578B\u3001\u767B\u5F55\u4E0E\u8FD0\u884C\u4FDD\u62A4\u3002"),
21662
+ h("p", { className: "odc-intro" }, "\u67E5\u770B\u989D\u5EA6\uFF0C\u7BA1\u7406\u6A21\u578B\u3001\u8FDE\u63A5\u4E0E\u8BF7\u6C42\u8BBE\u7F6E\u3002"),
21556
21663
  h("div", { className: "odc-tabs", role: "tablist", "aria-label": "CoRouter \u8BBE\u7F6E\u5206\u7C7B" }, TABS.map((item, index) => h("button", {
21557
21664
  key: item.id,
21558
21665
  ref: (node2) => {
@@ -3,7 +3,7 @@ export interface DeviceTokens {
3
3
  runtimeKey: string;
4
4
  }
5
5
  export declare const DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
6
- export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.5";
6
+ export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.7";
7
7
  export declare const DEVICE_REQUEST_TIMEOUT_MS = 20000;
8
8
  /** One bounded launch step of the temporary activation window. */
9
9
  export declare const DEVICE_OPEN_TIMEOUT_MS = 30000;
@@ -14,8 +14,8 @@ export declare class DeviceAuthError extends Error {
14
14
  readonly code: string;
15
15
  constructor(code: string);
16
16
  }
17
- /** Strict public-link allowlist: only an optional, single, non-secret code is allowed. */
18
- export declare function validateActivationUrl(value: unknown, userCode?: string): string | null;
17
+ /** Strict public-link allowlist: a legacy code or the public deviceId/userCode pair. */
18
+ export declare function validateActivationUrl(value: unknown, userCode?: string, deviceId?: string): string | null;
19
19
  export interface DeviceAuthorization {
20
20
  deviceId: string;
21
21
  deviceSecret: string;
@@ -8,22 +8,29 @@ function record(value) {
8
8
  }
9
9
 
10
10
  // src/device-auth.ts
11
- function validateActivationUrl(value, userCode) {
11
+ function validateActivationUrl(value, userCode, deviceId) {
12
12
  if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
13
13
  try {
14
14
  const url = new URL(value);
15
15
  if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
16
16
  const entries = [...url.searchParams];
17
- if (entries.length > 1) return null;
18
- if (entries.length) {
17
+ if (entries.length === 2) {
18
+ if (entries.some(([key]) => key !== "deviceId" && key !== "userCode")) return null;
19
+ const id = url.searchParams.get("deviceId");
20
+ const code = url.searchParams.get("userCode");
21
+ if (!validDeviceId(id) || !validUserCode(code) || deviceId !== void 0 && id !== deviceId || userCode !== void 0 && code !== userCode) return null;
22
+ } else if (entries.length === 1) {
19
23
  const entry = entries[0];
20
24
  if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
21
- }
25
+ } else if (entries.length > 2) return null;
22
26
  return url.href;
23
27
  } catch {
24
28
  return null;
25
29
  }
26
30
  }
31
+ function validDeviceId(value) {
32
+ return typeof value === "string" && /^[a-z0-9_-]{1,200}$/iu.test(value);
33
+ }
27
34
  function validUserCode(value) {
28
35
  return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
29
36
  }
@@ -115,9 +122,9 @@ function amount(value, label, negative = false) {
115
122
  if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
116
123
  return value;
117
124
  }
118
- function positiveInteger(value, label, max) {
125
+ function positiveInteger(value, label, max, min = 1) {
119
126
  const result = count(value, label);
120
- if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
127
+ if (result < min || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
121
128
  return result;
122
129
  }
123
130
  function parseEfforts(value) {
@@ -220,9 +227,9 @@ function parseStatusConfig(value) {
220
227
  apiKeyEnv,
221
228
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
222
229
  ...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
223
- ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
224
- ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
225
- ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
230
+ ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5, 1e3) },
231
+ ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5, 1e3) },
232
+ ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600, 5) }
226
233
  };
227
234
  }
228
235
  function parseStatus(value) {
@@ -235,9 +242,13 @@ function parseStatus(value) {
235
242
  if (ids.has(model.id)) throw new Error(`\u6A21\u578B\u5217\u8868\u91CD\u590D\u4E86 id "${model.id}"`);
236
243
  ids.add(model.id);
237
244
  }
245
+ const apiKeyConfigured = flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA");
246
+ const apiKeySource = value.apiKeySource === void 0 ? void 0 : member(value.apiKeySource, ["reference", "stored", "device"], "\u63A8\u7406\u51ED\u636E\u6765\u6E90");
247
+ if (!apiKeyConfigured && apiKeySource !== void 0) throw new Error("\u672A\u914D\u7F6E\u51ED\u636E\u65F6\u4E0D\u80FD\u6307\u5B9A\u63A8\u7406\u51ED\u636E\u6765\u6E90");
238
248
  return {
239
249
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
240
- apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
250
+ apiKeyConfigured,
251
+ ...apiKeySource === void 0 ? {} : { apiKeySource },
241
252
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
242
253
  quota: parseQuotaSnapshot(value.quota),
243
254
  login: parseLoginState(value.login),