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>
This commit is contained in:
189
tools/codex-tmux-driver/claude-codex-unified-bridge.js
Normal file
189
tools/codex-tmux-driver/claude-codex-unified-bridge.js
Normal file
@ -0,0 +1,189 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user