@wengine-ai/ccr-provider-qoder 0.0.0-stage → 0.1.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.md +54 -2
- package/package.json +30 -4
package/README.md
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wengine-ai/ccr-provider-qoder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Qoder subscription provider plugin for [claude-code-router](https://github.com/wengine-ai/claude-code-router-next), covering both Qoder sites: **qoder.com** (international) and **qoder.cn** (Qoder CN).
|
|
4
|
+
|
|
5
|
+
## What it provides
|
|
6
|
+
|
|
7
|
+
Everything below exists once per site; the two sites are independent (own account, own credentials, own models, own quota):
|
|
8
|
+
|
|
9
|
+
| | international | Qoder CN |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| managed provider | `qoder-subscription` | `qoder-cn-subscription` |
|
|
12
|
+
| marker credential | `ccr-managed-qoder` | `ccr-managed-qoder-cn` |
|
|
13
|
+
| credential file | `qoder-credentials.json` | `qoder-cn-credentials.json` |
|
|
14
|
+
| fixed chat upstream | `api3.qoder.sh` | `gateway.qoder.com.cn` |
|
|
15
|
+
| admin routes | `/api/qoder/*` | `/api/qoder/cn/*` |
|
|
16
|
+
| transformer | `qoder` | `qoder-cn` |
|
|
17
|
+
|
|
18
|
+
- **Transformer** (`qoder` / `qoder-cn`): unified → chat-completions request conversion (leading system message, thinking → `reasoning_content`, stable session id → `prompt_cache_key`, reasoning effort passthrough) and strict SSE/JSON response validation (error-frame classification, `[DONE]` integrity, usage mapping). The conversion is site-independent; each managed provider locks its own transformer name.
|
|
19
|
+
- **Managed runtime** (`qoder` / `qoder-cn`): pins that site's fixed chat upstream, routes through the host's subscription dispatcher, owns abort/deadline wiring and the error taxonomy (`qoder_reauth_required`, `qoder_rate_limited` with `retry_after`-derived cooldown, `qoder_upstream_error`). A runtime claims only its own managed provider.
|
|
20
|
+
- **Auth service**: PAT sign-in with the job-token engine, per-site credential file (0600, atomic, size-capped), model sync/visibility, usage reads with a 60s cache, cooling circuit breaker and reconnect.
|
|
21
|
+
- **Admin routes** (loopback-only, per site): status, connect, sync, usage, model visibility, disconnect.
|
|
22
|
+
- **One config protector**: re-locks BOTH managed providers in a single pass and re-adds any of them that vanish from the config.
|
|
23
|
+
- **UI card + locales** (en/zh): one card with two independent sections (Qoder / Qoder CN) — PAT connect form, status, quota windows, model management.
|
|
24
|
+
|
|
25
|
+
## PAT setup
|
|
26
|
+
|
|
27
|
+
For the international site:
|
|
28
|
+
|
|
29
|
+
1. Sign in at [qoder.com](https://qoder.com) and open **Account → Integrations** (`qoder.com/account/integrations`).
|
|
30
|
+
2. Create a personal access token (starts with `pt-`).
|
|
31
|
+
|
|
32
|
+
For Qoder CN, do the same on [qoder.cn](https://qoder.cn) (the token exchange runs against `openapi.qoder.com.cn`).
|
|
33
|
+
|
|
34
|
+
Then, in the CCR settings page, open the Qoder card and use the matching section (**Qoder** or **Qoder CN**):
|
|
35
|
+
|
|
36
|
+
3. Paste the token and press **Connect**. The token is kept only in that site's private credential file and is cleared from the form immediately.
|
|
37
|
+
4. Press **Refresh models** to sync the model list.
|
|
38
|
+
|
|
39
|
+
Both sections can be connected at the same time; disconnecting one never touches the other.
|
|
40
|
+
|
|
41
|
+
## Scope limits
|
|
42
|
+
|
|
43
|
+
- Remote origins are fixed per site: `openapi.qoder.sh` / `api3.qoder.sh` for qoder.com and `openapi.qoder.com.cn` / `gateway.qoder.com.cn` for qoder.cn. No device flow, no campaigns/check-in, no VPC, no QwenWork.
|
|
44
|
+
- Sign-in is PAT-only; the job token is refreshed from its refresh token when present, and a lapsed/absent refresh falls back to PAT re-exchange (a refused PAT marks that site's account expired until you reconnect). The PAT is kept in the plugin's private 0600 credential file.
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
pnpm install # standalone package deps
|
|
50
|
+
pnpm vitest run # fully offline tests (all fetch mocked/injected)
|
|
51
|
+
pnpm tsc --noEmit
|
|
52
|
+
pnpm build # produces dist/ (self-contained plugin + UI card bundles)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Tests never touch the network: every engine is injected via `options.engine`, every fetch is mocked.
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wengine-ai/ccr-provider-qoder",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Qoder subscription provider (qoder.com and qoder.cn): Personal Access Token sign-in, fixed agent_chat_generation upstream, model management and usage.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/plugin.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsx scripts/build.ts",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"test:watch": "vitest"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude-code-router",
|
|
14
|
+
"qoder",
|
|
15
|
+
"provider-plugin",
|
|
16
|
+
"ccr-provider-plugin"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^24.0.15",
|
|
21
|
+
"esbuild": "^0.25.1",
|
|
22
|
+
"fastify": "^5.4.0",
|
|
23
|
+
"tsx": "^4.20.3",
|
|
24
|
+
"typescript": "^5.8.2",
|
|
25
|
+
"vitest": "^4.1.9"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/**/*.js",
|
|
29
|
+
"dist/*.json",
|
|
30
|
+
"dist/*.md"
|
|
31
|
+
]
|
|
32
|
+
}
|