@cocorograph/hub-agent 0.5.16 → 0.5.18
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/src/main.mjs +86 -2
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import path from "node:path"
|
|
|
17
17
|
|
|
18
18
|
import pino from "pino"
|
|
19
19
|
|
|
20
|
-
import { readConfig } from "./config.mjs"
|
|
20
|
+
import { readConfig, writeConfig } from "./config.mjs"
|
|
21
21
|
import { loadPlugins, runHookBroadcast, runHookChain } from "./plugin-loader.mjs"
|
|
22
22
|
import { WsClient } from "./ws-client.mjs"
|
|
23
23
|
import { PtyBridge } from "./pty-bridge.mjs"
|
|
@@ -169,6 +169,40 @@ export async function startDaemon({ version, ptyModule } = {}) {
|
|
|
169
169
|
const SESSION_EVENTS_DIR =
|
|
170
170
|
process.env.COCKPIT_SESSION_EVENTS_DIR || "/tmp/cockpit_session_events"
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* `/tmp/cockpit_session_events/` 全 .json を読んで session_name → {event, at}
|
|
174
|
+
* の Map を返す (Phase 4: tmux.list_sessions レスポンスに含めて配信する用)。
|
|
175
|
+
*
|
|
176
|
+
* fs.watch 由来の `session.event` push はファイル変更時のみのため、frontend が
|
|
177
|
+
* 新規マウント (ハードリロード等) すると過去 event を取得できず CockpitStatusDot
|
|
178
|
+
* が全グレー表示になる問題があった。これを list 同梱で復元する。
|
|
179
|
+
*/
|
|
180
|
+
async function readAllSessionEvents() {
|
|
181
|
+
const out = new Map()
|
|
182
|
+
let files
|
|
183
|
+
try {
|
|
184
|
+
files = await readdir(SESSION_EVENTS_DIR)
|
|
185
|
+
} catch {
|
|
186
|
+
return out
|
|
187
|
+
}
|
|
188
|
+
for (const f of files) {
|
|
189
|
+
if (!f.endsWith(".json")) continue
|
|
190
|
+
const sessionName = f.slice(0, -5)
|
|
191
|
+
try {
|
|
192
|
+
const text = await readFile(path.join(SESSION_EVENTS_DIR, f), "utf-8")
|
|
193
|
+
const data = JSON.parse(text)
|
|
194
|
+
if (!data || typeof data.event !== "string") continue
|
|
195
|
+
out.set(sessionName, {
|
|
196
|
+
event: data.event,
|
|
197
|
+
at: typeof data.at === "number" ? data.at : Date.now(),
|
|
198
|
+
})
|
|
199
|
+
} catch {
|
|
200
|
+
// 一時欠如 / parse 失敗は無視
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return out
|
|
204
|
+
}
|
|
205
|
+
|
|
172
206
|
/**
|
|
173
207
|
* `/tmp/cockpit_session_events/<tmux_session>.json` を fs.watch して
|
|
174
208
|
* UserPromptSubmit / Stop の event を `session.event` 型で Hub に push する。
|
|
@@ -302,15 +336,56 @@ function startStateLoop({ client, plugins, logger, intervalMs }) {
|
|
|
302
336
|
* `pty.*` は PtyBridge にそのまま流す。`tmux.exec` は Sprint E で tmux 抽象に
|
|
303
337
|
* 渡す予定なので、現状は plugin hook chain だけ走らせて log に残す。
|
|
304
338
|
*/
|
|
339
|
+
/**
|
|
340
|
+
* Hub から `ready` / `ack` で配信された Claude CLI 設定を agent.json と
|
|
341
|
+
* in-memory config に反映する (Phase 3 live-sync)。
|
|
342
|
+
*
|
|
343
|
+
* 旧設計 (Phase 2) では enrollment 時のみ取得していたため、cockpit 管理画面で
|
|
344
|
+
* 設定を変更しても既存 agent には再 enroll しない限り反映されなかった。Phase 3 で
|
|
345
|
+
* 全 WS 接続/heartbeat 毎に最新値が同期されるようにした。
|
|
346
|
+
*
|
|
347
|
+
* 値が変化していない場合は writeConfig を呼ばない (書込負荷軽減)。
|
|
348
|
+
* 失敗しても agent は止めず log だけ出して継続。
|
|
349
|
+
*/
|
|
350
|
+
export async function maybeSyncClaudeSettings(msg, ctx) {
|
|
351
|
+
if (typeof msg?.claude_model !== "string" && typeof msg?.claude_permission_mode !== "string") {
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
const nextModel = typeof msg.claude_model === "string" ? msg.claude_model : ctx.config.claude_model || ""
|
|
355
|
+
const nextMode = typeof msg.claude_permission_mode === "string" ? msg.claude_permission_mode : ctx.config.claude_permission_mode || ""
|
|
356
|
+
const prevModel = ctx.config.claude_model || ""
|
|
357
|
+
const prevMode = ctx.config.claude_permission_mode || ""
|
|
358
|
+
if (nextModel === prevModel && nextMode === prevMode) return
|
|
359
|
+
ctx.config.claude_model = nextModel
|
|
360
|
+
ctx.config.claude_permission_mode = nextMode
|
|
361
|
+
try {
|
|
362
|
+
await writeConfig({
|
|
363
|
+
agent_id: ctx.config.agent_id,
|
|
364
|
+
agent_token: ctx.config.agent_token,
|
|
365
|
+
hub_url: ctx.config.hub_url,
|
|
366
|
+
claude_model: nextModel,
|
|
367
|
+
claude_permission_mode: nextMode,
|
|
368
|
+
})
|
|
369
|
+
ctx.logger.info(
|
|
370
|
+
{ claude_model: nextModel, claude_permission_mode: nextMode },
|
|
371
|
+
"claude settings synced from hub",
|
|
372
|
+
)
|
|
373
|
+
} catch (err) {
|
|
374
|
+
ctx.logger.warn({ err: err.message }, "failed to persist claude settings to agent.json")
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
305
378
|
async function dispatch(msg, ctx) {
|
|
306
379
|
const t = msg?.type || ""
|
|
307
380
|
try {
|
|
308
381
|
switch (t) {
|
|
309
382
|
case "ready":
|
|
310
383
|
ctx.logger.info({ msg }, "hub ready")
|
|
384
|
+
await maybeSyncClaudeSettings(msg, ctx)
|
|
311
385
|
return
|
|
312
386
|
case "ack":
|
|
313
387
|
ctx.logger.debug({ echo: msg.echo }, "hub ack")
|
|
388
|
+
await maybeSyncClaudeSettings(msg, ctx)
|
|
314
389
|
return
|
|
315
390
|
case "error":
|
|
316
391
|
ctx.logger.warn({ msg }, "hub error")
|
|
@@ -396,10 +471,19 @@ async function dispatch(msg, ctx) {
|
|
|
396
471
|
case "tmux.list_sessions": {
|
|
397
472
|
try {
|
|
398
473
|
const sessions = await listTmuxSessions({ plugins: ctx.plugins, logger: ctx.logger })
|
|
474
|
+
// 各 session の最新 hook event を /tmp/cockpit_session_events/ から読んで
|
|
475
|
+
// レスポンスに含める (Phase 4: ハードリロード時の lastEvent 復元用)。
|
|
476
|
+
// fs.watch 由来の session.event push は揮発性のため、frontend が新規
|
|
477
|
+
// マウントすると過去 event を取れず全グレー表示になる事象を解消する。
|
|
478
|
+
const lastEventByName = await readAllSessionEvents()
|
|
479
|
+
const enriched = sessions.map((s) => ({
|
|
480
|
+
...s,
|
|
481
|
+
last_event: lastEventByName.get(s.name) || null,
|
|
482
|
+
}))
|
|
399
483
|
ctx.client.send({
|
|
400
484
|
type: "tmux.sessions",
|
|
401
485
|
request_id: msg.request_id,
|
|
402
|
-
sessions,
|
|
486
|
+
sessions: enriched,
|
|
403
487
|
})
|
|
404
488
|
} catch (err) {
|
|
405
489
|
ctx.client.send({
|