@ycheung5/pi-computer-use 0.0.0-stage → 0.1.0

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 (92) hide show
  1. package/GLOSSARY.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -2
  4. package/assets/accessibility.cs +208 -0
  5. package/assets/browser-indicators.cs +37 -0
  6. package/assets/capture.cs +215 -0
  7. package/assets/clipboard.cs +197 -0
  8. package/assets/controls.cs +249 -0
  9. package/assets/indicator-preview.png +0 -0
  10. package/assets/indicators.cs +252 -0
  11. package/assets/node-LICENSE.txt +2732 -0
  12. package/assets/ocr.cs +86 -0
  13. package/assets/ownership.cs +59 -0
  14. package/assets/pointer.cs +229 -0
  15. package/assets/puppeteer-LICENSE.txt +202 -0
  16. package/assets/readme-banner.svg +33 -0
  17. package/assets/stop.cs +197 -0
  18. package/assets/text.cs +249 -0
  19. package/assets/wgc.cs +167 -0
  20. package/assets/window-management.cs +217 -0
  21. package/assets/window-marker.cs +23 -0
  22. package/assets/window-references.cs +80 -0
  23. package/assets/windows.cs +127 -0
  24. package/dist/accessibility.d.ts +29 -0
  25. package/dist/accessibility.js +258 -0
  26. package/dist/browser-actions.d.ts +21 -0
  27. package/dist/browser-actions.js +213 -0
  28. package/dist/browser-indicators.d.ts +23 -0
  29. package/dist/browser-indicators.js +69 -0
  30. package/dist/browser-profiles.d.ts +10 -0
  31. package/dist/browser-profiles.js +174 -0
  32. package/dist/browser.d.ts +11 -0
  33. package/dist/browser.js +2071 -0
  34. package/dist/capture.d.ts +74 -0
  35. package/dist/capture.js +199 -0
  36. package/dist/client.d.ts +38 -0
  37. package/dist/client.js +331 -0
  38. package/dist/desktop.d.ts +20 -0
  39. package/dist/desktop.js +42 -0
  40. package/dist/extension.d.ts +4 -0
  41. package/dist/extension.js +409 -0
  42. package/dist/launcher.d.ts +6 -0
  43. package/dist/launcher.js +85 -0
  44. package/dist/native.d.ts +11 -0
  45. package/dist/native.js +145 -0
  46. package/dist/ocr.d.ts +52 -0
  47. package/dist/ocr.js +246 -0
  48. package/dist/ownership.d.ts +4 -0
  49. package/dist/ownership.js +24 -0
  50. package/dist/pointer.d.ts +108 -0
  51. package/dist/pointer.js +174 -0
  52. package/dist/protocol.d.ts +15 -0
  53. package/dist/protocol.js +1 -0
  54. package/dist/runtime.d.ts +8 -0
  55. package/dist/runtime.js +72 -0
  56. package/dist/settings.d.ts +85 -0
  57. package/dist/settings.js +449 -0
  58. package/dist/stop.d.ts +24 -0
  59. package/dist/stop.js +156 -0
  60. package/dist/text.d.ts +100 -0
  61. package/dist/text.js +183 -0
  62. package/dist/waits.d.ts +19 -0
  63. package/dist/waits.js +262 -0
  64. package/dist/window-management.d.ts +25 -0
  65. package/dist/window-management.js +167 -0
  66. package/dist/worker.d.ts +1 -0
  67. package/dist/worker.js +540 -0
  68. package/docs/README.md +54 -0
  69. package/docs/adr/0001-preserve-reference-behavior.md +9 -0
  70. package/docs/adr/0002-window-lifetime-references.md +11 -0
  71. package/docs/adr/0003-exclusive-desktop-control.md +11 -0
  72. package/docs/adr/0004-pi-first-acceptance.md +9 -0
  73. package/docs/adr/0005-default-apps-and-workspace-reuse.md +7 -0
  74. package/docs/agents/domain.md +9 -0
  75. package/docs/agents/issue-tracker.md +10 -0
  76. package/docs/agents/triage-labels.md +9 -0
  77. package/docs/browser.md +113 -0
  78. package/docs/implementation-plan.md +139 -0
  79. package/docs/indicators.md +53 -0
  80. package/docs/native-interop-research.md +17 -0
  81. package/docs/ownership.md +25 -0
  82. package/docs/pi-extension-research.md +57 -0
  83. package/docs/pointer.md +65 -0
  84. package/docs/runtime.md +42 -0
  85. package/docs/settings.md +33 -0
  86. package/docs/spec.md +161 -0
  87. package/docs/stopping.md +83 -0
  88. package/docs/text.md +79 -0
  89. package/docs/tickets.md +84 -0
  90. package/docs/validation.md +1372 -0
  91. package/docs/windows.md +171 -0
  92. package/package.json +134 -4
@@ -0,0 +1,217 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.IO;
4
+ using System.Runtime.InteropServices;
5
+ using System.Text;
6
+ using System.Threading;
7
+ using Microsoft.Win32;
8
+
9
+ partial class WindowsBridge {
10
+ [DllImport("user32.dll",EntryPoint="ShowWindowAsync")] static extern bool ManagementShow(IntPtr window,int command);
11
+ [DllImport("user32.dll",EntryPoint="SetWindowPos",SetLastError=true)] static extern bool ManagementMove(IntPtr window,IntPtr after,int x,int y,int width,int height,uint flags);
12
+ [DllImport("user32.dll",EntryPoint="PostMessageW",SetLastError=true)] static extern bool ManagementPost(IntPtr window,uint message,IntPtr wParam,IntPtr lParam);
13
+ [DllImport("user32.dll",EntryPoint="AttachThreadInput")] static extern bool ManagementAttach(uint from,uint to,bool attach);
14
+ [DllImport("kernel32.dll",EntryPoint="GetCurrentThreadId")] static extern uint ManagementThread();
15
+ static uint ManagementOwner(IntPtr window) {uint owner;GetWindowThreadProcessId(window,out owner);return owner;}
16
+ static void ManagementAssert(IntPtr window,uint owner) {CheckWindowReference(window);if(!IsWindow(window)||ManagementOwner(window)!=owner)throw new InvalidOperationException("WINDOW_IDENTITY_CHANGED: refresh window observations before acting");}
17
+ static bool? ManagementResponsive(IntPtr window) {UIntPtr ignored;bool answered=SendMessageTimeout(window,0,UIntPtr.Zero,IntPtr.Zero,3,100,out ignored)!=IntPtr.Zero;int error=Marshal.GetLastWin32Error();return !answered&&error==5?(bool?)null:answered;}
18
+ static Dictionary<string,object> ManagementWindow(IntPtr window,uint owner) {
19
+ Rect bounds;if(!GetWindowRect(window,out bounds))throw new InvalidOperationException("WINDOW_BOUNDS_UNAVAILABLE");
20
+ StringBuilder title=new StringBuilder(2048);GetWindowText(window,title,title.Capacity);
21
+ return new Dictionary<string,object>{{"nativeWindowHandle",window.ToInt64()},{"processId",owner},{"name",title.ToString()},{"boundingBox",new Dictionary<string,object>{{"x",bounds.Left},{"y",bounds.Top},{"width",bounds.Right-bounds.Left},{"height",bounds.Bottom-bounds.Top}}}};
22
+ }
23
+ // Read only the documented, 8-byte-aligned WTSINFOEX level-1 prefix;
24
+ // account-name fields are never copied or reported.
25
+ [StructLayout(LayoutKind.Explicit,Size=20)]struct ManagementSessionHeader {
26
+ [FieldOffset(0)]public uint Level;[FieldOffset(8)]public uint Session;
27
+ [FieldOffset(12)]public uint Connection;[FieldOffset(16)]public int Flags;
28
+ }
29
+ [DllImport("kernel32.dll",EntryPoint="GetCurrentProcessId")]static extern uint ManagementProcessId();
30
+ [DllImport("kernel32.dll",EntryPoint="ProcessIdToSessionId")]static extern bool ManagementSessionId(uint process,out uint session);
31
+ [DllImport("wtsapi32.dll",EntryPoint="WTSQuerySessionInformationW")]static extern bool ManagementQuerySession(IntPtr server,uint session,int kind,out IntPtr buffer,out uint bytes);
32
+ [DllImport("wtsapi32.dll",EntryPoint="WTSFreeMemory")]static extern void ManagementFreeSession(IntPtr buffer);
33
+ static void ManagementEnsureUnlockedSession() {
34
+ uint session;if(!ManagementSessionId(ManagementProcessId(),out session))return;
35
+ IntPtr buffer=IntPtr.Zero;uint bytes;
36
+ try {
37
+ if(!ManagementQuerySession(IntPtr.Zero,session,25,out buffer,out bytes) || buffer==IntPtr.Zero || bytes<20)return;
38
+ ManagementSessionHeader state=(ManagementSessionHeader)Marshal.PtrToStructure(buffer,typeof(ManagementSessionHeader));
39
+ if(state.Level==1 && state.Session==session && state.Flags==0)throw new InvalidOperationException("DESKTOP_SESSION_LOCKED: unlock Windows before requesting foreground input");
40
+ }finally {if(buffer!=IntPtr.Zero)ManagementFreeSession(buffer);}
41
+ // Unknown/unavailable session state does not invent a lock diagnosis;
42
+ // the existing identity, foreground and input guards still apply.
43
+ }
44
+ [DllImport("user32.dll",EntryPoint="BringWindowToTop")]static extern bool ManagementBringToTop(IntPtr window);
45
+ sealed class ActivationRefusedException : InvalidOperationException {
46
+ public readonly Dictionary<string,object> Diagnostic;
47
+ public ActivationRefusedException(Dictionary<string,object> diagnostic) : base("TARGET_ACTIVATION_REFUSED: Windows did not grant foreground access") {Diagnostic=diagnostic;}
48
+ }
49
+ static string ManagementForegroundRelationship(IntPtr foreground,IntPtr window,uint owner) {
50
+ if(foreground==IntPtr.Zero)return "unavailable";
51
+ if(foreground==window)return "target";
52
+ if(GetAncestor(foreground,2)==window)return "target_child";
53
+ return ManagementOwner(foreground)==owner?"same_process":"other_process";
54
+ }
55
+ static bool ManagementActivate(IntPtr window,uint owner) {return ManagementActivate(window,owner,null);}
56
+ // One active operation per desktop; retain the existing lifetime witness,
57
+ // not another element cache. A helper restart forgets visible intent safely.
58
+ static string visibleOperation;
59
+ static WindowReference visibleTarget;
60
+ static void ApplyVisibleIntent(Dictionary<string,object> args,IntPtr window) {
61
+ if(args.ContainsKey("activate") || TextOption(args,"dispatch","")=="background" || TextOption(args,"method","")=="background")return;
62
+ if(visibleTarget!=null && visibleOperation==pointerOperation && visibleTarget.Window==window && visibleTarget.Owner==WindowOwner(window) && WindowMarker.Matches(window,visibleTarget.Owner,visibleTarget.Marker))args["activate"]=true;
63
+ }
64
+ static bool ManagementActivate(IntPtr window,uint owner,Dictionary<string,object> diagnostics) {
65
+ ManagementAssert(window,owner);ManagementEnsureUnlockedSession();
66
+ if(IsIconic(window)){BeginWindowDispatch();ManagementShow(window,9);}
67
+ if(GetForegroundWindow()==window)return true;
68
+ BeginWindowDispatch();bool initial=SetForegroundWindow(window);
69
+ if(diagnostics!=null)diagnostics["initialRequestAccepted"]=initial;
70
+ if(GetForegroundWindow()==window)return true;
71
+ IntPtr front=GetForegroundWindow();uint ignored;uint frontThread=GetWindowThreadProcessId(front,out ignored),own=ManagementThread();bool attached=false;
72
+ if(diagnostics!=null)diagnostics["foregroundBeforeFallback"]=ManagementForegroundRelationship(front,window,owner);
73
+ try {
74
+ bool attempted=frontThread!=0&&frontThread!=own;
75
+ if(attempted)attached=ManagementAttach(own,frontThread,true);
76
+ if(diagnostics!=null){diagnostics["inputThreadAttachAttempted"]=attempted;if(attempted)diagnostics["inputThreadAttached"]=attached;}
77
+ ManagementAssert(window,owner);BeginWindowDispatch();bool raised=ManagementBringToTop(window);ManagementAssert(window,owner);BeginWindowDispatch();bool requested=SetForegroundWindow(window);
78
+ if(diagnostics!=null){diagnostics["raiseAccepted"]=raised;diagnostics["fallbackRequestAccepted"]=requested;}
79
+ }
80
+ finally {if(attached)ManagementAttach(own,frontThread,false);}
81
+ for(int index=0;index<8&&GetForegroundWindow()!=window;index++)Thread.Sleep(10);
82
+ IntPtr finalForeground=GetForegroundWindow();
83
+ if(diagnostics!=null)diagnostics["foregroundAfterFallback"]=ManagementForegroundRelationship(finalForeground,window,owner);
84
+ return finalForeground==window;
85
+ }
86
+ static object ManageNativeWindow(string action,Dictionary<string,object> args) {
87
+ if(action=="launch_app")return LaunchFromDesktop(args);
88
+ // Shared Win32 capture resolver performs HWND/PID/title matching without resolving UIA.
89
+ IntPtr window=ResolveCaptureWindow(args);uint owner=ManagementOwner(window);ManagementAssert(window,owner);
90
+ Dictionary<string,object> result=Result(true,action);result["nativeWindowHandle"]=window.ToInt64();result["processId"]=owner;result["window"]=ManagementWindow(window,owner);
91
+ bool? responsive=ManagementResponsive(window);result["targetResponsive"]=responsive;
92
+ ManagementAssert(window,owner);
93
+ if(action=="close_window") {
94
+ BeginWindowDispatch();bool posted=ManagementPost(window,0x0010,IntPtr.Zero,IntPtr.Zero);result["posted"]=posted;result["verification"]=posted?"sent":"unconfirmed";result["note"]=posted?"WM_CLOSE was dispatched; the application may veto closure or show a save dialog. Observe windows to verify.":"Windows refused posting WM_CLOSE; no closure was confirmed.";if(!posted)result["nativeError"]=Marshal.GetLastWin32Error();return result;
95
+ }
96
+ if(action=="move_window") {
97
+ int x=Convert.ToInt32(args["x"]),y=Convert.ToInt32(args["y"]);
98
+ BeginWindowDispatch();bool moved=ManagementMove(window,IntPtr.Zero,x,y,0,0,0x0001|0x0004|0x0010|0x4000);
99
+ result["moved"]=moved;result["x"]=x;result["y"]=y;result["verification"]=moved?"sent":"unconfirmed";
100
+ if(!moved)result["warning"]="SetWindowPos refused the move; the window may be unavailable or maximized.";
101
+ if(!args.ContainsKey("activate")||!Convert.ToBoolean(args["activate"]))return result;
102
+ }
103
+ bool activated=responsive==true&&ManagementActivate(window,owner);result["activated"]=activated;
104
+ if(activated){visibleOperation=pointerOperation;visibleTarget=ObserveWindowReference(window,result);if(visibleTarget!=null){currentReference=visibleTarget;result["windowRef"]=visibleTarget.Id;}}
105
+ if(!activated){result["verification"]="unconfirmed";result["warning"]=responsive==true?"Windows foreground rules refused activation; no keyboard or mouse workaround was sent.":"The target is unresponsive or inaccessible; activation was not attempted.";}
106
+ else if(action=="activate_window")result["verification"]="sent";
107
+ return result;
108
+ }
109
+
110
+ static readonly HashSet<string> ManagementCommandHosts=new HashSet<string>(StringComparer.OrdinalIgnoreCase){"cmd","powershell","pwsh","wt","wsl","bash","conhost","wscript","cscript","mshta","rundll32","regsvr32","msiexec","forfiles","schtasks"};
111
+ [DllImport("shlwapi.dll",CharSet=CharSet.Unicode,EntryPoint="AssocQueryStringW")]static extern int ManagementAssociation(uint flags,int kind,string association,string verb,StringBuilder output,ref uint length);
112
+ static string AssociationString(string association,bool protocol,int kind) {
113
+ // ASSOCF_INIT_IGNOREUNKNOWN refuses the generic Open-With fallback;
114
+ // ASSOCF_IS_PROTOCOL honors current-user HTTP(S) associations.
115
+ uint length=0;uint flags=0x400u|(protocol?0x1000u:0u);
116
+ ManagementAssociation(flags,kind,association,"open",null,ref length);
117
+ if(length==0 || length>32768)return "";
118
+ StringBuilder output=new StringBuilder((int)length);
119
+ return ManagementAssociation(flags,kind,association,"open",output,ref length)==0?output.ToString():"";
120
+ }
121
+ static Dictionary<string,object> DefaultLaunchHandler(string association,bool protocol) {
122
+ string executable=AssociationString(association,protocol,2),name=AssociationString(association,protocol,4),progId=AssociationString(association,protocol,20);
123
+ if(executable.Length==0 && name.Length==0 && progId.Length==0)throw new InvalidOperationException("DEFAULT_APP_UNAVAILABLE: Windows did not resolve an open handler for "+association);
124
+ return new Dictionary<string,object>{{"association",association},{"executable",executable},{"name",name},{"progId",progId},{"source","windows-association"}};
125
+ }
126
+ static void ReleaseLaunchCom(object value) {try{if(value!=null&&Marshal.IsComObject(value))Marshal.ReleaseComObject(value);}catch(InvalidComObjectException){}}
127
+ static bool FullyQualifiedLaunchPath(string value) {return value.Length>=3&&Char.IsLetter(value[0])&&value[1]==':'&&(value[2]=='\\'||value[2]=='/')||value.StartsWith("\\\\",StringComparison.Ordinal)&&value.TrimStart('\\').Split('\\').Length>=2;}
128
+ static string ResolveLaunchFile(string target) {
129
+ if(Path.IsPathRooted(target)) {
130
+ if(!FullyQualifiedLaunchPath(target))throw new InvalidOperationException("LAUNCH_INVALID_TARGET: use a fully qualified absolute path");
131
+ if(!File.Exists(target))throw new InvalidOperationException("LAUNCH_NOT_FOUND: target file does not exist");
132
+ return Path.GetFullPath(target);
133
+ }
134
+ if(target.IndexOfAny(new char[]{'\\','/',':'})>=0)return null;
135
+ string[] extensions=Path.GetExtension(target).Length>0?new string[]{""}:new string[]{".exe",".com",".lnk"};
136
+ foreach(string directory in (Environment.GetEnvironmentVariable("PATH")??"").Split(';'))foreach(string extension in extensions) {
137
+ string candidate;try{candidate=Path.Combine(directory.Trim('"'),target+extension);}catch(ArgumentException){continue;}
138
+ if(File.Exists(candidate))return Path.GetFullPath(candidate);
139
+ }
140
+ string name=target.EndsWith(".exe",StringComparison.OrdinalIgnoreCase)?target:target+".exe";
141
+ foreach(RegistryHive hive in new RegistryHive[]{RegistryHive.CurrentUser,RegistryHive.LocalMachine})foreach(RegistryView view in new RegistryView[]{RegistryView.Registry64,RegistryView.Registry32}) {
142
+ try{using(RegistryKey root=RegistryKey.OpenBaseKey(hive,view))using(RegistryKey key=root.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"+name)){
143
+ string candidate=key==null?null:Convert.ToString(key.GetValue(""));if(String.IsNullOrEmpty(candidate))continue;candidate=Environment.ExpandEnvironmentVariables(candidate.Trim('"'));if(File.Exists(candidate))return Path.GetFullPath(candidate);
144
+ }}catch(System.Security.SecurityException){}
145
+ }
146
+ return null;
147
+ }
148
+ static string ResolveInstalledLaunch(string target) {
149
+ object application=null,folder=null,items=null;List<string> matches=new List<string>();
150
+ try{
151
+ application=Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));folder=((dynamic)application).NameSpace("shell:AppsFolder");if(folder==null)throw new InvalidOperationException("Installed app enumeration is unavailable");items=((dynamic)folder).Items();int count=((dynamic)items).Count;
152
+ for(int index=0;index<count;index++) {
153
+ object item=null;try{item=((dynamic)items).Item(index);string name=Convert.ToString(((dynamic)item).Name),id=Convert.ToString(((dynamic)item).ExtendedProperty("System.AppUserModel.ID"));
154
+ if(String.Equals(target,name,StringComparison.OrdinalIgnoreCase)||String.Equals(target,id,StringComparison.OrdinalIgnoreCase)){if(String.IsNullOrEmpty(id))id=Convert.ToString(((dynamic)item).Path);if(!String.IsNullOrEmpty(id))matches.Add(id);}
155
+ }finally{ReleaseLaunchCom(item);}
156
+ }
157
+ }finally{ReleaseLaunchCom(items);ReleaseLaunchCom(folder);ReleaseLaunchCom(application);}
158
+ if(matches.Count==0)throw new InvalidOperationException("LAUNCH_NOT_FOUND: use an executable path or exact installed app name/AppUserModelID");
159
+ if(matches.Count!=1)throw new InvalidOperationException("LAUNCH_AMBIGUOUS: use a full executable path or AppUserModelID");
160
+ return "shell:AppsFolder\\"+matches[0];
161
+ }
162
+ static void CheckLaunchProgram(string file,string arguments) {
163
+ string program=file,embedded="";HashSet<string> seen=new HashSet<string>(StringComparer.OrdinalIgnoreCase);
164
+ while(String.Equals(Path.GetExtension(program),".lnk",StringComparison.OrdinalIgnoreCase)){
165
+ if(!seen.Add(program)||seen.Count>8)throw new InvalidOperationException("LAUNCH_INVALID_TARGET: shortcut chain is cyclic or too deep");
166
+ object shell=null,link=null;try{shell=Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell"));link=((dynamic)shell).CreateShortcut(program);embedded+=" "+Convert.ToString(((dynamic)link).Arguments);program=Environment.ExpandEnvironmentVariables(Convert.ToString(((dynamic)link).TargetPath));if(String.IsNullOrWhiteSpace(program))throw new InvalidOperationException("LAUNCH_INVALID_TARGET: shortcut has no executable target");}finally{ReleaseLaunchCom(link);ReleaseLaunchCom(shell);}
167
+ }
168
+ if((arguments.Trim().Length>0||embedded.Trim().Length>0)&&ManagementCommandHosts.Contains(Path.GetFileNameWithoutExtension(program)))throw new InvalidOperationException("LAUNCH_COMMAND_REFUSED: command/script hosts accept no arguments through launch_app; use Pi's shell tool");
169
+ }
170
+ static object LaunchFromDesktop(Dictionary<string,object> args) {
171
+ Dictionary<string,object> result=null;Exception failure=null;
172
+ Thread thread=new Thread(delegate(){
173
+ try{
174
+ string target=Convert.ToString(args["target"]).Trim(),arguments=Convert.ToString(args["arguments"]),directory=Convert.ToString(args["workingDirectory"]);
175
+ Uri uri;bool protocol=Uri.TryCreate(target,UriKind.Absolute,out uri)&&(uri.Scheme=="http"||uri.Scheme=="https");
176
+ if(!protocol)target=Environment.ExpandEnvironmentVariables(target);
177
+ if(protocol && String.IsNullOrEmpty(uri.Host))throw new InvalidOperationException("LAUNCH_INVALID_TARGET: HTTP(S) URL requires a host");
178
+ bool browser=String.Equals(target,"browser",StringComparison.OrdinalIgnoreCase);
179
+ string file=protocol||browser?null:ResolveLaunchFile(target),kind=browser?"default-browser":protocol?"url":file==null?"installed-app":"file";
180
+ Dictionary<string,object> handler=null;
181
+ if(file!=null){string extension=Path.GetExtension(file);if(String.Equals(extension,".exe",StringComparison.OrdinalIgnoreCase)||String.Equals(extension,".com",StringComparison.OrdinalIgnoreCase)||String.Equals(extension,".lnk",StringComparison.OrdinalIgnoreCase))CheckLaunchProgram(file,arguments);else {
182
+ if(new HashSet<string>(StringComparer.OrdinalIgnoreCase){".bat",".cmd",".ps1",".vbs",".vbe",".js",".jse",".wsf",".wsh",".hta",".reg",".msi",".msp",".scr",".cpl"}.Contains(extension))throw new InvalidOperationException("LAUNCH_COMMAND_REFUSED: use Pi's shell tool for scripts/installers");
183
+ kind="document";handler=DefaultLaunchHandler(Path.GetExtension(file),false);
184
+ }target=file;}
185
+ else if(browser)handler=DefaultLaunchHandler("https",true);
186
+ else if(protocol)handler=DefaultLaunchHandler(uri.Scheme,true);
187
+ else target=ResolveInstalledLaunch(target);
188
+ if(handler!=null) {
189
+ if(arguments.Length>0||directory.Length>0)throw new InvalidOperationException("LAUNCH_INVALID_ARGUMENTS: default-handler URLs/documents do not accept arguments or workingDirectory");
190
+ string handlerExecutable=Convert.ToString(handler["executable"]);
191
+ if(ManagementCommandHosts.Contains(Path.GetFileNameWithoutExtension(handlerExecutable)))throw new InvalidOperationException("LAUNCH_COMMAND_REFUSED: default handler is a command/script host; use Pi's shell tool");
192
+ }
193
+ if(directory.Length>0){directory=Environment.ExpandEnvironmentVariables(directory);if(!FullyQualifiedLaunchPath(directory)||!Directory.Exists(directory))throw new InvalidOperationException("LAUNCH_INVALID_DIRECTORY: use an existing fully qualified directory");directory=Path.GetFullPath(directory);}
194
+ if(kind=="installed-app"&&(arguments.Length>0||directory.Length>0))throw new InvalidOperationException("LAUNCH_INVALID_ARGUMENTS: installed AppUserModelID launches do not accept arguments or workingDirectory");
195
+ if(Option(args,"resolveOnly",false)) {
196
+ result=Result(true,"launch_app");result["target"]=target;result["targetKind"]=kind;result["handler"]=handler;result["verification"]="resolved";result["launched"]=false;
197
+ result["note"]="Resolution only; no app was opened. Select and observe the intended existing window before creating a task tab/document. Handler metadata is not a window identity.";return;
198
+ }
199
+ if(browser){target=Convert.ToString(handler["executable"]);if(target.Length==0)throw new InvalidOperationException("DEFAULT_APP_UNAVAILABLE: Windows resolved the browser but did not provide a blank-window launch executable; reuse its observed window or open a requested HTTP(S) URL");}
200
+ object windows=null,desktop=null,document=null,application=null;
201
+ try{
202
+ windows=Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39")));
203
+ object location=0,root=null;int handle=0;desktop=((dynamic)windows).FindWindowSW(ref location,ref root,8,out handle,1);
204
+ if(desktop==null)throw new InvalidOperationException("interactive Explorer desktop is unavailable");
205
+ document=((dynamic)desktop).Document;application=((dynamic)document).Application;
206
+ ((dynamic)application).ShellExecute(target,arguments,directory,"open",Convert.ToBoolean(args["activate"])?1:4);
207
+ }catch(Exception error){throw new InvalidOperationException("LAUNCH_SHELL_FAILED: "+error.Message+"; no child-process fallback or replay was attempted",error);}
208
+ finally{ReleaseLaunchCom(application);ReleaseLaunchCom(document);ReleaseLaunchCom(desktop);ReleaseLaunchCom(windows);}
209
+ result=Result(true,"launch_app");result["target"]=target;result["targetKind"]=kind;if(handler!=null)result["handler"]=handler;result["launchMethod"]="explorer-shell";result["verification"]="dispatched";result["note"]="Explorer accepted the launch. New windows are observations, not confirmed matches. The app can reuse a window or activate itself; use list_windows/wait_for to verify. No elevation was requested.";
210
+ }catch(Exception error){failure=error;}
211
+ });
212
+ thread.IsBackground=true;thread.SetApartmentState(ApartmentState.STA);thread.Start();
213
+ if(!thread.Join(15000))throw new InvalidOperationException("LAUNCH_PENDING_TIMEOUT: Explorer outcome is unknown; do not replay the launch");
214
+ if(failure!=null)throw failure;
215
+ return result;
216
+ }
217
+ }
@@ -0,0 +1,23 @@
1
+ using System;
2
+ using System.Runtime.InteropServices;
3
+
4
+ // Stateless witness checks shared by the authoritative bridge and delegated WGC.
5
+ static class WindowMarker {
6
+ [DllImport("user32.dll",CharSet=CharSet.Unicode,SetLastError=true)] static extern bool SetProp(IntPtr window,string name,IntPtr value);
7
+ [DllImport("user32.dll",CharSet=CharSet.Unicode)] static extern IntPtr GetProp(IntPtr window,string name);
8
+ [DllImport("user32.dll",CharSet=CharSet.Unicode)] static extern IntPtr RemoveProp(IntPtr window,string name);
9
+ [DllImport("user32.dll")] static extern bool IsWindow(IntPtr window);
10
+ [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr window,out uint owner);
11
+ internal static bool Matches(IntPtr window,uint owner,string name) {
12
+ uint actual;GetWindowThreadProcessId(window,out actual);
13
+ return owner!=0 && actual==owner && IsWindow(window) && GetProp(window,name)==new IntPtr(1);
14
+ }
15
+ internal static bool Bind(IntPtr window,uint owner,string name,out int error) {
16
+ uint actual;GetWindowThreadProcessId(window,out actual);error=0;
17
+ if(actual!=owner || owner==0 || !IsWindow(window))return false;
18
+ if(!SetProp(window,name,new IntPtr(1))){error=Marshal.GetLastWin32Error();return false;}
19
+ if(Matches(window,owner,name))return true;
20
+ Remove(window,owner,name);return false;
21
+ }
22
+ internal static void Remove(IntPtr window,uint owner,string name) {if(Matches(window,owner,name))RemoveProp(window,name);}
23
+ }
@@ -0,0 +1,80 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+
4
+ partial class WindowsBridge {
5
+ sealed class WindowReference {
6
+ internal string Id,Marker;internal IntPtr Window;internal uint Owner;internal DateTime Seen;
7
+ }
8
+ static readonly string referenceEpoch=Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+','-').Replace('/','_');
9
+ static long referenceSequence,elementSequence;
10
+ static readonly Dictionary<string,WindowReference> windowReferences=new Dictionary<string,WindowReference>();
11
+ static WindowReference currentReference;
12
+ static bool windowDispatchAttempted;
13
+ static void BeginWindowDispatch(){CheckReferenceBeforeDispatch();windowDispatchAttempted=true;}
14
+ static void FinishWindowReference(Dictionary<string,object> payload){
15
+ if(currentReference==null)return;
16
+ bool cleanupFailed=payload.ContainsKey("cleanupConfirmed") && !Convert.ToBoolean(payload["cleanupConfirmed"]);
17
+ if(!cleanupFailed && Convert.ToString(payload["action"])=="close_window" && windowDispatchAttempted && payload.ContainsKey("posted") && Convert.ToBoolean(payload["posted"]) && !IsWindow(currentReference.Window)){
18
+ // WM_CLOSE was posted; observing its intended disappearance is not a failed action.
19
+ payload["targetState"]="absent";payload["windowRef"]=currentReference.Id;return;
20
+ }
21
+ if(!CurrentReferenceIntact()){
22
+ payload["ok"]=false;payload.Remove("inputRequired");payload["dispatchAttempted"]=windowDispatchAttempted;
23
+ string warning=windowDispatchAttempted?"WINDOW_REFERENCE_LOST_AFTER_DISPATCH: app effects may have occurred; observe before further action; never replay":"WINDOW_REFERENCE_LOST: lifetime proof is unavailable; observe again";
24
+ if(payload.ContainsKey("cleanupConfirmed") && !Convert.ToBoolean(payload["cleanupConfirmed"]))payload["identityWarning"]=warning;else payload["error"]=warning;
25
+ if(windowDispatchAttempted){payload["effectsUncertain"]=true;if(!payload.ContainsKey("verification"))payload["verification"]="unconfirmed";}
26
+ }
27
+ payload["windowRef"]=currentReference.Id;
28
+ }
29
+ static bool CurrentReferenceIntact() {return currentReference==null || WindowMarker.Matches(currentReference.Window,currentReference.Owner,currentReference.Marker);}
30
+ static void CheckReferenceBeforeDispatch() {if(currentReference!=null)CheckWindowReference(currentReference.Window);}
31
+ static void CheckWindowReference(IntPtr window) {
32
+ if(currentReference!=null && currentReference.Window==window && !WindowMarker.Matches(window,currentReference.Owner,currentReference.Marker))throw new InvalidOperationException("WINDOW_REFERENCE_LOST: lifetime proof is unavailable; observe again; never replay uncertain effects");
33
+ }
34
+ static WindowReference ResolveWindowReference(Dictionary<string,object> args) {
35
+ if(!args.ContainsKey("windowRef"))return null;
36
+ string id=Convert.ToString(args["windowRef"]);WindowReference entry;
37
+ if(!windowReferences.TryGetValue(id,out entry))throw new InvalidOperationException("STALE_WINDOW_REFERENCE: unknown or expired reference; observe again");
38
+ if(entry.Seen<DateTime.UtcNow.AddMinutes(-5)){WindowMarker.Remove(entry.Window,entry.Owner,entry.Marker);windowReferences.Remove(id);throw new InvalidOperationException("STALE_WINDOW_REFERENCE: reference expired; observe again");}
39
+ if(!WindowMarker.Matches(entry.Window,entry.Owner,entry.Marker))throw new InvalidOperationException("WINDOW_REFERENCE_LOST: lifetime proof is unavailable; observe again");
40
+ if((args.ContainsKey("nativeWindowHandle") && Convert.ToInt64(args["nativeWindowHandle"])!=entry.Window.ToInt64()) || (args.ContainsKey("processId") && Convert.ToUInt32(args["processId"])!=entry.Owner) || (args.ContainsKey("windowTitle") && WindowTitle(entry.Window).IndexOf(Convert.ToString(args["windowTitle"]),StringComparison.OrdinalIgnoreCase)<0))throw new InvalidOperationException("WINDOW_REFERENCE_CONFLICT: selectors disagree with the observed lifetime");
41
+ if(TextOption(args,"scope","")=="desktop" || args.ContainsKey("browserTab"))throw new InvalidOperationException("WINDOW_REFERENCE_CONFLICT: window reference cannot target desktop or browser channel");
42
+ entry.Seen=DateTime.UtcNow;currentReference=entry;return entry;
43
+ }
44
+ static object ObserveReferencePresence(Dictionary<string,object> args) {
45
+ WindowReference entry;string id=Convert.ToString(args["windowRef"]);
46
+ if(windowReferences.TryGetValue(id,out entry) && ((args.ContainsKey("nativeWindowHandle") && Convert.ToInt64(args["nativeWindowHandle"])!=entry.Window.ToInt64()) || (args.ContainsKey("processId") && Convert.ToUInt32(args["processId"])!=entry.Owner)))throw new InvalidOperationException("WINDOW_REFERENCE_CONFLICT: selectors disagree with the observed lifetime");
47
+ if(windowReferences.TryGetValue(id,out entry) && entry.Seen>=DateTime.UtcNow.AddMinutes(-5) && !IsWindow(entry.Window)){
48
+ if(args.ContainsKey("windowTitle"))throw new InvalidOperationException("WINDOW_REFERENCE_CONSTRAINT_UNAVAILABLE: destroyed window title cannot be verified");
49
+ return new Dictionary<string,object>{{"ok",true},{"action","list_windows"},{"referencePresent",false},{"windows",new List<object>()}};
50
+ }
51
+ entry=ResolveWindowReference(args);
52
+ return new Dictionary<string,object>{{"ok",true},{"action","list_windows"},{"referencePresent",true},{"windows",new List<object>()}};
53
+ }
54
+ static void PruneWindowReferences() {
55
+ var remove=new List<string>();
56
+ foreach(var pair in windowReferences)if(pair.Value.Seen<DateTime.UtcNow.AddMinutes(-5) || !WindowMarker.Matches(pair.Value.Window,pair.Value.Owner,pair.Value.Marker))remove.Add(pair.Key);
57
+ foreach(string id in remove){WindowReference entry=windowReferences[id];WindowMarker.Remove(entry.Window,entry.Owner,entry.Marker);windowReferences.Remove(id);}
58
+ }
59
+ static WindowReference ObserveWindowReference(IntPtr window,Dictionary<string,object> metadata) {
60
+ if(window==IntPtr.Zero){metadata["windowRefUnavailable"]="WINDOW_REFERENCE_UNAVAILABLE: no native root could be established";return null;}
61
+ CheckWindowReference(window);uint owner=WindowOwner(window);
62
+ foreach(var pair in windowReferences)if(pair.Value.Window==window && pair.Value.Owner==owner && pair.Value.Seen>=DateTime.UtcNow.AddMinutes(-5) && WindowMarker.Matches(window,owner,pair.Value.Marker)){pair.Value.Seen=DateTime.UtcNow;return pair.Value;}
63
+ PruneWindowReferences();
64
+ if(windowReferences.Count>=4096){metadata["windowRefUnavailable"]="REFERENCE_CAPACITY: observe again after expiry";return null;}
65
+ string serial=(++referenceSequence).ToString("x"),marker="PiComputerUse.Window."+referenceEpoch+"."+serial;int error;
66
+ if(!WindowMarker.Bind(window,owner,marker,out error)){metadata["windowRefUnavailable"]="WINDOW_REFERENCE_UNAVAILABLE: marker acquisition failed (native error "+error+")";return null;}
67
+ var entry=new WindowReference {Id="w:"+referenceEpoch+":"+serial,Marker=marker,Window=window,Owner=owner,Seen=DateTime.UtcNow};windowReferences.Add(entry.Id,entry);return entry;
68
+ }
69
+ static void AddWindowReference(Dictionary<string,object> metadata,IntPtr window) {
70
+ WindowReference entry=ObserveWindowReference(window,metadata);if(entry!=null)metadata["windowRef"]=entry.Id;
71
+ }
72
+ static Dictionary<string,object> WindowReferenceDescriptor(IntPtr window) {
73
+ WindowReference entry=currentReference!=null && currentReference.Window==window?currentReference:ObserveWindowReference(window,new Dictionary<string,object>());
74
+ return entry==null?null:new Dictionary<string,object>{{"name",entry.Marker},{"windowRef",entry.Id}};
75
+ }
76
+ static void CleanupWindowReferences() {
77
+ foreach(var pair in windowReferences)WindowMarker.Remove(pair.Value.Window,pair.Value.Owner,pair.Value.Marker);
78
+ windowReferences.Clear();observed.Clear();elementKeys.Clear();
79
+ }
80
+ }
@@ -0,0 +1,127 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Runtime.InteropServices;
4
+ using System.Text;
5
+ using System.Web.Script.Serialization;
6
+
7
+ // Focused Win32 bridge; guarded input lives in pointer.cs.
8
+ partial class WindowsBridge {
9
+ static bool physicalInputAnnounced;
10
+ static void NotifyPhysicalInput(bool active) {
11
+ if(physicalInputAnnounced==active)return;
12
+ physicalInputAnnounced=active;
13
+ Console.WriteLine(new JavaScriptSerializer().Serialize(new Dictionary<string,object> {{"event","input_state"},{"active",active},{"operationId",pointerOperation}}));
14
+ Console.Out.Flush();
15
+ }
16
+ delegate bool EnumWindow(IntPtr handle, IntPtr parameter);
17
+ [StructLayout(LayoutKind.Sequential)] struct Rect { public int Left, Top, Right, Bottom; }
18
+ [DllImport("user32.dll")] static extern bool EnumWindows(EnumWindow callback, IntPtr parameter);
19
+ [DllImport("user32.dll")] static extern bool IsWindow(IntPtr handle);
20
+ [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr handle);
21
+ [DllImport("user32.dll")] static extern bool IsWindowEnabled(IntPtr handle);
22
+ [DllImport("user32.dll")] static extern bool IsIconic(IntPtr handle);
23
+ [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
24
+ [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr handle, out Rect rect);
25
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetWindowText(IntPtr handle, StringBuilder text, int count);
26
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetClassName(IntPtr handle, StringBuilder text, int count);
27
+ [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr handle, out uint process);
28
+ [DllImport("user32.dll", SetLastError=true)] static extern IntPtr SendMessageTimeout(IntPtr handle, uint message, UIntPtr wParam, IntPtr lParam, uint flags, uint timeout, out UIntPtr result);
29
+ [DllImport("user32.dll")] static extern bool SetProcessDPIAware();
30
+ [DllImport("user32.dll")] static extern bool SetProcessDpiAwarenessContext(IntPtr context);
31
+
32
+ static Dictionary<string, object> Result(bool ok, string action) {
33
+ return new Dictionary<string, object> { { "ok", ok }, { "action", action } };
34
+ }
35
+
36
+ static object ListWindows(Dictionary<string, object> args) {
37
+ if(args.ContainsKey("windowRef"))return ObserveReferencePresence(args);
38
+ int maximum = Convert.ToInt32(args["maxWindows"]);
39
+ bool invisible = Convert.ToBoolean(args["includeInvisible"]);
40
+ List<object> windows = new List<object>();
41
+ bool truncated=false,identityChanged=false,metadataOnly=Option(args,"_metadataOnly",false);
42
+ EnumWindows(delegate(IntPtr handle,IntPtr parameter) {
43
+ // Eligibility probes are not retained metadata. Re-read retained fields after binding.
44
+ if(!invisible && (!IsWindowVisible(handle) || IsIconic(handle)))return true;
45
+ uint pid;GetWindowThreadProcessId(handle,out pid);
46
+ if(pid==0 || !IsWindow(handle))return true;
47
+ if(windows.Count==maximum){truncated=true;return false;}
48
+ var bindingMetadata=new Dictionary<string,object>();
49
+ WindowReference binding=metadataOnly?null:ObserveWindowReference(handle,bindingMetadata);
50
+ bool visible=IsWindowVisible(handle),offscreen=!visible || IsIconic(handle);
51
+ if(!invisible && offscreen){if(binding!=null && !WindowMarker.Matches(handle,binding.Owner,binding.Marker))identityChanged=true;return true;}
52
+ Rect bounds;if(!GetWindowRect(handle,out bounds)){if(binding!=null)identityChanged=true;return true;}
53
+ var title=new StringBuilder(2048);var className=new StringBuilder(256);
54
+ GetWindowText(handle,title,title.Capacity);GetClassName(handle,className,className.Capacity);
55
+ UIntPtr ignored;bool answered=SendMessageTimeout(handle,0,UIntPtr.Zero,IntPtr.Zero,3,100,out ignored)!=IntPtr.Zero;
56
+ int responseError=answered?0:Marshal.GetLastWin32Error();
57
+ uint ownerAfter;GetWindowThreadProcessId(handle,out ownerAfter);
58
+ if(!IsWindow(handle) || ownerAfter!=pid || (binding!=null && !WindowMarker.Matches(handle,binding.Owner,binding.Marker))){if(binding!=null)identityChanged=true;return true;}
59
+ Dictionary<string,object> item=new Dictionary<string,object>{
60
+ {"depth",1},{"name",title.ToString()},{"className",className.ToString()},
61
+ {"controlType","Window"},{"processId",pid},{"nativeWindowHandle",handle.ToInt64()},
62
+ {"boundingBox",new Dictionary<string,object>{{"x",bounds.Left},{"y",bounds.Top},{"width",bounds.Right-bounds.Left},{"height",bounds.Bottom-bounds.Top},{"centerX",(bounds.Left+(double)bounds.Right)/2},{"centerY",(bounds.Top+(double)bounds.Bottom)/2}}},
63
+ {"isEnabled",IsWindowEnabled(handle)},{"isOffscreen",offscreen},{"hasKeyboardFocus",handle==GetForegroundWindow()},{"source","win32"},
64
+ {"responsive",responseError==5?(object)null:answered},{"responsiveness",answered?"responsive":responseError==5?"access_denied":"unresponsive_or_unknown"}
65
+ };
66
+ if(binding!=null){item["id"]="uia:hwnd:"+handle.ToInt64()+":pid:"+pid;item["windowRef"]=binding.Id;}
67
+ else if(metadataOnly)item["windowRefUnavailable"]="UNBOUND_METADATA: observe the selected window to obtain a reference";
68
+ else foreach(var pair in bindingMetadata)item[pair.Key]=pair.Value;
69
+ RememberPointerOrigin(handle);
70
+ if(binding!=null && !WindowMarker.Matches(handle,binding.Owner,binding.Marker)){identityChanged=true;return true;}
71
+ windows.Add(item);return true;
72
+ },IntPtr.Zero);
73
+ Dictionary<string, object> result = Result(true, "list_windows");
74
+ result["windows"] = windows;
75
+ result["truncated"] = truncated || identityChanged;
76
+ if(identityChanged){result["conclusive"]=false;result["observationError"]="WINDOW_REFERENCE_LOST: a lifetime changed during enumeration; incomplete observation";}
77
+ result["source"] = "win32";
78
+ return result;
79
+ }
80
+
81
+ [MTAThread]
82
+ static void Main() {
83
+ Console.InputEncoding = new UTF8Encoding(false);
84
+ Console.OutputEncoding = new UTF8Encoding(false);
85
+ try { if (!SetProcessDpiAwarenessContext(new IntPtr(-4))) SetProcessDPIAware(); }
86
+ catch (EntryPointNotFoundException) { SetProcessDPIAware(); }
87
+ JavaScriptSerializer json = new JavaScriptSerializer { MaxJsonLength = 32 * 1024 * 1024 };
88
+ string line;
89
+ while ((line = Console.ReadLine()) != null) {
90
+ Dictionary<string, object> request = null;
91
+ try {
92
+ request = json.Deserialize<Dictionary<string, object>>(line);
93
+ if (request.ContainsKey("cancel")) continue;
94
+ string action = Convert.ToString(request["action"]);
95
+ currentReference=null;windowDispatchAttempted=false;
96
+ pointerInputAction=action=="click" || action=="double_click" || action=="move" || action=="drag" || action=="scroll" || action=="type_text" || action=="keypress";
97
+ pointerOperation=request.ContainsKey("operationId")?Convert.ToString(request["operationId"]):"";
98
+ object result;
99
+ try {
100
+ Dictionary<string, object> args = (Dictionary<string, object>)request["args"];
101
+ if (action == "type_text" || action == "keypress") result = ExecuteText(action,args);
102
+ else if (action == "click" || action == "double_click" || action == "move" || action == "drag" || (action == "scroll" && (!args.ContainsKey("elementId") || (args.ContainsKey("_cancelEvent") && !Option(args,"_quietScroll",false))))) result = ExecutePointer(action,args);
103
+ else if (action == "list_windows") result = ListWindows(args);
104
+ else if (action == "browser_frame") result = MarkBrowserFrame(args);
105
+ else if (action == "focus" || action == "invoke" || action == "set_value" || action == "scroll") result = ExecuteControl(action,args);
106
+ else if (action == "launch_app" || action == "activate_window" || action == "move_window" || action == "close_window") result = ManageNativeWindow(action,args);
107
+ else if (action == "screenshot") result = CaptureScreenshot(args);
108
+ else if (action == "snapshot" || action == "accessibility_tree" || action == "find" || action == "element_info") result = ObserveAccessibility(action, args);
109
+ else throw new InvalidOperationException("UNKNOWN_ACTION");
110
+ } catch (Exception error) {
111
+ Dictionary<string, object> failure = Result(false, action);
112
+ failure["error"] = "WINDOWS_ERROR: " + error.Message;
113
+ ActivationRefusedException refused=error as ActivationRefusedException;
114
+ if(refused!=null)failure["activationDiagnostic"]=refused.Diagnostic;
115
+ result = failure;
116
+ }
117
+ var payload=result as Dictionary<string,object>;
118
+ if(payload!=null)FinishWindowReference(payload);
119
+ Console.WriteLine(json.Serialize(new Dictionary<string, object> { { "id", request["id"] }, { "result", result } }));
120
+ Console.Out.Flush();
121
+ } catch (Exception error) {
122
+ Console.Error.WriteLine("NATIVE_PROTOCOL_ERROR: " + error.Message);
123
+ }
124
+ }
125
+ CleanupWindowReferences();
126
+ }
127
+ }
@@ -0,0 +1,29 @@
1
+ import type { WorkerResult } from "./protocol.js";
2
+ type Property = {
3
+ type: "string" | "integer" | "boolean";
4
+ default?: unknown;
5
+ enum?: readonly string[];
6
+ minimum?: number;
7
+ maximum?: number;
8
+ minLength?: number;
9
+ };
10
+ export declare const observationTools: {
11
+ action: string;
12
+ parameters: {
13
+ properties: Record<string, Property>;
14
+ required?: string[];
15
+ type: string;
16
+ additionalProperties: boolean;
17
+ };
18
+ }[];
19
+ export declare const controlTools: {
20
+ action: string;
21
+ parameters: {
22
+ properties: Record<string, Property>;
23
+ required?: string[];
24
+ type: string;
25
+ additionalProperties: boolean;
26
+ };
27
+ }[];
28
+ export declare function executeAccessibility(action: string, args: Record<string, unknown>, signal?: AbortSignal, execute?: (normalized: Record<string, unknown>) => Promise<WorkerResult>): Promise<WorkerResult>;
29
+ export {};