@jeffreyjyz/opencode-command-code 0.3.2 → 0.3.3

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
@@ -3702,21 +3702,53 @@ function createCommandCode(options = {}, images = () => false) {
3702
3702
  return { languageModel: model };
3703
3703
  }
3704
3704
 
3705
- // src/startupLog.ts
3706
- import { mkdir, appendFile } from "fs/promises";
3705
+ // src/modelsCache.ts
3706
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
3707
3707
  import { homedir as homedir2 } from "os";
3708
3708
  import { join } from "path";
3709
+ function cachePath() {
3710
+ const base = process.env.XDG_CACHE_HOME ?? join(homedir2(), ".cache");
3711
+ return join(base, "command-code", "models.json");
3712
+ }
3713
+ async function readModelsCache() {
3714
+ try {
3715
+ const parsed = JSON.parse(await readFile2(cachePath(), "utf8"));
3716
+ if (!Array.isArray(parsed.claude) || !Array.isArray(parsed.open))
3717
+ return;
3718
+ return parsed;
3719
+ } catch {
3720
+ return;
3721
+ }
3722
+ }
3723
+ async function writeModelsCache(cache) {
3724
+ try {
3725
+ const path = cachePath();
3726
+ await mkdir(join(path, ".."), { recursive: true });
3727
+ await writeFile(path, JSON.stringify(cache));
3728
+ } catch {}
3729
+ }
3730
+ function idsDiffer(a, b) {
3731
+ if (a.length !== b.length)
3732
+ return true;
3733
+ const left = new Set(a.map((m) => m.id));
3734
+ return b.some((m) => !left.has(m.id));
3735
+ }
3736
+
3737
+ // src/startupLog.ts
3738
+ import { mkdir as mkdir2, appendFile } from "fs/promises";
3739
+ import { homedir as homedir3 } from "os";
3740
+ import { join as join2 } from "path";
3709
3741
  function setupTimingLine(t) {
3710
3742
  return `setup: key=${t.key}ms register=${t.register}ms connection=${t.connection}ms refresh=${t.refresh}ms models=${t.models}`;
3711
3743
  }
3712
3744
  function startupLogPath() {
3713
- const base = process.env.XDG_CACHE_HOME ?? join(homedir2(), ".cache");
3714
- return join(base, "command-code", "startup.log");
3745
+ const base = process.env.XDG_CACHE_HOME ?? join2(homedir3(), ".cache");
3746
+ return join2(base, "command-code", "startup.log");
3715
3747
  }
3716
3748
  async function writeStartupLine(line) {
3717
3749
  try {
3718
3750
  const path = startupLogPath();
3719
- await mkdir(join(path, ".."), { recursive: true });
3751
+ await mkdir2(join2(path, ".."), { recursive: true });
3720
3752
  await appendFile(path, `[${new Date().toISOString()}] ${line}
3721
3753
  `);
3722
3754
  } catch {}
@@ -3839,6 +3871,11 @@ var commandCodeV2 = {
3839
3871
  }, { providerID: lane.id });
3840
3872
  }
3841
3873
  const seed = staticSeedModels();
3874
+ const cached = await readModelsCache();
3875
+ if (cached) {
3876
+ seed["command-code-anthropic"] = mergeModels(seed["command-code-anthropic"], cached.claude.map((m) => toV2Model(m, LANES[0])));
3877
+ seed["command-code-openai"] = mergeModels(seed["command-code-openai"], cached.open.map((m) => toV2Model(m, LANES[1])));
3878
+ }
3842
3879
  await ctx.provider.transform((editor) => {
3843
3880
  for (const lane of LANES) {
3844
3881
  editor.update(lane.id, (provider) => {
@@ -3901,6 +3938,15 @@ var commandCodeV2 = {
3901
3938
  }
3902
3939
  const controller = new AbortController;
3903
3940
  let refreshing = false;
3941
+ let appliedClaude = cached?.claude ?? [];
3942
+ let appliedOpen = cached?.open ?? [];
3943
+ const logTimings = (refreshMs, modelCount) => void writeStartupLine(setupTimingLine({
3944
+ key: keyMs,
3945
+ register: registerMs,
3946
+ connection: connectionMs,
3947
+ refresh: refreshMs,
3948
+ models: modelCount
3949
+ }));
3904
3950
  const refresh = async () => {
3905
3951
  if (refreshing || controller.signal.aborted)
3906
3952
  return;
@@ -3913,27 +3959,30 @@ var commandCodeV2 = {
3913
3959
  const split = await loadModels(key);
3914
3960
  if (controller.signal.aborted)
3915
3961
  return;
3962
+ writeModelsCache({ fetchedAt: Date.now(), claude: split.claude, open: split.open });
3963
+ const modelCount = split.claude.length + split.open.length;
3964
+ if (!idsDiffer(split.claude, appliedClaude) && !idsDiffer(split.open, appliedOpen)) {
3965
+ logTimings(Date.now() - refreshStart, modelCount);
3966
+ return;
3967
+ }
3916
3968
  await ctx.provider.transform((editor) => {
3917
3969
  editor.models.set("command-code-anthropic", mergeModels(seed["command-code-anthropic"], split.claude.map((m) => toV2Model(m, LANES[0]))));
3918
3970
  editor.models.set("command-code-openai", mergeModels(seed["command-code-openai"], split.open.map((m) => toV2Model(m, LANES[1]))));
3919
3971
  });
3920
- writeStartupLine(setupTimingLine({
3921
- key: keyMs,
3922
- register: registerMs,
3923
- connection: connectionMs,
3924
- refresh: Date.now() - refreshStart,
3925
- models: split.claude.length + split.open.length
3926
- }));
3972
+ appliedClaude = split.claude;
3973
+ appliedOpen = split.open;
3974
+ logTimings(Date.now() - refreshStart, modelCount);
3927
3975
  } catch (e) {
3928
3976
  console.warn("[command-code] live model list unavailable, keeping the snapshot:", e);
3929
3977
  } finally {
3930
3978
  refreshing = false;
3931
3979
  }
3932
3980
  };
3933
- refresh();
3981
+ const firstRefresh = setTimeout(() => void refresh(), 3000);
3934
3982
  const timer = setInterval(() => void refresh(), 30 * 60 * 1000);
3935
3983
  return () => {
3936
3984
  controller.abort();
3985
+ clearTimeout(firstRefresh);
3937
3986
  clearInterval(timer);
3938
3987
  };
3939
3988
  }
@@ -3941,7 +3990,7 @@ var commandCodeV2 = {
3941
3990
  // package.json
3942
3991
  var package_default = {
3943
3992
  name: "@jeffreyjyz/opencode-command-code",
3944
- version: "0.3.2",
3993
+ version: "0.3.3",
3945
3994
  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.",
3946
3995
  type: "module",
3947
3996
  main: "dist/index.js",
@@ -0,0 +1,18 @@
1
+ export interface CachedModel {
2
+ id: string;
3
+ name: string;
4
+ contextLength: number;
5
+ }
6
+ export interface ModelsCache {
7
+ fetchedAt: number;
8
+ claude: CachedModel[];
9
+ open: CachedModel[];
10
+ }
11
+ /** Never throws: a missing or corrupt cache is simply "no cache". */
12
+ export declare function readModelsCache(): Promise<ModelsCache | undefined>;
13
+ /** Best-effort write; diagnostics must never break setup. */
14
+ export declare function writeModelsCache(cache: ModelsCache): Promise<void>;
15
+ /** True when two lists differ by ids (the only thing a picker shows). */
16
+ export declare function idsDiffer(a: readonly CachedModel[], b: readonly CachedModel[]): boolean;
17
+ /** Path for tests and diagnostics. */
18
+ export declare function modelsCachePath(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",