@rk0429/agentic-relay 0.8.0 → 0.9.0
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 +141 -45
- package/package.json +1 -1
package/dist/relay.mjs
CHANGED
|
@@ -307,6 +307,12 @@ function buildChildMcpServers(parentMcpServers, childHttpUrl) {
|
|
|
307
307
|
}
|
|
308
308
|
return result;
|
|
309
309
|
}
|
|
310
|
+
function inferFailureReason(stderr, stdout) {
|
|
311
|
+
const combined = `${stderr} ${stdout}`.toLowerCase();
|
|
312
|
+
if (combined.includes("timed out") || combined.includes("timeout")) return "timeout";
|
|
313
|
+
if (combined.includes("max turns") || combined.includes("max_turns") || combined.includes("turn limit")) return "max_turns_exhausted";
|
|
314
|
+
return "adapter_error";
|
|
315
|
+
}
|
|
310
316
|
function buildContextFromEnv() {
|
|
311
317
|
const traceId = process.env["RELAY_TRACE_ID"] ?? `trace-${nanoid2()}`;
|
|
312
318
|
const parentSessionId = process.env["RELAY_PARENT_SESSION_ID"] ?? null;
|
|
@@ -344,7 +350,8 @@ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooks
|
|
|
344
350
|
sessionId: "",
|
|
345
351
|
exitCode: 1,
|
|
346
352
|
stdout: "",
|
|
347
|
-
stderr: `Spawn blocked: ${guardResult.reason}
|
|
353
|
+
stderr: `Spawn blocked: ${guardResult.reason}`,
|
|
354
|
+
failureReason: "recursion_blocked"
|
|
348
355
|
};
|
|
349
356
|
}
|
|
350
357
|
const adapter = registry2.get(effectiveBackend);
|
|
@@ -354,7 +361,8 @@ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooks
|
|
|
354
361
|
sessionId: "",
|
|
355
362
|
exitCode: 1,
|
|
356
363
|
stdout: "",
|
|
357
|
-
stderr: `Backend "${effectiveBackend}" is not available. Use list_available_backends to see available options
|
|
364
|
+
stderr: `Backend "${effectiveBackend}" is not available. Use list_available_backends to see available options.`,
|
|
365
|
+
failureReason: "backend_unavailable"
|
|
358
366
|
};
|
|
359
367
|
}
|
|
360
368
|
const spawnStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -446,7 +454,8 @@ ${wrapped}` : wrapped;
|
|
|
446
454
|
sessionId: "",
|
|
447
455
|
exitCode: 1,
|
|
448
456
|
stdout: "",
|
|
449
|
-
stderr: `Task instruction file not found: ${input.taskInstructionPath}
|
|
457
|
+
stderr: `Task instruction file not found: ${input.taskInstructionPath}`,
|
|
458
|
+
failureReason: "instruction_file_error"
|
|
450
459
|
};
|
|
451
460
|
}
|
|
452
461
|
const instructionContent = readFileSync(safePath, "utf-8");
|
|
@@ -459,7 +468,8 @@ ${input.prompt}`;
|
|
|
459
468
|
sessionId: "",
|
|
460
469
|
exitCode: 1,
|
|
461
470
|
stdout: "",
|
|
462
|
-
stderr: `Failed to read task instruction file: ${message}
|
|
471
|
+
stderr: `Failed to read task instruction file: ${message}`,
|
|
472
|
+
failureReason: "instruction_file_error"
|
|
463
473
|
};
|
|
464
474
|
}
|
|
465
475
|
}
|
|
@@ -473,7 +483,8 @@ ${input.prompt}`;
|
|
|
473
483
|
exitCode: 1,
|
|
474
484
|
stdout: "",
|
|
475
485
|
stderr: `Backend "${effectiveBackend}" does not support session continuation (continueSession).`,
|
|
476
|
-
_noSession: true
|
|
486
|
+
_noSession: true,
|
|
487
|
+
_failureReason: "session_continuation_unsupported"
|
|
477
488
|
};
|
|
478
489
|
}
|
|
479
490
|
return adapter.continueSession(input.resumeSessionId, effectivePrompt);
|
|
@@ -517,7 +528,8 @@ ${input.prompt}`;
|
|
|
517
528
|
sessionId: session.relaySessionId,
|
|
518
529
|
exitCode: result.exitCode,
|
|
519
530
|
stdout: result.stdout,
|
|
520
|
-
stderr: result.stderr
|
|
531
|
+
stderr: result.stderr,
|
|
532
|
+
..."_failureReason" in result ? { failureReason: result._failureReason } : {}
|
|
521
533
|
};
|
|
522
534
|
}
|
|
523
535
|
onProgress?.({ stage: "executing", percent: 50 });
|
|
@@ -533,6 +545,7 @@ ${input.prompt}`;
|
|
|
533
545
|
}
|
|
534
546
|
guard.recordSpawn(context);
|
|
535
547
|
const status = result.exitCode === 0 ? "completed" : "error";
|
|
548
|
+
const failureReason = result.exitCode !== 0 ? inferFailureReason(result.stderr, result.stdout) : void 0;
|
|
536
549
|
await sessionManager2.update(session.relaySessionId, { status });
|
|
537
550
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
538
551
|
const metadata = {
|
|
@@ -541,7 +554,8 @@ ${input.prompt}`;
|
|
|
541
554
|
...input.preferredBackend ? { requestedBackend: input.preferredBackend } : {},
|
|
542
555
|
selectionReason,
|
|
543
556
|
startedAt: spawnStartedAt,
|
|
544
|
-
completedAt
|
|
557
|
+
completedAt,
|
|
558
|
+
...result.tokenUsage ? { tokenUsage: result.tokenUsage } : {}
|
|
545
559
|
};
|
|
546
560
|
onProgress?.({ stage: "completed", percent: 100 });
|
|
547
561
|
if (hooksEngine2) {
|
|
@@ -581,16 +595,19 @@ ${input.prompt}`;
|
|
|
581
595
|
stdout: result.stdout,
|
|
582
596
|
stderr: result.stderr,
|
|
583
597
|
nativeSessionId: result.nativeSessionId,
|
|
584
|
-
metadata
|
|
598
|
+
metadata,
|
|
599
|
+
...failureReason ? { failureReason } : {}
|
|
585
600
|
};
|
|
586
601
|
} catch (error) {
|
|
587
602
|
await sessionManager2.update(session.relaySessionId, { status: "error" });
|
|
588
603
|
const message = error instanceof Error ? error.message : String(error);
|
|
604
|
+
const catchFailureReason = message.toLowerCase().includes("timed out") || message.toLowerCase().includes("timeout") ? "timeout" : "unknown";
|
|
589
605
|
return {
|
|
590
606
|
sessionId: session.relaySessionId,
|
|
591
607
|
exitCode: 1,
|
|
592
608
|
stdout: "",
|
|
593
|
-
stderr: message
|
|
609
|
+
stderr: message,
|
|
610
|
+
failureReason: catchFailureReason
|
|
594
611
|
};
|
|
595
612
|
}
|
|
596
613
|
}
|
|
@@ -620,7 +637,8 @@ var init_spawn_agent = __esm({
|
|
|
620
637
|
timeoutMs: z2.number().optional().describe("Timeout in milliseconds for agent execution. Default: no timeout."),
|
|
621
638
|
taskInstructionPath: z2.string().optional().describe(
|
|
622
639
|
"Path to a file containing task instructions. Content is prepended to the prompt. Path is resolved relative to the project root and validated against path traversal."
|
|
623
|
-
)
|
|
640
|
+
),
|
|
641
|
+
label: z2.string().optional().describe("Human-readable label for identifying this agent in parallel results")
|
|
624
642
|
});
|
|
625
643
|
}
|
|
626
644
|
});
|
|
@@ -723,13 +741,15 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
|
|
|
723
741
|
const reason = `Max depth exceeded: ${envContext.depth} >= ${guard.getConfig().maxDepth}`;
|
|
724
742
|
logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
|
|
725
743
|
return {
|
|
726
|
-
results: agents.map((
|
|
744
|
+
results: agents.map((agent, index) => ({
|
|
727
745
|
index,
|
|
728
746
|
sessionId: "",
|
|
729
747
|
exitCode: 1,
|
|
730
748
|
stdout: "",
|
|
731
749
|
stderr: `Batch spawn blocked: ${reason}`,
|
|
732
|
-
error: reason
|
|
750
|
+
error: reason,
|
|
751
|
+
failureReason: "recursion_blocked",
|
|
752
|
+
...agent.label ? { label: agent.label } : {}
|
|
733
753
|
})),
|
|
734
754
|
totalCount: agents.length,
|
|
735
755
|
successCount: 0,
|
|
@@ -742,13 +762,15 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
|
|
|
742
762
|
const reason = `Batch would exceed max calls per session: ${currentCount} + ${agents.length} > ${maxCalls}`;
|
|
743
763
|
logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
|
|
744
764
|
return {
|
|
745
|
-
results: agents.map((
|
|
765
|
+
results: agents.map((agent, index) => ({
|
|
746
766
|
index,
|
|
747
767
|
sessionId: "",
|
|
748
768
|
exitCode: 1,
|
|
749
769
|
stdout: "",
|
|
750
770
|
stderr: `Batch spawn blocked: ${reason}`,
|
|
751
|
-
error: reason
|
|
771
|
+
error: reason,
|
|
772
|
+
failureReason: "recursion_blocked",
|
|
773
|
+
...agent.label ? { label: agent.label } : {}
|
|
752
774
|
})),
|
|
753
775
|
totalCount: agents.length,
|
|
754
776
|
successCount: 0,
|
|
@@ -790,7 +812,9 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
|
|
|
790
812
|
stdout: r.stdout,
|
|
791
813
|
stderr: r.stderr,
|
|
792
814
|
...r.nativeSessionId ? { nativeSessionId: r.nativeSessionId } : {},
|
|
793
|
-
...r.metadata ? { metadata: r.metadata } : {}
|
|
815
|
+
...r.metadata ? { metadata: r.metadata } : {},
|
|
816
|
+
...r.failureReason ? { failureReason: r.failureReason } : {},
|
|
817
|
+
...agents[index]?.label ? { label: agents[index].label } : {}
|
|
794
818
|
};
|
|
795
819
|
if (r.exitCode !== 0) {
|
|
796
820
|
base.originalInput = agents[index];
|
|
@@ -805,7 +829,9 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
|
|
|
805
829
|
stdout: "",
|
|
806
830
|
stderr: errorMessage,
|
|
807
831
|
error: errorMessage,
|
|
808
|
-
originalInput: agents[index]
|
|
832
|
+
originalInput: agents[index],
|
|
833
|
+
failureReason: "unknown",
|
|
834
|
+
...agents[index]?.label ? { label: agents[index].label } : {}
|
|
809
835
|
};
|
|
810
836
|
});
|
|
811
837
|
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
@@ -979,6 +1005,70 @@ var init_backend_selector = __esm({
|
|
|
979
1005
|
}
|
|
980
1006
|
});
|
|
981
1007
|
|
|
1008
|
+
// src/mcp-server/response-formatter.ts
|
|
1009
|
+
function formatSpawnAgentResponse(result) {
|
|
1010
|
+
const isError = result.exitCode !== 0;
|
|
1011
|
+
let text;
|
|
1012
|
+
if (isError) {
|
|
1013
|
+
const reasonPart = result.failureReason ? `, reason: ${result.failureReason}` : "";
|
|
1014
|
+
text = `FAILED (exit ${result.exitCode}${reasonPart})`;
|
|
1015
|
+
if (result.stderr) {
|
|
1016
|
+
text += `
|
|
1017
|
+
${result.stderr}`;
|
|
1018
|
+
}
|
|
1019
|
+
} else {
|
|
1020
|
+
text = `Session: ${result.sessionId}
|
|
1021
|
+
|
|
1022
|
+
${result.stdout}`;
|
|
1023
|
+
}
|
|
1024
|
+
if (result.metadata) {
|
|
1025
|
+
text += `
|
|
1026
|
+
|
|
1027
|
+
<metadata>
|
|
1028
|
+
${JSON.stringify(result.metadata, null, 2)}
|
|
1029
|
+
</metadata>`;
|
|
1030
|
+
}
|
|
1031
|
+
return { text, isError };
|
|
1032
|
+
}
|
|
1033
|
+
function formatParallelResponse(result) {
|
|
1034
|
+
const isError = result.failureCount === result.totalCount;
|
|
1035
|
+
const parts = [];
|
|
1036
|
+
if (result.hasConflicts && result.conflicts) {
|
|
1037
|
+
parts.push(`\u26A0 FILE CONFLICTS DETECTED: Multiple agents modified the same files.`);
|
|
1038
|
+
parts.push(`Conflicting files: ${result.conflicts.map((c) => c.path).join(", ")}
|
|
1039
|
+
`);
|
|
1040
|
+
}
|
|
1041
|
+
const durations = result.results.map((r) => r.metadata?.durationMs).filter((d) => d !== void 0);
|
|
1042
|
+
const avgDuration = durations.length > 0 ? (durations.reduce((a, b) => a + b, 0) / durations.length / 1e3).toFixed(1) : "?";
|
|
1043
|
+
parts.push(
|
|
1044
|
+
`${result.totalCount} agents: ${result.successCount} succeeded, ${result.failureCount} failed (avg ${avgDuration}s)
|
|
1045
|
+
`
|
|
1046
|
+
);
|
|
1047
|
+
for (const r of result.results) {
|
|
1048
|
+
const labelPart = r.label ? ` [${r.label}]` : "";
|
|
1049
|
+
const backend = r.metadata?.selectedBackend ?? "?";
|
|
1050
|
+
const duration = r.metadata?.durationMs !== void 0 ? `${(r.metadata.durationMs / 1e3).toFixed(1)}s` : "?s";
|
|
1051
|
+
if (r.exitCode === 0) {
|
|
1052
|
+
parts.push(`--- Agent ${r.index}${labelPart} (${backend}, ${duration}) SUCCESS ---`);
|
|
1053
|
+
parts.push(r.stdout || "(no output)");
|
|
1054
|
+
} else {
|
|
1055
|
+
const reasonPart = r.failureReason ? `, reason: ${r.failureReason}` : "";
|
|
1056
|
+
parts.push(`--- Agent ${r.index}${labelPart} FAILED (${backend}, ${duration}${reasonPart}) ---`);
|
|
1057
|
+
parts.push(r.stderr || r.error || "(no output)");
|
|
1058
|
+
}
|
|
1059
|
+
parts.push("");
|
|
1060
|
+
}
|
|
1061
|
+
parts.push(`<metadata>
|
|
1062
|
+
${JSON.stringify(result, null, 2)}
|
|
1063
|
+
</metadata>`);
|
|
1064
|
+
return { text: parts.join("\n"), isError };
|
|
1065
|
+
}
|
|
1066
|
+
var init_response_formatter = __esm({
|
|
1067
|
+
"src/mcp-server/response-formatter.ts"() {
|
|
1068
|
+
"use strict";
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
|
|
982
1072
|
// src/mcp-server/server.ts
|
|
983
1073
|
var server_exports = {};
|
|
984
1074
|
__export(server_exports, {
|
|
@@ -1002,6 +1092,7 @@ var init_server = __esm({
|
|
|
1002
1092
|
init_list_available_backends();
|
|
1003
1093
|
init_backend_selector();
|
|
1004
1094
|
init_logger();
|
|
1095
|
+
init_response_formatter();
|
|
1005
1096
|
spawnAgentsParallelInputShape = {
|
|
1006
1097
|
agents: z5.array(spawnAgentInputSchema).min(1).max(10).describe(
|
|
1007
1098
|
"Array of agent configurations to execute in parallel (1-10 agents)"
|
|
@@ -1018,7 +1109,7 @@ var init_server = __esm({
|
|
|
1018
1109
|
this.backendSelector = new BackendSelector();
|
|
1019
1110
|
this.server = new McpServer({
|
|
1020
1111
|
name: "agentic-relay",
|
|
1021
|
-
version: "0.
|
|
1112
|
+
version: "0.9.0"
|
|
1022
1113
|
});
|
|
1023
1114
|
this.registerTools(this.server);
|
|
1024
1115
|
}
|
|
@@ -1067,17 +1158,7 @@ var init_server = __esm({
|
|
|
1067
1158
|
this._childHttpUrl,
|
|
1068
1159
|
onProgress
|
|
1069
1160
|
);
|
|
1070
|
-
const isError = result
|
|
1071
|
-
let text = isError ? `Error (exit ${result.exitCode}): ${result.stderr || result.stdout}` : `Session: ${result.sessionId}
|
|
1072
|
-
|
|
1073
|
-
${result.stdout}`;
|
|
1074
|
-
if (result.metadata) {
|
|
1075
|
-
text += `
|
|
1076
|
-
|
|
1077
|
-
<metadata>
|
|
1078
|
-
${JSON.stringify(result.metadata, null, 2)}
|
|
1079
|
-
</metadata>`;
|
|
1080
|
-
}
|
|
1161
|
+
const { text, isError } = formatSpawnAgentResponse(result);
|
|
1081
1162
|
return {
|
|
1082
1163
|
content: [{ type: "text", text }],
|
|
1083
1164
|
isError
|
|
@@ -1122,15 +1203,7 @@ ${JSON.stringify(result.metadata, null, 2)}
|
|
|
1122
1203
|
this._childHttpUrl,
|
|
1123
1204
|
onProgress
|
|
1124
1205
|
);
|
|
1125
|
-
const isError = result
|
|
1126
|
-
let text = "";
|
|
1127
|
-
if (result.hasConflicts) {
|
|
1128
|
-
text += "\u26A0 FILE CONFLICTS DETECTED: Multiple agents modified the same files.\n";
|
|
1129
|
-
text += `Conflicting files: ${result.conflicts.map((c) => c.path).join(", ")}
|
|
1130
|
-
|
|
1131
|
-
`;
|
|
1132
|
-
}
|
|
1133
|
-
text += JSON.stringify(result, null, 2);
|
|
1206
|
+
const { text, isError } = formatParallelResponse(result);
|
|
1134
1207
|
return {
|
|
1135
1208
|
content: [{ type: "text", text }],
|
|
1136
1209
|
isError
|
|
@@ -1151,11 +1224,24 @@ ${JSON.stringify(result.metadata, null, 2)}
|
|
|
1151
1224
|
failedResults: z5.array(z5.object({
|
|
1152
1225
|
index: z5.number(),
|
|
1153
1226
|
originalInput: spawnAgentInputSchema
|
|
1154
|
-
})).min(1).describe("Array of failed results with their original input configurations")
|
|
1227
|
+
})).min(1).describe("Array of failed results with their original input configurations"),
|
|
1228
|
+
overrides: z5.object({
|
|
1229
|
+
maxTurns: z5.number().optional(),
|
|
1230
|
+
timeoutMs: z5.number().optional(),
|
|
1231
|
+
preferredBackend: z5.enum(["claude", "codex", "gemini"]).optional()
|
|
1232
|
+
}).optional().describe("Parameter overrides applied to all retried agents")
|
|
1155
1233
|
},
|
|
1156
1234
|
async (params) => {
|
|
1157
1235
|
try {
|
|
1158
|
-
const agents = params.failedResults.map((r) =>
|
|
1236
|
+
const agents = params.failedResults.map((r) => {
|
|
1237
|
+
const input = { ...r.originalInput };
|
|
1238
|
+
if (params.overrides) {
|
|
1239
|
+
if (params.overrides.maxTurns !== void 0) input.maxTurns = params.overrides.maxTurns;
|
|
1240
|
+
if (params.overrides.timeoutMs !== void 0) input.timeoutMs = params.overrides.timeoutMs;
|
|
1241
|
+
if (params.overrides.preferredBackend !== void 0) input.preferredBackend = params.overrides.preferredBackend;
|
|
1242
|
+
}
|
|
1243
|
+
return input;
|
|
1244
|
+
});
|
|
1159
1245
|
const result = await executeSpawnAgentsParallel(
|
|
1160
1246
|
agents,
|
|
1161
1247
|
this.registry,
|
|
@@ -1166,8 +1252,7 @@ ${JSON.stringify(result.metadata, null, 2)}
|
|
|
1166
1252
|
this.backendSelector,
|
|
1167
1253
|
this._childHttpUrl
|
|
1168
1254
|
);
|
|
1169
|
-
const isError = result
|
|
1170
|
-
const text = JSON.stringify(result, null, 2);
|
|
1255
|
+
const { text, isError } = formatParallelResponse(result);
|
|
1171
1256
|
return {
|
|
1172
1257
|
content: [{ type: "text", text }],
|
|
1173
1258
|
isError
|
|
@@ -1331,7 +1416,7 @@ ${JSON.stringify(result.metadata, null, 2)}
|
|
|
1331
1416
|
});
|
|
1332
1417
|
const server = new McpServer({
|
|
1333
1418
|
name: "agentic-relay",
|
|
1334
|
-
version: "0.
|
|
1419
|
+
version: "0.9.0"
|
|
1335
1420
|
});
|
|
1336
1421
|
this.registerTools(server);
|
|
1337
1422
|
transport.onclose = () => {
|
|
@@ -1770,7 +1855,16 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
1770
1855
|
let sessionId = "";
|
|
1771
1856
|
let isError = false;
|
|
1772
1857
|
let errorMessages = [];
|
|
1858
|
+
let totalInputTokens = 0;
|
|
1859
|
+
let totalOutputTokens = 0;
|
|
1773
1860
|
for await (const message of q) {
|
|
1861
|
+
if (message.type === "assistant") {
|
|
1862
|
+
const betaMessage = message.message;
|
|
1863
|
+
if (betaMessage?.usage) {
|
|
1864
|
+
totalInputTokens += betaMessage.usage.input_tokens ?? 0;
|
|
1865
|
+
totalOutputTokens += betaMessage.usage.output_tokens ?? 0;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1774
1868
|
if (message.type === "result") {
|
|
1775
1869
|
sessionId = message.session_id;
|
|
1776
1870
|
if (message.subtype === "success") {
|
|
@@ -1782,11 +1876,13 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
1782
1876
|
}
|
|
1783
1877
|
}
|
|
1784
1878
|
logger.debug(`Claude SDK session: ${sessionId}`);
|
|
1879
|
+
const hasTokenUsage = totalInputTokens > 0 || totalOutputTokens > 0;
|
|
1785
1880
|
return {
|
|
1786
1881
|
exitCode: isError ? 1 : 0,
|
|
1787
1882
|
stdout: resultText,
|
|
1788
1883
|
stderr: errorMessages.join("\n"),
|
|
1789
|
-
...sessionId ? { nativeSessionId: sessionId } : {}
|
|
1884
|
+
...sessionId ? { nativeSessionId: sessionId } : {},
|
|
1885
|
+
...hasTokenUsage ? { tokenUsage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } } : {}
|
|
1790
1886
|
};
|
|
1791
1887
|
} catch (error) {
|
|
1792
1888
|
if (abortController.signal.aborted) {
|
|
@@ -4266,7 +4362,7 @@ function createVersionCommand(registry2) {
|
|
|
4266
4362
|
description: "Show relay and backend versions"
|
|
4267
4363
|
},
|
|
4268
4364
|
async run() {
|
|
4269
|
-
const relayVersion = "0.
|
|
4365
|
+
const relayVersion = "0.9.0";
|
|
4270
4366
|
console.log(`agentic-relay v${relayVersion}`);
|
|
4271
4367
|
console.log("");
|
|
4272
4368
|
console.log("Backends:");
|
|
@@ -4616,7 +4712,7 @@ void configManager.getConfig().then((config) => {
|
|
|
4616
4712
|
var main = defineCommand10({
|
|
4617
4713
|
meta: {
|
|
4618
4714
|
name: "relay",
|
|
4619
|
-
version: "0.
|
|
4715
|
+
version: "0.9.0",
|
|
4620
4716
|
description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
|
|
4621
4717
|
},
|
|
4622
4718
|
subCommands: {
|
package/package.json
CHANGED