@pasko70/pibo 1.0.1 → 1.0.2

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.
@@ -0,0 +1,58 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { detectDesktopEnv, hasDesktopDisplay, printLinuxVirtualDisplayHint } from './desktop-env.js';
4
+ export const PIBO_XVFB_SERVICE_NAME = 'pibo-xvfb.service';
5
+ export const PIBO_XVFB_SERVICE_PATH = `/etc/systemd/system/${PIBO_XVFB_SERVICE_NAME}`;
6
+ export function createPiboXvfbServiceUnit() {
7
+ return [
8
+ '[Unit]',
9
+ 'Description=Virtual X display for Pibo browser automation',
10
+ 'After=network.target',
11
+ '',
12
+ '[Service]',
13
+ 'Type=simple',
14
+ 'ExecStart=/usr/bin/Xvfb :0 -screen 0 1920x1080x24 -ac -nolisten tcp',
15
+ 'Restart=always',
16
+ 'RestartSec=2',
17
+ '',
18
+ '[Install]',
19
+ 'WantedBy=multi-user.target',
20
+ '',
21
+ ].join('\n');
22
+ }
23
+ function isRootUser() {
24
+ return typeof process.getuid === 'function' && process.getuid() === 0;
25
+ }
26
+ async function waitForDisplaySocket(timeoutMs = 5000) {
27
+ const deadline = Date.now() + timeoutMs;
28
+ while (Date.now() < deadline) {
29
+ if (existsSync('/tmp/.X11-unix/X0'))
30
+ return true;
31
+ await new Promise((resolve) => setTimeout(resolve, 100));
32
+ }
33
+ return existsSync('/tmp/.X11-unix/X0');
34
+ }
35
+ export async function ensureLinuxVirtualDisplay(options) {
36
+ if (process.platform !== 'linux')
37
+ return false;
38
+ if (hasDesktopDisplay(detectDesktopEnv()))
39
+ return false;
40
+ if (!isRootUser()) {
41
+ console.log('No desktop display detected and automatic virtual display setup requires root.');
42
+ printLinuxVirtualDisplayHint(' ');
43
+ return false;
44
+ }
45
+ console.log('No desktop display detected. Provisioning virtual X display for browser-use.');
46
+ await options.runInherited('apt-get', ['update']);
47
+ await options.runInherited('apt-get', ['install', '-y', 'xvfb', 'xauth', 'x11-xserver-utils']);
48
+ await writeFile(PIBO_XVFB_SERVICE_PATH, createPiboXvfbServiceUnit(), 'utf8');
49
+ await options.runInherited('systemctl', ['daemon-reload']);
50
+ await options.runInherited('systemctl', ['enable', '--now', PIBO_XVFB_SERVICE_NAME]);
51
+ process.env.DISPLAY = ':0';
52
+ const ready = await waitForDisplaySocket();
53
+ if (!ready) {
54
+ throw new Error('Virtual X display service was installed but DISPLAY :0 did not become ready.');
55
+ }
56
+ console.log('Virtual X display ready on DISPLAY=:0');
57
+ return true;
58
+ }
@@ -49,7 +49,7 @@ function runBuffered(command, args, env = process.env) {
49
49
  });
50
50
  });
51
51
  }
52
- function runInherited(command, args, env = process.env) {
52
+ export function runInheritedCommand(command, args, env = process.env) {
53
53
  return new Promise((resolve, reject) => {
54
54
  console.log(`Running: ${command} ${args.join(' ')}`);
55
55
  const child = spawn(command, args, {
@@ -136,9 +136,9 @@ export async function installToolPythonRuntime(name, spec) {
136
136
  await mkdir(paths.rootDir, { recursive: true });
137
137
  await mkdir(paths.homeDir, { recursive: true });
138
138
  if (!existsSync(paths.venvDir)) {
139
- await runInherited('uv', ['venv', paths.venvDir, '--python', spec.pythonVersion]);
139
+ await runInheritedCommand('uv', ['venv', paths.venvDir, '--python', spec.pythonVersion]);
140
140
  }
141
- await runInherited('uv', [
141
+ await runInheritedCommand('uv', [
142
142
  'pip',
143
143
  'install',
144
144
  '--python',
@@ -146,7 +146,7 @@ export async function installToolPythonRuntime(name, spec) {
146
146
  spec.packageName,
147
147
  ]);
148
148
  if (spec.postInstallArgs?.length) {
149
- await runInherited(paths.executablePath, spec.postInstallArgs, getToolPythonRuntimeEnv(paths, spec));
149
+ await runInheritedCommand(paths.executablePath, spec.postInstallArgs, getToolPythonRuntimeEnv(paths, spec));
150
150
  }
151
151
  return paths;
152
152
  }
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { detectDesktopEnv, hasDesktopDisplay, printDesktopEnvStatus, printLinuxVirtualDisplayHint } from './desktop-env.js';
3
3
  import { BROWSER_USE_GUIDE, REMOTE_BROWSER_GUIDE } from './guides.js';
4
- import { getToolPythonRuntimePaths, installToolPythonRuntime, printToolPythonRuntimeDoctor, removeToolPythonRuntime, } from './python-runtime.js';
4
+ import { ensureLinuxVirtualDisplay } from './linux-virtual-display.js';
5
+ import { getToolPythonRuntimePaths, installToolPythonRuntime, runInheritedCommand, printToolPythonRuntimeDoctor, removeToolPythonRuntime, } from './python-runtime.js';
5
6
  const MAX_AGENT_CONTEXT_SNIPPET_LENGTH = 480;
6
7
  const INSTALLED_TOOLS_CONTEXT_PATH = '.pibo/context/installed-pibo-tools.md';
7
8
  const REGISTRY = [
@@ -90,6 +91,9 @@ export function getInstalledCliToolContextFile() {
90
91
  }
91
92
  export async function installCliTool(entry, runSetup) {
92
93
  if (runSetup) {
94
+ if (entry.name === 'browser-use') {
95
+ await ensureLinuxVirtualDisplay({ runInherited: runInheritedCommand });
96
+ }
93
97
  await installToolPythonRuntime(entry.name, entry.runtime);
94
98
  }
95
99
  const status = getCliToolStatus(entry);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Minimal TypeScript wrapper around Pi Coding Agent.",
6
6
  "files": [