146 lines
4.2 KiB
JavaScript
146 lines
4.2 KiB
JavaScript
|
|
// codex-claude-auto-bridge.js
|
|||
|
|
// CodexとClaudeを自動で橋渡しするシステム
|
|||
|
|
|
|||
|
|
const fs = require('fs').promises;
|
|||
|
|
const path = require('path');
|
|||
|
|
const TmuxCodexController = require('./tmux-codex-controller');
|
|||
|
|
const CodexOutputWatcher = require('./codex-output-watcher');
|
|||
|
|
|
|||
|
|
class CodexClaudeAutoBridge {
|
|||
|
|
constructor(config = {}) {
|
|||
|
|
this.config = {
|
|||
|
|
sessionName: config.sessionName || 'codex-safe',
|
|||
|
|
outputFile: config.outputFile || './codex-response.txt',
|
|||
|
|
logFile: config.logFile || './bridge.log',
|
|||
|
|
watchInterval: config.watchInterval || 500,
|
|||
|
|
...config
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
this.controller = new TmuxCodexController(this.config.sessionName);
|
|||
|
|
this.watcher = new CodexOutputWatcher(this.config.sessionName);
|
|||
|
|
this.isRunning = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ブリッジを開始
|
|||
|
|
async start() {
|
|||
|
|
console.log('🌉 Starting Codex-Claude Auto Bridge...');
|
|||
|
|
this.isRunning = true;
|
|||
|
|
|
|||
|
|
// 出力ウォッチャーのイベント設定
|
|||
|
|
this.watcher.on('response', async (response) => {
|
|||
|
|
await this.handleCodexResponse(response);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.watcher.on('ready', () => {
|
|||
|
|
console.log('💚 Codex is ready for next input');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 監視開始
|
|||
|
|
this.watcher.start(this.config.watchInterval);
|
|||
|
|
|
|||
|
|
await this.log('Bridge started');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Codexの応答を処理
|
|||
|
|
async handleCodexResponse(response) {
|
|||
|
|
console.log('\n📝 Got Codex response!');
|
|||
|
|
|
|||
|
|
// 応答をファイルに保存(Claudeが読めるように)
|
|||
|
|
await this.saveResponse(response);
|
|||
|
|
|
|||
|
|
// ログに記録
|
|||
|
|
await this.log(`Codex response: ${response.substring(0, 100)}...`);
|
|||
|
|
|
|||
|
|
// 通知
|
|||
|
|
console.log('✅ Response saved to:', this.config.outputFile);
|
|||
|
|
console.log('📢 Please read the response file and send next message to Codex!');
|
|||
|
|
|
|||
|
|
// 自動応答モードの場合(オプション)
|
|||
|
|
if (this.config.autoReply) {
|
|||
|
|
await this.sendAutoReply();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 応答をファイルに保存
|
|||
|
|
async saveResponse(response) {
|
|||
|
|
const timestamp = new Date().toISOString();
|
|||
|
|
const content = `=== Codex Response at ${timestamp} ===\n\n${response}\n\n`;
|
|||
|
|
|
|||
|
|
await fs.writeFile(this.config.outputFile, content);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Codexにメッセージを送信
|
|||
|
|
async sendToCodex(message) {
|
|||
|
|
console.log(`📤 Sending to Codex: "${message}"`);
|
|||
|
|
await this.controller.sendKeys(message, true); // Enterも送る
|
|||
|
|
await this.log(`Sent to Codex: ${message}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 自動応答(実験的)
|
|||
|
|
async sendAutoReply() {
|
|||
|
|
// 簡単な自動応答ロジック
|
|||
|
|
const replies = [
|
|||
|
|
"なるほど!それについてもう少し詳しく教えて",
|
|||
|
|
"いい感じだにゃ!次はどうする?",
|
|||
|
|
"了解!他に何か提案はある?"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const reply = replies[Math.floor(Math.random() * replies.length)];
|
|||
|
|
|
|||
|
|
console.log(`🤖 Auto-replying in 3 seconds: "${reply}"`);
|
|||
|
|
setTimeout(async () => {
|
|||
|
|
await this.sendToCodex(reply);
|
|||
|
|
}, 3000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ログ記録
|
|||
|
|
async log(message) {
|
|||
|
|
const timestamp = new Date().toISOString();
|
|||
|
|
const logEntry = `[${timestamp}] ${message}\n`;
|
|||
|
|
|
|||
|
|
await fs.appendFile(this.config.logFile, logEntry);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 停止
|
|||
|
|
stop() {
|
|||
|
|
this.watcher.stop();
|
|||
|
|
this.isRunning = false;
|
|||
|
|
console.log('🛑 Bridge stopped');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CLIとして使う場合
|
|||
|
|
if (require.main === module) {
|
|||
|
|
const bridge = new CodexClaudeAutoBridge({
|
|||
|
|
outputFile: './codex-response.txt',
|
|||
|
|
autoReply: false // 自動応答は無効
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 引数からメッセージを取得
|
|||
|
|
const initialMessage = process.argv.slice(2).join(' ');
|
|||
|
|
|
|||
|
|
async function run() {
|
|||
|
|
// ブリッジ開始
|
|||
|
|
await bridge.start();
|
|||
|
|
|
|||
|
|
// 初期メッセージがあれば送信
|
|||
|
|
if (initialMessage) {
|
|||
|
|
console.log('📨 Sending initial message...');
|
|||
|
|
await bridge.sendToCodex(initialMessage);
|
|||
|
|
} else {
|
|||
|
|
console.log('💡 Send a message to Codex using:');
|
|||
|
|
console.log(' tmux send-keys -t codex-safe "your message" Enter');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Ctrl+Cで終了
|
|||
|
|
process.on('SIGINT', () => {
|
|||
|
|
console.log('\n👋 Shutting down...');
|
|||
|
|
bridge.stop();
|
|||
|
|
process.exit(0);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
run().catch(console.error);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = CodexClaudeAutoBridge;
|