@edge-sky/dsh-oauth-adapter 0.1.2-dev.2 → 0.1.3-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +27 -60
- package/README.zh.md +27 -62
- package/lib/client.js +242 -162
- package/lib/index.js +1 -1
- package/lib/model-service.d.ts +1 -1
- package/lib/model-service.js +11 -6
- package/lib/model-types.d.ts +2 -0
- package/lib/protocol.js +2 -2
- package/lib/types/client/locales.d.ts +4 -3
- package/lib/types/client/store.d.ts +1 -1
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/model-types.d.ts +2 -0
- package/package.json +1 -1
package/README.i18n.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# Bilingual-pair consistency record: the git blob hash of each side as of the
|
|
2
2
|
# last confirmed-consistent state. Both languages carry equal authority.
|
|
3
|
-
README.md:
|
|
4
|
-
README.zh.md:
|
|
3
|
+
README.md: 745b055c42ba308b7e9f33819f6c084bad98dda4
|
|
4
|
+
README.zh.md: 6bcdf2437386378d19e0045d8c6397ba65637374
|
package/README.md
CHANGED
|
@@ -2,84 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
OAuth
|
|
5
|
+
An OAuth account page for the DSH Web profile. It supports:
|
|
6
|
+
|
|
7
|
+
| Provider | pi-ai sign-in path |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| OpenAI Codex | Browser callback or device code |
|
|
10
|
+
| GitHub Copilot | GitHub or GitHub Enterprise device code |
|
|
11
|
+
| Anthropic | Browser callback with manual code/redirect fallback |
|
|
12
|
+
| Kimi For Coding | Device code |
|
|
13
|
+
| OpenRouter | Browser PKCE callback |
|
|
14
|
+
| xAI | Device code |
|
|
15
|
+
|
|
16
|
+
The adapter has only been tested with DSH `0.1.1-rc.2`; compatibility with future versions is need to test.
|
|
6
17
|
|
|
7
18
|
## Install
|
|
8
19
|
|
|
9
20
|
```sh
|
|
10
21
|
dsh plugin --profile web add @edge-sky/dsh-oauth-adapter
|
|
11
|
-
dsh web
|
|
12
22
|
```
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Use the **OAuth models** area of **Settings → Models** to synchronize an existing connection, configure unmatched models, and add, edit or delete manual entries. This area occupies the public `settings.models.footer` slot. No additional model settings page is installed.
|
|
17
|
-
|
|
18
|
-
| Provider | Model protocol |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| GitHub Copilot | Select Chat Completions, Responses or Anthropic Messages per model |
|
|
21
|
-
| OpenRouter | Select Chat Completions or Anthropic Messages per model |
|
|
22
|
-
| OpenAI Codex | Codex Responses |
|
|
23
|
-
| xAI | OpenAI Responses |
|
|
24
|
-
| Anthropic, Kimi Coding | Anthropic Messages |
|
|
25
|
-
|
|
26
|
-
Model ID is required; a blank display name uses the ID. Fixed-protocol providers have no protocol selector and reject protocol overrides on the server. An unknown model receives the provider's configured default capacities, text input and no inferred reasoning capability. Saving permits an attempted request; it does not prove the model exists, that the account can call it, or that all capabilities are known.
|
|
27
|
-
|
|
28
|
-
## Synchronization
|
|
29
|
-
|
|
30
|
-
Discovery uses each provider's authenticated endpoint, including Copilot's OAuth-resolved API host, Codex's account header, Anthropic cursor pagination and OpenRouter's account-filtered offset pagination. OpenRouter's public catalog is not used as a substitute. An unsupported endpoint or insufficient OAuth scope is reported and manual configuration remains available. Endpoint details and references are in [migration and operation](docs/oauth-models.md).
|
|
24
|
+
or
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
```sh
|
|
27
|
+
npx @deepseek-ai/dsh plugin --profile web add @edge-sky/dsh-oauth-adapter
|
|
28
|
+
```
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
Start DSH:
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
```sh
|
|
33
|
+
npx @deepseek-ai/dsh web
|
|
34
|
+
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
<img width="2940" height="1766" alt="image" src="https://github.com/user-attachments/assets/cbd98703-ee7a-491b-aa2f-02e42b99f6b9" />
|
|
39
37
|
|
|
40
|
-
The optional Host plugin configuration accepts:
|
|
41
38
|
|
|
42
|
-
| Field | Default | Meaning |
|
|
43
|
-
| --- | --- | --- |
|
|
44
|
-
| `debug` | `false` | Transport lifecycle diagnostics |
|
|
45
|
-
| `modelSyncTimeoutMs` | `30000` | Discovery and route-release timeout |
|
|
46
|
-
| `modelSyncMaxResponseBytes` | `4194304` | Maximum bytes per remote page |
|
|
47
|
-
| `modelSyncMaxPages` | `100` | Maximum pages per synchronization |
|
|
48
|
-
| `codexClientVersion` | `0.149.0` | Version sent to the Codex discovery endpoint |
|
|
49
39
|
|
|
50
|
-
|
|
40
|
+
<img width="2270" height="1524" alt="image" src="https://github.com/user-attachments/assets/e0cc6591-73a4-4e87-9ef6-68950126b290" />
|
|
51
41
|
|
|
52
|
-
```sh
|
|
53
|
-
pnpm install
|
|
54
|
-
pnpm run typecheck
|
|
55
|
-
pnpm test
|
|
56
|
-
```
|
|
57
42
|
|
|
58
|
-
|
|
43
|
+
## How it works
|
|
59
44
|
|
|
60
|
-
|
|
45
|
+
DSH integrates pi-ai through the `@deepseek-ai/dsh-llm-pi-ai` adapter, but does not provide an OAuth entry point. `@edge-sky/dsh-oauth-adapter` exposes pi-ai's existing provider login flows through the DSH Authorization Service. Browser links, device codes, text and secret input, method selection, cancellation, and withdrawn prompts all use the same provider-neutral transport.
|
|
61
46
|
|
|
62
|
-
|
|
47
|
+
Because DSH does not currently mount the `ctx.authorization` service, this plugin also mounts it manually.
|
|
63
48
|
|
|
64
|
-
|
|
49
|
+
The plugin itself does not manage OAuth credentials or maintain login state. Maybe DSH support this sign-in method in the future; but for now, this plugin lets you enjoy it.
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
pnpm install --frozen-lockfile
|
|
68
|
-
artifact_dir=$(mktemp -d)
|
|
69
|
-
npm pack --pack-destination "$artifact_dir"
|
|
70
|
-
export DSH_HOME=$(mktemp -d)
|
|
71
|
-
mkdir -p "$DSH_HOME/profiles/web"
|
|
72
|
-
cat > "$DSH_HOME/profiles/web/pnpm-workspace.yaml" <<'YAML'
|
|
73
|
-
packages:
|
|
74
|
-
- .
|
|
75
|
-
nodeLinker: hoisted
|
|
76
|
-
autoInstallPeers: false
|
|
77
|
-
allowBuilds:
|
|
78
|
-
'@google/genai': false
|
|
79
|
-
protobufjs: false
|
|
80
|
-
YAML
|
|
81
|
-
npx @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add "$artifact_dir"/*.tgz
|
|
82
|
-
npx @deepseek-ai/dsh@0.1.2-rc.1 web --no-open
|
|
83
|
-
```
|
|
51
|
+
If this plugins is has helped you, please give a star for me, or create issues if you have any suggestion~
|
|
84
52
|
|
|
85
|
-
After stopping the server, run `unset DSH_HOME` to restore the default directory. The test profile explicitly skips installation scripts for these two dependencies; the plugin repository's policy does not propagate to consumers. New users still need to configure this policy in their own profiles. Successful startup verifies plugin loading; real-account login, model discovery, and inference need separate verification.
|
package/README.zh.md
CHANGED
|
@@ -2,84 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
为 DSH Web profile
|
|
5
|
+
为 DSH Web profile 提供独立的 OAuth 账户页面。当前支持:
|
|
6
|
+
|
|
7
|
+
| 提供商 | pi-ai 登录路径 |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| OpenAI Codex | 浏览器回调或设备码 |
|
|
10
|
+
| GitHub Copilot | GitHub 或 GitHub Enterprise 设备码 |
|
|
11
|
+
| Anthropic | 浏览器回调,并支持手工粘贴授权码/重定向地址 |
|
|
12
|
+
| Kimi For Coding | 设备码 |
|
|
13
|
+
| OpenRouter | 浏览器 PKCE 回调 |
|
|
14
|
+
| xAI | 设备码 |
|
|
15
|
+
|
|
16
|
+
当前仅对 DSH`0.1.1-rc.2`进行了测试,其他版本的兼容性需要自行测试
|
|
6
17
|
|
|
7
18
|
## 安装
|
|
8
19
|
|
|
9
20
|
```sh
|
|
10
21
|
dsh plugin --profile web add @edge-sky/dsh-oauth-adapter
|
|
11
|
-
dsh web
|
|
12
22
|
```
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
在 **设置 → 模型** 的 **OAuth 模型** 区域同步已连接账号、补充待配置模型,以及添加、编辑或删除手动模型。该区域使用公开的 `settings.models.footer` 插槽,不增加独立模型设置页面。
|
|
17
|
-
|
|
18
|
-
| 供应商 | 模型协议 |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| GitHub Copilot | 每个模型选择 Chat Completions、Responses 或 Anthropic Messages |
|
|
21
|
-
| OpenRouter | 每个模型选择 Chat Completions 或 Anthropic Messages |
|
|
22
|
-
| OpenAI Codex | Codex Responses |
|
|
23
|
-
| xAI | OpenAI Responses |
|
|
24
|
-
| Anthropic、Kimi Coding | Anthropic Messages |
|
|
25
|
-
|
|
26
|
-
模型 ID 必填,显示名称留空时使用 ID。固定协议供应商不显示协议选择器,后端也拒绝协议覆盖。未知模型采用供应商配置的默认容量、文本输入,不根据名称推断思考能力。保存后可尝试发送请求,但不代表已验证模型存在、账号可调用或全部模型能力。
|
|
27
|
-
|
|
28
|
-
## 同步行为
|
|
29
|
-
|
|
30
|
-
发现使用各供应商的认证端点,包括 Copilot 经 OAuth 解析的 API 主机、Codex 账号请求头、Anthropic 游标分页及 OpenRouter 账号过滤列表的偏移分页。不会使用 OpenRouter 公开总目录代替账号列表。端点不支持或 OAuth 权限不足时明确报错,仍可手动配置。端点和依据见[迁移与运行说明](docs/oauth-models.zh.md)。
|
|
31
|
-
|
|
32
|
-
只有同一供应商内精确匹配的 ID 才继承 pi-ai 的完整模型描述。不通过显示名称或未经证实的别名推断调用协议。未知 ID 保持为**待配置**。成功同步使用完整远端结果替换自动项,包括真正的空列表;手动项始终保留,同 ID 合并,用户明确填写的字段优先。失败保留最近成功结果。首次成功发现前,保留原配置或内置列表并标明未经验证。重新连接至不同或无法识别的账号时,先清除旧账号的自动结果,再进行发现。
|
|
24
|
+
或
|
|
33
25
|
|
|
34
|
-
|
|
26
|
+
```sh
|
|
27
|
+
npx @deepseek-ai/dsh plugin --profile web add @edge-sky/dsh-oauth-adapter
|
|
28
|
+
```
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
启动 DSH:
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
```sh
|
|
33
|
+
npx @deepseek-ai/dsh web
|
|
34
|
+
```
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
<img width="2940" height="1766" alt="image" src="https://github.com/user-attachments/assets/7f1dc846-9cea-468c-8a1e-27f4fa34a4fe" />
|
|
41
37
|
|
|
42
|
-
| 字段 | 默认值 | 含义 |
|
|
43
|
-
| --- | --- | --- |
|
|
44
|
-
| `debug` | `false` | 连接生命周期诊断 |
|
|
45
|
-
| `modelSyncTimeoutMs` | `30000` | 模型发现及旧路由释放的超时 |
|
|
46
|
-
| `modelSyncMaxResponseBytes` | `4194304` | 每页远端响应的最大字节数 |
|
|
47
|
-
| `modelSyncMaxPages` | `100` | 一次同步的最大页数 |
|
|
48
|
-
| `codexClientVersion` | `0.149.0` | Codex 发现端点所接收的客户端版本 |
|
|
49
38
|
|
|
50
|
-
|
|
39
|
+
<img width="2270" height="1524" alt="image" src="https://github.com/user-attachments/assets/8007002a-a036-4cf4-b015-d4e50eff25d9" />
|
|
51
40
|
|
|
52
|
-
```sh
|
|
53
|
-
pnpm install
|
|
54
|
-
pnpm run typecheck
|
|
55
|
-
pnpm test
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
测试覆盖供应商响应、匹配、失败保留、revision 冲突、迁移恢复、取消、经过网络拦截的真实 rc 适配器请求,以及 DOM 环境中加载已发布 rc 模型页产物的可重放快照。WebSocket 测试需要绑定回环端口。测试凭据全部为合成数据,这些验证不代表任何供应商的真实账号发现或推理调用已经通过。无需修改 DSH 源码。
|
|
59
41
|
|
|
60
|
-
##
|
|
42
|
+
## 工作原理
|
|
61
43
|
|
|
62
|
-
|
|
44
|
+
DSH 通过 `@deepseek-ai/dsh-llm-pi-ai` 适配器接入 pi-ai,但并没有提供 OAuth 的入口。`@edge-sky/dsh-oauth-adapter` 通过 DSH Authorization Service 暴露 pi-ai 已有的 provider 登录流程。浏览器链接、设备码、文本与密码输入、登录方式选择、取消和撤回提示均通过同一套 provider-neutral 传输完成。
|
|
63
45
|
|
|
64
|
-
|
|
46
|
+
由于 DSH 目前并没有挂载`ctx.authorization`服务,因此该插件同时手动对其进行了挂载
|
|
65
47
|
|
|
66
|
-
|
|
67
|
-
pnpm install --frozen-lockfile
|
|
68
|
-
artifact_dir=$(mktemp -d)
|
|
69
|
-
npm pack --pack-destination "$artifact_dir"
|
|
70
|
-
export DSH_HOME=$(mktemp -d)
|
|
71
|
-
mkdir -p "$DSH_HOME/profiles/web"
|
|
72
|
-
cat > "$DSH_HOME/profiles/web/pnpm-workspace.yaml" <<'YAML'
|
|
73
|
-
packages:
|
|
74
|
-
- .
|
|
75
|
-
nodeLinker: hoisted
|
|
76
|
-
autoInstallPeers: false
|
|
77
|
-
allowBuilds:
|
|
78
|
-
'@google/genai': false
|
|
79
|
-
protobufjs: false
|
|
80
|
-
YAML
|
|
81
|
-
npx @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add "$artifact_dir"/*.tgz
|
|
82
|
-
npx @deepseek-ai/dsh@0.1.2-rc.1 web --no-open
|
|
83
|
-
```
|
|
48
|
+
插件本身并不承担 OAuth 凭证管理与维持登录态的职责,或许将来 DSH 会官方支持这一登录方式,但至少现在你可以通过这个插件体验它
|
|
84
49
|
|
|
85
|
-
|
|
50
|
+
如果这个插件帮到了你,还请留下你的小星星,如果有任何建议,欢迎 issues~
|
package/lib/client.js
CHANGED
|
@@ -98,6 +98,7 @@ window.__ModuleLoader__.load({
|
|
|
98
98
|
},
|
|
99
99
|
children: [
|
|
100
100
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("modelId"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
101
|
+
className: "dsh-oauth-input",
|
|
101
102
|
required: true,
|
|
102
103
|
maxLength: 256,
|
|
103
104
|
value: id,
|
|
@@ -105,12 +106,14 @@ window.__ModuleLoader__.load({
|
|
|
105
106
|
onChange: (event) => setId(event.target.value)
|
|
106
107
|
})] }),
|
|
107
108
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("modelName"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
109
|
+
className: "dsh-oauth-input",
|
|
108
110
|
maxLength: 256,
|
|
109
111
|
value: name,
|
|
110
112
|
onChange: (event) => setName(event.target.value),
|
|
111
113
|
placeholder: id
|
|
112
114
|
})] }),
|
|
113
115
|
choices.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("modelProtocol"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
116
|
+
className: "dsh-oauth-select",
|
|
114
117
|
value: api,
|
|
115
118
|
onChange: (event) => setApi(event.target.value),
|
|
116
119
|
children: choices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
@@ -123,11 +126,13 @@ window.__ModuleLoader__.load({
|
|
|
123
126
|
children: t("manualModelHint")
|
|
124
127
|
}),
|
|
125
128
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
129
|
+
className: "dsh-oauth-button",
|
|
126
130
|
type: "submit",
|
|
127
131
|
disabled: busy || !validManual(provider, model),
|
|
128
132
|
children: t("saveModel")
|
|
129
133
|
}),
|
|
130
134
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
135
|
+
className: "dsh-oauth-button",
|
|
131
136
|
type: "button",
|
|
132
137
|
disabled: busy,
|
|
133
138
|
onClick: close,
|
|
@@ -137,6 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
137
142
|
});
|
|
138
143
|
}
|
|
139
144
|
function ProviderModels({ page, controller, busy, error, t }) {
|
|
145
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
140
146
|
const [editing, setEditing] = (0, react.useState)();
|
|
141
147
|
const [submitted, setSubmitted] = (0, react.useState)(false);
|
|
142
148
|
const [confirmMigration, setConfirmMigration] = (0, react.useState)(false);
|
|
@@ -155,144 +161,184 @@ window.__ModuleLoader__.load({
|
|
|
155
161
|
const disabled = busy || !page.connected || !page.writable;
|
|
156
162
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
157
163
|
className: "dsh-oauth-model-provider",
|
|
158
|
-
children: [
|
|
159
|
-
|
|
160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
page.source === "account" ? t("accountCatalog") : t("builtinCatalog")
|
|
164
|
-
] }),
|
|
165
|
-
page.syncedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
166
|
-
t("lastModelSync"),
|
|
167
|
-
": ",
|
|
168
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
169
|
-
dateTime: page.syncedAt,
|
|
170
|
-
children: new Date(page.syncedAt).toLocaleString()
|
|
171
|
-
})
|
|
172
|
-
] }),
|
|
173
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
174
|
-
t("matchedModels"),
|
|
175
|
-
": ",
|
|
176
|
-
page.counts.matched,
|
|
177
|
-
" · ",
|
|
178
|
-
t("pendingModels"),
|
|
179
|
-
": ",
|
|
180
|
-
page.counts.pending,
|
|
181
|
-
" · ",
|
|
182
|
-
t("manualModels"),
|
|
183
|
-
": ",
|
|
184
|
-
page.counts.manual
|
|
185
|
-
] }),
|
|
186
|
-
(error || page.error) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
187
|
-
role: "alert",
|
|
188
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(errorLabel) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("technicalDetails") }), error?.message ?? page.error] })]
|
|
189
|
-
}),
|
|
190
|
-
page.migration ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
191
|
-
disabled,
|
|
192
|
-
onClick: () => setConfirmMigration(!confirmMigration),
|
|
193
|
-
children: t("reviewMigration")
|
|
194
|
-
}), confirmMigration && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
195
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("migrationDescription") }),
|
|
196
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
197
|
-
page.migration.fields.join(", "),
|
|
198
|
-
" · ",
|
|
199
|
-
page.migration.models,
|
|
200
|
-
" ",
|
|
201
|
-
t("modelsTitle")
|
|
202
|
-
] }),
|
|
203
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
204
|
-
disabled,
|
|
205
|
-
onClick: () => controller.migrateModels(page.provider),
|
|
206
|
-
children: t("confirmMigration")
|
|
207
|
-
})
|
|
208
|
-
] })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
209
|
-
disabled: disabled || !page.managed,
|
|
210
|
-
onClick: () => controller.syncModels(page.provider),
|
|
211
|
-
children: busy ? t("modelWorking") : t("syncModels")
|
|
164
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
165
|
+
className: "dsh-oauth-model-header",
|
|
166
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
167
|
+
className: "dsh-oauth-name",
|
|
168
|
+
children: PROVIDERS.find((p) => p.id === page.provider)?.modelGroupLabel
|
|
212
169
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
170
|
+
className: "dsh-oauth-button",
|
|
171
|
+
type: "button",
|
|
172
|
+
"aria-expanded": expanded,
|
|
173
|
+
"aria-controls": `dsh-oauth-models-${page.provider}`,
|
|
174
|
+
onClick: () => setExpanded(!expanded),
|
|
175
|
+
children: t(expanded ? "collapseModels" : "editModel")
|
|
176
|
+
})]
|
|
177
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
178
|
+
className: "dsh-oauth-model-details",
|
|
179
|
+
id: `dsh-oauth-models-${page.provider}`,
|
|
180
|
+
hidden: !expanded,
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
183
|
+
className: "dsh-oauth-muted",
|
|
184
|
+
children: [
|
|
185
|
+
page.connected ? t("modelConnected") : t("disconnected"),
|
|
186
|
+
" · ",
|
|
187
|
+
page.source === "account" ? t("accountCatalog") : t("builtinCatalog")
|
|
188
|
+
]
|
|
189
|
+
}),
|
|
190
|
+
page.syncedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
191
|
+
className: "dsh-oauth-muted",
|
|
192
|
+
children: [
|
|
193
|
+
t("lastModelSync"),
|
|
194
|
+
": ",
|
|
195
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
196
|
+
dateTime: page.syncedAt,
|
|
197
|
+
children: new Date(page.syncedAt).toLocaleString()
|
|
198
|
+
})
|
|
199
|
+
]
|
|
200
|
+
}),
|
|
201
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
202
|
+
className: "dsh-oauth-muted",
|
|
203
|
+
children: [
|
|
204
|
+
t("matchedModels"),
|
|
205
|
+
": ",
|
|
206
|
+
page.counts.matched,
|
|
207
|
+
" · ",
|
|
208
|
+
t("manualModels"),
|
|
209
|
+
": ",
|
|
210
|
+
page.counts.manual
|
|
211
|
+
]
|
|
212
|
+
}),
|
|
213
|
+
(error || page.error) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
214
|
+
className: "dsh-oauth-error",
|
|
215
|
+
role: "alert",
|
|
216
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(errorLabel) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("technicalDetails") }), error?.message ?? page.error] })]
|
|
217
|
+
}),
|
|
218
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
219
|
+
className: "dsh-oauth-model-actions",
|
|
220
|
+
children: page.migration ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
221
|
+
className: "dsh-oauth-button",
|
|
222
|
+
disabled,
|
|
223
|
+
onClick: () => setConfirmMigration(!confirmMigration),
|
|
224
|
+
children: t("reviewMigration")
|
|
225
|
+
}), confirmMigration && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
226
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("migrationDescription") }),
|
|
227
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
228
|
+
page.migration.fields.join(", "),
|
|
229
|
+
" · ",
|
|
230
|
+
page.migration.models,
|
|
231
|
+
" ",
|
|
232
|
+
t("modelsTitle")
|
|
233
|
+
] }),
|
|
234
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
235
|
+
className: "dsh-oauth-button",
|
|
236
|
+
disabled,
|
|
237
|
+
onClick: () => controller.migrateModels(page.provider),
|
|
238
|
+
children: t("confirmMigration")
|
|
239
|
+
})
|
|
240
|
+
] })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
241
|
+
className: "dsh-oauth-button",
|
|
242
|
+
disabled: disabled || !page.managed,
|
|
243
|
+
onClick: () => controller.syncModels(page.provider),
|
|
244
|
+
children: busy ? t("modelWorking") : t("syncModels")
|
|
245
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
246
|
+
className: "dsh-oauth-button",
|
|
247
|
+
disabled: disabled || !page.managed,
|
|
248
|
+
onClick: () => setEditing(null),
|
|
249
|
+
children: t("addModel")
|
|
250
|
+
})] })
|
|
251
|
+
}),
|
|
252
|
+
editing === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelForm, {
|
|
253
|
+
provider: page.provider,
|
|
254
|
+
busy,
|
|
255
|
+
t,
|
|
256
|
+
save: (model) => {
|
|
257
|
+
setSubmitted(true);
|
|
258
|
+
controller.saveModel(page.provider, model);
|
|
259
|
+
},
|
|
260
|
+
close: () => setEditing(void 0)
|
|
261
|
+
}, "new"),
|
|
262
|
+
["manual", "matched"].map((source) => {
|
|
263
|
+
const label = t(source === "manual" ? "manualModels" : "matchedModels");
|
|
264
|
+
const rows = page.rows.filter((row) => source === "manual" ? row.source === "manual" : row.source === "catalog" || row.source === "matched");
|
|
265
|
+
const offset = source === "manual" ? page.manualOffset : page.offset;
|
|
266
|
+
const total = page.counts[source];
|
|
267
|
+
const move = (next) => controller.readModels(page.provider, source === "matched" ? next : page.offset, source === "manual" ? next : page.manualOffset);
|
|
268
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
269
|
+
className: "dsh-oauth-model-group",
|
|
270
|
+
"aria-label": label,
|
|
271
|
+
children: [
|
|
272
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: label }),
|
|
273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
274
|
+
className: "dsh-oauth-model-list",
|
|
275
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: source === "manual" && editing?.id === row.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelForm, {
|
|
276
|
+
provider: page.provider,
|
|
277
|
+
initial: editing,
|
|
278
|
+
busy,
|
|
279
|
+
t,
|
|
280
|
+
save: (model) => {
|
|
281
|
+
setSubmitted(true);
|
|
282
|
+
controller.saveModel(page.provider, model);
|
|
283
|
+
},
|
|
284
|
+
close: () => setEditing(void 0)
|
|
285
|
+
}, row.id) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
286
|
+
className: "dsh-oauth-model-identity",
|
|
287
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: row.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: row.id })]
|
|
288
|
+
}), source === "manual" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
289
|
+
className: "dsh-oauth-model-actions",
|
|
290
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
291
|
+
className: "dsh-oauth-button",
|
|
292
|
+
disabled,
|
|
293
|
+
onClick: () => setEditing({
|
|
294
|
+
id: row.id,
|
|
295
|
+
name: row.name,
|
|
296
|
+
...MODEL_PROTOCOLS[page.provider].length > 1 ? { api: row.api } : {}
|
|
297
|
+
}),
|
|
298
|
+
children: t("editModel")
|
|
299
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
300
|
+
className: "dsh-oauth-button",
|
|
301
|
+
"data-variant": "danger",
|
|
302
|
+
disabled,
|
|
303
|
+
onClick: () => controller.deleteModel(page.provider, row.id),
|
|
304
|
+
children: t("deleteModel")
|
|
305
|
+
})]
|
|
306
|
+
})] }) }, row.id))
|
|
254
307
|
}),
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
})
|
|
262
|
-
children:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
disabled: page.offset + 10 >= page.total || busy,
|
|
290
|
-
onClick: () => controller.readModels(page.provider, page.offset + 10),
|
|
291
|
-
children: t("nextModels")
|
|
292
|
-
})
|
|
293
|
-
]
|
|
294
|
-
})
|
|
295
|
-
]
|
|
308
|
+
total === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
309
|
+
className: "dsh-oauth-muted",
|
|
310
|
+
children: t("noModels")
|
|
311
|
+
}),
|
|
312
|
+
total > 10 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
|
|
313
|
+
className: "dsh-oauth-pagination",
|
|
314
|
+
"aria-label": `${label} · ${t("modelsPagination")}`,
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
317
|
+
className: "dsh-oauth-button",
|
|
318
|
+
disabled: offset === 0 || busy,
|
|
319
|
+
onClick: () => move(Math.max(0, offset - 10)),
|
|
320
|
+
children: t("previousModels")
|
|
321
|
+
}),
|
|
322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
323
|
+
offset + 1,
|
|
324
|
+
"–",
|
|
325
|
+
Math.min(offset + 10, total),
|
|
326
|
+
" / ",
|
|
327
|
+
total
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
330
|
+
className: "dsh-oauth-button",
|
|
331
|
+
disabled: offset + 10 >= total || busy,
|
|
332
|
+
onClick: () => move(offset + 10),
|
|
333
|
+
children: t("nextModels")
|
|
334
|
+
})
|
|
335
|
+
]
|
|
336
|
+
})
|
|
337
|
+
]
|
|
338
|
+
}, source);
|
|
339
|
+
})
|
|
340
|
+
]
|
|
341
|
+
})]
|
|
296
342
|
});
|
|
297
343
|
}
|
|
298
344
|
function OAuthModelsSection(props) {
|
|
@@ -311,8 +357,14 @@ window.__ModuleLoader__.load({
|
|
|
311
357
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
312
358
|
className: "dsh-oauth-section",
|
|
313
359
|
children: [
|
|
314
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
315
|
-
|
|
360
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
361
|
+
className: "dsh-oauth-title",
|
|
362
|
+
children: t("modelsTitle")
|
|
363
|
+
}),
|
|
364
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
365
|
+
className: "dsh-oauth-intro",
|
|
366
|
+
children: t("modelsIntro")
|
|
367
|
+
}),
|
|
316
368
|
snapshot.connection !== "open" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
317
369
|
role: "status",
|
|
318
370
|
children: t("hostDisconnected")
|
|
@@ -320,7 +372,7 @@ window.__ModuleLoader__.load({
|
|
|
320
372
|
PROVIDERS.map((provider) => {
|
|
321
373
|
const page = snapshot.modelPages?.[provider.id];
|
|
322
374
|
if (!page) return null;
|
|
323
|
-
if (!page.connected
|
|
375
|
+
if (!page.connected || !snapshot.accounts.some((account) => account.provider === provider.id && account.configured)) return null;
|
|
324
376
|
const error = snapshot.modelErrors?.[provider.id];
|
|
325
377
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderModels, {
|
|
326
378
|
page,
|
|
@@ -588,7 +640,7 @@ window.__ModuleLoader__.load({
|
|
|
588
640
|
modelRouteConflict: "This provider is owned by an existing configuration. Review the details before migration.",
|
|
589
641
|
loginSyncFailed: "Sign-in succeeded, but model synchronization failed. Open Models to retry.",
|
|
590
642
|
modelsTitle: "OAuth models",
|
|
591
|
-
modelsIntro: "
|
|
643
|
+
modelsIntro: "Models are automatically matched against the pi-ai catalog built into DSH. If a model you need is not matched, try adding it manually.",
|
|
592
644
|
modelId: "Model ID",
|
|
593
645
|
modelName: "Display name",
|
|
594
646
|
modelProtocol: "Request protocol",
|
|
@@ -598,7 +650,7 @@ window.__ModuleLoader__.load({
|
|
|
598
650
|
accountCatalog: "Account-synchronized list",
|
|
599
651
|
builtinCatalog: "Built-in list · account access unverified",
|
|
600
652
|
lastModelSync: "Last successful synchronization",
|
|
601
|
-
matchedModels: "
|
|
653
|
+
matchedModels: "Automatic matches",
|
|
602
654
|
pendingModels: "Needs configuration",
|
|
603
655
|
manualModels: "Manual",
|
|
604
656
|
modelConflict: "Models changed elsewhere. The list was refreshed; review it and retry.",
|
|
@@ -611,8 +663,9 @@ window.__ModuleLoader__.load({
|
|
|
611
663
|
addModel: "Add model",
|
|
612
664
|
configureModel: "Configure",
|
|
613
665
|
editModel: "Edit",
|
|
666
|
+
collapseModels: "Collapse",
|
|
614
667
|
deleteModel: "Delete manual entry",
|
|
615
|
-
noModels: "No
|
|
668
|
+
noModels: "No models in this group.",
|
|
616
669
|
modelsPagination: "OAuth model pages",
|
|
617
670
|
previousModels: "Previous",
|
|
618
671
|
nextModels: "Next",
|
|
@@ -654,7 +707,7 @@ window.__ModuleLoader__.load({
|
|
|
654
707
|
modelRouteConflict: "该供应商由现有配置管理,请查看详情后再迁移。",
|
|
655
708
|
loginSyncFailed: "登录成功,但模型同步失败。请前往模型页面重试。",
|
|
656
709
|
modelsTitle: "OAuth 模型",
|
|
657
|
-
modelsIntro: "
|
|
710
|
+
modelsIntro: "模型由 DSH 内置 pi-ai 自动匹配。若未匹配到你需要的模型,请尝试手动添加。",
|
|
658
711
|
modelId: "模型 ID",
|
|
659
712
|
modelName: "显示名称",
|
|
660
713
|
modelProtocol: "调用协议",
|
|
@@ -664,9 +717,9 @@ window.__ModuleLoader__.load({
|
|
|
664
717
|
accountCatalog: "已同步账号列表",
|
|
665
718
|
builtinCatalog: "内置列表 · 尚未验证账号权限",
|
|
666
719
|
lastModelSync: "最近成功同步",
|
|
667
|
-
matchedModels: "
|
|
720
|
+
matchedModels: "自动匹配",
|
|
668
721
|
pendingModels: "待配置",
|
|
669
|
-
manualModels: "
|
|
722
|
+
manualModels: "手动配置",
|
|
670
723
|
modelConflict: "模型已被其他操作修改。列表已刷新,请确认后重试。",
|
|
671
724
|
modelOperationFailed: "模型操作失败,请查看详情后重试。",
|
|
672
725
|
reviewMigration: "预览迁移",
|
|
@@ -677,8 +730,9 @@ window.__ModuleLoader__.load({
|
|
|
677
730
|
addModel: "手动添加",
|
|
678
731
|
configureModel: "补充配置",
|
|
679
732
|
editModel: "编辑",
|
|
733
|
+
collapseModels: "收起",
|
|
680
734
|
deleteModel: "删除手动项",
|
|
681
|
-
noModels: "
|
|
735
|
+
noModels: "此分组暂无模型。",
|
|
682
736
|
modelsPagination: "OAuth 模型分页",
|
|
683
737
|
previousModels: "上一页",
|
|
684
738
|
nextModels: "下一页",
|
|
@@ -811,9 +865,9 @@ window.__ModuleLoader__.load({
|
|
|
811
865
|
}
|
|
812
866
|
loadModels() {
|
|
813
867
|
this.modelsVisible = true;
|
|
814
|
-
for (const provider of this.snapshot.accounts) this.readModels(provider.provider);
|
|
868
|
+
for (const provider of this.snapshot.accounts) if (provider.configured) this.readModels(provider.provider);
|
|
815
869
|
}
|
|
816
|
-
readModels(provider, offset = 0) {
|
|
870
|
+
readModels(provider, offset = 0, manualOffset = 0) {
|
|
817
871
|
if (this.snapshot.connection !== "open") return;
|
|
818
872
|
const requestId = crypto.randomUUID();
|
|
819
873
|
this.latestModelReads.set(provider, requestId);
|
|
@@ -825,7 +879,8 @@ window.__ModuleLoader__.load({
|
|
|
825
879
|
type: "models-list",
|
|
826
880
|
requestId,
|
|
827
881
|
provider,
|
|
828
|
-
offset
|
|
882
|
+
offset,
|
|
883
|
+
manualOffset
|
|
829
884
|
});
|
|
830
885
|
}
|
|
831
886
|
syncModels(provider) {
|
|
@@ -977,12 +1032,12 @@ window.__ModuleLoader__.load({
|
|
|
977
1032
|
receive(message) {
|
|
978
1033
|
switch (message.type) {
|
|
979
1034
|
case "models-changed":
|
|
980
|
-
if (this.modelsVisible) this.readModels(message.provider, this.snapshot.modelPages?.[message.provider]?.offset ?? 0);
|
|
1035
|
+
if (this.modelsVisible) this.readModels(message.provider, this.snapshot.modelPages?.[message.provider]?.offset ?? 0, this.snapshot.modelPages?.[message.provider]?.manualOffset ?? 0);
|
|
981
1036
|
return;
|
|
982
1037
|
case "models-page": {
|
|
983
1038
|
const request = this.modelRequests.get(message.requestId);
|
|
984
1039
|
this.modelRequests.delete(message.requestId);
|
|
985
|
-
if (!request) return;
|
|
1040
|
+
if (!request || !this.snapshot.accounts.some((account) => account.provider === request.provider && account.configured)) return;
|
|
986
1041
|
if (!request.write && this.latestModelReads.get(request.provider) !== message.requestId) return;
|
|
987
1042
|
const current = this.snapshot.modelPages?.[request.provider];
|
|
988
1043
|
if (current && message.page.revision < current.revision) {
|
|
@@ -1011,7 +1066,7 @@ window.__ModuleLoader__.load({
|
|
|
1011
1066
|
case "models-error": {
|
|
1012
1067
|
const request = this.modelRequests.get(message.requestId);
|
|
1013
1068
|
this.modelRequests.delete(message.requestId);
|
|
1014
|
-
if (!request) return;
|
|
1069
|
+
if (!request || !this.snapshot.accounts.some((account) => account.provider === request.provider && account.configured)) return;
|
|
1015
1070
|
if (!request.write && this.latestModelReads.get(request.provider) !== message.requestId) return;
|
|
1016
1071
|
this.update({
|
|
1017
1072
|
...this.snapshot,
|
|
@@ -1033,14 +1088,30 @@ window.__ModuleLoader__.load({
|
|
|
1033
1088
|
case "snapshot":
|
|
1034
1089
|
const accounts = [...message.accounts];
|
|
1035
1090
|
const reconciled = reconcileAttemptView(this.snapshot.attempt, accounts);
|
|
1091
|
+
const modelPages = { ...this.snapshot.modelPages };
|
|
1092
|
+
const modelBusy = { ...this.snapshot.modelBusy };
|
|
1093
|
+
const modelErrors = { ...this.snapshot.modelErrors };
|
|
1094
|
+
for (const account of accounts) {
|
|
1095
|
+
if (account.configured) continue;
|
|
1096
|
+
delete modelPages[account.provider];
|
|
1097
|
+
delete modelBusy[account.provider];
|
|
1098
|
+
delete modelErrors[account.provider];
|
|
1099
|
+
this.latestModelReads.delete(account.provider);
|
|
1100
|
+
for (const [id, request] of this.modelRequests) if (request.provider === account.provider) this.modelRequests.delete(id);
|
|
1101
|
+
}
|
|
1036
1102
|
const nextSnapshot = {
|
|
1037
1103
|
...this.snapshot,
|
|
1038
|
-
accounts
|
|
1104
|
+
accounts,
|
|
1105
|
+
modelPages,
|
|
1106
|
+
modelBusy,
|
|
1107
|
+
modelErrors
|
|
1039
1108
|
};
|
|
1040
1109
|
if (reconciled === void 0) delete nextSnapshot.attempt;
|
|
1041
1110
|
else nextSnapshot.attempt = reconciled;
|
|
1042
1111
|
this.update(nextSnapshot);
|
|
1043
|
-
if (this.modelsVisible)
|
|
1112
|
+
if (this.modelsVisible) {
|
|
1113
|
+
for (const account of accounts) if (account.configured) this.readModels(account.provider, this.snapshot.modelPages?.[account.provider]?.offset ?? 0, this.snapshot.modelPages?.[account.provider]?.manualOffset ?? 0);
|
|
1114
|
+
}
|
|
1044
1115
|
return;
|
|
1045
1116
|
case "started":
|
|
1046
1117
|
const { error: _error, errorDetail: _errorDetail, ...current } = this.snapshot;
|
|
@@ -1152,14 +1223,23 @@ window.__ModuleLoader__.load({
|
|
|
1152
1223
|
.dsh-oauth-flow{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l3);font-size:13px;line-height:20px}.dsh-oauth-link{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);text-decoration:none}.dsh-oauth-link:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-link-label{font-weight:500}.dsh-oauth-link-host{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsh-oauth-link-icon{color:var(--dsw-alias-brand-text)}.dsh-oauth-code{display:flex;align-items:center;gap:8px}.dsh-oauth-code code{padding:5px 8px;border-radius:6px;background:var(--dsw-alias-bg-module-platform);font-size:14px;letter-spacing:.08em}
|
|
1153
1224
|
.dsh-oauth-prompt{display:flex;flex-direction:column;gap:7px}.dsh-oauth-input,.dsh-oauth-select{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}
|
|
1154
1225
|
|
|
1155
|
-
.dsh-oauth-model-provider
|
|
1156
|
-
.dsh-oauth-model-
|
|
1157
|
-
.dsh-oauth-model-
|
|
1158
|
-
.dsh-oauth-model-
|
|
1159
|
-
.dsh-oauth-model-
|
|
1160
|
-
.dsh-oauth-model-
|
|
1161
|
-
.dsh-oauth-model-
|
|
1162
|
-
.dsh-oauth-model-
|
|
1226
|
+
.dsh-oauth-model-provider{display:flex;flex-direction:column;gap:10px;min-width:0;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}
|
|
1227
|
+
.dsh-oauth-model-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.dsh-oauth-model-header h3{min-width:0;overflow-wrap:anywhere}.dsh-oauth-model-header button{flex-shrink:0}
|
|
1228
|
+
.dsh-oauth-model-details{display:flex;flex-direction:column;gap:10px}.dsh-oauth-model-details[hidden]{display:none}
|
|
1229
|
+
.dsh-oauth-model-provider h3,.dsh-oauth-model-provider h4{margin:0;line-height:22px;font-weight:500}
|
|
1230
|
+
.dsh-oauth-model-provider h4{font-size:13px;color:var(--dsw-alias-label-secondary)}
|
|
1231
|
+
.dsh-oauth-model-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
1232
|
+
.dsh-oauth-model-group{display:flex;flex-direction:column;gap:8px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
1233
|
+
.dsh-oauth-model-list{display:flex;flex-direction:column;gap:0;margin:0;padding:0;list-style:none}
|
|
1234
|
+
.dsh-oauth-model-list li{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:10px 0;min-width:0}
|
|
1235
|
+
.dsh-oauth-model-list li>.dsh-oauth-model-form{box-sizing:border-box;width:100%;min-width:0}
|
|
1236
|
+
.dsh-oauth-model-list li+li{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
1237
|
+
.dsh-oauth-model-identity{display:flex;flex-direction:column;gap:3px;min-width:0;overflow-wrap:anywhere}
|
|
1238
|
+
.dsh-oauth-model-identity strong{font-size:13px;line-height:20px;font-weight:500}
|
|
1239
|
+
.dsh-oauth-model-identity code{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}
|
|
1240
|
+
.dsh-oauth-model-form{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px}
|
|
1241
|
+
.dsh-oauth-model-form label{display:flex;flex-direction:column;gap:5px;font-size:13px;line-height:20px}
|
|
1242
|
+
.dsh-oauth-pagination{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;color:var(--dsw-alias-label-tertiary)}
|
|
1163
1243
|
`;
|
|
1164
1244
|
//#endregion
|
|
1165
1245
|
//#region src/client/index.ts
|
package/lib/index.js
CHANGED
|
@@ -274,7 +274,7 @@ class OAuthConnection {
|
|
|
274
274
|
}
|
|
275
275
|
if (!this.closed)
|
|
276
276
|
this.send({ type: 'models-page', requestId: command.requestId,
|
|
277
|
-
page: await this.models.page(command.provider, command.type === 'models-list' ? command.offset : 0) });
|
|
277
|
+
page: await this.models.page(command.provider, command.type === 'models-list' ? command.offset : 0, command.type === 'models-list' ? command.manualOffset : 0) });
|
|
278
278
|
}
|
|
279
279
|
catch (error) {
|
|
280
280
|
if (!this.closed)
|
package/lib/model-service.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export declare class OAuthModelService {
|
|
|
51
51
|
/** Wait on the actual route registry; settings persistence alone does not imply adapter release. */
|
|
52
52
|
private releaseLegacy;
|
|
53
53
|
private ensureManaged;
|
|
54
|
-
page(provider: ProviderId, offset: number): Promise<ModelPage>;
|
|
54
|
+
page(provider: ProviderId, offset: number, manualOffset?: number): Promise<ModelPage>;
|
|
55
55
|
migrate(provider: ProviderId, revision: number): Promise<void>;
|
|
56
56
|
saveManual(provider: ProviderId, model: ManualModel, revision: number): Promise<void>;
|
|
57
57
|
deleteManual(provider: ProviderId, id: string, revision: number): Promise<void>;
|
package/lib/model-service.js
CHANGED
|
@@ -214,20 +214,25 @@ export class OAuthModelService {
|
|
|
214
214
|
throw error;
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
async page(provider, offset) {
|
|
217
|
+
async page(provider, offset, manualOffset = 0) {
|
|
218
218
|
await this.ready;
|
|
219
219
|
const state = this.state().providers[provider];
|
|
220
220
|
const fallback = { phase: 'pending', legacy: this.legacy(provider).profile ?? {}, manual: [] };
|
|
221
|
-
const
|
|
221
|
+
const connected = await this.connected(provider);
|
|
222
|
+
const rows = connected ? materialize(provider, state ?? fallback).rows : [];
|
|
223
|
+
const automatic = rows.filter(row => row.source === 'catalog' || row.source === 'matched');
|
|
224
|
+
const manual = rows.filter(row => row.source === 'manual');
|
|
222
225
|
const legacy = this.legacy(provider);
|
|
223
|
-
offset = Math.min(offset, Math.max(0, Math.floor((
|
|
226
|
+
offset = Math.min(offset, Math.max(0, Math.floor((automatic.length - 1) / MODEL_PAGE_SIZE) * MODEL_PAGE_SIZE));
|
|
227
|
+
manualOffset = Math.min(manualOffset, Math.max(0, Math.floor((manual.length - 1) / MODEL_PAGE_SIZE) * MODEL_PAGE_SIZE));
|
|
224
228
|
return {
|
|
225
|
-
provider, revision: this.revision(), offset, total:
|
|
229
|
+
provider, revision: this.revision(), offset, manualOffset, total: automatic.length + manual.length,
|
|
230
|
+
rows: [...automatic.slice(offset, offset + MODEL_PAGE_SIZE), ...manual.slice(manualOffset, manualOffset + MODEL_PAGE_SIZE)],
|
|
226
231
|
source: state?.discovered ? 'account' : 'catalog',
|
|
227
232
|
counts: { matched: rows.filter(r => r.source === 'matched' || r.source === 'catalog').length, pending: rows.filter(r => r.source === 'pending').length, manual: rows.filter(r => r.source === 'manual').length },
|
|
228
|
-
connected
|
|
233
|
+
connected, writable: this.ctx.settings.writable,
|
|
229
234
|
managed: this.isManaged(provider), busy: this.syncs.has(provider),
|
|
230
|
-
...(state?.syncedAt === undefined ? {} : { syncedAt: state.syncedAt }),
|
|
235
|
+
...(!connected || state?.syncedAt === undefined ? {} : { syncedAt: state.syncedAt }),
|
|
231
236
|
...(this.errors.has(provider) ? { error: this.errors.get(provider).message, errorCode: this.errors.get(provider).code } : {}),
|
|
232
237
|
...((state?.phase === 'pending' || !state && legacy.custom) && !legacy.blocked ? { migration: { fields: Object.keys(state?.legacy ?? legacy.profile ?? {}), models: (state?.legacy ?? legacy.profile)?.models?.length ?? 0 } } : {}),
|
|
233
238
|
};
|
package/lib/model-types.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export interface ModelPage {
|
|
|
29
29
|
provider: ProviderId;
|
|
30
30
|
revision: number;
|
|
31
31
|
offset: number;
|
|
32
|
+
manualOffset: number;
|
|
32
33
|
total: number;
|
|
33
34
|
rows: ModelRow[];
|
|
34
35
|
source: 'catalog' | 'account';
|
|
@@ -54,6 +55,7 @@ export type ModelCommand = {
|
|
|
54
55
|
requestId: string;
|
|
55
56
|
provider: ProviderId;
|
|
56
57
|
offset: number;
|
|
58
|
+
manualOffset: number;
|
|
57
59
|
} | {
|
|
58
60
|
type: 'models-sync' | 'models-migrate';
|
|
59
61
|
requestId: string;
|
package/lib/protocol.js
CHANGED
|
@@ -66,8 +66,8 @@ export function parseClientCommand(raw) {
|
|
|
66
66
|
const provider = candidate.provider;
|
|
67
67
|
const fail = { ok: false, requestId, message: 'model command fields are invalid' };
|
|
68
68
|
if (candidate.type === 'models-list') {
|
|
69
|
-
return exactKeys(candidate, ['type', 'requestId', 'provider', 'offset']) && Number.isSafeInteger(candidate.offset) && Number(candidate.offset) >= 0
|
|
70
|
-
? { ok: true, value: { type: 'models-list', requestId, provider, offset: Number(candidate.offset) } } : fail;
|
|
69
|
+
return exactKeys(candidate, ['type', 'requestId', 'provider', 'offset', 'manualOffset']) && Number.isSafeInteger(candidate.offset) && Number(candidate.offset) >= 0 && Number.isSafeInteger(candidate.manualOffset) && Number(candidate.manualOffset) >= 0
|
|
70
|
+
? { ok: true, value: { type: 'models-list', requestId, provider, offset: Number(candidate.offset), manualOffset: Number(candidate.manualOffset) } } : fail;
|
|
71
71
|
}
|
|
72
72
|
if (!Number.isSafeInteger(candidate.revision) || Number(candidate.revision) < 0)
|
|
73
73
|
return fail;
|
|
@@ -6,7 +6,7 @@ export declare const en: {
|
|
|
6
6
|
readonly modelRouteConflict: "This provider is owned by an existing configuration. Review the details before migration.";
|
|
7
7
|
readonly loginSyncFailed: "Sign-in succeeded, but model synchronization failed. Open Models to retry.";
|
|
8
8
|
readonly modelsTitle: "OAuth models";
|
|
9
|
-
readonly modelsIntro: "
|
|
9
|
+
readonly modelsIntro: "Models are automatically matched against the pi-ai catalog built into DSH. If a model you need is not matched, try adding it manually.";
|
|
10
10
|
readonly modelId: "Model ID";
|
|
11
11
|
readonly modelName: "Display name";
|
|
12
12
|
readonly modelProtocol: "Request protocol";
|
|
@@ -16,7 +16,7 @@ export declare const en: {
|
|
|
16
16
|
readonly accountCatalog: "Account-synchronized list";
|
|
17
17
|
readonly builtinCatalog: "Built-in list · account access unverified";
|
|
18
18
|
readonly lastModelSync: "Last successful synchronization";
|
|
19
|
-
readonly matchedModels: "
|
|
19
|
+
readonly matchedModels: "Automatic matches";
|
|
20
20
|
readonly pendingModels: "Needs configuration";
|
|
21
21
|
readonly manualModels: "Manual";
|
|
22
22
|
readonly modelConflict: "Models changed elsewhere. The list was refreshed; review it and retry.";
|
|
@@ -29,8 +29,9 @@ export declare const en: {
|
|
|
29
29
|
readonly addModel: "Add model";
|
|
30
30
|
readonly configureModel: "Configure";
|
|
31
31
|
readonly editModel: "Edit";
|
|
32
|
+
readonly collapseModels: "Collapse";
|
|
32
33
|
readonly deleteModel: "Delete manual entry";
|
|
33
|
-
readonly noModels: "No
|
|
34
|
+
readonly noModels: "No models in this group.";
|
|
34
35
|
readonly modelsPagination: "OAuth model pages";
|
|
35
36
|
readonly previousModels: "Previous";
|
|
36
37
|
readonly nextModels: "Next";
|
|
@@ -58,7 +58,7 @@ export declare class OAuthAccountsController {
|
|
|
58
58
|
cancel(attemptId: string): void;
|
|
59
59
|
forget(provider: ProviderId): void;
|
|
60
60
|
loadModels(): void;
|
|
61
|
-
readModels(provider: ProviderId, offset?: number): void;
|
|
61
|
+
readModels(provider: ProviderId, offset?: number, manualOffset?: number): void;
|
|
62
62
|
syncModels(provider: ProviderId): void;
|
|
63
63
|
migrateModels(provider: ProviderId): void;
|
|
64
64
|
saveModel(provider: ProviderId, model: ManualModel): void;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Plugin-owned styles, injected and removed with the browser Cordis fiber. */
|
|
2
|
-
export declare const styles = "\n.dsh-oauth-section{display:flex;flex-direction:column;gap:12px;max-width:720px;color:var(--dsw-alias-label-primary)}\n.dsh-oauth-title{margin:0;font-size:16px;line-height:24px;font-weight:500}\n.dsh-oauth-intro,.dsh-oauth-muted{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dsh-oauth-error{display:flex;flex-direction:column;gap:6px;margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.dsh-oauth-error p{margin:0}.dsh-oauth-error pre{box-sizing:border-box;max-width:100%;max-height:320px;margin:0;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;overflow:auto;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:11px/17px ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.dsh-oauth-error-details summary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-oauth-error-details[open] summary{margin-bottom:6px}\n.dsh-oauth-list{display:flex;flex-direction:column;gap:8px;margin:4px 0 0;padding:0;list-style:none}\n.dsh-oauth-card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.dsh-oauth-name{font-size:14px;font-weight:500}.dsh-oauth-state{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.dsh-oauth-state[data-state=ready] .dsh-oauth-dot{background:var(--dsw-alias-state-success-primary)}.dsh-oauth-state[data-state=warning] .dsh-oauth-dot{background:var(--dsw-alias-state-error-primary)}.dsh-oauth-state[data-state=authorizing] .dsh-oauth-dot{background:var(--dsw-alias-button-info-fill)}\n.dsh-oauth-actions{display:flex;gap:6px;margin-left:auto}.dsh-oauth-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:15px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}.dsh-oauth-button[data-variant=primary]{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]{border-color:transparent;color:var(--dsw-alias-state-error-primary)}.dsh-oauth-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-button[data-variant=primary]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.dsh-oauth-button:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.dsh-oauth-button:disabled{cursor:not-allowed;opacity:.4}\n.dsh-oauth-flow{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l3);font-size:13px;line-height:20px}.dsh-oauth-link{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);text-decoration:none}.dsh-oauth-link:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-link-label{font-weight:500}.dsh-oauth-link-host{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsh-oauth-link-icon{color:var(--dsw-alias-brand-text)}.dsh-oauth-code{display:flex;align-items:center;gap:8px}.dsh-oauth-code code{padding:5px 8px;border-radius:6px;background:var(--dsw-alias-bg-module-platform);font-size:14px;letter-spacing:.08em}\n.dsh-oauth-prompt{display:flex;flex-direction:column;gap:7px}.dsh-oauth-input,.dsh-oauth-select{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}\n\n.dsh-oauth-model-provider
|
|
2
|
+
export declare const styles = "\n.dsh-oauth-section{display:flex;flex-direction:column;gap:12px;max-width:720px;color:var(--dsw-alias-label-primary)}\n.dsh-oauth-title{margin:0;font-size:16px;line-height:24px;font-weight:500}\n.dsh-oauth-intro,.dsh-oauth-muted{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dsh-oauth-error{display:flex;flex-direction:column;gap:6px;margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.dsh-oauth-error p{margin:0}.dsh-oauth-error pre{box-sizing:border-box;max-width:100%;max-height:320px;margin:0;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;overflow:auto;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:11px/17px ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.dsh-oauth-error-details summary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-oauth-error-details[open] summary{margin-bottom:6px}\n.dsh-oauth-list{display:flex;flex-direction:column;gap:8px;margin:4px 0 0;padding:0;list-style:none}\n.dsh-oauth-card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.dsh-oauth-name{font-size:14px;font-weight:500}.dsh-oauth-state{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.dsh-oauth-state[data-state=ready] .dsh-oauth-dot{background:var(--dsw-alias-state-success-primary)}.dsh-oauth-state[data-state=warning] .dsh-oauth-dot{background:var(--dsw-alias-state-error-primary)}.dsh-oauth-state[data-state=authorizing] .dsh-oauth-dot{background:var(--dsw-alias-button-info-fill)}\n.dsh-oauth-actions{display:flex;gap:6px;margin-left:auto}.dsh-oauth-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:15px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}.dsh-oauth-button[data-variant=primary]{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]{border-color:transparent;color:var(--dsw-alias-state-error-primary)}.dsh-oauth-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-button[data-variant=primary]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.dsh-oauth-button:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.dsh-oauth-button:disabled{cursor:not-allowed;opacity:.4}\n.dsh-oauth-flow{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l3);font-size:13px;line-height:20px}.dsh-oauth-link{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);text-decoration:none}.dsh-oauth-link:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-link-label{font-weight:500}.dsh-oauth-link-host{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsh-oauth-link-icon{color:var(--dsw-alias-brand-text)}.dsh-oauth-code{display:flex;align-items:center;gap:8px}.dsh-oauth-code code{padding:5px 8px;border-radius:6px;background:var(--dsw-alias-bg-module-platform);font-size:14px;letter-spacing:.08em}\n.dsh-oauth-prompt{display:flex;flex-direction:column;gap:7px}.dsh-oauth-input,.dsh-oauth-select{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}\n\n.dsh-oauth-model-provider{display:flex;flex-direction:column;gap:10px;min-width:0;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-model-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.dsh-oauth-model-header h3{min-width:0;overflow-wrap:anywhere}.dsh-oauth-model-header button{flex-shrink:0}\n.dsh-oauth-model-details{display:flex;flex-direction:column;gap:10px}.dsh-oauth-model-details[hidden]{display:none}\n.dsh-oauth-model-provider h3,.dsh-oauth-model-provider h4{margin:0;line-height:22px;font-weight:500}\n.dsh-oauth-model-provider h4{font-size:13px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-model-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dsh-oauth-model-group{display:flex;flex-direction:column;gap:8px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2)}\n.dsh-oauth-model-list{display:flex;flex-direction:column;gap:0;margin:0;padding:0;list-style:none}\n.dsh-oauth-model-list li{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:10px 0;min-width:0}\n.dsh-oauth-model-list li>.dsh-oauth-model-form{box-sizing:border-box;width:100%;min-width:0}\n.dsh-oauth-model-list li+li{border-top:1px solid var(--dsw-alias-border-l2)}\n.dsh-oauth-model-identity{display:flex;flex-direction:column;gap:3px;min-width:0;overflow-wrap:anywhere}\n.dsh-oauth-model-identity strong{font-size:13px;line-height:20px;font-weight:500}\n.dsh-oauth-model-identity code{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}\n.dsh-oauth-model-form{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px}\n.dsh-oauth-model-form label{display:flex;flex-direction:column;gap:5px;font-size:13px;line-height:20px}\n.dsh-oauth-pagination{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n";
|
|
3
3
|
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -29,6 +29,7 @@ export interface ModelPage {
|
|
|
29
29
|
provider: ProviderId;
|
|
30
30
|
revision: number;
|
|
31
31
|
offset: number;
|
|
32
|
+
manualOffset: number;
|
|
32
33
|
total: number;
|
|
33
34
|
rows: ModelRow[];
|
|
34
35
|
source: 'catalog' | 'account';
|
|
@@ -54,6 +55,7 @@ export type ModelCommand = {
|
|
|
54
55
|
requestId: string;
|
|
55
56
|
provider: ProviderId;
|
|
56
57
|
offset: number;
|
|
58
|
+
manualOffset: number;
|
|
57
59
|
} | {
|
|
58
60
|
type: 'models-sync' | 'models-migrate';
|
|
59
61
|
requestId: string;
|
package/package.json
CHANGED