neoagent 2.3.1-beta.84 → 2.3.1-beta.85
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/package.json +1 -1
- package/runtime/paths.js +6 -6
- package/server/guest-agent.package.json +2 -3
- package/server/guest_agent.js +0 -7
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +4 -4
- package/server/services/android/controller.js +2 -2
- package/server/services/browser/controller.js +207 -45
- package/server/services/runtime/backends/local-vm.js +46 -30
- package/server/services/runtime/guest_bootstrap.js +109 -103
- package/server/services/runtime/qemu.js +343 -77
- package/server/services/runtime/validation.js +1 -27
|
@@ -5,6 +5,16 @@ const { DATA_DIR } = require('../../../runtime/paths');
|
|
|
5
5
|
|
|
6
6
|
const VM_ROOT = path.join(DATA_DIR, 'runtime-vms');
|
|
7
7
|
const GUEST_BOOTSTRAP_ROOT = path.join(VM_ROOT, 'guest-bootstrap');
|
|
8
|
+
const REPO_ROOT = path.resolve(__dirname, '../../..');
|
|
9
|
+
const GUEST_PAYLOAD_ENTRIES = Object.freeze([
|
|
10
|
+
{ source: 'server/guest-agent.package.json', target: 'package.json' },
|
|
11
|
+
{ source: 'runtime/env.js', target: 'runtime/env.js' },
|
|
12
|
+
{ source: 'runtime/paths.js', target: 'runtime/paths.js' },
|
|
13
|
+
{ source: 'server/guest_agent.js', target: 'server/guest_agent.js' },
|
|
14
|
+
{ source: 'server/services/cli', target: 'server/services/cli' },
|
|
15
|
+
{ source: 'server/services/browser', target: 'server/services/browser' },
|
|
16
|
+
{ source: 'server/services/android', target: 'server/services/android' },
|
|
17
|
+
]);
|
|
8
18
|
|
|
9
19
|
fs.mkdirSync(GUEST_BOOTSTRAP_ROOT, { recursive: true });
|
|
10
20
|
|
|
@@ -12,16 +22,50 @@ function encodeGuestToken(value) {
|
|
|
12
22
|
return Buffer.from(String(value || ''), 'utf8').toString('base64');
|
|
13
23
|
}
|
|
14
24
|
|
|
25
|
+
function createGuestPayloadArchive(seedDir) {
|
|
26
|
+
const seedRoot = path.dirname(seedDir);
|
|
27
|
+
const stagingRoot = path.join(seedRoot, 'guest-payload');
|
|
28
|
+
const archivePath = path.join(seedRoot, 'guest-payload.tar.gz');
|
|
29
|
+
fs.rmSync(stagingRoot, { recursive: true, force: true });
|
|
30
|
+
fs.rmSync(archivePath, { force: true });
|
|
31
|
+
fs.mkdirSync(stagingRoot, { recursive: true });
|
|
32
|
+
|
|
33
|
+
for (const entry of GUEST_PAYLOAD_ENTRIES) {
|
|
34
|
+
const sourcePath = path.join(REPO_ROOT, entry.source);
|
|
35
|
+
const targetPath = path.join(stagingRoot, entry.target);
|
|
36
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
37
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
38
|
+
fs.cpSync(sourcePath, targetPath, { recursive: true, force: true });
|
|
39
|
+
} else {
|
|
40
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const tarResult = spawnSync('tar', ['-czf', archivePath, '-C', stagingRoot, '.'], {
|
|
45
|
+
encoding: 'utf8',
|
|
46
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
47
|
+
});
|
|
48
|
+
if (tarResult.status !== 0 || !fs.existsSync(archivePath)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
String(tarResult.stderr || tarResult.stdout || tarResult.error?.message || 'Failed to create guest payload archive.')
|
|
51
|
+
.trim(),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
fs.rmSync(stagingRoot, { recursive: true, force: true });
|
|
55
|
+
return archivePath;
|
|
56
|
+
}
|
|
57
|
+
|
|
15
58
|
function createCloudInitScript({
|
|
16
59
|
guestToken,
|
|
17
|
-
|
|
18
|
-
hostDataMount = '/mnt/neoagent-data',
|
|
60
|
+
guestPayloadPath = '/var/lib/neoagent/guest-payload.tar.gz',
|
|
19
61
|
guestAgentPort = 8421,
|
|
20
62
|
}) {
|
|
21
63
|
const guestTokenB64 = encodeGuestToken(guestToken);
|
|
22
64
|
const envFile = '/etc/neoagent/neoagent.env';
|
|
23
65
|
const appDir = '/opt/neoagent';
|
|
24
66
|
const bootstrapMarker = '/var/lib/neoagent/bootstrap-complete';
|
|
67
|
+
const browserReadyMarker = '/var/lib/neoagent/browser-runtime-ready';
|
|
68
|
+
const browserDepsMarker = '/var/lib/neoagent/browser-deps-installed';
|
|
25
69
|
const nodeSourceSetupUrl = 'https://deb.nodesource.com/setup_20.x';
|
|
26
70
|
|
|
27
71
|
return [
|
|
@@ -29,53 +73,20 @@ function createCloudInitScript({
|
|
|
29
73
|
'set -uo pipefail', // Removed -e to handle non-critical failures gracefully
|
|
30
74
|
'',
|
|
31
75
|
'export DEBIAN_FRONTEND=noninteractive',
|
|
32
|
-
`HOST_SHARE_MOUNT=${JSON.stringify(hostShareMount)}`,
|
|
33
|
-
`HOST_DATA_MOUNT=${JSON.stringify(hostDataMount)}`,
|
|
34
|
-
'HOST_SHARE_TAG=neoagent-host',
|
|
35
|
-
'HOST_SHARE_TAG_FALLBACK=neoagent-host-pci',
|
|
36
|
-
'HOST_DATA_TAG=neoagent-data',
|
|
37
|
-
'HOST_DATA_TAG_FALLBACK=neoagent-data-pci',
|
|
38
76
|
`APP_DIR=${JSON.stringify(appDir)}`,
|
|
39
77
|
`BOOTSTRAP_MARKER=${JSON.stringify(bootstrapMarker)}`,
|
|
78
|
+
`BROWSER_READY_MARKER=${JSON.stringify(browserReadyMarker)}`,
|
|
79
|
+
`BROWSER_DEPS_MARKER=${JSON.stringify(browserDepsMarker)}`,
|
|
40
80
|
`ENV_FILE=${JSON.stringify(envFile)}`,
|
|
81
|
+
`GUEST_PAYLOAD_PATH=${JSON.stringify(guestPayloadPath)}`,
|
|
41
82
|
'',
|
|
42
|
-
'mkdir -p /etc/neoagent /var/lib/neoagent "$
|
|
83
|
+
'mkdir -p /etc/neoagent /var/lib/neoagent "$APP_DIR"',
|
|
43
84
|
'',
|
|
44
|
-
'#
|
|
45
|
-
'
|
|
46
|
-
'modprobe 9pnet_virtio 2>/dev/null || true',
|
|
47
|
-
'',
|
|
48
|
-
'function mount_9p_tag() {',
|
|
49
|
-
' local tag="$1"',
|
|
50
|
-
' local target="$2"',
|
|
51
|
-
' local mode="$3"',
|
|
52
|
-
' mount -t 9p -o "trans=virtio,version=9p2000.L,msize=131072,${mode}" "$tag" "$target" >/dev/null 2>&1',
|
|
53
|
-
'}',
|
|
54
|
-
'',
|
|
55
|
-
'if ! mount_9p_tag "$HOST_SHARE_TAG" "$HOST_SHARE_MOUNT" ro; then',
|
|
56
|
-
' if mount_9p_tag "$HOST_SHARE_TAG_FALLBACK" "$HOST_SHARE_MOUNT" ro; then',
|
|
57
|
-
' HOST_SHARE_TAG="$HOST_SHARE_TAG_FALLBACK"',
|
|
58
|
-
' fi',
|
|
59
|
-
'fi',
|
|
60
|
-
'if ! mount_9p_tag "$HOST_DATA_TAG" "$HOST_DATA_MOUNT" rw; then',
|
|
61
|
-
' if mount_9p_tag "$HOST_DATA_TAG_FALLBACK" "$HOST_DATA_MOUNT" rw; then',
|
|
62
|
-
' HOST_DATA_TAG="$HOST_DATA_TAG_FALLBACK"',
|
|
63
|
-
' fi',
|
|
64
|
-
'fi',
|
|
65
|
-
'',
|
|
66
|
-
'if ! grep -qs "${HOST_SHARE_MOUNT}" /etc/fstab; then',
|
|
67
|
-
' echo "${HOST_SHARE_TAG} ${HOST_SHARE_MOUNT} 9p trans=virtio,version=9p2000.L,msize=262144,ro 0 0" >> /etc/fstab',
|
|
68
|
-
'fi',
|
|
69
|
-
'if ! grep -qs "${HOST_DATA_MOUNT}" /etc/fstab; then',
|
|
70
|
-
' echo "${HOST_DATA_TAG} ${HOST_DATA_MOUNT} 9p trans=virtio,version=9p2000.L,msize=262144,rw 0 0" >> /etc/fstab',
|
|
71
|
-
'fi',
|
|
72
|
-
'',
|
|
73
|
-
'mount -a >/dev/null 2>&1 || true',
|
|
74
|
-
'',
|
|
75
|
-
'# Redirect logs to both host-writable share and console',
|
|
76
|
-
'LOG_FILE="${HOST_DATA_MOUNT}/bootstrap.log"',
|
|
85
|
+
'# Redirect logs to a guest-local file and console',
|
|
86
|
+
'LOG_FILE="/var/log/neoagent-bootstrap.log"',
|
|
77
87
|
'exec > >(tee -a "$LOG_FILE" >/dev/console) 2>&1',
|
|
78
88
|
'echo "NeoAgent guest bootstrap starting..."',
|
|
89
|
+
'rm -f "$BOOTSTRAP_MARKER" "$BROWSER_READY_MARKER"',
|
|
79
90
|
'',
|
|
80
91
|
'function retry_cmd() {',
|
|
81
92
|
' local n=1',
|
|
@@ -95,49 +106,14 @@ function createCloudInitScript({
|
|
|
95
106
|
' done',
|
|
96
107
|
'}',
|
|
97
108
|
'',
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'',
|
|
101
|
-
'echo "Installing dependencies..."',
|
|
102
|
-
'retry_cmd apt-get install -y --no-install-recommends \\',
|
|
103
|
-
' curl ca-certificates gnupg openjdk-17-jre-headless git rsync build-essential \\',
|
|
104
|
-
' python3 unzip libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0 libcups2 \\',
|
|
105
|
-
' libx11-xcb1 libgtk-3-0 libnss3 libnspr4 libxcomposite1 libxdamage1 \\',
|
|
106
|
-
' libxrandr2 libxkbcommon0 libasound2t64 libgbm1 libdrm2 libdbus-1-3 \\',
|
|
107
|
-
' libpango-1.0-0 libpangocairo-1.0-0 libxshmfence1 || echo "Warning: Some dependencies failed to install."',
|
|
108
|
-
'',
|
|
109
|
-
'if [ -d "$HOST_SHARE_MOUNT" ]; then',
|
|
110
|
-
' echo "Syncing guest agent sources..."',
|
|
111
|
-
' SYNC_PATHS=(',
|
|
112
|
-
' server/guest-agent.package.json:package.json',
|
|
113
|
-
' runtime/env.js',
|
|
114
|
-
' runtime/paths.js',
|
|
115
|
-
' server/guest_agent.js',
|
|
116
|
-
' server/services/cli',
|
|
117
|
-
' server/services/browser',
|
|
118
|
-
' server/services/android',
|
|
119
|
-
' )',
|
|
120
|
-
' for relPath in "${SYNC_PATHS[@]}"; do',
|
|
121
|
-
' sourceRelPath="${relPath%%:*}"',
|
|
122
|
-
' targetRelPath="${relPath##*:}"',
|
|
123
|
-
' sourcePath="$HOST_SHARE_MOUNT/$sourceRelPath"',
|
|
124
|
-
' targetPath="$APP_DIR/$targetRelPath"',
|
|
125
|
-
' if [ -e "$sourcePath" ]; then',
|
|
126
|
-
' mkdir -p "$(dirname "$targetPath")"',
|
|
127
|
-
' if [ -d "$sourcePath" ]; then',
|
|
128
|
-
' mkdir -p "$targetPath"',
|
|
129
|
-
' rsync -a --delete "$sourcePath"/ "$targetPath"/',
|
|
130
|
-
' else',
|
|
131
|
-
' rsync -a "$sourcePath" "$targetPath"',
|
|
132
|
-
' fi',
|
|
133
|
-
' else',
|
|
134
|
-
' echo "Warning: Optional source path missing: $relPath"',
|
|
135
|
-
' fi',
|
|
136
|
-
' done',
|
|
137
|
-
'else',
|
|
138
|
-
' echo "Error: Host repo share is not available. Bootstrap cannot continue." >&2',
|
|
109
|
+
'if [ ! -f "$GUEST_PAYLOAD_PATH" ]; then',
|
|
110
|
+
' echo "Error: Guest payload archive is missing at $GUEST_PAYLOAD_PATH." >&2',
|
|
139
111
|
' exit 1',
|
|
140
112
|
'fi',
|
|
113
|
+
'echo "Extracting guest runtime payload..."',
|
|
114
|
+
'rm -rf "$APP_DIR"',
|
|
115
|
+
'mkdir -p "$APP_DIR"',
|
|
116
|
+
'tar -xzf "$GUEST_PAYLOAD_PATH" -C "$APP_DIR" || { echo "Error: Failed to extract guest runtime payload." >&2; exit 1; }',
|
|
141
117
|
'',
|
|
142
118
|
'if ! command -v node >/dev/null 2>&1 || ! node -e "process.exit(Number(process.versions.node.split(\'.\')[0]) >= 20 ? 0 : 1)"; then',
|
|
143
119
|
' echo "Installing Node.js..."',
|
|
@@ -150,28 +126,50 @@ function createCloudInitScript({
|
|
|
150
126
|
'chmod 0600 "$ENV_FILE"',
|
|
151
127
|
'',
|
|
152
128
|
'cd "$APP_DIR"',
|
|
129
|
+
'export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1',
|
|
153
130
|
'if [ ! -d node_modules ] || [ ! -f node_modules/.neoagent-bootstrap-stamp ] || [ package.json -nt node_modules/.neoagent-bootstrap-stamp ]; then',
|
|
154
131
|
' echo "Installing npm dependencies..."',
|
|
155
|
-
'
|
|
156
|
-
' retry_cmd npm install --omit=dev --no-audit --no-fund || echo "
|
|
132
|
+
' retry_cmd npm cache clean --force',
|
|
133
|
+
' retry_cmd npm install --omit=dev --no-audit --no-fund || { echo "Error: npm install failed." >&2; exit 1; }',
|
|
157
134
|
' mkdir -p node_modules',
|
|
158
135
|
' date > node_modules/.neoagent-bootstrap-stamp',
|
|
159
136
|
'fi',
|
|
160
137
|
'',
|
|
161
|
-
'
|
|
138
|
+
'if [ ! -f "$BROWSER_DEPS_MARKER" ]; then',
|
|
139
|
+
' echo "Updating package lists..."',
|
|
140
|
+
' retry_cmd apt-get update || echo "Warning: apt-get update failed, proceeding with cached lists."',
|
|
141
|
+
'',
|
|
142
|
+
' echo "Installing browser runtime dependencies..."',
|
|
143
|
+
' retry_cmd apt-get install -y --no-install-recommends \\',
|
|
144
|
+
' curl ca-certificates gnupg git rsync unzip \\',
|
|
145
|
+
' dbus-x11 \\',
|
|
146
|
+
' xvfb \\',
|
|
147
|
+
' libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0 libcups2 \\',
|
|
148
|
+
' libx11-xcb1 libgtk-3-0 libnss3 libnspr4 libxcomposite1 libxdamage1 \\',
|
|
149
|
+
' libxrandr2 libxkbcommon0 libasound2t64 libgbm1 libdrm2 libdbus-1-3 \\',
|
|
150
|
+
' libpango-1.0-0 libpangocairo-1.0-0 libxshmfence1 || echo "Warning: Some browser dependencies failed to install."',
|
|
151
|
+
' touch "$BROWSER_DEPS_MARKER"',
|
|
152
|
+
'else',
|
|
153
|
+
' echo "Browser runtime dependencies already installed; skipping apt install."',
|
|
154
|
+
'fi',
|
|
155
|
+
'',
|
|
156
|
+
'systemctl daemon-reload',
|
|
157
|
+
'systemctl enable neoagent-guest-agent.service || true',
|
|
158
|
+
'if ! systemctl is-active --quiet neoagent-guest-agent.service; then',
|
|
159
|
+
' systemctl start neoagent-guest-agent.service || true',
|
|
160
|
+
'fi',
|
|
161
|
+
'echo "NeoAgent guest agent is available; continuing browser runtime provisioning..."',
|
|
162
|
+
'',
|
|
162
163
|
'PLAYWRIGHT_BROWSERS_PATH="$APP_DIR/.playwright-browsers"',
|
|
163
|
-
'PLAYWRIGHT_STAMP="$PLAYWRIGHT_BROWSERS_PATH/.
|
|
164
|
-
'
|
|
164
|
+
'PLAYWRIGHT_STAMP="$PLAYWRIGHT_BROWSERS_PATH/.firefox-installed"',
|
|
165
|
+
'mkdir -p "$PLAYWRIGHT_BROWSERS_PATH"',
|
|
166
|
+
'if [ ! -f "$PLAYWRIGHT_STAMP" ] || [ package.json -nt "$PLAYWRIGHT_STAMP" ]; then',
|
|
165
167
|
' echo "Installing Playwright browsers..."',
|
|
166
|
-
'
|
|
167
|
-
' PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH" npx playwright install chromium --with-deps || \\',
|
|
168
|
-
' PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH" node ./node_modules/playwright-chromium/install.js || true',
|
|
168
|
+
' PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH" retry_cmd npx playwright install firefox || { echo "Error: Playwright browser install failed." >&2; exit 1; }',
|
|
169
169
|
' date > "$PLAYWRIGHT_STAMP"',
|
|
170
170
|
'fi',
|
|
171
171
|
'',
|
|
172
|
-
'
|
|
173
|
-
'systemctl enable neoagent-guest-agent.service || true',
|
|
174
|
-
'systemctl restart neoagent-guest-agent.service || true',
|
|
172
|
+
'touch "$BROWSER_READY_MARKER"',
|
|
175
173
|
'touch "$BOOTSTRAP_MARKER"',
|
|
176
174
|
'echo "NeoAgent guest bootstrap completed."',
|
|
177
175
|
'',
|
|
@@ -180,15 +178,13 @@ function createCloudInitScript({
|
|
|
180
178
|
|
|
181
179
|
function createCloudInitUserData({
|
|
182
180
|
guestToken,
|
|
183
|
-
|
|
184
|
-
hostDataMount = '/mnt/neoagent-data',
|
|
181
|
+
guestPayloadBase64,
|
|
185
182
|
guestAgentPort = 8421,
|
|
186
183
|
}) {
|
|
187
184
|
const guestTokenB64 = encodeGuestToken(guestToken);
|
|
188
185
|
const bootstrapScript = createCloudInitScript({
|
|
189
186
|
guestToken,
|
|
190
|
-
|
|
191
|
-
hostDataMount,
|
|
187
|
+
guestPayloadPath: '/var/lib/neoagent/guest-payload.tar.gz',
|
|
192
188
|
guestAgentPort,
|
|
193
189
|
});
|
|
194
190
|
|
|
@@ -200,8 +196,14 @@ function createCloudInitUserData({
|
|
|
200
196
|
" permissions: '0600'",
|
|
201
197
|
' owner: root:root',
|
|
202
198
|
' content: |',
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
` NEOAGENT_VM_GUEST_TOKEN_B64=${guestTokenB64}`,
|
|
200
|
+
` NEOAGENT_GUEST_AGENT_PORT=${guestAgentPort}`,
|
|
201
|
+
' - path: /var/lib/neoagent/guest-payload.tar.gz',
|
|
202
|
+
" permissions: '0644'",
|
|
203
|
+
' owner: root:root',
|
|
204
|
+
" encoding: 'b64'",
|
|
205
|
+
' content: |',
|
|
206
|
+
` ${guestPayloadBase64}`,
|
|
205
207
|
' - path: /usr/local/bin/neoagent-guest-bootstrap.sh',
|
|
206
208
|
" permissions: '0755'",
|
|
207
209
|
' owner: root:root',
|
|
@@ -409,7 +411,6 @@ function createSeedIso(sourceDir, isoPath) {
|
|
|
409
411
|
function ensureGuestBootstrapSeed({
|
|
410
412
|
userRoot,
|
|
411
413
|
guestToken,
|
|
412
|
-
hostShareMount = '/mnt/neoagent-host',
|
|
413
414
|
guestAgentPort = 8421,
|
|
414
415
|
guestArch = 'x64',
|
|
415
416
|
}) {
|
|
@@ -422,7 +423,13 @@ function ensureGuestBootstrapSeed({
|
|
|
422
423
|
const userDataPath = path.join(seedDir, 'user-data');
|
|
423
424
|
const metaDataPath = path.join(seedDir, 'meta-data');
|
|
424
425
|
const startupNshPath = path.join(seedDir, 'startup.nsh');
|
|
425
|
-
const
|
|
426
|
+
const guestPayloadArchivePath = createGuestPayloadArchive(seedDir);
|
|
427
|
+
const guestPayloadBase64 = fs.readFileSync(guestPayloadArchivePath).toString('base64');
|
|
428
|
+
const userData = createCloudInitUserData({
|
|
429
|
+
guestToken,
|
|
430
|
+
guestPayloadBase64,
|
|
431
|
+
guestAgentPort,
|
|
432
|
+
});
|
|
426
433
|
const metaData = createCloudInitMetaData({
|
|
427
434
|
instanceId: `neoagent-${path.basename(userRoot)}`,
|
|
428
435
|
localHostName: `neoagent-${path.basename(userRoot)}`,
|
|
@@ -467,7 +474,6 @@ function ensureGuestBootstrapSeed({
|
|
|
467
474
|
userDataPath,
|
|
468
475
|
metaDataPath,
|
|
469
476
|
startupNshPath,
|
|
470
|
-
hostShareMount,
|
|
471
477
|
};
|
|
472
478
|
}
|
|
473
479
|
|