@goodandready/dsh-opencode-provider 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +7 -5
- package/README.ru.md +5 -3
- package/README.zh.md +5 -3
- package/lib/client.js +186 -69
- package/lib/index.js +21 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.4
|
|
4
|
+
|
|
5
|
+
- **Sidebar settings**: the quota-card switch moved out of Connection into its own **Sidebar** section in Settings → OpenCode and into the TUI; saving now refreshes the sidebar card immediately instead of waiting for the 60-second poll.
|
|
6
|
+
- 85 tests.
|
|
7
|
+
|
|
8
|
+
## 0.5.3
|
|
9
|
+
|
|
10
|
+
- **Per-account runway**: the quota panel estimates time to exhaustion for each account from the 5-hour, weekly and monthly burn rate and flags the first window projected to run out before its reset. The estimate is intentionally approximate; only the current percentage and reset time are exposed by the usage endpoint.
|
|
11
|
+
- 84 tests.
|
|
12
|
+
|
|
3
13
|
## 0.5.2
|
|
4
14
|
|
|
5
15
|
- **Quota fallback**: choose a free model in Settings → OpenCode. A Go quota rejection retries the same request once on Console/Zen with that model; failures surface the original quota error.
|
package/README.md
CHANGED
|
@@ -86,6 +86,7 @@ graph LR
|
|
|
86
86
|
- Gemini `thoughtSignature` on historical function calls.
|
|
87
87
|
- **Multi-account rotation** before a stream starts: 401/403/429 and quota/usage-limit bodies (400/402/403/429). An invalid key cools down for 24h, a quota rejection for 10 minutes or `Retry-After`, a rate limit for 60s or `Retry-After`. A request already streaming is never replayed on another account; the next request uses the next usable key.
|
|
88
88
|
- **Per-account quotas**: each account row shows its own 5-hour / weekly / monthly usage, the active account drives the sidebar and quota panel, and the panel lists every account side by side.
|
|
89
|
+
- **Per-account runway**: estimates time to exhaustion from the average burn rate of each window and marks the first window projected to run out before its reset; approximate because the API exposes only the current percentage and reset time.
|
|
89
90
|
- **Model → account rules**: first matching glob pins a preferred account for that model; the other accounts remain fallbacks if it fails or is cooling.
|
|
90
91
|
- **Request heatmap** in the quota panel: a Monday-first weekday × hour grid in local time, plus a 24-hour aggregate strip underneath. Selectors switch between all requests, any single model, and any single account. The adapter appends `{at, provider, model, purpose?, account, reason}` to `~/.dsh/storages/dsh-opencode/requests.jsonl`; no prompt, response or key is stored. Auxiliary `session-title`/`compaction` calls are excluded from the heatmap and exact duplicate records collapse. The log compacts at 2 MB to the newest 5000 lines. Collection starts with this build — the projection cache only has `lastPromptAt`, not per-request timestamps.
|
|
91
92
|
- **Stable `x-opencode-session`** derived from the durable session id, so retries and every turn of a conversation reuse the same routing key.
|
|
@@ -122,12 +123,12 @@ npm pack
|
|
|
122
123
|
Install the **packed tarball** into a profile:
|
|
123
124
|
|
|
124
125
|
```bash
|
|
125
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
126
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
126
127
|
# DSH Desktop:
|
|
127
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
128
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
128
129
|
|
|
129
130
|
# Or install the published package:
|
|
130
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
131
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.4
|
|
131
132
|
```
|
|
132
133
|
|
|
133
134
|
Restart dsh (or DSH Desktop), then pick `OpenCode` or `OpenCode Go` in the model picker.
|
|
@@ -230,11 +231,12 @@ Open **Settings → OpenCode**:
|
|
|
230
231
|
- **Quota** — 5-hour, weekly and monthly bars with reset dates, plus warning banners at 80% and 95%.
|
|
231
232
|
- **Accounts** — add extra accounts by credential reference, set each account's key from the page, see whether it is configured, pin one, or return to automatic rotation. **Import all from auth.json** copies every OpenCode key into its own credential ref and adds an account for it; **Check all accounts** refreshes the per-account quota/health rows.
|
|
232
233
|
- **Models** — search, refresh the live catalog, uncheck models to build an allowlist, override the wire transport per model, and see each model's input/output price plus its Go request estimate (`Go 34.7K/5h`). Hover the estimate for the full Go / Go Plus monthly allowance and 5h/week/month request counts; expand **Subscription limits** for a table of every model with both plans.
|
|
233
|
-
- **Connection** — API bases, usage endpoint, timeouts, output cap, context override
|
|
234
|
+
- **Connection** — API bases, usage endpoint, timeouts, output cap, context override. The OpenCode auth-entry field autocompletes from the keys present in `auth.json`.
|
|
235
|
+
- **Sidebar** — show or hide the quota card. The card refreshes immediately after saving and otherwise every 60 seconds.
|
|
234
236
|
- **Plugin update** — current/latest version, update badge, and **Update now**. It installs the exact registry version through the normal `dsh plugin add` path, blocks a second click while installing, and reports when DSH must be restarted.
|
|
235
237
|
- **Routing** — sticky account per conversation and the free fallback model used after a Go quota error. The quota panel shows the last routing decisions with their reason.
|
|
236
238
|
|
|
237
|
-
The sidebar quota card is **off by default**. Enable *Show the quota card in the sidebar*
|
|
239
|
+
The sidebar quota card is **off by default**. Enable *Show the quota card in the sidebar* under **Sidebar**; the card appears immediately after saving, and clicking it opens the full quota panel with the **Usage by model** table (7/30/90-day window).
|
|
238
240
|
|
|
239
241
|
The Host serves `/dsh-opencode/state`, `/health`, `/analytics`, `/config`, `/key`, `/key/clear`, `/auth-import`, `/refresh`, `/models/refresh`, `/test` and `/update`. Writes require JSON, are refused for cross-origin requests, and are restricted to loopback (`127.0.0.1` / `::1`); reads stay same-origin. This keeps key and configuration writes off a LAN-exposed web server until the host's own auth token can be integrated.
|
|
240
242
|
|
package/README.ru.md
CHANGED
|
@@ -88,7 +88,9 @@ graph LR
|
|
|
88
88
|
- Автоматический переход на следующий аккаунт при 401/403/429 или ответах о превышении квот (400/402/403/429).
|
|
89
89
|
- Недействительный ключ охлаждается 24 часа, превышение квоты — 10 минут (или по заголовку `Retry-After`), rate limit — 60 секунд (или по `Retry-After`).
|
|
90
90
|
- Если запрос уже начал стриминг, он не переключается посреди ответа; следующий запрос берёт следующий готовый ключ.
|
|
91
|
+
- **Sidebar-карточка квот**: отдельный переключатель в разделе **Sidebar** (и в TUI); после сохранения карточка появляется/исчезает сразу, без ожидания 60-секундного polling.
|
|
91
92
|
- **Квоты по каждому аккаунту**: отображение индивидуального расхода за 5 часов, неделю и месяц; активный аккаунт управляет сайдбаром и панелью.
|
|
93
|
+
- **Runway по каждому аккаунту**: оценка времени до исчерпания по среднему темпу каждого окна; отмечается первое окно, которое закончится раньше сброса. Оценка приблизительная: API отдаёт только текущий процент и время сброса.
|
|
92
94
|
- **Онбординг аккаунтов**: **Import all from auth.json** переносит все ключи OpenCode в отдельные credential ref и добавляет аккаунты; **Check all accounts** обновляет квоты и health по каждому аккаунту.
|
|
93
95
|
- **Правила «модель ➔ аккаунт»**: сопоставление по glob-паттернам закрепляет предпочтительный аккаунт за моделью; остальные аккаунты остаются в резерве.
|
|
94
96
|
- **Квотный fallback**: выберите бесплатную модель в настройках; при ошибке квоты Go тот же запрос один раз повторяется через Console/Zen. Если fallback тоже не сработал, возвращается исходная ошибка квоты.
|
|
@@ -119,12 +121,12 @@ npm pack
|
|
|
119
121
|
Установка из **упакованного tarball**:
|
|
120
122
|
|
|
121
123
|
```bash
|
|
122
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
124
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
123
125
|
# DSH Desktop:
|
|
124
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
126
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
125
127
|
|
|
126
128
|
# Либо установка опубликованного пакета из npm:
|
|
127
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
129
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.4
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode** или **OpenCode Go** в списке моделей.
|
package/README.zh.md
CHANGED
|
@@ -89,7 +89,9 @@ graph LR
|
|
|
89
89
|
- 在生成流开始前,遇到 401/403/429 或配额用尽响应(400/402/403/429)自动重试下一个账户。
|
|
90
90
|
- 无效密钥冷却 24 小时,超额冷却 10 分钟(或遵循 `Retry-After`),速率限制冷却 60 秒。
|
|
91
91
|
- 正在流式传输中的请求不会被打断,下一个请求会自动使用下一个可用密钥。
|
|
92
|
+
- **侧边栏配额卡片**:独立 **Sidebar** 开关(TUI 也可设置);保存后立即显示/隐藏,无需等待 60 秒轮询。
|
|
92
93
|
- **多账户额度与健康监控**:每行独立展示 5 小时、每周与每月使用情况,活跃账户驱动侧边栏与配额面板。
|
|
94
|
+
- **每账户预计可用时长**:按各窗口的平均消耗速度估算耗尽时间,并标记在重置前最先耗尽的窗口;API 仅提供当前百分比与重置时间,因此为近似值。
|
|
93
95
|
- **模型 ➔ 账户路由规则**:支持 Glob 通配符(如 `*free*`、`deepseek-*`)将模型优先绑定至特定账户,其他账户保留为备用。
|
|
94
96
|
- **配额回退**:在设置中选择免费模型;Go 返回配额错误时,同一请求通过 Console/Zen 重试一次。若回退也失败,返回原始配额错误。
|
|
95
97
|
- **会话粘性账户**:同一会话优先使用上次成功的账户;显式模型规则、固定账户和配额回退优先。
|
|
@@ -116,12 +118,12 @@ npm pack
|
|
|
116
118
|
通过**打包文件 (.tgz)**安装:
|
|
117
119
|
|
|
118
120
|
```bash
|
|
119
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
121
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
120
122
|
# DSH 桌面端:
|
|
121
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
123
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.4.tgz
|
|
122
124
|
|
|
123
125
|
# 或从 npm 安装已发布版本:
|
|
124
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
126
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.4
|
|
125
127
|
```
|
|
126
128
|
|
|
127
129
|
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode** 或 **OpenCode Go** 即可。
|
package/lib/client.js
CHANGED
|
@@ -67,6 +67,8 @@ window.__ModuleLoader__.load({
|
|
|
67
67
|
fiveHour: "5-hour",
|
|
68
68
|
weekly: "Weekly",
|
|
69
69
|
monthly: "Monthly",
|
|
70
|
+
runway: "Runway (est.)",
|
|
71
|
+
runwaySafe: "safe",
|
|
70
72
|
noUsage: "Quota is unavailable.",
|
|
71
73
|
usageError: "Quota error",
|
|
72
74
|
refresh: "Refresh",
|
|
@@ -149,7 +151,9 @@ window.__ModuleLoader__.load({
|
|
|
149
151
|
streamIdleTimeoutMs: "Stream idle timeout (ms)",
|
|
150
152
|
maxOutputTokens: "Output limit (empty = model default, 0 = omit)",
|
|
151
153
|
contextWindow: "Context override",
|
|
154
|
+
sidebar: "Sidebar",
|
|
152
155
|
showSidebarQuota: "Show the quota card in the sidebar",
|
|
156
|
+
showSidebarQuotaHint: "The card updates immediately after saving and otherwise every 60 seconds.",
|
|
153
157
|
routing: "Routing",
|
|
154
158
|
routingHint: "Recent account/model choices. No prompts or keys are recorded.",
|
|
155
159
|
routingEmpty: "No routing decisions yet.",
|
|
@@ -245,6 +249,8 @@ window.__ModuleLoader__.load({
|
|
|
245
249
|
fiveHour: "5 小时",
|
|
246
250
|
weekly: "每周",
|
|
247
251
|
monthly: "每月",
|
|
252
|
+
runway: "预计可用时长",
|
|
253
|
+
runwaySafe: "安全",
|
|
248
254
|
noUsage: "配额不可用。",
|
|
249
255
|
usageError: "配额错误",
|
|
250
256
|
refresh: "刷新",
|
|
@@ -327,7 +333,9 @@ window.__ModuleLoader__.load({
|
|
|
327
333
|
streamIdleTimeoutMs: "流空闲超时(毫秒)",
|
|
328
334
|
maxOutputTokens: "输出上限(空 = 模型默认,0 = 省略)",
|
|
329
335
|
contextWindow: "上下文覆盖",
|
|
336
|
+
sidebar: "侧边栏",
|
|
330
337
|
showSidebarQuota: "在侧边栏显示配额卡片",
|
|
338
|
+
showSidebarQuotaHint: "保存后立即更新;其余时间每 60 秒更新一次。",
|
|
331
339
|
routing: "路由",
|
|
332
340
|
routingHint: "最近使用的账户/模型选择。不会记录提示词或密钥。",
|
|
333
341
|
routingEmpty: "尚无路由决策。",
|
|
@@ -450,6 +458,9 @@ window.__ModuleLoader__.load({
|
|
|
450
458
|
});
|
|
451
459
|
}
|
|
452
460
|
//#endregion
|
|
461
|
+
//#region src/client/events.ts
|
|
462
|
+
const CONFIG_CHANGED_EVENT = "dsh-opencode-config-changed";
|
|
463
|
+
//#endregion
|
|
453
464
|
//#region src/client/heatmap.tsx
|
|
454
465
|
const DAYS = [
|
|
455
466
|
"Mon",
|
|
@@ -915,6 +926,73 @@ window.__ModuleLoader__.load({
|
|
|
915
926
|
});
|
|
916
927
|
}
|
|
917
928
|
//#endregion
|
|
929
|
+
//#region src/quota-runway.ts
|
|
930
|
+
const HOUR = 36e5;
|
|
931
|
+
const DAY = 24 * HOUR;
|
|
932
|
+
const DURATIONS = {
|
|
933
|
+
fiveHour: 5 * HOUR,
|
|
934
|
+
weekly: 7 * DAY,
|
|
935
|
+
monthly: 30 * DAY
|
|
936
|
+
};
|
|
937
|
+
function percentOf(window) {
|
|
938
|
+
const value = Number(window.percentUsed);
|
|
939
|
+
return Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Estimate when one window reaches 100% at the average burn rate observed
|
|
943
|
+
* since it started. The API exposes only the current percentage and reset
|
|
944
|
+
* time; the start is derived from the known window length, so this is a
|
|
945
|
+
* deliberately conservative, approximate runway.
|
|
946
|
+
*/
|
|
947
|
+
function estimateRunway(window, durationMs, now = Date.now()) {
|
|
948
|
+
if (window === void 0 || window.resetsAt === void 0) return void 0;
|
|
949
|
+
const resetsAtMs = Date.parse(window.resetsAt);
|
|
950
|
+
if (!Number.isFinite(resetsAtMs) || resetsAtMs <= now) return void 0;
|
|
951
|
+
const used = percentOf(window);
|
|
952
|
+
const timeToReset = resetsAtMs - now;
|
|
953
|
+
if (used >= 100) return {
|
|
954
|
+
etaMs: 0,
|
|
955
|
+
resetsAtMs,
|
|
956
|
+
exhaustsBeforeReset: true
|
|
957
|
+
};
|
|
958
|
+
const elapsed = now - (resetsAtMs - durationMs);
|
|
959
|
+
if (elapsed <= 0) return void 0;
|
|
960
|
+
const rate = used / elapsed;
|
|
961
|
+
if (!Number.isFinite(rate) || rate <= 0) return {
|
|
962
|
+
etaMs: Number.POSITIVE_INFINITY,
|
|
963
|
+
resetsAtMs,
|
|
964
|
+
exhaustsBeforeReset: false
|
|
965
|
+
};
|
|
966
|
+
const etaMs = (100 - used) / rate;
|
|
967
|
+
return {
|
|
968
|
+
etaMs,
|
|
969
|
+
resetsAtMs,
|
|
970
|
+
exhaustsBeforeReset: etaMs < timeToReset
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
/** Pick the window that is projected to run out first, if any. */
|
|
974
|
+
function accountRunway(windows, now = Date.now()) {
|
|
975
|
+
if (windows === void 0) return { kind: "unknown" };
|
|
976
|
+
let best;
|
|
977
|
+
let known = 0;
|
|
978
|
+
for (const name of Object.keys(DURATIONS)) {
|
|
979
|
+
const estimate = estimateRunway(windows[name], DURATIONS[name], now);
|
|
980
|
+
if (estimate === void 0) continue;
|
|
981
|
+
known += 1;
|
|
982
|
+
if (!estimate.exhaustsBeforeReset) continue;
|
|
983
|
+
if (best === void 0 || estimate.etaMs < best.etaMs) best = {
|
|
984
|
+
etaMs: estimate.etaMs,
|
|
985
|
+
window: name
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
if (best !== void 0) return {
|
|
989
|
+
kind: "eta",
|
|
990
|
+
etaMs: best.etaMs,
|
|
991
|
+
window: best.window
|
|
992
|
+
};
|
|
993
|
+
return known === 0 ? { kind: "unknown" } : { kind: "safe" };
|
|
994
|
+
}
|
|
995
|
+
//#endregion
|
|
918
996
|
//#region src/client/quota-card.tsx
|
|
919
997
|
const PANEL_EVENT = "dsh-opencode-panel-state";
|
|
920
998
|
function broadcastPanel(open) {
|
|
@@ -943,9 +1021,14 @@ window.__ModuleLoader__.load({
|
|
|
943
1021
|
};
|
|
944
1022
|
load();
|
|
945
1023
|
const timer = setInterval(load, intervalMs);
|
|
1024
|
+
const onConfigChanged = () => {
|
|
1025
|
+
load();
|
|
1026
|
+
};
|
|
1027
|
+
window.addEventListener(CONFIG_CHANGED_EVENT, onConfigChanged);
|
|
946
1028
|
return () => {
|
|
947
1029
|
cancelled = true;
|
|
948
1030
|
clearInterval(timer);
|
|
1031
|
+
window.removeEventListener(CONFIG_CHANGED_EVENT, onConfigChanged);
|
|
949
1032
|
};
|
|
950
1033
|
}, [intervalMs]);
|
|
951
1034
|
return state;
|
|
@@ -969,6 +1052,18 @@ window.__ModuleLoader__.load({
|
|
|
969
1052
|
function usagePercent(window) {
|
|
970
1053
|
return window === void 0 ? "—" : `${Math.round(window.percentUsed)}%`;
|
|
971
1054
|
}
|
|
1055
|
+
function runwayText(summary) {
|
|
1056
|
+
if (summary.kind === "safe") return "safe";
|
|
1057
|
+
if (summary.kind === "unknown") return "—";
|
|
1058
|
+
if (summary.etaMs <= 0) return "now";
|
|
1059
|
+
const hours = summary.etaMs / 36e5;
|
|
1060
|
+
if (hours >= 24) return `~${Math.round(hours / 24)}d`;
|
|
1061
|
+
if (hours >= 1) return `~${Math.round(hours)}h`;
|
|
1062
|
+
return `~${Math.max(1, Math.round(hours * 60))}m`;
|
|
1063
|
+
}
|
|
1064
|
+
function runwayTitle(summary, t) {
|
|
1065
|
+
return summary.kind === "eta" ? `${t("runway")}: ${summary.window}` : "";
|
|
1066
|
+
}
|
|
972
1067
|
function accountStatus(account, t) {
|
|
973
1068
|
if (account.mark !== "") {
|
|
974
1069
|
const label = account.mark === "invalid-credential" ? t("markInvalid") : account.mark === "quota" ? t("markQuota") : account.mark === "rate-limit" ? t("markRateLimit") : t("markTransport");
|
|
@@ -1173,8 +1268,13 @@ window.__ModuleLoader__.load({
|
|
|
1173
1268
|
load();
|
|
1174
1269
|
loadAnalytics(days);
|
|
1175
1270
|
const timer = setInterval(load, 6e4);
|
|
1271
|
+
const onConfigChanged = () => {
|
|
1272
|
+
load();
|
|
1273
|
+
};
|
|
1274
|
+
window.addEventListener(CONFIG_CHANGED_EVENT, onConfigChanged);
|
|
1176
1275
|
return () => {
|
|
1177
1276
|
clearInterval(timer);
|
|
1277
|
+
window.removeEventListener(CONFIG_CHANGED_EVENT, onConfigChanged);
|
|
1178
1278
|
analyticsSeq.current += 1;
|
|
1179
1279
|
};
|
|
1180
1280
|
}, [days]);
|
|
@@ -1277,9 +1377,11 @@ window.__ModuleLoader__.load({
|
|
|
1277
1377
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("accountLabel") }),
|
|
1278
1378
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("fiveHour") }),
|
|
1279
1379
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("weekly") }),
|
|
1280
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("monthly") })
|
|
1380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("monthly") }),
|
|
1381
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("runway") })
|
|
1281
1382
|
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: state.accounts.map((account) => {
|
|
1282
1383
|
const accountWindows = account.usage?.report?.windows;
|
|
1384
|
+
const runway = accountRunway(accountWindows);
|
|
1283
1385
|
const status = accountStatus(account, t);
|
|
1284
1386
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1285
1387
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [
|
|
@@ -1294,7 +1396,11 @@ window.__ModuleLoader__.load({
|
|
|
1294
1396
|
] }),
|
|
1295
1397
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: usagePercent(accountWindows?.fiveHour) }),
|
|
1296
1398
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: usagePercent(accountWindows?.weekly) }),
|
|
1297
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: usagePercent(accountWindows?.monthly) })
|
|
1399
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: usagePercent(accountWindows?.monthly) }),
|
|
1400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1401
|
+
title: runwayTitle(runway, t),
|
|
1402
|
+
children: runway.kind === "safe" ? t("runwaySafe") : runwayText(runway)
|
|
1403
|
+
})
|
|
1298
1404
|
] }, account.label);
|
|
1299
1405
|
}) })]
|
|
1300
1406
|
}),
|
|
@@ -1714,6 +1820,7 @@ window.__ModuleLoader__.load({
|
|
|
1714
1820
|
setError(null);
|
|
1715
1821
|
try {
|
|
1716
1822
|
await saveConfig(patchOf(draft));
|
|
1823
|
+
window.dispatchEvent(new CustomEvent(CONFIG_CHANGED_EVENT));
|
|
1717
1824
|
setNotice(t("saved"));
|
|
1718
1825
|
setState(null);
|
|
1719
1826
|
setDraft(null);
|
|
@@ -2568,78 +2675,84 @@ window.__ModuleLoader__.load({
|
|
|
2568
2675
|
})
|
|
2569
2676
|
]
|
|
2570
2677
|
}),
|
|
2678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2679
|
+
className: "oc-card",
|
|
2680
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2681
|
+
className: "oc-card-title",
|
|
2682
|
+
children: t("advanced")
|
|
2683
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2684
|
+
className: "oc-grid",
|
|
2685
|
+
children: [
|
|
2686
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2687
|
+
label: t("apiKeyEnv"),
|
|
2688
|
+
hint: t("apiKeyEnvHint"),
|
|
2689
|
+
value: draft.apiKeyEnv,
|
|
2690
|
+
onChange: (value) => update({ apiKeyEnv: value })
|
|
2691
|
+
}),
|
|
2692
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2693
|
+
label: t("openCodeAuthEntry"),
|
|
2694
|
+
hint: t("openCodeAuthEntryHint"),
|
|
2695
|
+
value: draft.openCodeAuthEntry,
|
|
2696
|
+
onChange: (value) => update({ openCodeAuthEntry: value }),
|
|
2697
|
+
list: "oc-auth-entries"
|
|
2698
|
+
}),
|
|
2699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("datalist", {
|
|
2700
|
+
id: "oc-auth-entries",
|
|
2701
|
+
children: state.auth.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: entry }, entry))
|
|
2702
|
+
}),
|
|
2703
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2704
|
+
label: t("apiBase"),
|
|
2705
|
+
value: draft.apiBase,
|
|
2706
|
+
onChange: (value) => update({ apiBase: value })
|
|
2707
|
+
}),
|
|
2708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2709
|
+
label: t("goApiBase"),
|
|
2710
|
+
value: draft.goApiBase,
|
|
2711
|
+
onChange: (value) => update({ goApiBase: value })
|
|
2712
|
+
}),
|
|
2713
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2714
|
+
label: t("goZenBase"),
|
|
2715
|
+
value: draft.goZenBase,
|
|
2716
|
+
onChange: (value) => update({ goZenBase: value })
|
|
2717
|
+
}),
|
|
2718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2719
|
+
label: t("usageUrl"),
|
|
2720
|
+
value: draft.usageUrl,
|
|
2721
|
+
onChange: (value) => update({ usageUrl: value })
|
|
2722
|
+
}),
|
|
2723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2724
|
+
label: t("requestTimeoutMs"),
|
|
2725
|
+
type: "number",
|
|
2726
|
+
value: draft.requestTimeoutMs,
|
|
2727
|
+
onChange: (value) => update({ requestTimeoutMs: value })
|
|
2728
|
+
}),
|
|
2729
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2730
|
+
label: t("streamIdleTimeoutMs"),
|
|
2731
|
+
type: "number",
|
|
2732
|
+
value: draft.streamIdleTimeoutMs,
|
|
2733
|
+
onChange: (value) => update({ streamIdleTimeoutMs: value })
|
|
2734
|
+
}),
|
|
2735
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2736
|
+
label: t("maxOutputTokens"),
|
|
2737
|
+
type: "number",
|
|
2738
|
+
value: draft.maxOutputTokens,
|
|
2739
|
+
onChange: (value) => update({ maxOutputTokens: value })
|
|
2740
|
+
}),
|
|
2741
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2742
|
+
label: t("contextWindow"),
|
|
2743
|
+
type: "number",
|
|
2744
|
+
value: draft.contextWindow,
|
|
2745
|
+
onChange: (value) => update({ contextWindow: value })
|
|
2746
|
+
})
|
|
2747
|
+
]
|
|
2748
|
+
})]
|
|
2749
|
+
}),
|
|
2571
2750
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2572
2751
|
className: "oc-card",
|
|
2573
2752
|
children: [
|
|
2574
2753
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2575
2754
|
className: "oc-card-title",
|
|
2576
|
-
children: t("
|
|
2577
|
-
}),
|
|
2578
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2579
|
-
className: "oc-grid",
|
|
2580
|
-
children: [
|
|
2581
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2582
|
-
label: t("apiKeyEnv"),
|
|
2583
|
-
hint: t("apiKeyEnvHint"),
|
|
2584
|
-
value: draft.apiKeyEnv,
|
|
2585
|
-
onChange: (value) => update({ apiKeyEnv: value })
|
|
2586
|
-
}),
|
|
2587
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2588
|
-
label: t("openCodeAuthEntry"),
|
|
2589
|
-
hint: t("openCodeAuthEntryHint"),
|
|
2590
|
-
value: draft.openCodeAuthEntry,
|
|
2591
|
-
onChange: (value) => update({ openCodeAuthEntry: value }),
|
|
2592
|
-
list: "oc-auth-entries"
|
|
2593
|
-
}),
|
|
2594
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("datalist", {
|
|
2595
|
-
id: "oc-auth-entries",
|
|
2596
|
-
children: state.auth.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: entry }, entry))
|
|
2597
|
-
}),
|
|
2598
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2599
|
-
label: t("apiBase"),
|
|
2600
|
-
value: draft.apiBase,
|
|
2601
|
-
onChange: (value) => update({ apiBase: value })
|
|
2602
|
-
}),
|
|
2603
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2604
|
-
label: t("goApiBase"),
|
|
2605
|
-
value: draft.goApiBase,
|
|
2606
|
-
onChange: (value) => update({ goApiBase: value })
|
|
2607
|
-
}),
|
|
2608
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2609
|
-
label: t("goZenBase"),
|
|
2610
|
-
value: draft.goZenBase,
|
|
2611
|
-
onChange: (value) => update({ goZenBase: value })
|
|
2612
|
-
}),
|
|
2613
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2614
|
-
label: t("usageUrl"),
|
|
2615
|
-
value: draft.usageUrl,
|
|
2616
|
-
onChange: (value) => update({ usageUrl: value })
|
|
2617
|
-
}),
|
|
2618
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2619
|
-
label: t("requestTimeoutMs"),
|
|
2620
|
-
type: "number",
|
|
2621
|
-
value: draft.requestTimeoutMs,
|
|
2622
|
-
onChange: (value) => update({ requestTimeoutMs: value })
|
|
2623
|
-
}),
|
|
2624
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2625
|
-
label: t("streamIdleTimeoutMs"),
|
|
2626
|
-
type: "number",
|
|
2627
|
-
value: draft.streamIdleTimeoutMs,
|
|
2628
|
-
onChange: (value) => update({ streamIdleTimeoutMs: value })
|
|
2629
|
-
}),
|
|
2630
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2631
|
-
label: t("maxOutputTokens"),
|
|
2632
|
-
type: "number",
|
|
2633
|
-
value: draft.maxOutputTokens,
|
|
2634
|
-
onChange: (value) => update({ maxOutputTokens: value })
|
|
2635
|
-
}),
|
|
2636
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2637
|
-
label: t("contextWindow"),
|
|
2638
|
-
type: "number",
|
|
2639
|
-
value: draft.contextWindow,
|
|
2640
|
-
onChange: (value) => update({ contextWindow: value })
|
|
2641
|
-
})
|
|
2642
|
-
]
|
|
2755
|
+
children: t("sidebar")
|
|
2643
2756
|
}),
|
|
2644
2757
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2645
2758
|
className: "oc-row",
|
|
@@ -2648,6 +2761,10 @@ window.__ModuleLoader__.load({
|
|
|
2648
2761
|
checked: draft.showSidebarQuota,
|
|
2649
2762
|
onChange: (event) => update({ showSidebarQuota: event.target.checked })
|
|
2650
2763
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("showSidebarQuota") })]
|
|
2764
|
+
}),
|
|
2765
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2766
|
+
className: "oc-hint",
|
|
2767
|
+
children: t("showSidebarQuotaHint")
|
|
2651
2768
|
})
|
|
2652
2769
|
]
|
|
2653
2770
|
}),
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const DEFAULT_GO_API_BASE = "https://opencode.ai/inference/go";
|
|
|
17
17
|
const DEFAULT_GO_ZEN_BASE = "https://opencode.ai/zen/go/v1";
|
|
18
18
|
const DEFAULT_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
19
19
|
const DEFAULT_AUTH_ENTRY = "opencode-go";
|
|
20
|
-
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.
|
|
20
|
+
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.4";
|
|
21
21
|
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
22
22
|
const DEFAULT_MAX_TOKENS = 32768;
|
|
23
23
|
const DEFAULT_GENERATE_MAX_TOKENS = 32768;
|
|
@@ -4871,6 +4871,10 @@ function buildOpenCodeTuiSection(deps) {
|
|
|
4871
4871
|
{
|
|
4872
4872
|
id: "advanced",
|
|
4873
4873
|
title: "Advanced"
|
|
4874
|
+
},
|
|
4875
|
+
{
|
|
4876
|
+
id: "sidebar",
|
|
4877
|
+
title: "Sidebar"
|
|
4874
4878
|
}
|
|
4875
4879
|
],
|
|
4876
4880
|
fields: [
|
|
@@ -4957,6 +4961,18 @@ function buildOpenCodeTuiSection(deps) {
|
|
|
4957
4961
|
value: text.trim() === "true"
|
|
4958
4962
|
})
|
|
4959
4963
|
},
|
|
4964
|
+
{
|
|
4965
|
+
path: ["showSidebarQuota"],
|
|
4966
|
+
group: "sidebar",
|
|
4967
|
+
kind: "boolean",
|
|
4968
|
+
label: "Show quota card in sidebar",
|
|
4969
|
+
hint: "Card visibility updates on the next sidebar refresh.",
|
|
4970
|
+
format: (value) => String(value === void 0 ? deps.showSidebarQuota() : value === true),
|
|
4971
|
+
parse: (text) => ({
|
|
4972
|
+
kind: "set",
|
|
4973
|
+
value: text.trim() === "true"
|
|
4974
|
+
})
|
|
4975
|
+
},
|
|
4960
4976
|
{
|
|
4961
4977
|
path: ["activeAccount"],
|
|
4962
4978
|
group: "advanced",
|
|
@@ -5028,7 +5044,8 @@ function applyOpenCodeTuiSettings(ctx, deps) {
|
|
|
5028
5044
|
activeAccount: deps.activeAccount(),
|
|
5029
5045
|
filterModelsByPlan: deps.filterModelsByPlan(),
|
|
5030
5046
|
quotaFallbackModel: deps.quotaFallbackModel(),
|
|
5031
|
-
stickySession: deps.stickySession()
|
|
5047
|
+
stickySession: deps.stickySession(),
|
|
5048
|
+
showSidebarQuota: deps.showSidebarQuota()
|
|
5032
5049
|
});
|
|
5033
5050
|
let lastSignature = signature();
|
|
5034
5051
|
const refresh = () => {
|
|
@@ -5209,7 +5226,8 @@ function apply(ctx, config) {
|
|
|
5209
5226
|
activeAccount: () => current().activeAccount,
|
|
5210
5227
|
filterModelsByPlan: () => current().filterModelsByPlan,
|
|
5211
5228
|
quotaFallbackModel: () => current().quotaFallbackModel,
|
|
5212
|
-
stickySession: () => current().stickySession
|
|
5229
|
+
stickySession: () => current().stickySession,
|
|
5230
|
+
showSidebarQuota: () => current().showSidebarQuota
|
|
5213
5231
|
});
|
|
5214
5232
|
});
|
|
5215
5233
|
ctx.inject(["webServer"], (webCtx) => {
|