dsh-agy-link 0.3.5 → 0.4.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.
@@ -0,0 +1,54 @@
1
+ # ADR-012: Multi-Account Pool & Sequential Drain Architecture
2
+
3
+ ## Status
4
+ Proposed (2026-08-20)
5
+
6
+ ## Context
7
+ Google Antigravity (via agy CLI) provides access to Gemini, Claude (Sonnet/Opus), and GPT-OSS models with generous rate limits. However, heavy agentic workflows (large code refactors, multi-step tool loops, reasoning-heavy turns) can hit per-model rate limits (`429 Too Many Requests`, `RESOURCE_EXHAUSTED`, or upstream capacity warnings).
8
+
9
+ Single-account users face blocking interruptions when an account hits quota. Users with multiple Google accounts (e.g. A, B, C) want to pool their accounts so that when Account A's quota for a model family is exhausted, the system automatically falls back to Account B, then Account C, without manual switching or disrupting active conversations.
10
+
11
+ ### Architectural Approaches Considered
12
+
13
+ 1. **Reverse-Engineering HTTP/SSE Protocol (The `chaos-03x/dsh-agy` approach)**:
14
+ - *Pros*: Pure TypeScript HTTP requests, sub-millisecond account switching in memory.
15
+ - *Cons*: High ban/risk profile (needs manual fingerprint spoofing), brittle Tool Calling schema translation (frequent 400 Bad Request crashes on Protobuf / JSON-Schema mismatch, as seen in dsh-agy issues #1, #2, #4), loss of agy's 50+ native tools and subagent execution sandbox.
16
+
17
+ 2. **Multi-Profile agy CLI Process Isolation (Our chosen approach)**:
18
+ - *Pros*: 100% official Google agy binary execution (zero account ban risk, zero schema translation issues, full native tool execution); clean physical process-level credential isolation via `HOME=~/.dsh/agy-accounts/<id>/`; optional per-account proxy via `ALL_PROXY` injection.
19
+ - *Cons*: Light process spawn overhead (~50-100ms), requires directory management.
20
+
21
+ ## Decisions
22
+
23
+ ### 1. Multi-Profile Storage Structure
24
+ Each managed account has an isolated home directory under the DSH state directory:
25
+ ```
26
+ ~/.dsh/agy-accounts/
27
+ ├── accounts.json # Metadata index (accounts, proxies, aliases, active order)
28
+ ├── acc_a1b2c3/ # Account A isolated environment
29
+ │ └── .gemini/
30
+ │ └── antigravity-cli/ # agy CLI credentials, tokens, settings for Account A
31
+ ├── acc_d4e5f6/ # Account B isolated environment
32
+ │ └── .gemini/
33
+ │ └── antigravity-cli/
34
+ ```
35
+ Spawning agy for Account $X$ simply injects `HOME=/path/to/~/.dsh/agy-accounts/acc_X/` into `env`. No Docker containers or root privileges required.
36
+
37
+ ### 2. Sticky Sequential Drain Strategy (按模型家族顺次耗尽)
38
+ - **Family-Scoped Cooldown**: Quota is tracked per model family (`google`, `anthropic`, `openai`). If Account A hits 429 on Claude 4.6, only Account A's `anthropic` family is marked in cooldown. Account A can still serve Gemini 3.7 Flash requests.
39
+ - **Sticky Affinity**: Requests stick to the first healthy account (e.g., Account A) to maximize conversation continuity and token caching.
40
+ - **Transparent Fallback**: When an in-flight request on Account A receives 429 / quota exceeded, the adapter records cooldown on Account A for that family and immediately transparently retries on Account B in the same streaming span. The user experiences zero interruption.
41
+
42
+ ### 3. Cooldown & Reset Handling
43
+ - If the error contains a server reset time (or `Retry-After`), cooldown is set to `Date.now() + resetMs`.
44
+ - Default cooldown window is 10 minutes (tiered up to 60 minutes on consecutive 429s).
45
+ - Cooldown expires automatically; when Account A recovers, it resumes primary position.
46
+
47
+ ### 4. Optional Per-Account Proxy (防关联支持)
48
+ Each account can optionally specify a dedicated proxy URL (`socks5://...` or `http://...`). When set, `ALL_PROXY` / `HTTPS_PROXY` / `HTTP_PROXY` are scoped exclusively to that account's child process. If unconfigured, it inherits the global host environment proxy.
49
+
50
+ ## Consequences
51
+
52
+ - **Reliability**: No 400 schema crashes, full native tools support.
53
+ - **Smooth UX**: Multiple accounts can be registered via Web QR codes or CLI, providing 3x-5x continuous quota.
54
+ - **Safety**: Safe for single-user multiple accounts without requiring heavy containerization.
@@ -1,41 +1,51 @@
1
- # Market PR draft (awesome-dsh-plugin)
1
+ # Market PR draft (awesome-dsh-plugin & dsh-market)
2
2
 
3
- Target: awesome-dsh-plugin repository, `data/plugins/<owner>__dsh-agy-link.yml`.
4
- Replace `<owner>` with the GitHub owner chosen at publish time. File the PR
5
- after: npm published, GitHub repo created, >= 10 commits, >= 1 day old,
6
- topic `dsh-plugin` set.
3
+ Target: [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) repository.
4
+ File path: `data/plugins/amlyczz__dsh-agy-link.yml`
5
+
6
+ > **Note**: Submitting to `awesome-dsh-plugin` automatically indexes the plugin into `dsh-market` (the in-app GUI plugin market inside DeepSeek Harness), `awesome-dsh-plugin.com`, DSH Desktop, and DSH Get!
7
7
 
8
8
  ---
9
9
 
10
+ ### `data/plugins/amlyczz__dsh-agy-link.yml`
11
+
10
12
  ```yaml
11
- name: dsh-agy-link
12
- description: Google Antigravity (agy CLI) models for DSH — streaming chat, thinking, tool activity, usage, in-GUI Google OAuth login.
13
- npm: dsh-agy-link
14
- repository: https://github.com/<owner>/dsh-agy-link
13
+ url: https://github.com/amlyczz/dsh-agy-link
14
+ name: amlyczz/dsh-agy-link
15
15
  category: model
16
- tags:
17
- - antigravity
18
- - gemini
19
- - agy
20
- - model-provider
16
+ description:
17
+ en: 'Google Antigravity (agy CLI) models for DSH — streaming chat with Gemini/Claude/GPT-OSS subscriptions, native tool cards, thinking turns, and in-GUI Google OAuth login.'
18
+ zh: '将 Google Antigravity (agy CLI) 接入 DSH:无 API Key 使用 Gemini/Claude/GPT-OSS 订阅模型,支持流式对话、原生工具卡片、思考轮次注记及 Web 界面 Google OAuth 扫码登录。'
21
19
  ```
22
20
 
23
- PR body draft:
21
+ ---
22
+
23
+ ### PR Title & Body Draft
24
+
25
+ **Title**: `Add amlyczz/dsh-agy-link (model)`
26
+
27
+ **Body**:
24
28
 
25
29
  ```markdown
26
- ## Add dsh-agy-link (model)
30
+ ## Add amlyczz/dsh-agy-link (model)
27
31
 
32
+ - **Repo**: https://github.com/amlyczz/dsh-agy-link
28
33
  - **npm**: https://www.npmjs.com/package/dsh-agy-link
29
- - **repo**: https://github.com/<owner>/dsh-agy-link
30
- - **category**: model
31
-
32
- Brings Google Antigravity models (Gemini / Claude / GPT-OSS slugs exposed by
33
- the agy CLI) into DSH as a first-class provider route: full streaming with
34
- thinking and tool-activity annotation, token usage, session-conversation
35
- binding, model discovery with effort folding, in-GUI Google OAuth login with
36
- QR, an agy_ask delegation tool, and a /agy command family with a redacted
37
- doctor export. Dormant-safe when agy is missing or signed out.
38
-
39
- Checklist: dsh bundle manifest present; >= 10 commits; repo age >= 1 day;
40
- `dsh-plugin` topic set on the repository.
34
+ - **Category**: `model`
35
+
36
+ Brings Google Antigravity models (Gemini / Claude / GPT-OSS slugs exposed by the agy CLI) into DSH as a first-class provider route:
37
+ - Full streaming responses with thinking turns and token usage.
38
+ - Native DSH tool cards (terminal, diff, view, search) mirrored via `agy_tool`.
39
+ - Multi-turn continuity with session-conversation binding and continuation spans.
40
+ - In-GUI Google OAuth login with QR code & auth helper.
41
+ - Sliding activity watchdog supporting indefinite execution for active long tasks.
42
+ - `/agy` command suite (`status`, `auth`, `doctor`, `ask`).
43
+
44
+ ### Pre-submission Checklist
45
+ - [x] `dsh.bundle` manifest present in `package.json`
46
+ - [x] >= 10 commits (currently 40+)
47
+ - [x] Repository age >= 1 day
48
+ - [x] `dsh-plugin` topic set on GitHub repository
49
+ - [x] Published and verified on npm (`dsh-agy-link`)
41
50
  ```
51
+
@@ -0,0 +1,229 @@
1
+ # Comprehensive Specification: Multi-Account Pool, Quota Statistics & Sequential Drain
2
+
3
+ > **Document Type**: Technical Specification & Architecture Design
4
+ > **Status**: Approved for Implementation
5
+ > **Scope**: Host Backend, Account Engine, Quota Statistics, Transparent Retry Pipeline, DSH Attached WebUI, CLI Family
6
+
7
+ ---
8
+
9
+ ## 1. Executive Summary & Core Tenets
10
+
11
+ ### 1.1 Background & Motivation
12
+ In agentic coding workflows using Google Antigravity models (Gemini 3.7 Flash, Claude 4.6 Sonnet/Opus, GPT-OSS 120B), heavy usage can hit rate limits (`429 Too Many Requests`, `RESOURCE_EXHAUSTED`, or upstream capacity warnings).
13
+
14
+ This specification details a **Multi-Account Pool (号池系统)** that enables pooling multiple Google accounts (e.g. Accounts A, B, C) with **Live Quota Statistics (实时配额进度条)**, **Sticky Sequential Drain (按模型家族顺次耗尽)**, **Zero-Interruption In-Flight Fallback (零感知自动重试)**, and full integration into the **DSH Built-in Settings WebUI**.
15
+
16
+ ### 1.2 Core Architectural Principles
17
+ 1. **Zero Ban Risk via Official CLI Execution**: Execution runs through the official Google `agy` binary (`agy -p ...`). No reverse-engineered chat streaming, no fragile Protobuf schema translation, no spoofed fingerprinting. Full access to agy's 50+ native Agent tools.
18
+ 2. **Multi-Profile Directory Isolation (Verified)**: Local process isolation via dedicated `HOME` directories (`~/.dsh/agy-accounts/<id>/`). Zero Docker containers or background daemons. Verified to boot isolated agy processes in ~50ms.
19
+ 3. **Live Quota Inspection (实时额度统计)**: Direct upstream query of `v1internal:fetchAvailableModels` to extract `remainingFraction` (0.0~1.0) and `resetTime` per model family (`google`, `anthropic`, `openai`), visually rendered as colorful progress bars in the DSH settings panel.
20
+ 4. **Zero-Config Proxy Inheritance (零配置开箱即用)**: All accounts default to inheriting the active system/terminal proxy (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`) from DSH. Single-proxy-port users (e.g. Clash on 7890) can use 3+ accounts seamlessly without configuring proxy settings.
21
+ 5. **Family-Scoped Precise Cooldown (按模型家族精准冷却)**: Cooldowns are tracked per model family (`google`, `anthropic`, `openai`). A 429 on Claude only cools down Claude; Gemini requests remain active on that same account. Cooldown duration leverages the server's exact `resetTime`.
22
+ 6. **Sticky Sequential Drain**: Always prioritize Account A until a specific model family is exhausted, then fail over to Account B, then C. Minimizes context thrashing and maximizes session token cache hits.
23
+ 7. **Native DSH WebUI Attachment**: Fully integrated into the native DSH Web interface via `settings.section` slot — zero external ports or separate web servers needed.
24
+
25
+ ---
26
+
27
+ ## 2. Multi-Profile Isolation & OAuth Flow (Verified)
28
+
29
+ ### 2.1 Directory Structure
30
+ ```
31
+ ~/.dsh/agy-accounts/
32
+ ├── pool.json # Account registry, active mode, and cached quota metadata
33
+ ├── acc_1700000000_a1/ # Account A (e.g. work@gmail.com)
34
+ │ └── .gemini/
35
+ │ └── antigravity-cli/
36
+ │ └── antigravity-oauth-token
37
+ ├── acc_1700000000_b2/ # Account B (e.g. personal@gmail.com)
38
+ │ └── .gemini/
39
+ │ └── antigravity-cli/
40
+ │ └── antigravity-oauth-token
41
+ └── acc_1700000000_c3/ # Account C (e.g. backup@gmail.com)
42
+ └── .gemini/
43
+ └── antigravity-cli/
44
+ └── antigravity-oauth-token
45
+ ```
46
+
47
+ ### 2.2 Verified Add-Account OAuth Workflow
48
+ ```
49
+ [ User clicks: "➕ 添加新 Google 账号" ]
50
+ │
51
+ ▼
52
+ 1. Backend allocates directory: ~/.dsh/agy-accounts/acc_<id>/
53
+ │
54
+ ▼
55
+ 2. Generates Google OAuth PKCE authorization URL:
56
+ - Client ID: AGY_PUBLIC_CLIENT_ID (Antigravity Consumer Public OAuth Client)
57
+ - Scopes: cloud-platform, userinfo.email, userinfo.profile, cclog, experimentsandconfigs
58
+ │
59
+ ▼
60
+ 3. WebUI renders:
61
+ - 👉 Button: Open authorization page in browser (works through local proxy)
62
+ - 📱 Inline Base64 QR code
63
+ - 📋 Authorization code input field
64
+ │
65
+ ▼
66
+ [ User authorizes & pastes code back ]
67
+ │
68
+ ▼
69
+ 4. Backend exchanges code at https://oauth2.googleapis.com/token:
70
+ - Receives: { access_token, refresh_token, expires_in }
71
+ - Resolves email via https://www.googleapis.com/oauth2/v1/userinfo
72
+ - Fetches live quota stats via v1internal:fetchAvailableModels
73
+ │
74
+ ▼
75
+ 5. Writes token into ~/.dsh/agy-accounts/acc_<id>/.gemini/antigravity-cli/antigravity-oauth-token
76
+ and registers account in pool.json.
77
+ │
78
+ ▼
79
+ 6. Account is live and immediately ready for use!
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 3. Real-Time Quota Statistics (实时配额统计)
85
+
86
+ ### 3.1 Quota API Endpoint & Payload
87
+ - **Endpoint**: `https://daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels` (Fallback: `https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels`)
88
+ - **Headers**: `Authorization: Bearer <access_token>`, `User-Agent: antigravity/1.1.15 darwin/arm64`
89
+ - **Response Format**:
90
+ ```json
91
+ {
92
+ "models": {
93
+ "gemini-3.7-flash": {
94
+ "displayName": "Gemini 3.7 Flash",
95
+ "quotaInfo": {
96
+ "remainingFraction": 0.85,
97
+ "resetTime": "2026-08-20T16:00:00Z"
98
+ }
99
+ },
100
+ "claude-3-7-sonnet": {
101
+ "displayName": "Claude 3.7 Sonnet",
102
+ "quotaInfo": {
103
+ "remainingFraction": 0.40,
104
+ "resetTime": "2026-08-20T18:30:00Z"
105
+ }
106
+ },
107
+ "gpt-oss-120b-medium": {
108
+ "displayName": "GPT-OSS 120B",
109
+ "quotaInfo": {
110
+ "remainingFraction": 1.0,
111
+ "resetTime": "2026-08-21T00:00:00Z"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### 3.2 Family-Scoped Quota Aggregation
119
+ Each model maps to a family:
120
+ - **`google`**: `gemini-*`, `gemma-*`
121
+ - **`anthropic`**: `claude-*`
122
+ - **`openai`**: `gpt-*`, `openai/*`
123
+
124
+ For each family $F$:
125
+ $$\text{remainingFraction}(F) = \min_{m \in F} (\text{model.quotaInfo.remainingFraction})$$
126
+ $$\text{resetTime}(F) = \min_{m \in F} (\text{model.quotaInfo.resetTime})$$
127
+
128
+ ---
129
+
130
+ ## 4. Scheduling & Transparent Fallback Engine
131
+
132
+ ### 4.1 Account Selection Algorithm (Sequential Drain)
133
+
134
+ ```
135
+ [ Request: model = "claude-sonnet-4-6", family = "anthropic" ]
136
+ │
137
+ ▼
138
+ Get candidate accounts in priority order:
139
+ [ Account A, Account B, Account C ]
140
+ │
141
+ Filter: enabled === true
142
+ │
143
+ Filter: cooldowns['anthropic'].cooldownUntil <= Date.now()
144
+ AND remainingFraction['anthropic'] > 0.05
145
+ │
146
+ ┌─────────────┴─────────────┐
147
+ ▼ ▼
148
+ Candidates Available? All in Cooldown?
149
+ │ │
150
+ ├── Yes: Pick first └── No: Throw POOL_EXHAUSTED
151
+ │ (Account A) (with earliest reset countdown)
152
+ ▼
153
+ Spawn agy with Account A (HOME=~/.dsh/agy-accounts/acc_A)
154
+ ```
155
+
156
+ ### 4.2 In-Flight Transparent Fallback (429 Zero-Interruption Retry)
157
+ - When Account A hits 429, `RESOURCE_EXHAUSTED`, or upstream capacity errors:
158
+ 1. Record cooldown on Account A for `anthropic`:
159
+ $$\text{cooldownUntil} = \text{server.resetTime} \parallel (\text{Date.now()} + 600\,000)$$
160
+ 2. Select next healthy account (Account B).
161
+ 3. Re-spawn agy under Account B with the conversation digest prefix.
162
+ 4. Stream chunks directly to the caller. The user experiences zero interruption or error modals.
163
+
164
+ ---
165
+
166
+ ## 5. DSH Attached WebUI Specification
167
+
168
+ Attached via `settings.section` slot under DSH Settings $\rightarrow$ Antigravity.
169
+
170
+ ### 5.1 Visual Mockup with Quota Bars
171
+
172
+ ```
173
+ ┌──────────────────────────────────────────────────────────────────────────┐
174
+ │ 👥 Antigravity Account Pool (Google 多账号池) │
175
+ │ 调度模式: [ 顺次耗尽 (Sequential Drain) ▾ ] | [ 🔄 刷新全部额度 ] │
176
+ │ 提示: 默认共用本地系统/代理环境,账号 A 额度耗尽自动无缝切换到账号 B │
177
+ ├──────────────────────────────────────────────────────────────────────────┤
178
+ │ 1. 🟢 Account A (work@gmail.com) [当前主用] │
179
+ │ 📊 额度余量: │
180
+ │ • Gemini: [████████████████████░░] 85% (重置时间: 16:00) │
181
+ │ • Claude: [████████░░░░░░░░░░░░░░] 40% (重置时间: 18:30) │
182
+ │ • GPT-OSS: [██████████████████████] 100% │
183
+ │ [ 🔍 刷新额度 ] [ ⚙️ 代理设置 ] [ 🔄 重新认证 ] │
184
+ ├──────────────────────────────────────────────────────────────────────────┤
185
+ │ 2. 🟡 Account B (personal@gmail.com) │
186
+ │ 📊 额度余量: │
187
+ │ • Gemini: [██████████████████████] 100% │
188
+ │ • Claude: [░░░░░░░░░░░░░░░░░░░░░░] 0% (已耗尽 · 7分12秒后重置) │
189
+ │ • GPT-OSS: [██████████████████████] 100% │
190
+ │ [ 🔍 刷新额度 ] [ ⚙️ 代理设置 ] [ ⬆️ 设为主用 ] [ 🗑️ 移除 ] │
191
+ ├──────────────────────────────────────────────────────────────────────────┤
192
+ │ 3. ⚪ Account C (backup@gmail.com) │
193
+ │ 📊 额度余量: │
194
+ │ • Gemini: [██████████████████████] 100% │
195
+ │ • Claude: [██████████████████████] 100% │
196
+ │ • GPT-OSS: [██████████████████████] 100% │
197
+ │ [ 🔍 刷新额度 ] [ ⚙️ 代理设置 ] [ ⬆️ 设为主用 ] [ 🗑️ 移除 ] │
198
+ ├──────────────────────────────────────────────────────────────────────────┤
199
+ │ [ ➕ 添加新 Google 账号 (Add Account) ] │
200
+ └──────────────────────────────────────────────────────────────────────────┘
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 6. Host Web Server Routes
206
+
207
+ All host routes are namespaced under `/plugins/agy-link/`:
208
+
209
+ | Method | Endpoint | Description |
210
+ |---|---|---|
211
+ | `GET` | `/plugins/agy-link/pool` | Get all accounts, live quota bars, cooldown timers, mode |
212
+ | `POST` | `/plugins/agy-link/pool/add` | Allocate a new account slot and generate OAuth URL & QR |
213
+ | `POST` | `/plugins/agy-link/pool/auth-code` | Submit OAuth code, exchange tokens, fetch initial quota |
214
+ | `POST` | `/plugins/agy-link/pool/auth-cancel` | Cancel in-progress OAuth for a slot |
215
+ | `POST` | `/plugins/agy-link/pool/refresh-quota`| Refresh real-time quota for one or all accounts |
216
+ | `POST` | `/plugins/agy-link/pool/remove` | Delete an account directory and its records |
217
+ | `POST` | `/plugins/agy-link/pool/reorder` | Update account priority order |
218
+ | `POST` | `/plugins/agy-link/pool/proxy` | Update optional proxy override for an account |
219
+ | `POST` | `/plugins/agy-link/pool/test` | Test connectivity & quota health of an account slot |
220
+
221
+ ---
222
+
223
+ ## 7. CLI Family Commands
224
+
225
+ - `/agy pool` / `/agy accounts`: Print summary table of pooled accounts, live quota %, and family cooldowns.
226
+ - `/agy add-account [alias]`: Start terminal-guided OAuth login for a new account slot.
227
+ - `/agy refresh-quota [id]`: Fetch fresh quota percentages from Google backend.
228
+ - `/agy remove-account <id>`: Delete an account slot.
229
+ - `/agy clear-cooldown [id]`: Manually reset cooldown timers for accounts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.3.5",
3
+ "version": "0.4.9",
4
4
  "description": "Google Antigravity (agy CLI) models for DeepSeek Harness — stream Gemini/Claude/GPT-OSS subscriptions into DSH with thinking, tool activity, token usage and in-GUI Google OAuth login.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,7 +58,8 @@
58
58
  "@types/node": "^24.3.0",
59
59
  "@types/qrcode": "^1.5.5",
60
60
  "tsdown": "^0.22.14",
61
- "typescript": "^5.9.2"
61
+ "typescript": "^5.9.2",
62
+ "undici": "^8.10.0"
62
63
  },
63
64
  "dsh": {
64
65
  "bundle": {