@steipete/peekaboo 3.0.0-beta1
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.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/package.json +79 -0
- package/peekaboo +0 -0
- package/peekaboo-mcp.js +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Peter Steinberger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Peekaboo 🫣 - Mac automation that sees the screen and does the clicks.
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@steipete/peekaboo)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[-0078d7?logo=apple&logoColor=white&style=flat-square)](https://www.apple.com/macos/)
|
|
8
|
+
[](https://swift.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
[](https://github.com/steipete/peekaboo/releases/latest)
|
|
11
|
+
[](https://github.com/steipete/homebrew-tap)
|
|
12
|
+
[](https://deepwiki.com/steipete/peekaboo)
|
|
13
|
+
|
|
14
|
+
Peekaboo brings high-fidelity screen capture, AI analysis, and complete GUI automation to macOS. Version 3 adds native agent flows and multi-screen automation across the CLI and MCP server.
|
|
15
|
+
> Note: v3 is currently in beta (3.0.0-beta1) and has a few known issues; see the changelog for details.
|
|
16
|
+
|
|
17
|
+
## What you get
|
|
18
|
+
- Pixel-accurate captures (windows, screens, menu bar) with optional Retina 2x scaling.
|
|
19
|
+
- Natural-language agent that chains Peekaboo tools (see, click, type, scroll, hotkey, menu, window, app, dock, space).
|
|
20
|
+
- Menu and menubar discovery with structured JSON; no clicks required.
|
|
21
|
+
- Multi-provider AI: GPT-5.1 family, Claude 4.x, Grok 4-fast (vision), Gemini 2.5, and local Ollama models.
|
|
22
|
+
- MCP server for Claude Desktop and Cursor plus a native CLI; the same tools in both.
|
|
23
|
+
- Configurable, testable workflows with reproducible sessions and strict typing.
|
|
24
|
+
- Requires macOS Screen Recording + Accessibility permissions (see [docs/permissions.md](docs/permissions.md)).
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
- macOS app + CLI (Homebrew):
|
|
28
|
+
```bash
|
|
29
|
+
brew install steipete/tap/peekaboo
|
|
30
|
+
```
|
|
31
|
+
- MCP server (Node 22+, no global install needed):
|
|
32
|
+
```bash
|
|
33
|
+
npx -y @steipete/peekaboo
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
```bash
|
|
38
|
+
# Capture full screen at Retina scale and save to Desktop
|
|
39
|
+
peekaboo image --mode screen --retina --path ~/Desktop/screen.png
|
|
40
|
+
|
|
41
|
+
# Click a button by label (captures, resolves, and clicks in one go)
|
|
42
|
+
peekaboo see --app Safari --json-output | jq -r '.data.session_id' | read SID
|
|
43
|
+
peekaboo click --on "Reload this page" --session "$SID"
|
|
44
|
+
|
|
45
|
+
# Run a natural-language automation
|
|
46
|
+
peekaboo "Open Notes and create a TODO list with three items"
|
|
47
|
+
|
|
48
|
+
# Run as an MCP server (Claude/Cursor)
|
|
49
|
+
npx -y @steipete/peekaboo
|
|
50
|
+
|
|
51
|
+
# Minimal Claude Desktop config snippet (Developer → Edit Config):
|
|
52
|
+
# {
|
|
53
|
+
# "mcpServers": {
|
|
54
|
+
# "peekaboo": {
|
|
55
|
+
# "command": "npx",
|
|
56
|
+
# "args": ["-y", "@steipete/peekaboo"],
|
|
57
|
+
# "env": {
|
|
58
|
+
# "PEEKABOO_AI_PROVIDERS": "openai/gpt-5.1,anthropic/claude-opus-4"
|
|
59
|
+
# }
|
|
60
|
+
# }
|
|
61
|
+
# }
|
|
62
|
+
# }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Command | Key flags / subcommands | What it does |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| [see](docs/commands/see.md) | `--app`, `--mode screen/window`, `--retina`, `--json-output` | Capture and annotate UI, return session + element IDs |
|
|
68
|
+
| [click](docs/commands/click.md) | `--on <id/query>`, `--session`, `--wait`, coords | Click by element ID, label, or coordinates |
|
|
69
|
+
| [type](docs/commands/type.md) | `--text`, `--clear`, `--delay-ms` | Enter text with pacing options |
|
|
70
|
+
| [press](docs/commands/press.md) | key names, `--repeat` | Special keys and sequences |
|
|
71
|
+
| [hotkey](docs/commands/hotkey.md) | combos like `cmd,shift,t` | Modifier combos (cmd/ctrl/alt/shift) |
|
|
72
|
+
| [scroll](docs/commands/scroll.md) | `--on <id>`, `--direction up/down`, `--ticks` | Scroll views or elements |
|
|
73
|
+
| [swipe](docs/commands/swipe.md) | `--from/--to`, `--duration`, `--steps` | Smooth gesture-style drags |
|
|
74
|
+
| [drag](docs/commands/drag.md) | `--from/--to`, modifiers, Dock/Trash targets | Drag-and-drop between elements/coords |
|
|
75
|
+
| [move](docs/commands/move.md) | `--to <id/coords>`, `--screen-index` | Position the cursor without clicking |
|
|
76
|
+
| [window](docs/commands/window.md) | `list`, `move`, `resize`, `focus`, `set-bounds` | Move/resize/focus windows and Spaces |
|
|
77
|
+
| [app](docs/commands/app.md) | `launch`, `quit`, `relaunch`, `switch`, `list` | Launch, quit, relaunch, switch apps |
|
|
78
|
+
| [space](docs/commands/space.md) | `list`, `switch`, `move-window` | List or switch macOS Spaces |
|
|
79
|
+
| [menu](docs/commands/menu.md) | `list`, `list-all`, `click`, `click-extra` | List/click app menus and extras |
|
|
80
|
+
| [menubar](docs/commands/menubar.md) | `list`, `click` | Target status-bar items by name/index |
|
|
81
|
+
| [dock](docs/commands/dock.md) | `launch`, `right-click`, `hide`, `show`, `list` | Interact with Dock items |
|
|
82
|
+
| [dialog](docs/commands/dialog.md) | `list`, `click`, `input`, `file`, `dismiss` | Drive system dialogs (open/save/etc.) |
|
|
83
|
+
| [image](docs/commands/image.md) | `--mode screen/window/menu`, `--retina`, `--analyze` | Screenshot screen/window/menu bar (+analyze) |
|
|
84
|
+
| [list](docs/commands/list.md) | `apps`, `windows`, `screens`, `menubar`, `permissions` | Enumerate apps, windows, screens, permissions |
|
|
85
|
+
| [tools](docs/commands/tools.md) | `--source native\|mcp`, `--server <name>` | Inspect native + MCP tools |
|
|
86
|
+
| [config](docs/commands/config.md) | `init`, `show`, `add`, `login`, `models` | Manage credentials/providers/settings |
|
|
87
|
+
| [permissions](docs/commands/permissions.md) | `status`, `grant` | Check/grant required macOS permissions |
|
|
88
|
+
| [run](docs/commands/run.md) | `.peekaboo.json`, `--output`, `--no-fail-fast` | Execute `.peekaboo.json` automation scripts |
|
|
89
|
+
| [sleep](docs/commands/sleep.md) | `--duration` (ms) | Millisecond delays between steps |
|
|
90
|
+
| [clean](docs/commands/clean.md) | `--all-sessions`, `--older-than`, `--session` | Prune sessions and caches |
|
|
91
|
+
| [agent](docs/commands/agent.md) | `--model`, `--dry-run`, `--resume`, `--max-steps`, audio | Natural-language multi-step automation |
|
|
92
|
+
| [mcp](docs/commands/mcp.md) | `serve`, `list`, `add`, `enable/disable`, `test` | Manage external MCP servers and serve Peekaboo |
|
|
93
|
+
|
|
94
|
+
## Models and providers
|
|
95
|
+
- OpenAI: GPT-5.1 (default) and GPT-4.1/4o vision
|
|
96
|
+
- Anthropic: Claude 4.x
|
|
97
|
+
- xAI: Grok 4-fast reasoning + vision
|
|
98
|
+
- Google: Gemini 2.5 (pro/flash)
|
|
99
|
+
- Local: Ollama (llama3.3, llava, etc.)
|
|
100
|
+
|
|
101
|
+
Set providers via `PEEKABOO_AI_PROVIDERS` or `peekaboo config add`.
|
|
102
|
+
|
|
103
|
+
## Learn more
|
|
104
|
+
- Command reference: [docs/commands/](docs/commands/)
|
|
105
|
+
- Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
106
|
+
- Building from source: [docs/building.md](docs/building.md)
|
|
107
|
+
- Testing guide: [docs/testing/tools.md](docs/testing/tools.md)
|
|
108
|
+
- MCP setup: [docs/commands/mcp.md](docs/commands/mcp.md)
|
|
109
|
+
- Permissions: [docs/permissions.md](docs/permissions.md)
|
|
110
|
+
- Ollama/local models: [docs/ollama.md](docs/ollama.md)
|
|
111
|
+
- Agent chat loop: [docs/agent-chat.md](docs/agent-chat.md)
|
|
112
|
+
- Service API reference: [docs/service-api-reference.md](docs/service-api-reference.md)
|
|
113
|
+
|
|
114
|
+
## Development basics
|
|
115
|
+
- Requirements: macOS 15+, Xcode 16+/Swift 6.2. Node 22+ only if you run the pnpm docs/build helper scripts (core CLI/app/MCP are Swift-only).
|
|
116
|
+
- Install deps: `pnpm install` then `pnpm run build:cli` or `pnpm run test:safe`.
|
|
117
|
+
- Lint/format: `pnpm run lint && pnpm run format`.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@steipete/peekaboo",
|
|
3
|
+
"version": "3.0.0-beta1",
|
|
4
|
+
"description": "macOS automation MCP server with screen capture, UI interaction, and AI analysis",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "peekaboo-mcp.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"peekaboo": "peekaboo",
|
|
10
|
+
"peekaboo-mcp": "peekaboo-mcp.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"peekaboo",
|
|
14
|
+
"peekaboo-mcp.js",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build:cli": "./runner swift build --package-path Apps/CLI",
|
|
20
|
+
"build:cli:release": "./runner swift build --configuration release --package-path Apps/CLI",
|
|
21
|
+
"build:swift": "./runner ./scripts/build-swift-arm.sh",
|
|
22
|
+
"build:swift:all": "./runner ./scripts/build-swift-universal.sh",
|
|
23
|
+
"build:polter": "./runner polter peekaboo --version",
|
|
24
|
+
"build": "pnpm run build:cli",
|
|
25
|
+
"test:safe": "./runner swift test --package-path Apps/CLI -Xswiftc -DPEEKABOO_SKIP_AUTOMATION --no-parallel",
|
|
26
|
+
"test:automation": "PEEKABOO_INCLUDE_AUTOMATION_TESTS=true ./runner swift test --package-path Apps/CLI --no-parallel",
|
|
27
|
+
"test:automation:read": "RUN_AUTOMATION_READ=true PEEKABOO_INCLUDE_AUTOMATION_TESTS=true ./runner swift test --package-path Apps/CLI --no-parallel",
|
|
28
|
+
"test:automation:local": "./runner bash -lc 'cd Apps/CLI && BIN_PATH=$(swift build --show-bin-path) && RUN_LOCAL_TESTS=true PEEKABOO_INCLUDE_AUTOMATION_TESTS=true PEEKABOO_CLI_PATH=\"$BIN_PATH/peekaboo\" swift test --no-parallel'",
|
|
29
|
+
"test:all": "./runner bash -lc 'set -euo pipefail; cd Apps/CLI && swift test -Xswiftc -DPEEKABOO_SKIP_AUTOMATION --no-parallel && PEEKABOO_INCLUDE_AUTOMATION_TESTS=true swift test --no-parallel'",
|
|
30
|
+
"test": "pnpm run test:safe",
|
|
31
|
+
"tachikoma:test:integration": "./runner bash -lc 'cd Tachikoma && source ~/.profile && INTEGRATION_TESTS=1 swift test --parallel -Xswiftc -DLIVE_PROVIDER_TESTS'",
|
|
32
|
+
"lint:swift": "./runner swiftlint lint --config .swiftlint.yml",
|
|
33
|
+
"lint": "pnpm run lint:swift",
|
|
34
|
+
"format:swift": "./runner swiftformat .",
|
|
35
|
+
"format": "pnpm run format:swift",
|
|
36
|
+
"prepare-release": "./runner node scripts/prepare-release.js",
|
|
37
|
+
"docs:list": "./runner node scripts/docs-list.mjs",
|
|
38
|
+
"lint:docs": "./runner node scripts/docs-lint.mjs",
|
|
39
|
+
"polter": "FORCE_COLOR=1 CLICOLOR_FORCE=1 NODE_PATH=../poltergeist/node_modules script -q /dev/null ./runner node ../poltergeist/dist/polter.js",
|
|
40
|
+
"polter:dev": "cd /Users/steipete/Projects/Peekaboo && FORCE_COLOR=1 CLICOLOR_FORCE=1 NODE_PATH=../poltergeist/node_modules ./runner pnpm --dir ../poltergeist exec tsx ../poltergeist/src/polter.ts",
|
|
41
|
+
"peekaboo": "FORCE_COLOR=1 CLICOLOR_FORCE=1 NODE_PATH=../poltergeist/node_modules script -q /dev/null ./scripts/poltergeist-wrapper.sh peekaboo",
|
|
42
|
+
"peekaboo:dev": "pnpm run polter:dev -- peekaboo",
|
|
43
|
+
"poltergeist:start": "./runner ./scripts/poltergeist-wrapper.sh start",
|
|
44
|
+
"poltergeist:haunt": "./runner ./scripts/poltergeist-wrapper.sh haunt",
|
|
45
|
+
"poltergeist:stop": "./runner ./scripts/poltergeist-wrapper.sh stop",
|
|
46
|
+
"poltergeist:rest": "./runner ./scripts/poltergeist-wrapper.sh rest",
|
|
47
|
+
"poltergeist:status": "./runner ./scripts/poltergeist-wrapper.sh status",
|
|
48
|
+
"poltergeist:logs": "./runner ./scripts/poltergeist-wrapper.sh logs",
|
|
49
|
+
"oracle": "./runner pnpm -C ../oracle oracle",
|
|
50
|
+
"postinstall": "chmod +x peekaboo peekaboo-mcp.js 2>/dev/null || true"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/steipete/peekaboo.git"
|
|
55
|
+
},
|
|
56
|
+
"author": "Peter Steinberger <steipete@gmail.com>",
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/steipete/peekaboo/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/steipete/peekaboo#readme",
|
|
62
|
+
"keywords": [
|
|
63
|
+
"mcp",
|
|
64
|
+
"model-context-protocol",
|
|
65
|
+
"macos",
|
|
66
|
+
"automation",
|
|
67
|
+
"screen-capture",
|
|
68
|
+
"ai"
|
|
69
|
+
],
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=22.0.0"
|
|
72
|
+
},
|
|
73
|
+
"os": ["darwin"],
|
|
74
|
+
"cpu": ["x64", "arm64"],
|
|
75
|
+
"dependencies": {},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"chrome-devtools-mcp": "0.10.1"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/peekaboo
ADDED
|
Binary file
|
package/peekaboo-mcp.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Peekaboo MCP wrapper that restarts the Swift server on crash
|
|
3
|
+
|
|
4
|
+
import { spawn, execSync } from 'child_process';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const binaryPath = join(__dirname, 'peekaboo');
|
|
11
|
+
|
|
12
|
+
const MAX_RESTARTS = 5;
|
|
13
|
+
const RESTART_WINDOW_MS = 60_000;
|
|
14
|
+
const INITIAL_DELAY_MS = 1000;
|
|
15
|
+
const MAX_DELAY_MS = 30_000;
|
|
16
|
+
|
|
17
|
+
class PeekabooMCPWrapper {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.restartTimestamps = [];
|
|
20
|
+
this.delay = INITIAL_DELAY_MS;
|
|
21
|
+
this.child = null;
|
|
22
|
+
this.shuttingDown = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
start() {
|
|
26
|
+
if (!existsSync(binaryPath)) {
|
|
27
|
+
console.error(`[Peekaboo MCP] Binary not found at ${binaryPath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
this.restartTimestamps = this.restartTimestamps.filter(ts => now - ts < RESTART_WINDOW_MS);
|
|
33
|
+
if (this.restartTimestamps.length >= MAX_RESTARTS) {
|
|
34
|
+
console.error(`[Peekaboo MCP] Aborting: restarted ${MAX_RESTARTS} times within a minute.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.error('[Peekaboo MCP] Starting Swift server...');
|
|
39
|
+
this.child = spawn(binaryPath, ['mcp', 'serve'], {
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
env: {
|
|
42
|
+
...process.env,
|
|
43
|
+
PEEKABOO_MCP_WRAPPER: 'true'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.child.on('exit', (code, signal) => {
|
|
48
|
+
if (this.shuttingDown) return process.exit(code || 0);
|
|
49
|
+
|
|
50
|
+
if (code === 0 || signal === 'SIGINT' || signal === 'SIGTERM') {
|
|
51
|
+
console.error('[Peekaboo MCP] Server exited cleanly');
|
|
52
|
+
process.exit(code || 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.handleCrash(code, signal);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this.child.on('error', (err) => {
|
|
59
|
+
console.error('[Peekaboo MCP] Failed to launch:', err.message);
|
|
60
|
+
if (err.code === 'EACCES') {
|
|
61
|
+
try {
|
|
62
|
+
execSync(`chmod +x "${binaryPath}"`);
|
|
63
|
+
console.error('[Peekaboo MCP] Fixed executable bit, retrying...');
|
|
64
|
+
this.handleCrash(1);
|
|
65
|
+
return;
|
|
66
|
+
} catch (_) {
|
|
67
|
+
console.error('[Peekaboo MCP] Could not make binary executable.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.handleCrash(err.code || 1);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleCrash(code, signal) {
|
|
75
|
+
console.error(`[Peekaboo MCP] Server crashed (code ${code}${signal ? `, signal ${signal}` : ''}).`);
|
|
76
|
+
this.restartTimestamps.push(Date.now());
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
this.delay = Math.min(this.delay * 2, MAX_DELAY_MS);
|
|
79
|
+
this.start();
|
|
80
|
+
}, this.delay);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
shutdown() {
|
|
84
|
+
this.shuttingDown = true;
|
|
85
|
+
if (this.child && !this.child.killed) {
|
|
86
|
+
this.child.kill('SIGTERM');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const wrapper = new PeekabooMCPWrapper();
|
|
92
|
+
wrapper.start();
|
|
93
|
+
|
|
94
|
+
process.on('SIGINT', () => {
|
|
95
|
+
console.error('\n[Peekaboo MCP] SIGINT received, shutting down...');
|
|
96
|
+
wrapper.shutdown();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
process.on('SIGTERM', () => {
|
|
100
|
+
console.error('[Peekaboo MCP] SIGTERM received, shutting down...');
|
|
101
|
+
wrapper.shutdown();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
process.on('uncaughtException', (err) => {
|
|
105
|
+
console.error('[Peekaboo MCP] Uncaught exception:', err);
|
|
106
|
+
wrapper.shutdown();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
process.on('unhandledRejection', (reason) => {
|
|
110
|
+
console.error('[Peekaboo MCP] Unhandled rejection:', reason);
|
|
111
|
+
});
|