runtime-api.js 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /**
  2. * 运行时 API:设备操作(adb)、日志、桩函数
  3. * config 由主脚本传入:{ projectRoot, adbInteractPath }
  4. */
  5. const path = require('path')
  6. const fs = require('fs')
  7. const { spawnSync } = require('child_process')
  8. const UTF8_BOM = Buffer.from('\uFEFF', 'utf8')
  9. function appendLog(folderPath, message) {
  10. if (!folderPath || typeof folderPath !== 'string') return Promise.resolve()
  11. const logDir = path.resolve(folderPath)
  12. const logFile = path.join(logDir, 'log.txt')
  13. try {
  14. fs.mkdirSync(logDir, { recursive: true })
  15. const line = (typeof message === 'string' ? message : String(message)) + '\n'
  16. const exists = fs.existsSync(logFile)
  17. if (!exists || (exists && fs.statSync(logFile).size === 0)) fs.appendFileSync(logFile, UTF8_BOM)
  18. fs.appendFileSync(logFile, Buffer.from(line, 'utf8'))
  19. } catch (e) {}
  20. return Promise.resolve()
  21. }
  22. const _stub = (name) => ({ success: false, error: `${name} must be implemented in main process` })
  23. function createElectronAPI(overrides = {}, config = {}) {
  24. const { projectRoot, adbInteractPath, nodeExePath } = config
  25. const nodeExe = (nodeExePath && nodeExePath !== 'node') ? nodeExePath : 'node'
  26. const runAdb = (action, args = [], deviceId = '') => {
  27. if (!adbInteractPath) return { success: false, error: 'adbInteractPath not configured' }
  28. const env = projectRoot ? { ...process.env, STATIC_ROOT: path.join(projectRoot, 'static') } : process.env
  29. const r = spawnSync(nodeExe, [adbInteractPath, action, ...args, deviceId], { encoding: 'utf-8', timeout: 10000, env })
  30. const errStr = (r.stderr && String(r.stderr).trim()) || (r.error && r.error.message) || `exit code ${r.status}`
  31. return { success: r.status === 0, error: r.status === 0 ? undefined : errStr }
  32. }
  33. const sendTap = (device, x, y) => runAdb('tap', [String(x), String(y)], device)
  34. const sendSwipe = (device, x1, y1, x2, y2, duration) =>
  35. runAdb('swipe-coords', [String(x1), String(y1), String(x2), String(y2), String(duration || 300)], device)
  36. const sendKeyEvent = (device, keyCode) => runAdb('keyevent', [String(keyCode)], device)
  37. const sendText = (device, text) => runAdb('text', [String(text)], device)
  38. let sendSystemKeyImpl = () => _stub('sendSystemKey')
  39. const adbSysBtnPath = adbInteractPath ? path.join(path.dirname(adbInteractPath), 'adb-sys-btn.js') : null
  40. if (adbSysBtnPath && fs.existsSync(adbSysBtnPath)) {
  41. try {
  42. const { sendSystemButton } = require(adbSysBtnPath)
  43. sendSystemKeyImpl = (device, keyCode) => sendSystemButton(String(keyCode), device)
  44. } catch (e) {}
  45. }
  46. return {
  47. sendTap,
  48. sendSwipe,
  49. sendKeyEvent,
  50. sendText,
  51. sendSystemKey: sendSystemKeyImpl,
  52. matchImageAndGetCoordinate: null,
  53. findTextAndGetCoordinate: () => _stub('findTextAndGetCoordinate'),
  54. appendLog,
  55. readTextFile: null,
  56. writeTextFile: null,
  57. saveChatHistory: () => _stub('saveChatHistory'),
  58. readChatHistory: () => _stub('readChatHistory'),
  59. readAllChatHistory: () => _stub('readAllChatHistory'),
  60. saveChatHistorySummary: () => _stub('saveChatHistorySummary'),
  61. getChatHistorySummary: () => _stub('getChatHistorySummary'),
  62. saveChatHistoryTxt: () => _stub('saveChatHistoryTxt'),
  63. extractChatHistory: () => _stub('extractChatHistory'),
  64. readLastMessage: () => _stub('readLastMessage'),
  65. ocrLastMessage: () => _stub('ocrLastMessage'),
  66. getCachedScreenshot: () => _stub('getCachedScreenshot'),
  67. captureScreenshot: () => _stub('captureScreenshot'),
  68. sendScroll: () => _stub('sendScroll'),
  69. matchImageRegionLocation: () => _stub('matchImageRegionLocation'),
  70. ...overrides,
  71. }
  72. }
  73. module.exports = {
  74. appendLog,
  75. createElectronAPI,
  76. }