episoda 0.2.50 → 0.2.52
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 +203 -231
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1563,15 +1563,15 @@ var require_git_executor = __commonJS({
|
|
|
1563
1563
|
try {
|
|
1564
1564
|
const { stdout: gitDir } = await execAsync2("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1565
1565
|
const gitDirPath = gitDir.trim();
|
|
1566
|
-
const
|
|
1566
|
+
const fs17 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1567
1567
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1568
1568
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1569
1569
|
try {
|
|
1570
|
-
await
|
|
1570
|
+
await fs17.access(rebaseMergePath);
|
|
1571
1571
|
inRebase = true;
|
|
1572
1572
|
} catch {
|
|
1573
1573
|
try {
|
|
1574
|
-
await
|
|
1574
|
+
await fs17.access(rebaseApplyPath);
|
|
1575
1575
|
inRebase = true;
|
|
1576
1576
|
} catch {
|
|
1577
1577
|
inRebase = false;
|
|
@@ -1625,9 +1625,9 @@ var require_git_executor = __commonJS({
|
|
|
1625
1625
|
error: validation.error || "UNKNOWN_ERROR"
|
|
1626
1626
|
};
|
|
1627
1627
|
}
|
|
1628
|
-
const
|
|
1628
|
+
const fs17 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1629
1629
|
try {
|
|
1630
|
-
await
|
|
1630
|
+
await fs17.access(command.path);
|
|
1631
1631
|
return {
|
|
1632
1632
|
success: false,
|
|
1633
1633
|
error: "WORKTREE_EXISTS",
|
|
@@ -1681,9 +1681,9 @@ var require_git_executor = __commonJS({
|
|
|
1681
1681
|
*/
|
|
1682
1682
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1683
1683
|
try {
|
|
1684
|
-
const
|
|
1684
|
+
const fs17 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1685
1685
|
try {
|
|
1686
|
-
await
|
|
1686
|
+
await fs17.access(command.path);
|
|
1687
1687
|
} catch {
|
|
1688
1688
|
return {
|
|
1689
1689
|
success: false,
|
|
@@ -1718,7 +1718,7 @@ var require_git_executor = __commonJS({
|
|
|
1718
1718
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1719
1719
|
if (result.success) {
|
|
1720
1720
|
try {
|
|
1721
|
-
await
|
|
1721
|
+
await fs17.rm(command.path, { recursive: true, force: true });
|
|
1722
1722
|
} catch {
|
|
1723
1723
|
}
|
|
1724
1724
|
return {
|
|
@@ -1852,10 +1852,10 @@ var require_git_executor = __commonJS({
|
|
|
1852
1852
|
*/
|
|
1853
1853
|
async executeCloneBare(command, options) {
|
|
1854
1854
|
try {
|
|
1855
|
-
const
|
|
1856
|
-
const
|
|
1855
|
+
const fs17 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1856
|
+
const path18 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1857
1857
|
try {
|
|
1858
|
-
await
|
|
1858
|
+
await fs17.access(command.path);
|
|
1859
1859
|
return {
|
|
1860
1860
|
success: false,
|
|
1861
1861
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1864,9 +1864,9 @@ var require_git_executor = __commonJS({
|
|
|
1864
1864
|
};
|
|
1865
1865
|
} catch {
|
|
1866
1866
|
}
|
|
1867
|
-
const parentDir =
|
|
1867
|
+
const parentDir = path18.dirname(command.path);
|
|
1868
1868
|
try {
|
|
1869
|
-
await
|
|
1869
|
+
await fs17.mkdir(parentDir, { recursive: true });
|
|
1870
1870
|
} catch {
|
|
1871
1871
|
}
|
|
1872
1872
|
const { stdout, stderr } = await execAsync2(
|
|
@@ -1914,22 +1914,22 @@ var require_git_executor = __commonJS({
|
|
|
1914
1914
|
*/
|
|
1915
1915
|
async executeProjectInfo(cwd, options) {
|
|
1916
1916
|
try {
|
|
1917
|
-
const
|
|
1918
|
-
const
|
|
1917
|
+
const fs17 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1918
|
+
const path18 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1919
1919
|
let currentPath = cwd;
|
|
1920
1920
|
let projectPath = cwd;
|
|
1921
1921
|
let bareRepoPath;
|
|
1922
1922
|
for (let i = 0; i < 10; i++) {
|
|
1923
|
-
const bareDir =
|
|
1924
|
-
const episodaDir =
|
|
1923
|
+
const bareDir = path18.join(currentPath, ".bare");
|
|
1924
|
+
const episodaDir = path18.join(currentPath, ".episoda");
|
|
1925
1925
|
try {
|
|
1926
|
-
await
|
|
1927
|
-
await
|
|
1926
|
+
await fs17.access(bareDir);
|
|
1927
|
+
await fs17.access(episodaDir);
|
|
1928
1928
|
projectPath = currentPath;
|
|
1929
1929
|
bareRepoPath = bareDir;
|
|
1930
1930
|
break;
|
|
1931
1931
|
} catch {
|
|
1932
|
-
const parentPath =
|
|
1932
|
+
const parentPath = path18.dirname(currentPath);
|
|
1933
1933
|
if (parentPath === currentPath) {
|
|
1934
1934
|
break;
|
|
1935
1935
|
}
|
|
@@ -2533,36 +2533,36 @@ var require_auth = __commonJS({
|
|
|
2533
2533
|
};
|
|
2534
2534
|
})();
|
|
2535
2535
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2536
|
-
exports2.getConfigDir =
|
|
2536
|
+
exports2.getConfigDir = getConfigDir8;
|
|
2537
2537
|
exports2.getConfigPath = getConfigPath;
|
|
2538
2538
|
exports2.loadConfig = loadConfig7;
|
|
2539
2539
|
exports2.saveConfig = saveConfig2;
|
|
2540
2540
|
exports2.validateToken = validateToken;
|
|
2541
|
-
var
|
|
2542
|
-
var
|
|
2541
|
+
var fs17 = __importStar(require("fs"));
|
|
2542
|
+
var path18 = __importStar(require("path"));
|
|
2543
2543
|
var os7 = __importStar(require("os"));
|
|
2544
2544
|
var child_process_1 = require("child_process");
|
|
2545
2545
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2546
|
-
function
|
|
2547
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2546
|
+
function getConfigDir8() {
|
|
2547
|
+
return process.env.EPISODA_CONFIG_DIR || path18.join(os7.homedir(), ".episoda");
|
|
2548
2548
|
}
|
|
2549
2549
|
function getConfigPath(configPath) {
|
|
2550
2550
|
if (configPath) {
|
|
2551
2551
|
return configPath;
|
|
2552
2552
|
}
|
|
2553
|
-
return
|
|
2553
|
+
return path18.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
|
|
2554
2554
|
}
|
|
2555
2555
|
function ensureConfigDir(configPath) {
|
|
2556
|
-
const dir =
|
|
2557
|
-
const isNew = !
|
|
2556
|
+
const dir = path18.dirname(configPath);
|
|
2557
|
+
const isNew = !fs17.existsSync(dir);
|
|
2558
2558
|
if (isNew) {
|
|
2559
|
-
|
|
2559
|
+
fs17.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2560
2560
|
}
|
|
2561
2561
|
if (process.platform === "darwin") {
|
|
2562
|
-
const nosyncPath =
|
|
2563
|
-
if (isNew || !
|
|
2562
|
+
const nosyncPath = path18.join(dir, ".nosync");
|
|
2563
|
+
if (isNew || !fs17.existsSync(nosyncPath)) {
|
|
2564
2564
|
try {
|
|
2565
|
-
|
|
2565
|
+
fs17.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2566
2566
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2567
2567
|
stdio: "ignore",
|
|
2568
2568
|
timeout: 5e3
|
|
@@ -2574,9 +2574,9 @@ var require_auth = __commonJS({
|
|
|
2574
2574
|
}
|
|
2575
2575
|
async function loadConfig7(configPath) {
|
|
2576
2576
|
const fullPath = getConfigPath(configPath);
|
|
2577
|
-
if (
|
|
2577
|
+
if (fs17.existsSync(fullPath)) {
|
|
2578
2578
|
try {
|
|
2579
|
-
const content =
|
|
2579
|
+
const content = fs17.readFileSync(fullPath, "utf8");
|
|
2580
2580
|
const config = JSON.parse(content);
|
|
2581
2581
|
return config;
|
|
2582
2582
|
} catch (error) {
|
|
@@ -2601,7 +2601,7 @@ var require_auth = __commonJS({
|
|
|
2601
2601
|
ensureConfigDir(fullPath);
|
|
2602
2602
|
try {
|
|
2603
2603
|
const content = JSON.stringify(config, null, 2);
|
|
2604
|
-
|
|
2604
|
+
fs17.writeFileSync(fullPath, content, { mode: 384 });
|
|
2605
2605
|
} catch (error) {
|
|
2606
2606
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2607
2607
|
}
|
|
@@ -2718,7 +2718,7 @@ var require_package = __commonJS({
|
|
|
2718
2718
|
"package.json"(exports2, module2) {
|
|
2719
2719
|
module2.exports = {
|
|
2720
2720
|
name: "episoda",
|
|
2721
|
-
version: "0.2.
|
|
2721
|
+
version: "0.2.52",
|
|
2722
2722
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2723
2723
|
main: "dist/index.js",
|
|
2724
2724
|
types: "dist/index.d.ts",
|
|
@@ -3095,11 +3095,16 @@ var IPCServer = class {
|
|
|
3095
3095
|
};
|
|
3096
3096
|
|
|
3097
3097
|
// src/daemon/daemon-process.ts
|
|
3098
|
-
var
|
|
3098
|
+
var import_core12 = __toESM(require_dist());
|
|
3099
3099
|
|
|
3100
3100
|
// src/utils/update-checker.ts
|
|
3101
3101
|
var import_child_process2 = require("child_process");
|
|
3102
3102
|
var semver = __toESM(require("semver"));
|
|
3103
|
+
|
|
3104
|
+
// src/ipc/ipc-client.ts
|
|
3105
|
+
var import_core5 = __toESM(require_dist());
|
|
3106
|
+
|
|
3107
|
+
// src/utils/update-checker.ts
|
|
3103
3108
|
var PACKAGE_NAME = "episoda";
|
|
3104
3109
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
3105
3110
|
async function checkForUpdates(currentVersion) {
|
|
@@ -3758,12 +3763,12 @@ async function handleExec(command, projectPath) {
|
|
|
3758
3763
|
// src/daemon/handlers/stale-commit-cleanup.ts
|
|
3759
3764
|
var import_child_process4 = require("child_process");
|
|
3760
3765
|
var import_util = require("util");
|
|
3761
|
-
var
|
|
3766
|
+
var import_core6 = __toESM(require_dist());
|
|
3762
3767
|
var execAsync = (0, import_util.promisify)(import_child_process4.exec);
|
|
3763
3768
|
async function cleanupStaleCommits(projectPath) {
|
|
3764
3769
|
try {
|
|
3765
3770
|
const machineId = await getMachineId();
|
|
3766
|
-
const config = await (0,
|
|
3771
|
+
const config = await (0, import_core6.loadConfig)();
|
|
3767
3772
|
if (!config?.access_token) {
|
|
3768
3773
|
return {
|
|
3769
3774
|
success: false,
|
|
@@ -4013,14 +4018,16 @@ var path7 = __toESM(require("path"));
|
|
|
4013
4018
|
var os2 = __toESM(require("os"));
|
|
4014
4019
|
|
|
4015
4020
|
// src/tunnel/tunnel-api.ts
|
|
4016
|
-
var
|
|
4021
|
+
var import_core7 = __toESM(require_dist());
|
|
4017
4022
|
async function provisionNamedTunnel(moduleId, port = 3e3) {
|
|
4018
|
-
|
|
4023
|
+
console.log(`[TunnelAPI] EP1038: provisionNamedTunnel called for moduleId ${moduleId} with port ${port}`);
|
|
4024
|
+
const config = await (0, import_core7.loadConfig)();
|
|
4019
4025
|
if (!config?.access_token) {
|
|
4020
4026
|
return { success: false, error: "Not authenticated" };
|
|
4021
4027
|
}
|
|
4022
4028
|
try {
|
|
4023
4029
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
4030
|
+
console.log(`[TunnelAPI] EP1038: POSTing to ${apiUrl}/api/tunnels with port=${port}`);
|
|
4024
4031
|
const response = await fetch(`${apiUrl}/api/tunnels`, {
|
|
4025
4032
|
method: "POST",
|
|
4026
4033
|
headers: {
|
|
@@ -4050,7 +4057,8 @@ async function provisionNamedTunnel(moduleId, port = 3e3) {
|
|
|
4050
4057
|
}
|
|
4051
4058
|
}
|
|
4052
4059
|
async function provisionNamedTunnelByUid(moduleUid, port = 3e3) {
|
|
4053
|
-
|
|
4060
|
+
console.log(`[TunnelAPI] EP1038: provisionNamedTunnelByUid called for ${moduleUid} with port ${port}`);
|
|
4061
|
+
const config = await (0, import_core7.loadConfig)();
|
|
4054
4062
|
if (!config?.access_token) {
|
|
4055
4063
|
return { success: false, error: "Not authenticated" };
|
|
4056
4064
|
}
|
|
@@ -4087,7 +4095,7 @@ async function updateTunnelStatus(moduleUid, status, error) {
|
|
|
4087
4095
|
if (!moduleUid || moduleUid === "LOCAL") {
|
|
4088
4096
|
return;
|
|
4089
4097
|
}
|
|
4090
|
-
const config = await (0,
|
|
4098
|
+
const config = await (0, import_core7.loadConfig)();
|
|
4091
4099
|
if (!config?.access_token) {
|
|
4092
4100
|
return;
|
|
4093
4101
|
}
|
|
@@ -4112,7 +4120,7 @@ async function clearTunnelUrl(moduleUid) {
|
|
|
4112
4120
|
if (!moduleUid || moduleUid === "LOCAL") {
|
|
4113
4121
|
return;
|
|
4114
4122
|
}
|
|
4115
|
-
const config = await (0,
|
|
4123
|
+
const config = await (0, import_core7.loadConfig)();
|
|
4116
4124
|
if (!config?.access_token) {
|
|
4117
4125
|
return;
|
|
4118
4126
|
}
|
|
@@ -5269,7 +5277,7 @@ var http = __toESM(require("http"));
|
|
|
5269
5277
|
var fs11 = __toESM(require("fs"));
|
|
5270
5278
|
var path12 = __toESM(require("path"));
|
|
5271
5279
|
var import_events2 = require("events");
|
|
5272
|
-
var
|
|
5280
|
+
var import_core8 = __toESM(require_dist());
|
|
5273
5281
|
|
|
5274
5282
|
// src/utils/port-check.ts
|
|
5275
5283
|
var net2 = __toESM(require("net"));
|
|
@@ -5636,7 +5644,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
5636
5644
|
// ============ Private Methods ============
|
|
5637
5645
|
async fetchEnvVars(projectPath) {
|
|
5638
5646
|
try {
|
|
5639
|
-
const config = await (0,
|
|
5647
|
+
const config = await (0, import_core8.loadConfig)();
|
|
5640
5648
|
if (!config?.access_token || !config?.project_id) {
|
|
5641
5649
|
return {};
|
|
5642
5650
|
}
|
|
@@ -5796,7 +5804,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
5796
5804
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5797
5805
|
}
|
|
5798
5806
|
getLogsDir() {
|
|
5799
|
-
const logsDir = path12.join((0,
|
|
5807
|
+
const logsDir = path12.join((0, import_core8.getConfigDir)(), "logs");
|
|
5800
5808
|
if (!fs11.existsSync(logsDir)) {
|
|
5801
5809
|
fs11.mkdirSync(logsDir, { recursive: true });
|
|
5802
5810
|
}
|
|
@@ -5852,6 +5860,48 @@ function getDevServerRunner() {
|
|
|
5852
5860
|
return instance2;
|
|
5853
5861
|
}
|
|
5854
5862
|
|
|
5863
|
+
// src/utils/port-allocator.ts
|
|
5864
|
+
var PORT_RANGE_START = 3100;
|
|
5865
|
+
var PORT_RANGE_END = 3199;
|
|
5866
|
+
var PORT_WARNING_THRESHOLD = 80;
|
|
5867
|
+
var portAssignments = /* @__PURE__ */ new Map();
|
|
5868
|
+
function allocatePort(moduleUid) {
|
|
5869
|
+
const existing = portAssignments.get(moduleUid);
|
|
5870
|
+
if (existing) {
|
|
5871
|
+
return existing;
|
|
5872
|
+
}
|
|
5873
|
+
const usedPorts = new Set(portAssignments.values());
|
|
5874
|
+
if (usedPorts.size >= PORT_WARNING_THRESHOLD) {
|
|
5875
|
+
console.warn(
|
|
5876
|
+
`[PortAllocator] Warning: ${usedPorts.size}/${PORT_RANGE_END - PORT_RANGE_START + 1} ports allocated`
|
|
5877
|
+
);
|
|
5878
|
+
}
|
|
5879
|
+
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
5880
|
+
if (!usedPorts.has(port)) {
|
|
5881
|
+
portAssignments.set(moduleUid, port);
|
|
5882
|
+
console.log(`[PortAllocator] Assigned port ${port} to ${moduleUid}`);
|
|
5883
|
+
return port;
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
throw new Error(
|
|
5887
|
+
`No available ports in range ${PORT_RANGE_START}-${PORT_RANGE_END}. ${portAssignments.size} modules are using all available ports.`
|
|
5888
|
+
);
|
|
5889
|
+
}
|
|
5890
|
+
function releasePort(moduleUid) {
|
|
5891
|
+
const port = portAssignments.get(moduleUid);
|
|
5892
|
+
if (port) {
|
|
5893
|
+
portAssignments.delete(moduleUid);
|
|
5894
|
+
console.log(`[PortAllocator] Released port ${port} from ${moduleUid}`);
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
function clearAllPorts() {
|
|
5898
|
+
const count = portAssignments.size;
|
|
5899
|
+
portAssignments.clear();
|
|
5900
|
+
if (count > 0) {
|
|
5901
|
+
console.log(`[PortAllocator] Cleared ${count} port assignments`);
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5855
5905
|
// src/preview/preview-manager.ts
|
|
5856
5906
|
var DEFAULT_PORT = 3e3;
|
|
5857
5907
|
var PreviewManager = class extends import_events3.EventEmitter {
|
|
@@ -6064,6 +6114,7 @@ var PreviewManager = class extends import_events3.EventEmitter {
|
|
|
6064
6114
|
} catch (error) {
|
|
6065
6115
|
console.warn(`[PreviewManager] Error clearing tunnel URL for ${moduleUid}:`, error);
|
|
6066
6116
|
}
|
|
6117
|
+
releasePort(moduleUid);
|
|
6067
6118
|
if (state) {
|
|
6068
6119
|
state.state = "stopped";
|
|
6069
6120
|
state.tunnelUrl = void 0;
|
|
@@ -6242,7 +6293,7 @@ function getPreviewManager() {
|
|
|
6242
6293
|
|
|
6243
6294
|
// src/utils/dev-server.ts
|
|
6244
6295
|
var import_child_process10 = require("child_process");
|
|
6245
|
-
var
|
|
6296
|
+
var import_core9 = __toESM(require_dist());
|
|
6246
6297
|
var fs12 = __toESM(require("fs"));
|
|
6247
6298
|
var path13 = __toESM(require("path"));
|
|
6248
6299
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
@@ -6252,7 +6303,7 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
|
6252
6303
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
6253
6304
|
var activeServers = /* @__PURE__ */ new Map();
|
|
6254
6305
|
function getLogsDir() {
|
|
6255
|
-
const logsDir = path13.join((0,
|
|
6306
|
+
const logsDir = path13.join((0, import_core9.getConfigDir)(), "logs");
|
|
6256
6307
|
if (!fs12.existsSync(logsDir)) {
|
|
6257
6308
|
fs12.mkdirSync(logsDir, { recursive: true });
|
|
6258
6309
|
}
|
|
@@ -6453,7 +6504,7 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
6453
6504
|
console.log(`[DevServer] EP932: Starting dev server for ${moduleUid} on port ${port} (auto-restart: ${autoRestart})...`);
|
|
6454
6505
|
let injectedEnvVars = {};
|
|
6455
6506
|
try {
|
|
6456
|
-
const config = await (0,
|
|
6507
|
+
const config = await (0, import_core9.loadConfig)();
|
|
6457
6508
|
if (config?.access_token && config?.project_id) {
|
|
6458
6509
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
6459
6510
|
const result = await fetchEnvVarsWithCache(apiUrl, config.access_token, {
|
|
@@ -6569,81 +6620,10 @@ function getDevServerStatus() {
|
|
|
6569
6620
|
}));
|
|
6570
6621
|
}
|
|
6571
6622
|
|
|
6572
|
-
// src/
|
|
6623
|
+
// src/daemon/worktree-manager.ts
|
|
6573
6624
|
var fs13 = __toESM(require("fs"));
|
|
6574
6625
|
var path14 = __toESM(require("path"));
|
|
6575
|
-
var
|
|
6576
|
-
function detectDevPort(projectPath) {
|
|
6577
|
-
const envPort = getPortFromEnv(projectPath);
|
|
6578
|
-
if (envPort) {
|
|
6579
|
-
console.log(`[PortDetect] Found PORT=${envPort} in .env`);
|
|
6580
|
-
return envPort;
|
|
6581
|
-
}
|
|
6582
|
-
const scriptPort = getPortFromPackageJson(projectPath);
|
|
6583
|
-
if (scriptPort) {
|
|
6584
|
-
console.log(`[PortDetect] Found port ${scriptPort} in package.json dev script`);
|
|
6585
|
-
return scriptPort;
|
|
6586
|
-
}
|
|
6587
|
-
console.log(`[PortDetect] Using default port ${DEFAULT_PORT2}`);
|
|
6588
|
-
return DEFAULT_PORT2;
|
|
6589
|
-
}
|
|
6590
|
-
function getPortFromEnv(projectPath) {
|
|
6591
|
-
const envPaths = [
|
|
6592
|
-
path14.join(projectPath, ".env"),
|
|
6593
|
-
path14.join(projectPath, ".env.local"),
|
|
6594
|
-
path14.join(projectPath, ".env.development"),
|
|
6595
|
-
path14.join(projectPath, ".env.development.local")
|
|
6596
|
-
];
|
|
6597
|
-
for (const envPath of envPaths) {
|
|
6598
|
-
try {
|
|
6599
|
-
if (!fs13.existsSync(envPath)) continue;
|
|
6600
|
-
const content = fs13.readFileSync(envPath, "utf-8");
|
|
6601
|
-
const lines = content.split("\n");
|
|
6602
|
-
for (const line of lines) {
|
|
6603
|
-
const match = line.match(/^\s*PORT\s*=\s*["']?(\d+)["']?\s*(?:#.*)?$/);
|
|
6604
|
-
if (match) {
|
|
6605
|
-
const port = parseInt(match[1], 10);
|
|
6606
|
-
if (port > 0 && port < 65536) {
|
|
6607
|
-
return port;
|
|
6608
|
-
}
|
|
6609
|
-
}
|
|
6610
|
-
}
|
|
6611
|
-
} catch {
|
|
6612
|
-
}
|
|
6613
|
-
}
|
|
6614
|
-
return null;
|
|
6615
|
-
}
|
|
6616
|
-
function getPortFromPackageJson(projectPath) {
|
|
6617
|
-
const packageJsonPath = path14.join(projectPath, "package.json");
|
|
6618
|
-
try {
|
|
6619
|
-
if (!fs13.existsSync(packageJsonPath)) return null;
|
|
6620
|
-
const content = fs13.readFileSync(packageJsonPath, "utf-8");
|
|
6621
|
-
const pkg = JSON.parse(content);
|
|
6622
|
-
const devScript = pkg.scripts?.dev;
|
|
6623
|
-
if (!devScript) return null;
|
|
6624
|
-
const portMatch = devScript.match(/(?:--port[=\s]|-p[=\s])(\d+)/);
|
|
6625
|
-
if (portMatch) {
|
|
6626
|
-
const port = parseInt(portMatch[1], 10);
|
|
6627
|
-
if (port > 0 && port < 65536) {
|
|
6628
|
-
return port;
|
|
6629
|
-
}
|
|
6630
|
-
}
|
|
6631
|
-
const envMatch = devScript.match(/PORT[=\s](\d+)/);
|
|
6632
|
-
if (envMatch) {
|
|
6633
|
-
const port = parseInt(envMatch[1], 10);
|
|
6634
|
-
if (port > 0 && port < 65536) {
|
|
6635
|
-
return port;
|
|
6636
|
-
}
|
|
6637
|
-
}
|
|
6638
|
-
} catch {
|
|
6639
|
-
}
|
|
6640
|
-
return null;
|
|
6641
|
-
}
|
|
6642
|
-
|
|
6643
|
-
// src/daemon/worktree-manager.ts
|
|
6644
|
-
var fs14 = __toESM(require("fs"));
|
|
6645
|
-
var path15 = __toESM(require("path"));
|
|
6646
|
-
var import_core9 = __toESM(require_dist());
|
|
6626
|
+
var import_core10 = __toESM(require_dist());
|
|
6647
6627
|
function validateModuleUid(moduleUid) {
|
|
6648
6628
|
if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
|
|
6649
6629
|
return false;
|
|
@@ -6666,9 +6646,9 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
6666
6646
|
// ============================================================
|
|
6667
6647
|
this.lockPath = "";
|
|
6668
6648
|
this.projectRoot = projectRoot;
|
|
6669
|
-
this.bareRepoPath =
|
|
6670
|
-
this.configPath =
|
|
6671
|
-
this.gitExecutor = new
|
|
6649
|
+
this.bareRepoPath = path14.join(projectRoot, ".bare");
|
|
6650
|
+
this.configPath = path14.join(projectRoot, ".episoda", "config.json");
|
|
6651
|
+
this.gitExecutor = new import_core10.GitExecutor();
|
|
6672
6652
|
}
|
|
6673
6653
|
/**
|
|
6674
6654
|
* Initialize worktree manager from existing project root
|
|
@@ -6676,10 +6656,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
6676
6656
|
* @returns true if valid project, false otherwise
|
|
6677
6657
|
*/
|
|
6678
6658
|
async initialize() {
|
|
6679
|
-
if (!
|
|
6659
|
+
if (!fs13.existsSync(this.bareRepoPath)) {
|
|
6680
6660
|
return false;
|
|
6681
6661
|
}
|
|
6682
|
-
if (!
|
|
6662
|
+
if (!fs13.existsSync(this.configPath)) {
|
|
6683
6663
|
return false;
|
|
6684
6664
|
}
|
|
6685
6665
|
try {
|
|
@@ -6726,8 +6706,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
6726
6706
|
*/
|
|
6727
6707
|
static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
|
|
6728
6708
|
const manager = new _WorktreeManager(projectRoot);
|
|
6729
|
-
const episodaDir =
|
|
6730
|
-
|
|
6709
|
+
const episodaDir = path14.join(projectRoot, ".episoda");
|
|
6710
|
+
fs13.mkdirSync(episodaDir, { recursive: true });
|
|
6731
6711
|
const cloneResult = await manager.gitExecutor.execute({
|
|
6732
6712
|
action: "clone_bare",
|
|
6733
6713
|
url: repoUrl,
|
|
@@ -6758,7 +6738,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
6758
6738
|
error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
|
|
6759
6739
|
};
|
|
6760
6740
|
}
|
|
6761
|
-
const worktreePath =
|
|
6741
|
+
const worktreePath = path14.join(this.projectRoot, moduleUid);
|
|
6762
6742
|
const lockAcquired = await this.acquireLock();
|
|
6763
6743
|
if (!lockAcquired) {
|
|
6764
6744
|
return {
|
|
@@ -6940,7 +6920,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
6940
6920
|
let prunedCount = 0;
|
|
6941
6921
|
await this.updateConfigSafe((config) => {
|
|
6942
6922
|
const initialCount = config.worktrees.length;
|
|
6943
|
-
config.worktrees = config.worktrees.filter((w) =>
|
|
6923
|
+
config.worktrees = config.worktrees.filter((w) => fs13.existsSync(w.worktreePath));
|
|
6944
6924
|
prunedCount = initialCount - config.worktrees.length;
|
|
6945
6925
|
return config;
|
|
6946
6926
|
});
|
|
@@ -7021,16 +7001,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7021
7001
|
const retryInterval = 50;
|
|
7022
7002
|
while (Date.now() - startTime < timeoutMs) {
|
|
7023
7003
|
try {
|
|
7024
|
-
|
|
7004
|
+
fs13.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
7025
7005
|
return true;
|
|
7026
7006
|
} catch (err) {
|
|
7027
7007
|
if (err.code === "EEXIST") {
|
|
7028
7008
|
try {
|
|
7029
|
-
const stats =
|
|
7009
|
+
const stats = fs13.statSync(lockPath);
|
|
7030
7010
|
const lockAge = Date.now() - stats.mtimeMs;
|
|
7031
7011
|
if (lockAge > 3e4) {
|
|
7032
7012
|
try {
|
|
7033
|
-
const lockContent =
|
|
7013
|
+
const lockContent = fs13.readFileSync(lockPath, "utf-8").trim();
|
|
7034
7014
|
const lockPid = parseInt(lockContent, 10);
|
|
7035
7015
|
if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
|
|
7036
7016
|
await new Promise((resolve3) => setTimeout(resolve3, retryInterval));
|
|
@@ -7039,7 +7019,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7039
7019
|
} catch {
|
|
7040
7020
|
}
|
|
7041
7021
|
try {
|
|
7042
|
-
|
|
7022
|
+
fs13.unlinkSync(lockPath);
|
|
7043
7023
|
} catch {
|
|
7044
7024
|
}
|
|
7045
7025
|
continue;
|
|
@@ -7060,16 +7040,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7060
7040
|
*/
|
|
7061
7041
|
releaseLock() {
|
|
7062
7042
|
try {
|
|
7063
|
-
|
|
7043
|
+
fs13.unlinkSync(this.getLockPath());
|
|
7064
7044
|
} catch {
|
|
7065
7045
|
}
|
|
7066
7046
|
}
|
|
7067
7047
|
readConfig() {
|
|
7068
7048
|
try {
|
|
7069
|
-
if (!
|
|
7049
|
+
if (!fs13.existsSync(this.configPath)) {
|
|
7070
7050
|
return null;
|
|
7071
7051
|
}
|
|
7072
|
-
const content =
|
|
7052
|
+
const content = fs13.readFileSync(this.configPath, "utf-8");
|
|
7073
7053
|
return JSON.parse(content);
|
|
7074
7054
|
} catch (error) {
|
|
7075
7055
|
console.error("[WorktreeManager] Failed to read config:", error);
|
|
@@ -7078,11 +7058,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7078
7058
|
}
|
|
7079
7059
|
writeConfig(config) {
|
|
7080
7060
|
try {
|
|
7081
|
-
const dir =
|
|
7082
|
-
if (!
|
|
7083
|
-
|
|
7061
|
+
const dir = path14.dirname(this.configPath);
|
|
7062
|
+
if (!fs13.existsSync(dir)) {
|
|
7063
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
7084
7064
|
}
|
|
7085
|
-
|
|
7065
|
+
fs13.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
7086
7066
|
} catch (error) {
|
|
7087
7067
|
console.error("[WorktreeManager] Failed to write config:", error);
|
|
7088
7068
|
throw error;
|
|
@@ -7163,14 +7143,14 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7163
7143
|
}
|
|
7164
7144
|
try {
|
|
7165
7145
|
for (const file of files) {
|
|
7166
|
-
const srcPath =
|
|
7167
|
-
const destPath =
|
|
7168
|
-
if (
|
|
7169
|
-
const destDir =
|
|
7170
|
-
if (!
|
|
7171
|
-
|
|
7172
|
-
}
|
|
7173
|
-
|
|
7146
|
+
const srcPath = path14.join(mainWorktree.worktreePath, file);
|
|
7147
|
+
const destPath = path14.join(worktree.worktreePath, file);
|
|
7148
|
+
if (fs13.existsSync(srcPath)) {
|
|
7149
|
+
const destDir = path14.dirname(destPath);
|
|
7150
|
+
if (!fs13.existsSync(destDir)) {
|
|
7151
|
+
fs13.mkdirSync(destDir, { recursive: true });
|
|
7152
|
+
}
|
|
7153
|
+
fs13.copyFileSync(srcPath, destPath);
|
|
7174
7154
|
console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`);
|
|
7175
7155
|
} else {
|
|
7176
7156
|
console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`);
|
|
@@ -7253,27 +7233,27 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7253
7233
|
}
|
|
7254
7234
|
};
|
|
7255
7235
|
function getEpisodaRoot() {
|
|
7256
|
-
return process.env.EPISODA_ROOT ||
|
|
7236
|
+
return process.env.EPISODA_ROOT || path14.join(require("os").homedir(), "episoda");
|
|
7257
7237
|
}
|
|
7258
7238
|
async function isWorktreeProject(projectRoot) {
|
|
7259
7239
|
const manager = new WorktreeManager(projectRoot);
|
|
7260
7240
|
return manager.initialize();
|
|
7261
7241
|
}
|
|
7262
7242
|
async function findProjectRoot(startPath) {
|
|
7263
|
-
let current =
|
|
7243
|
+
let current = path14.resolve(startPath);
|
|
7264
7244
|
const episodaRoot = getEpisodaRoot();
|
|
7265
7245
|
if (!current.startsWith(episodaRoot)) {
|
|
7266
7246
|
return null;
|
|
7267
7247
|
}
|
|
7268
7248
|
for (let i = 0; i < 10; i++) {
|
|
7269
|
-
const bareDir =
|
|
7270
|
-
const episodaDir =
|
|
7271
|
-
if (
|
|
7249
|
+
const bareDir = path14.join(current, ".bare");
|
|
7250
|
+
const episodaDir = path14.join(current, ".episoda");
|
|
7251
|
+
if (fs13.existsSync(bareDir) && fs13.existsSync(episodaDir)) {
|
|
7272
7252
|
if (await isWorktreeProject(current)) {
|
|
7273
7253
|
return current;
|
|
7274
7254
|
}
|
|
7275
7255
|
}
|
|
7276
|
-
const parent =
|
|
7256
|
+
const parent = path14.dirname(current);
|
|
7277
7257
|
if (parent === current) {
|
|
7278
7258
|
break;
|
|
7279
7259
|
}
|
|
@@ -7283,24 +7263,24 @@ async function findProjectRoot(startPath) {
|
|
|
7283
7263
|
}
|
|
7284
7264
|
|
|
7285
7265
|
// src/utils/worktree.ts
|
|
7286
|
-
var
|
|
7287
|
-
var
|
|
7266
|
+
var path15 = __toESM(require("path"));
|
|
7267
|
+
var fs14 = __toESM(require("fs"));
|
|
7288
7268
|
var os5 = __toESM(require("os"));
|
|
7289
|
-
var
|
|
7269
|
+
var import_core11 = __toESM(require_dist());
|
|
7290
7270
|
function getEpisodaRoot2() {
|
|
7291
|
-
return process.env.EPISODA_ROOT ||
|
|
7271
|
+
return process.env.EPISODA_ROOT || path15.join(os5.homedir(), "episoda");
|
|
7292
7272
|
}
|
|
7293
7273
|
function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
|
|
7294
7274
|
const root = getEpisodaRoot2();
|
|
7295
|
-
const worktreePath =
|
|
7275
|
+
const worktreePath = path15.join(root, workspaceSlug, projectSlug, moduleUid);
|
|
7296
7276
|
return {
|
|
7297
7277
|
path: worktreePath,
|
|
7298
|
-
exists:
|
|
7278
|
+
exists: fs14.existsSync(worktreePath),
|
|
7299
7279
|
moduleUid
|
|
7300
7280
|
};
|
|
7301
7281
|
}
|
|
7302
7282
|
async function getWorktreeInfoForModule(moduleUid) {
|
|
7303
|
-
const config = await (0,
|
|
7283
|
+
const config = await (0, import_core11.loadConfig)();
|
|
7304
7284
|
if (!config?.workspace_slug || !config?.project_slug) {
|
|
7305
7285
|
console.warn("[Worktree] Missing workspace_slug or project_slug in config");
|
|
7306
7286
|
return null;
|
|
@@ -7308,72 +7288,62 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
7308
7288
|
return getWorktreeInfo(moduleUid, config.workspace_slug, config.project_slug);
|
|
7309
7289
|
}
|
|
7310
7290
|
|
|
7311
|
-
// src/utils/port-allocator.ts
|
|
7312
|
-
var portAssignments = /* @__PURE__ */ new Map();
|
|
7313
|
-
function clearAllPorts() {
|
|
7314
|
-
const count = portAssignments.size;
|
|
7315
|
-
portAssignments.clear();
|
|
7316
|
-
if (count > 0) {
|
|
7317
|
-
console.log(`[PortAllocator] Cleared ${count} port assignments`);
|
|
7318
|
-
}
|
|
7319
|
-
}
|
|
7320
|
-
|
|
7321
7291
|
// src/framework-detector.ts
|
|
7322
|
-
var
|
|
7323
|
-
var
|
|
7292
|
+
var fs15 = __toESM(require("fs"));
|
|
7293
|
+
var path16 = __toESM(require("path"));
|
|
7324
7294
|
function getInstallCommand(cwd) {
|
|
7325
|
-
if (
|
|
7295
|
+
if (fs15.existsSync(path16.join(cwd, "bun.lockb"))) {
|
|
7326
7296
|
return {
|
|
7327
7297
|
command: ["bun", "install"],
|
|
7328
7298
|
description: "Installing dependencies with bun",
|
|
7329
7299
|
detectedFrom: "bun.lockb"
|
|
7330
7300
|
};
|
|
7331
7301
|
}
|
|
7332
|
-
if (
|
|
7302
|
+
if (fs15.existsSync(path16.join(cwd, "pnpm-lock.yaml"))) {
|
|
7333
7303
|
return {
|
|
7334
7304
|
command: ["pnpm", "install"],
|
|
7335
7305
|
description: "Installing dependencies with pnpm",
|
|
7336
7306
|
detectedFrom: "pnpm-lock.yaml"
|
|
7337
7307
|
};
|
|
7338
7308
|
}
|
|
7339
|
-
if (
|
|
7309
|
+
if (fs15.existsSync(path16.join(cwd, "yarn.lock"))) {
|
|
7340
7310
|
return {
|
|
7341
7311
|
command: ["yarn", "install"],
|
|
7342
7312
|
description: "Installing dependencies with yarn",
|
|
7343
7313
|
detectedFrom: "yarn.lock"
|
|
7344
7314
|
};
|
|
7345
7315
|
}
|
|
7346
|
-
if (
|
|
7316
|
+
if (fs15.existsSync(path16.join(cwd, "package-lock.json"))) {
|
|
7347
7317
|
return {
|
|
7348
7318
|
command: ["npm", "ci"],
|
|
7349
7319
|
description: "Installing dependencies with npm ci",
|
|
7350
7320
|
detectedFrom: "package-lock.json"
|
|
7351
7321
|
};
|
|
7352
7322
|
}
|
|
7353
|
-
if (
|
|
7323
|
+
if (fs15.existsSync(path16.join(cwd, "package.json"))) {
|
|
7354
7324
|
return {
|
|
7355
7325
|
command: ["npm", "install"],
|
|
7356
7326
|
description: "Installing dependencies with npm",
|
|
7357
7327
|
detectedFrom: "package.json"
|
|
7358
7328
|
};
|
|
7359
7329
|
}
|
|
7360
|
-
if (
|
|
7330
|
+
if (fs15.existsSync(path16.join(cwd, "Pipfile.lock")) || fs15.existsSync(path16.join(cwd, "Pipfile"))) {
|
|
7361
7331
|
return {
|
|
7362
7332
|
command: ["pipenv", "install"],
|
|
7363
7333
|
description: "Installing dependencies with pipenv",
|
|
7364
|
-
detectedFrom:
|
|
7334
|
+
detectedFrom: fs15.existsSync(path16.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
|
|
7365
7335
|
};
|
|
7366
7336
|
}
|
|
7367
|
-
if (
|
|
7337
|
+
if (fs15.existsSync(path16.join(cwd, "poetry.lock"))) {
|
|
7368
7338
|
return {
|
|
7369
7339
|
command: ["poetry", "install"],
|
|
7370
7340
|
description: "Installing dependencies with poetry",
|
|
7371
7341
|
detectedFrom: "poetry.lock"
|
|
7372
7342
|
};
|
|
7373
7343
|
}
|
|
7374
|
-
if (
|
|
7375
|
-
const pyprojectPath =
|
|
7376
|
-
const content =
|
|
7344
|
+
if (fs15.existsSync(path16.join(cwd, "pyproject.toml"))) {
|
|
7345
|
+
const pyprojectPath = path16.join(cwd, "pyproject.toml");
|
|
7346
|
+
const content = fs15.readFileSync(pyprojectPath, "utf-8");
|
|
7377
7347
|
if (content.includes("[tool.poetry]")) {
|
|
7378
7348
|
return {
|
|
7379
7349
|
command: ["poetry", "install"],
|
|
@@ -7382,41 +7352,41 @@ function getInstallCommand(cwd) {
|
|
|
7382
7352
|
};
|
|
7383
7353
|
}
|
|
7384
7354
|
}
|
|
7385
|
-
if (
|
|
7355
|
+
if (fs15.existsSync(path16.join(cwd, "requirements.txt"))) {
|
|
7386
7356
|
return {
|
|
7387
7357
|
command: ["pip", "install", "-r", "requirements.txt"],
|
|
7388
7358
|
description: "Installing dependencies with pip",
|
|
7389
7359
|
detectedFrom: "requirements.txt"
|
|
7390
7360
|
};
|
|
7391
7361
|
}
|
|
7392
|
-
if (
|
|
7362
|
+
if (fs15.existsSync(path16.join(cwd, "Gemfile.lock")) || fs15.existsSync(path16.join(cwd, "Gemfile"))) {
|
|
7393
7363
|
return {
|
|
7394
7364
|
command: ["bundle", "install"],
|
|
7395
7365
|
description: "Installing dependencies with bundler",
|
|
7396
|
-
detectedFrom:
|
|
7366
|
+
detectedFrom: fs15.existsSync(path16.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
|
|
7397
7367
|
};
|
|
7398
7368
|
}
|
|
7399
|
-
if (
|
|
7369
|
+
if (fs15.existsSync(path16.join(cwd, "go.sum")) || fs15.existsSync(path16.join(cwd, "go.mod"))) {
|
|
7400
7370
|
return {
|
|
7401
7371
|
command: ["go", "mod", "download"],
|
|
7402
7372
|
description: "Downloading Go modules",
|
|
7403
|
-
detectedFrom:
|
|
7373
|
+
detectedFrom: fs15.existsSync(path16.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
|
|
7404
7374
|
};
|
|
7405
7375
|
}
|
|
7406
|
-
if (
|
|
7376
|
+
if (fs15.existsSync(path16.join(cwd, "Cargo.lock")) || fs15.existsSync(path16.join(cwd, "Cargo.toml"))) {
|
|
7407
7377
|
return {
|
|
7408
7378
|
command: ["cargo", "build"],
|
|
7409
7379
|
description: "Building Rust project (downloads dependencies)",
|
|
7410
|
-
detectedFrom:
|
|
7380
|
+
detectedFrom: fs15.existsSync(path16.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
|
|
7411
7381
|
};
|
|
7412
7382
|
}
|
|
7413
7383
|
return null;
|
|
7414
7384
|
}
|
|
7415
7385
|
|
|
7416
7386
|
// src/daemon/daemon-process.ts
|
|
7417
|
-
var
|
|
7387
|
+
var fs16 = __toESM(require("fs"));
|
|
7418
7388
|
var os6 = __toESM(require("os"));
|
|
7419
|
-
var
|
|
7389
|
+
var path17 = __toESM(require("path"));
|
|
7420
7390
|
var packageJson = require_package();
|
|
7421
7391
|
async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
7422
7392
|
const now = Date.now();
|
|
@@ -7451,7 +7421,7 @@ async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
|
7451
7421
|
refresh_token: tokenResponse.refresh_token || config.refresh_token,
|
|
7452
7422
|
expires_at: now + tokenResponse.expires_in * 1e3
|
|
7453
7423
|
};
|
|
7454
|
-
await (0,
|
|
7424
|
+
await (0, import_core12.saveConfig)(updatedConfig);
|
|
7455
7425
|
console.log("[Daemon] EP904: Access token refreshed successfully");
|
|
7456
7426
|
return updatedConfig;
|
|
7457
7427
|
} catch (error) {
|
|
@@ -7460,7 +7430,7 @@ async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
|
7460
7430
|
}
|
|
7461
7431
|
}
|
|
7462
7432
|
async function fetchWithAuth(url, options = {}, retryOnUnauthorized = true) {
|
|
7463
|
-
let config = await (0,
|
|
7433
|
+
let config = await (0, import_core12.loadConfig)();
|
|
7464
7434
|
if (!config?.access_token) {
|
|
7465
7435
|
throw new Error("No access token configured");
|
|
7466
7436
|
}
|
|
@@ -7487,7 +7457,7 @@ async function fetchWithAuth(url, options = {}, retryOnUnauthorized = true) {
|
|
|
7487
7457
|
}
|
|
7488
7458
|
async function fetchEnvVars2() {
|
|
7489
7459
|
try {
|
|
7490
|
-
const config = await (0,
|
|
7460
|
+
const config = await (0, import_core12.loadConfig)();
|
|
7491
7461
|
if (!config?.project_id) {
|
|
7492
7462
|
console.warn("[Daemon] EP973: No project_id in config, cannot fetch env vars");
|
|
7493
7463
|
return {};
|
|
@@ -7570,7 +7540,7 @@ var Daemon = class _Daemon {
|
|
|
7570
7540
|
console.log("[Daemon] Starting Episoda daemon...");
|
|
7571
7541
|
this.machineId = await getMachineId();
|
|
7572
7542
|
console.log(`[Daemon] Machine ID: ${this.machineId}`);
|
|
7573
|
-
const config = await (0,
|
|
7543
|
+
const config = await (0, import_core12.loadConfig)();
|
|
7574
7544
|
if (config?.device_id) {
|
|
7575
7545
|
this.deviceId = config.device_id;
|
|
7576
7546
|
console.log(`[Daemon] Loaded cached Device ID (UUID): ${this.deviceId}`);
|
|
@@ -7707,7 +7677,7 @@ var Daemon = class _Daemon {
|
|
|
7707
7677
|
};
|
|
7708
7678
|
});
|
|
7709
7679
|
this.ipcServer.on("verify-server-connection", async () => {
|
|
7710
|
-
const config = await (0,
|
|
7680
|
+
const config = await (0, import_core12.loadConfig)();
|
|
7711
7681
|
if (!config?.access_token || !config?.api_url) {
|
|
7712
7682
|
return {
|
|
7713
7683
|
verified: false,
|
|
@@ -7767,6 +7737,7 @@ var Daemon = class _Daemon {
|
|
|
7767
7737
|
}
|
|
7768
7738
|
await tunnelManager.stopTunnel(moduleUid);
|
|
7769
7739
|
await stopDevServer(moduleUid);
|
|
7740
|
+
releasePort(moduleUid);
|
|
7770
7741
|
await clearTunnelUrl(moduleUid);
|
|
7771
7742
|
this.tunnelHealthFailures.delete(moduleUid);
|
|
7772
7743
|
console.log(`[Daemon] EP823: Tunnel stopped for ${moduleUid}`);
|
|
@@ -7880,7 +7851,7 @@ var Daemon = class _Daemon {
|
|
|
7880
7851
|
console.warn(`[Daemon] Stale connection detected for ${projectPath}, forcing reconnection`);
|
|
7881
7852
|
await this.disconnectProject(projectPath);
|
|
7882
7853
|
}
|
|
7883
|
-
const config = await (0,
|
|
7854
|
+
const config = await (0, import_core12.loadConfig)();
|
|
7884
7855
|
if (!config || !config.access_token) {
|
|
7885
7856
|
throw new Error("No access token found. Please run: episoda auth");
|
|
7886
7857
|
}
|
|
@@ -7901,8 +7872,8 @@ var Daemon = class _Daemon {
|
|
|
7901
7872
|
wsUrl = `${wsProtocol}//${wsHostname}:${wsPort}`;
|
|
7902
7873
|
}
|
|
7903
7874
|
console.log(`[Daemon] Connecting to ${wsUrl} for project ${projectId}...`);
|
|
7904
|
-
const client = new
|
|
7905
|
-
const gitExecutor = new
|
|
7875
|
+
const client = new import_core12.EpisodaClient();
|
|
7876
|
+
const gitExecutor = new import_core12.GitExecutor();
|
|
7906
7877
|
const connection = {
|
|
7907
7878
|
projectId,
|
|
7908
7879
|
projectPath,
|
|
@@ -7917,7 +7888,7 @@ var Daemon = class _Daemon {
|
|
|
7917
7888
|
client.updateActivity();
|
|
7918
7889
|
try {
|
|
7919
7890
|
const gitCmd = message.command;
|
|
7920
|
-
const bareRepoPath =
|
|
7891
|
+
const bareRepoPath = path17.join(projectPath, ".bare");
|
|
7921
7892
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
7922
7893
|
if (gitCmd.worktreePath) {
|
|
7923
7894
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -8071,8 +8042,9 @@ var Daemon = class _Daemon {
|
|
|
8071
8042
|
return;
|
|
8072
8043
|
}
|
|
8073
8044
|
console.log(`[Daemon] EP1024: Using worktree path ${worktree.path} for ${cmd.moduleUid}`);
|
|
8074
|
-
const port = cmd.port ||
|
|
8075
|
-
|
|
8045
|
+
const port = cmd.port || allocatePort(cmd.moduleUid);
|
|
8046
|
+
console.log(`[Daemon] EP1038: Allocated port ${port} for ${cmd.moduleUid}`);
|
|
8047
|
+
const devConfig = await (0, import_core12.loadConfig)();
|
|
8076
8048
|
const customCommand = devConfig?.project_settings?.worktree_dev_server_script;
|
|
8077
8049
|
const startResult = await previewManager.startPreview({
|
|
8078
8050
|
moduleUid: cmd.moduleUid,
|
|
@@ -8332,8 +8304,8 @@ var Daemon = class _Daemon {
|
|
|
8332
8304
|
let daemonPid;
|
|
8333
8305
|
try {
|
|
8334
8306
|
const pidPath = getPidFilePath();
|
|
8335
|
-
if (
|
|
8336
|
-
const pidStr =
|
|
8307
|
+
if (fs16.existsSync(pidPath)) {
|
|
8308
|
+
const pidStr = fs16.readFileSync(pidPath, "utf-8").trim();
|
|
8337
8309
|
daemonPid = parseInt(pidStr, 10);
|
|
8338
8310
|
}
|
|
8339
8311
|
} catch (pidError) {
|
|
@@ -8454,27 +8426,27 @@ var Daemon = class _Daemon {
|
|
|
8454
8426
|
*/
|
|
8455
8427
|
async installGitHooks(projectPath) {
|
|
8456
8428
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
8457
|
-
const hooksDir =
|
|
8458
|
-
if (!
|
|
8429
|
+
const hooksDir = path17.join(projectPath, ".git", "hooks");
|
|
8430
|
+
if (!fs16.existsSync(hooksDir)) {
|
|
8459
8431
|
console.warn(`[Daemon] Hooks directory not found: ${hooksDir}`);
|
|
8460
8432
|
return;
|
|
8461
8433
|
}
|
|
8462
8434
|
for (const hookName of hooks) {
|
|
8463
8435
|
try {
|
|
8464
|
-
const hookPath =
|
|
8465
|
-
const bundledHookPath =
|
|
8466
|
-
if (!
|
|
8436
|
+
const hookPath = path17.join(hooksDir, hookName);
|
|
8437
|
+
const bundledHookPath = path17.join(__dirname, "..", "hooks", hookName);
|
|
8438
|
+
if (!fs16.existsSync(bundledHookPath)) {
|
|
8467
8439
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
8468
8440
|
continue;
|
|
8469
8441
|
}
|
|
8470
|
-
const hookContent =
|
|
8471
|
-
if (
|
|
8472
|
-
const existingContent =
|
|
8442
|
+
const hookContent = fs16.readFileSync(bundledHookPath, "utf-8");
|
|
8443
|
+
if (fs16.existsSync(hookPath)) {
|
|
8444
|
+
const existingContent = fs16.readFileSync(hookPath, "utf-8");
|
|
8473
8445
|
if (existingContent === hookContent) {
|
|
8474
8446
|
continue;
|
|
8475
8447
|
}
|
|
8476
8448
|
}
|
|
8477
|
-
|
|
8449
|
+
fs16.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
8478
8450
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
8479
8451
|
} catch (error) {
|
|
8480
8452
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -8489,7 +8461,7 @@ var Daemon = class _Daemon {
|
|
|
8489
8461
|
*/
|
|
8490
8462
|
async cacheDeviceId(deviceId) {
|
|
8491
8463
|
try {
|
|
8492
|
-
const config = await (0,
|
|
8464
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8493
8465
|
if (!config) {
|
|
8494
8466
|
console.warn("[Daemon] Cannot cache device ID - no config found");
|
|
8495
8467
|
return;
|
|
@@ -8502,7 +8474,7 @@ var Daemon = class _Daemon {
|
|
|
8502
8474
|
device_id: deviceId,
|
|
8503
8475
|
machine_id: this.machineId
|
|
8504
8476
|
};
|
|
8505
|
-
await (0,
|
|
8477
|
+
await (0, import_core12.saveConfig)(updatedConfig);
|
|
8506
8478
|
console.log(`[Daemon] Cached device ID to config: ${deviceId}`);
|
|
8507
8479
|
} catch (error) {
|
|
8508
8480
|
console.warn("[Daemon] Failed to cache device ID:", error instanceof Error ? error.message : error);
|
|
@@ -8516,7 +8488,7 @@ var Daemon = class _Daemon {
|
|
|
8516
8488
|
*/
|
|
8517
8489
|
async syncProjectSettings(projectId) {
|
|
8518
8490
|
try {
|
|
8519
|
-
const config = await (0,
|
|
8491
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8520
8492
|
if (!config) return;
|
|
8521
8493
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
8522
8494
|
const response = await fetchWithAuth(`${apiUrl}/api/projects/${projectId}/settings`);
|
|
@@ -8550,7 +8522,7 @@ var Daemon = class _Daemon {
|
|
|
8550
8522
|
cached_at: Date.now()
|
|
8551
8523
|
}
|
|
8552
8524
|
};
|
|
8553
|
-
await (0,
|
|
8525
|
+
await (0, import_core12.saveConfig)(updatedConfig);
|
|
8554
8526
|
console.log(`[Daemon] EP973: Project settings synced (slugs: ${projectSlug}/${workspaceSlug})`);
|
|
8555
8527
|
}
|
|
8556
8528
|
} catch (error) {
|
|
@@ -8570,7 +8542,7 @@ var Daemon = class _Daemon {
|
|
|
8570
8542
|
console.warn("[Daemon] EP995: Cannot sync project path - deviceId not available");
|
|
8571
8543
|
return;
|
|
8572
8544
|
}
|
|
8573
|
-
const config = await (0,
|
|
8545
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8574
8546
|
if (!config) return;
|
|
8575
8547
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
8576
8548
|
const response = await fetchWithAuth(`${apiUrl}/api/account/machines/${this.deviceId}`, {
|
|
@@ -8603,7 +8575,7 @@ var Daemon = class _Daemon {
|
|
|
8603
8575
|
*/
|
|
8604
8576
|
async updateModuleWorktreeStatus(moduleUid, status, worktreePath, errorMessage) {
|
|
8605
8577
|
try {
|
|
8606
|
-
const config = await (0,
|
|
8578
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8607
8579
|
if (!config) return;
|
|
8608
8580
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
8609
8581
|
const body = {
|
|
@@ -8658,7 +8630,7 @@ var Daemon = class _Daemon {
|
|
|
8658
8630
|
console.log("[Daemon] EP1003: Cannot reconcile - deviceId not available yet");
|
|
8659
8631
|
return;
|
|
8660
8632
|
}
|
|
8661
|
-
const config = await (0,
|
|
8633
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8662
8634
|
if (!config) return;
|
|
8663
8635
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
8664
8636
|
const controller = new AbortController();
|
|
@@ -8744,7 +8716,7 @@ var Daemon = class _Daemon {
|
|
|
8744
8716
|
try {
|
|
8745
8717
|
const envVars = await fetchEnvVars2();
|
|
8746
8718
|
console.log(`[Daemon] EP1002: Fetched ${Object.keys(envVars).length} env vars for ${moduleUid}`);
|
|
8747
|
-
const config = await (0,
|
|
8719
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8748
8720
|
const setupConfig = config?.project_settings;
|
|
8749
8721
|
await this.runWorktreeSetupSync(
|
|
8750
8722
|
moduleUid,
|
|
@@ -8922,7 +8894,7 @@ var Daemon = class _Daemon {
|
|
|
8922
8894
|
}
|
|
8923
8895
|
this.healthCheckInProgress = true;
|
|
8924
8896
|
try {
|
|
8925
|
-
const config = await (0,
|
|
8897
|
+
const config = await (0, import_core12.loadConfig)();
|
|
8926
8898
|
if (config?.access_token) {
|
|
8927
8899
|
await this.performHealthChecks(config);
|
|
8928
8900
|
}
|
|
@@ -9041,7 +9013,7 @@ var Daemon = class _Daemon {
|
|
|
9041
9013
|
*/
|
|
9042
9014
|
async fetchActiveModuleUids(projectId) {
|
|
9043
9015
|
try {
|
|
9044
|
-
const config = await (0,
|
|
9016
|
+
const config = await (0, import_core12.loadConfig)();
|
|
9045
9017
|
if (!config?.access_token || !config?.api_url) {
|
|
9046
9018
|
return null;
|
|
9047
9019
|
}
|
|
@@ -9144,7 +9116,7 @@ var Daemon = class _Daemon {
|
|
|
9144
9116
|
async restartTunnel(moduleUid, port) {
|
|
9145
9117
|
const previewManager = getPreviewManager();
|
|
9146
9118
|
try {
|
|
9147
|
-
const config = await (0,
|
|
9119
|
+
const config = await (0, import_core12.loadConfig)();
|
|
9148
9120
|
if (!config?.access_token) {
|
|
9149
9121
|
console.error(`[Daemon] EP833: No access token for tunnel restart`);
|
|
9150
9122
|
return;
|
|
@@ -9337,8 +9309,8 @@ var Daemon = class _Daemon {
|
|
|
9337
9309
|
await this.shutdown();
|
|
9338
9310
|
try {
|
|
9339
9311
|
const pidPath = getPidFilePath();
|
|
9340
|
-
if (
|
|
9341
|
-
|
|
9312
|
+
if (fs16.existsSync(pidPath)) {
|
|
9313
|
+
fs16.unlinkSync(pidPath);
|
|
9342
9314
|
console.log("[Daemon] PID file cleaned up");
|
|
9343
9315
|
}
|
|
9344
9316
|
} catch (error) {
|