episoda 0.2.22 → 0.2.24
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 +101 -33
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +93 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -530,6 +530,24 @@ var require_git_executor = __commonJS({
|
|
|
530
530
|
* Execute status command
|
|
531
531
|
*/
|
|
532
532
|
async executeStatus(cwd, options) {
|
|
533
|
+
try {
|
|
534
|
+
const isBareResult = await execAsync("git rev-parse --is-bare-repository", { cwd, timeout: 5e3 });
|
|
535
|
+
if (isBareResult.stdout.trim() === "true") {
|
|
536
|
+
const headResult = await execAsync("git symbolic-ref --short HEAD", { cwd, timeout: 5e3 });
|
|
537
|
+
const branchName = headResult.stdout.trim();
|
|
538
|
+
return {
|
|
539
|
+
success: true,
|
|
540
|
+
output: `## ${branchName}`,
|
|
541
|
+
details: {
|
|
542
|
+
uncommittedFiles: [],
|
|
543
|
+
// No working tree in bare repo
|
|
544
|
+
branchName,
|
|
545
|
+
currentBranch: branchName
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
} catch {
|
|
550
|
+
}
|
|
533
551
|
const result = await this.runGitCommand(["status", "--porcelain", "-b"], cwd, options);
|
|
534
552
|
if (result.success && result.output) {
|
|
535
553
|
const statusInfo = (0, git_parser_1.parseGitStatus)(result.output);
|
|
@@ -1861,7 +1879,6 @@ var require_git_executor = __commonJS({
|
|
|
1861
1879
|
const fs11 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1862
1880
|
const path13 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1863
1881
|
let currentPath = cwd;
|
|
1864
|
-
let worktreeMode = false;
|
|
1865
1882
|
let projectPath = cwd;
|
|
1866
1883
|
let bareRepoPath;
|
|
1867
1884
|
for (let i = 0; i < 10; i++) {
|
|
@@ -1870,7 +1887,6 @@ var require_git_executor = __commonJS({
|
|
|
1870
1887
|
try {
|
|
1871
1888
|
await fs11.access(bareDir);
|
|
1872
1889
|
await fs11.access(episodaDir);
|
|
1873
|
-
worktreeMode = true;
|
|
1874
1890
|
projectPath = currentPath;
|
|
1875
1891
|
bareRepoPath = bareDir;
|
|
1876
1892
|
break;
|
|
@@ -1884,9 +1900,8 @@ var require_git_executor = __commonJS({
|
|
|
1884
1900
|
}
|
|
1885
1901
|
return {
|
|
1886
1902
|
success: true,
|
|
1887
|
-
output:
|
|
1903
|
+
output: bareRepoPath ? "Episoda project" : "Git repository",
|
|
1888
1904
|
details: {
|
|
1889
|
-
worktreeMode,
|
|
1890
1905
|
projectPath,
|
|
1891
1906
|
bareRepoPath
|
|
1892
1907
|
}
|
|
@@ -2468,7 +2483,7 @@ var require_auth = __commonJS({
|
|
|
2468
2483
|
exports2.getConfigDir = getConfigDir5;
|
|
2469
2484
|
exports2.getConfigPath = getConfigPath4;
|
|
2470
2485
|
exports2.loadConfig = loadConfig6;
|
|
2471
|
-
exports2.saveConfig =
|
|
2486
|
+
exports2.saveConfig = saveConfig3;
|
|
2472
2487
|
exports2.validateToken = validateToken;
|
|
2473
2488
|
var fs11 = __importStar(require("fs"));
|
|
2474
2489
|
var path13 = __importStar(require("path"));
|
|
@@ -2518,7 +2533,7 @@ var require_auth = __commonJS({
|
|
|
2518
2533
|
return null;
|
|
2519
2534
|
}
|
|
2520
2535
|
}
|
|
2521
|
-
async function
|
|
2536
|
+
async function saveConfig3(config, configPath) {
|
|
2522
2537
|
const fullPath = getConfigPath4(configPath);
|
|
2523
2538
|
ensureConfigDir(fullPath);
|
|
2524
2539
|
try {
|
|
@@ -3172,7 +3187,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3172
3187
|
}
|
|
3173
3188
|
/**
|
|
3174
3189
|
* Initialize worktree manager from existing project root
|
|
3175
|
-
*
|
|
3190
|
+
* EP971: All projects use worktree architecture
|
|
3191
|
+
* @returns true if valid project, false otherwise
|
|
3176
3192
|
*/
|
|
3177
3193
|
async initialize() {
|
|
3178
3194
|
if (!fs3.existsSync(this.bareRepoPath)) {
|
|
@@ -3183,7 +3199,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3183
3199
|
}
|
|
3184
3200
|
try {
|
|
3185
3201
|
const config = this.readConfig();
|
|
3186
|
-
return config
|
|
3202
|
+
return config !== null;
|
|
3187
3203
|
} catch {
|
|
3188
3204
|
return false;
|
|
3189
3205
|
}
|
|
@@ -3208,7 +3224,6 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
3208
3224
|
workspaceSlug,
|
|
3209
3225
|
projectSlug,
|
|
3210
3226
|
bareRepoPath: manager.bareRepoPath,
|
|
3211
|
-
worktreeMode: true,
|
|
3212
3227
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3213
3228
|
worktrees: []
|
|
3214
3229
|
};
|
|
@@ -3740,6 +3755,60 @@ async function findProjectRoot(startPath) {
|
|
|
3740
3755
|
return null;
|
|
3741
3756
|
}
|
|
3742
3757
|
|
|
3758
|
+
// src/api/machine-settings.ts
|
|
3759
|
+
async function fetchProjectPath(config, projectId) {
|
|
3760
|
+
if (!config.device_id || !config.access_token) {
|
|
3761
|
+
return null;
|
|
3762
|
+
}
|
|
3763
|
+
const serverUrl = config.api_url || "https://episoda.dev";
|
|
3764
|
+
const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`;
|
|
3765
|
+
try {
|
|
3766
|
+
const response = await fetch(url, {
|
|
3767
|
+
method: "GET",
|
|
3768
|
+
headers: {
|
|
3769
|
+
"Authorization": `Bearer ${config.access_token}`,
|
|
3770
|
+
"Content-Type": "application/json"
|
|
3771
|
+
}
|
|
3772
|
+
});
|
|
3773
|
+
if (!response.ok) {
|
|
3774
|
+
if (response.status === 404) {
|
|
3775
|
+
return null;
|
|
3776
|
+
}
|
|
3777
|
+
console.debug(`[MachineSettings] fetchProjectPath failed: ${response.status} ${response.statusText}`);
|
|
3778
|
+
return null;
|
|
3779
|
+
}
|
|
3780
|
+
const data = await response.json();
|
|
3781
|
+
return data.data?.path || null;
|
|
3782
|
+
} catch (error) {
|
|
3783
|
+
console.debug(`[MachineSettings] fetchProjectPath network error:`, error);
|
|
3784
|
+
return null;
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
async function syncProjectPath(config, projectId, path13) {
|
|
3788
|
+
if (!config.device_id || !config.access_token) {
|
|
3789
|
+
return false;
|
|
3790
|
+
}
|
|
3791
|
+
const serverUrl = config.api_url || "https://episoda.dev";
|
|
3792
|
+
const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`;
|
|
3793
|
+
try {
|
|
3794
|
+
const response = await fetch(url, {
|
|
3795
|
+
method: "PUT",
|
|
3796
|
+
headers: {
|
|
3797
|
+
"Authorization": `Bearer ${config.access_token}`,
|
|
3798
|
+
"Content-Type": "application/json"
|
|
3799
|
+
},
|
|
3800
|
+
body: JSON.stringify({ path: path13 })
|
|
3801
|
+
});
|
|
3802
|
+
if (!response.ok) {
|
|
3803
|
+
console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`);
|
|
3804
|
+
}
|
|
3805
|
+
return response.ok;
|
|
3806
|
+
} catch (error) {
|
|
3807
|
+
console.debug(`[MachineSettings] syncProjectPath network error:`, error);
|
|
3808
|
+
return false;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3743
3812
|
// src/commands/dev.ts
|
|
3744
3813
|
var CONNECTION_MAX_RETRIES = 3;
|
|
3745
3814
|
function findGitRoot(startDir) {
|
|
@@ -3798,14 +3867,11 @@ async function devCommand(options = {}) {
|
|
|
3798
3867
|
await new Promise((resolve4) => setTimeout(resolve4, 2e3));
|
|
3799
3868
|
}
|
|
3800
3869
|
}
|
|
3801
|
-
const serverUrl = config.api_url || process.env.EPISODA_API_URL || "https://episoda.dev";
|
|
3802
3870
|
let projectPath;
|
|
3803
|
-
const
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
projectPath = cachedSettings.local_project_path;
|
|
3808
|
-
status.debug(`Using cached project path: ${projectPath}`);
|
|
3871
|
+
const serverPath = await fetchProjectPath(config, config.project_id);
|
|
3872
|
+
if (serverPath && fs4.existsSync(serverPath)) {
|
|
3873
|
+
projectPath = serverPath;
|
|
3874
|
+
status.debug(`Using server-synced project path: ${projectPath}`);
|
|
3809
3875
|
} else {
|
|
3810
3876
|
const detectedRoot = findGitRoot(options.cwd || process.cwd());
|
|
3811
3877
|
projectPath = detectedRoot || path5.resolve(options.cwd || process.cwd());
|
|
@@ -3814,44 +3880,19 @@ async function devCommand(options = {}) {
|
|
|
3814
3880
|
} else {
|
|
3815
3881
|
status.warning(`Could not detect git root, using: ${projectPath}`);
|
|
3816
3882
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
...cachedSettings,
|
|
3821
|
-
local_project_path: projectPath,
|
|
3822
|
-
cached_at: Date.now()
|
|
3883
|
+
syncProjectPath(config, config.project_id, projectPath).then((synced) => {
|
|
3884
|
+
if (synced) {
|
|
3885
|
+
status.debug("Project path synced to server");
|
|
3823
3886
|
}
|
|
3824
|
-
};
|
|
3825
|
-
await (0, import_core4.saveConfig)(updatedConfig);
|
|
3826
|
-
status.debug("Cached project settings locally");
|
|
3827
|
-
const settingsUrl = `${serverUrl}/api/projects/${config.project_id}/settings`;
|
|
3828
|
-
fetch(settingsUrl, {
|
|
3829
|
-
method: "PATCH",
|
|
3830
|
-
headers: {
|
|
3831
|
-
"Authorization": `Bearer ${config.access_token}`,
|
|
3832
|
-
"Content-Type": "application/json"
|
|
3833
|
-
},
|
|
3834
|
-
body: JSON.stringify({ local_project_path: projectPath })
|
|
3835
3887
|
}).catch(() => {
|
|
3836
3888
|
});
|
|
3837
3889
|
}
|
|
3838
3890
|
const isWorktree = await isWorktreeProject(projectPath);
|
|
3839
3891
|
if (!isWorktree) {
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
status.info("Episoda requires projects to be cloned in worktree mode.");
|
|
3845
|
-
status.info("To set up this project:");
|
|
3846
|
-
status.info(" 1. Get the workspace/project slug from episoda.dev");
|
|
3847
|
-
status.info(" 2. Run: episoda clone {workspace}/{project}");
|
|
3848
|
-
status.info(" 3. Then: cd ~/episoda/{workspace}/{project}");
|
|
3849
|
-
status.info("");
|
|
3850
|
-
} else {
|
|
3851
|
-
status.error("Not a git repository or worktree project.");
|
|
3852
|
-
status.info("Run this command from within a project cloned with `episoda clone`.");
|
|
3853
|
-
status.info("");
|
|
3854
|
-
}
|
|
3892
|
+
status.error("Not an Episoda project.");
|
|
3893
|
+
status.info("");
|
|
3894
|
+
status.info("Use `episoda clone {workspace}/{project}` to set up a project.");
|
|
3895
|
+
status.info("");
|
|
3855
3896
|
process.exit(1);
|
|
3856
3897
|
}
|
|
3857
3898
|
let daemonPid = isDaemonRunning();
|
|
@@ -5044,9 +5085,6 @@ function addProject2(projectId, projectPath, options) {
|
|
|
5044
5085
|
if (existingByPath) {
|
|
5045
5086
|
existingByPath.id = projectId;
|
|
5046
5087
|
existingByPath.last_active = now;
|
|
5047
|
-
if (options?.worktreeMode !== void 0) {
|
|
5048
|
-
existingByPath.worktreeMode = options.worktreeMode;
|
|
5049
|
-
}
|
|
5050
5088
|
if (options?.bareRepoPath) {
|
|
5051
5089
|
existingByPath.bareRepoPath = options.bareRepoPath;
|
|
5052
5090
|
}
|
|
@@ -5066,8 +5104,7 @@ function addProject2(projectId, projectPath, options) {
|
|
|
5066
5104
|
name: projectName,
|
|
5067
5105
|
added_at: now,
|
|
5068
5106
|
last_active: now,
|
|
5069
|
-
//
|
|
5070
|
-
worktreeMode: options?.worktreeMode,
|
|
5107
|
+
// EP971: Bare repo path for worktree architecture
|
|
5071
5108
|
bareRepoPath: options?.bareRepoPath
|
|
5072
5109
|
};
|
|
5073
5110
|
data.projects.push(newProject);
|
|
@@ -5105,7 +5142,7 @@ async function cloneCommand(slugArg, options = {}) {
|
|
|
5105
5142
|
return;
|
|
5106
5143
|
}
|
|
5107
5144
|
throw new Error(
|
|
5108
|
-
`Directory exists but is not
|
|
5145
|
+
`Directory exists but is not an Episoda project: ${projectPath}
|
|
5109
5146
|
Please remove it manually or use a different location.`
|
|
5110
5147
|
);
|
|
5111
5148
|
}
|
|
@@ -5146,7 +5183,6 @@ Please configure a repository in the project settings on episoda.dev.`
|
|
|
5146
5183
|
status.success("\u2713 Bootstrap scripts extracted");
|
|
5147
5184
|
status.info("");
|
|
5148
5185
|
addProject2(projectDetails.id, projectPath, {
|
|
5149
|
-
worktreeMode: true,
|
|
5150
5186
|
bareRepoPath
|
|
5151
5187
|
});
|
|
5152
5188
|
status.success("\u2713 Project registered with daemon");
|
|
@@ -5701,7 +5737,7 @@ import_commander.program.command("connect").description("Connect using a pre-aut
|
|
|
5701
5737
|
process.exit(1);
|
|
5702
5738
|
}
|
|
5703
5739
|
});
|
|
5704
|
-
import_commander.program.command("dev").description("Connect to Episoda and start dev server
|
|
5740
|
+
import_commander.program.command("dev").description("Connect to Episoda and start dev server").option("--auto-restart", "Auto-restart dev server if it crashes").option("--connection-only", "Connection-only mode (don't start dev server)").allowUnknownOption().action(async (options, cmd) => {
|
|
5705
5741
|
try {
|
|
5706
5742
|
const args = process.argv.slice(process.argv.indexOf("dev") + 1);
|
|
5707
5743
|
const separatorIndex = args.indexOf("--");
|
|
@@ -5740,7 +5776,7 @@ import_commander.program.command("disconnect").description("Disconnect from epis
|
|
|
5740
5776
|
process.exit(1);
|
|
5741
5777
|
}
|
|
5742
5778
|
});
|
|
5743
|
-
import_commander.program.command("clone").description("Clone a project
|
|
5779
|
+
import_commander.program.command("clone").description("Clone a project for multi-module development").argument("<workspace/project>", "Workspace and project slug (e.g., my-team/my-project)").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (slug, options) => {
|
|
5744
5780
|
try {
|
|
5745
5781
|
await cloneCommand(slug, options);
|
|
5746
5782
|
} catch (error) {
|