@cocorograph/hub-agent 0.5.24 → 0.5.25

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/install.sh +182 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.5.24",
3
+ "version": "0.5.25",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -214,6 +214,183 @@ ensure_npm_user_prefix() {
214
214
  done
215
215
  }
216
216
 
217
+ # =============================================================================
218
+ # Node TLS 環境の自動修復
219
+ #
220
+ # 背景: npm install で `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` が出る環境がある。
221
+ # 原因は「node は OS の信頼ストアを使わず、自分にコンパイル時に焼き込まれた
222
+ # CA リストだけで TLS 検証する」設計にある:
223
+ #
224
+ # - curl は macOS Keychain (or /etc/ssl/certs) を使う → 通る
225
+ # - node はバンドル CA のみ → 通らない
226
+ #
227
+ # テナント環境では以下のいずれかで MITM 的な証明書差し替えが起きうる:
228
+ # - ウィルス対策ソフト (Sophos / Trend Micro / Norton / Symantec / Kaspersky 等)
229
+ # - 企業の SSL インスペクション proxy (ZScaler / Cloudflare WARP for Teams 等)
230
+ # - VPN クライアントによるトラフィック検査
231
+ # - 広告ブロッカー (NextDNS / AdGuard / 1.1.1.1 等)
232
+ # - 親会社配布のセキュリティアプリ
233
+ #
234
+ # どれもユーザー操作なしには検出しにくく、ユーザー自身も気づいていないことが多い。
235
+ #
236
+ # 対処: ユーザーが既に OS で信頼している CA バンドルを node にも渡せば、curl と
237
+ # node の信頼ストアの乖離が解消する。`NODE_EXTRA_CA_CERTS` 環境変数を使えば
238
+ # node のバンドル CA に「追加で」信頼する証明書を渡せる (バンドル CA を置き換える
239
+ # わけではないので、通常環境への副作用はない)。
240
+ #
241
+ # 1. pre-flight check で node の TLS が通るかテスト
242
+ # 2. 失敗時のみ、OS の信頼ストアを PEM に書き出して NODE_EXTRA_CA_CERTS にセット
243
+ # 3. シェル profile (.zprofile / .bash_profile) にも追記して永続化
244
+ # 4. 再テスト → ダメなら明確な日本語エラーガイドで終了
245
+ # =============================================================================
246
+
247
+ # node 側 TLS で npm registry に到達できるかテスト。成功で 0、失敗で 1。
248
+ # stderr の最後の行を grep 用に echo するので、呼び出し側で原因種別を判定できる。
249
+ _test_node_tls() {
250
+ if ! present node; then
251
+ return 1
252
+ fi
253
+ # node -e で出る ERR_TLS_CERT_ALTNAME_INVALID 等の他種別エラーは別途扱う必要が
254
+ # あるため戻り値だけで判定。stdout/stderr は呼び出し側で破棄してよい。
255
+ node -e "require('https').get('https://registry.npmjs.org/', r => { process.exit(r.statusCode >= 400 ? 1 : 0); }).on('error', e => { console.error(e.code || e.message); process.exit(1); });" 2>&1
256
+ }
257
+
258
+ # OS の信頼ストア (system / login keychain or /etc/ssl/certs) を PEM に書き出す。
259
+ # 返り値: 成功なら 0 + ファイルパスを stdout に echo / 失敗なら 1。
260
+ _export_os_ca_bundle() {
261
+ local out_pem="$HOME/.hub-agent-ca.pem"
262
+ case "$(uname -s)" in
263
+ Darwin)
264
+ # System.keychain には企業配布 CA や手動追加 CA、SystemRootCertificates.keychain
265
+ # には Apple 配布の標準 root CA が入っている。両方をマージ。
266
+ # `-p` で PEM 形式、`-a` で全件出力。pemcat に近い操作。
267
+ {
268
+ security find-certificate -a -p /Library/Keychains/System.keychain 2>/dev/null || true
269
+ security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain 2>/dev/null || true
270
+ } > "$out_pem"
271
+ ;;
272
+ Linux)
273
+ # Debian/Ubuntu 系
274
+ if [[ -r /etc/ssl/certs/ca-certificates.crt ]]; then
275
+ cp /etc/ssl/certs/ca-certificates.crt "$out_pem"
276
+ # RHEL/CentOS/Fedora 系
277
+ elif [[ -r /etc/pki/tls/certs/ca-bundle.crt ]]; then
278
+ cp /etc/pki/tls/certs/ca-bundle.crt "$out_pem"
279
+ # SUSE 系
280
+ elif [[ -r /var/lib/ca-certificates/ca-bundle.pem ]]; then
281
+ cp /var/lib/ca-certificates/ca-bundle.pem "$out_pem"
282
+ else
283
+ return 1
284
+ fi
285
+ ;;
286
+ *)
287
+ return 1
288
+ ;;
289
+ esac
290
+ if [[ ! -s "$out_pem" ]]; then
291
+ rm -f "$out_pem"
292
+ return 1
293
+ fi
294
+ echo "$out_pem"
295
+ }
296
+
297
+ # シェル profile に `export NODE_EXTRA_CA_CERTS=...` を追記する。
298
+ # 既に同じパス指定の export 行があればスキップ (idempotent)。
299
+ _persist_node_extra_ca_certs() {
300
+ local ca_path="$1"
301
+ local snippet="export NODE_EXTRA_CA_CERTS=\"$ca_path\""
302
+ local marker="# >>> hub-agent: NODE_EXTRA_CA_CERTS (TLS fallback) >>>"
303
+ local end_marker="# <<< hub-agent: NODE_EXTRA_CA_CERTS <<<"
304
+ local target
305
+ for target in "$HOME/.zprofile" "$HOME/.bash_profile"; do
306
+ [[ -e "$target" ]] || touch "$target"
307
+ if grep -Fq "$snippet" "$target" 2>/dev/null; then
308
+ continue
309
+ fi
310
+ {
311
+ printf '\n%s\n' "$marker"
312
+ printf '%s\n' "$snippet"
313
+ printf '%s\n' "$end_marker"
314
+ } >> "$target"
315
+ color_ok "$target に NODE_EXTRA_CA_CERTS を追記"
316
+ done
317
+ }
318
+
319
+ # 「UNABLE_TO_GET_ISSUER_CERT_LOCALLY が出た時に何を確認すべきか」のガイド表示。
320
+ # フォールバックも効かなかった最終手段ケース用。
321
+ _print_tls_failure_guidance() {
322
+ # ヘッダーだけ printf で色付け。本文は cat <<EOF で複数行を読みやすく出す。
323
+ printf '\n\033[1;31m✗ node の TLS 検証が修復できませんでした。\033[0m\n'
324
+ cat <<EOF
325
+
326
+ curl は通るのに node だけ落ちる場合、お使いの Mac/PC に
327
+ HTTPS 通信を検査しているソフトが入っている可能性が高いです。
328
+ 典型例:
329
+ - ウィルス対策ソフト (Sophos / Norton / Trend Micro / Symantec 等)
330
+ - 親会社配布のセキュリティアプリ
331
+ - SSL インスペクション機能つきの VPN クライアント
332
+ - 広告ブロッカー (NextDNS / 1.1.1.1 for Families / AdGuard 等)
333
+
334
+ 以下のコマンドで「実際の証明書発行者」が見えます。
335
+ ここに表示される \`issuer\` が \`Let's Encrypt\` や \`DigiCert\` ではなく、
336
+ 特定のソフト名 (ZScaler / Cocorograph / 製品名 等) なら、それが原因です:
337
+
338
+ node -e 'const t=require("tls");const s=t.connect(443,"registry.npmjs.org",{servername:"registry.npmjs.org",rejectUnauthorized:false},()=>{let c=s.getPeerCertificate(true);while(c&&Object.keys(c).length){console.log("issuer:",JSON.stringify(c.issuer));if(!c.issuerCertificate||c.issuerCertificate===c)break;c=c.issuerCertificate;}s.end();});'
339
+
340
+ 暫定的に install を進めたい場合(自分の環境を信頼している前提):
341
+
342
+ npm install -g $PACKAGE_NAME --strict-ssl=false
343
+ npm install -g $CLAUDE_CODE_PACKAGE --strict-ssl=false
344
+ hub-agent enroll <token> --hub-url <hub-url>
345
+ hub-agent install-service
346
+
347
+ 詳細サポートは Hub の cockpit チャネルへ。
348
+
349
+ EOF
350
+ }
351
+
352
+ # pre-flight TLS 検査 + 自動 fallback の本体。
353
+ # main() から ensure_npm_user_prefix の直後で呼ぶ想定。
354
+ ensure_node_tls_works() {
355
+ # Linux の中には node を持たない経路 (本スクリプトより前に node が入る) もあるので
356
+ # node が無ければスキップ (後段の ensure_global_install で別エラーになる)。
357
+ present node || return 0
358
+
359
+ color_step "node の TLS 検証を pre-flight check"
360
+
361
+ # ステップ 0: brew link 直後はシェル command hash に古い node/npm パスが残る
362
+ # ことがあるので、念のためクリアしてから検査する。
363
+ hash -r 2>/dev/null || true
364
+
365
+ if _test_node_tls >/dev/null 2>&1; then
366
+ color_ok "node TLS 検証 OK (registry.npmjs.org に到達可能)"
367
+ return 0
368
+ fi
369
+
370
+ color_warn "node TLS 検証失敗 → OS 信頼ストアから CA をエクスポートして再試行"
371
+
372
+ local ca_pem
373
+ if ! ca_pem=$(_export_os_ca_bundle); then
374
+ color_err "OS の信頼ストア (macOS Keychain / Linux ca-certificates) からの CA エクスポートに失敗"
375
+ _print_tls_failure_guidance
376
+ exit 1
377
+ fi
378
+ color_ok "$ca_pem に OS 信頼ストアの CA を書き出し ($(wc -l < "$ca_pem" | tr -d ' ') 行)"
379
+
380
+ export NODE_EXTRA_CA_CERTS="$ca_pem"
381
+ _persist_node_extra_ca_certs "$ca_pem"
382
+
383
+ # 再テスト
384
+ if _test_node_tls >/dev/null 2>&1; then
385
+ color_ok "node TLS 検証 OK (NODE_EXTRA_CA_CERTS=$ca_pem 経由)"
386
+ return 0
387
+ fi
388
+
389
+ color_err "OS 信頼ストアを渡しても node TLS 検証が通りません"
390
+ _print_tls_failure_guidance
391
+ exit 1
392
+ }
393
+
217
394
  ensure_pkg() {
218
395
  local cmd="$1"
219
396
  local brew_pkg="$2"
@@ -371,6 +548,11 @@ main() {
371
548
  ensure_pkg tmux tmux tmux
372
549
  ensure_node_version
373
550
  ensure_npm_user_prefix
551
+ # ensure_node_version 直後だと brew link でシェルの command hash がズレている
552
+ # 場合があるため、prefix 設定後にまとめて TLS pre-flight check + 自動 fallback
553
+ # を行う。失敗時はガイダンス付きで exit するので、後段の npm install で TLS
554
+ # エラーを再度浴びる経路はカットされる。
555
+ ensure_node_tls_works
374
556
  ensure_global_install
375
557
  ensure_claude_code
376
558
  do_enroll