@ai-ide-bridge/cli 1.0.5 → 1.1.1
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/.turbo/turbo-build.log +1 -1
- package/dist/commands/configure.js +78 -10
- package/dist/commands/daemon.d.ts +1 -0
- package/dist/commands/daemon.js +107 -13
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/init.js +70 -5
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +62 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +12 -0
- package/dist/commands/start.js +4 -4
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +43 -0
- package/dist/core/daemon-session.d.ts +14 -0
- package/dist/core/daemon-session.js +179 -0
- package/dist/core/daemon.d.ts +16 -0
- package/dist/core/daemon.js +168 -0
- package/dist/core/formatter.d.ts +3 -0
- package/dist/core/formatter.js +44 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/parser.d.ts +164 -0
- package/dist/core/parser.js +37 -0
- package/dist/core/registry.d.ts +16 -0
- package/dist/core/registry.js +53 -0
- package/dist/core/server.d.ts +19 -0
- package/dist/core/server.js +185 -0
- package/dist/core/session.d.ts +11 -0
- package/dist/core/session.js +39 -0
- package/dist/core/types.d.ts +166 -0
- package/dist/core/types.js +44 -0
- package/dist/index.js +22 -5
- package/dist/oauth/device-flow.d.ts +12 -0
- package/dist/oauth/device-flow.js +93 -0
- package/dist/oauth/flow.d.ts +11 -0
- package/dist/oauth/flow.js +75 -0
- package/dist/oauth/index.d.ts +6 -0
- package/dist/oauth/index.js +5 -0
- package/dist/oauth/lifecycle.d.ts +13 -0
- package/dist/oauth/lifecycle.js +56 -0
- package/dist/oauth/providers.d.ts +2 -0
- package/dist/oauth/providers.js +19 -0
- package/dist/oauth/storage-file.d.ts +2 -0
- package/dist/oauth/storage-file.js +68 -0
- package/dist/oauth/storage.d.ts +2 -0
- package/dist/oauth/storage.js +4 -0
- package/dist/oauth/types.d.ts +44 -0
- package/dist/oauth/types.js +1 -0
- package/dist/plugins/copilot/auth.d.ts +7 -0
- package/dist/plugins/copilot/auth.js +30 -0
- package/dist/plugins/copilot/index.d.ts +5 -0
- package/dist/plugins/copilot/index.js +4 -0
- package/dist/plugins/copilot/plugin.d.ts +8 -0
- package/dist/plugins/copilot/plugin.js +29 -0
- package/dist/plugins/copilot/session.d.ts +8 -0
- package/dist/plugins/copilot/session.js +115 -0
- package/dist/plugins/copilot/tools.d.ts +10 -0
- package/dist/plugins/copilot/tools.js +10 -0
- package/dist/plugins/copilot/types.d.ts +15 -0
- package/dist/plugins/copilot/types.js +27 -0
- package/dist/plugins/cursor/index.d.ts +2 -0
- package/dist/plugins/cursor/index.js +2 -0
- package/dist/plugins/cursor/plugin.d.ts +8 -0
- package/dist/plugins/cursor/plugin.js +36 -0
- package/dist/plugins/cursor/session.d.ts +11 -0
- package/dist/plugins/cursor/session.js +69 -0
- package/dist/plugins/cursor/tools.d.ts +11 -0
- package/dist/plugins/cursor/tools.js +13 -0
- package/dist/plugins/windsurf/auth.d.ts +3 -0
- package/dist/plugins/windsurf/auth.js +20 -0
- package/dist/plugins/windsurf/daemon.d.ts +6 -0
- package/dist/plugins/windsurf/daemon.js +16 -0
- package/dist/plugins/windsurf/index.d.ts +5 -0
- package/dist/plugins/windsurf/index.js +4 -0
- package/dist/plugins/windsurf/models.d.ts +2 -0
- package/dist/plugins/windsurf/models.js +42 -0
- package/dist/plugins/windsurf/plugin.d.ts +8 -0
- package/dist/plugins/windsurf/plugin.js +31 -0
- package/dist/plugins/windsurf/session.d.ts +5 -0
- package/dist/plugins/windsurf/session.js +6 -0
- package/dist/plugins/windsurf/tools.d.ts +3 -0
- package/dist/plugins/windsurf/tools.js +10 -0
- package/dist/plugins/windsurf/types.d.ts +22 -0
- package/dist/plugins/windsurf/types.js +1 -0
- package/dist/utils/config.d.ts +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/opencode.d.ts +3 -1
- package/dist/utils/opencode.js +3 -3
- package/dist/utils/platform.d.ts +1 -0
- package/dist/utils/platform.js +3 -0
- package/package.json +3 -5
- package/src/commands/configure.ts +107 -12
- package/src/commands/daemon.ts +112 -13
- package/src/commands/doctor.ts +1 -1
- package/src/commands/init.ts +72 -5
- package/src/commands/login.ts +98 -0
- package/src/commands/logout.ts +15 -0
- package/src/commands/start.ts +4 -4
- package/src/core/config.ts +45 -0
- package/src/core/daemon-session.ts +199 -0
- package/src/core/daemon.ts +206 -0
- package/src/core/formatter.ts +56 -0
- package/src/core/index.ts +9 -0
- package/src/core/parser.ts +47 -0
- package/src/core/registry.ts +62 -0
- package/src/core/server.ts +211 -0
- package/src/core/session.ts +54 -0
- package/src/core/types.ts +100 -0
- package/src/index.ts +22 -4
- package/src/oauth/device-flow.ts +111 -0
- package/src/oauth/flow.ts +94 -0
- package/src/oauth/index.ts +6 -0
- package/src/oauth/lifecycle.ts +77 -0
- package/src/oauth/providers.ts +21 -0
- package/src/oauth/storage-file.ts +77 -0
- package/src/oauth/storage.ts +6 -0
- package/src/oauth/types.ts +50 -0
- package/src/plugins/copilot/auth.ts +39 -0
- package/src/plugins/copilot/index.ts +5 -0
- package/src/plugins/copilot/plugin.ts +31 -0
- package/src/plugins/copilot/session.ts +130 -0
- package/src/plugins/copilot/tools.ts +21 -0
- package/src/plugins/copilot/types.ts +43 -0
- package/src/plugins/cursor/index.ts +2 -0
- package/src/plugins/cursor/plugin.ts +37 -0
- package/src/plugins/cursor/session.ts +78 -0
- package/src/plugins/cursor/tools.ts +25 -0
- package/src/plugins/windsurf/auth.ts +23 -0
- package/src/plugins/windsurf/daemon.ts +24 -0
- package/src/plugins/windsurf/index.ts +5 -0
- package/src/plugins/windsurf/models.ts +44 -0
- package/src/plugins/windsurf/plugin.ts +34 -0
- package/src/plugins/windsurf/session.ts +8 -0
- package/src/plugins/windsurf/tools.ts +13 -0
- package/src/plugins/windsurf/types.ts +24 -0
- package/src/utils/config.ts +1 -1
- package/src/utils/opencode.ts +4 -3
- package/src/utils/platform.ts +3 -0
- package/test/configure.test.ts +19 -4
- package/test/daemon.test.ts +224 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Cursor } from '@cursor/sdk';
|
|
2
|
+
import { CursorBridgeSession } from './session.js';
|
|
3
|
+
export class CursorBridgePlugin {
|
|
4
|
+
name = 'cursor';
|
|
5
|
+
version = '2.0.0';
|
|
6
|
+
async authenticate(config) {
|
|
7
|
+
const apiKey = config.CURSOR_API_KEY;
|
|
8
|
+
if (!apiKey)
|
|
9
|
+
return false;
|
|
10
|
+
try {
|
|
11
|
+
await Cursor.me({ apiKey });
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async listModels(config) {
|
|
19
|
+
const apiKey = config.CURSOR_API_KEY;
|
|
20
|
+
if (!apiKey)
|
|
21
|
+
throw new Error('Missing CURSOR_API_KEY');
|
|
22
|
+
const models = await Cursor.models.list({ apiKey });
|
|
23
|
+
return models.map((m) => ({
|
|
24
|
+
id: m.id,
|
|
25
|
+
name: m.id,
|
|
26
|
+
capabilities: { streaming: true, tools: true },
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
async createSession(config, model) {
|
|
30
|
+
const apiKey = config.CURSOR_API_KEY;
|
|
31
|
+
if (!apiKey)
|
|
32
|
+
throw new Error('Missing CURSOR_API_KEY');
|
|
33
|
+
const cwd = config.CURSOR_OPENCODE_BRIDGE_CWD ?? process.cwd();
|
|
34
|
+
return new CursorBridgeSession(apiKey, model, cwd);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BridgeSession, Message, ToolDefinition, StreamChunk } from '../../core/index.js';
|
|
2
|
+
export declare class CursorBridgeSession implements BridgeSession {
|
|
3
|
+
private agent;
|
|
4
|
+
private apiKey;
|
|
5
|
+
private modelId;
|
|
6
|
+
private cwd;
|
|
7
|
+
constructor(apiKey: string, modelId: string, cwd?: string);
|
|
8
|
+
send(messages: Message[], tools?: ToolDefinition[]): AsyncIterable<StreamChunk>;
|
|
9
|
+
dispose(): Promise<void>;
|
|
10
|
+
private buildPrompt;
|
|
11
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Agent } from '@cursor/sdk';
|
|
2
|
+
import { translateTools } from './tools.js';
|
|
3
|
+
export class CursorBridgeSession {
|
|
4
|
+
agent = null;
|
|
5
|
+
apiKey;
|
|
6
|
+
modelId;
|
|
7
|
+
cwd;
|
|
8
|
+
constructor(apiKey, modelId, cwd = process.cwd()) {
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
this.modelId = modelId;
|
|
11
|
+
this.cwd = cwd;
|
|
12
|
+
}
|
|
13
|
+
async *send(messages, tools) {
|
|
14
|
+
const prompt = this.buildPrompt(messages);
|
|
15
|
+
const cursorTools = tools ? translateTools(tools) : undefined;
|
|
16
|
+
try {
|
|
17
|
+
this.agent = await Agent.create({
|
|
18
|
+
apiKey: this.apiKey,
|
|
19
|
+
model: { id: this.modelId },
|
|
20
|
+
local: { cwd: this.cwd, settingSources: [] },
|
|
21
|
+
});
|
|
22
|
+
const sendOptions = {
|
|
23
|
+
model: { id: this.modelId },
|
|
24
|
+
onDelta: ({ update }) => {
|
|
25
|
+
if (update.type === 'text-delta' && 'text' in update && update.text) {
|
|
26
|
+
// onDelta is synchronous callback, we buffer and yield in the loop
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const run = await this.agent.send(prompt, sendOptions);
|
|
31
|
+
const result = await run.wait();
|
|
32
|
+
if (result.status === 'error' || result.status === 'cancelled') {
|
|
33
|
+
yield {
|
|
34
|
+
type: 'error',
|
|
35
|
+
content: `Agent run ${result.status}: ${result.result ?? 'no details'}`,
|
|
36
|
+
finishReason: 'error',
|
|
37
|
+
};
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
yield { type: 'text', content: result.result ?? '', finishReason: 'stop' };
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44
|
+
yield { type: 'error', content: msg, finishReason: 'error' };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async dispose() {
|
|
48
|
+
if (this.agent) {
|
|
49
|
+
try {
|
|
50
|
+
await this.agent[Symbol.asyncDispose]();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Ignore dispose errors
|
|
54
|
+
}
|
|
55
|
+
this.agent = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
buildPrompt(messages) {
|
|
59
|
+
const blocks = [];
|
|
60
|
+
for (const m of messages) {
|
|
61
|
+
const text = typeof m.content === 'string' ? m.content : '';
|
|
62
|
+
if (!text)
|
|
63
|
+
continue;
|
|
64
|
+
const label = m.role === 'tool' ? `tool (${m.tool_call_id ?? m.name ?? 'result'})` : m.role;
|
|
65
|
+
blocks.push(`[${label}]\n${text}`);
|
|
66
|
+
}
|
|
67
|
+
return `\nFollow this conversation transcript and reply as the assistant.\n\n${blocks.join('\n\n---\n\n')}\n`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../../core/index.js';
|
|
2
|
+
export interface CursorTool {
|
|
3
|
+
type: 'function';
|
|
4
|
+
function: {
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
parameters: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function translateTools(tools: ToolDefinition[]): CursorTool[];
|
|
11
|
+
export declare function translateToolResult(toolCallId: string, result: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function translateTools(tools) {
|
|
2
|
+
return tools.map((tool) => ({
|
|
3
|
+
type: 'function',
|
|
4
|
+
function: {
|
|
5
|
+
name: tool.function.name,
|
|
6
|
+
description: tool.function.description,
|
|
7
|
+
parameters: tool.function.parameters,
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
export function translateToolResult(toolCallId, result) {
|
|
12
|
+
return `[tool result for ${toolCallId}]\n${result}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const WINDSURF_API_BASE = 'https://server.codeium.com';
|
|
2
|
+
export function getToken(config) {
|
|
3
|
+
return config.WINDSURF_TOKEN ?? config.WINDSURF_OAUTH_TOKEN ?? null;
|
|
4
|
+
}
|
|
5
|
+
export async function validateToken(token) {
|
|
6
|
+
try {
|
|
7
|
+
const response = await fetch(`${WINDSURF_API_BASE}/api/v1/validate_token`, {
|
|
8
|
+
method: 'POST',
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
Authorization: `Bearer ${token}`,
|
|
12
|
+
},
|
|
13
|
+
body: JSON.stringify({ token }),
|
|
14
|
+
});
|
|
15
|
+
return response.ok;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createDaemonManager } from '../../core/index.js';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const DEFAULT_KNOWN_PATHS = [
|
|
4
|
+
join('/Applications', 'Windsurf.app', 'Contents', 'Resources', 'language_server'),
|
|
5
|
+
join('/usr', 'local', 'bin', 'language_server'),
|
|
6
|
+
];
|
|
7
|
+
export function createWindsurfDaemon(options = {}) {
|
|
8
|
+
const knownPaths = options.knownPaths ?? DEFAULT_KNOWN_PATHS;
|
|
9
|
+
return createDaemonManager({
|
|
10
|
+
binaryName: 'language_server',
|
|
11
|
+
downloadUrl: `https://server.codeium.com/language_server/latest/{platform}/{arch}`,
|
|
12
|
+
checksum: 'PLACEHOLDER_SHA256',
|
|
13
|
+
knownPaths,
|
|
14
|
+
envVar: 'WINDSURF_LANGUAGE_SERVER_PATH',
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { WindsurfBridgePlugin } from './plugin.js';
|
|
2
|
+
export { WINDSURF_MODELS } from './models.js';
|
|
3
|
+
export { createWindsurfDaemon } from './daemon.js';
|
|
4
|
+
export { WindsurfBridgeSession } from './session.js';
|
|
5
|
+
export type { WindsurfModel, WindsurfConfig } from './types.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const WINDSURF_MODELS = [
|
|
2
|
+
{
|
|
3
|
+
id: 'claude-4.5-sonnet',
|
|
4
|
+
name: 'Claude 4.5 Sonnet (Windsurf)',
|
|
5
|
+
capabilities: { streaming: true, tools: true },
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
id: 'claude-4.5-opus',
|
|
9
|
+
name: 'Claude 4.5 Opus (Windsurf)',
|
|
10
|
+
capabilities: { streaming: true, tools: true },
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'gpt-5.2',
|
|
14
|
+
name: 'GPT-5.2 (Windsurf)',
|
|
15
|
+
capabilities: { streaming: true, tools: true },
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'gpt-5.2-codex',
|
|
19
|
+
name: 'GPT-5.2 Codex (Windsurf)',
|
|
20
|
+
capabilities: { streaming: true, tools: true },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'gpt-4o',
|
|
24
|
+
name: 'GPT-4o (Windsurf)',
|
|
25
|
+
capabilities: { streaming: true, tools: true },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'gemini-3.0-pro',
|
|
29
|
+
name: 'Gemini 3.0 Pro (Windsurf)',
|
|
30
|
+
capabilities: { streaming: true, tools: true },
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'gemini-3.0-flash',
|
|
34
|
+
name: 'Gemini 3.0 Flash (Windsurf)',
|
|
35
|
+
capabilities: { streaming: true, tools: true },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'swe-1.5',
|
|
39
|
+
name: 'SWE-1.5 (Windsurf)',
|
|
40
|
+
capabilities: { streaming: true, tools: true },
|
|
41
|
+
},
|
|
42
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BridgePlugin, BridgeSession, ModelInfo } from '../../core/index.js';
|
|
2
|
+
export declare class WindsurfBridgePlugin implements BridgePlugin {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
authenticate(config: Record<string, string>): Promise<boolean>;
|
|
6
|
+
listModels(config: Record<string, string>): Promise<ModelInfo[]>;
|
|
7
|
+
createSession(config: Record<string, string>, model: string): Promise<BridgeSession>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { WindsurfBridgeSession } from './session.js';
|
|
2
|
+
import { WINDSURF_MODELS } from './models.js';
|
|
3
|
+
import { getToken, validateToken } from './auth.js';
|
|
4
|
+
import { createWindsurfDaemon } from './daemon.js';
|
|
5
|
+
export class WindsurfBridgePlugin {
|
|
6
|
+
name = 'windsurf';
|
|
7
|
+
version = '2.0.0';
|
|
8
|
+
async authenticate(config) {
|
|
9
|
+
const token = getToken(config);
|
|
10
|
+
if (!token)
|
|
11
|
+
return false;
|
|
12
|
+
return validateToken(token);
|
|
13
|
+
}
|
|
14
|
+
async listModels(config) {
|
|
15
|
+
const token = getToken(config);
|
|
16
|
+
if (!token)
|
|
17
|
+
throw new Error('Missing WINDSURF_TOKEN');
|
|
18
|
+
return WINDSURF_MODELS.map((m) => ({
|
|
19
|
+
id: m.id,
|
|
20
|
+
name: m.name,
|
|
21
|
+
capabilities: m.capabilities,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
async createSession(config, model) {
|
|
25
|
+
const token = getToken(config);
|
|
26
|
+
if (!token)
|
|
27
|
+
throw new Error('Missing WINDSURF_TOKEN');
|
|
28
|
+
const daemon = createWindsurfDaemon();
|
|
29
|
+
return new WindsurfBridgeSession(daemon, token, model);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DaemonBridgeSession } from '../../core/index.js';
|
|
2
|
+
import type { DaemonManager } from '../../core/index.js';
|
|
3
|
+
export declare class WindsurfBridgeSession extends DaemonBridgeSession {
|
|
4
|
+
constructor(daemon: DaemonManager, token: string, model: string, cwd?: string);
|
|
5
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface WindsurfModel {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
capabilities: {
|
|
5
|
+
streaming: boolean;
|
|
6
|
+
tools: boolean;
|
|
7
|
+
vision?: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface WindsurfConfig {
|
|
11
|
+
WINDSURF_TOKEN?: string;
|
|
12
|
+
WINDSURF_OAUTH_TOKEN?: string;
|
|
13
|
+
WINDSURF_LANGUAGE_SERVER_PATH?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface WindsurfTool {
|
|
16
|
+
type: 'function';
|
|
17
|
+
function: {
|
|
18
|
+
name: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
parameters: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BridgeConfig } from '
|
|
1
|
+
import { BridgeConfig } from '../core/index.js';
|
|
2
2
|
export declare function readConfig(): BridgeConfig;
|
|
3
3
|
export declare function writeConfig(config: BridgeConfig): void;
|
|
4
4
|
export declare function setPluginConfig(pluginName: string, envVars: Record<string, string>): void;
|
package/dist/utils/config.js
CHANGED
package/dist/utils/opencode.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare function findOpencodeConfig(): string | null;
|
|
2
|
-
export declare function injectProvider(configPath: string, providerId: string,
|
|
2
|
+
export declare function injectProvider(configPath: string, providerId: string, models: Record<string, {
|
|
3
|
+
name: string;
|
|
4
|
+
}>, port: number, defaultModelId: string): void;
|
package/dist/utils/opencode.js
CHANGED
|
@@ -14,7 +14,7 @@ export function findOpencodeConfig() {
|
|
|
14
14
|
}
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
|
-
export function injectProvider(configPath, providerId,
|
|
17
|
+
export function injectProvider(configPath, providerId, models, port, defaultModelId) {
|
|
18
18
|
const raw = fs.readFileSync(configPath, 'utf8');
|
|
19
19
|
let config;
|
|
20
20
|
try {
|
|
@@ -32,8 +32,8 @@ export function injectProvider(configPath, providerId, modelId, port) {
|
|
|
32
32
|
apiKey: 'bridge-local',
|
|
33
33
|
baseURL: `http://127.0.0.1:${port}/v1`,
|
|
34
34
|
},
|
|
35
|
-
models
|
|
35
|
+
models,
|
|
36
36
|
};
|
|
37
|
-
config.model = `${providerId}/${
|
|
37
|
+
config.model = `${providerId}/${defaultModelId}`;
|
|
38
38
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
39
39
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPlatform(): NodeJS.Platform;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ide-bridge/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"llm-bridge": "./dist/index.js"
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
"start": "node dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@
|
|
18
|
-
"
|
|
19
|
-
"@ai-ide-bridge/cursor": "workspace:*",
|
|
20
|
-
"@ai-ide-bridge/windsurf": "workspace:*"
|
|
17
|
+
"@cursor/sdk": "^1.0.13",
|
|
18
|
+
"zod": "^3.25.76"
|
|
21
19
|
},
|
|
22
20
|
"devDependencies": {
|
|
23
21
|
"@types/node": "^22.15.0",
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { findOpencodeConfig, injectProvider } from '../utils/opencode.js';
|
|
2
2
|
import { readConfig } from '../utils/config.js';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import { CursorBridgePlugin } from '../plugins/cursor/index.js';
|
|
5
|
+
import { CopilotBridgePlugin } from '../plugins/copilot/index.js';
|
|
6
|
+
import { WindsurfBridgePlugin } from '../plugins/windsurf/index.js';
|
|
7
|
+
|
|
8
|
+
const PROVIDER_PLUGINS: Record<
|
|
9
|
+
string,
|
|
10
|
+
new () => {
|
|
11
|
+
name: string;
|
|
12
|
+
listModels(config: Record<string, string>): Promise<{ id: string; name: string }[]>;
|
|
13
|
+
}
|
|
14
|
+
> = {
|
|
15
|
+
cursor: CursorBridgePlugin,
|
|
16
|
+
copilot: CopilotBridgePlugin,
|
|
17
|
+
windsurf: WindsurfBridgePlugin,
|
|
18
|
+
};
|
|
3
19
|
|
|
4
20
|
export async function configureOpencodeCommand(): Promise<void> {
|
|
5
21
|
const configPath = findOpencodeConfig();
|
|
@@ -9,17 +25,96 @@ export async function configureOpencodeCommand(): Promise<void> {
|
|
|
9
25
|
}
|
|
10
26
|
|
|
11
27
|
const bridgeConfig = readConfig();
|
|
28
|
+
const configuredPlugins = Object.keys(bridgeConfig.plugins || {});
|
|
29
|
+
|
|
30
|
+
if (configuredPlugins.length === 0) {
|
|
31
|
+
console.error('No plugins configured. Run `llm-bridge init` first.');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const allSelectedModels: Record<string, { name: string }> = {};
|
|
36
|
+
let firstSelectedModelId: string | null = null;
|
|
37
|
+
|
|
38
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
39
|
+
const ask = (q: string) => new Promise<string>((resolve) => rl.question(q, resolve));
|
|
40
|
+
|
|
41
|
+
for (const pluginName of configuredPlugins) {
|
|
42
|
+
const PluginClass = PROVIDER_PLUGINS[pluginName];
|
|
43
|
+
if (!PluginClass) {
|
|
44
|
+
console.warn(`Skipping unknown provider: ${pluginName}`);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const plugin = new PluginClass();
|
|
49
|
+
const pluginConfig = bridgeConfig.plugins[pluginName];
|
|
50
|
+
|
|
51
|
+
let models: { id: string; name: string }[];
|
|
52
|
+
try {
|
|
53
|
+
models = await plugin.listModels(pluginConfig);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.warn(
|
|
56
|
+
`Failed to list models for ${pluginName}: ${e instanceof Error ? e.message : String(e)}`,
|
|
57
|
+
);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (models.length === 0) {
|
|
62
|
+
console.warn(`No models available for ${pluginName}.`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`\nModels available for ${pluginName}:`);
|
|
67
|
+
models.forEach((m, i) => {
|
|
68
|
+
console.log(` ${i + 1}) ${pluginName}/${m.id}`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const input = await ask(`Select models (comma-separated numbers, 'all', or 'skip'): `);
|
|
72
|
+
|
|
73
|
+
let selectedIndices: number[] = [];
|
|
74
|
+
|
|
75
|
+
if (input.toLowerCase() === 'all') {
|
|
76
|
+
selectedIndices = models.map((_, i) => i);
|
|
77
|
+
} else if (input.toLowerCase() === 'skip') {
|
|
78
|
+
continue;
|
|
79
|
+
} else {
|
|
80
|
+
selectedIndices = input
|
|
81
|
+
.split(',')
|
|
82
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
83
|
+
.filter((i) => i >= 0 && i < models.length);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (selectedIndices.length === 0) {
|
|
87
|
+
console.log(`No models selected for ${pluginName}. Skipping.`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const idx of selectedIndices) {
|
|
92
|
+
const model = models[idx];
|
|
93
|
+
const qualifiedId = `${pluginName}/${model.id}`;
|
|
94
|
+
allSelectedModels[qualifiedId] = { name: qualifiedId };
|
|
95
|
+
if (!firstSelectedModelId) {
|
|
96
|
+
firstSelectedModelId = qualifiedId;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
rl.close();
|
|
102
|
+
|
|
103
|
+
if (Object.keys(allSelectedModels).length === 0) {
|
|
104
|
+
console.error('No models selected. Run `llm-bridge configure` again to try again.');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
12
108
|
const providerId = 'llm-bridge';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
console.log(
|
|
23
|
-
console.log(`Provider: ${providerId},
|
|
24
|
-
console.log(`Plugin: ${plugin}`);
|
|
109
|
+
injectProvider(
|
|
110
|
+
configPath,
|
|
111
|
+
providerId,
|
|
112
|
+
allSelectedModels,
|
|
113
|
+
bridgeConfig.port,
|
|
114
|
+
firstSelectedModelId!,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const modelCount = Object.keys(allSelectedModels).length;
|
|
118
|
+
console.log(`\nInjected provider into ${configPath}`);
|
|
119
|
+
console.log(`Provider: ${providerId}, Models: ${modelCount}, Default: ${firstSelectedModelId}`);
|
|
25
120
|
}
|