Files
hakorune/tools/codex-tmux-driver/tmux-perfect-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

217 lines
6.2 KiB
JavaScript
Raw 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.

// tmux-perfect-bridge.js
// tmux × tmux = 完璧な双方向自動ブリッジ!
const { spawn } = require('child_process');
const EventEmitter = require('events');
class TmuxPerfectBridge extends EventEmitter {
constructor() {
super();
this.codexSession = 'codex-bridge';
this.claudeSession = 'claude-bridge';
this.isRunning = false;
this.lastCodexOutput = '';
this.lastClaudeOutput = '';
}
// 両方のAIをtmuxで起動
async start() {
console.log('🚀 Starting Perfect Bridge...');
// Codexを起動
await this.startSession(this.codexSession, '/home/tomoaki/.volta/bin/codex');
// Claudeを起動仮のコマンド
// await this.startSession(this.claudeSession, 'claude');
console.log('✅ Both AIs are ready in tmux!');
this.isRunning = true;
}
// tmuxセッションを起動
async startSession(sessionName, command) {
// 既存セッションを削除
await this.exec('tmux', ['kill-session', '-t', sessionName]).catch(() => {});
// 新規セッション作成
await this.exec('tmux', ['new-session', '-d', '-s', sessionName, command]);
console.log(`📺 Started ${sessionName}`);
// 起動待ち
await this.sleep(2000);
}
// Codex → Claude 転送
async forwardCodexToClaude() {
const codexOutput = await this.capturePane(this.codexSession);
const newContent = this.extractNewContent(codexOutput, this.lastCodexOutput);
if (newContent && this.isCodexResponse(newContent)) {
console.log('📨 Codex → Claude:', newContent.substring(0, 50) + '...');
// tmux send-keysで直接送信Enterも完璧
await this.sendToSession(this.claudeSession, newContent);
this.lastCodexOutput = codexOutput;
this.emit('codex-to-claude', newContent);
}
}
// Claude → Codex 転送
async forwardClaudeToCodex() {
const claudeOutput = await this.capturePane(this.claudeSession);
const newContent = this.extractNewContent(claudeOutput, this.lastClaudeOutput);
if (newContent && this.isClaudeResponse(newContent)) {
console.log('📨 Claude → Codex:', newContent.substring(0, 50) + '...');
// tmux send-keysで直接送信Enterも完璧
await this.sendToSession(this.codexSession, newContent);
this.lastClaudeOutput = claudeOutput;
this.emit('claude-to-codex', newContent);
}
}
// 双方向監視ループ
async startWatching(intervalMs = 1000) {
console.log('👁️ Starting bidirectional watch...');
const watchLoop = setInterval(async () => {
if (!this.isRunning) {
clearInterval(watchLoop);
return;
}
try {
// 両方向をチェック
await this.forwardCodexToClaude();
await this.forwardClaudeToCodex();
} catch (err) {
console.error('❌ Watch error:', err);
}
}, intervalMs);
}
// tmuxペインをキャプチャ
async capturePane(sessionName) {
const result = await this.exec('tmux', ['capture-pane', '-t', sessionName, '-p']);
return result.stdout;
}
// tmuxセッションに送信Enterも
async sendToSession(sessionName, text) {
await this.exec('tmux', ['send-keys', '-t', sessionName, text, 'Enter']);
}
// 新しいコンテンツを抽出
extractNewContent(current, previous) {
// 簡単な差分検出(実際はもっと高度にする)
if (current.length > previous.length) {
return current.substring(previous.length).trim();
}
return null;
}
// Codexの応答かどうか判定
isCodexResponse(text) {
return !text.includes('Working') &&
!text.includes('▌') &&
text.length > 10;
}
// Claudeの応答かどうか判定
isClaudeResponse(text) {
// Claudeの出力パターンに応じて調整
return text.length > 10;
}
// 初期メッセージを送信
async sendInitialMessage(message) {
console.log('🎯 Sending initial message to Codex...');
await this.sendToSession(this.codexSession, message);
}
// 両セッションを表示(デバッグ用)
showSessions() {
console.log('\n📺 Showing both sessions side by side...');
spawn('tmux', [
'new-window', '-n', 'AI-Bridge',
`tmux select-pane -t 0 \\; \
attach-session -t ${this.codexSession} \\; \
split-window -h \\; \
attach-session -t ${this.claudeSession}`
], { stdio: 'inherit' });
}
// 停止
async stop() {
this.isRunning = false;
await this.exec('tmux', ['kill-session', '-t', this.codexSession]).catch(() => {});
await this.exec('tmux', ['kill-session', '-t', this.claudeSession]).catch(() => {});
console.log('👋 Bridge stopped');
}
// ヘルパー関数
exec(command, args) {
return new Promise((resolve, reject) => {
const proc = spawn(command, args);
let stdout = '';
let stderr = '';
proc.stdout.on('data', (data) => stdout += data);
proc.stderr.on('data', (data) => stderr += data);
proc.on('close', (code) => {
if (code !== 0 && !stderr.includes('no server running')) {
reject(new Error(`${command} exited with code ${code}: ${stderr}`));
} else {
resolve({ stdout, stderr });
}
});
});
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// デモ実行
async function demo() {
const bridge = new TmuxPerfectBridge();
// イベントリスナー
bridge.on('codex-to-claude', (content) => {
console.log('🔄 Transferred from Codex to Claude');
});
bridge.on('claude-to-codex', (content) => {
console.log('🔄 Transferred from Claude to Codex');
});
try {
// ブリッジ開始
await bridge.start();
// 初期メッセージ
await bridge.sendInitialMessage('Nyashプロジェクトについて、お互いに意見を交換してください');
// 監視開始
await bridge.startWatching(500);
// デバッグ用に画面表示
// bridge.showSessions();
} catch (err) {
console.error('❌ Error:', err);
}
}
// エクスポート
module.exports = TmuxPerfectBridge;
// 直接実行
if (require.main === module) {
demo();
}