mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-16 22:30:45 +08:00
Add Desktop CDP helpers for local plugin enablement.
Provide a minimal remote-debugging client and a one-shot enable script for linked Desktop plugins. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
838ff2fed4
commit
217e1f781a
2 changed files with 392 additions and 0 deletions
86
scripts/_enable-local-plugins.mjs
Normal file
86
scripts/_enable-local-plugins.mjs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* 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))
|
||||
Loading…
Add table
Add a link
Reference in a new issue