codex-grok-bridge 1.0.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +73 -50
- package/package.json +6 -2
- package/scripts/codex-grok.mjs +2 -1
- package/scripts/codex-wrapper.mjs +2 -1
- package/scripts/install-codex-grok-app.ps1 +67 -0
- package/scripts/install-codex-grok-app.sh +95 -15
- package/scripts/launch-desktop.mjs +97 -22
- package/src/bridge.mjs +4 -30
- package/src/cli-inference.mjs +4 -2
- package/src/models.mjs +121 -0
- package/src/paths.mjs +163 -0
- package/src/proxy.mjs +2 -2
- package/src/router.mjs +10 -28
- package/src/runtime.mjs +3 -2
- package/src/tools.mjs +145 -22
package/src/tools.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
isImageGenerationItem,
|
|
5
5
|
saveGeneratedImage,
|
|
6
6
|
} from "./imagegen.mjs";
|
|
7
|
+
import { resolveGrokModel } from "./models.mjs";
|
|
7
8
|
|
|
8
9
|
const OBJECT_SCHEMA = { type: "object", properties: {} };
|
|
9
10
|
|
|
@@ -56,16 +57,145 @@ const GROK_INPUT_ITEM_TYPES = new Set([
|
|
|
56
57
|
"function_call_output",
|
|
57
58
|
"shell_call",
|
|
58
59
|
]);
|
|
60
|
+
// Codex item ids, status, and new client metadata have 422'd the upstream.
|
|
61
|
+
// Keep only the fields Grok's Responses input is known to accept.
|
|
62
|
+
const INPUT_ITEM_FIELDS = {
|
|
63
|
+
message: ["type", "role", "content"],
|
|
64
|
+
reasoning: ["type", "summary"],
|
|
65
|
+
function_call: ["type", "name", "call_id", "arguments"],
|
|
66
|
+
function_call_output: ["type", "name", "call_id", "output"],
|
|
67
|
+
shell_call: ["type", "name", "call_id", "action"],
|
|
68
|
+
};
|
|
69
|
+
const CONTENT_PART_FIELDS = {
|
|
70
|
+
input_text: ["type", "text"],
|
|
71
|
+
output_text: ["type", "text"],
|
|
72
|
+
summary_text: ["type", "text"],
|
|
73
|
+
input_image: ["type", "image_url", "detail"],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function pickKeys(node, keys) {
|
|
77
|
+
const next = {};
|
|
78
|
+
for (const key of keys) {
|
|
79
|
+
if (node[key] !== undefined) next[key] = node[key];
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function whitelistContentPart(part) {
|
|
85
|
+
if (!part || typeof part !== "object" || Array.isArray(part)) return part;
|
|
86
|
+
const keys = CONTENT_PART_FIELDS[part.type];
|
|
87
|
+
if (!keys) return part;
|
|
88
|
+
const next = pickKeys(part, keys);
|
|
89
|
+
if (next.image_url && typeof next.image_url === "object")
|
|
90
|
+
next.image_url = pickKeys(next.image_url, ["url", "detail"]);
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function whitelistInputNode(node) {
|
|
95
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return node;
|
|
96
|
+
const itemKeys = INPUT_ITEM_FIELDS[node.type];
|
|
97
|
+
if (itemKeys) {
|
|
98
|
+
const next = pickKeys(node, itemKeys);
|
|
99
|
+
if (Array.isArray(next.content))
|
|
100
|
+
next.content = next.content.map(whitelistContentPart);
|
|
101
|
+
return next;
|
|
102
|
+
}
|
|
103
|
+
if (node.type == null && node.role != null && node.content !== undefined) {
|
|
104
|
+
const next = pickKeys(node, ["role", "content"]);
|
|
105
|
+
if (Array.isArray(next.content))
|
|
106
|
+
next.content = next.content.map(whitelistContentPart);
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
return whitelistContentPart(node);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isObjectFragment(schema) {
|
|
113
|
+
return (
|
|
114
|
+
schema &&
|
|
115
|
+
typeof schema === "object" &&
|
|
116
|
+
!Array.isArray(schema) &&
|
|
117
|
+
(schema.type === "object" || schema.type == null)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function mergeObjectFragments(fragments) {
|
|
122
|
+
const properties = {};
|
|
123
|
+
let required;
|
|
124
|
+
for (const fragment of fragments) {
|
|
125
|
+
if (fragment.properties && typeof fragment.properties === "object")
|
|
126
|
+
Object.assign(properties, fragment.properties);
|
|
127
|
+
if (Array.isArray(fragment.required)) {
|
|
128
|
+
required =
|
|
129
|
+
required === undefined
|
|
130
|
+
? fragment.required.slice()
|
|
131
|
+
: required.filter((key) => fragment.required.includes(key));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { properties, required };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function emitObjectParameters(source, overrides = {}) {
|
|
138
|
+
const properties =
|
|
139
|
+
overrides.properties ??
|
|
140
|
+
(source.properties && typeof source.properties === "object"
|
|
141
|
+
? source.properties
|
|
142
|
+
: {});
|
|
143
|
+
const required =
|
|
144
|
+
overrides.required !== undefined
|
|
145
|
+
? overrides.required
|
|
146
|
+
: Array.isArray(source.required)
|
|
147
|
+
? source.required
|
|
148
|
+
: undefined;
|
|
149
|
+
const next = { type: "object", properties: { ...properties } };
|
|
150
|
+
if (required?.length) next.required = required.slice();
|
|
151
|
+
if (source.additionalProperties !== undefined)
|
|
152
|
+
next.additionalProperties = source.additionalProperties;
|
|
153
|
+
if (source.description !== undefined) next.description = source.description;
|
|
154
|
+
return next;
|
|
155
|
+
}
|
|
59
156
|
|
|
60
157
|
function usableParameters(parameters) {
|
|
61
|
-
if (!parameters || typeof parameters !== "object"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Array.isArray(parameters.anyOf)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
158
|
+
if (!parameters || typeof parameters !== "object" || Array.isArray(parameters))
|
|
159
|
+
return OBJECT_SCHEMA;
|
|
160
|
+
const variants = Array.isArray(parameters.oneOf)
|
|
161
|
+
? parameters.oneOf
|
|
162
|
+
: Array.isArray(parameters.anyOf)
|
|
163
|
+
? parameters.anyOf
|
|
164
|
+
: null;
|
|
165
|
+
if (!variants) {
|
|
166
|
+
return parameters.type === "object"
|
|
167
|
+
? emitObjectParameters(parameters)
|
|
168
|
+
: OBJECT_SCHEMA;
|
|
169
|
+
}
|
|
170
|
+
const fragments = variants.filter(isObjectFragment);
|
|
171
|
+
if (fragments.length === 0) {
|
|
172
|
+
return parameters.type === "object"
|
|
173
|
+
? emitObjectParameters(parameters)
|
|
174
|
+
: OBJECT_SCHEMA;
|
|
175
|
+
}
|
|
176
|
+
const merged = mergeObjectFragments(fragments);
|
|
177
|
+
const properties = {
|
|
178
|
+
...(parameters.properties && typeof parameters.properties === "object"
|
|
179
|
+
? parameters.properties
|
|
180
|
+
: {}),
|
|
181
|
+
...merged.properties,
|
|
182
|
+
};
|
|
183
|
+
let required = merged.required;
|
|
184
|
+
if (Array.isArray(parameters.required)) {
|
|
185
|
+
required =
|
|
186
|
+
required === undefined
|
|
187
|
+
? parameters.required.slice()
|
|
188
|
+
: required.filter((key) => parameters.required.includes(key));
|
|
189
|
+
}
|
|
190
|
+
const first = fragments[0];
|
|
191
|
+
return emitObjectParameters(
|
|
192
|
+
{
|
|
193
|
+
additionalProperties:
|
|
194
|
+
parameters.additionalProperties ?? first.additionalProperties,
|
|
195
|
+
description: parameters.description ?? first.description,
|
|
196
|
+
},
|
|
197
|
+
{ properties, required },
|
|
198
|
+
);
|
|
69
199
|
}
|
|
70
200
|
|
|
71
201
|
function sanitize(name) {
|
|
@@ -189,7 +319,7 @@ function toProxyInputNode(node, map, state) {
|
|
|
189
319
|
if (
|
|
190
320
|
key === "encrypted_content" ||
|
|
191
321
|
key === "encrypted_function_args" ||
|
|
192
|
-
key
|
|
322
|
+
key.startsWith("internal_")
|
|
193
323
|
)
|
|
194
324
|
continue;
|
|
195
325
|
const converted = toProxyInputNode(value, map, state);
|
|
@@ -204,7 +334,7 @@ function toProxyInputNode(node, map, state) {
|
|
|
204
334
|
(part) => part && typeof part.text === "string" && part.text.trim(),
|
|
205
335
|
);
|
|
206
336
|
if (!summary.length) return DROP;
|
|
207
|
-
return { type: "reasoning", summary };
|
|
337
|
+
return whitelistInputNode({ type: "reasoning", summary });
|
|
208
338
|
}
|
|
209
339
|
|
|
210
340
|
if (next.type === "agent_message") {
|
|
@@ -214,7 +344,7 @@ function toProxyInputNode(node, map, state) {
|
|
|
214
344
|
? [{ type: "input_text", text: next.text }]
|
|
215
345
|
: [];
|
|
216
346
|
if (!content.length) return DROP;
|
|
217
|
-
return { type: "message", role: "assistant", content };
|
|
347
|
+
return whitelistInputNode({ type: "message", role: "assistant", content });
|
|
218
348
|
}
|
|
219
349
|
|
|
220
350
|
if (
|
|
@@ -227,10 +357,7 @@ function toProxyInputNode(node, map, state) {
|
|
|
227
357
|
delete next.namespace;
|
|
228
358
|
if (typeof next.call_id === "string") state.callIds.set(next.call_id, proxyName);
|
|
229
359
|
}
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (
|
|
360
|
+
} else if (
|
|
234
361
|
next.type === "custom_tool_call" &&
|
|
235
362
|
typeof next.name === "string"
|
|
236
363
|
) {
|
|
@@ -244,10 +371,7 @@ function toProxyInputNode(node, map, state) {
|
|
|
244
371
|
delete next.input;
|
|
245
372
|
if (typeof next.call_id === "string") state.callIds.set(next.call_id, proxyName);
|
|
246
373
|
}
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (
|
|
374
|
+
} else if (
|
|
251
375
|
next.type === "function_call_output" ||
|
|
252
376
|
next.type === "custom_tool_call_output"
|
|
253
377
|
) {
|
|
@@ -262,10 +386,9 @@ function toProxyInputNode(node, map, state) {
|
|
|
262
386
|
next.name = proxyName;
|
|
263
387
|
delete next.namespace;
|
|
264
388
|
}
|
|
265
|
-
return next;
|
|
266
389
|
}
|
|
267
390
|
|
|
268
|
-
return next;
|
|
391
|
+
return whitelistInputNode(next);
|
|
269
392
|
}
|
|
270
393
|
|
|
271
394
|
function toProxyInput(input, map) {
|
|
@@ -304,7 +427,7 @@ export function toProxyRequest(body) {
|
|
|
304
427
|
const { tools, map } = flattenCodexTools(body.tools ?? []);
|
|
305
428
|
const effort = EFFORT[body.reasoning?.effort] ?? "high";
|
|
306
429
|
const request = {
|
|
307
|
-
model:
|
|
430
|
+
model: resolveGrokModel(body.model),
|
|
308
431
|
input: toProxyInput(body.input, map),
|
|
309
432
|
tools,
|
|
310
433
|
reasoning: { effort },
|