@remcp/runtime 0.2.111 → 0.2.113
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 +10 -0
- package/package.json +1 -1
- package/src/extended/desktop-linux.mjs +10 -16
- package/src/helpers/wayland-eis-helper.py +35 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.113 — 2026-09-26
|
|
4
|
+
|
|
5
|
+
- No runtime tool names, schemas, or device-side tool implementations change in this release. The runtime package version remains in lockstep with the hosted/client release while relay heartbeat, reconnect admission, telemetry cadence, gateway capacity, and production load-safety are hardened.
|
|
6
|
+
|
|
7
|
+
## 0.2.112 — 2026-09-25
|
|
8
|
+
|
|
9
|
+
- Linux EIS absolute pointer motion converts desktop-wide coordinates into the selected libei region before dispatch and keeps the current pointer device when it remains valid, eliminating accepted-but-undelivered absolute moves.
|
|
10
|
+
- Wayland portal drag-and-drop uses one region-safe target transition while pressed, then reasserts the final target before release so `pointerdown → pointermove → pointerup` remains intact.
|
|
11
|
+
- The live computer-use smoke is serialized per user to prevent concurrent desktop-input races; the final ASUS GNOME Wayland run passed 82/82 checks. Runtime tool names and schemas are unchanged.
|
|
12
|
+
|
|
3
13
|
## 0.2.111 — 2026-09-25
|
|
4
14
|
|
|
5
15
|
- `read_document` reads `.xlsx` files again while `allowedRoots` confinement is enabled: the reader no longer re-enters its own temp staging path through the allowlist.
|
package/package.json
CHANGED
|
@@ -1270,22 +1270,16 @@ export async function dragDrop(args = {}) {
|
|
|
1270
1270
|
await portalPointerButton(button, true, portalOptions);
|
|
1271
1271
|
await new Promise(resolve => setTimeout(resolve, clamp(args.hold_ms, 120, 60, 5000)));
|
|
1272
1272
|
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
fromX + (toX - fromX) * ratio,
|
|
1284
|
-
fromY + (toY - fromY) * ratio,
|
|
1285
|
-
portalOptions,
|
|
1286
|
-
);
|
|
1287
|
-
await new Promise(resolve => setTimeout(resolve, stepDelay));
|
|
1288
|
-
}
|
|
1273
|
+
// Keep the pressed pointer on a single absolute-motion transition. Repeated
|
|
1274
|
+
// absolute moves can cross EIS region/device contexts and lose the logical
|
|
1275
|
+
// pressed state. The EIS helper converts the desktop target into region-local
|
|
1276
|
+
// coordinates, so one target move is sufficient and preserves buttons=1.
|
|
1277
|
+
await portalMoveTo(toX, toY, portalOptions);
|
|
1278
|
+
await new Promise(resolve => setTimeout(resolve, clamp(args.duration_ms, 160, 80, 5000)));
|
|
1279
|
+
// Reassert the final target immediately before release. On Mutter/XWayland this
|
|
1280
|
+
// mirrors a real pointer-up gesture and ensures the release is delivered as a
|
|
1281
|
+
// pointerup/mouseup at the dragged target instead of only clearing button state.
|
|
1282
|
+
await portalMoveTo(toX, toY, portalOptions);
|
|
1289
1283
|
await new Promise(resolve => setTimeout(resolve, 40));
|
|
1290
1284
|
await portalPointerButton(button, false, portalOptions);
|
|
1291
1285
|
await new Promise(resolve => setTimeout(resolve, 80));
|
|
@@ -216,6 +216,8 @@ class EiSender:
|
|
|
216
216
|
self.lib.ei_device_has_capability.restype = ctypes.c_bool
|
|
217
217
|
self.lib.ei_device_get_region_at.argtypes = [c_void_p, ctypes.c_double, ctypes.c_double]
|
|
218
218
|
self.lib.ei_device_get_region_at.restype = c_void_p
|
|
219
|
+
self.lib.ei_region_convert_point.argtypes = [c_void_p, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)]
|
|
220
|
+
self.lib.ei_region_convert_point.restype = ctypes.c_bool
|
|
219
221
|
self.lib.ei_device_start_emulating.argtypes = [c_void_p, ctypes.c_uint32]
|
|
220
222
|
self.lib.ei_device_stop_emulating.argtypes = [c_void_p]
|
|
221
223
|
self.lib.ei_device_keyboard_key.argtypes = [c_void_p, ctypes.c_uint32, ctypes.c_bool]
|
|
@@ -367,17 +369,46 @@ class EiSender:
|
|
|
367
369
|
return state["ptr"]
|
|
368
370
|
raise RuntimeError(f"no resumed EIS device with capability {cap}")
|
|
369
371
|
|
|
372
|
+
def _absolute_motion_for_device(self, device, x, y):
|
|
373
|
+
if not self.lib.ei_device_has_capability(device, EI_CAP_POINTER_ABSOLUTE):
|
|
374
|
+
return None
|
|
375
|
+
region = self.lib.ei_device_get_region_at(device, float(x), float(y))
|
|
376
|
+
if not region:
|
|
377
|
+
return None
|
|
378
|
+
motion_x = ctypes.c_double(float(x))
|
|
379
|
+
motion_y = ctypes.c_double(float(y))
|
|
380
|
+
if not self.lib.ei_region_convert_point(region, ctypes.byref(motion_x), ctypes.byref(motion_y)):
|
|
381
|
+
raise RuntimeError(f"failed to convert desktop coordinate ({x}, {y}) into the selected EIS region")
|
|
382
|
+
return motion_x.value, motion_y.value
|
|
383
|
+
|
|
370
384
|
def device_for_absolute(self, x, y):
|
|
371
385
|
candidates = []
|
|
386
|
+
|
|
387
|
+
# Preserve pointer identity across absolute motion whenever the current
|
|
388
|
+
# device still covers the destination. This is required for pointer
|
|
389
|
+
# capture during drags: switching EIS devices mid-gesture can drop the
|
|
390
|
+
# captured pointer before its button release is observed.
|
|
391
|
+
preferred = self.devices.get(self.pointer_device_key)
|
|
392
|
+
if preferred and preferred["resumed"] and preferred["emulating"]:
|
|
393
|
+
device = preferred["ptr"]
|
|
394
|
+
if self.lib.ei_device_has_capability(device, EI_CAP_POINTER_ABSOLUTE):
|
|
395
|
+
candidates.append(device)
|
|
396
|
+
converted = self._absolute_motion_for_device(device, x, y)
|
|
397
|
+
if converted is not None:
|
|
398
|
+
return device, converted[0], converted[1]
|
|
399
|
+
|
|
372
400
|
for state in self.devices.values():
|
|
373
401
|
if not state["resumed"] or not state["emulating"]:
|
|
374
402
|
continue
|
|
375
403
|
device = state["ptr"]
|
|
404
|
+
if preferred and self._device_key(device) == self.pointer_device_key:
|
|
405
|
+
continue
|
|
376
406
|
if not self.lib.ei_device_has_capability(device, EI_CAP_POINTER_ABSOLUTE):
|
|
377
407
|
continue
|
|
378
408
|
candidates.append(device)
|
|
379
|
-
|
|
380
|
-
|
|
409
|
+
converted = self._absolute_motion_for_device(device, x, y)
|
|
410
|
+
if converted is not None:
|
|
411
|
+
return device, converted[0], converted[1]
|
|
381
412
|
if candidates:
|
|
382
413
|
raise RuntimeError(f"no absolute-pointer EIS region contains desktop coordinate ({x}, {y})")
|
|
383
414
|
raise RuntimeError("no resumed EIS device with absolute-pointer capability")
|
|
@@ -424,8 +455,8 @@ class EiSender:
|
|
|
424
455
|
elif op == "motion_absolute":
|
|
425
456
|
x = float(payload["x"])
|
|
426
457
|
y = float(payload["y"])
|
|
427
|
-
device = self.device_for_absolute(x, y)
|
|
428
|
-
self.lib.ei_device_pointer_motion_absolute(device,
|
|
458
|
+
device, motion_x, motion_y = self.device_for_absolute(x, y)
|
|
459
|
+
self.lib.ei_device_pointer_motion_absolute(device, motion_x, motion_y)
|
|
429
460
|
self.remember_pointer(device, (x, y))
|
|
430
461
|
self.frame(device)
|
|
431
462
|
elif op == "button":
|