@cocorograph/hub-agent 0.6.71 → 0.6.73

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 CHANGED
@@ -22,6 +22,26 @@ curl -fsSL https://api.hub.cocorograph.com/api/cockpit/agents/install-script | b
22
22
  - `hub-agent enroll <token>` (token は Hub session 経由で埋め込み済、5 分有効)
23
23
  - `hub-agent install-service` で OS サービス登録
24
24
 
25
+ ### Windows (WSL2 方式)
26
+
27
+ Windows では WSL2 (Ubuntu) の中で hub-agent を動かします。Cockpit Agents ページで
28
+ OS を Windows に切り替えると **管理者 PowerShell 用ワンライナー**が表示されるので、
29
+ **管理者として実行した PowerShell** にコピペします:
30
+
31
+ ```powershell
32
+ $env:HUB_AGENT_TOKEN="<tok>"; $env:HUB_AGENT_URL="https://api.hub.cocorograph.com"; `
33
+ irm https://unpkg.com/@cocorograph/hub-agent@latest/scripts/install.ps1 | iex
34
+ ```
35
+
36
+ このスクリプトは WSL2 (`Ubuntu-24.04`) を導入 (要再起動 1 回・再起動後に自動再開)、
37
+ Ubuntu 初回ユーザーを無人作成 (Windows と同名 / パスワードなし / NOPASSWD sudo)、
38
+ WSL 内で既存 `install.sh` を実行、Task Scheduler でログオン時の常駐起動を登録します。
39
+
40
+ > ⚠️ 作業リポジトリは **WSL 内 FS** (`/home/<user>/...`) に置いてください。Windows ドライブ
41
+ > (`/mnt/c/...`) はファイル監視 (inotify) が WSL 跨ぎで不安定です。
42
+
43
+ 詳細・設計判断は `scripts/install.ps1` と `docs/windows-installer-design.md` を参照。
44
+
25
45
  ### 手動インストール (デバッグ用)
26
46
 
27
47
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.6.71",
3
+ "version": "0.6.73",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -0,0 +1,325 @@
1
+ # =============================================================================
2
+ # hub-agent Windows one-liner installer (WSL2 method)
3
+ #
4
+ # The Hub UI shows a PowerShell one-liner with the enrollment token + hub url
5
+ # embedded. The user pastes it into an ADMINISTRATOR PowerShell. This is the
6
+ # Windows counterpart of the bash install.sh.
7
+ #
8
+ # IMPORTANT: This script is intentionally ASCII-only. Windows PowerShell 5.1
9
+ # fetches the script with `irm` and runs it with `iex`; when the HTTP response
10
+ # has no charset, 5.1 decodes UTF-8 bytes using the legacy code page (e.g.
11
+ # CP932 on Japanese Windows), which turns any non-ASCII text into mojibake and
12
+ # can break parsing. Keeping everything ASCII avoids that entirely.
13
+ #
14
+ # Design doc: D00000_hub-agent/docs/windows-installer-design.md
15
+ #
16
+ # Standalone (no token):
17
+ # irm https://unpkg.com/@cocorograph/hub-agent@latest/scripts/install.ps1 | iex
18
+ #
19
+ # Form generated by the Hub UI (token/url embedded):
20
+ # $env:HUB_AGENT_TOKEN="<tok>"; $env:HUB_AGENT_URL="https://api.hub.cocorograph.com"; `
21
+ # irm https://unpkg.com/@cocorograph/hub-agent@latest/scripts/install.ps1 | iex
22
+ #
23
+ # Flow:
24
+ # Phase 1: install WSL2 (admin required; reboot bridged via RunOnce)
25
+ # After reboot: create the first Ubuntu user unattended
26
+ # (cloud-init first, root-install + useradd as fallback)
27
+ # Phase 2: run the existing install.sh inside WSL
28
+ # (tmux/node/hub-agent/claude/enroll/install-service)
29
+ # Autostart: a Task Scheduler logon task wakes the distro so its systemd
30
+ # brings hub-agent up.
31
+ # =============================================================================
32
+
33
+ #Requires -Version 5.1
34
+ $ErrorActionPreference = "Stop"
35
+
36
+ # --- constants --------------------------------------------------------------
37
+ # Preferred distro is the LTS. We try this first, then a couple of fallbacks
38
+ # (older Windows 10 WSL catalogs may not know "Ubuntu-24.04").
39
+ $PREFERRED_DISTRO = "Ubuntu-24.04"
40
+ $DISTRO_CANDIDATES = @("Ubuntu-24.04", "Ubuntu")
41
+ $INSTALL_SH = "https://unpkg.com/@cocorograph/hub-agent@latest/scripts/install.sh"
42
+ $PS1_URL = "https://unpkg.com/@cocorograph/hub-agent@latest/scripts/install.ps1"
43
+ $RUNONCE_NAME = "HubAgentSetup"
44
+ $TASK_NAME = "HubAgentWSLBoot"
45
+ $DEFAULT_HUB = "https://api.hub.cocorograph.com"
46
+
47
+ # --- params from env --------------------------------------------------------
48
+ $Token = $env:HUB_AGENT_TOKEN
49
+ $HubUrl = if ($env:HUB_AGENT_URL) { $env:HUB_AGENT_URL } else { $DEFAULT_HUB }
50
+ $IsResume = ($env:HUB_AGENT_RESUME -eq "1") # set by RunOnce after reboot
51
+
52
+ # --- colored logging (ASCII only) ------------------------------------------
53
+ function Write-Step($m) { Write-Host "==> $m" -ForegroundColor Cyan }
54
+ function Write-Ok($m) { Write-Host "[OK] $m" -ForegroundColor Green }
55
+ function Write-Warn($m) { Write-Host "[!] $m" -ForegroundColor Yellow }
56
+ function Write-Err($m) { Write-Host "[x] $m" -ForegroundColor Red }
57
+
58
+ # --- helpers ----------------------------------------------------------------
59
+ function Test-IsAdmin {
60
+ $id = [Security.Principal.WindowsIdentity]::GetCurrent()
61
+ $pr = New-Object Security.Principal.WindowsPrincipal($id)
62
+ return $pr.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
63
+ }
64
+
65
+ # `wsl -l -q` prints UTF-16LE with stray NULs; sanitize and split to an array.
66
+ function Get-WslDistros {
67
+ try {
68
+ $raw = (wsl.exe -l -q) 2>$null
69
+ if (-not $raw) { return @() }
70
+ return ($raw -replace "`0", "") -split "`r?`n" |
71
+ ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
72
+ } catch { return @() }
73
+ }
74
+
75
+ # Return the name of an installed Ubuntu distro (prefer 24.04), or $null.
76
+ function Resolve-InstalledUbuntu {
77
+ $installed = Get-WslDistros
78
+ foreach ($cand in $DISTRO_CANDIDATES) {
79
+ if ($installed -contains $cand) { return $cand }
80
+ }
81
+ # any distro whose name starts with "Ubuntu"
82
+ $u = $installed | Where-Object { $_ -like "Ubuntu*" } | Select-Object -First 1
83
+ if ($u) { return $u }
84
+ return $null
85
+ }
86
+
87
+ # Is the WSL engine (kernel + platform) usable at all?
88
+ function Test-WslReady {
89
+ try { wsl.exe --status *> $null; return ($LASTEXITCODE -eq 0) } catch { return $false }
90
+ }
91
+
92
+ # Does user exist inside the given distro? (idempotency check)
93
+ function Test-WslUserReady($distro, $user) {
94
+ if (-not $distro -or -not $user) { return $false }
95
+ try { wsl.exe -d $distro -u root -- id $user *> $null; return ($LASTEXITCODE -eq 0) }
96
+ catch { return $false }
97
+ }
98
+
99
+ # Sanitize Windows username to a valid Linux name ([a-z_][a-z0-9_-]*).
100
+ function Get-LinuxUserName {
101
+ $u = ($env:USERNAME).ToLower() -replace '[^a-z0-9_-]', ''
102
+ if ($u -eq '' -or $u -match '^[0-9]') { $u = "hub$u" }
103
+ return $u
104
+ }
105
+
106
+ # Register "re-run myself with RESUME=1 after reboot", carrying token/url.
107
+ function Set-ResumeOnReboot {
108
+ $cmd = "powershell -NoProfile -ExecutionPolicy Bypass -Command " +
109
+ "`"`$env:HUB_AGENT_TOKEN='$Token'; `$env:HUB_AGENT_URL='$HubUrl'; " +
110
+ "`$env:HUB_AGENT_RESUME='1'; irm $PS1_URL | iex`""
111
+ Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce" `
112
+ -Name $RUNONCE_NAME -Value $cmd
113
+ }
114
+
115
+ # Leave the reboot decision to the user (auto-reboot risks unsaved work).
116
+ # RunOnce is already registered, so "later" still auto-resumes on next boot.
117
+ function Invoke-RebootPrompt {
118
+ Write-Host ""
119
+ Write-Step "WSL2 is installed. A Windows reboot is required to continue."
120
+ Write-Host " After reboot, setup resumes automatically (no manual steps)." -ForegroundColor Cyan
121
+ $ans = Read-Host "Reboot now? [Y = reboot now / N = I'll reboot later]"
122
+ if ($ans -match '^[Yy]') { Restart-Computer }
123
+ else { Write-Warn "Reboot Windows later and setup will continue automatically." }
124
+ }
125
+
126
+ # Place cloud-init user-data BEFORE first boot. WSL applies it on first launch,
127
+ # skipping the interactive OOBE. The file name must match the registered distro
128
+ # name, so we write one file per candidate name to be safe.
129
+ function Write-CloudInit($user) {
130
+ $ciDir = Join-Path $env:USERPROFILE ".cloud-init"
131
+ New-Item -ItemType Directory -Force -Path $ciDir | Out-Null
132
+ $yaml = @"
133
+ #cloud-config
134
+ users:
135
+ - name: $user
136
+ groups: [sudo]
137
+ shell: /bin/bash
138
+ sudo: ['ALL=(ALL) NOPASSWD:ALL']
139
+ lock_passwd: true
140
+ write_files:
141
+ - path: /etc/wsl.conf
142
+ content: |
143
+ [user]
144
+ default=$user
145
+ [boot]
146
+ systemd=true
147
+ "@
148
+ $yaml = $yaml -replace "`r`n", "`n" # cloud-init wants LF + UTF-8 (no BOM)
149
+ $enc = New-Object System.Text.UTF8Encoding($false)
150
+ foreach ($name in $DISTRO_CANDIDATES) {
151
+ $path = Join-Path $ciDir "$name.user-data"
152
+ [System.IO.File]::WriteAllText($path, $yaml, $enc)
153
+ }
154
+ Write-Ok "cloud-init user-data placed in $ciDir"
155
+ }
156
+
157
+ # Update the WSL engine + online distro catalog. On Windows 10 the inbox WSL
158
+ # catalog is often stale and does not know Ubuntu-24.04; `wsl --update` pulls
159
+ # the Store WSL which refreshes the catalog. Failures are non-fatal (warn).
160
+ function Update-WslEngine {
161
+ Write-Step "Updating WSL engine (wsl --update)"
162
+ try {
163
+ wsl.exe --update *> $null
164
+ if ($LASTEXITCODE -ne 0) { Write-Warn "wsl --update returned $LASTEXITCODE (continuing)" }
165
+ else { Write-Ok "WSL engine updated" }
166
+ } catch { Write-Warn "wsl --update failed (continuing)" }
167
+ }
168
+
169
+ # Install a distro without launching it. Tries candidate names and a no-flag
170
+ # fallback (older WSL may not support --no-launch). Returns the installed name
171
+ # or throws with actionable guidance.
172
+ function Install-Distro {
173
+ $existing = Resolve-InstalledUbuntu
174
+ if ($existing) { Write-Ok "Ubuntu distro already installed: $existing"; return $existing }
175
+
176
+ Update-WslEngine
177
+
178
+ foreach ($name in $DISTRO_CANDIDATES) {
179
+ Write-Step "Installing distro: $name"
180
+ # 1) modern: --no-launch
181
+ wsl.exe --install -d $name --no-launch *> $null
182
+ if ($LASTEXITCODE -eq 0 -and (Get-WslDistros) -contains $name) { return $name }
183
+ # 2) older WSL may reject --no-launch; retry without it (still no OOBE
184
+ # because cloud-init is already staged).
185
+ wsl.exe --install -d $name *> $null
186
+ if ($LASTEXITCODE -eq 0 -and (Get-WslDistros) -contains $name) { return $name }
187
+ Write-Warn "Could not install '$name', trying next candidate..."
188
+ }
189
+
190
+ # If we get here every candidate failed.
191
+ Write-Err "Failed to install a WSL Ubuntu distro automatically."
192
+ Write-Host ""
193
+ Write-Host "Please run these in an ADMINISTRATOR PowerShell, then re-run this installer:" -ForegroundColor Yellow
194
+ Write-Host " wsl --update" -ForegroundColor Yellow
195
+ Write-Host " wsl --list --online # confirm an Ubuntu name is listed" -ForegroundColor Yellow
196
+ Write-Host " wsl --install -d Ubuntu-24.04 # or the Ubuntu name shown above" -ForegroundColor Yellow
197
+ Write-Host ""
198
+ Write-Host "Note: Windows 10 needs version 21H2+ and an up-to-date WSL." -ForegroundColor Yellow
199
+ throw "distro install failed"
200
+ }
201
+
202
+ # cloud-init path: wait for completion, return $true if the user got created.
203
+ function Invoke-CloudInitProvision($distro, $user) {
204
+ Write-Step "Creating first Ubuntu user unattended (cloud-init)"
205
+ try { wsl.exe -d $distro -u root -- cloud-init status --wait *> $null }
206
+ catch { Write-Warn "cloud-init status --wait failed (cloud-init may be unsupported)"; return $false }
207
+ return (Test-WslUserReady $distro $user)
208
+ }
209
+
210
+ # Fallback: launcher `install --root` then useradd. Launcher exe name depends
211
+ # on the distro (ubuntu2404.exe etc.); resolve it from the distro name.
212
+ function Invoke-RootInstallProvision($distro, $user) {
213
+ Write-Step "Creating first Ubuntu user unattended (root-install fallback)"
214
+ $launcher = ($distro.ToLower() -replace '[^a-z0-9]', '') + ".exe" # Ubuntu-24.04 -> ubuntu2404.exe
215
+ try { & $launcher install --root *> $null }
216
+ catch { Write-Warn "$launcher install --root failed (continuing to useradd)" }
217
+ $script = @"
218
+ set -e
219
+ useradd -m -s /bin/bash -G sudo '$user' 2>/dev/null || true
220
+ passwd -d '$user'
221
+ echo '$user ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-hub-$user
222
+ chmod 0440 /etc/sudoers.d/90-hub-$user
223
+ printf '[user]\ndefault=$user\n[boot]\nsystemd=true\n' > /etc/wsl.conf
224
+ "@
225
+ $script = $script -replace "`r`n", "`n"
226
+ wsl.exe -d $distro -u root -- bash -lc $script
227
+ wsl.exe --shutdown
228
+ return (Test-WslUserReady $distro $user)
229
+ }
230
+
231
+ # Ensure systemd=true in the distro's /etc/wsl.conf (idempotent).
232
+ function Set-Systemd($distro) {
233
+ $has = (wsl.exe -d $distro -u root -- bash -lc "grep -q 'systemd=true' /etc/wsl.conf 2>/dev/null && echo yes || echo no")
234
+ if (($has -replace "`0","").Trim() -ne "yes") {
235
+ wsl.exe -d $distro -u root -- bash -lc "printf '[boot]\nsystemd=true\n' >> /etc/wsl.conf"
236
+ wsl.exe --shutdown
237
+ }
238
+ }
239
+
240
+ # Register a logon task that wakes the distro (its systemd starts hub-agent).
241
+ function Register-BootTask($distro, $user) {
242
+ Write-Step "Registering autostart on logon (Task Scheduler)"
243
+ $action = New-ScheduledTaskAction -Execute "wsl.exe" -Argument "-d $distro -u $user -- /bin/true"
244
+ $trigger = New-ScheduledTaskTrigger -AtLogOn
245
+ $set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
246
+ Register-ScheduledTask -TaskName $TASK_NAME -Action $action -Trigger $trigger -Settings $set `
247
+ -RunLevel Limited -Force | Out-Null
248
+ Write-Ok "Logon task '$TASK_NAME' registered"
249
+ }
250
+
251
+ # Run the existing install.sh inside WSL.
252
+ function Invoke-WslInstaller($distro, $user) {
253
+ Write-Step "Setting up hub-agent inside WSL (existing install.sh)"
254
+ $tok = if ($Token) { $Token } else { "" }
255
+ $inner = "curl -fsSL '$INSTALL_SH' | HUB_AGENT_TOKEN='$tok' HUB_AGENT_URL='$HubUrl' bash"
256
+ wsl.exe -d $distro -u $user -- bash -lc $inner
257
+ if ($LASTEXITCODE -ne 0) { throw "install.sh inside WSL failed (exit $LASTEXITCODE)" }
258
+ }
259
+
260
+ # =============================================================================
261
+ # main
262
+ # =============================================================================
263
+ function Main {
264
+ Write-Step "Starting hub-agent Windows setup (WSL2 method)"
265
+
266
+ if (-not (Test-IsAdmin)) {
267
+ Write-Err "Administrator privileges are required. Reopen PowerShell as Administrator."
268
+ exit 1
269
+ }
270
+
271
+ $user = Get-LinuxUserName
272
+
273
+ # --- Phase 1: install WSL2 + distro (only when none present; bridges reboot) ---
274
+ if (-not $IsResume -and -not (Resolve-InstalledUbuntu)) {
275
+ Write-Step "Installing WSL2 + Ubuntu"
276
+ Set-ResumeOnReboot # arm auto-resume first
277
+ Write-CloudInit $user # stage cloud-init before first boot
278
+ $installed = Install-Distro # robust: update + candidate names + fallbacks
279
+ Write-Ok "Distro installed: $installed"
280
+ Invoke-RebootPrompt # reboot is the user's choice
281
+ return
282
+ }
283
+
284
+ # --- after reboot, or WSL already present: resolve the real distro name ---
285
+ $distro = Resolve-InstalledUbuntu
286
+ if (-not $distro) {
287
+ # RESUME but distro not visible yet: the deployment may still be unpacking.
288
+ Write-Warn "Ubuntu distro not found yet; attempting install..."
289
+ Write-CloudInit $user
290
+ $distro = Install-Distro
291
+ }
292
+ Write-Ok "Using distro: $distro"
293
+
294
+ # --- create the first Ubuntu user unattended ---
295
+ if (-not (Test-WslUserReady $distro $user)) {
296
+ if (-not (Test-Path (Join-Path $env:USERPROFILE ".cloud-init\$distro.user-data"))) {
297
+ Write-CloudInit $user
298
+ }
299
+ $ok = Invoke-CloudInitProvision $distro $user
300
+ if (-not $ok) {
301
+ Write-Warn "cloud-init did not create the user; switching to root-install method."
302
+ $ok = Invoke-RootInstallProvision $distro $user
303
+ }
304
+ if (-not $ok) { throw "Failed to create Ubuntu user '$user' unattended" }
305
+ Write-Ok "Created Ubuntu user '$user' (no password / NOPASSWD sudo)"
306
+ } else {
307
+ Write-Ok "Ubuntu user '$user' already exists"
308
+ }
309
+
310
+ Set-Systemd $distro
311
+
312
+ # --- Phase 2: run install.sh inside WSL ---
313
+ Invoke-WslInstaller $distro $user
314
+
315
+ # --- autostart ---
316
+ Register-BootTask $distro $user
317
+
318
+ Write-Host ""
319
+ Write-Ok "Setup complete. Check the online status in the Hub UI:"
320
+ Write-Host " https://hub.cocorograph.com/user/cockpit/agents" -ForegroundColor Cyan
321
+ Write-Warn "Keep your working repos INSIDE WSL (e.g. /home/$user/...)."
322
+ Write-Warn "Windows drives (/mnt/c/...) have unreliable file watching."
323
+ }
324
+
325
+ Main