episoda 0.2.14 → 0.2.15
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/dist/daemon/daemon-process.js +63 -13
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +58 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1718,6 +1718,18 @@ var require_websocket_client = __commonJS({
|
|
|
1718
1718
|
this.isIntentionalDisconnect = false;
|
|
1719
1719
|
this.lastConnectAttemptTime = Date.now();
|
|
1720
1720
|
this.lastErrorCode = void 0;
|
|
1721
|
+
if (this.ws) {
|
|
1722
|
+
try {
|
|
1723
|
+
this.ws.removeAllListeners();
|
|
1724
|
+
this.ws.terminate();
|
|
1725
|
+
} catch {
|
|
1726
|
+
}
|
|
1727
|
+
this.ws = void 0;
|
|
1728
|
+
}
|
|
1729
|
+
if (this.reconnectTimeout) {
|
|
1730
|
+
clearTimeout(this.reconnectTimeout);
|
|
1731
|
+
this.reconnectTimeout = void 0;
|
|
1732
|
+
}
|
|
1721
1733
|
return new Promise((resolve2, reject) => {
|
|
1722
1734
|
const connectionTimeout = setTimeout(() => {
|
|
1723
1735
|
if (this.ws) {
|
|
@@ -1946,6 +1958,10 @@ var require_websocket_client = __commonJS({
|
|
|
1946
1958
|
console.log("[EpisodaClient] Intentional disconnect - not reconnecting");
|
|
1947
1959
|
return;
|
|
1948
1960
|
}
|
|
1961
|
+
if (this.reconnectTimeout) {
|
|
1962
|
+
console.log("[EpisodaClient] Reconnection already scheduled, skipping duplicate");
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1949
1965
|
if (this.heartbeatTimer) {
|
|
1950
1966
|
clearInterval(this.heartbeatTimer);
|
|
1951
1967
|
this.heartbeatTimer = void 0;
|
|
@@ -2264,7 +2280,7 @@ var require_package = __commonJS({
|
|
|
2264
2280
|
"package.json"(exports2, module2) {
|
|
2265
2281
|
module2.exports = {
|
|
2266
2282
|
name: "episoda",
|
|
2267
|
-
version: "0.2.
|
|
2283
|
+
version: "0.2.14",
|
|
2268
2284
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2269
2285
|
main: "dist/index.js",
|
|
2270
2286
|
types: "dist/index.d.ts",
|
|
@@ -3156,6 +3172,10 @@ var Daemon = class {
|
|
|
3156
3172
|
// Updated by 'auth_success' (add) and 'disconnected' (remove) events
|
|
3157
3173
|
this.liveConnections = /* @__PURE__ */ new Set();
|
|
3158
3174
|
// projectPath
|
|
3175
|
+
// EP813: Track connections that are still authenticating (in progress)
|
|
3176
|
+
// Prevents race condition between restoreConnections() and add-project IPC
|
|
3177
|
+
this.pendingConnections = /* @__PURE__ */ new Set();
|
|
3178
|
+
// projectPath
|
|
3159
3179
|
this.shuttingDown = false;
|
|
3160
3180
|
this.ipcServer = new IPCServer();
|
|
3161
3181
|
}
|
|
@@ -3224,18 +3244,31 @@ var Daemon = class {
|
|
|
3224
3244
|
this.ipcServer.on("add-project", async (params) => {
|
|
3225
3245
|
const { projectId, projectPath } = params;
|
|
3226
3246
|
addProject(projectId, projectPath);
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3247
|
+
const MAX_RETRIES = 3;
|
|
3248
|
+
const INITIAL_DELAY = 1e3;
|
|
3249
|
+
let lastError = "";
|
|
3250
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
3251
|
+
try {
|
|
3252
|
+
await this.connectProject(projectId, projectPath);
|
|
3253
|
+
const isHealthy = this.isConnectionHealthy(projectPath);
|
|
3254
|
+
if (!isHealthy) {
|
|
3255
|
+
console.warn(`[Daemon] Connection completed but not healthy for ${projectPath}`);
|
|
3256
|
+
lastError = "Connection established but not healthy";
|
|
3257
|
+
return { success: false, connected: false, error: lastError };
|
|
3258
|
+
}
|
|
3259
|
+
return { success: true, connected: true };
|
|
3260
|
+
} catch (error) {
|
|
3261
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
3262
|
+
console.error(`[Daemon] Connection attempt ${attempt}/${MAX_RETRIES} failed:`, lastError);
|
|
3263
|
+
if (attempt < MAX_RETRIES) {
|
|
3264
|
+
const delay = INITIAL_DELAY * Math.pow(2, attempt - 1);
|
|
3265
|
+
console.log(`[Daemon] Retrying in ${delay / 1e3}s...`);
|
|
3266
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
3267
|
+
await this.disconnectProject(projectPath);
|
|
3268
|
+
}
|
|
3233
3269
|
}
|
|
3234
|
-
return { success: true, connected: true };
|
|
3235
|
-
} catch (error) {
|
|
3236
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3237
|
-
return { success: false, connected: false, error: errorMessage };
|
|
3238
3270
|
}
|
|
3271
|
+
return { success: false, connected: false, error: `Failed after ${MAX_RETRIES} attempts: ${lastError}` };
|
|
3239
3272
|
});
|
|
3240
3273
|
this.ipcServer.on("remove-project", async (params) => {
|
|
3241
3274
|
const { projectPath } = params;
|
|
@@ -3310,6 +3343,19 @@ var Daemon = class {
|
|
|
3310
3343
|
console.log(`[Daemon] Already connected to ${projectPath}`);
|
|
3311
3344
|
return;
|
|
3312
3345
|
}
|
|
3346
|
+
if (this.pendingConnections.has(projectPath)) {
|
|
3347
|
+
console.log(`[Daemon] Connection in progress for ${projectPath}, waiting...`);
|
|
3348
|
+
const maxWait = 35e3;
|
|
3349
|
+
const startTime = Date.now();
|
|
3350
|
+
while (this.pendingConnections.has(projectPath) && Date.now() - startTime < maxWait) {
|
|
3351
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
3352
|
+
}
|
|
3353
|
+
if (this.liveConnections.has(projectPath)) {
|
|
3354
|
+
console.log(`[Daemon] Pending connection succeeded for ${projectPath}`);
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
console.warn(`[Daemon] Pending connection timed out for ${projectPath}`);
|
|
3358
|
+
}
|
|
3313
3359
|
console.warn(`[Daemon] Stale connection detected for ${projectPath}, forcing reconnection`);
|
|
3314
3360
|
await this.disconnectProject(projectPath);
|
|
3315
3361
|
}
|
|
@@ -3336,6 +3382,7 @@ var Daemon = class {
|
|
|
3336
3382
|
gitExecutor
|
|
3337
3383
|
};
|
|
3338
3384
|
this.connections.set(projectPath, connection);
|
|
3385
|
+
this.pendingConnections.add(projectPath);
|
|
3339
3386
|
client.on("command", async (message) => {
|
|
3340
3387
|
if (message.type === "command" && message.command) {
|
|
3341
3388
|
console.log(`[Daemon] Received command for ${projectId}:`, message.command);
|
|
@@ -3431,6 +3478,7 @@ var Daemon = class {
|
|
|
3431
3478
|
console.log(`[Daemon] Authenticated for project ${projectId}`);
|
|
3432
3479
|
touchProject(projectPath);
|
|
3433
3480
|
this.liveConnections.add(projectPath);
|
|
3481
|
+
this.pendingConnections.delete(projectPath);
|
|
3434
3482
|
const authMessage = message;
|
|
3435
3483
|
if (authMessage.userId && authMessage.workspaceId) {
|
|
3436
3484
|
await this.configureGitUser(projectPath, authMessage.userId, authMessage.workspaceId, this.machineId, projectId, authMessage.deviceId);
|
|
@@ -3474,9 +3522,9 @@ var Daemon = class {
|
|
|
3474
3522
|
console.warn(`[Daemon] Could not read daemon PID:`, pidError instanceof Error ? pidError.message : pidError);
|
|
3475
3523
|
}
|
|
3476
3524
|
const authSuccessPromise = new Promise((resolve2, reject) => {
|
|
3477
|
-
const AUTH_TIMEOUT =
|
|
3525
|
+
const AUTH_TIMEOUT = 3e4;
|
|
3478
3526
|
const timeout = setTimeout(() => {
|
|
3479
|
-
reject(new Error("Authentication timeout - server
|
|
3527
|
+
reject(new Error("Authentication timeout after 30s - server may be under heavy load. Try again in a few seconds."));
|
|
3480
3528
|
}, AUTH_TIMEOUT);
|
|
3481
3529
|
const authHandler = () => {
|
|
3482
3530
|
clearTimeout(timeout);
|
|
@@ -3502,6 +3550,7 @@ var Daemon = class {
|
|
|
3502
3550
|
} catch (error) {
|
|
3503
3551
|
console.error(`[Daemon] Failed to connect to ${projectId}:`, error);
|
|
3504
3552
|
this.connections.delete(projectPath);
|
|
3553
|
+
this.pendingConnections.delete(projectPath);
|
|
3505
3554
|
throw error;
|
|
3506
3555
|
}
|
|
3507
3556
|
}
|
|
@@ -3519,6 +3568,7 @@ var Daemon = class {
|
|
|
3519
3568
|
await connection.client.disconnect();
|
|
3520
3569
|
this.connections.delete(projectPath);
|
|
3521
3570
|
this.liveConnections.delete(projectPath);
|
|
3571
|
+
this.pendingConnections.delete(projectPath);
|
|
3522
3572
|
console.log(`[Daemon] Disconnected from ${projectPath}`);
|
|
3523
3573
|
}
|
|
3524
3574
|
/**
|