getbotons 0.0.0-stage → 0.1.0

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.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +505 -2
  3. package/installer/admin.mjs +6 -0
  4. package/installer/botons.mjs +430 -0
  5. package/installer/brain-clis.mjs +28 -0
  6. package/installer/linger.mjs +39 -0
  7. package/installer/output.mjs +27 -0
  8. package/installer/revision.mjs +30 -0
  9. package/installer/shell-path.mjs +66 -0
  10. package/installer/source-lock.json +2891 -0
  11. package/package.json +26 -4
  12. package/payload/LICENSE +21 -0
  13. package/payload/SECURITY.md +33 -0
  14. package/payload/installer/admin.mjs +6 -0
  15. package/payload/installer/botons.mjs +430 -0
  16. package/payload/installer/brain-clis.mjs +28 -0
  17. package/payload/installer/linger.mjs +39 -0
  18. package/payload/installer/output.mjs +27 -0
  19. package/payload/installer/revision.mjs +30 -0
  20. package/payload/installer/shell-path.mjs +66 -0
  21. package/payload/installer/source-lock.json +2891 -0
  22. package/payload/npm-shrinkwrap.json +51 -0
  23. package/payload/package.json +13 -0
  24. package/payload/scripts/backup.ts +47 -0
  25. package/payload/scripts/botons-backup.service +15 -0
  26. package/payload/scripts/botons-backup.timer +11 -0
  27. package/payload/scripts/botons.service +36 -0
  28. package/payload/scripts/check-browser.ts +90 -0
  29. package/payload/scripts/doctor.ts +790 -0
  30. package/payload/scripts/install-backup-timer.ts +52 -0
  31. package/payload/scripts/install-service.ts +206 -0
  32. package/payload/scripts/login.ts +17 -0
  33. package/payload/scripts/migrate-names.ts +47 -0
  34. package/payload/scripts/pair.ts +163 -0
  35. package/payload/scripts/restore.ts +108 -0
  36. package/payload/server/bin/botons +4 -0
  37. package/payload/server/bin/legacy/minion +4 -0
  38. package/payload/server/bin/legacy/tots +4 -0
  39. package/payload/server/src/acting.ts +16 -0
  40. package/payload/server/src/api-keys.ts +544 -0
  41. package/payload/server/src/api.ts +1484 -0
  42. package/payload/server/src/app.ts +546 -0
  43. package/payload/server/src/approvals.ts +873 -0
  44. package/payload/server/src/ask-rules.ts +181 -0
  45. package/payload/server/src/audit.ts +158 -0
  46. package/payload/server/src/auth.ts +497 -0
  47. package/payload/server/src/backup.ts +382 -0
  48. package/payload/server/src/brain-login.ts +75 -0
  49. package/payload/server/src/browser-cert.ts +67 -0
  50. package/payload/server/src/browser-handoff.ts +119 -0
  51. package/payload/server/src/browser-handover.ts +175 -0
  52. package/payload/server/src/browser-policy.ts +210 -0
  53. package/payload/server/src/browser-proxy.ts +580 -0
  54. package/payload/server/src/browser-request.ts +335 -0
  55. package/payload/server/src/browser-screenshot.ts +56 -0
  56. package/payload/server/src/browser-send.ts +155 -0
  57. package/payload/server/src/browser.ts +2164 -0
  58. package/payload/server/src/callback.ts +218 -0
  59. package/payload/server/src/capabilities.ts +90 -0
  60. package/payload/server/src/cards.ts +243 -0
  61. package/payload/server/src/cli/approve-mcp.ts +119 -0
  62. package/payload/server/src/cli/botons.ts +629 -0
  63. package/payload/server/src/cli/browser-mcp.ts +168 -0
  64. package/payload/server/src/cli/workspace-file.ts +78 -0
  65. package/payload/server/src/cli-homes.ts +38 -0
  66. package/payload/server/src/cli-install.ts +104 -0
  67. package/payload/server/src/cli-updates.ts +112 -0
  68. package/payload/server/src/codex-config-repair.ts +80 -0
  69. package/payload/server/src/config.ts +473 -0
  70. package/payload/server/src/connector-access.ts +158 -0
  71. package/payload/server/src/connector-launch.ts +203 -0
  72. package/payload/server/src/connectors.ts +947 -0
  73. package/payload/server/src/disk-usage.ts +62 -0
  74. package/payload/server/src/engine.ts +2328 -0
  75. package/payload/server/src/env-init.ts +5 -0
  76. package/payload/server/src/events.ts +46 -0
  77. package/payload/server/src/files.ts +528 -0
  78. package/payload/server/src/github-access.ts +52 -0
  79. package/payload/server/src/health.ts +117 -0
  80. package/payload/server/src/held-dir.ts +96 -0
  81. package/payload/server/src/hooks.ts +141 -0
  82. package/payload/server/src/http.ts +207 -0
  83. package/payload/server/src/index.ts +139 -0
  84. package/payload/server/src/install-type.ts +11 -0
  85. package/payload/server/src/instructions.ts +204 -0
  86. package/payload/server/src/isolation.ts +234 -0
  87. package/payload/server/src/legacy-upgrade.ts +668 -0
  88. package/payload/server/src/limits.ts +44 -0
  89. package/payload/server/src/lock.ts +75 -0
  90. package/payload/server/src/log.ts +137 -0
  91. package/payload/server/src/managed-clis.ts +65 -0
  92. package/payload/server/src/mcp-client.ts +229 -0
  93. package/payload/server/src/notify.ts +193 -0
  94. package/payload/server/src/oauth-connections.ts +650 -0
  95. package/payload/server/src/oauth.ts +311 -0
  96. package/payload/server/src/outbound.ts +142 -0
  97. package/payload/server/src/portable.ts +1669 -0
  98. package/payload/server/src/prompt.ts +676 -0
  99. package/payload/server/src/providers/api-brain.ts +612 -0
  100. package/payload/server/src/providers/api-history.ts +131 -0
  101. package/payload/server/src/providers/api-tools.ts +481 -0
  102. package/payload/server/src/providers/api-wire.ts +690 -0
  103. package/payload/server/src/providers/claude-cli.ts +620 -0
  104. package/payload/server/src/providers/cli-check.ts +265 -0
  105. package/payload/server/src/providers/cli-turn.ts +20 -0
  106. package/payload/server/src/providers/codex-cli.ts +552 -0
  107. package/payload/server/src/providers/codex-workspace.ts +61 -0
  108. package/payload/server/src/providers/failures.ts +143 -0
  109. package/payload/server/src/providers/index.ts +131 -0
  110. package/payload/server/src/providers/mcp-stdio.ts +147 -0
  111. package/payload/server/src/providers/mock.ts +94 -0
  112. package/payload/server/src/providers/process.ts +268 -0
  113. package/payload/server/src/providers/readiness.ts +43 -0
  114. package/payload/server/src/providers/sse.ts +73 -0
  115. package/payload/server/src/providers/types.ts +173 -0
  116. package/payload/server/src/rates.ts +128 -0
  117. package/payload/server/src/reactions.ts +266 -0
  118. package/payload/server/src/recent-logs.ts +489 -0
  119. package/payload/server/src/rehearsal.ts +593 -0
  120. package/payload/server/src/rehearsals.ts +303 -0
  121. package/payload/server/src/replay.ts +165 -0
  122. package/payload/server/src/sandbox.ts +767 -0
  123. package/payload/server/src/schedule.ts +214 -0
  124. package/payload/server/src/scheduler.ts +24 -0
  125. package/payload/server/src/schema.ts +882 -0
  126. package/payload/server/src/secrets.ts +46 -0
  127. package/payload/server/src/setup.ts +356 -0
  128. package/payload/server/src/skill-packages.ts +363 -0
  129. package/payload/server/src/skills.ts +527 -0
  130. package/payload/server/src/spend.ts +255 -0
  131. package/payload/server/src/ssrf.ts +126 -0
  132. package/payload/server/src/starter-gate.ts +168 -0
  133. package/payload/server/src/starters.ts +610 -0
  134. package/payload/server/src/storage.ts +32 -0
  135. package/payload/server/src/store.ts +3087 -0
  136. package/payload/server/src/tailnet.ts +132 -0
  137. package/payload/server/src/templates.ts +454 -0
  138. package/payload/server/src/trash.ts +772 -0
  139. package/payload/server/src/util.ts +97 -0
  140. package/payload/server/src/webhooks.ts +216 -0
  141. package/payload/server/src/work-feed.ts +473 -0
  142. package/payload/shared/app-version.d.mts +1 -0
  143. package/payload/shared/app-version.mjs +3 -0
  144. package/payload/shared/boton.ts +323 -0
  145. package/payload/shared/brand.ts +107 -0
  146. package/payload/shared/cards.ts +427 -0
  147. package/payload/shared/effort.ts +91 -0
  148. package/payload/shared/files.ts +104 -0
  149. package/payload/shared/handoff.ts +97 -0
  150. package/payload/shared/install-commands.ts +7 -0
  151. package/payload/shared/legacy-names.d.mts +35 -0
  152. package/payload/shared/legacy-names.mjs +244 -0
  153. package/payload/shared/live-view.ts +94 -0
  154. package/payload/shared/look-critter.ts +189 -0
  155. package/payload/shared/look-tater.ts +127 -0
  156. package/payload/shared/look.ts +200 -0
  157. package/payload/shared/reactions.ts +43 -0
  158. package/payload/shared/seeded.ts +34 -0
  159. package/payload/shared/services.ts +90 -0
  160. package/payload/shared/types.ts +3016 -0
  161. package/payload/shared/util.ts +68 -0
  162. package/payload/shared/versions.ts +44 -0
  163. package/payload/shared/wake.ts +164 -0
  164. package/payload/web/dist/apple-touch-icon.png +0 -0
  165. package/payload/web/dist/assets/Icons-nytVAa9g.js +1 -0
  166. package/payload/web/dist/assets/NewBotDialog-DXr2XhDP.js +1 -0
  167. package/payload/web/dist/assets/PairScreen-CMcJO7cQ.js +1 -0
  168. package/payload/web/dist/assets/Panel-Bvqz3I3m.js +4 -0
  169. package/payload/web/dist/assets/ReactionPicker-B0SKsjiy.js +1 -0
  170. package/payload/web/dist/assets/SettingRows-CvLomtuo.js +4 -0
  171. package/payload/web/dist/assets/SettingsDialog-CQ41LV-P.js +8 -0
  172. package/payload/web/dist/assets/Setup-C-3P7Mtn.js +1 -0
  173. package/payload/web/dist/assets/Skills-DXMH69kH.js +1 -0
  174. package/payload/web/dist/assets/StarterSetup-Ce2V1_Cs.js +5 -0
  175. package/payload/web/dist/assets/Steps-CKtB3GAM.js +1 -0
  176. package/payload/web/dist/assets/emoji-catalog-DfpPz8qw.js +540 -0
  177. package/payload/web/dist/assets/index-BEU7FDxV.css +1 -0
  178. package/payload/web/dist/assets/index-Dhwn-rf0.js +3 -0
  179. package/payload/web/dist/assets/react-C1iE8mnY.js +9 -0
  180. package/payload/web/dist/assets/reactions-rw0YeCmA.js +1 -0
  181. package/payload/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  182. package/payload/web/dist/assets/util-Cx5feSJu.js +1 -0
  183. package/payload/web/dist/assets/vendor-B3qdcjGo.js +14 -0
  184. package/payload/web/dist/favicon.svg +1 -0
  185. package/payload/web/dist/icon-192.png +0 -0
  186. package/payload/web/dist/icon-512.png +0 -0
  187. package/payload/web/dist/icon.svg +1 -0
  188. package/payload/web/dist/index.html +67 -0
  189. package/payload/web/dist/manifest.webmanifest +12 -0
  190. package/shared/legacy-names.d.mts +35 -0
  191. package/shared/legacy-names.mjs +244 -0
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "getbotons",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "getbotons",
9
+ "version": "0.1.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "croner": "^11.0.0",
13
+ "playwright-core": "^1.65.0"
14
+ },
15
+ "engines": {
16
+ "node": ">=24"
17
+ }
18
+ },
19
+ "node_modules/croner": {
20
+ "version": "11.0.0",
21
+ "resolved": "https://registry.npmjs.org/croner/-/croner-11.0.0.tgz",
22
+ "integrity": "sha512-T17ADjdttrVToyzLpLq9Xb9xzcMwpQ/M3Qs9szhh0MVNTbv6NWQ9OFYa2qlzPYXFMn+uoDlnW1IpIv6cAvFtyA==",
23
+ "funding": [
24
+ {
25
+ "type": "other",
26
+ "url": "https://paypal.me/hexagonpp"
27
+ },
28
+ {
29
+ "type": "github",
30
+ "url": "https://github.com/sponsors/hexagon"
31
+ }
32
+ ],
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18.0"
36
+ }
37
+ },
38
+ "node_modules/playwright-core": {
39
+ "version": "1.65.0",
40
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.65.0.tgz",
41
+ "integrity": "sha512-vNSKgH6YqtN7cdT/L334zo0a+YwkrpicyrHJlWWPIm/Q+Nn2SWInil01oT0mbu96U4GgoRtCYQD9CzJQgju+lA==",
42
+ "license": "Apache-2.0",
43
+ "bin": {
44
+ "playwright-core": "cli.js"
45
+ },
46
+ "engines": {
47
+ "node": ">=20"
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "getbotons",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "dependencies": {
10
+ "croner": "^11.0.0",
11
+ "playwright-core": "^1.65.0"
12
+ }
13
+ }
@@ -0,0 +1,47 @@
1
+ // `npm run backup` (and the nightly botons-backup.timer); BOTONS_BACKUP_RSYNC_TO also copies the new backup there.
2
+ import '../server/src/env-init.ts';
3
+ import { spawnSync } from 'node:child_process';
4
+ import { type BackupResult, backup } from '../server/src/backup.ts';
5
+ import { botonsHome } from '../server/src/config.ts';
6
+
7
+ let home: string;
8
+ try {
9
+ home = botonsHome();
10
+ } catch (err) {
11
+ console.error((err as Error).message);
12
+ process.exit(1);
13
+ }
14
+ let result: BackupResult;
15
+ try {
16
+ result = backup({ home });
17
+ } catch (err) {
18
+ console.error(`Backup of ${home} failed: ${(err as Error).message}`);
19
+ process.exit(1);
20
+ }
21
+
22
+ const { manifest } = result;
23
+ const counts = Object.entries(manifest.counts)
24
+ .map(([table, n]) => `${n} ${table}`)
25
+ .join(', ');
26
+ console.log(`Backed up ${home} to ${result.dir}`);
27
+ console.log(` schema v${manifest.schemaVersion}: ${counts}${manifest.workspaces ? '; workspaces.tar.gz' : ''}`);
28
+ if (manifest.tarWarning) console.warn(` tar warnings (these files are not in the backup):\n${manifest.tarWarning.replace(/^/gm, ' ')}`);
29
+ if (result.removed.length) console.log(` removed old backups: ${result.removed.join(', ')}`);
30
+ // Fail the run (and the unit) only after the database backup is in place and copied.
31
+ let failed = false;
32
+ if (manifest.workspacesError) {
33
+ console.error(`The workspaces were NOT backed up; the database was. Older backups were kept. ${manifest.workspacesError}`);
34
+ failed = true;
35
+ }
36
+
37
+ const target = process.env.BOTONS_BACKUP_RSYNC_TO;
38
+ if (target) {
39
+ const rsync = spawnSync('rsync', ['-a', result.dir, `${target.replace(/\/+$/, '')}/`], { stdio: 'inherit' });
40
+ if (rsync.status !== 0) {
41
+ console.error(`Copying the backup to ${target} failed (${rsync.error?.message ?? `rsync exit ${rsync.status}`}). The local backup is kept.`);
42
+ failed = true;
43
+ } else {
44
+ console.log(` copied to ${target}`);
45
+ }
46
+ }
47
+ if (failed) process.exit(1);
@@ -0,0 +1,15 @@
1
+ # Installed by `npm run backup:install`, which fills in the Node binary and
2
+ # checkout paths. Started daily by @TIMER@.
3
+ [Unit]
4
+ Description=Botons backup (database snapshot and workspaces)
5
+
6
+ [Service]
7
+ Type=oneshot
8
+ # Optional. Set BOTONS_HOME here if the server uses a non-default one, and
9
+ # BOTONS_BACKUP_RSYNC_TO to copy each backup to another machine. It may be
10
+ # missing.
11
+ EnvironmentFile=-%h/.config/botons/env
12
+ UMask=0077
13
+ Nice=10
14
+ IOSchedulingClass=idle
15
+ ExecStart="@NODE@" "@REPO@/scripts/backup.ts"
@@ -0,0 +1,11 @@
1
+ # Installed by `npm run backup:install`. Runs @SERVICE@ daily.
2
+ [Unit]
3
+ Description=Nightly Botons backup
4
+
5
+ [Timer]
6
+ OnCalendar=*-*-* 03:30:00
7
+ # If the machine was off or asleep at 03:30, back up as soon as it is back.
8
+ Persistent=true
9
+
10
+ [Install]
11
+ WantedBy=timers.target
@@ -0,0 +1,36 @@
1
+ # Installed by `npm run service:install`, which fills in the Node binary, the
2
+ # app folder and the environment file. See "Run it as a service" in the README.
3
+ [Unit]
4
+ Description=@DESCRIPTION@
5
+ # Restart after a crash, but give up (and leave it failed) if it keeps failing:
6
+ # more than 10 starts in 5 minutes.
7
+ StartLimitIntervalSec=300
8
+ StartLimitBurst=10
9
+
10
+ [Service]
11
+ Type=exec
12
+ WorkingDirectory=@APP@
13
+ # PATH (node, claude, codex and the tools Botons use), BOTONS_CLAUDE_BIN,
14
+ # BOTONS_CODEX_BIN and any other BOTONS_* settings. The backup unit reads the
15
+ # same file.
16
+ EnvironmentFile=@ENV_FILE@
17
+ # Node runs the TypeScript directly: no npm, no build step at start.
18
+ ExecStart="@NODE@" "@APP@/server/src/index.ts"
19
+ Restart=on-failure
20
+ RestartSec=5
21
+ # 78 (EX_CONFIG): a bad setting, or a database this code cannot open.
22
+ # Restarting cannot fix either, so systemd leaves the unit failed instead.
23
+ RestartPreventExitStatus=78
24
+ # Stopping (and restarting) sends SIGTERM to the server alone. It takes no new
25
+ # work, gives running turns up to BOTONS_DRAIN_MS (default 2m) to finish, then
26
+ # stops its agent processes itself; work that was cut off or still queued runs
27
+ # after the next start. Whatever is still in the unit's cgroup after the server
28
+ # exits, or after TimeoutStopSec (set above the drain budget), gets SIGKILL.
29
+ KillMode=mixed
30
+ TimeoutStopSec=@STOP_TIMEOUT@
31
+ StandardOutput=journal
32
+ StandardError=journal
33
+ SyslogIdentifier=@NAME@
34
+
35
+ [Install]
36
+ WantedBy=default.target
@@ -0,0 +1,90 @@
1
+ // `npm run check:browser` (in Docker: `botons check-browser`): starts the managed browser the way a Boton's turn
2
+ // does (server/src/browser.ts, openGuarded) on a temp profile, against a page this script serves on loopback, and
3
+ // checks that it reads the page, takes a screenshot, and that nothing else gets out: a request to another loopback
4
+ // port, a POST and a WebSocket to the allowed one. Touches no install: no data folder, no server, no network beyond
5
+ // 127.0.0.1. Exit 0 when the browser works with every guard, 1 when it doesn't (and why).
6
+ import { mkdtempSync, rmSync } from 'node:fs';
7
+ import { createServer, type Server } from 'node:http';
8
+ import type { AddressInfo } from 'node:net';
9
+ import { tmpdir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { findBrowser, loadPlaywright, openGuarded } from '../server/src/browser.ts';
12
+ import { loadConfig } from '../server/src/config.ts';
13
+
14
+ function listen(handler: (method: string, url: string) => { status?: number; body: string }): Promise<{ server: Server; port: number; hits: string[] }> {
15
+ const hits: string[] = [];
16
+ const server = createServer((req, res) => {
17
+ hits.push(`${req.method} ${req.url}`);
18
+ const out = handler(req.method ?? '', req.url ?? '');
19
+ res.writeHead(out.status ?? 200, { 'content-type': 'text/html; charset=utf-8' });
20
+ res.end(out.body);
21
+ });
22
+ server.on('upgrade', (req, socket) => {
23
+ hits.push(`UPGRADE ${req.url}`);
24
+ socket.destroy();
25
+ });
26
+ return new Promise((r) => server.listen(0, '127.0.0.1', () => r({ server, port: (server.address() as AddressInfo).port, hits })));
27
+ }
28
+
29
+ async function main(): Promise<string[]> {
30
+ const cfg = loadConfig(process.env, () => ({ ip: null, dnsName: null }), { create: false });
31
+ const problems: string[] = [];
32
+ const other = await listen(() => ({ body: 'other' }));
33
+ const site = await listen((method, url) =>
34
+ url.startsWith('/page')
35
+ ? {
36
+ body: `<!doctype html><title>Botons browser check</title><p id="t">botons-browser-ok</p><script>
37
+ fetch('http://127.0.0.1:${other.port}/fetch').catch(() => {});
38
+ fetch('/post', { method: 'POST', body: 'x' }).catch(() => {});
39
+ try { new WebSocket('ws://127.0.0.1:' + location.port + '/ws'); } catch (e) {}
40
+ </script>`,
41
+ }
42
+ : { status: method === 'GET' ? 404 : 405, body: '' },
43
+ );
44
+ const profile = mkdtempSync(join(tmpdir(), 'botons-check-browser-'));
45
+ // Only what the server's own availability check finds (BOTONS_BROWSER_PATH, Playwright's, the system's).
46
+ const found = await findBrowser(cfg.browser.path);
47
+ try {
48
+ if (!found.available || !found.executable) return [found.why ?? 'no browser'];
49
+ const blocked: string[] = [];
50
+ const { context, page } = await openGuarded((await loadPlaywright())!, join(profile, 'profile'), {
51
+ executable: found.executable,
52
+ sandbox: cfg.browser.sandbox,
53
+ headless: true,
54
+ policy: () => ({ enabled: true, sites: [`127.0.0.1:${site.port}`] }),
55
+ note: (url, why) => blocked.push(`${url}: ${why}`),
56
+ });
57
+ try {
58
+ await page.goto(`http://127.0.0.1:${site.port}/page`, { waitUntil: 'load', timeout: 30_000 });
59
+ await page.waitForTimeout(1500);
60
+ const text = await page.locator('#t').innerText({ timeout: 10_000 });
61
+ if (text !== 'botons-browser-ok') problems.push(`the page read as ${JSON.stringify(text)}`);
62
+ const png = await page.screenshot({ type: 'png' });
63
+ if (png.subarray(1, 4).toString() !== 'PNG') problems.push('the screenshot is not a PNG');
64
+ const reached = site.hits.filter((h) => h !== 'GET /page' && !h.startsWith('GET /favicon'));
65
+ if (reached.length) problems.push(`the allowed site got more than reads of the page: ${reached.join(', ')}`);
66
+ if (other.hits.length) problems.push(`another loopback port was reached: ${other.hits.join(', ')}`);
67
+ if (blocked.length < 3) problems.push(`expected the fetch, the POST and the WebSocket to be blocked; blocked: ${blocked.join('; ') || 'nothing'}`);
68
+ } finally {
69
+ await context.close();
70
+ }
71
+ return problems;
72
+ } catch (err) {
73
+ // Playwright's message ends with Chromium's own log, where the reason (a sandbox that can't start, say) is.
74
+ const lines = (err as Error).message.split('\n').map((l) => l.trim()).filter(Boolean);
75
+ const why = lines.filter((l) => /sandbox|namespace|fatal|error/i.test(l)).slice(0, 5);
76
+ return [`the browser did not start or failed: ${lines[0]?.slice(0, 300)}`, ...why.map((l) => l.slice(0, 300))];
77
+ } finally {
78
+ other.server.close();
79
+ site.server.close();
80
+ rmSync(profile, { recursive: true, force: true });
81
+ }
82
+ }
83
+
84
+ const problems = await main();
85
+ const sandbox = loadConfig(process.env, () => ({ ip: null, dnsName: null }), { create: false }).browser.sandbox ? 'with its sandbox' : 'without its sandbox (BOTONS_BROWSER_SANDBOX=0)';
86
+ if (problems.length) {
87
+ console.error(`The managed browser does not work here (${sandbox}):\n${problems.map((p) => `- ${p}`).join('\n')}`);
88
+ process.exit(1);
89
+ }
90
+ console.log(`The managed browser works here (${sandbox}): it read a page and took a screenshot, and nothing else got out.`);