@aexol/spectral 0.9.212 → 0.9.215

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 (110) hide show
  1. package/dist/backend/generated-images.d.ts +90 -0
  2. package/dist/backend/generated-images.d.ts.map +1 -0
  3. package/dist/backend/generated-images.js +214 -0
  4. package/dist/backend/machine-routing.d.ts +25 -0
  5. package/dist/backend/machine-routing.d.ts.map +1 -0
  6. package/dist/backend/machine-routing.js +35 -0
  7. package/dist/commands/serve.d.ts.map +1 -1
  8. package/dist/commands/serve.js +20 -2
  9. package/dist/extensions/browser/browser-service.d.ts +75 -1
  10. package/dist/extensions/browser/browser-service.d.ts.map +1 -1
  11. package/dist/extensions/browser/browser-service.js +600 -34
  12. package/dist/extensions/browser/index.d.ts +10 -4
  13. package/dist/extensions/browser/index.d.ts.map +1 -1
  14. package/dist/extensions/browser/index.js +44 -5
  15. package/dist/extensions/browser/stream/stream-tool.d.ts +10 -0
  16. package/dist/extensions/browser/stream/stream-tool.d.ts.map +1 -1
  17. package/dist/extensions/browser/stream/stream-tool.js +42 -14
  18. package/dist/extensions/browser/tools/core.d.ts.map +1 -1
  19. package/dist/extensions/browser/tools/core.js +63 -2
  20. package/dist/extensions/desktop-control/index.d.ts +55 -0
  21. package/dist/extensions/desktop-control/index.d.ts.map +1 -0
  22. package/dist/extensions/desktop-control/index.js +633 -0
  23. package/dist/extensions/desktop-control/keys.d.ts +33 -0
  24. package/dist/extensions/desktop-control/keys.d.ts.map +1 -0
  25. package/dist/extensions/desktop-control/keys.js +170 -0
  26. package/dist/extensions/desktop-control/linux.d.ts +26 -0
  27. package/dist/extensions/desktop-control/linux.d.ts.map +1 -0
  28. package/dist/extensions/desktop-control/linux.js +195 -0
  29. package/dist/extensions/desktop-control/macos.d.ts +53 -0
  30. package/dist/extensions/desktop-control/macos.d.ts.map +1 -0
  31. package/dist/extensions/desktop-control/macos.js +382 -0
  32. package/dist/extensions/desktop-control/safety.d.ts +89 -0
  33. package/dist/extensions/desktop-control/safety.d.ts.map +1 -0
  34. package/dist/extensions/desktop-control/safety.js +157 -0
  35. package/dist/extensions/desktop-control/types.d.ts +79 -0
  36. package/dist/extensions/desktop-control/types.d.ts.map +1 -0
  37. package/dist/extensions/desktop-control/types.js +10 -0
  38. package/dist/extensions/desktop-control/windows.d.ts +31 -0
  39. package/dist/extensions/desktop-control/windows.d.ts.map +1 -0
  40. package/dist/extensions/desktop-control/windows.js +276 -0
  41. package/dist/extensions/desktop-screenshot/index.d.ts +103 -0
  42. package/dist/extensions/desktop-screenshot/index.d.ts.map +1 -1
  43. package/dist/extensions/desktop-screenshot/index.js +363 -17
  44. package/dist/extensions/image-generation/index.d.ts.map +1 -1
  45. package/dist/extensions/image-generation/index.js +133 -34
  46. package/dist/extensions/seo/index.d.ts.map +1 -1
  47. package/dist/extensions/seo/index.js +4 -0
  48. package/dist/extensions/seo/tools/backend-bridge.d.ts +4 -0
  49. package/dist/extensions/seo/tools/backend-bridge.d.ts.map +1 -0
  50. package/dist/extensions/seo/tools/backend-bridge.js +378 -0
  51. package/dist/extensions/seo/tools/full-audit.d.ts +2 -0
  52. package/dist/extensions/seo/tools/full-audit.d.ts.map +1 -1
  53. package/dist/extensions/seo/tools/full-audit.js +1 -1
  54. package/dist/extensions/seo/tools/report.d.ts +10 -0
  55. package/dist/extensions/seo/tools/report.d.ts.map +1 -1
  56. package/dist/extensions/seo/tools/report.js +1 -1
  57. package/dist/extensions/spectral-vision-fallback.d.ts.map +1 -1
  58. package/dist/extensions/spectral-vision-fallback.js +11 -1
  59. package/dist/generated/zeus/const.d.ts.map +1 -1
  60. package/dist/generated/zeus/const.js +121 -4
  61. package/dist/generated/zeus/index.d.ts +527 -0
  62. package/dist/generated/zeus/index.d.ts.map +1 -1
  63. package/dist/mcp/tool-registrar.d.ts.map +1 -1
  64. package/dist/mcp/tool-registrar.js +5 -2
  65. package/dist/mcp/types.d.ts +5 -0
  66. package/dist/mcp/types.d.ts.map +1 -1
  67. package/dist/relay/dispatcher.d.ts +12 -1
  68. package/dist/relay/dispatcher.d.ts.map +1 -1
  69. package/dist/relay/dispatcher.js +44 -42
  70. package/dist/sdk/ai/providers/faux.js +1 -1
  71. package/dist/sdk/ai/providers/openai-completions.d.ts.map +1 -1
  72. package/dist/sdk/ai/providers/openai-completions.js +57 -16
  73. package/dist/sdk/ai/providers/openrouter-images.d.ts.map +1 -1
  74. package/dist/sdk/ai/providers/openrouter-images.js +45 -9
  75. package/dist/sdk/ai/providers/simple-options.d.ts.map +1 -1
  76. package/dist/sdk/ai/providers/simple-options.js +2 -0
  77. package/dist/sdk/ai/types.d.ts +37 -1
  78. package/dist/sdk/ai/types.d.ts.map +1 -1
  79. package/dist/sdk/ai/utils/image-source.d.ts +121 -0
  80. package/dist/sdk/ai/utils/image-source.d.ts.map +1 -0
  81. package/dist/sdk/ai/utils/image-source.js +410 -0
  82. package/dist/sdk/coding-agent/core/extensions/native-extensions.d.ts.map +1 -1
  83. package/dist/sdk/coding-agent/core/extensions/native-extensions.js +14 -0
  84. package/dist/sdk/coding-agent/core/extensions/types.d.ts +9 -0
  85. package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
  86. package/dist/sdk/coding-agent/core/system-prompt.d.ts.map +1 -1
  87. package/dist/sdk/coding-agent/core/system-prompt.js +15 -9
  88. package/dist/sdk/coding-agent/modes/rpc/rpc-mode.d.ts.map +1 -1
  89. package/dist/sdk/coding-agent/modes/rpc/rpc-mode.js +16 -3
  90. package/dist/sdk/coding-agent/utils/image-resize.d.ts.map +1 -1
  91. package/dist/sdk/coding-agent/utils/image-resize.js +7 -3
  92. package/dist/server/agent-bridge.d.ts +14 -0
  93. package/dist/server/agent-bridge.d.ts.map +1 -1
  94. package/dist/server/agent-bridge.js +105 -26
  95. package/dist/server/handlers/queue.d.ts.map +1 -1
  96. package/dist/server/handlers/queue.js +11 -34
  97. package/dist/server/image-attachments.d.ts +109 -0
  98. package/dist/server/image-attachments.d.ts.map +1 -0
  99. package/dist/server/image-attachments.js +195 -0
  100. package/dist/server/session-stream.d.ts +67 -0
  101. package/dist/server/session-stream.d.ts.map +1 -1
  102. package/dist/server/session-stream.js +222 -23
  103. package/dist/server/storage.d.ts.map +1 -1
  104. package/dist/server/storage.js +10 -31
  105. package/dist/server/ui/terminal-confirm.d.ts +145 -0
  106. package/dist/server/ui/terminal-confirm.d.ts.map +1 -0
  107. package/dist/server/ui/terminal-confirm.js +333 -0
  108. package/dist/server/wire.d.ts +30 -5
  109. package/dist/server/wire.d.ts.map +1 -1
  110. package/package.json +1 -1
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared types for the desktop-control (computer-use) extension.
3
+ *
4
+ * The extension injects real mouse/keyboard input into the user's desktop.
5
+ * Every mutating action is wrapped by a safety layer in index.ts:
6
+ * screenshot BEFORE -> execute -> screenshot AFTER, with an interactive
7
+ * confirmation gate, headless deny-by-default, dry-run mode and an optional
8
+ * app allowlist.
9
+ */
10
+ export type MouseButton = "left" | "right" | "middle";
11
+ /** Kinds of native actions an adapter can support. `hotkey` covers both desktop_press_key and desktop_hotkey. */
12
+ export type ActionKind = "mouse_move" | "mouse_click" | "mouse_drag" | "scroll" | "type_text" | "hotkey" | "focus_window";
13
+ /**
14
+ * Normalized action description, platform-agnostic.
15
+ * Scroll semantics: dy > 0 scrolls DOWN, dx > 0 scrolls RIGHT (units: lines).
16
+ */
17
+ export type DesktopAction = {
18
+ kind: "mouse_move";
19
+ x: number;
20
+ y: number;
21
+ } | {
22
+ kind: "mouse_click";
23
+ x: number;
24
+ y: number;
25
+ button: MouseButton;
26
+ clickCount: number;
27
+ } | {
28
+ kind: "mouse_drag";
29
+ fromX: number;
30
+ fromY: number;
31
+ toX: number;
32
+ toY: number;
33
+ } | {
34
+ kind: "scroll";
35
+ x?: number;
36
+ y?: number;
37
+ dx: number;
38
+ dy: number;
39
+ } | {
40
+ kind: "type_text";
41
+ text: string;
42
+ } | {
43
+ kind: "hotkey";
44
+ tokens: string[];
45
+ } | {
46
+ kind: "focus_window";
47
+ title: string;
48
+ owner?: string;
49
+ windowId?: string | number;
50
+ };
51
+ /** Result of a permission/capability probe. */
52
+ export interface PermissionCheck {
53
+ ok: boolean;
54
+ error?: string;
55
+ }
56
+ /**
57
+ * Per-OS backend. Adapters only build and run native commands; all safety
58
+ * policy lives in safety.ts / index.ts. Builders are exported separately so
59
+ * unit tests can assert generated osascript / .ps1 / xdotool argv without
60
+ * executing anything.
61
+ */
62
+ export interface DesktopAdapter {
63
+ platform: string;
64
+ capabilities: ActionKind[];
65
+ /** Optional TCC/permission probe (macOS accessibility, etc.). */
66
+ checkPermissions?(): Promise<PermissionCheck>;
67
+ /** Name (or "proc title") of the frontmost app, or null if unavailable. */
68
+ frontmostApp(): Promise<string | null>;
69
+ /** Human-readable command that WOULD run (used by dry-run plans). */
70
+ buildPlan(action: DesktopAction): string;
71
+ /**
72
+ * Execute the action natively. May return a short read-back string with the
73
+ * post-action state (macOS: "x,y" cursor position from CGEventGetLocation)
74
+ * so the safety layer can surface it and detect silent injection failures;
75
+ * null/undefined when the adapter has nothing to report.
76
+ */
77
+ execute(action: DesktopAction): Promise<string | null | void>;
78
+ }
79
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/extensions/desktop-control/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD,iHAAiH;AACjH,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAExF,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,iEAAiE;IACjE,gBAAgB,CAAC,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9C,2EAA2E;IAC3E,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,qEAAqE;IACrE,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IACzC;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;CAC/D"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Shared types for the desktop-control (computer-use) extension.
3
+ *
4
+ * The extension injects real mouse/keyboard input into the user's desktop.
5
+ * Every mutating action is wrapped by a safety layer in index.ts:
6
+ * screenshot BEFORE -> execute -> screenshot AFTER, with an interactive
7
+ * confirmation gate, headless deny-by-default, dry-run mode and an optional
8
+ * app allowlist.
9
+ */
10
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Windows adapter for desktop-control.
3
+ *
4
+ * Input injection via PowerShell scripts (written to temp .ps1 files and run
5
+ * with cross-spawn, same pattern as the desktop-screenshot extension) that
6
+ * Add-Type a small user32 P/Invoke class:
7
+ * - SetCursorPos for absolute cursor placement
8
+ * - SendInput for clicks (MOUSEEVENTF_*), scrolling (WHEEL/HWHEEL) and keys
9
+ * (VK codes; type_text uses KEYEVENTF_UNICODE so layout/IME is irrelevant)
10
+ * - SetForegroundWindow/ShowWindow/IsIconic for window focus
11
+ *
12
+ * NOTE: each invocation pays a one-time Add-Type JIT (~1-2s). UIPI means a
13
+ * non-elevated spectral cannot inject into elevated (admin) windows.
14
+ */
15
+ import type { DesktopAction, DesktopAdapter } from "./types.js";
16
+ /** argv for running a temp .ps1 (mirrors desktop-screenshot's runPowerShell). */
17
+ export declare function buildPowerShellArgs(psPath: string): string[];
18
+ export declare function buildMousePS(action: Extract<DesktopAction, {
19
+ kind: "mouse_move" | "mouse_click" | "mouse_drag" | "scroll";
20
+ }>): string;
21
+ export declare function resolveChordTokens(tokens: string[]): {
22
+ key: number;
23
+ modifiers: number[];
24
+ };
25
+ export declare function buildHotkeyPS(key: number, modifiers: number[]): string;
26
+ /** One SendUnicode call per char (KEYEVENTF_UNICODE down+up); \n -> CR (Enter). */
27
+ export declare function buildTypeTextPS(text: string): string;
28
+ export declare function buildFocusPS(hwnd: string | number): string;
29
+ export declare function buildFrontmostPS(): string;
30
+ export declare const win32Adapter: DesktopAdapter;
31
+ //# sourceMappingURL=windows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../../src/extensions/desktop-control/windows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwDhE,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5D;AAUD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,QAAQ,CAAA;CAAE,CAAC,GAC/F,MAAM,CA2CR;AAYD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAOzF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAStE;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASpD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAS1D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAUzC;AAgCD,eAAO,MAAM,YAAY,EAAE,cAyD1B,CAAC"}
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Windows adapter for desktop-control.
3
+ *
4
+ * Input injection via PowerShell scripts (written to temp .ps1 files and run
5
+ * with cross-spawn, same pattern as the desktop-screenshot extension) that
6
+ * Add-Type a small user32 P/Invoke class:
7
+ * - SetCursorPos for absolute cursor placement
8
+ * - SendInput for clicks (MOUSEEVENTF_*), scrolling (WHEEL/HWHEEL) and keys
9
+ * (VK codes; type_text uses KEYEVENTF_UNICODE so layout/IME is irrelevant)
10
+ * - SetForegroundWindow/ShowWindow/IsIconic for window focus
11
+ *
12
+ * NOTE: each invocation pays a one-time Add-Type JIT (~1-2s). UIPI means a
13
+ * non-elevated spectral cannot inject into elevated (admin) windows.
14
+ */
15
+ import { unlinkSync, writeFileSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { randomUUID } from "node:crypto";
19
+ import crossSpawn from "cross-spawn";
20
+ import { WINDOWS_MODIFIER_VK, WINDOWS_VK, isModifierToken } from "./keys.js";
21
+ const TIMEOUT_MS = 15_000;
22
+ const WHEEL_DELTA = 120; // per line
23
+ const CSHARP_PRELUDE = `using System;
24
+ using System.Runtime.InteropServices;
25
+ public class Win32Input {
26
+ [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
27
+ [DllImport("user32.dll", SetLastError = true)] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
28
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
29
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
30
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
31
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
32
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
33
+ [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);
34
+ [StructLayout(LayoutKind.Sequential)] public struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
35
+ [StructLayout(LayoutKind.Sequential)] public struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
36
+ [StructLayout(LayoutKind.Explicit)] public struct INPUTUNION { [FieldOffset(0)] public MOUSEINPUT mi; [FieldOffset(0)] public KEYBDINPUT ki; }
37
+ [StructLayout(LayoutKind.Sequential)] public struct INPUT { public uint type; public INPUTUNION u; }
38
+ public const uint INPUT_MOUSE = 0, INPUT_KEYBOARD = 1;
39
+ public const uint KEYEVENTF_KEYUP = 0x0002, KEYEVENTF_UNICODE = 0x0004;
40
+ public const uint LEFT_DOWN = 0x0002, LEFT_UP = 0x0004, RIGHT_DOWN = 0x0008, RIGHT_UP = 0x0010, MIDDLE_DOWN = 0x0020, MIDDLE_UP = 0x0040, WHEEL = 0x0800, HWHEEL = 0x1000;
41
+ public static void SendMouse(uint flags, int dx, int dy, int data) {
42
+ var input = new INPUT(); input.type = INPUT_MOUSE;
43
+ input.u.mi.dx = dx; input.u.mi.dy = dy; input.u.mi.mouseData = unchecked((uint)data);
44
+ input.u.mi.dwFlags = flags; input.u.mi.time = 0; input.u.mi.dwExtraInfo = IntPtr.Zero;
45
+ var arr = new INPUT[1]; arr[0] = input;
46
+ SendInput(1, arr, Marshal.SizeOf(typeof(INPUT)));
47
+ }
48
+ public static void SendKey(ushort vk, bool isDown) {
49
+ var input = new INPUT(); input.type = INPUT_KEYBOARD;
50
+ input.u.ki.wVk = vk; input.u.ki.wScan = 0; input.u.ki.dwFlags = isDown ? 0u : KEYEVENTF_KEYUP;
51
+ input.u.ki.time = 0; input.u.ki.dwExtraInfo = IntPtr.Zero;
52
+ var arr = new INPUT[1]; arr[0] = input;
53
+ SendInput(1, arr, Marshal.SizeOf(typeof(INPUT)));
54
+ }
55
+ public static void SendUnicode(int code) {
56
+ var down = new INPUT(); down.type = INPUT_KEYBOARD;
57
+ down.u.ki.wVk = 0; down.u.ki.wScan = (ushort)code; down.u.ki.dwFlags = KEYEVENTF_UNICODE;
58
+ down.u.ki.time = 0; down.u.ki.dwExtraInfo = IntPtr.Zero;
59
+ var up = new INPUT(); up.type = INPUT_KEYBOARD;
60
+ up.u.ki.wVk = 0; up.u.ki.wScan = (ushort)code; up.u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
61
+ up.u.ki.time = 0; up.u.ki.dwExtraInfo = IntPtr.Zero;
62
+ var arr = new INPUT[2]; arr[0] = down; arr[1] = up;
63
+ SendInput(2, arr, Marshal.SizeOf(typeof(INPUT)));
64
+ }
65
+ }`;
66
+ const PS_PRELUDE = ['Add-Type -AssemblyName System.Windows.Forms', 'Add-Type @"', CSHARP_PRELUDE, '"@'];
67
+ // ---------------------------------------------------------------------------
68
+ // Builders (exported for unit tests — pure, never executed by tests)
69
+ // ---------------------------------------------------------------------------
70
+ /** argv for running a temp .ps1 (mirrors desktop-screenshot's runPowerShell). */
71
+ export function buildPowerShellArgs(psPath) {
72
+ return ["-NoProfile", "-Sta", "-ExecutionPolicy", "Bypass", "-File", psPath];
73
+ }
74
+ const BTN_FLAGS = {
75
+ left: { down: "LEFT_DOWN", up: "LEFT_UP" },
76
+ right: { down: "RIGHT_DOWN", up: "RIGHT_UP" },
77
+ middle: { down: "MIDDLE_DOWN", up: "MIDDLE_UP" },
78
+ };
79
+ const DRAG_STEPS = 10;
80
+ export function buildMousePS(action) {
81
+ const lines = [];
82
+ switch (action.kind) {
83
+ case "mouse_move":
84
+ lines.push(`[Win32Input]::SetCursorPos(${action.x}, ${action.y})`);
85
+ break;
86
+ case "mouse_click": {
87
+ const b = BTN_FLAGS[action.button];
88
+ lines.push(`[Win32Input]::SetCursorPos(${action.x}, ${action.y})`);
89
+ lines.push("Start-Sleep -Milliseconds 80");
90
+ for (let i = 0; i < action.clickCount; i++) {
91
+ if (i > 0)
92
+ lines.push("Start-Sleep -Milliseconds 50");
93
+ lines.push(`[Win32Input]::SendMouse([Win32Input]::${b.down}, 0, 0, 0)`);
94
+ lines.push(`[Win32Input]::SendMouse([Win32Input]::${b.up}, 0, 0, 0)`);
95
+ }
96
+ break;
97
+ }
98
+ case "mouse_drag":
99
+ lines.push(`[Win32Input]::SetCursorPos(${action.fromX}, ${action.fromY})`);
100
+ lines.push("Start-Sleep -Milliseconds 80");
101
+ lines.push("[Win32Input]::SendMouse([Win32Input]::LEFT_DOWN, 0, 0, 0)");
102
+ for (let i = 1; i <= DRAG_STEPS; i++) {
103
+ const t = i / DRAG_STEPS;
104
+ const x = Math.round(action.fromX + (action.toX - action.fromX) * t);
105
+ const y = Math.round(action.fromY + (action.toY - action.fromY) * t);
106
+ lines.push(`[Win32Input]::SetCursorPos(${x}, ${y})`);
107
+ lines.push("Start-Sleep -Milliseconds 15");
108
+ }
109
+ lines.push("[Win32Input]::SendMouse([Win32Input]::LEFT_UP, 0, 0, 0)");
110
+ break;
111
+ case "scroll": {
112
+ if (action.x !== undefined) {
113
+ lines.push(`[Win32Input]::SetCursorPos(${action.x}, ${action.y})`);
114
+ lines.push("Start-Sleep -Milliseconds 50");
115
+ }
116
+ // Windows wheel delta: positive = up, so invert dy. dx positive = right.
117
+ if (action.dy !== 0)
118
+ lines.push(`[Win32Input]::SendMouse([Win32Input]::WHEEL, 0, 0, ${-action.dy * WHEEL_DELTA})`);
119
+ if (action.dx !== 0)
120
+ lines.push(`[Win32Input]::SendMouse([Win32Input]::HWHEEL, 0, 0, ${action.dx * WHEEL_DELTA})`);
121
+ break;
122
+ }
123
+ }
124
+ lines.push("Write-Output 'OK'");
125
+ return [...PS_PRELUDE, ...lines].join("\r\n");
126
+ }
127
+ function resolveVk(name) {
128
+ const vk = WINDOWS_VK[name];
129
+ if (vk === undefined) {
130
+ throw new Error(`unsupported key "${name}" for Windows VK injection (supported: letters, digits, f1-f12, return/tab/space/escape/backspace/delete, arrows, home/end/pageup/pagedown, punctuation)`);
131
+ }
132
+ return vk;
133
+ }
134
+ export function resolveChordTokens(tokens) {
135
+ const modifiers = [];
136
+ for (const t of tokens.slice(0, -1)) {
137
+ if (!isModifierToken(t))
138
+ throw new Error(`unsupported hotkey token "${t}" (expected modifier)`);
139
+ modifiers.push(WINDOWS_MODIFIER_VK[t]);
140
+ }
141
+ return { key: resolveVk(tokens[tokens.length - 1]), modifiers };
142
+ }
143
+ export function buildHotkeyPS(key, modifiers) {
144
+ const lines = [];
145
+ for (const m of modifiers)
146
+ lines.push(`[Win32Input]::SendKey(0x${m.toString(16)}, $true)`);
147
+ lines.push(`[Win32Input]::SendKey(0x${key.toString(16)}, $true)`);
148
+ lines.push("Start-Sleep -Milliseconds 30");
149
+ lines.push(`[Win32Input]::SendKey(0x${key.toString(16)}, $false)`);
150
+ for (const m of [...modifiers].reverse())
151
+ lines.push(`[Win32Input]::SendKey(0x${m.toString(16)}, $false)`);
152
+ lines.push("Write-Output 'OK'");
153
+ return [...PS_PRELUDE, ...lines].join("\r\n");
154
+ }
155
+ /** One SendUnicode call per char (KEYEVENTF_UNICODE down+up); \n -> CR (Enter). */
156
+ export function buildTypeTextPS(text) {
157
+ const lines = [];
158
+ for (const ch of text.replace(/\r?\n/g, "\r")) {
159
+ const code = ch.codePointAt(0);
160
+ if (code === undefined)
161
+ continue;
162
+ lines.push(`[Win32Input]::SendUnicode(0x${code.toString(16)})`);
163
+ }
164
+ lines.push("Write-Output 'OK'");
165
+ return [...PS_PRELUDE, ...lines].join("\r\n");
166
+ }
167
+ export function buildFocusPS(hwnd) {
168
+ const hwndHex = Number(hwnd).toString(16);
169
+ return [
170
+ ...PS_PRELUDE,
171
+ `$hwnd = [IntPtr]::new(0x${hwndHex})`,
172
+ "if ([Win32Input]::IsIconic($hwnd)) { [Win32Input]::ShowWindow($hwnd, 9) | Out-Null }", // SW_RESTORE
173
+ "[Win32Input]::SetForegroundWindow($hwnd) | Out-Null",
174
+ "Write-Output 'OK'",
175
+ ].join("\r\n");
176
+ }
177
+ export function buildFrontmostPS() {
178
+ return [
179
+ ...PS_PRELUDE,
180
+ "$fg = [Win32Input]::GetForegroundWindow()",
181
+ "$sb = New-Object System.Text.StringBuilder(512)",
182
+ "[Win32Input]::GetWindowText($fg, $sb, 512) | Out-Null",
183
+ "$procId = 0",
184
+ "[Win32Input]::GetWindowThreadProcessId($fg, [ref]$procId) | Out-Null",
185
+ "try { $p = Get-Process -Id $procId -ErrorAction Stop; Write-Output ($p.ProcessName + ' ' + $sb.ToString()) } catch { Write-Output $sb.ToString() }",
186
+ ].join("\r\n");
187
+ }
188
+ // ---------------------------------------------------------------------------
189
+ // Runner + adapter
190
+ // ---------------------------------------------------------------------------
191
+ async function runPowerShellScript(script) {
192
+ const psPath = join(tmpdir(), `desktop-control-${randomUUID()}.ps1`);
193
+ // 0600: temp scripts may contain typed text; always unlink after execution.
194
+ writeFileSync(psPath, script, { encoding: "utf-8", mode: 0o600 });
195
+ try {
196
+ return await new Promise((resolve, reject) => {
197
+ const child = crossSpawn("powershell.exe", buildPowerShellArgs(psPath), {
198
+ timeout: TIMEOUT_MS,
199
+ windowsHide: true,
200
+ });
201
+ let stdout = "";
202
+ let stderr = "";
203
+ child.stdout?.on("data", (chunk) => { stdout += chunk.toString(); });
204
+ child.stderr?.on("data", (chunk) => { stderr += chunk.toString(); });
205
+ child.on("error", reject);
206
+ child.on("close", (code) => {
207
+ if (code === 0)
208
+ return resolve(stdout);
209
+ const tail = stderr.trim().split("\n").pop() || `exited with code ${code}`;
210
+ reject(new Error(`powershell.exe ${tail}`));
211
+ });
212
+ });
213
+ }
214
+ finally {
215
+ try {
216
+ unlinkSync(psPath);
217
+ }
218
+ catch { /* best effort — already gone */ }
219
+ }
220
+ }
221
+ export const win32Adapter = {
222
+ platform: "win32",
223
+ capabilities: ["mouse_move", "mouse_click", "mouse_drag", "scroll", "type_text", "hotkey", "focus_window"],
224
+ async frontmostApp() {
225
+ try {
226
+ const out = (await runPowerShellScript(buildFrontmostPS())).trim();
227
+ return out.length > 0 ? out : null;
228
+ }
229
+ catch {
230
+ return null;
231
+ }
232
+ },
233
+ buildPlan(action) {
234
+ switch (action.kind) {
235
+ case "mouse_move":
236
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SetCursorPos(${action.x}, ${action.y})`;
237
+ case "mouse_click":
238
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SetCursorPos + SendInput ${action.button} x${action.clickCount} @(${action.x},${action.y})`;
239
+ case "mouse_drag":
240
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # LEFT_DOWN@(${action.fromX},${action.fromY}) + ${DRAG_STEPS} moves -> LEFT_UP@(${action.toX},${action.toY})`;
241
+ case "scroll":
242
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SendInput WHEEL dy=${-action.dy * WHEEL_DELTA} HWHEEL dx=${action.dx * WHEEL_DELTA}`;
243
+ case "type_text":
244
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SendInput KEYEVENTF_UNICODE x${action.text.length}`;
245
+ case "hotkey":
246
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SendInput VK ${action.tokens.join("+")}`;
247
+ case "focus_window":
248
+ return `powershell.exe -NoProfile -Sta -File <tempfile> # SetForegroundWindow(hwnd for "${action.title}")`;
249
+ }
250
+ },
251
+ async execute(action) {
252
+ switch (action.kind) {
253
+ case "mouse_move":
254
+ case "mouse_click":
255
+ case "mouse_drag":
256
+ case "scroll":
257
+ await runPowerShellScript(buildMousePS(action));
258
+ return;
259
+ case "type_text":
260
+ await runPowerShellScript(buildTypeTextPS(action.text));
261
+ return;
262
+ case "hotkey": {
263
+ const { key, modifiers } = resolveChordTokens(action.tokens);
264
+ await runPowerShellScript(buildHotkeyPS(key, modifiers));
265
+ return;
266
+ }
267
+ case "focus_window": {
268
+ if (action.windowId === undefined) {
269
+ throw new Error("focus_window requires a resolved window id (use list_windows, then retry with an exact title)");
270
+ }
271
+ await runPowerShellScript(buildFocusPS(action.windowId));
272
+ return;
273
+ }
274
+ }
275
+ },
276
+ };
@@ -13,5 +13,108 @@
13
13
  * extension automatically analyzes with a vision model.
14
14
  */
15
15
  import type { ExtensionAPI } from "../../sdk/coding-agent/index.js";
16
+ import type { ImageContent } from "../../sdk/ai/index.js";
17
+ export type ScreenshotFormat = "png" | "jpg" | "jpeg";
18
+ export interface ScreenshotOptions {
19
+ screen?: string | number;
20
+ format?: ScreenshotFormat;
21
+ filename?: string;
22
+ }
23
+ export interface WindowInfo {
24
+ id: number | string;
25
+ owner: string;
26
+ title: string;
27
+ }
28
+ export declare function tmpDir(): string;
29
+ export declare function saveScreenshot(buf: Buffer, format: ScreenshotFormat, cwd: string): string;
30
+ interface DarwinScreenRect {
31
+ x: number;
32
+ y: number;
33
+ width: number;
34
+ height: number;
35
+ }
36
+ export interface DarwinScreenGeometry {
37
+ displayCount: number;
38
+ /** Primary display top-left origin in the global CGEvent (points) space. */
39
+ primaryOrigin: {
40
+ x: number;
41
+ y: number;
42
+ };
43
+ /** Main screen (keyboard focus): logical size + top-left global origin, in points. */
44
+ main: DarwinScreenRect;
45
+ backingScaleFactor: number;
46
+ }
47
+ /** Test hook: forget the cached geometry so the next probe hits osascript again. */
48
+ export declare function resetDarwinScreenGeometryCache(): void;
49
+ /**
50
+ * Logical display geometry via JXA/AppKit, cached for a few seconds (display
51
+ * arrangements rarely change mid-session). Resolves to `null` when the probe
52
+ * fails — captures are then left untouched and the tool result says the
53
+ * scale is unverified. Exported for tests.
54
+ */
55
+ export declare function darwinScreenGeometry(): Promise<DarwinScreenGeometry | null>;
56
+ export type DarwinScreenshotMode = "screen" | "window";
57
+ export interface DarwinNormalizedScreenshot {
58
+ buffer: Buffer;
59
+ /** Coordinate-space metadata appended to the tool result text ("" when nothing to say). */
60
+ metadata: string;
61
+ /** Structured fields mirrored into the tool result `details` (additive). */
62
+ details: {
63
+ imageWidthPx?: number;
64
+ imageHeightPx?: number;
65
+ logicalWidthPt?: number;
66
+ logicalHeightPt?: number;
67
+ displayCount?: number;
68
+ primaryOriginPt?: {
69
+ x: number;
70
+ y: number;
71
+ };
72
+ scaleApplied?: number;
73
+ resampled?: boolean;
74
+ };
75
+ }
76
+ /**
77
+ * Normalize a macOS capture so image pixels map 1:1 onto the logical screen
78
+ * points the desktop_* tools accept. Full-screen captures are resampled to
79
+ * the main screen's logical width when the file is a Retina (backing-scale)
80
+ * capture; window captures are resampled by the display's backing scale
81
+ * factor (their coordinates are window-local, which the metadata states).
82
+ * Never throws: on any probe failure the original buffer comes back with a
83
+ * caution note instead of a silently wrong scale. Exported for tests.
84
+ */
85
+ export declare function normalizeDarwinScreenshot(buf: Buffer, format: ScreenshotFormat, mode: DarwinScreenshotMode): Promise<DarwinNormalizedScreenshot>;
86
+ /**
87
+ * Run a PowerShell script file and return its stdout as a string.
88
+ *
89
+ * Uses `cross-spawn` instead of Node's `execFile` because, on Windows,
90
+ * `execFile` does not resolve commands through PATHEXT and requires a fully
91
+ * populated PATH. Inside Electron/desktop sandboxes the PATH is often trimmed,
92
+ * which leads to `spawn powershell.exe ENOENT`. `cross-spawn` resolves the
93
+ * executable via PATHEXT/PATH like a shell would.
94
+ *
95
+ * `-Sta` is passed so the single-threaded apartment required by
96
+ * System.Windows.Forms / CopyFromScreen is initialized even in sessions that
97
+ * default to MTA (avoids COM failures).
98
+ */
99
+ export declare function runPowerShell(psPath: string, opts?: {
100
+ timeoutMs?: number;
101
+ }): Promise<string>;
102
+ export declare function screenshot(options?: ScreenshotOptions): Promise<Buffer>;
103
+ export declare function screenshotWindow(windowId: number | string, options: ScreenshotOptions, platform: string): Promise<Buffer>;
104
+ export declare function listWindows(): Promise<WindowInfo[]>;
105
+ export declare function formatWindowList(windows: WindowInfo[], maxRows?: number): string;
106
+ /**
107
+ * Last-resort inline carrier for a screenshot that could not be hosted.
108
+ *
109
+ * Returns the image unchanged when its base64 payload already fits
110
+ * `MAX_INLINE_IMAGE_BASE64_CHARS`, a downscaled/re-encoded copy when resizing
111
+ * gets it under the cap, and `null` when even that fails (no image resizer
112
+ * available, or the capture is absurdly large). Callers MUST treat `null` as
113
+ * "do not attach base64" and report the failure — never as "attach it anyway".
114
+ *
115
+ * Exported for tests.
116
+ */
117
+ export declare function fitInlineScreenshot(image: ImageContent, maxBase64Chars?: number): Promise<ImageContent | null>;
16
118
  export default function desktopScreenshotExtension(ext: ExtensionAPI): Promise<void>;
119
+ export {};
17
120
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/desktop-screenshot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EAEX,YAAY,EAEZ,MAAM,iCAAiC,CAAC;AA6ezC,wBAA8B,0BAA0B,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFzF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/desktop-screenshot/index.ts"],"names":[],"mappings":"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"}