opencode-go-usage-tui 1.0.1 → 1.2.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/dist/tui.js CHANGED
@@ -9,44 +9,245 @@ import { insertNode as _$insertNode } from "@opentui/solid";
9
9
  import { insert as _$insert } from "@opentui/solid";
10
10
  import { createElement as _$createElement } from "@opentui/solid";
11
11
  import { createSignal, createEffect, onMount, onCleanup, Show } from "solid-js";
12
- import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
+ import { readFileSync, writeFileSync, mkdirSync, renameSync, existsSync } from "node:fs";
13
13
  import { join, dirname } from "node:path";
14
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
15
+
16
+ // src/i18n.ts
17
+ var ZH_T = {
18
+ panelTitle: "Go \u7528\u91CF",
19
+ rowRolling: "5\u5C0F\u65F6\u7528\u91CF",
20
+ rowWeekly: "\u6BCF\u5468\u7528\u91CF",
21
+ rowMonthly: "\u6BCF\u6708\u7528\u91CF",
22
+ weeklyShort: "\u5468",
23
+ updatedAt: "\u6700\u8FD1\u66F4\u65B0",
24
+ notConfigured: "\u672A\u914D\u7F6E\u8D26\u53F7",
25
+ notConfiguredHint: "\u8F93\u5165 /go-config \u8BBE\u7F6E",
26
+ loading: "\u52A0\u8F7D\u4E2D...",
27
+ queryFailed: "\u67E5\u8BE2\u5931\u8D25",
28
+ cookieExpired: "cookie \u5DF2\u8FC7\u671F",
29
+ reconfigureHint: "\u8F93\u5165 /go-config \u91CD\u65B0\u914D\u7F6E",
30
+ resetDone: "\u5DF2\u91CD\u7F6E",
31
+ dayHour: "{d}\u5929{h}\u5C0F\u65F6",
32
+ hourMin: "{h}\u5C0F\u65F6{m}\u5206\u949F",
33
+ minute: "{m}\u5206\u949F",
34
+ cmdTitle: "Go Usage: Configure",
35
+ cmdDesc: "\u8BBE\u7F6E OpenCode Go workspace ID \u548C auth cookie",
36
+ wsPromptTitle: "\u8F93\u5165 workspace_id",
37
+ wsPromptDesc: "\u5728 opencode.ai \u63A7\u5236\u53F0 URL \u4E2D\u83B7\u53D6\uFF08wrk_ \u5F00\u5934\uFF09",
38
+ wsPlaceholder: "wrk_xxxxxxxxxxxx",
39
+ cookiePromptTitle: "\u8F93\u5165 auth cookie",
40
+ cookiePromptDesc: "\u767B\u5F55 opencode.ai \u540E\uFF0C\u6D4F\u89C8\u5668 F12 \u2192 Application \u2192 Cookies \u2192 opencode.ai \u2192 \u590D\u5236 auth \u7684\u503C",
41
+ cookiePlaceholder: "Fe26.2*...",
42
+ configSaved: "Go \u7528\u91CF\u914D\u7F6E\u5DF2\u4FDD\u5B58",
43
+ langTitle: "\u663E\u793A\u8BED\u8A00",
44
+ langCmdTitle: "Go Usage: Language",
45
+ langCmdDesc: "\u5207\u6362\u663E\u793A\u8BED\u8A00 | Switch display language",
46
+ langSwitchedZh: "\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587",
47
+ langSwitchedEn: "Switched to English",
48
+ onboardLangTitle: "Welcome to Go Usage / \u6B22\u8FCE\u4F7F\u7528 Go \u7528\u91CF",
49
+ onboardLangDesc: "Choose display language / \u9009\u62E9\u663E\u793A\u8BED\u8A00",
50
+ onboardConfigTitle: "Welcome to Go Usage / \u6B22\u8FCE\u4F7F\u7528 Go \u7528\u91CF",
51
+ onboardConfigDesc: "First-time setup / \u9996\u6B21\u4F7F\u7528\u9700\u914D\u7F6E\u8D26\u53F7",
52
+ setupCanceled: "\u5DF2\u53D6\u6D88\u914D\u7F6E\uFF0C\u8F93\u5165 /go-config \u53EF\u91CD\u65B0\u8BBE\u7F6E"
53
+ };
54
+ var EN_T = {
55
+ panelTitle: "Go Usage",
56
+ rowRolling: "5h usage",
57
+ rowWeekly: "Weekly",
58
+ rowMonthly: "Monthly",
59
+ weeklyShort: "Wk",
60
+ updatedAt: "Updated",
61
+ notConfigured: "Not configured",
62
+ notConfiguredHint: "Run /go-config to set up",
63
+ loading: "Loading...",
64
+ queryFailed: "Query failed",
65
+ cookieExpired: "Cookie expired",
66
+ reconfigureHint: "Run /go-config to reconfigure",
67
+ resetDone: "Reset",
68
+ dayHour: "{d}d {h}h",
69
+ hourMin: "{h}h {m}m",
70
+ minute: "{m}m",
71
+ cmdTitle: "Go Usage: Configure",
72
+ cmdDesc: "Set OpenCode Go workspace ID and auth cookie",
73
+ wsPromptTitle: "Enter workspace_id",
74
+ wsPromptDesc: "Get it from the opencode.ai console URL (starts with wrk_)",
75
+ wsPlaceholder: "wrk_xxxxxxxxxxxx",
76
+ cookiePromptTitle: "Enter auth cookie",
77
+ cookiePromptDesc: "After logging into opencode.ai, press F12 \u2192 Application \u2192 Cookies \u2192 opencode.ai \u2192 copy the auth value",
78
+ cookiePlaceholder: "Fe26.2*...",
79
+ configSaved: "Go usage config saved",
80
+ langTitle: "Display language",
81
+ langCmdTitle: "Go Usage: Language",
82
+ langCmdDesc: "\u5207\u6362\u663E\u793A\u8BED\u8A00 | Switch display language",
83
+ langSwitchedZh: "\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587",
84
+ langSwitchedEn: "Switched to English",
85
+ onboardLangTitle: "Welcome to Go Usage / \u6B22\u8FCE\u4F7F\u7528 Go \u7528\u91CF",
86
+ onboardLangDesc: "Choose display language / \u9009\u62E9\u663E\u793A\u8BED\u8A00",
87
+ onboardConfigTitle: "Welcome to Go Usage / \u6B22\u8FCE\u4F7F\u7528 Go \u7528\u91CF",
88
+ onboardConfigDesc: "First-time setup / \u9996\u6B21\u4F7F\u7528\u9700\u914D\u7F6E\u8D26\u53F7",
89
+ setupCanceled: "Setup canceled, run /go-config to reconfigure"
90
+ };
91
+ var LANGS = { zh: ZH_T, en: EN_T };
92
+ var LANG_META = [
93
+ { code: "zh", label: "\u4E2D\u6587" },
94
+ { code: "en", label: "English" }
95
+ ];
96
+ function applyParams(tpl, params) {
97
+ if (!params) return tpl;
98
+ return tpl.replace(
99
+ /\{(\w+)\}/g,
100
+ (m, k) => k in params ? String(params[k]) : m
101
+ );
102
+ }
103
+ function createT(getCode) {
104
+ return (key, params) => applyParams(LANGS[getCode()][key], params);
105
+ }
106
+ function detectLang() {
107
+ try {
108
+ const loc = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase();
109
+ if (loc.startsWith("zh")) return "zh";
110
+ return "en";
111
+ } catch {
112
+ return "en";
113
+ }
114
+ }
115
+
116
+ // src/index.tsx
117
+ var VERSION = "1.2.0";
14
118
  var CHECK_INTERVAL = Number(process?.env?.OPENCODE_GO_CHECK_INTERVAL ?? 6e4);
15
119
  var WARN_THRESHOLD = Number(process?.env?.OPENCODE_GO_WARN_THRESHOLD ?? 0.8);
16
120
  var CONFIG_DIR = process?.env?.OPENCODE_CONFIG_DIR || (process?.env?.USERPROFILE ? `${process.env.USERPROFILE}\\.config\\opencode` : "") || process?.env?.HOME + "/.config/opencode";
17
121
  var CONFIG_FILE = join(CONFIG_DIR, "go-usage-config.json");
18
- function loadConfig() {
19
- const env = process?.env ?? {};
20
- let fileCfg = {};
122
+ var COOKIE_ENC_FILE = join(CONFIG_DIR, "go-auth-cookie.enc");
123
+ var KEY_FILE = join(CONFIG_DIR, ".encryption-key");
124
+ var COOKIE_PLAIN_FILE = `${CONFIG_DIR}\\go-auth-cookie.txt`;
125
+ var KV_PREFIX = "go-usage";
126
+ function loadEncryptionKey() {
127
+ try {
128
+ const hex2 = readFileSync(KEY_FILE, "utf8").trim();
129
+ if (hex2) return Buffer.from(hex2, "hex");
130
+ } catch {
131
+ }
132
+ const key = randomBytes(32);
133
+ mkdirSync(dirname(KEY_FILE), {
134
+ recursive: true
135
+ });
136
+ writeFileSync(KEY_FILE, key.toString("hex"), {
137
+ encoding: "utf8",
138
+ mode: 384
139
+ });
140
+ return key;
141
+ }
142
+ function encryptText(text) {
143
+ const key = loadEncryptionKey();
144
+ const iv = randomBytes(16);
145
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
146
+ const data = cipher.update(text, "utf8", "hex") + cipher.final("hex");
147
+ return JSON.stringify({
148
+ v: 1,
149
+ iv: iv.toString("hex"),
150
+ data,
151
+ tag: cipher.getAuthTag().toString("hex")
152
+ });
153
+ }
154
+ function decryptText(encJson) {
155
+ const {
156
+ iv,
157
+ data,
158
+ tag
159
+ } = JSON.parse(encJson);
160
+ const key = loadEncryptionKey();
161
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"));
162
+ decipher.setAuthTag(Buffer.from(tag, "hex"));
163
+ return decipher.update(data, "hex", "utf8") + decipher.final("utf8");
164
+ }
165
+ function readJsonFile(file) {
21
166
  try {
22
- fileCfg = JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
167
+ return JSON.parse(readFileSync(file, "utf8"));
23
168
  } catch {
24
- fileCfg = {};
169
+ return null;
170
+ }
171
+ }
172
+ function loadConfig() {
173
+ const env = process?.env ?? {};
174
+ const fileCfg = readJsonFile(CONFIG_FILE) ?? {};
175
+ if (env.OPENCODE_GO_AUTH_COOKIE) {
176
+ return {
177
+ workspaceId: env.OPENCODE_GO_WORKSPACE_ID || fileCfg.workspace_id || "",
178
+ authCookie: env.OPENCODE_GO_AUTH_COOKIE
179
+ };
25
180
  }
26
- const cookieFile = env.OPENCODE_GO_COOKIE_FILE || fileCfg.cookie_file || `${CONFIG_DIR}\\go-auth-cookie.txt`;
27
- let fileCookie = "";
28
181
  try {
29
- let p = cookieFile;
30
- if (p === "~") p = (env.HOME || env.USERPROFILE) + "";
31
- else if (p.startsWith("~/")) p = (env.HOME || env.USERPROFILE || "") + p.slice(1);
32
- fileCookie = readFileSync(p, "utf8").trim();
182
+ if (existsSync(COOKIE_ENC_FILE)) {
183
+ const cookie = decryptText(readFileSync(COOKIE_ENC_FILE, "utf8"));
184
+ if (cookie) {
185
+ return {
186
+ workspaceId: env.OPENCODE_GO_WORKSPACE_ID || fileCfg.workspace_id || "",
187
+ authCookie: cookie
188
+ };
189
+ }
190
+ }
33
191
  } catch {
34
- fileCookie = "";
192
+ }
193
+ const legacyCookie = fileCfg.auth_cookie || fileCfg.cookie || (() => {
194
+ try {
195
+ return readFileSync(COOKIE_PLAIN_FILE, "utf8").trim();
196
+ } catch {
197
+ return "";
198
+ }
199
+ })();
200
+ if (legacyCookie) {
201
+ const enc = encryptText(legacyCookie);
202
+ mkdirSync(CONFIG_DIR, {
203
+ recursive: true
204
+ });
205
+ writeFileSync(COOKIE_ENC_FILE, enc, "utf8");
206
+ const cleanCfg = {
207
+ ...fileCfg
208
+ };
209
+ delete cleanCfg.cookie;
210
+ delete cleanCfg.auth_cookie;
211
+ try {
212
+ writeFileSync(CONFIG_FILE, JSON.stringify(cleanCfg, null, 2), "utf8");
213
+ } catch {
214
+ }
215
+ try {
216
+ if (existsSync(COOKIE_PLAIN_FILE)) renameSync(COOKIE_PLAIN_FILE, COOKIE_PLAIN_FILE + ".bak");
217
+ } catch {
218
+ }
219
+ return {
220
+ workspaceId: env.OPENCODE_GO_WORKSPACE_ID || fileCfg.workspace_id || "",
221
+ authCookie: legacyCookie
222
+ };
35
223
  }
36
224
  return {
37
225
  workspaceId: env.OPENCODE_GO_WORKSPACE_ID || fileCfg.workspace_id || "",
38
- authCookie: env.OPENCODE_GO_AUTH_COOKIE || fileCfg.auth_cookie || fileCfg.cookie || fileCookie
226
+ authCookie: ""
39
227
  };
40
228
  }
41
229
  function saveConfig(patch) {
42
- let fileCfg = {};
43
- try {
44
- fileCfg = JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
45
- } catch {
46
- fileCfg = {};
47
- }
230
+ const fileCfg = readJsonFile(CONFIG_FILE) ?? {};
48
231
  if (patch.workspace_id !== void 0) fileCfg.workspace_id = patch.workspace_id;
49
- if (patch.cookie !== void 0) fileCfg.cookie = patch.cookie;
232
+ if (patch.cookie !== void 0) {
233
+ if (patch.cookie) {
234
+ mkdirSync(CONFIG_DIR, {
235
+ recursive: true
236
+ });
237
+ writeFileSync(COOKIE_ENC_FILE, encryptText(patch.cookie), "utf8");
238
+ delete fileCfg.cookie;
239
+ delete fileCfg.auth_cookie;
240
+ try {
241
+ if (existsSync(COOKIE_PLAIN_FILE)) renameSync(COOKIE_PLAIN_FILE, COOKIE_PLAIN_FILE + ".bak");
242
+ } catch {
243
+ }
244
+ } else {
245
+ try {
246
+ if (existsSync(COOKIE_ENC_FILE)) renameSync(COOKIE_ENC_FILE, COOKIE_ENC_FILE + ".bak");
247
+ } catch {
248
+ }
249
+ }
250
+ }
50
251
  try {
51
252
  mkdirSync(dirname(CONFIG_FILE), {
52
253
  recursive: true
@@ -56,6 +257,21 @@ function saveConfig(patch) {
56
257
  }
57
258
  }
58
259
  var [configTick, setConfigTick] = createSignal(0);
260
+ var [langCode, setLangCode] = createSignal(detectLang());
261
+ var t = createT(() => langCode());
262
+ function kvTryGet(api, key) {
263
+ try {
264
+ return api.kv.get(key);
265
+ } catch {
266
+ return void 0;
267
+ }
268
+ }
269
+ function kvTrySet(api, key, val) {
270
+ try {
271
+ api.kv.set(key, val);
272
+ } catch {
273
+ }
274
+ }
59
275
  async function fetchUsage(workspaceId, authCookie) {
60
276
  if (!workspaceId || !authCookie) return {
61
277
  error: "not_configured"
@@ -119,13 +335,21 @@ async function fetchUsage(workspaceId, authCookie) {
119
335
  }
120
336
  }
121
337
  function formatReset(sec) {
122
- if (sec <= 0) return "\u5DF2\u91CD\u7F6E";
338
+ if (sec <= 0) return t("resetDone");
123
339
  const d = Math.floor(sec / 86400);
124
340
  const h = Math.floor(sec % 86400 / 3600);
125
341
  const m = Math.floor(sec % 3600 / 60);
126
- if (d > 0) return `${d}\u5929${h}\u5C0F\u65F6`;
127
- if (h > 0) return `${h}\u5C0F\u65F6${m}\u5206\u949F`;
128
- return `${m}\u5206\u949F`;
342
+ if (d > 0) return t("dayHour", {
343
+ d,
344
+ h
345
+ });
346
+ if (h > 0) return t("hourMin", {
347
+ h,
348
+ m
349
+ });
350
+ return t("minute", {
351
+ m
352
+ });
129
353
  }
130
354
  function progressBar(percent, width) {
131
355
  const clamped = Math.max(0, Math.min(100, percent));
@@ -177,11 +401,11 @@ function GoUsagePanel(props) {
177
401
  setConfigured(true);
178
402
  const u = await fetchUsage(cfg.workspaceId, cfg.authCookie);
179
403
  if (u === null) {
180
- setError("\u67E5\u8BE2\u5931\u8D25");
404
+ setError(t("queryFailed"));
181
405
  return;
182
406
  }
183
407
  if (u.error === "cookie_expired") {
184
- setError("cookie \u5DF2\u8FC7\u671F");
408
+ setError(t("cookieExpired"));
185
409
  return;
186
410
  }
187
411
  if (u.error === "not_configured") {
@@ -208,12 +432,12 @@ function GoUsagePanel(props) {
208
432
  ...FALLBACK
209
433
  });
210
434
  createEffect(() => {
211
- const t = props.theme;
435
+ const th = props.theme;
212
436
  const p = {
213
437
  ...FALLBACK
214
438
  };
215
439
  for (const k of Object.keys(FALLBACK)) {
216
- const h = hex(t?.[k]);
440
+ const h = hex(th?.[k]);
217
441
  if (h) p[k] = h;
218
442
  }
219
443
  setPal(p);
@@ -271,7 +495,7 @@ function GoUsagePanel(props) {
271
495
  };
272
496
  const sep = () => "\u2500".repeat(Math.max(1, panelWidth() - 4));
273
497
  return (() => {
274
- var _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$11 = _$createElement("span"), _el$12 = _$createElement("span"), _el$13 = _$createElement("b");
498
+ var _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$11 = _$createElement("span"), _el$12 = _$createElement("span"), _el$13 = _$createElement("b"), _el$14 = _$createElement("span"), _el$15 = _$createTextNode(` v`);
275
499
  _$insertNode(_el$1, _el$10);
276
500
  var _ref$ = boxEl;
277
501
  typeof _ref$ === "function" ? _$use(_ref$, _el$1) : boxEl = _el$1;
@@ -286,23 +510,28 @@ function GoUsagePanel(props) {
286
510
  });
287
511
  _$insertNode(_el$10, _el$11);
288
512
  _$insertNode(_el$10, _el$12);
513
+ _$insertNode(_el$10, _el$14);
289
514
  _$setProp(_el$10, "onMouseUp", () => setOpen((o) => !o));
290
515
  _$insert(_el$11, () => open() ? "\u25BC " : "\u25B6 ");
291
516
  _$insertNode(_el$12, _el$13);
292
- _$insertNode(_el$13, _$createTextNode(`Go \u7528\u91CF`));
517
+ _$insert(_el$13, () => t("panelTitle"));
518
+ _$insertNode(_el$14, _el$15);
519
+ _$insert(_el$14, VERSION, null);
293
520
  _$insert(_el$10, _$createComponent(Show, {
294
521
  get when() {
295
522
  return _$memo(() => !!!open())() && usage();
296
523
  },
297
524
  get children() {
298
- var _el$15 = _$createElement("span"), _el$16 = _$createTextNode(` \u5468 `), _el$18 = _$createTextNode(`%`);
299
- _$insertNode(_el$15, _el$16);
300
- _$insertNode(_el$15, _el$18);
301
- _$insert(_el$15, () => usage().weeklyUsage.usagePercent, _el$18);
302
- _$effect((_$p) => _$setProp(_el$15, "style", {
525
+ var _el$16 = _$createElement("span"), _el$17 = _$createTextNode(` `), _el$18 = _$createTextNode(` `), _el$19 = _$createTextNode(`%`);
526
+ _$insertNode(_el$16, _el$17);
527
+ _$insertNode(_el$16, _el$18);
528
+ _$insertNode(_el$16, _el$19);
529
+ _$insert(_el$16, () => t("weeklyShort"), _el$18);
530
+ _$insert(_el$16, () => usage().weeklyUsage.usagePercent, _el$19);
531
+ _$effect((_$p) => _$setProp(_el$16, "style", {
303
532
  fg: colorFor(usage().weeklyUsage.usagePercent)
304
533
  }, _$p));
305
- return _el$15;
534
+ return _el$16;
306
535
  }
307
536
  }), null);
308
537
  _$insert(_el$1, _$createComponent(Show, {
@@ -311,23 +540,23 @@ function GoUsagePanel(props) {
311
540
  },
312
541
  get children() {
313
542
  return [(() => {
314
- var _el$19 = _$createElement("text");
315
- _$insert(_el$19, sep);
316
- _$effect((_$p) => _$setProp(_el$19, "fg", colors().muted, _$p));
317
- return _el$19;
543
+ var _el$20 = _$createElement("text");
544
+ _$insert(_el$20, sep);
545
+ _$effect((_$p) => _$setProp(_el$20, "fg", colors().muted, _$p));
546
+ return _el$20;
318
547
  })(), _$createComponent(Show, {
319
548
  get when() {
320
549
  return !configured();
321
550
  },
322
551
  get children() {
323
552
  return [(() => {
324
- var _el$20 = _$createElement("text");
325
- _$insertNode(_el$20, _$createTextNode(`\u672A\u914D\u7F6E\u8D26\u53F7`));
326
- _$effect((_$p) => _$setProp(_el$20, "fg", colors().warning, _$p));
327
- return _el$20;
553
+ var _el$21 = _$createElement("text");
554
+ _$insert(_el$21, () => t("notConfigured"));
555
+ _$effect((_$p) => _$setProp(_el$21, "fg", colors().warning, _$p));
556
+ return _el$21;
328
557
  })(), (() => {
329
558
  var _el$22 = _$createElement("text");
330
- _$insertNode(_el$22, _$createTextNode(`\u8F93\u5165 /go-config \u8BBE\u7F6E`));
559
+ _$insert(_el$22, () => t("notConfiguredHint"));
331
560
  _$effect((_$p) => _$setProp(_el$22, "fg", colors().muted, _$p));
332
561
  return _el$22;
333
562
  })()];
@@ -347,49 +576,50 @@ function GoUsagePanel(props) {
347
576
  return configured();
348
577
  },
349
578
  get children() {
350
- var _el$31 = _$createElement("text");
351
- _$insertNode(_el$31, _$createTextNode(`\u52A0\u8F7D\u4E2D...`));
352
- _$effect((_$p) => _$setProp(_el$31, "fg", colors().muted, _$p));
353
- return _el$31;
579
+ var _el$29 = _$createElement("text");
580
+ _$insert(_el$29, () => t("loading"));
581
+ _$effect((_$p) => _$setProp(_el$29, "fg", colors().muted, _$p));
582
+ return _el$29;
354
583
  }
355
584
  });
356
585
  },
357
586
  get children() {
358
- return [_$memo(() => renderRow("5\u5C0F\u65F6\u7528\u91CF", usage().rollingUsage)), _$memo(() => renderRow("\u6BCF\u5468\u7528\u91CF", usage().weeklyUsage)), _$memo(() => renderRow("\u6BCF\u6708\u7528\u91CF", usage().monthlyUsage)), (() => {
359
- var _el$27 = _$createElement("text"), _el$28 = _$createElement("span"), _el$30 = _$createElement("span");
360
- _$insertNode(_el$27, _el$28);
361
- _$insertNode(_el$27, _el$30);
362
- _$insertNode(_el$28, _$createTextNode(`\u6700\u8FD1\u66F4\u65B0 `));
363
- _$insert(_el$30, lastUpdated);
587
+ return [_$memo(() => renderRow(t("rowRolling"), usage().rollingUsage)), _$memo(() => renderRow(t("rowWeekly"), usage().weeklyUsage)), _$memo(() => renderRow(t("rowMonthly"), usage().monthlyUsage)), (() => {
588
+ var _el$25 = _$createElement("text"), _el$26 = _$createElement("span"), _el$27 = _$createTextNode(` `), _el$28 = _$createElement("span");
589
+ _$insertNode(_el$25, _el$26);
590
+ _$insertNode(_el$25, _el$28);
591
+ _$insertNode(_el$26, _el$27);
592
+ _$insert(_el$26, () => t("updatedAt"), _el$27);
593
+ _$insert(_el$28, lastUpdated);
364
594
  _$effect((_p$) => {
365
- var _v$8 = {
595
+ var _v$9 = {
366
596
  fg: colors().muted
367
- }, _v$9 = {
597
+ }, _v$0 = {
368
598
  fg: colors().muted
369
599
  };
370
- _v$8 !== _p$.e && (_p$.e = _$setProp(_el$28, "style", _v$8, _p$.e));
371
- _v$9 !== _p$.t && (_p$.t = _$setProp(_el$30, "style", _v$9, _p$.t));
600
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$26, "style", _v$9, _p$.e));
601
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$28, "style", _v$0, _p$.t));
372
602
  return _p$;
373
603
  }, {
374
604
  e: void 0,
375
605
  t: void 0
376
606
  });
377
- return _el$27;
607
+ return _el$25;
378
608
  })()];
379
609
  }
380
610
  });
381
611
  },
382
612
  get children() {
383
613
  return [(() => {
614
+ var _el$23 = _$createElement("text");
615
+ _$insert(_el$23, error);
616
+ _$effect((_$p) => _$setProp(_el$23, "fg", colors().error, _$p));
617
+ return _el$23;
618
+ })(), (() => {
384
619
  var _el$24 = _$createElement("text");
385
- _$insert(_el$24, error);
386
- _$effect((_$p) => _$setProp(_el$24, "fg", colors().error, _$p));
620
+ _$insert(_el$24, () => t("reconfigureHint"));
621
+ _$effect((_$p) => _$setProp(_el$24, "fg", colors().muted, _$p));
387
622
  return _el$24;
388
- })(), (() => {
389
- var _el$25 = _$createElement("text");
390
- _$insertNode(_el$25, _$createTextNode(`\u8F93\u5165 /go-config \u91CD\u65B0\u914D\u7F6E`));
391
- _$effect((_$p) => _$setProp(_el$25, "fg", colors().muted, _$p));
392
- return _el$25;
393
623
  })()];
394
624
  }
395
625
  })];
@@ -400,15 +630,19 @@ function GoUsagePanel(props) {
400
630
  fg: colors().muted
401
631
  }, _v$7 = {
402
632
  fg: colors().primary
633
+ }, _v$8 = {
634
+ fg: colors().muted
403
635
  };
404
636
  _v$5 !== _p$.e && (_p$.e = _$setProp(_el$1, "borderColor", _v$5, _p$.e));
405
637
  _v$6 !== _p$.t && (_p$.t = _$setProp(_el$11, "style", _v$6, _p$.t));
406
638
  _v$7 !== _p$.a && (_p$.a = _$setProp(_el$12, "style", _v$7, _p$.a));
639
+ _v$8 !== _p$.o && (_p$.o = _$setProp(_el$14, "style", _v$8, _p$.o));
407
640
  return _p$;
408
641
  }, {
409
642
  e: void 0,
410
643
  t: void 0,
411
- a: void 0
644
+ a: void 0,
645
+ o: void 0
412
646
  });
413
647
  return _el$1;
414
648
  })();
@@ -428,63 +662,121 @@ function createSidebarSlot(api) {
428
662
  }
429
663
  };
430
664
  }
431
- var tui = async (api) => {
432
- api.slots.register(createSidebarSlot(api));
433
- api.command?.register(() => [{
434
- title: "Go Usage: Configure",
435
- value: "go-usage.config",
436
- description: "\u8BBE\u7F6E OpenCode Go workspace ID \u548C auth cookie",
437
- slash: {
438
- name: "go-config"
665
+ function runConfigDialog(api, dialog) {
666
+ dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
667
+ get title() {
668
+ return t("wsPromptTitle");
669
+ },
670
+ description: () => (() => {
671
+ var _el$30 = _$createElement("text");
672
+ _$insert(_el$30, () => t("wsPromptDesc"));
673
+ return _el$30;
674
+ })(),
675
+ get placeholder() {
676
+ return t("wsPlaceholder");
439
677
  },
440
- onSelect: (dialog) => {
678
+ onConfirm: (value) => {
679
+ const wsId = value.trim();
680
+ if (!wsId) {
681
+ dialog?.clear();
682
+ return;
683
+ }
684
+ saveConfig({
685
+ workspace_id: wsId
686
+ });
441
687
  dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
442
- title: "\u8F93\u5165 workspace_id",
688
+ get title() {
689
+ return t("cookiePromptTitle");
690
+ },
443
691
  description: () => (() => {
444
- var _el$33 = _$createElement("text");
445
- _$insertNode(_el$33, _$createTextNode(`\u5728 opencode.ai \u63A7\u5236\u53F0 URL \u4E2D\u83B7\u53D6\uFF08wrk_ \u5F00\u5934\uFF09`));
446
- return _el$33;
692
+ var _el$31 = _$createElement("text");
693
+ _$insert(_el$31, () => t("cookiePromptDesc"));
694
+ return _el$31;
447
695
  })(),
448
- placeholder: "wrk_xxxxxxxxxxxx",
449
- onConfirm: (value) => {
450
- const wsId = value.trim();
451
- if (!wsId) {
696
+ get placeholder() {
697
+ return t("cookiePlaceholder");
698
+ },
699
+ onConfirm: (val) => {
700
+ const cookie = val.trim();
701
+ if (!cookie) {
452
702
  dialog?.clear();
453
703
  return;
454
704
  }
455
705
  saveConfig({
456
- workspace_id: wsId
706
+ cookie
457
707
  });
458
- dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
459
- title: "\u8F93\u5165 auth cookie",
460
- description: () => (() => {
461
- var _el$35 = _$createElement("text");
462
- _$insertNode(_el$35, _$createTextNode(`\u767B\u5F55 opencode.ai \u540E\uFF0C\u6D4F\u89C8\u5668 F12 \u2192 Application \u2192 Cookies \u2192 opencode.ai \u2192 \u590D\u5236 auth \u7684\u503C`));
463
- return _el$35;
464
- })(),
465
- placeholder: "Fe26.2*...",
466
- onConfirm: (val) => {
467
- const cookie = val.trim();
468
- if (!cookie) {
469
- dialog?.clear();
470
- return;
471
- }
472
- saveConfig({
473
- cookie
474
- });
475
- setConfigTick((v) => v + 1);
476
- api.ui.toast({
477
- variant: "success",
478
- message: "Go \u7528\u91CF\u914D\u7F6E\u5DF2\u4FDD\u5B58"
479
- });
480
- dialog?.clear();
481
- },
482
- onCancel: () => dialog?.clear()
483
- }));
708
+ setConfigTick((v) => v + 1);
709
+ api.ui.toast({
710
+ variant: "success",
711
+ message: t("configSaved")
712
+ });
713
+ dialog?.clear();
484
714
  },
485
715
  onCancel: () => dialog?.clear()
486
716
  }));
487
- }
717
+ },
718
+ onCancel: () => dialog?.clear()
719
+ }));
720
+ }
721
+ function runLangDialog(api, dialog) {
722
+ dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
723
+ get title() {
724
+ return t("langTitle");
725
+ },
726
+ get options() {
727
+ return LANG_META.map((m) => ({
728
+ title: `${m.label}${langCode() === m.code ? " \u2713" : ""}`,
729
+ value: m.code
730
+ }));
731
+ },
732
+ onSelect: (opt) => {
733
+ const code = opt.value;
734
+ setLangCode(code);
735
+ kvTrySet(api, `${KV_PREFIX}.lang`, code);
736
+ api.ui.toast({
737
+ message: code === "zh" ? t("langSwitchedZh") : t("langSwitchedEn")
738
+ });
739
+ const cfg = loadConfig();
740
+ if (!cfg.workspaceId || !cfg.authCookie) {
741
+ runConfigDialog(api, dialog);
742
+ } else {
743
+ dialog?.clear();
744
+ }
745
+ },
746
+ onCancel: () => dialog?.clear()
747
+ }));
748
+ }
749
+ var tui = async (api) => {
750
+ api.slots.register(createSidebarSlot(api));
751
+ const savedLang = kvTryGet(api, `${KV_PREFIX}.lang`);
752
+ if (savedLang === "zh" || savedLang === "en") setLangCode(savedLang);
753
+ else setLangCode(detectLang());
754
+ const onboarded = kvTryGet(api, `${KV_PREFIX}.onboarded`);
755
+ if (!onboarded) {
756
+ kvTrySet(api, `${KV_PREFIX}.onboarded`, "1");
757
+ setTimeout(() => {
758
+ try {
759
+ api.command.trigger("go-usage.lang");
760
+ } catch {
761
+ }
762
+ }, 1500);
763
+ }
764
+ api.command?.register(() => [{
765
+ title: t("cmdTitle"),
766
+ value: "go-usage.config",
767
+ description: t("cmdDesc"),
768
+ slash: {
769
+ name: "go-config"
770
+ },
771
+ onSelect: (dialog) => runConfigDialog(api, dialog)
772
+ }, {
773
+ title: t("langCmdTitle"),
774
+ value: "go-usage.lang",
775
+ description: t("langCmdDesc"),
776
+ slash: {
777
+ name: "go-lang"
778
+ },
779
+ onSelect: (dialog) => runLangDialog(api, dialog)
488
780
  }]);
489
781
  };
490
782
  var mod = {