dev3000 0.0.173 → 0.0.174
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 +18 -2
- package/dist/cdp-monitor.d.ts.map +1 -1
- package/dist/cdp-monitor.js +234 -79
- package/dist/cdp-monitor.js.map +1 -1
- package/dist/cli.js +24 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/resume.d.ts +11 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +75 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/dev-environment.d.ts +23 -1
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +174 -23
- package/dist/dev-environment.js.map +1 -1
- package/dist/tui-interface-opentui.d.ts +2 -0
- package/dist/tui-interface-opentui.d.ts.map +1 -1
- package/dist/tui-interface-opentui.js +17 -3
- package/dist/tui-interface-opentui.js.map +1 -1
- package/dist/utils/agent-selection.js +1 -1
- package/dist/utils/agent-selection.js.map +1 -1
- package/dist/utils/project-metadata.d.ts +4 -0
- package/dist/utils/project-metadata.d.ts.map +1 -0
- package/dist/utils/project-metadata.js +48 -0
- package/dist/utils/project-metadata.js.map +1 -0
- package/package.json +8 -10
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { basename } from "path";
|
|
4
|
+
import { checkBinaryExists } from "../utils/agent-selection.js";
|
|
5
|
+
import { readProjectAgentName as readRememberedProjectAgentName } from "../utils/project-metadata.js";
|
|
6
|
+
import { loadUserConfig } from "../utils/user-config.js";
|
|
7
|
+
function ensureCommandPath(env) {
|
|
8
|
+
if (!env.PATH || env.PATH === "") {
|
|
9
|
+
env.PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
10
|
+
}
|
|
11
|
+
return env;
|
|
12
|
+
}
|
|
13
|
+
export function readProjectAgentName(cwd = process.cwd()) {
|
|
14
|
+
return readRememberedProjectAgentName(cwd);
|
|
15
|
+
}
|
|
16
|
+
export function resolveResumeAgentName(cwd = process.cwd()) {
|
|
17
|
+
const projectAgentName = readProjectAgentName(cwd);
|
|
18
|
+
if (projectAgentName) {
|
|
19
|
+
return projectAgentName;
|
|
20
|
+
}
|
|
21
|
+
const defaultAgentName = loadUserConfig().defaultAgent?.name;
|
|
22
|
+
return typeof defaultAgentName === "string" && defaultAgentName.trim().length > 0 ? defaultAgentName.trim() : null;
|
|
23
|
+
}
|
|
24
|
+
export function getResumeLaunchSpec(agentName, cwd = process.cwd()) {
|
|
25
|
+
switch (agentName.toLowerCase()) {
|
|
26
|
+
case "claude":
|
|
27
|
+
return { agentName, binary: "claude", args: ["-c"] };
|
|
28
|
+
case "claude-yolo":
|
|
29
|
+
return { agentName, binary: "claude", args: ["--dangerously-skip-permissions", "-c"] };
|
|
30
|
+
case "codex":
|
|
31
|
+
return { agentName, binary: "codex", args: ["resume", "--last", "-C", cwd] };
|
|
32
|
+
case "codex-yolo":
|
|
33
|
+
return {
|
|
34
|
+
agentName,
|
|
35
|
+
binary: "codex",
|
|
36
|
+
args: ["resume", "--last", "--dangerously-bypass-approvals-and-sandbox", "-C", cwd]
|
|
37
|
+
};
|
|
38
|
+
case "opencode":
|
|
39
|
+
return { agentName, binary: "opencode", args: ["-c"] };
|
|
40
|
+
default:
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function resumeLastAgent(cwd = process.cwd()) {
|
|
45
|
+
const agentName = resolveResumeAgentName(cwd);
|
|
46
|
+
if (!agentName) {
|
|
47
|
+
console.error(chalk.red("❌ No previous agent found for this project."));
|
|
48
|
+
console.error(chalk.gray("Start d3k with an agent once, then `d3k resume` can reopen it here."));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const spec = getResumeLaunchSpec(agentName, cwd);
|
|
52
|
+
if (!spec) {
|
|
53
|
+
console.error(chalk.red(`❌ Agent "${agentName}" does not support automatic resume yet.`));
|
|
54
|
+
console.error(chalk.gray("Supported today: claude, claude-yolo, codex, codex-yolo, opencode"));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
if (!checkBinaryExists(spec.binary)) {
|
|
58
|
+
console.error(chalk.red(`❌ ${spec.binary} is not installed or not on PATH.`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.cyan(`↩ Resuming ${spec.agentName} for ${basename(cwd)}...`));
|
|
62
|
+
const env = ensureCommandPath({ ...process.env });
|
|
63
|
+
const child = spawn(spec.binary, spec.args, {
|
|
64
|
+
cwd,
|
|
65
|
+
env,
|
|
66
|
+
stdio: "inherit",
|
|
67
|
+
shell: false
|
|
68
|
+
});
|
|
69
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
70
|
+
child.once("error", reject);
|
|
71
|
+
child.once("exit", (code) => resolve(code ?? 1));
|
|
72
|
+
});
|
|
73
|
+
process.exit(exitCode);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=resume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume.js","sourceRoot":"","sources":["../../src/commands/resume.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,6 +39,7 @@ interface DevEnvironmentOptions {
|
|
|
39
39
|
port: string;
|
|
40
40
|
serverCommand: string;
|
|
41
41
|
startupTimeoutSeconds: number;
|
|
42
|
+
browserNavigationTimeoutSeconds: number;
|
|
42
43
|
profileDir: string;
|
|
43
44
|
browserTool: "agent-browser" | "next-browser";
|
|
44
45
|
logFile: string;
|
|
@@ -57,6 +58,7 @@ interface DevEnvironmentOptions {
|
|
|
57
58
|
debugPort?: number;
|
|
58
59
|
headless?: boolean;
|
|
59
60
|
withAgent?: string;
|
|
61
|
+
agentName?: string;
|
|
60
62
|
skillsAgentId?: string;
|
|
61
63
|
autoSkills?: boolean;
|
|
62
64
|
installSkills?: boolean;
|
|
@@ -69,6 +71,26 @@ interface DevEnvironmentOptions {
|
|
|
69
71
|
* This function is used to reason about multi-instance shutdown behavior.
|
|
70
72
|
*/
|
|
71
73
|
export declare function countActiveD3kInstances(excludeCurrentPid?: boolean): number;
|
|
74
|
+
type D3kLockInfo = {
|
|
75
|
+
pid: number;
|
|
76
|
+
cwd?: string | null;
|
|
77
|
+
createdAt?: string | null;
|
|
78
|
+
processStartTime?: string | null;
|
|
79
|
+
};
|
|
80
|
+
type LockValidationResult = {
|
|
81
|
+
active: boolean;
|
|
82
|
+
reason: string;
|
|
83
|
+
lockInfo: D3kLockInfo | null;
|
|
84
|
+
};
|
|
85
|
+
type LockValidationDeps = {
|
|
86
|
+
processExists?: (pid: number) => boolean;
|
|
87
|
+
getProcessStartTime?: (pid: number) => string | null;
|
|
88
|
+
getProcessCommand?: (pid: number) => string | null;
|
|
89
|
+
};
|
|
90
|
+
export declare function parseD3kLockContent(content: string): D3kLockInfo | null;
|
|
91
|
+
export declare function getProcessStartTime(pid: number): string | null;
|
|
92
|
+
export declare function getProcessCommand(pid: number): string | null;
|
|
93
|
+
export declare function validateD3kLockContent(content: string, deps?: LockValidationDeps): LockValidationResult;
|
|
72
94
|
/**
|
|
73
95
|
* Check if a server is actually listening and responding on a port.
|
|
74
96
|
* Used for waiting for a dev server to start up.
|
|
@@ -94,7 +116,7 @@ export declare function createPersistentLogFile(): string;
|
|
|
94
116
|
* processes belong to THIS d3k instance so we don't accidentally kill
|
|
95
117
|
* Chrome instances from other d3k sessions.
|
|
96
118
|
*/
|
|
97
|
-
export declare function writeSessionInfo(projectName: string, logFilePath: string, appPort: string, publicUrl?: string | null, cdpUrl?: string | null, chromePids?: number[], serverCommand?: string, framework?: "nextjs" | "svelte" | "other", serverPid?: number, skillsInstalled?: string[], skillsAgentId?: string | null, preferredBrowserTool?: "agent-browser" | "next-browser"): void;
|
|
119
|
+
export declare function writeSessionInfo(projectName: string, logFilePath: string, appPort: string, publicUrl?: string | null, cdpUrl?: string | null, chromePids?: number[], serverCommand?: string, framework?: "nextjs" | "svelte" | "other", serverPid?: number, skillsInstalled?: string[], skillsAgentId?: string | null, preferredBrowserTool?: "agent-browser" | "next-browser", agentName?: string | null): void;
|
|
98
120
|
export declare function getSessionChromePids(projectName: string): number[];
|
|
99
121
|
export declare class DevEnvironment {
|
|
100
122
|
private serverProcess;
|
|
@@ -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,7 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { execSync, spawn, spawnSync } 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
|
+
import { createConnection, createServer } from "net";
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import { homedir, tmpdir } from "os";
|
|
8
8
|
import { dirname, join, resolve, sep } from "path";
|
|
@@ -14,6 +14,7 @@ import { ScreencastManager } from "./screencast-manager.js";
|
|
|
14
14
|
import { NextJsErrorDetector, OutputProcessor, StandardLogParser } from "./services/parsers/index.js";
|
|
15
15
|
import { getBundledSkillsPath, listAvailableSkills } from "./skills/index.js";
|
|
16
16
|
import { DevTUI } from "./tui-interface.js";
|
|
17
|
+
import { readProjectAgentName, rememberProjectAgentName } from "./utils/project-metadata.js";
|
|
17
18
|
import { getProjectDir, getProjectDisplayName, getProjectName } from "./utils/project-name.js";
|
|
18
19
|
import { getApplicablePackages, getSkillsPathForLocation, installSkillPackage, isPackageInstalled } from "./utils/skill-installer.js";
|
|
19
20
|
import { formatTimestamp } from "./utils/timestamp.js";
|
|
@@ -163,6 +164,115 @@ export function countActiveD3kInstances(excludeCurrentPid = false) {
|
|
|
163
164
|
return excludeCurrentPid ? 0 : 1;
|
|
164
165
|
}
|
|
165
166
|
}
|
|
167
|
+
export function parseD3kLockContent(content) {
|
|
168
|
+
const trimmed = content.trim();
|
|
169
|
+
if (!trimmed)
|
|
170
|
+
return null;
|
|
171
|
+
if (/^\d+$/.test(trimmed)) {
|
|
172
|
+
const pid = Number.parseInt(trimmed, 10);
|
|
173
|
+
return Number.isInteger(pid) && pid > 0 ? { pid } : null;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(trimmed);
|
|
177
|
+
const pid = parsed?.pid;
|
|
178
|
+
if (!parsed || typeof parsed !== "object" || typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
pid,
|
|
183
|
+
cwd: typeof parsed.cwd === "string" ? parsed.cwd : null,
|
|
184
|
+
createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : null,
|
|
185
|
+
processStartTime: typeof parsed.processStartTime === "string" ? parsed.processStartTime : null
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function getProcessPsField(pid, field) {
|
|
193
|
+
try {
|
|
194
|
+
const result = spawnSync("ps", ["-p", String(pid), "-o", `${field}=`], {
|
|
195
|
+
encoding: "utf8",
|
|
196
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
197
|
+
});
|
|
198
|
+
if (result.status !== 0)
|
|
199
|
+
return null;
|
|
200
|
+
const output = result.stdout.trim();
|
|
201
|
+
return output || null;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export function getProcessStartTime(pid) {
|
|
208
|
+
return getProcessPsField(pid, "lstart");
|
|
209
|
+
}
|
|
210
|
+
export function getProcessCommand(pid) {
|
|
211
|
+
return getProcessPsField(pid, "command");
|
|
212
|
+
}
|
|
213
|
+
function isLikelyD3kCommand(command) {
|
|
214
|
+
const normalized = command.toLowerCase();
|
|
215
|
+
return normalized.includes("d3k") || normalized.includes("dev3000");
|
|
216
|
+
}
|
|
217
|
+
export function validateD3kLockContent(content, deps = {}) {
|
|
218
|
+
const lockInfo = parseD3kLockContent(content);
|
|
219
|
+
if (!lockInfo) {
|
|
220
|
+
return {
|
|
221
|
+
active: false,
|
|
222
|
+
reason: "lock file contents are invalid",
|
|
223
|
+
lockInfo: null
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const processExists = deps.processExists ||
|
|
227
|
+
((pid) => {
|
|
228
|
+
try {
|
|
229
|
+
process.kill(pid, 0);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
if (!processExists(lockInfo.pid)) {
|
|
237
|
+
return {
|
|
238
|
+
active: false,
|
|
239
|
+
reason: `process ${lockInfo.pid} is not running`,
|
|
240
|
+
lockInfo
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const getStartTime = deps.getProcessStartTime || getProcessStartTime;
|
|
244
|
+
if (lockInfo.processStartTime) {
|
|
245
|
+
const liveStartTime = getStartTime(lockInfo.pid);
|
|
246
|
+
if (liveStartTime && liveStartTime !== lockInfo.processStartTime) {
|
|
247
|
+
return {
|
|
248
|
+
active: false,
|
|
249
|
+
reason: `pid ${lockInfo.pid} was reused by a different process`,
|
|
250
|
+
lockInfo
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (liveStartTime === lockInfo.processStartTime) {
|
|
254
|
+
return {
|
|
255
|
+
active: true,
|
|
256
|
+
reason: `process ${lockInfo.pid} matches recorded start time`,
|
|
257
|
+
lockInfo
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const getCommand = deps.getProcessCommand || getProcessCommand;
|
|
262
|
+
const liveCommand = getCommand(lockInfo.pid);
|
|
263
|
+
if (liveCommand && !isLikelyD3kCommand(liveCommand)) {
|
|
264
|
+
return {
|
|
265
|
+
active: false,
|
|
266
|
+
reason: `pid ${lockInfo.pid} belongs to a non-d3k process`,
|
|
267
|
+
lockInfo
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
active: true,
|
|
272
|
+
reason: `process ${lockInfo.pid} still owns the lock`,
|
|
273
|
+
lockInfo
|
|
274
|
+
};
|
|
275
|
+
}
|
|
166
276
|
/**
|
|
167
277
|
* Check if a port is available for binding (no process is listening on it).
|
|
168
278
|
* Used for finding available ports before starting servers.
|
|
@@ -172,6 +282,40 @@ async function isPortAvailable(port) {
|
|
|
172
282
|
if (!Number.isInteger(portNumber) || portNumber < 0 || portNumber > 65535) {
|
|
173
283
|
return false;
|
|
174
284
|
}
|
|
285
|
+
const hasActiveListener = await Promise.any(["127.0.0.1", "::1", "localhost"].map((host) => new Promise((resolve, reject) => {
|
|
286
|
+
const socket = createConnection({ host, port: portNumber });
|
|
287
|
+
let settled = false;
|
|
288
|
+
const finish = (result, shouldReject = false) => {
|
|
289
|
+
if (settled)
|
|
290
|
+
return;
|
|
291
|
+
settled = true;
|
|
292
|
+
socket.destroy();
|
|
293
|
+
if (shouldReject) {
|
|
294
|
+
reject(new Error("unreachable"));
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
resolve(result);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
socket.once("connect", () => finish(true));
|
|
301
|
+
socket.once("timeout", () => finish(false, true));
|
|
302
|
+
socket.once("error", (error) => {
|
|
303
|
+
if (error.code === "ECONNREFUSED" ||
|
|
304
|
+
error.code === "EHOSTUNREACH" ||
|
|
305
|
+
error.code === "ENETUNREACH" ||
|
|
306
|
+
error.code === "EINVAL") {
|
|
307
|
+
finish(false, true);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
finish(false);
|
|
311
|
+
});
|
|
312
|
+
socket.setTimeout(150);
|
|
313
|
+
})))
|
|
314
|
+
.then((result) => result)
|
|
315
|
+
.catch(() => false);
|
|
316
|
+
if (hasActiveListener) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
175
319
|
return new Promise((resolve) => {
|
|
176
320
|
const server = createServer();
|
|
177
321
|
server.once("error", (error) => {
|
|
@@ -358,13 +502,15 @@ export function createPersistentLogFile() {
|
|
|
358
502
|
* processes belong to THIS d3k instance so we don't accidentally kill
|
|
359
503
|
* Chrome instances from other d3k sessions.
|
|
360
504
|
*/
|
|
361
|
-
export function writeSessionInfo(projectName, logFilePath, appPort, publicUrl, cdpUrl, chromePids, serverCommand, framework, serverPid, skillsInstalled, skillsAgentId, preferredBrowserTool) {
|
|
505
|
+
export function writeSessionInfo(projectName, logFilePath, appPort, publicUrl, cdpUrl, chromePids, serverCommand, framework, serverPid, skillsInstalled, skillsAgentId, preferredBrowserTool, agentName) {
|
|
362
506
|
const projectDir = getProjectDir();
|
|
363
507
|
try {
|
|
364
508
|
// Create project directory if it doesn't exist
|
|
365
509
|
if (!existsSync(projectDir)) {
|
|
366
510
|
mkdirSync(projectDir, { recursive: true });
|
|
367
511
|
}
|
|
512
|
+
const sessionFile = join(projectDir, "session.json");
|
|
513
|
+
const persistedAgentName = readProjectAgentName();
|
|
368
514
|
// Session file contains project info
|
|
369
515
|
const sessionInfo = {
|
|
370
516
|
projectName,
|
|
@@ -381,11 +527,14 @@ export function writeSessionInfo(projectName, logFilePath, appPort, publicUrl, c
|
|
|
381
527
|
serverPid: serverPid || null,
|
|
382
528
|
skillsInstalled: skillsInstalled || [],
|
|
383
529
|
skillsAgentId: skillsAgentId || null,
|
|
384
|
-
preferredBrowserTool: preferredBrowserTool || "agent-browser"
|
|
530
|
+
preferredBrowserTool: preferredBrowserTool || "agent-browser",
|
|
531
|
+
agentName: agentName || persistedAgentName || null
|
|
385
532
|
};
|
|
386
533
|
// Write session file in project directory
|
|
387
|
-
const sessionFile = join(projectDir, "session.json");
|
|
388
534
|
writeFileSync(sessionFile, JSON.stringify(sessionInfo, null, 2));
|
|
535
|
+
if (sessionInfo.agentName) {
|
|
536
|
+
rememberProjectAgentName(sessionInfo.agentName);
|
|
537
|
+
}
|
|
389
538
|
}
|
|
390
539
|
catch (error) {
|
|
391
540
|
// Non-fatal - just log a warning
|
|
@@ -1189,21 +1338,21 @@ export class DevEnvironment {
|
|
|
1189
1338
|
// Check if lock file exists
|
|
1190
1339
|
if (existsSync(this.lockFile)) {
|
|
1191
1340
|
const lockContent = readFileSync(this.lockFile, "utf8");
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
process.kill(oldPID, 0); // Signal 0 just checks if process exists
|
|
1196
|
-
// Process is running, lock is valid
|
|
1341
|
+
const validation = validateD3kLockContent(lockContent);
|
|
1342
|
+
if (validation.active) {
|
|
1343
|
+
this.debugLog(`Lock file is active: ${validation.reason}`);
|
|
1197
1344
|
return false;
|
|
1198
1345
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
this.debugLog(`Removing stale lock file for PID ${oldPID}`);
|
|
1202
|
-
unlinkSync(this.lockFile);
|
|
1203
|
-
}
|
|
1346
|
+
this.debugLog(`Removing stale lock file: ${validation.reason}`);
|
|
1347
|
+
unlinkSync(this.lockFile);
|
|
1204
1348
|
}
|
|
1205
|
-
//
|
|
1206
|
-
writeFileSync(this.lockFile,
|
|
1349
|
+
// Store enough metadata to detect PID reuse after crashes.
|
|
1350
|
+
writeFileSync(this.lockFile, JSON.stringify({
|
|
1351
|
+
pid: process.pid,
|
|
1352
|
+
cwd: process.cwd(),
|
|
1353
|
+
createdAt: new Date().toISOString(),
|
|
1354
|
+
processStartTime: getProcessStartTime(process.pid)
|
|
1355
|
+
}));
|
|
1207
1356
|
this.debugLog(`Acquired lock file: ${this.lockFile}`);
|
|
1208
1357
|
return true;
|
|
1209
1358
|
}
|
|
@@ -1247,7 +1396,7 @@ export class DevEnvironment {
|
|
|
1247
1396
|
const cdpUrl = this.cdpMonitor?.getCdpUrl() || null;
|
|
1248
1397
|
const chromePids = this.cdpMonitor?.getChromePids() || [];
|
|
1249
1398
|
const skillsInstalled = listAvailableSkills(process.cwd());
|
|
1250
|
-
writeSessionInfo(projectName, this.options.logFile, this.options.port, this.publicAppUrl, cdpUrl, chromePids, this.options.serverCommand, this.options.framework, this.serverProcess?.pid, skillsInstalled, this.options.skillsAgentId ?? null, this.options.browserTool);
|
|
1399
|
+
writeSessionInfo(projectName, this.options.logFile, this.options.port, this.publicAppUrl, cdpUrl, chromePids, this.options.serverCommand, this.options.framework, this.serverProcess?.pid, skillsInstalled, this.options.skillsAgentId ?? null, this.options.browserTool, this.options.agentName ?? null);
|
|
1251
1400
|
}
|
|
1252
1401
|
detectPortChange(text) {
|
|
1253
1402
|
// Detect Next.js port switch: "⚠ Port 3000 is in use by process 39543, using available port 3001 instead."
|
|
@@ -1561,7 +1710,7 @@ export class DevEnvironment {
|
|
|
1561
1710
|
this.cdpMonitor = new CDPMonitor(this.options.profileDir, this.screenshotDir, (_source, message) => {
|
|
1562
1711
|
this.logger.log("browser", message);
|
|
1563
1712
|
}, this.options.debug, this.options.browser, this.options.pluginReactScan, this.options.port, // App server port to monitor
|
|
1564
|
-
this.preferredAppUrl, this.options.debugPort, // Chrome debug port
|
|
1713
|
+
this.preferredAppUrl, this.options.browserNavigationTimeoutSeconds * 1000, this.options.debugPort, // Chrome debug port
|
|
1565
1714
|
this.options.headless, // Headless mode for serverless/CI environments
|
|
1566
1715
|
this.options.framework // Framework hint for optional React DevTools launch args
|
|
1567
1716
|
);
|
|
@@ -1594,9 +1743,7 @@ export class DevEnvironment {
|
|
|
1594
1743
|
this.writeCurrentSessionInfo(projectName);
|
|
1595
1744
|
this.debugLog(`Updated session info with CDP URL: ${cdpUrl}, Chrome PIDs: [${chromePids.join(", ")}]`);
|
|
1596
1745
|
this.logger.log("browser", `[CDP] Session info written with cdpUrl: ${cdpUrl ? "available" : "null"}`);
|
|
1597
|
-
|
|
1598
|
-
await this.cdpMonitor.navigateToUrl(this.preferredAppUrl);
|
|
1599
|
-
this.logger.log("browser", `[CDP] Navigated to ${this.preferredAppUrl}`);
|
|
1746
|
+
this.logger.log("browser", `[CDP] Loading page will hand off to ${this.preferredAppUrl}`);
|
|
1600
1747
|
}
|
|
1601
1748
|
catch (error) {
|
|
1602
1749
|
// Log error and throw to trigger graceful shutdown
|
|
@@ -1932,12 +2079,14 @@ export class DevEnvironment {
|
|
|
1932
2079
|
console.log(chalk.yellow("\n🛑 Received interrupt signal. Cleaning up processes..."));
|
|
1933
2080
|
}
|
|
1934
2081
|
// Shutdown CDP monitor FIRST - this should close Chrome
|
|
2082
|
+
let chromeShutdownHandled = false;
|
|
1935
2083
|
if (this.cdpMonitor) {
|
|
1936
2084
|
try {
|
|
1937
2085
|
if (!this.options.tui) {
|
|
1938
2086
|
console.log(chalk.cyan("🔄 Closing Chrome browser..."));
|
|
1939
2087
|
}
|
|
1940
2088
|
await this.cdpMonitor.shutdown();
|
|
2089
|
+
chromeShutdownHandled = true;
|
|
1941
2090
|
if (!this.options.tui) {
|
|
1942
2091
|
console.log(chalk.green("✅ Chrome browser closed"));
|
|
1943
2092
|
}
|
|
@@ -1973,8 +2122,10 @@ export class DevEnvironment {
|
|
|
1973
2122
|
}
|
|
1974
2123
|
}
|
|
1975
2124
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
2125
|
+
if (!chromeShutdownHandled) {
|
|
2126
|
+
// Safety net: ensure tracked Chrome processes are always terminated on shutdown.
|
|
2127
|
+
this.killTrackedChromePids(sessionInfo?.chromePids ?? [], "handleShutdown");
|
|
2128
|
+
}
|
|
1978
2129
|
// REMOVED: No longer clean up CLI config files on shutdown
|
|
1979
2130
|
// This was causing Claude Code instances to crash when dev3000 was killed
|
|
1980
2131
|
// Config file cleanup removed; keep user config files untouched on shutdown
|