@recallai/desktop-sdk 2.0.7 → 2.0.9
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 +38 -3
- package/index.d.ts +10 -2
- package/index.js +233 -30
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
|
-
### 2.0.
|
|
1
|
+
### 2.0.9 [patch] (MacOS+Windows) - 2026-03-27
|
|
2
|
+
- Forward invalid command errors back to user on Windows.
|
|
3
|
+
- Restart video capture on windows as necessary.
|
|
4
|
+
- Improvements to audio device tracking algorithms on Windows.
|
|
5
|
+
- Teams audio capture fixes on Windows.
|
|
6
|
+
- Teams delayed detection fixes on Windows.
|
|
7
|
+
- Fixed issue where Chromium browsers would occasionally experience slow detection speed when joining the meeting with the microphone muted on MacOS
|
|
8
|
+
- Reduced initial startup burden associated with mic logs on MacOS
|
|
9
|
+
- Zoom screenshare detection works with annotations disabled
|
|
10
|
+
- Fixed issues with speaker labels being misattributed in > 2 participant meetings on MacOS
|
|
11
|
+
- Fixed issue with Teams not firing meeting-closed when closed from PIP view with main window minimized on MacOS
|
|
12
|
+
- Fixed issues with Arc / Brave sometimes failing to detect after-call screen on MacOS
|
|
13
|
+
- Fixed issues with chromium browsers prematurely ending meeting when switching tabs without PIP enabled on MacOS
|
|
14
|
+
- Fixed issue where transcript events would stop firing after recovering from network outage on MacOS
|
|
15
|
+
- Fixed issue where having video frame capture enabled would occasionally cause crashes on MacOS
|
|
16
|
+
- Fixed issue where Brave meetings would sometimes not be detected after leaving and rejoining meeting quickly on MacOS
|
|
17
|
+
- Fixed issue where audio would sometimes die mid-stream causing transcription and audio loss
|
|
18
|
+
- Fixed issue where audio streams would die and never recover
|
|
19
|
+
- Add additional telemetry to audio stream lifecycle
|
|
20
|
+
- Add additional telemetry and fix to commands not properly responding on macOS
|
|
21
|
+
|
|
22
|
+
### 2.0.8 [patch] (MacOS+Windows) - 2026-03-13
|
|
23
|
+
- Reduced blips/audio-glitches in the audio mixing process
|
|
24
|
+
- Fixed Windows audio latency during capture
|
|
25
|
+
- Added Windows network-status event and automatic recording stopping.
|
|
26
|
+
- More robust device switching and restarting on windows.
|
|
27
|
+
- Prevent google meet updated events after recording stopped on macOS.
|
|
28
|
+
- Fixed Brave detection on macOS
|
|
29
|
+
- Fixed Chrome fullscreen detection on macOS
|
|
30
|
+
- Fixed issue for Google Meet where compliance message would send to wrong input when different window is focused on macOS
|
|
31
|
+
- Fixed issue for Zoom where host would incorrectly get recognized as a separate participant on macOS
|
|
32
|
+
- Fixed issue for screenshare capturing where screen sharing indicator would sometimes persist after recording ended on macOS
|
|
33
|
+
- Added ZoomAudioDevice to mic capture blacklist on macOS
|
|
34
|
+
- Fixed Chrome title detection on macOS
|
|
35
|
+
- Supported title on MS Teams on macOS
|
|
36
|
+
|
|
37
|
+
### 2.0.7 [patch] (MacOS+Windows) - 2026-02-26
|
|
2
38
|
- Fixed issues capturing audio when devices change on windows.
|
|
3
39
|
- Added a network shutdown event on macOS.
|
|
4
40
|
- Set recording id on SDK events on macOS and windows.
|
|
@@ -7,8 +43,7 @@
|
|
|
7
43
|
### 2.0.6 [patch] (MacOS+Windows) - 2026-02-11
|
|
8
44
|
- Fix case where recording-ended would not trigger on Windows
|
|
9
45
|
|
|
10
|
-
### 2.0.5 [patch] (MacOS+Windows) - 2026-02-11
|
|
11
|
-
|
|
46
|
+
### 2.0.5 [patch] (MacOS+Windows) - 2026-02-11 (deprecated)
|
|
12
47
|
- Support for Google Chrome 145.0.7632.46+
|
|
13
48
|
- Improvements for Brave
|
|
14
49
|
- Automatically detect which microphone is being used by the meeting platform
|
package/index.d.ts
CHANGED
|
@@ -33,6 +33,10 @@ export interface RecallAiSdkConfig {
|
|
|
33
33
|
acquirePermissionsOnStartup?: Permission[];
|
|
34
34
|
restartOnError?: boolean;
|
|
35
35
|
dev?: boolean;
|
|
36
|
+
testMode?: boolean;
|
|
37
|
+
testSpeedModifier?: string;
|
|
38
|
+
testTargetBundleId?: string;
|
|
39
|
+
testTargetBundleIdRemapped?: string;
|
|
36
40
|
}
|
|
37
41
|
export interface StartRecordingConfig {
|
|
38
42
|
windowId: string;
|
|
@@ -48,7 +52,7 @@ export interface ResumeRecordingConfig {
|
|
|
48
52
|
windowId: string;
|
|
49
53
|
}
|
|
50
54
|
/**
|
|
51
|
-
* @deprecated Recordings are uploaded based on
|
|
55
|
+
* @deprecated Recordings are automatically uploaded based on your rentention configuration. This is now a no-op.
|
|
52
56
|
*/
|
|
53
57
|
export interface UploadRecordingConfig {
|
|
54
58
|
windowId: string;
|
|
@@ -130,13 +134,15 @@ export declare function stopRecording({ windowId }: StopRecordingConfig): Promis
|
|
|
130
134
|
export declare function pauseRecording({ windowId }: PauseRecordingConfig): Promise<null>;
|
|
131
135
|
export declare function resumeRecording({ windowId }: ResumeRecordingConfig): Promise<null>;
|
|
132
136
|
/**
|
|
133
|
-
* @deprecated Recordings are uploaded based on
|
|
137
|
+
* @deprecated Recordings are automatically uploaded based on your rentention configuration. This is now a no-op.
|
|
134
138
|
*/
|
|
135
139
|
export declare function uploadRecording({ windowId }: UploadRecordingConfig): Promise<null>;
|
|
136
140
|
export declare function prepareDesktopAudioRecording(): Promise<string>;
|
|
137
141
|
export declare function requestPermission(permission: Permission): Promise<null>;
|
|
138
142
|
export declare function testUnexpectedShutdown(): Promise<null>;
|
|
139
143
|
export declare function addEventListener<T extends keyof EventTypeToPayloadMap>(type: T, callback: (event: EventTypeToPayloadMap[T]) => void): void;
|
|
144
|
+
export declare function removeEventListener<T extends keyof EventTypeToPayloadMap>(type: T, callback: (event: EventTypeToPayloadMap[T]) => void): void;
|
|
145
|
+
export declare function removeAllEventListeners(): void;
|
|
140
146
|
declare const RecallAiSdk: {
|
|
141
147
|
init: typeof init;
|
|
142
148
|
shutdown: typeof shutdown;
|
|
@@ -148,6 +154,8 @@ declare const RecallAiSdk: {
|
|
|
148
154
|
prepareDesktopAudioRecording: typeof prepareDesktopAudioRecording;
|
|
149
155
|
requestPermission: typeof requestPermission;
|
|
150
156
|
addEventListener: typeof addEventListener;
|
|
157
|
+
removeEventListener: typeof removeEventListener;
|
|
158
|
+
removeAllEventListeners: typeof removeAllEventListeners;
|
|
151
159
|
testUnexpectedShutdown: typeof testUnexpectedShutdown;
|
|
152
160
|
};
|
|
153
161
|
export default RecallAiSdk;
|
package/index.js
CHANGED
|
@@ -46,6 +46,8 @@ exports.prepareDesktopAudioRecording = prepareDesktopAudioRecording;
|
|
|
46
46
|
exports.requestPermission = requestPermission;
|
|
47
47
|
exports.testUnexpectedShutdown = testUnexpectedShutdown;
|
|
48
48
|
exports.addEventListener = addEventListener;
|
|
49
|
+
exports.removeEventListener = removeEventListener;
|
|
50
|
+
exports.removeAllEventListeners = removeAllEventListeners;
|
|
49
51
|
const path = __importStar(require("path"));
|
|
50
52
|
const child_process_1 = require("child_process");
|
|
51
53
|
const readline = __importStar(require("node:readline"));
|
|
@@ -76,6 +78,14 @@ let lastOptions;
|
|
|
76
78
|
let remainingAutomaticRestarts = 10;
|
|
77
79
|
let unexpectedShutdown = false;
|
|
78
80
|
let exiting = false;
|
|
81
|
+
let pendingStdinWrites = [];
|
|
82
|
+
let flushingPendingStdinWrites = false;
|
|
83
|
+
let waitingForStdinDrain = false;
|
|
84
|
+
let stdinWriteSession = 0;
|
|
85
|
+
let nextWriteSequence = 1;
|
|
86
|
+
let activeDrainStartMs = null;
|
|
87
|
+
let activeDrainStartIso = null;
|
|
88
|
+
let activeDrainWriteSequence = null;
|
|
79
89
|
let logBuffer = [];
|
|
80
90
|
let logIndex = 0;
|
|
81
91
|
let packageVersion;
|
|
@@ -185,6 +195,9 @@ async function doLog(level, log, echo = true) {
|
|
|
185
195
|
}
|
|
186
196
|
}
|
|
187
197
|
catch (e) {
|
|
198
|
+
if (proc?.exitCode === 0 || exiting) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
188
201
|
console.error("Failed to send log to Desktop SDK", e.stack, e.message);
|
|
189
202
|
}
|
|
190
203
|
}
|
|
@@ -213,6 +226,157 @@ function flushPendingCommands(err) {
|
|
|
213
226
|
delete pendingCommands[commandId];
|
|
214
227
|
});
|
|
215
228
|
}
|
|
229
|
+
function resolvePendingCommand(commandId, result) {
|
|
230
|
+
const pendingCommand = pendingCommands[commandId];
|
|
231
|
+
if (!pendingCommand) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
pendingCommand.resolve(result);
|
|
235
|
+
delete pendingCommands[commandId];
|
|
236
|
+
}
|
|
237
|
+
function rejectPendingCommand(commandId, err) {
|
|
238
|
+
const pendingCommand = pendingCommands[commandId];
|
|
239
|
+
if (!pendingCommand) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
pendingCommand.reject(err);
|
|
243
|
+
delete pendingCommands[commandId];
|
|
244
|
+
}
|
|
245
|
+
function resetPendingStdinWrites() {
|
|
246
|
+
pendingStdinWrites = [];
|
|
247
|
+
flushingPendingStdinWrites = false;
|
|
248
|
+
waitingForStdinDrain = false;
|
|
249
|
+
stdinWriteSession++;
|
|
250
|
+
activeDrainStartMs = null;
|
|
251
|
+
activeDrainStartIso = null;
|
|
252
|
+
activeDrainWriteSequence = null;
|
|
253
|
+
}
|
|
254
|
+
// Serialize writes into the child stdin and stop when the stream applies backpressure.
|
|
255
|
+
function flushPendingStdinWrites() {
|
|
256
|
+
if (flushingPendingStdinWrites || waitingForStdinDrain) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const currentProc = proc;
|
|
260
|
+
const stdin = currentProc?.stdin;
|
|
261
|
+
if (!currentProc || !stdin || currentProc.exitCode !== null) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
flushingPendingStdinWrites = true;
|
|
265
|
+
const session = stdinWriteSession;
|
|
266
|
+
try {
|
|
267
|
+
while (pendingStdinWrites.length > 0) {
|
|
268
|
+
if (proc !== currentProc || currentProc.stdin !== stdin || currentProc.exitCode !== null) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const nextWrite = pendingStdinWrites.shift();
|
|
272
|
+
if (!nextWrite) {
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
const canContinue = stdin.write(nextWrite.data, (err) => {
|
|
276
|
+
if (!err) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (session !== stdinWriteSession) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
rejectPendingCommand(nextWrite.commandId, new Error(`Failed writing ${nextWrite.command} command [${nextWrite.writeSequence}] (${nextWrite.commandId}) to Desktop SDK stdin: ${err.message}`));
|
|
283
|
+
if (currentProc.exitCode === null && !currentProc.killed) {
|
|
284
|
+
currentProc.kill();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (!canContinue) {
|
|
288
|
+
activeDrainStartMs = Date.now();
|
|
289
|
+
activeDrainStartIso = new Date(activeDrainStartMs).toISOString();
|
|
290
|
+
activeDrainWriteSequence = nextWrite.writeSequence;
|
|
291
|
+
waitingForStdinDrain = true;
|
|
292
|
+
stdin.once('drain', () => {
|
|
293
|
+
if (session !== stdinWriteSession) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (proc !== currentProc || currentProc.stdin !== stdin || currentProc.exitCode !== null) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const drainStopMs = Date.now();
|
|
300
|
+
const drainStartMs = activeDrainStartMs;
|
|
301
|
+
const drainStartIso = activeDrainStartIso;
|
|
302
|
+
const drainWriteSequence = activeDrainWriteSequence;
|
|
303
|
+
activeDrainStartMs = null;
|
|
304
|
+
activeDrainStartIso = null;
|
|
305
|
+
activeDrainWriteSequence = null;
|
|
306
|
+
waitingForStdinDrain = false;
|
|
307
|
+
flushingPendingStdinWrites = false;
|
|
308
|
+
if (drainStartMs !== null && drainStartIso) {
|
|
309
|
+
const drainStopIso = new Date(drainStopMs).toISOString();
|
|
310
|
+
const durationMs = drainStopMs - drainStartMs;
|
|
311
|
+
void enqueueCommand("log", {
|
|
312
|
+
log: `stdin drain completed | start=${drainStartIso} | stop=${drainStopIso} | durationMs=${durationMs} | blockedWriteSequence=${drainWriteSequence ?? "unknown"}`,
|
|
313
|
+
level: "info",
|
|
314
|
+
echo: false
|
|
315
|
+
}, { front: true, suppressSendLog: true }).catch((error) => {
|
|
316
|
+
if (proc?.exitCode === 0 || exiting) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
console.error("Failed to enqueue stdin drain log", error.stack, error.message);
|
|
320
|
+
});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
flushPendingStdinWrites();
|
|
324
|
+
});
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
331
|
+
resetPendingStdinWrites();
|
|
332
|
+
flushPendingCommands(error);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
if (!waitingForStdinDrain) {
|
|
337
|
+
flushingPendingStdinWrites = false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function enqueueStdinWrite(write, front = false) {
|
|
342
|
+
if (front) {
|
|
343
|
+
pendingStdinWrites.unshift(write);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
pendingStdinWrites.push(write);
|
|
347
|
+
}
|
|
348
|
+
flushPendingStdinWrites();
|
|
349
|
+
}
|
|
350
|
+
function enqueueCommand(command, params = {}, options = {}) {
|
|
351
|
+
return new Promise((resolve, reject) => {
|
|
352
|
+
const currentProc = proc;
|
|
353
|
+
const stdin = currentProc?.stdin;
|
|
354
|
+
if (!currentProc ||
|
|
355
|
+
currentProc.exitCode !== null ||
|
|
356
|
+
currentProc.killed ||
|
|
357
|
+
!stdin ||
|
|
358
|
+
stdin.destroyed ||
|
|
359
|
+
stdin.writableEnded ||
|
|
360
|
+
!stdin.writable) {
|
|
361
|
+
reject(new Error("The Desktop SDK is not started or is no longer accepting commands; call `shutdown` and `init` to start it again."));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const commandId = (0, uuid_1.v4)();
|
|
365
|
+
const writeSequence = nextWriteSequence++;
|
|
366
|
+
pendingCommands[commandId] = { resolve, reject, command, writeSequence };
|
|
367
|
+
const payload = {
|
|
368
|
+
command,
|
|
369
|
+
commandId,
|
|
370
|
+
writeSequence,
|
|
371
|
+
params
|
|
372
|
+
};
|
|
373
|
+
const payloadStr = JSON.stringify(payload);
|
|
374
|
+
enqueueStdinWrite({ command, commandId, data: payloadStr + "\n", writeSequence }, options.front ?? false);
|
|
375
|
+
if (!options.suppressSendLog && command !== "log") {
|
|
376
|
+
doLog("info", [`Sending command [${writeSequence}]: ` + payloadStr], false);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
216
380
|
function startProcess() {
|
|
217
381
|
if (proc && proc.exitCode === null) {
|
|
218
382
|
logError("Desktop SDK: Trying to start process while it is already started");
|
|
@@ -227,6 +391,12 @@ function startProcess() {
|
|
|
227
391
|
return;
|
|
228
392
|
}
|
|
229
393
|
let envExtra = {};
|
|
394
|
+
// forward all env starting with RECALL_
|
|
395
|
+
Object.keys(process.env).forEach(key => {
|
|
396
|
+
if (key.startsWith("RECALL_")) {
|
|
397
|
+
envExtra[key] = process.env[key];
|
|
398
|
+
}
|
|
399
|
+
});
|
|
230
400
|
if (process.platform === "win32" && process.env.GLOBAL_GST_RECALL !== "1") {
|
|
231
401
|
envExtra["GST_PLUGIN_PATH"] = path.join(path.dirname(exe_path), "gstreamer-1.0");
|
|
232
402
|
}
|
|
@@ -244,6 +414,18 @@ function startProcess() {
|
|
|
244
414
|
if (sdk_version) {
|
|
245
415
|
envExtra["SDK_VERSION"] = sdk_version;
|
|
246
416
|
}
|
|
417
|
+
if (lastOptions?.testMode) {
|
|
418
|
+
envExtra["TEST_MODE"] = "1";
|
|
419
|
+
}
|
|
420
|
+
if (lastOptions?.testTargetBundleId) {
|
|
421
|
+
envExtra["TEST_TARGET_BUNDLE_ID"] = lastOptions.testTargetBundleId;
|
|
422
|
+
}
|
|
423
|
+
if (lastOptions?.testTargetBundleIdRemapped) {
|
|
424
|
+
envExtra["TEST_TARGET_BUNDLE_ID_REMAP"] = lastOptions.testTargetBundleIdRemapped;
|
|
425
|
+
}
|
|
426
|
+
if (lastOptions?.testSpeedModifier) {
|
|
427
|
+
envExtra["TEST_SPEED_MODIFIER"] = String(lastOptions.testSpeedModifier);
|
|
428
|
+
}
|
|
247
429
|
const gst_dump_dir = process.env.RECALLAI_DESKTOP_SDK_DEV ? path.join(os.tmpdir(), "gst.nocommit") : os.tmpdir();
|
|
248
430
|
if (!fs.existsSync(gst_dump_dir)) {
|
|
249
431
|
try {
|
|
@@ -257,7 +439,7 @@ function startProcess() {
|
|
|
257
439
|
stdio: (process.platform === "darwin" ? ["pipe", "pipe", "pipe", "pipe"] : "pipe"),
|
|
258
440
|
env: {
|
|
259
441
|
GST_RECALL_DEBUG: "2,transcriber:4,rtewebsocketsink:4,rtewebhooksink:4,audiomixer:4,galleryview:4,dsdks3sink:5",
|
|
260
|
-
GST_DEBUG: "3,GST_CAPS:5,GST_SCHEDULING:5,GST_PADS:5,video-scaler:1,transcriber:4,filebuffereds3sink:4,seekables3sink:4,rtpsession:1,videodecoder:2,basesink:2,webrtcbin:2,websocketsink:4,audiomixer:4,galleryview:4,removeonsinkeosbin:4,fallbackswitch:6,sendmessageonsinkeosbin:4,dsdks3sink:5",
|
|
442
|
+
GST_DEBUG: "3,GST_CAPS:5,GST_SCHEDULING:5,GST_PADS:5,video-scaler:1,transcriber:4,filebuffereds3sink:4,seekables3sink:4,rtpsession:1,audioresample:1,videodecoder:2,basesink:2,webrtcbin:2,websocketsink:4,audiomixer:4,galleryview:4,removeonsinkeosbin:4,fallbackswitch:6,sendmessageonsinkeosbin:4,dsdks3sink:5",
|
|
261
443
|
GST_DEBUG_DUMP_DOT_DIR: gst_dump_dir,
|
|
262
444
|
RUST_BACKTRACE: "1",
|
|
263
445
|
// "DYLD_INSERT_LIBRARIES":"/opt/homebrew/lib/libjemalloc.dylib",
|
|
@@ -268,6 +450,10 @@ function startProcess() {
|
|
|
268
450
|
...envExtra,
|
|
269
451
|
}
|
|
270
452
|
});
|
|
453
|
+
const currentProc = proc;
|
|
454
|
+
stdinWriteSession++;
|
|
455
|
+
flushingPendingStdinWrites = false;
|
|
456
|
+
waitingForStdinDrain = false;
|
|
271
457
|
if (process.platform === "darwin") {
|
|
272
458
|
const fd3 = proc.stdio[3];
|
|
273
459
|
if (fd3 && fd3 instanceof node_stream_1.Readable) {
|
|
@@ -288,15 +474,13 @@ function startProcess() {
|
|
|
288
474
|
}
|
|
289
475
|
break;
|
|
290
476
|
case "response":
|
|
291
|
-
|
|
292
|
-
if (pendingCommand) {
|
|
477
|
+
if (pendingCommands[data.commandId]) {
|
|
293
478
|
if (data.status === "success") {
|
|
294
|
-
|
|
479
|
+
resolvePendingCommand(data.commandId, data.result);
|
|
295
480
|
}
|
|
296
481
|
else {
|
|
297
|
-
|
|
482
|
+
rejectPendingCommand(data.commandId, new Error(data.result));
|
|
298
483
|
}
|
|
299
|
-
delete pendingCommands[data.commandId];
|
|
300
484
|
}
|
|
301
485
|
break;
|
|
302
486
|
}
|
|
@@ -320,7 +504,18 @@ function startProcess() {
|
|
|
320
504
|
}
|
|
321
505
|
});
|
|
322
506
|
proc.on('error', (error) => {
|
|
507
|
+
if (proc !== currentProc) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
proc = null;
|
|
511
|
+
resetPendingStdinWrites();
|
|
323
512
|
flushPendingCommands(new Error(`Process error: ${error.message}`));
|
|
513
|
+
rlData?.close();
|
|
514
|
+
rlStdout?.close();
|
|
515
|
+
rlStderr?.close();
|
|
516
|
+
rlData = null;
|
|
517
|
+
rlStdout = null;
|
|
518
|
+
rlStderr = null;
|
|
324
519
|
emitEvent('error', {
|
|
325
520
|
type: 'process',
|
|
326
521
|
message: `The Desktop SDK server process has failed to start or exited improperly.`
|
|
@@ -328,10 +523,17 @@ function startProcess() {
|
|
|
328
523
|
logError(`Desktop SDK: Process error: ${error.message}`);
|
|
329
524
|
});
|
|
330
525
|
proc.on('close', async (code, signal) => {
|
|
526
|
+
if (proc !== currentProc) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
proc = null;
|
|
530
|
+
resetPendingStdinWrites();
|
|
331
531
|
flushPendingCommands(new Error(`Process exited with code ${code}, signal ${signal}.`));
|
|
332
532
|
emitEvent('shutdown', { code: code ?? 0, signal: signal ?? '' });
|
|
533
|
+
rlData?.close();
|
|
333
534
|
rlStdout?.close();
|
|
334
535
|
rlStderr?.close();
|
|
536
|
+
rlData = null;
|
|
335
537
|
rlStdout = null;
|
|
336
538
|
rlStderr = null;
|
|
337
539
|
if (code === 0 || signal === 'SIGINT' || exiting) {
|
|
@@ -342,7 +544,6 @@ function startProcess() {
|
|
|
342
544
|
type: 'process',
|
|
343
545
|
message: "The Desktop SDK server process exited unexpectedly."
|
|
344
546
|
});
|
|
345
|
-
proc = null;
|
|
346
547
|
unexpectedShutdown = true;
|
|
347
548
|
if (lastOptions.restartOnError && remainingAutomaticRestarts > 0) {
|
|
348
549
|
remainingAutomaticRestarts--;
|
|
@@ -354,24 +555,7 @@ function startProcess() {
|
|
|
354
555
|
});
|
|
355
556
|
}
|
|
356
557
|
function sendCommand(command, params = {}) {
|
|
357
|
-
return
|
|
358
|
-
if (!proc || !proc.stdin) {
|
|
359
|
-
reject(new Error("The Desktop SDK is not started; call `init` to start it."));
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const commandId = (0, uuid_1.v4)();
|
|
363
|
-
pendingCommands[commandId] = { resolve, reject };
|
|
364
|
-
const payload = {
|
|
365
|
-
command,
|
|
366
|
-
commandId,
|
|
367
|
-
params
|
|
368
|
-
};
|
|
369
|
-
const payloadStr = JSON.stringify(payload);
|
|
370
|
-
proc.stdin.write(payloadStr + "\n");
|
|
371
|
-
if (command !== "log") {
|
|
372
|
-
doLog("info", ["Sending command: " + payloadStr], false);
|
|
373
|
-
}
|
|
374
|
-
});
|
|
558
|
+
return enqueueCommand(command, params);
|
|
375
559
|
}
|
|
376
560
|
async function doInit(options) {
|
|
377
561
|
startProcess();
|
|
@@ -404,11 +588,19 @@ async function shutdown() {
|
|
|
404
588
|
const result = await sendCommand("shutdown");
|
|
405
589
|
if (proc) {
|
|
406
590
|
const currentProc = proc;
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
591
|
+
await new Promise((resolve) => {
|
|
592
|
+
if (currentProc.exitCode !== null) {
|
|
593
|
+
resolve();
|
|
594
|
+
return;
|
|
410
595
|
}
|
|
411
|
-
|
|
596
|
+
currentProc.once("close", () => resolve());
|
|
597
|
+
setTimeout(() => {
|
|
598
|
+
if (!currentProc.killed) {
|
|
599
|
+
currentProc.kill();
|
|
600
|
+
}
|
|
601
|
+
resolve();
|
|
602
|
+
}, 5000);
|
|
603
|
+
});
|
|
412
604
|
}
|
|
413
605
|
return result;
|
|
414
606
|
}
|
|
@@ -431,7 +623,7 @@ function resumeRecording({ windowId }) {
|
|
|
431
623
|
return sendCommand("resumeRecording", { windowId });
|
|
432
624
|
}
|
|
433
625
|
/**
|
|
434
|
-
* @deprecated Recordings are uploaded based on
|
|
626
|
+
* @deprecated Recordings are automatically uploaded based on your rentention configuration. This is now a no-op.
|
|
435
627
|
*/
|
|
436
628
|
function uploadRecording({ windowId }) {
|
|
437
629
|
return sendCommand("uploadRecording", { windowId });
|
|
@@ -448,6 +640,15 @@ function testUnexpectedShutdown() {
|
|
|
448
640
|
function addEventListener(type, callback) {
|
|
449
641
|
listeners.push({ type, callback });
|
|
450
642
|
}
|
|
643
|
+
function removeEventListener(type, callback) {
|
|
644
|
+
const index = listeners.findIndex(l => l.type === type && l.callback === callback);
|
|
645
|
+
if (index !== -1) {
|
|
646
|
+
listeners.splice(index, 1);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function removeAllEventListeners() {
|
|
650
|
+
listeners.length = 0;
|
|
651
|
+
}
|
|
451
652
|
const RecallAiSdk = {
|
|
452
653
|
init,
|
|
453
654
|
shutdown,
|
|
@@ -459,6 +660,8 @@ const RecallAiSdk = {
|
|
|
459
660
|
prepareDesktopAudioRecording,
|
|
460
661
|
requestPermission,
|
|
461
662
|
addEventListener,
|
|
663
|
+
removeEventListener,
|
|
664
|
+
removeAllEventListeners,
|
|
462
665
|
testUnexpectedShutdown
|
|
463
666
|
};
|
|
464
667
|
exports.default = RecallAiSdk;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recallai/desktop-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Recall Desktop SDK",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
"@types/node": "^24.2.0",
|
|
21
21
|
"typescript": "^5.3.3"
|
|
22
22
|
},
|
|
23
|
-
"commit_sha": "
|
|
23
|
+
"commit_sha": "2bc571dbad7ed8b15a925c9a4cdd0e7fab3c6955"
|
|
24
24
|
}
|