@willjackson/claude-code-bridge 0.2.4 → 0.4.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/README.md +83 -185
- package/dist/{chunk-5HWFDZKH.js → chunk-MHUQYPTB.js} +409 -359
- package/dist/chunk-MHUQYPTB.js.map +1 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +245 -159
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +108 -290
- package/dist/index.js +9 -81
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
- package/dist/chunk-5HWFDZKH.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Bridge,
|
|
4
|
+
BridgeMcpServer,
|
|
4
5
|
ConnectionState,
|
|
5
6
|
WebSocketTransport,
|
|
6
7
|
createLogger,
|
|
7
|
-
detectEnvironment,
|
|
8
|
-
discoverDdevProjects,
|
|
9
|
-
discoverDockerPeers,
|
|
10
|
-
discoverDocksalProjects,
|
|
11
|
-
discoverLandoProjects,
|
|
12
|
-
getDefaultConfig,
|
|
13
|
-
getHostGateway,
|
|
14
8
|
loadConfigSync
|
|
15
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MHUQYPTB.js";
|
|
16
10
|
|
|
17
11
|
// src/cli/index.ts
|
|
18
12
|
import { Command as Command7 } from "commander";
|
|
@@ -22,6 +16,7 @@ import { Command } from "commander";
|
|
|
22
16
|
import * as fs from "fs";
|
|
23
17
|
import * as path from "path";
|
|
24
18
|
import * as os from "os";
|
|
19
|
+
import { spawn } from "child_process";
|
|
25
20
|
import { minimatch } from "minimatch";
|
|
26
21
|
|
|
27
22
|
// src/cli/utils.ts
|
|
@@ -303,6 +298,59 @@ function registerHandlers(bridge, config) {
|
|
|
303
298
|
};
|
|
304
299
|
}
|
|
305
300
|
}
|
|
301
|
+
if (taskData?.action === "list_directory") {
|
|
302
|
+
const dirPath = taskData.path;
|
|
303
|
+
if (!dirPath) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
data: { error: "list_directory requires path" }
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(cwd, dirPath);
|
|
310
|
+
const resolvedPath = path.resolve(fullPath);
|
|
311
|
+
const resolvedCwd = path.resolve(cwd);
|
|
312
|
+
if (!resolvedPath.startsWith(resolvedCwd)) {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
data: { error: "Cannot list directories outside project directory" }
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
320
|
+
return {
|
|
321
|
+
success: false,
|
|
322
|
+
data: { error: `Directory not found: ${dirPath}` }
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const stats = fs.statSync(resolvedPath);
|
|
326
|
+
if (!stats.isDirectory()) {
|
|
327
|
+
return {
|
|
328
|
+
success: false,
|
|
329
|
+
data: { error: `Not a directory: ${dirPath}` }
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const entries = fs.readdirSync(resolvedPath, { withFileTypes: true });
|
|
333
|
+
const listing = entries.map((entry) => ({
|
|
334
|
+
name: entry.name,
|
|
335
|
+
type: entry.isDirectory() ? "directory" : "file"
|
|
336
|
+
}));
|
|
337
|
+
logger.info({ path: dirPath, count: listing.length }, "Directory listed successfully");
|
|
338
|
+
return {
|
|
339
|
+
success: true,
|
|
340
|
+
data: {
|
|
341
|
+
action: "list_directory",
|
|
342
|
+
path: dirPath,
|
|
343
|
+
entries: listing
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
} catch (err) {
|
|
347
|
+
logger.error({ error: err.message, path: dirPath }, "Failed to list directory");
|
|
348
|
+
return {
|
|
349
|
+
success: false,
|
|
350
|
+
data: { error: `Failed to list directory: ${err.message}` }
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
306
354
|
const projectInfo = {
|
|
307
355
|
cwd,
|
|
308
356
|
platform: process.platform,
|
|
@@ -366,18 +414,6 @@ function registerHandlers(bridge, config) {
|
|
|
366
414
|
}
|
|
367
415
|
function buildBridgeConfig(options, globalOptions) {
|
|
368
416
|
const fileConfig = loadConfigSync(globalOptions.config);
|
|
369
|
-
let envConfig = {};
|
|
370
|
-
if (options.auto) {
|
|
371
|
-
const env = detectEnvironment();
|
|
372
|
-
const defaultEnvConfig = getDefaultConfig(env);
|
|
373
|
-
logger.info({ environment: env.type, isContainer: env.isContainer }, "Auto-detected environment");
|
|
374
|
-
envConfig = {
|
|
375
|
-
mode: defaultEnvConfig.mode,
|
|
376
|
-
instanceName: defaultEnvConfig.instanceName,
|
|
377
|
-
listen: defaultEnvConfig.listen,
|
|
378
|
-
connect: defaultEnvConfig.connect
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
417
|
const cliConfig = {};
|
|
382
418
|
if (options.port) {
|
|
383
419
|
cliConfig.listen = {
|
|
@@ -398,19 +434,19 @@ function buildBridgeConfig(options, globalOptions) {
|
|
|
398
434
|
};
|
|
399
435
|
}
|
|
400
436
|
let mode = "peer";
|
|
401
|
-
const hasListen = cliConfig.listen ||
|
|
402
|
-
const hasConnect = cliConfig.connect ||
|
|
437
|
+
const hasListen = cliConfig.listen || fileConfig.listen;
|
|
438
|
+
const hasConnect = cliConfig.connect || fileConfig.connect;
|
|
403
439
|
if (hasListen && !hasConnect) {
|
|
404
440
|
mode = "host";
|
|
405
441
|
} else if (hasConnect && !hasListen) {
|
|
406
442
|
mode = "client";
|
|
407
443
|
}
|
|
408
444
|
const finalConfig = {
|
|
409
|
-
mode: cliConfig.mode ??
|
|
410
|
-
instanceName: cliConfig.instanceName ??
|
|
445
|
+
mode: cliConfig.mode ?? fileConfig.mode ?? mode,
|
|
446
|
+
instanceName: cliConfig.instanceName ?? fileConfig.instanceName ?? `bridge-${process.pid}`,
|
|
411
447
|
listen: {
|
|
412
|
-
port: cliConfig.listen?.port ??
|
|
413
|
-
host: cliConfig.listen?.host ??
|
|
448
|
+
port: cliConfig.listen?.port ?? fileConfig.listen.port,
|
|
449
|
+
host: cliConfig.listen?.host ?? fileConfig.listen.host
|
|
414
450
|
},
|
|
415
451
|
taskTimeout: fileConfig.interaction.taskTimeout,
|
|
416
452
|
contextSharing: {
|
|
@@ -418,7 +454,7 @@ function buildBridgeConfig(options, globalOptions) {
|
|
|
418
454
|
syncInterval: fileConfig.contextSharing.syncInterval
|
|
419
455
|
}
|
|
420
456
|
};
|
|
421
|
-
const connectUrl = cliConfig.connect?.url ??
|
|
457
|
+
const connectUrl = cliConfig.connect?.url ?? fileConfig.connect?.url;
|
|
422
458
|
if (connectUrl) {
|
|
423
459
|
finalConfig.connect = {
|
|
424
460
|
url: connectUrl
|
|
@@ -427,10 +463,12 @@ function buildBridgeConfig(options, globalOptions) {
|
|
|
427
463
|
return finalConfig;
|
|
428
464
|
}
|
|
429
465
|
async function startBridge(options, globalOptions) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
466
|
+
if (!process.env.CLAUDE_BRIDGE_DAEMON_CHILD) {
|
|
467
|
+
const { running, pid } = isAlreadyRunning();
|
|
468
|
+
if (running) {
|
|
469
|
+
console.error(`Bridge is already running (PID: ${pid})`);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
434
472
|
}
|
|
435
473
|
const config = buildBridgeConfig(options, globalOptions);
|
|
436
474
|
console.log("Starting Claude Code Bridge...");
|
|
@@ -442,9 +480,24 @@ async function startBridge(options, globalOptions) {
|
|
|
442
480
|
if (config.connect) {
|
|
443
481
|
console.log(` Connecting to: ${config.connect.url}`);
|
|
444
482
|
}
|
|
445
|
-
if (options.daemon) {
|
|
446
|
-
|
|
447
|
-
|
|
483
|
+
if (options.daemon && !process.env.CLAUDE_BRIDGE_DAEMON_CHILD) {
|
|
484
|
+
const logFile = path.join(os.homedir(), ".claude-bridge", "bridge.log");
|
|
485
|
+
ensureBridgeDir();
|
|
486
|
+
const args = process.argv.slice(2).filter((arg) => arg !== "-d" && arg !== "--daemon");
|
|
487
|
+
const out = fs.openSync(logFile, "a");
|
|
488
|
+
const err = fs.openSync(logFile, "a");
|
|
489
|
+
const child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
490
|
+
detached: true,
|
|
491
|
+
stdio: ["ignore", out, err],
|
|
492
|
+
env: { ...process.env, CLAUDE_BRIDGE_DAEMON_CHILD: "1" }
|
|
493
|
+
});
|
|
494
|
+
child.unref();
|
|
495
|
+
writePidFile(child.pid);
|
|
496
|
+
console.log(` Running in background (PID: ${child.pid})`);
|
|
497
|
+
console.log(` Log file: ${logFile}`);
|
|
498
|
+
console.log(` Use 'claude-bridge status' to check status`);
|
|
499
|
+
console.log(` Use 'claude-bridge stop' to stop the bridge`);
|
|
500
|
+
process.exit(0);
|
|
448
501
|
}
|
|
449
502
|
const bridge = new Bridge(config);
|
|
450
503
|
setupGracefulShutdown({
|
|
@@ -453,7 +506,7 @@ async function startBridge(options, globalOptions) {
|
|
|
453
506
|
await bridge.stop();
|
|
454
507
|
logger.info("Bridge stopped");
|
|
455
508
|
},
|
|
456
|
-
afterCleanup:
|
|
509
|
+
afterCleanup: process.env.CLAUDE_BRIDGE_DAEMON_CHILD ? removePidFile : void 0,
|
|
457
510
|
verbose: true,
|
|
458
511
|
timeout: 1e4
|
|
459
512
|
});
|
|
@@ -470,7 +523,7 @@ async function startBridge(options, globalOptions) {
|
|
|
470
523
|
excludePatterns: fileConfig.contextSharing.excludePatterns
|
|
471
524
|
});
|
|
472
525
|
}
|
|
473
|
-
if (
|
|
526
|
+
if (process.env.CLAUDE_BRIDGE_DAEMON_CHILD) {
|
|
474
527
|
writePidFile(process.pid);
|
|
475
528
|
}
|
|
476
529
|
console.log("Bridge started successfully.");
|
|
@@ -480,13 +533,13 @@ async function startBridge(options, globalOptions) {
|
|
|
480
533
|
To connect from another bridge:`);
|
|
481
534
|
console.log(` claude-bridge connect ws://localhost:${config.listen.port}`);
|
|
482
535
|
}
|
|
483
|
-
if (!
|
|
536
|
+
if (!process.env.CLAUDE_BRIDGE_DAEMON_CHILD) {
|
|
484
537
|
console.log("\nPress Ctrl+C to stop.");
|
|
485
538
|
}
|
|
486
539
|
} catch (error) {
|
|
487
540
|
logger.error({ error: error.message }, "Failed to start bridge");
|
|
488
541
|
console.error(`Failed to start bridge: ${error.message}`);
|
|
489
|
-
if (
|
|
542
|
+
if (process.env.CLAUDE_BRIDGE_DAEMON_CHILD) {
|
|
490
543
|
removePidFile();
|
|
491
544
|
}
|
|
492
545
|
process.exit(1);
|
|
@@ -494,7 +547,7 @@ To connect from another bridge:`);
|
|
|
494
547
|
}
|
|
495
548
|
function createStartCommand() {
|
|
496
549
|
const command = new Command("start");
|
|
497
|
-
command.description("Start the bridge server").option("-p, --port <port>", "Port to listen on (default: 8765
|
|
550
|
+
command.description("Start the bridge server").option("-p, --port <port>", "Port to listen on (default: 8765)").option("-h, --host <host>", "Host to bind to (default: 0.0.0.0)").option("-c, --connect <url>", "URL to connect to on startup (e.g., ws://localhost:8765)").option("-d, --daemon", "Run in background").option("--with-handlers", "Enable file reading and task handling capabilities").action(async (options) => {
|
|
498
551
|
const globalOptions = command.parent?.opts();
|
|
499
552
|
await startBridge(options, globalOptions);
|
|
500
553
|
});
|
|
@@ -841,76 +894,12 @@ function createConnectCommand() {
|
|
|
841
894
|
return command;
|
|
842
895
|
}
|
|
843
896
|
|
|
844
|
-
// src/cli/commands/discover.ts
|
|
845
|
-
import { Command as Command5 } from "commander";
|
|
846
|
-
var logger5 = createLogger("cli:discover");
|
|
847
|
-
function printPeersTable(peers) {
|
|
848
|
-
if (peers.length === 0) {
|
|
849
|
-
console.log("No bridges found.");
|
|
850
|
-
console.log("");
|
|
851
|
-
console.log("Suggestions:");
|
|
852
|
-
console.log(" - Start a bridge: claude-bridge start");
|
|
853
|
-
console.log(" - Add bridge labels to Docker containers");
|
|
854
|
-
console.log(" - Install bridge addon in your Docksal/DDEV project");
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
const cols = {
|
|
858
|
-
name: { title: "Name", width: 25 },
|
|
859
|
-
source: { title: "Source", width: 12 },
|
|
860
|
-
url: { title: "URL", width: 35 },
|
|
861
|
-
status: { title: "Status", width: 12 }
|
|
862
|
-
};
|
|
863
|
-
console.log(
|
|
864
|
-
cols.name.title.padEnd(cols.name.width) + cols.source.title.padEnd(cols.source.width) + cols.url.title.padEnd(cols.url.width) + cols.status.title.padEnd(cols.status.width)
|
|
865
|
-
);
|
|
866
|
-
console.log(
|
|
867
|
-
"-".repeat(cols.name.width - 1) + " " + "-".repeat(cols.source.width - 1) + " " + "-".repeat(cols.url.width - 1) + " " + "-".repeat(cols.status.width - 1)
|
|
868
|
-
);
|
|
869
|
-
for (const peer of peers) {
|
|
870
|
-
const row = peer.name.slice(0, cols.name.width - 1).padEnd(cols.name.width) + peer.source.padEnd(cols.source.width) + peer.url.slice(0, cols.url.width - 1).padEnd(cols.url.width) + (peer.status || "unknown").padEnd(cols.status.width);
|
|
871
|
-
console.log(row);
|
|
872
|
-
}
|
|
873
|
-
console.log("");
|
|
874
|
-
console.log("To connect to a bridge:");
|
|
875
|
-
console.log(" claude-bridge connect <url>");
|
|
876
|
-
}
|
|
877
|
-
async function discoverBridges() {
|
|
878
|
-
console.log("Discovering bridges on local network...\n");
|
|
879
|
-
const allPeers = [];
|
|
880
|
-
const dockerPeers = discoverDockerPeers();
|
|
881
|
-
allPeers.push(...dockerPeers);
|
|
882
|
-
logger5.debug({ count: dockerPeers.length }, "Docker peers discovered");
|
|
883
|
-
const docksalPeers = discoverDocksalProjects();
|
|
884
|
-
allPeers.push(...docksalPeers);
|
|
885
|
-
logger5.debug({ count: docksalPeers.length }, "Docksal peers discovered");
|
|
886
|
-
const ddevPeers = discoverDdevProjects();
|
|
887
|
-
allPeers.push(...ddevPeers);
|
|
888
|
-
logger5.debug({ count: ddevPeers.length }, "DDEV peers discovered");
|
|
889
|
-
const landoPeers = discoverLandoProjects();
|
|
890
|
-
allPeers.push(...landoPeers);
|
|
891
|
-
logger5.debug({ count: landoPeers.length }, "Lando peers discovered");
|
|
892
|
-
printPeersTable(allPeers);
|
|
893
|
-
}
|
|
894
|
-
function createDiscoverCommand() {
|
|
895
|
-
const command = new Command5("discover");
|
|
896
|
-
command.description("Discover bridges on local network").action(async () => {
|
|
897
|
-
try {
|
|
898
|
-
await discoverBridges();
|
|
899
|
-
} catch (error) {
|
|
900
|
-
logger5.error({ error: error.message }, "Discover command failed");
|
|
901
|
-
console.error(`Error: ${error.message}`);
|
|
902
|
-
process.exit(1);
|
|
903
|
-
}
|
|
904
|
-
});
|
|
905
|
-
return command;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
897
|
// src/cli/commands/info.ts
|
|
909
|
-
import { Command as
|
|
898
|
+
import { Command as Command5 } from "commander";
|
|
910
899
|
import * as fs4 from "fs";
|
|
911
900
|
import * as path4 from "path";
|
|
912
901
|
import * as os4 from "os";
|
|
913
|
-
var
|
|
902
|
+
var logger5 = createLogger("cli:info");
|
|
914
903
|
function getConfigFilePath() {
|
|
915
904
|
const localConfig = path4.join(process.cwd(), ".claude-bridge.yml");
|
|
916
905
|
if (fs4.existsSync(localConfig)) {
|
|
@@ -944,30 +933,25 @@ function printKeyValue(key, value, indent = 0) {
|
|
|
944
933
|
console.log(`${prefix}${key}: ${formatValue(value)}`);
|
|
945
934
|
}
|
|
946
935
|
function showInfo(globalOptions) {
|
|
947
|
-
console.log("Claude Code Bridge -
|
|
948
|
-
console.log("=".repeat(
|
|
949
|
-
printSection("
|
|
950
|
-
|
|
951
|
-
printKeyValue("
|
|
952
|
-
printKeyValue("Platform",
|
|
953
|
-
printKeyValue("
|
|
954
|
-
printKeyValue("
|
|
955
|
-
printKeyValue("
|
|
936
|
+
console.log("Claude Code Bridge - System Information");
|
|
937
|
+
console.log("=".repeat(40));
|
|
938
|
+
printSection("System");
|
|
939
|
+
printKeyValue("Node Version", process.version);
|
|
940
|
+
printKeyValue("OS", `${os4.type()} ${os4.release()}`);
|
|
941
|
+
printKeyValue("Platform", process.platform);
|
|
942
|
+
printKeyValue("Arch", os4.arch());
|
|
943
|
+
printKeyValue("Home Directory", os4.homedir());
|
|
944
|
+
printKeyValue("Working Directory", process.cwd());
|
|
956
945
|
printSection("Network");
|
|
957
|
-
const
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
console.log(" Note: To connect from host machine:");
|
|
967
|
-
console.log(` ws://localhost:${listenPort}`);
|
|
968
|
-
console.log("");
|
|
969
|
-
console.log(" To connect to host from this container:");
|
|
970
|
-
console.log(` ws://${hostGateway}:8766`);
|
|
946
|
+
const interfaces = os4.networkInterfaces();
|
|
947
|
+
for (const [name, addrs] of Object.entries(interfaces)) {
|
|
948
|
+
if (addrs) {
|
|
949
|
+
for (const addr of addrs) {
|
|
950
|
+
if (addr.family === "IPv4" && !addr.internal) {
|
|
951
|
+
printKeyValue(name, addr.address);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
971
955
|
}
|
|
972
956
|
printSection("Configuration");
|
|
973
957
|
const configPath = globalOptions.config || getConfigFilePath();
|
|
@@ -1006,43 +990,16 @@ function showInfo(globalOptions) {
|
|
|
1006
990
|
printKeyValue("Require Confirmation", config.interaction.requireConfirmation, 2);
|
|
1007
991
|
printKeyValue("Notify On Activity", config.interaction.notifyOnActivity, 2);
|
|
1008
992
|
printKeyValue("Task Timeout", `${config.interaction.taskTimeout}ms`, 2);
|
|
1009
|
-
printSection("System");
|
|
1010
|
-
printKeyValue("Node Version", process.version);
|
|
1011
|
-
printKeyValue("OS", `${os4.type()} ${os4.release()}`);
|
|
1012
|
-
printKeyValue("Arch", os4.arch());
|
|
1013
|
-
printKeyValue("Home Directory", os4.homedir());
|
|
1014
|
-
printKeyValue("Working Directory", process.cwd());
|
|
1015
|
-
printSection("Environment Variables");
|
|
1016
|
-
const relevantEnvVars = [
|
|
1017
|
-
"DOCKSAL_STACK",
|
|
1018
|
-
"DOCKSAL_PROJECT",
|
|
1019
|
-
"IS_DDEV_PROJECT",
|
|
1020
|
-
"DDEV_PROJECT",
|
|
1021
|
-
"LANDO",
|
|
1022
|
-
"LANDO_APP_NAME",
|
|
1023
|
-
"LOG_LEVEL"
|
|
1024
|
-
];
|
|
1025
|
-
let hasEnvVars = false;
|
|
1026
|
-
for (const varName of relevantEnvVars) {
|
|
1027
|
-
const value = process.env[varName];
|
|
1028
|
-
if (value !== void 0) {
|
|
1029
|
-
printKeyValue(varName, value);
|
|
1030
|
-
hasEnvVars = true;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
if (!hasEnvVars) {
|
|
1034
|
-
console.log(" (no relevant environment variables set)");
|
|
1035
|
-
}
|
|
1036
993
|
console.log("");
|
|
1037
994
|
}
|
|
1038
995
|
function createInfoCommand() {
|
|
1039
|
-
const command = new
|
|
1040
|
-
command.description("Show
|
|
996
|
+
const command = new Command5("info");
|
|
997
|
+
command.description("Show system and configuration info").action(() => {
|
|
1041
998
|
try {
|
|
1042
999
|
const globalOptions = command.parent?.opts() ?? {};
|
|
1043
1000
|
showInfo(globalOptions);
|
|
1044
1001
|
} catch (error) {
|
|
1045
|
-
|
|
1002
|
+
logger5.error({ error: error.message }, "Info command failed");
|
|
1046
1003
|
console.error(`Error: ${error.message}`);
|
|
1047
1004
|
process.exit(1);
|
|
1048
1005
|
}
|
|
@@ -1050,6 +1007,135 @@ function createInfoCommand() {
|
|
|
1050
1007
|
return command;
|
|
1051
1008
|
}
|
|
1052
1009
|
|
|
1010
|
+
// src/cli/commands/mcp-server.ts
|
|
1011
|
+
import { Command as Command6 } from "commander";
|
|
1012
|
+
import * as fs5 from "fs";
|
|
1013
|
+
import * as path5 from "path";
|
|
1014
|
+
import * as os5 from "os";
|
|
1015
|
+
import { spawn as spawn2 } from "child_process";
|
|
1016
|
+
var logger6 = createLogger("cli:mcp-server");
|
|
1017
|
+
var DEFAULT_DAEMON_PORT = 8766;
|
|
1018
|
+
function getStatusFilePath2() {
|
|
1019
|
+
const bridgeDir = path5.join(os5.homedir(), ".claude-bridge");
|
|
1020
|
+
return path5.join(bridgeDir, "status.json");
|
|
1021
|
+
}
|
|
1022
|
+
function getPidFilePath4() {
|
|
1023
|
+
const bridgeDir = path5.join(os5.homedir(), ".claude-bridge");
|
|
1024
|
+
return path5.join(bridgeDir, "bridge.pid");
|
|
1025
|
+
}
|
|
1026
|
+
function isDaemonRunning() {
|
|
1027
|
+
const pidFile = getPidFilePath4();
|
|
1028
|
+
if (!fs5.existsSync(pidFile)) {
|
|
1029
|
+
return { running: false };
|
|
1030
|
+
}
|
|
1031
|
+
try {
|
|
1032
|
+
const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim(), 10);
|
|
1033
|
+
process.kill(pid, 0);
|
|
1034
|
+
const statusFile = getStatusFilePath2();
|
|
1035
|
+
if (fs5.existsSync(statusFile)) {
|
|
1036
|
+
const status = JSON.parse(fs5.readFileSync(statusFile, "utf-8"));
|
|
1037
|
+
return { running: true, pid, port: status.port };
|
|
1038
|
+
}
|
|
1039
|
+
return { running: true, pid };
|
|
1040
|
+
} catch {
|
|
1041
|
+
return { running: false };
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
async function waitForDaemon(port, timeoutMs) {
|
|
1045
|
+
const startTime = Date.now();
|
|
1046
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
1047
|
+
const { running, port: runningPort } = isDaemonRunning();
|
|
1048
|
+
if (running && (runningPort === port || runningPort === void 0)) {
|
|
1049
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
await new Promise((resolve2) => setTimeout(resolve2, 200));
|
|
1053
|
+
}
|
|
1054
|
+
throw new Error(`Daemon did not start within ${timeoutMs}ms`);
|
|
1055
|
+
}
|
|
1056
|
+
function ensureBridgeDir2() {
|
|
1057
|
+
const bridgeDir = path5.join(os5.homedir(), ".claude-bridge");
|
|
1058
|
+
if (!fs5.existsSync(bridgeDir)) {
|
|
1059
|
+
fs5.mkdirSync(bridgeDir, { recursive: true });
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
async function ensureDaemonRunning(port) {
|
|
1063
|
+
const { running, pid, port: runningPort } = isDaemonRunning();
|
|
1064
|
+
if (running) {
|
|
1065
|
+
const effectivePort = runningPort ?? port;
|
|
1066
|
+
console.error(`[MCP] Bridge daemon already running (PID: ${pid}, port: ${effectivePort})`);
|
|
1067
|
+
return effectivePort;
|
|
1068
|
+
}
|
|
1069
|
+
console.error("[MCP] Starting bridge daemon...");
|
|
1070
|
+
ensureBridgeDir2();
|
|
1071
|
+
const logFile = path5.join(os5.homedir(), ".claude-bridge", "bridge.log");
|
|
1072
|
+
const out = fs5.openSync(logFile, "a");
|
|
1073
|
+
const err = fs5.openSync(logFile, "a");
|
|
1074
|
+
const cliPath = process.argv[1];
|
|
1075
|
+
const child = spawn2(process.execPath, [cliPath, "start", "--daemon", "--port", String(port)], {
|
|
1076
|
+
detached: true,
|
|
1077
|
+
stdio: ["ignore", out, err]
|
|
1078
|
+
});
|
|
1079
|
+
child.unref();
|
|
1080
|
+
console.error(`[MCP] Daemon starting (PID: ${child.pid})`);
|
|
1081
|
+
try {
|
|
1082
|
+
await waitForDaemon(port, 5e3);
|
|
1083
|
+
console.error(`[MCP] Daemon ready on port ${port}`);
|
|
1084
|
+
return port;
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
console.error(`[MCP] Warning: Could not verify daemon is ready: ${error.message}`);
|
|
1087
|
+
return port;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
async function startMcpServer(options, _globalOptions) {
|
|
1091
|
+
let bridgeUrl = options.connect ?? `ws://localhost:${DEFAULT_DAEMON_PORT}`;
|
|
1092
|
+
if (!options.connect) {
|
|
1093
|
+
try {
|
|
1094
|
+
const port = await ensureDaemonRunning(DEFAULT_DAEMON_PORT);
|
|
1095
|
+
bridgeUrl = `ws://localhost:${port}`;
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
console.error(`[MCP] Warning: Could not ensure daemon is running: ${error.message}`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const config = {
|
|
1101
|
+
bridgeUrl,
|
|
1102
|
+
name: options.name ?? "claude-bridge",
|
|
1103
|
+
version: "0.4.0",
|
|
1104
|
+
instanceName: `mcp-server-${process.pid}`,
|
|
1105
|
+
taskTimeout: 6e4
|
|
1106
|
+
};
|
|
1107
|
+
const server = new BridgeMcpServer(config);
|
|
1108
|
+
setupGracefulShutdown({
|
|
1109
|
+
cleanup: async () => {
|
|
1110
|
+
console.error("[MCP] Shutting down...");
|
|
1111
|
+
await server.stop();
|
|
1112
|
+
},
|
|
1113
|
+
verbose: false,
|
|
1114
|
+
timeout: 5e3
|
|
1115
|
+
});
|
|
1116
|
+
handleUnhandledRejections({
|
|
1117
|
+
exit: true,
|
|
1118
|
+
logger: (msg, err) => {
|
|
1119
|
+
console.error(`[MCP] ${msg}: ${err.message}`);
|
|
1120
|
+
logger6.error({ error: err.message }, msg);
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
try {
|
|
1124
|
+
await server.start();
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
console.error(`[MCP] Failed to start MCP server: ${error.message}`);
|
|
1127
|
+
process.exit(1);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
function createMcpServerCommand() {
|
|
1131
|
+
const command = new Command6("mcp-server");
|
|
1132
|
+
command.description("Start the MCP server for Claude Code integration").option("-c, --connect <url>", `Bridge WebSocket URL (default: ws://localhost:${DEFAULT_DAEMON_PORT})`).option("--name <name>", "MCP server name (default: claude-bridge)").action(async (options) => {
|
|
1133
|
+
const globalOptions = command.parent?.opts();
|
|
1134
|
+
await startMcpServer(options, globalOptions);
|
|
1135
|
+
});
|
|
1136
|
+
return command;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1053
1139
|
// src/cli/index.ts
|
|
1054
1140
|
var VERSION = "0.1.0";
|
|
1055
1141
|
var logger7 = createLogger("cli");
|
|
@@ -1073,8 +1159,8 @@ async function main() {
|
|
|
1073
1159
|
program.addCommand(createStopCommand());
|
|
1074
1160
|
program.addCommand(createStatusCommand());
|
|
1075
1161
|
program.addCommand(createConnectCommand());
|
|
1076
|
-
program.addCommand(createDiscoverCommand());
|
|
1077
1162
|
program.addCommand(createInfoCommand());
|
|
1163
|
+
program.addCommand(createMcpServerCommand());
|
|
1078
1164
|
await program.parseAsync(process.argv);
|
|
1079
1165
|
}
|
|
1080
1166
|
main().catch((error) => {
|