@jeffreyjyz/opencode-command-code 0.2.2 → 0.2.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/dist/index.js CHANGED
@@ -544,6 +544,65 @@ async function loadModels(key) {
544
544
  return { claude, open };
545
545
  }
546
546
 
547
+ // src/usagelog.ts
548
+ import { appendFileSync, mkdirSync } from "fs";
549
+ import { homedir as homedir2 } from "os";
550
+ import { dirname, join } from "path";
551
+ function usageLogPath() {
552
+ if (process.env.MPC_USAGE_LOG)
553
+ return process.env.MPC_USAGE_LOG;
554
+ const base = process.env.XDG_CACHE_HOME ?? join(homedir2(), ".cache");
555
+ return join(base, "mpc", "usage.jsonl");
556
+ }
557
+ var logged = new Set;
558
+ var MAX_SEEN = 5000;
559
+ function numberOr(value) {
560
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
561
+ }
562
+ function logUsage(info) {
563
+ try {
564
+ if (!info || info.role !== "assistant" || !info.tokens)
565
+ return;
566
+ const completed = info.time?.completed;
567
+ if (!completed)
568
+ return;
569
+ if (info.id) {
570
+ if (logged.has(info.id))
571
+ return;
572
+ if (logged.size >= MAX_SEEN)
573
+ logged.clear();
574
+ logged.add(info.id);
575
+ }
576
+ const input = numberOr(info.tokens.input);
577
+ const output = numberOr(info.tokens.output);
578
+ const cacheRead = numberOr(info.tokens.cache?.read);
579
+ const cacheWrite = numberOr(info.tokens.cache?.write);
580
+ if (input + output + cacheRead + cacheWrite === 0)
581
+ return;
582
+ const line = JSON.stringify({
583
+ ts: new Date(completed).toISOString(),
584
+ provider: info.providerID,
585
+ model: info.modelID,
586
+ input,
587
+ cacheRead,
588
+ cacheWrite,
589
+ output,
590
+ costUsd: numberOr(info.cost),
591
+ messageID: info.id
592
+ });
593
+ const path = usageLogPath();
594
+ mkdirSync(dirname(path), { recursive: true });
595
+ appendFileSync(path, `${line}
596
+ `);
597
+ } catch {}
598
+ }
599
+ function logEvent(event) {
600
+ const e = event;
601
+ if (e?.type !== "message.updated")
602
+ return;
603
+ logUsage(e.properties?.info);
604
+ }
605
+
547
606
  // src/v2.ts
548
607
  import { Plugin } from "@opencode/plugin";
549
608
  var PLUGIN_ID = "command-code";
@@ -619,6 +678,10 @@ var commandCodeV2 = Plugin.define({
619
678
  if (!localKey && !hasConnection) {
620
679
  console.warn(`[command-code] no API key found. Run /connect and choose "${INTEGRATION_NAME}", or \`cmd login\` (~/.commandcode/auth.json), or set CMD_API_KEY.`);
621
680
  }
681
+ try {
682
+ const events = ctx.event;
683
+ events?.subscribe?.((event) => logEvent(event));
684
+ } catch {}
622
685
  await ctx.integration.transform((editor) => {
623
686
  editor.update(INTEGRATION_ID, (integration) => {
624
687
  if (integration.name === integration.id) {
@@ -676,11 +739,15 @@ var commandCodeV2 = Plugin.define({
676
739
  });
677
740
  });
678
741
  const controller = new AbortController;
679
- (async () => {
680
- const key = await credentialKey(ctx);
681
- if (!key)
742
+ let refreshing = false;
743
+ const refresh = async () => {
744
+ if (refreshing || controller.signal.aborted)
682
745
  return;
746
+ refreshing = true;
683
747
  try {
748
+ const key = await credentialKey(ctx);
749
+ if (!key)
750
+ return;
684
751
  const split = await loadModels(key);
685
752
  if (controller.signal.aborted)
686
753
  return;
@@ -689,10 +756,17 @@ var commandCodeV2 = Plugin.define({
689
756
  editor.models.set("command-code-openai", split.open.map((m) => toV2Model(m, LANES[1])));
690
757
  });
691
758
  } catch (e) {
692
- console.warn("[command-code] live model list unavailable, keeping static seed:", e);
759
+ console.warn("[command-code] live model list unavailable, keeping previous list:", e);
760
+ } finally {
761
+ refreshing = false;
693
762
  }
694
- })();
695
- return () => controller.abort();
763
+ };
764
+ refresh();
765
+ const timer = setInterval(() => void refresh(), 30 * 60 * 1000);
766
+ return () => {
767
+ controller.abort();
768
+ clearInterval(timer);
769
+ };
696
770
  }
697
771
  });
698
772
 
@@ -762,6 +836,9 @@ var CommandCodePlugin = async (_input) => {
762
836
  }
763
837
  ]
764
838
  },
839
+ event: async ({ event }) => {
840
+ logEvent(event);
841
+ },
765
842
  config: async (cfg) => {
766
843
  cfg.provider ??= {};
767
844
  const existing = (id) => cfg.provider?.[id] ?? {};
@@ -0,0 +1,25 @@
1
+ export interface UsageMessage {
2
+ id?: string;
3
+ role?: string;
4
+ providerID?: string;
5
+ modelID?: string;
6
+ cost?: number;
7
+ tokens?: {
8
+ input?: number;
9
+ output?: number;
10
+ cache?: {
11
+ read?: number;
12
+ write?: number;
13
+ };
14
+ };
15
+ time?: {
16
+ created?: number;
17
+ completed?: number;
18
+ };
19
+ }
20
+ /** `$MPC_USAGE_LOG`, else `$XDG_CACHE_HOME/mpc/usage.jsonl`, else `~/.cache/…`. */
21
+ export declare function usageLogPath(): string;
22
+ /** Append one JSONL line for a finished assistant message. Never throws. */
23
+ export declare function logUsage(info: UsageMessage | undefined): void;
24
+ /** Narrow an opencode event down to an assistant message update. */
25
+ export declare function logEvent(event: unknown): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",