@jeffreyjyz/opencode-command-code 0.3.2 → 0.3.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 +74 -16
- package/dist/models.d.ts +4 -0
- package/dist/modelsCache.d.ts +18 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3041,6 +3041,7 @@ async function loadModels(key) {
|
|
|
3041
3041
|
}
|
|
3042
3042
|
}
|
|
3043
3043
|
let plan = { planId: "", purchasedCredits: 0, freeCredits: 0 };
|
|
3044
|
+
let gated = true;
|
|
3044
3045
|
try {
|
|
3045
3046
|
const [sub, cr] = await Promise.all([subscriptions(key), credits(key)]);
|
|
3046
3047
|
plan = {
|
|
@@ -3048,10 +3049,12 @@ async function loadModels(key) {
|
|
|
3048
3049
|
purchasedCredits: cr.credits.purchasedCredits ?? 0,
|
|
3049
3050
|
freeCredits: cr.credits.freeCredits ?? 0
|
|
3050
3051
|
};
|
|
3051
|
-
} catch {
|
|
3052
|
+
} catch {
|
|
3053
|
+
gated = false;
|
|
3054
|
+
}
|
|
3052
3055
|
const allowed = models.filter((m) => evaluateModelAccess(m.id, plan).allowed);
|
|
3053
3056
|
const { claude, open } = splitModels(allowed);
|
|
3054
|
-
return { claude, open };
|
|
3057
|
+
return { claude, open, gated };
|
|
3055
3058
|
}
|
|
3056
3059
|
|
|
3057
3060
|
// src/provider/stream.ts
|
|
@@ -3702,21 +3705,53 @@ function createCommandCode(options = {}, images = () => false) {
|
|
|
3702
3705
|
return { languageModel: model };
|
|
3703
3706
|
}
|
|
3704
3707
|
|
|
3705
|
-
// src/
|
|
3706
|
-
import { mkdir,
|
|
3708
|
+
// src/modelsCache.ts
|
|
3709
|
+
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
3707
3710
|
import { homedir as homedir2 } from "os";
|
|
3708
3711
|
import { join } from "path";
|
|
3712
|
+
function cachePath() {
|
|
3713
|
+
const base = process.env.XDG_CACHE_HOME ?? join(homedir2(), ".cache");
|
|
3714
|
+
return join(base, "command-code", "models.json");
|
|
3715
|
+
}
|
|
3716
|
+
async function readModelsCache() {
|
|
3717
|
+
try {
|
|
3718
|
+
const parsed = JSON.parse(await readFile2(cachePath(), "utf8"));
|
|
3719
|
+
if (!Array.isArray(parsed.claude) || !Array.isArray(parsed.open))
|
|
3720
|
+
return;
|
|
3721
|
+
return parsed;
|
|
3722
|
+
} catch {
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
async function writeModelsCache(cache) {
|
|
3727
|
+
try {
|
|
3728
|
+
const path = cachePath();
|
|
3729
|
+
await mkdir(join(path, ".."), { recursive: true });
|
|
3730
|
+
await writeFile(path, JSON.stringify(cache));
|
|
3731
|
+
} catch {}
|
|
3732
|
+
}
|
|
3733
|
+
function idsDiffer(a, b) {
|
|
3734
|
+
if (a.length !== b.length)
|
|
3735
|
+
return true;
|
|
3736
|
+
const left = new Set(a.map((m) => m.id));
|
|
3737
|
+
return b.some((m) => !left.has(m.id));
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
// src/startupLog.ts
|
|
3741
|
+
import { mkdir as mkdir2, appendFile } from "fs/promises";
|
|
3742
|
+
import { homedir as homedir3 } from "os";
|
|
3743
|
+
import { join as join2 } from "path";
|
|
3709
3744
|
function setupTimingLine(t) {
|
|
3710
3745
|
return `setup: key=${t.key}ms register=${t.register}ms connection=${t.connection}ms refresh=${t.refresh}ms models=${t.models}`;
|
|
3711
3746
|
}
|
|
3712
3747
|
function startupLogPath() {
|
|
3713
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
3714
|
-
return
|
|
3748
|
+
const base = process.env.XDG_CACHE_HOME ?? join2(homedir3(), ".cache");
|
|
3749
|
+
return join2(base, "command-code", "startup.log");
|
|
3715
3750
|
}
|
|
3716
3751
|
async function writeStartupLine(line) {
|
|
3717
3752
|
try {
|
|
3718
3753
|
const path = startupLogPath();
|
|
3719
|
-
await
|
|
3754
|
+
await mkdir2(join2(path, ".."), { recursive: true });
|
|
3720
3755
|
await appendFile(path, `[${new Date().toISOString()}] ${line}
|
|
3721
3756
|
`);
|
|
3722
3757
|
} catch {}
|
|
@@ -3839,6 +3874,11 @@ var commandCodeV2 = {
|
|
|
3839
3874
|
}, { providerID: lane.id });
|
|
3840
3875
|
}
|
|
3841
3876
|
const seed = staticSeedModels();
|
|
3877
|
+
const cached = await readModelsCache();
|
|
3878
|
+
if (cached) {
|
|
3879
|
+
seed["command-code-anthropic"] = mergeModels(seed["command-code-anthropic"], cached.claude.map((m) => toV2Model(m, LANES[0])));
|
|
3880
|
+
seed["command-code-openai"] = mergeModels(seed["command-code-openai"], cached.open.map((m) => toV2Model(m, LANES[1])));
|
|
3881
|
+
}
|
|
3842
3882
|
await ctx.provider.transform((editor) => {
|
|
3843
3883
|
for (const lane of LANES) {
|
|
3844
3884
|
editor.update(lane.id, (provider) => {
|
|
@@ -3901,6 +3941,16 @@ var commandCodeV2 = {
|
|
|
3901
3941
|
}
|
|
3902
3942
|
const controller = new AbortController;
|
|
3903
3943
|
let refreshing = false;
|
|
3944
|
+
let appliedClaude = cached?.claude ?? [];
|
|
3945
|
+
let appliedOpen = cached?.open ?? [];
|
|
3946
|
+
let canTransform = false;
|
|
3947
|
+
const logTimings = (refreshMs, modelCount) => void writeStartupLine(setupTimingLine({
|
|
3948
|
+
key: keyMs,
|
|
3949
|
+
register: registerMs,
|
|
3950
|
+
connection: connectionMs,
|
|
3951
|
+
refresh: refreshMs,
|
|
3952
|
+
models: modelCount
|
|
3953
|
+
}));
|
|
3904
3954
|
const refresh = async () => {
|
|
3905
3955
|
if (refreshing || controller.signal.aborted)
|
|
3906
3956
|
return;
|
|
@@ -3913,27 +3963,35 @@ var commandCodeV2 = {
|
|
|
3913
3963
|
const split = await loadModels(key);
|
|
3914
3964
|
if (controller.signal.aborted)
|
|
3915
3965
|
return;
|
|
3966
|
+
const modelCount = split.claude.length + split.open.length;
|
|
3967
|
+
if (!split.gated) {
|
|
3968
|
+
logTimings(Date.now() - refreshStart, modelCount);
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3971
|
+
writeModelsCache({ fetchedAt: Date.now(), claude: split.claude, open: split.open });
|
|
3972
|
+
if (!canTransform || !idsDiffer(split.claude, appliedClaude) && !idsDiffer(split.open, appliedOpen)) {
|
|
3973
|
+
logTimings(Date.now() - refreshStart, modelCount);
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3916
3976
|
await ctx.provider.transform((editor) => {
|
|
3917
3977
|
editor.models.set("command-code-anthropic", mergeModels(seed["command-code-anthropic"], split.claude.map((m) => toV2Model(m, LANES[0]))));
|
|
3918
3978
|
editor.models.set("command-code-openai", mergeModels(seed["command-code-openai"], split.open.map((m) => toV2Model(m, LANES[1]))));
|
|
3919
3979
|
});
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
connection: connectionMs,
|
|
3924
|
-
refresh: Date.now() - refreshStart,
|
|
3925
|
-
models: split.claude.length + split.open.length
|
|
3926
|
-
}));
|
|
3980
|
+
appliedClaude = split.claude;
|
|
3981
|
+
appliedOpen = split.open;
|
|
3982
|
+
logTimings(Date.now() - refreshStart, modelCount);
|
|
3927
3983
|
} catch (e) {
|
|
3928
3984
|
console.warn("[command-code] live model list unavailable, keeping the snapshot:", e);
|
|
3929
3985
|
} finally {
|
|
3930
3986
|
refreshing = false;
|
|
3987
|
+
canTransform = true;
|
|
3931
3988
|
}
|
|
3932
3989
|
};
|
|
3933
|
-
refresh();
|
|
3990
|
+
const firstRefresh = setTimeout(() => void refresh(), 3000);
|
|
3934
3991
|
const timer = setInterval(() => void refresh(), 30 * 60 * 1000);
|
|
3935
3992
|
return () => {
|
|
3936
3993
|
controller.abort();
|
|
3994
|
+
clearTimeout(firstRefresh);
|
|
3937
3995
|
clearInterval(timer);
|
|
3938
3996
|
};
|
|
3939
3997
|
}
|
|
@@ -3941,7 +3999,7 @@ var commandCodeV2 = {
|
|
|
3941
3999
|
// package.json
|
|
3942
4000
|
var package_default = {
|
|
3943
4001
|
name: "@jeffreyjyz/opencode-command-code",
|
|
3944
|
-
version: "0.3.
|
|
4002
|
+
version: "0.3.4",
|
|
3945
4003
|
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
4004
|
type: "module",
|
|
3947
4005
|
main: "dist/index.js",
|
package/dist/models.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export type CmdModel = {
|
|
|
6
6
|
export type ModelSplit = {
|
|
7
7
|
claude: CmdModel[];
|
|
8
8
|
open: CmdModel[];
|
|
9
|
+
/** True when the billing lookup succeeded and the plan gate really ran. A
|
|
10
|
+
* false here means "show everything", which is the API-enforced fallback —
|
|
11
|
+
* callers must not treat that as a new model list. */
|
|
12
|
+
gated: boolean;
|
|
9
13
|
};
|
|
10
14
|
export declare function isClaude(id: string): boolean;
|
|
11
15
|
export declare function splitModels(models: CmdModel[]): {
|
|
@@ -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.
|
|
3
|
+
"version": "0.3.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",
|