opencode-pixel-office 1.0.9 → 1.0.10
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/README.md +3 -3
- package/bin/claude-code-hook.js +14 -12
- package/bin/opencode-pixel-office.js +103 -25
- package/client/dist/assets/{index-BYMX3DUt.js → index-Cfnbdbzw.js} +73 -73
- package/client/dist/index.html +1 -1
- package/package.json +1 -1
- package/plugin/pixel-office.js +14 -143
- package/server/index.ts +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The system consists of three main parts:
|
|
|
15
15
|
|
|
16
16
|
```mermaid
|
|
17
17
|
graph TD
|
|
18
|
-
A[OpenCode IDE] -->|Plugin Events via HTTP| B(Pixel Office Server :
|
|
18
|
+
A[OpenCode IDE] -->|Plugin Events via HTTP| B(Pixel Office Server :5100)
|
|
19
19
|
B -->|Broadcast State via WebSocket| C[React Client]
|
|
20
20
|
C -->|Render| D[PixiJS Scene]
|
|
21
21
|
C -->|Render| E[HUD / Sidebar]
|
|
@@ -92,7 +92,7 @@ Monitor your agents from your phone or tablet!
|
|
|
92
92
|
This sets up the standalone app in `~/.opencode/pixel-office` and installs the `pixel-office.js` plugin script to `~/.opencode/plugins/`.
|
|
93
93
|
|
|
94
94
|
3. **Start OpenCode**:
|
|
95
|
-
Simply open your IDE. Pixel Office will auto-launch in your browser at `http://localhost:
|
|
95
|
+
Simply open your IDE. Pixel Office will auto-launch in your browser at `http://localhost:5100`.
|
|
96
96
|
|
|
97
97
|
### CLI Commands
|
|
98
98
|
|
|
@@ -114,7 +114,7 @@ npm install
|
|
|
114
114
|
#### 2. Start the Server (Dev Mode)
|
|
115
115
|
```bash
|
|
116
116
|
npm start
|
|
117
|
-
# Server runs on http://localhost:
|
|
117
|
+
# Server runs on http://localhost:5100, watching for changes
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
#### 3. Start the Client (Dev Mode)
|
package/bin/claude-code-hook.js
CHANGED
|
@@ -48,15 +48,17 @@ const mapHookToEvent = (input) => {
|
|
|
48
48
|
|
|
49
49
|
const info = {
|
|
50
50
|
id: sessionId || `claude-${Date.now()}`,
|
|
51
|
-
sessionID: sessionId
|
|
52
|
-
|
|
51
|
+
sessionID: sessionId,
|
|
52
|
+
agent: "Claude",
|
|
53
|
+
title: cwd || "Claude Code",
|
|
54
|
+
model: { modelID: "claude", providerID: "anthropic" },
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
switch (hook) {
|
|
56
58
|
case "SessionStart":
|
|
57
59
|
return {
|
|
58
60
|
type: "session.created",
|
|
59
|
-
properties: { info
|
|
61
|
+
properties: { info },
|
|
60
62
|
};
|
|
61
63
|
case "SessionEnd":
|
|
62
64
|
return {
|
|
@@ -67,35 +69,35 @@ const mapHookToEvent = (input) => {
|
|
|
67
69
|
return {
|
|
68
70
|
type: "message.updated",
|
|
69
71
|
properties: {
|
|
70
|
-
info,
|
|
71
|
-
message: { content: prompt },
|
|
72
|
+
info: { ...info, role: "user" },
|
|
73
|
+
message: { content: prompt, role: "user" },
|
|
72
74
|
},
|
|
73
75
|
};
|
|
74
76
|
case "PreToolUse":
|
|
75
77
|
return {
|
|
76
78
|
type: "tool.execute.before",
|
|
77
|
-
properties: { tool: { name: toolName, input: toolInput } },
|
|
79
|
+
properties: { info, tool: { name: toolName, input: toolInput } },
|
|
78
80
|
};
|
|
79
81
|
case "PostToolUse":
|
|
80
82
|
case "PostToolUseFailure":
|
|
81
83
|
return {
|
|
82
84
|
type: "tool.execute.after",
|
|
83
|
-
properties: { tool: { name: toolName, input: toolInput } },
|
|
85
|
+
properties: { info, tool: { name: toolName, input: toolInput } },
|
|
84
86
|
};
|
|
85
87
|
case "PermissionRequest":
|
|
86
88
|
return {
|
|
87
89
|
type: "permission.asked",
|
|
88
|
-
properties: { permission },
|
|
90
|
+
properties: { info, permission },
|
|
89
91
|
};
|
|
90
92
|
case "Notification":
|
|
91
93
|
return {
|
|
92
94
|
type: "tui.toast.show",
|
|
93
|
-
properties: { message: input.message || "" },
|
|
95
|
+
properties: { info, message: input.message || "" },
|
|
94
96
|
};
|
|
95
97
|
case "PreCompact":
|
|
96
98
|
return {
|
|
97
99
|
type: "session.compacted",
|
|
98
|
-
properties: { info
|
|
100
|
+
properties: { info },
|
|
99
101
|
};
|
|
100
102
|
case "Stop":
|
|
101
103
|
return {
|
|
@@ -106,7 +108,7 @@ const mapHookToEvent = (input) => {
|
|
|
106
108
|
case "SubagentStop":
|
|
107
109
|
return {
|
|
108
110
|
type: "session.updated",
|
|
109
|
-
properties: { info
|
|
111
|
+
properties: { info },
|
|
110
112
|
};
|
|
111
113
|
default:
|
|
112
114
|
return null;
|
|
@@ -119,7 +121,7 @@ const main = async () => {
|
|
|
119
121
|
process.exit(0);
|
|
120
122
|
}
|
|
121
123
|
const input = JSON.parse(raw);
|
|
122
|
-
const endpoint = process.env.PIXEL_OFFICE_URL || "http://localhost:
|
|
124
|
+
const endpoint = process.env.PIXEL_OFFICE_URL || "http://localhost:5100/events";
|
|
123
125
|
if (readMode() !== "claude-code") {
|
|
124
126
|
process.exit(0);
|
|
125
127
|
}
|
|
@@ -23,11 +23,22 @@ const shouldSwitch = args[0] === "switch";
|
|
|
23
23
|
const switchTarget = shouldSwitch ? args[1] : null;
|
|
24
24
|
const shouldUninstall = args.includes("uninstall");
|
|
25
25
|
const shouldStart = args[0] === "start";
|
|
26
|
+
const shouldVersion = args.includes("--version") || args.includes("-v");
|
|
26
27
|
const yesFlag = args.includes("--yes") || args.includes("-y");
|
|
27
28
|
const skipJson = args.includes("--no-json");
|
|
28
29
|
const portIndex = args.findIndex((arg) => arg === "--port");
|
|
29
30
|
const portArg = portIndex !== -1 ? args[portIndex + 1] : null;
|
|
30
31
|
|
|
32
|
+
const getVersion = () => {
|
|
33
|
+
try {
|
|
34
|
+
const pkgPath = path.resolve(__dirname, "..", "package.json");
|
|
35
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
36
|
+
return pkg.version || "unknown";
|
|
37
|
+
} catch {
|
|
38
|
+
return "unknown";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
31
42
|
const printHelp = () => {
|
|
32
43
|
console.log("opencode-pixel-office installer\n");
|
|
33
44
|
console.log("Usage:");
|
|
@@ -38,9 +49,10 @@ const printHelp = () => {
|
|
|
38
49
|
console.log(" opencode-pixel-office uninstall");
|
|
39
50
|
console.log(" opencode-pixel-office stop");
|
|
40
51
|
console.log("\nOptions:");
|
|
41
|
-
console.log(" --port <number> Configure the server port (default:
|
|
52
|
+
console.log(" --port <number> Configure the server port (default: 5100)");
|
|
42
53
|
console.log(" --no-json Skip updating opencode.json");
|
|
43
54
|
console.log(" --yes, -y Overwrite without prompting");
|
|
55
|
+
console.log(" --version, -v Show version number");
|
|
44
56
|
};
|
|
45
57
|
|
|
46
58
|
const installClaudeCodeHooks = () => {
|
|
@@ -131,7 +143,33 @@ const copyRecursiveSync = (src, dest) => {
|
|
|
131
143
|
}
|
|
132
144
|
};
|
|
133
145
|
|
|
146
|
+
const stopServer = (port) => {
|
|
147
|
+
try {
|
|
148
|
+
const pidOutput = execSync(`lsof -t -i :${port} 2>/dev/null`).toString().trim();
|
|
149
|
+
if (pidOutput) {
|
|
150
|
+
const pids = pidOutput.split('\n').map(p => parseInt(p, 10)).filter(p => !isNaN(p) && p > 0);
|
|
151
|
+
for (const pid of pids) {
|
|
152
|
+
try {
|
|
153
|
+
process.kill(pid);
|
|
154
|
+
console.log(`✓ Stopped server (PID: ${pid})`);
|
|
155
|
+
} catch {
|
|
156
|
+
// Process may have already exited
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return pids.length > 0;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// lsof failed, no process found
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
};
|
|
166
|
+
|
|
134
167
|
const run = async () => {
|
|
168
|
+
if (shouldVersion) {
|
|
169
|
+
console.log(`opencode-pixel-office v${getVersion()}`);
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
135
173
|
if (shouldSwitch) {
|
|
136
174
|
if (switchTarget !== "opencode" && switchTarget !== "claude-code") {
|
|
137
175
|
console.error("Switch target must be 'opencode' or 'claude-code'.");
|
|
@@ -150,6 +188,14 @@ const run = async () => {
|
|
|
150
188
|
}
|
|
151
189
|
|
|
152
190
|
if (shouldUninstall) {
|
|
191
|
+
// Stop server first
|
|
192
|
+
const config = loadConfig();
|
|
193
|
+
const port = config.port || 5100;
|
|
194
|
+
console.log("Stopping Pixel Office server...");
|
|
195
|
+
if (!stopServer(port)) {
|
|
196
|
+
console.log("- Server not running");
|
|
197
|
+
}
|
|
198
|
+
|
|
153
199
|
const targetPluginPath = path.join(DEFAULT_PLUGIN_DIR, PLUGIN_NAME);
|
|
154
200
|
|
|
155
201
|
// Remove Plugin
|
|
@@ -168,6 +214,56 @@ const run = async () => {
|
|
|
168
214
|
console.log(`- App directory not found at ${DEFAULT_APP_DIR}`);
|
|
169
215
|
}
|
|
170
216
|
|
|
217
|
+
// Remove Claude Code hooks
|
|
218
|
+
const claudeDir = path.join(os.homedir(), ".claude");
|
|
219
|
+
const hookDir = path.join(claudeDir, "hooks");
|
|
220
|
+
const hookFile = path.join(hookDir, "opencode-pixel-office-hook.js");
|
|
221
|
+
const settingsPath = path.join(claudeDir, "settings.json");
|
|
222
|
+
|
|
223
|
+
if (fs.existsSync(hookFile)) {
|
|
224
|
+
fs.unlinkSync(hookFile);
|
|
225
|
+
console.log(`✓ Removed Claude Code hook: ${hookFile}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (fs.existsSync(settingsPath)) {
|
|
229
|
+
try {
|
|
230
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
231
|
+
if (settings.hooks) {
|
|
232
|
+
let modified = false;
|
|
233
|
+
for (const eventName of Object.keys(settings.hooks)) {
|
|
234
|
+
const hooks = settings.hooks[eventName];
|
|
235
|
+
if (Array.isArray(hooks)) {
|
|
236
|
+
const filtered = hooks.filter(h => {
|
|
237
|
+
if (Array.isArray(h.hooks)) {
|
|
238
|
+
h.hooks = h.hooks.filter(inner =>
|
|
239
|
+
!inner.command?.includes("opencode-pixel-office-hook")
|
|
240
|
+
);
|
|
241
|
+
return h.hooks.length > 0;
|
|
242
|
+
}
|
|
243
|
+
return !h.command?.includes("opencode-pixel-office-hook");
|
|
244
|
+
});
|
|
245
|
+
if (filtered.length !== hooks.length) {
|
|
246
|
+
settings.hooks[eventName] = filtered;
|
|
247
|
+
modified = true;
|
|
248
|
+
}
|
|
249
|
+
if (filtered.length === 0) {
|
|
250
|
+
delete settings.hooks[eventName];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
255
|
+
delete settings.hooks;
|
|
256
|
+
}
|
|
257
|
+
if (modified) {
|
|
258
|
+
fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
|
|
259
|
+
console.log(`✓ Removed hooks from Claude Code settings`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
// ignore settings parse errors
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
171
267
|
// Clean up Config (Legacy)
|
|
172
268
|
if (fs.existsSync(DEFAULT_CONFIG_PATH) && !skipJson) {
|
|
173
269
|
try {
|
|
@@ -192,7 +288,7 @@ const run = async () => {
|
|
|
192
288
|
|
|
193
289
|
if (shouldStart) {
|
|
194
290
|
const config = loadConfig();
|
|
195
|
-
const port = portArg ? parseInt(portArg, 10) : (config.port ||
|
|
291
|
+
const port = portArg ? parseInt(portArg, 10) : (config.port || 5100);
|
|
196
292
|
const serverScript = "server/index.ts";
|
|
197
293
|
|
|
198
294
|
const rootSource = path.resolve(__dirname, "..");
|
|
@@ -252,29 +348,10 @@ const run = async () => {
|
|
|
252
348
|
|
|
253
349
|
if (args.includes("stop")) {
|
|
254
350
|
const config = loadConfig();
|
|
255
|
-
const port = config.port ||
|
|
256
|
-
|
|
351
|
+
const port = config.port || 5100;
|
|
257
352
|
console.log(`Stopping Pixel Office server on port ${port}...`);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (pidOutput) {
|
|
261
|
-
const pid = parseInt(pidOutput, 10);
|
|
262
|
-
if (!isNaN(pid) && pid > 0) {
|
|
263
|
-
process.kill(pid);
|
|
264
|
-
console.log(`✓ Server stopped (PID: ${pid})`);
|
|
265
|
-
} else {
|
|
266
|
-
console.log(`- No server found on port ${port} (PID: ${pidOutput})`);
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
console.log(`- No server found on port ${port}`);
|
|
270
|
-
}
|
|
271
|
-
} catch (e) {
|
|
272
|
-
if (e.message.includes("Command failed")) {
|
|
273
|
-
// likely lsof failed meaning no process found
|
|
274
|
-
console.log(`- No server found on port ${port}`);
|
|
275
|
-
} else {
|
|
276
|
-
console.error(`! Failed to stop server: ${e.message}`);
|
|
277
|
-
}
|
|
353
|
+
if (!stopServer(port)) {
|
|
354
|
+
console.log(`- No server found on port ${port}`);
|
|
278
355
|
}
|
|
279
356
|
process.exit(0);
|
|
280
357
|
}
|
|
@@ -360,7 +437,8 @@ const run = async () => {
|
|
|
360
437
|
|
|
361
438
|
console.log(`✓ Standalone app installed to ${DEFAULT_APP_DIR}`);
|
|
362
439
|
|
|
363
|
-
console.log("\
|
|
440
|
+
console.log("\nInstallation complete!");
|
|
441
|
+
console.log("Run 'opencode-pixel-office start' to launch the server.");
|
|
364
442
|
};
|
|
365
443
|
|
|
366
444
|
run().catch((error) => {
|