feat(v0.3): H2 server — Claude Code hooks → live status side-channel
- types: ClaudeStatus + ServerMessage 'status'; Session.claudeStatus; SessionManager.handleHookEvent - session: inject WEBTERM_SESSION + WEBTERM_HOOK_URL into the spawned shell env so hooks know which tab they belong to - http/hook.ts: pure event→status mapper (PreToolUse→working, PermissionRequest/ Notification:permission_prompt→waiting, Stop/idle_prompt→idle); 6 unit tests - server: POST /hook (loopback-only, express.json 64kb) → manager pushes 'status' - fix: closeIdleConnections() on shutdown so an idle hook keep-alive socket doesn't hang close()/SIGTERM - integration ⑦ (real PTY): attach → POST /hook → ws receives status. 205 tests green.
This commit is contained in:
13
src/types.ts
13
src/types.ts
@@ -44,12 +44,17 @@ export type ClientMessage =
|
||||
| { type: 'input'; data: string }
|
||||
| { type: 'resize'; cols: number; rows: number };
|
||||
|
||||
/** Claude Code activity, derived from hooks (H2). 'unknown' = no hook signal yet. */
|
||||
export type ClaudeStatus = 'working' | 'waiting' | 'idle' | 'unknown';
|
||||
|
||||
/** server → client. exit.code = shell code; -1 when spawn never succeeded (M4).
|
||||
* exit.reason optional normally, REQUIRED on spawn failure / abnormal exit. */
|
||||
* exit.reason optional normally, REQUIRED on spawn failure / abnormal exit.
|
||||
* status (H2) = Claude Code activity pushed from a hook side-channel. */
|
||||
export type ServerMessage =
|
||||
| { type: 'attached'; sessionId: string }
|
||||
| { type: 'output'; data: string }
|
||||
| { type: 'exit'; code: number; reason?: string };
|
||||
| { type: 'exit'; code: number; reason?: string }
|
||||
| { type: 'status'; status: ClaudeStatus; detail?: string };
|
||||
|
||||
/** parseClientMessage result — never throws; errors flow here (§5.3). */
|
||||
export type ParseResult =
|
||||
@@ -139,6 +144,8 @@ export interface Session {
|
||||
* and attach replays the buffer then re-sends `exit` (L1). */
|
||||
exitedAt: number | null;
|
||||
exitCode: number | null;
|
||||
/** Claude Code activity from hooks (H2); updated by manager.handleHookEvent. */
|
||||
claudeStatus: ClaudeStatus;
|
||||
readonly pty: IPty;
|
||||
}
|
||||
|
||||
@@ -156,6 +163,8 @@ export interface Session {
|
||||
export interface SessionManager {
|
||||
handleAttach(ws: WebSocketLike, sessionId: string | null, dims: Dims, now: number): Session;
|
||||
get(id: string): Session | undefined;
|
||||
/** Set a session's Claude status (from a hook) and push it to the attached ws (H2). */
|
||||
handleHookEvent(sessionId: string, status: ClaudeStatus, detail?: string): void;
|
||||
/** reclaim when now - max(detachedAt, lastOutputAt) > idleTtlMs (M3). Returns count. */
|
||||
reapIdle(now: number): number;
|
||||
shutdown(): void;
|
||||
|
||||
Reference in New Issue
Block a user