@remcp/runtime 0.2.164 → 0.2.165
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,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.165 — 2026-10-18
|
|
4
|
+
|
|
5
|
+
**Crash-session recovery without losing browser automation when the local snapshot budget fills.**
|
|
6
|
+
|
|
7
|
+
- When the dedicated Chromium profile already has 12 interrupted-session backups, preserve its tabs and all evidence and start a single, owner-only isolated browser profile instead of failing to launch.
|
|
8
|
+
- Reuse the isolated profile across subsequent starts; never copy credentials or account state. Emit a local warning that sign-in state is not shared.
|
|
9
|
+
- Still fail closed on unsafe symlinks, oversized evidence, unowned recovery directories or exhausted isolated-profile recovery capacity. No automatic deletion of backups.
|
|
10
|
+
- Keep 83 runtime operations, the ten hosted MCP definitions, URL/DNS/SSRF checks and authorization boundaries unchanged. Add regression tests for full-capacity and unsafe-profile cases.
|
|
11
|
+
|
|
3
12
|
## 0.2.154 — 2026-10-17
|
|
4
13
|
|
|
5
14
|
**ReMCP Gray v2 brand parity release.** The runtime tool contract remains at 83 operations with unchanged permissions and I/O semantics. The coordinated public client and OpenAI submission assets receive the owner-provided gray identity.
|
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ discovery for new clients.
|
|
|
64
64
|
|
|
65
65
|
For browser typing, `browser_text` is the visible DOM locator while `text` is the Unicode payload. `browser_action(action="scroll")` accepts page/element deltas or direction/wheel steps and scrolls the page or nearest scrollable ancestor; `scroll_into_view` remains the separate semantic reveal operation.
|
|
66
66
|
|
|
67
|
-
When ReMCP auto-launches Chrome/Edge for local CDP, it uses a dedicated ReMCP browser profile. If that browser was interrupted (for example during an agent upgrade), and Chromium has saved tabs in that profile, the next launch requests Chromium's own last-session restoration instead of opening a new `about:blank` tab. A clean browser shutdown never triggers automatic reopening. This inspects only local session metadata, never reads or transmits tab URLs to ReMCP's relay. The browser itself controls which tabs can actually be recovered. On an interrupted profile, ReMCP now saves a private, local-only snapshot of `Session_*` and `Tabs_*` files before Chromium can rotate them on startup, even when only tab files remain. Recovery snapshots are adjacent to the dedicated browser profile in a `browser-cdp-recovery/` directory (owner-only on Unix); existing snapshots are never deleted automatically. Copy limits are 16 regular files, 32 MiB per file, 64 MiB per snapshot and 12 distinct snapshots. If evidence
|
|
67
|
+
When ReMCP auto-launches Chrome/Edge for local CDP, it uses a dedicated ReMCP browser profile. If that browser was interrupted (for example during an agent upgrade), and Chromium has saved tabs in that profile, the next launch requests Chromium's own last-session restoration instead of opening a new `about:blank` tab. A clean browser shutdown never triggers automatic reopening. This inspects only local session metadata, never reads or transmits tab URLs to ReMCP's relay. The browser itself controls which tabs can actually be recovered. On an interrupted profile, ReMCP now saves a private, local-only snapshot of `Session_*` and `Tabs_*` files before Chromium can rotate them on startup, even when only tab files remain. Recovery snapshots are adjacent to the dedicated browser profile in a `browser-cdp-recovery/` directory (owner-only on Unix); existing snapshots are never deleted automatically. Copy limits are 16 regular files, 32 MiB per file, 64 MiB per snapshot and 12 distinct snapshots. If the primary profile reaches the 12-snapshot limit, ReMCP switches to one separate owner-only isolated browser profile rather than overwriting prior recovery evidence. The original profile, its tabs and backups remain on disk; credentials and sign-in state are **not** copied into the isolated profile, and a warning is logged. Subsequent launches reuse that isolated profile. If either profile has unsafe evidence (for example symlinks or oversized files) or the isolated profile also reaches its recovery limit, browser auto-launch fails closed. No session URLs are parsed, returned through tools, or uploaded. You can review and manage those local snapshots yourself.
|
|
68
68
|
|
|
69
69
|
For frontend QA, use semantic data to operate the page and rendered pixels to verify it: `browser_action(action="set_viewport")` sets an exact responsive-test viewport, `browser_wait` waits for the real application state, and `browser_snapshot(include_screenshot=true, selector=...)` recenters the component, returns its bounds, and attaches the real CDP-rendered viewport PNG. This intentionally treats screenshots as the visual oracle without making screenshot-driven coordinate guessing the primary control path.
|
|
70
70
|
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { isIP } from 'node:net';
|
|
|
5
5
|
import { lookup } from 'node:dns/promises';
|
|
6
6
|
import { closeSync, constants as fsConstants, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readdirSync } from 'node:fs';
|
|
7
7
|
import { resolveSafePath } from '../util.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import { selectSafeBrowserLaunchProfile } from './browser-session-backup.mjs';
|
|
9
9
|
import { shareBrowserDiscovery } from './browser-discovery-flight.mjs';
|
|
10
10
|
import { clamp, commandExists, jsonResult, optionalString, requireEnum, spawnDetached, unavailable } from './common.mjs';
|
|
11
11
|
|
|
@@ -154,8 +154,9 @@ export async function ensureBrowserEndpoint(endpoint) {
|
|
|
154
154
|
mkdirSync(profile, { recursive:true, mode:0o700 });
|
|
155
155
|
// Chrome rotates its Sessions/ files on startup. Protect existing local
|
|
156
156
|
// evidence before a restart, including profiles with only Tabs_* left.
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
const selected = selectSafeBrowserLaunchProfile(profile);
|
|
158
|
+
if (selected.isolated) console.warn('ReMCP browser: using an isolated recovery profile; existing session and backups are preserved, and sign-in state is not copied.');
|
|
159
|
+
spawnDetached(executable, browserAutoLaunchArgs(selected.profile));
|
|
159
160
|
if (!await waitForBrowserEndpoint(5000)) throw new Error('ReMCP launched a browser but its local CDP endpoint did not become ready');
|
|
160
161
|
})().finally(() => { browserLaunchPromise = null; });
|
|
161
162
|
}
|
|
@@ -42,7 +42,8 @@ function safeDirectory(dir) {
|
|
|
42
42
|
if (process.platform === 'win32' && lstatSync(dir).isSymbolicLink()) throw new Error('Unsafe recovery directory');
|
|
43
43
|
const fd = openSync(dir, constants.O_RDONLY | DIRECTORY | NOFOLLOW);
|
|
44
44
|
try {
|
|
45
|
-
|
|
45
|
+
const stat = fstatSync(fd);
|
|
46
|
+
if (!stat.isDirectory() || (process.platform !== 'win32' && typeof process.getuid === 'function' && stat.uid !== process.getuid())) throw new Error('Unsafe recovery directory');
|
|
46
47
|
if (process.platform !== 'win32') fchmodSync(fd, 0o700);
|
|
47
48
|
} finally { closeSync(fd); }
|
|
48
49
|
}
|
|
@@ -133,3 +134,28 @@ export function preserveInterruptedBrowserSession(profile) {
|
|
|
133
134
|
}
|
|
134
135
|
} finally { if (dirFd !== undefined) closeSync(dirFd); }
|
|
135
136
|
}
|
|
137
|
+
|
|
138
|
+
// Never overwrite an interrupted Chromium profile when its recovery evidence
|
|
139
|
+
// is at capacity. One sibling profile keeps automation available while
|
|
140
|
+
// retaining the original profile and all snapshot files untouched.
|
|
141
|
+
export function selectSafeBrowserLaunchProfile(profile) {
|
|
142
|
+
const isolated = profile + '-isolated';
|
|
143
|
+
let prior = false;
|
|
144
|
+
try { lstatSync(isolated); prior = true; }
|
|
145
|
+
catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
|
146
|
+
if (prior) {
|
|
147
|
+
safeDirectory(isolated);
|
|
148
|
+
preserveInterruptedBrowserSession(isolated);
|
|
149
|
+
return { profile: isolated, isolated: true };
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
preserveInterruptedBrowserSession(profile);
|
|
153
|
+
return { profile, isolated: false };
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error?.message !== 'Session recovery snapshot limit reached') throw error;
|
|
156
|
+
// Never prune user-owned session recovery evidence.
|
|
157
|
+
safeDirectory(isolated);
|
|
158
|
+
preserveInterruptedBrowserSession(isolated);
|
|
159
|
+
return { profile: isolated, isolated: true };
|
|
160
|
+
}
|
|
161
|
+
}
|