infinicode 2.8.27 → 2.8.29
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/.opencode/plugins/home-logo-animation.tsx +124 -124
- package/.opencode/plugins/mesh-commands.tsx +140 -140
- package/.opencode/plugins/routing-mode-display.tsx +138 -138
- package/.opencode/themes/infinibot-gold.json +222 -222
- package/.opencode/tui.json +8 -8
- package/README.md +623 -623
- package/bin/robopark.js +2 -2
- package/dist/kernel/agents/backends/registry.js +22 -2
- package/dist/kernel/federation/dashboard-html.d.ts +1 -1
- package/dist/kernel/federation/dashboard-html.js +1512 -1403
- package/dist/kernel/plugins/dashboard-plugin.js +8 -8
- package/dist/robopark/add-robot.js +25 -3
- package/dist/robopark/auto-start.js +38 -38
- package/dist/robopark/probe.js +15 -15
- package/dist/robopark/setup-livekit.js +52 -52
- package/package.json +2 -2
- package/packages/robopark/README.md +63 -63
- package/packages/robopark/pi-client/README.md +17 -17
- package/packages/robopark/scheduler/main.py +827 -2
- package/packages/robopark/scheduler/preview_agent.py +7 -0
- package/packages/robopark/scheduler/requirements-robot.txt +9 -9
- package/packages/robopark/vision/README.md +66 -66
- package/packages/robopark/vision/requirements-demo.txt +16 -16
|
@@ -33,14 +33,14 @@ export function createDashboardPlugin(options = {}) {
|
|
|
33
33
|
}
|
|
34
34
|
// HTML dashboard
|
|
35
35
|
const missions = kernelRef?.listMissions() ?? [];
|
|
36
|
-
const html = `<!doctype html><html><head><meta charset="utf-8"><title>OpenKernel Dashboard</title>
|
|
37
|
-
<style>body{font:14px/1.5 system-ui;margin:2rem}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:4px 8px}</style></head>
|
|
38
|
-
<body><h1>OpenKernel Dashboard</h1>
|
|
39
|
-
<h2>Missions (${missions.length})</h2>
|
|
40
|
-
<table><tr><th>ID</th><th>Name</th><th>Status</th><th>Goal</th><th>Tasks</th></tr>
|
|
41
|
-
${missions.map(m => `<tr><td>${m.id}</td><td>${m.name}</td><td>${m.status}</td><td>${escapeHtml(m.goal)}</td><td>${m.tasks.filter(t => t.status === 'COMPLETED').length}/${m.tasks.length}</td></tr>`).join('')}
|
|
42
|
-
</table>
|
|
43
|
-
<p><a href="/status">/status</a> · <a href="/missions">/missions</a> · <a href="/events">/events</a></p>
|
|
36
|
+
const html = `<!doctype html><html><head><meta charset="utf-8"><title>OpenKernel Dashboard</title>
|
|
37
|
+
<style>body{font:14px/1.5 system-ui;margin:2rem}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:4px 8px}</style></head>
|
|
38
|
+
<body><h1>OpenKernel Dashboard</h1>
|
|
39
|
+
<h2>Missions (${missions.length})</h2>
|
|
40
|
+
<table><tr><th>ID</th><th>Name</th><th>Status</th><th>Goal</th><th>Tasks</th></tr>
|
|
41
|
+
${missions.map(m => `<tr><td>${m.id}</td><td>${m.name}</td><td>${m.status}</td><td>${escapeHtml(m.goal)}</td><td>${m.tasks.filter(t => t.status === 'COMPLETED').length}/${m.tasks.length}</td></tr>`).join('')}
|
|
42
|
+
</table>
|
|
43
|
+
<p><a href="/status">/status</a> · <a href="/missions">/missions</a> · <a href="/events">/events</a></p>
|
|
44
44
|
</body></html>`;
|
|
45
45
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
46
46
|
res.end(html);
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
import chalk from 'chalk';
|
|
25
25
|
import { resolveContext } from './profile.js';
|
|
26
26
|
import { roboparkSetup } from './setup.js';
|
|
27
|
-
async function mintEnrollmentToken(schedulerUrl, name) {
|
|
27
|
+
async function mintEnrollmentToken(schedulerUrl, name, characterId) {
|
|
28
28
|
const url = `${schedulerUrl.replace(/\/$/, '')}/api/devices`;
|
|
29
29
|
const res = await fetch(url, {
|
|
30
30
|
method: 'POST',
|
|
31
31
|
headers: { 'content-type': 'application/json' },
|
|
32
|
-
body: JSON.stringify({ name }),
|
|
32
|
+
body: JSON.stringify({ name, character_id: characterId }),
|
|
33
33
|
});
|
|
34
34
|
if (!res.ok) {
|
|
35
35
|
const text = await res.text().catch(() => '');
|
|
@@ -37,6 +37,24 @@ async function mintEnrollmentToken(schedulerUrl, name) {
|
|
|
37
37
|
}
|
|
38
38
|
return (await res.json());
|
|
39
39
|
}
|
|
40
|
+
async function pickCharacterPreset(schedulerUrl, name) {
|
|
41
|
+
const lower = name.toLowerCase();
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(`${schedulerUrl.replace(/\/$/, '')}/api/character-presets`);
|
|
44
|
+
if (!res.ok)
|
|
45
|
+
return undefined;
|
|
46
|
+
const presets = (await res.json());
|
|
47
|
+
// Exact id match first, then fuzzy name match.
|
|
48
|
+
const exact = presets.find(p => p.id.toLowerCase() === lower || p.name.toLowerCase() === lower);
|
|
49
|
+
if (exact)
|
|
50
|
+
return exact.id;
|
|
51
|
+
const partial = presets.find(p => lower.includes(p.id.toLowerCase()) || p.name.toLowerCase().includes(lower));
|
|
52
|
+
return partial?.id;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
40
58
|
/**
|
|
41
59
|
* Build a Windows-task-friendly command string from an argv array.
|
|
42
60
|
*
|
|
@@ -63,9 +81,13 @@ export async function roboparkAddRobot(config, opts) {
|
|
|
63
81
|
console.log(` scheduler: ${chalk.cyan(ctx.schedulerUrl)}`);
|
|
64
82
|
console.log(` name: ${chalk.cyan(opts.name)}${ctx.site ? chalk.dim(' @ ' + ctx.site) : ''}`);
|
|
65
83
|
console.log();
|
|
84
|
+
const characterId = await pickCharacterPreset(ctx.schedulerUrl, opts.name);
|
|
85
|
+
if (characterId) {
|
|
86
|
+
console.log(` character: ${chalk.cyan(characterId)}`);
|
|
87
|
+
}
|
|
66
88
|
let minted;
|
|
67
89
|
try {
|
|
68
|
-
minted = await mintEnrollmentToken(ctx.schedulerUrl, opts.name);
|
|
90
|
+
minted = await mintEnrollmentToken(ctx.schedulerUrl, opts.name, characterId);
|
|
69
91
|
}
|
|
70
92
|
catch (e) {
|
|
71
93
|
console.log(chalk.red(` ✗ failed to mint enrollment token: ${e.message}`));
|
|
@@ -65,21 +65,21 @@ async function registerSystemd(service) {
|
|
|
65
65
|
const envLines = Object.entries(service.env ?? {})
|
|
66
66
|
.map(([k, v]) => `Environment="${k}=${v.replace(/"/g, '\\"')}"`)
|
|
67
67
|
.join('\n');
|
|
68
|
-
const unit = `[Unit]
|
|
69
|
-
Description=RoboPark ${service.role} for ${service.name}
|
|
70
|
-
After=network-online.target
|
|
71
|
-
Wants=network-online.target
|
|
72
|
-
|
|
73
|
-
[Service]
|
|
74
|
-
Type=simple
|
|
75
|
-
ExecStart=${service.command} ${service.args.map(shellQuote).join(' ')}
|
|
76
|
-
WorkingDirectory=${service.workingDir ?? '/opt/robopark'}
|
|
77
|
-
Restart=always
|
|
78
|
-
RestartSec=5
|
|
79
|
-
${envLines}
|
|
80
|
-
|
|
81
|
-
[Install]
|
|
82
|
-
WantedBy=multi-user.target
|
|
68
|
+
const unit = `[Unit]
|
|
69
|
+
Description=RoboPark ${service.role} for ${service.name}
|
|
70
|
+
After=network-online.target
|
|
71
|
+
Wants=network-online.target
|
|
72
|
+
|
|
73
|
+
[Service]
|
|
74
|
+
Type=simple
|
|
75
|
+
ExecStart=${service.command} ${service.args.map(shellQuote).join(' ')}
|
|
76
|
+
WorkingDirectory=${service.workingDir ?? '/opt/robopark'}
|
|
77
|
+
Restart=always
|
|
78
|
+
RestartSec=5
|
|
79
|
+
${envLines}
|
|
80
|
+
|
|
81
|
+
[Install]
|
|
82
|
+
WantedBy=multi-user.target
|
|
83
83
|
`;
|
|
84
84
|
try {
|
|
85
85
|
writeFileSync(unitPath, unit, 'utf8');
|
|
@@ -94,29 +94,29 @@ async function registerLaunchd(service) {
|
|
|
94
94
|
const plistPath = join(homedir(), 'Library', 'LaunchAgents', `${label}.plist`);
|
|
95
95
|
mkdirSync(join(homedir(), 'Library', 'LaunchAgents'), { recursive: true });
|
|
96
96
|
const env = service.env ?? {};
|
|
97
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
98
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
99
|
-
<plist version="1.0">
|
|
100
|
-
<dict>
|
|
101
|
-
<key>Label</key>
|
|
102
|
-
<string>${label}</string>
|
|
103
|
-
<key>ProgramArguments</key>
|
|
104
|
-
<array>
|
|
105
|
-
<string>${service.command}</string>
|
|
106
|
-
${service.args.map(a => `<string>${a.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')}</string>`).join('\n ')}
|
|
107
|
-
</array>
|
|
108
|
-
<key>WorkingDirectory</key>
|
|
109
|
-
<string>${service.workingDir ?? homedir()}</string>
|
|
110
|
-
<key>RunAtLoad</key>
|
|
111
|
-
<true/>
|
|
112
|
-
<key>KeepAlive</key>
|
|
113
|
-
<true/>
|
|
114
|
-
<key>StandardOutPath</key>
|
|
115
|
-
<string>${join(homedir(), 'Library', 'Logs', `${label}.log`)}</string>
|
|
116
|
-
<key>StandardErrorPath</key>
|
|
117
|
-
<string>${join(homedir(), 'Library', 'Logs', `${label}.err.log`)}</string>
|
|
118
|
-
${Object.entries(env).map(([k, v]) => `<key>EnvironmentVariables</key>\n <dict>\n <key>${k}</key>\n <string>${v}</string>\n </dict>`).join('\n ')}
|
|
119
|
-
</dict>
|
|
97
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
98
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
99
|
+
<plist version="1.0">
|
|
100
|
+
<dict>
|
|
101
|
+
<key>Label</key>
|
|
102
|
+
<string>${label}</string>
|
|
103
|
+
<key>ProgramArguments</key>
|
|
104
|
+
<array>
|
|
105
|
+
<string>${service.command}</string>
|
|
106
|
+
${service.args.map(a => `<string>${a.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')}</string>`).join('\n ')}
|
|
107
|
+
</array>
|
|
108
|
+
<key>WorkingDirectory</key>
|
|
109
|
+
<string>${service.workingDir ?? homedir()}</string>
|
|
110
|
+
<key>RunAtLoad</key>
|
|
111
|
+
<true/>
|
|
112
|
+
<key>KeepAlive</key>
|
|
113
|
+
<true/>
|
|
114
|
+
<key>StandardOutPath</key>
|
|
115
|
+
<string>${join(homedir(), 'Library', 'Logs', `${label}.log`)}</string>
|
|
116
|
+
<key>StandardErrorPath</key>
|
|
117
|
+
<string>${join(homedir(), 'Library', 'Logs', `${label}.err.log`)}</string>
|
|
118
|
+
${Object.entries(env).map(([k, v]) => `<key>EnvironmentVariables</key>\n <dict>\n <key>${k}</key>\n <string>${v}</string>\n </dict>`).join('\n ')}
|
|
119
|
+
</dict>
|
|
120
120
|
</plist>`;
|
|
121
121
|
try {
|
|
122
122
|
writeFileSync(plistPath, plist, 'utf8');
|
package/dist/robopark/probe.js
CHANGED
|
@@ -72,21 +72,21 @@ function runOpencodePrompt(prompt, timeoutMs) {
|
|
|
72
72
|
}
|
|
73
73
|
function probePrompt(target, token) {
|
|
74
74
|
const tokenHint = token ? `mesh token is "${token}"` : 'no mesh token provided';
|
|
75
|
-
return `You are an infrastructure probe for the RoboPark fleet.
|
|
76
|
-
Target node: ${target.name} (${target.ip}) via ${target.source}.
|
|
77
|
-
${tokenHint}.
|
|
78
|
-
|
|
79
|
-
Do NOT modify anything. Only report facts. Keep your answer under 600 words.
|
|
80
|
-
|
|
81
|
-
Please report:
|
|
82
|
-
1. Hostname, OS, architecture.
|
|
83
|
-
2. LAN IPs and Tailscale IP if available.
|
|
84
|
-
3. Whether these processes are running: infinicode node, robopark scheduler, robopark preview_agent.
|
|
85
|
-
4. If this node is the hub, whether the scheduler at http://localhost:8080/api/settings responds and what production_mode is.
|
|
86
|
-
5. If this node is a robot/satellite, whether it has enrolled to the scheduler and whether ~/.robopark/device_token exists.
|
|
87
|
-
6. Any recent errors in journalctl/systemctl for infinicode or robopark services (last 10 lines).
|
|
88
|
-
7. Whether a camera (/dev/video0) or microphone is detected.
|
|
89
|
-
|
|
75
|
+
return `You are an infrastructure probe for the RoboPark fleet.
|
|
76
|
+
Target node: ${target.name} (${target.ip}) via ${target.source}.
|
|
77
|
+
${tokenHint}.
|
|
78
|
+
|
|
79
|
+
Do NOT modify anything. Only report facts. Keep your answer under 600 words.
|
|
80
|
+
|
|
81
|
+
Please report:
|
|
82
|
+
1. Hostname, OS, architecture.
|
|
83
|
+
2. LAN IPs and Tailscale IP if available.
|
|
84
|
+
3. Whether these processes are running: infinicode node, robopark scheduler, robopark preview_agent.
|
|
85
|
+
4. If this node is the hub, whether the scheduler at http://localhost:8080/api/settings responds and what production_mode is.
|
|
86
|
+
5. If this node is a robot/satellite, whether it has enrolled to the scheduler and whether ~/.robopark/device_token exists.
|
|
87
|
+
6. Any recent errors in journalctl/systemctl for infinicode or robopark services (last 10 lines).
|
|
88
|
+
7. Whether a camera (/dev/video0) or microphone is detected.
|
|
89
|
+
|
|
90
90
|
Answer as concise structured Markdown bullet list.`;
|
|
91
91
|
}
|
|
92
92
|
async function probeTarget(target, opts) {
|
|
@@ -83,68 +83,68 @@ async function getHubPlatformFromStatus(hubUrl, token) {
|
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
const LIVEKIT_SETUP_PROMPT_HEADER = `You are the RoboPark infrastructure agent running on the fleet hub.
|
|
87
|
-
|
|
88
|
-
GOAL: Install and start a LiveKit server in Docker on this machine so the RoboPark scheduler can mint tokens and the robot preview agent can publish camera/audio.
|
|
89
|
-
|
|
90
|
-
DO NOT ask the user for confirmation. Execute the steps directly. If a step is already done, skip it and report it.
|
|
86
|
+
const LIVEKIT_SETUP_PROMPT_HEADER = `You are the RoboPark infrastructure agent running on the fleet hub.
|
|
87
|
+
|
|
88
|
+
GOAL: Install and start a LiveKit server in Docker on this machine so the RoboPark scheduler can mint tokens and the robot preview agent can publish camera/audio.
|
|
89
|
+
|
|
90
|
+
DO NOT ask the user for confirmation. Execute the steps directly. If a step is already done, skip it and report it.
|
|
91
91
|
`;
|
|
92
|
-
const LIVEKIT_SETUP_PROMPT_FOOTER = `
|
|
93
|
-
IMPORTANT: this is a headless fleet node. Do not open a browser. Only run shell commands and report the final LiveKit URL + credentials in a JSON code block like:
|
|
94
|
-
\`\`\`json
|
|
95
|
-
{
|
|
96
|
-
"url": "ws://127.0.0.1:7880",
|
|
97
|
-
"api_key": "...",
|
|
98
|
-
"api_secret": "...",
|
|
99
|
-
"running": true
|
|
100
|
-
}
|
|
101
|
-
\`\`\`
|
|
92
|
+
const LIVEKIT_SETUP_PROMPT_FOOTER = `
|
|
93
|
+
IMPORTANT: this is a headless fleet node. Do not open a browser. Only run shell commands and report the final LiveKit URL + credentials in a JSON code block like:
|
|
94
|
+
\`\`\`json
|
|
95
|
+
{
|
|
96
|
+
"url": "ws://127.0.0.1:7880",
|
|
97
|
+
"api_key": "...",
|
|
98
|
+
"api_secret": "...",
|
|
99
|
+
"running": true
|
|
100
|
+
}
|
|
101
|
+
\`\`\`
|
|
102
102
|
`;
|
|
103
|
-
const LIVEKIT_SETUP_STEPS_LINUX = `Steps:
|
|
104
|
-
1. Check if Docker is installed and running. If not, install Docker (apt-get install docker.io on Debian/Ubuntu, or dnf install docker on Fedora, or use the official convenience script if needed) and start it with systemctl.
|
|
105
|
-
2. Pull the official LiveKit server image: docker pull livekit/livekit-server:latest
|
|
106
|
-
3. Create a directory /opt/livekit if it does not exist.
|
|
107
|
-
4. Generate API credentials: run the LiveKit CLI container to create a key/secret and save them to /opt/livekit/keys.yaml:
|
|
108
|
-
docker run --rm livekit/livekit-server:latest generate-keys
|
|
109
|
-
Parse the output and write it as a YAML file with fields api_key and api_secret.
|
|
110
|
-
5. Start the LiveKit server container with host networking on port 7880:
|
|
111
|
-
docker run -d --name livekit-server --restart unless-stopped --network host \\
|
|
112
|
-
-v /opt/livekit/keys.yaml:/etc/livekit/keys.yaml:ro \\
|
|
113
|
-
livekit/livekit-server:latest \\
|
|
114
|
-
--config /etc/livekit/keys.yaml --bind 0.0.0.0 --port 7880
|
|
115
|
-
6. Wait up to 30 seconds and verify it is healthy: curl -sf http://127.0.0.1:7880
|
|
116
|
-
7. Report back the WebSocket URL (ws://THIS_MACHINE_TAILSCALE_IP:7880 or ws://127.0.0.1:7880), the api_key, the api_secret, and whether the container is running.
|
|
103
|
+
const LIVEKIT_SETUP_STEPS_LINUX = `Steps:
|
|
104
|
+
1. Check if Docker is installed and running. If not, install Docker (apt-get install docker.io on Debian/Ubuntu, or dnf install docker on Fedora, or use the official convenience script if needed) and start it with systemctl.
|
|
105
|
+
2. Pull the official LiveKit server image: docker pull livekit/livekit-server:latest
|
|
106
|
+
3. Create a directory /opt/livekit if it does not exist.
|
|
107
|
+
4. Generate API credentials: run the LiveKit CLI container to create a key/secret and save them to /opt/livekit/keys.yaml:
|
|
108
|
+
docker run --rm livekit/livekit-server:latest generate-keys
|
|
109
|
+
Parse the output and write it as a YAML file with fields api_key and api_secret.
|
|
110
|
+
5. Start the LiveKit server container with host networking on port 7880:
|
|
111
|
+
docker run -d --name livekit-server --restart unless-stopped --network host \\
|
|
112
|
+
-v /opt/livekit/keys.yaml:/etc/livekit/keys.yaml:ro \\
|
|
113
|
+
livekit/livekit-server:latest \\
|
|
114
|
+
--config /etc/livekit/keys.yaml --bind 0.0.0.0 --port 7880
|
|
115
|
+
6. Wait up to 30 seconds and verify it is healthy: curl -sf http://127.0.0.1:7880
|
|
116
|
+
7. Report back the WebSocket URL (ws://THIS_MACHINE_TAILSCALE_IP:7880 or ws://127.0.0.1:7880), the api_key, the api_secret, and whether the container is running.
|
|
117
117
|
`;
|
|
118
118
|
// Windows hosts: Docker Desktop does not support --network host, so ports are
|
|
119
119
|
// mapped explicitly. --dev mode ships with well-known devkey/secret, which
|
|
120
120
|
// sidesteps the `generate-keys` dance (that container command is awkward to
|
|
121
121
|
// run/parse consistently from PowerShell).
|
|
122
|
-
const LIVEKIT_SETUP_STEPS_WINDOWS = `Steps (Windows host — use PowerShell, NOT bash):
|
|
123
|
-
1. Check if Docker Desktop is installed and running: docker info
|
|
124
|
-
If it is not installed, stop and report that Docker Desktop must be installed manually (winget install Docker.DockerDesktop), since it requires a GUI/WSL2 backend and cannot be silently installed headlessly.
|
|
125
|
-
2. Pull the official LiveKit server image: docker pull livekit/livekit-server:latest
|
|
126
|
-
3. Start the LiveKit server container in --dev mode (uses the well-known devkey/secret credentials, no key generation needed). Docker Desktop on Windows does not support --network host, so map ports explicitly:
|
|
127
|
-
docker run -d --name livekit-server --restart unless-stopped \`
|
|
128
|
-
-p 7880:7880 -p 7881:7881 -p 7881:7881/udp -p 50000-50100:50000-50100/udp \`
|
|
129
|
-
livekit/livekit-server:latest --dev --bind 0.0.0.0
|
|
130
|
-
(If a container named livekit-server already exists, remove it first with: docker rm -f livekit-server)
|
|
131
|
-
4. Wait up to 30 seconds and verify it is healthy: curl.exe -sf http://127.0.0.1:7880 (or Invoke-WebRequest)
|
|
132
|
-
5. The credentials in --dev mode are fixed: api_key = devkey, api_secret = secret.
|
|
133
|
-
6. Report back the WebSocket URL (ws://THIS_MACHINE_TAILSCALE_IP:7880 or ws://127.0.0.1:7880), api_key "devkey", api_secret "secret", and whether the container is running.
|
|
122
|
+
const LIVEKIT_SETUP_STEPS_WINDOWS = `Steps (Windows host — use PowerShell, NOT bash):
|
|
123
|
+
1. Check if Docker Desktop is installed and running: docker info
|
|
124
|
+
If it is not installed, stop and report that Docker Desktop must be installed manually (winget install Docker.DockerDesktop), since it requires a GUI/WSL2 backend and cannot be silently installed headlessly.
|
|
125
|
+
2. Pull the official LiveKit server image: docker pull livekit/livekit-server:latest
|
|
126
|
+
3. Start the LiveKit server container in --dev mode (uses the well-known devkey/secret credentials, no key generation needed). Docker Desktop on Windows does not support --network host, so map ports explicitly:
|
|
127
|
+
docker run -d --name livekit-server --restart unless-stopped \`
|
|
128
|
+
-p 7880:7880 -p 7881:7881 -p 7881:7881/udp -p 50000-50100:50000-50100/udp \`
|
|
129
|
+
livekit/livekit-server:latest --dev --bind 0.0.0.0
|
|
130
|
+
(If a container named livekit-server already exists, remove it first with: docker rm -f livekit-server)
|
|
131
|
+
4. Wait up to 30 seconds and verify it is healthy: curl.exe -sf http://127.0.0.1:7880 (or Invoke-WebRequest)
|
|
132
|
+
5. The credentials in --dev mode are fixed: api_key = devkey, api_secret = secret.
|
|
133
|
+
6. Report back the WebSocket URL (ws://THIS_MACHINE_TAILSCALE_IP:7880 or ws://127.0.0.1:7880), api_key "devkey", api_secret "secret", and whether the container is running.
|
|
134
134
|
`;
|
|
135
135
|
// Used when the hub's platform could not be determined before dispatch: ask
|
|
136
136
|
// the agent to detect its own OS first, then follow the matching branch.
|
|
137
|
-
const LIVEKIT_SETUP_STEPS_AUTODETECT = `Steps:
|
|
138
|
-
0. FIRST, detect your own OS before running anything else:
|
|
139
|
-
- On Windows, $env:OS will be "Windows_NT" (PowerShell) — or the presence of \`Get-Command\` with no \`uname\` binary.
|
|
140
|
-
- On Linux/macOS, \`uname\` will succeed and print Linux/Darwin.
|
|
141
|
-
Branch into ONE of the two command sets below based on that detection. Do not run both.
|
|
142
|
-
|
|
143
|
-
--- IF WINDOWS (use PowerShell): ---
|
|
144
|
-
${LIVEKIT_SETUP_STEPS_WINDOWS}
|
|
145
|
-
|
|
146
|
-
--- IF LINUX/macOS (use bash/sh): ---
|
|
147
|
-
${LIVEKIT_SETUP_STEPS_LINUX}
|
|
137
|
+
const LIVEKIT_SETUP_STEPS_AUTODETECT = `Steps:
|
|
138
|
+
0. FIRST, detect your own OS before running anything else:
|
|
139
|
+
- On Windows, $env:OS will be "Windows_NT" (PowerShell) — or the presence of \`Get-Command\` with no \`uname\` binary.
|
|
140
|
+
- On Linux/macOS, \`uname\` will succeed and print Linux/Darwin.
|
|
141
|
+
Branch into ONE of the two command sets below based on that detection. Do not run both.
|
|
142
|
+
|
|
143
|
+
--- IF WINDOWS (use PowerShell): ---
|
|
144
|
+
${LIVEKIT_SETUP_STEPS_WINDOWS}
|
|
145
|
+
|
|
146
|
+
--- IF LINUX/macOS (use bash/sh): ---
|
|
147
|
+
${LIVEKIT_SETUP_STEPS_LINUX}
|
|
148
148
|
`;
|
|
149
149
|
function buildLivekitSetupPrompt(platform) {
|
|
150
150
|
let steps;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinicode",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.29",
|
|
4
4
|
"description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/kernel/index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"license": "MIT",
|
|
68
68
|
"repository": {
|
|
69
69
|
"type": "git",
|
|
70
|
-
"url": "https://github.com/
|
|
70
|
+
"url": "https://github.com/MrYungCEO/infinicode"
|
|
71
71
|
},
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=20"
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
# robopark
|
|
2
|
-
|
|
3
|
-
The **RoboPark fleet control CLI** — set up, watch, and drive a room full of
|
|
4
|
-
talking robots from one command. `robopark` is the operator front-end; it rides
|
|
5
|
-
the [`infinicode`](https://www.npmjs.com/package/infinicode) device mesh, which
|
|
6
|
-
it installs as a dependency. You install and think in one name — everything else
|
|
7
|
-
is plumbing.
|
|
8
|
-
|
|
9
|
-
## Install
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm install -g robopark
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## The whole setup — two commands
|
|
16
|
-
|
|
17
|
-
Operators think in **scheduler / robot / laptop**; the mesh underneath thinks in
|
|
18
|
-
hub / satellite / peer. `robopark` translates, and each role turns on the right
|
|
19
|
-
defaults automatically.
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
# On the on-site hub (LiveKit Server 1) — LAN + Tailscale + dashboard on:
|
|
23
|
-
robopark setup --scheduler --gateway ws://<infinibot-gateway>:18789 --start
|
|
24
|
-
|
|
25
|
-
# On each robot Pi — accept-only, LAN-discovered:
|
|
26
|
-
robopark setup --robot --name robopanda --start
|
|
27
|
-
|
|
28
|
-
# From anywhere — a live table of the whole fleet:
|
|
29
|
-
robopark fleet --watch
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Run `robopark setup` with no role for a guided wizard.
|
|
33
|
-
|
|
34
|
-
## Commands
|
|
35
|
-
|
|
36
|
-
| Command | Does |
|
|
37
|
-
|---|---|
|
|
38
|
-
| `robopark setup` | Configure this device's role + site and (with `--start`) launch its node. |
|
|
39
|
-
| `robopark fleet [--watch]` | Live table of every node the mesh can see. |
|
|
40
|
-
| `robopark run <node> <cmd>` | Run a command on one node (use `.` for the local node). |
|
|
41
|
-
| `robopark apply` | Publish this hub's providers / keys / policy to every satellite. |
|
|
42
|
-
| `robopark dashboard [--open]` | Open the web control panel served by the hub. |
|
|
43
|
-
|
|
44
|
-
## How it fits together
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
you ── robopark ──▶ infinicode nodes (the mesh) ──▶ InfiniBot (watch)
|
|
48
|
-
└──▶ web dashboard :47913 (watch / drive)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- **robopark** — the remote control. A CLI, no daemon. Writes config and calls
|
|
52
|
-
each node's HTTP API.
|
|
53
|
-
- **infinicode** — the runtime. `infinicode serve` runs on every device, forms
|
|
54
|
-
the mesh, runs agents, streams telemetry, and serves the dashboard.
|
|
55
|
-
- **InfiniBot** — the cockpit. Each node pushes its status *up* to InfiniBot;
|
|
56
|
-
you watch there (or in the built-in dashboard).
|
|
57
|
-
|
|
58
|
-
`robopark` never talks to InfiniBot directly — it drives infinicode nodes, and
|
|
59
|
-
those surface in InfiniBot.
|
|
60
|
-
|
|
61
|
-
## License
|
|
62
|
-
|
|
63
|
-
MIT
|
|
1
|
+
# robopark
|
|
2
|
+
|
|
3
|
+
The **RoboPark fleet control CLI** — set up, watch, and drive a room full of
|
|
4
|
+
talking robots from one command. `robopark` is the operator front-end; it rides
|
|
5
|
+
the [`infinicode`](https://www.npmjs.com/package/infinicode) device mesh, which
|
|
6
|
+
it installs as a dependency. You install and think in one name — everything else
|
|
7
|
+
is plumbing.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install -g robopark
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## The whole setup — two commands
|
|
16
|
+
|
|
17
|
+
Operators think in **scheduler / robot / laptop**; the mesh underneath thinks in
|
|
18
|
+
hub / satellite / peer. `robopark` translates, and each role turns on the right
|
|
19
|
+
defaults automatically.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
# On the on-site hub (LiveKit Server 1) — LAN + Tailscale + dashboard on:
|
|
23
|
+
robopark setup --scheduler --gateway ws://<infinibot-gateway>:18789 --start
|
|
24
|
+
|
|
25
|
+
# On each robot Pi — accept-only, LAN-discovered:
|
|
26
|
+
robopark setup --robot --name robopanda --start
|
|
27
|
+
|
|
28
|
+
# From anywhere — a live table of the whole fleet:
|
|
29
|
+
robopark fleet --watch
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run `robopark setup` with no role for a guided wizard.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
| Command | Does |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `robopark setup` | Configure this device's role + site and (with `--start`) launch its node. |
|
|
39
|
+
| `robopark fleet [--watch]` | Live table of every node the mesh can see. |
|
|
40
|
+
| `robopark run <node> <cmd>` | Run a command on one node (use `.` for the local node). |
|
|
41
|
+
| `robopark apply` | Publish this hub's providers / keys / policy to every satellite. |
|
|
42
|
+
| `robopark dashboard [--open]` | Open the web control panel served by the hub. |
|
|
43
|
+
|
|
44
|
+
## How it fits together
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
you ── robopark ──▶ infinicode nodes (the mesh) ──▶ InfiniBot (watch)
|
|
48
|
+
└──▶ web dashboard :47913 (watch / drive)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **robopark** — the remote control. A CLI, no daemon. Writes config and calls
|
|
52
|
+
each node's HTTP API.
|
|
53
|
+
- **infinicode** — the runtime. `infinicode serve` runs on every device, forms
|
|
54
|
+
the mesh, runs agents, streams telemetry, and serves the dashboard.
|
|
55
|
+
- **InfiniBot** — the cockpit. Each node pushes its status *up* to InfiniBot;
|
|
56
|
+
you watch there (or in the built-in dashboard).
|
|
57
|
+
|
|
58
|
+
`robopark` never talks to InfiniBot directly — it drives infinicode nodes, and
|
|
59
|
+
those surface in InfiniBot.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# robopark-pi-client — full robot client (reference)
|
|
2
|
-
|
|
3
|
-
The full on-robot client: enrollment, heartbeat, motor control, and a
|
|
4
|
-
LiveKit publish loop (`livekit_bridge.py`) that joins a standing per-device
|
|
5
|
-
room whenever `production_mode` is on. `pi_ui.py` serves a touchscreen UI
|
|
6
|
-
with a manual "Join Conversation" button — the human-in-the-loop trigger.
|
|
7
|
-
|
|
8
|
-
This is a reference implementation shipped for Pi deployment; it is not
|
|
9
|
-
currently wired into `robopark setup --robot` (which uses `../scheduler/
|
|
10
|
-
preview_agent.py` for the on-demand preview + vision-trigger flow — see
|
|
11
|
-
`../vision/README.md`). `client.py`'s standing-room model and the scheduler's
|
|
12
|
-
`request-session`-per-trigger model are two different session strategies;
|
|
13
|
-
reconciling them into one is follow-up work, not done here.
|
|
14
|
-
|
|
15
|
-
Install: `pip install -r requirements.txt` (`livekit`/`livekit-api` are
|
|
16
|
-
commented out — Pi-only, install separately with the `livekit` SDK).
|
|
17
|
-
`install.sh`/`*.service` are Linux/Pi systemd wrappers.
|
|
1
|
+
# robopark-pi-client — full robot client (reference)
|
|
2
|
+
|
|
3
|
+
The full on-robot client: enrollment, heartbeat, motor control, and a
|
|
4
|
+
LiveKit publish loop (`livekit_bridge.py`) that joins a standing per-device
|
|
5
|
+
room whenever `production_mode` is on. `pi_ui.py` serves a touchscreen UI
|
|
6
|
+
with a manual "Join Conversation" button — the human-in-the-loop trigger.
|
|
7
|
+
|
|
8
|
+
This is a reference implementation shipped for Pi deployment; it is not
|
|
9
|
+
currently wired into `robopark setup --robot` (which uses `../scheduler/
|
|
10
|
+
preview_agent.py` for the on-demand preview + vision-trigger flow — see
|
|
11
|
+
`../vision/README.md`). `client.py`'s standing-room model and the scheduler's
|
|
12
|
+
`request-session`-per-trigger model are two different session strategies;
|
|
13
|
+
reconciling them into one is follow-up work, not done here.
|
|
14
|
+
|
|
15
|
+
Install: `pip install -r requirements.txt` (`livekit`/`livekit-api` are
|
|
16
|
+
commented out — Pi-only, install separately with the `livekit` SDK).
|
|
17
|
+
`install.sh`/`*.service` are Linux/Pi systemd wrappers.
|