@episoda/cli 0.2.187 → 0.2.190
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 +318 -198
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1640,15 +1640,15 @@ var require_git_executor = __commonJS({
|
|
|
1640
1640
|
try {
|
|
1641
1641
|
const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1642
1642
|
const gitDirPath = gitDir.trim();
|
|
1643
|
-
const
|
|
1643
|
+
const fs36 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1644
1644
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1645
1645
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1646
1646
|
try {
|
|
1647
|
-
await
|
|
1647
|
+
await fs36.access(rebaseMergePath);
|
|
1648
1648
|
inRebase = true;
|
|
1649
1649
|
} catch {
|
|
1650
1650
|
try {
|
|
1651
|
-
await
|
|
1651
|
+
await fs36.access(rebaseApplyPath);
|
|
1652
1652
|
inRebase = true;
|
|
1653
1653
|
} catch {
|
|
1654
1654
|
inRebase = false;
|
|
@@ -1704,9 +1704,9 @@ var require_git_executor = __commonJS({
|
|
|
1704
1704
|
};
|
|
1705
1705
|
}
|
|
1706
1706
|
}
|
|
1707
|
-
const
|
|
1707
|
+
const fs36 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1708
1708
|
try {
|
|
1709
|
-
await
|
|
1709
|
+
await fs36.access(command.path);
|
|
1710
1710
|
return {
|
|
1711
1711
|
success: false,
|
|
1712
1712
|
error: "WORKTREE_EXISTS",
|
|
@@ -1765,9 +1765,9 @@ var require_git_executor = __commonJS({
|
|
|
1765
1765
|
*/
|
|
1766
1766
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1767
1767
|
try {
|
|
1768
|
-
const
|
|
1768
|
+
const fs36 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1769
1769
|
try {
|
|
1770
|
-
await
|
|
1770
|
+
await fs36.access(command.path);
|
|
1771
1771
|
} catch {
|
|
1772
1772
|
return {
|
|
1773
1773
|
success: false,
|
|
@@ -1802,7 +1802,7 @@ var require_git_executor = __commonJS({
|
|
|
1802
1802
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1803
1803
|
if (result.success) {
|
|
1804
1804
|
try {
|
|
1805
|
-
await
|
|
1805
|
+
await fs36.rm(command.path, { recursive: true, force: true });
|
|
1806
1806
|
} catch {
|
|
1807
1807
|
}
|
|
1808
1808
|
return {
|
|
@@ -1936,10 +1936,10 @@ var require_git_executor = __commonJS({
|
|
|
1936
1936
|
*/
|
|
1937
1937
|
async executeCloneBare(command, options) {
|
|
1938
1938
|
try {
|
|
1939
|
-
const
|
|
1940
|
-
const
|
|
1939
|
+
const fs36 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1940
|
+
const path38 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1941
1941
|
try {
|
|
1942
|
-
await
|
|
1942
|
+
await fs36.access(command.path);
|
|
1943
1943
|
return {
|
|
1944
1944
|
success: false,
|
|
1945
1945
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1948,9 +1948,9 @@ var require_git_executor = __commonJS({
|
|
|
1948
1948
|
};
|
|
1949
1949
|
} catch {
|
|
1950
1950
|
}
|
|
1951
|
-
const parentDir =
|
|
1951
|
+
const parentDir = path38.dirname(command.path);
|
|
1952
1952
|
try {
|
|
1953
|
-
await
|
|
1953
|
+
await fs36.mkdir(parentDir, { recursive: true });
|
|
1954
1954
|
} catch {
|
|
1955
1955
|
}
|
|
1956
1956
|
const { stdout, stderr } = await execAsync3(
|
|
@@ -1998,22 +1998,22 @@ var require_git_executor = __commonJS({
|
|
|
1998
1998
|
*/
|
|
1999
1999
|
async executeProjectInfo(cwd, options) {
|
|
2000
2000
|
try {
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2001
|
+
const fs36 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
2002
|
+
const path38 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
2003
2003
|
let currentPath = cwd;
|
|
2004
2004
|
let projectPath = cwd;
|
|
2005
2005
|
let bareRepoPath;
|
|
2006
2006
|
for (let i = 0; i < 10; i++) {
|
|
2007
|
-
const bareDir =
|
|
2008
|
-
const episodaDir =
|
|
2007
|
+
const bareDir = path38.join(currentPath, ".bare");
|
|
2008
|
+
const episodaDir = path38.join(currentPath, ".episoda");
|
|
2009
2009
|
try {
|
|
2010
|
-
await
|
|
2011
|
-
await
|
|
2010
|
+
await fs36.access(bareDir);
|
|
2011
|
+
await fs36.access(episodaDir);
|
|
2012
2012
|
projectPath = currentPath;
|
|
2013
2013
|
bareRepoPath = bareDir;
|
|
2014
2014
|
break;
|
|
2015
2015
|
} catch {
|
|
2016
|
-
const parentPath =
|
|
2016
|
+
const parentPath = path38.dirname(currentPath);
|
|
2017
2017
|
if (parentPath === currentPath) {
|
|
2018
2018
|
break;
|
|
2019
2019
|
}
|
|
@@ -2236,7 +2236,7 @@ var require_websocket_client = __commonJS({
|
|
|
2236
2236
|
clearTimeout(this.reconnectTimeout);
|
|
2237
2237
|
this.reconnectTimeout = void 0;
|
|
2238
2238
|
}
|
|
2239
|
-
return new Promise((
|
|
2239
|
+
return new Promise((resolve8, reject) => {
|
|
2240
2240
|
const connectionTimeout = setTimeout(() => {
|
|
2241
2241
|
if (this.ws) {
|
|
2242
2242
|
this.ws.terminate();
|
|
@@ -2267,7 +2267,7 @@ var require_websocket_client = __commonJS({
|
|
|
2267
2267
|
daemonPid: this.daemonPid
|
|
2268
2268
|
});
|
|
2269
2269
|
this.startHeartbeat();
|
|
2270
|
-
|
|
2270
|
+
resolve8();
|
|
2271
2271
|
});
|
|
2272
2272
|
this.ws.on("pong", () => {
|
|
2273
2273
|
if (this.heartbeatTimeoutTimer) {
|
|
@@ -2411,13 +2411,13 @@ var require_websocket_client = __commonJS({
|
|
|
2411
2411
|
console.warn("[EpisodaClient] Cannot send - WebSocket not connected");
|
|
2412
2412
|
return false;
|
|
2413
2413
|
}
|
|
2414
|
-
return new Promise((
|
|
2414
|
+
return new Promise((resolve8) => {
|
|
2415
2415
|
this.ws.send(JSON.stringify(message), (error) => {
|
|
2416
2416
|
if (error) {
|
|
2417
2417
|
console.error("[EpisodaClient] Failed to send message:", error);
|
|
2418
|
-
|
|
2418
|
+
resolve8(false);
|
|
2419
2419
|
} else {
|
|
2420
|
-
|
|
2420
|
+
resolve8(true);
|
|
2421
2421
|
}
|
|
2422
2422
|
});
|
|
2423
2423
|
});
|
|
@@ -2724,33 +2724,33 @@ var require_auth = __commonJS({
|
|
|
2724
2724
|
exports2.loadConfig = loadConfig16;
|
|
2725
2725
|
exports2.saveConfig = saveConfig4;
|
|
2726
2726
|
exports2.validateToken = validateToken;
|
|
2727
|
-
var
|
|
2728
|
-
var
|
|
2729
|
-
var
|
|
2727
|
+
var fs36 = __importStar(require("fs"));
|
|
2728
|
+
var path38 = __importStar(require("path"));
|
|
2729
|
+
var os17 = __importStar(require("os"));
|
|
2730
2730
|
var child_process_1 = require("child_process");
|
|
2731
2731
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2732
2732
|
var hasWarnedMissingProjectId = false;
|
|
2733
2733
|
var hasWarnedMissingRequiredFields = false;
|
|
2734
2734
|
function getConfigDir10() {
|
|
2735
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2735
|
+
return process.env.EPISODA_CONFIG_DIR || path38.join(os17.homedir(), ".episoda");
|
|
2736
2736
|
}
|
|
2737
2737
|
function getConfigPath(configPath) {
|
|
2738
2738
|
if (configPath) {
|
|
2739
2739
|
return configPath;
|
|
2740
2740
|
}
|
|
2741
|
-
return
|
|
2741
|
+
return path38.join(getConfigDir10(), DEFAULT_CONFIG_FILE);
|
|
2742
2742
|
}
|
|
2743
2743
|
function ensureConfigDir(configPath) {
|
|
2744
|
-
const dir =
|
|
2745
|
-
const isNew = !
|
|
2744
|
+
const dir = path38.dirname(configPath);
|
|
2745
|
+
const isNew = !fs36.existsSync(dir);
|
|
2746
2746
|
if (isNew) {
|
|
2747
|
-
|
|
2747
|
+
fs36.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2748
2748
|
}
|
|
2749
2749
|
if (process.platform === "darwin") {
|
|
2750
|
-
const nosyncPath =
|
|
2751
|
-
if (isNew || !
|
|
2750
|
+
const nosyncPath = path38.join(dir, ".nosync");
|
|
2751
|
+
if (isNew || !fs36.existsSync(nosyncPath)) {
|
|
2752
2752
|
try {
|
|
2753
|
-
|
|
2753
|
+
fs36.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2754
2754
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2755
2755
|
stdio: "ignore",
|
|
2756
2756
|
timeout: 5e3
|
|
@@ -2764,11 +2764,11 @@ var require_auth = __commonJS({
|
|
|
2764
2764
|
const fullPath = getConfigPath(configPath);
|
|
2765
2765
|
const isCloudMode = process.env.EPISODA_MODE === "cloud";
|
|
2766
2766
|
const readConfigFile = (pathToFile) => {
|
|
2767
|
-
if (!
|
|
2767
|
+
if (!fs36.existsSync(pathToFile)) {
|
|
2768
2768
|
return null;
|
|
2769
2769
|
}
|
|
2770
2770
|
try {
|
|
2771
|
-
const content =
|
|
2771
|
+
const content = fs36.readFileSync(pathToFile, "utf8");
|
|
2772
2772
|
return JSON.parse(content);
|
|
2773
2773
|
} catch (error) {
|
|
2774
2774
|
console.error("Error loading config:", error);
|
|
@@ -2807,11 +2807,11 @@ var require_auth = __commonJS({
|
|
|
2807
2807
|
}
|
|
2808
2808
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2809
2809
|
const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
|
|
2810
|
-
if (!
|
|
2810
|
+
if (!fs36.existsSync(workspaceConfigPath)) {
|
|
2811
2811
|
return null;
|
|
2812
2812
|
}
|
|
2813
2813
|
try {
|
|
2814
|
-
const content =
|
|
2814
|
+
const content = fs36.readFileSync(workspaceConfigPath, "utf8");
|
|
2815
2815
|
const workspaceConfig2 = JSON.parse(content);
|
|
2816
2816
|
const expiresAtEnv = envValue(process.env.EPISODA_ACCESS_TOKEN_EXPIRES_AT);
|
|
2817
2817
|
return {
|
|
@@ -2839,11 +2839,11 @@ var require_auth = __commonJS({
|
|
|
2839
2839
|
}
|
|
2840
2840
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2841
2841
|
const projectConfigPath = require("path").join(homeDir, "episoda", workspaceSlug, projectSlug, ".episoda", "config.json");
|
|
2842
|
-
if (!
|
|
2842
|
+
if (!fs36.existsSync(projectConfigPath)) {
|
|
2843
2843
|
return null;
|
|
2844
2844
|
}
|
|
2845
2845
|
try {
|
|
2846
|
-
const content =
|
|
2846
|
+
const content = fs36.readFileSync(projectConfigPath, "utf8");
|
|
2847
2847
|
const projectConfig2 = JSON.parse(content);
|
|
2848
2848
|
return {
|
|
2849
2849
|
project_id: projectConfig2.projectId || projectConfig2.project_id,
|
|
@@ -2911,7 +2911,7 @@ var require_auth = __commonJS({
|
|
|
2911
2911
|
ensureConfigDir(fullPath);
|
|
2912
2912
|
try {
|
|
2913
2913
|
const content = JSON.stringify(config, null, 2);
|
|
2914
|
-
|
|
2914
|
+
fs36.writeFileSync(fullPath, content, { mode: 384 });
|
|
2915
2915
|
} catch (error) {
|
|
2916
2916
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2917
2917
|
}
|
|
@@ -3046,7 +3046,7 @@ var require_package = __commonJS({
|
|
|
3046
3046
|
"package.json"(exports2, module2) {
|
|
3047
3047
|
module2.exports = {
|
|
3048
3048
|
name: "@episoda/cli",
|
|
3049
|
-
version: "0.2.
|
|
3049
|
+
version: "0.2.190",
|
|
3050
3050
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
3051
3051
|
main: "dist/index.js",
|
|
3052
3052
|
types: "dist/index.d.ts",
|
|
@@ -3362,10 +3362,10 @@ var IPCServer = class {
|
|
|
3362
3362
|
this.server = net.createServer((socket) => {
|
|
3363
3363
|
this.handleConnection(socket);
|
|
3364
3364
|
});
|
|
3365
|
-
return new Promise((
|
|
3365
|
+
return new Promise((resolve8, reject) => {
|
|
3366
3366
|
this.server.listen(socketPath, () => {
|
|
3367
3367
|
fs3.chmodSync(socketPath, 384);
|
|
3368
|
-
|
|
3368
|
+
resolve8();
|
|
3369
3369
|
});
|
|
3370
3370
|
this.server.on("error", reject);
|
|
3371
3371
|
});
|
|
@@ -3376,12 +3376,12 @@ var IPCServer = class {
|
|
|
3376
3376
|
async stop() {
|
|
3377
3377
|
if (!this.server) return;
|
|
3378
3378
|
const socketPath = getSocketPath();
|
|
3379
|
-
return new Promise((
|
|
3379
|
+
return new Promise((resolve8) => {
|
|
3380
3380
|
this.server.close(() => {
|
|
3381
3381
|
if (fs3.existsSync(socketPath)) {
|
|
3382
3382
|
fs3.unlinkSync(socketPath);
|
|
3383
3383
|
}
|
|
3384
|
-
|
|
3384
|
+
resolve8();
|
|
3385
3385
|
});
|
|
3386
3386
|
});
|
|
3387
3387
|
}
|
|
@@ -3626,7 +3626,7 @@ function getDownloadUrl() {
|
|
|
3626
3626
|
return platformUrls[arch4] || null;
|
|
3627
3627
|
}
|
|
3628
3628
|
async function downloadFile(url, destPath) {
|
|
3629
|
-
return new Promise((
|
|
3629
|
+
return new Promise((resolve8, reject) => {
|
|
3630
3630
|
const followRedirect = (currentUrl, redirectCount = 0) => {
|
|
3631
3631
|
if (redirectCount > 5) {
|
|
3632
3632
|
reject(new Error("Too many redirects"));
|
|
@@ -3656,7 +3656,7 @@ async function downloadFile(url, destPath) {
|
|
|
3656
3656
|
response.pipe(file);
|
|
3657
3657
|
file.on("finish", () => {
|
|
3658
3658
|
file.close();
|
|
3659
|
-
|
|
3659
|
+
resolve8();
|
|
3660
3660
|
});
|
|
3661
3661
|
file.on("error", (err) => {
|
|
3662
3662
|
fs4.unlinkSync(destPath);
|
|
@@ -4070,10 +4070,10 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4070
4070
|
const isTracked = Array.from(this.tunnelStates.values()).some((s) => s.info.pid === pid);
|
|
4071
4071
|
console.log(`[Tunnel] EP904: Found cloudflared PID ${pid} on port ${port} (tracked: ${isTracked})`);
|
|
4072
4072
|
this.killByPid(pid, "SIGTERM");
|
|
4073
|
-
await new Promise((
|
|
4073
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
4074
4074
|
if (this.isProcessRunning(pid)) {
|
|
4075
4075
|
this.killByPid(pid, "SIGKILL");
|
|
4076
|
-
await new Promise((
|
|
4076
|
+
await new Promise((resolve8) => setTimeout(resolve8, 200));
|
|
4077
4077
|
}
|
|
4078
4078
|
killed.push(pid);
|
|
4079
4079
|
}
|
|
@@ -4107,7 +4107,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4107
4107
|
if (!this.tunnelStates.has(moduleUid)) {
|
|
4108
4108
|
console.log(`[Tunnel] EP877: Found orphaned process PID ${pid} for ${moduleUid}, killing...`);
|
|
4109
4109
|
this.killByPid(pid, "SIGTERM");
|
|
4110
|
-
await new Promise((
|
|
4110
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
4111
4111
|
if (this.isProcessRunning(pid)) {
|
|
4112
4112
|
this.killByPid(pid, "SIGKILL");
|
|
4113
4113
|
}
|
|
@@ -4122,7 +4122,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4122
4122
|
if (!trackedPids.includes(pid) && !cleaned.includes(pid)) {
|
|
4123
4123
|
console.log(`[Tunnel] EP877: Found untracked cloudflared process PID ${pid}, killing...`);
|
|
4124
4124
|
this.killByPid(pid, "SIGTERM");
|
|
4125
|
-
await new Promise((
|
|
4125
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
4126
4126
|
if (this.isProcessRunning(pid)) {
|
|
4127
4127
|
this.killByPid(pid, "SIGKILL");
|
|
4128
4128
|
}
|
|
@@ -4212,7 +4212,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4212
4212
|
return { success: false, error: `Failed to get cloudflared: ${errorMessage}` };
|
|
4213
4213
|
}
|
|
4214
4214
|
}
|
|
4215
|
-
return new Promise((
|
|
4215
|
+
return new Promise((resolve8) => {
|
|
4216
4216
|
const tunnelInfo = {
|
|
4217
4217
|
moduleUid,
|
|
4218
4218
|
url: previewUrl || "",
|
|
@@ -4278,7 +4278,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4278
4278
|
moduleUid,
|
|
4279
4279
|
url: tunnelInfo.url
|
|
4280
4280
|
});
|
|
4281
|
-
|
|
4281
|
+
resolve8({ success: true, url: tunnelInfo.url });
|
|
4282
4282
|
}
|
|
4283
4283
|
};
|
|
4284
4284
|
process2.stderr?.on("data", (data) => {
|
|
@@ -4305,7 +4305,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4305
4305
|
onStatusChange?.("error", errorMsg);
|
|
4306
4306
|
this.emitEvent({ type: "error", moduleUid, error: errorMsg });
|
|
4307
4307
|
}
|
|
4308
|
-
|
|
4308
|
+
resolve8({ success: false, error: errorMsg });
|
|
4309
4309
|
} else if (wasConnected) {
|
|
4310
4310
|
if (currentState && !currentState.intentionallyStopped) {
|
|
4311
4311
|
console.log(`[Tunnel] EP948: Named tunnel ${moduleUid} crashed unexpectedly, attempting reconnect...`);
|
|
@@ -4336,7 +4336,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4336
4336
|
this.emitEvent({ type: "error", moduleUid, error: error.message });
|
|
4337
4337
|
}
|
|
4338
4338
|
if (!connected) {
|
|
4339
|
-
|
|
4339
|
+
resolve8({ success: false, error: error.message });
|
|
4340
4340
|
}
|
|
4341
4341
|
});
|
|
4342
4342
|
setTimeout(() => {
|
|
@@ -4359,7 +4359,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4359
4359
|
onStatusChange?.("error", errorMsg);
|
|
4360
4360
|
this.emitEvent({ type: "error", moduleUid, error: errorMsg });
|
|
4361
4361
|
}
|
|
4362
|
-
|
|
4362
|
+
resolve8({ success: false, error: errorMsg });
|
|
4363
4363
|
}
|
|
4364
4364
|
}, TUNNEL_TIMEOUTS.NAMED_TUNNEL_CONNECT);
|
|
4365
4365
|
});
|
|
@@ -4420,7 +4420,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4420
4420
|
if (orphanPid && this.isProcessRunning(orphanPid)) {
|
|
4421
4421
|
console.log(`[Tunnel] EP877: Killing orphaned process ${orphanPid} for ${moduleUid} before starting new tunnel`);
|
|
4422
4422
|
this.killByPid(orphanPid, "SIGTERM");
|
|
4423
|
-
await new Promise((
|
|
4423
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
4424
4424
|
if (this.isProcessRunning(orphanPid)) {
|
|
4425
4425
|
this.killByPid(orphanPid, "SIGKILL");
|
|
4426
4426
|
}
|
|
@@ -4429,7 +4429,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4429
4429
|
const killedOnPort = await this.killCloudflaredOnPort(port);
|
|
4430
4430
|
if (killedOnPort.length > 0) {
|
|
4431
4431
|
console.log(`[Tunnel] EP904: Pre-start port cleanup killed ${killedOnPort.length} process(es) on port ${port}`);
|
|
4432
|
-
await new Promise((
|
|
4432
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
4433
4433
|
}
|
|
4434
4434
|
const cleanup = await this.cleanupOrphanedProcesses();
|
|
4435
4435
|
if (cleanup.cleaned > 0) {
|
|
@@ -4469,7 +4469,7 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4469
4469
|
if (orphanPid && this.isProcessRunning(orphanPid)) {
|
|
4470
4470
|
console.log(`[Tunnel] EP877: Stopping orphaned process ${orphanPid} for ${moduleUid} via PID file`);
|
|
4471
4471
|
this.killByPid(orphanPid, "SIGTERM");
|
|
4472
|
-
await new Promise((
|
|
4472
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
4473
4473
|
if (this.isProcessRunning(orphanPid)) {
|
|
4474
4474
|
this.killByPid(orphanPid, "SIGKILL");
|
|
4475
4475
|
}
|
|
@@ -4485,16 +4485,16 @@ var TunnelManager = class extends import_events.EventEmitter {
|
|
|
4485
4485
|
const tunnel = state.info;
|
|
4486
4486
|
if (tunnel.process && !tunnel.process.killed) {
|
|
4487
4487
|
tunnel.process.kill("SIGTERM");
|
|
4488
|
-
await new Promise((
|
|
4488
|
+
await new Promise((resolve8) => {
|
|
4489
4489
|
const timeout = setTimeout(() => {
|
|
4490
4490
|
if (tunnel.process && !tunnel.process.killed) {
|
|
4491
4491
|
tunnel.process.kill("SIGKILL");
|
|
4492
4492
|
}
|
|
4493
|
-
|
|
4493
|
+
resolve8();
|
|
4494
4494
|
}, 3e3);
|
|
4495
4495
|
tunnel.process.once("exit", () => {
|
|
4496
4496
|
clearTimeout(timeout);
|
|
4497
|
-
|
|
4497
|
+
resolve8();
|
|
4498
4498
|
});
|
|
4499
4499
|
});
|
|
4500
4500
|
}
|
|
@@ -4781,15 +4781,15 @@ async function writeToStdinWithBackpressure(process2, data, drainTimeoutMs, labe
|
|
|
4781
4781
|
if (!stdin || stdin.destroyed) {
|
|
4782
4782
|
throw new Error(`[${label}] stdin not available. session=${sessionId}`);
|
|
4783
4783
|
}
|
|
4784
|
-
await new Promise((
|
|
4784
|
+
await new Promise((resolve8, reject) => {
|
|
4785
4785
|
const ok = stdin.write(data);
|
|
4786
4786
|
if (ok) {
|
|
4787
|
-
|
|
4787
|
+
resolve8();
|
|
4788
4788
|
return;
|
|
4789
4789
|
}
|
|
4790
4790
|
const onDrain = () => {
|
|
4791
4791
|
cleanup();
|
|
4792
|
-
|
|
4792
|
+
resolve8();
|
|
4793
4793
|
};
|
|
4794
4794
|
const onError = (err) => {
|
|
4795
4795
|
cleanup();
|
|
@@ -4810,18 +4810,18 @@ async function writeToStdinWithBackpressure(process2, data, drainTimeoutMs, labe
|
|
|
4810
4810
|
});
|
|
4811
4811
|
}
|
|
4812
4812
|
function waitForProcessExit(process2, alive, timeoutMs) {
|
|
4813
|
-
return new Promise((
|
|
4813
|
+
return new Promise((resolve8) => {
|
|
4814
4814
|
if (!process2 || !alive) {
|
|
4815
|
-
|
|
4815
|
+
resolve8(true);
|
|
4816
4816
|
return;
|
|
4817
4817
|
}
|
|
4818
4818
|
const onExit = () => {
|
|
4819
4819
|
clearTimeout(timer);
|
|
4820
|
-
|
|
4820
|
+
resolve8(true);
|
|
4821
4821
|
};
|
|
4822
4822
|
const timer = setTimeout(() => {
|
|
4823
4823
|
process2.removeListener("exit", onExit);
|
|
4824
|
-
|
|
4824
|
+
resolve8(false);
|
|
4825
4825
|
}, timeoutMs);
|
|
4826
4826
|
process2.once("exit", onExit);
|
|
4827
4827
|
});
|
|
@@ -5817,10 +5817,10 @@ var CodexPersistentRuntime = class {
|
|
|
5817
5817
|
}
|
|
5818
5818
|
waitForThreadId(timeoutMs) {
|
|
5819
5819
|
if (this._agentSessionId) return Promise.resolve(this._agentSessionId);
|
|
5820
|
-
return new Promise((
|
|
5820
|
+
return new Promise((resolve8, reject) => {
|
|
5821
5821
|
const onId = (id) => {
|
|
5822
5822
|
clearTimeout(timer);
|
|
5823
|
-
|
|
5823
|
+
resolve8(id);
|
|
5824
5824
|
};
|
|
5825
5825
|
const timer = setTimeout(() => {
|
|
5826
5826
|
this.threadIdWaiters = this.threadIdWaiters.filter((w) => w !== onId);
|
|
@@ -5851,12 +5851,12 @@ var CodexPersistentRuntime = class {
|
|
|
5851
5851
|
sendRequest(method, params, timeoutMs = INIT_TIMEOUT_MS) {
|
|
5852
5852
|
const id = this.nextId++;
|
|
5853
5853
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
5854
|
-
return new Promise(async (
|
|
5854
|
+
return new Promise(async (resolve8, reject) => {
|
|
5855
5855
|
const timeout = setTimeout(() => {
|
|
5856
5856
|
this.pending.delete(id);
|
|
5857
5857
|
reject(new Error(`JSON-RPC timeout: ${method}`));
|
|
5858
5858
|
}, timeoutMs);
|
|
5859
|
-
this.pending.set(id, { resolve:
|
|
5859
|
+
this.pending.set(id, { resolve: resolve8, reject, timeout });
|
|
5860
5860
|
try {
|
|
5861
5861
|
await this.writeToStdin(JSON.stringify(msg) + "\n");
|
|
5862
5862
|
} catch (err) {
|
|
@@ -6006,11 +6006,11 @@ var UnifiedAgentRuntime = class {
|
|
|
6006
6006
|
if (!this.currentTurn && this.impl.turnState === "idle") {
|
|
6007
6007
|
return this.dispatchTurn(message, callbacks);
|
|
6008
6008
|
}
|
|
6009
|
-
return new Promise((
|
|
6009
|
+
return new Promise((resolve8, reject) => {
|
|
6010
6010
|
this.queuedTurns.push({
|
|
6011
6011
|
message,
|
|
6012
6012
|
callbacks,
|
|
6013
|
-
resolve:
|
|
6013
|
+
resolve: resolve8,
|
|
6014
6014
|
reject
|
|
6015
6015
|
});
|
|
6016
6016
|
});
|
|
@@ -7523,13 +7523,13 @@ async function getChildProcessRssMb(pid) {
|
|
|
7523
7523
|
if (!pid || pid <= 0) return null;
|
|
7524
7524
|
try {
|
|
7525
7525
|
if (typeof import_child_process9.execFile !== "function") return null;
|
|
7526
|
-
const stdout = await new Promise((
|
|
7526
|
+
const stdout = await new Promise((resolve8, reject) => {
|
|
7527
7527
|
(0, import_child_process9.execFile)("ps", ["-o", "rss=", "-p", String(pid)], { timeout: 1e3 }, (err, out) => {
|
|
7528
7528
|
if (err) {
|
|
7529
7529
|
reject(err);
|
|
7530
7530
|
return;
|
|
7531
7531
|
}
|
|
7532
|
-
|
|
7532
|
+
resolve8(String(out));
|
|
7533
7533
|
});
|
|
7534
7534
|
});
|
|
7535
7535
|
const kb = Number(String(stdout).trim());
|
|
@@ -7632,8 +7632,8 @@ var AgentControlPlane = class {
|
|
|
7632
7632
|
async withConfigLock(fn) {
|
|
7633
7633
|
const previousLock = this.configWriteLock;
|
|
7634
7634
|
let releaseLock;
|
|
7635
|
-
this.configWriteLock = new Promise((
|
|
7636
|
-
releaseLock =
|
|
7635
|
+
this.configWriteLock = new Promise((resolve8) => {
|
|
7636
|
+
releaseLock = resolve8;
|
|
7637
7637
|
});
|
|
7638
7638
|
try {
|
|
7639
7639
|
await previousLock;
|
|
@@ -8862,7 +8862,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8862
8862
|
const lockContent = fs14.readFileSync(lockPath, "utf-8").trim();
|
|
8863
8863
|
const lockPid = parseInt(lockContent, 10);
|
|
8864
8864
|
if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
|
|
8865
|
-
await new Promise((
|
|
8865
|
+
await new Promise((resolve8) => setTimeout(resolve8, retryInterval));
|
|
8866
8866
|
continue;
|
|
8867
8867
|
}
|
|
8868
8868
|
} catch {
|
|
@@ -8876,7 +8876,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8876
8876
|
} catch {
|
|
8877
8877
|
continue;
|
|
8878
8878
|
}
|
|
8879
|
-
await new Promise((
|
|
8879
|
+
await new Promise((resolve8) => setTimeout(resolve8, retryInterval));
|
|
8880
8880
|
continue;
|
|
8881
8881
|
}
|
|
8882
8882
|
throw err;
|
|
@@ -9663,13 +9663,14 @@ function getInstallCommand(cwd) {
|
|
|
9663
9663
|
}
|
|
9664
9664
|
|
|
9665
9665
|
// src/daemon/daemon-process.ts
|
|
9666
|
-
var
|
|
9666
|
+
var fs35 = __toESM(require("fs"));
|
|
9667
9667
|
var http2 = __toESM(require("http"));
|
|
9668
|
-
var
|
|
9668
|
+
var os16 = __toESM(require("os"));
|
|
9669
|
+
var path37 = __toESM(require("path"));
|
|
9669
9670
|
var import_child_process19 = require("child_process");
|
|
9670
9671
|
|
|
9671
9672
|
// src/daemon/ipc-router.ts
|
|
9672
|
-
var
|
|
9673
|
+
var os15 = __toESM(require("os"));
|
|
9673
9674
|
var import_core15 = __toESM(require_dist());
|
|
9674
9675
|
|
|
9675
9676
|
// src/daemon/handlers/file-handlers.ts
|
|
@@ -10400,7 +10401,7 @@ async function handleExec(command, projectPath) {
|
|
|
10400
10401
|
env = {}
|
|
10401
10402
|
} = command;
|
|
10402
10403
|
const effectiveTimeout = Math.min(Math.max(timeout, 1e3), MAX_TIMEOUT);
|
|
10403
|
-
return new Promise((
|
|
10404
|
+
return new Promise((resolve8) => {
|
|
10404
10405
|
let stdout = "";
|
|
10405
10406
|
let stderr = "";
|
|
10406
10407
|
let timedOut = false;
|
|
@@ -10408,7 +10409,7 @@ async function handleExec(command, projectPath) {
|
|
|
10408
10409
|
const done = (result) => {
|
|
10409
10410
|
if (resolved) return;
|
|
10410
10411
|
resolved = true;
|
|
10411
|
-
|
|
10412
|
+
resolve8(result);
|
|
10412
10413
|
};
|
|
10413
10414
|
try {
|
|
10414
10415
|
const proc = (0, import_child_process11.spawn)(cmd, {
|
|
@@ -10512,18 +10513,18 @@ var import_core12 = __toESM(require_dist());
|
|
|
10512
10513
|
// src/utils/port-check.ts
|
|
10513
10514
|
var net2 = __toESM(require("net"));
|
|
10514
10515
|
async function isPortInUse(port) {
|
|
10515
|
-
return new Promise((
|
|
10516
|
+
return new Promise((resolve8) => {
|
|
10516
10517
|
const server = net2.createServer();
|
|
10517
10518
|
server.once("error", (err) => {
|
|
10518
10519
|
if (err.code === "EADDRINUSE") {
|
|
10519
|
-
|
|
10520
|
+
resolve8(true);
|
|
10520
10521
|
} else {
|
|
10521
|
-
|
|
10522
|
+
resolve8(false);
|
|
10522
10523
|
}
|
|
10523
10524
|
});
|
|
10524
10525
|
server.once("listening", () => {
|
|
10525
10526
|
server.close();
|
|
10526
|
-
|
|
10527
|
+
resolve8(false);
|
|
10527
10528
|
});
|
|
10528
10529
|
server.listen(port);
|
|
10529
10530
|
});
|
|
@@ -10926,7 +10927,7 @@ var DevServerRegistry = class {
|
|
|
10926
10927
|
return killed;
|
|
10927
10928
|
}
|
|
10928
10929
|
wait(ms) {
|
|
10929
|
-
return new Promise((
|
|
10930
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
10930
10931
|
}
|
|
10931
10932
|
killWsServerOnPort(wsPort, worktreePath) {
|
|
10932
10933
|
const killed = [];
|
|
@@ -11500,7 +11501,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
11500
11501
|
return Math.min(delay, DEV_SERVER_CONSTANTS.MAX_RESTART_DELAY_MS);
|
|
11501
11502
|
}
|
|
11502
11503
|
async checkHealth(port) {
|
|
11503
|
-
return new Promise((
|
|
11504
|
+
return new Promise((resolve8) => {
|
|
11504
11505
|
const req = http.request(
|
|
11505
11506
|
{
|
|
11506
11507
|
hostname: "localhost",
|
|
@@ -11509,18 +11510,18 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
11509
11510
|
method: "HEAD",
|
|
11510
11511
|
timeout: DEV_SERVER_CONSTANTS.HEALTH_CHECK_TIMEOUT_MS
|
|
11511
11512
|
},
|
|
11512
|
-
() =>
|
|
11513
|
+
() => resolve8(true)
|
|
11513
11514
|
);
|
|
11514
|
-
req.on("error", () =>
|
|
11515
|
+
req.on("error", () => resolve8(false));
|
|
11515
11516
|
req.on("timeout", () => {
|
|
11516
11517
|
req.destroy();
|
|
11517
|
-
|
|
11518
|
+
resolve8(false);
|
|
11518
11519
|
});
|
|
11519
11520
|
req.end();
|
|
11520
11521
|
});
|
|
11521
11522
|
}
|
|
11522
11523
|
async checkWsHealth(wsPort) {
|
|
11523
|
-
return new Promise((
|
|
11524
|
+
return new Promise((resolve8) => {
|
|
11524
11525
|
const req = http.request(
|
|
11525
11526
|
{
|
|
11526
11527
|
hostname: "127.0.0.1",
|
|
@@ -11530,14 +11531,14 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
11530
11531
|
timeout: DEV_SERVER_CONSTANTS.HEALTH_CHECK_TIMEOUT_MS
|
|
11531
11532
|
},
|
|
11532
11533
|
(res) => {
|
|
11533
|
-
|
|
11534
|
+
resolve8(res.statusCode === 200);
|
|
11534
11535
|
res.resume();
|
|
11535
11536
|
}
|
|
11536
11537
|
);
|
|
11537
|
-
req.on("error", () =>
|
|
11538
|
+
req.on("error", () => resolve8(false));
|
|
11538
11539
|
req.on("timeout", () => {
|
|
11539
11540
|
req.destroy();
|
|
11540
|
-
|
|
11541
|
+
resolve8(false);
|
|
11541
11542
|
});
|
|
11542
11543
|
req.end();
|
|
11543
11544
|
});
|
|
@@ -11565,7 +11566,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
11565
11566
|
return false;
|
|
11566
11567
|
}
|
|
11567
11568
|
wait(ms) {
|
|
11568
|
-
return new Promise((
|
|
11569
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
11569
11570
|
}
|
|
11570
11571
|
getLogsDir() {
|
|
11571
11572
|
const logsDir = path25.join((0, import_core12.getConfigDir)(), "logs");
|
|
@@ -11764,7 +11765,7 @@ var PreviewManager = class extends import_events3.EventEmitter {
|
|
|
11764
11765
|
for (let attempt = 1; attempt <= MAX_TUNNEL_RETRIES; attempt++) {
|
|
11765
11766
|
if (attempt > 1) {
|
|
11766
11767
|
console.log(`[PreviewManager] Retrying tunnel for ${moduleUid} (attempt ${attempt}/${MAX_TUNNEL_RETRIES})...`);
|
|
11767
|
-
await new Promise((
|
|
11768
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e3));
|
|
11768
11769
|
}
|
|
11769
11770
|
tunnelResult = await this.tunnel.startTunnel({
|
|
11770
11771
|
moduleUid,
|
|
@@ -11896,7 +11897,7 @@ var PreviewManager = class extends import_events3.EventEmitter {
|
|
|
11896
11897
|
}
|
|
11897
11898
|
console.log(`[PreviewManager] Restarting preview for ${moduleUid}`);
|
|
11898
11899
|
await this.stopPreview(moduleUid);
|
|
11899
|
-
await new Promise((
|
|
11900
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
11900
11901
|
return this.startPreview({
|
|
11901
11902
|
moduleUid,
|
|
11902
11903
|
worktreePath: state.worktreePath,
|
|
@@ -12872,6 +12873,9 @@ async function handleProjectSetup(params) {
|
|
|
12872
12873
|
|
|
12873
12874
|
// src/daemon/handlers/pty-handler.ts
|
|
12874
12875
|
var pty = __toESM(require("@lydell/node-pty"));
|
|
12876
|
+
var fs30 = __toESM(require("fs"));
|
|
12877
|
+
var os14 = __toESM(require("os"));
|
|
12878
|
+
var path30 = __toESM(require("path"));
|
|
12875
12879
|
var INACTIVITY_TIMEOUT_MS3 = 30 * 60 * 1e3;
|
|
12876
12880
|
var sessions = /* @__PURE__ */ new Map();
|
|
12877
12881
|
async function handlePtySpawn(payload, client) {
|
|
@@ -12881,6 +12885,7 @@ async function handlePtySpawn(payload, client) {
|
|
|
12881
12885
|
return;
|
|
12882
12886
|
}
|
|
12883
12887
|
console.log(`[PTY] Spawning PTY for ${moduleUid} / ${agent_run_id}: ${command} ${args.join(" ")}`);
|
|
12888
|
+
const bootstrap = createCredentialBootstrap(payload, agent_run_id);
|
|
12884
12889
|
let proc;
|
|
12885
12890
|
try {
|
|
12886
12891
|
proc = pty.spawn(command, args, {
|
|
@@ -12888,9 +12893,14 @@ async function handlePtySpawn(payload, client) {
|
|
|
12888
12893
|
cols,
|
|
12889
12894
|
rows,
|
|
12890
12895
|
cwd: cwd || process.cwd(),
|
|
12891
|
-
env: {
|
|
12896
|
+
env: {
|
|
12897
|
+
...process.env,
|
|
12898
|
+
...env || {},
|
|
12899
|
+
...bootstrap.env
|
|
12900
|
+
}
|
|
12892
12901
|
});
|
|
12893
12902
|
} catch (err) {
|
|
12903
|
+
cleanupCredentialDirs(bootstrap.cleanupDirs);
|
|
12894
12904
|
console.error(`[PTY] Failed to spawn PTY for ${agent_run_id}:`, err);
|
|
12895
12905
|
await client.send({
|
|
12896
12906
|
type: "pty_exit",
|
|
@@ -12907,7 +12917,8 @@ async function handlePtySpawn(payload, client) {
|
|
|
12907
12917
|
agent_run_id,
|
|
12908
12918
|
startedAt: Date.now(),
|
|
12909
12919
|
lastOutputAt: Date.now(),
|
|
12910
|
-
watchdogTimer: null
|
|
12920
|
+
watchdogTimer: null,
|
|
12921
|
+
credentialDirs: bootstrap.cleanupDirs
|
|
12911
12922
|
};
|
|
12912
12923
|
sessions.set(agent_run_id, session);
|
|
12913
12924
|
const resetWatchdog = () => {
|
|
@@ -12941,6 +12952,7 @@ async function handlePtySpawn(payload, client) {
|
|
|
12941
12952
|
if (session.watchdogTimer) clearTimeout(session.watchdogTimer);
|
|
12942
12953
|
console.log(`[PTY] Process exited for ${agent_run_id} with code ${exitCode} after ${durationMs}ms`);
|
|
12943
12954
|
sessions.delete(agent_run_id);
|
|
12955
|
+
cleanupCredentialDirs(session.credentialDirs);
|
|
12944
12956
|
client.send({
|
|
12945
12957
|
type: "pty_exit",
|
|
12946
12958
|
moduleUid,
|
|
@@ -12976,15 +12988,67 @@ function handlePtyKill(payload) {
|
|
|
12976
12988
|
session.pty.kill();
|
|
12977
12989
|
} catch {
|
|
12978
12990
|
}
|
|
12991
|
+
cleanupCredentialDirs(session.credentialDirs);
|
|
12979
12992
|
sessions.delete(agent_run_id);
|
|
12980
12993
|
console.log(`[PTY] Killed session ${agent_run_id}`);
|
|
12981
12994
|
}
|
|
12995
|
+
function createCredentialBootstrap(payload, agentRunId) {
|
|
12996
|
+
const bootstrap = payload.credential_bootstrap;
|
|
12997
|
+
if (!bootstrap?.oauth?.access_token) {
|
|
12998
|
+
return { env: {}, cleanupDirs: [] };
|
|
12999
|
+
}
|
|
13000
|
+
const baseDir = fs30.mkdtempSync(path30.join(os14.tmpdir(), `episoda-pty-${agentRunId}-`));
|
|
13001
|
+
const cleanupDirs = [baseDir];
|
|
13002
|
+
if (bootstrap.provider === "claude") {
|
|
13003
|
+
const claudeConfigDir = path30.join(baseDir, ".claude");
|
|
13004
|
+
fs30.mkdirSync(claudeConfigDir, { recursive: true });
|
|
13005
|
+
const credentialsPath = path30.join(claudeConfigDir, ".credentials.json");
|
|
13006
|
+
const claudeAiOauth = {
|
|
13007
|
+
accessToken: bootstrap.oauth.access_token
|
|
13008
|
+
};
|
|
13009
|
+
if (bootstrap.oauth.refresh_token) claudeAiOauth.refreshToken = bootstrap.oauth.refresh_token;
|
|
13010
|
+
if (bootstrap.oauth.expires_at) claudeAiOauth.expiresAt = new Date(bootstrap.oauth.expires_at).getTime();
|
|
13011
|
+
if (bootstrap.oauth.scopes) claudeAiOauth.scopes = bootstrap.oauth.scopes;
|
|
13012
|
+
fs30.writeFileSync(credentialsPath, JSON.stringify({ claudeAiOauth }, null, 2), { mode: 384 });
|
|
13013
|
+
return {
|
|
13014
|
+
env: { CLAUDE_CONFIG_DIR: claudeConfigDir },
|
|
13015
|
+
cleanupDirs
|
|
13016
|
+
};
|
|
13017
|
+
}
|
|
13018
|
+
const codexHome = path30.join(baseDir, ".codex");
|
|
13019
|
+
fs30.mkdirSync(codexHome, { recursive: true });
|
|
13020
|
+
const authPath = path30.join(codexHome, "auth.json");
|
|
13021
|
+
const authPayload = {
|
|
13022
|
+
tokens: {
|
|
13023
|
+
access_token: bootstrap.oauth.access_token,
|
|
13024
|
+
refresh_token: bootstrap.oauth.refresh_token,
|
|
13025
|
+
id_token: bootstrap.oauth.id_token,
|
|
13026
|
+
account_id: bootstrap.oauth.account_id
|
|
13027
|
+
}
|
|
13028
|
+
};
|
|
13029
|
+
fs30.writeFileSync(authPath, JSON.stringify(authPayload, null, 2), { mode: 384 });
|
|
13030
|
+
return {
|
|
13031
|
+
env: { CODEX_HOME: codexHome },
|
|
13032
|
+
cleanupDirs
|
|
13033
|
+
};
|
|
13034
|
+
}
|
|
13035
|
+
function cleanupCredentialDirs(dirs) {
|
|
13036
|
+
for (const dirPath of dirs) {
|
|
13037
|
+
try {
|
|
13038
|
+
if (fs30.existsSync(dirPath)) {
|
|
13039
|
+
fs30.rmSync(dirPath, { recursive: true, force: true });
|
|
13040
|
+
}
|
|
13041
|
+
} catch (error) {
|
|
13042
|
+
console.warn(`[PTY] Failed to cleanup credential dir ${dirPath}:`, error);
|
|
13043
|
+
}
|
|
13044
|
+
}
|
|
13045
|
+
}
|
|
12982
13046
|
|
|
12983
13047
|
// src/utils/dev-server.ts
|
|
12984
13048
|
var import_child_process15 = require("child_process");
|
|
12985
13049
|
var import_core14 = __toESM(require_dist());
|
|
12986
|
-
var
|
|
12987
|
-
var
|
|
13050
|
+
var fs31 = __toESM(require("fs"));
|
|
13051
|
+
var path31 = __toESM(require("path"));
|
|
12988
13052
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
12989
13053
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
12990
13054
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -12992,26 +13056,26 @@ var MAX_LOG_SIZE_BYTES2 = 5 * 1024 * 1024;
|
|
|
12992
13056
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
12993
13057
|
var activeServers = /* @__PURE__ */ new Map();
|
|
12994
13058
|
function getLogsDir() {
|
|
12995
|
-
const logsDir =
|
|
12996
|
-
if (!
|
|
12997
|
-
|
|
13059
|
+
const logsDir = path31.join((0, import_core14.getConfigDir)(), "logs");
|
|
13060
|
+
if (!fs31.existsSync(logsDir)) {
|
|
13061
|
+
fs31.mkdirSync(logsDir, { recursive: true });
|
|
12998
13062
|
}
|
|
12999
13063
|
return logsDir;
|
|
13000
13064
|
}
|
|
13001
13065
|
function getLogFilePath(moduleUid) {
|
|
13002
|
-
return
|
|
13066
|
+
return path31.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
13003
13067
|
}
|
|
13004
13068
|
function rotateLogIfNeeded(logPath) {
|
|
13005
13069
|
try {
|
|
13006
|
-
if (
|
|
13007
|
-
const stats =
|
|
13070
|
+
if (fs31.existsSync(logPath)) {
|
|
13071
|
+
const stats = fs31.statSync(logPath);
|
|
13008
13072
|
if (stats.size > MAX_LOG_SIZE_BYTES2) {
|
|
13009
13073
|
const backupPath = `${logPath}.1`;
|
|
13010
|
-
if (
|
|
13011
|
-
|
|
13074
|
+
if (fs31.existsSync(backupPath)) {
|
|
13075
|
+
fs31.unlinkSync(backupPath);
|
|
13012
13076
|
}
|
|
13013
|
-
|
|
13014
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
13077
|
+
fs31.renameSync(logPath, backupPath);
|
|
13078
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path31.basename(logPath)}`);
|
|
13015
13079
|
}
|
|
13016
13080
|
}
|
|
13017
13081
|
} catch (error) {
|
|
@@ -13024,7 +13088,7 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
13024
13088
|
const prefix = isError ? "ERR" : "OUT";
|
|
13025
13089
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
13026
13090
|
`;
|
|
13027
|
-
|
|
13091
|
+
fs31.appendFileSync(logPath, logLine);
|
|
13028
13092
|
} catch {
|
|
13029
13093
|
}
|
|
13030
13094
|
}
|
|
@@ -13044,7 +13108,7 @@ async function killProcessOnPort(port) {
|
|
|
13044
13108
|
} catch {
|
|
13045
13109
|
}
|
|
13046
13110
|
}
|
|
13047
|
-
await new Promise((
|
|
13111
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
13048
13112
|
for (const pid of pids) {
|
|
13049
13113
|
try {
|
|
13050
13114
|
(0, import_child_process15.execSync)(`kill -0 ${pid} 2>/dev/null`, { encoding: "utf8" });
|
|
@@ -13053,7 +13117,7 @@ async function killProcessOnPort(port) {
|
|
|
13053
13117
|
} catch {
|
|
13054
13118
|
}
|
|
13055
13119
|
}
|
|
13056
|
-
await new Promise((
|
|
13120
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
13057
13121
|
const stillInUse = await isPortInUse(port);
|
|
13058
13122
|
if (stillInUse) {
|
|
13059
13123
|
console.error(`[DevServer] EP929: Port ${port} still in use after kill attempts`);
|
|
@@ -13073,7 +13137,7 @@ async function waitForPort(port, timeoutMs = 3e4) {
|
|
|
13073
13137
|
if (await isPortInUse(port)) {
|
|
13074
13138
|
return true;
|
|
13075
13139
|
}
|
|
13076
|
-
await new Promise((
|
|
13140
|
+
await new Promise((resolve8) => setTimeout(resolve8, checkInterval));
|
|
13077
13141
|
}
|
|
13078
13142
|
return false;
|
|
13079
13143
|
}
|
|
@@ -13145,7 +13209,7 @@ async function handleProcessExit(moduleUid, code, signal) {
|
|
|
13145
13209
|
const delay = calculateRestartDelay(serverInfo.restartCount);
|
|
13146
13210
|
console.log(`[DevServer] EP932: Restarting ${moduleUid} in ${delay}ms (attempt ${serverInfo.restartCount + 1}/${MAX_RESTART_ATTEMPTS})`);
|
|
13147
13211
|
writeToLog(serverInfo.logFile || "", `Scheduling restart in ${delay}ms (attempt ${serverInfo.restartCount + 1})`, false);
|
|
13148
|
-
await new Promise((
|
|
13212
|
+
await new Promise((resolve8) => setTimeout(resolve8, delay));
|
|
13149
13213
|
if (!activeServers.has(moduleUid)) {
|
|
13150
13214
|
console.log(`[DevServer] EP932: Server ${moduleUid} was removed during restart delay, aborting restart`);
|
|
13151
13215
|
return;
|
|
@@ -13203,8 +13267,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
13203
13267
|
});
|
|
13204
13268
|
injectedEnvVars = result.envVars;
|
|
13205
13269
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
13206
|
-
const envFilePath =
|
|
13207
|
-
if (!
|
|
13270
|
+
const envFilePath = path31.join(projectPath, ".env");
|
|
13271
|
+
if (!fs31.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
13208
13272
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
13209
13273
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
13210
13274
|
}
|
|
@@ -13270,7 +13334,7 @@ async function stopDevServer(moduleUid) {
|
|
|
13270
13334
|
writeToLog(serverInfo.logFile, `Stopping server (manual stop)`, false);
|
|
13271
13335
|
}
|
|
13272
13336
|
serverInfo.process.kill("SIGTERM");
|
|
13273
|
-
await new Promise((
|
|
13337
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e3));
|
|
13274
13338
|
if (!serverInfo.process.killed) {
|
|
13275
13339
|
serverInfo.process.kill("SIGKILL");
|
|
13276
13340
|
}
|
|
@@ -13288,7 +13352,7 @@ async function restartDevServer(moduleUid) {
|
|
|
13288
13352
|
writeToLog(logFile, `Manual restart requested`, false);
|
|
13289
13353
|
}
|
|
13290
13354
|
await stopDevServer(moduleUid);
|
|
13291
|
-
await new Promise((
|
|
13355
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
13292
13356
|
if (await isPortInUse(port)) {
|
|
13293
13357
|
await killProcessOnPort(port);
|
|
13294
13358
|
}
|
|
@@ -13342,9 +13406,9 @@ var IPCRouter = class {
|
|
|
13342
13406
|
// EP726: Kept for backward compatibility
|
|
13343
13407
|
machineName: this.host.deviceName,
|
|
13344
13408
|
// EP1186: User-friendly machine name from server
|
|
13345
|
-
hostname:
|
|
13346
|
-
platform:
|
|
13347
|
-
arch:
|
|
13409
|
+
hostname: os15.hostname(),
|
|
13410
|
+
platform: os15.platform(),
|
|
13411
|
+
arch: os15.arch(),
|
|
13348
13412
|
projects
|
|
13349
13413
|
};
|
|
13350
13414
|
});
|
|
@@ -13370,7 +13434,7 @@ var IPCRouter = class {
|
|
|
13370
13434
|
if (attempt < MAX_RETRIES) {
|
|
13371
13435
|
const delay = INITIAL_DELAY * Math.pow(2, attempt - 1);
|
|
13372
13436
|
console.log(`[Daemon] Retrying in ${delay / 1e3}s...`);
|
|
13373
|
-
await new Promise((
|
|
13437
|
+
await new Promise((resolve8) => setTimeout(resolve8, delay));
|
|
13374
13438
|
await this.host.disconnectProject(projectPath);
|
|
13375
13439
|
}
|
|
13376
13440
|
}
|
|
@@ -13546,8 +13610,8 @@ var IPCRouter = class {
|
|
|
13546
13610
|
};
|
|
13547
13611
|
|
|
13548
13612
|
// src/daemon/update-manager.ts
|
|
13549
|
-
var
|
|
13550
|
-
var
|
|
13613
|
+
var fs32 = __toESM(require("fs"));
|
|
13614
|
+
var path32 = __toESM(require("path"));
|
|
13551
13615
|
var import_child_process17 = require("child_process");
|
|
13552
13616
|
var import_core17 = __toESM(require_dist());
|
|
13553
13617
|
var semver2 = __toESM(require("semver"));
|
|
@@ -13805,8 +13869,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
13805
13869
|
console.log(`[Daemon] EP1324: Update to ${targetVersion} installed, restarting daemon...`);
|
|
13806
13870
|
await this.host.shutdown();
|
|
13807
13871
|
const configDir = (0, import_core17.getConfigDir)();
|
|
13808
|
-
const logPath =
|
|
13809
|
-
const logFd =
|
|
13872
|
+
const logPath = path32.join(configDir, "daemon.log");
|
|
13873
|
+
const logFd = fs32.openSync(logPath, "a");
|
|
13810
13874
|
const child = (0, import_child_process17.spawn)("node", [this.daemonEntryFile], {
|
|
13811
13875
|
detached: true,
|
|
13812
13876
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -13816,7 +13880,7 @@ var UpdateManager = class _UpdateManager {
|
|
|
13816
13880
|
});
|
|
13817
13881
|
if (!child.pid) {
|
|
13818
13882
|
try {
|
|
13819
|
-
|
|
13883
|
+
fs32.closeSync(logFd);
|
|
13820
13884
|
} catch {
|
|
13821
13885
|
}
|
|
13822
13886
|
this.recordUpdateFailure(targetVersion, "Failed to spawn replacement daemon (missing pid)");
|
|
@@ -13824,7 +13888,7 @@ var UpdateManager = class _UpdateManager {
|
|
|
13824
13888
|
}
|
|
13825
13889
|
child.unref();
|
|
13826
13890
|
const pidPath = getPidFilePath();
|
|
13827
|
-
|
|
13891
|
+
fs32.writeFileSync(pidPath, child.pid.toString(), "utf-8");
|
|
13828
13892
|
console.log(`[Daemon] EP1324: New daemon spawned (PID: ${child.pid}), exiting old process`);
|
|
13829
13893
|
process.exit(0);
|
|
13830
13894
|
} catch (error) {
|
|
@@ -13875,8 +13939,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
13875
13939
|
};
|
|
13876
13940
|
|
|
13877
13941
|
// src/daemon/health-orchestrator.ts
|
|
13878
|
-
var
|
|
13879
|
-
var
|
|
13942
|
+
var fs33 = __toESM(require("fs"));
|
|
13943
|
+
var path33 = __toESM(require("path"));
|
|
13880
13944
|
var import_core18 = __toESM(require_dist());
|
|
13881
13945
|
var HealthOrchestrator = class _HealthOrchestrator {
|
|
13882
13946
|
constructor(host) {
|
|
@@ -14136,26 +14200,26 @@ var HealthOrchestrator = class _HealthOrchestrator {
|
|
|
14136
14200
|
checkAndRotateLog() {
|
|
14137
14201
|
try {
|
|
14138
14202
|
const configDir = (0, import_core18.getConfigDir)();
|
|
14139
|
-
const logPath =
|
|
14140
|
-
if (!
|
|
14141
|
-
const stats =
|
|
14203
|
+
const logPath = path33.join(configDir, "daemon.log");
|
|
14204
|
+
if (!fs33.existsSync(logPath)) return;
|
|
14205
|
+
const stats = fs33.statSync(logPath);
|
|
14142
14206
|
if (stats.size < _HealthOrchestrator.MAX_LOG_SIZE_BYTES) return;
|
|
14143
14207
|
console.log(`[Daemon] EP1351: daemon.log is ${Math.round(stats.size / 1024 / 1024)}MB, rotating...`);
|
|
14144
14208
|
for (let i = _HealthOrchestrator.MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
14145
14209
|
const src = `${logPath}.${i}`;
|
|
14146
14210
|
const dst = `${logPath}.${i + 1}`;
|
|
14147
|
-
if (
|
|
14211
|
+
if (fs33.existsSync(src)) {
|
|
14148
14212
|
try {
|
|
14149
|
-
|
|
14213
|
+
fs33.renameSync(src, dst);
|
|
14150
14214
|
} catch {
|
|
14151
14215
|
}
|
|
14152
14216
|
}
|
|
14153
14217
|
}
|
|
14154
14218
|
try {
|
|
14155
|
-
|
|
14219
|
+
fs33.copyFileSync(logPath, `${logPath}.1`);
|
|
14156
14220
|
} catch {
|
|
14157
14221
|
}
|
|
14158
|
-
|
|
14222
|
+
fs33.truncateSync(logPath, 0);
|
|
14159
14223
|
console.log("[Daemon] EP1351: Log rotated successfully");
|
|
14160
14224
|
} catch (error) {
|
|
14161
14225
|
console.warn("[Daemon] EP1351: Log rotation failed:", error instanceof Error ? error.message : error);
|
|
@@ -14204,9 +14268,9 @@ var HealthOrchestrator = class _HealthOrchestrator {
|
|
|
14204
14268
|
if (Number.isFinite(lastAccessedMs)) {
|
|
14205
14269
|
referenceTimestampMs = lastAccessedMs;
|
|
14206
14270
|
fallbackAgeSource.push(`${w.moduleUid}(lastAccessed)`);
|
|
14207
|
-
} else if (w.worktreePath &&
|
|
14271
|
+
} else if (w.worktreePath && fs33.existsSync(w.worktreePath)) {
|
|
14208
14272
|
try {
|
|
14209
|
-
const stats =
|
|
14273
|
+
const stats = fs33.statSync(w.worktreePath);
|
|
14210
14274
|
referenceTimestampMs = stats.mtimeMs;
|
|
14211
14275
|
fallbackAgeSource.push(`${w.moduleUid}(fs.mtime)`);
|
|
14212
14276
|
} catch {
|
|
@@ -14646,7 +14710,7 @@ var ConnectionManager = class {
|
|
|
14646
14710
|
* handlers are removed deterministically on every exit path.
|
|
14647
14711
|
*/
|
|
14648
14712
|
async waitForAuthentication(client, timeoutMs = 3e4) {
|
|
14649
|
-
await new Promise((
|
|
14713
|
+
await new Promise((resolve8, reject) => {
|
|
14650
14714
|
let settled = false;
|
|
14651
14715
|
const cleanup = () => {
|
|
14652
14716
|
clearTimeout(timeout);
|
|
@@ -14665,7 +14729,7 @@ var ConnectionManager = class {
|
|
|
14665
14729
|
if (settled) return;
|
|
14666
14730
|
settled = true;
|
|
14667
14731
|
cleanup();
|
|
14668
|
-
|
|
14732
|
+
resolve8();
|
|
14669
14733
|
};
|
|
14670
14734
|
const authErrorHandler = (message) => {
|
|
14671
14735
|
if (settled) return;
|
|
@@ -14752,7 +14816,7 @@ function resolveDaemonWsEndpoint(config, env = process.env) {
|
|
|
14752
14816
|
|
|
14753
14817
|
// src/daemon/project-message-router.ts
|
|
14754
14818
|
var import_core19 = __toESM(require_dist());
|
|
14755
|
-
var
|
|
14819
|
+
var path34 = __toESM(require("path"));
|
|
14756
14820
|
var ProjectMessageRouter = class {
|
|
14757
14821
|
constructor(host) {
|
|
14758
14822
|
this.host = host;
|
|
@@ -14770,7 +14834,7 @@ var ProjectMessageRouter = class {
|
|
|
14770
14834
|
client.updateActivity();
|
|
14771
14835
|
try {
|
|
14772
14836
|
const gitCmd = message.command;
|
|
14773
|
-
const bareRepoPath =
|
|
14837
|
+
const bareRepoPath = path34.join(projectPath, ".bare");
|
|
14774
14838
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
14775
14839
|
if (gitCmd.worktreePath) {
|
|
14776
14840
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -15144,32 +15208,32 @@ async function fetchEnvVars2(projectId) {
|
|
|
15144
15208
|
}
|
|
15145
15209
|
|
|
15146
15210
|
// src/daemon/project-git-config.ts
|
|
15147
|
-
var
|
|
15148
|
-
var
|
|
15211
|
+
var fs34 = __toESM(require("fs"));
|
|
15212
|
+
var path35 = __toESM(require("path"));
|
|
15149
15213
|
var import_core21 = __toESM(require_dist());
|
|
15150
15214
|
function getGitDirs(projectPath) {
|
|
15151
|
-
const bareDir =
|
|
15152
|
-
const gitPath =
|
|
15153
|
-
if (
|
|
15215
|
+
const bareDir = path35.join(projectPath, ".bare");
|
|
15216
|
+
const gitPath = path35.join(projectPath, ".git");
|
|
15217
|
+
if (fs34.existsSync(bareDir) && fs34.statSync(bareDir).isDirectory()) {
|
|
15154
15218
|
return { gitDir: bareDir, workDir: projectPath };
|
|
15155
15219
|
}
|
|
15156
|
-
if (
|
|
15220
|
+
if (fs34.existsSync(gitPath) && fs34.statSync(gitPath).isDirectory()) {
|
|
15157
15221
|
return { gitDir: null, workDir: projectPath };
|
|
15158
15222
|
}
|
|
15159
|
-
if (
|
|
15223
|
+
if (fs34.existsSync(gitPath) && fs34.statSync(gitPath).isFile()) {
|
|
15160
15224
|
return { gitDir: null, workDir: projectPath };
|
|
15161
15225
|
}
|
|
15162
|
-
const entries =
|
|
15226
|
+
const entries = fs34.readdirSync(projectPath, { withFileTypes: true });
|
|
15163
15227
|
for (const entry of entries) {
|
|
15164
15228
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
15165
|
-
const worktreePath =
|
|
15166
|
-
const worktreeGit =
|
|
15167
|
-
if (
|
|
15229
|
+
const worktreePath = path35.join(projectPath, entry.name);
|
|
15230
|
+
const worktreeGit = path35.join(worktreePath, ".git");
|
|
15231
|
+
if (fs34.existsSync(worktreeGit)) {
|
|
15168
15232
|
return { gitDir: null, workDir: worktreePath };
|
|
15169
15233
|
}
|
|
15170
15234
|
}
|
|
15171
15235
|
}
|
|
15172
|
-
if (
|
|
15236
|
+
if (fs34.existsSync(bareDir)) {
|
|
15173
15237
|
return { gitDir: bareDir, workDir: projectPath };
|
|
15174
15238
|
}
|
|
15175
15239
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -15214,24 +15278,24 @@ async function configureGitUser(projectPath, userId, workspaceId, machineId, pro
|
|
|
15214
15278
|
async function installGitHooks(projectPath) {
|
|
15215
15279
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
15216
15280
|
let hooksDir;
|
|
15217
|
-
const bareHooksDir =
|
|
15218
|
-
const gitHooksDir =
|
|
15219
|
-
if (
|
|
15281
|
+
const bareHooksDir = path35.join(projectPath, ".bare", "hooks");
|
|
15282
|
+
const gitHooksDir = path35.join(projectPath, ".git", "hooks");
|
|
15283
|
+
if (fs34.existsSync(bareHooksDir)) {
|
|
15220
15284
|
hooksDir = bareHooksDir;
|
|
15221
|
-
} else if (
|
|
15285
|
+
} else if (fs34.existsSync(gitHooksDir) && fs34.statSync(path35.join(projectPath, ".git")).isDirectory()) {
|
|
15222
15286
|
hooksDir = gitHooksDir;
|
|
15223
15287
|
} else {
|
|
15224
|
-
const parentBareHooks =
|
|
15225
|
-
if (
|
|
15288
|
+
const parentBareHooks = path35.join(projectPath, "..", ".bare", "hooks");
|
|
15289
|
+
if (fs34.existsSync(parentBareHooks)) {
|
|
15226
15290
|
hooksDir = parentBareHooks;
|
|
15227
15291
|
} else {
|
|
15228
15292
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
15229
15293
|
return;
|
|
15230
15294
|
}
|
|
15231
15295
|
}
|
|
15232
|
-
if (!
|
|
15296
|
+
if (!fs34.existsSync(hooksDir)) {
|
|
15233
15297
|
try {
|
|
15234
|
-
|
|
15298
|
+
fs34.mkdirSync(hooksDir, { recursive: true });
|
|
15235
15299
|
} catch {
|
|
15236
15300
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
15237
15301
|
return;
|
|
@@ -15239,20 +15303,20 @@ async function installGitHooks(projectPath) {
|
|
|
15239
15303
|
}
|
|
15240
15304
|
for (const hookName of hooks) {
|
|
15241
15305
|
try {
|
|
15242
|
-
const hookPath =
|
|
15243
|
-
const bundledHookPath =
|
|
15244
|
-
if (!
|
|
15306
|
+
const hookPath = path35.join(hooksDir, hookName);
|
|
15307
|
+
const bundledHookPath = path35.join(__dirname, "..", "hooks", hookName);
|
|
15308
|
+
if (!fs34.existsSync(bundledHookPath)) {
|
|
15245
15309
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
15246
15310
|
continue;
|
|
15247
15311
|
}
|
|
15248
|
-
const hookContent =
|
|
15249
|
-
if (
|
|
15250
|
-
const existingContent =
|
|
15312
|
+
const hookContent = fs34.readFileSync(bundledHookPath, "utf-8");
|
|
15313
|
+
if (fs34.existsSync(hookPath)) {
|
|
15314
|
+
const existingContent = fs34.readFileSync(hookPath, "utf-8");
|
|
15251
15315
|
if (existingContent === hookContent) {
|
|
15252
15316
|
continue;
|
|
15253
15317
|
}
|
|
15254
15318
|
}
|
|
15255
|
-
|
|
15319
|
+
fs34.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
15256
15320
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
15257
15321
|
} catch (error) {
|
|
15258
15322
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -15282,6 +15346,36 @@ async function cacheMachineUuid(machineUuid, machineId) {
|
|
|
15282
15346
|
}
|
|
15283
15347
|
}
|
|
15284
15348
|
|
|
15349
|
+
// src/daemon/reconciliation-utils.ts
|
|
15350
|
+
var path36 = __toESM(require("path"));
|
|
15351
|
+
function isPathUnderRoot(candidatePath, projectRoot) {
|
|
15352
|
+
const relative4 = path36.relative(projectRoot, candidatePath);
|
|
15353
|
+
return relative4 !== "" && !relative4.startsWith("..") && !path36.isAbsolute(relative4);
|
|
15354
|
+
}
|
|
15355
|
+
function collectUnexpectedWorktrees(params) {
|
|
15356
|
+
const projectRootResolved = path36.resolve(params.projectRoot);
|
|
15357
|
+
const bareRepoPathResolved = path36.resolve(params.bareRepoPath);
|
|
15358
|
+
const unexpectedWorktrees = [];
|
|
15359
|
+
for (const worktree of params.worktrees) {
|
|
15360
|
+
const worktreePathResolved = path36.resolve(worktree.path);
|
|
15361
|
+
if (worktreePathResolved === bareRepoPathResolved) continue;
|
|
15362
|
+
if (worktree.prunable || worktree.locked) continue;
|
|
15363
|
+
if (!isPathUnderRoot(worktreePathResolved, projectRootResolved)) continue;
|
|
15364
|
+
const moduleUid = path36.basename(worktreePathResolved);
|
|
15365
|
+
if (!validateModuleUid(moduleUid)) continue;
|
|
15366
|
+
if (!/^EP\d+$/.test(moduleUid)) continue;
|
|
15367
|
+
if (params.expectedModuleUids.has(moduleUid)) continue;
|
|
15368
|
+
const branch = worktree.branch || "unknown";
|
|
15369
|
+
if (branch === "HEAD (detached)") continue;
|
|
15370
|
+
unexpectedWorktrees.push({
|
|
15371
|
+
moduleUid,
|
|
15372
|
+
path: worktreePathResolved,
|
|
15373
|
+
branch
|
|
15374
|
+
});
|
|
15375
|
+
}
|
|
15376
|
+
return unexpectedWorktrees;
|
|
15377
|
+
}
|
|
15378
|
+
|
|
15285
15379
|
// src/daemon/daemon-process.ts
|
|
15286
15380
|
var packageJson = require_package();
|
|
15287
15381
|
var Daemon = class _Daemon {
|
|
@@ -15484,9 +15578,9 @@ var Daemon = class _Daemon {
|
|
|
15484
15578
|
this.healthServer = http2.createServer((req, res) => {
|
|
15485
15579
|
if (req.url === "/health" || req.url === "/") {
|
|
15486
15580
|
const isConnected = this.connectionManager.liveConnectionCount() > 0;
|
|
15487
|
-
const projects = Array.from(this.connectionManager.entries()).map(([
|
|
15488
|
-
path:
|
|
15489
|
-
connected: this.connectionManager.hasLiveConnection(
|
|
15581
|
+
const projects = Array.from(this.connectionManager.entries()).map(([path38, conn]) => ({
|
|
15582
|
+
path: path38,
|
|
15583
|
+
connected: this.connectionManager.hasLiveConnection(path38)
|
|
15490
15584
|
}));
|
|
15491
15585
|
const status = {
|
|
15492
15586
|
status: isConnected ? "healthy" : "degraded",
|
|
@@ -15559,8 +15653,8 @@ var Daemon = class _Daemon {
|
|
|
15559
15653
|
await this.shutdown();
|
|
15560
15654
|
try {
|
|
15561
15655
|
const pidPath = getPidFilePath();
|
|
15562
|
-
if (
|
|
15563
|
-
|
|
15656
|
+
if (fs35.existsSync(pidPath)) {
|
|
15657
|
+
fs35.unlinkSync(pidPath);
|
|
15564
15658
|
console.log("[Daemon] PID file cleaned up (watchdog)");
|
|
15565
15659
|
}
|
|
15566
15660
|
} catch (error) {
|
|
@@ -15623,8 +15717,8 @@ var Daemon = class _Daemon {
|
|
|
15623
15717
|
}
|
|
15624
15718
|
}
|
|
15625
15719
|
let releaseLock;
|
|
15626
|
-
const lockPromise = new Promise((
|
|
15627
|
-
releaseLock =
|
|
15720
|
+
const lockPromise = new Promise((resolve8) => {
|
|
15721
|
+
releaseLock = resolve8;
|
|
15628
15722
|
});
|
|
15629
15723
|
this.tunnelOperationLocks.set(moduleUid, lockPromise);
|
|
15630
15724
|
try {
|
|
@@ -15670,7 +15764,7 @@ var Daemon = class _Daemon {
|
|
|
15670
15764
|
const maxWait = 35e3;
|
|
15671
15765
|
const startTime = Date.now();
|
|
15672
15766
|
while (this.connectionManager.hasPendingConnection(projectPath) && Date.now() - startTime < maxWait) {
|
|
15673
|
-
await new Promise((
|
|
15767
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
15674
15768
|
}
|
|
15675
15769
|
if (this.connectionManager.hasLiveConnection(projectPath)) {
|
|
15676
15770
|
console.log(`[Daemon] Pending connection succeeded for ${projectPath}`);
|
|
@@ -16238,8 +16332,8 @@ var Daemon = class _Daemon {
|
|
|
16238
16332
|
let daemonPid;
|
|
16239
16333
|
try {
|
|
16240
16334
|
const pidPath = getPidFilePath();
|
|
16241
|
-
if (
|
|
16242
|
-
const pidStr =
|
|
16335
|
+
if (fs35.existsSync(pidPath)) {
|
|
16336
|
+
const pidStr = fs35.readFileSync(pidPath, "utf-8").trim();
|
|
16243
16337
|
daemonPid = parseInt(pidStr, 10);
|
|
16244
16338
|
}
|
|
16245
16339
|
} catch (pidError) {
|
|
@@ -16250,9 +16344,9 @@ var Daemon = class _Daemon {
|
|
|
16250
16344
|
const containerId = process.env.EPISODA_CONTAINER_ID;
|
|
16251
16345
|
const capabilities = ["worktree_create_v1"];
|
|
16252
16346
|
await client.connect(wsEndpoint.wsUrl, config.access_token, this.machineId, {
|
|
16253
|
-
hostname:
|
|
16254
|
-
osPlatform:
|
|
16255
|
-
osArch:
|
|
16347
|
+
hostname: os16.hostname(),
|
|
16348
|
+
osPlatform: os16.platform(),
|
|
16349
|
+
osArch: os16.arch(),
|
|
16256
16350
|
daemonPid,
|
|
16257
16351
|
cliVersion: this.cliRuntimeVersion,
|
|
16258
16352
|
cliPackageName: packageJson.name,
|
|
@@ -16489,6 +16583,7 @@ var Daemon = class _Daemon {
|
|
|
16489
16583
|
await tunnelManager.initialize();
|
|
16490
16584
|
const moduleStatuses = [];
|
|
16491
16585
|
const expectedModuleUids = new Set(modules.map((m) => m.uid));
|
|
16586
|
+
const bareRepoPath = path37.join(projectPath, ".bare");
|
|
16492
16587
|
for (const mod of modules) {
|
|
16493
16588
|
const moduleUid = mod.uid;
|
|
16494
16589
|
const worktree = await getWorktreeInfoForModule(moduleUid);
|
|
@@ -16519,11 +16614,36 @@ var Daemon = class _Daemon {
|
|
|
16519
16614
|
if (orphanTunnels.length > 0) {
|
|
16520
16615
|
console.log(`[Daemon] EP1003: Reporting ${orphanTunnels.length} orphan tunnel(s) for server cleanup`);
|
|
16521
16616
|
}
|
|
16617
|
+
let unexpectedWorktrees;
|
|
16618
|
+
try {
|
|
16619
|
+
const listResult = await new import_core22.GitExecutor().execute(
|
|
16620
|
+
{ action: "worktree_list" },
|
|
16621
|
+
{ cwd: bareRepoPath }
|
|
16622
|
+
);
|
|
16623
|
+
if (listResult.success) {
|
|
16624
|
+
const localWorktrees = listResult.details?.worktrees || [];
|
|
16625
|
+
const discoveredUnexpectedWorktrees = collectUnexpectedWorktrees({
|
|
16626
|
+
worktrees: localWorktrees,
|
|
16627
|
+
expectedModuleUids,
|
|
16628
|
+
projectRoot: projectPath,
|
|
16629
|
+
bareRepoPath
|
|
16630
|
+
});
|
|
16631
|
+
if (discoveredUnexpectedWorktrees.length > 0) {
|
|
16632
|
+
unexpectedWorktrees = discoveredUnexpectedWorktrees;
|
|
16633
|
+
console.log(`[Daemon] EP1446: Reporting ${discoveredUnexpectedWorktrees.length} unexpected worktree(s) for recovery`);
|
|
16634
|
+
}
|
|
16635
|
+
} else {
|
|
16636
|
+
console.warn("[Daemon] EP1446: Failed to list local worktrees for reconciliation");
|
|
16637
|
+
}
|
|
16638
|
+
} catch (error) {
|
|
16639
|
+
console.warn("[Daemon] EP1446: Error while scanning local worktrees:", error instanceof Error ? error.message : error);
|
|
16640
|
+
}
|
|
16522
16641
|
const report = {
|
|
16523
16642
|
projectId,
|
|
16524
16643
|
machineId: this.machineUuid,
|
|
16525
16644
|
modules: moduleStatuses,
|
|
16526
|
-
orphanTunnels: orphanTunnels.length > 0 ? orphanTunnels : void 0
|
|
16645
|
+
orphanTunnels: orphanTunnels.length > 0 ? orphanTunnels : void 0,
|
|
16646
|
+
unexpectedWorktrees
|
|
16527
16647
|
};
|
|
16528
16648
|
console.log(`[Daemon] EP1003: Sending reconciliation report with ${moduleStatuses.length} module(s)`);
|
|
16529
16649
|
await client.send({
|
|
@@ -16770,7 +16890,7 @@ var Daemon = class _Daemon {
|
|
|
16770
16890
|
console.log(`[Daemon] EP1002: Worktree setup complete for ${moduleUid}`);
|
|
16771
16891
|
}
|
|
16772
16892
|
async runForegroundCommand(command, args, cwd, env, timeoutMs) {
|
|
16773
|
-
await new Promise((
|
|
16893
|
+
await new Promise((resolve8, reject) => {
|
|
16774
16894
|
const commandLabel = `${command} ${args.join(" ")}`.trim();
|
|
16775
16895
|
const child = (0, import_child_process19.spawn)(command, args, {
|
|
16776
16896
|
cwd,
|
|
@@ -16793,7 +16913,7 @@ var Daemon = class _Daemon {
|
|
|
16793
16913
|
reject(error);
|
|
16794
16914
|
return;
|
|
16795
16915
|
}
|
|
16796
|
-
|
|
16916
|
+
resolve8();
|
|
16797
16917
|
};
|
|
16798
16918
|
termTimer = setTimeout(() => {
|
|
16799
16919
|
timedOut = true;
|
|
@@ -16906,8 +17026,8 @@ var Daemon = class _Daemon {
|
|
|
16906
17026
|
await this.shutdown();
|
|
16907
17027
|
try {
|
|
16908
17028
|
const pidPath = getPidFilePath();
|
|
16909
|
-
if (
|
|
16910
|
-
|
|
17029
|
+
if (fs35.existsSync(pidPath)) {
|
|
17030
|
+
fs35.unlinkSync(pidPath);
|
|
16911
17031
|
console.log("[Daemon] PID file cleaned up");
|
|
16912
17032
|
}
|
|
16913
17033
|
} catch (error) {
|