@cocorograph/hub-agent 0.5.30 → 0.5.31
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/bin/hub-agent.mjs +9 -1
- package/package.json +1 -1
- package/src/hub-bundle.mjs +90 -2
package/bin/hub-agent.mjs
CHANGED
|
@@ -95,7 +95,15 @@ program
|
|
|
95
95
|
agentId: cfg.agent_id,
|
|
96
96
|
agentToken: cfg.agent_token,
|
|
97
97
|
})
|
|
98
|
-
|
|
98
|
+
const mcpSummary = r.mcp
|
|
99
|
+
? ` mcp_added=${r.mcp.added.length}${r.mcp.skipped.length > 0 ? ` mcp_skipped=${r.mcp.skipped.length}` : ""}`
|
|
100
|
+
: ""
|
|
101
|
+
console.log(
|
|
102
|
+
`hub bundle synced: version=${r.version} written=${r.written.length} skipped_same=${r.skipped_same.length}${mcpSummary}`,
|
|
103
|
+
)
|
|
104
|
+
if (r.mcp && r.mcp.added.length > 0) {
|
|
105
|
+
console.log(` mcp servers added: ${r.mcp.added.join(", ")}`)
|
|
106
|
+
}
|
|
99
107
|
} catch (err) {
|
|
100
108
|
console.error(`sync-bundle failed: ${err.message}`)
|
|
101
109
|
process.exit(1)
|
package/package.json
CHANGED
package/src/hub-bundle.mjs
CHANGED
|
@@ -257,7 +257,89 @@ function runOnce(argv, { logger } = {}) {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
/**
|
|
260
|
-
*
|
|
260
|
+
* Bundle response の `mcp_servers` を Claude Code CLI に登録する。
|
|
261
|
+
*
|
|
262
|
+
* Hub backend は user の権限 (is_staff 等) でフィルタした後のリストを返す
|
|
263
|
+
* ため、ここでは届いた全 server を `claude mcp add` する。staff 専用 URL は
|
|
264
|
+
* そもそも一般ユーザーのレスポンスに含まれないので、ここで二重判定する
|
|
265
|
+
* 必要は無い (= server URL を agent 側ロジックに埋め込まない設計)。
|
|
266
|
+
*
|
|
267
|
+
* `claude mcp add --transport <transport> --scope <scope> <name> <url>` を
|
|
268
|
+
* 冪等に呼ぶ。既に同名 server が登録されていれば CLI が「Updated server」
|
|
269
|
+
* メッセージで上書きする (= 何度実行しても安全)。
|
|
270
|
+
*
|
|
271
|
+
* `claude` CLI が PATH に居ない / 未インストール環境ではスキップ。
|
|
272
|
+
* (Cockpit 用途では install.sh が事前に claude をインストールしている
|
|
273
|
+
* 想定だが、独自運用環境への配慮で fail-soft にする)
|
|
274
|
+
*/
|
|
275
|
+
export async function applyMcpServers(bundle, { logger } = {}) {
|
|
276
|
+
const servers = Array.isArray(bundle?.mcp_servers) ? bundle.mcp_servers : []
|
|
277
|
+
if (servers.length === 0) return { added: [], skipped: [] }
|
|
278
|
+
|
|
279
|
+
// `claude` CLI の存在確認 (which / where 相当)。なければ no-op。
|
|
280
|
+
const claudeBin = await resolveClaudeBin()
|
|
281
|
+
if (!claudeBin) {
|
|
282
|
+
logger?.info("claude CLI が PATH に無いため MCP server 自動登録をスキップ")
|
|
283
|
+
return { added: [], skipped: servers.map((s) => s.name) }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const added = []
|
|
287
|
+
const skipped = []
|
|
288
|
+
for (const s of servers) {
|
|
289
|
+
if (!s?.name || !s?.url) {
|
|
290
|
+
skipped.push(s?.name || "<unnamed>")
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
const transport = s.transport || "http"
|
|
294
|
+
const scope = s.scope || "user"
|
|
295
|
+
const argv = [
|
|
296
|
+
claudeBin,
|
|
297
|
+
"mcp",
|
|
298
|
+
"add",
|
|
299
|
+
"--transport",
|
|
300
|
+
transport,
|
|
301
|
+
"--scope",
|
|
302
|
+
scope,
|
|
303
|
+
s.name,
|
|
304
|
+
s.url,
|
|
305
|
+
]
|
|
306
|
+
try {
|
|
307
|
+
await runOnce(argv, { logger })
|
|
308
|
+
added.push(s.name)
|
|
309
|
+
} catch (err) {
|
|
310
|
+
// 既存のため fail することはほぼ無い (CLI は冪等)。ログだけ残して継続。
|
|
311
|
+
logger?.warn({ name: s.name, err: err.message }, "claude mcp add failed")
|
|
312
|
+
skipped.push(s.name)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { added, skipped }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* `claude` バイナリの絶対パスを返す。なければ null。
|
|
320
|
+
* PATH を辿る (`which` / `where`) ことで fnm / nvm / homebrew / volta 等の
|
|
321
|
+
* バージョンマネージャ配下にあるものも拾う。
|
|
322
|
+
*/
|
|
323
|
+
async function resolveClaudeBin() {
|
|
324
|
+
const isWindows = process.platform === "win32"
|
|
325
|
+
const cmd = isWindows ? "where" : "which"
|
|
326
|
+
return new Promise((resolve) => {
|
|
327
|
+
const child = spawn(cmd, ["claude"], { stdio: ["ignore", "pipe", "ignore"] })
|
|
328
|
+
let out = ""
|
|
329
|
+
child.stdout?.on("data", (chunk) => {
|
|
330
|
+
out += chunk.toString("utf-8")
|
|
331
|
+
})
|
|
332
|
+
child.on("exit", (code) => {
|
|
333
|
+
if (code !== 0) return resolve(null)
|
|
334
|
+
const first = out.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0]
|
|
335
|
+
resolve(first || null)
|
|
336
|
+
})
|
|
337
|
+
child.on("error", () => resolve(null))
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* fetch + apply + post_install + MCP server 自動登録をまとめて実行。
|
|
261
343
|
*/
|
|
262
344
|
export async function syncBundle({ hubUrl, agentId, agentToken, logger, fetchImpl } = {}) {
|
|
263
345
|
const bundle = await fetchBundle({ hubUrl, agentId, agentToken, fetchImpl })
|
|
@@ -267,5 +349,11 @@ export async function syncBundle({ hubUrl, agentId, agentToken, logger, fetchImp
|
|
|
267
349
|
} catch (err) {
|
|
268
350
|
logger?.warn({ err: err.message }, "post_install partially failed (continuing)")
|
|
269
351
|
}
|
|
270
|
-
|
|
352
|
+
let mcp = { added: [], skipped: [] }
|
|
353
|
+
try {
|
|
354
|
+
mcp = await applyMcpServers(bundle, { logger })
|
|
355
|
+
} catch (err) {
|
|
356
|
+
logger?.warn({ err: err.message }, "applyMcpServers failed (continuing)")
|
|
357
|
+
}
|
|
358
|
+
return { version: bundle.version, ...result, mcp }
|
|
271
359
|
}
|