dev3000 0.0.174 → 0.0.176

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.
Files changed (75) hide show
  1. package/README.md +0 -4
  2. package/dist/cdp-monitor.d.ts +5 -1
  3. package/dist/cdp-monitor.d.ts.map +1 -1
  4. package/dist/cdp-monitor.js +273 -46
  5. package/dist/cdp-monitor.js.map +1 -1
  6. package/dist/cli.js +154 -217
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/crawl.d.ts.map +1 -1
  9. package/dist/commands/crawl.js +4 -43
  10. package/dist/commands/crawl.js.map +1 -1
  11. package/dist/commands/errors.d.ts.map +1 -1
  12. package/dist/commands/errors.js +4 -53
  13. package/dist/commands/errors.js.map +1 -1
  14. package/dist/commands/fix.d.ts.map +1 -1
  15. package/dist/commands/fix.js +5 -74
  16. package/dist/commands/fix.js.map +1 -1
  17. package/dist/commands/logs.d.ts.map +1 -1
  18. package/dist/commands/logs.js +4 -53
  19. package/dist/commands/logs.js.map +1 -1
  20. package/dist/commands/skill-runner.d.ts +15 -0
  21. package/dist/commands/skill-runner.d.ts.map +1 -0
  22. package/dist/commands/skill-runner.js +615 -0
  23. package/dist/commands/skill-runner.js.map +1 -0
  24. package/dist/dev-environment.d.ts +6 -3
  25. package/dist/dev-environment.d.ts.map +1 -1
  26. package/dist/dev-environment.js +123 -105
  27. package/dist/dev-environment.js.map +1 -1
  28. package/dist/skills/d3k/internal-skill.md +145 -0
  29. package/dist/skills/index.test.ts +28 -1
  30. package/dist/skills/index.ts +58 -7
  31. package/dist/utils/agent-browser.d.ts.map +1 -1
  32. package/dist/utils/agent-browser.js +6 -3
  33. package/dist/utils/agent-browser.js.map +1 -1
  34. package/dist/utils/agent-detection.d.ts +1 -0
  35. package/dist/utils/agent-detection.d.ts.map +1 -1
  36. package/dist/utils/agent-detection.js +11 -0
  37. package/dist/utils/agent-detection.js.map +1 -1
  38. package/dist/utils/agent-selection.js +3 -3
  39. package/dist/utils/agent-selection.js.map +1 -1
  40. package/dist/utils/browser-command-argv.d.ts +1 -1
  41. package/dist/utils/browser-command-argv.d.ts.map +1 -1
  42. package/dist/utils/browser-command-argv.js +1 -1
  43. package/dist/utils/browser-command-argv.js.map +1 -1
  44. package/dist/utils/cli-options.d.ts +5 -0
  45. package/dist/utils/cli-options.d.ts.map +1 -0
  46. package/dist/utils/cli-options.js +36 -0
  47. package/dist/utils/cli-options.js.map +1 -0
  48. package/dist/utils/project-name.d.ts +2 -0
  49. package/dist/utils/project-name.d.ts.map +1 -1
  50. package/dist/utils/project-name.js +6 -0
  51. package/dist/utils/project-name.js.map +1 -1
  52. package/dist/utils/session.d.ts +14 -0
  53. package/dist/utils/session.d.ts.map +1 -0
  54. package/dist/utils/session.js +65 -0
  55. package/dist/utils/session.js.map +1 -0
  56. package/dist/utils/version-check.js +2 -2
  57. package/dist/utils/version-check.js.map +1 -1
  58. package/package.json +10 -19
  59. package/dist/commands/cloud-check-pr.d.ts +0 -9
  60. package/dist/commands/cloud-check-pr.d.ts.map +0 -1
  61. package/dist/commands/cloud-check-pr.js +0 -243
  62. package/dist/commands/cloud-check-pr.js.map +0 -1
  63. package/dist/commands/cloud-fix.d.ts +0 -13
  64. package/dist/commands/cloud-fix.d.ts.map +0 -1
  65. package/dist/commands/cloud-fix.js +0 -79
  66. package/dist/commands/cloud-fix.js.map +0 -1
  67. package/dist/commands/find-component.d.ts +0 -8
  68. package/dist/commands/find-component.d.ts.map +0 -1
  69. package/dist/commands/find-component.js +0 -182
  70. package/dist/commands/find-component.js.map +0 -1
  71. package/dist/skills/d3k/SKILL.md +0 -126
  72. package/dist/skills/index.d.ts +0 -46
  73. package/dist/skills/index.d.ts.map +0 -1
  74. package/dist/skills/index.js +0 -174
  75. package/dist/skills/index.js.map +0 -1
package/README.md CHANGED
@@ -62,9 +62,6 @@ d3k fix --time 30 # Analyze last 30 minutes (default: 10)
62
62
  d3k crawl # Discover URLs by crawling the app
63
63
  d3k crawl --depth all # Exhaustive crawl (default: 1 level)
64
64
 
65
- d3k find-component "nav.header" # Find React component source
66
- d3k find-component "[data-testid='button']"
67
-
68
65
  d3k restart # Restart the development server (rarely needed)
69
66
  ```
70
67
 
@@ -74,7 +71,6 @@ d3k restart # Restart the development server (rarely needed)
74
71
  d3k skill [name] # Get skill content or list available skills
75
72
  d3k upgrade # Upgrade d3k to the latest version
76
73
  d3k agent-browser # Run the bundled agent-browser CLI
77
- d3k cloud # Cloud-based tools using Vercel Sandbox
78
74
  ```
79
75
 
80
76
  ## Options
@@ -17,6 +17,8 @@ export interface CDPTargetInfo {
17
17
  url?: string;
18
18
  webSocketDebuggerUrl?: string;
19
19
  }
20
+ export declare const CHROME_CRASH_RESTORE_SUPPRESSION_FLAGS: string[];
21
+ export declare function resetChromeCrashRestoreState(profileDir: string): number;
20
22
  export declare function getLoadingHtmlCandidates(currentDir: string, execPath?: string): string[];
21
23
  export declare function selectCDPTarget(targets: CDPTargetInfo[], options?: {
22
24
  appServerPort?: string;
@@ -44,13 +46,14 @@ export declare class CDPMonitor {
44
46
  private onWindowClosedCallback;
45
47
  private appServerPort?;
46
48
  private initialAppUrl?;
49
+ private externalCdpBase?;
47
50
  private headless;
48
51
  private framework?;
49
52
  private reactTrackingEnabled;
50
53
  private lastReactSnapshotLogTime;
51
54
  private pendingCommands;
52
55
  private navigationTimeoutMs;
53
- constructor(profileDir: string, screenshotDir: string, logger: (source: string, message: string) => void, debug?: boolean, browserPath?: string, pluginReactScan?: boolean, appServerPort?: string, initialAppUrl?: string, navigationTimeoutMs?: number, debugPort?: number, headless?: boolean, framework?: "nextjs" | "svelte" | "other");
56
+ constructor(profileDir: string, screenshotDir: string, logger: (source: string, message: string) => void, debug?: boolean, browserPath?: string, pluginReactScan?: boolean, appServerPort?: string, initialAppUrl?: string, navigationTimeoutMs?: number, debugPort?: number, headless?: boolean, framework?: "nextjs" | "svelte" | "other", externalCdpBase?: string);
54
57
  private resolveReactDevToolsExtensionPath;
55
58
  private debugLog;
56
59
  private runCommand;
@@ -114,6 +117,7 @@ export declare class CDPMonitor {
114
117
  */
115
118
  prepareShutdown(): void;
116
119
  private waitForBrowserExit;
120
+ private sendBrowserCloseCommand;
117
121
  shutdown(): Promise<void>;
118
122
  private killInstanceChromeProcesses;
119
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cdp-monitor.d.ts","sourceRoot":"","sources":["../src/cdp-monitor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cdp-monitor.d.ts","sourceRoot":"","sources":["../src/cdp-monitor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAE9B,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,SAAS,CAAA;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AA6DD,eAAO,MAAM,sCAAsC,UAIlD,CAAA;AAyFD,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAcvE;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,EAAE,CAS1G;AAwDD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,EAAE,EACxB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;CAClB,GACL,aAAa,CA4Cf;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAA2C;IACzD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,qBAAqB,CAAe;IAC5C,OAAO,CAAC,oBAAoB,CAAiB;IAC7C,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,sBAAsB,CAA4B;IAC1D,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,eAAe,CAAC,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,SAAS,CAAC,CAA+B;IACjD,OAAO,CAAC,oBAAoB,CAAiB;IAC7C,OAAO,CAAC,wBAAwB,CAAY;IAC5C,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,mBAAmB,CAAwC;gBAGjE,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,EACjD,KAAK,GAAE,OAAe,EACtB,WAAW,CAAC,EAAE,MAAM,EACpB,eAAe,GAAE,OAAe,EAChC,aAAa,CAAC,EAAE,MAAM,EACtB,aAAa,CAAC,EAAE,MAAM,EACtB,mBAAmB,GAAE,MAAsC,EAC3D,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,GAAE,OAAe,EACzB,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,EACzC,eAAe,CAAC,EAAE,MAAM;IAqB1B,OAAO,CAAC,iCAAiC;IA6BzC,OAAO,CAAC,QAAQ;YAMF,UAAU;YAuBV,aAAa;IAqD3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgClB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B5B,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B,aAAa,IAAI,MAAM,EAAE;IAIzB,yBAAyB,CAAC,QAAQ,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;YAIhD,kBAAkB;IA2BhC,OAAO,CAAC,iBAAiB;IA2CzB,OAAO,CAAC,2BAA2B;IA4CnC;;;;OAIG;YACW,6BAA6B;YAoC7B,YAAY;YAmMZ,YAAY;YAuJZ,gBAAgB;YA2BhB,cAAc;IAiD5B,OAAO,CAAC,kBAAkB;YAMZ,qBAAqB;YAsDrB,gBAAgB;YAqBhB,gBAAgB;IA0E9B,OAAO,CAAC,kBAAkB;IAiU1B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,gBAAgB;IA+ClB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmEvF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;YAK7D,wBAAwB;IAqTtC,OAAO,CAAC,uBAAuB;IAkG/B,OAAO,CAAC,6BAA6B;YAuBvB,cAAc;IA4CtB,kBAAkB,CAAC,WAAW,EAAE;QACpC,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACtC,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACnC,EAAE,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAC7B,IAAI,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAChC,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDjB;;;OAGG;IACH,eAAe,IAAI,IAAI;YAKT,kBAAkB;YAyBlB,uBAAuB;IAoF/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAoFjB,2BAA2B;CA+C1C"}
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "child_process";
2
- import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "fs";
2
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "fs";
3
3
  import { tmpdir } from "os";
4
4
  import { dirname, join } from "path";
5
5
  import { fileURLToPath } from "url";
@@ -45,6 +45,98 @@ const EMBEDDED_LOADING_HTML = `<!DOCTYPE html>
45
45
  </html>`;
46
46
  const DEFAULT_CDP_COMMAND_TIMEOUT_MS = 10000;
47
47
  const DEFAULT_NAVIGATION_TIMEOUT_MS = 60000;
48
+ export const CHROME_CRASH_RESTORE_SUPPRESSION_FLAGS = [
49
+ "--disable-session-crashed-bubble",
50
+ "--disable-restore-session-state",
51
+ "--hide-crash-restore-bubble"
52
+ ];
53
+ function isRecord(value) {
54
+ return typeof value === "object" && value !== null && !Array.isArray(value);
55
+ }
56
+ function patchChromePreferences(data) {
57
+ let changed = false;
58
+ const profile = isRecord(data.profile) ? data.profile : {};
59
+ if (data.profile !== profile) {
60
+ data.profile = profile;
61
+ changed = true;
62
+ }
63
+ if (profile.exit_type !== "Normal") {
64
+ profile.exit_type = "Normal";
65
+ changed = true;
66
+ }
67
+ if (profile.exited_cleanly !== true) {
68
+ profile.exited_cleanly = true;
69
+ changed = true;
70
+ }
71
+ return changed;
72
+ }
73
+ function patchChromeLocalState(data) {
74
+ let changed = false;
75
+ if (data.exit_type === "Crashed") {
76
+ data.exit_type = "Normal";
77
+ changed = true;
78
+ }
79
+ if (data.exited_cleanly === false) {
80
+ data.exited_cleanly = true;
81
+ changed = true;
82
+ }
83
+ if (isRecord(data.profile)) {
84
+ if (data.profile.exit_type === "Crashed") {
85
+ data.profile.exit_type = "Normal";
86
+ changed = true;
87
+ }
88
+ if (data.profile.exited_cleanly === false) {
89
+ data.profile.exited_cleanly = true;
90
+ changed = true;
91
+ }
92
+ }
93
+ return changed;
94
+ }
95
+ function patchJsonFile(filePath, patch) {
96
+ if (!existsSync(filePath)) {
97
+ return false;
98
+ }
99
+ try {
100
+ const data = JSON.parse(readFileSync(filePath, "utf-8"));
101
+ if (!isRecord(data) || !patch(data)) {
102
+ return false;
103
+ }
104
+ writeFileSync(filePath, JSON.stringify(data));
105
+ return true;
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ }
111
+ function getChromePreferencesFiles(profileDir) {
112
+ const files = new Set([join(profileDir, "Default", "Preferences")]);
113
+ if (!existsSync(profileDir)) {
114
+ return Array.from(files);
115
+ }
116
+ try {
117
+ for (const entry of readdirSync(profileDir, { withFileTypes: true })) {
118
+ if (entry.isDirectory() && (entry.name === "Default" || entry.name.startsWith("Profile "))) {
119
+ files.add(join(profileDir, entry.name, "Preferences"));
120
+ }
121
+ }
122
+ }
123
+ catch {
124
+ // Ignore unreadable profile directories.
125
+ }
126
+ return Array.from(files);
127
+ }
128
+ export function resetChromeCrashRestoreState(profileDir) {
129
+ let changedFiles = 0;
130
+ for (const preferencesFile of getChromePreferencesFiles(profileDir)) {
131
+ if (patchJsonFile(preferencesFile, patchChromePreferences)) {
132
+ changedFiles++;
133
+ }
134
+ }
135
+ if (patchJsonFile(join(profileDir, "Local State"), patchChromeLocalState)) {
136
+ changedFiles++;
137
+ }
138
+ return changedFiles;
139
+ }
48
140
  export function getLoadingHtmlCandidates(currentDir, execPath = process.execPath) {
49
141
  const candidates = [join(currentDir, "src/loading.html"), join(currentDir, "loading.html")];
50
142
  const packageRoot = dirname(dirname(execPath));
@@ -157,13 +249,14 @@ export class CDPMonitor {
157
249
  onWindowClosedCallback = null; // Callback for when window is manually closed
158
250
  appServerPort; // Port of the user's app server to monitor
159
251
  initialAppUrl; // App URL that the loading page should hand off to
252
+ externalCdpBase; // Existing CDP HTTP endpoint to connect to instead of launching Chrome
160
253
  headless = false; // Run Chrome in headless mode
161
254
  framework; // Framework hint from project detection
162
255
  reactTrackingEnabled = false;
163
256
  lastReactSnapshotLogTime = 0;
164
257
  pendingCommands = new Map();
165
258
  navigationTimeoutMs = DEFAULT_NAVIGATION_TIMEOUT_MS;
166
- constructor(profileDir, screenshotDir, logger, debug = false, browserPath, pluginReactScan = false, appServerPort, initialAppUrl, navigationTimeoutMs = DEFAULT_NAVIGATION_TIMEOUT_MS, debugPort, headless = false, framework) {
259
+ constructor(profileDir, screenshotDir, logger, debug = false, browserPath, pluginReactScan = false, appServerPort, initialAppUrl, navigationTimeoutMs = DEFAULT_NAVIGATION_TIMEOUT_MS, debugPort, headless = false, framework, externalCdpBase) {
167
260
  this.profileDir = profileDir;
168
261
  this.screenshotDir = screenshotDir;
169
262
  this.appServerPort = appServerPort;
@@ -175,6 +268,7 @@ export class CDPMonitor {
175
268
  this.navigationTimeoutMs = navigationTimeoutMs;
176
269
  this.headless = headless;
177
270
  this.framework = framework;
271
+ this.externalCdpBase = externalCdpBase?.replace(/\/$/, "");
178
272
  this.reactTrackingEnabled = framework === "nextjs";
179
273
  // Use custom debug port if provided, otherwise use default 9222
180
274
  if (debugPort) {
@@ -290,6 +384,13 @@ export class CDPMonitor {
290
384
  const urlObj = new URL(url);
291
385
  const hostname = urlObj.hostname;
292
386
  const port = urlObj.port || (urlObj.protocol === "https:" ? "443" : "80");
387
+ if (this.initialAppUrl) {
388
+ const initial = new URL(this.initialAppUrl);
389
+ const initialPort = initial.port || (initial.protocol === "https:" ? "443" : "80");
390
+ if (urlObj.protocol === initial.protocol && hostname === initial.hostname && port === initialPort) {
391
+ return true;
392
+ }
393
+ }
293
394
  // Only monitor localhost/127.0.0.1 (the user's local dev server)
294
395
  const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0";
295
396
  if (!isLocalhost) {
@@ -307,10 +408,15 @@ export class CDPMonitor {
307
408
  }
308
409
  }
309
410
  async start() {
310
- // Launch Chrome with CDP enabled
311
- this.debugLog("Starting Chrome launch process");
312
- await this.launchChrome();
313
- this.debugLog("Chrome launch completed");
411
+ if (this.externalCdpBase) {
412
+ this.debugLog(`Using external CDP endpoint: ${this.externalCdpBase}`);
413
+ }
414
+ else {
415
+ // Launch Chrome with CDP enabled
416
+ this.debugLog("Starting Chrome launch process");
417
+ await this.launchChrome();
418
+ this.debugLog("Chrome launch completed");
419
+ }
314
420
  // Connect to Chrome DevTools Protocol
315
421
  this.debugLog("Starting CDP connection");
316
422
  await this.connectToCDP();
@@ -323,6 +429,10 @@ export class CDPMonitor {
323
429
  this.debugLog("Setting up CDP event handlers");
324
430
  this.setupEventHandlers();
325
431
  this.debugLog("CDP event handlers setup completed");
432
+ if (this.externalCdpBase && this.initialAppUrl) {
433
+ this.debugLog(`Navigating external browser to ${this.initialAppUrl}`);
434
+ await this.navigateToUrl(this.initialAppUrl);
435
+ }
326
436
  }
327
437
  getCdpUrl() {
328
438
  return this.cdpUrl;
@@ -438,10 +548,21 @@ export class CDPMonitor {
438
548
  */
439
549
  async killExistingChromeWithProfile() {
440
550
  try {
441
- // Find Chrome processes using this profile directory
551
+ // Build a set of PIDs that must never be killed: this Node process and
552
+ // its parent. d3k's own argv contains the profile path (via --profile-dir),
553
+ // which previously caused a substring match here and made d3k SIGTERM itself.
554
+ const selfPids = new Set();
555
+ if (typeof process.pid === "number")
556
+ selfPids.add(process.pid);
557
+ if (typeof process.ppid === "number")
558
+ selfPids.add(process.ppid);
559
+ // Find Chrome processes using this profile directory. We only match the
560
+ // canonical `--user-data-dir=<profile>` form Chrome consumes; matching the
561
+ // bare path was too loose and caught unrelated processes (including d3k itself).
442
562
  const processes = await this.listProcesses();
443
563
  const pids = processes
444
- .filter((proc) => proc.command.includes(`--user-data-dir=${this.profileDir}`) || proc.command.includes(this.profileDir))
564
+ .filter((proc) => !selfPids.has(proc.pid))
565
+ .filter((proc) => proc.command.includes(`--user-data-dir=${this.profileDir}`))
445
566
  .map((proc) => proc.pid)
446
567
  .filter((pid) => pid !== this.browser?.pid);
447
568
  for (const pid of pids) {
@@ -465,6 +586,10 @@ export class CDPMonitor {
465
586
  async launchChrome() {
466
587
  // Kill any existing Chrome using this profile to prevent CDP conflicts
467
588
  await this.killExistingChromeWithProfile();
589
+ const resetProfileFiles = resetChromeCrashRestoreState(this.profileDir);
590
+ if (resetProfileFiles > 0) {
591
+ this.debugLog(`Reset Chrome crash restore state in ${resetProfileFiles} profile file(s)`);
592
+ }
468
593
  return new Promise((resolve, reject) => {
469
594
  // Use custom browser path if provided, otherwise try different Chrome executables based on platform
470
595
  const chromeCommands = this.browserPath
@@ -474,6 +599,8 @@ export class CDPMonitor {
474
599
  "google-chrome",
475
600
  "chrome",
476
601
  "chromium",
602
+ "brave",
603
+ "brave-browser",
477
604
  "/Applications/Arc.app/Contents/MacOS/Arc",
478
605
  "/Applications/Comet.app/Contents/MacOS/Comet",
479
606
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
@@ -513,8 +640,7 @@ export class CDPMonitor {
513
640
  "--disable-sync",
514
641
  "--metrics-recording-only",
515
642
  "--disable-default-apps",
516
- "--disable-session-crashed-bubble",
517
- "--disable-restore-session-state"
643
+ ...CHROME_CRASH_RESTORE_SUPPRESSION_FLAGS
518
644
  ];
519
645
  if (shouldEnableReactDevTools && reactDevToolsExtensionPath) {
520
646
  chromeArgs.push(`--disable-extensions-except=${reactDevToolsExtensionPath}`);
@@ -625,19 +751,20 @@ export class CDPMonitor {
625
751
  });
626
752
  }
627
753
  async connectToCDP() {
628
- this.debugLog(`Attempting to connect to CDP on port ${this.debugPort}`);
754
+ const cdpBase = this.externalCdpBase || `http://localhost:${this.debugPort}`;
755
+ this.debugLog(`Attempting to connect to CDP at ${cdpBase}`);
629
756
  // Retry connection with exponential backoff
630
757
  let retryCount = 0;
631
758
  const maxRetries = 5;
632
759
  while (retryCount < maxRetries) {
633
760
  try {
634
761
  // Get the WebSocket URL from Chrome's debug endpoint
635
- const targetsResponse = await fetch(`http://localhost:${this.debugPort}/json`);
762
+ const targetsResponse = await fetch(`${cdpBase}/json`);
636
763
  let targets = (await targetsResponse.json());
637
764
  this.debugLog(`Found ${targets.length} targets: ${JSON.stringify(targets.map((target) => ({ type: target.type ?? "unknown", url: target.url ?? "" })))}`);
638
765
  if (targets.length === 0) {
639
766
  this.debugLog("No debuggable targets found; creating a blank page target");
640
- const newTargetResponse = await fetch(`http://localhost:${this.debugPort}/json/new?about:blank`, {
767
+ const newTargetResponse = await fetch(`${cdpBase}/json/new?about:blank`, {
641
768
  method: "PUT"
642
769
  });
643
770
  if (newTargetResponse.ok) {
@@ -704,6 +831,14 @@ export class CDPMonitor {
704
831
  // Use a small delay to distinguish between temporary reconnects and permanent failures
705
832
  setTimeout(() => {
706
833
  if (!this.isShuttingDown && this.onWindowClosedCallback) {
834
+ if (this.externalCdpBase) {
835
+ this.debugLog("External CDP connection lost - attempting recovery");
836
+ this.logger("browser", "[CDP] Attempting to recover external CDP connection");
837
+ this.attemptReconnect().catch((err) => {
838
+ this.logger("browser", `[CDP] Reconnection failed: ${err.message}`);
839
+ });
840
+ return;
841
+ }
707
842
  // Check if Chrome process is still alive
708
843
  if (!this.browser || this.browser.killed || !this.browser.pid) {
709
844
  this.debugLog("Chrome process is dead and CDP connection lost, triggering d3k shutdown");
@@ -1774,49 +1909,141 @@ export class CDPMonitor {
1774
1909
  browser.once("exit", onExit);
1775
1910
  });
1776
1911
  }
1912
+ async sendBrowserCloseCommand() {
1913
+ try {
1914
+ await this.sendCDPCommand("Browser.close", {}, 3000);
1915
+ this.debugLog("Sent Browser.close command");
1916
+ return;
1917
+ }
1918
+ catch (error) {
1919
+ this.debugLog(`Browser.close on page target failed: ${error}`);
1920
+ }
1921
+ const versionResponse = await fetch(`http://localhost:${this.debugPort}/json/version`, {
1922
+ signal: AbortSignal.timeout(1000)
1923
+ });
1924
+ if (!versionResponse.ok) {
1925
+ throw new Error(`Failed to get browser CDP endpoint: HTTP ${versionResponse.status}`);
1926
+ }
1927
+ const version = (await versionResponse.json());
1928
+ if (!version.webSocketDebuggerUrl) {
1929
+ throw new Error("Browser CDP endpoint did not include webSocketDebuggerUrl");
1930
+ }
1931
+ await new Promise((resolve, reject) => {
1932
+ const ws = new WebSocket(version.webSocketDebuggerUrl);
1933
+ let commandSent = false;
1934
+ let settled = false;
1935
+ const settle = (callback) => {
1936
+ if (settled)
1937
+ return;
1938
+ settled = true;
1939
+ clearTimeout(timeout);
1940
+ try {
1941
+ ws.close();
1942
+ }
1943
+ catch {
1944
+ // Ignore close errors.
1945
+ }
1946
+ callback();
1947
+ };
1948
+ const timeout = setTimeout(() => {
1949
+ settle(() => reject(new Error("Browser.close command timed out")));
1950
+ }, 3000);
1951
+ ws.on("open", () => {
1952
+ commandSent = true;
1953
+ ws.send(JSON.stringify({ id: 1, method: "Browser.close", params: {} }), (error) => {
1954
+ if (error) {
1955
+ settle(() => reject(error));
1956
+ }
1957
+ });
1958
+ });
1959
+ ws.on("message", (data) => {
1960
+ try {
1961
+ const message = JSON.parse(data.toString());
1962
+ if (message.id !== 1) {
1963
+ return;
1964
+ }
1965
+ const responseError = message.error;
1966
+ if (responseError) {
1967
+ settle(() => reject(new Error(responseError.message || "Browser.close failed")));
1968
+ return;
1969
+ }
1970
+ settle(resolve);
1971
+ }
1972
+ catch (error) {
1973
+ settle(() => reject(error instanceof Error ? error : new Error(String(error))));
1974
+ }
1975
+ });
1976
+ ws.on("error", (error) => {
1977
+ settle(() => reject(error));
1978
+ });
1979
+ ws.on("close", () => {
1980
+ if (commandSent) {
1981
+ settle(resolve);
1982
+ }
1983
+ else {
1984
+ settle(() => reject(new Error("Browser CDP websocket closed before Browser.close was sent")));
1985
+ }
1986
+ });
1987
+ });
1988
+ this.debugLog("Sent Browser.close command via browser CDP endpoint");
1989
+ }
1777
1990
  async shutdown() {
1778
1991
  this.isShuttingDown = true;
1779
1992
  let browserClosedCleanly = false;
1993
+ if (this.externalCdpBase) {
1994
+ if (this.connection) {
1995
+ try {
1996
+ this.connection.ws.close();
1997
+ }
1998
+ catch (_e) {
1999
+ // Ignore close errors
2000
+ }
2001
+ this.connection = null;
2002
+ }
2003
+ this.chromePids.clear();
2004
+ return;
2005
+ }
1780
2006
  // Ask the browser process to exit first so Chrome doesn't think it crashed.
1781
- if (this.connection?.sessionId) {
2007
+ if (this.connection) {
1782
2008
  try {
1783
- await this.sendCDPCommand("Browser.close");
1784
- this.debugLog("Sent Browser.close command");
1785
- browserClosedCleanly = await this.waitForBrowserExit(2000);
2009
+ await this.sendBrowserCloseCommand();
2010
+ browserClosedCleanly = await this.waitForBrowserExit(5000);
1786
2011
  }
1787
2012
  catch (_e) {
1788
2013
  this.debugLog("Browser.close failed, trying page/tab close fallback");
1789
2014
  }
1790
- try {
1791
- // Try to close the page
1792
- await this.sendCDPCommand("Page.close");
1793
- this.debugLog("Sent Page.close command");
1794
- await new Promise((resolve) => setTimeout(resolve, 100));
1795
- }
1796
- catch (_e) {
1797
- this.debugLog("Page.close failed, trying Target.closeTarget");
1798
- }
1799
- try {
1800
- // Get the list of targets to find our specific tab
1801
- const targets = (await this.sendCDPCommand("Target.getTargets"));
1802
- this.debugLog(`Found ${targets.targetInfos?.length || 0} targets`);
1803
- // Find our page target
1804
- const pageTarget = targets.targetInfos?.find((t) => t.type === "page");
1805
- if (pageTarget) {
1806
- this.debugLog(`Closing page target: ${pageTarget.targetId}`);
1807
- await this.sendCDPCommand("Target.closeTarget", {
1808
- targetId: pageTarget.targetId
1809
- });
1810
- this.debugLog("Closed Chrome tab via CDP");
1811
- }
1812
- // Give it more time for the tab to close
1813
- await new Promise((resolve) => setTimeout(resolve, 1000));
1814
- }
1815
- catch (_e) {
1816
- this.debugLog("Failed to close tab via CDP, will force close Chrome");
1817
- }
1818
2015
  if (!browserClosedCleanly) {
1819
- browserClosedCleanly = await this.waitForBrowserExit(1500);
2016
+ try {
2017
+ // Try to close the page
2018
+ await this.sendCDPCommand("Page.close");
2019
+ this.debugLog("Sent Page.close command");
2020
+ await new Promise((resolve) => setTimeout(resolve, 100));
2021
+ }
2022
+ catch (_e) {
2023
+ this.debugLog("Page.close failed, trying Target.closeTarget");
2024
+ }
2025
+ try {
2026
+ // Get the list of targets to find our specific tab
2027
+ const targets = (await this.sendCDPCommand("Target.getTargets"));
2028
+ this.debugLog(`Found ${targets.targetInfos?.length || 0} targets`);
2029
+ // Find our page target
2030
+ const pageTarget = targets.targetInfos?.find((t) => t.type === "page");
2031
+ if (pageTarget) {
2032
+ this.debugLog(`Closing page target: ${pageTarget.targetId}`);
2033
+ await this.sendCDPCommand("Target.closeTarget", {
2034
+ targetId: pageTarget.targetId
2035
+ });
2036
+ this.debugLog("Closed Chrome tab via CDP");
2037
+ }
2038
+ // Give it more time for the tab to close
2039
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2040
+ }
2041
+ catch (_e) {
2042
+ this.debugLog("Failed to close tab via CDP, will force close Chrome");
2043
+ }
2044
+ if (!browserClosedCleanly) {
2045
+ browserClosedCleanly = await this.waitForBrowserExit(1500);
2046
+ }
1820
2047
  }
1821
2048
  }
1822
2049
  // Close CDP connection