@cocorograph/hub-agent 0.6.54 → 0.6.56
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 +40 -0
- package/src/profiles.mjs +24 -11
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -711,6 +711,28 @@ async function readAllSessionEvents() {
|
|
|
711
711
|
return out
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
+
/**
|
|
715
|
+
* 単一 session の最新 session-event をファイル正本から読む。無ければ null。
|
|
716
|
+
* claude.tui.bind 時の `stop` 再配信 (取りこぼし回復) に使う。
|
|
717
|
+
*/
|
|
718
|
+
async function readSessionEventFile(sessionName) {
|
|
719
|
+
if (!sessionName || /[/\\]/.test(sessionName)) return null
|
|
720
|
+
try {
|
|
721
|
+
const text = await readFile(
|
|
722
|
+
path.join(SESSION_EVENTS_DIR, `${sessionName}.json`),
|
|
723
|
+
"utf-8",
|
|
724
|
+
)
|
|
725
|
+
const data = JSON.parse(text)
|
|
726
|
+
if (!data || typeof data.event !== "string") return null
|
|
727
|
+
return {
|
|
728
|
+
event: data.event,
|
|
729
|
+
at: typeof data.at === "number" ? data.at : Date.now(),
|
|
730
|
+
}
|
|
731
|
+
} catch {
|
|
732
|
+
return null
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
714
736
|
/**
|
|
715
737
|
* `/tmp/cockpit_session_events/<tmux_session>.json` を fs.watch して
|
|
716
738
|
* UserPromptSubmit / Stop の event を `session.event` 型で Hub に push する。
|
|
@@ -1436,6 +1458,24 @@ async function dispatch(msg, ctx) {
|
|
|
1436
1458
|
skipped: !!rebind.skipped,
|
|
1437
1459
|
error: rebind.ok ? undefined : rebind.error,
|
|
1438
1460
|
})
|
|
1461
|
+
// 取りこぼし回復 (2026-06-07): 直近のターン境界が `stop` ならファイル正本から
|
|
1462
|
+
// 再配信する。fs.watch 由来の session.event push は揮発性 (WS 切断・remount 中の
|
|
1463
|
+
// broadcast は失われる) で、stop を取りこぼした frontend は三点リーダーが回り
|
|
1464
|
+
// 続ける。bind は TUI ビューのマウントごとに来るので、ここで最新 stop を返せば
|
|
1465
|
+
// 再表示時に必ずターン終了が伝わる。prompt_submit は再配信しない (stale な
|
|
1466
|
+
// 「処理中」を誤って点灯させないため。点灯は jsonl エコー / 実 hook が担う)。
|
|
1467
|
+
if (sessionName) {
|
|
1468
|
+
const lastEvent = await readSessionEventFile(sessionName)
|
|
1469
|
+
if (lastEvent && lastEvent.event === "stop") {
|
|
1470
|
+
ctx.client.send({
|
|
1471
|
+
type: "session.event",
|
|
1472
|
+
session_name: sessionName,
|
|
1473
|
+
event: lastEvent.event,
|
|
1474
|
+
at: lastEvent.at,
|
|
1475
|
+
replay: true,
|
|
1476
|
+
})
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1439
1479
|
} catch (err) {
|
|
1440
1480
|
logger?.warn(
|
|
1441
1481
|
{ err: err?.message, sessionName, cwd },
|
package/src/profiles.mjs
CHANGED
|
@@ -197,27 +197,40 @@ export async function addProfile(opts = {}) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
|
-
* プロファイルの
|
|
200
|
+
* プロファイルの `.claude.json` から最後にログインしていたアカウント情報を読む。
|
|
201
201
|
* Claude Code がログイン時に oauthAccount を書き込むため、「このディレクトリで最後に
|
|
202
202
|
* 使われたアカウント」のメタ情報として UI 表示に使える (認証トークンそのものではない)。
|
|
203
203
|
* ファイルが無い・壊れている・未ログインなら null。
|
|
204
204
|
*
|
|
205
|
+
* ⚠️ パスの罠: Claude Code は `.claude.json` を CLAUDE_CONFIG_DIR 指定時のみ
|
|
206
|
+
* `<configDir>/.claude.json` に置き、既定プロファイルでは `~/.claude.json`
|
|
207
|
+
* (ホーム直下、`~/.claude/` の外) に置く。一律 `<configDir>/.claude.json` を読むと
|
|
208
|
+
* 既定プロファイルだけ常に null になる (2026-06-07 実バグ: Cockpit のアカウント
|
|
209
|
+
* 切替 UI で既定プロファイルのみメール・組織が表示されなかった)。
|
|
210
|
+
*
|
|
205
211
|
* @param {string} configDir
|
|
206
212
|
* @returns {Promise<{email: string|null, organization: string|null}|null>}
|
|
207
213
|
*/
|
|
208
214
|
export async function readProfileAccount(configDir) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
const candidates = [path.join(configDir, ".claude.json")]
|
|
216
|
+
if (path.resolve(configDir) === defaultConfigDir()) {
|
|
217
|
+
candidates.push(path.join(os.homedir(), ".claude.json"))
|
|
218
|
+
}
|
|
219
|
+
for (const file of candidates) {
|
|
220
|
+
try {
|
|
221
|
+
const raw = await fs.readFile(file, "utf-8")
|
|
222
|
+
const oa = JSON.parse(raw)?.oauthAccount
|
|
223
|
+
if (!oa || typeof oa !== "object") continue
|
|
224
|
+
return {
|
|
225
|
+
email: typeof oa.emailAddress === "string" ? oa.emailAddress : null,
|
|
226
|
+
organization:
|
|
227
|
+
typeof oa.organizationName === "string" ? oa.organizationName : null,
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
/* 次の候補へ */
|
|
217
231
|
}
|
|
218
|
-
} catch {
|
|
219
|
-
return null
|
|
220
232
|
}
|
|
233
|
+
return null
|
|
221
234
|
}
|
|
222
235
|
|
|
223
236
|
/**
|