dsh-keys-manager 0.2.1 → 0.2.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,97 @@
1
+ # Changelog
2
+
3
+ All notable changes to dsh-keys-manager are documented here. Assembled from `.changes/unreleased/` fragments by the release-prep workflow.
4
+
5
+ ## [0.2.3] - 2026-10-22
6
+
7
+ ### Added
8
+
9
+ - The provider list gains five single-select quick filters — All, Built-in, Plugin, OAuth-capable and Transport — that narrow the managed and available lists and compose with the existing search.
10
+ - OAuth-capable uses the same verdict as a row's Add account button, so it lists exactly the routes that can start a sign-in right now.
11
+ - The chosen filter also governs the transport accounts section, which appears under All and Transport; with Transport selected and no accounts it stays visible with a `0` count and the existing empty copy.
12
+ - The quick-filter chips show a visible focus ring, the row is announced as a labelled group, the unattributed count keeps its number in its accessible name, and an empty transport list says so instead of reusing the search-empty copy.
13
+
14
+ ### Fixed
15
+
16
+ - A transport-plugin route (Cursor) now renders as a plugin Transport provider card and can no longer be adopted, key-bound or tested as a pi-ai provider; its account controls moved into that card.
17
+ - A transport account whose route id collides with an ordinary provider's id keeps its row (and its only sign-in/remove control) instead of being treated as card-owned by a card that never rendered it.
18
+ - Activating an account no longer holds the global mutation queue while it reads the OAuth credential, so a slow credential store cannot delay unrelated key or settings changes; the activation's errors, returned state and cooldown reset are unchanged.
19
+ - An activation now writes its credential projection only while the route still serves the value it read, and never deletes a grant another writer replaced. A rejected state flush puts the projection back — removing it only when the activation created it — so a refused activation leaves nothing behind.
20
+ - A refused activation restores the route's owner classification along with the projection it puts back, so requests keep using the restored credential and failures keep attributing to it instead of falling back to the key pool; the activated account keeps its claim whenever its projection is still serving.
21
+ - Provider rows show an `Unattributed` count for stream attempts a route served with no account owner — whether its ownership read had not landed yet or had failed — and the count clears once a later read attributes the route. A failover retry is a new attempt, so one request can be counted more than once.
22
+ - A request waiting on ownership hydration no longer keeps the process alive for the rest of the bounded wait.
23
+
24
+ ### Changed
25
+
26
+ - Release notes are now assembled from authored `.changes/unreleased/` fragments instead of `git log` subjects, and a release with no changelog bullets is refused.
27
+ - Backfill `CHANGELOG.md` for 0.1.0–0.2.2 with authored notes; earlier sections listed merge subjects.
28
+
29
+ ## [0.2.2] - 2026-10-21
30
+
31
+ ### Added
32
+
33
+ - Extra providers can declare environment-sourced request headers; values are read from your environment when the provider is added and refused loudly when missing, blank or credential-bearing.
34
+ - `coreweave` joins the catalog through that carrier with `COREWEAVE_API_KEY` and `COREWEAVE_PROJECT` for its `OpenAI-Project` header, and providers with declared headers use their seed catalog instead of model discovery.
35
+ - Changing the environment later surfaces as a state warning, and removing the provider drops only the header value it set.
36
+ - New published component `dsh-llm-omp-transports` serves Cursor through DSH's LLM seam: text and reasoning deltas, usage before a single finish, finish kinds, error classification and abort.
37
+ - Tool calls are projected from DSH into the transport and unresolved calls hand off to DSH's tool loop; transport-resolved native frames are not re-dispatched.
38
+ - Cursor sign-in uses a PKCE flow with poll and refresh, and a generated 123-row model table comes from a pinned catalog.
39
+ - The install now mounts two components (the plugin detail page lists both), and the keys manager lists, activates, removes and re-authenticates the resulting Cursor accounts beside the pi-ai pool.
40
+ - Key and account rows gain a Test action that runs one real request through the LLM seam and reports whether that credential serves the route, without touching health, cursor or binding state.
41
+
42
+ ### Changed
43
+
44
+ - Release prep drives both published packages from one version input: the resolved version is written to both manifests with both tags, and a transport manifest that disagrees with it is refused.
45
+
46
+ ### Fixed
47
+
48
+ - An OAuth-served route no longer fails with `MISSING_CREDENTIAL`: a broker reference is written only while the key pool actually serves the route, so pi-ai can reach its own grant, and a broken binding left by an earlier version is healed at startup.
49
+ - A login harvest that aborts because the attempt already settled now demotes the pi-ai projection it had written, so the route stops serving a superseded grant instead of staying bound to it.
50
+
51
+ ### Docs
52
+
53
+ - README documents the Desktop/Web install path.
54
+ - README lists the extra providers in a collapsible table.
55
+
56
+ ## [0.2.1] - 2026-10-20
57
+
58
+ ### Fixed
59
+
60
+ - The plugin declares `authorization` in its inject list, so loading no longer fails with `cannot get property "authorization" without inject`.
61
+
62
+ ## [0.2.0] - 2026-10-20
63
+
64
+ ### Added
65
+
66
+ - OAuth accounts are pool members beside keys for Anthropic, GitHub Copilot, Kimi Coding, OpenAI Codex, OpenRouter, Radius and xAI: each provider card offers "Add account" next to "Add key".
67
+ - Account rows show the signed-in identity, status (active, cooling, paused, missing), last-used time and counters, with activate, re-authenticate and remove actions.
68
+ - Sign-in runs through the DSH authorization seam, bridging device-code and paste prompts into the plugin dialog.
69
+ - Accounts inherit sticky, round-robin, failover and cooldown behaviour; a failing account cools down and the provider falls back to its key pool, and re-activating the account restores it.
70
+ - Refresh rotations upstream are synced back into the owning account, and grant tokens stay in the DSH credential store: the manager keeps references and identity metadata only.
71
+
72
+ ### Docs
73
+
74
+ - The install documentation states that a registry install ships a prebuilt tarball and runs no build on your machine, while the clone/link path builds during `pnpm i`.
75
+ - The per-language documentation links moved into each README's Documentation section; no link changed target.
76
+
77
+ ## [0.1.0] - 2026-10-19
78
+
79
+ ### Added
80
+
81
+ - Twelve extra API-key providers from oh-my-pi are available as ordinary `llm-pi-ai` routes: siliconflow, siliconflow-cn, litellm, charm-hyper, zhipu-coding-plan, alibaba-token-plan, abliteration, cline-pass, commandcode, firepass, yolo-auto, and ollama-cloud.
82
+ - An extra provider is added with its API key, shows a seed model list until runtime discovery overrides it, and is released back to the plain route when removed.
83
+ - Several API keys per provider, each with an alias and a health state, instead of one shared key.
84
+ - Each session is pinned to one key, with round-robin selection for new sessions.
85
+ - A failed request retries on the same key within a bounded budget, then fails over to another key.
86
+ - A failing key cools down exponentially, capped at seven days, and returns to the rotation on its own.
87
+ - The plugin detail page gains a search-driven manager, and the existing model cards show a "managed by Keys Manager" badge.
88
+
89
+ ### Fixed
90
+
91
+ - Retry pins expire after 60 seconds, and starting a new step no longer deletes another in-flight step's pin.
92
+ - Request-step records are keyed to the provider the request finally uses, and unmanaged providers are skipped before anything is recorded.
93
+
94
+ ### Docs
95
+
96
+ - README rebuilt around a quick start, the two supported install paths (`dsh plugin --profile web add` and clone plus `dsh plugin --profile web link .`), the provider table and the managed-state notes, with a full Chinese translation.
97
+ - Development, release and known-limit pages moved to `docs/` in both languages, and the known limits for concurrent steps, ambient request identity and purpose-scoped streams are stated instead of implied.
package/README.md CHANGED
@@ -23,27 +23,86 @@ manager.
23
23
  - Extra built-in providers installed as ordinary `llm-pi-ai` routes.
24
24
  - OAuth account sign-in for seven pi-ai-backed providers, stored beside
25
25
  the API keys.
26
+ - A companion transport component for providers the vendored pi-ai cannot
27
+ speak — Cursor (`cursor`) today — installed with the plugin.
26
28
 
27
29
  ### Extra providers
28
30
 
29
- `src/providers.ts` contains a curated catalog of 34 extra providers (21 from
30
- the original catalog plus 13 added in this release, for example SiliconFlow,
31
- Zhipu coding plan, Cline Pass, Ollama Cloud, and Alibaba Token Plan (CN)),
32
- derived from
31
+ This catalog of 35 extra providers comes from
33
32
  [oh-my-pi](https://github.com/can1357/oh-my-pi)'s provider catalog (MIT).
34
- An extra id is added only when the shipped DSH catalog does not already
35
- declare that route id (for example, `meta` and `typesafe` are not
36
- duplicated); same-endpoint providers with distinct credential identities
37
- are kept by design.
33
+
34
+ <details>
35
+ <summary>All 35 extra providers</summary>
36
+
37
+ | Provider | Route id | API key env |
38
+ |---|---|---|
39
+ | DeepInfra | `deepinfra` | `DEEPINFRA_API_KEY` |
40
+ | Novita | `novita` | `NOVITA_API_KEY` |
41
+ | StepFun | `stepfun` | `STEPFUN_API_KEY` |
42
+ | SambaNova | `sambanova` | `SAMBANOVA_API_KEY` |
43
+ | Qianfan | `qianfan` | `QIANFAN_API_KEY` |
44
+ | GMI Cloud | `gmi-cloud` | `GMI_API_KEY` |
45
+ | Synthetic | `synthetic` | `SYNTHETIC_API_KEY` |
46
+ | ZenMux | `zenmux` | `ZENMUX_API_KEY` |
47
+ | Kilo Gateway | `kilo` | `KILO_API_KEY` |
48
+ | NanoGPT | `nanogpt` | `NANO_GPT_API_KEY` |
49
+ | Venice AI | `venice` | `VENICE_API_KEY` |
50
+ | AIML API | `aimlapi` | `AIMLAPI_API_KEY` |
51
+ | AI& | `aiand` | `AIAND_API_KEY` |
52
+ | Helmcode | `helmcode` | `HELMCODE_API_KEY` |
53
+ | Alibaba Coding Plan | `alibaba-coding-plan` | `ALIBABA_CODING_PLAN_API_KEY` |
54
+ | MiniMax Coding (Global) | `minimax-code` | `MINIMAX_CODE_API_KEY` |
55
+ | MiniMax Coding (CN) | `minimax-code-cn` | `MINIMAX_CODE_CN_API_KEY` |
56
+ | Umans AI Coding Plan | `umans` | `UMANS_AI_CODING_PLAN_API_KEY` |
57
+ | Sakana AI | `sakana` | `SAKANA_API_KEY` |
58
+ | CoralBricks | `coralbricks` | `CORAL_API_KEY` |
59
+ | Wafer Serverless | `wafer-serverless` | `WAFER_SERVERLESS_API_KEY` |
60
+ | Alibaba Token Plan | `alibaba-token-plan` | `ALIBABA_TOKEN_PLAN_API_KEY` |
61
+ | Alibaba Token Plan (CN) | `alibaba-token-plan-cn` | `BAILIAN_TOKEN_PLAN_API_KEY` |
62
+ | Charm Hyper | `charm-hyper` | `CHARM_HYPER_API_KEY`, `HYPER_API_KEY` |
63
+ | LiteLLM Proxy | `litellm` | `LITELLM_API_KEY` |
64
+ | SiliconFlow | `siliconflow` | `SILICONFLOW_API_KEY` |
65
+ | SiliconFlow (CN) | `siliconflow-cn` | `SILICONFLOW_CN_API_KEY` |
66
+ | Zhipu Coding Plan | `zhipu-coding-plan` | `ZHIPU_API_KEY` |
67
+ | Abliteration | `abliteration` | `ABLITERATION_API_KEY`, `ABLIT_KEY` |
68
+ | ClinePass | `cline-pass` | `CLINE_API_KEY` |
69
+ | Command Code | `commandcode` | `COMMAND_CODE_API_KEY`, `COMMANDCODE_API_KEY` |
70
+ | CoreWeave Serverless Inference | `coreweave` | `COREWEAVE_API_KEY` |
71
+ | Fire Pass | `firepass` | `FIREPASS_API_KEY` |
72
+ | Ollama Cloud | `ollama-cloud` | `OLLAMA_CLOUD_API_KEY` |
73
+ | Yolo Auto | `yolo-auto` | `YOLO_AUTO_API_KEY` |
74
+
75
+ Where two variables are listed, either is accepted; the first is the primary.
76
+
77
+ CoreWeave Serverless Inference also needs `COREWEAVE_PROJECT=<team>/<project>` in the DSH process environment before it is added.
78
+
79
+ </details>
38
80
 
39
81
  ## Quick start
40
82
 
83
+ ### Install — DeepSeek Harness Desktop / Web
84
+
85
+ In the DeepSeek Harness Desktop or Web app, open **Plugins**, click
86
+ **Add plugin**, enter `dsh-keys-manager` in the package field, and click
87
+ **Install**. When the install finishes, choose **Enable now** so the plugin is
88
+ switched on.
89
+
90
+ Through the wizard, the app installs the same published npm package as the
91
+ CLI path below, already carrying its built bundle. Refresh the DeepSeek
92
+ Harness page afterwards so the browser half is fetched.
93
+
41
94
  ### Install — published package
42
95
 
43
96
  ```sh
44
97
  dsh plugin --profile web add dsh-keys-manager
45
98
  ```
46
99
 
100
+ Installing `dsh-keys-manager` installs two bundle components. The published
101
+ package depends on `dsh-llm-omp-transports`, so one install resolves both npm
102
+ packages, and the plugin page lists both `dsh-keys-manager` and
103
+ `llm-omp-transports`. Enable both: the manager owns credentials and routing,
104
+ the transport component owns the Cursor wire protocol.
105
+
47
106
  ### Install — local checkout
48
107
 
49
108
  ```sh
@@ -53,13 +112,13 @@ pnpm i
53
112
  dsh plugin --profile web link .
54
113
  ```
55
114
 
56
- Only the published-package path avoids a local build: `dsh plugin --profile web add dsh-keys-manager` installs the npm
57
- tarball as-is — package lifecycle scripts (`prepare` / `prepack`) do not run
58
- for registry installs, and the tarball already ships the built `lib/` and
59
- the client bundle. The local `dsh plugin --profile web link .` path is
60
- different — installing a local directory runs `prepare` during `pnpm i`,
61
- which builds `lib/` and the client bundle, so the clone flow above needs no
62
- manual build step.
115
+ The two CLI paths differ in where the build output comes from: `dsh plugin
116
+ --profile web add dsh-keys-manager` installs the published npm tarball, which
117
+ ships the built `lib/` and the client bundle. The local
118
+ `dsh plugin --profile web link .` path runs `prepare` during `pnpm i`, which
119
+ builds `lib/` and the client bundle, so the clone flow above includes that
120
+ build step; `prepack` builds the same output when the package is packed or
121
+ published.
63
122
 
64
123
  Refresh the Web page after installing so the browser half is fetched.
65
124
 
@@ -113,6 +172,25 @@ credential store, behind the account's `grantRef`; the manager state keeps
113
172
  the reference plus non-sensitive metadata (email, account id, expiry) — the
114
173
  same secret-free layout as API keys.
115
174
 
175
+ ### Cursor transport
176
+
177
+ Cursor is served by the companion `dsh-llm-omp-transports` component on its
178
+ own `cursor` route. It does not run through the pi-ai key pool: the route
179
+ keeps exactly one grant at `llm-omp-transports/cursor`, refreshed
180
+ automatically on the credential seam.
181
+
182
+ - Models come from the pinned `@oh-my-pi/pi-catalog@18.12.3` table (123 rows),
183
+ generated into the transport at build time. The route streams text and
184
+ reasoning, and hands tool calls to DSH's tool loop.
185
+ - Sign in from the **Transport accounts** section of the Keys Manager page: a
186
+ browser page plus a poll. The row shows identity, expiry and status; after an
187
+ expired or rejected grant, re-login is manual.
188
+ - Images are not forwarded yet. The route advertises text-only input and
189
+ refuses image blocks with a typed error instead of dropping them.
190
+ - The client is unofficial and presents Cursor's vendor-internal OAuth
191
+ identity. Read the [transports package docs](packages/llm-omp/README.md)
192
+ before relying on it; the risks and limits are listed there.
193
+
116
194
  ### Selection and failure policy
117
195
 
118
196
  - **Sticky session**: the first request of a session selects a key and binds
@@ -184,6 +262,20 @@ also support OAuth (Anthropic, GitHub Copilot, Kimi Coding, OpenAI Codex,
184
262
  OpenRouter, Radius, xAI) remain manageable through their API-key path, and
185
263
  their OAuth sign-in is available as [OAuth accounts](#oauth-accounts).
186
264
 
265
+ ## Repository layout
266
+
267
+ This repository is a pnpm workspace with two published packages:
268
+
269
+ | Path | npm package | Role |
270
+ |---|---|---|
271
+ | root | `dsh-keys-manager` | Host plugin, Keys Manager Web client, bundle patch |
272
+ | `packages/llm-omp` | `dsh-llm-omp-transports` | Cursor (and future) transport component |
273
+
274
+ The root package declares the transports package as a dependency, so the
275
+ published Keys Manager tarball pulls the transport in rather than vendoring
276
+ it. Both packages are released together and carry the same version; see
277
+ [Release](docs/release.md).
278
+
187
279
  ## Documentation
188
280
 
189
281
  | Doc | Content |
@@ -191,6 +283,7 @@ their OAuth sign-in is available as [OAuth accounts](#oauth-accounts).
191
283
  | [Development](docs/development.md) | build and dev workflow, repository layout |
192
284
  | [Release](docs/release.md) | CI checks and the npm release flow |
193
285
  | [Known limits](docs/known-limits.md) | current design boundaries, including deferred providers |
286
+ | [Transports package](packages/llm-omp/README.md) | Cursor transport: support status, risks, build/test, release |
194
287
 
195
288
  Chinese versions: [README.zh-CN.md](README.zh-CN.md) and the `*.zh-CN.md`
196
289
  files in `docs/` — [开发](docs/development.zh-CN.md) ·
package/README.zh-CN.md CHANGED
@@ -19,19 +19,76 @@ DSH Keys Manager 是一个 DeepSeek Harness 插件,把一个提供商 API key
19
19
  - 既有 Models 提供商卡片内出现"由 Keys Manager 托管"徽标。
20
20
  - 额外内置提供商以普通 `llm-pi-ai` 路由安装。
21
21
  - 七家 pi-ai 覆盖的提供商支持 OAuth 账户登录,凭据与 API key 并存管理。
22
+ - 随插件附带一个传输组件,服务随附 pi-ai 无法支持的提供商——目前是 Cursor(`cursor`)。
22
23
 
23
24
  ### 额外提供商
24
25
 
25
- `src/providers.ts` 收录 34 个精选提供商(原有 21 个加本次发布新增 13 个,例如 SiliconFlow、智谱编码套餐、Cline Pass、Ollama Cloud、Alibaba Token Plan (CN)),目录源自 [oh-my-pi](https://github.com/can1357/oh-my-pi)(MIT)。只有当随附 DSH 目录尚未声明该 route id 时才会新增条目(例如 `meta`、`typesafe` 不会重复提供);与既有端点共享 base URL、但凭据身份不同的提供商会按设计保留。
26
+ 这 35 个额外提供商收录自
27
+ [oh-my-pi](https://github.com/can1357/oh-my-pi) 的提供商目录(MIT)。
28
+
29
+ <details>
30
+ <summary>全部 35 个额外提供商</summary>
31
+
32
+ | 提供商 | Route id | API key 环境变量 |
33
+ |---|---|---|
34
+ | DeepInfra | `deepinfra` | `DEEPINFRA_API_KEY` |
35
+ | Novita | `novita` | `NOVITA_API_KEY` |
36
+ | StepFun | `stepfun` | `STEPFUN_API_KEY` |
37
+ | SambaNova | `sambanova` | `SAMBANOVA_API_KEY` |
38
+ | Qianfan | `qianfan` | `QIANFAN_API_KEY` |
39
+ | GMI Cloud | `gmi-cloud` | `GMI_API_KEY` |
40
+ | Synthetic | `synthetic` | `SYNTHETIC_API_KEY` |
41
+ | ZenMux | `zenmux` | `ZENMUX_API_KEY` |
42
+ | Kilo Gateway | `kilo` | `KILO_API_KEY` |
43
+ | NanoGPT | `nanogpt` | `NANO_GPT_API_KEY` |
44
+ | Venice AI | `venice` | `VENICE_API_KEY` |
45
+ | AIML API | `aimlapi` | `AIMLAPI_API_KEY` |
46
+ | AI& | `aiand` | `AIAND_API_KEY` |
47
+ | Helmcode | `helmcode` | `HELMCODE_API_KEY` |
48
+ | Alibaba Coding Plan | `alibaba-coding-plan` | `ALIBABA_CODING_PLAN_API_KEY` |
49
+ | MiniMax Coding (Global) | `minimax-code` | `MINIMAX_CODE_API_KEY` |
50
+ | MiniMax Coding (CN) | `minimax-code-cn` | `MINIMAX_CODE_CN_API_KEY` |
51
+ | Umans AI Coding Plan | `umans` | `UMANS_AI_CODING_PLAN_API_KEY` |
52
+ | Sakana AI | `sakana` | `SAKANA_API_KEY` |
53
+ | CoralBricks | `coralbricks` | `CORAL_API_KEY` |
54
+ | Wafer Serverless | `wafer-serverless` | `WAFER_SERVERLESS_API_KEY` |
55
+ | Alibaba Token Plan | `alibaba-token-plan` | `ALIBABA_TOKEN_PLAN_API_KEY` |
56
+ | Alibaba Token Plan (CN) | `alibaba-token-plan-cn` | `BAILIAN_TOKEN_PLAN_API_KEY` |
57
+ | Charm Hyper | `charm-hyper` | `CHARM_HYPER_API_KEY`, `HYPER_API_KEY` |
58
+ | LiteLLM Proxy | `litellm` | `LITELLM_API_KEY` |
59
+ | SiliconFlow | `siliconflow` | `SILICONFLOW_API_KEY` |
60
+ | SiliconFlow (CN) | `siliconflow-cn` | `SILICONFLOW_CN_API_KEY` |
61
+ | Zhipu Coding Plan | `zhipu-coding-plan` | `ZHIPU_API_KEY` |
62
+ | Abliteration | `abliteration` | `ABLITERATION_API_KEY`, `ABLIT_KEY` |
63
+ | ClinePass | `cline-pass` | `CLINE_API_KEY` |
64
+ | Command Code | `commandcode` | `COMMAND_CODE_API_KEY`, `COMMANDCODE_API_KEY` |
65
+ | CoreWeave Serverless Inference | `coreweave` | `COREWEAVE_API_KEY` |
66
+ | Fire Pass | `firepass` | `FIREPASS_API_KEY` |
67
+ | Ollama Cloud | `ollama-cloud` | `OLLAMA_CLOUD_API_KEY` |
68
+ | Yolo Auto | `yolo-auto` | `YOLO_AUTO_API_KEY` |
69
+
70
+ 列出两个变量时二者皆可接受,第一个为主变量。
71
+
72
+ CoreWeave Serverless Inference 还需在添加前把 `COREWEAVE_PROJECT=<team>/<project>` 设置到 DSH 进程环境中。
73
+
74
+ </details>
26
75
 
27
76
  ## 快速开始
28
77
 
78
+ ### 安装 —— DeepSeek Harness 桌面版 / Web 版
79
+
80
+ 桌面版或 Web 版打开 **插件** 页面,点击 **添加插件**,在包名输入框中填入 `dsh-keys-manager` 并点击 **安装**;安装完成后选择 **立即启用** 启用插件。
81
+
82
+ 通过向导安装时,应用会下载与下面 CLI 路径相同的已发布 npm 包,包内已包含构建好的产物。安装完成后刷新页面,让插件的浏览器侧代码被加载。
83
+
29
84
  ### 安装 —— 已发布包
30
85
 
31
86
  ```sh
32
87
  dsh plugin --profile web add dsh-keys-manager
33
88
  ```
34
89
 
90
+ 安装 `dsh-keys-manager` 会安装两个 bundle 组件。已发布包依赖 `dsh-llm-omp-transports`,因此一次安装会解析两个 npm 包,插件页面会同时列出 `dsh-keys-manager` 与 `llm-omp-transports`。两者都要启用:管理器负责凭据与路由,传输组件负责 Cursor 的线协议。
91
+
35
92
  ### 安装 —— 本地检出
36
93
 
37
94
  ```sh
@@ -41,7 +98,7 @@ pnpm i
41
98
  dsh plugin --profile web link .
42
99
  ```
43
100
 
44
- 只有已发布包的安装路径不会在本地执行构建:`dsh plugin --profile web add dsh-keys-manager` 按原样安装 npm tarball——注册表安装不会运行包生命周期脚本(`prepare` / `prepack`),tarball 内已带构建好的 `lib/` 与客户端 bundle。本地 `dsh plugin --profile web link .` 路径不同——安装本地目录会在 `pnpm i` 期间运行 `prepare`,构建 `lib/` 与客户端 bundle,因此上面的克隆流程不需要手动构建步骤。
101
+ 两条 CLI 安装路径的区别在于构建产物的来源:`dsh plugin --profile web add dsh-keys-manager` 安装的已发布 npm tarball 中已包含构建好的 `lib/` 与客户端 bundle。本地 `dsh plugin --profile web link .` 路径会在 `pnpm i` 期间运行 `prepare`,构建 `lib/` 与客户端 bundle,因此上面的克隆流程包含这一步构建;`prepack` 则在打包或发布时生成同样的产物。
45
102
 
46
103
  安装完成后刷新 Web 页面,让浏览器侧代码被取到。
47
104
 
@@ -65,6 +122,15 @@ dsh plugin --profile web link .
65
122
 
66
123
  添加账户只把提供商签发的授权存进 Harness 凭据存储(挂在账户的 `grantRef` 之下);管理器状态里只存引用与非敏感元数据(邮箱、账户 ID、过期时间)——与 API key 相同的无明文布局。
67
124
 
125
+ ### Cursor 传输
126
+
127
+ Cursor 由随附的 `dsh-llm-omp-transports` 组件在独立 `cursor` 路由上提供。它不经过 pi-ai key 池:该路由在 `llm-omp-transports/cursor` 只保留一条授权,并在凭据 seam 上自动刷新。
128
+
129
+ - 模型来自固定的 `@oh-my-pi/pi-catalog@18.12.3` 表(123 行),构建时生成进传输包。路由支持文本与推理流,并把工具调用交给 DSH 的工具循环。
130
+ - 在 Keys Manager 页面的 **传输账户**(`Transport accounts`)区块登录:浏览器页面加轮询。账户行显示身份、有效期与状态;授权过期或被拒后需要手动重新登录。
131
+ - 暂不转发图片。路由按纯文本宣告输入,遇到图片块以类型化错误拒绝,而不是静默丢弃。
132
+ - 该客户端是非官方实现,会呈现 Cursor 厂商内部的 OAuth 身份。依赖它之前请阅读[传输包文档](packages/llm-omp/README.md),风险与限制列在那里。
133
+
68
134
  ### 选择与故障策略
69
135
 
70
136
  - **会话粘滞**:会话的首次请求选出一把 key 并为该会话/提供商对绑定,后续请求复用。
@@ -110,6 +176,17 @@ $DSH_HOME/keys-manager/state.json
110
176
 
111
177
  其余 DSH 目录路由都提供 API key 凭据。同时支持 OAuth 的提供商(Anthropic、GitHub Copilot、Kimi Coding、OpenAI Codex、OpenRouter、Radius、xAI)仍可经其 API key 路径管理,其 OAuth 登录则以 [OAuth 账户](#oauth-账户)形式提供。
112
178
 
179
+ ## 仓库布局
180
+
181
+ 本仓库是 pnpm workspace,包含两个已发布包:
182
+
183
+ | 路径 | npm 包 | 职责 |
184
+ |---|---|---|
185
+ | 根目录 | `dsh-keys-manager` | Host 插件、Keys Manager Web 客户端、bundle patch |
186
+ | `packages/llm-omp` | `dsh-llm-omp-transports` | Cursor(及后续)传输组件 |
187
+
188
+ 根包把传输包声明为依赖,因此已发布的 Keys Manager tarball 会拉取传输包,而不是把它 vendor 进来。两个包一起发布且版本号一致,详见[发布](docs/release.zh-CN.md)。
189
+
113
190
  ## 文档
114
191
 
115
192
  | 文档 | 内容 |
@@ -117,6 +194,7 @@ $DSH_HOME/keys-manager/state.json
117
194
  | [开发](docs/development.zh-CN.md) | 构建与开发工作流、仓库布局 |
118
195
  | [发布](docs/release.zh-CN.md) | CI 检查与 npm 发布流程 |
119
196
  | [已知限制](docs/known-limits.zh-CN.md) | 当前设计边界,含延后提供商 |
197
+ | [传输包(英文)](packages/llm-omp/README.md) | Cursor 传输:支持状态、风险、构建/测试与发布 |
120
198
 
121
199
  英文版本:[README.md](README.md) 及 `docs/` 下的 `*.md` —— [Development](docs/development.md) · [Release](docs/release.md) · [Known limits](docs/known-limits.md)。当前设计边界速览:[已知限制](docs/known-limits.zh-CN.md)。
122
200
 
@@ -1,5 +1,8 @@
1
- # Mounts the Keys Manager Host plugin. The Client half is declared by
2
- # package.json#dsh.client and is served from lib/client.js.
1
+ # Mounts the Keys Manager Host plugin plus its second component, the
2
+ # llm-omp-transports transport package. The Client half is declared by package.json#dsh.client and
3
+ # is served from lib/client.js.
3
4
  - insert:
4
5
  - id: dsh-keys-manager
5
6
  name: 'dsh-keys-manager'
7
+ - id: llm-omp-transports
8
+ name: 'dsh-llm-omp-transports'
@@ -1,2 +1,2 @@
1
1
  /** Stylesheet owned by the Keys Manager client surface. */
2
- export declare const CSS = "\n.km-page{max-width:920px;margin:0 auto;padding:0 0 8px;color:var(--dsw-alias-label-primary);font-size:13.5px;line-height:1.5}\n.km-embedded-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin:0 0 16px}\n.km-summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:0 0 16px;flex:1;min-width:0}\n.km-embedded-head .km-summary{margin:0}\n.km-embedded-actions{display:flex;gap:8px;flex-shrink:0;padding-top:2px}\n.km-summary-line{color:var(--dsw-alias-label-secondary);font-size:12.5px;line-height:1.5}\n.km-stat{display:flex;flex-direction:column;gap:2px;padding:11px 14px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.km-stat strong{font-size:19px;line-height:1.15;font-weight:650;font-variant-numeric:tabular-nums}\n.km-stat span{color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-search{position:relative;display:flex;align-items:center;margin:0 0 20px}\n.km-search-icon{position:absolute;left:12px;color:var(--dsw-alias-label-secondary);pointer-events:none}\n.km-search-input{width:100%;box-sizing:border-box;padding:10px 38px 10px 36px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}\n.km-search-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 20%,transparent)}\n.km-search-clear{position:absolute;right:8px;display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-search-clear:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-section{margin:0 0 22px}\n.km-section-head{display:flex;align-items:baseline;gap:8px;margin:0 0 9px}\n.km-section-title{font-size:14px;font-weight:650;margin:0;letter-spacing:.01em}\n.km-section-count{font-size:12px;color:var(--dsw-alias-label-secondary)}\n.km-provider-list{display:flex;flex-direction:column;gap:8px}\n.km-show-more{display:flex;justify-content:center;padding:8px 0 0}\n.km-card{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}\n.km-provider-managed{border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 26%,var(--dsw-alias-border-l1))}\n.km-provider-open{overflow:visible}\n.km-provider-head{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}\n.km-provider-head:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 3%,transparent)}\n.km-avatar{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex:0 0 32px;border-radius:9px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);font-weight:650;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.km-provider-main{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}\n.km-provider-title-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}\n.km-provider-name{font-weight:650;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}\n.km-provider-meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap;color:var(--dsw-alias-label-secondary);font-size:11.5px;min-width:0}\n.km-provider-side{display:flex;align-items:center;gap:9px;flex-shrink:0}\n.km-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap}\n.km-chevron{color:var(--dsw-alias-label-secondary);transition:transform .16s ease}\n.km-chevron-open{transform:rotate(180deg)}\n.km-provider-body{border-top:1px solid var(--dsw-alias-border-l1);padding:13px 14px 14px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 40%,transparent)}\n.km-adopt{display:flex;flex-direction:column;gap:12px}\n.km-adopt-copy{display:flex;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-adopt-title{color:var(--dsw-alias-label-primary);font-size:12.5px}\n.km-adopt-actions{display:flex;gap:8px;align-items:center}\n.km-meta-line{display:flex;gap:6px;align-items:baseline;flex-wrap:wrap;font-size:11.5px}\n.km-managed-body{display:flex;flex-direction:column;gap:10px}\n.km-managed-toolbar{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap}\n.km-managed-summary{display:flex;flex-direction:column;gap:2px;min-width:0;font-size:11.5px}\n.km-managed-actions{display:flex;gap:6px;align-items:center;flex-shrink:0}\n.km-keys{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-bg-layer-1);overflow:visible}\n.km-key-row{position:relative;display:grid;grid-template-columns:minmax(0,1.35fr) minmax(150px,.8fr) minmax(150px,.9fr) 32px;gap:10px;align-items:center;padding:10px 11px;border-top:1px solid var(--dsw-alias-border-l1)}\n.km-key-row:first-child{border-top:0}\n.km-account-row{position:relative;display:grid;grid-template-columns:minmax(0,1.35fr) minmax(150px,.8fr) minmax(150px,.9fr) 32px;gap:10px;align-items:center;padding:10px 11px 10px 14px;border-top:1px solid var(--dsw-alias-border-l1)}\n.km-account-row:first-child{border-top:0}\n.km-account-row::before{content:'';position:absolute;left:0;top:12px;bottom:12px;width:2px;border-radius:1px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,transparent)}\n.km-account-glyph{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;flex:0 0 22px;border-radius:50%;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 14%,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-state-business-primary)}\n.km-key-identity{min-width:0;display:flex;flex-direction:column;gap:2px}\n.km-key-alias-line{display:flex;align-items:center;gap:6px;min-width:0}\n.km-key-alias{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}\n.km-key-sub{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-secondary);flex-wrap:wrap}\n.km-key-status{display:flex;flex-direction:column;gap:2px;align-items:flex-start}\n.km-key-last{display:flex;flex-direction:column;gap:2px;min-width:0}\n.km-key-time{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}\n.km-key-time.km-warn{color:var(--dsw-alias-state-warn-primary)}\n.km-key-actions{position:relative;display:flex;justify-content:flex-end}\n.km-status{display:inline-flex;align-items:center;gap:5px;padding:2px 7px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-size:11px;white-space:nowrap;color:var(--dsw-alias-label-secondary)}\n.km-status-success{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 26%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}\n.km-status-warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 8%,transparent)}\n.km-status-error{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent)}\n.km-status-idle{color:var(--dsw-alias-state-idle-primary)}\n.km-dot{display:inline-block;width:6px;height:6px;border-radius:50%;flex:0 0 6px;background:var(--dsw-alias-state-idle-primary)}\n.km-dot-success{background:var(--dsw-alias-state-success-primary)}\n.km-dot-warn{background:var(--dsw-alias-state-warn-primary)}\n.km-dot-error{background:var(--dsw-alias-state-error-primary)}\n.km-dot-idle{background:var(--dsw-alias-state-idle-primary)}\n.km-dot-sep{color:var(--dsw-alias-label-secondary)}\n.km-tag{display:inline-flex;align-items:center;padding:1px 6px;border-radius:5px;font-size:10.5px;line-height:1.45;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);white-space:nowrap}\n.km-tag-accent{color:var(--dsw-alias-state-business-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 34%,var(--dsw-alias-border-l1));background:var(--dsw-alias-state-business-tertiary)}\n.km-tag-warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 8%,transparent)}\n.km-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:0 0 auto}\n.km-icon-btn:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary)}\n.km-icon-btn:disabled{opacity:.5;cursor:default}\n.km-menu{position:absolute;top:32px;right:0;z-index:20;min-width:138px;padding:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-overlay);box-shadow:0 7px 22px color-mix(in srgb,var(--dsw-alias-label-primary) 14%,transparent)}\n.km-menu-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;text-align:left;cursor:pointer}\n.km-menu-item:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-menu-item:disabled{opacity:.5;cursor:default}\n.km-menu-danger{color:var(--dsw-alias-state-error-primary)}\n.km-menu-sep{height:1px;margin:4px 2px;background:var(--dsw-alias-border-l1)}\n.km-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:7px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;font-weight:550;line-height:1.2;cursor:pointer;white-space:nowrap;transition:opacity .15s ease,background .15s ease}\n.km-btn:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-btn:disabled{opacity:.5;cursor:default}\n.km-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);border-color:transparent}\n.km-btn-primary:hover{opacity:.9;background:var(--dsw-alias-label-primary)}\n.km-btn-ghost{background:transparent;border-color:transparent;color:var(--dsw-alias-label-secondary)}\n.km-btn-ghost:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-btn-danger{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-bg-base);border-color:transparent}\n.km-btn-danger:hover{opacity:.9;background:var(--dsw-alias-state-error-primary)}\n.km-btn-small{padding:5px 8px;font-size:12px;border-radius:7px}\n.km-input{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;outline:none}\n.km-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}\n.km-input:disabled{opacity:.6}\nselect.km-input{appearance:auto}\n.km-form{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-bg-layer-1)}\n.km-form-lead{display:flex;flex-direction:column;gap:2px}\n.km-form-title{font-weight:650;font-size:12.5px}\n.km-form-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:10px}\n.km-field{display:flex;flex-direction:column;gap:5px;min-width:0}\n.km-field-wide{min-width:0}\n.km-field-label{font-size:11.5px;font-weight:550;color:var(--dsw-alias-label-secondary)}\n.km-field-hint{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.45}\n.km-secret{position:relative;display:flex;align-items:center}\n.km-secret-input{padding-right:38px}\n.km-secret-toggle{position:absolute;right:4px;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-secret-toggle:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-form-actions{display:flex;justify-content:flex-end;gap:8px}\n.km-rename{display:flex;align-items:center;gap:4px;min-width:0;flex:1 1 auto;max-width:100%}\n.km-rename-input{flex:1 1 auto;width:auto;min-width:60px;padding:4px 7px;font-size:12.5px}\n.km-inline-error{padding:7px 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,var(--dsw-alias-border-l1));border-radius:7px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);color:var(--dsw-alias-state-error-primary);font-size:12px;word-break:break-word}\n.km-key-error{grid-column:1/-1;margin-top:-2px}\n.km-notice{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-notice-icon{flex:0 0 auto;margin-top:1px}\n.km-notice-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-notice-error{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,transparent)}\n.km-notice-body{display:flex;flex-direction:column;gap:2px;min-width:0}\n.km-notice-title{color:var(--dsw-alias-label-primary);font-weight:600;font-size:12px}\n.km-notice-text{line-height:1.5}\n.km-notice-action{flex:0 0 auto}\n.km-empty-keys{padding:12px;border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-empty{display:flex;align-items:center;gap:8px;padding:14px;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-provider-foot{display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:10px;padding-top:9px;border-top:1px dashed var(--dsw-alias-border-l1);font-size:11px;min-height:15px}\n.km-settings{padding:14px}\n.km-settings-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}\n.km-settings-title{font-size:14px;font-weight:650;margin:0 0 3px}\n.km-settings-intro{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-settings-icon{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-border-l1)}\n.km-settings-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr);gap:12px;align-items:start}\n.km-field-note{grid-column:1/-1}\n.km-policy-note{padding:9px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}\n.km-policy-title{display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;color:var(--dsw-alias-label-primary);margin-bottom:2px}\n.km-policy-note p{margin:0;font-size:11.5px;line-height:1.5}\n.km-settings-actions{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:12px}\n.km-saved{display:inline-flex;align-items:center;gap:4px;color:var(--dsw-alias-state-success-primary);font-size:11.5px}\n.km-banner{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border-radius:8px;margin:0 0 12px;font-size:12px;line-height:1.5;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}\n.km-banner-error{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,transparent)}\n.km-banner-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-banner-text{flex:1;min-width:0;word-break:break-word}\n.km-banner-close{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:0;border-radius:5px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-banner-close:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-loading{display:flex;align-items:center;gap:8px;padding:20px 0;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-load-failure{display:flex;flex-direction:column;gap:10px;align-items:flex-start;padding:18px;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-empty-title{font-weight:600;color:var(--dsw-alias-label-primary)}\n.km-footer{display:flex;align-items:flex-start;gap:6px;padding-top:4px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.5}\n.km-muted{color:var(--dsw-alias-label-secondary)}\n.km-warn{color:var(--dsw-alias-state-warn-primary)}\n.km-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}\n.km-truncate{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.km-spin{animation:km-spin 1s linear infinite}\n.km-models-note{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin:8px 0 0;padding:7px 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-brand-primary) 24%,var(--dsw-alias-border-l1));border-radius:7px;background:color-mix(in srgb,var(--dsw-alias-brand-primary) 6%,transparent);font-size:11.5px;color:var(--dsw-alias-label-primary)}\n.km-models-note-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-models-note-text{font-weight:600}\n.km-models-note-hint{color:var(--dsw-alias-label-secondary);font-weight:400}\n.km-modal-backdrop{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:24px;background:color-mix(in srgb,var(--dsw-alias-label-primary) 26%,transparent)}\n.km-modal{width:min(440px,100%);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-overlay);padding:16px;box-shadow:0 18px 48px color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent)}\n.km-modal-title{font-weight:650;font-size:14px;margin-bottom:7px}\n.km-modal-body{color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-modal-copy{margin:0;line-height:1.55}\n.km-login-flow{display:flex;flex-direction:column;gap:10px}\n.km-login-pending{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-login-notices{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5;word-break:break-word}\n.km-login-link{color:var(--dsw-alias-state-business-primary);text-decoration:underline;text-underline-offset:2px}\n.km-login-link:hover{color:var(--dsw-alias-label-primary)}\n.km-login-prompt{display:flex;flex-direction:column;gap:10px;padding-top:2px;border-top:1px dashed var(--dsw-alias-border-l1)}\n.km-login-prompt-title{font-weight:600;font-size:12.5px;color:var(--dsw-alias-label-primary)}\n.km-modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n@keyframes km-spin{to{transform:rotate(360deg)}}\n@media(max-width:720px){\n .km-summary{grid-template-columns:repeat(2,minmax(0,1fr))}\n .km-key-row,.km-account-row{grid-template-columns:minmax(0,1fr) 32px;gap:6px}\n .km-key-status,.km-key-last{grid-column:1;padding-left:0}\n .km-key-actions{grid-column:2;grid-row:1}\n .km-settings-grid{grid-template-columns:1fr}\n .km-form-grid{grid-template-columns:1fr}\n .km-provider-side .km-status{display:none}\n .km-embedded-head{flex-direction:column;gap:10px}\n .km-embedded-actions{padding-top:0}\n}\n";
2
+ export declare const CSS = "\n.km-page{max-width:920px;margin:0 auto;padding:0 0 8px;color:var(--dsw-alias-label-primary);font-size:13.5px;line-height:1.5}\n.km-embedded-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin:0 0 16px}\n.km-summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:0 0 16px;flex:1;min-width:0}\n.km-embedded-head .km-summary{margin:0}\n.km-embedded-actions{display:flex;gap:8px;flex-shrink:0;padding-top:2px}\n.km-summary-line{color:var(--dsw-alias-label-secondary);font-size:12.5px;line-height:1.5}\n.km-stat{display:flex;flex-direction:column;gap:2px;padding:11px 14px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.km-stat strong{font-size:19px;line-height:1.15;font-weight:650;font-variant-numeric:tabular-nums}\n.km-stat span{color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}\n.km-chip{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;border:1px solid var(--dsw-alias-border-l1);border-radius:999px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;cursor:pointer}\n.km-chip:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-chip:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 20%,transparent)}\n.km-chip-on{border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary)}\n.km-chip-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}\n.km-chip-on .km-chip-count{color:inherit}\n.km-search{position:relative;display:flex;align-items:center;margin:0 0 20px}\n.km-search-icon{position:absolute;left:12px;color:var(--dsw-alias-label-secondary);pointer-events:none}\n.km-search-input{width:100%;box-sizing:border-box;padding:10px 38px 10px 36px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}\n.km-search-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 20%,transparent)}\n.km-search-clear{position:absolute;right:8px;display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-search-clear:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-section{margin:0 0 22px}\n.km-section-head{display:flex;align-items:baseline;gap:8px;margin:0 0 9px}\n.km-section-title{font-size:14px;font-weight:650;margin:0;letter-spacing:.01em}\n.km-section-count{font-size:12px;color:var(--dsw-alias-label-secondary)}\n.km-provider-list{display:flex;flex-direction:column;gap:8px}\n.km-show-more{display:flex;justify-content:center;padding:8px 0 0}\n.km-card{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}\n.km-provider-managed{border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 26%,var(--dsw-alias-border-l1))}\n.km-provider-open{overflow:visible}\n.km-provider-head{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}\n.km-provider-head:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 3%,transparent)}\n.km-avatar{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex:0 0 32px;border-radius:9px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);font-weight:650;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.km-provider-main{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}\n.km-provider-title-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}\n.km-provider-name{font-weight:650;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}\n.km-provider-meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap;color:var(--dsw-alias-label-secondary);font-size:11.5px;min-width:0}\n.km-provider-side{display:flex;align-items:center;gap:9px;flex-shrink:0}\n.km-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap}\n.km-chevron{color:var(--dsw-alias-label-secondary);transition:transform .16s ease}\n.km-chevron-open{transform:rotate(180deg)}\n.km-provider-body{border-top:1px solid var(--dsw-alias-border-l1);padding:13px 14px 14px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 40%,transparent)}\n.km-adopt{display:flex;flex-direction:column;gap:12px}\n.km-adopt-copy{display:flex;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-adopt-title{color:var(--dsw-alias-label-primary);font-size:12.5px}\n.km-adopt-actions{display:flex;gap:8px;align-items:center}\n.km-meta-line{display:flex;gap:6px;align-items:baseline;flex-wrap:wrap;font-size:11.5px}\n.km-managed-body{display:flex;flex-direction:column;gap:10px}\n.km-managed-toolbar{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap}\n.km-managed-summary{display:flex;flex-direction:column;gap:2px;min-width:0;font-size:11.5px}\n.km-managed-actions{display:flex;gap:6px;align-items:center;flex-shrink:0}\n.km-keys{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-bg-layer-1);overflow:visible}\n.km-key-row{position:relative;display:grid;grid-template-columns:minmax(0,1.35fr) minmax(150px,.8fr) minmax(150px,.9fr) 32px;gap:10px;align-items:center;padding:10px 11px;border-top:1px solid var(--dsw-alias-border-l1)}\n.km-key-row:first-child{border-top:0}\n.km-account-row{position:relative;display:grid;grid-template-columns:minmax(0,1.35fr) minmax(150px,.8fr) minmax(150px,.9fr) 32px;gap:10px;align-items:center;padding:10px 11px 10px 14px;border-top:1px solid var(--dsw-alias-border-l1)}\n.km-account-row:first-child{border-top:0}\n.km-account-row::before{content:'';position:absolute;left:0;top:12px;bottom:12px;width:2px;border-radius:1px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,transparent)}\n.km-account-glyph{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;flex:0 0 22px;border-radius:50%;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 14%,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-state-business-primary)}\n.km-key-identity{min-width:0;display:flex;flex-direction:column;gap:2px}\n.km-key-alias-line{display:flex;align-items:center;gap:6px;min-width:0}\n.km-key-alias{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}\n.km-key-sub{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-secondary);flex-wrap:wrap}\n.km-key-status{display:flex;flex-direction:column;gap:2px;align-items:flex-start}\n.km-key-last{display:flex;flex-direction:column;gap:2px;min-width:0}\n.km-key-time{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}\n.km-key-time.km-warn{color:var(--dsw-alias-state-warn-primary)}\n.km-key-actions{position:relative;display:flex;justify-content:flex-end}\n.km-status{display:inline-flex;align-items:center;gap:5px;padding:2px 7px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-size:11px;white-space:nowrap;color:var(--dsw-alias-label-secondary)}\n.km-status-success{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 26%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}\n.km-status-warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 8%,transparent)}\n.km-status-error{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent)}\n.km-status-idle{color:var(--dsw-alias-state-idle-primary)}\n.km-dot{display:inline-block;width:6px;height:6px;border-radius:50%;flex:0 0 6px;background:var(--dsw-alias-state-idle-primary)}\n.km-dot-success{background:var(--dsw-alias-state-success-primary)}\n.km-dot-warn{background:var(--dsw-alias-state-warn-primary)}\n.km-dot-error{background:var(--dsw-alias-state-error-primary)}\n.km-dot-idle{background:var(--dsw-alias-state-idle-primary)}\n.km-dot-sep{color:var(--dsw-alias-label-secondary)}\n.km-tag{display:inline-flex;align-items:center;padding:1px 6px;border-radius:5px;font-size:10.5px;line-height:1.45;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);white-space:nowrap}\n.km-tag-accent{color:var(--dsw-alias-state-business-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 34%,var(--dsw-alias-border-l1));background:var(--dsw-alias-state-business-tertiary)}\n.km-tag-warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 8%,transparent)}\n.km-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:0 0 auto}\n.km-icon-btn:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary)}\n.km-icon-btn:disabled{opacity:.5;cursor:default}\n.km-menu{position:absolute;top:32px;right:0;z-index:20;min-width:138px;padding:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-overlay);box-shadow:0 7px 22px color-mix(in srgb,var(--dsw-alias-label-primary) 14%,transparent)}\n.km-menu-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;text-align:left;cursor:pointer}\n.km-menu-item:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-menu-item:disabled{opacity:.5;cursor:default}\n.km-menu-danger{color:var(--dsw-alias-state-error-primary)}\n.km-menu-sep{height:1px;margin:4px 2px;background:var(--dsw-alias-border-l1)}\n.km-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:7px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;font-weight:550;line-height:1.2;cursor:pointer;white-space:nowrap;transition:opacity .15s ease,background .15s ease}\n.km-btn:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-btn:disabled{opacity:.5;cursor:default}\n.km-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);border-color:transparent}\n.km-btn-primary:hover{opacity:.9;background:var(--dsw-alias-label-primary)}\n.km-btn-ghost{background:transparent;border-color:transparent;color:var(--dsw-alias-label-secondary)}\n.km-btn-ghost:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-btn-danger{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-bg-base);border-color:transparent}\n.km-btn-danger:hover{opacity:.9;background:var(--dsw-alias-state-error-primary)}\n.km-btn-small{padding:5px 8px;font-size:12px;border-radius:7px}\n.km-input{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;outline:none}\n.km-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}\n.km-input:disabled{opacity:.6}\nselect.km-input{appearance:auto}\n.km-form{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-bg-layer-1)}\n.km-form-lead{display:flex;flex-direction:column;gap:2px}\n.km-form-title{font-weight:650;font-size:12.5px}\n.km-form-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:10px}\n.km-field{display:flex;flex-direction:column;gap:5px;min-width:0}\n.km-field-wide{min-width:0}\n.km-field-label{font-size:11.5px;font-weight:550;color:var(--dsw-alias-label-secondary)}\n.km-field-hint{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.45}\n.km-secret{position:relative;display:flex;align-items:center}\n.km-secret-input{padding-right:38px}\n.km-secret-toggle{position:absolute;right:4px;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-secret-toggle:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.km-form-actions{display:flex;justify-content:flex-end;gap:8px}\n.km-rename{display:flex;align-items:center;gap:4px;min-width:0;flex:1 1 auto;max-width:100%}\n.km-rename-input{flex:1 1 auto;width:auto;min-width:60px;padding:4px 7px;font-size:12.5px}\n.km-inline-error{padding:7px 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,var(--dsw-alias-border-l1));border-radius:7px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);color:var(--dsw-alias-state-error-primary);font-size:12px;word-break:break-word}\n.km-key-error{grid-column:1/-1;margin-top:-2px}\n.km-notice{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-notice-icon{flex:0 0 auto;margin-top:1px}\n.km-notice-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-notice-error{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,transparent)}\n.km-notice-body{display:flex;flex-direction:column;gap:2px;min-width:0}\n.km-notice-title{color:var(--dsw-alias-label-primary);font-weight:600;font-size:12px}\n.km-notice-text{line-height:1.5}\n.km-notice-action{flex:0 0 auto}\n.km-empty-keys{padding:12px;border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-empty{display:flex;align-items:center;gap:8px;padding:14px;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-provider-foot{display:flex;justify-content:space-between;gap:10px;align-items:center;margin-top:10px;padding-top:9px;border-top:1px dashed var(--dsw-alias-border-l1);font-size:11px;min-height:15px}\n.km-settings{padding:14px}\n.km-settings-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}\n.km-settings-title{font-size:14px;font-weight:650;margin:0 0 3px}\n.km-settings-intro{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px}\n.km-settings-icon{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-border-l1)}\n.km-settings-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr);gap:12px;align-items:start}\n.km-field-note{grid-column:1/-1}\n.km-policy-note{padding:9px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}\n.km-policy-title{display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;color:var(--dsw-alias-label-primary);margin-bottom:2px}\n.km-policy-note p{margin:0;font-size:11.5px;line-height:1.5}\n.km-settings-actions{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:12px}\n.km-saved{display:inline-flex;align-items:center;gap:4px;color:var(--dsw-alias-state-success-primary);font-size:11.5px}\n.km-banner{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border-radius:8px;margin:0 0 12px;font-size:12px;line-height:1.5;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}\n.km-banner-error{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,transparent)}\n.km-banner-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 32%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-banner-text{flex:1;min-width:0;word-break:break-word}\n.km-banner-close{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:0;border-radius:5px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}\n.km-banner-close:hover{background:var(--dsw-alias-bg-layer-2)}\n.km-loading{display:flex;align-items:center;gap:8px;padding:20px 0;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-load-failure{display:flex;flex-direction:column;gap:10px;align-items:flex-start;padding:18px;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-empty-title{font-weight:600;color:var(--dsw-alias-label-primary)}\n.km-footer{display:flex;align-items:flex-start;gap:6px;padding-top:4px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.5}\n.km-muted{color:var(--dsw-alias-label-secondary)}\n.km-warn{color:var(--dsw-alias-state-warn-primary)}\n.km-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}\n.km-truncate{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.km-spin{animation:km-spin 1s linear infinite}\n.km-models-note{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin:8px 0 0;padding:7px 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-brand-primary) 24%,var(--dsw-alias-border-l1));border-radius:7px;background:color-mix(in srgb,var(--dsw-alias-brand-primary) 6%,transparent);font-size:11.5px;color:var(--dsw-alias-label-primary)}\n.km-models-note-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 30%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,transparent)}\n.km-models-note-text{font-weight:600}\n.km-models-note-hint{color:var(--dsw-alias-label-secondary);font-weight:400}\n.km-modal-backdrop{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:24px;background:color-mix(in srgb,var(--dsw-alias-label-primary) 26%,transparent)}\n.km-modal{width:min(440px,100%);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-overlay);padding:16px;box-shadow:0 18px 48px color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent)}\n.km-modal-title{font-weight:650;font-size:14px;margin-bottom:7px}\n.km-modal-body{color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-modal-copy{margin:0;line-height:1.55}\n.km-login-flow{display:flex;flex-direction:column;gap:10px}\n.km-login-pending{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:12.5px}\n.km-login-notices{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5;word-break:break-word}\n.km-login-link{color:var(--dsw-alias-state-business-primary);text-decoration:underline;text-underline-offset:2px}\n.km-login-link:hover{color:var(--dsw-alias-label-primary)}\n.km-login-prompt{display:flex;flex-direction:column;gap:10px;padding-top:2px;border-top:1px dashed var(--dsw-alias-border-l1)}\n.km-login-prompt-title{font-weight:600;font-size:12.5px;color:var(--dsw-alias-label-primary)}\n.km-modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n@keyframes km-spin{to{transform:rotate(360deg)}}\n@media(max-width:720px){\n .km-summary{grid-template-columns:repeat(2,minmax(0,1fr))}\n .km-key-row,.km-account-row{grid-template-columns:minmax(0,1fr) 32px;gap:6px}\n .km-key-status,.km-key-last{grid-column:1;padding-left:0}\n .km-key-actions{grid-column:2;grid-row:1}\n .km-settings-grid{grid-template-columns:1fr}\n .km-form-grid{grid-template-columns:1fr}\n .km-provider-side .km-status{display:none}\n .km-embedded-head{flex-direction:column;gap:10px}\n .km-embedded-actions{padding-top:0}\n}\n";
@@ -1,7 +1,7 @@
1
1
  /** Browser-half domain and service contracts for the Keys Manager. */
2
2
  import type * as React from 'react';
3
- import type { AccountAttemptNotice, AccountAttemptPrompt, AccountAttemptView, KeyStatus, KeyView, ManagerSettings, OAuthAccountView, ProviderStatus, ProviderView, StateView } from '../domain';
4
- export type { AccountAttemptNotice, AccountAttemptPrompt, AccountAttemptView, KeyStatus, KeyView, ManagerSettings, OAuthAccountView, ProviderStatus, ProviderView, StateView, };
3
+ import type { AccountAttemptNotice, AccountAttemptPrompt, AccountAttemptView, KeyStatus, KeyTestResult, KeyView, ManagerSettings, OAuthAccountView, ProviderStatus, ProviderView, StateView, TransportAccountStatus, TransportAccountView } from '../domain';
4
+ export type { AccountAttemptNotice, AccountAttemptPrompt, AccountAttemptView, KeyStatus, KeyTestResult, KeyView, ManagerSettings, OAuthAccountView, ProviderStatus, ProviderView, StateView, TransportAccountStatus, TransportAccountView, };
5
5
  /** Locale dictionaries registered under the plugin namespace. */
6
6
  export interface LocaleDicts {
7
7
  zh: Record<string, string>;
@@ -52,6 +52,17 @@ export type AckResult = {
52
52
  ok: false;
53
53
  error: string;
54
54
  };
55
+ /**
56
+ * Result of the per-row connection test. The payload is a `KeyTestResult`,
57
+ * not a `StateView`, so it has its own mapper instead of `call`.
58
+ */
59
+ export type KeyTestOutcome = {
60
+ ok: true;
61
+ value: KeyTestResult;
62
+ } | {
63
+ ok: false;
64
+ error: string;
65
+ };
55
66
  /** Immutable UI snapshot. */
56
67
  export interface ClientSnapshot {
57
68
  status: 'idle' | 'loading' | 'ready' | 'error';
@@ -84,6 +95,20 @@ export interface ClientStore {
84
95
  activateAccount(provider: string, accountId: string): Promise<ActionOutcome>;
85
96
  /** Drop one account row and its stored grant payload. */
86
97
  removeAccount(provider: string, accountId: string): Promise<ActionOutcome>;
98
+ /**
99
+ * Send one minimal request with the selected key or account row. The host
100
+ * never changes health state; the answer only reports the outcome.
101
+ */
102
+ testKey(provider: string, keyId: string): Promise<KeyTestOutcome>;
103
+ /**
104
+ * Start the `llm-omp-transports/<route>` sign-in; resolves with the attempt
105
+ * view, which the login dialog polls through {@link accountAttempt}.
106
+ */
107
+ beginTransportLogin(route: string): Promise<AccountAttemptResult>;
108
+ /** Re-read one transport route's stored grant (activation writes nothing). */
109
+ activateTransportAccount(route: string): Promise<ActionOutcome>;
110
+ /** Delete one transport route's grant record. */
111
+ removeTransportAccount(route: string): Promise<ActionOutcome>;
87
112
  tick(): void;
88
113
  dismissError(): void;
89
114
  }
@@ -1,6 +1,20 @@
1
1
  /** Keys Manager browser components, rendered from the plugin-detail card and
2
2
  * the Models provider-card extension slot. */
3
3
  import React from 'react';
4
+ import type { ProviderView } from './types';
5
+ /**
6
+ * True when this route can offer OAuth sign-in right now.
7
+ *
8
+ * C6-2: the catalog's `nativeAuth.oauth` flag says the provider supports
9
+ * OAuth upstream; the host's `accountSupported` verdict says this Harness
10
+ * build has a flow it can actually begin. The host verdict is authoritative
11
+ * whenever present, so a nativeAuth.oauth route without a pi-ai flow hides
12
+ * Add account instead of opening a sign-in that would fail. Older host
13
+ * snapshots that predate the field keep the catalog-only behaviour.
14
+ */
15
+ export declare function oauthCapable(provider: ProviderView): boolean;
16
+ /** Where the row tag says a route came from, including legacy snapshots. */
17
+ export declare function originOf(provider: ProviderView): 'dsh' | 'plugin' | 'custom';
4
18
  declare function KeysManagerBody(): React.ReactElement;
5
19
  interface KeysManagerCardProps {
6
20
  view?: 'summary' | 'page';