@recallai/desktop-sdk 1.0.6 → 1.1.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 +20 -2
- package/index.d.ts +125 -160
- package/index.js +277 -276
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
-
### 1.0
|
|
1
|
+
### 1.1.0 [minor] (MacOS+Windows) - 2025-08-29
|
|
2
|
+
|
|
3
|
+
- Reduce idle power consumption by 80%
|
|
4
|
+
- Add capture support for MS Teams on MacOS
|
|
5
|
+
- Add capture support for MS Teams on Windows
|
|
6
|
+
- Fixed a bug detecting Zoom on Windows for non-premium zoom members
|
|
7
|
+
- Fixed a bug capuring video on old Win10 Windows installs
|
|
8
|
+
- Add support for gathering meeting urls for Zoom/Teams on Windows
|
|
9
|
+
- Add support for AssemblyAI Universal Streaming Model (v3)
|
|
10
|
+
- Add support for transcript.partial_data
|
|
11
|
+
- Fix permissions to allow system only audio without requiring an initial request
|
|
12
|
+
- Add Google Meet url to URL field
|
|
13
|
+
- Generate index.js and index.d.ts from an index.ts file
|
|
14
|
+
- Prevent rogue screen capture kit calls from causing CPU drain in system audio only mode
|
|
15
|
+
- Fix memory growth during recording
|
|
16
|
+
- Fix memory leaks on idle
|
|
17
|
+
- Prevent zombie exe on stdin kill
|
|
18
|
+
|
|
19
|
+
### 1.0.6 [patch] (MacOS) - 2025-08-15
|
|
2
20
|
|
|
3
21
|
- Fixes a bug where whole-desktop audio capture would produce corrupted recordings.
|
|
4
22
|
|
|
5
|
-
### 1.0.5 [patch] (MacOS) - 2025-
|
|
23
|
+
### 1.0.5 [patch] (MacOS) - 2025-08-11
|
|
6
24
|
|
|
7
25
|
- Fixes a bug that would cause an occasional crash.
|
|
8
26
|
|
package/index.d.ts
CHANGED
|
@@ -1,163 +1,128 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
///
|
|
57
|
-
|
|
58
|
-
export interface RecallAiSdkWindow {
|
|
1
|
+
export type RecallAiSdkEvent = RecordingStartEvent | RecordingStopEvent | UploadProgressEvent | MeetingDetectedEvent | MeetingUpdatedEvent | MeetingClosedEvent | SdkStateChangeEvent | ErrorEvent | MediaCaptureStatusEvent | ParticipantCaptureStatusEvent | PermissionsGrantedEvent | RealtimeEvent | ShutdownEvent;
|
|
2
|
+
export type EventTypeToPayloadMap = {
|
|
3
|
+
'recording-started': RecordingStartEvent;
|
|
4
|
+
'recording-ended': RecordingStopEvent;
|
|
5
|
+
'upload-progress': UploadProgressEvent;
|
|
6
|
+
'meeting-detected': MeetingDetectedEvent;
|
|
7
|
+
'meeting-updated': MeetingUpdatedEvent;
|
|
8
|
+
'meeting-closed': MeetingClosedEvent;
|
|
9
|
+
'sdk-state-change': SdkStateChangeEvent;
|
|
10
|
+
'error': ErrorEvent;
|
|
11
|
+
'media-capture-status': MediaCaptureStatusEvent;
|
|
12
|
+
'participant-capture-status': ParticipantCaptureStatusEvent;
|
|
13
|
+
'permissions-granted': PermissionsGrantedEvent;
|
|
14
|
+
'permission-status': PermissionStatusEvent;
|
|
15
|
+
'realtime-event': RealtimeEvent;
|
|
16
|
+
'shutdown': ShutdownEvent;
|
|
17
|
+
};
|
|
18
|
+
export type Permission = 'accessibility' | 'screen-capture' | 'microphone' | 'system-audio';
|
|
19
|
+
export interface RecallAiSdkWindow {
|
|
20
|
+
id: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
platform: string;
|
|
24
|
+
}
|
|
25
|
+
export interface RecallAiSdkConfig {
|
|
26
|
+
api_url?: string;
|
|
27
|
+
apiUrl?: string;
|
|
28
|
+
acquirePermissionsOnStartup?: Permission[];
|
|
29
|
+
restartOnError?: boolean;
|
|
30
|
+
dev?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface StartRecordingConfig {
|
|
33
|
+
windowId: string;
|
|
34
|
+
uploadToken: string;
|
|
35
|
+
}
|
|
36
|
+
export interface StopRecordingConfig {
|
|
37
|
+
windowId: string;
|
|
38
|
+
}
|
|
39
|
+
export interface PauseRecordingConfig {
|
|
40
|
+
windowId: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ResumeRecordingConfig {
|
|
43
|
+
windowId: string;
|
|
44
|
+
}
|
|
45
|
+
export interface UploadRecordingConfig {
|
|
46
|
+
windowId: string;
|
|
47
|
+
}
|
|
48
|
+
export interface RecordingStartEvent {
|
|
49
|
+
window: RecallAiSdkWindow;
|
|
50
|
+
}
|
|
51
|
+
export interface RecordingStopEvent {
|
|
52
|
+
window: RecallAiSdkWindow;
|
|
53
|
+
}
|
|
54
|
+
export interface UploadProgressEvent {
|
|
55
|
+
window: {
|
|
59
56
|
id: string;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
export interface StopRecordingConfig {
|
|
78
|
-
windowId: string;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface PauseRecordingConfig {
|
|
82
|
-
windowId: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface ResumeRecordingConfig {
|
|
86
|
-
windowId: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface UploadRecordingConfig {
|
|
90
|
-
windowId: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
///
|
|
94
|
-
|
|
95
|
-
export interface RecordingStartEvent {
|
|
96
|
-
window: RecallAiSdkWindow;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface RecordingStopEvent {
|
|
100
|
-
window: RecallAiSdkWindow;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface UploadProgressEvent {
|
|
104
|
-
window: { id: string };
|
|
105
|
-
progress: number;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface MeetingDetectedEvent {
|
|
109
|
-
window: RecallAiSdkWindow;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface MeetingUpdatedEvent {
|
|
113
|
-
window: RecallAiSdkWindow;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface MeetingClosedEvent {
|
|
117
|
-
window: RecallAiSdkWindow;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface SdkStateChangeEvent {
|
|
121
|
-
sdk: {
|
|
122
|
-
state: {
|
|
123
|
-
code: 'recording' | 'idle' | 'paused';
|
|
124
|
-
};
|
|
57
|
+
};
|
|
58
|
+
progress: number;
|
|
59
|
+
}
|
|
60
|
+
export interface MeetingDetectedEvent {
|
|
61
|
+
window: RecallAiSdkWindow;
|
|
62
|
+
}
|
|
63
|
+
export interface MeetingUpdatedEvent {
|
|
64
|
+
window: RecallAiSdkWindow;
|
|
65
|
+
}
|
|
66
|
+
export interface MeetingClosedEvent {
|
|
67
|
+
window: RecallAiSdkWindow;
|
|
68
|
+
}
|
|
69
|
+
export interface SdkStateChangeEvent {
|
|
70
|
+
sdk: {
|
|
71
|
+
state: {
|
|
72
|
+
code: 'recording' | 'idle' | 'paused';
|
|
125
73
|
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export interface ShutdownEvent {
|
|
160
|
-
code: number;
|
|
161
|
-
signal: string;
|
|
162
|
-
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export interface MediaCaptureStatusEvent {
|
|
77
|
+
window: RecallAiSdkWindow;
|
|
78
|
+
type: 'video' | 'audio';
|
|
79
|
+
capturing: boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface ParticipantCaptureStatusEvent {
|
|
82
|
+
window: RecallAiSdkWindow;
|
|
83
|
+
type: 'video' | 'audio' | 'screenshare';
|
|
84
|
+
capturing: boolean;
|
|
85
|
+
}
|
|
86
|
+
export interface PermissionsGrantedEvent {
|
|
87
|
+
}
|
|
88
|
+
export interface PermissionStatusEvent {
|
|
89
|
+
permission: Permission;
|
|
90
|
+
status: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ErrorEvent {
|
|
93
|
+
window?: RecallAiSdkWindow;
|
|
94
|
+
type: string;
|
|
95
|
+
message: string;
|
|
96
|
+
}
|
|
97
|
+
export interface RealtimeEvent {
|
|
98
|
+
window: RecallAiSdkWindow;
|
|
99
|
+
event: string;
|
|
100
|
+
data: any;
|
|
101
|
+
}
|
|
102
|
+
export interface ShutdownEvent {
|
|
103
|
+
code: number;
|
|
104
|
+
signal: string;
|
|
163
105
|
}
|
|
106
|
+
export declare function init(options: RecallAiSdkConfig): Promise<null>;
|
|
107
|
+
export declare function shutdown(): Promise<null>;
|
|
108
|
+
export declare function startRecording(config: StartRecordingConfig): Promise<null>;
|
|
109
|
+
export declare function stopRecording({ windowId }: StopRecordingConfig): Promise<null>;
|
|
110
|
+
export declare function pauseRecording({ windowId }: PauseRecordingConfig): Promise<null>;
|
|
111
|
+
export declare function resumeRecording({ windowId }: ResumeRecordingConfig): Promise<null>;
|
|
112
|
+
export declare function uploadRecording({ windowId }: UploadRecordingConfig): Promise<null>;
|
|
113
|
+
export declare function prepareDesktopAudioRecording(): Promise<string>;
|
|
114
|
+
export declare function requestPermission(permission: Permission): Promise<null>;
|
|
115
|
+
export declare function addEventListener<T extends keyof EventTypeToPayloadMap>(type: T, callback: (event: EventTypeToPayloadMap[T]) => void): void;
|
|
116
|
+
declare const RecallAiSdk: {
|
|
117
|
+
init: typeof init;
|
|
118
|
+
shutdown: typeof shutdown;
|
|
119
|
+
startRecording: typeof startRecording;
|
|
120
|
+
stopRecording: typeof stopRecording;
|
|
121
|
+
pauseRecording: typeof pauseRecording;
|
|
122
|
+
resumeRecording: typeof resumeRecording;
|
|
123
|
+
uploadRecording: typeof uploadRecording;
|
|
124
|
+
prepareDesktopAudioRecording: typeof prepareDesktopAudioRecording;
|
|
125
|
+
requestPermission: typeof requestPermission;
|
|
126
|
+
addEventListener: typeof addEventListener;
|
|
127
|
+
};
|
|
128
|
+
export default RecallAiSdk;
|
package/index.js
CHANGED
|
@@ -1,336 +1,337 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.init = init;
|
|
37
|
+
exports.shutdown = shutdown;
|
|
38
|
+
exports.startRecording = startRecording;
|
|
39
|
+
exports.stopRecording = stopRecording;
|
|
40
|
+
exports.pauseRecording = pauseRecording;
|
|
41
|
+
exports.resumeRecording = resumeRecording;
|
|
42
|
+
exports.uploadRecording = uploadRecording;
|
|
43
|
+
exports.prepareDesktopAudioRecording = prepareDesktopAudioRecording;
|
|
44
|
+
exports.requestPermission = requestPermission;
|
|
45
|
+
exports.addEventListener = addEventListener;
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
const readline = __importStar(require("node:readline"));
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const os = __importStar(require("node:os"));
|
|
51
|
+
const uuid_1 = require("uuid");
|
|
8
52
|
// HACK: This is needed because of https://github.com/electron/electron/issues/6262
|
|
9
|
-
|
|
10
53
|
// Search for the unpacked directory first, since fs.existsSync actually reports
|
|
11
54
|
// the "virtual" files in the .asar as being present
|
|
12
|
-
|
|
13
55
|
let exe_paths = [];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
56
|
+
if (process.platform === "darwin") {
|
|
57
|
+
exe_paths.push(path.join(__dirname, "desktop_sdk_macos_exe").replace('app.asar', 'app.asar.unpacked'));
|
|
58
|
+
exe_paths.push(path.join(__dirname, "desktop_sdk_macos_exe"));
|
|
59
|
+
}
|
|
60
|
+
else if (process.platform === "win32") {
|
|
61
|
+
exe_paths.push(path.join(__dirname, "agent-windows.exe").replace('app.asar', 'app.asar.unpacked'));
|
|
62
|
+
exe_paths.push(path.join(__dirname, "agent-windows.exe"));
|
|
21
63
|
}
|
|
22
|
-
|
|
23
64
|
let proc;
|
|
24
65
|
const listeners = [];
|
|
25
66
|
const pendingCommands = {};
|
|
26
|
-
|
|
27
67
|
let lastOptions;
|
|
28
68
|
let remainingAutomaticRestarts = 10;
|
|
29
69
|
let unexpectedShutdown = false;
|
|
30
|
-
|
|
31
70
|
let logBuffer = [];
|
|
32
71
|
let logIndex = 0;
|
|
33
|
-
|
|
34
72
|
function flushLogBuffer() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
73
|
+
const buf = logBuffer.slice(); // just here for the copy
|
|
74
|
+
logBuffer = [];
|
|
75
|
+
logIndex = 0;
|
|
76
|
+
// we use a for... in... here because it does not loop over the sparse (undefined) entries
|
|
77
|
+
// if you doubt this, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#array_iteration_and_for...in
|
|
78
|
+
// and know that Nick and Tucker spent at least 10 minutes debating this
|
|
79
|
+
for (const idx in buf) {
|
|
80
|
+
const { level, log, echo } = buf[idx];
|
|
81
|
+
doLog(level, [log], echo);
|
|
82
|
+
}
|
|
44
83
|
}
|
|
45
|
-
|
|
46
84
|
async function doLog(level, log, echo = true) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
try {
|
|
86
|
+
const levelMap = {
|
|
87
|
+
"info": "log",
|
|
88
|
+
"warning": "warn",
|
|
89
|
+
"error": "error"
|
|
90
|
+
};
|
|
91
|
+
if (echo) {
|
|
92
|
+
const consoleMethod = console[levelMap[level] ?? "log"];
|
|
93
|
+
consoleMethod(...log);
|
|
94
|
+
}
|
|
95
|
+
const logPayload = {
|
|
96
|
+
log: log.join(" "),
|
|
97
|
+
level: level,
|
|
98
|
+
echo
|
|
99
|
+
};
|
|
100
|
+
const idx = logIndex++;
|
|
101
|
+
logBuffer[idx] = logPayload;
|
|
102
|
+
if (proc && proc.exitCode === null) {
|
|
103
|
+
await sendCommand("log", logPayload);
|
|
104
|
+
delete logBuffer[idx];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error("Failed to send log to Desktop SDK", e.stack, e.message);
|
|
70
109
|
}
|
|
71
|
-
} catch (e) {
|
|
72
|
-
console.error("Failed to send log to Desktop SDK", e.stack, e.message);
|
|
73
|
-
}
|
|
74
110
|
}
|
|
75
|
-
|
|
76
111
|
function log(...log) {
|
|
77
|
-
|
|
112
|
+
doLog("info", log);
|
|
78
113
|
}
|
|
79
|
-
|
|
80
114
|
function logWarning(...log) {
|
|
81
|
-
|
|
115
|
+
doLog("warning", log);
|
|
82
116
|
}
|
|
83
|
-
|
|
84
117
|
function logError(...log) {
|
|
85
|
-
|
|
118
|
+
doLog("error", log);
|
|
86
119
|
}
|
|
87
|
-
|
|
88
120
|
function emitEvent(type, payload) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
121
|
+
if (type !== "upload-progress" && type !== "realtime-event") {
|
|
122
|
+
doLog("info", ["Receiving event: " + type + " | " + JSON.stringify(payload)], false);
|
|
123
|
+
}
|
|
124
|
+
for (const listener of listeners) {
|
|
125
|
+
if (listener.type === type) {
|
|
126
|
+
listener.callback(payload);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
97
129
|
}
|
|
98
|
-
|
|
99
130
|
function flushPendingCommands(err) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
Object.keys(pendingCommands).forEach(commandId => {
|
|
132
|
+
pendingCommands[commandId].reject(err);
|
|
133
|
+
delete pendingCommands[commandId];
|
|
134
|
+
});
|
|
104
135
|
}
|
|
105
|
-
|
|
106
136
|
function startProcess() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const exe_path = exe_paths.find(fs.existsSync);
|
|
113
|
-
|
|
114
|
-
if (!exe_path) {
|
|
115
|
-
logError(`Desktop SDK: Couldn't launch! This is likely an issue with the build tool you're using.`);
|
|
116
|
-
|
|
117
|
-
for (const exe_path of exe_paths)
|
|
118
|
-
logError("Tried:", exe_path);
|
|
119
|
-
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
let envExtra = {};
|
|
124
|
-
|
|
125
|
-
if (process.platform === "win32" && process.env.GLOBAL_GST_RECALL !== "1") {
|
|
126
|
-
envExtra["GST_PLUGIN_PATH"] = path.join(path.dirname(exe_path), "gstreamer-1.0");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
proc = spawn(exe_path, {
|
|
130
|
-
stdio: "pipe",
|
|
131
|
-
env: {
|
|
132
|
-
GST_DEBUG: "2",
|
|
133
|
-
GST_DEBUG_DUMP_DOT_DIR: process.env.RECALLAI_DESKTOP_SDK_DEV ? "/tmp/gst.nocommit" : os.tmpdir(),
|
|
134
|
-
RUST_BACKTRACE: true,
|
|
135
|
-
// "DYLD_INSERT_LIBRARIES":"/opt/homebrew/lib/libjemalloc.dylib",
|
|
136
|
-
// "MALLOC_CONF":"prof:true,prof_active:true,prof_prefix:jeprof",
|
|
137
|
-
// "DYLD_FORCE_FLAT_NAMESPACE":"1",
|
|
138
|
-
...envExtra,
|
|
137
|
+
if (proc && proc.exitCode === null) {
|
|
138
|
+
logError("Desktop SDK: Trying to start process while it is already started");
|
|
139
|
+
return;
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
rlStdout.on('line', (line) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (line.startsWith("recall_ai_command|")) {
|
|
149
|
-
try {
|
|
150
|
-
const data = JSON.parse(line.substring(18));
|
|
151
|
-
|
|
152
|
-
switch (data.type) {
|
|
153
|
-
case "event":
|
|
154
|
-
const event = JSON.parse(data.event);
|
|
155
|
-
emitEvent(event.type, event.payload);
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case "response":
|
|
159
|
-
const pendingCommand = pendingCommands[data.commandId];
|
|
160
|
-
if (pendingCommand) {
|
|
161
|
-
if (data.status === "success") {
|
|
162
|
-
pendingCommand.resolve(data.result);
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
pendingCommand.reject(new Error(data.result));
|
|
166
|
-
}
|
|
167
|
-
delete pendingCommands[data.commandId];
|
|
168
|
-
}
|
|
169
|
-
break;
|
|
141
|
+
const exe_path = exe_paths.find(fs.existsSync);
|
|
142
|
+
if (!exe_path) {
|
|
143
|
+
logError(`Desktop SDK: Couldn't launch! This is likely an issue with the build tool you're using.`);
|
|
144
|
+
for (const exe_path of exe_paths) {
|
|
145
|
+
logError("Tried:", exe_path);
|
|
170
146
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(line);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let envExtra = {};
|
|
150
|
+
if (process.platform === "win32" && process.env.GLOBAL_GST_RECALL !== "1") {
|
|
151
|
+
envExtra["GST_PLUGIN_PATH"] = path.join(path.dirname(exe_path), "gstreamer-1.0");
|
|
177
152
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
153
|
+
const gst_dump_dir = process.env.RECALLAI_DESKTOP_SDK_DEV ? path.join(os.tmpdir(), "gst.nocommit") : os.tmpdir();
|
|
154
|
+
if (!fs.existsSync(gst_dump_dir)) {
|
|
155
|
+
try {
|
|
156
|
+
fs.mkdirSync(gst_dump_dir);
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
logError("Failed to create gst dump dir:", String(e));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
proc = (0, child_process_1.spawn)(exe_path, {
|
|
163
|
+
stdio: "pipe",
|
|
164
|
+
env: {
|
|
165
|
+
GST_DEBUG: "2",
|
|
166
|
+
GST_DEBUG_DUMP_DOT_DIR: gst_dump_dir,
|
|
167
|
+
RUST_BACKTRACE: "1",
|
|
168
|
+
// "DYLD_INSERT_LIBRARIES":"/opt/homebrew/lib/libjemalloc.dylib",
|
|
169
|
+
// "MALLOC_CONF":"prof:true,prof_active:true,prof_prefix:jeprof",
|
|
170
|
+
// "DYLD_FORCE_FLAT_NAMESPACE":"1",
|
|
171
|
+
...envExtra,
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
const rlStdout = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity });
|
|
175
|
+
const rlStderr = readline.createInterface({ input: proc.stderr, crlfDelay: Infinity });
|
|
176
|
+
rlStdout.on('line', (line) => {
|
|
177
|
+
if (line.startsWith("recall_ai_command|")) {
|
|
178
|
+
try {
|
|
179
|
+
const data = JSON.parse(line.substring(18));
|
|
180
|
+
switch (data.type) {
|
|
181
|
+
case "event":
|
|
182
|
+
if (data.event) {
|
|
183
|
+
const event = JSON.parse(data.event);
|
|
184
|
+
emitEvent(event.type, event.payload);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
case "response":
|
|
188
|
+
const pendingCommand = pendingCommands[data.commandId];
|
|
189
|
+
if (pendingCommand) {
|
|
190
|
+
if (data.status === "success") {
|
|
191
|
+
pendingCommand.resolve(data.result);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
pendingCommand.reject(new Error(data.result));
|
|
195
|
+
}
|
|
196
|
+
delete pendingCommands[data.commandId];
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
logError("Desktop SDK: Failed to parse incoming data:", String(err));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
if (process.env.RECALLAI_DESKTOP_SDK_DEV) {
|
|
207
|
+
console.log(line);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
rlStderr.on('line', (line) => {
|
|
212
|
+
if (process.env.RECALLAI_DESKTOP_SDK_DEV) {
|
|
213
|
+
console.error(line);
|
|
214
|
+
}
|
|
190
215
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
216
|
+
proc.on('error', (error) => {
|
|
217
|
+
flushPendingCommands(new Error(`Process error: ${error.message}`));
|
|
218
|
+
emitEvent('error', {
|
|
219
|
+
type: 'process',
|
|
220
|
+
message: `The Desktop SDK server process has failed to start or exited improperly.`
|
|
221
|
+
});
|
|
222
|
+
logError(`Desktop SDK: Process error: ${error.message}`);
|
|
223
|
+
});
|
|
224
|
+
proc.on('close', async (code, signal) => {
|
|
225
|
+
flushPendingCommands(new Error(`Process exited with code ${code}, signal ${signal}.`));
|
|
226
|
+
emitEvent('shutdown', { code: code ?? 0, signal: signal ?? '' });
|
|
227
|
+
if (code === 0 || signal === 'SIGINT') {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
logError(`Desktop SDK: Process exited with code ${code}, signal ${signal}`);
|
|
231
|
+
emitEvent('error', {
|
|
232
|
+
type: 'process',
|
|
233
|
+
message: "The Desktop SDK server process exited unexpectedly."
|
|
234
|
+
});
|
|
235
|
+
proc = null;
|
|
236
|
+
unexpectedShutdown = true;
|
|
237
|
+
if (lastOptions.restartOnError && remainingAutomaticRestarts > 0) {
|
|
238
|
+
remainingAutomaticRestarts--;
|
|
239
|
+
logError(`Automatically restarting Desktop SDK due to unexpected exit! Automatic restarts left: ${remainingAutomaticRestarts}`);
|
|
240
|
+
doInit(lastOptions);
|
|
241
|
+
}
|
|
206
242
|
});
|
|
207
|
-
|
|
208
|
-
proc = null;
|
|
209
|
-
unexpectedShutdown = true;
|
|
210
|
-
|
|
211
|
-
if (lastOptions.restartOnError && remainingAutomaticRestarts > 0) {
|
|
212
|
-
remainingAutomaticRestarts--;
|
|
213
|
-
logError(`Automatically restarting Desktop SDK due to unexpected exit! Automatic restarts left: ${remainingAutomaticRestarts}`);
|
|
214
|
-
doInit(lastOptions);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
243
|
}
|
|
218
|
-
|
|
219
244
|
function sendCommand(command, params = {}) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (command !== "log") {
|
|
239
|
-
doLog("info", ["Sending command: " + payloadStr], false);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
245
|
+
return new Promise((resolve, reject) => {
|
|
246
|
+
if (!proc || !proc.stdin) {
|
|
247
|
+
reject(new Error("The Desktop SDK is not started; call `init` to start it."));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const commandId = (0, uuid_1.v4)();
|
|
251
|
+
pendingCommands[commandId] = { resolve, reject };
|
|
252
|
+
const payload = {
|
|
253
|
+
command,
|
|
254
|
+
commandId,
|
|
255
|
+
params
|
|
256
|
+
};
|
|
257
|
+
const payloadStr = JSON.stringify(payload);
|
|
258
|
+
proc.stdin.write(payloadStr + "\n");
|
|
259
|
+
if (command !== "log") {
|
|
260
|
+
doLog("info", ["Sending command: " + payloadStr], false);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
242
263
|
}
|
|
243
|
-
|
|
244
264
|
async function doInit(options) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
await sendCommand("init", { config: JSON.stringify(options) });
|
|
253
|
-
flushLogBuffer();
|
|
265
|
+
startProcess();
|
|
266
|
+
if (unexpectedShutdown) {
|
|
267
|
+
logError("Desktop SDK: Recovered from unexpected shutdown");
|
|
268
|
+
unexpectedShutdown = false;
|
|
269
|
+
}
|
|
270
|
+
await sendCommand("init", { config: JSON.stringify(options) });
|
|
271
|
+
flushLogBuffer();
|
|
254
272
|
}
|
|
255
|
-
|
|
256
273
|
async function init(options) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (options.restartOnError === undefined)
|
|
272
|
-
options.restartOnError = true;
|
|
273
|
-
|
|
274
|
-
lastOptions = options;
|
|
275
|
-
return doInit(options);
|
|
274
|
+
if (process.platform !== "darwin" && process.platform !== "win32") {
|
|
275
|
+
throw new Error(`Platform ${process.platform} is not supported by Desktop SDK`);
|
|
276
|
+
}
|
|
277
|
+
const { api_url, apiUrl, dev } = options;
|
|
278
|
+
options.api_url = api_url ?? apiUrl ?? "https://api.recall.ai";
|
|
279
|
+
if (!dev && (!options.api_url || !options.api_url.startsWith("https"))) {
|
|
280
|
+
throw new Error(`apiUrl must be an https url, got: ${options.api_url}`);
|
|
281
|
+
}
|
|
282
|
+
if (options.restartOnError === undefined) {
|
|
283
|
+
options.restartOnError = true;
|
|
284
|
+
}
|
|
285
|
+
lastOptions = options;
|
|
286
|
+
await doInit(options);
|
|
287
|
+
return null;
|
|
276
288
|
}
|
|
277
|
-
|
|
278
289
|
async function shutdown() {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return result;
|
|
290
|
+
const result = await sendCommand("shutdown");
|
|
291
|
+
if (proc) {
|
|
292
|
+
const currentProc = proc;
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
if (!currentProc.killed) {
|
|
295
|
+
currentProc.kill();
|
|
296
|
+
}
|
|
297
|
+
}, 5000);
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
291
300
|
}
|
|
292
|
-
|
|
293
301
|
function startRecording(config) {
|
|
294
|
-
|
|
302
|
+
return sendCommand("startRecording", { config: JSON.stringify(config) });
|
|
295
303
|
}
|
|
296
|
-
|
|
297
304
|
function stopRecording({ windowId }) {
|
|
298
|
-
|
|
305
|
+
return sendCommand("stopRecording", { windowId });
|
|
299
306
|
}
|
|
300
|
-
|
|
301
307
|
function pauseRecording({ windowId }) {
|
|
302
|
-
|
|
308
|
+
return sendCommand("pauseRecording", { windowId });
|
|
303
309
|
}
|
|
304
|
-
|
|
305
310
|
function resumeRecording({ windowId }) {
|
|
306
|
-
|
|
311
|
+
return sendCommand("resumeRecording", { windowId });
|
|
307
312
|
}
|
|
308
|
-
|
|
309
313
|
function uploadRecording({ windowId }) {
|
|
310
|
-
|
|
314
|
+
return sendCommand("uploadRecording", { windowId });
|
|
311
315
|
}
|
|
312
|
-
|
|
313
316
|
function prepareDesktopAudioRecording() {
|
|
314
|
-
|
|
317
|
+
return sendCommand("prepareDesktopAudioRecording");
|
|
315
318
|
}
|
|
316
|
-
|
|
317
319
|
function requestPermission(permission) {
|
|
318
|
-
|
|
320
|
+
return sendCommand("requestPermission", { permission });
|
|
319
321
|
}
|
|
320
|
-
|
|
321
322
|
function addEventListener(type, callback) {
|
|
322
|
-
|
|
323
|
+
listeners.push({ type, callback });
|
|
323
324
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
requestPermission
|
|
325
|
+
const RecallAiSdk = {
|
|
326
|
+
init,
|
|
327
|
+
shutdown,
|
|
328
|
+
startRecording,
|
|
329
|
+
stopRecording,
|
|
330
|
+
pauseRecording,
|
|
331
|
+
resumeRecording,
|
|
332
|
+
uploadRecording,
|
|
333
|
+
prepareDesktopAudioRecording,
|
|
334
|
+
requestPermission,
|
|
335
|
+
addEventListener,
|
|
336
336
|
};
|
|
337
|
+
exports.default = RecallAiSdk;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recallai/desktop-sdk",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Recall Desktop SDK",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"install": "node ./setup.js",
|
|
16
|
-
"validate-types": "tsc --noEmit index.d.ts"
|
|
16
|
+
"validate-types": "tsc --noEmit index.d.ts",
|
|
17
|
+
"build": "tsc -p tsconfig.build.json"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
20
|
+
"@types/node": "^24.2.0",
|
|
19
21
|
"typescript": "^5.3.3"
|
|
20
22
|
},
|
|
21
|
-
"commit_sha": "
|
|
23
|
+
"commit_sha": "6b2ffc56b52550ef46336f4ed9646d20f0e4b722"
|
|
22
24
|
}
|