@pasko70/pibo 1.0.0 → 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.
@@ -3,6 +3,7 @@ export class InitialSessionContext {
3
3
  profileName;
4
4
  sessionId;
5
5
  parentSessionId;
6
+ model;
6
7
  skills;
7
8
  tools;
8
9
  subagents;
@@ -17,6 +18,7 @@ export class InitialSessionContext {
17
18
  this.profileName = options.profileName;
18
19
  this.sessionId = options.sessionId;
19
20
  this.parentSessionId = options.parentSessionId;
21
+ this.model = options.model ? { ...options.model } : undefined;
20
22
  this.skills = [...(options.skills ?? [])];
21
23
  this.tools = [...(options.tools ?? [])];
22
24
  this.subagents = [...(options.subagents ?? [])];
@@ -33,6 +35,7 @@ export class InitialSessionContextBuilder {
33
35
  profileName;
34
36
  sessionId;
35
37
  parentSessionId;
38
+ model;
36
39
  skills = [];
37
40
  tools = [];
38
41
  subagents = [];
@@ -54,6 +57,10 @@ export class InitialSessionContextBuilder {
54
57
  this.parentSessionId = parentSessionId;
55
58
  return this;
56
59
  }
60
+ withModel(model) {
61
+ this.model = { ...model };
62
+ return this;
63
+ }
57
64
  withBuiltinTools(mode) {
58
65
  this.builtinTools = mode;
59
66
  return this;
@@ -123,6 +130,7 @@ export class InitialSessionContextBuilder {
123
130
  profileName: this.profileName,
124
131
  sessionId: this.sessionId,
125
132
  parentSessionId: this.parentSessionId,
133
+ model: this.model,
126
134
  skills: this.skills,
127
135
  tools: this.tools,
128
136
  subagents: this.subagents,
@@ -201,6 +201,7 @@ export async function createPiboRuntime(options = {}) {
201
201
  services,
202
202
  sessionManager: runtimeSessionManager,
203
203
  sessionStartEvent,
204
+ model: resolveProfileModel(profile, services),
204
205
  thinkingLevel: options.thinkingLevel,
205
206
  customTools,
206
207
  noTools: profile.builtinTools === "disabled" ? "builtin" : undefined,
@@ -228,6 +229,18 @@ export async function createPiboRuntime(options = {}) {
228
229
  sessionManager,
229
230
  });
230
231
  }
232
+ function resolveProfileModel(profile, services) {
233
+ if (!profile.model)
234
+ return undefined;
235
+ const model = services.modelRegistry.find(profile.model.provider, profile.model.id);
236
+ if (!model) {
237
+ throw new Error(`Profile "${profile.profileName}" requests unknown model ${profile.model.provider}/${profile.model.id}.`);
238
+ }
239
+ if (!services.modelRegistry.hasConfiguredAuth(model)) {
240
+ throw new Error(`Profile "${profile.profileName}" requires configured auth for ${profile.model.provider}/${profile.model.id}.`);
241
+ }
242
+ return model;
243
+ }
231
244
  export async function inspectPiboProfile(options = {}) {
232
245
  const cwd = options.cwd ?? process.cwd();
233
246
  const profile = options.profile ?? createDefaultPiboProfile();
@@ -14,6 +14,7 @@ function profileForSession(baseProfile, piSessionId, parentPiSessionId) {
14
14
  profileName: baseProfile.profileName,
15
15
  sessionId: piSessionId,
16
16
  parentSessionId: parentPiSessionId,
17
+ model: baseProfile.model,
17
18
  skills: baseProfile.skills,
18
19
  tools: baseProfile.tools,
19
20
  subagents: baseProfile.subagents,
@@ -98,6 +98,16 @@ export const piboCorePlugin = definePiboPlugin({
98
98
  .createSession();
99
99
  },
100
100
  });
101
+ api.registerProfile({
102
+ name: "pibo-kimi-coding",
103
+ aliases: ["kimi", "kimi-coding"],
104
+ description: "Pibo profile pinned to Kimi For Coding. Requires KIMI_API_KEY or configured kimi-coding auth.",
105
+ create(context) {
106
+ return createBaseProfileBuilder("pibo-kimi-coding", context)
107
+ .withModel({ provider: "kimi-coding", id: "kimi-for-coding" })
108
+ .createSession();
109
+ },
110
+ });
101
111
  api.registerGatewayAction({
102
112
  name: "status",
103
113
  description: "Return current session status.",
@@ -88,3 +88,12 @@ export function printDesktopEnvStatus(indent = ' ') {
88
88
  console.log(`${indent}desktop: not detected`);
89
89
  console.log(`${indent}warning: headed browser mode is unavailable; use headless mode or start a desktop display first.`);
90
90
  }
91
+ export function printLinuxVirtualDisplayHint(indent = ' ') {
92
+ if (process.platform !== 'linux')
93
+ return;
94
+ console.log(`${indent}linux headed browser hint:`);
95
+ console.log(`${indent} Install a virtual X display if this host has no desktop session.`);
96
+ console.log(`${indent} Ubuntu/Debian: sudo apt update && sudo apt install -y xvfb xauth x11-xserver-utils`);
97
+ console.log(`${indent} Start one display: Xvfb :0 -screen 0 1920x1080x24 -ac -nolisten tcp`);
98
+ console.log(`${indent} Then export DISPLAY=:0 before running browser-use.`);
99
+ }
@@ -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
+ }
@@ -4,7 +4,7 @@ import { mkdir, rm } from 'node:fs/promises';
4
4
  import { homedir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { ErrorCode, formatCliError } from '../cli-errors.js';
7
- import { getDesktopProcessEnv, printDesktopEnvStatus } from './desktop-env.js';
7
+ import { detectDesktopEnv, getDesktopProcessEnv, hasDesktopDisplay, printDesktopEnvStatus, printLinuxVirtualDisplayHint, } from './desktop-env.js';
8
8
  function getPiboHome() {
9
9
  return process.env.PIBO_HOME || join(homedir(), '.pibo');
10
10
  }
@@ -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, {
@@ -94,6 +94,9 @@ export async function printToolPythonRuntimeDoctor(name, spec) {
94
94
  console.log(` venv: ${existsSync(paths.venvDir) ? 'present' : 'missing'}`);
95
95
  console.log(` executable: ${existsSync(paths.executablePath) ? paths.executablePath : 'missing'}`);
96
96
  printDesktopEnvStatus(' ');
97
+ if (name === 'browser-use' && process.platform === 'linux' && !hasDesktopDisplay(detectDesktopEnv())) {
98
+ printLinuxVirtualDisplayHint(' ');
99
+ }
97
100
  if (existsSync(paths.executablePath)) {
98
101
  const doctor = await runBuffered(paths.executablePath, ['doctor'], getToolPythonRuntimeEnv(paths, spec));
99
102
  console.log(` tool doctor: ${doctor.ok ? 'ok' : 'failed'}`);
@@ -133,9 +136,9 @@ export async function installToolPythonRuntime(name, spec) {
133
136
  await mkdir(paths.rootDir, { recursive: true });
134
137
  await mkdir(paths.homeDir, { recursive: true });
135
138
  if (!existsSync(paths.venvDir)) {
136
- await runInherited('uv', ['venv', paths.venvDir, '--python', spec.pythonVersion]);
139
+ await runInheritedCommand('uv', ['venv', paths.venvDir, '--python', spec.pythonVersion]);
137
140
  }
138
- await runInherited('uv', [
141
+ await runInheritedCommand('uv', [
139
142
  'pip',
140
143
  'install',
141
144
  '--python',
@@ -143,7 +146,7 @@ export async function installToolPythonRuntime(name, spec) {
143
146
  spec.packageName,
144
147
  ]);
145
148
  if (spec.postInstallArgs?.length) {
146
- await runInherited(paths.executablePath, spec.postInstallArgs, getToolPythonRuntimeEnv(paths, spec));
149
+ await runInheritedCommand(paths.executablePath, spec.postInstallArgs, getToolPythonRuntimeEnv(paths, spec));
147
150
  }
148
151
  return paths;
149
152
  }
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { printDesktopEnvStatus } from './desktop-env.js';
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);
@@ -97,6 +101,9 @@ export async function installCliTool(entry, runSetup) {
97
101
  console.log(` executable: ${status.executablePath}`);
98
102
  console.log(` home: ${status.homeDir}`);
99
103
  printDesktopEnvStatus(' ');
104
+ if (entry.name === 'browser-use' && process.platform === 'linux' && !hasDesktopDisplay(detectDesktopEnv())) {
105
+ printLinuxVirtualDisplayHint(' ');
106
+ }
100
107
  console.log(` env: pibo tools env ${entry.name}`);
101
108
  }
102
109
  export async function removeCliTool(entry) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Minimal TypeScript wrapper around Pi Coding Agent.",
6
6
  "files": [