@jeffreyjyz/opencode-command-code 0.1.0 → 0.1.2

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/access.d.ts CHANGED
@@ -1,20 +1,10 @@
1
1
  import { canonicalizeModelId, PLAN_RULES } from "./gating";
2
-
3
2
  export { canonicalizeModelId, PLAN_RULES };
4
3
  export type PlanLike = {
5
- planId: string;
6
- purchasedCredits: number;
7
- freeCredits: number;
4
+ planId: string;
5
+ purchasedCredits: number;
6
+ freeCredits: number;
8
7
  };
9
- export declare function evaluateModelAccess(
10
- model: string,
11
- plan: PlanLike,
12
- ): {
13
- allowed: boolean;
8
+ export declare function evaluateModelAccess(model: string, plan: PlanLike): {
9
+ allowed: boolean;
14
10
  };
15
- /** Filter a live model list down to what the plan allows. */
16
- export declare function filterByPlan<
17
- T extends {
18
- id: string;
19
- },
20
- >(models: T[], plan: PlanLike): T[];
package/dist/api.d.ts CHANGED
@@ -1,59 +1,53 @@
1
+ export declare const API_BASE = "https://api.commandcode.ai";
1
2
  export declare function authHeaders(key: string): Record<string, string>;
2
- export declare function getJson(
3
- path: string,
4
- key: string,
5
- timeoutMs?: number,
6
- ): Promise<unknown>;
3
+ export declare function getJson(path: string, key: string, timeoutMs?: number): Promise<unknown>;
7
4
  export type Whoami = {
8
- success: boolean;
9
- user?: {
10
- name?: string;
11
- userName?: string;
12
- };
5
+ success: boolean;
6
+ user?: {
7
+ name?: string;
8
+ userName?: string;
9
+ };
13
10
  };
14
11
  export type SubData = {
15
- status: string;
16
- planId: string;
17
- currentPeriodStart?: string;
18
- currentPeriodEnd?: string;
12
+ status: string;
13
+ planId: string;
14
+ currentPeriodStart?: string;
15
+ currentPeriodEnd?: string;
19
16
  };
20
17
  export type Window = {
21
- used: number;
22
- cap: number;
23
- exceeded?: boolean;
24
- resetAt?: number;
18
+ used: number;
19
+ cap: number;
20
+ exceeded?: boolean;
21
+ resetAt?: number;
25
22
  };
26
23
  export type Credits = {
27
- credits: {
28
- monthlyCredits: number;
29
- purchasedCredits?: number;
30
- freeCredits?: number;
31
- };
32
- windowLimits?: {
33
- fiveHour?: Window;
34
- weekly?: Window;
35
- };
24
+ credits: {
25
+ monthlyCredits: number;
26
+ purchasedCredits?: number;
27
+ freeCredits?: number;
28
+ };
29
+ windowLimits?: {
30
+ fiveHour?: Window;
31
+ weekly?: Window;
32
+ };
36
33
  };
37
34
  export type Summary = {
38
- totalCount?: number;
39
- totalCost?: number;
40
- successRate?: number;
41
- totalTokensIn?: number;
42
- totalTokensOut?: number;
35
+ totalCount?: number;
36
+ totalCost?: number;
37
+ successRate?: number;
38
+ totalTokensIn?: number;
39
+ totalTokensOut?: number;
43
40
  };
44
41
  export type ProviderModelsResp = {
45
- data: Array<{
46
- id: string;
47
- name?: string;
48
- context_length?: number;
49
- owned_by?: string;
50
- }>;
42
+ data: Array<{
43
+ id: string;
44
+ name?: string;
45
+ context_length?: number;
46
+ owned_by?: string;
47
+ }>;
51
48
  };
52
49
  export declare function whoami(key: string): Promise<Whoami>;
53
50
  export declare function subscriptions(key: string): Promise<SubData>;
54
51
  export declare function credits(key: string): Promise<Credits>;
55
52
  export declare function usageSummary(key: string): Promise<Summary>;
56
- export declare function providerModels(
57
- key: string,
58
- ): Promise<ProviderModelsResp>;
59
- export declare function getPlanInfo(key: string): Promise<SubData>;
53
+ export declare function providerModels(key: string): Promise<ProviderModelsResp>;
package/dist/gating.d.ts CHANGED
@@ -1,18 +1,13 @@
1
- export declare const OPEN_SOURCE: "opensource";
2
- export declare const PREMIUM: "premium";
3
1
  export type Category = "opensource" | "premium";
4
2
  /** model id -> category (models absent from this table have unknown category) */
5
3
  export declare const MODEL_CATEGORIES: Record<string, Category>;
6
4
  /** planId -> access rules; plan ids absent from this table get everything */
7
- export declare const PLAN_RULES: Record<
8
- string,
9
- {
10
- allowedCategories: Category[];
11
- blockedModels: string[];
12
- }
13
- >;
14
- /** canonical known model ids (lowercase compare) */
15
- export declare const KNOWN_MODELS: string[];
16
- /** deprecated/aliased model id -> canonical id */
17
- export declare const MODEL_ALIASES: Record<string, string>;
5
+ export declare const PLAN_RULES: Record<string, {
6
+ allowedCategories: Category[];
7
+ blockedModels: string[];
8
+ }>;
9
+ /** Models the API hard-blocks (403 MODEL_NOT_IN_PLAN) per plan, beyond the
10
+ * category rules. Empirically probed — the CLI bundle misses these because it
11
+ * tracks category via serving lane, not per-model plan entitlements. */
12
+ export declare const HARD_BLOCKED: Record<string, string[]>;
18
13
  export declare function canonicalizeModelId(model: string): string;
package/dist/index.js CHANGED
@@ -48,9 +48,6 @@ async function usageSummary(key) {
48
48
  async function providerModels(key) {
49
49
  return getJson("/provider/v1/models", key, 15000);
50
50
  }
51
- async function getPlanInfo(key) {
52
- return subscriptions(key);
53
- }
54
51
 
55
52
  // src/key.ts
56
53
  import { readFile } from "fs/promises";
@@ -72,198 +69,248 @@ async function resolveKey(getAuth) {
72
69
  } catch {}
73
70
  throw new Error("No Command Code API key found. Run /connect, choose Command Code, and paste your key (or `cmd login` in a terminal, or set CMD_API_KEY).");
74
71
  }
75
-
76
- // src/gating.ts
77
- var MODEL_CATEGORIES = {
78
- "claude-sonnet-5": "premium",
79
- "claude-sonnet-4-6": "premium",
80
- "claude-fable-5": "premium",
81
- "claude-opus-5": "premium",
82
- "claude-opus-4-8": "premium",
83
- "claude-opus-4-7": "premium",
84
- "claude-haiku-4-5-20251001": "premium",
85
- "gpt-5.6-sol": "opensource",
86
- "gpt-5.6-terra": "premium",
87
- "gpt-5.6-luna": "opensource",
88
- "gpt-5.5": "premium",
89
- "gpt-5.4": "premium",
90
- "gpt-5.3-codex": "premium",
91
- "gpt-5.4-mini": "premium",
92
- "google/gemini-3.5-flash": "premium",
93
- "google/gemini-3.1-flash-lite": "premium",
94
- "sakana/fugu-ultra": "premium",
95
- "meta/muse-spark-1.1": "premium",
96
- "meta/muse-spark-1.2": "opensource",
97
- "meta/muse-spark-1.2-contributor": "opensource",
98
- "xai/grok-4.5": "opensource",
99
- "xai/grok-4.6": "opensource",
100
- "google/gemini-3.7-flash": "opensource",
101
- "z-ai/glm-5.3-flash": "opensource",
102
- "tencent/hy4-preview": "opensource",
103
- "tencent/hy3-paid": "opensource",
104
- "tencent/Hy3": "opensource",
105
- "MiniMaxAI/MiniMax-M3-Free": "opensource",
106
- "MiniMaxAI/MiniMax-M3": "opensource",
107
- "MiniMaxAI/MiniMax-M2.7": "opensource",
108
- "minimax/minimax-m3-free": "opensource",
109
- "minimax/minimax-m2.7-free": "opensource",
110
- "MiniMaxAI/MiniMax-M2.5": "opensource",
111
- "deepseek/deepseek-v4-pro": "opensource",
112
- "deepseek/deepseek-v4-flash": "opensource",
113
- "moonshotai/Kimi-K3": "opensource",
114
- "moonshotai/Kimi-K2.7-Code": "opensource",
115
- "moonshotai/Kimi-K2.7-Code-Highspeed": "opensource",
116
- "moonshotai/Kimi-K2.6": "opensource",
117
- "moonshotai/Kimi-K2.5": "opensource",
118
- "zai-org/GLM-5.3": "opensource",
119
- "zai-org/GLM-5.2": "opensource",
120
- "zai-org/GLM-5.2-Fast": "opensource",
121
- "zai-org/GLM-5.1": "opensource",
122
- "zai-org/GLM-5": "opensource",
123
- "xiaomi/mimo-v2.5-pro": "opensource",
124
- "xiaomi/mimo-v2.5": "opensource",
125
- "Qwen/Qwen3.6-Max-Preview": "opensource",
126
- "Qwen/Qwen3.6-Plus": "opensource",
127
- "Qwen/Qwen3.7-Max": "opensource",
128
- "Qwen/Qwen3.7-Plus": "opensource",
129
- "Qwen/Qwen3.8-Max": "opensource",
130
- "Qwen/Qwen3.8-27B": "opensource",
131
- "Qwen/Qwen3.8-Flash": "opensource",
132
- "Qwen/Qwen3.7-Flash": "opensource",
133
- "stepfun/Step-3.7-Flash": "opensource",
134
- "stepfun/Step-3.5-Flash": "opensource",
135
- "nvidia/nemotron-3-ultra-550b-a55b": "opensource",
136
- "thinkingmachines/inkling": "opensource"
137
- };
138
- var PLAN_RULES = {
139
- "individual-go": {
140
- allowedCategories: ["opensource"],
141
- blockedModels: [
142
- "vercel-ai-gateway:meta/muse-spark-1.2",
143
- "vercel-ai-gateway:xai/grok-4.6",
144
- "openrouter:google/gemini-3.7-flash",
145
- "vercel-ai-gateway:gpt-5.6-sol"
146
- ]
72
+ // ../core/gating.json
73
+ var gating_default = {
74
+ categories: {
75
+ "claude-sonnet-5": "premium",
76
+ "claude-sonnet-4-6": "premium",
77
+ "claude-fable-5": "premium",
78
+ "claude-opus-5": "premium",
79
+ "claude-opus-4-8": "premium",
80
+ "claude-opus-4-7": "premium",
81
+ "claude-haiku-4-5-20251001": "premium",
82
+ "gpt-5.6-sol": "opensource",
83
+ "gpt-5.6-terra": "premium",
84
+ "gpt-5.6-luna": "opensource",
85
+ "gpt-5.5": "premium",
86
+ "gpt-5.4": "premium",
87
+ "gpt-5.3-codex": "premium",
88
+ "gpt-5.4-mini": "premium",
89
+ "google/gemini-3.5-flash": "premium",
90
+ "google/gemini-3.1-flash-lite": "premium",
91
+ "sakana/fugu-ultra": "premium",
92
+ "meta/muse-spark-1.1": "premium",
93
+ "meta/muse-spark-1.2": "opensource",
94
+ "meta/muse-spark-1.2-contributor": "opensource",
95
+ "xai/grok-4.5": "opensource",
96
+ "xai/grok-4.6": "opensource",
97
+ "google/gemini-3.7-flash": "opensource",
98
+ "z-ai/glm-5.3-flash": "opensource",
99
+ "tencent/hy4-preview": "opensource",
100
+ "tencent/hy3-paid": "opensource",
101
+ "tencent/Hy3": "opensource",
102
+ "MiniMaxAI/MiniMax-M3-Free": "opensource",
103
+ "MiniMaxAI/MiniMax-M3": "opensource",
104
+ "MiniMaxAI/MiniMax-M2.7": "opensource",
105
+ "minimax/minimax-m3-free": "opensource",
106
+ "minimax/minimax-m2.7-free": "opensource",
107
+ "MiniMaxAI/MiniMax-M2.5": "opensource",
108
+ "deepseek/deepseek-v4-pro": "opensource",
109
+ "deepseek/deepseek-v4-flash": "opensource",
110
+ "moonshotai/Kimi-K3": "opensource",
111
+ "moonshotai/Kimi-K2.7-Code": "opensource",
112
+ "moonshotai/Kimi-K2.7-Code-Highspeed": "opensource",
113
+ "moonshotai/Kimi-K2.6": "opensource",
114
+ "moonshotai/Kimi-K2.5": "opensource",
115
+ "zai-org/GLM-5.3": "opensource",
116
+ "zai-org/GLM-5.2": "opensource",
117
+ "zai-org/GLM-5.2-Fast": "opensource",
118
+ "zai-org/GLM-5.1": "opensource",
119
+ "zai-org/GLM-5": "opensource",
120
+ "xiaomi/mimo-v2.5-pro": "opensource",
121
+ "xiaomi/mimo-v2.5": "opensource",
122
+ "Qwen/Qwen3.6-Max-Preview": "opensource",
123
+ "Qwen/Qwen3.6-Plus": "opensource",
124
+ "Qwen/Qwen3.7-Max": "opensource",
125
+ "Qwen/Qwen3.7-Plus": "opensource",
126
+ "Qwen/Qwen3.8-Max": "opensource",
127
+ "Qwen/Qwen3.8-27B": "opensource",
128
+ "Qwen/Qwen3.8-Flash": "opensource",
129
+ "Qwen/Qwen3.7-Flash": "opensource",
130
+ "stepfun/Step-3.7-Flash": "opensource",
131
+ "stepfun/Step-3.5-Flash": "opensource",
132
+ "nvidia/nemotron-3-ultra-550b-a55b": "opensource",
133
+ "thinkingmachines/inkling": "opensource"
147
134
  },
148
- "individual-goat": {
149
- allowedCategories: ["opensource"],
150
- blockedModels: []
135
+ plans: {
136
+ "individual-go": {
137
+ allowedCategories: [
138
+ "opensource"
139
+ ],
140
+ blockedModels: [
141
+ "vercel-ai-gateway:meta/muse-spark-1.2",
142
+ "vercel-ai-gateway:xai/grok-4.6",
143
+ "openrouter:google/gemini-3.7-flash",
144
+ "vercel-ai-gateway:gpt-5.6-sol"
145
+ ]
146
+ },
147
+ "individual-goat": {
148
+ allowedCategories: [
149
+ "opensource"
150
+ ],
151
+ blockedModels: []
152
+ },
153
+ "individual-pro": {
154
+ allowedCategories: [
155
+ "premium",
156
+ "opensource"
157
+ ],
158
+ blockedModels: [
159
+ "anthropic:claude-fable-5",
160
+ "anthropic:claude-opus-5",
161
+ "anthropic:claude-opus-4-8",
162
+ "anthropic:claude-opus-4-7",
163
+ "anthropic:claude-opus-4-6",
164
+ "anthropic:claude-opus-4-5-20251101",
165
+ "vercel-ai-gateway:sakana/fugu-ultra"
166
+ ]
167
+ },
168
+ "individual-pro-v1": {
169
+ allowedCategories: [
170
+ "premium",
171
+ "opensource"
172
+ ],
173
+ blockedModels: [
174
+ "anthropic:claude-fable-5",
175
+ "anthropic:claude-opus-5",
176
+ "anthropic:claude-opus-4-8",
177
+ "anthropic:claude-opus-4-7",
178
+ "anthropic:claude-opus-4-6",
179
+ "anthropic:claude-opus-4-5-20251101",
180
+ "vercel-ai-gateway:sakana/fugu-ultra"
181
+ ]
182
+ },
183
+ "individual-provider": {
184
+ allowedCategories: [
185
+ "premium",
186
+ "opensource"
187
+ ],
188
+ blockedModels: []
189
+ },
190
+ "individual-max": {
191
+ allowedCategories: [
192
+ "premium",
193
+ "opensource"
194
+ ],
195
+ blockedModels: []
196
+ },
197
+ "individual-ultra": {
198
+ allowedCategories: [
199
+ "premium",
200
+ "opensource"
201
+ ],
202
+ blockedModels: []
203
+ },
204
+ "teams-pro": {
205
+ allowedCategories: [
206
+ "premium",
207
+ "opensource"
208
+ ],
209
+ blockedModels: []
210
+ }
151
211
  },
152
- "individual-pro": {
153
- allowedCategories: ["premium", "opensource"],
154
- blockedModels: [
155
- "anthropic:claude-fable-5",
156
- "anthropic:claude-opus-5",
157
- "anthropic:claude-opus-4-8",
158
- "anthropic:claude-opus-4-7",
159
- "anthropic:claude-opus-4-6",
160
- "anthropic:claude-opus-4-5-20251101",
161
- "vercel-ai-gateway:sakana/fugu-ultra"
162
- ]
212
+ knownModels: [
213
+ "claude-sonnet-4-6",
214
+ "claude-fable-5",
215
+ "claude-opus-5",
216
+ "claude-opus-4-8",
217
+ "claude-opus-4-7",
218
+ "claude-haiku-4-5-20251001",
219
+ "gpt-5.6-sol",
220
+ "gpt-5.6-terra",
221
+ "gpt-5.6-luna",
222
+ "gpt-5.5",
223
+ "gpt-5.4",
224
+ "gpt-5.3-codex",
225
+ "gpt-5.4-mini",
226
+ "moonshotai/Kimi-K3",
227
+ "thinkingmachines/inkling",
228
+ "thinkingmachines/inkling-small",
229
+ "deepseek/deepseek-v4-pro",
230
+ "deepseek/deepseek-v4-flash",
231
+ "deepseek/deepseek-v4-flash-vision-exp",
232
+ "moonshotai/Kimi-K2.7-Code",
233
+ "moonshotai/Kimi-K2.7-Code-Highspeed",
234
+ "moonshotai/Kimi-K2.6",
235
+ "moonshotai/Kimi-K2.5",
236
+ "zai-org/GLM-5.3",
237
+ "z-ai/glm-5.3-flash",
238
+ "zai-org/GLM-5.2",
239
+ "zai-org/GLM-5.2-Fast",
240
+ "zai-org/GLM-5.1",
241
+ "zai-org/GLM-5",
242
+ "xiaomi/mimo-v2.5-pro",
243
+ "xiaomi/mimo-v2.5",
244
+ "stepfun/Step-3.7-Flash",
245
+ "stepfun/Step-3.5-Flash",
246
+ "tencent/hy4-preview",
247
+ "tencent/hy3-paid",
248
+ "tencent/Hy3",
249
+ "google/gemini-3.7-flash",
250
+ "google/gemini-3.6-flash",
251
+ "google/gemini-3.5-flash",
252
+ "google/gemini-3.5-flash-lite",
253
+ "google/gemini-3.1-flash-lite",
254
+ "sakana/fugu-ultra",
255
+ "xai/grok-4.5",
256
+ "xai/grok-4.6",
257
+ "meta/muse-spark-1.1",
258
+ "meta/muse-spark-1.2",
259
+ "meta/muse-spark-1.2-contributor",
260
+ "nvidia/nemotron-3-ultra-550b-a55b",
261
+ "poolside/laguna-s-2.1-free",
262
+ "inclusionai/ling-3.0-flash-free",
263
+ "minimax/minimax-m3-free",
264
+ "minimax/minimax-m2.7-free",
265
+ "claude-sonnet-5",
266
+ "MiniMaxAI/MiniMax-M3-Free",
267
+ "MiniMaxAI/MiniMax-M3",
268
+ "MiniMaxAI/MiniMax-M2.7",
269
+ "MiniMaxAI/MiniMax-M2.5",
270
+ "Qwen/Qwen3.6-Max-Preview",
271
+ "Qwen/Qwen3.6-Plus",
272
+ "Qwen/Qwen3.7-Max",
273
+ "Qwen/Qwen3.7-Plus",
274
+ "Qwen/Qwen3.8-Max",
275
+ "Qwen/Qwen3.8-27B",
276
+ "Qwen/Qwen3.8-Flash",
277
+ "Qwen/Qwen3.7-Flash"
278
+ ],
279
+ aliases: {
280
+ "claude-sonnet-4-20250514": "claude-sonnet-4-6",
281
+ "claude-sonnet-4-5-20250929": "claude-sonnet-4-6",
282
+ "claude-opus-4-5-20251101": "claude-opus-4-7",
283
+ "claude-opus-4-6": "claude-opus-4-7",
284
+ "claude-haiku-4-5": "claude-haiku-4-5-20251001"
163
285
  },
164
- "individual-pro-v1": {
165
- allowedCategories: ["premium", "opensource"],
166
- blockedModels: [
167
- "anthropic:claude-fable-5",
168
- "anthropic:claude-opus-5",
169
- "anthropic:claude-opus-4-8",
170
- "anthropic:claude-opus-4-7",
171
- "anthropic:claude-opus-4-6",
172
- "anthropic:claude-opus-4-5-20251101",
173
- "vercel-ai-gateway:sakana/fugu-ultra"
286
+ hardBlocked: {
287
+ "individual-goat": [
288
+ "meta/muse-spark-1.1",
289
+ "google/gemini-3.5-flash",
290
+ "google/gemini-3.6-flash",
291
+ "google/gemini-3.5-flash-lite",
292
+ "google/gemini-3.1-flash-lite"
293
+ ],
294
+ "individual-go": [
295
+ "meta/muse-spark-1.1",
296
+ "google/gemini-3.5-flash",
297
+ "google/gemini-3.6-flash",
298
+ "google/gemini-3.5-flash-lite",
299
+ "google/gemini-3.1-flash-lite",
300
+ "meta/muse-spark-1.2",
301
+ "meta/muse-spark-1.2-contributor",
302
+ "meta/muse-spark-1.3",
303
+ "meta/muse-spark-1.3-contributor"
174
304
  ]
175
- },
176
- "individual-provider": {
177
- allowedCategories: ["premium", "opensource"],
178
- blockedModels: []
179
- },
180
- "individual-max": {
181
- allowedCategories: ["premium", "opensource"],
182
- blockedModels: []
183
- },
184
- "individual-ultra": {
185
- allowedCategories: ["premium", "opensource"],
186
- blockedModels: []
187
- },
188
- "teams-pro": {
189
- allowedCategories: ["premium", "opensource"],
190
- blockedModels: []
191
305
  }
192
306
  };
193
- var KNOWN_MODELS = [
194
- "claude-sonnet-4-6",
195
- "claude-fable-5",
196
- "claude-opus-5",
197
- "claude-opus-4-8",
198
- "claude-opus-4-7",
199
- "claude-haiku-4-5-20251001",
200
- "gpt-5.6-sol",
201
- "gpt-5.6-terra",
202
- "gpt-5.6-luna",
203
- "gpt-5.5",
204
- "gpt-5.4",
205
- "gpt-5.3-codex",
206
- "gpt-5.4-mini",
207
- "moonshotai/Kimi-K3",
208
- "thinkingmachines/inkling",
209
- "thinkingmachines/inkling-small",
210
- "deepseek/deepseek-v4-pro",
211
- "deepseek/deepseek-v4-flash",
212
- "deepseek/deepseek-v4-flash-vision-exp",
213
- "moonshotai/Kimi-K2.7-Code",
214
- "moonshotai/Kimi-K2.7-Code-Highspeed",
215
- "moonshotai/Kimi-K2.6",
216
- "moonshotai/Kimi-K2.5",
217
- "zai-org/GLM-5.3",
218
- "z-ai/glm-5.3-flash",
219
- "zai-org/GLM-5.2",
220
- "zai-org/GLM-5.2-Fast",
221
- "zai-org/GLM-5.1",
222
- "zai-org/GLM-5",
223
- "xiaomi/mimo-v2.5-pro",
224
- "xiaomi/mimo-v2.5",
225
- "stepfun/Step-3.7-Flash",
226
- "stepfun/Step-3.5-Flash",
227
- "tencent/hy4-preview",
228
- "tencent/hy3-paid",
229
- "tencent/Hy3",
230
- "google/gemini-3.7-flash",
231
- "google/gemini-3.6-flash",
232
- "google/gemini-3.5-flash",
233
- "google/gemini-3.5-flash-lite",
234
- "google/gemini-3.1-flash-lite",
235
- "sakana/fugu-ultra",
236
- "xai/grok-4.5",
237
- "xai/grok-4.6",
238
- "meta/muse-spark-1.1",
239
- "meta/muse-spark-1.2",
240
- "meta/muse-spark-1.2-contributor",
241
- "nvidia/nemotron-3-ultra-550b-a55b",
242
- "poolside/laguna-s-2.1-free",
243
- "inclusionai/ling-3.0-flash-free",
244
- "minimax/minimax-m3-free",
245
- "minimax/minimax-m2.7-free",
246
- "claude-sonnet-5",
247
- "MiniMaxAI/MiniMax-M3-Free",
248
- "MiniMaxAI/MiniMax-M3",
249
- "MiniMaxAI/MiniMax-M2.7",
250
- "MiniMaxAI/MiniMax-M2.5",
251
- "Qwen/Qwen3.6-Max-Preview",
252
- "Qwen/Qwen3.6-Plus",
253
- "Qwen/Qwen3.7-Max",
254
- "Qwen/Qwen3.7-Plus",
255
- "Qwen/Qwen3.8-Max",
256
- "Qwen/Qwen3.8-27B",
257
- "Qwen/Qwen3.8-Flash",
258
- "Qwen/Qwen3.7-Flash"
259
- ];
260
- var MODEL_ALIASES = {
261
- "claude-sonnet-4-20250514": "claude-sonnet-4-6",
262
- "claude-sonnet-4-5-20250929": "claude-sonnet-4-6",
263
- "claude-opus-4-5-20251101": "claude-opus-4-7",
264
- "claude-opus-4-6": "claude-opus-4-7",
265
- "claude-haiku-4-5": "claude-haiku-4-5-20251001"
266
- };
307
+
308
+ // src/gating.ts
309
+ var MODEL_CATEGORIES = gating_default.categories;
310
+ var PLAN_RULES = gating_default.plans;
311
+ var HARD_BLOCKED = gating_default.hardBlocked;
312
+ var KNOWN_MODELS = gating_default.knownModels;
313
+ var MODEL_ALIASES = gating_default.aliases;
267
314
  function findKnown(s) {
268
315
  const k = s.toLowerCase();
269
316
  return KNOWN_MODELS.find((m) => m.toLowerCase() === k);
@@ -280,26 +327,6 @@ function canonicalizeModelId(model) {
280
327
  }
281
328
 
282
329
  // src/access.ts
283
- var HARD_BLOCKED = {
284
- "individual-goat": [
285
- "meta/muse-spark-1.1",
286
- "google/gemini-3.5-flash",
287
- "google/gemini-3.6-flash",
288
- "google/gemini-3.5-flash-lite",
289
- "google/gemini-3.1-flash-lite"
290
- ],
291
- "individual-go": [
292
- "meta/muse-spark-1.1",
293
- "google/gemini-3.5-flash",
294
- "google/gemini-3.6-flash",
295
- "google/gemini-3.5-flash-lite",
296
- "google/gemini-3.1-flash-lite",
297
- "meta/muse-spark-1.2",
298
- "meta/muse-spark-1.2-contributor",
299
- "meta/muse-spark-1.3",
300
- "meta/muse-spark-1.3-contributor"
301
- ]
302
- };
303
330
  function evaluateModelAccess(model, plan) {
304
331
  if (plan.purchasedCredits > 0 || plan.freeCredits > 0)
305
332
  return { allowed: true };
@@ -325,9 +352,6 @@ function evaluateModelAccess(model, plan) {
325
352
  return { allowed: false };
326
353
  return { allowed: true };
327
354
  }
328
- function filterByPlan(models, plan) {
329
- return models.filter((m) => evaluateModelAccess(m.id, plan).allowed);
330
- }
331
355
  var KNOWN_KEYS = Object.keys(MODEL_CATEGORIES);
332
356
  function siblingCategory(model) {
333
357
  const lower = model.toLowerCase();
@@ -356,12 +380,10 @@ function splitModels(models) {
356
380
  (isClaude(m.id) ? claude : open).push(m);
357
381
  return { claude, open };
358
382
  }
359
- async function loadModels(key, opts) {
383
+ async function loadModels(key) {
360
384
  let models;
361
- let source;
362
385
  if (cache && Date.now() - cache.at < CACHE_TTL_MS) {
363
386
  models = cache.models;
364
- source = "cache";
365
387
  } else {
366
388
  try {
367
389
  const resp = await providerModels(key);
@@ -371,35 +393,61 @@ async function loadModels(key, opts) {
371
393
  contextLength: m.context_length ?? 0
372
394
  }));
373
395
  cache = { at: Date.now(), models };
374
- source = "live";
375
396
  } catch {
376
397
  if (cache) {
377
398
  models = cache.models;
378
- source = "stale";
379
399
  } else {
380
400
  throw new Error("Could not fetch Command Code model list (https://api.commandcode.ai/provider/v1/models). Check network/API key.");
381
401
  }
382
402
  }
383
403
  }
384
404
  let plan = { planId: "", purchasedCredits: 0, freeCredits: 0 };
385
- let gatedOut = [];
386
- if (!opts?.skipPlan) {
387
- try {
388
- const sub = await getPlanInfo(key);
389
- const cr = await credits(key);
390
- plan = {
391
- planId: sub.planId,
392
- purchasedCredits: cr.credits.purchasedCredits ?? 0,
393
- freeCredits: cr.credits.freeCredits ?? 0
394
- };
395
- } catch {}
396
- }
397
- const allowed = filterByPlan(models, plan);
398
- const allowedIds = new Set(allowed.map((m) => m.id));
399
- gatedOut = models.filter((m) => !allowedIds.has(m.id)).map((m) => m.id);
405
+ try {
406
+ const [sub, cr] = await Promise.all([subscriptions(key), credits(key)]);
407
+ plan = {
408
+ planId: sub.planId,
409
+ purchasedCredits: cr.credits.purchasedCredits ?? 0,
410
+ freeCredits: cr.credits.freeCredits ?? 0
411
+ };
412
+ } catch {}
413
+ const allowed = models.filter((m) => evaluateModelAccess(m.id, plan).allowed);
400
414
  const { claude, open } = splitModels(allowed);
401
- return { claude, open, gatedOut, source };
415
+ return { claude, open };
402
416
  }
417
+ // ../core/plans.json
418
+ var plans_default = {
419
+ nameRules: [
420
+ { needles: ["enterprise"], name: "Enterprise" },
421
+ { needles: ["provider"], name: "Provider" },
422
+ { needles: ["team"], name: "Team Pro" },
423
+ { needles: ["max", "20"], name: "Max 20x" },
424
+ { needles: ["max"], name: "Max 10x" },
425
+ { needles: ["goat"], name: "GOAT" },
426
+ { needles: ["pro"], name: "Pro" },
427
+ { needles: ["go"], name: "Go" }
428
+ ],
429
+ defaultName: "Free",
430
+ caps: {
431
+ Enterprise: null,
432
+ Provider: null,
433
+ "Team Pro": 40,
434
+ "Max 20x": 300,
435
+ "Max 10x": 150,
436
+ GOAT: 70,
437
+ Pro: 80,
438
+ Go: 10,
439
+ Free: null
440
+ },
441
+ plans: [
442
+ { name: "Go", price: "$1", monthly: "$10", fiveHour: "$3", weekly: "$6" },
443
+ { name: "GOAT", price: "$10", monthly: "$70", fiveHour: "$14", weekly: "$35" },
444
+ { name: "Pro", price: "$20", monthly: "$80", fiveHour: "$16", weekly: "$40" },
445
+ { name: "Provider", price: "$15", monthly: "PAYG", fiveHour: "\u2014", weekly: "\u2014" },
446
+ { name: "Max 10x", price: "$100", monthly: "$150", fiveHour: "$45", weekly: "$90" },
447
+ { name: "Max 20x", price: "$200", monthly: "$300", fiveHour: "$90", weekly: "$180" },
448
+ { name: "Team Pro", price: "$40", monthly: "$40", fiveHour: "$12", weekly: "$24" }
449
+ ]
450
+ };
403
451
 
404
452
  // src/usage.ts
405
453
  var BAR_WIDTH = 12;
@@ -440,46 +488,34 @@ function relTime(resetAtMs, nowSecs) {
440
488
  return "<1m";
441
489
  }
442
490
  function parseIsoUtc(s) {
443
- const m = s.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)Z?/);
491
+ const m = s.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|[+-]\d{2}:?\d{2})?$/);
444
492
  if (!m)
445
493
  return;
446
- const [, y, mo, d, h, mi, sec] = m;
447
- return Date.UTC(+y, +mo - 1, +d, +h, +mi, Math.floor(+sec));
494
+ const [, y, mo, d, h, mi, sec, off] = m;
495
+ let offsetMs = 0;
496
+ if (off && off !== "Z") {
497
+ const sign = off.startsWith("-") ? -1 : 1;
498
+ const digits = off.slice(1).replace(":", "");
499
+ const hh = Number(digits.slice(0, 2));
500
+ const mm = Number(digits.slice(2, 4) || "0");
501
+ offsetMs = sign * (hh * 60 + mm) * 60000;
502
+ }
503
+ return Date.UTC(+y, +mo - 1, +d, +h, +mi, Math.floor(+sec)) - offsetMs;
448
504
  }
449
- var PLAN_CAPS = {
450
- "individual-go": 10,
451
- "individual-goat": 70,
452
- "individual-pro": 80,
453
- "individual-provider": 0,
454
- "individual-max": 150,
455
- "individual-ultra": 0,
456
- "teams-pro": 40
457
- };
505
+ var NAME_RULES = plans_default.nameRules;
506
+ var DEFAULT_NAME = plans_default.defaultName;
507
+ var CAPS = plans_default.caps;
508
+ var PLANS = plans_default.plans;
458
509
  function planMonthlyCap(planId) {
459
- if (planId.includes("max-20"))
460
- return 300;
461
- const cap = PLAN_CAPS[planId];
462
- return cap && cap > 0 ? cap : undefined;
510
+ return CAPS[planName(planId)] ?? undefined;
463
511
  }
464
512
  function planName(planId) {
465
513
  const id = planId.toLowerCase();
466
- if (id.includes("max-20"))
467
- return "Max 20x";
468
- if (id.includes("max"))
469
- return "Max 10x";
470
- if (id.includes("goat"))
471
- return "GOAT";
472
- if (id.includes("provider"))
473
- return "Provider";
474
- if (id.includes("ultra"))
475
- return "Ultra";
476
- if (id.includes("team"))
477
- return "Team Pro";
478
- if (id.includes("pro"))
479
- return "Pro";
480
- if (id.includes("go"))
481
- return "Go";
482
- return "Free";
514
+ for (const rule of NAME_RULES) {
515
+ if (rule.needles.every((n) => id.includes(n)))
516
+ return rule.name;
517
+ }
518
+ return DEFAULT_NAME;
483
519
  }
484
520
  function windowLine(label, w, nowSecs) {
485
521
  const status = w.exceeded ? " \xB7 **LIMIT EXCEEDED**" : "";
@@ -487,22 +523,13 @@ function windowLine(label, w, nowSecs) {
487
523
  `;
488
524
  }
489
525
  function plansTable(current) {
490
- const plans = [
491
- ["go", "Go", "$1", "$10", "$3", "$6"],
492
- ["goat", "GOAT", "$10", "$70", "$14", "$35"],
493
- ["pro", "Pro", "$20", "$80", "$16", "$40"],
494
- ["provider", "Provider", "$15", "PAYG", "\u2014", "\u2014"],
495
- ["max-10", "Max 10x", "$100", "$150", "$45", "$90"],
496
- ["max-20", "Max 20x", "$200", "$300", "$90", "$180"],
497
- ["team-pro", "Team Pro", "$40", "$40", "$12", "$24"]
498
- ];
499
- const cur = current.toLowerCase();
526
+ const mine = planName(current);
500
527
  let out = `| Plan | Price | Credits/mo | 5-hour | Weekly |
501
528
  |---|---|---|---|---|
502
529
  `;
503
- for (const [id, name, price, monthly, h5, wk] of plans) {
504
- const mark = cur.includes(id) ? "**" : "";
505
- out += `| ${mark}${name}${mark} | ${price}/mo | ${monthly} | ${h5} | ${wk} |
530
+ for (const { name, price, monthly, fiveHour, weekly } of PLANS) {
531
+ const mark = name === mine ? "**" : "";
532
+ out += `| ${mark}${name}${mark} | ${price}/mo | ${monthly} | ${fiveHour} | ${weekly} |
506
533
  `;
507
534
  }
508
535
  out += "\nWindows throttle only included monthly credits; on-demand (`/extra`) credits are never throttled.\n";
@@ -562,8 +589,16 @@ async function renderUsage(key) {
562
589
  }
563
590
 
564
591
  // src/index.ts
565
- var PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
566
- function toModelDefs(models, providerID, npm) {
592
+ var PROVIDER_BASE = `${API_BASE}/provider/v1`;
593
+ var MODEL_CAPABILITIES = {
594
+ temperature: true,
595
+ reasoning: true,
596
+ attachment: false,
597
+ toolcall: true,
598
+ input: { text: true, audio: false, image: false, video: false, pdf: false },
599
+ output: { text: true, audio: false, image: false, video: false, pdf: false }
600
+ };
601
+ function toModelDefs(models, providerID, npm, interleaved = false) {
567
602
  return Object.fromEntries(models.map((m) => [
568
603
  m.id,
569
604
  {
@@ -571,39 +606,32 @@ function toModelDefs(models, providerID, npm) {
571
606
  providerID,
572
607
  api: { id: m.id, url: PROVIDER_BASE, npm },
573
608
  name: m.name,
574
- capabilities: {
575
- temperature: true,
576
- reasoning: true,
577
- attachment: false,
578
- toolcall: true,
579
- interleaved: false,
580
- input: { text: true, audio: false, image: false, video: false, pdf: false },
581
- output: { text: true, audio: false, image: false, video: false, pdf: false }
582
- },
609
+ capabilities: { ...MODEL_CAPABILITIES, interleaved },
583
610
  cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
584
611
  limit: { context: m.contextLength || 128000, output: 32000 },
585
612
  status: "active",
586
613
  options: {},
587
614
  headers: {},
588
- release_date: ""
615
+ release_date: "",
616
+ ...interleaved !== false ? { interleaved } : {}
589
617
  }
590
618
  ]));
591
619
  }
592
620
  var CommandCodePlugin = async (_input) => {
593
621
  return {
594
622
  auth: {
595
- provider: "command-code",
623
+ provider: "command-code-anthropic",
596
624
  loader: async (getAuth) => {
597
625
  const auth = await getAuth();
598
626
  if (auth?.type !== "api") {
599
- throw new Error("No API key available. Please run '/connect' and choose Command Code.");
627
+ throw new Error("No API key available. Please run '/connect' and choose Command Code (Anthropic).");
600
628
  }
601
629
  return { apiKey: auth.key, baseURL: PROVIDER_BASE };
602
630
  },
603
631
  methods: [
604
632
  {
605
633
  type: "api",
606
- label: "Command Code API key",
634
+ label: "Command Code API key (Claude / Anthropic)",
607
635
  prompts: [
608
636
  {
609
637
  type: "text",
@@ -619,7 +647,7 @@ var CommandCodePlugin = async (_input) => {
619
647
  const me = await whoami(key);
620
648
  if (!me.success)
621
649
  return { type: "failed" };
622
- return { type: "success", key, provider: "command-code" };
650
+ return { type: "success", key, provider: "command-code-anthropic" };
623
651
  } catch {
624
652
  return { type: "failed" };
625
653
  }
@@ -631,48 +659,46 @@ var CommandCodePlugin = async (_input) => {
631
659
  cfg.provider ??= {};
632
660
  const existing = (id) => cfg.provider?.[id] ?? {};
633
661
  let split;
634
- try {
635
- split = await loadModels(await resolveKey());
636
- } catch {}
637
- const claudeDefs = split ? toModelDefs(split.claude, "command-code", "@ai-sdk/anthropic") : {};
638
- const openDefs = split ? toModelDefs(split.open, "command-code-open", "@ai-sdk/openai-compatible") : {};
639
- const userCc = existing("command-code");
640
- cfg.provider["command-code"] = {
641
- npm: userCc.options ? undefined : "@ai-sdk/anthropic",
642
- name: "Command Code",
643
- options: { baseURL: PROVIDER_BASE, ...userCc.options },
644
- models: { ...claudeDefs, ...userCc.models }
645
- };
646
- const userOpen = existing("command-code-open");
647
662
  let openKey;
648
663
  try {
649
664
  openKey = await resolveKey();
665
+ split = await loadModels(openKey);
650
666
  } catch {}
651
- cfg.provider["command-code-open"] = {
652
- npm: "@ai-sdk/openai-compatible",
653
- name: userOpen.options ? undefined : "Command Code (Open)",
667
+ const claudeDefs = split ? toModelDefs(split.claude, "command-code-anthropic", "@ai-sdk/anthropic") : {};
668
+ const openDefs = split ? toModelDefs(split.open, "command-code-openai", "@ai-sdk/openai-compatible", { field: "reasoning_content" }) : {};
669
+ const userAnthropic = existing("command-code-anthropic");
670
+ cfg.provider["command-code-anthropic"] = {
671
+ npm: userAnthropic.options ? undefined : "@ai-sdk/anthropic",
672
+ name: "Command Code (Anthropic)",
673
+ options: { baseURL: PROVIDER_BASE, ...userAnthropic.options },
674
+ models: { ...claudeDefs, ...userAnthropic.models }
675
+ };
676
+ const userOpenai = existing("command-code-openai");
677
+ cfg.provider["command-code-openai"] = {
678
+ npm: userOpenai.options ? undefined : "@ai-sdk/openai-compatible",
679
+ name: userOpenai.options ? undefined : "Command Code (OpenAI)",
654
680
  options: {
655
681
  baseURL: PROVIDER_BASE,
656
682
  ...openKey ? { apiKey: openKey } : {},
657
- ...userOpen.options
683
+ ...userOpenai.options
658
684
  },
659
- models: { ...openDefs, ...userOpen.models }
685
+ models: { ...openDefs, ...userOpenai.models }
660
686
  };
661
687
  cfg.command ??= {};
662
688
  cfg.command["cmd-usage"] = {
663
689
  description: "Show Command Code plan, credits, and usage windows",
664
- template: "Call the cmd_usage tool and present its markdown output verbatim to the user. If the tool errors, tell the user to run /connect (Command Code) and retry. $ARGUMENTS"
690
+ template: "Call the cmd_usage tool and present its markdown output verbatim to the user. If the tool errors, tell the user to run /connect (Command Code (Anthropic)) and retry. $ARGUMENTS"
665
691
  };
666
692
  },
667
693
  provider: {
668
- id: "command-code",
694
+ id: "command-code-anthropic",
669
695
  models: async (_provider, ctx) => {
670
696
  const key = await resolveKey(async () => {
671
697
  const a = ctx.auth;
672
698
  return a?.type === "api" && a.key ? { key: a.key } : undefined;
673
699
  });
674
700
  const split = await loadModels(key);
675
- return toModelDefs(split.claude, "command-code", "@ai-sdk/anthropic");
701
+ return toModelDefs(split.claude, "command-code-anthropic", "@ai-sdk/anthropic");
676
702
  }
677
703
  },
678
704
  tool: {
package/dist/key.d.ts CHANGED
@@ -1,8 +1,3 @@
1
- export declare function resolveKey(
2
- getAuth?: () => Promise<
3
- | {
4
- key: string;
5
- }
6
- | undefined
7
- >,
8
- ): Promise<string>;
1
+ export declare function resolveKey(getAuth?: () => Promise<{
2
+ key: string;
3
+ } | undefined>): Promise<string>;
package/dist/models.d.ts CHANGED
@@ -1,23 +1,16 @@
1
1
  export type CmdModel = {
2
- id: string;
3
- name: string;
4
- contextLength: number;
2
+ id: string;
3
+ name: string;
4
+ contextLength: number;
5
5
  };
6
6
  export type ModelSplit = {
7
- claude: CmdModel[];
8
- open: CmdModel[];
9
- gatedOut: string[];
10
- source: "live" | "cache" | "stale";
7
+ claude: CmdModel[];
8
+ open: CmdModel[];
11
9
  };
12
10
  export declare function isClaude(id: string): boolean;
13
11
  export declare function splitModels(models: CmdModel[]): {
14
- claude: CmdModel[];
15
- open: CmdModel[];
12
+ claude: CmdModel[];
13
+ open: CmdModel[];
16
14
  };
17
15
  /** Fetch live model list, apply plan gating, split by wire protocol. */
18
- export declare function loadModels(
19
- key: string,
20
- opts?: {
21
- skipPlan?: boolean;
22
- },
23
- ): Promise<ModelSplit>;
16
+ export declare function loadModels(key: string): Promise<ModelSplit>;
package/dist/usage.d.ts CHANGED
@@ -3,19 +3,12 @@ export declare function money(v: number): string;
3
3
  export declare function compact(n: number): string;
4
4
  export declare function bar(used: number, cap: number): string;
5
5
  export declare function pctStr(used: number, cap: number): string;
6
- export declare function relTime(
7
- resetAtMs: number | undefined,
8
- nowSecs: number,
9
- ): string;
10
- /** ISO date → epoch ms (UTC, no offset handling — off by hours at most). */
6
+ export declare function relTime(resetAtMs: number | undefined, nowSecs: number): string;
7
+ /** ISO 8601 → epoch ms. Handles trailing Z or a ±HH:MM / ±HHMM offset. */
11
8
  export declare function parseIsoUtc(s: string): number | undefined;
12
9
  export declare function planMonthlyCap(planId: string): number | undefined;
13
10
  export declare function planName(planId: string): string;
14
- export declare function windowLine(
15
- label: string,
16
- w: Window,
17
- nowSecs: number,
18
- ): string;
11
+ export declare function windowLine(label: string, w: Window, nowSecs: number): string;
19
12
  export declare function plansTable(current: string): string;
20
13
  /** Fetch billing data in parallel and render the markdown dashboard. */
21
14
  export declare function renderUsage(key: string): Promise<string>;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "scripts": {
9
- "build": "bun build src/index.ts --outdir dist --target bun --external @opencode-ai/plugin --external @opencode-ai/sdk && tsc --emitDeclarationOnly --declaration --outDir dist",
9
+ "build": "bun build src/index.ts --outdir dist --target bun --external @opencode-ai/plugin --external @opencode-ai/sdk && tsc -p tsconfig.build.json",
10
10
  "test": "bun test",
11
11
  "extract": "bun run scripts/extract-gating.ts",
12
12
  "typecheck": "tsc --noEmit"
@@ -18,7 +18,7 @@
18
18
  "@types/bun": "latest",
19
19
  "typescript": "^5"
20
20
  },
21
- "files": ["dist", "README.md", "LICENSE"],
21
+ "files": ["dist", "LICENSE"],
22
22
  "keywords": ["opencode", "command-code", "plugin", "provider", "ai-sdk"],
23
23
  "license": "MIT",
24
24
  "publishConfig": {