@cocorograph/hub-agent 0.5.3 → 0.5.4
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 +28 -1
- package/src/tmux.mjs +23 -4
- package/src/ws-client.mjs +4 -1
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 仕様書: ナレッジ/インフラ/cockpit-hub-hosted-integration-spec (id=6080)
|
|
12
12
|
*/
|
|
13
|
+
import { readFile } from "node:fs/promises"
|
|
14
|
+
import os from "node:os"
|
|
15
|
+
import path from "node:path"
|
|
16
|
+
|
|
13
17
|
import pino from "pino"
|
|
14
18
|
|
|
15
19
|
import { readConfig } from "./config.mjs"
|
|
@@ -29,6 +33,24 @@ import { getSessionUsages, getUsage } from "./usage.mjs"
|
|
|
29
33
|
|
|
30
34
|
const logger = pino({ name: "hub-agent" })
|
|
31
35
|
|
|
36
|
+
const BUNDLE_MANIFEST_PATH =
|
|
37
|
+
process.env.HUB_BUNDLE_MANIFEST ||
|
|
38
|
+
path.join(os.homedir(), ".claude", "scripts", "manifest.json")
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `~/.claude/scripts/manifest.json` から Hub AI bundle の version を読む。
|
|
42
|
+
* 未セットアップなら null を返す。
|
|
43
|
+
*/
|
|
44
|
+
async function readBundleVersion() {
|
|
45
|
+
try {
|
|
46
|
+
const text = await readFile(BUNDLE_MANIFEST_PATH, "utf-8")
|
|
47
|
+
const data = JSON.parse(text)
|
|
48
|
+
return typeof data?.version === "string" ? data.version : null
|
|
49
|
+
} catch {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
export async function startDaemon({ version, ptyModule } = {}) {
|
|
33
55
|
const config = await readConfig()
|
|
34
56
|
if (!config) {
|
|
@@ -45,7 +67,12 @@ export async function startDaemon({ version, ptyModule } = {}) {
|
|
|
45
67
|
const resolvedPty = ptyModule || (await import("node-pty"))
|
|
46
68
|
const ptyBridge = new PtyBridge({ ptyModule: resolvedPty, logger, plugins })
|
|
47
69
|
|
|
48
|
-
const
|
|
70
|
+
const bundleVersion = await readBundleVersion()
|
|
71
|
+
if (bundleVersion) {
|
|
72
|
+
logger.info({ bundleVersion }, "hub bundle version detected")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const client = new WsClient(config, { logger, version, bundleVersion })
|
|
49
76
|
|
|
50
77
|
// EventEmitter の 'error' は listener が無いと process が落ちる。
|
|
51
78
|
// ws-client は close 側で reconnect を予約しているので、ここでは log だけ。
|
package/src/tmux.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import path from "node:path"
|
|
|
20
20
|
import { promisify } from "node:util"
|
|
21
21
|
|
|
22
22
|
import { detectSessionState } from "./state.mjs"
|
|
23
|
+
import { getSessionUsages } from "./usage.mjs"
|
|
23
24
|
|
|
24
25
|
const execFileP = promisify(execFile)
|
|
25
26
|
|
|
@@ -159,20 +160,38 @@ export async function listSessions(opts = {}) {
|
|
|
159
160
|
}
|
|
160
161
|
})
|
|
161
162
|
|
|
162
|
-
// state + cwd + worktree
|
|
163
|
-
|
|
163
|
+
// state + cwd + worktree 親判定 + statusLine 由来 context% を並列付与
|
|
164
|
+
// statusLine の `used_percentage` を context_pct (USED %) として使う。
|
|
165
|
+
// pane 解析 (state.detectSessionState) はフォーマット変動に脆いので、
|
|
166
|
+
// statusLine cache がある cwd では優先採用する。
|
|
167
|
+
const [wtParentMap, sessionUsages] = await Promise.all([
|
|
168
|
+
buildWorktreeParentMap(),
|
|
169
|
+
getSessionUsages().catch(() => []),
|
|
170
|
+
])
|
|
171
|
+
// cwd → context_pct の lookup (同一 cwd で複数 session_id ある場合は最新 mtime 優先)
|
|
172
|
+
const ctxByCwd = new Map()
|
|
173
|
+
for (const u of sessionUsages) {
|
|
174
|
+
const prev = ctxByCwd.get(u.cwd)
|
|
175
|
+
if (!prev || u.updatedAtMs > prev.updatedAtMs) {
|
|
176
|
+
ctxByCwd.set(u.cwd, { contextPercent: u.contextPercent, updatedAtMs: u.updatedAtMs })
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
return Promise.all(
|
|
165
181
|
base.map(async (s) => {
|
|
166
182
|
const [state, cwd] = await Promise.all([
|
|
167
183
|
detectSessionState(s.name, opts),
|
|
168
184
|
getSessionCwd(s.name, opts),
|
|
169
185
|
])
|
|
186
|
+
// statusLine 由来 context% を最優先 (USED %), なければ pane 解析の fallback
|
|
187
|
+
const fromStatusLine = cwd ? ctxByCwd.get(cwd)?.contextPercent : null
|
|
188
|
+
const context_pct =
|
|
189
|
+
typeof fromStatusLine === "number" ? fromStatusLine : state.context_pct
|
|
170
190
|
return {
|
|
171
191
|
...s,
|
|
172
192
|
status: state.status,
|
|
173
|
-
context_pct
|
|
193
|
+
context_pct,
|
|
174
194
|
cwd,
|
|
175
|
-
// worktree session の場合は親 workspace の session 名を入れる (旧 cockpit webapp 互換)
|
|
176
195
|
parent_session_name: wtParentMap.get(s.name) || null,
|
|
177
196
|
}
|
|
178
197
|
}),
|
package/src/ws-client.mjs
CHANGED
|
@@ -20,13 +20,14 @@ const MAX_BACKOFF_MS = 30_000
|
|
|
20
20
|
export class WsClient extends EventEmitter {
|
|
21
21
|
/**
|
|
22
22
|
* @param {{ hub_url: string, agent_id: string, agent_token: string }} config
|
|
23
|
-
* @param {{ logger?: import('pino').Logger, version?: string }} opts
|
|
23
|
+
* @param {{ logger?: import('pino').Logger, version?: string, bundleVersion?: string|null, hostname?: string }} opts
|
|
24
24
|
*/
|
|
25
25
|
constructor(config, opts = {}) {
|
|
26
26
|
super()
|
|
27
27
|
this.config = config
|
|
28
28
|
this.logger = opts.logger
|
|
29
29
|
this.version = opts.version || "0.1.0"
|
|
30
|
+
this.bundleVersion = opts.bundleVersion || null
|
|
30
31
|
this.hostname = opts.hostname || os.hostname()
|
|
31
32
|
this.ws = null
|
|
32
33
|
this.heartbeatTimer = null
|
|
@@ -56,6 +57,7 @@ export class WsClient extends EventEmitter {
|
|
|
56
57
|
agent_id: this.config.agent_id,
|
|
57
58
|
hostname: this.hostname,
|
|
58
59
|
version: this.version,
|
|
60
|
+
bundle_version: this.bundleVersion,
|
|
59
61
|
})
|
|
60
62
|
this._startHeartbeat()
|
|
61
63
|
this.emit("open")
|
|
@@ -142,6 +144,7 @@ export class WsClient extends EventEmitter {
|
|
|
142
144
|
const ok = this._sendJson({
|
|
143
145
|
type: "heartbeat",
|
|
144
146
|
uptime_sec: Math.floor((Date.now() - this.startedAt) / 1000),
|
|
147
|
+
bundle_version: this.bundleVersion,
|
|
145
148
|
})
|
|
146
149
|
if (!ok) {
|
|
147
150
|
this.logger?.warn("heartbeat send failed, forcing reconnect")
|