dashcam 0.8.4 → 1.0.1-beta.2
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/.dashcam/cli-config.json +3 -0
- package/.dashcam/recording.log +135 -0
- package/.dashcam/web-config.json +11 -0
- package/.github/RELEASE.md +59 -0
- package/.github/workflows/build.yml +103 -0
- package/.github/workflows/publish.yml +43 -0
- package/.github/workflows/release.yml +107 -0
- package/LOG_TRACKING_GUIDE.md +225 -0
- package/README.md +709 -155
- package/bin/dashcam.cjs +8 -0
- package/bin/dashcam.js +542 -0
- package/bin/index.js +63 -0
- package/examples/execute-script.js +152 -0
- package/examples/simple-test.js +37 -0
- package/lib/applicationTracker.js +311 -0
- package/lib/auth.js +222 -0
- package/lib/binaries.js +21 -0
- package/lib/config.js +34 -0
- package/lib/extension-logs/helpers.js +182 -0
- package/lib/extension-logs/index.js +347 -0
- package/lib/extension-logs/manager.js +344 -0
- package/lib/ffmpeg.js +156 -0
- package/lib/logTracker.js +23 -0
- package/lib/logger.js +118 -0
- package/lib/logs/index.js +432 -0
- package/lib/permissions.js +85 -0
- package/lib/processManager.js +255 -0
- package/lib/recorder.js +675 -0
- package/lib/store.js +58 -0
- package/lib/tracking/FileTracker.js +98 -0
- package/lib/tracking/FileTrackerManager.js +62 -0
- package/lib/tracking/LogsTracker.js +147 -0
- package/lib/tracking/active-win.js +212 -0
- package/lib/tracking/icons/darwin.js +39 -0
- package/lib/tracking/icons/index.js +167 -0
- package/lib/tracking/icons/windows.js +27 -0
- package/lib/tracking/idle.js +82 -0
- package/lib/tracking.js +23 -0
- package/lib/uploader.js +449 -0
- package/lib/utilities/jsonl.js +77 -0
- package/lib/webLogsDaemon.js +234 -0
- package/lib/websocket/server.js +223 -0
- package/package.json +50 -21
- package/recording.log +814 -0
- package/sea-bundle.mjs +34595 -0
- package/test-page.html +15 -0
- package/test.log +1 -0
- package/test_run.log +48 -0
- package/test_workflow.sh +80 -0
- package/examples/crash-test.js +0 -11
- package/examples/github-issue.sh +0 -1
- package/examples/protocol.html +0 -22
- package/index.js +0 -158
- package/lib.js +0 -199
- package/recorder.js +0 -85
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { server } from './websocket/server.js';
|
|
2
|
+
import { WebTrackerManager } from './extension-logs/manager.js';
|
|
3
|
+
import { WebLogsTracker } from './extension-logs/index.js';
|
|
4
|
+
import { logger, setVerbose } from './logger.js';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
const DAEMON_DIR = path.join(process.cwd(), '.dashcam');
|
|
9
|
+
const DAEMON_PID_FILE = path.join(DAEMON_DIR, 'daemon.pid');
|
|
10
|
+
const DAEMON_CONFIG_FILE = path.join(DAEMON_DIR, 'web-config.json');
|
|
11
|
+
|
|
12
|
+
// Ensure daemon directory exists
|
|
13
|
+
if (!fs.existsSync(DAEMON_DIR)) {
|
|
14
|
+
fs.mkdirSync(DAEMON_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class WebLogsDaemon {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.webTrackerManager = null;
|
|
20
|
+
this.webWatchTracker = null;
|
|
21
|
+
this.isRunning = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async start() {
|
|
25
|
+
if (this.isRunning) {
|
|
26
|
+
logger.debug('Web logs daemon already running, skipping start');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Enable verbose logging for daemon
|
|
31
|
+
setVerbose(true);
|
|
32
|
+
logger.info('Starting web logs daemon...');
|
|
33
|
+
try {
|
|
34
|
+
// Start WebSocket server
|
|
35
|
+
logger.debug('Initializing WebSocket server...');
|
|
36
|
+
await server.start();
|
|
37
|
+
logger.info(`WebSocket server started on port ${server.port}`);
|
|
38
|
+
|
|
39
|
+
// Create web tracker manager
|
|
40
|
+
logger.debug('Creating WebTrackerManager...');
|
|
41
|
+
this.webTrackerManager = new WebTrackerManager(server);
|
|
42
|
+
|
|
43
|
+
// Load existing config if available
|
|
44
|
+
logger.debug('Loading daemon configuration...');
|
|
45
|
+
const config = this.loadConfig();
|
|
46
|
+
logger.debug('Loaded config:', { configKeys: Object.keys(config), configCount: Object.keys(config).length });
|
|
47
|
+
|
|
48
|
+
this.webWatchTracker = new WebLogsTracker({
|
|
49
|
+
config,
|
|
50
|
+
webTrackerManager: this.webTrackerManager
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.isRunning = true;
|
|
54
|
+
|
|
55
|
+
// Write PID file
|
|
56
|
+
logger.debug('Writing daemon PID file...');
|
|
57
|
+
fs.writeFileSync(DAEMON_PID_FILE, process.pid.toString());
|
|
58
|
+
|
|
59
|
+
logger.info('Web logs daemon started successfully');
|
|
60
|
+
|
|
61
|
+
// Keep process alive
|
|
62
|
+
process.on('SIGTERM', () => {
|
|
63
|
+
logger.info('Received SIGTERM, stopping daemon...');
|
|
64
|
+
this.stop();
|
|
65
|
+
});
|
|
66
|
+
process.on('SIGINT', () => {
|
|
67
|
+
logger.info('Received SIGINT, stopping daemon...');
|
|
68
|
+
this.stop();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error('Failed to start web logs daemon', { error: error.message, stack: error.stack });
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
stop() {
|
|
78
|
+
if (!this.isRunning) {
|
|
79
|
+
logger.debug('Web logs daemon not running, skipping stop');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
logger.info('Stopping web logs daemon...');
|
|
84
|
+
|
|
85
|
+
if (this.webWatchTracker) {
|
|
86
|
+
logger.debug('Destroying web watch tracker...');
|
|
87
|
+
this.webWatchTracker.destroy();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (this.webTrackerManager) {
|
|
91
|
+
logger.debug('Destroying web tracker manager...');
|
|
92
|
+
this.webTrackerManager.destroy();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
logger.debug('Stopping WebSocket server...');
|
|
96
|
+
server.stop();
|
|
97
|
+
|
|
98
|
+
// Remove PID file
|
|
99
|
+
if (fs.existsSync(DAEMON_PID_FILE)) {
|
|
100
|
+
logger.debug('Removing daemon PID file...');
|
|
101
|
+
fs.unlinkSync(DAEMON_PID_FILE);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.isRunning = false;
|
|
105
|
+
logger.info('Web logs daemon stopped');
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
updateConfig(config) {
|
|
110
|
+
logger.debug('Updating daemon config...', { configKeys: Object.keys(config) });
|
|
111
|
+
if (this.webWatchTracker) {
|
|
112
|
+
this.webWatchTracker.updateConfig(config);
|
|
113
|
+
}
|
|
114
|
+
this.saveConfig(config);
|
|
115
|
+
logger.info('Daemon config updated successfully');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
loadConfig() {
|
|
119
|
+
try {
|
|
120
|
+
if (fs.existsSync(DAEMON_CONFIG_FILE)) {
|
|
121
|
+
logger.debug('Loading daemon config from file...', { configFile: DAEMON_CONFIG_FILE });
|
|
122
|
+
const data = fs.readFileSync(DAEMON_CONFIG_FILE, 'utf8');
|
|
123
|
+
const config = JSON.parse(data);
|
|
124
|
+
logger.debug('Daemon config loaded successfully', { configKeys: Object.keys(config) });
|
|
125
|
+
return config;
|
|
126
|
+
} else {
|
|
127
|
+
logger.debug('No daemon config file found, using empty config');
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
logger.error('Failed to load daemon config', { error: error.message, configFile: DAEMON_CONFIG_FILE });
|
|
131
|
+
}
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
saveConfig(config) {
|
|
136
|
+
try {
|
|
137
|
+
logger.debug('Saving daemon config to file...', { configFile: DAEMON_CONFIG_FILE, configKeys: Object.keys(config) });
|
|
138
|
+
fs.writeFileSync(DAEMON_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
139
|
+
logger.debug('Daemon config saved successfully');
|
|
140
|
+
} catch (error) {
|
|
141
|
+
logger.error('Failed to save daemon config', { error: error.message, configFile: DAEMON_CONFIG_FILE });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static isDaemonRunning() {
|
|
146
|
+
try {
|
|
147
|
+
if (!fs.existsSync(DAEMON_PID_FILE)) {
|
|
148
|
+
logger.debug('Daemon PID file does not exist, daemon not running');
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim());
|
|
153
|
+
if (isNaN(pid)) {
|
|
154
|
+
logger.warn('Invalid PID in daemon PID file', { pidFile: DAEMON_PID_FILE });
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
logger.debug('Checking if daemon process is still running...', { pid });
|
|
159
|
+
// Check if process is still running
|
|
160
|
+
process.kill(pid, 0);
|
|
161
|
+
logger.debug('Daemon process is running', { pid });
|
|
162
|
+
return true;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
logger.debug('Daemon process not running or not accessible', { error: error.message });
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static async ensureDaemonRunning() {
|
|
170
|
+
if (!WebLogsDaemon.isDaemonRunning()) {
|
|
171
|
+
logger.info('Web logs daemon not running, starting it...');
|
|
172
|
+
|
|
173
|
+
// Spawn daemon process
|
|
174
|
+
const { spawn } = await import('child_process');
|
|
175
|
+
const child = spawn('node', [
|
|
176
|
+
path.join(process.cwd(), 'bin/dashcam.js'),
|
|
177
|
+
'_internal_daemon'
|
|
178
|
+
], {
|
|
179
|
+
detached: true,
|
|
180
|
+
stdio: 'inherit' // Changed from 'ignore' to 'inherit' for debugging
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
logger.debug('Spawned daemon process', { pid: child.pid });
|
|
184
|
+
child.unref();
|
|
185
|
+
|
|
186
|
+
// Wait a moment for daemon to start
|
|
187
|
+
logger.debug('Waiting for daemon to start...');
|
|
188
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
189
|
+
|
|
190
|
+
if (!WebLogsDaemon.isDaemonRunning()) {
|
|
191
|
+
logger.error('Failed to start web logs daemon after spawn attempt');
|
|
192
|
+
throw new Error('Failed to start web logs daemon');
|
|
193
|
+
} else {
|
|
194
|
+
logger.info('Web logs daemon started successfully');
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
logger.debug('Web logs daemon already running');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static stopDaemon() {
|
|
202
|
+
try {
|
|
203
|
+
if (!fs.existsSync(DAEMON_PID_FILE)) {
|
|
204
|
+
logger.debug('No daemon PID file found, daemon not running');
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim());
|
|
209
|
+
if (isNaN(pid)) {
|
|
210
|
+
logger.warn('Invalid PID in daemon PID file');
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
logger.info('Stopping daemon process...', { pid });
|
|
215
|
+
process.kill(pid, 'SIGTERM');
|
|
216
|
+
|
|
217
|
+
// Wait for cleanup
|
|
218
|
+
setTimeout(() => {
|
|
219
|
+
if (fs.existsSync(DAEMON_PID_FILE)) {
|
|
220
|
+
logger.debug('Removing daemon PID file after cleanup timeout');
|
|
221
|
+
fs.unlinkSync(DAEMON_PID_FILE);
|
|
222
|
+
}
|
|
223
|
+
}, 1000);
|
|
224
|
+
|
|
225
|
+
logger.info('Daemon stop signal sent');
|
|
226
|
+
return true;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
logger.error('Failed to stop daemon', { error: error.message });
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export { WebLogsDaemon, DAEMON_CONFIG_FILE };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import { logger } from '../logger.js';
|
|
3
|
+
|
|
4
|
+
// Simple ref implementation for reactivity (adapted from Vue's ref)
|
|
5
|
+
function ref(value) {
|
|
6
|
+
return {
|
|
7
|
+
value,
|
|
8
|
+
_isRef: true
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class WSServer {
|
|
13
|
+
#socket = null;
|
|
14
|
+
#handlers = {};
|
|
15
|
+
|
|
16
|
+
constructor(ports) {
|
|
17
|
+
this.ports = ports;
|
|
18
|
+
this.port = null;
|
|
19
|
+
this.isListening = ref(false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
on(event, handler) {
|
|
23
|
+
if (!this.#handlers[event]) this.#handlers[event] = [];
|
|
24
|
+
if (this.#handlers[event].find((cb) => cb === handler))
|
|
25
|
+
throw new Error('Handler already registered');
|
|
26
|
+
this.#handlers[event].push(handler);
|
|
27
|
+
return () => {
|
|
28
|
+
this.#handlers[event] = this.#handlers[event].filter((cb) => cb !== handler);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
emit(event, payload) {
|
|
33
|
+
if (!this.#handlers[event]) return;
|
|
34
|
+
for (const cb of this.#handlers[event]) {
|
|
35
|
+
try {
|
|
36
|
+
cb(payload);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
logger.error(
|
|
39
|
+
'Failed calling handler for event ' +
|
|
40
|
+
event +
|
|
41
|
+
' with error ' +
|
|
42
|
+
err.message
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async start() {
|
|
49
|
+
if (this.#socket) {
|
|
50
|
+
logger.error('The websocket server is already started');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
logger.debug('WebSocketServer: Starting server, trying ports...', { ports: this.ports });
|
|
55
|
+
for (const port of this.ports) {
|
|
56
|
+
const ws = await new Promise((resolve) => {
|
|
57
|
+
logger.debug('WebSocketServer: Trying port ' + port);
|
|
58
|
+
const ws = new WebSocketServer({ port, host: '127.0.0.1' });
|
|
59
|
+
ws.on('error', () => {
|
|
60
|
+
logger.debug('WebSocketServer: Failed to listen on port ' + port);
|
|
61
|
+
resolve();
|
|
62
|
+
});
|
|
63
|
+
ws.on('listening', () => {
|
|
64
|
+
logger.debug('WebSocketServer: Successfully listening on port ' + port);
|
|
65
|
+
resolve(ws);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (ws) return this.#setup(ws, port);
|
|
70
|
+
}
|
|
71
|
+
throw new Error('Unable to listen to any of the provided ports');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#setup(socket, port) {
|
|
75
|
+
logger.debug('WebSocketServer: Setting up server on port', { port });
|
|
76
|
+
|
|
77
|
+
const states = {
|
|
78
|
+
NEW: 'NEW',
|
|
79
|
+
SENT_HEADERS: 'SENT_HEADERS',
|
|
80
|
+
CONNECTED: 'CONNECTED',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this.port = port;
|
|
84
|
+
this.#socket = socket;
|
|
85
|
+
|
|
86
|
+
this.#socket.on('close', (arg) => {
|
|
87
|
+
logger.debug('WebSocketServer: Server closed', { arg });
|
|
88
|
+
this.#socket = null;
|
|
89
|
+
this.isListening.value = false;
|
|
90
|
+
this.emit('close', arg);
|
|
91
|
+
});
|
|
92
|
+
this.#socket.on('error', (arg) => {
|
|
93
|
+
logger.error('WebSocketServer: Server error', { error: arg });
|
|
94
|
+
this.#socket = null;
|
|
95
|
+
this.isListening.value = false;
|
|
96
|
+
this.emit('error', arg);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.#socket.on('connection', (client) => {
|
|
100
|
+
logger.info('WebSocketServer: New client connection established');
|
|
101
|
+
|
|
102
|
+
let state = states.NEW;
|
|
103
|
+
const failValidation = () => {
|
|
104
|
+
logger.warn('WebSocketServer: Client validation failed, closing connection');
|
|
105
|
+
client.send(
|
|
106
|
+
JSON.stringify({ error: 'Unidentified client, closing socket' })
|
|
107
|
+
);
|
|
108
|
+
client.close();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const timeout = setTimeout(() => {
|
|
112
|
+
if (state === states.CONNECTED) return;
|
|
113
|
+
logger.warn('WebSocketServer: Client validation timeout, closing connection');
|
|
114
|
+
failValidation();
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
}, 10000);
|
|
117
|
+
|
|
118
|
+
client.on('message', (data, isBinary) => {
|
|
119
|
+
let message = isBinary ? data : data.toString();
|
|
120
|
+
logger.debug('WebSocketServer: Received message from client', {
|
|
121
|
+
isBinary,
|
|
122
|
+
messageLength: message.length,
|
|
123
|
+
state
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
message = JSON.parse(message);
|
|
128
|
+
logger.debug('WebSocketServer: Parsed message', { type: message.type, hasPayload: !!message.payload });
|
|
129
|
+
} catch (err) {
|
|
130
|
+
logger.debug('WebSocketServer: Message is not JSON, treating as raw string');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (state === states.SENT_HEADERS) {
|
|
134
|
+
if (message === 'dashcam_extension_socket_confirm') {
|
|
135
|
+
state = states.CONNECTED;
|
|
136
|
+
logger.info('WebSocketServer: Client successfully validated and connected');
|
|
137
|
+
this.emit('connection', client);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
logger.warn('WebSocketServer: Invalid confirmation message from client');
|
|
141
|
+
failValidation();
|
|
142
|
+
clearTimeout(timeout);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.emit('message', message, client);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
logger.debug('WebSocketServer: Sending connection header to client');
|
|
149
|
+
client.send('dashcam_desktop_socket_connected', (err) => {
|
|
150
|
+
if (err) {
|
|
151
|
+
logger.error('WebSocketServer: Failed to send connection header', { error: err.message });
|
|
152
|
+
client.close();
|
|
153
|
+
clearTimeout(timeout);
|
|
154
|
+
} else {
|
|
155
|
+
logger.debug('WebSocketServer: Connection header sent, waiting for confirmation');
|
|
156
|
+
state = states.SENT_HEADERS;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
this.isListening.value = true;
|
|
162
|
+
logger.info('WebSocketServer: Setup complete, server is listening', { port });
|
|
163
|
+
this.emit('listening', port);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
broadcast(message) {
|
|
167
|
+
if (!this.#socket) {
|
|
168
|
+
logger.error('WebSocketServer: Cannot broadcast, server not currently running');
|
|
169
|
+
throw new Error('Server not currently running');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
logger.debug('WebSocketServer: Broadcasting message to all clients', {
|
|
173
|
+
clientCount: this.#socket.clients.size,
|
|
174
|
+
messageType: message.type || 'raw'
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
this.#socket.clients.forEach((client) => {
|
|
178
|
+
try {
|
|
179
|
+
this.send(client, message);
|
|
180
|
+
} catch (err) {
|
|
181
|
+
logger.error('WebSocketServer: Failed to send message to client', { error: err.message });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
send(client, message) {
|
|
187
|
+
if (!this.#socket) {
|
|
188
|
+
logger.error('WebSocketServer: Cannot send, server not currently running');
|
|
189
|
+
throw new Error('Server not currently running');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
logger.debug('WebSocketServer: Sending message to client', {
|
|
193
|
+
messageType: message.type || 'raw',
|
|
194
|
+
messageLength: JSON.stringify(message).length
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
client.send(
|
|
198
|
+
typeof message === 'string' ? message : JSON.stringify(message)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async stop() {
|
|
203
|
+
if (this.#socket) {
|
|
204
|
+
logger.debug('WebSocketServer: Stopping server...');
|
|
205
|
+
this.#socket.close();
|
|
206
|
+
this.#socket = null;
|
|
207
|
+
this.isListening.value = false;
|
|
208
|
+
logger.info('WebSocketServer: Server stopped');
|
|
209
|
+
} else {
|
|
210
|
+
logger.debug('WebSocketServer: Server already stopped');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// This list of ports is randomly generated of ports between 10000 and 65000
|
|
216
|
+
// This exact same list of ports needs to be used on the desktop app's websocket server
|
|
217
|
+
const server = new WSServer([
|
|
218
|
+
10368, 16240, 21855, 24301, 25928,
|
|
219
|
+
// 27074, 31899, 34205, 36109, 37479, 38986,
|
|
220
|
+
// 39618, 41890, 47096, 48736, 49893, 53659, 55927, 56001, 62895,
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
export { server, ref };
|
package/package.json
CHANGED
|
@@ -1,33 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dashcam",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "1.0.1-beta.2",
|
|
4
|
+
"description": "Minimal CLI version of Dashcam desktop app",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dashcam": "./bin/dashcam.js"
|
|
8
|
+
},
|
|
6
9
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
10
|
+
"start": "node bin/dashcam.js",
|
|
11
|
+
"prebuild": "npm run bundle",
|
|
12
|
+
"bundle": "esbuild bin/dashcam.js --bundle --platform=node --target=node20 --format=cjs --outfile=dist/bundle.cjs --external:@mapbox/node-pre-gyp --external:file-icon --external:get-windows --banner:js=\"const importMetaUrl = typeof document === 'undefined' ? require('url').pathToFileURL(__filename).href : document.currentScript && document.currentScript.src || new URL('index.js', document.baseURI).href;\" --define:import.meta.url='importMetaUrl'",
|
|
13
|
+
"build": "pkg dist/bundle.cjs --out-path dist --compress GZip --public",
|
|
14
|
+
"build:macos": "pkg dist/bundle.cjs --targets node20-macos-x64 --output dist/dashcam-macos-x64 --compress GZip --public && pkg dist/bundle.cjs --targets node20-macos-arm64 --output dist/dashcam-macos-arm64 --compress GZip --public",
|
|
15
|
+
"build:linux": "pkg dist/bundle.cjs --targets node20-linux-x64 --output dist/dashcam-linux-x64 --compress GZip --public && pkg dist/bundle.cjs --targets node20-linux-arm64 --output dist/dashcam-linux-arm64 --compress GZip --public",
|
|
16
|
+
"build:windows": "pkg dist/bundle.cjs --targets node20-win-x64 --output dist/dashcam-windows-x64.exe --compress GZip --public && pkg dist/bundle.cjs --targets node20-win-arm64 --output dist/dashcam-windows-arm64.exe --compress GZip --public",
|
|
17
|
+
"build:all": "npm run build:macos && npm run build:linux && npm run build:windows"
|
|
9
18
|
},
|
|
10
|
-
"
|
|
11
|
-
"
|
|
19
|
+
"pkg": {
|
|
20
|
+
"scripts": "dist/bundle.cjs",
|
|
21
|
+
"targets": [
|
|
22
|
+
"node20-macos-x64",
|
|
23
|
+
"node20-macos-arm64",
|
|
24
|
+
"node20-linux-x64",
|
|
25
|
+
"node20-linux-arm64",
|
|
26
|
+
"node20-win-x64",
|
|
27
|
+
"node20-win-arm64"
|
|
28
|
+
],
|
|
29
|
+
"outputPath": "dist",
|
|
30
|
+
"ignore": [
|
|
31
|
+
"node_modules/get-windows/**",
|
|
32
|
+
"node_modules/file-icon/**"
|
|
33
|
+
]
|
|
12
34
|
},
|
|
13
|
-
"author": "",
|
|
14
|
-
"license": "ISC",
|
|
15
35
|
"dependencies": {
|
|
16
|
-
"@
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
36
|
+
"@aws-sdk/client-s3": "^3.919.0",
|
|
37
|
+
"@aws-sdk/client-sts": "^3.919.0",
|
|
38
|
+
"@aws-sdk/lib-storage": "^3.919.0",
|
|
39
|
+
"auth0": "^4.0.0",
|
|
40
|
+
"commander": "^11.0.0",
|
|
41
|
+
"execa": "^9.6.0",
|
|
42
|
+
"ffmpeg-static": "^5.2.0",
|
|
43
|
+
"ffprobe-static": "^3.1.0",
|
|
44
|
+
"file-icon": "^6.0.0",
|
|
45
|
+
"get-windows": "^9.2.3",
|
|
46
|
+
"got": "^11.8.6",
|
|
47
|
+
"mask-sensitive-data": "^0.11.5",
|
|
48
|
+
"node-fetch": "^2.7.0",
|
|
49
|
+
"open": "^9.1.0",
|
|
50
|
+
"tail": "^2.2.6",
|
|
51
|
+
"winston": "^3.11.0",
|
|
52
|
+
"ws": "^8.18.3"
|
|
23
53
|
},
|
|
24
54
|
"devDependencies": {
|
|
25
|
-
"
|
|
55
|
+
"@yao-pkg/pkg": "^6.10.1",
|
|
56
|
+
"esbuild": "^0.19.0"
|
|
26
57
|
},
|
|
58
|
+
"type": "module",
|
|
27
59
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
29
|
-
},
|
|
30
|
-
"volta": {
|
|
31
|
-
"node": "22.19.0"
|
|
60
|
+
"node": ">=20.0.0"
|
|
32
61
|
}
|
|
33
62
|
}
|