dsh-opendesign-corouter 0.1.4 → 0.1.6

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
@@ -32,7 +32,7 @@ dsh plugin --profile web add dsh-opendesign-corouter
32
32
  也可打开 [Actions → CI](https://github.com/KouzakiUmi/dsh-opendesign-corouter/actions/workflows/ci.yml),从成功构建下载 **`dsh-opendesign-corouter-build`** artifact。登录 GitHub 后解压 ZIP,取得 `.tgz` 与 `.sha256`,再安装本地包:
33
33
 
34
34
  ```console
35
- dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.4.tgz
35
+ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.6.tgz
36
36
  ```
37
37
 
38
38
  `web` 只是示例,请替换为目标 Profile。Desktop 使用桌面安装器提供的 `dsh` 入口;独立公共 CLI 不能代管保留的 `desktop` Profile。安装后的启用与重载按目标 DSH 的管理流程进行。
@@ -40,7 +40,7 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.4.tgz
40
40
  ### 2. 配置连接
41
41
 
42
42
  1. 打开 **设置 → OpenDesign CoRouter → 连接与登录**。
43
- 2. 点击 **开始设备授权**,再用常用浏览器 **打开授权页**,核对授权码并确认;也可单独配置工作空间 API Key 或沿用 `COROUTER_API_KEY`。
43
+ 2. 点击 **开始设备授权**,取得授权码后自动打开临时 Chrome 官方授权页,核对并确认;打开失败可用 **在新窗口打开授权页** 重试,也可复制链接到常用浏览器,或单独配置工作空间 API Key / 沿用 `COROUTER_API_KEY`。
44
44
  3. 返回设置页等待保存成功。额度默认优先读取桌面登录态;要明确使用设备授权的账号,可关闭桌面优先。
45
45
  4. 回到模型选择器,选择 **OpenDesign CoRouter · 套餐赠送** 或 **OpenDesign CoRouter · 付费模型**。
46
46
 
@@ -52,8 +52,8 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.4.tgz
52
52
 
53
53
  | Provider | 模型数 | 用途 |
54
54
  | --- | ---: | --- |
55
- | `opendesign-corouter-plan` | 12 | 官网 Design Plan 套餐赠送模型 |
56
- | `opendesign-corouter-paid` | 21 | 其余公开对话模型 |
55
+ | `opendesign-corouter-plan` | 13 | Core / Design Plan 附赠通道,含预检确认覆盖的 MiniMax M3.1 Flash |
56
+ | `opendesign-corouter-paid` | 25 | Plan 套餐模型及其他钱包付费模型 |
57
57
 
58
58
  上表为内置目录。模型页可从 OpenAI `/models` 端口刷新,逐个启用或停用模型,并配置温度、top-p、默认输出上限、上下文预算及思考深度。停用同时影响模型选择器和直接调用;参数会保存并应用到实际请求。媒体生成接口不在对话目录中,未知能力不按名称猜测。
59
59
 
@@ -72,7 +72,7 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.4.tgz
72
72
 
73
73
  ### 桌面优先,也能独立登录
74
74
 
75
- 独立登录采用设备授权,在常用浏览器打开普通官方授权页,沿用现有配置、密码管理器和自动填充。确认后设备令牌保存到 DSH 凭据服务;取消、超时和卸载停止轮询,不关闭用户浏览器,也不修改桌面配置。
75
+ 独立登录采用设备授权,官方授权页可交给插件自己启动的临时 Chrome 窗口显示(不读取或改动你已有的浏览器配置),也可复制链接到你自己的浏览器沿用已有登录态、密码管理器和自动填充。确认后设备令牌保存到 DSH 凭据服务;取消、超时和卸载停止轮询并关闭临时窗口,不关闭你自己的浏览器,也不修改桌面配置。
76
76
 
77
77
  <picture>
78
78
  <source media="(prefers-color-scheme: dark)" srcset="docs/previews/theme-dark.png">
package/SECURITY.md CHANGED
@@ -6,7 +6,7 @@
6
6
  - 设备授权双令牌原子保存;runtimeKey 只自动提供给请求快照中的官方推理 origin,controlKey 只发往官方控制面。旧 Cookie grant 保留只读兼容。
7
7
  - 推理自定义地址由用户显式配置;除本机回环外要求 HTTPS,拒绝 URL 内嵌凭据、查询串与片段。
8
8
  - 额度分池不跨身份混合。桌面认证失效时,可回退到单独保存的控制台登录;权限不足和未知额度不伪装成 0。
9
- - 独立登录使用设备授权,普通浏览器仅打开官方激活页;插件不启动浏览器自动化、不读取浏览器配置或 Cookie。deviceSecret 只在 Host 内存用于轮询;授权码、激活链接和到期时间仅在等待期提供给 UI。取消、超时和卸载中止请求,不关闭用户浏览器。持久化成功是不可撤回的提交边界,之后取消不会假称已撤回。
9
+ - 独立登录使用设备授权。Host 可用 `playwright-core` 启动一个短暂的 Chrome 窗口显示官方激活页;这是插件自己创建的临时浏览器上下文,不创建、不驱动、不读取用户已有的浏览器配置或 Cookie,也不在该页面输入、点击或读取内容。由系统外壳拦截 `target="_blank"` 的宿主里,官方链接仍作为手动路径保留。deviceSecret 只在 Host 内存用于轮询;授权码、激活链接和到期时间仅在等待期提供给 UI,打开结果只返回固定代码,不返回 URL、浏览器句柄或错误正文。取消、超时和卸载中止请求,并有界请求关闭该临时窗口(每阶段最多约 5 秒,超时为尽力清理);不关闭用户自己的浏览器。持久化成功是不可撤回的提交边界,之后取消不会假称已撤回。
10
10
  - `OPENDESIGN_VELA_PATH` 是用户信任的本地程序路径。插件仅以固定 argv 执行只读查询,不通过 shell;不校验该程序的签名或下载它。不要将此变量指向来源不明的程序。
11
11
  - RPC 使用严格白名单解析;原始错误正文、Cookie、请求头、Key 和桌面本地路径不进入状态返回。并发保存有版本冲突检测。
12
12
  - 请求、流空闲、额度接口、CLI、登录和 RPC 均有有界超时。停用插件会撤销 Provider / RPC 并中止自身请求。
@@ -1,11 +1,32 @@
1
1
  # 第三方代码与资料
2
2
 
3
- 本项目的消息/图片兼容处理、套餐模型元数据参考 [dsh-opendesign-go 0.1.9](https://github.com/EliteOtaku/dsh-opendesign-go)。额度归一化及登录生命周期的基础处理改编自 [dsh-subusage 0.16.2](https://github.com/KouzakiUmi/dsh-subusage)。空轮判定参考 [dsh-empty-turn-guard 0.4.0](https://github.com/FYTXY/dsh-empty-turn-guard)。设置布局参考 [dsh-code-pill](https://github.com/KouzakiUmi/dsh-code-pill)。
3
+ 本项目的消息/图片兼容处理、套餐模型元数据参考 [dsh-opendesign-go 0.1.9](https://github.com/EliteOtaku/dsh-opendesign-go)。额度归一化及登录生命周期的基础处理改编自 [dsh-subusage 0.16.2](https://github.com/KouzakiUmi/dsh-subusage);临时授权窗口的 Chrome 启动策略改编自 [dsh-subusage 0.16.3](https://github.com/KouzakiUmi/dsh-subusage) 的 `lib/cookie-login.js`。空轮判定参考 [dsh-empty-turn-guard 0.4.0](https://github.com/FYTXY/dsh-empty-turn-guard)。设置布局参考 [dsh-code-pill](https://github.com/KouzakiUmi/dsh-code-pill)。
4
4
 
5
5
  离线主题夹具使用 [DeepSeek Harness 官方主题令牌](https://github.com/deepseek-ai/deepseek-harness/blob/22ddee3792402c3e70ec5f86a63c3e3e895a2f60/packages/client/ui-theme/src/styles/design-platform.css),仅供测试,不随插件安装包分发。
6
6
 
7
7
  Client bundle 内含 [Zod 4.6.6](https://github.com/colinhacks/zod) 与 DSH 协议辅助代码,分别保留 Colin McDonnell 与 DeepSeek 的 MIT 声明。
8
8
 
9
+ Host 侧运行时依赖 [playwright-core 1.63.0](https://github.com/microsoft/playwright)(Apache-2.0),只用于启动显示官方授权页的临时 Chrome 窗口:不下载浏览器,使用本机已安装的 Chrome。它按 npm 依赖安装,不并入本项目源码。
10
+
11
+ ```text
12
+ Apache License
13
+ Version 2.0, January 2004
14
+ http://www.apache.org/licenses/
15
+
16
+ Copyright (c) Microsoft Corporation.
17
+ Licensed under the Apache License, Version 2.0 (the "License");
18
+ you may not use this file except in compliance with the License.
19
+ You may obtain a copy of the License at
20
+
21
+ https://www.apache.org/licenses/LICENSE-2.0
22
+
23
+ Unless required by applicable law or agreed to in writing, software
24
+ distributed under the License is distributed on an "AS IS" BASIS,
25
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26
+ See the License for the specific language governing permissions and
27
+ limitations under the License.
28
+ ```
29
+
9
30
  以上项目为 MIT 许可。保留如下版权和许可声明,适用于相应移植与改编部分:
10
31
 
11
32
  ```text
@@ -0,0 +1,104 @@
1
+ /** Bounds on one navigation; the window stays open for the user afterwards. */
2
+ export declare const ACTIVATION_NAVIGATE_TIMEOUT_MS = 15000;
3
+ /**
4
+ * End-to-end budget for one open request: launch + newPage + navigate. Kept below the
5
+ * client's 30 s RPC wait so a successful open is reported to the user, not a timeout.
6
+ */
7
+ export declare const ACTIVATION_OPEN_BUDGET_MS = 20000;
8
+ /** Bound on closing a window; a wedged browser must not pin disposal. */
9
+ export declare const ACTIVATION_CLOSE_TIMEOUT_MS = 5000;
10
+ /** Why the window could not be offered. Codes are public; no URL or error text is echoed. */
11
+ export type ActivationOpenCode = 'opened' | 'invalid-url' | 'unavailable' | 'failed';
12
+ export interface ActivationOpenResult {
13
+ code: ActivationOpenCode;
14
+ /** True only when a Chrome window is showing the activation page. */
15
+ opened: boolean;
16
+ }
17
+ /** The subset of playwright-core this module uses, so tests can inject a stand-in. */
18
+ export interface ActivationPage {
19
+ goto(url: string, options: {
20
+ waitUntil: 'domcontentloaded';
21
+ timeout: number;
22
+ }): Promise<unknown>;
23
+ /** playwright's Page.close(); default does not run unload handlers. */
24
+ close?(): Promise<void>;
25
+ }
26
+ export interface ActivationBrowser {
27
+ close(): Promise<unknown>;
28
+ isConnected(): boolean;
29
+ newPage(): Promise<ActivationPage>;
30
+ on?(event: 'disconnected', listener: () => void): unknown;
31
+ }
32
+ export interface ActivationLauncherOptions {
33
+ /** Injected for offline tests; production resolves playwright-core lazily. */
34
+ launch?: () => Promise<ActivationBrowser>;
35
+ timeoutMs?: number;
36
+ /** Reports whether a launch is even possible, e.g. a missing Chrome install. */
37
+ available?: () => boolean;
38
+ }
39
+ /**
40
+ * Chrome without the automation flag subusage found counterproductive on guarded pages.
41
+ * `chromiumSandbox: true` is explicit on purpose: playwright otherwise appends
42
+ * `--no-sandbox` (verified in playwright-core 1.63.0), and this window renders a page
43
+ * this plugin does not control.
44
+ */
45
+ export declare function chromeLaunchOptions(): {
46
+ channel: "chrome";
47
+ headless: boolean;
48
+ chromiumSandbox: boolean;
49
+ ignoreDefaultArgs: string[];
50
+ args: string[];
51
+ };
52
+ export declare class ActivationLauncher {
53
+ private browser;
54
+ private page;
55
+ private pending;
56
+ private disposal;
57
+ /** In-flight window closure, shared so disposal can join it. */
58
+ private closing;
59
+ /** Closures of handles that were never adopted; disposal joins them too. */
60
+ private tracked;
61
+ /** Chrome starts in flight, so disposal waits for the handle they will produce. */
62
+ private starting;
63
+ private disposed;
64
+ /** Bumped by `close()`: an attempt from an older session must not resurrect a window. */
65
+ private session;
66
+ private readonly options;
67
+ constructor(options?: ActivationLauncherOptions);
68
+ /**
69
+ * Show `url` in the temporary window, reusing one already open. Concurrent calls share
70
+ * a single attempt: the caller's button stays honest about what actually happened.
71
+ */
72
+ open(url: string): Promise<ActivationOpenResult>;
73
+ /**
74
+ * Close the temporary window and stay usable: a cancelled or expired job takes its
75
+ * window away, while the next login may open a new one.
76
+ */
77
+ close(): Promise<void>;
78
+ /**
79
+ * Retire the launcher for good. Cleanup is best-effort and bounded: it joins in-flight
80
+ * starts and closures, but a browser that ignores `close()` may outlive the 5 s wait.
81
+ */
82
+ dispose(): Promise<void>;
83
+ /**
84
+ * Close the window this launcher owns right now. Ownership is taken before the async
85
+ * close, so a job that adopts a new window afterwards is not affected by this closure.
86
+ * The closure promise is shared so a later `dispose()` joins it.
87
+ */
88
+ private closeCurrent;
89
+ private attempt;
90
+ /** The job that started this attempt ended, or the launcher retired. */
91
+ private stale;
92
+ private ensureBrowser;
93
+ private ensurePage;
94
+ /** A handle that arrived too late is closed, and still counts as work disposal joins. */
95
+ private trackClose;
96
+ private trackPageClose;
97
+ private adopt;
98
+ /**
99
+ * Bound one launch step by the attempt's shared deadline. A step that settles only
100
+ * after that deadline still hands its handle back: dropping it would leak a Chrome
101
+ * process nobody can close.
102
+ */
103
+ private bounded;
104
+ }
package/lib/catalog.d.ts CHANGED
@@ -16,16 +16,17 @@
16
16
  * 分组语义(重要):`plan`/`paid` 是**本插件本地目录分类**,用于把“套餐赠送通道”与
17
17
  * “其余付费通道”拆成两个 Provider,不是上游强制计费契约。最终可用性、扣哪个额度池、
18
18
  * 是否先扣 Design Plan 额度再用套餐模型/钱包额度,全部由服务端按账号套餐决定。
19
- * `paid` 21 款里只有 8 款出现在官网套餐可抵扣名单,其余 13 款同样可经钱包/按量付费调用,
20
- * 因此 `paid` 收录 21 款而不是 13 款。
19
+ * 2026-10-27 官方 GET /v1/models 实测补齐五个 ID,现为 plan 13 + paid 25。
20
+ * 同日桌面 Vela 按模型 billing preflight:M3.1 Flash 的 modelCovered=true /
21
+ * funding=coding_plan,新增于附赠通道;其余四项为 false / wallet,归付费通道。
22
+ * 此分类以当次工作区证据为依据,不保证其他账号或未来套餐权益。
23
+ * 新增项证据及名称来源见 docs/client-evidence.md;不从型号名推断能力。
21
24
  *
22
- * 未收录(证据不足,不猜测):
23
- * - 套餐模型组中尚未公开精确 ID 的 `Muse Spark 1.3`、`Gemini 4 Argon`:
24
- * 前者详情页没有 `data-model`/模型 ID,后者在模型目录里没有详情页。
25
+ * 未收录:
25
26
  * - 媒体模型 `GPT Image 2`、`Nano Banana 2`、`Seedream 5 Lite / Pro`:
26
27
  * 走 `/api/v1/media/*`,不应出现在对话模型列表里,避免选中后 chat 请求必败。
27
28
  *
28
- * `paid` 21 款的能力字段一律缺省:既没有官网数值,也没有可信默认表,
29
+ * `paid` 25 款及新增 M3.1 Flash 的能力字段一律缺省:没有可核实的能力数值,
29
30
  * 不按名称推断图像能力,也不给未知项填 `['text']` 谎称不支持图像。
30
31
  */
31
32
  export interface CatalogModel {
@@ -38,8 +39,8 @@ export interface CatalogModel {
38
39
  reasoningEfforts?: readonly string[];
39
40
  }
40
41
  /**
41
- * 顺序按供应商分组(与官网模型目录导航一致);`plan` 为 Design Plan 额度覆盖的 12 款,
42
- * 其余 21 款公开对话模型归 `paid`。
42
+ * `plan` 为官网 12 款 Core / Design Plan 模型,加预检确认覆盖的 M3.1 Flash;
43
+ * 其余 25 项归 `paid`(Plan / 钱包付费通道)。
43
44
  */
44
45
  export declare const MODELS: readonly CatalogModel[];
45
46
  export declare function modelsFor(group: 'plan' | 'paid'): readonly CatalogModel[];
package/lib/client.d.ts CHANGED
@@ -183,9 +183,15 @@ export interface AdvancedProps {
183
183
  export declare function Advanced({ status, formSnapshot, setFormField, pendingField }: AdvancedProps): React.FunctionComponentElement<{
184
184
  children?: React.ReactNode | undefined;
185
185
  }>;
186
- /** Approval opens through an ordinary link; no browser profile is created or controlled. */
187
- export declare function LoginApproval({ login }: {
186
+ /** Approval is an ordinary official link; the Host may also show it in a temporary window. */
187
+ export declare function LoginApproval({ login, onOpen, opening, notice }: {
188
188
  login: LoginState | undefined;
189
+ onOpen?: () => void;
190
+ opening?: boolean;
191
+ notice?: {
192
+ kind: 'status' | 'error';
193
+ text: string;
194
+ } | null;
189
195
  }): React.DetailedReactHTMLElement<{
190
196
  className: string;
191
197
  'aria-label': string;
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
  }
@@ -20021,6 +20028,7 @@ var MAX_API_KEY = 16384;
20021
20028
  var HTTP_ASCII = /^[\x21-\x7e]+$/u;
20022
20029
  var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
20023
20030
  var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
20031
+ var ACTIVATION_OPEN_CODES = ["opened", "no-link", "invalid-url", "unavailable", "failed"];
20024
20032
  var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
20025
20033
  var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
20026
20034
  var SESSION_SOURCES = ["desktop", "login", "environment"];
@@ -20196,6 +20204,10 @@ function parseSaveApiKeyRequest(value) {
20196
20204
  if (key.length > MAX_API_KEY) throw new Error(`API Key \u4E0D\u80FD\u8D85\u8FC7 ${MAX_API_KEY} \u5B57\u7B26`);
20197
20205
  return { value: key, expectedRevision: text(value.expectedRevision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION) };
20198
20206
  }
20207
+ function parseActivationOpenState(value) {
20208
+ if (!record2(value)) throw new Error("\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C\u5FC5\u987B\u662F\u5BF9\u8C61");
20209
+ return { code: member(value.code, ACTIVATION_OPEN_CODES, "\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C"), opened: flag(value.opened, "\u6388\u6743\u9875\u662F\u5426\u5DF2\u6253\u5F00") };
20210
+ }
20199
20211
 
20200
20212
  // src/generated/typert.remote-client.ts
20201
20213
  var StatusCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#Status", create: () => external_exports.unknown().transform((value) => parseStatus(value)) };
@@ -20203,6 +20215,7 @@ var QuotaSnapshotCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#
20203
20215
  var LoginStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#LoginState", create: () => external_exports.unknown().transform((value) => parseLoginState(value)) };
20204
20216
  var StartLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#StartLoginRequest", create: () => external_exports.unknown().transform((value) => parseStartLoginRequest(value)) };
20205
20217
  var CancelLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#CancelLoginRequest", create: () => external_exports.unknown().transform((value) => parseCancelLoginRequest(value)) };
20218
+ var ActivationOpenStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#ActivationOpenState", create: () => external_exports.unknown().transform((value) => parseActivationOpenState(value)) };
20206
20219
  var SaveApiKeyRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#SaveApiKeyRequest", create: () => external_exports.unknown().transform((value) => parseSaveApiKeyRequest(value)) };
20207
20220
  var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter", methods: {
20208
20221
  "openDesignCoRouter/status": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: StatusCodec },
@@ -20212,6 +20225,7 @@ var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter",
20212
20225
  "openDesignCoRouter/startLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: StartLoginRequestCodec, strictCodec: StartLoginRequestCodec }], result: LoginStateCodec },
20213
20226
  "openDesignCoRouter/loginStatus": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: LoginStateCodec },
20214
20227
  "openDesignCoRouter/cancelLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: CancelLoginRequestCodec, strictCodec: CancelLoginRequestCodec }], result: LoginStateCodec },
20228
+ "openDesignCoRouter/openActivationPage": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: ActivationOpenStateCodec },
20215
20229
  "openDesignCoRouter/saveApiKey": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: SaveApiKeyRequestCodec, strictCodec: SaveApiKeyRequestCodec }], result: StatusCodec }
20216
20230
  } }, external_exports);
20217
20231
  var typert_remote_client_default = TYPERT_REMOTE;
@@ -20235,13 +20249,14 @@ var MAX_REQUEST_TIMEOUT_MS = 36e5;
20235
20249
  var DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
20236
20250
  var MIN_STREAM_IDLE_TIMEOUT_MS = 1e3;
20237
20251
  var MAX_STREAM_IDLE_TIMEOUT_MS = 18e5;
20252
+ var RPC_TIMEOUT_MS = 3e4;
20238
20253
  var MAX_OUTPUT_TOKENS = 1e7;
20239
20254
  var MAX_CONTEXT_WINDOW = 1e8;
20240
20255
  var GENERIC_ERROR = "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
20241
20256
  var LOGIN_LABELS = {
20242
20257
  idle: "\u5C1A\u672A\u5F00\u59CB\u72EC\u7ACB\u767B\u5F55",
20243
20258
  launching: "\u6B63\u5728\u7533\u8BF7\u8BBE\u5907\u6388\u6743\u7801\u2026",
20244
- waiting: "\u8BF7\u6253\u5F00\u6388\u6743\u9875\uFF0C\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u4E2D\u786E\u8BA4\u672C\u6B21\u767B\u5F55\u3002",
20259
+ waiting: "\u8BF7\u5728\u6388\u6743\u7A97\u53E3\u6216\u5E38\u7528\u6D4F\u89C8\u5668\u4E2D\u786E\u8BA4\u672C\u6B21\u767B\u5F55\u3002",
20245
20260
  verifying: "\u6B63\u5728\u4FDD\u5B58\u8BBE\u5907\u6388\u6743\u2026",
20246
20261
  success: "\u767B\u5F55\u6210\u529F\uFF0C\u8BBE\u5907\u6388\u6743\u5DF2\u4FDD\u5B58\u3002",
20247
20262
  cancelled: "\u5DF2\u53D6\u6D88\u672C\u6B21\u767B\u5F55\u3002",
@@ -20332,7 +20347,7 @@ function createCaller(ctx) {
20332
20347
  if (!operation) throw new Error("Unknown generated Remote method");
20333
20348
  const parameter = operation.parameters[0];
20334
20349
  const input2 = operation.input({ strict: true }).create().parse(parameter ? { [parameter.wire]: request } : {});
20335
- const response = await withTimeout(Promise.resolve(operation.invoke(remote, input2)), 25e3, controller.signal);
20350
+ const response = await withTimeout(Promise.resolve(operation.invoke(remote, input2)), RPC_TIMEOUT_MS, controller.signal);
20336
20351
  if (!("ok" in response) || !response.ok) throw new Error("RPC failed");
20337
20352
  return response.value;
20338
20353
  };
@@ -21101,7 +21116,14 @@ function Switch({ checked, disabled, label, hint, onChange }) {
21101
21116
  function loginInFlight(state) {
21102
21117
  return state === "launching" || state === "waiting" || state === "verifying";
21103
21118
  }
21104
- function LoginApproval({ login }) {
21119
+ var ACTIVATION_OPEN_LABELS = {
21120
+ 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",
21121
+ "no-link": "\u6388\u6743\u7801\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u7533\u8BF7\u3002",
21122
+ "invalid-url": "\u6388\u6743\u94FE\u63A5\u672A\u901A\u8FC7\u6821\u9A8C\uFF0C\u8BF7\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00\u3002",
21123
+ 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",
21124
+ failed: "\u4E34\u65F6\u6D4F\u89C8\u5668\u7A97\u53E3\u672A\u80FD\u6253\u5F00\uFF0C\u8BF7\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00\u3002"
21125
+ };
21126
+ function LoginApproval({ login, onOpen, opening, notice }) {
21105
21127
  if (login?.state !== "waiting" || login.userCode === void 0 || login.expiresAt === void 0) return null;
21106
21128
  const url2 = validateActivationUrl(login.activationUrl, login.userCode);
21107
21129
  if (url2 === null) return null;
@@ -21113,10 +21135,19 @@ function LoginApproval({ login }) {
21113
21135
  h(
21114
21136
  "div",
21115
21137
  { className: "odc-actions" },
21116
- h("a", { className: "odc-button odc-button-primary", href: url2, target: "_blank", rel: "noreferrer noopener" }, "\u6253\u5F00\u6388\u6743\u9875")
21138
+ h("button", {
21139
+ className: "odc-button odc-button-primary",
21140
+ type: "button",
21141
+ disabled: opening === true || onOpen === void 0,
21142
+ onClick: () => {
21143
+ onOpen?.();
21144
+ }
21145
+ }, opening === true ? "\u6B63\u5728\u6253\u5F00\u2026" : "\u5728\u65B0\u7A97\u53E3\u6253\u5F00\u6388\u6743\u9875"),
21146
+ h("a", { className: "odc-button", href: url2, target: "_blank", rel: "noreferrer noopener" }, "\u590D\u5236\u94FE\u63A5\u624B\u52A8\u6253\u5F00")
21117
21147
  ),
21118
- h("p", { className: "odc-hint" }, `\u6388\u6743\u7801\u6709\u6548\u81F3 ${formatTime(login.expiresAt)}\u3002\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u786E\u8BA4\u540E\uFF0C\u8FD4\u56DE\u6B64\u9875\u67E5\u770B\u7ED3\u679C\u3002`),
21119
- h("p", { className: "odc-hint" }, "\u53EF\u53F3\u952E\u590D\u5236\u6388\u6743\u94FE\u63A5\u5230\u5E38\u7528\u6D4F\u89C8\u5668\uFF1B\u5BC6\u7801\u7BA1\u7406\u5668\u548C\u81EA\u52A8\u586B\u5145\u7531\u6D4F\u89C8\u5668\u6B63\u5E38\u63D0\u4F9B\u3002\u4EC5\u786E\u8BA4\u4F60\u672C\u4EBA\u53D1\u8D77\u7684\u6388\u6743\u3002")
21148
+ notice ? h(NoticeView, { notice }) : null,
21149
+ 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`),
21150
+ 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")
21120
21151
  );
21121
21152
  }
21122
21153
  function Connection({ status, formSnapshot, setFormField, pendingField, caller, loadStatus, refreshAfterLogin }) {
@@ -21126,23 +21157,39 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21126
21157
  const [notice, setNotice] = React.useState(null);
21127
21158
  const [apiKey, setApiKey] = React.useState("");
21128
21159
  const [keyBusy, setKeyBusy] = React.useState(false);
21160
+ const [opening, setOpening] = React.useState(false);
21161
+ const [activationNotice, setActivationNotice] = React.useState(null);
21162
+ const activationJob = React.useRef(null);
21163
+ const activationToken = React.useRef(0);
21164
+ const autoOpenPending = React.useRef(false);
21129
21165
  React.useEffect(() => () => {
21130
21166
  actionVersion.current += 1;
21131
21167
  }, []);
21132
21168
  const login = status?.login;
21169
+ React.useEffect(() => {
21170
+ const job = login?.jobId ?? null;
21171
+ if (activationJob.current === job) return;
21172
+ activationJob.current = job;
21173
+ activationToken.current += 1;
21174
+ setActivationNotice(null);
21175
+ setOpening(false);
21176
+ }, [login?.jobId]);
21133
21177
  const state = login?.state ?? "idle";
21134
21178
  const inFlight = loginInFlight(state);
21135
21179
  const startLogin = async () => {
21136
21180
  if (status === void 0 || busy || keyBusy) return;
21137
21181
  const version2 = ++actionVersion.current;
21138
21182
  setBusy(true);
21183
+ autoOpenPending.current = true;
21139
21184
  setNotice({ kind: "status", text: "\u6B63\u5728\u7533\u8BF7\u8BBE\u5907\u6388\u6743\u2026" });
21140
21185
  try {
21141
21186
  const next = await caller("startLogin", { expectedRevision: status.revision });
21142
21187
  if (!mounted.current || version2 !== actionVersion.current) return;
21143
21188
  setNotice(null);
21189
+ setActivationNotice(null);
21144
21190
  await (next.state === "success" ? refreshAfterLogin() : loadStatus());
21145
21191
  } catch {
21192
+ autoOpenPending.current = false;
21146
21193
  if (mounted.current && version2 === actionVersion.current) setNotice({ kind: "error", text: "\u65E0\u6CD5\u542F\u52A8\u767B\u5F55\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
21147
21194
  } finally {
21148
21195
  if (mounted.current && version2 === actionVersion.current) setBusy(false);
@@ -21186,6 +21233,32 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21186
21233
  if (mounted.current && version2 === actionVersion.current) setKeyBusy(false);
21187
21234
  }
21188
21235
  };
21236
+ const openActivation = async () => {
21237
+ if (status === void 0 || busy || keyBusy || opening || login?.state !== "waiting") return;
21238
+ const job = login.jobId;
21239
+ const token = ++activationToken.current;
21240
+ activationJob.current = job;
21241
+ setOpening(true);
21242
+ setActivationNotice(null);
21243
+ const current = () => mounted.current && activationToken.current === token && activationJob.current === job;
21244
+ try {
21245
+ const result = await caller("openActivationPage");
21246
+ if (current()) setActivationNotice({ kind: result.opened ? "status" : "error", text: ACTIVATION_OPEN_LABELS[result.code] });
21247
+ } catch {
21248
+ if (current()) setActivationNotice({ kind: "error", text: ACTIVATION_OPEN_LABELS.failed });
21249
+ } finally {
21250
+ if (mounted.current && activationToken.current === token) setOpening(false);
21251
+ }
21252
+ };
21253
+ React.useEffect(() => {
21254
+ if (!autoOpenPending.current || busy || keyBusy) return;
21255
+ if (state === "waiting") {
21256
+ autoOpenPending.current = false;
21257
+ void openActivation();
21258
+ } else if (state === "error" || state === "cancelled" || state === "success") {
21259
+ autoOpenPending.current = false;
21260
+ }
21261
+ }, [state, login?.jobId, busy, keyBusy]);
21189
21262
  const desktopChecked = formSnapshot.value?.useDesktopCredential ?? status?.config.useDesktopCredential ?? true;
21190
21263
  return h(
21191
21264
  React.Fragment,
@@ -21214,7 +21287,7 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21214
21287
  "section",
21215
21288
  { className: "odc-card" },
21216
21289
  h("h3", null, "\u8BBE\u5907\u6388\u6743\u767B\u5F55"),
21217
- h("p", { className: "odc-hint" }, "\u7533\u8BF7\u6388\u6743\u7801\u540E\uFF0C\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u5B98\u65B9\u6388\u6743\u9875\u3002\u6CBF\u7528\u5DF2\u6709\u767B\u5F55\u72B6\u6001\u3001\u5BC6\u7801\u7BA1\u7406\u5668\u548C\u81EA\u52A8\u586B\u5145\uFF1B\u786E\u8BA4\u540E\u81EA\u52A8\u4FDD\u5B58\u8BBE\u5907\u4EE4\u724C\uFF0C\u7528\u4E8E\u67E5\u8BE2\u989D\u5EA6\u53CA\u5B98\u65B9\u7F51\u5173\u63A8\u7406\u3002"),
21290
+ h("p", { className: "odc-hint" }, "\u7533\u8BF7\u6388\u6743\u7801\u540E\uFF0C\u7531\u63D2\u4EF6\u542F\u52A8\u4E34\u65F6\u7A97\u53E3\u663E\u793A\u5B98\u65B9\u6388\u6743\u9875\uFF0C\u4E5F\u53EF\u590D\u5236\u94FE\u63A5\u5230\u4F60\u81EA\u5DF1\u7684\u5E38\u7528\u6D4F\u89C8\u5668\u3002\u786E\u8BA4\u540E\u81EA\u52A8\u4FDD\u5B58\u8BBE\u5907\u4EE4\u724C\uFF0C\u7528\u4E8E\u67E5\u8BE2\u989D\u5EA6\u53CA\u5B98\u65B9\u7F51\u5173\u63A8\u7406\u3002"),
21218
21291
  h(
21219
21292
  "div",
21220
21293
  { className: "odc-actions", style: { marginTop: 14 } },
@@ -21226,7 +21299,9 @@ function Connection({ status, formSnapshot, setFormField, pendingField, caller,
21226
21299
  } }, "\u53D6\u6D88\u767B\u5F55") : null,
21227
21300
  h("a", { className: "odc-link", href: OFFICIAL_SITE, target: "_blank", rel: "noreferrer noopener" }, "\u6253\u5F00 CoRouter \u5B98\u7F51")
21228
21301
  ),
21229
- h(LoginApproval, { login }),
21302
+ h(LoginApproval, { login, onOpen: () => {
21303
+ void openActivation();
21304
+ }, opening, notice: activationNotice }),
21230
21305
  h("p", { className: "odc-meta", role: state === "error" ? "alert" : "status" }, login?.message ?? LOGIN_LABELS[state]),
21231
21306
  h(NoticeView, { notice })
21232
21307
  ),
@@ -3,8 +3,10 @@ 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.4";
6
+ export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.6";
7
7
  export declare const DEVICE_REQUEST_TIMEOUT_MS = 20000;
8
+ /** One bounded launch step of the temporary activation window. */
9
+ export declare const DEVICE_OPEN_TIMEOUT_MS = 30000;
8
10
  export declare const DEVICE_RESPONSE_MAX_BYTES = 65536;
9
11
  export declare const DEVICE_POLL_MIN_MS = 1000;
10
12
  export declare const DEVICE_POLL_MAX_MS = 300000;
@@ -12,8 +14,8 @@ export declare class DeviceAuthError extends Error {
12
14
  readonly code: string;
13
15
  constructor(code: string);
14
16
  }
15
- /** Strict public-link allowlist: only an optional, single, non-secret code is allowed. */
16
- 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;
17
19
  export interface DeviceAuthorization {
18
20
  deviceId: string;
19
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
  }
@@ -89,6 +96,7 @@ var MAX_API_KEY = 16384;
89
96
  var HTTP_ASCII = /^[\x21-\x7e]+$/u;
90
97
  var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
91
98
  var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
99
+ var ACTIVATION_OPEN_CODES = ["opened", "no-link", "invalid-url", "unavailable", "failed"];
92
100
  var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
93
101
  var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
94
102
  var SESSION_SOURCES = ["desktop", "login", "environment"];
@@ -264,6 +272,10 @@ function parseSaveApiKeyRequest(value) {
264
272
  if (key.length > MAX_API_KEY) throw new Error(`API Key \u4E0D\u80FD\u8D85\u8FC7 ${MAX_API_KEY} \u5B57\u7B26`);
265
273
  return { value: key, expectedRevision: text(value.expectedRevision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION) };
266
274
  }
275
+ function parseActivationOpenState(value) {
276
+ if (!record(value)) throw new Error("\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C\u5FC5\u987B\u662F\u5BF9\u8C61");
277
+ return { code: member(value.code, ACTIVATION_OPEN_CODES, "\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C"), opened: flag(value.opened, "\u6388\u6743\u9875\u662F\u5426\u5DF2\u6253\u5F00") };
278
+ }
267
279
 
268
280
  // src/generated/typert.remote-client.ts
269
281
  var StatusCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#Status", create: () => z.unknown().transform((value) => parseStatus(value)) };
@@ -271,6 +283,7 @@ var QuotaSnapshotCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#
271
283
  var LoginStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#LoginState", create: () => z.unknown().transform((value) => parseLoginState(value)) };
272
284
  var StartLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#StartLoginRequest", create: () => z.unknown().transform((value) => parseStartLoginRequest(value)) };
273
285
  var CancelLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#CancelLoginRequest", create: () => z.unknown().transform((value) => parseCancelLoginRequest(value)) };
286
+ var ActivationOpenStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#ActivationOpenState", create: () => z.unknown().transform((value) => parseActivationOpenState(value)) };
274
287
  var SaveApiKeyRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#SaveApiKeyRequest", create: () => z.unknown().transform((value) => parseSaveApiKeyRequest(value)) };
275
288
  var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter", methods: {
276
289
  "openDesignCoRouter/status": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: StatusCodec },
@@ -280,6 +293,7 @@ var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter",
280
293
  "openDesignCoRouter/startLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: StartLoginRequestCodec, strictCodec: StartLoginRequestCodec }], result: LoginStateCodec },
281
294
  "openDesignCoRouter/loginStatus": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: LoginStateCodec },
282
295
  "openDesignCoRouter/cancelLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: CancelLoginRequestCodec, strictCodec: CancelLoginRequestCodec }], result: LoginStateCodec },
296
+ "openDesignCoRouter/openActivationPage": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: ActivationOpenStateCodec },
283
297
  "openDesignCoRouter/saveApiKey": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: SaveApiKeyRequestCodec, strictCodec: SaveApiKeyRequestCodec }], result: StatusCodec }
284
298
  } }, z);
285
299
  var typert_remote_client_default = TYPERT_REMOTE;
@@ -1,5 +1,5 @@
1
1
  import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
2
- import type { Status, QuotaSnapshot, LoginState, StartLoginRequest, CancelLoginRequest, SaveApiKeyRequest } from '../remote.js';
2
+ import type { Status, QuotaSnapshot, LoginState, StartLoginRequest, CancelLoginRequest, ActivationOpenState, SaveApiKeyRequest } from '../remote.js';
3
3
  export type CoRouterRemote = {
4
4
  status: () => Promise<RemoteResult<Status>>;
5
5
  refreshModels: () => Promise<RemoteResult<Status>>;
@@ -8,6 +8,7 @@ export type CoRouterRemote = {
8
8
  startLogin: (input: StartLoginRequest) => Promise<RemoteResult<LoginState>>;
9
9
  loginStatus: () => Promise<RemoteResult<LoginState>>;
10
10
  cancelLogin: (input: CancelLoginRequest) => Promise<RemoteResult<LoginState>>;
11
+ openActivationPage: () => Promise<RemoteResult<ActivationOpenState>>;
11
12
  saveApiKey: (input: SaveApiKeyRequest) => Promise<RemoteResult<Status>>;
12
13
  };
13
14
  declare module '@deepseek-ai/dsh-typert-protocol' {