dsh-opencode-go-usage 1.1.0 → 1.2.1
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/lib/client.js +19 -4
- package/lib/index.js +92 -6
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -37,7 +37,8 @@ window.__ModuleLoader__.load({
|
|
|
37
37
|
".ocg-fill{height:100%;border-radius:3px;transition:width .4s ease}",
|
|
38
38
|
".ocg-err{font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
39
39
|
".ocg-rail{box-sizing:border-box;width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-size:10px;font-weight:700;color:var(--dsw-alias-label-primary);cursor:default}",
|
|
40
|
-
".ocg-rail:hover{background:var(--dsw-alias-interactive-bg-hover)}"
|
|
40
|
+
".ocg-rail:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
41
|
+
".ocg-update{display:inline-block;padding:1px 6px;border-radius:8px;font-size:10px;line-height:16px;font-weight:600;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);text-decoration:none;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:110px}"
|
|
41
42
|
].join("");
|
|
42
43
|
document.head.appendChild(tag);
|
|
43
44
|
}
|
|
@@ -45,7 +46,7 @@ window.__ModuleLoader__.load({
|
|
|
45
46
|
// ── locale dictionaries ──
|
|
46
47
|
const NS = "dsh-opencode-go-usage";
|
|
47
48
|
const zh = {
|
|
48
|
-
"window.rolling": "滚动窗口",
|
|
49
|
+
"window.rolling": "滚动窗口 (5h)",
|
|
49
50
|
"window.weekly": "周窗口",
|
|
50
51
|
"window.monthly": "月窗口",
|
|
51
52
|
"window.rolling.hint": "5 小时",
|
|
@@ -57,7 +58,9 @@ window.__ModuleLoader__.load({
|
|
|
57
58
|
"refresh": "刷新",
|
|
58
59
|
"loading": "加载中…",
|
|
59
60
|
"rail.title": "OpenCode GO 月用量 {pct}%",
|
|
60
|
-
"rail.plain": "OpenCode GO"
|
|
61
|
+
"rail.plain": "OpenCode GO",
|
|
62
|
+
"update.available": "新版本 v{v}",
|
|
63
|
+
"update.title": "点击查看升级说明"
|
|
61
64
|
};
|
|
62
65
|
const en = {
|
|
63
66
|
"window.rolling": "Rolling (5h)",
|
|
@@ -72,7 +75,9 @@ window.__ModuleLoader__.load({
|
|
|
72
75
|
"refresh": "Refresh",
|
|
73
76
|
"loading": "Loading…",
|
|
74
77
|
"rail.title": "OpenCode GO monthly {pct}%",
|
|
75
|
-
"rail.plain": "OpenCode GO"
|
|
78
|
+
"rail.plain": "OpenCode GO",
|
|
79
|
+
"update.available": "v{v} available",
|
|
80
|
+
"update.title": "Click for upgrade instructions"
|
|
76
81
|
};
|
|
77
82
|
|
|
78
83
|
// ── data helpers ──
|
|
@@ -194,6 +199,16 @@ window.__ModuleLoader__.load({
|
|
|
194
199
|
className: "ocg-head",
|
|
195
200
|
children: [
|
|
196
201
|
react_jsx_runtime.jsx("span", { children: "OpenCode GO" }),
|
|
202
|
+
(data && data.update && data.update.available && data.update.latest
|
|
203
|
+
? react_jsx_runtime.jsx("a", {
|
|
204
|
+
className: "ocg-update",
|
|
205
|
+
href: "https://www.npmjs.com/package/dsh-opencode-go-usage",
|
|
206
|
+
target: "_blank",
|
|
207
|
+
rel: "noreferrer",
|
|
208
|
+
title: t("update.title"),
|
|
209
|
+
children: t("update.available", { v: data.update.latest })
|
|
210
|
+
})
|
|
211
|
+
: null),
|
|
197
212
|
react_jsx_runtime.jsx("button", {
|
|
198
213
|
className: "ocg-refresh",
|
|
199
214
|
onClick: () => setStamp(stamp + 1),
|
package/lib/index.js
CHANGED
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
* settings.
|
|
24
24
|
*
|
|
25
25
|
* Settings namespace `dsh-opencode-go-usage`:
|
|
26
|
-
* - apiKeyEnv:
|
|
27
|
-
* - baseUrl:
|
|
28
|
-
* - cacheMs:
|
|
26
|
+
* - apiKeyEnv: credential ref / env var for the API key (default OPENCODE_GO_API_KEY)
|
|
27
|
+
* - baseUrl: GO gateway base (default https://opencode.ai/zen/go)
|
|
28
|
+
* - cacheMs: host-side cache TTL (default 30000)
|
|
29
|
+
* - updateCheck: check npm for newer versions (default true); result shows
|
|
30
|
+
* in the widget and the command — the plugin never installs
|
|
31
|
+
* itself, upgrading stays an explicit user action.
|
|
29
32
|
*/
|
|
30
33
|
|
|
31
34
|
import { readFileSync } from "node:fs";
|
|
@@ -43,12 +46,43 @@ export const namespace = "dsh-opencode-go-usage";
|
|
|
43
46
|
const DEFAULT_BASE_URL = "https://opencode.ai/zen/go";
|
|
44
47
|
const DEFAULT_API_KEY_ENV = "OPENCODE_GO_API_KEY";
|
|
45
48
|
const DEFAULT_CACHE_MS = 30_000;
|
|
49
|
+
/** npm package name this plugin is published under (update checks). */
|
|
50
|
+
const NPM_PACKAGE = "dsh-opencode-go-usage";
|
|
51
|
+
/** How often the update check may hit the npm registry (ms). */
|
|
52
|
+
const UPDATE_CHECK_INTERVAL_MS = 24 * 3600_000;
|
|
53
|
+
|
|
54
|
+
/** The version this running copy was installed as (read once at load). */
|
|
55
|
+
const CURRENT_VERSION = readPackageVersion();
|
|
56
|
+
|
|
57
|
+
/** Read `version` from the installed package.json next to this file. */
|
|
58
|
+
function readPackageVersion() {
|
|
59
|
+
try {
|
|
60
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
61
|
+
if (typeof pkg?.version === "string") return pkg.version;
|
|
62
|
+
} catch {
|
|
63
|
+
// fall through
|
|
64
|
+
}
|
|
65
|
+
return "0.0.0";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Compare two `x.y.z` version strings; returns true when `a` is newer than `b`. */
|
|
69
|
+
function isNewer(a, b) {
|
|
70
|
+
const pa = String(a).split("-")[0].split(".").map(Number);
|
|
71
|
+
const pb = String(b).split("-")[0].split(".").map(Number);
|
|
72
|
+
for (let i = 0; i < 3; i++) {
|
|
73
|
+
const na = pa[i] ?? 0;
|
|
74
|
+
const nb = pb[i] ?? 0;
|
|
75
|
+
if (na !== nb) return na > nb;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
46
79
|
|
|
47
80
|
/** Settings schema for this plugin's namespace. */
|
|
48
81
|
export const Config = z.object({
|
|
49
82
|
apiKeyEnv: z.string().default(DEFAULT_API_KEY_ENV),
|
|
50
83
|
baseUrl: z.string().default(DEFAULT_BASE_URL),
|
|
51
|
-
cacheMs: z.number().default(DEFAULT_CACHE_MS)
|
|
84
|
+
cacheMs: z.number().default(DEFAULT_CACHE_MS),
|
|
85
|
+
updateCheck: z.boolean().default(true)
|
|
52
86
|
});
|
|
53
87
|
|
|
54
88
|
/**
|
|
@@ -150,10 +184,50 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
150
184
|
apiKeyEnv: stored.apiKeyEnv,
|
|
151
185
|
baseUrl: stored.baseUrl,
|
|
152
186
|
cacheMs: stored.cacheMs,
|
|
187
|
+
updateCheck: stored.updateCheck !== false,
|
|
153
188
|
locale
|
|
154
189
|
};
|
|
155
190
|
};
|
|
156
191
|
|
|
192
|
+
// ── update check (npm) ─────────────────────────────────────────────────
|
|
193
|
+
// Checks the registry at most once per UPDATE_CHECK_INTERVAL_MS; failures
|
|
194
|
+
// are silent. The plugin only reports — upgrading stays a user action
|
|
195
|
+
// (`dsh plugin --profile web add dsh-opencode-go-usage@latest`).
|
|
196
|
+
/** @type {{ at: number, latest: string | null } | null} */
|
|
197
|
+
let updateState = null;
|
|
198
|
+
const checkForUpdate = async (config) => {
|
|
199
|
+
const now = Date.now();
|
|
200
|
+
if (updateState !== null && now - updateState.at < UPDATE_CHECK_INTERVAL_MS) return updateState;
|
|
201
|
+
const snapshot = { at: now, latest: null };
|
|
202
|
+
try {
|
|
203
|
+
const res = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE}/latest`, {
|
|
204
|
+
signal: AbortSignal.timeout(10_000)
|
|
205
|
+
});
|
|
206
|
+
if (res.ok) {
|
|
207
|
+
const json = await res.json();
|
|
208
|
+
if (typeof json?.version === "string" && isNewer(json.version, CURRENT_VERSION)) {
|
|
209
|
+
snapshot.latest = json.version;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
// offline / registry hiccup — keep the previous result, refresh later
|
|
214
|
+
snapshot.at = updateState?.at ?? now;
|
|
215
|
+
snapshot.latest = updateState?.latest ?? null;
|
|
216
|
+
}
|
|
217
|
+
updateState = snapshot;
|
|
218
|
+
return snapshot;
|
|
219
|
+
};
|
|
220
|
+
const updateInfoOf = (config) => {
|
|
221
|
+
const st = updateState;
|
|
222
|
+
if (!config.updateCheck || st === null || st.latest === null) {
|
|
223
|
+
return { current: CURRENT_VERSION, available: false, latest: null };
|
|
224
|
+
}
|
|
225
|
+
return { current: CURRENT_VERSION, available: true, latest: st.latest };
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Kick off the first check shortly after boot (non-blocking).
|
|
229
|
+
void checkForUpdate(readConfig()).catch(() => {});
|
|
230
|
+
|
|
157
231
|
// Host-side cache: one in-flight promise + a TTL, so several open tabs or
|
|
158
232
|
// the command never hammer the gateway.
|
|
159
233
|
/** @type {{ at: number, promise: Promise<unknown> } | null} */
|
|
@@ -180,8 +254,12 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
180
254
|
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
181
255
|
res.setHeader("cache-control", "no-store");
|
|
182
256
|
try {
|
|
257
|
+
const config = readConfig();
|
|
258
|
+
// Trigger a background refresh when the interval elapsed (never
|
|
259
|
+
// block the response on the registry).
|
|
260
|
+
void checkForUpdate(config).catch(() => {});
|
|
183
261
|
const data = await quotaOnce();
|
|
184
|
-
res.end(JSON.stringify(data));
|
|
262
|
+
res.end(JSON.stringify({ ...data, update: updateInfoOf(config) }));
|
|
185
263
|
} catch (error) {
|
|
186
264
|
res.statusCode = 502;
|
|
187
265
|
res.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
@@ -235,8 +313,16 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
235
313
|
description: "show OpenCode GO plan usage (rolling/weekly/monthly windows)",
|
|
236
314
|
handler: async () => {
|
|
237
315
|
try {
|
|
316
|
+
const config = readConfig();
|
|
238
317
|
const data = await quotaOnce();
|
|
239
|
-
|
|
318
|
+
let text = renderUsageText(data, config.locale);
|
|
319
|
+
const update = updateInfoOf(config);
|
|
320
|
+
if (update.available && update.latest !== null) {
|
|
321
|
+
text += config.locale === "en"
|
|
322
|
+
? `\n\nUpdate available: v${update.latest} — run \`dsh plugin --profile web add ${NPM_PACKAGE}@latest\` to upgrade.`
|
|
323
|
+
: `\n\n发现新版本 v${update.latest} — 执行 \`dsh plugin --profile web add ${NPM_PACKAGE}@latest\` 升级。`;
|
|
324
|
+
}
|
|
325
|
+
return { kind: "success", text };
|
|
240
326
|
} catch (error) {
|
|
241
327
|
return { kind: "error", text: error instanceof Error ? error.message : String(error) };
|
|
242
328
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-opencode-go-usage",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "DSH (DeepSeek Harness) plugin: OpenCode GO plan quota widget in the sidebar, same-origin usage proxy, and a /opencode-go chat command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|