@episoda/cli 0.2.162 → 0.2.164
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 +238 -69
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +280 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -343,6 +343,8 @@ var require_git_executor = __commonJS({
|
|
|
343
343
|
case "delete_branch":
|
|
344
344
|
return await this.executeDeleteBranch(command, cwd, options);
|
|
345
345
|
// EP597: Read operations for production local dev mode
|
|
346
|
+
case "list_branches":
|
|
347
|
+
return await this.executeListBranches(cwd, options);
|
|
346
348
|
case "branch_exists":
|
|
347
349
|
return await this.executeBranchExists(command, cwd, options);
|
|
348
350
|
case "branch_has_commits":
|
|
@@ -632,6 +634,65 @@ var require_git_executor = __commonJS({
|
|
|
632
634
|
args.push(command.branch);
|
|
633
635
|
return await this.runGitCommand(args, cwd, options);
|
|
634
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* List local branches with remote-tracking signal and current branch marker.
|
|
639
|
+
*/
|
|
640
|
+
async executeListBranches(cwd, options) {
|
|
641
|
+
try {
|
|
642
|
+
const timeout = options?.timeout || 1e4;
|
|
643
|
+
const branchMap = /* @__PURE__ */ new Map();
|
|
644
|
+
const addBranch = (name, attrs) => {
|
|
645
|
+
const normalized = name.trim();
|
|
646
|
+
if (!normalized)
|
|
647
|
+
return;
|
|
648
|
+
const existing = branchMap.get(normalized);
|
|
649
|
+
if (existing) {
|
|
650
|
+
existing.current = existing.current || !!attrs?.current;
|
|
651
|
+
existing.remote = existing.remote || !!attrs?.remote;
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
branchMap.set(normalized, {
|
|
655
|
+
name: normalized,
|
|
656
|
+
current: !!attrs?.current,
|
|
657
|
+
remote: !!attrs?.remote
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
const statusResult = await this.executeStatus(cwd, options);
|
|
661
|
+
const currentBranch = statusResult.success ? statusResult.details?.branchName : void 0;
|
|
662
|
+
try {
|
|
663
|
+
const { stdout } = await execAsync(`git for-each-ref --format='%(refname:short)' refs/heads`, { cwd, timeout });
|
|
664
|
+
stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((name) => addBranch(name, { current: name === currentBranch }));
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
try {
|
|
668
|
+
const { stdout } = await execAsync(`git for-each-ref --format='%(refname:short)' refs/remotes/origin`, { cwd, timeout });
|
|
669
|
+
stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((refName) => {
|
|
670
|
+
if (refName === "origin/HEAD")
|
|
671
|
+
return;
|
|
672
|
+
const normalized = refName.startsWith("origin/") ? refName.slice("origin/".length) : refName;
|
|
673
|
+
addBranch(normalized, { remote: true, current: normalized === currentBranch });
|
|
674
|
+
});
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
if (currentBranch && currentBranch !== "HEAD") {
|
|
678
|
+
addBranch(currentBranch, { current: true });
|
|
679
|
+
}
|
|
680
|
+
const branches = Array.from(branchMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
681
|
+
return {
|
|
682
|
+
success: true,
|
|
683
|
+
details: {
|
|
684
|
+
branchName: currentBranch,
|
|
685
|
+
branches
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
} catch (error) {
|
|
689
|
+
return {
|
|
690
|
+
success: false,
|
|
691
|
+
error: "UNKNOWN_ERROR",
|
|
692
|
+
output: error.message || "Failed to list branches"
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
635
696
|
/**
|
|
636
697
|
* EP597: Execute branch_exists command
|
|
637
698
|
* Checks if a branch exists locally and/or remotely
|
|
@@ -1579,15 +1640,15 @@ var require_git_executor = __commonJS({
|
|
|
1579
1640
|
try {
|
|
1580
1641
|
const { stdout: gitDir } = await execAsync("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1581
1642
|
const gitDirPath = gitDir.trim();
|
|
1582
|
-
const
|
|
1643
|
+
const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1583
1644
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1584
1645
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1585
1646
|
try {
|
|
1586
|
-
await
|
|
1647
|
+
await fs16.access(rebaseMergePath);
|
|
1587
1648
|
inRebase = true;
|
|
1588
1649
|
} catch {
|
|
1589
1650
|
try {
|
|
1590
|
-
await
|
|
1651
|
+
await fs16.access(rebaseApplyPath);
|
|
1591
1652
|
inRebase = true;
|
|
1592
1653
|
} catch {
|
|
1593
1654
|
inRebase = false;
|
|
@@ -1643,9 +1704,9 @@ var require_git_executor = __commonJS({
|
|
|
1643
1704
|
};
|
|
1644
1705
|
}
|
|
1645
1706
|
}
|
|
1646
|
-
const
|
|
1707
|
+
const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1647
1708
|
try {
|
|
1648
|
-
await
|
|
1709
|
+
await fs16.access(command.path);
|
|
1649
1710
|
return {
|
|
1650
1711
|
success: false,
|
|
1651
1712
|
error: "WORKTREE_EXISTS",
|
|
@@ -1704,9 +1765,9 @@ var require_git_executor = __commonJS({
|
|
|
1704
1765
|
*/
|
|
1705
1766
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1706
1767
|
try {
|
|
1707
|
-
const
|
|
1768
|
+
const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1708
1769
|
try {
|
|
1709
|
-
await
|
|
1770
|
+
await fs16.access(command.path);
|
|
1710
1771
|
} catch {
|
|
1711
1772
|
return {
|
|
1712
1773
|
success: false,
|
|
@@ -1741,7 +1802,7 @@ var require_git_executor = __commonJS({
|
|
|
1741
1802
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1742
1803
|
if (result.success) {
|
|
1743
1804
|
try {
|
|
1744
|
-
await
|
|
1805
|
+
await fs16.rm(command.path, { recursive: true, force: true });
|
|
1745
1806
|
} catch {
|
|
1746
1807
|
}
|
|
1747
1808
|
return {
|
|
@@ -1875,10 +1936,10 @@ var require_git_executor = __commonJS({
|
|
|
1875
1936
|
*/
|
|
1876
1937
|
async executeCloneBare(command, options) {
|
|
1877
1938
|
try {
|
|
1878
|
-
const
|
|
1879
|
-
const
|
|
1939
|
+
const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1940
|
+
const path19 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1880
1941
|
try {
|
|
1881
|
-
await
|
|
1942
|
+
await fs16.access(command.path);
|
|
1882
1943
|
return {
|
|
1883
1944
|
success: false,
|
|
1884
1945
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1887,9 +1948,9 @@ var require_git_executor = __commonJS({
|
|
|
1887
1948
|
};
|
|
1888
1949
|
} catch {
|
|
1889
1950
|
}
|
|
1890
|
-
const parentDir =
|
|
1951
|
+
const parentDir = path19.dirname(command.path);
|
|
1891
1952
|
try {
|
|
1892
|
-
await
|
|
1953
|
+
await fs16.mkdir(parentDir, { recursive: true });
|
|
1893
1954
|
} catch {
|
|
1894
1955
|
}
|
|
1895
1956
|
const { stdout, stderr } = await execAsync(
|
|
@@ -1937,22 +1998,22 @@ var require_git_executor = __commonJS({
|
|
|
1937
1998
|
*/
|
|
1938
1999
|
async executeProjectInfo(cwd, options) {
|
|
1939
2000
|
try {
|
|
1940
|
-
const
|
|
1941
|
-
const
|
|
2001
|
+
const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
2002
|
+
const path19 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1942
2003
|
let currentPath = cwd;
|
|
1943
2004
|
let projectPath = cwd;
|
|
1944
2005
|
let bareRepoPath;
|
|
1945
2006
|
for (let i = 0; i < 10; i++) {
|
|
1946
|
-
const bareDir =
|
|
1947
|
-
const episodaDir =
|
|
2007
|
+
const bareDir = path19.join(currentPath, ".bare");
|
|
2008
|
+
const episodaDir = path19.join(currentPath, ".episoda");
|
|
1948
2009
|
try {
|
|
1949
|
-
await
|
|
1950
|
-
await
|
|
2010
|
+
await fs16.access(bareDir);
|
|
2011
|
+
await fs16.access(episodaDir);
|
|
1951
2012
|
projectPath = currentPath;
|
|
1952
2013
|
bareRepoPath = bareDir;
|
|
1953
2014
|
break;
|
|
1954
2015
|
} catch {
|
|
1955
|
-
const parentPath =
|
|
2016
|
+
const parentPath = path19.dirname(currentPath);
|
|
1956
2017
|
if (parentPath === currentPath) {
|
|
1957
2018
|
break;
|
|
1958
2019
|
}
|
|
@@ -2153,6 +2214,8 @@ var require_websocket_client = __commonJS({
|
|
|
2153
2214
|
this.osArch = deviceInfo?.osArch;
|
|
2154
2215
|
this.daemonPid = deviceInfo?.daemonPid;
|
|
2155
2216
|
this.cliVersion = deviceInfo?.cliVersion;
|
|
2217
|
+
this.cliPackageName = deviceInfo?.cliPackageName;
|
|
2218
|
+
this.capabilities = deviceInfo?.capabilities;
|
|
2156
2219
|
this.environment = deviceInfo?.environment;
|
|
2157
2220
|
this.containerId = deviceInfo?.containerId;
|
|
2158
2221
|
this.isDisconnecting = false;
|
|
@@ -2192,6 +2255,8 @@ var require_websocket_client = __commonJS({
|
|
|
2192
2255
|
type: "auth",
|
|
2193
2256
|
token,
|
|
2194
2257
|
version: this.cliVersion || version_1.VERSION,
|
|
2258
|
+
cliPackageName: this.cliPackageName,
|
|
2259
|
+
capabilities: this.capabilities,
|
|
2195
2260
|
environment: this.environment,
|
|
2196
2261
|
machineId,
|
|
2197
2262
|
containerId: this.containerId,
|
|
@@ -2511,6 +2576,8 @@ var require_websocket_client = __commonJS({
|
|
|
2511
2576
|
osArch: this.osArch,
|
|
2512
2577
|
daemonPid: this.daemonPid,
|
|
2513
2578
|
cliVersion: this.cliVersion,
|
|
2579
|
+
cliPackageName: this.cliPackageName,
|
|
2580
|
+
capabilities: this.capabilities,
|
|
2514
2581
|
environment: this.environment,
|
|
2515
2582
|
containerId: this.containerId
|
|
2516
2583
|
}).then(() => {
|
|
@@ -2602,38 +2669,38 @@ var require_auth = __commonJS({
|
|
|
2602
2669
|
};
|
|
2603
2670
|
})();
|
|
2604
2671
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2605
|
-
exports2.getConfigDir =
|
|
2672
|
+
exports2.getConfigDir = getConfigDir6;
|
|
2606
2673
|
exports2.getConfigPath = getConfigPath4;
|
|
2607
2674
|
exports2.loadConfig = loadConfig9;
|
|
2608
2675
|
exports2.saveConfig = saveConfig3;
|
|
2609
2676
|
exports2.validateToken = validateToken;
|
|
2610
|
-
var
|
|
2611
|
-
var
|
|
2677
|
+
var fs16 = __importStar(require("fs"));
|
|
2678
|
+
var path19 = __importStar(require("path"));
|
|
2612
2679
|
var os5 = __importStar(require("os"));
|
|
2613
2680
|
var child_process_1 = require("child_process");
|
|
2614
2681
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2615
2682
|
var hasWarnedMissingProjectId = false;
|
|
2616
2683
|
var hasWarnedMissingRequiredFields = false;
|
|
2617
|
-
function
|
|
2618
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2684
|
+
function getConfigDir6() {
|
|
2685
|
+
return process.env.EPISODA_CONFIG_DIR || path19.join(os5.homedir(), ".episoda");
|
|
2619
2686
|
}
|
|
2620
2687
|
function getConfigPath4(configPath) {
|
|
2621
2688
|
if (configPath) {
|
|
2622
2689
|
return configPath;
|
|
2623
2690
|
}
|
|
2624
|
-
return
|
|
2691
|
+
return path19.join(getConfigDir6(), DEFAULT_CONFIG_FILE);
|
|
2625
2692
|
}
|
|
2626
2693
|
function ensureConfigDir(configPath) {
|
|
2627
|
-
const dir =
|
|
2628
|
-
const isNew = !
|
|
2694
|
+
const dir = path19.dirname(configPath);
|
|
2695
|
+
const isNew = !fs16.existsSync(dir);
|
|
2629
2696
|
if (isNew) {
|
|
2630
|
-
|
|
2697
|
+
fs16.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2631
2698
|
}
|
|
2632
2699
|
if (process.platform === "darwin") {
|
|
2633
|
-
const nosyncPath =
|
|
2634
|
-
if (isNew || !
|
|
2700
|
+
const nosyncPath = path19.join(dir, ".nosync");
|
|
2701
|
+
if (isNew || !fs16.existsSync(nosyncPath)) {
|
|
2635
2702
|
try {
|
|
2636
|
-
|
|
2703
|
+
fs16.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2637
2704
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2638
2705
|
stdio: "ignore",
|
|
2639
2706
|
timeout: 5e3
|
|
@@ -2647,11 +2714,11 @@ var require_auth = __commonJS({
|
|
|
2647
2714
|
const fullPath = getConfigPath4(configPath);
|
|
2648
2715
|
const isCloudMode = process.env.EPISODA_MODE === "cloud";
|
|
2649
2716
|
const readConfigFile = (pathToFile) => {
|
|
2650
|
-
if (!
|
|
2717
|
+
if (!fs16.existsSync(pathToFile)) {
|
|
2651
2718
|
return null;
|
|
2652
2719
|
}
|
|
2653
2720
|
try {
|
|
2654
|
-
const content =
|
|
2721
|
+
const content = fs16.readFileSync(pathToFile, "utf8");
|
|
2655
2722
|
return JSON.parse(content);
|
|
2656
2723
|
} catch (error) {
|
|
2657
2724
|
console.error("Error loading config:", error);
|
|
@@ -2690,11 +2757,11 @@ var require_auth = __commonJS({
|
|
|
2690
2757
|
}
|
|
2691
2758
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2692
2759
|
const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
|
|
2693
|
-
if (!
|
|
2760
|
+
if (!fs16.existsSync(workspaceConfigPath)) {
|
|
2694
2761
|
return null;
|
|
2695
2762
|
}
|
|
2696
2763
|
try {
|
|
2697
|
-
const content =
|
|
2764
|
+
const content = fs16.readFileSync(workspaceConfigPath, "utf8");
|
|
2698
2765
|
const workspaceConfig2 = JSON.parse(content);
|
|
2699
2766
|
const expiresAtEnv = envValue(process.env.EPISODA_ACCESS_TOKEN_EXPIRES_AT);
|
|
2700
2767
|
return {
|
|
@@ -2722,11 +2789,11 @@ var require_auth = __commonJS({
|
|
|
2722
2789
|
}
|
|
2723
2790
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2724
2791
|
const projectConfigPath = require("path").join(homeDir, "episoda", workspaceSlug, projectSlug, ".episoda", "config.json");
|
|
2725
|
-
if (!
|
|
2792
|
+
if (!fs16.existsSync(projectConfigPath)) {
|
|
2726
2793
|
return null;
|
|
2727
2794
|
}
|
|
2728
2795
|
try {
|
|
2729
|
-
const content =
|
|
2796
|
+
const content = fs16.readFileSync(projectConfigPath, "utf8");
|
|
2730
2797
|
const projectConfig2 = JSON.parse(content);
|
|
2731
2798
|
return {
|
|
2732
2799
|
project_id: projectConfig2.projectId || projectConfig2.project_id,
|
|
@@ -2794,7 +2861,7 @@ var require_auth = __commonJS({
|
|
|
2794
2861
|
ensureConfigDir(fullPath);
|
|
2795
2862
|
try {
|
|
2796
2863
|
const content = JSON.stringify(config, null, 2);
|
|
2797
|
-
|
|
2864
|
+
fs16.writeFileSync(fullPath, content, { mode: 384 });
|
|
2798
2865
|
} catch (error) {
|
|
2799
2866
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2800
2867
|
}
|
|
@@ -2908,7 +2975,7 @@ var require_dist = __commonJS({
|
|
|
2908
2975
|
|
|
2909
2976
|
// src/index.ts
|
|
2910
2977
|
var import_commander = require("commander");
|
|
2911
|
-
var
|
|
2978
|
+
var import_core19 = __toESM(require_dist());
|
|
2912
2979
|
|
|
2913
2980
|
// src/commands/daemon.ts
|
|
2914
2981
|
var import_core4 = __toESM(require_dist());
|
|
@@ -2982,6 +3049,21 @@ function killAllEpisodaProcesses() {
|
|
|
2982
3049
|
function getPidFilePath() {
|
|
2983
3050
|
return path.join((0, import_core.getConfigDir)(), "daemon.pid");
|
|
2984
3051
|
}
|
|
3052
|
+
function looksLikeEpisodaDaemonProcess(pid) {
|
|
3053
|
+
if (process.platform === "win32") {
|
|
3054
|
+
return true;
|
|
3055
|
+
}
|
|
3056
|
+
try {
|
|
3057
|
+
const command = (0, import_child_process.execSync)(`ps -p ${pid} -o command=`, {
|
|
3058
|
+
encoding: "utf-8",
|
|
3059
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3060
|
+
timeout: 3e3
|
|
3061
|
+
}).trim();
|
|
3062
|
+
return command.includes("daemon-process");
|
|
3063
|
+
} catch {
|
|
3064
|
+
return true;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
2985
3067
|
function isDaemonRunning() {
|
|
2986
3068
|
const pidPath = getPidFilePath();
|
|
2987
3069
|
try {
|
|
@@ -2996,6 +3078,10 @@ function isDaemonRunning() {
|
|
|
2996
3078
|
}
|
|
2997
3079
|
try {
|
|
2998
3080
|
process.kill(pid, 0);
|
|
3081
|
+
if (!looksLikeEpisodaDaemonProcess(pid)) {
|
|
3082
|
+
fs.unlinkSync(pidPath);
|
|
3083
|
+
return null;
|
|
3084
|
+
}
|
|
2999
3085
|
return pid;
|
|
3000
3086
|
} catch (error) {
|
|
3001
3087
|
fs.unlinkSync(pidPath);
|
|
@@ -3654,8 +3740,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3654
3740
|
const allWorktrees = this.listWorktrees();
|
|
3655
3741
|
const activeSet = new Set(activeModuleUids);
|
|
3656
3742
|
const orphaned = allWorktrees.filter((w) => !activeSet.has(w.moduleUid));
|
|
3657
|
-
const
|
|
3658
|
-
return { orphaned, valid };
|
|
3743
|
+
const valid2 = allWorktrees.filter((w) => activeSet.has(w.moduleUid));
|
|
3744
|
+
return { orphaned, valid: valid2 };
|
|
3659
3745
|
}
|
|
3660
3746
|
/**
|
|
3661
3747
|
* Update last accessed timestamp for a worktree
|
|
@@ -3690,7 +3776,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3690
3776
|
*/
|
|
3691
3777
|
async validateWorktrees() {
|
|
3692
3778
|
const config = this.readConfig();
|
|
3693
|
-
const
|
|
3779
|
+
const valid2 = [];
|
|
3694
3780
|
const stale = [];
|
|
3695
3781
|
const orphaned = [];
|
|
3696
3782
|
const listResult = await this.gitExecutor.execute({
|
|
@@ -3701,7 +3787,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3701
3787
|
);
|
|
3702
3788
|
for (const worktree of config?.worktrees || []) {
|
|
3703
3789
|
if (actualWorktrees.has(worktree.worktreePath)) {
|
|
3704
|
-
|
|
3790
|
+
valid2.push(worktree);
|
|
3705
3791
|
actualWorktrees.delete(worktree.worktreePath);
|
|
3706
3792
|
} else {
|
|
3707
3793
|
stale.push(worktree);
|
|
@@ -3712,7 +3798,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3712
3798
|
orphaned.push(wpath);
|
|
3713
3799
|
}
|
|
3714
3800
|
}
|
|
3715
|
-
return { valid, stale, orphaned };
|
|
3801
|
+
return { valid: valid2, stale, orphaned };
|
|
3716
3802
|
}
|
|
3717
3803
|
/**
|
|
3718
3804
|
* EP1190: Clean up non-module worktrees (like 'main')
|
|
@@ -4165,7 +4251,7 @@ async function fetchProjectPath(config, projectId) {
|
|
|
4165
4251
|
return null;
|
|
4166
4252
|
}
|
|
4167
4253
|
}
|
|
4168
|
-
async function syncProjectPath(config, projectId,
|
|
4254
|
+
async function syncProjectPath(config, projectId, path19) {
|
|
4169
4255
|
const machineUuid = config.machine_uuid || config.device_id;
|
|
4170
4256
|
if (!machineUuid || !config.access_token) {
|
|
4171
4257
|
return false;
|
|
@@ -4179,7 +4265,7 @@ async function syncProjectPath(config, projectId, path18) {
|
|
|
4179
4265
|
"Authorization": `Bearer ${config.access_token}`,
|
|
4180
4266
|
"Content-Type": "application/json"
|
|
4181
4267
|
},
|
|
4182
|
-
body: JSON.stringify({ path:
|
|
4268
|
+
body: JSON.stringify({ path: path19 })
|
|
4183
4269
|
});
|
|
4184
4270
|
if (!response.ok) {
|
|
4185
4271
|
console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`);
|
|
@@ -4492,14 +4578,12 @@ async function daemonCommand(options = {}) {
|
|
|
4492
4578
|
if (health.healthyConnections > 0) {
|
|
4493
4579
|
status.debug(`Daemon already running and healthy (PID: ${existingPid})`);
|
|
4494
4580
|
} else if (health.staleConnections > 0) {
|
|
4495
|
-
status.
|
|
4496
|
-
needsRestart = true;
|
|
4581
|
+
status.warning("Daemon has stale connections; continuing without restart and reconnecting...");
|
|
4497
4582
|
} else {
|
|
4498
4583
|
status.debug(`Daemon running but no connections (PID: ${existingPid})`);
|
|
4499
4584
|
}
|
|
4500
4585
|
} catch {
|
|
4501
|
-
status.
|
|
4502
|
-
needsRestart = true;
|
|
4586
|
+
status.warning("Health check failed; continuing with reconnect flow...");
|
|
4503
4587
|
}
|
|
4504
4588
|
} else {
|
|
4505
4589
|
status.debug("Daemon not reachable via IPC, restarting...");
|
|
@@ -5488,6 +5572,7 @@ var import_core8 = __toESM(require_dist());
|
|
|
5488
5572
|
var import_child_process9 = require("child_process");
|
|
5489
5573
|
var semver = __toESM(require("semver"));
|
|
5490
5574
|
var PACKAGE_NAME = "@episoda/cli";
|
|
5575
|
+
var LEGACY_PACKAGE_NAME = "episoda";
|
|
5491
5576
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
5492
5577
|
function isFileLinkedInstall() {
|
|
5493
5578
|
try {
|
|
@@ -5621,6 +5706,39 @@ function getInstalledVersion() {
|
|
|
5621
5706
|
return null;
|
|
5622
5707
|
}
|
|
5623
5708
|
}
|
|
5709
|
+
function getLegacyInstalledVersion() {
|
|
5710
|
+
try {
|
|
5711
|
+
const output = (0, import_child_process9.execSync)(`npm list -g ${LEGACY_PACKAGE_NAME} --json`, {
|
|
5712
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5713
|
+
timeout: 1e4
|
|
5714
|
+
}).toString();
|
|
5715
|
+
const data = JSON.parse(output);
|
|
5716
|
+
return data?.dependencies?.[LEGACY_PACKAGE_NAME]?.version || null;
|
|
5717
|
+
} catch {
|
|
5718
|
+
return null;
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
function detectCliInstallChannel(embeddedVersion) {
|
|
5722
|
+
const scopedVersion = getInstalledVersion();
|
|
5723
|
+
const legacyVersion = getLegacyInstalledVersion();
|
|
5724
|
+
const effectiveVersion = scopedVersion || legacyVersion || embeddedVersion || null;
|
|
5725
|
+
return {
|
|
5726
|
+
scopedVersion,
|
|
5727
|
+
legacyVersion,
|
|
5728
|
+
legacyOnly: !scopedVersion && !!legacyVersion,
|
|
5729
|
+
effectiveVersion
|
|
5730
|
+
};
|
|
5731
|
+
}
|
|
5732
|
+
function resolveEffectiveCliVersion(embeddedVersion) {
|
|
5733
|
+
const installedVersion = detectCliInstallChannel(embeddedVersion).effectiveVersion;
|
|
5734
|
+
if (!installedVersion) {
|
|
5735
|
+
return embeddedVersion;
|
|
5736
|
+
}
|
|
5737
|
+
if (semver.valid(installedVersion) && semver.valid(embeddedVersion)) {
|
|
5738
|
+
return semver.gt(installedVersion, embeddedVersion) ? installedVersion : embeddedVersion;
|
|
5739
|
+
}
|
|
5740
|
+
return installedVersion === embeddedVersion ? embeddedVersion : installedVersion;
|
|
5741
|
+
}
|
|
5624
5742
|
|
|
5625
5743
|
// src/utils/github-token.ts
|
|
5626
5744
|
var INVALID_GITHUB_TOKEN_LITERALS = /* @__PURE__ */ new Set([
|
|
@@ -5657,12 +5775,14 @@ async function statusCommand(options = {}) {
|
|
|
5657
5775
|
status.info("Configuration:");
|
|
5658
5776
|
status.info(` Project ID: ${config.project_id}`);
|
|
5659
5777
|
status.info(` API URL: ${config.api_url}`);
|
|
5778
|
+
const effectiveCliVersion = resolveEffectiveCliVersion(import_core8.VERSION);
|
|
5779
|
+
const installChannel = detectCliInstallChannel(import_core8.VERSION);
|
|
5660
5780
|
if (!options.skipUpdateCheck) {
|
|
5661
|
-
const updateResult = await checkForUpdates(
|
|
5781
|
+
const updateResult = await checkForUpdates(effectiveCliVersion);
|
|
5662
5782
|
if (updateResult.isLinked) {
|
|
5663
|
-
status.info(` CLI Version: ${
|
|
5783
|
+
status.info(` CLI Version: ${effectiveCliVersion} (linked)`);
|
|
5664
5784
|
} else if (updateResult.updateAvailable) {
|
|
5665
|
-
status.info(` CLI Version: ${
|
|
5785
|
+
status.info(` CLI Version: ${effectiveCliVersion} \u2192 ${updateResult.latestVersion} (updating...)`);
|
|
5666
5786
|
const result = await updateAndRestartDaemon(updateResult.latestVersion);
|
|
5667
5787
|
if (result.updateSuccess && result.restartSuccess) {
|
|
5668
5788
|
status.success(` CLI Version: ${updateResult.latestVersion} \u2713 (updated & daemon restarted)`);
|
|
@@ -5673,13 +5793,17 @@ async function statusCommand(options = {}) {
|
|
|
5673
5793
|
} else if (result.updateSuccess) {
|
|
5674
5794
|
status.warning(` CLI Version: ${updateResult.latestVersion} (updated, restart manually with 'episoda dev')`);
|
|
5675
5795
|
} else {
|
|
5676
|
-
status.warning(` CLI Version: ${
|
|
5796
|
+
status.warning(` CLI Version: ${effectiveCliVersion} (update failed: ${result.error})`);
|
|
5677
5797
|
}
|
|
5678
5798
|
} else {
|
|
5679
|
-
status.info(` CLI Version: ${
|
|
5799
|
+
status.info(` CLI Version: ${effectiveCliVersion} \u2713`);
|
|
5680
5800
|
}
|
|
5681
5801
|
} else {
|
|
5682
|
-
status.info(` CLI Version: ${
|
|
5802
|
+
status.info(` CLI Version: ${effectiveCliVersion}`);
|
|
5803
|
+
}
|
|
5804
|
+
if (installChannel.legacyOnly) {
|
|
5805
|
+
status.warning(` \u26A0 Legacy package channel detected (episoda@${installChannel.legacyVersion})`);
|
|
5806
|
+
status.info(" Migrate with: npm install -g @episoda/cli@latest");
|
|
5683
5807
|
}
|
|
5684
5808
|
status.info(` Config file: ${(0, import_core8.getConfigPath)()}`);
|
|
5685
5809
|
status.info("");
|
|
@@ -5694,8 +5818,15 @@ async function statusCommand(options = {}) {
|
|
|
5694
5818
|
} catch {
|
|
5695
5819
|
}
|
|
5696
5820
|
if (!daemonStatus) {
|
|
5697
|
-
|
|
5698
|
-
|
|
5821
|
+
const daemonPid = isDaemonRunning();
|
|
5822
|
+
if (daemonPid) {
|
|
5823
|
+
status.error("\u2717 Daemon process is running but IPC is not reachable");
|
|
5824
|
+
status.info(` PID: ${daemonPid}`);
|
|
5825
|
+
status.info(' Run "episoda dev" to re-establish IPC/connection (or "episoda stop && episoda dev").');
|
|
5826
|
+
} else {
|
|
5827
|
+
status.warning("\u26A0 Daemon not running");
|
|
5828
|
+
status.info(' Run "episoda dev" to start the daemon.');
|
|
5829
|
+
}
|
|
5699
5830
|
return;
|
|
5700
5831
|
}
|
|
5701
5832
|
const deviceDisplayName = daemonStatus.machineName || daemonStatus.hostname;
|
|
@@ -6588,11 +6719,11 @@ async function listWorktrees(options) {
|
|
|
6588
6719
|
throw new Error(`Invalid worktree project at ${projectRoot}`);
|
|
6589
6720
|
}
|
|
6590
6721
|
const config = manager.getConfig();
|
|
6591
|
-
const { valid, stale, orphaned } = await manager.validateWorktrees();
|
|
6722
|
+
const { valid: valid2, stale, orphaned } = await manager.validateWorktrees();
|
|
6592
6723
|
console.log("");
|
|
6593
6724
|
console.log(import_chalk2.default.bold(`Worktrees for ${config?.workspaceSlug}/${config?.projectSlug}`));
|
|
6594
6725
|
console.log("\u2500".repeat(60));
|
|
6595
|
-
const hasAnyWorktrees =
|
|
6726
|
+
const hasAnyWorktrees = valid2.length > 0 || stale.length > 0 || orphaned.length > 0;
|
|
6596
6727
|
if (!hasAnyWorktrees) {
|
|
6597
6728
|
console.log("");
|
|
6598
6729
|
console.log(import_chalk2.default.gray(" No worktrees checked out"));
|
|
@@ -6602,7 +6733,7 @@ async function listWorktrees(options) {
|
|
|
6602
6733
|
return;
|
|
6603
6734
|
}
|
|
6604
6735
|
const gitExecutor = new import_core13.GitExecutor();
|
|
6605
|
-
for (const wt of
|
|
6736
|
+
for (const wt of valid2) {
|
|
6606
6737
|
console.log("");
|
|
6607
6738
|
let statusIndicator = import_chalk2.default.green("\u25CF");
|
|
6608
6739
|
let statusNote = "";
|
|
@@ -6649,8 +6780,8 @@ async function listWorktrees(options) {
|
|
|
6649
6780
|
console.log("");
|
|
6650
6781
|
console.log(import_chalk2.default.gray("\u2500".repeat(60)));
|
|
6651
6782
|
const parts = [];
|
|
6652
|
-
if (
|
|
6653
|
-
parts.push(`${
|
|
6783
|
+
if (valid2.length > 0) {
|
|
6784
|
+
parts.push(`${valid2.length} active`);
|
|
6654
6785
|
}
|
|
6655
6786
|
if (stale.length > 0) {
|
|
6656
6787
|
parts.push(import_chalk2.default.yellow(`${stale.length} stale`));
|
|
@@ -6717,7 +6848,8 @@ function performUpdate(targetVersion) {
|
|
|
6717
6848
|
}
|
|
6718
6849
|
}
|
|
6719
6850
|
async function updateCommand(options = {}) {
|
|
6720
|
-
const currentVersion = import_core14.VERSION;
|
|
6851
|
+
const currentVersion = resolveEffectiveCliVersion(import_core14.VERSION);
|
|
6852
|
+
const installChannel = detectCliInstallChannel(import_core14.VERSION);
|
|
6721
6853
|
if (isFileLinkedInstall()) {
|
|
6722
6854
|
status.info(`Current version: ${currentVersion} (linked)`);
|
|
6723
6855
|
status.info("");
|
|
@@ -6727,6 +6859,10 @@ async function updateCommand(options = {}) {
|
|
|
6727
6859
|
return;
|
|
6728
6860
|
}
|
|
6729
6861
|
status.info(`Current version: ${currentVersion}`);
|
|
6862
|
+
if (installChannel.legacyOnly) {
|
|
6863
|
+
status.warning(`Legacy package channel detected: episoda@${installChannel.legacyVersion}`);
|
|
6864
|
+
status.info("Migrating to @episoda/cli will be handled by this update.");
|
|
6865
|
+
}
|
|
6730
6866
|
status.info("Checking for updates...");
|
|
6731
6867
|
const result = await checkForUpdates(currentVersion);
|
|
6732
6868
|
if (result.offline) {
|
|
@@ -7563,6 +7699,83 @@ async function migrationsFixCommand(options) {
|
|
|
7563
7699
|
status.success("Migration conflicts fixed.");
|
|
7564
7700
|
}
|
|
7565
7701
|
|
|
7702
|
+
// src/utils/legacy-channel-notice.ts
|
|
7703
|
+
var fs15 = __toESM(require("fs"));
|
|
7704
|
+
var path18 = __toESM(require("path"));
|
|
7705
|
+
var import_core18 = __toESM(require_dist());
|
|
7706
|
+
var LEGACY_NOTICE_FILE = "legacy-channel-notice.json";
|
|
7707
|
+
var LEGACY_NOTICE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
7708
|
+
var SUPPRESSED_COMMANDS = /* @__PURE__ */ new Set([
|
|
7709
|
+
"auth",
|
|
7710
|
+
"connect",
|
|
7711
|
+
"status",
|
|
7712
|
+
"update",
|
|
7713
|
+
"help",
|
|
7714
|
+
"migrations"
|
|
7715
|
+
]);
|
|
7716
|
+
function getNoticeFilePath() {
|
|
7717
|
+
return path18.join((0, import_core18.getConfigDir)(), LEGACY_NOTICE_FILE);
|
|
7718
|
+
}
|
|
7719
|
+
function getPrimaryCommand(argv) {
|
|
7720
|
+
const args = argv.slice(2);
|
|
7721
|
+
return args.find((arg) => arg.length > 0 && !arg.startsWith("-"));
|
|
7722
|
+
}
|
|
7723
|
+
function readNoticeRecord() {
|
|
7724
|
+
try {
|
|
7725
|
+
const raw = fs15.readFileSync(getNoticeFilePath(), "utf-8");
|
|
7726
|
+
const parsed = JSON.parse(raw);
|
|
7727
|
+
if (!parsed?.legacyVersion || typeof parsed.shownAt !== "number") {
|
|
7728
|
+
return null;
|
|
7729
|
+
}
|
|
7730
|
+
return parsed;
|
|
7731
|
+
} catch {
|
|
7732
|
+
return null;
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
function writeNoticeRecord(record) {
|
|
7736
|
+
try {
|
|
7737
|
+
fs15.mkdirSync((0, import_core18.getConfigDir)(), { recursive: true });
|
|
7738
|
+
fs15.writeFileSync(getNoticeFilePath(), JSON.stringify(record), "utf-8");
|
|
7739
|
+
} catch {
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
7742
|
+
function shouldShowLegacyNotice(input) {
|
|
7743
|
+
if (input.suppressEnv) return false;
|
|
7744
|
+
if (!input.command) return false;
|
|
7745
|
+
if (SUPPRESSED_COMMANDS.has(input.command)) return false;
|
|
7746
|
+
if (!input.legacyOnly || !input.legacyVersion) return false;
|
|
7747
|
+
if (!input.lastNotice) return true;
|
|
7748
|
+
if (input.lastNotice.legacyVersion !== input.legacyVersion) return true;
|
|
7749
|
+
if (input.now - input.lastNotice.shownAt > LEGACY_NOTICE_TTL_MS) return true;
|
|
7750
|
+
return false;
|
|
7751
|
+
}
|
|
7752
|
+
function maybeWarnLegacyInstallChannel(argv, embeddedVersion) {
|
|
7753
|
+
const command = getPrimaryCommand(argv);
|
|
7754
|
+
const channel = detectCliInstallChannel(embeddedVersion);
|
|
7755
|
+
const lastNotice = readNoticeRecord();
|
|
7756
|
+
const now = Date.now();
|
|
7757
|
+
const shouldShow = shouldShowLegacyNotice({
|
|
7758
|
+
command,
|
|
7759
|
+
legacyOnly: channel.legacyOnly,
|
|
7760
|
+
legacyVersion: channel.legacyVersion,
|
|
7761
|
+
lastNotice,
|
|
7762
|
+
now,
|
|
7763
|
+
suppressEnv: process.env.CI === "true" || process.env.EPISODA_SUPPRESS_LEGACY_NOTICE === "1"
|
|
7764
|
+
});
|
|
7765
|
+
if (!shouldShow || !channel.legacyVersion) {
|
|
7766
|
+
return;
|
|
7767
|
+
}
|
|
7768
|
+
status.warning(`Legacy CLI install channel detected: episoda@${channel.legacyVersion}`);
|
|
7769
|
+
status.info("Migrate to the supported package channel:");
|
|
7770
|
+
status.info(" npm install -g @episoda/cli@latest");
|
|
7771
|
+
status.info("Then restart daemon sessions with:");
|
|
7772
|
+
status.info(" episoda stop && episoda dev");
|
|
7773
|
+
writeNoticeRecord({
|
|
7774
|
+
legacyVersion: channel.legacyVersion,
|
|
7775
|
+
shownAt: now
|
|
7776
|
+
});
|
|
7777
|
+
}
|
|
7778
|
+
|
|
7566
7779
|
// src/index.ts
|
|
7567
7780
|
async function handleProtocolInvocation(rawUrl) {
|
|
7568
7781
|
const parsed = parseProtocolUrl(rawUrl);
|
|
@@ -7583,7 +7796,7 @@ if (protocolArg) {
|
|
|
7583
7796
|
process.exit(1);
|
|
7584
7797
|
});
|
|
7585
7798
|
}
|
|
7586
|
-
import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(
|
|
7799
|
+
import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core19.VERSION);
|
|
7587
7800
|
import_commander.program.command("auth").description("Authenticate to Episoda via OAuth and configure CLI").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (options) => {
|
|
7588
7801
|
try {
|
|
7589
7802
|
await authCommand(options);
|
|
@@ -7841,6 +8054,7 @@ envCmd.command("pull").description("Generate .env file from database (explicit f
|
|
|
7841
8054
|
}
|
|
7842
8055
|
});
|
|
7843
8056
|
if (!protocolArg) {
|
|
8057
|
+
maybeWarnLegacyInstallChannel(process.argv, import_core19.VERSION);
|
|
7844
8058
|
import_commander.program.parse();
|
|
7845
8059
|
}
|
|
7846
8060
|
//# sourceMappingURL=index.js.map
|