UNPKG

kawazu

Version:

kawazu CLI tool for real-time chat in your editor

657 lines (656 loc) 34.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.joinRoom = joinRoom; const socket_io_client_1 = require("socket.io-client"); const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const chokidar_1 = __importDefault(require("chokidar")); const inquirer_1 = __importDefault(require("inquirer")); const file_1 = require("../utils/file"); const message_1 = require("../utils/message"); const config_1 = require("../utils/config"); async function joinRoom(roomId, options) { if (!(0, file_1.isValidRoomSlug)(roomId)) { console.error(chalk_1.default.red('❌ 無効なルームIDです。英数字、ハイフン、アンダースコアのみ使用できます。')); return; } // 認証チェック console.log(chalk_1.default.blue('🔍 認証状態を確認中...')); const config = await (0, config_1.requireAuth)(); // デバッグモードのチェック const debugMode = process.env.KAWAZU_DEBUG === 'true'; const spinner = debugMode ? null : (0, ora_1.default)('ルームに接続中...').start(); if (debugMode) { console.log(chalk_1.default.yellow('🔍 デバッグモードが有効です')); console.log(chalk_1.default.blue('🔍 ルームに接続中...')); } try { // ユーザー名の取得(認証済みユーザーの場合はconfig.user_usernameを使用) const username = options.username || config.user_username || config.default_username || await promptUsername(); // .codechatファイルのパス const codechatFile = (0, file_1.getCodechatPath)(roomId); // .codechatファイルの存在チェックと作成 const fileResult = await (0, file_1.createCodechatFileIfNotExists)(codechatFile, roomId, username); if (fileResult.existed) { console.log(chalk_1.default.green(`📂 既存のチャットファイルを使用します`)); console.log(chalk_1.default.gray(`💡 これまでのメッセージ履歴が保持されています`)); } else { console.log(chalk_1.default.blue(`📄 新しいチャットファイルを作成しました`)); } // WebSocket接続(認証トークン付き・安定性重視設定) const socket = (0, socket_io_client_1.io)(config.server_url, { timeout: 30000, // 接続タイムアウトを30秒に延長 transports: ['polling', 'websocket'], // pollingを優先して安定性を重視 forceNew: false, // 既存接続の再利用を許可 autoConnect: true, reconnection: true, reconnectionAttempts: 10, // 再接続試行回数を増加 reconnectionDelay: 1000, // 初回再接続を早める reconnectionDelayMax: 10000, // 最大遅延を延長 randomizationFactor: 0.5, auth: { token: config.auth_token }, upgrade: true, rememberUpgrade: false // アップグレード記憶を無効化(安定性重視) }); if (spinner) { spinner.text = 'サーバーに接続中...'; } else { console.log(chalk_1.default.blue('🔍 サーバーに接続中...')); } // 接続エラーハンドリング socket.on('connect_error', (error) => { if (spinner) { spinner.fail('サーバー接続に失敗しました'); } else { console.log(chalk_1.default.red('❌ サーバー接続に失敗しました')); } console.error(chalk_1.default.red(`接続エラー: ${error.message}`)); console.log(chalk_1.default.blue('🔍 接続エラーの詳細:'), error); // 認証エラーの場合 if (error.message.includes('unauthorized') || error.message.includes('authentication')) { console.log(chalk_1.default.yellow(' 認証エラーが発生しました。再ログインが必要です。')); console.log(chalk_1.default.blue(' ログイン: ') + chalk_1.default.cyan('kawazu login')); } else { console.error(chalk_1.default.gray(`詳細: ${JSON.stringify(error, null, 2)}`)); console.error(chalk_1.default.gray(`サーバーURL: ${config.server_url}`)); } process.exit(1); }); // 切断イベント socket.on('disconnect', (reason, details) => { console.log(chalk_1.default.yellow(`🔍 WebSocket切断: ${reason}`)); if (details) { console.log(chalk_1.default.gray('🔍 切断詳細:'), details); } // 参加前の切断の場合はエラーとして扱う if (!roomJoined && reason !== 'io client disconnect') { console.error(chalk_1.default.red('❌ ルーム参加前に接続が切断されました')); console.log(chalk_1.default.yellow('💡 サーバー側で認証処理に問題がある可能性があります')); } }); // 再接続イベント socket.on('reconnect', (attemptNumber) => { console.log(chalk_1.default.blue(`🔍 WebSocket再接続: ${attemptNumber}`)); }); // 再接続エラー socket.on('reconnect_error', (error) => { console.log(chalk_1.default.red(`🔍 再接続エラー: ${error.message}`)); }); // タイムアウト処理 setTimeout(() => { if (!socket.connected) { if (spinner) { spinner.fail('接続タイムアウト'); } else { console.log(chalk_1.default.red('❌ 接続タイムアウト')); } console.error(chalk_1.default.red('サーバーへの接続がタイムアウトしました')); process.exit(1); } }, 30000); // ルーム参加のタイムアウト処理 let roomJoined = false; setTimeout(() => { if (socket.connected && !roomJoined) { if (spinner) { spinner.fail('ルーム参加タイムアウト'); } else { console.log(chalk_1.default.red('❌ ルーム参加タイムアウト')); } console.error(chalk_1.default.red('ルーム参加がタイムアウトしました')); console.log(chalk_1.default.yellow('サーバーからの応答がありません。')); process.exit(1); } }, 60000); // 接続成功時の処理 socket.on('connect', () => { if (spinner) { spinner.text = 'ルームに参加中...'; } else { console.log(chalk_1.default.blue('🔍 ルームに参加中...')); } console.log(chalk_1.default.blue('🔍 WebSocket接続成功')); // 接続安定化のため少し待ってからリクエスト送信 setTimeout(() => { if (!socket.connected) { console.log(chalk_1.default.red('🔍 Socket disconnected, aborting join request')); return; } // ルーム参加リクエスト const joinData = { room_slug: roomId, username: username, password: options.password }; console.log(chalk_1.default.blue('🔍 join-roomリクエスト送信:'), joinData); console.log(chalk_1.default.blue('🔍 Socket状態:'), { connected: socket.connected, id: socket.id }); socket.emit('join-room', joinData); }, 1000); // 1秒待機に延長 }); // ルーム参加成功 socket.on('joined-room', async (data) => { roomJoined = true; if (spinner) { spinner.succeed(`ルーム "${data.room.name}" に参加しました!`); } else { console.log(chalk_1.default.green(`✅ ルーム "${data.room.name}" に参加しました!`)); } // メッセージ履歴を取得して表示(最新10件を確実に取得) try { console.log(chalk_1.default.blue('📜 最新のチャット履歴を取得しています...')); await (0, file_1.loadMessageHistory)(codechatFile, roomId, config.server_url); console.log(chalk_1.default.green('✅ メッセージ履歴の読み込みが完了しました')); } catch (error) { console.log(chalk_1.default.yellow('⚠️ メッセージ履歴の取得でエラーが発生しましたが、チャットは正常に使用できます')); console.log(chalk_1.default.gray(' デバッグ情報:', error)); } console.log(chalk_1.default.green(`📝 ${codechatFile} をエディタで開いてチャットを開始してください`)); console.log(chalk_1.default.blue(`💡 終了するには Ctrl+C を押してください`)); console.log(chalk_1.default.cyan(`📊 SNSのように過去のメッセージ履歴も表示中(最大50件)`)); // ファイル監視を開始 setupFileWatcher(codechatFile, socket, roomId, username); // Socket イベントリスナーの設定 setupSocketListeners(socket, codechatFile, username, roomId, config.server_url); }); // エラーハンドリング socket.on('error', (error) => { if (spinner) { spinner.fail('エラーが発生しました'); } else { console.log(chalk_1.default.red('❌ エラーが発生しました')); } console.error(chalk_1.default.red(`❌ ${error.message || error}`)); console.log(chalk_1.default.blue('🔍 エラーの詳細:'), JSON.stringify(error, null, 2)); // ユーザー名の問題の場合 if (error.message && error.message.includes('Invalid username')) { console.log(chalk_1.default.yellow('💡 ユーザー名に問題があります。英数字、日本語、アンダースコア、ハイフンが使用できます。')); } else if (error.message && (error.message.includes('Password required') || error.message.includes('Invalid password'))) { console.log(chalk_1.default.yellow('💡 プライベートルームの場合は -p オプションでパスワードを指定してください')); console.log(chalk_1.default.gray('例: kawazu join room-name -p password')); } socket.disconnect(); process.exit(1); }); // Socket イベントリスナーは joinRoom 内で設定される } catch (error) { if (spinner) { spinner.fail('ルーム参加に失敗しました'); } else { console.log(chalk_1.default.red('❌ ルーム参加に失敗しました')); } console.error(chalk_1.default.red(`エラー: ${error.message}`)); process.exit(1); } } function setupSocketListeners(socket, codechatFile, currentUsername, roomId, serverUrl) { // 新しいメッセージを受信 socket.on('new-message', async (message) => { try { console.log(chalk_1.default.blue('🔍 new-messageイベント受信:'), { type: typeof message, isNull: message === null, isUndefined: message === undefined, data: message }); // メッセージの有効性をチェック if (!message || typeof message !== 'object' || !message.username || !message.content) { console.log(chalk_1.default.yellow('⚠️ 無効なメッセージを受信しました:'), { message, hasUsername: message && message.username, hasContent: message && message.content, messageType: typeof message }); return; } console.log(chalk_1.default.green('✅ 有効なメッセージを受信:'), { username: message.username, content: message.content, created_at: message.created_at }); const isOwnMessage = message.username === currentUsername; // 自分のメッセージは送信時にすでにファイルに追加されているため、重複を防ぐためスキップ if (isOwnMessage) { console.log(chalk_1.default.gray('🔍 自分のメッセージのため重複を防ぐためスキップします')); return; } // 他人のメッセージをファイルに追加(最新7つを維持) const formattedMessage = (0, message_1.formatMessage)(message.username, message.content, message.created_at, isOwnMessage); await (0, file_1.appendMessageToFile)(codechatFile, formattedMessage); console.log(chalk_1.default.green('📝 メッセージをファイルに追加しました(履歴を維持)')); } catch (error) { console.error(chalk_1.default.red('メッセージ処理エラー:'), error.message); console.log(chalk_1.default.gray('受信したメッセージ:'), JSON.stringify(message, null, 2)); console.log(chalk_1.default.gray('エラースタック:'), error.stack); } }); // プロフィールURL応答 socket.on('profile-url-response', (data) => { if (!data || typeof data !== 'object' || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なプロフィール応答を受信しました')); return; } if (data.exists && data.url) { console.log(chalk_1.default.blue(`👤 ${data.username} のプロフィール: ${data.url}`)); if (!data.is_public) { console.log(chalk_1.default.yellow(' ⚠️ このプロフィールはプライベート設定です')); } } else { console.log(chalk_1.default.gray(`👤 ${data.username} のプロフィールは見つかりませんでした`)); } }); // ユーザー参加通知 socket.on('user-joined', (data) => { if (!data || typeof data !== 'object' || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なユーザー参加通知を受信しました')); return; } console.log(chalk_1.default.blue(`👋 ${data.username} がルームに参加しました`)); }); // ユーザー退出通知 socket.on('user-left', (data) => { if (!data || typeof data !== 'object' || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なユーザー退出通知を受信しました')); return; } console.log(chalk_1.default.yellow(`👋 ${data.username} がルームから退出しました`)); }); // タイピング状態 socket.on('user-typing', (data) => { if (!data || typeof data !== 'object' || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なタイピング状態を受信しました')); return; } if (data.is_typing) { console.log(chalk_1.default.gray(`✏️ ${data.username} が入力中...`)); } }); // 参加者一覧 socket.on('participants-list', (participants) => { if (!participants || !Array.isArray(participants)) { console.log(chalk_1.default.yellow('⚠️ 無効な参加者一覧を受信しました')); return; } try { const usernames = participants .filter(p => p && typeof p === 'object' && p.username) .map(p => p.username) .join(', '); console.log(chalk_1.default.cyan(`👥 参加者 (${participants.length}人): ${usernames || '参加者なし'}`)); console.log(chalk_1.default.gray('💡 ユーザー名をタップしてプロフィールを表示: kawazu profile <username>')); } catch (error) { console.error(chalk_1.default.red('参加者一覧の処理でエラーが発生しました:'), error.message); console.log(chalk_1.default.gray('受信した参加者データ:'), JSON.stringify(participants, null, 2)); } }); // ファイル共有関連のリスナー setupFileShareListeners(socket); } function setupFileShareListeners(socket) { // ファイル共有リクエスト受信 socket.on('file-share-request', (data) => { if (!data || !data.file_name || !data.owner_username || !data.share_token) { console.log(chalk_1.default.yellow('⚠️ 無効なファイル共有リクエストを受信しました')); return; } console.log(chalk_1.default.blue(`\n📤 ファイル共有リクエスト`)); console.log(chalk_1.default.cyan(`ファイル: ${data.file_name}`)); console.log(chalk_1.default.gray(`所有者: ${data.owner_username}`)); console.log(chalk_1.default.gray(`権限: ${data.permission_type === 'read' ? '読み取り専用' : '読み書き可能'}`)); if (data.expires_at) { console.log(chalk_1.default.gray(`有効期限: ${new Date(data.expires_at).toLocaleString()}`)); } console.log(chalk_1.default.green(`承認: kawazu approve ${data.share_token}`)); console.log(chalk_1.default.red(`拒否: kawazu deny ${data.share_token}`)); }); // ファイル共有承認通知 socket.on('file-share-approved', (data) => { if (!data || !data.file_name || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なファイル共有承認通知を受信しました')); return; } console.log(chalk_1.default.green(`\n✅ ファイル共有が承認されました`)); console.log(chalk_1.default.cyan(`ファイル: ${data.file_name}`)); console.log(chalk_1.default.gray(`承認者: ${data.username}`)); if (data.reason) { console.log(chalk_1.default.gray(`理由: ${data.reason}`)); } }); // ファイル共有拒否通知 socket.on('file-share-denied', (data) => { if (!data || !data.file_name || !data.username) { console.log(chalk_1.default.yellow('⚠️ 無効なファイル共有拒否通知を受信しました')); return; } console.log(chalk_1.default.red(`\n❌ ファイル共有が拒否されました`)); console.log(chalk_1.default.cyan(`ファイル: ${data.file_name}`)); console.log(chalk_1.default.gray(`拒否者: ${data.username}`)); if (data.reason) { console.log(chalk_1.default.gray(`理由: ${data.reason}`)); } }); // 共有ファイル更新通知 socket.on('shared-file-updated', (data) => { if (!data || !data.file_name || !data.updated_by || !data.updated_at) { console.log(chalk_1.default.yellow('⚠️ 無効な共有ファイル更新通知を受信しました')); return; } console.log(chalk_1.default.blue(`\n📝 共有ファイルが更新されました`)); console.log(chalk_1.default.cyan(`ファイル: ${data.file_name}`)); console.log(chalk_1.default.gray(`更新者: ${data.updated_by}`)); console.log(chalk_1.default.gray(`更新時刻: ${new Date(data.updated_at).toLocaleString()}`)); }); // ファイル共有作成成功 socket.on('file-share-created', (data) => { if (!data || !data.file_name || !data.share_token || !data.expires_at) { console.log(chalk_1.default.yellow('⚠️ 無効なファイル共有作成通知を受信しました')); return; } console.log(chalk_1.default.green(`\n✅ ファイル共有を作成しました`)); console.log(chalk_1.default.cyan(`ファイル: ${data.file_name}`)); console.log(chalk_1.default.gray(`トークン: ${data.share_token}`)); console.log(chalk_1.default.gray(`有効期限: ${new Date(data.expires_at).toLocaleString()}`)); }); } function setupFileWatcher(codechatFile, socket, roomId, username) { let lastContent = ''; const watcher = chokidar_1.default.watch(codechatFile, { persistent: true, ignoreInitial: true, usePolling: true, interval: 500 }); // 初期内容を読み込み (0, file_1.readFileContent)(codechatFile).then(content => { lastContent = content; console.log(chalk_1.default.gray('🔍 ファイル監視開始 - 初期ファイルサイズ:'), content.length); }); watcher.on('change', async () => { try { console.log(chalk_1.default.blue('🔍 ファイル変更検出')); // 短時間での連続変更を防ぐデバウンス処理 await new Promise(resolve => setTimeout(resolve, 200)); const currentContent = await (0, file_1.readFileContent)(codechatFile); console.log(chalk_1.default.gray('🔍 現在のファイルサイズ:'), currentContent.length); console.log(chalk_1.default.gray('🔍 前回のファイルサイズ:'), lastContent.length); // 内容が実質的に変わっていない場合はスキップ if (currentContent === lastContent) { console.log(chalk_1.default.gray('🔍 内容に変更がないためスキップ')); return; } // 新しく追加された内容を検出 const newContent = (0, message_1.extractNewContent)(currentContent, lastContent); console.log(chalk_1.default.gray('🔍 抽出された新しいコンテンツ:'), `"${newContent}"`); // デバッグ用:シンプルな差分検出も試してみる const simpleNewContent = currentContent.length > lastContent.length ? currentContent.substring(lastContent.length).trim() : ''; console.log(chalk_1.default.gray('🔍 シンプル差分検出結果:'), `"${simpleNewContent}"`); // 入力線以降の部分だけを比較する方法も試してみる const inputLineStart = '------------------------------------------------------------------------------>'; const currentInputIndex = currentContent.lastIndexOf(inputLineStart); const lastInputIndex = lastContent.lastIndexOf(inputLineStart); let inputAreaNewContent = ''; if (currentInputIndex !== -1 && lastInputIndex !== -1) { const currentInputSection = currentContent.substring(currentInputIndex); const lastInputSection = lastContent.substring(lastInputIndex); if (currentInputSection !== lastInputSection) { // 入力エリアに変更があった場合 const currentLines = currentInputSection.split('\n'); const lastLines = lastInputSection.split('\n'); // 新しい行を検出 if (currentLines.length > lastLines.length) { inputAreaNewContent = currentLines.slice(lastLines.length).join('\n').trim(); } } } console.log(chalk_1.default.gray('🔍 入力エリア差分検出結果:'), `"${inputAreaNewContent}"`); // いずれかの方法で新しいコンテンツが見つかった場合 const finalNewContent = newContent || inputAreaNewContent; if (finalNewContent) { console.log(chalk_1.default.green('🔍 新しいコンテンツを検出しました')); // 行ごとに処理 const lines = finalNewContent.split('\n'); console.log(chalk_1.default.gray('🔍 処理する行数:'), lines.length); for (const line of lines) { const trimmedLine = line.trim(); console.log(chalk_1.default.gray('🔍 処理中の行:'), `"${trimmedLine}"`); if (!trimmedLine) { console.log(chalk_1.default.gray('🔍 空行のためスキップ')); continue; } // ファイル共有コマンドをチェック if ((0, message_1.isFileShareCommand)(trimmedLine)) { console.log(chalk_1.default.blue('🔍 ファイル共有コマンドを検出')); await handleFileShareCommand(trimmedLine, socket, roomId, username); continue; } // 通常のメッセージとして送信 const sanitizedContent = (0, message_1.sanitizeMessage)(trimmedLine); console.log(chalk_1.default.gray('🔍 サニタイズ後のコンテンツ:'), `"${sanitizedContent}"`); if (sanitizedContent) { console.log(chalk_1.default.green('🔍 メッセージを送信中...')); console.log(chalk_1.default.gray('🔍 送信データ:'), { room_slug: roomId, username: username, content: sanitizedContent, message_type: (0, message_1.detectMessageType)(sanitizedContent) }); socket.emit('send-message', { room_slug: roomId, username: username, content: sanitizedContent, message_type: (0, message_1.detectMessageType)(sanitizedContent) }); console.log(chalk_1.default.green('✅ メッセージ送信完了')); // 自分のメッセージをファイルに追加 const ownFormattedMessage = (0, message_1.formatMessage)(username, sanitizedContent, new Date().toISOString(), true); await (0, file_1.appendMessageToFile)(codechatFile, ownFormattedMessage); console.log(chalk_1.default.green('📝 自分のメッセージをファイルに追加しました')); // メッセージ送信後に入力エリアをクリア setTimeout(async () => { console.log(chalk_1.default.gray('🔍 入力エリアをクリア中...')); // ファイル監視を一時的に停止 watcher.unwatch(codechatFile); await (0, file_1.clearInputArea)(codechatFile); // lastContentを更新してファイル監視のループを防ぐ lastContent = await (0, file_1.readFileContent)(codechatFile); // ファイル監視を再開 watcher.add(codechatFile); console.log(chalk_1.default.gray('✅ 入力エリアクリア完了')); }, 200); } else { console.log(chalk_1.default.yellow('🔍 サニタイズ後のコンテンツが空のため送信しません')); // メッセージを送信しなかった場合でもlastContentを更新 lastContent = currentContent; } } // メッセージを送信しなかった場合はlastContentを更新 lastContent = currentContent; } else { console.log(chalk_1.default.yellow('🔍 新しいコンテンツが見つかりませんでした')); console.log(chalk_1.default.gray('🔍 詳細:')); console.log(chalk_1.default.gray(' - extractNewContent:'), `"${newContent}"`); console.log(chalk_1.default.gray(' - inputAreaNewContent:'), `"${inputAreaNewContent}"`); // 新しいコンテンツがない場合もlastContentを更新 lastContent = currentContent; } } catch (error) { console.error(chalk_1.default.red('ファイル処理エラー:', error.message)); console.error(chalk_1.default.red('スタックトレース:'), error.stack); } }); // エラーハンドリング watcher.on('error', (error) => { console.error(chalk_1.default.red('ファイル監視エラー:'), error); }); console.log(chalk_1.default.green('📁 ファイル監視が開始されました')); console.log(chalk_1.default.gray('監視対象:'), codechatFile); // 終了処理 process.on('SIGINT', () => { console.log(chalk_1.default.yellow('\n💭 チャットを終了しています...')); watcher.close(); socket.disconnect(); console.log(chalk_1.default.green('👋 ありがとうございました!')); process.exit(0); }); process.on('SIGTERM', () => { watcher.close(); socket.disconnect(); process.exit(0); }); } async function promptUsername() { const { username } = await inquirer_1.default.prompt([ { type: 'input', name: 'username', message: 'ユーザー名を入力してください:', validate: (input) => { const trimmed = input.trim(); if (trimmed.length === 0) return 'ユーザー名を入力してください'; if (trimmed.length > 50) return 'ユーザー名は50文字以内で入力してください'; if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) return 'ユーザー名は英数字、ハイフン、アンダースコアのみ使用できます'; return true; } } ]); return username.trim(); } async function handleFileShareCommand(line, socket, roomId, username) { const command = (0, message_1.parseFileShareCommand)(line); if (!command) { console.log(chalk_1.default.red('❌ 無効なファイル共有コマンドです')); return; } try { switch (command.command) { case 'share': if (!command.filePath) { console.log(chalk_1.default.red('❌ ファイルパスが指定されていません')); return; } console.log(chalk_1.default.blue(`📤 ファイル共有リクエストを送信: ${command.filePath}`)); // 共有ファイルコマンドを実行 const { shareFile } = await Promise.resolve().then(() => __importStar(require('./share'))); await shareFile(command.filePath, { room: roomId, users: command.users?.join(','), permission: command.permission }); break; case 'approve': if (!command.token) { console.log(chalk_1.default.red('❌ トークンが指定されていません')); return; } console.log(chalk_1.default.green(`✅ ファイル共有を承認: ${command.token}`)); // Socket.IOで承認を送信 socket.emit('respond-file-share', { share_token: command.token, username, action: 'approve' }); break; case 'deny': if (!command.token) { console.log(chalk_1.default.red('❌ トークンが指定されていません')); return; } console.log(chalk_1.default.red(`❌ ファイル共有を拒否: ${command.token}`)); // Socket.IOで拒否を送信 socket.emit('respond-file-share', { share_token: command.token, username, action: 'deny' }); break; default: console.log(chalk_1.default.red('❌ 不明なファイル共有コマンドです')); } } catch (error) { console.error(chalk_1.default.red('ファイル共有コマンドエラー:'), error.message); } }