@remcp/runtime 0.2.81 → 0.2.83

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.83 — 2026-09-21
4
+
5
+ - Linux X11 `record_screen` capability now requires both `ffmpeg` and a real `DISPLAY`; headless hosts no longer advertise a recorder that will fail.
6
+ - The runtime no longer invents `:0.0` for headless X11 calls and fails closed with a clear display requirement before ffmpeg runs.
7
+ - `wf-recorder` is selected only for actual Wayland sessions.
8
+ - Regression coverage locks the headless capability and handler behavior; lockstep patch release at `0.2.83`.
9
+
10
+ ## 0.2.82 — 2026-09-21
11
+
12
+ - Clarified `set_config_value` routing so local runtime preferences stay separate from hosted account-level `device_action` administration.
13
+ - Runtime functionality remains at 83 release tools; this release keeps client/runtime versions in lockstep with the hosted MCP catalog consolidation.
14
+
3
15
  ## Unreleased
4
16
 
5
17
  ## 0.2.81 — 2026-09-21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.81",
3
+ "version": "0.2.83",
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",
package/src/catalog.mjs CHANGED
@@ -814,7 +814,7 @@ export const toolDefinitions = [
814
814
  {
815
815
  name: 'set_config_value',
816
816
  title: 'Change runtime setting',
817
- description: 'Change one of this local runtime’s own preferences on the selected computer: telemetryEnabled, maxReadLines, maxBufferedLines or maxOutputBytes. This does not rename the paired device; use hosted rename_device for that label. Access roots, blocked commands, shell/guardrails and the write limit remain local-only.',
817
+ description: 'Change one of this local runtime’s own preferences on the selected computer: telemetryEnabled, maxReadLines, maxBufferedLines or maxOutputBytes. This is not account-level device administration; use hosted device_action for ping, rename, enable/disable or removal. Access roots, blocked commands, shell/guardrails and the write limit remain local-only.',
818
818
  inputSchema: {
819
819
  type: 'object',
820
820
  properties: {
@@ -247,11 +247,12 @@ export async function powerAction(args) {
247
247
  export function recordScreenAvailable({
248
248
  platform = process.platform,
249
249
  wayland = platform === 'linux' ? isWaylandSession() : false,
250
+ display = process.env.DISPLAY || '',
250
251
  commandExistsFn = commandExists,
251
252
  } = {}) {
252
253
  if (platform === 'linux') {
253
254
  if (wayland) return commandExistsFn('wf-recorder') && commandExistsFn('timeout');
254
- return commandExistsFn('ffmpeg');
255
+ return Boolean(String(display).trim()) && commandExistsFn('ffmpeg');
255
256
  }
256
257
  if (platform === 'darwin' || platform === 'win32') return commandExistsFn('ffmpeg');
257
258
  return false;
@@ -261,13 +262,17 @@ export async function recordScreen(args) {
261
262
  const seconds = clamp(args.duration_seconds, 5, 1, 120);
262
263
  const fps = clamp(args.fps, 15, 1, 60);
263
264
  const destination = args.destination ? await resolveSafePath(args.destination, 'destination') : path.join(os.tmpdir(), `remcp-screen-${Date.now()}.mp4`);
264
- if (process.platform === 'linux' && commandExists('wf-recorder') && commandExists('timeout')) {
265
+ const wayland = process.platform === 'linux' && isWaylandSession();
266
+ if (wayland && commandExists('wf-recorder') && commandExists('timeout')) {
265
267
  const result = await runFile('timeout', ['--signal=INT', `${seconds}s`, 'wf-recorder', '-f',destination,'-r',String(fps),'-c','libx264'], { label:'screen recording', timeout:(seconds+10)*1000, allowFailure:true });
266
268
  if (![0, 124, 130].includes(Number(result.code))) throw new Error(result.stderr.trim() || `wf-recorder exited ${result.code}`);
267
269
  } else {
268
- if (process.platform === 'linux' && isWaylandSession()) {
270
+ if (wayland) {
269
271
  unavailable('Screen recording', 'wf-recorder and timeout are required on Wayland');
270
272
  }
273
+ if (process.platform === 'linux' && !String(process.env.DISPLAY || '').trim()) {
274
+ unavailable('Screen recording', 'an active X11 DISPLAY is required on Linux X11');
275
+ }
271
276
  if (!commandExists('ffmpeg')) {
272
277
  if (process.platform === 'darwin') unavailable('Screen recording', 'ffmpeg is required on macOS');
273
278
  if (process.platform === 'win32') unavailable('Screen recording', 'ffmpeg is required on Windows');
@@ -276,7 +281,7 @@ export async function recordScreen(args) {
276
281
  let argv;
277
282
  if (process.platform === 'win32') argv=['-y','-f','gdigrab','-framerate',String(fps),'-i','desktop','-t',String(seconds),'-pix_fmt','yuv420p',destination];
278
283
  else if (process.platform === 'darwin') argv=['-y','-f','avfoundation','-framerate',String(fps),'-i','1:none','-t',String(seconds),'-pix_fmt','yuv420p',destination];
279
- else argv=['-y','-f','x11grab','-framerate',String(fps),'-i',process.env.DISPLAY || ':0.0','-t',String(seconds),'-pix_fmt','yuv420p',destination];
284
+ else argv=['-y','-f','x11grab','-framerate',String(fps),'-i',process.env.DISPLAY,'-t',String(seconds),'-pix_fmt','yuv420p',destination];
280
285
  await runFile('ffmpeg', argv, { label:'screen recording', timeout:(seconds+20)*1000, maxBuffer:8*1024*1024 });
281
286
  }
282
287
  const info = await stat(destination);