@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 +62 -18
- package/dist/detector.d.ts +36 -1
- package/dist/detector.d.ts.map +1 -1
- package/dist/detector.js +559 -84
- package/dist/detector.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/meeting-detect.sh +160 -72
- package/package.json +2 -1
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
|
-
-
|
|
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(
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
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": "
|
|
119
|
-
"timestamp": "
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
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:
|
|
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;
|
|
145
|
-
debug?: boolean;
|
|
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
|
package/dist/detector.d.ts
CHANGED
|
@@ -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
|
package/dist/detector.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|