dsh-keys-manager 0.2.2 → 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 +83 -19
- package/lib/client/styles.d.ts +1 -1
- package/lib/client/ui.d.ts +14 -0
- package/lib/client.js +141 -31
- package/lib/domain.d.ts +8 -0
- package/lib/index.d.ts +108 -22
- package/lib/index.js +774 -113
- package/lib/index.js.map +1 -1
- package/locale/en.json +12 -2
- package/locale/zh.json +12 -2
- package/package.json +2 -2
- package/scripts/prepare-release.mjs +318 -128
package/CHANGELOG.md
CHANGED
|
@@ -1,33 +1,97 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to dsh-keys-manager are documented here.
|
|
4
|
-
|
|
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.
|
|
5
28
|
|
|
6
29
|
## [0.2.2] - 2026-10-21
|
|
7
30
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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.
|
|
16
55
|
|
|
17
56
|
## [0.2.1] - 2026-10-20
|
|
18
57
|
|
|
19
|
-
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- The plugin declares `authorization` in its inject list, so loading no longer fails with `cannot get property "authorization" without inject`.
|
|
20
61
|
|
|
21
62
|
## [0.2.0] - 2026-10-20
|
|
22
63
|
|
|
23
|
-
|
|
24
|
-
|
|
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.
|
|
25
76
|
|
|
26
77
|
## [0.1.0] - 2026-10-19
|
|
27
78
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
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/lib/client/styles.d.ts
CHANGED
|
@@ -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";
|
package/lib/client/ui.d.ts
CHANGED
|
@@ -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';
|
package/lib/client.js
CHANGED
|
@@ -44,9 +44,20 @@ var zh = {
|
|
|
44
44
|
title: "\u5BC6\u94A5\u7BA1\u7406",
|
|
45
45
|
subtitle: "\u4E3A\u540C\u4E00\u4E2A Provider \u914D\u7F6E\u591A\u4E2A API Key\u3002\u5355\u4F1A\u8BDD\u56FA\u5B9A\u4F7F\u7528\u4E00\u4E2A Key\uFF0C\u65B0\u4F1A\u8BDD\u8F6E\u6362\uFF1B\u5931\u8D25\u540E\u81EA\u52A8\u51B7\u5374\u5E76\u6062\u590D\u3002",
|
|
46
46
|
search: "\u641C\u7D22 Provider\uFF08\u540D\u79F0\u3001ID \u6216\u73AF\u5883\u53D8\u91CF\uFF09",
|
|
47
|
+
filterAll: "\u5168\u90E8",
|
|
48
|
+
filterBuiltin: "\u5185\u7F6E",
|
|
49
|
+
filterPlugin: "\u63D2\u4EF6",
|
|
50
|
+
filterOauth: "\u652F\u6301 OAuth",
|
|
51
|
+
filterTransport: "\u4F20\u8F93",
|
|
52
|
+
filterGroup: "\u7B5B\u9009",
|
|
53
|
+
// Unattributed requests (I-000120, D8): the compass's fixed copy, shared
|
|
54
|
+
// with the filter plan's dictionary task — one pair, never a second.
|
|
55
|
+
unattributed: "{n} \u65E0\u5F52\u5C5E",
|
|
56
|
+
unattributedHint: "\u8BE5\u8DEF\u7531\u5728\u5F52\u5C5E\u8BFB\u53D6\u5C1A\u672A\u843D\u5730\uFF08\u6216\u5DF2\u5931\u8D25\uFF09\u671F\u95F4\u5E94\u7B54\u7684\u6D41\u5C1D\u8BD5\u6570\uFF1B\u5931\u8D25\u8F6C\u79FB\u91CD\u8BD5\u4F1A\u5404\u8BA1\u4E00\u6B21\uFF0C\u6545\u5355\u4E2A\u8BF7\u6C42\u53EF\u80FD\u88AB\u8BA1\u591A\u6B21\u3002\u8BFB\u53D6\u5B8C\u6210\u5F52\u5C5E\u540E\u6E05\u96F6\u3002",
|
|
47
57
|
managed: "\u5DF2\u63A5\u7BA1",
|
|
48
58
|
available: "\u53EF\u6DFB\u52A0 Providers",
|
|
49
59
|
managedEmpty: "\u8FD8\u6CA1\u6709\u63A5\u7BA1\u7684 Provider\u3002\u4ECE\u4E0B\u65B9\u9009\u62E9\u4E00\u4E2A\u5F00\u59CB\u3002",
|
|
60
|
+
managedFilteredEmpty: "\u5F53\u524D\u7B5B\u9009\u4E0B\u6CA1\u6709\u5DF2\u63A5\u7BA1\u7684 Provider\u3002",
|
|
50
61
|
availableEmpty: "\u6CA1\u6709\u5339\u914D\u7684 Provider\u3002",
|
|
51
62
|
keys: "\u5BC6\u94A5",
|
|
52
63
|
healthy: "\u53EF\u7528",
|
|
@@ -194,7 +205,8 @@ var zh = {
|
|
|
194
205
|
deleteAccountBodyNoKeys: "\u8FD9\u4F1A\u4ECE\u51ED\u8BC1\u5E93\u4E2D\u5220\u9664\u8BE5\u8D26\u6237\u4FDD\u5B58\u7684 OAuth \u6388\u6743\u3002\u8BE5\u8DEF\u7531\u6CA1\u6709 API-key \u6C60\uFF0C\u5220\u9664\u540E\u5C06\u505C\u6B62\u670D\u52A1\uFF0C\u76F4\u5230\u91CD\u65B0\u767B\u5F55\u8D26\u6237\u3002",
|
|
195
206
|
// Transport accounts (llm-omp-transports/<route>): managed beside the
|
|
196
207
|
// pi-ai pool, so the copy never promises a key-pool fallback.
|
|
197
|
-
|
|
208
|
+
transportEmpty: "\u8FD8\u6CA1\u6709\u4F20\u8F93\u8D26\u6237\u3002",
|
|
209
|
+
transportProviderHint: "\u8BE5\u8DEF\u7531\u7531\u4F20\u8F93\u63D2\u4EF6\u63D0\u4F9B\uFF0C\u51ED\u8BC1\u5C31\u5728\u672C\u5361\u7247\u4E2D\u7BA1\u7406\u3002",
|
|
198
210
|
transportTag: "\u4F20\u8F93",
|
|
199
211
|
transportSignIn: "\u767B\u5F55",
|
|
200
212
|
transportExpires: "\u6709\u6548\u671F\u81F3 {time}",
|
|
@@ -210,9 +222,18 @@ var en = {
|
|
|
210
222
|
title: "Keys Manager",
|
|
211
223
|
subtitle: "Give one provider several API keys. A session keeps one key; new sessions rotate; failures cool down and recover automatically.",
|
|
212
224
|
search: "Search providers by name, id, or environment variable",
|
|
225
|
+
filterAll: "All",
|
|
226
|
+
filterBuiltin: "Built-in",
|
|
227
|
+
filterPlugin: "Plugin",
|
|
228
|
+
filterOauth: "OAuth-capable",
|
|
229
|
+
filterTransport: "Transport",
|
|
230
|
+
filterGroup: "Filters",
|
|
231
|
+
unattributed: "{n} unattributed",
|
|
232
|
+
unattributedHint: "Stream attempts this route served with no account owner because its ownership read had not landed (or had failed). A failover retry is a new attempt, so one request can be counted more than once. The count clears when a read attributes the route.",
|
|
213
233
|
managed: "Managed",
|
|
214
234
|
available: "Available providers",
|
|
215
235
|
managedEmpty: "No managed providers yet. Adopt one below to start.",
|
|
236
|
+
managedFilteredEmpty: "No managed providers match the current filter.",
|
|
216
237
|
availableEmpty: "No matching providers.",
|
|
217
238
|
keys: "keys",
|
|
218
239
|
healthy: "healthy",
|
|
@@ -359,7 +380,8 @@ var en = {
|
|
|
359
380
|
deleteAccountBody: "This removes the account\u2019s stored OAuth grant from the credential store. If it is the active account, the provider route stops serving it and falls back to its API-key pool; the provider stays managed.",
|
|
360
381
|
deleteAccountBodyNoKeys: "This removes the account\u2019s stored OAuth grant from the credential store. This route has no API-key pool, so it stops serving requests until an account is signed in again.",
|
|
361
382
|
// Transport accounts — same values as locale/en.json.
|
|
362
|
-
|
|
383
|
+
transportEmpty: "No transport accounts yet.",
|
|
384
|
+
transportProviderHint: "Served by a transport plugin: its credential is managed inside this card.",
|
|
363
385
|
transportTag: "Transport",
|
|
364
386
|
transportSignIn: "Sign in",
|
|
365
387
|
transportExpires: "Expires {time}",
|
|
@@ -386,6 +408,13 @@ var CSS = `
|
|
|
386
408
|
.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)}
|
|
387
409
|
.km-stat strong{font-size:19px;line-height:1.15;font-weight:650;font-variant-numeric:tabular-nums}
|
|
388
410
|
.km-stat span{color:var(--dsw-alias-label-secondary);font-size:12px}
|
|
411
|
+
.km-filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}
|
|
412
|
+
.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}
|
|
413
|
+
.km-chip:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}
|
|
414
|
+
.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)}
|
|
415
|
+
.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)}
|
|
416
|
+
.km-chip-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
417
|
+
.km-chip-on .km-chip-count{color:inherit}
|
|
389
418
|
.km-search{position:relative;display:flex;align-items:center;margin:0 0 20px}
|
|
390
419
|
.km-search-icon{position:absolute;left:12px;color:var(--dsw-alias-label-secondary);pointer-events:none}
|
|
391
420
|
.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}
|
|
@@ -873,6 +902,37 @@ function providerInitial(provider) {
|
|
|
873
902
|
function oauthCapable(provider) {
|
|
874
903
|
return provider.nativeAuth?.oauth === true && provider.accountSupported !== false;
|
|
875
904
|
}
|
|
905
|
+
function originOf(provider) {
|
|
906
|
+
return provider.origin ?? (provider.extra ? "plugin" : "dsh");
|
|
907
|
+
}
|
|
908
|
+
var FILTER_CHIPS = [
|
|
909
|
+
{ value: "all", label: "filterAll" },
|
|
910
|
+
{ value: "builtin", label: "filterBuiltin" },
|
|
911
|
+
{ value: "plugin", label: "filterPlugin" },
|
|
912
|
+
{ value: "oauth", label: "filterOauth" },
|
|
913
|
+
{ value: "transport", label: "filterTransport" }
|
|
914
|
+
];
|
|
915
|
+
function matchesFilter(provider, filter) {
|
|
916
|
+
switch (filter) {
|
|
917
|
+
case "transport":
|
|
918
|
+
return provider.transport === true;
|
|
919
|
+
case "builtin":
|
|
920
|
+
return provider.transport !== true && originOf(provider) === "dsh";
|
|
921
|
+
case "plugin":
|
|
922
|
+
return provider.transport !== true && originOf(provider) === "plugin";
|
|
923
|
+
case "oauth":
|
|
924
|
+
return provider.transport !== true && oauthCapable(provider);
|
|
925
|
+
default:
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
function orphanTransportAccounts(providers, transportAccounts) {
|
|
930
|
+
return transportAccounts.filter((account) => !providers.some((provider) => provider.transport === true && provider.provider === account.id));
|
|
931
|
+
}
|
|
932
|
+
function managedEntryCount(providers, transportAccounts) {
|
|
933
|
+
const cards = providers.filter((provider) => provider.managed || provider.transport === true).length;
|
|
934
|
+
return cards + orphanTransportAccounts(providers, transportAccounts).length;
|
|
935
|
+
}
|
|
876
936
|
function accountIdentityOf(key, provider) {
|
|
877
937
|
const oauth = key.oauth;
|
|
878
938
|
if (oauth !== null && oauth !== void 0) {
|
|
@@ -1648,7 +1708,7 @@ function TransportAccountRow({ account }) {
|
|
|
1648
1708
|
}) : null
|
|
1649
1709
|
);
|
|
1650
1710
|
}
|
|
1651
|
-
function ProviderCard({ provider, expanded, onToggle, now }) {
|
|
1711
|
+
function ProviderCard({ provider, expanded, onToggle, now, transportAccount }) {
|
|
1652
1712
|
const runtime = useRuntime();
|
|
1653
1713
|
const t = useT();
|
|
1654
1714
|
const [adding, setAdding] = import_react2.default.useState(false);
|
|
@@ -1663,11 +1723,15 @@ function ProviderCard({ provider, expanded, onToggle, now }) {
|
|
|
1663
1723
|
const keyCount = provider.keyCount;
|
|
1664
1724
|
const warning = provider.warning;
|
|
1665
1725
|
const isExtra = provider.extra;
|
|
1666
|
-
const
|
|
1667
|
-
const
|
|
1726
|
+
const transport = provider.transport === true;
|
|
1727
|
+
const origin = originOf(provider);
|
|
1728
|
+
const originLabel = transport || origin === "plugin" ? t("pluginBuiltin") : origin === "dsh" ? t("dshBuiltin") : t("customProvider");
|
|
1668
1729
|
const providerMeta = [
|
|
1669
1730
|
provider.provider,
|
|
1670
|
-
|
|
1731
|
+
// `active`/`configured` are read off the route's profile or the host's
|
|
1732
|
+
// provider list, neither of which describes a transport route: its state
|
|
1733
|
+
// is the account row it carries (signed out when the host lists none).
|
|
1734
|
+
transport ? t(transportStatusKey(transportAccount?.status ?? "signed-out")) : provider.active ? t("active") : provider.configured ? t("configured") : t("notConfigured"),
|
|
1671
1735
|
provider.api ?? null
|
|
1672
1736
|
].filter((value) => Boolean(value));
|
|
1673
1737
|
async function adopt() {
|
|
@@ -1737,8 +1801,9 @@ function ProviderCard({ provider, expanded, onToggle, now }) {
|
|
|
1737
1801
|
{ className: "km-provider-title-line" },
|
|
1738
1802
|
h("span", { className: "km-provider-name" }, provider.displayName || provider.provider),
|
|
1739
1803
|
h("span", { className: "km-tag" }, originLabel),
|
|
1804
|
+
transport ? h("span", { className: "km-tag km-tag-accent" }, t("transportTag")) : null,
|
|
1740
1805
|
managed ? h("span", { className: "km-tag km-tag-accent" }, t("managedBadge")) : null,
|
|
1741
|
-
!managed && !provider.supported && !accountAdoptable ? h("span", { className: "km-tag km-tag-warn" }, t("unsupported")) : null
|
|
1806
|
+
!transport && !managed && !provider.supported && !accountAdoptable ? h("span", { className: "km-tag km-tag-warn" }, t("unsupported")) : null
|
|
1742
1807
|
),
|
|
1743
1808
|
h(
|
|
1744
1809
|
"span",
|
|
@@ -1752,7 +1817,14 @@ function ProviderCard({ provider, expanded, onToggle, now }) {
|
|
|
1752
1817
|
h(
|
|
1753
1818
|
"span",
|
|
1754
1819
|
{ className: "km-provider-side" },
|
|
1755
|
-
|
|
1820
|
+
// I-000120 / D8: requests this route served without an attributed
|
|
1821
|
+
// account. Rendered only above zero, and the visible label carries the
|
|
1822
|
+
// count so it is the accessible name; the hint stays a description.
|
|
1823
|
+
(provider.unattributedRequests ?? 0) > 0 ? h("span", {
|
|
1824
|
+
className: "km-tag km-tag-warn km-unattributed",
|
|
1825
|
+
title: t("unattributedHint")
|
|
1826
|
+
}, t("unattributed", { n: provider.unattributedRequests ?? 0 })) : null,
|
|
1827
|
+
transport ? null : managed ? h("span", { className: "km-count" }, countText(t, keyCount, "providerKeyOne", "providerKeys")) : h("span", { className: "km-count km-muted" }, adoptable ? t("unmanaged") : t("unsupported")),
|
|
1756
1828
|
managed ? h(StatusPill, { status: provider.status, t }) : null,
|
|
1757
1829
|
h(Icon, { name: "chevron", size: 16, className: cx("km-chevron", expanded && "km-chevron-open") })
|
|
1758
1830
|
)
|
|
@@ -1762,7 +1834,9 @@ function ProviderCard({ provider, expanded, onToggle, now }) {
|
|
|
1762
1834
|
{ className: "km-provider-body" },
|
|
1763
1835
|
error !== null ? h("div", { className: "km-inline-error", role: "alert" }, error) : null,
|
|
1764
1836
|
warning !== void 0 || broken ? h(Notice, { tone: "warn", title: t("warning") }, broken ? t("bindingBroken") : warning) : null,
|
|
1765
|
-
|
|
1837
|
+
// A transport route has no adopt step and no key pool: its whole
|
|
1838
|
+
// credential surface is the one account row its own plugin serves.
|
|
1839
|
+
transport ? transportAccount !== void 0 ? h(TransportAccountRow, { account: transportAccount }) : h("div", { className: "km-muted" }, t("transportProviderHint")) : !managed ? h(
|
|
1766
1840
|
"div",
|
|
1767
1841
|
{ className: "km-adopt" },
|
|
1768
1842
|
h(
|
|
@@ -2178,6 +2252,7 @@ function KeysManagerBody() {
|
|
|
2178
2252
|
const state = snapshot.state;
|
|
2179
2253
|
const now = snapshot.now;
|
|
2180
2254
|
const [query, setQuery] = import_react2.default.useState("");
|
|
2255
|
+
const [filter, setFilter] = import_react2.default.useState("all");
|
|
2181
2256
|
const [showAllAvailable, setShowAllAvailable] = import_react2.default.useState(false);
|
|
2182
2257
|
const [expanded, setExpanded] = import_react2.default.useState(() => /* @__PURE__ */ new Set());
|
|
2183
2258
|
const loadedRef = import_react2.default.useRef(false);
|
|
@@ -2201,9 +2276,23 @@ function KeysManagerBody() {
|
|
|
2201
2276
|
].filter((value) => Boolean(value)).join(" ").toLowerCase();
|
|
2202
2277
|
return haystack.includes(normalizedQuery);
|
|
2203
2278
|
};
|
|
2204
|
-
const managed = providers.filter((provider) => provider.managed && matches(provider));
|
|
2205
|
-
const available = providers.filter((provider) => !provider.managed && matches(provider));
|
|
2279
|
+
const managed = providers.filter((provider) => (provider.managed || provider.transport === true) && matchesFilter(provider, filter) && matches(provider));
|
|
2280
|
+
const available = providers.filter((provider) => !provider.managed && provider.transport !== true && matchesFilter(provider, filter) && matches(provider));
|
|
2206
2281
|
const visibleAvailable = showAllAvailable || normalizedQuery.length > 0 ? available : available.slice(0, 12);
|
|
2282
|
+
const matchesAccount = (account) => {
|
|
2283
|
+
if (normalizedQuery.length === 0) return true;
|
|
2284
|
+
const haystack = [account.id, account.scope, account.displayName].join(" ").toLowerCase();
|
|
2285
|
+
return haystack.includes(normalizedQuery);
|
|
2286
|
+
};
|
|
2287
|
+
const orphanAccounts = orphanTransportAccounts(providers, transportAccounts).filter(matchesAccount);
|
|
2288
|
+
const visibleOrphans = filter === "all" || filter === "transport" ? orphanAccounts : [];
|
|
2289
|
+
const filterCount = (value) => {
|
|
2290
|
+
const searchable = providers.filter(matches);
|
|
2291
|
+
if (value === "all") return searchable.length + orphanAccounts.length;
|
|
2292
|
+
if (value === "transport") return searchable.filter((provider) => provider.transport === true).length + orphanAccounts.length;
|
|
2293
|
+
return searchable.filter((provider) => matchesFilter(provider, value)).length;
|
|
2294
|
+
};
|
|
2295
|
+
const managedHiddenByChip = filter !== "all" && providers.some((provider) => provider.managed || provider.transport === true);
|
|
2207
2296
|
function toggle(provider) {
|
|
2208
2297
|
setExpanded((current) => {
|
|
2209
2298
|
const next = new Set(current);
|
|
@@ -2242,7 +2331,7 @@ function KeysManagerBody() {
|
|
|
2242
2331
|
h(
|
|
2243
2332
|
"div",
|
|
2244
2333
|
{ className: "km-summary" },
|
|
2245
|
-
h("div", { className: "km-stat" }, h("strong", null, loading ? "\u2014" :
|
|
2334
|
+
h("div", { className: "km-stat" }, h("strong", null, loading ? "\u2014" : managedEntryCount(providers, transportAccounts)), h("span", null, t("managed"))),
|
|
2246
2335
|
h("div", { className: "km-stat" }, h("strong", null, loading ? "\u2014" : state?.keyCount ?? 0), h("span", null, t("keys"))),
|
|
2247
2336
|
h("div", { className: "km-stat" }, h("strong", null, loading ? "\u2014" : state?.healthyKeyCount ?? 0), h("span", null, t("healthy"))),
|
|
2248
2337
|
h("div", { className: "km-stat" }, h("strong", null, loading ? "\u2014" : state?.coolingKeyCount ?? 0), h("span", null, t("cooling")))
|
|
@@ -2259,6 +2348,26 @@ function KeysManagerBody() {
|
|
|
2259
2348
|
}, h(Icon, { name: "refresh", size: 14 }), t("refresh"))
|
|
2260
2349
|
)
|
|
2261
2350
|
),
|
|
2351
|
+
// Quick filter: single select, view state only, composed with the text
|
|
2352
|
+
// search below. Counts are derived per predicate from the snapshot.
|
|
2353
|
+
!loading && !failed ? h("div", {
|
|
2354
|
+
className: "km-filters",
|
|
2355
|
+
role: "group",
|
|
2356
|
+
"aria-label": t("filterGroup")
|
|
2357
|
+
}, FILTER_CHIPS.map(
|
|
2358
|
+
(chip) => h(
|
|
2359
|
+
"button",
|
|
2360
|
+
{
|
|
2361
|
+
key: chip.value,
|
|
2362
|
+
type: "button",
|
|
2363
|
+
className: cx("km-chip", `km-chip-${chip.value}`, filter === chip.value && "km-chip-on"),
|
|
2364
|
+
"aria-pressed": filter === chip.value,
|
|
2365
|
+
onClick: () => setFilter(chip.value)
|
|
2366
|
+
},
|
|
2367
|
+
h("span", { className: "km-chip-label" }, t(chip.label)),
|
|
2368
|
+
h("span", { className: "km-chip-count" }, filterCount(chip.value))
|
|
2369
|
+
)
|
|
2370
|
+
)) : null,
|
|
2262
2371
|
h(
|
|
2263
2372
|
"div",
|
|
2264
2373
|
{ className: "km-search" },
|
|
@@ -2288,15 +2397,29 @@ function KeysManagerBody() {
|
|
|
2288
2397
|
"div",
|
|
2289
2398
|
{ className: "km-section-head" },
|
|
2290
2399
|
h("h2", { className: "km-section-title" }, t("managed")),
|
|
2291
|
-
|
|
2400
|
+
// What is below it, like the available section: the tile above keeps
|
|
2401
|
+
// the global total.
|
|
2402
|
+
h("span", { className: "km-section-count" }, managed.length + visibleOrphans.length)
|
|
2292
2403
|
),
|
|
2293
|
-
|
|
2404
|
+
// The placeholder only when the section renders neither a card nor a row
|
|
2405
|
+
// the active chip selects.
|
|
2406
|
+
managed.length === 0 && visibleOrphans.length === 0 ? h("div", { className: "km-empty" }, h(Icon, { name: "key", size: 20 }), h(
|
|
2407
|
+
"span",
|
|
2408
|
+
null,
|
|
2409
|
+
normalizedQuery.length > 0 ? t("noSearch") : filter === "transport" ? t("transportEmpty") : managedHiddenByChip ? t("managedFilteredEmpty") : t("managedEmpty")
|
|
2410
|
+
)) : null,
|
|
2411
|
+
managed.length > 0 ? h("div", { className: "km-provider-list" }, managed.map((provider) => h(ProviderCard, {
|
|
2294
2412
|
key: provider.provider,
|
|
2295
2413
|
provider,
|
|
2296
2414
|
now,
|
|
2415
|
+
transportAccount: provider.transport === true ? transportAccounts.find((account) => account.id === provider.provider) : void 0,
|
|
2297
2416
|
expanded: expanded.has(provider.provider),
|
|
2298
2417
|
onToggle: () => toggle(provider.provider)
|
|
2299
|
-
})))
|
|
2418
|
+
}))) : null,
|
|
2419
|
+
visibleOrphans.length > 0 ? h("div", { className: "km-provider-list" }, visibleOrphans.map((account) => h(TransportAccountRow, {
|
|
2420
|
+
key: account.scope,
|
|
2421
|
+
account
|
|
2422
|
+
}))) : null
|
|
2300
2423
|
) : null,
|
|
2301
2424
|
!loading && !failed ? h(
|
|
2302
2425
|
"section",
|
|
@@ -2324,20 +2447,6 @@ function KeysManagerBody() {
|
|
|
2324
2447
|
h(Button, { variant: "ghost", size: "small", onClick: () => setShowAllAvailable(false) }, t("showLess"))
|
|
2325
2448
|
) : null
|
|
2326
2449
|
) : null,
|
|
2327
|
-
!loading && !failed && transportAccounts.length > 0 ? h(
|
|
2328
|
-
"section",
|
|
2329
|
-
{ className: "km-section" },
|
|
2330
|
-
h(
|
|
2331
|
-
"div",
|
|
2332
|
-
{ className: "km-section-head" },
|
|
2333
|
-
h("h2", { className: "km-section-title" }, t("transportAccounts")),
|
|
2334
|
-
h("span", { className: "km-section-count" }, transportAccounts.length)
|
|
2335
|
-
),
|
|
2336
|
-
h("div", { className: "km-provider-list" }, transportAccounts.map((account) => h(TransportAccountRow, {
|
|
2337
|
-
key: account.scope,
|
|
2338
|
-
account
|
|
2339
|
-
})))
|
|
2340
|
-
) : null,
|
|
2341
2450
|
!loading && !failed ? h("section", { className: "km-section" }, h(SettingsCard)) : null,
|
|
2342
2451
|
h(
|
|
2343
2452
|
"footer",
|
|
@@ -2353,8 +2462,9 @@ function KeysManagerCard({ view }) {
|
|
|
2353
2462
|
if (view === "summary") {
|
|
2354
2463
|
const state = snapshot.state;
|
|
2355
2464
|
if (state === null) return h("span", { className: "km-summary-line" }, snapshot.status === "error" ? t("loadError") : t("loading"));
|
|
2356
|
-
|
|
2357
|
-
return h("span", { className: "km-summary-line" }, t("
|
|
2465
|
+
const managed = managedEntryCount(state.providers, state.transportAccounts ?? []);
|
|
2466
|
+
if (managed === 0) return h("span", { className: "km-summary-line" }, t("summaryEmpty"));
|
|
2467
|
+
return h("span", { className: "km-summary-line" }, t("summaryText", { providers: managed, keys: state.keyCount }));
|
|
2358
2468
|
}
|
|
2359
2469
|
return h(KeysManagerBody);
|
|
2360
2470
|
}
|
package/lib/domain.d.ts
CHANGED
|
@@ -170,6 +170,8 @@ export interface ProviderView {
|
|
|
170
170
|
* is account-adoptable while carrying no key-pool credential.
|
|
171
171
|
*/
|
|
172
172
|
accountSupported?: boolean;
|
|
173
|
+
/** True when another plugin serves this route on its own credential (not the pi-ai key pool). */
|
|
174
|
+
transport?: boolean;
|
|
173
175
|
reason?: string;
|
|
174
176
|
settingsNs: string;
|
|
175
177
|
settingsPath: string[];
|
|
@@ -192,6 +194,12 @@ export interface ProviderView {
|
|
|
192
194
|
status: ProviderStatus;
|
|
193
195
|
keys: KeyView[];
|
|
194
196
|
warning?: string;
|
|
197
|
+
/**
|
|
198
|
+
* Requests this route served before its ownership read decided which
|
|
199
|
+
* account serves it (`I-000120`, D8). Process-local and additive: an older
|
|
200
|
+
* snapshot without the field still renders, and zero keeps it absent.
|
|
201
|
+
*/
|
|
202
|
+
unattributedRequests?: number;
|
|
195
203
|
}
|
|
196
204
|
/** Health state a transport account row renders. */
|
|
197
205
|
export type TransportAccountStatus = 'active' | 'expired' | 'needs-reauth' | 'signed-out';
|