import WebSocket from 'ws'; /** * Build the handshake headers for an agent WebSocket connection * (terminal / vscode tunnel). Re-sends the ALB sticky cookie when present so a * reconnect lands on the same API task (scale-out safe). */ export declare function buildAgentWsHeaders(token: string, agentId: string, cookie?: string): Record; export interface BaseWebSocketOptions { maxReconnectRetries: number; reconnectBaseDelayMs: number; /** 再接続バックオフの上限 (ms)。省略時は cap なし。 */ reconnectMaxDelayMs?: number; logPrefix: string; /** ping 送信間隔 (ms)。省略時は WS_HEARTBEAT_INTERVAL_MS。0 以下で無効化。 */ heartbeatIntervalMs?: number; /** 連続未応答 pong の許容回数。省略時は WS_PONG_MAX_MISSED。 */ pongMaxMissed?: number; /** * サーバーが恒久的な認証拒否(無効なトークン、Agent ID トークンバインディング不一致等) * で接続を閉じる際に使う WebSocket クローズコード。省略時はこの判定を一切行わず、 * どのクローズコードでも常に再接続する(従来の挙動)。 * * この値は接続先サーバー固有の取り決めであるため、オプトインにしている。例えば * AppSyncSubscriber は AWS AppSync のリアルタイムエンドポイントと通信しており、 * そちらのクローズコードの意味はこの API サーバーの取り決めとは無関係なので、 * このオプションを渡さない(=常に再接続する)。 */ authRejectedCloseCode?: number; } /** * WebSocket 接続の共通ライフサイクル管理 * - 接続 / 再接続 / 切断 * - JSON メッセージパース * - 再接続時のコールバック */ export declare abstract class BaseWebSocketConnection { protected ws: WebSocket | null; protected closed: boolean; protected readonly reconnectAttemptsRef: { current: number; }; protected readonly options: BaseWebSocketOptions; private heartbeatTimer; /** * Liveness state for the isAlive / missed-pong heartbeat method. * - alive: set true on every 'pong'. Reset to false right after each ping so * the next tick can tell whether a pong arrived in between. * - missed: consecutive ticks without a pong. Reaching pongMaxMissed terminates. * alive starts false (consistent with the API side): the very first ping tick * counts as one missed unless a pong has already arrived. */ private heartbeatAlive; private heartbeatMissed; /** * ALB sticky-session cookies (AWSALB / AWSALBCORS etc.) captured from the * WS handshake (upgrade) response's Set-Cookie headers, keyed by cookie * name. Re-sent as a Cookie header on reconnect so the agent lands on the * SAME API task when the API is scaled out behind an ALB. In-memory only — * stickiness across agent restarts is not needed. */ private readonly stickyCookies; constructor(options: BaseWebSocketOptions); connect(): Promise; disconnect(): void; /** WebSocket URL とオプションを返す */ protected abstract createWebSocket(): WebSocket; /** 接続成功時の処理。Promise を resolve するタイミングはサブクラスが決める */ protected abstract onOpen(ws: WebSocket, resolve: (value: void) => void): void; /** パースされたメッセージを処理する */ protected abstract onParsedMessage(msg: TMessage, resolve?: (value: void) => void): void; /** 切断時のクリーンアップ(サブクラスでオーバーライド) */ protected onDisconnect(): void; /** WebSocket を閉じる(サブクラスでオーバーライド可能) */ protected closeWebSocket(ws: WebSocket): void; protected sendMessage(msg: unknown): void; /** 再接続成功時のコールバック(サブクラスでオーバーライド可能) */ protected onReconnected(): void; /** * Cookie request header assembled from the sticky cookies captured on the * previous handshake, or undefined when none were received. Subclasses that * build their own handshake headers in createWebSocket() should include * this so reconnects stick to the same ALB target. */ protected getStickyCookieHeader(): string | undefined; /** * Capture Set-Cookie headers from the WS handshake (upgrade) response. * Only the `name=value` pair is kept (attributes like Path/Expires are * request-irrelevant); a later handshake overwrites cookies by name. */ private captureStickyCookies; protected doConnect(): Promise; /** * ハートビート (ping/pong) を開始する。 * * ws 標準の「isAlive」単一インターバル方式を使う。各インターバルで: * 1. 前回の tick 以降に pong を受信していなければ missed をインクリメントし、 * missed が pongMaxMissed に達したら接続を死んでいる(half-open / * ロードバランサに切られた)とみなして terminate する。 * 2. pong を受信していれば missed を 0 にリセットする。 * 3. alive を false に戻し、次の ping を送信する。 * * ただし最初の tick は ping を送るだけで miss 判定をしない(接続直後は相手に * pong を返す機会がまだ無いため)。これにより健全な接続でも phantom miss を * 数えず、pongMaxMissed 回の許容を接続直後からフルに使える(finding #6)。 * * terminate は 'close' イベントを発火させ、既存の再接続ロジックを起動する。 * 1 回の未応答では terminate しないため、イベントループ stall による誤検知を防ぐ。 */ private startHeartbeat; /** ハートビートを停止し、生存確認状態をリセットする。 */ private stopHeartbeat; /** WebSocket close イベント時の追加処理(サブクラスでオーバーライド可能) */ protected onWebSocketClose(): void; /** * サーバーによる恒久的な認証拒否(authRejectedCloseCode)でクローズされた際の処理 * (サブクラスでオーバーライド可能)。二度と再接続しないことが確定しているため、 * onWebSocketClose() のような再接続を前提としたグレース処理ではなく、即時の * リソース解放を行うべき場面で使う。 */ protected onPermanentClose(): void; private doReconnect; } //# sourceMappingURL=base-websocket.d.ts.map