opencommand-plugin 0.0.2 → 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 +48 -3
- package/dist/index.js +380 -15
- 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
|
@@ -7,15 +7,43 @@ export interface ProxyStartConfig {
|
|
|
7
7
|
commandCodeToken: string;
|
|
8
8
|
sessionCookie?: string;
|
|
9
9
|
}
|
|
10
|
+
interface CommandCodeModelDefinition {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category?: string;
|
|
15
|
+
reasoning?: boolean;
|
|
16
|
+
context: number;
|
|
17
|
+
output: number;
|
|
18
|
+
cost: {
|
|
19
|
+
input: number;
|
|
20
|
+
output: number;
|
|
21
|
+
cache_read?: number;
|
|
22
|
+
cache_write?: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
interface OpenCodeProviderConfig {
|
|
26
|
+
npm: string;
|
|
27
|
+
name: string;
|
|
28
|
+
options: {
|
|
29
|
+
apiKey: string;
|
|
30
|
+
baseURL: string;
|
|
31
|
+
};
|
|
32
|
+
models: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
interface OpenCodeConfig {
|
|
35
|
+
provider?: Record<string, OpenCodeProviderConfig>;
|
|
36
|
+
}
|
|
10
37
|
interface CookiePair {
|
|
11
38
|
name: string;
|
|
12
39
|
value: string;
|
|
13
40
|
}
|
|
41
|
+
export declare const COMMAND_CODE_GO_PLAN_MODELS: CommandCodeModelDefinition[];
|
|
14
42
|
export declare class ProxyManager {
|
|
15
43
|
private proxyProcess;
|
|
16
44
|
private config;
|
|
17
45
|
private proxyBinaryPath;
|
|
18
|
-
constructor(binaryPath
|
|
46
|
+
constructor(binaryPath?: string);
|
|
19
47
|
findAvailablePort(): Promise<number>;
|
|
20
48
|
start(startConfig: ProxyStartConfig): Promise<ProxyConfig>;
|
|
21
49
|
stop(): Promise<void>;
|
|
@@ -52,8 +80,9 @@ export declare class OpenCommandPlugin {
|
|
|
52
80
|
private readonly commandCodeTokenKey;
|
|
53
81
|
private readonly legacyTokenKey;
|
|
54
82
|
private readonly sessionCookieKey;
|
|
55
|
-
constructor(proxyBinaryPath
|
|
83
|
+
constructor(proxyBinaryPath?: string, storageDir?: string);
|
|
56
84
|
activate(): Promise<void>;
|
|
85
|
+
ensureStarted(): Promise<ProxyConfig | undefined>;
|
|
57
86
|
deactivate(): Promise<void>;
|
|
58
87
|
private loadCommandCodeToken;
|
|
59
88
|
private loadSessionCookie;
|
|
@@ -65,4 +94,20 @@ export declare class OpenCommandPlugin {
|
|
|
65
94
|
private restartIfPossible;
|
|
66
95
|
getProxyConfig(): string;
|
|
67
96
|
}
|
|
68
|
-
export
|
|
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;
|
|
102
|
+
export declare const OpenCommandOpenCodePlugin: () => Promise<{
|
|
103
|
+
auth: {
|
|
104
|
+
provider: string;
|
|
105
|
+
loader: () => Promise<{
|
|
106
|
+
apiKey: string;
|
|
107
|
+
baseURL: string;
|
|
108
|
+
} | null>;
|
|
109
|
+
methods: never[];
|
|
110
|
+
};
|
|
111
|
+
config: (config: OpenCodeConfig) => Promise<void>;
|
|
112
|
+
}>;
|
|
113
|
+
export default OpenCommandOpenCodePlugin;
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.OpenCommandPlugin = exports.BrowserCookieExtractor = exports.SecretStorage = exports.ProxyManager = void 0;
|
|
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;
|
|
41
|
+
exports.registerOpenCommandProvider = registerOpenCommandProvider;
|
|
37
42
|
const cp = __importStar(require("child_process"));
|
|
38
43
|
const crypto = __importStar(require("crypto"));
|
|
39
44
|
const fs = __importStar(require("fs"));
|
|
@@ -41,8 +46,188 @@ const fsp = __importStar(require("fs/promises"));
|
|
|
41
46
|
const net = __importStar(require("net"));
|
|
42
47
|
const os = __importStar(require("os"));
|
|
43
48
|
const path = __importStar(require("path"));
|
|
49
|
+
const PROVIDER_ID = "opencommand";
|
|
50
|
+
const PROVIDER_NAME = "CommandCode";
|
|
51
|
+
const PROVIDER_API_KEY_PLACEHOLDER = "opencommand";
|
|
52
|
+
const DEFAULT_PROXY_BASE_URL = "http://localhost:3000/v1";
|
|
53
|
+
const COMMAND_CODE_API_BASE_URL = "https://api.commandcode.ai";
|
|
54
|
+
exports.COMMAND_CODE_GO_PLAN_MODELS = [
|
|
55
|
+
{
|
|
56
|
+
id: "deepseek/deepseek-v4-pro",
|
|
57
|
+
name: "DeepSeek V4 Pro",
|
|
58
|
+
description: "Hybrid-attention long-context reasoning",
|
|
59
|
+
reasoning: true,
|
|
60
|
+
context: 1000000,
|
|
61
|
+
output: 8192,
|
|
62
|
+
cost: { input: 1.74, output: 3.48, cache_read: 0.0145 },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "deepseek/deepseek-v4-flash",
|
|
66
|
+
name: "DeepSeek V4 Flash",
|
|
67
|
+
description: "Fast hybrid-attention reasoning",
|
|
68
|
+
reasoning: true,
|
|
69
|
+
context: 1000000,
|
|
70
|
+
output: 8192,
|
|
71
|
+
cost: { input: 0.14, output: 0.28, cache_read: 0.01 },
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "moonshotai/Kimi-K2.6",
|
|
75
|
+
name: "Kimi K2.6",
|
|
76
|
+
description: "Long-horizon coding with vision",
|
|
77
|
+
context: 256000,
|
|
78
|
+
output: 8192,
|
|
79
|
+
cost: { input: 0.95, output: 4.0, cache_read: 0.16 },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "moonshotai/Kimi-K2.5",
|
|
83
|
+
name: "Kimi K2.5",
|
|
84
|
+
description: "Multimodal frontend coding",
|
|
85
|
+
context: 256000,
|
|
86
|
+
output: 8192,
|
|
87
|
+
cost: { input: 0.6, output: 3.0, cache_read: 0.1 },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "zai-org/GLM-5.1",
|
|
91
|
+
name: "GLM-5.1",
|
|
92
|
+
description: "Long-horizon autonomous coding agent",
|
|
93
|
+
context: 200000,
|
|
94
|
+
output: 8192,
|
|
95
|
+
cost: { input: 1.4, output: 4.4, cache_read: 0.26 },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "zai-org/GLM-5",
|
|
99
|
+
name: "GLM-5",
|
|
100
|
+
description: "Multi-mode thinking and long-range planning",
|
|
101
|
+
context: 200000,
|
|
102
|
+
output: 8192,
|
|
103
|
+
cost: { input: 1.0, output: 3.2, cache_read: 0.2 },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "MiniMaxAI/MiniMax-M2.7",
|
|
107
|
+
name: "MiniMax M2.7",
|
|
108
|
+
description: "End-to-end software engineering agent",
|
|
109
|
+
context: 200000,
|
|
110
|
+
output: 8192,
|
|
111
|
+
cost: { input: 0.3, output: 1.2, cache_read: 0.06 },
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "MiniMaxAI/MiniMax-M2.5",
|
|
115
|
+
name: "MiniMax M2.5",
|
|
116
|
+
description: "Cross-platform full-stack agentic development",
|
|
117
|
+
context: 200000,
|
|
118
|
+
output: 8192,
|
|
119
|
+
cost: { input: 0.27, output: 0.95, cache_read: 0.03 },
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "Qwen/Qwen3.6-Max-Preview",
|
|
123
|
+
name: "Qwen 3.6 Max Preview",
|
|
124
|
+
description: "Vibe coding and efficient agent execution",
|
|
125
|
+
reasoning: true,
|
|
126
|
+
context: 200000,
|
|
127
|
+
output: 8192,
|
|
128
|
+
cost: { input: 1.3, output: 7.8, cache_read: 0.26, cache_write: 1.63 },
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "Qwen/Qwen3.6-Plus",
|
|
132
|
+
name: "Qwen 3.6 Plus",
|
|
133
|
+
description: "Agentic coding and reasoning",
|
|
134
|
+
reasoning: true,
|
|
135
|
+
context: 200000,
|
|
136
|
+
output: 8192,
|
|
137
|
+
cost: { input: 0.5, output: 3.0, cache_read: 0.1 },
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "stepfun/Step-3.5-Flash",
|
|
141
|
+
name: "Step 3.5 Flash",
|
|
142
|
+
description: "Fast sparse-MoE agentic reasoning",
|
|
143
|
+
reasoning: true,
|
|
144
|
+
context: 1000000,
|
|
145
|
+
output: 8192,
|
|
146
|
+
cost: { input: 0.1, output: 0.3, cache_read: 0.02 },
|
|
147
|
+
},
|
|
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
|
+
];
|
|
44
229
|
class ProxyManager {
|
|
45
|
-
constructor(binaryPath) {
|
|
230
|
+
constructor(binaryPath = resolveProxyBinaryPath()) {
|
|
46
231
|
this.proxyProcess = null;
|
|
47
232
|
this.config = null;
|
|
48
233
|
this.proxyBinaryPath = binaryPath;
|
|
@@ -386,7 +571,7 @@ const firefoxCookieQuery = [
|
|
|
386
571
|
"SELECT host, name, value, '' FROM moz_cookies WHERE host LIKE '%commandcode.ai%';",
|
|
387
572
|
].join("\n");
|
|
388
573
|
class OpenCommandPlugin {
|
|
389
|
-
constructor(proxyBinaryPath, storageDir) {
|
|
574
|
+
constructor(proxyBinaryPath = resolveProxyBinaryPath(), storageDir) {
|
|
390
575
|
this.commandCodeTokenKey = "opencommand.command_code_token";
|
|
391
576
|
this.legacyTokenKey = "opencommand.cc_session_token";
|
|
392
577
|
this.sessionCookieKey = "opencommand.cc_session_cookie";
|
|
@@ -397,26 +582,34 @@ class OpenCommandPlugin {
|
|
|
397
582
|
}
|
|
398
583
|
async activate() {
|
|
399
584
|
console.log("OpenCommand Plugin activating...");
|
|
585
|
+
try {
|
|
586
|
+
const config = await this.ensureStarted();
|
|
587
|
+
if (config)
|
|
588
|
+
console.log(`✓ OpenCommand proxy ready at ${config.port}`);
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
console.error("Failed to start proxy:", error);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async ensureStarted() {
|
|
595
|
+
const existingConfig = this.proxyManager.getConfig();
|
|
596
|
+
if (existingConfig && this.proxyManager.isRunning())
|
|
597
|
+
return existingConfig;
|
|
400
598
|
const commandCodeToken = await this.loadCommandCodeToken();
|
|
401
599
|
if (!commandCodeToken) {
|
|
402
600
|
console.warn("No COMMAND_CODE_TOKEN found. Please configure your CommandCode API key.");
|
|
403
|
-
return;
|
|
601
|
+
return undefined;
|
|
404
602
|
}
|
|
405
603
|
const sessionCookie = await this.loadSessionCookie();
|
|
406
604
|
if (!sessionCookie) {
|
|
407
605
|
console.warn("No CommandCode Studio cookie found. Inference works, but usage scraping may be unavailable.");
|
|
408
606
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
this.saveOpenCodeConfig(config);
|
|
416
|
-
}
|
|
417
|
-
catch (error) {
|
|
418
|
-
console.error("Failed to start proxy:", error);
|
|
419
|
-
}
|
|
607
|
+
const config = await this.proxyManager.start({
|
|
608
|
+
commandCodeToken,
|
|
609
|
+
sessionCookie,
|
|
610
|
+
});
|
|
611
|
+
this.saveOpenCodeConfig(config);
|
|
612
|
+
return config;
|
|
420
613
|
}
|
|
421
614
|
async deactivate() {
|
|
422
615
|
console.log("OpenCommand Plugin deactivating...");
|
|
@@ -500,3 +693,175 @@ function firstDefined(...values) {
|
|
|
500
693
|
}
|
|
501
694
|
return undefined;
|
|
502
695
|
}
|
|
696
|
+
function resolveProxyBinaryPath() {
|
|
697
|
+
const candidates = [
|
|
698
|
+
process.env.OPENCOMMAND_PROXY_PATH,
|
|
699
|
+
path.join(os.homedir(), ".opencommand", "proxy"),
|
|
700
|
+
path.join(process.cwd(), "packages", "proxy", "proxy"),
|
|
701
|
+
path.join(__dirname, "proxy"),
|
|
702
|
+
].filter((candidate) => Boolean(candidate));
|
|
703
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || candidates[candidates.length - 1];
|
|
704
|
+
}
|
|
705
|
+
function openCodeModelConfig(model) {
|
|
706
|
+
return {
|
|
707
|
+
id: model.id,
|
|
708
|
+
name: model.name,
|
|
709
|
+
description: model.description,
|
|
710
|
+
category: model.category ?? "opensource",
|
|
711
|
+
reasoning: model.reasoning ?? false,
|
|
712
|
+
tool_call: true,
|
|
713
|
+
attachment: false,
|
|
714
|
+
cost: model.cost,
|
|
715
|
+
limit: {
|
|
716
|
+
context: model.context,
|
|
717
|
+
output: model.output,
|
|
718
|
+
},
|
|
719
|
+
modalities: {
|
|
720
|
+
input: ["text"],
|
|
721
|
+
output: ["text"],
|
|
722
|
+
},
|
|
723
|
+
};
|
|
724
|
+
}
|
|
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) {
|
|
817
|
+
const models = {};
|
|
818
|
+
for (const model of modelDefinitions) {
|
|
819
|
+
models[model.id] = openCodeModelConfig(model);
|
|
820
|
+
}
|
|
821
|
+
config.provider = {
|
|
822
|
+
...(config.provider ?? {}),
|
|
823
|
+
[PROVIDER_ID]: {
|
|
824
|
+
npm: "@ai-sdk/openai-compatible",
|
|
825
|
+
name: PROVIDER_NAME,
|
|
826
|
+
options: {
|
|
827
|
+
apiKey: PROVIDER_API_KEY_PLACEHOLDER,
|
|
828
|
+
baseURL,
|
|
829
|
+
},
|
|
830
|
+
models,
|
|
831
|
+
},
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
let runtimePlugin;
|
|
835
|
+
function getRuntimePlugin() {
|
|
836
|
+
runtimePlugin ?? (runtimePlugin = new OpenCommandPlugin());
|
|
837
|
+
return runtimePlugin;
|
|
838
|
+
}
|
|
839
|
+
const OpenCommandOpenCodePlugin = async () => ({
|
|
840
|
+
auth: {
|
|
841
|
+
provider: PROVIDER_ID,
|
|
842
|
+
loader: async () => {
|
|
843
|
+
const proxyConfig = await getRuntimePlugin().ensureStarted();
|
|
844
|
+
if (!proxyConfig)
|
|
845
|
+
return null;
|
|
846
|
+
return {
|
|
847
|
+
apiKey: PROVIDER_API_KEY_PLACEHOLDER,
|
|
848
|
+
baseURL: `http://localhost:${proxyConfig.port}/v1`,
|
|
849
|
+
};
|
|
850
|
+
},
|
|
851
|
+
methods: [],
|
|
852
|
+
},
|
|
853
|
+
config: async (config) => {
|
|
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);
|
|
864
|
+
},
|
|
865
|
+
});
|
|
866
|
+
exports.OpenCommandOpenCodePlugin = OpenCommandOpenCodePlugin;
|
|
867
|
+
exports.default = exports.OpenCommandOpenCodePlugin;
|