infinicode 2.8.113 → 2.8.115
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/README.md +20 -2
- package/dist/cli.js +23 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +15 -4
- package/dist/commands/mesh-install.d.ts +1 -0
- package/dist/commands/mesh-install.js +28 -11
- package/dist/commands/mesh-link.d.ts +10 -0
- package/dist/commands/mesh-link.js +46 -0
- package/dist/kernel/federation/dashboard-html.d.ts +1 -1
- package/dist/kernel/federation/dashboard-html.js +5 -5
- package/dist/kernel/federation/index.d.ts +2 -0
- package/dist/kernel/federation/index.js +1 -0
- package/dist/kernel/federation/mesh-link.d.ts +16 -0
- package/dist/kernel/federation/mesh-link.js +52 -0
- package/dist/kernel/federation/transport-http.js +24 -0
- package/package.json +1 -1
|
@@ -1258,7 +1258,7 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
|
|
|
1258
1258
|
<div class="rp-field"><label>Enrollment token</label><input id="rp-command-enrollment" placeholder="optional" spellcheck="false"></div>
|
|
1259
1259
|
<div class="rp-field"><label>Control token</label><input id="rp-command-token" type="password" spellcheck="false"></div>
|
|
1260
1260
|
<div class="rp-field"><label>RoboVoice folder on livekit-1</label><input id="rp-command-robovoice" value="C:\ROBOVOICE-main" spellcheck="false"></div>
|
|
1261
|
-
<div class="rp-field"><label>Release</label><input id="rp-command-release" value="2.8.
|
|
1261
|
+
<div class="rp-field"><label>Release</label><input id="rp-command-release" value="2.8.115" spellcheck="false"></div>
|
|
1262
1262
|
</div>
|
|
1263
1263
|
<div class="rp-command-toolbar">
|
|
1264
1264
|
<input type="search" id="rp-command-search" placeholder="Search logs, camera, Docker, motors, npm...">
|
|
@@ -2843,7 +2843,7 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
|
|
|
2843
2843
|
var RP_COMMAND_ENV_KEY='rp-command-env-v2';
|
|
2844
2844
|
var RP_CUSTOM_COMMANDS_KEY='rp-custom-commands-v1';
|
|
2845
2845
|
var rpVisibleCommands=[];
|
|
2846
|
-
function rpCommandDefaults(){return {lan:'192.168.0.9',tail:'100.84.147.24',site:'Tel Aviv',robot:'bmw',character:'vixen',device:'',enrollment:'',token:dashboardMeshToken(),robovoice:'C:\\ROBOVOICE-main',release:'2.8.
|
|
2846
|
+
function rpCommandDefaults(){return {lan:'192.168.0.9',tail:'100.84.147.24',site:'Tel Aviv',robot:'bmw',character:'vixen',device:'',enrollment:'',token:dashboardMeshToken(),robovoice:'C:\\ROBOVOICE-main',release:'2.8.115',network:'lan'};}
|
|
2847
2847
|
function rpLoadStoredJson(key,fallback){try{var value=JSON.parse(localStorage.getItem(key)||'null');return value&&typeof value==='object'?value:fallback;}catch(e){return fallback;}}
|
|
2848
2848
|
function rpCommandEnv(){
|
|
2849
2849
|
var d=rpCommandDefaults(),s=rpLoadStoredJson(RP_COMMAND_ENV_KEY,{});
|
|
@@ -2852,7 +2852,7 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
|
|
|
2852
2852
|
}
|
|
2853
2853
|
function rpSaveCommandEnv(){try{localStorage.setItem(RP_COMMAND_ENV_KEY,JSON.stringify(rpCommandEnv()));}catch(e){}}
|
|
2854
2854
|
function rpShellArg(value){var text=String(value||'');return /^[A-Za-z0-9._:\/-]+$/.test(text)?text:'"'+text.replace(/"/g,'\\"')+'"';}
|
|
2855
|
-
function rpCompleteRobotCommand(setupCommand){var release=rpCommandEnv().release||'2.8.
|
|
2855
|
+
function rpCompleteRobotCommand(setupCommand){var release=rpCommandEnv().release||'2.8.115';return 'sudo npm install -g infinicode@'+rpShellArg(release)+' --force && hash -r && (sudo robopark stop || true) && '+setupCommand;}
|
|
2856
2856
|
function rpRobotSetupCommand(){
|
|
2857
2857
|
var e=rpCommandEnv(),control=rpControlUrl(e.network),argv=['sudo','robopark','setup','--robot','--name',e.robot,'--site',e.site,'--hub-url',control,'--token',e.token,'--port','47913',e.network==='tailscale'?'--tailscale':'--lan'];
|
|
2858
2858
|
if(e.enrollment)argv.push('--enrollment-token',e.enrollment);if(e.character)argv.push('--character',e.character);argv.push('--start','--auto-start','--yes');
|
|
@@ -2860,11 +2860,11 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
|
|
|
2860
2860
|
}
|
|
2861
2861
|
function rpCommandCatalog(){
|
|
2862
2862
|
var e=rpCommandEnv(),control=rpControlUrl(e.network),device=e.device||e.robot,enroll=e.enrollment?' --enrollment-token '+rpShellArg(e.enrollment):'',auth='?token='+encodeURIComponent(e.token);
|
|
2863
|
-
var api=control+'/robopark/api',release=e.release||'2.8.
|
|
2863
|
+
var api=control+'/robopark/api',release=e.release||'2.8.115';
|
|
2864
2864
|
return [
|
|
2865
2865
|
{id:'hub-up',category:'Provision',title:'Install and start RoboVoice-first control host',description:'Run in elevated PowerShell on livekit-1. Starts the private compatibility data service, proven RoboVoice stack, mesh relay, and full Control Center.',command:'npm install -g infinicode@'+rpShellArg(release)+' --force; robopark serve --port 8080 --no-open; robopark agent-up --path '+rpShellArg(e.robovoice)+' --scheduler-url http://127.0.0.1:8080 --token '+rpShellArg(e.token)+'; infinicode serve --hub --name livekit-1 --port 47913 --token '+rpShellArg(e.token)+' --lan --dashboard --scheduler-url http://127.0.0.1:8080 --auto-update --supervised'},
|
|
2866
2866
|
{id:'robot-install',category:'Provision',title:'Install or repair selected robot',description:'Run on the Pi. One control URL, one token, one supervised runtime; camera, audio, motion, motor server, and mesh start together.',command:rpRobotSetupCommand()},
|
|
2867
|
-
{id:'operator-tail',category:'Provision',title:'
|
|
2867
|
+
{id:'operator-tail',category:'Provision',title:'Direct agent mesh link',description:'Copy only this URL and give it to the agent on the other Tailscale device. It contains the exact control endpoint and mesh credential; the agent can run infinicode mesh join <link>.',command:rpControlUrl('tailscale')+'/fed/join?token='+encodeURIComponent(e.token)},
|
|
2868
2868
|
{id:'production-on',category:'Production',title:'Arm always-on motion conversations',description:'Enables local motion-triggered RoboVoice calls for the canonical device.',command:'Invoke-RestMethod -Method Patch -ContentType "application/json" -Body (@{production_mode=$true} | ConvertTo-Json) "'+api+'/devices/'+encodeURIComponent(device)+auth+'"'},
|
|
2869
2869
|
{id:'production-off',category:'Production',title:'Disarm production conversations',description:'Stops new motion-triggered calls without taking camera monitoring or remote control offline.',command:'Invoke-RestMethod -Method Patch -ContentType "application/json" -Body (@{production_mode=$false} | ConvertTo-Json) "'+api+'/devices/'+encodeURIComponent(device)+auth+'"'},
|
|
2870
2870
|
{id:'session-start',category:'Voice',title:'Start one production conversation',description:'Runs the same robot motion-to-RoboVoice path used onsite.',command:'Invoke-RestMethod -Method Post "'+api+'/robots/'+encodeURIComponent(device)+'/pipeline-test/start'+auth+'" | ConvertTo-Json -Depth 8'},
|
|
@@ -25,6 +25,8 @@ export type { ComputeCandidate, ComputeNeed } from './compute-router.js';
|
|
|
25
25
|
export { loadRole, saveRole, rolePreamble } from './role-context.js';
|
|
26
26
|
export { discoverTailscalePeers, tailscaleAvailable } from './discovery.js';
|
|
27
27
|
export type { DiscoverOptions } from './discovery.js';
|
|
28
|
+
export { buildMeshJoinLink, parseMeshJoinLink, redactMeshJoinLink, MESH_JOIN_PATH } from './mesh-link.js';
|
|
29
|
+
export type { MeshJoinLink } from './mesh-link.js';
|
|
28
30
|
export { LanBeacon, DEFAULT_LAN_DISCOVERY_PORT } from './lan-discovery.js';
|
|
29
31
|
export type { LanBeaconOptions, DiscoveredLanPeer } from './lan-discovery.js';
|
|
30
32
|
export { envelope, reply, frame, frameToSSE, parseSSE, newId } from './protocol.js';
|
|
@@ -18,6 +18,7 @@ export { AutoUpdate, compareVersions } from './auto-update.js';
|
|
|
18
18
|
export { ComputeRouter } from './compute-router.js';
|
|
19
19
|
export { loadRole, saveRole, rolePreamble } from './role-context.js';
|
|
20
20
|
export { discoverTailscalePeers, tailscaleAvailable } from './discovery.js';
|
|
21
|
+
export { buildMeshJoinLink, parseMeshJoinLink, redactMeshJoinLink, MESH_JOIN_PATH } from './mesh-link.js';
|
|
21
22
|
export { LanBeacon, DEFAULT_LAN_DISCOVERY_PORT } from './lan-discovery.js';
|
|
22
23
|
export { envelope, reply, frame, frameToSSE, parseSSE, newId } from './protocol.js';
|
|
23
24
|
export * as moltfed from './moltfed-adapter.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Canonical, copyable connection link for one Infinicode mesh node. */
|
|
2
|
+
export declare const MESH_JOIN_PATH = "/fed/join";
|
|
3
|
+
export interface MeshJoinLink {
|
|
4
|
+
/** Authenticated HTTP base URL consumed by Federation.connect(). */
|
|
5
|
+
seed: string;
|
|
6
|
+
/** Shared mesh bearer token, when the target mesh is protected. */
|
|
7
|
+
token?: string;
|
|
8
|
+
/** Original normalized link, suitable for MCP --connect. */
|
|
9
|
+
link: string;
|
|
10
|
+
}
|
|
11
|
+
/** Build a single shareable URL from a reachable mesh base URL. */
|
|
12
|
+
export declare function buildMeshJoinLink(baseUrl: string, token?: string): string;
|
|
13
|
+
/** Parse and validate a canonical mesh URL without making a network request. */
|
|
14
|
+
export declare function parseMeshJoinLink(value: string): MeshJoinLink;
|
|
15
|
+
/** Safe for terminals and logs while retaining enough information to identify the link. */
|
|
16
|
+
export declare function redactMeshJoinLink(value: string): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Canonical, copyable connection link for one Infinicode mesh node. */
|
|
2
|
+
export const MESH_JOIN_PATH = '/fed/join';
|
|
3
|
+
function parsedHttpUrl(value) {
|
|
4
|
+
let url;
|
|
5
|
+
try {
|
|
6
|
+
url = new URL(value.trim());
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
throw new Error('mesh link must be a valid http:// or https:// URL');
|
|
10
|
+
}
|
|
11
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
12
|
+
throw new Error('mesh link must use http:// or https://');
|
|
13
|
+
}
|
|
14
|
+
if (url.username || url.password)
|
|
15
|
+
throw new Error('mesh link must not contain URL user credentials');
|
|
16
|
+
return url;
|
|
17
|
+
}
|
|
18
|
+
/** Build a single shareable URL from a reachable mesh base URL. */
|
|
19
|
+
export function buildMeshJoinLink(baseUrl, token) {
|
|
20
|
+
const url = parsedHttpUrl(baseUrl);
|
|
21
|
+
url.pathname = MESH_JOIN_PATH;
|
|
22
|
+
url.search = '';
|
|
23
|
+
url.hash = '';
|
|
24
|
+
if (token)
|
|
25
|
+
url.searchParams.set('token', token);
|
|
26
|
+
return url.toString();
|
|
27
|
+
}
|
|
28
|
+
/** Parse and validate a canonical mesh URL without making a network request. */
|
|
29
|
+
export function parseMeshJoinLink(value) {
|
|
30
|
+
const url = parsedHttpUrl(value);
|
|
31
|
+
if (url.pathname.replace(/\/+$/, '') !== MESH_JOIN_PATH) {
|
|
32
|
+
throw new Error(`mesh link path must be ${MESH_JOIN_PATH}`);
|
|
33
|
+
}
|
|
34
|
+
const token = url.searchParams.get('token')?.trim() || undefined;
|
|
35
|
+
for (const key of url.searchParams.keys()) {
|
|
36
|
+
if (key !== 'token')
|
|
37
|
+
throw new Error(`unsupported mesh link parameter: ${key}`);
|
|
38
|
+
}
|
|
39
|
+
url.hash = '';
|
|
40
|
+
const seed = `${url.protocol}//${url.host}`;
|
|
41
|
+
return { seed, token, link: buildMeshJoinLink(seed, token) };
|
|
42
|
+
}
|
|
43
|
+
/** Safe for terminals and logs while retaining enough information to identify the link. */
|
|
44
|
+
export function redactMeshJoinLink(value) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = parseMeshJoinLink(value);
|
|
47
|
+
return buildMeshJoinLink(parsed.seed, parsed.token ? '<redacted>' : undefined);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return '<invalid mesh link>';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -22,6 +22,7 @@ import { readFileSync } from 'node:fs';
|
|
|
22
22
|
import { createRequire } from 'node:module';
|
|
23
23
|
import { once } from 'node:events';
|
|
24
24
|
import { frameToSSE, parseSSE } from './protocol.js';
|
|
25
|
+
import { buildMeshJoinLink, MESH_JOIN_PATH } from './mesh-link.js';
|
|
25
26
|
const execFileAsync = promisify(execFile);
|
|
26
27
|
/** Read the livekit-client UMD bundle from the installed dependency, once.
|
|
27
28
|
* Returns null (cached) if the package isn't present — the dashboard then
|
|
@@ -363,6 +364,29 @@ export class MeshServer {
|
|
|
363
364
|
return;
|
|
364
365
|
}
|
|
365
366
|
const remote = req.socket.remoteAddress ?? 'unknown';
|
|
367
|
+
if (req.method === 'GET' && path === MESH_JOIN_PATH) {
|
|
368
|
+
const rawHost = String(req.headers.host ?? '');
|
|
369
|
+
const authority = /^[A-Za-z0-9.:[\]-]+$/.test(rawHost) ? rawHost : `127.0.0.1:${this.opts.port}`;
|
|
370
|
+
const scheme = this.opts.tls ? 'https' : 'http';
|
|
371
|
+
const link = buildMeshJoinLink(`${scheme}://${authority}`, this.opts.token ? this.presentedToken(req) : undefined);
|
|
372
|
+
const manifest = this.opts.getManifest();
|
|
373
|
+
res.writeHead(200, {
|
|
374
|
+
'content-type': 'application/json; charset=utf-8',
|
|
375
|
+
'cache-control': 'no-store',
|
|
376
|
+
'referrer-policy': 'no-referrer',
|
|
377
|
+
});
|
|
378
|
+
res.end(JSON.stringify({
|
|
379
|
+
protocol: 'infinicode-mesh-join',
|
|
380
|
+
version: 1,
|
|
381
|
+
link,
|
|
382
|
+
node: { id: manifest.nodeId, name: manifest.displayName, role: manifest.role },
|
|
383
|
+
use: {
|
|
384
|
+
direct: `infinicode mcp --connect "${link}"`,
|
|
385
|
+
install: `infinicode mesh join "${link}" --host all`,
|
|
386
|
+
},
|
|
387
|
+
}));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
366
390
|
if (req.method === 'GET' && path === '/fed/ui-build') {
|
|
367
391
|
const marker = this.opts.dashboardHtml?.match(/<meta name="robopark-ui-build" content="([^"]+)">/)?.[1] ?? 'none';
|
|
368
392
|
res.writeHead(200, {
|
package/package.json
CHANGED