infinicode 2.8.43 → 2.8.45
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.
|
@@ -1319,40 +1319,67 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
1319
1319
|
return r.json();
|
|
1320
1320
|
});
|
|
1321
1321
|
}
|
|
1322
|
-
function mergeSchedulerRobots(nodes){
|
|
1323
|
-
return Promise.all([getJson('/robopark/api/robots'), getJson('/robopark/api/devices')]).then(function(data){
|
|
1324
|
-
var rows = data[0], devices = data[1];
|
|
1325
|
-
var deviceById = {}, deviceByName = {};
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
var
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1322
|
+
function mergeSchedulerRobots(nodes){
|
|
1323
|
+
return Promise.all([getJson('/robopark/api/robots'), getJson('/robopark/api/devices')]).then(function(data){
|
|
1324
|
+
var rows = data[0], devices = data[1];
|
|
1325
|
+
var deviceById = {}, deviceByName = {};
|
|
1326
|
+
function norm(v){ return String(v||'').trim().toLowerCase(); }
|
|
1327
|
+
(Array.isArray(devices)?devices:[]).forEach(function(device){
|
|
1328
|
+
deviceById[device.id] = device;
|
|
1329
|
+
if(device.name) deviceByName[norm(device.name)] = device;
|
|
1330
|
+
});
|
|
1331
|
+
schedulerDevicesById = deviceById;
|
|
1332
|
+
schedulerDevicesByName = deviceByName;
|
|
1333
|
+
var merged = (nodes||[]).slice();
|
|
1334
|
+
var candidates = (Array.isArray(rows)?rows:[]).slice();
|
|
1335
|
+
// Enrollment creates a devices row first; include it even when the
|
|
1336
|
+
// legacy robots table has not been populated for that device yet.
|
|
1337
|
+
(Array.isArray(devices)?devices:[]).forEach(function(device){
|
|
1338
|
+
var already = candidates.some(function(row){
|
|
1339
|
+
return norm(row.id||row.robot_id)===norm(device.id) || norm(row.name)===norm(device.name);
|
|
1340
|
+
});
|
|
1341
|
+
if(!already) candidates.push({id:device.id, name:device.name});
|
|
1342
|
+
});
|
|
1343
|
+
candidates.forEach(function(row){
|
|
1344
|
+
var id = row.id || row.robot_id;
|
|
1345
|
+
if(!id) return;
|
|
1346
|
+
var name = row.name || id;
|
|
1347
|
+
var device = deviceById[id] || deviceByName[norm(name)] || {};
|
|
1348
|
+
var status = String(device.status || row.status || '').toLowerCase();
|
|
1349
|
+
var heartbeat = device.last_heartbeat || row.last_heartbeat;
|
|
1350
|
+
var heartbeatAt = heartbeat ? Date.parse(heartbeat) : NaN;
|
|
1351
|
+
var heartbeatFresh = Number.isFinite(heartbeatAt) && (Date.now()-heartbeatAt) < 45_000;
|
|
1352
|
+
var connected = heartbeatFresh || ['online','detecting','connecting','running'].indexOf(status)>=0;
|
|
1353
|
+
var keys = [String(id).toLowerCase(), String(name).toLowerCase()];
|
|
1354
|
+
var existing = merged.find(function(n){
|
|
1355
|
+
return norm(n.schedulerRobotId)===norm(id)
|
|
1356
|
+
|| norm(n.nodeId)===norm(id)
|
|
1357
|
+
|| norm(n.displayName)===norm(name);
|
|
1358
|
+
});
|
|
1359
|
+
if(existing){
|
|
1360
|
+
existing.schedulerRobotId = id;
|
|
1361
|
+
existing.schedulerDeviceId = device.id || id;
|
|
1362
|
+
existing.displayName = existing.displayName || name;
|
|
1363
|
+
existing.connected = !!existing.connected || connected;
|
|
1364
|
+
existing.lastSeenAt = heartbeatAt && Number.isFinite(heartbeatAt) ? heartbeatAt : existing.lastSeenAt;
|
|
1365
|
+
existing.telemKeys = (existing.telemKeys||[]).concat(keys).filter(function(v,i,a){return a.indexOf(v)===i;});
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
merged.push({
|
|
1369
|
+
nodeId: 'scheduler-robot-'+id,
|
|
1370
|
+
schedulerRobotId: id,
|
|
1371
|
+
schedulerDeviceId: device.id || id,
|
|
1372
|
+
displayName: name,
|
|
1346
1373
|
role: 'robot',
|
|
1347
1374
|
platform: 'RoboPark robot',
|
|
1348
1375
|
version: 'scheduler',
|
|
1349
1376
|
capabilities: ['camera','microphone','voice'],
|
|
1350
1377
|
connected: connected,
|
|
1351
1378
|
load: 0,
|
|
1352
|
-
lastSeenAt:
|
|
1353
|
-
telemKeys: keys,
|
|
1354
|
-
});
|
|
1355
|
-
});
|
|
1379
|
+
lastSeenAt: heartbeatAt && Number.isFinite(heartbeatAt) ? heartbeatAt : null,
|
|
1380
|
+
telemKeys: keys,
|
|
1381
|
+
});
|
|
1382
|
+
});
|
|
1356
1383
|
return merged;
|
|
1357
1384
|
}).catch(function(){ return nodes||[]; });
|
|
1358
1385
|
}
|
|
@@ -2005,7 +2032,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
2005
2032
|
var grid = $('rp-grid'), totals = $('rp-totals');
|
|
2006
2033
|
// stash per-robot rows so the Park drawer can show live session stats
|
|
2007
2034
|
roboparkByName = {};
|
|
2008
|
-
robotsFrom(d).forEach(function(x){ roboparkByName[robotName(x)] = x; if(x.name) roboparkByName[x.name] = x; });
|
|
2035
|
+
robotsFrom(d).forEach(function(x){ roboparkByName[String(robotName(x)).toLowerCase()] = x; if(x.name) roboparkByName[String(x.name).toLowerCase()] = x; });
|
|
2009
2036
|
// C4: also feed lastRobots for the Incidents sub-tab
|
|
2010
2037
|
lastRobots = robotsFrom(d) || [];
|
|
2011
2038
|
// push the same map into the Park so the canvas can color robots
|
|
@@ -3553,7 +3580,9 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
3553
3580
|
connected: match?!!match.connected:false,
|
|
3554
3581
|
load: (match&&match.load!=null)?match.load:0,
|
|
3555
3582
|
platform: match?(match.platform||''):'', version: match?(match.version||'?'):'—',
|
|
3556
|
-
site: match?(match.site||''):'', caps: match?(match.capabilities||[]):[],
|
|
3583
|
+
site: match?(match.site||''):'', caps: match?(match.capabilities||[]):[],
|
|
3584
|
+
schedulerRobotId: match?(match.schedulerRobotId||match.schedulerDeviceId||''):'',
|
|
3585
|
+
schedulerDeviceId: match?(match.schedulerDeviceId||match.schedulerRobotId||''):'',
|
|
3557
3586
|
telemKeys: [slot.label.toLowerCase()].concat(slot.keys||[]).concat(match?[(match.displayName||'').toLowerCase()]:[]),
|
|
3558
3587
|
wx:wx, wy:wy, _hit:prevHit(id)
|
|
3559
3588
|
};
|
|
@@ -5285,7 +5314,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5285
5314
|
// enrolled device's id is a generated dev_xxxx, distinct from its name).
|
|
5286
5315
|
// Falls back to n.name if telemetry hasn't matched a row yet.
|
|
5287
5316
|
function schedRow(n){
|
|
5288
|
-
var row=roboparkByName[n.name], exact=row, keys=(n.telemKeys||[]).concat([String(n.name||'').toLowerCase()]);
|
|
5317
|
+
var row=roboparkByName[String(n.name||'').toLowerCase()] || roboparkByName[n.name], exact=row, keys=(n.telemKeys||[]).concat([String(n.name||'').toLowerCase()]);
|
|
5289
5318
|
var seen={}, candidates=[];
|
|
5290
5319
|
for(var rk in roboparkByName){
|
|
5291
5320
|
var candidate=roboparkByName[rk], cid=candidate&&(candidate.robot_id||candidate.id);
|
|
@@ -5309,7 +5338,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5309
5338
|
function schedId(n){
|
|
5310
5339
|
var row=schedRow(n); if(row&&(row.id||row.robot_id)) return row.id||row.robot_id;
|
|
5311
5340
|
if(n.schedulerRobotId) return n.schedulerRobotId;
|
|
5312
|
-
var byName=schedulerDevicesByName[n.name]; if(byName&&byName.id) return byName.id;
|
|
5341
|
+
var byName=schedulerDevicesByName[String(n.name||'').toLowerCase()]; if(byName&&byName.id) return byName.id;
|
|
5313
5342
|
return n.name;
|
|
5314
5343
|
}
|
|
5315
5344
|
function closeDrawer(){
|
|
@@ -5439,13 +5468,13 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5439
5468
|
function pollRoboparkQuiet(){
|
|
5440
5469
|
fetch('/robopark/api/telemetry'+QS,{cache:'no-store'}).then(function(r){return r.json();}).then(function(d){
|
|
5441
5470
|
var list=(Array.isArray(d)?d:(d&&d.robots)||[]); roboparkByName={};
|
|
5442
|
-
list.forEach(function(x){ roboparkByName[x.robot_id||x.id||x.name||'robot']=x; if(x.name) roboparkByName[x.name]=x; });
|
|
5471
|
+
list.forEach(function(x){ roboparkByName[String(x.robot_id||x.id||x.name||'robot').toLowerCase()]=x; if(x.name) roboparkByName[String(x.name).toLowerCase()]=x; });
|
|
5443
5472
|
refreshDrawer();
|
|
5444
5473
|
}).catch(function(){});
|
|
5445
5474
|
}
|
|
5446
5475
|
function schedulerDevice(n){
|
|
5447
5476
|
var row=schedRow(n), id=row&&(row.robot_id||row.id);
|
|
5448
|
-
return (id&&schedulerDevicesById[id]) || schedulerDevicesById[n.schedulerRobotId] || schedulerDevicesByName[n.name] || schedulerDevicesById[n.name] || null;
|
|
5477
|
+
return (id&&schedulerDevicesById[id]) || schedulerDevicesById[n.schedulerDeviceId] || schedulerDevicesById[n.schedulerRobotId] || schedulerDevicesByName[String(n.name||'').toLowerCase()] || schedulerDevicesById[n.name] || null;
|
|
5449
5478
|
}
|
|
5450
5479
|
function deviceList(inv, key, fallback){
|
|
5451
5480
|
var list=inv&&Array.isArray(inv[key])?inv[key]:[];
|
|
@@ -5592,7 +5621,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
|
|
|
5592
5621
|
// available services from the supervisor_status report (if any),
|
|
5593
5622
|
// otherwise shows a placeholder.
|
|
5594
5623
|
function buildShellSection(robotId){
|
|
5595
|
-
var device = robotId ?
|
|
5624
|
+
var device = robotId ? schedulerDevicesByName[String(robotName({name:robotId, id:robotId})||'').toLowerCase()] || null : null;
|
|
5596
5625
|
// Try to read the device row by id directly
|
|
5597
5626
|
try{
|
|
5598
5627
|
for(var k in schedulerDevicesById){
|
|
@@ -18,6 +18,9 @@ const PATTERNS = [
|
|
|
18
18
|
'cli.js serve', // source/dev invocation: `node dist/cli.js serve ...`
|
|
19
19
|
'robopark serve',
|
|
20
20
|
'robopark-cli.js serve',
|
|
21
|
+
// `robopark serve` detaches this child, so the Node launcher is gone while
|
|
22
|
+
// the Python scheduler remains. Match both Windows and POSIX separators.
|
|
23
|
+
'scheduler.{0,3}main\\.py',
|
|
21
24
|
'preview_agent.py',
|
|
22
25
|
'app_pi_clean.py',
|
|
23
26
|
];
|
package/package.json
CHANGED
|
@@ -4166,6 +4166,8 @@ async def dashboard():
|
|
|
4166
4166
|
const showDevices = ref(true);
|
|
4167
4167
|
const showCommands = ref(false);
|
|
4168
4168
|
const commandNetwork = ref('lan');
|
|
4169
|
+
const commandHubLan = ref('http://192.168.1.12:47913');
|
|
4170
|
+
const commandHubTailscale = ref('http://100.84.147.24:47913');
|
|
4169
4171
|
const commandRobotId = ref('');
|
|
4170
4172
|
const newDevice = ref({ name: '', tailscale_ip: '', lan_ip: '', motor_server_url: '', character_id: '', livekit_url: '', video_device: 'auto', audio_device: 'default', notes: '' });
|
|
4171
4173
|
const rotatedToken = ref(null);
|
|
@@ -4527,11 +4529,9 @@ async def dashboard():
|
|
|
4527
4529
|
const dismissRotatedToken = () => { rotatedToken.value = null; };
|
|
4528
4530
|
const enrollmentCommand = (token) => {
|
|
4529
4531
|
if (!token) return '';
|
|
4530
|
-
const schedulerPort = window.location.port || '8080';
|
|
4531
4532
|
const networkFlag = enrollmentNetwork.value === 'tailscale' ? '--tailscale' : '--lan';
|
|
4532
|
-
const
|
|
4533
|
-
|
|
4534
|
-
return `robopark setup --robot --name "${token.name || token.device_id}" --hub-url ${hubUrl} --scheduler-port ${schedulerPort} ${networkFlag} --enrollment-token ${token.token} --start --auto-start`;
|
|
4533
|
+
const hubUrl = enrollmentNetwork.value === 'tailscale' ? commandHubTailscale.value : commandHubLan.value;
|
|
4534
|
+
return `robopark setup --robot --name "${token.name || token.device_id}" --hub-url ${hubUrl} --scheduler-port 8080 ${networkFlag} --enrollment-token ${token.token} --start --auto-start`;
|
|
4535
4535
|
};
|
|
4536
4536
|
|
|
4537
4537
|
const joinConversation = async (device) => {
|
|
@@ -4732,7 +4732,10 @@ async def dashboard():
|
|
|
4732
4732
|
const stackName = (id) => voiceStacks.value.find(s => s.id === id)?.name || '—';
|
|
4733
4733
|
const characterName = (id) => characterPresets.value.find(c => c.id === id)?.name || '—';
|
|
4734
4734
|
|
|
4735
|
-
const schedulerBase = () =>
|
|
4735
|
+
const schedulerBase = () => {
|
|
4736
|
+
const hub = commandNetwork.value === 'tailscale' ? commandHubTailscale.value : commandHubLan.value;
|
|
4737
|
+
return hub.replace(/:\\d+\\/?$/, ':8080').replace(/\\/$/, '');
|
|
4738
|
+
};
|
|
4736
4739
|
const selectedCommandRobot = computed(() => devices.value.find(d => d.id === commandRobotId.value) || devices.value[0] || null);
|
|
4737
4740
|
const commandRows = computed(() => {
|
|
4738
4741
|
const base = schedulerBase();
|
|
@@ -4741,10 +4744,11 @@ async def dashboard():
|
|
|
4741
4744
|
const robotName = robot?.name || '<ROBOT_NAME>';
|
|
4742
4745
|
const networkFlag = commandNetwork.value === 'tailscale' ? '--tailscale' : '--lan';
|
|
4743
4746
|
const tokenPlaceholder = '<ENROLLMENT_TOKEN_FROM_ADD_DEVICE>';
|
|
4747
|
+
const hub = commandNetwork.value === 'tailscale' ? commandHubTailscale.value : commandHubLan.value;
|
|
4744
4748
|
return [
|
|
4745
4749
|
{ group: 'Scheduler', name: 'Health check', description: 'Confirm the control center API is reachable.', command: `curl -s ${base}/api/settings` },
|
|
4746
4750
|
{ group: 'Scheduler', name: 'Start scheduler', description: 'Start the RoboPark scheduler and dashboard on port 8080.', command: 'robopark serve --port 8080' },
|
|
4747
|
-
{ group: 'Robot', name: 'Enroll robot', description: `Generated for ${robotName} over ${commandNetwork.value}. Paste the one-time token from the Add Device dialog.`, command: `robopark setup --robot --name "${robotName}" --hub-url ${
|
|
4751
|
+
{ group: 'Robot', name: 'Enroll robot', description: `Generated for ${robotName} over ${commandNetwork.value}. Paste the one-time token from the Add Device dialog.`, command: `robopark setup --robot --name "${robotName}" --hub-url ${hub} --scheduler-port 8080 ${networkFlag} --enrollment-token ${tokenPlaceholder} --start --auto-start` },
|
|
4748
4752
|
{ group: 'Robot', name: 'Simulate motion', description: 'Queue a full production trigger for the selected robot.', command: `curl -s -X POST ${base}/api/robots/${robotId}/simulate-trigger` },
|
|
4749
4753
|
{ group: 'Robot', name: 'Stop conversation', description: 'Stop the active conversation and clear queued trigger state.', command: `curl -s -X POST ${base}/api/robots/${robotId}/simulate-stop` },
|
|
4750
4754
|
{ group: 'Robot', name: 'Recover stale state', description: 'Release a robot stuck in connecting/running after a failed teardown.', command: `curl -s -X POST ${base}/api/robots/${robotId}/recover` },
|
|
@@ -4761,7 +4765,7 @@ async def dashboard():
|
|
|
4761
4765
|
|
|
4762
4766
|
return {
|
|
4763
4767
|
robots, servers, sessions, stats, connected, serverMetrics,
|
|
4764
|
-
devices, settings, livekit, showAddDevice, showDevices, showCommands, commandNetwork, commandRobotId, selectedCommandRobot, commandRows, copyCommand, newDevice, rotatedToken, enrollmentNetwork, enrollmentCommand, simulation,
|
|
4768
|
+
devices, settings, livekit, showAddDevice, showDevices, showCommands, commandNetwork, commandHubLan, commandHubTailscale, commandRobotId, selectedCommandRobot, commandRows, copyCommand, newDevice, rotatedToken, enrollmentNetwork, enrollmentCommand, simulation,
|
|
4765
4769
|
showLiveKitConfig, livekitForm, preview, deviceInventory,
|
|
4766
4770
|
voiceStacks, characterPresets, showVoiceStackForm, showCharacterForm,
|
|
4767
4771
|
editingVoiceStack, editingCharacter, voiceStackForm, characterForm,
|
|
@@ -4830,6 +4834,14 @@ async def dashboard():
|
|
|
4830
4834
|
<button @click="commandNetwork = 'tailscale'" :class="commandNetwork === 'tailscale' ? 'bg-cyan-600' : 'bg-gray-700'" class="px-3 py-2 rounded text-sm">Tailscale</button>
|
|
4831
4835
|
</div>
|
|
4832
4836
|
</div>
|
|
4837
|
+
<div class="grid md:grid-cols-2 gap-3 mb-4">
|
|
4838
|
+
<label class="text-xs text-gray-400">LAN hub URL
|
|
4839
|
+
<input v-model="commandHubLan" class="block w-full mt-1 bg-gray-700 rounded px-3 py-2 text-sm text-white" placeholder="http://192.168.x.x:47913">
|
|
4840
|
+
</label>
|
|
4841
|
+
<label class="text-xs text-gray-400">Tailscale hub URL
|
|
4842
|
+
<input v-model="commandHubTailscale" class="block w-full mt-1 bg-gray-700 rounded px-3 py-2 text-sm text-white" placeholder="http://100.x.x.x:47913">
|
|
4843
|
+
</label>
|
|
4844
|
+
</div>
|
|
4833
4845
|
<div class="grid md:grid-cols-2 gap-3">
|
|
4834
4846
|
<div v-for="cmd in commandRows" :key="cmd.group + cmd.name" class="bg-gray-900 rounded p-3">
|
|
4835
4847
|
<div class="flex items-start justify-between gap-2">
|