@rk0429/agentic-relay 2.0.6 → 2.0.8
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/relay.mjs +250 -66
- 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
|
|
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(
|
|
350
|
+
sessionDir: join6(getRelayHome3(), "sessions"),
|
|
351
351
|
eventsFileName: "events.jsonl"
|
|
352
352
|
};
|
|
353
353
|
AgentEventStore = class {
|
|
@@ -1085,7 +1085,16 @@ function buildContextFromEnv() {
|
|
|
1085
1085
|
const depth = Number(process.env["RELAY_DEPTH"] ?? "0");
|
|
1086
1086
|
return { traceId, parentSessionId, depth };
|
|
1087
1087
|
}
|
|
1088
|
-
async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl, onProgress, agentEventStore, hookMemoryDir = "./memory", taskCompleter, taskLifecycleManager, relayContext) {
|
|
1088
|
+
async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl, onProgress, agentEventStore, hookMemoryDir = "./memory", taskCompleter, taskLifecycleManager, relayContext, signal) {
|
|
1089
|
+
if (signal?.aborted) {
|
|
1090
|
+
return {
|
|
1091
|
+
sessionId: "",
|
|
1092
|
+
exitCode: 1,
|
|
1093
|
+
stdout: "",
|
|
1094
|
+
stderr: "Spawn cancelled: parent session disconnected",
|
|
1095
|
+
failureReason: "unknown"
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1089
1098
|
onProgress?.({ stage: "initializing", percent: 0 });
|
|
1090
1099
|
let inlineTaskId;
|
|
1091
1100
|
if (input.title && taskLifecycleManager) {
|
|
@@ -1405,7 +1414,8 @@ ${input.prompt}`;
|
|
|
1405
1414
|
maxDepth: guard.getConfig().maxDepth,
|
|
1406
1415
|
traceId: envContext.traceId
|
|
1407
1416
|
},
|
|
1408
|
-
...mcpServers ? { mcpServers } : {}
|
|
1417
|
+
...mcpServers ? { mcpServers } : {},
|
|
1418
|
+
...signal ? { signal } : {}
|
|
1409
1419
|
});
|
|
1410
1420
|
}
|
|
1411
1421
|
})();
|
|
@@ -1853,7 +1863,7 @@ var init_conflict_detector = __esm({
|
|
|
1853
1863
|
});
|
|
1854
1864
|
|
|
1855
1865
|
// src/mcp-server/tools/spawn-agents-parallel.ts
|
|
1856
|
-
async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl, onProgress, agentEventStore, hookMemoryDir = "./memory", taskCompleter, taskLifecycleManager, relayContext) {
|
|
1866
|
+
async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl, onProgress, agentEventStore, hookMemoryDir = "./memory", taskCompleter, taskLifecycleManager, relayContext, signal) {
|
|
1857
1867
|
for (let index = 0; index < agents.length; index += 1) {
|
|
1858
1868
|
try {
|
|
1859
1869
|
resolveValidatedSessionMetadata(agents[index]);
|
|
@@ -1939,7 +1949,8 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
|
|
|
1939
1949
|
hookMemoryDir,
|
|
1940
1950
|
taskCompleter,
|
|
1941
1951
|
taskLifecycleManager,
|
|
1942
|
-
relayContext
|
|
1952
|
+
relayContext,
|
|
1953
|
+
signal
|
|
1943
1954
|
).then((result) => {
|
|
1944
1955
|
completedCount++;
|
|
1945
1956
|
onProgress?.({
|
|
@@ -8442,7 +8453,7 @@ var init_server = __esm({
|
|
|
8442
8453
|
this.agentEventStore
|
|
8443
8454
|
);
|
|
8444
8455
|
this.server = new McpServer(
|
|
8445
|
-
{ name: "agentic-relay", version: "2.0.
|
|
8456
|
+
{ name: "agentic-relay", version: "2.0.8" },
|
|
8446
8457
|
createMcpServerOptions()
|
|
8447
8458
|
);
|
|
8448
8459
|
this.registerTools(this.server);
|
|
@@ -8474,7 +8485,7 @@ var init_server = __esm({
|
|
|
8474
8485
|
* Run spawn_agent in the background and store the result in taskStore.
|
|
8475
8486
|
* Fire-and-forget: errors are caught and stored as failed task results.
|
|
8476
8487
|
*/
|
|
8477
|
-
runSpawnAgentInBackground(taskId, params, extra, relayContext) {
|
|
8488
|
+
runSpawnAgentInBackground(taskId, params, extra, relayContext, signal) {
|
|
8478
8489
|
const run = async () => {
|
|
8479
8490
|
try {
|
|
8480
8491
|
const result = await executeSpawnAgent(
|
|
@@ -8491,7 +8502,8 @@ var init_server = __esm({
|
|
|
8491
8502
|
this.hookMemoryDir,
|
|
8492
8503
|
void 0,
|
|
8493
8504
|
this.taskLifecycleManager,
|
|
8494
|
-
relayContext
|
|
8505
|
+
relayContext,
|
|
8506
|
+
signal
|
|
8495
8507
|
);
|
|
8496
8508
|
const controlOptions = {
|
|
8497
8509
|
inlineSummaryLength: this.inlineSummaryLength ?? DEFAULT_INLINE_SUMMARY_LENGTH,
|
|
@@ -8532,7 +8544,7 @@ var init_server = __esm({
|
|
|
8532
8544
|
* Run spawn_agents_parallel in the background and store the result in taskStore.
|
|
8533
8545
|
* Fire-and-forget: errors are caught and stored as failed task results.
|
|
8534
8546
|
*/
|
|
8535
|
-
runSpawnAgentsParallelInBackground(taskId, agents, extra, relayContext) {
|
|
8547
|
+
runSpawnAgentsParallelInBackground(taskId, agents, extra, relayContext, signal) {
|
|
8536
8548
|
const run = async () => {
|
|
8537
8549
|
try {
|
|
8538
8550
|
const result = await executeSpawnAgentsParallel(
|
|
@@ -8549,7 +8561,8 @@ var init_server = __esm({
|
|
|
8549
8561
|
this.hookMemoryDir,
|
|
8550
8562
|
void 0,
|
|
8551
8563
|
this.taskLifecycleManager,
|
|
8552
|
-
relayContext
|
|
8564
|
+
relayContext,
|
|
8565
|
+
signal
|
|
8553
8566
|
);
|
|
8554
8567
|
const controlOptions = {
|
|
8555
8568
|
inlineSummaryLength: this.inlineSummaryLength ?? DEFAULT_INLINE_SUMMARY_LENGTH,
|
|
@@ -8585,7 +8598,7 @@ var init_server = __esm({
|
|
|
8585
8598
|
};
|
|
8586
8599
|
void run();
|
|
8587
8600
|
}
|
|
8588
|
-
registerTools(server, relayContext) {
|
|
8601
|
+
registerTools(server, relayContext, signal) {
|
|
8589
8602
|
server.experimental.tasks.registerToolTask(
|
|
8590
8603
|
"spawn_agent",
|
|
8591
8604
|
{
|
|
@@ -8599,7 +8612,7 @@ var init_server = __esm({
|
|
|
8599
8612
|
ttl: DEFAULT_TASK_TTL,
|
|
8600
8613
|
pollInterval: DEFAULT_POLL_INTERVAL
|
|
8601
8614
|
});
|
|
8602
|
-
this.runSpawnAgentInBackground(task.taskId, params, extra, relayContext);
|
|
8615
|
+
this.runSpawnAgentInBackground(task.taskId, params, extra, relayContext, signal);
|
|
8603
8616
|
return { task };
|
|
8604
8617
|
},
|
|
8605
8618
|
getTask: async (_params, extra) => {
|
|
@@ -8625,7 +8638,7 @@ var init_server = __esm({
|
|
|
8625
8638
|
ttl: DEFAULT_TASK_TTL,
|
|
8626
8639
|
pollInterval: DEFAULT_POLL_INTERVAL
|
|
8627
8640
|
});
|
|
8628
|
-
this.runSpawnAgentsParallelInBackground(task.taskId, params.agents, extra, relayContext);
|
|
8641
|
+
this.runSpawnAgentsParallelInBackground(task.taskId, params.agents, extra, relayContext, signal);
|
|
8629
8642
|
return { task };
|
|
8630
8643
|
},
|
|
8631
8644
|
getTask: async (_params, extra) => {
|
|
@@ -8664,7 +8677,8 @@ var init_server = __esm({
|
|
|
8664
8677
|
this.hookMemoryDir,
|
|
8665
8678
|
void 0,
|
|
8666
8679
|
this.taskLifecycleManager,
|
|
8667
|
-
relayContext
|
|
8680
|
+
relayContext,
|
|
8681
|
+
signal
|
|
8668
8682
|
);
|
|
8669
8683
|
const controlOptions = {
|
|
8670
8684
|
inlineSummaryLength: this.inlineSummaryLength ?? DEFAULT_INLINE_SUMMARY_LENGTH,
|
|
@@ -8936,6 +8950,7 @@ var init_server = __esm({
|
|
|
8936
8950
|
*/
|
|
8937
8951
|
async startChildHttpServer() {
|
|
8938
8952
|
const sessions = /* @__PURE__ */ new Map();
|
|
8953
|
+
const sessionAbortControllers = /* @__PURE__ */ new Map();
|
|
8939
8954
|
const httpServer = createServer(async (req, res) => {
|
|
8940
8955
|
const url = req.url ?? "";
|
|
8941
8956
|
if (url !== "/mcp" && !url.startsWith("/mcp?")) {
|
|
@@ -8952,19 +8967,27 @@ var init_server = __esm({
|
|
|
8952
8967
|
}
|
|
8953
8968
|
}
|
|
8954
8969
|
const childRelayContext = extractRelayContextFromUrl(url);
|
|
8970
|
+
const abortController = new AbortController();
|
|
8955
8971
|
const transport = new StreamableHTTPServerTransport({
|
|
8956
8972
|
sessionIdGenerator: () => randomUUID()
|
|
8957
8973
|
});
|
|
8958
8974
|
const server = new McpServer(
|
|
8959
|
-
{ name: "agentic-relay", version: "2.0.
|
|
8975
|
+
{ name: "agentic-relay", version: "2.0.8" },
|
|
8960
8976
|
createMcpServerOptions()
|
|
8961
8977
|
);
|
|
8962
|
-
this.registerTools(server, childRelayContext);
|
|
8978
|
+
this.registerTools(server, childRelayContext, abortController.signal);
|
|
8963
8979
|
transport.onclose = () => {
|
|
8964
8980
|
const sid2 = transport.sessionId;
|
|
8965
8981
|
if (sid2) {
|
|
8966
8982
|
sessions.delete(sid2);
|
|
8967
|
-
|
|
8983
|
+
const ctrl = sessionAbortControllers.get(sid2);
|
|
8984
|
+
if (ctrl) {
|
|
8985
|
+
ctrl.abort();
|
|
8986
|
+
sessionAbortControllers.delete(sid2);
|
|
8987
|
+
logger.debug(`Child MCP session closed, background tasks aborted: ${sid2}`);
|
|
8988
|
+
} else {
|
|
8989
|
+
logger.debug(`Child MCP session closed: ${sid2}`);
|
|
8990
|
+
}
|
|
8968
8991
|
}
|
|
8969
8992
|
};
|
|
8970
8993
|
await server.connect(transport);
|
|
@@ -8972,12 +8995,18 @@ var init_server = __esm({
|
|
|
8972
8995
|
const sid = transport.sessionId;
|
|
8973
8996
|
if (sid) {
|
|
8974
8997
|
sessions.set(sid, { transport, server });
|
|
8998
|
+
sessionAbortControllers.set(sid, abortController);
|
|
8975
8999
|
logger.debug(`Child MCP session created: ${sid}`);
|
|
8976
9000
|
if (sessions.size > MAX_CHILD_HTTP_SESSIONS) {
|
|
8977
9001
|
const oldestEntry = sessions.entries().next().value;
|
|
8978
9002
|
if (oldestEntry) {
|
|
8979
9003
|
const [oldestSessionId, oldestSession] = oldestEntry;
|
|
8980
9004
|
sessions.delete(oldestSessionId);
|
|
9005
|
+
const evictedCtrl = sessionAbortControllers.get(oldestSessionId);
|
|
9006
|
+
if (evictedCtrl) {
|
|
9007
|
+
evictedCtrl.abort();
|
|
9008
|
+
sessionAbortControllers.delete(oldestSessionId);
|
|
9009
|
+
}
|
|
8981
9010
|
logger.warn(
|
|
8982
9011
|
`Child MCP session evicted due to limit (${MAX_CHILD_HTTP_SESSIONS}): ${oldestSessionId}`
|
|
8983
9012
|
);
|
|
@@ -10635,6 +10664,14 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
10635
10664
|
const timeoutMs = resolveClaudeSdkTimeoutMs();
|
|
10636
10665
|
const abortController = new AbortController();
|
|
10637
10666
|
const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
|
|
10667
|
+
const onExternalAbort = () => abortController.abort();
|
|
10668
|
+
if (flags.signal) {
|
|
10669
|
+
if (flags.signal.aborted) {
|
|
10670
|
+
abortController.abort();
|
|
10671
|
+
} else {
|
|
10672
|
+
flags.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
10673
|
+
}
|
|
10674
|
+
}
|
|
10638
10675
|
try {
|
|
10639
10676
|
const { query } = await loadClaudeSDK();
|
|
10640
10677
|
const options = {
|
|
@@ -10705,10 +10742,11 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
10705
10742
|
};
|
|
10706
10743
|
} catch (error) {
|
|
10707
10744
|
if (abortController.signal.aborted) {
|
|
10745
|
+
const reason = flags.signal?.aborted ? "Claude execution cancelled: parent session disconnected" : `Claude SDK query timed out after ${timeoutMs}ms`;
|
|
10708
10746
|
return {
|
|
10709
10747
|
exitCode: 1,
|
|
10710
10748
|
stdout: "",
|
|
10711
|
-
stderr:
|
|
10749
|
+
stderr: reason
|
|
10712
10750
|
};
|
|
10713
10751
|
}
|
|
10714
10752
|
return {
|
|
@@ -10718,6 +10756,7 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
10718
10756
|
};
|
|
10719
10757
|
} finally {
|
|
10720
10758
|
if (timer !== void 0) clearTimeout(timer);
|
|
10759
|
+
flags.signal?.removeEventListener("abort", onExternalAbort);
|
|
10721
10760
|
}
|
|
10722
10761
|
}
|
|
10723
10762
|
async *executeStreaming(flags) {
|
|
@@ -10969,7 +11008,7 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
10969
11008
|
|
|
10970
11009
|
// src/adapters/codex-adapter.ts
|
|
10971
11010
|
init_logger();
|
|
10972
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
11011
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, mkdtemp, copyFile } from "fs/promises";
|
|
10973
11012
|
import { homedir as homedir2 } from "os";
|
|
10974
11013
|
import { join as join2, dirname as dirname2 } from "path";
|
|
10975
11014
|
async function loadCodexSDK() {
|
|
@@ -11066,6 +11105,12 @@ function toTOMLString(value) {
|
|
|
11066
11105
|
function toTOMLStringArray(values) {
|
|
11067
11106
|
return `[${values.map(toTOMLString).join(", ")}]`;
|
|
11068
11107
|
}
|
|
11108
|
+
function toTOMLInlineTable(values) {
|
|
11109
|
+
return `{ ${Object.entries(values).map(([key, value]) => `${toTOMLString(key)} = ${toTOMLString(value)}`).join(", ")} }`;
|
|
11110
|
+
}
|
|
11111
|
+
function toTOMLTableKey(key) {
|
|
11112
|
+
return /^[A-Za-z0-9_-]+$/.test(key) ? key : toTOMLString(key);
|
|
11113
|
+
}
|
|
11069
11114
|
function generateMcpServersTOML(servers) {
|
|
11070
11115
|
const parts = [];
|
|
11071
11116
|
for (const server of servers) {
|
|
@@ -11079,11 +11124,88 @@ function generateMcpServersTOML(servers) {
|
|
|
11079
11124
|
}
|
|
11080
11125
|
return parts.join("\n");
|
|
11081
11126
|
}
|
|
11127
|
+
function generateChildMcpServersTOML(servers) {
|
|
11128
|
+
const parts = [];
|
|
11129
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
11130
|
+
parts.push(`[mcp_servers.${toTOMLTableKey(name)}]`);
|
|
11131
|
+
if ("url" in server) {
|
|
11132
|
+
parts.push(`url = ${toTOMLString(server.url)}`);
|
|
11133
|
+
if (server.headers && Object.keys(server.headers).length > 0) {
|
|
11134
|
+
parts.push(`headers = ${toTOMLInlineTable(server.headers)}`);
|
|
11135
|
+
}
|
|
11136
|
+
} else {
|
|
11137
|
+
parts.push(`command = ${toTOMLString(server.command)}`);
|
|
11138
|
+
if (server.args && server.args.length > 0) {
|
|
11139
|
+
parts.push(`args = ${toTOMLStringArray(server.args)}`);
|
|
11140
|
+
}
|
|
11141
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
11142
|
+
parts.push(`env = ${toTOMLInlineTable(server.env)}`);
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
parts.push("");
|
|
11146
|
+
}
|
|
11147
|
+
return parts.join("\n");
|
|
11148
|
+
}
|
|
11149
|
+
function rewriteCodexConfigWithMcpServers(existingContent, mcpServers) {
|
|
11150
|
+
const parsed = parseTOMLMcpServers(existingContent);
|
|
11151
|
+
const preamble = parsed.preambleLines.join("\n").replace(/\n+$/, "");
|
|
11152
|
+
const mcpSection = generateChildMcpServersTOML(mcpServers);
|
|
11153
|
+
const postamble = parsed.postambleLines.join("\n").replace(/^\n+/, "");
|
|
11154
|
+
const parts = [];
|
|
11155
|
+
if (preamble) parts.push(preamble);
|
|
11156
|
+
if (mcpSection) parts.push(mcpSection);
|
|
11157
|
+
if (postamble) parts.push(postamble);
|
|
11158
|
+
let output = parts.join("\n\n");
|
|
11159
|
+
if (!output.endsWith("\n")) output += "\n";
|
|
11160
|
+
return output;
|
|
11161
|
+
}
|
|
11162
|
+
function getRelayHome() {
|
|
11163
|
+
return process.env["RELAY_HOME"] ?? join2(homedir2(), ".relay");
|
|
11164
|
+
}
|
|
11165
|
+
function encodeNativeSessionId(threadId, codexHome) {
|
|
11166
|
+
if (!codexHome) {
|
|
11167
|
+
return threadId;
|
|
11168
|
+
}
|
|
11169
|
+
return `${threadId}@@${Buffer.from(codexHome, "utf8").toString("base64url")}`;
|
|
11170
|
+
}
|
|
11171
|
+
function decodeNativeSessionId(nativeSessionId) {
|
|
11172
|
+
const separatorIndex = nativeSessionId.lastIndexOf("@@");
|
|
11173
|
+
if (separatorIndex === -1) {
|
|
11174
|
+
return { threadId: nativeSessionId };
|
|
11175
|
+
}
|
|
11176
|
+
const threadId = nativeSessionId.slice(0, separatorIndex);
|
|
11177
|
+
const encodedHome = nativeSessionId.slice(separatorIndex + 2);
|
|
11178
|
+
try {
|
|
11179
|
+
const codexHome = Buffer.from(encodedHome, "base64url").toString("utf8");
|
|
11180
|
+
return { threadId, codexHome };
|
|
11181
|
+
} catch {
|
|
11182
|
+
return { threadId: nativeSessionId };
|
|
11183
|
+
}
|
|
11184
|
+
}
|
|
11185
|
+
function isFileNotFoundError(error) {
|
|
11186
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
11187
|
+
}
|
|
11188
|
+
async function copyIfExists(from, to) {
|
|
11189
|
+
try {
|
|
11190
|
+
await copyFile(from, to);
|
|
11191
|
+
} catch (error) {
|
|
11192
|
+
if (isFileNotFoundError(error)) {
|
|
11193
|
+
return;
|
|
11194
|
+
}
|
|
11195
|
+
throw error;
|
|
11196
|
+
}
|
|
11197
|
+
}
|
|
11082
11198
|
var CodexAdapter = class extends BaseAdapter {
|
|
11083
11199
|
id = "codex";
|
|
11084
11200
|
command = "codex";
|
|
11201
|
+
getCodexHomePath() {
|
|
11202
|
+
return join2(homedir2(), ".codex");
|
|
11203
|
+
}
|
|
11204
|
+
getIsolatedCodexHomesPath() {
|
|
11205
|
+
return join2(getRelayHome(), "codex-homes");
|
|
11206
|
+
}
|
|
11085
11207
|
getConfigPath() {
|
|
11086
|
-
return join2(
|
|
11208
|
+
return join2(this.getCodexHomePath(), "config.toml");
|
|
11087
11209
|
}
|
|
11088
11210
|
async checkAuthStatus() {
|
|
11089
11211
|
const result = await this.processManager.execute(this.command, [
|
|
@@ -11096,46 +11218,74 @@ var CodexAdapter = class extends BaseAdapter {
|
|
|
11096
11218
|
...!authenticated ? { message: "codex authentication not configured" } : {}
|
|
11097
11219
|
};
|
|
11098
11220
|
}
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
const
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11221
|
+
async prepareIsolatedCodexHome(mcpServers) {
|
|
11222
|
+
const codexHomesDir = this.getIsolatedCodexHomesPath();
|
|
11223
|
+
await mkdir2(codexHomesDir, { recursive: true });
|
|
11224
|
+
const isolatedCodexHome = await mkdtemp(join2(codexHomesDir, "codex-"));
|
|
11225
|
+
const realCodexHome = this.getCodexHomePath();
|
|
11226
|
+
await copyIfExists(
|
|
11227
|
+
join2(realCodexHome, "auth.json"),
|
|
11228
|
+
join2(isolatedCodexHome, "auth.json")
|
|
11229
|
+
);
|
|
11230
|
+
await copyIfExists(
|
|
11231
|
+
join2(realCodexHome, "version.json"),
|
|
11232
|
+
join2(isolatedCodexHome, "version.json")
|
|
11233
|
+
);
|
|
11234
|
+
await copyIfExists(
|
|
11235
|
+
join2(realCodexHome, "internal_storage.json"),
|
|
11236
|
+
join2(isolatedCodexHome, "internal_storage.json")
|
|
11237
|
+
);
|
|
11238
|
+
await copyIfExists(
|
|
11239
|
+
join2(realCodexHome, ".codex-global-state.json"),
|
|
11240
|
+
join2(isolatedCodexHome, ".codex-global-state.json")
|
|
11241
|
+
);
|
|
11242
|
+
let existingConfig = "";
|
|
11243
|
+
try {
|
|
11244
|
+
existingConfig = await readFile2(this.getConfigPath(), "utf-8");
|
|
11245
|
+
} catch (error) {
|
|
11246
|
+
if (!isFileNotFoundError(error)) {
|
|
11247
|
+
throw error;
|
|
11116
11248
|
}
|
|
11117
11249
|
}
|
|
11118
|
-
|
|
11250
|
+
const isolatedConfig = rewriteCodexConfigWithMcpServers(
|
|
11251
|
+
existingConfig,
|
|
11252
|
+
mcpServers
|
|
11253
|
+
);
|
|
11254
|
+
await writeFile2(join2(isolatedCodexHome, "config.toml"), isolatedConfig, {
|
|
11255
|
+
mode: 384
|
|
11256
|
+
});
|
|
11257
|
+
return isolatedCodexHome;
|
|
11119
11258
|
}
|
|
11120
|
-
buildCodexOptions(flags) {
|
|
11259
|
+
async buildCodexOptions(flags) {
|
|
11121
11260
|
const options = {};
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11261
|
+
let env;
|
|
11262
|
+
let isolatedCodexHome;
|
|
11263
|
+
const ensureEnv = () => {
|
|
11264
|
+
if (!env) {
|
|
11265
|
+
env = {};
|
|
11266
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
11267
|
+
if (value !== void 0) {
|
|
11268
|
+
env[key] = value;
|
|
11269
|
+
}
|
|
11127
11270
|
}
|
|
11128
11271
|
}
|
|
11129
|
-
env
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11272
|
+
return env;
|
|
11273
|
+
};
|
|
11274
|
+
if (flags.mcpContext) {
|
|
11275
|
+
const resolvedEnv = ensureEnv();
|
|
11276
|
+
resolvedEnv.RELAY_TRACE_ID = flags.mcpContext.traceId;
|
|
11277
|
+
resolvedEnv.RELAY_PARENT_SESSION_ID = flags.mcpContext.parentSessionId;
|
|
11278
|
+
resolvedEnv.RELAY_DEPTH = String(flags.mcpContext.depth);
|
|
11133
11279
|
}
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11280
|
+
if (flags.mcpServers && Object.keys(flags.mcpServers).length > 0) {
|
|
11281
|
+
const resolvedEnv = ensureEnv();
|
|
11282
|
+
isolatedCodexHome = await this.prepareIsolatedCodexHome(flags.mcpServers);
|
|
11283
|
+
resolvedEnv.CODEX_HOME = isolatedCodexHome;
|
|
11137
11284
|
}
|
|
11138
|
-
|
|
11285
|
+
if (env) {
|
|
11286
|
+
options.env = env;
|
|
11287
|
+
}
|
|
11288
|
+
return { options, isolatedCodexHome };
|
|
11139
11289
|
}
|
|
11140
11290
|
mapFlags(flags) {
|
|
11141
11291
|
const args = mapCommonToNative("codex", flags);
|
|
@@ -11189,20 +11339,35 @@ ${prompt}`;
|
|
|
11189
11339
|
);
|
|
11190
11340
|
try {
|
|
11191
11341
|
const { Codex } = await loadCodexSDK();
|
|
11192
|
-
const
|
|
11342
|
+
const { options, isolatedCodexHome } = await this.buildCodexOptions(flags);
|
|
11343
|
+
const codex = new Codex(options);
|
|
11193
11344
|
const thread = codex.startThread({
|
|
11194
11345
|
...flags.model ? { model: flags.model } : {},
|
|
11195
11346
|
workingDirectory: process.cwd(),
|
|
11196
11347
|
approvalPolicy: "never"
|
|
11197
11348
|
});
|
|
11198
|
-
const result = await thread.run(effectivePrompt
|
|
11349
|
+
const result = await thread.run(effectivePrompt, {
|
|
11350
|
+
...flags.signal ? { signal: flags.signal } : {}
|
|
11351
|
+
});
|
|
11199
11352
|
return {
|
|
11200
11353
|
exitCode: 0,
|
|
11201
11354
|
stdout: result.finalResponse,
|
|
11202
11355
|
stderr: "",
|
|
11203
|
-
...thread.id ? {
|
|
11356
|
+
...thread.id ? {
|
|
11357
|
+
nativeSessionId: encodeNativeSessionId(
|
|
11358
|
+
thread.id,
|
|
11359
|
+
isolatedCodexHome
|
|
11360
|
+
)
|
|
11361
|
+
} : {}
|
|
11204
11362
|
};
|
|
11205
11363
|
} catch (error) {
|
|
11364
|
+
if (flags.signal?.aborted) {
|
|
11365
|
+
return {
|
|
11366
|
+
exitCode: 1,
|
|
11367
|
+
stdout: "",
|
|
11368
|
+
stderr: "Codex execution cancelled: parent session disconnected"
|
|
11369
|
+
};
|
|
11370
|
+
}
|
|
11206
11371
|
return {
|
|
11207
11372
|
exitCode: 1,
|
|
11208
11373
|
stdout: "",
|
|
@@ -11221,7 +11386,8 @@ ${prompt}`;
|
|
|
11221
11386
|
);
|
|
11222
11387
|
try {
|
|
11223
11388
|
const { Codex } = await loadCodexSDK();
|
|
11224
|
-
const
|
|
11389
|
+
const { options, isolatedCodexHome } = await this.buildCodexOptions(flags);
|
|
11390
|
+
const codex = new Codex(options);
|
|
11225
11391
|
const thread = codex.startThread({
|
|
11226
11392
|
...flags.model ? { model: flags.model } : {},
|
|
11227
11393
|
workingDirectory: process.cwd(),
|
|
@@ -11282,14 +11448,20 @@ ${prompt}`;
|
|
|
11282
11448
|
yield {
|
|
11283
11449
|
type: "done",
|
|
11284
11450
|
result: { exitCode: 0, stdout: finalResponse, stderr: "" },
|
|
11285
|
-
nativeSessionId: threadId
|
|
11451
|
+
nativeSessionId: threadId || thread.id ? encodeNativeSessionId(
|
|
11452
|
+
threadId ?? thread.id ?? "",
|
|
11453
|
+
isolatedCodexHome
|
|
11454
|
+
) : void 0
|
|
11286
11455
|
};
|
|
11287
11456
|
} else if (event.type === "turn.failed") {
|
|
11288
11457
|
const errorMessage = event.error.message ?? "Turn failed";
|
|
11289
11458
|
yield {
|
|
11290
11459
|
type: "done",
|
|
11291
11460
|
result: { exitCode: 1, stdout: "", stderr: errorMessage },
|
|
11292
|
-
nativeSessionId: threadId
|
|
11461
|
+
nativeSessionId: threadId || thread.id ? encodeNativeSessionId(
|
|
11462
|
+
threadId ?? thread.id ?? "",
|
|
11463
|
+
isolatedCodexHome
|
|
11464
|
+
) : void 0
|
|
11293
11465
|
};
|
|
11294
11466
|
} else if (event.type === "error") {
|
|
11295
11467
|
yield {
|
|
@@ -11310,8 +11482,20 @@ ${prompt}`;
|
|
|
11310
11482
|
async continueSession(nativeSessionId, prompt) {
|
|
11311
11483
|
try {
|
|
11312
11484
|
const { Codex } = await loadCodexSDK();
|
|
11313
|
-
const
|
|
11314
|
-
const
|
|
11485
|
+
const { threadId, codexHome } = decodeNativeSessionId(nativeSessionId);
|
|
11486
|
+
const codex = new Codex(
|
|
11487
|
+
codexHome ? {
|
|
11488
|
+
env: {
|
|
11489
|
+
...Object.fromEntries(
|
|
11490
|
+
Object.entries(process.env).filter(
|
|
11491
|
+
([, value]) => value !== void 0
|
|
11492
|
+
)
|
|
11493
|
+
),
|
|
11494
|
+
CODEX_HOME: codexHome
|
|
11495
|
+
}
|
|
11496
|
+
} : {}
|
|
11497
|
+
);
|
|
11498
|
+
const thread = codex.resumeThread(threadId, {
|
|
11315
11499
|
workingDirectory: process.cwd(),
|
|
11316
11500
|
approvalPolicy: "never"
|
|
11317
11501
|
});
|
|
@@ -11650,7 +11834,7 @@ import { readFile as readFile4, writeFile as writeFile4, readdir, mkdir as mkdir
|
|
|
11650
11834
|
import { join as join4 } from "path";
|
|
11651
11835
|
import { homedir as homedir4 } from "os";
|
|
11652
11836
|
import { nanoid } from "nanoid";
|
|
11653
|
-
function
|
|
11837
|
+
function getRelayHome2() {
|
|
11654
11838
|
return process.env["RELAY_HOME"] ?? join4(homedir4(), ".relay");
|
|
11655
11839
|
}
|
|
11656
11840
|
function getSessionsDir(relayHome2) {
|
|
@@ -11690,7 +11874,7 @@ var SessionManager = class _SessionManager {
|
|
|
11690
11874
|
]);
|
|
11691
11875
|
sessionsDir;
|
|
11692
11876
|
constructor(sessionsDir) {
|
|
11693
|
-
this.sessionsDir = sessionsDir ?? getSessionsDir(
|
|
11877
|
+
this.sessionsDir = sessionsDir ?? getSessionsDir(getRelayHome2());
|
|
11694
11878
|
}
|
|
11695
11879
|
getSessionsDir() {
|
|
11696
11880
|
return this.sessionsDir;
|
|
@@ -13433,7 +13617,7 @@ function createMCPCommand(configManager2, registry2, sessionManager2, hooksEngin
|
|
|
13433
13617
|
responseOutputDir,
|
|
13434
13618
|
relayConfig
|
|
13435
13619
|
);
|
|
13436
|
-
await server.start({ transport, port, currentVersion: "2.0.
|
|
13620
|
+
await server.start({ transport, port, currentVersion: "2.0.8" });
|
|
13437
13621
|
}
|
|
13438
13622
|
})
|
|
13439
13623
|
},
|
|
@@ -13593,7 +13777,7 @@ function createVersionCommand(registry2) {
|
|
|
13593
13777
|
description: "Show relay and backend versions"
|
|
13594
13778
|
},
|
|
13595
13779
|
async run() {
|
|
13596
|
-
const relayVersion = "2.0.
|
|
13780
|
+
const relayVersion = "2.0.8";
|
|
13597
13781
|
console.log(`agentic-relay v${relayVersion}`);
|
|
13598
13782
|
console.log("");
|
|
13599
13783
|
console.log("Backends:");
|
|
@@ -13990,7 +14174,7 @@ var subCommandNames = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "upd
|
|
|
13990
14174
|
var main = defineCommand11({
|
|
13991
14175
|
meta: {
|
|
13992
14176
|
name: "relay",
|
|
13993
|
-
version: "2.0.
|
|
14177
|
+
version: "2.0.8",
|
|
13994
14178
|
description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
|
|
13995
14179
|
},
|
|
13996
14180
|
args: {
|
package/package.json
CHANGED