arise-browser 0.1.3 → 0.2.1
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 +4 -5
- package/deploy/neko/CONTEXT.md +24 -23
- package/deploy/neko/Dockerfile +19 -0
- package/deploy/neko/arise-browser.service +4 -3
- package/deploy/neko/setup.sh +34 -243
- package/deploy/neko/supervisord.conf +43 -0
- package/dist/bin/arise-browser.js +17 -3
- package/dist/bin/arise-browser.js.map +1 -1
- package/dist/src/server/routes/health.js +1 -1
- package/dist/src/types/index.d.ts +4 -4
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/virtual-display/manager.d.ts +11 -16
- package/dist/src/virtual-display/manager.d.ts.map +1 -1
- package/dist/src/virtual-display/manager.js +119 -168
- package/dist/src/virtual-display/manager.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/skill/arise-browser/SKILL.md +58 -118
- package/skill/arise-browser/references/api.md +1 -1
- package/deploy/neko/neko.yaml +0 -12
- package/deploy/neko/openbox.xml +0 -763
- package/deploy/neko/pulseaudio.pa +0 -16
- package/deploy/neko/xorg.conf +0 -118
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* VirtualDisplayManager — manages
|
|
3
|
-
*
|
|
2
|
+
* VirtualDisplayManager — manages a Docker container running Neko
|
|
3
|
+
* (Xvfb + PulseAudio + Openbox + Chrome + Neko WebRTC) for cloud server deployment.
|
|
4
4
|
*
|
|
5
|
-
* arise-browser
|
|
5
|
+
* arise-browser controls the container via `docker` CLI and connects to Chrome via CDP.
|
|
6
6
|
*/
|
|
7
7
|
export interface VirtualDisplayConfig {
|
|
8
|
-
/**
|
|
9
|
-
display: string;
|
|
10
|
-
/** Screen resolution+depth (default "1920x1080x24") */
|
|
11
|
-
screen: string;
|
|
12
|
-
/** Neko HTTP/WS port (default 6090) */
|
|
8
|
+
/** Neko HTTP/WS port on host (default 6090) */
|
|
13
9
|
nekoPort: number;
|
|
14
10
|
/** Neko user password (default "neko") */
|
|
15
11
|
nekoPassword: string;
|
|
16
12
|
/** Neko admin password (default "admin") */
|
|
17
13
|
nekoAdminPassword: string;
|
|
18
|
-
/** Chrome
|
|
14
|
+
/** Chrome CDP port on host (default 9222) */
|
|
19
15
|
chromeDebugPort: number;
|
|
20
|
-
/**
|
|
21
|
-
|
|
16
|
+
/** Screen resolution (default "1920x1080@30") */
|
|
17
|
+
screen: string;
|
|
18
|
+
/** Docker container name (default "arise-neko") */
|
|
19
|
+
containerName: string;
|
|
20
|
+
/** Docker image name (default "arise-neko") */
|
|
21
|
+
imageName: string;
|
|
22
22
|
}
|
|
23
23
|
export declare class VirtualDisplayManager {
|
|
24
|
-
private _xvfb;
|
|
25
|
-
private _pulseaudio;
|
|
26
|
-
private _openbox;
|
|
27
|
-
private _chrome;
|
|
28
|
-
private _neko;
|
|
29
24
|
private _started;
|
|
30
25
|
private readonly _config;
|
|
31
26
|
constructor(config?: Partial<VirtualDisplayConfig>);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/virtual-display/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,MAAM,WAAW,oBAAoB;IACnC
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/virtual-display/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAiDD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAEnC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC;IAIlD,IAAI,MAAM,IAAI,oBAAoB,CAEjC;IAED,SAAS,IAAI,OAAO;IAId,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkGtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB5B"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* VirtualDisplayManager — manages
|
|
3
|
-
*
|
|
2
|
+
* VirtualDisplayManager — manages a Docker container running Neko
|
|
3
|
+
* (Xvfb + PulseAudio + Openbox + Chrome + Neko WebRTC) for cloud server deployment.
|
|
4
4
|
*
|
|
5
|
-
* arise-browser
|
|
5
|
+
* arise-browser controls the container via `docker` CLI and connects to Chrome via CDP.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
8
|
import { resolve, dirname, join } from "node:path";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { createLogger } from "../logger.js";
|
|
11
|
-
import { ProcessRunner } from "./process-runner.js";
|
|
12
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const log = createLogger("virtual-display");
|
|
13
14
|
/**
|
|
14
15
|
* Find the package root by walking up from __dirname to find package.json.
|
|
15
|
-
* Works both in src/ (dev) and dist/ (compiled).
|
|
16
16
|
*/
|
|
17
17
|
function findPackageRoot() {
|
|
18
18
|
let dir = __dirname;
|
|
@@ -21,49 +21,52 @@ function findPackageRoot() {
|
|
|
21
21
|
return dir;
|
|
22
22
|
dir = dirname(dir);
|
|
23
23
|
}
|
|
24
|
-
// Fallback: assume 2 levels up from src/virtual-display or dist/src/virtual-display
|
|
25
24
|
return resolve(__dirname, "../..");
|
|
26
25
|
}
|
|
27
26
|
const DEFAULT_CONFIG = {
|
|
28
|
-
display: ":99",
|
|
29
|
-
screen: "1920x1080x24",
|
|
30
27
|
nekoPort: 6090,
|
|
31
28
|
nekoPassword: "neko",
|
|
32
29
|
nekoAdminPassword: "admin",
|
|
33
30
|
chromeDebugPort: 9222,
|
|
34
|
-
|
|
31
|
+
screen: "1920x1080@30",
|
|
32
|
+
containerName: "arise-neko",
|
|
33
|
+
imageName: "arise-neko",
|
|
35
34
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
/** Run a command and return stdout. Rejects on non-zero exit. */
|
|
36
|
+
function exec(cmd, args, timeoutMs = 30_000) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
execFile(cmd, args, { timeout: timeoutMs }, (err, stdout, stderr) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
const msg = stderr?.trim() || stdout?.trim() || err.message;
|
|
41
|
+
reject(new Error(`${cmd} ${args[0]}: ${msg}`));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
resolve(stdout.trim());
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
50
|
-
function
|
|
51
|
-
|
|
49
|
+
/** Poll a URL until it returns 2xx or timeout. */
|
|
50
|
+
async function waitForHttp(url, timeoutMs, intervalMs = 500) {
|
|
51
|
+
const deadline = Date.now() + timeoutMs;
|
|
52
|
+
while (Date.now() < deadline) {
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch(url);
|
|
55
|
+
if (res.ok)
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// not ready yet
|
|
60
|
+
}
|
|
61
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Timed out waiting for ${url} (${timeoutMs}ms)`);
|
|
52
64
|
}
|
|
53
|
-
const log = createLogger("virtual-display");
|
|
54
65
|
export class VirtualDisplayManager {
|
|
55
|
-
_xvfb = null;
|
|
56
|
-
_pulseaudio = null;
|
|
57
|
-
_openbox = null;
|
|
58
|
-
_chrome = null;
|
|
59
|
-
_neko = null;
|
|
60
66
|
_started = false;
|
|
61
67
|
_config;
|
|
62
68
|
constructor(config) {
|
|
63
69
|
this._config = { ...DEFAULT_CONFIG, ...config };
|
|
64
|
-
if (!this._config.chromePath) {
|
|
65
|
-
this._config.chromePath = findChrome();
|
|
66
|
-
}
|
|
67
70
|
}
|
|
68
71
|
get config() {
|
|
69
72
|
return { ...this._config };
|
|
@@ -74,152 +77,100 @@ export class VirtualDisplayManager {
|
|
|
74
77
|
async start() {
|
|
75
78
|
if (this._started)
|
|
76
79
|
return;
|
|
77
|
-
const {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const { nekoPort, nekoPassword, nekoAdminPassword, chromeDebugPort, screen, containerName, imageName, } = this._config;
|
|
81
|
+
log.info(`Starting Docker container "${containerName}" (neko=:${nekoPort}, cdp=:${chromeDebugPort})`);
|
|
82
|
+
// Ensure no stale container exists
|
|
83
|
+
try {
|
|
84
|
+
await exec("docker", ["rm", "-f", containerName]);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// container didn't exist, fine
|
|
88
|
+
}
|
|
89
|
+
// Build image if not present
|
|
90
|
+
const deployDir = resolve(findPackageRoot(), "deploy/neko");
|
|
82
91
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
args: [
|
|
135
|
-
`--remote-debugging-port=${chromeDebugPort}`,
|
|
136
|
-
`--user-data-dir=${join(process.env.HOME || "/root", ".arise-browser", "chrome-profile")}`,
|
|
137
|
-
`--display=${display}`,
|
|
138
|
-
"--window-position=0,0",
|
|
139
|
-
"--no-first-run",
|
|
140
|
-
"--start-maximized",
|
|
141
|
-
"--bwsi",
|
|
142
|
-
"--force-dark-mode",
|
|
143
|
-
"--disable-file-system",
|
|
144
|
-
"--disable-gpu",
|
|
145
|
-
"--disable-software-rasterizer",
|
|
146
|
-
"--disable-dev-shm-usage",
|
|
147
|
-
"--no-sandbox",
|
|
148
|
-
"--disable-setuid-sandbox",
|
|
149
|
-
],
|
|
150
|
-
env: {
|
|
151
|
-
...displayEnv,
|
|
152
|
-
PULSE_SERVER: "unix:/tmp/pulseaudio.socket",
|
|
153
|
-
},
|
|
154
|
-
readiness: {
|
|
155
|
-
type: "http",
|
|
156
|
-
target: `http://localhost:${chromeDebugPort}/json/version`,
|
|
157
|
-
timeoutMs: 15_000,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
await this._chrome.start();
|
|
161
|
-
// 5. Neko Server
|
|
162
|
-
this._neko = new ProcessRunner({
|
|
163
|
-
name: "neko",
|
|
164
|
-
command: "/usr/local/bin/neko",
|
|
165
|
-
args: ["serve", "--bind", `0.0.0.0:${nekoPort}`],
|
|
166
|
-
env: {
|
|
167
|
-
...displayEnv,
|
|
168
|
-
PULSE_SERVER: "unix:/tmp/pulseaudio.socket",
|
|
169
|
-
// Convert Xvfb format "1920x1080x24" → Neko format "1920x1080@30"
|
|
170
|
-
NEKO_DESKTOP_SCREEN: screen.replace(/x\d+$/, "@30"),
|
|
171
|
-
NEKO_MEMBER_PROVIDER: "multiuser",
|
|
172
|
-
NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: nekoAdminPassword,
|
|
173
|
-
NEKO_MEMBER_MULTIUSER_USER_PASSWORD: nekoPassword,
|
|
174
|
-
NEKO_SESSION_IMPLICIT_HOSTING: "true",
|
|
175
|
-
NEKO_SESSION_MERCIFUL_RECONNECT: "true",
|
|
176
|
-
},
|
|
177
|
-
readiness: {
|
|
178
|
-
type: "http",
|
|
179
|
-
target: `http://localhost:${nekoPort}/health`,
|
|
180
|
-
timeoutMs: 15_000,
|
|
181
|
-
intervalMs: 500,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
await this._neko.start();
|
|
92
|
+
await exec("docker", ["image", "inspect", imageName]);
|
|
93
|
+
log.info(`Docker image "${imageName}" found`);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
log.info(`Building Docker image "${imageName}" from ${deployDir}...`);
|
|
97
|
+
await exec("docker", ["build", "-t", imageName, deployDir], 120_000);
|
|
98
|
+
log.info(`Docker image "${imageName}" built`);
|
|
99
|
+
}
|
|
100
|
+
// docker run
|
|
101
|
+
const runArgs = [
|
|
102
|
+
"run",
|
|
103
|
+
"-d",
|
|
104
|
+
"--name",
|
|
105
|
+
containerName,
|
|
106
|
+
"--shm-size=2gb",
|
|
107
|
+
// Port mappings
|
|
108
|
+
"-p",
|
|
109
|
+
`${nekoPort}:8080`,
|
|
110
|
+
"-p",
|
|
111
|
+
`127.0.0.1:${chromeDebugPort}:9223`,
|
|
112
|
+
"-p",
|
|
113
|
+
"52000-52100:52000-52100/udp",
|
|
114
|
+
// Persistent Chrome profile via named volume
|
|
115
|
+
"-v",
|
|
116
|
+
`${containerName}-profile:/home/neko/.config/arise-chrome`,
|
|
117
|
+
// Neko environment
|
|
118
|
+
"-e",
|
|
119
|
+
`NEKO_DESKTOP_SCREEN=${screen}`,
|
|
120
|
+
"-e",
|
|
121
|
+
`NEKO_MEMBER_MULTIUSER_USER_PASSWORD=${nekoPassword}`,
|
|
122
|
+
"-e",
|
|
123
|
+
`NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD=${nekoAdminPassword}`,
|
|
124
|
+
"-e",
|
|
125
|
+
"NEKO_WEBRTC_EPR=52000-52100",
|
|
126
|
+
"-e",
|
|
127
|
+
"NEKO_WEBRTC_ICELITE=1",
|
|
128
|
+
"-e",
|
|
129
|
+
"NEKO_SESSION_IMPLICIT_HOSTING=true",
|
|
130
|
+
imageName,
|
|
131
|
+
];
|
|
132
|
+
try {
|
|
133
|
+
const containerId = await exec("docker", runArgs, 30_000);
|
|
134
|
+
log.info(`Container started: ${containerId.slice(0, 12)}`);
|
|
135
|
+
// Wait for Neko health
|
|
136
|
+
log.info("Waiting for Neko health...");
|
|
137
|
+
await waitForHttp(`http://localhost:${nekoPort}/health`, 30_000);
|
|
138
|
+
log.info("Neko is healthy");
|
|
139
|
+
// Wait for Chrome CDP
|
|
140
|
+
log.info("Waiting for Chrome CDP...");
|
|
141
|
+
await waitForHttp(`http://localhost:${chromeDebugPort}/json/version`, 30_000);
|
|
142
|
+
log.info("Chrome CDP is ready");
|
|
185
143
|
this._started = true;
|
|
186
144
|
log.info("Virtual display environment started successfully");
|
|
187
145
|
}
|
|
188
146
|
catch (err) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
147
|
+
log.error({ err }, "Failed to start virtual display, cleaning up");
|
|
148
|
+
try {
|
|
149
|
+
await exec("docker", ["rm", "-f", containerName]);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// best effort
|
|
153
|
+
}
|
|
193
154
|
throw err;
|
|
194
155
|
}
|
|
195
156
|
}
|
|
196
157
|
async stop() {
|
|
197
158
|
if (!this._started)
|
|
198
159
|
return;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
214
|
-
log.error({ err }, "Error stopping process");
|
|
215
|
-
}
|
|
216
|
-
}
|
|
160
|
+
const { containerName } = this._config;
|
|
161
|
+
log.info(`Stopping container "${containerName}"...`);
|
|
162
|
+
try {
|
|
163
|
+
await exec("docker", ["stop", "-t", "10", containerName], 20_000);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
log.error({ err }, "Error stopping container");
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
await exec("docker", ["rm", "-f", containerName]);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
log.error({ err }, "Error removing container");
|
|
217
173
|
}
|
|
218
|
-
this._neko = null;
|
|
219
|
-
this._chrome = null;
|
|
220
|
-
this._openbox = null;
|
|
221
|
-
this._pulseaudio = null;
|
|
222
|
-
this._xvfb = null;
|
|
223
174
|
this._started = false;
|
|
224
175
|
log.info("Virtual display environment stopped");
|
|
225
176
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/virtual-display/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/virtual-display/manager.ts"],"names":[],"mappings":"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"}
|