opencommand-plugin 0.0.3 → 0.0.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/README.md +30 -62
- package/dist/index.d.ts +6 -1
- package/dist/index.js +189 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,80 +1,48 @@
|
|
|
1
|
-
# OpenCommand Plugin
|
|
1
|
+
# OpenCommand Plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenCode plugin for CommandCode. It starts the local OpenCommand proxy, stores CommandCode auth locally, registers the `opencommand` provider, and loads the right model list for the active CommandCode plan.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
7
|
+
- Starts/stops the Go proxy automatically.
|
|
8
|
+
- Uses a dynamic local port and exposes `http://localhost:<port>/v1` to OpenCode.
|
|
9
|
+
- Stores the CommandCode API token locally in `~/.opencommand/opencommand-secrets.json`.
|
|
10
|
+
- Supports `COMMAND_CODE_TOKEN` and `COMMANDCODE_API_KEY` environment overrides.
|
|
11
|
+
- Attempts local browser cookie discovery for CommandCode Studio usage scraping.
|
|
12
|
+
- Registers OpenCode provider `opencommand` using `@ai-sdk/openai-compatible`.
|
|
13
|
+
- Detects the active CommandCode plan and registers the matching OpenCode model list.
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## Auth
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
Handles proxy process lifecycle:
|
|
18
|
-
- Start/stop proxy on dynamic port
|
|
19
|
-
- Health check polling
|
|
20
|
-
- Process signal handling
|
|
17
|
+
Primary token key:
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- Mock implementation (real version uses OpenCode API)
|
|
27
|
-
|
|
28
|
-
### OpenCommandPlugin
|
|
29
|
-
Main plugin interface:
|
|
30
|
-
- activate() - Initialize on editor startup
|
|
31
|
-
- deactivate() - Clean up on shutdown
|
|
32
|
-
- setSessionToken() - Configure CommandCode credentials
|
|
33
|
-
- getProxyConfig() - Get current config
|
|
34
|
-
|
|
35
|
-
## Building
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm install
|
|
39
|
-
npm run build
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"opencommand.command_code_token": "user_xxx..."
|
|
22
|
+
}
|
|
40
23
|
```
|
|
41
24
|
|
|
42
|
-
|
|
25
|
+
Do not put this token in synced `opencode.jsonc`.
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
npm test
|
|
46
|
-
```
|
|
27
|
+
## Models in v0.0.4
|
|
47
28
|
|
|
48
|
-
|
|
29
|
+
The proxy detects the active CommandCode subscription through `/alpha/billing/subscriptions` and filters models:
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Token retrieved from SecretStorage
|
|
54
|
-
- Proxy started on random available port
|
|
55
|
-
- Configuration saved to OpenCode
|
|
56
|
-
- Clients use http://localhost:PORT/v1 for API calls
|
|
57
|
-
4. On shutdown:
|
|
58
|
-
- Proxy process terminated gracefully
|
|
59
|
-
- SIGTERM → wait 5s → SIGKILL
|
|
31
|
+
- Go: open-source models only.
|
|
32
|
+
- Pro: open-source + premium, excluding Opus.
|
|
33
|
+
- Max / Ultra / Teams Pro: open-source + premium including Opus.
|
|
60
34
|
|
|
61
|
-
|
|
35
|
+
If detection fails, the plugin falls back to the local proxy model endpoint and then to the Go/open-source model list.
|
|
62
36
|
|
|
63
|
-
|
|
37
|
+
## Build
|
|
64
38
|
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"proxyUrl": "http://localhost:3001",
|
|
69
|
-
"apiBaseUrl": "http://localhost:3001/v1"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
39
|
+
```bash
|
|
40
|
+
npm ci
|
|
41
|
+
npm run build
|
|
72
42
|
```
|
|
73
43
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## Related Issues
|
|
44
|
+
## Test
|
|
77
45
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
46
|
+
```bash
|
|
47
|
+
npm test -- --runInBand
|
|
48
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ interface CommandCodeModelDefinition {
|
|
|
11
11
|
id: string;
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
|
14
|
+
category?: string;
|
|
14
15
|
reasoning?: boolean;
|
|
15
16
|
context: number;
|
|
16
17
|
output: number;
|
|
@@ -93,7 +94,11 @@ export declare class OpenCommandPlugin {
|
|
|
93
94
|
private restartIfPossible;
|
|
94
95
|
getProxyConfig(): string;
|
|
95
96
|
}
|
|
96
|
-
export declare function
|
|
97
|
+
export declare function fetchOpenCommandModels(baseURL: string): Promise<CommandCodeModelDefinition[] | undefined>;
|
|
98
|
+
export declare function commandCodeModelsForPlan(planID: string): CommandCodeModelDefinition[];
|
|
99
|
+
export declare function fetchCommandCodePlanModels(commandCodeToken: string, apiBaseURL?: string): Promise<CommandCodeModelDefinition[] | undefined>;
|
|
100
|
+
export declare function parseCommandCodePlanID(body: unknown): string | undefined;
|
|
101
|
+
export declare function registerOpenCommandProvider(config: OpenCodeConfig, baseURL?: string, modelDefinitions?: CommandCodeModelDefinition[]): void;
|
|
97
102
|
export declare const OpenCommandOpenCodePlugin: () => Promise<{
|
|
98
103
|
auth: {
|
|
99
104
|
provider: string;
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.OpenCommandOpenCodePlugin = exports.OpenCommandPlugin = exports.BrowserCookieExtractor = exports.SecretStorage = exports.ProxyManager = exports.COMMAND_CODE_GO_PLAN_MODELS = void 0;
|
|
37
|
+
exports.fetchOpenCommandModels = fetchOpenCommandModels;
|
|
38
|
+
exports.commandCodeModelsForPlan = commandCodeModelsForPlan;
|
|
39
|
+
exports.fetchCommandCodePlanModels = fetchCommandCodePlanModels;
|
|
40
|
+
exports.parseCommandCodePlanID = parseCommandCodePlanID;
|
|
37
41
|
exports.registerOpenCommandProvider = registerOpenCommandProvider;
|
|
38
42
|
const cp = __importStar(require("child_process"));
|
|
39
43
|
const crypto = __importStar(require("crypto"));
|
|
@@ -46,6 +50,7 @@ const PROVIDER_ID = "opencommand";
|
|
|
46
50
|
const PROVIDER_NAME = "CommandCode";
|
|
47
51
|
const PROVIDER_API_KEY_PLACEHOLDER = "opencommand";
|
|
48
52
|
const DEFAULT_PROXY_BASE_URL = "http://localhost:3000/v1";
|
|
53
|
+
const COMMAND_CODE_API_BASE_URL = "https://api.commandcode.ai";
|
|
49
54
|
exports.COMMAND_CODE_GO_PLAN_MODELS = [
|
|
50
55
|
{
|
|
51
56
|
id: "deepseek/deepseek-v4-pro",
|
|
@@ -141,6 +146,86 @@ exports.COMMAND_CODE_GO_PLAN_MODELS = [
|
|
|
141
146
|
cost: { input: 0.1, output: 0.3, cache_read: 0.02 },
|
|
142
147
|
},
|
|
143
148
|
];
|
|
149
|
+
const COMMAND_CODE_PREMIUM_MODELS = [
|
|
150
|
+
{
|
|
151
|
+
id: "anthropic:claude-opus-4-7",
|
|
152
|
+
name: "Claude Opus 4.7",
|
|
153
|
+
description: "Premium Claude Opus coding and reasoning",
|
|
154
|
+
category: "premium",
|
|
155
|
+
reasoning: true,
|
|
156
|
+
context: 200000,
|
|
157
|
+
output: 8192,
|
|
158
|
+
cost: { input: 0, output: 0 },
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "anthropic:claude-opus-4-6",
|
|
162
|
+
name: "Claude Opus 4.6",
|
|
163
|
+
description: "Premium Claude Opus coding and reasoning",
|
|
164
|
+
category: "premium",
|
|
165
|
+
reasoning: true,
|
|
166
|
+
context: 200000,
|
|
167
|
+
output: 8192,
|
|
168
|
+
cost: { input: 0, output: 0 },
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "anthropic:claude-sonnet-4-6",
|
|
172
|
+
name: "Claude Sonnet 4.6",
|
|
173
|
+
description: "Premium Claude Sonnet coding",
|
|
174
|
+
category: "premium",
|
|
175
|
+
reasoning: true,
|
|
176
|
+
context: 200000,
|
|
177
|
+
output: 8192,
|
|
178
|
+
cost: { input: 0, output: 0 },
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: "anthropic:claude-haiku-4-5-20251001",
|
|
182
|
+
name: "Claude Haiku 4.5",
|
|
183
|
+
description: "Fast premium Claude model",
|
|
184
|
+
category: "premium",
|
|
185
|
+
context: 200000,
|
|
186
|
+
output: 8192,
|
|
187
|
+
cost: { input: 0, output: 0 },
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "openai:gpt-5.5",
|
|
191
|
+
name: "GPT-5.5",
|
|
192
|
+
description: "Premium OpenAI reasoning model",
|
|
193
|
+
category: "premium",
|
|
194
|
+
reasoning: true,
|
|
195
|
+
context: 200000,
|
|
196
|
+
output: 8192,
|
|
197
|
+
cost: { input: 0, output: 0 },
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "openai:gpt-5.4",
|
|
201
|
+
name: "GPT-5.4",
|
|
202
|
+
description: "Premium OpenAI coding model",
|
|
203
|
+
category: "premium",
|
|
204
|
+
reasoning: true,
|
|
205
|
+
context: 200000,
|
|
206
|
+
output: 8192,
|
|
207
|
+
cost: { input: 0, output: 0 },
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: "openai:gpt-5.4-mini",
|
|
211
|
+
name: "GPT-5.4 Mini",
|
|
212
|
+
description: "Efficient premium OpenAI coding model",
|
|
213
|
+
category: "premium",
|
|
214
|
+
context: 200000,
|
|
215
|
+
output: 8192,
|
|
216
|
+
cost: { input: 0, output: 0 },
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "openai:gpt-5.3-codex",
|
|
220
|
+
name: "GPT-5.3 Codex",
|
|
221
|
+
description: "Premium OpenAI Codex coding model",
|
|
222
|
+
category: "premium",
|
|
223
|
+
reasoning: true,
|
|
224
|
+
context: 200000,
|
|
225
|
+
output: 8192,
|
|
226
|
+
cost: { input: 0, output: 0 },
|
|
227
|
+
},
|
|
228
|
+
];
|
|
144
229
|
class ProxyManager {
|
|
145
230
|
constructor(binaryPath = resolveProxyBinaryPath()) {
|
|
146
231
|
this.proxyProcess = null;
|
|
@@ -622,6 +707,7 @@ function openCodeModelConfig(model) {
|
|
|
622
707
|
id: model.id,
|
|
623
708
|
name: model.name,
|
|
624
709
|
description: model.description,
|
|
710
|
+
category: model.category ?? "opensource",
|
|
625
711
|
reasoning: model.reasoning ?? false,
|
|
626
712
|
tool_call: true,
|
|
627
713
|
attachment: false,
|
|
@@ -636,9 +722,100 @@ function openCodeModelConfig(model) {
|
|
|
636
722
|
},
|
|
637
723
|
};
|
|
638
724
|
}
|
|
639
|
-
function
|
|
725
|
+
function modelFromProxy(model) {
|
|
726
|
+
if (!model.id)
|
|
727
|
+
return undefined;
|
|
728
|
+
return {
|
|
729
|
+
id: model.id,
|
|
730
|
+
name: model.name ?? model.id,
|
|
731
|
+
description: model.description ?? "CommandCode model",
|
|
732
|
+
category: model.category,
|
|
733
|
+
reasoning: model.reasoning ?? false,
|
|
734
|
+
context: model.limit?.context ?? 200000,
|
|
735
|
+
output: model.limit?.output ?? 8192,
|
|
736
|
+
cost: model.cost ?? { input: 0, output: 0 },
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
async function fetchOpenCommandModels(baseURL) {
|
|
740
|
+
try {
|
|
741
|
+
const response = await fetch(`${baseURL.replace(/\/$/, "")}/models`, {
|
|
742
|
+
headers: { Authorization: `Bearer ${PROVIDER_API_KEY_PLACEHOLDER}` },
|
|
743
|
+
});
|
|
744
|
+
if (!response.ok)
|
|
745
|
+
return undefined;
|
|
746
|
+
const body = (await response.json());
|
|
747
|
+
const models = (body.data ?? [])
|
|
748
|
+
.map(modelFromProxy)
|
|
749
|
+
.filter((model) => Boolean(model));
|
|
750
|
+
return models.length > 0 ? models : undefined;
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
console.debug("Could not fetch OpenCommand models from local proxy:", error);
|
|
754
|
+
return undefined;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function commandCodeModelsForPlan(planID) {
|
|
758
|
+
const models = [...exports.COMMAND_CODE_GO_PLAN_MODELS];
|
|
759
|
+
if (!planCanUsePremium(planID))
|
|
760
|
+
return models;
|
|
761
|
+
for (const model of COMMAND_CODE_PREMIUM_MODELS) {
|
|
762
|
+
if (isOpusModel(model.id) && !planCanUseOpus(planID))
|
|
763
|
+
continue;
|
|
764
|
+
models.push(model);
|
|
765
|
+
}
|
|
766
|
+
return models;
|
|
767
|
+
}
|
|
768
|
+
async function fetchCommandCodePlanModels(commandCodeToken, apiBaseURL = COMMAND_CODE_API_BASE_URL) {
|
|
769
|
+
try {
|
|
770
|
+
const response = await fetch(`${apiBaseURL.replace(/\/$/, "")}/alpha/billing/subscriptions`, {
|
|
771
|
+
headers: {
|
|
772
|
+
Accept: "application/json",
|
|
773
|
+
Authorization: `Bearer ${commandCodeToken}`,
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
if (!response.ok)
|
|
777
|
+
return undefined;
|
|
778
|
+
const body = await response.json();
|
|
779
|
+
const planID = parseCommandCodePlanID(body);
|
|
780
|
+
return planID ? commandCodeModelsForPlan(planID) : undefined;
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
console.debug("Could not detect CommandCode plan for model registration:", error);
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function parseCommandCodePlanID(body) {
|
|
788
|
+
const root = body;
|
|
789
|
+
if (typeof root?.planId === "string" && root.planId.trim())
|
|
790
|
+
return root.planId.trim();
|
|
791
|
+
const data = root?.data;
|
|
792
|
+
if (Array.isArray(data)) {
|
|
793
|
+
const active = data.find((item) => {
|
|
794
|
+
const entry = item;
|
|
795
|
+
return typeof entry.planId === "string" && entry.status === "active";
|
|
796
|
+
});
|
|
797
|
+
const first = active ?? data[0];
|
|
798
|
+
return typeof first?.planId === "string" && first.planId.trim()
|
|
799
|
+
? first.planId.trim()
|
|
800
|
+
: undefined;
|
|
801
|
+
}
|
|
802
|
+
const single = data;
|
|
803
|
+
return typeof single?.planId === "string" && single.planId.trim()
|
|
804
|
+
? single.planId.trim()
|
|
805
|
+
: undefined;
|
|
806
|
+
}
|
|
807
|
+
function planCanUsePremium(planID) {
|
|
808
|
+
return ["individual-pro", "individual-max", "individual-ultra", "teams-pro"].includes(planID.toLowerCase());
|
|
809
|
+
}
|
|
810
|
+
function planCanUseOpus(planID) {
|
|
811
|
+
return ["individual-max", "individual-ultra", "teams-pro"].includes(planID.toLowerCase());
|
|
812
|
+
}
|
|
813
|
+
function isOpusModel(modelID) {
|
|
814
|
+
return modelID.toLowerCase().includes("opus");
|
|
815
|
+
}
|
|
816
|
+
function registerOpenCommandProvider(config, baseURL = DEFAULT_PROXY_BASE_URL, modelDefinitions = exports.COMMAND_CODE_GO_PLAN_MODELS) {
|
|
640
817
|
const models = {};
|
|
641
|
-
for (const model of
|
|
818
|
+
for (const model of modelDefinitions) {
|
|
642
819
|
models[model.id] = openCodeModelConfig(model);
|
|
643
820
|
}
|
|
644
821
|
config.provider = {
|
|
@@ -674,7 +851,16 @@ const OpenCommandOpenCodePlugin = async () => ({
|
|
|
674
851
|
methods: [],
|
|
675
852
|
},
|
|
676
853
|
config: async (config) => {
|
|
677
|
-
|
|
854
|
+
let baseURL = DEFAULT_PROXY_BASE_URL;
|
|
855
|
+
let models;
|
|
856
|
+
const proxyConfig = await getRuntimePlugin().ensureStarted();
|
|
857
|
+
if (proxyConfig) {
|
|
858
|
+
baseURL = `http://localhost:${proxyConfig.port}/v1`;
|
|
859
|
+
models =
|
|
860
|
+
(await fetchCommandCodePlanModels(proxyConfig.commandCodeToken)) ??
|
|
861
|
+
(await fetchOpenCommandModels(baseURL));
|
|
862
|
+
}
|
|
863
|
+
registerOpenCommandProvider(config, baseURL, models ?? exports.COMMAND_CODE_GO_PLAN_MODELS);
|
|
678
864
|
},
|
|
679
865
|
});
|
|
680
866
|
exports.OpenCommandOpenCodePlugin = OpenCommandOpenCodePlugin;
|