episoda 0.2.35 → 0.2.36
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 +133 -80
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -397,6 +397,9 @@ var require_git_executor = __commonJS({
|
|
|
397
397
|
return await this.executeWorktreeList(cwd, options);
|
|
398
398
|
case "worktree_prune":
|
|
399
399
|
return await this.executeWorktreePrune(cwd, options);
|
|
400
|
+
// EP1002: Worktree setup command for unified orchestration
|
|
401
|
+
case "worktree_setup":
|
|
402
|
+
return await this.executeWorktreeSetup(command, cwd, options);
|
|
400
403
|
case "clone_bare":
|
|
401
404
|
return await this.executeCloneBare(command, options);
|
|
402
405
|
case "project_info":
|
|
@@ -1710,6 +1713,10 @@ var require_git_executor = __commonJS({
|
|
|
1710
1713
|
args.push(command.path);
|
|
1711
1714
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1712
1715
|
if (result.success) {
|
|
1716
|
+
try {
|
|
1717
|
+
await fs17.rm(command.path, { recursive: true, force: true });
|
|
1718
|
+
} catch {
|
|
1719
|
+
}
|
|
1713
1720
|
return {
|
|
1714
1721
|
success: true,
|
|
1715
1722
|
output: `Removed worktree at ${command.path}`,
|
|
@@ -1823,6 +1830,18 @@ var require_git_executor = __commonJS({
|
|
|
1823
1830
|
};
|
|
1824
1831
|
}
|
|
1825
1832
|
}
|
|
1833
|
+
/**
|
|
1834
|
+
* EP1002: Worktree setup command stub
|
|
1835
|
+
* The actual implementation is in the daemon which intercepts this command.
|
|
1836
|
+
* This stub exists for type checking and as a fallback.
|
|
1837
|
+
*/
|
|
1838
|
+
async executeWorktreeSetup(command, cwd, options) {
|
|
1839
|
+
return {
|
|
1840
|
+
success: false,
|
|
1841
|
+
error: "NOT_IMPLEMENTED",
|
|
1842
|
+
output: "worktree_setup must be handled by the daemon, not GitExecutor"
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1826
1845
|
/**
|
|
1827
1846
|
* EP944: Clone a repository as a bare repository
|
|
1828
1847
|
* Used for worktree-based development setup
|
|
@@ -2593,6 +2612,9 @@ var require_errors = __commonJS({
|
|
|
2593
2612
|
"WORKTREE_NOT_FOUND": "Worktree not found at this path",
|
|
2594
2613
|
"WORKTREE_LOCKED": "Worktree is locked",
|
|
2595
2614
|
"BRANCH_IN_USE": "Branch is already checked out in another worktree",
|
|
2615
|
+
// EP1002: Worktree setup error messages
|
|
2616
|
+
"NOT_IMPLEMENTED": "Command not implemented in this context",
|
|
2617
|
+
"SETUP_FAILED": "Worktree setup failed",
|
|
2596
2618
|
"UNKNOWN_ERROR": "Unknown error occurred"
|
|
2597
2619
|
};
|
|
2598
2620
|
let message = messages[code] || `Error: ${code}`;
|
|
@@ -2699,7 +2721,7 @@ var require_package = __commonJS({
|
|
|
2699
2721
|
"package.json"(exports2, module2) {
|
|
2700
2722
|
module2.exports = {
|
|
2701
2723
|
name: "episoda",
|
|
2702
|
-
version: "0.2.
|
|
2724
|
+
version: "0.2.35",
|
|
2703
2725
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2704
2726
|
main: "dist/index.js",
|
|
2705
2727
|
types: "dist/index.d.ts",
|
|
@@ -6242,17 +6264,6 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
6242
6264
|
}
|
|
6243
6265
|
return getWorktreeInfo(moduleUid, config.workspace_slug, config.project_slug);
|
|
6244
6266
|
}
|
|
6245
|
-
async function getProjectRootPath() {
|
|
6246
|
-
const config = await (0, import_core9.loadConfig)();
|
|
6247
|
-
if (!config?.workspace_slug || !config?.project_slug) {
|
|
6248
|
-
return null;
|
|
6249
|
-
}
|
|
6250
|
-
return path15.join(
|
|
6251
|
-
getEpisodaRoot2(),
|
|
6252
|
-
config.workspace_slug,
|
|
6253
|
-
config.project_slug
|
|
6254
|
-
);
|
|
6255
|
-
}
|
|
6256
6267
|
|
|
6257
6268
|
// src/utils/port-allocator.ts
|
|
6258
6269
|
var PORT_RANGE_START = 3100;
|
|
@@ -6898,6 +6909,32 @@ var Daemon = class _Daemon {
|
|
|
6898
6909
|
} else {
|
|
6899
6910
|
console.log(`[Daemon] Running git command in bare repo: ${bareRepoPath}`);
|
|
6900
6911
|
}
|
|
6912
|
+
if (gitCmd.action === "worktree_setup") {
|
|
6913
|
+
const wtManager = new WorktreeManager(projectPath);
|
|
6914
|
+
await wtManager.initialize();
|
|
6915
|
+
const SETUP_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6916
|
+
const setupPromise = this.handleWorktreeSetup(gitCmd, projectPath, wtManager);
|
|
6917
|
+
const timeoutPromise = new Promise(
|
|
6918
|
+
(_, reject) => setTimeout(() => reject(new Error("Worktree setup timed out after 15 minutes")), SETUP_TIMEOUT_MS)
|
|
6919
|
+
);
|
|
6920
|
+
let setupResult;
|
|
6921
|
+
try {
|
|
6922
|
+
setupResult = await Promise.race([setupPromise, timeoutPromise]);
|
|
6923
|
+
} catch (timeoutError) {
|
|
6924
|
+
setupResult = {
|
|
6925
|
+
success: false,
|
|
6926
|
+
error: "COMMAND_TIMEOUT",
|
|
6927
|
+
output: timeoutError instanceof Error ? timeoutError.message : "Setup timed out"
|
|
6928
|
+
};
|
|
6929
|
+
}
|
|
6930
|
+
await client.send({
|
|
6931
|
+
type: "result",
|
|
6932
|
+
commandId: message.id,
|
|
6933
|
+
result: setupResult
|
|
6934
|
+
});
|
|
6935
|
+
console.log(`[Daemon] EP1002: Worktree setup completed for ${gitCmd.moduleUid}:`, setupResult.success ? "success" : "failed");
|
|
6936
|
+
return;
|
|
6937
|
+
}
|
|
6901
6938
|
const result = await gitExecutor.execute(gitCmd, {
|
|
6902
6939
|
cwd
|
|
6903
6940
|
});
|
|
@@ -7367,74 +7404,6 @@ var Daemon = class _Daemon {
|
|
|
7367
7404
|
console.error(`[Daemon] EP956: Cannot resolve worktree path for ${moduleUid} (missing config slugs)`);
|
|
7368
7405
|
return;
|
|
7369
7406
|
}
|
|
7370
|
-
if (!worktree.exists && startingWork) {
|
|
7371
|
-
console.log(`[Daemon] EP959: Creating worktree for ${moduleUid} at ${worktree.path}`);
|
|
7372
|
-
const projectRoot = await getProjectRootPath();
|
|
7373
|
-
if (!projectRoot) {
|
|
7374
|
-
console.error(`[Daemon] EP959: Cannot determine project root for worktree creation`);
|
|
7375
|
-
return;
|
|
7376
|
-
}
|
|
7377
|
-
const worktreeManager = new WorktreeManager(projectRoot);
|
|
7378
|
-
const initialized = await worktreeManager.initialize();
|
|
7379
|
-
if (!initialized) {
|
|
7380
|
-
console.error(`[Daemon] EP959: Failed to initialize WorktreeManager at ${projectRoot}`);
|
|
7381
|
-
return;
|
|
7382
|
-
}
|
|
7383
|
-
const moduleBranchName = branchName || moduleUid;
|
|
7384
|
-
const createResult = await worktreeManager.createWorktree(moduleUid, moduleBranchName, true);
|
|
7385
|
-
if (!createResult.success) {
|
|
7386
|
-
console.error(`[Daemon] EP959: Failed to create worktree for ${moduleUid}: ${createResult.error}`);
|
|
7387
|
-
return;
|
|
7388
|
-
}
|
|
7389
|
-
console.log(`[Daemon] EP959: Worktree created for ${moduleUid} at ${createResult.worktreePath}`);
|
|
7390
|
-
if (this.deviceId) {
|
|
7391
|
-
try {
|
|
7392
|
-
const ownershipConfig = await (0, import_core10.loadConfig)();
|
|
7393
|
-
const ownershipApiUrl = ownershipConfig?.api_url || "https://episoda.dev";
|
|
7394
|
-
const ownershipResponse = await fetchWithAuth(`${ownershipApiUrl}/api/modules/${moduleUid}`, {
|
|
7395
|
-
method: "PATCH",
|
|
7396
|
-
body: JSON.stringify({ checkout_machine_id: this.deviceId })
|
|
7397
|
-
});
|
|
7398
|
-
if (ownershipResponse.ok) {
|
|
7399
|
-
console.log(`[Daemon] EP990: Claimed ownership of ${moduleUid} for device ${this.deviceId}`);
|
|
7400
|
-
} else {
|
|
7401
|
-
console.warn(`[Daemon] EP990: Failed to claim ownership of ${moduleUid}: ${ownershipResponse.status}`);
|
|
7402
|
-
}
|
|
7403
|
-
} catch (ownershipError) {
|
|
7404
|
-
console.warn(`[Daemon] EP990: Error claiming ownership of ${moduleUid}:`, ownershipError);
|
|
7405
|
-
}
|
|
7406
|
-
}
|
|
7407
|
-
worktree = await getWorktreeInfoForModule(moduleUid);
|
|
7408
|
-
if (!worktree || !worktree.exists) {
|
|
7409
|
-
console.error(`[Daemon] EP959: Worktree still not found after creation for ${moduleUid}`);
|
|
7410
|
-
return;
|
|
7411
|
-
}
|
|
7412
|
-
const worktreeConfig = await (0, import_core10.loadConfig)();
|
|
7413
|
-
const setupConfig = worktreeConfig?.project_settings;
|
|
7414
|
-
const envVars = await fetchEnvVars2();
|
|
7415
|
-
const hasEnvVars = Object.keys(envVars).length > 0;
|
|
7416
|
-
const hasSetupConfig = setupConfig?.worktree_copy_files?.length || setupConfig?.worktree_setup_script || hasEnvVars;
|
|
7417
|
-
{
|
|
7418
|
-
console.log(`[Daemon] EP986: Starting async worktree setup for ${moduleUid}${hasSetupConfig ? " (with config)" : " (for dependency installation)"}`);
|
|
7419
|
-
await worktreeManager.updateWorktreeStatus(moduleUid, "pending");
|
|
7420
|
-
await this.updateModuleWorktreeStatus(moduleUid, "pending", worktree.path);
|
|
7421
|
-
this.runWorktreeSetupAsync(
|
|
7422
|
-
moduleUid,
|
|
7423
|
-
worktreeManager,
|
|
7424
|
-
setupConfig?.worktree_copy_files || [],
|
|
7425
|
-
setupConfig?.worktree_setup_script,
|
|
7426
|
-
worktree.path,
|
|
7427
|
-
envVars
|
|
7428
|
-
// EP973: Use server-fetched env vars
|
|
7429
|
-
).then(() => {
|
|
7430
|
-
console.log(`[Daemon] EP959: Setup complete for ${moduleUid}, starting tunnel`);
|
|
7431
|
-
this.startTunnelForModule(moduleUid, worktree.path);
|
|
7432
|
-
}).catch((err) => {
|
|
7433
|
-
console.error(`[Daemon] EP959: Setup failed for ${moduleUid}:`, err);
|
|
7434
|
-
});
|
|
7435
|
-
return;
|
|
7436
|
-
}
|
|
7437
|
-
}
|
|
7438
7407
|
if (!worktree.exists) {
|
|
7439
7408
|
console.log(`[Daemon] EP956: No worktree for ${moduleUid} at ${worktree.path}, skipping tunnel`);
|
|
7440
7409
|
return;
|
|
@@ -7996,6 +7965,90 @@ var Daemon = class _Daemon {
|
|
|
7996
7965
|
throw error;
|
|
7997
7966
|
}
|
|
7998
7967
|
}
|
|
7968
|
+
/**
|
|
7969
|
+
* EP1002: Handle worktree_setup command from server
|
|
7970
|
+
* This provides a unified setup flow for both local and cloud environments.
|
|
7971
|
+
* Server orchestrates, daemon executes.
|
|
7972
|
+
*/
|
|
7973
|
+
async handleWorktreeSetup(command, projectPath, worktreeManager) {
|
|
7974
|
+
const { path: worktreePath, moduleUid } = command;
|
|
7975
|
+
console.log(`[Daemon] EP1002: Handling worktree_setup for ${moduleUid} at ${worktreePath}`);
|
|
7976
|
+
try {
|
|
7977
|
+
const envVars = await fetchEnvVars2();
|
|
7978
|
+
console.log(`[Daemon] EP1002: Fetched ${Object.keys(envVars).length} env vars for ${moduleUid}`);
|
|
7979
|
+
const config = await (0, import_core10.loadConfig)();
|
|
7980
|
+
const setupConfig = config?.project_settings;
|
|
7981
|
+
await this.runWorktreeSetupSync(
|
|
7982
|
+
moduleUid,
|
|
7983
|
+
worktreeManager,
|
|
7984
|
+
setupConfig?.worktree_copy_files || [],
|
|
7985
|
+
setupConfig?.worktree_setup_script,
|
|
7986
|
+
worktreePath,
|
|
7987
|
+
envVars
|
|
7988
|
+
);
|
|
7989
|
+
return {
|
|
7990
|
+
success: true,
|
|
7991
|
+
output: `Worktree setup completed for ${moduleUid}`,
|
|
7992
|
+
details: {
|
|
7993
|
+
moduleUid,
|
|
7994
|
+
worktreePath,
|
|
7995
|
+
envVarsCount: Object.keys(envVars).length
|
|
7996
|
+
}
|
|
7997
|
+
};
|
|
7998
|
+
} catch (error) {
|
|
7999
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
8000
|
+
console.error(`[Daemon] EP1002: Worktree setup failed for ${moduleUid}:`, errorMessage);
|
|
8001
|
+
return {
|
|
8002
|
+
success: false,
|
|
8003
|
+
error: "SETUP_FAILED",
|
|
8004
|
+
output: errorMessage
|
|
8005
|
+
};
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
/**
|
|
8009
|
+
* EP1002: Synchronous worktree setup for command-driven flow
|
|
8010
|
+
* Similar to runWorktreeSetupAsync but runs synchronously for server orchestration
|
|
8011
|
+
*/
|
|
8012
|
+
async runWorktreeSetupSync(moduleUid, worktreeManager, copyFiles, setupScript, worktreePath, envVars = {}) {
|
|
8013
|
+
console.log(`[Daemon] EP1002: Running worktree setup for ${moduleUid}`);
|
|
8014
|
+
await worktreeManager.updateWorktreeStatus(moduleUid, "running");
|
|
8015
|
+
await this.updateModuleWorktreeStatus(moduleUid, "setup", worktreePath);
|
|
8016
|
+
if (Object.keys(envVars).length > 0) {
|
|
8017
|
+
console.log(`[Daemon] EP1002: Writing .env with ${Object.keys(envVars).length} variables`);
|
|
8018
|
+
writeEnvFile(worktreePath, envVars);
|
|
8019
|
+
}
|
|
8020
|
+
const installCmd = getInstallCommand(worktreePath);
|
|
8021
|
+
if (installCmd) {
|
|
8022
|
+
console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
8023
|
+
console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
|
|
8024
|
+
try {
|
|
8025
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
8026
|
+
execSync7(installCmd.command.join(" "), {
|
|
8027
|
+
cwd: worktreePath,
|
|
8028
|
+
stdio: "inherit",
|
|
8029
|
+
timeout: 10 * 60 * 1e3,
|
|
8030
|
+
// 10 minute timeout
|
|
8031
|
+
env: { ...process.env, CI: "true" }
|
|
8032
|
+
});
|
|
8033
|
+
console.log(`[Daemon] EP1002: Dependencies installed successfully`);
|
|
8034
|
+
} catch (installError) {
|
|
8035
|
+
const errorMsg = installError instanceof Error ? installError.message : String(installError);
|
|
8036
|
+
console.warn(`[Daemon] EP1002: Dependency installation failed (non-fatal): ${errorMsg}`);
|
|
8037
|
+
}
|
|
8038
|
+
} else {
|
|
8039
|
+
console.log(`[Daemon] EP1002: No package manager detected, skipping dependency installation`);
|
|
8040
|
+
}
|
|
8041
|
+
if (setupScript) {
|
|
8042
|
+
console.log(`[Daemon] EP1002: Running setup script`);
|
|
8043
|
+
const scriptResult = await worktreeManager.runSetupScript(moduleUid, setupScript);
|
|
8044
|
+
if (!scriptResult.success) {
|
|
8045
|
+
throw new Error(`Setup script failed: ${scriptResult.error}`);
|
|
8046
|
+
}
|
|
8047
|
+
}
|
|
8048
|
+
await worktreeManager.updateWorktreeStatus(moduleUid, "ready");
|
|
8049
|
+
await this.updateModuleWorktreeStatus(moduleUid, "ready", worktreePath);
|
|
8050
|
+
console.log(`[Daemon] EP1002: Worktree setup complete for ${moduleUid}`);
|
|
8051
|
+
}
|
|
7999
8052
|
/**
|
|
8000
8053
|
* EP959-11: Run worktree setup asynchronously
|
|
8001
8054
|
* EP964: Added envVars parameter to inject .env file
|