pi-openai-codex-compat 0.0.10-alpha.2 → 0.0.10-alpha.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
CHANGED
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
### Fixed
|
|
15
15
|
|
|
16
|
+
- Give the `/codex-settings` search field explicit focus that follows typed
|
|
17
|
+
input into search and Up/Down back to the result list, allowing Space to
|
|
18
|
+
change a filtered setting and applying accent color to the result cursor and
|
|
19
|
+
selected item only while that list has focus, with concise focus and action
|
|
20
|
+
labels.
|
|
16
21
|
- Preserve regular-file permission modes across sequential `apply_patch`
|
|
17
22
|
replacements and state-changing moves, including files created by earlier
|
|
18
23
|
instructions in the same patch.
|
|
@@ -41,6 +46,8 @@
|
|
|
41
46
|
|
|
42
47
|
### Changed
|
|
43
48
|
|
|
49
|
+
- Replace the multi-line startup configuration dump with a concise package and
|
|
50
|
+
settings-command notice, and simplify the fallback settings-save error.
|
|
44
51
|
- Align deferred tool loading with Pi 0.84.3, preferring message-anchored
|
|
45
52
|
additional tools on capable Codex models while retaining tool-search and
|
|
46
53
|
top-level fallbacks.
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import type { ExtensionAPI
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_CONFIG,
|
|
4
|
-
loadConfig,
|
|
5
|
-
writableConfigPath,
|
|
6
|
-
type CodexCompatConfig,
|
|
7
|
-
} from "./config.ts";
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { DEFAULT_CONFIG, loadConfig, type CodexCompatConfig } from "./config.ts";
|
|
8
3
|
import type { ConfigContext } from "./config-context.ts";
|
|
9
4
|
import { registerCodexProvider } from "./codex-provider.ts";
|
|
10
5
|
import { installCodexFooter } from "./footer.ts";
|
|
@@ -25,30 +20,6 @@ export {
|
|
|
25
20
|
type OpenAICodexWebSocketDebugStats,
|
|
26
21
|
} from "./codex-transport.ts";
|
|
27
22
|
|
|
28
|
-
const DISPLAY_NAME = "OpenAI Codex Compat";
|
|
29
|
-
|
|
30
|
-
function settingsSummary(ctx: ExtensionContext, config: CodexCompatConfig): string {
|
|
31
|
-
return [
|
|
32
|
-
DISPLAY_NAME,
|
|
33
|
-
`fast mode: ${config.fastMode ? "on" : "off"}`,
|
|
34
|
-
`Responses Lite: ${config.responsesLite ? "on" : "off"}`,
|
|
35
|
-
`reasoning mode: ${config.reasoningMode}`,
|
|
36
|
-
`Codex tool background: ${config.toolBackground}`,
|
|
37
|
-
`apply_patch: ${config.applyPatch ? "on" : "off"}`,
|
|
38
|
-
`apply_patch debug output: ${config.applyPatchDebug ? "on" : "off"}`,
|
|
39
|
-
`apply_patch diagnostics capture: ${config.applyPatchDiagnostics ? "on" : "off"}`,
|
|
40
|
-
`image_gen.imagegen: ${config.imageGeneration ? "on" : "off"}`,
|
|
41
|
-
`image result detail: ${config.imageDetail}`,
|
|
42
|
-
`web.run: ${config.webRun ? "on" : "off"}`,
|
|
43
|
-
`web search: ${config.webSearch}`,
|
|
44
|
-
`text verbosity: ${config.textVerbosity}`,
|
|
45
|
-
`reasoning summary: ${config.reasoningSummary}`,
|
|
46
|
-
`auto-compact threshold: ${config.autoCompactAtPercent ?? "Pi default"}`,
|
|
47
|
-
`save target: ${writableConfigPath(ctx.cwd, ctx.isProjectTrusted())}`,
|
|
48
|
-
"settings: /codex-settings (Enter saves & closes; Esc discards; Ctrl+S saves)",
|
|
49
|
-
].join("\n");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
23
|
export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
53
24
|
let activeConfig: CodexCompatConfig | undefined;
|
|
54
25
|
const resolveConfig = (ctx: ConfigContext): CodexCompatConfig => {
|
|
@@ -65,7 +36,7 @@ export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
|
65
36
|
activeConfig = loadConfig(ctx.cwd, ctx.isProjectTrusted());
|
|
66
37
|
installCodexFooter(ctx, resolveConfig);
|
|
67
38
|
if (event.reason !== "reload" && ctx.mode === "tui") {
|
|
68
|
-
ctx.ui.notify(
|
|
39
|
+
ctx.ui.notify("pi-openai-codex-compat loaded · /codex-settings", "info");
|
|
69
40
|
}
|
|
70
41
|
});
|
|
71
42
|
|
|
@@ -9,6 +9,10 @@ import type { Api, Model } from "@earendil-works/pi-ai";
|
|
|
9
9
|
import {
|
|
10
10
|
type Component,
|
|
11
11
|
Container,
|
|
12
|
+
decodeKittyPrintable,
|
|
13
|
+
type Focusable,
|
|
14
|
+
fuzzyFilter,
|
|
15
|
+
Input,
|
|
12
16
|
Key,
|
|
13
17
|
matchesKey,
|
|
14
18
|
type SettingItem,
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
TEXT_VERBOSITY_SCHEMA,
|
|
28
32
|
WEB_SEARCH_MODE_SCHEMA,
|
|
29
33
|
configLayer,
|
|
34
|
+
globalConfigPath,
|
|
30
35
|
loadConfig,
|
|
31
36
|
parseEnvironmentConfig,
|
|
32
37
|
saveConfig,
|
|
@@ -100,6 +105,16 @@ function thresholdValue(value: number | undefined): string {
|
|
|
100
105
|
return value === undefined ? "Pi default" : `${value}%`;
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
function isSearchTextInput(data: string): boolean {
|
|
109
|
+
if (decodeKittyPrintable(data) !== undefined || data.includes("\u001b[200~")) return true;
|
|
110
|
+
if (data.length === 0) return false;
|
|
111
|
+
for (let index = 0; index < data.length; index++) {
|
|
112
|
+
const code = data.charCodeAt(index);
|
|
113
|
+
if (code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f)) return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
export function settingItems(
|
|
104
119
|
config: CodexCompatConfig,
|
|
105
120
|
environmentConfig: ConfigLayer = {},
|
|
@@ -322,8 +337,11 @@ async function showSettings(
|
|
|
322
337
|
let revision = 0;
|
|
323
338
|
let savedRevision = 0;
|
|
324
339
|
let saveQueue = Promise.resolve();
|
|
325
|
-
const filePath = writableConfigPath(ctx.cwd, ctx.isProjectTrusted());
|
|
326
340
|
const environmentConfig = parseEnvironmentConfig();
|
|
341
|
+
const settingsScope =
|
|
342
|
+
writableConfigPath(ctx.cwd, ctx.isProjectTrusted()) === globalConfigPath()
|
|
343
|
+
? "global"
|
|
344
|
+
: "project";
|
|
327
345
|
const changeContext = (): SettingsChangeContext => {
|
|
328
346
|
return {
|
|
329
347
|
model: selectedRegistryModel(ctx),
|
|
@@ -335,13 +353,13 @@ async function showSettings(
|
|
|
335
353
|
let closing = false;
|
|
336
354
|
const container = new Container();
|
|
337
355
|
container.addChild(
|
|
338
|
-
new Text(theme.fg("accent", theme.bold("
|
|
339
|
-
);
|
|
340
|
-
container.addChild(
|
|
341
|
-
new Text(theme.fg("dim", `Ctrl+S saves without closing to ${filePath}`), 1, 0),
|
|
356
|
+
new Text(theme.fg("accent", theme.bold("Codex Settings (pi-openai-codex-compat)")), 1, 1),
|
|
342
357
|
);
|
|
343
358
|
const saveStatus = new Text(
|
|
344
|
-
theme.fg(
|
|
359
|
+
theme.fg(
|
|
360
|
+
"dim",
|
|
361
|
+
`Changes apply to the session immediately · Ctrl+S updates ${settingsScope} settings`,
|
|
362
|
+
),
|
|
345
363
|
1,
|
|
346
364
|
0,
|
|
347
365
|
);
|
|
@@ -372,10 +390,7 @@ async function showSettings(
|
|
|
372
390
|
: theme.fg("warning", "Unsaved session changes."),
|
|
373
391
|
);
|
|
374
392
|
} catch (cause) {
|
|
375
|
-
const error = errorFromThrown(
|
|
376
|
-
cause,
|
|
377
|
-
"Saving Codex settings failed with a non-Error value.",
|
|
378
|
-
);
|
|
393
|
+
const error = errorFromThrown(cause, "Could not save Codex settings.");
|
|
379
394
|
if (closeAfterSave) closing = false;
|
|
380
395
|
const message = error.message;
|
|
381
396
|
saveStatus.setText(theme.fg("error", message));
|
|
@@ -402,42 +417,112 @@ async function showSettings(
|
|
|
402
417
|
};
|
|
403
418
|
|
|
404
419
|
const listTheme = getSettingsListTheme();
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
420
|
+
const items = settingItems(config, environmentConfig);
|
|
421
|
+
const searchInput = new Input();
|
|
422
|
+
let rootFocused = false;
|
|
423
|
+
let searchFocused = false;
|
|
424
|
+
let filteredItems = items;
|
|
425
|
+
const resultListTheme = {
|
|
426
|
+
...listTheme,
|
|
427
|
+
label: (text: string, selected: boolean) =>
|
|
428
|
+
searchFocused && selected ? theme.fg("text", text) : listTheme.label(text, selected),
|
|
429
|
+
value: (text: string, selected: boolean) =>
|
|
430
|
+
searchFocused && selected ? theme.fg("text", text) : listTheme.value(text, selected),
|
|
431
|
+
get cursor(): string {
|
|
432
|
+
return searchFocused ? "→ " : listTheme.cursor;
|
|
418
433
|
},
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
434
|
+
};
|
|
435
|
+
const changeSetting = (id: string, value: string): void => {
|
|
436
|
+
const patch = settingPatch(id, value);
|
|
437
|
+
if (!patch) return;
|
|
438
|
+
|
|
439
|
+
config = applySettingPatch(config, patch);
|
|
440
|
+
revision++;
|
|
441
|
+
callbacks.onChange?.(config, changeContext());
|
|
442
|
+
saveStatus.setText(theme.fg("warning", "Unsaved session changes."));
|
|
443
|
+
tui.requestRender();
|
|
444
|
+
};
|
|
445
|
+
const createList = (): SettingsList =>
|
|
446
|
+
new SettingsList(filteredItems, 12, resultListTheme, changeSetting, discardAndClose);
|
|
447
|
+
let list = createList();
|
|
448
|
+
|
|
449
|
+
const setSearchFocused = (focused: boolean): void => {
|
|
450
|
+
searchFocused = focused;
|
|
451
|
+
searchInput.focused = rootFocused && searchFocused;
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const updateSearch = (data: string): void => {
|
|
455
|
+
const previousQuery = searchInput.getValue();
|
|
456
|
+
searchInput.handleInput(data);
|
|
457
|
+
const query = searchInput.getValue();
|
|
458
|
+
if (query === previousQuery) return;
|
|
459
|
+
filteredItems = fuzzyFilter(items, query, (item) => item.label);
|
|
460
|
+
list = createList();
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const settingsSelector: Component = {
|
|
423
464
|
render: (width: number) => {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
465
|
+
const query = searchInput.getValue();
|
|
466
|
+
const searchLabel = searchFocused
|
|
467
|
+
? theme.fg("accent", theme.bold("Search"))
|
|
468
|
+
: theme.fg("dim", "Search");
|
|
469
|
+
const searchLines = searchFocused
|
|
470
|
+
? searchInput.render(width)
|
|
471
|
+
: [truncateToWidth(listTheme.hint(query.length > 0 ? ` ${query}` : ""), width, "")];
|
|
472
|
+
const listLines =
|
|
473
|
+
filteredItems.length > 0
|
|
474
|
+
? list.render(width)
|
|
475
|
+
: [truncateToWidth(listTheme.hint(" No matching settings"), width, ""), ""];
|
|
476
|
+
const hint = searchFocused
|
|
477
|
+
? " Type to filter · ↑↓ results · Tab switch · Enter save · Esc discard"
|
|
478
|
+
: " ↑↓ navigate · Type to filter · Space change · Tab search · Enter save · Esc discard";
|
|
479
|
+
if (listLines.length > 0) {
|
|
480
|
+
listLines[listLines.length - 1] = truncateToWidth(listTheme.hint(hint), width, "");
|
|
433
481
|
}
|
|
434
|
-
return
|
|
482
|
+
return [truncateToWidth(searchLabel, width, ""), ...searchLines, "", ...listLines];
|
|
483
|
+
},
|
|
484
|
+
invalidate: () => {
|
|
485
|
+
searchInput.invalidate();
|
|
486
|
+
list.invalidate();
|
|
435
487
|
},
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
488
|
+
handleInput: (data: string) => {
|
|
489
|
+
if (matchesKey(data, Key.tab) || matchesKey(data, Key.shift("tab"))) {
|
|
490
|
+
setSearchFocused(!searchFocused);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (
|
|
494
|
+
searchFocused &&
|
|
495
|
+
(matchesKey(data, Key.up) ||
|
|
496
|
+
matchesKey(data, Key.down) ||
|
|
497
|
+
keybindings.matches(data, "tui.select.up") ||
|
|
498
|
+
keybindings.matches(data, "tui.select.down"))
|
|
499
|
+
) {
|
|
500
|
+
setSearchFocused(false);
|
|
501
|
+
list.handleInput(data);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (!searchFocused) {
|
|
505
|
+
if (!matchesKey(data, Key.space) && isSearchTextInput(data)) {
|
|
506
|
+
setSearchFocused(true);
|
|
507
|
+
updateSearch(data);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
list.handleInput(data);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
updateSearch(data);
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
container.addChild(settingsSelector);
|
|
439
517
|
|
|
440
|
-
|
|
518
|
+
const component: Component & Focusable = {
|
|
519
|
+
get focused() {
|
|
520
|
+
return rootFocused;
|
|
521
|
+
},
|
|
522
|
+
set focused(focused: boolean) {
|
|
523
|
+
rootFocused = focused;
|
|
524
|
+
searchInput.focused = rootFocused && searchFocused;
|
|
525
|
+
},
|
|
441
526
|
render: (width: number) => container.render(width),
|
|
442
527
|
invalidate: () => container.invalidate(),
|
|
443
528
|
handleInput: (data: string) => {
|
|
@@ -454,10 +539,11 @@ async function showSettings(
|
|
|
454
539
|
discardAndClose();
|
|
455
540
|
return;
|
|
456
541
|
}
|
|
457
|
-
|
|
542
|
+
settingsSelector.handleInput?.(data);
|
|
458
543
|
tui.requestRender();
|
|
459
544
|
},
|
|
460
545
|
};
|
|
546
|
+
return component;
|
|
461
547
|
});
|
|
462
548
|
|
|
463
549
|
await saveQueue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-openai-codex-compat",
|
|
3
|
-
"version": "0.0.10-alpha.
|
|
3
|
+
"version": "0.0.10-alpha.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"
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@2h2d/oxlint-config": "0.1.0",
|
|
44
|
+
"@2h2d/ts-config": "0.0.1",
|
|
44
45
|
"@earendil-works/pi-ai": "0.84.3",
|
|
45
46
|
"@earendil-works/pi-coding-agent": "0.84.3",
|
|
46
47
|
"@earendil-works/pi-tui": "0.84.3",
|