episoda 0.2.121 → 0.2.123

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.
@@ -2815,7 +2815,7 @@ var require_package = __commonJS({
2815
2815
  "package.json"(exports2, module2) {
2816
2816
  module2.exports = {
2817
2817
  name: "episoda",
2818
- version: "0.2.120",
2818
+ version: "0.2.122",
2819
2819
  description: "CLI tool for Episoda local development workflow orchestration",
2820
2820
  main: "dist/index.js",
2821
2821
  types: "dist/index.d.ts",
@@ -8959,23 +8959,32 @@ If changes are needed, explain what needs to be done.`;
8959
8959
  const mcpServersToRegister = this.getMcpServersForSession(session);
8960
8960
  if (mcpServersToRegister.length > 0) {
8961
8961
  const mcpConfig = {};
8962
+ const baseEnv = {};
8963
+ for (const [key, value] of Object.entries(process.env)) {
8964
+ if (value !== void 0) {
8965
+ baseEnv[key] = value;
8966
+ }
8967
+ }
8962
8968
  const sessionToken = process.env.EPISODA_ACCESS_TOKEN || process.env.EPISODA_SESSION_TOKEN || "";
8963
8969
  const devEnvId = process.env.EPISODA_CONTAINER_ID || process.env.EPISODA_MACHINE_ID || "";
8964
8970
  const episodaMcpEnv = {
8971
+ ...baseEnv,
8965
8972
  EPISODA_API_URL: process.env.EPISODA_API_URL || "https://episoda.dev",
8966
8973
  EPISODA_SESSION_TOKEN: sessionToken,
8967
8974
  MODULE_UID: session.moduleUid,
8968
8975
  DEV_ENVIRONMENT_ID: devEnvId
8969
8976
  };
8977
+ const githubMcpEnv = {
8978
+ ...baseEnv,
8979
+ GITHUB_PERSONAL_ACCESS_TOKEN: session.credentials.githubToken || ""
8980
+ };
8970
8981
  for (const mcpServer of mcpServersToRegister) {
8971
8982
  const parts = mcpServer.command.split(" ");
8972
8983
  if (mcpServer.name === "github") {
8973
8984
  mcpConfig[mcpServer.name] = {
8974
8985
  command: parts[0],
8975
8986
  args: parts.slice(1),
8976
- env: {
8977
- GITHUB_PERSONAL_ACCESS_TOKEN: session.credentials.githubToken || ""
8978
- }
8987
+ env: githubMcpEnv
8979
8988
  };
8980
8989
  } else {
8981
8990
  mcpConfig[mcpServer.name] = {
@@ -8984,7 +8993,7 @@ If changes are needed, explain what needs to be done.`;
8984
8993
  env: episodaMcpEnv
8985
8994
  };
8986
8995
  }
8987
- console.log(`[AgentManager] EP1253: Registering MCP server: ${mcpServer.name} with explicit env`);
8996
+ console.log(`[AgentManager] EP1253: Registering MCP server: ${mcpServer.name} with full env (${Object.keys(mcpServer.name === "github" ? githubMcpEnv : episodaMcpEnv).length} vars)`);
8988
8997
  }
8989
8998
  const mcpConfigJson = JSON.stringify({ mcpServers: mcpConfig });
8990
8999
  args.push("--mcp-config", mcpConfigJson);
@@ -9005,12 +9014,12 @@ If changes are needed, explain what needs to be done.`;
9005
9014
  }
9006
9015
  const useOAuth = !!session.credentials.oauthToken;
9007
9016
  const useApiKey = !useOAuth && !!session.credentials.apiKey;
9017
+ const sessionCodexDir = path20.join(os6.homedir(), ".codex", "sessions", sessionId);
9018
+ const sessionClaudeDir = path20.join(os6.homedir(), ".claude", "sessions", sessionId);
9008
9019
  if (provider === "codex") {
9009
9020
  await this.withConfigLock(async () => {
9010
- const codexDir = path20.join(os6.homedir(), ".codex");
9011
- if (!fs19.existsSync(codexDir)) {
9012
- fs19.mkdirSync(codexDir, { recursive: true });
9013
- }
9021
+ fs19.mkdirSync(sessionCodexDir, { recursive: true });
9022
+ console.log(`[AgentManager] EP1260: Created session-specific Codex dir: ${sessionCodexDir}`);
9014
9023
  if (useOAuth) {
9015
9024
  const codexConfig = generateCodexConfig({
9016
9025
  accessToken: session.credentials.oauthToken,
@@ -9019,23 +9028,13 @@ If changes are needed, explain what needs to be done.`;
9019
9028
  accountId: session.credentials.accountId,
9020
9029
  expiresAt: session.credentials.expiresAt
9021
9030
  }, session.projectPath);
9022
- const authJsonPath = path20.join(codexDir, "auth.json");
9031
+ const authJsonPath = path20.join(sessionCodexDir, "auth.json");
9023
9032
  fs19.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
9024
- console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
9033
+ console.log(`[AgentManager] EP1260: Wrote Codex auth.json to ${authJsonPath}`);
9025
9034
  if (codexConfig["config.toml"]) {
9026
- const configTomlPath = path20.join(codexDir, "config.toml");
9027
- let existingConfig = "";
9028
- try {
9029
- existingConfig = fs19.readFileSync(configTomlPath, "utf-8");
9030
- } catch {
9031
- }
9032
- const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9033
- const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
9034
- const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
9035
- if (!projectAlreadyTrusted) {
9036
- fs19.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
9037
- console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
9038
- }
9035
+ const configTomlPath = path20.join(sessionCodexDir, "config.toml");
9036
+ fs19.writeFileSync(configTomlPath, codexConfig["config.toml"], { mode: 420 });
9037
+ console.log(`[AgentManager] EP1260: Wrote Codex config.toml to ${configTomlPath}`);
9039
9038
  }
9040
9039
  } else if (useApiKey) {
9041
9040
  console.log("[AgentManager] EP1133: Using Codex with API key (OPENAI_API_KEY)");
@@ -9043,16 +9042,11 @@ If changes are needed, explain what needs to be done.`;
9043
9042
  });
9044
9043
  } else {
9045
9044
  await this.withConfigLock(async () => {
9046
- const claudeDir = path20.join(os6.homedir(), ".claude");
9047
- const credentialsPath = path20.join(claudeDir, ".credentials.json");
9048
- const statsigDir = path20.join(claudeDir, "statsig");
9049
- if (!fs19.existsSync(claudeDir)) {
9050
- fs19.mkdirSync(claudeDir, { recursive: true });
9051
- }
9052
- if (!fs19.existsSync(statsigDir)) {
9053
- fs19.mkdirSync(statsigDir, { recursive: true });
9054
- }
9045
+ const statsigDir = path20.join(sessionClaudeDir, "statsig");
9046
+ fs19.mkdirSync(statsigDir, { recursive: true });
9047
+ console.log(`[AgentManager] EP1260: Created session-specific Claude dir: ${sessionClaudeDir}`);
9055
9048
  if (useOAuth) {
9049
+ const credentialsPath = path20.join(sessionClaudeDir, ".credentials.json");
9056
9050
  const oauthCredentials = {
9057
9051
  accessToken: session.credentials.oauthToken
9058
9052
  };
@@ -9069,7 +9063,7 @@ If changes are needed, explain what needs to be done.`;
9069
9063
  claudeAiOauth: oauthCredentials
9070
9064
  }, null, 2);
9071
9065
  fs19.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
9072
- console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
9066
+ console.log(`[AgentManager] EP1260: Wrote OAuth credentials to ${credentialsPath}`);
9073
9067
  try {
9074
9068
  const claudeConfig = generateClaudeConfig({
9075
9069
  githubToken: session.credentials.githubToken
@@ -9080,13 +9074,13 @@ If changes are needed, explain what needs to be done.`;
9080
9074
  if (!hasEvaluations || !hasStableId) {
9081
9075
  throw new Error(`Invalid statsig config: missing required files`);
9082
9076
  }
9083
- const settingsPath = path20.join(claudeDir, "settings.json");
9077
+ const settingsPath = path20.join(sessionClaudeDir, "settings.json");
9084
9078
  fs19.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
9085
9079
  for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
9086
9080
  const filePath = path20.join(statsigDir, filename);
9087
9081
  fs19.writeFileSync(filePath, content, { mode: 420 });
9088
9082
  }
9089
- console.log("[AgentManager] Wrote Claude config files");
9083
+ console.log("[AgentManager] EP1260: Wrote Claude config files to session directory");
9090
9084
  } catch (configError) {
9091
9085
  console.warn("[AgentManager] Failed to write Claude config files:", configError instanceof Error ? configError.message : configError);
9092
9086
  }
@@ -9104,6 +9098,13 @@ If changes are needed, explain what needs to be done.`;
9104
9098
  // MCP servers inherit these from the Claude Code process
9105
9099
  MODULE_UID: session.moduleUid
9106
9100
  };
9101
+ if (provider === "codex") {
9102
+ envVars.CODEX_HOME = sessionCodexDir;
9103
+ console.log(`[AgentManager] EP1260: Set CODEX_HOME=${sessionCodexDir}`);
9104
+ } else {
9105
+ envVars.CLAUDE_CONFIG_DIR = sessionClaudeDir;
9106
+ console.log(`[AgentManager] EP1260: Set CLAUDE_CONFIG_DIR=${sessionClaudeDir}`);
9107
+ }
9107
9108
  if (!envVars.DEV_ENVIRONMENT_ID) {
9108
9109
  envVars.DEV_ENVIRONMENT_ID = process.env.EPISODA_CONTAINER_ID || process.env.EPISODA_MACHINE_ID || "";
9109
9110
  if (envVars.DEV_ENVIRONMENT_ID) {
@@ -9352,6 +9353,25 @@ If changes are needed, explain what needs to be done.`;
9352
9353
  this.removePidFile(sessionId);
9353
9354
  console.log(`[AgentManager] Session ${sessionId} stopped`);
9354
9355
  }
9356
+ /**
9357
+ * EP1260: Clean up session-specific credential directories
9358
+ * Call this when a session is truly ended (not just paused/stopped)
9359
+ * e.g., module state transition, explicit session close, etc.
9360
+ */
9361
+ cleanupSessionCredentials(sessionId, provider) {
9362
+ const providers = provider ? [provider] : ["claude", "codex"];
9363
+ for (const p of providers) {
9364
+ const sessionDir = p === "codex" ? path20.join(os6.homedir(), ".codex", "sessions", sessionId) : path20.join(os6.homedir(), ".claude", "sessions", sessionId);
9365
+ try {
9366
+ if (fs19.existsSync(sessionDir)) {
9367
+ fs19.rmSync(sessionDir, { recursive: true, force: true });
9368
+ console.log(`[AgentManager] EP1260: Cleaned up ${p} session credentials: ${sessionDir}`);
9369
+ }
9370
+ } catch (error) {
9371
+ console.warn(`[AgentManager] EP1260: Failed to clean up ${p} session credentials:`, error);
9372
+ }
9373
+ }
9374
+ }
9355
9375
  /**
9356
9376
  * Stop all active sessions
9357
9377
  */