@cocorograph/hub-agent 0.6.76 → 0.6.78

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.6.76",
3
+ "version": "0.6.78",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -512,6 +512,31 @@ _install_node_lts_linux() {
512
512
  color_ok "node $(node --version 2>/dev/null || echo '?') を導入"
513
513
  }
514
514
 
515
+ # ネイティブアドオン (node-pty 等) のビルドに必要な make / g++ / python3 を導入する。
516
+ # node-pty は prebuild が無いと node-gyp rebuild にフォールバックするが、クリーンな
517
+ # Ubuntu には build-essential が無く `gyp ERR! not found: make` で失敗する
518
+ # (WSL クリーン Ubuntu で発覚)。macOS は Xcode CLT 前提なので何もしない。
519
+ ensure_build_tools() {
520
+ [[ "$(uname)" == "Darwin" ]] && return 0
521
+ if present make && present g++; then
522
+ color_ok "build tools (make / g++) already installed"
523
+ return 0
524
+ fi
525
+ color_step "ネイティブビルド依存 (build-essential / python3) を install"
526
+ if present apt-get; then
527
+ retry 3 sudo apt-get update -y
528
+ retry 3 sudo apt-get install -y build-essential python3
529
+ elif present dnf; then
530
+ retry 3 sudo dnf groupinstall -y "Development Tools"
531
+ retry 3 sudo dnf install -y python3
532
+ elif present pacman; then
533
+ retry 3 sudo pacman -S --noconfirm base-devel python
534
+ else
535
+ color_err "対応するパッケージマネージャが見つかりません。make / g++ / python3 を手動で install してください"
536
+ exit 1
537
+ fi
538
+ }
539
+
515
540
  # Node.js のサポート範囲ポリシー判定。
516
541
  # - 未インストール → LTS を install
517
542
  # - [MIN, MAX] 範囲内 → 現状維持(ユーザーの環境を尊重)
@@ -731,7 +756,7 @@ EOF
731
756
 
732
757
  main() {
733
758
  color_step "hub-agent ワンライナーセットアップを開始"
734
- STEP_TOTAL=10
759
+ STEP_TOTAL=11
735
760
  STEP_NUM=0
736
761
 
737
762
  step_header "Homebrew"
@@ -740,6 +765,8 @@ main() {
740
765
  ensure_pkg tmux tmux tmux
741
766
  step_header "Node.js (Active LTS)"
742
767
  ensure_node_version
768
+ step_header "Build tools (native addons)"
769
+ ensure_build_tools
743
770
  step_header "npm global prefix"
744
771
  ensure_npm_user_prefix
745
772
  # ensure_node_version 直後だと brew link でシェルの command hash がズレている
@@ -128,6 +128,43 @@ function run(cmd, args, opts = {}) {
128
128
  }
129
129
  }
130
130
 
131
+ /** 現在のユーザー名。systemd の linger 操作対象に渡す。 */
132
+ function currentUsername() {
133
+ try {
134
+ return os.userInfo().username
135
+ } catch {
136
+ return process.env.USER || process.env.LOGNAME || ""
137
+ }
138
+ }
139
+
140
+ /**
141
+ * systemd の user linger を有効化する (Linux 専用・非致命)。
142
+ *
143
+ * hub-agent.service は `WantedBy=default.target` の **user サービス**
144
+ * (`systemctl --user`) のため、linger が無いとログインセッションが無い間は
145
+ * user systemd インスタンス自体が起動せず、サービスも常駐しない。とくに WSL2 の
146
+ * 自動起動は「ログオン時 Task Scheduler が `wsl ... /bin/true` で distro を一瞬
147
+ * 起こす」方式のため、linger 無しだとそのコマンド終了と同時に user セッションが
148
+ * 畳まれ hub-agent が即停止する (= セットアップ後に自動起動しない主因)。
149
+ * `loginctl enable-linger <user>` で boot 時から user インスタンスを常駐させる。
150
+ *
151
+ * loginctl が無い / systemd 不在の最小環境では失敗しうるが、その場合でも
152
+ * install 自体は続行する (warn のみ)。
153
+ */
154
+ function enableLinger(opts = {}) {
155
+ const spawn = opts.spawnSync ?? spawnSync
156
+ const user = opts.user ?? currentUsername()
157
+ const r = spawn("loginctl", ["enable-linger", user], { stdio: "ignore" })
158
+ if (r.error || r.status !== 0) {
159
+ console.warn(
160
+ `warning: loginctl enable-linger ${user} に失敗しました ` +
161
+ `(systemd/loginctl 不在の可能性)。WSL2/headless では自動起動しない場合があります。`,
162
+ )
163
+ return false
164
+ }
165
+ return true
166
+ }
167
+
131
168
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
132
169
 
133
170
  /**
@@ -282,9 +319,18 @@ export async function installService({ bin } = {}) {
282
319
  await ensureDir(path.dirname(dest))
283
320
  await fs.writeFile(dest, expanded, { mode: 0o644 })
284
321
 
322
+ // linger を先に有効化する。これが無いと user サービスは login セッションが
323
+ // 無い間 (= WSL2 のログオンタスク経由の boot や headless) に常駐しない。
324
+ const linger = enableLinger()
285
325
  run("systemctl", ["--user", "daemon-reload"])
286
326
  run("systemctl", ["--user", "enable", "--now", SYSTEMD_UNIT_NAME])
287
- return { platform: "linux", path: dest, unit: SYSTEMD_UNIT_NAME, bin: hubAgentBin }
327
+ return {
328
+ platform: "linux",
329
+ path: dest,
330
+ unit: SYSTEMD_UNIT_NAME,
331
+ bin: hubAgentBin,
332
+ linger,
333
+ }
288
334
  }
289
335
 
290
336
  throw new Error(`unsupported platform: ${process.platform}`)
@@ -348,6 +394,8 @@ export async function uninstallService() {
348
394
  spawnSync("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT_NAME], {
349
395
  stdio: "ignore",
350
396
  })
397
+ // install 時に有効化した linger を戻す (非致命)。
398
+ spawnSync("loginctl", ["disable-linger", currentUsername()], { stdio: "ignore" })
351
399
  const dest = linuxUnitPath()
352
400
  try {
353
401
  await fs.unlink(dest)
@@ -370,4 +418,6 @@ export const _internal = {
370
418
  repoTemplatesDir,
371
419
  waitUntilBootedOut,
372
420
  bootstrapWithRetry,
421
+ currentUsername,
422
+ enableLinger,
373
423
  }