@mostrom/meeting-detector 1.0.4 → 1.0.5

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 CHANGED
@@ -9,7 +9,9 @@ Real-time meeting detection for macOS desktop apps using TCC (Transparency, Cons
9
9
  - 🔍 **Process attribution**: Identifies which app is using camera/microphone with PID
10
10
  - đŸŽ›ī¸ **Event-driven**: Clean Node.js API with TypeScript support
11
11
  - đŸšĢ **Smart deduplication**: Prevents spam from multi-process apps like Teams
12
- - 📱 **Front app correlation**: Shows which app is currently in focus
12
+ - 🔁 **Lifecycle hooks**: Emits `meeting_started`, `meeting_changed`, and `meeting_ended`
13
+ - 🧠 **Uncertainty-safe**: Uses `Unknown`/no-detection instead of guessing
14
+ - 🔒 **Privacy-first**: Redacts sensitive metadata by default
13
15
 
14
16
  ## Installation
15
17
 
@@ -71,10 +73,19 @@ import type { MeetingSignal } from '@mostrom/meeting-detector';
71
73
  const detector = new MeetingDetector({ debug: true });
72
74
 
73
75
  detector.onMeeting((signal: MeetingSignal) => {
74
- console.log(`${signal.process} is using ${signal.service}`);
75
- if (signal.verdict === 'requested') {
76
- console.log('🔴 Meeting started');
77
- }
76
+ console.log('Raw signal:', signal.service, signal.verdict);
77
+ });
78
+
79
+ detector.onMeetingStarted((event) => {
80
+ console.log(`✅ Started: ${event.platform}`);
81
+ });
82
+
83
+ detector.onMeetingChanged((event) => {
84
+ console.log(`🔄 Changed: ${event.previous_platform} -> ${event.platform}`);
85
+ });
86
+
87
+ detector.onMeetingEnded((event) => {
88
+ console.log(`âšī¸ Ended: ${event.platform} (${event.reason})`);
78
89
  });
79
90
 
80
91
  detector.onError((error) => {
@@ -104,10 +115,16 @@ new MeetingDetector(options?: MeetingDetectorOptions)
104
115
  - `stop()` - Stop monitoring
105
116
  - `isRunning()` - Check if running
106
117
  - `onMeeting(callback)` - Add meeting event listener
118
+ - `onMeetingStarted(callback)` - Add meeting started listener
119
+ - `onMeetingChanged(callback)` - Add meeting changed listener
120
+ - `onMeetingEnded(callback)` - Add meeting ended listener
107
121
  - `onError(callback)` - Add error event listener
108
122
 
109
123
  #### Events
110
- - `meeting` - Emitted when meeting state changes
124
+ - `meeting` - Emitted for normalized raw meeting signals
125
+ - `meeting_started` - Emitted when active meeting starts
126
+ - `meeting_changed` - Emitted when active platform changes
127
+ - `meeting_ended` - Emitted when meeting ends (timeout/stop)
111
128
  - `error` - Emitted on errors
112
129
  - `exit` - Emitted when process exits
113
130
 
@@ -115,34 +132,61 @@ new MeetingDetector(options?: MeetingDetectorOptions)
115
132
 
116
133
  ```json
117
134
  {
118
- "event": "meeting_signal",
119
- "timestamp": "2025-09-01T14:30:29Z",
120
- "service": "microphone",
121
- "verdict": "requested",
122
- "process": "Microsoft Teams WebView Helper",
123
- "pid": "7390",
124
- "front_app": "MSTeams",
125
- "camera_active": true
135
+ "event": "meeting_started",
136
+ "timestamp": "2026-03-07T12:30:29.000Z",
137
+ "platform": "Microsoft Teams",
138
+ "confidence": "high",
139
+ "reason": "signal"
126
140
  }
127
141
  ```
128
142
 
129
143
  ## TypeScript Types
130
144
 
131
145
  ```typescript
146
+ type MeetingPlatform =
147
+ | 'Microsoft Teams'
148
+ | 'Zoom'
149
+ | 'Google Meet'
150
+ | 'Slack'
151
+ | 'Cisco Webex'
152
+ | 'Unknown';
153
+
132
154
  interface MeetingSignal {
133
155
  event: 'meeting_signal';
134
156
  timestamp: string;
135
- service: 'microphone' | 'camera' | '';
157
+ service: string;
136
158
  verdict: 'requested' | 'allowed' | 'denied' | '';
159
+ preflight?: boolean;
137
160
  process: string;
138
161
  pid: string;
162
+ parent_pid: string;
163
+ process_path: string;
139
164
  front_app: string;
165
+ window_title: string;
166
+ session_id: string;
140
167
  camera_active: boolean;
168
+ chrome_url?: string;
169
+ }
170
+
171
+ interface MeetingLifecycleEvent {
172
+ event: 'meeting_started' | 'meeting_changed' | 'meeting_ended';
173
+ timestamp: string;
174
+ platform: MeetingPlatform;
175
+ previous_platform?: MeetingPlatform;
176
+ confidence: 'high' | 'medium' | 'low';
177
+ reason: 'signal' | 'switch' | 'timeout' | 'stop';
178
+ raw_signal?: MeetingSignal;
141
179
  }
142
180
 
143
181
  interface MeetingDetectorOptions {
144
- scriptPath?: string; // Path to bash script (default: './meeting-detect.sh')
145
- debug?: boolean; // Enable debug logging (default: false)
182
+ scriptPath?: string;
183
+ debug?: boolean;
184
+ sessionDeduplicationMs?: number;
185
+ meetingEndTimeoutMs?: number;
186
+ emitUnknown?: boolean;
187
+ includeSensitiveMetadata?: boolean;
188
+ includeRawSignalInLifecycle?: boolean;
189
+ startupProbe?: boolean;
146
190
  }
147
191
  ```
148
192
 
@@ -179,4 +223,4 @@ The detector runs a bash script that monitors macOS TCC (privacy) logs for micro
179
223
 
180
224
  ## License
181
225
 
182
- MIT
226
+ MIT
@@ -1,9 +1,17 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import { MeetingDetectorOptions, MeetingEventCallback, ErrorEventCallback } from './types.js';
2
+ import { MeetingDetectorOptions, MeetingEventCallback, ErrorEventCallback, MeetingLifecycleCallback } from './types.js';
3
3
  export declare class MeetingDetector extends EventEmitter {
4
+ private static readonly LOW_CONFIDENCE_WINDOW_MS;
5
+ private static readonly LOW_CONFIDENCE_FALLBACK_MIN_SIGNALS;
6
+ private static readonly LOW_CONFIDENCE_FALLBACK_MIN_DURATION_MS;
7
+ private static readonly PRECHECK_PRONE_SERVICES;
4
8
  private process?;
5
9
  private options;
6
10
  private activeSessions;
11
+ private pendingConfidence;
12
+ private serviceContext;
13
+ private activeMeeting;
14
+ private meetingEndTimer?;
7
15
  constructor(options?: MeetingDetectorOptions);
8
16
  /**
9
17
  * Start monitoring for meeting signals
@@ -26,6 +34,12 @@ export declare class MeetingDetector extends EventEmitter {
26
34
  * Add an error event listener
27
35
  */
28
36
  onError(callback: ErrorEventCallback): void;
37
+ /**
38
+ * Add lifecycle event listeners
39
+ */
40
+ onMeetingStarted(callback: MeetingLifecycleCallback): void;
41
+ onMeetingChanged(callback: MeetingLifecycleCallback): void;
42
+ onMeetingEnded(callback: MeetingLifecycleCallback): void;
29
43
  /**
30
44
  * Comprehensive filtering to prevent false positives
31
45
  * Filters out:
@@ -36,6 +50,20 @@ export declare class MeetingDetector extends EventEmitter {
36
50
  * - Google Meet signals without valid meeting URL patterns
37
51
  */
38
52
  private shouldIgnoreSignal;
53
+ private sanitizeSignalForOutput;
54
+ private normalizePlatform;
55
+ private getSignalConfidence;
56
+ private emitMeetingLifecycle;
57
+ private scheduleMeetingEndCheck;
58
+ private handleMeetingEndTimeout;
59
+ private updateMeetingLifecycle;
60
+ private getServiceKey;
61
+ private isFrontAppConsistentWithService;
62
+ private stabilizeSignalContext;
63
+ private isLowConfidenceSignal;
64
+ private hasStrongMeetingEvidence;
65
+ private cleanupExpiredPendingConfidence;
66
+ private resolveConfidence;
39
67
  /**
40
68
  * Generate a unique session key based on the signal properties
41
69
  */
@@ -50,7 +78,14 @@ export declare class MeetingDetector extends EventEmitter {
50
78
  */
51
79
  private cleanupExpiredSessions;
52
80
  private parseSignal;
81
+ private includesAny;
53
82
  private transformAppName;
83
+ /**
84
+ * Check at startup whether a supported meeting is already active.
85
+ * Emits a synthetic meeting_started lifecycle event if found.
86
+ * This handles the case where the detector starts while a call is already in progress.
87
+ */
88
+ private probeActiveMeetingAtStartup;
54
89
  }
55
90
  export declare function detector(callback: MeetingEventCallback, options?: MeetingDetectorOptions): MeetingDetector;
56
91
  //# sourceMappingURL=detector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAiB,sBAAsB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAO7G,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,cAAc,CAAuC;gBAEjD,OAAO,GAAE,sBAA2B;IAgBhD;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,IAAI;IAuEnD;;OAEG;IACI,IAAI,IAAI,IAAI;IAcnB;;OAEG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IAItD;;OAEG;IACI,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAIlD;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IA0E1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,gBAAgB;CAqDzB;AAGD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAI1G"}
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAEL,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAElB,wBAAwB,EAEzB,MAAM,YAAY,CAAC;AA0BpB,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAS;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAK;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uCAAuC,CAAS;IACxE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAK5C;IAEH,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,iBAAiB,CAAmD;IAC5E,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,OAAO,GAAE,sBAA2B;IAqBhD;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,IAAI;IAoHnD;;OAEG;IACI,IAAI,IAAI,IAAI;IAyBnB;;OAEG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IAItD;;OAEG;IACI,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAIlD;;OAEG;IACI,gBAAgB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI;IAI1D,gBAAgB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI;IAI1D,cAAc,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI;IAI/D;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAiF1B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,uBAAuB;IAkB/B,OAAO,CAAC,sBAAsB;IAwC9B,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,+BAA+B;IAuBvC,OAAO,CAAC,sBAAsB;IAiC9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,+BAA+B;IAQvC,OAAO,CAAC,iBAAiB;IA0BzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,gBAAgB;IAiFxB;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;CAyFpC;AAGD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAI1G"}