@rmbk/compeek 0.2.0 → 0.2.3

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 (3) hide show
  1. package/README.md +94 -48
  2. package/bin/compeek.mjs +124 -57
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,75 +1,91 @@
1
1
  # compeek — AI Eyes & Hands for Any Desktop
2
2
 
3
- > A computer use agent framework powered by Claude. Define goals in natural language, point at any software, watch the agent work.
3
+ > Tell the AI what to do. Point it at any software. Watch it work.
4
+ >
5
+ > No APIs. No plugins. No integrations. Just screen and keyboard.
4
6
 
5
- **No APIs. No plugins. No integrations. Just screen and keyboard.**
7
+ [Try the Dashboard](https://compeek.rmbk.me) | [Docker Image](https://ghcr.io/uburuntu/compeek) | [npm](https://www.npmjs.com/package/@rmbk/compeek)
6
8
 
7
- [Dashboard](https://compeek.rmbk.me) | [Docker Image](https://ghcr.io/uburuntu/compeek) | [npm](https://www.npmjs.com/package/compeek)
9
+ ![compeek AI agent controlling a Linux desktop](docs/hero.png)
10
+
11
+ ## What can it do?
12
+
13
+ **compeek** (компик + peek) turns Claude into a desktop agent that can use any application — just like a person sitting at a computer.
14
+
15
+ - **See** any application through screenshots
16
+ - **Think** through complex tasks step by step (you can watch it reason)
17
+ - **Act** with mouse clicks, keyboard typing, and scrolling
18
+ - **Read** documents like passports, IDs, and invoices
19
+ - **Validate** its own work by checking what it filled in
20
+ - **Show you everything** — a real-time dashboard shows what the AI sees and thinks
8
21
 
9
22
  ## Quick Start
10
23
 
24
+ ### Option 1: One command (recommended)
25
+
26
+ ```bash
27
+ npx @rmbk/compeek start --open
28
+ ```
29
+
30
+ This downloads a virtual desktop, starts it, and opens the dashboard in your browser.
31
+
32
+ ### Option 2: Install script
33
+
11
34
  ```bash
12
- # One-line install (Linux/macOS/WSL2)
13
35
  curl -fsSL https://compeek.rmbk.me/install.sh | bash
36
+ ```
14
37
 
15
- # Or via npx
16
- npx compeek start --open
38
+ ### Option 3: Docker
17
39
 
18
- # Or docker directly
40
+ ```bash
19
41
  docker run -d -p 3001:3000 -p 6081:6080 --shm-size=512m ghcr.io/uburuntu/compeek
20
42
  ```
21
43
 
22
- The container prints a **connection string** and a **clickable dashboard link** no manual port entry needed. Check `docker logs compeek-1`.
44
+ After starting, check the terminal for a **clickable link** that connects the dashboard automatically. Or run `docker logs compeek-1` to see it.
23
45
 
24
46
  Open the [dashboard](https://compeek.rmbk.me), paste your Anthropic API key in Settings, and start a workflow.
25
47
 
26
- ## What is compeek?
48
+ ## How It Works
27
49
 
28
- **compeek** (компик + peek) turns Claude into an autonomous desktop agent. It sees any application through screenshots, interacts via mouse and keyboard, and validates its own work — all without requiring any integration with the target software.
50
+ ![Architecture: browser-native agent loop communicates with Docker containers via HTTP](docs/architecture.png)
29
51
 
30
- - **See** — screenshot any application + zoom into details
31
- - **Think** — extended thinking for transparent reasoning
32
- - **Act** — mouse clicks, keyboard input, scrolling
33
- - **Read** — extract data from document photos (passports, IDs, invoices)
34
- - **Validate** — self-check by comparing filled forms against expected data
35
- - **Observe** — real-time dashboard showing what the AI sees and thinks
52
+ The AI runs **in your browser** — it looks at the virtual desktop, decides what to do, and sends mouse/keyboard commands. The virtual desktop is just a Linux computer in a container — no AI runs inside it.
36
53
 
37
- ## Architecture
54
+ <details>
55
+ <summary>Technical architecture details</summary>
38
56
 
39
- ```
40
- Browser (React dashboard) Docker Container
41
- ┌──────────────────────┐ ┌──────────────────────┐
42
- │ Agent Loop │ │ Xvfb + Mutter │
43
- │ ├─ Anthropic API │ HTTP │ ├─ Firefox
44
- │ └─ Tool dispatch ───┼─────────┼─▸ Tool Server :3000 │
45
- │ │ │ │ └─ xdotool/scrot
46
- │ Session Manager │ │ ├─ noVNC :6080 │
47
- │ Settings (API key) │ │ └─ VNC :5900 │
48
- └──────────────────────┘ └──────────────────────┘
49
- ```
57
+ The agent loop runs in the browser via `@anthropic-ai/sdk` with `dangerouslyAllowBrowser: true`.
58
+ It uses `computer_20250124`, `bash_20250124`, and `text_editor_20250728` tools.
59
+ Extended thinking is enabled with a 10240 token budget.
60
+
61
+ Each container runs Ubuntu 24.04 with Xvfb (1280x720), XFWM4, x11vnc, noVNC, and Firefox (with uBlock Origin).
62
+ The container exposes a minimal Express tool server with endpoints:
63
+ `GET /api/health`, `GET /api/info`, `POST /api/tool`, `POST /api/bash`.
64
+
65
+ Communication is HTTP-only. No WebSocket, no state in containers.
50
66
 
51
- The agent loop runs **in the browser** — it calls the Anthropic API directly and sends mouse/keyboard commands to Docker containers via HTTP. Each container is a stateless virtual desktop with a lightweight tool server. No backend needed.
67
+ </details>
52
68
 
53
69
  ## Desktop Modes
54
70
 
55
71
  Set `DESKTOP_MODE` when starting a container:
56
72
 
57
- | Mode | What starts | Use case |
73
+ | Mode | What you get | Best for |
58
74
  |------|-------------|----------|
59
- | `full` (default) | Xvfb + Mutter + Tint2 + Firefox + target app | QA testing with pre-loaded app |
60
- | `browser` | Xvfb + Mutter + Firefox | General web browsing agent |
61
- | `minimal` | Xvfb + Mutter only | Agent launches everything itself |
62
- | `headless` | Xvfb + tool server only | API-only, bash commands only |
75
+ | `full` (default) | Desktop + browser + sample app | Testing forms and web apps |
76
+ | `browser` | Desktop + browser | General web browsing |
77
+ | `minimal` | Desktop only | Let the AI install what it needs |
78
+ | `headless` | No visual commands only | Automated scripts |
63
79
 
64
80
  ```bash
65
- npx compeek start --mode browser
81
+ npx @rmbk/compeek start --mode browser
66
82
  # or
67
83
  docker run -d -e DESKTOP_MODE=browser -p 3001:3000 -p 6081:6080 --shm-size=512m ghcr.io/uburuntu/compeek
68
84
  ```
69
85
 
70
- ## Connection Strings
86
+ ## Connecting to a Desktop
71
87
 
72
- Containers print a base64-encoded config and a dashboard link on startup:
88
+ The container prints a **connection code** and a **clickable link** when it starts:
73
89
 
74
90
  ```
75
91
  Connection string: eyJuYW1lIj...
@@ -77,23 +93,53 @@ Dashboard link: https://compeek.rmbk.me/#config=eyJuYW1lIj...
77
93
  ```
78
94
 
79
95
  Three ways to connect:
80
- 1. **Click the link** — auto-adds the session
81
- 2. **Paste the string** in the Add Session dialog
82
- 3. **Manual entry** type host and ports
96
+ 1. **Click the link** in your terminal — auto-adds the session
97
+ 2. **Paste the code** in the Add Session dialog
98
+ 3. **Type the address** manually (host + ports)
83
99
 
84
100
  ## CLI
85
101
 
86
102
  ```bash
87
- npx compeek start # Pull image, start container, print connection info
88
- npx compeek start --open # Same + open dashboard in browser
89
- npx compeek stop # Stop all compeek containers
90
- npx compeek stop 1 # Stop compeek-1
91
- npx compeek status # List running containers
92
- npx compeek logs # Follow container logs
93
- npx compeek open # Open dashboard with auto-connect URL
103
+ npx @rmbk/compeek start # Pull image, start container, print connection info
104
+ npx @rmbk/compeek start --open # Same + open dashboard in browser
105
+ npx @rmbk/compeek stop # Stop all compeek containers
106
+ npx @rmbk/compeek stop 1 # Stop compeek-1
107
+ npx @rmbk/compeek status # List running containers
108
+ npx @rmbk/compeek logs # Follow container logs
109
+ npx @rmbk/compeek open # Open dashboard with auto-connect URL
110
+ ```
111
+
112
+ Flags for `start`: `--name`, `--api-port`, `--vnc-port`, `--mode`, `--persist`, `--password`, `--tunnel`, `--no-pull`, `--open`.
113
+
114
+ | Flag | Description |
115
+ |------|-------------|
116
+ | `--open` | Open dashboard in browser after starting |
117
+ | `--mode <m>` | Desktop mode: `full`, `browser`, `minimal`, `headless` |
118
+ | `--persist` | Mount a named Docker volume so files survive container restarts |
119
+ | `--password <pw>` | Set a custom VNC password (auto-generated if omitted) |
120
+ | `--tunnel` | Create public URLs via localtunnel for remote access |
121
+
122
+ ## Security
123
+
124
+ Each container auto-generates a **VNC password** on startup. The password is included in the connection link so the dashboard connects seamlessly — you don't need to type it.
125
+
126
+ You can set your own password with `--password`:
127
+
128
+ ```bash
129
+ npx @rmbk/compeek start --password mysecret
130
+ ```
131
+
132
+ ### Remote access
133
+
134
+ If you're running compeek on the same machine as your browser, everything works locally — no tunneling needed.
135
+
136
+ To access a container from another machine (e.g. a remote server), use `--tunnel` to create public URLs:
137
+
138
+ ```bash
139
+ npx @rmbk/compeek start --tunnel
94
140
  ```
95
141
 
96
- Flags for `start`: `--name`, `--api-port`, `--vnc-port`, `--mode`, `--no-pull`, `--open`.
142
+ This uses [localtunnel](https://theboroer.github.io/localtunnel-www/) to make the container reachable over the internet. The VNC desktop is password-protected, but the tool API currently has no authentication — use a VPN or firewall for sensitive environments.
97
143
 
98
144
  ## Development
99
145
 
package/bin/compeek.mjs CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { execSync, spawn } from 'node:child_process';
7
7
  import http from 'node:http';
8
+ import crypto from 'node:crypto';
8
9
 
9
10
  const IMAGE = 'ghcr.io/uburuntu/compeek:latest';
10
11
  const CONTAINER_PREFIX = 'compeek-';
@@ -12,6 +13,22 @@ const DASHBOARD_URL = 'https://compeek.rmbk.me';
12
13
  const HEALTH_TIMEOUT = 30_000;
13
14
  const HEALTH_INTERVAL = 1_000;
14
15
 
16
+ // ── ANSI Colors ──────────────────────────────────────────
17
+
18
+ const isColorSupported = process.stdout.isTTY && !process.env.NO_COLOR;
19
+ const c = isColorSupported ? {
20
+ reset: '\x1b[0m',
21
+ bold: '\x1b[1m',
22
+ dim: '\x1b[2m',
23
+ cyan: '\x1b[36m',
24
+ green: '\x1b[32m',
25
+ yellow: '\x1b[33m',
26
+ red: '\x1b[31m',
27
+ magenta: '\x1b[35m',
28
+ white: '\x1b[97m',
29
+ gray: '\x1b[90m',
30
+ } : { reset: '', bold: '', dim: '', cyan: '', green: '', yellow: '', red: '', magenta: '', white: '', gray: '' };
31
+
15
32
  // ── Helpers ──────────────────────────────────────────────
16
33
 
17
34
  function run(cmd, opts = {}) {
@@ -70,6 +87,19 @@ function findNextName() {
70
87
  }
71
88
 
72
89
  function waitForHealth(host, port, timeout) {
90
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
91
+ let frame = 0;
92
+ let spinner;
93
+
94
+ if (isColorSupported) {
95
+ spinner = setInterval(() => {
96
+ process.stdout.write(`\r ${c.cyan}${frames[frame]}${c.reset} Waiting for container...`);
97
+ frame = (frame + 1) % frames.length;
98
+ }, 80);
99
+ } else {
100
+ console.log(' Waiting for container...');
101
+ }
102
+
73
103
  return new Promise((resolve, reject) => {
74
104
  const start = Date.now();
75
105
  const check = () => {
@@ -79,7 +109,13 @@ function waitForHealth(host, port, timeout) {
79
109
  res.on('end', () => {
80
110
  try {
81
111
  const data = JSON.parse(body);
82
- if (data.status === 'ok') return resolve(data);
112
+ if (data.status === 'ok') {
113
+ if (spinner) {
114
+ clearInterval(spinner);
115
+ process.stdout.write(`\r ${c.green}✓${c.reset} Container ready \n`);
116
+ }
117
+ return resolve(data);
118
+ }
83
119
  } catch { /* retry */ }
84
120
  retry();
85
121
  });
@@ -88,16 +124,23 @@ function waitForHealth(host, port, timeout) {
88
124
  req.on('timeout', () => { req.destroy(); retry(); });
89
125
  };
90
126
  const retry = () => {
91
- if (Date.now() - start > timeout) return reject(new Error('Health check timed out'));
127
+ if (Date.now() - start > timeout) {
128
+ if (spinner) {
129
+ clearInterval(spinner);
130
+ process.stdout.write(`\r ${c.red}✗${c.reset} Health check timed out \n`);
131
+ }
132
+ return reject(new Error('Health check timed out'));
133
+ }
92
134
  setTimeout(check, HEALTH_INTERVAL);
93
135
  };
94
136
  check();
95
137
  });
96
138
  }
97
139
 
98
- function buildConnectionString(name, apiHost, apiPort, vncHost, vncPort) {
99
- const config = JSON.stringify({ name, type: 'compeek', apiHost, apiPort, vncHost, vncPort });
100
- return Buffer.from(config).toString('base64');
140
+ function buildConnectionString(name, apiHost, apiPort, vncHost, vncPort, vncPassword) {
141
+ const config = { name, type: 'compeek', apiHost, apiPort, vncHost, vncPort };
142
+ if (vncPassword) config.vncPassword = vncPassword;
143
+ return Buffer.from(JSON.stringify(config)).toString('base64');
101
144
  }
102
145
 
103
146
  function openUrl(url) {
@@ -115,7 +158,7 @@ function openUrl(url) {
115
158
 
116
159
  async function cmdStart(args) {
117
160
  if (!hasDocker()) {
118
- console.error('Docker is not available. Install Docker first: https://docs.docker.com/get-docker/');
161
+ console.error(`${c.red}Docker is not available.${c.reset} Install Docker first: https://docs.docker.com/get-docker/`);
119
162
  process.exit(1);
120
163
  }
121
164
 
@@ -125,18 +168,32 @@ async function cmdStart(args) {
125
168
  const apiPort = parseInt(flags['api-port']) || defaultApi;
126
169
  const vncPort = parseInt(flags['vnc-port']) || defaultVnc;
127
170
  const mode = flags.mode || 'full';
171
+ const vncPassword = flags.password || crypto.randomBytes(6).toString('base64url').slice(0, 8);
128
172
  const sessionName = name.replace(CONTAINER_PREFIX, '').replace(/^(\d+)$/, 'Desktop $1');
129
173
 
174
+ console.log('');
175
+ console.log(` ${c.bold}${c.cyan}compeek${c.reset}`);
176
+ console.log('');
177
+
130
178
  if (!flags['no-pull']) {
131
- console.log(`Pulling ${IMAGE}...`);
179
+ console.log(` ${c.dim}Pulling image...${c.reset}`);
132
180
  try {
133
181
  run(`docker pull ${IMAGE}`, { stdio: 'inherit' });
134
182
  } catch {
135
- console.log('Pull failed, using cached image if available.');
183
+ console.log(` ${c.yellow}Pull failed, using cached image.${c.reset}`);
136
184
  }
185
+ console.log('');
137
186
  }
138
187
 
139
- console.log(`Starting ${name} (api:${apiPort}, vnc:${vncPort}, mode:${mode})...`);
188
+ const info = [
189
+ `mode:${c.white}${mode}${c.reset}`,
190
+ `api:${c.white}${apiPort}${c.reset}`,
191
+ `vnc:${c.white}${vncPort}${c.reset}`,
192
+ ];
193
+ if (flags.persist) info.push(`${c.green}persist${c.reset}`);
194
+ if (flags.tunnel) info.push(`${c.yellow}tunnel${c.reset}`);
195
+
196
+ console.log(` ${c.cyan}▸${c.reset} Starting ${c.bold}${name}${c.reset} ${c.dim}${info.join(' · ')}${c.reset}`);
140
197
 
141
198
  // Remove existing container with same name if stopped
142
199
  run(`docker rm -f ${name}`, { allowFail: true });
@@ -149,41 +206,40 @@ async function cmdStart(args) {
149
206
  `--shm-size=512m`,
150
207
  `-e DISPLAY=:1`,
151
208
  `-e DESKTOP_MODE=${mode}`,
152
- `-e COMPEEK_SESSION_NAME=${sessionName}`,
209
+ `-e COMPEEK_SESSION_NAME="${sessionName}"`,
210
+ `-e VNC_PASSWORD="${vncPassword}"`,
211
+ flags.tunnel ? '-e ENABLE_TUNNEL=true' : '',
212
+ flags.persist ? `-v ${name}-data:/home/compeek/data` : '',
153
213
  `--security-opt seccomp=unconfined`,
154
214
  IMAGE,
155
- ].join(' '));
156
-
157
- console.log('Waiting for container to be ready...');
215
+ ].filter(Boolean).join(' '));
158
216
 
159
217
  try {
160
218
  await waitForHealth('localhost', apiPort, HEALTH_TIMEOUT);
161
- console.log('Container is ready.');
162
219
  } catch {
163
- console.error('Container did not become healthy. Check logs: npx compeek logs');
220
+ console.error(`\n ${c.red}Container did not start.${c.reset} Check logs: npx compeek logs`);
164
221
  process.exit(1);
165
222
  }
166
223
 
167
- const connStr = buildConnectionString(sessionName, 'localhost', apiPort, 'localhost', vncPort);
224
+ const connStr = buildConnectionString(sessionName, 'localhost', apiPort, 'localhost', vncPort, vncPassword);
225
+ const dashboardLink = `${DASHBOARD_URL}/#config=${connStr}`;
168
226
 
169
227
  console.log('');
170
- console.log('=========================================');
171
- console.log(` ${name}`);
172
- console.log('=========================================');
173
- console.log(` Tool API : http://localhost:${apiPort}`);
228
+ console.log(` ${c.dim}──── Links ─────────────────────────────────────${c.reset}`);
229
+ console.log('');
230
+ console.log(` ${c.bold}Dashboard${c.reset} ${c.cyan}${dashboardLink}${c.reset}`);
231
+ console.log(` ${c.dim}Tool API${c.reset} http://localhost:${apiPort}`);
174
232
  if (mode !== 'headless') {
175
- console.log(` noVNC : http://localhost:${vncPort}`);
233
+ console.log(` ${c.dim}noVNC${c.reset} http://localhost:${vncPort}`);
234
+ console.log(` ${c.dim}Password${c.reset} ${vncPassword}`);
176
235
  }
177
236
  console.log('');
178
- console.log(' Connection string:');
179
- console.log(` ${connStr}`);
237
+ console.log(` ${c.dim}──── Connection string ──────────────────────────${c.reset}`);
238
+ console.log(` ${c.dim}${connStr}${c.reset}`);
180
239
  console.log('');
181
- console.log(' Dashboard link:');
182
- console.log(` ${DASHBOARD_URL}/#config=${connStr}`);
183
- console.log('=========================================');
184
240
 
185
241
  if (flags.open) {
186
- openUrl(`${DASHBOARD_URL}/#config=${connStr}`);
242
+ openUrl(dashboardLink);
187
243
  }
188
244
  }
189
245
 
@@ -191,33 +247,41 @@ function cmdStop(args) {
191
247
  const target = args[0];
192
248
  if (target) {
193
249
  const name = target.startsWith(CONTAINER_PREFIX) ? target : `${CONTAINER_PREFIX}${target}`;
194
- console.log(`Stopping ${name}...`);
250
+ console.log(` ${c.cyan}▸${c.reset} Stopping ${c.bold}${name}${c.reset}...`);
195
251
  run(`docker rm -f ${name}`, { allowFail: true, stdio: 'inherit' });
252
+ console.log(` ${c.green}✓${c.reset} Stopped`);
196
253
  } else {
197
254
  const containers = listContainers();
198
255
  if (containers.length === 0) {
199
- console.log('No compeek containers running.');
256
+ console.log(` ${c.dim}No compeek containers running.${c.reset}`);
200
257
  return;
201
258
  }
202
- for (const c of containers) {
203
- console.log(`Stopping ${c.name}...`);
204
- run(`docker rm -f ${c.name}`, { allowFail: true });
259
+ for (const ctr of containers) {
260
+ console.log(` ${c.cyan}▸${c.reset} Stopping ${c.bold}${ctr.name}${c.reset}...`);
261
+ run(`docker rm -f ${ctr.name}`, { allowFail: true });
205
262
  }
206
- console.log(`Stopped ${containers.length} container(s).`);
263
+ console.log(` ${c.green}✓${c.reset} Stopped ${containers.length} container(s)`);
207
264
  }
208
265
  }
209
266
 
210
267
  function cmdStatus() {
211
268
  const containers = listContainers();
212
269
  if (containers.length === 0) {
213
- console.log('No compeek containers found.');
270
+ console.log(` ${c.dim}No compeek containers found.${c.reset}`);
214
271
  return;
215
272
  }
216
- console.log('NAME\t\t\tSTATUS\t\t\t\tPORTS');
217
- console.log('─'.repeat(80));
218
- for (const c of containers) {
219
- console.log(`${c.name}\t\t${c.status}\t\t${c.ports}`);
273
+ console.log('');
274
+ for (const ctr of containers) {
275
+ const isUp = ctr.status.startsWith('Up');
276
+ const dot = isUp ? `${c.green}●${c.reset}` : `${c.red}●${c.reset}`;
277
+ const statusText = isUp ? `${c.green}${ctr.status}${c.reset}` : `${c.dim}${ctr.status}${c.reset}`;
278
+ console.log(` ${dot} ${c.bold}${ctr.name}${c.reset} ${statusText}`);
279
+ if (ctr.ports) {
280
+ const portList = ctr.ports.replace(/0\.0\.0\.0:/g, ':').replace(/:::(\d+)/g, ':$1');
281
+ console.log(` ${c.dim}${portList}${c.reset}`);
282
+ }
220
283
  }
284
+ console.log('');
221
285
  }
222
286
 
223
287
  function cmdLogs(args) {
@@ -284,7 +348,7 @@ function parseFlags(args) {
284
348
  const arg = args[i];
285
349
  if (arg.startsWith('--')) {
286
350
  const key = arg.slice(2);
287
- if (key === 'open' || key === 'no-pull') {
351
+ if (key === 'open' || key === 'no-pull' || key === 'persist' || key === 'tunnel') {
288
352
  flags[key] = true;
289
353
  } else if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
290
354
  flags[key] = args[++i];
@@ -318,24 +382,27 @@ switch (command) {
318
382
  case '-h':
319
383
  case 'help':
320
384
  console.log(`
321
- compeek — AI desktop agent
322
-
323
- Usage: npx compeek [command] [options]
324
-
325
- Commands:
326
- start Start a new container (default)
327
- stop [name] Stop one or all compeek containers
328
- status List running containers
329
- logs [name] Follow container logs
330
- open [name] Open dashboard with auto-connect URL
331
-
332
- Start options:
333
- --name <n> Container name (default: compeek-N)
334
- --api-port <p> Host port for tool API (default: auto)
335
- --vnc-port <p> Host port for noVNC (default: auto)
336
- --mode <m> Desktop mode: full|browser|minimal|headless
337
- --no-pull Skip docker pull
338
- --open Open dashboard after start
385
+ ${c.bold}${c.cyan}compeek${c.reset} ${c.dim}— AI eyes & hands for any desktop${c.reset}
386
+
387
+ ${c.bold}Usage${c.reset} npx @rmbk/compeek ${c.dim}[command] [options]${c.reset}
388
+
389
+ ${c.bold}Commands${c.reset}
390
+ start ${c.dim}............${c.reset} Start a new virtual desktop ${c.dim}(default)${c.reset}
391
+ stop ${c.dim}[name]${c.reset} ${c.dim}....${c.reset} Stop one or all containers
392
+ status ${c.dim}...........${c.reset} List running containers
393
+ logs ${c.dim}[name]${c.reset} ${c.dim}....${c.reset} Follow container logs
394
+ open ${c.dim}[name]${c.reset} ${c.dim}....${c.reset} Open dashboard in browser
395
+
396
+ ${c.bold}Options${c.reset}
397
+ --open ${c.dim}..........${c.reset} Open dashboard after start
398
+ --mode ${c.dim}<m>${c.reset} ${c.dim}......${c.reset} full ${c.dim}|${c.reset} browser ${c.dim}|${c.reset} minimal ${c.dim}|${c.reset} headless
399
+ --persist ${c.dim}.......${c.reset} Mount volume for persistent data
400
+ --password ${c.dim}<pw>${c.reset} ${c.dim}.${c.reset} Custom VNC password ${c.dim}(auto-generated if omitted)${c.reset}
401
+ --tunnel ${c.dim}........${c.reset} Enable localtunnel for remote access
402
+ --no-pull ${c.dim}.......${c.reset} Skip pulling latest Docker image
403
+ --name ${c.dim}<n>${c.reset} ${c.dim}......${c.reset} Custom container name
404
+ --api-port ${c.dim}<p>${c.reset} ${c.dim}.${c.reset} Host port for tool API
405
+ --vnc-port ${c.dim}<p>${c.reset} ${c.dim}.${c.reset} Host port for noVNC
339
406
  `);
340
407
  break;
341
408
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmbk/compeek",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "AI eyes and hands for any desktop application — a general-purpose computer use agent framework powered by Claude Opus 4.6",
5
5
  "license": "MIT",
6
6
  "author": "rmbk",