episoda 0.2.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.
Files changed (111) hide show
  1. package/dist/commands/auth.d.ts +22 -0
  2. package/dist/commands/auth.d.ts.map +1 -0
  3. package/dist/commands/auth.js +384 -0
  4. package/dist/commands/auth.js.map +1 -0
  5. package/dist/commands/dev.d.ts +20 -0
  6. package/dist/commands/dev.d.ts.map +1 -0
  7. package/dist/commands/dev.js +305 -0
  8. package/dist/commands/dev.js.map +1 -0
  9. package/dist/commands/status.d.ts +9 -0
  10. package/dist/commands/status.d.ts.map +1 -0
  11. package/dist/commands/status.js +75 -0
  12. package/dist/commands/status.js.map +1 -0
  13. package/dist/commands/stop.d.ts +17 -0
  14. package/dist/commands/stop.d.ts.map +1 -0
  15. package/dist/commands/stop.js +81 -0
  16. package/dist/commands/stop.js.map +1 -0
  17. package/dist/core/auth.d.ts +26 -0
  18. package/dist/core/auth.d.ts.map +1 -0
  19. package/dist/core/auth.js +113 -0
  20. package/dist/core/auth.js.map +1 -0
  21. package/dist/core/command-protocol.d.ts +262 -0
  22. package/dist/core/command-protocol.d.ts.map +1 -0
  23. package/dist/core/command-protocol.js +13 -0
  24. package/dist/core/command-protocol.js.map +1 -0
  25. package/dist/core/connection-manager.d.ts +58 -0
  26. package/dist/core/connection-manager.d.ts.map +1 -0
  27. package/dist/core/connection-manager.js +215 -0
  28. package/dist/core/connection-manager.js.map +1 -0
  29. package/dist/core/errors.d.ts +18 -0
  30. package/dist/core/errors.d.ts.map +1 -0
  31. package/dist/core/errors.js +55 -0
  32. package/dist/core/errors.js.map +1 -0
  33. package/dist/core/git-executor.d.ts +157 -0
  34. package/dist/core/git-executor.d.ts.map +1 -0
  35. package/dist/core/git-executor.js +1605 -0
  36. package/dist/core/git-executor.js.map +1 -0
  37. package/dist/core/git-parser.d.ts +40 -0
  38. package/dist/core/git-parser.d.ts.map +1 -0
  39. package/dist/core/git-parser.js +194 -0
  40. package/dist/core/git-parser.js.map +1 -0
  41. package/dist/core/git-validator.d.ts +42 -0
  42. package/dist/core/git-validator.d.ts.map +1 -0
  43. package/dist/core/git-validator.js +102 -0
  44. package/dist/core/git-validator.js.map +1 -0
  45. package/dist/core/index.d.ts +17 -0
  46. package/dist/core/index.d.ts.map +1 -0
  47. package/dist/core/index.js +41 -0
  48. package/dist/core/index.js.map +1 -0
  49. package/dist/core/version.d.ts +9 -0
  50. package/dist/core/version.d.ts.map +1 -0
  51. package/dist/core/version.js +19 -0
  52. package/dist/core/version.js.map +1 -0
  53. package/dist/core/websocket-client.d.ts +122 -0
  54. package/dist/core/websocket-client.d.ts.map +1 -0
  55. package/dist/core/websocket-client.js +438 -0
  56. package/dist/core/websocket-client.js.map +1 -0
  57. package/dist/daemon/daemon-manager.d.ts +71 -0
  58. package/dist/daemon/daemon-manager.d.ts.map +1 -0
  59. package/dist/daemon/daemon-manager.js +289 -0
  60. package/dist/daemon/daemon-manager.js.map +1 -0
  61. package/dist/daemon/daemon-process.d.ts +13 -0
  62. package/dist/daemon/daemon-process.d.ts.map +1 -0
  63. package/dist/daemon/daemon-process.js +608 -0
  64. package/dist/daemon/daemon-process.js.map +1 -0
  65. package/dist/daemon/machine-id.d.ts +36 -0
  66. package/dist/daemon/machine-id.d.ts.map +1 -0
  67. package/dist/daemon/machine-id.js +195 -0
  68. package/dist/daemon/machine-id.js.map +1 -0
  69. package/dist/daemon/project-tracker.d.ts +92 -0
  70. package/dist/daemon/project-tracker.d.ts.map +1 -0
  71. package/dist/daemon/project-tracker.js +259 -0
  72. package/dist/daemon/project-tracker.js.map +1 -0
  73. package/dist/dev-wrapper.d.ts +88 -0
  74. package/dist/dev-wrapper.d.ts.map +1 -0
  75. package/dist/dev-wrapper.js +288 -0
  76. package/dist/dev-wrapper.js.map +1 -0
  77. package/dist/framework-detector.d.ts +29 -0
  78. package/dist/framework-detector.d.ts.map +1 -0
  79. package/dist/framework-detector.js +276 -0
  80. package/dist/framework-detector.js.map +1 -0
  81. package/dist/git-helpers/git-credential-helper.d.ts +29 -0
  82. package/dist/git-helpers/git-credential-helper.d.ts.map +1 -0
  83. package/dist/git-helpers/git-credential-helper.js +349 -0
  84. package/dist/git-helpers/git-credential-helper.js.map +1 -0
  85. package/dist/hooks/post-checkout +296 -0
  86. package/dist/hooks/pre-commit +139 -0
  87. package/dist/index.d.ts +8 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +102 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/ipc/ipc-client.d.ts +95 -0
  92. package/dist/ipc/ipc-client.d.ts.map +1 -0
  93. package/dist/ipc/ipc-client.js +204 -0
  94. package/dist/ipc/ipc-client.js.map +1 -0
  95. package/dist/ipc/ipc-server.d.ts +55 -0
  96. package/dist/ipc/ipc-server.d.ts.map +1 -0
  97. package/dist/ipc/ipc-server.js +177 -0
  98. package/dist/ipc/ipc-server.js.map +1 -0
  99. package/dist/output.d.ts +48 -0
  100. package/dist/output.d.ts.map +1 -0
  101. package/dist/output.js +129 -0
  102. package/dist/output.js.map +1 -0
  103. package/dist/utils/port-check.d.ts +15 -0
  104. package/dist/utils/port-check.d.ts.map +1 -0
  105. package/dist/utils/port-check.js +79 -0
  106. package/dist/utils/port-check.js.map +1 -0
  107. package/dist/utils/update-checker.d.ts +23 -0
  108. package/dist/utils/update-checker.d.ts.map +1 -0
  109. package/dist/utils/update-checker.js +95 -0
  110. package/dist/utils/update-checker.js.map +1 -0
  111. package/package.json +51 -0
@@ -0,0 +1,608 @@
1
+ "use strict";
2
+ /**
3
+ * Episoda Daemon Process
4
+ *
5
+ * Main entry point for the persistent daemon that:
6
+ * - Maintains WebSocket connections to multiple projects
7
+ * - Listens for IPC commands from CLI
8
+ * - Handles graceful shutdown
9
+ * - Survives terminal close
10
+ *
11
+ * This file is spawned by daemon-manager.ts in detached mode.
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const machine_id_1 = require("./machine-id");
48
+ const project_tracker_1 = require("./project-tracker");
49
+ const daemon_manager_1 = require("./daemon-manager");
50
+ const ipc_server_1 = require("../ipc/ipc-server");
51
+ const core_1 = require("../core");
52
+ const update_checker_1 = require("../utils/update-checker");
53
+ const fs = __importStar(require("fs"));
54
+ const os = __importStar(require("os"));
55
+ const path = __importStar(require("path"));
56
+ // EP783: Get current version for update checking
57
+ const packageJson = require('../../package.json');
58
+ /**
59
+ * Daemon state
60
+ */
61
+ class Daemon {
62
+ constructor() {
63
+ this.machineId = '';
64
+ this.deviceId = null; // EP726: Cached device UUID from server
65
+ this.deviceName = null; // EP661: Cached device name from server
66
+ this.flyMachineId = null; // EP735: Fly.io machine ID for sticky session routing
67
+ // EP738: Removed httpServer - device info now flows through WebSocket broadcast + database
68
+ this.connections = new Map(); // projectPath -> connection
69
+ // EP701: Track which connections are currently live (WebSocket open)
70
+ // Updated by 'auth_success' (add) and 'disconnected' (remove) events
71
+ this.liveConnections = new Set(); // projectPath
72
+ this.shuttingDown = false;
73
+ this.ipcServer = new ipc_server_1.IPCServer();
74
+ }
75
+ /**
76
+ * Start the daemon
77
+ */
78
+ async start() {
79
+ console.log('[Daemon] Starting Episoda daemon...');
80
+ // Get machine ID
81
+ this.machineId = await (0, machine_id_1.getMachineId)();
82
+ console.log(`[Daemon] Machine ID: ${this.machineId}`);
83
+ // EP726: Load cached device ID from config if available
84
+ const config = await (0, core_1.loadConfig)();
85
+ if (config?.device_id) {
86
+ this.deviceId = config.device_id;
87
+ console.log(`[Daemon] Loaded cached Device ID (UUID): ${this.deviceId}`);
88
+ }
89
+ // Start IPC server
90
+ await this.ipcServer.start();
91
+ console.log('[Daemon] IPC server started');
92
+ // EP738: Removed HTTP server - device info now flows through WebSocket broadcast + database
93
+ // Register IPC command handlers
94
+ this.registerIPCHandlers();
95
+ // Restore connections for tracked projects
96
+ await this.restoreConnections();
97
+ // Setup graceful shutdown
98
+ this.setupShutdownHandlers();
99
+ console.log('[Daemon] Daemon started successfully');
100
+ // EP783: Check for updates in background (non-blocking)
101
+ this.checkAndNotifyUpdates();
102
+ }
103
+ /**
104
+ * EP783: Check for CLI updates and auto-update in background
105
+ * Non-blocking - runs after daemon starts, fails silently on errors
106
+ */
107
+ async checkAndNotifyUpdates() {
108
+ try {
109
+ const result = await (0, update_checker_1.checkForUpdates)(packageJson.version);
110
+ if (result.updateAvailable) {
111
+ console.log(`\n⬆️ Update available: ${result.currentVersion} → ${result.latestVersion}`);
112
+ console.log(' Updating in background...\n');
113
+ (0, update_checker_1.performBackgroundUpdate)();
114
+ }
115
+ }
116
+ catch (error) {
117
+ // Silently ignore - update check is non-critical
118
+ }
119
+ }
120
+ // EP738: Removed startHttpServer - device info now flows through WebSocket broadcast + database
121
+ /**
122
+ * Register IPC command handlers
123
+ */
124
+ registerIPCHandlers() {
125
+ // Ping - health check
126
+ this.ipcServer.on('ping', async () => {
127
+ return { status: 'ok' };
128
+ });
129
+ // Status - get daemon status
130
+ // EP726: Now includes deviceId (UUID) for unified device identification
131
+ // EP738: Added hostname, platform, arch for status command (HTTP server removed)
132
+ // EP776: Use liveConnections (actual WebSocket state) instead of connections Map
133
+ this.ipcServer.on('status', async () => {
134
+ const projects = (0, project_tracker_1.getAllProjects)().map(p => ({
135
+ id: p.id,
136
+ path: p.path,
137
+ name: p.name,
138
+ connected: this.liveConnections.has(p.path),
139
+ }));
140
+ return {
141
+ running: true,
142
+ machineId: this.machineId,
143
+ deviceId: this.deviceId, // EP726: UUID for unified device identification
144
+ hostname: os.hostname(),
145
+ platform: os.platform(),
146
+ arch: os.arch(),
147
+ projects,
148
+ };
149
+ });
150
+ // EP734: Add project - now blocking, waits for connection to complete
151
+ // This eliminates the need for polling from the client side
152
+ this.ipcServer.on('add-project', async (params) => {
153
+ const { projectId, projectPath } = params;
154
+ (0, project_tracker_1.addProject)(projectId, projectPath);
155
+ try {
156
+ // Await connection - blocks until connected or fails
157
+ await this.connectProject(projectId, projectPath);
158
+ return { success: true, connected: true };
159
+ }
160
+ catch (error) {
161
+ const errorMessage = error instanceof Error ? error.message : String(error);
162
+ return { success: false, connected: false, error: errorMessage };
163
+ }
164
+ });
165
+ // EP734: Removed connection-status handler - no longer needed with blocking add-project
166
+ // Remove project
167
+ this.ipcServer.on('remove-project', async (params) => {
168
+ const { projectPath } = params;
169
+ await this.disconnectProject(projectPath);
170
+ (0, project_tracker_1.removeProject)(projectPath);
171
+ return { success: true };
172
+ });
173
+ // Connect project
174
+ this.ipcServer.on('connect-project', async (params) => {
175
+ const { projectPath } = params;
176
+ const project = (0, project_tracker_1.getAllProjects)().find(p => p.path === projectPath);
177
+ if (!project) {
178
+ throw new Error('Project not tracked');
179
+ }
180
+ await this.connectProject(project.id, projectPath);
181
+ return { success: true };
182
+ });
183
+ // Disconnect project
184
+ this.ipcServer.on('disconnect-project', async (params) => {
185
+ const { projectPath } = params;
186
+ await this.disconnectProject(projectPath);
187
+ return { success: true };
188
+ });
189
+ // Shutdown
190
+ this.ipcServer.on('shutdown', async () => {
191
+ console.log('[Daemon] Shutdown requested via IPC');
192
+ await this.shutdown();
193
+ return { success: true };
194
+ });
195
+ }
196
+ /**
197
+ * Restore WebSocket connections for tracked projects
198
+ */
199
+ async restoreConnections() {
200
+ const projects = (0, project_tracker_1.getAllProjects)();
201
+ for (const project of projects) {
202
+ try {
203
+ await this.connectProject(project.id, project.path);
204
+ }
205
+ catch (error) {
206
+ console.error(`[Daemon] Failed to restore connection for ${project.name}:`, error);
207
+ }
208
+ }
209
+ }
210
+ /**
211
+ * Connect to a project's WebSocket
212
+ */
213
+ async connectProject(projectId, projectPath) {
214
+ // Skip if already connected
215
+ if (this.connections.has(projectPath)) {
216
+ console.log(`[Daemon] Already connected to ${projectPath}`);
217
+ return;
218
+ }
219
+ // Load auth token from config
220
+ const config = await (0, core_1.loadConfig)();
221
+ if (!config || !config.access_token) {
222
+ throw new Error('No access token found. Please run: episoda auth');
223
+ }
224
+ // EP734: Removed pre-flight health check - WebSocket will fail naturally if server is down
225
+ // This saves ~200-500ms on every connection attempt
226
+ // EP734: Determine server URL using cached settings (avoids network call)
227
+ // Priority: 1. Cached local_server_url, 2. Global config api_url, 3. Default production
228
+ let serverUrl = config.api_url || process.env.EPISODA_API_URL || 'https://episoda.dev';
229
+ // Use cached local_server_url if available
230
+ if (config.project_settings?.local_server_url) {
231
+ serverUrl = config.project_settings.local_server_url;
232
+ console.log(`[Daemon] Using cached server URL: ${serverUrl}`);
233
+ }
234
+ // EP593: Connect to WebSocket server on port 3001 (standalone WebSocket server)
235
+ const serverUrlObj = new URL(serverUrl);
236
+ const wsProtocol = serverUrlObj.protocol === 'https:' ? 'wss:' : 'ws:';
237
+ const wsPort = process.env.EPISODA_WS_PORT || '3001';
238
+ const wsUrl = `${wsProtocol}//${serverUrlObj.hostname}:${wsPort}`;
239
+ console.log(`[Daemon] Connecting to ${wsUrl} for project ${projectId}...`);
240
+ // Create EpisodaClient (handles auth, reconnection, heartbeat)
241
+ const client = new core_1.EpisodaClient();
242
+ // Create GitExecutor for this project
243
+ const gitExecutor = new core_1.GitExecutor();
244
+ // Store connection
245
+ const connection = {
246
+ projectId,
247
+ projectPath,
248
+ client,
249
+ gitExecutor,
250
+ };
251
+ this.connections.set(projectPath, connection);
252
+ // Register command handler
253
+ client.on('command', async (message) => {
254
+ if (message.type === 'command' && message.command) {
255
+ console.log(`[Daemon] Received command for ${projectId}:`, message.command);
256
+ // EP605: Update activity timestamp to reset idle detection
257
+ client.updateActivity();
258
+ try {
259
+ // Execute git command with working directory
260
+ const result = await gitExecutor.execute(message.command, {
261
+ cwd: projectPath
262
+ });
263
+ // Send result back
264
+ await client.send({
265
+ type: 'result',
266
+ commandId: message.id,
267
+ result
268
+ });
269
+ console.log(`[Daemon] Command completed for ${projectId}:`, result.success ? 'success' : 'failed');
270
+ }
271
+ catch (error) {
272
+ // Send error result
273
+ await client.send({
274
+ type: 'result',
275
+ commandId: message.id,
276
+ result: {
277
+ success: false,
278
+ error: 'UNKNOWN_ERROR',
279
+ output: error instanceof Error ? error.message : String(error)
280
+ }
281
+ });
282
+ console.error(`[Daemon] Command execution error for ${projectId}:`, error);
283
+ }
284
+ }
285
+ });
286
+ // Register shutdown handler - allows server to request graceful shutdown
287
+ // EP613: Only exit on user-requested shutdowns, reconnect for server restarts
288
+ client.on('shutdown', async (message) => {
289
+ const shutdownMessage = message;
290
+ const reason = shutdownMessage.reason || 'unknown';
291
+ console.log(`[Daemon] Received shutdown request from server for ${projectId}`);
292
+ console.log(`[Daemon] Reason: ${reason} - ${shutdownMessage.message || 'No message'}`);
293
+ if (reason === 'user_requested') {
294
+ // User explicitly requested disconnect - exit cleanly
295
+ console.log(`[Daemon] User requested disconnect, shutting down...`);
296
+ await this.cleanupAndExit();
297
+ }
298
+ else {
299
+ // Server restart or deployment - don't exit, let WebSocket reconnection handle it
300
+ console.log(`[Daemon] Server shutdown (${reason}), will reconnect automatically...`);
301
+ // The WebSocket close event will trigger reconnection via EpisodaClient
302
+ // We don't call process.exit() here - just let the connection close naturally
303
+ }
304
+ });
305
+ // Register auth success handler
306
+ client.on('auth_success', async (message) => {
307
+ console.log(`[Daemon] Authenticated for project ${projectId}`);
308
+ (0, project_tracker_1.touchProject)(projectPath);
309
+ // EP701: Mark connection as live (for /device-info endpoint)
310
+ this.liveConnections.add(projectPath);
311
+ // EP595: Configure git with userId and workspaceId for post-checkout hook
312
+ // EP655: Also configure machineId for device isolation
313
+ // EP661: Cache deviceName for browser identification
314
+ // EP726: Cache deviceId (UUID) for unified device identification
315
+ // EP735: Cache flyMachineId for sticky session routing
316
+ const authMessage = message;
317
+ if (authMessage.userId && authMessage.workspaceId) {
318
+ // EP726: Pass deviceId to configureGitUser so it's stored in git config
319
+ await this.configureGitUser(projectPath, authMessage.userId, authMessage.workspaceId, this.machineId, projectId, authMessage.deviceId);
320
+ // EP610: Install git hooks after configuring git user
321
+ await this.installGitHooks(projectPath);
322
+ }
323
+ // EP661: Store device name for logging and config
324
+ if (authMessage.deviceName) {
325
+ this.deviceName = authMessage.deviceName;
326
+ console.log(`[Daemon] Device name: ${this.deviceName}`);
327
+ }
328
+ // EP726: Cache device UUID for unified device identification
329
+ // Persist to config for future use
330
+ if (authMessage.deviceId) {
331
+ this.deviceId = authMessage.deviceId;
332
+ console.log(`[Daemon] Device ID (UUID): ${this.deviceId}`);
333
+ // Persist deviceId to config file so it's available on daemon restart
334
+ await this.cacheDeviceId(authMessage.deviceId);
335
+ }
336
+ // EP735: Cache Fly.io machine ID for sticky session routing
337
+ // Only set when server is running on Fly.io, null otherwise
338
+ if (authMessage.flyMachineId) {
339
+ this.flyMachineId = authMessage.flyMachineId;
340
+ console.log(`[Daemon] Fly Machine ID: ${this.flyMachineId}`);
341
+ }
342
+ });
343
+ // Register error handler
344
+ client.on('error', (message) => {
345
+ console.error(`[Daemon] Server error for ${projectId}:`, message);
346
+ });
347
+ // EP701: Register disconnected handler to track connection state
348
+ // Removes from liveConnections immediately so /device-info returns accurate state
349
+ // Keeps entry in connections map for potential reconnection (client handles reconnect internally)
350
+ client.on('disconnected', (event) => {
351
+ const disconnectEvent = event;
352
+ console.log(`[Daemon] Connection closed for ${projectId}: code=${disconnectEvent.code}, willReconnect=${disconnectEvent.willReconnect}`);
353
+ // Always remove from liveConnections - connection is dead until reconnected
354
+ this.liveConnections.delete(projectPath);
355
+ // Only remove from connections map if we won't reconnect (intentional disconnect)
356
+ if (!disconnectEvent.willReconnect) {
357
+ this.connections.delete(projectPath);
358
+ console.log(`[Daemon] Removed connection for ${projectPath} from map`);
359
+ }
360
+ });
361
+ try {
362
+ // EP601: Read daemon PID from file
363
+ let daemonPid;
364
+ try {
365
+ const pidPath = (0, daemon_manager_1.getPidFilePath)();
366
+ if (fs.existsSync(pidPath)) {
367
+ const pidStr = fs.readFileSync(pidPath, 'utf-8').trim();
368
+ daemonPid = parseInt(pidStr, 10);
369
+ }
370
+ }
371
+ catch (pidError) {
372
+ console.warn(`[Daemon] Could not read daemon PID:`, pidError instanceof Error ? pidError.message : pidError);
373
+ }
374
+ // Connect with OAuth token, machine ID, and device info (EP596, EP601: includes daemonPid)
375
+ await client.connect(wsUrl, config.access_token, this.machineId, {
376
+ hostname: os.hostname(),
377
+ osPlatform: os.platform(),
378
+ osArch: os.arch(),
379
+ daemonPid
380
+ });
381
+ console.log(`[Daemon] Successfully connected to project ${projectId}`);
382
+ }
383
+ catch (error) {
384
+ console.error(`[Daemon] Failed to connect to ${projectId}:`, error);
385
+ this.connections.delete(projectPath);
386
+ throw error;
387
+ }
388
+ }
389
+ /**
390
+ * Disconnect from a project's WebSocket
391
+ */
392
+ async disconnectProject(projectPath) {
393
+ const connection = this.connections.get(projectPath);
394
+ if (!connection) {
395
+ return;
396
+ }
397
+ // Clear reconnect timer
398
+ if (connection.reconnectTimer) {
399
+ clearTimeout(connection.reconnectTimer);
400
+ }
401
+ // Disconnect client (handles WebSocket close gracefully)
402
+ await connection.client.disconnect();
403
+ this.connections.delete(projectPath);
404
+ this.liveConnections.delete(projectPath); // EP701: Also clean up liveConnections
405
+ console.log(`[Daemon] Disconnected from ${projectPath}`);
406
+ }
407
+ /**
408
+ * Setup graceful shutdown handlers
409
+ * EP613: Now uses cleanupAndExit to ensure PID file is removed
410
+ */
411
+ setupShutdownHandlers() {
412
+ const shutdownHandler = async (signal) => {
413
+ console.log(`[Daemon] Received ${signal}, shutting down...`);
414
+ await this.cleanupAndExit();
415
+ };
416
+ process.on('SIGTERM', () => shutdownHandler('SIGTERM'));
417
+ process.on('SIGINT', () => shutdownHandler('SIGINT'));
418
+ }
419
+ /**
420
+ * EP595: Configure git with user and workspace ID for post-checkout hook
421
+ * EP655: Added machineId for device isolation in multi-device environments
422
+ * EP725: Added projectId for main branch badge tracking
423
+ * EP726: Added deviceId (UUID) for unified device identification
424
+ *
425
+ * This stores the IDs in .git/config so the post-checkout hook can
426
+ * update module.checkout_* fields when git operations happen from terminal.
427
+ */
428
+ async configureGitUser(projectPath, userId, workspaceId, machineId, projectId, deviceId) {
429
+ try {
430
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
431
+ // Set git config values in the project's .git/config
432
+ execSync(`git config episoda.userId ${userId}`, {
433
+ cwd: projectPath,
434
+ encoding: 'utf8',
435
+ stdio: 'pipe'
436
+ });
437
+ execSync(`git config episoda.workspaceId ${workspaceId}`, {
438
+ cwd: projectPath,
439
+ encoding: 'utf8',
440
+ stdio: 'pipe'
441
+ });
442
+ // EP655: Set machineId for device isolation in post-checkout hook
443
+ execSync(`git config episoda.machineId ${machineId}`, {
444
+ cwd: projectPath,
445
+ encoding: 'utf8',
446
+ stdio: 'pipe'
447
+ });
448
+ // EP725: Set projectId for main branch badge tracking
449
+ // This ensures main branch checkouts are recorded with project_id
450
+ execSync(`git config episoda.projectId ${projectId}`, {
451
+ cwd: projectPath,
452
+ encoding: 'utf8',
453
+ stdio: 'pipe'
454
+ });
455
+ // EP726: Set deviceId (UUID) for unified device identification
456
+ // This allows the post-checkout hook to pass the UUID to the database
457
+ if (deviceId) {
458
+ execSync(`git config episoda.deviceId ${deviceId}`, {
459
+ cwd: projectPath,
460
+ encoding: 'utf8',
461
+ stdio: 'pipe'
462
+ });
463
+ }
464
+ console.log(`[Daemon] Configured git for project: episoda.userId=${userId}, machineId=${machineId}, projectId=${projectId}${deviceId ? `, deviceId=${deviceId}` : ''}`);
465
+ }
466
+ catch (error) {
467
+ // Non-fatal error - git hook just won't work
468
+ console.warn(`[Daemon] Failed to configure git user for ${projectPath}:`, error instanceof Error ? error.message : error);
469
+ }
470
+ }
471
+ /**
472
+ * EP610: Install git hooks from bundled files
473
+ *
474
+ * Installs post-checkout and pre-commit hooks to enable:
475
+ * - Branch tracking (post-checkout updates module.checkout_* fields)
476
+ * - Main branch protection (pre-commit blocks direct commits to main)
477
+ */
478
+ async installGitHooks(projectPath) {
479
+ const hooks = ['post-checkout', 'pre-commit'];
480
+ const hooksDir = path.join(projectPath, '.git', 'hooks');
481
+ // Ensure hooks directory exists
482
+ if (!fs.existsSync(hooksDir)) {
483
+ console.warn(`[Daemon] Hooks directory not found: ${hooksDir}`);
484
+ return;
485
+ }
486
+ for (const hookName of hooks) {
487
+ try {
488
+ const hookPath = path.join(hooksDir, hookName);
489
+ // Read bundled hook content
490
+ // __dirname in compiled code points to dist/daemon/, hooks are in dist/hooks/
491
+ const bundledHookPath = path.join(__dirname, '..', 'hooks', hookName);
492
+ if (!fs.existsSync(bundledHookPath)) {
493
+ console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
494
+ continue;
495
+ }
496
+ const hookContent = fs.readFileSync(bundledHookPath, 'utf-8');
497
+ // Check if hook already exists with same content
498
+ if (fs.existsSync(hookPath)) {
499
+ const existingContent = fs.readFileSync(hookPath, 'utf-8');
500
+ if (existingContent === hookContent) {
501
+ // Hook is up to date
502
+ continue;
503
+ }
504
+ }
505
+ // Write hook file
506
+ fs.writeFileSync(hookPath, hookContent, { mode: 0o755 });
507
+ console.log(`[Daemon] Installed git hook: ${hookName}`);
508
+ }
509
+ catch (error) {
510
+ // Non-fatal - just log warning
511
+ console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
512
+ }
513
+ }
514
+ }
515
+ /**
516
+ * EP726: Cache device UUID to config file
517
+ *
518
+ * Persists the device_id (UUID) received from the server so it's available
519
+ * on daemon restart without needing to re-register the device.
520
+ */
521
+ async cacheDeviceId(deviceId) {
522
+ try {
523
+ const config = await (0, core_1.loadConfig)();
524
+ if (!config) {
525
+ console.warn('[Daemon] Cannot cache device ID - no config found');
526
+ return;
527
+ }
528
+ // Only update if device_id has changed
529
+ if (config.device_id === deviceId) {
530
+ return;
531
+ }
532
+ // Update config with device_id and machine_id
533
+ const updatedConfig = {
534
+ ...config,
535
+ device_id: deviceId,
536
+ machine_id: this.machineId
537
+ };
538
+ await (0, core_1.saveConfig)(updatedConfig);
539
+ console.log(`[Daemon] Cached device ID to config: ${deviceId}`);
540
+ }
541
+ catch (error) {
542
+ console.warn('[Daemon] Failed to cache device ID:', error instanceof Error ? error.message : error);
543
+ }
544
+ }
545
+ /**
546
+ * Gracefully shutdown daemon
547
+ */
548
+ async shutdown() {
549
+ if (this.shuttingDown)
550
+ return;
551
+ this.shuttingDown = true;
552
+ console.log('[Daemon] Shutting down...');
553
+ // Close all WebSocket connections
554
+ for (const [projectPath, connection] of this.connections) {
555
+ if (connection.reconnectTimer) {
556
+ clearTimeout(connection.reconnectTimer);
557
+ }
558
+ await connection.client.disconnect();
559
+ }
560
+ this.connections.clear();
561
+ // EP738: Removed HTTP server cleanup - server no longer exists
562
+ // Stop IPC server
563
+ await this.ipcServer.stop();
564
+ console.log('[Daemon] Shutdown complete');
565
+ }
566
+ /**
567
+ * EP613: Clean up PID file and exit gracefully
568
+ * Called when user explicitly requests disconnect
569
+ */
570
+ async cleanupAndExit() {
571
+ await this.shutdown();
572
+ // Clean up PID file
573
+ try {
574
+ const pidPath = (0, daemon_manager_1.getPidFilePath)();
575
+ if (fs.existsSync(pidPath)) {
576
+ fs.unlinkSync(pidPath);
577
+ console.log('[Daemon] PID file cleaned up');
578
+ }
579
+ }
580
+ catch (error) {
581
+ console.error('[Daemon] Failed to clean up PID file:', error);
582
+ }
583
+ console.log('[Daemon] Exiting...');
584
+ process.exit(0);
585
+ }
586
+ }
587
+ /**
588
+ * Main entry point
589
+ */
590
+ async function main() {
591
+ // Only run if explicitly in daemon mode
592
+ if (!process.env.EPISODA_DAEMON_MODE) {
593
+ console.error('This script should only be run by daemon-manager');
594
+ process.exit(1);
595
+ }
596
+ const daemon = new Daemon();
597
+ await daemon.start();
598
+ // Keep process alive
599
+ await new Promise(() => {
600
+ // Never resolves - daemon runs until killed
601
+ });
602
+ }
603
+ // Run daemon
604
+ main().catch(error => {
605
+ console.error('[Daemon] Fatal error:', error);
606
+ process.exit(1);
607
+ });
608
+ //# sourceMappingURL=daemon-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-process.js","sourceRoot":"","sources":["../../src/daemon/daemon-process.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,6CAA2C;AAC3C,uDAA6H;AAC7H,qDAAiD;AACjD,kDAA6C;AAC7C,kCAAyI;AACzI,4DAAkF;AAClF,uCAAwB;AACxB,uCAAwB;AACxB,2CAA4B;AAE5B,iDAAiD;AACjD,MAAM,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;AAejD;;GAEG;AACH,MAAM,MAAM;IAaV;QAZQ,cAAS,GAAW,EAAE,CAAA;QACtB,aAAQ,GAAkB,IAAI,CAAA,CAAC,wCAAwC;QACvE,eAAU,GAAkB,IAAI,CAAA,CAAC,wCAAwC;QACzE,iBAAY,GAAkB,IAAI,CAAA,CAAC,sDAAsD;QAEjG,2FAA2F;QACnF,gBAAW,GAAG,IAAI,GAAG,EAA4B,CAAA,CAAC,4BAA4B;QACtF,qEAAqE;QACrE,qEAAqE;QAC7D,oBAAe,GAAG,IAAI,GAAG,EAAU,CAAA,CAAC,cAAc;QAClD,iBAAY,GAAG,KAAK,CAAA;QAG1B,IAAI,CAAC,SAAS,GAAG,IAAI,sBAAS,EAAE,CAAA;IAClC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAA;QAElD,iBAAiB;QACjB,IAAI,CAAC,SAAS,GAAG,MAAM,IAAA,yBAAY,GAAE,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QAErD,wDAAwD;QACxD,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAU,GAAE,CAAA;QACjC,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAA;YAChC,OAAO,CAAC,GAAG,CAAC,4CAA4C,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC1E,CAAC;QAED,mBAAmB;QACnB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;QAC5B,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAA;QAE1C,4FAA4F;QAE5F,gCAAgC;QAChC,IAAI,CAAC,mBAAmB,EAAE,CAAA;QAE1B,2CAA2C;QAC3C,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAA;QAE/B,0BAA0B;QAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAE5B,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAA;QAEnD,wDAAwD;QACxD,IAAI,CAAC,qBAAqB,EAAE,CAAA;IAC9B,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAA,gCAAe,EAAC,WAAW,CAAC,OAAO,CAAC,CAAA;YAEzD,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,2BAA2B,MAAM,CAAC,cAAc,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;gBACzF,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAA;gBAC7C,IAAA,wCAAuB,GAAE,CAAA;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,iDAAiD;QACnD,CAAC;IACH,CAAC;IAED,gGAAgG;IAEhG;;OAEG;IACK,mBAAmB;QACzB,sBAAsB;QACtB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;YACnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QACzB,CAAC,CAAC,CAAA;QAEF,6BAA6B;QAC7B,wEAAwE;QACxE,iFAAiF;QACjF,iFAAiF;QACjF,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,QAAQ,GAAG,IAAA,gCAAc,GAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC1C,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;aAC5C,CAAC,CAAC,CAAA;YAEH,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,gDAAgD;gBACzE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;gBACvB,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;gBACvB,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;gBACf,QAAQ;aACT,CAAA;QACH,CAAC,CAAC,CAAA;QAEF,sEAAsE;QACtE,4DAA4D;QAC5D,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,MAAkD,EAAE,EAAE;YAC5F,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;YACzC,IAAA,4BAAY,EAAC,SAAS,EAAE,WAAW,CAAC,CAAA;YAEpC,IAAI,CAAC;gBACH,qDAAqD;gBACrD,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;gBACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;YAC3C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC3E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAA;YAClE,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,wFAAwF;QAExF,iBAAiB;QACjB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,MAA+B,EAAE,EAAE;YAC5E,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;YAC9B,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;YACzC,IAAA,+BAAc,EAAC,WAAW,CAAC,CAAA;YAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,kBAAkB;QAClB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,iBAAiB,EAAE,KAAK,EAAE,MAA+B,EAAE,EAAE;YAC7E,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;YAC9B,MAAM,OAAO,GAAG,IAAA,gCAAc,GAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;YAClE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;YACxC,CAAC;YACD,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;YAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,qBAAqB;QACrB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,MAA+B,EAAE,EAAE;YAChF,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;YAC9B,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;YACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,WAAW;QACX,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACvC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAA;YAClD,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAA;YACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,kBAAkB;QAC9B,MAAM,QAAQ,GAAG,IAAA,gCAAc,GAAE,CAAA;QAEjC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;YACrD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,6CAA6C,OAAO,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAA;YACpF,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,WAAmB;QACjE,4BAA4B;QAC5B,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,iCAAiC,WAAW,EAAE,CAAC,CAAA;YAC3D,OAAM;QACR,CAAC;QAED,8BAA8B;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAU,GAAE,CAAA;QACjC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;QACpE,CAAC;QAED,2FAA2F;QAC3F,oDAAoD;QAEpD,0EAA0E;QAC1E,wFAAwF;QACxF,IAAI,SAAS,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,qBAAqB,CAAA;QAEtF,2CAA2C;QAC3C,IAAI,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;YAC9C,SAAS,GAAG,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAA;YACpD,OAAO,CAAC,GAAG,CAAC,qCAAqC,SAAS,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,gFAAgF;QAChF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;QACvC,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;QACtE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,CAAA;QACpD,MAAM,KAAK,GAAG,GAAG,UAAU,KAAK,YAAY,CAAC,QAAQ,IAAI,MAAM,EAAE,CAAA;QACjE,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,gBAAgB,SAAS,KAAK,CAAC,CAAA;QAE1E,+DAA+D;QAC/D,MAAM,MAAM,GAAG,IAAI,oBAAa,EAAE,CAAA;QAElC,sCAAsC;QACtC,MAAM,WAAW,GAAG,IAAI,kBAAW,EAAE,CAAA;QAErC,mBAAmB;QACnB,MAAM,UAAU,GAAqB;YACnC,SAAS;YACT,WAAW;YACX,MAAM;YACN,WAAW;SACZ,CAAA;QACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAE7C,2BAA2B;QAC3B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACrC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,iCAAiC,SAAS,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;gBAE3E,2DAA2D;gBAC3D,MAAM,CAAC,cAAc,EAAE,CAAA;gBAEvB,IAAI,CAAC;oBACH,6CAA6C;oBAC7C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAqB,EAAE;wBACtE,GAAG,EAAE,WAAW;qBACjB,CAAC,CAAA;oBAEF,mBAAmB;oBACnB,MAAM,MAAM,CAAC,IAAI,CAAC;wBAChB,IAAI,EAAE,QAAQ;wBACd,SAAS,EAAE,OAAO,CAAC,EAAG;wBACtB,MAAM;qBACP,CAAC,CAAA;oBAEF,OAAO,CAAC,GAAG,CAAC,kCAAkC,SAAS,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;gBACpG,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,oBAAoB;oBACpB,MAAM,MAAM,CAAC,IAAI,CAAC;wBAChB,IAAI,EAAE,QAAQ;wBACd,SAAS,EAAE,OAAO,CAAC,EAAG;wBACtB,MAAM,EAAE;4BACN,OAAO,EAAE,KAAK;4BACd,KAAK,EAAE,eAAe;4BACtB,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAC/D;qBACF,CAAC,CAAA;oBAEF,OAAO,CAAC,KAAK,CAAC,wCAAwC,SAAS,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC5E,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,yEAAyE;QACzE,8EAA8E;QAC9E,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACtC,MAAM,eAAe,GAAG,OAAgD,CAAA;YACxE,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,IAAI,SAAS,CAAA;YAElD,OAAO,CAAC,GAAG,CAAC,sDAAsD,SAAS,EAAE,CAAC,CAAA;YAC9E,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,MAAM,eAAe,CAAC,OAAO,IAAI,YAAY,EAAE,CAAC,CAAA;YAEtF,IAAI,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBAChC,sDAAsD;gBACtD,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAA;gBACnE,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;YAC7B,CAAC;iBAAM,CAAC;gBACN,kFAAkF;gBAClF,OAAO,CAAC,GAAG,CAAC,6BAA6B,MAAM,oCAAoC,CAAC,CAAA;gBACpF,wEAAwE;gBACxE,8EAA8E;YAChF,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,gCAAgC;QAChC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC1C,OAAO,CAAC,GAAG,CAAC,sCAAsC,SAAS,EAAE,CAAC,CAAA;YAC9D,IAAA,8BAAY,EAAC,WAAW,CAAC,CAAA;YAEzB,6DAA6D;YAC7D,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;YAErC,0EAA0E;YAC1E,uDAAuD;YACvD,qDAAqD;YACrD,iEAAiE;YACjE,uDAAuD;YACvD,MAAM,WAAW,GAAG,OAAmH,CAAA;YACvI,IAAI,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;gBAClD,wEAAwE;gBACxE,MAAM,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;gBACtI,sDAAsD;gBACtD,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAA;YACzC,CAAC;YAED,kDAAkD;YAClD,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;gBAC3B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,UAAU,CAAA;gBACxC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;YACzD,CAAC;YAED,6DAA6D;YAC7D,mCAAmC;YACnC,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBACzB,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAA;gBACpC,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;gBAE1D,sEAAsE;gBACtE,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;YAChD,CAAC;YAED,4DAA4D;YAC5D,4DAA4D;YAC5D,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;gBAC7B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,YAAY,CAAA;gBAC5C,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,YAAY,EAAE,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,yBAAyB;QACzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE;YAC7B,OAAO,CAAC,KAAK,CAAC,6BAA6B,SAAS,GAAG,EAAE,OAAO,CAAC,CAAA;QACnE,CAAC,CAAC,CAAA;QAEF,iEAAiE;QACjE,kFAAkF;QAClF,kGAAkG;QAClG,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;YAClC,MAAM,eAAe,GAAG,KAAwB,CAAA;YAChD,OAAO,CAAC,GAAG,CAAC,kCAAkC,SAAS,UAAU,eAAe,CAAC,IAAI,mBAAmB,eAAe,CAAC,aAAa,EAAE,CAAC,CAAA;YAExI,4EAA4E;YAC5E,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;YAExC,kFAAkF;YAClF,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC;gBACnC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;gBACpC,OAAO,CAAC,GAAG,CAAC,mCAAmC,WAAW,WAAW,CAAC,CAAA;YACxE,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC;YACH,mCAAmC;YACnC,IAAI,SAA6B,CAAA;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAA,+BAAc,GAAE,CAAA;gBAChC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;oBACvD,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;gBAClC,CAAC;YACH,CAAC;YAAC,OAAO,QAAQ,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;YAC9G,CAAC;YAED,2FAA2F;YAC3F,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE;gBAC/D,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;gBACvB,UAAU,EAAE,EAAE,CAAC,QAAQ,EAAE;gBACzB,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE;gBACjB,SAAS;aACV,CAAC,CAAA;YACF,OAAO,CAAC,GAAG,CAAC,8CAA8C,SAAS,EAAE,CAAC,CAAA;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,SAAS,GAAG,EAAE,KAAK,CAAC,CAAA;YACnE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;YACpC,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACpD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAM;QACR,CAAC;QAED,wBAAwB;QACxB,IAAI,UAAU,CAAC,cAAc,EAAE,CAAC;YAC9B,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,CAAA;QACzC,CAAC;QAED,yDAAyD;QACzD,MAAM,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAA;QACpC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QACpC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,CAAC,uCAAuC;QAEhF,OAAO,CAAC,GAAG,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED;;;OAGG;IACK,qBAAqB;QAC3B,MAAM,eAAe,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;YAC/C,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,oBAAoB,CAAC,CAAA;YAC5D,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;QAC7B,CAAC,CAAA;QAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAA;QACvD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;IACvD,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,gBAAgB,CAAC,WAAmB,EAAE,MAAc,EAAE,WAAmB,EAAE,SAAiB,EAAE,SAAiB,EAAE,QAAwB;QACrJ,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,wDAAa,eAAe,GAAC,CAAA;YAElD,qDAAqD;YACrD,QAAQ,CAAC,6BAA6B,MAAM,EAAE,EAAE;gBAC9C,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd,CAAC,CAAA;YAEF,QAAQ,CAAC,kCAAkC,WAAW,EAAE,EAAE;gBACxD,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd,CAAC,CAAA;YAEF,kEAAkE;YAClE,QAAQ,CAAC,gCAAgC,SAAS,EAAE,EAAE;gBACpD,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd,CAAC,CAAA;YAEF,sDAAsD;YACtD,kEAAkE;YAClE,QAAQ,CAAC,gCAAgC,SAAS,EAAE,EAAE;gBACpD,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd,CAAC,CAAA;YAEF,+DAA+D;YAC/D,sEAAsE;YACtE,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,+BAA+B,QAAQ,EAAE,EAAE;oBAClD,GAAG,EAAE,WAAW;oBAChB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,MAAM;iBACd,CAAC,CAAA;YACJ,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,uDAAuD,MAAM,eAAe,SAAS,eAAe,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACzK,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,6CAA6C;YAC7C,OAAO,CAAC,IAAI,CAAC,6CAA6C,WAAW,GAAG,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QAC3H,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,eAAe,CAAC,WAAmB;QAC/C,MAAM,KAAK,GAAG,CAAC,eAAe,EAAE,YAAY,CAAC,CAAA;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAExD,gCAAgC;QAChC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,uCAAuC,QAAQ,EAAE,CAAC,CAAA;YAC/D,OAAM;QACR,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;gBAE9C,4BAA4B;gBAC5B,8EAA8E;gBAC9E,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;gBAErE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;oBACpC,OAAO,CAAC,IAAI,CAAC,oCAAoC,eAAe,EAAE,CAAC,CAAA;oBACnE,SAAQ;gBACV,CAAC;gBAED,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;gBAE7D,iDAAiD;gBACjD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC5B,MAAM,eAAe,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;oBAC1D,IAAI,eAAe,KAAK,WAAW,EAAE,CAAC;wBACpC,qBAAqB;wBACrB,SAAQ;oBACV,CAAC;gBACH,CAAC;gBAED,kBAAkB;gBAClB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;gBACxD,OAAO,CAAC,GAAG,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAA;YACzD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,+BAA+B;gBAC/B,OAAO,CAAC,IAAI,CAAC,8BAA8B,QAAQ,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YAC9G,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,aAAa,CAAC,QAAgB;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAU,GAAE,CAAA;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAA;gBACjE,OAAM;YACR,CAAC;YAED,uCAAuC;YACvC,IAAI,MAAM,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAClC,OAAM;YACR,CAAC;YAED,8CAA8C;YAC9C,MAAM,aAAa,GAAkB;gBACnC,GAAG,MAAM;gBACT,SAAS,EAAE,QAAQ;gBACnB,UAAU,EAAE,IAAI,CAAC,SAAS;aAC3B,CAAA;YAED,MAAM,IAAA,iBAAU,EAAC,aAAa,CAAC,CAAA;YAC/B,OAAO,CAAC,GAAG,CAAC,wCAAwC,QAAQ,EAAE,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACrG,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,QAAQ;QACpB,IAAI,IAAI,CAAC,YAAY;YAAE,OAAM;QAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAExB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;QAExC,kCAAkC;QAClC,KAAK,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACzD,IAAI,UAAU,CAAC,cAAc,EAAE,CAAC;gBAC9B,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,CAAA;YACzC,CAAC;YACD,MAAM,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAA;QACtC,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;QAExB,+DAA+D;QAE/D,kBAAkB;QAClB,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;QAE3B,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;IAC3C,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,cAAc;QAC1B,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAA;QAErB,oBAAoB;QACpB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAA,+BAAc,GAAE,CAAA;YAChC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3B,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;gBACtB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAA;YAC7C,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;QAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;CACF;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,wCAAwC;IACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;QACrC,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAA;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAA;IAC3B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IAEpB,qBAAqB;IACrB,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE;QACrB,4CAA4C;IAC9C,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,aAAa;AACb,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAA;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Machine ID generation and management
3
+ *
4
+ * Generates a stable machine identifier used for multi-machine connection tracking.
5
+ * Format: {hostname}-{hardware-uuid-prefix}
6
+ * Example: "Alans-MacBook-Pro-a3f2b1c4"
7
+ *
8
+ * EP731: Uses hardware UUID instead of random UUID to prevent duplicate device
9
+ * registrations when ~/.episoda syncs between devices via iCloud/Dropbox.
10
+ *
11
+ * Properties:
12
+ * - Stable across daemon restarts
13
+ * - Unique per PHYSICAL machine (uses hardware UUID)
14
+ * - Human-readable (includes hostname)
15
+ * - Survives OS reboots
16
+ * - Cannot sync between devices (hardware-based)
17
+ */
18
+ /**
19
+ * Get or generate machine ID
20
+ *
21
+ * Reads from config directory's machine-id if exists, otherwise generates new one
22
+ * and saves it for future use.
23
+ *
24
+ * @returns Machine ID string
25
+ */
26
+ export declare function getMachineId(): Promise<string>;
27
+ /**
28
+ * Reset machine ID (force regeneration)
29
+ *
30
+ * Deletes the stored machine ID file, forcing a new ID to be generated
31
+ * on next getMachineId() call.
32
+ *
33
+ * Use case: Developer explicitly wants to change their machine identity
34
+ */
35
+ export declare function resetMachineId(): void;
36
+ //# sourceMappingURL=machine-id.d.ts.map