@remcp/runtime 0.2.70 → 0.2.72

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.70",
3
+ "version": "0.2.72",
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",
@@ -583,8 +583,28 @@ while queue:
583
583
  for i in range(e.childCount):queue.append((e.getChildAtIndex(i),app_name,pid,window_name,node_path+"."+str(i)))
584
584
  except:pass
585
585
  if target is None:raise Exception("UI element not found")
586
- if action in ("click","invoke","select","toggle"):
587
- target.queryAction().doAction(0)
586
+ pointer_fallback=None
587
+ if action in ("click","invoke"):
588
+ try:
589
+ applied=target.queryAction().doAction(0)
590
+ if applied is False: raise Exception("accessibility action returned false")
591
+ except Exception as action_error:
592
+ try:
593
+ extents=target.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
594
+ def _rect_value(rect,name,index):
595
+ try: return int(getattr(rect,name))
596
+ except:
597
+ try: return int(rect[index])
598
+ except: return 0
599
+ px=_rect_value(extents,"x",0);py=_rect_value(extents,"y",1)
600
+ pw=_rect_value(extents,"width",2);ph=_rect_value(extents,"height",3)
601
+ if pw<=0 or ph<=0: raise Exception("target has no usable screen bounds")
602
+ pointer_fallback={"x":px+pw//2,"y":py+ph//2,"width":pw,"height":ph,"reason":str(action_error)}
603
+ except Exception as bounds_error:
604
+ raise Exception("accessibility action failed: %s; coordinate fallback unavailable: %s"%(action_error,bounds_error))
605
+ elif action in ("select","toggle"):
606
+ applied=target.queryAction().doAction(0)
607
+ if applied is False: raise Exception(action+" accessibility action returned false")
588
608
  elif action=="focus":
589
609
  if target.queryComponent().grabFocus() is False: raise Exception("focus accessibility action returned false")
590
610
  elif action=="set_value":
@@ -618,10 +638,28 @@ elif action in ("add_to_selection","remove_from_selection"):
618
638
  if not selection.selectChild(idx): raise Exception("could not add item to selection")
619
639
  else:
620
640
  if not selection.deselectChild(idx): raise Exception("could not remove item from selection")
621
- print(json.dumps({"action":action,"name":target.name or "","role":target.getRoleName(),**target_meta}))`;
641
+ out={"action":action,"name":target.name or "","role":target.getRoleName(),**target_meta}
642
+ if pointer_fallback is not None: out["pointer_fallback"]=pointer_fallback
643
+ print(json.dumps(out))`;
622
644
  const result = await runFile('python3', ['-c', script], { label: 'AT-SPI action', timeout: 30_000, allowFailure: true });
623
645
  let payload = null;
624
646
  try { payload = JSON.parse(result.stdout || ''); } catch {}
647
+ if (payload?.pointer_fallback) {
648
+ const fallback = payload.pointer_fallback;
649
+ const x = Number(fallback.x), y = Number(fallback.y);
650
+ if (!Number.isFinite(x) || !Number.isFinite(y)) unavailable('Linux accessibility action', 'coordinate fallback returned invalid screen bounds');
651
+ await pointer({ action:'click', x, y, ...(args.backend ? { backend:args.backend } : {}) });
652
+ return jsonResult({
653
+ action,
654
+ id:id || null,
655
+ name:payload.name || '',
656
+ role:payload.role || '',
657
+ backend:'pointer_fallback',
658
+ x,
659
+ y,
660
+ fallback_reason:String(fallback.reason || 'accessibility action unavailable').slice(0, 500),
661
+ });
662
+ }
625
663
  if (payload && !payload.error) return text(JSON.stringify(payload));
626
664
  if (payload?.error) unavailable('Linux accessibility action', payload.error);
627
665
  if (result.code !== 0) {