episoda 0.2.83 → 0.2.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon/daemon-process.js +633 -381
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -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.85",
|
|
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,112 @@ 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 authData = {
|
|
5025
|
+
OPENAI_API_KEY: null,
|
|
5026
|
+
tokens: {
|
|
5027
|
+
id_token: credentials.idToken || credentials.accessToken,
|
|
5028
|
+
// Fallback to access_token if no id_token
|
|
5029
|
+
access_token: credentials.accessToken,
|
|
5030
|
+
refresh_token: credentials.refreshToken || null,
|
|
5031
|
+
account_id: credentials.accountId || null
|
|
5032
|
+
},
|
|
5033
|
+
last_refresh: (/* @__PURE__ */ new Date()).toISOString()
|
|
5034
|
+
};
|
|
5035
|
+
return JSON.stringify(authData, null, 2);
|
|
5036
|
+
}
|
|
5037
|
+
function generateCodexConfigToml(projectPath) {
|
|
5038
|
+
const escapedPath = projectPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
5039
|
+
return `[projects."${escapedPath}"]
|
|
5040
|
+
trust_level = "trusted"
|
|
5041
|
+
`;
|
|
5042
|
+
}
|
|
5043
|
+
function generateCodexConfig(credentials, projectPath) {
|
|
5044
|
+
const files = {
|
|
5045
|
+
"auth.json": generateCodexAuthJson(credentials)
|
|
5046
|
+
};
|
|
5047
|
+
if (projectPath) {
|
|
5048
|
+
files["config.toml"] = generateCodexConfigToml(projectPath);
|
|
5049
|
+
}
|
|
5050
|
+
return files;
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
// src/agent/agent-manager.ts
|
|
5054
|
+
var import_child_process9 = require("child_process");
|
|
5055
|
+
var path10 = __toESM(require("path"));
|
|
5056
|
+
var fs9 = __toESM(require("fs"));
|
|
4954
5057
|
var os3 = __toESM(require("os"));
|
|
4955
5058
|
|
|
4956
5059
|
// src/agent/claude-config.ts
|
|
@@ -5255,11 +5358,30 @@ var AgentManager = class {
|
|
|
5255
5358
|
this.sessions = /* @__PURE__ */ new Map();
|
|
5256
5359
|
this.processes = /* @__PURE__ */ new Map();
|
|
5257
5360
|
this.initialized = false;
|
|
5258
|
-
|
|
5361
|
+
// EP1133: Lock for config file writes to prevent race conditions
|
|
5362
|
+
this.configWriteLock = Promise.resolve();
|
|
5363
|
+
this.pidDir = path10.join(os3.homedir(), ".episoda", "agent-pids");
|
|
5364
|
+
}
|
|
5365
|
+
/**
|
|
5366
|
+
* EP1133: Acquire lock for config file writes
|
|
5367
|
+
* Ensures sequential writes to prevent file corruption
|
|
5368
|
+
*/
|
|
5369
|
+
async withConfigLock(fn) {
|
|
5370
|
+
const previousLock = this.configWriteLock;
|
|
5371
|
+
let releaseLock;
|
|
5372
|
+
this.configWriteLock = new Promise((resolve3) => {
|
|
5373
|
+
releaseLock = resolve3;
|
|
5374
|
+
});
|
|
5375
|
+
try {
|
|
5376
|
+
await previousLock;
|
|
5377
|
+
return await fn();
|
|
5378
|
+
} finally {
|
|
5379
|
+
releaseLock();
|
|
5380
|
+
}
|
|
5259
5381
|
}
|
|
5260
5382
|
/**
|
|
5261
5383
|
* Initialize the agent manager
|
|
5262
|
-
* - Ensure Claude Code
|
|
5384
|
+
* - Ensure agent CLIs are available (Claude Code, Codex)
|
|
5263
5385
|
* - Clean up any orphaned processes from previous daemon runs
|
|
5264
5386
|
*/
|
|
5265
5387
|
async initialize() {
|
|
@@ -5267,8 +5389,8 @@ var AgentManager = class {
|
|
|
5267
5389
|
return;
|
|
5268
5390
|
}
|
|
5269
5391
|
console.log("[AgentManager] Initializing...");
|
|
5270
|
-
if (!
|
|
5271
|
-
|
|
5392
|
+
if (!fs9.existsSync(this.pidDir)) {
|
|
5393
|
+
fs9.mkdirSync(this.pidDir, { recursive: true });
|
|
5272
5394
|
}
|
|
5273
5395
|
await this.cleanupOrphanedProcesses();
|
|
5274
5396
|
try {
|
|
@@ -5277,6 +5399,12 @@ var AgentManager = class {
|
|
|
5277
5399
|
} catch (error) {
|
|
5278
5400
|
console.warn("[AgentManager] Claude Code not available:", error instanceof Error ? error.message : error);
|
|
5279
5401
|
}
|
|
5402
|
+
try {
|
|
5403
|
+
await ensureCodexBinary();
|
|
5404
|
+
console.log("[AgentManager] Codex CLI binary verified");
|
|
5405
|
+
} catch (error) {
|
|
5406
|
+
console.warn("[AgentManager] Codex CLI not available:", error instanceof Error ? error.message : error);
|
|
5407
|
+
}
|
|
5280
5408
|
this.initialized = true;
|
|
5281
5409
|
console.log("[AgentManager] Initialized");
|
|
5282
5410
|
}
|
|
@@ -5285,28 +5413,38 @@ var AgentManager = class {
|
|
|
5285
5413
|
*
|
|
5286
5414
|
* Creates the session record but doesn't spawn the process yet.
|
|
5287
5415
|
* The process is spawned on the first message.
|
|
5416
|
+
*
|
|
5417
|
+
* EP1133: Added provider parameter for multi-provider support
|
|
5288
5418
|
*/
|
|
5289
5419
|
async startSession(options) {
|
|
5290
|
-
const { sessionId, moduleId, moduleUid, projectPath, message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
|
|
5420
|
+
const { sessionId, moduleId, moduleUid, projectPath, provider = "claude", message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
|
|
5291
5421
|
if (this.sessions.has(sessionId)) {
|
|
5292
5422
|
return { success: false, error: "Session already exists" };
|
|
5293
5423
|
}
|
|
5294
5424
|
const oauthToken = credentials?.oauthToken;
|
|
5295
5425
|
const apiKey = credentials?.apiKey;
|
|
5296
5426
|
if (!oauthToken && !apiKey) {
|
|
5427
|
+
const providerName = provider === "claude" ? "Claude" : "Codex";
|
|
5297
5428
|
return {
|
|
5298
5429
|
success: false,
|
|
5299
|
-
error:
|
|
5430
|
+
error: `Missing credentials. Please connect your ${providerName} account in Settings.`
|
|
5300
5431
|
};
|
|
5301
5432
|
}
|
|
5302
5433
|
const authMethod = oauthToken ? "OAuth" : "API key";
|
|
5303
|
-
console.log(`[AgentManager] Using ${authMethod} authentication`);
|
|
5434
|
+
console.log(`[AgentManager] Using ${provider} provider with ${authMethod} authentication`);
|
|
5304
5435
|
try {
|
|
5305
|
-
|
|
5436
|
+
if (provider === "claude") {
|
|
5437
|
+
await ensureClaudeBinary();
|
|
5438
|
+
} else if (provider === "codex") {
|
|
5439
|
+
await ensureCodexBinary();
|
|
5440
|
+
} else {
|
|
5441
|
+
return { success: false, error: `Unknown provider: ${provider}` };
|
|
5442
|
+
}
|
|
5306
5443
|
} catch (error) {
|
|
5444
|
+
const cliName = provider === "claude" ? "Claude Code" : "Codex CLI";
|
|
5307
5445
|
return {
|
|
5308
5446
|
success: false,
|
|
5309
|
-
error: error instanceof Error ? error.message :
|
|
5447
|
+
error: error instanceof Error ? error.message : `${cliName} not available`
|
|
5310
5448
|
};
|
|
5311
5449
|
}
|
|
5312
5450
|
const session = {
|
|
@@ -5314,6 +5452,8 @@ var AgentManager = class {
|
|
|
5314
5452
|
moduleId,
|
|
5315
5453
|
moduleUid,
|
|
5316
5454
|
projectPath,
|
|
5455
|
+
provider,
|
|
5456
|
+
// EP1133: Store provider in session
|
|
5317
5457
|
credentials,
|
|
5318
5458
|
systemPrompt,
|
|
5319
5459
|
status: "starting",
|
|
@@ -5321,7 +5461,7 @@ var AgentManager = class {
|
|
|
5321
5461
|
lastActivityAt: /* @__PURE__ */ new Date()
|
|
5322
5462
|
};
|
|
5323
5463
|
this.sessions.set(sessionId, session);
|
|
5324
|
-
console.log(`[AgentManager] Started session ${sessionId} for ${moduleUid}`);
|
|
5464
|
+
console.log(`[AgentManager] Started ${provider} session ${sessionId} for ${moduleUid}`);
|
|
5325
5465
|
return this.sendMessage({
|
|
5326
5466
|
sessionId,
|
|
5327
5467
|
message,
|
|
@@ -5334,38 +5474,70 @@ var AgentManager = class {
|
|
|
5334
5474
|
/**
|
|
5335
5475
|
* Send a message to an agent session
|
|
5336
5476
|
*
|
|
5337
|
-
* Spawns a new
|
|
5338
|
-
*
|
|
5339
|
-
* Subsequent messages use --resume with the claudeSessionId for conversation continuity.
|
|
5477
|
+
* Spawns a new agent CLI process for each message.
|
|
5478
|
+
* EP1133: Supports both Claude Code and Codex CLI with provider-specific handling.
|
|
5340
5479
|
*/
|
|
5341
5480
|
async sendMessage(options) {
|
|
5342
|
-
const { sessionId, message, isFirstMessage, claudeSessionId, onChunk, onComplete, onError } = options;
|
|
5481
|
+
const { sessionId, message, isFirstMessage, agentSessionId, claudeSessionId, onChunk, onComplete, onError } = options;
|
|
5343
5482
|
const session = this.sessions.get(sessionId);
|
|
5344
5483
|
if (!session) {
|
|
5345
5484
|
return { success: false, error: "Session not found" };
|
|
5346
5485
|
}
|
|
5347
5486
|
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
5348
5487
|
session.status = "running";
|
|
5488
|
+
const resumeSessionId = agentSessionId || claudeSessionId;
|
|
5349
5489
|
try {
|
|
5350
|
-
const
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5490
|
+
const provider = session.provider || "claude";
|
|
5491
|
+
let binaryPath;
|
|
5492
|
+
let args;
|
|
5493
|
+
if (provider === "codex") {
|
|
5494
|
+
binaryPath = await ensureCodexBinary();
|
|
5495
|
+
args = [
|
|
5496
|
+
"exec",
|
|
5497
|
+
"--json",
|
|
5498
|
+
// JSONL streaming output
|
|
5499
|
+
"--skip-git-repo-check",
|
|
5500
|
+
// Allow running outside git repos
|
|
5501
|
+
"--full-auto",
|
|
5502
|
+
// Automatic execution with sandbox
|
|
5503
|
+
"--cd",
|
|
5504
|
+
session.projectPath
|
|
5505
|
+
// Working directory
|
|
5506
|
+
];
|
|
5507
|
+
if (resumeSessionId) {
|
|
5508
|
+
args.push("resume", resumeSessionId);
|
|
5509
|
+
}
|
|
5510
|
+
let fullMessage = message;
|
|
5511
|
+
if (isFirstMessage && session.systemPrompt) {
|
|
5512
|
+
fullMessage = `${session.systemPrompt}
|
|
5513
|
+
|
|
5514
|
+
---
|
|
5515
|
+
|
|
5516
|
+
${message}`;
|
|
5517
|
+
}
|
|
5518
|
+
args.push(fullMessage);
|
|
5519
|
+
} else {
|
|
5520
|
+
binaryPath = await ensureClaudeBinary();
|
|
5521
|
+
args = [
|
|
5522
|
+
"--print",
|
|
5523
|
+
// Non-interactive mode
|
|
5524
|
+
"--output-format",
|
|
5525
|
+
"stream-json",
|
|
5526
|
+
// Structured streaming output
|
|
5527
|
+
"--verbose"
|
|
5528
|
+
// Required for stream-json with --print
|
|
5529
|
+
];
|
|
5530
|
+
if (isFirstMessage && session.systemPrompt) {
|
|
5531
|
+
args.push("--system-prompt", session.systemPrompt);
|
|
5532
|
+
}
|
|
5533
|
+
if (resumeSessionId) {
|
|
5534
|
+
args.push("--resume", resumeSessionId);
|
|
5535
|
+
session.agentSessionId = resumeSessionId;
|
|
5536
|
+
session.claudeSessionId = resumeSessionId;
|
|
5537
|
+
}
|
|
5538
|
+
args.push("--", message);
|
|
5539
|
+
}
|
|
5540
|
+
console.log(`[AgentManager] Spawning ${provider} CLI for session ${sessionId}`);
|
|
5369
5541
|
console.log(`[AgentManager] Command: ${binaryPath} ${args.join(" ").substring(0, 100)}...`);
|
|
5370
5542
|
let spawnCmd;
|
|
5371
5543
|
let spawnArgs;
|
|
@@ -5378,68 +5550,110 @@ var AgentManager = class {
|
|
|
5378
5550
|
}
|
|
5379
5551
|
const useOAuth = !!session.credentials.oauthToken;
|
|
5380
5552
|
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
|
-
|
|
5553
|
+
if (provider === "codex") {
|
|
5554
|
+
await this.withConfigLock(async () => {
|
|
5555
|
+
const codexDir = path10.join(os3.homedir(), ".codex");
|
|
5556
|
+
if (!fs9.existsSync(codexDir)) {
|
|
5557
|
+
fs9.mkdirSync(codexDir, { recursive: true });
|
|
5558
|
+
}
|
|
5559
|
+
if (useOAuth) {
|
|
5560
|
+
const codexConfig = generateCodexConfig({
|
|
5561
|
+
accessToken: session.credentials.oauthToken,
|
|
5562
|
+
refreshToken: session.credentials.refreshToken,
|
|
5563
|
+
idToken: session.credentials.idToken,
|
|
5564
|
+
accountId: session.credentials.accountId,
|
|
5565
|
+
expiresAt: session.credentials.expiresAt
|
|
5566
|
+
}, session.projectPath);
|
|
5567
|
+
const authJsonPath = path10.join(codexDir, "auth.json");
|
|
5568
|
+
fs9.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
|
|
5569
|
+
console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
|
|
5570
|
+
if (codexConfig["config.toml"]) {
|
|
5571
|
+
const configTomlPath = path10.join(codexDir, "config.toml");
|
|
5572
|
+
let existingConfig = "";
|
|
5573
|
+
try {
|
|
5574
|
+
existingConfig = fs9.readFileSync(configTomlPath, "utf-8");
|
|
5575
|
+
} catch {
|
|
5576
|
+
}
|
|
5577
|
+
const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5578
|
+
const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
|
|
5579
|
+
const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
|
|
5580
|
+
if (!projectAlreadyTrusted) {
|
|
5581
|
+
fs9.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
|
|
5582
|
+
console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5585
|
+
} else if (useApiKey) {
|
|
5586
|
+
console.log("[AgentManager] EP1133: Using Codex with API key (OPENAI_API_KEY)");
|
|
5587
|
+
}
|
|
5588
|
+
});
|
|
5589
|
+
} else {
|
|
5590
|
+
await this.withConfigLock(async () => {
|
|
5591
|
+
const claudeDir = path10.join(os3.homedir(), ".claude");
|
|
5592
|
+
const credentialsPath = path10.join(claudeDir, ".credentials.json");
|
|
5593
|
+
const statsigDir = path10.join(claudeDir, "statsig");
|
|
5594
|
+
if (!fs9.existsSync(claudeDir)) {
|
|
5595
|
+
fs9.mkdirSync(claudeDir, { recursive: true });
|
|
5596
|
+
}
|
|
5597
|
+
if (!fs9.existsSync(statsigDir)) {
|
|
5598
|
+
fs9.mkdirSync(statsigDir, { recursive: true });
|
|
5599
|
+
}
|
|
5600
|
+
if (useOAuth) {
|
|
5601
|
+
const oauthCredentials = {
|
|
5602
|
+
accessToken: session.credentials.oauthToken
|
|
5603
|
+
};
|
|
5604
|
+
if (session.credentials.refreshToken) {
|
|
5605
|
+
oauthCredentials.refreshToken = session.credentials.refreshToken;
|
|
5606
|
+
}
|
|
5607
|
+
if (session.credentials.expiresAt) {
|
|
5608
|
+
oauthCredentials.expiresAt = session.credentials.expiresAt;
|
|
5609
|
+
}
|
|
5610
|
+
if (session.credentials.scopes) {
|
|
5611
|
+
oauthCredentials.scopes = session.credentials.scopes;
|
|
5612
|
+
}
|
|
5613
|
+
const credentialsContent = JSON.stringify({
|
|
5614
|
+
claudeAiOauth: oauthCredentials
|
|
5615
|
+
}, null, 2);
|
|
5616
|
+
fs9.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
|
|
5617
|
+
console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
|
|
5618
|
+
try {
|
|
5619
|
+
const claudeConfig = generateClaudeConfig();
|
|
5620
|
+
const statsigFiles = Object.keys(claudeConfig.statsig);
|
|
5621
|
+
const hasEvaluations = statsigFiles.some((f) => f.includes("cached.evaluations"));
|
|
5622
|
+
const hasStableId = statsigFiles.some((f) => f.includes("stable_id"));
|
|
5623
|
+
if (!hasEvaluations || !hasStableId) {
|
|
5624
|
+
throw new Error(`Invalid statsig config: missing required files`);
|
|
5625
|
+
}
|
|
5626
|
+
const settingsPath = path10.join(claudeDir, "settings.json");
|
|
5627
|
+
fs9.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
|
|
5628
|
+
for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
|
|
5629
|
+
const filePath = path10.join(statsigDir, filename);
|
|
5630
|
+
fs9.writeFileSync(filePath, content, { mode: 420 });
|
|
5631
|
+
}
|
|
5632
|
+
console.log("[AgentManager] Wrote Claude config files");
|
|
5633
|
+
} catch (configError) {
|
|
5634
|
+
console.warn("[AgentManager] Failed to write Claude config files:", configError instanceof Error ? configError.message : configError);
|
|
5635
|
+
}
|
|
5636
|
+
} else if (useApiKey) {
|
|
5637
|
+
console.log("[AgentManager] Using Claude with API key (ANTHROPIC_API_KEY)");
|
|
5638
|
+
}
|
|
5639
|
+
});
|
|
5640
|
+
}
|
|
5641
|
+
const envVars = {
|
|
5642
|
+
...process.env,
|
|
5643
|
+
// Disable color output for cleaner JSON parsing
|
|
5644
|
+
NO_COLOR: "1",
|
|
5645
|
+
FORCE_COLOR: "0"
|
|
5646
|
+
};
|
|
5647
|
+
if (useApiKey && session.credentials.apiKey) {
|
|
5648
|
+
if (provider === "codex") {
|
|
5649
|
+
envVars.OPENAI_API_KEY = session.credentials.apiKey;
|
|
5650
|
+
} else {
|
|
5651
|
+
envVars.ANTHROPIC_API_KEY = session.credentials.apiKey;
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
const childProcess = (0, import_child_process9.spawn)(spawnCmd, spawnArgs, {
|
|
5434
5655
|
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
|
-
},
|
|
5656
|
+
env: envVars,
|
|
5443
5657
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5444
5658
|
});
|
|
5445
5659
|
this.processes.set(sessionId, childProcess);
|
|
@@ -5464,42 +5678,76 @@ var AgentManager = class {
|
|
|
5464
5678
|
if (!line.trim()) continue;
|
|
5465
5679
|
try {
|
|
5466
5680
|
const parsed = JSON.parse(line);
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5681
|
+
if (provider === "codex") {
|
|
5682
|
+
switch (parsed.type) {
|
|
5683
|
+
case "thread.started":
|
|
5684
|
+
if (parsed.thread_id) {
|
|
5685
|
+
extractedSessionId = parsed.thread_id;
|
|
5686
|
+
session.agentSessionId = extractedSessionId;
|
|
5687
|
+
}
|
|
5688
|
+
break;
|
|
5689
|
+
case "item.completed":
|
|
5690
|
+
if (parsed.item?.type === "agent_message" && parsed.item.text) {
|
|
5691
|
+
onChunk(parsed.item.text);
|
|
5692
|
+
} else if (parsed.item?.type === "reasoning" && parsed.item.summary) {
|
|
5693
|
+
onChunk(`[Thinking: ${parsed.item.summary}]
|
|
5694
|
+
`);
|
|
5695
|
+
}
|
|
5696
|
+
break;
|
|
5697
|
+
case "item.failed":
|
|
5698
|
+
case "turn.failed":
|
|
5699
|
+
onError(parsed.item?.error || parsed.error?.message || "Codex operation failed");
|
|
5700
|
+
break;
|
|
5701
|
+
case "error":
|
|
5702
|
+
onError(parsed.message || parsed.error?.message || "Unknown error from Codex");
|
|
5703
|
+
break;
|
|
5704
|
+
// Ignore: turn.started, turn.completed (usage stats), item.started
|
|
5705
|
+
default:
|
|
5706
|
+
if (!["turn.started", "turn.completed", "item.started"].includes(parsed.type)) {
|
|
5707
|
+
console.log(`[AgentManager] Codex event: ${parsed.type}`);
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
} else {
|
|
5711
|
+
switch (parsed.type) {
|
|
5712
|
+
case "assistant":
|
|
5713
|
+
if (parsed.message?.content) {
|
|
5714
|
+
for (const block of parsed.message.content) {
|
|
5715
|
+
if (block.type === "text" && block.text) {
|
|
5716
|
+
onChunk(block.text);
|
|
5717
|
+
}
|
|
5473
5718
|
}
|
|
5474
5719
|
}
|
|
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
|
-
|
|
5720
|
+
break;
|
|
5721
|
+
case "content_block_delta":
|
|
5722
|
+
if (parsed.delta?.text) {
|
|
5723
|
+
onChunk(parsed.delta.text);
|
|
5724
|
+
}
|
|
5725
|
+
break;
|
|
5726
|
+
case "result":
|
|
5727
|
+
if (parsed.session_id) {
|
|
5728
|
+
extractedSessionId = parsed.session_id;
|
|
5729
|
+
session.agentSessionId = extractedSessionId;
|
|
5730
|
+
session.claudeSessionId = extractedSessionId;
|
|
5731
|
+
}
|
|
5732
|
+
if (parsed.result?.session_id) {
|
|
5733
|
+
extractedSessionId = parsed.result.session_id;
|
|
5734
|
+
session.agentSessionId = extractedSessionId;
|
|
5735
|
+
session.claudeSessionId = extractedSessionId;
|
|
5736
|
+
}
|
|
5737
|
+
break;
|
|
5738
|
+
case "system":
|
|
5739
|
+
if (parsed.session_id) {
|
|
5740
|
+
extractedSessionId = parsed.session_id;
|
|
5741
|
+
session.agentSessionId = extractedSessionId;
|
|
5742
|
+
session.claudeSessionId = extractedSessionId;
|
|
5743
|
+
}
|
|
5744
|
+
break;
|
|
5745
|
+
case "error":
|
|
5746
|
+
onError(parsed.error?.message || parsed.message || "Unknown error from Claude Code");
|
|
5747
|
+
break;
|
|
5748
|
+
default:
|
|
5749
|
+
console.log(`[AgentManager] Claude event: ${parsed.type}`);
|
|
5750
|
+
}
|
|
5503
5751
|
}
|
|
5504
5752
|
} catch (parseError) {
|
|
5505
5753
|
if (line.trim()) {
|
|
@@ -5513,15 +5761,15 @@ var AgentManager = class {
|
|
|
5513
5761
|
stderrBuffer += data.toString();
|
|
5514
5762
|
});
|
|
5515
5763
|
childProcess.on("exit", (code, signal) => {
|
|
5516
|
-
console.log(`[AgentManager]
|
|
5764
|
+
console.log(`[AgentManager] ${provider} CLI exited for session ${sessionId}: code=${code}, signal=${signal}`);
|
|
5517
5765
|
this.processes.delete(sessionId);
|
|
5518
5766
|
this.removePidFile(sessionId);
|
|
5519
5767
|
if (code === 0) {
|
|
5520
5768
|
session.status = "stopped";
|
|
5521
|
-
onComplete(extractedSessionId || session.claudeSessionId);
|
|
5769
|
+
onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
|
|
5522
5770
|
} else if (signal === "SIGINT") {
|
|
5523
5771
|
session.status = "stopped";
|
|
5524
|
-
onComplete(extractedSessionId || session.claudeSessionId);
|
|
5772
|
+
onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
|
|
5525
5773
|
} else {
|
|
5526
5774
|
session.status = "error";
|
|
5527
5775
|
const errorMsg = stderrBuffer.trim() || `Process exited with code ${code}`;
|
|
@@ -5546,13 +5794,13 @@ var AgentManager = class {
|
|
|
5546
5794
|
/**
|
|
5547
5795
|
* Abort an agent session (SIGINT)
|
|
5548
5796
|
*
|
|
5549
|
-
* Sends SIGINT to the
|
|
5797
|
+
* Sends SIGINT to the agent CLI process to abort the current operation.
|
|
5550
5798
|
*/
|
|
5551
5799
|
async abortSession(sessionId) {
|
|
5552
|
-
const
|
|
5553
|
-
if (
|
|
5800
|
+
const agentProcess = this.processes.get(sessionId);
|
|
5801
|
+
if (agentProcess && !agentProcess.killed) {
|
|
5554
5802
|
console.log(`[AgentManager] Aborting session ${sessionId} with SIGINT`);
|
|
5555
|
-
|
|
5803
|
+
agentProcess.kill("SIGINT");
|
|
5556
5804
|
}
|
|
5557
5805
|
const session = this.sessions.get(sessionId);
|
|
5558
5806
|
if (session) {
|
|
@@ -5566,23 +5814,23 @@ var AgentManager = class {
|
|
|
5566
5814
|
* If it doesn't exit within 5 seconds, sends SIGTERM.
|
|
5567
5815
|
*/
|
|
5568
5816
|
async stopSession(sessionId) {
|
|
5569
|
-
const
|
|
5817
|
+
const agentProcess = this.processes.get(sessionId);
|
|
5570
5818
|
const session = this.sessions.get(sessionId);
|
|
5571
5819
|
if (session) {
|
|
5572
5820
|
session.status = "stopping";
|
|
5573
5821
|
}
|
|
5574
|
-
if (
|
|
5822
|
+
if (agentProcess && !agentProcess.killed) {
|
|
5575
5823
|
console.log(`[AgentManager] Stopping session ${sessionId}`);
|
|
5576
|
-
|
|
5824
|
+
agentProcess.kill("SIGINT");
|
|
5577
5825
|
await new Promise((resolve3) => {
|
|
5578
5826
|
const timeout = setTimeout(() => {
|
|
5579
|
-
if (!
|
|
5827
|
+
if (!agentProcess.killed) {
|
|
5580
5828
|
console.log(`[AgentManager] Force killing session ${sessionId}`);
|
|
5581
|
-
|
|
5829
|
+
agentProcess.kill("SIGTERM");
|
|
5582
5830
|
}
|
|
5583
5831
|
resolve3();
|
|
5584
5832
|
}, 5e3);
|
|
5585
|
-
|
|
5833
|
+
agentProcess.once("exit", () => {
|
|
5586
5834
|
clearTimeout(timeout);
|
|
5587
5835
|
resolve3();
|
|
5588
5836
|
});
|
|
@@ -5626,14 +5874,14 @@ var AgentManager = class {
|
|
|
5626
5874
|
*/
|
|
5627
5875
|
async cleanupOrphanedProcesses() {
|
|
5628
5876
|
let cleaned = 0;
|
|
5629
|
-
if (!
|
|
5877
|
+
if (!fs9.existsSync(this.pidDir)) {
|
|
5630
5878
|
return { cleaned };
|
|
5631
5879
|
}
|
|
5632
|
-
const pidFiles =
|
|
5880
|
+
const pidFiles = fs9.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
|
|
5633
5881
|
for (const pidFile of pidFiles) {
|
|
5634
|
-
const pidPath =
|
|
5882
|
+
const pidPath = path10.join(this.pidDir, pidFile);
|
|
5635
5883
|
try {
|
|
5636
|
-
const pidStr =
|
|
5884
|
+
const pidStr = fs9.readFileSync(pidPath, "utf-8").trim();
|
|
5637
5885
|
const pid = parseInt(pidStr, 10);
|
|
5638
5886
|
if (!isNaN(pid)) {
|
|
5639
5887
|
try {
|
|
@@ -5644,7 +5892,7 @@ var AgentManager = class {
|
|
|
5644
5892
|
} catch {
|
|
5645
5893
|
}
|
|
5646
5894
|
}
|
|
5647
|
-
|
|
5895
|
+
fs9.unlinkSync(pidPath);
|
|
5648
5896
|
} catch (error) {
|
|
5649
5897
|
console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
|
|
5650
5898
|
}
|
|
@@ -5658,17 +5906,17 @@ var AgentManager = class {
|
|
|
5658
5906
|
* Write PID file for session tracking
|
|
5659
5907
|
*/
|
|
5660
5908
|
writePidFile(sessionId, pid) {
|
|
5661
|
-
const pidPath =
|
|
5662
|
-
|
|
5909
|
+
const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
|
|
5910
|
+
fs9.writeFileSync(pidPath, pid.toString());
|
|
5663
5911
|
}
|
|
5664
5912
|
/**
|
|
5665
5913
|
* Remove PID file for session
|
|
5666
5914
|
*/
|
|
5667
5915
|
removePidFile(sessionId) {
|
|
5668
|
-
const pidPath =
|
|
5916
|
+
const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
|
|
5669
5917
|
try {
|
|
5670
|
-
if (
|
|
5671
|
-
|
|
5918
|
+
if (fs9.existsSync(pidPath)) {
|
|
5919
|
+
fs9.unlinkSync(pidPath);
|
|
5672
5920
|
}
|
|
5673
5921
|
} catch {
|
|
5674
5922
|
}
|
|
@@ -5699,10 +5947,10 @@ var import_events3 = require("events");
|
|
|
5699
5947
|
var import_fs = require("fs");
|
|
5700
5948
|
|
|
5701
5949
|
// src/preview/dev-server-runner.ts
|
|
5702
|
-
var
|
|
5950
|
+
var import_child_process11 = require("child_process");
|
|
5703
5951
|
var http = __toESM(require("http"));
|
|
5704
|
-
var
|
|
5705
|
-
var
|
|
5952
|
+
var fs13 = __toESM(require("fs"));
|
|
5953
|
+
var path14 = __toESM(require("path"));
|
|
5706
5954
|
var import_events2 = require("events");
|
|
5707
5955
|
var import_core8 = __toESM(require_dist());
|
|
5708
5956
|
|
|
@@ -5727,13 +5975,13 @@ async function isPortInUse(port) {
|
|
|
5727
5975
|
}
|
|
5728
5976
|
|
|
5729
5977
|
// src/utils/env-cache.ts
|
|
5730
|
-
var
|
|
5731
|
-
var
|
|
5978
|
+
var fs11 = __toESM(require("fs"));
|
|
5979
|
+
var path12 = __toESM(require("path"));
|
|
5732
5980
|
var os4 = __toESM(require("os"));
|
|
5733
5981
|
|
|
5734
5982
|
// src/utils/env-setup.ts
|
|
5735
|
-
var
|
|
5736
|
-
var
|
|
5983
|
+
var fs10 = __toESM(require("fs"));
|
|
5984
|
+
var path11 = __toESM(require("path"));
|
|
5737
5985
|
async function fetchEnvVars(apiUrl, accessToken) {
|
|
5738
5986
|
try {
|
|
5739
5987
|
const url = `${apiUrl}/api/cli/env-vars`;
|
|
@@ -5768,29 +6016,29 @@ function writeEnvFile(targetPath, envVars) {
|
|
|
5768
6016
|
}
|
|
5769
6017
|
return `${key}=${value}`;
|
|
5770
6018
|
}).join("\n") + "\n";
|
|
5771
|
-
const envPath =
|
|
5772
|
-
|
|
6019
|
+
const envPath = path11.join(targetPath, ".env");
|
|
6020
|
+
fs10.writeFileSync(envPath, envContent, { mode: 384 });
|
|
5773
6021
|
console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
|
|
5774
6022
|
}
|
|
5775
6023
|
|
|
5776
6024
|
// src/utils/env-cache.ts
|
|
5777
6025
|
var DEFAULT_CACHE_TTL = 60;
|
|
5778
|
-
var CACHE_DIR =
|
|
6026
|
+
var CACHE_DIR = path12.join(os4.homedir(), ".episoda", "cache");
|
|
5779
6027
|
function getCacheFilePath(projectId) {
|
|
5780
|
-
return
|
|
6028
|
+
return path12.join(CACHE_DIR, `env-vars-${projectId}.json`);
|
|
5781
6029
|
}
|
|
5782
6030
|
function ensureCacheDir() {
|
|
5783
|
-
if (!
|
|
5784
|
-
|
|
6031
|
+
if (!fs11.existsSync(CACHE_DIR)) {
|
|
6032
|
+
fs11.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
|
|
5785
6033
|
}
|
|
5786
6034
|
}
|
|
5787
6035
|
function readCache(projectId) {
|
|
5788
6036
|
try {
|
|
5789
6037
|
const cacheFile = getCacheFilePath(projectId);
|
|
5790
|
-
if (!
|
|
6038
|
+
if (!fs11.existsSync(cacheFile)) {
|
|
5791
6039
|
return null;
|
|
5792
6040
|
}
|
|
5793
|
-
const content =
|
|
6041
|
+
const content = fs11.readFileSync(cacheFile, "utf-8");
|
|
5794
6042
|
const data = JSON.parse(content);
|
|
5795
6043
|
if (!data.vars || typeof data.vars !== "object" || !data.fetchedAt) {
|
|
5796
6044
|
return null;
|
|
@@ -5809,7 +6057,7 @@ function writeCache(projectId, vars) {
|
|
|
5809
6057
|
fetchedAt: Date.now(),
|
|
5810
6058
|
projectId
|
|
5811
6059
|
};
|
|
5812
|
-
|
|
6060
|
+
fs11.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
5813
6061
|
} catch (error) {
|
|
5814
6062
|
console.warn("[env-cache] Failed to write cache:", error instanceof Error ? error.message : error);
|
|
5815
6063
|
}
|
|
@@ -5878,11 +6126,11 @@ No cached values available as fallback.`
|
|
|
5878
6126
|
}
|
|
5879
6127
|
|
|
5880
6128
|
// src/preview/dev-server-registry.ts
|
|
5881
|
-
var
|
|
5882
|
-
var
|
|
6129
|
+
var fs12 = __toESM(require("fs"));
|
|
6130
|
+
var path13 = __toESM(require("path"));
|
|
5883
6131
|
var os5 = __toESM(require("os"));
|
|
5884
|
-
var
|
|
5885
|
-
var DEV_SERVER_REGISTRY_DIR =
|
|
6132
|
+
var import_child_process10 = require("child_process");
|
|
6133
|
+
var DEV_SERVER_REGISTRY_DIR = path13.join(os5.homedir(), ".episoda", "dev-servers");
|
|
5886
6134
|
var DevServerRegistry = class {
|
|
5887
6135
|
constructor() {
|
|
5888
6136
|
this.ensureRegistryDir();
|
|
@@ -5892,9 +6140,9 @@ var DevServerRegistry = class {
|
|
|
5892
6140
|
*/
|
|
5893
6141
|
ensureRegistryDir() {
|
|
5894
6142
|
try {
|
|
5895
|
-
if (!
|
|
6143
|
+
if (!fs12.existsSync(DEV_SERVER_REGISTRY_DIR)) {
|
|
5896
6144
|
console.log(`[DevServerRegistry] EP1042: Creating registry directory: ${DEV_SERVER_REGISTRY_DIR}`);
|
|
5897
|
-
|
|
6145
|
+
fs12.mkdirSync(DEV_SERVER_REGISTRY_DIR, { recursive: true });
|
|
5898
6146
|
}
|
|
5899
6147
|
} catch (error) {
|
|
5900
6148
|
console.error(`[DevServerRegistry] EP1042: Failed to create registry directory:`, error);
|
|
@@ -5905,7 +6153,7 @@ var DevServerRegistry = class {
|
|
|
5905
6153
|
* Get the registry file path for a module
|
|
5906
6154
|
*/
|
|
5907
6155
|
getEntryPath(moduleUid) {
|
|
5908
|
-
return
|
|
6156
|
+
return path13.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
|
|
5909
6157
|
}
|
|
5910
6158
|
/**
|
|
5911
6159
|
* Register a dev server
|
|
@@ -5916,7 +6164,7 @@ var DevServerRegistry = class {
|
|
|
5916
6164
|
try {
|
|
5917
6165
|
this.ensureRegistryDir();
|
|
5918
6166
|
const entryPath = this.getEntryPath(entry.moduleUid);
|
|
5919
|
-
|
|
6167
|
+
fs12.writeFileSync(entryPath, JSON.stringify(entry, null, 2), "utf8");
|
|
5920
6168
|
console.log(`[DevServerRegistry] EP1042: Registered ${entry.moduleUid} (PID ${entry.pid}, port ${entry.port})`);
|
|
5921
6169
|
} catch (error) {
|
|
5922
6170
|
console.error(`[DevServerRegistry] EP1042: Failed to register ${entry.moduleUid}:`, error);
|
|
@@ -5930,8 +6178,8 @@ var DevServerRegistry = class {
|
|
|
5930
6178
|
unregister(moduleUid) {
|
|
5931
6179
|
try {
|
|
5932
6180
|
const entryPath = this.getEntryPath(moduleUid);
|
|
5933
|
-
if (
|
|
5934
|
-
|
|
6181
|
+
if (fs12.existsSync(entryPath)) {
|
|
6182
|
+
fs12.unlinkSync(entryPath);
|
|
5935
6183
|
console.log(`[DevServerRegistry] EP1042: Unregistered ${moduleUid}`);
|
|
5936
6184
|
}
|
|
5937
6185
|
} catch (error) {
|
|
@@ -5947,10 +6195,10 @@ var DevServerRegistry = class {
|
|
|
5947
6195
|
getByModule(moduleUid) {
|
|
5948
6196
|
try {
|
|
5949
6197
|
const entryPath = this.getEntryPath(moduleUid);
|
|
5950
|
-
if (!
|
|
6198
|
+
if (!fs12.existsSync(entryPath)) {
|
|
5951
6199
|
return null;
|
|
5952
6200
|
}
|
|
5953
|
-
const content =
|
|
6201
|
+
const content = fs12.readFileSync(entryPath, "utf8");
|
|
5954
6202
|
const entry = JSON.parse(content);
|
|
5955
6203
|
if (!entry.pid || !entry.port || !entry.worktreePath) {
|
|
5956
6204
|
console.warn(`[DevServerRegistry] EP1042: Invalid entry for ${moduleUid}, removing`);
|
|
@@ -5988,7 +6236,7 @@ var DevServerRegistry = class {
|
|
|
5988
6236
|
const entries = [];
|
|
5989
6237
|
try {
|
|
5990
6238
|
this.ensureRegistryDir();
|
|
5991
|
-
const files =
|
|
6239
|
+
const files = fs12.readdirSync(DEV_SERVER_REGISTRY_DIR).filter((f) => f.endsWith(".json"));
|
|
5992
6240
|
for (const file of files) {
|
|
5993
6241
|
const moduleUid = file.replace(".json", "");
|
|
5994
6242
|
const entry = this.getByModule(moduleUid);
|
|
@@ -6036,7 +6284,7 @@ var DevServerRegistry = class {
|
|
|
6036
6284
|
*/
|
|
6037
6285
|
getProcessCwd(pid) {
|
|
6038
6286
|
try {
|
|
6039
|
-
const output = (0,
|
|
6287
|
+
const output = (0, import_child_process10.execSync)(`lsof -p ${pid} -Fn | grep ^n | grep cwd | head -1`, {
|
|
6040
6288
|
encoding: "utf8",
|
|
6041
6289
|
timeout: 5e3
|
|
6042
6290
|
}).trim();
|
|
@@ -6046,7 +6294,7 @@ var DevServerRegistry = class {
|
|
|
6046
6294
|
return null;
|
|
6047
6295
|
} catch {
|
|
6048
6296
|
try {
|
|
6049
|
-
return
|
|
6297
|
+
return fs12.readlinkSync(`/proc/${pid}/cwd`);
|
|
6050
6298
|
} catch {
|
|
6051
6299
|
return null;
|
|
6052
6300
|
}
|
|
@@ -6061,7 +6309,7 @@ var DevServerRegistry = class {
|
|
|
6061
6309
|
findProcessesInWorktree(worktreePath) {
|
|
6062
6310
|
const pids = [];
|
|
6063
6311
|
try {
|
|
6064
|
-
const output = (0,
|
|
6312
|
+
const output = (0, import_child_process10.execSync)(
|
|
6065
6313
|
`lsof -c node -c next | grep "${worktreePath}" | awk '{print $2}' | sort -u`,
|
|
6066
6314
|
{ encoding: "utf8", timeout: 5e3 }
|
|
6067
6315
|
).trim();
|
|
@@ -6122,7 +6370,7 @@ var DevServerRegistry = class {
|
|
|
6122
6370
|
*/
|
|
6123
6371
|
findProcessesOnPort(port) {
|
|
6124
6372
|
try {
|
|
6125
|
-
const output = (0,
|
|
6373
|
+
const output = (0, import_child_process10.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
6126
6374
|
if (!output) {
|
|
6127
6375
|
return [];
|
|
6128
6376
|
}
|
|
@@ -6376,7 +6624,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6376
6624
|
*/
|
|
6377
6625
|
async killProcessOnPort(port) {
|
|
6378
6626
|
try {
|
|
6379
|
-
const result = (0,
|
|
6627
|
+
const result = (0, import_child_process11.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
6380
6628
|
if (!result) {
|
|
6381
6629
|
return true;
|
|
6382
6630
|
}
|
|
@@ -6384,15 +6632,15 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6384
6632
|
console.log(`[DevServerRunner] Found ${pids.length} process(es) on port ${port}`);
|
|
6385
6633
|
for (const pid of pids) {
|
|
6386
6634
|
try {
|
|
6387
|
-
(0,
|
|
6635
|
+
(0, import_child_process11.execSync)(`kill -15 ${pid} 2>/dev/null || true`);
|
|
6388
6636
|
} catch {
|
|
6389
6637
|
}
|
|
6390
6638
|
}
|
|
6391
6639
|
await this.wait(1e3);
|
|
6392
6640
|
for (const pid of pids) {
|
|
6393
6641
|
try {
|
|
6394
|
-
(0,
|
|
6395
|
-
(0,
|
|
6642
|
+
(0, import_child_process11.execSync)(`kill -0 ${pid} 2>/dev/null`);
|
|
6643
|
+
(0, import_child_process11.execSync)(`kill -9 ${pid} 2>/dev/null || true`);
|
|
6396
6644
|
} catch {
|
|
6397
6645
|
}
|
|
6398
6646
|
}
|
|
@@ -6416,8 +6664,8 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6416
6664
|
cacheTtl: 300
|
|
6417
6665
|
});
|
|
6418
6666
|
console.log(`[DevServerRunner] Loaded ${Object.keys(result.envVars).length} env vars`);
|
|
6419
|
-
const envFilePath =
|
|
6420
|
-
if (!
|
|
6667
|
+
const envFilePath = path14.join(projectPath, ".env");
|
|
6668
|
+
if (!fs13.existsSync(envFilePath) && Object.keys(result.envVars).length > 0) {
|
|
6421
6669
|
console.log(`[DevServerRunner] Writing .env file`);
|
|
6422
6670
|
writeEnvFile(projectPath, result.envVars);
|
|
6423
6671
|
}
|
|
@@ -6441,7 +6689,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6441
6689
|
PORT: String(port),
|
|
6442
6690
|
NODE_OPTIONS: enhancedNodeOptions
|
|
6443
6691
|
};
|
|
6444
|
-
const proc = (0,
|
|
6692
|
+
const proc = (0, import_child_process11.spawn)(cmd, args, {
|
|
6445
6693
|
cwd: projectPath,
|
|
6446
6694
|
env: mergedEnv,
|
|
6447
6695
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6566,25 +6814,25 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6566
6814
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
6567
6815
|
}
|
|
6568
6816
|
getLogsDir() {
|
|
6569
|
-
const logsDir =
|
|
6570
|
-
if (!
|
|
6571
|
-
|
|
6817
|
+
const logsDir = path14.join((0, import_core8.getConfigDir)(), "logs");
|
|
6818
|
+
if (!fs13.existsSync(logsDir)) {
|
|
6819
|
+
fs13.mkdirSync(logsDir, { recursive: true });
|
|
6572
6820
|
}
|
|
6573
6821
|
return logsDir;
|
|
6574
6822
|
}
|
|
6575
6823
|
getLogFilePath(moduleUid) {
|
|
6576
|
-
return
|
|
6824
|
+
return path14.join(this.getLogsDir(), `dev-${moduleUid}.log`);
|
|
6577
6825
|
}
|
|
6578
6826
|
rotateLogIfNeeded(logPath) {
|
|
6579
6827
|
try {
|
|
6580
|
-
if (
|
|
6581
|
-
const stats =
|
|
6828
|
+
if (fs13.existsSync(logPath)) {
|
|
6829
|
+
const stats = fs13.statSync(logPath);
|
|
6582
6830
|
if (stats.size > DEV_SERVER_CONSTANTS.MAX_LOG_SIZE_BYTES) {
|
|
6583
6831
|
const backupPath = `${logPath}.1`;
|
|
6584
|
-
if (
|
|
6585
|
-
|
|
6832
|
+
if (fs13.existsSync(backupPath)) {
|
|
6833
|
+
fs13.unlinkSync(backupPath);
|
|
6586
6834
|
}
|
|
6587
|
-
|
|
6835
|
+
fs13.renameSync(logPath, backupPath);
|
|
6588
6836
|
}
|
|
6589
6837
|
}
|
|
6590
6838
|
} catch {
|
|
@@ -6595,7 +6843,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
|
|
|
6595
6843
|
try {
|
|
6596
6844
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6597
6845
|
const prefix = isError ? "ERR" : "OUT";
|
|
6598
|
-
|
|
6846
|
+
fs13.appendFileSync(logPath, `[${timestamp}] [${prefix}] ${line}
|
|
6599
6847
|
`);
|
|
6600
6848
|
} catch {
|
|
6601
6849
|
}
|
|
@@ -6623,21 +6871,21 @@ function getDevServerRunner() {
|
|
|
6623
6871
|
}
|
|
6624
6872
|
|
|
6625
6873
|
// src/utils/port-allocator.ts
|
|
6626
|
-
var
|
|
6627
|
-
var
|
|
6874
|
+
var fs14 = __toESM(require("fs"));
|
|
6875
|
+
var path15 = __toESM(require("path"));
|
|
6628
6876
|
var os6 = __toESM(require("os"));
|
|
6629
6877
|
var PORT_RANGE_START = 3100;
|
|
6630
6878
|
var PORT_RANGE_END = 3199;
|
|
6631
6879
|
var PORT_WARNING_THRESHOLD = 80;
|
|
6632
|
-
var PORTS_FILE =
|
|
6880
|
+
var PORTS_FILE = path15.join(os6.homedir(), ".episoda", "ports.json");
|
|
6633
6881
|
var portAssignments = /* @__PURE__ */ new Map();
|
|
6634
6882
|
var initialized = false;
|
|
6635
6883
|
function loadFromDisk() {
|
|
6636
6884
|
if (initialized) return;
|
|
6637
6885
|
initialized = true;
|
|
6638
6886
|
try {
|
|
6639
|
-
if (
|
|
6640
|
-
const content =
|
|
6887
|
+
if (fs14.existsSync(PORTS_FILE)) {
|
|
6888
|
+
const content = fs14.readFileSync(PORTS_FILE, "utf8");
|
|
6641
6889
|
const data = JSON.parse(content);
|
|
6642
6890
|
for (const [moduleUid, port] of Object.entries(data)) {
|
|
6643
6891
|
if (typeof port === "number" && port >= PORT_RANGE_START && port <= PORT_RANGE_END) {
|
|
@@ -6654,15 +6902,15 @@ function loadFromDisk() {
|
|
|
6654
6902
|
}
|
|
6655
6903
|
function saveToDisk() {
|
|
6656
6904
|
try {
|
|
6657
|
-
const dir =
|
|
6658
|
-
if (!
|
|
6659
|
-
|
|
6905
|
+
const dir = path15.dirname(PORTS_FILE);
|
|
6906
|
+
if (!fs14.existsSync(dir)) {
|
|
6907
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
6660
6908
|
}
|
|
6661
6909
|
const data = {};
|
|
6662
6910
|
for (const [moduleUid, port] of portAssignments) {
|
|
6663
6911
|
data[moduleUid] = port;
|
|
6664
6912
|
}
|
|
6665
|
-
|
|
6913
|
+
fs14.writeFileSync(PORTS_FILE, JSON.stringify(data, null, 2), "utf8");
|
|
6666
6914
|
} catch (error) {
|
|
6667
6915
|
console.warn(`[PortAllocator] EP1042: Failed to save ports.json:`, error);
|
|
6668
6916
|
}
|
|
@@ -7138,10 +7386,10 @@ function getPreviewManager() {
|
|
|
7138
7386
|
}
|
|
7139
7387
|
|
|
7140
7388
|
// src/utils/dev-server.ts
|
|
7141
|
-
var
|
|
7389
|
+
var import_child_process12 = require("child_process");
|
|
7142
7390
|
var import_core9 = __toESM(require_dist());
|
|
7143
|
-
var
|
|
7144
|
-
var
|
|
7391
|
+
var fs15 = __toESM(require("fs"));
|
|
7392
|
+
var path16 = __toESM(require("path"));
|
|
7145
7393
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
7146
7394
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
7147
7395
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -7149,26 +7397,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
|
7149
7397
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
7150
7398
|
var activeServers = /* @__PURE__ */ new Map();
|
|
7151
7399
|
function getLogsDir() {
|
|
7152
|
-
const logsDir =
|
|
7153
|
-
if (!
|
|
7154
|
-
|
|
7400
|
+
const logsDir = path16.join((0, import_core9.getConfigDir)(), "logs");
|
|
7401
|
+
if (!fs15.existsSync(logsDir)) {
|
|
7402
|
+
fs15.mkdirSync(logsDir, { recursive: true });
|
|
7155
7403
|
}
|
|
7156
7404
|
return logsDir;
|
|
7157
7405
|
}
|
|
7158
7406
|
function getLogFilePath(moduleUid) {
|
|
7159
|
-
return
|
|
7407
|
+
return path16.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
7160
7408
|
}
|
|
7161
7409
|
function rotateLogIfNeeded(logPath) {
|
|
7162
7410
|
try {
|
|
7163
|
-
if (
|
|
7164
|
-
const stats =
|
|
7411
|
+
if (fs15.existsSync(logPath)) {
|
|
7412
|
+
const stats = fs15.statSync(logPath);
|
|
7165
7413
|
if (stats.size > MAX_LOG_SIZE_BYTES) {
|
|
7166
7414
|
const backupPath = `${logPath}.1`;
|
|
7167
|
-
if (
|
|
7168
|
-
|
|
7415
|
+
if (fs15.existsSync(backupPath)) {
|
|
7416
|
+
fs15.unlinkSync(backupPath);
|
|
7169
7417
|
}
|
|
7170
|
-
|
|
7171
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
7418
|
+
fs15.renameSync(logPath, backupPath);
|
|
7419
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path16.basename(logPath)}`);
|
|
7172
7420
|
}
|
|
7173
7421
|
}
|
|
7174
7422
|
} catch (error) {
|
|
@@ -7181,13 +7429,13 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
7181
7429
|
const prefix = isError ? "ERR" : "OUT";
|
|
7182
7430
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
7183
7431
|
`;
|
|
7184
|
-
|
|
7432
|
+
fs15.appendFileSync(logPath, logLine);
|
|
7185
7433
|
} catch {
|
|
7186
7434
|
}
|
|
7187
7435
|
}
|
|
7188
7436
|
async function killProcessOnPort(port) {
|
|
7189
7437
|
try {
|
|
7190
|
-
const result = (0,
|
|
7438
|
+
const result = (0, import_child_process12.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
|
|
7191
7439
|
if (!result) {
|
|
7192
7440
|
console.log(`[DevServer] EP929: No process found on port ${port}`);
|
|
7193
7441
|
return true;
|
|
@@ -7196,7 +7444,7 @@ async function killProcessOnPort(port) {
|
|
|
7196
7444
|
console.log(`[DevServer] EP929: Found ${pids.length} process(es) on port ${port}: ${pids.join(", ")}`);
|
|
7197
7445
|
for (const pid of pids) {
|
|
7198
7446
|
try {
|
|
7199
|
-
(0,
|
|
7447
|
+
(0, import_child_process12.execSync)(`kill -15 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
|
|
7200
7448
|
console.log(`[DevServer] EP929: Sent SIGTERM to PID ${pid}`);
|
|
7201
7449
|
} catch {
|
|
7202
7450
|
}
|
|
@@ -7204,8 +7452,8 @@ async function killProcessOnPort(port) {
|
|
|
7204
7452
|
await new Promise((resolve3) => setTimeout(resolve3, 1e3));
|
|
7205
7453
|
for (const pid of pids) {
|
|
7206
7454
|
try {
|
|
7207
|
-
(0,
|
|
7208
|
-
(0,
|
|
7455
|
+
(0, import_child_process12.execSync)(`kill -0 ${pid} 2>/dev/null`, { encoding: "utf8" });
|
|
7456
|
+
(0, import_child_process12.execSync)(`kill -9 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
|
|
7209
7457
|
console.log(`[DevServer] EP929: Force killed PID ${pid}`);
|
|
7210
7458
|
} catch {
|
|
7211
7459
|
}
|
|
@@ -7256,7 +7504,7 @@ function spawnDevServerProcess(projectPath, port, moduleUid, logPath, customComm
|
|
|
7256
7504
|
if (injectedCount > 0) {
|
|
7257
7505
|
console.log(`[DevServer] EP998: Injecting ${injectedCount} env vars from database`);
|
|
7258
7506
|
}
|
|
7259
|
-
const devProcess = (0,
|
|
7507
|
+
const devProcess = (0, import_child_process12.spawn)(cmd, args, {
|
|
7260
7508
|
cwd: projectPath,
|
|
7261
7509
|
env: mergedEnv,
|
|
7262
7510
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7360,8 +7608,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
7360
7608
|
});
|
|
7361
7609
|
injectedEnvVars = result.envVars;
|
|
7362
7610
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
7363
|
-
const envFilePath =
|
|
7364
|
-
if (!
|
|
7611
|
+
const envFilePath = path16.join(projectPath, ".env");
|
|
7612
|
+
if (!fs15.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
7365
7613
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
7366
7614
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
7367
7615
|
}
|
|
@@ -7467,8 +7715,8 @@ function getDevServerStatus() {
|
|
|
7467
7715
|
}
|
|
7468
7716
|
|
|
7469
7717
|
// src/daemon/worktree-manager.ts
|
|
7470
|
-
var
|
|
7471
|
-
var
|
|
7718
|
+
var fs16 = __toESM(require("fs"));
|
|
7719
|
+
var path17 = __toESM(require("path"));
|
|
7472
7720
|
var import_core10 = __toESM(require_dist());
|
|
7473
7721
|
function validateModuleUid(moduleUid) {
|
|
7474
7722
|
if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
|
|
@@ -7492,8 +7740,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7492
7740
|
// ============================================================
|
|
7493
7741
|
this.lockPath = "";
|
|
7494
7742
|
this.projectRoot = projectRoot;
|
|
7495
|
-
this.bareRepoPath =
|
|
7496
|
-
this.configPath =
|
|
7743
|
+
this.bareRepoPath = path17.join(projectRoot, ".bare");
|
|
7744
|
+
this.configPath = path17.join(projectRoot, ".episoda", "config.json");
|
|
7497
7745
|
this.gitExecutor = new import_core10.GitExecutor();
|
|
7498
7746
|
}
|
|
7499
7747
|
/**
|
|
@@ -7504,13 +7752,13 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7504
7752
|
*/
|
|
7505
7753
|
async initialize() {
|
|
7506
7754
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
7507
|
-
if (!
|
|
7755
|
+
if (!fs16.existsSync(this.bareRepoPath)) {
|
|
7508
7756
|
if (debug) {
|
|
7509
7757
|
console.log(`[WorktreeManager] initialize: .bare not found at ${this.bareRepoPath}`);
|
|
7510
7758
|
}
|
|
7511
7759
|
return false;
|
|
7512
7760
|
}
|
|
7513
|
-
if (!
|
|
7761
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
7514
7762
|
if (debug) {
|
|
7515
7763
|
console.log(`[WorktreeManager] initialize: config not found at ${this.configPath}`);
|
|
7516
7764
|
}
|
|
@@ -7542,10 +7790,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7542
7790
|
*/
|
|
7543
7791
|
async ensureFetchRefspecConfigured() {
|
|
7544
7792
|
try {
|
|
7545
|
-
const { execSync:
|
|
7793
|
+
const { execSync: execSync10 } = require("child_process");
|
|
7546
7794
|
let fetchRefspec = null;
|
|
7547
7795
|
try {
|
|
7548
|
-
fetchRefspec =
|
|
7796
|
+
fetchRefspec = execSync10("git config --get remote.origin.fetch", {
|
|
7549
7797
|
cwd: this.bareRepoPath,
|
|
7550
7798
|
encoding: "utf-8",
|
|
7551
7799
|
timeout: 5e3
|
|
@@ -7554,7 +7802,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7554
7802
|
}
|
|
7555
7803
|
if (!fetchRefspec) {
|
|
7556
7804
|
console.log("[WorktreeManager] EP1014: Configuring missing fetch refspec for bare repo");
|
|
7557
|
-
|
|
7805
|
+
execSync10('git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', {
|
|
7558
7806
|
cwd: this.bareRepoPath,
|
|
7559
7807
|
timeout: 5e3
|
|
7560
7808
|
});
|
|
@@ -7569,8 +7817,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7569
7817
|
*/
|
|
7570
7818
|
static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
|
|
7571
7819
|
const manager = new _WorktreeManager(projectRoot);
|
|
7572
|
-
const episodaDir =
|
|
7573
|
-
|
|
7820
|
+
const episodaDir = path17.join(projectRoot, ".episoda");
|
|
7821
|
+
fs16.mkdirSync(episodaDir, { recursive: true });
|
|
7574
7822
|
const cloneResult = await manager.gitExecutor.execute({
|
|
7575
7823
|
action: "clone_bare",
|
|
7576
7824
|
url: repoUrl,
|
|
@@ -7601,7 +7849,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7601
7849
|
error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
|
|
7602
7850
|
};
|
|
7603
7851
|
}
|
|
7604
|
-
const worktreePath =
|
|
7852
|
+
const worktreePath = path17.join(this.projectRoot, moduleUid);
|
|
7605
7853
|
const lockAcquired = await this.acquireLock();
|
|
7606
7854
|
if (!lockAcquired) {
|
|
7607
7855
|
return {
|
|
@@ -7792,7 +8040,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7792
8040
|
let prunedCount = 0;
|
|
7793
8041
|
await this.updateConfigSafe((config) => {
|
|
7794
8042
|
const initialCount = config.worktrees.length;
|
|
7795
|
-
config.worktrees = config.worktrees.filter((w) =>
|
|
8043
|
+
config.worktrees = config.worktrees.filter((w) => fs16.existsSync(w.worktreePath));
|
|
7796
8044
|
prunedCount = initialCount - config.worktrees.length;
|
|
7797
8045
|
return config;
|
|
7798
8046
|
});
|
|
@@ -7873,16 +8121,16 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7873
8121
|
const retryInterval = 50;
|
|
7874
8122
|
while (Date.now() - startTime < timeoutMs) {
|
|
7875
8123
|
try {
|
|
7876
|
-
|
|
8124
|
+
fs16.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
7877
8125
|
return true;
|
|
7878
8126
|
} catch (err) {
|
|
7879
8127
|
if (err.code === "EEXIST") {
|
|
7880
8128
|
try {
|
|
7881
|
-
const stats =
|
|
8129
|
+
const stats = fs16.statSync(lockPath);
|
|
7882
8130
|
const lockAge = Date.now() - stats.mtimeMs;
|
|
7883
8131
|
if (lockAge > 3e4) {
|
|
7884
8132
|
try {
|
|
7885
|
-
const lockContent =
|
|
8133
|
+
const lockContent = fs16.readFileSync(lockPath, "utf-8").trim();
|
|
7886
8134
|
const lockPid = parseInt(lockContent, 10);
|
|
7887
8135
|
if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
|
|
7888
8136
|
await new Promise((resolve3) => setTimeout(resolve3, retryInterval));
|
|
@@ -7891,7 +8139,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7891
8139
|
} catch {
|
|
7892
8140
|
}
|
|
7893
8141
|
try {
|
|
7894
|
-
|
|
8142
|
+
fs16.unlinkSync(lockPath);
|
|
7895
8143
|
} catch {
|
|
7896
8144
|
}
|
|
7897
8145
|
continue;
|
|
@@ -7912,7 +8160,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7912
8160
|
*/
|
|
7913
8161
|
releaseLock() {
|
|
7914
8162
|
try {
|
|
7915
|
-
|
|
8163
|
+
fs16.unlinkSync(this.getLockPath());
|
|
7916
8164
|
} catch {
|
|
7917
8165
|
}
|
|
7918
8166
|
}
|
|
@@ -7936,11 +8184,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7936
8184
|
// Turborepo cache
|
|
7937
8185
|
];
|
|
7938
8186
|
for (const cacheDir of cacheDirs) {
|
|
7939
|
-
const cachePath =
|
|
8187
|
+
const cachePath = path17.join(worktreePath, cacheDir);
|
|
7940
8188
|
try {
|
|
7941
|
-
if (
|
|
8189
|
+
if (fs16.existsSync(cachePath)) {
|
|
7942
8190
|
console.log(`[WorktreeManager] EP1070: Cleaning build cache: ${cacheDir}`);
|
|
7943
|
-
|
|
8191
|
+
fs16.rmSync(cachePath, { recursive: true, force: true });
|
|
7944
8192
|
}
|
|
7945
8193
|
} catch (error) {
|
|
7946
8194
|
console.warn(`[WorktreeManager] EP1070: Failed to clean ${cacheDir} (non-blocking):`, error.message);
|
|
@@ -7949,10 +8197,10 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7949
8197
|
}
|
|
7950
8198
|
readConfig() {
|
|
7951
8199
|
try {
|
|
7952
|
-
if (!
|
|
8200
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
7953
8201
|
return null;
|
|
7954
8202
|
}
|
|
7955
|
-
const content =
|
|
8203
|
+
const content = fs16.readFileSync(this.configPath, "utf-8");
|
|
7956
8204
|
return JSON.parse(content);
|
|
7957
8205
|
} catch (error) {
|
|
7958
8206
|
if (error instanceof SyntaxError) {
|
|
@@ -7966,11 +8214,11 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7966
8214
|
}
|
|
7967
8215
|
writeConfig(config) {
|
|
7968
8216
|
try {
|
|
7969
|
-
const dir =
|
|
7970
|
-
if (!
|
|
7971
|
-
|
|
8217
|
+
const dir = path17.dirname(this.configPath);
|
|
8218
|
+
if (!fs16.existsSync(dir)) {
|
|
8219
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
7972
8220
|
}
|
|
7973
|
-
|
|
8221
|
+
fs16.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
7974
8222
|
} catch (error) {
|
|
7975
8223
|
console.error("[WorktreeManager] Failed to write config:", error);
|
|
7976
8224
|
throw error;
|
|
@@ -8051,14 +8299,14 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8051
8299
|
}
|
|
8052
8300
|
try {
|
|
8053
8301
|
for (const file of files) {
|
|
8054
|
-
const srcPath =
|
|
8055
|
-
const destPath =
|
|
8056
|
-
if (
|
|
8057
|
-
const destDir =
|
|
8058
|
-
if (!
|
|
8059
|
-
|
|
8060
|
-
}
|
|
8061
|
-
|
|
8302
|
+
const srcPath = path17.join(mainWorktree.worktreePath, file);
|
|
8303
|
+
const destPath = path17.join(worktree.worktreePath, file);
|
|
8304
|
+
if (fs16.existsSync(srcPath)) {
|
|
8305
|
+
const destDir = path17.dirname(destPath);
|
|
8306
|
+
if (!fs16.existsSync(destDir)) {
|
|
8307
|
+
fs16.mkdirSync(destDir, { recursive: true });
|
|
8308
|
+
}
|
|
8309
|
+
fs16.copyFileSync(srcPath, destPath);
|
|
8062
8310
|
console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`);
|
|
8063
8311
|
} else {
|
|
8064
8312
|
console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`);
|
|
@@ -8089,8 +8337,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8089
8337
|
console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
|
|
8090
8338
|
console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
|
|
8091
8339
|
try {
|
|
8092
|
-
const { execSync:
|
|
8093
|
-
|
|
8340
|
+
const { execSync: execSync10 } = require("child_process");
|
|
8341
|
+
execSync10(script, {
|
|
8094
8342
|
cwd: worktree.worktreePath,
|
|
8095
8343
|
stdio: "inherit",
|
|
8096
8344
|
timeout: TIMEOUT_MINUTES * 60 * 1e3,
|
|
@@ -8124,8 +8372,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8124
8372
|
console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
|
|
8125
8373
|
console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
|
|
8126
8374
|
try {
|
|
8127
|
-
const { execSync:
|
|
8128
|
-
|
|
8375
|
+
const { execSync: execSync10 } = require("child_process");
|
|
8376
|
+
execSync10(script, {
|
|
8129
8377
|
cwd: worktree.worktreePath,
|
|
8130
8378
|
stdio: "inherit",
|
|
8131
8379
|
timeout: TIMEOUT_MINUTES * 60 * 1e3,
|
|
@@ -8141,7 +8389,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8141
8389
|
}
|
|
8142
8390
|
};
|
|
8143
8391
|
function getEpisodaRoot() {
|
|
8144
|
-
return process.env.EPISODA_ROOT ||
|
|
8392
|
+
return process.env.EPISODA_ROOT || path17.join(require("os").homedir(), "episoda");
|
|
8145
8393
|
}
|
|
8146
8394
|
async function isWorktreeProject(projectRoot) {
|
|
8147
8395
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
@@ -8156,7 +8404,7 @@ async function isWorktreeProject(projectRoot) {
|
|
|
8156
8404
|
return result;
|
|
8157
8405
|
}
|
|
8158
8406
|
async function findProjectRoot(startPath) {
|
|
8159
|
-
let current =
|
|
8407
|
+
let current = path17.resolve(startPath);
|
|
8160
8408
|
const episodaRoot = getEpisodaRoot();
|
|
8161
8409
|
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
8162
8410
|
if (debug) {
|
|
@@ -8169,14 +8417,14 @@ async function findProjectRoot(startPath) {
|
|
|
8169
8417
|
return null;
|
|
8170
8418
|
}
|
|
8171
8419
|
for (let i = 0; i < 10; i++) {
|
|
8172
|
-
const bareDir =
|
|
8173
|
-
const episodaDir =
|
|
8420
|
+
const bareDir = path17.join(current, ".bare");
|
|
8421
|
+
const episodaDir = path17.join(current, ".episoda");
|
|
8174
8422
|
if (debug) {
|
|
8175
|
-
const bareExists =
|
|
8176
|
-
const episodaExists =
|
|
8423
|
+
const bareExists = fs16.existsSync(bareDir);
|
|
8424
|
+
const episodaExists = fs16.existsSync(episodaDir);
|
|
8177
8425
|
console.log(`[WorktreeManager] findProjectRoot: checking ${current} (.bare=${bareExists}, .episoda=${episodaExists})`);
|
|
8178
8426
|
}
|
|
8179
|
-
if (
|
|
8427
|
+
if (fs16.existsSync(bareDir) && fs16.existsSync(episodaDir)) {
|
|
8180
8428
|
if (await isWorktreeProject(current)) {
|
|
8181
8429
|
if (debug) {
|
|
8182
8430
|
console.log(`[WorktreeManager] findProjectRoot: found valid project at ${current}`);
|
|
@@ -8184,7 +8432,7 @@ async function findProjectRoot(startPath) {
|
|
|
8184
8432
|
return current;
|
|
8185
8433
|
}
|
|
8186
8434
|
}
|
|
8187
|
-
const parent =
|
|
8435
|
+
const parent = path17.dirname(current);
|
|
8188
8436
|
if (parent === current) {
|
|
8189
8437
|
break;
|
|
8190
8438
|
}
|
|
@@ -8197,19 +8445,19 @@ async function findProjectRoot(startPath) {
|
|
|
8197
8445
|
}
|
|
8198
8446
|
|
|
8199
8447
|
// src/utils/worktree.ts
|
|
8200
|
-
var
|
|
8201
|
-
var
|
|
8448
|
+
var path18 = __toESM(require("path"));
|
|
8449
|
+
var fs17 = __toESM(require("fs"));
|
|
8202
8450
|
var os7 = __toESM(require("os"));
|
|
8203
8451
|
var import_core11 = __toESM(require_dist());
|
|
8204
8452
|
function getEpisodaRoot2() {
|
|
8205
|
-
return process.env.EPISODA_ROOT ||
|
|
8453
|
+
return process.env.EPISODA_ROOT || path18.join(os7.homedir(), "episoda");
|
|
8206
8454
|
}
|
|
8207
8455
|
function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
|
|
8208
8456
|
const root = getEpisodaRoot2();
|
|
8209
|
-
const worktreePath =
|
|
8457
|
+
const worktreePath = path18.join(root, workspaceSlug, projectSlug, moduleUid);
|
|
8210
8458
|
return {
|
|
8211
8459
|
path: worktreePath,
|
|
8212
|
-
exists:
|
|
8460
|
+
exists: fs17.existsSync(worktreePath),
|
|
8213
8461
|
moduleUid
|
|
8214
8462
|
};
|
|
8215
8463
|
}
|
|
@@ -8223,61 +8471,61 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
8223
8471
|
}
|
|
8224
8472
|
|
|
8225
8473
|
// src/framework-detector.ts
|
|
8226
|
-
var
|
|
8227
|
-
var
|
|
8474
|
+
var fs18 = __toESM(require("fs"));
|
|
8475
|
+
var path19 = __toESM(require("path"));
|
|
8228
8476
|
function getInstallCommand(cwd) {
|
|
8229
|
-
if (
|
|
8477
|
+
if (fs18.existsSync(path19.join(cwd, "bun.lockb"))) {
|
|
8230
8478
|
return {
|
|
8231
8479
|
command: ["bun", "install"],
|
|
8232
8480
|
description: "Installing dependencies with bun",
|
|
8233
8481
|
detectedFrom: "bun.lockb"
|
|
8234
8482
|
};
|
|
8235
8483
|
}
|
|
8236
|
-
if (
|
|
8484
|
+
if (fs18.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) {
|
|
8237
8485
|
return {
|
|
8238
8486
|
command: ["pnpm", "install"],
|
|
8239
8487
|
description: "Installing dependencies with pnpm",
|
|
8240
8488
|
detectedFrom: "pnpm-lock.yaml"
|
|
8241
8489
|
};
|
|
8242
8490
|
}
|
|
8243
|
-
if (
|
|
8491
|
+
if (fs18.existsSync(path19.join(cwd, "yarn.lock"))) {
|
|
8244
8492
|
return {
|
|
8245
8493
|
command: ["yarn", "install"],
|
|
8246
8494
|
description: "Installing dependencies with yarn",
|
|
8247
8495
|
detectedFrom: "yarn.lock"
|
|
8248
8496
|
};
|
|
8249
8497
|
}
|
|
8250
|
-
if (
|
|
8498
|
+
if (fs18.existsSync(path19.join(cwd, "package-lock.json"))) {
|
|
8251
8499
|
return {
|
|
8252
8500
|
command: ["npm", "ci"],
|
|
8253
8501
|
description: "Installing dependencies with npm ci",
|
|
8254
8502
|
detectedFrom: "package-lock.json"
|
|
8255
8503
|
};
|
|
8256
8504
|
}
|
|
8257
|
-
if (
|
|
8505
|
+
if (fs18.existsSync(path19.join(cwd, "package.json"))) {
|
|
8258
8506
|
return {
|
|
8259
8507
|
command: ["npm", "install"],
|
|
8260
8508
|
description: "Installing dependencies with npm",
|
|
8261
8509
|
detectedFrom: "package.json"
|
|
8262
8510
|
};
|
|
8263
8511
|
}
|
|
8264
|
-
if (
|
|
8512
|
+
if (fs18.existsSync(path19.join(cwd, "Pipfile.lock")) || fs18.existsSync(path19.join(cwd, "Pipfile"))) {
|
|
8265
8513
|
return {
|
|
8266
8514
|
command: ["pipenv", "install"],
|
|
8267
8515
|
description: "Installing dependencies with pipenv",
|
|
8268
|
-
detectedFrom:
|
|
8516
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
|
|
8269
8517
|
};
|
|
8270
8518
|
}
|
|
8271
|
-
if (
|
|
8519
|
+
if (fs18.existsSync(path19.join(cwd, "poetry.lock"))) {
|
|
8272
8520
|
return {
|
|
8273
8521
|
command: ["poetry", "install"],
|
|
8274
8522
|
description: "Installing dependencies with poetry",
|
|
8275
8523
|
detectedFrom: "poetry.lock"
|
|
8276
8524
|
};
|
|
8277
8525
|
}
|
|
8278
|
-
if (
|
|
8279
|
-
const pyprojectPath =
|
|
8280
|
-
const content =
|
|
8526
|
+
if (fs18.existsSync(path19.join(cwd, "pyproject.toml"))) {
|
|
8527
|
+
const pyprojectPath = path19.join(cwd, "pyproject.toml");
|
|
8528
|
+
const content = fs18.readFileSync(pyprojectPath, "utf-8");
|
|
8281
8529
|
if (content.includes("[tool.poetry]")) {
|
|
8282
8530
|
return {
|
|
8283
8531
|
command: ["poetry", "install"],
|
|
@@ -8286,41 +8534,41 @@ function getInstallCommand(cwd) {
|
|
|
8286
8534
|
};
|
|
8287
8535
|
}
|
|
8288
8536
|
}
|
|
8289
|
-
if (
|
|
8537
|
+
if (fs18.existsSync(path19.join(cwd, "requirements.txt"))) {
|
|
8290
8538
|
return {
|
|
8291
8539
|
command: ["pip", "install", "-r", "requirements.txt"],
|
|
8292
8540
|
description: "Installing dependencies with pip",
|
|
8293
8541
|
detectedFrom: "requirements.txt"
|
|
8294
8542
|
};
|
|
8295
8543
|
}
|
|
8296
|
-
if (
|
|
8544
|
+
if (fs18.existsSync(path19.join(cwd, "Gemfile.lock")) || fs18.existsSync(path19.join(cwd, "Gemfile"))) {
|
|
8297
8545
|
return {
|
|
8298
8546
|
command: ["bundle", "install"],
|
|
8299
8547
|
description: "Installing dependencies with bundler",
|
|
8300
|
-
detectedFrom:
|
|
8548
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
|
|
8301
8549
|
};
|
|
8302
8550
|
}
|
|
8303
|
-
if (
|
|
8551
|
+
if (fs18.existsSync(path19.join(cwd, "go.sum")) || fs18.existsSync(path19.join(cwd, "go.mod"))) {
|
|
8304
8552
|
return {
|
|
8305
8553
|
command: ["go", "mod", "download"],
|
|
8306
8554
|
description: "Downloading Go modules",
|
|
8307
|
-
detectedFrom:
|
|
8555
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
|
|
8308
8556
|
};
|
|
8309
8557
|
}
|
|
8310
|
-
if (
|
|
8558
|
+
if (fs18.existsSync(path19.join(cwd, "Cargo.lock")) || fs18.existsSync(path19.join(cwd, "Cargo.toml"))) {
|
|
8311
8559
|
return {
|
|
8312
8560
|
command: ["cargo", "build"],
|
|
8313
8561
|
description: "Building Rust project (downloads dependencies)",
|
|
8314
|
-
detectedFrom:
|
|
8562
|
+
detectedFrom: fs18.existsSync(path19.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
|
|
8315
8563
|
};
|
|
8316
8564
|
}
|
|
8317
8565
|
return null;
|
|
8318
8566
|
}
|
|
8319
8567
|
|
|
8320
8568
|
// src/daemon/daemon-process.ts
|
|
8321
|
-
var
|
|
8569
|
+
var fs19 = __toESM(require("fs"));
|
|
8322
8570
|
var os8 = __toESM(require("os"));
|
|
8323
|
-
var
|
|
8571
|
+
var path20 = __toESM(require("path"));
|
|
8324
8572
|
var packageJson = require_package();
|
|
8325
8573
|
async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
8326
8574
|
const now = Date.now();
|
|
@@ -8834,7 +9082,7 @@ var Daemon = class _Daemon {
|
|
|
8834
9082
|
client.updateActivity();
|
|
8835
9083
|
try {
|
|
8836
9084
|
const gitCmd = message.command;
|
|
8837
|
-
const bareRepoPath =
|
|
9085
|
+
const bareRepoPath = path20.join(projectPath, ".bare");
|
|
8838
9086
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
8839
9087
|
if (gitCmd.worktreePath) {
|
|
8840
9088
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -9108,6 +9356,8 @@ var Daemon = class _Daemon {
|
|
|
9108
9356
|
moduleId: cmd.moduleId,
|
|
9109
9357
|
moduleUid: cmd.moduleUid,
|
|
9110
9358
|
projectPath: agentWorkingDir,
|
|
9359
|
+
provider: cmd.provider || "claude",
|
|
9360
|
+
// EP1133: Multi-provider support
|
|
9111
9361
|
message: cmd.message,
|
|
9112
9362
|
credentials: cmd.credentials,
|
|
9113
9363
|
systemPrompt: cmd.systemPrompt,
|
|
@@ -9125,7 +9375,9 @@ var Daemon = class _Daemon {
|
|
|
9125
9375
|
sessionId: cmd.sessionId,
|
|
9126
9376
|
message: cmd.message,
|
|
9127
9377
|
isFirstMessage: false,
|
|
9378
|
+
agentSessionId: cmd.agentSessionId || cmd.claudeSessionId,
|
|
9128
9379
|
claudeSessionId: cmd.claudeSessionId,
|
|
9380
|
+
// Backward compat
|
|
9129
9381
|
...callbacks
|
|
9130
9382
|
});
|
|
9131
9383
|
result = {
|
|
@@ -9359,8 +9611,8 @@ var Daemon = class _Daemon {
|
|
|
9359
9611
|
let daemonPid;
|
|
9360
9612
|
try {
|
|
9361
9613
|
const pidPath = getPidFilePath();
|
|
9362
|
-
if (
|
|
9363
|
-
const pidStr =
|
|
9614
|
+
if (fs19.existsSync(pidPath)) {
|
|
9615
|
+
const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
|
|
9364
9616
|
daemonPid = parseInt(pidStr, 10);
|
|
9365
9617
|
}
|
|
9366
9618
|
} catch (pidError) {
|
|
@@ -9441,28 +9693,28 @@ var Daemon = class _Daemon {
|
|
|
9441
9693
|
* - workDir: The directory to run git commands in (cwd)
|
|
9442
9694
|
*/
|
|
9443
9695
|
getGitDirs(projectPath) {
|
|
9444
|
-
const bareDir =
|
|
9445
|
-
const gitPath =
|
|
9446
|
-
if (
|
|
9696
|
+
const bareDir = path20.join(projectPath, ".bare");
|
|
9697
|
+
const gitPath = path20.join(projectPath, ".git");
|
|
9698
|
+
if (fs19.existsSync(bareDir) && fs19.statSync(bareDir).isDirectory()) {
|
|
9447
9699
|
return { gitDir: bareDir, workDir: projectPath };
|
|
9448
9700
|
}
|
|
9449
|
-
if (
|
|
9701
|
+
if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isDirectory()) {
|
|
9450
9702
|
return { gitDir: null, workDir: projectPath };
|
|
9451
9703
|
}
|
|
9452
|
-
if (
|
|
9704
|
+
if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isFile()) {
|
|
9453
9705
|
return { gitDir: null, workDir: projectPath };
|
|
9454
9706
|
}
|
|
9455
|
-
const entries =
|
|
9707
|
+
const entries = fs19.readdirSync(projectPath, { withFileTypes: true });
|
|
9456
9708
|
for (const entry of entries) {
|
|
9457
9709
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
9458
|
-
const worktreePath =
|
|
9459
|
-
const worktreeGit =
|
|
9460
|
-
if (
|
|
9710
|
+
const worktreePath = path20.join(projectPath, entry.name);
|
|
9711
|
+
const worktreeGit = path20.join(worktreePath, ".git");
|
|
9712
|
+
if (fs19.existsSync(worktreeGit)) {
|
|
9461
9713
|
return { gitDir: null, workDir: worktreePath };
|
|
9462
9714
|
}
|
|
9463
9715
|
}
|
|
9464
9716
|
}
|
|
9465
|
-
if (
|
|
9717
|
+
if (fs19.existsSync(bareDir)) {
|
|
9466
9718
|
return { gitDir: bareDir, workDir: projectPath };
|
|
9467
9719
|
}
|
|
9468
9720
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -9480,31 +9732,31 @@ var Daemon = class _Daemon {
|
|
|
9480
9732
|
*/
|
|
9481
9733
|
async configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
|
|
9482
9734
|
try {
|
|
9483
|
-
const { execSync:
|
|
9735
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
9484
9736
|
const { gitDir, workDir } = this.getGitDirs(projectPath);
|
|
9485
9737
|
const gitCmd = gitDir ? `git --git-dir="${gitDir}"` : "git";
|
|
9486
|
-
|
|
9738
|
+
execSync10(`${gitCmd} config episoda.userId ${userId}`, {
|
|
9487
9739
|
cwd: workDir,
|
|
9488
9740
|
encoding: "utf8",
|
|
9489
9741
|
stdio: "pipe"
|
|
9490
9742
|
});
|
|
9491
|
-
|
|
9743
|
+
execSync10(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
|
|
9492
9744
|
cwd: workDir,
|
|
9493
9745
|
encoding: "utf8",
|
|
9494
9746
|
stdio: "pipe"
|
|
9495
9747
|
});
|
|
9496
|
-
|
|
9748
|
+
execSync10(`${gitCmd} config episoda.machineId ${machineId}`, {
|
|
9497
9749
|
cwd: workDir,
|
|
9498
9750
|
encoding: "utf8",
|
|
9499
9751
|
stdio: "pipe"
|
|
9500
9752
|
});
|
|
9501
|
-
|
|
9753
|
+
execSync10(`${gitCmd} config episoda.projectId ${projectId}`, {
|
|
9502
9754
|
cwd: workDir,
|
|
9503
9755
|
encoding: "utf8",
|
|
9504
9756
|
stdio: "pipe"
|
|
9505
9757
|
});
|
|
9506
9758
|
if (machineUuid) {
|
|
9507
|
-
|
|
9759
|
+
execSync10(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
|
|
9508
9760
|
cwd: workDir,
|
|
9509
9761
|
encoding: "utf8",
|
|
9510
9762
|
stdio: "pipe"
|
|
@@ -9526,24 +9778,24 @@ var Daemon = class _Daemon {
|
|
|
9526
9778
|
async installGitHooks(projectPath) {
|
|
9527
9779
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
9528
9780
|
let hooksDir;
|
|
9529
|
-
const bareHooksDir =
|
|
9530
|
-
const gitHooksDir =
|
|
9531
|
-
if (
|
|
9781
|
+
const bareHooksDir = path20.join(projectPath, ".bare", "hooks");
|
|
9782
|
+
const gitHooksDir = path20.join(projectPath, ".git", "hooks");
|
|
9783
|
+
if (fs19.existsSync(bareHooksDir)) {
|
|
9532
9784
|
hooksDir = bareHooksDir;
|
|
9533
|
-
} else if (
|
|
9785
|
+
} else if (fs19.existsSync(gitHooksDir) && fs19.statSync(path20.join(projectPath, ".git")).isDirectory()) {
|
|
9534
9786
|
hooksDir = gitHooksDir;
|
|
9535
9787
|
} else {
|
|
9536
|
-
const parentBareHooks =
|
|
9537
|
-
if (
|
|
9788
|
+
const parentBareHooks = path20.join(projectPath, "..", ".bare", "hooks");
|
|
9789
|
+
if (fs19.existsSync(parentBareHooks)) {
|
|
9538
9790
|
hooksDir = parentBareHooks;
|
|
9539
9791
|
} else {
|
|
9540
9792
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
9541
9793
|
return;
|
|
9542
9794
|
}
|
|
9543
9795
|
}
|
|
9544
|
-
if (!
|
|
9796
|
+
if (!fs19.existsSync(hooksDir)) {
|
|
9545
9797
|
try {
|
|
9546
|
-
|
|
9798
|
+
fs19.mkdirSync(hooksDir, { recursive: true });
|
|
9547
9799
|
} catch (error) {
|
|
9548
9800
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
9549
9801
|
return;
|
|
@@ -9551,20 +9803,20 @@ var Daemon = class _Daemon {
|
|
|
9551
9803
|
}
|
|
9552
9804
|
for (const hookName of hooks) {
|
|
9553
9805
|
try {
|
|
9554
|
-
const hookPath =
|
|
9555
|
-
const bundledHookPath =
|
|
9556
|
-
if (!
|
|
9806
|
+
const hookPath = path20.join(hooksDir, hookName);
|
|
9807
|
+
const bundledHookPath = path20.join(__dirname, "..", "hooks", hookName);
|
|
9808
|
+
if (!fs19.existsSync(bundledHookPath)) {
|
|
9557
9809
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
9558
9810
|
continue;
|
|
9559
9811
|
}
|
|
9560
|
-
const hookContent =
|
|
9561
|
-
if (
|
|
9562
|
-
const existingContent =
|
|
9812
|
+
const hookContent = fs19.readFileSync(bundledHookPath, "utf-8");
|
|
9813
|
+
if (fs19.existsSync(hookPath)) {
|
|
9814
|
+
const existingContent = fs19.readFileSync(hookPath, "utf-8");
|
|
9563
9815
|
if (existingContent === hookContent) {
|
|
9564
9816
|
continue;
|
|
9565
9817
|
}
|
|
9566
9818
|
}
|
|
9567
|
-
|
|
9819
|
+
fs19.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
9568
9820
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
9569
9821
|
} catch (error) {
|
|
9570
9822
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -10031,8 +10283,8 @@ var Daemon = class _Daemon {
|
|
|
10031
10283
|
console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
10032
10284
|
console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
|
|
10033
10285
|
try {
|
|
10034
|
-
const { execSync:
|
|
10035
|
-
|
|
10286
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10287
|
+
execSync10(installCmd.command.join(" "), {
|
|
10036
10288
|
cwd: worktreePath,
|
|
10037
10289
|
stdio: "inherit",
|
|
10038
10290
|
timeout: 10 * 60 * 1e3,
|
|
@@ -10085,8 +10337,8 @@ var Daemon = class _Daemon {
|
|
|
10085
10337
|
console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
10086
10338
|
console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
|
|
10087
10339
|
try {
|
|
10088
|
-
const { execSync:
|
|
10089
|
-
|
|
10340
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10341
|
+
execSync10(installCmd.command.join(" "), {
|
|
10090
10342
|
cwd: worktreePath,
|
|
10091
10343
|
stdio: "inherit",
|
|
10092
10344
|
timeout: 10 * 60 * 1e3,
|
|
@@ -10670,8 +10922,8 @@ var Daemon = class _Daemon {
|
|
|
10670
10922
|
await this.shutdown();
|
|
10671
10923
|
try {
|
|
10672
10924
|
const pidPath = getPidFilePath();
|
|
10673
|
-
if (
|
|
10674
|
-
|
|
10925
|
+
if (fs19.existsSync(pidPath)) {
|
|
10926
|
+
fs19.unlinkSync(pidPath);
|
|
10675
10927
|
console.log("[Daemon] PID file cleaned up");
|
|
10676
10928
|
}
|
|
10677
10929
|
} catch (error) {
|