episoda 0.2.40 → 0.2.42
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 +1470 -474
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +232 -539
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1552,15 +1552,15 @@ var require_git_executor = __commonJS({
|
|
|
1552
1552
|
try {
|
|
1553
1553
|
const { stdout: gitDir } = await execAsync("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1554
1554
|
const gitDirPath = gitDir.trim();
|
|
1555
|
-
const
|
|
1555
|
+
const fs14 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1556
1556
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1557
1557
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1558
1558
|
try {
|
|
1559
|
-
await
|
|
1559
|
+
await fs14.access(rebaseMergePath);
|
|
1560
1560
|
inRebase = true;
|
|
1561
1561
|
} catch {
|
|
1562
1562
|
try {
|
|
1563
|
-
await
|
|
1563
|
+
await fs14.access(rebaseApplyPath);
|
|
1564
1564
|
inRebase = true;
|
|
1565
1565
|
} catch {
|
|
1566
1566
|
inRebase = false;
|
|
@@ -1614,9 +1614,9 @@ var require_git_executor = __commonJS({
|
|
|
1614
1614
|
error: validation.error || "UNKNOWN_ERROR"
|
|
1615
1615
|
};
|
|
1616
1616
|
}
|
|
1617
|
-
const
|
|
1617
|
+
const fs14 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1618
1618
|
try {
|
|
1619
|
-
await
|
|
1619
|
+
await fs14.access(command.path);
|
|
1620
1620
|
return {
|
|
1621
1621
|
success: false,
|
|
1622
1622
|
error: "WORKTREE_EXISTS",
|
|
@@ -1670,9 +1670,9 @@ var require_git_executor = __commonJS({
|
|
|
1670
1670
|
*/
|
|
1671
1671
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1672
1672
|
try {
|
|
1673
|
-
const
|
|
1673
|
+
const fs14 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1674
1674
|
try {
|
|
1675
|
-
await
|
|
1675
|
+
await fs14.access(command.path);
|
|
1676
1676
|
} catch {
|
|
1677
1677
|
return {
|
|
1678
1678
|
success: false,
|
|
@@ -1707,7 +1707,7 @@ var require_git_executor = __commonJS({
|
|
|
1707
1707
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1708
1708
|
if (result.success) {
|
|
1709
1709
|
try {
|
|
1710
|
-
await
|
|
1710
|
+
await fs14.rm(command.path, { recursive: true, force: true });
|
|
1711
1711
|
} catch {
|
|
1712
1712
|
}
|
|
1713
1713
|
return {
|
|
@@ -1841,10 +1841,10 @@ var require_git_executor = __commonJS({
|
|
|
1841
1841
|
*/
|
|
1842
1842
|
async executeCloneBare(command, options) {
|
|
1843
1843
|
try {
|
|
1844
|
-
const
|
|
1845
|
-
const
|
|
1844
|
+
const fs14 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1845
|
+
const path16 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1846
1846
|
try {
|
|
1847
|
-
await
|
|
1847
|
+
await fs14.access(command.path);
|
|
1848
1848
|
return {
|
|
1849
1849
|
success: false,
|
|
1850
1850
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1853,9 +1853,9 @@ var require_git_executor = __commonJS({
|
|
|
1853
1853
|
};
|
|
1854
1854
|
} catch {
|
|
1855
1855
|
}
|
|
1856
|
-
const parentDir =
|
|
1856
|
+
const parentDir = path16.dirname(command.path);
|
|
1857
1857
|
try {
|
|
1858
|
-
await
|
|
1858
|
+
await fs14.mkdir(parentDir, { recursive: true });
|
|
1859
1859
|
} catch {
|
|
1860
1860
|
}
|
|
1861
1861
|
const { stdout, stderr } = await execAsync(
|
|
@@ -1903,22 +1903,22 @@ var require_git_executor = __commonJS({
|
|
|
1903
1903
|
*/
|
|
1904
1904
|
async executeProjectInfo(cwd, options) {
|
|
1905
1905
|
try {
|
|
1906
|
-
const
|
|
1907
|
-
const
|
|
1906
|
+
const fs14 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1907
|
+
const path16 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1908
1908
|
let currentPath = cwd;
|
|
1909
1909
|
let projectPath = cwd;
|
|
1910
1910
|
let bareRepoPath;
|
|
1911
1911
|
for (let i = 0; i < 10; i++) {
|
|
1912
|
-
const bareDir =
|
|
1913
|
-
const episodaDir =
|
|
1912
|
+
const bareDir = path16.join(currentPath, ".bare");
|
|
1913
|
+
const episodaDir = path16.join(currentPath, ".episoda");
|
|
1914
1914
|
try {
|
|
1915
|
-
await
|
|
1916
|
-
await
|
|
1915
|
+
await fs14.access(bareDir);
|
|
1916
|
+
await fs14.access(episodaDir);
|
|
1917
1917
|
projectPath = currentPath;
|
|
1918
1918
|
bareRepoPath = bareDir;
|
|
1919
1919
|
break;
|
|
1920
1920
|
} catch {
|
|
1921
|
-
const parentPath =
|
|
1921
|
+
const parentPath = path16.dirname(currentPath);
|
|
1922
1922
|
if (parentPath === currentPath) {
|
|
1923
1923
|
break;
|
|
1924
1924
|
}
|
|
@@ -2512,31 +2512,31 @@ var require_auth = __commonJS({
|
|
|
2512
2512
|
exports2.loadConfig = loadConfig9;
|
|
2513
2513
|
exports2.saveConfig = saveConfig3;
|
|
2514
2514
|
exports2.validateToken = validateToken;
|
|
2515
|
-
var
|
|
2516
|
-
var
|
|
2515
|
+
var fs14 = __importStar(require("fs"));
|
|
2516
|
+
var path16 = __importStar(require("path"));
|
|
2517
2517
|
var os4 = __importStar(require("os"));
|
|
2518
2518
|
var child_process_1 = require("child_process");
|
|
2519
2519
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2520
2520
|
function getConfigDir5() {
|
|
2521
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2521
|
+
return process.env.EPISODA_CONFIG_DIR || path16.join(os4.homedir(), ".episoda");
|
|
2522
2522
|
}
|
|
2523
2523
|
function getConfigPath4(configPath) {
|
|
2524
2524
|
if (configPath) {
|
|
2525
2525
|
return configPath;
|
|
2526
2526
|
}
|
|
2527
|
-
return
|
|
2527
|
+
return path16.join(getConfigDir5(), DEFAULT_CONFIG_FILE);
|
|
2528
2528
|
}
|
|
2529
2529
|
function ensureConfigDir(configPath) {
|
|
2530
|
-
const dir =
|
|
2531
|
-
const isNew = !
|
|
2530
|
+
const dir = path16.dirname(configPath);
|
|
2531
|
+
const isNew = !fs14.existsSync(dir);
|
|
2532
2532
|
if (isNew) {
|
|
2533
|
-
|
|
2533
|
+
fs14.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2534
2534
|
}
|
|
2535
2535
|
if (process.platform === "darwin") {
|
|
2536
|
-
const nosyncPath =
|
|
2537
|
-
if (isNew || !
|
|
2536
|
+
const nosyncPath = path16.join(dir, ".nosync");
|
|
2537
|
+
if (isNew || !fs14.existsSync(nosyncPath)) {
|
|
2538
2538
|
try {
|
|
2539
|
-
|
|
2539
|
+
fs14.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2540
2540
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2541
2541
|
stdio: "ignore",
|
|
2542
2542
|
timeout: 5e3
|
|
@@ -2548,11 +2548,11 @@ var require_auth = __commonJS({
|
|
|
2548
2548
|
}
|
|
2549
2549
|
async function loadConfig9(configPath) {
|
|
2550
2550
|
const fullPath = getConfigPath4(configPath);
|
|
2551
|
-
if (!
|
|
2551
|
+
if (!fs14.existsSync(fullPath)) {
|
|
2552
2552
|
return null;
|
|
2553
2553
|
}
|
|
2554
2554
|
try {
|
|
2555
|
-
const content =
|
|
2555
|
+
const content = fs14.readFileSync(fullPath, "utf8");
|
|
2556
2556
|
const config = JSON.parse(content);
|
|
2557
2557
|
return config;
|
|
2558
2558
|
} catch (error) {
|
|
@@ -2565,7 +2565,7 @@ var require_auth = __commonJS({
|
|
|
2565
2565
|
ensureConfigDir(fullPath);
|
|
2566
2566
|
try {
|
|
2567
2567
|
const content = JSON.stringify(config, null, 2);
|
|
2568
|
-
|
|
2568
|
+
fs14.writeFileSync(fullPath, content, { mode: 384 });
|
|
2569
2569
|
} catch (error) {
|
|
2570
2570
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2571
2571
|
}
|
|
@@ -2681,211 +2681,9 @@ var require_dist = __commonJS({
|
|
|
2681
2681
|
var import_commander = require("commander");
|
|
2682
2682
|
var import_core18 = __toESM(require_dist());
|
|
2683
2683
|
|
|
2684
|
-
// src/commands/
|
|
2684
|
+
// src/commands/daemon.ts
|
|
2685
2685
|
var import_core4 = __toESM(require_dist());
|
|
2686
2686
|
|
|
2687
|
-
// src/framework-detector.ts
|
|
2688
|
-
var fs = __toESM(require("fs"));
|
|
2689
|
-
var path = __toESM(require("path"));
|
|
2690
|
-
async function detectFramework(cwd = process.cwd()) {
|
|
2691
|
-
const packageJsonPath = path.join(cwd, "package.json");
|
|
2692
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
2693
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
2694
|
-
const scripts = packageJson.scripts || {};
|
|
2695
|
-
if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {
|
|
2696
|
-
if (scripts.dev) {
|
|
2697
|
-
return {
|
|
2698
|
-
framework: "Next.js",
|
|
2699
|
-
command: ["npm", "run", "dev"],
|
|
2700
|
-
confidence: "high",
|
|
2701
|
-
detectedFrom: "package.json (next dependency + dev script)"
|
|
2702
|
-
};
|
|
2703
|
-
}
|
|
2704
|
-
return {
|
|
2705
|
-
framework: "Next.js",
|
|
2706
|
-
command: ["npx", "next", "dev"],
|
|
2707
|
-
confidence: "medium",
|
|
2708
|
-
detectedFrom: "package.json (next dependency)"
|
|
2709
|
-
};
|
|
2710
|
-
}
|
|
2711
|
-
if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
|
|
2712
|
-
if (scripts.dev) {
|
|
2713
|
-
return {
|
|
2714
|
-
framework: "React",
|
|
2715
|
-
command: ["npm", "run", "dev"],
|
|
2716
|
-
confidence: "high",
|
|
2717
|
-
detectedFrom: "package.json (react + dev script)"
|
|
2718
|
-
};
|
|
2719
|
-
}
|
|
2720
|
-
if (scripts.start) {
|
|
2721
|
-
return {
|
|
2722
|
-
framework: "React",
|
|
2723
|
-
command: ["npm", "start"],
|
|
2724
|
-
confidence: "high",
|
|
2725
|
-
detectedFrom: "package.json (react + start script)"
|
|
2726
|
-
};
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
if (packageJson.dependencies?.express) {
|
|
2730
|
-
if (scripts.dev) {
|
|
2731
|
-
return {
|
|
2732
|
-
framework: "Express",
|
|
2733
|
-
command: ["npm", "run", "dev"],
|
|
2734
|
-
confidence: "high",
|
|
2735
|
-
detectedFrom: "package.json (express + dev script)"
|
|
2736
|
-
};
|
|
2737
|
-
}
|
|
2738
|
-
if (scripts.start) {
|
|
2739
|
-
return {
|
|
2740
|
-
framework: "Express",
|
|
2741
|
-
command: ["npm", "start"],
|
|
2742
|
-
confidence: "medium",
|
|
2743
|
-
detectedFrom: "package.json (express + start script)"
|
|
2744
|
-
};
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
|
|
2748
|
-
if (scripts.dev) {
|
|
2749
|
-
return {
|
|
2750
|
-
framework: "Vue",
|
|
2751
|
-
command: ["npm", "run", "dev"],
|
|
2752
|
-
confidence: "high",
|
|
2753
|
-
detectedFrom: "package.json (vue + dev script)"
|
|
2754
|
-
};
|
|
2755
|
-
}
|
|
2756
|
-
if (scripts.serve) {
|
|
2757
|
-
return {
|
|
2758
|
-
framework: "Vue",
|
|
2759
|
-
command: ["npm", "run", "serve"],
|
|
2760
|
-
confidence: "high",
|
|
2761
|
-
detectedFrom: "package.json (vue + serve script)"
|
|
2762
|
-
};
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
if (scripts.dev) {
|
|
2766
|
-
return {
|
|
2767
|
-
framework: "Node.js",
|
|
2768
|
-
command: ["npm", "run", "dev"],
|
|
2769
|
-
confidence: "medium",
|
|
2770
|
-
detectedFrom: "package.json (dev script)"
|
|
2771
|
-
};
|
|
2772
|
-
}
|
|
2773
|
-
if (scripts.start) {
|
|
2774
|
-
return {
|
|
2775
|
-
framework: "Node.js",
|
|
2776
|
-
command: ["npm", "start"],
|
|
2777
|
-
confidence: "low",
|
|
2778
|
-
detectedFrom: "package.json (start script)"
|
|
2779
|
-
};
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2782
|
-
const requirementsPath = path.join(cwd, "requirements.txt");
|
|
2783
|
-
if (fs.existsSync(requirementsPath)) {
|
|
2784
|
-
const requirements = fs.readFileSync(requirementsPath, "utf-8");
|
|
2785
|
-
if (requirements.includes("Django") || requirements.includes("django")) {
|
|
2786
|
-
const managePy = path.join(cwd, "manage.py");
|
|
2787
|
-
if (fs.existsSync(managePy)) {
|
|
2788
|
-
return {
|
|
2789
|
-
framework: "Django",
|
|
2790
|
-
command: ["python", "manage.py", "runserver"],
|
|
2791
|
-
confidence: "high",
|
|
2792
|
-
detectedFrom: "requirements.txt (Django) + manage.py"
|
|
2793
|
-
};
|
|
2794
|
-
}
|
|
2795
|
-
return {
|
|
2796
|
-
framework: "Django",
|
|
2797
|
-
command: ["python", "manage.py", "runserver"],
|
|
2798
|
-
confidence: "medium",
|
|
2799
|
-
detectedFrom: "requirements.txt (Django)"
|
|
2800
|
-
};
|
|
2801
|
-
}
|
|
2802
|
-
if (requirements.includes("Flask") || requirements.includes("flask")) {
|
|
2803
|
-
const appFiles = ["app.py", "application.py", "wsgi.py"];
|
|
2804
|
-
for (const file of appFiles) {
|
|
2805
|
-
if (fs.existsSync(path.join(cwd, file))) {
|
|
2806
|
-
return {
|
|
2807
|
-
framework: "Flask",
|
|
2808
|
-
command: ["flask", "run"],
|
|
2809
|
-
confidence: "high",
|
|
2810
|
-
detectedFrom: `requirements.txt (Flask) + ${file}`
|
|
2811
|
-
};
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
return {
|
|
2815
|
-
framework: "Flask",
|
|
2816
|
-
command: ["flask", "run"],
|
|
2817
|
-
confidence: "medium",
|
|
2818
|
-
detectedFrom: "requirements.txt (Flask)"
|
|
2819
|
-
};
|
|
2820
|
-
}
|
|
2821
|
-
if (requirements.includes("fastapi") || requirements.includes("uvicorn")) {
|
|
2822
|
-
return {
|
|
2823
|
-
framework: "FastAPI",
|
|
2824
|
-
command: ["uvicorn", "main:app", "--reload"],
|
|
2825
|
-
confidence: "medium",
|
|
2826
|
-
detectedFrom: "requirements.txt (fastapi/uvicorn)"
|
|
2827
|
-
};
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
const gemfilePath = path.join(cwd, "Gemfile");
|
|
2831
|
-
if (fs.existsSync(gemfilePath)) {
|
|
2832
|
-
const gemfile = fs.readFileSync(gemfilePath, "utf-8");
|
|
2833
|
-
if (gemfile.includes("rails")) {
|
|
2834
|
-
return {
|
|
2835
|
-
framework: "Rails",
|
|
2836
|
-
command: ["rails", "server"],
|
|
2837
|
-
confidence: "high",
|
|
2838
|
-
detectedFrom: "Gemfile (rails)"
|
|
2839
|
-
};
|
|
2840
|
-
}
|
|
2841
|
-
if (gemfile.includes("sinatra")) {
|
|
2842
|
-
return {
|
|
2843
|
-
framework: "Sinatra",
|
|
2844
|
-
command: ["ruby", "app.rb"],
|
|
2845
|
-
confidence: "medium",
|
|
2846
|
-
detectedFrom: "Gemfile (sinatra)"
|
|
2847
|
-
};
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
|
-
const goModPath = path.join(cwd, "go.mod");
|
|
2851
|
-
if (fs.existsSync(goModPath)) {
|
|
2852
|
-
return {
|
|
2853
|
-
framework: "Go",
|
|
2854
|
-
command: ["go", "run", "."],
|
|
2855
|
-
confidence: "medium",
|
|
2856
|
-
detectedFrom: "go.mod"
|
|
2857
|
-
};
|
|
2858
|
-
}
|
|
2859
|
-
const cargoTomlPath = path.join(cwd, "Cargo.toml");
|
|
2860
|
-
if (fs.existsSync(cargoTomlPath)) {
|
|
2861
|
-
return {
|
|
2862
|
-
framework: "Rust",
|
|
2863
|
-
command: ["cargo", "run"],
|
|
2864
|
-
confidence: "medium",
|
|
2865
|
-
detectedFrom: "Cargo.toml"
|
|
2866
|
-
};
|
|
2867
|
-
}
|
|
2868
|
-
return null;
|
|
2869
|
-
}
|
|
2870
|
-
async function resolveDevCommand(providedCommand, cwd = process.cwd()) {
|
|
2871
|
-
if (providedCommand && providedCommand.length > 0) {
|
|
2872
|
-
return { command: providedCommand, detection: null };
|
|
2873
|
-
}
|
|
2874
|
-
const detection = await detectFramework(cwd);
|
|
2875
|
-
if (detection) {
|
|
2876
|
-
return { command: detection.command, detection };
|
|
2877
|
-
}
|
|
2878
|
-
return {
|
|
2879
|
-
command: ["npm", "run", "dev"],
|
|
2880
|
-
detection: {
|
|
2881
|
-
framework: "Unknown",
|
|
2882
|
-
command: ["npm", "run", "dev"],
|
|
2883
|
-
confidence: "low",
|
|
2884
|
-
detectedFrom: "default fallback"
|
|
2885
|
-
}
|
|
2886
|
-
};
|
|
2887
|
-
}
|
|
2888
|
-
|
|
2889
2687
|
// src/output.ts
|
|
2890
2688
|
var import_chalk = __toESM(require("chalk"));
|
|
2891
2689
|
var import_ora = __toESM(require("ora"));
|
|
@@ -2904,17 +2702,10 @@ var status = {
|
|
|
2904
2702
|
process.stdout.write(`\r${import_chalk.default.blue("\u23F3")} ${message}`);
|
|
2905
2703
|
}
|
|
2906
2704
|
};
|
|
2907
|
-
function printFrameworkDetection(framework, command, confidence) {
|
|
2908
|
-
console.log(import_chalk.default.bold("\n\u{1F50D} Framework Detection"));
|
|
2909
|
-
console.log(import_chalk.default.cyan(" Framework:"), framework);
|
|
2910
|
-
console.log(import_chalk.default.cyan(" Command:"), command.join(" "));
|
|
2911
|
-
console.log(import_chalk.default.cyan(" Confidence:"), confidence);
|
|
2912
|
-
console.log();
|
|
2913
|
-
}
|
|
2914
2705
|
|
|
2915
2706
|
// src/daemon/daemon-manager.ts
|
|
2916
|
-
var
|
|
2917
|
-
var
|
|
2707
|
+
var fs = __toESM(require("fs"));
|
|
2708
|
+
var path = __toESM(require("path"));
|
|
2918
2709
|
var import_child_process = require("child_process");
|
|
2919
2710
|
var import_core = __toESM(require_dist());
|
|
2920
2711
|
function killAllEpisodaProcesses() {
|
|
@@ -2946,13 +2737,13 @@ function killAllEpisodaProcesses() {
|
|
|
2946
2737
|
(0, import_child_process.execSync)("sleep 0.5", { timeout: 2e3 });
|
|
2947
2738
|
}
|
|
2948
2739
|
const configDir = (0, import_core.getConfigDir)();
|
|
2949
|
-
const pidPath =
|
|
2950
|
-
const sockPath =
|
|
2951
|
-
if (
|
|
2952
|
-
|
|
2740
|
+
const pidPath = path.join(configDir, "daemon.pid");
|
|
2741
|
+
const sockPath = path.join(configDir, "daemon.sock");
|
|
2742
|
+
if (fs.existsSync(pidPath)) {
|
|
2743
|
+
fs.unlinkSync(pidPath);
|
|
2953
2744
|
}
|
|
2954
|
-
if (
|
|
2955
|
-
|
|
2745
|
+
if (fs.existsSync(sockPath)) {
|
|
2746
|
+
fs.unlinkSync(sockPath);
|
|
2956
2747
|
}
|
|
2957
2748
|
} catch (error) {
|
|
2958
2749
|
console.warn("[Cleanup] Error during process cleanup:", error instanceof Error ? error.message : error);
|
|
@@ -2960,25 +2751,25 @@ function killAllEpisodaProcesses() {
|
|
|
2960
2751
|
return killedCount;
|
|
2961
2752
|
}
|
|
2962
2753
|
function getPidFilePath() {
|
|
2963
|
-
return
|
|
2754
|
+
return path.join((0, import_core.getConfigDir)(), "daemon.pid");
|
|
2964
2755
|
}
|
|
2965
2756
|
function isDaemonRunning() {
|
|
2966
2757
|
const pidPath = getPidFilePath();
|
|
2967
2758
|
try {
|
|
2968
|
-
if (!
|
|
2759
|
+
if (!fs.existsSync(pidPath)) {
|
|
2969
2760
|
return null;
|
|
2970
2761
|
}
|
|
2971
|
-
const pidStr =
|
|
2762
|
+
const pidStr = fs.readFileSync(pidPath, "utf-8").trim();
|
|
2972
2763
|
const pid = parseInt(pidStr, 10);
|
|
2973
2764
|
if (isNaN(pid)) {
|
|
2974
|
-
|
|
2765
|
+
fs.unlinkSync(pidPath);
|
|
2975
2766
|
return null;
|
|
2976
2767
|
}
|
|
2977
2768
|
try {
|
|
2978
2769
|
process.kill(pid, 0);
|
|
2979
2770
|
return pid;
|
|
2980
2771
|
} catch (error) {
|
|
2981
|
-
|
|
2772
|
+
fs.unlinkSync(pidPath);
|
|
2982
2773
|
return null;
|
|
2983
2774
|
}
|
|
2984
2775
|
} catch (error) {
|
|
@@ -2992,15 +2783,15 @@ async function startDaemon() {
|
|
|
2992
2783
|
throw new Error(`Daemon already running (PID: ${existingPid})`);
|
|
2993
2784
|
}
|
|
2994
2785
|
const configDir = (0, import_core.getConfigDir)();
|
|
2995
|
-
if (!
|
|
2996
|
-
|
|
2786
|
+
if (!fs.existsSync(configDir)) {
|
|
2787
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
2997
2788
|
}
|
|
2998
|
-
const daemonScript =
|
|
2999
|
-
if (!
|
|
2789
|
+
const daemonScript = path.join(__dirname, "daemon", "daemon-process.js");
|
|
2790
|
+
if (!fs.existsSync(daemonScript)) {
|
|
3000
2791
|
throw new Error(`Daemon script not found: ${daemonScript}. Make sure CLI is built.`);
|
|
3001
2792
|
}
|
|
3002
|
-
const logPath =
|
|
3003
|
-
const logFd =
|
|
2793
|
+
const logPath = path.join(configDir, "daemon.log");
|
|
2794
|
+
const logFd = fs.openSync(logPath, "a");
|
|
3004
2795
|
const child = (0, import_child_process.spawn)("node", [daemonScript], {
|
|
3005
2796
|
detached: true,
|
|
3006
2797
|
// Run independently of parent
|
|
@@ -3015,7 +2806,7 @@ async function startDaemon() {
|
|
|
3015
2806
|
child.unref();
|
|
3016
2807
|
const pid = child.pid;
|
|
3017
2808
|
const pidPath = getPidFilePath();
|
|
3018
|
-
|
|
2809
|
+
fs.writeFileSync(pidPath, pid.toString(), "utf-8");
|
|
3019
2810
|
await new Promise((resolve5) => setTimeout(resolve5, 500));
|
|
3020
2811
|
const runningPid = isDaemonRunning();
|
|
3021
2812
|
if (!runningPid) {
|
|
@@ -3027,8 +2818,8 @@ async function stopDaemon(timeout = 5e3) {
|
|
|
3027
2818
|
const pid = isDaemonRunning();
|
|
3028
2819
|
if (!pid) {
|
|
3029
2820
|
const pidPath = getPidFilePath();
|
|
3030
|
-
if (
|
|
3031
|
-
|
|
2821
|
+
if (fs.existsSync(pidPath)) {
|
|
2822
|
+
fs.unlinkSync(pidPath);
|
|
3032
2823
|
}
|
|
3033
2824
|
return false;
|
|
3034
2825
|
}
|
|
@@ -3041,8 +2832,8 @@ async function stopDaemon(timeout = 5e3) {
|
|
|
3041
2832
|
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
3042
2833
|
} catch (error) {
|
|
3043
2834
|
const pidPath2 = getPidFilePath();
|
|
3044
|
-
if (
|
|
3045
|
-
|
|
2835
|
+
if (fs.existsSync(pidPath2)) {
|
|
2836
|
+
fs.unlinkSync(pidPath2);
|
|
3046
2837
|
}
|
|
3047
2838
|
return true;
|
|
3048
2839
|
}
|
|
@@ -3050,8 +2841,8 @@ async function stopDaemon(timeout = 5e3) {
|
|
|
3050
2841
|
console.warn(`Daemon didn't stop gracefully, forcing shutdown (PID: ${pid})`);
|
|
3051
2842
|
process.kill(pid, "SIGKILL");
|
|
3052
2843
|
const pidPath = getPidFilePath();
|
|
3053
|
-
if (
|
|
3054
|
-
|
|
2844
|
+
if (fs.existsSync(pidPath)) {
|
|
2845
|
+
fs.unlinkSync(pidPath);
|
|
3055
2846
|
}
|
|
3056
2847
|
return true;
|
|
3057
2848
|
} catch (error) {
|
|
@@ -3062,10 +2853,10 @@ async function stopDaemon(timeout = 5e3) {
|
|
|
3062
2853
|
|
|
3063
2854
|
// src/ipc/ipc-client.ts
|
|
3064
2855
|
var net = __toESM(require("net"));
|
|
3065
|
-
var
|
|
2856
|
+
var path2 = __toESM(require("path"));
|
|
3066
2857
|
var crypto = __toESM(require("crypto"));
|
|
3067
2858
|
var import_core2 = __toESM(require_dist());
|
|
3068
|
-
var getSocketPath = () =>
|
|
2859
|
+
var getSocketPath = () => path2.join((0, import_core2.getConfigDir)(), "daemon.sock");
|
|
3069
2860
|
var DEFAULT_TIMEOUT = 15e3;
|
|
3070
2861
|
async function sendCommand(command, params, timeout = DEFAULT_TIMEOUT) {
|
|
3071
2862
|
return new Promise((resolve5, reject) => {
|
|
@@ -3154,40 +2945,14 @@ async function getDevServerStatus() {
|
|
|
3154
2945
|
return await sendCommand("dev-server-status");
|
|
3155
2946
|
}
|
|
3156
2947
|
|
|
3157
|
-
// src/commands/
|
|
2948
|
+
// src/commands/daemon.ts
|
|
3158
2949
|
var import_child_process3 = require("child_process");
|
|
3159
|
-
var
|
|
3160
|
-
var
|
|
3161
|
-
|
|
3162
|
-
// src/utils/port-check.ts
|
|
3163
|
-
var net2 = __toESM(require("net"));
|
|
3164
|
-
async function isPortInUse(port) {
|
|
3165
|
-
return new Promise((resolve5) => {
|
|
3166
|
-
const server = net2.createServer();
|
|
3167
|
-
server.once("error", (err) => {
|
|
3168
|
-
if (err.code === "EADDRINUSE") {
|
|
3169
|
-
resolve5(true);
|
|
3170
|
-
} else {
|
|
3171
|
-
resolve5(false);
|
|
3172
|
-
}
|
|
3173
|
-
});
|
|
3174
|
-
server.once("listening", () => {
|
|
3175
|
-
server.close();
|
|
3176
|
-
resolve5(false);
|
|
3177
|
-
});
|
|
3178
|
-
server.listen(port);
|
|
3179
|
-
});
|
|
3180
|
-
}
|
|
3181
|
-
function getServerPort() {
|
|
3182
|
-
if (process.env.PORT) {
|
|
3183
|
-
return parseInt(process.env.PORT, 10);
|
|
3184
|
-
}
|
|
3185
|
-
return 3e3;
|
|
3186
|
-
}
|
|
2950
|
+
var path5 = __toESM(require("path"));
|
|
2951
|
+
var fs4 = __toESM(require("fs"));
|
|
3187
2952
|
|
|
3188
2953
|
// src/daemon/worktree-manager.ts
|
|
3189
|
-
var
|
|
3190
|
-
var
|
|
2954
|
+
var fs2 = __toESM(require("fs"));
|
|
2955
|
+
var path3 = __toESM(require("path"));
|
|
3191
2956
|
var import_core3 = __toESM(require_dist());
|
|
3192
2957
|
function validateModuleUid(moduleUid) {
|
|
3193
2958
|
if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
|
|
@@ -3211,8 +2976,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3211
2976
|
// ============================================================
|
|
3212
2977
|
this.lockPath = "";
|
|
3213
2978
|
this.projectRoot = projectRoot;
|
|
3214
|
-
this.bareRepoPath =
|
|
3215
|
-
this.configPath =
|
|
2979
|
+
this.bareRepoPath = path3.join(projectRoot, ".bare");
|
|
2980
|
+
this.configPath = path3.join(projectRoot, ".episoda", "config.json");
|
|
3216
2981
|
this.gitExecutor = new import_core3.GitExecutor();
|
|
3217
2982
|
}
|
|
3218
2983
|
/**
|
|
@@ -3221,10 +2986,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3221
2986
|
* @returns true if valid project, false otherwise
|
|
3222
2987
|
*/
|
|
3223
2988
|
async initialize() {
|
|
3224
|
-
if (!
|
|
2989
|
+
if (!fs2.existsSync(this.bareRepoPath)) {
|
|
3225
2990
|
return false;
|
|
3226
2991
|
}
|
|
3227
|
-
if (!
|
|
2992
|
+
if (!fs2.existsSync(this.configPath)) {
|
|
3228
2993
|
return false;
|
|
3229
2994
|
}
|
|
3230
2995
|
try {
|
|
@@ -3271,8 +3036,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3271
3036
|
*/
|
|
3272
3037
|
static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
|
|
3273
3038
|
const manager = new _WorktreeManager(projectRoot);
|
|
3274
|
-
const episodaDir =
|
|
3275
|
-
|
|
3039
|
+
const episodaDir = path3.join(projectRoot, ".episoda");
|
|
3040
|
+
fs2.mkdirSync(episodaDir, { recursive: true });
|
|
3276
3041
|
const cloneResult = await manager.gitExecutor.execute({
|
|
3277
3042
|
action: "clone_bare",
|
|
3278
3043
|
url: repoUrl,
|
|
@@ -3303,7 +3068,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3303
3068
|
error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
|
|
3304
3069
|
};
|
|
3305
3070
|
}
|
|
3306
|
-
const worktreePath =
|
|
3071
|
+
const worktreePath = path3.join(this.projectRoot, moduleUid);
|
|
3307
3072
|
const lockAcquired = await this.acquireLock();
|
|
3308
3073
|
if (!lockAcquired) {
|
|
3309
3074
|
return {
|
|
@@ -3485,7 +3250,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3485
3250
|
let prunedCount = 0;
|
|
3486
3251
|
await this.updateConfigSafe((config) => {
|
|
3487
3252
|
const initialCount = config.worktrees.length;
|
|
3488
|
-
config.worktrees = config.worktrees.filter((w) =>
|
|
3253
|
+
config.worktrees = config.worktrees.filter((w) => fs2.existsSync(w.worktreePath));
|
|
3489
3254
|
prunedCount = initialCount - config.worktrees.length;
|
|
3490
3255
|
return config;
|
|
3491
3256
|
});
|
|
@@ -3566,16 +3331,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3566
3331
|
const retryInterval = 50;
|
|
3567
3332
|
while (Date.now() - startTime < timeoutMs) {
|
|
3568
3333
|
try {
|
|
3569
|
-
|
|
3334
|
+
fs2.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
3570
3335
|
return true;
|
|
3571
3336
|
} catch (err) {
|
|
3572
3337
|
if (err.code === "EEXIST") {
|
|
3573
3338
|
try {
|
|
3574
|
-
const stats =
|
|
3339
|
+
const stats = fs2.statSync(lockPath);
|
|
3575
3340
|
const lockAge = Date.now() - stats.mtimeMs;
|
|
3576
3341
|
if (lockAge > 3e4) {
|
|
3577
3342
|
try {
|
|
3578
|
-
const lockContent =
|
|
3343
|
+
const lockContent = fs2.readFileSync(lockPath, "utf-8").trim();
|
|
3579
3344
|
const lockPid = parseInt(lockContent, 10);
|
|
3580
3345
|
if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
|
|
3581
3346
|
await new Promise((resolve5) => setTimeout(resolve5, retryInterval));
|
|
@@ -3584,7 +3349,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3584
3349
|
} catch {
|
|
3585
3350
|
}
|
|
3586
3351
|
try {
|
|
3587
|
-
|
|
3352
|
+
fs2.unlinkSync(lockPath);
|
|
3588
3353
|
} catch {
|
|
3589
3354
|
}
|
|
3590
3355
|
continue;
|
|
@@ -3605,16 +3370,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3605
3370
|
*/
|
|
3606
3371
|
releaseLock() {
|
|
3607
3372
|
try {
|
|
3608
|
-
|
|
3373
|
+
fs2.unlinkSync(this.getLockPath());
|
|
3609
3374
|
} catch {
|
|
3610
3375
|
}
|
|
3611
3376
|
}
|
|
3612
3377
|
readConfig() {
|
|
3613
3378
|
try {
|
|
3614
|
-
if (!
|
|
3379
|
+
if (!fs2.existsSync(this.configPath)) {
|
|
3615
3380
|
return null;
|
|
3616
3381
|
}
|
|
3617
|
-
const content =
|
|
3382
|
+
const content = fs2.readFileSync(this.configPath, "utf-8");
|
|
3618
3383
|
return JSON.parse(content);
|
|
3619
3384
|
} catch (error) {
|
|
3620
3385
|
console.error("[WorktreeManager] Failed to read config:", error);
|
|
@@ -3623,11 +3388,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3623
3388
|
}
|
|
3624
3389
|
writeConfig(config) {
|
|
3625
3390
|
try {
|
|
3626
|
-
const dir =
|
|
3627
|
-
if (!
|
|
3628
|
-
|
|
3391
|
+
const dir = path3.dirname(this.configPath);
|
|
3392
|
+
if (!fs2.existsSync(dir)) {
|
|
3393
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
3629
3394
|
}
|
|
3630
|
-
|
|
3395
|
+
fs2.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
3631
3396
|
} catch (error) {
|
|
3632
3397
|
console.error("[WorktreeManager] Failed to write config:", error);
|
|
3633
3398
|
throw error;
|
|
@@ -3708,14 +3473,14 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3708
3473
|
}
|
|
3709
3474
|
try {
|
|
3710
3475
|
for (const file of files) {
|
|
3711
|
-
const srcPath =
|
|
3712
|
-
const destPath =
|
|
3713
|
-
if (
|
|
3714
|
-
const destDir =
|
|
3715
|
-
if (!
|
|
3716
|
-
|
|
3476
|
+
const srcPath = path3.join(mainWorktree.worktreePath, file);
|
|
3477
|
+
const destPath = path3.join(worktree.worktreePath, file);
|
|
3478
|
+
if (fs2.existsSync(srcPath)) {
|
|
3479
|
+
const destDir = path3.dirname(destPath);
|
|
3480
|
+
if (!fs2.existsSync(destDir)) {
|
|
3481
|
+
fs2.mkdirSync(destDir, { recursive: true });
|
|
3717
3482
|
}
|
|
3718
|
-
|
|
3483
|
+
fs2.copyFileSync(srcPath, destPath);
|
|
3719
3484
|
console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`);
|
|
3720
3485
|
} else {
|
|
3721
3486
|
console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`);
|
|
@@ -3798,30 +3563,30 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3798
3563
|
}
|
|
3799
3564
|
};
|
|
3800
3565
|
function getEpisodaRoot() {
|
|
3801
|
-
return process.env.EPISODA_ROOT ||
|
|
3566
|
+
return process.env.EPISODA_ROOT || path3.join(require("os").homedir(), "episoda");
|
|
3802
3567
|
}
|
|
3803
3568
|
function getProjectPath(workspaceSlug, projectSlug) {
|
|
3804
|
-
return
|
|
3569
|
+
return path3.join(getEpisodaRoot(), workspaceSlug, projectSlug);
|
|
3805
3570
|
}
|
|
3806
3571
|
async function isWorktreeProject(projectRoot) {
|
|
3807
3572
|
const manager = new WorktreeManager(projectRoot);
|
|
3808
3573
|
return manager.initialize();
|
|
3809
3574
|
}
|
|
3810
3575
|
async function findProjectRoot(startPath) {
|
|
3811
|
-
let current =
|
|
3576
|
+
let current = path3.resolve(startPath);
|
|
3812
3577
|
const episodaRoot = getEpisodaRoot();
|
|
3813
3578
|
if (!current.startsWith(episodaRoot)) {
|
|
3814
3579
|
return null;
|
|
3815
3580
|
}
|
|
3816
3581
|
for (let i = 0; i < 10; i++) {
|
|
3817
|
-
const bareDir =
|
|
3818
|
-
const episodaDir =
|
|
3819
|
-
if (
|
|
3582
|
+
const bareDir = path3.join(current, ".bare");
|
|
3583
|
+
const episodaDir = path3.join(current, ".episoda");
|
|
3584
|
+
if (fs2.existsSync(bareDir) && fs2.existsSync(episodaDir)) {
|
|
3820
3585
|
if (await isWorktreeProject(current)) {
|
|
3821
3586
|
return current;
|
|
3822
3587
|
}
|
|
3823
3588
|
}
|
|
3824
|
-
const parent =
|
|
3589
|
+
const parent = path3.dirname(current);
|
|
3825
3590
|
if (parent === current) {
|
|
3826
3591
|
break;
|
|
3827
3592
|
}
|
|
@@ -3859,7 +3624,7 @@ async function fetchProjectPath(config, projectId) {
|
|
|
3859
3624
|
return null;
|
|
3860
3625
|
}
|
|
3861
3626
|
}
|
|
3862
|
-
async function syncProjectPath(config, projectId,
|
|
3627
|
+
async function syncProjectPath(config, projectId, path16) {
|
|
3863
3628
|
if (!config.device_id || !config.access_token) {
|
|
3864
3629
|
return false;
|
|
3865
3630
|
}
|
|
@@ -3872,7 +3637,7 @@ async function syncProjectPath(config, projectId, path17) {
|
|
|
3872
3637
|
"Authorization": `Bearer ${config.access_token}`,
|
|
3873
3638
|
"Content-Type": "application/json"
|
|
3874
3639
|
},
|
|
3875
|
-
body: JSON.stringify({ path:
|
|
3640
|
+
body: JSON.stringify({ path: path16 })
|
|
3876
3641
|
});
|
|
3877
3642
|
if (!response.ok) {
|
|
3878
3643
|
console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`);
|
|
@@ -3885,22 +3650,22 @@ async function syncProjectPath(config, projectId, path17) {
|
|
|
3885
3650
|
}
|
|
3886
3651
|
|
|
3887
3652
|
// src/utils/bootstrap.ts
|
|
3888
|
-
var
|
|
3889
|
-
var
|
|
3653
|
+
var fs3 = __toESM(require("fs"));
|
|
3654
|
+
var path4 = __toESM(require("path"));
|
|
3890
3655
|
var import_child_process2 = require("child_process");
|
|
3891
3656
|
async function extractBootstrapScripts(bareRepoPath, projectPath) {
|
|
3892
|
-
const scriptsDir =
|
|
3893
|
-
const scriptPath =
|
|
3894
|
-
if (
|
|
3657
|
+
const scriptsDir = path4.join(projectPath, ".episoda", "scripts");
|
|
3658
|
+
const scriptPath = path4.join(scriptsDir, "api-helper.sh");
|
|
3659
|
+
if (fs3.existsSync(scriptPath)) {
|
|
3895
3660
|
return false;
|
|
3896
3661
|
}
|
|
3897
|
-
|
|
3662
|
+
fs3.mkdirSync(scriptsDir, { recursive: true });
|
|
3898
3663
|
try {
|
|
3899
3664
|
const scriptContent = (0, import_child_process2.execSync)(
|
|
3900
3665
|
`git --git-dir="${bareRepoPath}" show main:scripts/api-helper.sh`,
|
|
3901
3666
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
3902
3667
|
);
|
|
3903
|
-
|
|
3668
|
+
fs3.writeFileSync(scriptPath, scriptContent, { mode: 493 });
|
|
3904
3669
|
return true;
|
|
3905
3670
|
} catch (error) {
|
|
3906
3671
|
console.log("[bootstrap] Could not extract api-helper.sh:", error);
|
|
@@ -3908,7 +3673,7 @@ async function extractBootstrapScripts(bareRepoPath, projectPath) {
|
|
|
3908
3673
|
}
|
|
3909
3674
|
}
|
|
3910
3675
|
|
|
3911
|
-
// src/commands/
|
|
3676
|
+
// src/commands/daemon.ts
|
|
3912
3677
|
var CONNECTION_MAX_RETRIES = 3;
|
|
3913
3678
|
function findGitRoot(startDir) {
|
|
3914
3679
|
try {
|
|
@@ -3922,7 +3687,7 @@ function findGitRoot(startDir) {
|
|
|
3922
3687
|
return null;
|
|
3923
3688
|
}
|
|
3924
3689
|
}
|
|
3925
|
-
async function
|
|
3690
|
+
async function daemonCommand(options = {}) {
|
|
3926
3691
|
try {
|
|
3927
3692
|
const config = await (0, import_core4.loadConfig)();
|
|
3928
3693
|
if (!config || !config.access_token || !config.project_id) {
|
|
@@ -3956,8 +3721,6 @@ async function devCommand(options = {}) {
|
|
|
3956
3721
|
status.debug("Daemon not reachable via IPC, restarting...");
|
|
3957
3722
|
needsRestart = true;
|
|
3958
3723
|
}
|
|
3959
|
-
} else {
|
|
3960
|
-
needsRestart = false;
|
|
3961
3724
|
}
|
|
3962
3725
|
if (needsRestart) {
|
|
3963
3726
|
const killedCount = killAllEpisodaProcesses();
|
|
@@ -3968,22 +3731,18 @@ async function devCommand(options = {}) {
|
|
|
3968
3731
|
}
|
|
3969
3732
|
let projectPath;
|
|
3970
3733
|
const serverPath = await fetchProjectPath(config, config.project_id);
|
|
3971
|
-
if (serverPath &&
|
|
3734
|
+
if (serverPath && fs4.existsSync(serverPath)) {
|
|
3972
3735
|
projectPath = serverPath;
|
|
3973
3736
|
status.debug(`Using server-synced project path: ${projectPath}`);
|
|
3974
3737
|
} else {
|
|
3975
3738
|
const detectedRoot = findGitRoot(options.cwd || process.cwd());
|
|
3976
|
-
projectPath = detectedRoot ||
|
|
3739
|
+
projectPath = detectedRoot || path5.resolve(options.cwd || process.cwd());
|
|
3977
3740
|
if (detectedRoot) {
|
|
3978
3741
|
status.debug(`Detected project root: ${projectPath}`);
|
|
3979
3742
|
} else {
|
|
3980
3743
|
status.warning(`Could not detect git root, using: ${projectPath}`);
|
|
3981
3744
|
}
|
|
3982
|
-
syncProjectPath(config, config.project_id, projectPath).
|
|
3983
|
-
if (synced) {
|
|
3984
|
-
status.debug("Project path synced to server");
|
|
3985
|
-
}
|
|
3986
|
-
}).catch(() => {
|
|
3745
|
+
syncProjectPath(config, config.project_id, projectPath).catch(() => {
|
|
3987
3746
|
});
|
|
3988
3747
|
}
|
|
3989
3748
|
const isWorktree = await isWorktreeProject(projectPath);
|
|
@@ -3994,10 +3753,10 @@ async function devCommand(options = {}) {
|
|
|
3994
3753
|
status.info("");
|
|
3995
3754
|
process.exit(1);
|
|
3996
3755
|
}
|
|
3997
|
-
const bareRepoPath =
|
|
3756
|
+
const bareRepoPath = path5.join(projectPath, ".bare");
|
|
3998
3757
|
const scriptsExtracted = await extractBootstrapScripts(bareRepoPath, projectPath);
|
|
3999
3758
|
if (scriptsExtracted) {
|
|
4000
|
-
status.success("
|
|
3759
|
+
status.success("Bootstrap scripts extracted");
|
|
4001
3760
|
}
|
|
4002
3761
|
let daemonPid = isDaemonRunning();
|
|
4003
3762
|
if (!daemonPid) {
|
|
@@ -4014,10 +3773,10 @@ async function devCommand(options = {}) {
|
|
|
4014
3773
|
}
|
|
4015
3774
|
const reachable = await isDaemonReachable();
|
|
4016
3775
|
if (!reachable) {
|
|
4017
|
-
status.error("Daemon is running but not responding. Try: episoda stop && episoda
|
|
3776
|
+
status.error("Daemon is running but not responding. Try: episoda stop && episoda daemon");
|
|
4018
3777
|
process.exit(1);
|
|
4019
3778
|
}
|
|
4020
|
-
status.info(
|
|
3779
|
+
status.info("Connecting project to Episoda...");
|
|
4021
3780
|
let connected = false;
|
|
4022
3781
|
let lastError;
|
|
4023
3782
|
for (let retry = 0; retry < CONNECTION_MAX_RETRIES && !connected; retry++) {
|
|
@@ -4042,41 +3801,25 @@ async function devCommand(options = {}) {
|
|
|
4042
3801
|
process.exit(1);
|
|
4043
3802
|
}
|
|
4044
3803
|
status.success("Connected to Episoda");
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
} else if (!options.command) {
|
|
4052
|
-
const serverPort = getServerPort();
|
|
4053
|
-
const portInUse = await isPortInUse(serverPort);
|
|
4054
|
-
if (portInUse) {
|
|
4055
|
-
connectionOnlyMode = true;
|
|
4056
|
-
status.info(`Server already running on port ${serverPort}`);
|
|
4057
|
-
status.info("Connecting to existing server...");
|
|
4058
|
-
}
|
|
4059
|
-
}
|
|
4060
|
-
if (!connectionOnlyMode) {
|
|
4061
|
-
try {
|
|
4062
|
-
const result = await resolveDevCommand(options.command || null, projectPath);
|
|
4063
|
-
command = result.command;
|
|
4064
|
-
detection = result.detection;
|
|
4065
|
-
if (detection && !options.command) {
|
|
4066
|
-
printFrameworkDetection(detection.framework, detection.command, detection.confidence);
|
|
4067
|
-
}
|
|
4068
|
-
} catch (error) {
|
|
4069
|
-
status.debug("No dev command detected");
|
|
4070
|
-
command = void 0;
|
|
4071
|
-
}
|
|
4072
|
-
}
|
|
4073
|
-
if (command && command.length > 0) {
|
|
4074
|
-
await runDevServer(command, projectPath, options.autoRestart ?? false);
|
|
3804
|
+
if (options.background) {
|
|
3805
|
+
status.info("");
|
|
3806
|
+
status.info("Daemon running in background.");
|
|
3807
|
+
status.info("Use `episoda status` to check connection.");
|
|
3808
|
+
status.info("Use `episoda stop` to stop the daemon.");
|
|
3809
|
+
process.exit(0);
|
|
4075
3810
|
} else {
|
|
4076
3811
|
status.info("");
|
|
4077
|
-
status.info("
|
|
4078
|
-
status.info("Press Ctrl+C to disconnect.");
|
|
3812
|
+
status.info("Daemon connected! Dev servers start automatically when you begin work on a module.");
|
|
3813
|
+
status.info("Press Ctrl+C to disconnect (daemon continues in background).");
|
|
4079
3814
|
status.info("");
|
|
3815
|
+
const shutdownHandler = (signal) => {
|
|
3816
|
+
status.info(`
|
|
3817
|
+
Received ${signal}. Daemon continues running in background.`);
|
|
3818
|
+
status.info("Use `episoda stop` to fully stop the daemon.");
|
|
3819
|
+
process.exit(0);
|
|
3820
|
+
};
|
|
3821
|
+
process.on("SIGTERM", () => shutdownHandler("SIGTERM"));
|
|
3822
|
+
process.on("SIGINT", () => shutdownHandler("SIGINT"));
|
|
4080
3823
|
await new Promise(() => {
|
|
4081
3824
|
});
|
|
4082
3825
|
}
|
|
@@ -4085,73 +3828,17 @@ async function devCommand(options = {}) {
|
|
|
4085
3828
|
process.exit(1);
|
|
4086
3829
|
}
|
|
4087
3830
|
}
|
|
4088
|
-
async function runDevServer(command, cwd, autoRestart) {
|
|
4089
|
-
let devProcess;
|
|
4090
|
-
let restartCount = 0;
|
|
4091
|
-
let shuttingDown = false;
|
|
4092
|
-
const startServer = () => {
|
|
4093
|
-
status.info(`Starting dev server: ${command.join(" ")}`);
|
|
4094
|
-
devProcess = (0, import_child_process3.spawn)(command[0], command.slice(1), {
|
|
4095
|
-
cwd,
|
|
4096
|
-
stdio: ["inherit", "inherit", "inherit"],
|
|
4097
|
-
shell: true
|
|
4098
|
-
});
|
|
4099
|
-
devProcess.on("exit", (code, signal) => {
|
|
4100
|
-
if (shuttingDown) {
|
|
4101
|
-
status.info("Dev server stopped");
|
|
4102
|
-
return;
|
|
4103
|
-
}
|
|
4104
|
-
if (code === 0) {
|
|
4105
|
-
status.success("Dev server exited successfully");
|
|
4106
|
-
} else {
|
|
4107
|
-
status.error(`Dev server exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`);
|
|
4108
|
-
}
|
|
4109
|
-
if (autoRestart && !shuttingDown) {
|
|
4110
|
-
restartCount++;
|
|
4111
|
-
status.info(`Auto-restarting dev server (attempt ${restartCount})...`);
|
|
4112
|
-
setTimeout(() => {
|
|
4113
|
-
startServer();
|
|
4114
|
-
}, 2e3);
|
|
4115
|
-
} else if (!shuttingDown) {
|
|
4116
|
-
status.info("");
|
|
4117
|
-
status.info("Dev server stopped, but daemon remains connected.");
|
|
4118
|
-
status.info("Git operations will still be executed by Episoda.");
|
|
4119
|
-
status.info("Press Ctrl+C to disconnect.");
|
|
4120
|
-
status.info("");
|
|
4121
|
-
}
|
|
4122
|
-
});
|
|
4123
|
-
devProcess.on("error", (error) => {
|
|
4124
|
-
status.error(`Dev server error: ${error.message}`);
|
|
4125
|
-
});
|
|
4126
|
-
};
|
|
4127
|
-
const shutdownHandler = async (signal) => {
|
|
4128
|
-
if (shuttingDown) return;
|
|
4129
|
-
shuttingDown = true;
|
|
4130
|
-
status.info(`
|
|
4131
|
-
Received ${signal}, shutting down...`);
|
|
4132
|
-
if (devProcess) {
|
|
4133
|
-
devProcess.kill("SIGTERM");
|
|
4134
|
-
}
|
|
4135
|
-
status.info("Dev server stopped. Daemon continues running in background.");
|
|
4136
|
-
process.exit(0);
|
|
4137
|
-
};
|
|
4138
|
-
process.on("SIGTERM", () => shutdownHandler("SIGTERM"));
|
|
4139
|
-
process.on("SIGINT", () => shutdownHandler("SIGINT"));
|
|
4140
|
-
startServer();
|
|
4141
|
-
await new Promise(() => {
|
|
4142
|
-
});
|
|
4143
|
-
}
|
|
4144
3831
|
|
|
4145
3832
|
// src/commands/auth.ts
|
|
4146
3833
|
var os = __toESM(require("os"));
|
|
4147
|
-
var
|
|
4148
|
-
var
|
|
3834
|
+
var fs6 = __toESM(require("fs"));
|
|
3835
|
+
var path7 = __toESM(require("path"));
|
|
4149
3836
|
var import_child_process5 = require("child_process");
|
|
4150
3837
|
var import_core6 = __toESM(require_dist());
|
|
4151
3838
|
|
|
4152
3839
|
// src/daemon/machine-id.ts
|
|
4153
|
-
var
|
|
4154
|
-
var
|
|
3840
|
+
var fs5 = __toESM(require("fs"));
|
|
3841
|
+
var path6 = __toESM(require("path"));
|
|
4155
3842
|
var crypto2 = __toESM(require("crypto"));
|
|
4156
3843
|
var import_child_process4 = require("child_process");
|
|
4157
3844
|
var import_core5 = __toESM(require_dist());
|
|
@@ -4160,17 +3847,17 @@ function isValidUUID(str) {
|
|
|
4160
3847
|
return uuidRegex.test(str);
|
|
4161
3848
|
}
|
|
4162
3849
|
async function getMachineId() {
|
|
4163
|
-
const machineIdPath =
|
|
3850
|
+
const machineIdPath = path6.join((0, import_core5.getConfigDir)(), "machine-id");
|
|
4164
3851
|
try {
|
|
4165
|
-
if (
|
|
4166
|
-
const existingId =
|
|
3852
|
+
if (fs5.existsSync(machineIdPath)) {
|
|
3853
|
+
const existingId = fs5.readFileSync(machineIdPath, "utf-8").trim();
|
|
4167
3854
|
if (existingId) {
|
|
4168
3855
|
if (isValidUUID(existingId)) {
|
|
4169
3856
|
return existingId;
|
|
4170
3857
|
}
|
|
4171
3858
|
console.log("[MachineId] Migrating legacy machine ID to UUID format...");
|
|
4172
3859
|
const newUUID = generateMachineId();
|
|
4173
|
-
|
|
3860
|
+
fs5.writeFileSync(machineIdPath, newUUID, "utf-8");
|
|
4174
3861
|
console.log(`[MachineId] Migrated: ${existingId} \u2192 ${newUUID}`);
|
|
4175
3862
|
return newUUID;
|
|
4176
3863
|
}
|
|
@@ -4179,11 +3866,11 @@ async function getMachineId() {
|
|
|
4179
3866
|
}
|
|
4180
3867
|
const machineId = generateMachineId();
|
|
4181
3868
|
try {
|
|
4182
|
-
const dir =
|
|
4183
|
-
if (!
|
|
4184
|
-
|
|
3869
|
+
const dir = path6.dirname(machineIdPath);
|
|
3870
|
+
if (!fs5.existsSync(dir)) {
|
|
3871
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
4185
3872
|
}
|
|
4186
|
-
|
|
3873
|
+
fs5.writeFileSync(machineIdPath, machineId, "utf-8");
|
|
4187
3874
|
} catch (error) {
|
|
4188
3875
|
console.error("Warning: Could not save machine ID to disk:", error);
|
|
4189
3876
|
}
|
|
@@ -4200,14 +3887,14 @@ function getHardwareUUID() {
|
|
|
4200
3887
|
return output;
|
|
4201
3888
|
}
|
|
4202
3889
|
} else if (process.platform === "linux") {
|
|
4203
|
-
if (
|
|
4204
|
-
const machineId =
|
|
3890
|
+
if (fs5.existsSync("/etc/machine-id")) {
|
|
3891
|
+
const machineId = fs5.readFileSync("/etc/machine-id", "utf-8").trim();
|
|
4205
3892
|
if (machineId && machineId.length > 0) {
|
|
4206
3893
|
return machineId;
|
|
4207
3894
|
}
|
|
4208
3895
|
}
|
|
4209
|
-
if (
|
|
4210
|
-
const dbusId =
|
|
3896
|
+
if (fs5.existsSync("/var/lib/dbus/machine-id")) {
|
|
3897
|
+
const dbusId = fs5.readFileSync("/var/lib/dbus/machine-id", "utf-8").trim();
|
|
4211
3898
|
if (dbusId && dbusId.length > 0) {
|
|
4212
3899
|
return dbusId;
|
|
4213
3900
|
}
|
|
@@ -4767,13 +4454,13 @@ function openBrowser(url) {
|
|
|
4767
4454
|
async function installGitCredentialHelper(apiUrl) {
|
|
4768
4455
|
try {
|
|
4769
4456
|
const homeDir = os.homedir();
|
|
4770
|
-
const episodaBinDir =
|
|
4771
|
-
const helperPath =
|
|
4772
|
-
|
|
4457
|
+
const episodaBinDir = path7.join(homeDir, ".episoda", "bin");
|
|
4458
|
+
const helperPath = path7.join(episodaBinDir, "git-credential-episoda");
|
|
4459
|
+
fs6.mkdirSync(episodaBinDir, { recursive: true });
|
|
4773
4460
|
const scriptContent = generateCredentialHelperScript(apiUrl);
|
|
4774
|
-
|
|
4461
|
+
fs6.writeFileSync(helperPath, scriptContent, { mode: 493 });
|
|
4775
4462
|
try {
|
|
4776
|
-
|
|
4463
|
+
fs6.accessSync(helperPath, fs6.constants.X_OK);
|
|
4777
4464
|
} catch {
|
|
4778
4465
|
}
|
|
4779
4466
|
try {
|
|
@@ -4804,19 +4491,19 @@ function updateShellProfile(binDir) {
|
|
|
4804
4491
|
}
|
|
4805
4492
|
const homeDir = os.homedir();
|
|
4806
4493
|
const profiles = [
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4494
|
+
path7.join(homeDir, ".bashrc"),
|
|
4495
|
+
path7.join(homeDir, ".zshrc"),
|
|
4496
|
+
path7.join(homeDir, ".profile")
|
|
4810
4497
|
];
|
|
4811
4498
|
const exportLine = `export PATH="${binDir}:$PATH" # Added by episoda auth`;
|
|
4812
4499
|
for (const profile of profiles) {
|
|
4813
4500
|
try {
|
|
4814
|
-
if (
|
|
4815
|
-
const content =
|
|
4501
|
+
if (fs6.existsSync(profile)) {
|
|
4502
|
+
const content = fs6.readFileSync(profile, "utf8");
|
|
4816
4503
|
if (content.includes(".episoda/bin")) {
|
|
4817
4504
|
continue;
|
|
4818
4505
|
}
|
|
4819
|
-
|
|
4506
|
+
fs6.appendFileSync(profile, `
|
|
4820
4507
|
# Episoda CLI
|
|
4821
4508
|
${exportLine}
|
|
4822
4509
|
`);
|
|
@@ -4828,8 +4515,8 @@ ${exportLine}
|
|
|
4828
4515
|
|
|
4829
4516
|
// src/commands/connect.ts
|
|
4830
4517
|
var os2 = __toESM(require("os"));
|
|
4831
|
-
var
|
|
4832
|
-
var
|
|
4518
|
+
var fs7 = __toESM(require("fs"));
|
|
4519
|
+
var path8 = __toESM(require("path"));
|
|
4833
4520
|
var import_child_process6 = require("child_process");
|
|
4834
4521
|
var import_core7 = __toESM(require_dist());
|
|
4835
4522
|
async function connectCommand(options) {
|
|
@@ -4904,11 +4591,11 @@ async function exchangeUserCode(apiUrl, userCode, machineId) {
|
|
|
4904
4591
|
async function installGitCredentialHelper2(apiUrl) {
|
|
4905
4592
|
try {
|
|
4906
4593
|
const homeDir = os2.homedir();
|
|
4907
|
-
const episodaBinDir =
|
|
4908
|
-
const helperPath =
|
|
4909
|
-
|
|
4594
|
+
const episodaBinDir = path8.join(homeDir, ".episoda", "bin");
|
|
4595
|
+
const helperPath = path8.join(episodaBinDir, "git-credential-episoda");
|
|
4596
|
+
fs7.mkdirSync(episodaBinDir, { recursive: true });
|
|
4910
4597
|
const scriptContent = generateCredentialHelperScript(apiUrl);
|
|
4911
|
-
|
|
4598
|
+
fs7.writeFileSync(helperPath, scriptContent, { mode: 493 });
|
|
4912
4599
|
try {
|
|
4913
4600
|
const allHelpers = (0, import_child_process6.execSync)("git config --global --get-all credential.helper", {
|
|
4914
4601
|
encoding: "utf8",
|
|
@@ -4937,19 +4624,19 @@ function updateShellProfile2(binDir) {
|
|
|
4937
4624
|
}
|
|
4938
4625
|
const homeDir = os2.homedir();
|
|
4939
4626
|
const profiles = [
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4627
|
+
path8.join(homeDir, ".bashrc"),
|
|
4628
|
+
path8.join(homeDir, ".zshrc"),
|
|
4629
|
+
path8.join(homeDir, ".profile")
|
|
4943
4630
|
];
|
|
4944
4631
|
const exportLine = `export PATH="${binDir}:$PATH" # Added by episoda`;
|
|
4945
4632
|
for (const profile of profiles) {
|
|
4946
4633
|
try {
|
|
4947
|
-
if (
|
|
4948
|
-
const content =
|
|
4634
|
+
if (fs7.existsSync(profile)) {
|
|
4635
|
+
const content = fs7.readFileSync(profile, "utf8");
|
|
4949
4636
|
if (content.includes(".episoda/bin")) {
|
|
4950
4637
|
continue;
|
|
4951
4638
|
}
|
|
4952
|
-
|
|
4639
|
+
fs7.appendFileSync(profile, `
|
|
4953
4640
|
# Episoda CLI
|
|
4954
4641
|
${exportLine}
|
|
4955
4642
|
`);
|
|
@@ -5251,24 +4938,24 @@ async function stopCommand(options = {}) {
|
|
|
5251
4938
|
}
|
|
5252
4939
|
|
|
5253
4940
|
// src/commands/clone.ts
|
|
5254
|
-
var
|
|
5255
|
-
var
|
|
4941
|
+
var fs9 = __toESM(require("fs"));
|
|
4942
|
+
var path10 = __toESM(require("path"));
|
|
5256
4943
|
var import_core10 = __toESM(require_dist());
|
|
5257
4944
|
|
|
5258
4945
|
// src/daemon/project-tracker.ts
|
|
5259
|
-
var
|
|
5260
|
-
var
|
|
4946
|
+
var fs8 = __toESM(require("fs"));
|
|
4947
|
+
var path9 = __toESM(require("path"));
|
|
5261
4948
|
var import_core9 = __toESM(require_dist());
|
|
5262
4949
|
function getProjectsFilePath() {
|
|
5263
|
-
return
|
|
4950
|
+
return path9.join((0, import_core9.getConfigDir)(), "projects.json");
|
|
5264
4951
|
}
|
|
5265
4952
|
function readProjects() {
|
|
5266
4953
|
const projectsPath = getProjectsFilePath();
|
|
5267
4954
|
try {
|
|
5268
|
-
if (!
|
|
4955
|
+
if (!fs8.existsSync(projectsPath)) {
|
|
5269
4956
|
return { projects: [] };
|
|
5270
4957
|
}
|
|
5271
|
-
const content =
|
|
4958
|
+
const content = fs8.readFileSync(projectsPath, "utf-8");
|
|
5272
4959
|
const data = JSON.parse(content);
|
|
5273
4960
|
if (!data.projects || !Array.isArray(data.projects)) {
|
|
5274
4961
|
console.warn("Invalid projects.json structure, resetting");
|
|
@@ -5283,11 +4970,11 @@ function readProjects() {
|
|
|
5283
4970
|
function writeProjects(data) {
|
|
5284
4971
|
const projectsPath = getProjectsFilePath();
|
|
5285
4972
|
try {
|
|
5286
|
-
const dir =
|
|
5287
|
-
if (!
|
|
5288
|
-
|
|
4973
|
+
const dir = path9.dirname(projectsPath);
|
|
4974
|
+
if (!fs8.existsSync(dir)) {
|
|
4975
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
5289
4976
|
}
|
|
5290
|
-
|
|
4977
|
+
fs8.writeFileSync(projectsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
5291
4978
|
} catch (error) {
|
|
5292
4979
|
throw new Error(`Failed to write projects.json: ${error}`);
|
|
5293
4980
|
}
|
|
@@ -5311,7 +4998,7 @@ function addProject2(projectId, projectPath, options) {
|
|
|
5311
4998
|
console.log(`[ProjectTracker] Replacing project entry: ${existingById.path} -> ${projectPath}`);
|
|
5312
4999
|
data.projects.splice(existingByIdIndex, 1);
|
|
5313
5000
|
}
|
|
5314
|
-
const projectName =
|
|
5001
|
+
const projectName = path9.basename(projectPath);
|
|
5315
5002
|
const newProject = {
|
|
5316
5003
|
id: projectId,
|
|
5317
5004
|
path: projectPath,
|
|
@@ -5345,9 +5032,9 @@ async function cloneCommand(slugArg, options = {}) {
|
|
|
5345
5032
|
}
|
|
5346
5033
|
const apiUrl = options.apiUrl || config.api_url || "https://episoda.dev";
|
|
5347
5034
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
5348
|
-
if (
|
|
5349
|
-
const bareRepoPath =
|
|
5350
|
-
if (
|
|
5035
|
+
if (fs9.existsSync(projectPath)) {
|
|
5036
|
+
const bareRepoPath = path10.join(projectPath, ".bare");
|
|
5037
|
+
if (fs9.existsSync(bareRepoPath)) {
|
|
5351
5038
|
status.warning(`Project already cloned at ${projectPath}`);
|
|
5352
5039
|
status.info("");
|
|
5353
5040
|
status.info("Next steps:");
|
|
@@ -5379,7 +5066,7 @@ Please configure a repository in the project settings on episoda.dev.`
|
|
|
5379
5066
|
status.info("");
|
|
5380
5067
|
status.info("Creating project directory...");
|
|
5381
5068
|
const episodaRoot = getEpisodaRoot();
|
|
5382
|
-
|
|
5069
|
+
fs9.mkdirSync(projectPath, { recursive: true });
|
|
5383
5070
|
status.success(`\u2713 Created ${projectPath}`);
|
|
5384
5071
|
status.info("Cloning repository (bare)...");
|
|
5385
5072
|
try {
|
|
@@ -5417,9 +5104,9 @@ Please configure a repository in the project settings on episoda.dev.`
|
|
|
5417
5104
|
status.info(" 3. episoda checkout {moduleUid} # e.g., episoda checkout EP100");
|
|
5418
5105
|
status.info("");
|
|
5419
5106
|
} catch (error) {
|
|
5420
|
-
if (
|
|
5107
|
+
if (fs9.existsSync(projectPath)) {
|
|
5421
5108
|
try {
|
|
5422
|
-
|
|
5109
|
+
fs9.rmSync(projectPath, { recursive: true, force: true });
|
|
5423
5110
|
} catch {
|
|
5424
5111
|
}
|
|
5425
5112
|
}
|
|
@@ -5520,8 +5207,8 @@ async function fetchWithRetry(url, options, maxRetries = 3) {
|
|
|
5520
5207
|
}
|
|
5521
5208
|
|
|
5522
5209
|
// src/utils/env-setup.ts
|
|
5523
|
-
var
|
|
5524
|
-
var
|
|
5210
|
+
var fs10 = __toESM(require("fs"));
|
|
5211
|
+
var path11 = __toESM(require("path"));
|
|
5525
5212
|
async function fetchEnvVars(apiUrl, accessToken) {
|
|
5526
5213
|
try {
|
|
5527
5214
|
const url = `${apiUrl}/api/cli/env-vars`;
|
|
@@ -5556,8 +5243,8 @@ function writeEnvFile(targetPath, envVars) {
|
|
|
5556
5243
|
}
|
|
5557
5244
|
return `${key}=${value}`;
|
|
5558
5245
|
}).join("\n") + "\n";
|
|
5559
|
-
const envPath =
|
|
5560
|
-
|
|
5246
|
+
const envPath = path11.join(targetPath, ".env");
|
|
5247
|
+
fs10.writeFileSync(envPath, envContent, { mode: 384 });
|
|
5561
5248
|
console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
|
|
5562
5249
|
}
|
|
5563
5250
|
async function setupWorktreeEnv(worktreePath, apiUrl, accessToken) {
|
|
@@ -5735,7 +5422,7 @@ async function updateModuleCheckout(apiUrl, moduleId, accessToken, branchName) {
|
|
|
5735
5422
|
}
|
|
5736
5423
|
|
|
5737
5424
|
// src/commands/release.ts
|
|
5738
|
-
var
|
|
5425
|
+
var path12 = __toESM(require("path"));
|
|
5739
5426
|
var import_core12 = __toESM(require_dist());
|
|
5740
5427
|
async function releaseCommand(moduleUid, options = {}) {
|
|
5741
5428
|
if (!moduleUid || !moduleUid.match(/^EP\d+$/)) {
|
|
@@ -5782,7 +5469,7 @@ Commit or stash your changes first, or use --force to discard them.`
|
|
|
5782
5469
|
);
|
|
5783
5470
|
}
|
|
5784
5471
|
}
|
|
5785
|
-
const currentPath =
|
|
5472
|
+
const currentPath = path12.resolve(process.cwd());
|
|
5786
5473
|
if (currentPath.startsWith(existing.worktreePath)) {
|
|
5787
5474
|
status.warning("You are inside the worktree being released.");
|
|
5788
5475
|
status.info(`Please cd to ${projectRoot} first.`);
|
|
@@ -5856,8 +5543,8 @@ async function updateModuleRelease(apiUrl, projectId, workspaceSlug, moduleUid,
|
|
|
5856
5543
|
}
|
|
5857
5544
|
|
|
5858
5545
|
// src/commands/list.ts
|
|
5859
|
-
var
|
|
5860
|
-
var
|
|
5546
|
+
var fs11 = __toESM(require("fs"));
|
|
5547
|
+
var path13 = __toESM(require("path"));
|
|
5861
5548
|
var import_chalk2 = __toESM(require("chalk"));
|
|
5862
5549
|
var import_core13 = __toESM(require_dist());
|
|
5863
5550
|
async function listCommand(subcommand, options = {}) {
|
|
@@ -5869,7 +5556,7 @@ async function listCommand(subcommand, options = {}) {
|
|
|
5869
5556
|
}
|
|
5870
5557
|
async function listProjects(options) {
|
|
5871
5558
|
const episodaRoot = getEpisodaRoot();
|
|
5872
|
-
if (!
|
|
5559
|
+
if (!fs11.existsSync(episodaRoot)) {
|
|
5873
5560
|
status.info("No projects cloned yet.");
|
|
5874
5561
|
status.info("");
|
|
5875
5562
|
status.info("Clone a project with:");
|
|
@@ -5877,12 +5564,12 @@ async function listProjects(options) {
|
|
|
5877
5564
|
return;
|
|
5878
5565
|
}
|
|
5879
5566
|
const projects = [];
|
|
5880
|
-
const workspaces =
|
|
5567
|
+
const workspaces = fs11.readdirSync(episodaRoot, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
|
|
5881
5568
|
for (const workspace of workspaces) {
|
|
5882
|
-
const workspacePath =
|
|
5883
|
-
const projectDirs =
|
|
5569
|
+
const workspacePath = path13.join(episodaRoot, workspace.name);
|
|
5570
|
+
const projectDirs = fs11.readdirSync(workspacePath, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
|
|
5884
5571
|
for (const projectDir of projectDirs) {
|
|
5885
|
-
const projectPath =
|
|
5572
|
+
const projectPath = path13.join(workspacePath, projectDir.name);
|
|
5886
5573
|
if (await isWorktreeProject(projectPath)) {
|
|
5887
5574
|
const manager = new WorktreeManager(projectPath);
|
|
5888
5575
|
await manager.initialize();
|
|
@@ -6122,26 +5809,26 @@ var import_child_process9 = require("child_process");
|
|
|
6122
5809
|
var import_core15 = __toESM(require_dist());
|
|
6123
5810
|
|
|
6124
5811
|
// src/utils/env-cache.ts
|
|
6125
|
-
var
|
|
6126
|
-
var
|
|
5812
|
+
var fs12 = __toESM(require("fs"));
|
|
5813
|
+
var path14 = __toESM(require("path"));
|
|
6127
5814
|
var os3 = __toESM(require("os"));
|
|
6128
5815
|
var DEFAULT_CACHE_TTL = 60;
|
|
6129
|
-
var CACHE_DIR =
|
|
5816
|
+
var CACHE_DIR = path14.join(os3.homedir(), ".episoda", "cache");
|
|
6130
5817
|
function getCacheFilePath(projectId) {
|
|
6131
|
-
return
|
|
5818
|
+
return path14.join(CACHE_DIR, `env-vars-${projectId}.json`);
|
|
6132
5819
|
}
|
|
6133
5820
|
function ensureCacheDir() {
|
|
6134
|
-
if (!
|
|
6135
|
-
|
|
5821
|
+
if (!fs12.existsSync(CACHE_DIR)) {
|
|
5822
|
+
fs12.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
|
|
6136
5823
|
}
|
|
6137
5824
|
}
|
|
6138
5825
|
function readCache(projectId) {
|
|
6139
5826
|
try {
|
|
6140
5827
|
const cacheFile = getCacheFilePath(projectId);
|
|
6141
|
-
if (!
|
|
5828
|
+
if (!fs12.existsSync(cacheFile)) {
|
|
6142
5829
|
return null;
|
|
6143
5830
|
}
|
|
6144
|
-
const content =
|
|
5831
|
+
const content = fs12.readFileSync(cacheFile, "utf-8");
|
|
6145
5832
|
const data = JSON.parse(content);
|
|
6146
5833
|
if (!data.vars || typeof data.vars !== "object" || !data.fetchedAt) {
|
|
6147
5834
|
return null;
|
|
@@ -6160,7 +5847,7 @@ function writeCache(projectId, vars) {
|
|
|
6160
5847
|
fetchedAt: Date.now(),
|
|
6161
5848
|
projectId
|
|
6162
5849
|
};
|
|
6163
|
-
|
|
5850
|
+
fs12.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
6164
5851
|
} catch (error) {
|
|
6165
5852
|
console.warn("[env-cache] Failed to write cache:", error instanceof Error ? error.message : error);
|
|
6166
5853
|
}
|
|
@@ -6396,8 +6083,8 @@ function printExports(envVars) {
|
|
|
6396
6083
|
}
|
|
6397
6084
|
|
|
6398
6085
|
// src/commands/env.ts
|
|
6399
|
-
var
|
|
6400
|
-
var
|
|
6086
|
+
var fs13 = __toESM(require("fs"));
|
|
6087
|
+
var path15 = __toESM(require("path"));
|
|
6401
6088
|
var readline = __toESM(require("readline"));
|
|
6402
6089
|
var import_core17 = __toESM(require_dist());
|
|
6403
6090
|
async function envListCommand(options = {}) {
|
|
@@ -6587,8 +6274,8 @@ async function envPullCommand(options = {}) {
|
|
|
6587
6274
|
return;
|
|
6588
6275
|
}
|
|
6589
6276
|
const filename = options.file || ".env";
|
|
6590
|
-
const filepath =
|
|
6591
|
-
|
|
6277
|
+
const filepath = path15.resolve(process.cwd(), filename);
|
|
6278
|
+
fs13.writeFileSync(filepath, fullContent, { mode: 384 });
|
|
6592
6279
|
status.success(`Wrote ${Object.keys(envVars).length} env vars to ${filename}`);
|
|
6593
6280
|
}
|
|
6594
6281
|
async function promptForValue(key) {
|
|
@@ -6661,18 +6348,24 @@ import_commander.program.command("connect").description("Connect using a pre-aut
|
|
|
6661
6348
|
process.exit(1);
|
|
6662
6349
|
}
|
|
6663
6350
|
});
|
|
6664
|
-
import_commander.program.command("
|
|
6351
|
+
import_commander.program.command("daemon").description("Connect project to Episoda daemon (dev servers start automatically)").option("--background", "Run in background and exit immediately").action(async (options) => {
|
|
6665
6352
|
try {
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6353
|
+
await daemonCommand({
|
|
6354
|
+
background: options.background
|
|
6355
|
+
});
|
|
6356
|
+
} catch (error) {
|
|
6357
|
+
status.error(`Daemon command failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6358
|
+
process.exit(1);
|
|
6359
|
+
}
|
|
6360
|
+
});
|
|
6361
|
+
import_commander.program.command("dev").description('Alias for "daemon" (deprecated)').option("--background", "Run in background and exit immediately").action(async (options) => {
|
|
6362
|
+
status.warning('Note: "episoda dev" is deprecated. Use "episoda daemon" instead.');
|
|
6363
|
+
try {
|
|
6364
|
+
await daemonCommand({
|
|
6365
|
+
background: options.background
|
|
6673
6366
|
});
|
|
6674
6367
|
} catch (error) {
|
|
6675
|
-
status.error(`
|
|
6368
|
+
status.error(`Command failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6676
6369
|
process.exit(1);
|
|
6677
6370
|
}
|
|
6678
6371
|
});
|