@jacobbd/relay-ai 0.9.4 → 0.9.6
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/README.md +2 -0
- package/dist/{chunk-KDIY732Q.js → chunk-PYJQMEJD.js} +417 -365
- package/dist/chunk-PYJQMEJD.js.map +1 -0
- package/dist/cli.js +966 -235
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{ui-command-OIY4243G.js → ui-command-NYYLGTCD.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-KDIY732Q.js.map +0 -1
- /package/dist/{ui-command-OIY4243G.js.map → ui-command-NYYLGTCD.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
GLOBAL_OPENCODE_KEYRING_ACCOUNT,
|
|
13
13
|
MAX_MODEL_CATALOG,
|
|
14
14
|
PREVIEW_PROXY_PORT,
|
|
15
|
-
UNSUPPORTED_VOICE_MESSAGE,
|
|
16
15
|
VERSION,
|
|
17
16
|
VERTEX_ANTHROPIC_NPM,
|
|
18
17
|
addCustomEndpointProvider,
|
|
@@ -85,6 +84,7 @@ import {
|
|
|
85
84
|
getCodexProxyDebugLogPath,
|
|
86
85
|
getConfigPath,
|
|
87
86
|
getGeminiProxyDebugLogPath,
|
|
87
|
+
getLogsPath,
|
|
88
88
|
getProvidersPath,
|
|
89
89
|
getProxyDebugLogPath,
|
|
90
90
|
getReasoningCapabilities,
|
|
@@ -169,8 +169,9 @@ import {
|
|
|
169
169
|
resolveRelayCatalogSlots,
|
|
170
170
|
routableModelsForTarget,
|
|
171
171
|
routeLookupIds,
|
|
172
|
+
runCodexCommand,
|
|
173
|
+
runCodexCommandSync,
|
|
172
174
|
runServerCommand,
|
|
173
|
-
sanitizeUnsupportedInlineData,
|
|
174
175
|
savePreferences,
|
|
175
176
|
saveProviderCredential,
|
|
176
177
|
saveRegistry,
|
|
@@ -186,20 +187,18 @@ import {
|
|
|
186
187
|
startProxy,
|
|
187
188
|
startProxyCatalog,
|
|
188
189
|
startServer,
|
|
189
|
-
summarizeSdkRequestForTrace,
|
|
190
190
|
supportsClaudeTransparentMode,
|
|
191
191
|
supportsMultiAgentV2,
|
|
192
192
|
supportsNativeOAuth,
|
|
193
193
|
syntheticTemplate,
|
|
194
194
|
thinkingProviderOptions,
|
|
195
195
|
toggleProviderEnabled,
|
|
196
|
-
translateRequest,
|
|
197
196
|
updateCustomEndpointProvider,
|
|
198
197
|
upstreamHttpStatus,
|
|
199
198
|
validateCustomEndpointUrl,
|
|
200
199
|
writeSecureLogLine,
|
|
201
200
|
zenRegistryStub
|
|
202
|
-
} from "./chunk-
|
|
201
|
+
} from "./chunk-PYJQMEJD.js";
|
|
203
202
|
import {
|
|
204
203
|
filterTemplates,
|
|
205
204
|
getTemplateById,
|
|
@@ -1297,7 +1296,7 @@ ${pc4.bold("Subcommands:")}
|
|
|
1297
1296
|
(none) Provider hub wizard ${pc4.dim("[Phase 1.1]")}
|
|
1298
1297
|
add Add a provider (Groq, Mistral, Together AI, \u2026) ${pc4.dim("[Phase 1.1]")}
|
|
1299
1298
|
import Optional one-time import from OpenCode CLI ${pc4.dim("[Phase 1.0]")}
|
|
1300
|
-
auth Sign in with OAuth (GitHub Copilot, xAI, OpenAI, ClinePass)
|
|
1299
|
+
auth Sign in with OAuth (Antigravity, GitHub Copilot, xAI, OpenAI, ClinePass)
|
|
1301
1300
|
list Show configured providers ${pc4.dim("[Phase 1.0]")}
|
|
1302
1301
|
remove Remove a provider by id ${pc4.dim("[Phase 1.1]")}
|
|
1303
1302
|
refresh-models Update cached model lists ${pc4.dim("[Phase 1.2]")}`;
|
|
@@ -2235,8 +2234,7 @@ async function runProvidersCommand(args) {
|
|
|
2235
2234
|
// src/codex.ts
|
|
2236
2235
|
import pc7 from "picocolors";
|
|
2237
2236
|
import * as p8 from "@clack/prompts";
|
|
2238
|
-
import {
|
|
2239
|
-
import { join as join5 } from "path";
|
|
2237
|
+
import { join as join6 } from "path";
|
|
2240
2238
|
|
|
2241
2239
|
// src/codex-proxy.ts
|
|
2242
2240
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3041,18 +3039,18 @@ async function writeResponsesStream(fullStream, modelId, write, onDone, onProgre
|
|
|
3041
3039
|
});
|
|
3042
3040
|
outputItems.unshift(reasoningItem);
|
|
3043
3041
|
}
|
|
3044
|
-
for (const
|
|
3045
|
-
const normalizedArgs = normalizeCodexSubagentArguments(
|
|
3042
|
+
for (const tool4 of toolStates) {
|
|
3043
|
+
const normalizedArgs = normalizeCodexSubagentArguments(tool4.name, tool4.args);
|
|
3046
3044
|
emit("response.function_call_arguments.done", {
|
|
3047
3045
|
type: "response.function_call_arguments.done",
|
|
3048
|
-
item_id:
|
|
3049
|
-
output_index:
|
|
3046
|
+
item_id: tool4.itemId,
|
|
3047
|
+
output_index: tool4.outputIndex,
|
|
3050
3048
|
arguments: normalizedArgs
|
|
3051
3049
|
});
|
|
3052
|
-
const fcItem = buildFinalToolItem(resolveOutputKind(
|
|
3050
|
+
const fcItem = buildFinalToolItem(resolveOutputKind(tool4.name, options?.toolContext), tool4.name, tool4.callId, tool4.itemId, normalizedArgs);
|
|
3053
3051
|
emit("response.output_item.done", {
|
|
3054
3052
|
type: "response.output_item.done",
|
|
3055
|
-
output_index:
|
|
3053
|
+
output_index: tool4.outputIndex,
|
|
3056
3054
|
item: fcItem
|
|
3057
3055
|
});
|
|
3058
3056
|
outputItems.push(fcItem);
|
|
@@ -3180,7 +3178,7 @@ function decodeCompactionContent(encrypted) {
|
|
|
3180
3178
|
if (!encrypted) return null;
|
|
3181
3179
|
try {
|
|
3182
3180
|
const obj = JSON.parse(Buffer.from(encrypted, "base64").toString("utf8"));
|
|
3183
|
-
return typeof obj
|
|
3181
|
+
return obj?.v === 1 && typeof obj.summary === "string" ? obj.summary : null;
|
|
3184
3182
|
} catch {
|
|
3185
3183
|
return null;
|
|
3186
3184
|
}
|
|
@@ -3464,6 +3462,41 @@ function allowlistedNativeHeaders(inboundHeaders) {
|
|
|
3464
3462
|
}
|
|
3465
3463
|
return out;
|
|
3466
3464
|
}
|
|
3465
|
+
function prepareNativeCodexBody(body) {
|
|
3466
|
+
if (!Array.isArray(body.input)) return body;
|
|
3467
|
+
let changed = false;
|
|
3468
|
+
const input = body.input.map((item) => {
|
|
3469
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
3470
|
+
const record = item;
|
|
3471
|
+
if (record.type !== "compaction" && record.type !== "context_compaction") return item;
|
|
3472
|
+
const summary = decodeCompactionContent(
|
|
3473
|
+
typeof record.encrypted_content === "string" ? record.encrypted_content : void 0
|
|
3474
|
+
);
|
|
3475
|
+
if (summary === null) return item;
|
|
3476
|
+
changed = true;
|
|
3477
|
+
return {
|
|
3478
|
+
type: "message",
|
|
3479
|
+
role: "user",
|
|
3480
|
+
content: [{
|
|
3481
|
+
type: "input_text",
|
|
3482
|
+
text: `[Summary of earlier conversation]
|
|
3483
|
+
${summary}`
|
|
3484
|
+
}]
|
|
3485
|
+
};
|
|
3486
|
+
});
|
|
3487
|
+
return changed ? { ...body, input } : body;
|
|
3488
|
+
}
|
|
3489
|
+
function prepareNativeHttpBody(body) {
|
|
3490
|
+
const text5 = typeof body === "string" ? body : Buffer.from(body).toString("utf8");
|
|
3491
|
+
try {
|
|
3492
|
+
const parsed = JSON.parse(text5);
|
|
3493
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return body;
|
|
3494
|
+
const prepared = prepareNativeCodexBody(parsed);
|
|
3495
|
+
return prepared === parsed ? body : JSON.stringify(prepared);
|
|
3496
|
+
} catch {
|
|
3497
|
+
return body;
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3467
3500
|
async function forwardNativeCodexHttp(options) {
|
|
3468
3501
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3469
3502
|
const headers = allowlistedNativeHeaders(options.inboundHeaders);
|
|
@@ -3471,7 +3504,7 @@ async function forwardNativeCodexHttp(options) {
|
|
|
3471
3504
|
return fetchImpl(options.nativeUrl ?? NATIVE_CODEX_RESPONSES_URL, {
|
|
3472
3505
|
method: "POST",
|
|
3473
3506
|
headers,
|
|
3474
|
-
body: options.body,
|
|
3507
|
+
body: prepareNativeHttpBody(options.body),
|
|
3475
3508
|
signal: options.signal,
|
|
3476
3509
|
redirect: "manual"
|
|
3477
3510
|
});
|
|
@@ -3549,17 +3582,17 @@ var COLLABORATION_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
3549
3582
|
]);
|
|
3550
3583
|
function isCollaborationTool(value) {
|
|
3551
3584
|
if (!value || typeof value !== "object") return false;
|
|
3552
|
-
const
|
|
3553
|
-
const name = typeof
|
|
3554
|
-
if (
|
|
3585
|
+
const tool4 = value;
|
|
3586
|
+
const name = typeof tool4.name === "string" ? tool4.name : "";
|
|
3587
|
+
if (tool4.type === "namespace") return name === "collaboration" || name === "multi_agent_v1";
|
|
3555
3588
|
return COLLABORATION_TOOL_NAMES.has(name) || name.startsWith("collaboration__") || name.startsWith("multi_agent_v1__");
|
|
3556
3589
|
}
|
|
3557
3590
|
function stripCollaborationToolList(value) {
|
|
3558
3591
|
if (!Array.isArray(value)) return value;
|
|
3559
|
-
return value.filter((
|
|
3560
|
-
if (!
|
|
3561
|
-
const record =
|
|
3562
|
-
if (!Array.isArray(record.tools)) return
|
|
3592
|
+
return value.filter((tool4) => !isCollaborationTool(tool4)).map((tool4) => {
|
|
3593
|
+
if (!tool4 || typeof tool4 !== "object") return tool4;
|
|
3594
|
+
const record = tool4;
|
|
3595
|
+
if (!Array.isArray(record.tools)) return tool4;
|
|
3563
3596
|
return { ...record, tools: stripCollaborationToolList(record.tools) };
|
|
3564
3597
|
});
|
|
3565
3598
|
}
|
|
@@ -3738,6 +3771,53 @@ async function resolveRoutedCollaborationInput(input, context) {
|
|
|
3738
3771
|
return normalizePlaintextCollaborationForExternal(out);
|
|
3739
3772
|
}
|
|
3740
3773
|
|
|
3774
|
+
// src/codex/route-audit.ts
|
|
3775
|
+
import { chmodSync, mkdirSync, writeFileSync } from "fs";
|
|
3776
|
+
import { join as join2 } from "path";
|
|
3777
|
+
var DIR_MODE = 448;
|
|
3778
|
+
var FILE_MODE = 384;
|
|
3779
|
+
var CODEX_ROUTE_AUDIT_LOG = "codex-route-audit.jsonl";
|
|
3780
|
+
function safeIdentifier(value) {
|
|
3781
|
+
if (value === void 0) return void 0;
|
|
3782
|
+
return value.replace(/[\u0000-\u001f\u007f]/g, "_").slice(0, 300);
|
|
3783
|
+
}
|
|
3784
|
+
function sanitizeCodexRouteAuditEvent(event) {
|
|
3785
|
+
return {
|
|
3786
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3787
|
+
transport: event.transport,
|
|
3788
|
+
requestedModel: safeIdentifier(event.requestedModel),
|
|
3789
|
+
dispatch: event.dispatch,
|
|
3790
|
+
phase: event.phase,
|
|
3791
|
+
...event.provider ? { provider: safeIdentifier(event.provider) } : {},
|
|
3792
|
+
...event.routeModel ? { routeModel: safeIdentifier(event.routeModel) } : {},
|
|
3793
|
+
...event.upstreamModel ? { upstreamModel: safeIdentifier(event.upstreamModel) } : {},
|
|
3794
|
+
...event.outcome ? { outcome: event.outcome } : {},
|
|
3795
|
+
...event.status !== void 0 ? { status: typeof event.status === "string" ? safeIdentifier(event.status) : event.status } : {}
|
|
3796
|
+
};
|
|
3797
|
+
}
|
|
3798
|
+
function getCodexRouteAuditLogPath() {
|
|
3799
|
+
const dir = getLogsPath();
|
|
3800
|
+
mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
3801
|
+
try {
|
|
3802
|
+
chmodSync(dir, DIR_MODE);
|
|
3803
|
+
} catch {
|
|
3804
|
+
}
|
|
3805
|
+
return join2(dir, CODEX_ROUTE_AUDIT_LOG);
|
|
3806
|
+
}
|
|
3807
|
+
function prepareCodexRouteAuditLog(path3 = getCodexRouteAuditLogPath()) {
|
|
3808
|
+
writeFileSync(path3, "", { mode: FILE_MODE });
|
|
3809
|
+
chmodSync(path3, FILE_MODE);
|
|
3810
|
+
return path3;
|
|
3811
|
+
}
|
|
3812
|
+
function appendCodexRouteAudit(path3, event) {
|
|
3813
|
+
try {
|
|
3814
|
+
writeFileSync(path3, `${JSON.stringify(sanitizeCodexRouteAuditEvent(event))}
|
|
3815
|
+
`, { flag: "a", mode: FILE_MODE });
|
|
3816
|
+
chmodSync(path3, FILE_MODE);
|
|
3817
|
+
} catch {
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3741
3821
|
// src/codex-proxy.ts
|
|
3742
3822
|
function captureCompletedResponse(sseText) {
|
|
3743
3823
|
if (!sseText.includes("response.completed")) return void 0;
|
|
@@ -3745,11 +3825,34 @@ function captureCompletedResponse(sseText) {
|
|
|
3745
3825
|
if (!dataLine) return void 0;
|
|
3746
3826
|
try {
|
|
3747
3827
|
const obj = JSON.parse(dataLine.slice(5).trim());
|
|
3748
|
-
if (obj && obj.type === "response.completed"
|
|
3828
|
+
if (obj && obj.type === "response.completed" && obj.response && typeof obj.response === "object") {
|
|
3829
|
+
return obj.response;
|
|
3830
|
+
}
|
|
3749
3831
|
} catch {
|
|
3750
3832
|
}
|
|
3751
3833
|
return void 0;
|
|
3752
3834
|
}
|
|
3835
|
+
var MAX_EXTERNAL_RESPONSE_STATES = 8;
|
|
3836
|
+
var EXTERNAL_TOOL_OUTPUT_TYPES = /* @__PURE__ */ new Set([
|
|
3837
|
+
"function_call_output",
|
|
3838
|
+
"custom_tool_call_output",
|
|
3839
|
+
"tool_search_output"
|
|
3840
|
+
]);
|
|
3841
|
+
function responsesInputItems(input) {
|
|
3842
|
+
if (Array.isArray(input)) return input;
|
|
3843
|
+
if (typeof input === "string") {
|
|
3844
|
+
return [{ type: "message", role: "user", content: input }];
|
|
3845
|
+
}
|
|
3846
|
+
return [];
|
|
3847
|
+
}
|
|
3848
|
+
function isExternalToolOutputItem(item) {
|
|
3849
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
|
|
3850
|
+
const type = item.type;
|
|
3851
|
+
return typeof type === "string" && EXTERNAL_TOOL_OUTPUT_TYPES.has(type);
|
|
3852
|
+
}
|
|
3853
|
+
function isExternalToolContinuation(input) {
|
|
3854
|
+
return Array.isArray(input) && input.length > 0 && input.every(isExternalToolOutputItem);
|
|
3855
|
+
}
|
|
3753
3856
|
function estimateCodexRequestChars(params) {
|
|
3754
3857
|
let chars = (params.system ?? "").length;
|
|
3755
3858
|
for (const msg of params.messages) {
|
|
@@ -3881,6 +3984,18 @@ function codexRouteLookupIds(requestedModel) {
|
|
|
3881
3984
|
return [...new Set(ids)];
|
|
3882
3985
|
}
|
|
3883
3986
|
function findCodexProxyRoute(routes, requestedModel) {
|
|
3987
|
+
const bareRequestedModel = parseCodexAppModelSlug(requestedModel);
|
|
3988
|
+
const providerSeparator = bareRequestedModel.indexOf("__");
|
|
3989
|
+
if (providerSeparator > 0) {
|
|
3990
|
+
const requestedProvider = bareRequestedModel.slice(0, providerSeparator);
|
|
3991
|
+
const requestedIds = codexRouteLookupIds(bareRequestedModel.slice(providerSeparator + 2));
|
|
3992
|
+
const providerRoute = routes.find((route) => {
|
|
3993
|
+
if (route.providerId !== requestedProvider) return false;
|
|
3994
|
+
const routeIds = codexRouteLookupIds(route.modelId);
|
|
3995
|
+
return requestedIds.some((id) => routeIds.includes(id));
|
|
3996
|
+
});
|
|
3997
|
+
if (providerRoute) return providerRoute;
|
|
3998
|
+
}
|
|
3884
3999
|
const ids = codexRouteLookupIds(requestedModel);
|
|
3885
4000
|
for (const id of ids) {
|
|
3886
4001
|
const route = routes.find(
|
|
@@ -3936,11 +4051,32 @@ async function prepareExternalCodexBody(body, context) {
|
|
|
3936
4051
|
);
|
|
3937
4052
|
return { ...externalBody, input: resolvedInput };
|
|
3938
4053
|
}
|
|
4054
|
+
function applyExternalCodexRuntimeIdentity(params, route) {
|
|
4055
|
+
const selectedModel = route.auditUpstreamModelId ?? route.upstreamModelId ?? route.modelId;
|
|
4056
|
+
const provider = route.providerId ?? "relay";
|
|
4057
|
+
const identity = [
|
|
4058
|
+
"<external-model-identity>",
|
|
4059
|
+
`The selected model for this turn is ${JSON.stringify(selectedModel)} through provider ${JSON.stringify(provider)}.`,
|
|
4060
|
+
"Codex is the host application and agent environment, not the model identity.",
|
|
4061
|
+
"Follow Codex host and tool instructions normally, but do not infer that you are an OpenAI or GPT model from host names, tool names, documentation, or conversation context.",
|
|
4062
|
+
"If asked what model you are, report the selected model and provider above; do not use self-identification as evidence of the network route.",
|
|
4063
|
+
"</external-model-identity>"
|
|
4064
|
+
].join("\n");
|
|
4065
|
+
return {
|
|
4066
|
+
...params,
|
|
4067
|
+
system: params.system?.trim() ? `${identity}
|
|
4068
|
+
|
|
4069
|
+
${params.system}` : identity
|
|
4070
|
+
};
|
|
4071
|
+
}
|
|
3939
4072
|
async function startCodexProxy(routes, options = {}) {
|
|
3940
4073
|
const opts = typeof options === "boolean" ? { debug: options } : options;
|
|
3941
4074
|
const debug = opts.debug ?? false;
|
|
3942
4075
|
const requireAuth = opts.requireAuth ?? true;
|
|
3943
4076
|
const mixedNative = opts.mixedNative;
|
|
4077
|
+
const audit = (event) => {
|
|
4078
|
+
if (opts.routeAuditPath) appendCodexRouteAudit(opts.routeAuditPath, event);
|
|
4079
|
+
};
|
|
3944
4080
|
const nativePayloadRelay = mixedNative ? createNativePayloadRelay({}) : void 0;
|
|
3945
4081
|
silenceSdkWarnings();
|
|
3946
4082
|
const models = /* @__PURE__ */ new Map();
|
|
@@ -4114,6 +4250,7 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4114
4250
|
log14(`subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4115
4251
|
}
|
|
4116
4252
|
if (mixedNative && markedSubagent && !subagentRoute) {
|
|
4253
|
+
audit({ transport: "http", requestedModel: modelId, dispatch: "relay-subagent", phase: "complete", outcome: "error", status: 503 });
|
|
4117
4254
|
sendJson(res, 503, {
|
|
4118
4255
|
error: {
|
|
4119
4256
|
message: "Codex marked this request as a Sub-agent, but no configured Codex Sub-agent route is available.",
|
|
@@ -4126,10 +4263,20 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4126
4263
|
if (!markedSubagent) {
|
|
4127
4264
|
const dispatch = classifyCodexDispatch(modelId, routes, mixedNative.nativeModelIds);
|
|
4128
4265
|
if (dispatch.kind === "unknown") {
|
|
4266
|
+
audit({ transport: "http", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4129
4267
|
sendJson(res, 404, { error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } });
|
|
4130
4268
|
return;
|
|
4131
4269
|
}
|
|
4132
4270
|
if (dispatch.kind === "native") {
|
|
4271
|
+
audit({
|
|
4272
|
+
transport: "http",
|
|
4273
|
+
requestedModel: modelId,
|
|
4274
|
+
dispatch: "native",
|
|
4275
|
+
phase: "dispatch",
|
|
4276
|
+
provider: "openai-native",
|
|
4277
|
+
routeModel: modelId,
|
|
4278
|
+
upstreamModel: modelId
|
|
4279
|
+
});
|
|
4133
4280
|
const controller = new AbortController();
|
|
4134
4281
|
req.once("aborted", () => controller.abort());
|
|
4135
4282
|
try {
|
|
@@ -4143,7 +4290,29 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4143
4290
|
const contentType = nativeResponse.headers.get("content-type");
|
|
4144
4291
|
res.writeHead(nativeResponse.status, contentType ? { "content-type": contentType } : void 0);
|
|
4145
4292
|
res.end(Buffer.from(await nativeResponse.arrayBuffer()));
|
|
4293
|
+
audit({
|
|
4294
|
+
transport: "http",
|
|
4295
|
+
requestedModel: modelId,
|
|
4296
|
+
dispatch: "native",
|
|
4297
|
+
phase: "complete",
|
|
4298
|
+
provider: "openai-native",
|
|
4299
|
+
routeModel: modelId,
|
|
4300
|
+
upstreamModel: modelId,
|
|
4301
|
+
outcome: nativeResponse.ok ? "ok" : "error",
|
|
4302
|
+
status: nativeResponse.status
|
|
4303
|
+
});
|
|
4146
4304
|
} catch (err) {
|
|
4305
|
+
audit({
|
|
4306
|
+
transport: "http",
|
|
4307
|
+
requestedModel: modelId,
|
|
4308
|
+
dispatch: "native",
|
|
4309
|
+
phase: "complete",
|
|
4310
|
+
provider: "openai-native",
|
|
4311
|
+
routeModel: modelId,
|
|
4312
|
+
upstreamModel: modelId,
|
|
4313
|
+
outcome: "error",
|
|
4314
|
+
status: "forward-failed"
|
|
4315
|
+
});
|
|
4147
4316
|
if (!res.writableEnded) sendJson(res, 502, { error: { message: "Native Codex request failed", type: "upstream_error" } });
|
|
4148
4317
|
}
|
|
4149
4318
|
return;
|
|
@@ -4168,13 +4337,23 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4168
4337
|
}
|
|
4169
4338
|
}
|
|
4170
4339
|
const { route, languageModel } = resolved;
|
|
4340
|
+
const relayDispatch = markedSubagent ? "relay-subagent" : "relay";
|
|
4341
|
+
audit({
|
|
4342
|
+
transport: "http",
|
|
4343
|
+
requestedModel: modelId,
|
|
4344
|
+
dispatch: relayDispatch,
|
|
4345
|
+
phase: "dispatch",
|
|
4346
|
+
provider: route.providerId ?? "relay",
|
|
4347
|
+
routeModel: route.modelId,
|
|
4348
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId
|
|
4349
|
+
});
|
|
4171
4350
|
try {
|
|
4172
4351
|
const routedBody = await prepareExternalCodexBody(body, {
|
|
4173
4352
|
relay: nativePayloadRelay,
|
|
4174
4353
|
mixedNative,
|
|
4175
4354
|
headers: req.headers
|
|
4176
4355
|
});
|
|
4177
|
-
let params = applyClaudeCodeOAuthIdentity(route, translateResponsesRequest(
|
|
4356
|
+
let params = applyClaudeCodeOAuthIdentity(route, applyExternalCodexRuntimeIdentity(translateResponsesRequest(
|
|
4178
4357
|
routedBody,
|
|
4179
4358
|
route.npm,
|
|
4180
4359
|
{
|
|
@@ -4186,7 +4365,7 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4186
4365
|
upstreamModelId: route.upstreamModelId
|
|
4187
4366
|
},
|
|
4188
4367
|
{ maxTools: maxToolsForNpm(route.npm) }
|
|
4189
|
-
));
|
|
4368
|
+
), route));
|
|
4190
4369
|
if (route.contextWindow && route.contextWindow > 0) {
|
|
4191
4370
|
const before = params.messages.length;
|
|
4192
4371
|
const estimatedChars = estimateCodexRequestChars(params);
|
|
@@ -4241,9 +4420,31 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4241
4420
|
log14(`response progress: model=${route.modelId} elapsedMs=${progress.elapsedMs} reasoningChars=${progress.reasoningChars} textChars=${progress.textChars} toolCalls=${progress.toolCallCount} reasoningTail=${JSON.stringify(progress.reasoningTail)}`);
|
|
4242
4421
|
}
|
|
4243
4422
|
});
|
|
4423
|
+
audit({
|
|
4424
|
+
transport: "http",
|
|
4425
|
+
requestedModel: modelId,
|
|
4426
|
+
dispatch: relayDispatch,
|
|
4427
|
+
phase: "complete",
|
|
4428
|
+
provider: route.providerId ?? "relay",
|
|
4429
|
+
routeModel: route.modelId,
|
|
4430
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4431
|
+
outcome: "ok",
|
|
4432
|
+
status: 200
|
|
4433
|
+
});
|
|
4244
4434
|
} catch (err) {
|
|
4245
4435
|
const msg = formatUpstreamError(err);
|
|
4246
4436
|
const status = upstreamHttpStatus(err, msg);
|
|
4437
|
+
audit({
|
|
4438
|
+
transport: "http",
|
|
4439
|
+
requestedModel: modelId,
|
|
4440
|
+
dispatch: relayDispatch,
|
|
4441
|
+
phase: "complete",
|
|
4442
|
+
provider: route.providerId ?? "relay",
|
|
4443
|
+
routeModel: route.modelId,
|
|
4444
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4445
|
+
outcome: "error",
|
|
4446
|
+
status
|
|
4447
|
+
});
|
|
4247
4448
|
if (debug) log14(`sdk error: ${route.modelId}: ${msg}`);
|
|
4248
4449
|
if (status === 429) {
|
|
4249
4450
|
writeResponsesRateLimitStream(modelId, msg, write);
|
|
@@ -4265,9 +4466,31 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4265
4466
|
});
|
|
4266
4467
|
}
|
|
4267
4468
|
sendJson(res, 200, response);
|
|
4469
|
+
audit({
|
|
4470
|
+
transport: "http",
|
|
4471
|
+
requestedModel: modelId,
|
|
4472
|
+
dispatch: relayDispatch,
|
|
4473
|
+
phase: "complete",
|
|
4474
|
+
provider: route.providerId ?? "relay",
|
|
4475
|
+
routeModel: route.modelId,
|
|
4476
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4477
|
+
outcome: "ok",
|
|
4478
|
+
status: 200
|
|
4479
|
+
});
|
|
4268
4480
|
} catch (err) {
|
|
4269
4481
|
const msg = formatUpstreamError(err);
|
|
4270
4482
|
const status = upstreamHttpStatus(err, msg);
|
|
4483
|
+
audit({
|
|
4484
|
+
transport: "http",
|
|
4485
|
+
requestedModel: modelId,
|
|
4486
|
+
dispatch: relayDispatch,
|
|
4487
|
+
phase: "complete",
|
|
4488
|
+
provider: route.providerId ?? "relay",
|
|
4489
|
+
routeModel: route.modelId,
|
|
4490
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4491
|
+
outcome: "error",
|
|
4492
|
+
status
|
|
4493
|
+
});
|
|
4271
4494
|
if (debug) log14(`sdk error: ${route.modelId}: ${msg}`);
|
|
4272
4495
|
if (status === 429) {
|
|
4273
4496
|
sendJson(res, 200, responsesRateLimitBody(modelId, msg));
|
|
@@ -4392,21 +4615,53 @@ Sec-WebSocket-Accept: ${wsAcceptKey(clientKey)}\r
|
|
|
4392
4615
|
`
|
|
4393
4616
|
);
|
|
4394
4617
|
let frameBuf = Buffer.alloc(0);
|
|
4395
|
-
let
|
|
4618
|
+
let externalActive = false;
|
|
4396
4619
|
let nativeActive = false;
|
|
4397
4620
|
let nativeUpstream;
|
|
4621
|
+
let nativeSendTurn;
|
|
4622
|
+
let socketClosing = false;
|
|
4623
|
+
const externalResponseStates = /* @__PURE__ */ new Map();
|
|
4624
|
+
let currentExternalCompletedResponse;
|
|
4625
|
+
let currentExternalStateInput;
|
|
4626
|
+
let currentExternalConsumedResponseId;
|
|
4398
4627
|
let currentRequestModel = "";
|
|
4399
|
-
const
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4628
|
+
const rememberExternalResponse = (response, input) => {
|
|
4629
|
+
const responseId = typeof response.id === "string" ? response.id : void 0;
|
|
4630
|
+
const output = Array.isArray(response.output) ? response.output : void 0;
|
|
4631
|
+
if (!responseId || !output || response.error) return;
|
|
4632
|
+
externalResponseStates.delete(responseId);
|
|
4633
|
+
externalResponseStates.set(responseId, { input: [...input], output: [...output] });
|
|
4634
|
+
while (externalResponseStates.size > MAX_EXTERNAL_RESPONSE_STATES) {
|
|
4635
|
+
const oldest = externalResponseStates.keys().next().value;
|
|
4636
|
+
if (!oldest) break;
|
|
4637
|
+
externalResponseStates.delete(oldest);
|
|
4403
4638
|
}
|
|
4404
4639
|
};
|
|
4640
|
+
const resolveExternalContinuation = (body) => {
|
|
4641
|
+
const previousResponseId = typeof body.previous_response_id === "string" ? body.previous_response_id : void 0;
|
|
4642
|
+
if (!previousResponseId || !isExternalToolContinuation(body.input)) return { body };
|
|
4643
|
+
const previous = externalResponseStates.get(previousResponseId);
|
|
4644
|
+
if (!previous) return { body, orphanedResponseId: previousResponseId };
|
|
4645
|
+
return {
|
|
4646
|
+
body: {
|
|
4647
|
+
...body,
|
|
4648
|
+
input: [...previous.input, ...previous.output, ...body.input]
|
|
4649
|
+
},
|
|
4650
|
+
consumedResponseId: previousResponseId
|
|
4651
|
+
};
|
|
4652
|
+
};
|
|
4653
|
+
const closeSocket = (code = 1e3) => {
|
|
4654
|
+
if (socketClosing || socket.destroyed) return;
|
|
4655
|
+
socketClosing = true;
|
|
4656
|
+
socket.write(wsCloseFrame(code));
|
|
4657
|
+
socket.end();
|
|
4658
|
+
};
|
|
4405
4659
|
const sendWsEvent = (sseChunk2) => {
|
|
4406
|
-
if (socket.destroyed) return;
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4660
|
+
if (socketClosing || socket.destroyed) return;
|
|
4661
|
+
const completed = captureCompletedResponse(sseChunk2);
|
|
4662
|
+
if (completed) {
|
|
4663
|
+
currentExternalCompletedResponse = completed;
|
|
4664
|
+
if (debug) {
|
|
4410
4665
|
appendCodexBodyDump({
|
|
4411
4666
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4412
4667
|
transport: "ws",
|
|
@@ -4424,7 +4679,6 @@ Sec-WebSocket-Accept: ${wsAcceptKey(clientKey)}\r
|
|
|
4424
4679
|
};
|
|
4425
4680
|
const onData = (chunk) => {
|
|
4426
4681
|
frameBuf = Buffer.concat([frameBuf, chunk]);
|
|
4427
|
-
if (handled && !nativeActive) return;
|
|
4428
4682
|
const frame = wsDecodeFrame(frameBuf);
|
|
4429
4683
|
if (!frame) return;
|
|
4430
4684
|
frameBuf = Buffer.alloc(0);
|
|
@@ -4446,7 +4700,10 @@ Sec-WebSocket-Accept: ${wsAcceptKey(clientKey)}\r
|
|
|
4446
4700
|
socket.end();
|
|
4447
4701
|
return;
|
|
4448
4702
|
}
|
|
4449
|
-
|
|
4703
|
+
if (externalActive) {
|
|
4704
|
+
closeSocket(1008);
|
|
4705
|
+
return;
|
|
4706
|
+
}
|
|
4450
4707
|
void (async () => {
|
|
4451
4708
|
let body;
|
|
4452
4709
|
try {
|
|
@@ -4487,6 +4744,7 @@ data: ${JSON.stringify({ error: { message: "Invalid JSON", type: "invalid_reques
|
|
|
4487
4744
|
log14(`WS subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4488
4745
|
}
|
|
4489
4746
|
if (mixedNative && markedSubagent && !subagentRoute) {
|
|
4747
|
+
audit({ transport: "ws", requestedModel: modelId, dispatch: "relay-subagent", phase: "complete", outcome: "error", status: 503 });
|
|
4490
4748
|
sendWsEvent(`event: error
|
|
4491
4749
|
data: ${JSON.stringify({ error: {
|
|
4492
4750
|
message: "Codex marked this request as a Sub-agent, but no configured Codex Sub-agent route is available.",
|
|
@@ -4501,6 +4759,7 @@ data: ${JSON.stringify({ error: {
|
|
|
4501
4759
|
if (!markedSubagent) {
|
|
4502
4760
|
const dispatch = classifyCodexDispatch(modelId, routes, mixedNative.nativeModelIds);
|
|
4503
4761
|
if (dispatch.kind === "unknown") {
|
|
4762
|
+
audit({ transport: "ws", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4504
4763
|
sendWsEvent(`event: error
|
|
4505
4764
|
data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } })}
|
|
4506
4765
|
|
|
@@ -4509,10 +4768,23 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4509
4768
|
return;
|
|
4510
4769
|
}
|
|
4511
4770
|
if (dispatch.kind === "native") {
|
|
4771
|
+
audit({
|
|
4772
|
+
transport: "ws",
|
|
4773
|
+
requestedModel: modelId,
|
|
4774
|
+
dispatch: "native",
|
|
4775
|
+
phase: "dispatch",
|
|
4776
|
+
provider: "openai-native",
|
|
4777
|
+
routeModel: modelId,
|
|
4778
|
+
upstreamModel: modelId
|
|
4779
|
+
});
|
|
4780
|
+
const nativeBody = prepareNativeCodexBody(body);
|
|
4781
|
+
if (debug && nativeBody !== body) {
|
|
4782
|
+
log14(`WS native history normalized: model=${modelId} converted Relay compaction for native verification`);
|
|
4783
|
+
}
|
|
4512
4784
|
if (nativeActive && nativeUpstream) {
|
|
4513
4785
|
if (nativeUpstream.readyState === WebSocket.OPEN) {
|
|
4514
4786
|
if (debug) log14(`WS native forwarding next turn: model=${modelId}`);
|
|
4515
|
-
|
|
4787
|
+
nativeSendTurn?.(nativeBody, modelId);
|
|
4516
4788
|
} else if (debug) {
|
|
4517
4789
|
log14(`WS native cannot forward next turn: upstream_state=${nativeUpstream.readyState}`);
|
|
4518
4790
|
}
|
|
@@ -4523,6 +4795,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4523
4795
|
let upstream;
|
|
4524
4796
|
let nativeOpened = false;
|
|
4525
4797
|
let nativeCompleted = false;
|
|
4798
|
+
let nativeTurnModelId = modelId;
|
|
4526
4799
|
let nativeFrameCount = 0;
|
|
4527
4800
|
let finished = false;
|
|
4528
4801
|
let connectTimer;
|
|
@@ -4542,10 +4815,24 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4542
4815
|
if (finished) return;
|
|
4543
4816
|
finished = true;
|
|
4544
4817
|
nativeActive = false;
|
|
4818
|
+
nativeSendTurn = void 0;
|
|
4545
4819
|
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4546
4820
|
clearTimers();
|
|
4547
4821
|
if (debug && message) {
|
|
4548
|
-
log14(`WS native upstream failed: model=${
|
|
4822
|
+
log14(`WS native upstream failed: model=${nativeTurnModelId} opened=${nativeOpened} frames=${nativeFrameCount} message=${message}`);
|
|
4823
|
+
}
|
|
4824
|
+
if (message && !nativeCompleted) {
|
|
4825
|
+
audit({
|
|
4826
|
+
transport: "ws",
|
|
4827
|
+
requestedModel: nativeTurnModelId,
|
|
4828
|
+
dispatch: "native",
|
|
4829
|
+
phase: "complete",
|
|
4830
|
+
provider: "openai-native",
|
|
4831
|
+
routeModel: nativeTurnModelId,
|
|
4832
|
+
upstreamModel: nativeTurnModelId,
|
|
4833
|
+
outcome: "error",
|
|
4834
|
+
status: "upstream-failed"
|
|
4835
|
+
});
|
|
4549
4836
|
}
|
|
4550
4837
|
if (message && !nativeCompleted) sendNativeError(message);
|
|
4551
4838
|
try {
|
|
@@ -4554,20 +4841,31 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4554
4841
|
}
|
|
4555
4842
|
closeSocket(closeCode);
|
|
4556
4843
|
};
|
|
4844
|
+
const sendNativeTurn = (turnBody, turnModelId) => {
|
|
4845
|
+
if (!upstream || upstream.readyState !== WebSocket.OPEN) {
|
|
4846
|
+
if (debug) log14(`WS native cannot send turn: model=${turnModelId} upstream_state=${upstream?.readyState ?? "missing"}`);
|
|
4847
|
+
return;
|
|
4848
|
+
}
|
|
4849
|
+
nativeTurnModelId = turnModelId;
|
|
4850
|
+
nativeCompleted = false;
|
|
4851
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4852
|
+
upstream.send(JSON.stringify({ type: "response.create", ...turnBody }));
|
|
4853
|
+
firstFrameTimer = setTimeout(() => closeBoth("Native Codex WebSocket response timed out"), 6e4);
|
|
4854
|
+
};
|
|
4557
4855
|
try {
|
|
4558
4856
|
if (debug) {
|
|
4559
4857
|
log14(`WS native connecting: model=${modelId} url=${target.url} headers=[${Object.keys(target.headers).join(",")}]`);
|
|
4560
4858
|
}
|
|
4561
4859
|
upstream = new WebSocket(target.url, { headers: target.headers });
|
|
4562
4860
|
nativeUpstream = upstream;
|
|
4861
|
+
nativeSendTurn = sendNativeTurn;
|
|
4563
4862
|
nativeActive = true;
|
|
4564
4863
|
connectTimer = setTimeout(() => closeBoth("Native Codex WebSocket connection timed out"), 15e3);
|
|
4565
4864
|
upstream.once("open", () => {
|
|
4566
4865
|
nativeOpened = true;
|
|
4567
4866
|
if (connectTimer) clearTimeout(connectTimer);
|
|
4568
4867
|
if (debug) log14(`WS native upstream open: model=${modelId}`);
|
|
4569
|
-
|
|
4570
|
-
firstFrameTimer = setTimeout(() => closeBoth("Native Codex WebSocket response timed out"), 6e4);
|
|
4868
|
+
sendNativeTurn(nativeBody, modelId);
|
|
4571
4869
|
});
|
|
4572
4870
|
upstream.once("unexpected-response", (_request, response) => {
|
|
4573
4871
|
if (debug) log14(`WS native upstream HTTP rejection: model=${modelId} status=${response.statusCode}`);
|
|
@@ -4585,6 +4883,17 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4585
4883
|
if (typeof parsed.type === "string") eventType = parsed.type;
|
|
4586
4884
|
if (eventType === "response.completed" || eventType === "response.failed" || eventType === "response.incomplete") {
|
|
4587
4885
|
nativeCompleted = true;
|
|
4886
|
+
audit({
|
|
4887
|
+
transport: "ws",
|
|
4888
|
+
requestedModel: modelId,
|
|
4889
|
+
dispatch: "native",
|
|
4890
|
+
phase: "complete",
|
|
4891
|
+
provider: "openai-native",
|
|
4892
|
+
routeModel: modelId,
|
|
4893
|
+
upstreamModel: modelId,
|
|
4894
|
+
outcome: eventType === "response.completed" ? "ok" : "error",
|
|
4895
|
+
status: eventType
|
|
4896
|
+
});
|
|
4588
4897
|
}
|
|
4589
4898
|
} catch {
|
|
4590
4899
|
}
|
|
@@ -4605,6 +4914,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4605
4914
|
if (debug) log14(`WS native downstream close: model=${modelId} frames=${nativeFrameCount} completed=${nativeCompleted}`);
|
|
4606
4915
|
finished = true;
|
|
4607
4916
|
nativeActive = false;
|
|
4917
|
+
nativeSendTurn = void 0;
|
|
4608
4918
|
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4609
4919
|
clearTimers();
|
|
4610
4920
|
try {
|
|
@@ -4619,6 +4929,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "i
|
|
|
4619
4929
|
}
|
|
4620
4930
|
}
|
|
4621
4931
|
}
|
|
4932
|
+
externalActive = true;
|
|
4622
4933
|
let resolved = subagentRoute ? resolveModel(routes, models, subagentRoute.modelId) : resolveModel(routes, models, modelId);
|
|
4623
4934
|
if (!resolved) {
|
|
4624
4935
|
const fb = routes[0];
|
|
@@ -4637,13 +4948,35 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
4637
4948
|
}
|
|
4638
4949
|
}
|
|
4639
4950
|
const { route, languageModel } = resolved;
|
|
4951
|
+
const relayDispatch = markedSubagent ? "relay-subagent" : "relay";
|
|
4952
|
+
audit({
|
|
4953
|
+
transport: "ws",
|
|
4954
|
+
requestedModel: modelId,
|
|
4955
|
+
dispatch: relayDispatch,
|
|
4956
|
+
phase: "dispatch",
|
|
4957
|
+
provider: route.providerId ?? "relay",
|
|
4958
|
+
routeModel: route.modelId,
|
|
4959
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId
|
|
4960
|
+
});
|
|
4961
|
+
currentExternalCompletedResponse = void 0;
|
|
4962
|
+
currentExternalStateInput = void 0;
|
|
4963
|
+
currentExternalConsumedResponseId = void 0;
|
|
4964
|
+
const continuation = resolveExternalContinuation(body);
|
|
4965
|
+
if (continuation.orphanedResponseId) {
|
|
4966
|
+
if (debug) log14(`WS continuation rejected: unknown previous_response_id=${continuation.orphanedResponseId}`);
|
|
4967
|
+
writeResponsesErrorStream(modelId, "Unknown or expired previous_response_id", sendWsEvent, 400);
|
|
4968
|
+
externalActive = false;
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4640
4971
|
try {
|
|
4641
|
-
const routedBody = await prepareExternalCodexBody(body, {
|
|
4972
|
+
const routedBody = await prepareExternalCodexBody(continuation.body, {
|
|
4642
4973
|
relay: nativePayloadRelay,
|
|
4643
4974
|
mixedNative,
|
|
4644
4975
|
headers: req.headers
|
|
4645
4976
|
});
|
|
4646
|
-
|
|
4977
|
+
currentExternalStateInput = responsesInputItems(routedBody.input);
|
|
4978
|
+
currentExternalConsumedResponseId = continuation.consumedResponseId;
|
|
4979
|
+
let params = applyClaudeCodeOAuthIdentity(route, applyExternalCodexRuntimeIdentity(translateResponsesRequest(
|
|
4647
4980
|
routedBody,
|
|
4648
4981
|
route.npm,
|
|
4649
4982
|
{
|
|
@@ -4655,7 +4988,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
4655
4988
|
upstreamModelId: route.upstreamModelId
|
|
4656
4989
|
},
|
|
4657
4990
|
{ maxTools: maxToolsForNpm(route.npm) }
|
|
4658
|
-
));
|
|
4991
|
+
), route));
|
|
4659
4992
|
if (route.contextWindow && route.contextWindow > 0) {
|
|
4660
4993
|
const before = params.messages.length;
|
|
4661
4994
|
const estimatedChars = estimateCodexRequestChars(params);
|
|
@@ -4688,9 +5021,37 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
4688
5021
|
log14(`WS response progress: model=${route.modelId} elapsedMs=${progress.elapsedMs} reasoningChars=${progress.reasoningChars} textChars=${progress.textChars} toolCalls=${progress.toolCallCount} reasoningTail=${JSON.stringify(progress.reasoningTail)}`);
|
|
4689
5022
|
}
|
|
4690
5023
|
});
|
|
5024
|
+
if (currentExternalCompletedResponse && currentExternalStateInput) {
|
|
5025
|
+
if (currentExternalConsumedResponseId) {
|
|
5026
|
+
externalResponseStates.delete(currentExternalConsumedResponseId);
|
|
5027
|
+
}
|
|
5028
|
+
rememberExternalResponse(currentExternalCompletedResponse, currentExternalStateInput);
|
|
5029
|
+
}
|
|
5030
|
+
audit({
|
|
5031
|
+
transport: "ws",
|
|
5032
|
+
requestedModel: modelId,
|
|
5033
|
+
dispatch: relayDispatch,
|
|
5034
|
+
phase: "complete",
|
|
5035
|
+
provider: route.providerId ?? "relay",
|
|
5036
|
+
routeModel: route.modelId,
|
|
5037
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
5038
|
+
outcome: "ok",
|
|
5039
|
+
status: "response.completed"
|
|
5040
|
+
});
|
|
4691
5041
|
} catch (err) {
|
|
4692
5042
|
const msg = formatUpstreamError(err);
|
|
4693
5043
|
const status = upstreamHttpStatus(err, msg);
|
|
5044
|
+
audit({
|
|
5045
|
+
transport: "ws",
|
|
5046
|
+
requestedModel: modelId,
|
|
5047
|
+
dispatch: relayDispatch,
|
|
5048
|
+
phase: "complete",
|
|
5049
|
+
provider: route.providerId ?? "relay",
|
|
5050
|
+
routeModel: route.modelId,
|
|
5051
|
+
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
5052
|
+
outcome: "error",
|
|
5053
|
+
status
|
|
5054
|
+
});
|
|
4694
5055
|
if (debug) log14(`WS sdk error: ${route.modelId}: ${msg}`);
|
|
4695
5056
|
if (status === 429) {
|
|
4696
5057
|
writeResponsesRateLimitStream(modelId, msg, sendWsEvent);
|
|
@@ -4698,10 +5059,11 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
4698
5059
|
writeResponsesErrorStream(modelId, msg, sendWsEvent, status);
|
|
4699
5060
|
}
|
|
4700
5061
|
}
|
|
4701
|
-
|
|
5062
|
+
externalActive = false;
|
|
4702
5063
|
})();
|
|
4703
5064
|
};
|
|
4704
5065
|
socket.on("error", () => socket.destroy());
|
|
5066
|
+
socket.once("close", () => externalResponseStates.clear());
|
|
4705
5067
|
socket.on("data", onData);
|
|
4706
5068
|
onData(head);
|
|
4707
5069
|
});
|
|
@@ -4726,44 +5088,44 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
4726
5088
|
}
|
|
4727
5089
|
|
|
4728
5090
|
// src/codex/profile.ts
|
|
4729
|
-
import { join as
|
|
5091
|
+
import { join as join4 } from "path";
|
|
4730
5092
|
|
|
4731
5093
|
// src/codex/session.ts
|
|
4732
5094
|
import {
|
|
4733
5095
|
copyFileSync,
|
|
4734
|
-
chmodSync,
|
|
5096
|
+
chmodSync as chmodSync2,
|
|
4735
5097
|
existsSync as existsSync3,
|
|
4736
|
-
mkdirSync,
|
|
5098
|
+
mkdirSync as mkdirSync2,
|
|
4737
5099
|
readdirSync,
|
|
4738
5100
|
readFileSync as readFileSync2,
|
|
4739
5101
|
renameSync,
|
|
4740
5102
|
rmSync,
|
|
4741
5103
|
statSync,
|
|
4742
5104
|
unlinkSync,
|
|
4743
|
-
writeFileSync
|
|
5105
|
+
writeFileSync as writeFileSync2
|
|
4744
5106
|
} from "fs";
|
|
4745
5107
|
import { homedir as homedir3 } from "os";
|
|
4746
|
-
import { basename, dirname, join as
|
|
5108
|
+
import { basename, dirname, join as join3 } from "path";
|
|
4747
5109
|
var CODEX_PROFILE_NAME = "relay-ai-launch";
|
|
4748
5110
|
var STALE_SESSION_MS = 5 * 60 * 1e3;
|
|
4749
5111
|
var MAX_BACKUPS = 5;
|
|
4750
5112
|
function getCodexHome(env = process.env) {
|
|
4751
|
-
return env["CODEX_HOME"] ||
|
|
5113
|
+
return env["CODEX_HOME"] || join3(homedir3(), ".codex");
|
|
4752
5114
|
}
|
|
4753
5115
|
function getCodexProfilePath() {
|
|
4754
|
-
return
|
|
5116
|
+
return join3(getCodexHome(), `${CODEX_PROFILE_NAME}.config.toml`);
|
|
4755
5117
|
}
|
|
4756
5118
|
function getRelayAiCodexDir(env = process.env) {
|
|
4757
|
-
return
|
|
5119
|
+
return join3(getAppHome(env), "codex");
|
|
4758
5120
|
}
|
|
4759
5121
|
function getSessionLockPath(env = process.env) {
|
|
4760
|
-
return
|
|
5122
|
+
return join3(getRelayAiCodexDir(env), "session.json");
|
|
4761
5123
|
}
|
|
4762
5124
|
function getBackupsDir(env = process.env) {
|
|
4763
|
-
return
|
|
5125
|
+
return join3(getRelayAiCodexDir(env), "backups");
|
|
4764
5126
|
}
|
|
4765
5127
|
function getCatalogPath(providerId, env = process.env) {
|
|
4766
|
-
return
|
|
5128
|
+
return join3(getRelayAiCodexDir(env), `models-${providerId}.json`);
|
|
4767
5129
|
}
|
|
4768
5130
|
function ownedOverlayPaths(env = process.env) {
|
|
4769
5131
|
const paths = [getCodexProfilePath()];
|
|
@@ -4771,15 +5133,15 @@ function ownedOverlayPaths(env = process.env) {
|
|
|
4771
5133
|
if (existsSync3(codexDir)) {
|
|
4772
5134
|
for (const name of readdirSync(codexDir)) {
|
|
4773
5135
|
if (name.startsWith("models-") && name.endsWith(".json")) {
|
|
4774
|
-
paths.push(
|
|
5136
|
+
paths.push(join3(codexDir, name));
|
|
4775
5137
|
}
|
|
4776
5138
|
}
|
|
4777
5139
|
}
|
|
4778
|
-
const agentsDir =
|
|
5140
|
+
const agentsDir = join3(getCodexHome(env), "agents");
|
|
4779
5141
|
if (existsSync3(agentsDir)) {
|
|
4780
5142
|
for (const name of readdirSync(agentsDir)) {
|
|
4781
5143
|
if (/^relay-model-[a-z0-9-]+\.toml$/i.test(name)) {
|
|
4782
|
-
paths.push(
|
|
5144
|
+
paths.push(join3(agentsDir, name));
|
|
4783
5145
|
}
|
|
4784
5146
|
}
|
|
4785
5147
|
}
|
|
@@ -4787,27 +5149,27 @@ function ownedOverlayPaths(env = process.env) {
|
|
|
4787
5149
|
return paths;
|
|
4788
5150
|
}
|
|
4789
5151
|
function atomicWriteFile(path3, content) {
|
|
4790
|
-
|
|
5152
|
+
mkdirSync2(dirname(path3), { recursive: true });
|
|
4791
5153
|
const tmp = `${path3}.tmp.${process.pid}`;
|
|
4792
|
-
|
|
5154
|
+
writeFileSync2(tmp, content, { encoding: "utf8", mode: 384 });
|
|
4793
5155
|
renameSync(tmp, path3);
|
|
4794
5156
|
try {
|
|
4795
|
-
|
|
5157
|
+
chmodSync2(path3, 384);
|
|
4796
5158
|
} catch {
|
|
4797
5159
|
}
|
|
4798
5160
|
}
|
|
4799
5161
|
function rotateBackups(filePath, env = process.env) {
|
|
4800
5162
|
if (!existsSync3(filePath)) return;
|
|
4801
5163
|
const backupsDir = getBackupsDir(env);
|
|
4802
|
-
|
|
5164
|
+
mkdirSync2(backupsDir, { recursive: true });
|
|
4803
5165
|
const base = basename(filePath);
|
|
4804
5166
|
const stamp = Date.now();
|
|
4805
|
-
const backupPath =
|
|
5167
|
+
const backupPath = join3(backupsDir, `${base}.${stamp}.bak`);
|
|
4806
5168
|
copyFileSync(filePath, backupPath);
|
|
4807
|
-
const backups = readdirSync(backupsDir).filter((n) => n.startsWith(`${base}.`) && n.endsWith(".bak")).map((n) => ({ name: n, mtime: statSync(
|
|
5169
|
+
const backups = readdirSync(backupsDir).filter((n) => n.startsWith(`${base}.`) && n.endsWith(".bak")).map((n) => ({ name: n, mtime: statSync(join3(backupsDir, n)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
4808
5170
|
for (const old of backups.slice(MAX_BACKUPS)) {
|
|
4809
5171
|
try {
|
|
4810
|
-
unlinkSync(
|
|
5172
|
+
unlinkSync(join3(backupsDir, old.name));
|
|
4811
5173
|
} catch {
|
|
4812
5174
|
}
|
|
4813
5175
|
}
|
|
@@ -4828,7 +5190,7 @@ function readSessionLock(env = process.env) {
|
|
|
4828
5190
|
}
|
|
4829
5191
|
function writeSessionLock(lock, env = process.env) {
|
|
4830
5192
|
const path3 = getSessionLockPath(env);
|
|
4831
|
-
|
|
5193
|
+
mkdirSync2(getRelayAiCodexDir(env), { recursive: true });
|
|
4832
5194
|
atomicWriteFile(path3, `${JSON.stringify(lock, null, 2)}
|
|
4833
5195
|
`);
|
|
4834
5196
|
}
|
|
@@ -4951,20 +5313,21 @@ function getCatalogOutputPath(providerId) {
|
|
|
4951
5313
|
return getCatalogPath(providerId);
|
|
4952
5314
|
}
|
|
4953
5315
|
function getFavoritesCatalogPath() {
|
|
4954
|
-
return
|
|
5316
|
+
return join4(getRelayAiCodexDir(), "models-favorites.json");
|
|
4955
5317
|
}
|
|
4956
5318
|
function getFavoritesAppCatalogPath() {
|
|
4957
|
-
return
|
|
5319
|
+
return join4(getRelayAiCodexDir(), "app-models-favorites.json");
|
|
4958
5320
|
}
|
|
4959
5321
|
function profileName() {
|
|
4960
5322
|
return CODEX_PROFILE_NAME;
|
|
4961
5323
|
}
|
|
4962
5324
|
|
|
4963
5325
|
// src/codex/launch.ts
|
|
4964
|
-
import {
|
|
5326
|
+
import { execSync as execSync2 } from "child_process";
|
|
5327
|
+
import spawn2 from "cross-spawn";
|
|
4965
5328
|
import { existsSync as existsSync4 } from "fs";
|
|
4966
5329
|
import { homedir as homedir4 } from "os";
|
|
4967
|
-
import { join as
|
|
5330
|
+
import { join as join5 } from "path";
|
|
4968
5331
|
var isWindows2 = process.platform === "win32";
|
|
4969
5332
|
var CODEX_CI_ENV_VARS = [
|
|
4970
5333
|
"CI",
|
|
@@ -4985,11 +5348,11 @@ function stripCodexInheritedEnv(env) {
|
|
|
4985
5348
|
return out;
|
|
4986
5349
|
}
|
|
4987
5350
|
var CODEX_FALLBACK_PATHS = isWindows2 ? [
|
|
4988
|
-
|
|
4989
|
-
|
|
5351
|
+
join5(process.env["APPDATA"] ?? homedir4(), "npm", "codex.cmd"),
|
|
5352
|
+
join5(process.env["APPDATA"] ?? homedir4(), "npm", "codex")
|
|
4990
5353
|
] : [
|
|
4991
|
-
|
|
4992
|
-
|
|
5354
|
+
join5(homedir4(), ".local", "bin", "codex"),
|
|
5355
|
+
join5(homedir4(), ".npm", "bin", "codex"),
|
|
4993
5356
|
"/usr/local/bin/codex",
|
|
4994
5357
|
"/opt/homebrew/bin/codex"
|
|
4995
5358
|
];
|
|
@@ -5023,12 +5386,7 @@ function selectCodexBinary(candidates, exists, canRun) {
|
|
|
5023
5386
|
}
|
|
5024
5387
|
function canRunCodexBinary(path3) {
|
|
5025
5388
|
try {
|
|
5026
|
-
|
|
5027
|
-
encoding: "utf8",
|
|
5028
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
5029
|
-
timeout: 5e3,
|
|
5030
|
-
shell: isWindows2
|
|
5031
|
-
});
|
|
5389
|
+
runCodexCommandSync(path3, ["--version"], { timeout: 5e3 });
|
|
5032
5390
|
return true;
|
|
5033
5391
|
} catch {
|
|
5034
5392
|
return false;
|
|
@@ -5067,8 +5425,7 @@ function launchCodex(modelId, env, extraArgs) {
|
|
|
5067
5425
|
const args = ["--profile", profileName(), "-m", modelId, ...ensureCodexSandboxArgs(extraArgs)];
|
|
5068
5426
|
const child = spawn2(codexPath, args, {
|
|
5069
5427
|
stdio: "inherit",
|
|
5070
|
-
env
|
|
5071
|
-
shell: isWindows2
|
|
5428
|
+
env
|
|
5072
5429
|
});
|
|
5073
5430
|
const forward = (signal) => {
|
|
5074
5431
|
child.kill(signal);
|
|
@@ -5498,14 +5855,12 @@ async function resolveCodexMixedModels(input) {
|
|
|
5498
5855
|
subagents: subagentResult.resolved,
|
|
5499
5856
|
all,
|
|
5500
5857
|
providersById: new Map(input.compatible.map((provider) => [provider.id, provider])),
|
|
5501
|
-
dropped: [...visibleResult.droppedFavorites, ...subagentResult.droppedFavorites]
|
|
5858
|
+
dropped: [...visibleResult.droppedFavorites, ...subagentResult.droppedFavorites],
|
|
5859
|
+
capacitySkipped: [...visibleResult.capacitySkippedFavorites, ...subagentResult.capacitySkippedFavorites]
|
|
5502
5860
|
};
|
|
5503
5861
|
}
|
|
5504
5862
|
|
|
5505
5863
|
// src/codex/native-catalog.ts
|
|
5506
|
-
import { execFile } from "child_process";
|
|
5507
|
-
import { promisify } from "util";
|
|
5508
|
-
var execFileAsync = promisify(execFile);
|
|
5509
5864
|
function isCatalogModel(value) {
|
|
5510
5865
|
if (!value || typeof value !== "object") return false;
|
|
5511
5866
|
const model = value;
|
|
@@ -5522,7 +5877,7 @@ function validateNativeCodexCatalog(value) {
|
|
|
5522
5877
|
}
|
|
5523
5878
|
async function captureNativeCodexCatalog(options) {
|
|
5524
5879
|
const run = options.run ?? (async (args) => {
|
|
5525
|
-
const result = await
|
|
5880
|
+
const result = await runCodexCommand(options.binaryPath, args, { maxBuffer: 16 * 1024 * 1024 });
|
|
5526
5881
|
return result.stdout;
|
|
5527
5882
|
});
|
|
5528
5883
|
const stdout = await run(options.bundled ? ["debug", "models", "--bundled"] : ["debug", "models"]);
|
|
@@ -5545,6 +5900,22 @@ async function captureNativeCodexCatalog(options) {
|
|
|
5545
5900
|
}
|
|
5546
5901
|
|
|
5547
5902
|
// src/codex/mixed-catalog.ts
|
|
5903
|
+
function externalInstructionValue(value) {
|
|
5904
|
+
if (typeof value === "string") {
|
|
5905
|
+
return value.replace(/^You are Codex,[^\n]*?\.\s*/i, "").replace(/\bAs Codex,\s+(\w)/g, (_match, nextChar) => nextChar.toUpperCase()).replace(/\s+as Codex\b/gi, "");
|
|
5906
|
+
}
|
|
5907
|
+
if (Array.isArray(value)) return value.map(externalInstructionValue);
|
|
5908
|
+
if (!value || typeof value !== "object") return value;
|
|
5909
|
+
return Object.fromEntries(
|
|
5910
|
+
Object.entries(value).map(([key, nested]) => [
|
|
5911
|
+
key,
|
|
5912
|
+
externalInstructionValue(nested)
|
|
5913
|
+
])
|
|
5914
|
+
);
|
|
5915
|
+
}
|
|
5916
|
+
function externalModelMessages(templateMessages) {
|
|
5917
|
+
return externalInstructionValue(templateMessages);
|
|
5918
|
+
}
|
|
5548
5919
|
function externalCatalogEntryFromTemplate(template, entry, priority, visibility, multiAgentVersion) {
|
|
5549
5920
|
const resolvedModel = entry.resolved.model;
|
|
5550
5921
|
const generated = catalogEntryFromModel(
|
|
@@ -5554,7 +5925,7 @@ function externalCatalogEntryFromTemplate(template, entry, priority, visibility,
|
|
|
5554
5925
|
false,
|
|
5555
5926
|
entry.slug
|
|
5556
5927
|
);
|
|
5557
|
-
|
|
5928
|
+
const external = {
|
|
5558
5929
|
...template,
|
|
5559
5930
|
...generated,
|
|
5560
5931
|
slug: entry.slug,
|
|
@@ -5562,6 +5933,9 @@ function externalCatalogEntryFromTemplate(template, entry, priority, visibility,
|
|
|
5562
5933
|
visibility,
|
|
5563
5934
|
multi_agent_version: multiAgentVersion
|
|
5564
5935
|
};
|
|
5936
|
+
external.model_messages = externalModelMessages(template.model_messages);
|
|
5937
|
+
delete external.comp_hash;
|
|
5938
|
+
return external;
|
|
5565
5939
|
}
|
|
5566
5940
|
function composeMixedCodexCatalog(input) {
|
|
5567
5941
|
const template = input.nativeModels.find((model) => model.slug === "gpt-5.5") ?? input.nativeModels.find((model) => model.visibility === "list") ?? input.nativeModels[0];
|
|
@@ -5682,6 +6056,7 @@ async function prepareCodexMixedRelayRoutes(models, trace = false) {
|
|
|
5682
6056
|
apiKey: backend.token,
|
|
5683
6057
|
baseURL: `http://127.0.0.1:${backend.port}`,
|
|
5684
6058
|
upstreamModelId: proxyRoute.aliasId,
|
|
6059
|
+
auditUpstreamModelId: original.model.upstreamModelId || original.model.id,
|
|
5685
6060
|
providerId: original.providerId,
|
|
5686
6061
|
authType: "oauth",
|
|
5687
6062
|
oauthAccountId: original.oauthAccountId,
|
|
@@ -6012,7 +6387,7 @@ async function writeFavoritesLaunchArtifacts(resolved, starting, proxyPort) {
|
|
|
6012
6387
|
return { profilePath, catalogPath };
|
|
6013
6388
|
}
|
|
6014
6389
|
async function writeMixedLaunchArtifacts(plan, proxyPort) {
|
|
6015
|
-
const catalogPath =
|
|
6390
|
+
const catalogPath = join6(getRelayAiCodexDir(), "models-mixed.json");
|
|
6016
6391
|
writeOverlayFile(catalogPath, serializeCatalog(plan.catalog));
|
|
6017
6392
|
const profilePath = getProfileOutputPath();
|
|
6018
6393
|
writeOverlayFile(profilePath, buildCodexMixedProfileToml({
|
|
@@ -6136,7 +6511,7 @@ async function runCodexVertexLaunch(passthroughArgs, trace) {
|
|
|
6136
6511
|
restoreCodexOverlay();
|
|
6137
6512
|
}
|
|
6138
6513
|
}
|
|
6139
|
-
async function
|
|
6514
|
+
async function runCodexCommand2(codexArgs, trace = false, launch = {}) {
|
|
6140
6515
|
if (codexArgs.includes("--help") || codexArgs.includes("-h")) {
|
|
6141
6516
|
console.log(codexHelpText());
|
|
6142
6517
|
return 0;
|
|
@@ -6334,7 +6709,7 @@ Error: ${launchPlan.error}
|
|
|
6334
6709
|
let mixedPlan = null;
|
|
6335
6710
|
if (mixedMode) {
|
|
6336
6711
|
try {
|
|
6337
|
-
const version =
|
|
6712
|
+
const version = runCodexCommandSync(codexPath, ["--version"]).stdout.trim();
|
|
6338
6713
|
const mixedModels = await resolveCodexMixedModels({
|
|
6339
6714
|
activeProvider,
|
|
6340
6715
|
selectedModel,
|
|
@@ -6597,17 +6972,17 @@ import * as p10 from "@clack/prompts";
|
|
|
6597
6972
|
|
|
6598
6973
|
// src/gemini/launch.ts
|
|
6599
6974
|
import { spawn as spawn3 } from "child_process";
|
|
6600
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
6975
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
6601
6976
|
import { homedir as homedir5, tmpdir } from "os";
|
|
6602
|
-
import { join as
|
|
6977
|
+
import { join as join7 } from "path";
|
|
6603
6978
|
var isWindows3 = process.platform === "win32";
|
|
6604
6979
|
var GEMINI_API_KEY_AUTH_TYPE = "gemini-api-key";
|
|
6605
6980
|
var GEMINI_FALLBACK_PATHS = isWindows3 ? [
|
|
6606
|
-
|
|
6607
|
-
|
|
6981
|
+
join7(process.env["APPDATA"] ?? homedir5(), "npm", "gemini.cmd"),
|
|
6982
|
+
join7(process.env["APPDATA"] ?? homedir5(), "npm", "gemini")
|
|
6608
6983
|
] : [
|
|
6609
|
-
|
|
6610
|
-
|
|
6984
|
+
join7(homedir5(), ".local", "bin", "gemini"),
|
|
6985
|
+
join7(homedir5(), ".npm", "bin", "gemini"),
|
|
6611
6986
|
"/usr/local/bin/gemini",
|
|
6612
6987
|
"/opt/homebrew/bin/gemini"
|
|
6613
6988
|
];
|
|
@@ -6628,7 +7003,7 @@ function buildGeminiChildEnv(proxyPort, proxyToken) {
|
|
|
6628
7003
|
return env;
|
|
6629
7004
|
}
|
|
6630
7005
|
function createGeminiCliHomeOverlay() {
|
|
6631
|
-
const cliHome = mkdtempSync(
|
|
7006
|
+
const cliHome = mkdtempSync(join7(tmpdir(), "relay-ai-gemini-"));
|
|
6632
7007
|
const settings = {
|
|
6633
7008
|
security: {
|
|
6634
7009
|
auth: {
|
|
@@ -6636,9 +7011,9 @@ function createGeminiCliHomeOverlay() {
|
|
|
6636
7011
|
}
|
|
6637
7012
|
}
|
|
6638
7013
|
};
|
|
6639
|
-
const geminiDir =
|
|
6640
|
-
|
|
6641
|
-
|
|
7014
|
+
const geminiDir = join7(cliHome, ".gemini");
|
|
7015
|
+
mkdirSync3(geminiDir);
|
|
7016
|
+
writeFileSync3(join7(geminiDir, "settings.json"), `${JSON.stringify(settings, null, 2)}
|
|
6642
7017
|
`, {
|
|
6643
7018
|
encoding: "utf8",
|
|
6644
7019
|
mode: 384
|
|
@@ -7738,6 +8113,267 @@ import * as p11 from "@clack/prompts";
|
|
|
7738
8113
|
import http from "http";
|
|
7739
8114
|
import { streamText as streamText3, generateText as generateText3 } from "ai";
|
|
7740
8115
|
|
|
8116
|
+
// src/antigravity/request-adapter.ts
|
|
8117
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8118
|
+
import { tool as tool3, jsonSchema as jsonSchema3 } from "ai";
|
|
8119
|
+
var UNSUPPORTED_VOICE_MESSAGE = "Voice transcription isn\u2019t supported by Relay AI yet. Please type your message. Your coding session remains active.";
|
|
8120
|
+
var OMITTED_VOICE_TEXT = "[Voice recording omitted because transcription is not supported by Relay AI.]";
|
|
8121
|
+
function isSupportedImage(part) {
|
|
8122
|
+
return part.inlineData?.mimeType.toLowerCase().startsWith("image/") ?? false;
|
|
8123
|
+
}
|
|
8124
|
+
function isUnsupportedInlineData(part) {
|
|
8125
|
+
return !!part.inlineData && !isSupportedImage(part);
|
|
8126
|
+
}
|
|
8127
|
+
function sanitizeUnsupportedInlineData(ccReq) {
|
|
8128
|
+
const contents = ccReq.request?.contents ?? [];
|
|
8129
|
+
let latestUserIndex = -1;
|
|
8130
|
+
for (let i = contents.length - 1; i >= 0; i--) {
|
|
8131
|
+
if (contents[i].role === "user") {
|
|
8132
|
+
latestUserIndex = i;
|
|
8133
|
+
break;
|
|
8134
|
+
}
|
|
8135
|
+
}
|
|
8136
|
+
let latestUserTurnHasUnsupportedMedia = false;
|
|
8137
|
+
const sanitizedContents = contents.map((message, index) => ({
|
|
8138
|
+
...message,
|
|
8139
|
+
parts: message.parts.map((part) => {
|
|
8140
|
+
if (!isUnsupportedInlineData(part)) return part;
|
|
8141
|
+
if (index === latestUserIndex) latestUserTurnHasUnsupportedMedia = true;
|
|
8142
|
+
return { text: OMITTED_VOICE_TEXT };
|
|
8143
|
+
})
|
|
8144
|
+
}));
|
|
8145
|
+
return {
|
|
8146
|
+
request: {
|
|
8147
|
+
...ccReq,
|
|
8148
|
+
request: {
|
|
8149
|
+
...ccReq.request,
|
|
8150
|
+
contents: sanitizedContents
|
|
8151
|
+
}
|
|
8152
|
+
},
|
|
8153
|
+
latestUserTurnHasUnsupportedMedia
|
|
8154
|
+
};
|
|
8155
|
+
}
|
|
8156
|
+
function tracePartChars(part) {
|
|
8157
|
+
if (typeof part.text === "string") return part.text.length;
|
|
8158
|
+
if (part.type !== "tool-result") return void 0;
|
|
8159
|
+
const output = part.output;
|
|
8160
|
+
if (typeof output === "string") return output.length;
|
|
8161
|
+
if (output && typeof output === "object" && typeof output.value === "string") {
|
|
8162
|
+
return output.value.length;
|
|
8163
|
+
}
|
|
8164
|
+
try {
|
|
8165
|
+
return output === void 0 ? void 0 : JSON.stringify(output).length;
|
|
8166
|
+
} catch {
|
|
8167
|
+
return void 0;
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
8170
|
+
function summarizeSdkRequestForTrace(request2) {
|
|
8171
|
+
const messages = request2.messages.map((message) => {
|
|
8172
|
+
const content = message.content;
|
|
8173
|
+
if (typeof content === "string") {
|
|
8174
|
+
return { role: message.role, parts: [{ type: "text", chars: content.length }] };
|
|
8175
|
+
}
|
|
8176
|
+
const parts = Array.isArray(content) ? content.map((rawPart) => {
|
|
8177
|
+
const part = rawPart;
|
|
8178
|
+
const summary = {
|
|
8179
|
+
type: typeof part.type === "string" ? part.type : typeof rawPart
|
|
8180
|
+
};
|
|
8181
|
+
const chars = tracePartChars(part);
|
|
8182
|
+
if (chars !== void 0) summary.chars = chars;
|
|
8183
|
+
if (typeof part.toolName === "string") summary.toolName = part.toolName;
|
|
8184
|
+
if (typeof part.toolCallId === "string") summary.toolCallId = part.toolCallId;
|
|
8185
|
+
return summary;
|
|
8186
|
+
}) : [{ type: typeof content }];
|
|
8187
|
+
return { role: message.role, parts };
|
|
8188
|
+
});
|
|
8189
|
+
return {
|
|
8190
|
+
systemChars: request2.system?.length ?? 0,
|
|
8191
|
+
messages,
|
|
8192
|
+
toolNames: Object.keys(request2.tools ?? {}),
|
|
8193
|
+
...request2.toolChoice ? { toolChoice: request2.toolChoice } : {}
|
|
8194
|
+
};
|
|
8195
|
+
}
|
|
8196
|
+
var JSON_SCHEMA_TYPES = /* @__PURE__ */ new Map([
|
|
8197
|
+
["ARRAY", "array"],
|
|
8198
|
+
["BOOLEAN", "boolean"],
|
|
8199
|
+
["INTEGER", "integer"],
|
|
8200
|
+
["NULL", "null"],
|
|
8201
|
+
["NUMBER", "number"],
|
|
8202
|
+
["OBJECT", "object"],
|
|
8203
|
+
["STRING", "string"]
|
|
8204
|
+
]);
|
|
8205
|
+
function expandTextWithThinking(text5) {
|
|
8206
|
+
if (!text5.includes("<thinking>")) {
|
|
8207
|
+
return [{ type: "text", text: text5 }];
|
|
8208
|
+
}
|
|
8209
|
+
const out = [];
|
|
8210
|
+
const tokens = text5.split(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
8211
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
8212
|
+
const token = tokens[i] ?? "";
|
|
8213
|
+
if (!token.trim()) continue;
|
|
8214
|
+
out.push({ type: i % 2 === 1 ? "reasoning" : "text", text: token });
|
|
8215
|
+
}
|
|
8216
|
+
return out.length > 0 ? out : [{ type: "text", text: text5 }];
|
|
8217
|
+
}
|
|
8218
|
+
function normalizeSchemaType(value) {
|
|
8219
|
+
if (typeof value === "string") {
|
|
8220
|
+
return JSON_SCHEMA_TYPES.get(value) ?? value;
|
|
8221
|
+
}
|
|
8222
|
+
if (Array.isArray(value)) {
|
|
8223
|
+
return value.map(normalizeSchemaType);
|
|
8224
|
+
}
|
|
8225
|
+
return value;
|
|
8226
|
+
}
|
|
8227
|
+
function normalizeJsonSchema(value) {
|
|
8228
|
+
if (Array.isArray(value)) {
|
|
8229
|
+
return value.map(normalizeJsonSchema);
|
|
8230
|
+
}
|
|
8231
|
+
if (!value || typeof value !== "object") {
|
|
8232
|
+
return value;
|
|
8233
|
+
}
|
|
8234
|
+
return Object.fromEntries(
|
|
8235
|
+
Object.entries(value).map(([key, child]) => [
|
|
8236
|
+
key,
|
|
8237
|
+
key === "type" ? normalizeSchemaType(child) : normalizeJsonSchema(child)
|
|
8238
|
+
])
|
|
8239
|
+
);
|
|
8240
|
+
}
|
|
8241
|
+
function translateTools(ccTools, options = {}) {
|
|
8242
|
+
if (!ccTools?.length) return void 0;
|
|
8243
|
+
const tools = {};
|
|
8244
|
+
let toolCount = 0;
|
|
8245
|
+
for (const t of ccTools) {
|
|
8246
|
+
if (t.functionDeclarations) {
|
|
8247
|
+
for (const fd of t.functionDeclarations) {
|
|
8248
|
+
if (options.maxTools !== void 0 && toolCount >= options.maxTools) break;
|
|
8249
|
+
tools[fd.name] = tool3({
|
|
8250
|
+
description: fd.description || "",
|
|
8251
|
+
inputSchema: jsonSchema3(
|
|
8252
|
+
normalizeJsonSchema(fd.parameters || { type: "object", properties: {} })
|
|
8253
|
+
)
|
|
8254
|
+
});
|
|
8255
|
+
toolCount++;
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
return Object.keys(tools).length > 0 ? tools : void 0;
|
|
8260
|
+
}
|
|
8261
|
+
function translateRequest(ccReq, options = {}) {
|
|
8262
|
+
const systemInstructions = [];
|
|
8263
|
+
const sdkMessages = [];
|
|
8264
|
+
const nameToIdList = /* @__PURE__ */ new Map();
|
|
8265
|
+
const fallbackAssistantReasoning = [...options.fallbackAssistantReasoning ?? []];
|
|
8266
|
+
const request2 = ccReq.request || {};
|
|
8267
|
+
if (request2.systemInstruction?.parts) {
|
|
8268
|
+
for (const part of request2.systemInstruction.parts) {
|
|
8269
|
+
if (part.text) {
|
|
8270
|
+
systemInstructions.push(part.text);
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
const contents = request2.contents || [];
|
|
8275
|
+
for (const msg of contents) {
|
|
8276
|
+
const role = msg.role;
|
|
8277
|
+
if (role === "system") {
|
|
8278
|
+
for (const part of msg.parts) {
|
|
8279
|
+
if (part.text) {
|
|
8280
|
+
systemInstructions.push(part.text);
|
|
8281
|
+
}
|
|
8282
|
+
}
|
|
8283
|
+
continue;
|
|
8284
|
+
}
|
|
8285
|
+
const sdkRole = role === "model" ? "assistant" : "user";
|
|
8286
|
+
const hasFunctionCall = msg.parts.some((p15) => p15.functionCall);
|
|
8287
|
+
const hasAssistantReasoning = role === "model" && msg.parts.some((p15) => p15.thought || p15.text?.includes("<thinking>"));
|
|
8288
|
+
const hasComplexParts = msg.parts.some((p15) => p15.thought || p15.inlineData || p15.functionCall || p15.functionResponse);
|
|
8289
|
+
const singleText = msg.parts.length === 1 ? msg.parts[0]?.text : void 0;
|
|
8290
|
+
if (!hasComplexParts && singleText !== void 0 && !singleText.includes("<thinking>")) {
|
|
8291
|
+
sdkMessages.push({
|
|
8292
|
+
role: sdkRole,
|
|
8293
|
+
content: singleText
|
|
8294
|
+
});
|
|
8295
|
+
continue;
|
|
8296
|
+
}
|
|
8297
|
+
const contentParts = [];
|
|
8298
|
+
const toolResults = [];
|
|
8299
|
+
if (role === "model" && hasFunctionCall && !hasAssistantReasoning) {
|
|
8300
|
+
const fallback = fallbackAssistantReasoning.shift();
|
|
8301
|
+
if (fallback?.trim()) {
|
|
8302
|
+
contentParts.push({ type: "reasoning", text: fallback });
|
|
8303
|
+
}
|
|
8304
|
+
}
|
|
8305
|
+
for (const part of msg.parts) {
|
|
8306
|
+
if (part.text !== void 0) {
|
|
8307
|
+
if (part.thought) {
|
|
8308
|
+
contentParts.push({ type: "reasoning", text: part.text });
|
|
8309
|
+
} else {
|
|
8310
|
+
for (const piece of expandTextWithThinking(part.text)) {
|
|
8311
|
+
contentParts.push(piece);
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
8314
|
+
} else if (part.inlineData) {
|
|
8315
|
+
if (isSupportedImage(part)) {
|
|
8316
|
+
contentParts.push({
|
|
8317
|
+
type: "image",
|
|
8318
|
+
image: part.inlineData.data,
|
|
8319
|
+
mimeType: part.inlineData.mimeType
|
|
8320
|
+
});
|
|
8321
|
+
} else {
|
|
8322
|
+
contentParts.push({ type: "text", text: OMITTED_VOICE_TEXT });
|
|
8323
|
+
}
|
|
8324
|
+
} else if (part.functionCall) {
|
|
8325
|
+
const id = "call_" + randomUUID2().replace(/-/g, "");
|
|
8326
|
+
const name = part.functionCall.name;
|
|
8327
|
+
if (!nameToIdList.has(name)) nameToIdList.set(name, []);
|
|
8328
|
+
nameToIdList.get(name).push(id);
|
|
8329
|
+
contentParts.push({
|
|
8330
|
+
type: "tool-call",
|
|
8331
|
+
toolCallId: id,
|
|
8332
|
+
toolName: name,
|
|
8333
|
+
input: part.functionCall.args || {}
|
|
8334
|
+
});
|
|
8335
|
+
} else if (part.functionResponse) {
|
|
8336
|
+
const name = part.functionResponse.name;
|
|
8337
|
+
const idList = nameToIdList.get(name) || [];
|
|
8338
|
+
const id = idList.shift() || "call_" + randomUUID2().replace(/-/g, "");
|
|
8339
|
+
toolResults.push({
|
|
8340
|
+
type: "tool-result",
|
|
8341
|
+
toolCallId: id,
|
|
8342
|
+
toolName: name,
|
|
8343
|
+
output: { type: "text", value: serializeToolResultContent(part.functionResponse.response) }
|
|
8344
|
+
});
|
|
8345
|
+
}
|
|
8346
|
+
}
|
|
8347
|
+
if (toolResults.length > 0) {
|
|
8348
|
+
sdkMessages.push({
|
|
8349
|
+
role: "tool",
|
|
8350
|
+
content: toolResults
|
|
8351
|
+
});
|
|
8352
|
+
}
|
|
8353
|
+
if (contentParts.length > 0) {
|
|
8354
|
+
sdkMessages.push({
|
|
8355
|
+
role: sdkRole,
|
|
8356
|
+
content: contentParts
|
|
8357
|
+
});
|
|
8358
|
+
}
|
|
8359
|
+
}
|
|
8360
|
+
const system = systemInstructions.length > 0 ? systemInstructions.join("\n\n") : void 0;
|
|
8361
|
+
const tools = translateTools(request2.tools, options);
|
|
8362
|
+
let toolChoice;
|
|
8363
|
+
const mode = request2.toolConfig?.functionCallingConfig?.mode;
|
|
8364
|
+
if (mode === "ANY") {
|
|
8365
|
+
toolChoice = "required";
|
|
8366
|
+
} else if (mode === "AUTO" || tools) {
|
|
8367
|
+
toolChoice = "auto";
|
|
8368
|
+
}
|
|
8369
|
+
return {
|
|
8370
|
+
system,
|
|
8371
|
+
messages: sdkMessages,
|
|
8372
|
+
tools,
|
|
8373
|
+
toolChoice
|
|
8374
|
+
};
|
|
8375
|
+
}
|
|
8376
|
+
|
|
7741
8377
|
// src/antigravity/response-adapter.ts
|
|
7742
8378
|
function normalizeFunctionCallArgs(args) {
|
|
7743
8379
|
const out = {};
|
|
@@ -9615,19 +10251,19 @@ async function resolveAntigravityLaunchRoutes(opts) {
|
|
|
9615
10251
|
}
|
|
9616
10252
|
|
|
9617
10253
|
// src/antigravity/launch-cli.ts
|
|
9618
|
-
import { execFileSync
|
|
10254
|
+
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
9619
10255
|
import spawn4 from "cross-spawn";
|
|
9620
10256
|
import { existsSync as existsSync6 } from "fs";
|
|
9621
10257
|
import { homedir as homedir6 } from "os";
|
|
9622
|
-
import { join as
|
|
10258
|
+
import { join as join8 } from "path";
|
|
9623
10259
|
var isWindows4 = process.platform === "win32";
|
|
9624
10260
|
var FALLBACK_PATHS = isWindows4 ? [
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
10261
|
+
join8(process.env["APPDATA"] ?? homedir6(), "npm", "agy.cmd"),
|
|
10262
|
+
join8(process.env["APPDATA"] ?? homedir6(), "npm", "agy"),
|
|
10263
|
+
join8(homedir6(), "AppData", "Roaming", "npm", "agy.cmd")
|
|
9628
10264
|
] : [
|
|
9629
|
-
|
|
9630
|
-
|
|
10265
|
+
join8(homedir6(), ".local", "bin", "agy"),
|
|
10266
|
+
join8(homedir6(), ".npm", "bin", "agy"),
|
|
9631
10267
|
"/usr/local/bin/agy",
|
|
9632
10268
|
"/opt/homebrew/bin/agy"
|
|
9633
10269
|
];
|
|
@@ -9653,7 +10289,7 @@ function readAntigravityCliVersion(binaryPath = findAntigravityCliBinary() ?? vo
|
|
|
9653
10289
|
return { version: null, error: 'Antigravity CLI binary "agy" not found' };
|
|
9654
10290
|
}
|
|
9655
10291
|
try {
|
|
9656
|
-
const raw =
|
|
10292
|
+
const raw = execFileSync(binaryPath, ["--version"], {
|
|
9657
10293
|
encoding: "utf8",
|
|
9658
10294
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9659
10295
|
}).trim();
|
|
@@ -9702,10 +10338,10 @@ function launchAntigravityCli(env, extraArgs) {
|
|
|
9702
10338
|
}
|
|
9703
10339
|
|
|
9704
10340
|
// src/antigravity/launch-ide.ts
|
|
9705
|
-
import { execFileSync as
|
|
10341
|
+
import { execFileSync as execFileSync2, execSync as execSync4, spawn as spawn5 } from "child_process";
|
|
9706
10342
|
import { existsSync as existsSync7 } from "fs";
|
|
9707
10343
|
import { homedir as homedir7 } from "os";
|
|
9708
|
-
import { join as
|
|
10344
|
+
import { join as join9 } from "path";
|
|
9709
10345
|
|
|
9710
10346
|
// src/antigravity/ide-profile.ts
|
|
9711
10347
|
import fs from "fs";
|
|
@@ -9739,8 +10375,8 @@ function prepareIdeProfile(profileDir, gatewayUrl) {
|
|
|
9739
10375
|
}
|
|
9740
10376
|
|
|
9741
10377
|
// src/antigravity/launch-ide.ts
|
|
9742
|
-
var LINUX_APP_PROFILE_DIR =
|
|
9743
|
-
var LINUX_IDE_PROFILE_DIR =
|
|
10378
|
+
var LINUX_APP_PROFILE_DIR = join9(homedir7(), ".relay-ai", "antigravity", "app-profile");
|
|
10379
|
+
var LINUX_IDE_PROFILE_DIR = join9(homedir7(), ".relay-ai", "antigravity", "profile");
|
|
9744
10380
|
function sleep(ms) {
|
|
9745
10381
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
9746
10382
|
}
|
|
@@ -9748,7 +10384,7 @@ function linuxAntigravityBinary() {
|
|
|
9748
10384
|
const candidates = [
|
|
9749
10385
|
"/usr/share/antigravity/antigravity",
|
|
9750
10386
|
"/opt/antigravity/antigravity",
|
|
9751
|
-
|
|
10387
|
+
join9(homedir7(), ".local", "share", "antigravity", "antigravity")
|
|
9752
10388
|
];
|
|
9753
10389
|
for (const candidate of candidates) {
|
|
9754
10390
|
if (existsSync7(candidate)) return candidate;
|
|
@@ -9806,7 +10442,7 @@ function defaultProcessList() {
|
|
|
9806
10442
|
const psArgs = process.platform === "linux" ? ["-eo", "pid=,args="] : ["-axo", "pid=,command="];
|
|
9807
10443
|
if (process.platform !== "darwin" && process.platform !== "linux") return "";
|
|
9808
10444
|
try {
|
|
9809
|
-
return
|
|
10445
|
+
return execFileSync2("ps", psArgs, {
|
|
9810
10446
|
encoding: "utf8",
|
|
9811
10447
|
stdio: ["ignore", "pipe", "ignore"],
|
|
9812
10448
|
maxBuffer: 1024 * 1024 * 4
|
|
@@ -9870,11 +10506,11 @@ function quitAntigravityIdeGracefully() {
|
|
|
9870
10506
|
}
|
|
9871
10507
|
if (process.platform !== "darwin") return;
|
|
9872
10508
|
try {
|
|
9873
|
-
|
|
10509
|
+
execFileSync2("osascript", ["-e", 'tell application "Antigravity IDE" to quit'], {
|
|
9874
10510
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9875
10511
|
});
|
|
9876
10512
|
} catch {
|
|
9877
|
-
|
|
10513
|
+
execFileSync2("osascript", ["-e", 'tell application id "com.google.antigravity-ide" to quit'], {
|
|
9878
10514
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9879
10515
|
});
|
|
9880
10516
|
}
|
|
@@ -9890,11 +10526,11 @@ function quitAntigravityAppGracefully() {
|
|
|
9890
10526
|
}
|
|
9891
10527
|
if (process.platform !== "darwin") return;
|
|
9892
10528
|
try {
|
|
9893
|
-
|
|
10529
|
+
execFileSync2("osascript", ["-e", 'tell application "Antigravity" to quit'], {
|
|
9894
10530
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9895
10531
|
});
|
|
9896
10532
|
} catch {
|
|
9897
|
-
|
|
10533
|
+
execFileSync2("osascript", ["-e", 'tell application id "com.google.antigravity" to quit'], {
|
|
9898
10534
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9899
10535
|
});
|
|
9900
10536
|
}
|
|
@@ -9903,15 +10539,15 @@ function findAntigravityAppBinary() {
|
|
|
9903
10539
|
const override = getAppPathOverride("antigravity");
|
|
9904
10540
|
if (override) return existsSync7(override) ? override : null;
|
|
9905
10541
|
if (process.platform === "win32") {
|
|
9906
|
-
const localAppData = process.env["LOCALAPPDATA"] ??
|
|
9907
|
-
const winPath =
|
|
10542
|
+
const localAppData = process.env["LOCALAPPDATA"] ?? join9(homedir7(), "AppData", "Local");
|
|
10543
|
+
const winPath = join9(localAppData, "Programs", "Antigravity", "Antigravity.exe");
|
|
9908
10544
|
return existsSync7(winPath) ? winPath : null;
|
|
9909
10545
|
}
|
|
9910
10546
|
if (process.platform === "linux") return linuxAntigravityBinary();
|
|
9911
10547
|
if (process.platform !== "darwin") return null;
|
|
9912
10548
|
const defaultPath = "/Applications/Antigravity.app/Contents/MacOS/Antigravity";
|
|
9913
10549
|
if (existsSync7(defaultPath)) return defaultPath;
|
|
9914
|
-
const homePath =
|
|
10550
|
+
const homePath = join9(homedir7(), "Applications", "Antigravity.app", "Contents", "MacOS", "Antigravity");
|
|
9915
10551
|
if (existsSync7(homePath)) return homePath;
|
|
9916
10552
|
return null;
|
|
9917
10553
|
}
|
|
@@ -9919,15 +10555,15 @@ function findAntigravityIdeBinary() {
|
|
|
9919
10555
|
const override = getAppPathOverride("antigravity-ide");
|
|
9920
10556
|
if (override) return existsSync7(override) ? override : null;
|
|
9921
10557
|
if (process.platform === "win32") {
|
|
9922
|
-
const localAppData = process.env["LOCALAPPDATA"] ??
|
|
9923
|
-
const winPath =
|
|
10558
|
+
const localAppData = process.env["LOCALAPPDATA"] ?? join9(homedir7(), "AppData", "Local");
|
|
10559
|
+
const winPath = join9(localAppData, "Programs", "Antigravity IDE", "Antigravity IDE.exe");
|
|
9924
10560
|
return existsSync7(winPath) ? winPath : null;
|
|
9925
10561
|
}
|
|
9926
10562
|
if (process.platform === "linux") return linuxAntigravityBinary();
|
|
9927
10563
|
if (process.platform !== "darwin") return null;
|
|
9928
10564
|
const defaultPath = "/Applications/Antigravity IDE.app/Contents/Resources/app/bin/antigravity-ide";
|
|
9929
10565
|
if (existsSync7(defaultPath)) return defaultPath;
|
|
9930
|
-
const homePath =
|
|
10566
|
+
const homePath = join9(homedir7(), "Applications", "Antigravity IDE.app", "Contents", "Resources", "app", "bin", "antigravity-ide");
|
|
9931
10567
|
if (existsSync7(homePath)) return homePath;
|
|
9932
10568
|
return null;
|
|
9933
10569
|
}
|
|
@@ -9988,7 +10624,7 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
|
|
|
9988
10624
|
return;
|
|
9989
10625
|
}
|
|
9990
10626
|
prepareIdeProfile(profileDir, gatewayUrl);
|
|
9991
|
-
const relayExtensionsDir =
|
|
10627
|
+
const relayExtensionsDir = join9(homedir7(), ".relay-ai", "antigravity", "extensions");
|
|
9992
10628
|
const args = [
|
|
9993
10629
|
`--user-data-dir=${profileDir}`,
|
|
9994
10630
|
`--extensions-dir=${relayExtensionsDir}`,
|
|
@@ -10018,7 +10654,7 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
|
|
|
10018
10654
|
|
|
10019
10655
|
// src/antigravity.ts
|
|
10020
10656
|
import { homedir as homedir8 } from "os";
|
|
10021
|
-
import { join as
|
|
10657
|
+
import { join as join10 } from "path";
|
|
10022
10658
|
var SHUTDOWN_DRAIN_MS = 500;
|
|
10023
10659
|
var AGY_FAVORITES_PROVIDER_ID = "__relay_agy_favorites__";
|
|
10024
10660
|
var AGY_FAVORITES_PROVIDER_LABEL = "\u2605 Antigravity CLI Favorites";
|
|
@@ -10312,7 +10948,7 @@ async function runAntigravityAppCommand(childArgs, trace = false, boot) {
|
|
|
10312
10948
|
trace,
|
|
10313
10949
|
boot,
|
|
10314
10950
|
async (env, _routes, gatewayHandle) => {
|
|
10315
|
-
const profileDir =
|
|
10951
|
+
const profileDir = join10(homedir8(), ".relay-ai", "antigravity", "app-profile");
|
|
10316
10952
|
if (isAntigravityAppRunning(profileDir)) {
|
|
10317
10953
|
const restart = await p11.confirm({
|
|
10318
10954
|
message: "Restart Antigravity to apply this Relay gateway?",
|
|
@@ -10360,7 +10996,7 @@ async function runAntigravityIdeCommand(childArgs, trace = false, boot) {
|
|
|
10360
10996
|
trace,
|
|
10361
10997
|
boot,
|
|
10362
10998
|
async (env, _routes, gatewayHandle) => {
|
|
10363
|
-
const profileDir =
|
|
10999
|
+
const profileDir = join10(homedir8(), ".relay-ai", "antigravity", "profile");
|
|
10364
11000
|
if (isAntigravityIdeRunning(profileDir)) {
|
|
10365
11001
|
const restart = await p11.confirm({
|
|
10366
11002
|
message: "Restart Antigravity IDE to apply this Relay gateway?",
|
|
@@ -10405,8 +11041,7 @@ async function runAntigravityIdeCommand(childArgs, trace = false, boot) {
|
|
|
10405
11041
|
// src/codex-app.ts
|
|
10406
11042
|
import pc10 from "picocolors";
|
|
10407
11043
|
import * as p12 from "@clack/prompts";
|
|
10408
|
-
import {
|
|
10409
|
-
import { join as join12 } from "path";
|
|
11044
|
+
import { join as join13 } from "path";
|
|
10410
11045
|
|
|
10411
11046
|
// src/codex/app-provider-routes.ts
|
|
10412
11047
|
function codexRouteToProxyRoute(provider, model, apiKey) {
|
|
@@ -10495,14 +11130,14 @@ async function buildCodexAppProviderCatalogRoutes(provider, apiKey, selectedMode
|
|
|
10495
11130
|
}
|
|
10496
11131
|
|
|
10497
11132
|
// src/codex/app-config.ts
|
|
10498
|
-
import { existsSync as existsSync8, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as
|
|
10499
|
-
import { dirname as dirname2, join as
|
|
11133
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
11134
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
10500
11135
|
import { parse, stringify } from "smol-toml";
|
|
10501
11136
|
function getCodexConfigPath() {
|
|
10502
|
-
return
|
|
11137
|
+
return join11(getCodexHome(), "config.toml");
|
|
10503
11138
|
}
|
|
10504
11139
|
function getCodexAppSidecarProfilePath() {
|
|
10505
|
-
return
|
|
11140
|
+
return join11(getCodexHome(), `${CODEX_APP_PROVIDER_ID}.config.toml`);
|
|
10506
11141
|
}
|
|
10507
11142
|
function asRecord(value) {
|
|
10508
11143
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -10677,8 +11312,13 @@ function applyAppConfigPatch(spec, configPath = getCodexConfigPath()) {
|
|
|
10677
11312
|
const text5 = `${stringify(merged)}
|
|
10678
11313
|
`;
|
|
10679
11314
|
validateAppConfigText(text5, spec);
|
|
10680
|
-
|
|
10681
|
-
|
|
11315
|
+
mkdirSync4(dirname2(configPath), { recursive: true });
|
|
11316
|
+
atomicWriteFile(configPath, text5);
|
|
11317
|
+
const written = readCodexConfigText(configPath);
|
|
11318
|
+
if (written !== text5) {
|
|
11319
|
+
throw new Error(`Codex config readback mismatch at ${configPath}`);
|
|
11320
|
+
}
|
|
11321
|
+
validateAppConfigText(written, spec);
|
|
10682
11322
|
return text5;
|
|
10683
11323
|
}
|
|
10684
11324
|
function applyRestoreKey(config, key, had, value) {
|
|
@@ -10734,7 +11374,7 @@ function restoreConfigFromState(state, configPath = getCodexConfigPath()) {
|
|
|
10734
11374
|
rmSync3(configPath, { force: true });
|
|
10735
11375
|
return true;
|
|
10736
11376
|
}
|
|
10737
|
-
|
|
11377
|
+
writeFileSync4(configPath, `${stringify(config)}
|
|
10738
11378
|
`, "utf8");
|
|
10739
11379
|
return true;
|
|
10740
11380
|
}
|
|
@@ -10745,31 +11385,69 @@ function previewAppConfigToml(spec) {
|
|
|
10745
11385
|
return text5;
|
|
10746
11386
|
}
|
|
10747
11387
|
|
|
11388
|
+
// src/codex/app-readiness.ts
|
|
11389
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
11390
|
+
function proxyRoot(spec) {
|
|
11391
|
+
const base = spec.proxyBaseUrl ?? `http://127.0.0.1:${spec.proxyPort}/v1`;
|
|
11392
|
+
if (!base.endsWith("/v1")) throw new Error("Codex App proxy base URL must end in /v1");
|
|
11393
|
+
return base.slice(0, -3);
|
|
11394
|
+
}
|
|
11395
|
+
async function checkedJson(url, fetchImpl) {
|
|
11396
|
+
const response = await fetchImpl(url);
|
|
11397
|
+
if (!response.ok) throw new Error(`Relay readiness check failed: GET ${url} returned HTTP ${response.status}`);
|
|
11398
|
+
return response.json();
|
|
11399
|
+
}
|
|
11400
|
+
async function verifyCodexAppReadiness(spec, options = {}) {
|
|
11401
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
11402
|
+
const root = proxyRoot(spec);
|
|
11403
|
+
const health = await checkedJson(`${root}/health`, fetchImpl);
|
|
11404
|
+
if (health.ok !== true) throw new Error("Relay proxy health check did not report ready");
|
|
11405
|
+
const catalog = JSON.parse(readFileSync4(spec.catalogPath, "utf8"));
|
|
11406
|
+
if (!Array.isArray(catalog.models) || catalog.models.length === 0) {
|
|
11407
|
+
throw new Error("Relay Codex model catalog is empty or invalid");
|
|
11408
|
+
}
|
|
11409
|
+
const catalogIds = catalog.models.map((model) => model?.slug).filter((id) => typeof id === "string" && id.length > 0);
|
|
11410
|
+
if (catalogIds.length !== catalog.models.length) throw new Error("Relay Codex model catalog contains an invalid model slug");
|
|
11411
|
+
if (!catalogIds.includes(spec.route.modelId)) {
|
|
11412
|
+
throw new Error(`Relay Codex model catalog is missing selected model ${spec.route.modelId}`);
|
|
11413
|
+
}
|
|
11414
|
+
const advertised = await checkedJson(`${root}/v1/models`, fetchImpl);
|
|
11415
|
+
const advertisedIds = new Set((advertised.data ?? []).map((model) => model.id).filter((id) => typeof id === "string"));
|
|
11416
|
+
for (const id of catalogIds) {
|
|
11417
|
+
if (!advertisedIds.has(id)) throw new Error(`Relay proxy does not advertise catalog model ${id}`);
|
|
11418
|
+
}
|
|
11419
|
+
validateAppConfigText(readCodexConfigText(options.configPath), spec);
|
|
11420
|
+
}
|
|
11421
|
+
|
|
10748
11422
|
// src/codex/app-session.ts
|
|
10749
11423
|
import {
|
|
10750
11424
|
copyFileSync as copyFileSync2,
|
|
10751
11425
|
existsSync as existsSync9,
|
|
10752
|
-
mkdirSync as
|
|
11426
|
+
mkdirSync as mkdirSync5,
|
|
10753
11427
|
readdirSync as readdirSync2,
|
|
10754
|
-
readFileSync as
|
|
11428
|
+
readFileSync as readFileSync5,
|
|
10755
11429
|
rmSync as rmSync4,
|
|
10756
11430
|
statSync as statSync2
|
|
10757
11431
|
} from "fs";
|
|
10758
|
-
import { basename as basename2, join as
|
|
11432
|
+
import { basename as basename2, join as join12 } from "path";
|
|
11433
|
+
import { createHash as createHash3 } from "crypto";
|
|
10759
11434
|
function getAppSessionLockPath(env = process.env) {
|
|
10760
|
-
return
|
|
11435
|
+
return join12(getRelayAiCodexDir(env), "session-app.json");
|
|
10761
11436
|
}
|
|
10762
11437
|
function getAppRestoreStatePath(env = process.env) {
|
|
10763
|
-
return
|
|
11438
|
+
return join12(getRelayAiCodexDir(env), "app-restore-state.json");
|
|
10764
11439
|
}
|
|
10765
11440
|
function getAppCatalogPath(providerId, env = process.env) {
|
|
10766
|
-
return
|
|
11441
|
+
return join12(getRelayAiCodexDir(env), `app-models-${providerId}.json`);
|
|
11442
|
+
}
|
|
11443
|
+
function fileSha256(path3) {
|
|
11444
|
+
return createHash3("sha256").update(readFileSync5(path3)).digest("hex");
|
|
10767
11445
|
}
|
|
10768
11446
|
function readAppSessionLock(env = process.env) {
|
|
10769
11447
|
const path3 = getAppSessionLockPath(env);
|
|
10770
11448
|
if (!existsSync9(path3)) return null;
|
|
10771
11449
|
try {
|
|
10772
|
-
const parsed = JSON.parse(
|
|
11450
|
+
const parsed = JSON.parse(readFileSync5(path3, "utf8"));
|
|
10773
11451
|
if (typeof parsed.pid === "number" && typeof parsed.startedAt === "string") return parsed;
|
|
10774
11452
|
} catch {
|
|
10775
11453
|
}
|
|
@@ -10787,7 +11465,7 @@ function readAppRestoreState(env = process.env) {
|
|
|
10787
11465
|
const path3 = getAppRestoreStatePath(env);
|
|
10788
11466
|
if (!existsSync9(path3)) return null;
|
|
10789
11467
|
try {
|
|
10790
|
-
return JSON.parse(
|
|
11468
|
+
return JSON.parse(readFileSync5(path3, "utf8"));
|
|
10791
11469
|
} catch {
|
|
10792
11470
|
return null;
|
|
10793
11471
|
}
|
|
@@ -10806,9 +11484,9 @@ function backupConfigToml(env = process.env) {
|
|
|
10806
11484
|
if (!existsSync9(configPath)) return void 0;
|
|
10807
11485
|
rotateBackups(configPath, env);
|
|
10808
11486
|
const backupsDir = getBackupsDir(env);
|
|
10809
|
-
|
|
11487
|
+
mkdirSync5(backupsDir, { recursive: true });
|
|
10810
11488
|
const base = basename2(configPath);
|
|
10811
|
-
const backupPath =
|
|
11489
|
+
const backupPath = join12(backupsDir, `${base}.${Date.now()}.bak`);
|
|
10812
11490
|
copyFileSync2(configPath, backupPath);
|
|
10813
11491
|
return backupPath;
|
|
10814
11492
|
}
|
|
@@ -10825,7 +11503,7 @@ function saveAppRestoreStateBeforePatch(env = process.env) {
|
|
|
10825
11503
|
function ownedAppCatalogPaths(env = process.env) {
|
|
10826
11504
|
const codexDir = getRelayAiCodexDir(env);
|
|
10827
11505
|
if (!existsSync9(codexDir)) return [];
|
|
10828
|
-
return readdirSync2(codexDir).filter((n) => n.startsWith("app-models-") && n.endsWith(".json")).map((n) =>
|
|
11506
|
+
return readdirSync2(codexDir).filter((n) => n.startsWith("app-models-") && n.endsWith(".json")).map((n) => join12(codexDir, n));
|
|
10829
11507
|
}
|
|
10830
11508
|
function removeAppCatalogs(env = process.env) {
|
|
10831
11509
|
const removed = [];
|
|
@@ -10843,7 +11521,7 @@ function newestConfigBackup(env = process.env) {
|
|
|
10843
11521
|
if (!existsSync9(backupDir)) return null;
|
|
10844
11522
|
const configBase = basename2(getCodexConfigPath());
|
|
10845
11523
|
const candidates = readdirSync2(backupDir).filter((name) => name.startsWith(`${configBase}.`) && name.endsWith(".bak")).map((name) => {
|
|
10846
|
-
const path3 =
|
|
11524
|
+
const path3 = join12(backupDir, name);
|
|
10847
11525
|
try {
|
|
10848
11526
|
return { path: path3, mtimeMs: statSync2(path3).mtimeMs };
|
|
10849
11527
|
} catch {
|
|
@@ -10869,7 +11547,12 @@ function restoreCodexAppOverlay(env = process.env) {
|
|
|
10869
11547
|
clearAppSessionLock(env);
|
|
10870
11548
|
return { restored: false, message: "Nothing to restore." };
|
|
10871
11549
|
}
|
|
10872
|
-
|
|
11550
|
+
const exactBackupIsSafe = Boolean(
|
|
11551
|
+
managed && lock?.backupPath && existsSync9(lock.backupPath) && lock.patchedConfigSha256 && lock.originalConfigSha256 && fileSha256(getCodexConfigPath()) === lock.patchedConfigSha256 && fileSha256(lock.backupPath) === lock.originalConfigSha256
|
|
11552
|
+
);
|
|
11553
|
+
if (exactBackupIsSafe) {
|
|
11554
|
+
copyFileSync2(lock.backupPath, getCodexConfigPath());
|
|
11555
|
+
} else if (restoreState) {
|
|
10873
11556
|
restoreConfigFromState(restoreState);
|
|
10874
11557
|
} else if (lock?.backupPath && existsSync9(lock.backupPath)) {
|
|
10875
11558
|
copyFileSync2(lock.backupPath, getCodexConfigPath());
|
|
@@ -10945,10 +11628,15 @@ function codexProxyRouteToCodexRoute(route, fallbackProviderId) {
|
|
|
10945
11628
|
refreshToken: route.refreshToken
|
|
10946
11629
|
};
|
|
10947
11630
|
}
|
|
10948
|
-
|
|
11631
|
+
function codexAppUsesExplicitSelection(configOnly, launchProvider, launchModel) {
|
|
11632
|
+
void configOnly;
|
|
11633
|
+
return Boolean(launchProvider && launchModel);
|
|
11634
|
+
}
|
|
11635
|
+
async function waitForShutdownWithConfirm(assumeYes = false) {
|
|
10949
11636
|
while (true) {
|
|
10950
11637
|
const signal = await waitForShutdown2();
|
|
10951
|
-
if (signal !== "sigint")
|
|
11638
|
+
if (signal !== "sigint") return signal;
|
|
11639
|
+
if (assumeYes) return signal;
|
|
10952
11640
|
console.log("");
|
|
10953
11641
|
const choice = await p12.select({
|
|
10954
11642
|
message: "Close ChatGPT Desktop and restore your Codex config?",
|
|
@@ -10957,11 +11645,19 @@ async function waitForShutdownWithConfirm() {
|
|
|
10957
11645
|
{ value: "no", label: "No, keep session running" }
|
|
10958
11646
|
]
|
|
10959
11647
|
});
|
|
10960
|
-
if (p12.isCancel(choice) || choice === "yes")
|
|
11648
|
+
if (p12.isCancel(choice) || choice === "yes") return signal;
|
|
10961
11649
|
}
|
|
10962
11650
|
}
|
|
10963
|
-
|
|
11651
|
+
function unattendedShutdownClosesApp(assumeYes, signal) {
|
|
11652
|
+
return assumeYes && signal !== "sigint";
|
|
11653
|
+
}
|
|
11654
|
+
async function maybeCloseRunningCodexApp(assumeYes = false) {
|
|
10964
11655
|
if (!isCodexAppRunning()) return;
|
|
11656
|
+
if (assumeYes) {
|
|
11657
|
+
p12.log.step("Stopping ChatGPT Desktop...");
|
|
11658
|
+
quitCodexAppGracefully();
|
|
11659
|
+
return;
|
|
11660
|
+
}
|
|
10965
11661
|
const shouldClose = await p12.confirm({ message: "ChatGPT Desktop is still running. Close it?" });
|
|
10966
11662
|
if (shouldClose && !p12.isCancel(shouldClose)) {
|
|
10967
11663
|
p12.log.step("Stopping ChatGPT Desktop...");
|
|
@@ -10985,6 +11681,7 @@ ${pc10.bold("Options:")}
|
|
|
10985
11681
|
--vertex Use Claude models through Google Vertex AI
|
|
10986
11682
|
--with-native Load native Codex models beside Relay models for this launch
|
|
10987
11683
|
--relay-only Keep the current Relay-only launch behavior
|
|
11684
|
+
--yes, -y Approve a fully specified launch/restart without prompting
|
|
10988
11685
|
--restore Restore Codex config after an interrupted app session
|
|
10989
11686
|
--config Preview the generated Codex app configuration without launching
|
|
10990
11687
|
--trace Write proxy debug logs to ~/.relay-ai/logs/ and show errors on exit
|
|
@@ -11011,6 +11708,7 @@ ${pc10.bold("Preview (no writes):")}
|
|
|
11011
11708
|
${pc10.bold("Examples:")}
|
|
11012
11709
|
relay-ai codex-app
|
|
11013
11710
|
relay-ai codex-app --vertex
|
|
11711
|
+
relay-ai codex-app --provider antigravity --model gemini-3.1-pro-high --with-native --yes
|
|
11014
11712
|
relay-ai codex-app --config
|
|
11015
11713
|
relay-ai codex-app --restore
|
|
11016
11714
|
|
|
@@ -11119,8 +11817,10 @@ async function runCodexAppVertexLaunch(configOnly, trace = false) {
|
|
|
11119
11817
|
catalogPath
|
|
11120
11818
|
};
|
|
11121
11819
|
saveAppRestoreStateBeforePatch();
|
|
11820
|
+
sessionActive = true;
|
|
11122
11821
|
const backupPath = backupConfigToml();
|
|
11123
11822
|
applyAppConfigPatch(spec);
|
|
11823
|
+
await verifyCodexAppReadiness(spec);
|
|
11124
11824
|
writeAppSessionLock({
|
|
11125
11825
|
pid: process.pid,
|
|
11126
11826
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11128,9 +11828,10 @@ async function runCodexAppVertexLaunch(configOnly, trace = false) {
|
|
|
11128
11828
|
catalogPaths: [catalogPath],
|
|
11129
11829
|
restoreStatePath: getAppRestoreStatePath(),
|
|
11130
11830
|
backupPath,
|
|
11131
|
-
proxyPort
|
|
11831
|
+
proxyPort,
|
|
11832
|
+
patchedConfigSha256: fileSha256(getCodexConfigPath()),
|
|
11833
|
+
...backupPath ? { originalConfigSha256: fileSha256(backupPath) } : {}
|
|
11132
11834
|
});
|
|
11133
|
-
sessionActive = true;
|
|
11134
11835
|
p12.log.info(`Vertex AI \xB7 ${selectedEntry.display_name} \u2014 project: ${config.project} / location: ${config.location}`);
|
|
11135
11836
|
logProxy(proxyPort);
|
|
11136
11837
|
logActiveModel(selectedEntry.display_name, selectedEntry.id);
|
|
@@ -11139,6 +11840,7 @@ async function runCodexAppVertexLaunch(configOnly, trace = false) {
|
|
|
11139
11840
|
} catch (err) {
|
|
11140
11841
|
p12.log.warn(String(err instanceof Error ? err.message : err));
|
|
11141
11842
|
p12.log.info(codexAppInstallHint());
|
|
11843
|
+
throw err;
|
|
11142
11844
|
}
|
|
11143
11845
|
printCodexAppSessionPanel({
|
|
11144
11846
|
modelLabel: selectedEntry.display_name,
|
|
@@ -11170,6 +11872,13 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11170
11872
|
console.log(result.message);
|
|
11171
11873
|
return result.liveSession ? 1 : 0;
|
|
11172
11874
|
}
|
|
11875
|
+
const configOnly = args.includes("--config");
|
|
11876
|
+
if (opts.assumeYes && !configOnly) {
|
|
11877
|
+
if (opts.vertex || !opts.launchProvider || !opts.launchModel || !opts.codexLaunchMode) {
|
|
11878
|
+
console.error(pc10.red("--yes requires --provider, --model, and either --with-native or --relay-only."));
|
|
11879
|
+
return 1;
|
|
11880
|
+
}
|
|
11881
|
+
}
|
|
11173
11882
|
try {
|
|
11174
11883
|
codexAppSupported();
|
|
11175
11884
|
} catch (err) {
|
|
@@ -11177,7 +11886,6 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11177
11886
|
return 1;
|
|
11178
11887
|
}
|
|
11179
11888
|
const interrupted = recoverInterruptedCodexAppSession();
|
|
11180
|
-
const configOnly = args.includes("--config");
|
|
11181
11889
|
const trace = args.includes("--trace");
|
|
11182
11890
|
const debugLogPath = getCodexProxyDebugLogPath();
|
|
11183
11891
|
if (trace && !configOnly) {
|
|
@@ -11185,7 +11893,7 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11185
11893
|
}
|
|
11186
11894
|
const isTty = Boolean(process.stdin.isTTY);
|
|
11187
11895
|
if (!configOnly) {
|
|
11188
|
-
const sessionCheck = checkAppSessionLock(isTty);
|
|
11896
|
+
const sessionCheck = checkAppSessionLock(isTty || Boolean(opts.assumeYes));
|
|
11189
11897
|
if (!sessionCheck.ok) {
|
|
11190
11898
|
if (sessionCheck.reason === "non_tty") {
|
|
11191
11899
|
console.error(pc10.red("relay-ai codex-app requires an interactive terminal."));
|
|
@@ -11243,7 +11951,7 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11243
11951
|
compatible.find((lp) => lp.id === prefs.lastCodexProvider) ?? compatible[0]
|
|
11244
11952
|
);
|
|
11245
11953
|
let selectedModel = activeProvider.models.find((m) => m.id === prefs.lastCodexModel) ?? activeProvider.models[0];
|
|
11246
|
-
if (
|
|
11954
|
+
if (codexAppUsesExplicitSelection(configOnly, opts.launchProvider, opts.launchModel)) {
|
|
11247
11955
|
const bootSelection = resolveBootSelection(
|
|
11248
11956
|
compatible,
|
|
11249
11957
|
opts.launchProvider,
|
|
@@ -11314,7 +12022,7 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11314
12022
|
try {
|
|
11315
12023
|
const embeddedBinary = findEmbeddedCodexBinary();
|
|
11316
12024
|
if (!embeddedBinary) throw new Error("Embedded ChatGPT/Codex runtime was not found; mixed Desktop mode is unavailable on this installation");
|
|
11317
|
-
const version =
|
|
12025
|
+
const version = runCodexCommandSync(embeddedBinary, ["--version"]).stdout.trim();
|
|
11318
12026
|
const mixedModels = await resolveCodexMixedModels({
|
|
11319
12027
|
activeProvider,
|
|
11320
12028
|
selectedModel,
|
|
@@ -11322,6 +12030,11 @@ async function runCodexAppCommand(args, opts = {}) {
|
|
|
11322
12030
|
generalFavorites: favorites,
|
|
11323
12031
|
subagentFavorites: prefs.codexSubagentModels ?? []
|
|
11324
12032
|
});
|
|
12033
|
+
if (mixedModels.capacitySkipped.length > 0) {
|
|
12034
|
+
p12.log.warn(
|
|
12035
|
+
`Skipped ${mixedModels.capacitySkipped.length} favorite(s) because the mixed catalog is full: ` + mixedModels.capacitySkipped.map((f) => `${f.providerId}:${f.modelId}`).join(", ")
|
|
12036
|
+
);
|
|
12037
|
+
}
|
|
11325
12038
|
assertConfiguredCodexSubagentsResolved(prefs.codexSubagentModels ?? [], mixedModels);
|
|
11326
12039
|
const multiAgentV2Supported = mixedModels.subagents.length === 0 || supportsMultiAgentV2(embeddedBinary);
|
|
11327
12040
|
if (!multiAgentV2Supported) {
|
|
@@ -11345,7 +12058,7 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11345
12058
|
return 1;
|
|
11346
12059
|
}
|
|
11347
12060
|
}
|
|
11348
|
-
if (!configOnly) {
|
|
12061
|
+
if (!configOnly && !opts.assumeYes) {
|
|
11349
12062
|
const modelLabel = formatCodexModelLabel(selectedModel);
|
|
11350
12063
|
const confirmed = await confirmCodexLaunch(
|
|
11351
12064
|
activeProvider.name,
|
|
@@ -11361,7 +12074,7 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11361
12074
|
let proxyHandle = null;
|
|
11362
12075
|
let sessionActive = false;
|
|
11363
12076
|
try {
|
|
11364
|
-
const catalogPath = mixedPlan ?
|
|
12077
|
+
const catalogPath = mixedPlan ? join13(getRelayAiCodexDir(), "app-models-mixed.json") : favoritesActive && resolvedFavorites.length > 0 ? getFavoritesAppCatalogPath() : getAppCatalogPath(route.providerId);
|
|
11365
12078
|
const activeRoute = mixedPlan ? {
|
|
11366
12079
|
tier: "proxy",
|
|
11367
12080
|
modelId: mixedPlan.selectedSlug,
|
|
@@ -11424,10 +12137,12 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11424
12137
|
return 0;
|
|
11425
12138
|
}
|
|
11426
12139
|
let proxyPort;
|
|
12140
|
+
const routeAuditPath = mixedPlan ? prepareCodexRouteAuditLog() : void 0;
|
|
11427
12141
|
if (mixedPlan) {
|
|
11428
12142
|
proxyHandle = await startCodexProxy(mixedPlan.relayRoutes, {
|
|
11429
12143
|
requireAuth: false,
|
|
11430
12144
|
debug: trace,
|
|
12145
|
+
routeAuditPath,
|
|
11431
12146
|
mixedNative: {
|
|
11432
12147
|
nativeModelIds: mixedPlan.nativeModelIds,
|
|
11433
12148
|
subagentRouteModelId: mixedPlan.subagentRouteModelId,
|
|
@@ -11436,6 +12151,7 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11436
12151
|
}
|
|
11437
12152
|
});
|
|
11438
12153
|
proxyPort = proxyHandle.port;
|
|
12154
|
+
p12.log.info(`Route audit (metadata only): ${routeAuditPath}`);
|
|
11439
12155
|
} else if (favoritesActive && resolvedFavorites.length > 0) {
|
|
11440
12156
|
const needsBackend = (r) => {
|
|
11441
12157
|
const m = r.model;
|
|
@@ -11495,8 +12211,10 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11495
12211
|
...mixedPlan ? { proxyBaseUrl: `${mixedProxyBaseUrl(proxyPort, mixedPlan.capability)}/v1` } : {}
|
|
11496
12212
|
};
|
|
11497
12213
|
saveAppRestoreStateBeforePatch();
|
|
12214
|
+
sessionActive = true;
|
|
11498
12215
|
const backupPath = backupConfigToml();
|
|
11499
12216
|
applyAppConfigPatch(spec);
|
|
12217
|
+
await verifyCodexAppReadiness(spec);
|
|
11500
12218
|
writeAppSessionLock({
|
|
11501
12219
|
pid: process.pid,
|
|
11502
12220
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11504,9 +12222,10 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11504
12222
|
catalogPaths: [catalogPath],
|
|
11505
12223
|
restoreStatePath: getAppRestoreStatePath(),
|
|
11506
12224
|
backupPath,
|
|
11507
|
-
proxyPort
|
|
12225
|
+
proxyPort,
|
|
12226
|
+
patchedConfigSha256: fileSha256(getCodexConfigPath()),
|
|
12227
|
+
...backupPath ? { originalConfigSha256: fileSha256(backupPath) } : {}
|
|
11508
12228
|
});
|
|
11509
|
-
sessionActive = true;
|
|
11510
12229
|
const prevRecent = prefs.recentModelsByProvider?.[activeProvider.id] ?? [];
|
|
11511
12230
|
const updatedRecent = [selectedModel.id, ...prevRecent.filter((id) => id !== selectedModel.id)].slice(0, 3);
|
|
11512
12231
|
savePreferences({
|
|
@@ -11517,10 +12236,11 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11517
12236
|
logProxy(proxyPort);
|
|
11518
12237
|
logActiveModel(modelLabel, selectedModel.id);
|
|
11519
12238
|
try {
|
|
11520
|
-
await launchOrRestartCodexApp();
|
|
12239
|
+
await launchOrRestartCodexApp(void 0, opts.assumeYes);
|
|
11521
12240
|
} catch (err) {
|
|
11522
12241
|
p12.log.warn(String(err instanceof Error ? err.message : err));
|
|
11523
12242
|
p12.log.info(codexAppInstallHint());
|
|
12243
|
+
throw err;
|
|
11524
12244
|
}
|
|
11525
12245
|
printCodexAppSessionPanel({
|
|
11526
12246
|
modelLabel,
|
|
@@ -11529,14 +12249,21 @@ Mixed Codex App mode is unavailable: ${err instanceof Error ? err.message : err}
|
|
|
11529
12249
|
restoreCommand: "relay-ai codex-app --restore"
|
|
11530
12250
|
});
|
|
11531
12251
|
codexAppOutro(modelLabel);
|
|
11532
|
-
await waitForShutdownWithConfirm();
|
|
12252
|
+
const shutdownSignal = await waitForShutdownWithConfirm(opts.assumeYes);
|
|
11533
12253
|
if (trace) printTraceLog(debugLogPath);
|
|
11534
12254
|
console.log("");
|
|
11535
12255
|
if (sessionActive) {
|
|
11536
12256
|
restoreCodexAppOverlay();
|
|
11537
12257
|
sessionActive = false;
|
|
11538
12258
|
}
|
|
11539
|
-
|
|
12259
|
+
if (unattendedShutdownClosesApp(Boolean(opts.assumeYes), shutdownSignal)) {
|
|
12260
|
+
if (isCodexAppRunning()) {
|
|
12261
|
+
p12.log.step("Stopping ChatGPT Desktop after unattended Relay shutdown...");
|
|
12262
|
+
quitCodexAppGracefully();
|
|
12263
|
+
}
|
|
12264
|
+
} else {
|
|
12265
|
+
await maybeCloseRunningCodexApp(opts.assumeYes);
|
|
12266
|
+
}
|
|
11540
12267
|
return 0;
|
|
11541
12268
|
} finally {
|
|
11542
12269
|
proxyHandle?.close();
|
|
@@ -11555,38 +12282,38 @@ import pc11 from "picocolors";
|
|
|
11555
12282
|
import * as p13 from "@clack/prompts";
|
|
11556
12283
|
|
|
11557
12284
|
// src/claude-desktop/app-config.ts
|
|
11558
|
-
import { existsSync as existsSync10, readFileSync as
|
|
12285
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
|
|
11559
12286
|
import { homedir as homedir9 } from "os";
|
|
11560
|
-
import { join as
|
|
11561
|
-
import { randomUUID as
|
|
12287
|
+
import { join as join14, dirname as dirname3 } from "path";
|
|
12288
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
11562
12289
|
function getClaudeDesktopHome() {
|
|
11563
12290
|
if (process.platform === "win32") {
|
|
11564
|
-
return
|
|
12291
|
+
return join14(process.env.LOCALAPPDATA || join14(homedir9(), "AppData", "Local"), "Claude-3p");
|
|
11565
12292
|
}
|
|
11566
12293
|
if (process.platform === "linux") {
|
|
11567
|
-
return
|
|
12294
|
+
return join14(process.env.XDG_CONFIG_HOME || join14(homedir9(), ".config"), "Claude-3p");
|
|
11568
12295
|
}
|
|
11569
|
-
return
|
|
12296
|
+
return join14(homedir9(), "Library", "Application Support", "Claude-3p");
|
|
11570
12297
|
}
|
|
11571
12298
|
function getConfigLibraryPath() {
|
|
11572
|
-
return
|
|
12299
|
+
return join14(getClaudeDesktopHome(), "configLibrary");
|
|
11573
12300
|
}
|
|
11574
12301
|
function getMetaJsonPath() {
|
|
11575
|
-
return
|
|
12302
|
+
return join14(getConfigLibraryPath(), "_meta.json");
|
|
11576
12303
|
}
|
|
11577
12304
|
function readMetaJson() {
|
|
11578
12305
|
const metaPath = getMetaJsonPath();
|
|
11579
12306
|
if (!existsSync10(metaPath)) return null;
|
|
11580
12307
|
try {
|
|
11581
|
-
return JSON.parse(
|
|
12308
|
+
return JSON.parse(readFileSync6(metaPath, "utf8"));
|
|
11582
12309
|
} catch {
|
|
11583
12310
|
return null;
|
|
11584
12311
|
}
|
|
11585
12312
|
}
|
|
11586
12313
|
function writeMetaJson(meta) {
|
|
11587
12314
|
const metaPath = getMetaJsonPath();
|
|
11588
|
-
|
|
11589
|
-
|
|
12315
|
+
mkdirSync6(dirname3(metaPath), { recursive: true });
|
|
12316
|
+
writeFileSync5(metaPath, `${JSON.stringify(meta, null, 2)}
|
|
11590
12317
|
`, "utf8");
|
|
11591
12318
|
}
|
|
11592
12319
|
function buildRelayAiConfig(proxyPort) {
|
|
@@ -11599,11 +12326,11 @@ function buildRelayAiConfig(proxyPort) {
|
|
|
11599
12326
|
};
|
|
11600
12327
|
}
|
|
11601
12328
|
function writeRelayAiConfig(proxyPort) {
|
|
11602
|
-
const uuid =
|
|
11603
|
-
const configPath =
|
|
12329
|
+
const uuid = randomUUID3();
|
|
12330
|
+
const configPath = join14(getConfigLibraryPath(), `${uuid}.json`);
|
|
11604
12331
|
const config = buildRelayAiConfig(proxyPort);
|
|
11605
|
-
|
|
11606
|
-
|
|
12332
|
+
mkdirSync6(dirname3(configPath), { recursive: true });
|
|
12333
|
+
writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
|
|
11607
12334
|
`, "utf8");
|
|
11608
12335
|
const meta = readMetaJson() || { appliedId: "", entries: [] };
|
|
11609
12336
|
meta.appliedId = uuid;
|
|
@@ -11758,22 +12485,22 @@ async function buildClaudeAppServerCatalog(entries, providersById, trace) {
|
|
|
11758
12485
|
import {
|
|
11759
12486
|
copyFileSync as copyFileSync3,
|
|
11760
12487
|
existsSync as existsSync11,
|
|
11761
|
-
mkdirSync as
|
|
11762
|
-
readFileSync as
|
|
12488
|
+
mkdirSync as mkdirSync7,
|
|
12489
|
+
readFileSync as readFileSync7,
|
|
11763
12490
|
renameSync as renameSync2,
|
|
11764
12491
|
rmSync as rmSync5,
|
|
11765
12492
|
unlinkSync as unlinkSync2,
|
|
11766
|
-
writeFileSync as
|
|
12493
|
+
writeFileSync as writeFileSync6
|
|
11767
12494
|
} from "fs";
|
|
11768
|
-
import { dirname as dirname4, join as
|
|
12495
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
11769
12496
|
function getSessionLockPath2() {
|
|
11770
|
-
return
|
|
12497
|
+
return join15(getClaudeDesktopHome(), ".relay-ai.lock");
|
|
11771
12498
|
}
|
|
11772
12499
|
function inspectSessionLock() {
|
|
11773
12500
|
const path3 = getSessionLockPath2();
|
|
11774
12501
|
if (!existsSync11(path3)) return { status: "missing" };
|
|
11775
12502
|
try {
|
|
11776
|
-
const parsed = JSON.parse(
|
|
12503
|
+
const parsed = JSON.parse(readFileSync7(path3, "utf8"));
|
|
11777
12504
|
if (typeof parsed.pid === "number" && typeof parsed.startedAt === "string" && typeof parsed.uuid === "string" && typeof parsed.proxyPort === "number") {
|
|
11778
12505
|
return { status: "valid", lock: parsed };
|
|
11779
12506
|
}
|
|
@@ -11784,9 +12511,9 @@ function inspectSessionLock() {
|
|
|
11784
12511
|
function writeSessionLock2(lock) {
|
|
11785
12512
|
const path3 = getSessionLockPath2();
|
|
11786
12513
|
const tempPath = `${path3}.tmp.${process.pid}`;
|
|
11787
|
-
|
|
12514
|
+
mkdirSync7(dirname4(path3), { recursive: true });
|
|
11788
12515
|
try {
|
|
11789
|
-
|
|
12516
|
+
writeFileSync6(tempPath, `${JSON.stringify(lock, null, 2)}
|
|
11790
12517
|
`, "utf8");
|
|
11791
12518
|
renameSync2(tempPath, path3);
|
|
11792
12519
|
} finally {
|
|
@@ -11821,7 +12548,7 @@ function restoreMetaJson() {
|
|
|
11821
12548
|
}
|
|
11822
12549
|
}
|
|
11823
12550
|
function removeRelayAiConfig(uuid) {
|
|
11824
|
-
const configPath =
|
|
12551
|
+
const configPath = join15(getConfigLibraryPath(), `${uuid}.json`);
|
|
11825
12552
|
if (existsSync11(configPath)) {
|
|
11826
12553
|
try {
|
|
11827
12554
|
rmSync5(configPath, { force: true });
|
|
@@ -12139,17 +12866,17 @@ ${pc11.bold("Claude Desktop 3P Mode Active")}`);
|
|
|
12139
12866
|
}
|
|
12140
12867
|
|
|
12141
12868
|
// src/ai-doc.ts
|
|
12142
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
12869
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
12143
12870
|
import { homedir as homedir10 } from "os";
|
|
12144
|
-
import { join as
|
|
12871
|
+
import { join as join16 } from "path";
|
|
12145
12872
|
var SKILL_DIR_NAME = "relay-ai-cli";
|
|
12146
12873
|
var SKILL_INSTALL_DIRS = [
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12874
|
+
join16(getAppHome(), "skills"),
|
|
12875
|
+
join16(homedir10(), ".claude", "skills"),
|
|
12876
|
+
join16(homedir10(), ".agents", "skills"),
|
|
12877
|
+
join16(homedir10(), ".codex", "skills"),
|
|
12878
|
+
join16(homedir10(), ".cursor", "skills"),
|
|
12879
|
+
join16(homedir10(), ".cursor", "skills-cursor")
|
|
12153
12880
|
];
|
|
12154
12881
|
function parseSkillVersion(content) {
|
|
12155
12882
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -12163,10 +12890,10 @@ function parseSkillVersion(content) {
|
|
|
12163
12890
|
return null;
|
|
12164
12891
|
}
|
|
12165
12892
|
function readInstalledSkillVersion(skillDir) {
|
|
12166
|
-
const skillPath =
|
|
12893
|
+
const skillPath = join16(skillDir, "SKILL.md");
|
|
12167
12894
|
if (!existsSync12(skillPath)) return null;
|
|
12168
12895
|
try {
|
|
12169
|
-
const head =
|
|
12896
|
+
const head = readFileSync8(skillPath, "utf-8").slice(0, 1024);
|
|
12170
12897
|
return parseSkillVersion(head.includes("---", 4) ? head : `${head}
|
|
12171
12898
|
---
|
|
12172
12899
|
`);
|
|
@@ -12176,8 +12903,8 @@ function readInstalledSkillVersion(skillDir) {
|
|
|
12176
12903
|
}
|
|
12177
12904
|
function skillInstallTargets() {
|
|
12178
12905
|
return SKILL_INSTALL_DIRS.map((dir) => {
|
|
12179
|
-
const skillDir =
|
|
12180
|
-
return { skillDir, skillPath:
|
|
12906
|
+
const skillDir = join16(dir, SKILL_DIR_NAME);
|
|
12907
|
+
return { skillDir, skillPath: join16(skillDir, "SKILL.md") };
|
|
12181
12908
|
});
|
|
12182
12909
|
}
|
|
12183
12910
|
function formatProviderModels(provider) {
|
|
@@ -12681,8 +13408,8 @@ function installAiDoc(opts = {}) {
|
|
|
12681
13408
|
result.skipped.push(skillPath);
|
|
12682
13409
|
continue;
|
|
12683
13410
|
}
|
|
12684
|
-
|
|
12685
|
-
|
|
13411
|
+
mkdirSync8(skillDir, { recursive: true });
|
|
13412
|
+
writeFileSync7(skillPath, doc, "utf-8");
|
|
12686
13413
|
if (previous) {
|
|
12687
13414
|
result.updated.push({ path: skillPath, fromVersion: previous });
|
|
12688
13415
|
} else {
|
|
@@ -12840,18 +13567,18 @@ function buildHttpProxyChildEnv(baseEnv, proxyUrl, caCertPath) {
|
|
|
12840
13567
|
}
|
|
12841
13568
|
|
|
12842
13569
|
// src/http-proxy/ca.ts
|
|
12843
|
-
import { randomBytes as randomBytes2, randomUUID as
|
|
13570
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID4 } from "crypto";
|
|
12844
13571
|
import {
|
|
12845
|
-
chmodSync as
|
|
13572
|
+
chmodSync as chmodSync3,
|
|
12846
13573
|
existsSync as existsSync13,
|
|
12847
|
-
mkdirSync as
|
|
12848
|
-
readFileSync as
|
|
13574
|
+
mkdirSync as mkdirSync10,
|
|
13575
|
+
readFileSync as readFileSync9,
|
|
12849
13576
|
readdirSync as readdirSync3,
|
|
12850
13577
|
rmSync as rmSync6,
|
|
12851
13578
|
statSync as statSync3,
|
|
12852
|
-
writeFileSync as
|
|
13579
|
+
writeFileSync as writeFileSync9
|
|
12853
13580
|
} from "fs";
|
|
12854
|
-
import { dirname as dirname6, join as
|
|
13581
|
+
import { dirname as dirname6, join as join18, resolve } from "path";
|
|
12855
13582
|
import forge from "node-forge";
|
|
12856
13583
|
var SESSION_ROOT = "http-proxy-sessions";
|
|
12857
13584
|
var OWNER_FILE = "owner.pid";
|
|
@@ -12871,22 +13598,22 @@ function processIsRunning(pid) {
|
|
|
12871
13598
|
}
|
|
12872
13599
|
}
|
|
12873
13600
|
function cleanupStaleHttpProxySessions(appHome = getAppHome()) {
|
|
12874
|
-
const root =
|
|
13601
|
+
const root = join18(appHome, SESSION_ROOT);
|
|
12875
13602
|
if (!existsSync13(root)) return;
|
|
12876
13603
|
const now = Date.now();
|
|
12877
13604
|
for (const name of readdirSync3(root)) {
|
|
12878
|
-
const sessionDir =
|
|
13605
|
+
const sessionDir = join18(root, name);
|
|
12879
13606
|
try {
|
|
12880
13607
|
const stat = statSync3(sessionDir);
|
|
12881
13608
|
if (!stat.isDirectory()) continue;
|
|
12882
|
-
const ownerPath =
|
|
13609
|
+
const ownerPath = join18(sessionDir, OWNER_FILE);
|
|
12883
13610
|
if (!existsSync13(ownerPath)) {
|
|
12884
13611
|
if (now - stat.mtimeMs > MID_CREATION_GRACE_MS) {
|
|
12885
13612
|
rmSync6(sessionDir, { recursive: true, force: true });
|
|
12886
13613
|
}
|
|
12887
13614
|
continue;
|
|
12888
13615
|
}
|
|
12889
|
-
const pid = Number(
|
|
13616
|
+
const pid = Number(readFileSync9(ownerPath, "utf8").trim());
|
|
12890
13617
|
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
12891
13618
|
const ownerStat = statSync3(ownerPath);
|
|
12892
13619
|
const newestMtimeMs = Math.max(stat.mtimeMs, ownerStat.mtimeMs);
|
|
@@ -12902,13 +13629,13 @@ function cleanupStaleHttpProxySessions(appHome = getAppHome()) {
|
|
|
12902
13629
|
}
|
|
12903
13630
|
function createHttpProxyCertificates(appHome = getAppHome()) {
|
|
12904
13631
|
cleanupStaleHttpProxySessions(appHome);
|
|
12905
|
-
const root =
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
const sessionDir =
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
13632
|
+
const root = join18(appHome, SESSION_ROOT);
|
|
13633
|
+
mkdirSync10(root, { recursive: true, mode: 448 });
|
|
13634
|
+
chmodSync3(root, 448);
|
|
13635
|
+
const sessionDir = join18(root, randomUUID4());
|
|
13636
|
+
mkdirSync10(sessionDir, { mode: 448 });
|
|
13637
|
+
chmodSync3(sessionDir, 448);
|
|
13638
|
+
writeFileSync9(join18(sessionDir, OWNER_FILE), `${process.pid}
|
|
12912
13639
|
`, { mode: 384 });
|
|
12913
13640
|
try {
|
|
12914
13641
|
const caKeys = forge.pki.rsa.generateKeyPair(2048);
|
|
@@ -12943,9 +13670,9 @@ function createHttpProxyCertificates(appHome = getAppHome()) {
|
|
|
12943
13670
|
]);
|
|
12944
13671
|
server.sign(caKeys.privateKey, forge.md.sha256.create());
|
|
12945
13672
|
const caCert = forge.pki.certificateToPem(ca);
|
|
12946
|
-
const caCertPath =
|
|
12947
|
-
|
|
12948
|
-
|
|
13673
|
+
const caCertPath = join18(sessionDir, "relay-ai-ca.pem");
|
|
13674
|
+
writeFileSync9(caCertPath, caCert, { encoding: "utf8", mode: 384 });
|
|
13675
|
+
chmodSync3(caCertPath, 384);
|
|
12949
13676
|
let cleaned = false;
|
|
12950
13677
|
const cleanupOnExit = () => {
|
|
12951
13678
|
if (cleaned) return;
|
|
@@ -12986,18 +13713,18 @@ function createHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath) {
|
|
|
12986
13713
|
if (resolve(additionalCaCertPath) === resolve(relayCaCertPath)) {
|
|
12987
13714
|
return relayCaCertPath;
|
|
12988
13715
|
}
|
|
12989
|
-
const relayCa =
|
|
12990
|
-
const additionalCa =
|
|
13716
|
+
const relayCa = readFileSync9(relayCaCertPath, "utf8").trimEnd();
|
|
13717
|
+
const additionalCa = readFileSync9(additionalCaCertPath, "utf8").trim();
|
|
12991
13718
|
if (!additionalCa) return relayCaCertPath;
|
|
12992
|
-
const combinedPath =
|
|
12993
|
-
|
|
13719
|
+
const combinedPath = join18(dirname6(relayCaCertPath), "combined-ca.pem");
|
|
13720
|
+
writeFileSync9(
|
|
12994
13721
|
combinedPath,
|
|
12995
13722
|
`${relayCa}
|
|
12996
13723
|
${additionalCa}
|
|
12997
13724
|
`,
|
|
12998
13725
|
{ encoding: "utf8", mode: 384 }
|
|
12999
13726
|
);
|
|
13000
|
-
|
|
13727
|
+
chmodSync3(combinedPath, 384);
|
|
13001
13728
|
return combinedPath;
|
|
13002
13729
|
}
|
|
13003
13730
|
|
|
@@ -13761,6 +14488,10 @@ function parseArgs(args) {
|
|
|
13761
14488
|
parsed2.vertex = true;
|
|
13762
14489
|
continue;
|
|
13763
14490
|
}
|
|
14491
|
+
if (arg === "--yes" || arg === "-y") {
|
|
14492
|
+
parsed2.assumeYes = true;
|
|
14493
|
+
continue;
|
|
14494
|
+
}
|
|
13764
14495
|
if (arg === "--with-native") {
|
|
13765
14496
|
if (parsed2.codexLaunchMode === "relay-only") parsed2.error = "--with-native and --relay-only cannot be used together";
|
|
13766
14497
|
parsed2.codexLaunchMode = "mixed";
|
|
@@ -15008,7 +15739,7 @@ Options:
|
|
|
15008
15739
|
--trace Write debug logs under ~/.relay-ai/logs/`);
|
|
15009
15740
|
return 0;
|
|
15010
15741
|
}
|
|
15011
|
-
const { runUiCommand } = await import("./ui-command-
|
|
15742
|
+
const { runUiCommand } = await import("./ui-command-NYYLGTCD.js");
|
|
15012
15743
|
return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
|
|
15013
15744
|
}
|
|
15014
15745
|
if (parsed.command === "models") {
|
|
@@ -15051,7 +15782,7 @@ Options:
|
|
|
15051
15782
|
console.log(codexAppHelpText());
|
|
15052
15783
|
return 0;
|
|
15053
15784
|
}
|
|
15054
|
-
return runCodexAppCommand(parsed.claudeArgs, { vertex: parsed.vertex, launchProvider: parsed.launchProvider, launchModel: parsed.launchModel, codexLaunchMode: parsed.codexLaunchMode });
|
|
15785
|
+
return runCodexAppCommand(parsed.claudeArgs, { vertex: parsed.vertex, launchProvider: parsed.launchProvider, launchModel: parsed.launchModel, codexLaunchMode: parsed.codexLaunchMode, assumeYes: parsed.assumeYes });
|
|
15055
15786
|
}
|
|
15056
15787
|
if (parsed.command === "claude-app") {
|
|
15057
15788
|
if (parsed.showVersion) {
|
|
@@ -15073,7 +15804,7 @@ Options:
|
|
|
15073
15804
|
console.log(codexHelpText());
|
|
15074
15805
|
return 0;
|
|
15075
15806
|
}
|
|
15076
|
-
return
|
|
15807
|
+
return runCodexCommand2(parsed.claudeArgs, parsed.trace, {
|
|
15077
15808
|
launchProvider: parsed.launchProvider,
|
|
15078
15809
|
launchModel: parsed.launchModel,
|
|
15079
15810
|
vertex: parsed.vertex,
|