chrome-cdp-manager 1.2.2 → 1.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-cdp-manager",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "Set up and drive a Chrome DevTools Protocol (CDP) instance on macOS or Windows through a dedicated launcher (consistent Dock/taskbar icon). Works with any Chromium-based browser — Chrome, Edge, Brave, Chromium, Vivaldi, Opera, Arc.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/commands.js CHANGED
@@ -136,20 +136,30 @@ export async function htmlCommand(url, opts) {
136
136
  const target = normalizeUrl(url);
137
137
 
138
138
  ensureLauncherReady(config);
139
- await ensureBrowserRunning(config, { headless, timeoutMs });
139
+ const { launched } = await ensureBrowserRunning(config, { headless, timeoutMs });
140
140
 
141
141
  console.error(`Fetching HTML for ${target} ...`);
142
- const html = await getPageHtml(config.cdpPort, target, {
143
- close: Boolean(opts.close),
144
- timeoutMs,
145
- });
146
-
147
- if (opts.output) {
148
- const outPath = path.resolve(opts.output);
149
- fs.writeFileSync(outPath, html);
150
- console.error(`Saved HTML to ${outPath}`);
151
- } else {
152
- process.stdout.write(html.endsWith("\n") ? html : `${html}\n`);
142
+ try {
143
+ const html = await getPageHtml(config.cdpPort, target, {
144
+ close: !launched || Boolean(opts.close),
145
+ timeoutMs,
146
+ });
147
+
148
+ if (opts.output) {
149
+ const outPath = path.resolve(opts.output);
150
+ fs.writeFileSync(outPath, html);
151
+ console.error(`Saved HTML to ${outPath}`);
152
+ } else {
153
+ process.stdout.write(html.endsWith("\n") ? html : `${html}\n`);
154
+ }
155
+ } finally {
156
+ if (launched && headless) {
157
+ try {
158
+ await closeBrowser(config.cdpPort);
159
+ } catch (err) {
160
+ // ignore errors during shutdown
161
+ }
162
+ }
153
163
  }
154
164
  }
155
165
 
@@ -3,6 +3,8 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawn, execFileSync } from "node:child_process";
5
5
 
6
+ import { getWindowSizeFromProfile } from "../preferences.js";
7
+
6
8
  /**
7
9
  * macOS launcher: a real `.app` bundle whose executable is a small bash
8
10
  * launcher. Headed launches go through `open <bundle>` so LaunchServices
@@ -164,7 +166,13 @@ export function ensure(config, { force = false } = {}) {
164
166
  export function launch(config, { headless } = {}) {
165
167
  if (headless) {
166
168
  const { executable } = bundleLayout(config.launcherPath);
167
- const child = spawn(executable, ["--headless=new"], {
169
+ const args = ["--headless=new"];
170
+ const size = getWindowSizeFromProfile(config.profileDir);
171
+ if (size) {
172
+ args.push(`--window-size=${size.width},${size.height}`);
173
+ args.push(`--screen-info={0,0 ${size.width}x${size.height}}`);
174
+ }
175
+ const child = spawn(executable, args, {
168
176
  detached: true,
169
177
  stdio: "ignore",
170
178
  });
@@ -3,6 +3,8 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawn, execFileSync } from "node:child_process";
5
5
 
6
+ import { getWindowSizeFromProfile } from "../preferences.js";
7
+
6
8
  /**
7
9
  * Windows launcher: a Start Menu `.lnk` shortcut with a custom icon and the CDP
8
10
  * flags baked into its arguments. Headed launches go through the shortcut so
@@ -86,13 +88,19 @@ export function ensure(config, { force = false } = {}) {
86
88
  */
87
89
  export function launch(config, { headless } = {}) {
88
90
  if (headless) {
91
+ const args = [
92
+ `--remote-debugging-port=${config.cdpPort}`,
93
+ `--user-data-dir=${config.profileDir}`,
94
+ "--headless=new",
95
+ ];
96
+ const size = getWindowSizeFromProfile(config.profileDir);
97
+ if (size) {
98
+ args.push(`--window-size=${size.width},${size.height}`);
99
+ args.push(`--screen-info={0,0 ${size.width}x${size.height}}`);
100
+ }
89
101
  const child = spawn(
90
102
  config.browserPath,
91
- [
92
- `--remote-debugging-port=${config.cdpPort}`,
93
- `--user-data-dir=${config.profileDir}`,
94
- "--headless=new",
95
- ],
103
+ args,
96
104
  { detached: true, stdio: "ignore" },
97
105
  );
98
106
  child.unref();
@@ -0,0 +1,29 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Reads the last closed window dimensions from the Chrome profile Preferences file.
6
+ * @param {string} profileDir
7
+ * @returns {{ width: number, height: number, maximized: boolean } | null}
8
+ */
9
+ export function getWindowSizeFromProfile(profileDir) {
10
+ try {
11
+ const preferencesPath = path.join(profileDir, "Default", "Preferences");
12
+ if (fs.existsSync(preferencesPath)) {
13
+ const content = fs.readFileSync(preferencesPath, "utf8");
14
+ const prefs = JSON.parse(content);
15
+ const placement = prefs?.browser?.window_placement;
16
+ if (placement) {
17
+ const { left, top, right, bottom, maximized } = placement;
18
+ const width = right - left;
19
+ const height = bottom - top;
20
+ if (typeof width === "number" && typeof height === "number" && width > 0 && height > 0) {
21
+ return { width, height, maximized: Boolean(maximized) };
22
+ }
23
+ }
24
+ }
25
+ } catch {
26
+ // Ignore and fallback
27
+ }
28
+ return null;
29
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reads the last closed window dimensions from the Chrome profile Preferences file.
3
+ * @param {string} profileDir
4
+ * @returns {{ width: number, height: number, maximized: boolean } | null}
5
+ */
6
+ export function getWindowSizeFromProfile(profileDir: string): {
7
+ width: number;
8
+ height: number;
9
+ maximized: boolean;
10
+ } | null;