@goodandready/dsh-opencode-provider 0.5.25 → 0.5.26
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 +6 -0
- package/README.md +3 -3
- package/README.ru.md +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +93 -8
- package/lib/index.d.ts +7 -1
- package/lib/index.js +58 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.26 — release 3: quota display and switch notices
|
|
4
|
+
|
|
5
|
+
- **#75 — quota display modes**: new `usageDisplay` setting with `auto`/`always`/`off` (legacy `showSidebarQuota` maps to `always`); `auto` shows the sidebar card when a Go usage report exists. Exposed in Settings and TUI.
|
|
6
|
+
- **#79 — account-switch notice**: routing trace entries now carry the opaque DSH session id; a `conversation.composer.dock` notice shows a non-blocking switch message for the current conversation after rotation or quota fallback. No prompt content or keys are exposed.
|
|
7
|
+
- 130 tests.
|
|
8
|
+
|
|
3
9
|
## 0.5.25 — release 2: stale data
|
|
4
10
|
|
|
5
11
|
- **#87 — stale usage**: a failed usage refresh now returns the last successful report with `stale: true` and `lastSuccessAt` instead of discarding it. The quota sidebar/panel and Settings label the stale snapshot; an explicit refresh no longer loses the previous reading.
|
package/README.md
CHANGED
|
@@ -132,12 +132,12 @@ npm pack
|
|
|
132
132
|
Install the **packed tarball** into a profile:
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
135
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
136
136
|
# DSH Desktop:
|
|
137
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
137
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
138
138
|
|
|
139
139
|
# Or install the published package:
|
|
140
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
140
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.26
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
Restart dsh (or DSH Desktop), then pick **OpenCode (plugin)** or **OpenCode Go (plugin)** in the model picker.
|
package/README.ru.md
CHANGED
|
@@ -131,12 +131,12 @@ npm pack
|
|
|
131
131
|
Установка из **упакованного tarball**:
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
134
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
135
135
|
# DSH Desktop:
|
|
136
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
136
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
137
137
|
|
|
138
138
|
# Либо установка опубликованного пакета из npm:
|
|
139
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
139
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.26
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode (plugin)** или **OpenCode Go (plugin)** в списке моделей.
|
package/README.zh.md
CHANGED
|
@@ -126,12 +126,12 @@ npm pack
|
|
|
126
126
|
通过**打包文件 (.tgz)**安装:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
129
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
130
130
|
# DSH 桌面端:
|
|
131
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
131
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.26.tgz
|
|
132
132
|
|
|
133
133
|
# 或从 npm 安装已发布版本:
|
|
134
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
134
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.26
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode (plugin)** 或 **OpenCode Go (plugin)** 即可。
|
package/lib/client.js
CHANGED
|
@@ -189,6 +189,11 @@ window.__ModuleLoader__.load({
|
|
|
189
189
|
sidebar: "Sidebar",
|
|
190
190
|
showSidebarQuota: "Show the quota card in the sidebar",
|
|
191
191
|
showSidebarQuotaHint: "The card updates immediately after saving and otherwise every 60 seconds.",
|
|
192
|
+
usageDisplay: "Quota display",
|
|
193
|
+
usageDisplayAuto: "Auto (when Go usage is available)",
|
|
194
|
+
usageDisplayAlways: "Always",
|
|
195
|
+
usageDisplayOff: "Off",
|
|
196
|
+
usageDisplayHint: "Auto shows the sidebar card only when a Go usage report is available; Always keeps it visible; Off stops polling-hidden display.",
|
|
192
197
|
routing: "Routing",
|
|
193
198
|
routingHint: "Recent account/model choices. No prompts or keys are recorded.",
|
|
194
199
|
routingEmpty: "No routing decisions yet.",
|
|
@@ -199,6 +204,7 @@ window.__ModuleLoader__.load({
|
|
|
199
204
|
routingDefault: "Automatic",
|
|
200
205
|
routingRotation: "Rotation",
|
|
201
206
|
routingFallback: "Quota fallback",
|
|
207
|
+
accountSwitchNotice: "Account switched to {account} ({reason}).",
|
|
202
208
|
routingFailed: "Failed",
|
|
203
209
|
quotaFallbackModel: "Quota fallback model",
|
|
204
210
|
quotaFallbackModelHint: "If Go rejects with a quota error, retry once on this free model through Console/Zen.",
|
|
@@ -409,6 +415,11 @@ window.__ModuleLoader__.load({
|
|
|
409
415
|
sidebar: "侧边栏",
|
|
410
416
|
showSidebarQuota: "在侧边栏显示配额卡片",
|
|
411
417
|
showSidebarQuotaHint: "保存后立即更新;其余时间每 60 秒更新一次。",
|
|
418
|
+
usageDisplay: "配额显示",
|
|
419
|
+
usageDisplayAuto: "自动(有 Go 用量时)",
|
|
420
|
+
usageDisplayAlways: "常驻",
|
|
421
|
+
usageDisplayOff: "关闭",
|
|
422
|
+
usageDisplayHint: "自动:仅有 Go 用量报告时显示侧边栏卡片;常驻:始终显示;关闭:隐藏。",
|
|
412
423
|
routing: "路由",
|
|
413
424
|
routingHint: "最近使用的账户/模型选择。不会记录提示词或密钥。",
|
|
414
425
|
routingEmpty: "尚无路由决策。",
|
|
@@ -419,6 +430,7 @@ window.__ModuleLoader__.load({
|
|
|
419
430
|
routingDefault: "自动",
|
|
420
431
|
routingRotation: "轮换",
|
|
421
432
|
routingFallback: "配额回退",
|
|
433
|
+
accountSwitchNotice: "账户已切换为 {account}({reason})。",
|
|
422
434
|
routingFailed: "失败",
|
|
423
435
|
quotaFallbackModel: "配额回退模型",
|
|
424
436
|
quotaFallbackModelHint: "Go 返回配额错误时,通过 Console/Zen 用此免费模型重试一次。",
|
|
@@ -469,6 +481,9 @@ window.__ModuleLoader__.load({
|
|
|
469
481
|
function fetchState() {
|
|
470
482
|
return call("/dsh-opencode/state");
|
|
471
483
|
}
|
|
484
|
+
function fetchNotices() {
|
|
485
|
+
return call("/dsh-opencode/notices");
|
|
486
|
+
}
|
|
472
487
|
function refreshState() {
|
|
473
488
|
return call("/dsh-opencode/refresh", {
|
|
474
489
|
method: "POST",
|
|
@@ -1238,7 +1253,10 @@ window.__ModuleLoader__.load({
|
|
|
1238
1253
|
const { t } = props;
|
|
1239
1254
|
const state = useHostState(6e4);
|
|
1240
1255
|
const expanded = usePanelOpen();
|
|
1241
|
-
if (state === null
|
|
1256
|
+
if (state === null) return null;
|
|
1257
|
+
const mode = state.config.usageDisplay ?? (state.config.showSidebarQuota === true ? "always" : "auto");
|
|
1258
|
+
if (mode === "off") return null;
|
|
1259
|
+
if (mode === "auto" && state.usage === null) return null;
|
|
1242
1260
|
const windows = state.usage?.report?.windows;
|
|
1243
1261
|
const accountLabel = state.accounts.find((account) => account.active)?.label;
|
|
1244
1262
|
const rows = [
|
|
@@ -1681,6 +1699,7 @@ window.__ModuleLoader__.load({
|
|
|
1681
1699
|
maxOutputTokensOverrides: Object.fromEntries(Object.entries(state.config.maxOutputTokensOverrides).map(([id, value]) => [id, String(value)])),
|
|
1682
1700
|
showSidebarQuota: state.config.showSidebarQuota,
|
|
1683
1701
|
activeAccount: state.config.activeAccount,
|
|
1702
|
+
usageDisplay: state.config.usageDisplay,
|
|
1684
1703
|
accounts: state.config.accounts.map((account) => ({ ...account })),
|
|
1685
1704
|
modelAccountRules: state.config.modelAccountRules.map((rule) => ({ ...rule })),
|
|
1686
1705
|
quotaFallbackModel: state.config.quotaFallbackModel,
|
|
@@ -1727,6 +1746,7 @@ window.__ModuleLoader__.load({
|
|
|
1727
1746
|
maxOutputTokensOverrides: numberRecordOrNull(draft.maxOutputTokensOverrides),
|
|
1728
1747
|
showSidebarQuota: draft.showSidebarQuota,
|
|
1729
1748
|
activeAccount: draft.activeAccount.trim(),
|
|
1749
|
+
usageDisplay: draft.usageDisplay,
|
|
1730
1750
|
accounts: draft.accounts.map((account) => ({
|
|
1731
1751
|
label: account.label.trim(),
|
|
1732
1752
|
apiKeyEnv: account.apiKeyEnv.trim()
|
|
@@ -3148,16 +3168,33 @@ window.__ModuleLoader__.load({
|
|
|
3148
3168
|
children: t("sidebar")
|
|
3149
3169
|
}),
|
|
3150
3170
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3151
|
-
className: "oc-
|
|
3152
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3171
|
+
className: "oc-field",
|
|
3172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3173
|
+
className: "oc-label",
|
|
3174
|
+
children: t("usageDisplay")
|
|
3175
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3176
|
+
className: "oc-select",
|
|
3177
|
+
value: draft.usageDisplay,
|
|
3178
|
+
onChange: (event) => update({ usageDisplay: event.target.value }),
|
|
3179
|
+
children: [
|
|
3180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3181
|
+
value: "auto",
|
|
3182
|
+
children: t("usageDisplayAuto")
|
|
3183
|
+
}),
|
|
3184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3185
|
+
value: "always",
|
|
3186
|
+
children: t("usageDisplayAlways")
|
|
3187
|
+
}),
|
|
3188
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3189
|
+
value: "off",
|
|
3190
|
+
children: t("usageDisplayOff")
|
|
3191
|
+
})
|
|
3192
|
+
]
|
|
3193
|
+
})]
|
|
3157
3194
|
}),
|
|
3158
3195
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3159
3196
|
className: "oc-hint",
|
|
3160
|
-
children: t("
|
|
3197
|
+
children: t("usageDisplayHint")
|
|
3161
3198
|
})
|
|
3162
3199
|
]
|
|
3163
3200
|
}),
|
|
@@ -3290,6 +3327,7 @@ window.__ModuleLoader__.load({
|
|
|
3290
3327
|
.oc-drag { cursor: grab; color: var(--oc-text-tertiary); user-select: none; font-size: 13px; line-height: 1; }
|
|
3291
3328
|
.oc-drag:active { cursor: grabbing; }
|
|
3292
3329
|
.oc-notice { font-size: 12px; padding: 7px 10px; border-radius: 7px; background: var(--oc-accent-soft); border: 1px solid var(--oc-accent-border); }
|
|
3330
|
+
.oc-switch-notice { margin: 4px auto; padding: 4px 10px; border: 1px solid var(--oc-border); border-radius: 999px; background: var(--oc-fill-soft); color: var(--oc-text-secondary); font-size: 11px; line-height: 1.4; text-align: center; }
|
|
3293
3331
|
.oc-error { font-size: 12px; padding: 7px 10px; border-radius: 7px; background: var(--oc-danger-soft); border: 1px solid var(--oc-danger-border); color: var(--oc-danger); }
|
|
3294
3332
|
[class*="_footerActions"]:has(.oc-foot), [class*="_footerActions"]:has(.oc-railButton) { flex-direction: column; }
|
|
3295
3333
|
.oc-foot { box-sizing: border-box; flex: 1 1 auto; width: 100%; min-width: 0; display: flex; flex-direction: column; gap: 6px; margin: 0 0 4px; padding: 8px; border: 0.5px solid transparent; border-radius: var(--dsw-radius-md, 8px); background: transparent; color: var(--oc-text-secondary); font: inherit; text-align: left; cursor: pointer; }
|
|
@@ -3321,6 +3359,47 @@ window.__ModuleLoader__.load({
|
|
|
3321
3359
|
.oc-table th, .oc-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--oc-border-soft); vertical-align: top; }
|
|
3322
3360
|
.oc-table th { font-weight: 600; opacity: 0.8; }
|
|
3323
3361
|
`;
|
|
3362
|
+
// src/client/switch-notice.tsx
|
|
3363
|
+
/** Ambient notice under the composer when this conversation switched accounts. */
|
|
3364
|
+
function SwitchNotice({ sessionId, t }) {
|
|
3365
|
+
const [notice, setNotice] = (0, react.useState)(null);
|
|
3366
|
+
const seenAt = (0, react.useRef)(Date.now() - 5e3);
|
|
3367
|
+
(0, react.useEffect)(() => {
|
|
3368
|
+
if (sessionId === void 0 || String(sessionId) === "") return;
|
|
3369
|
+
let alive = true;
|
|
3370
|
+
const poll = () => {
|
|
3371
|
+
fetchNotices().then(({ events }) => {
|
|
3372
|
+
if (!alive) return;
|
|
3373
|
+
const event = events.filter((entry) => entry.session === String(sessionId) && entry.at > seenAt.current && entry.ok && (entry.reason === "rotation" || entry.reason === "quota-fallback")).sort((left, right) => right.at - left.at)[0];
|
|
3374
|
+
if (event === void 0) return;
|
|
3375
|
+
seenAt.current = event.at;
|
|
3376
|
+
setNotice({
|
|
3377
|
+
account: event.account,
|
|
3378
|
+
reason: event.reason,
|
|
3379
|
+
at: event.at
|
|
3380
|
+
});
|
|
3381
|
+
}).catch(() => {});
|
|
3382
|
+
};
|
|
3383
|
+
poll();
|
|
3384
|
+
const timer = setInterval(poll, 5e3);
|
|
3385
|
+
return () => {
|
|
3386
|
+
alive = false;
|
|
3387
|
+
clearInterval(timer);
|
|
3388
|
+
};
|
|
3389
|
+
}, [sessionId]);
|
|
3390
|
+
(0, react.useEffect)(() => {
|
|
3391
|
+
if (notice === null) return;
|
|
3392
|
+
const timer = setTimeout(() => setNotice(null), 6e4);
|
|
3393
|
+
return () => clearTimeout(timer);
|
|
3394
|
+
}, [notice]);
|
|
3395
|
+
if (notice === null) return null;
|
|
3396
|
+
const reason = notice.reason === "quota-fallback" ? t("routingFallback") : t("routingRotation");
|
|
3397
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3398
|
+
className: "oc-switch-notice",
|
|
3399
|
+
role: "status",
|
|
3400
|
+
children: t("accountSwitchNotice").replace("{account}", notice.account).replace("{reason}", reason)
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3324
3403
|
// src/client/index.tsx
|
|
3325
3404
|
function injectCss() {
|
|
3326
3405
|
if (typeof document === "undefined") return () => {};
|
|
@@ -3356,6 +3435,12 @@ window.__ModuleLoader__.load({
|
|
|
3356
3435
|
locale: NS,
|
|
3357
3436
|
inject: () => ({ close: () => ctx.get("layout")?.selectPanel(null) })
|
|
3358
3437
|
}, QuotaPanel));
|
|
3438
|
+
ctx.slots.inject("conversation.composer.dock", () => slots.register({
|
|
3439
|
+
name: "conversation.composer.dock",
|
|
3440
|
+
id: "opencode-switch-notice",
|
|
3441
|
+
order: 80,
|
|
3442
|
+
locale: NS
|
|
3443
|
+
}, SwitchNotice));
|
|
3359
3444
|
ctx.inject(["layout"], (layoutCtx) => {
|
|
3360
3445
|
const layoutSlots = layoutCtx.slots;
|
|
3361
3446
|
layoutCtx.slots.inject("sidebar.footer.action", () => layoutSlots.register({
|
package/lib/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Context } from "@deepseek-ai/cordis";
|
|
|
4
4
|
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
5
5
|
import { WebServer } from "@deepseek-ai/dsh-host-webserver";
|
|
6
6
|
type TransportId = 'openai-chat' | 'openai-responses' | 'anthropic' | 'google';
|
|
7
|
+
type UsageDisplay = 'auto' | 'always' | 'off';
|
|
7
8
|
interface AccountConfig {
|
|
8
9
|
label?: string;
|
|
9
10
|
apiKeyEnv?: string;
|
|
@@ -66,7 +67,9 @@ interface Config {
|
|
|
66
67
|
stickySession?: boolean;
|
|
67
68
|
/** Pinned account id; empty means automatic. */
|
|
68
69
|
activeAccount?: string;
|
|
69
|
-
/**
|
|
70
|
+
/** Sidebar quota visibility; legacy booleans map to always/auto. */
|
|
71
|
+
usageDisplay?: UsageDisplay;
|
|
72
|
+
/** Legacy boolean; used only when `usageDisplay` is absent. */
|
|
70
73
|
showSidebarQuota?: boolean;
|
|
71
74
|
/** Product user agent sent to OpenCode. */
|
|
72
75
|
userAgent?: string;
|
|
@@ -97,6 +100,7 @@ interface ResolvedConfig {
|
|
|
97
100
|
autoSwitch: boolean;
|
|
98
101
|
stickySession: boolean;
|
|
99
102
|
activeAccount: string;
|
|
103
|
+
usageDisplay: UsageDisplay;
|
|
100
104
|
showSidebarQuota: boolean;
|
|
101
105
|
userAgent: string;
|
|
102
106
|
}
|
|
@@ -163,6 +167,8 @@ interface RoutingTraceEntry {
|
|
|
163
167
|
readonly provider: string;
|
|
164
168
|
readonly model: string;
|
|
165
169
|
readonly account: string;
|
|
170
|
+
/** DSH session id; never prompt content. */
|
|
171
|
+
readonly session?: string;
|
|
166
172
|
readonly reason: RoutingReason;
|
|
167
173
|
readonly fallback: boolean;
|
|
168
174
|
readonly ok: boolean;
|
package/lib/index.js
CHANGED
|
@@ -81,7 +81,7 @@ const DEFAULT_GO_API_BASE = "https://opencode.ai/inference/go";
|
|
|
81
81
|
const DEFAULT_GO_ZEN_BASE = "https://opencode.ai/zen/go/v1";
|
|
82
82
|
const DEFAULT_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
83
83
|
const DEFAULT_AUTH_ENTRY = "opencode-go";
|
|
84
|
-
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.
|
|
84
|
+
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.26";
|
|
85
85
|
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
86
86
|
const DEFAULT_MAX_TOKENS = 32768;
|
|
87
87
|
const DEFAULT_GENERATE_MAX_TOKENS = 32768;
|
|
@@ -126,6 +126,11 @@ const Config = z.object({
|
|
|
126
126
|
autoSwitch: markVolatile(z.boolean().default(true)),
|
|
127
127
|
stickySession: markVolatile(z.boolean().default(true)),
|
|
128
128
|
activeAccount: markVolatile(z.string().default("")),
|
|
129
|
+
usageDisplay: markVolatile(z.union([
|
|
130
|
+
"auto",
|
|
131
|
+
"always",
|
|
132
|
+
"off"
|
|
133
|
+
])),
|
|
129
134
|
showSidebarQuota: markVolatile(z.boolean().default(false)),
|
|
130
135
|
userAgent: markVolatile(z.string().default(DEFAULT_USER_AGENT))
|
|
131
136
|
});
|
|
@@ -222,6 +227,7 @@ function resolveConfig(config) {
|
|
|
222
227
|
autoSwitch: config.autoSwitch !== false,
|
|
223
228
|
stickySession: config.stickySession !== false,
|
|
224
229
|
activeAccount: typeof config.activeAccount === "string" ? config.activeAccount : "",
|
|
230
|
+
usageDisplay: config.usageDisplay === "always" || config.usageDisplay === "off" ? config.usageDisplay : config.usageDisplay === "auto" ? "auto" : config.showSidebarQuota === true ? "always" : "auto",
|
|
225
231
|
showSidebarQuota: config.showSidebarQuota === true,
|
|
226
232
|
userAgent: typeof config.userAgent === "string" && config.userAgent !== "" ? config.userAgent : DEFAULT_USER_AGENT
|
|
227
233
|
};
|
|
@@ -3305,6 +3311,7 @@ var OpenCodeAdapter = class extends LlmAdapter {
|
|
|
3305
3311
|
const runStartedAt = Date.now();
|
|
3306
3312
|
const model = effectiveModel(options.model, config);
|
|
3307
3313
|
const sessionId = openCodeSessionId(options);
|
|
3314
|
+
const traceSession = options.sessionId === void 0 ? "" : String(options.sessionId);
|
|
3308
3315
|
const rawKeys = orderKeysForModel(this.deps.resolveKeys !== void 0 ? await this.deps.resolveKeys(options.provider) : [await this.deps.resolveKey(options.provider)], options.model, config.modelAccountRules);
|
|
3309
3316
|
const rule = matchModelAccount(options.model, config.modelAccountRules);
|
|
3310
3317
|
let keys = rawKeys;
|
|
@@ -3404,6 +3411,7 @@ var OpenCodeAdapter = class extends LlmAdapter {
|
|
|
3404
3411
|
provider: options.provider,
|
|
3405
3412
|
model: options.model,
|
|
3406
3413
|
account: entry.label ?? "default",
|
|
3414
|
+
...traceSession === "" ? {} : { session: traceSession },
|
|
3407
3415
|
reason: attemptReason,
|
|
3408
3416
|
fallback,
|
|
3409
3417
|
ok: true
|
|
@@ -3418,6 +3426,7 @@ var OpenCodeAdapter = class extends LlmAdapter {
|
|
|
3418
3426
|
provider: options.provider,
|
|
3419
3427
|
model: options.model,
|
|
3420
3428
|
account: entry.label ?? "default",
|
|
3429
|
+
...traceSession === "" ? {} : { session: traceSession },
|
|
3421
3430
|
reason: attemptReason,
|
|
3422
3431
|
fallback,
|
|
3423
3432
|
ok: false,
|
|
@@ -3446,6 +3455,7 @@ var OpenCodeAdapter = class extends LlmAdapter {
|
|
|
3446
3455
|
provider: options.provider,
|
|
3447
3456
|
model: options.model,
|
|
3448
3457
|
account: lastAccount,
|
|
3458
|
+
...traceSession === "" ? {} : { session: traceSession },
|
|
3449
3459
|
reason,
|
|
3450
3460
|
fallback,
|
|
3451
3461
|
ok: false,
|
|
@@ -4734,6 +4744,7 @@ function publicConfig(config) {
|
|
|
4734
4744
|
autoSwitch: config.autoSwitch,
|
|
4735
4745
|
stickySession: config.stickySession,
|
|
4736
4746
|
activeAccount: config.activeAccount,
|
|
4747
|
+
usageDisplay: config.usageDisplay,
|
|
4737
4748
|
showSidebarQuota: config.showSidebarQuota,
|
|
4738
4749
|
userAgent: config.userAgent
|
|
4739
4750
|
};
|
|
@@ -4923,6 +4934,10 @@ function configOps(patch) {
|
|
|
4923
4934
|
if ("autoSwitch" in patch) set("autoSwitch", patch.autoSwitch === true);
|
|
4924
4935
|
if ("stickySession" in patch) set("stickySession", patch.stickySession === true);
|
|
4925
4936
|
if ("activeAccount" in patch) set("activeAccount", patch.activeAccount);
|
|
4937
|
+
if ("usageDisplay" in patch) {
|
|
4938
|
+
const value = patch.usageDisplay;
|
|
4939
|
+
if (value === "auto" || value === "always" || value === "off") set("usageDisplay", value);
|
|
4940
|
+
}
|
|
4926
4941
|
if ("showSidebarQuota" in patch) set("showSidebarQuota", patch.showSidebarQuota === true);
|
|
4927
4942
|
if ("userAgent" in patch) set("userAgent", patch.userAgent);
|
|
4928
4943
|
return ops;
|
|
@@ -5074,6 +5089,21 @@ function applyRoutes(ctx, web, deps) {
|
|
|
5074
5089
|
json(res, 500, { error: error instanceof Error ? error.message : "state failed" });
|
|
5075
5090
|
}
|
|
5076
5091
|
});
|
|
5092
|
+
route("/dsh-opencode/notices", async (req, res) => {
|
|
5093
|
+
if (req.method !== "GET") {
|
|
5094
|
+
json(res, 405, { error: "GET required" });
|
|
5095
|
+
return;
|
|
5096
|
+
}
|
|
5097
|
+
if (!trustedHost(req)) {
|
|
5098
|
+
json(res, 403, { error: "non-local Host refused" });
|
|
5099
|
+
return;
|
|
5100
|
+
}
|
|
5101
|
+
if (!allowedOrigin(req)) {
|
|
5102
|
+
json(res, 403, { error: "cross-origin request refused" });
|
|
5103
|
+
return;
|
|
5104
|
+
}
|
|
5105
|
+
json(res, 200, { events: deps.adapter.routingTrace() });
|
|
5106
|
+
});
|
|
5077
5107
|
route("/dsh-opencode/health", async (req, res) => {
|
|
5078
5108
|
if (req.method !== "GET") {
|
|
5079
5109
|
json(res, 405, { error: "GET required" });
|
|
@@ -5477,6 +5507,32 @@ function buildOpenCodeTuiSection(deps) {
|
|
|
5477
5507
|
value: text.trim() === "true"
|
|
5478
5508
|
})
|
|
5479
5509
|
},
|
|
5510
|
+
{
|
|
5511
|
+
path: ["usageDisplay"],
|
|
5512
|
+
group: "sidebar",
|
|
5513
|
+
kind: "select",
|
|
5514
|
+
label: "Quota display",
|
|
5515
|
+
hint: "Auto, Always, or Off for the sidebar quota card.",
|
|
5516
|
+
options: [
|
|
5517
|
+
{
|
|
5518
|
+
value: "auto",
|
|
5519
|
+
label: "Auto"
|
|
5520
|
+
},
|
|
5521
|
+
{
|
|
5522
|
+
value: "always",
|
|
5523
|
+
label: "Always"
|
|
5524
|
+
},
|
|
5525
|
+
{
|
|
5526
|
+
value: "off",
|
|
5527
|
+
label: "Off"
|
|
5528
|
+
}
|
|
5529
|
+
],
|
|
5530
|
+
format: (value) => typeof value === "string" ? value : deps.usageDisplay(),
|
|
5531
|
+
parse: (text) => text === "auto" || text === "always" || text === "off" ? {
|
|
5532
|
+
kind: "set",
|
|
5533
|
+
value: text
|
|
5534
|
+
} : void 0
|
|
5535
|
+
},
|
|
5480
5536
|
{
|
|
5481
5537
|
path: ["showSidebarQuota"],
|
|
5482
5538
|
group: "sidebar",
|
|
@@ -5793,6 +5849,7 @@ function apply(ctx, config) {
|
|
|
5793
5849
|
quotaFallbackModel: () => current().quotaFallbackModel,
|
|
5794
5850
|
autoSwitch: () => current().autoSwitch,
|
|
5795
5851
|
stickySession: () => current().stickySession,
|
|
5852
|
+
usageDisplay: () => current().usageDisplay,
|
|
5796
5853
|
showSidebarQuota: () => current().showSidebarQuota
|
|
5797
5854
|
});
|
|
5798
5855
|
});
|