Files
hakorune/tools/codex-tmux-driver/claude-codex-unified-bridge.js
Moe Charm 4e1b595796 AI協調開発研究ドキュメントの完成と Phase 10.9-β 進捗
【AI協調開発研究】
- AI二重化モデルの学術論文draft完成(workshop_paper_draft.md)
- 「隠れた危機」分析とbirthの原則哲学化
- TyEnv「唯一の真実」協調会話を保存・研究資料に統合
- papers管理構造の整備(wip/under-review/published分離)

【Phase 10.9-β HostCall進捗】
- JitConfigBox: relax_numeric フラグ追加(i64→f64コアーション制御)
- HostcallRegistryBox: 署名検証・白黒リスト・コアーション対応
- JitHostcallRegistryBox: Nyash側レジストリ操作API
- Lower統合: env直読 → jit::config::current() 参照に統一
- 数値緩和設定: NYASH_JIT_HOSTCALL_RELAX_NUMERIC/Config.set_flag

【検証サンプル拡充】
- math.sin/cos/abs/min/max 関数スタイル(examples/jit_math_function_style_*.nyash)
- 境界ケース: 署名不一致・コアーション許可・mutating拒否サンプル
- E2E実証: String.length→allow, Array.push→fallback, math関数の署名一致観測

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 12:09:09 +09:00

189 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// claude-codex-unified-bridge.js
// 同一hook-serverを使った完璧な双方向ブリッジ
const { spawn } = require('child_process');
const WebSocket = require('ws');
const EventEmitter = require('events');
class ClaudeCodexUnifiedBridge extends EventEmitter {
constructor(config = {}) {
super();
this.config = {
hookServer: config.hookServer || 'ws://localhost:8770',
claudeSession: config.claudeSession || 'claude-8771',
codexSession: config.codexSession || 'codex-safe',
watchInterval: config.watchInterval || 500,
...config
};
this.ws = null;
this.isRunning = false;
this.lastClaudeOutput = '';
this.lastCodexOutput = '';
}
// ブリッジ開始
async start() {
console.log('🌉 Starting Claude-Codex Unified Bridge...');
console.log('📡 Hook Server:', this.config.hookServer);
// WebSocket接続
await this.connectToHookServer();
// 監視開始
this.isRunning = true;
this.startWatching();
console.log('✅ Bridge is running!');
}
// hook-serverに接続
connectToHookServer() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.config.hookServer);
this.ws.on('open', () => {
console.log('✅ Connected to hook-server');
// ブリッジとして登録
this.ws.send(JSON.stringify({
source: 'bridge',
type: 'register',
data: 'claude-codex-bridge'
}));
resolve();
});
this.ws.on('error', (err) => {
console.error('❌ WebSocket error:', err);
reject(err);
});
this.ws.on('close', () => {
console.log('🔌 Disconnected from hook-server');
this.isRunning = false;
});
});
}
// 監視ループ
startWatching() {
const watchLoop = setInterval(async () => {
if (!this.isRunning) {
clearInterval(watchLoop);
return;
}
try {
// Codexの出力をチェック
await this.checkCodexOutput();
// Claudeの出力もチェック必要に応じて
// await this.checkClaudeOutput();
} catch (err) {
console.error('❌ Watch error:', err);
}
}, this.config.watchInterval);
}
// Codexの出力をチェック
async checkCodexOutput() {
const output = await this.capturePane(this.config.codexSession);
// 新しい内容があるかチェック
if (output !== this.lastCodexOutput) {
const newContent = this.extractNewContent(output, this.lastCodexOutput);
if (newContent && this.isCodexResponse(newContent)) {
console.log('📨 Codex response detected!');
// Claudeに転送
this.sendToClaude(newContent);
this.lastCodexOutput = output;
}
}
}
// Claudeにメッセージを送信hook-server経由
sendToClaude(message) {
console.log('📤 Sending to Claude via hook-server...');
const payload = {
source: 'codex',
type: 'inject-input',
data: `[Codex Response]\n${message}`
};
this.ws.send(JSON.stringify(payload));
this.emit('codex-to-claude', message);
}
// tmuxペインをキャプチャ
capturePane(sessionName) {
return new Promise((resolve, reject) => {
const proc = spawn('tmux', ['capture-pane', '-t', sessionName, '-p']);
let output = '';
proc.stdout.on('data', (data) => output += data);
proc.on('close', (code) => {
if (code === 0) {
resolve(output);
} else {
reject(new Error(`tmux capture failed with code ${code}`));
}
});
});
}
// 新しいコンテンツを抽出
extractNewContent(current, previous) {
if (current.length > previous.length) {
return current.substring(previous.length).trim();
}
return null;
}
// Codexの応答かどうか判定
isCodexResponse(text) {
// Working状態でない、プロンプトでない、十分な長さ
return !text.includes('Working') &&
!text.includes('▌') &&
text.length > 20 &&
!text.includes('⏎ send');
}
// 停止
stop() {
this.isRunning = false;
if (this.ws) {
this.ws.close();
}
console.log('🛑 Bridge stopped');
}
}
// メイン実行
if (require.main === module) {
const bridge = new ClaudeCodexUnifiedBridge();
// イベントリスナー
bridge.on('codex-to-claude', (content) => {
console.log('📊 Transferred to Claude:', content.substring(0, 50) + '...');
});
// 開始
bridge.start().catch(console.error);
// 終了処理
process.on('SIGINT', () => {
console.log('\n👋 Shutting down...');
bridge.stop();
process.exit(0);
});
}
module.exports = ClaudeCodexUnifiedBridge;