aws-runtime-bridge 1.9.98 → 1.9.100

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.
Files changed (120) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +7 -1
  2. package/dist/adapter/AcodeSdkAdapter.js +5 -5
  3. package/dist/adapter/ClaudeSdkAdapter.d.ts +4 -0
  4. package/dist/adapter/ClaudeSdkAdapter.js +2 -2
  5. package/dist/adapter/CodexSdkAdapter.d.ts +2 -0
  6. package/dist/adapter/CodexSdkAdapter.js +3 -3
  7. package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
  8. package/dist/adapter/OpencodeSdkAdapter.js +2 -2
  9. package/dist/adapter/idle-poll-loop.d.ts +4 -1
  10. package/dist/adapter/idle-poll-loop.js +1 -1
  11. package/dist/adapter/types.d.ts +4 -2
  12. package/dist/browser/browser-pool.d.ts +73 -0
  13. package/dist/browser/browser-pool.js +2 -0
  14. package/dist/browser/browser-profile-store.d.ts +28 -0
  15. package/dist/browser/browser-profile-store.js +2 -0
  16. package/dist/browser/built-in-browser-tools.d.ts +27 -0
  17. package/dist/browser/built-in-browser-tools.js +2 -0
  18. package/dist/browser/element-locator.d.ts +24 -0
  19. package/dist/browser/element-locator.js +2 -0
  20. package/dist/browser/element-picker.d.ts +17 -0
  21. package/dist/browser/element-picker.js +2 -0
  22. package/dist/browser/input-dispatcher.d.ts +34 -0
  23. package/dist/browser/input-dispatcher.js +2 -0
  24. package/dist/browser/listener-store.d.ts +32 -0
  25. package/dist/browser/listener-store.js +2 -0
  26. package/dist/browser/page-listener-manager.d.ts +106 -0
  27. package/dist/browser/page-listener-manager.js +307 -0
  28. package/dist/browser/page-listener-types.d.ts +90 -0
  29. package/dist/browser/page-listener-types.js +1 -0
  30. package/dist/browser/screencast-manager.d.ts +42 -0
  31. package/dist/browser/screencast-manager.js +2 -0
  32. package/dist/browser/snapshot-serializer.d.ts +15 -0
  33. package/dist/browser/snapshot-serializer.js +5 -0
  34. package/dist/browser/ssrf-guard.d.ts +15 -0
  35. package/dist/browser/ssrf-guard.js +2 -0
  36. package/dist/browser/stealth.d.ts +7 -0
  37. package/dist/browser/stealth.js +173 -0
  38. package/dist/browser/step-archive.d.ts +52 -0
  39. package/dist/browser/step-archive.js +2 -0
  40. package/dist/browser/types.d.ts +192 -0
  41. package/dist/browser/types.js +2 -0
  42. package/dist/browser/ws-server.d.ts +67 -0
  43. package/dist/browser/ws-server.js +2 -0
  44. package/dist/index.js +1 -1
  45. package/dist/middleware/auth.d.ts +2 -0
  46. package/dist/middleware/auth.js +1 -1
  47. package/dist/remote-desktop/computer-control.d.ts +30 -0
  48. package/dist/remote-desktop/computer-control.js +2 -0
  49. package/dist/remote-desktop/coords.d.ts +13 -0
  50. package/dist/remote-desktop/coords.js +2 -0
  51. package/dist/remote-desktop/frame-backpressure.d.ts +7 -0
  52. package/dist/remote-desktop/frame-backpressure.js +2 -0
  53. package/dist/remote-desktop/index.d.ts +7 -0
  54. package/dist/remote-desktop/index.js +4 -0
  55. package/dist/remote-desktop/input-backends/interface.d.ts +17 -0
  56. package/dist/remote-desktop/input-backends/interface.js +1 -0
  57. package/dist/remote-desktop/input-backends/key-map.d.ts +2 -0
  58. package/dist/remote-desktop/input-backends/key-map.js +2 -0
  59. package/dist/remote-desktop/input-dispatcher.d.ts +11 -0
  60. package/dist/remote-desktop/input-dispatcher.js +2 -0
  61. package/dist/remote-desktop/permission-manager.d.ts +7 -0
  62. package/dist/remote-desktop/permission-manager.js +2 -0
  63. package/dist/remote-desktop/screen-capture.d.ts +27 -0
  64. package/dist/remote-desktop/screen-capture.js +2 -0
  65. package/dist/remote-desktop/session-manager.d.ts +59 -0
  66. package/dist/remote-desktop/session-manager.js +2 -0
  67. package/dist/remote-desktop/system-backend.d.ts +65 -0
  68. package/dist/remote-desktop/system-backend.js +5 -0
  69. package/dist/remote-desktop/types.d.ts +68 -0
  70. package/dist/remote-desktop/types.js +1 -0
  71. package/dist/remote-desktop/ui-automation.d.ts +48 -0
  72. package/dist/remote-desktop/ui-automation.js +31 -0
  73. package/dist/remote-desktop/win32-api.d.ts +29 -0
  74. package/dist/remote-desktop/win32-api.js +2 -0
  75. package/dist/routes/background-tasks.d.ts +2 -0
  76. package/dist/routes/background-tasks.js +2 -0
  77. package/dist/routes/browser-listeners.d.ts +2 -0
  78. package/dist/routes/browser-listeners.js +2 -0
  79. package/dist/routes/browser-sessions.d.ts +2 -0
  80. package/dist/routes/browser-sessions.js +2 -0
  81. package/dist/routes/pty.js +3 -3
  82. package/dist/routes/terminal.js +30 -30
  83. package/dist/services/agent-pty-manager.d.ts +22 -0
  84. package/dist/services/agent-pty-manager.js +5 -0
  85. package/dist/services/browser-session-manager.d.ts +55 -0
  86. package/dist/services/browser-session-manager.js +12 -0
  87. package/dist/services/cli-commands.d.ts +4 -0
  88. package/dist/services/cli-commands.js +5 -5
  89. package/package/acode/dist/await-complete-tool.d.ts +17 -0
  90. package/package/acode/dist/await-complete-tool.js +21 -0
  91. package/package/acode/dist/background-command-manager.d.ts +148 -0
  92. package/package/acode/dist/background-command-manager.js +8 -0
  93. package/package/acode/dist/background-command-store.d.ts +46 -0
  94. package/package/acode/dist/background-command-store.js +2 -0
  95. package/package/acode/dist/background-command-tools.d.ts +5 -0
  96. package/package/acode/dist/background-command-tools.js +18 -0
  97. package/package/acode/dist/background-output-ringbuffer.d.ts +23 -0
  98. package/package/acode/dist/background-output-ringbuffer.js +3 -0
  99. package/package/acode/dist/built-in-file-tools.d.ts +8 -1
  100. package/package/acode/dist/built-in-file-tools.js +2 -2
  101. package/package/acode/dist/compaction.js +6 -6
  102. package/package/acode/dist/index.d.ts +1 -0
  103. package/package/acode/dist/index.js +1 -1
  104. package/package/acode/dist/runtime.d.ts +6 -0
  105. package/package/acode/dist/runtime.js +27 -27
  106. package/package/acode/dist/sub-agent-tools.d.ts +0 -1
  107. package/package/acode/dist/sub-agent-tools.js +11 -28
  108. package/package/acode/dist/terminal-tool.d.ts +7 -0
  109. package/package/acode/dist/terminal-tool.js +2 -0
  110. package/package/acode/dist/terminal-types.d.ts +38 -0
  111. package/package/acode/dist/terminal-types.js +1 -0
  112. package/package/acode/dist/types.d.ts +10 -1
  113. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +3 -1
  114. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  115. package/package/aws-client-agent-mcp/dist/logger.d.ts +2 -0
  116. package/package/aws-client-agent-mcp/dist/logger.js +1 -1
  117. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +8 -0
  118. package/package/aws-client-agent-mcp/dist/mcp-server.js +30 -19
  119. package/package/aws-client-agent-mcp/dist/types.d.ts +25 -0
  120. package/package.json +6 -2
@@ -0,0 +1,13 @@
1
+ export type RemoteDesktopPlatform = "windows" | "macos" | "linux";
2
+ export interface ScreenRect {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ scaleFactor: number;
8
+ }
9
+
10
+ export declare function toOsMouseCoords(screen: ScreenRect | null, x: number, y: number, platform: RemoteDesktopPlatform): {
11
+ x: number;
12
+ y: number;
13
+ };
@@ -0,0 +1,2 @@
1
+ function u(o,n,r,c){const t=Math.round(n),a=Math.round(r);if(!o)return{x:t,y:a};switch(c){case"macos":{const s=o.scaleFactor>0?o.scaleFactor:1;return{x:Math.round(t/s),y:Math.round(a/s)}}case"windows":case"linux":default:return{x:t,y:a}}}export{u as toOsMouseCoords};
2
+
@@ -0,0 +1,7 @@
1
+ import type { RemoteDesktopQuality } from "./types.js";
2
+
3
+ export declare const FRAME_BUFFER_LIMITS: Record<RemoteDesktopQuality, number>;
4
+
5
+ export declare function getFrameBufferLimit(quality: RemoteDesktopQuality): number;
6
+
7
+ export declare function shouldDropFrameForBackpressure(bufferedAmount: number, maxBufferedBytes: number): boolean;
@@ -0,0 +1,2 @@
1
+ const o={sd:2097152,hd:4194304,original:8388608};function t(r){return o[r]}function n(r,e){return r>e}export{o as FRAME_BUFFER_LIMITS,t as getFrameBufferLimit,n as shouldDropFrameForBackpressure};
2
+
@@ -0,0 +1,7 @@
1
+ import type { Server } from "node:http";
2
+ import { SessionManager } from "./session-manager.js";
3
+ import { PermissionManager } from "./permission-manager.js";
4
+ declare const permissionManager: PermissionManager;
5
+ declare const sessionManager: SessionManager;
6
+ export declare function attachRemoteDesktopWebSocketServer(server: Server): void;
7
+ export { sessionManager, permissionManager };
@@ -0,0 +1,4 @@
1
+ import{WebSocketServer as u}from"ws";import{SessionManager as S}from"./session-manager.js";import{PermissionManager as y}from"./permission-manager.js";import{isRuntimeTokenValid as b}from"../middleware/auth.js";import{getFrameBufferLimit as T,shouldDropFrameForBackpressure as P}from"./frame-backpressure.js";const g=new y,i=new S(g);function W(r,o){const e=o.searchParams.get("token");if(e)return e;const t=r.headers["x-runtime-token"];if(typeof t=="string")return t;const s=r.headers.authorization;return typeof s=="string"&&s.startsWith("Bearer ")?s.slice(7):""}function c(r,o){r.readyState===r.OPEN&&r.send(JSON.stringify(o))}function x(r,o,e){switch(e.type){case"remote-desktop:input":{const t=e.data;i.handleInput(o,t).catch(s=>{c(r,{type:"remote-desktop:error",sessionId:o,data:{error:String(s)}})});break}case"remote-desktop:stop":{i.closeSession(o),r.close();break}case"remote-desktop:quality":{const t=i.getSession(o);t&&e.data&&t.screenCapture.setQuality(e.data.quality,e.data.fps);break}case"remote-desktop:control-mode":{const t=i.getSession(o);t&&e.data&&(e.data.enabled?t.enableControl().catch(s=>{console.warn("[remote-desktop] enableControl failed:",s)}):t.stopInputBackend());break}case"remote-desktop:switch-screen":{const t=i.getSession(o);t&&e.data&&t.switchScreen(e.data.screenId||null).catch(s=>{c(r,{type:"remote-desktop:error",sessionId:o,data:{error:String(s)}})});break}case"remote-desktop:ping":{c(r,{type:"remote-desktop:pong",sessionId:o,data:e.data});break}}}function B(r){const o=new u({noServer:!0});r.on("upgrade",(e,t,s)=>{const d=new URL(e.url||"/",`http://${e.headers.host}`);if(d.pathname!=="/remote-desktop")return;const m=W(e,d);if(!b(m)&&!g.validateToken(m)){t.write(`HTTP/1.1 401 Unauthorized\r
2
+ \r
3
+ `),t.destroy();return}o.handleUpgrade(e,t,s,n=>{const h=d.searchParams.get("agentId")||"default";i.createSession(h,m).then(a=>{const p=i.getSession(a.sessionId);p?.attachWebSocket(n),c(n,{type:"remote-desktop:start-result",sessionId:a.sessionId,data:{screens:a.screens,permissions:a.permissions}}),p&&p.startFrameLoop(f=>{n.readyState===n.OPEN&&!P(n.bufferedAmount,T(p.screenCapture.getQuality()))&&n.send(f)}),n.on("message",f=>{try{const k=f.toString();if(k.startsWith("{")){const l=JSON.parse(k);x(n,a.sessionId,l)}}catch{}}),n.on("close",()=>{i.closeSession(a.sessionId)}),n.on("error",()=>{i.closeSession(a.sessionId)})}).catch(a=>{console.error("[remote-desktop] createSession failed:",a),c(n,{type:"remote-desktop:error",sessionId:"",data:{error:String(a)}}),n.close(1011,String(a))})})})}export{B as attachRemoteDesktopWebSocketServer,g as permissionManager,i as sessionManager};
4
+
@@ -0,0 +1,17 @@
1
+ export interface InputBackend {
2
+ moveMouse(x: number, y: number): Promise<void>;
3
+ moveMouseRelative(dx: number, dy: number): Promise<void>;
4
+ click(x: number, y: number, button: 'left' | 'right'): Promise<void>;
5
+ doubleClick(x: number, y: number): Promise<void>;
6
+ mouseDown(button: 'left' | 'right'): Promise<void>;
7
+ mouseUp(button: 'left' | 'right'): Promise<void>;
8
+ scroll(direction: 'up' | 'down' | 'left' | 'right', amount: number): Promise<void>;
9
+ type(text: string): Promise<void>;
10
+ keyPress(keys: string[]): Promise<void>;
11
+ keyDown(key: string): Promise<void>;
12
+ keyUp(key: string): Promise<void>;
13
+ getScreenSize(): {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ }
@@ -0,0 +1,2 @@
1
+ export declare const KEY_MAP: Record<string, string>;
2
+ export declare function mapKey(domCode: string): string;
@@ -0,0 +1,2 @@
1
+ const a={KeyA:"a",KeyB:"b",KeyC:"c",KeyD:"d",KeyE:"e",KeyF:"f",KeyG:"g",KeyH:"h",KeyI:"i",KeyJ:"j",KeyK:"k",KeyL:"l",KeyM:"m",KeyN:"n",KeyO:"o",KeyP:"p",KeyQ:"q",KeyR:"r",KeyS:"s",KeyT:"t",KeyU:"u",KeyV:"v",KeyW:"w",KeyX:"x",KeyY:"y",KeyZ:"z",Digit0:"0",Digit1:"1",Digit2:"2",Digit3:"3",Digit4:"4",Digit5:"5",Digit6:"6",Digit7:"7",Digit8:"8",Digit9:"9",F1:"f1",F2:"f2",F3:"f3",F4:"f4",F5:"f5",F6:"f6",F7:"f7",F8:"f8",F9:"f9",F10:"f10",F11:"f11",F12:"f12",F13:"f13",F14:"f14",F15:"f15",F16:"f16",F17:"f17",F18:"f18",F19:"f19",F20:"f20",F21:"f21",F22:"f22",F23:"f23",F24:"f24",ShiftLeft:"shift",ShiftRight:"shift",ControlLeft:"control",ControlRight:"control",AltLeft:"alt",AltRight:"alt",MetaLeft:"command",MetaRight:"command",Enter:"enter",NumpadEnter:"enter",Backspace:"backspace",Tab:"tab",Escape:"escape",Space:"space",CapsLock:"capslock",Delete:"delete",Insert:"insert",Home:"home",End:"end",PageUp:"pageup",PageDown:"pagedown",ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Minus:"-",Equal:"=",BracketLeft:"[",BracketRight:"]",Backslash:"\\",Semicolon:";",Quote:"'",Backquote:"`",Comma:",",Period:".",Slash:"/",Numpad0:"numpad_0",Numpad1:"numpad_1",Numpad2:"numpad_2",Numpad3:"numpad_3",Numpad4:"numpad_4",Numpad5:"numpad_5",Numpad6:"numpad_6",Numpad7:"numpad_7",Numpad8:"numpad_8",Numpad9:"numpad_9",NumpadMultiply:"numpad_multiply",NumpadAdd:"numpad_add",NumpadSubtract:"numpad_subtract",NumpadDecimal:"numpad_decimal",NumpadDivide:"numpad_divide"};function t(e){return a[e]??e.toLowerCase()}export{a as KEY_MAP,t as mapKey};
2
+
@@ -0,0 +1,11 @@
1
+ import type { InputBackend } from "./input-backends/interface.js";
2
+ import type { InputMessage, RemoteDesktopCapability, ScreenInfo } from "./types.js";
3
+ export declare class InputDispatcher {
4
+ private backend;
5
+ private permissions;
6
+ private screen;
7
+ constructor(backend: InputBackend, permissions: Set<RemoteDesktopCapability>, screen: ScreenInfo);
8
+
9
+ setScreen(screen: ScreenInfo): void;
10
+ handle(message: InputMessage): Promise<void>;
11
+ }
@@ -0,0 +1,2 @@
1
+ class k{constructor(e,t,s){this.backend=e,this.permissions=t,this.screen=s}setScreen(e){this.screen=e}async handle(e){const{type:t}=e,s=t.startsWith("mouse")||t==="leftClick"||t==="rightClick"||t==="doubleClick",c=t.startsWith("keyboard"),r=t.startsWith("scroll");if(s&&!this.permissions.has("remote-desktop:mouse")||c&&!this.permissions.has("remote-desktop:keyboard")||r&&!this.permissions.has("remote-desktop:scroll"))return;const a=e.x!==void 0?Math.round(e.x/1e3*this.screen.width)+this.screen.x:0,i=e.y!==void 0?Math.round(e.y/1e3*this.screen.height)+this.screen.y:0;switch(t){case"mouseMove":await this.backend.moveMouse(a,i);break;case"mouseMoveRelative":await this.backend.moveMouseRelative(e.dx??0,e.dy??0);break;case"mouseDown":e.x!==void 0&&e.y!==void 0&&await this.backend.moveMouse(a,i),await this.backend.mouseDown(e.button??"left");break;case"mouseUp":await this.backend.mouseUp(e.button??"left");break;case"leftClick":await this.backend.click(a,i,"left");break;case"rightClick":await this.backend.click(a,i,"right");break;case"doubleClick":await this.backend.doubleClick(a,i);break;case"scrollUp":await this.backend.scroll("up",e.amount??3);break;case"scrollDown":await this.backend.scroll("down",e.amount??3);break;case"scrollLeft":await this.backend.scroll("left",e.amount??3);break;case"scrollRight":await this.backend.scroll("right",e.amount??3);break;case"keyboardPressKey":await this.backend.keyPress(e.key??[]);break;case"keyboardDownKey":for(const o of e.key??[])await this.backend.keyDown(o);break;case"keyboardReleaseKey":for(const o of e.key??[])await this.backend.keyUp(o);break;case"keyboardType":await this.backend.type(e.key?.[0]??"");break}}}export{k as InputDispatcher};
2
+
@@ -0,0 +1,7 @@
1
+ import type { RemoteDesktopCapability } from "./types.js";
2
+ export declare class PermissionManager {
3
+
4
+ resolvePermissions(token: string): Set<RemoteDesktopCapability>;
5
+ validateToken(token: string): boolean;
6
+ hasCapability(permissions: Set<RemoteDesktopCapability>, capability: RemoteDesktopCapability): boolean;
7
+ }
@@ -0,0 +1,2 @@
1
+ const o=["remote-desktop:view","remote-desktop:mouse","remote-desktop:keyboard","remote-desktop:scroll","remote-desktop:file-upload","remote-desktop:clipboard"];class s{resolvePermissions(e){return this.validateToken(e)?new Set(o):new Set}validateToken(e){return typeof e=="string"&&e.length>0}hasCapability(e,t){return e.has(t)}}export{s as PermissionManager};
2
+
@@ -0,0 +1,27 @@
1
+ import type { RemoteDesktopQuality, ScreenInfo, ScreenshotResult } from "./types.js";
2
+ export declare class ScreenCapture {
3
+ private screens;
4
+ private interval;
5
+
6
+ private fps;
7
+
8
+ private quality;
9
+ private activeScreenId;
10
+ private onFrame;
11
+
12
+ private capturing;
13
+ listScreens(): Promise<ScreenInfo[]>;
14
+
15
+ captureVirtualDesktop(): Promise<ScreenshotResult>;
16
+
17
+ setActiveScreen(screenId: string | null): void;
18
+
19
+ startLoop(onFrame: (frame: Buffer) => void): void;
20
+
21
+ private scheduleLoop;
22
+ stopLoop(): void;
23
+
24
+ setQuality(quality?: RemoteDesktopQuality, fps?: number): void;
25
+ getQuality(): RemoteDesktopQuality;
26
+ getScreens(): ScreenInfo[];
27
+ }
@@ -0,0 +1,2 @@
1
+ let o=null;async function S(){return o||(o=await import("node-screenshots")),o}let u=null;async function v(){return u||(u=await import("sharp")),u}const w={sd:{scale:.5,jpegQuality:55},hd:{scale:1,jpegQuality:72},original:{scale:1,jpegQuality:95}};class M{constructor(){this.screens=[],this.interval=null,this.fps=20,this.quality="hd",this.activeScreenId=null,this.onFrame=null,this.capturing=!1}async listScreens(){try{const t=await S(),{Monitor:i}=t,a=i.all();return this.screens=a.map((e,s)=>({id:String(e.id()),name:e.name()||`Screen ${s+1}`,x:e.x(),y:e.y(),width:e.width(),height:e.height(),scaleFactor:e.scaleFactor()})),this.screens}catch(t){return console.error("[remote-desktop] listScreens failed:",t),this.screens=[{id:"0",name:"Screen 1",x:0,y:0,width:1920,height:1080,scaleFactor:1}],this.screens}}async captureVirtualDesktop(){const t=await S(),{Monitor:i}=t,a=i.all();if(a.length===0)throw new Error("No screens available");let e=a[0];if(this.activeScreenId){const c=a.find(n=>String(n.id())===this.activeScreenId);c&&(e=c)}const s=e.captureImageSync(),r=s.width,l=s.height;let h,p=r,g=l;try{const c=await v(),n=w[this.quality],f=Math.max(1,Math.round(r*n.scale)),y=Math.max(1,Math.round(l*n.scale)),m=s.toRawSync();h=await c(m,{raw:{width:r,height:l,channels:4}}).resize(f,y,{fit:"fill"}).jpeg({quality:n.jpegQuality}).toBuffer(),p=f,g=y}catch{h=s.toJpegSync()}return{data:h,width:p,height:g}}setActiveScreen(t){this.activeScreenId=t}startLoop(t){this.stopLoop(),this.onFrame=t,this.scheduleLoop()}scheduleLoop(){if(!this.onFrame)return;const t=Math.max(16,Math.round(1e3/this.fps));this.interval=setInterval(async()=>{if(!this.capturing){this.capturing=!0;try{const i=await this.captureVirtualDesktop();this.onFrame?.(i.data)}catch{}finally{this.capturing=!1}}},t)}stopLoop(){this.interval&&(clearInterval(this.interval),this.interval=null)}setQuality(t,i){t&&Object.prototype.hasOwnProperty.call(w,t)&&(this.quality=t),i!==void 0&&(this.fps=Math.max(1,Math.min(60,i))),this.interval&&(clearInterval(this.interval),this.interval=null,this.scheduleLoop())}getQuality(){return this.quality}getScreens(){return this.screens}}export{M as ScreenCapture};
2
+
@@ -0,0 +1,59 @@
1
+ import type { WebSocket } from "ws";
2
+ import { ScreenCapture } from "./screen-capture.js";
3
+ import { InputDispatcher } from "./input-dispatcher.js";
4
+ import { PermissionManager } from "./permission-manager.js";
5
+ import { ComputerControl } from "./computer-control.js";
6
+ import type { InputMessage, RemoteDesktopCapability, ScreenInfo, StartResult } from "./types.js";
7
+ export declare class RemoteDesktopSession {
8
+
9
+ static readonly FOCUS_POLL_INTERVAL_MS = 1000;
10
+ id: string;
11
+ agentId: string;
12
+ permissions: Set<RemoteDesktopCapability>;
13
+ screenCapture: ScreenCapture;
14
+ inputDispatcher: InputDispatcher | null;
15
+ computerControl: ComputerControl;
16
+ state: "active" | "closed" | "error";
17
+ ws: WebSocket | null;
18
+ private inputBackend;
19
+ private onFrame;
20
+ private focusPollTimer;
21
+ private lastFocusState;
22
+
23
+ private inputChain;
24
+ constructor(id: string, agentId: string, permissions: Set<RemoteDesktopCapability>);
25
+ init(): Promise<{
26
+ screens: ScreenInfo[];
27
+ }>;
28
+
29
+ switchScreen(screenId: string | null): Promise<void>;
30
+
31
+ startInputBackend(): void;
32
+
33
+ enableControl(): Promise<void>;
34
+
35
+ stopInputBackend(): void;
36
+
37
+ private startFocusPolling;
38
+
39
+ private stopFocusPolling;
40
+
41
+ private sendFocusState;
42
+ attachWebSocket(ws: WebSocket): void;
43
+ startFrameLoop(onFrame: (frame: Buffer) => void): void;
44
+ stopFrameLoop(): void;
45
+
46
+ handleInput(message: InputMessage): Promise<void>;
47
+ close(): void;
48
+ }
49
+ export declare class SessionManager {
50
+ private sessions;
51
+ private agentSessions;
52
+ private permissionManager;
53
+ constructor(permissionManager: PermissionManager);
54
+ createSession(agentId: string, token: string): Promise<StartResult>;
55
+ getSession(sessionId: string): RemoteDesktopSession | undefined;
56
+ handleInput(sessionId: string, message: InputMessage): Promise<void>;
57
+ closeSession(sessionId: string): void;
58
+ closeAll(): void;
59
+ }
@@ -0,0 +1,2 @@
1
+ import{randomUUID as a}from"node:crypto";import{ScreenCapture as c}from"./screen-capture.js";import{InputDispatcher as h}from"./input-dispatcher.js";import{ComputerControl as u}from"./computer-control.js";import{SystemBackend as p}from"./system-backend.js";class o{constructor(s,t,e){this.inputDispatcher=null,this.state="active",this.ws=null,this.onFrame=null,this.focusPollTimer=null,this.lastFocusState=null,this.inputChain=Promise.resolve(),this.id=s,this.agentId=t,this.permissions=e,this.screenCapture=new c,this.inputBackend=new p,this.computerControl=new u(this.screenCapture,this.inputBackend)}async init(){const s=await this.screenCapture.listScreens(),t=s[0];return t&&(this.inputDispatcher=new h(this.inputBackend,this.permissions,t),this.inputBackend.setScreenSize(t.width,t.height),this.inputBackend.setActiveScreen(t)),{screens:s}}async switchScreen(s){this.screenCapture.setActiveScreen(s);const t=this.screenCapture.getScreens(),e=s?t.find(i=>i.id===s):t[0];e&&this.inputDispatcher&&(this.inputDispatcher.setScreen(e),this.inputBackend.setScreenSize(e.width,e.height),this.inputBackend.setActiveScreen(e))}startInputBackend(){this.inputBackend.startPersistentProcess()}async enableControl(){this.inputBackend.startPersistentProcess(),this.startFocusPolling(),await this.inputBackend.centerMouse()}stopInputBackend(){this.stopFocusPolling(),this.inputBackend.stopPersistentProcess()}startFocusPolling(){this.stopFocusPolling(),this.focusPollTimer=setInterval(()=>{const s=this.inputBackend.isInputFocused();s!==this.lastFocusState&&(this.lastFocusState=s,this.sendFocusState(s))},o.FOCUS_POLL_INTERVAL_MS)}stopFocusPolling(){this.focusPollTimer&&(clearInterval(this.focusPollTimer),this.focusPollTimer=null),this.lastFocusState=null}sendFocusState(s){!this.ws||this.ws.readyState!==this.ws.OPEN||this.ws.send(JSON.stringify({type:"remote-desktop:focus-state",sessionId:this.id,data:{hasInputFocus:s}}))}attachWebSocket(s){this.ws=s}startFrameLoop(s){this.onFrame=s,this.screenCapture.startLoop(s)}stopFrameLoop(){this.screenCapture.stopLoop(),this.onFrame=null}handleInput(s){if(!this.inputDispatcher)return Promise.resolve();const t=this.inputChain.then(()=>this.inputDispatcher.handle(s));return this.inputChain=t.catch(()=>{}),t}close(){this.state="closed",this.stopFocusPolling(),this.stopFrameLoop(),this.inputBackend.stopPersistentProcess(),this.ws=null}}o.FOCUS_POLL_INTERVAL_MS=1e3;class P{constructor(s){this.sessions=new Map,this.agentSessions=new Map,this.permissionManager=s}async createSession(s,t){if(!this.permissionManager.validateToken(t))throw new Error("Invalid token");const e=this.agentSessions.get(s);e&&this.closeSession(e);const i=this.permissionManager.resolvePermissions(t),n=a(),r=new o(n,s,i);return await r.init(),this.sessions.set(n,r),this.agentSessions.set(s,n),{sessionId:n,screens:r.screenCapture.getScreens(),permissions:Array.from(i)}}getSession(s){return this.sessions.get(s)}async handleInput(s,t){const e=this.sessions.get(s);!e||e.state!=="active"||await e.handleInput(t)}closeSession(s){const t=this.sessions.get(s);if(t){t.close(),this.sessions.delete(s);for(const[e,i]of this.agentSessions)if(i===s){this.agentSessions.delete(e);break}}}closeAll(){for(const s of this.sessions.values())s.close();this.sessions.clear(),this.agentSessions.clear()}}export{o as RemoteDesktopSession,P as SessionManager};
2
+
@@ -0,0 +1,65 @@
1
+ import type { InputBackend } from "./input-backends/interface.js";
2
+ import type { ScreenInfo } from "./types.js";
3
+ export declare class SystemBackend implements InputBackend {
4
+ private platform;
5
+ private screenSize;
6
+
7
+ private activeScreen;
8
+
9
+ private win32Api;
10
+
11
+ constructor(platformOverride?: "windows" | "macos" | "linux");
12
+
13
+ private static readonly WIN32_INIT_TYPES;
14
+ private psProcess;
15
+ private psReady;
16
+ private psReadyPromise;
17
+ private psBuffer;
18
+ private cliclickProcess;
19
+ private xdotoolProcess;
20
+
21
+ startPersistentProcess(): void;
22
+
23
+ private startPowerShellProcess;
24
+
25
+ private ensurePowerShell;
26
+
27
+ stopPersistentProcess(): void;
28
+
29
+ private psCmd;
30
+
31
+ moveMouse(x: number, y: number): Promise<void>;
32
+
33
+ moveMouseRelative(dx: number, dy: number): Promise<void>;
34
+
35
+ centerMouse(): Promise<void>;
36
+
37
+ click(x: number, y: number, button: 'left' | 'right'): Promise<void>;
38
+
39
+ doubleClick(x: number, y: number): Promise<void>;
40
+ mouseDown(button: 'left' | 'right'): Promise<void>;
41
+ mouseUp(button: 'left' | 'right'): Promise<void>;
42
+ scroll(direction: 'up' | 'down' | 'left' | 'right', amount: number): Promise<void>;
43
+
44
+ type(text: string): Promise<void>;
45
+ keyPress(keys: string[]): Promise<void>;
46
+ keyDown(key: string): Promise<void>;
47
+ keyUp(key: string): Promise<void>;
48
+ getScreenSize(): {
49
+ width: number;
50
+ height: number;
51
+ };
52
+ setScreenSize(width: number, height: number): void;
53
+
54
+ setActiveScreen(screen: ScreenInfo): void;
55
+ getMousePos(): Promise<{
56
+ x: number;
57
+ y: number;
58
+ }>;
59
+
60
+ isInputFocused(): boolean;
61
+
62
+ private warnInputErr;
63
+
64
+ private getVirtualKeyCode;
65
+ }
@@ -0,0 +1,5 @@
1
+ import{spawn as y,exec as P}from"node:child_process";import{promisify as E}from"node:util";import{mapKey as a}from"./input-backends/key-map.js";import{toOsMouseCoords as p}from"./coords.js";import{initWin32Api as S,MOUSEEVENTF_LEFTDOWN as h,MOUSEEVENTF_LEFTUP as u,MOUSEEVENTF_RIGHTDOWN as m,MOUSEEVENTF_RIGHTUP as d,MOUSEEVENTF_WHEEL as k,MOUSEEVENTF_HWHEEL as v,KEYEVENTF_KEYUP as f}from"./win32-api.js";const r=E(P);function g(){return process.platform==="win32"?"windows":process.platform==="darwin"?"macos":"linux"}class l{constructor(s){this.screenSize={width:1920,height:1080},this.activeScreen=null,this.win32Api=null,this.psProcess=null,this.psReady=!1,this.psReadyPromise=null,this.psBuffer="",this.cliclickProcess=null,this.xdotoolProcess=null,this.platform=s??g()}startPersistentProcess(){if(this.platform==="windows"){if(this.win32Api=S(),this.win32Api){console.log("[remote-desktop] Win32 FFI API ready (koffi)");return}console.warn("[remote-desktop] Win32 FFI unavailable, falling back to PowerShell"),this.startPowerShellProcess()}}startPowerShellProcess(){this.psProcess||this.psReadyPromise||(this.psReadyPromise=new Promise(s=>{const e=y("powershell.exe",["-NoProfile","-NoLogo","-Command","-"],{stdio:["pipe","pipe","pipe"],windowsHide:!0});e.stdin?.write(`${l.WIN32_INIT_TYPES}; [W]::SetProcessDPIAware() | Out-Null; Add-Type -AssemblyName System.Windows.Forms; echo "__READY__"
2
+ `),e.stdout?.on("data",t=>{this.psBuffer+=t.toString(),this.psBuffer.includes("__READY__")&&(this.psBuffer="",this.psProcess=e,this.psReady=!0,console.log("[remote-desktop] PowerShell persistent process ready"),s(!0))}),e.stderr?.on("data",t=>{console.warn("[remote-desktop] PowerShell stderr:",t.toString())}),e.on("error",t=>{console.error("[remote-desktop] PowerShell process error:",t),this.psProcess=null,this.psReady=!1,this.psReadyPromise=null,s(!1)}),e.on("exit",()=>{this.psProcess=null,this.psReady=!1,this.psReadyPromise=null,s(!1)})}))}async ensurePowerShell(){return this.psProcess?.stdin?.writable&&this.psReady?!0:(this.psReadyPromise||this.startPowerShellProcess(),await this.psReadyPromise??!1)}stopPersistentProcess(){this.psProcess&&(this.psProcess.stdin?.end(),this.psProcess.kill(),this.psProcess=null,this.psReady=!1,this.psReadyPromise=null),this.cliclickProcess&&(this.cliclickProcess.kill(),this.cliclickProcess=null),this.xdotoolProcess&&(this.xdotoolProcess.kill(),this.xdotoolProcess=null)}psCmd(s){if(this.psProcess?.stdin?.writable&&this.psReady){this.psProcess.stdin.write(s+`
3
+ `);return}const e=`${l.WIN32_INIT_TYPES}; [W]::SetProcessDPIAware() | Out-Null; Add-Type -AssemblyName System.Windows.Forms; ${s}`,t=Buffer.from(e,"utf16le").toString("base64");r(`powershell.exe -NoProfile -EncodedCommand ${t}`).catch(i=>{console.warn("[remote-desktop] fallback PowerShell command failed:",i.message)})}async moveMouse(s,e){const{x:t,y:i}=p(this.activeScreen,s,e,this.platform);if(this.platform==="windows"){if(this.win32Api){this.win32Api.setCursorPos(t,i);return}this.psCmd(`[W]::SetCursorPos(${t}, ${i})`)}else this.platform==="macos"?r(`cliclick m:${t},${i}`).catch(o=>this.warnInputErr("macOS moveMouse",o)):r(`xdotool mousemove ${t} ${i}`).catch(o=>this.warnInputErr("linux moveMouse",o))}async moveMouseRelative(s,e){if(this.platform==="windows"){if(this.win32Api){const o=this.win32Api.getCursorPos();o?this.win32Api.setCursorPos(o.x+Math.round(s),o.y+Math.round(e)):this.warnInputErr("win32 moveMouseRelative",new Error("getCursorPos returned null"));return}this.psCmd(`[W]::mouse_event(1, ${Math.round(s)}, ${Math.round(e)}, 0, 0)`);return}const{x:t,y:i}=await this.getMousePos();await this.moveMouse(t+s,i+e)}async centerMouse(){this.activeScreen&&await this.moveMouse(this.activeScreen.x+this.activeScreen.width/2,this.activeScreen.y+this.activeScreen.height/2)}async click(s,e,t){const{x:i,y:o}=p(this.activeScreen,s,e,this.platform);if(this.platform==="windows"){if(this.win32Api){const w=t==="right"?m:h,x=t==="right"?d:u;this.win32Api.setCursorPos(i,o),this.win32Api.mouseEvent(w,0,0,0),this.win32Api.mouseEvent(x,0,0,0);return}const n=t==="right"?8:2,c=t==="right"?16:4;this.psCmd(`[W]::SetCursorPos(${i},${o}); [W]::mouse_event(${n},0,0,0,0); [W]::mouse_event(${c},0,0,0,0)`)}else this.platform==="macos"?r(`cliclick ${t==="right"?"rc":"c"}:${i},${o}`).catch(c=>this.warnInputErr("macOS click",c)):r(`xdotool mousemove ${i} ${o} && xdotool click ${t==="right"?3:1}`).catch(c=>this.warnInputErr("linux click",c))}async doubleClick(s,e){const{x:t,y:i}=p(this.activeScreen,s,e,this.platform);if(this.platform==="windows"){if(this.win32Api){this.win32Api.setCursorPos(t,i);for(let o=0;o<2;o++)this.win32Api.mouseEvent(h,0,0,0),this.win32Api.mouseEvent(u,0,0,0);return}this.psCmd(`[W]::SetCursorPos(${t},${i}); [W]::mouse_event(2,0,0,0,0); [W]::mouse_event(4,0,0,0,0); [W]::mouse_event(2,0,0,0,0); [W]::mouse_event(4,0,0,0,0)`)}else this.platform==="macos"?r(`cliclick dd:${t},${i}`).catch(o=>this.warnInputErr("macOS doubleClick",o)):r(`xdotool mousemove ${t} ${i} && xdotool click --repeat 2 1`).catch(o=>this.warnInputErr("linux doubleClick",o))}async mouseDown(s){if(this.platform==="windows"){if(this.win32Api){const t=s==="right"?m:h;this.win32Api.mouseEvent(t,0,0,0);return}const e=s==="right"?8:2;this.psCmd(`[W]::mouse_event(${e},0,0,0,0)`)}else this.platform==="macos"?r(`cliclick ${s==="right"?"rd":"dd"}:`).catch(t=>this.warnInputErr("macOS mouseDown",t)):r(`xdotool mousedown ${s==="right"?3:1}`).catch(t=>this.warnInputErr("linux mouseDown",t))}async mouseUp(s){if(this.platform==="windows"){if(this.win32Api){const t=s==="right"?d:u;this.win32Api.mouseEvent(t,0,0,0);return}const e=s==="right"?16:4;this.psCmd(`[W]::mouse_event(${e},0,0,0,0)`)}else this.platform==="macos"?r(`cliclick ${s==="right"?"ru":"du"}:`).catch(t=>this.warnInputErr("macOS mouseUp",t)):r(`xdotool mouseup ${s==="right"?3:1}`).catch(t=>this.warnInputErr("linux mouseUp",t))}async scroll(s,e){const t=Math.max(1,Math.round(e));if(this.platform==="windows"){const i=s==="left"||s==="right",o=i?v:k,n=i?s==="right"?t*120:-t*120:s==="down"?-t*120:t*120;if(this.win32Api){this.win32Api.mouseEvent(o,0,0,n);return}this.psCmd(`[W]::mouse_event(${o},0,0,${n},0)`)}else this.platform==="macos"?r(`python3 -c "from Quartz import CGEventCreateScrollWheelEvent2, kCGScrollEventUnitPixel; from CoreGraphics import CGEventPost, kCGHIDEventTap; e = CGEventCreateScrollWheelEvent2(None, kCGScrollEventUnitPixel, ${s==="left"||s==="right"?1:0}, ${(s==="down"||s==="right"?-1:1)*t*10}); CGEventPost(kCGHIDEventTap, e)" 2>/dev/null || true`).catch(n=>this.warnInputErr("macOS scroll",n)):r(`xdotool click --repeat ${t} ${s==="up"?4:s==="down"?5:s==="left"?6:7}`).catch(o=>this.warnInputErr("linux scroll",o))}async type(s){if(this.platform==="windows"){const t=`[System.Windows.Forms.SendKeys]::SendWait('${s.replace(/[{}()[\]^%~+]/g,"{$&}").replace(/\n/g,"{ENTER}").replace(/'/g,"''")}')`;if(await this.ensurePowerShell()){this.psProcess?.stdin?.write(t+`
4
+ `);return}this.psCmd(t)}else this.platform==="macos"?r(`cliclick t:'${s.replace(/'/g,"'\\''")}'`).catch(e=>this.warnInputErr("macOS type",e)):r(`xdotool type --clearmodifiers '${s.replace(/'/g,"'\\''")}'`).catch(e=>this.warnInputErr("linux type",e))}async keyPress(s){const e=s.map(a);if(this.platform==="windows"){if(this.win32Api){for(const t of e){const i=this.getVirtualKeyCode(t);i&&(this.win32Api.keybdEvent(i,0),await new Promise(o=>setTimeout(o,10)),this.win32Api.keybdEvent(i,f))}return}for(const t of e){const i=this.getVirtualKeyCode(t);i&&this.psCmd(`[W]::keybd_event(${i},0,0,0); Start-Sleep -Milliseconds 10; [W]::keybd_event(${i},0,2,0)`)}}else this.platform==="macos"?r(`cliclick kp:${e.join("+")}`).catch(t=>this.warnInputErr("macOS keyPress",t)):r(`xdotool key '${e.join("+")}'`).catch(t=>this.warnInputErr("linux keyPress",t))}async keyDown(s){const e=a(s);if(this.platform==="windows"){const t=this.getVirtualKeyCode(e);if(!t)return;if(this.win32Api){this.win32Api.keybdEvent(t,0);return}this.psCmd(`[W]::keybd_event(${t},0,0,0)`)}else this.platform==="macos"?r(`cliclick kd:${e}`).catch(t=>this.warnInputErr("macOS keyDown",t)):r(`xdotool keydown ${e}`).catch(t=>this.warnInputErr("linux keyDown",t))}async keyUp(s){const e=a(s);if(this.platform==="windows"){const t=this.getVirtualKeyCode(e);if(!t)return;if(this.win32Api){this.win32Api.keybdEvent(t,f);return}this.psCmd(`[W]::keybd_event(${t},0,2,0)`)}else this.platform==="macos"?r(`cliclick ku:${e}`).catch(t=>this.warnInputErr("macOS keyUp",t)):r(`xdotool keyup ${e}`).catch(t=>this.warnInputErr("linux keyUp",t))}getScreenSize(){return this.screenSize}setScreenSize(s,e){this.screenSize={width:s,height:e}}setActiveScreen(s){this.activeScreen=s,this.screenSize={width:s.width,height:s.height}}async getMousePos(){if(this.platform==="windows"){if(this.win32Api)return this.win32Api.getCursorPos()??{x:0,y:0};const s=`${l.WIN32_INIT_TYPES}; [W]::SetProcessDPIAware() | Out-Null; Add-Type -AssemblyName System.Windows.Forms; $p = [System.Windows.Forms.Cursor]::Position; Write-Output "$($p.X),$($p.Y)"`,e=Buffer.from(s,"utf16le").toString("base64"),{stdout:t}=await r(`powershell.exe -NoProfile -EncodedCommand ${e}`),[i,o]=t.trim().split(",").map(Number);return{x:i||0,y:o||0}}else if(this.platform==="macos"){const{stdout:s}=await r(`osascript -e 'tell application "System Events" to get position of the mouse'`),[e,t]=s.trim().split(",").map(Number);return{x:e||0,y:t||0}}else{const{stdout:s}=await r("xdotool getmouselocation --shell"),e=s.match(/X=(\d+)/),t=s.match(/Y=(\d+)/);return{x:e?Number(e[1]):0,y:t?Number(t[1]):0}}}isInputFocused(){return this.platform!=="windows"||!this.win32Api?!1:this.win32Api.isInputFocused()}warnInputErr(s,e){console.warn(`[remote-desktop] ${s} failed:`,e?.message??String(e))}getVirtualKeyCode(s){return{a:65,b:66,c:67,d:68,e:69,f:70,g:71,h:72,i:73,j:74,k:75,l:76,m:77,n:78,o:79,p:80,q:81,r:82,s:83,t:84,u:85,v:86,w:87,x:88,y:89,z:90,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,enter:13,return:13,tab:9,escape:27,backspace:8,delete:46,up:38,down:40,left:37,right:39,home:36,end:35,pageup:33,pagedown:34,space:32,shift:16,control:17,alt:18,f1:112,f2:113,f3:114,f4:115,f5:116,f6:117,f7:118,f8:119,f9:120,f10:121,f11:122,f12:123}[s.toLowerCase()]||0}}l.WIN32_INIT_TYPES=`Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class W { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); [DllImport("user32.dll")] public static extern void mouse_event(uint f, int dx, int dy, int c, IntPtr i); [DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, IntPtr dwExtraInfo); }'`;export{l as SystemBackend};
5
+
@@ -0,0 +1,68 @@
1
+ export interface ScreenInfo {
2
+ id: string;
3
+ name: string;
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ scaleFactor: number;
9
+ }
10
+ export type InputMessageType = 'mouseMove' | 'mouseMoveRelative' | 'mouseDown' | 'mouseUp' | 'leftClick' | 'rightClick' | 'doubleClick' | 'scrollUp' | 'scrollDown' | 'scrollLeft' | 'scrollRight' | 'keyboardPressKey' | 'keyboardReleaseKey' | 'keyboardDownKey' | 'keyboardType';
11
+ export interface InputMessage {
12
+ type: InputMessageType;
13
+ x?: number;
14
+ y?: number;
15
+ dx?: number;
16
+ dy?: number;
17
+ button?: 'left' | 'right';
18
+ key?: string[];
19
+ amount?: number;
20
+ screenId?: string;
21
+ }
22
+ export type RemoteDesktopCapability = 'remote-desktop:view' | 'remote-desktop:mouse' | 'remote-desktop:keyboard' | 'remote-desktop:file-upload' | 'remote-desktop:clipboard' | 'remote-desktop:scroll';
23
+ export interface RemoteDesktopMessage {
24
+ type: string;
25
+ sessionId: string;
26
+ data?: any;
27
+ }
28
+ export interface ScreenshotResult {
29
+ data: Buffer;
30
+ width: number;
31
+ height: number;
32
+ }
33
+
34
+ export type RemoteDesktopQuality = "sd" | "hd" | "original";
35
+ export interface StartResult {
36
+ sessionId: string;
37
+ screens: ScreenInfo[];
38
+ permissions: RemoteDesktopCapability[];
39
+ }
40
+ export type ConnectionState = 'idle' | 'connecting' | 'authenticating' | 'connected' | 'disconnected' | 'error';
41
+ export interface AppInfo {
42
+ pid: number;
43
+ name: string;
44
+ isForeground: boolean;
45
+ windowTitle?: string;
46
+ windowBounds?: {
47
+ x: number;
48
+ y: number;
49
+ width: number;
50
+ height: number;
51
+ };
52
+ }
53
+ export interface UIElement {
54
+ id: string;
55
+ role: string;
56
+ name?: string;
57
+ value?: string;
58
+ bounds?: {
59
+ x: number;
60
+ y: number;
61
+ width: number;
62
+ height: number;
63
+ };
64
+ actions?: string[];
65
+ children?: UIElement[];
66
+ isFocused?: boolean;
67
+ isEditable?: boolean;
68
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,48 @@
1
+ export interface AppInfo {
2
+ pid: number;
3
+ name: string;
4
+ isForeground: boolean;
5
+ windowTitle?: string;
6
+ windowBounds?: {
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ };
12
+ }
13
+ export interface UIElement {
14
+ id: string;
15
+ role: string;
16
+ name?: string;
17
+ value?: string;
18
+ bounds?: {
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ };
24
+ actions?: string[];
25
+ children?: UIElement[];
26
+ isFocused?: boolean;
27
+ isEditable?: boolean;
28
+ }
29
+ export declare class UIAutomation {
30
+
31
+ listApplications(): Promise<AppInfo[]>;
32
+
33
+ getUIElements(pid: number): Promise<UIElement[]>;
34
+
35
+ clickElement(elementId: string): Promise<void>;
36
+
37
+ setElementValue(elementId: string, value: string): Promise<void>;
38
+ private listAppsWindows;
39
+ private parseWindowsApps;
40
+ private getUIElementsWindows;
41
+ private parseWindowsElements;
42
+ private listAppsMacOS;
43
+ private parseMacOSApps;
44
+ private getUIElementsMacOS;
45
+ private parseMacOSElements;
46
+ private listAppsLinux;
47
+ private getUIElementsLinux;
48
+ }
@@ -0,0 +1,31 @@
1
+ import{exec as m}from"node:child_process";import{promisify as c}from"node:util";const r=c(m);class h{async listApplications(){return process.platform==="win32"?this.listAppsWindows():process.platform==="darwin"?this.listAppsMacOS():this.listAppsLinux()}async getUIElements(t){return process.platform==="win32"?this.getUIElementsWindows(t):process.platform==="darwin"?this.getUIElementsMacOS(t):this.getUIElementsLinux(t)}async clickElement(t){throw new Error(`clickElement not yet implemented for ${process.platform}. Use click(x, y) with element bounds.`)}async setElementValue(t,n){throw new Error(`setElementValue not yet implemented for ${process.platform}. Use type(text) with element focus.`)}async listAppsWindows(){const t='powershell.exe -NoProfile -Command "Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes; $root = [System.Windows.Automation.AutomationElement]::RootElement; $cond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Window); $wins = $root.FindAll([System.Windows.Automation.TreeScope]::Children, $cond); $result = $wins | ForEach-Object { $r = $_.Current.BoundingRectangle; [PSCustomObject]@{ pid=$_.Current.ProcessId; name=$_.Current.ProcessName; title=$_.Current.Name; x=$r.X; y=$r.Y; w=$r.Width; h=$r.Height } }; $result | ConvertTo-Json -Compress"';try{const{stdout:n}=await r(t,{timeout:1e4,maxBuffer:1048576});return this.parseWindowsApps(n)}catch{return[]}}parseWindowsApps(t){try{const n=JSON.parse(t.trim());return(Array.isArray(n)?n:[n]).map((e,s)=>({pid:e.pid||0,name:e.name||`Process ${e.pid}`,isForeground:!1,windowTitle:e.title||void 0,windowBounds:e.w?{x:e.x,y:e.y,width:e.w,height:e.h}:void 0}))}catch{return[]}}async getUIElementsWindows(t){const n=`powershell.exe -NoProfile -Command "Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes; $root = [System.Windows.Automation.AutomationElement]::RootElement; $pidCond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ProcessIdProperty, ${t}); $app = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $pidCond); if ($app) { $children = $app.FindAll([System.Windows.Automation.TreeScope]::Descendants, [System.Windows.Automation.Condition]::TrueCondition); $children | ForEach-Object { $r = $_.Current.BoundingRectangle; [PSCustomObject]@{ id=$_.GetRuntimeId(); role=$_.Current.ControlType.ProgrammaticName; name=$_.Current.Name; value=$_.Current.HelpText; x=$r.X; y=$r.Y; w=$r.Width; h=$r.Height } } | ConvertTo-Json -Compress }"`;try{const{stdout:o}=await r(n,{timeout:1e4,maxBuffer:1048576});return this.parseWindowsElements(o)}catch{return[]}}parseWindowsElements(t){try{const n=JSON.parse(t.trim());return(Array.isArray(n)?n:[n]).map(e=>({id:String(e.id||""),role:(e.role||"").replace("ControlType.",""),name:e.name||void 0,value:e.value||void 0,bounds:e.w?{x:e.x,y:e.y,width:e.w,height:e.h}:void 0}))}catch{return[]}}async listAppsMacOS(){const t=`osascript -e 'tell application "System Events" to set appList to (every process whose background only is false)' -e 'set output to ""' -e 'repeat with proc in appList' -e 'set procName to name of proc' -e 'set procPid to unix id of proc' -e 'set winList to every window of proc' -e 'repeat with win in winList' -e 'try' -e 'set winName to name of win' -e 'set {winX, winY, winW, winH} to {position, size} of win' -e 'set output to output & procPid & "\\t" & procName & "\\t" & winName & "\\t" & winX & "," & winY & "," & winW & "," & winH & "\\n"' -e 'end try' -e 'end repeat' -e 'end repeat' -e 'output'`;try{const{stdout:n}=await r(t,{timeout:1e4});return this.parseMacOSApps(n)}catch{return[]}}parseMacOSApps(t){return t.trim().split(`
2
+ `).filter(Boolean).map(o=>{const e=o.split(" "),[s,d,l,p]=e,i=p?p.split(",").map(Number):[];return{pid:Number(s)||0,name:d||"",isForeground:!1,windowTitle:l||void 0,windowBounds:i.length===4?{x:i[0],y:i[1],width:i[2],height:i[3]}:void 0}})}async getUIElementsMacOS(t){const n=`osascript -e 'tell application "System Events"' -e 'set proc to first process whose unix id is ${t}' -e 'set output to ""' -e 'repeat with win in (every window of proc)' -e 'set winName to name of win' -e 'set elemList to every UI element of win' -e 'repeat with elem in elemList' -e 'set eRole to role of elem' -e 'set eName to name of elem' -e 'set eDesc to description of elem' -e 'set {eX, eY} to position of elem' -e 'set {eW, eH} to size of elem' -e 'set output to output & eRole & "\\t" & eName & "\\t" & eDesc & "\\t" & eX & "," & eY & "," & eW & "," & eH & "\\n"' -e 'end repeat' -e 'end repeat' -e 'end tell' -e 'output'`;try{const{stdout:o}=await r(n,{timeout:1e4});return this.parseMacOSElements(o)}catch{return[]}}parseMacOSElements(t){return t.trim().split(`
3
+ `).filter(Boolean).map((o,e)=>{const s=o.split(" "),[d,l,p,i]=s,a=i?i.split(",").map(Number):[];return{id:`mac-${e}`,role:(d||"").replace("AX","").toLowerCase(),name:l||void 0,value:p||void 0,bounds:a.length===4?{x:a[0],y:a[1],width:a[2],height:a[3]}:void 0}})}async listAppsLinux(){const t=`python3 -c "
4
+ import pyatspi, json
5
+ desktop = pyatspi.Registry.getDesktop(0)
6
+ apps = []
7
+ for i in range(desktop.childCount):
8
+ app = desktop.getChildAtIndex(i)
9
+ if app:
10
+ apps.append({'pid': app.getApplicationID(), 'name': app.name, 'role': app.getRoleName()})
11
+ print(json.dumps(apps))
12
+ " 2>/dev/null`;try{const{stdout:n}=await r(t,{timeout:1e4});return JSON.parse(n.trim()).map(e=>({pid:e.pid||0,name:e.name||"",isForeground:!1,windowTitle:e.name}))}catch{return[]}}async getUIElementsLinux(t){const n=`python3 -c "
13
+ import pyatspi, json
14
+ desktop = pyatspi.Registry.getDesktop(0)
15
+ elements = []
16
+ for i in range(desktop.childCount):
17
+ app = desktop.getChildAtIndex(i)
18
+ if app and app.getApplicationID() == ${t}:
19
+ for j in range(min(app.childCount, 50)):
20
+ child = app.getChildAtIndex(j)
21
+ if child:
22
+ b = child.extents
23
+ elements.append({
24
+ 'id': f'linux-{j}',
25
+ 'role': child.getRoleName(),
26
+ 'name': child.name,
27
+ 'x': b.x, 'y': b.y, 'w': b.width, 'h': b.height
28
+ })
29
+ print(json.dumps(elements))
30
+ " 2>/dev/null`;try{const{stdout:o}=await r(n,{timeout:1e4});return JSON.parse(o.trim()).map(s=>({id:s.id,role:s.role,name:s.name||void 0,bounds:{x:s.x,y:s.y,width:s.w,height:s.h}}))}catch{return[]}}}export{h as UIAutomation};
31
+
@@ -0,0 +1,29 @@
1
+ export declare const MOUSEEVENTF_MOVE = 1;
2
+ export declare const MOUSEEVENTF_LEFTDOWN = 2;
3
+ export declare const MOUSEEVENTF_LEFTUP = 4;
4
+ export declare const MOUSEEVENTF_RIGHTDOWN = 8;
5
+ export declare const MOUSEEVENTF_RIGHTUP = 16;
6
+ export declare const MOUSEEVENTF_WHEEL = 2048;
7
+ export declare const MOUSEEVENTF_HWHEEL = 4096;
8
+ export declare const KEYEVENTF_KEYUP = 2;
9
+
10
+ export declare function isInputControlClassName(className: string): boolean;
11
+ export interface Win32Api {
12
+
13
+ setCursorPos(x: number, y: number): boolean;
14
+
15
+ getCursorPos(): {
16
+ x: number;
17
+ y: number;
18
+ } | null;
19
+
20
+ mouseEvent(flags: number, dx: number, dy: number, data: number): void;
21
+
22
+ keybdEvent(vk: number, flags: number): void;
23
+
24
+ isInputFocused(): boolean;
25
+ }
26
+
27
+ export declare function initWin32Api(): Win32Api | null;
28
+
29
+ export declare function resetWin32ApiForTest(): void;
@@ -0,0 +1,2 @@
1
+ let e=null;try{e=(await import("koffi")).default}catch{e=null}const g=1,N=2,v=4,P=8,_=16,O=2048,S=4096,W=2,x=["Edit","RichEdit","RICHEDIT","ComboBox","ComboBoxEx32","WindowsForms10.EDIT","DirectUIHWND","Chrome_RenderWidgetHostHWND"];function C(r){return r?x.some(t=>r===t||r.startsWith(t)):!1}let o;function U(){if(o!==void 0)return o;if(process.platform!=="win32"||!e)return o=null,o;try{e.struct("POINT",{x:"long",y:"long"}),e.struct("RECT",{left:"long",top:"long",right:"long",bottom:"long"}),e.struct("GUITHREADINFO",{cbSize:"uint32",flags:"uint32",hwndActive:"void*",hwndFocus:"void*",hwndCapture:"void*",hwndMenuOwner:"void*",hwndMoveSize:"void*",hwndCaret:"void*",rcCaret:"RECT"});const r=e.sizeof("GUITHREADINFO"),t=e.load("user32.dll"),l=t.func("int SetProcessDPIAware(void)"),E=t.func("int SetCursorPos(int X, int Y)"),f=t.func("int GetCursorPos(_Out_ POINT *lpPoint)"),F=t.func("void mouse_event(uint dwFlags, int dx, int dy, int dwData, int64 dwExtraInfo)"),h=t.func("void keybd_event(uint8 bVk, uint8 bScan, uint dwFlags, int64 dwExtraInfo)"),I=t.func("void* GetForegroundWindow(void)"),T=t.func("uint32 GetWindowThreadProcessId(void* hWnd, _Out_ uint32 *lpdwProcessId)"),p=t.func("int GetGUIThreadInfo(uint32 idThread, _Inout_ GUITHREADINFO *lpGUI)"),w=t.func("int GetClassNameW(void* hWnd, char16_t *lpClassName, int nMaxCount)");l(),o={setCursorPos:(n,i)=>E(Math.round(n),Math.round(i))===1,getCursorPos:()=>{const n={};return f(n)===0?null:{x:n.x??0,y:n.y??0}},mouseEvent:(n,i,u,s)=>F(n,Math.round(i),Math.round(u),s,0),keybdEvent:(n,i)=>h(n,0,i,0),isInputFocused:()=>{try{const n=I();if(!n)return!1;const u=T(n,[0]);if(!u)return!1;const s={cbSize:r};if(p(u,s)===0)return!1;if(s.hwndCaret)return!0;const d=new Uint16Array(256);if(w(s.hwndFocus,d,256)===0)return!1;let a="";for(let c=0;c<d.length&&d[c]!==0;c++)a+=String.fromCharCode(d[c]);return C(a)}catch{return!1}}}}catch(r){console.warn("[remote-desktop] Win32 FFI init failed, falling back to PowerShell:",r.message),o=null}return o}function m(){o=void 0}export{W as KEYEVENTF_KEYUP,S as MOUSEEVENTF_HWHEEL,N as MOUSEEVENTF_LEFTDOWN,v as MOUSEEVENTF_LEFTUP,g as MOUSEEVENTF_MOVE,P as MOUSEEVENTF_RIGHTDOWN,_ as MOUSEEVENTF_RIGHTUP,O as MOUSEEVENTF_WHEEL,U as initWin32Api,C as isInputControlClassName,m as resetWin32ApiForTest};
2
+
@@ -0,0 +1,2 @@
1
+
2
+ export declare const backgroundTasksRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,2 @@
1
+ import{promises as k}from"node:fs";import l from"node:os";import c from"node:path";import{Router as I}from"express";import{validateToken as u}from"../middleware/auth.js";import{createLogger as A}from"../utils/logger.js";import{listPtySessions as y}from"./pty.js";const g=A("acode-bg-tasks"),m=I(),S=c.join(l.homedir(),".acode","bg-tasks");function j(r){return String(r).replace(/[^a-zA-Z0-9_-]/g,"_")}function w(r){return c.join(S,j(r),"state.json")}m.get("/acode-bg-tasks/:agentId",u,async(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const i=w(String(e));try{const a=await k.readFile(i,"utf-8"),n=JSON.parse(a),f=(Array.isArray(n.tasks)?n.tasks:[]).map(p=>{const t=p;return{taskId:t.taskId,status:t.status,command:t.commandPreview??t.command,commandPreview:t.commandPreview,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}});s.json({ok:!0,agentId:String(e),updatedAt:typeof n.updatedAt=="number"?n.updatedAt:null,tasks:f})}catch(a){if((typeof a=="object"&&a!==null?Reflect.get(a,"code"):void 0)==="ENOENT"){s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}if(a instanceof SyntaxError){g.warn("[acode-bg-tasks] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",i),s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}const d=a;g.error("[acode-bg-tasks] \u8BFB\u53D6\u5931\u8D25: %s",d.message),s.status(500).json({ok:!1,error:d.message})}}),m.get("/acode-agent-pty-sessions/:agentId",u,(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const i=String(e),n=y().filter(d=>d.ownerUserId===i);s.json({ok:!0,agentId:i,sessions:n})});export{m as backgroundTasksRouter};
2
+
@@ -0,0 +1,2 @@
1
+
2
+ export declare const browserListenersRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,2 @@
1
+ import{Router as u}from"express";import{validateToken as p,validateTokenOrLoopback as f}from"../middleware/auth.js";import{createLogger as w}from"../utils/logger.js";import{addBrowserListener as y,listBrowserListeners as k,removeBrowserListener as h,toggleBrowserListener as j,updateBrowserListener as S,collectAgentListenerEvents as v}from"../services/browser-session-manager.js";const g=w("browser-listeners"),m=u();m.get("/browser-listeners/:agentId/events",f,async(s,r)=>{const n=String(s.params.agentId??"").trim();if(!n){r.status(400).json({ok:!1,error:"agentId is required"});return}try{const o=await v(n);r.json({ok:!0,agentId:n,events:o})}catch(o){const e=o;g.error("[browser-listeners] \u6536\u96C6\u4E8B\u4EF6\u5931\u8D25: %s",e.message),r.status(500).json({ok:!1,error:e.message})}}),m.post("/browser-listeners/:agentId/:browserId",p,async(s,r)=>{const n=String(s.params.agentId??"").trim(),o=String(s.params.browserId??"").trim();if(!n||!o){r.status(400).json({ok:!1,error:"agentId and browserId are required"});return}const e=s.body,t=typeof e.selector=="string"?e.selector.trim():"",d=typeof e.prompt=="string"?e.prompt.trim():"";if(!t){r.status(400).json({ok:!1,error:"selector is required"});return}if(!d){r.status(400).json({ok:!1,error:"prompt is required"});return}const c=typeof e.pathPattern=="string"&&e.pathPattern.trim()?e.pathPattern.trim():"",l=typeof e.enabled=="boolean"?e.enabled:!0,I=Array.isArray(e.changeTypes)?e.changeTypes.filter(i=>typeof i=="string"&&(i==="added"||i==="removed"||i==="changed"||i==="exists")):void 0;try{const i=await y(n,o,{selector:t,pathPattern:c,prompt:d,enabled:l,changeTypes:I});r.json({ok:!0,listener:i})}catch(i){const a=i;g.error("[browser-listeners] \u6DFB\u52A0\u5931\u8D25: %s",a.message),r.status(500).json({ok:!1,error:a.message})}}),m.get("/browser-listeners/:agentId/:browserId",p,async(s,r)=>{const n=String(s.params.agentId??"").trim(),o=String(s.params.browserId??"").trim();if(!n||!o){r.status(400).json({ok:!1,error:"agentId and browserId are required"});return}try{const e=await k(n,o);r.json({ok:!0,agentId:n,browserId:o,listeners:e})}catch(e){const t=e;g.error("[browser-listeners] \u5217\u8868\u5931\u8D25: %s",t.message),r.status(500).json({ok:!1,error:t.message})}}),m.patch("/browser-listeners/:agentId/:browserId/:listenerId",p,async(s,r)=>{const n=String(s.params.agentId??"").trim(),o=String(s.params.browserId??"").trim(),e=String(s.params.listenerId??"").trim();if(!n||!o||!e){r.status(400).json({ok:!1,error:"agentId, browserId and listenerId are required"});return}const t=s.body,d=typeof t.enabled=="boolean"?t.enabled:void 0;try{const c=await j(n,o,e,d);if(!c){r.status(404).json({ok:!1,error:`listener ${e} not found`});return}r.json({ok:!0,listener:c})}catch(c){const l=c;g.error("[browser-listeners] \u5207\u6362\u5931\u8D25: %s",l.message),r.status(500).json({ok:!1,error:l.message})}}),m.put("/browser-listeners/:agentId/:browserId/:listenerId",p,async(s,r)=>{const n=String(s.params.agentId??"").trim(),o=String(s.params.browserId??"").trim(),e=String(s.params.listenerId??"").trim();if(!n||!o||!e){r.status(400).json({ok:!1,error:"agentId, browserId and listenerId are required"});return}const t=s.body,d=typeof t.selector=="string"?t.selector.trim():void 0,c=typeof t.prompt=="string"?t.prompt.trim():void 0,l=typeof t.pathPattern=="string"?t.pathPattern:void 0,I=typeof t.enabled=="boolean"?t.enabled:void 0,i=Array.isArray(t.changeTypes)?t.changeTypes.filter(a=>typeof a=="string"&&(a==="added"||a==="removed"||a==="changed"||a==="exists")):void 0;try{const a=await S(n,o,e,{selector:d,pathPattern:l,prompt:c,enabled:I,changeTypes:i});if(!a){r.status(404).json({ok:!1,error:`listener ${e} not found`});return}r.json({ok:!0,listener:a})}catch(a){const b=a;g.error("[browser-listeners] \u66F4\u65B0\u5931\u8D25: %s",b.message),r.status(500).json({ok:!1,error:b.message})}}),m.delete("/browser-listeners/:agentId/:browserId/:listenerId",p,async(s,r)=>{const n=String(s.params.agentId??"").trim(),o=String(s.params.browserId??"").trim(),e=String(s.params.listenerId??"").trim();if(!n||!o||!e){r.status(400).json({ok:!1,error:"agentId, browserId and listenerId are required"});return}try{const t=await h(n,o,e);r.json({ok:t,listenerId:e,removed:t})}catch(t){const d=t;g.error("[browser-listeners] \u5220\u9664\u5931\u8D25: %s",d.message),r.status(500).json({ok:!1,error:d.message})}});export{m as browserListenersRouter};
2
+
@@ -0,0 +1,2 @@
1
+
2
+ export declare const browserSessionsRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,2 @@
1
+ import{Router as m}from"express";import{validateToken as a}from"../middleware/auth.js";import{createLogger as c}from"../utils/logger.js";import{closeBrowserSessionById as u,isBrowserSessionManagerStarted as i,listBrowserWsUrls as w}from"../services/browser-session-manager.js";const g=c("browser-sessions"),d=m();d.get("/browser-sessions/:agentId",a,async(o,s)=>{const r=String(o.params.agentId??"");if(!r.trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}try{if(!i()){s.json({ok:!0,agentId:r,sessions:[]});return}const e=w(r);s.json({ok:!0,agentId:r,sessions:e})}catch(e){const t=e;g.error("[browser-sessions] \u5217\u8868\u5931\u8D25: %s",t.message),s.status(500).json({ok:!1,error:t.message})}}),d.delete("/browser-sessions/:agentId/:browserId",a,async(o,s)=>{const r=String(o.params.agentId??""),e=String(o.params.browserId??"");if(!r.trim()||!e.trim()){s.status(400).json({ok:!1,error:"agentId and browserId are required"});return}try{if(!i()){s.json({ok:!0,agentId:r,browserId:e});return}await u(r,e),s.json({ok:!0,agentId:r,browserId:e})}catch(t){const n=t;g.error("[browser-sessions] \u5173\u95ED\u5931\u8D25: %s",n.message),s.status(500).json({ok:!1,error:n.message})}});export{d as browserSessionsRouter};
2
+