@victor-software-house/pi-openai-proxy 2.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.mjs +1 -1
- package/dist/exposure.d.mts +5 -1
- package/dist/exposure.mjs +9 -8
- package/dist/index.mjs +7 -1
- package/extensions/proxy.ts +177 -194
- package/package.json +1 -1
package/dist/config.mjs
CHANGED
package/dist/exposure.d.mts
CHANGED
|
@@ -37,9 +37,13 @@ type ModelExposureOutcome = ModelExposureResult | ModelExposureError;
|
|
|
37
37
|
/**
|
|
38
38
|
* Compute the full model-exposure result from config and available models.
|
|
39
39
|
*
|
|
40
|
+
* @param available - Models with auth configured (pi's getAvailable())
|
|
41
|
+
* @param allRegistered - All registered models regardless of auth (pi's getAll())
|
|
42
|
+
* @param config - Model exposure configuration
|
|
43
|
+
*
|
|
40
44
|
* Call this at startup and whenever config or the model registry changes.
|
|
41
45
|
*/
|
|
42
|
-
declare function computeModelExposure(available: readonly Model<Api>[], config: ModelExposureConfig): ModelExposureOutcome;
|
|
46
|
+
declare function computeModelExposure(available: readonly Model<Api>[], allRegistered: readonly Model<Api>[], config: ModelExposureConfig): ModelExposureOutcome;
|
|
43
47
|
/**
|
|
44
48
|
* Resolve a model ID from an incoming request against the exposure result.
|
|
45
49
|
*
|
package/dist/exposure.mjs
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
//#region src/openai/model-exposure.ts
|
|
3
|
-
function filterExposedModels(available, config) {
|
|
3
|
+
function filterExposedModels(available, allRegistered, config) {
|
|
4
4
|
switch (config.modelExposureMode) {
|
|
5
|
-
case "
|
|
6
|
-
case "
|
|
7
|
-
const providers = new Set(config.scopedProviders);
|
|
8
|
-
return available.filter((m) => providers.has(m.provider));
|
|
9
|
-
}
|
|
5
|
+
case "scoped": return [...available];
|
|
6
|
+
case "all": return [...allRegistered];
|
|
10
7
|
case "custom": {
|
|
11
8
|
const allowed = new Set(config.customModels);
|
|
12
9
|
return available.filter((m) => allowed.has(`${m.provider}/${m.id}`));
|
|
@@ -125,10 +122,14 @@ function validatePrefixUniqueness(models, prefixes, mode) {
|
|
|
125
122
|
/**
|
|
126
123
|
* Compute the full model-exposure result from config and available models.
|
|
127
124
|
*
|
|
125
|
+
* @param available - Models with auth configured (pi's getAvailable())
|
|
126
|
+
* @param allRegistered - All registered models regardless of auth (pi's getAll())
|
|
127
|
+
* @param config - Model exposure configuration
|
|
128
|
+
*
|
|
128
129
|
* Call this at startup and whenever config or the model registry changes.
|
|
129
130
|
*/
|
|
130
|
-
function computeModelExposure(available, config) {
|
|
131
|
-
const exposed = filterExposedModels(available, config);
|
|
131
|
+
function computeModelExposure(available, allRegistered, config) {
|
|
132
|
+
const exposed = filterExposedModels(available, allRegistered, config);
|
|
132
133
|
const prefixError = validatePrefixUniqueness(exposed, config.providerPrefixes, config.publicModelIdMode);
|
|
133
134
|
if (prefixError !== void 0) return {
|
|
134
135
|
ok: false,
|
package/dist/index.mjs
CHANGED
|
@@ -66,6 +66,12 @@ function getRegistry() {
|
|
|
66
66
|
function getAvailableModels() {
|
|
67
67
|
return getRegistry().getAvailable();
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Get all registered models (regardless of auth state).
|
|
71
|
+
*/
|
|
72
|
+
function getAllModels() {
|
|
73
|
+
return getRegistry().getAll();
|
|
74
|
+
}
|
|
69
75
|
//#endregion
|
|
70
76
|
//#region src/server/errors.ts
|
|
71
77
|
function makeError(message, type, param, code) {
|
|
@@ -1294,7 +1300,7 @@ function buildExposureConfig(config) {
|
|
|
1294
1300
|
* Returns the exposure result or throws on config errors.
|
|
1295
1301
|
*/
|
|
1296
1302
|
function getExposure(config) {
|
|
1297
|
-
const outcome = computeModelExposure(getAvailableModels(), buildExposureConfig(config));
|
|
1303
|
+
const outcome = computeModelExposure(getAvailableModels(), getAllModels(), buildExposureConfig(config));
|
|
1298
1304
|
if (!outcome.ok) throw new Error(`Model exposure configuration error: ${outcome.message}`);
|
|
1299
1305
|
return outcome;
|
|
1300
1306
|
}
|
package/extensions/proxy.ts
CHANGED
|
@@ -30,7 +30,13 @@ import {
|
|
|
30
30
|
getSettingsListTheme,
|
|
31
31
|
ModelRegistry,
|
|
32
32
|
} from "@mariozechner/pi-coding-agent";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
type Component,
|
|
35
|
+
Container,
|
|
36
|
+
type SettingItem,
|
|
37
|
+
SettingsList,
|
|
38
|
+
Text,
|
|
39
|
+
} from "@mariozechner/pi-tui";
|
|
34
40
|
|
|
35
41
|
// Config schema -- single source of truth
|
|
36
42
|
import {
|
|
@@ -77,6 +83,12 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
77
83
|
return registry.getAvailable();
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
function getAllRegisteredModels(): Model<Api>[] {
|
|
87
|
+
const auth = AuthStorage.create();
|
|
88
|
+
const registry = new ModelRegistry(auth);
|
|
89
|
+
return registry.getAll();
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
function getUniqueProviders(models: readonly Model<Api>[]): string[] {
|
|
81
93
|
const seen = new Set<string>();
|
|
82
94
|
for (const m of models) {
|
|
@@ -447,7 +459,8 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
447
459
|
|
|
448
460
|
// Public ID preview (first 5 exposed models)
|
|
449
461
|
const models = getAvailableModels();
|
|
450
|
-
const
|
|
462
|
+
const allModels = getAllRegisteredModels();
|
|
463
|
+
const outcome = computeModelExposure(models, allModels, buildExposureConfig());
|
|
451
464
|
if (outcome.ok && outcome.models.length > 0) {
|
|
452
465
|
const preview = outcome.models.slice(0, 5).map((m) => m.publicId);
|
|
453
466
|
const suffix =
|
|
@@ -469,24 +482,13 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
469
482
|
const models = getAvailableModels();
|
|
470
483
|
const issues: string[] = [];
|
|
471
484
|
|
|
485
|
+
const allModels = getAllRegisteredModels();
|
|
486
|
+
|
|
472
487
|
// Check available models
|
|
473
488
|
if (models.length === 0) {
|
|
474
489
|
issues.push("No models have auth configured. The proxy will expose 0 models.");
|
|
475
490
|
}
|
|
476
491
|
|
|
477
|
-
// Check scoped providers reference valid providers
|
|
478
|
-
if (config.modelExposureMode === "scoped") {
|
|
479
|
-
const availableProviders = new Set(getUniqueProviders(models));
|
|
480
|
-
for (const p of config.scopedProviders) {
|
|
481
|
-
if (!availableProviders.has(p)) {
|
|
482
|
-
issues.push(`Scoped provider '${p}' has no available models (no auth or unknown).`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (config.scopedProviders.length === 0) {
|
|
486
|
-
issues.push("Scoped mode with empty provider list will expose 0 models.");
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
492
|
// Check custom models reference valid canonical IDs
|
|
491
493
|
if (config.modelExposureMode === "custom") {
|
|
492
494
|
const canonicalSet = new Set(models.map((m) => `${m.provider}/${m.id}`));
|
|
@@ -501,7 +503,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
501
503
|
}
|
|
502
504
|
|
|
503
505
|
// Run the full exposure computation to catch ID/prefix errors
|
|
504
|
-
const outcome = computeModelExposure(models, buildExposureConfig());
|
|
506
|
+
const outcome = computeModelExposure(models, allModels, buildExposureConfig());
|
|
505
507
|
if (!outcome.ok) {
|
|
506
508
|
issues.push(outcome.message);
|
|
507
509
|
}
|
|
@@ -532,27 +534,92 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
532
534
|
|
|
533
535
|
let lastGeneratedToken = "";
|
|
534
536
|
|
|
535
|
-
function
|
|
536
|
-
|
|
537
|
+
function customModelsDisplay(): string {
|
|
538
|
+
if (config.modelExposureMode !== "custom") return "n/a";
|
|
539
|
+
return config.customModels.length > 0
|
|
540
|
+
? `${String(config.customModels.length)} selected`
|
|
541
|
+
: "(none)";
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Build a submenu Component for selecting custom models.
|
|
546
|
+
* Shows all available models as a toggleable checklist.
|
|
547
|
+
*/
|
|
548
|
+
function buildModelSelectorSubmenu(
|
|
549
|
+
_currentValue: string,
|
|
550
|
+
done: (selectedValue?: string) => void,
|
|
551
|
+
): Component {
|
|
537
552
|
const models = getAvailableModels();
|
|
538
|
-
const
|
|
553
|
+
const selected = new Set(config.customModels);
|
|
554
|
+
let selectedIndex = 0;
|
|
539
555
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
556
|
+
function toggle(canonicalId: string): void {
|
|
557
|
+
if (selected.has(canonicalId)) {
|
|
558
|
+
selected.delete(canonicalId);
|
|
559
|
+
} else {
|
|
560
|
+
selected.add(canonicalId);
|
|
561
|
+
}
|
|
562
|
+
config = { ...config, customModels: [...selected] };
|
|
563
|
+
saveConfigToFile(config);
|
|
564
|
+
config = loadConfigFromFile();
|
|
565
|
+
}
|
|
544
566
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
567
|
+
return {
|
|
568
|
+
render(width: number): string[] {
|
|
569
|
+
const lines: string[] = [];
|
|
570
|
+
lines.push(" Select models (Space: toggle, Esc: done)");
|
|
571
|
+
lines.push("");
|
|
572
|
+
|
|
573
|
+
if (models.length === 0) {
|
|
574
|
+
lines.push(" No models available (no auth configured)");
|
|
575
|
+
return lines;
|
|
576
|
+
}
|
|
548
577
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
578
|
+
const maxVisible = 15;
|
|
579
|
+
const start = Math.max(
|
|
580
|
+
0,
|
|
581
|
+
Math.min(selectedIndex - Math.floor(maxVisible / 2), models.length - maxVisible),
|
|
582
|
+
);
|
|
583
|
+
const end = Math.min(start + maxVisible, models.length);
|
|
584
|
+
|
|
585
|
+
for (let i = start; i < end; i++) {
|
|
586
|
+
const m = models[i];
|
|
587
|
+
if (m === undefined) continue;
|
|
588
|
+
const canonical = `${m.provider}/${m.id}`;
|
|
589
|
+
const check = selected.has(canonical) ? "[x]" : "[ ]";
|
|
590
|
+
const cursor = i === selectedIndex ? "> " : " ";
|
|
591
|
+
const line = `${cursor}${check} ${canonical}`;
|
|
592
|
+
const truncated = line.length > width ? line.slice(0, width - 1) : line;
|
|
593
|
+
lines.push(truncated);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
lines.push("");
|
|
597
|
+
lines.push(` ${String(selected.size)} of ${String(models.length)} selected`);
|
|
598
|
+
return lines;
|
|
599
|
+
},
|
|
600
|
+
invalidate(): void {
|
|
601
|
+
// no-op
|
|
602
|
+
},
|
|
603
|
+
handleInput(data: string): void {
|
|
604
|
+
if (data === "\x1B" || data === "q") {
|
|
605
|
+
done(`${String(selected.size)} selected`);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (data === "\x1B[A" && selectedIndex > 0) {
|
|
609
|
+
selectedIndex--;
|
|
610
|
+
} else if (data === "\x1B[B" && selectedIndex < models.length - 1) {
|
|
611
|
+
selectedIndex++;
|
|
612
|
+
} else if (data === " " || data === "\r") {
|
|
613
|
+
const m = models[selectedIndex];
|
|
614
|
+
if (m !== undefined) {
|
|
615
|
+
toggle(`${m.provider}/${m.id}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
}
|
|
555
621
|
|
|
622
|
+
function buildSettingItems(): SettingItem[] {
|
|
556
623
|
return [
|
|
557
624
|
// --- Server ---
|
|
558
625
|
{
|
|
@@ -618,67 +685,24 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
618
685
|
{
|
|
619
686
|
id: "modelExposureMode",
|
|
620
687
|
label: "Exposure mode",
|
|
621
|
-
description:
|
|
688
|
+
description:
|
|
689
|
+
"scoped = pi's available models, all = all registered, custom = manual selection",
|
|
622
690
|
currentValue: config.modelExposureMode,
|
|
623
|
-
values: ["
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
id: "scopedProviders",
|
|
627
|
-
label: "Scoped providers",
|
|
628
|
-
description: `Toggle providers for scoped mode. Available: ${availableProviders.join(", ") || "(none)"}`,
|
|
629
|
-
currentValue: scopedDisplay,
|
|
630
|
-
values: availableProviders.map((p) => {
|
|
631
|
-
const selected = scopedSet.has(p);
|
|
632
|
-
return selected ? `[-] ${p}` : `[+] ${p}`;
|
|
633
|
-
}),
|
|
691
|
+
values: ["scoped", "all", "custom"],
|
|
634
692
|
},
|
|
635
693
|
{
|
|
636
694
|
id: "customModels",
|
|
637
|
-
label: "
|
|
638
|
-
description:
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
label: "Prefix overrides",
|
|
645
|
-
description: "Custom prefix labels for providers (provider=label)",
|
|
646
|
-
currentValue: prefixDisplay,
|
|
647
|
-
values: buildPrefixValues(availableProviders),
|
|
695
|
+
label: "Select models",
|
|
696
|
+
description:
|
|
697
|
+
config.modelExposureMode === "custom"
|
|
698
|
+
? "Press Enter to open model selector"
|
|
699
|
+
: "Switch exposure mode to 'custom' to select models",
|
|
700
|
+
currentValue: customModelsDisplay(),
|
|
701
|
+
submenu: config.modelExposureMode === "custom" ? buildModelSelectorSubmenu : undefined,
|
|
648
702
|
},
|
|
649
703
|
];
|
|
650
704
|
}
|
|
651
705
|
|
|
652
|
-
/**
|
|
653
|
-
* Build toggle values for custom model selection.
|
|
654
|
-
* Each model is shown as "[+] provider/id" or "[-] provider/id".
|
|
655
|
-
*/
|
|
656
|
-
function buildCustomModelValues(models: readonly Model<Api>[]): string[] {
|
|
657
|
-
const customSet = new Set(config.customModels);
|
|
658
|
-
return models.map((m) => {
|
|
659
|
-
const canonical = `${m.provider}/${m.id}`;
|
|
660
|
-
const selected = customSet.has(canonical);
|
|
661
|
-
return selected ? `[-] ${canonical}` : `[+] ${canonical}`;
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Build toggle values for prefix override editing.
|
|
667
|
-
* Each provider is shown as "provider=label" or "provider (default)".
|
|
668
|
-
*/
|
|
669
|
-
function buildPrefixValues(providers: readonly string[]): string[] {
|
|
670
|
-
const values: string[] = [];
|
|
671
|
-
for (const p of providers) {
|
|
672
|
-
const override = config.providerPrefixes[p];
|
|
673
|
-
if (override !== undefined && override.length > 0) {
|
|
674
|
-
values.push(`clear ${p}`);
|
|
675
|
-
} else {
|
|
676
|
-
values.push(`set ${p}`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return values;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
706
|
const VALID_ID_MODES = new Set<string>(["collision-prefixed", "universal", "always-prefixed"]);
|
|
683
707
|
|
|
684
708
|
function isPublicModelIdMode(v: string): v is PublicModelIdMode {
|
|
@@ -743,14 +767,8 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
743
767
|
config = { ...config, modelExposureMode: value };
|
|
744
768
|
}
|
|
745
769
|
break;
|
|
746
|
-
case "scopedProviders":
|
|
747
|
-
applyScopedProviderToggle(value);
|
|
748
|
-
break;
|
|
749
770
|
case "customModels":
|
|
750
|
-
|
|
751
|
-
break;
|
|
752
|
-
case "providerPrefixes":
|
|
753
|
-
applyPrefixAction(value);
|
|
771
|
+
// Handled by submenu -- no cycling
|
|
754
772
|
break;
|
|
755
773
|
}
|
|
756
774
|
saveConfigToFile(config);
|
|
@@ -758,70 +776,32 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
758
776
|
}
|
|
759
777
|
|
|
760
778
|
/**
|
|
761
|
-
*
|
|
762
|
-
* Value format: "[+] provider" to add, "[-] provider" to remove.
|
|
763
|
-
*/
|
|
764
|
-
function applyScopedProviderToggle(value: string): void {
|
|
765
|
-
const match = /^\[([+-])\]\s+(.+)$/.exec(value);
|
|
766
|
-
if (match === null) return;
|
|
767
|
-
const action = match[1];
|
|
768
|
-
const provider = match[2];
|
|
769
|
-
if (provider === undefined) return;
|
|
770
|
-
|
|
771
|
-
const current = new Set(config.scopedProviders);
|
|
772
|
-
if (action === "+") {
|
|
773
|
-
current.add(provider);
|
|
774
|
-
} else {
|
|
775
|
-
current.delete(provider);
|
|
776
|
-
}
|
|
777
|
-
config = { ...config, scopedProviders: [...current] };
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* Toggle a model in/out of the custom models list.
|
|
782
|
-
* Value format: "[+] provider/model-id" to add, "[-] provider/model-id" to remove.
|
|
783
|
-
*/
|
|
784
|
-
function applyCustomModelToggle(value: string): void {
|
|
785
|
-
const match = /^\[([+-])\]\s+(.+)$/.exec(value);
|
|
786
|
-
if (match === null) return;
|
|
787
|
-
const action = match[1];
|
|
788
|
-
const canonicalId = match[2];
|
|
789
|
-
if (canonicalId === undefined) return;
|
|
790
|
-
|
|
791
|
-
const current = new Set(config.customModels);
|
|
792
|
-
if (action === "+") {
|
|
793
|
-
current.add(canonicalId);
|
|
794
|
-
} else {
|
|
795
|
-
current.delete(canonicalId);
|
|
796
|
-
}
|
|
797
|
-
config = { ...config, customModels: [...current] };
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Apply a prefix override action.
|
|
802
|
-
* Value format: "set provider" to prompt for a label, "clear provider" to remove override.
|
|
779
|
+
* Get the display value for a setting after it has been applied.
|
|
803
780
|
*/
|
|
804
|
-
function
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
781
|
+
function getDisplayValue(id: string): string {
|
|
782
|
+
switch (id) {
|
|
783
|
+
case "lifetime":
|
|
784
|
+
return config.lifetime;
|
|
785
|
+
case "host":
|
|
786
|
+
return config.host;
|
|
787
|
+
case "port":
|
|
788
|
+
return String(config.port);
|
|
789
|
+
case "authToken":
|
|
790
|
+
return config.authToken.length > 0 ? "enabled" : "disabled";
|
|
791
|
+
case "remoteImages":
|
|
792
|
+
return config.remoteImages ? "on" : "off";
|
|
793
|
+
case "maxBodySizeMb":
|
|
794
|
+
return `${String(config.maxBodySizeMb)} MB`;
|
|
795
|
+
case "upstreamTimeoutSec":
|
|
796
|
+
return `${String(config.upstreamTimeoutSec)}s`;
|
|
797
|
+
case "publicModelIdMode":
|
|
798
|
+
return config.publicModelIdMode;
|
|
799
|
+
case "modelExposureMode":
|
|
800
|
+
return config.modelExposureMode;
|
|
801
|
+
case "customModels":
|
|
802
|
+
return customModelsDisplay();
|
|
803
|
+
default:
|
|
804
|
+
return "";
|
|
825
805
|
}
|
|
826
806
|
}
|
|
827
807
|
|
|
@@ -830,54 +810,57 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
830
810
|
|
|
831
811
|
await ctx.ui.custom<void>(
|
|
832
812
|
(tui, theme, _kb, done) => {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
container.addChild(
|
|
857
|
-
new Text(
|
|
858
|
-
theme.fg(
|
|
859
|
-
"dim",
|
|
860
|
-
"Esc: close | Arrow keys: navigate | Space: toggle | Restart proxy to apply",
|
|
861
|
-
),
|
|
862
|
-
1,
|
|
863
|
-
0,
|
|
864
|
-
),
|
|
865
|
-
);
|
|
813
|
+
const container = new Container();
|
|
814
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("Proxy Settings")), 1, 0));
|
|
815
|
+
container.addChild(new Text(theme.fg("dim", getConfigPath()), 1, 0));
|
|
816
|
+
|
|
817
|
+
const items = buildSettingItems();
|
|
818
|
+
const settingsList = new SettingsList(
|
|
819
|
+
items,
|
|
820
|
+
12,
|
|
821
|
+
getSettingsListTheme(),
|
|
822
|
+
(id, newValue) => {
|
|
823
|
+
lastGeneratedToken = "";
|
|
824
|
+
applySetting(id, newValue);
|
|
825
|
+
if (lastGeneratedToken.length > 0) {
|
|
826
|
+
ctx.ui.notify(`Auth token: ${lastGeneratedToken}`, "info");
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Update display value in-place (no rebuild, preserves selection)
|
|
830
|
+
settingsList.updateValue(id, getDisplayValue(id));
|
|
831
|
+
|
|
832
|
+
// When exposure mode changes, update the "Select models" item
|
|
833
|
+
if (id === "modelExposureMode") {
|
|
834
|
+
settingsList.updateValue("customModels", customModelsDisplay());
|
|
835
|
+
}
|
|
866
836
|
|
|
867
|
-
|
|
868
|
-
|
|
837
|
+
tui.requestRender();
|
|
838
|
+
},
|
|
839
|
+
() => done(undefined),
|
|
840
|
+
{ enableSearch: true },
|
|
841
|
+
);
|
|
869
842
|
|
|
870
|
-
|
|
843
|
+
container.addChild(settingsList);
|
|
844
|
+
container.addChild(
|
|
845
|
+
new Text(
|
|
846
|
+
theme.fg(
|
|
847
|
+
"dim",
|
|
848
|
+
"Esc: close | Arrow keys: navigate | Space: toggle | Restart proxy to apply",
|
|
849
|
+
),
|
|
850
|
+
1,
|
|
851
|
+
0,
|
|
852
|
+
),
|
|
853
|
+
);
|
|
871
854
|
|
|
872
855
|
return {
|
|
873
856
|
render(width: number): string[] {
|
|
874
|
-
return
|
|
857
|
+
return container.render(width);
|
|
875
858
|
},
|
|
876
859
|
invalidate(): void {
|
|
877
|
-
|
|
860
|
+
container.invalidate();
|
|
878
861
|
},
|
|
879
862
|
handleInput(data: string): void {
|
|
880
|
-
|
|
863
|
+
settingsList.handleInput?.(data);
|
|
881
864
|
tui.requestRender();
|
|
882
865
|
},
|
|
883
866
|
};
|
package/package.json
CHANGED