@cocorograph/hub-agent 0.8.68 → 0.8.70
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/package.json +1 -1
- package/scripts/install.ps1 +95 -4
- package/scripts/install.sh +18 -4
package/package.json
CHANGED
package/scripts/install.ps1
CHANGED
|
@@ -103,10 +103,49 @@ function Get-LinuxUserName {
|
|
|
103
103
|
return $u
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
# Quote a value as ONE PowerShell single-quoted literal, for text that PowerShell
|
|
107
|
+
# itself will parse later (the RunOnce command line below). PowerShell escapes a quote
|
|
108
|
+
# by doubling it: o'hare -> 'o''hare'.
|
|
109
|
+
#
|
|
110
|
+
# WARNING: this is PowerShell quoting. It is NOT bash quoting, and there is deliberately
|
|
111
|
+
# no bash equivalent in this file any more -- everything handed to wsl.exe goes as plain
|
|
112
|
+
# argv tokens instead (see Invoke-WslInstaller).
|
|
113
|
+
#
|
|
114
|
+
# Doubling the ASCII quote is NOT enough on its own. PowerShell also accepts the
|
|
115
|
+
# typographic quotes as string delimiters, so a value containing U+2019 closes the
|
|
116
|
+
# literal early and the rest of it is parsed as code. Measured 2026-09-10 on pwsh:
|
|
117
|
+
# "'" + "x<U+2019>; Write-Output INJECTED; #" + "'" -> Invoke-Expression ran INJECTED.
|
|
118
|
+
# A double quote is just as bad one level out: it ends the -Command "..." region no
|
|
119
|
+
# matter how the inner value is quoted.
|
|
120
|
+
#
|
|
121
|
+
# So this allows printable ASCII only, minus the double quote, and refuses the rest.
|
|
122
|
+
# Enumerating dangerous characters is the losing side of this; an allowlist is not.
|
|
123
|
+
# An enrollment token and a hub URL are both ASCII, so nothing legitimate is lost.
|
|
124
|
+
#
|
|
125
|
+
# WARNING: the end anchor must be \z, not $. In .NET, $ also matches immediately before a
|
|
126
|
+
# final newline, so '...*$' accepts "tok" plus a trailing LF -- and a token pasted out of
|
|
127
|
+
# a browser or a text file carries exactly that. Measured 2026-09-10 on pwsh:
|
|
128
|
+
# ("tok" + [char]10) -match '^[\x20\x21\x23-\x7e]*$' -> True (leaks through)
|
|
129
|
+
# ("tok" + [char]10) -match '^[\x20\x21\x23-\x7e]*\z' -> False (rejected)
|
|
130
|
+
# The empty string still passes, which is what a run without a token needs.
|
|
131
|
+
#
|
|
132
|
+
# WARNING: the rejection must not echo the value. It carries the enrollment token, and
|
|
133
|
+
# an exception goes to the console, to transcripts and into whatever diagnostics get
|
|
134
|
+
# shared afterwards. Name the setting, not its contents.
|
|
135
|
+
function ConvertTo-PowerShellSingleQuoted($name, $value) {
|
|
136
|
+
$s = [string]$value
|
|
137
|
+
if ($s -notmatch '^[\x20\x21\x23-\x7e]*\z') {
|
|
138
|
+
throw "$name contains a character that cannot be safely embedded in the resume command (allowed: printable ASCII except the double quote)"
|
|
139
|
+
}
|
|
140
|
+
return "'" + ($s -replace "'", "''") + "'"
|
|
141
|
+
}
|
|
142
|
+
|
|
106
143
|
# Register "re-run myself with RESUME=1 after reboot", carrying token/url.
|
|
107
144
|
function Set-ResumeOnReboot {
|
|
145
|
+
$qTok = ConvertTo-PowerShellSingleQuoted "HUB_AGENT_TOKEN" $Token
|
|
146
|
+
$qUrl = ConvertTo-PowerShellSingleQuoted "HUB_AGENT_URL" $HubUrl
|
|
108
147
|
$cmd = "powershell -NoProfile -ExecutionPolicy Bypass -Command " +
|
|
109
|
-
"`"`$env:HUB_AGENT_TOKEN
|
|
148
|
+
"`"`$env:HUB_AGENT_TOKEN=$qTok; `$env:HUB_AGENT_URL=$qUrl; " +
|
|
110
149
|
"`$env:HUB_AGENT_RESUME='1'; irm $PS1_URL | iex`""
|
|
111
150
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce" `
|
|
112
151
|
-Name $RUNONCE_NAME -Value $cmd
|
|
@@ -267,12 +306,64 @@ function Register-BootTask($distro, $user) {
|
|
|
267
306
|
}
|
|
268
307
|
|
|
269
308
|
# Run the existing install.sh inside WSL.
|
|
309
|
+
#
|
|
310
|
+
# WARNING: do not build a shell expression and hand it to wsl.exe. 0.8.69 shipped
|
|
311
|
+
# bash -lc 't=$(mktemp) || exit 1; trap ...; curl -fsSL <url> -o "$t" && ... bash "$t"'
|
|
312
|
+
# and on a real Windows 11 host install.sh never ran: curl died with
|
|
313
|
+
# `option -o: is badly used here` (2026-09-10).
|
|
314
|
+
#
|
|
315
|
+
# What is measured, and what is not -- keep these apart, the inference below is what
|
|
316
|
+
# sent the first investigation into install.sh, which had not even been downloaded:
|
|
317
|
+
# - measured: curl 8.5.0 emits that message only when -o gets an empty *quoted*
|
|
318
|
+
# argument. So by the time bash ran curl, $t was empty.
|
|
319
|
+
# - measured: in the same failing run, the other `bash -lc` calls in this file
|
|
320
|
+
# (Set-Systemd, which contains && and ||) went through without error. The only
|
|
321
|
+
# thing that set the broken call apart was DOUBLE QUOTES inside the argument.
|
|
322
|
+
# - INFERRED, not confirmed: that the assignment got separated from the curl.
|
|
323
|
+
# - NOT measured: which layer re-quoted it. Windows PowerShell 5.1 and wsl.exe both
|
|
324
|
+
# re-quote native arguments and neither can be reproduced on a non-Windows test bed.
|
|
325
|
+
#
|
|
326
|
+
# So do not rely on knowing the rule -- rely on not needing one. One wsl.exe call per
|
|
327
|
+
# step, every argument a plain token, $LASTEXITCODE checked after each. Splitting the
|
|
328
|
+
# steps is also what makes failures propagate at all: the original `curl ... | bash`
|
|
329
|
+
# reported the pipeline's last exit status, so a failed download still left
|
|
330
|
+
# $LASTEXITCODE at 0 and every broken run printed "[OK] Setup complete" (2026-09-09).
|
|
331
|
+
#
|
|
332
|
+
# Each throw names its own step, so a download failure cannot be read as install.sh
|
|
333
|
+
# failing. That confusion cost a release.
|
|
270
334
|
function Invoke-WslInstaller($distro, $user) {
|
|
271
335
|
Write-Step "Setting up hub-agent inside WSL (existing install.sh)"
|
|
272
336
|
$tok = if ($Token) { $Token } else { "" }
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
337
|
+
|
|
338
|
+
# Ask the distro where HOME is instead of assuming /home/<user>. Download into HOME,
|
|
339
|
+
# not /tmp: a name under a world-writable directory can be pre-created by any other
|
|
340
|
+
# local account, and we are about to execute whatever lands there.
|
|
341
|
+
$wslHome = (wsl.exe -d $distro -u $user -- printenv HOME | Out-String)
|
|
342
|
+
if ($LASTEXITCODE -ne 0) { throw "could not resolve HOME for '$user' inside WSL (exit $LASTEXITCODE)" }
|
|
343
|
+
$wslHome = ($wslHome -replace "`r", "").Trim()
|
|
344
|
+
if (-not $wslHome) { throw "could not resolve HOME for '$user' inside WSL (empty)" }
|
|
345
|
+
|
|
346
|
+
# One name per run. With a fixed name, two concurrent runs corrupt each other: one
|
|
347
|
+
# curl truncates the file the other is already executing (bash reads a script as it
|
|
348
|
+
# goes, so the loser runs half an installer), and whichever finishes first deletes
|
|
349
|
+
# the other's script. HOME is not world-writable, so the only possible collision is
|
|
350
|
+
# with ourselves -- a GUID settles it without another round trip for mktemp.
|
|
351
|
+
$script = "$wslHome/.hub-agent-install." + [guid]::NewGuid().ToString("N") + ".sh"
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
wsl.exe -d $distro -u $user -- curl -fsSL $INSTALL_SH -o $script
|
|
355
|
+
if ($LASTEXITCODE -ne 0) { throw "downloading install.sh inside WSL failed (exit $LASTEXITCODE)" }
|
|
356
|
+
|
|
357
|
+
# `env` carries the settings as plain argv tokens. Interpolating them into a shell
|
|
358
|
+
# string is what broke above, and argv also keeps the token out of shell history.
|
|
359
|
+
wsl.exe -d $distro -u $user -- env "HUB_AGENT_TOKEN=$tok" "HUB_AGENT_URL=$HubUrl" bash $script
|
|
360
|
+
if ($LASTEXITCODE -ne 0) { throw "install.sh inside WSL failed (exit $LASTEXITCODE)" }
|
|
361
|
+
} finally {
|
|
362
|
+
# Inside try so a partial download is reclaimed too. Best effort: a run killed from
|
|
363
|
+
# outside PowerShell leaves one .hub-agent-install.<guid>.sh in the user's own home.
|
|
364
|
+
# Never delete by wildcard here -- that would collect a concurrent run's script.
|
|
365
|
+
wsl.exe -d $distro -u $user -- rm -f $script | Out-Null
|
|
366
|
+
}
|
|
276
367
|
}
|
|
277
368
|
|
|
278
369
|
# =============================================================================
|
package/scripts/install.sh
CHANGED
|
@@ -1443,12 +1443,26 @@ main() {
|
|
|
1443
1443
|
}
|
|
1444
1444
|
|
|
1445
1445
|
# このファイルが source されたか(実行されたか)を判定する。
|
|
1446
|
-
# - `bash -c "$(curl ...)"` / `curl ... | bash`
|
|
1447
|
-
# - `bash install.sh` / `./install.sh`
|
|
1448
|
-
# - `source install.sh`(test/install-sh.test.mjs
|
|
1446
|
+
# - `bash -c "$(curl ...)"` / `curl ... | bash` / `bash -s` : 実行
|
|
1447
|
+
# - `bash install.sh` / `./install.sh` : 実行
|
|
1448
|
+
# - `source install.sh`(test/install-sh.test.mjs) : source
|
|
1449
|
+
#
|
|
1450
|
+
# ⚠️ `BASH_SOURCE[0]` と `$0` の比較で判定してはいけない。関数の中で見る
|
|
1451
|
+
# `BASH_SOURCE[0]` は「その関数が定義されたファイル」を指すが、標準入力から読ませた
|
|
1452
|
+
# ときの値は bash の版で割れる: bash 5.2 系は文字列 `main`、bash 3.2 / 5.3 は空。
|
|
1453
|
+
# 5.2 系では `main` != `bash` が成立して **source と誤判定**され、
|
|
1454
|
+
# `curl ... | bash` のワンライナーが main を呼ばずに無言・exit 0 で終わっていた
|
|
1455
|
+
# (2026-09-09 実測。Ubuntu 24.04 の 5.2.21 / Debian trixie の 5.2.37 で再現し、
|
|
1456
|
+
# macOS の 3.2.57 / 5.3.15 では再現しない。開発機だけで確かめると気づけない)。
|
|
1457
|
+
#
|
|
1458
|
+
# FUNCNAME はコールスタックで、bash は source したファイルのトップレベルを `source`
|
|
1459
|
+
# という枠として積む。したがってこの関数をトップレベルから呼ぶと、source されたときだけ
|
|
1460
|
+
# `FUNCNAME[1]` が `source` になる。3.2 / 5.2 / 5.3 のいずれでも一致する。
|
|
1461
|
+
# ⚠️ この関数は**トップレベルから直接呼ぶこと**。別の関数を挟むと `FUNCNAME[1]` が
|
|
1462
|
+
# その関数名になり、判定が壊れる。
|
|
1449
1463
|
# 環境変数で切り替えると、変数が残ったシェルや CI でワンライナーが無言の no-op になるため使わない。
|
|
1450
1464
|
_install_sh_is_sourced() {
|
|
1451
|
-
[[
|
|
1465
|
+
[[ "${FUNCNAME[1]:-}" == "source" ]]
|
|
1452
1466
|
}
|
|
1453
1467
|
|
|
1454
1468
|
if ! _install_sh_is_sourced; then
|