codehere 0.3.0-rc.4 → 0.3.0-rc.5
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/dist/index.js +44 -11
- package/dist/infrastructure/protocols/probe-port.js +36 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -354,10 +354,30 @@ program
|
|
|
354
354
|
const port = parseInt(options?.port ?? '3773', 10);
|
|
355
355
|
const host = options?.host ?? '127.0.0.1';
|
|
356
356
|
const shouldOpen = options?.open !== false;
|
|
357
|
+
const url = `http://${host}:${port}/`;
|
|
358
|
+
const openBrowser = async () => {
|
|
359
|
+
if (!shouldOpen)
|
|
360
|
+
return;
|
|
361
|
+
const { spawn } = await import('child_process');
|
|
362
|
+
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
363
|
+
try {
|
|
364
|
+
spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
/* browser open is best-effort */
|
|
368
|
+
}
|
|
369
|
+
};
|
|
357
370
|
try {
|
|
358
371
|
const { startMCPServer } = await import('./infrastructure/protocols/mcp-server-impl.js');
|
|
359
372
|
const server = startMCPServer({ port, host });
|
|
360
|
-
|
|
373
|
+
// Wait for the server to either start listening or fail. The default
|
|
374
|
+
// Node behavior on listen() failure is an unhandled 'error' event which
|
|
375
|
+
// crashes the process with a stack trace. We catch it here so the user
|
|
376
|
+
// sees a friendly message instead.
|
|
377
|
+
await new Promise((resolve, reject) => {
|
|
378
|
+
server.once('listening', () => resolve());
|
|
379
|
+
server.once('error', reject);
|
|
380
|
+
});
|
|
361
381
|
newline();
|
|
362
382
|
console.log(success('codehere is running'));
|
|
363
383
|
newline();
|
|
@@ -367,16 +387,7 @@ program
|
|
|
367
387
|
newline();
|
|
368
388
|
console.log(colors.dim(' Press Ctrl+C to stop'));
|
|
369
389
|
newline();
|
|
370
|
-
|
|
371
|
-
const { spawn } = await import('child_process');
|
|
372
|
-
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
373
|
-
try {
|
|
374
|
-
spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
375
|
-
}
|
|
376
|
-
catch {
|
|
377
|
-
/* browser open is best-effort */
|
|
378
|
-
}
|
|
379
|
-
}
|
|
390
|
+
await openBrowser();
|
|
380
391
|
process.on('SIGINT', () => {
|
|
381
392
|
server.close();
|
|
382
393
|
console.log(colors.dim('\ncodehere stopped'));
|
|
@@ -387,6 +398,28 @@ program
|
|
|
387
398
|
});
|
|
388
399
|
}
|
|
389
400
|
catch (err) {
|
|
401
|
+
const code = err?.code;
|
|
402
|
+
if (code === 'EADDRINUSE') {
|
|
403
|
+
// Port is taken. Probe to find out by what — if it's a running codehere,
|
|
404
|
+
// we should hand the user the existing URL; if it's something else,
|
|
405
|
+
// tell them to pick a different port.
|
|
406
|
+
const { isCodehereOnPort } = await import('./infrastructure/protocols/probe-port.js');
|
|
407
|
+
const existing = await isCodehereOnPort(host, port);
|
|
408
|
+
newline();
|
|
409
|
+
if (existing.isCodehere) {
|
|
410
|
+
console.log(success(`codehere is already running at ${url}`));
|
|
411
|
+
if (existing.version) {
|
|
412
|
+
console.log(colors.dim(` version: ${existing.version}`));
|
|
413
|
+
}
|
|
414
|
+
newline();
|
|
415
|
+
console.log(colors.dim(' Opening browser...'));
|
|
416
|
+
await openBrowser();
|
|
417
|
+
process.exit(0);
|
|
418
|
+
}
|
|
419
|
+
console.error(error(`Port ${port} is in use by another process.`));
|
|
420
|
+
console.error(colors.dim(` Try: codehere start --port ${port + 1}`));
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
390
423
|
console.error(error('Failed to start:'), err instanceof Error ? err.message : String(err));
|
|
391
424
|
process.exit(1);
|
|
392
425
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect whether a given host:port is already serving a codehere instance.
|
|
3
|
+
*
|
|
4
|
+
* Used by `codehere start` when the listen() fails with EADDRINUSE: we'd
|
|
5
|
+
* rather give the user a friendly "codehere already running, opening browser"
|
|
6
|
+
* than a Node stack trace. If something ELSE holds the port we tell them to
|
|
7
|
+
* pick a different one.
|
|
8
|
+
*
|
|
9
|
+
* Approach: hit the existing server's /health endpoint. The codehere MCP
|
|
10
|
+
* server returns a JSON body with `server: 'codehere-mcp'` — no other tool
|
|
11
|
+
* uses that exact identifier. Generous 1.5-second timeout so a slow loopback
|
|
12
|
+
* doesn't false-negative.
|
|
13
|
+
*/
|
|
14
|
+
export async function isCodehereOnPort(host, port, timeoutMs = 1500) {
|
|
15
|
+
const ac = new AbortController();
|
|
16
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(`http://${host}:${port}/health`, { signal: ac.signal });
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
return { isCodehere: false };
|
|
21
|
+
const body = (await res.json());
|
|
22
|
+
if (body?.server === 'codehere-mcp') {
|
|
23
|
+
return { isCodehere: true, version: body.version };
|
|
24
|
+
}
|
|
25
|
+
return { isCodehere: false };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Connection refused, timeout, malformed response — anything that's not
|
|
29
|
+
// a positive codehere identification. Treat as "not codehere."
|
|
30
|
+
return { isCodehere: false };
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=probe-port.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehere",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.5",
|
|
4
4
|
"description": "A PM for your AI coding agents. Delegate, orchestrate, and audit Claude Code, Codex, Aider, and OpenCode from one local web UI — every AI action traced, every file scored.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|