infinicode 2.8.49 → 2.8.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kernel/federation/dashboard-html.d.ts +1 -1
- package/dist/kernel/federation/dashboard-html.js +31 -13
- package/dist/robopark/enroll.js +3 -3
- package/dist/robopark/preview-agent-launcher.js +3 -3
- package/dist/robopark/python-env.d.ts +6 -0
- package/dist/robopark/python-env.js +50 -1
- package/dist/robopark/vision-agent-launcher.js +4 -3
- package/package.json +1 -1
|
@@ -5448,14 +5448,16 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5448
5448
|
// LiveKit is the authoritative camera feed. The standalone OpenCV MJPEG
|
|
5449
5449
|
// server is opt-in because running it beside preview_agent would open the
|
|
5450
5450
|
// same Windows camera twice and make both feeds unreliable.
|
|
5451
|
-
function tryVisionOverlay(n){
|
|
5452
|
-
var img=$('pk-vision-img'); if(!img) return;
|
|
5453
|
-
if(String(window.localStorage.getItem('robopark.visionOverlay')||'').toLowerCase()!=='true'){
|
|
5454
|
-
visionOn=false; img.src=''; img.style.display='none'; return;
|
|
5455
|
-
}
|
|
5456
|
-
var row=schedRow(n),
|
|
5457
|
-
|
|
5458
|
-
var
|
|
5451
|
+
function tryVisionOverlay(n, force){
|
|
5452
|
+
var img=$('pk-vision-img'); if(!img) return;
|
|
5453
|
+
if(!force && String(window.localStorage.getItem('robopark.visionOverlay')||'').toLowerCase()!=='true'){
|
|
5454
|
+
visionOn=false; img.src=''; img.style.display='none'; return;
|
|
5455
|
+
}
|
|
5456
|
+
var row=schedRow(n)||{}, device=schedulerDevice(n)||{};
|
|
5457
|
+
var preferTail=/^100\./.test(location.hostname);
|
|
5458
|
+
var ip=preferTail ? (device.tailscale_ip||row.tailscale_ip||device.lan_ip||row.lan_ip) : (device.lan_ip||row.lan_ip||device.tailscale_ip||row.tailscale_ip);
|
|
5459
|
+
if(!ip){ visionOn=false; img.src=''; img.style.display='none'; return; }
|
|
5460
|
+
var port=row.vision_port||5000;
|
|
5459
5461
|
visionOn=true;
|
|
5460
5462
|
img.onerror=function(){ visionOn=false; img.style.display='none'; img.src=''; };
|
|
5461
5463
|
img.onload=function(){ img.style.display='block'; };
|
|
@@ -5485,7 +5487,8 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5485
5487
|
var device=schedulerDevice(n)||{}, inv=device.device_inventory||{};
|
|
5486
5488
|
if(hasRealInventory(inv)) deviceSelectorsReady=true;
|
|
5487
5489
|
var search=$('pk-device-search'), query=(search&&search.value||'').trim().toLowerCase();
|
|
5488
|
-
|
|
5490
|
+
// Do not invent Picamera/V4L2 choices when the robot has not reported hardware.
|
|
5491
|
+
var video=deviceList(inv,'video',['auto','none']);
|
|
5489
5492
|
var input=deviceList(inv,'audio_input',['default','none']);
|
|
5490
5493
|
var output=deviceList(inv,'audio_output',['default','none']);
|
|
5491
5494
|
function fill(id, list, selected){
|
|
@@ -5501,7 +5504,11 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5501
5504
|
fill('pk-audio-output', output, device.audio_output_device||'default');
|
|
5502
5505
|
var greetings=$('pk-greeting-phrases');
|
|
5503
5506
|
if(greetings && document.activeElement!==greetings) greetings.value=(device.greeting_phrases||[]).join(String.fromCharCode(10));
|
|
5504
|
-
var note=$('pk-device-note');
|
|
5507
|
+
var note=$('pk-device-note');
|
|
5508
|
+
if(note){
|
|
5509
|
+
var source=inv.source||'robot heartbeat', counts=(inv.video||[]).length+'/'+(inv.audio_input||[]).length+'/'+(inv.audio_output||[]).length;
|
|
5510
|
+
note.textContent=hasRealInventory(inv) ? ('inventory from '+source+' · camera/mic/speaker '+counts) : (device.id ? 'no real hardware inventory reported yet · restart vision-agent + preview-agent on this robot' : 'waiting for robot heartbeat inventory');
|
|
5511
|
+
}
|
|
5505
5512
|
}
|
|
5506
5513
|
function saveDeviceSelectors(n){
|
|
5507
5514
|
var device=schedulerDevice(n), id=device&&device.id; if(!id){ showOut('device inventory is not available yet'); return; }
|
|
@@ -5867,7 +5874,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5867
5874
|
var effPresetOptions='', effVoiceOptions='';
|
|
5868
5875
|
for(var ci=0;ci<characterPresets.length;ci++){ var cp=characterPresets[ci]; effPresetOptions += '<option value="'+esc(cp.id||'')+'"'+((cp.id===n.id)?' selected':'')+'>'+esc(cp.name||cp.id||'')+'</option>'; }
|
|
5869
5876
|
for(var vi=0;vi<voiceStacks.length;vi++){ var vs=voiceStacks[vi]; effVoiceOptions += '<option value="'+esc(vs.id||'')+'">'+esc(vs.name||vs.id||'')+'</option>'; }
|
|
5870
|
-
var devForPm=schedulerDevice(n)||{};
|
|
5877
|
+
var devForPm=schedulerDevice(n)||{};
|
|
5871
5878
|
b.innerHTML =
|
|
5872
5879
|
sect('👁 camera feed',
|
|
5873
5880
|
'<div class="pk-cam" id="pk-cam"><canvas id="pk-camcv" width="384" height="216"></canvas>'
|
|
@@ -5895,7 +5902,12 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5895
5902
|
+ buildShellSection(schedId(n))
|
|
5896
5903
|
+ sect('🎭 character / voice','<div class="pk-note" id="pk-eff-note" style="margin-bottom:0.45rem;">loading effective config…</div><div class="rp-field"><label>Character preset</label><select class="rp-char" id="pk-eff-char" data-robot="'+esc(schedId(n))+'">'+effPresetOptions+'</select></div><div class="rp-field"><label>Voice stack</label><select class="rp-voice" id="pk-eff-voice" data-robot="'+esc(schedId(n))+'">'+effVoiceOptions+'</select></div><button class="rp-btn" id="pk-eff-save" data-robot="'+esc(schedId(n))+'" style="margin-top:0.35rem;">Set up on this robot</button>');
|
|
5897
5904
|
b.innerHTML += sect('Camera + audio devices','<div class="rp-field"><label>Search devices</label><input id="pk-device-search" placeholder="camera, USB, microphone, HDMI..." autocomplete="off"></div><div class="rp-field"><label>Camera input</label><select id="pk-video-device"></select></div><div class="rp-field"><label>Microphone input</label><select id="pk-audio-input"></select></div><div class="rp-field"><label>Speaker / output inventory</label><select id="pk-audio-output"></select></div><div class="rp-field"><label>Preset greeting phrases <span class="pk-note">one per line, cached per voice</span></label><textarea id="pk-greeting-phrases" rows="3" placeholder="Hi, I am Jaguar! / Welcome to RoboPark."></textarea></div><button class="rp-btn" id="pk-device-save" style="margin-top:0.35rem">Save device selection + greetings</button><div class="pk-note" id="pk-device-note">waiting for robot heartbeat inventory</div>');
|
|
5898
|
-
|
|
5905
|
+
// Put live hardware selection beside the camera and audio tests,
|
|
5906
|
+
// ahead of persona and service administration in the Park drawer.
|
|
5907
|
+
var deviceInput=$('pk-device-search'), deviceSection=deviceInput&&deviceInput.closest('.pk-sect');
|
|
5908
|
+
var speakerTest=$('pk-spk-test'), speakerSection=speakerTest&&speakerTest.closest('.pk-sect');
|
|
5909
|
+
if(deviceSection && speakerSection) speakerSection.after(deviceSection);
|
|
5910
|
+
refreshDeviceSelectors(n);
|
|
5899
5911
|
b.innerHTML += '<div class="pk-ctl" style="margin-top:0.5rem"><button class="rp-btn danger" id="pk-recover" data-robot="'+esc(n.name)+'">Recover robot</button><span class="pk-note" style="margin:0">clears stale sessions and queued triggers for this robot</span></div>';
|
|
5900
5912
|
var deviceSearch=$('pk-device-search'); if(deviceSearch) deviceSearch.addEventListener('input', function(){ refreshDeviceSelectors(n); });
|
|
5901
5913
|
var deviceSave=$('pk-device-save'); if(deviceSave) deviceSave.addEventListener('click', function(){ saveDeviceSelectors(n); });
|
|
@@ -5903,7 +5915,13 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5903
5915
|
_bindShellSection(n);
|
|
5904
5916
|
_spkTestRenderHistory(schedId(n));
|
|
5905
5917
|
var runSpkTest = $('pk-spk-runtest'); if(runSpkTest) runSpkTest.addEventListener('click', function(){ if(rpReadOnly){ showOut('read-only node'); return; } runSpeakerTest(n); });
|
|
5906
|
-
var gl=$('pk-golive');
|
|
5918
|
+
var gl=$('pk-golive');
|
|
5919
|
+
if(gl){
|
|
5920
|
+
gl.addEventListener('click', function(){ if(rpReadOnly){ showOut('read-only node'); return; } goLive(n); });
|
|
5921
|
+
var vl=document.createElement('button'); vl.className='rp-btn'; vl.id='pk-vision-live'; vl.textContent='Open RoboVision feed';
|
|
5922
|
+
vl.addEventListener('click', function(){ tryVisionOverlay(n, true); if(!visionOn) showOut('RoboVision feed unavailable: robot has no reachable LAN/Tailscale address'); });
|
|
5923
|
+
gl.after(vl);
|
|
5924
|
+
}
|
|
5907
5925
|
var pm=$('pk-prodmode'); if(pm) pm.addEventListener('change', function(){ if(rpReadOnly){ pm.checked=!pm.checked; showOut('read-only node'); return; } setProductionMode(n, pm.checked); });
|
|
5908
5926
|
var svcCopy=$('pk-svc-clicmd-copy'); if(svcCopy) svcCopy.addEventListener('click', function(){ var i=$('pk-svc-clicmd'); i.select(); try{document.execCommand('copy');}catch(e){} showOut('command copied — run it on the robot itself'); });
|
|
5909
5927
|
var svcList=$('pk-svc-list'); if(svcList) svcList.addEventListener('click', function(ev){
|
package/dist/robopark/enroll.js
CHANGED
|
@@ -9,7 +9,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
|
9
9
|
import { homedir, hostname } from 'node:os';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
|
-
import { findPython, findSchedulerPath,
|
|
12
|
+
import { findPython, findSchedulerPath, prepareRobotPython } from './python-env.js';
|
|
13
13
|
const ROBOPARK_DIR = join(homedir(), '.robopark');
|
|
14
14
|
const ENROLLMENT_FILE = join(ROBOPARK_DIR, 'enrollment_token');
|
|
15
15
|
function ensureDir() {
|
|
@@ -39,8 +39,8 @@ export async function roboparkEnroll(opts) {
|
|
|
39
39
|
console.log(chalk.red(' ✗ could not find preview_agent.py'));
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
42
|
-
const python = findPython();
|
|
43
|
-
if (!
|
|
42
|
+
const python = prepareRobotPython(findPython(), script);
|
|
43
|
+
if (!python)
|
|
44
44
|
process.exit(1);
|
|
45
45
|
const args = [
|
|
46
46
|
script,
|
|
@@ -8,7 +8,7 @@ import { spawn } from 'node:child_process';
|
|
|
8
8
|
import { hostname } from 'node:os';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { discoverContext } from './discovery.js';
|
|
11
|
-
import { findPython, findSchedulerPath,
|
|
11
|
+
import { findPython, findSchedulerPath, prepareRobotPython } from './python-env.js';
|
|
12
12
|
const DEFAULT_SCHEDULER_PORT = 8080;
|
|
13
13
|
export async function roboparkPreviewAgent(opts) {
|
|
14
14
|
const ctx = await discoverContext();
|
|
@@ -24,8 +24,8 @@ export async function roboparkPreviewAgent(opts) {
|
|
|
24
24
|
console.log(chalk.red(' ✗ could not find preview_agent.py'));
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
|
-
const python = findPython();
|
|
28
|
-
if (!
|
|
27
|
+
const python = prepareRobotPython(findPython(), script);
|
|
28
|
+
if (!python)
|
|
29
29
|
process.exit(1);
|
|
30
30
|
const args = [
|
|
31
31
|
script,
|
|
@@ -13,3 +13,9 @@ export declare function findPython(): string;
|
|
|
13
13
|
* (e.g. app_pi_clean.py + requirements_pi_unified.txt).
|
|
14
14
|
*/
|
|
15
15
|
export declare function ensurePythonDeps(python: string, scriptPath: string, modules?: string[], reqFile?: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Create and maintain the isolated robot runtime at ~/.robopark/venv.
|
|
18
|
+
* System site packages stay visible so Pi apt packages such as OpenCV,
|
|
19
|
+
* PyAudio and Picamera2 do not need to be rebuilt by pip on ARM.
|
|
20
|
+
*/
|
|
21
|
+
export declare function prepareRobotPython(basePython: string, scriptPath: string, modules?: string[], reqFile?: string): string | null;
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* and `vision-agent`, which spawn preview_agent.py / app_pi_clean.py.
|
|
4
4
|
*/
|
|
5
5
|
import { execSync, spawnSync } from 'node:child_process';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
7
8
|
import { join } from 'node:path';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
8
10
|
import chalk from 'chalk';
|
|
9
11
|
/** Modules preview_agent.py imports — kept in sync with requirements-robot.txt. */
|
|
10
12
|
const REQUIRED_MODULES = ['httpx', 'cv2', 'pyaudio', 'livekit'];
|
|
@@ -52,6 +54,12 @@ export function findPython() {
|
|
|
52
54
|
}
|
|
53
55
|
return process.platform === 'win32' ? 'python' : 'python3';
|
|
54
56
|
}
|
|
57
|
+
const ROBOPARK_VENV = join(homedir(), '.robopark', 'venv');
|
|
58
|
+
function venvPythonPath() {
|
|
59
|
+
return process.platform === 'win32'
|
|
60
|
+
? join(ROBOPARK_VENV, 'Scripts', 'python.exe')
|
|
61
|
+
: join(ROBOPARK_VENV, 'bin', 'python');
|
|
62
|
+
}
|
|
55
63
|
function missingModules(python, modules) {
|
|
56
64
|
return modules.filter(mod => {
|
|
57
65
|
const res = spawnSync(python, ['-c', `import ${mod}`], { stdio: 'ignore' });
|
|
@@ -89,3 +97,44 @@ export function ensurePythonDeps(python, scriptPath, modules = REQUIRED_MODULES,
|
|
|
89
97
|
}
|
|
90
98
|
return true;
|
|
91
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Create and maintain the isolated robot runtime at ~/.robopark/venv.
|
|
102
|
+
* System site packages stay visible so Pi apt packages such as OpenCV,
|
|
103
|
+
* PyAudio and Picamera2 do not need to be rebuilt by pip on ARM.
|
|
104
|
+
*/
|
|
105
|
+
export function prepareRobotPython(basePython, scriptPath, modules = REQUIRED_MODULES, reqFile = 'requirements-robot.txt') {
|
|
106
|
+
const reqPath = join(scriptPath, '..', reqFile);
|
|
107
|
+
if (!existsSync(reqPath)) {
|
|
108
|
+
console.log(chalk.red(` ✗ ${reqFile} not found next to ${scriptPath}`));
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const python = venvPythonPath();
|
|
112
|
+
if (!existsSync(python)) {
|
|
113
|
+
console.log(chalk.dim(` creating RoboPark Python environment: ${ROBOPARK_VENV}`));
|
|
114
|
+
mkdirSync(join(homedir(), '.robopark'), { recursive: true });
|
|
115
|
+
const created = spawnSync(basePython, ['-m', 'venv', '--system-site-packages', ROBOPARK_VENV], { stdio: 'inherit' });
|
|
116
|
+
if (created.status !== 0 || !existsSync(python)) {
|
|
117
|
+
console.log(chalk.red(' ✗ could not create ~/.robopark/venv. On Debian/Raspberry Pi install python3-venv, then retry.'));
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const requirements = readFileSync(reqPath, 'utf8');
|
|
122
|
+
const fingerprint = createHash('sha256').update(requirements).digest('hex');
|
|
123
|
+
const marker = join(ROBOPARK_VENV, `.robopark-${reqFile}.sha256`);
|
|
124
|
+
const installedFingerprint = existsSync(marker) ? readFileSync(marker, 'utf8').trim() : '';
|
|
125
|
+
if (installedFingerprint !== fingerprint || missingModules(python, modules).length > 0) {
|
|
126
|
+
console.log(chalk.dim(` syncing RoboPark Python dependencies from ${reqFile}…`));
|
|
127
|
+
const install = spawnSync(python, ['-m', 'pip', 'install', '--upgrade', '-r', reqPath], { stdio: 'inherit' });
|
|
128
|
+
if (install.status !== 0) {
|
|
129
|
+
console.log(chalk.red(` ✗ dependency install failed in ${ROBOPARK_VENV}`));
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
writeFileSync(marker, `${fingerprint}\n`);
|
|
133
|
+
}
|
|
134
|
+
const stillMissing = missingModules(python, modules);
|
|
135
|
+
if (stillMissing.length) {
|
|
136
|
+
console.log(chalk.red(` ✗ venv is still missing: ${stillMissing.join(', ')}`));
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return python;
|
|
140
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { spawn } from 'node:child_process';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
-
import { findPython, findVisionPath,
|
|
10
|
+
import { findPython, findVisionPath, prepareRobotPython, VISION_REQUIRED_MODULES } from './python-env.js';
|
|
11
11
|
const DEFAULT_VISION_PORT = 5000;
|
|
12
12
|
const DEFAULT_MOTION_WEBHOOK_PORT = 5057; // must match preview-agent's --vision-webhook-port default
|
|
13
13
|
function isPiArm() {
|
|
@@ -19,12 +19,13 @@ export async function roboparkVisionAgent(opts) {
|
|
|
19
19
|
console.log(chalk.red(' ✗ could not find app_pi_clean.py'));
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
|
-
const
|
|
22
|
+
const basePython = findPython();
|
|
23
23
|
// Real Pi: opencv comes from `apt install python3-opencv`, not pip (see
|
|
24
24
|
// requirements_pi_unified.txt) — pip-installing it here would fight the
|
|
25
25
|
// ARM-optimized system build. Everywhere else: plain pip install works.
|
|
26
26
|
const reqFile = isPiArm() ? 'requirements_pi_unified.txt' : 'requirements-demo.txt';
|
|
27
|
-
|
|
27
|
+
const python = prepareRobotPython(basePython, script, VISION_REQUIRED_MODULES, reqFile);
|
|
28
|
+
if (!python)
|
|
28
29
|
process.exit(1);
|
|
29
30
|
const port = opts.port ?? String(DEFAULT_VISION_PORT);
|
|
30
31
|
const webhookUrl = opts.motionWebhookUrl ?? `http://127.0.0.1:${DEFAULT_MOTION_WEBHOOK_PORT}/`;
|
package/package.json
CHANGED