| 48 | ]; |
| 49 | |
| 50 | const main = async () => { |
| 51 | log("provisioning tart linux guest..."); |
| 52 | const vm = await tartVm("linux", "arm64").provision(); |
| 53 | log(`provisioned host=${vm.host}`); |
| 54 | try { |
| 55 | log("pushing packaged bundle tarball..."); |
| 56 | await execFileP("sshpass", [ |
| 57 | "-p", |
| 58 | "admin", |
| 59 | "scp", |
| 60 | "-o", |
| 61 | "StrictHostKeyChecking=no", |
| 62 | "-o", |
| 63 | "UserKnownHostsFile=/dev/null", |
| 64 | bundleTgz, |
| 65 | `admin@${vm.host}:/tmp/app.tgz`, |
| 66 | ]); |
| 67 | log("extracting + installing Electron runtime deps (apt)..."); |
| 68 | await vm.ssh("mkdir -p /tmp/app && tar -xzf /tmp/app.tgz -C /tmp/app"); |
| 69 | await vm.ssh("sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq"); |
| 70 | // Install each package independently so one missing name (noble t64 rename) |
| 71 | // doesn't fail the whole transaction. |
| 72 | const apt = await vm.ssh( |
| 73 | `for p in ${DEPS.join(" ")}; do sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $p >/dev/null 2>&1 && echo "ok $p" || echo "skip $p"; done`, |
| 74 | ); |
| 75 | log(`apt:\n${apt.stdout.trim()}`); |
| 76 | const nss = await vm.ssh("ldconfig -p | grep -c libnss3 || true"); |
| 77 | log(`libnss3 present: ${nss.stdout.trim()}`); |
| 78 | |
| 79 | const home = "/tmp/eh"; |
| 80 | const exe = "/tmp/app/executor-desktop"; |
| 81 | const sidecar = "/tmp/app/resources/sidecar/executor-sidecar"; |
| 82 | const webui = "/tmp/app/resources/web-ui"; |
| 83 | |
| 84 | log("starting bundled sidecar as supervised daemon..."); |
| 85 | await vm.ssh( |
| 86 | `rm -rf ${home}; mkdir -p ${home}; nohup env HOME=${home} EXECUTOR_SUPERVISED=1 EXECUTOR_DATA_DIR=${home}/.executor EXECUTOR_PORT=4789 EXECUTOR_HOST=127.0.0.1 EXECUTOR_AUTH_TOKEN=linux-attach EXECUTOR_CLIENT_DIR=${webui} ${sidecar} > /tmp/daemon.log 2>&1 &`, |
| 87 | ); |
| 88 | // Wait for the daemon to publish its manifest + serve. |
| 89 | for (let i = 0; i < 30; i++) { |
| 90 | const r = await vm.ssh(`cat ${home}/.executor/server-control/server.json 2>/dev/null`); |
| 91 | if (r.stdout.includes("cli-daemon")) break; |
| 92 | await new Promise((res) => setTimeout(res, 1000)); |
| 93 | } |
| 94 | const before = (await vm.ssh(`cat ${home}/.executor/server-control/server.json`)).stdout.trim(); |
| 95 | const beforeManifest = JSON.parse(before) as { kind: string; pid: number }; |
| 96 | log(`daemon manifest: kind=${beforeManifest.kind} pid=${beforeManifest.pid}`); |
| 97 | if (beforeManifest.kind !== "cli-daemon") throw new Error("daemon did not publish cli-daemon"); |
| 98 | const health = await vm.ssh( |
| 99 | `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:4789/api/health`, |
| 100 | ); |
| 101 | log(`/api/health = ${health.stdout.trim()}`); |
| 102 | |
| 103 | log("launching PACKAGED app under Xvfb..."); |
| 104 | await vm.ssh( |
| 105 | `nohup xvfb-run -a env HOME=${home} ELECTRON_ENABLE_LOGGING=1 ${exe} --no-sandbox > /tmp/app.log 2>&1 &`, |
| 106 | ); |
| 107 | |