@ruifung/codemode-bridge 1.0.3-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.
Files changed (39) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +378 -0
  3. package/dist/cli/commands.d.ts +70 -0
  4. package/dist/cli/commands.js +436 -0
  5. package/dist/cli/config-manager.d.ts +53 -0
  6. package/dist/cli/config-manager.js +142 -0
  7. package/dist/cli/index.d.ts +19 -0
  8. package/dist/cli/index.js +165 -0
  9. package/dist/executor/container-executor.d.ts +81 -0
  10. package/dist/executor/container-executor.js +351 -0
  11. package/dist/executor/executor-test-suite.d.ts +22 -0
  12. package/dist/executor/executor-test-suite.js +395 -0
  13. package/dist/executor/isolated-vm-executor.d.ts +78 -0
  14. package/dist/executor/isolated-vm-executor.js +368 -0
  15. package/dist/executor/vm2-executor.d.ts +21 -0
  16. package/dist/executor/vm2-executor.js +109 -0
  17. package/dist/executor/wrap-code.d.ts +52 -0
  18. package/dist/executor/wrap-code.js +80 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +6 -0
  21. package/dist/mcp/config.d.ts +44 -0
  22. package/dist/mcp/config.js +102 -0
  23. package/dist/mcp/e2e-bridge-test-suite.d.ts +28 -0
  24. package/dist/mcp/e2e-bridge-test-suite.js +429 -0
  25. package/dist/mcp/executor.d.ts +31 -0
  26. package/dist/mcp/executor.js +121 -0
  27. package/dist/mcp/mcp-adapter.d.ts +12 -0
  28. package/dist/mcp/mcp-adapter.js +49 -0
  29. package/dist/mcp/mcp-client.d.ts +85 -0
  30. package/dist/mcp/mcp-client.js +441 -0
  31. package/dist/mcp/oauth-handler.d.ts +33 -0
  32. package/dist/mcp/oauth-handler.js +138 -0
  33. package/dist/mcp/server.d.ts +25 -0
  34. package/dist/mcp/server.js +322 -0
  35. package/dist/mcp/token-persistence.d.ts +57 -0
  36. package/dist/mcp/token-persistence.js +131 -0
  37. package/dist/utils/logger.d.ts +44 -0
  38. package/dist/utils/logger.js +123 -0
  39. package/package.json +56 -0
@@ -0,0 +1,436 @@
1
+ /**
2
+ * CLI Commands for Code Mode Bridge
3
+ *
4
+ * Implements subcommands for managing and running the bridge
5
+ */
6
+ import chalk from "chalk";
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import { loadConfig, saveConfig, addServer, removeServer, updateServer, getServer, listServers, validateServer, getConfigFilePath, } from "./config-manager.js";
10
+ import { startCodeModeBridgeServer } from "../mcp/server.js";
11
+ import { getServerConfig } from "../mcp/config.js";
12
+ import { initializeLogger, logInfo, logError, flushStderrBuffer } from "../utils/logger.js";
13
+ import { tokenPersistence } from "../mcp/token-persistence.js";
14
+ import { MCPClient } from "../mcp/mcp-client.js";
15
+ /**
16
+ * Run the bridge server
17
+ */
18
+ export async function runServer(configPath, servers, debug) {
19
+ try {
20
+ // Initialize logger with debug mode if requested
21
+ initializeLogger(debug);
22
+ console.error(chalk.cyan("\n🚀 Code Mode Bridge"));
23
+ console.error(chalk.cyan("====================\n"));
24
+ // Load the bridge configuration
25
+ const bridgeConfig = loadConfig(configPath);
26
+ logInfo(`Loaded config from: ${getConfigFilePath(configPath)}`, { component: 'CLI' });
27
+ logInfo(`Found ${Object.keys(bridgeConfig.servers).length} configured servers`, { component: 'CLI' });
28
+ // Determine which servers to connect to
29
+ let serverNames = [];
30
+ if (servers && servers.length > 0) {
31
+ serverNames = servers;
32
+ logInfo(`Loading servers: ${serverNames.join(", ")}`, { component: 'CLI' });
33
+ }
34
+ else if (Object.keys(bridgeConfig.servers).length > 0) {
35
+ serverNames = Object.keys(bridgeConfig.servers);
36
+ logInfo(`No servers specified, loading all configured servers: ${serverNames.join(", ")}`, { component: 'CLI' });
37
+ }
38
+ else {
39
+ console.error(chalk.yellow("ℹ") + " No servers configured\n");
40
+ }
41
+ // Load server configurations
42
+ const serverConfigs = [];
43
+ for (const serverName of serverNames) {
44
+ try {
45
+ const serverConfig = getServerConfig(bridgeConfig, serverName);
46
+ serverConfigs.push(serverConfig);
47
+ logInfo(`Loaded ${serverName}`, { component: 'CLI' });
48
+ }
49
+ catch (err) {
50
+ logError(`Failed to load ${serverName}`, err instanceof Error ? err : { error: String(err) });
51
+ process.exit(1);
52
+ }
53
+ }
54
+ logInfo(`Starting bridge with ${serverConfigs.length} server(s)`, { component: 'CLI' });
55
+ // Start the MCP bridge server
56
+ await startCodeModeBridgeServer(serverConfigs);
57
+ // Flush buffered stderr output from stdio tools now that Bridge is fully running
58
+ flushStderrBuffer();
59
+ logInfo("Bridge is running!", { component: 'CLI' });
60
+ }
61
+ catch (error) {
62
+ logError("Error", error instanceof Error ? error : { error: String(error) });
63
+ process.exit(1);
64
+ }
65
+ }
66
+ /**
67
+ * List all configured servers
68
+ */
69
+ export function listServersCommand(configPath) {
70
+ try {
71
+ const config = loadConfig(configPath);
72
+ const servers = listServers(config);
73
+ if (servers.length === 0) {
74
+ console.log(chalk.yellow("No servers configured.\n"));
75
+ console.log(`To add a server, use:\n`);
76
+ console.log(` ${chalk.cyan("codemode-bridge config add <name> --type stdio --command <command>")}`);
77
+ return;
78
+ }
79
+ console.log(chalk.cyan("\nConfigured Servers:") + "\n");
80
+ for (const { name, entry } of servers) {
81
+ console.log(chalk.bold(name));
82
+ if (entry.type === "stdio") {
83
+ console.log(` Type: ${chalk.blue(entry.type)}`);
84
+ console.log(` Command: ${entry.command}`);
85
+ if (entry.args && entry.args.length > 0) {
86
+ console.log(` Args: ${entry.args.join(" ")}`);
87
+ }
88
+ }
89
+ else if (entry.type === "http") {
90
+ console.log(` Type: ${chalk.blue(entry.type)}`);
91
+ console.log(` URL: ${entry.url}`);
92
+ }
93
+ if (entry.env && Object.keys(entry.env).length > 0) {
94
+ console.log(` Env: ${JSON.stringify(entry.env)}`);
95
+ }
96
+ console.log();
97
+ }
98
+ console.log(`Config file: ${chalk.gray(getConfigFilePath(configPath))}\n`);
99
+ }
100
+ catch (error) {
101
+ console.error(chalk.red("✗") +
102
+ " Error: " +
103
+ (error instanceof Error ? error.message : String(error)));
104
+ process.exit(1);
105
+ }
106
+ }
107
+ /**
108
+ * Show a specific server configuration
109
+ */
110
+ export function showServerCommand(name, configPath) {
111
+ try {
112
+ const config = loadConfig(configPath);
113
+ const entry = getServer(config, name);
114
+ if (!entry) {
115
+ console.error(chalk.red("✗") + ` Server "${name}" not found`);
116
+ process.exit(1);
117
+ }
118
+ console.log(chalk.cyan(`\nServer: ${name}\n`));
119
+ console.log(JSON.stringify(entry, null, 2));
120
+ console.log();
121
+ }
122
+ catch (error) {
123
+ console.error(chalk.red("✗") +
124
+ " Error: " +
125
+ (error instanceof Error ? error.message : String(error)));
126
+ process.exit(1);
127
+ }
128
+ }
129
+ /**
130
+ * Add a new server configuration
131
+ */
132
+ export function addServerCommand(name, options, configPath) {
133
+ try {
134
+ const entry = {
135
+ type: options.type,
136
+ };
137
+ if (options.type === "stdio") {
138
+ if (!options.command) {
139
+ console.error(chalk.red("✗") + ' Missing --command for stdio server');
140
+ process.exit(1);
141
+ }
142
+ entry.command = options.command;
143
+ if (options.args) {
144
+ entry.args = options.args;
145
+ }
146
+ }
147
+ else if (options.type === "http") {
148
+ if (!options.url) {
149
+ console.error(chalk.red("✗") + ' Missing --url for http server');
150
+ process.exit(1);
151
+ }
152
+ entry.url = options.url;
153
+ }
154
+ else {
155
+ console.error(chalk.red("✗") + ` Invalid type: "${options.type}"`);
156
+ process.exit(1);
157
+ }
158
+ if (options.env) {
159
+ entry.env = options.env;
160
+ }
161
+ // Validate
162
+ const validation = validateServer(entry);
163
+ if (!validation.valid) {
164
+ console.error(chalk.red("✗") + " Validation failed:");
165
+ for (const error of validation.errors) {
166
+ console.error(" " + error);
167
+ }
168
+ process.exit(1);
169
+ }
170
+ // Load, add, and save
171
+ const config = loadConfig(configPath);
172
+ addServer(config, name, entry);
173
+ saveConfig(config, configPath);
174
+ console.log(chalk.green("✓") + ` Added server "${name}"\n`);
175
+ showServerCommand(name, configPath);
176
+ }
177
+ catch (error) {
178
+ console.error(chalk.red("✗") +
179
+ " Error: " +
180
+ (error instanceof Error ? error.message : String(error)));
181
+ process.exit(1);
182
+ }
183
+ }
184
+ /**
185
+ * Remove a server configuration
186
+ */
187
+ export function removeServerCommand(name, configPath) {
188
+ try {
189
+ const config = loadConfig(configPath);
190
+ removeServer(config, name);
191
+ saveConfig(config, configPath);
192
+ console.log(chalk.green("✓") + ` Removed server "${name}"\n`);
193
+ }
194
+ catch (error) {
195
+ console.error(chalk.red("✗") +
196
+ " Error: " +
197
+ (error instanceof Error ? error.message : String(error)));
198
+ process.exit(1);
199
+ }
200
+ }
201
+ /**
202
+ * Edit a server configuration
203
+ */
204
+ export function editServerCommand(name, options, configPath) {
205
+ try {
206
+ const config = loadConfig(configPath);
207
+ const entry = getServer(config, name);
208
+ if (!entry) {
209
+ console.error(chalk.red("✗") + ` Server "${name}" not found`);
210
+ process.exit(1);
211
+ }
212
+ // Update fields
213
+ if (options.type)
214
+ entry.type = options.type;
215
+ if (options.command)
216
+ entry.command = options.command;
217
+ if (options.args)
218
+ entry.args = options.args;
219
+ if (options.url)
220
+ entry.url = options.url;
221
+ if (options.env)
222
+ entry.env = options.env;
223
+ // Validate
224
+ const validation = validateServer(entry);
225
+ if (!validation.valid) {
226
+ console.error(chalk.red("✗") + " Validation failed:");
227
+ for (const error of validation.errors) {
228
+ console.error(" " + error);
229
+ }
230
+ process.exit(1);
231
+ }
232
+ // Save
233
+ updateServer(config, name, entry);
234
+ saveConfig(config, configPath);
235
+ console.log(chalk.green("✓") + ` Updated server "${name}"\n`);
236
+ showServerCommand(name, configPath);
237
+ }
238
+ catch (error) {
239
+ console.error(chalk.red("✗") +
240
+ " Error: " +
241
+ (error instanceof Error ? error.message : String(error)));
242
+ process.exit(1);
243
+ }
244
+ }
245
+ /**
246
+ * Show config file information
247
+ */
248
+ export function configInfoCommand(configPath) {
249
+ const filePath = getConfigFilePath(configPath);
250
+ const dir = path.dirname(filePath);
251
+ const exists = fs.existsSync(filePath);
252
+ console.log(chalk.cyan("\nConfiguration Information\n"));
253
+ console.log(`Config file: ${chalk.bold(filePath)}`);
254
+ console.log(`Directory: ${chalk.bold(dir)}`);
255
+ console.log(`Status: ${exists ? chalk.green("exists") : chalk.yellow("will be created")}`);
256
+ if (configPath) {
257
+ console.log(`\n${chalk.yellow("ℹ")} Using custom config path`);
258
+ }
259
+ else {
260
+ console.log(`\n${chalk.yellow("ℹ")} Using default config path`);
261
+ }
262
+ console.log();
263
+ }
264
+ /**
265
+ * Login command for OAuth servers
266
+ *
267
+ * Usage: codemode-bridge auth login <server-name>
268
+ *
269
+ * Starts the bridge connected only to the specified OAuth server.
270
+ * This allows the OAuth flow to complete naturally during connection.
271
+ */
272
+ export async function authLoginCommand(serverName, configPath) {
273
+ try {
274
+ // Initialize logger
275
+ initializeLogger(false);
276
+ // Load config and validate server
277
+ const config = loadConfig(configPath);
278
+ const serverEntry = getServer(config, serverName);
279
+ if (!serverEntry) {
280
+ console.error(chalk.red("✗") + ` Server "${serverName}" not found`);
281
+ process.exit(1);
282
+ }
283
+ // Check if server has OAuth configured
284
+ if (!serverEntry.oauth) {
285
+ console.error(chalk.red("✗") + ` Server "${serverName}" does not have OAuth configured`);
286
+ process.exit(1);
287
+ }
288
+ // Only HTTP servers support OAuth
289
+ if (serverEntry.type !== "http") {
290
+ console.error(chalk.red("✗") + ` OAuth is only supported for HTTP servers (${serverName} is ${serverEntry.type})`);
291
+ process.exit(1);
292
+ }
293
+ if (!serverEntry.url) {
294
+ console.error(chalk.red("✗") + ` Server "${serverName}" is missing URL configuration`);
295
+ process.exit(1);
296
+ }
297
+ console.log(chalk.cyan(`\nAuthenticating with ${chalk.bold(serverName)}...\n`));
298
+ // Create an MCP client for OAuth authentication
299
+ // This will handle the OAuth flow without fully connecting the client
300
+ const mcpConfig = {
301
+ name: serverName,
302
+ type: serverEntry.type,
303
+ url: serverEntry.url,
304
+ oauth: serverEntry.oauth,
305
+ };
306
+ const client = new MCPClient(mcpConfig);
307
+ // Authenticate via OAuth - this triggers browser authorization and token exchange
308
+ logInfo(`Starting OAuth authentication for ${serverName}`, { component: 'CLI' });
309
+ await client.authenticateOAuth();
310
+ // OAuth flow is complete and tokens are saved
311
+ console.log(chalk.green('\n✓ Authentication successful. Tokens have been saved.\n'));
312
+ process.exit(0);
313
+ }
314
+ catch (error) {
315
+ logError(`Failed to complete OAuth authentication for ${serverName}`, error instanceof Error ? error : { error: String(error) });
316
+ console.error(chalk.red("\n✗ Authentication failed"));
317
+ if (error instanceof Error) {
318
+ console.error(chalk.red(error.message));
319
+ }
320
+ process.exit(1);
321
+ }
322
+ }
323
+ /**
324
+ * Logout command for OAuth servers
325
+ *
326
+ * Usage: codemode-bridge auth logout <server-name>
327
+ *
328
+ * Clears all stored authentication data (tokens and client info) for the server.
329
+ */
330
+ export function authLogoutCommand(serverName, configPath) {
331
+ try {
332
+ // Load config and get server
333
+ const config = loadConfig(configPath);
334
+ const serverEntry = getServer(config, serverName);
335
+ if (!serverEntry) {
336
+ console.error(chalk.red("✗") + ` Server "${serverName}" not found`);
337
+ process.exit(1);
338
+ }
339
+ // Check if server has OAuth configured
340
+ if (!serverEntry.oauth) {
341
+ console.error(chalk.red("✗") + ` Server "${serverName}" does not have OAuth configured`);
342
+ process.exit(1);
343
+ }
344
+ // Get the server URL for token persistence
345
+ let serverUrl;
346
+ if (serverEntry.type === "http" && serverEntry.url) {
347
+ serverUrl = serverEntry.url;
348
+ }
349
+ else {
350
+ // For stdio servers, use server name as key
351
+ serverUrl = serverName;
352
+ }
353
+ // Clear all auth data including client information
354
+ tokenPersistence.clearAll(serverUrl);
355
+ logInfo(`Cleared all authentication data for ${serverName}`, { component: 'CLI' });
356
+ console.log(chalk.green(`✓ Logged out from ${serverName}`));
357
+ console.log(chalk.cyan(`\nAll tokens and client information have been cleared.`));
358
+ }
359
+ catch (error) {
360
+ logError(`Failed to logout from ${serverName}`, error instanceof Error ? error : { error: String(error) });
361
+ process.exit(1);
362
+ }
363
+ }
364
+ /**
365
+ * List command for OAuth servers
366
+ *
367
+ * Usage: codemode-bridge auth list
368
+ *
369
+ * Shows all OAuth-enabled servers and their authentication status.
370
+ */
371
+ export function authListCommand(configPath) {
372
+ try {
373
+ // Load config
374
+ const config = loadConfig(configPath);
375
+ // Find all OAuth-enabled servers
376
+ const oauthServers = [];
377
+ for (const [name, entry] of Object.entries(config.servers || {})) {
378
+ if (entry.oauth) {
379
+ // Determine server URL for token lookup
380
+ let serverUrl;
381
+ if (entry.type === "http" && entry.url) {
382
+ serverUrl = entry.url;
383
+ }
384
+ else {
385
+ serverUrl = name;
386
+ }
387
+ // Get token status
388
+ const tokenStatus = tokenPersistence.getTokenStatus(serverUrl);
389
+ let status;
390
+ if (tokenStatus.exists && !tokenStatus.isExpired) {
391
+ status = 'authenticated';
392
+ }
393
+ else if (tokenStatus.exists && tokenStatus.isExpired) {
394
+ status = 'expired';
395
+ }
396
+ else {
397
+ status = 'needs login';
398
+ }
399
+ oauthServers.push({
400
+ name,
401
+ entry,
402
+ serverUrl,
403
+ status,
404
+ });
405
+ }
406
+ }
407
+ // Display results
408
+ if (oauthServers.length === 0) {
409
+ console.log(chalk.yellow("No OAuth-enabled servers configured.\n"));
410
+ return;
411
+ }
412
+ console.log(chalk.cyan("\nOAuth Servers:\n"));
413
+ for (const server of oauthServers) {
414
+ const name = chalk.bold(server.name);
415
+ let statusColor;
416
+ switch (server.status) {
417
+ case 'authenticated':
418
+ statusColor = chalk.green;
419
+ break;
420
+ case 'expired':
421
+ statusColor = chalk.yellow;
422
+ break;
423
+ default:
424
+ statusColor = chalk.gray;
425
+ }
426
+ console.log(`${name} (${server.entry.type})${chalk.dim(` - ${statusColor(server.status)}`)}`);
427
+ }
428
+ console.log();
429
+ }
430
+ catch (error) {
431
+ console.error(chalk.red("✗") +
432
+ " Error: " +
433
+ (error instanceof Error ? error.message : String(error)));
434
+ process.exit(1);
435
+ }
436
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Configuration Manager for Code Mode Bridge
3
+ *
4
+ * Manages the bridge configuration stored in .config/codemode-bridge/mcp.json
5
+ * Provides utilities to load, save, and manipulate the configuration
6
+ */
7
+ import type { MCPJsonConfig, MCPServerConfigEntry } from "../mcp/config.js";
8
+ /**
9
+ * Get the default config directory for the current platform
10
+ */
11
+ export declare function getDefaultConfigDir(): string;
12
+ /**
13
+ * Get the full path to the mcp.json config file
14
+ */
15
+ export declare function getConfigFilePath(overridePath?: string): string;
16
+ /**
17
+ * Load the configuration file, creating it if it doesn't exist
18
+ */
19
+ export declare function loadConfig(configPath?: string): MCPJsonConfig;
20
+ /**
21
+ * Save the configuration file
22
+ */
23
+ export declare function saveConfig(config: MCPJsonConfig, configPath?: string): void;
24
+ /**
25
+ * Add a server to the configuration
26
+ */
27
+ export declare function addServer(config: MCPJsonConfig, name: string, entry: MCPServerConfigEntry): MCPJsonConfig;
28
+ /**
29
+ * Remove a server from the configuration
30
+ */
31
+ export declare function removeServer(config: MCPJsonConfig, name: string): MCPJsonConfig;
32
+ /**
33
+ * Update a server in the configuration
34
+ */
35
+ export declare function updateServer(config: MCPJsonConfig, name: string, entry: MCPServerConfigEntry): MCPJsonConfig;
36
+ /**
37
+ * Get a server from the configuration
38
+ */
39
+ export declare function getServer(config: MCPJsonConfig, name: string): MCPServerConfigEntry | null;
40
+ /**
41
+ * List all servers in the configuration
42
+ */
43
+ export declare function listServers(config: MCPJsonConfig): {
44
+ name: string;
45
+ entry: MCPServerConfigEntry;
46
+ }[];
47
+ /**
48
+ * Validate a server configuration entry
49
+ */
50
+ export declare function validateServer(entry: MCPServerConfigEntry): {
51
+ valid: boolean;
52
+ errors: string[];
53
+ };
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Configuration Manager for Code Mode Bridge
3
+ *
4
+ * Manages the bridge configuration stored in .config/codemode-bridge/mcp.json
5
+ * Provides utilities to load, save, and manipulate the configuration
6
+ */
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ /**
10
+ * Get the default config directory for the current platform
11
+ */
12
+ export function getDefaultConfigDir() {
13
+ const homeDir = process.env.HOME || process.env.USERPROFILE || process.cwd();
14
+ if (process.platform === "win32") {
15
+ return path.join(homeDir, ".config", "codemode-bridge");
16
+ }
17
+ else if (process.platform === "darwin") {
18
+ return path.join(homeDir, ".config", "codemode-bridge");
19
+ }
20
+ else {
21
+ // Linux
22
+ return path.join(homeDir, ".config", "codemode-bridge");
23
+ }
24
+ }
25
+ /**
26
+ * Get the full path to the mcp.json config file
27
+ */
28
+ export function getConfigFilePath(overridePath) {
29
+ if (overridePath) {
30
+ return path.resolve(overridePath);
31
+ }
32
+ return path.join(getDefaultConfigDir(), "mcp.json");
33
+ }
34
+ /**
35
+ * Load the configuration file, creating it if it doesn't exist
36
+ */
37
+ export function loadConfig(configPath) {
38
+ const filePath = getConfigFilePath(configPath);
39
+ if (!fs.existsSync(filePath)) {
40
+ // Create default config
41
+ const defaultConfig = {
42
+ servers: {},
43
+ };
44
+ saveConfig(defaultConfig, configPath);
45
+ return defaultConfig;
46
+ }
47
+ try {
48
+ const content = fs.readFileSync(filePath, "utf-8");
49
+ return JSON.parse(content);
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Failed to parse config file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
53
+ }
54
+ }
55
+ /**
56
+ * Save the configuration file
57
+ */
58
+ export function saveConfig(config, configPath) {
59
+ const filePath = getConfigFilePath(configPath);
60
+ const dir = path.dirname(filePath);
61
+ // Create directory if it doesn't exist
62
+ if (!fs.existsSync(dir)) {
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+ try {
66
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2), "utf-8");
67
+ }
68
+ catch (error) {
69
+ throw new Error(`Failed to save config file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
70
+ }
71
+ }
72
+ /**
73
+ * Add a server to the configuration
74
+ */
75
+ export function addServer(config, name, entry) {
76
+ if (config.servers[name]) {
77
+ throw new Error(`Server "${name}" already exists`);
78
+ }
79
+ config.servers[name] = entry;
80
+ return config;
81
+ }
82
+ /**
83
+ * Remove a server from the configuration
84
+ */
85
+ export function removeServer(config, name) {
86
+ if (!config.servers[name]) {
87
+ throw new Error(`Server "${name}" not found`);
88
+ }
89
+ delete config.servers[name];
90
+ return config;
91
+ }
92
+ /**
93
+ * Update a server in the configuration
94
+ */
95
+ export function updateServer(config, name, entry) {
96
+ if (!config.servers[name]) {
97
+ throw new Error(`Server "${name}" not found`);
98
+ }
99
+ config.servers[name] = entry;
100
+ return config;
101
+ }
102
+ /**
103
+ * Get a server from the configuration
104
+ */
105
+ export function getServer(config, name) {
106
+ return config.servers[name] || null;
107
+ }
108
+ /**
109
+ * List all servers in the configuration
110
+ */
111
+ export function listServers(config) {
112
+ return Object.entries(config.servers).map(([name, entry]) => ({
113
+ name,
114
+ entry,
115
+ }));
116
+ }
117
+ /**
118
+ * Validate a server configuration entry
119
+ */
120
+ export function validateServer(entry) {
121
+ const errors = [];
122
+ if (!entry.type) {
123
+ errors.push("Missing 'type' field (must be 'stdio' or 'http')");
124
+ }
125
+ else if (entry.type !== "stdio" && entry.type !== "http") {
126
+ errors.push(`Invalid type: "${entry.type}" (must be 'stdio' or 'http')`);
127
+ }
128
+ if (entry.type === "stdio") {
129
+ if (!entry.command) {
130
+ errors.push("Missing 'command' field for stdio server");
131
+ }
132
+ }
133
+ else if (entry.type === "http") {
134
+ if (!entry.url) {
135
+ errors.push("Missing 'url' field for http server");
136
+ }
137
+ }
138
+ return {
139
+ valid: errors.length === 0,
140
+ errors,
141
+ };
142
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Code Mode Bridge CLI
4
+ *
5
+ * Main entry point for the command-line interface
6
+ *
7
+ * Usage:
8
+ * codemode-bridge run [options] - Start the bridge server
9
+ * codemode-bridge config list [options] - List configured servers
10
+ * codemode-bridge config show <name> - Show a server configuration
11
+ * codemode-bridge config add <name> - Add a new server
12
+ * codemode-bridge config remove <name> - Remove a server
13
+ * codemode-bridge config edit <name> - Edit a server
14
+ * codemode-bridge config info - Show config file information
15
+ * codemode-bridge auth list [options] - List OAuth-enabled servers and status
16
+ * codemode-bridge auth login <name> - Prepare to login to an OAuth server
17
+ * codemode-bridge auth logout <name> - Logout from an OAuth server
18
+ */
19
+ export {};