@poolzin/poolbot-office 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/README.md +161 -0
- package/bin/poolbot-office-config.js +150 -0
- package/bin/poolbot-office-server.js +209 -0
- package/bin/poolbot-office.js +37 -0
- package/package.json +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# PoolBot Office
|
|
2
|
+
|
|
3
|
+
> Visual monitoring & management frontend for PoolBot Multi-Agent system
|
|
4
|
+
|
|
5
|
+
**Version:** 0.1.0
|
|
6
|
+
**Status:** 🚧 In Development
|
|
7
|
+
**Target Release:** v1.0.0 (Q2 2026)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🎯 Overview
|
|
12
|
+
|
|
13
|
+
PoolBot Office is a comprehensive visual dashboard for monitoring and managing your PoolBot Multi-Agent system. It features:
|
|
14
|
+
|
|
15
|
+
- **2D Office View** - Isometric floor plan with agent desks and collaboration visualization
|
|
16
|
+
- **3D Office View** - Full 3D scene with animated characters and holographic displays
|
|
17
|
+
- **Chat Interface** - Real-time chat with agents, streaming support, and message history
|
|
18
|
+
- **Console Dashboard** - Complete management for agents, channels, skills, and cron jobs
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install dependencies
|
|
26
|
+
cd apps/office
|
|
27
|
+
pnpm install
|
|
28
|
+
|
|
29
|
+
# Development mode
|
|
30
|
+
pnpm dev
|
|
31
|
+
|
|
32
|
+
# Build for production
|
|
33
|
+
pnpm build
|
|
34
|
+
|
|
35
|
+
# Start production server
|
|
36
|
+
pnpm start
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📋 Features
|
|
42
|
+
|
|
43
|
+
### Virtual Office
|
|
44
|
+
- [x] 2D isometric floor plan
|
|
45
|
+
- [x] 3D scene with React Three Fiber
|
|
46
|
+
- [x] Agent avatars with status animations
|
|
47
|
+
- [x] Collaboration lines (message flow visualization)
|
|
48
|
+
- [x] Furniture (desks, chairs, plants)
|
|
49
|
+
- [x] Spawn/despawn effects
|
|
50
|
+
|
|
51
|
+
### Chat Interface
|
|
52
|
+
- [x] Bottom-docked chat bar
|
|
53
|
+
- [x] Real-time message streaming
|
|
54
|
+
- [x] Agent selector with status
|
|
55
|
+
- [x] Message history with search
|
|
56
|
+
- [x] Copy message functionality
|
|
57
|
+
- [x] Typing indicators
|
|
58
|
+
|
|
59
|
+
### Console Dashboard
|
|
60
|
+
- [x] Overview stats (agents, channels, skills, cron)
|
|
61
|
+
- [x] Alert banners
|
|
62
|
+
- [x] Quick actions
|
|
63
|
+
- [x] Agent management (CRUD)
|
|
64
|
+
- [x] Channel management (connect/disconnect)
|
|
65
|
+
- [x] Skills marketplace (install/uninstall)
|
|
66
|
+
- [x] Cron job management (run/pause/delete)
|
|
67
|
+
- [x] Settings (gateway, theme, language)
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 🛠️ Tech Stack
|
|
72
|
+
|
|
73
|
+
| Layer | Technology |
|
|
74
|
+
|-------|------------|
|
|
75
|
+
| Build Tool | Vite 6 |
|
|
76
|
+
| UI Framework | React 19 |
|
|
77
|
+
| 2D Rendering | SVG + CSS |
|
|
78
|
+
| 3D Rendering | React Three Fiber + Drei |
|
|
79
|
+
| State Management | React useState/useEffect |
|
|
80
|
+
| Styling | CSS + Inline styles |
|
|
81
|
+
| Language | TypeScript 5 |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 📁 Project Structure
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
apps/office/
|
|
89
|
+
├── bin/ # CLI executables
|
|
90
|
+
│ ├── poolbot-office.js
|
|
91
|
+
│ ├── poolbot-office-config.js
|
|
92
|
+
│ └── poolbot-office-server.js
|
|
93
|
+
├── src/
|
|
94
|
+
│ ├── components/
|
|
95
|
+
│ │ ├── chat/ # Chat interface components
|
|
96
|
+
│ │ ├── console/ # Console dashboard pages
|
|
97
|
+
│ │ ├── office-2d/ # 2D office components
|
|
98
|
+
│ │ └── office-3d/ # 3D office components
|
|
99
|
+
│ ├── gateway/ # WebSocket client
|
|
100
|
+
│ ├── styles/ # Global styles
|
|
101
|
+
│ ├── App.tsx # Main app component
|
|
102
|
+
│ └── main.tsx # Entry point
|
|
103
|
+
├── public/ # Static assets
|
|
104
|
+
├── index.html # HTML template
|
|
105
|
+
├── package.json
|
|
106
|
+
├── tsconfig.json
|
|
107
|
+
└── vite.config.ts
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🔧 CLI Options
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
poolbot-office [options]
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
-t, --token <token> Gateway auth token (auto-detected)
|
|
119
|
+
-g, --gateway <url> Gateway WebSocket URL
|
|
120
|
+
-p, --port <port> Server port (default: 5180)
|
|
121
|
+
--host <host> Bind address (default: 0.0.0.0)
|
|
122
|
+
-h, --help Show help
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 📊 Progress
|
|
128
|
+
|
|
129
|
+
**Current Status:** 60% Complete (9/15 phases)
|
|
130
|
+
|
|
131
|
+
### Completed Phases
|
|
132
|
+
- [x] Phase 1: Setup do Projeto
|
|
133
|
+
- [x] Phase 2: CLI e Servidor
|
|
134
|
+
- [x] Phase 3: Gateway WebSocket Client
|
|
135
|
+
- [x] Phase 4: 2D Office Layout
|
|
136
|
+
- [x] Phase 5: 3D Office Layout
|
|
137
|
+
- [x] Phase 6: Chat Interface
|
|
138
|
+
- [x] Phase 7: Console Dashboard
|
|
139
|
+
- [x] Phase 8: Polish & Integration
|
|
140
|
+
- [x] Phase 9: Documentation
|
|
141
|
+
|
|
142
|
+
### Remaining Phases
|
|
143
|
+
- [ ] Phase 10: Testing (Unit/E2E)
|
|
144
|
+
- [ ] Phase 11: Release Preparation
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 📝 License
|
|
149
|
+
|
|
150
|
+
MIT License - see [LICENSE](../../LICENSE) for details.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 🙏 Acknowledgments
|
|
155
|
+
|
|
156
|
+
PoolBot Office is inspired by and based on the architecture of [OpenClaw Office](https://github.com/openclaw/openclaw-office).
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
**Documentation:** https://docs.molt.bot/office
|
|
161
|
+
**GitHub:** https://github.com/poolbot/poolbot
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PoolBot Office Configuration
|
|
5
|
+
*
|
|
6
|
+
* Handles CLI argument parsing and configuration management
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_PORT = 5180;
|
|
14
|
+
const DEFAULT_HOST = "0.0.0.0";
|
|
15
|
+
const DEFAULT_GATEWAY = "ws://localhost:18789";
|
|
16
|
+
|
|
17
|
+
export function parseArgs() {
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const parsed = {};
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
const arg = args[i];
|
|
23
|
+
switch (arg) {
|
|
24
|
+
case "-h":
|
|
25
|
+
case "--help":
|
|
26
|
+
parsed.help = true;
|
|
27
|
+
break;
|
|
28
|
+
case "-t":
|
|
29
|
+
case "--token":
|
|
30
|
+
parsed.token = args[++i];
|
|
31
|
+
break;
|
|
32
|
+
case "-g":
|
|
33
|
+
case "--gateway":
|
|
34
|
+
parsed.gateway = args[++i];
|
|
35
|
+
break;
|
|
36
|
+
case "-p":
|
|
37
|
+
case "--port":
|
|
38
|
+
parsed.port = parseInt(args[++i], 10);
|
|
39
|
+
break;
|
|
40
|
+
case "--host":
|
|
41
|
+
parsed.host = args[++i];
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function detectPoolBotToken() {
|
|
50
|
+
try {
|
|
51
|
+
const credentialsPath = join(homedir(), ".poolbot", "credentials", "gateway.json");
|
|
52
|
+
if (!existsSync(credentialsPath)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const credentials = JSON.parse(readFileSync(credentialsPath, "utf-8"));
|
|
56
|
+
return credentials.token || null;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveConfig() {
|
|
63
|
+
const args = parseArgs();
|
|
64
|
+
const homeDir = homedir();
|
|
65
|
+
const officeConfigPath = join(homeDir, ".poolbot", "office-config.json");
|
|
66
|
+
|
|
67
|
+
// Load persisted config if exists
|
|
68
|
+
let persistedGatewayUrl;
|
|
69
|
+
if (existsSync(officeConfigPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const persisted = JSON.parse(readFileSync(officeConfigPath, "utf-8"));
|
|
72
|
+
persistedGatewayUrl = persisted.gatewayUrl;
|
|
73
|
+
} catch {
|
|
74
|
+
// Ignore corrupted config
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Determine gateway URL
|
|
79
|
+
let gatewayUrl = args.gateway || persistedGatewayUrl || DEFAULT_GATEWAY;
|
|
80
|
+
const shouldPersistGatewayUrl = !!args.gateway && !persistedGatewayUrl;
|
|
81
|
+
|
|
82
|
+
// Determine token
|
|
83
|
+
let token = args.token || detectPoolBotToken() || undefined;
|
|
84
|
+
|
|
85
|
+
// Extract token from gateway URL if present (for remote gateways)
|
|
86
|
+
if (gatewayUrl.includes("?token=")) {
|
|
87
|
+
const url = new URL(gatewayUrl.replace("ws://", "http://").replace("wss://", "https://"));
|
|
88
|
+
const urlToken = url.searchParams.get("token");
|
|
89
|
+
if (urlToken) {
|
|
90
|
+
token = urlToken;
|
|
91
|
+
// Remove token from URL for clean storage
|
|
92
|
+
url.searchParams.delete("token");
|
|
93
|
+
gatewayUrl = url.toString().replace("http://", "ws://").replace("https://", "wss://");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
gatewayUrl,
|
|
99
|
+
token,
|
|
100
|
+
port: args.port || DEFAULT_PORT,
|
|
101
|
+
host: args.host || DEFAULT_HOST,
|
|
102
|
+
officeConfigPath,
|
|
103
|
+
shouldPersistGatewayUrl,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function writePersistedOfficeConfig(gatewayUrl, configPath) {
|
|
108
|
+
try {
|
|
109
|
+
const configDir = join(configPath, "..");
|
|
110
|
+
if (!existsSync(configDir)) {
|
|
111
|
+
mkdirSync(configDir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
writeFileSync(configPath, JSON.stringify({ gatewayUrl }, null, 2));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("Failed to persist office config:", error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function printHelp() {
|
|
120
|
+
console.log(`
|
|
121
|
+
PoolBot Office — Visual Agent Monitoring & Management
|
|
122
|
+
|
|
123
|
+
Usage:
|
|
124
|
+
poolbot-office [options]
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
-t, --token <token> Gateway auth token (auto-detected from ~/.poolbot)
|
|
128
|
+
-g, --gateway <url> Gateway WebSocket URL (default: ws://localhost:18789)
|
|
129
|
+
-p, --port <port> Server port (default: 5180)
|
|
130
|
+
--host <host> Bind address (default: 0.0.0.0)
|
|
131
|
+
-h, --help Show help
|
|
132
|
+
|
|
133
|
+
Examples:
|
|
134
|
+
poolbot-office # Auto-detect local gateway
|
|
135
|
+
poolbot-office --token abc123 # Explicit token
|
|
136
|
+
poolbot-office -g ws://remote:18789 # Remote gateway
|
|
137
|
+
poolbot-office -p 8080 # Custom port
|
|
138
|
+
|
|
139
|
+
Remote Gateway Support:
|
|
140
|
+
PoolBot Office supports both local and remote gateways.
|
|
141
|
+
For remote gateways (Aliyun, Tencent Cloud, etc.), use:
|
|
142
|
+
|
|
143
|
+
poolbot-office -g "ws://your-remote-gateway:18789?token=your-token"
|
|
144
|
+
|
|
145
|
+
The token is automatically extracted from the URL.
|
|
146
|
+
|
|
147
|
+
Documentation:
|
|
148
|
+
https://docs.molt.bot/office
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PoolBot Office Server
|
|
5
|
+
*
|
|
6
|
+
* HTTP server for serving the Office UI and proxying WebSocket connections
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
11
|
+
import { join, extname } from "node:path";
|
|
12
|
+
import { networkInterfaces } from "node:os";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* MIME types for static files
|
|
16
|
+
*/
|
|
17
|
+
const MIME_TYPES = {
|
|
18
|
+
".html": "text/html; charset=utf-8",
|
|
19
|
+
".css": "text/css; charset=utf-8",
|
|
20
|
+
".js": "application/javascript; charset=utf-8",
|
|
21
|
+
".json": "application/json; charset=utf-8",
|
|
22
|
+
".png": "image/png",
|
|
23
|
+
".jpg": "image/jpeg",
|
|
24
|
+
".svg": "image/svg+xml",
|
|
25
|
+
".ico": "image/x-icon",
|
|
26
|
+
".woff": "font/woff",
|
|
27
|
+
".woff2": "font/woff2",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get file content type
|
|
32
|
+
*/
|
|
33
|
+
function getContentType(filePath) {
|
|
34
|
+
const ext = extname(filePath).toLowerCase();
|
|
35
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Send JSON response
|
|
40
|
+
*/
|
|
41
|
+
function sendJson(res, status, body) {
|
|
42
|
+
res.statusCode = status;
|
|
43
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
44
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
45
|
+
res.end(JSON.stringify(body));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Send static file
|
|
50
|
+
*/
|
|
51
|
+
function sendStaticFile(res, filePath) {
|
|
52
|
+
if (!existsSync(filePath)) {
|
|
53
|
+
res.statusCode = 404;
|
|
54
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
55
|
+
res.end("Not Found");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const content = readFileSync(filePath);
|
|
61
|
+
res.statusCode = 200;
|
|
62
|
+
res.setHeader("Content-Type", getContentType(filePath));
|
|
63
|
+
res.setHeader("Cache-Control", "public, max-age=31536000");
|
|
64
|
+
res.end(content);
|
|
65
|
+
} catch {
|
|
66
|
+
res.statusCode = 500;
|
|
67
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
68
|
+
res.end("Internal Server Error");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handle health check requests
|
|
74
|
+
*/
|
|
75
|
+
function handleHealthCheck(req, res) {
|
|
76
|
+
if (req.url === "/healthz") {
|
|
77
|
+
res.statusCode = 200;
|
|
78
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
79
|
+
res.end("ok");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (req.url === "/health") {
|
|
84
|
+
sendJson(res, 200, {
|
|
85
|
+
status: "ok",
|
|
86
|
+
service: "poolbot-office",
|
|
87
|
+
version: "0.1.0",
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Handle gateway config requests
|
|
96
|
+
*/
|
|
97
|
+
function handleGatewayConfig(req, res, config) {
|
|
98
|
+
if (req.url === "/api/gateway-config") {
|
|
99
|
+
sendJson(res, 200, {
|
|
100
|
+
gatewayUrl: config.gatewayUrl,
|
|
101
|
+
hasToken: !!config.token,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create Office HTTP server
|
|
109
|
+
*/
|
|
110
|
+
export function createOfficeServer(options) {
|
|
111
|
+
const { config, distDir } = options;
|
|
112
|
+
|
|
113
|
+
const server = createServer((req, res) => {
|
|
114
|
+
// Health checks
|
|
115
|
+
if (req.url === "/healthz" || req.url === "/health") {
|
|
116
|
+
handleHealthCheck(req, res);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Gateway config API
|
|
121
|
+
if (req.url?.startsWith("/api/")) {
|
|
122
|
+
handleGatewayConfig(req, res, config);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Static file serving
|
|
127
|
+
let requestPath = req.url === "/" ? "/index.html" : req.url;
|
|
128
|
+
|
|
129
|
+
// Remove query parameters
|
|
130
|
+
requestPath = requestPath.split("?")[0];
|
|
131
|
+
|
|
132
|
+
const filePath = join(distDir, requestPath);
|
|
133
|
+
|
|
134
|
+
// Security: prevent directory traversal
|
|
135
|
+
if (!filePath.startsWith(distDir)) {
|
|
136
|
+
res.statusCode = 403;
|
|
137
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
138
|
+
res.end("Forbidden");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
sendStaticFile(res, filePath);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return { server };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Format startup summary
|
|
150
|
+
*/
|
|
151
|
+
export function formatStartupSummary(config) {
|
|
152
|
+
const localIp = getLocalIpAddress();
|
|
153
|
+
|
|
154
|
+
const lines = [
|
|
155
|
+
"",
|
|
156
|
+
" ┌─────────────────────────────────────────────────┐",
|
|
157
|
+
" │ │",
|
|
158
|
+
" │ PoolBot Office is running! │",
|
|
159
|
+
" │ │",
|
|
160
|
+
" └─────────────────────────────────────────────────┘",
|
|
161
|
+
"",
|
|
162
|
+
` ➜ Local: http://localhost:${config.port}/`,
|
|
163
|
+
` ➜ Network: http://${localIp}:${config.port}/`,
|
|
164
|
+
"",
|
|
165
|
+
` Gateway: ${config.gatewayUrl}`,
|
|
166
|
+
` Token: ${config.token ? "✓ Configured" : "✗ Not configured"}`,
|
|
167
|
+
"",
|
|
168
|
+
" ─────────────────────────────────────────────────",
|
|
169
|
+
"",
|
|
170
|
+
" Quick Start:",
|
|
171
|
+
" 1. Open the URL above in your browser",
|
|
172
|
+
" 2. Configure gateway connection if needed",
|
|
173
|
+
" 3. Start monitoring your agents!",
|
|
174
|
+
"",
|
|
175
|
+
" Documentation: https://docs.molt.bot/office",
|
|
176
|
+
"",
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get local IP address
|
|
184
|
+
*/
|
|
185
|
+
function getLocalIpAddress() {
|
|
186
|
+
try {
|
|
187
|
+
const nets = networkInterfaces();
|
|
188
|
+
|
|
189
|
+
for (const name of Object.keys(nets)) {
|
|
190
|
+
const netArray = nets[name];
|
|
191
|
+
if (!netArray) continue;
|
|
192
|
+
|
|
193
|
+
for (const net of netArray) {
|
|
194
|
+
const family = net.family;
|
|
195
|
+
const internal = net.internal;
|
|
196
|
+
const address = net.address;
|
|
197
|
+
|
|
198
|
+
// Skip internal (loopback) and non-IPv4 addresses
|
|
199
|
+
if (internal || family !== "IPv4") continue;
|
|
200
|
+
|
|
201
|
+
return address;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// Fallback
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return "localhost";
|
|
209
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PoolBot Office CLI
|
|
5
|
+
*
|
|
6
|
+
* Visual monitoring & management frontend for PoolBot Multi-Agent system
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
printHelp,
|
|
13
|
+
resolveConfig,
|
|
14
|
+
parseArgs,
|
|
15
|
+
writePersistedOfficeConfig,
|
|
16
|
+
} from "./poolbot-office-config.js";
|
|
17
|
+
import { createOfficeServer, formatStartupSummary } from "./poolbot-office-server.js";
|
|
18
|
+
|
|
19
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
20
|
+
const distDir = resolve(__dirname, "..", "dist");
|
|
21
|
+
|
|
22
|
+
const args = parseArgs();
|
|
23
|
+
if (args.help) {
|
|
24
|
+
printHelp();
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const config = resolveConfig();
|
|
29
|
+
if (config.shouldPersistGatewayUrl) {
|
|
30
|
+
writePersistedOfficeConfig(config.gatewayUrl, config.officeConfigPath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { server } = createOfficeServer({ config, distDir });
|
|
34
|
+
|
|
35
|
+
server.listen(config.port, config.host, () => {
|
|
36
|
+
console.log(formatStartupSummary(config));
|
|
37
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@poolzin/poolbot-office",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Visual monitoring & management frontend for PoolBot Multi-Agent system",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai-agents",
|
|
7
|
+
"dashboard",
|
|
8
|
+
"monitoring",
|
|
9
|
+
"multi-agent",
|
|
10
|
+
"office",
|
|
11
|
+
"poolbot",
|
|
12
|
+
"react",
|
|
13
|
+
"visualization",
|
|
14
|
+
"vite"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/poolbot/poolbot/tree/main/apps/office",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/poolbot/poolbot/issues"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/poolbot/poolbot.git",
|
|
24
|
+
"directory": "apps/office"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"poolbot-office": "bin/poolbot-office.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist/",
|
|
31
|
+
"bin/",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"type": "module",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -b && vite build",
|
|
38
|
+
"check": "pnpm lint && pnpm format:check",
|
|
39
|
+
"dev": "vite",
|
|
40
|
+
"format": "oxfmt --write src/",
|
|
41
|
+
"format:check": "oxfmt --check src/",
|
|
42
|
+
"lint": "oxlint src/",
|
|
43
|
+
"preview": "vite preview",
|
|
44
|
+
"start": "node ./bin/poolbot-office.js",
|
|
45
|
+
"test": "vitest run --passWithNoTests",
|
|
46
|
+
"test:watch": "vitest --passWithNoTests",
|
|
47
|
+
"typecheck": "tsc --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@react-three/drei": "^10.7.7",
|
|
51
|
+
"@react-three/fiber": "^9.5.0",
|
|
52
|
+
"@react-three/postprocessing": "^3.0.4",
|
|
53
|
+
"i18next": "^25.8.13",
|
|
54
|
+
"i18next-browser-languagedetector": "^8.2.1",
|
|
55
|
+
"immer": "^10.1.1",
|
|
56
|
+
"lucide-react": "^0.575.0",
|
|
57
|
+
"react": "^19.1.0",
|
|
58
|
+
"react-dom": "^19.1.0",
|
|
59
|
+
"react-i18next": "^16.5.4",
|
|
60
|
+
"react-markdown": "^10.1.0",
|
|
61
|
+
"react-router-dom": "^7.13.1",
|
|
62
|
+
"react-textarea-autosize": "^8.5.9",
|
|
63
|
+
"recharts": "^2.15.0",
|
|
64
|
+
"remark-gfm": "^4.0.1",
|
|
65
|
+
"three": "^0.183.1",
|
|
66
|
+
"zustand": "^5.0.0"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@tailwindcss/vite": "^4.1.0",
|
|
70
|
+
"@testing-library/jest-dom": "^6.6.0",
|
|
71
|
+
"@testing-library/react": "^16.3.0",
|
|
72
|
+
"@types/react": "^19.1.0",
|
|
73
|
+
"@types/react-dom": "^19.1.0",
|
|
74
|
+
"@types/three": "^0.183.1",
|
|
75
|
+
"@vitejs/plugin-react": "^4.5.0",
|
|
76
|
+
"fake-indexeddb": "^6.2.5",
|
|
77
|
+
"jsdom": "^26.1.0",
|
|
78
|
+
"tailwindcss": "^4.1.0",
|
|
79
|
+
"typescript": "^5.8.0",
|
|
80
|
+
"vite": "^6.3.0",
|
|
81
|
+
"vitest": "^3.1.0"
|
|
82
|
+
},
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=22"
|
|
85
|
+
}
|
|
86
|
+
}
|