@cocorograph/hub-agent 0.5.14 → 0.5.15

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.5.14",
3
+ "version": "0.5.15",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -9,7 +9,7 @@
9
9
  * テンプレ内の __HUB_AGENT_BIN__ / __HOME__ / __PATH__ を実行時に置換する。
10
10
  * 仕様書: ナレッジ/インフラ/cockpit-hub-hosted-integration-spec (id=6080)
11
11
  */
12
- import { promises as fs } from "node:fs"
12
+ import { promises as fs, existsSync, realpathSync } from "node:fs"
13
13
  import os from "node:os"
14
14
  import path from "node:path"
15
15
  import { spawnSync } from "node:child_process"
@@ -63,10 +63,59 @@ function run(cmd, args, opts = {}) {
63
63
  }
64
64
  }
65
65
 
66
+ /**
67
+ * `which hub-agent` の結果を、launchd / systemd から長期間 exec 可能な
68
+ * 安定パスに正規化する。pure 関数。`opts` で fs を差し替えてテスト可能。
69
+ *
70
+ * 背景: fnm 利用者の `which hub-agent` は現在のシェル専用の一時パス
71
+ * (`~/.local/state/fnm_multishells/<PID>_<ts>/bin/hub-agent`) を返す。
72
+ * それを plist に書き込むと、install-service を叩いたシェル終了後に
73
+ * symlink が消えて launchd が exec できず、agent が無音で停止する事故が
74
+ * 発生する (2026-05-19, シェル PID 4683 で install したまま放置されて
75
+ * 検知が遅れた)。
76
+ *
77
+ * 優先順:
78
+ * 1. fnm default alias `~/.local/share/fnm/aliases/default/bin/hub-agent`
79
+ * が存在すればそれを返す (fnm 利用者にとって最も安定)
80
+ * 2. whichPath が `/fnm_multishells/` を含むなら realpath で
81
+ * `~/.local/share/fnm/node-versions/vX.Y.Z/.../bin/hub-agent.mjs`
82
+ * に展開 (node アップグレード前提でも install-service 再実行で済む)
83
+ * 3. それ以外 (brew / nvm / 通常 PATH) は whichPath をそのまま返す
84
+ */
85
+ function normalizeBinPath(whichPath, opts = {}) {
86
+ const home = opts.home ?? os.homedir()
87
+ const fileExists = opts.fileExists ?? existsSync
88
+ const resolveReal = opts.realpath ?? realpathSync
89
+
90
+ const fnmDefaultBin = path.join(
91
+ home,
92
+ ".local",
93
+ "share",
94
+ "fnm",
95
+ "aliases",
96
+ "default",
97
+ "bin",
98
+ "hub-agent",
99
+ )
100
+ if (fileExists(fnmDefaultBin)) return fnmDefaultBin
101
+
102
+ if (!whichPath) return null
103
+ if (whichPath.includes("/fnm_multishells/")) {
104
+ try {
105
+ return resolveReal(whichPath)
106
+ } catch {
107
+ // realpath 失敗時は whichPath をそのまま返す (既存挙動の保持)
108
+ }
109
+ }
110
+ return whichPath
111
+ }
112
+
66
113
  /** インストール先の hub-agent CLI のフルパスを返す。`hub-agent` が PATH にある前提。 */
67
114
  function detectHubAgentBin() {
68
115
  const r = spawnSync("/usr/bin/which", ["hub-agent"], { encoding: "utf-8" })
69
- if (r.status === 0 && r.stdout.trim()) return r.stdout.trim()
116
+ const whichPath = r.status === 0 && r.stdout ? r.stdout.trim() : null
117
+ const normalized = normalizeBinPath(whichPath)
118
+ if (normalized) return normalized
70
119
  // fallback: node $repo/bin/hub-agent.mjs
71
120
  return path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "bin", "hub-agent.mjs")
72
121
  }
@@ -179,6 +228,7 @@ export async function uninstallService() {
179
228
  export const _internal = {
180
229
  expandTemplate,
181
230
  detectHubAgentBin,
231
+ normalizeBinPath,
182
232
  macPlistPath,
183
233
  linuxUnitPath,
184
234
  repoTemplatesDir,