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.
@@ -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":"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"}
1
+ {"version":3,"file":"dev-environment.d.ts","sourceRoot":"","sources":["../src/dev-environment.ts"],"names":[],"mappings":"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"}
@@ -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
- // In sandboxed environments, skip port checking - lsof often doesn't exist
170
- // and port conflicts are rare due to process isolation
171
- if (isInSandbox()) {
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
- // Regular environment - do proper port checking with lsof
175
- try {
176
- // Check if lsof command exists first
177
- const checkCmd = process.platform === "win32" ? "where" : "which";
178
- try {
179
- await new Promise((resolve, reject) => {
180
- const check = spawn(checkCmd, ["lsof"], { stdio: "pipe" });
181
- check.on("error", reject);
182
- check.on("exit", (code) => {
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
- return !result; // If no output, port is available
207
- }
208
- catch {
209
- return true; // Assume port is available if check fails
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("\n❌ Your app server failed to start after 30 seconds."));
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
- process.exit(1);
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("\n❌ Your app server failed to start after 30 seconds."));
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
- process.exit(1);
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 = 30;
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
- // CDP monitoring is critical - shutdown if it fails
1414
- this.gracefulShutdown();
1415
- throw error;
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