claude-mpm 4.0.32__py3-none-any.whl → 4.0.34__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/documentation.json +51 -34
- claude_mpm/agents/templates/research.json +0 -11
- claude_mpm/cli/__init__.py +63 -26
- claude_mpm/cli/commands/agent_manager.py +10 -8
- claude_mpm/core/framework_loader.py +173 -84
- claude_mpm/dashboard/static/css/dashboard.css +449 -0
- claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +774 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +257 -3
- claude_mpm/dashboard/static/js/components/build-tracker.js +289 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +168 -39
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +17 -0
- claude_mpm/dashboard/static/js/components/session-manager.js +23 -3
- claude_mpm/dashboard/static/js/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/js/dashboard.js +207 -31
- claude_mpm/dashboard/static/js/socket-client.js +85 -6
- claude_mpm/dashboard/templates/index.html +1 -0
- claude_mpm/hooks/claude_hooks/connection_pool.py +12 -2
- claude_mpm/hooks/claude_hooks/event_handlers.py +81 -19
- claude_mpm/hooks/claude_hooks/hook_handler.py +72 -10
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +398 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +10 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +34 -48
- claude_mpm/services/agents/deployment/agent_template_builder.py +18 -10
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +10 -25
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +189 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +3 -2
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +10 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +10 -14
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +8 -85
- claude_mpm/services/agents/memory/content_manager.py +98 -105
- claude_mpm/services/event_bus/__init__.py +18 -0
- claude_mpm/services/event_bus/event_bus.py +334 -0
- claude_mpm/services/event_bus/relay.py +301 -0
- claude_mpm/services/events/__init__.py +44 -0
- claude_mpm/services/events/consumers/__init__.py +18 -0
- claude_mpm/services/events/consumers/dead_letter.py +296 -0
- claude_mpm/services/events/consumers/logging.py +183 -0
- claude_mpm/services/events/consumers/metrics.py +242 -0
- claude_mpm/services/events/consumers/socketio.py +376 -0
- claude_mpm/services/events/core.py +470 -0
- claude_mpm/services/events/interfaces.py +230 -0
- claude_mpm/services/events/producers/__init__.py +14 -0
- claude_mpm/services/events/producers/hook.py +269 -0
- claude_mpm/services/events/producers/system.py +327 -0
- claude_mpm/services/mcp_gateway/core/process_pool.py +411 -0
- claude_mpm/services/mcp_gateway/server/stdio_server.py +13 -0
- claude_mpm/services/monitor_build_service.py +345 -0
- claude_mpm/services/socketio/event_normalizer.py +667 -0
- claude_mpm/services/socketio/handlers/connection.py +78 -20
- claude_mpm/services/socketio/handlers/hook.py +14 -5
- claude_mpm/services/socketio/migration_utils.py +329 -0
- claude_mpm/services/socketio/server/broadcaster.py +26 -33
- claude_mpm/services/socketio/server/core.py +4 -3
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/METADATA +4 -3
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/RECORD +67 -46
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.0.34.dist-info}/top_level.txt +0 -0
|
@@ -27,7 +27,11 @@ class AgentInference {
|
|
|
27
27
|
// PM delegation tracking for unique instance views
|
|
28
28
|
pmDelegations: new Map(), // delegation_id -> delegation context
|
|
29
29
|
// Map of agent events to their PM delegation
|
|
30
|
-
agentToDelegation: new Map() // agent_name -> delegation_id
|
|
30
|
+
agentToDelegation: new Map(), // agent_name -> delegation_id
|
|
31
|
+
// Track orphan subagent events (SubagentStart without PM Task)
|
|
32
|
+
orphanSubagents: new Map(), // event_index -> orphan context
|
|
33
|
+
// Track SubagentStart events for orphan detection
|
|
34
|
+
subagentStartEvents: new Map() // agent_name -> array of start events
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
console.log('Agent inference system initialized');
|
|
@@ -43,7 +47,9 @@ class AgentInference {
|
|
|
43
47
|
sessionAgents: new Map(),
|
|
44
48
|
eventAgentMap: new Map(),
|
|
45
49
|
pmDelegations: new Map(),
|
|
46
|
-
agentToDelegation: new Map()
|
|
50
|
+
agentToDelegation: new Map(),
|
|
51
|
+
orphanSubagents: new Map(),
|
|
52
|
+
subagentStartEvents: new Map()
|
|
47
53
|
};
|
|
48
54
|
}
|
|
49
55
|
|
|
@@ -364,6 +370,8 @@ class AgentInference {
|
|
|
364
370
|
this.state.eventAgentMap.clear();
|
|
365
371
|
this.state.pmDelegations.clear();
|
|
366
372
|
this.state.agentToDelegation.clear();
|
|
373
|
+
this.state.orphanSubagents.clear();
|
|
374
|
+
this.state.subagentStartEvents.clear();
|
|
367
375
|
|
|
368
376
|
console.log('Processing agent inference for', events.length, 'events');
|
|
369
377
|
|
|
@@ -397,6 +405,25 @@ class AgentInference {
|
|
|
397
405
|
};
|
|
398
406
|
}
|
|
399
407
|
|
|
408
|
+
// Track SubagentStart events for orphan detection
|
|
409
|
+
const hookEventName = event.hook_event_name || event.data?.hook_event_name || '';
|
|
410
|
+
const isSubagentStart = hookEventName === 'SubagentStart' ||
|
|
411
|
+
event.type === 'hook.subagent_start' ||
|
|
412
|
+
event.subtype === 'subagent_start';
|
|
413
|
+
|
|
414
|
+
if (isSubagentStart && inference.type === 'subagent') {
|
|
415
|
+
// Track this SubagentStart event
|
|
416
|
+
if (!this.state.subagentStartEvents.has(inference.agentName)) {
|
|
417
|
+
this.state.subagentStartEvents.set(inference.agentName, []);
|
|
418
|
+
}
|
|
419
|
+
this.state.subagentStartEvents.get(inference.agentName).push({
|
|
420
|
+
eventIndex: index,
|
|
421
|
+
event: event,
|
|
422
|
+
timestamp: event.timestamp,
|
|
423
|
+
sessionId: sessionId
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
400
427
|
// Track delegation boundaries and PM delegations
|
|
401
428
|
if (event.tool_name === 'Task' && inference.type === 'subagent') {
|
|
402
429
|
// Start of subagent delegation - create PM delegation entry
|
|
@@ -486,12 +513,16 @@ class AgentInference {
|
|
|
486
513
|
}
|
|
487
514
|
});
|
|
488
515
|
|
|
516
|
+
// Identify orphan subagents after all events are processed
|
|
517
|
+
this.identifyOrphanSubagents(events);
|
|
518
|
+
|
|
489
519
|
console.log('Agent inference processing complete. Results:', {
|
|
490
520
|
total_events: events.length,
|
|
491
521
|
inferred_agents: this.state.eventAgentMap.size,
|
|
492
522
|
unique_sessions: this.state.sessionAgents.size,
|
|
493
523
|
pm_delegations: this.state.pmDelegations.size,
|
|
494
|
-
agent_to_delegation_mappings: this.state.agentToDelegation.size
|
|
524
|
+
agent_to_delegation_mappings: this.state.agentToDelegation.size,
|
|
525
|
+
orphan_subagents: this.state.orphanSubagents.size
|
|
495
526
|
});
|
|
496
527
|
}
|
|
497
528
|
|
|
@@ -580,6 +611,229 @@ class AgentInference {
|
|
|
580
611
|
return this.state.agentToDelegation;
|
|
581
612
|
}
|
|
582
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Build hierarchical delegation tree structure
|
|
616
|
+
* @returns {Object} Tree structure with PM nodes and subagent children
|
|
617
|
+
*/
|
|
618
|
+
buildDelegationHierarchy() {
|
|
619
|
+
// Get all PM delegations
|
|
620
|
+
const pmDelegations = this.getPMDelegations();
|
|
621
|
+
const events = this.eventViewer.events;
|
|
622
|
+
|
|
623
|
+
// Build hierarchy tree
|
|
624
|
+
const hierarchy = {
|
|
625
|
+
mainPM: {
|
|
626
|
+
type: 'pm',
|
|
627
|
+
name: 'PM',
|
|
628
|
+
delegations: [],
|
|
629
|
+
ownEvents: [],
|
|
630
|
+
totalEvents: 0
|
|
631
|
+
},
|
|
632
|
+
impliedPM: {
|
|
633
|
+
type: 'pm_implied',
|
|
634
|
+
name: 'Implied PM',
|
|
635
|
+
delegations: [],
|
|
636
|
+
ownEvents: [],
|
|
637
|
+
totalEvents: 0
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
// Process explicit PM delegations
|
|
642
|
+
for (const [delegationId, delegation] of pmDelegations) {
|
|
643
|
+
hierarchy.mainPM.delegations.push({
|
|
644
|
+
id: delegationId,
|
|
645
|
+
agentName: delegation.agentName,
|
|
646
|
+
taskContext: this.extractTaskContext(delegation.pmCall),
|
|
647
|
+
events: delegation.agentEvents,
|
|
648
|
+
startTime: delegation.timestamp,
|
|
649
|
+
endTime: delegation.endIndex ? events[delegation.endIndex]?.timestamp : null,
|
|
650
|
+
status: delegation.endIndex ? 'completed' : 'active'
|
|
651
|
+
});
|
|
652
|
+
hierarchy.mainPM.totalEvents += delegation.agentEvents.length;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Find PM's own events
|
|
656
|
+
events.forEach((event, index) => {
|
|
657
|
+
const inference = this.getInferredAgent(index);
|
|
658
|
+
if (inference && inference.type === 'main_agent') {
|
|
659
|
+
hierarchy.mainPM.ownEvents.push({
|
|
660
|
+
eventIndex: index,
|
|
661
|
+
event: event
|
|
662
|
+
});
|
|
663
|
+
hierarchy.mainPM.totalEvents++;
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// Find orphan subagent events
|
|
668
|
+
const orphanEvents = new Map();
|
|
669
|
+
events.forEach((event, index) => {
|
|
670
|
+
const inference = this.getInferredAgent(index);
|
|
671
|
+
if (inference && inference.type === 'subagent') {
|
|
672
|
+
// Check if this is part of any PM delegation
|
|
673
|
+
let isOrphan = true;
|
|
674
|
+
for (const [_, delegation] of pmDelegations) {
|
|
675
|
+
if (delegation.agentEvents.some(e => e.eventIndex === index)) {
|
|
676
|
+
isOrphan = false;
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (isOrphan) {
|
|
682
|
+
const agentName = inference.agentName;
|
|
683
|
+
if (!orphanEvents.has(agentName)) {
|
|
684
|
+
orphanEvents.set(agentName, []);
|
|
685
|
+
}
|
|
686
|
+
orphanEvents.get(agentName).push({
|
|
687
|
+
eventIndex: index,
|
|
688
|
+
event: event,
|
|
689
|
+
inference: inference
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Add orphan agents as implied PM delegations
|
|
696
|
+
for (const [agentName, agentEvents] of orphanEvents) {
|
|
697
|
+
hierarchy.impliedPM.delegations.push({
|
|
698
|
+
id: `implied_${agentName}`,
|
|
699
|
+
agentName: agentName,
|
|
700
|
+
taskContext: 'No explicit PM delegation',
|
|
701
|
+
events: agentEvents,
|
|
702
|
+
startTime: agentEvents[0].event.timestamp,
|
|
703
|
+
endTime: agentEvents[agentEvents.length - 1].event.timestamp,
|
|
704
|
+
status: 'completed'
|
|
705
|
+
});
|
|
706
|
+
hierarchy.impliedPM.totalEvents += agentEvents.length;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return hierarchy;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Extract task context from PM call
|
|
714
|
+
* @param {Object} pmCall - PM's Task tool call
|
|
715
|
+
* @returns {string} Task description
|
|
716
|
+
*/
|
|
717
|
+
extractTaskContext(pmCall) {
|
|
718
|
+
if (!pmCall) return 'Unknown task';
|
|
719
|
+
|
|
720
|
+
const params = pmCall.tool_parameters || pmCall.data?.tool_parameters || {};
|
|
721
|
+
return params.task || params.request || params.description || 'Task delegation';
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Identify orphan subagents (SubagentStart without PM Task delegation)
|
|
726
|
+
* @param {Array} events - All events to analyze
|
|
727
|
+
*/
|
|
728
|
+
identifyOrphanSubagents(events) {
|
|
729
|
+
const ORPHAN_TIME_WINDOW = 5000; // 5 seconds to group orphans together
|
|
730
|
+
|
|
731
|
+
// Check each SubagentStart event
|
|
732
|
+
for (const [agentName, startEvents] of this.state.subagentStartEvents) {
|
|
733
|
+
for (const startEvent of startEvents) {
|
|
734
|
+
const eventIndex = startEvent.eventIndex;
|
|
735
|
+
const timestamp = new Date(startEvent.timestamp).getTime();
|
|
736
|
+
|
|
737
|
+
// Check if this SubagentStart has a corresponding PM Task delegation
|
|
738
|
+
let hasTaskDelegation = false;
|
|
739
|
+
|
|
740
|
+
// Look for a Task tool call within a reasonable time window before this SubagentStart
|
|
741
|
+
for (let i = Math.max(0, eventIndex - 20); i < eventIndex; i++) {
|
|
742
|
+
const prevEvent = events[i];
|
|
743
|
+
if (!prevEvent) continue;
|
|
744
|
+
|
|
745
|
+
const prevTimestamp = new Date(prevEvent.timestamp).getTime();
|
|
746
|
+
const timeDiff = timestamp - prevTimestamp;
|
|
747
|
+
|
|
748
|
+
// Check if this is a Task tool call within 10 seconds
|
|
749
|
+
if (prevEvent.tool_name === 'Task' && timeDiff >= 0 && timeDiff < 10000) {
|
|
750
|
+
const inference = this.state.eventAgentMap.get(i);
|
|
751
|
+
if (inference && inference.agentName === agentName) {
|
|
752
|
+
hasTaskDelegation = true;
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// If no Task delegation found, mark as orphan
|
|
759
|
+
if (!hasTaskDelegation) {
|
|
760
|
+
this.state.orphanSubagents.set(eventIndex, {
|
|
761
|
+
agentName: agentName,
|
|
762
|
+
timestamp: startEvent.timestamp,
|
|
763
|
+
sessionId: startEvent.sessionId,
|
|
764
|
+
event: startEvent.event,
|
|
765
|
+
groupingKey: null // Will be set by grouping logic
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Group orphan subagents by time proximity or session
|
|
772
|
+
this.groupOrphanSubagents(ORPHAN_TIME_WINDOW);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Group orphan subagents that occur close together in time or same session
|
|
777
|
+
* @param {number} timeWindow - Time window in milliseconds for grouping
|
|
778
|
+
*/
|
|
779
|
+
groupOrphanSubagents(timeWindow) {
|
|
780
|
+
const orphansList = Array.from(this.state.orphanSubagents.values())
|
|
781
|
+
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
782
|
+
|
|
783
|
+
let currentGroup = null;
|
|
784
|
+
let lastTimestamp = null;
|
|
785
|
+
|
|
786
|
+
for (const orphan of orphansList) {
|
|
787
|
+
const timestamp = new Date(orphan.timestamp).getTime();
|
|
788
|
+
|
|
789
|
+
// Check if this orphan should be in the same group
|
|
790
|
+
if (!currentGroup ||
|
|
791
|
+
(lastTimestamp && timestamp - lastTimestamp > timeWindow)) {
|
|
792
|
+
// Start a new group
|
|
793
|
+
currentGroup = `implied_pm_${orphan.sessionId}_${timestamp}`;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
orphan.groupingKey = currentGroup;
|
|
797
|
+
lastTimestamp = timestamp;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Check if a subagent event is an orphan (no PM Task delegation)
|
|
803
|
+
* @param {number} eventIndex - Index of the event
|
|
804
|
+
* @returns {boolean} True if the event is from an orphan subagent
|
|
805
|
+
*/
|
|
806
|
+
isOrphanSubagent(eventIndex) {
|
|
807
|
+
return this.state.orphanSubagents.has(eventIndex);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Get orphan subagent context for an event
|
|
812
|
+
* @param {number} eventIndex - Index of the event
|
|
813
|
+
* @returns {Object|null} Orphan context or null
|
|
814
|
+
*/
|
|
815
|
+
getOrphanContext(eventIndex) {
|
|
816
|
+
return this.state.orphanSubagents.get(eventIndex) || null;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Get all orphan subagent groups
|
|
821
|
+
* @returns {Map} Map of groupingKey -> array of orphan contexts
|
|
822
|
+
*/
|
|
823
|
+
getOrphanGroups() {
|
|
824
|
+
const groups = new Map();
|
|
825
|
+
|
|
826
|
+
for (const orphan of this.state.orphanSubagents.values()) {
|
|
827
|
+
const key = orphan.groupingKey;
|
|
828
|
+
if (!groups.has(key)) {
|
|
829
|
+
groups.set(key, []);
|
|
830
|
+
}
|
|
831
|
+
groups.get(key).push(orphan);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return groups;
|
|
835
|
+
}
|
|
836
|
+
|
|
583
837
|
/**
|
|
584
838
|
* Get unique agent instances (one per agent type, consolidating multiple delegations)
|
|
585
839
|
* This is used for the unique instance view in the agents tab
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Tracker Component
|
|
3
|
+
*
|
|
4
|
+
* WHY: Displays and manages version/build information for both MPM and Monitor UI,
|
|
5
|
+
* providing users with clear visibility of the current system versions.
|
|
6
|
+
*
|
|
7
|
+
* DESIGN DECISION: Implemented as a standalone component that can be easily
|
|
8
|
+
* integrated into the dashboard header, with automatic updates from SocketIO.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export class BuildTracker {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.element = null;
|
|
14
|
+
this.buildInfo = {
|
|
15
|
+
monitor: {
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
build: 1,
|
|
18
|
+
formatted_build: "0001",
|
|
19
|
+
full_version: "v1.0.0-0001"
|
|
20
|
+
},
|
|
21
|
+
mpm: {
|
|
22
|
+
version: "unknown",
|
|
23
|
+
build: "unknown",
|
|
24
|
+
full_version: "v0.0.0"
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Socket client reference (will be set during initialization)
|
|
29
|
+
this.socketClient = null;
|
|
30
|
+
|
|
31
|
+
// Initialize the component
|
|
32
|
+
this.init();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the build tracker component
|
|
37
|
+
*/
|
|
38
|
+
init() {
|
|
39
|
+
console.log('Initializing BuildTracker component');
|
|
40
|
+
this.createElements();
|
|
41
|
+
this.setupEventListeners();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create the DOM elements for version display
|
|
46
|
+
*
|
|
47
|
+
* WHY: Creates a clean, unobtrusive version display that fits
|
|
48
|
+
* seamlessly into the dashboard header.
|
|
49
|
+
*/
|
|
50
|
+
createElements() {
|
|
51
|
+
// Create container element
|
|
52
|
+
this.element = document.createElement('div');
|
|
53
|
+
this.element.className = 'version-display';
|
|
54
|
+
this.element.id = 'version-display';
|
|
55
|
+
|
|
56
|
+
// Create MPM version element
|
|
57
|
+
const mpmVersion = document.createElement('span');
|
|
58
|
+
mpmVersion.className = 'version-item mpm-version';
|
|
59
|
+
mpmVersion.id = 'mpm-version';
|
|
60
|
+
mpmVersion.innerHTML = `
|
|
61
|
+
<span class="version-label">MPM</span>
|
|
62
|
+
<span class="version-value">v0.0.0</span>
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
// Create separator
|
|
66
|
+
const separator = document.createElement('span');
|
|
67
|
+
separator.className = 'version-separator';
|
|
68
|
+
separator.textContent = '|';
|
|
69
|
+
|
|
70
|
+
// Create Monitor version element
|
|
71
|
+
const monitorVersion = document.createElement('span');
|
|
72
|
+
monitorVersion.className = 'version-item monitor-version';
|
|
73
|
+
monitorVersion.id = 'monitor-version';
|
|
74
|
+
monitorVersion.innerHTML = `
|
|
75
|
+
<span class="version-label">Monitor</span>
|
|
76
|
+
<span class="version-value">v1.0.0-0001</span>
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
// Assemble elements
|
|
80
|
+
this.element.appendChild(mpmVersion);
|
|
81
|
+
this.element.appendChild(separator);
|
|
82
|
+
this.element.appendChild(monitorVersion);
|
|
83
|
+
|
|
84
|
+
// Add tooltip for detailed info
|
|
85
|
+
this.element.title = 'Click for detailed version information';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set the socket client for receiving updates
|
|
90
|
+
*
|
|
91
|
+
* @param {Object} socketClient - The Socket.IO client instance
|
|
92
|
+
*/
|
|
93
|
+
setSocketClient(socketClient) {
|
|
94
|
+
this.socketClient = socketClient;
|
|
95
|
+
|
|
96
|
+
// Listen for build info updates
|
|
97
|
+
if (this.socketClient && this.socketClient.socket) {
|
|
98
|
+
// Listen for welcome message with build info
|
|
99
|
+
this.socketClient.socket.on('welcome', (eventData) => {
|
|
100
|
+
// Handle both old format (direct) and new schema (nested in data)
|
|
101
|
+
const buildInfo = eventData.build_info ||
|
|
102
|
+
(eventData.data && eventData.data.build_info);
|
|
103
|
+
if (buildInfo) {
|
|
104
|
+
this.updateBuildInfo(buildInfo);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Listen for status updates with build info
|
|
109
|
+
this.socketClient.socket.on('status', (eventData) => {
|
|
110
|
+
// Handle both old format (direct) and new schema (nested in data)
|
|
111
|
+
const buildInfo = eventData.build_info ||
|
|
112
|
+
(eventData.data && eventData.data.build_info);
|
|
113
|
+
if (buildInfo) {
|
|
114
|
+
this.updateBuildInfo(buildInfo);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Listen for explicit build info updates
|
|
119
|
+
this.socketClient.socket.on('build_info', (data) => {
|
|
120
|
+
this.updateBuildInfo(data);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Update the build information
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} buildInfo - Build information from server
|
|
129
|
+
*/
|
|
130
|
+
updateBuildInfo(buildInfo) {
|
|
131
|
+
console.log('Updating build info:', buildInfo);
|
|
132
|
+
|
|
133
|
+
// Store the build info
|
|
134
|
+
this.buildInfo = buildInfo;
|
|
135
|
+
|
|
136
|
+
// Update display
|
|
137
|
+
this.updateDisplay();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Update the version display elements
|
|
142
|
+
*
|
|
143
|
+
* WHY: Keeps the UI in sync with the latest version information
|
|
144
|
+
* received from the server.
|
|
145
|
+
*/
|
|
146
|
+
updateDisplay() {
|
|
147
|
+
// Update MPM version
|
|
148
|
+
const mpmElement = this.element.querySelector('.mpm-version .version-value');
|
|
149
|
+
if (mpmElement && this.buildInfo.mpm) {
|
|
150
|
+
const mpmVersion = this.buildInfo.mpm.full_version ||
|
|
151
|
+
`v${this.buildInfo.mpm.version}`;
|
|
152
|
+
mpmElement.textContent = mpmVersion;
|
|
153
|
+
|
|
154
|
+
// Add build number to tooltip if available
|
|
155
|
+
if (this.buildInfo.mpm.build && this.buildInfo.mpm.build !== "unknown") {
|
|
156
|
+
mpmElement.parentElement.title = `MPM Build: ${this.buildInfo.mpm.build}`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Update Monitor version
|
|
161
|
+
const monitorElement = this.element.querySelector('.monitor-version .version-value');
|
|
162
|
+
if (monitorElement && this.buildInfo.monitor) {
|
|
163
|
+
const monitorVersion = this.buildInfo.monitor.full_version ||
|
|
164
|
+
`v${this.buildInfo.monitor.version}-${this.buildInfo.monitor.formatted_build}`;
|
|
165
|
+
monitorElement.textContent = monitorVersion;
|
|
166
|
+
|
|
167
|
+
// Add last updated to tooltip if available
|
|
168
|
+
if (this.buildInfo.monitor.last_updated) {
|
|
169
|
+
const lastUpdated = new Date(this.buildInfo.monitor.last_updated).toLocaleString();
|
|
170
|
+
monitorElement.parentElement.title = `Monitor Build: ${this.buildInfo.monitor.formatted_build}\nLast Updated: ${lastUpdated}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Setup event listeners
|
|
177
|
+
*
|
|
178
|
+
* WHY: Allows users to interact with the version display for
|
|
179
|
+
* additional information or actions.
|
|
180
|
+
*/
|
|
181
|
+
setupEventListeners() {
|
|
182
|
+
// Click handler for showing detailed version info
|
|
183
|
+
this.element.addEventListener('click', () => {
|
|
184
|
+
this.showDetailedInfo();
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Show detailed version information in a modal or alert
|
|
190
|
+
*
|
|
191
|
+
* WHY: Provides power users with detailed build and version
|
|
192
|
+
* information for debugging and support purposes.
|
|
193
|
+
*/
|
|
194
|
+
showDetailedInfo() {
|
|
195
|
+
const info = [];
|
|
196
|
+
|
|
197
|
+
// MPM information
|
|
198
|
+
if (this.buildInfo.mpm) {
|
|
199
|
+
info.push('=== MPM Framework ===');
|
|
200
|
+
info.push(`Version: ${this.buildInfo.mpm.version}`);
|
|
201
|
+
if (this.buildInfo.mpm.build && this.buildInfo.mpm.build !== "unknown") {
|
|
202
|
+
info.push(`Build: ${this.buildInfo.mpm.build}`);
|
|
203
|
+
}
|
|
204
|
+
info.push(`Full: ${this.buildInfo.mpm.full_version}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
info.push('');
|
|
208
|
+
|
|
209
|
+
// Monitor information
|
|
210
|
+
if (this.buildInfo.monitor) {
|
|
211
|
+
info.push('=== Monitor UI ===');
|
|
212
|
+
info.push(`Version: ${this.buildInfo.monitor.version}`);
|
|
213
|
+
info.push(`Build: ${this.buildInfo.monitor.formatted_build} (${this.buildInfo.monitor.build})`);
|
|
214
|
+
info.push(`Full: ${this.buildInfo.monitor.full_version}`);
|
|
215
|
+
if (this.buildInfo.monitor.last_updated) {
|
|
216
|
+
const lastUpdated = new Date(this.buildInfo.monitor.last_updated).toLocaleString();
|
|
217
|
+
info.push(`Updated: ${lastUpdated}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Show in console and alert
|
|
222
|
+
console.log('Version Information:\n' + info.join('\n'));
|
|
223
|
+
|
|
224
|
+
// Create a simple modal-like display
|
|
225
|
+
const modal = document.createElement('div');
|
|
226
|
+
modal.className = 'version-modal';
|
|
227
|
+
modal.innerHTML = `
|
|
228
|
+
<div class="version-modal-content">
|
|
229
|
+
<h3>Version Information</h3>
|
|
230
|
+
<pre>${info.join('\n')}</pre>
|
|
231
|
+
<button onclick="this.parentElement.parentElement.remove()">Close</button>
|
|
232
|
+
</div>
|
|
233
|
+
`;
|
|
234
|
+
|
|
235
|
+
// Add to body
|
|
236
|
+
document.body.appendChild(modal);
|
|
237
|
+
|
|
238
|
+
// Auto-remove after 10 seconds
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
modal.remove();
|
|
241
|
+
}, 10000);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Mount the component to a parent element
|
|
246
|
+
*
|
|
247
|
+
* @param {HTMLElement|string} parent - Parent element or selector
|
|
248
|
+
*/
|
|
249
|
+
mount(parent) {
|
|
250
|
+
const parentElement = typeof parent === 'string'
|
|
251
|
+
? document.querySelector(parent)
|
|
252
|
+
: parent;
|
|
253
|
+
|
|
254
|
+
if (parentElement && this.element) {
|
|
255
|
+
parentElement.appendChild(this.element);
|
|
256
|
+
console.log('BuildTracker mounted to', parent);
|
|
257
|
+
} else {
|
|
258
|
+
console.error('Failed to mount BuildTracker: parent element not found');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get the component's DOM element
|
|
264
|
+
*
|
|
265
|
+
* @returns {HTMLElement} The component's root element
|
|
266
|
+
*/
|
|
267
|
+
getElement() {
|
|
268
|
+
return this.element;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Destroy the component and clean up
|
|
273
|
+
*/
|
|
274
|
+
destroy() {
|
|
275
|
+
if (this.element && this.element.parentNode) {
|
|
276
|
+
this.element.parentNode.removeChild(this.element);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Clean up socket listeners
|
|
280
|
+
if (this.socketClient && this.socketClient.socket) {
|
|
281
|
+
this.socketClient.socket.off('welcome');
|
|
282
|
+
this.socketClient.socket.off('status');
|
|
283
|
+
this.socketClient.socket.off('build_info');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.element = null;
|
|
287
|
+
this.socketClient = null;
|
|
288
|
+
}
|
|
289
|
+
}
|