@rk0429/agentic-relay 2.0.6 → 2.0.7

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.
Files changed (2) hide show
  1. package/dist/relay.mjs +186 -50
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -332,7 +332,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } fr
332
332
  import { join as join6 } from "path";
333
333
  import { homedir as homedir5 } from "os";
334
334
  import { nanoid as nanoid2 } from "nanoid";
335
- function getRelayHome2() {
335
+ function getRelayHome3() {
336
336
  return process.env["RELAY_HOME"] ?? join6(homedir5(), ".relay");
337
337
  }
338
338
  function isValidEventType(type) {
@@ -347,7 +347,7 @@ var init_agent_event_store = __esm({
347
347
  maxEvents: 1e3,
348
348
  ttlMs: 36e5,
349
349
  backend: "jsonl",
350
- sessionDir: join6(getRelayHome2(), "sessions"),
350
+ sessionDir: join6(getRelayHome3(), "sessions"),
351
351
  eventsFileName: "events.jsonl"
352
352
  };
353
353
  AgentEventStore = class {
@@ -8442,7 +8442,7 @@ var init_server = __esm({
8442
8442
  this.agentEventStore
8443
8443
  );
8444
8444
  this.server = new McpServer(
8445
- { name: "agentic-relay", version: "2.0.6" },
8445
+ { name: "agentic-relay", version: "2.0.7" },
8446
8446
  createMcpServerOptions()
8447
8447
  );
8448
8448
  this.registerTools(this.server);
@@ -8956,7 +8956,7 @@ var init_server = __esm({
8956
8956
  sessionIdGenerator: () => randomUUID()
8957
8957
  });
8958
8958
  const server = new McpServer(
8959
- { name: "agentic-relay", version: "2.0.6" },
8959
+ { name: "agentic-relay", version: "2.0.7" },
8960
8960
  createMcpServerOptions()
8961
8961
  );
8962
8962
  this.registerTools(server, childRelayContext);
@@ -10969,7 +10969,7 @@ var ClaudeAdapter = class extends BaseAdapter {
10969
10969
 
10970
10970
  // src/adapters/codex-adapter.ts
10971
10971
  init_logger();
10972
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10972
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, mkdtemp, copyFile } from "fs/promises";
10973
10973
  import { homedir as homedir2 } from "os";
10974
10974
  import { join as join2, dirname as dirname2 } from "path";
10975
10975
  async function loadCodexSDK() {
@@ -11066,6 +11066,12 @@ function toTOMLString(value) {
11066
11066
  function toTOMLStringArray(values) {
11067
11067
  return `[${values.map(toTOMLString).join(", ")}]`;
11068
11068
  }
11069
+ function toTOMLInlineTable(values) {
11070
+ return `{ ${Object.entries(values).map(([key, value]) => `${toTOMLString(key)} = ${toTOMLString(value)}`).join(", ")} }`;
11071
+ }
11072
+ function toTOMLTableKey(key) {
11073
+ return /^[A-Za-z0-9_-]+$/.test(key) ? key : toTOMLString(key);
11074
+ }
11069
11075
  function generateMcpServersTOML(servers) {
11070
11076
  const parts = [];
11071
11077
  for (const server of servers) {
@@ -11079,11 +11085,88 @@ function generateMcpServersTOML(servers) {
11079
11085
  }
11080
11086
  return parts.join("\n");
11081
11087
  }
11088
+ function generateChildMcpServersTOML(servers) {
11089
+ const parts = [];
11090
+ for (const [name, server] of Object.entries(servers)) {
11091
+ parts.push(`[mcp_servers.${toTOMLTableKey(name)}]`);
11092
+ if ("url" in server) {
11093
+ parts.push(`url = ${toTOMLString(server.url)}`);
11094
+ if (server.headers && Object.keys(server.headers).length > 0) {
11095
+ parts.push(`headers = ${toTOMLInlineTable(server.headers)}`);
11096
+ }
11097
+ } else {
11098
+ parts.push(`command = ${toTOMLString(server.command)}`);
11099
+ if (server.args && server.args.length > 0) {
11100
+ parts.push(`args = ${toTOMLStringArray(server.args)}`);
11101
+ }
11102
+ if (server.env && Object.keys(server.env).length > 0) {
11103
+ parts.push(`env = ${toTOMLInlineTable(server.env)}`);
11104
+ }
11105
+ }
11106
+ parts.push("");
11107
+ }
11108
+ return parts.join("\n");
11109
+ }
11110
+ function rewriteCodexConfigWithMcpServers(existingContent, mcpServers) {
11111
+ const parsed = parseTOMLMcpServers(existingContent);
11112
+ const preamble = parsed.preambleLines.join("\n").replace(/\n+$/, "");
11113
+ const mcpSection = generateChildMcpServersTOML(mcpServers);
11114
+ const postamble = parsed.postambleLines.join("\n").replace(/^\n+/, "");
11115
+ const parts = [];
11116
+ if (preamble) parts.push(preamble);
11117
+ if (mcpSection) parts.push(mcpSection);
11118
+ if (postamble) parts.push(postamble);
11119
+ let output = parts.join("\n\n");
11120
+ if (!output.endsWith("\n")) output += "\n";
11121
+ return output;
11122
+ }
11123
+ function getRelayHome() {
11124
+ return process.env["RELAY_HOME"] ?? join2(homedir2(), ".relay");
11125
+ }
11126
+ function encodeNativeSessionId(threadId, codexHome) {
11127
+ if (!codexHome) {
11128
+ return threadId;
11129
+ }
11130
+ return `${threadId}@@${Buffer.from(codexHome, "utf8").toString("base64url")}`;
11131
+ }
11132
+ function decodeNativeSessionId(nativeSessionId) {
11133
+ const separatorIndex = nativeSessionId.lastIndexOf("@@");
11134
+ if (separatorIndex === -1) {
11135
+ return { threadId: nativeSessionId };
11136
+ }
11137
+ const threadId = nativeSessionId.slice(0, separatorIndex);
11138
+ const encodedHome = nativeSessionId.slice(separatorIndex + 2);
11139
+ try {
11140
+ const codexHome = Buffer.from(encodedHome, "base64url").toString("utf8");
11141
+ return { threadId, codexHome };
11142
+ } catch {
11143
+ return { threadId: nativeSessionId };
11144
+ }
11145
+ }
11146
+ function isFileNotFoundError(error) {
11147
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
11148
+ }
11149
+ async function copyIfExists(from, to) {
11150
+ try {
11151
+ await copyFile(from, to);
11152
+ } catch (error) {
11153
+ if (isFileNotFoundError(error)) {
11154
+ return;
11155
+ }
11156
+ throw error;
11157
+ }
11158
+ }
11082
11159
  var CodexAdapter = class extends BaseAdapter {
11083
11160
  id = "codex";
11084
11161
  command = "codex";
11162
+ getCodexHomePath() {
11163
+ return join2(homedir2(), ".codex");
11164
+ }
11165
+ getIsolatedCodexHomesPath() {
11166
+ return join2(getRelayHome(), "codex-homes");
11167
+ }
11085
11168
  getConfigPath() {
11086
- return join2(homedir2(), ".codex", "config.toml");
11169
+ return join2(this.getCodexHomePath(), "config.toml");
11087
11170
  }
11088
11171
  async checkAuthStatus() {
11089
11172
  const result = await this.processManager.execute(this.command, [
@@ -11096,46 +11179,74 @@ var CodexAdapter = class extends BaseAdapter {
11096
11179
  ...!authenticated ? { message: "codex authentication not configured" } : {}
11097
11180
  };
11098
11181
  }
11099
- buildCodexMcpConfig(mcpServers) {
11100
- if (!mcpServers || Object.keys(mcpServers).length === 0) {
11101
- return void 0;
11102
- }
11103
- const configServers = {};
11104
- for (const [name, server] of Object.entries(mcpServers)) {
11105
- if ("url" in server) {
11106
- configServers[name] = {
11107
- url: server.url,
11108
- ...server.headers ? { headers: server.headers } : {}
11109
- };
11110
- } else {
11111
- configServers[name] = {
11112
- command: server.command,
11113
- ...server.args ? { args: server.args } : {},
11114
- ...server.env ? { env: server.env } : {}
11115
- };
11182
+ async prepareIsolatedCodexHome(mcpServers) {
11183
+ const codexHomesDir = this.getIsolatedCodexHomesPath();
11184
+ await mkdir2(codexHomesDir, { recursive: true });
11185
+ const isolatedCodexHome = await mkdtemp(join2(codexHomesDir, "codex-"));
11186
+ const realCodexHome = this.getCodexHomePath();
11187
+ await copyIfExists(
11188
+ join2(realCodexHome, "auth.json"),
11189
+ join2(isolatedCodexHome, "auth.json")
11190
+ );
11191
+ await copyIfExists(
11192
+ join2(realCodexHome, "version.json"),
11193
+ join2(isolatedCodexHome, "version.json")
11194
+ );
11195
+ await copyIfExists(
11196
+ join2(realCodexHome, "internal_storage.json"),
11197
+ join2(isolatedCodexHome, "internal_storage.json")
11198
+ );
11199
+ await copyIfExists(
11200
+ join2(realCodexHome, ".codex-global-state.json"),
11201
+ join2(isolatedCodexHome, ".codex-global-state.json")
11202
+ );
11203
+ let existingConfig = "";
11204
+ try {
11205
+ existingConfig = await readFile2(this.getConfigPath(), "utf-8");
11206
+ } catch (error) {
11207
+ if (!isFileNotFoundError(error)) {
11208
+ throw error;
11116
11209
  }
11117
11210
  }
11118
- return { mcp_servers: configServers };
11211
+ const isolatedConfig = rewriteCodexConfigWithMcpServers(
11212
+ existingConfig,
11213
+ mcpServers
11214
+ );
11215
+ await writeFile2(join2(isolatedCodexHome, "config.toml"), isolatedConfig, {
11216
+ mode: 384
11217
+ });
11218
+ return isolatedCodexHome;
11119
11219
  }
11120
- buildCodexOptions(flags) {
11220
+ async buildCodexOptions(flags) {
11121
11221
  const options = {};
11122
- if (flags.mcpContext) {
11123
- const env = {};
11124
- for (const [key, value] of Object.entries(process.env)) {
11125
- if (value !== void 0) {
11126
- env[key] = value;
11222
+ let env;
11223
+ let isolatedCodexHome;
11224
+ const ensureEnv = () => {
11225
+ if (!env) {
11226
+ env = {};
11227
+ for (const [key, value] of Object.entries(process.env)) {
11228
+ if (value !== void 0) {
11229
+ env[key] = value;
11230
+ }
11127
11231
  }
11128
11232
  }
11129
- env.RELAY_TRACE_ID = flags.mcpContext.traceId;
11130
- env.RELAY_PARENT_SESSION_ID = flags.mcpContext.parentSessionId;
11131
- env.RELAY_DEPTH = String(flags.mcpContext.depth);
11132
- options.env = env;
11233
+ return env;
11234
+ };
11235
+ if (flags.mcpContext) {
11236
+ const resolvedEnv = ensureEnv();
11237
+ resolvedEnv.RELAY_TRACE_ID = flags.mcpContext.traceId;
11238
+ resolvedEnv.RELAY_PARENT_SESSION_ID = flags.mcpContext.parentSessionId;
11239
+ resolvedEnv.RELAY_DEPTH = String(flags.mcpContext.depth);
11133
11240
  }
11134
- const config = this.buildCodexMcpConfig(flags.mcpServers);
11135
- if (config) {
11136
- options.config = config;
11241
+ if (flags.mcpServers && Object.keys(flags.mcpServers).length > 0) {
11242
+ const resolvedEnv = ensureEnv();
11243
+ isolatedCodexHome = await this.prepareIsolatedCodexHome(flags.mcpServers);
11244
+ resolvedEnv.CODEX_HOME = isolatedCodexHome;
11245
+ }
11246
+ if (env) {
11247
+ options.env = env;
11137
11248
  }
11138
- return options;
11249
+ return { options, isolatedCodexHome };
11139
11250
  }
11140
11251
  mapFlags(flags) {
11141
11252
  const args = mapCommonToNative("codex", flags);
@@ -11189,7 +11300,8 @@ ${prompt}`;
11189
11300
  );
11190
11301
  try {
11191
11302
  const { Codex } = await loadCodexSDK();
11192
- const codex = new Codex(this.buildCodexOptions(flags));
11303
+ const { options, isolatedCodexHome } = await this.buildCodexOptions(flags);
11304
+ const codex = new Codex(options);
11193
11305
  const thread = codex.startThread({
11194
11306
  ...flags.model ? { model: flags.model } : {},
11195
11307
  workingDirectory: process.cwd(),
@@ -11200,7 +11312,12 @@ ${prompt}`;
11200
11312
  exitCode: 0,
11201
11313
  stdout: result.finalResponse,
11202
11314
  stderr: "",
11203
- ...thread.id ? { nativeSessionId: thread.id } : {}
11315
+ ...thread.id ? {
11316
+ nativeSessionId: encodeNativeSessionId(
11317
+ thread.id,
11318
+ isolatedCodexHome
11319
+ )
11320
+ } : {}
11204
11321
  };
11205
11322
  } catch (error) {
11206
11323
  return {
@@ -11221,7 +11338,8 @@ ${prompt}`;
11221
11338
  );
11222
11339
  try {
11223
11340
  const { Codex } = await loadCodexSDK();
11224
- const codex = new Codex(this.buildCodexOptions(flags));
11341
+ const { options, isolatedCodexHome } = await this.buildCodexOptions(flags);
11342
+ const codex = new Codex(options);
11225
11343
  const thread = codex.startThread({
11226
11344
  ...flags.model ? { model: flags.model } : {},
11227
11345
  workingDirectory: process.cwd(),
@@ -11282,14 +11400,20 @@ ${prompt}`;
11282
11400
  yield {
11283
11401
  type: "done",
11284
11402
  result: { exitCode: 0, stdout: finalResponse, stderr: "" },
11285
- nativeSessionId: threadId ?? thread.id ?? void 0
11403
+ nativeSessionId: threadId || thread.id ? encodeNativeSessionId(
11404
+ threadId ?? thread.id ?? "",
11405
+ isolatedCodexHome
11406
+ ) : void 0
11286
11407
  };
11287
11408
  } else if (event.type === "turn.failed") {
11288
11409
  const errorMessage = event.error.message ?? "Turn failed";
11289
11410
  yield {
11290
11411
  type: "done",
11291
11412
  result: { exitCode: 1, stdout: "", stderr: errorMessage },
11292
- nativeSessionId: threadId ?? thread.id ?? void 0
11413
+ nativeSessionId: threadId || thread.id ? encodeNativeSessionId(
11414
+ threadId ?? thread.id ?? "",
11415
+ isolatedCodexHome
11416
+ ) : void 0
11293
11417
  };
11294
11418
  } else if (event.type === "error") {
11295
11419
  yield {
@@ -11310,8 +11434,20 @@ ${prompt}`;
11310
11434
  async continueSession(nativeSessionId, prompt) {
11311
11435
  try {
11312
11436
  const { Codex } = await loadCodexSDK();
11313
- const codex = new Codex();
11314
- const thread = codex.resumeThread(nativeSessionId, {
11437
+ const { threadId, codexHome } = decodeNativeSessionId(nativeSessionId);
11438
+ const codex = new Codex(
11439
+ codexHome ? {
11440
+ env: {
11441
+ ...Object.fromEntries(
11442
+ Object.entries(process.env).filter(
11443
+ ([, value]) => value !== void 0
11444
+ )
11445
+ ),
11446
+ CODEX_HOME: codexHome
11447
+ }
11448
+ } : {}
11449
+ );
11450
+ const thread = codex.resumeThread(threadId, {
11315
11451
  workingDirectory: process.cwd(),
11316
11452
  approvalPolicy: "never"
11317
11453
  });
@@ -11650,7 +11786,7 @@ import { readFile as readFile4, writeFile as writeFile4, readdir, mkdir as mkdir
11650
11786
  import { join as join4 } from "path";
11651
11787
  import { homedir as homedir4 } from "os";
11652
11788
  import { nanoid } from "nanoid";
11653
- function getRelayHome() {
11789
+ function getRelayHome2() {
11654
11790
  return process.env["RELAY_HOME"] ?? join4(homedir4(), ".relay");
11655
11791
  }
11656
11792
  function getSessionsDir(relayHome2) {
@@ -11690,7 +11826,7 @@ var SessionManager = class _SessionManager {
11690
11826
  ]);
11691
11827
  sessionsDir;
11692
11828
  constructor(sessionsDir) {
11693
- this.sessionsDir = sessionsDir ?? getSessionsDir(getRelayHome());
11829
+ this.sessionsDir = sessionsDir ?? getSessionsDir(getRelayHome2());
11694
11830
  }
11695
11831
  getSessionsDir() {
11696
11832
  return this.sessionsDir;
@@ -13433,7 +13569,7 @@ function createMCPCommand(configManager2, registry2, sessionManager2, hooksEngin
13433
13569
  responseOutputDir,
13434
13570
  relayConfig
13435
13571
  );
13436
- await server.start({ transport, port, currentVersion: "2.0.6" });
13572
+ await server.start({ transport, port, currentVersion: "2.0.7" });
13437
13573
  }
13438
13574
  })
13439
13575
  },
@@ -13593,7 +13729,7 @@ function createVersionCommand(registry2) {
13593
13729
  description: "Show relay and backend versions"
13594
13730
  },
13595
13731
  async run() {
13596
- const relayVersion = "2.0.6";
13732
+ const relayVersion = "2.0.7";
13597
13733
  console.log(`agentic-relay v${relayVersion}`);
13598
13734
  console.log("");
13599
13735
  console.log("Backends:");
@@ -13990,7 +14126,7 @@ var subCommandNames = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "upd
13990
14126
  var main = defineCommand11({
13991
14127
  meta: {
13992
14128
  name: "relay",
13993
- version: "2.0.6",
14129
+ version: "2.0.7",
13994
14130
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
13995
14131
  },
13996
14132
  args: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI with MCP-based multi-layer sub-agent orchestration",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",