pixel-office-openclaw 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tridents Lab
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,146 @@
1
+ # 🏢 Pixel Office — by Tridents Lab 🔱
2
+
3
+ Watch your [OpenClaw](https://openclaw.ai) AI agents work in a pixel-art virtual office.
4
+
5
+ Each agent gets an animated character that walks around, sits at desks, and shows real-time status as they execute tasks. Click any agent to chat with them directly.
6
+
7
+ <!-- ![Pixel Office Demo](demo.gif) -->
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ npx pixel-office-openclaw
13
+ ```
14
+
15
+ That's it. Pixel Office auto-detects your local OpenClaw instance and starts a web server at `http://localhost:3002`.
16
+
17
+ > **No OpenClaw?** Try `npx pixel-office-openclaw --mock` to see it with simulated agents.
18
+
19
+ ## Features
20
+
21
+ - 🎮 **Pixel-art office** with animated characters and furniture
22
+ - 🔄 **Real-time status** — agents glow green when working, go idle when done
23
+ - 💬 **Chat** — click any agent to send them a message
24
+ - 🏗️ **Layout editor** — customize the office with desks, plants, bookshelves
25
+ - 🎨 **Role-based sprites** — coders, researchers, planners get distinct looks
26
+ - ✨ **Matrix animations** — spawn/despawn effects when agents come and go
27
+ - 📊 **Activity log** — see what your agents are doing in real-time
28
+ - 🖱️ **Interactive** — click agents to freeze them, hover for status tooltips
29
+
30
+ ## Installation
31
+
32
+ ### Global install (recommended)
33
+
34
+ ```bash
35
+ npm install -g pixel-office-openclaw
36
+ pixel-office-openclaw
37
+ ```
38
+
39
+ ### npx (no install)
40
+
41
+ ```bash
42
+ npx pixel-office-openclaw
43
+ ```
44
+
45
+ ### From source
46
+
47
+ ```bash
48
+ git clone https://github.com/neomatrix25/pixel-office.git
49
+ cd pixel-office
50
+ npm install
51
+ npm run build
52
+ npm start
53
+ ```
54
+
55
+ ## Configuration
56
+
57
+ Pixel Office auto-detects your OpenClaw setup. Override with CLI flags:
58
+
59
+ ```
60
+ pixel-office-openclaw [options]
61
+
62
+ Options:
63
+ -p, --port <number> Port (default: 3002)
64
+ --host <address> Bind address (default: 127.0.0.1)
65
+ --openclaw-home <path> OpenClaw home dir (default: ~/.openclaw)
66
+ --gateway-url <url> Gateway URL (auto-detected)
67
+ --gateway-token <token> Gateway token (auto-detected)
68
+ --no-chat Disable chat endpoint
69
+ --mock Demo mode with simulated agents
70
+ --open Open browser automatically
71
+ ```
72
+
73
+ ### Examples
74
+
75
+ ```bash
76
+ # Default — auto-detect everything
77
+ pixel-office-openclaw
78
+
79
+ # Custom port
80
+ pixel-office-openclaw --port 8080
81
+
82
+ # Expose to your network (default is localhost only)
83
+ pixel-office-openclaw --host 0.0.0.0
84
+
85
+ # Demo mode — no OpenClaw needed
86
+ pixel-office-openclaw --mock
87
+
88
+ # Remote OpenClaw instance
89
+ pixel-office --gateway-url http://myserver:18789 --gateway-token mytoken
90
+ ```
91
+
92
+ ### Remote Access
93
+
94
+ By default, Pixel Office only listens on `127.0.0.1` (localhost). To access from other machines:
95
+
96
+ ```bash
97
+ pixel-office-openclaw --host 0.0.0.0
98
+ ```
99
+
100
+ ⚠️ **Security**: When exposed to a network, the server can read your agent sessions and send messages. Only expose on trusted networks.
101
+
102
+ ## How It Works
103
+
104
+ Pixel Office runs a lightweight Express server that:
105
+
106
+ 1. **Reads agent session stores** from `~/.openclaw/agents/*/sessions/sessions.json`
107
+ 2. **Deduplicates sessions** — one character per agent, using the most recently active session
108
+ 3. **Serves a React app** that renders a pixel-art office on HTML Canvas
109
+ 4. **Polls every 3 seconds** for status updates
110
+ 5. **Proxies chat messages** to agents via the OpenClaw gateway API
111
+
112
+ No data leaves your machine. The gateway token stays server-side and is never sent to the browser.
113
+
114
+ ## Layout Editor
115
+
116
+ Click the **Edit** button (bottom toolbar) to customize your office:
117
+
118
+ - 🎨 Paint floor tiles and walls
119
+ - 🪑 Place furniture (desks, chairs, plants, bookshelves)
120
+ - ↩️ Undo/redo (Ctrl+Z / Ctrl+Y)
121
+ - 🔄 Rotate items (R key)
122
+ - 💾 Save your layout
123
+
124
+ ## Requirements
125
+
126
+ - **Node.js 18+**
127
+ - **OpenClaw** running locally (or use `--mock` for demo)
128
+
129
+ ## Tech Stack
130
+
131
+ - React 19 + TypeScript
132
+ - Canvas 2D rendering (no WebGL)
133
+ - Vite for builds
134
+ - Express 5 for the bridge server
135
+ - A* pathfinding for character navigation
136
+
137
+ ## License
138
+
139
+ MIT — see [LICENSE](LICENSE)
140
+
141
+ ## Credits
142
+
143
+ Built by [Tridents Lab](https://github.com/neomatrix25) 🔱
144
+
145
+ Character sprites based on [pixel-agents](https://github.com/pablodelucca/pixel-agents) (MIT).
146
+ Office furniture sprites by [2dPig](https://2dpig.itch.io) (CC0 Public Domain).
package/cli.js ADDED
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Pixel Office CLI — visualize OpenClaw agents in a pixel-art office.
4
+ *
5
+ * Usage:
6
+ * pixel-office [options]
7
+ *
8
+ * Options:
9
+ * -p, --port <number> Port to listen on (default: 3002)
10
+ * -h, --host <address> Bind address (default: 127.0.0.1)
11
+ * --openclaw-home <path> OpenClaw home directory (default: ~/.openclaw)
12
+ * --gateway-url <url> Gateway URL (auto-detected from config)
13
+ * --gateway-token <token> Gateway token (auto-detected from config)
14
+ * --no-chat Disable chat send endpoint
15
+ * --mock Start with mock mode (no OpenClaw needed)
16
+ * --open Open browser after starting
17
+ * -v, --version Show version
18
+ * --help Show this help
19
+ */
20
+
21
+ import { readFileSync, existsSync } from 'fs'
22
+ import { join } from 'path'
23
+ import { homedir } from 'os'
24
+ import { fileURLToPath } from 'url'
25
+ import { dirname } from 'path'
26
+ import { execSync } from 'child_process'
27
+
28
+ const __filename = fileURLToPath(import.meta.url)
29
+ const __dirname = dirname(__filename)
30
+
31
+ // ── Argument Parsing ────────────────────────────────────────────
32
+
33
+ const args = process.argv.slice(2)
34
+
35
+ function getArg(flags, defaultValue = undefined) {
36
+ for (let i = 0; i < args.length; i++) {
37
+ if (flags.includes(args[i]) && i + 1 < args.length) {
38
+ return args[i + 1]
39
+ }
40
+ }
41
+ return defaultValue
42
+ }
43
+
44
+ function hasFlag(flags) {
45
+ return args.some(a => flags.includes(a))
46
+ }
47
+
48
+ if (hasFlag(['--help'])) {
49
+ console.log(`
50
+ Pixel Office — Watch your OpenClaw agents work in a pixel-art office 🦞
51
+
52
+ Usage: pixel-office [options]
53
+
54
+ Options:
55
+ -p, --port <number> Port to listen on (default: 3002)
56
+ --host <address> Bind address (default: 127.0.0.1)
57
+ --openclaw-home <path> OpenClaw home dir (default: ~/.openclaw)
58
+ --gateway-url <url> Gateway URL (auto-detected)
59
+ --gateway-token <token> Gateway token (auto-detected)
60
+ --no-chat Disable chat/send endpoint
61
+ --mock Mock mode (no OpenClaw needed)
62
+ --no-open Don't open browser after starting
63
+ -v, --version Show version
64
+ --help Show this help
65
+
66
+ Examples:
67
+ pixel-office # Auto-detect local OpenClaw
68
+ pixel-office --port 8080 # Custom port
69
+ pixel-office --host 0.0.0.0 # Expose to network
70
+ pixel-office --mock # Demo without OpenClaw
71
+ `)
72
+ process.exit(0)
73
+ }
74
+
75
+ if (hasFlag(['-v', '--version'])) {
76
+ try {
77
+ const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf-8'))
78
+ console.log(`pixel-office v${pkg.version}`)
79
+ } catch {
80
+ console.log('pixel-office (unknown version)')
81
+ }
82
+ process.exit(0)
83
+ }
84
+
85
+ // ── Configuration ───────────────────────────────────────────────
86
+
87
+ const PORT = parseInt(getArg(['-p', '--port'], '3002'), 10)
88
+ const HOST = getArg(['--host'], '127.0.0.1')
89
+ const OPENCLAW_HOME = getArg(['--openclaw-home'], join(homedir(), '.openclaw'))
90
+ const CHAT_DISABLED = hasFlag(['--no-chat'])
91
+ const MOCK_MODE = hasFlag(['--mock'])
92
+ const OPEN_BROWSER = !hasFlag(['--no-open'])
93
+
94
+ // ── Auto-detect Gateway ─────────────────────────────────────────
95
+
96
+ function autoDetectGateway() {
97
+ const configPath = join(OPENCLAW_HOME, 'openclaw.json')
98
+ if (!existsSync(configPath)) return { url: null, token: null }
99
+
100
+ try {
101
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'))
102
+ const gw = config.gateway || {}
103
+ const port = gw.port || 18789
104
+ const token = gw.auth?.token || null
105
+ const url = `http://127.0.0.1:${port}`
106
+ return { url, token }
107
+ } catch (err) {
108
+ console.warn('[pixel-office] Could not read OpenClaw config:', err.message)
109
+ return { url: null, token: null }
110
+ }
111
+ }
112
+
113
+ let GATEWAY_URL = getArg(['--gateway-url'])
114
+ let GATEWAY_TOKEN = getArg(['--gateway-token'])
115
+
116
+ if (!GATEWAY_URL || !GATEWAY_TOKEN) {
117
+ const detected = autoDetectGateway()
118
+ if (!GATEWAY_URL) GATEWAY_URL = detected.url
119
+ if (!GATEWAY_TOKEN) GATEWAY_TOKEN = detected.token
120
+ }
121
+
122
+ // ── Export config and start server ──────────────────────────────
123
+
124
+ process.env.PORT = String(PORT)
125
+ process.env.PIXEL_OFFICE_HOST = HOST
126
+ process.env.OPENCLAW_HOME = OPENCLAW_HOME
127
+ if (GATEWAY_URL) process.env.OPENCLAW_GATEWAY_URL = GATEWAY_URL
128
+ if (GATEWAY_TOKEN) process.env.OPENCLAW_GATEWAY_TOKEN = GATEWAY_TOKEN
129
+ if (CHAT_DISABLED) process.env.PIXEL_OFFICE_NO_CHAT = '1'
130
+
131
+ // Print startup banner
132
+ console.log('')
133
+ console.log(' 🏢 Pixel Office')
134
+ console.log(' ────────────────────────────────')
135
+ if (MOCK_MODE) {
136
+ console.log(' Mode: Mock (no OpenClaw needed)')
137
+ } else {
138
+ console.log(` OpenClaw: ${OPENCLAW_HOME}`)
139
+ console.log(` Gateway: ${GATEWAY_URL || '(not detected)'}`)
140
+ console.log(` Token: ${GATEWAY_TOKEN ? '(configured)' : '(not detected)'}`)
141
+ }
142
+ console.log(` Server: http://${HOST}:${PORT}`)
143
+ console.log(` Chat: ${CHAT_DISABLED ? 'disabled' : 'enabled'}`)
144
+ console.log(' ────────────────────────────────')
145
+ console.log('')
146
+
147
+ if (!MOCK_MODE && !GATEWAY_URL) {
148
+ console.warn(' ⚠️ No OpenClaw instance detected.')
149
+ console.warn(' Make sure OpenClaw is running, or pass --gateway-url and --gateway-token.')
150
+ console.warn(' Starting anyway — you can connect via the UI.\n')
151
+ }
152
+
153
+ // Open browser after a short delay
154
+ if (OPEN_BROWSER) {
155
+ setTimeout(() => {
156
+ const url = `http://localhost:${PORT}`
157
+ try {
158
+ const platform = process.platform
159
+ if (platform === 'darwin') execSync(`open ${url}`)
160
+ else if (platform === 'win32') execSync(`start ${url}`)
161
+ else execSync(`xdg-open ${url} 2>/dev/null || sensible-browser ${url} 2>/dev/null`)
162
+ } catch { /* ignore */ }
163
+ }, 1000)
164
+ }
165
+
166
+ // Import and start the server
167
+ import('./server.js')
Binary file
@@ -0,0 +1,7 @@
1
+ This asset pack is released under the Creative Commons Public Domain Dedication License (aka CC0 or CC Zero).
2
+
3
+ CC0 is a public dedication tool, which allows creators to give up their copyright and put their works into the worldwide public domain. CC0 allows users to distribute, remix, adapt, and build upon the material in any medium or format, with no conditions.
4
+
5
+ More info here:
6
+ https://creativecommons.org/publicdomain/zero/1.0/
7
+
Binary file
@@ -0,0 +1,98 @@
1
+ {
2
+ "version": 1,
3
+ "cols": 20,
4
+ "rows": 12,
5
+ "tiles": [
6
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7
+ 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 3, 3, 3, 3, 0,
8
+ 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 3, 3, 3, 3, 0,
9
+ 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 3, 3, 3, 3, 0,
10
+ 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 3, 3, 3, 3, 0,
11
+ 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 0, 3, 3, 3, 3, 0,
12
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
13
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
14
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 0,
15
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 0,
16
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 0,
17
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
18
+ ],
19
+ "furniture": [
20
+ {"uid": "desk-1-1", "type": "desk", "col": 1, "row": 1},
21
+ {"uid": "chair-1-3", "type": "chair", "col": 2, "row": 3},
22
+ {"uid": "chair-1-4", "type": "chair", "col": 1, "row": 3},
23
+ {"uid": "pc-1-1", "type": "pc", "col": 1, "row": 1},
24
+ {"uid": "pc-1-2", "type": "pc", "col": 2, "row": 1},
25
+
26
+ {"uid": "desk-4-1", "type": "desk", "col": 4, "row": 1},
27
+ {"uid": "chair-4-3", "type": "chair", "col": 5, "row": 3},
28
+ {"uid": "chair-4-4", "type": "chair", "col": 4, "row": 3},
29
+ {"uid": "pc-4-1", "type": "pc", "col": 4, "row": 1},
30
+ {"uid": "pc-4-2", "type": "pc", "col": 5, "row": 1},
31
+
32
+ {"uid": "desk-1-4", "type": "desk", "col": 1, "row": 4},
33
+ {"uid": "chair-1-6", "type": "chair", "col": 2, "row": 6},
34
+ {"uid": "chair-1-7", "type": "chair", "col": 1, "row": 6},
35
+ {"uid": "pc-1-4", "type": "pc", "col": 1, "row": 4},
36
+
37
+ {"uid": "desk-4-4", "type": "desk", "col": 4, "row": 4},
38
+ {"uid": "chair-4-6", "type": "chair", "col": 5, "row": 6},
39
+ {"uid": "chair-4-7", "type": "chair", "col": 4, "row": 6},
40
+ {"uid": "pc-4-4", "type": "pc", "col": 4, "row": 4},
41
+
42
+ {"uid": "desk-9-1", "type": "desk", "col": 9, "row": 1},
43
+ {"uid": "chair-9-3", "type": "chair", "col": 10, "row": 3},
44
+ {"uid": "chair-9-4", "type": "chair", "col": 9, "row": 3},
45
+ {"uid": "pc-9-1", "type": "pc", "col": 9, "row": 1},
46
+ {"uid": "pc-9-2", "type": "pc", "col": 10, "row": 1},
47
+
48
+ {"uid": "desk-11-1", "type": "desk", "col": 11, "row": 1},
49
+ {"uid": "chair-11-3", "type": "chair", "col": 12, "row": 3},
50
+ {"uid": "chair-11-4", "type": "chair", "col": 11, "row": 3},
51
+ {"uid": "pc-11-1", "type": "pc", "col": 11, "row": 1},
52
+ {"uid": "pc-11-2", "type": "pc", "col": 12, "row": 1},
53
+
54
+ {"uid": "wb-15-1", "type": "whiteboard", "col": 15, "row": 1},
55
+ {"uid": "chair-15-3", "type": "chair", "col": 15, "row": 3},
56
+ {"uid": "chair-16-3", "type": "chair", "col": 16, "row": 3},
57
+ {"uid": "chair-17-3", "type": "chair", "col": 17, "row": 3},
58
+ {"uid": "chair-18-3", "type": "chair", "col": 18, "row": 3},
59
+
60
+ {"uid": "desk-1-8", "type": "desk", "col": 1, "row": 8},
61
+ {"uid": "chair-1-8s", "type": "chair", "col": 1, "row": 8},
62
+ {"uid": "chair-2-8s", "type": "chair", "col": 2, "row": 8},
63
+ {"uid": "pc-1-8", "type": "pc", "col": 1, "row": 8},
64
+
65
+ {"uid": "desk-4-8", "type": "desk", "col": 4, "row": 8},
66
+ {"uid": "chair-4-8s", "type": "chair", "col": 4, "row": 8},
67
+ {"uid": "chair-5-8s", "type": "chair", "col": 5, "row": 8},
68
+ {"uid": "pc-4-8", "type": "pc", "col": 4, "row": 8},
69
+
70
+ {"uid": "desk-12-8", "type": "desk", "col": 12, "row": 8},
71
+ {"uid": "chair-12-8s", "type": "chair", "col": 12, "row": 8},
72
+ {"uid": "chair-13-8s", "type": "chair", "col": 13, "row": 8},
73
+ {"uid": "pc-12-8", "type": "pc", "col": 12, "row": 8},
74
+
75
+ {"uid": "desk-15-8", "type": "desk", "col": 15, "row": 8},
76
+ {"uid": "chair-15-8s", "type": "chair", "col": 15, "row": 8},
77
+ {"uid": "chair-16-8s", "type": "chair", "col": 16, "row": 8},
78
+ {"uid": "pc-15-8", "type": "pc", "col": 15, "row": 8},
79
+
80
+ {"uid": "plant-7-1", "type": "plant", "col": 7, "row": 1},
81
+ {"uid": "plant-7-6", "type": "plant", "col": 7, "row": 7},
82
+ {"uid": "plant-14-6", "type": "plant", "col": 14, "row": 7},
83
+ {"uid": "plant-8-10", "type": "plant", "col": 8, "row": 10},
84
+ {"uid": "plant-11-10", "type": "plant", "col": 11, "row": 10},
85
+ {"uid": "plant-1-7", "type": "plant", "col": 1, "row": 7},
86
+
87
+ {"uid": "cooler-8-7", "type": "cooler", "col": 9, "row": 7},
88
+
89
+ {"uid": "bs-7-8", "type": "bookshelf", "col": 7, "row": 8},
90
+ {"uid": "bs-7-10", "type": "bookshelf", "col": 7, "row": 10},
91
+ {"uid": "bs-18-8", "type": "bookshelf", "col": 18, "row": 8},
92
+ {"uid": "bs-18-10", "type": "bookshelf", "col": 18, "row": 10},
93
+
94
+ {"uid": "lamp-13-1", "type": "lamp", "col": 13, "row": 2},
95
+ {"uid": "lamp-18-7", "type": "lamp", "col": 18, "row": 7},
96
+ {"uid": "lamp-6-7", "type": "lamp", "col": 6, "row": 7}
97
+ ]
98
+ }