@jian-ai/remote-control 0.8.1 → 0.8.3

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jian-ai/remote-control",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Jian Remote Control worker — connect a computer to a Jian gateway over HTTPS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,8 @@
13
13
  "files": [
14
14
  "bin",
15
15
  "macos",
16
+ "linux",
17
+ "windows",
16
18
  "README.md"
17
19
  ],
18
20
  "engines": {
@@ -0,0 +1,501 @@
1
+ # Jian Remote Control — Windows computer-use helper
2
+ # JSON lines on stdin → JSON lines on stdout (same cmds as macOS/Linux helpers).
3
+ # Overlay: topmost transparent WinForms pointer (white + #2880F8 stroke/glow; macOS uses bitmap).
4
+ # Input: SendInput with save/restore so agent clicks do not strand the user cursor.
5
+ # English-only errors.
6
+
7
+ $ErrorActionPreference = 'Stop'
8
+ Add-Type -AssemblyName System.Windows.Forms
9
+ Add-Type -AssemblyName System.Drawing
10
+
11
+ $script:PointerSize = 36
12
+ $script:HotspotX = 2
13
+ $script:HotspotY = 2
14
+ $script:TipX = 0.0
15
+ $script:TipY = 0.0
16
+ $script:HasTip = $false
17
+ $script:OverlayForm = $null
18
+ $script:SessionActive = $false
19
+
20
+ Add-Type @"
21
+ using System;
22
+ using System.Runtime.InteropServices;
23
+ public static class JianInput {
24
+ [StructLayout(LayoutKind.Sequential)]
25
+ public struct INPUT {
26
+ public uint type;
27
+ public InputUnion U;
28
+ }
29
+ [StructLayout(LayoutKind.Explicit)]
30
+ public struct InputUnion {
31
+ [FieldOffset(0)] public MOUSEINPUT mi;
32
+ [FieldOffset(0)] public KEYBDINPUT ki;
33
+ }
34
+ [StructLayout(LayoutKind.Sequential)]
35
+ public struct MOUSEINPUT {
36
+ public int dx;
37
+ public int dy;
38
+ public uint mouseData;
39
+ public uint dwFlags;
40
+ public uint time;
41
+ public IntPtr dwExtraInfo;
42
+ }
43
+ [StructLayout(LayoutKind.Sequential)]
44
+ public struct KEYBDINPUT {
45
+ public ushort wVk;
46
+ public ushort wScan;
47
+ public uint dwFlags;
48
+ public uint time;
49
+ public IntPtr dwExtraInfo;
50
+ }
51
+ [StructLayout(LayoutKind.Sequential)]
52
+ public struct POINT { public int X; public int Y; }
53
+
54
+ public const uint INPUT_MOUSE = 0;
55
+ public const uint INPUT_KEYBOARD = 1;
56
+ public const uint MOUSEEVENTF_MOVE = 0x0001;
57
+ public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
58
+ public const uint MOUSEEVENTF_LEFTUP = 0x0004;
59
+ public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
60
+ public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
61
+ public const uint MOUSEEVENTF_WHEEL = 0x0800;
62
+ public const uint MOUSEEVENTF_HWHEEL = 0x1000;
63
+ public const uint MOUSEEVENTF_ABSOLUTE = 0x8000;
64
+ public const uint KEYEVENTF_KEYUP = 0x0002;
65
+ public const uint KEYEVENTF_UNICODE = 0x0004;
66
+
67
+ [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
68
+ [DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT lpPoint);
69
+ [DllImport("user32.dll")] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
70
+ [DllImport("user32.dll")] public static extern int ShowCursor(bool bShow);
71
+ [DllImport("user32.dll")] public static extern short VkKeyScan(char ch);
72
+ [DllImport("user32.dll")] public static extern uint MapVirtualKey(uint uCode, uint uMapType);
73
+ }
74
+ "@
75
+
76
+ function Write-Response($obj) {
77
+ $json = ($obj | ConvertTo-Json -Compress -Depth 6)
78
+ [Console]::Out.WriteLine($json)
79
+ [Console]::Out.Flush()
80
+ }
81
+
82
+ function Write-Ok([hashtable]$extra = @{}) {
83
+ $body = @{ ok = $true }
84
+ foreach ($k in $extra.Keys) { $body[$k] = $extra[$k] }
85
+ Write-Response $body
86
+ }
87
+
88
+ function Write-Err([string]$message) {
89
+ Write-Response @{ ok = $false; error = $message }
90
+ }
91
+
92
+ function Get-PrimaryBounds {
93
+ [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
94
+ }
95
+
96
+ function Ease-OutCubic([double]$t) {
97
+ $x = [Math]::Min(1.0, [Math]::Max(0.0, $t))
98
+ return 1.0 - [Math]::Pow(1.0 - $x, 3)
99
+ }
100
+
101
+ function Ensure-Overlay {
102
+ if ($script:OverlayForm -ne $null) { return }
103
+ $form = New-Object System.Windows.Forms.Form
104
+ $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
105
+ $form.ShowInTaskbar = $false
106
+ $form.TopMost = $true
107
+ $form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
108
+ $form.Size = New-Object System.Drawing.Size($script:PointerSize, $script:PointerSize)
109
+ $form.BackColor = [System.Drawing.Color]::Magenta
110
+ $form.TransparencyKey = [System.Drawing.Color]::Magenta
111
+ $form.Opacity = 0.94
112
+ $form.Enabled = $false
113
+ $form.Add_Paint({
114
+ param($sender, $e)
115
+ $g = $e.Graphics
116
+ $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
117
+ $pts = @(
118
+ (New-Object System.Drawing.Point(2, 2)),
119
+ (New-Object System.Drawing.Point(2, 26)),
120
+ (New-Object System.Drawing.Point(10, 20)),
121
+ (New-Object System.Drawing.Point(15, 32)),
122
+ (New-Object System.Drawing.Point(20, 30)),
123
+ (New-Object System.Drawing.Point(13, 17)),
124
+ (New-Object System.Drawing.Point(22, 17))
125
+ )
126
+ # Parity with macOS ship sprite: white fill + #2880F8 stroke/glow (geometric stand-in).
127
+ $glowBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(140, 40, 128, 248))
128
+ $glowPts = @(
129
+ (New-Object System.Drawing.Point(3, 4)),
130
+ (New-Object System.Drawing.Point(3, 28)),
131
+ (New-Object System.Drawing.Point(11, 22)),
132
+ (New-Object System.Drawing.Point(16, 34)),
133
+ (New-Object System.Drawing.Point(21, 32)),
134
+ (New-Object System.Drawing.Point(14, 19)),
135
+ (New-Object System.Drawing.Point(23, 19))
136
+ )
137
+ $g.FillPolygon($glowBrush, $glowPts)
138
+ $fill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(255, 255, 255, 255))
139
+ $g.FillPolygon($fill, $pts)
140
+ $pen = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(255, 40, 128, 248), 2.4)
141
+ $g.DrawPolygon($pen, $pts)
142
+ $glowBrush.Dispose(); $fill.Dispose(); $pen.Dispose()
143
+ })
144
+ $script:OverlayForm = $form
145
+ }
146
+
147
+ function Position-Overlay {
148
+ if ($script:OverlayForm -eq $null) { return }
149
+ $x = [int]($script:TipX - $script:HotspotX)
150
+ $y = [int]($script:TipY - $script:HotspotY)
151
+ $script:OverlayForm.Location = New-Object System.Drawing.Point($x, $y)
152
+ }
153
+
154
+ function Ensure-Session {
155
+ Ensure-Overlay
156
+ if (-not $script:HasTip) {
157
+ $b = Get-PrimaryBounds
158
+ $script:TipX = $b.Width / 2
159
+ $script:TipY = $b.Height / 2
160
+ $script:HasTip = $true
161
+ }
162
+ Position-Overlay
163
+ if (-not $script:OverlayForm.Visible) {
164
+ $script:OverlayForm.Show()
165
+ }
166
+ $script:OverlayForm.Opacity = 0.94
167
+ $script:OverlayForm.Invalidate()
168
+ $script:SessionActive = $true
169
+ [System.Windows.Forms.Application]::DoEvents()
170
+ }
171
+
172
+ function Set-Idle {
173
+ Ensure-Session
174
+ $script:OverlayForm.Opacity = 0.18
175
+ [System.Windows.Forms.Application]::DoEvents()
176
+ }
177
+
178
+ function End-Session {
179
+ if ($script:OverlayForm -ne $null) {
180
+ $script:OverlayForm.Hide()
181
+ $script:OverlayForm.Opacity = 0
182
+ }
183
+ $script:SessionActive = $false
184
+ $script:HasTip = $false
185
+ [System.Windows.Forms.Application]::DoEvents()
186
+ }
187
+
188
+ function Move-Tip([double]$x, [double]$y) {
189
+ $script:TipX = $x
190
+ $script:TipY = $y
191
+ $script:HasTip = $true
192
+ Ensure-Overlay
193
+ if ($script:SessionActive -or $true) {
194
+ if (-not $script:OverlayForm.Visible) { $script:OverlayForm.Show() }
195
+ }
196
+ Position-Overlay
197
+ $script:OverlayForm.Invalidate()
198
+ [System.Windows.Forms.Application]::DoEvents()
199
+ }
200
+
201
+ function Animate-Overlay([double]$tx, [double]$ty) {
202
+ Ensure-Session
203
+ $sx = $script:TipX
204
+ $sy = $script:TipY
205
+ $dx = $tx - $sx
206
+ $dy = $ty - $sy
207
+ $distance = [Math]::Sqrt($dx * $dx + $dy * $dy)
208
+ if ($distance -lt 1.5) {
209
+ Move-Tip $tx $ty
210
+ return
211
+ }
212
+ $duration = [Math]::Min(0.11, [Math]::Max(0.028, $distance * 0.00025))
213
+ $steps = [Math]::Max(1, [int][Math]::Ceiling($duration * 60))
214
+ $stepSleep = $duration / $steps
215
+ for ($i = 1; $i -le $steps; $i++) {
216
+ $t = Ease-OutCubic ($i / $steps)
217
+ Move-Tip ($sx + $dx * $t) ($sy + $dy * $t)
218
+ if ($i -lt $steps) { Start-Sleep -Milliseconds ([Math]::Max(1, [int]($stepSleep * 1000))) }
219
+ }
220
+ Move-Tip $tx $ty
221
+ }
222
+
223
+ function Get-CursorPos {
224
+ $p = New-Object JianInput+POINT
225
+ [void][JianInput]::GetCursorPos([ref]$p)
226
+ return @($p.X, $p.Y)
227
+ }
228
+
229
+ function Warp-Cursor([int]$x, [int]$y) {
230
+ [void][JianInput]::SetCursorPos($x, $y)
231
+ }
232
+
233
+ function With-PreservedCursor([scriptblock]$body) {
234
+ $saved = Get-CursorPos
235
+ [void][JianInput]::ShowCursor($false)
236
+ try {
237
+ & $body
238
+ } finally {
239
+ Warp-Cursor $saved[0] $saved[1]
240
+ Start-Sleep -Milliseconds 8
241
+ [void][JianInput]::ShowCursor($true)
242
+ }
243
+ }
244
+
245
+ function Send-MouseClick([string]$button, [int]$count) {
246
+ $down = if ($button -eq 'right') { [JianInput]::MOUSEEVENTF_RIGHTDOWN } else { [JianInput]::MOUSEEVENTF_LEFTDOWN }
247
+ $up = if ($button -eq 'right') { [JianInput]::MOUSEEVENTF_RIGHTUP } else { [JianInput]::MOUSEEVENTF_LEFTUP }
248
+ for ($i = 0; $i -lt $count; $i++) {
249
+ $inputs = @(
250
+ New-Object JianInput+INPUT
251
+ New-Object JianInput+INPUT
252
+ )
253
+ $inputs[0].type = [JianInput]::INPUT_MOUSE
254
+ $inputs[0].U.mi.dwFlags = $down
255
+ $inputs[1].type = [JianInput]::INPUT_MOUSE
256
+ $inputs[1].U.mi.dwFlags = $up
257
+ [void][JianInput]::SendInput(2, $inputs, [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
258
+ if ($i -lt $count - 1) { Start-Sleep -Milliseconds 40 }
259
+ }
260
+ }
261
+
262
+ function Click-At([double]$x, [double]$y, [string]$button, [int]$count) {
263
+ Animate-Overlay $x $y
264
+ With-PreservedCursor {
265
+ Warp-Cursor ([int]$x) ([int]$y)
266
+ Send-MouseClick $button $count
267
+ }
268
+ }
269
+
270
+ function Drag-To([double]$fx, [double]$fy, [double]$tx, [double]$ty) {
271
+ Animate-Overlay $fx $fy
272
+ With-PreservedCursor {
273
+ Warp-Cursor ([int]$fx) ([int]$fy)
274
+ $down = New-Object JianInput+INPUT
275
+ $down.type = [JianInput]::INPUT_MOUSE
276
+ $down.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_LEFTDOWN
277
+ [void][JianInput]::SendInput(1, @($down), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
278
+ $distance = [Math]::Sqrt(($tx - $fx) * ($tx - $fx) + ($ty - $fy) * ($ty - $fy))
279
+ $steps = [Math]::Max(6, [Math]::Min(14, [int]($distance / 40) + 6))
280
+ for ($i = 1; $i -le $steps; $i++) {
281
+ $t = Ease-OutCubic ($i / $steps)
282
+ $px = $fx + ($tx - $fx) * $t
283
+ $py = $fy + ($ty - $fy) * $t
284
+ Move-Tip $px $py
285
+ Warp-Cursor ([int]$px) ([int]$py)
286
+ Start-Sleep -Milliseconds 6
287
+ }
288
+ $up = New-Object JianInput+INPUT
289
+ $up.type = [JianInput]::INPUT_MOUSE
290
+ $up.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_LEFTUP
291
+ [void][JianInput]::SendInput(1, @($up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
292
+ }
293
+ Move-Tip $tx $ty
294
+ }
295
+
296
+ function Scroll-At([double]$x, [double]$y, [string]$direction, [int]$amount) {
297
+ Animate-Overlay $x $y
298
+ $wheel = 0
299
+ $hwheel = 0
300
+ switch ($direction) {
301
+ 'up' { $wheel = 120 }
302
+ 'down' { $wheel = -120 }
303
+ 'left' { $hwheel = -120 }
304
+ 'right' { $hwheel = 120 }
305
+ }
306
+ With-PreservedCursor {
307
+ Warp-Cursor ([int]$x) ([int]$y)
308
+ for ($i = 0; $i -lt [Math]::Max(1, $amount); $i++) {
309
+ $inp = New-Object JianInput+INPUT
310
+ $inp.type = [JianInput]::INPUT_MOUSE
311
+ if ($wheel -ne 0) {
312
+ $inp.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_WHEEL
313
+ $inp.U.mi.mouseData = [uint32]([int32]$wheel)
314
+ } else {
315
+ $inp.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_HWHEEL
316
+ $inp.U.mi.mouseData = [uint32]([int32]$hwheel)
317
+ }
318
+ [void][JianInput]::SendInput(1, @($inp), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
319
+ Start-Sleep -Milliseconds 12
320
+ }
321
+ }
322
+ }
323
+
324
+ function Type-Text([string]$text) {
325
+ foreach ($ch in $text.ToCharArray()) {
326
+ if ($ch -eq "`n" -or $ch -eq "`r") {
327
+ Post-Key 0x0D
328
+ continue
329
+ }
330
+ if ($ch -eq "`t") {
331
+ Post-Key 0x09
332
+ continue
333
+ }
334
+ $inputs = @(
335
+ New-Object JianInput+INPUT
336
+ New-Object JianInput+INPUT
337
+ )
338
+ $inputs[0].type = [JianInput]::INPUT_KEYBOARD
339
+ $inputs[0].U.ki.wScan = [uint16][int][char]$ch
340
+ $inputs[0].U.ki.dwFlags = [JianInput]::KEYEVENTF_UNICODE
341
+ $inputs[1].type = [JianInput]::INPUT_KEYBOARD
342
+ $inputs[1].U.ki.wScan = [uint16][int][char]$ch
343
+ $inputs[1].U.ki.dwFlags = [JianInput]::KEYEVENTF_UNICODE -bor [JianInput]::KEYEVENTF_KEYUP
344
+ [void][JianInput]::SendInput(2, $inputs, [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
345
+ Start-Sleep -Milliseconds 2
346
+ }
347
+ }
348
+
349
+ function Post-Key([ushort]$vk, [uint32]$flags = 0) {
350
+ $down = New-Object JianInput+INPUT
351
+ $down.type = [JianInput]::INPUT_KEYBOARD
352
+ $down.U.ki.wVk = $vk
353
+ $down.U.ki.dwFlags = $flags
354
+ $up = New-Object JianInput+INPUT
355
+ $up.type = [JianInput]::INPUT_KEYBOARD
356
+ $up.U.ki.wVk = $vk
357
+ $up.U.ki.dwFlags = $flags -bor [JianInput]::KEYEVENTF_KEYUP
358
+ [void][JianInput]::SendInput(2, @($down, $up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
359
+ }
360
+
361
+ function Parse-And-PostKey([string]$keySpec) {
362
+ $parts = $keySpec.Split('+') | ForEach-Object { $_.Trim().ToLowerInvariant() } | Where-Object { $_ }
363
+ if (-not $parts) { return $false }
364
+ $mods = @()
365
+ $key = $null
366
+ foreach ($part in $parts) {
367
+ switch ($part) {
368
+ { $_ -in @('cmd','command','meta','super','win','windows') } { $mods += 0x5B; break }
369
+ { $_ -in @('ctrl','control') } { $mods += 0x11; break }
370
+ { $_ -in @('alt','option','opt') } { $mods += 0x12; break }
371
+ 'shift' { $mods += 0x10; break }
372
+ default {
373
+ if ($null -ne $key) { return $false }
374
+ $key = $part
375
+ }
376
+ }
377
+ }
378
+ if ($null -eq $key) { return $false }
379
+ $named = @{
380
+ return = 0x0D; enter = 0x0D; tab = 0x09; escape = 0x1B; esc = 0x1B
381
+ delete = 0x2E; backspace = 0x08; space = 0x20
382
+ up = 0x26; down = 0x28; left = 0x25; right = 0x27
383
+ home = 0x24; end = 0x23; pageup = 0x21; pagedown = 0x22
384
+ }
385
+ $vk = $null
386
+ if ($named.ContainsKey($key)) { $vk = [ushort]$named[$key] }
387
+ elseif ($key.Length -eq 1) {
388
+ $scan = [JianInput]::VkKeyScan([char]$key[0])
389
+ if ($scan -eq -1) { return $false }
390
+ $vk = [ushort]($scan -band 0xFF)
391
+ }
392
+ elseif ($key.StartsWith('f') -and $key.Substring(1) -match '^\d+$') {
393
+ $n = [int]$key.Substring(1)
394
+ if ($n -ge 1 -and $n -le 12) { $vk = [ushort](0x70 + $n - 1) }
395
+ }
396
+ if ($null -eq $vk) { return $false }
397
+ foreach ($m in $mods) {
398
+ $down = New-Object JianInput+INPUT
399
+ $down.type = [JianInput]::INPUT_KEYBOARD
400
+ $down.U.ki.wVk = [ushort]$m
401
+ [void][JianInput]::SendInput(1, @($down), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
402
+ }
403
+ Post-Key $vk
404
+ for ($i = $mods.Count - 1; $i -ge 0; $i--) {
405
+ $up = New-Object JianInput+INPUT
406
+ $up.type = [JianInput]::INPUT_KEYBOARD
407
+ $up.U.ki.wVk = [ushort]$mods[$i]
408
+ $up.U.ki.dwFlags = [JianInput]::KEYEVENTF_KEYUP
409
+ [void][JianInput]::SendInput(1, @($up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
410
+ }
411
+ return $true
412
+ }
413
+
414
+ function Geometry-Response {
415
+ $b = Get-PrimaryBounds
416
+ return @{
417
+ ok = $true
418
+ originX = 0
419
+ originY = 0
420
+ pointWidth = [double]$b.Width
421
+ pointHeight = [double]$b.Height
422
+ pixelWidth = [double]$b.Width
423
+ pixelHeight = [double]$b.Height
424
+ scaleFactor = 1.0
425
+ }
426
+ }
427
+
428
+ function Handle-Command($obj) {
429
+ $cmd = [string]$obj.cmd
430
+ switch ($cmd) {
431
+ 'ping' { Write-Ok; break }
432
+ 'geometry' { Write-Response (Geometry-Response); break }
433
+ 'ensureSession' { Ensure-Session; Write-Ok; break }
434
+ 'setIdle' { Set-Idle; Write-Ok; break }
435
+ 'endSession' { End-Session; Write-Ok; break }
436
+ 'move' {
437
+ Animate-Overlay ([double]$obj.pointX) ([double]$obj.pointY)
438
+ Write-Ok
439
+ break
440
+ }
441
+ 'click' {
442
+ $button = if ($obj.button) { [string]$obj.button } else { 'left' }
443
+ $count = if ($obj.count) { [int]$obj.count } else { 1 }
444
+ Click-At $script:TipX $script:TipY $button.ToLowerInvariant() $count
445
+ Write-Ok
446
+ break
447
+ }
448
+ 'clickAt' {
449
+ $button = if ($obj.button) { [string]$obj.button } else { 'left' }
450
+ $count = if ($obj.count) { [int]$obj.count } else { 1 }
451
+ Click-At ([double]$obj.pointX) ([double]$obj.pointY) $button.ToLowerInvariant() $count
452
+ Write-Ok
453
+ break
454
+ }
455
+ 'drag' {
456
+ Drag-To ([double]$obj.fromX) ([double]$obj.fromY) ([double]$obj.toX) ([double]$obj.toY)
457
+ Write-Ok
458
+ break
459
+ }
460
+ 'type' {
461
+ Type-Text ([string]$obj.text)
462
+ Write-Ok
463
+ break
464
+ }
465
+ 'key' {
466
+ if (Parse-And-PostKey ([string]$obj.key)) { Write-Ok }
467
+ else { Write-Err ("unrecognized key: " + $obj.key) }
468
+ break
469
+ }
470
+ 'scroll' {
471
+ Scroll-At ([double]$obj.pointX) ([double]$obj.pointY) ([string]$obj.direction).ToLowerInvariant() ([int]$obj.amount)
472
+ Write-Ok
473
+ break
474
+ }
475
+ default { Write-Err ("unknown cmd: " + $cmd) }
476
+ }
477
+ }
478
+
479
+ [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
480
+ [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
481
+
482
+ while ($true) {
483
+ $line = [Console]::In.ReadLine()
484
+ if ($null -eq $line) { break }
485
+ $line = $line.Trim()
486
+ if (-not $line) { continue }
487
+ try {
488
+ $obj = $line | ConvertFrom-Json
489
+ Handle-Command $obj
490
+ } catch {
491
+ [Console]::Error.WriteLine("computer-helper: $($_.Exception.Message)")
492
+ Write-Err $_.Exception.Message
493
+ }
494
+ [System.Windows.Forms.Application]::DoEvents()
495
+ }
496
+
497
+ End-Session
498
+ if ($script:OverlayForm -ne $null) {
499
+ $script:OverlayForm.Close()
500
+ $script:OverlayForm.Dispose()
501
+ }