@victor-software-house/pi-openai-proxy 4.9.2 → 4.9.4
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/index.mjs +368 -34
- package/extensions/proxy.ts +26 -26
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import { computeModelExposure, resolveExposedModel } from "./exposure.mjs";
|
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { AuthStorage, ModelRegistry, SettingsManager } from "@mariozechner/pi-coding-agent";
|
|
6
6
|
import { randomBytes } from "node:crypto";
|
|
7
|
-
import { Type } from "@sinclair/typebox";
|
|
8
7
|
import { completeSimple, streamSimple } from "@mariozechner/pi-ai";
|
|
9
8
|
import { Hono } from "hono";
|
|
10
9
|
import { stream } from "hono/streaming";
|
|
@@ -53,8 +52,8 @@ let settingsManager;
|
|
|
53
52
|
*/
|
|
54
53
|
function initRegistry() {
|
|
55
54
|
authStorage = AuthStorage.create();
|
|
56
|
-
registry =
|
|
57
|
-
settingsManager = SettingsManager.create();
|
|
55
|
+
registry = ModelRegistry.create(authStorage);
|
|
56
|
+
settingsManager = SettingsManager.create(process.cwd());
|
|
58
57
|
return registry.getError();
|
|
59
58
|
}
|
|
60
59
|
function getRegistry() {
|
|
@@ -72,6 +71,16 @@ function getAvailableModels() {
|
|
|
72
71
|
return getRegistry().getAvailable();
|
|
73
72
|
}
|
|
74
73
|
/**
|
|
74
|
+
* Resolve request auth for a specific model at request time.
|
|
75
|
+
*
|
|
76
|
+
* This uses Pi's current request-auth contract, which may return both an API
|
|
77
|
+
* key and model-specific headers. That covers API-key providers, OAuth-backed
|
|
78
|
+
* providers, authHeader handling, and dynamic models.json header resolution.
|
|
79
|
+
*/
|
|
80
|
+
async function getRequestAuth(model) {
|
|
81
|
+
return getRegistry().getApiKeyAndHeaders(model);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
75
84
|
* Get the `enabledModels` patterns from pi's global settings.
|
|
76
85
|
*
|
|
77
86
|
* These are the canonical model IDs (e.g. "anthropic/claude-sonnet-4-6")
|
|
@@ -79,9 +88,10 @@ function getAvailableModels() {
|
|
|
79
88
|
*
|
|
80
89
|
* Returns undefined when no filter is configured (all models enabled).
|
|
81
90
|
*/
|
|
82
|
-
function getEnabledModels() {
|
|
83
|
-
getSettingsManager()
|
|
84
|
-
|
|
91
|
+
async function getEnabledModels() {
|
|
92
|
+
const manager = getSettingsManager();
|
|
93
|
+
await manager.reload();
|
|
94
|
+
return manager.getEnabledModels();
|
|
85
95
|
}
|
|
86
96
|
//#endregion
|
|
87
97
|
//#region src/server/errors.ts
|
|
@@ -751,6 +761,328 @@ async function* streamToSSE(events, requestId, model, includeUsage) {
|
|
|
751
761
|
yield encodeDone();
|
|
752
762
|
}
|
|
753
763
|
//#endregion
|
|
764
|
+
//#region node_modules/typebox/build/system/memory/metrics.mjs
|
|
765
|
+
/** TypeBox instantiation metrics */
|
|
766
|
+
const Metrics = {
|
|
767
|
+
assign: 0,
|
|
768
|
+
create: 0,
|
|
769
|
+
clone: 0,
|
|
770
|
+
discard: 0,
|
|
771
|
+
update: 0
|
|
772
|
+
};
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region node_modules/typebox/build/guard/guard.mjs
|
|
775
|
+
/** Returns true if this value is an array */
|
|
776
|
+
function IsArray(value) {
|
|
777
|
+
return Array.isArray(value);
|
|
778
|
+
}
|
|
779
|
+
/** Returns true if this value is bigint */
|
|
780
|
+
function IsBigInt(value) {
|
|
781
|
+
return IsEqual(typeof value, "bigint");
|
|
782
|
+
}
|
|
783
|
+
/** Returns true if this value is a boolean */
|
|
784
|
+
function IsBoolean(value) {
|
|
785
|
+
return IsEqual(typeof value, "boolean");
|
|
786
|
+
}
|
|
787
|
+
/** Returns true if this value is null */
|
|
788
|
+
function IsNull(value) {
|
|
789
|
+
return IsEqual(value, null);
|
|
790
|
+
}
|
|
791
|
+
/** Returns true if this value is number */
|
|
792
|
+
function IsNumber(value) {
|
|
793
|
+
return Number.isFinite(value);
|
|
794
|
+
}
|
|
795
|
+
/** Returns true if this value is an object */
|
|
796
|
+
function IsObject(value) {
|
|
797
|
+
return IsEqual(typeof value, "object") && !IsNull(value);
|
|
798
|
+
}
|
|
799
|
+
/** Returns true if this value is string */
|
|
800
|
+
function IsString(value) {
|
|
801
|
+
return IsEqual(typeof value, "string");
|
|
802
|
+
}
|
|
803
|
+
function IsEqual(left, right) {
|
|
804
|
+
return left === right;
|
|
805
|
+
}
|
|
806
|
+
/** Returns true if the PropertyKey is Unsafe (ref: prototype-pollution). */
|
|
807
|
+
function IsUnsafePropertyKey(key) {
|
|
808
|
+
return IsEqual(key, "__proto__") || IsEqual(key, "constructor") || IsEqual(key, "prototype");
|
|
809
|
+
}
|
|
810
|
+
/** Returns true if this value has this property key */
|
|
811
|
+
function HasPropertyKey(value, key) {
|
|
812
|
+
return IsUnsafePropertyKey(key) ? Object.prototype.hasOwnProperty.call(value, key) : key in value;
|
|
813
|
+
}
|
|
814
|
+
/** Returns property keys for this object via `Object.getOwnPropertyKeys({ ... })` */
|
|
815
|
+
function Keys(value) {
|
|
816
|
+
return Object.getOwnPropertyNames(value);
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
819
|
+
//#region node_modules/typebox/build/system/memory/clone.mjs
|
|
820
|
+
function IsGuard(value) {
|
|
821
|
+
return IsObject(value) && HasPropertyKey(value, "~guard");
|
|
822
|
+
}
|
|
823
|
+
function FromGuard(value) {
|
|
824
|
+
return value;
|
|
825
|
+
}
|
|
826
|
+
function FromArray(value) {
|
|
827
|
+
return value.map((value) => FromValue(value));
|
|
828
|
+
}
|
|
829
|
+
function FromObject(value) {
|
|
830
|
+
const result = {};
|
|
831
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
832
|
+
for (const key of Object.keys(descriptors)) {
|
|
833
|
+
const descriptor = descriptors[key];
|
|
834
|
+
if (HasPropertyKey(descriptor, "value")) Object.defineProperty(result, key, {
|
|
835
|
+
...descriptor,
|
|
836
|
+
value: FromValue(descriptor.value)
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
function FromRegExp(value) {
|
|
842
|
+
return new RegExp(value.source, value.flags);
|
|
843
|
+
}
|
|
844
|
+
function FromUnknown(value) {
|
|
845
|
+
return value;
|
|
846
|
+
}
|
|
847
|
+
function FromValue(value) {
|
|
848
|
+
return value instanceof RegExp ? FromRegExp(value) : IsGuard(value) ? FromGuard(value) : IsArray(value) ? FromArray(value) : IsObject(value) ? FromObject(value) : FromUnknown(value);
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Clones a value using the TypeBox type cloning strategy. This function preserves non-enumerable
|
|
852
|
+
* properties from the source value. This is to ensure cloned types retain discriminable
|
|
853
|
+
* hidden properties.
|
|
854
|
+
*/
|
|
855
|
+
function Clone(value) {
|
|
856
|
+
Metrics.clone += 1;
|
|
857
|
+
return FromValue(value);
|
|
858
|
+
}
|
|
859
|
+
//#endregion
|
|
860
|
+
//#region node_modules/typebox/build/system/settings/settings.mjs
|
|
861
|
+
const settings = {
|
|
862
|
+
immutableTypes: false,
|
|
863
|
+
maxErrors: 8,
|
|
864
|
+
useAcceleration: true,
|
|
865
|
+
exactOptionalPropertyTypes: false,
|
|
866
|
+
enumerableKind: false,
|
|
867
|
+
correctiveParse: false
|
|
868
|
+
};
|
|
869
|
+
/** Gets current system settings */
|
|
870
|
+
function Get() {
|
|
871
|
+
return settings;
|
|
872
|
+
}
|
|
873
|
+
//#endregion
|
|
874
|
+
//#region node_modules/typebox/build/system/memory/create.mjs
|
|
875
|
+
function MergeHidden(left, right) {
|
|
876
|
+
for (const key of Object.keys(right)) Object.defineProperty(left, key, {
|
|
877
|
+
configurable: true,
|
|
878
|
+
writable: true,
|
|
879
|
+
enumerable: false,
|
|
880
|
+
value: right[key]
|
|
881
|
+
});
|
|
882
|
+
return left;
|
|
883
|
+
}
|
|
884
|
+
function Merge(left, right) {
|
|
885
|
+
return {
|
|
886
|
+
...left,
|
|
887
|
+
...right
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Creates an object with hidden, enumerable, and optional property sets. This function
|
|
892
|
+
* ensures types are instantiated according to configuration rules for enumerable and
|
|
893
|
+
* non-enumerable properties.
|
|
894
|
+
*/
|
|
895
|
+
function Create(hidden, enumerable, options = {}) {
|
|
896
|
+
Metrics.create += 1;
|
|
897
|
+
const settings = Get();
|
|
898
|
+
const withOptions = Merge(enumerable, options);
|
|
899
|
+
const withHidden = settings.enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
900
|
+
return settings.immutableTypes ? Object.freeze(withHidden) : withHidden;
|
|
901
|
+
}
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region node_modules/typebox/build/system/memory/update.mjs
|
|
904
|
+
/**
|
|
905
|
+
* Updates a value with new properties while preserving property enumerability. Use this function to modify
|
|
906
|
+
* existing types without altering their configuration.
|
|
907
|
+
*/
|
|
908
|
+
function Update(current, hidden, enumerable) {
|
|
909
|
+
Metrics.update += 1;
|
|
910
|
+
const settings = Get();
|
|
911
|
+
const result = Clone(current);
|
|
912
|
+
for (const key of Object.keys(hidden)) Object.defineProperty(result, key, {
|
|
913
|
+
configurable: true,
|
|
914
|
+
writable: true,
|
|
915
|
+
enumerable: settings.enumerableKind,
|
|
916
|
+
value: hidden[key]
|
|
917
|
+
});
|
|
918
|
+
for (const key of Object.keys(enumerable)) Object.defineProperty(result, key, {
|
|
919
|
+
configurable: true,
|
|
920
|
+
enumerable: true,
|
|
921
|
+
writable: true,
|
|
922
|
+
value: enumerable[key]
|
|
923
|
+
});
|
|
924
|
+
return result;
|
|
925
|
+
}
|
|
926
|
+
//#endregion
|
|
927
|
+
//#region node_modules/typebox/build/type/types/schema.mjs
|
|
928
|
+
function IsSchema(value) {
|
|
929
|
+
return IsObject(value);
|
|
930
|
+
}
|
|
931
|
+
//#endregion
|
|
932
|
+
//#region node_modules/typebox/build/type/types/_optional.mjs
|
|
933
|
+
/** Adds Optional to the given type. */
|
|
934
|
+
function OptionalAdd(type) {
|
|
935
|
+
return Update(type, { "~optional": true }, {});
|
|
936
|
+
}
|
|
937
|
+
/** Applies an Optional modifier to the given type. */
|
|
938
|
+
function Optional(type) {
|
|
939
|
+
return OptionalAdd(type);
|
|
940
|
+
}
|
|
941
|
+
/** Returns true if the given value is TOptional */
|
|
942
|
+
function IsOptional(value) {
|
|
943
|
+
return IsSchema(value) && HasPropertyKey(value, "~optional");
|
|
944
|
+
}
|
|
945
|
+
//#endregion
|
|
946
|
+
//#region node_modules/typebox/build/type/types/array.mjs
|
|
947
|
+
/** Creates an Array type. */
|
|
948
|
+
function _Array_(items, options) {
|
|
949
|
+
return Create({ "~kind": "Array" }, {
|
|
950
|
+
type: "array",
|
|
951
|
+
items
|
|
952
|
+
}, options);
|
|
953
|
+
}
|
|
954
|
+
//#endregion
|
|
955
|
+
//#region node_modules/typebox/build/type/types/properties.mjs
|
|
956
|
+
/** Creates a RequiredArray derived from the given TProperties value. */
|
|
957
|
+
function RequiredArray(properties) {
|
|
958
|
+
return Keys(properties).filter((key) => !IsOptional(properties[key]));
|
|
959
|
+
}
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region node_modules/typebox/build/type/types/object.mjs
|
|
962
|
+
/** Creates an Object type. */
|
|
963
|
+
function _Object_(properties, options = {}) {
|
|
964
|
+
const requiredKeys = RequiredArray(properties);
|
|
965
|
+
const required = requiredKeys.length > 0 ? { required: requiredKeys } : {};
|
|
966
|
+
return Create({ "~kind": "Object" }, {
|
|
967
|
+
type: "object",
|
|
968
|
+
...required,
|
|
969
|
+
properties
|
|
970
|
+
}, options);
|
|
971
|
+
}
|
|
972
|
+
//#endregion
|
|
973
|
+
//#region node_modules/typebox/build/type/types/union.mjs
|
|
974
|
+
/** Creates a Union type. */
|
|
975
|
+
function Union(anyOf, options = {}) {
|
|
976
|
+
return Create({ "~kind": "Union" }, { anyOf }, options);
|
|
977
|
+
}
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region node_modules/typebox/build/type/types/unknown.mjs
|
|
980
|
+
/** Creates an Unknown type. */
|
|
981
|
+
function Unknown(options) {
|
|
982
|
+
return Create({ ["~kind"]: "Unknown" }, {}, options);
|
|
983
|
+
}
|
|
984
|
+
//#endregion
|
|
985
|
+
//#region node_modules/typebox/build/system/hashing/hash.mjs
|
|
986
|
+
var ByteMarker;
|
|
987
|
+
(function(ByteMarker) {
|
|
988
|
+
ByteMarker[ByteMarker["Array"] = 0] = "Array";
|
|
989
|
+
ByteMarker[ByteMarker["BigInt"] = 1] = "BigInt";
|
|
990
|
+
ByteMarker[ByteMarker["Boolean"] = 2] = "Boolean";
|
|
991
|
+
ByteMarker[ByteMarker["Date"] = 3] = "Date";
|
|
992
|
+
ByteMarker[ByteMarker["Constructor"] = 4] = "Constructor";
|
|
993
|
+
ByteMarker[ByteMarker["Function"] = 5] = "Function";
|
|
994
|
+
ByteMarker[ByteMarker["Null"] = 6] = "Null";
|
|
995
|
+
ByteMarker[ByteMarker["Number"] = 7] = "Number";
|
|
996
|
+
ByteMarker[ByteMarker["Object"] = 8] = "Object";
|
|
997
|
+
ByteMarker[ByteMarker["RegExp"] = 9] = "RegExp";
|
|
998
|
+
ByteMarker[ByteMarker["String"] = 10] = "String";
|
|
999
|
+
ByteMarker[ByteMarker["Symbol"] = 11] = "Symbol";
|
|
1000
|
+
ByteMarker[ByteMarker["TypeArray"] = 12] = "TypeArray";
|
|
1001
|
+
ByteMarker[ByteMarker["Undefined"] = 13] = "Undefined";
|
|
1002
|
+
})(ByteMarker || (ByteMarker = {}));
|
|
1003
|
+
const [Prime, Size] = [BigInt("1099511628211"), BigInt("18446744073709551616")];
|
|
1004
|
+
Array.from({ length: 256 }).map((_, i) => BigInt(i));
|
|
1005
|
+
const F64 = new Float64Array(1);
|
|
1006
|
+
new DataView(F64.buffer);
|
|
1007
|
+
new Uint8Array(F64.buffer);
|
|
1008
|
+
new TextEncoder();
|
|
1009
|
+
//#endregion
|
|
1010
|
+
//#region node_modules/typebox/build/type/types/boolean.mjs
|
|
1011
|
+
/** Creates a Boolean type. */
|
|
1012
|
+
function Boolean(options) {
|
|
1013
|
+
return Create({ "~kind": "Boolean" }, { type: "boolean" }, options);
|
|
1014
|
+
}
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region node_modules/typebox/build/type/types/integer.mjs
|
|
1017
|
+
const IntegerPattern = "-?(?:0|[1-9][0-9]*)";
|
|
1018
|
+
/** Creates a Integer type. */
|
|
1019
|
+
function Integer(options) {
|
|
1020
|
+
return Create({ "~kind": "Integer" }, { type: "integer" }, options);
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
1023
|
+
//#region node_modules/typebox/build/type/types/literal.mjs
|
|
1024
|
+
var InvalidLiteralValue = class extends Error {
|
|
1025
|
+
constructor(value) {
|
|
1026
|
+
super(`Invalid Literal value`);
|
|
1027
|
+
Object.defineProperty(this, "cause", {
|
|
1028
|
+
value: { value },
|
|
1029
|
+
writable: false,
|
|
1030
|
+
configurable: false,
|
|
1031
|
+
enumerable: false
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
function LiteralTypeName(value) {
|
|
1036
|
+
return IsBigInt(value) ? "bigint" : IsBoolean(value) ? "boolean" : IsNumber(value) ? "number" : IsString(value) ? "string" : (() => {
|
|
1037
|
+
throw new InvalidLiteralValue(value);
|
|
1038
|
+
})();
|
|
1039
|
+
}
|
|
1040
|
+
/** Creates a Literal type. */
|
|
1041
|
+
function Literal(value, options) {
|
|
1042
|
+
return Create({ "~kind": "Literal" }, {
|
|
1043
|
+
type: LiteralTypeName(value),
|
|
1044
|
+
const: value
|
|
1045
|
+
}, options);
|
|
1046
|
+
}
|
|
1047
|
+
//#endregion
|
|
1048
|
+
//#region node_modules/typebox/build/type/types/null.mjs
|
|
1049
|
+
/** Creates a Null type. */
|
|
1050
|
+
function Null(options) {
|
|
1051
|
+
return Create({ "~kind": "Null" }, { type: "null" }, options);
|
|
1052
|
+
}
|
|
1053
|
+
//#endregion
|
|
1054
|
+
//#region node_modules/typebox/build/type/types/number.mjs
|
|
1055
|
+
const NumberPattern = "-?(?:0|[1-9][0-9]*)(?:.[0-9]+)?";
|
|
1056
|
+
/** Creates a Number type. */
|
|
1057
|
+
function Number$1(options) {
|
|
1058
|
+
return Create({ "~kind": "Number" }, { type: "number" }, options);
|
|
1059
|
+
}
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region node_modules/typebox/build/type/types/string.mjs
|
|
1062
|
+
/** Creates a String type. */
|
|
1063
|
+
function String$1(options) {
|
|
1064
|
+
return Create({ "~kind": "String" }, { type: "string" }, options);
|
|
1065
|
+
}
|
|
1066
|
+
//#endregion
|
|
1067
|
+
//#region node_modules/typebox/build/type/types/record.mjs
|
|
1068
|
+
const IntegerKey = `^${IntegerPattern}$`;
|
|
1069
|
+
`${NumberPattern}`;
|
|
1070
|
+
//#endregion
|
|
1071
|
+
//#region node_modules/typebox/build/type/script/token/internal/char.mjs
|
|
1072
|
+
function Range(start, end) {
|
|
1073
|
+
return Array.from({ length: end - start + 1 }, (_, i) => String.fromCharCode(start + i));
|
|
1074
|
+
}
|
|
1075
|
+
const Alpha = [...Range(97, 122), ...Range(65, 90)];
|
|
1076
|
+
const Digit = ["0", ...Range(49, 57)];
|
|
1077
|
+
[...Digit];
|
|
1078
|
+
[...[
|
|
1079
|
+
...Alpha,
|
|
1080
|
+
"_",
|
|
1081
|
+
"$"
|
|
1082
|
+
], ...Digit];
|
|
1083
|
+
[...Digit];
|
|
1084
|
+
new RegExp(IntegerKey);
|
|
1085
|
+
//#endregion
|
|
754
1086
|
//#region src/openai/json-schema-to-typebox.ts
|
|
755
1087
|
/**
|
|
756
1088
|
* JSON Schema -> TypeBox conversion for OpenAI function tool parameters.
|
|
@@ -819,7 +1151,7 @@ function jsonSchemaToTypebox(schema, path = "") {
|
|
|
819
1151
|
if (typeof rawType !== "string") {
|
|
820
1152
|
if (Object.keys(schema).length === 0 || Object.keys(schema).length === 1 && description !== void 0) return {
|
|
821
1153
|
ok: true,
|
|
822
|
-
schema:
|
|
1154
|
+
schema: Unknown(opts)
|
|
823
1155
|
};
|
|
824
1156
|
return {
|
|
825
1157
|
ok: false,
|
|
@@ -831,23 +1163,23 @@ function jsonSchemaToTypebox(schema, path = "") {
|
|
|
831
1163
|
case "object": return convertObject(schema, path, opts);
|
|
832
1164
|
case "string": return {
|
|
833
1165
|
ok: true,
|
|
834
|
-
schema:
|
|
1166
|
+
schema: String$1(opts)
|
|
835
1167
|
};
|
|
836
1168
|
case "number": return {
|
|
837
1169
|
ok: true,
|
|
838
|
-
schema:
|
|
1170
|
+
schema: Number$1(opts)
|
|
839
1171
|
};
|
|
840
1172
|
case "integer": return {
|
|
841
1173
|
ok: true,
|
|
842
|
-
schema:
|
|
1174
|
+
schema: Integer(opts)
|
|
843
1175
|
};
|
|
844
1176
|
case "boolean": return {
|
|
845
1177
|
ok: true,
|
|
846
|
-
schema:
|
|
1178
|
+
schema: Boolean(opts)
|
|
847
1179
|
};
|
|
848
1180
|
case "null": return {
|
|
849
1181
|
ok: true,
|
|
850
|
-
schema:
|
|
1182
|
+
schema: Null(opts)
|
|
851
1183
|
};
|
|
852
1184
|
case "array": return convertArray(schema, path, opts);
|
|
853
1185
|
default: return {
|
|
@@ -884,7 +1216,7 @@ function convertAnyOf(branches, path, opts) {
|
|
|
884
1216
|
}
|
|
885
1217
|
return {
|
|
886
1218
|
ok: true,
|
|
887
|
-
schema:
|
|
1219
|
+
schema: Union(converted, opts)
|
|
888
1220
|
};
|
|
889
1221
|
}
|
|
890
1222
|
function convertEnum(values, path, opts) {
|
|
@@ -902,10 +1234,9 @@ function convertEnum(values, path, opts) {
|
|
|
902
1234
|
message: `Empty enum at ${path || "root"}`,
|
|
903
1235
|
path
|
|
904
1236
|
};
|
|
905
|
-
const literals = stringValues.map((v) => Type.Literal(v));
|
|
906
1237
|
return {
|
|
907
1238
|
ok: true,
|
|
908
|
-
schema:
|
|
1239
|
+
schema: Union(stringValues.map((v) => Literal(v)), opts)
|
|
909
1240
|
};
|
|
910
1241
|
}
|
|
911
1242
|
function convertNullableType(typeArray, schema, path, opts) {
|
|
@@ -928,7 +1259,7 @@ function convertNullableType(typeArray, schema, path, opts) {
|
|
|
928
1259
|
if (!innerResult.ok) return innerResult;
|
|
929
1260
|
return {
|
|
930
1261
|
ok: true,
|
|
931
|
-
schema:
|
|
1262
|
+
schema: Union([innerResult.schema, Null()], opts)
|
|
932
1263
|
};
|
|
933
1264
|
}
|
|
934
1265
|
function convertObject(schema, path, opts) {
|
|
@@ -942,7 +1273,7 @@ function convertObject(schema, path, opts) {
|
|
|
942
1273
|
};
|
|
943
1274
|
if (rawProperties === void 0) return {
|
|
944
1275
|
ok: true,
|
|
945
|
-
schema:
|
|
1276
|
+
schema: _Object_({}, opts)
|
|
946
1277
|
};
|
|
947
1278
|
if (!isRecord(rawProperties)) return {
|
|
948
1279
|
ok: false,
|
|
@@ -958,24 +1289,24 @@ function convertObject(schema, path, opts) {
|
|
|
958
1289
|
const result = jsonSchemaToTypebox(propSchema, path.length > 0 ? `${path}.${key}` : key);
|
|
959
1290
|
if (!result.ok) return result;
|
|
960
1291
|
if (requiredSet.has(key)) typeboxProperties[key] = result.schema;
|
|
961
|
-
else typeboxProperties[key] =
|
|
1292
|
+
else typeboxProperties[key] = Optional(result.schema);
|
|
962
1293
|
}
|
|
963
1294
|
return {
|
|
964
1295
|
ok: true,
|
|
965
|
-
schema:
|
|
1296
|
+
schema: _Object_(typeboxProperties, opts)
|
|
966
1297
|
};
|
|
967
1298
|
}
|
|
968
1299
|
function convertArray(schema, path, opts) {
|
|
969
1300
|
const items = schema["items"];
|
|
970
1301
|
if (items === void 0) return {
|
|
971
1302
|
ok: true,
|
|
972
|
-
schema:
|
|
1303
|
+
schema: _Array_(Unknown(), opts)
|
|
973
1304
|
};
|
|
974
1305
|
const itemResult = jsonSchemaToTypebox(items, path.length > 0 ? `${path}.items` : "items");
|
|
975
1306
|
if (!itemResult.ok) return itemResult;
|
|
976
1307
|
return {
|
|
977
1308
|
ok: true,
|
|
978
|
-
schema:
|
|
1309
|
+
schema: _Array_(itemResult.schema, opts)
|
|
979
1310
|
};
|
|
980
1311
|
}
|
|
981
1312
|
//#endregion
|
|
@@ -998,7 +1329,7 @@ function convertTools(openaiTools) {
|
|
|
998
1329
|
param: result.path
|
|
999
1330
|
};
|
|
1000
1331
|
parameters = result.schema;
|
|
1001
|
-
} else parameters =
|
|
1332
|
+
} else parameters = _Object_({});
|
|
1002
1333
|
piTools.push({
|
|
1003
1334
|
name: fn.name,
|
|
1004
1335
|
description: fn.description ?? "",
|
|
@@ -1589,8 +1920,10 @@ async function buildStreamOptions(model, request, options) {
|
|
|
1589
1920
|
if (combinedSignal !== void 0) opts.signal = combinedSignal;
|
|
1590
1921
|
if (options.upstreamApiKey !== void 0) opts.apiKey = options.upstreamApiKey;
|
|
1591
1922
|
else {
|
|
1592
|
-
const
|
|
1593
|
-
if (
|
|
1923
|
+
const auth = await getRequestAuth(model);
|
|
1924
|
+
if (!auth.ok) throw new Error(auth.error);
|
|
1925
|
+
if (auth.apiKey !== void 0) opts.apiKey = auth.apiKey;
|
|
1926
|
+
if (auth.headers !== void 0) opts.headers = auth.headers;
|
|
1594
1927
|
}
|
|
1595
1928
|
const payloadFields = collectPayloadFields(request, model.api);
|
|
1596
1929
|
const strictFlags = collectToolStrictFlags(request.tools);
|
|
@@ -1619,19 +1952,19 @@ async function piStream(model, context, request, options) {
|
|
|
1619
1952
|
}
|
|
1620
1953
|
//#endregion
|
|
1621
1954
|
//#region src/server/routes.ts
|
|
1622
|
-
function fileConfigReader() {
|
|
1955
|
+
async function fileConfigReader() {
|
|
1623
1956
|
const file = loadConfigFromFile();
|
|
1624
1957
|
return {
|
|
1625
1958
|
publicModelIdMode: file.publicModelIdMode,
|
|
1626
1959
|
modelExposureMode: file.modelExposureMode,
|
|
1627
|
-
enabledModels: getEnabledModels(),
|
|
1960
|
+
enabledModels: await getEnabledModels(),
|
|
1628
1961
|
customModels: file.customModels,
|
|
1629
1962
|
providerPrefixes: file.providerPrefixes
|
|
1630
1963
|
};
|
|
1631
1964
|
}
|
|
1632
1965
|
function createRoutes(config, configReader = fileConfigReader) {
|
|
1633
|
-
function getExposure() {
|
|
1634
|
-
const outcome = computeModelExposure(getAvailableModels(), configReader());
|
|
1966
|
+
async function getExposure() {
|
|
1967
|
+
const outcome = computeModelExposure(getAvailableModels(), await configReader());
|
|
1635
1968
|
if (!outcome.ok) throw new Error(`Model exposure configuration error: ${outcome.message}`);
|
|
1636
1969
|
return outcome;
|
|
1637
1970
|
}
|
|
@@ -1645,17 +1978,17 @@ function createRoutes(config, configReader = fileConfigReader) {
|
|
|
1645
1978
|
return mapped;
|
|
1646
1979
|
}
|
|
1647
1980
|
const routes = new Hono();
|
|
1648
|
-
routes.get("/v1/models", (c) => {
|
|
1649
|
-
const exposure = getExposure();
|
|
1981
|
+
routes.get("/v1/models", async (c) => {
|
|
1982
|
+
const exposure = await getExposure();
|
|
1650
1983
|
return c.json(buildModelList(exposure.models));
|
|
1651
1984
|
});
|
|
1652
|
-
routes.get("/v1/models/*", (c) => {
|
|
1985
|
+
routes.get("/v1/models/*", async (c) => {
|
|
1653
1986
|
const rawPath = c.req.path;
|
|
1654
1987
|
if (!rawPath.startsWith("/v1/models/")) return c.json(modelNotFound(""), 404);
|
|
1655
1988
|
const modelIdEncoded = rawPath.slice(11);
|
|
1656
1989
|
if (modelIdEncoded.length === 0) return c.json(modelNotFound(""), 404);
|
|
1657
1990
|
const modelId = decodeURIComponent(modelIdEncoded);
|
|
1658
|
-
const resolved = resolveExposedModel(getExposure(), modelId);
|
|
1991
|
+
const resolved = resolveExposedModel(await getExposure(), modelId);
|
|
1659
1992
|
if (resolved === void 0) return c.json(modelNotFound(modelId), 404);
|
|
1660
1993
|
return c.json(toOpenAIModel(resolved));
|
|
1661
1994
|
});
|
|
@@ -1675,7 +2008,7 @@ function createRoutes(config, configReader = fileConfigReader) {
|
|
|
1675
2008
|
return c.json(invalidRequest(validation.message, validation.param ?? void 0), 400);
|
|
1676
2009
|
}
|
|
1677
2010
|
const request = validation.data;
|
|
1678
|
-
const resolved = resolveExposedModel(getExposure(), request.model);
|
|
2011
|
+
const resolved = resolveExposedModel(await getExposure(), request.model);
|
|
1679
2012
|
if (resolved === void 0) return c.json(modelNotFound(request.model), 404);
|
|
1680
2013
|
const model = resolved.model;
|
|
1681
2014
|
const canonicalModelId = resolved.canonicalId;
|
|
@@ -1689,7 +2022,8 @@ function createRoutes(config, configReader = fileConfigReader) {
|
|
|
1689
2022
|
context.tools = toolConversion.tools;
|
|
1690
2023
|
}
|
|
1691
2024
|
if (upstreamApiKey === void 0) {
|
|
1692
|
-
|
|
2025
|
+
const requestAuth = await getRequestAuth(model);
|
|
2026
|
+
if (!requestAuth.ok) return c.json(authenticationError(`Could not resolve request auth for provider '${model.provider}'. ${requestAuth.error}. Configure credentials via 'pi /login' or pass X-Pi-Upstream-Api-Key header.`), 401);
|
|
1693
2027
|
}
|
|
1694
2028
|
const completionOptions = {
|
|
1695
2029
|
upstreamApiKey,
|
package/extensions/proxy.ts
CHANGED
|
@@ -94,24 +94,24 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
94
94
|
// --- Model registry access (cached, refreshed per call) ---
|
|
95
95
|
|
|
96
96
|
const cachedAuth = AuthStorage.create();
|
|
97
|
-
const cachedRegistry =
|
|
98
|
-
const settingsManager = SettingsManager.create();
|
|
97
|
+
const cachedRegistry = ModelRegistry.create(cachedAuth);
|
|
98
|
+
const settingsManager = SettingsManager.create(process.cwd());
|
|
99
99
|
|
|
100
100
|
function getAvailableModels(): Model<Api>[] {
|
|
101
101
|
cachedRegistry.refresh();
|
|
102
102
|
return cachedRegistry.getAvailable();
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
function getEnabledModels(): readonly string[] | undefined {
|
|
106
|
-
settingsManager.reload();
|
|
105
|
+
async function getEnabledModels(): Promise<readonly string[] | undefined> {
|
|
106
|
+
await settingsManager.reload();
|
|
107
107
|
return settingsManager.getEnabledModels();
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function buildExposureConfig(): ModelExposureConfig {
|
|
110
|
+
async function buildExposureConfig(): Promise<ModelExposureConfig> {
|
|
111
111
|
return {
|
|
112
112
|
publicModelIdMode: config.publicModelIdMode,
|
|
113
113
|
modelExposureMode: config.modelExposureMode,
|
|
114
|
-
enabledModels: getEnabledModels(),
|
|
114
|
+
enabledModels: await getEnabledModels(),
|
|
115
115
|
customModels: config.customModels,
|
|
116
116
|
providerPrefixes: config.providerPrefixes,
|
|
117
117
|
};
|
|
@@ -137,7 +137,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
137
137
|
|
|
138
138
|
pi.on("session_start", async (_event, ctx) => {
|
|
139
139
|
config = loadConfigFromFile();
|
|
140
|
-
maybeAutoSyncZed(ctx);
|
|
140
|
+
await maybeAutoSyncZed(ctx);
|
|
141
141
|
await refreshStatus(ctx);
|
|
142
142
|
});
|
|
143
143
|
|
|
@@ -193,16 +193,16 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
193
193
|
await showStatus(ctx);
|
|
194
194
|
return;
|
|
195
195
|
case "verify":
|
|
196
|
-
verifyExposure(ctx);
|
|
196
|
+
await verifyExposure(ctx);
|
|
197
197
|
return;
|
|
198
198
|
case "models":
|
|
199
|
-
showModels(ctx);
|
|
199
|
+
await showModels(ctx);
|
|
200
200
|
return;
|
|
201
201
|
case "zed-sync":
|
|
202
|
-
handleZedSync(ctx, args);
|
|
202
|
+
await handleZedSync(ctx, args);
|
|
203
203
|
return;
|
|
204
204
|
case "show":
|
|
205
|
-
showConfig(ctx);
|
|
205
|
+
await showConfig(ctx);
|
|
206
206
|
return;
|
|
207
207
|
case "path":
|
|
208
208
|
ctx.ui.notify(getConfigPath(), "info");
|
|
@@ -460,7 +460,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
460
460
|
await refreshStatus(ctx);
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
function showConfig(ctx: ExtensionContext): void {
|
|
463
|
+
async function showConfig(ctx: ExtensionContext): Promise<void> {
|
|
464
464
|
config = loadConfigFromFile();
|
|
465
465
|
const authDisplay =
|
|
466
466
|
config.authToken.length > 0 ? `enabled (token: ${config.authToken})` : "disabled";
|
|
@@ -483,7 +483,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
483
483
|
];
|
|
484
484
|
|
|
485
485
|
if (config.modelExposureMode === "scoped") {
|
|
486
|
-
const enabledModels = getEnabledModels();
|
|
486
|
+
const enabledModels = await getEnabledModels();
|
|
487
487
|
if (enabledModels !== undefined && enabledModels.length > 0) {
|
|
488
488
|
exposureLines.push(`enabled: ${String(enabledModels.length)} pi model(s)`);
|
|
489
489
|
}
|
|
@@ -500,7 +500,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
500
500
|
|
|
501
501
|
// Public ID preview (first 5 exposed models)
|
|
502
502
|
const models = getAvailableModels();
|
|
503
|
-
const outcome = computeModelExposure(models, buildExposureConfig());
|
|
503
|
+
const outcome = computeModelExposure(models, await buildExposureConfig());
|
|
504
504
|
if (outcome.ok && outcome.models.length > 0) {
|
|
505
505
|
const preview = outcome.models.slice(0, 5).map((m) => m.publicId);
|
|
506
506
|
const suffix =
|
|
@@ -517,10 +517,10 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
517
517
|
|
|
518
518
|
// --- /proxy models ---
|
|
519
519
|
|
|
520
|
-
function showModels(ctx: ExtensionContext): void {
|
|
520
|
+
async function showModels(ctx: ExtensionContext): Promise<void> {
|
|
521
521
|
config = loadConfigFromFile();
|
|
522
522
|
const models = getAvailableModels();
|
|
523
|
-
const outcome = computeModelExposure(models, buildExposureConfig());
|
|
523
|
+
const outcome = computeModelExposure(models, await buildExposureConfig());
|
|
524
524
|
|
|
525
525
|
if (!outcome.ok) {
|
|
526
526
|
ctx.ui.notify(`Model exposure error: ${outcome.message}`, "warning");
|
|
@@ -562,7 +562,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
562
562
|
|
|
563
563
|
// --- /proxy verify ---
|
|
564
564
|
|
|
565
|
-
function verifyExposure(ctx: ExtensionContext): void {
|
|
565
|
+
async function verifyExposure(ctx: ExtensionContext): Promise<void> {
|
|
566
566
|
config = loadConfigFromFile();
|
|
567
567
|
const models = getAvailableModels();
|
|
568
568
|
const issues: string[] = [];
|
|
@@ -586,7 +586,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
586
586
|
}
|
|
587
587
|
|
|
588
588
|
// Run the full exposure computation to catch ID/prefix errors
|
|
589
|
-
const outcome = computeModelExposure(models, buildExposureConfig());
|
|
589
|
+
const outcome = computeModelExposure(models, await buildExposureConfig());
|
|
590
590
|
if (!outcome.ok) {
|
|
591
591
|
issues.push(outcome.message);
|
|
592
592
|
}
|
|
@@ -607,9 +607,9 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
607
607
|
/**
|
|
608
608
|
* Run Zed sync and return the result. Shared by the command and auto-sync.
|
|
609
609
|
*/
|
|
610
|
-
function runZedSync(dryRun: boolean): { ok: boolean; message: string } {
|
|
610
|
+
async function runZedSync(dryRun: boolean): Promise<{ ok: boolean; message: string }> {
|
|
611
611
|
const available = getAvailableModels();
|
|
612
|
-
const outcome = computeModelExposure(available, buildExposureConfig());
|
|
612
|
+
const outcome = computeModelExposure(available, await buildExposureConfig());
|
|
613
613
|
if (!outcome.ok) {
|
|
614
614
|
return { ok: false, message: `Model exposure error: ${outcome.message}` };
|
|
615
615
|
}
|
|
@@ -634,19 +634,19 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
634
634
|
return { ok: true, message: `${prefix}${result.summary} (${result.configPath})` };
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
-
function handleZedSync(ctx: ExtensionContext, args: string): void {
|
|
637
|
+
async function handleZedSync(ctx: ExtensionContext, args: string): Promise<void> {
|
|
638
638
|
config = loadConfigFromFile();
|
|
639
639
|
const dryRun = args.includes("--dry-run");
|
|
640
|
-
const result = runZedSync(dryRun);
|
|
640
|
+
const result = await runZedSync(dryRun);
|
|
641
641
|
ctx.ui.notify(`Zed sync: ${result.message}`, result.ok ? "info" : "error");
|
|
642
642
|
}
|
|
643
643
|
|
|
644
644
|
/**
|
|
645
645
|
* Trigger auto-sync to Zed if enabled. Called after config save.
|
|
646
646
|
*/
|
|
647
|
-
function maybeAutoSyncZed(ctx: ExtensionContext): void {
|
|
647
|
+
async function maybeAutoSyncZed(ctx: ExtensionContext): Promise<void> {
|
|
648
648
|
if (!config.zed.autoSync) return;
|
|
649
|
-
const result = runZedSync(false);
|
|
649
|
+
const result = await runZedSync(false);
|
|
650
650
|
if (result.ok) {
|
|
651
651
|
ctx.ui.notify(`Zed auto-sync: ${result.message}`, "info");
|
|
652
652
|
}
|
|
@@ -723,7 +723,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
723
723
|
|
|
724
724
|
// HACK: SettingsList has no public API for jumping to an index.
|
|
725
725
|
// Accesses private fields via bracket notation for provider jumping.
|
|
726
|
-
// Pinned to pi-tui behavior as of @mariozechner/pi-coding-agent ^0.
|
|
726
|
+
// Pinned to pi-tui behavior as of @mariozechner/pi-coding-agent ^0.63.1.
|
|
727
727
|
// Remove when SettingsList exposes a jumpTo/setSelectedIndex method.
|
|
728
728
|
|
|
729
729
|
// Isolated unsafe accessor for SettingsList private fields.
|
|
@@ -1106,7 +1106,7 @@ export default function proxyExtension(pi: ExtensionAPI): void {
|
|
|
1106
1106
|
settingsList.updateValue("customModels", customModelsDisplay());
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
|
-
maybeAutoSyncZed(ctx);
|
|
1109
|
+
void maybeAutoSyncZed(ctx);
|
|
1110
1110
|
tui.requestRender();
|
|
1111
1111
|
},
|
|
1112
1112
|
() => done(undefined),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@victor-software-house/pi-openai-proxy",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.4",
|
|
4
4
|
"description": "OpenAI-compatible HTTP proxy for pi's multi-provider model registry",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Victor Software House",
|
|
@@ -72,9 +72,8 @@
|
|
|
72
72
|
"prepublishOnly": "bun test && bun run build"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@mariozechner/pi-ai": "^0.
|
|
76
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
77
|
-
"@sinclair/typebox": "^0.34.0",
|
|
75
|
+
"@mariozechner/pi-ai": "^0.73.0",
|
|
76
|
+
"@mariozechner/pi-coding-agent": "^0.73.0",
|
|
78
77
|
"citty": "^0.1.6",
|
|
79
78
|
"hono": "^4.12.8",
|
|
80
79
|
"jsonc-parser": "^3.3.1",
|
|
@@ -98,6 +97,7 @@
|
|
|
98
97
|
"oxlint-tsgolint": "^0.17.1",
|
|
99
98
|
"semantic-release": "^25.0.3",
|
|
100
99
|
"tsdown": "^0.21.4",
|
|
100
|
+
"typebox": "^1.1.38",
|
|
101
101
|
"typescript": "^5.9.3"
|
|
102
102
|
}
|
|
103
103
|
}
|