dev3000 0.0.167 → 0.0.168
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/cdp-monitor.d.ts.map +1 -1
- package/dist/cdp-monitor.js +54 -18
- package/dist/cdp-monitor.js.map +1 -1
- package/dist/cli.js +29 -1
- package/dist/cli.js.map +1 -1
- package/dist/dev-environment.d.ts +7 -0
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +145 -51
- package/dist/dev-environment.js.map +1 -1
- package/dist/tui-interface-opentui.js +1 -1
- package/dist/utils/tmux-helpers.d.ts.map +1 -1
- package/dist/utils/tmux-helpers.js +12 -6
- package/dist/utils/tmux-helpers.js.map +1 -1
- package/package.json +4 -4
|
@@ -38,6 +38,7 @@ export declare function gracefulKillProcess(options: GracefulKillOptions): Promi
|
|
|
38
38
|
interface DevEnvironmentOptions {
|
|
39
39
|
port: string;
|
|
40
40
|
serverCommand: string;
|
|
41
|
+
startupTimeoutSeconds: number;
|
|
41
42
|
profileDir: string;
|
|
42
43
|
logFile: string;
|
|
43
44
|
debug?: boolean;
|
|
@@ -125,12 +126,18 @@ export declare class DevEnvironment {
|
|
|
125
126
|
private isSessionCwdOwned;
|
|
126
127
|
private isPidRunning;
|
|
127
128
|
private killServerPidIfOwned;
|
|
129
|
+
private killTrackedChromePids;
|
|
128
130
|
private cleanupOrphanedServer;
|
|
129
131
|
private acquireLock;
|
|
130
132
|
private releaseLock;
|
|
131
133
|
private detectPortChange;
|
|
132
134
|
private debugLog;
|
|
133
135
|
private showRecentLogs;
|
|
136
|
+
private getRecentLogLines;
|
|
137
|
+
private simplifyLogLine;
|
|
138
|
+
private inferStartupFailureHint;
|
|
139
|
+
private getStartupContextLines;
|
|
140
|
+
private writeStartupFailureContext;
|
|
134
141
|
private checkForCommonIssues;
|
|
135
142
|
private waitForServer;
|
|
136
143
|
private initializeD3KLog;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-environment.d.ts","sourceRoot":"","sources":["../src/dev-environment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-environment.d.ts","sourceRoot":"","sources":["../src/dev-environment.ts"],"names":[],"mappings":"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"}
|
package/dist/dev-environment.js
CHANGED
|
@@ -2,10 +2,12 @@ import chalk from "chalk";
|
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
4
4
|
import https from "https";
|
|
5
|
+
import { createServer } from "net";
|
|
5
6
|
import ora from "ora";
|
|
6
7
|
import { homedir, tmpdir } from "os";
|
|
7
8
|
import { dirname, join, resolve, sep } from "path";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
10
|
+
import { stripVTControlCharacters } from "util";
|
|
9
11
|
import { CDPMonitor } from "./cdp-monitor.js";
|
|
10
12
|
import { ScreencastManager } from "./screencast-manager.js";
|
|
11
13
|
import { NextJsErrorDetector, OutputProcessor, StandardLogParser } from "./services/parsers/index.js";
|
|
@@ -163,51 +165,27 @@ export function countActiveD3kInstances(excludeCurrentPid = false) {
|
|
|
163
165
|
/**
|
|
164
166
|
* Check if a port is available for binding (no process is listening on it).
|
|
165
167
|
* Used for finding available ports before starting servers.
|
|
166
|
-
* In sandbox environments, skips checking since lsof often doesn't exist.
|
|
167
168
|
*/
|
|
168
169
|
async function isPortAvailable(port) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return true;
|
|
170
|
+
const portNumber = Number.parseInt(port, 10);
|
|
171
|
+
if (!Number.isInteger(portNumber) || portNumber < 0 || portNumber > 65535) {
|
|
172
|
+
return false;
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (code === 0)
|
|
184
|
-
resolve();
|
|
185
|
-
else
|
|
186
|
-
reject(new Error("lsof not found"));
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
// lsof doesn't exist, assume port is available
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
// lsof exists, use it to check the port
|
|
195
|
-
const result = await new Promise((resolve, reject) => {
|
|
196
|
-
const proc = spawn("lsof", ["-ti", `:${port}`], { stdio: "pipe" });
|
|
197
|
-
let output = "";
|
|
198
|
-
proc.on("error", (err) => {
|
|
199
|
-
reject(err);
|
|
200
|
-
});
|
|
201
|
-
proc.stdout?.on("data", (data) => {
|
|
202
|
-
output += data.toString();
|
|
203
|
-
});
|
|
204
|
-
proc.on("exit", () => resolve(output.trim()));
|
|
174
|
+
return new Promise((resolve) => {
|
|
175
|
+
const server = createServer();
|
|
176
|
+
server.once("error", (error) => {
|
|
177
|
+
// EADDRINUSE/EACCES both mean we cannot bind this port.
|
|
178
|
+
if (error.code === "EADDRINUSE" || error.code === "EACCES") {
|
|
179
|
+
resolve(false);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
resolve(true);
|
|
205
183
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
184
|
+
server.once("listening", () => {
|
|
185
|
+
server.close(() => resolve(true));
|
|
186
|
+
});
|
|
187
|
+
server.listen(portNumber);
|
|
188
|
+
});
|
|
211
189
|
}
|
|
212
190
|
export async function isServerListening(port) {
|
|
213
191
|
// Try HTTP first
|
|
@@ -859,11 +837,13 @@ export class DevEnvironment {
|
|
|
859
837
|
await this.tui.updateStatus("Waiting for app...");
|
|
860
838
|
const serverStarted = await this.waitForServer();
|
|
861
839
|
if (!serverStarted) {
|
|
840
|
+
this.writeStartupFailureContext(`App did not become reachable within ${this.options.startupTimeoutSeconds} seconds.`, 30);
|
|
862
841
|
await this.tui.updateStatus("❌ Server failed to start");
|
|
863
|
-
console.error(chalk.red(
|
|
842
|
+
console.error(chalk.red(`\n❌ Your app server failed to start after ${this.options.startupTimeoutSeconds} seconds.`));
|
|
864
843
|
console.error(chalk.yellow(`Check the logs at ~/.d3k/${getProjectName()}/logs/ for errors.`));
|
|
865
844
|
console.error(chalk.yellow("Exiting without launching browser."));
|
|
866
|
-
|
|
845
|
+
await this.gracefulShutdown();
|
|
846
|
+
return;
|
|
867
847
|
}
|
|
868
848
|
// Update TUI with confirmed port (may have changed during server startup)
|
|
869
849
|
this.tui.updateAppPort(this.options.port);
|
|
@@ -918,11 +898,13 @@ export class DevEnvironment {
|
|
|
918
898
|
this.spinner.text = "Waiting for app...";
|
|
919
899
|
const serverStarted = await this.waitForServer();
|
|
920
900
|
if (!serverStarted) {
|
|
901
|
+
this.writeStartupFailureContext(`App did not become reachable within ${this.options.startupTimeoutSeconds} seconds.`, 30);
|
|
921
902
|
this.spinner.fail("Server failed to start");
|
|
922
|
-
console.error(chalk.red(
|
|
903
|
+
console.error(chalk.red(`\n❌ Your app server failed to start after ${this.options.startupTimeoutSeconds} seconds.`));
|
|
923
904
|
console.error(chalk.yellow(`Check the logs at ~/.d3k/${getProjectName()}/logs/ for errors.`));
|
|
924
905
|
console.error(chalk.yellow("Exiting without launching browser."));
|
|
925
|
-
|
|
906
|
+
await this.gracefulShutdown();
|
|
907
|
+
return;
|
|
926
908
|
}
|
|
927
909
|
// Legacy server removed - using CLI commands instead
|
|
928
910
|
// await this.waitForMcpServer()
|
|
@@ -1034,6 +1016,7 @@ export class DevEnvironment {
|
|
|
1034
1016
|
const nodeModulesExists = existsSync(join(process.cwd(), "node_modules"));
|
|
1035
1017
|
// If it's an early exit and node_modules doesn't exist, show helpful message
|
|
1036
1018
|
if (isEarlyExit && !nodeModulesExists) {
|
|
1019
|
+
this.writeStartupFailureContext(`Server exited early with code ${code}.`, 30);
|
|
1037
1020
|
if (this.spinner?.isSpinning) {
|
|
1038
1021
|
this.spinner.fail("Server script failed to start - missing dependencies");
|
|
1039
1022
|
}
|
|
@@ -1048,6 +1031,7 @@ export class DevEnvironment {
|
|
|
1048
1031
|
}
|
|
1049
1032
|
// If it's an early exit but node_modules exists, it's still likely a configuration issue
|
|
1050
1033
|
if (isEarlyExit) {
|
|
1034
|
+
this.writeStartupFailureContext(`Server exited early with code ${code}.`, 30);
|
|
1051
1035
|
if (this.spinner?.isSpinning) {
|
|
1052
1036
|
this.spinner.fail(`Server script failed to start (exited with code ${code})`);
|
|
1053
1037
|
}
|
|
@@ -1067,6 +1051,7 @@ export class DevEnvironment {
|
|
|
1067
1051
|
// - Code 143: SIGTERM
|
|
1068
1052
|
const isFatalExit = code !== 0 && code !== 130 && code !== 143;
|
|
1069
1053
|
if (isFatalExit) {
|
|
1054
|
+
this.writeStartupFailureContext(`Server process exited with code ${code}.`, 30);
|
|
1070
1055
|
// Stop spinner and show error for fatal exits
|
|
1071
1056
|
if (this.spinner?.isSpinning) {
|
|
1072
1057
|
this.spinner.fail(`Server process exited with code ${code}`);
|
|
@@ -1157,6 +1142,26 @@ export class DevEnvironment {
|
|
|
1157
1142
|
}
|
|
1158
1143
|
}
|
|
1159
1144
|
}
|
|
1145
|
+
killTrackedChromePids(chromePids, reason) {
|
|
1146
|
+
if (chromePids.length === 0)
|
|
1147
|
+
return;
|
|
1148
|
+
this.debugLog(`Killing tracked Chrome PIDs (${reason}): [${chromePids.join(", ")}]`);
|
|
1149
|
+
for (const pid of chromePids) {
|
|
1150
|
+
try {
|
|
1151
|
+
process.kill(pid, "SIGTERM");
|
|
1152
|
+
}
|
|
1153
|
+
catch {
|
|
1154
|
+
// Ignore - process may already be dead
|
|
1155
|
+
}
|
|
1156
|
+
try {
|
|
1157
|
+
process.kill(pid, 0);
|
|
1158
|
+
process.kill(pid, "SIGKILL");
|
|
1159
|
+
}
|
|
1160
|
+
catch {
|
|
1161
|
+
// Ignore - process may already be dead
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1160
1165
|
cleanupOrphanedServer() {
|
|
1161
1166
|
const projectName = getProjectName();
|
|
1162
1167
|
const sessionInfo = getSessionInfo(projectName);
|
|
@@ -1310,6 +1315,79 @@ export class DevEnvironment {
|
|
|
1310
1315
|
console.log(chalk.yellow(`💡 Check logs for details: ${this.options.logFile}`));
|
|
1311
1316
|
}
|
|
1312
1317
|
}
|
|
1318
|
+
getRecentLogLines(limit = 30) {
|
|
1319
|
+
try {
|
|
1320
|
+
if (!existsSync(this.options.logFile))
|
|
1321
|
+
return [];
|
|
1322
|
+
const logContent = readFileSync(this.options.logFile, "utf8");
|
|
1323
|
+
return logContent
|
|
1324
|
+
.split("\n")
|
|
1325
|
+
.map((line) => line.trimEnd())
|
|
1326
|
+
.filter((line) => line.trim() && !line.includes("[STARTUP_FAILURE]") && !line.includes("[STARTUP_CONTEXT]"))
|
|
1327
|
+
.slice(-limit);
|
|
1328
|
+
}
|
|
1329
|
+
catch {
|
|
1330
|
+
return [];
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
simplifyLogLine(line) {
|
|
1334
|
+
// Strip ANSI escapes + non-printables, then remove "[time] [source]" prefix.
|
|
1335
|
+
const noAnsi = stripVTControlCharacters(line).replace(/[^\t\r\n -~]/g, "");
|
|
1336
|
+
return noAnsi.replace(/^\[[^\]]+\]\s+\[[^\]]+\]\s+/, "");
|
|
1337
|
+
}
|
|
1338
|
+
inferStartupFailureHint(logLines) {
|
|
1339
|
+
const joined = logLines.join("\n");
|
|
1340
|
+
if (/ERR_PNPM_NO_SCRIPT/i.test(joined) || /Missing script:\s*dev/i.test(joined)) {
|
|
1341
|
+
return "Missing package script. Pass --script or --command for this project.";
|
|
1342
|
+
}
|
|
1343
|
+
if (/EADDRINUSE|port .* in use/i.test(joined)) {
|
|
1344
|
+
return "Port conflict detected. Try another --port or stop the process using that port.";
|
|
1345
|
+
}
|
|
1346
|
+
if (/Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(joined)) {
|
|
1347
|
+
return "Dependency or path issue detected. Verify installs and command paths.";
|
|
1348
|
+
}
|
|
1349
|
+
if (/permission denied|EACCES/i.test(joined)) {
|
|
1350
|
+
return "Permission issue detected. Check executable permissions and environment access.";
|
|
1351
|
+
}
|
|
1352
|
+
if (/\/\/:dev:ts:/i.test(joined) && !/vercel-site:dev:|Local:\s+http|Ready in/i.test(joined)) {
|
|
1353
|
+
return "Background typegen/typecheck is running, but app server has not started yet. Use a longer timeout or run the app-specific dev command.";
|
|
1354
|
+
}
|
|
1355
|
+
if (/Starting compilation in watch mode/i.test(joined) && !/Local:\s+http|Ready in|listening on/i.test(joined)) {
|
|
1356
|
+
return "Compiler watch mode started, but no app port was detected yet. This may need a longer startup timeout.";
|
|
1357
|
+
}
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
getStartupContextLines(limit = 30) {
|
|
1361
|
+
const recentLines = this.getRecentLogLines(200);
|
|
1362
|
+
if (recentLines.length === 0)
|
|
1363
|
+
return [];
|
|
1364
|
+
const errorPattern = /(^|[\s:])(error|failed|fatal|exception|unhandled rejection|err_|eaddrinuse|module_not_found|missing script|not found|timeout)([\s:.,]|$)/i;
|
|
1365
|
+
const ignorePattern = /Found 0 errors/i;
|
|
1366
|
+
const errorLines = recentLines.filter((line) => errorPattern.test(line) && !ignorePattern.test(line));
|
|
1367
|
+
// Prefer error-focused context when available; otherwise fall back to the latest lines.
|
|
1368
|
+
if (errorLines.length > 0) {
|
|
1369
|
+
const focused = errorLines.slice(-20);
|
|
1370
|
+
const tail = recentLines.slice(-10);
|
|
1371
|
+
return [...focused, ...tail].slice(-limit);
|
|
1372
|
+
}
|
|
1373
|
+
return recentLines.slice(-limit);
|
|
1374
|
+
}
|
|
1375
|
+
writeStartupFailureContext(reason, contextLines = 30) {
|
|
1376
|
+
const context = this.getStartupContextLines(contextLines);
|
|
1377
|
+
const hint = this.inferStartupFailureHint(context);
|
|
1378
|
+
this.logger.log("server", `[STARTUP_FAILURE] ${reason}`);
|
|
1379
|
+
this.logger.log("server", `[STARTUP_FAILURE] Command: ${this.options.serverCommand}`);
|
|
1380
|
+
this.logger.log("server", `[STARTUP_FAILURE] Full logs: ${this.options.logFile}`);
|
|
1381
|
+
if (hint) {
|
|
1382
|
+
this.logger.log("server", `[STARTUP_HINT] ${hint}`);
|
|
1383
|
+
}
|
|
1384
|
+
if (context.length > 0) {
|
|
1385
|
+
this.logger.log("server", `[STARTUP_CONTEXT] Selected ${context.length} relevant log lines:`);
|
|
1386
|
+
for (const line of context) {
|
|
1387
|
+
this.logger.log("server", `[STARTUP_CONTEXT] ${this.simplifyLogLine(line)}`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1313
1391
|
checkForCommonIssues() {
|
|
1314
1392
|
try {
|
|
1315
1393
|
if (!existsSync(this.options.logFile))
|
|
@@ -1337,7 +1415,7 @@ export class DevEnvironment {
|
|
|
1337
1415
|
}
|
|
1338
1416
|
}
|
|
1339
1417
|
async waitForServer() {
|
|
1340
|
-
const maxAttempts =
|
|
1418
|
+
const maxAttempts = this.options.startupTimeoutSeconds;
|
|
1341
1419
|
let attempts = 0;
|
|
1342
1420
|
const startTime = Date.now();
|
|
1343
1421
|
this.debugLog(`Waiting for server to report its port...`);
|
|
@@ -1410,9 +1488,23 @@ export class DevEnvironment {
|
|
|
1410
1488
|
}
|
|
1411
1489
|
catch (error) {
|
|
1412
1490
|
console.error(chalk.red("⚠️ CDP monitoring setup failed:"), error);
|
|
1413
|
-
|
|
1414
|
-
this.
|
|
1415
|
-
|
|
1491
|
+
this.logger.log("browser", `[CDP] Monitoring startup failed: ${error}`);
|
|
1492
|
+
this.logger.log("browser", "[CDP] Browser monitoring disabled; app server will continue running.");
|
|
1493
|
+
if (this.tui) {
|
|
1494
|
+
this.tui.updateStatus("⚠ Browser monitoring failed; continuing with server logs only");
|
|
1495
|
+
}
|
|
1496
|
+
// Best-effort cleanup of partially initialized monitor, but keep d3k running.
|
|
1497
|
+
if (this.cdpMonitor) {
|
|
1498
|
+
try {
|
|
1499
|
+
this.cdpMonitor.prepareShutdown();
|
|
1500
|
+
await this.cdpMonitor.shutdown();
|
|
1501
|
+
}
|
|
1502
|
+
catch {
|
|
1503
|
+
// Ignore cleanup errors; monitoring is already disabled
|
|
1504
|
+
}
|
|
1505
|
+
this.cdpMonitor = null;
|
|
1506
|
+
}
|
|
1507
|
+
this.debugLog(`CDP monitoring disabled due to startup failure: ${error instanceof Error ? error.message : error}`);
|
|
1416
1508
|
}
|
|
1417
1509
|
}
|
|
1418
1510
|
async startCDPMonitoring() {
|
|
@@ -1573,6 +1665,8 @@ export class DevEnvironment {
|
|
|
1573
1665
|
if (sessionInfo?.serverPid) {
|
|
1574
1666
|
this.killServerPidIfOwned(sessionInfo.serverPid, sessionInfo.cwd, "graceful shutdown");
|
|
1575
1667
|
}
|
|
1668
|
+
// Always kill tracked Chrome processes, even if cdpMonitor is unavailable.
|
|
1669
|
+
this.killTrackedChromePids(sessionInfo?.chromePids ?? [], "graceful shutdown");
|
|
1576
1670
|
// Shutdown CDP monitor if it was started
|
|
1577
1671
|
if (this.cdpMonitor) {
|
|
1578
1672
|
try {
|
|
@@ -1813,8 +1907,6 @@ export class DevEnvironment {
|
|
|
1813
1907
|
}
|
|
1814
1908
|
// Fallback: force kill any remaining Chrome processes for THIS instance only
|
|
1815
1909
|
try {
|
|
1816
|
-
const projectName = getProjectName();
|
|
1817
|
-
const sessionInfo = getSessionInfo(projectName);
|
|
1818
1910
|
const chromePids = sessionInfo?.chromePids ?? [];
|
|
1819
1911
|
if (chromePids.length > 0) {
|
|
1820
1912
|
this.debugLog(`Fallback cleanup: killing Chrome PIDs for this instance: [${chromePids.join(", ")}]`);
|
|
@@ -1840,6 +1932,8 @@ export class DevEnvironment {
|
|
|
1840
1932
|
}
|
|
1841
1933
|
}
|
|
1842
1934
|
}
|
|
1935
|
+
// Safety net: ensure tracked Chrome processes are always terminated on shutdown.
|
|
1936
|
+
this.killTrackedChromePids(sessionInfo?.chromePids ?? [], "handleShutdown");
|
|
1843
1937
|
// REMOVED: No longer clean up CLI config files on shutdown
|
|
1844
1938
|
// This was causing Claude Code instances to crash when dev3000 was killed
|
|
1845
1939
|
// Config file cleanup removed; keep user config files untouched on shutdown
|