agileflow 2.99.7 → 3.0.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/CHANGELOG.md +10 -0
- package/lib/cache-provider.js +155 -0
- package/lib/codebase-indexer.js +1 -1
- package/lib/content-sanitizer.js +1 -0
- package/lib/dashboard-protocol.js +25 -0
- package/lib/dashboard-server.js +184 -133
- package/lib/errors.js +18 -0
- package/lib/file-cache.js +1 -1
- package/lib/flag-detection.js +11 -20
- package/lib/git-operations.js +15 -33
- package/lib/merge-operations.js +40 -34
- package/lib/process-executor.js +199 -0
- package/lib/registry-cache.js +13 -47
- package/lib/skill-loader.js +206 -0
- package/lib/smart-json-file.js +2 -4
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +13 -12
- package/scripts/agileflow-statusline.sh +30 -0
- package/scripts/agileflow-welcome.js +181 -212
- package/scripts/auto-self-improve.js +3 -3
- package/scripts/claude-smart.sh +67 -0
- package/scripts/claude-tmux.sh +248 -170
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +5 -6
- package/scripts/lib/configure-features.js +44 -0
- package/scripts/lib/configure-repair.js +5 -6
- package/scripts/lib/configure-utils.js +2 -3
- package/scripts/lib/context-formatter.js +87 -8
- package/scripts/lib/damage-control-utils.js +37 -3
- package/scripts/lib/file-lock.js +392 -0
- package/scripts/lib/ideation-index.js +2 -5
- package/scripts/lib/lifecycle-detector.js +123 -0
- package/scripts/lib/process-cleanup.js +55 -81
- package/scripts/lib/scale-detector.js +357 -0
- package/scripts/lib/signal-detectors.js +779 -0
- package/scripts/lib/story-state-machine.js +1 -1
- package/scripts/lib/sync-ideation-status.js +2 -3
- package/scripts/lib/task-registry.js +7 -1
- package/scripts/lib/team-events.js +357 -0
- package/scripts/messaging-bridge.js +79 -36
- package/scripts/migrate-ideation-index.js +37 -14
- package/scripts/obtain-context.js +37 -19
- package/scripts/ralph-loop.js +3 -4
- package/scripts/smart-detect.js +390 -0
- package/scripts/team-manager.js +174 -30
- package/src/core/commands/audit.md +13 -11
- package/src/core/commands/babysit.md +162 -115
- package/src/core/commands/changelog.md +21 -4
- package/src/core/commands/configure.md +105 -2
- package/src/core/commands/debt.md +12 -2
- package/src/core/commands/feedback.md +7 -6
- package/src/core/commands/ideate/history.md +1 -1
- package/src/core/commands/ideate/new.md +5 -5
- package/src/core/commands/logic/audit.md +2 -2
- package/src/core/commands/pr.md +7 -6
- package/src/core/commands/research/analyze.md +28 -20
- package/src/core/commands/research/ask.md +43 -0
- package/src/core/commands/research/import.md +29 -21
- package/src/core/commands/research/list.md +8 -7
- package/src/core/commands/research/synthesize.md +356 -20
- package/src/core/commands/research/view.md +8 -5
- package/src/core/commands/review.md +24 -6
- package/src/core/commands/skill/create.md +34 -0
- package/tools/cli/lib/docs-setup.js +4 -0
package/lib/dashboard-server.js
CHANGED
|
@@ -21,32 +21,17 @@
|
|
|
21
21
|
|
|
22
22
|
'use strict';
|
|
23
23
|
|
|
24
|
-
const http = require('http');
|
|
25
|
-
const crypto = require('crypto');
|
|
26
24
|
const { EventEmitter } = require('events');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
createAutomationStatus,
|
|
38
|
-
createAutomationResult,
|
|
39
|
-
createInboxList,
|
|
40
|
-
createInboxItem,
|
|
41
|
-
createStatusUpdate,
|
|
42
|
-
createSessionList,
|
|
43
|
-
parseInboundMessage,
|
|
44
|
-
serializeMessage,
|
|
45
|
-
} = require('./dashboard-protocol');
|
|
46
|
-
const { getProjectRoot, isAgileflowProject, getAgentsDir } = require('./paths');
|
|
47
|
-
const { validatePath } = require('./validate-paths');
|
|
48
|
-
const { execFileSync, spawn } = require('child_process');
|
|
49
|
-
const os = require('os');
|
|
25
|
+
|
|
26
|
+
// Lazy-loaded dependencies - deferred until first use
|
|
27
|
+
let _http, _crypto, _protocol, _paths, _validatePaths, _childProcess;
|
|
28
|
+
|
|
29
|
+
function getHttp() { if (!_http) _http = require('http'); return _http; }
|
|
30
|
+
function getCrypto() { if (!_crypto) _crypto = require('crypto'); return _crypto; }
|
|
31
|
+
function getProtocol() { if (!_protocol) _protocol = require('./dashboard-protocol'); return _protocol; }
|
|
32
|
+
function getPaths() { if (!_paths) _paths = require('./paths'); return _paths; }
|
|
33
|
+
function getValidatePaths() { if (!_validatePaths) _validatePaths = require('./validate-paths'); return _validatePaths; }
|
|
34
|
+
function getChildProcess() { if (!_childProcess) _childProcess = require('child_process'); return _childProcess; }
|
|
50
35
|
|
|
51
36
|
// Lazy-load automation modules to avoid circular dependencies
|
|
52
37
|
let AutomationRegistry = null;
|
|
@@ -142,7 +127,7 @@ class DashboardSession {
|
|
|
142
127
|
send(message) {
|
|
143
128
|
if (this.ws && this.ws.writable) {
|
|
144
129
|
try {
|
|
145
|
-
const frame = encodeWebSocketFrame(serializeMessage(message));
|
|
130
|
+
const frame = encodeWebSocketFrame(getProtocol().serializeMessage(message));
|
|
146
131
|
this.ws.write(frame);
|
|
147
132
|
this.lastActivity = new Date();
|
|
148
133
|
} catch (error) {
|
|
@@ -180,7 +165,7 @@ class DashboardSession {
|
|
|
180
165
|
setState(state) {
|
|
181
166
|
this.state = state;
|
|
182
167
|
this.send(
|
|
183
|
-
createSessionState(this.id, state, {
|
|
168
|
+
getProtocol().createSessionState(this.id, state, {
|
|
184
169
|
messageCount: this.messages.length,
|
|
185
170
|
lastActivity: this.lastActivity.toISOString(),
|
|
186
171
|
})
|
|
@@ -249,13 +234,13 @@ class TerminalInstance {
|
|
|
249
234
|
|
|
250
235
|
this.pty.onData(data => {
|
|
251
236
|
if (!this.closed) {
|
|
252
|
-
this.session.send(createTerminalOutput(this.id, data));
|
|
237
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, data));
|
|
253
238
|
}
|
|
254
239
|
});
|
|
255
240
|
|
|
256
241
|
this.pty.onExit(({ exitCode }) => {
|
|
257
242
|
this.closed = true;
|
|
258
|
-
this.session.send(createTerminalExit(this.id, exitCode));
|
|
243
|
+
this.session.send(getProtocol().createTerminalExit(this.id, exitCode));
|
|
259
244
|
});
|
|
260
245
|
|
|
261
246
|
return true;
|
|
@@ -275,7 +260,7 @@ class TerminalInstance {
|
|
|
275
260
|
const filteredEnv = this._getFilteredEnv();
|
|
276
261
|
|
|
277
262
|
// Use bash with interactive flag for better compatibility
|
|
278
|
-
this.pty = spawn(this.shell, ['-i'], {
|
|
263
|
+
this.pty = getChildProcess().spawn(this.shell, ['-i'], {
|
|
279
264
|
cwd: this.cwd,
|
|
280
265
|
env: {
|
|
281
266
|
...filteredEnv,
|
|
@@ -291,25 +276,25 @@ class TerminalInstance {
|
|
|
291
276
|
|
|
292
277
|
this.pty.stdout.on('data', data => {
|
|
293
278
|
if (!this.closed) {
|
|
294
|
-
this.session.send(createTerminalOutput(this.id, data.toString()));
|
|
279
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, data.toString()));
|
|
295
280
|
}
|
|
296
281
|
});
|
|
297
282
|
|
|
298
283
|
this.pty.stderr.on('data', data => {
|
|
299
284
|
if (!this.closed) {
|
|
300
|
-
this.session.send(createTerminalOutput(this.id, data.toString()));
|
|
285
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, data.toString()));
|
|
301
286
|
}
|
|
302
287
|
});
|
|
303
288
|
|
|
304
289
|
this.pty.on('close', exitCode => {
|
|
305
290
|
this.closed = true;
|
|
306
|
-
this.session.send(createTerminalExit(this.id, exitCode));
|
|
291
|
+
this.session.send(getProtocol().createTerminalExit(this.id, exitCode));
|
|
307
292
|
});
|
|
308
293
|
|
|
309
294
|
this.pty.on('error', error => {
|
|
310
295
|
console.error('[Terminal] Shell error:', error.message);
|
|
311
296
|
if (!this.closed) {
|
|
312
|
-
this.session.send(createTerminalOutput(this.id, `\r\nError: ${error.message}\r\n`));
|
|
297
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, `\r\nError: ${error.message}\r\n`));
|
|
313
298
|
}
|
|
314
299
|
});
|
|
315
300
|
|
|
@@ -318,7 +303,7 @@ class TerminalInstance {
|
|
|
318
303
|
if (!this.closed) {
|
|
319
304
|
const welcomeMsg = `\x1b[32mAgileFlow Terminal\x1b[0m (basic mode - node-pty not available)\r\n`;
|
|
320
305
|
const cwdMsg = `Working directory: ${this.cwd}\r\n\r\n`;
|
|
321
|
-
this.session.send(createTerminalOutput(this.id, welcomeMsg + cwdMsg));
|
|
306
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, welcomeMsg + cwdMsg));
|
|
322
307
|
}
|
|
323
308
|
}, 100);
|
|
324
309
|
|
|
@@ -355,7 +340,7 @@ class TerminalInstance {
|
|
|
355
340
|
}
|
|
356
341
|
|
|
357
342
|
// Echo to terminal
|
|
358
|
-
this.session.send(createTerminalOutput(this.id, echoData));
|
|
343
|
+
this.session.send(getProtocol().createTerminalOutput(this.id, echoData));
|
|
359
344
|
|
|
360
345
|
// Send to shell stdin
|
|
361
346
|
this.pty.stdin.write(data);
|
|
@@ -406,7 +391,7 @@ class TerminalManager {
|
|
|
406
391
|
* @returns {string} - Terminal ID
|
|
407
392
|
*/
|
|
408
393
|
createTerminal(session, options = {}) {
|
|
409
|
-
const terminalId = options.id ||
|
|
394
|
+
const terminalId = options.id || getCrypto().randomBytes(8).toString('hex');
|
|
410
395
|
const terminal = new TerminalInstance(terminalId, session, {
|
|
411
396
|
cwd: options.cwd || session.projectRoot,
|
|
412
397
|
cols: options.cols,
|
|
@@ -507,13 +492,13 @@ class DashboardServer extends EventEmitter {
|
|
|
507
492
|
|
|
508
493
|
this.port = options.port || DEFAULT_PORT;
|
|
509
494
|
this.host = options.host || DEFAULT_HOST;
|
|
510
|
-
this.projectRoot = options.projectRoot || getProjectRoot();
|
|
495
|
+
this.projectRoot = options.projectRoot || getPaths().getProjectRoot();
|
|
511
496
|
|
|
512
497
|
// Auth is on by default - auto-generate key if not provided
|
|
513
498
|
// Set requireAuth: false explicitly to disable
|
|
514
499
|
this.requireAuth = options.requireAuth !== false;
|
|
515
500
|
this.apiKey =
|
|
516
|
-
options.apiKey || (this.requireAuth ?
|
|
501
|
+
options.apiKey || (this.requireAuth ? getCrypto().randomBytes(32).toString('hex') : null);
|
|
517
502
|
|
|
518
503
|
// Session management
|
|
519
504
|
this.sessions = new Map();
|
|
@@ -536,12 +521,15 @@ class DashboardServer extends EventEmitter {
|
|
|
536
521
|
this.httpServer = null;
|
|
537
522
|
|
|
538
523
|
// Validate project
|
|
539
|
-
if (!isAgileflowProject(this.projectRoot)) {
|
|
524
|
+
if (!getPaths().isAgileflowProject(this.projectRoot)) {
|
|
540
525
|
throw new Error(`Not an AgileFlow project: ${this.projectRoot}`);
|
|
541
526
|
}
|
|
542
527
|
|
|
543
528
|
// Initialize automation registry lazily
|
|
544
529
|
this._initAutomations();
|
|
530
|
+
|
|
531
|
+
// Listen for team metrics saves to broadcast to clients
|
|
532
|
+
this._initTeamMetricsListener();
|
|
545
533
|
}
|
|
546
534
|
|
|
547
535
|
/**
|
|
@@ -555,12 +543,12 @@ class DashboardServer extends EventEmitter {
|
|
|
555
543
|
// Listen to runner events
|
|
556
544
|
this._automationRunner.on('started', ({ automationId }) => {
|
|
557
545
|
this._runningAutomations.set(automationId, { startTime: Date.now() });
|
|
558
|
-
this.broadcast(createAutomationStatus(automationId, 'running'));
|
|
546
|
+
this.broadcast(getProtocol().createAutomationStatus(automationId, 'running'));
|
|
559
547
|
});
|
|
560
548
|
|
|
561
549
|
this._automationRunner.on('completed', ({ automationId, result }) => {
|
|
562
550
|
this._runningAutomations.delete(automationId);
|
|
563
|
-
this.broadcast(createAutomationStatus(automationId, 'completed', result));
|
|
551
|
+
this.broadcast(getProtocol().createAutomationStatus(automationId, 'completed', result));
|
|
564
552
|
|
|
565
553
|
// Add result to inbox if it has output or changes
|
|
566
554
|
if (result.output || result.changes) {
|
|
@@ -570,7 +558,7 @@ class DashboardServer extends EventEmitter {
|
|
|
570
558
|
|
|
571
559
|
this._automationRunner.on('failed', ({ automationId, result }) => {
|
|
572
560
|
this._runningAutomations.delete(automationId);
|
|
573
|
-
this.broadcast(createAutomationStatus(automationId, 'error', { error: result.error }));
|
|
561
|
+
this.broadcast(getProtocol().createAutomationStatus(automationId, 'error', { error: result.error }));
|
|
574
562
|
|
|
575
563
|
// Add failure to inbox
|
|
576
564
|
this._addToInbox(automationId, result);
|
|
@@ -607,7 +595,7 @@ class DashboardServer extends EventEmitter {
|
|
|
607
595
|
};
|
|
608
596
|
|
|
609
597
|
this._inbox.set(itemId, item);
|
|
610
|
-
this.broadcast(createInboxItem(item));
|
|
598
|
+
this.broadcast(getProtocol().createInboxItem(item));
|
|
611
599
|
}
|
|
612
600
|
|
|
613
601
|
/**
|
|
@@ -623,7 +611,7 @@ class DashboardServer extends EventEmitter {
|
|
|
623
611
|
'Cache-Control': 'no-store',
|
|
624
612
|
};
|
|
625
613
|
|
|
626
|
-
this.httpServer =
|
|
614
|
+
this.httpServer = getHttp().createServer((req, res) => {
|
|
627
615
|
// Simple health check endpoint
|
|
628
616
|
if (req.url === '/health') {
|
|
629
617
|
res.writeHead(200, securityHeaders);
|
|
@@ -713,7 +701,7 @@ class DashboardServer extends EventEmitter {
|
|
|
713
701
|
const providedBuffer = Buffer.from(providedKey, 'utf8');
|
|
714
702
|
if (
|
|
715
703
|
keyBuffer.length !== providedBuffer.length ||
|
|
716
|
-
!
|
|
704
|
+
!getCrypto().timingSafeEqual(keyBuffer, providedBuffer)
|
|
717
705
|
) {
|
|
718
706
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
719
707
|
socket.destroy();
|
|
@@ -778,7 +766,7 @@ class DashboardServer extends EventEmitter {
|
|
|
778
766
|
}
|
|
779
767
|
|
|
780
768
|
// Generate new session ID
|
|
781
|
-
return
|
|
769
|
+
return getCrypto().randomBytes(16).toString('hex');
|
|
782
770
|
}
|
|
783
771
|
|
|
784
772
|
/**
|
|
@@ -801,7 +789,7 @@ class DashboardServer extends EventEmitter {
|
|
|
801
789
|
|
|
802
790
|
// Send initial state
|
|
803
791
|
session.send(
|
|
804
|
-
createSessionState(sessionId, 'connected', {
|
|
792
|
+
getProtocol().createSessionState(sessionId, 'connected', {
|
|
805
793
|
resumed: isResume,
|
|
806
794
|
messageCount: session.messages.length,
|
|
807
795
|
project: require('path').basename(this.projectRoot),
|
|
@@ -814,6 +802,9 @@ class DashboardServer extends EventEmitter {
|
|
|
814
802
|
// Send project status (stories/epics)
|
|
815
803
|
this.sendStatusUpdate(session);
|
|
816
804
|
|
|
805
|
+
// Send team metrics
|
|
806
|
+
this.sendTeamMetrics(session);
|
|
807
|
+
|
|
817
808
|
// Send session list with sync info
|
|
818
809
|
this.sendSessionList(session);
|
|
819
810
|
|
|
@@ -874,83 +865,83 @@ class DashboardServer extends EventEmitter {
|
|
|
874
865
|
handleMessage(session, data) {
|
|
875
866
|
// Rate limit incoming messages
|
|
876
867
|
if (!session.checkRateLimit()) {
|
|
877
|
-
session.send(createError('RATE_LIMITED', 'Too many messages, please slow down'));
|
|
868
|
+
session.send(getProtocol().createError('RATE_LIMITED', 'Too many messages, please slow down'));
|
|
878
869
|
return;
|
|
879
870
|
}
|
|
880
871
|
|
|
881
|
-
const message = parseInboundMessage(data);
|
|
872
|
+
const message = getProtocol().parseInboundMessage(data);
|
|
882
873
|
if (!message) {
|
|
883
|
-
session.send(createError('INVALID_MESSAGE', 'Failed to parse message'));
|
|
874
|
+
session.send(getProtocol().createError('INVALID_MESSAGE', 'Failed to parse message'));
|
|
884
875
|
return;
|
|
885
876
|
}
|
|
886
877
|
|
|
887
878
|
console.log(`[Session ${session.id}] Received: ${message.type}`);
|
|
888
879
|
|
|
889
880
|
switch (message.type) {
|
|
890
|
-
case InboundMessageType.MESSAGE:
|
|
881
|
+
case getProtocol().InboundMessageType.MESSAGE:
|
|
891
882
|
this.handleUserMessage(session, message);
|
|
892
883
|
break;
|
|
893
884
|
|
|
894
|
-
case InboundMessageType.CANCEL:
|
|
885
|
+
case getProtocol().InboundMessageType.CANCEL:
|
|
895
886
|
this.handleCancel(session);
|
|
896
887
|
break;
|
|
897
888
|
|
|
898
|
-
case InboundMessageType.REFRESH:
|
|
889
|
+
case getProtocol().InboundMessageType.REFRESH:
|
|
899
890
|
this.handleRefresh(session, message);
|
|
900
891
|
break;
|
|
901
892
|
|
|
902
|
-
case InboundMessageType.GIT_STAGE:
|
|
903
|
-
case InboundMessageType.GIT_UNSTAGE:
|
|
904
|
-
case InboundMessageType.GIT_REVERT:
|
|
905
|
-
case InboundMessageType.GIT_COMMIT:
|
|
893
|
+
case getProtocol().InboundMessageType.GIT_STAGE:
|
|
894
|
+
case getProtocol().InboundMessageType.GIT_UNSTAGE:
|
|
895
|
+
case getProtocol().InboundMessageType.GIT_REVERT:
|
|
896
|
+
case getProtocol().InboundMessageType.GIT_COMMIT:
|
|
906
897
|
this.handleGitAction(session, message);
|
|
907
898
|
break;
|
|
908
899
|
|
|
909
|
-
case InboundMessageType.GIT_DIFF_REQUEST:
|
|
900
|
+
case getProtocol().InboundMessageType.GIT_DIFF_REQUEST:
|
|
910
901
|
this.handleDiffRequest(session, message);
|
|
911
902
|
break;
|
|
912
903
|
|
|
913
|
-
case InboundMessageType.SESSION_CLOSE:
|
|
904
|
+
case getProtocol().InboundMessageType.SESSION_CLOSE:
|
|
914
905
|
this.closeSession(session.id);
|
|
915
906
|
break;
|
|
916
907
|
|
|
917
|
-
case InboundMessageType.TERMINAL_SPAWN:
|
|
908
|
+
case getProtocol().InboundMessageType.TERMINAL_SPAWN:
|
|
918
909
|
this.handleTerminalSpawn(session, message);
|
|
919
910
|
break;
|
|
920
911
|
|
|
921
|
-
case InboundMessageType.TERMINAL_INPUT:
|
|
912
|
+
case getProtocol().InboundMessageType.TERMINAL_INPUT:
|
|
922
913
|
this.handleTerminalInput(session, message);
|
|
923
914
|
break;
|
|
924
915
|
|
|
925
|
-
case InboundMessageType.TERMINAL_RESIZE:
|
|
916
|
+
case getProtocol().InboundMessageType.TERMINAL_RESIZE:
|
|
926
917
|
this.handleTerminalResize(session, message);
|
|
927
918
|
break;
|
|
928
919
|
|
|
929
|
-
case InboundMessageType.TERMINAL_CLOSE:
|
|
920
|
+
case getProtocol().InboundMessageType.TERMINAL_CLOSE:
|
|
930
921
|
this.handleTerminalClose(session, message);
|
|
931
922
|
break;
|
|
932
923
|
|
|
933
|
-
case InboundMessageType.AUTOMATION_LIST_REQUEST:
|
|
924
|
+
case getProtocol().InboundMessageType.AUTOMATION_LIST_REQUEST:
|
|
934
925
|
this.sendAutomationList(session);
|
|
935
926
|
break;
|
|
936
927
|
|
|
937
|
-
case InboundMessageType.AUTOMATION_RUN:
|
|
928
|
+
case getProtocol().InboundMessageType.AUTOMATION_RUN:
|
|
938
929
|
this.handleAutomationRun(session, message);
|
|
939
930
|
break;
|
|
940
931
|
|
|
941
|
-
case InboundMessageType.AUTOMATION_STOP:
|
|
932
|
+
case getProtocol().InboundMessageType.AUTOMATION_STOP:
|
|
942
933
|
this.handleAutomationStop(session, message);
|
|
943
934
|
break;
|
|
944
935
|
|
|
945
|
-
case InboundMessageType.INBOX_LIST_REQUEST:
|
|
936
|
+
case getProtocol().InboundMessageType.INBOX_LIST_REQUEST:
|
|
946
937
|
this.sendInboxList(session);
|
|
947
938
|
break;
|
|
948
939
|
|
|
949
|
-
case InboundMessageType.INBOX_ACTION:
|
|
940
|
+
case getProtocol().InboundMessageType.INBOX_ACTION:
|
|
950
941
|
this.handleInboxAction(session, message);
|
|
951
942
|
break;
|
|
952
943
|
|
|
953
|
-
case InboundMessageType.OPEN_FILE:
|
|
944
|
+
case getProtocol().InboundMessageType.OPEN_FILE:
|
|
954
945
|
this.handleOpenFile(session, message);
|
|
955
946
|
break;
|
|
956
947
|
|
|
@@ -966,7 +957,7 @@ class DashboardServer extends EventEmitter {
|
|
|
966
957
|
handleUserMessage(session, message) {
|
|
967
958
|
const content = message.content?.trim();
|
|
968
959
|
if (!content) {
|
|
969
|
-
session.send(createError('EMPTY_MESSAGE', 'Message content is empty'));
|
|
960
|
+
session.send(getProtocol().createError('EMPTY_MESSAGE', 'Message content is empty'));
|
|
970
961
|
return;
|
|
971
962
|
}
|
|
972
963
|
|
|
@@ -985,7 +976,7 @@ class DashboardServer extends EventEmitter {
|
|
|
985
976
|
*/
|
|
986
977
|
handleCancel(session) {
|
|
987
978
|
session.setState('idle');
|
|
988
|
-
session.send(createNotification('info', 'Cancelled', 'Operation cancelled'));
|
|
979
|
+
session.send(getProtocol().createNotification('info', 'Cancelled', 'Operation cancelled'));
|
|
989
980
|
this.emit('user:cancel', session);
|
|
990
981
|
}
|
|
991
982
|
|
|
@@ -1015,9 +1006,13 @@ class DashboardServer extends EventEmitter {
|
|
|
1015
1006
|
case 'inbox':
|
|
1016
1007
|
this.sendInboxList(session);
|
|
1017
1008
|
break;
|
|
1009
|
+
case 'team_metrics':
|
|
1010
|
+
this.sendTeamMetrics(session);
|
|
1011
|
+
break;
|
|
1018
1012
|
default:
|
|
1019
1013
|
this.sendGitStatus(session);
|
|
1020
1014
|
this.sendStatusUpdate(session);
|
|
1015
|
+
this.sendTeamMetrics(session);
|
|
1021
1016
|
this.sendSessionList(session);
|
|
1022
1017
|
this.sendAutomationList(session);
|
|
1023
1018
|
this.sendInboxList(session);
|
|
@@ -1035,12 +1030,12 @@ class DashboardServer extends EventEmitter {
|
|
|
1035
1030
|
if (files && files.length > 0) {
|
|
1036
1031
|
for (const f of files) {
|
|
1037
1032
|
if (typeof f !== 'string' || f.includes('\0')) {
|
|
1038
|
-
session.send(createError('GIT_ERROR', 'Invalid file path'));
|
|
1033
|
+
session.send(getProtocol().createError('GIT_ERROR', 'Invalid file path'));
|
|
1039
1034
|
return;
|
|
1040
1035
|
}
|
|
1041
1036
|
const resolved = require('path').resolve(this.projectRoot, f);
|
|
1042
1037
|
if (!resolved.startsWith(this.projectRoot)) {
|
|
1043
|
-
session.send(createError('GIT_ERROR', 'File path outside project'));
|
|
1038
|
+
session.send(getProtocol().createError('GIT_ERROR', 'File path outside project'));
|
|
1044
1039
|
return;
|
|
1045
1040
|
}
|
|
1046
1041
|
}
|
|
@@ -1053,7 +1048,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1053
1048
|
commitMessage.length > 10000 ||
|
|
1054
1049
|
commitMessage.includes('\0')
|
|
1055
1050
|
) {
|
|
1056
|
-
session.send(createError('GIT_ERROR', 'Invalid commit message'));
|
|
1051
|
+
session.send(getProtocol().createError('GIT_ERROR', 'Invalid commit message'));
|
|
1057
1052
|
return;
|
|
1058
1053
|
}
|
|
1059
1054
|
}
|
|
@@ -1062,40 +1057,40 @@ class DashboardServer extends EventEmitter {
|
|
|
1062
1057
|
|
|
1063
1058
|
try {
|
|
1064
1059
|
switch (type) {
|
|
1065
|
-
case InboundMessageType.GIT_STAGE:
|
|
1060
|
+
case getProtocol().InboundMessageType.GIT_STAGE:
|
|
1066
1061
|
if (fileArgs) {
|
|
1067
|
-
execFileSync('git', ['add', '--', ...fileArgs], { cwd: this.projectRoot });
|
|
1062
|
+
getChildProcess().execFileSync('git', ['add', '--', ...fileArgs], { cwd: this.projectRoot });
|
|
1068
1063
|
} else {
|
|
1069
|
-
execFileSync('git', ['add', '-A'], { cwd: this.projectRoot });
|
|
1064
|
+
getChildProcess().execFileSync('git', ['add', '-A'], { cwd: this.projectRoot });
|
|
1070
1065
|
}
|
|
1071
1066
|
break;
|
|
1072
|
-
case InboundMessageType.GIT_UNSTAGE:
|
|
1067
|
+
case getProtocol().InboundMessageType.GIT_UNSTAGE:
|
|
1073
1068
|
if (fileArgs) {
|
|
1074
|
-
execFileSync('git', ['restore', '--staged', '--', ...fileArgs], {
|
|
1069
|
+
getChildProcess().execFileSync('git', ['restore', '--staged', '--', ...fileArgs], {
|
|
1075
1070
|
cwd: this.projectRoot,
|
|
1076
1071
|
});
|
|
1077
1072
|
} else {
|
|
1078
|
-
execFileSync('git', ['restore', '--staged', '.'], { cwd: this.projectRoot });
|
|
1073
|
+
getChildProcess().execFileSync('git', ['restore', '--staged', '.'], { cwd: this.projectRoot });
|
|
1079
1074
|
}
|
|
1080
1075
|
break;
|
|
1081
|
-
case InboundMessageType.GIT_REVERT:
|
|
1076
|
+
case getProtocol().InboundMessageType.GIT_REVERT:
|
|
1082
1077
|
if (fileArgs) {
|
|
1083
|
-
execFileSync('git', ['checkout', '--', ...fileArgs], { cwd: this.projectRoot });
|
|
1078
|
+
getChildProcess().execFileSync('git', ['checkout', '--', ...fileArgs], { cwd: this.projectRoot });
|
|
1084
1079
|
}
|
|
1085
1080
|
break;
|
|
1086
|
-
case InboundMessageType.GIT_COMMIT:
|
|
1081
|
+
case getProtocol().InboundMessageType.GIT_COMMIT:
|
|
1087
1082
|
if (commitMessage) {
|
|
1088
|
-
execFileSync('git', ['commit', '-m', commitMessage], { cwd: this.projectRoot });
|
|
1083
|
+
getChildProcess().execFileSync('git', ['commit', '-m', commitMessage], { cwd: this.projectRoot });
|
|
1089
1084
|
}
|
|
1090
1085
|
break;
|
|
1091
1086
|
}
|
|
1092
1087
|
|
|
1093
1088
|
// Send updated git status
|
|
1094
1089
|
this.sendGitStatus(session);
|
|
1095
|
-
session.send(createNotification('success', 'Git', `${type.replace('git_', '')} completed`));
|
|
1090
|
+
session.send(getProtocol().createNotification('success', 'Git', `${type.replace('git_', '')} completed`));
|
|
1096
1091
|
} catch (error) {
|
|
1097
1092
|
console.error('[Git Error]', error.message);
|
|
1098
|
-
session.send(createError('GIT_ERROR', 'Git operation failed'));
|
|
1093
|
+
session.send(getProtocol().createError('GIT_ERROR', 'Git operation failed'));
|
|
1099
1094
|
}
|
|
1100
1095
|
}
|
|
1101
1096
|
|
|
@@ -1106,7 +1101,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1106
1101
|
try {
|
|
1107
1102
|
const status = this.getGitStatus();
|
|
1108
1103
|
session.send({
|
|
1109
|
-
type: OutboundMessageType.GIT_STATUS,
|
|
1104
|
+
type: getProtocol().OutboundMessageType.GIT_STATUS,
|
|
1110
1105
|
...status,
|
|
1111
1106
|
timestamp: new Date().toISOString(),
|
|
1112
1107
|
});
|
|
@@ -1120,13 +1115,13 @@ class DashboardServer extends EventEmitter {
|
|
|
1120
1115
|
*/
|
|
1121
1116
|
getGitStatus() {
|
|
1122
1117
|
try {
|
|
1123
|
-
const branch = execFileSync('git', ['branch', '--show-current'], {
|
|
1118
|
+
const branch = getChildProcess().execFileSync('git', ['branch', '--show-current'], {
|
|
1124
1119
|
cwd: this.projectRoot,
|
|
1125
1120
|
encoding: 'utf8',
|
|
1126
1121
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1127
1122
|
}).trim();
|
|
1128
1123
|
|
|
1129
|
-
const statusOutput = execFileSync('git', ['status', '--porcelain'], {
|
|
1124
|
+
const statusOutput = getChildProcess().execFileSync('git', ['status', '--porcelain'], {
|
|
1130
1125
|
cwd: this.projectRoot,
|
|
1131
1126
|
encoding: 'utf8',
|
|
1132
1127
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -1185,7 +1180,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1185
1180
|
const { path: filePath, staged } = message;
|
|
1186
1181
|
|
|
1187
1182
|
if (!filePath) {
|
|
1188
|
-
session.send(createError('INVALID_REQUEST', 'File path is required'));
|
|
1183
|
+
session.send(getProtocol().createError('INVALID_REQUEST', 'File path is required'));
|
|
1189
1184
|
return;
|
|
1190
1185
|
}
|
|
1191
1186
|
|
|
@@ -1194,7 +1189,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1194
1189
|
const stats = this.parseDiffStats(diff);
|
|
1195
1190
|
|
|
1196
1191
|
session.send(
|
|
1197
|
-
createGitDiff(filePath, diff, {
|
|
1192
|
+
getProtocol().createGitDiff(filePath, diff, {
|
|
1198
1193
|
additions: stats.additions,
|
|
1199
1194
|
deletions: stats.deletions,
|
|
1200
1195
|
staged: !!staged,
|
|
@@ -1202,7 +1197,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1202
1197
|
);
|
|
1203
1198
|
} catch (error) {
|
|
1204
1199
|
console.error('[Diff Error]', error.message);
|
|
1205
|
-
session.send(createError('DIFF_ERROR', 'Failed to get diff'));
|
|
1200
|
+
session.send(getProtocol().createError('DIFF_ERROR', 'Failed to get diff'));
|
|
1206
1201
|
}
|
|
1207
1202
|
}
|
|
1208
1203
|
|
|
@@ -1214,7 +1209,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1214
1209
|
*/
|
|
1215
1210
|
getFileDiff(filePath, staged = false) {
|
|
1216
1211
|
// Validate filePath stays within project root
|
|
1217
|
-
const pathResult = validatePath(filePath, this.projectRoot, { allowSymlinks: true });
|
|
1212
|
+
const pathResult = getValidatePaths().validatePath(filePath, this.projectRoot, { allowSymlinks: true });
|
|
1218
1213
|
if (!pathResult.ok) {
|
|
1219
1214
|
return '';
|
|
1220
1215
|
}
|
|
@@ -1222,14 +1217,14 @@ class DashboardServer extends EventEmitter {
|
|
|
1222
1217
|
try {
|
|
1223
1218
|
const diffArgs = staged ? ['diff', '--cached', '--', filePath] : ['diff', '--', filePath];
|
|
1224
1219
|
|
|
1225
|
-
const diff = execFileSync('git', diffArgs, {
|
|
1220
|
+
const diff = getChildProcess().execFileSync('git', diffArgs, {
|
|
1226
1221
|
cwd: this.projectRoot,
|
|
1227
1222
|
encoding: 'utf8',
|
|
1228
1223
|
});
|
|
1229
1224
|
|
|
1230
1225
|
// If no diff, file might be untracked - show entire file content as addition
|
|
1231
1226
|
if (!diff && !staged) {
|
|
1232
|
-
const statusOutput = execFileSync('git', ['status', '--porcelain', '--', filePath], {
|
|
1227
|
+
const statusOutput = getChildProcess().execFileSync('git', ['status', '--porcelain', '--', filePath], {
|
|
1233
1228
|
cwd: this.projectRoot,
|
|
1234
1229
|
encoding: 'utf8',
|
|
1235
1230
|
}).trim();
|
|
@@ -1318,12 +1313,68 @@ class DashboardServer extends EventEmitter {
|
|
|
1318
1313
|
})),
|
|
1319
1314
|
};
|
|
1320
1315
|
|
|
1321
|
-
session.send(createStatusUpdate(summary));
|
|
1316
|
+
session.send(getProtocol().createStatusUpdate(summary));
|
|
1322
1317
|
} catch (error) {
|
|
1323
1318
|
console.error('[Status Update Error]', error.message);
|
|
1324
1319
|
}
|
|
1325
1320
|
}
|
|
1326
1321
|
|
|
1322
|
+
/**
|
|
1323
|
+
* Initialize listener for team metrics events
|
|
1324
|
+
*/
|
|
1325
|
+
_initTeamMetricsListener() {
|
|
1326
|
+
try {
|
|
1327
|
+
const { teamMetricsEmitter } = require('../scripts/lib/team-events');
|
|
1328
|
+
teamMetricsEmitter.on('metrics_saved', () => {
|
|
1329
|
+
this.broadcastTeamMetrics();
|
|
1330
|
+
});
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
// team-events not available - non-critical
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* Send team metrics to a single session
|
|
1338
|
+
*/
|
|
1339
|
+
sendTeamMetrics(session) {
|
|
1340
|
+
const path = require('path');
|
|
1341
|
+
const fs = require('fs');
|
|
1342
|
+
const sessionStatePath = path.join(this.projectRoot, 'docs', '09-agents', 'session-state.json');
|
|
1343
|
+
if (!fs.existsSync(sessionStatePath)) return;
|
|
1344
|
+
|
|
1345
|
+
try {
|
|
1346
|
+
const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
1347
|
+
const traces = (state.team_metrics && state.team_metrics.traces) || {};
|
|
1348
|
+
|
|
1349
|
+
for (const [traceId, metrics] of Object.entries(traces)) {
|
|
1350
|
+
session.send(getProtocol().createTeamMetrics(traceId, metrics));
|
|
1351
|
+
}
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
// Non-critical
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* Broadcast team metrics to all connected clients
|
|
1359
|
+
*/
|
|
1360
|
+
broadcastTeamMetrics() {
|
|
1361
|
+
const path = require('path');
|
|
1362
|
+
const fs = require('fs');
|
|
1363
|
+
const sessionStatePath = path.join(this.projectRoot, 'docs', '09-agents', 'session-state.json');
|
|
1364
|
+
if (!fs.existsSync(sessionStatePath)) return;
|
|
1365
|
+
|
|
1366
|
+
try {
|
|
1367
|
+
const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
1368
|
+
const traces = (state.team_metrics && state.team_metrics.traces) || {};
|
|
1369
|
+
|
|
1370
|
+
for (const [traceId, metrics] of Object.entries(traces)) {
|
|
1371
|
+
this.broadcast(getProtocol().createTeamMetrics(traceId, metrics));
|
|
1372
|
+
}
|
|
1373
|
+
} catch (error) {
|
|
1374
|
+
// Non-critical
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1327
1378
|
/**
|
|
1328
1379
|
* Send session list with sync status to dashboard
|
|
1329
1380
|
*/
|
|
@@ -1347,7 +1398,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1347
1398
|
// Get branch and sync status via git
|
|
1348
1399
|
try {
|
|
1349
1400
|
const cwd = s.metadata.worktreePath || this.projectRoot;
|
|
1350
|
-
entry.branch = execFileSync('git', ['branch', '--show-current'], {
|
|
1401
|
+
entry.branch = getChildProcess().execFileSync('git', ['branch', '--show-current'], {
|
|
1351
1402
|
cwd,
|
|
1352
1403
|
encoding: 'utf8',
|
|
1353
1404
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -1355,7 +1406,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1355
1406
|
|
|
1356
1407
|
// Get ahead/behind counts relative to upstream
|
|
1357
1408
|
try {
|
|
1358
|
-
const counts = execFileSync(
|
|
1409
|
+
const counts = getChildProcess().execFileSync(
|
|
1359
1410
|
'git',
|
|
1360
1411
|
['rev-list', '--left-right', '--count', 'HEAD...@{u}'],
|
|
1361
1412
|
{
|
|
@@ -1388,7 +1439,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1388
1439
|
sessions.push(entry);
|
|
1389
1440
|
}
|
|
1390
1441
|
|
|
1391
|
-
session.send(createSessionList(sessions));
|
|
1442
|
+
session.send(getProtocol().createSessionList(sessions));
|
|
1392
1443
|
}
|
|
1393
1444
|
|
|
1394
1445
|
/**
|
|
@@ -1398,14 +1449,14 @@ class DashboardServer extends EventEmitter {
|
|
|
1398
1449
|
const { path: filePath, line } = message;
|
|
1399
1450
|
|
|
1400
1451
|
if (!filePath || typeof filePath !== 'string') {
|
|
1401
|
-
session.send(createError('INVALID_REQUEST', 'File path is required'));
|
|
1452
|
+
session.send(getProtocol().createError('INVALID_REQUEST', 'File path is required'));
|
|
1402
1453
|
return;
|
|
1403
1454
|
}
|
|
1404
1455
|
|
|
1405
1456
|
// Validate the path stays within project root
|
|
1406
|
-
const pathResult = validatePath(filePath, this.projectRoot, { allowSymlinks: true });
|
|
1457
|
+
const pathResult = getValidatePaths().validatePath(filePath, this.projectRoot, { allowSymlinks: true });
|
|
1407
1458
|
if (!pathResult.ok) {
|
|
1408
|
-
session.send(createError('OPEN_FILE_ERROR', 'File path outside project'));
|
|
1459
|
+
session.send(getProtocol().createError('OPEN_FILE_ERROR', 'File path outside project'));
|
|
1409
1460
|
return;
|
|
1410
1461
|
}
|
|
1411
1462
|
|
|
@@ -1423,28 +1474,28 @@ class DashboardServer extends EventEmitter {
|
|
|
1423
1474
|
case 'cursor':
|
|
1424
1475
|
case 'windsurf': {
|
|
1425
1476
|
const gotoArg = lineNum ? `${fullPath}:${lineNum}` : fullPath;
|
|
1426
|
-
spawn(editor, ['--goto', gotoArg], { detached: true, stdio: 'ignore' }).unref();
|
|
1477
|
+
getChildProcess().spawn(editor, ['--goto', gotoArg], { detached: true, stdio: 'ignore' }).unref();
|
|
1427
1478
|
break;
|
|
1428
1479
|
}
|
|
1429
1480
|
case 'subl':
|
|
1430
1481
|
case 'sublime_text': {
|
|
1431
1482
|
const sublArg = lineNum ? `${fullPath}:${lineNum}` : fullPath;
|
|
1432
|
-
spawn(editor, [sublArg], { detached: true, stdio: 'ignore' }).unref();
|
|
1483
|
+
getChildProcess().spawn(editor, [sublArg], { detached: true, stdio: 'ignore' }).unref();
|
|
1433
1484
|
break;
|
|
1434
1485
|
}
|
|
1435
1486
|
default: {
|
|
1436
1487
|
// Generic: just open the file
|
|
1437
|
-
spawn(editor, [fullPath], { detached: true, stdio: 'ignore' }).unref();
|
|
1488
|
+
getChildProcess().spawn(editor, [fullPath], { detached: true, stdio: 'ignore' }).unref();
|
|
1438
1489
|
break;
|
|
1439
1490
|
}
|
|
1440
1491
|
}
|
|
1441
1492
|
|
|
1442
1493
|
session.send(
|
|
1443
|
-
createNotification('info', 'Editor', `Opened ${require('path').basename(fullPath)}`)
|
|
1494
|
+
getProtocol().createNotification('info', 'Editor', `Opened ${require('path').basename(fullPath)}`)
|
|
1444
1495
|
);
|
|
1445
1496
|
} catch (error) {
|
|
1446
1497
|
console.error('[Open File Error]', error.message);
|
|
1447
|
-
session.send(createError('OPEN_FILE_ERROR', `Failed to open file: ${error.message}`));
|
|
1498
|
+
session.send(getProtocol().createError('OPEN_FILE_ERROR', `Failed to open file: ${error.message}`));
|
|
1448
1499
|
}
|
|
1449
1500
|
}
|
|
1450
1501
|
|
|
@@ -1457,10 +1508,10 @@ class DashboardServer extends EventEmitter {
|
|
|
1457
1508
|
// Validate cwd stays within project root
|
|
1458
1509
|
let safeCwd = this.projectRoot;
|
|
1459
1510
|
if (cwd) {
|
|
1460
|
-
const cwdResult = validatePath(cwd, this.projectRoot, { allowSymlinks: true });
|
|
1511
|
+
const cwdResult = getValidatePaths().validatePath(cwd, this.projectRoot, { allowSymlinks: true });
|
|
1461
1512
|
if (!cwdResult.ok) {
|
|
1462
1513
|
session.send(
|
|
1463
|
-
createError('TERMINAL_ERROR', 'Working directory must be within project root')
|
|
1514
|
+
getProtocol().createError('TERMINAL_ERROR', 'Working directory must be within project root')
|
|
1464
1515
|
);
|
|
1465
1516
|
return;
|
|
1466
1517
|
}
|
|
@@ -1480,7 +1531,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1480
1531
|
timestamp: new Date().toISOString(),
|
|
1481
1532
|
});
|
|
1482
1533
|
} else {
|
|
1483
|
-
session.send(createError('TERMINAL_ERROR', 'Failed to spawn terminal'));
|
|
1534
|
+
session.send(getProtocol().createError('TERMINAL_ERROR', 'Failed to spawn terminal'));
|
|
1484
1535
|
}
|
|
1485
1536
|
}
|
|
1486
1537
|
|
|
@@ -1521,7 +1572,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1521
1572
|
}
|
|
1522
1573
|
|
|
1523
1574
|
this.terminalManager.closeTerminal(terminalId);
|
|
1524
|
-
session.send(createNotification('info', 'Terminal', 'Terminal closed'));
|
|
1575
|
+
session.send(getProtocol().createNotification('info', 'Terminal', 'Terminal closed'));
|
|
1525
1576
|
}
|
|
1526
1577
|
|
|
1527
1578
|
// ==========================================================================
|
|
@@ -1533,7 +1584,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1533
1584
|
*/
|
|
1534
1585
|
sendAutomationList(session) {
|
|
1535
1586
|
if (!this._automationRegistry) {
|
|
1536
|
-
session.send(createAutomationList([]));
|
|
1587
|
+
session.send(getProtocol().createAutomationList([]));
|
|
1537
1588
|
return;
|
|
1538
1589
|
}
|
|
1539
1590
|
|
|
@@ -1555,10 +1606,10 @@ class DashboardServer extends EventEmitter {
|
|
|
1555
1606
|
};
|
|
1556
1607
|
});
|
|
1557
1608
|
|
|
1558
|
-
session.send(createAutomationList(enriched));
|
|
1609
|
+
session.send(getProtocol().createAutomationList(enriched));
|
|
1559
1610
|
} catch (error) {
|
|
1560
1611
|
console.error('[Automations] List error:', error.message);
|
|
1561
|
-
session.send(createAutomationList([]));
|
|
1612
|
+
session.send(getProtocol().createAutomationList([]));
|
|
1562
1613
|
}
|
|
1563
1614
|
}
|
|
1564
1615
|
|
|
@@ -1628,12 +1679,12 @@ class DashboardServer extends EventEmitter {
|
|
|
1628
1679
|
const { id: automationId } = message;
|
|
1629
1680
|
|
|
1630
1681
|
if (!automationId) {
|
|
1631
|
-
session.send(createError('INVALID_REQUEST', 'Automation ID is required'));
|
|
1682
|
+
session.send(getProtocol().createError('INVALID_REQUEST', 'Automation ID is required'));
|
|
1632
1683
|
return;
|
|
1633
1684
|
}
|
|
1634
1685
|
|
|
1635
1686
|
if (!this._automationRunner) {
|
|
1636
|
-
session.send(createError('AUTOMATION_ERROR', 'Automation runner not initialized'));
|
|
1687
|
+
session.send(getProtocol().createError('AUTOMATION_ERROR', 'Automation runner not initialized'));
|
|
1637
1688
|
return;
|
|
1638
1689
|
}
|
|
1639
1690
|
|
|
@@ -1641,12 +1692,12 @@ class DashboardServer extends EventEmitter {
|
|
|
1641
1692
|
// Check if already running
|
|
1642
1693
|
if (this._runningAutomations.has(automationId)) {
|
|
1643
1694
|
session.send(
|
|
1644
|
-
createNotification('warning', 'Automation', `${automationId} is already running`)
|
|
1695
|
+
getProtocol().createNotification('warning', 'Automation', `${automationId} is already running`)
|
|
1645
1696
|
);
|
|
1646
1697
|
return;
|
|
1647
1698
|
}
|
|
1648
1699
|
|
|
1649
|
-
session.send(createNotification('info', 'Automation', `Starting ${automationId}...`));
|
|
1700
|
+
session.send(getProtocol().createNotification('info', 'Automation', `Starting ${automationId}...`));
|
|
1650
1701
|
|
|
1651
1702
|
// Run the automation (async)
|
|
1652
1703
|
const result = await this._automationRunner.run(automationId);
|
|
@@ -1654,23 +1705,23 @@ class DashboardServer extends EventEmitter {
|
|
|
1654
1705
|
// Send result notification
|
|
1655
1706
|
if (result.success) {
|
|
1656
1707
|
session.send(
|
|
1657
|
-
createNotification('success', 'Automation', `${automationId} completed successfully`)
|
|
1708
|
+
getProtocol().createNotification('success', 'Automation', `${automationId} completed successfully`)
|
|
1658
1709
|
);
|
|
1659
1710
|
} else {
|
|
1660
1711
|
session.send(
|
|
1661
|
-
createNotification('error', 'Automation', `${automationId} failed: ${result.error}`)
|
|
1712
|
+
getProtocol().createNotification('error', 'Automation', `${automationId} failed: ${result.error}`)
|
|
1662
1713
|
);
|
|
1663
1714
|
}
|
|
1664
1715
|
|
|
1665
1716
|
// Send final status
|
|
1666
|
-
session.send(createAutomationStatus(automationId, result.success ? 'idle' : 'error', result));
|
|
1717
|
+
session.send(getProtocol().createAutomationStatus(automationId, result.success ? 'idle' : 'error', result));
|
|
1667
1718
|
|
|
1668
1719
|
// Refresh the list
|
|
1669
1720
|
this.sendAutomationList(session);
|
|
1670
1721
|
} catch (error) {
|
|
1671
1722
|
console.error('[Automation Error]', error.message);
|
|
1672
|
-
session.send(createError('AUTOMATION_ERROR', 'Automation execution failed'));
|
|
1673
|
-
session.send(createAutomationStatus(automationId, 'error', { error: 'Execution failed' }));
|
|
1723
|
+
session.send(getProtocol().createError('AUTOMATION_ERROR', 'Automation execution failed'));
|
|
1724
|
+
session.send(getProtocol().createAutomationStatus(automationId, 'error', { error: 'Execution failed' }));
|
|
1674
1725
|
}
|
|
1675
1726
|
}
|
|
1676
1727
|
|
|
@@ -1681,7 +1732,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1681
1732
|
const { id: automationId } = message;
|
|
1682
1733
|
|
|
1683
1734
|
if (!automationId) {
|
|
1684
|
-
session.send(createError('INVALID_REQUEST', 'Automation ID is required'));
|
|
1735
|
+
session.send(getProtocol().createError('INVALID_REQUEST', 'Automation ID is required'));
|
|
1685
1736
|
return;
|
|
1686
1737
|
}
|
|
1687
1738
|
|
|
@@ -1691,8 +1742,8 @@ class DashboardServer extends EventEmitter {
|
|
|
1691
1742
|
}
|
|
1692
1743
|
|
|
1693
1744
|
this._runningAutomations.delete(automationId);
|
|
1694
|
-
session.send(createAutomationStatus(automationId, 'idle'));
|
|
1695
|
-
session.send(createNotification('info', 'Automation', `${automationId} stopped`));
|
|
1745
|
+
session.send(getProtocol().createAutomationStatus(automationId, 'idle'));
|
|
1746
|
+
session.send(getProtocol().createNotification('info', 'Automation', `${automationId} stopped`));
|
|
1696
1747
|
}
|
|
1697
1748
|
|
|
1698
1749
|
// ==========================================================================
|
|
@@ -1707,7 +1758,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1707
1758
|
(a, b) => new Date(b.timestamp) - new Date(a.timestamp)
|
|
1708
1759
|
);
|
|
1709
1760
|
|
|
1710
|
-
session.send(createInboxList(items));
|
|
1761
|
+
session.send(getProtocol().createInboxList(items));
|
|
1711
1762
|
}
|
|
1712
1763
|
|
|
1713
1764
|
/**
|
|
@@ -1717,13 +1768,13 @@ class DashboardServer extends EventEmitter {
|
|
|
1717
1768
|
const { id: itemId, action } = message;
|
|
1718
1769
|
|
|
1719
1770
|
if (!itemId) {
|
|
1720
|
-
session.send(createError('INVALID_REQUEST', 'Item ID is required'));
|
|
1771
|
+
session.send(getProtocol().createError('INVALID_REQUEST', 'Item ID is required'));
|
|
1721
1772
|
return;
|
|
1722
1773
|
}
|
|
1723
1774
|
|
|
1724
1775
|
const item = this._inbox.get(itemId);
|
|
1725
1776
|
if (!item) {
|
|
1726
|
-
session.send(createError('NOT_FOUND', `Inbox item ${itemId} not found`));
|
|
1777
|
+
session.send(getProtocol().createError('NOT_FOUND', `Inbox item ${itemId} not found`));
|
|
1727
1778
|
return;
|
|
1728
1779
|
}
|
|
1729
1780
|
|
|
@@ -1731,14 +1782,14 @@ class DashboardServer extends EventEmitter {
|
|
|
1731
1782
|
case 'accept':
|
|
1732
1783
|
// Mark as accepted and remove
|
|
1733
1784
|
item.status = 'accepted';
|
|
1734
|
-
session.send(createNotification('success', 'Inbox', `Accepted: ${item.title}`));
|
|
1785
|
+
session.send(getProtocol().createNotification('success', 'Inbox', `Accepted: ${item.title}`));
|
|
1735
1786
|
this._inbox.delete(itemId);
|
|
1736
1787
|
break;
|
|
1737
1788
|
|
|
1738
1789
|
case 'dismiss':
|
|
1739
1790
|
// Mark as dismissed and remove
|
|
1740
1791
|
item.status = 'dismissed';
|
|
1741
|
-
session.send(createNotification('info', 'Inbox', `Dismissed: ${item.title}`));
|
|
1792
|
+
session.send(getProtocol().createNotification('info', 'Inbox', `Dismissed: ${item.title}`));
|
|
1742
1793
|
this._inbox.delete(itemId);
|
|
1743
1794
|
break;
|
|
1744
1795
|
|
|
@@ -1748,7 +1799,7 @@ class DashboardServer extends EventEmitter {
|
|
|
1748
1799
|
break;
|
|
1749
1800
|
|
|
1750
1801
|
default:
|
|
1751
|
-
session.send(createError('INVALID_ACTION', `Unknown action: ${action}`));
|
|
1802
|
+
session.send(getProtocol().createError('INVALID_ACTION', `Unknown action: ${action}`));
|
|
1752
1803
|
return;
|
|
1753
1804
|
}
|
|
1754
1805
|
|