Files
gnome-jarvis/computer-use/atspi.js
T
snxraven 0312fedaa7
Rolling release / release (push) Successful in 6m33s
Fixes
2026-09-12 11:26:19 -04:00

30 lines
1.9 KiB
JavaScript

import { spawn } from 'node:child_process';
function runHelper({ spawnImpl, python, helper, args, timeoutMs, label }) {
return new Promise((resolve, reject) => {
const child = spawnImpl(python, [helper, ...args], { stdio: ['ignore', 'pipe', 'pipe'] });
let out = ''; let err = ''; let settled = false;
const timer = timeoutMs > 0 ? setTimeout(() => { if (settled) return; settled = true; child.kill('SIGTERM'); setTimeout(() => child.kill('SIGKILL'), 500); reject(new Error(`${label} timed out`)); }, timeoutMs) : null;
const done = (fn) => (value) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); fn(value); };
child.stdout.on('data', (d) => { out += d; });
child.stderr.on('data', (d) => { err += d; });
child.on('error', done(reject));
child.on('close', (code) => done(code === 0 ? resolve : reject)(code === 0 ? out : new Error(err.trim() || `${label} exited ${code}`)));
});
}
export class AtspiProvider {
constructor({ python = 'python3', helper = new URL('./py/atspi_snapshot.py', import.meta.url).pathname, spawnImpl = spawn, timeoutMs = 5000 } = {}) {
this.python = python; this.helper = helper; this.spawnImpl = spawnImpl; this.timeoutMs = timeoutMs;
}
async tree({ focusedOnly = true, maxNodes = 400 } = {}) {
const out = await runHelper({ spawnImpl: this.spawnImpl, python: this.python, helper: this.helper, args: [focusedOnly ? 'focused' : 'desktop', String(Math.min(400, maxNodes))], timeoutMs: this.timeoutMs, label: 'AT-SPI' });
return JSON.parse(String(out));
}
async action(target, action) {
const helper = this.helper.replace('atspi_snapshot.py', 'atspi_action.py');
const out = await runHelper({ spawnImpl: this.spawnImpl, python: this.python, helper, args: [JSON.stringify(target), String(action)], timeoutMs: this.timeoutMs, label: 'AT-SPI action' });
return JSON.parse(String(out));
}
}