episoda 0.2.106 → 0.2.107
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.
|
@@ -1571,15 +1571,15 @@ var require_git_executor = __commonJS({
|
|
|
1571
1571
|
try {
|
|
1572
1572
|
const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1573
1573
|
const gitDirPath = gitDir.trim();
|
|
1574
|
-
const
|
|
1574
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1575
1575
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1576
1576
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1577
1577
|
try {
|
|
1578
|
-
await
|
|
1578
|
+
await fs24.access(rebaseMergePath);
|
|
1579
1579
|
inRebase = true;
|
|
1580
1580
|
} catch {
|
|
1581
1581
|
try {
|
|
1582
|
-
await
|
|
1582
|
+
await fs24.access(rebaseApplyPath);
|
|
1583
1583
|
inRebase = true;
|
|
1584
1584
|
} catch {
|
|
1585
1585
|
inRebase = false;
|
|
@@ -1633,9 +1633,9 @@ var require_git_executor = __commonJS({
|
|
|
1633
1633
|
error: validation.error || "UNKNOWN_ERROR"
|
|
1634
1634
|
};
|
|
1635
1635
|
}
|
|
1636
|
-
const
|
|
1636
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1637
1637
|
try {
|
|
1638
|
-
await
|
|
1638
|
+
await fs24.access(command.path);
|
|
1639
1639
|
return {
|
|
1640
1640
|
success: false,
|
|
1641
1641
|
error: "WORKTREE_EXISTS",
|
|
@@ -1689,9 +1689,9 @@ var require_git_executor = __commonJS({
|
|
|
1689
1689
|
*/
|
|
1690
1690
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1691
1691
|
try {
|
|
1692
|
-
const
|
|
1692
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1693
1693
|
try {
|
|
1694
|
-
await
|
|
1694
|
+
await fs24.access(command.path);
|
|
1695
1695
|
} catch {
|
|
1696
1696
|
return {
|
|
1697
1697
|
success: false,
|
|
@@ -1726,7 +1726,7 @@ var require_git_executor = __commonJS({
|
|
|
1726
1726
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1727
1727
|
if (result.success) {
|
|
1728
1728
|
try {
|
|
1729
|
-
await
|
|
1729
|
+
await fs24.rm(command.path, { recursive: true, force: true });
|
|
1730
1730
|
} catch {
|
|
1731
1731
|
}
|
|
1732
1732
|
return {
|
|
@@ -1860,10 +1860,10 @@ var require_git_executor = __commonJS({
|
|
|
1860
1860
|
*/
|
|
1861
1861
|
async executeCloneBare(command, options) {
|
|
1862
1862
|
try {
|
|
1863
|
-
const
|
|
1864
|
-
const
|
|
1863
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1864
|
+
const path25 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1865
1865
|
try {
|
|
1866
|
-
await
|
|
1866
|
+
await fs24.access(command.path);
|
|
1867
1867
|
return {
|
|
1868
1868
|
success: false,
|
|
1869
1869
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1872,9 +1872,9 @@ var require_git_executor = __commonJS({
|
|
|
1872
1872
|
};
|
|
1873
1873
|
} catch {
|
|
1874
1874
|
}
|
|
1875
|
-
const parentDir =
|
|
1875
|
+
const parentDir = path25.dirname(command.path);
|
|
1876
1876
|
try {
|
|
1877
|
-
await
|
|
1877
|
+
await fs24.mkdir(parentDir, { recursive: true });
|
|
1878
1878
|
} catch {
|
|
1879
1879
|
}
|
|
1880
1880
|
const { stdout, stderr } = await execAsync3(
|
|
@@ -1922,22 +1922,22 @@ var require_git_executor = __commonJS({
|
|
|
1922
1922
|
*/
|
|
1923
1923
|
async executeProjectInfo(cwd, options) {
|
|
1924
1924
|
try {
|
|
1925
|
-
const
|
|
1926
|
-
const
|
|
1925
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1926
|
+
const path25 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1927
1927
|
let currentPath = cwd;
|
|
1928
1928
|
let projectPath = cwd;
|
|
1929
1929
|
let bareRepoPath;
|
|
1930
1930
|
for (let i = 0; i < 10; i++) {
|
|
1931
|
-
const bareDir =
|
|
1932
|
-
const episodaDir =
|
|
1931
|
+
const bareDir = path25.join(currentPath, ".bare");
|
|
1932
|
+
const episodaDir = path25.join(currentPath, ".episoda");
|
|
1933
1933
|
try {
|
|
1934
|
-
await
|
|
1935
|
-
await
|
|
1934
|
+
await fs24.access(bareDir);
|
|
1935
|
+
await fs24.access(episodaDir);
|
|
1936
1936
|
projectPath = currentPath;
|
|
1937
1937
|
bareRepoPath = bareDir;
|
|
1938
1938
|
break;
|
|
1939
1939
|
} catch {
|
|
1940
|
-
const parentPath =
|
|
1940
|
+
const parentPath = path25.dirname(currentPath);
|
|
1941
1941
|
if (parentPath === currentPath) {
|
|
1942
1942
|
break;
|
|
1943
1943
|
}
|
|
@@ -2590,31 +2590,31 @@ var require_auth = __commonJS({
|
|
|
2590
2590
|
exports2.loadConfig = loadConfig8;
|
|
2591
2591
|
exports2.saveConfig = saveConfig2;
|
|
2592
2592
|
exports2.validateToken = validateToken;
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
2593
|
+
var fs24 = __importStar(require("fs"));
|
|
2594
|
+
var path25 = __importStar(require("path"));
|
|
2595
2595
|
var os9 = __importStar(require("os"));
|
|
2596
2596
|
var child_process_1 = require("child_process");
|
|
2597
2597
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2598
2598
|
function getConfigDir8() {
|
|
2599
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2599
|
+
return process.env.EPISODA_CONFIG_DIR || path25.join(os9.homedir(), ".episoda");
|
|
2600
2600
|
}
|
|
2601
2601
|
function getConfigPath(configPath) {
|
|
2602
2602
|
if (configPath) {
|
|
2603
2603
|
return configPath;
|
|
2604
2604
|
}
|
|
2605
|
-
return
|
|
2605
|
+
return path25.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
|
|
2606
2606
|
}
|
|
2607
2607
|
function ensureConfigDir(configPath) {
|
|
2608
|
-
const dir =
|
|
2609
|
-
const isNew = !
|
|
2608
|
+
const dir = path25.dirname(configPath);
|
|
2609
|
+
const isNew = !fs24.existsSync(dir);
|
|
2610
2610
|
if (isNew) {
|
|
2611
|
-
|
|
2611
|
+
fs24.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2612
2612
|
}
|
|
2613
2613
|
if (process.platform === "darwin") {
|
|
2614
|
-
const nosyncPath =
|
|
2615
|
-
if (isNew || !
|
|
2614
|
+
const nosyncPath = path25.join(dir, ".nosync");
|
|
2615
|
+
if (isNew || !fs24.existsSync(nosyncPath)) {
|
|
2616
2616
|
try {
|
|
2617
|
-
|
|
2617
|
+
fs24.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2618
2618
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2619
2619
|
stdio: "ignore",
|
|
2620
2620
|
timeout: 5e3
|
|
@@ -2626,9 +2626,9 @@ var require_auth = __commonJS({
|
|
|
2626
2626
|
}
|
|
2627
2627
|
async function loadConfig8(configPath) {
|
|
2628
2628
|
const fullPath = getConfigPath(configPath);
|
|
2629
|
-
if (
|
|
2629
|
+
if (fs24.existsSync(fullPath)) {
|
|
2630
2630
|
try {
|
|
2631
|
-
const content =
|
|
2631
|
+
const content = fs24.readFileSync(fullPath, "utf8");
|
|
2632
2632
|
const config = JSON.parse(content);
|
|
2633
2633
|
return config;
|
|
2634
2634
|
} catch (error) {
|
|
@@ -2638,9 +2638,9 @@ var require_auth = __commonJS({
|
|
|
2638
2638
|
if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_WORKSPACE) {
|
|
2639
2639
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2640
2640
|
const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
|
|
2641
|
-
if (
|
|
2641
|
+
if (fs24.existsSync(workspaceConfigPath)) {
|
|
2642
2642
|
try {
|
|
2643
|
-
const content =
|
|
2643
|
+
const content = fs24.readFileSync(workspaceConfigPath, "utf8");
|
|
2644
2644
|
const workspaceConfig = JSON.parse(content);
|
|
2645
2645
|
return {
|
|
2646
2646
|
access_token: process.env.EPISODA_ACCESS_TOKEN || workspaceConfig.accessToken,
|
|
@@ -2687,7 +2687,7 @@ var require_auth = __commonJS({
|
|
|
2687
2687
|
ensureConfigDir(fullPath);
|
|
2688
2688
|
try {
|
|
2689
2689
|
const content = JSON.stringify(config, null, 2);
|
|
2690
|
-
|
|
2690
|
+
fs24.writeFileSync(fullPath, content, { mode: 384 });
|
|
2691
2691
|
} catch (error) {
|
|
2692
2692
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2693
2693
|
}
|
|
@@ -2843,7 +2843,7 @@ var require_package = __commonJS({
|
|
|
2843
2843
|
"@modelcontextprotocol/server-github": "^0.6.0"
|
|
2844
2844
|
},
|
|
2845
2845
|
devDependencies: {
|
|
2846
|
-
"@episoda/core": "
|
|
2846
|
+
"@episoda/core": "workspace:*",
|
|
2847
2847
|
"@types/node": "^20.11.24",
|
|
2848
2848
|
"@types/semver": "7.7.1",
|
|
2849
2849
|
"@types/tar": "6.1.13",
|
|
@@ -4058,8 +4058,8 @@ async function handleExec(command, projectPath) {
|
|
|
4058
4058
|
}
|
|
4059
4059
|
|
|
4060
4060
|
// src/daemon/handlers/worktree-handlers.ts
|
|
4061
|
-
var
|
|
4062
|
-
var
|
|
4061
|
+
var path16 = __toESM(require("path"));
|
|
4062
|
+
var fs15 = __toESM(require("fs"));
|
|
4063
4063
|
var import_child_process8 = require("child_process");
|
|
4064
4064
|
var import_util = require("util");
|
|
4065
4065
|
|
|
@@ -7332,6 +7332,144 @@ async function deleteWorktree(config, moduleUid) {
|
|
|
7332
7332
|
}
|
|
7333
7333
|
}
|
|
7334
7334
|
|
|
7335
|
+
// src/daemon/package-manager.ts
|
|
7336
|
+
var fs14 = __toESM(require("fs"));
|
|
7337
|
+
var path15 = __toESM(require("path"));
|
|
7338
|
+
var PACKAGE_MANAGERS = {
|
|
7339
|
+
javascript: [
|
|
7340
|
+
{
|
|
7341
|
+
name: "pnpm",
|
|
7342
|
+
detector: (p) => {
|
|
7343
|
+
if (fs14.existsSync(path15.join(p, "pnpm-lock.yaml"))) {
|
|
7344
|
+
return "pnpm-lock.yaml";
|
|
7345
|
+
}
|
|
7346
|
+
const pkgJsonPath = path15.join(p, "package.json");
|
|
7347
|
+
if (fs14.existsSync(pkgJsonPath)) {
|
|
7348
|
+
try {
|
|
7349
|
+
const pkg = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
|
|
7350
|
+
if (pkg.packageManager?.startsWith("pnpm")) {
|
|
7351
|
+
return "package.json (packageManager field)";
|
|
7352
|
+
}
|
|
7353
|
+
} catch {
|
|
7354
|
+
}
|
|
7355
|
+
}
|
|
7356
|
+
return null;
|
|
7357
|
+
},
|
|
7358
|
+
config: {
|
|
7359
|
+
installCmd: "pnpm install --frozen-lockfile",
|
|
7360
|
+
cacheEnvVar: "PNPM_HOME"
|
|
7361
|
+
}
|
|
7362
|
+
},
|
|
7363
|
+
{
|
|
7364
|
+
name: "yarn",
|
|
7365
|
+
detector: (p) => fs14.existsSync(path15.join(p, "yarn.lock")) ? "yarn.lock" : null,
|
|
7366
|
+
config: {
|
|
7367
|
+
installCmd: "yarn install --frozen-lockfile",
|
|
7368
|
+
cacheEnvVar: "YARN_CACHE_FOLDER"
|
|
7369
|
+
}
|
|
7370
|
+
},
|
|
7371
|
+
{
|
|
7372
|
+
name: "bun",
|
|
7373
|
+
detector: (p) => fs14.existsSync(path15.join(p, "bun.lockb")) ? "bun.lockb" : null,
|
|
7374
|
+
config: {
|
|
7375
|
+
installCmd: "bun install --frozen-lockfile"
|
|
7376
|
+
}
|
|
7377
|
+
},
|
|
7378
|
+
{
|
|
7379
|
+
name: "npm",
|
|
7380
|
+
detector: (p) => fs14.existsSync(path15.join(p, "package-lock.json")) ? "package-lock.json" : null,
|
|
7381
|
+
config: {
|
|
7382
|
+
installCmd: "npm ci"
|
|
7383
|
+
}
|
|
7384
|
+
},
|
|
7385
|
+
{
|
|
7386
|
+
// EP1222: Default to pnpm for new projects without lockfile
|
|
7387
|
+
// This encourages standardization on pnpm across Episoda projects
|
|
7388
|
+
name: "pnpm",
|
|
7389
|
+
detector: (p) => fs14.existsSync(path15.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
|
|
7390
|
+
config: {
|
|
7391
|
+
installCmd: "pnpm install",
|
|
7392
|
+
cacheEnvVar: "PNPM_HOME"
|
|
7393
|
+
}
|
|
7394
|
+
}
|
|
7395
|
+
],
|
|
7396
|
+
python: [
|
|
7397
|
+
{
|
|
7398
|
+
name: "uv",
|
|
7399
|
+
detector: (p) => {
|
|
7400
|
+
const pyprojectPath = path15.join(p, "pyproject.toml");
|
|
7401
|
+
if (fs14.existsSync(path15.join(p, "uv.lock"))) {
|
|
7402
|
+
return "uv.lock";
|
|
7403
|
+
}
|
|
7404
|
+
if (fs14.existsSync(pyprojectPath)) {
|
|
7405
|
+
try {
|
|
7406
|
+
const content = fs14.readFileSync(pyprojectPath, "utf-8");
|
|
7407
|
+
if (content.includes("[tool.uv]") || content.includes("[project]")) {
|
|
7408
|
+
return "pyproject.toml";
|
|
7409
|
+
}
|
|
7410
|
+
} catch {
|
|
7411
|
+
}
|
|
7412
|
+
}
|
|
7413
|
+
return null;
|
|
7414
|
+
},
|
|
7415
|
+
config: {
|
|
7416
|
+
installCmd: "uv sync",
|
|
7417
|
+
cacheEnvVar: "UV_CACHE_DIR"
|
|
7418
|
+
}
|
|
7419
|
+
},
|
|
7420
|
+
{
|
|
7421
|
+
name: "pip",
|
|
7422
|
+
detector: (p) => fs14.existsSync(path15.join(p, "requirements.txt")) ? "requirements.txt" : null,
|
|
7423
|
+
config: {
|
|
7424
|
+
installCmd: "pip install -r requirements.txt"
|
|
7425
|
+
}
|
|
7426
|
+
}
|
|
7427
|
+
]
|
|
7428
|
+
// Future: Add more languages here
|
|
7429
|
+
// rust: [
|
|
7430
|
+
// {
|
|
7431
|
+
// name: 'cargo',
|
|
7432
|
+
// detector: (p) => fs.existsSync(path.join(p, 'Cargo.toml')) ? 'Cargo.toml' : null,
|
|
7433
|
+
// config: { installCmd: 'cargo build' }
|
|
7434
|
+
// }
|
|
7435
|
+
// ],
|
|
7436
|
+
// go: [
|
|
7437
|
+
// {
|
|
7438
|
+
// name: 'go',
|
|
7439
|
+
// detector: (p) => fs.existsSync(path.join(p, 'go.mod')) ? 'go.mod' : null,
|
|
7440
|
+
// config: { installCmd: 'go mod download' }
|
|
7441
|
+
// }
|
|
7442
|
+
// ],
|
|
7443
|
+
};
|
|
7444
|
+
function detectPackageManager(worktreePath, preferredLanguages) {
|
|
7445
|
+
const checkedFiles = [];
|
|
7446
|
+
const languages = preferredLanguages || Object.keys(PACKAGE_MANAGERS);
|
|
7447
|
+
for (const language of languages) {
|
|
7448
|
+
const managers = PACKAGE_MANAGERS[language];
|
|
7449
|
+
if (!managers) continue;
|
|
7450
|
+
for (const { name, detector, config } of managers) {
|
|
7451
|
+
const matchedFile = detector(worktreePath);
|
|
7452
|
+
if (matchedFile) {
|
|
7453
|
+
checkedFiles.push(matchedFile);
|
|
7454
|
+
return {
|
|
7455
|
+
detected: true,
|
|
7456
|
+
packageManager: {
|
|
7457
|
+
name,
|
|
7458
|
+
language,
|
|
7459
|
+
...config
|
|
7460
|
+
},
|
|
7461
|
+
checkedFiles,
|
|
7462
|
+
matchedFile
|
|
7463
|
+
};
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
return {
|
|
7468
|
+
detected: false,
|
|
7469
|
+
checkedFiles
|
|
7470
|
+
};
|
|
7471
|
+
}
|
|
7472
|
+
|
|
7335
7473
|
// src/daemon/handlers/worktree-handlers.ts
|
|
7336
7474
|
async function getConfigForApi() {
|
|
7337
7475
|
if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_ACCESS_TOKEN) {
|
|
@@ -7350,6 +7488,16 @@ async function getConfigForApi() {
|
|
|
7350
7488
|
return (0, import_core9.loadConfig)();
|
|
7351
7489
|
}
|
|
7352
7490
|
var execAsync = (0, import_util.promisify)(import_child_process8.exec);
|
|
7491
|
+
async function autoDetectSetupScript(worktreePath) {
|
|
7492
|
+
const detection = detectPackageManager(worktreePath);
|
|
7493
|
+
if (!detection.detected || !detection.packageManager) {
|
|
7494
|
+
console.log(`[Worktree] EP1222: No package manager detected in ${worktreePath}`);
|
|
7495
|
+
return null;
|
|
7496
|
+
}
|
|
7497
|
+
const { name, language, installCmd } = detection.packageManager;
|
|
7498
|
+
console.log(`[Worktree] EP1222: Detected ${name} (${language}) via ${detection.matchedFile}`);
|
|
7499
|
+
return installCmd;
|
|
7500
|
+
}
|
|
7353
7501
|
async function handleWorktreeCreate(request2) {
|
|
7354
7502
|
const {
|
|
7355
7503
|
workspaceSlug,
|
|
@@ -7374,18 +7522,18 @@ async function handleWorktreeCreate(request2) {
|
|
|
7374
7522
|
}
|
|
7375
7523
|
try {
|
|
7376
7524
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7377
|
-
const bareRepoPath =
|
|
7378
|
-
if (!
|
|
7525
|
+
const bareRepoPath = path16.join(projectPath, ".bare");
|
|
7526
|
+
if (!fs15.existsSync(bareRepoPath)) {
|
|
7379
7527
|
console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
|
|
7380
7528
|
console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
|
|
7381
|
-
const episodaDir =
|
|
7382
|
-
|
|
7529
|
+
const episodaDir = path16.join(projectPath, ".episoda");
|
|
7530
|
+
fs15.mkdirSync(episodaDir, { recursive: true });
|
|
7383
7531
|
try {
|
|
7384
7532
|
console.log(`[Worktree] K1273: Starting git clone...`);
|
|
7385
7533
|
await execAsync(`git clone --bare "${repoUrl}" "${bareRepoPath}"`);
|
|
7386
7534
|
console.log(`[Worktree] K1273: Clone successful`);
|
|
7387
7535
|
await execAsync(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`);
|
|
7388
|
-
const configPath =
|
|
7536
|
+
const configPath = path16.join(episodaDir, "config.json");
|
|
7389
7537
|
const config2 = {
|
|
7390
7538
|
projectId,
|
|
7391
7539
|
workspaceSlug,
|
|
@@ -7394,7 +7542,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
7394
7542
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7395
7543
|
worktrees: []
|
|
7396
7544
|
};
|
|
7397
|
-
|
|
7545
|
+
fs15.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
7398
7546
|
console.log(`[Worktree] K1273: Project initialized at ${projectPath}`);
|
|
7399
7547
|
} catch (cloneError) {
|
|
7400
7548
|
console.error(`[Worktree] K1273: Git clone failed: ${cloneError.message}`);
|
|
@@ -7442,11 +7590,12 @@ async function handleWorktreeCreate(request2) {
|
|
|
7442
7590
|
}
|
|
7443
7591
|
if (envVars && Object.keys(envVars).length > 0) {
|
|
7444
7592
|
const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
7445
|
-
const envPath =
|
|
7446
|
-
|
|
7593
|
+
const envPath = path16.join(worktreePath, ".env");
|
|
7594
|
+
fs15.writeFileSync(envPath, envContent + "\n", "utf-8");
|
|
7447
7595
|
console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
|
|
7448
7596
|
}
|
|
7449
|
-
|
|
7597
|
+
const effectiveSetupScript = setupScript || await autoDetectSetupScript(worktreePath);
|
|
7598
|
+
if (effectiveSetupScript) {
|
|
7450
7599
|
await manager.updateWorktreeStatus(moduleUid, "setup");
|
|
7451
7600
|
if (config?.access_token) {
|
|
7452
7601
|
await updateWorktree(config, {
|
|
@@ -7458,7 +7607,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
7458
7607
|
});
|
|
7459
7608
|
}
|
|
7460
7609
|
console.log(`[Worktree] EP1143: Running setup script...`);
|
|
7461
|
-
const scriptResult = await manager.runSetupScript(moduleUid,
|
|
7610
|
+
const scriptResult = await manager.runSetupScript(moduleUid, effectiveSetupScript);
|
|
7462
7611
|
if (!scriptResult.success) {
|
|
7463
7612
|
console.error(`[Worktree] EP1143: Setup script failed: ${scriptResult.error}`);
|
|
7464
7613
|
await manager.updateWorktreeStatus(moduleUid, "error", scriptResult.error);
|
|
@@ -7616,12 +7765,12 @@ async function handleProjectEject(request2) {
|
|
|
7616
7765
|
console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
|
|
7617
7766
|
try {
|
|
7618
7767
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7619
|
-
const bareRepoPath =
|
|
7620
|
-
if (!
|
|
7768
|
+
const bareRepoPath = path16.join(projectPath, ".bare");
|
|
7769
|
+
if (!fs15.existsSync(projectPath)) {
|
|
7621
7770
|
console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
|
|
7622
7771
|
return { success: true };
|
|
7623
7772
|
}
|
|
7624
|
-
if (!
|
|
7773
|
+
if (!fs15.existsSync(bareRepoPath)) {
|
|
7625
7774
|
console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
|
|
7626
7775
|
return { success: true };
|
|
7627
7776
|
}
|
|
@@ -7636,12 +7785,12 @@ async function handleProjectEject(request2) {
|
|
|
7636
7785
|
};
|
|
7637
7786
|
}
|
|
7638
7787
|
}
|
|
7639
|
-
const artifactsPath =
|
|
7640
|
-
if (
|
|
7788
|
+
const artifactsPath = path16.join(projectPath, "artifacts");
|
|
7789
|
+
if (fs15.existsSync(artifactsPath)) {
|
|
7641
7790
|
await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
|
|
7642
7791
|
}
|
|
7643
7792
|
console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
|
|
7644
|
-
await
|
|
7793
|
+
await fs15.promises.rm(projectPath, { recursive: true, force: true });
|
|
7645
7794
|
console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
|
|
7646
7795
|
return { success: true };
|
|
7647
7796
|
} catch (error) {
|
|
@@ -7655,7 +7804,7 @@ async function handleProjectEject(request2) {
|
|
|
7655
7804
|
async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
|
|
7656
7805
|
const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
|
|
7657
7806
|
try {
|
|
7658
|
-
const files = await
|
|
7807
|
+
const files = await fs15.promises.readdir(artifactsPath);
|
|
7659
7808
|
if (files.length === 0) {
|
|
7660
7809
|
return;
|
|
7661
7810
|
}
|
|
@@ -7669,8 +7818,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7669
7818
|
}
|
|
7670
7819
|
const artifacts = [];
|
|
7671
7820
|
for (const fileName of files) {
|
|
7672
|
-
const filePath =
|
|
7673
|
-
const stat = await
|
|
7821
|
+
const filePath = path16.join(artifactsPath, fileName);
|
|
7822
|
+
const stat = await fs15.promises.stat(filePath);
|
|
7674
7823
|
if (stat.isDirectory()) {
|
|
7675
7824
|
continue;
|
|
7676
7825
|
}
|
|
@@ -7679,9 +7828,9 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7679
7828
|
continue;
|
|
7680
7829
|
}
|
|
7681
7830
|
try {
|
|
7682
|
-
const content = await
|
|
7831
|
+
const content = await fs15.promises.readFile(filePath);
|
|
7683
7832
|
const base64Content = content.toString("base64");
|
|
7684
|
-
const ext =
|
|
7833
|
+
const ext = path16.extname(fileName).toLowerCase();
|
|
7685
7834
|
const mimeTypes = {
|
|
7686
7835
|
".json": "application/json",
|
|
7687
7836
|
".txt": "text/plain",
|
|
@@ -7737,8 +7886,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7737
7886
|
}
|
|
7738
7887
|
|
|
7739
7888
|
// src/daemon/handlers/project-handlers.ts
|
|
7740
|
-
var
|
|
7741
|
-
var
|
|
7889
|
+
var path17 = __toESM(require("path"));
|
|
7890
|
+
var fs16 = __toESM(require("fs"));
|
|
7742
7891
|
function validateSlug(slug, fieldName) {
|
|
7743
7892
|
if (!slug || typeof slug !== "string") {
|
|
7744
7893
|
return `${fieldName} is required`;
|
|
@@ -7778,14 +7927,14 @@ async function handleProjectSetup(params) {
|
|
|
7778
7927
|
console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
|
|
7779
7928
|
try {
|
|
7780
7929
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7781
|
-
const artifactsPath =
|
|
7782
|
-
const configDir =
|
|
7783
|
-
const configPath =
|
|
7784
|
-
await
|
|
7785
|
-
await
|
|
7930
|
+
const artifactsPath = path17.join(projectPath, "artifacts");
|
|
7931
|
+
const configDir = path17.join(projectPath, ".episoda");
|
|
7932
|
+
const configPath = path17.join(configDir, "config.json");
|
|
7933
|
+
await fs16.promises.mkdir(artifactsPath, { recursive: true });
|
|
7934
|
+
await fs16.promises.mkdir(configDir, { recursive: true });
|
|
7786
7935
|
let existingConfig = {};
|
|
7787
7936
|
try {
|
|
7788
|
-
const existing = await
|
|
7937
|
+
const existing = await fs16.promises.readFile(configPath, "utf-8");
|
|
7789
7938
|
existingConfig = JSON.parse(existing);
|
|
7790
7939
|
} catch {
|
|
7791
7940
|
}
|
|
@@ -7798,7 +7947,7 @@ async function handleProjectSetup(params) {
|
|
|
7798
7947
|
// Only set created_at if not already present
|
|
7799
7948
|
created_at: existingConfig.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
7800
7949
|
};
|
|
7801
|
-
await
|
|
7950
|
+
await fs16.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
7802
7951
|
console.log(`[ProjectSetup] EP1199: Project setup complete at ${projectPath}`);
|
|
7803
7952
|
return {
|
|
7804
7953
|
success: true,
|
|
@@ -7912,12 +8061,12 @@ async function cleanupStaleCommits(projectPath) {
|
|
|
7912
8061
|
|
|
7913
8062
|
// src/agent/claude-binary.ts
|
|
7914
8063
|
var import_child_process10 = require("child_process");
|
|
7915
|
-
var
|
|
7916
|
-
var
|
|
8064
|
+
var path18 = __toESM(require("path"));
|
|
8065
|
+
var fs17 = __toESM(require("fs"));
|
|
7917
8066
|
var cachedBinaryPath = null;
|
|
7918
8067
|
function isValidClaudeBinary(binaryPath) {
|
|
7919
8068
|
try {
|
|
7920
|
-
|
|
8069
|
+
fs17.accessSync(binaryPath, fs17.constants.X_OK);
|
|
7921
8070
|
const version = (0, import_child_process10.execSync)(`"${binaryPath}" --version`, {
|
|
7922
8071
|
encoding: "utf-8",
|
|
7923
8072
|
timeout: 5e3,
|
|
@@ -7950,14 +8099,14 @@ async function ensureClaudeBinary() {
|
|
|
7950
8099
|
}
|
|
7951
8100
|
const bundledPaths = [
|
|
7952
8101
|
// In production: node_modules/.bin/claude
|
|
7953
|
-
|
|
8102
|
+
path18.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
|
|
7954
8103
|
// In monorepo development: packages/episoda/node_modules/.bin/claude
|
|
7955
|
-
|
|
8104
|
+
path18.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
|
|
7956
8105
|
// Root monorepo node_modules
|
|
7957
|
-
|
|
8106
|
+
path18.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
|
|
7958
8107
|
];
|
|
7959
8108
|
for (const bundledPath of bundledPaths) {
|
|
7960
|
-
if (
|
|
8109
|
+
if (fs17.existsSync(bundledPath) && isValidClaudeBinary(bundledPath)) {
|
|
7961
8110
|
cachedBinaryPath = bundledPath;
|
|
7962
8111
|
return cachedBinaryPath;
|
|
7963
8112
|
}
|
|
@@ -7983,12 +8132,12 @@ async function ensureClaudeBinary() {
|
|
|
7983
8132
|
|
|
7984
8133
|
// src/agent/codex-binary.ts
|
|
7985
8134
|
var import_child_process11 = require("child_process");
|
|
7986
|
-
var
|
|
7987
|
-
var
|
|
8135
|
+
var path19 = __toESM(require("path"));
|
|
8136
|
+
var fs18 = __toESM(require("fs"));
|
|
7988
8137
|
var cachedBinaryPath2 = null;
|
|
7989
8138
|
function isValidCodexBinary(binaryPath) {
|
|
7990
8139
|
try {
|
|
7991
|
-
|
|
8140
|
+
fs18.accessSync(binaryPath, fs18.constants.X_OK);
|
|
7992
8141
|
const version = (0, import_child_process11.execSync)(`"${binaryPath}" --version`, {
|
|
7993
8142
|
encoding: "utf-8",
|
|
7994
8143
|
timeout: 5e3,
|
|
@@ -8021,14 +8170,14 @@ async function ensureCodexBinary() {
|
|
|
8021
8170
|
}
|
|
8022
8171
|
const bundledPaths = [
|
|
8023
8172
|
// In production: node_modules/.bin/codex
|
|
8024
|
-
|
|
8173
|
+
path19.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
|
|
8025
8174
|
// In monorepo development: packages/episoda/node_modules/.bin/codex
|
|
8026
|
-
|
|
8175
|
+
path19.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
|
|
8027
8176
|
// Root monorepo node_modules
|
|
8028
|
-
|
|
8177
|
+
path19.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
|
|
8029
8178
|
];
|
|
8030
8179
|
for (const bundledPath of bundledPaths) {
|
|
8031
|
-
if (
|
|
8180
|
+
if (fs18.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
|
|
8032
8181
|
cachedBinaryPath2 = bundledPath;
|
|
8033
8182
|
return cachedBinaryPath2;
|
|
8034
8183
|
}
|
|
@@ -8095,8 +8244,8 @@ function generateCodexConfig(credentials, projectPath) {
|
|
|
8095
8244
|
|
|
8096
8245
|
// src/agent/agent-manager.ts
|
|
8097
8246
|
var import_child_process12 = require("child_process");
|
|
8098
|
-
var
|
|
8099
|
-
var
|
|
8247
|
+
var path20 = __toESM(require("path"));
|
|
8248
|
+
var fs19 = __toESM(require("fs"));
|
|
8100
8249
|
var os6 = __toESM(require("os"));
|
|
8101
8250
|
|
|
8102
8251
|
// src/agent/claude-config.ts
|
|
@@ -8419,7 +8568,7 @@ var AgentManager = class {
|
|
|
8419
8568
|
this.initialized = false;
|
|
8420
8569
|
// EP1133: Lock for config file writes to prevent race conditions
|
|
8421
8570
|
this.configWriteLock = Promise.resolve();
|
|
8422
|
-
this.pidDir =
|
|
8571
|
+
this.pidDir = path20.join(os6.homedir(), ".episoda", "agent-pids");
|
|
8423
8572
|
}
|
|
8424
8573
|
/**
|
|
8425
8574
|
* EP1133: Acquire lock for config file writes
|
|
@@ -8448,8 +8597,8 @@ var AgentManager = class {
|
|
|
8448
8597
|
return;
|
|
8449
8598
|
}
|
|
8450
8599
|
console.log("[AgentManager] Initializing...");
|
|
8451
|
-
if (!
|
|
8452
|
-
|
|
8600
|
+
if (!fs19.existsSync(this.pidDir)) {
|
|
8601
|
+
fs19.mkdirSync(this.pidDir, { recursive: true });
|
|
8453
8602
|
}
|
|
8454
8603
|
await this.cleanupOrphanedProcesses();
|
|
8455
8604
|
try {
|
|
@@ -8696,9 +8845,9 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8696
8845
|
const useApiKey = !useOAuth && !!session.credentials.apiKey;
|
|
8697
8846
|
if (provider === "codex") {
|
|
8698
8847
|
await this.withConfigLock(async () => {
|
|
8699
|
-
const codexDir =
|
|
8700
|
-
if (!
|
|
8701
|
-
|
|
8848
|
+
const codexDir = path20.join(os6.homedir(), ".codex");
|
|
8849
|
+
if (!fs19.existsSync(codexDir)) {
|
|
8850
|
+
fs19.mkdirSync(codexDir, { recursive: true });
|
|
8702
8851
|
}
|
|
8703
8852
|
if (useOAuth) {
|
|
8704
8853
|
const codexConfig = generateCodexConfig({
|
|
@@ -8708,21 +8857,21 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8708
8857
|
accountId: session.credentials.accountId,
|
|
8709
8858
|
expiresAt: session.credentials.expiresAt
|
|
8710
8859
|
}, session.projectPath);
|
|
8711
|
-
const authJsonPath =
|
|
8712
|
-
|
|
8860
|
+
const authJsonPath = path20.join(codexDir, "auth.json");
|
|
8861
|
+
fs19.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
|
|
8713
8862
|
console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
|
|
8714
8863
|
if (codexConfig["config.toml"]) {
|
|
8715
|
-
const configTomlPath =
|
|
8864
|
+
const configTomlPath = path20.join(codexDir, "config.toml");
|
|
8716
8865
|
let existingConfig = "";
|
|
8717
8866
|
try {
|
|
8718
|
-
existingConfig =
|
|
8867
|
+
existingConfig = fs19.readFileSync(configTomlPath, "utf-8");
|
|
8719
8868
|
} catch {
|
|
8720
8869
|
}
|
|
8721
8870
|
const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8722
8871
|
const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
|
|
8723
8872
|
const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
|
|
8724
8873
|
if (!projectAlreadyTrusted) {
|
|
8725
|
-
|
|
8874
|
+
fs19.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
|
|
8726
8875
|
console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
|
|
8727
8876
|
}
|
|
8728
8877
|
}
|
|
@@ -8732,14 +8881,14 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8732
8881
|
});
|
|
8733
8882
|
} else {
|
|
8734
8883
|
await this.withConfigLock(async () => {
|
|
8735
|
-
const claudeDir =
|
|
8736
|
-
const credentialsPath =
|
|
8737
|
-
const statsigDir =
|
|
8738
|
-
if (!
|
|
8739
|
-
|
|
8884
|
+
const claudeDir = path20.join(os6.homedir(), ".claude");
|
|
8885
|
+
const credentialsPath = path20.join(claudeDir, ".credentials.json");
|
|
8886
|
+
const statsigDir = path20.join(claudeDir, "statsig");
|
|
8887
|
+
if (!fs19.existsSync(claudeDir)) {
|
|
8888
|
+
fs19.mkdirSync(claudeDir, { recursive: true });
|
|
8740
8889
|
}
|
|
8741
|
-
if (!
|
|
8742
|
-
|
|
8890
|
+
if (!fs19.existsSync(statsigDir)) {
|
|
8891
|
+
fs19.mkdirSync(statsigDir, { recursive: true });
|
|
8743
8892
|
}
|
|
8744
8893
|
if (useOAuth) {
|
|
8745
8894
|
const oauthCredentials = {
|
|
@@ -8757,7 +8906,7 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8757
8906
|
const credentialsContent = JSON.stringify({
|
|
8758
8907
|
claudeAiOauth: oauthCredentials
|
|
8759
8908
|
}, null, 2);
|
|
8760
|
-
|
|
8909
|
+
fs19.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
|
|
8761
8910
|
console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
|
|
8762
8911
|
try {
|
|
8763
8912
|
const claudeConfig = generateClaudeConfig({
|
|
@@ -8769,11 +8918,11 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8769
8918
|
if (!hasEvaluations || !hasStableId) {
|
|
8770
8919
|
throw new Error(`Invalid statsig config: missing required files`);
|
|
8771
8920
|
}
|
|
8772
|
-
const settingsPath =
|
|
8773
|
-
|
|
8921
|
+
const settingsPath = path20.join(claudeDir, "settings.json");
|
|
8922
|
+
fs19.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
|
|
8774
8923
|
for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
|
|
8775
|
-
const filePath =
|
|
8776
|
-
|
|
8924
|
+
const filePath = path20.join(statsigDir, filename);
|
|
8925
|
+
fs19.writeFileSync(filePath, content, { mode: 420 });
|
|
8777
8926
|
}
|
|
8778
8927
|
if (session.credentials.githubToken) {
|
|
8779
8928
|
console.log("[AgentManager] EP1146: GitHub MCP server enabled with installation token");
|
|
@@ -9043,14 +9192,14 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9043
9192
|
*/
|
|
9044
9193
|
async cleanupOrphanedProcesses() {
|
|
9045
9194
|
let cleaned = 0;
|
|
9046
|
-
if (!
|
|
9195
|
+
if (!fs19.existsSync(this.pidDir)) {
|
|
9047
9196
|
return { cleaned };
|
|
9048
9197
|
}
|
|
9049
|
-
const pidFiles =
|
|
9198
|
+
const pidFiles = fs19.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
|
|
9050
9199
|
for (const pidFile of pidFiles) {
|
|
9051
|
-
const pidPath =
|
|
9200
|
+
const pidPath = path20.join(this.pidDir, pidFile);
|
|
9052
9201
|
try {
|
|
9053
|
-
const pidStr =
|
|
9202
|
+
const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
|
|
9054
9203
|
const pid = parseInt(pidStr, 10);
|
|
9055
9204
|
if (!isNaN(pid)) {
|
|
9056
9205
|
try {
|
|
@@ -9061,7 +9210,7 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9061
9210
|
} catch {
|
|
9062
9211
|
}
|
|
9063
9212
|
}
|
|
9064
|
-
|
|
9213
|
+
fs19.unlinkSync(pidPath);
|
|
9065
9214
|
} catch (error) {
|
|
9066
9215
|
console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
|
|
9067
9216
|
}
|
|
@@ -9075,17 +9224,17 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9075
9224
|
* Write PID file for session tracking
|
|
9076
9225
|
*/
|
|
9077
9226
|
writePidFile(sessionId, pid) {
|
|
9078
|
-
const pidPath =
|
|
9079
|
-
|
|
9227
|
+
const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
|
|
9228
|
+
fs19.writeFileSync(pidPath, pid.toString());
|
|
9080
9229
|
}
|
|
9081
9230
|
/**
|
|
9082
9231
|
* Remove PID file for session
|
|
9083
9232
|
*/
|
|
9084
9233
|
removePidFile(sessionId) {
|
|
9085
|
-
const pidPath =
|
|
9234
|
+
const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
|
|
9086
9235
|
try {
|
|
9087
|
-
if (
|
|
9088
|
-
|
|
9236
|
+
if (fs19.existsSync(pidPath)) {
|
|
9237
|
+
fs19.unlinkSync(pidPath);
|
|
9089
9238
|
}
|
|
9090
9239
|
} catch {
|
|
9091
9240
|
}
|
|
@@ -9095,8 +9244,8 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9095
9244
|
// src/utils/dev-server.ts
|
|
9096
9245
|
var import_child_process13 = require("child_process");
|
|
9097
9246
|
var import_core11 = __toESM(require_dist());
|
|
9098
|
-
var
|
|
9099
|
-
var
|
|
9247
|
+
var fs20 = __toESM(require("fs"));
|
|
9248
|
+
var path21 = __toESM(require("path"));
|
|
9100
9249
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
9101
9250
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
9102
9251
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -9104,26 +9253,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
|
9104
9253
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
9105
9254
|
var activeServers = /* @__PURE__ */ new Map();
|
|
9106
9255
|
function getLogsDir() {
|
|
9107
|
-
const logsDir =
|
|
9108
|
-
if (!
|
|
9109
|
-
|
|
9256
|
+
const logsDir = path21.join((0, import_core11.getConfigDir)(), "logs");
|
|
9257
|
+
if (!fs20.existsSync(logsDir)) {
|
|
9258
|
+
fs20.mkdirSync(logsDir, { recursive: true });
|
|
9110
9259
|
}
|
|
9111
9260
|
return logsDir;
|
|
9112
9261
|
}
|
|
9113
9262
|
function getLogFilePath(moduleUid) {
|
|
9114
|
-
return
|
|
9263
|
+
return path21.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
9115
9264
|
}
|
|
9116
9265
|
function rotateLogIfNeeded(logPath) {
|
|
9117
9266
|
try {
|
|
9118
|
-
if (
|
|
9119
|
-
const stats =
|
|
9267
|
+
if (fs20.existsSync(logPath)) {
|
|
9268
|
+
const stats = fs20.statSync(logPath);
|
|
9120
9269
|
if (stats.size > MAX_LOG_SIZE_BYTES) {
|
|
9121
9270
|
const backupPath = `${logPath}.1`;
|
|
9122
|
-
if (
|
|
9123
|
-
|
|
9271
|
+
if (fs20.existsSync(backupPath)) {
|
|
9272
|
+
fs20.unlinkSync(backupPath);
|
|
9124
9273
|
}
|
|
9125
|
-
|
|
9126
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
9274
|
+
fs20.renameSync(logPath, backupPath);
|
|
9275
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path21.basename(logPath)}`);
|
|
9127
9276
|
}
|
|
9128
9277
|
}
|
|
9129
9278
|
} catch (error) {
|
|
@@ -9136,7 +9285,7 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
9136
9285
|
const prefix = isError ? "ERR" : "OUT";
|
|
9137
9286
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
9138
9287
|
`;
|
|
9139
|
-
|
|
9288
|
+
fs20.appendFileSync(logPath, logLine);
|
|
9140
9289
|
} catch {
|
|
9141
9290
|
}
|
|
9142
9291
|
}
|
|
@@ -9315,8 +9464,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
9315
9464
|
});
|
|
9316
9465
|
injectedEnvVars = result.envVars;
|
|
9317
9466
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
9318
|
-
const envFilePath =
|
|
9319
|
-
if (!
|
|
9467
|
+
const envFilePath = path21.join(projectPath, ".env");
|
|
9468
|
+
if (!fs20.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
9320
9469
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
9321
9470
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
9322
9471
|
}
|
|
@@ -9422,19 +9571,19 @@ function getDevServerStatus() {
|
|
|
9422
9571
|
}
|
|
9423
9572
|
|
|
9424
9573
|
// src/utils/worktree.ts
|
|
9425
|
-
var
|
|
9426
|
-
var
|
|
9574
|
+
var path22 = __toESM(require("path"));
|
|
9575
|
+
var fs21 = __toESM(require("fs"));
|
|
9427
9576
|
var os7 = __toESM(require("os"));
|
|
9428
9577
|
var import_core12 = __toESM(require_dist());
|
|
9429
9578
|
function getEpisodaRoot2() {
|
|
9430
|
-
return process.env.EPISODA_ROOT ||
|
|
9579
|
+
return process.env.EPISODA_ROOT || path22.join(os7.homedir(), "episoda");
|
|
9431
9580
|
}
|
|
9432
9581
|
function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
|
|
9433
9582
|
const root = getEpisodaRoot2();
|
|
9434
|
-
const worktreePath =
|
|
9583
|
+
const worktreePath = path22.join(root, workspaceSlug, projectSlug, moduleUid);
|
|
9435
9584
|
return {
|
|
9436
9585
|
path: worktreePath,
|
|
9437
|
-
exists:
|
|
9586
|
+
exists: fs21.existsSync(worktreePath),
|
|
9438
9587
|
moduleUid
|
|
9439
9588
|
};
|
|
9440
9589
|
}
|
|
@@ -9448,15 +9597,15 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
9448
9597
|
return null;
|
|
9449
9598
|
}
|
|
9450
9599
|
const root = getEpisodaRoot2();
|
|
9451
|
-
const workspaceRoot =
|
|
9600
|
+
const workspaceRoot = path22.join(root, config.workspace_slug);
|
|
9452
9601
|
try {
|
|
9453
|
-
const entries =
|
|
9602
|
+
const entries = fs21.readdirSync(workspaceRoot, { withFileTypes: true });
|
|
9454
9603
|
for (const entry of entries) {
|
|
9455
9604
|
if (!entry.isDirectory()) {
|
|
9456
9605
|
continue;
|
|
9457
9606
|
}
|
|
9458
|
-
const worktreePath =
|
|
9459
|
-
if (
|
|
9607
|
+
const worktreePath = path22.join(workspaceRoot, entry.name, moduleUid);
|
|
9608
|
+
if (fs21.existsSync(worktreePath)) {
|
|
9460
9609
|
return {
|
|
9461
9610
|
path: worktreePath,
|
|
9462
9611
|
exists: true,
|
|
@@ -9473,61 +9622,61 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
9473
9622
|
}
|
|
9474
9623
|
|
|
9475
9624
|
// src/framework-detector.ts
|
|
9476
|
-
var
|
|
9477
|
-
var
|
|
9478
|
-
function
|
|
9479
|
-
if (
|
|
9625
|
+
var fs22 = __toESM(require("fs"));
|
|
9626
|
+
var path23 = __toESM(require("path"));
|
|
9627
|
+
function getInstallCommand2(cwd) {
|
|
9628
|
+
if (fs22.existsSync(path23.join(cwd, "bun.lockb"))) {
|
|
9480
9629
|
return {
|
|
9481
9630
|
command: ["bun", "install"],
|
|
9482
9631
|
description: "Installing dependencies with bun",
|
|
9483
9632
|
detectedFrom: "bun.lockb"
|
|
9484
9633
|
};
|
|
9485
9634
|
}
|
|
9486
|
-
if (
|
|
9635
|
+
if (fs22.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) {
|
|
9487
9636
|
return {
|
|
9488
9637
|
command: ["pnpm", "install"],
|
|
9489
9638
|
description: "Installing dependencies with pnpm",
|
|
9490
9639
|
detectedFrom: "pnpm-lock.yaml"
|
|
9491
9640
|
};
|
|
9492
9641
|
}
|
|
9493
|
-
if (
|
|
9642
|
+
if (fs22.existsSync(path23.join(cwd, "yarn.lock"))) {
|
|
9494
9643
|
return {
|
|
9495
9644
|
command: ["yarn", "install"],
|
|
9496
9645
|
description: "Installing dependencies with yarn",
|
|
9497
9646
|
detectedFrom: "yarn.lock"
|
|
9498
9647
|
};
|
|
9499
9648
|
}
|
|
9500
|
-
if (
|
|
9649
|
+
if (fs22.existsSync(path23.join(cwd, "package-lock.json"))) {
|
|
9501
9650
|
return {
|
|
9502
9651
|
command: ["npm", "ci"],
|
|
9503
9652
|
description: "Installing dependencies with npm ci",
|
|
9504
9653
|
detectedFrom: "package-lock.json"
|
|
9505
9654
|
};
|
|
9506
9655
|
}
|
|
9507
|
-
if (
|
|
9656
|
+
if (fs22.existsSync(path23.join(cwd, "package.json"))) {
|
|
9508
9657
|
return {
|
|
9509
9658
|
command: ["npm", "install"],
|
|
9510
9659
|
description: "Installing dependencies with npm",
|
|
9511
9660
|
detectedFrom: "package.json"
|
|
9512
9661
|
};
|
|
9513
9662
|
}
|
|
9514
|
-
if (
|
|
9663
|
+
if (fs22.existsSync(path23.join(cwd, "Pipfile.lock")) || fs22.existsSync(path23.join(cwd, "Pipfile"))) {
|
|
9515
9664
|
return {
|
|
9516
9665
|
command: ["pipenv", "install"],
|
|
9517
9666
|
description: "Installing dependencies with pipenv",
|
|
9518
|
-
detectedFrom:
|
|
9667
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
|
|
9519
9668
|
};
|
|
9520
9669
|
}
|
|
9521
|
-
if (
|
|
9670
|
+
if (fs22.existsSync(path23.join(cwd, "poetry.lock"))) {
|
|
9522
9671
|
return {
|
|
9523
9672
|
command: ["poetry", "install"],
|
|
9524
9673
|
description: "Installing dependencies with poetry",
|
|
9525
9674
|
detectedFrom: "poetry.lock"
|
|
9526
9675
|
};
|
|
9527
9676
|
}
|
|
9528
|
-
if (
|
|
9529
|
-
const pyprojectPath =
|
|
9530
|
-
const content =
|
|
9677
|
+
if (fs22.existsSync(path23.join(cwd, "pyproject.toml"))) {
|
|
9678
|
+
const pyprojectPath = path23.join(cwd, "pyproject.toml");
|
|
9679
|
+
const content = fs22.readFileSync(pyprojectPath, "utf-8");
|
|
9531
9680
|
if (content.includes("[tool.poetry]")) {
|
|
9532
9681
|
return {
|
|
9533
9682
|
command: ["poetry", "install"],
|
|
@@ -9536,42 +9685,42 @@ function getInstallCommand(cwd) {
|
|
|
9536
9685
|
};
|
|
9537
9686
|
}
|
|
9538
9687
|
}
|
|
9539
|
-
if (
|
|
9688
|
+
if (fs22.existsSync(path23.join(cwd, "requirements.txt"))) {
|
|
9540
9689
|
return {
|
|
9541
9690
|
command: ["pip", "install", "-r", "requirements.txt"],
|
|
9542
9691
|
description: "Installing dependencies with pip",
|
|
9543
9692
|
detectedFrom: "requirements.txt"
|
|
9544
9693
|
};
|
|
9545
9694
|
}
|
|
9546
|
-
if (
|
|
9695
|
+
if (fs22.existsSync(path23.join(cwd, "Gemfile.lock")) || fs22.existsSync(path23.join(cwd, "Gemfile"))) {
|
|
9547
9696
|
return {
|
|
9548
9697
|
command: ["bundle", "install"],
|
|
9549
9698
|
description: "Installing dependencies with bundler",
|
|
9550
|
-
detectedFrom:
|
|
9699
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
|
|
9551
9700
|
};
|
|
9552
9701
|
}
|
|
9553
|
-
if (
|
|
9702
|
+
if (fs22.existsSync(path23.join(cwd, "go.sum")) || fs22.existsSync(path23.join(cwd, "go.mod"))) {
|
|
9554
9703
|
return {
|
|
9555
9704
|
command: ["go", "mod", "download"],
|
|
9556
9705
|
description: "Downloading Go modules",
|
|
9557
|
-
detectedFrom:
|
|
9706
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
|
|
9558
9707
|
};
|
|
9559
9708
|
}
|
|
9560
|
-
if (
|
|
9709
|
+
if (fs22.existsSync(path23.join(cwd, "Cargo.lock")) || fs22.existsSync(path23.join(cwd, "Cargo.toml"))) {
|
|
9561
9710
|
return {
|
|
9562
9711
|
command: ["cargo", "build"],
|
|
9563
9712
|
description: "Building Rust project (downloads dependencies)",
|
|
9564
|
-
detectedFrom:
|
|
9713
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
|
|
9565
9714
|
};
|
|
9566
9715
|
}
|
|
9567
9716
|
return null;
|
|
9568
9717
|
}
|
|
9569
9718
|
|
|
9570
9719
|
// src/daemon/daemon-process.ts
|
|
9571
|
-
var
|
|
9720
|
+
var fs23 = __toESM(require("fs"));
|
|
9572
9721
|
var http2 = __toESM(require("http"));
|
|
9573
9722
|
var os8 = __toESM(require("os"));
|
|
9574
|
-
var
|
|
9723
|
+
var path24 = __toESM(require("path"));
|
|
9575
9724
|
var packageJson = require_package();
|
|
9576
9725
|
async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
9577
9726
|
const now = Date.now();
|
|
@@ -9792,9 +9941,9 @@ var Daemon = class _Daemon {
|
|
|
9792
9941
|
this.healthServer = http2.createServer((req, res) => {
|
|
9793
9942
|
if (req.url === "/health" || req.url === "/") {
|
|
9794
9943
|
const isConnected = this.liveConnections.size > 0;
|
|
9795
|
-
const projects = Array.from(this.connections.entries()).map(([
|
|
9796
|
-
path:
|
|
9797
|
-
connected: this.liveConnections.has(
|
|
9944
|
+
const projects = Array.from(this.connections.entries()).map(([path25, conn]) => ({
|
|
9945
|
+
path: path25,
|
|
9946
|
+
connected: this.liveConnections.has(path25)
|
|
9798
9947
|
}));
|
|
9799
9948
|
const status = {
|
|
9800
9949
|
status: isConnected ? "healthy" : "degraded",
|
|
@@ -10162,7 +10311,7 @@ var Daemon = class _Daemon {
|
|
|
10162
10311
|
client.updateActivity();
|
|
10163
10312
|
try {
|
|
10164
10313
|
const gitCmd = message.command;
|
|
10165
|
-
const bareRepoPath =
|
|
10314
|
+
const bareRepoPath = path24.join(projectPath, ".bare");
|
|
10166
10315
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
10167
10316
|
if (gitCmd.worktreePath) {
|
|
10168
10317
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -10747,8 +10896,8 @@ var Daemon = class _Daemon {
|
|
|
10747
10896
|
let daemonPid;
|
|
10748
10897
|
try {
|
|
10749
10898
|
const pidPath = getPidFilePath();
|
|
10750
|
-
if (
|
|
10751
|
-
const pidStr =
|
|
10899
|
+
if (fs23.existsSync(pidPath)) {
|
|
10900
|
+
const pidStr = fs23.readFileSync(pidPath, "utf-8").trim();
|
|
10752
10901
|
daemonPid = parseInt(pidStr, 10);
|
|
10753
10902
|
}
|
|
10754
10903
|
} catch (pidError) {
|
|
@@ -10829,28 +10978,28 @@ var Daemon = class _Daemon {
|
|
|
10829
10978
|
* - workDir: The directory to run git commands in (cwd)
|
|
10830
10979
|
*/
|
|
10831
10980
|
getGitDirs(projectPath) {
|
|
10832
|
-
const bareDir =
|
|
10833
|
-
const gitPath =
|
|
10834
|
-
if (
|
|
10981
|
+
const bareDir = path24.join(projectPath, ".bare");
|
|
10982
|
+
const gitPath = path24.join(projectPath, ".git");
|
|
10983
|
+
if (fs23.existsSync(bareDir) && fs23.statSync(bareDir).isDirectory()) {
|
|
10835
10984
|
return { gitDir: bareDir, workDir: projectPath };
|
|
10836
10985
|
}
|
|
10837
|
-
if (
|
|
10986
|
+
if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isDirectory()) {
|
|
10838
10987
|
return { gitDir: null, workDir: projectPath };
|
|
10839
10988
|
}
|
|
10840
|
-
if (
|
|
10989
|
+
if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isFile()) {
|
|
10841
10990
|
return { gitDir: null, workDir: projectPath };
|
|
10842
10991
|
}
|
|
10843
|
-
const entries =
|
|
10992
|
+
const entries = fs23.readdirSync(projectPath, { withFileTypes: true });
|
|
10844
10993
|
for (const entry of entries) {
|
|
10845
10994
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
10846
|
-
const worktreePath =
|
|
10847
|
-
const worktreeGit =
|
|
10848
|
-
if (
|
|
10995
|
+
const worktreePath = path24.join(projectPath, entry.name);
|
|
10996
|
+
const worktreeGit = path24.join(worktreePath, ".git");
|
|
10997
|
+
if (fs23.existsSync(worktreeGit)) {
|
|
10849
10998
|
return { gitDir: null, workDir: worktreePath };
|
|
10850
10999
|
}
|
|
10851
11000
|
}
|
|
10852
11001
|
}
|
|
10853
|
-
if (
|
|
11002
|
+
if (fs23.existsSync(bareDir)) {
|
|
10854
11003
|
return { gitDir: bareDir, workDir: projectPath };
|
|
10855
11004
|
}
|
|
10856
11005
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -10914,24 +11063,24 @@ var Daemon = class _Daemon {
|
|
|
10914
11063
|
async installGitHooks(projectPath) {
|
|
10915
11064
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
10916
11065
|
let hooksDir;
|
|
10917
|
-
const bareHooksDir =
|
|
10918
|
-
const gitHooksDir =
|
|
10919
|
-
if (
|
|
11066
|
+
const bareHooksDir = path24.join(projectPath, ".bare", "hooks");
|
|
11067
|
+
const gitHooksDir = path24.join(projectPath, ".git", "hooks");
|
|
11068
|
+
if (fs23.existsSync(bareHooksDir)) {
|
|
10920
11069
|
hooksDir = bareHooksDir;
|
|
10921
|
-
} else if (
|
|
11070
|
+
} else if (fs23.existsSync(gitHooksDir) && fs23.statSync(path24.join(projectPath, ".git")).isDirectory()) {
|
|
10922
11071
|
hooksDir = gitHooksDir;
|
|
10923
11072
|
} else {
|
|
10924
|
-
const parentBareHooks =
|
|
10925
|
-
if (
|
|
11073
|
+
const parentBareHooks = path24.join(projectPath, "..", ".bare", "hooks");
|
|
11074
|
+
if (fs23.existsSync(parentBareHooks)) {
|
|
10926
11075
|
hooksDir = parentBareHooks;
|
|
10927
11076
|
} else {
|
|
10928
11077
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
10929
11078
|
return;
|
|
10930
11079
|
}
|
|
10931
11080
|
}
|
|
10932
|
-
if (!
|
|
11081
|
+
if (!fs23.existsSync(hooksDir)) {
|
|
10933
11082
|
try {
|
|
10934
|
-
|
|
11083
|
+
fs23.mkdirSync(hooksDir, { recursive: true });
|
|
10935
11084
|
} catch (error) {
|
|
10936
11085
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
10937
11086
|
return;
|
|
@@ -10939,20 +11088,20 @@ var Daemon = class _Daemon {
|
|
|
10939
11088
|
}
|
|
10940
11089
|
for (const hookName of hooks) {
|
|
10941
11090
|
try {
|
|
10942
|
-
const hookPath =
|
|
10943
|
-
const bundledHookPath =
|
|
10944
|
-
if (!
|
|
11091
|
+
const hookPath = path24.join(hooksDir, hookName);
|
|
11092
|
+
const bundledHookPath = path24.join(__dirname, "..", "hooks", hookName);
|
|
11093
|
+
if (!fs23.existsSync(bundledHookPath)) {
|
|
10945
11094
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
10946
11095
|
continue;
|
|
10947
11096
|
}
|
|
10948
|
-
const hookContent =
|
|
10949
|
-
if (
|
|
10950
|
-
const existingContent =
|
|
11097
|
+
const hookContent = fs23.readFileSync(bundledHookPath, "utf-8");
|
|
11098
|
+
if (fs23.existsSync(hookPath)) {
|
|
11099
|
+
const existingContent = fs23.readFileSync(hookPath, "utf-8");
|
|
10951
11100
|
if (existingContent === hookContent) {
|
|
10952
11101
|
continue;
|
|
10953
11102
|
}
|
|
10954
11103
|
}
|
|
10955
|
-
|
|
11104
|
+
fs23.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
10956
11105
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
10957
11106
|
} catch (error) {
|
|
10958
11107
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -11290,7 +11439,7 @@ var Daemon = class _Daemon {
|
|
|
11290
11439
|
console.log(`[Daemon] EP1002: Writing .env with ${Object.keys(envVars).length} variables`);
|
|
11291
11440
|
writeEnvFile(worktreePath, envVars);
|
|
11292
11441
|
}
|
|
11293
|
-
const installCmd =
|
|
11442
|
+
const installCmd = getInstallCommand2(worktreePath);
|
|
11294
11443
|
if (installCmd) {
|
|
11295
11444
|
console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
11296
11445
|
console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
|
|
@@ -11344,7 +11493,7 @@ var Daemon = class _Daemon {
|
|
|
11344
11493
|
console.warn(`[Daemon] EP964: File copy failed (non-fatal): ${copyResult.error}`);
|
|
11345
11494
|
}
|
|
11346
11495
|
}
|
|
11347
|
-
const installCmd =
|
|
11496
|
+
const installCmd = getInstallCommand2(worktreePath);
|
|
11348
11497
|
if (installCmd) {
|
|
11349
11498
|
console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
11350
11499
|
console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
|
|
@@ -11956,8 +12105,8 @@ var Daemon = class _Daemon {
|
|
|
11956
12105
|
await this.shutdown();
|
|
11957
12106
|
try {
|
|
11958
12107
|
const pidPath = getPidFilePath();
|
|
11959
|
-
if (
|
|
11960
|
-
|
|
12108
|
+
if (fs23.existsSync(pidPath)) {
|
|
12109
|
+
fs23.unlinkSync(pidPath);
|
|
11961
12110
|
console.log("[Daemon] PID file cleaned up");
|
|
11962
12111
|
}
|
|
11963
12112
|
} catch (error) {
|