robopark 3.3.36 → 3.3.38
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/robopark/add-robot.js +2 -2
- package/dist/robopark/add-robot.js.map +1 -1
- package/dist/robopark/audio-select.js +128 -0
- package/dist/robopark/audio-select.js.map +1 -0
- package/dist/robopark/device-preempt.js +185 -0
- package/dist/robopark/device-preempt.js.map +1 -0
- package/dist/robopark/kiosk-browser.js +145 -0
- package/dist/robopark/kiosk-browser.js.map +1 -0
- package/dist/robopark/start.js +455 -0
- package/dist/robopark/start.js.map +1 -0
- package/dist/robopark/voice-list.js +23 -12
- package/dist/robopark/voice-list.js.map +1 -1
- package/dist/robopark-cli.js +25 -0
- package/dist/robopark-cli.js.map +1 -1
- package/package.json +7 -3
- package/scheduler/main.py +150 -30
- package/scheduler/static/voice-join.html +48 -2
- package/ui/standalone/.next/BUILD_ID +1 -1
- package/ui/standalone/.next/app-build-manifest.json +72 -72
- package/ui/standalone/.next/app-path-routes-manifest.json +16 -16
- package/ui/standalone/.next/build-manifest.json +2 -2
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js +1 -1
- package/ui/standalone/.next/server/app/fed/[...path]/route.js +2 -2
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js +1 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route.js +1 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js +1 -1
- package/ui/standalone/.next/server/app-paths-manifest.json +16 -16
- package/ui/standalone/.next/server/functions-config-manifest.json +1 -1
- package/ui/standalone/.next/server/pages/500.html +1 -1
- package/ui/standalone/public/voice-join.html +48 -2
- package/scheduler/__pycache__/main.cpython-312.pyc +0 -0
- package/ui/standalone/.next/server/chunks/5123.js +0 -11
- package/ui/standalone/.next/server/chunks/7661.js +0 -11
- package/ui/standalone/.next/static/ULHwbueNr3RGevy8FSrTW/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/ULHwbueNr3RGevy8FSrTW/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/Xg-e8h70WpdFcsZ2vcfOG/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/Xg-e8h70WpdFcsZ2vcfOG/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/voice/page-d98593eea4888404.js +0 -1
- package/ui/standalone/.next/static/chunks/app/_not-found/page-9f0acae7481c60bb.js +0 -1
- package/ui/standalone/.next/static/chunks/app/layout-4829e027e68fded4.js +0 -1
- package/ui/standalone/.next/static/chunks/app/voice-tester/page-f5ad3acef9248b78.js +0 -1
- package/ui/standalone/.next/static/chunks/main-app-f2b2a716b0f3924b.js +0 -1
- package/ui/standalone/.next/static/gGysrZrp8HXGK_0MbZa9v/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/gGysrZrp8HXGK_0MbZa9v/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/tDI_bBJ7vZjqqxcXg1GZn/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/tDI_bBJ7vZjqqxcXg1GZn/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/zAWQgn-m46BFpT3z33lh-/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/zAWQgn-m46BFpT3z33lh-/_ssgManifest.js +0 -1
- /package/ui/standalone/.next/static/{KwY1YC79lI9nQqOVYXOmY → 2zW74Q0KZAsncyNMwT9yQ}/_buildManifest.js +0 -0
- /package/ui/standalone/.next/static/{KwY1YC79lI9nQqOVYXOmY → 2zW74Q0KZAsncyNMwT9yQ}/_ssgManifest.js +0 -0
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import chalk from 'chalk';
|
|
25
25
|
import { resolveContext } from './profile.js';
|
|
26
26
|
import { roboparkSetup } from './setup.js';
|
|
27
|
-
function robotReachableSchedulerUrl(schedulerUrl, hubUrl) {
|
|
27
|
+
export function robotReachableSchedulerUrl(schedulerUrl, hubUrl) {
|
|
28
28
|
if (!hubUrl)
|
|
29
29
|
return schedulerUrl;
|
|
30
30
|
try {
|
|
@@ -41,7 +41,7 @@ function robotReachableSchedulerUrl(schedulerUrl, hubUrl) {
|
|
|
41
41
|
}
|
|
42
42
|
return schedulerUrl;
|
|
43
43
|
}
|
|
44
|
-
function schedulerApiUrl(schedulerUrl, path, token) {
|
|
44
|
+
export function schedulerApiUrl(schedulerUrl, path, token) {
|
|
45
45
|
const url = new URL(`${schedulerUrl.replace(/\/+$/, '')}${path}`);
|
|
46
46
|
if (token && url.pathname.startsWith('/robopark/')) {
|
|
47
47
|
url.searchParams.set('token', token);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-robot.js","sourceRoot":"","sources":["../../src/robopark/add-robot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,cAAc,EAA4B,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAc3C,
|
|
1
|
+
{"version":3,"file":"add-robot.js","sourceRoot":"","sources":["../../src/robopark/add-robot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoboPark — explicit kiosk microphone/speaker selection.
|
|
3
|
+
*
|
|
4
|
+
* This adds no new API. The vision server already owns the media selection:
|
|
5
|
+
* `GET /api/media/inventory` lists the devices and `POST /api/media/config`
|
|
6
|
+
* sets `audio_device` / `audio_output_device`, forwarding both to
|
|
7
|
+
* audio_server_pi.py's own `/set-device`. The scheduler already stores the
|
|
8
|
+
* chosen values on the device row (`devices.audio_device`,
|
|
9
|
+
* `devices.audio_output_device`, both patchable via `PATCH /api/devices/<id>`).
|
|
10
|
+
*
|
|
11
|
+
* So `--mic` / `--speaker` only resolve a human-typed name or index against
|
|
12
|
+
* that inventory and drive the two endpoints that already exist; the scheduler
|
|
13
|
+
* device row is the persistence, which is what makes a rerun with no flags
|
|
14
|
+
* reapply the previous choice.
|
|
15
|
+
*/
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
export async function fetchInventory(port) {
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/media/inventory`, {
|
|
20
|
+
signal: AbortSignal.timeout(5_000),
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
return null;
|
|
24
|
+
return (await res.json());
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve `--mic ...` / `--speaker ...` to the id the vision server expects.
|
|
32
|
+
*
|
|
33
|
+
* A bare index is passed straight through: the inventory is the audio server's
|
|
34
|
+
* sounddevice list, and on a machine where that server is not running the list
|
|
35
|
+
* is empty but an index is still the right thing to persist. A name is matched
|
|
36
|
+
* case-insensitively, exact first, then unique substring — an ambiguous
|
|
37
|
+
* substring is an error rather than a coin flip, since picking the wrong
|
|
38
|
+
* microphone is silent and looks like a dead agent.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveAudioDevice(wanted, choices, kind) {
|
|
41
|
+
const value = wanted.trim();
|
|
42
|
+
if (!value)
|
|
43
|
+
return {};
|
|
44
|
+
if (/^\d+$/.test(value)) {
|
|
45
|
+
const known = choices.find(c => String(c.id) === value);
|
|
46
|
+
return known
|
|
47
|
+
? { id: value }
|
|
48
|
+
: { id: value, warning: `${kind} index ${value} is not in the vision server's inventory — applying it anyway` };
|
|
49
|
+
}
|
|
50
|
+
const lower = value.toLowerCase();
|
|
51
|
+
const exact = choices.filter(c => String(c.name).toLowerCase() === lower);
|
|
52
|
+
if (exact.length === 1)
|
|
53
|
+
return { id: String(exact[0].id) };
|
|
54
|
+
const partial = choices.filter(c => String(c.name).toLowerCase().includes(lower));
|
|
55
|
+
if (partial.length === 1)
|
|
56
|
+
return { id: String(partial[0].id) };
|
|
57
|
+
if (partial.length > 1) {
|
|
58
|
+
return { error: `"${value}" matches ${partial.length} ${kind}s: ${partial.map(c => `${c.id}=${c.name}`).join(', ')} — pass the index instead` };
|
|
59
|
+
}
|
|
60
|
+
const listed = choices.length
|
|
61
|
+
? choices.map(c => `${c.id}=${c.name}`).join(', ')
|
|
62
|
+
: '(the vision server reported no audio devices; audio_server_pi.py is not running on this machine)';
|
|
63
|
+
return { error: `no ${kind} matches "${value}". Available: ${listed}` };
|
|
64
|
+
}
|
|
65
|
+
/** Push a selection at the vision server, which forwards it to the audio server. */
|
|
66
|
+
export async function applyAudioSelection(port, selection) {
|
|
67
|
+
const body = {};
|
|
68
|
+
if (selection.audioDevice !== undefined)
|
|
69
|
+
body.audio_device = selection.audioDevice;
|
|
70
|
+
if (selection.audioOutputDevice !== undefined)
|
|
71
|
+
body.audio_output_device = selection.audioOutputDevice;
|
|
72
|
+
if (!Object.keys(body).length)
|
|
73
|
+
return true;
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/media/config`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'content-type': 'application/json' },
|
|
78
|
+
body: JSON.stringify(body),
|
|
79
|
+
signal: AbortSignal.timeout(8_000),
|
|
80
|
+
});
|
|
81
|
+
return res.ok;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Turn the flags (or, with no flags, the values already stored on the device
|
|
89
|
+
* row) into a selection to apply.
|
|
90
|
+
*
|
|
91
|
+
* Throws on an unresolvable name: a kiosk that silently kept the Windows
|
|
92
|
+
* default after being told which microphone to use is the failure this flag
|
|
93
|
+
* exists to remove.
|
|
94
|
+
*/
|
|
95
|
+
export async function planAudioSelection(port, flags, stored) {
|
|
96
|
+
const selection = {};
|
|
97
|
+
if (!flags.mic && !flags.speaker) {
|
|
98
|
+
// No flags: reapply what a previous run persisted, so reruns are stable.
|
|
99
|
+
if (stored.audioDevice)
|
|
100
|
+
selection.audioDevice = String(stored.audioDevice);
|
|
101
|
+
if (stored.audioOutputDevice)
|
|
102
|
+
selection.audioOutputDevice = String(stored.audioOutputDevice);
|
|
103
|
+
return selection;
|
|
104
|
+
}
|
|
105
|
+
const inventory = (await fetchInventory(port)) ?? {};
|
|
106
|
+
if (inventory.audio_server && inventory.audio_server.online === false) {
|
|
107
|
+
console.log(chalk.yellow(` ⚠ audio: the vision server cannot reach audio_server_pi.py (${inventory.audio_server.error ?? 'offline'})`));
|
|
108
|
+
console.log(chalk.dim(' the selection is still recorded and pushed, but nothing will switch devices until that server runs'));
|
|
109
|
+
}
|
|
110
|
+
if (flags.mic) {
|
|
111
|
+
const resolved = resolveAudioDevice(flags.mic, inventory.audio_input ?? [], 'microphone');
|
|
112
|
+
if (resolved.error)
|
|
113
|
+
throw new Error(resolved.error);
|
|
114
|
+
if (resolved.warning)
|
|
115
|
+
console.log(chalk.yellow(` ⚠ ${resolved.warning}`));
|
|
116
|
+
selection.audioDevice = resolved.id;
|
|
117
|
+
}
|
|
118
|
+
if (flags.speaker) {
|
|
119
|
+
const resolved = resolveAudioDevice(flags.speaker, inventory.audio_output ?? [], 'speaker');
|
|
120
|
+
if (resolved.error)
|
|
121
|
+
throw new Error(resolved.error);
|
|
122
|
+
if (resolved.warning)
|
|
123
|
+
console.log(chalk.yellow(` ⚠ ${resolved.warning}`));
|
|
124
|
+
selection.audioOutputDevice = resolved.id;
|
|
125
|
+
}
|
|
126
|
+
return selection;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=audio-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-select.js","sourceRoot":"","sources":["../../src/robopark/audio-select.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAkB1B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,sBAAsB,EAAE;YACtE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;SACnC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,OAA2B,EAC3B,IAA8B;IAE9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;QACxD,OAAO,KAAK;YACV,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE;YACf,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,UAAU,KAAK,+DAA+D,EAAE,CAAC;IACpH,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAClF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,aAAa,OAAO,CAAC,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;IAClJ,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;QAC3B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAClD,CAAC,CAAC,kGAAkG,CAAC;IACvG,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,aAAa,KAAK,iBAAiB,MAAM,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,SAAyB;IAC/E,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,IAAI,SAAS,CAAC,WAAW,KAAK,SAAS;QAAE,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,WAAW,CAAC;IACnF,IAAI,SAAS,CAAC,iBAAiB,KAAK,SAAS;QAAE,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC,iBAAiB,CAAC;IACtG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,mBAAmB,EAAE;YACnE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;SACnC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAY,EACZ,KAAyC,EACzC,MAA0E;IAE1E,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACjC,yEAAyE;QACzE,IAAI,MAAM,CAAC,WAAW;YAAE,SAAS,CAAC,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,iBAAiB;YAAE,SAAS,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAC7F,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,SAAS,CAAC,YAAY,IAAI,SAAS,CAAC,YAAY,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,iEAAiE,SAAS,CAAC,YAAY,CAAC,KAAK,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;QACzI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,wGAAwG,CAAC,CAAC,CAAC;IACnI,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,WAAW,IAAI,EAAE,EAAE,YAAY,CAAC,CAAC;QAC1F,IAAI,QAAQ,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3E,SAAS,CAAC,WAAW,GAAG,QAAQ,CAAC,EAAE,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,YAAY,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;QAC5F,IAAI,QAAQ,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3E,SAAS,CAAC,iBAAiB,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoboPark — camera/microphone device preemption.
|
|
3
|
+
*
|
|
4
|
+
* On a kiosk the vision server is the sole owner of the capture devices. A USB
|
|
5
|
+
* camera on Windows cannot be opened twice, so any leftover holder makes the
|
|
6
|
+
* MJPEG generator yield nothing: Flask answers `/` with 200 while `/video_feed`
|
|
7
|
+
* never produces a first byte. That is the exact shape of the kongor failure
|
|
8
|
+
* this module exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* The selection is an ALLOWLIST, not a heuristic sweep. Only three families are
|
|
11
|
+
* ever targeted, and every one of them is something robopark itself started:
|
|
12
|
+
* 1. the vision/audio python servers, matched on their script filename;
|
|
13
|
+
* 2. a python process listening on the vision port;
|
|
14
|
+
* 3. a kiosk browser running out of a `~/.robopark/kiosk-*` profile directory,
|
|
15
|
+
* which is the dedicated profile `kiosk-browser.ts` creates and nothing
|
|
16
|
+
* else on the machine uses.
|
|
17
|
+
*
|
|
18
|
+
* Everything else is skipped and reported. Node processes are never candidates
|
|
19
|
+
* at all: a false positive here would take down the infinicode mesh node, the
|
|
20
|
+
* scheduler, or the control-center UI, which is far worse than a camera that
|
|
21
|
+
* fails to open.
|
|
22
|
+
*/
|
|
23
|
+
import { spawnSync } from 'node:child_process';
|
|
24
|
+
/** Python scripts that open the camera or the audio devices. */
|
|
25
|
+
const DEVICE_SCRIPTS = [
|
|
26
|
+
'app_pi_clean.py',
|
|
27
|
+
'audio_server_pi.py',
|
|
28
|
+
'vision_motion_trigger.py',
|
|
29
|
+
];
|
|
30
|
+
const PYTHON_IMAGE = /^(python|python3|pythonw)(\d(\.\d+)?)?(\.exe)?$/i;
|
|
31
|
+
const BROWSER_IMAGE = /^(chrome|msedge|brave|chromium|chromium-browser|google-chrome|firefox|firefox-esr)(\.exe)?$/i;
|
|
32
|
+
/**
|
|
33
|
+
* The kiosk profile directory `kiosk-browser.ts` builds
|
|
34
|
+
* (`~/.robopark/kiosk-<robot>-<family>`). Matching the path rather than the
|
|
35
|
+
* browser image is what separates the kiosk window from the operator's own
|
|
36
|
+
* browser, which must never be touched.
|
|
37
|
+
*/
|
|
38
|
+
const KIOSK_PROFILE = /[\\/]\.robopark[\\/]kiosk-/i;
|
|
39
|
+
/**
|
|
40
|
+
* Processes that are never killed, whatever else they look like.
|
|
41
|
+
*
|
|
42
|
+
* These are the operator's tooling. `/T` is likewise never used anywhere in
|
|
43
|
+
* this module: killing a process tree has previously taken the infinicode mesh
|
|
44
|
+
* node down as collateral.
|
|
45
|
+
*/
|
|
46
|
+
const PROTECTED = [
|
|
47
|
+
{ pattern: /infinicode/i, label: 'infinicode mesh node' },
|
|
48
|
+
{ pattern: /robopark[\\/]scheduler|scheduler[\\/]main\.py|uvicorn/i, label: 'robopark scheduler' },
|
|
49
|
+
{ pattern: /\bnext\b|frontend-service|robopark[\\/]ui/i, label: 'control-center UI' },
|
|
50
|
+
{ pattern: /robopark-cli|[\\/]bin[\\/]robopark/i, label: 'robopark CLI' },
|
|
51
|
+
];
|
|
52
|
+
function protectedBy(cmd) {
|
|
53
|
+
return PROTECTED.find(entry => entry.pattern.test(cmd))?.label ?? null;
|
|
54
|
+
}
|
|
55
|
+
/** Every process on this machine, with its full command line. */
|
|
56
|
+
export function listProcesses() {
|
|
57
|
+
if (process.platform === 'win32') {
|
|
58
|
+
const res = spawnSync('powershell', [
|
|
59
|
+
'-NoProfile',
|
|
60
|
+
'-NonInteractive',
|
|
61
|
+
'-Command',
|
|
62
|
+
'Get-CimInstance Win32_Process | Select-Object ProcessId,Name,CommandLine | ConvertTo-Json -Compress',
|
|
63
|
+
], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
64
|
+
if (res.status !== 0 || !res.stdout.trim())
|
|
65
|
+
return [];
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(res.stdout);
|
|
68
|
+
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
|
69
|
+
return rows.map(r => ({ pid: Number(r.ProcessId), name: String(r.Name ?? ''), cmd: String(r.CommandLine ?? '') }));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const res = spawnSync('ps', ['-eo', 'pid=,comm=,args='], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
76
|
+
if (res.status !== 0)
|
|
77
|
+
return [];
|
|
78
|
+
return res.stdout
|
|
79
|
+
.split('\n')
|
|
80
|
+
.map(line => line.trim().match(/^(\d+)\s+(\S+)\s*(.*)$/))
|
|
81
|
+
.filter((m) => Boolean(m))
|
|
82
|
+
.map(m => ({ pid: Number(m[1]), name: m[2].split('/').pop() ?? m[2], cmd: m[3] }));
|
|
83
|
+
}
|
|
84
|
+
/** PIDs listening on `port`, whatever they are. */
|
|
85
|
+
export function listenersOn(port) {
|
|
86
|
+
const pids = new Set();
|
|
87
|
+
if (process.platform === 'win32') {
|
|
88
|
+
const res = spawnSync('netstat', ['-ano', '-p', 'tcp'], { encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 });
|
|
89
|
+
if (res.status !== 0)
|
|
90
|
+
return pids;
|
|
91
|
+
for (const line of res.stdout.split('\n')) {
|
|
92
|
+
if (!/LISTENING/i.test(line))
|
|
93
|
+
continue;
|
|
94
|
+
const parts = line.trim().split(/\s+/);
|
|
95
|
+
const local = parts[1] ?? '';
|
|
96
|
+
if (!local.endsWith(`:${port}`))
|
|
97
|
+
continue;
|
|
98
|
+
const pid = Number(parts[parts.length - 1]);
|
|
99
|
+
if (Number.isInteger(pid) && pid > 0)
|
|
100
|
+
pids.add(pid);
|
|
101
|
+
}
|
|
102
|
+
return pids;
|
|
103
|
+
}
|
|
104
|
+
const res = spawnSync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-t'], { encoding: 'utf8' });
|
|
105
|
+
if (res.status !== 0)
|
|
106
|
+
return pids;
|
|
107
|
+
for (const line of res.stdout.split('\n')) {
|
|
108
|
+
const pid = Number(line.trim());
|
|
109
|
+
if (Number.isInteger(pid) && pid > 0)
|
|
110
|
+
pids.add(pid);
|
|
111
|
+
}
|
|
112
|
+
return pids;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Decide, without killing anything, which processes hold the kiosk's devices.
|
|
116
|
+
*
|
|
117
|
+
* `skipped` only records processes that came close enough to matter — a
|
|
118
|
+
* protected process, or something on the vision port that is not ours. Listing
|
|
119
|
+
* every idle process would bury the two lines an operator needs to read.
|
|
120
|
+
*/
|
|
121
|
+
export function planPreemption(port, rows, listeners) {
|
|
122
|
+
const targets = [];
|
|
123
|
+
const skipped = [];
|
|
124
|
+
const self = new Set([process.pid, process.ppid]);
|
|
125
|
+
for (const row of rows) {
|
|
126
|
+
if (self.has(row.pid))
|
|
127
|
+
continue;
|
|
128
|
+
const script = DEVICE_SCRIPTS.find(s => row.cmd.includes(s));
|
|
129
|
+
const isPython = PYTHON_IMAGE.test(row.name);
|
|
130
|
+
const onPort = listeners.has(row.pid);
|
|
131
|
+
const kioskBrowser = BROWSER_IMAGE.test(row.name) && KIOSK_PROFILE.test(row.cmd);
|
|
132
|
+
let reason = null;
|
|
133
|
+
if (isPython && script)
|
|
134
|
+
reason = `vision/audio server (${script})`;
|
|
135
|
+
else if (isPython && onPort)
|
|
136
|
+
reason = `python listening on the vision port ${port}`;
|
|
137
|
+
else if (kioskBrowser)
|
|
138
|
+
reason = `robopark kiosk browser (~/.robopark/kiosk-* profile)`;
|
|
139
|
+
if (!reason) {
|
|
140
|
+
// Anything else holding the vision port is reported but not killed: it is
|
|
141
|
+
// not a process robopark started, so it is not ours to terminate.
|
|
142
|
+
if (onPort) {
|
|
143
|
+
skipped.push({ pid: row.pid, name: row.name, reason: `holds port ${port} but is not a robopark python server — not killing; stop it yourself if the feed stays dead` });
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const guard = protectedBy(row.cmd);
|
|
148
|
+
if (guard) {
|
|
149
|
+
skipped.push({ pid: row.pid, name: row.name, reason: `looks like a device holder but is protected (${guard}) — skipped` });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
targets.push({ pid: row.pid, name: row.name, reason });
|
|
153
|
+
}
|
|
154
|
+
return { targets, skipped };
|
|
155
|
+
}
|
|
156
|
+
/** Kill one pid. Never `/T`: that would take the process's children with it. */
|
|
157
|
+
function killPid(pid) {
|
|
158
|
+
if (process.platform === 'win32') {
|
|
159
|
+
const res = spawnSync('taskkill', ['/PID', String(pid), '/F'], { encoding: 'utf8' });
|
|
160
|
+
return { ok: res.status === 0, detail: String(res.stderr || res.stdout || '').trim() };
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
process.kill(pid, 'SIGTERM');
|
|
164
|
+
return { ok: true, detail: '' };
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
return { ok: false, detail: err instanceof Error ? err.message : String(err) };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export function preemptDevices(port, opts = {}) {
|
|
171
|
+
const plan = planPreemption(port, listProcesses(), listenersOn(port));
|
|
172
|
+
const killed = [];
|
|
173
|
+
const failed = [];
|
|
174
|
+
if (opts.dryRun)
|
|
175
|
+
return { ...plan, killed, failed };
|
|
176
|
+
for (const target of plan.targets) {
|
|
177
|
+
const res = killPid(target.pid);
|
|
178
|
+
if (res.ok)
|
|
179
|
+
killed.push(target);
|
|
180
|
+
else
|
|
181
|
+
failed.push({ ...target, detail: res.detail || 'taskkill failed' });
|
|
182
|
+
}
|
|
183
|
+
return { ...plan, killed, failed };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=device-preempt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-preempt.js","sourceRoot":"","sources":["../../src/robopark/device-preempt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoboPark — kiosk browser launch.
|
|
3
|
+
*
|
|
4
|
+
* `robopark start <robot>` has to end with the live call page actually open on
|
|
5
|
+
* the kiosk, with the camera and microphone already granted: there is nobody
|
|
6
|
+
* standing at the machine to click "Allow".
|
|
7
|
+
*
|
|
8
|
+
* The POSIX installers (scripts/install-firefox-robot-kiosk.sh,
|
|
9
|
+
* ui/standalone/public/install-robot-kiosk.sh) do this with a root-installed
|
|
10
|
+
* Firefox policies.json that pre-grants Camera/Microphone for one origin and
|
|
11
|
+
* unblocks autoplay. That needs sudo and a writable install directory, neither
|
|
12
|
+
* of which exists on the Windows kiosks, so the same grants are expressed here
|
|
13
|
+
* per-browser-family and per-launch instead:
|
|
14
|
+
* - Chromium family: --use-fake-ui-for-media-stream (auto-accept getUserMedia
|
|
15
|
+
* against the REAL devices; the "fake" is the picker UI, not the camera)
|
|
16
|
+
* plus --autoplay-policy=no-user-gesture-required.
|
|
17
|
+
* - Firefox: a dedicated profile whose user.js sets
|
|
18
|
+
* permissions.default.camera/microphone=1 and the same autoplay prefs the
|
|
19
|
+
* policy file locks.
|
|
20
|
+
*/
|
|
21
|
+
import { spawn } from 'node:child_process';
|
|
22
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
const WINDOWS_CANDIDATES = [
|
|
26
|
+
'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
27
|
+
'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
28
|
+
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
29
|
+
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
30
|
+
'C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe',
|
|
31
|
+
'C:\\Program Files\\Chromium\\Application\\chrome.exe',
|
|
32
|
+
'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
|
|
33
|
+
'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
|
|
34
|
+
];
|
|
35
|
+
const POSIX_CANDIDATES = [
|
|
36
|
+
'/usr/bin/chromium-browser',
|
|
37
|
+
'/usr/bin/chromium',
|
|
38
|
+
'/usr/bin/google-chrome',
|
|
39
|
+
'/usr/bin/microsoft-edge',
|
|
40
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
41
|
+
'/usr/bin/firefox',
|
|
42
|
+
'/usr/bin/firefox-esr',
|
|
43
|
+
'/Applications/Firefox.app/Contents/MacOS/firefox',
|
|
44
|
+
];
|
|
45
|
+
export function browserFamily(exe) {
|
|
46
|
+
return /firefox/i.test(exe) ? 'firefox' : 'chromium';
|
|
47
|
+
}
|
|
48
|
+
/** First installed kiosk-capable browser, or null. Chromium family wins: its
|
|
49
|
+
* permission grants are per-launch flags and need no elevation. */
|
|
50
|
+
export function findBrowser(override) {
|
|
51
|
+
if (override)
|
|
52
|
+
return existsSync(override) ? override : null;
|
|
53
|
+
const fromEnv = process.env.ROBOPARK_BROWSER;
|
|
54
|
+
if (fromEnv && existsSync(fromEnv))
|
|
55
|
+
return fromEnv;
|
|
56
|
+
const candidates = process.platform === 'win32' ? WINDOWS_CANDIDATES : POSIX_CANDIDATES;
|
|
57
|
+
return candidates.find(existsSync) ?? null;
|
|
58
|
+
}
|
|
59
|
+
function originOf(url) {
|
|
60
|
+
try {
|
|
61
|
+
return new URL(url).origin;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* getUserMedia is unavailable on a plain-http origin that is not loopback, so a
|
|
69
|
+
* hub reached as http://HUB:8080 can never grant the camera no matter how the
|
|
70
|
+
* permission is pre-set. Chromium's origin allowlist is the only way to make
|
|
71
|
+
* that origin a secure context without a certificate.
|
|
72
|
+
*/
|
|
73
|
+
function needsInsecureOriginOverride(url) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = new URL(url);
|
|
76
|
+
if (parsed.protocol !== 'http:')
|
|
77
|
+
return false;
|
|
78
|
+
return !['localhost', '127.0.0.1', '::1', ''].includes(parsed.hostname);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function firefoxProfile(dir, url) {
|
|
85
|
+
mkdirSync(dir, { recursive: true });
|
|
86
|
+
const prefs = [
|
|
87
|
+
['permissions.default.camera', '1'],
|
|
88
|
+
['permissions.default.microphone', '1'],
|
|
89
|
+
['media.autoplay.default', '0'],
|
|
90
|
+
['media.autoplay.blocking_policy', '0'],
|
|
91
|
+
['media.navigator.permission.disabled', 'true'],
|
|
92
|
+
// Same reason as the Chromium allowlist above: without these a plain-http
|
|
93
|
+
// hub origin has no navigator.mediaDevices at all.
|
|
94
|
+
['media.devices.insecure.enabled', needsInsecureOriginOverride(url) ? 'true' : 'false'],
|
|
95
|
+
['media.getusermedia.insecure.enabled', needsInsecureOriginOverride(url) ? 'true' : 'false'],
|
|
96
|
+
['browser.shell.checkDefaultBrowser', 'false'],
|
|
97
|
+
['datareporting.policy.firstRunURL', '""'],
|
|
98
|
+
];
|
|
99
|
+
writeFileSync(join(dir, 'user.js'), prefs.map(([k, v]) => `user_pref("${k}", ${v});`).join('\n') + '\n', 'utf8');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The exact executable + argv for a kiosk call page, without spawning.
|
|
103
|
+
*
|
|
104
|
+
* Split out so the command line can be printed and reviewed; `launchKiosk`
|
|
105
|
+
* only adds the spawn.
|
|
106
|
+
*/
|
|
107
|
+
export function kioskLaunch(exe, url, robot) {
|
|
108
|
+
const family = browserFamily(exe);
|
|
109
|
+
const profileDir = join(homedir(), '.robopark', `kiosk-${robot.replace(/[^a-zA-Z0-9_-]/g, '_')}-${family}`);
|
|
110
|
+
if (family === 'firefox') {
|
|
111
|
+
return { exe, args: ['-profile', profileDir, '--kiosk', url], family, profileDir };
|
|
112
|
+
}
|
|
113
|
+
const args = [
|
|
114
|
+
// A dedicated profile is load-bearing, not hygiene: launching Chromium
|
|
115
|
+
// while the operator's normal browser is running otherwise just hands the
|
|
116
|
+
// URL to that process and every permission flag below is silently dropped.
|
|
117
|
+
`--user-data-dir=${profileDir}`,
|
|
118
|
+
'--use-fake-ui-for-media-stream',
|
|
119
|
+
'--autoplay-policy=no-user-gesture-required',
|
|
120
|
+
'--no-first-run',
|
|
121
|
+
'--no-default-browser-check',
|
|
122
|
+
'--disable-session-crashed-bubble',
|
|
123
|
+
'--kiosk',
|
|
124
|
+
];
|
|
125
|
+
if (needsInsecureOriginOverride(url)) {
|
|
126
|
+
args.push(`--unsafely-treat-insecure-origin-as-secure=${originOf(url)}`);
|
|
127
|
+
}
|
|
128
|
+
args.push(url);
|
|
129
|
+
return { exe, args, family, profileDir };
|
|
130
|
+
}
|
|
131
|
+
/** Shell-ish rendering of a launch, for logging. */
|
|
132
|
+
export function launchCommandLine(launch) {
|
|
133
|
+
return [launch.exe, ...launch.args]
|
|
134
|
+
.map(a => (/[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a))
|
|
135
|
+
.join(' ');
|
|
136
|
+
}
|
|
137
|
+
export function launchKiosk(launch, url) {
|
|
138
|
+
if (launch.family === 'firefox')
|
|
139
|
+
firefoxProfile(launch.profileDir, url);
|
|
140
|
+
else
|
|
141
|
+
mkdirSync(launch.profileDir, { recursive: true });
|
|
142
|
+
const proc = spawn(launch.exe, launch.args, { stdio: 'ignore', detached: true });
|
|
143
|
+
proc.unref();
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=kiosk-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kiosk-browser.js","sourceRoot":"","sources":["../../src/robopark/kiosk-browser.ts"],"names":[],"mappings":"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"}
|