@esotech/contextuate 2.0.0 → 2.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 +169 -1
- package/dist/commands/claude.d.ts +21 -0
- package/dist/commands/claude.js +213 -0
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +67 -6
- package/dist/commands/install.d.ts +28 -0
- package/dist/commands/install.js +116 -11
- package/dist/commands/monitor.d.ts +55 -0
- package/dist/commands/monitor.js +1007 -0
- package/dist/commands/remove.d.ts +3 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +113 -1
- package/dist/monitor/daemon/circuit-breaker.d.ts +121 -0
- package/dist/monitor/daemon/circuit-breaker.js +552 -0
- package/dist/monitor/daemon/cli.d.ts +8 -0
- package/dist/monitor/daemon/cli.js +82 -0
- package/dist/monitor/daemon/index.d.ts +137 -0
- package/dist/monitor/daemon/index.js +695 -0
- package/dist/monitor/daemon/notifier.d.ts +25 -0
- package/dist/monitor/daemon/notifier.js +98 -0
- package/dist/monitor/daemon/processor.d.ts +89 -0
- package/dist/monitor/daemon/processor.js +455 -0
- package/dist/monitor/daemon/state.d.ts +80 -0
- package/dist/monitor/daemon/state.js +162 -0
- package/dist/monitor/daemon/watcher.d.ts +47 -0
- package/dist/monitor/daemon/watcher.js +171 -0
- package/dist/monitor/daemon/wrapper-manager.d.ts +106 -0
- package/dist/monitor/daemon/wrapper-manager.js +374 -0
- package/dist/monitor/hooks/emit-event.js +652 -0
- package/dist/monitor/persistence/file-store.d.ts +88 -0
- package/dist/monitor/persistence/file-store.js +335 -0
- package/dist/monitor/persistence/index.d.ts +7 -0
- package/dist/monitor/persistence/index.js +10 -0
- package/dist/monitor/server/adapters/redis.d.ts +38 -0
- package/dist/monitor/server/adapters/redis.js +213 -0
- package/dist/monitor/server/adapters/unix-socket.d.ts +33 -0
- package/dist/monitor/server/adapters/unix-socket.js +182 -0
- package/dist/monitor/server/broker.d.ts +135 -0
- package/dist/monitor/server/broker.js +475 -0
- package/dist/monitor/server/cli.d.ts +8 -0
- package/dist/monitor/server/cli.js +98 -0
- package/dist/monitor/server/fastify.d.ts +16 -0
- package/dist/monitor/server/fastify.js +184 -0
- package/dist/monitor/server/index.d.ts +36 -0
- package/dist/monitor/server/index.js +153 -0
- package/dist/monitor/server/websocket.d.ts +80 -0
- package/dist/monitor/server/websocket.js +453 -0
- package/dist/monitor/ui/assets/index-4IssW9On.js +59 -0
- package/dist/monitor/ui/assets/index-vo9hLe5R.css +32 -0
- package/dist/monitor/ui/favicon.png +0 -0
- package/dist/monitor/ui/index.html +14 -0
- package/dist/monitor/ui/logo.png +0 -0
- package/dist/monitor/ui/logo.svg +1 -0
- package/dist/runtime/driver.d.ts +16 -0
- package/dist/runtime/tools.d.ts +10 -0
- package/dist/templates/README.md +33 -7
- package/dist/templates/agents/aegis.md +4 -0
- package/dist/templates/agents/archon.md +13 -22
- package/dist/templates/agents/atlas.md +4 -0
- package/dist/templates/agents/canvas.md +4 -0
- package/dist/templates/agents/chronicle.md +4 -0
- package/dist/templates/agents/chronos.md +4 -0
- package/dist/templates/agents/cipher.md +4 -0
- package/dist/templates/agents/crucible.md +4 -0
- package/dist/templates/agents/echo.md +4 -0
- package/dist/templates/agents/forge.md +4 -0
- package/dist/templates/agents/ledger.md +4 -0
- package/dist/templates/agents/meridian.md +4 -0
- package/dist/templates/agents/nexus.md +4 -0
- package/dist/templates/agents/pythia.md +217 -0
- package/dist/templates/agents/scribe.md +4 -0
- package/dist/templates/agents/sentinel.md +4 -0
- package/dist/templates/agents/{oracle.md → thoth.md} +11 -7
- package/dist/templates/agents/unity.md +4 -0
- package/dist/templates/agents/vox.md +4 -0
- package/dist/templates/agents/weaver.md +4 -0
- package/dist/templates/framework-agents/documentation-expert.md +3 -3
- package/dist/templates/framework-agents/tools-expert.md +8 -8
- package/dist/templates/skills/consult.md +138 -0
- package/dist/templates/skills/orchestrate.md +173 -0
- package/dist/templates/skills/pythia.md +37 -0
- package/dist/templates/standards/agent-roles.md +68 -21
- package/dist/templates/standards/coding-standards.md +9 -26
- package/dist/templates/templates/context.md +17 -2
- package/dist/templates/templates/contextuate.md +21 -28
- package/dist/templates/templates/standards/go.md +167 -0
- package/dist/templates/templates/standards/java.md +167 -0
- package/dist/templates/templates/standards/javascript.md +292 -0
- package/dist/templates/templates/standards/php.md +181 -0
- package/dist/templates/templates/standards/python.md +175 -0
- package/dist/templates/tools/agent-creator.md +252 -0
- package/dist/templates/tools/agent-creator.tool.md +2 -2
- package/dist/templates/tools/quickref.md +216 -0
- package/dist/templates/tools/spawn.md +31 -0
- package/dist/templates/tools/standards-detector.md +301 -0
- package/dist/templates/version.json +1 -1
- package/dist/types/monitor.d.ts +660 -0
- package/dist/types/monitor.js +75 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/tokens.d.ts +10 -0
- package/package.json +18 -5
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Event Broker
|
|
4
|
+
*
|
|
5
|
+
* Session management and WebSocket broadcast layer for the 3-layer architecture.
|
|
6
|
+
*
|
|
7
|
+
* Role:
|
|
8
|
+
* - Load sessions from disk on startup
|
|
9
|
+
* - Connect to daemon to receive processed events and session updates
|
|
10
|
+
* - Handle session management actions (rename, pin, hide, delete, set parent)
|
|
11
|
+
* - Broadcast updates to WebSocket clients
|
|
12
|
+
* - Subscribe to Redis for multi-machine UI aggregation (optional)
|
|
13
|
+
*
|
|
14
|
+
* Note: Event processing (correlation, parent linking, etc.) is handled by the daemon.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.EventBroker = void 0;
|
|
51
|
+
const net = __importStar(require("net"));
|
|
52
|
+
class EventBroker {
|
|
53
|
+
constructor(config) {
|
|
54
|
+
this.daemonSocket = null;
|
|
55
|
+
this.persistence = null;
|
|
56
|
+
this.handlers = new Set();
|
|
57
|
+
this.sessions = new Map();
|
|
58
|
+
this.reconnectTimeout = null;
|
|
59
|
+
this.stopping = false;
|
|
60
|
+
this.connectionAttempts = 0;
|
|
61
|
+
this.config = config;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Start the event broker
|
|
65
|
+
*/
|
|
66
|
+
async start() {
|
|
67
|
+
// Load sessions from disk
|
|
68
|
+
await this.loadSessions();
|
|
69
|
+
// Connect to daemon socket (optional - daemon may not be running)
|
|
70
|
+
this.connectToDaemon();
|
|
71
|
+
console.log('[Broker] Started - sessions loaded, daemon connection initiated');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Stop the event broker
|
|
75
|
+
*/
|
|
76
|
+
async stop() {
|
|
77
|
+
// Set stopping flag BEFORE clearing timeout or destroying socket
|
|
78
|
+
// This prevents the close event from scheduling a new reconnection
|
|
79
|
+
this.stopping = true;
|
|
80
|
+
if (this.reconnectTimeout) {
|
|
81
|
+
clearTimeout(this.reconnectTimeout);
|
|
82
|
+
this.reconnectTimeout = null;
|
|
83
|
+
}
|
|
84
|
+
if (this.daemonSocket) {
|
|
85
|
+
this.daemonSocket.destroy();
|
|
86
|
+
this.daemonSocket = null;
|
|
87
|
+
}
|
|
88
|
+
console.log('[Broker] Stopped');
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Connect to daemon socket to receive processed events
|
|
92
|
+
*/
|
|
93
|
+
connectToDaemon() {
|
|
94
|
+
const socketPath = '/tmp/contextuate-daemon.sock';
|
|
95
|
+
this.daemonSocket = net.createConnection(socketPath);
|
|
96
|
+
let buffer = '';
|
|
97
|
+
this.daemonSocket.on('data', (data) => {
|
|
98
|
+
buffer += data.toString();
|
|
99
|
+
const lines = buffer.split('\n');
|
|
100
|
+
buffer = lines.pop() || '';
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
if (line.trim()) {
|
|
103
|
+
try {
|
|
104
|
+
const message = JSON.parse(line);
|
|
105
|
+
this.handleDaemonMessage(message);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error('[Broker] Failed to parse daemon message:', err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
this.daemonSocket.on('connect', () => {
|
|
114
|
+
this.connectionAttempts = 0; // Reset on successful connection
|
|
115
|
+
console.log('[Broker] Connected to daemon');
|
|
116
|
+
});
|
|
117
|
+
this.daemonSocket.on('error', (err) => {
|
|
118
|
+
// Only log after a few attempts to avoid noise during startup
|
|
119
|
+
if (this.connectionAttempts >= 3) {
|
|
120
|
+
console.log('[Broker] Daemon connection error:', err.message);
|
|
121
|
+
}
|
|
122
|
+
this.scheduleDaemonReconnect();
|
|
123
|
+
});
|
|
124
|
+
this.daemonSocket.on('close', () => {
|
|
125
|
+
if (!this.stopping && this.connectionAttempts === 0) {
|
|
126
|
+
// Only log if we were previously connected
|
|
127
|
+
console.log('[Broker] Daemon connection closed, will retry...');
|
|
128
|
+
}
|
|
129
|
+
this.scheduleDaemonReconnect();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Schedule reconnection to daemon
|
|
134
|
+
*/
|
|
135
|
+
scheduleDaemonReconnect() {
|
|
136
|
+
// Don't reconnect if we're stopping
|
|
137
|
+
if (this.stopping) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (this.reconnectTimeout) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.connectionAttempts++;
|
|
144
|
+
// Fast retries initially (500ms for first 5 attempts), then slow down
|
|
145
|
+
const delay = this.connectionAttempts <= 5 ? 500 : 5000;
|
|
146
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
147
|
+
this.reconnectTimeout = null;
|
|
148
|
+
// Double-check stopping flag before reconnecting
|
|
149
|
+
if (!this.stopping) {
|
|
150
|
+
this.connectToDaemon();
|
|
151
|
+
}
|
|
152
|
+
}, delay);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Handle message from daemon
|
|
156
|
+
*/
|
|
157
|
+
handleDaemonMessage(message) {
|
|
158
|
+
if (message.type === 'session_update') {
|
|
159
|
+
// Update local session cache
|
|
160
|
+
const session = message.session;
|
|
161
|
+
const isNew = !this.sessions.has(session.sessionId);
|
|
162
|
+
this.sessions.set(session.sessionId, session);
|
|
163
|
+
// Emit to WebSocket clients
|
|
164
|
+
if (isNew) {
|
|
165
|
+
this.emit('session_created', session);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.emit('session_updated', session);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if (message.type === 'wrapper_connected') {
|
|
172
|
+
// Wrapper session connected
|
|
173
|
+
this.emit('wrapper_connected', {
|
|
174
|
+
wrapperId: message.wrapperId,
|
|
175
|
+
state: message.state,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else if (message.type === 'wrapper_disconnected') {
|
|
179
|
+
// Wrapper session disconnected
|
|
180
|
+
this.emit('wrapper_disconnected', {
|
|
181
|
+
wrapperId: message.wrapperId,
|
|
182
|
+
exitCode: message.exitCode,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (message.type === 'wrapper_state') {
|
|
186
|
+
// Wrapper state changed
|
|
187
|
+
this.emit('wrapper_state', {
|
|
188
|
+
wrapperId: message.wrapperId,
|
|
189
|
+
state: message.state,
|
|
190
|
+
claudeSessionId: message.claudeSessionId,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else if (message.type === 'wrapper_output') {
|
|
194
|
+
// Wrapper terminal output
|
|
195
|
+
this.emit('wrapper_output', {
|
|
196
|
+
wrapperId: message.wrapperId,
|
|
197
|
+
data: message.data,
|
|
198
|
+
timestamp: message.timestamp,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else if (message.type === 'wrapper_spawned') {
|
|
202
|
+
// Wrapper spawn response
|
|
203
|
+
this.emit('wrapper_spawned', {
|
|
204
|
+
wrapperId: message.wrapperId || '',
|
|
205
|
+
success: message.success,
|
|
206
|
+
error: message.error,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
else if (message.type === 'wrappers_list') {
|
|
210
|
+
// List of active wrappers from daemon
|
|
211
|
+
this.emit('wrappers_list', {
|
|
212
|
+
wrappers: message.wrappers || [],
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else if (message.eventType) {
|
|
216
|
+
// This is an event from the daemon
|
|
217
|
+
const event = message;
|
|
218
|
+
this.emit('event', event);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Send a message to the daemon
|
|
223
|
+
*/
|
|
224
|
+
sendToDaemon(message) {
|
|
225
|
+
if (this.daemonSocket && this.daemonSocket.writable) {
|
|
226
|
+
this.daemonSocket.write(JSON.stringify(message) + '\n');
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
console.error('[Broker] Cannot send to daemon - not connected');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Set the persistence store
|
|
234
|
+
*/
|
|
235
|
+
setPersistence(store) {
|
|
236
|
+
this.persistence = store;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Register an event handler
|
|
240
|
+
*/
|
|
241
|
+
onEvent(handler) {
|
|
242
|
+
this.handlers.add(handler);
|
|
243
|
+
return () => this.handlers.delete(handler);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get all active sessions
|
|
247
|
+
*/
|
|
248
|
+
getSessions(includeHidden = true) {
|
|
249
|
+
const sessions = Array.from(this.sessions.values());
|
|
250
|
+
if (includeHidden) {
|
|
251
|
+
return sessions;
|
|
252
|
+
}
|
|
253
|
+
return sessions.filter(session => !session.hidden);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get a specific session
|
|
257
|
+
*/
|
|
258
|
+
getSession(sessionId) {
|
|
259
|
+
return this.sessions.get(sessionId);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Persist session to storage
|
|
263
|
+
*/
|
|
264
|
+
async persistSession(session) {
|
|
265
|
+
if (this.persistence) {
|
|
266
|
+
try {
|
|
267
|
+
await this.persistence.saveSession(session);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
console.error('[Broker] Failed to persist session:', err);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Emit event to all handlers
|
|
276
|
+
*/
|
|
277
|
+
emit(type, data) {
|
|
278
|
+
this.handlers.forEach((handler) => {
|
|
279
|
+
try {
|
|
280
|
+
const result = handler(type, data);
|
|
281
|
+
if (result instanceof Promise) {
|
|
282
|
+
result.catch((err) => {
|
|
283
|
+
console.error('[Broker] Handler error:', err);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
console.error('[Broker] Handler error:', err);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Load existing sessions from persistence (disk)
|
|
294
|
+
*/
|
|
295
|
+
async loadSessions() {
|
|
296
|
+
if (this.persistence) {
|
|
297
|
+
try {
|
|
298
|
+
const sessions = await this.persistence.getSessions();
|
|
299
|
+
for (const session of sessions) {
|
|
300
|
+
this.sessions.set(session.sessionId, session);
|
|
301
|
+
}
|
|
302
|
+
console.log(`[Broker] Loaded ${sessions.length} sessions from persistence`);
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
console.error('[Broker] Failed to load sessions:', err);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get configuration
|
|
311
|
+
*/
|
|
312
|
+
getConfig() {
|
|
313
|
+
return this.config;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Hide a session (soft-hide, preserves data)
|
|
317
|
+
*/
|
|
318
|
+
async hideSession(sessionId) {
|
|
319
|
+
const session = this.sessions.get(sessionId);
|
|
320
|
+
if (!session) {
|
|
321
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
322
|
+
}
|
|
323
|
+
session.hidden = true;
|
|
324
|
+
await this.persistSession(session);
|
|
325
|
+
this.emit('session_updated', session);
|
|
326
|
+
console.log(`[Broker] Session hidden: ${sessionId.slice(0, 8)}`);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Unhide a session
|
|
330
|
+
*/
|
|
331
|
+
async unhideSession(sessionId) {
|
|
332
|
+
const session = this.sessions.get(sessionId);
|
|
333
|
+
if (!session) {
|
|
334
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
335
|
+
}
|
|
336
|
+
session.hidden = false;
|
|
337
|
+
await this.persistSession(session);
|
|
338
|
+
this.emit('session_updated', session);
|
|
339
|
+
console.log(`[Broker] Session unhidden: ${sessionId.slice(0, 8)}`);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Delete a session (permanent removal)
|
|
343
|
+
*/
|
|
344
|
+
async deleteSession(sessionId) {
|
|
345
|
+
const session = this.sessions.get(sessionId);
|
|
346
|
+
if (!session) {
|
|
347
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
348
|
+
}
|
|
349
|
+
// Remove from parent's child list if applicable
|
|
350
|
+
if (session.parentSessionId) {
|
|
351
|
+
const parent = this.sessions.get(session.parentSessionId);
|
|
352
|
+
if (parent) {
|
|
353
|
+
parent.childSessionIds = parent.childSessionIds.filter(id => id !== sessionId);
|
|
354
|
+
await this.persistSession(parent);
|
|
355
|
+
this.emit('session_updated', parent);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Remove from memory
|
|
359
|
+
this.sessions.delete(sessionId);
|
|
360
|
+
// Remove from persistence
|
|
361
|
+
if (this.persistence) {
|
|
362
|
+
try {
|
|
363
|
+
await this.persistence.deleteSession(sessionId);
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
console.error('[Broker] Failed to delete session from persistence:', err);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
console.log(`[Broker] Session deleted: ${sessionId.slice(0, 8)}`);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Hide all sessions
|
|
373
|
+
*/
|
|
374
|
+
async hideAllSessions() {
|
|
375
|
+
const sessions = Array.from(this.sessions.values());
|
|
376
|
+
for (const session of sessions) {
|
|
377
|
+
session.hidden = true;
|
|
378
|
+
await this.persistSession(session);
|
|
379
|
+
}
|
|
380
|
+
console.log(`[Broker] All sessions hidden (${sessions.length} total)`);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Delete all sessions
|
|
384
|
+
*/
|
|
385
|
+
async deleteAllSessions() {
|
|
386
|
+
const sessionIds = Array.from(this.sessions.keys());
|
|
387
|
+
// Clear memory
|
|
388
|
+
this.sessions.clear();
|
|
389
|
+
// Clear persistence
|
|
390
|
+
if (this.persistence) {
|
|
391
|
+
try {
|
|
392
|
+
await this.persistence.deleteAllSessions();
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
console.error('[Broker] Failed to delete all sessions from persistence:', err);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
console.log(`[Broker] All sessions deleted (${sessionIds.length} total)`);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Manually set parent session (user override for grouping)
|
|
402
|
+
*/
|
|
403
|
+
async setParentSession(sessionId, parentSessionId) {
|
|
404
|
+
const session = this.sessions.get(sessionId);
|
|
405
|
+
if (!session) {
|
|
406
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
407
|
+
}
|
|
408
|
+
// Remove from old parent's child list
|
|
409
|
+
if (session.parentSessionId) {
|
|
410
|
+
const oldParent = this.sessions.get(session.parentSessionId);
|
|
411
|
+
if (oldParent) {
|
|
412
|
+
oldParent.childSessionIds = oldParent.childSessionIds.filter(id => id !== sessionId);
|
|
413
|
+
await this.persistSession(oldParent);
|
|
414
|
+
this.emit('session_updated', oldParent);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Update session's parent
|
|
418
|
+
session.parentSessionId = parentSessionId || undefined;
|
|
419
|
+
session.manualParentSessionId = parentSessionId || undefined;
|
|
420
|
+
session.isUserInitiated = !parentSessionId; // No parent = user-initiated
|
|
421
|
+
// Add to new parent's child list
|
|
422
|
+
if (parentSessionId) {
|
|
423
|
+
const newParent = this.sessions.get(parentSessionId);
|
|
424
|
+
if (newParent && !newParent.childSessionIds.includes(sessionId)) {
|
|
425
|
+
newParent.childSessionIds.push(sessionId);
|
|
426
|
+
await this.persistSession(newParent);
|
|
427
|
+
this.emit('session_updated', newParent);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
await this.persistSession(session);
|
|
431
|
+
this.emit('session_updated', session);
|
|
432
|
+
console.log(`[Broker] Session ${sessionId.slice(0, 8)} parent set to ${parentSessionId?.slice(0, 8) || 'none'}`);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Toggle session pinned state
|
|
436
|
+
*/
|
|
437
|
+
async togglePin(sessionId) {
|
|
438
|
+
const session = this.sessions.get(sessionId);
|
|
439
|
+
if (!session) {
|
|
440
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
441
|
+
}
|
|
442
|
+
session.isPinned = !session.isPinned;
|
|
443
|
+
await this.persistSession(session);
|
|
444
|
+
this.emit('session_updated', session);
|
|
445
|
+
console.log(`[Broker] Session ${sessionId.slice(0, 8)} ${session.isPinned ? 'pinned' : 'unpinned'}`);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Set whether a session is user-initiated
|
|
449
|
+
*/
|
|
450
|
+
async setUserInitiated(sessionId, isUserInitiated) {
|
|
451
|
+
const session = this.sessions.get(sessionId);
|
|
452
|
+
if (!session) {
|
|
453
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
454
|
+
}
|
|
455
|
+
session.isUserInitiated = isUserInitiated;
|
|
456
|
+
await this.persistSession(session);
|
|
457
|
+
this.emit('session_updated', session);
|
|
458
|
+
console.log(`[Broker] Session ${sessionId.slice(0, 8)} marked as ${isUserInitiated ? 'user-initiated' : 'sub-agent'}`);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Rename a session (set custom label)
|
|
462
|
+
*/
|
|
463
|
+
async renameSession(sessionId, label) {
|
|
464
|
+
const session = this.sessions.get(sessionId);
|
|
465
|
+
if (!session) {
|
|
466
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
467
|
+
}
|
|
468
|
+
// Empty string clears the label
|
|
469
|
+
session.label = label.trim() || undefined;
|
|
470
|
+
await this.persistSession(session);
|
|
471
|
+
this.emit('session_updated', session);
|
|
472
|
+
console.log(`[Broker] Session ${sessionId.slice(0, 8)} renamed to "${session.label || '(cleared)'}"`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
exports.EventBroker = EventBroker;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Server CLI Entry Point
|
|
5
|
+
*
|
|
6
|
+
* This is the entry point for running the UI server as a standalone process.
|
|
7
|
+
* Used when starting the server in detached mode.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const commander_1 = require("commander");
|
|
45
|
+
const index_js_1 = require("./index.js");
|
|
46
|
+
const monitor_js_1 = require("../../types/monitor.js");
|
|
47
|
+
const PATHS = (0, monitor_js_1.getDefaultMonitorPaths)();
|
|
48
|
+
const program = new commander_1.Command();
|
|
49
|
+
program
|
|
50
|
+
.name('contextuate-server')
|
|
51
|
+
.description('Contextuate Monitor UI Server')
|
|
52
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
53
|
+
.option('-p, --port <port>', 'HTTP port override', parseInt)
|
|
54
|
+
.option('-w, --ws-port <port>', 'WebSocket port override', parseInt)
|
|
55
|
+
.parse(process.argv);
|
|
56
|
+
const options = program.opts();
|
|
57
|
+
async function main() {
|
|
58
|
+
if (!options.config) {
|
|
59
|
+
console.error('[Error] Config file path is required (--config)');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
// Load configuration
|
|
63
|
+
let config;
|
|
64
|
+
try {
|
|
65
|
+
const configContent = await fs.promises.readFile(options.config, 'utf-8');
|
|
66
|
+
config = JSON.parse(configContent);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(`[Error] Failed to load config: ${err.message}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
// Apply command-line overrides
|
|
73
|
+
if (options.port) {
|
|
74
|
+
config.server.port = options.port;
|
|
75
|
+
}
|
|
76
|
+
if (options.wsPort) {
|
|
77
|
+
config.server.wsPort = options.wsPort;
|
|
78
|
+
}
|
|
79
|
+
// Create and start server
|
|
80
|
+
const server = await (0, index_js_1.createMonitorServer)({
|
|
81
|
+
config,
|
|
82
|
+
dataDir: PATHS.baseDir
|
|
83
|
+
});
|
|
84
|
+
await server.start();
|
|
85
|
+
console.log(`[Server] UI server running on http://localhost:${config.server.port}`);
|
|
86
|
+
// Handle shutdown signals
|
|
87
|
+
const shutdown = async () => {
|
|
88
|
+
console.log('\n[Info] Shutting down server...');
|
|
89
|
+
await server.stop();
|
|
90
|
+
process.exit(0);
|
|
91
|
+
};
|
|
92
|
+
process.on('SIGINT', shutdown);
|
|
93
|
+
process.on('SIGTERM', shutdown);
|
|
94
|
+
}
|
|
95
|
+
main().catch((err) => {
|
|
96
|
+
console.error('[Error] Fatal error:', err);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify HTTP Server
|
|
3
|
+
*
|
|
4
|
+
* Serves the Vue UI and provides REST API endpoints
|
|
5
|
+
* for the monitor dashboard.
|
|
6
|
+
*/
|
|
7
|
+
import { FastifyInstance } from 'fastify';
|
|
8
|
+
import type { EventBroker } from './broker';
|
|
9
|
+
import type { PersistenceStore } from '../../types/monitor';
|
|
10
|
+
export interface FastifyServerOptions {
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
broker: EventBroker;
|
|
14
|
+
persistence?: PersistenceStore;
|
|
15
|
+
}
|
|
16
|
+
export declare function createFastifyServer(options: FastifyServerOptions): Promise<FastifyInstance>;
|