desktop-commands.js
1 /** 2 * Shared command factories for Electron/desktop app adapters. 3 * Eliminates duplicate screenshot/status/new/dump implementations 4 * across cursor, codex, chatwise, etc. 5 */ 6 import * as fs from 'node:fs'; 7 import { cli, Strategy } from '@jackwener/opencli/registry'; 8 /** 9 * Factory: capture DOM HTML + accessibility snapshot. 10 */ 11 export function makeScreenshotCommand(site, displayName, extra = {}) { 12 const label = displayName ?? site; 13 return cli({ 14 ...extra, 15 site, 16 name: 'screenshot', 17 description: `Capture a snapshot of the current ${label} window (DOM + Accessibility tree)`, 18 domain: 'localhost', 19 strategy: Strategy.UI, 20 browser: true, 21 args: [ 22 { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` }, 23 ], 24 columns: ['Status', 'File'], 25 func: async (page, kwargs) => { 26 const outputPath = kwargs.output || `/tmp/${site}-snapshot.txt`; 27 const snap = await page.snapshot({ compact: true }); 28 const html = await page.evaluate('document.documentElement.outerHTML'); 29 const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html'; 30 const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt'; 31 fs.writeFileSync(htmlPath, html); 32 fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2)); 33 return [ 34 { Status: 'Success', File: htmlPath }, 35 { Status: 'Success', File: snapPath }, 36 ]; 37 }, 38 }); 39 } 40 /** 41 * Factory: check CDP connection status. 42 */ 43 export function makeStatusCommand(site, displayName, extra = {}) { 44 const label = displayName ?? site; 45 return cli({ 46 ...extra, 47 site, 48 name: 'status', 49 description: `Check active CDP connection to ${label}`, 50 domain: 'localhost', 51 strategy: Strategy.UI, 52 browser: true, 53 columns: ['Status', 'Url', 'Title'], 54 func: async (page) => { 55 const url = await page.evaluate('window.location.href'); 56 const title = await page.evaluate('document.title'); 57 return [{ Status: 'Connected', Url: url, Title: title }]; 58 }, 59 }); 60 } 61 /** 62 * Factory: start a new session via Cmd/Ctrl+N. 63 */ 64 export function makeNewCommand(site, displayName, extra = {}) { 65 const label = displayName ?? site; 66 return cli({ 67 ...extra, 68 site, 69 name: 'new', 70 description: `Start a new ${label} session`, 71 domain: 'localhost', 72 strategy: Strategy.UI, 73 browser: true, 74 columns: ['Status'], 75 func: async (page) => { 76 const isMac = process.platform === 'darwin'; 77 await page.pressKey(isMac ? 'Meta+N' : 'Control+N'); 78 await page.wait(1); 79 return [{ Status: 'Success' }]; 80 }, 81 }); 82 } 83 /** 84 * Factory: dump DOM + snapshot for reverse-engineering. 85 */ 86 export function makeDumpCommand(site) { 87 return cli({ 88 site, 89 name: 'dump', 90 description: `Dump the DOM and Accessibility tree of ${site} for reverse-engineering`, 91 domain: 'localhost', 92 strategy: Strategy.UI, 93 browser: true, 94 columns: ['action', 'files'], 95 func: async (page) => { 96 const dom = await page.evaluate('document.body.innerHTML'); 97 fs.writeFileSync(`/tmp/${site}-dom.html`, dom); 98 const snap = await page.snapshot({ interactive: false }); 99 fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(snap, null, 2)); 100 return [ 101 { 102 action: 'Dom extraction finished', 103 files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json`, 104 }, 105 ]; 106 }, 107 }); 108 }