pi-openai-codex-compat 0.0.1 → 0.0.3
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 +60 -0
- package/README.md +63 -20
- package/extensions/openai-codex-compat/apply-patch.ts +4 -5
- package/extensions/openai-codex-compat/codex-cache-key.ts +9 -0
- package/extensions/openai-codex-compat/codex-protocol.ts +3 -2
- package/extensions/openai-codex-compat/codex-provider.ts +221 -107
- package/extensions/openai-codex-compat/codex-stream.ts +85 -14
- package/extensions/openai-codex-compat/codex-transport.ts +714 -161
- package/extensions/openai-codex-compat/config.ts +164 -5
- package/extensions/openai-codex-compat/image-generation-schema.ts +37 -0
- package/extensions/openai-codex-compat/image-generation.ts +25 -48
- package/extensions/openai-codex-compat/index.ts +8 -1
- package/extensions/openai-codex-compat/provider-error.ts +79 -0
- package/extensions/openai-codex-compat/responses-replay.ts +0 -7
- package/extensions/openai-codex-compat/settings-pane.ts +107 -28
- package/extensions/openai-codex-compat/web-run.ts +7 -0
- package/package.json +2 -1
|
@@ -10,11 +10,15 @@ import {
|
|
|
10
10
|
type SettingItem,
|
|
11
11
|
SettingsList,
|
|
12
12
|
Text,
|
|
13
|
+
truncateToWidth,
|
|
13
14
|
} from "@earendil-works/pi-tui";
|
|
14
15
|
import {
|
|
16
|
+
CONFIG_ENVIRONMENT_VARIABLES,
|
|
15
17
|
configLayer,
|
|
16
18
|
loadConfig,
|
|
19
|
+
parseEnvironmentConfig,
|
|
17
20
|
saveConfig,
|
|
21
|
+
withoutEnvironmentOverrides,
|
|
18
22
|
writableConfigPath,
|
|
19
23
|
type CodexCompatConfig,
|
|
20
24
|
type ConfigLayer,
|
|
@@ -48,8 +52,11 @@ function thresholdValue(value: number | undefined): string {
|
|
|
48
52
|
return value === undefined ? "Pi default" : `${value}%`;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
export function settingItems(
|
|
52
|
-
|
|
55
|
+
export function settingItems(
|
|
56
|
+
config: CodexCompatConfig,
|
|
57
|
+
environmentConfig: ConfigLayer = {},
|
|
58
|
+
): SettingItem[] {
|
|
59
|
+
const items: SettingItem[] = [
|
|
53
60
|
{
|
|
54
61
|
id: "fastMode",
|
|
55
62
|
label: "Fast mode",
|
|
@@ -130,6 +137,18 @@ export function settingItems(config: CodexCompatConfig): SettingItem[] {
|
|
|
130
137
|
values: ["Pi default", "75%", "80%", "85%", "90%", "95%"],
|
|
131
138
|
},
|
|
132
139
|
];
|
|
140
|
+
|
|
141
|
+
return items.map((item) => {
|
|
142
|
+
const id = item.id as SettingId;
|
|
143
|
+
if (!Object.hasOwn(environmentConfig, id)) return item;
|
|
144
|
+
|
|
145
|
+
const variable = CONFIG_ENVIRONMENT_VARIABLES[id];
|
|
146
|
+
const lockedItem = { ...item };
|
|
147
|
+
lockedItem.currentValue = `${item.currentValue} (env)`;
|
|
148
|
+
lockedItem.description = `${item.description} Locked by ${variable}.`;
|
|
149
|
+
delete lockedItem.values;
|
|
150
|
+
return lockedItem;
|
|
151
|
+
});
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
export function settingPatch(id: string, value: string): ConfigLayer | undefined {
|
|
@@ -212,18 +231,21 @@ async function showSettings(
|
|
|
212
231
|
}
|
|
213
232
|
|
|
214
233
|
let config = callbacks.getConfig?.(ctx) ?? loadConfig(ctx.cwd, ctx.isProjectTrusted());
|
|
234
|
+
let persistedConfig = { ...config };
|
|
215
235
|
let revision = 0;
|
|
216
236
|
let savedRevision = 0;
|
|
217
237
|
let saveQueue = Promise.resolve();
|
|
218
238
|
const filePath = writableConfigPath(ctx.cwd, ctx.isProjectTrusted());
|
|
239
|
+
const environmentConfig = parseEnvironmentConfig();
|
|
219
240
|
|
|
220
|
-
await ctx.ui.custom((tui, theme,
|
|
241
|
+
await ctx.ui.custom((tui, theme, keybindings, done) => {
|
|
242
|
+
let closing = false;
|
|
221
243
|
const container = new Container();
|
|
222
244
|
container.addChild(
|
|
223
245
|
new Text(theme.fg("accent", theme.bold("OpenAI Codex Compatibility Settings")), 1, 1),
|
|
224
246
|
);
|
|
225
247
|
container.addChild(
|
|
226
|
-
new Text(theme.fg("dim", `
|
|
248
|
+
new Text(theme.fg("dim", `Ctrl+S saves without closing to ${filePath}`), 1, 0),
|
|
227
249
|
);
|
|
228
250
|
const saveStatus = new Text(
|
|
229
251
|
theme.fg("dim", "Changes apply to this session immediately."),
|
|
@@ -232,10 +254,61 @@ async function showSettings(
|
|
|
232
254
|
);
|
|
233
255
|
container.addChild(saveStatus);
|
|
234
256
|
|
|
257
|
+
const queueSave = (closeAfterSave: boolean): void => {
|
|
258
|
+
if (closing) return;
|
|
259
|
+
if (closeAfterSave) closing = true;
|
|
260
|
+
|
|
261
|
+
const snapshotConfig = { ...config };
|
|
262
|
+
const snapshot = withoutEnvironmentOverrides(configLayer(snapshotConfig), environmentConfig);
|
|
263
|
+
const snapshotRevision = revision;
|
|
264
|
+
saveStatus.setText(theme.fg("dim", closeAfterSave ? "Saving and closing…" : "Saving…"));
|
|
265
|
+
saveQueue = saveQueue.then(async () => {
|
|
266
|
+
try {
|
|
267
|
+
const savedPath = await saveConfig(ctx.cwd, ctx.isProjectTrusted(), snapshot);
|
|
268
|
+
if (snapshotRevision >= savedRevision) {
|
|
269
|
+
persistedConfig = snapshotConfig;
|
|
270
|
+
savedRevision = snapshotRevision;
|
|
271
|
+
}
|
|
272
|
+
if (closeAfterSave) {
|
|
273
|
+
done(undefined);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
saveStatus.setText(
|
|
277
|
+
savedRevision === revision
|
|
278
|
+
? theme.fg("success", `Saved to ${savedPath}`)
|
|
279
|
+
: theme.fg("warning", "Unsaved session changes."),
|
|
280
|
+
);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (closeAfterSave) closing = false;
|
|
283
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
284
|
+
saveStatus.setText(theme.fg("error", message));
|
|
285
|
+
ctx.ui.notify(message, "error");
|
|
286
|
+
}
|
|
287
|
+
tui.requestRender();
|
|
288
|
+
});
|
|
289
|
+
tui.requestRender();
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const discardAndClose = (): void => {
|
|
293
|
+
if (closing) return;
|
|
294
|
+
closing = true;
|
|
295
|
+
saveStatus.setText(theme.fg("dim", "Discarding unsaved changes…"));
|
|
296
|
+
saveQueue = saveQueue.then(() => {
|
|
297
|
+
if (revision !== savedRevision) {
|
|
298
|
+
config = { ...persistedConfig };
|
|
299
|
+
revision = savedRevision;
|
|
300
|
+
callbacks.onChange?.(config, ctx);
|
|
301
|
+
}
|
|
302
|
+
done(undefined);
|
|
303
|
+
});
|
|
304
|
+
tui.requestRender();
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const listTheme = getSettingsListTheme();
|
|
235
308
|
const list = new SettingsList(
|
|
236
|
-
settingItems(config),
|
|
309
|
+
settingItems(config, environmentConfig),
|
|
237
310
|
12,
|
|
238
|
-
|
|
311
|
+
listTheme,
|
|
239
312
|
(id, value) => {
|
|
240
313
|
const patch = settingPatch(id, value);
|
|
241
314
|
if (!patch) return;
|
|
@@ -246,36 +319,42 @@ async function showSettings(
|
|
|
246
319
|
saveStatus.setText(theme.fg("warning", "Unsaved session changes."));
|
|
247
320
|
tui.requestRender();
|
|
248
321
|
},
|
|
249
|
-
|
|
322
|
+
discardAndClose,
|
|
250
323
|
{ enableSearch: true },
|
|
251
324
|
);
|
|
252
|
-
container.addChild(
|
|
325
|
+
container.addChild({
|
|
326
|
+
render: (width: number) => {
|
|
327
|
+
const lines = list.render(width);
|
|
328
|
+
if (lines.length > 0) {
|
|
329
|
+
lines[lines.length - 1] = truncateToWidth(
|
|
330
|
+
listTheme.hint(
|
|
331
|
+
" Type to search · Space changes · Enter save & close · Esc discard & close",
|
|
332
|
+
),
|
|
333
|
+
width,
|
|
334
|
+
"",
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return lines;
|
|
338
|
+
},
|
|
339
|
+
invalidate: () => list.invalidate(),
|
|
340
|
+
handleInput: (data: string) => list.handleInput(data),
|
|
341
|
+
});
|
|
253
342
|
|
|
254
343
|
return {
|
|
255
344
|
render: (width: number) => container.render(width),
|
|
256
345
|
invalidate: () => container.invalidate(),
|
|
257
346
|
handleInput: (data: string) => {
|
|
347
|
+
if (closing) return;
|
|
258
348
|
if (matchesKey(data, Key.ctrl("s"))) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
? theme.fg("success", `Saved to ${savedPath}`)
|
|
269
|
-
: theme.fg("warning", "Unsaved session changes."),
|
|
270
|
-
);
|
|
271
|
-
} catch (error) {
|
|
272
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
273
|
-
saveStatus.setText(theme.fg("error", message));
|
|
274
|
-
ctx.ui.notify(message, "error");
|
|
275
|
-
}
|
|
276
|
-
tui.requestRender();
|
|
277
|
-
});
|
|
278
|
-
tui.requestRender();
|
|
349
|
+
queueSave(false);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (matchesKey(data, Key.enter)) {
|
|
353
|
+
queueSave(true);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (matchesKey(data, Key.escape) || keybindings.matches(data, "tui.select.cancel")) {
|
|
357
|
+
discardAndClose();
|
|
279
358
|
return;
|
|
280
359
|
}
|
|
281
360
|
list.handleInput(data);
|
|
@@ -116,6 +116,13 @@ export default function registerWebRun(
|
|
|
116
116
|
name: WEB_RUN_TOOL_NAME,
|
|
117
117
|
label: WEB_RUN_TOOL_NAME,
|
|
118
118
|
description: WEB_RUN_DESCRIPTION,
|
|
119
|
+
promptSnippet: "Search and browse the internet",
|
|
120
|
+
promptGuidelines: [
|
|
121
|
+
"Use `web.run` when the user explicitly asks to browse or when answering requires current, niche, high-stakes, or precisely sourced information, including recommendations that may change over time.",
|
|
122
|
+
'Batch independent `web.run` operations in one call, pass only required parameters, and keep `search_query` to at most four queries; use `response_length: "medium"` or `"long"` when sending four.',
|
|
123
|
+
"For technical research, prefer primary sources; for OpenAI product questions, inspect local code first and restrict fallback browsing to official OpenAI sites.",
|
|
124
|
+
"Cite supported claims with direct Markdown links near the relevant text, never expose internal reference IDs, and respect the description's quotation and source word limits.",
|
|
125
|
+
],
|
|
119
126
|
parameters: WEB_RUN_PARAMETERS,
|
|
120
127
|
executionMode: "parallel",
|
|
121
128
|
renderShell: "self",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-openai-codex-compat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "OpenAI Codex compatibility for Pi with native compaction, fast mode, and Codex-optimized capabilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"scripts": {
|
|
32
32
|
"check": "hk check --all --check",
|
|
33
33
|
"test": "node --test --test-concurrency=1 test/*.test.ts",
|
|
34
|
+
"test:live:codex": "PI_CODEX_LIVE_TEST=1 node --test --test-concurrency=1 test/codex-transport.live.test.ts",
|
|
34
35
|
"fmt": "oxfmt",
|
|
35
36
|
"lint": "oxlint",
|
|
36
37
|
"lint:fix": "oxlint --fix"
|