taskplane 0.1.2 → 0.1.3
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/dashboard/server.cjs +60 -9
- package/package.json +1 -1
package/dashboard/server.cjs
CHANGED
|
@@ -18,7 +18,8 @@ const { execFileSync, exec } = require("child_process");
|
|
|
18
18
|
// ─── Configuration ──────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
const PUBLIC_DIR = path.join(__dirname, "public");
|
|
21
|
-
const DEFAULT_PORT =
|
|
21
|
+
const DEFAULT_PORT = 8100;
|
|
22
|
+
const MAX_PORT_ATTEMPTS = 20;
|
|
22
23
|
const POLL_INTERVAL = 2000; // ms between state checks
|
|
23
24
|
|
|
24
25
|
// REPO_ROOT is resolved after parseArgs() — see initialization below.
|
|
@@ -427,7 +428,7 @@ function serveHistoryEntry(req, res, batchId) {
|
|
|
427
428
|
|
|
428
429
|
// ─── HTTP Server ────────────────────────────────────────────────────────────
|
|
429
430
|
|
|
430
|
-
function createServer(
|
|
431
|
+
function createServer() {
|
|
431
432
|
const server = http.createServer((req, res) => {
|
|
432
433
|
const pathname = new URL(req.url, "http://localhost").pathname;
|
|
433
434
|
|
|
@@ -456,10 +457,6 @@ function createServer(port) {
|
|
|
456
457
|
}
|
|
457
458
|
});
|
|
458
459
|
|
|
459
|
-
server.listen(port, () => {
|
|
460
|
-
console.log(`\n Orchestrator Dashboard → http://localhost:${port}\n`);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
460
|
return server;
|
|
464
461
|
}
|
|
465
462
|
|
|
@@ -473,7 +470,57 @@ function openBrowser(url) {
|
|
|
473
470
|
|
|
474
471
|
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
475
472
|
|
|
476
|
-
|
|
473
|
+
/** Try to listen on a port. Resolves with the port on success, rejects on EADDRINUSE. */
|
|
474
|
+
function tryListen(server, port) {
|
|
475
|
+
return new Promise((resolve, reject) => {
|
|
476
|
+
const onError = (err) => {
|
|
477
|
+
server.removeListener("listening", onListening);
|
|
478
|
+
reject(err);
|
|
479
|
+
};
|
|
480
|
+
const onListening = () => {
|
|
481
|
+
server.removeListener("error", onError);
|
|
482
|
+
resolve(port);
|
|
483
|
+
};
|
|
484
|
+
server.once("error", onError);
|
|
485
|
+
server.once("listening", onListening);
|
|
486
|
+
server.listen(port);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** Find an available port starting from `start`, trying up to MAX_PORT_ATTEMPTS. */
|
|
491
|
+
async function findPort(server, start, explicit) {
|
|
492
|
+
// If the user explicitly passed --port, only try that one
|
|
493
|
+
if (explicit) {
|
|
494
|
+
try {
|
|
495
|
+
return await tryListen(server, start);
|
|
496
|
+
} catch (err) {
|
|
497
|
+
if (err.code === "EADDRINUSE") {
|
|
498
|
+
console.error(`\n Port ${start} is already in use.`);
|
|
499
|
+
console.error(` Try: taskplane dashboard --port ${start + 1}\n`);
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
throw err;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Auto-scan for an available port
|
|
506
|
+
for (let port = start; port < start + MAX_PORT_ATTEMPTS; port++) {
|
|
507
|
+
try {
|
|
508
|
+
return await tryListen(server, port);
|
|
509
|
+
} catch (err) {
|
|
510
|
+
if (err.code === "EADDRINUSE") {
|
|
511
|
+
// Close the server so we can retry on the next port
|
|
512
|
+
server.close();
|
|
513
|
+
server = createServer();
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
throw err;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
console.error(`\n No available port found in range ${start}-${start + MAX_PORT_ATTEMPTS - 1}.\n`);
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async function main() {
|
|
477
524
|
const opts = parseArgs();
|
|
478
525
|
|
|
479
526
|
// Resolve project root: --root flag > cwd
|
|
@@ -481,7 +528,11 @@ function main() {
|
|
|
481
528
|
BATCH_STATE_PATH = path.join(REPO_ROOT, ".pi", "batch-state.json");
|
|
482
529
|
BATCH_HISTORY_PATH = path.join(REPO_ROOT, ".pi", "batch-history.json");
|
|
483
530
|
|
|
484
|
-
const server = createServer(
|
|
531
|
+
const server = createServer();
|
|
532
|
+
const explicitPort = process.argv.slice(2).includes("--port");
|
|
533
|
+
const port = await findPort(server, opts.port, explicitPort);
|
|
534
|
+
|
|
535
|
+
console.log(`\n Orchestrator Dashboard → http://localhost:${port}\n`);
|
|
485
536
|
|
|
486
537
|
// Broadcast state to all SSE clients on interval
|
|
487
538
|
const pollTimer = setInterval(broadcastState, POLL_INTERVAL);
|
|
@@ -507,7 +558,7 @@ function main() {
|
|
|
507
558
|
|
|
508
559
|
// Auto-open browser
|
|
509
560
|
if (opts.open) {
|
|
510
|
-
setTimeout(() => openBrowser(`http://localhost:${
|
|
561
|
+
setTimeout(() => openBrowser(`http://localhost:${port}`), 500);
|
|
511
562
|
}
|
|
512
563
|
|
|
513
564
|
// Graceful shutdown
|