@mangoc/dsh-sub2api-sync 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -80
- package/README.zh.md +15 -3
- package/lib/client.cjs +228 -32
- package/lib/client.js +228 -32
- package/lib/config.d.ts +12 -2
- package/lib/dshgo.d.ts +35 -0
- package/lib/index.js +296 -27
- package/lib/types.d.ts +7 -0
- package/package.json +33 -2
package/README.md
CHANGED
|
@@ -2,61 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
Sync one [sub2api](https://github.com/Wei-Shaw/sub2api) instance's groups, keys, and models into [`dsh-llm-pi-ai`](https://github.com/deepseek-ai/deepseek-harness) provider routes: log in once, integrate automatically, keep syncing.
|
|
4
4
|
|
|
5
|
-
Design
|
|
5
|
+
Design notes: [Phase 1 sync engine](../../../docs/design/2026-09-04-sub2api-provider-sync.md) and the [client UI revamp](../../../docs/design/2026-09-30-sub2api-sync-client-ui.zh.md) (Chinese; the current implementation baseline).
|
|
6
6
|
|
|
7
|
-
## Status:
|
|
7
|
+
## Status: Mode A (panel-driven) + client UI
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **Sign-in channels**: panel password + 2FA (C3) is shipped; web authorization (C2, OAuth authorization code + PKCE) lands once sub2api deploys its OAuth endpoints. PAT (C1) was removed with this plugin — sub2api upstream never shipped the matching endpoint, so the channel has no token source;
|
|
10
|
+
- **Keys-driven sync**: the Keys card lists the panel's existing relay keys (none ticked by default) → **tick one or many** (cross-group) → "Sync selected" → **one key becomes one provider** (route `sub2api-<10-hex>`, no sequence id) → models are selectable on the next request; create new keys with the "Create key" dialog (defaulting to the group's name);
|
|
11
|
+
- **Provider naming**: rendered from `providerNameTemplate`, default `{siteName}({keyName})` — `<site name>(<key name>)`, the site name comes from instance probing;
|
|
12
|
+
- **UI**: the top-level settings card (`settings.section`), the plugin detail page's connect card and sync report (`plugins.detail.section`), and the plugin-page tuning card (`plugins.bundle.config`), styled after `ui-settings-account`, fully bilingual (zh/en);
|
|
13
|
+
- **Data seam**: configuration reads/writes go through the platform `configForms` (field-level, revision-fenced, schema-validated); host routes only carry actions, secrets, and status projections;
|
|
14
|
+
- The fingerprint routes (old Mode B) and group-driven sync (rev.5) are both retired — sync anchors real panel key ids.
|
|
10
15
|
|
|
11
16
|
## Install
|
|
12
17
|
|
|
13
18
|
### User install (release channels)
|
|
14
19
|
|
|
15
|
-
The harness loads
|
|
16
|
-
**built** `lib/index.js`. Tarballs from any channel below ship `lib/` already —
|
|
17
|
-
no local build needed:
|
|
20
|
+
The harness loads the plugin through the package's `main`/`exports` entry — the **built** `lib/index.js`. Tarballs from any channel below ship `lib/` already; no local build needed:
|
|
18
21
|
|
|
19
|
-
**npm** —
|
|
20
|
-
The scoped name is canonical; the unscoped short name is an alias. Either
|
|
21
|
-
installs as a first-class plugin:
|
|
22
|
+
**npm** — two names published in lockstep: same code, same version, the scoped name is canonical and the unscoped short name is an alias. Either installs:
|
|
22
23
|
|
|
23
24
|
```sh
|
|
24
25
|
dsh plugin --profile web add @mangoc/dsh-sub2api-sync # canonical (scoped)
|
|
25
26
|
dsh plugin --profile web add dsh-sub2api-sync # alias (unscoped short name)
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
**GitHub Releases tarball** (
|
|
29
|
-
latest release):
|
|
29
|
+
**GitHub Releases tarball** (npm not involved; the URL always points to the latest release):
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
32
|
dsh plugin --profile web add https://github.com/Shuai-S/dsh-sub2api-sync/releases/latest/download/dsh-sub2api-sync.tgz
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
**From source** (pnpm runs the `prepare` build on git dependencies; needs a
|
|
36
|
-
local toolchain and is slower on first install):
|
|
35
|
+
**From source** (pnpm runs the `prepare` build on git dependencies; needs a local toolchain, slower on first install):
|
|
37
36
|
|
|
38
37
|
```sh
|
|
39
38
|
dsh plugin --profile web add github:Shuai-S/dsh-sub2api-sync#path:packages/llm/sub2api-sync
|
|
40
39
|
```
|
|
41
40
|
|
|
42
|
-
After installing, restart the app or toggle the plugin off/on so the entry is
|
|
43
|
-
imported again; a failed entry is not retried on its own.
|
|
41
|
+
After installing, restart the app or toggle the plugin off/on so the entry is imported again; a failed entry is not retried on its own.
|
|
44
42
|
|
|
45
43
|
### Release (maintainers)
|
|
46
44
|
|
|
47
|
-
Every release publishes **both names in lockstep**
|
|
48
|
-
([scripts/pack-all.mjs](scripts/pack-all.mjs)): one tarball for the scoped
|
|
49
|
-
canonical name and one for the unscoped alias, sharing the same version. Each
|
|
50
|
-
variant embeds its own plugin name at pack time (client-bundle `PKG_NAME`,
|
|
51
|
-
ModuleLoader wrapper id, `cordis.patch.yml`), so both install independently.
|
|
52
|
-
CI ([.github/workflows/release.yml](../../../.github/workflows/release.yml))
|
|
53
|
-
runs the whole flow: bump `version` in
|
|
54
|
-
`packages/llm/sub2api-sync/package.json` → commit →
|
|
55
|
-
`git tag vX.Y.Z && git push origin vX.Y.Z` → `pnpm check` → two tarballs →
|
|
56
|
-
GitHub Release (1 stable-name tarball asset) → npm dual
|
|
57
|
-
publish (requires the repo secret `NPM_TOKEN`; `publishConfig.registry` is
|
|
58
|
-
pinned to the official npmjs registry, immune to mirror configs). Manual dual
|
|
59
|
-
publish from a local checkout:
|
|
45
|
+
Every release publishes **both names in lockstep** ([scripts/pack-all.mjs](scripts/pack-all.mjs)): one tarball for the scoped canonical name and one for the unscoped alias, sharing the same version. The plugin name embedded in the client bundle (mount key, detail-page matching, ModuleLoader id) and `cordis.patch.yml` are injected per package name at pack time, so both variants install independently. CI ([.github/workflows/release.yml](../../../.github/workflows/release.yml)) runs the whole flow: bump `version` in `packages/llm/sub2api-sync/package.json` → commit → `git tag vX.Y.Z && git push origin vX.Y.Z` → `pnpm check` → two tarballs → npm dual publish (requires the repo secret `NPM_TOKEN`; `publishConfig.registry` is pinned to the official npmjs registry, immune to mirror configs); the GitHub Release carries the short-name tarball asset. Manual dual publish from a local checkout:
|
|
60
46
|
|
|
61
47
|
```sh
|
|
62
48
|
pnpm pack:all # emits both tarballs into dist/; repo files restored afterwards
|
|
@@ -64,14 +50,11 @@ npm publish dist/mangoc-dsh-sub2api-sync-*.tgz --registry=https://registry.npmjs
|
|
|
64
50
|
npm publish dist/dsh-sub2api-sync-*.tgz --registry=https://registry.npmjs.org --access public
|
|
65
51
|
```
|
|
66
52
|
|
|
67
|
-
Publishing only the canonical package also works:
|
|
68
|
-
`pnpm --filter @mangoc/dsh-sub2api-sync publish`.
|
|
53
|
+
Publishing only the canonical package also works: `pnpm --filter @mangoc/dsh-sub2api-sync publish`.
|
|
69
54
|
|
|
70
55
|
### Local development (linked checkout)
|
|
71
56
|
|
|
72
|
-
`dsh plugin add` only symlinks a local directory (`link:`); it does **not**
|
|
73
|
-
install or build the target, so building first is a hard precondition — not
|
|
74
|
-
just a development convenience:
|
|
57
|
+
`dsh plugin add` only symlinks a local directory (`link:`); it does **not** install dependencies or build the target, so building first is a hard precondition — not a development-time optional step:
|
|
75
58
|
|
|
76
59
|
```sh
|
|
77
60
|
pnpm install # installs deps (runs the `prepare` build)
|
|
@@ -79,65 +62,64 @@ pnpm build # tsdown + client wrapper; emits lib/index.js and lib/clie
|
|
|
79
62
|
dsh plugin --profile web add ./packages/llm/sub2api-sync
|
|
80
63
|
```
|
|
81
64
|
|
|
82
|
-
After rebuilding, restart the app or toggle the plugin off/on so the entry is
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"15": openai-completions # force a wire protocol per group
|
|
102
|
-
modelListOverrides:
|
|
103
|
-
"15": [gpt-x, gpt-y] # fallback when /v1/models is unreachable
|
|
104
|
-
compatTemplate:
|
|
105
|
-
compat:
|
|
106
|
-
supportsDeveloperRole: false # inherited by every synced model entry
|
|
107
|
-
```
|
|
65
|
+
After rebuilding, restart the app or toggle the plugin off/on so the entry is imported again; a failed entry is not retried on its own.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
1. Click **Connect panel** (from the settings card or the detail page's connect card), fill the **instance URL** (e.g. `https://cn.api.nexarelay.com`, optionally with `/v1`) plus credentials (panel email/password; TOTP accounts enter the one-time code when prompted; the password is stored in the credential store, so later sign-ins need no retyping);
|
|
70
|
+
2. The **Keys card** lists the instance's relay keys (newest first, 5 per page, "Load more" at the bottom). The checkbox in front of each row selects it for sync; "**Create key**" in the corner opens a dialog: pick a group → the name defaults to the group's name → the new key is ticked automatically;
|
|
71
|
+
3. Tick the keys to use (multi-select, cross-group) and hit "**Sync selected (N)**" — the engine builds **one provider per ticked key**, discovers models, maps the wire protocol from the owning group's platform (anthropic → anthropic-messages, the OpenAI family → openai-responses, the rest → openai-completions, overridable per group), and writes them into `llm-pi-ai.providers`;
|
|
72
|
+
4. Models are selectable on the next request. The "Sync" block at the bottom of the main card also triggers a full sync and shows the complete report; every report entry carries a "**Remove**" button — local routes and credentials only (**no panel calls**, works even with an empty balance) — and unticks the key so the next sync cannot resurrect it; the optional tuning fields live on the **Plugins page → Sub2API Sync** config card.
|
|
73
|
+
|
|
74
|
+
## dsh-Go (Android) adaptation
|
|
75
|
+
|
|
76
|
+
The plugin adapts to [DSH Go](https://github.com/ccch1mneyyy/dsh-Go)'s native settings capabilities (native protocol v1):
|
|
77
|
+
|
|
78
|
+
- **Native settings page** (Settings › Plugins › Sub2API Sync): every field applies instantly (volatile) with bilingual descriptions; the engine-maintained `routeKeyIds` map is hidden natively and advanced groups (retry policy and the like) start collapsed; the **panel password** is a credential slot (`DSH_SUB2API_PANEL_PASSWORD`) — set it from the phone and both the web card and the native panel sign in without retyping (the per-instance ref wins; this slot is the fallback);
|
|
79
|
+
- **Native surfaces**: a status strip above the input (routes · last sync) and a **Sync status** panel (chat details › Plugins tab): sign-in state, instance, route list, and **Sync now / Sign in / Sign out** actions; long actions run in the background and refresh on settle, failures show in the panel's callout. Requires the dsh-go bridge on the host (bundled with dsh-Go's local engine; remote hosts usually lack it, and nothing shows over DSH Remote);
|
|
80
|
+
- **Web island**: the full settings card works in dsh-Go's web mode. Over DSH Remote the plugin's action routes are not relayed — the card shows an "action routes unreachable" banner; sign in and sync from the desktop DSH Web instead;
|
|
81
|
+
- Two-factor (TOTP) accounts finish only the first leg natively; complete the second leg in the web settings card.
|
|
82
|
+
|
|
83
|
+
## Security
|
|
108
84
|
|
|
109
|
-
|
|
85
|
+
- All secrets (panel tokens, passwords, relay keys) live only in the harness credential store (deterministic refs `DSH_SUB2API_TOKEN_<instance hash>`, `DSH_SUB2API_KEY_<key-id>`, `DSH_SUB2API_PASSWORD_<instance hash>`, and the dsh-Go native slot `DSH_SUB2API_PANEL_PASSWORD`); never in settings, logs, or sessions;
|
|
86
|
+
- `GET /keys` returns only key metadata (id/name/group/status), **never plaintext**; plaintext is used by the engine to resolve that key and write it into the credential store immediately;
|
|
87
|
+
- Host action routes accept loopback same-origin requests only; over cross-device web access the configuration is read-only (platform `memory` mode) and actions require `allowRemoteActions` in `cordis.yml` (off by default).
|
|
110
88
|
|
|
111
89
|
## What a sync does
|
|
112
90
|
|
|
113
|
-
For every
|
|
91
|
+
For every ticked key, the engine (idempotent diff + single-flight + optimistic concurrency):
|
|
114
92
|
|
|
115
|
-
1.
|
|
116
|
-
2.
|
|
117
|
-
3.
|
|
118
|
-
4. writes
|
|
93
|
+
1. resolves the key's plaintext (from the panel list when it returns it, otherwise the credential store: the new-style `DSH_SUB2API_KEY_<10-hex>` ref first, then the legacy `DSH_SUB2API_KEY_<key-id>` migration fallback, finally the OAuth reveal endpoint), keeping the credential consistent with the panel;
|
|
94
|
+
2. calls `GET {instance}/v1/models` with that key (falling back to `modelListOverrides` on failure);
|
|
95
|
+
3. maps the **owning group's** `platform` onto a wire protocol and derives the entry `baseURL` (the Anthropic SDK gets the bare host, OpenAI-family SDKs get the `/v1` root);
|
|
96
|
+
4. writes `providers.sub2api-<10-hex>` through a targeted `settings.mutate` carrying `expectedRevision`, with a lenient retry policy (5 retries / 120 s backoff cap).
|
|
119
97
|
|
|
120
|
-
|
|
98
|
+
The route suffix is a **10-hex-digit truncation of SHA-256(instance URL, key id)** — stable, collision-free across instances, and **deliberately free of the panel's sequential key id**: the route name leaks nothing about how many keys the site has issued, and a shared config screenshot reveals nothing either. Entries only ever cover the `sub2api-<10-hex>` (current) and `sub2api-<digits>` (legacy, migrated away on the next sync) shapes (**one key, one provider**; the display name is the key name rendered from `providerNameTemplate`). Keys that disappear from the selection lose their entry and credential; the remote key is only revoked when `deleteOrphanKeys` is enabled (the key id is reverse-mapped from the panel key list; legacy numeric routes carry their own). Sign-out, definitive auth loss, and instance switches **purge the plugin's sync state site-wide** — owned routes, relay-key credentials, and the **last sync report** go together (a report describing state that no longer exists must not keep rendering).
|
|
121
99
|
|
|
122
100
|
## Model Experience
|
|
123
101
|
|
|
124
|
-
-
|
|
125
|
-
- **
|
|
126
|
-
- **
|
|
127
|
-
- **
|
|
128
|
-
-
|
|
102
|
+
- One provider per group, one relay key each; the model catalog comes from instance discovery; per-model `compat` inherits the user template; reasoning-effort templates win when explicitly set, otherwise they auto-fill per model (see below);
|
|
103
|
+
- **Model metadata enrichment (C4)**: at sync time, per-model facts are filled from the models.dev directory — **display name** (`name`, the model-catalog editor's display-name column), `contextWindow`, `maxTokens`, image input, reasoning efforts. With `enrichModelMeta` on (default) it fetches over the network (24 h in-process cache), falling back to the **bundled snapshot** (`data/models-dev.snapshot.json`, regenerate with `pnpm --filter @mangoc/dsh-sub2api-sync models:refresh`) on failure; off, only the bundled snapshot is used (offline-deterministic). Models hit by neither keep the route-level defaults 128000/8192/text-only;
|
|
104
|
+
- **Reasoning-effort auto-fill**: the models.dev entry's `reasoning_options` effort enumeration maps onto pi-ai's `reasoningEfforts` (`none` → `off: null`, same-name passthrough otherwise). Merging is **first-party first**: a vendor-official entry (openai/anthropic/google/zhipuai/zai/deepseek/moonshotai/xai/alibaba and vendor-governed cloud hosting) wins outright — official entries without effort data (budget-only older Claude/Gemini) likewise silence aggregator-invented vocabularies; with no first-party entry, aggregator sources intersect. Priority: **explicit template setting (including `false`) > per-model auto-fill > field left unset**; the display name is the key name rendered from `providerNameTemplate`.
|
|
105
|
+
- **Native Claude reasoning efforts**: for confirmed adaptive-thinking Claude models with catalog effort metadata (Opus 4.6/4.7/4.8, Sonnet 4.6 and the like), syncing over `anthropic-messages` also writes per-model `compat.forceAdaptiveThinking: true`. The switch makes the SDK send Claude's native `thinking: { type: "adaptive" }` and `output_config: { effort: "max" }` (Max selected as the example) — no OpenAI protocol bridge, no Max-to-token-budget downgrade. User-template compat values of the same name (including explicit `false`) win; disabled reasoning, legacy models, unknown aliases/new versions, missing catalog effort metadata, and other protocols never auto-enable it. Update the plugin and sync again to repair existing managed routes; repeated syncs write nothing twice; no sub2api record values change. Note: the usage log's effort is the request's declared effort, not a measurement of actual thinking.
|
|
106
|
+
- **Display-name enrichment**: the models.dev `name` lands on the pi-ai model entry's `name` field (what the model selector and the "customized model catalog" editor display); the models.dev " (latest)" family marker is stripped, names identical to the model id (no information) are not written, merging is first-party first (official names override, aggregator names fill absences), and name-only entries join the enrichment too.
|
|
129
107
|
|
|
130
108
|
## Known Limitations and Deferred Work
|
|
131
109
|
|
|
132
|
-
- **
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
110
|
+
- **Masked panel plaintext**: if your panel account cannot see key plaintext (non-admin), sync fails and asks you to sign in with an admin account — a deliberate non-silent degradation.
|
|
111
|
+
- **No invariant surface**: this package publishes no `./invariant` (the sync is a settings write, verifiable by reading back from the same service).
|
|
112
|
+
- **Web authorization (C2) awaits sub2api's OAuth endpoint deployment** (design note §12); until then the UI hides the channel automatically.
|
|
113
|
+
- **dsh version floor: 0.2.0-rc.2** (the client half depends on the `configForms`/locale services; 0.1.x-era apps fail loudly).
|
|
114
|
+
- **The client half allows baseline external modules only** (React / Cordis / ui-primitives / ui-slots / client-store / ui-dockkit) — client code must not value-import `config.ts` (it would drag schemastery into the browser bundle; the dynamic module table has no such row and loading fails silently); `test/client-bundle.test.ts` guards the artifact.
|
|
115
|
+
- Behind Cloudflare, the zone must allow the host-side calls to `/api/v1/*` (a WAF skip rule).
|
|
116
|
+
- Metadata enrichment sends one public catalog request to models.dev (no credentials, no conversation content); `enrichModelMeta: false` goes fully offline (bundled snapshot only).
|
|
117
|
+
- Reasoning-effort auto-fill covers only models whose models.dev entry provides an `effort` enumeration (OpenAI gpt-5 line/o3, GLM 5.x, Qwen3.8, DeepSeek V4 line, and the like); toggle/`budget_tokens`-style reasoning (older Claude, Gemini, deepseek-v3.2, kimi-k2.x) has no effort-string wire shape, `reasoningEfforts` cannot express it, and the template/`compat` stays in charge.
|
|
118
|
+
- Usage lookup (`GET /v1/usage`) is a decided non-goal.
|
|
119
|
+
- Coexistence with the community plugin `GodD6366/dsh-sub2api`: route names do not collide (this plugin's `sub2api-<10-hex>` digest shape), and each plugin manages its own entries and credential refs.
|
|
120
|
+
- **The panel API is a private frontend contract** with no stability promise; responses are validated for required fields and fail loudly with context.
|
|
121
|
+
- **Requires a dsh release where `dsh-llm-pi-ai` attaches zero usage to replayed assistant messages** (the multi-turn pi-ai crash is fixed on current master / the 0.1.2-rc.1 line).
|
|
122
|
+
- **REAL composition, disposal, and snapshot tests against a live harness composition are pending**; current verification is unit tests plus a live smoke test.
|
|
141
123
|
|
|
142
124
|
## Development
|
|
143
125
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- **登录通道**:面板密码+2FA(C3)已交付;网页授权(C2,OAuth 授权码 + PKCE)待 sub2api 侧部署后接入。PAT(C1)已随本插件移除——sub2api 上游从未部署对应端点,该通道无令牌来源;
|
|
10
10
|
- **Keys 驱动同步**:Keys 卡列出面板已有 relay key(缺省全不选)→ **勾选多把**(可跨分组)→「同步所选」→ **一把 key 一个供应商**(路由 `sub2api-<10-hex>`,不含顺序 id)→ 模型下一请求即可用;需要新 key 用「创建 key」弹窗(默认与分组同名);
|
|
11
11
|
- **供应商命名**:`providerNameTemplate` 渲染,缺省 `{siteName}({keyName})`——即 `<站点名称>(<key 名>)`,站点名称来自实例探测;
|
|
12
|
-
- **UI**:设置页顶级卡片(`settings.section`)+ 插件页调参卡(`plugins.bundle.config`),样式对齐 `ui-settings-account`,文案全双语(zh/en);
|
|
12
|
+
- **UI**:设置页顶级卡片(`settings.section`)+ 插件详情页连接卡与同步报告(`plugins.detail.section`)+ 插件页调参卡(`plugins.bundle.config`),样式对齐 `ui-settings-account`,文案全双语(zh/en);
|
|
13
13
|
- **数据缝**:配置读写走平台 `configForms`(字段级、revision 围栏、schema 校验);宿主路由只承载动作/秘密/状态投影;
|
|
14
14
|
- 指纹路由(旧 Mode B)与分组驱动同步(rev.5)均已退役——同步锚定真实面板 key id。
|
|
15
15
|
|
|
@@ -66,14 +66,23 @@ dsh plugin --profile web add ./packages/llm/sub2api-sync
|
|
|
66
66
|
|
|
67
67
|
## 使用
|
|
68
68
|
|
|
69
|
-
1.
|
|
69
|
+
1. 点「连接面板」(设置卡或插件详情页的连接卡均可进入),在弹窗里填**实例地址**(如 `https://cn.api.nexarelay.com`,可带 `/v1`)+ 凭据(面板邮箱/密码,TOTP 账号按提示输入一次性验证码;密码会存入凭据存储,之后免输入);
|
|
70
70
|
2. **Keys 卡**列出该实例已有的 relay key(最新创建在前,首屏 5 条,底部「加载更多」翻页)。每一行前面的勾选框 = 选中用于同步;右上角「**创建 key**」弹窗可新建:选分组 → 名称默认与分组同名 → 创建即自动勾选;
|
|
71
71
|
3. 勾选好要用的 key(可多选、可跨分组),点「**同步所选(N)**」——引擎为**每一把勾选的 key 建一个供应商**,各自发现模型、按所属分组平台映射协议(anthropic → anthropic-messages,OpenAI 家族 → openai-responses,其余 → openai-completions,可按分组覆盖),写入 `llm-pi-ai.providers`;
|
|
72
72
|
4. 模型选择器下一请求即可用。主卡下方的「同步」块也能触发一次全量同步并查看完整报告;报告的每个条目带「**移除**」按钮——只删本地路由与凭据(**不调面板**,余额不足也可用),并自动取消该 key 的勾选,下次同步不会复活;可选高级参数在 **插件页 → Sub2API Sync** 调参卡。
|
|
73
73
|
|
|
74
|
+
## dsh-Go(Android)适配
|
|
75
|
+
|
|
76
|
+
插件适配 [DSH Go](https://github.com/ccch1mneyyy/dsh-Go)(社区 Android 客户端)的原生设置能力(原生协议 v1):
|
|
77
|
+
|
|
78
|
+
- **原生设置页**(设置 › 插件 › Sub2API Sync):全部字段即改即生效(volatile),附中英文说明;引擎内部字段(`routeKeyIds`)在原生页隐藏,重试策略等高级组默认折叠;**面板密码**是凭据槽字段(`DSH_SUB2API_PANEL_PASSWORD`)——手机上即可写入凭据库,之后网页卡与原生面板登录都免输密码(实例派生引用优先,本槽兜底);
|
|
79
|
+
- **原生界面**:输入框上方状态条(路由数 · 上次同步)与「同步状态」面板(对话详情 › 插件页签):登录状态、实例、路由列表、**立即同步 / 登录 / 退出**动作;长动作后台执行、完成即刷新,失败原因显示在面板顶部提示条。需要主机带 dsh-go 桥接(dsh-Go 本机引擎自带;远程主机一般没有,经 DSH Remote 不显示);
|
|
80
|
+
- **网页岛**:完整设置卡在 dsh-Go 的网页版可用;经 DSH Remote 遥控电脑时动作路由不随中继转发——卡片顶部会出现「动作路由不可达」警示条,登录与同步请回到电脑上的 DSH Web;
|
|
81
|
+
- 两步验证(TOTP)账号的原生登录只完成第一段,第二段请到网页版设置卡完成。
|
|
82
|
+
|
|
74
83
|
## 安全
|
|
75
84
|
|
|
76
|
-
- 一切秘密(面板 token、密码、各 relay key)只进 harness 凭据存储(确定性引用 `DSH_SUB2API_TOKEN_<实例哈希>`、`DSH_SUB2API_KEY_<key-id>`、`DSH_SUB2API_PASSWORD_
|
|
85
|
+
- 一切秘密(面板 token、密码、各 relay key)只进 harness 凭据存储(确定性引用 `DSH_SUB2API_TOKEN_<实例哈希>`、`DSH_SUB2API_KEY_<key-id>`、`DSH_SUB2API_PASSWORD_<实例哈希>`、dsh-Go 原生设置槽 `DSH_SUB2API_PANEL_PASSWORD`),不进 settings、不进日志、不进会话;
|
|
77
86
|
- `GET /keys` 只回 key 元数据(id/名称/分组/状态),**不回明文**;明文只用于引擎解析该 key 并即时写入凭据存储;
|
|
78
87
|
- 宿主动作路由只接受回环同源请求;跨设备访问 web UI 时配置只读(平台 `memory` 模式),动作需在 `cordis.yml` 显式开启 `allowRemoteActions`(默认关闭)。
|
|
79
88
|
|
|
@@ -108,6 +117,9 @@ dsh plugin --profile web add ./packages/llm/sub2api-sync
|
|
|
108
117
|
- 推理档位自动填充只覆盖 models.dev 提供 `effort` 枚举的模型(OpenAI gpt-5 系/o3、GLM 5.x、Qwen3.8、DeepSeek V4 系等);`toggle`/`budget_tokens` 型推理(旧版 Claude、Gemini、deepseek-v3.2、kimi-k2.x 等)的线上形态不是 effort 字符串,`reasoningEfforts` 无法表达,仍由模板/`compat` 管辖。
|
|
109
118
|
- 用量查询(`GET /v1/usage`)是已决策的非目标。
|
|
110
119
|
- 与社区插件 `GodD6366/dsh-sub2api` 同装:路由名不冲突(本插件 `sub2api-<10-hex>` 摘要形态),各自管理条目与凭据引用。
|
|
120
|
+
- **面板 API 是私有前端契约**:无稳定性承诺;响应按必需字段校验,缺失即带上下文响亮失败。
|
|
121
|
+
- **需要 `dsh-llm-pi-ai` 对重放的 assistant 消息计零用量**的 dsh 版本(多轮 pi-ai 崩溃已在当前 master / 0.1.2-rc.1 线修复)。
|
|
122
|
+
- **针对真实 harness 组合的 REAL 组合/销毁/快照测试待补**:当前以单元测试 + 真机冒烟验证。
|
|
111
123
|
|
|
112
124
|
## 开发
|
|
113
125
|
|