From e7692e6b8225606c5da525a5c8b7dbb56607d89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9C=B1?= <10714957+xiao-zhu245@user.noreply.gitee.com> Date: Sat, 12 Jul 2025 11:02:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=A2=9E=E5=8A=A0=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/plugins/example-plugin/gsm3-api.js | 165 ++++++ server/data/plugins/example-plugin/index.html | 551 ++++++++++++++++++ .../src/modules/terminal/TerminalManager.ts | 12 +- server/src/routes/pluginApi.ts | 286 ++++++++- 4 files changed, 998 insertions(+), 16 deletions(-) diff --git a/server/data/plugins/example-plugin/gsm3-api.js b/server/data/plugins/example-plugin/gsm3-api.js index 78910f3..b1b88b8 100644 --- a/server/data/plugins/example-plugin/gsm3-api.js +++ b/server/data/plugins/example-plugin/gsm3-api.js @@ -140,6 +140,82 @@ class GSM3API { return await this.request(`/instances/${instanceId}/status`) } + /** + * 创建新实例 + * @param {Object} instanceData 实例数据 + * @param {string} instanceData.name 实例名称 + * @param {string} instanceData.description 实例描述 + * @param {string} instanceData.workingDirectory 工作目录 + * @param {string} instanceData.startCommand 启动命令 + * @param {boolean} instanceData.autoStart 是否自动启动 + * @param {string} instanceData.stopCommand 停止命令 + */ + async createInstance(instanceData) { + return await this.request('/instances', { + method: 'POST', + body: instanceData + }) + } + + /** + * 更新实例 + * @param {string} instanceId 实例ID + * @param {Object} instanceData 实例数据 + */ + async updateInstance(instanceId, instanceData) { + return await this.request(`/instances/${instanceId}`, { + method: 'PUT', + body: instanceData + }) + } + + /** + * 删除实例 + * @param {string} instanceId 实例ID + */ + async deleteInstance(instanceId) { + return await this.request(`/instances/${instanceId}`, { + method: 'DELETE' + }) + } + + /** + * 启动实例 + * @param {string} instanceId 实例ID + */ + async startInstance(instanceId) { + return await this.request(`/instances/${instanceId}/start`, { + method: 'POST' + }) + } + + /** + * 停止实例 + * @param {string} instanceId 实例ID + */ + async stopInstance(instanceId) { + return await this.request(`/instances/${instanceId}/stop`, { + method: 'POST' + }) + } + + /** + * 重启实例 + * @param {string} instanceId 实例ID + */ + async restartInstance(instanceId) { + return await this.request(`/instances/${instanceId}/restart`, { + method: 'POST' + }) + } + + /** + * 获取实例市场列表 + */ + async getMarketInstances() { + return await this.request('/instances/market') + } + // ==================== 终端管理API ==================== /** @@ -149,6 +225,95 @@ class GSM3API { return await this.request('/terminals') } + /** + * 获取终端会话统计信息 + */ + async getTerminalStats() { + return await this.request('/terminals/stats', { + method: 'GET' + }) + } + + /** + * 获取终端会话详细信息 + */ + async getTerminalSessions() { + return await this.request('/terminals/sessions', { + method: 'GET' + }) + } + + /** + * 获取活跃终端进程信息 + */ + async getActiveTerminalProcesses() { + return await this.request('/terminals/active-processes', { + method: 'GET' + }) + } + + /** + * 更新终端会话名称 + * @param {string} sessionId 会话ID + * @param {string} name 新的会话名称 + */ + async updateTerminalSessionName(sessionId, name) { + return await this.request(`/terminals/sessions/${sessionId}/name`, { + method: 'PUT', + body: { name } + }) + } + + /** + * 验证终端配置 + * @param {string} workingDirectory 工作目录 + * @param {string} shell Shell程序路径 + */ + async validateTerminalConfig(workingDirectory, shell) { + return await this.request('/terminals/validate-config', { + method: 'POST', + body: { workingDirectory, shell } + }) + } + + /** + * 获取系统默认Shell信息 + */ + async getDefaultShell() { + return await this.request('/terminals/default-shell', { + method: 'GET' + }) + } + + /** + * 获取终端主题配置 + */ + async getTerminalThemes() { + return await this.request('/terminals/themes', { + method: 'GET' + }) + } + + /** + * 获取终端字体配置 + */ + async getTerminalFonts() { + return await this.request('/terminals/fonts', { + method: 'GET' + }) + } + + /** + * 测试终端连接 + * @param {string} workingDirectory 工作目录 + */ + async testTerminalConnection(workingDirectory) { + return await this.request('/terminals/test-connection', { + method: 'POST', + body: { workingDirectory } + }) + } + // ==================== 游戏管理API ==================== /** diff --git a/server/data/plugins/example-plugin/index.html b/server/data/plugins/example-plugin/index.html index 21ac184..950879b 100644 --- a/server/data/plugins/example-plugin/index.html +++ b/server/data/plugins/example-plugin/index.html @@ -339,6 +339,85 @@ +
+

🖥️ 实例管理演示

+

体验插件实例管理API的功能:

+ +
+ + + + +
+ +
+ + + + + +
+ + + +
+ +
+

💻 终端管理演示

+

体验插件终端管理API的功能:

+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + +
+
+

📊 系统信息

@@ -811,6 +890,478 @@ } } + // ==================== 实例管理函数 ==================== + + // 显示创建实例表单 + function showCreateInstanceForm() { + document.getElementById('createInstanceForm').style.display = 'block' + } + + // 隐藏创建实例表单 + function hideCreateInstanceForm() { + document.getElementById('createInstanceForm').style.display = 'none' + // 清空表单 + document.getElementById('instanceName').value = '' + document.getElementById('instanceDescription').value = '' + document.getElementById('instanceWorkingDir').value = '' + document.getElementById('instanceStartCommand').value = '' + document.getElementById('instanceStopCommand').value = 'ctrl+c' + document.getElementById('instanceAutoStart').checked = false + } + + // 创建实例 + async function createInstance() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const name = document.getElementById('instanceName').value + const description = document.getElementById('instanceDescription').value + const workingDirectory = document.getElementById('instanceWorkingDir').value + const startCommand = document.getElementById('instanceStartCommand').value + const stopCommand = document.getElementById('instanceStopCommand').value + const autoStart = document.getElementById('instanceAutoStart').checked + + if (!name || !workingDirectory || !startCommand) { + showApiError('创建实例失败', new Error('请填写实例名称、工作目录和启动命令')) + return + } + + const instanceData = { + name: name.trim(), + description: description.trim(), + workingDirectory: workingDirectory.trim(), + startCommand: startCommand.trim(), + stopCommand: stopCommand, + autoStart: autoStart + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在创建实例...') + const result = await window.gsm3.createInstance(instanceData) + showApiResult('创建实例成功', result) + hideCreateInstanceForm() + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例创建成功') + } + } catch (error) { + showApiError('创建实例失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '创建实例失败: ' + error.message) + } + } + } + + // 获取市场实例 + async function getMarketInstances() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取市场实例列表...') + const result = await window.gsm3.getMarketInstances() + showApiResult('市场实例列表', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '市场实例列表获取成功') + } + } catch (error) { + showApiError('获取市场实例列表失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取市场实例列表失败: ' + error.message) + } + } + } + + // 获取实例详情 + async function getInstanceDetails() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const instanceId = document.getElementById('instanceIdInput').value + if (!instanceId) { + showApiError('获取实例详情失败', new Error('请输入实例ID')) + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取实例详情...') + const result = await window.gsm3.getInstance(instanceId) + showApiResult(`实例详情: ${instanceId}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例详情获取成功') + } + } catch (error) { + showApiError('获取实例详情失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取实例详情失败: ' + error.message) + } + } + } + + // 启动实例 + async function startInstance() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const instanceId = document.getElementById('instanceIdInput').value + if (!instanceId) { + showApiError('启动实例失败', new Error('请输入实例ID')) + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在启动实例...') + const result = await window.gsm3.startInstance(instanceId) + showApiResult(`启动实例: ${instanceId}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例启动成功') + } + } catch (error) { + showApiError('启动实例失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '启动实例失败: ' + error.message) + } + } + } + + // 停止实例 + async function stopInstance() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const instanceId = document.getElementById('instanceIdInput').value + if (!instanceId) { + showApiError('停止实例失败', new Error('请输入实例ID')) + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在停止实例...') + const result = await window.gsm3.stopInstance(instanceId) + showApiResult(`停止实例: ${instanceId}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例停止成功') + } + } catch (error) { + showApiError('停止实例失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '停止实例失败: ' + error.message) + } + } + } + + // 重启实例 + async function restartInstance() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const instanceId = document.getElementById('instanceIdInput').value + if (!instanceId) { + showApiError('重启实例失败', new Error('请输入实例ID')) + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在重启实例...') + const result = await window.gsm3.restartInstance(instanceId) + showApiResult(`重启实例: ${instanceId}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例重启成功') + } + } catch (error) { + showApiError('重启实例失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '重启实例失败: ' + error.message) + } + } + } + + // 删除实例 + async function deleteInstance() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const instanceId = document.getElementById('instanceIdInput').value + if (!instanceId) { + showApiError('删除实例失败', new Error('请输入实例ID')) + return + } + + if (!confirm(`确定要删除实例 ${instanceId} 吗?此操作不可撤销!`)) { + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在删除实例...') + const result = await window.gsm3.deleteInstance(instanceId) + showApiResult(`删除实例: ${instanceId}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '实例删除成功') + } + } catch (error) { + showApiError('删除实例失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '删除实例失败: ' + error.message) + } + } + } + + // ==================== 终端管理函数 ==================== + + // 获取终端统计信息 + async function getTerminalStats() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取终端统计信息...') + const result = await window.gsm3.getTerminalStats() + showApiResult('终端统计信息', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端统计信息获取成功') + } + } catch (error) { + showApiError('获取终端统计信息失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取终端统计信息失败: ' + error.message) + } + } + } + + // 获取终端会话列表 + async function getTerminalSessions() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取终端会话列表...') + const result = await window.gsm3.getTerminalSessions() + showApiResult('终端会话列表', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端会话列表获取成功') + } + } catch (error) { + showApiError('获取终端会话列表失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取终端会话列表失败: ' + error.message) + } + } + } + + // 获取活跃终端进程 + async function getActiveTerminalProcesses() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取活跃终端进程...') + const result = await window.gsm3.getActiveTerminalProcesses() + showApiResult('活跃终端进程', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '活跃终端进程获取成功') + } + } catch (error) { + showApiError('获取活跃终端进程失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取活跃终端进程失败: ' + error.message) + } + } + } + + // 获取默认Shell + async function getDefaultShell() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取默认Shell信息...') + const result = await window.gsm3.getDefaultShell() + showApiResult('默认Shell信息', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '默认Shell信息获取成功') + } + } catch (error) { + showApiError('获取默认Shell信息失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取默认Shell信息失败: ' + error.message) + } + } + } + + // 获取终端主题 + async function getTerminalThemes() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取终端主题配置...') + const result = await window.gsm3.getTerminalThemes() + showApiResult('终端主题配置', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端主题配置获取成功') + } + } catch (error) { + showApiError('获取终端主题配置失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取终端主题配置失败: ' + error.message) + } + } + } + + // 获取终端字体 + async function getTerminalFonts() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在获取终端字体配置...') + const result = await window.gsm3.getTerminalFonts() + showApiResult('终端字体配置', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端字体配置获取成功') + } + } catch (error) { + showApiError('获取终端字体配置失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '获取终端字体配置失败: ' + error.message) + } + } + } + + // 验证终端配置 + async function validateTerminalConfig() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在验证终端配置...') + const workingDirectory = process.cwd ? process.cwd() : '/' + const shell = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash' + const result = await window.gsm3.validateTerminalConfig(workingDirectory, shell) + showApiResult('验证终端配置', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端配置验证成功') + } + } catch (error) { + showApiError('验证终端配置失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '验证终端配置失败: ' + error.message) + } + } + } + + // 测试终端连接 + async function testTerminalConnection() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在测试终端连接...') + const workingDirectory = process.cwd ? process.cwd() : '/' + const result = await window.gsm3.testTerminalConnection(workingDirectory) + showApiResult('测试终端连接', result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端连接测试成功') + } + } catch (error) { + showApiError('测试终端连接失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '测试终端连接失败: ' + error.message) + } + } + } + + // 更新终端会话名称 + async function updateTerminalSessionName() { + try { + if (!window.gsm3) { + throw new Error('GSM3 API对象未找到') + } + + const sessionId = document.getElementById('sessionIdInput').value + const sessionName = document.getElementById('sessionNameInput').value + + if (!sessionId || !sessionName) { + showApiError('更新会话名称失败', new Error('请输入会话ID和新名称')) + return + } + + showApiLoading('正在初始化API...') + await window.gsm3.initialize() + + showApiLoading('正在更新终端会话名称...') + const result = await window.gsm3.updateTerminalSessionName(sessionId, sessionName) + showApiResult(`更新会话名称: ${sessionId} -> ${sessionName}`, result) + if (window.gsm3.showNotification) { + window.gsm3.showNotification('success', '终端会话名称更新成功') + } + } catch (error) { + showApiError('更新终端会话名称失败', error) + if (window.gsm3 && window.gsm3.showNotification) { + window.gsm3.showNotification('error', '更新终端会话名称失败: ' + error.message) + } + } + } + // 控制台欢迎信息 console.log('%c🧩 GSM3 插件系统', 'color: #667eea; font-size: 20px; font-weight: bold;') console.log('%c欢迎使用示例插件!', 'color: #764ba2; font-size: 14px;') diff --git a/server/src/modules/terminal/TerminalManager.ts b/server/src/modules/terminal/TerminalManager.ts index 1a10ec1..6dfeb57 100644 --- a/server/src/modules/terminal/TerminalManager.ts +++ b/server/src/modules/terminal/TerminalManager.ts @@ -63,16 +63,16 @@ export class TerminalManager { const arch = os.arch() if (platform === 'win32') { - // this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_win32_x64.exe') - this.ptyPath = path.resolve(__dirname, '../../PTY/pty_win32_x64.exe') + this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_win32_x64.exe') + // this.ptyPath = path.resolve(__dirname, '../../PTY/pty_win32_x64.exe') } else { // Linux平台根据架构选择对应的PTY文件 if (arch === 'arm64' || arch === 'aarch64') { - // this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_linux_arm64') - this.ptyPath = path.resolve(__dirname, '../../PTY/pty_linux_arm64') + this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_linux_arm64') + // this.ptyPath = path.resolve(__dirname, '../../PTY/pty_linux_arm64') } else { - // this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_linux_x64') - this.ptyPath = path.resolve(__dirname, '../../PTY/pty_linux_x64') + this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_linux_x64') + // this.ptyPath = path.resolve(__dirname, '../../PTY/pty_linux_x64') } } diff --git a/server/src/routes/pluginApi.ts b/server/src/routes/pluginApi.ts index 9e75089..078ec4c 100644 --- a/server/src/routes/pluginApi.ts +++ b/server/src/routes/pluginApi.ts @@ -5,6 +5,7 @@ import type { SystemManager } from '../modules/system/SystemManager.js' import type { TerminalManager } from '../modules/terminal/TerminalManager.js' import type { GameManager } from '../modules/game/GameManager.js' import filesRouter from './files.js' +import { setupTerminalRoutes } from './terminal.js' import logger from '../utils/logger.js' const router = Router() @@ -200,33 +201,298 @@ router.get('/instances/:id/status', async (req: Request, res: Response) => { } }) -// ==================== 终端管理API ==================== - -// 获取终端会话列表 -router.get('/terminals', async (req: Request, res: Response) => { +// 获取市场实例列表 +router.get('/instances/market', async (req: Request, res: Response) => { try { - if (!terminalManager) { + if (!instanceManager) { return res.status(503).json({ success: false, - message: '终端管理器未初始化' + message: '实例管理器未初始化' }) } - const terminals = terminalManager.getSessionStats() + const marketInstances = instanceManager.getMarketInstances() res.json({ success: true, - data: terminals + data: marketInstances }) } catch (error) { - logger.error('插件获取终端列表失败:', error) + logger.error('插件获取市场实例列表失败:', error) res.status(500).json({ success: false, - message: '获取终端列表失败', + message: '获取市场实例列表失败', error: error instanceof Error ? error.message : '未知错误' }) } }) +// 创建实例 +router.post('/instances', async (req: Request, res: Response) => { + try { + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const { name, description, workingDirectory, startCommand, stopCommand, autoStart } = req.body + + // 验证必填字段 + if (!name || !workingDirectory || !startCommand) { + return res.status(400).json({ + success: false, + message: '缺少必填字段: name, workingDirectory, startCommand' + }) + } + + // 验证停止命令 + const validStopCommands = ['ctrl+c', 'stop', 'quit', 'exit'] + if (stopCommand && !validStopCommands.includes(stopCommand)) { + return res.status(400).json({ + success: false, + message: `无效的停止命令。支持的命令: ${validStopCommands.join(', ')}` + }) + } + + const instanceData = { + name: name.trim(), + description: description?.trim() || '', + workingDirectory: workingDirectory.trim(), + startCommand: startCommand.trim(), + stopCommand: stopCommand || 'ctrl+c', + autoStart: autoStart || false + } + + const result = await instanceManager.createInstance(instanceData) + res.status(201).json({ + success: true, + data: result, + message: '实例创建成功' + }) + } catch (error) { + logger.error('插件创建实例失败:', error) + res.status(500).json({ + success: false, + message: '创建实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// 更新实例 +router.put('/instances/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params + + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const instance = instanceManager.getInstance(id) + if (!instance) { + return res.status(404).json({ + success: false, + message: '实例不存在' + }) + } + + const { name, description, workingDirectory, startCommand, stopCommand, autoStart } = req.body + + // 验证必填字段 + if (!name || !workingDirectory || !startCommand) { + return res.status(400).json({ + success: false, + message: '缺少必填字段: name, workingDirectory, startCommand' + }) + } + + // 验证停止命令 + const validStopCommands = ['ctrl+c', 'stop', 'quit', 'exit'] + if (stopCommand && !validStopCommands.includes(stopCommand)) { + return res.status(400).json({ + success: false, + message: `无效的停止命令。支持的命令: ${validStopCommands.join(', ')}` + }) + } + + const instanceData = { + name: name.trim(), + description: description?.trim() || '', + workingDirectory: workingDirectory.trim(), + startCommand: startCommand.trim(), + stopCommand: stopCommand || 'ctrl+c', + autoStart: autoStart || false + } + + const result = await instanceManager.updateInstance(id, instanceData) + res.json({ + success: true, + data: result, + message: '实例更新成功' + }) + } catch (error) { + logger.error('插件更新实例失败:', error) + res.status(500).json({ + success: false, + message: '更新实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// 删除实例 +router.delete('/instances/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params + + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const instance = instanceManager.getInstance(id) + if (!instance) { + return res.status(404).json({ + success: false, + message: '实例不存在' + }) + } + + await instanceManager.deleteInstance(id) + res.json({ + success: true, + message: '实例删除成功' + }) + } catch (error) { + logger.error('插件删除实例失败:', error) + res.status(500).json({ + success: false, + message: '删除实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// 启动实例 +router.post('/instances/:id/start', async (req: Request, res: Response) => { + try { + const { id } = req.params + + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const instance = instanceManager.getInstance(id) + if (!instance) { + return res.status(404).json({ + success: false, + message: '实例不存在' + }) + } + + const result = await instanceManager.startInstance(id) + res.json({ + success: true, + data: result, + message: '实例启动成功' + }) + } catch (error) { + logger.error('插件启动实例失败:', error) + res.status(500).json({ + success: false, + message: '启动实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// 停止实例 +router.post('/instances/:id/stop', async (req: Request, res: Response) => { + try { + const { id } = req.params + + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const instance = instanceManager.getInstance(id) + if (!instance) { + return res.status(404).json({ + success: false, + message: '实例不存在' + }) + } + + const result = await instanceManager.stopInstance(id) + res.json({ + success: true, + data: result, + message: '实例停止成功' + }) + } catch (error) { + logger.error('插件停止实例失败:', error) + res.status(500).json({ + success: false, + message: '停止实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// 重启实例 +router.post('/instances/:id/restart', async (req: Request, res: Response) => { + try { + const { id } = req.params + + if (!instanceManager) { + return res.status(503).json({ + success: false, + message: '实例管理器未初始化' + }) + } + + const instance = instanceManager.getInstance(id) + if (!instance) { + return res.status(404).json({ + success: false, + message: '实例不存在' + }) + } + + const result = await instanceManager.restartInstance(id) + res.json({ + success: true, + data: result, + message: '实例重启成功' + }) + } catch (error) { + logger.error('插件重启实例失败:', error) + res.status(500).json({ + success: false, + message: '重启实例失败', + error: error instanceof Error ? error.message : '未知错误' + }) + } +}) + +// ==================== 终端管理API ==================== + +// 转发终端操作请求到terminal路由 +const terminalRouter = setupTerminalRoutes(terminalManager) +router.use('/terminals', terminalRouter) + // ==================== 游戏管理API ==================== // 获取游戏列表