/** * 运行时 API:设备操作(adb)、日志、桩函数 * config 由主脚本传入:{ projectRoot, adbInteractPath } */ const path = require('path') const fs = require('fs') const { spawnSync } = require('child_process') const UTF8_BOM = Buffer.from('\uFEFF', 'utf8') function appendLog(folderPath, message) { if (!folderPath || typeof folderPath !== 'string') return Promise.resolve() const logDir = path.resolve(folderPath) const logFile = path.join(logDir, 'log.txt') try { fs.mkdirSync(logDir, { recursive: true }) const line = (typeof message === 'string' ? message : String(message)) + '\n' const exists = fs.existsSync(logFile) if (!exists || (exists && fs.statSync(logFile).size === 0)) fs.appendFileSync(logFile, UTF8_BOM) fs.appendFileSync(logFile, Buffer.from(line, 'utf8')) } catch (e) {} return Promise.resolve() } const _stub = (name) => ({ success: false, error: `${name} must be implemented in main process` }) function createElectronAPI(overrides = {}, config = {}) { const { projectRoot, adbInteractPath, nodeExePath } = config const nodeExe = (nodeExePath && nodeExePath !== 'node') ? nodeExePath : 'node' const runAdb = (action, args = [], deviceId = '') => { if (!adbInteractPath) return { success: false, error: 'adbInteractPath not configured' } const env = projectRoot ? { ...process.env, STATIC_ROOT: path.join(projectRoot, 'static') } : process.env const r = spawnSync(nodeExe, [adbInteractPath, action, ...args, deviceId], { encoding: 'utf-8', timeout: 10000, env }) const errStr = (r.stderr && String(r.stderr).trim()) || (r.error && r.error.message) || `exit code ${r.status}` return { success: r.status === 0, error: r.status === 0 ? undefined : errStr } } const sendTap = (device, x, y) => runAdb('tap', [String(x), String(y)], device) const sendSwipe = (device, x1, y1, x2, y2, duration) => runAdb('swipe-coords', [String(x1), String(y1), String(x2), String(y2), String(duration || 300)], device) const sendKeyEvent = (device, keyCode) => runAdb('keyevent', [String(keyCode)], device) const sendText = (device, text) => runAdb('text', [String(text)], device) let sendSystemKeyImpl = () => _stub('sendSystemKey') const adbSysBtnPath = adbInteractPath ? path.join(path.dirname(adbInteractPath), 'adb-sys-btn.js') : null if (adbSysBtnPath && fs.existsSync(adbSysBtnPath)) { try { const { sendSystemButton } = require(adbSysBtnPath) sendSystemKeyImpl = (device, keyCode) => sendSystemButton(String(keyCode), device) } catch (e) {} } return { sendTap, sendSwipe, sendKeyEvent, sendText, sendSystemKey: sendSystemKeyImpl, matchImageAndGetCoordinate: null, findTextAndGetCoordinate: () => _stub('findTextAndGetCoordinate'), appendLog, readTextFile: null, writeTextFile: null, saveChatHistory: () => _stub('saveChatHistory'), readChatHistory: () => _stub('readChatHistory'), readAllChatHistory: () => _stub('readAllChatHistory'), saveChatHistorySummary: () => _stub('saveChatHistorySummary'), getChatHistorySummary: () => _stub('getChatHistorySummary'), saveChatHistoryTxt: () => _stub('saveChatHistoryTxt'), extractChatHistory: () => _stub('extractChatHistory'), readLastMessage: () => _stub('readLastMessage'), ocrLastMessage: () => _stub('ocrLastMessage'), getCachedScreenshot: () => _stub('getCachedScreenshot'), captureScreenshot: () => _stub('captureScreenshot'), sendScroll: () => _stub('sendScroll'), matchImageRegionLocation: () => _stub('matchImageRegionLocation'), ...overrides, } } module.exports = { appendLog, createElectronAPI, }