@remcp/runtime 0.2.149 → 0.2.150

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.150 — 2026-10-15
4
+
5
+ - Read-only browser tab discovery and semantic snapshots no longer auto-launch a separate Chromium browser when CDP is unavailable.
6
+ - Give a clear Accessibility fallback (`ui_snapshot` / `ui_find`) without changing existing typed `structuredContent` for empty and non-empty lists.
7
+ - Keep explicit navigation as the sole browser auto-launch entry point, with existing CDP loopback and target safety controls.
8
+ - Add five regression tests. No new MCP tools or schemas; the 83-operation runtime catalog is preserved.
9
+
10
+
3
11
  ## 0.2.149 — 2026-10-14
4
12
 
5
13
  - Automatically snapshot raw, local-only Chromium session files on crashed ReMCP CDP profiles before browser auto-launch; preserve orphaned tabs and prior snapshots without leaking URLs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.149",
3
+ "version": "0.2.150",
4
4
  "description": "First-party ReMCP local device runtime: filesystem, terminal, computer-use, browser, diagnostics and document tools over MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -414,8 +414,22 @@ export async function filterBrowserTargets(list, { validate = assertResolvedPage
414
414
 
415
415
  export async function listBrowserTargets(endpoint) {
416
416
  assertBrowserRemoteEnabled();
417
- await ensureBrowserEndpoint(endpoint);
418
- return filterBrowserTargets(await requestJson('/json/list', endpoint));
417
+ // Reading tabs and page semantics must not launch a separate Chromium/profile.
418
+ // Only an explicit browser_navigate(action=new_tab) can bootstrap an isolated browser.
419
+ const base = endpointUrl(endpoint); // keeps the CDP address loopback-only
420
+ let list;
421
+ try {
422
+ list = await requestJson('/json/list', endpoint, { timeout_ms: 1500 });
423
+ } catch (error) {
424
+ const message = String(error?.message || '');
425
+ // Keep HTTP/policy/oversize/malformed-response errors distinct from an unavailable CDP.
426
+ if (!/fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|abort|timed out|network error/i.test(message)) throw error;
427
+ const configured = Boolean(endpoint || process.env.REMCP_CDP_URL);
428
+ const label = configured ? 'The configured CDP endpoint' : 'Browser CDP endpoint';
429
+ throw new Error(`${label} is unavailable at ${base.origin}. Read-only browser_tabs and browser_snapshot do not launch a browser. Use ui_snapshot and ui_find for the existing browser window; do not retry this CDP endpoint unchanged. Only browser_navigate(action=new_tab), when explicitly requested, may start a local isolated browser.`);
430
+ }
431
+ // Retain the current bounded concurrent DNS/security checks for up to 1000 tabs.
432
+ return filterBrowserTargets(list);
419
433
  }
420
434
 
421
435
  function validatedTargetWebSocketUrl(target, endpoint) {
@@ -855,12 +869,18 @@ async function callPageFunction(session, functionDeclaration, args = [], awaitPr
855
869
 
856
870
  export async function browserTabs(args = {}) {
857
871
  const targets = await listBrowserTargets(args.endpoint);
858
- return jsonResult(targets.filter(item => item.type === 'page').slice(0, 500).map(item => ({
872
+ const pages = targets.filter(item => item.type === 'page').slice(0, 500).map(item => ({
859
873
  id: boundedText(item.id, 256),
860
874
  title: boundedText(item.title, 1000),
861
875
  url: boundedText(item.url, 4096),
862
876
  description: boundedText(item.description, 1000),
863
- })));
877
+ }));
878
+ const result = jsonResult(pages);
879
+ // Keep the typed empty-array contract while providing a native fallback hint.
880
+ if (!pages.length) {
881
+ result.content = [{ type: 'text', text: 'CDP is connected, but no debuggable Chromium tabs are available. Use ui_snapshot and ui_find to inspect existing browser windows without CDP; only open a new tab when explicitly requested.' }];
882
+ }
883
+ return result;
864
884
  }
865
885
 
866
886
  async function waitForHistoryReady(session, expectedUrl, timeoutMs) {