@episoda/cli 0.2.164 → 0.2.165
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.
|
@@ -1640,15 +1640,15 @@ var require_git_executor = __commonJS({
|
|
|
1640
1640
|
try {
|
|
1641
1641
|
const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1642
1642
|
const gitDirPath = gitDir.trim();
|
|
1643
|
-
const
|
|
1643
|
+
const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1644
1644
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1645
1645
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1646
1646
|
try {
|
|
1647
|
-
await
|
|
1647
|
+
await fs30.access(rebaseMergePath);
|
|
1648
1648
|
inRebase = true;
|
|
1649
1649
|
} catch {
|
|
1650
1650
|
try {
|
|
1651
|
-
await
|
|
1651
|
+
await fs30.access(rebaseApplyPath);
|
|
1652
1652
|
inRebase = true;
|
|
1653
1653
|
} catch {
|
|
1654
1654
|
inRebase = false;
|
|
@@ -1704,9 +1704,9 @@ var require_git_executor = __commonJS({
|
|
|
1704
1704
|
};
|
|
1705
1705
|
}
|
|
1706
1706
|
}
|
|
1707
|
-
const
|
|
1707
|
+
const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1708
1708
|
try {
|
|
1709
|
-
await
|
|
1709
|
+
await fs30.access(command.path);
|
|
1710
1710
|
return {
|
|
1711
1711
|
success: false,
|
|
1712
1712
|
error: "WORKTREE_EXISTS",
|
|
@@ -1765,9 +1765,9 @@ var require_git_executor = __commonJS({
|
|
|
1765
1765
|
*/
|
|
1766
1766
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1767
1767
|
try {
|
|
1768
|
-
const
|
|
1768
|
+
const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1769
1769
|
try {
|
|
1770
|
-
await
|
|
1770
|
+
await fs30.access(command.path);
|
|
1771
1771
|
} catch {
|
|
1772
1772
|
return {
|
|
1773
1773
|
success: false,
|
|
@@ -1802,7 +1802,7 @@ var require_git_executor = __commonJS({
|
|
|
1802
1802
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1803
1803
|
if (result.success) {
|
|
1804
1804
|
try {
|
|
1805
|
-
await
|
|
1805
|
+
await fs30.rm(command.path, { recursive: true, force: true });
|
|
1806
1806
|
} catch {
|
|
1807
1807
|
}
|
|
1808
1808
|
return {
|
|
@@ -1936,10 +1936,10 @@ var require_git_executor = __commonJS({
|
|
|
1936
1936
|
*/
|
|
1937
1937
|
async executeCloneBare(command, options) {
|
|
1938
1938
|
try {
|
|
1939
|
-
const
|
|
1940
|
-
const
|
|
1939
|
+
const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1940
|
+
const path31 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1941
1941
|
try {
|
|
1942
|
-
await
|
|
1942
|
+
await fs30.access(command.path);
|
|
1943
1943
|
return {
|
|
1944
1944
|
success: false,
|
|
1945
1945
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1948,9 +1948,9 @@ var require_git_executor = __commonJS({
|
|
|
1948
1948
|
};
|
|
1949
1949
|
} catch {
|
|
1950
1950
|
}
|
|
1951
|
-
const parentDir =
|
|
1951
|
+
const parentDir = path31.dirname(command.path);
|
|
1952
1952
|
try {
|
|
1953
|
-
await
|
|
1953
|
+
await fs30.mkdir(parentDir, { recursive: true });
|
|
1954
1954
|
} catch {
|
|
1955
1955
|
}
|
|
1956
1956
|
const { stdout, stderr } = await execAsync3(
|
|
@@ -1998,22 +1998,22 @@ var require_git_executor = __commonJS({
|
|
|
1998
1998
|
*/
|
|
1999
1999
|
async executeProjectInfo(cwd, options) {
|
|
2000
2000
|
try {
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2001
|
+
const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
2002
|
+
const path31 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
2003
2003
|
let currentPath = cwd;
|
|
2004
2004
|
let projectPath = cwd;
|
|
2005
2005
|
let bareRepoPath;
|
|
2006
2006
|
for (let i = 0; i < 10; i++) {
|
|
2007
|
-
const bareDir =
|
|
2008
|
-
const episodaDir =
|
|
2007
|
+
const bareDir = path31.join(currentPath, ".bare");
|
|
2008
|
+
const episodaDir = path31.join(currentPath, ".episoda");
|
|
2009
2009
|
try {
|
|
2010
|
-
await
|
|
2011
|
-
await
|
|
2010
|
+
await fs30.access(bareDir);
|
|
2011
|
+
await fs30.access(episodaDir);
|
|
2012
2012
|
projectPath = currentPath;
|
|
2013
2013
|
bareRepoPath = bareDir;
|
|
2014
2014
|
break;
|
|
2015
2015
|
} catch {
|
|
2016
|
-
const parentPath =
|
|
2016
|
+
const parentPath = path31.dirname(currentPath);
|
|
2017
2017
|
if (parentPath === currentPath) {
|
|
2018
2018
|
break;
|
|
2019
2019
|
}
|
|
@@ -2674,33 +2674,33 @@ var require_auth = __commonJS({
|
|
|
2674
2674
|
exports2.loadConfig = loadConfig16;
|
|
2675
2675
|
exports2.saveConfig = saveConfig4;
|
|
2676
2676
|
exports2.validateToken = validateToken;
|
|
2677
|
-
var
|
|
2678
|
-
var
|
|
2677
|
+
var fs30 = __importStar(require("fs"));
|
|
2678
|
+
var path31 = __importStar(require("path"));
|
|
2679
2679
|
var os13 = __importStar(require("os"));
|
|
2680
2680
|
var child_process_1 = require("child_process");
|
|
2681
2681
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2682
2682
|
var hasWarnedMissingProjectId = false;
|
|
2683
2683
|
var hasWarnedMissingRequiredFields = false;
|
|
2684
2684
|
function getConfigDir10() {
|
|
2685
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2685
|
+
return process.env.EPISODA_CONFIG_DIR || path31.join(os13.homedir(), ".episoda");
|
|
2686
2686
|
}
|
|
2687
2687
|
function getConfigPath(configPath) {
|
|
2688
2688
|
if (configPath) {
|
|
2689
2689
|
return configPath;
|
|
2690
2690
|
}
|
|
2691
|
-
return
|
|
2691
|
+
return path31.join(getConfigDir10(), DEFAULT_CONFIG_FILE);
|
|
2692
2692
|
}
|
|
2693
2693
|
function ensureConfigDir(configPath) {
|
|
2694
|
-
const dir =
|
|
2695
|
-
const isNew = !
|
|
2694
|
+
const dir = path31.dirname(configPath);
|
|
2695
|
+
const isNew = !fs30.existsSync(dir);
|
|
2696
2696
|
if (isNew) {
|
|
2697
|
-
|
|
2697
|
+
fs30.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2698
2698
|
}
|
|
2699
2699
|
if (process.platform === "darwin") {
|
|
2700
|
-
const nosyncPath =
|
|
2701
|
-
if (isNew || !
|
|
2700
|
+
const nosyncPath = path31.join(dir, ".nosync");
|
|
2701
|
+
if (isNew || !fs30.existsSync(nosyncPath)) {
|
|
2702
2702
|
try {
|
|
2703
|
-
|
|
2703
|
+
fs30.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2704
2704
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2705
2705
|
stdio: "ignore",
|
|
2706
2706
|
timeout: 5e3
|
|
@@ -2714,11 +2714,11 @@ var require_auth = __commonJS({
|
|
|
2714
2714
|
const fullPath = getConfigPath(configPath);
|
|
2715
2715
|
const isCloudMode = process.env.EPISODA_MODE === "cloud";
|
|
2716
2716
|
const readConfigFile = (pathToFile) => {
|
|
2717
|
-
if (!
|
|
2717
|
+
if (!fs30.existsSync(pathToFile)) {
|
|
2718
2718
|
return null;
|
|
2719
2719
|
}
|
|
2720
2720
|
try {
|
|
2721
|
-
const content =
|
|
2721
|
+
const content = fs30.readFileSync(pathToFile, "utf8");
|
|
2722
2722
|
return JSON.parse(content);
|
|
2723
2723
|
} catch (error) {
|
|
2724
2724
|
console.error("Error loading config:", error);
|
|
@@ -2757,11 +2757,11 @@ var require_auth = __commonJS({
|
|
|
2757
2757
|
}
|
|
2758
2758
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2759
2759
|
const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
|
|
2760
|
-
if (!
|
|
2760
|
+
if (!fs30.existsSync(workspaceConfigPath)) {
|
|
2761
2761
|
return null;
|
|
2762
2762
|
}
|
|
2763
2763
|
try {
|
|
2764
|
-
const content =
|
|
2764
|
+
const content = fs30.readFileSync(workspaceConfigPath, "utf8");
|
|
2765
2765
|
const workspaceConfig2 = JSON.parse(content);
|
|
2766
2766
|
const expiresAtEnv = envValue(process.env.EPISODA_ACCESS_TOKEN_EXPIRES_AT);
|
|
2767
2767
|
return {
|
|
@@ -2789,11 +2789,11 @@ var require_auth = __commonJS({
|
|
|
2789
2789
|
}
|
|
2790
2790
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2791
2791
|
const projectConfigPath = require("path").join(homeDir, "episoda", workspaceSlug, projectSlug, ".episoda", "config.json");
|
|
2792
|
-
if (!
|
|
2792
|
+
if (!fs30.existsSync(projectConfigPath)) {
|
|
2793
2793
|
return null;
|
|
2794
2794
|
}
|
|
2795
2795
|
try {
|
|
2796
|
-
const content =
|
|
2796
|
+
const content = fs30.readFileSync(projectConfigPath, "utf8");
|
|
2797
2797
|
const projectConfig2 = JSON.parse(content);
|
|
2798
2798
|
return {
|
|
2799
2799
|
project_id: projectConfig2.projectId || projectConfig2.project_id,
|
|
@@ -2861,7 +2861,7 @@ var require_auth = __commonJS({
|
|
|
2861
2861
|
ensureConfigDir(fullPath);
|
|
2862
2862
|
try {
|
|
2863
2863
|
const content = JSON.stringify(config, null, 2);
|
|
2864
|
-
|
|
2864
|
+
fs30.writeFileSync(fullPath, content, { mode: 384 });
|
|
2865
2865
|
} catch (error) {
|
|
2866
2866
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2867
2867
|
}
|
|
@@ -2978,7 +2978,7 @@ var require_package = __commonJS({
|
|
|
2978
2978
|
"package.json"(exports2, module2) {
|
|
2979
2979
|
module2.exports = {
|
|
2980
2980
|
name: "@episoda/cli",
|
|
2981
|
-
version: "0.2.
|
|
2981
|
+
version: "0.2.165",
|
|
2982
2982
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2983
2983
|
main: "dist/index.js",
|
|
2984
2984
|
types: "dist/index.d.ts",
|
|
@@ -4979,7 +4979,9 @@ var import_core9 = __toESM(require_dist());
|
|
|
4979
4979
|
|
|
4980
4980
|
// src/agent/claude-persistent-runtime.ts
|
|
4981
4981
|
var import_child_process7 = require("child_process");
|
|
4982
|
-
var
|
|
4982
|
+
var DEFAULT_STARTUP_GRACE_MS = 3e4;
|
|
4983
|
+
var HEARTBEAT_ACK_MARKER = "__EPISODA_HEARTBEAT_ACK__";
|
|
4984
|
+
var ECHO_TIMEOUT_MS = parseInt(process.env.AGENT_ECHO_TIMEOUT_MS || `${DEFAULT_STARTUP_GRACE_MS}`, 10);
|
|
4983
4985
|
var INACTIVITY_TIMEOUT_MS = parseInt(process.env.AGENT_STREAM_INACTIVITY_TIMEOUT_MS || "180000", 10);
|
|
4984
4986
|
var SHUTDOWN_SIGTERM_WAIT_MS = 2e3;
|
|
4985
4987
|
var SHUTDOWN_SIGKILL_WAIT_MS = 2e3;
|
|
@@ -5015,7 +5017,6 @@ var ClaudePersistentRuntime = class {
|
|
|
5015
5017
|
this.echoTimer = null;
|
|
5016
5018
|
this.inactivityTimer = null;
|
|
5017
5019
|
this.turnStartTime = 0;
|
|
5018
|
-
this.sawAnyStdoutThisTurn = false;
|
|
5019
5020
|
// EP1360: Instrumentation
|
|
5020
5021
|
this.spawnTimestamp = 0;
|
|
5021
5022
|
this.sessionId = options.sessionId;
|
|
@@ -5055,6 +5056,11 @@ var ClaudePersistentRuntime = class {
|
|
|
5055
5056
|
this.alive = true;
|
|
5056
5057
|
this.process.stderr?.on("data", (data) => {
|
|
5057
5058
|
const chunk = data.toString();
|
|
5059
|
+
this.resetInactivityTimer();
|
|
5060
|
+
if (this._turnState === "waiting_for_echo") {
|
|
5061
|
+
const source = chunk.includes(HEARTBEAT_ACK_MARKER) ? "stderr-marker" : "stderr";
|
|
5062
|
+
this.recordStartupHeartbeatAck(source);
|
|
5063
|
+
}
|
|
5058
5064
|
this.stderrTail = (this.stderrTail + chunk).slice(-this.maxStderrTailBytes);
|
|
5059
5065
|
if (RUNTIME_DEBUG) {
|
|
5060
5066
|
console.error(`[ClaudePersistentRuntime] stderr (session=${this.sessionId}): ${chunk.trimEnd()}`);
|
|
@@ -5062,6 +5068,7 @@ var ClaudePersistentRuntime = class {
|
|
|
5062
5068
|
});
|
|
5063
5069
|
this.process.stdout?.on("data", (data) => {
|
|
5064
5070
|
this.resetInactivityTimer();
|
|
5071
|
+
this.recordStartupHeartbeatAck("stdout");
|
|
5065
5072
|
this.stdoutBuffer += data.toString();
|
|
5066
5073
|
const lines = this.stdoutBuffer.split("\n");
|
|
5067
5074
|
this.stdoutBuffer = lines.pop() || "";
|
|
@@ -5112,7 +5119,6 @@ var ClaudePersistentRuntime = class {
|
|
|
5112
5119
|
this.turnStartTime = Date.now();
|
|
5113
5120
|
this.turnTtftLogged = false;
|
|
5114
5121
|
this.seenToolUseIds.clear();
|
|
5115
|
-
this.sawAnyStdoutThisTurn = false;
|
|
5116
5122
|
this._turnState = "waiting_for_echo";
|
|
5117
5123
|
const payload = JSON.stringify({ type: "user", message: { role: "user", content: message } }) + "\n";
|
|
5118
5124
|
try {
|
|
@@ -5125,9 +5131,9 @@ var ClaudePersistentRuntime = class {
|
|
|
5125
5131
|
}
|
|
5126
5132
|
this.echoTimer = setTimeout(() => {
|
|
5127
5133
|
if (this._turnState === "waiting_for_echo") {
|
|
5128
|
-
console.warn(`[ClaudePersistentRuntime]
|
|
5134
|
+
console.warn(`[ClaudePersistentRuntime] Startup heartbeat timeout after ${ECHO_TIMEOUT_MS}ms \u2014 auto-degrading. session=${this.sessionId}`);
|
|
5129
5135
|
if (this.callbacks) {
|
|
5130
|
-
this.callbacks.onError(`ECHO_TIMEOUT: No
|
|
5136
|
+
this.callbacks.onError(`ECHO_TIMEOUT: No startup heartbeat within ${ECHO_TIMEOUT_MS}ms after send`);
|
|
5131
5137
|
this.endTurn();
|
|
5132
5138
|
}
|
|
5133
5139
|
}
|
|
@@ -5210,14 +5216,7 @@ var ClaudePersistentRuntime = class {
|
|
|
5210
5216
|
}
|
|
5211
5217
|
handleParsedEvent(parsed) {
|
|
5212
5218
|
const type = parsed.type;
|
|
5213
|
-
|
|
5214
|
-
this.sawAnyStdoutThisTurn = true;
|
|
5215
|
-
this.clearEchoTimer();
|
|
5216
|
-
this._turnState = "streaming";
|
|
5217
|
-
if (RUNTIME_DEBUG) {
|
|
5218
|
-
console.log(`[ClaudePersistentRuntime] First stdout for turn \u2014 streaming started. session=${this.sessionId}`);
|
|
5219
|
-
}
|
|
5220
|
-
}
|
|
5219
|
+
this.recordStartupHeartbeatAck("stdout");
|
|
5221
5220
|
if (type === "user") {
|
|
5222
5221
|
return;
|
|
5223
5222
|
}
|
|
@@ -5403,6 +5402,14 @@ var ClaudePersistentRuntime = class {
|
|
|
5403
5402
|
this.endTurn();
|
|
5404
5403
|
cb.onComplete(this._agentSessionId, resultMeta);
|
|
5405
5404
|
}
|
|
5405
|
+
recordStartupHeartbeatAck(source) {
|
|
5406
|
+
if (this._turnState !== "waiting_for_echo") return;
|
|
5407
|
+
this.clearEchoTimer();
|
|
5408
|
+
this._turnState = "streaming";
|
|
5409
|
+
if (RUNTIME_DEBUG) {
|
|
5410
|
+
console.log(`[ClaudePersistentRuntime] Startup heartbeat ack via ${source}; streaming started. session=${this.sessionId}`);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5406
5413
|
endTurn() {
|
|
5407
5414
|
this.clearTimers();
|
|
5408
5415
|
this.callbacks = null;
|
|
@@ -9472,7 +9479,7 @@ function getInstallCommand(cwd) {
|
|
|
9472
9479
|
}
|
|
9473
9480
|
|
|
9474
9481
|
// src/daemon/daemon-process.ts
|
|
9475
|
-
var
|
|
9482
|
+
var fs29 = __toESM(require("fs"));
|
|
9476
9483
|
var http2 = __toESM(require("http"));
|
|
9477
9484
|
var os12 = __toESM(require("os"));
|
|
9478
9485
|
|
|
@@ -10280,8 +10287,8 @@ async function handleExec(command, projectPath) {
|
|
|
10280
10287
|
}
|
|
10281
10288
|
|
|
10282
10289
|
// src/daemon/handlers/worktree-handlers.ts
|
|
10283
|
-
var
|
|
10284
|
-
var
|
|
10290
|
+
var path24 = __toESM(require("path"));
|
|
10291
|
+
var fs23 = __toESM(require("fs"));
|
|
10285
10292
|
var os10 = __toESM(require("os"));
|
|
10286
10293
|
var import_child_process15 = require("child_process");
|
|
10287
10294
|
var import_util2 = require("util");
|
|
@@ -11765,6 +11772,42 @@ function detectPackageManager(worktreePath, preferredLanguages) {
|
|
|
11765
11772
|
};
|
|
11766
11773
|
}
|
|
11767
11774
|
|
|
11775
|
+
// src/daemon/build-packages.ts
|
|
11776
|
+
var fs22 = __toESM(require("fs"));
|
|
11777
|
+
var path23 = __toESM(require("path"));
|
|
11778
|
+
function hasPackageScript(worktreePath, scriptName) {
|
|
11779
|
+
try {
|
|
11780
|
+
const packageJsonPath = path23.join(worktreePath, "package.json");
|
|
11781
|
+
if (!fs22.existsSync(packageJsonPath)) {
|
|
11782
|
+
return false;
|
|
11783
|
+
}
|
|
11784
|
+
const packageJson2 = JSON.parse(fs22.readFileSync(packageJsonPath, "utf-8"));
|
|
11785
|
+
return typeof packageJson2.scripts?.[scriptName] === "string";
|
|
11786
|
+
} catch {
|
|
11787
|
+
return false;
|
|
11788
|
+
}
|
|
11789
|
+
}
|
|
11790
|
+
function getBuildPackagesCommand(packageManagerName, hasBuildPackagesScript) {
|
|
11791
|
+
if (!hasBuildPackagesScript) {
|
|
11792
|
+
return null;
|
|
11793
|
+
}
|
|
11794
|
+
switch (packageManagerName) {
|
|
11795
|
+
case "pnpm":
|
|
11796
|
+
return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
|
|
11797
|
+
case "yarn":
|
|
11798
|
+
return "yarn build:packages";
|
|
11799
|
+
case "npm":
|
|
11800
|
+
return "npm run build:packages";
|
|
11801
|
+
case "bun":
|
|
11802
|
+
return "bun run build:packages";
|
|
11803
|
+
default:
|
|
11804
|
+
return null;
|
|
11805
|
+
}
|
|
11806
|
+
}
|
|
11807
|
+
function shouldRunBuildPackagesBootstrap(installSucceeded, buildPackagesCommand) {
|
|
11808
|
+
return installSucceeded && !!buildPackagesCommand;
|
|
11809
|
+
}
|
|
11810
|
+
|
|
11768
11811
|
// src/daemon/handlers/worktree-handlers.ts
|
|
11769
11812
|
async function getConfigForApi() {
|
|
11770
11813
|
if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_ACCESS_TOKEN) {
|
|
@@ -11788,18 +11831,18 @@ function persistWorkspaceProjectContext(workspaceSlug, projectSlug, projectId) {
|
|
|
11788
11831
|
return;
|
|
11789
11832
|
}
|
|
11790
11833
|
const homeDir = process.env.HOME || os10.homedir();
|
|
11791
|
-
const workspaceConfigPath =
|
|
11834
|
+
const workspaceConfigPath = path24.join(
|
|
11792
11835
|
homeDir,
|
|
11793
11836
|
"episoda",
|
|
11794
11837
|
workspaceSlug,
|
|
11795
11838
|
".episoda",
|
|
11796
11839
|
"config.json"
|
|
11797
11840
|
);
|
|
11798
|
-
if (!
|
|
11841
|
+
if (!fs23.existsSync(workspaceConfigPath)) {
|
|
11799
11842
|
return;
|
|
11800
11843
|
}
|
|
11801
11844
|
try {
|
|
11802
|
-
const content =
|
|
11845
|
+
const content = fs23.readFileSync(workspaceConfigPath, "utf8");
|
|
11803
11846
|
const workspaceConfig = JSON.parse(content);
|
|
11804
11847
|
let changed = false;
|
|
11805
11848
|
if (projectId && workspaceConfig.projectId !== projectId && workspaceConfig.project_id !== projectId) {
|
|
@@ -11811,7 +11854,7 @@ function persistWorkspaceProjectContext(workspaceSlug, projectSlug, projectId) {
|
|
|
11811
11854
|
changed = true;
|
|
11812
11855
|
}
|
|
11813
11856
|
if (changed) {
|
|
11814
|
-
|
|
11857
|
+
fs23.writeFileSync(workspaceConfigPath, JSON.stringify(workspaceConfig, null, 2), "utf8");
|
|
11815
11858
|
console.log("[Worktree] Updated workspace config with project context");
|
|
11816
11859
|
}
|
|
11817
11860
|
} catch (error) {
|
|
@@ -11828,32 +11871,6 @@ async function autoDetectSetupScript(worktreePath) {
|
|
|
11828
11871
|
console.log(`[Worktree] EP1222: Detected ${name} (${language}) via ${detection.matchedFile}`);
|
|
11829
11872
|
return installCmd;
|
|
11830
11873
|
}
|
|
11831
|
-
function getBuildPackagesCommand(packageManagerName) {
|
|
11832
|
-
switch (packageManagerName) {
|
|
11833
|
-
case "pnpm":
|
|
11834
|
-
return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
|
|
11835
|
-
case "yarn":
|
|
11836
|
-
return "yarn build:packages";
|
|
11837
|
-
case "npm":
|
|
11838
|
-
return "npm run build:packages";
|
|
11839
|
-
case "bun":
|
|
11840
|
-
return "bun run build:packages";
|
|
11841
|
-
default:
|
|
11842
|
-
return null;
|
|
11843
|
-
}
|
|
11844
|
-
}
|
|
11845
|
-
function hasPackageScript(worktreePath, scriptName) {
|
|
11846
|
-
try {
|
|
11847
|
-
const packageJsonPath = path23.join(worktreePath, "package.json");
|
|
11848
|
-
if (!fs22.existsSync(packageJsonPath)) {
|
|
11849
|
-
return false;
|
|
11850
|
-
}
|
|
11851
|
-
const packageJson2 = JSON.parse(fs22.readFileSync(packageJsonPath, "utf-8"));
|
|
11852
|
-
return typeof packageJson2.scripts?.[scriptName] === "string";
|
|
11853
|
-
} catch {
|
|
11854
|
-
return false;
|
|
11855
|
-
}
|
|
11856
|
-
}
|
|
11857
11874
|
async function handleWorktreeCreate(request2) {
|
|
11858
11875
|
const {
|
|
11859
11876
|
workspaceSlug,
|
|
@@ -11884,19 +11901,19 @@ async function handleWorktreeCreate(request2) {
|
|
|
11884
11901
|
}
|
|
11885
11902
|
try {
|
|
11886
11903
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
11887
|
-
const bareRepoPath =
|
|
11904
|
+
const bareRepoPath = path24.join(projectPath, ".bare");
|
|
11888
11905
|
const gitEnv = projectId ? { ...process.env, EPISODA_PROJECT_ID: projectId } : process.env;
|
|
11889
|
-
if (!
|
|
11906
|
+
if (!fs23.existsSync(bareRepoPath)) {
|
|
11890
11907
|
console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
|
|
11891
11908
|
console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
|
|
11892
|
-
const episodaDir =
|
|
11893
|
-
|
|
11909
|
+
const episodaDir = path24.join(projectPath, ".episoda");
|
|
11910
|
+
fs23.mkdirSync(episodaDir, { recursive: true });
|
|
11894
11911
|
try {
|
|
11895
11912
|
console.log(`[Worktree] K1273: Starting git clone...`);
|
|
11896
11913
|
await execAsync2(`git clone --bare "${repoUrl}" "${bareRepoPath}"`, { env: gitEnv });
|
|
11897
11914
|
console.log(`[Worktree] K1273: Clone successful`);
|
|
11898
11915
|
await execAsync2(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`, { env: gitEnv });
|
|
11899
|
-
const configPath =
|
|
11916
|
+
const configPath = path24.join(episodaDir, "config.json");
|
|
11900
11917
|
const config = {
|
|
11901
11918
|
projectId,
|
|
11902
11919
|
workspaceSlug,
|
|
@@ -11905,7 +11922,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
11905
11922
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11906
11923
|
worktrees: []
|
|
11907
11924
|
};
|
|
11908
|
-
|
|
11925
|
+
fs23.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
11909
11926
|
console.log(`[Worktree] K1273: Project initialized at ${projectPath}`);
|
|
11910
11927
|
} catch (cloneError) {
|
|
11911
11928
|
console.error(`[Worktree] K1273: Git clone failed: ${cloneError.message}`);
|
|
@@ -11940,16 +11957,16 @@ async function handleWorktreeCreate(request2) {
|
|
|
11940
11957
|
let finalError;
|
|
11941
11958
|
if (envVars && Object.keys(envVars).length > 0) {
|
|
11942
11959
|
const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
11943
|
-
const envPath =
|
|
11944
|
-
|
|
11960
|
+
const envPath = path24.join(worktreePath, ".env");
|
|
11961
|
+
fs23.writeFileSync(envPath, envContent + "\n", "utf-8");
|
|
11945
11962
|
console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
|
|
11946
11963
|
}
|
|
11947
11964
|
const isCloud = process.env.EPISODA_MODE === "cloud";
|
|
11948
11965
|
let effectiveSetupScript = setupScript || await autoDetectSetupScript(worktreePath);
|
|
11949
11966
|
const detection = detectPackageManager(worktreePath);
|
|
11950
|
-
const buildCmd = getBuildPackagesCommand(detection.packageManager?.name);
|
|
11951
11967
|
const hasBuildPackages = hasPackageScript(worktreePath, "build:packages");
|
|
11952
|
-
|
|
11968
|
+
const buildCmd = getBuildPackagesCommand(detection.packageManager?.name, hasBuildPackages);
|
|
11969
|
+
if (buildCmd && !effectiveSetupScript?.includes("build:packages")) {
|
|
11953
11970
|
effectiveSetupScript = effectiveSetupScript ? `${effectiveSetupScript}
|
|
11954
11971
|
${buildCmd}` : buildCmd;
|
|
11955
11972
|
console.log(`[Worktree] EP1386: Added build:packages bootstrap for ${isCloud ? "cloud" : "local"} setup`);
|
|
@@ -12082,12 +12099,12 @@ async function handleProjectEject(request2) {
|
|
|
12082
12099
|
console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
|
|
12083
12100
|
try {
|
|
12084
12101
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
12085
|
-
const bareRepoPath =
|
|
12086
|
-
if (!
|
|
12102
|
+
const bareRepoPath = path24.join(projectPath, ".bare");
|
|
12103
|
+
if (!fs23.existsSync(projectPath)) {
|
|
12087
12104
|
console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
|
|
12088
12105
|
return { success: true };
|
|
12089
12106
|
}
|
|
12090
|
-
if (!
|
|
12107
|
+
if (!fs23.existsSync(bareRepoPath)) {
|
|
12091
12108
|
console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
|
|
12092
12109
|
return { success: true };
|
|
12093
12110
|
}
|
|
@@ -12102,12 +12119,12 @@ async function handleProjectEject(request2) {
|
|
|
12102
12119
|
};
|
|
12103
12120
|
}
|
|
12104
12121
|
}
|
|
12105
|
-
const artifactsPath =
|
|
12106
|
-
if (
|
|
12122
|
+
const artifactsPath = path24.join(projectPath, "artifacts");
|
|
12123
|
+
if (fs23.existsSync(artifactsPath)) {
|
|
12107
12124
|
await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
|
|
12108
12125
|
}
|
|
12109
12126
|
console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
|
|
12110
|
-
await
|
|
12127
|
+
await fs23.promises.rm(projectPath, { recursive: true, force: true });
|
|
12111
12128
|
console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
|
|
12112
12129
|
return { success: true };
|
|
12113
12130
|
} catch (error) {
|
|
@@ -12121,7 +12138,7 @@ async function handleProjectEject(request2) {
|
|
|
12121
12138
|
async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
|
|
12122
12139
|
const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
|
|
12123
12140
|
try {
|
|
12124
|
-
const files = await
|
|
12141
|
+
const files = await fs23.promises.readdir(artifactsPath);
|
|
12125
12142
|
if (files.length === 0) {
|
|
12126
12143
|
return;
|
|
12127
12144
|
}
|
|
@@ -12135,8 +12152,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
12135
12152
|
}
|
|
12136
12153
|
const artifacts = [];
|
|
12137
12154
|
for (const fileName of files) {
|
|
12138
|
-
const filePath =
|
|
12139
|
-
const stat = await
|
|
12155
|
+
const filePath = path24.join(artifactsPath, fileName);
|
|
12156
|
+
const stat = await fs23.promises.stat(filePath);
|
|
12140
12157
|
if (stat.isDirectory()) {
|
|
12141
12158
|
continue;
|
|
12142
12159
|
}
|
|
@@ -12145,9 +12162,9 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
12145
12162
|
continue;
|
|
12146
12163
|
}
|
|
12147
12164
|
try {
|
|
12148
|
-
const content = await
|
|
12165
|
+
const content = await fs23.promises.readFile(filePath);
|
|
12149
12166
|
const base64Content = content.toString("base64");
|
|
12150
|
-
const ext =
|
|
12167
|
+
const ext = path24.extname(fileName).toLowerCase();
|
|
12151
12168
|
const mimeTypes = {
|
|
12152
12169
|
".json": "application/json",
|
|
12153
12170
|
".txt": "text/plain",
|
|
@@ -12203,8 +12220,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
12203
12220
|
}
|
|
12204
12221
|
|
|
12205
12222
|
// src/daemon/handlers/project-handlers.ts
|
|
12206
|
-
var
|
|
12207
|
-
var
|
|
12223
|
+
var path25 = __toESM(require("path"));
|
|
12224
|
+
var fs24 = __toESM(require("fs"));
|
|
12208
12225
|
function validateSlug(slug, fieldName) {
|
|
12209
12226
|
if (!slug || typeof slug !== "string") {
|
|
12210
12227
|
return `${fieldName} is required`;
|
|
@@ -12244,14 +12261,14 @@ async function handleProjectSetup(params) {
|
|
|
12244
12261
|
console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
|
|
12245
12262
|
try {
|
|
12246
12263
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
12247
|
-
const artifactsPath =
|
|
12248
|
-
const configDir =
|
|
12249
|
-
const configPath =
|
|
12250
|
-
await
|
|
12251
|
-
await
|
|
12264
|
+
const artifactsPath = path25.join(projectPath, "artifacts");
|
|
12265
|
+
const configDir = path25.join(projectPath, ".episoda");
|
|
12266
|
+
const configPath = path25.join(configDir, "config.json");
|
|
12267
|
+
await fs24.promises.mkdir(artifactsPath, { recursive: true });
|
|
12268
|
+
await fs24.promises.mkdir(configDir, { recursive: true });
|
|
12252
12269
|
let existingConfig = {};
|
|
12253
12270
|
try {
|
|
12254
|
-
const existing = await
|
|
12271
|
+
const existing = await fs24.promises.readFile(configPath, "utf-8");
|
|
12255
12272
|
existingConfig = JSON.parse(existing);
|
|
12256
12273
|
} catch {
|
|
12257
12274
|
}
|
|
@@ -12264,7 +12281,7 @@ async function handleProjectSetup(params) {
|
|
|
12264
12281
|
// Only set created_at if not already present
|
|
12265
12282
|
created_at: existingConfig.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
12266
12283
|
};
|
|
12267
|
-
await
|
|
12284
|
+
await fs24.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
12268
12285
|
console.log(`[ProjectSetup] EP1199: Project setup complete at ${projectPath}`);
|
|
12269
12286
|
return {
|
|
12270
12287
|
success: true,
|
|
@@ -12284,8 +12301,8 @@ async function handleProjectSetup(params) {
|
|
|
12284
12301
|
// src/utils/dev-server.ts
|
|
12285
12302
|
var import_child_process16 = require("child_process");
|
|
12286
12303
|
var import_core14 = __toESM(require_dist());
|
|
12287
|
-
var
|
|
12288
|
-
var
|
|
12304
|
+
var fs25 = __toESM(require("fs"));
|
|
12305
|
+
var path26 = __toESM(require("path"));
|
|
12289
12306
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
12290
12307
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
12291
12308
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -12293,26 +12310,26 @@ var MAX_LOG_SIZE_BYTES2 = 5 * 1024 * 1024;
|
|
|
12293
12310
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
12294
12311
|
var activeServers = /* @__PURE__ */ new Map();
|
|
12295
12312
|
function getLogsDir() {
|
|
12296
|
-
const logsDir =
|
|
12297
|
-
if (!
|
|
12298
|
-
|
|
12313
|
+
const logsDir = path26.join((0, import_core14.getConfigDir)(), "logs");
|
|
12314
|
+
if (!fs25.existsSync(logsDir)) {
|
|
12315
|
+
fs25.mkdirSync(logsDir, { recursive: true });
|
|
12299
12316
|
}
|
|
12300
12317
|
return logsDir;
|
|
12301
12318
|
}
|
|
12302
12319
|
function getLogFilePath(moduleUid) {
|
|
12303
|
-
return
|
|
12320
|
+
return path26.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
12304
12321
|
}
|
|
12305
12322
|
function rotateLogIfNeeded(logPath) {
|
|
12306
12323
|
try {
|
|
12307
|
-
if (
|
|
12308
|
-
const stats =
|
|
12324
|
+
if (fs25.existsSync(logPath)) {
|
|
12325
|
+
const stats = fs25.statSync(logPath);
|
|
12309
12326
|
if (stats.size > MAX_LOG_SIZE_BYTES2) {
|
|
12310
12327
|
const backupPath = `${logPath}.1`;
|
|
12311
|
-
if (
|
|
12312
|
-
|
|
12328
|
+
if (fs25.existsSync(backupPath)) {
|
|
12329
|
+
fs25.unlinkSync(backupPath);
|
|
12313
12330
|
}
|
|
12314
|
-
|
|
12315
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
12331
|
+
fs25.renameSync(logPath, backupPath);
|
|
12332
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path26.basename(logPath)}`);
|
|
12316
12333
|
}
|
|
12317
12334
|
}
|
|
12318
12335
|
} catch (error) {
|
|
@@ -12325,7 +12342,7 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
12325
12342
|
const prefix = isError ? "ERR" : "OUT";
|
|
12326
12343
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
12327
12344
|
`;
|
|
12328
|
-
|
|
12345
|
+
fs25.appendFileSync(logPath, logLine);
|
|
12329
12346
|
} catch {
|
|
12330
12347
|
}
|
|
12331
12348
|
}
|
|
@@ -12504,8 +12521,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
12504
12521
|
});
|
|
12505
12522
|
injectedEnvVars = result.envVars;
|
|
12506
12523
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
12507
|
-
const envFilePath =
|
|
12508
|
-
if (!
|
|
12524
|
+
const envFilePath = path26.join(projectPath, ".env");
|
|
12525
|
+
if (!fs25.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
12509
12526
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
12510
12527
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
12511
12528
|
}
|
|
@@ -12846,8 +12863,8 @@ var IPCRouter = class {
|
|
|
12846
12863
|
};
|
|
12847
12864
|
|
|
12848
12865
|
// src/daemon/update-manager.ts
|
|
12849
|
-
var
|
|
12850
|
-
var
|
|
12866
|
+
var fs26 = __toESM(require("fs"));
|
|
12867
|
+
var path27 = __toESM(require("path"));
|
|
12851
12868
|
var import_child_process18 = require("child_process");
|
|
12852
12869
|
var import_core17 = __toESM(require_dist());
|
|
12853
12870
|
var semver2 = __toESM(require("semver"));
|
|
@@ -13105,8 +13122,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
13105
13122
|
console.log(`[Daemon] EP1324: Update to ${targetVersion} installed, restarting daemon...`);
|
|
13106
13123
|
await this.host.shutdown();
|
|
13107
13124
|
const configDir = (0, import_core17.getConfigDir)();
|
|
13108
|
-
const logPath =
|
|
13109
|
-
const logFd =
|
|
13125
|
+
const logPath = path27.join(configDir, "daemon.log");
|
|
13126
|
+
const logFd = fs26.openSync(logPath, "a");
|
|
13110
13127
|
const child = (0, import_child_process18.spawn)("node", [this.daemonEntryFile], {
|
|
13111
13128
|
detached: true,
|
|
13112
13129
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -13114,7 +13131,7 @@ var UpdateManager = class _UpdateManager {
|
|
|
13114
13131
|
});
|
|
13115
13132
|
if (!child.pid) {
|
|
13116
13133
|
try {
|
|
13117
|
-
|
|
13134
|
+
fs26.closeSync(logFd);
|
|
13118
13135
|
} catch {
|
|
13119
13136
|
}
|
|
13120
13137
|
this.recordUpdateFailure(targetVersion, "Failed to spawn replacement daemon (missing pid)");
|
|
@@ -13122,7 +13139,7 @@ var UpdateManager = class _UpdateManager {
|
|
|
13122
13139
|
}
|
|
13123
13140
|
child.unref();
|
|
13124
13141
|
const pidPath = getPidFilePath();
|
|
13125
|
-
|
|
13142
|
+
fs26.writeFileSync(pidPath, child.pid.toString(), "utf-8");
|
|
13126
13143
|
console.log(`[Daemon] EP1324: New daemon spawned (PID: ${child.pid}), exiting old process`);
|
|
13127
13144
|
process.exit(0);
|
|
13128
13145
|
} catch (error) {
|
|
@@ -13173,8 +13190,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
13173
13190
|
};
|
|
13174
13191
|
|
|
13175
13192
|
// src/daemon/health-orchestrator.ts
|
|
13176
|
-
var
|
|
13177
|
-
var
|
|
13193
|
+
var fs27 = __toESM(require("fs"));
|
|
13194
|
+
var path28 = __toESM(require("path"));
|
|
13178
13195
|
var import_core18 = __toESM(require_dist());
|
|
13179
13196
|
var HealthOrchestrator = class _HealthOrchestrator {
|
|
13180
13197
|
constructor(host) {
|
|
@@ -13416,26 +13433,26 @@ var HealthOrchestrator = class _HealthOrchestrator {
|
|
|
13416
13433
|
checkAndRotateLog() {
|
|
13417
13434
|
try {
|
|
13418
13435
|
const configDir = (0, import_core18.getConfigDir)();
|
|
13419
|
-
const logPath =
|
|
13420
|
-
if (!
|
|
13421
|
-
const stats =
|
|
13436
|
+
const logPath = path28.join(configDir, "daemon.log");
|
|
13437
|
+
if (!fs27.existsSync(logPath)) return;
|
|
13438
|
+
const stats = fs27.statSync(logPath);
|
|
13422
13439
|
if (stats.size < _HealthOrchestrator.MAX_LOG_SIZE_BYTES) return;
|
|
13423
13440
|
console.log(`[Daemon] EP1351: daemon.log is ${Math.round(stats.size / 1024 / 1024)}MB, rotating...`);
|
|
13424
13441
|
for (let i = _HealthOrchestrator.MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
13425
13442
|
const src = `${logPath}.${i}`;
|
|
13426
13443
|
const dst = `${logPath}.${i + 1}`;
|
|
13427
|
-
if (
|
|
13444
|
+
if (fs27.existsSync(src)) {
|
|
13428
13445
|
try {
|
|
13429
|
-
|
|
13446
|
+
fs27.renameSync(src, dst);
|
|
13430
13447
|
} catch {
|
|
13431
13448
|
}
|
|
13432
13449
|
}
|
|
13433
13450
|
}
|
|
13434
13451
|
try {
|
|
13435
|
-
|
|
13452
|
+
fs27.copyFileSync(logPath, `${logPath}.1`);
|
|
13436
13453
|
} catch {
|
|
13437
13454
|
}
|
|
13438
|
-
|
|
13455
|
+
fs27.truncateSync(logPath, 0);
|
|
13439
13456
|
console.log("[Daemon] EP1351: Log rotated successfully");
|
|
13440
13457
|
} catch (error) {
|
|
13441
13458
|
console.warn("[Daemon] EP1351: Log rotation failed:", error instanceof Error ? error.message : error);
|
|
@@ -13865,7 +13882,7 @@ function resolveDaemonWsEndpoint(config, env = process.env) {
|
|
|
13865
13882
|
|
|
13866
13883
|
// src/daemon/project-message-router.ts
|
|
13867
13884
|
var import_core19 = __toESM(require_dist());
|
|
13868
|
-
var
|
|
13885
|
+
var path29 = __toESM(require("path"));
|
|
13869
13886
|
var ProjectMessageRouter = class {
|
|
13870
13887
|
constructor(host) {
|
|
13871
13888
|
this.host = host;
|
|
@@ -13882,7 +13899,7 @@ var ProjectMessageRouter = class {
|
|
|
13882
13899
|
client.updateActivity();
|
|
13883
13900
|
try {
|
|
13884
13901
|
const gitCmd = message.command;
|
|
13885
|
-
const bareRepoPath =
|
|
13902
|
+
const bareRepoPath = path29.join(projectPath, ".bare");
|
|
13886
13903
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
13887
13904
|
if (gitCmd.worktreePath) {
|
|
13888
13905
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -14222,32 +14239,32 @@ async function fetchEnvVars2(projectId) {
|
|
|
14222
14239
|
}
|
|
14223
14240
|
|
|
14224
14241
|
// src/daemon/project-git-config.ts
|
|
14225
|
-
var
|
|
14226
|
-
var
|
|
14242
|
+
var fs28 = __toESM(require("fs"));
|
|
14243
|
+
var path30 = __toESM(require("path"));
|
|
14227
14244
|
var import_core21 = __toESM(require_dist());
|
|
14228
14245
|
function getGitDirs(projectPath) {
|
|
14229
|
-
const bareDir =
|
|
14230
|
-
const gitPath =
|
|
14231
|
-
if (
|
|
14246
|
+
const bareDir = path30.join(projectPath, ".bare");
|
|
14247
|
+
const gitPath = path30.join(projectPath, ".git");
|
|
14248
|
+
if (fs28.existsSync(bareDir) && fs28.statSync(bareDir).isDirectory()) {
|
|
14232
14249
|
return { gitDir: bareDir, workDir: projectPath };
|
|
14233
14250
|
}
|
|
14234
|
-
if (
|
|
14251
|
+
if (fs28.existsSync(gitPath) && fs28.statSync(gitPath).isDirectory()) {
|
|
14235
14252
|
return { gitDir: null, workDir: projectPath };
|
|
14236
14253
|
}
|
|
14237
|
-
if (
|
|
14254
|
+
if (fs28.existsSync(gitPath) && fs28.statSync(gitPath).isFile()) {
|
|
14238
14255
|
return { gitDir: null, workDir: projectPath };
|
|
14239
14256
|
}
|
|
14240
|
-
const entries =
|
|
14257
|
+
const entries = fs28.readdirSync(projectPath, { withFileTypes: true });
|
|
14241
14258
|
for (const entry of entries) {
|
|
14242
14259
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
14243
|
-
const worktreePath =
|
|
14244
|
-
const worktreeGit =
|
|
14245
|
-
if (
|
|
14260
|
+
const worktreePath = path30.join(projectPath, entry.name);
|
|
14261
|
+
const worktreeGit = path30.join(worktreePath, ".git");
|
|
14262
|
+
if (fs28.existsSync(worktreeGit)) {
|
|
14246
14263
|
return { gitDir: null, workDir: worktreePath };
|
|
14247
14264
|
}
|
|
14248
14265
|
}
|
|
14249
14266
|
}
|
|
14250
|
-
if (
|
|
14267
|
+
if (fs28.existsSync(bareDir)) {
|
|
14251
14268
|
return { gitDir: bareDir, workDir: projectPath };
|
|
14252
14269
|
}
|
|
14253
14270
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -14292,24 +14309,24 @@ async function configureGitUser(projectPath, userId, workspaceId, machineId, pro
|
|
|
14292
14309
|
async function installGitHooks(projectPath) {
|
|
14293
14310
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
14294
14311
|
let hooksDir;
|
|
14295
|
-
const bareHooksDir =
|
|
14296
|
-
const gitHooksDir =
|
|
14297
|
-
if (
|
|
14312
|
+
const bareHooksDir = path30.join(projectPath, ".bare", "hooks");
|
|
14313
|
+
const gitHooksDir = path30.join(projectPath, ".git", "hooks");
|
|
14314
|
+
if (fs28.existsSync(bareHooksDir)) {
|
|
14298
14315
|
hooksDir = bareHooksDir;
|
|
14299
|
-
} else if (
|
|
14316
|
+
} else if (fs28.existsSync(gitHooksDir) && fs28.statSync(path30.join(projectPath, ".git")).isDirectory()) {
|
|
14300
14317
|
hooksDir = gitHooksDir;
|
|
14301
14318
|
} else {
|
|
14302
|
-
const parentBareHooks =
|
|
14303
|
-
if (
|
|
14319
|
+
const parentBareHooks = path30.join(projectPath, "..", ".bare", "hooks");
|
|
14320
|
+
if (fs28.existsSync(parentBareHooks)) {
|
|
14304
14321
|
hooksDir = parentBareHooks;
|
|
14305
14322
|
} else {
|
|
14306
14323
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
14307
14324
|
return;
|
|
14308
14325
|
}
|
|
14309
14326
|
}
|
|
14310
|
-
if (!
|
|
14327
|
+
if (!fs28.existsSync(hooksDir)) {
|
|
14311
14328
|
try {
|
|
14312
|
-
|
|
14329
|
+
fs28.mkdirSync(hooksDir, { recursive: true });
|
|
14313
14330
|
} catch {
|
|
14314
14331
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
14315
14332
|
return;
|
|
@@ -14317,20 +14334,20 @@ async function installGitHooks(projectPath) {
|
|
|
14317
14334
|
}
|
|
14318
14335
|
for (const hookName of hooks) {
|
|
14319
14336
|
try {
|
|
14320
|
-
const hookPath =
|
|
14321
|
-
const bundledHookPath =
|
|
14322
|
-
if (!
|
|
14337
|
+
const hookPath = path30.join(hooksDir, hookName);
|
|
14338
|
+
const bundledHookPath = path30.join(__dirname, "..", "hooks", hookName);
|
|
14339
|
+
if (!fs28.existsSync(bundledHookPath)) {
|
|
14323
14340
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
14324
14341
|
continue;
|
|
14325
14342
|
}
|
|
14326
|
-
const hookContent =
|
|
14327
|
-
if (
|
|
14328
|
-
const existingContent =
|
|
14343
|
+
const hookContent = fs28.readFileSync(bundledHookPath, "utf-8");
|
|
14344
|
+
if (fs28.existsSync(hookPath)) {
|
|
14345
|
+
const existingContent = fs28.readFileSync(hookPath, "utf-8");
|
|
14329
14346
|
if (existingContent === hookContent) {
|
|
14330
14347
|
continue;
|
|
14331
14348
|
}
|
|
14332
14349
|
}
|
|
14333
|
-
|
|
14350
|
+
fs28.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
14334
14351
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
14335
14352
|
} catch (error) {
|
|
14336
14353
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -14362,21 +14379,6 @@ async function cacheMachineUuid(machineUuid, machineId) {
|
|
|
14362
14379
|
|
|
14363
14380
|
// src/daemon/daemon-process.ts
|
|
14364
14381
|
var packageJson = require_package();
|
|
14365
|
-
function getBuildPackagesCommand2(installCmd) {
|
|
14366
|
-
const runner = installCmd?.command?.[0];
|
|
14367
|
-
switch (runner) {
|
|
14368
|
-
case "pnpm":
|
|
14369
|
-
return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
|
|
14370
|
-
case "yarn":
|
|
14371
|
-
return "yarn build:packages";
|
|
14372
|
-
case "npm":
|
|
14373
|
-
return "npm run build:packages";
|
|
14374
|
-
case "bun":
|
|
14375
|
-
return "bun run build:packages";
|
|
14376
|
-
default:
|
|
14377
|
-
return null;
|
|
14378
|
-
}
|
|
14379
|
-
}
|
|
14380
14382
|
var Daemon = class _Daemon {
|
|
14381
14383
|
constructor() {
|
|
14382
14384
|
this.machineId = "";
|
|
@@ -14488,9 +14490,9 @@ var Daemon = class _Daemon {
|
|
|
14488
14490
|
this.healthServer = http2.createServer((req, res) => {
|
|
14489
14491
|
if (req.url === "/health" || req.url === "/") {
|
|
14490
14492
|
const isConnected = this.connectionManager.liveConnectionCount() > 0;
|
|
14491
|
-
const projects = Array.from(this.connectionManager.entries()).map(([
|
|
14492
|
-
path:
|
|
14493
|
-
connected: this.connectionManager.hasLiveConnection(
|
|
14493
|
+
const projects = Array.from(this.connectionManager.entries()).map(([path31, conn]) => ({
|
|
14494
|
+
path: path31,
|
|
14495
|
+
connected: this.connectionManager.hasLiveConnection(path31)
|
|
14494
14496
|
}));
|
|
14495
14497
|
const status = {
|
|
14496
14498
|
status: isConnected ? "healthy" : "degraded",
|
|
@@ -14563,8 +14565,8 @@ var Daemon = class _Daemon {
|
|
|
14563
14565
|
await this.shutdown();
|
|
14564
14566
|
try {
|
|
14565
14567
|
const pidPath = getPidFilePath();
|
|
14566
|
-
if (
|
|
14567
|
-
|
|
14568
|
+
if (fs29.existsSync(pidPath)) {
|
|
14569
|
+
fs29.unlinkSync(pidPath);
|
|
14568
14570
|
console.log("[Daemon] PID file cleaned up (watchdog)");
|
|
14569
14571
|
}
|
|
14570
14572
|
} catch (error) {
|
|
@@ -14830,7 +14832,7 @@ var Daemon = class _Daemon {
|
|
|
14830
14832
|
const resolvedProjectPath = getProjectPath(cmd.workspaceSlug, cmd.projectSlug);
|
|
14831
14833
|
if (resolvedProjectPath !== projectPath) {
|
|
14832
14834
|
try {
|
|
14833
|
-
await
|
|
14835
|
+
await fs29.promises.access(resolvedProjectPath);
|
|
14834
14836
|
agentWorkingDir = resolvedProjectPath;
|
|
14835
14837
|
console.log(`[Daemon] EP1230: Agent for ${cmd.moduleUid || "project"} in resolved project path: ${agentWorkingDir}`);
|
|
14836
14838
|
} catch {
|
|
@@ -15162,8 +15164,8 @@ var Daemon = class _Daemon {
|
|
|
15162
15164
|
let daemonPid;
|
|
15163
15165
|
try {
|
|
15164
15166
|
const pidPath = getPidFilePath();
|
|
15165
|
-
if (
|
|
15166
|
-
const pidStr =
|
|
15167
|
+
if (fs29.existsSync(pidPath)) {
|
|
15168
|
+
const pidStr = fs29.readFileSync(pidPath, "utf-8").trim();
|
|
15167
15169
|
daemonPid = parseInt(pidStr, 10);
|
|
15168
15170
|
}
|
|
15169
15171
|
} catch (pidError) {
|
|
@@ -15633,23 +15635,25 @@ var Daemon = class _Daemon {
|
|
|
15633
15635
|
} else {
|
|
15634
15636
|
console.log(`[Daemon] EP1002: No package manager detected, skipping dependency installation`);
|
|
15635
15637
|
}
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
}
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15638
|
+
const hasBuildPackages = hasPackageScript(worktreePath, "build:packages");
|
|
15639
|
+
const buildCmd = getBuildPackagesCommand(installCmd?.command?.[0], hasBuildPackages);
|
|
15640
|
+
if (buildCmd && shouldRunBuildPackagesBootstrap(installSucceeded, buildCmd)) {
|
|
15641
|
+
const bootstrapCmd = buildCmd;
|
|
15642
|
+
console.log(`[Daemon] EP1386: Bootstrapping packages after dependency install (${bootstrapCmd})`);
|
|
15643
|
+
try {
|
|
15644
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
15645
|
+
execSync10(bootstrapCmd, {
|
|
15646
|
+
cwd: worktreePath,
|
|
15647
|
+
stdio: "inherit",
|
|
15648
|
+
timeout: 10 * 60 * 1e3,
|
|
15649
|
+
env: { ...process.env, CI: "true" }
|
|
15650
|
+
});
|
|
15651
|
+
console.log("[Daemon] EP1386: Package bootstrap completed");
|
|
15652
|
+
} catch (buildError) {
|
|
15653
|
+
const errorMsg = buildError instanceof Error ? buildError.message : String(buildError);
|
|
15654
|
+
await worktreeManager.updateWorktreeStatus(moduleUid, "error", errorMsg);
|
|
15655
|
+
await this.updateModuleWorktreeStatus(moduleUid, "error", worktreePath, errorMsg);
|
|
15656
|
+
throw new Error(`Package bootstrap failed: ${errorMsg}`);
|
|
15653
15657
|
}
|
|
15654
15658
|
}
|
|
15655
15659
|
if (setupScript) {
|
|
@@ -15731,8 +15735,8 @@ var Daemon = class _Daemon {
|
|
|
15731
15735
|
await this.shutdown();
|
|
15732
15736
|
try {
|
|
15733
15737
|
const pidPath = getPidFilePath();
|
|
15734
|
-
if (
|
|
15735
|
-
|
|
15738
|
+
if (fs29.existsSync(pidPath)) {
|
|
15739
|
+
fs29.unlinkSync(pidPath);
|
|
15736
15740
|
console.log("[Daemon] PID file cleaned up");
|
|
15737
15741
|
}
|
|
15738
15742
|
} catch (error) {
|