create-openclaw-bot 5.15.1 → 5.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.vi.md +2 -2
- package/dist/server/local-server.js +822 -89
- package/dist/setup/shared/bot-config-gen.js +10 -2
- package/dist/setup/shared/host-ui-ps1.js +179 -0
- package/package.json +1 -1
|
@@ -277,11 +277,19 @@
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
// ── gateway ──────────────────────────────────────────────────────────────
|
|
280
|
+
// Docker MUST bind 0.0.0.0 inside the container to be reachable at all, but that is contained:
|
|
281
|
+
// compose publishes the port as `127.0.0.1:<port>:<port>` (docker-gen), so the host still only
|
|
282
|
+
// answers on loopback. A NATIVE gateway has no port mapping to hide behind — 0.0.0.0 there puts
|
|
283
|
+
// it straight on the VPS's public interface, with the auth token crossing the wire in plaintext
|
|
284
|
+
// (the gateway speaks plain HTTP/WS) and, on a fresh VPS, no firewall in front of it. The
|
|
285
|
+
// `osChoice === 'vps'` clause predates native mode; keep native on loopback and reach it through
|
|
286
|
+
// an SSH tunnel, which is what docker-on-a-VPS effectively does too.
|
|
287
|
+
const openGatewayBind = deployMode === 'docker' || (osChoice === 'vps' && deployMode !== 'native');
|
|
280
288
|
cfg.gateway = {
|
|
281
289
|
port: gatewayPort,
|
|
282
290
|
mode: 'local',
|
|
283
|
-
bind:
|
|
284
|
-
...(
|
|
291
|
+
bind: openGatewayBind ? 'custom' : 'loopback',
|
|
292
|
+
...(openGatewayBind ? { customBindHost: '0.0.0.0' } : {}),
|
|
285
293
|
controlUi: {
|
|
286
294
|
allowedOrigins: gatewayAllowedOrigins.length > 0
|
|
287
295
|
? gatewayAllowedOrigins
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// PowerShell backend for host UI automation on Windows.
|
|
3
|
+
//
|
|
4
|
+
// Windows has no xdotool and no bundled CLI for input, so the actions are done the way the
|
|
5
|
+
// no-dependency tools do it: P/Invoke into user32.dll for the pointer and keys (SetCursorPos /
|
|
6
|
+
// mouse_event / keybd_event), System.Windows.Forms.SendKeys for text, System.Drawing's
|
|
7
|
+
// CopyFromScreen for captures, and Get/Set-Clipboard for the clipboard. Shipping this as one
|
|
8
|
+
// version-stamped .ps1 keeps the quoting sane: the server passes typed parameters, never a
|
|
9
|
+
// composed command line.
|
|
10
|
+
(function (root) {
|
|
11
|
+
const HOST_UI_PS1_VERSION = '1';
|
|
12
|
+
|
|
13
|
+
const HOST_UI_PS1 = String.raw`# OpenClaw host UI helper — version __VERSION__
|
|
14
|
+
# Generated by openclaw-setup. Edits are overwritten when the version changes.
|
|
15
|
+
param(
|
|
16
|
+
[Parameter(Mandatory = $true)][string]$Action,
|
|
17
|
+
[int]$X = -1,
|
|
18
|
+
[int]$Y = -1,
|
|
19
|
+
[int]$ToX = -1,
|
|
20
|
+
[int]$ToY = -1,
|
|
21
|
+
[int]$Amount = 3,
|
|
22
|
+
[string]$Text = '',
|
|
23
|
+
[string]$Button = 'left',
|
|
24
|
+
[int]$Clicks = 1,
|
|
25
|
+
[string]$Path = '',
|
|
26
|
+
[string]$Title = ''
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
$ErrorActionPreference = 'Stop'
|
|
30
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
31
|
+
|
|
32
|
+
if (-not ('OpenClawUi' -as [type])) {
|
|
33
|
+
Add-Type -Namespace '' -Name OpenClawUi -MemberDefinition @'
|
|
34
|
+
[DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y);
|
|
35
|
+
[DllImport("user32.dll")] public static extern void mouse_event(uint flags, uint dx, uint dy, uint data, int extra);
|
|
36
|
+
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(System.IntPtr hWnd);
|
|
37
|
+
[DllImport("user32.dll")] public static extern bool ShowWindow(System.IntPtr hWnd, int cmd);
|
|
38
|
+
'@
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
$MOUSEEVENTF = @{
|
|
42
|
+
leftdown = 0x0002; leftup = 0x0004;
|
|
43
|
+
rightdown = 0x0008; rightup = 0x0010;
|
|
44
|
+
middledown = 0x0020; middleup = 0x0040;
|
|
45
|
+
wheel = 0x0800; hwheel = 0x01000;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Out-Json($obj) { $obj | ConvertTo-Json -Compress -Depth 6 }
|
|
49
|
+
|
|
50
|
+
function Move-Pointer([int]$px, [int]$py) {
|
|
51
|
+
if ($px -ge 0 -and $py -ge 0) { [OpenClawUi]::SetCursorPos($px, $py) | Out-Null; Start-Sleep -Milliseconds 40 }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Invoke-Click([string]$btn, [int]$times) {
|
|
55
|
+
$down = $MOUSEEVENTF[($btn + 'down')]
|
|
56
|
+
$up = $MOUSEEVENTF[($btn + 'up')]
|
|
57
|
+
if (-not $down) { throw "unknown button: $btn" }
|
|
58
|
+
for ($i = 0; $i -lt [Math]::Max(1, $times); $i++) {
|
|
59
|
+
[OpenClawUi]::mouse_event($down, 0, 0, 0, 0)
|
|
60
|
+
[OpenClawUi]::mouse_event($up, 0, 0, 0, 0)
|
|
61
|
+
Start-Sleep -Milliseconds 60
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# SendKeys needs its own escaping: + ^ % ~ ( ) { } [ ] are modifiers there, so literal text has
|
|
66
|
+
# to be wrapped in braces or the bot's message silently turns into shortcuts.
|
|
67
|
+
function ConvertTo-SendKeysLiteral([string]$s) {
|
|
68
|
+
$sb = New-Object System.Text.StringBuilder
|
|
69
|
+
foreach ($ch in $s.ToCharArray()) {
|
|
70
|
+
if ('+^%~(){}[]'.Contains($ch)) { [void]$sb.Append('{' + $ch + '}') }
|
|
71
|
+
elseif ($ch -eq [char]10) { [void]$sb.Append('{ENTER}') }
|
|
72
|
+
elseif ($ch -eq [char]9) { [void]$sb.Append('{TAB}') }
|
|
73
|
+
elseif ($ch -eq [char]13) { }
|
|
74
|
+
else { [void]$sb.Append($ch) }
|
|
75
|
+
}
|
|
76
|
+
$sb.ToString()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# "ctrl+shift+t" / "enter" / "cmd+a" (cmd is mapped to ctrl on Windows) → SendKeys syntax.
|
|
80
|
+
function ConvertTo-SendKeysCombo([string]$combo) {
|
|
81
|
+
$named = @{
|
|
82
|
+
enter = '{ENTER}'; return = '{ENTER}'; tab = '{TAB}'; esc = '{ESC}'; escape = '{ESC}';
|
|
83
|
+
backspace = '{BACKSPACE}'; delete = '{DELETE}'; del = '{DELETE}'; home = '{HOME}'; end = '{END}';
|
|
84
|
+
pageup = '{PGUP}'; pagedown = '{PGDN}'; up = '{UP}'; down = '{DOWN}'; left = '{LEFT}'; right = '{RIGHT}';
|
|
85
|
+
space = ' '; f1 = '{F1}'; f2 = '{F2}'; f3 = '{F3}'; f4 = '{F4}'; f5 = '{F5}'; f6 = '{F6}';
|
|
86
|
+
f7 = '{F7}'; f8 = '{F8}'; f9 = '{F9}'; f10 = '{F10}'; f11 = '{F11}'; f12 = '{F12}';
|
|
87
|
+
printscreen = '{PRTSC}'; insert = '{INSERT}';
|
|
88
|
+
}
|
|
89
|
+
$prefix = ''
|
|
90
|
+
$parts = $combo.Split('+') | ForEach-Object { $_.Trim().ToLower() } | Where-Object { $_ }
|
|
91
|
+
$keyPart = ''
|
|
92
|
+
foreach ($p in $parts) {
|
|
93
|
+
switch ($p) {
|
|
94
|
+
'ctrl' { $prefix += '^' }
|
|
95
|
+
'control' { $prefix += '^' }
|
|
96
|
+
'cmd' { $prefix += '^' } # macOS habit; Windows equivalent is Ctrl
|
|
97
|
+
'meta' { $prefix += '^' }
|
|
98
|
+
'alt' { $prefix += '%' }
|
|
99
|
+
'shift' { $prefix += '+' }
|
|
100
|
+
'win' { $prefix += '' } # SendKeys cannot press Win; use the Windows key combos below
|
|
101
|
+
default { $keyPart = $p }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (-not $keyPart) { throw "no key in combo: $combo" }
|
|
105
|
+
$mapped = if ($named.ContainsKey($keyPart)) { $named[$keyPart] } else { $keyPart }
|
|
106
|
+
$prefix + $mapped
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
switch ($Action) {
|
|
110
|
+
'screen_size' {
|
|
111
|
+
$b = [System.Windows.Forms.SystemInformation]::VirtualScreen
|
|
112
|
+
Out-Json @{ ok = $true; width = $b.Width; height = $b.Height; left = $b.Left; top = $b.Top }
|
|
113
|
+
}
|
|
114
|
+
'screenshot' {
|
|
115
|
+
if (-not $Path) { throw 'screenshot needs -Path' }
|
|
116
|
+
$b = [System.Windows.Forms.SystemInformation]::VirtualScreen
|
|
117
|
+
$bmp = New-Object System.Drawing.Bitmap $b.Width, $b.Height
|
|
118
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
119
|
+
$g.CopyFromScreen($b.Left, $b.Top, 0, 0, $bmp.Size)
|
|
120
|
+
$dir = Split-Path -Parent $Path
|
|
121
|
+
if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
|
|
122
|
+
$bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
123
|
+
$g.Dispose(); $bmp.Dispose()
|
|
124
|
+
Out-Json @{ ok = $true; path = $Path; width = $b.Width; height = $b.Height }
|
|
125
|
+
}
|
|
126
|
+
'mouse_move' { Move-Pointer $X $Y; Out-Json @{ ok = $true; x = $X; y = $Y } }
|
|
127
|
+
'click' { Move-Pointer $X $Y; Invoke-Click $Button $Clicks; Out-Json @{ ok = $true; button = $Button; clicks = $Clicks } }
|
|
128
|
+
'drag' {
|
|
129
|
+
Move-Pointer $X $Y
|
|
130
|
+
[OpenClawUi]::mouse_event($MOUSEEVENTF['leftdown'], 0, 0, 0, 0)
|
|
131
|
+
Start-Sleep -Milliseconds 80
|
|
132
|
+
Move-Pointer $ToX $ToY
|
|
133
|
+
[OpenClawUi]::mouse_event($MOUSEEVENTF['leftup'], 0, 0, 0, 0)
|
|
134
|
+
Out-Json @{ ok = $true; from = @($X, $Y); to = @($ToX, $ToY) }
|
|
135
|
+
}
|
|
136
|
+
'scroll' {
|
|
137
|
+
Move-Pointer $X $Y
|
|
138
|
+
$ticks = [Math]::Max(1, [Math]::Abs($Amount))
|
|
139
|
+
$delta = if ($Amount -lt 0) { -120 } else { 120 }
|
|
140
|
+
for ($i = 0; $i -lt $ticks; $i++) {
|
|
141
|
+
[OpenClawUi]::mouse_event($MOUSEEVENTF['wheel'], 0, 0, [uint32]([int]$delta -band 0xFFFFFFFF), 0)
|
|
142
|
+
Start-Sleep -Milliseconds 40
|
|
143
|
+
}
|
|
144
|
+
Out-Json @{ ok = $true; amount = $Amount }
|
|
145
|
+
}
|
|
146
|
+
'type' {
|
|
147
|
+
[System.Windows.Forms.SendKeys]::SendWait((ConvertTo-SendKeysLiteral $Text))
|
|
148
|
+
Out-Json @{ ok = $true; typed = $Text.Length }
|
|
149
|
+
}
|
|
150
|
+
'key' {
|
|
151
|
+
foreach ($combo in ($Text -split '\s+' | Where-Object { $_ })) {
|
|
152
|
+
[System.Windows.Forms.SendKeys]::SendWait((ConvertTo-SendKeysCombo $combo))
|
|
153
|
+
Start-Sleep -Milliseconds 60
|
|
154
|
+
}
|
|
155
|
+
Out-Json @{ ok = $true; keys = $Text }
|
|
156
|
+
}
|
|
157
|
+
'clipboard_get' { Out-Json @{ ok = $true; text = (Get-Clipboard -Raw) } }
|
|
158
|
+
'clipboard_set' { Set-Clipboard -Value $Text; Out-Json @{ ok = $true; length = $Text.Length } }
|
|
159
|
+
'windows' {
|
|
160
|
+
$list = Get-Process | Where-Object { $_.MainWindowTitle } |
|
|
161
|
+
Select-Object -First 40 @{n = 'title'; e = { $_.MainWindowTitle } }, @{n = 'process'; e = { $_.ProcessName } }, Id
|
|
162
|
+
Out-Json @{ ok = $true; windows = @($list) }
|
|
163
|
+
}
|
|
164
|
+
'focus' {
|
|
165
|
+
if (-not $Title) { throw 'focus needs -Title' }
|
|
166
|
+
$proc = Get-Process | Where-Object { $_.MainWindowTitle -like "*$Title*" } | Select-Object -First 1
|
|
167
|
+
if (-not $proc) { Out-Json @{ ok = $false; error = "no window matching: $Title" }; break }
|
|
168
|
+
[OpenClawUi]::ShowWindow($proc.MainWindowHandle, 9) | Out-Null # SW_RESTORE
|
|
169
|
+
[OpenClawUi]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null
|
|
170
|
+
Out-Json @{ ok = $true; focused = $proc.MainWindowTitle }
|
|
171
|
+
}
|
|
172
|
+
default { Out-Json @{ ok = $false; error = "unknown action: $Action" } }
|
|
173
|
+
}
|
|
174
|
+
`.replace('__VERSION__', HOST_UI_PS1_VERSION);
|
|
175
|
+
|
|
176
|
+
const api = { HOST_UI_PS1, HOST_UI_PS1_VERSION };
|
|
177
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
178
|
+
root.__openclawHostUiPs1 = api;
|
|
179
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this);
|