episoda 0.2.84 → 0.2.86
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.
|
@@ -1563,15 +1563,15 @@ var require_git_executor = __commonJS({
|
|
|
1563
1563
|
try {
|
|
1564
1564
|
const { stdout: gitDir } = await execAsync2("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1565
1565
|
const gitDirPath = gitDir.trim();
|
|
1566
|
-
const
|
|
1566
|
+
const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1567
1567
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1568
1568
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1569
1569
|
try {
|
|
1570
|
-
await
|
|
1570
|
+
await fs20.access(rebaseMergePath);
|
|
1571
1571
|
inRebase = true;
|
|
1572
1572
|
} catch {
|
|
1573
1573
|
try {
|
|
1574
|
-
await
|
|
1574
|
+
await fs20.access(rebaseApplyPath);
|
|
1575
1575
|
inRebase = true;
|
|
1576
1576
|
} catch {
|
|
1577
1577
|
inRebase = false;
|
|
@@ -1625,9 +1625,9 @@ var require_git_executor = __commonJS({
|
|
|
1625
1625
|
error: validation.error || "UNKNOWN_ERROR"
|
|
1626
1626
|
};
|
|
1627
1627
|
}
|
|
1628
|
-
const
|
|
1628
|
+
const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1629
1629
|
try {
|
|
1630
|
-
await
|
|
1630
|
+
await fs20.access(command.path);
|
|
1631
1631
|
return {
|
|
1632
1632
|
success: false,
|
|
1633
1633
|
error: "WORKTREE_EXISTS",
|
|
@@ -1681,9 +1681,9 @@ var require_git_executor = __commonJS({
|
|
|
1681
1681
|
*/
|
|
1682
1682
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1683
1683
|
try {
|
|
1684
|
-
const
|
|
1684
|
+
const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1685
1685
|
try {
|
|
1686
|
-
await
|
|
1686
|
+
await fs20.access(command.path);
|
|
1687
1687
|
} catch {
|
|
1688
1688
|
return {
|
|
1689
1689
|
success: false,
|
|
@@ -1718,7 +1718,7 @@ var require_git_executor = __commonJS({
|
|
|
1718
1718
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1719
1719
|
if (result.success) {
|
|
1720
1720
|
try {
|
|
1721
|
-
await
|
|
1721
|
+
await fs20.rm(command.path, { recursive: true, force: true });
|
|
1722
1722
|
} catch {
|
|
1723
1723
|
}
|
|
1724
1724
|
return {
|
|
@@ -1852,10 +1852,10 @@ var require_git_executor = __commonJS({
|
|
|
1852
1852
|
*/
|
|
1853
1853
|
async executeCloneBare(command, options) {
|
|
1854
1854
|
try {
|
|
1855
|
-
const
|
|
1856
|
-
const
|
|
1855
|
+
const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1856
|
+
const path21 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1857
1857
|
try {
|
|
1858
|
-
await
|
|
1858
|
+
await fs20.access(command.path);
|
|
1859
1859
|
return {
|
|
1860
1860
|
success: false,
|
|
1861
1861
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1864,9 +1864,9 @@ var require_git_executor = __commonJS({
|
|
|
1864
1864
|
};
|
|
1865
1865
|
} catch {
|
|
1866
1866
|
}
|
|
1867
|
-
const parentDir =
|
|
1867
|
+
const parentDir = path21.dirname(command.path);
|
|
1868
1868
|
try {
|
|
1869
|
-
await
|
|
1869
|
+
await fs20.mkdir(parentDir, { recursive: true });
|
|
1870
1870
|
} catch {
|
|
1871
1871
|
}
|
|
1872
1872
|
const { stdout, stderr } = await execAsync2(
|
|
@@ -1914,22 +1914,22 @@ var require_git_executor = __commonJS({
|
|
|
1914
1914
|
*/
|
|
1915
1915
|
async executeProjectInfo(cwd, options) {
|
|
1916
1916
|
try {
|
|
1917
|
-
const
|
|
1918
|
-
const
|
|
1917
|
+
const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1918
|
+
const path21 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1919
1919
|
let currentPath = cwd;
|
|
1920
1920
|
let projectPath = cwd;
|
|
1921
1921
|
let bareRepoPath;
|
|
1922
1922
|
for (let i = 0; i < 10; i++) {
|
|
1923
|
-
const bareDir =
|
|
1924
|
-
const episodaDir =
|
|
1923
|
+
const bareDir = path21.join(currentPath, ".bare");
|
|
1924
|
+
const episodaDir = path21.join(currentPath, ".episoda");
|
|
1925
1925
|
try {
|
|
1926
|
-
await
|
|
1927
|
-
await
|
|
1926
|
+
await fs20.access(bareDir);
|
|
1927
|
+
await fs20.access(episodaDir);
|
|
1928
1928
|
projectPath = currentPath;
|
|
1929
1929
|
bareRepoPath = bareDir;
|
|
1930
1930
|
break;
|
|
1931
1931
|
} catch {
|
|
1932
|
-
const parentPath =
|
|
1932
|
+
const parentPath = path21.dirname(currentPath);
|
|
1933
1933
|
if (parentPath === currentPath) {
|
|
1934
1934
|
break;
|
|
1935
1935
|
}
|
|
@@ -2564,31 +2564,31 @@ var require_auth = __commonJS({
|
|
|
2564
2564
|
exports2.loadConfig = loadConfig7;
|
|
2565
2565
|
exports2.saveConfig = saveConfig2;
|
|
2566
2566
|
exports2.validateToken = validateToken;
|
|
2567
|
-
var
|
|
2568
|
-
var
|
|
2567
|
+
var fs20 = __importStar(require("fs"));
|
|
2568
|
+
var path21 = __importStar(require("path"));
|
|
2569
2569
|
var os9 = __importStar(require("os"));
|
|
2570
2570
|
var child_process_1 = require("child_process");
|
|
2571
2571
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2572
2572
|
function getConfigDir8() {
|
|
2573
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2573
|
+
return process.env.EPISODA_CONFIG_DIR || path21.join(os9.homedir(), ".episoda");
|
|
2574
2574
|
}
|
|
2575
2575
|
function getConfigPath(configPath) {
|
|
2576
2576
|
if (configPath) {
|
|
2577
2577
|
return configPath;
|
|
2578
2578
|
}
|
|
2579
|
-
return
|
|
2579
|
+
return path21.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
|
|
2580
2580
|
}
|
|
2581
2581
|
function ensureConfigDir(configPath) {
|
|
2582
|
-
const dir =
|
|
2583
|
-
const isNew = !
|
|
2582
|
+
const dir = path21.dirname(configPath);
|
|
2583
|
+
const isNew = !fs20.existsSync(dir);
|
|
2584
2584
|
if (isNew) {
|
|
2585
|
-
|
|
2585
|
+
fs20.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2586
2586
|
}
|
|
2587
2587
|
if (process.platform === "darwin") {
|
|
2588
|
-
const nosyncPath =
|
|
2589
|
-
if (isNew || !
|
|
2588
|
+
const nosyncPath = path21.join(dir, ".nosync");
|
|
2589
|
+
if (isNew || !fs20.existsSync(nosyncPath)) {
|
|
2590
2590
|
try {
|
|
2591
|
-
|
|
2591
|
+
fs20.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2592
2592
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2593
2593
|
stdio: "ignore",
|
|
2594
2594
|
timeout: 5e3
|
|
@@ -2600,9 +2600,9 @@ var require_auth = __commonJS({
|
|
|
2600
2600
|
}
|
|
2601
2601
|
async function loadConfig7(configPath) {
|
|
2602
2602
|
const fullPath = getConfigPath(configPath);
|
|
2603
|
-
if (
|
|
2603
|
+
if (fs20.existsSync(fullPath)) {
|
|
2604
2604
|
try {
|
|
2605
|
-
const content =
|
|
2605
|
+
const content = fs20.readFileSync(fullPath, "utf8");
|
|
2606
2606
|
const config = JSON.parse(content);
|
|
2607
2607
|
return config;
|
|
2608
2608
|
} catch (error) {
|
|
@@ -2631,7 +2631,7 @@ var require_auth = __commonJS({
|
|
|
2631
2631
|
ensureConfigDir(fullPath);
|
|
2632
2632
|
try {
|
|
2633
2633
|
const content = JSON.stringify(config, null, 2);
|
|
2634
|
-
|
|
2634
|
+
fs20.writeFileSync(fullPath, content, { mode: 384 });
|
|
2635
2635
|
} catch (error) {
|
|
2636
2636
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2637
2637
|
}
|
|
@@ -2748,7 +2748,7 @@ var require_package = __commonJS({
|
|
|
2748
2748
|
"package.json"(exports2, module2) {
|
|
2749
2749
|
module2.exports = {
|
|
2750
2750
|
name: "episoda",
|
|
2751
|
-
version: "0.2.
|
|
2751
|
+
version: "0.2.86",
|
|
2752
2752
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2753
2753
|
main: "dist/index.js",
|
|
2754
2754
|
types: "dist/index.d.ts",
|
|
@@ -2782,7 +2782,8 @@ var require_package = __commonJS({
|
|
|
2782
2782
|
zod: "^4.0.10"
|
|
2783
2783
|
},
|
|
2784
2784
|
optionalDependencies: {
|
|
2785
|
-
"@anthropic-ai/claude-code": "^2.0.0"
|
|
2785
|
+
"@anthropic-ai/claude-code": "^2.0.0",
|
|
2786
|
+
"@openai/codex": "^0.86.0"
|
|
2786
2787
|
},
|
|
2787
2788
|
devDependencies: {
|
|
2788
2789
|
"@episoda/core": "*",
|
|
@@ -4947,10 +4948,118 @@ async function ensureClaudeBinary() {
|
|
|
4947
4948
|
);
|
|
4948
4949
|
}
|
|
4949
4950
|
|
|
4950
|
-
// src/agent/
|
|
4951
|
+
// src/agent/codex-binary.ts
|
|
4951
4952
|
var import_child_process8 = require("child_process");
|
|
4952
4953
|
var path9 = __toESM(require("path"));
|
|
4953
4954
|
var fs8 = __toESM(require("fs"));
|
|
4955
|
+
var cachedBinaryPath2 = null;
|
|
4956
|
+
function isValidCodexBinary(binaryPath) {
|
|
4957
|
+
try {
|
|
4958
|
+
fs8.accessSync(binaryPath, fs8.constants.X_OK);
|
|
4959
|
+
const version = (0, import_child_process8.execSync)(`"${binaryPath}" --version`, {
|
|
4960
|
+
encoding: "utf-8",
|
|
4961
|
+
timeout: 5e3,
|
|
4962
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4963
|
+
}).trim();
|
|
4964
|
+
if (version && /codex.*\d+\.\d+/i.test(version)) {
|
|
4965
|
+
console.log(`[CodexBinary] Found Codex CLI at ${binaryPath}: ${version}`);
|
|
4966
|
+
return true;
|
|
4967
|
+
}
|
|
4968
|
+
return false;
|
|
4969
|
+
} catch {
|
|
4970
|
+
return false;
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
async function ensureCodexBinary() {
|
|
4974
|
+
if (cachedBinaryPath2) {
|
|
4975
|
+
return cachedBinaryPath2;
|
|
4976
|
+
}
|
|
4977
|
+
try {
|
|
4978
|
+
const pathResult = (0, import_child_process8.execSync)("which codex", {
|
|
4979
|
+
encoding: "utf-8",
|
|
4980
|
+
timeout: 5e3,
|
|
4981
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4982
|
+
}).trim();
|
|
4983
|
+
if (pathResult && isValidCodexBinary(pathResult)) {
|
|
4984
|
+
cachedBinaryPath2 = pathResult;
|
|
4985
|
+
return cachedBinaryPath2;
|
|
4986
|
+
}
|
|
4987
|
+
} catch {
|
|
4988
|
+
}
|
|
4989
|
+
const bundledPaths = [
|
|
4990
|
+
// In production: node_modules/.bin/codex
|
|
4991
|
+
path9.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
|
|
4992
|
+
// In monorepo development: packages/episoda/node_modules/.bin/codex
|
|
4993
|
+
path9.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
|
|
4994
|
+
// Root monorepo node_modules
|
|
4995
|
+
path9.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
|
|
4996
|
+
];
|
|
4997
|
+
for (const bundledPath of bundledPaths) {
|
|
4998
|
+
if (fs8.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
|
|
4999
|
+
cachedBinaryPath2 = bundledPath;
|
|
5000
|
+
return cachedBinaryPath2;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
try {
|
|
5004
|
+
const npxResult = (0, import_child_process8.execSync)("npx --yes @openai/codex --version", {
|
|
5005
|
+
encoding: "utf-8",
|
|
5006
|
+
timeout: 3e4,
|
|
5007
|
+
// npx might need to download
|
|
5008
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5009
|
+
}).trim();
|
|
5010
|
+
if (npxResult && /codex.*\d+\.\d+/i.test(npxResult)) {
|
|
5011
|
+
cachedBinaryPath2 = "npx:@openai/codex";
|
|
5012
|
+
console.log(`[CodexBinary] Using npx to run Codex CLI: ${npxResult}`);
|
|
5013
|
+
return cachedBinaryPath2;
|
|
5014
|
+
}
|
|
5015
|
+
} catch {
|
|
5016
|
+
}
|
|
5017
|
+
throw new Error(
|
|
5018
|
+
"Codex CLI not found. Please install it globally with: npm install -g @openai/codex"
|
|
5019
|
+
);
|
|
5020
|
+
}
|
|
5021
|
+
|
|
5022
|
+
// src/agent/codex-config.ts
|
|
5023
|
+
function generateCodexAuthJson(credentials) {
|
|
5024
|
+
const tokens = {
|
|
5025
|
+
id_token: credentials.idToken || credentials.accessToken,
|
|
5026
|
+
// Fallback to access_token if no id_token
|
|
5027
|
+
access_token: credentials.accessToken
|
|
5028
|
+
};
|
|
5029
|
+
if (credentials.refreshToken) {
|
|
5030
|
+
tokens.refresh_token = credentials.refreshToken;
|
|
5031
|
+
}
|
|
5032
|
+
if (credentials.accountId) {
|
|
5033
|
+
tokens.account_id = credentials.accountId;
|
|
5034
|
+
}
|
|
5035
|
+
const authData = {
|
|
5036
|
+
OPENAI_API_KEY: null,
|
|
5037
|
+
// This null is expected by Codex CLI
|
|
5038
|
+
tokens,
|
|
5039
|
+
last_refresh: (/* @__PURE__ */ new Date()).toISOString()
|
|
5040
|
+
};
|
|
5041
|
+
return JSON.stringify(authData, null, 2);
|
|
5042
|
+
}
|
|
5043
|
+
function generateCodexConfigToml(projectPath) {
|
|
5044
|
+
const escapedPath = projectPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
5045
|
+
return `[projects."${escapedPath}"]
|
|
5046
|
+
trust_level = "trusted"
|
|
5047
|
+
`;
|
|
5048
|
+
}
|
|
5049
|
+
function generateCodexConfig(credentials, projectPath) {
|
|
5050
|
+
const files = {
|
|
5051
|
+
"auth.json": generateCodexAuthJson(credentials)
|
|
5052
|
+
};
|
|
5053
|
+
if (projectPath) {
|
|
5054
|
+
files["config.toml"] = generateCodexConfigToml(projectPath);
|
|
5055
|
+
}
|
|
5056
|
+
return files;
|
|
5057
|
+
}
|
|
5058
|
+
|
|
5059
|
+
// src/agent/agent-manager.ts
|
|
5060
|
+
var import_child_process9 = require("child_process");
|
|
5061
|
+
var path10 = __toESM(require("path"));
|
|
5062
|
+
var fs9 = __toESM(require("fs"));
|
|
4954
5063
|
var os3 = __toESM(require("os"));
|
|
4955
5064
|
|
|
4956
5065
|
// src/agent/claude-config.ts
|
|
@@ -5255,11 +5364,30 @@ var AgentManager = class {
|
|
|
5255
5364
|
this.sessions = /* @__PURE__ */ new Map();
|
|
5256
5365
|
this.processes = /* @__PURE__ */ new Map();
|
|
5257
5366
|
this.initialized = false;
|
|
5258
|
-
|
|
5367
|
+
// EP1133: Lock for config file writes to prevent race conditions
|
|
5368
|
+
this.configWriteLock = Promise.resolve();
|
|
5369
|
+
this.pidDir = path10.join(os3.homedir(), ".episoda", "agent-pids");
|
|
5370
|
+
}
|
|
5371
|
+
/**
|
|
5372
|
+
* EP1133: Acquire lock for config file writes
|
|
5373
|
+
* Ensures sequential writes to prevent file corruption
|
|
5374
|
+
*/
|
|
5375
|
+
async withConfigLock(fn) {
|
|
5376
|
+
const previousLock = this.configWriteLock;
|
|
5377
|
+
let releaseLock;
|
|
5378
|
+
this.configWriteLock = new Promise((resolve3) => {
|
|
5379
|
+
releaseLock = resolve3;
|
|
5380
|
+
});
|
|
5381
|
+
try {
|
|
5382
|
+
await previousLock;
|
|
5383
|
+
return await fn();
|
|
5384
|
+
} finally {
|
|
5385
|
+
releaseLock();
|
|
5386
|
+
}
|
|
5259
5387
|
}
|
|
5260
5388
|
/**
|
|
5261
5389
|
* Initialize the agent manager
|
|
5262
|
-
* - Ensure Claude Code
|
|
5390
|
+
* - Ensure agent CLIs are available (Claude Code, Codex)
|
|
5263
5391
|
* - Clean up any orphaned processes from previous daemon runs
|
|
5264
5392
|
*/
|
|
5265
5393
|
async initialize() {
|
|
@@ -5267,8 +5395,8 @@ var AgentManager = class {
|
|
|
5267
5395
|
return;
|
|
5268
5396
|
}
|
|
5269
5397
|
console.log("[AgentManager] Initializing...");
|
|
5270
|
-
if (!
|
|
5271
|
-
|
|
5398
|
+
if (!fs9.existsSync(this.pidDir)) {
|
|
5399
|
+
fs9.mkdirSync(this.pidDir, { recursive: true });
|
|
5272
5400
|
}
|
|
5273
5401
|
await this.cleanupOrphanedProcesses();
|
|
5274
5402
|
try {
|
|
@@ -5277,6 +5405,12 @@ var AgentManager = class {
|
|
|
5277
5405
|
} catch (error) {
|
|
5278
5406
|
console.warn("[AgentManager] Claude Code not available:", error instanceof Error ? error.message : error);
|
|
5279
5407
|
}
|
|
5408
|
+
try {
|
|
5409
|
+
await ensureCodexBinary();
|
|
5410
|
+
console.log("[AgentManager] Codex CLI binary verified");
|
|
5411
|
+
} catch (error) {
|
|
5412
|
+
console.warn("[AgentManager] Codex CLI not available:", error instanceof Error ? error.message : error);
|
|
5413
|
+
}
|
|
5280
5414
|
this.initialized = true;
|
|
5281
5415
|
console.log("[AgentManager] Initialized");
|
|
5282
5416
|
}
|
|
@@ -5285,28 +5419,38 @@ var AgentManager = class {
|
|
|
5285
5419
|
*
|
|
5286
5420
|
* Creates the session record but doesn't spawn the process yet.
|
|
5287
5421
|
* The process is spawned on the first message.
|
|
5422
|
+
*
|
|
5423
|
+
* EP1133: Added provider parameter for multi-provider support
|
|
5288
5424
|
*/
|
|
5289
5425
|
async startSession(options) {
|
|
5290
|
-
const { sessionId, moduleId, moduleUid, projectPath, message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
|
|
5426
|
+
const { sessionId, moduleId, moduleUid, projectPath, provider = "claude", message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
|
|
5291
5427
|
if (this.sessions.has(sessionId)) {
|
|
5292
5428
|
return { success: false, error: "Session already exists" };
|
|
5293
5429
|
}
|
|
5294
5430
|
const oauthToken = credentials?.oauthToken;
|
|
5295
5431
|
const apiKey = credentials?.apiKey;
|
|
5296
5432
|
if (!oauthToken && !apiKey) {
|
|
5433
|
+
const providerName = provider === "claude" ? "Claude" : "Codex";
|
|
5297
5434
|
return {
|
|
5298
5435
|
success: false,
|
|
5299
|
-
error:
|
|
5436
|
+
error: `Missing credentials. Please connect your ${providerName} account in Settings.`
|
|
5300
5437
|
};
|
|
5301
5438
|
}
|
|
5302
5439
|
const authMethod = oauthToken ? "OAuth" : "API key";
|
|
5303
|
-
console.log(`[AgentManager] Using ${authMethod} authentication`);
|
|
5440
|
+
console.log(`[AgentManager] Using ${provider} provider with ${authMethod} authentication`);
|
|
5304
5441
|
try {
|
|
5305
|
-
|
|
5442
|
+
if (provider === "claude") {
|
|
5443
|
+
await ensureClaudeBinary();
|
|
5444
|
+
} else if (provider === "codex") {
|
|
5445
|
+
await ensureCodexBinary();
|
|
5446
|
+
} else {
|
|
5447
|
+
return { success: false, error: `Unknown provider: ${provider}` };
|
|
5448
|
+
}
|
|
5306
5449
|
} catch (error) {
|
|
5450
|
+
const cliName = provider === "claude" ? "Claude Code" : "Codex CLI";
|
|
5307
5451
|
return {
|
|
5308
5452
|
success: false,
|
|
5309
|
-
error: error instanceof Error ? error.message :
|
|
5453
|
+
error: error instanceof Error ? error.message : `${cliName} not available`
|
|
5310
5454
|
};
|
|
5311
5455
|
}
|
|
5312
5456
|
const session = {
|
|
@@ -5314,6 +5458,8 @@ var AgentManager = class {
|
|
|
5314
5458
|
moduleId,
|
|
5315
5459
|
moduleUid,
|
|
5316
5460
|
projectPath,
|
|
5461
|
+
provider,
|
|
5462
|
+
// EP1133: Store provider in session
|
|
5317
5463
|
credentials,
|
|
5318
5464
|
systemPrompt,
|
|
5319
5465
|
status: "starting",
|
|
@@ -5321,7 +5467,7 @@ var AgentManager = class {
|
|
|
5321
5467
|
lastActivityAt: /* @__PURE__ */ new Date()
|
|
5322
5468
|
};
|
|
5323
5469
|
this.sessions.set(sessionId, session);
|
|
5324
|
-
console.log(`[AgentManager] Started session ${sessionId} for ${moduleUid}`);
|
|
5470
|
+
console.log(`[AgentManager] Started ${provider} session ${sessionId} for ${moduleUid}`);
|
|
5325
5471
|
return this.sendMessage({
|
|
5326
5472
|
sessionId,
|
|
5327
5473
|
message,
|
|
@@ -5334,38 +5480,70 @@ var AgentManager = class {
|
|
|
5334
5480
|
/**
|
|
5335
5481
|
* Send a message to an agent session
|
|
5336
5482
|
*
|
|
5337
|
-
* Spawns a new
|
|
5338
|
-
*
|
|
5339
|
-
* Subsequent messages use --resume with the claudeSessionId for conversation continuity.
|
|
5483
|
+
* Spawns a new agent CLI process for each message.
|
|
5484
|
+
* EP1133: Supports both Claude Code and Codex CLI with provider-specific handling.
|
|
5340
5485
|
*/
|
|
5341
5486
|
async sendMessage(options) {
|
|
5342
|
-
const { sessionId, message, isFirstMessage, claudeSessionId, onChunk, onComplete, onError } = options;
|
|
5487
|
+
const { sessionId, message, isFirstMessage, agentSessionId, claudeSessionId, onChunk, onComplete, onError } = options;
|
|
5343
5488
|
const session = this.sessions.get(sessionId);
|
|
5344
5489
|
if (!session) {
|
|
5345
5490
|
return { success: false, error: "Session not found" };
|
|
5346
5491
|
}
|
|
5347
5492
|
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
5348
5493
|
session.status = "running";
|
|
5494
|
+
const resumeSessionId = agentSessionId || claudeSessionId;
|
|
5349
5495
|
try {
|
|
5350
|
-
const
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5496
|
+
const provider = session.provider || "claude";
|
|
5497
|
+
let binaryPath;
|
|
5498
|
+
let args;
|
|
5499
|
+
if (provider === "codex") {
|
|
5500
|
+
binaryPath = await ensureCodexBinary();
|
|
5501
|
+
args = [
|
|
5502
|
+
"exec",
|
|
5503
|
+
"--json",
|
|
5504
|
+
// JSONL streaming output
|
|
5505
|
+
"--skip-git-repo-check",
|
|
5506
|
+
// Allow running outside git repos
|
|
5507
|
+
"--full-auto",
|
|
5508
|
+
// Automatic execution with sandbox
|
|
5509
|
+
"--cd",
|
|
5510
|
+
session.projectPath
|
|
5511
|
+
// Working directory
|
|
5512
|
+
];
|
|
5513
|
+
if (resumeSessionId) {
|
|
5514
|
+
args.push("resume", resumeSessionId);
|
|
5515
|
+
}
|
|
5516
|
+
let fullMessage = message;
|
|
5517
|
+
if (isFirstMessage && session.systemPrompt) {
|
|
5518
|
+
fullMessage = `${session.systemPrompt}
|
|
5519
|
+
|
|
5520
|
+
---
|
|
5521
|
+
|
|
5522
|
+
${message}`;
|
|
5523
|
+
}
|
|
5524
|
+
args.push(fullMessage);
|
|
5525
|
+
} else {
|
|
5526
|
+
binaryPath = await ensureClaudeBinary();
|
|
5527
|
+
args = [
|
|
5528
|
+
"--print",
|
|
5529
|
+
// Non-interactive mode
|
|
5530
|
+
"--output-format",
|
|
5531
|
+
"stream-json",
|
|
5532
|
+
// Structured streaming output
|
|
5533
|
+
"--verbose"
|
|
5534
|
+
// Required for stream-json with --print
|
|
5535
|
+
];
|
|
5536
|
+
if (isFirstMessage && session.systemPrompt) {
|
|
5537
|
+
args.push("--system-prompt", session.systemPrompt);
|
|
5538
|
+
}
|
|
5539
|
+
if (resumeSessionId) {
|
|
5540
|
+
args.push("--resume", resumeSessionId);
|
|
5541
|
+
session.agentSessionId = resumeSessionId;
|
|
5542
|
+
session.claudeSessionId = resumeSessionId;
|
|
5543
|
+
}
|
|
5544
|
+
args.push("--", message);
|
|
5545
|
+
}
|
|
5546
|
+
console.log(`[AgentManager] Spawning ${provider} CLI for session ${sessionId}`);
|
|
5369
5547
|
console.log(`[AgentManager] Command: ${binaryPath} ${args.join(" ").substring(0, 100)}...`);
|
|
5370
5548
|
let spawnCmd;
|
|
5371
5549
|
let spawnArgs;
|
|
@@ -5378,68 +5556,110 @@ var AgentManager = class {
|
|
|
5378
5556
|
}
|
|
5379
5557
|
const useOAuth = !!session.credentials.oauthToken;
|
|
5380
5558
|
const useApiKey = !useOAuth && !!session.credentials.apiKey;
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
const
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5559
|
+
if (provider === "codex") {
|
|
5560
|
+
await this.withConfigLock(async () => {
|
|
5561
|
+
const codexDir = path10.join(os3.homedir(), ".codex");
|
|
5562
|
+
if (!fs9.existsSync(codexDir)) {
|
|
5563
|
+
fs9.mkdirSync(codexDir, { recursive: true });
|
|
5564
|
+
}
|
|
5565
|
+
if (useOAuth) {
|
|
5566
|
+
const codexConfig = generateCodexConfig({
|
|
5567
|
+
accessToken: session.credentials.oauthToken,
|
|
5568
|
+
refreshToken: session.credentials.refreshToken,
|
|
5569
|
+
idToken: session.credentials.idToken,
|
|
5570
|
+
accountId: session.credentials.accountId,
|
|
5571
|
+
expiresAt: session.credentials.expiresAt
|
|
5572
|
+
}, session.projectPath);
|
|
5573
|
+
const authJsonPath = path10.join(codexDir, "auth.json");
|
|
5574
|
+
fs9.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
|
|
5575
|
+
console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
|
|
5576
|
+
if (codexConfig["config.toml"]) {
|
|
5577
|
+
const configTomlPath = path10.join(codexDir, "config.toml");
|
|
5578
|
+
let existingConfig = "";
|
|
5579
|
+
try {
|
|
5580
|
+
existingConfig = fs9.readFileSync(configTomlPath, "utf-8");
|
|
5581
|
+
} catch {
|
|
5582
|
+
}
|
|
5583
|
+
const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5584
|
+
const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
|
|
5585
|
+
const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
|
|
5586
|
+
if (!projectAlreadyTrusted) {
|
|
5587
|
+
fs9.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
|
|
5588
|
+
console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
} else if (useApiKey) {
|
|
5592
|
+
console.log("[AgentManager] EP1133: Using Codex with API key (OPENAI_API_KEY)");
|
|
5593
|
+
}
|
|
5594
|
+
});
|
|
5595
|
+
} else {
|
|
5596
|
+
await this.withConfigLock(async () => {
|
|
5597
|
+
const claudeDir = path10.join(os3.homedir(), ".claude");
|
|
5598
|
+
const credentialsPath = path10.join(claudeDir, ".credentials.json");
|
|
5599
|
+
const statsigDir = path10.join(claudeDir, "statsig");
|
|
5600
|
+
if (!fs9.existsSync(claudeDir)) {
|
|
5601
|
+
fs9.mkdirSync(claudeDir, { recursive: true });
|
|
5602
|
+
}
|
|
5603
|
+
if (!fs9.existsSync(statsigDir)) {
|
|
5604
|
+
fs9.mkdirSync(statsigDir, { recursive: true });
|
|
5605
|
+
}
|
|
5606
|
+
if (useOAuth) {
|
|
5607
|
+
const oauthCredentials = {
|
|
5608
|
+
accessToken: session.credentials.oauthToken
|
|
5609
|
+
};
|
|
5610
|
+
if (session.credentials.refreshToken) {
|
|
5611
|
+
oauthCredentials.refreshToken = session.credentials.refreshToken;
|
|
5612
|
+
}
|
|
5613
|
+
if (session.credentials.expiresAt) {
|
|
5614
|
+
oauthCredentials.expiresAt = session.credentials.expiresAt;
|
|
5615
|
+
}
|
|
5616
|
+
if (session.credentials.scopes) {
|
|
5617
|
+
oauthCredentials.scopes = session.credentials.scopes;
|
|
5618
|
+
}
|
|
5619
|
+
const credentialsContent = JSON.stringify({
|
|
5620
|
+
claudeAiOauth: oauthCredentials
|
|
5621
|
+
}, null, 2);
|
|
5622
|
+
fs9.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
|
|
5623
|
+
console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
|
|
5624
|
+
try {
|
|
5625
|
+
const claudeConfig = generateClaudeConfig();
|
|
5626
|
+
const statsigFiles = Object.keys(claudeConfig.statsig);
|
|
5627
|
+
const hasEvaluations = statsigFiles.some((f) => f.includes("cached.evaluations"));
|
|
5628
|
+
const hasStableId = statsigFiles.some((f) => f.includes("stable_id"));
|
|
5629
|
+
if (!hasEvaluations || !hasStableId) {
|
|
5630
|
+
throw new Error(`Invalid statsig config: missing required files`);
|
|
5631
|
+
}
|
|
5632
|
+
const settingsPath = path10.join(claudeDir, "settings.json");
|
|
5633
|
+
fs9.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
|
|
5634
|
+
for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
|
|
5635
|
+
const filePath = path10.join(statsigDir, filename);
|
|
5636
|
+
fs9.writeFileSync(filePath, content, { mode: 420 });
|
|
5637
|
+
}
|
|
5638
|
+
console.log("[AgentManager] Wrote Claude config files");
|
|
5639
|
+
} catch (configError) {
|
|
5640
|
+
console.warn("[AgentManager] Failed to write Claude config files:", configError instanceof Error ? configError.message : configError);
|
|
5641
|
+
}
|
|
5642
|
+
} else if (useApiKey) {
|
|
5643
|
+
console.log("[AgentManager] Using Claude with API key (ANTHROPIC_API_KEY)");
|
|
5644
|
+
}
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
const envVars = {
|
|
5648
|
+
...process.env,
|
|
5649
|
+
// Disable color output for cleaner JSON parsing
|
|
5650
|
+
NO_COLOR: "1",
|
|
5651
|
+
FORCE_COLOR: "0"
|
|
5652
|
+
};
|
|
5653
|
+
if (useApiKey && session.credentials.apiKey) {
|
|
5654
|
+
if (provider === "codex") {
|
|
5655
|
+
envVars.OPENAI_API_KEY = session.credentials.apiKey;
|
|
5656
|
+
} else {
|
|
5657
|
+
envVars.ANTHROPIC_API_KEY = session.credentials.apiKey;
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
const childProcess = (0, import_child_process9.spawn)(spawnCmd, spawnArgs, {
|
|
5434
5661
|
cwd: session.projectPath,
|
|
5435
|
-
env:
|
|
5436
|
-
...process.env,
|
|
5437
|
-
// Disable color output for cleaner JSON parsing
|
|
5438
|
-
NO_COLOR: "1",
|
|
5439
|
-
FORCE_COLOR: "0",
|
|
5440
|
-
// Add API key if using API key auth mode
|
|
5441
|
-
...useApiKey && session.credentials.apiKey ? { ANTHROPIC_API_KEY: session.credentials.apiKey } : {}
|
|
5442
|
-
},
|
|
5662
|
+
env: envVars,
|
|
5443
5663
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5444
5664
|
});
|
|
5445
5665
|
this.processes.set(sessionId, childProcess);
|
|
@@ -5464,42 +5684,76 @@ var AgentManager = class {
|
|
|
5464
5684
|
if (!line.trim()) continue;
|
|
5465
5685
|
try {
|
|
5466
5686
|
const parsed = JSON.parse(line);
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5687
|
+
if (provider === "codex") {
|
|
5688
|
+
switch (parsed.type) {
|
|
5689
|
+
case "thread.started":
|
|
5690
|
+
if (parsed.thread_id) {
|
|
5691
|
+
extractedSessionId = parsed.thread_id;
|
|
5692
|
+
session.agentSessionId = extractedSessionId;
|
|
5693
|
+
}
|
|
5694
|
+
break;
|
|
5695
|
+
case "item.completed":
|
|
5696
|
+
if (parsed.item?.type === "agent_message" && parsed.item.text) {
|
|
5697
|
+
onChunk(parsed.item.text);
|
|
5698
|
+
} else if (parsed.item?.type === "reasoning" && parsed.item.summary) {
|
|
5699
|
+
onChunk(`[Thinking: ${parsed.item.summary}]
|
|
5700
|
+
`);
|
|
5701
|
+
}
|
|
5702
|
+
break;
|
|
5703
|
+
case "item.failed":
|
|
5704
|
+
case "turn.failed":
|
|
5705
|
+
onError(parsed.item?.error || parsed.error?.message || "Codex operation failed");
|
|
5706
|
+
break;
|
|
5707
|
+
case "error":
|
|
5708
|
+
onError(parsed.message || parsed.error?.message || "Unknown error from Codex");
|
|
5709
|
+
break;
|
|
5710
|
+
// Ignore: turn.started, turn.completed (usage stats), item.started
|
|
5711
|
+
default:
|
|
5712
|
+
if (!["turn.started", "turn.completed", "item.started"].includes(parsed.type)) {
|
|
5713
|
+
console.log(`[AgentManager] Codex event: ${parsed.type}`);
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
} else {
|
|
5717
|
+
switch (parsed.type) {
|
|
5718
|
+
case "assistant":
|
|
5719
|
+
if (parsed.message?.content) {
|
|
5720
|
+
for (const block of parsed.message.content) {
|
|
5721
|
+
if (block.type === "text" && block.text) {
|
|
5722
|
+
onChunk(block.text);
|
|
5723
|
+
}
|
|
5473
5724
|
}
|
|
5474
5725
|
}
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5726
|
+
break;
|
|
5727
|
+
case "content_block_delta":
|
|
5728
|
+
if (parsed.delta?.text) {
|
|
5729
|
+
onChunk(parsed.delta.text);
|
|
5730
|
+
}
|
|
5731
|
+
break;
|
|
5732
|
+
case "result":
|
|
5733
|
+
if (parsed.session_id) {
|
|
5734
|
+
extractedSessionId = parsed.session_id;
|
|
5735
|
+
session.agentSessionId = extractedSessionId;
|
|
5736
|
+
session.claudeSessionId = extractedSessionId;
|
|
5737
|
+
}
|
|
5738
|
+
if (parsed.result?.session_id) {
|
|
5739
|
+
extractedSessionId = parsed.result.session_id;
|
|
5740
|
+
session.agentSessionId = extractedSessionId;
|
|
5741
|
+
session.claudeSessionId = extractedSessionId;
|
|
5742
|
+
}
|
|
5743
|
+
break;
|
|
5744
|
+
case "system":
|
|
5745
|
+
if (parsed.session_id) {
|
|
5746
|
+
extractedSessionId = parsed.session_id;
|
|
5747
|
+
session.agentSessionId = extractedSessionId;
|
|
5748
|
+
session.claudeSessionId = extractedSessionId;
|
|
5749
|
+
}
|
|
5750
|
+
break;
|
|
5751
|
+
case "error":
|
|
5752
|
+
onError(parsed.error?.message || parsed.message || "Unknown error from Claude Code");
|
|
5753
|
+
break;
|
|
5754
|
+
default:
|
|
5755
|
+
console.log(`[AgentManager] Claude event: ${parsed.type}`);
|
|
5756
|
+
}
|
|
5503
5757
|
}
|
|
5504
5758
|
} catch (parseError) {
|
|
5505
5759
|
if (line.trim()) {
|
|
@@ -5513,15 +5767,15 @@ var AgentManager = class {
|
|
|
5513
5767
|
stderrBuffer += data.toString();
|
|
5514
5768
|
});
|
|
5515
5769
|
childProcess.on("exit", (code, signal) => {
|
|
5516
|
-
console.log(`[AgentManager]
|
|
5770
|
+
console.log(`[AgentManager] ${provider} CLI exited for session ${sessionId}: code=${code}, signal=${signal}`);
|
|
5517
5771
|
this.processes.delete(sessionId);
|
|
5518
5772
|
this.removePidFile(sessionId);
|
|
5519
5773
|
if (code === 0) {
|
|
5520
5774
|
session.status = "stopped";
|
|
5521
|
-
onComplete(extractedSessionId || session.claudeSessionId);
|
|
5775
|
+
onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
|
|
5522
5776
|
} else if (signal === "SIGINT") {
|
|
5523
5777
|
session.status = "stopped";
|
|
5524
|
-
onComplete(extractedSessionId || session.claudeSessionId);
|
|
5778
|
+
onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
|
|
5525
5779
|
} else {
|
|
5526
5780
|
session.status = "error";
|
|
5527
5781
|
const errorMsg = stderrBuffer.trim() || `Process exited with code ${code}`;
|
|
@@ -5546,13 +5800,13 @@ var AgentManager = class {
|
|
|
5546
5800
|
/**
|
|
5547
5801
|
* Abort an agent session (SIGINT)
|
|
5548
5802
|
*
|
|
5549
|
-
* Sends SIGINT to the
|
|
5803
|
+
* Sends SIGINT to the agent CLI process to abort the current operation.
|
|
5550
5804
|
*/
|
|
5551
5805
|
async abortSession(sessionId) {
|
|
5552
|
-
const
|
|
5553
|
-
if (
|
|
5806
|
+
const agentProcess = this.processes.get(sessionId);
|
|
5807
|
+
if (agentProcess && !agentProcess.killed) {
|
|
5554
5808
|
console.log(`[AgentManager] Aborting session ${sessionId} with SIGINT`);
|
|
5555
|
-
|
|
5809
|
+
agentProcess.kill("SIGINT");
|
|
5556
5810
|
}
|
|
5557
5811
|
const session = this.sessions.get(sessionId);
|
|
5558
5812
|
if (session) {
|
|
@@ -5566,23 +5820,23 @@ var AgentManager = class {
|
|
|
5566
5820
|
* If it doesn't exit within 5 seconds, sends SIGTERM.
|
|
5567
5821
|
*/
|
|
5568
5822
|
async stopSession(sessionId) {
|
|
5569
|
-
const
|
|
5823
|
+
const agentProcess = this.processes.get(sessionId);
|
|
5570
5824
|
const session = this.sessions.get(sessionId);
|
|
5571
5825
|
if (session) {
|
|
5572
5826
|
session.status = "stopping";
|
|
5573
5827
|
}
|
|
5574
|
-
if (
|
|
5828
|
+
if (agentProcess && !agentProcess.killed) {
|
|
5575
5829
|
console.log(`[AgentManager] Stopping session ${sessionId}`);
|
|
5576
|
-
|
|
5830
|
+
agentProcess.kill("SIGINT");
|
|
5577
5831
|
await new Promise((resolve3) => {
|
|
5578
5832
|
const timeout = setTimeout(() => {
|
|
5579
|
-
if (!
|
|
5833
|
+
if (!agentProcess.killed) {
|
|
5580
5834
|
console.log(`[AgentManager] Force killing session ${sessionId}`);
|
|
5581
|
-
|
|
5835
|
+
agentProcess.kill("SIGTERM");
|
|
5582
5836
|
}
|
|
5583
5837
|
resolve3();
|
|
5584
5838
|
}, 5e3);
|
|
5585
|
-
|
|
5839
|
+
agentProcess.once("exit", () => {
|
|
5586
5840
|
clearTimeout(timeout);
|
|
5587
5841
|
resolve3();
|
|
5588
5842
|
});
|
|
@@ -5626,14 +5880,14 @@ var AgentManager = class {
|
|
|
5626
5880
|
*/
|
|
5627
5881
|
async cleanupOrphanedProcesses() {
|
|
5628
5882
|
let cleaned = 0;
|
|
5629
|
-
if (!
|
|
5883
|
+
if (!fs9.existsSync(this.pidDir)) {
|
|
5630
5884
|
return { cleaned };
|
|
5631
5885
|
}
|
|
5632
|
-
const pidFiles =
|
|
5886
|
+
const pidFiles = fs9.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
|
|
5633
5887
|
for (const pidFile of pidFiles) {
|
|
5634
|
-
const pidPath =
|
|
5888
|
+
const pidPath = path10.join(this.pidDir, pidFile);
|
|
5635
5889
|
try {
|
|
5636
|
-
const pidStr =
|
|
5890
|
+
const pidStr = fs9.readFileSync(pidPath, "utf-8").trim();
|
|
5637
5891
|
const pid = parseInt(pidStr, 10);
|
|
5638
5892
|
if (!isNaN(pid)) {
|
|
5639
5893
|
try {
|
|
@@ -5644,7 +5898,7 @@ var AgentManager = class {
|
|
|
5644
5898
|
} catch {
|
|
5645
5899
|
}
|
|
5646
5900
|
}
|
|
5647
|
-
|
|
5901
|
+
fs9.unlinkSync(pidPath);
|
|
5648
5902
|
} catch (error) {
|
|
5649
5903
|
console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
|
|
5650
5904
|
}
|
|
@@ -5658,17 +5912,17 @@ var AgentManager = class {
|
|
|
5658
5912
|
* Write PID file for session tracking
|
|
5659
5913
|
*/
|
|
5660
5914
|
writePidFile(sessionId, pid) {
|
|
5661
|
-
const pidPath =
|
|
5662
|
-
|
|
5915
|
+
const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
|
|
5916
|
+
fs9.writeFileSync(pidPath, pid.toString());
|
|
5663
5917
|
}
|
|
5664
5918
|
/**
|
|
5665
5919
|
* Remove PID file for session
|
|
5666
5920
|
*/
|
|
5667
5921
|
removePidFile(sessionId) {
|
|
5668
|
-
const pidPath =
|
|
5922
|
+
const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
|
|
5669
5923
|
try {
|
|
5670
|
-
if (
|
|
5671
|
-
|
|
5924
|
+
if (fs9.existsSync(pidPath)) {
|
|
5925
|
+
fs9.unlinkSync(pidPath);
|
|
5672
5926
|
}
|
|
5673
5927
|
} catch {
|
|
5674
5928
|
}
|
|
@@ -5699,10 +5953,10 @@ var import_events3 = require("events");
|
|
|
5699
5953
|
var import_fs = require("fs");
|
|
5700
5954
|
|
|
5701
5955
|
// src/preview/dev-server-runner.ts
|
|
5702
|
-
var
|
|
5956
|
+
var import_child_process11 = require("child_process");
|
|
5703
5957
|
var http = __toESM(require("http"));
|
|
5704
|
-
var
|
|
5705
|
-
var
|
|
5958
|
+
var fs13 = __toESM(require("fs"));
|
|
5959
|
+
var path14 = __toESM(require("path"));
|
|
5706
5960
|
var import_events2 = require("events");
|
|
5707
5961
|
var import_core8 = __toESM(require_dist());
|
|
5708
5962
|
|
|
@@ -5727,13 +5981,13 @@ async function isPortInUse(port) {
|
|
|
5727
5981
|
}
|
|
5728
5982
|
|
|
5729
5983
|
// src/utils/env-cache.ts
|
|
5730
|
-
var
|
|
5731
|
-
var
|
|
5984
|
+
var fs11 = __toESM(require("fs"));
|
|
5985
|
+
var path12 = __toESM(require("path"));
|
|
5732
5986
|
var os4 = __toESM(require("os"));
|
|
5733
5987
|
|
|
5734
5988
|
// src/utils/env-setup.ts
|
|
5735
|
-
var
|
|
5736
|
-
var
|
|
5989
|
+
var fs10 = __toESM(require("fs"));
|
|
5990
|
+
var path11 = __toESM(require("path"));
|
|
5737
5991
|
async function fetchEnvVars(apiUrl, accessToken) {
|
|
5738
5992
|
try {
|
|
5739
5993
|
const url = `${apiUrl}/api/cli/env-vars`;
|
|
@@ -5768,29 +6022,29 @@ function writeEnvFile(targetPath, envVars) {
|
|
|
5768
6022
|
}
|
|
5769
6023
|
return `${key}=${value}`;
|
|
5770
6024
|
}).join("\n") + "\n";
|
|
5771
|
-
const envPath =
|
|
5772
|
-
|
|
6025
|
+
const envPath = path11.join(targetPath, ".env");
|
|
6026
|
+
fs10.writeFileSync(envPath, envContent, { mode: 384 });
|
|
5773
6027
|
console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
|
|
5774
6028
|
}
|
|
5775
6029
|
|
|
5776
6030
|
// src/utils/env-cache.ts
|
|
5777
6031
|
var DEFAULT_CACHE_TTL = 60;
|
|
5778
|
-
var CACHE_DIR =
|
|
6032
|
+
var CACHE_DIR = path12.join(os4.homedir(), ".episoda", "cache");
|
|
5779
6033
|
function getCacheFilePath(projectId) {
|
|
5780
|
-
return
|
|
6034
|
+
return path12.join(CACHE_DIR, `env-vars-${projectId}.json`);
|
|
5781
6035
|
}
|
|
5782
6036
|
function ensureCacheDir() {
|
|
5783
|
-
if (!
|
|
5784
|
-
|
|
6037
|
+
if (!fs11.existsSync(CACHE_DIR)) {
|
|
6038
|
+
fs11.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
|
|
5785
6039
|
}
|
|
5786
6040
|
}
|
|
5787
6041
|
function readCache(projectId) {
|
|
5788
6042
|
try {
|
|
5789
6043
|
const cacheFile = getCacheFilePath(projectId);
|
|
5790
|
-
if (!
|
|
6044
|
+
if (!fs11.existsSync(cacheFile)) {
|
|
5791
6045
|
return null;
|
|
5792
6046
|
}
|
|
5793
|
-
const content =
|
|
6047
|
+
const content = fs11.readFileSync(cacheFile, "utf-8");
|
|
5794
6048
|
const data = JSON.parse(content);
|
|
5795
6049
|
if (!data.vars || typeof data.vars !== "object" || !data.fetchedAt) {
|
|
5796
6050
|
return null;
|
|
@@ -5809,7 +6063,7 @@ function writeCache(projectId, vars) {
|
|
|
5809
6063
|
fetchedAt: Date.now(),
|
|
5810
6064
|
projectId
|
|
5811
6065
|
};
|
|
5812
|
-
|
|
6066
|
+
fs11.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
5813
6067
|
} catch (error) {
|
|
5814
6068
|
console.warn("[env-cache] Failed to write cache:", error instanceof Error ? error.message : error);
|
|
5815
6069
|
}
|
|
@@ -5878,11 +6132,11 @@ No cached values available as fallback.`
|
|
|
5878
6132
|
}
|
|
5879
6133
|
|
|
5880
6134
|
// src/preview/dev-server-registry.ts
|
|
5881
|
-
var
|
|
5882
|
-
var
|
|
6135
|
+
var fs12 = __toESM(require("fs"));
|
|
6136
|
+
var path13 = __toESM(require("path"));
|
|
5883
6137
|
var os5 = __toESM(require("os"));
|
|
5884
|
-
var
|
|
5885
|
-
var DEV_SERVER_REGISTRY_DIR =
|
|
6138
|
+
var import_child_process10 = require("child_process");
|
|
6139
|
+
var DEV_SERVER_REGISTRY_DIR = path13.join(os5.homedir(), ".episoda", "dev-servers");
|
|
5886
6140
|
var DevServerRegistry = class {
|
|
5887
6141
|
constructor() {
|
|
5888
6142
|
this.ensureRegistryDir();
|
|
@@ -5892,9 +6146,9 @@ var DevServerRegistry = class {
|
|
|
5892
6146
|
*/
|
|
5893
6147
|
ensureRegistryDir() {
|
|
5894
6148
|
try {
|
|
5895
|
-
if (!
|
|
6149
|
+
if (!fs12.existsSync(DEV_SERVER_REGISTRY_DIR)) {
|
|
5896
6150
|
console.log(`[DevServerRegistry] EP1042: Creating registry directory: ${DEV_SERVER_REGISTRY_DIR}`);
|
|
5897
|
-
|
|
6151
|
+
fs12.mkdirSync(DEV_SERVER_REGISTRY_DIR, { recursive: true });
|
|
5898
6152
|
}
|
|
5899
6153
|
} catch (error) {
|
|
5900
6154
|
console.error(`[DevServerRegistry] EP1042: Failed to create registry directory:`, error);
|
|
@@ -5905,7 +6159,7 @@ var DevServerRegistry = class {
|
|
|
5905
6159
|
* Get the registry file path for a module
|
|
5906
6160
|
*/
|
|
5907
6161
|
getEntryPath(moduleUid) {
|
|
5908
|
-
return
|
|
6162
|
+
return path13.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
|
|
5909
6163
|
}
|
|
5910
6164
|
/**
|
|
5911
6165
|
* Register a dev server
|
|
@@ -5916,7 +6170,7 @@ var DevServerRegistry = class {
|
|
|
5916
6170
|
try {
|
|
5917
6171
|
this.ensureRegistryDir();
|
|
5918
6172
|
const entryPath = this.getEntryPath(entry.moduleUid);
|
|
5919
|
-
|
|
6173
|
+
fs12.writeFileSync(entryPath, JSON.stringify(entry, null, 2), "utf8");
|
|
5920
6174
|
console.log(`[DevServerRegistry] EP1042: Registered ${entry.moduleUid} (PID ${entry.pid}, port ${entry.port})`);
|
|
5921
6175
|
} catch (error) {
|
|
5922
6176
|
console.error(`[DevServerRegistry] EP1042: Failed to register ${entry.moduleUid}:`, error);
|
|
@@ -5930,8 +6184,8 @@ var DevServerRegistry = class {
|
|
|
5930
6184
|
unregister(moduleUid) {
|
|
5931
6185
|
try {
|
|
5932
6186
|
const entryPath = this.getEntryPath(moduleUid);
|
|
5933
|
-
if (
|
|
5934
|
-
|
|
6187
|
+
if (fs12.existsSync(entryPath)) {
|
|
6188
|
+
fs12.unlinkSync(entryPath);
|
|
5935
6189
|
console.log(`[DevServerRegistry] EP1042: Unregistered ${moduleUid}`);
|
|
5936
6190
|
}
|
|
5937
6191
|
} catch (error) {
|
|
@@ -5947,10 +6201,10 @@ var DevServerRegistry = class {
|
|
|
5947
6201
|
getByModule(moduleUid) {
|
|
5948
6202
|
try {
|
|
5949
6203
|
const entryPath = this.getEntryPath(moduleUid);
|
|
5950
|
-
if (!
|
|
6204
|
+
if (!fs12.existsSync(entryPath)) {
|
|
5951
6205
|
return null;
|
|
5952
6206
|
}
|
|
5953
|
-
const content =
|
|
6207
|
+
const content = fs12.readFileSync(entryPath, "utf8");
|
|
5954
6208
|
const entry = JSON.parse(content);
|
|
5955
6209
|
if (!entry.pid || !entry.port || !entry.worktreePath) {
|
|
5956
6210
|
console.warn(`[DevServerRegistry] EP1042: Invalid entry for ${moduleUid}, removing`);
|
|
@@ -5988,7 +6242,7 @@ var DevServerRegistry = class {
|
|
|
5988
6242
|
const entries = [];
|
|
5989
6243
|
try {
|
|
5990
6244
|
this.ensureRegistryDir();
|
|
5991
|
-
const files =
|
|
6245
|
+
const files = fs12.readdirSync(DEV_SERVER_REGISTRY_DIR).filter((f) => f.endsWith(".json"));
|
|
5992
6246
|
for (const file of files) {
|
|
5993
6247
|
const moduleUid = file.replace(".json", "");
|
|
5994
6248
|
const entry = this.getByModule(moduleUid);
|
|
@@ -6036,7 +6290,7 @@ var DevServerRegistry = class {
|
|
|
6036
6290
|
*/
|
|
6037
6291
|
getProcessCwd(pid) {
|
|
6038
6292
|
try {
|
|
6039
|
-
const output = (0,
|
|
6293
|
+
const output = (0, import_child_process10.execSync)(`lsof -p ${pid} -Fn | grep ^n | grep cwd | head -1`, {
|
|
6040
6294
|
encoding: "utf8",
|
|
6041
6295
|
timeout: 5e3
|
|
6042
6296
|
}).trim();
|
|
@@ -6046,7 +6300,7 @@ var DevServerRegistry = class {
|
|
|
6046
6300
|
return null;
|
|
6047
6301
|
} catch {
|
|
6048
6302
|
try {
|
|
6049
|
-
return
|
|
6303
|
+
return fs12.readlinkSync(`/proc/${pid}/cwd`);
|
|
6050
6304
|
} catch {
|
|
6051
6305
|
return null;
|
|
6052
6306
|
}
|
|
@@ -6061,7 +6315,7 @@ var DevServerRegistry = class {
|
|
|
6061
6315
|
findProcessesInWorktree(worktreePath) {
|
|
6062
6316
|
const pids = [];
|
|
6063
6317
|
try {
|
|
6064
|
-
const output = (0,
|
|
6318
|
+
const output = (0, import_child_process10.execSync)(
|
|
6065
6319
|
`lsof -c node -c next | grep "${worktreePath}" | awk '{print $2}' | sort -u`,
|
|
6066
6320
|
{ encoding: "utf8", timeout: 5e3 }
|
|
6067
6321
|
).trim();
|
|
@@ -6122,7 +6376,7 @@ var DevServerRegistry = class {
|
|
|
6122
6376
|
*/
|
|
6123
6377
|
findProcessesOnPort(port) {
|
|
6124
6378
|
try {
|
|
6125
|
-
const output = (0,
|
|
6379
|
+
const output = (0, import_child_process10.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
6126
6380
|
if (!output) {
|
|
6127
6381
|
return [];
|
|
6128
6382
|
}
|
|
@@ -6376,7 +6630,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6376
6630
|
*/
|
|
6377
6631
|
async killProcessOnPort(port) {
|
|
6378
6632
|
try {
|
|
6379
|
-
const result = (0,
|
|
6633
|
+
const result = (0, import_child_process11.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
6380
6634
|
if (!result) {
|
|
6381
6635
|
return true;
|
|
6382
6636
|
}
|
|
@@ -6384,15 +6638,15 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6384
6638
|
console.log(`[DevServerRunner] Found ${pids.length} process(es) on port ${port}`);
|
|
6385
6639
|
for (const pid of pids) {
|
|
6386
6640
|
try {
|
|
6387
|
-
(0,
|
|
6641
|
+
(0, import_child_process11.execSync)(`kill -15 ${pid} 2>/dev/null || true`);
|
|
6388
6642
|
} catch {
|
|
6389
6643
|
}
|
|
6390
6644
|
}
|
|
6391
6645
|
await this.wait(1e3);
|
|
6392
6646
|
for (const pid of pids) {
|
|
6393
6647
|
try {
|
|
6394
|
-
(0,
|
|
6395
|
-
(0,
|
|
6648
|
+
(0, import_child_process11.execSync)(`kill -0 ${pid} 2>/dev/null`);
|
|
6649
|
+
(0, import_child_process11.execSync)(`kill -9 ${pid} 2>/dev/null || true`);
|
|
6396
6650
|
} catch {
|
|
6397
6651
|
}
|
|
6398
6652
|
}
|
|
@@ -6416,8 +6670,8 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6416
6670
|
cacheTtl: 300
|
|
6417
6671
|
});
|
|
6418
6672
|
console.log(`[DevServerRunner] Loaded ${Object.keys(result.envVars).length} env vars`);
|
|
6419
|
-
const envFilePath =
|
|
6420
|
-
if (!
|
|
6673
|
+
const envFilePath = path14.join(projectPath, ".env");
|
|
6674
|
+
if (!fs13.existsSync(envFilePath) && Object.keys(result.envVars).length > 0) {
|
|
6421
6675
|
console.log(`[DevServerRunner] Writing .env file`);
|
|
6422
6676
|
writeEnvFile(projectPath, result.envVars);
|
|
6423
6677
|
}
|
|
@@ -6441,7 +6695,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6441
6695
|
PORT: String(port),
|
|
6442
6696
|
NODE_OPTIONS: enhancedNodeOptions
|
|
6443
6697
|
};
|
|
6444
|
-
const proc = (0,
|
|
6698
|
+
const proc = (0, import_child_process11.spawn)(cmd, args, {
|
|
6445
6699
|
cwd: projectPath,
|
|
6446
6700
|
env: mergedEnv,
|
|
6447
6701
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6566,25 +6820,25 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6566
6820
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
6567
6821
|
}
|
|
6568
6822
|
getLogsDir() {
|
|
6569
|
-
const logsDir =
|
|
6570
|
-
if (!
|
|
6571
|
-
|
|
6823
|
+
const logsDir = path14.join((0, import_core8.getConfigDir)(), "logs");
|
|
6824
|
+
if (!fs13.existsSync(logsDir)) {
|
|
6825
|
+
fs13.mkdirSync(logsDir, { recursive: true });
|
|
6572
6826
|
}
|
|
6573
6827
|
return logsDir;
|
|
6574
6828
|
}
|
|
6575
6829
|
getLogFilePath(moduleUid) {
|
|
6576
|
-
return
|
|
6830
|
+
return path14.join(this.getLogsDir(), `dev-${moduleUid}.log`);
|
|
6577
6831
|
}
|
|
6578
6832
|
rotateLogIfNeeded(logPath) {
|
|
6579
6833
|
try {
|
|
6580
|
-
if (
|
|
6581
|
-
const stats =
|
|
6834
|
+
if (fs13.existsSync(logPath)) {
|
|
6835
|
+
const stats = fs13.statSync(logPath);
|
|
6582
6836
|
if (stats.size > DEV_SERVER_CONSTANTS.MAX_LOG_SIZE_BYTES) {
|
|
6583
6837
|
const backupPath = `${logPath}.1`;
|
|
6584
|
-
if (
|
|
6585
|
-
|
|
6838
|
+
if (fs13.existsSync(backupPath)) {
|
|
6839
|
+
fs13.unlinkSync(backupPath);
|
|
6586
6840
|
}
|
|
6587
|
-
|
|
6841
|
+
fs13.renameSync(logPath, backupPath);
|
|
6588
6842
|
}
|
|
6589
6843
|
}
|
|
6590
6844
|
} catch {
|
|
@@ -6595,7 +6849,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6595
6849
|
try {
|
|
6596
6850
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6597
6851
|
const prefix = isError ? "ERR" : "OUT";
|
|
6598
|
-
|
|
6852
|
+
fs13.appendFileSync(logPath, `[${timestamp}] [${prefix}] ${line}
|
|
6599
6853
|
`);
|
|
6600
6854
|
} catch {
|
|
6601
6855
|
}
|
|
@@ -6623,21 +6877,21 @@ function getDevServerRunner() {
|
|
|
6623
6877
|
}
|
|
6624
6878
|
|
|
6625
6879
|
// src/utils/port-allocator.ts
|
|
6626
|
-
var
|
|
6627
|
-
var
|
|
6880
|
+
var fs14 = __toESM(require("fs"));
|
|
6881
|
+
var path15 = __toESM(require("path"));
|
|
6628
6882
|
var os6 = __toESM(require("os"));
|
|
6629
6883
|
var PORT_RANGE_START = 3100;
|
|
6630
6884
|
var PORT_RANGE_END = 3199;
|
|
6631
6885
|
var PORT_WARNING_THRESHOLD = 80;
|
|
6632
|
-
var PORTS_FILE =
|
|
6886
|
+
var PORTS_FILE = path15.join(os6.homedir(), ".episoda", "ports.json");
|
|
6633
6887
|
var portAssignments = /* @__PURE__ */ new Map();
|
|
6634
6888
|
var initialized = false;
|
|
6635
6889
|
function loadFromDisk() {
|
|
6636
6890
|
if (initialized) return;
|
|
6637
6891
|
initialized = true;
|
|
6638
6892
|
try {
|
|
6639
|
-
if (
|
|
6640
|
-
const content =
|
|
6893
|
+
if (fs14.existsSync(PORTS_FILE)) {
|
|
6894
|
+
const content = fs14.readFileSync(PORTS_FILE, "utf8");
|
|
6641
6895
|
const data = JSON.parse(content);
|
|
6642
6896
|
for (const [moduleUid, port] of Object.entries(data)) {
|
|
6643
6897
|
if (typeof port === "number" && port >= PORT_RANGE_START && port <= PORT_RANGE_END) {
|
|
@@ -6654,15 +6908,15 @@ function loadFromDisk() {
|
|
|
6654
6908
|
}
|
|
6655
6909
|
function saveToDisk() {
|
|
6656
6910
|
try {
|
|
6657
|
-
const dir =
|
|
6658
|
-
if (!
|
|
6659
|
-
|
|
6911
|
+
const dir = path15.dirname(PORTS_FILE);
|
|
6912
|
+
if (!fs14.existsSync(dir)) {
|
|
6913
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
6660
6914
|
}
|
|
6661
6915
|
const data = {};
|
|
6662
6916
|
for (const [moduleUid, port] of portAssignments) {
|
|
6663
6917
|
data[moduleUid] = port;
|
|
6664
6918
|
}
|
|
6665
|
-
|
|
6919
|
+
fs14.writeFileSync(PORTS_FILE, JSON.stringify(data, null, 2), "utf8");
|
|
6666
6920
|
} catch (error) {
|
|
6667
6921
|
console.warn(`[PortAllocator] EP1042: Failed to save ports.json:`, error);
|
|
6668
6922
|
}
|
|
@@ -7138,10 +7392,10 @@ function getPreviewManager() {
|
|
|
7138
7392
|
}
|
|
7139
7393
|
|
|
7140
7394
|
// src/utils/dev-server.ts
|
|
7141
|
-
var
|
|
7395
|
+
var import_child_process12 = require("child_process");
|
|
7142
7396
|
var import_core9 = __toESM(require_dist());
|
|
7143
|
-
var
|
|
7144
|
-
var
|
|
7397
|
+
var fs15 = __toESM(require("fs"));
|
|
7398
|
+
var path16 = __toESM(require("path"));
|
|
7145
7399
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
7146
7400
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
7147
7401
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -7149,26 +7403,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
|
7149
7403
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
7150
7404
|
var activeServers = /* @__PURE__ */ new Map();
|
|
7151
7405
|
function getLogsDir() {
|
|
7152
|
-
const logsDir =
|
|
7153
|
-
if (!
|
|
7154
|
-
|
|
7406
|
+
const logsDir = path16.join((0, import_core9.getConfigDir)(), "logs");
|
|
7407
|
+
if (!fs15.existsSync(logsDir)) {
|
|
7408
|
+
fs15.mkdirSync(logsDir, { recursive: true });
|
|
7155
7409
|
}
|
|
7156
7410
|
return logsDir;
|
|
7157
7411
|
}
|
|
7158
7412
|
function getLogFilePath(moduleUid) {
|
|
7159
|
-
return
|
|
7413
|
+
return path16.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
7160
7414
|
}
|
|
7161
7415
|
function rotateLogIfNeeded(logPath) {
|
|
7162
7416
|
try {
|
|
7163
|
-
if (
|
|
7164
|
-
const stats =
|
|
7417
|
+
if (fs15.existsSync(logPath)) {
|
|
7418
|
+
const stats = fs15.statSync(logPath);
|
|
7165
7419
|
if (stats.size > MAX_LOG_SIZE_BYTES) {
|
|
7166
7420
|
const backupPath = `${logPath}.1`;
|
|
7167
|
-
if (
|
|
7168
|
-
|
|
7421
|
+
if (fs15.existsSync(backupPath)) {
|
|
7422
|
+
fs15.unlinkSync(backupPath);
|
|
7169
7423
|
}
|
|
7170
|
-
|
|
7171
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
7424
|
+
fs15.renameSync(logPath, backupPath);
|
|
7425
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path16.basename(logPath)}`);
|
|
7172
7426
|
}
|
|
7173
7427
|
}
|
|
7174
7428
|
} catch (error) {
|
|
@@ -7181,13 +7435,13 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
7181
7435
|
const prefix = isError ? "ERR" : "OUT";
|
|
7182
7436
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
7183
7437
|
`;
|
|
7184
|
-
|
|
7438
|
+
fs15.appendFileSync(logPath, logLine);
|
|
7185
7439
|
} catch {
|
|
7186
7440
|
}
|
|
7187
7441
|
}
|
|
7188
7442
|
async function killProcessOnPort(port) {
|
|
7189
7443
|
try {
|
|
7190
|
-
const result = (0,
|
|
7444
|
+
const result = (0, import_child_process12.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
7191
7445
|
if (!result) {
|
|
7192
7446
|
console.log(`[DevServer] EP929: No process found on port ${port}`);
|
|
7193
7447
|
return true;
|
|
@@ -7196,7 +7450,7 @@ async function killProcessOnPort(port) {
|
|
|
7196
7450
|
console.log(`[DevServer] EP929: Found ${pids.length} process(es) on port ${port}: ${pids.join(", ")}`);
|
|
7197
7451
|
for (const pid of pids) {
|
|
7198
7452
|
try {
|
|
7199
|
-
(0,
|
|
7453
|
+
(0, import_child_process12.execSync)(`kill -15 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
|
|
7200
7454
|
console.log(`[DevServer] EP929: Sent SIGTERM to PID ${pid}`);
|
|
7201
7455
|
} catch {
|
|
7202
7456
|
}
|
|
@@ -7204,8 +7458,8 @@ async function killProcessOnPort(port) {
|
|
|
7204
7458
|
await new Promise((resolve3) => setTimeout(resolve3, 1e3));
|
|
7205
7459
|
for (const pid of pids) {
|
|
7206
7460
|
try {
|
|
7207
|
-
(0,
|
|
7208
|
-
(0,
|
|
7461
|
+
(0, import_child_process12.execSync)(`kill -0 ${pid} 2>/dev/null`, { encoding: "utf8" });
|
|
7462
|
+
(0, import_child_process12.execSync)(`kill -9 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
|
|
7209
7463
|
console.log(`[DevServer] EP929: Force killed PID ${pid}`);
|
|
7210
7464
|
} catch {
|
|
7211
7465
|
}
|
|
@@ -7256,7 +7510,7 @@ function spawnDevServerProcess(projectPath, port, moduleUid, logPath, customComm
|
|
|
7256
7510
|
if (injectedCount > 0) {
|
|
7257
7511
|
console.log(`[DevServer] EP998: Injecting ${injectedCount} env vars from database`);
|
|
7258
7512
|
}
|
|
7259
|
-
const devProcess = (0,
|
|
7513
|
+
const devProcess = (0, import_child_process12.spawn)(cmd, args, {
|
|
7260
7514
|
cwd: projectPath,
|
|
7261
7515
|
env: mergedEnv,
|
|
7262
7516
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7360,8 +7614,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
7360
7614
|
});
|
|
7361
7615
|
injectedEnvVars = result.envVars;
|
|
7362
7616
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
7363
|
-
const envFilePath =
|
|
7364
|
-
if (!
|
|
7617
|
+
const envFilePath = path16.join(projectPath, ".env");
|
|
7618
|
+
if (!fs15.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
7365
7619
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
7366
7620
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
7367
7621
|
}
|
|
@@ -7467,8 +7721,8 @@ function getDevServerStatus() {
|
|
|
7467
7721
|
}
|
|
7468
7722
|
|
|
7469
7723
|
// src/daemon/worktree-manager.ts
|
|
7470
|
-
var
|
|
7471
|
-
var
|
|
7724
|
+
var fs16 = __toESM(require("fs"));
|
|
7725
|
+
var path17 = __toESM(require("path"));
|
|
7472
7726
|
var import_core10 = __toESM(require_dist());
|
|
7473
7727
|
function validateModuleUid(moduleUid) {
|
|
7474
7728
|
if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
|
|
@@ -7492,8 +7746,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7492
7746
|
// ============================================================
|
|
7493
7747
|
this.lockPath = "";
|
|
7494
7748
|
this.projectRoot = projectRoot;
|
|
7495
|
-
this.bareRepoPath =
|
|
7496
|
-
this.configPath =
|
|
7749
|
+
this.bareRepoPath = path17.join(projectRoot, ".bare");
|
|
7750
|
+
this.configPath = path17.join(projectRoot, ".episoda", "config.json");
|
|
7497
7751
|
this.gitExecutor = new import_core10.GitExecutor();
|
|
7498
7752
|
}
|
|
7499
7753
|
/**
|
|
@@ -7504,13 +7758,13 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7504
7758
|
*/
|
|
7505
7759
|
async initialize() {
|
|
7506
7760
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
7507
|
-
if (!
|
|
7761
|
+
if (!fs16.existsSync(this.bareRepoPath)) {
|
|
7508
7762
|
if (debug) {
|
|
7509
7763
|
console.log(`[WorktreeManager] initialize: .bare not found at ${this.bareRepoPath}`);
|
|
7510
7764
|
}
|
|
7511
7765
|
return false;
|
|
7512
7766
|
}
|
|
7513
|
-
if (!
|
|
7767
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
7514
7768
|
if (debug) {
|
|
7515
7769
|
console.log(`[WorktreeManager] initialize: config not found at ${this.configPath}`);
|
|
7516
7770
|
}
|
|
@@ -7542,10 +7796,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7542
7796
|
*/
|
|
7543
7797
|
async ensureFetchRefspecConfigured() {
|
|
7544
7798
|
try {
|
|
7545
|
-
const { execSync:
|
|
7799
|
+
const { execSync: execSync10 } = require("child_process");
|
|
7546
7800
|
let fetchRefspec = null;
|
|
7547
7801
|
try {
|
|
7548
|
-
fetchRefspec =
|
|
7802
|
+
fetchRefspec = execSync10("git config --get remote.origin.fetch", {
|
|
7549
7803
|
cwd: this.bareRepoPath,
|
|
7550
7804
|
encoding: "utf-8",
|
|
7551
7805
|
timeout: 5e3
|
|
@@ -7554,7 +7808,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7554
7808
|
}
|
|
7555
7809
|
if (!fetchRefspec) {
|
|
7556
7810
|
console.log("[WorktreeManager] EP1014: Configuring missing fetch refspec for bare repo");
|
|
7557
|
-
|
|
7811
|
+
execSync10('git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', {
|
|
7558
7812
|
cwd: this.bareRepoPath,
|
|
7559
7813
|
timeout: 5e3
|
|
7560
7814
|
});
|
|
@@ -7569,8 +7823,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7569
7823
|
*/
|
|
7570
7824
|
static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
|
|
7571
7825
|
const manager = new _WorktreeManager(projectRoot);
|
|
7572
|
-
const episodaDir =
|
|
7573
|
-
|
|
7826
|
+
const episodaDir = path17.join(projectRoot, ".episoda");
|
|
7827
|
+
fs16.mkdirSync(episodaDir, { recursive: true });
|
|
7574
7828
|
const cloneResult = await manager.gitExecutor.execute({
|
|
7575
7829
|
action: "clone_bare",
|
|
7576
7830
|
url: repoUrl,
|
|
@@ -7601,7 +7855,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7601
7855
|
error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
|
|
7602
7856
|
};
|
|
7603
7857
|
}
|
|
7604
|
-
const worktreePath =
|
|
7858
|
+
const worktreePath = path17.join(this.projectRoot, moduleUid);
|
|
7605
7859
|
const lockAcquired = await this.acquireLock();
|
|
7606
7860
|
if (!lockAcquired) {
|
|
7607
7861
|
return {
|
|
@@ -7792,7 +8046,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7792
8046
|
let prunedCount = 0;
|
|
7793
8047
|
await this.updateConfigSafe((config) => {
|
|
7794
8048
|
const initialCount = config.worktrees.length;
|
|
7795
|
-
config.worktrees = config.worktrees.filter((w) =>
|
|
8049
|
+
config.worktrees = config.worktrees.filter((w) => fs16.existsSync(w.worktreePath));
|
|
7796
8050
|
prunedCount = initialCount - config.worktrees.length;
|
|
7797
8051
|
return config;
|
|
7798
8052
|
});
|
|
@@ -7873,16 +8127,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7873
8127
|
const retryInterval = 50;
|
|
7874
8128
|
while (Date.now() - startTime < timeoutMs) {
|
|
7875
8129
|
try {
|
|
7876
|
-
|
|
8130
|
+
fs16.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
7877
8131
|
return true;
|
|
7878
8132
|
} catch (err) {
|
|
7879
8133
|
if (err.code === "EEXIST") {
|
|
7880
8134
|
try {
|
|
7881
|
-
const stats =
|
|
8135
|
+
const stats = fs16.statSync(lockPath);
|
|
7882
8136
|
const lockAge = Date.now() - stats.mtimeMs;
|
|
7883
8137
|
if (lockAge > 3e4) {
|
|
7884
8138
|
try {
|
|
7885
|
-
const lockContent =
|
|
8139
|
+
const lockContent = fs16.readFileSync(lockPath, "utf-8").trim();
|
|
7886
8140
|
const lockPid = parseInt(lockContent, 10);
|
|
7887
8141
|
if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
|
|
7888
8142
|
await new Promise((resolve3) => setTimeout(resolve3, retryInterval));
|
|
@@ -7891,7 +8145,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7891
8145
|
} catch {
|
|
7892
8146
|
}
|
|
7893
8147
|
try {
|
|
7894
|
-
|
|
8148
|
+
fs16.unlinkSync(lockPath);
|
|
7895
8149
|
} catch {
|
|
7896
8150
|
}
|
|
7897
8151
|
continue;
|
|
@@ -7912,7 +8166,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7912
8166
|
*/
|
|
7913
8167
|
releaseLock() {
|
|
7914
8168
|
try {
|
|
7915
|
-
|
|
8169
|
+
fs16.unlinkSync(this.getLockPath());
|
|
7916
8170
|
} catch {
|
|
7917
8171
|
}
|
|
7918
8172
|
}
|
|
@@ -7936,11 +8190,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7936
8190
|
// Turborepo cache
|
|
7937
8191
|
];
|
|
7938
8192
|
for (const cacheDir of cacheDirs) {
|
|
7939
|
-
const cachePath =
|
|
8193
|
+
const cachePath = path17.join(worktreePath, cacheDir);
|
|
7940
8194
|
try {
|
|
7941
|
-
if (
|
|
8195
|
+
if (fs16.existsSync(cachePath)) {
|
|
7942
8196
|
console.log(`[WorktreeManager] EP1070: Cleaning build cache: ${cacheDir}`);
|
|
7943
|
-
|
|
8197
|
+
fs16.rmSync(cachePath, { recursive: true, force: true });
|
|
7944
8198
|
}
|
|
7945
8199
|
} catch (error) {
|
|
7946
8200
|
console.warn(`[WorktreeManager] EP1070: Failed to clean ${cacheDir} (non-blocking):`, error.message);
|
|
@@ -7949,10 +8203,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7949
8203
|
}
|
|
7950
8204
|
readConfig() {
|
|
7951
8205
|
try {
|
|
7952
|
-
if (!
|
|
8206
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
7953
8207
|
return null;
|
|
7954
8208
|
}
|
|
7955
|
-
const content =
|
|
8209
|
+
const content = fs16.readFileSync(this.configPath, "utf-8");
|
|
7956
8210
|
return JSON.parse(content);
|
|
7957
8211
|
} catch (error) {
|
|
7958
8212
|
if (error instanceof SyntaxError) {
|
|
@@ -7966,11 +8220,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7966
8220
|
}
|
|
7967
8221
|
writeConfig(config) {
|
|
7968
8222
|
try {
|
|
7969
|
-
const dir =
|
|
7970
|
-
if (!
|
|
7971
|
-
|
|
8223
|
+
const dir = path17.dirname(this.configPath);
|
|
8224
|
+
if (!fs16.existsSync(dir)) {
|
|
8225
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
7972
8226
|
}
|
|
7973
|
-
|
|
8227
|
+
fs16.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
7974
8228
|
} catch (error) {
|
|
7975
8229
|
console.error("[WorktreeManager] Failed to write config:", error);
|
|
7976
8230
|
throw error;
|
|
@@ -8051,14 +8305,14 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8051
8305
|
}
|
|
8052
8306
|
try {
|
|
8053
8307
|
for (const file of files) {
|
|
8054
|
-
const srcPath =
|
|
8055
|
-
const destPath =
|
|
8056
|
-
if (
|
|
8057
|
-
const destDir =
|
|
8058
|
-
if (!
|
|
8059
|
-
|
|
8060
|
-
}
|
|
8061
|
-
|
|
8308
|
+
const srcPath = path17.join(mainWorktree.worktreePath, file);
|
|
8309
|
+
const destPath = path17.join(worktree.worktreePath, file);
|
|
8310
|
+
if (fs16.existsSync(srcPath)) {
|
|
8311
|
+
const destDir = path17.dirname(destPath);
|
|
8312
|
+
if (!fs16.existsSync(destDir)) {
|
|
8313
|
+
fs16.mkdirSync(destDir, { recursive: true });
|
|
8314
|
+
}
|
|
8315
|
+
fs16.copyFileSync(srcPath, destPath);
|
|
8062
8316
|
console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`);
|
|
8063
8317
|
} else {
|
|
8064
8318
|
console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`);
|
|
@@ -8089,8 +8343,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8089
8343
|
console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
|
|
8090
8344
|
console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
|
|
8091
8345
|
try {
|
|
8092
|
-
const { execSync:
|
|
8093
|
-
|
|
8346
|
+
const { execSync: execSync10 } = require("child_process");
|
|
8347
|
+
execSync10(script, {
|
|
8094
8348
|
cwd: worktree.worktreePath,
|
|
8095
8349
|
stdio: "inherit",
|
|
8096
8350
|
timeout: TIMEOUT_MINUTES * 60 * 1e3,
|
|
@@ -8124,8 +8378,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8124
8378
|
console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
|
|
8125
8379
|
console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
|
|
8126
8380
|
try {
|
|
8127
|
-
const { execSync:
|
|
8128
|
-
|
|
8381
|
+
const { execSync: execSync10 } = require("child_process");
|
|
8382
|
+
execSync10(script, {
|
|
8129
8383
|
cwd: worktree.worktreePath,
|
|
8130
8384
|
stdio: "inherit",
|
|
8131
8385
|
timeout: TIMEOUT_MINUTES * 60 * 1e3,
|
|
@@ -8141,7 +8395,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8141
8395
|
}
|
|
8142
8396
|
};
|
|
8143
8397
|
function getEpisodaRoot() {
|
|
8144
|
-
return process.env.EPISODA_ROOT ||
|
|
8398
|
+
return process.env.EPISODA_ROOT || path17.join(require("os").homedir(), "episoda");
|
|
8145
8399
|
}
|
|
8146
8400
|
async function isWorktreeProject(projectRoot) {
|
|
8147
8401
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
@@ -8156,7 +8410,7 @@ async function isWorktreeProject(projectRoot) {
|
|
|
8156
8410
|
return result;
|
|
8157
8411
|
}
|
|
8158
8412
|
async function findProjectRoot(startPath) {
|
|
8159
|
-
let current =
|
|
8413
|
+
let current = path17.resolve(startPath);
|
|
8160
8414
|
const episodaRoot = getEpisodaRoot();
|
|
8161
8415
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
8162
8416
|
if (debug) {
|
|
@@ -8169,14 +8423,14 @@ async function findProjectRoot(startPath) {
|
|
|
8169
8423
|
return null;
|
|
8170
8424
|
}
|
|
8171
8425
|
for (let i = 0; i < 10; i++) {
|
|
8172
|
-
const bareDir =
|
|
8173
|
-
const episodaDir =
|
|
8426
|
+
const bareDir = path17.join(current, ".bare");
|
|
8427
|
+
const episodaDir = path17.join(current, ".episoda");
|
|
8174
8428
|
if (debug) {
|
|
8175
|
-
const bareExists =
|
|
8176
|
-
const episodaExists =
|
|
8429
|
+
const bareExists = fs16.existsSync(bareDir);
|
|
8430
|
+
const episodaExists = fs16.existsSync(episodaDir);
|
|
8177
8431
|
console.log(`[WorktreeManager] findProjectRoot: checking ${current} (.bare=${bareExists}, .episoda=${episodaExists})`);
|
|
8178
8432
|
}
|
|
8179
|
-
if (
|
|
8433
|
+
if (fs16.existsSync(bareDir) && fs16.existsSync(episodaDir)) {
|
|
8180
8434
|
if (await isWorktreeProject(current)) {
|
|
8181
8435
|
if (debug) {
|
|
8182
8436
|
console.log(`[WorktreeManager] findProjectRoot: found valid project at ${current}`);
|
|
@@ -8184,7 +8438,7 @@ async function findProjectRoot(startPath) {
|
|
|
8184
8438
|
return current;
|
|
8185
8439
|
}
|
|
8186
8440
|
}
|
|
8187
|
-
const parent =
|
|
8441
|
+
const parent = path17.dirname(current);
|
|
8188
8442
|
if (parent === current) {
|
|
8189
8443
|
break;
|
|
8190
8444
|
}
|
|
@@ -8197,19 +8451,19 @@ async function findProjectRoot(startPath) {
|
|
|
8197
8451
|
}
|
|
8198
8452
|
|
|
8199
8453
|
// src/utils/worktree.ts
|
|
8200
|
-
var
|
|
8201
|
-
var
|
|
8454
|
+
var path18 = __toESM(require("path"));
|
|
8455
|
+
var fs17 = __toESM(require("fs"));
|
|
8202
8456
|
var os7 = __toESM(require("os"));
|
|
8203
8457
|
var import_core11 = __toESM(require_dist());
|
|
8204
8458
|
function getEpisodaRoot2() {
|
|
8205
|
-
return process.env.EPISODA_ROOT ||
|
|
8459
|
+
return process.env.EPISODA_ROOT || path18.join(os7.homedir(), "episoda");
|
|
8206
8460
|
}
|
|
8207
8461
|
function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
|
|
8208
8462
|
const root = getEpisodaRoot2();
|
|
8209
|
-
const worktreePath =
|
|
8463
|
+
const worktreePath = path18.join(root, workspaceSlug, projectSlug, moduleUid);
|
|
8210
8464
|
return {
|
|
8211
8465
|
path: worktreePath,
|
|
8212
|
-
exists:
|
|
8466
|
+
exists: fs17.existsSync(worktreePath),
|
|
8213
8467
|
moduleUid
|
|
8214
8468
|
};
|
|
8215
8469
|
}
|
|
@@ -8223,61 +8477,61 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
8223
8477
|
}
|
|
8224
8478
|
|
|
8225
8479
|
// src/framework-detector.ts
|
|
8226
|
-
var
|
|
8227
|
-
var
|
|
8480
|
+
var fs18 = __toESM(require("fs"));
|
|
8481
|
+
var path19 = __toESM(require("path"));
|
|
8228
8482
|
function getInstallCommand(cwd) {
|
|
8229
|
-
if (
|
|
8483
|
+
if (fs18.existsSync(path19.join(cwd, "bun.lockb"))) {
|
|
8230
8484
|
return {
|
|
8231
8485
|
command: ["bun", "install"],
|
|
8232
8486
|
description: "Installing dependencies with bun",
|
|
8233
8487
|
detectedFrom: "bun.lockb"
|
|
8234
8488
|
};
|
|
8235
8489
|
}
|
|
8236
|
-
if (
|
|
8490
|
+
if (fs18.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) {
|
|
8237
8491
|
return {
|
|
8238
8492
|
command: ["pnpm", "install"],
|
|
8239
8493
|
description: "Installing dependencies with pnpm",
|
|
8240
8494
|
detectedFrom: "pnpm-lock.yaml"
|
|
8241
8495
|
};
|
|
8242
8496
|
}
|
|
8243
|
-
if (
|
|
8497
|
+
if (fs18.existsSync(path19.join(cwd, "yarn.lock"))) {
|
|
8244
8498
|
return {
|
|
8245
8499
|
command: ["yarn", "install"],
|
|
8246
8500
|
description: "Installing dependencies with yarn",
|
|
8247
8501
|
detectedFrom: "yarn.lock"
|
|
8248
8502
|
};
|
|
8249
8503
|
}
|
|
8250
|
-
if (
|
|
8504
|
+
if (fs18.existsSync(path19.join(cwd, "package-lock.json"))) {
|
|
8251
8505
|
return {
|
|
8252
8506
|
command: ["npm", "ci"],
|
|
8253
8507
|
description: "Installing dependencies with npm ci",
|
|
8254
8508
|
detectedFrom: "package-lock.json"
|
|
8255
8509
|
};
|
|
8256
8510
|
}
|
|
8257
|
-
if (
|
|
8511
|
+
if (fs18.existsSync(path19.join(cwd, "package.json"))) {
|
|
8258
8512
|
return {
|
|
8259
8513
|
command: ["npm", "install"],
|
|
8260
8514
|
description: "Installing dependencies with npm",
|
|
8261
8515
|
detectedFrom: "package.json"
|
|
8262
8516
|
};
|
|
8263
8517
|
}
|
|
8264
|
-
if (
|
|
8518
|
+
if (fs18.existsSync(path19.join(cwd, "Pipfile.lock")) || fs18.existsSync(path19.join(cwd, "Pipfile"))) {
|
|
8265
8519
|
return {
|
|
8266
8520
|
command: ["pipenv", "install"],
|
|
8267
8521
|
description: "Installing dependencies with pipenv",
|
|
8268
|
-
detectedFrom:
|
|
8522
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
|
|
8269
8523
|
};
|
|
8270
8524
|
}
|
|
8271
|
-
if (
|
|
8525
|
+
if (fs18.existsSync(path19.join(cwd, "poetry.lock"))) {
|
|
8272
8526
|
return {
|
|
8273
8527
|
command: ["poetry", "install"],
|
|
8274
8528
|
description: "Installing dependencies with poetry",
|
|
8275
8529
|
detectedFrom: "poetry.lock"
|
|
8276
8530
|
};
|
|
8277
8531
|
}
|
|
8278
|
-
if (
|
|
8279
|
-
const pyprojectPath =
|
|
8280
|
-
const content =
|
|
8532
|
+
if (fs18.existsSync(path19.join(cwd, "pyproject.toml"))) {
|
|
8533
|
+
const pyprojectPath = path19.join(cwd, "pyproject.toml");
|
|
8534
|
+
const content = fs18.readFileSync(pyprojectPath, "utf-8");
|
|
8281
8535
|
if (content.includes("[tool.poetry]")) {
|
|
8282
8536
|
return {
|
|
8283
8537
|
command: ["poetry", "install"],
|
|
@@ -8286,41 +8540,41 @@ function getInstallCommand(cwd) {
|
|
|
8286
8540
|
};
|
|
8287
8541
|
}
|
|
8288
8542
|
}
|
|
8289
|
-
if (
|
|
8543
|
+
if (fs18.existsSync(path19.join(cwd, "requirements.txt"))) {
|
|
8290
8544
|
return {
|
|
8291
8545
|
command: ["pip", "install", "-r", "requirements.txt"],
|
|
8292
8546
|
description: "Installing dependencies with pip",
|
|
8293
8547
|
detectedFrom: "requirements.txt"
|
|
8294
8548
|
};
|
|
8295
8549
|
}
|
|
8296
|
-
if (
|
|
8550
|
+
if (fs18.existsSync(path19.join(cwd, "Gemfile.lock")) || fs18.existsSync(path19.join(cwd, "Gemfile"))) {
|
|
8297
8551
|
return {
|
|
8298
8552
|
command: ["bundle", "install"],
|
|
8299
8553
|
description: "Installing dependencies with bundler",
|
|
8300
|
-
detectedFrom:
|
|
8554
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
|
|
8301
8555
|
};
|
|
8302
8556
|
}
|
|
8303
|
-
if (
|
|
8557
|
+
if (fs18.existsSync(path19.join(cwd, "go.sum")) || fs18.existsSync(path19.join(cwd, "go.mod"))) {
|
|
8304
8558
|
return {
|
|
8305
8559
|
command: ["go", "mod", "download"],
|
|
8306
8560
|
description: "Downloading Go modules",
|
|
8307
|
-
detectedFrom:
|
|
8561
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
|
|
8308
8562
|
};
|
|
8309
8563
|
}
|
|
8310
|
-
if (
|
|
8564
|
+
if (fs18.existsSync(path19.join(cwd, "Cargo.lock")) || fs18.existsSync(path19.join(cwd, "Cargo.toml"))) {
|
|
8311
8565
|
return {
|
|
8312
8566
|
command: ["cargo", "build"],
|
|
8313
8567
|
description: "Building Rust project (downloads dependencies)",
|
|
8314
|
-
detectedFrom:
|
|
8568
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
|
|
8315
8569
|
};
|
|
8316
8570
|
}
|
|
8317
8571
|
return null;
|
|
8318
8572
|
}
|
|
8319
8573
|
|
|
8320
8574
|
// src/daemon/daemon-process.ts
|
|
8321
|
-
var
|
|
8575
|
+
var fs19 = __toESM(require("fs"));
|
|
8322
8576
|
var os8 = __toESM(require("os"));
|
|
8323
|
-
var
|
|
8577
|
+
var path20 = __toESM(require("path"));
|
|
8324
8578
|
var packageJson = require_package();
|
|
8325
8579
|
async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
8326
8580
|
const now = Date.now();
|
|
@@ -8834,7 +9088,7 @@ var Daemon = class _Daemon {
|
|
|
8834
9088
|
client.updateActivity();
|
|
8835
9089
|
try {
|
|
8836
9090
|
const gitCmd = message.command;
|
|
8837
|
-
const bareRepoPath =
|
|
9091
|
+
const bareRepoPath = path20.join(projectPath, ".bare");
|
|
8838
9092
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
8839
9093
|
if (gitCmd.worktreePath) {
|
|
8840
9094
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -9108,6 +9362,8 @@ var Daemon = class _Daemon {
|
|
|
9108
9362
|
moduleId: cmd.moduleId,
|
|
9109
9363
|
moduleUid: cmd.moduleUid,
|
|
9110
9364
|
projectPath: agentWorkingDir,
|
|
9365
|
+
provider: cmd.provider || "claude",
|
|
9366
|
+
// EP1133: Multi-provider support
|
|
9111
9367
|
message: cmd.message,
|
|
9112
9368
|
credentials: cmd.credentials,
|
|
9113
9369
|
systemPrompt: cmd.systemPrompt,
|
|
@@ -9125,7 +9381,9 @@ var Daemon = class _Daemon {
|
|
|
9125
9381
|
sessionId: cmd.sessionId,
|
|
9126
9382
|
message: cmd.message,
|
|
9127
9383
|
isFirstMessage: false,
|
|
9384
|
+
agentSessionId: cmd.agentSessionId || cmd.claudeSessionId,
|
|
9128
9385
|
claudeSessionId: cmd.claudeSessionId,
|
|
9386
|
+
// Backward compat
|
|
9129
9387
|
...callbacks
|
|
9130
9388
|
});
|
|
9131
9389
|
result = {
|
|
@@ -9359,8 +9617,8 @@ var Daemon = class _Daemon {
|
|
|
9359
9617
|
let daemonPid;
|
|
9360
9618
|
try {
|
|
9361
9619
|
const pidPath = getPidFilePath();
|
|
9362
|
-
if (
|
|
9363
|
-
const pidStr =
|
|
9620
|
+
if (fs19.existsSync(pidPath)) {
|
|
9621
|
+
const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
|
|
9364
9622
|
daemonPid = parseInt(pidStr, 10);
|
|
9365
9623
|
}
|
|
9366
9624
|
} catch (pidError) {
|
|
@@ -9441,28 +9699,28 @@ var Daemon = class _Daemon {
|
|
|
9441
9699
|
* - workDir: The directory to run git commands in (cwd)
|
|
9442
9700
|
*/
|
|
9443
9701
|
getGitDirs(projectPath) {
|
|
9444
|
-
const bareDir =
|
|
9445
|
-
const gitPath =
|
|
9446
|
-
if (
|
|
9702
|
+
const bareDir = path20.join(projectPath, ".bare");
|
|
9703
|
+
const gitPath = path20.join(projectPath, ".git");
|
|
9704
|
+
if (fs19.existsSync(bareDir) && fs19.statSync(bareDir).isDirectory()) {
|
|
9447
9705
|
return { gitDir: bareDir, workDir: projectPath };
|
|
9448
9706
|
}
|
|
9449
|
-
if (
|
|
9707
|
+
if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isDirectory()) {
|
|
9450
9708
|
return { gitDir: null, workDir: projectPath };
|
|
9451
9709
|
}
|
|
9452
|
-
if (
|
|
9710
|
+
if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isFile()) {
|
|
9453
9711
|
return { gitDir: null, workDir: projectPath };
|
|
9454
9712
|
}
|
|
9455
|
-
const entries =
|
|
9713
|
+
const entries = fs19.readdirSync(projectPath, { withFileTypes: true });
|
|
9456
9714
|
for (const entry of entries) {
|
|
9457
9715
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
9458
|
-
const worktreePath =
|
|
9459
|
-
const worktreeGit =
|
|
9460
|
-
if (
|
|
9716
|
+
const worktreePath = path20.join(projectPath, entry.name);
|
|
9717
|
+
const worktreeGit = path20.join(worktreePath, ".git");
|
|
9718
|
+
if (fs19.existsSync(worktreeGit)) {
|
|
9461
9719
|
return { gitDir: null, workDir: worktreePath };
|
|
9462
9720
|
}
|
|
9463
9721
|
}
|
|
9464
9722
|
}
|
|
9465
|
-
if (
|
|
9723
|
+
if (fs19.existsSync(bareDir)) {
|
|
9466
9724
|
return { gitDir: bareDir, workDir: projectPath };
|
|
9467
9725
|
}
|
|
9468
9726
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -9480,31 +9738,31 @@ var Daemon = class _Daemon {
|
|
|
9480
9738
|
*/
|
|
9481
9739
|
async configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
|
|
9482
9740
|
try {
|
|
9483
|
-
const { execSync:
|
|
9741
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
9484
9742
|
const { gitDir, workDir } = this.getGitDirs(projectPath);
|
|
9485
9743
|
const gitCmd = gitDir ? `git --git-dir="${gitDir}"` : "git";
|
|
9486
|
-
|
|
9744
|
+
execSync10(`${gitCmd} config episoda.userId ${userId}`, {
|
|
9487
9745
|
cwd: workDir,
|
|
9488
9746
|
encoding: "utf8",
|
|
9489
9747
|
stdio: "pipe"
|
|
9490
9748
|
});
|
|
9491
|
-
|
|
9749
|
+
execSync10(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
|
|
9492
9750
|
cwd: workDir,
|
|
9493
9751
|
encoding: "utf8",
|
|
9494
9752
|
stdio: "pipe"
|
|
9495
9753
|
});
|
|
9496
|
-
|
|
9754
|
+
execSync10(`${gitCmd} config episoda.machineId ${machineId}`, {
|
|
9497
9755
|
cwd: workDir,
|
|
9498
9756
|
encoding: "utf8",
|
|
9499
9757
|
stdio: "pipe"
|
|
9500
9758
|
});
|
|
9501
|
-
|
|
9759
|
+
execSync10(`${gitCmd} config episoda.projectId ${projectId}`, {
|
|
9502
9760
|
cwd: workDir,
|
|
9503
9761
|
encoding: "utf8",
|
|
9504
9762
|
stdio: "pipe"
|
|
9505
9763
|
});
|
|
9506
9764
|
if (machineUuid) {
|
|
9507
|
-
|
|
9765
|
+
execSync10(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
|
|
9508
9766
|
cwd: workDir,
|
|
9509
9767
|
encoding: "utf8",
|
|
9510
9768
|
stdio: "pipe"
|
|
@@ -9526,24 +9784,24 @@ var Daemon = class _Daemon {
|
|
|
9526
9784
|
async installGitHooks(projectPath) {
|
|
9527
9785
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
9528
9786
|
let hooksDir;
|
|
9529
|
-
const bareHooksDir =
|
|
9530
|
-
const gitHooksDir =
|
|
9531
|
-
if (
|
|
9787
|
+
const bareHooksDir = path20.join(projectPath, ".bare", "hooks");
|
|
9788
|
+
const gitHooksDir = path20.join(projectPath, ".git", "hooks");
|
|
9789
|
+
if (fs19.existsSync(bareHooksDir)) {
|
|
9532
9790
|
hooksDir = bareHooksDir;
|
|
9533
|
-
} else if (
|
|
9791
|
+
} else if (fs19.existsSync(gitHooksDir) && fs19.statSync(path20.join(projectPath, ".git")).isDirectory()) {
|
|
9534
9792
|
hooksDir = gitHooksDir;
|
|
9535
9793
|
} else {
|
|
9536
|
-
const parentBareHooks =
|
|
9537
|
-
if (
|
|
9794
|
+
const parentBareHooks = path20.join(projectPath, "..", ".bare", "hooks");
|
|
9795
|
+
if (fs19.existsSync(parentBareHooks)) {
|
|
9538
9796
|
hooksDir = parentBareHooks;
|
|
9539
9797
|
} else {
|
|
9540
9798
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
9541
9799
|
return;
|
|
9542
9800
|
}
|
|
9543
9801
|
}
|
|
9544
|
-
if (!
|
|
9802
|
+
if (!fs19.existsSync(hooksDir)) {
|
|
9545
9803
|
try {
|
|
9546
|
-
|
|
9804
|
+
fs19.mkdirSync(hooksDir, { recursive: true });
|
|
9547
9805
|
} catch (error) {
|
|
9548
9806
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
9549
9807
|
return;
|
|
@@ -9551,20 +9809,20 @@ var Daemon = class _Daemon {
|
|
|
9551
9809
|
}
|
|
9552
9810
|
for (const hookName of hooks) {
|
|
9553
9811
|
try {
|
|
9554
|
-
const hookPath =
|
|
9555
|
-
const bundledHookPath =
|
|
9556
|
-
if (!
|
|
9812
|
+
const hookPath = path20.join(hooksDir, hookName);
|
|
9813
|
+
const bundledHookPath = path20.join(__dirname, "..", "hooks", hookName);
|
|
9814
|
+
if (!fs19.existsSync(bundledHookPath)) {
|
|
9557
9815
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
9558
9816
|
continue;
|
|
9559
9817
|
}
|
|
9560
|
-
const hookContent =
|
|
9561
|
-
if (
|
|
9562
|
-
const existingContent =
|
|
9818
|
+
const hookContent = fs19.readFileSync(bundledHookPath, "utf-8");
|
|
9819
|
+
if (fs19.existsSync(hookPath)) {
|
|
9820
|
+
const existingContent = fs19.readFileSync(hookPath, "utf-8");
|
|
9563
9821
|
if (existingContent === hookContent) {
|
|
9564
9822
|
continue;
|
|
9565
9823
|
}
|
|
9566
9824
|
}
|
|
9567
|
-
|
|
9825
|
+
fs19.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
9568
9826
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
9569
9827
|
} catch (error) {
|
|
9570
9828
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -10031,8 +10289,8 @@ var Daemon = class _Daemon {
|
|
|
10031
10289
|
console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
10032
10290
|
console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
|
|
10033
10291
|
try {
|
|
10034
|
-
const { execSync:
|
|
10035
|
-
|
|
10292
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10293
|
+
execSync10(installCmd.command.join(" "), {
|
|
10036
10294
|
cwd: worktreePath,
|
|
10037
10295
|
stdio: "inherit",
|
|
10038
10296
|
timeout: 10 * 60 * 1e3,
|
|
@@ -10085,8 +10343,8 @@ var Daemon = class _Daemon {
|
|
|
10085
10343
|
console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
10086
10344
|
console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
|
|
10087
10345
|
try {
|
|
10088
|
-
const { execSync:
|
|
10089
|
-
|
|
10346
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10347
|
+
execSync10(installCmd.command.join(" "), {
|
|
10090
10348
|
cwd: worktreePath,
|
|
10091
10349
|
stdio: "inherit",
|
|
10092
10350
|
timeout: 10 * 60 * 1e3,
|
|
@@ -10670,8 +10928,8 @@ var Daemon = class _Daemon {
|
|
|
10670
10928
|
await this.shutdown();
|
|
10671
10929
|
try {
|
|
10672
10930
|
const pidPath = getPidFilePath();
|
|
10673
|
-
if (
|
|
10674
|
-
|
|
10931
|
+
if (fs19.existsSync(pidPath)) {
|
|
10932
|
+
fs19.unlinkSync(pidPath);
|
|
10675
10933
|
console.log("[Daemon] PID file cleaned up");
|
|
10676
10934
|
}
|
|
10677
10935
|
} catch (error) {
|