@devo-bmad-custom/agent-orchestration 1.0.6 → 1.0.7
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/package.json +4 -2
- package/src/.agents/skills/tmux-commands/SKILL.md +1 -1
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +58 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +100 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -0
- package/src/.agents/skills/ux-audit/SKILL.md +151 -0
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -0
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -0
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -0
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -0
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -0
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -0
- package/src/.agents/skills/writing-skills/SKILL.md +655 -0
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -0
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -0
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/src/.claude/commands/bmad-track-compact.md +19 -0
- package/src/.claude/commands/bmad-track-extended.md +19 -0
- package/src/.claude/commands/bmad-track-large.md +19 -0
- package/src/.claude/commands/bmad-track-medium.md +19 -0
- package/src/.claude/commands/bmad-track-nano.md +19 -0
- package/src/.claude/commands/bmad-track-rv.md +18 -0
- package/src/.claude/commands/bmad-track-small.md +19 -0
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/master-orchestrator-sidecar/docs-index.md +3 -0
- package/src/_memory/master-orchestrator-sidecar/instructions.md +2616 -0
- package/src/_memory/master-orchestrator-sidecar/memories.md +8 -0
- package/src/_memory/master-orchestrator-sidecar/session-state.md +15 -0
- package/src/_memory/master-orchestrator-sidecar/triage-history.md +3 -0
- package/src/_memory/master-orchestrator-sidecar/workflows-overview.html +1230 -0
- package/src/core/agents/master-orchestrator.md +54 -0
- package/src/docs/dev/tmux/actions_popup.py +291 -0
- package/src/docs/dev/tmux/actions_popup.sh +110 -0
- package/src/docs/dev/tmux/claude_usage.sh +15 -0
- package/src/docs/dev/tmux/colors.conf +26 -0
- package/src/docs/dev/tmux/cpu_usage.sh +7 -0
- package/src/docs/dev/tmux/dispatch.sh +10 -0
- package/src/docs/dev/tmux/float_init.sh +13 -0
- package/src/docs/dev/tmux/float_term.sh +23 -0
- package/src/docs/dev/tmux/open_clip.sh +14 -0
- package/src/docs/dev/tmux/paste_claude.sh +26 -0
- package/src/docs/dev/tmux/paste_clipboard.sh +13 -0
- package/src/docs/dev/tmux/paste_image_wrapper.sh +98 -0
- package/src/docs/dev/tmux/ram_usage.sh +3 -0
- package/src/docs/dev/tmux/title_sync.sh +54 -0
- package/src/docs/dev/tmux/tmux-setup.md +867 -0
- package/src/docs/dev/tmux/tmux-test.sh +255 -0
- package/src/docs/dev/tmux/tmux.conf +127 -0
- package/src/docs/dev/tmux/xclip +18 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Horizontal Scaling Reference
|
|
2
|
+
|
|
3
|
+
## Architecture Overview
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌─────────────┐
|
|
7
|
+
│Load Balancer│ (nginx/HAProxy with sticky sessions)
|
|
8
|
+
└──────┬──────┘
|
|
9
|
+
│
|
|
10
|
+
┌───┴───┐
|
|
11
|
+
│ │
|
|
12
|
+
┌──▼──┐ ┌──▼──┐
|
|
13
|
+
│WS #1│ │WS #2│ ... (Socket.IO servers)
|
|
14
|
+
└──┬──┘ └──┬──┘
|
|
15
|
+
│ │
|
|
16
|
+
└───┬───┘
|
|
17
|
+
│
|
|
18
|
+
┌───▼───┐
|
|
19
|
+
│ Redis │ (Pub/Sub adapter)
|
|
20
|
+
└───────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Redis Adapter Configuration
|
|
24
|
+
|
|
25
|
+
### Socket.IO with Redis
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
const { createServer } = require('http');
|
|
29
|
+
const { Server } = require('socket.io');
|
|
30
|
+
const { createAdapter } = require('@socket.io/redis-adapter');
|
|
31
|
+
const { createClient } = require('redis');
|
|
32
|
+
|
|
33
|
+
const httpServer = createServer();
|
|
34
|
+
const io = new Server(httpServer, {
|
|
35
|
+
cors: { origin: '*' }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Redis pub/sub client setup
|
|
39
|
+
const pubClient = createClient({
|
|
40
|
+
host: 'localhost',
|
|
41
|
+
port: 6379
|
|
42
|
+
});
|
|
43
|
+
const subClient = pubClient.duplicate();
|
|
44
|
+
|
|
45
|
+
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
|
|
46
|
+
io.adapter(createAdapter(pubClient, subClient));
|
|
47
|
+
console.log('Redis adapter connected');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Now broadcasts work across all servers
|
|
51
|
+
io.emit('news', { hello: 'world' });
|
|
52
|
+
|
|
53
|
+
httpServer.listen(3000);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Redis Streams for Reliable Delivery
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const { createAdapter } = require('@socket.io/redis-streams-adapter');
|
|
60
|
+
|
|
61
|
+
const redisClient = createClient({ url: 'redis://localhost:6379' });
|
|
62
|
+
|
|
63
|
+
redisClient.connect().then(() => {
|
|
64
|
+
io.adapter(createAdapter(redisClient, {
|
|
65
|
+
streamName: 'socket.io-stream',
|
|
66
|
+
maxLen: 10000, // Keep last 10k messages
|
|
67
|
+
readCount: 100 // Process 100 messages at a time
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Sticky Sessions
|
|
73
|
+
|
|
74
|
+
### Nginx Configuration
|
|
75
|
+
|
|
76
|
+
```nginx
|
|
77
|
+
upstream websocket_backend {
|
|
78
|
+
ip_hash; # Sticky sessions based on IP
|
|
79
|
+
server ws1.example.com:3000;
|
|
80
|
+
server ws2.example.com:3000;
|
|
81
|
+
server ws3.example.com:3000;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
server {
|
|
85
|
+
listen 80;
|
|
86
|
+
server_name example.com;
|
|
87
|
+
|
|
88
|
+
location /socket.io/ {
|
|
89
|
+
proxy_pass http://websocket_backend;
|
|
90
|
+
proxy_http_version 1.1;
|
|
91
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
92
|
+
proxy_set_header Connection "upgrade";
|
|
93
|
+
proxy_set_header Host $host;
|
|
94
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
95
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
96
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
97
|
+
|
|
98
|
+
# Timeouts
|
|
99
|
+
proxy_connect_timeout 7d;
|
|
100
|
+
proxy_send_timeout 7d;
|
|
101
|
+
proxy_read_timeout 7d;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### HAProxy Configuration
|
|
107
|
+
|
|
108
|
+
```haproxyconf
|
|
109
|
+
frontend websocket_frontend
|
|
110
|
+
bind *:80
|
|
111
|
+
mode http
|
|
112
|
+
option httplog
|
|
113
|
+
use_backend websocket_backend
|
|
114
|
+
|
|
115
|
+
backend websocket_backend
|
|
116
|
+
mode http
|
|
117
|
+
balance source # Sticky sessions by source IP
|
|
118
|
+
hash-type consistent # Consistent hashing
|
|
119
|
+
|
|
120
|
+
# Health checks
|
|
121
|
+
option httpchk GET /health
|
|
122
|
+
http-check expect status 200
|
|
123
|
+
|
|
124
|
+
server ws1 10.0.1.1:3000 check
|
|
125
|
+
server ws2 10.0.1.2:3000 check
|
|
126
|
+
server ws3 10.0.1.3:3000 check
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Cookie-based Sticky Sessions
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
// Server-side: Set affinity cookie
|
|
133
|
+
io.engine.on('connection', (rawSocket) => {
|
|
134
|
+
const serverID = process.env.SERVER_ID || 'server1';
|
|
135
|
+
rawSocket.request.res.setHeader(
|
|
136
|
+
'Set-Cookie',
|
|
137
|
+
`io=${serverID}; Path=/; HttpOnly; SameSite=Lax`
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```nginx
|
|
143
|
+
# Nginx: Use cookie for routing
|
|
144
|
+
upstream websocket_backend {
|
|
145
|
+
server ws1.example.com:3000;
|
|
146
|
+
server ws2.example.com:3000;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
map $cookie_io $backend_server {
|
|
150
|
+
"server1" ws1.example.com:3000;
|
|
151
|
+
"server2" ws2.example.com:3000;
|
|
152
|
+
default websocket_backend;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
location /socket.io/ {
|
|
156
|
+
proxy_pass http://$backend_server;
|
|
157
|
+
# ... other proxy settings
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## State Management
|
|
162
|
+
|
|
163
|
+
### Shared State in Redis
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const Redis = require('ioredis');
|
|
167
|
+
const redis = new Redis();
|
|
168
|
+
|
|
169
|
+
// Store user connection info
|
|
170
|
+
io.on('connection', async (socket) => {
|
|
171
|
+
const userId = socket.handshake.auth.userId;
|
|
172
|
+
|
|
173
|
+
// Track which server has this user
|
|
174
|
+
await redis.hset('user:connections', userId, process.env.SERVER_ID);
|
|
175
|
+
|
|
176
|
+
// Store user presence
|
|
177
|
+
await redis.hset(`user:${userId}`, {
|
|
178
|
+
socketId: socket.id,
|
|
179
|
+
serverId: process.env.SERVER_ID,
|
|
180
|
+
connectedAt: Date.now(),
|
|
181
|
+
status: 'online'
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
socket.on('disconnect', async () => {
|
|
185
|
+
await redis.hdel('user:connections', userId);
|
|
186
|
+
await redis.del(`user:${userId}`);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Send message to specific user across cluster
|
|
191
|
+
async function sendToUser(userId, event, data) {
|
|
192
|
+
const serverId = await redis.hget('user:connections', userId);
|
|
193
|
+
|
|
194
|
+
if (serverId === process.env.SERVER_ID) {
|
|
195
|
+
// User is on this server
|
|
196
|
+
const sockets = await io.in(`user:${userId}`).fetchSockets();
|
|
197
|
+
sockets.forEach(socket => socket.emit(event, data));
|
|
198
|
+
} else {
|
|
199
|
+
// User is on another server - use Redis to route
|
|
200
|
+
io.to(`user:${userId}`).emit(event, data);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Connection Limits
|
|
206
|
+
|
|
207
|
+
### Per-Server Limits
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
const MAX_CONNECTIONS = 50000;
|
|
211
|
+
|
|
212
|
+
io.engine.on('connection', (socket) => {
|
|
213
|
+
const currentConnections = io.engine.clientsCount;
|
|
214
|
+
|
|
215
|
+
if (currentConnections > MAX_CONNECTIONS) {
|
|
216
|
+
socket.close(1008, 'Server at capacity');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Kubernetes Horizontal Pod Autoscaling
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
apiVersion: autoscaling/v2
|
|
226
|
+
kind: HorizontalPodAutoscaler
|
|
227
|
+
metadata:
|
|
228
|
+
name: websocket-server-hpa
|
|
229
|
+
spec:
|
|
230
|
+
scaleTargetRef:
|
|
231
|
+
apiVersion: apps/v1
|
|
232
|
+
kind: Deployment
|
|
233
|
+
name: websocket-server
|
|
234
|
+
minReplicas: 3
|
|
235
|
+
maxReplicas: 20
|
|
236
|
+
metrics:
|
|
237
|
+
- type: Resource
|
|
238
|
+
resource:
|
|
239
|
+
name: cpu
|
|
240
|
+
target:
|
|
241
|
+
type: Utilization
|
|
242
|
+
averageUtilization: 70
|
|
243
|
+
- type: Pods
|
|
244
|
+
pods:
|
|
245
|
+
metric:
|
|
246
|
+
name: websocket_connections
|
|
247
|
+
target:
|
|
248
|
+
type: AverageValue
|
|
249
|
+
averageValue: "40000" # Scale when avg > 40k connections/pod
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Graceful Shutdown
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
const gracefulShutdown = () => {
|
|
256
|
+
console.log('Shutting down gracefully...');
|
|
257
|
+
|
|
258
|
+
// Stop accepting new connections
|
|
259
|
+
io.close(() => {
|
|
260
|
+
console.log('All connections closed');
|
|
261
|
+
process.exit(0);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Force close after 30 seconds
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
console.error('Forcing shutdown after timeout');
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}, 30000);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
272
|
+
process.on('SIGINT', gracefulShutdown);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Performance Optimization
|
|
276
|
+
|
|
277
|
+
### Node.js Clustering
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
const cluster = require('cluster');
|
|
281
|
+
const os = require('os');
|
|
282
|
+
|
|
283
|
+
if (cluster.isMaster) {
|
|
284
|
+
const numWorkers = os.cpus().length;
|
|
285
|
+
|
|
286
|
+
console.log(`Master ${process.pid} starting ${numWorkers} workers`);
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < numWorkers; i++) {
|
|
289
|
+
cluster.fork();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
cluster.on('exit', (worker) => {
|
|
293
|
+
console.log(`Worker ${worker.process.pid} died, spawning new`);
|
|
294
|
+
cluster.fork();
|
|
295
|
+
});
|
|
296
|
+
} else {
|
|
297
|
+
// Worker process runs Socket.IO server
|
|
298
|
+
const io = require('./socket-server');
|
|
299
|
+
io.listen(3000);
|
|
300
|
+
console.log(`Worker ${process.pid} started`);
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### uWebSockets.js for Maximum Performance
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
const uWS = require('uWebSockets.js');
|
|
308
|
+
|
|
309
|
+
const app = uWS.App()
|
|
310
|
+
.ws('/*', {
|
|
311
|
+
compression: uWS.SHARED_COMPRESSOR,
|
|
312
|
+
maxPayloadLength: 16 * 1024,
|
|
313
|
+
idleTimeout: 60,
|
|
314
|
+
|
|
315
|
+
open: (ws) => {
|
|
316
|
+
console.log('Client connected');
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
message: (ws, message, isBinary) => {
|
|
320
|
+
// Echo message
|
|
321
|
+
ws.send(message, isBinary);
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
close: (ws, code, message) => {
|
|
325
|
+
console.log('Client disconnected');
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
.listen(9001, (token) => {
|
|
329
|
+
if (token) {
|
|
330
|
+
console.log('Listening on port 9001');
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
```
|