mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-16 10:20:45 +08:00
Provide a minimal remote-debugging client and a one-shot enable script for linked Desktop plugins. Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
3 KiB
JavaScript
86 lines
3 KiB
JavaScript
/**
|
|
* Enable local linked plugins via Desktop CDP (one plugin per eval).
|
|
* Usage: node _enable-local-plugins.mjs
|
|
*/
|
|
import { spawn } from 'node:child_process'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const cdp = path.join(__dirname, '_desktop-cdp.mjs')
|
|
const NAMES = [
|
|
'dsh-search-mcp',
|
|
'dsh-trilium',
|
|
'uds-auth',
|
|
'dsh-ops-cron',
|
|
'dsh-netxops',
|
|
'dsh-im-ops',
|
|
'dsh-mind-map',
|
|
]
|
|
|
|
function runEval(code, timeoutMs = 90_000) {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(process.execPath, [cdp, 'eval', code], {
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
windowsHide: true,
|
|
})
|
|
let out = ''
|
|
let err = ''
|
|
const t = setTimeout(() => {
|
|
child.kill()
|
|
reject(new Error(`timeout after ${timeoutMs}ms: ${err || out}`))
|
|
}, timeoutMs)
|
|
child.stdout.on('data', (d) => { out += d })
|
|
child.stderr.on('data', (d) => { err += d })
|
|
child.on('close', (code) => {
|
|
clearTimeout(t)
|
|
if (code !== 0) reject(new Error(err || out || `exit ${code}`))
|
|
else {
|
|
try { resolve(JSON.parse(out)) } catch { resolve(out) }
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
function enableOne(name) {
|
|
// Keep under CDP 60s eval timeout
|
|
return runEval(`(async () => {
|
|
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
const byAria = a => [...document.querySelectorAll('button')].find(e => (e.getAttribute('aria-label')||'') === a)
|
|
const clickText = t => {
|
|
const b = [...document.querySelectorAll('button')].find(e => (e.innerText||'').trim() === t)
|
|
if (b) { b.click(); return true }
|
|
return false
|
|
}
|
|
byAria('插件')?.click(); await sleep(800)
|
|
const steps = []
|
|
const en = byAria('启用 ' + ${JSON.stringify(name)})
|
|
if (en) { en.click(); steps.push('启用'); await sleep(2500) }
|
|
byAria('查看 ' + ${JSON.stringify(name)})?.click(); await sleep(900)
|
|
if (clickText('立即启用')) { steps.push('立即启用'); await sleep(2800) }
|
|
byAria('返回插件列表')?.click(); await sleep(350)
|
|
byAria('查看 ' + ${JSON.stringify(name)})?.click(); await sleep(700)
|
|
const t = document.body.innerText || ''
|
|
const idx = t.indexOf(${JSON.stringify(name)})
|
|
const bit = idx >= 0 ? t.slice(idx, idx + 280) : ''
|
|
const running = /运行中/.test(bit)
|
|
const enableStill = !!byAria('启用 ' + ${JSON.stringify(name)})
|
|
byAria('返回插件列表')?.click()
|
|
return { name: ${JSON.stringify(name)}, steps, running, enableStill, bit: bit.slice(0, 200) }
|
|
})()`, 70_000)
|
|
}
|
|
|
|
const results = []
|
|
for (const name of NAMES) {
|
|
try {
|
|
const r = await enableOne(name)
|
|
results.push(r)
|
|
console.log(JSON.stringify(r))
|
|
} catch (e) {
|
|
results.push({ name, error: String(e.message || e).slice(0, 300) })
|
|
console.error(name, e.message || e)
|
|
}
|
|
}
|
|
|
|
const cron = await runEval(`(() => ({ hasCron: /定时任务/.test(document.body.innerText||''), head: (document.body.innerText||'').slice(0,120) }))()`)
|
|
console.log(JSON.stringify({ results, cron }, null, 2))
|