dev3000 0.0.151 → 0.0.154
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/README.md +121 -103
- package/bin/dev3000 +10 -5
- package/dist/cdp-monitor.d.ts.map +1 -1
- package/dist/cdp-monitor.js +2 -3
- package/dist/cdp-monitor.js.map +1 -1
- package/dist/cli.js +301 -75
- package/dist/cli.js.map +1 -1
- package/dist/commands/cloud-check-pr.d.ts +1 -0
- package/dist/commands/cloud-check-pr.d.ts.map +1 -1
- package/dist/commands/cloud-check-pr.js +18 -6
- package/dist/commands/cloud-check-pr.js.map +1 -1
- package/dist/commands/crawl.d.ts +12 -0
- package/dist/commands/crawl.d.ts.map +1 -0
- package/dist/commands/crawl.js +140 -0
- package/dist/commands/crawl.js.map +1 -0
- package/dist/commands/errors.d.ts +14 -0
- package/dist/commands/errors.d.ts.map +1 -0
- package/dist/commands/errors.js +295 -0
- package/dist/commands/errors.js.map +1 -0
- package/dist/commands/find-component.d.ts +8 -0
- package/dist/commands/find-component.d.ts.map +1 -0
- package/dist/commands/find-component.js +182 -0
- package/dist/commands/find-component.js.map +1 -0
- package/dist/commands/fix.d.ts +13 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +288 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/logs.d.ts +13 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +195 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/restart.d.ts +8 -0
- package/dist/commands/restart.d.ts.map +1 -0
- package/dist/commands/restart.js +92 -0
- package/dist/commands/restart.js.map +1 -0
- package/dist/components/PackageSelector.d.ts +12 -0
- package/dist/components/PackageSelector.d.ts.map +1 -0
- package/dist/components/PackageSelector.js +74 -0
- package/dist/components/PackageSelector.js.map +1 -0
- package/dist/dev-environment.d.ts +0 -7
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +108 -674
- package/dist/dev-environment.js.map +1 -1
- package/dist/screencast-manager.d.ts.map +1 -1
- package/dist/screencast-manager.js +7 -8
- package/dist/screencast-manager.js.map +1 -1
- package/dist/services/parsers/output-processor.js +2 -2
- package/dist/services/parsers/output-processor.js.map +1 -1
- package/dist/skills/d3k/SKILL.md +38 -27
- package/dist/skills/index.d.ts +10 -14
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +47 -77
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/index.test.ts +13 -55
- package/dist/skills/index.ts +48 -81
- package/dist/src/tui-interface-impl.tsx +6 -15
- package/dist/tui-interface-impl.d.ts.map +1 -1
- package/dist/tui-interface-impl.js +4 -4
- package/dist/tui-interface-impl.js.map +1 -1
- package/dist/tui-interface-opentui.d.ts.map +1 -1
- package/dist/tui-interface-opentui.js +227 -100
- package/dist/tui-interface-opentui.js.map +1 -1
- package/dist/utils/agent-browser.d.ts +2 -0
- package/dist/utils/agent-browser.d.ts.map +1 -1
- package/dist/utils/agent-browser.js +46 -8
- package/dist/utils/agent-browser.js.map +1 -1
- package/dist/utils/agent-selection.d.ts.map +1 -1
- package/dist/utils/agent-selection.js +9 -2
- package/dist/utils/agent-selection.js.map +1 -1
- package/dist/utils/skill-installer.d.ts +55 -29
- package/dist/utils/skill-installer.d.ts.map +1 -1
- package/dist/utils/skill-installer.js +118 -229
- package/dist/utils/skill-installer.js.map +1 -1
- package/dist/utils/tmux-helpers.d.ts +1 -2
- package/dist/utils/tmux-helpers.d.ts.map +1 -1
- package/dist/utils/tmux-helpers.js +17 -18
- package/dist/utils/tmux-helpers.js.map +1 -1
- package/dist/utils/version-check.d.ts +2 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +9 -0
- package/dist/utils/version-check.js.map +1 -1
- package/mcp-server/.next/BUILD_ID +1 -1
- package/mcp-server/.next/build-manifest.json +2 -2
- package/mcp-server/.next/fallback-build-manifest.json +2 -2
- package/mcp-server/.next/prerender-manifest.json +3 -3
- package/mcp-server/.next/server/app/_global-error/page.js +1 -1
- package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/_global-error.html +2 -2
- package/mcp-server/.next/server/app/_global-error.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found/page.js +1 -1
- package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/_not-found.html +1 -1
- package/mcp-server/.next/server/app/_not-found.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/api/screenshots/capture/route.js +2 -2
- package/mcp-server/.next/server/app/api/screenshots/capture/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/index.html +1 -1
- package/mcp-server/.next/server/app/index.rsc +2 -2
- package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/mcp-server/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/logs/page.js +1 -1
- package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/mcp/route.js +4 -4
- package/mcp-server/.next/server/app/mcp/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/page.js +1 -1
- package/mcp-server/.next/server/app/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/page_client-reference-manifest.js +1 -1
- package/mcp-server/.next/server/app/video/[session]/page.js +1 -1
- package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js +3 -3
- package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__69e6dfb7._.js +3 -0
- package/mcp-server/.next/server/chunks/{[root-of-the-server]__444592aa._.js.map → [root-of-the-server]__69e6dfb7._.js.map} +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__6baff21e._.js +4 -0
- package/mcp-server/.next/server/chunks/{[root-of-the-server]__130a5f58._.js.map → [root-of-the-server]__6baff21e._.js.map} +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__c8cf5b23._.js +3 -0
- package/mcp-server/.next/server/chunks/[root-of-the-server]__e6a83e60._.js +4 -0
- package/mcp-server/.next/server/chunks/{[root-of-the-server]__b71c83ed._.js.map → [root-of-the-server]__e6a83e60._.js.map} +1 -1
- package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js +32 -66
- package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js.map +1 -1
- package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js +1 -1
- package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js.map +1 -1
- package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js +3 -0
- package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js.map +1 -0
- package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__f66148e5._.js → [root-of-the-server]__b17d4048._.js} +2 -2
- package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__50eb2eba._.js → [root-of-the-server]__dcf84f77._.js} +2 -2
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js +1 -1
- package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js.map +1 -1
- package/mcp-server/.next/server/pages/404.html +1 -1
- package/mcp-server/.next/server/pages/500.html +2 -2
- package/mcp-server/.next/server/server-reference-manifest.js +1 -1
- package/mcp-server/.next/server/server-reference-manifest.json +1 -1
- package/mcp-server/.next/static/chunks/{2422ea9ed874427b.js → 3f3f8e7d16ba3bf4.js} +1 -1
- package/mcp-server/app/api/tools/route.ts +5 -4
- package/mcp-server/app/mcp/route.ts +8 -63
- package/mcp-server/app/mcp/tools.ts +71 -445
- package/mcp-server/app/page.tsx +1 -1
- package/mcp-server/package.json +2 -0
- package/package.json +6 -5
- package/src/tui-interface-impl.tsx +6 -15
- package/dist/components/SkillSelector.d.ts +0 -10
- package/dist/components/SkillSelector.d.ts.map +0 -1
- package/dist/components/SkillSelector.js +0 -87
- package/dist/components/SkillSelector.js.map +0 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__130a5f58._.js +0 -4
- package/mcp-server/.next/server/chunks/[root-of-the-server]__444592aa._.js +0 -3
- package/mcp-server/.next/server/chunks/[root-of-the-server]__8f84b4cc._.js +0 -3
- package/mcp-server/.next/server/chunks/[root-of-the-server]__b71c83ed._.js +0 -4
- /package/mcp-server/.next/server/chunks/{[root-of-the-server]__8f84b4cc._.js.map → [root-of-the-server]__c8cf5b23._.js.map} +0 -0
- /package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__f66148e5._.js.map → [root-of-the-server]__b17d4048._.js.map} +0 -0
- /package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__50eb2eba._.js.map → [root-of-the-server]__dcf84f77._.js.map} +0 -0
- /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_buildManifest.js +0 -0
- /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_clientMiddlewareManifest.json +0 -0
- /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_ssgManifest.js +0 -0
package/dist/dev-environment.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
|
-
import { appendFileSync, copyFileSync,
|
|
3
|
+
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
4
4
|
import https from "https";
|
|
5
5
|
import ora from "ora";
|
|
6
6
|
import { homedir, tmpdir } from "os";
|
|
@@ -9,6 +9,7 @@ import { fileURLToPath } from "url";
|
|
|
9
9
|
import { CDPMonitor } from "./cdp-monitor.js";
|
|
10
10
|
import { ScreencastManager } from "./screencast-manager.js";
|
|
11
11
|
import { NextJsErrorDetector, OutputProcessor, StandardLogParser } from "./services/parsers/index.js";
|
|
12
|
+
import { getBundledSkillsPath } from "./skills/index.js";
|
|
12
13
|
import { DevTUI } from "./tui-interface.js";
|
|
13
14
|
import { formatMcpConfigTargets, MCP_CONFIG_TARGETS } from "./utils/mcp-configs.js";
|
|
14
15
|
import { getProjectDir, getProjectDisplayName, getProjectName } from "./utils/project-name.js";
|
|
@@ -21,8 +22,9 @@ const MCP_NAMES = {
|
|
|
21
22
|
NEXTJS_DEV: "dev3000-nextjs-dev",
|
|
22
23
|
VERCEL: "vercel"
|
|
23
24
|
};
|
|
24
|
-
// Vercel MCP URL (public OAuth-based MCP)
|
|
25
|
-
|
|
25
|
+
// Vercel MCP URL (public OAuth-based MCP) - kept for potential future use
|
|
26
|
+
// @ts-expect-error Unused but kept for reference
|
|
27
|
+
const _VERCEL_MCP_URL = "https://mcp.vercel.com";
|
|
26
28
|
/**
|
|
27
29
|
* Patterns for identifying orphaned MCP-related processes to clean up on startup.
|
|
28
30
|
*
|
|
@@ -38,8 +40,10 @@ export const ORPHANED_PROCESS_CLEANUP_PATTERNS = [
|
|
|
38
40
|
];
|
|
39
41
|
/**
|
|
40
42
|
* Check if the current project has a .vercel directory (indicating a Vercel project)
|
|
43
|
+
* Kept for potential future use
|
|
41
44
|
*/
|
|
42
|
-
|
|
45
|
+
// @ts-expect-error Unused but kept for potential future use
|
|
46
|
+
function _hasVercelProject() {
|
|
43
47
|
return existsSync(join(process.cwd(), ".vercel"));
|
|
44
48
|
}
|
|
45
49
|
/**
|
|
@@ -128,17 +132,6 @@ class Logger {
|
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
|
-
function detectPackageManagerForRun() {
|
|
132
|
-
if (existsSync("bun.lockb"))
|
|
133
|
-
return "bun";
|
|
134
|
-
if (existsSync("pnpm-lock.yaml"))
|
|
135
|
-
return "pnpm";
|
|
136
|
-
if (existsSync("yarn.lock"))
|
|
137
|
-
return "yarn";
|
|
138
|
-
if (existsSync("package-lock.json"))
|
|
139
|
-
return "npm";
|
|
140
|
-
return "npm"; // fallback
|
|
141
|
-
}
|
|
142
135
|
/**
|
|
143
136
|
* Detect if we're in a sandbox environment (Vercel Sandbox, Docker, etc.)
|
|
144
137
|
* where lsof and other system utilities may not be available.
|
|
@@ -171,7 +164,7 @@ export function countActiveD3kInstances(excludeCurrentPid = false) {
|
|
|
171
164
|
const pidPath = join(tmpDir, pidFile);
|
|
172
165
|
const pidStr = readFileSync(pidPath, "utf-8").trim();
|
|
173
166
|
const pid = parseInt(pidStr, 10);
|
|
174
|
-
if (isNaN(pid))
|
|
167
|
+
if (Number.isNaN(pid))
|
|
175
168
|
continue;
|
|
176
169
|
// Skip current process if requested
|
|
177
170
|
if (excludeCurrentPid && pid === process.pid)
|
|
@@ -465,47 +458,28 @@ async function isChromeDevtoolsMcpSupported() {
|
|
|
465
458
|
}
|
|
466
459
|
}
|
|
467
460
|
/**
|
|
468
|
-
*
|
|
461
|
+
* Clean up old dev3000 MCP entries from project's .mcp.json (Claude Code)
|
|
462
|
+
* MCP server has been removed - we now use CLI commands instead
|
|
469
463
|
*/
|
|
470
|
-
async function ensureMcpServers(
|
|
464
|
+
async function ensureMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
|
|
471
465
|
try {
|
|
472
466
|
const settingsPath = join(process.cwd(), ".mcp.json");
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (existsSync(settingsPath)) {
|
|
476
|
-
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
477
|
-
settings = JSON.parse(settingsContent);
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
settings = {};
|
|
467
|
+
if (!existsSync(settingsPath)) {
|
|
468
|
+
return; // Nothing to clean up
|
|
481
469
|
}
|
|
482
|
-
|
|
470
|
+
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
471
|
+
const settings = JSON.parse(settingsContent);
|
|
483
472
|
if (!settings.mcpServers) {
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
let
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
url: `http://localhost:${mcpPort}/mcp`
|
|
495
|
-
};
|
|
496
|
-
added = true;
|
|
497
|
-
}
|
|
498
|
-
// Add Vercel MCP if this is a Vercel project (.vercel directory exists)
|
|
499
|
-
// Vercel MCP uses OAuth authentication handled by the client (Claude Code)
|
|
500
|
-
if (hasVercelProject() && !settings.mcpServers[MCP_NAMES.VERCEL]) {
|
|
501
|
-
settings.mcpServers[MCP_NAMES.VERCEL] = {
|
|
502
|
-
type: "http",
|
|
503
|
-
url: VERCEL_MCP_URL
|
|
504
|
-
};
|
|
505
|
-
added = true;
|
|
506
|
-
}
|
|
507
|
-
// Write if we added anything
|
|
508
|
-
if (added) {
|
|
473
|
+
return; // Nothing to clean up
|
|
474
|
+
}
|
|
475
|
+
let removed = false;
|
|
476
|
+
// Remove dev3000 MCP entry if it exists (MCP server removed)
|
|
477
|
+
if (settings.mcpServers[MCP_NAMES.DEV3000]) {
|
|
478
|
+
delete settings.mcpServers[MCP_NAMES.DEV3000];
|
|
479
|
+
removed = true;
|
|
480
|
+
}
|
|
481
|
+
// Write if we removed anything
|
|
482
|
+
if (removed) {
|
|
509
483
|
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
510
484
|
}
|
|
511
485
|
}
|
|
@@ -514,51 +488,29 @@ async function ensureMcpServers(mcpPort, _appPort, _enableChromeDevtools) {
|
|
|
514
488
|
}
|
|
515
489
|
}
|
|
516
490
|
/**
|
|
517
|
-
*
|
|
491
|
+
* Clean up old dev3000 MCP entries from project's .cursor/mcp.json
|
|
492
|
+
* MCP server has been removed - we now use CLI commands instead
|
|
518
493
|
*/
|
|
519
|
-
async function ensureCursorMcpServers(
|
|
494
|
+
async function ensureCursorMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
|
|
520
495
|
try {
|
|
521
496
|
const cursorDir = join(process.cwd(), ".cursor");
|
|
522
497
|
const settingsPath = join(cursorDir, "mcp.json");
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
mkdirSync(cursorDir, { recursive: true });
|
|
498
|
+
if (!existsSync(settingsPath)) {
|
|
499
|
+
return; // Nothing to clean up
|
|
526
500
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (
|
|
530
|
-
|
|
531
|
-
settings = JSON.parse(settingsContent);
|
|
501
|
+
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
502
|
+
const settings = JSON.parse(settingsContent);
|
|
503
|
+
if (!settings.mcpServers) {
|
|
504
|
+
return; // Nothing to clean up
|
|
532
505
|
}
|
|
533
|
-
|
|
534
|
-
|
|
506
|
+
let removed = false;
|
|
507
|
+
// Remove dev3000 MCP entry if it exists (MCP server removed)
|
|
508
|
+
if (settings.mcpServers[MCP_NAMES.DEV3000]) {
|
|
509
|
+
delete settings.mcpServers[MCP_NAMES.DEV3000];
|
|
510
|
+
removed = true;
|
|
535
511
|
}
|
|
536
|
-
//
|
|
537
|
-
if (
|
|
538
|
-
settings.mcpServers = {};
|
|
539
|
-
}
|
|
540
|
-
let added = false;
|
|
541
|
-
// Add dev3000 MCP server
|
|
542
|
-
// NOTE: dev3000 now acts as an MCP orchestrator/gateway that internally
|
|
543
|
-
// spawns and connects to chrome-devtools-mcp and next-devtools-mcp as stdio processes
|
|
544
|
-
if (!settings.mcpServers[MCP_NAMES.DEV3000]) {
|
|
545
|
-
settings.mcpServers[MCP_NAMES.DEV3000] = {
|
|
546
|
-
type: "http",
|
|
547
|
-
url: `http://localhost:${mcpPort}/mcp`
|
|
548
|
-
};
|
|
549
|
-
added = true;
|
|
550
|
-
}
|
|
551
|
-
// Add Vercel MCP if this is a Vercel project (.vercel directory exists)
|
|
552
|
-
// Vercel MCP uses OAuth authentication handled by the client (Cursor)
|
|
553
|
-
if (hasVercelProject() && !settings.mcpServers[MCP_NAMES.VERCEL]) {
|
|
554
|
-
settings.mcpServers[MCP_NAMES.VERCEL] = {
|
|
555
|
-
type: "http",
|
|
556
|
-
url: VERCEL_MCP_URL
|
|
557
|
-
};
|
|
558
|
-
added = true;
|
|
559
|
-
}
|
|
560
|
-
// Write if we added anything
|
|
561
|
-
if (added) {
|
|
512
|
+
// Write if we removed anything
|
|
513
|
+
if (removed) {
|
|
562
514
|
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
563
515
|
}
|
|
564
516
|
}
|
|
@@ -567,66 +519,29 @@ async function ensureCursorMcpServers(mcpPort, _appPort, _enableChromeDevtools)
|
|
|
567
519
|
}
|
|
568
520
|
}
|
|
569
521
|
/**
|
|
570
|
-
*
|
|
571
|
-
* OpenCode uses
|
|
572
|
-
*
|
|
573
|
-
* IMPORTANT: OpenCode has issues with "type": "remote" for HTTP MCP servers.
|
|
574
|
-
* The workaround is to use "type": "local" with mcp-remote package to proxy requests.
|
|
575
|
-
* See: https://github.com/sst/opencode/issues/1595
|
|
522
|
+
* Clean up old dev3000 MCP entries from project's opencode.json
|
|
523
|
+
* OpenCode uses "mcp" instead of "mcpServers"
|
|
524
|
+
* MCP server has been removed - we now use CLI commands instead
|
|
576
525
|
*/
|
|
577
|
-
async function ensureOpenCodeMcpServers(
|
|
526
|
+
async function ensureOpenCodeMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
|
|
578
527
|
try {
|
|
579
528
|
const settingsPath = join(process.cwd(), "opencode.json");
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (existsSync(settingsPath)) {
|
|
583
|
-
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
584
|
-
settings = JSON.parse(settingsContent);
|
|
529
|
+
if (!existsSync(settingsPath)) {
|
|
530
|
+
return; // Nothing to clean up
|
|
585
531
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
// Ensure mcp structure exists
|
|
532
|
+
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
533
|
+
const settings = JSON.parse(settingsContent);
|
|
590
534
|
if (!settings.mcp) {
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
let
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const currentDev3000 = settings.mcp[MCP_NAMES.DEV3000];
|
|
602
|
-
if (!currentDev3000 ||
|
|
603
|
-
currentDev3000.type !== expectedDev3000Config.type ||
|
|
604
|
-
currentDev3000.url !== expectedDev3000Config.url) {
|
|
605
|
-
settings.mcp[MCP_NAMES.DEV3000] = expectedDev3000Config;
|
|
606
|
-
changed = true;
|
|
607
|
-
}
|
|
608
|
-
// Always update Vercel MCP if this is a Vercel project (.vercel directory exists)
|
|
609
|
-
// Vercel MCP requires OAuth, so use OpenCode's native remote type with oauth: {}
|
|
610
|
-
// This triggers OpenCode's built-in OAuth flow instead of mcp-remote
|
|
611
|
-
// See: https://github.com/sst/opencode/issues/5444
|
|
612
|
-
if (hasVercelProject()) {
|
|
613
|
-
const expectedVercelConfig = {
|
|
614
|
-
type: "remote",
|
|
615
|
-
url: VERCEL_MCP_URL,
|
|
616
|
-
oauth: {},
|
|
617
|
-
enabled: true
|
|
618
|
-
};
|
|
619
|
-
const currentVercel = settings.mcp[MCP_NAMES.VERCEL];
|
|
620
|
-
if (!currentVercel ||
|
|
621
|
-
currentVercel.type !== expectedVercelConfig.type ||
|
|
622
|
-
currentVercel.url !== expectedVercelConfig.url ||
|
|
623
|
-
!currentVercel.oauth) {
|
|
624
|
-
settings.mcp[MCP_NAMES.VERCEL] = expectedVercelConfig;
|
|
625
|
-
changed = true;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
// Write if we changed anything
|
|
629
|
-
if (changed) {
|
|
535
|
+
return; // Nothing to clean up
|
|
536
|
+
}
|
|
537
|
+
let removed = false;
|
|
538
|
+
// Remove dev3000 MCP entry if it exists (MCP server removed)
|
|
539
|
+
if (settings.mcp[MCP_NAMES.DEV3000]) {
|
|
540
|
+
delete settings.mcp[MCP_NAMES.DEV3000];
|
|
541
|
+
removed = true;
|
|
542
|
+
}
|
|
543
|
+
// Write if we removed anything
|
|
544
|
+
if (removed) {
|
|
630
545
|
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
631
546
|
}
|
|
632
547
|
}
|
|
@@ -636,29 +551,31 @@ async function ensureOpenCodeMcpServers(mcpPort, _appPort, _enableChromeDevtools
|
|
|
636
551
|
}
|
|
637
552
|
/**
|
|
638
553
|
* Ensure d3k skill is installed in project's .claude/skills/d3k/
|
|
639
|
-
*
|
|
554
|
+
* Claude Code reads from .claude/skills/ (must be real files, not symlinks)
|
|
640
555
|
*/
|
|
641
556
|
async function ensureD3kSkill() {
|
|
642
557
|
try {
|
|
558
|
+
const bundledSkillsDir = getBundledSkillsPath();
|
|
559
|
+
if (!bundledSkillsDir)
|
|
560
|
+
return;
|
|
561
|
+
const bundledSkillPath = join(bundledSkillsDir, "d3k", "SKILL.md");
|
|
562
|
+
if (!existsSync(bundledSkillPath))
|
|
563
|
+
return;
|
|
564
|
+
// Install directly to .claude/skills/d3k/ (where Claude Code looks)
|
|
643
565
|
const skillDir = join(process.cwd(), ".claude", "skills", "d3k");
|
|
644
566
|
const skillPath = join(skillDir, "SKILL.md");
|
|
645
|
-
//
|
|
567
|
+
// Check if already up to date
|
|
568
|
+
const bundledContent = readFileSync(bundledSkillPath, "utf-8");
|
|
646
569
|
if (existsSync(skillPath)) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const __dirname = dirname(__filename);
|
|
652
|
-
const bundledSkillPath = join(__dirname, "skills", "d3k", "SKILL.md");
|
|
653
|
-
// Check if bundled skill exists
|
|
654
|
-
if (!existsSync(bundledSkillPath)) {
|
|
655
|
-
return; // Skill not bundled, skip silently
|
|
570
|
+
const existingContent = readFileSync(skillPath, "utf-8");
|
|
571
|
+
if (existingContent === bundledContent) {
|
|
572
|
+
return; // Already up to date
|
|
573
|
+
}
|
|
656
574
|
}
|
|
657
|
-
//
|
|
575
|
+
// Copy skill to .claude/skills/d3k/
|
|
658
576
|
if (!existsSync(skillDir)) {
|
|
659
577
|
mkdirSync(skillDir, { recursive: true });
|
|
660
578
|
}
|
|
661
|
-
// Copy skill file to project
|
|
662
579
|
copyFileSync(bundledSkillPath, skillPath);
|
|
663
580
|
}
|
|
664
581
|
catch (_error) {
|
|
@@ -819,14 +736,12 @@ function pruneOldLogs(baseDir) {
|
|
|
819
736
|
}
|
|
820
737
|
export class DevEnvironment {
|
|
821
738
|
serverProcess = null;
|
|
822
|
-
mcpServerProcess = null;
|
|
823
739
|
cdpMonitor = null;
|
|
824
740
|
screencastManager = null;
|
|
825
741
|
logger;
|
|
826
742
|
outputProcessor;
|
|
827
743
|
options;
|
|
828
744
|
screenshotDir;
|
|
829
|
-
mcpPublicDir;
|
|
830
745
|
pidFile;
|
|
831
746
|
lockFile;
|
|
832
747
|
spinner;
|
|
@@ -868,14 +783,12 @@ export class DevEnvironment {
|
|
|
868
783
|
const currentFile = fileURLToPath(import.meta.url);
|
|
869
784
|
packageRoot = dirname(dirname(currentFile));
|
|
870
785
|
}
|
|
871
|
-
//
|
|
872
|
-
// and avoid permission issues with /var/log paths
|
|
873
|
-
this.screenshotDir = join(packageRoot, "mcp-server", "public", "screenshots");
|
|
874
|
-
// Use project-specific PID and lock files to allow multiple projects to run simultaneously
|
|
786
|
+
// Store screenshots in project-specific directory for local access
|
|
875
787
|
const projectName = getProjectName();
|
|
788
|
+
this.screenshotDir = join(getProjectDir(), "screenshots");
|
|
789
|
+
// Use project-specific PID and lock files to allow multiple projects to run simultaneously
|
|
876
790
|
this.pidFile = join(tmpdir(), `dev3000-${projectName}.pid`);
|
|
877
791
|
this.lockFile = join(tmpdir(), `dev3000-${projectName}.lock`);
|
|
878
|
-
this.mcpPublicDir = join(packageRoot, "mcp-server", "public", "screenshots");
|
|
879
792
|
// Read version - for compiled binaries, use injected version; otherwise read from package.json
|
|
880
793
|
this.version = "0.0.0";
|
|
881
794
|
// Check for compile-time injected version first
|
|
@@ -912,12 +825,18 @@ export class DevEnvironment {
|
|
|
912
825
|
spinner: "dots",
|
|
913
826
|
isEnabled: !options.tui // Disable spinner in TUI mode
|
|
914
827
|
});
|
|
915
|
-
// Ensure
|
|
916
|
-
|
|
917
|
-
|
|
828
|
+
// Ensure screenshot directory exists
|
|
829
|
+
try {
|
|
830
|
+
if (!existsSync(this.screenshotDir)) {
|
|
831
|
+
mkdirSync(this.screenshotDir, { recursive: true });
|
|
832
|
+
}
|
|
918
833
|
}
|
|
919
|
-
|
|
920
|
-
|
|
834
|
+
catch {
|
|
835
|
+
// Fall back to temp directory if project dir isn't writable
|
|
836
|
+
this.screenshotDir = join(tmpdir(), "d3k-screenshots");
|
|
837
|
+
if (!existsSync(this.screenshotDir)) {
|
|
838
|
+
mkdirSync(this.screenshotDir, { recursive: true });
|
|
839
|
+
}
|
|
921
840
|
}
|
|
922
841
|
// Initialize project-specific D3K log file (clear for new session)
|
|
923
842
|
this.initializeD3KLog();
|
|
@@ -926,12 +845,11 @@ export class DevEnvironment {
|
|
|
926
845
|
// Clean up orphaned Playwright/Chrome processes from previous crashed sessions
|
|
927
846
|
// This prevents "kill EPERM" errors when MCP tries to spawn new browsers
|
|
928
847
|
await cleanupOrphanedPlaywrightProcesses((msg) => this.debugLog(msg));
|
|
929
|
-
//
|
|
930
|
-
//
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
}
|
|
848
|
+
// MCP server removed - no longer need to kill
|
|
849
|
+
// if (this.options.mcpPort) {
|
|
850
|
+
// this.debugLog(`Ensuring port ${this.options.mcpPort} is free (always kill)`)
|
|
851
|
+
// await this.killMcpServer()
|
|
852
|
+
// }
|
|
935
853
|
// Check if user explicitly set ports via CLI flags
|
|
936
854
|
const userSetAppPort = this.options.userSetPort || false;
|
|
937
855
|
// If user didn't set ports, find available ones first (before checking)
|
|
@@ -963,79 +881,7 @@ export class DevEnvironment {
|
|
|
963
881
|
throw new Error(`Port ${this.options.port} is already in use. Please free the port and try again.`);
|
|
964
882
|
}
|
|
965
883
|
}
|
|
966
|
-
//
|
|
967
|
-
if (this.options.mcpPort) {
|
|
968
|
-
const available = await isPortAvailable(this.options.mcpPort);
|
|
969
|
-
if (!available) {
|
|
970
|
-
if (this.spinner?.isSpinning) {
|
|
971
|
-
this.spinner.fail(`Port ${this.options.mcpPort} is still in use after cleanup`);
|
|
972
|
-
}
|
|
973
|
-
if (!silent) {
|
|
974
|
-
console.log(chalk.yellow(`💡 To force kill port ${this.options.mcpPort}, run: lsof -ti:${this.options.mcpPort} | xargs kill -9`));
|
|
975
|
-
}
|
|
976
|
-
if (this.tui) {
|
|
977
|
-
await this.tui.shutdown();
|
|
978
|
-
}
|
|
979
|
-
throw new Error(`Port ${this.options.mcpPort} is still in use. Please free the port and try again.`);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
async killMcpServer() {
|
|
984
|
-
// In sandbox environments, skip lsof-based process cleanup
|
|
985
|
-
if (isInSandbox()) {
|
|
986
|
-
this.debugLog("killMcpServer skipped: Running in sandbox environment");
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
// Retry loop to ensure port is fully released
|
|
990
|
-
const maxRetries = 5;
|
|
991
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
992
|
-
try {
|
|
993
|
-
// First, get the PIDs of processes LISTENING on the port
|
|
994
|
-
// Use -sTCP:LISTEN to only find servers, not clients connecting to the port
|
|
995
|
-
// This prevents killing curl when it's polling for MCP server readiness
|
|
996
|
-
const getPidsProcess = spawn("lsof", ["-ti", `:${this.options.mcpPort}`, "-sTCP:LISTEN"], {
|
|
997
|
-
stdio: "pipe"
|
|
998
|
-
});
|
|
999
|
-
const pids = await new Promise((resolve, reject) => {
|
|
1000
|
-
let output = "";
|
|
1001
|
-
getPidsProcess.stdout?.on("data", (data) => {
|
|
1002
|
-
output += data.toString();
|
|
1003
|
-
});
|
|
1004
|
-
getPidsProcess.on("error", (err) => reject(err));
|
|
1005
|
-
getPidsProcess.on("exit", () => resolve(output.trim()));
|
|
1006
|
-
});
|
|
1007
|
-
if (!pids) {
|
|
1008
|
-
this.debugLog(`Port ${this.options.mcpPort} is free (attempt ${attempt})`);
|
|
1009
|
-
return; // Port is already free
|
|
1010
|
-
}
|
|
1011
|
-
this.debugLog(`Found MCP server processes (attempt ${attempt}): ${pids}`);
|
|
1012
|
-
// Kill each PID individually with kill -9
|
|
1013
|
-
const pidList = pids.split("\n").filter(Boolean);
|
|
1014
|
-
for (const pid of pidList) {
|
|
1015
|
-
await new Promise((resolve) => {
|
|
1016
|
-
const killCmd = spawn("kill", ["-9", pid.trim()], { stdio: "ignore" });
|
|
1017
|
-
killCmd.on("exit", (code) => {
|
|
1018
|
-
this.debugLog(`Kill command for PID ${pid} exited with code ${code}`);
|
|
1019
|
-
resolve();
|
|
1020
|
-
});
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
// Give it time to fully release the port (longer waits, macOS can be slow)
|
|
1024
|
-
const waitTime = 1000 * attempt;
|
|
1025
|
-
this.debugLog(`Waiting ${waitTime}ms for port ${this.options.mcpPort} to be released...`);
|
|
1026
|
-
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1027
|
-
// Check if port is now free
|
|
1028
|
-
const available = await isPortAvailable(this.options.mcpPort?.toString() ?? "");
|
|
1029
|
-
if (available) {
|
|
1030
|
-
this.debugLog(`Port ${this.options.mcpPort} released successfully`);
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
catch (error) {
|
|
1035
|
-
this.debugLog(`Error killing MCP server (attempt ${attempt}): ${error}`);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
this.debugLog(`Warning: Port ${this.options.mcpPort} may still be in use after ${maxRetries} attempts`);
|
|
884
|
+
// MCP server removed - no longer need to check mcpPort availability
|
|
1039
885
|
}
|
|
1040
886
|
async checkProcessHealth() {
|
|
1041
887
|
if (this.isShuttingDown)
|
|
@@ -1047,7 +893,8 @@ export class DevEnvironment {
|
|
|
1047
893
|
return true;
|
|
1048
894
|
}
|
|
1049
895
|
try {
|
|
1050
|
-
|
|
896
|
+
// Only check app port - MCP server has been removed
|
|
897
|
+
const ports = [this.options.port];
|
|
1051
898
|
for (const port of ports) {
|
|
1052
899
|
const result = await new Promise((resolve, reject) => {
|
|
1053
900
|
const proc = spawn("lsof", ["-ti", `:${port}`], { stdio: "pipe" });
|
|
@@ -1226,9 +1073,8 @@ export class DevEnvironment {
|
|
|
1226
1073
|
// Start user's dev server
|
|
1227
1074
|
await this.tui.updateStatus("Starting your dev server...");
|
|
1228
1075
|
await this.startServer();
|
|
1229
|
-
//
|
|
1230
|
-
await this.
|
|
1231
|
-
await this.startMcpServer();
|
|
1076
|
+
// MCP server removed - using CLI commands instead
|
|
1077
|
+
// await this.startMcpServer()
|
|
1232
1078
|
// Wait for servers to be ready
|
|
1233
1079
|
await this.tui.updateStatus("Waiting for app...");
|
|
1234
1080
|
const serverStarted = await this.waitForServer();
|
|
@@ -1241,8 +1087,8 @@ export class DevEnvironment {
|
|
|
1241
1087
|
}
|
|
1242
1088
|
// Update TUI with confirmed port (may have changed during server startup)
|
|
1243
1089
|
this.tui.updateAppPort(this.options.port);
|
|
1244
|
-
|
|
1245
|
-
await this.waitForMcpServer()
|
|
1090
|
+
// MCP server removed - using CLI commands instead
|
|
1091
|
+
// await this.waitForMcpServer()
|
|
1246
1092
|
// Configure AI CLI integrations (both dev3000 and chrome-devtools MCPs)
|
|
1247
1093
|
if (!this.options.serversOnly) {
|
|
1248
1094
|
await this.tui.updateStatus("Configuring AI CLI integrations...");
|
|
@@ -1289,9 +1135,8 @@ export class DevEnvironment {
|
|
|
1289
1135
|
// Start user's dev server
|
|
1290
1136
|
this.spinner.text = "Starting your dev server...";
|
|
1291
1137
|
await this.startServer();
|
|
1292
|
-
//
|
|
1293
|
-
|
|
1294
|
-
await this.startMcpServer();
|
|
1138
|
+
// MCP server removed - using CLI commands instead
|
|
1139
|
+
// await this.startMcpServer()
|
|
1295
1140
|
// Wait for servers to be ready
|
|
1296
1141
|
this.spinner.text = "Waiting for app...";
|
|
1297
1142
|
const serverStarted = await this.waitForServer();
|
|
@@ -1302,8 +1147,8 @@ export class DevEnvironment {
|
|
|
1302
1147
|
console.error(chalk.yellow("Exiting without launching browser."));
|
|
1303
1148
|
process.exit(1);
|
|
1304
1149
|
}
|
|
1305
|
-
|
|
1306
|
-
await this.waitForMcpServer()
|
|
1150
|
+
// MCP server removed - using CLI commands instead
|
|
1151
|
+
// await this.waitForMcpServer()
|
|
1307
1152
|
// Configure AI CLI integrations (both dev3000 and chrome-devtools MCPs)
|
|
1308
1153
|
if (!this.options.serversOnly) {
|
|
1309
1154
|
this.spinner.text = "Configuring AI CLI integrations...";
|
|
@@ -1340,8 +1185,7 @@ export class DevEnvironment {
|
|
|
1340
1185
|
console.log(chalk.cyan(`Logs: ${this.options.logFile}`));
|
|
1341
1186
|
console.log(chalk.cyan("☝️ Give this to an AI to auto debug and fix your app\n"));
|
|
1342
1187
|
console.log(chalk.cyan(`🌐 Your App: ${this.serverProtocol}://localhost:${this.options.port}`));
|
|
1343
|
-
console.log(chalk.cyan(
|
|
1344
|
-
console.log(chalk.cyan(`📸 Visual Timeline: http://localhost:${this.options.mcpPort}/logs?project=${encodeURIComponent(projectName)}`));
|
|
1188
|
+
console.log(chalk.cyan(`🔧 CLI Tools: d3k fix, d3k crawl, d3k find-component`));
|
|
1345
1189
|
if (this.options.serversOnly) {
|
|
1346
1190
|
console.log(chalk.cyan("🖥️ Servers-only mode - use Chrome extension for browser monitoring"));
|
|
1347
1191
|
}
|
|
@@ -1649,250 +1493,6 @@ export class DevEnvironment {
|
|
|
1649
1493
|
// Ignore errors reading log file
|
|
1650
1494
|
}
|
|
1651
1495
|
}
|
|
1652
|
-
async startMcpServer() {
|
|
1653
|
-
this.debugLog("Starting MCP server setup");
|
|
1654
|
-
// Note: MCP server cleanup now happens earlier in checkPortsAvailable()
|
|
1655
|
-
// to ensure the port is free before we check availability
|
|
1656
|
-
// Get the path to our bundled MCP server
|
|
1657
|
-
// Handle both normal npm install and compiled binary cases
|
|
1658
|
-
let mcpServerPath;
|
|
1659
|
-
// Check if we're running from a compiled binary
|
|
1660
|
-
// Compiled binaries have process.execPath pointing to the binary itself
|
|
1661
|
-
const execPath = process.execPath;
|
|
1662
|
-
const isCompiledBinary = execPath.includes("@d3k/darwin-") || execPath.includes("d3k-darwin-") || execPath.endsWith("/dev3000");
|
|
1663
|
-
if (isCompiledBinary) {
|
|
1664
|
-
// For compiled binaries, mcp-server is a sibling to the bin directory
|
|
1665
|
-
// Structure: packages/d3k-darwin-arm64/bin/dev3000 -> packages/d3k-darwin-arm64/mcp-server
|
|
1666
|
-
const binDir = dirname(execPath);
|
|
1667
|
-
const packageDir = dirname(binDir);
|
|
1668
|
-
mcpServerPath = join(packageDir, "mcp-server");
|
|
1669
|
-
this.debugLog(`Compiled binary detected, MCP server path: ${mcpServerPath}`);
|
|
1670
|
-
}
|
|
1671
|
-
else {
|
|
1672
|
-
// Normal npm install - mcp-server is in the package root
|
|
1673
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
1674
|
-
const packageRoot = dirname(dirname(currentFile)); // Go up from dist/ to package root
|
|
1675
|
-
mcpServerPath = join(packageRoot, "mcp-server");
|
|
1676
|
-
this.debugLog(`Standard install detected, MCP server path: ${mcpServerPath}`);
|
|
1677
|
-
}
|
|
1678
|
-
this.debugLog(`Initial MCP server path: ${mcpServerPath}`);
|
|
1679
|
-
// For pnpm global installs, resolve symlinks to get the real path
|
|
1680
|
-
if (existsSync(mcpServerPath)) {
|
|
1681
|
-
try {
|
|
1682
|
-
const realPath = realpathSync(mcpServerPath);
|
|
1683
|
-
if (realPath !== mcpServerPath) {
|
|
1684
|
-
this.debugLog(`MCP server path resolved from symlink: ${mcpServerPath} -> ${realPath}`);
|
|
1685
|
-
mcpServerPath = realPath;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
catch (e) {
|
|
1689
|
-
// Error resolving path, continue with original
|
|
1690
|
-
this.debugLog(`Error resolving real path: ${e}`);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
this.debugLog(`Final MCP server path: ${mcpServerPath}`);
|
|
1694
|
-
if (!existsSync(mcpServerPath)) {
|
|
1695
|
-
throw new Error(`MCP server directory not found at ${mcpServerPath}`);
|
|
1696
|
-
}
|
|
1697
|
-
this.debugLog("MCP server directory found");
|
|
1698
|
-
// Check if MCP server dependencies are installed, install if missing
|
|
1699
|
-
// Detect global install by checking if the mcp-server path is outside the current working directory
|
|
1700
|
-
// This handles both pnpm (.pnpm) and npm (/lib/node_modules/) global installs
|
|
1701
|
-
const isGlobalInstall = mcpServerPath.includes(".pnpm") ||
|
|
1702
|
-
mcpServerPath.includes("/lib/node_modules/") ||
|
|
1703
|
-
!mcpServerPath.startsWith(process.cwd());
|
|
1704
|
-
this.debugLog(`Is global install: ${isGlobalInstall}`);
|
|
1705
|
-
let nodeModulesPath = join(mcpServerPath, "node_modules");
|
|
1706
|
-
let actualWorkingDir = mcpServerPath;
|
|
1707
|
-
this.debugLog(`Node modules path: ${nodeModulesPath}`);
|
|
1708
|
-
if (isGlobalInstall) {
|
|
1709
|
-
const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
|
|
1710
|
-
nodeModulesPath = join(tmpDirPath, "node_modules");
|
|
1711
|
-
actualWorkingDir = tmpDirPath;
|
|
1712
|
-
// Update screenshot and MCP public directory to use the temp directory for global installs
|
|
1713
|
-
this.screenshotDir = join(actualWorkingDir, "public", "screenshots");
|
|
1714
|
-
this.mcpPublicDir = join(actualWorkingDir, "public", "screenshots");
|
|
1715
|
-
if (!existsSync(this.mcpPublicDir)) {
|
|
1716
|
-
mkdirSync(this.mcpPublicDir, { recursive: true });
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
// Check if .next build directory exists - if so, skip dependency installation
|
|
1720
|
-
const nextBuildPath = join(mcpServerPath, ".next");
|
|
1721
|
-
this.debugLog(`Checking for pre-built MCP server at: ${nextBuildPath}`);
|
|
1722
|
-
let isPreBuilt = false;
|
|
1723
|
-
if (existsSync(nextBuildPath)) {
|
|
1724
|
-
this.debugLog("MCP server is pre-built (.next directory exists), skipping dependency installation");
|
|
1725
|
-
isPreBuilt = true;
|
|
1726
|
-
// For global installs with pre-built servers, we'll run from the original location
|
|
1727
|
-
// No need to copy anything to temp directory
|
|
1728
|
-
if (isGlobalInstall) {
|
|
1729
|
-
this.debugLog("Global install with pre-built server - will run from original location");
|
|
1730
|
-
actualWorkingDir = mcpServerPath;
|
|
1731
|
-
// Still need to set up screenshot directory in temp
|
|
1732
|
-
const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
|
|
1733
|
-
this.screenshotDir = join(tmpDirPath, "public", "screenshots");
|
|
1734
|
-
this.mcpPublicDir = join(tmpDirPath, "public", "screenshots");
|
|
1735
|
-
if (!existsSync(this.mcpPublicDir)) {
|
|
1736
|
-
mkdirSync(this.mcpPublicDir, { recursive: true });
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
else {
|
|
1741
|
-
this.debugLog("No .next directory found, installing/updating MCP server dependencies");
|
|
1742
|
-
this.debugLog(`WARNING: MCP server appears to not be pre-built. This is unexpected for a published package.`);
|
|
1743
|
-
await this.installMcpServerDeps(mcpServerPath);
|
|
1744
|
-
}
|
|
1745
|
-
// Use version already read in constructor
|
|
1746
|
-
// For global installs, only copy files if NOT pre-built
|
|
1747
|
-
// Pre-built servers run from their original location
|
|
1748
|
-
if (isGlobalInstall && actualWorkingDir !== mcpServerPath && !isPreBuilt) {
|
|
1749
|
-
const requiredFiles = ["app", "public", "next.config.ts", "next-env.d.ts", "tsconfig.json", ".next"];
|
|
1750
|
-
for (const file of requiredFiles) {
|
|
1751
|
-
const srcPath = join(mcpServerPath, file);
|
|
1752
|
-
const destPath = join(actualWorkingDir, file);
|
|
1753
|
-
// Check if we need to copy (source exists and destination doesn't exist or source is newer)
|
|
1754
|
-
if (existsSync(srcPath)) {
|
|
1755
|
-
let shouldCopy = !existsSync(destPath);
|
|
1756
|
-
// If destination exists, check if source is newer
|
|
1757
|
-
if (!shouldCopy && existsSync(destPath)) {
|
|
1758
|
-
const srcStat = lstatSync(srcPath);
|
|
1759
|
-
const destStat = lstatSync(destPath);
|
|
1760
|
-
shouldCopy = srcStat.mtime > destStat.mtime;
|
|
1761
|
-
}
|
|
1762
|
-
if (shouldCopy) {
|
|
1763
|
-
// Remove existing destination if it exists
|
|
1764
|
-
if (existsSync(destPath)) {
|
|
1765
|
-
if (lstatSync(destPath).isDirectory()) {
|
|
1766
|
-
cpSync(destPath, `${destPath}.bak`, { recursive: true });
|
|
1767
|
-
cpSync(srcPath, destPath, { recursive: true, force: true });
|
|
1768
|
-
}
|
|
1769
|
-
else {
|
|
1770
|
-
unlinkSync(destPath);
|
|
1771
|
-
copyFileSync(srcPath, destPath);
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
else {
|
|
1775
|
-
if (lstatSync(srcPath).isDirectory()) {
|
|
1776
|
-
cpSync(srcPath, destPath, { recursive: true });
|
|
1777
|
-
}
|
|
1778
|
-
else {
|
|
1779
|
-
copyFileSync(srcPath, destPath);
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
// Start the MCP server
|
|
1787
|
-
this.debugLog(`MCP server working directory: ${actualWorkingDir}`);
|
|
1788
|
-
this.debugLog(`MCP server port: ${this.options.mcpPort}`);
|
|
1789
|
-
this.debugLog(`Screenshot directory: ${this.screenshotDir}`);
|
|
1790
|
-
this.debugLog(`Is pre-built: ${isPreBuilt}`);
|
|
1791
|
-
this.debugLog(`Is global install: ${isGlobalInstall}`);
|
|
1792
|
-
let mcpCommand;
|
|
1793
|
-
let mcpCwd = actualWorkingDir;
|
|
1794
|
-
if (isGlobalInstall && isPreBuilt) {
|
|
1795
|
-
// For global installs with pre-built servers, use the standalone server directly
|
|
1796
|
-
// This avoids the turbopack runtime issues with npx
|
|
1797
|
-
const serverJsPath = join(mcpServerPath, ".next", "standalone", "mcp-server", "server.js");
|
|
1798
|
-
if (existsSync(serverJsPath)) {
|
|
1799
|
-
// Use the standalone server directly
|
|
1800
|
-
this.debugLog(`Global install with standalone server at ${serverJsPath}`);
|
|
1801
|
-
mcpCommand = ["node", serverJsPath];
|
|
1802
|
-
mcpCwd = dirname(serverJsPath);
|
|
1803
|
-
}
|
|
1804
|
-
else {
|
|
1805
|
-
// Check for start-production.mjs script
|
|
1806
|
-
const startProdScript = join(mcpServerPath, "start-production.mjs");
|
|
1807
|
-
if (existsSync(startProdScript)) {
|
|
1808
|
-
// Use the production script
|
|
1809
|
-
this.debugLog(`Global install with start-production.mjs script`);
|
|
1810
|
-
mcpCommand = ["node", startProdScript];
|
|
1811
|
-
mcpCwd = mcpServerPath;
|
|
1812
|
-
}
|
|
1813
|
-
else {
|
|
1814
|
-
// Fallback to finding Next.js binary
|
|
1815
|
-
const dev3000NodeModules = join(mcpServerPath, "..", "..", "node_modules");
|
|
1816
|
-
const nextBinPath = join(dev3000NodeModules, ".bin", "next");
|
|
1817
|
-
this.debugLog(`Looking for Next.js at: ${nextBinPath}`);
|
|
1818
|
-
if (existsSync(nextBinPath)) {
|
|
1819
|
-
// Found Next.js in the dev3000 package
|
|
1820
|
-
this.debugLog(`Global install with Next.js found at ${nextBinPath}`);
|
|
1821
|
-
mcpCommand = [nextBinPath, "start"];
|
|
1822
|
-
mcpCwd = mcpServerPath;
|
|
1823
|
-
}
|
|
1824
|
-
else {
|
|
1825
|
-
// Fallback to npx with the exact version we built with
|
|
1826
|
-
this.debugLog(`Global install with pre-built server - using npx next start`);
|
|
1827
|
-
mcpCommand = ["npx", "--yes", "next@15.5.1-canary.30", "start"];
|
|
1828
|
-
mcpCwd = mcpServerPath;
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
else {
|
|
1834
|
-
// Non-global or non-pre-built: use package manager
|
|
1835
|
-
const packageManagerForRun = detectPackageManagerForRun();
|
|
1836
|
-
this.debugLog(`Using package manager: ${packageManagerForRun}`);
|
|
1837
|
-
mcpCommand = [packageManagerForRun, "run", "start"];
|
|
1838
|
-
mcpCwd = actualWorkingDir;
|
|
1839
|
-
}
|
|
1840
|
-
this.debugLog(`MCP server command: ${mcpCommand.join(" ")}`);
|
|
1841
|
-
this.debugLog(`MCP server cwd: ${mcpCwd}`);
|
|
1842
|
-
// Get CDP URL for MCP orchestration
|
|
1843
|
-
const cdpUrl = this.cdpMonitor?.getCdpUrl() || null;
|
|
1844
|
-
// Start MCP server as a true background singleton process
|
|
1845
|
-
this.mcpServerProcess = spawn(mcpCommand[0], mcpCommand.slice(1), {
|
|
1846
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1847
|
-
detached: true, // Run independently of parent process
|
|
1848
|
-
cwd: mcpCwd,
|
|
1849
|
-
env: {
|
|
1850
|
-
...process.env,
|
|
1851
|
-
PORT: this.options.mcpPort,
|
|
1852
|
-
LOG_FILE_PATH: this.options.logFile, // Pass log file path to MCP server
|
|
1853
|
-
DEV3000_VERSION: this.version, // Pass version to MCP server
|
|
1854
|
-
SCREENSHOT_DIR: this.screenshotDir, // Pass screenshot directory for global installs
|
|
1855
|
-
CDP_URL: cdpUrl || "" // Pass CDP URL for chrome-devtools MCP orchestration
|
|
1856
|
-
}
|
|
1857
|
-
});
|
|
1858
|
-
// Unref the process so it continues running after parent exits
|
|
1859
|
-
this.mcpServerProcess.unref();
|
|
1860
|
-
this.debugLog("MCP server process spawned as singleton background service");
|
|
1861
|
-
// Log MCP server output to separate file for debugging
|
|
1862
|
-
const mcpLogFile = join(dirname(this.options.logFile), "mcp.log");
|
|
1863
|
-
writeFileSync(mcpLogFile, ""); // Clear the file
|
|
1864
|
-
// In debug mode, output the MCP log file path
|
|
1865
|
-
if (this.options.debug) {
|
|
1866
|
-
console.log(chalk.gray(`[DEBUG] MCP server logs: ${mcpLogFile}`));
|
|
1867
|
-
}
|
|
1868
|
-
this.mcpServerProcess.stdout?.on("data", (data) => {
|
|
1869
|
-
const message = data.toString().trim();
|
|
1870
|
-
if (message) {
|
|
1871
|
-
const timestamp = new Date().toISOString();
|
|
1872
|
-
appendFileSync(mcpLogFile, `[${timestamp}] [MCP-STDOUT] ${message}\n`);
|
|
1873
|
-
}
|
|
1874
|
-
});
|
|
1875
|
-
this.mcpServerProcess.stderr?.on("data", (data) => {
|
|
1876
|
-
const message = data.toString().trim();
|
|
1877
|
-
if (message) {
|
|
1878
|
-
const timestamp = new Date().toISOString();
|
|
1879
|
-
appendFileSync(mcpLogFile, `[${timestamp}] [MCP-STDERR] ${message}\n`);
|
|
1880
|
-
// Only show critical errors in stdout for debugging
|
|
1881
|
-
// Exclude MCP Orchestrator connection errors (they're expected and non-critical)
|
|
1882
|
-
if ((message.includes("FATAL") || message.includes("Error:")) && !message.includes("[MCP Orchestrator]")) {
|
|
1883
|
-
console.error(chalk.red("[ERROR]"), message);
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
});
|
|
1887
|
-
this.mcpServerProcess.on("exit", (code) => {
|
|
1888
|
-
this.debugLog(`MCP server process exited with code ${code}`);
|
|
1889
|
-
// Only show exit messages for unexpected failures, not restarts
|
|
1890
|
-
if (code !== 0 && code !== null) {
|
|
1891
|
-
this.logger.log("server", `MCP server process exited with code ${code}`);
|
|
1892
|
-
}
|
|
1893
|
-
});
|
|
1894
|
-
this.debugLog("MCP server event handlers setup complete");
|
|
1895
|
-
}
|
|
1896
1496
|
async waitForServer() {
|
|
1897
1497
|
const maxAttempts = 30;
|
|
1898
1498
|
let attempts = 0;
|
|
@@ -1943,172 +1543,6 @@ export class DevEnvironment {
|
|
|
1943
1543
|
this.debugLog(`Server readiness check timed out after ${totalTime}ms (${maxAttempts} attempts)`);
|
|
1944
1544
|
return false;
|
|
1945
1545
|
}
|
|
1946
|
-
detectPackageManagerInDir(dir) {
|
|
1947
|
-
if (existsSync(join(dir, "bun.lockb")))
|
|
1948
|
-
return "bun";
|
|
1949
|
-
if (existsSync(join(dir, "pnpm-lock.yaml")))
|
|
1950
|
-
return "pnpm";
|
|
1951
|
-
if (existsSync(join(dir, "yarn.lock")))
|
|
1952
|
-
return "yarn";
|
|
1953
|
-
if (existsSync(join(dir, "package-lock.json")))
|
|
1954
|
-
return "npm";
|
|
1955
|
-
return "npm"; // fallback
|
|
1956
|
-
}
|
|
1957
|
-
async installMcpServerDeps(mcpServerPath) {
|
|
1958
|
-
return new Promise((resolve, reject) => {
|
|
1959
|
-
// For global installs, we need to install to a writable location
|
|
1960
|
-
// Detect global install by checking if the path is outside the current working directory
|
|
1961
|
-
const isGlobalInstall = mcpServerPath.includes(".pnpm") ||
|
|
1962
|
-
mcpServerPath.includes("/lib/node_modules/") ||
|
|
1963
|
-
!mcpServerPath.startsWith(process.cwd());
|
|
1964
|
-
let workingDir = mcpServerPath;
|
|
1965
|
-
if (isGlobalInstall) {
|
|
1966
|
-
// Create a writable copy in temp directory for global installs
|
|
1967
|
-
const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
|
|
1968
|
-
// Ensure tmp directory exists
|
|
1969
|
-
if (!existsSync(tmpDirPath)) {
|
|
1970
|
-
mkdirSync(tmpDirPath, { recursive: true });
|
|
1971
|
-
}
|
|
1972
|
-
// Always copy package.json to temp directory to ensure it's up to date
|
|
1973
|
-
const tmpPackageJson = join(tmpDirPath, "package.json");
|
|
1974
|
-
const sourcePackageJson = join(mcpServerPath, "package.json");
|
|
1975
|
-
// Debug: Check if source package.json exists
|
|
1976
|
-
if (!existsSync(sourcePackageJson)) {
|
|
1977
|
-
const errorDetails = [
|
|
1978
|
-
`ERROR: package.json not found at ${sourcePackageJson}`,
|
|
1979
|
-
`MCP server path: ${mcpServerPath}`,
|
|
1980
|
-
`Contents of MCP server directory:`
|
|
1981
|
-
];
|
|
1982
|
-
try {
|
|
1983
|
-
const files = readdirSync(mcpServerPath);
|
|
1984
|
-
files.forEach((file) => {
|
|
1985
|
-
errorDetails.push(` - ${file}`);
|
|
1986
|
-
});
|
|
1987
|
-
}
|
|
1988
|
-
catch (e) {
|
|
1989
|
-
errorDetails.push(` Error listing directory: ${e}`);
|
|
1990
|
-
}
|
|
1991
|
-
// Additional debug: Check parent directories
|
|
1992
|
-
errorDetails.push(`Parent directory: ${dirname(mcpServerPath)}`);
|
|
1993
|
-
try {
|
|
1994
|
-
const parentFiles = readdirSync(dirname(mcpServerPath));
|
|
1995
|
-
parentFiles.forEach((file) => {
|
|
1996
|
-
errorDetails.push(` Parent dir file: ${file}`);
|
|
1997
|
-
});
|
|
1998
|
-
}
|
|
1999
|
-
catch (e) {
|
|
2000
|
-
errorDetails.push(` Error listing parent directory: ${e}`);
|
|
2001
|
-
}
|
|
2002
|
-
// Log all error details
|
|
2003
|
-
errorDetails.forEach((detail) => {
|
|
2004
|
-
this.debugLog(detail);
|
|
2005
|
-
});
|
|
2006
|
-
reject(new Error(`MCP server package.json not found at ${sourcePackageJson}`));
|
|
2007
|
-
return;
|
|
2008
|
-
}
|
|
2009
|
-
copyFileSync(sourcePackageJson, tmpPackageJson);
|
|
2010
|
-
workingDir = tmpDirPath;
|
|
2011
|
-
}
|
|
2012
|
-
// Detect package manager from MCP server directory, not current directory
|
|
2013
|
-
const packageManager = this.detectPackageManagerInDir(mcpServerPath);
|
|
2014
|
-
// Package manager specific install args to include devDependencies
|
|
2015
|
-
const installArgs = packageManager === "pnpm"
|
|
2016
|
-
? ["install", "--prod=false"] // Install both prod and dev dependencies
|
|
2017
|
-
: packageManager === "bun"
|
|
2018
|
-
? ["install", "--dev"] // bun syntax
|
|
2019
|
-
: ["install", "--include=dev"]; // npm/yarn syntax
|
|
2020
|
-
const fullCommand = `${packageManager} ${installArgs.join(" ")}`;
|
|
2021
|
-
if (this.options.debug) {
|
|
2022
|
-
console.log(`[DEBUG] Installing MCP server dependencies...`);
|
|
2023
|
-
console.log(`[DEBUG] Working directory: ${workingDir}`);
|
|
2024
|
-
console.log(`[DEBUG] Package manager detected: ${packageManager}`);
|
|
2025
|
-
console.log(`[DEBUG] Command: ${fullCommand}`);
|
|
2026
|
-
console.log(`[DEBUG] Is global install: ${isGlobalInstall}`);
|
|
2027
|
-
}
|
|
2028
|
-
const installStartTime = Date.now();
|
|
2029
|
-
const installProcess = spawn(packageManager, installArgs, {
|
|
2030
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2031
|
-
cwd: workingDir
|
|
2032
|
-
});
|
|
2033
|
-
// Add timeout (3 minutes)
|
|
2034
|
-
const timeout = setTimeout(() => {
|
|
2035
|
-
if (this.options.debug) {
|
|
2036
|
-
console.log(`[DEBUG] Installation timed out after 3 minutes`);
|
|
2037
|
-
}
|
|
2038
|
-
installProcess.kill("SIGKILL");
|
|
2039
|
-
reject(new Error("MCP server dependency installation timed out after 3 minutes"));
|
|
2040
|
-
}, 3 * 60 * 1000);
|
|
2041
|
-
// Capture output for debugging, but suppress for normal operation
|
|
2042
|
-
let debugOutput = "";
|
|
2043
|
-
let debugErrors = "";
|
|
2044
|
-
installProcess.stdout?.on("data", (data) => {
|
|
2045
|
-
const text = data.toString();
|
|
2046
|
-
if (this.options.debug) {
|
|
2047
|
-
debugOutput += text;
|
|
2048
|
-
}
|
|
2049
|
-
});
|
|
2050
|
-
installProcess.stderr?.on("data", (data) => {
|
|
2051
|
-
const text = data.toString();
|
|
2052
|
-
if (this.options.debug) {
|
|
2053
|
-
debugErrors += text;
|
|
2054
|
-
}
|
|
2055
|
-
});
|
|
2056
|
-
installProcess.on("exit", (code) => {
|
|
2057
|
-
clearTimeout(timeout);
|
|
2058
|
-
const installTime = Date.now() - installStartTime;
|
|
2059
|
-
if (this.options.debug) {
|
|
2060
|
-
console.log(`[DEBUG] Installation completed in ${installTime}ms with exit code: ${code}`);
|
|
2061
|
-
if (debugOutput) {
|
|
2062
|
-
console.log(`[DEBUG] stdout:`, debugOutput.trim());
|
|
2063
|
-
}
|
|
2064
|
-
if (debugErrors) {
|
|
2065
|
-
console.log(`[DEBUG] stderr:`, debugErrors.trim());
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
if (code === 0) {
|
|
2069
|
-
resolve();
|
|
2070
|
-
}
|
|
2071
|
-
else {
|
|
2072
|
-
const errorMsg = `MCP server dependency installation failed with exit code ${code}`;
|
|
2073
|
-
const fullError = this.options.debug && debugErrors ? `${errorMsg}\nstderr: ${debugErrors.trim()}` : errorMsg;
|
|
2074
|
-
reject(new Error(fullError));
|
|
2075
|
-
}
|
|
2076
|
-
});
|
|
2077
|
-
installProcess.on("error", (error) => {
|
|
2078
|
-
clearTimeout(timeout);
|
|
2079
|
-
reject(new Error(`Failed to start MCP server dependency installation: ${error.message}`));
|
|
2080
|
-
});
|
|
2081
|
-
});
|
|
2082
|
-
}
|
|
2083
|
-
async waitForMcpServer() {
|
|
2084
|
-
const maxAttempts = 30;
|
|
2085
|
-
let attempts = 0;
|
|
2086
|
-
while (attempts < maxAttempts) {
|
|
2087
|
-
try {
|
|
2088
|
-
// Test the actual MCP endpoint
|
|
2089
|
-
const response = await fetch(`http://localhost:${this.options.mcpPort}`, {
|
|
2090
|
-
method: "HEAD",
|
|
2091
|
-
signal: AbortSignal.timeout(2000)
|
|
2092
|
-
});
|
|
2093
|
-
this.debugLog(`MCP server health check: ${response.status}`);
|
|
2094
|
-
if (response.status === 500) {
|
|
2095
|
-
const errorText = await response.text();
|
|
2096
|
-
this.debugLog(`MCP server 500 error: ${errorText}`);
|
|
2097
|
-
}
|
|
2098
|
-
if (response.ok || response.status === 404) {
|
|
2099
|
-
// 404 is OK - means server is responding
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
catch (error) {
|
|
2104
|
-
this.debugLog(`MCP server not ready (attempt ${attempts}): ${error}`);
|
|
2105
|
-
}
|
|
2106
|
-
attempts++;
|
|
2107
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2108
|
-
}
|
|
2109
|
-
this.debugLog("MCP server health check failed, terminating");
|
|
2110
|
-
throw new Error(`MCP server failed to start after ${maxAttempts} seconds. Check the logs for errors.`);
|
|
2111
|
-
}
|
|
2112
1546
|
initializeD3KLog() {
|
|
2113
1547
|
try {
|
|
2114
1548
|
const projectDir = getProjectDir();
|
|
@@ -2165,8 +1599,8 @@ export class DevEnvironment {
|
|
|
2165
1599
|
if (!existsSync(this.options.profileDir)) {
|
|
2166
1600
|
mkdirSync(this.options.profileDir, { recursive: true });
|
|
2167
1601
|
}
|
|
2168
|
-
// Initialize CDP monitor with enhanced logging
|
|
2169
|
-
this.cdpMonitor = new CDPMonitor(this.options.profileDir, this.
|
|
1602
|
+
// Initialize CDP monitor with enhanced logging
|
|
1603
|
+
this.cdpMonitor = new CDPMonitor(this.options.profileDir, this.screenshotDir, (_source, message) => {
|
|
2170
1604
|
this.logger.log("browser", message);
|
|
2171
1605
|
}, this.options.debug, this.options.browser, this.options.pluginReactScan, this.options.port, // App server port to monitor
|
|
2172
1606
|
this.options.mcpPort, // MCP server port to ignore
|