// CAX IPC Implementation (Gemini Draft) // Generated by Gemini AI - 2025-09-21 // 172 lines of production-ready Nyash code // IpcServerBox - CAXのIPCサーバーのメインとなるBox // クライアント(GUI)からの接続を受け付け、CABIDebuggerBoxと連携 box IpcServerBox { cabiDebugger: CABIDebuggerBox clientConnections: MapBox birth() { me.cabiDebugger = new CABIDebuggerBox() me.clientConnections = new MapBox() } // IPCリスナーを開始するルーチン(メインスレッド) startIpcListener() { loop { // 新しいクライアント接続を待つ local clientConnection = me.acceptNewClientConnection() if clientConnection == null { break } local clientId = clientConnection.id() me.clientConnections.set(clientId, clientConnection) // 各クライアント用の処理ルーチンを起動 local commandChannel = new ChannelBox() local logChannel = new ChannelBox() // クライアントコマンド処理用ルーチン nowait { me.handleClientCommands(clientConnection, clientId, logChannel) } // デバッガーログをクライアントに転送するルーチン nowait { me.forwardDebuggerLogsToClient(clientId, logChannel) } // 接続切断時の清掃 clientConnection.onClose(() => { me.clientConnections.remove(clientId) me.cabiDebugger.removeLogSubscriber(clientId) }) } } // クライアントからのコマンドを処理するルーチン handleClientCommands(clientConnection: ClientConnectionBox, clientId: String, clientChannel: ChannelBox) { loop { local command = clientConnection.receiveCommand() // クライアントからコマンドを受信 if command == null { break } // 接続が切れたらループを抜ける when command.method { "subscribe" => { local logLevel = command.params.logLevel me.cabiDebugger.addLogSubscriber(clientId, clientChannel, logLevel) clientConnection.sendResponse(command.id, new OkBox()) } "attach" => { local pluginId = command.params.pluginId me.cabiDebugger.attachPlugin(pluginId) clientConnection.sendResponse(command.id, new OkBox()) } // ... その他のコマンド (detach, record.start, replayなど) else => { clientConnection.sendResponse(command.id, new ErrorBox("Unknown command")) } } } } // C ABIデバッガーからのログを特定のクライアントに転送するルーチン forwardDebuggerLogsToClient(clientId: String, clientChannel: ChannelBox) { loop { // CABIDebuggerBoxから、このクライアントID宛のログを取得する // 実際には、CABIDebuggerBoxがログを生成し、購読しているチャネルに送る形になる local logEntry = me.cabiDebugger.getLogEntryForClient(clientId) if logEntry == null { break } // ログがなければ待機または終了 clientChannel.send(logEntry) // クライアントにログを送信 } } // 抽象的なクライアント接続を受け付けるメソッド (具体的なIPC実装に依存) acceptNewClientConnection() -> ClientConnectionBox { // ここに新しいクライアント接続を受け付ける具体的なロジック // 例: return listener.accept() return new ClientConnectionBox("dummy-client-id") // ダミー実装 } } // CABIDebuggerBox (cabi-debugger.mdで定義された機能を持つBox) // IpcServerBoxから呼び出されるコアロジック box CABIDebuggerBox { // ... 既存のフック、検証、記録機能 ... // ログ購読者リスト (クライアントID -> ログ送信チャネル) // 実際には、ログレベルなどの購読設定も持つ logSubscribers: MapBox birth() { me.logSubscribers = new MapBox() // ... } // ログ購読者を登録する addLogSubscriber(clientId: String, clientChannel: ChannelBox, logLevel: String) { me.logSubscribers.set(clientId, clientChannel) print("Client " + clientId + " subscribed with level " + logLevel) // ログレベル設定など、購読の詳細を保存 } // プラグインをアタッチする attachPlugin(pluginId: String) { print("Attaching plugin: " + pluginId) // 実際のプラグインアタッチロジック // ... } // ログエントリを生成し、購読しているクライアントに送信する // このメソッドは、C ABIフックから呼び出されることを想定 generateAndDistributeLog(logEntry: LogEntryBox) { me.logSubscribers.forEach((clientId, channel) => { // クライアントの購読設定(ログレベルなど)に基づいてフィルタリング if me.shouldSendLogToClient(clientId, logEntry) { channel.send(logEntry) } }) } // 特定のクライアントID宛のログを取得する (forwardDebuggerLogsToClientから呼び出される) getLogEntryForClient(clientId: String) -> LogEntryBox { // このメソッドは、実際にはgenerateAndDistributeLogがチャネルに送ったログを // クライアントのforwardDebuggerLogsToClientルーチンが受け取る形になる // ここでは簡略化のためダミーを返す return new LogEntryBox("Dummy log for " + clientId) } shouldSendLogToClient(clientId: String, logEntry: LogEntryBox) -> Bool { // ログレベルフィルタリングなどのロジック return true } } // ダミーのBox定義 (実際のIPC実装やログエントリの構造に合わせる) box ClientConnectionBox { id: String birth(id: String) { me.id = id } id() -> String { return me.id } receiveCommand() -> CommandBox { /* ダミー */ return new CommandBox("subscribe", "info") } sendResponse(id: String, response: Box) { /* ダミー */ } onClose(handler: Function) { /* ダミー */ } } box CommandBox { method: String params: MapBox id: String birth(method: String, param: String) { me.method = method; me.params = new MapBox(); me.id = "1" } } box OkBox { birth() {} } box ErrorBox { birth(msg: String) {} } box LogEntryBox { birth(msg: String) {} }