@mefistofelix/cdp.js 0.5.0 → 0.5.1

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 CHANGED
@@ -542,8 +542,8 @@ await client.call({
542
542
  Provide `user_data_dir` or raw `args['user-data-dir']` to choose a custom
543
543
  directory. An effective custom directory makes `cdp_ext_install` default to true:
544
544
  the manager copies the two extension files into
545
- `<user-data-dir>/<profile-directory>/cdp_ext/` (`Default` when unset), sets the
546
- shared server URL in that manifest and adds startup loading flags. Normal launch
545
+ `<user-data-dir>/<profile-directory>/cdp_ext/` (`Default` when unset) and adds
546
+ startup loading flags. The server address comes only from the trigger URL. Normal launch
547
547
  options and profile preferences then apply. Set `cdp_ext_install: false` to use
548
548
  an extension already installed in that profile, or true to force preparation and
549
549
  loading. A null/false raw directory override makes automatic installation default off.
@@ -568,6 +568,13 @@ and enable the bridge. An `extensions` array adds its directories;
568
568
  `extensions: true` retains enabled profile extensions through `'*'`. Raw `args`
569
569
  still win and can prevent loading or connection.
570
570
 
571
+ Both launch paths keep `--no-first-run` and
572
+ `--disable-search-engine-choice-screen`, including when opening a new custom
573
+ profile with installation disabled. This suppresses onboarding and the search
574
+ engine selection prompt without changing the chosen search provider or writing
575
+ search preferences. Raw `args` can override either flag. An existing Chrome only
576
+ receives the URL; new startup flags require restarting that instance.
577
+
571
578
  The manager opens an actual local HTTP URL on the shared listener, for example
572
579
  `http://127.0.0.1:9223/cdp_ext?browser=work&websocket_url=...&token=...`.
573
580
  The port is the effective listening port, including an automatically allocated
@@ -590,15 +597,22 @@ manager while connected returns an explicit error, preserving the existing
590
597
  connection. After disconnection, a new bootstrap can rebind it.
591
598
 
592
599
  Passive external attachment remains available through `reverse: true`: it opens
593
- no URL and waits for `extension_id` (or the browser label). Without bootstrap,
594
- the extension announces its native ID and uses its manifest endpoint. This
595
- compatibility mode is optional; `cdp_ext: true` needs neither `reverse` nor an ID.
600
+ no URL and waits for `extension_id` (or the browser label). This compatibility
601
+ mode is optional; `cdp_ext: true` needs neither `reverse` nor an ID.
602
+ The current extension still requires a trigger URL in passive mode; it never
603
+ connects automatically to a manifest endpoint or a default port.
604
+
605
+ The extension stays idle until a valid trigger URL supplies the logical name,
606
+ WebSocket endpoint and token. Before that it makes no server requests and logs
607
+ no connection messages. A worker restart may resume the same browser session's
608
+ previous trigger from `chrome.storage.session`.
596
609
 
597
- The manifest's `cdp` configuration supplies initial connection defaults:
610
+ The shipped manifest needs no custom configuration keys. An optional custom
611
+ `cdp.reconnect_ms` override remains supported (Chrome displays a harmless
612
+ unrecognized-key warning if it is added):
598
613
 
599
614
  | Manifest `cdp` key | Default | Meaning |
600
615
  | --- | --- | --- |
601
- | `websocket_url` | `ws://127.0.0.1:9223` | Initial shared endpoint; URL bootstrap overrides it. |
602
616
  | `reconnect_ms` | `5000` | Awake retry and connected heartbeat interval; use a positive value below 30000. |
603
617
 
604
618
  Construction and direct CDP calls do not start a listener. The first browser call
@@ -662,10 +676,20 @@ another connection attempt; the configured interval applies while it is awake.
662
676
  Connected empty-text heartbeats keep an otherwise idle WebSocket worker active.
663
677
  These are transport messages, not event buffering or polling of browser state.
664
678
 
679
+ After a trigger, the server may be offline normally. Before each disconnected
680
+ WebSocket attempt the extension checks the same server's HTTP `/cdp_ext` endpoint
681
+ with a two-second timeout and no cache. It opens the socket only when that
682
+ endpoint identifies CDP.js. Concurrent retries share the check; an unavailable or
683
+ incompatible server causes one informational waiting message, not a failing
684
+ WebSocket attempt on every timer tick. Successful connections and later
685
+ disconnects log their status once per transition. Chrome can still emit a native
686
+ network diagnostic if the server disappears between the HTTP check and upgrade.
687
+
665
688
  ### Reverse protocol and supported commands
666
689
 
667
- The first announcement contains `{ browser, extension_id, token }` for bootstrap,
668
- or the native extension ID for passive attachment. Subsequent messages use CDP envelopes:
690
+ The current extension's first announcement contains `{ browser, extension_id, token }`
691
+ after a trigger. The server also accepts legacy native-ID announcements.
692
+ Subsequent messages use CDP envelopes:
669
693
 
670
694
  - Request: `{ id, method, params, sessionId? }`.
671
695
  - Response: `{ id, result }` or `{ id, error: { code, message } }`.
@@ -1805,5 +1829,11 @@ already running instance, startup when absent, one shared extension directory,
1805
1829
  logical names, binding tokens, conflicting names and preservation on manager close.
1806
1830
  Installation defaults, raw directory overrides, profile writes independent of
1807
1831
  installation and shared wildcard expansion are covered without opening personal profiles.
1832
+ Argument coverage crosses direct/extension launch modes with headless, proxy,
1833
+ image/login/translation options, extension selectors and raw overrides. Profile
1834
+ combinations include default/custom directories, null/false raw directory
1835
+ overrides, forced installation and independent preference-write controls.
1836
+ Offline worker checks verify silence before a trigger, deduplicated waiting logs,
1837
+ no WebSocket attempts against absent/incompatible servers and concurrent retry checks.
1808
1838
  The npm tarball contains `package.json`, `cdp.js`, `README.md` and the two extension
1809
1839
  files required by managed reverse launches.
package/cdp.js CHANGED
@@ -153,12 +153,10 @@ export class browser {
153
153
  'disable-prompt-on-repost': true,
154
154
  'force-color-profile': 'srgb',
155
155
  'metrics-recording-only': true,
156
- 'no-first-run': true,
157
156
  'password-store': 'basic',
158
157
  'use-mock-keychain': true,
159
158
  'no-service-autorun': true,
160
159
  'export-tagged-pdf': true,
161
- 'disable-search-engine-choice-screen': true,
162
160
  'disable-features': [
163
161
  'InfiniteSessionRestore',
164
162
  'ImprovedCookieControls',
@@ -177,6 +175,8 @@ export class browser {
177
175
  'user-data-dir': options.user_data_dir,
178
176
  'remote-debugging-port': 0,
179
177
  }
178
+ args['no-first-run'] = true
179
+ args['disable-search-engine-choice-screen'] = true
180
180
  if (options.headless) {
181
181
  args.headless = 'new'
182
182
  args['window-size'] = '1440,900'
@@ -658,9 +658,7 @@ export class cdp extends EventTarget {
658
658
  await fs.mkdir(record.extension_path, { recursive: true })
659
659
  record.extension_path = await fs.realpath(record.extension_path)
660
660
  record.extension_id = browser.extension_id(record.extension_path)
661
- const manifest = JSON.parse(await fs.readFile(new URL('./cdp_ext/manifest.json', import.meta.url), 'utf8'))
662
- manifest.cdp.websocket_url = record.reverse_url
663
- await fs.writeFile(path.join(record.extension_path, 'manifest.json'), JSON.stringify(manifest))
661
+ await fs.copyFile(new URL('./cdp_ext/manifest.json', import.meta.url), path.join(record.extension_path, 'manifest.json'))
664
662
  await fs.copyFile(new URL('./cdp_ext/cdp_ext.js', import.meta.url), path.join(record.extension_path, 'cdp_ext.js'))
665
663
  }
666
664
  if (args['user-data-dir'] && record.update_preferences !== false) {
@@ -1,4 +1,6 @@
1
- const config = chrome.runtime.getManifest().cdp ?? {}
1
+ const config = {
2
+ reconnect_ms: chrome.runtime.getManifest().cdp?.reconnect_ms ?? 5000,
3
+ }
2
4
  const configured = chrome.storage.session.get('cdp_bootstrap').then(value => Object.assign(config, value.cdp_bootstrap))
3
5
  const sessions = {}
4
6
  const attached = {}
@@ -8,6 +10,14 @@ let disconnecting
8
10
  let discovering = false
9
11
  let auto_attach = {}
10
12
  let bootstrapping
13
+ let checking = false
14
+ let connection_message
15
+
16
+ function report_connection(message) {
17
+ if (connection_message === message) return
18
+ connection_message = message
19
+ console.info('[cdp_ext] ' + message)
20
+ }
11
21
 
12
22
  async function bootstrap(tab) {
13
23
  const text = tab.pendingUrl ?? tab.url ?? ''
@@ -245,15 +255,43 @@ for (const [name, options] of Object.entries({
245
255
 
246
256
  async function connect() {
247
257
  await configured
248
- if (disconnecting) return
258
+ if (!config.browser || !config.websocket_url || !config.token) return
259
+ if (disconnecting || checking) return
249
260
  if (socket?.readyState === WebSocket.CONNECTING) return
250
261
  if (socket?.readyState === WebSocket.OPEN) return socket.send('')
251
- const connection = socket = new WebSocket(config.websocket_url ?? 'ws://127.0.0.1:9223')
252
- connection.onopen = () => connection.send(JSON.stringify({
253
- browser: config.browser ?? chrome.runtime.id,
254
- extension_id: chrome.runtime.id,
255
- token: config.token,
256
- }))
262
+ const endpoint = config.websocket_url
263
+ const url = new URL(endpoint)
264
+ url.protocol = url.protocol === 'wss:' ? 'https:' : 'http:'
265
+ url.pathname = '/cdp_ext'
266
+ url.search = ''
267
+ url.hash = ''
268
+ let available = false
269
+ checking = true
270
+ try {
271
+ const response = await fetch(url, {
272
+ cache: 'no-store',
273
+ signal: AbortSignal.timeout(2000),
274
+ })
275
+ available = response.ok && await response.text() === 'CDP.js browser connection'
276
+ } catch (error) {
277
+ if (!(error instanceof TypeError) && error.name !== 'TimeoutError') throw error
278
+ } finally {
279
+ checking = false
280
+ }
281
+ if (endpoint !== config.websocket_url) return connect()
282
+ if (!available) {
283
+ report_connection('Waiting for CDP.js server at ' + endpoint + '; retrying automatically.')
284
+ return
285
+ }
286
+ const connection = socket = new WebSocket(endpoint)
287
+ connection.onopen = () => {
288
+ report_connection('Connected to CDP.js server at ' + endpoint + '.')
289
+ connection.send(JSON.stringify({
290
+ browser: config.browser,
291
+ extension_id: chrome.runtime.id,
292
+ token: config.token,
293
+ }))
294
+ }
257
295
  connection.onmessage = async event => {
258
296
  if (!event.data) return
259
297
  const request = JSON.parse(event.data)
@@ -300,6 +338,7 @@ async function connect() {
300
338
  if (id != null && connection.readyState === WebSocket.OPEN) connection.send(JSON.stringify(response))
301
339
  }
302
340
  connection.onclose = () => {
341
+ report_connection('Waiting for CDP.js server at ' + endpoint + '; retrying automatically.')
303
342
  discovering = false
304
343
  disconnecting = Promise.all(Object.keys(attached).map(id => {
305
344
  const source = { tabId: Number(id) }
@@ -323,5 +362,5 @@ async function start() {
323
362
  chrome.runtime.onStartup.addListener(start)
324
363
  chrome.alarms.onAlarm.addListener(connect)
325
364
  chrome.alarms.create('cdp-reconnect', { periodInMinutes: 0.5 })
326
- setInterval(connect, config.reconnect_ms ?? 5000)
365
+ setInterval(connect, config.reconnect_ms)
327
366
  start()
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP reverse connection",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "minimum_chrome_version": "125",
6
6
  "permissions": [
7
7
  "accessibilityFeatures.read",
@@ -68,9 +68,5 @@
68
68
  "incognito": "spanning",
69
69
  "background": {
70
70
  "service_worker": "cdp_ext.js"
71
- },
72
- "cdp": {
73
- "websocket_url": "ws://127.0.0.1:9223",
74
- "reconnect_ms": 5000
75
71
  }
76
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mefistofelix/cdp.js",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Low-level Chromium DevTools Protocol client for Node.js, Deno and Bun, with no runtime dependencies.",
5
5
  "type": "module",
6
6
  "exports": "./cdp.js",