dsh-provider-qoder 0.1.0 → 0.2.0

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [简体中文](./README_CN.md)
4
4
 
5
- `dsh-provider-qoder` integrates your Qoder subscription into DeepSeek Harness (DSH), supporting streaming text, reasoning content, and tool calls, with support for both Global and China Qoder services.
5
+ `dsh-provider-qoder` integrates your Qoder subscription into DeepSeek Harness (DSH), supporting text and image input, streaming text, reasoning content, and tool calls across Global and China Qoder services.
6
6
 
7
7
  The plugin handles authentication and model communication, while tool execution, workspace operations, and permission management are handled by DSH.
8
8
  This project is a community adapter plugin.
@@ -12,7 +12,8 @@ This project is a community adapter plugin.
12
12
  - Configure subscription access via Qoder Personal Access Token (PAT).
13
13
  - Supports both Global (`global`) and China (`china`) services.
14
14
  - Discovers available models for your account, allows selecting enabled models, and displays pricing multipliers and reasoning effort options when reported by the service.
15
- - Supports streaming responses, reasoning content, tool calls, and multi-turn interactions with text-based tool results.
15
+ - Sends DSH image attachments to models advertised by Qoder as vision-language models, including images returned by tools. Images are published to the Qoder center service and referenced by URL to keep requests small; if publication fails, the image is sent inline instead so the turn still completes.
16
+ - Supports streaming responses, reasoning content, tool calls, and multi-turn interactions.
16
17
  - View account information, personal quota, organization resource packs, and reset dates in Settings.
17
18
 
18
19
  ## Installation
@@ -51,7 +52,7 @@ Select the region matching your account, enter your Qoder PAT into the input box
51
52
  3. Select the models you want to enable (keep at least one), then click **Save**.
52
53
  4. Select a Qoder model in the DSH model picker to start chatting.
53
54
 
54
- Fetching models uses the **saved PAT and service region**. If you switch accounts or regions, save your changes first before fetching models again. The initial model catalog is only a candidate reference; actual available models are subject to account query results. Model multipliers and reasoning effort options are provided by Qoder and may not be available for all models.
55
+ Fetching models uses the **saved PAT and service region**. If you switch accounts or regions, save your changes first before fetching models again. The initial model catalog is only a candidate reference; actual available models are subject to account query results. Model multipliers, reasoning effort options, and image-input capability are provided by Qoder and may not be available for all models.
55
56
 
56
57
  ### 3. View Account & Quota
57
58
 
@@ -69,11 +70,11 @@ Verify that the PAT is valid and matches the saved service region. If modified,
69
70
 
70
71
  ### Models remain unavailable after switching regions
71
72
 
72
- Switching regions does not automatically update your PAT or refetch models. After saving the PAT for the corresponding region, refetch available models, save your selection, and choose an available Qoder model in your conversation.
73
+ Global and China model catalogs are stored separately. Switching regions does not automatically update your PAT or refetch that region's models; after saving the matching PAT, fetch and save the models for the selected region.
73
74
 
74
75
  ### Are requests automatically retried?
75
76
 
76
- Automatic retries are managed by DSH. To enable retries, the running profile should include `@deepseek-ai/dsh-llm-retry`. The plugin reports empty responses, rate limits, server errors, timeouts, and transport errors using DSH's default retry policy; authentication, invalid requests, cancellations, quota issues, and protocol formatting errors are not retried by default.
77
+ Model-generation retries are managed by DSH. To enable them, the running profile should include `@deepseek-ai/dsh-llm-retry`. The plugin reports empty responses, rate limits, server errors, timeouts, and transport errors using DSH's default retry policy; authentication, invalid requests, cancellations, quota issues, and protocol formatting errors are not retried by default. The Qoder transport may independently retry an idempotent model-catalog, subscriber-profile, or quota read once, but never retries a model-generation POST internally.
77
78
 
78
79
  ## Install from Source & Development
79
80
 
@@ -108,6 +109,10 @@ pnpm run check
108
109
 
109
110
  This command runs non-UI mocked tests, builds the ESM package and type declarations, checks build artifacts, and previews the package tarball without sending real Qoder model requests or publishing npm packages.
110
111
 
112
+ ## References
113
+
114
+ - [pi-provider-qoder](https://github.com/simonsmh/pi-provider-qoder) - Reference implementation for Qoder authentication, API communication, and protocol handling.
115
+
111
116
  ## Feedback
112
117
 
113
118
  Please report issues via [GitHub Issues](https://github.com/mo-n/dsh-provider-qoder/issues) with the plugin and DSH versions, selected service region, reproduction steps, and sanitized error messages. Do not submit PATs, short-lived tokens, or personal account information.
package/README_CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](./README.md) | 简体中文
4
4
 
5
- `dsh-provider-qoder` 将你的 Qoder 订阅接入 DeepSeek Harness(DSH),支持流式文本、推理内容和工具调用,可选择国际版或中国区 Qoder 服务。
5
+ `dsh-provider-qoder` 将你的 Qoder 订阅接入 DeepSeek Harness(DSH),支持文本与图片输入、流式文本、推理内容和工具调用,可选择国际版或中国区 Qoder 服务。
6
6
 
7
7
  插件负责认证和模型通信,工具执行、工作区操作及权限管理由 DSH 负责。
8
8
  本项目是社区适配插件。
@@ -12,7 +12,8 @@
12
12
  - 通过 Qoder Personal Access Token(PAT,个人访问令牌)配置订阅访问。
13
13
  - 支持国际版(`global`)和中国区(`china`)服务。
14
14
  - 获取账号可用模型,选择启用的模型,并在服务返回相关信息时显示价格倍率和推理强度选项。
15
- - 支持流式回答、推理内容、工具调用及文本工具结果的多轮交互。
15
+ - 将 DSH 图片附件发送给 Qoder 标记为视觉语言模型的模型,同时支持工具返回的图片。图片会先上传到 Qoder 中心端服务并以 URL 引用,从而避免请求体膨胀;若上传失败,则自动改为内联发送,保证本轮对话仍可完成。
16
+ - 支持流式回答、推理内容、工具调用及多轮交互。
16
17
  - 在设置中查看账号信息、个人额度、组织资源包和重置时间。
17
18
 
18
19
  ## 安装
@@ -51,7 +52,7 @@ dsh plugin --profile web add dsh-provider-qoder
51
52
  3. 选择需要启用的模型,至少保留一个,然后点击 **保存**。
52
53
  4. 在 DSH 的模型选择器中选择 Qoder 模型,开始对话。
53
54
 
54
- 获取模型使用的是**已保存的 PAT 和服务区域**。更换账号或区域后,请先保存,再重新获取模型。初始模型目录仅供候选参考,实际可用模型以账号查询结果为准。模型倍率及推理强度选项由 Qoder 提供,并非所有模型都有这些信息。
55
+ 获取模型使用的是**已保存的 PAT 和服务区域**。更换账号或区域后,请先保存,再重新获取模型。初始模型目录仅供候选参考,实际可用模型以账号查询结果为准。模型倍率、推理强度选项及图片输入能力由 Qoder 提供,并非所有模型都有这些信息。
55
56
 
56
57
  ### 3. 查看账号与额度
57
58
 
@@ -69,11 +70,11 @@ dsh plugin --profile web add dsh-provider-qoder
69
70
 
70
71
  ### 切换区域后,模型仍不可用
71
72
 
72
- 切换区域不会自动替换 PAT 或重新获取模型。保存对应区域的 PAT 后,重新获取可用模型、保存选择,并在对话中选择可用的 Qoder 模型。
73
+ Global 与 China 的模型目录会分别保存。切换区域不会自动替换 PAT,也不会自动重新获取该区域的模型;请先保存匹配区域的 PAT,再获取并保存所选区域的模型。
73
74
 
74
75
  ### 请求是否会自动重试?
75
76
 
76
- 自动重试由 DSH 管理。需要重试时,运行中的 profile 应包含 `@deepseek-ai/dsh-llm-retry`。插件使用 DSH 默认重试策略报告空响应、限流、服务端、超时和传输错误;认证、无效请求、取消、额度及协议格式错误默认不重试。
77
+ 模型生成重试由 DSH 管理。需要启用时,运行中的 profile 应包含 `@deepseek-ai/dsh-llm-retry`。插件使用 DSH 默认重试策略报告空响应、限流、服务端、超时和传输错误;认证、无效请求、取消、额度及协议格式错误默认不重试。Qoder transport 可以独立重试一次幂等的模型目录、订阅者资料或配额读取,但绝不会在内部重试模型生成 POST。
77
78
 
78
79
  ## 从源码安装与开发
79
80
 
@@ -108,6 +109,10 @@ pnpm run check
108
109
 
109
110
  该命令运行模拟网络的非 UI 测试、构建 ESM 包与类型声明、检查构建产物并预览打包内容,不会发起真实 Qoder 模型请求或发布 npm 包。
110
111
 
112
+ ## 参考项目
113
+
114
+ - [pi-provider-qoder](https://github.com/simonsmh/pi-provider-qoder) - Qoder 认证、API 通信及协议处理的参考实现。
115
+
111
116
  ## 反馈
112
117
 
113
118
  请通过 [GitHub Issues](https://github.com/mo-n/dsh-provider-qoder/issues) 提交问题,附上插件和 DSH 版本、所选服务区域、复现步骤及脱敏后的错误信息。不要提交 PAT、短期令牌或个人账号信息。
package/lib/client.js CHANGED
@@ -6,16 +6,16 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
- //#region src/credential-contract.ts
10
- /** Shared Host/browser contract for the one managed Qoder credential. */
9
+ //#region src/dsh/credential-contract.ts
10
+ /** Shared DSH host/browser contract for the one managed Qoder credential. */
11
11
  const qoderCredentialRef = "QODER_MANAGED_CREDENTIAL";
12
12
  /** DSH's local provider uses `env` and `*-env` for ambient credential layers. */
13
13
  function isEnvironmentCredentialSource(source) {
14
14
  return source === "env" || source?.endsWith("-env") === true;
15
15
  }
16
16
  //#endregion
17
- //#region \0dsh-css:D:\code\dsh\dsh-llm-qoder\src\client\QoderCredentialCard.module.css.mjs
18
- const css = ".OAuTmW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.OAuTmW_credential p{margin:8px 0 0}.OAuTmW_credentialEditor{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.OAuTmW_credentialRowHead{align-items:center;gap:10px;display:flex}.OAuTmW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.OAuTmW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.OAuTmW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.OAuTmW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.OAuTmW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.OAuTmW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.OAuTmW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.OAuTmW_regionGroup{gap:8px;display:flex}.OAuTmW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.OAuTmW_regionOption:hover:not(.OAuTmW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.OAuTmW_credential button.OAuTmW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.OAuTmW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.OAuTmW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.OAuTmW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.OAuTmW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.OAuTmW_customized{border-top:1px solid var(--dsw-alias-border-l2);margin-top:14px;padding-top:10px}.OAuTmW_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.OAuTmW_customizedSummary::-webkit-details-marker{display:none}.OAuTmW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.OAuTmW_customized[open]>.OAuTmW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.OAuTmW_customizedBody{padding-top:12px}.OAuTmW_modelCatalog,.OAuTmW_modelList{flex-direction:column;gap:10px;display:flex}.OAuTmW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.OAuTmW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.OAuTmW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.OAuTmW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.OAuTmW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.OAuTmW_modelChoice{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:grid}.OAuTmW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.OAuTmW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.OAuTmW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.OAuTmW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.OAuTmW_modelUnavailable{cursor:not-allowed;opacity:.55}.OAuTmW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.OAuTmW_customizedSummary:before{transition:none}}.OAuTmW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.OAuTmW_heading{margin:0;font-size:18px;font-weight:600}.OAuTmW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.OAuTmW_configured,.OAuTmW_success{color:var(--dsw-alias-state-success-primary)}.OAuTmW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.OAuTmW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.OAuTmW_input:focus{border-color:var(--dsw-alias-brand-primary)}.OAuTmW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.OAuTmW_input:disabled{cursor:not-allowed;opacity:.4}.OAuTmW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.OAuTmW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.OAuTmW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.OAuTmW_credential button:disabled{cursor:not-allowed;opacity:.4}.OAuTmW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.OAuTmW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.OAuTmW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.OAuTmW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.OAuTmW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.OAuTmW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.OAuTmW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.OAuTmW_success{font-size:12px}.OAuTmW_accountSection{background:var(--dsw-alias-bg-module-platform);border-radius:8px;flex-direction:column;gap:10px;margin-top:12px;padding:12px;display:flex}.OAuTmW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.OAuTmW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.OAuTmW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.OAuTmW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.OAuTmW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.OAuTmW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.OAuTmW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.OAuTmW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.OAuTmW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.OAuTmW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.OAuTmW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.OAuTmW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.OAuTmW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.OAuTmW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.OAuTmW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.OAuTmW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.OAuTmW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.OAuTmW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.OAuTmW_accountLoadingText,.OAuTmW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}";
17
+ //#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderCredentialCard.module.css.mjs
18
+ const css = ".SRqrhW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.SRqrhW_credential p{margin:8px 0 0}.SRqrhW_credentialEditor{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.SRqrhW_credentialRowHead{align-items:center;gap:10px;display:flex}.SRqrhW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.SRqrhW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.SRqrhW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.SRqrhW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.SRqrhW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.SRqrhW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.SRqrhW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.SRqrhW_regionGroup{gap:8px;display:flex}.SRqrhW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.SRqrhW_regionOption:hover:not(.SRqrhW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.SRqrhW_credential button.SRqrhW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.SRqrhW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.SRqrhW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.SRqrhW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.SRqrhW_customized{border-top:1px solid var(--dsw-alias-border-l2);margin-top:14px;padding-top:10px}.SRqrhW_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.SRqrhW_customizedSummary::-webkit-details-marker{display:none}.SRqrhW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.SRqrhW_customizedBody{padding-top:12px}.SRqrhW_modelCatalog,.SRqrhW_modelList{flex-direction:column;gap:10px;display:flex}.SRqrhW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.SRqrhW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.SRqrhW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.SRqrhW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoice{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:grid}.SRqrhW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.SRqrhW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.SRqrhW_modelUnavailable{cursor:not-allowed;opacity:.55}.SRqrhW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.SRqrhW_customizedSummary:before{transition:none}}.SRqrhW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.SRqrhW_heading{margin:0;font-size:18px;font-weight:600}.SRqrhW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_configured,.SRqrhW_success{color:var(--dsw-alias-state-success-primary)}.SRqrhW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.SRqrhW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.SRqrhW_input:focus{border-color:var(--dsw-alias-brand-primary)}.SRqrhW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.SRqrhW_input:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.SRqrhW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.SRqrhW_credential button:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.SRqrhW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.SRqrhW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.SRqrhW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.SRqrhW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.SRqrhW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.SRqrhW_success{font-size:12px}.SRqrhW_accountSection{background:var(--dsw-alias-bg-module-platform);border-radius:8px;flex-direction:column;gap:10px;margin-top:12px;padding:12px;display:flex}.SRqrhW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.SRqrhW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.SRqrhW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.SRqrhW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.SRqrhW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.SRqrhW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.SRqrhW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.SRqrhW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.SRqrhW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.SRqrhW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.SRqrhW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.SRqrhW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.SRqrhW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.SRqrhW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.SRqrhW_accountLoadingText,.SRqrhW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.SRqrhW_userNameRow{align-items:center;gap:8px;display:flex}.SRqrhW_planBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px;display:inline-flex}.SRqrhW_orgTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_suspendedBanner{background:var(--dsw-alias-state-error-subtle,#ff4d4f1a);color:var(--dsw-alias-state-error-primary);border-radius:6px;align-items:center;gap:6px;margin-bottom:8px;padding:6px 10px;font-size:12px;display:flex}";
19
19
  const tagId = "dsh-provider-qoder/QoderCredentialCard.module.css";
20
20
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
21
21
  const tag = document.createElement("style");
@@ -25,65 +25,69 @@ window.__ModuleLoader__.load({
25
25
  document.head.appendChild(tag);
26
26
  }
27
27
  var QoderCredentialCard_module_css_default = {
28
- "accountEmptyText": "OAuTmW_accountEmptyText",
29
- "accountHead": "OAuTmW_accountHead",
30
- "accountLoadingText": "OAuTmW_accountLoadingText",
31
- "accountSection": "OAuTmW_accountSection",
32
- "actions": "OAuTmW_actions",
33
- "avatar": "OAuTmW_avatar",
34
- "configured": "OAuTmW_configured",
35
- "credential": "OAuTmW_credential",
36
- "credentialDot": "OAuTmW_credentialDot",
37
- "credentialDotConfigured": "OAuTmW_credentialDotConfigured",
38
- "credentialDotMissing": "OAuTmW_credentialDotMissing",
39
- "credentialEditor": "OAuTmW_credentialEditor",
40
- "credentialIdentity": "OAuTmW_credentialIdentity",
41
- "credentialName": "OAuTmW_credentialName",
42
- "credentialRowHead": "OAuTmW_credentialRowHead",
43
- "customized": "OAuTmW_customized",
44
- "customizedBody": "OAuTmW_customizedBody",
45
- "customizedSummary": "OAuTmW_customizedSummary",
46
- "danger": "OAuTmW_danger",
47
- "editButton": "OAuTmW_editButton",
48
- "editor": "OAuTmW_editor",
49
- "error": "OAuTmW_error",
50
- "head": "OAuTmW_head",
51
- "heading": "OAuTmW_heading",
52
- "hint": "OAuTmW_hint",
53
- "input": "OAuTmW_input",
54
- "label": "OAuTmW_label",
55
- "linkButton": "OAuTmW_linkButton",
56
- "modelBadge": "OAuTmW_modelBadge",
57
- "modelCatalog": "OAuTmW_modelCatalog",
58
- "modelCatalogHead": "OAuTmW_modelCatalogHead",
59
- "modelCatalogMeta": "OAuTmW_modelCatalogMeta",
60
- "modelCatalogTitle": "OAuTmW_modelCatalogTitle",
61
- "modelChoice": "OAuTmW_modelChoice",
62
- "modelChoiceName": "OAuTmW_modelChoiceName",
63
- "modelList": "OAuTmW_modelList",
64
- "modelRate": "OAuTmW_modelRate",
65
- "modelUnavailable": "OAuTmW_modelUnavailable",
66
- "primary": "OAuTmW_primary",
67
- "quotaBlock": "OAuTmW_quotaBlock",
68
- "quotaHeader": "OAuTmW_quotaHeader",
69
- "quotaLabel": "OAuTmW_quotaLabel",
70
- "quotaMeta": "OAuTmW_quotaMeta",
71
- "quotaProgress": "OAuTmW_quotaProgress",
72
- "quotaTop": "OAuTmW_quotaTop",
73
- "refreshBtn": "OAuTmW_refreshBtn",
74
- "regionBadge": "OAuTmW_regionBadge",
75
- "regionGroup": "OAuTmW_regionGroup",
76
- "regionOption": "OAuTmW_regionOption",
77
- "regionOptionActive": "OAuTmW_regionOptionActive",
78
- "regionOptionDisabled": "OAuTmW_regionOptionDisabled",
79
- "regionSelector": "OAuTmW_regionSelector",
80
- "secondary": "OAuTmW_secondary",
81
- "status": "OAuTmW_status",
82
- "success": "OAuTmW_success",
83
- "userDetails": "OAuTmW_userDetails",
84
- "userEmail": "OAuTmW_userEmail",
85
- "userInfo": "OAuTmW_userInfo",
86
- "userName": "OAuTmW_userName"
28
+ "accountEmptyText": "SRqrhW_accountEmptyText",
29
+ "accountHead": "SRqrhW_accountHead",
30
+ "accountLoadingText": "SRqrhW_accountLoadingText",
31
+ "accountSection": "SRqrhW_accountSection",
32
+ "actions": "SRqrhW_actions",
33
+ "avatar": "SRqrhW_avatar",
34
+ "configured": "SRqrhW_configured",
35
+ "credential": "SRqrhW_credential",
36
+ "credentialDot": "SRqrhW_credentialDot",
37
+ "credentialDotConfigured": "SRqrhW_credentialDotConfigured",
38
+ "credentialDotMissing": "SRqrhW_credentialDotMissing",
39
+ "credentialEditor": "SRqrhW_credentialEditor",
40
+ "credentialIdentity": "SRqrhW_credentialIdentity",
41
+ "credentialName": "SRqrhW_credentialName",
42
+ "credentialRowHead": "SRqrhW_credentialRowHead",
43
+ "customized": "SRqrhW_customized",
44
+ "customizedBody": "SRqrhW_customizedBody",
45
+ "customizedSummary": "SRqrhW_customizedSummary",
46
+ "danger": "SRqrhW_danger",
47
+ "editButton": "SRqrhW_editButton",
48
+ "editor": "SRqrhW_editor",
49
+ "error": "SRqrhW_error",
50
+ "head": "SRqrhW_head",
51
+ "heading": "SRqrhW_heading",
52
+ "hint": "SRqrhW_hint",
53
+ "input": "SRqrhW_input",
54
+ "label": "SRqrhW_label",
55
+ "linkButton": "SRqrhW_linkButton",
56
+ "modelBadge": "SRqrhW_modelBadge",
57
+ "modelCatalog": "SRqrhW_modelCatalog",
58
+ "modelCatalogHead": "SRqrhW_modelCatalogHead",
59
+ "modelCatalogMeta": "SRqrhW_modelCatalogMeta",
60
+ "modelCatalogTitle": "SRqrhW_modelCatalogTitle",
61
+ "modelChoice": "SRqrhW_modelChoice",
62
+ "modelChoiceName": "SRqrhW_modelChoiceName",
63
+ "modelList": "SRqrhW_modelList",
64
+ "modelRate": "SRqrhW_modelRate",
65
+ "modelUnavailable": "SRqrhW_modelUnavailable",
66
+ "orgTag": "SRqrhW_orgTag",
67
+ "planBadge": "SRqrhW_planBadge",
68
+ "primary": "SRqrhW_primary",
69
+ "quotaBlock": "SRqrhW_quotaBlock",
70
+ "quotaHeader": "SRqrhW_quotaHeader",
71
+ "quotaLabel": "SRqrhW_quotaLabel",
72
+ "quotaMeta": "SRqrhW_quotaMeta",
73
+ "quotaProgress": "SRqrhW_quotaProgress",
74
+ "quotaTop": "SRqrhW_quotaTop",
75
+ "refreshBtn": "SRqrhW_refreshBtn",
76
+ "regionBadge": "SRqrhW_regionBadge",
77
+ "regionGroup": "SRqrhW_regionGroup",
78
+ "regionOption": "SRqrhW_regionOption",
79
+ "regionOptionActive": "SRqrhW_regionOptionActive",
80
+ "regionOptionDisabled": "SRqrhW_regionOptionDisabled",
81
+ "regionSelector": "SRqrhW_regionSelector",
82
+ "secondary": "SRqrhW_secondary",
83
+ "status": "SRqrhW_status",
84
+ "success": "SRqrhW_success",
85
+ "suspendedBanner": "SRqrhW_suspendedBanner",
86
+ "userDetails": "SRqrhW_userDetails",
87
+ "userEmail": "SRqrhW_userEmail",
88
+ "userInfo": "SRqrhW_userInfo",
89
+ "userName": "SRqrhW_userName",
90
+ "userNameRow": "SRqrhW_userNameRow"
87
91
  };
88
92
  //#endregion
89
93
  //#region src/client/QoderAccountCard.tsx
@@ -242,14 +246,21 @@ window.__ModuleLoader__.load({
242
246
  });
243
247
  }
244
248
  if (accountState.status !== "ready") return null;
245
- const { profile, usage } = accountState.account;
249
+ const { profile, usage, plan } = accountState.account;
246
250
  const userQuota = usage?.userQuota;
247
251
  const orgPackage = usage?.orgResourcePackage;
248
252
  const avatarInitial = (profile.name || profile.email || "Q").charAt(0).toUpperCase();
249
253
  const resetDate = formatResetDate(usage?.expiresAt);
254
+ const planExpiry = plan?.endDate ? formatResetDate(plan.endDate) : void 0;
255
+ const isSuspended = plan?.organization?.isSuspended === true;
250
256
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
251
257
  className: QoderCredentialCard_module_css_default.accountSection,
252
258
  children: [
259
+ isSuspended ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
260
+ className: QoderCredentialCard_module_css_default.suspendedBanner,
261
+ role: "alert",
262
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("accountSuspended") })
263
+ }) : null,
253
264
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
254
265
  className: QoderCredentialCard_module_css_default.accountHead,
255
266
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -260,13 +271,27 @@ window.__ModuleLoader__.load({
260
271
  children: avatarInitial
261
272
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
262
273
  className: QoderCredentialCard_module_css_default.userDetails,
263
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
264
- className: QoderCredentialCard_module_css_default.userName,
265
- children: profile.name || "Qoder Subscriber"
266
- }), profile.email ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
267
- className: QoderCredentialCard_module_css_default.userEmail,
268
- children: profile.email
269
- }) : null]
274
+ children: [
275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
276
+ className: QoderCredentialCard_module_css_default.userNameRow,
277
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
278
+ className: QoderCredentialCard_module_css_default.userName,
279
+ children: profile.name || "Qoder Subscriber"
280
+ }), plan?.planTierName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
281
+ className: QoderCredentialCard_module_css_default.planBadge,
282
+ title: planExpiry ? t("planExpiresAt", { value: planExpiry }) : void 0,
283
+ children: plan.planTierName
284
+ }) : null]
285
+ }),
286
+ profile.email ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
287
+ className: QoderCredentialCard_module_css_default.userEmail,
288
+ children: profile.email
289
+ }) : null,
290
+ plan?.organization?.orgName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
291
+ className: QoderCredentialCard_module_css_default.orgTag,
292
+ children: t("organization", { name: plan.organization.orgName })
293
+ }) : null
294
+ ]
270
295
  })]
271
296
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
272
297
  type: "button",
@@ -307,13 +332,73 @@ window.__ModuleLoader__.load({
307
332
  });
308
333
  }
309
334
  //#endregion
335
+ //#region src/qoder/catalog.ts
336
+ const defaultMaxTokens = 32768;
337
+ const defaultModels = [
338
+ {
339
+ id: "cmodel",
340
+ name: "Cantus (Qoder)",
341
+ description: "Default Global Qoder subscription model for quick validation",
342
+ contextWindow: 1e6,
343
+ maxTokens: defaultMaxTokens,
344
+ supportsImages: true
345
+ },
346
+ {
347
+ id: "auto",
348
+ name: "Qoder Auto",
349
+ description: "Server-routed Global Qoder model pool",
350
+ contextWindow: 18e4,
351
+ maxTokens: defaultMaxTokens,
352
+ supportsImages: true
353
+ },
354
+ {
355
+ id: "ultimate",
356
+ name: "Qoder Ultimate",
357
+ description: "Highest-capability Global Qoder model pool",
358
+ contextWindow: 1e6,
359
+ maxTokens: defaultMaxTokens,
360
+ supportsImages: true
361
+ },
362
+ {
363
+ id: "performance",
364
+ name: "Qoder Performance",
365
+ description: "Performance-oriented Global Qoder model pool",
366
+ contextWindow: 1e6,
367
+ maxTokens: defaultMaxTokens,
368
+ supportsImages: true
369
+ },
370
+ {
371
+ id: "efficient",
372
+ name: "Qoder Efficient",
373
+ description: "Efficiency-oriented Global Qoder model pool",
374
+ contextWindow: 18e4,
375
+ maxTokens: defaultMaxTokens,
376
+ supportsImages: true
377
+ },
378
+ {
379
+ id: "lite",
380
+ name: "Qoder Lite",
381
+ description: "Basic Global Qoder model pool",
382
+ contextWindow: 18e4,
383
+ maxTokens: defaultMaxTokens
384
+ }
385
+ ];
386
+ //#endregion
310
387
  //#region src/client/credential-operations.ts
311
388
  function reconcileQoderModels(known, discovered) {
312
389
  const availableIds = new Set(discovered.map((model) => model.id));
313
390
  const unavailable = known.filter((model) => !availableIds.has(model.id));
391
+ const previous = new Map(known.map((model) => [model.id, model]));
392
+ const reconciled = discovered.map((model) => {
393
+ const budget = previous.get(model.id)?.contextWindow;
394
+ return budget === void 0 || model.contextWindow === void 0 ? model : {
395
+ ...model,
396
+ contextWindow: Math.min(budget, model.contextWindow)
397
+ };
398
+ });
314
399
  return {
315
- catalog: [...discovered, ...unavailable],
316
- selected: [...discovered],
400
+ catalog: [...reconciled, ...unavailable],
401
+ selected: reconciled,
317
402
  unavailableIds: new Set(unavailable.map((model) => model.id))
318
403
  };
319
404
  }
@@ -417,10 +502,13 @@ window.__ModuleLoader__.load({
417
502
  }
418
503
  //#endregion
419
504
  //#region src/client/QoderCredentialCard.tsx
420
- function modelsOf(value) {
505
+ function modelsOf(value, selectedRegion) {
421
506
  if (typeof value !== "object" || value === null) return [];
422
- const models = value.models;
423
- return Array.isArray(models) ? models : [];
507
+ const section = value;
508
+ const region = selectedRegion ?? (section.region === "china" ? "china" : "global");
509
+ const scoped = section.modelsByRegion?.[region];
510
+ if (Array.isArray(scoped)) return scoped;
511
+ return region === (section.region === "china" ? "china" : "global") && Array.isArray(section.models) ? section.models : defaultModels.map((model) => ({ ...model }));
424
512
  }
425
513
  function regionOf(value) {
426
514
  if (typeof value !== "object" || value === null) return "global";
@@ -439,13 +527,14 @@ window.__ModuleLoader__.load({
439
527
  const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
440
528
  (0, react.useEffect)(() => {
441
529
  if (modelSnapshot.status === "ready") {
442
- if (!modelsDirty) setModelDraft(modelsOf(modelSnapshot.value));
530
+ if (!modelsDirty) setModelDraft(modelsOf(modelSnapshot.value, regionDraft));
443
531
  if (!regionDirty) setRegionDraft(regionOf(modelSnapshot.value));
444
532
  }
445
533
  }, [
446
534
  modelSnapshot,
447
535
  modelsDirty,
448
- regionDirty
536
+ regionDirty,
537
+ regionDraft
449
538
  ]);
450
539
  const load = (0, react.useCallback)(async () => {
451
540
  const info = await operations.describe();
@@ -488,7 +577,7 @@ window.__ModuleLoader__.load({
488
577
  const currentRegion = regionOf(modelSnapshot.value);
489
578
  const closeEditor = () => {
490
579
  setDraft("");
491
- setModelDraft(modelsOf(modelSnapshot.value));
580
+ setModelDraft(modelsOf(modelSnapshot.value, regionOf(modelSnapshot.value)));
492
581
  setRegionDraft(regionOf(modelSnapshot.value));
493
582
  setModelsDirty(false);
494
583
  setRegionDirty(false);
@@ -508,7 +597,7 @@ window.__ModuleLoader__.load({
508
597
  setRegionDirty(false);
509
598
  }
510
599
  if (modelsDirty) {
511
- if (!await operations.storeModels(modelDraft)) {
600
+ if (!await operations.storeModels(regionDraft, modelDraft)) {
512
601
  setNotice("saveFailed");
513
602
  setMutation(void 0);
514
603
  return;
@@ -598,6 +687,8 @@ window.__ModuleLoader__.load({
598
687
  disabled: !modelWritable || busy,
599
688
  onClick: () => {
600
689
  setRegionDraft("global");
690
+ setModelDraft(modelsOf(modelSnapshot.value, "global"));
691
+ setModelsDirty(false);
601
692
  setRegionDirty(true);
602
693
  },
603
694
  children: t("regionGlobal")
@@ -609,6 +700,8 @@ window.__ModuleLoader__.load({
609
700
  disabled: !modelWritable || busy,
610
701
  onClick: () => {
611
702
  setRegionDraft("china");
703
+ setModelDraft(modelsOf(modelSnapshot.value, "china"));
704
+ setModelsDirty(false);
612
705
  setRegionDirty(true);
613
706
  },
614
707
  children: t("regionChina")
@@ -750,7 +843,11 @@ window.__ModuleLoader__.load({
750
843
  quotaUsed: "已用 {used} / {total} {unit}",
751
844
  resetsAt: "重置时间:{value}",
752
845
  noQuota: "暂无可用额度信息",
753
- quotaExceeded: "当前订阅额度已用尽。"
846
+ quotaExceeded: "当前订阅额度已用尽。",
847
+ plan: "套餐计划",
848
+ planExpiresAt: "有效期至:{value}",
849
+ organization: "组织:{name}",
850
+ accountSuspended: "当前账号或组织已被暂停/冻结。"
754
851
  };
755
852
  const en = {
756
853
  nav: "Qoder",
@@ -802,7 +899,11 @@ window.__ModuleLoader__.load({
802
899
  quotaUsed: "Used {used} / {total} {unit}",
803
900
  resetsAt: "Resets: {value}",
804
901
  noQuota: "No quota metrics available",
805
- quotaExceeded: "The current subscription quota is exhausted."
902
+ quotaExceeded: "The current subscription quota is exhausted.",
903
+ plan: "Plan",
904
+ planExpiresAt: "Expires at: {value}",
905
+ organization: "Org: {name}",
906
+ accountSuspended: "Account or organization is currently suspended."
806
907
  };
807
908
  //#endregion
808
909
  //#region src/client/index.ts
@@ -872,9 +973,13 @@ window.__ModuleLoader__.load({
872
973
  },
873
974
  getModelSnapshot: () => modelScope.getSnapshot(),
874
975
  subscribeModels: (listener) => modelScope.subscribe(listener),
875
- storeModels: async (models) => {
976
+ storeModels: async (region, models) => {
876
977
  try {
877
- await modelScope.set("models", models);
978
+ const current = modelScope.getSnapshot().value;
979
+ await modelScope.set("modelsByRegion", {
980
+ ...current?.modelsByRegion,
981
+ [region]: models
982
+ });
878
983
  return true;
879
984
  } catch {
880
985
  return false;