Updates
Rolling release / release (push) Successful in 8m31s

This commit is contained in:
2026-09-13 22:24:14 -04:00
parent 1ca4224377
commit a097adf4eb
62 changed files with 2707 additions and 957 deletions
+61
View File
@@ -0,0 +1,61 @@
import { spawn } from 'node:child_process';
import path from 'node:path';
export class PortalCamera {
constructor({
helper = path.resolve(new URL('./py/portal_camera.py', import.meta.url).pathname),
python = 'python3',
spawnImpl = spawn,
tmpDir = '/tmp/jarvis-webcam',
timeoutMs = 8000,
accessTimeoutMs = 120_000,
} = {}) {
this.helper = helper;
this.python = python;
this.spawnImpl = spawnImpl;
this.tmpDir = tmpDir;
this.timeoutMs = timeoutMs;
this.accessTimeoutMs = accessTimeoutMs;
}
access({ device = '' } = {}) {
return this._run(['access'], this.accessTimeoutMs, device);
}
capture(output, { device = '' } = {}) {
return this._run(['capture', output], this.timeoutMs, device);
}
_run(args, timeoutMs, device) {
return new Promise((resolve, reject) => {
const child = this.spawnImpl(this.python, [this.helper, ...args], {
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, JARVIS_WEBCAM_DEVICE: device || '' },
});
let out = '';
let err = '';
let settled = false;
const timer = setTimeout(() => {
if (settled) return;
settled = true;
child.kill('SIGTERM');
reject(new Error('webcam helper timed out'));
}, timeoutMs);
const done = (fn) => (value) => {
if (settled) return;
settled = true;
clearTimeout(timer);
fn(value);
};
child.stdout.on('data', (chunk) => { out += chunk; });
child.stderr.on('data', (chunk) => { err += chunk; });
child.on('error', done(reject));
child.on('close', (code) => {
let payload = {};
try { payload = JSON.parse(String(out).trim().split('\n').pop() || '{}'); } catch {}
if (code === 0 && payload.ok) done(resolve)(payload);
else done(reject)(new Error(payload.error || err.trim() || `webcam helper exited ${code}`));
});
});
}
}