@victor-software-house/pi-openai-proxy 4.3.0 → 4.3.1

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.d.mts CHANGED
@@ -50,10 +50,12 @@ interface ProxyConfig {
50
50
  readonly providerPrefixes: Readonly<Record<string, string>>;
51
51
  }
52
52
  declare const DEFAULT_CONFIG: Readonly<ProxyConfig>;
53
+ declare function isPublicModelIdMode(value: string): value is PublicModelIdMode;
54
+ declare function isModelExposureMode(value: string): value is ModelExposureMode;
53
55
  declare function normalizeConfig(raw: unknown): ProxyConfig;
54
56
  declare function getConfigPath(): string;
55
57
  declare function loadConfigFromFile(): ProxyConfig;
56
58
  declare function saveConfigToFile(config: ProxyConfig): void;
57
59
  declare function configToEnv(config: ProxyConfig): Record<string, string>;
58
60
  //#endregion
59
- export { DEFAULT_CONFIG, ModelExposureMode, ProxyConfig, PublicModelIdMode, configToEnv, getConfigPath, loadConfigFromFile, normalizeConfig, saveConfigToFile };
61
+ export { DEFAULT_CONFIG, ModelExposureMode, ProxyConfig, PublicModelIdMode, configToEnv, getConfigPath, isModelExposureMode, isPublicModelIdMode, loadConfigFromFile, normalizeConfig, saveConfigToFile };
package/dist/config.mjs CHANGED
@@ -1,118 +1,3 @@
1
1
  #!/usr/bin/env bun
2
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
- import { dirname, resolve } from "node:path";
4
- //#region src/config/schema.ts
5
- /**
6
- * Proxy configuration schema -- single source of truth.
7
- *
8
- * Used by both the proxy server and the pi extension.
9
- * The server reads the JSON config file as defaults, with env vars and CLI args as overrides.
10
- * The pi extension reads and writes the JSON config file via the /proxy config panel.
11
- */
12
- const DEFAULT_CONFIG = {
13
- host: "127.0.0.1",
14
- port: 4141,
15
- authToken: "",
16
- remoteImages: false,
17
- maxBodySizeMb: 50,
18
- upstreamTimeoutSec: 120,
19
- lifetime: "detached",
20
- publicModelIdMode: "collision-prefixed",
21
- modelExposureMode: "scoped",
22
- scopedProviders: [],
23
- customModels: [],
24
- providerPrefixes: {}
25
- };
26
- function isRecord(value) {
27
- return value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value);
28
- }
29
- function clampInt(raw, min, max, fallback) {
30
- if (typeof raw !== "number" || !Number.isFinite(raw)) return fallback;
31
- return Math.max(min, Math.min(max, Math.round(raw)));
32
- }
33
- const VALID_PUBLIC_ID_MODES = new Set([
34
- "collision-prefixed",
35
- "universal",
36
- "always-prefixed"
37
- ]);
38
- function isPublicModelIdMode(value) {
39
- return VALID_PUBLIC_ID_MODES.has(value);
40
- }
41
- const VALID_EXPOSURE_MODES = new Set([
42
- "all",
43
- "scoped",
44
- "custom"
45
- ]);
46
- function isModelExposureMode(value) {
47
- return VALID_EXPOSURE_MODES.has(value);
48
- }
49
- function normalizeStringArray(raw) {
50
- if (!Array.isArray(raw)) return [];
51
- const result = [];
52
- for (const item of raw) if (typeof item === "string" && item.length > 0) result.push(item);
53
- return result;
54
- }
55
- function normalizeStringRecord(raw) {
56
- if (!isRecord(raw)) return {};
57
- const result = {};
58
- for (const [key, val] of Object.entries(raw)) if (typeof val === "string" && val.length > 0) result[key] = val;
59
- return result;
60
- }
61
- function normalizeConfig(raw) {
62
- const v = isRecord(raw) ? raw : {};
63
- const rawHost = v["host"];
64
- const rawAuthToken = v["authToken"];
65
- const rawRemoteImages = v["remoteImages"];
66
- const rawPublicIdMode = v["publicModelIdMode"];
67
- const rawExposureMode = v["modelExposureMode"];
68
- return {
69
- host: typeof rawHost === "string" && rawHost.length > 0 ? rawHost : DEFAULT_CONFIG.host,
70
- port: clampInt(v["port"], 1, 65535, DEFAULT_CONFIG.port),
71
- authToken: typeof rawAuthToken === "string" ? rawAuthToken : DEFAULT_CONFIG.authToken,
72
- remoteImages: typeof rawRemoteImages === "boolean" ? rawRemoteImages : DEFAULT_CONFIG.remoteImages,
73
- maxBodySizeMb: clampInt(v["maxBodySizeMb"], 1, 500, DEFAULT_CONFIG.maxBodySizeMb),
74
- upstreamTimeoutSec: clampInt(v["upstreamTimeoutSec"], 5, 600, DEFAULT_CONFIG.upstreamTimeoutSec),
75
- lifetime: v["lifetime"] === "session" ? "session" : "detached",
76
- publicModelIdMode: typeof rawPublicIdMode === "string" && isPublicModelIdMode(rawPublicIdMode) ? rawPublicIdMode : DEFAULT_CONFIG.publicModelIdMode,
77
- modelExposureMode: typeof rawExposureMode === "string" && isModelExposureMode(rawExposureMode) ? rawExposureMode : DEFAULT_CONFIG.modelExposureMode,
78
- scopedProviders: normalizeStringArray(v["scopedProviders"]),
79
- customModels: normalizeStringArray(v["customModels"]),
80
- providerPrefixes: normalizeStringRecord(v["providerPrefixes"])
81
- };
82
- }
83
- function getConfigPath() {
84
- return resolve(process.env["PI_CODING_AGENT_DIR"] ?? resolve(process.env["HOME"] ?? "~", ".pi", "agent"), "proxy-config.json");
85
- }
86
- function loadConfigFromFile() {
87
- const p = getConfigPath();
88
- if (!existsSync(p)) return { ...DEFAULT_CONFIG };
89
- try {
90
- return normalizeConfig(JSON.parse(readFileSync(p, "utf-8")));
91
- } catch {
92
- return { ...DEFAULT_CONFIG };
93
- }
94
- }
95
- function saveConfigToFile(config) {
96
- const p = getConfigPath();
97
- const normalized = normalizeConfig(config);
98
- const tmp = `${p}.tmp`;
99
- try {
100
- mkdirSync(dirname(p), { recursive: true });
101
- writeFileSync(tmp, `${JSON.stringify(normalized, null, " ")}\n`, "utf-8");
102
- renameSync(tmp, p);
103
- } catch {
104
- if (existsSync(tmp)) unlinkSync(tmp);
105
- }
106
- }
107
- function configToEnv(config) {
108
- const env = {};
109
- env["PI_PROXY_HOST"] = config.host;
110
- env["PI_PROXY_PORT"] = String(config.port);
111
- if (config.authToken.length > 0) env["PI_PROXY_AUTH_TOKEN"] = config.authToken;
112
- env["PI_PROXY_REMOTE_IMAGES"] = String(config.remoteImages);
113
- env["PI_PROXY_MAX_BODY_SIZE"] = String(config.maxBodySizeMb * 1024 * 1024);
114
- env["PI_PROXY_UPSTREAM_TIMEOUT_MS"] = String(config.upstreamTimeoutSec * 1e3);
115
- return env;
116
- }
117
- //#endregion
118
- export { DEFAULT_CONFIG, configToEnv, getConfigPath, loadConfigFromFile, normalizeConfig, saveConfigToFile };
2
+ import { a as isPublicModelIdMode, c as saveConfigToFile, i as isModelExposureMode, n as configToEnv, o as loadConfigFromFile, r as getConfigPath, s as normalizeConfig, t as DEFAULT_CONFIG } from "./schema-x6mps-hM.mjs";
3
+ export { DEFAULT_CONFIG, configToEnv, getConfigPath, isModelExposureMode, isPublicModelIdMode, loadConfigFromFile, normalizeConfig, saveConfigToFile };
package/dist/exposure.mjs CHANGED
@@ -2,7 +2,11 @@
2
2
  //#region src/openai/model-exposure.ts
3
3
  function filterExposedModels(available, allRegistered, config) {
4
4
  switch (config.modelExposureMode) {
5
- case "scoped": return [...available];
5
+ case "scoped": {
6
+ if (config.scopedProviders.length === 0) return [...available];
7
+ const allowed = new Set(config.scopedProviders);
8
+ return available.filter((m) => allowed.has(m.provider));
9
+ }
6
10
  case "all": return [...allRegistered];
7
11
  case "custom": {
8
12
  const allowed = new Set(config.customModels);
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { getConfigPath, loadConfigFromFile } from "./config.mjs";
2
+ import { l as isRecord, o as loadConfigFromFile, r as getConfigPath } from "./schema-x6mps-hM.mjs";
3
3
  import { computeModelExposure, resolveExposedModel } from "./exposure.mjs";
4
4
  import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
5
5
  import { randomBytes } from "node:crypto";
@@ -476,6 +476,8 @@ function parseAndValidateDataUri(uri) {
476
476
  }
477
477
  //#endregion
478
478
  //#region src/openai/models.ts
479
+ /** Unix timestamp (seconds) from when the module was first loaded. */
480
+ const MODULE_CREATED = Math.floor(Date.now() / 1e3);
479
481
  /**
480
482
  * Convert an ExposedModel to an OpenAI model object.
481
483
  */
@@ -483,7 +485,7 @@ function toOpenAIModel(exposed) {
483
485
  return {
484
486
  id: exposed.publicId,
485
487
  object: "model",
486
- created: 0,
488
+ created: MODULE_CREATED,
487
489
  owned_by: exposed.provider
488
490
  };
489
491
  }
@@ -549,6 +551,7 @@ function buildChatCompletion(requestId, canonicalModelId, message) {
549
551
  object: "chat.completion",
550
552
  created: Math.floor(Date.now() / 1e3),
551
553
  model: canonicalModelId,
554
+ system_fingerprint: null,
552
555
  choices: [{
553
556
  index: 0,
554
557
  message: messageBody,
@@ -724,9 +727,31 @@ async function* streamToSSE(events, requestId, model, includeUsage) {
724
727
  }
725
728
  //#endregion
726
729
  //#region src/openai/json-schema-to-typebox.ts
727
- function isRecord$2(value) {
728
- return value !== null && typeof value === "object" && !Array.isArray(value);
729
- }
730
+ /**
731
+ * JSON Schema -> TypeBox conversion for OpenAI function tool parameters.
732
+ *
733
+ * Phase 2 contract:
734
+ * - Support a documented subset of JSON Schema only
735
+ * - Reject unsupported schema constructs with ConversionError
736
+ * - Do not silently downgrade complex schemas
737
+ *
738
+ * Supported subset:
739
+ * - type: object, string, number, integer, boolean, array, null
740
+ * - properties, required
741
+ * - enum (string enums)
742
+ * - arrays with supported item schema
743
+ * - nullable via type: [T, "null"]
744
+ * - anyOf for nullable types and simple unions (max 10 branches)
745
+ * - description on any schema node
746
+ *
747
+ * Rejected:
748
+ * - $ref
749
+ * - oneOf, allOf
750
+ * - recursive schemas
751
+ * - additionalProperties as a schema (boolean true/false allowed)
752
+ * - patternProperties
753
+ * - if/then/else
754
+ */
730
755
  /**
731
756
  * Unsupported JSON Schema keywords that we reject explicitly.
732
757
  * Note: `anyOf` is handled separately for common patterns (nullable types, simple unions).
@@ -747,7 +772,7 @@ const REJECTED_KEYWORDS = [
747
772
  * Returns a ConversionError for unsupported constructs.
748
773
  */
749
774
  function jsonSchemaToTypebox(schema, path = "") {
750
- if (!isRecord$2(schema)) return {
775
+ if (!isRecord(schema)) return {
751
776
  ok: false,
752
777
  message: "Schema must be an object",
753
778
  path
@@ -894,7 +919,7 @@ function convertObject(schema, path, opts) {
894
919
  ok: true,
895
920
  schema: Type.Object({}, opts)
896
921
  };
897
- if (!isRecord$2(rawProperties)) return {
922
+ if (!isRecord(rawProperties)) return {
898
923
  ok: false,
899
924
  message: `'properties' must be an object at ${path || "root"}`,
900
925
  path
@@ -1123,16 +1148,13 @@ const rejectedFields = [
1123
1148
  *
1124
1149
  * Phase 2 contract: unknown fields -> 422, rejected fields -> 422
1125
1150
  */
1126
- function isRecord$1(value) {
1127
- return value !== null && typeof value === "object" && !Array.isArray(value);
1128
- }
1129
1151
  /**
1130
1152
  * Validate a raw request body against the Phase 1 schema.
1131
1153
  *
1132
1154
  * Also checks for known rejected fields to give friendly errors.
1133
1155
  */
1134
1156
  function validateChatRequest(body) {
1135
- if (isRecord$1(body)) {
1157
+ if (isRecord(body)) {
1136
1158
  for (const field of rejectedFields) if (body[field] !== void 0) return {
1137
1159
  ok: false,
1138
1160
  status: 422,
@@ -1170,9 +1192,6 @@ function validateChatRequest(body) {
1170
1192
  }
1171
1193
  //#endregion
1172
1194
  //#region src/pi/complete.ts
1173
- function isRecord(value) {
1174
- return value !== null && typeof value === "object" && !Array.isArray(value);
1175
- }
1176
1195
  /**
1177
1196
  * Map OpenAI reasoning_effort to pi ThinkingLevel.
1178
1197
  *
@@ -1276,7 +1295,7 @@ async function piComplete(model, context, request, options) {
1276
1295
  return completeSimple(model, context, await buildStreamOptions(model, request, options));
1277
1296
  }
1278
1297
  /**
1279
- * Streaming completion: returns an async iterable of events.
1298
+ * Streaming completion: returns an event stream with abort capability.
1280
1299
  */
1281
1300
  async function piStream(model, context, request, options) {
1282
1301
  return streamSimple(model, context, await buildStreamOptions(model, request, options));
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bun
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ //#region src/utils/guards.ts
5
+ /**
6
+ * Shared type guard utilities.
7
+ */
8
+ /**
9
+ * Narrow `unknown` to `Record<string, unknown>`.
10
+ * Rejects null, undefined, and arrays.
11
+ */
12
+ function isRecord(value) {
13
+ return value !== null && typeof value === "object" && !Array.isArray(value);
14
+ }
15
+ //#endregion
16
+ //#region src/config/schema.ts
17
+ /**
18
+ * Proxy configuration schema -- single source of truth.
19
+ *
20
+ * Used by both the proxy server and the pi extension.
21
+ * The server reads the JSON config file as defaults, with env vars and CLI args as overrides.
22
+ * The pi extension reads and writes the JSON config file via the /proxy config panel.
23
+ */
24
+ const DEFAULT_CONFIG = {
25
+ host: "127.0.0.1",
26
+ port: 4141,
27
+ authToken: "",
28
+ remoteImages: false,
29
+ maxBodySizeMb: 50,
30
+ upstreamTimeoutSec: 120,
31
+ lifetime: "detached",
32
+ publicModelIdMode: "collision-prefixed",
33
+ modelExposureMode: "scoped",
34
+ scopedProviders: [],
35
+ customModels: [],
36
+ providerPrefixes: {}
37
+ };
38
+ function clampInt(raw, min, max, fallback) {
39
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return fallback;
40
+ return Math.max(min, Math.min(max, Math.round(raw)));
41
+ }
42
+ const VALID_PUBLIC_ID_MODES = new Set([
43
+ "collision-prefixed",
44
+ "universal",
45
+ "always-prefixed"
46
+ ]);
47
+ function isPublicModelIdMode(value) {
48
+ return VALID_PUBLIC_ID_MODES.has(value);
49
+ }
50
+ const VALID_EXPOSURE_MODES = new Set([
51
+ "all",
52
+ "scoped",
53
+ "custom"
54
+ ]);
55
+ function isModelExposureMode(value) {
56
+ return VALID_EXPOSURE_MODES.has(value);
57
+ }
58
+ function normalizeStringArray(raw) {
59
+ if (!Array.isArray(raw)) return [];
60
+ const result = [];
61
+ for (const item of raw) if (typeof item === "string" && item.length > 0) result.push(item);
62
+ return result;
63
+ }
64
+ function normalizeStringRecord(raw) {
65
+ if (!isRecord(raw)) return {};
66
+ const result = {};
67
+ for (const [key, val] of Object.entries(raw)) if (typeof val === "string" && val.length > 0) result[key] = val;
68
+ return result;
69
+ }
70
+ function normalizeConfig(raw) {
71
+ const v = isRecord(raw) ? raw : {};
72
+ const rawHost = v["host"];
73
+ const rawAuthToken = v["authToken"];
74
+ const rawRemoteImages = v["remoteImages"];
75
+ const rawPublicIdMode = v["publicModelIdMode"];
76
+ const rawExposureMode = v["modelExposureMode"];
77
+ return {
78
+ host: typeof rawHost === "string" && rawHost.length > 0 ? rawHost : DEFAULT_CONFIG.host,
79
+ port: clampInt(v["port"], 1, 65535, DEFAULT_CONFIG.port),
80
+ authToken: typeof rawAuthToken === "string" ? rawAuthToken : DEFAULT_CONFIG.authToken,
81
+ remoteImages: typeof rawRemoteImages === "boolean" ? rawRemoteImages : DEFAULT_CONFIG.remoteImages,
82
+ maxBodySizeMb: clampInt(v["maxBodySizeMb"], 1, 500, DEFAULT_CONFIG.maxBodySizeMb),
83
+ upstreamTimeoutSec: clampInt(v["upstreamTimeoutSec"], 5, 600, DEFAULT_CONFIG.upstreamTimeoutSec),
84
+ lifetime: v["lifetime"] === "session" ? "session" : "detached",
85
+ publicModelIdMode: typeof rawPublicIdMode === "string" && isPublicModelIdMode(rawPublicIdMode) ? rawPublicIdMode : DEFAULT_CONFIG.publicModelIdMode,
86
+ modelExposureMode: typeof rawExposureMode === "string" && isModelExposureMode(rawExposureMode) ? rawExposureMode : DEFAULT_CONFIG.modelExposureMode,
87
+ scopedProviders: normalizeStringArray(v["scopedProviders"]),
88
+ customModels: normalizeStringArray(v["customModels"]),
89
+ providerPrefixes: normalizeStringRecord(v["providerPrefixes"])
90
+ };
91
+ }
92
+ function getConfigPath() {
93
+ return resolve(process.env.PI_CODING_AGENT_DIR ?? resolve(process.env.HOME ?? "~", ".pi", "agent"), "proxy-config.json");
94
+ }
95
+ function loadConfigFromFile() {
96
+ const p = getConfigPath();
97
+ if (!existsSync(p)) return { ...DEFAULT_CONFIG };
98
+ try {
99
+ return normalizeConfig(JSON.parse(readFileSync(p, "utf-8")));
100
+ } catch {
101
+ return { ...DEFAULT_CONFIG };
102
+ }
103
+ }
104
+ function saveConfigToFile(config) {
105
+ const p = getConfigPath();
106
+ const normalized = normalizeConfig(config);
107
+ const tmp = `${p}.tmp`;
108
+ try {
109
+ mkdirSync(dirname(p), { recursive: true });
110
+ writeFileSync(tmp, `${JSON.stringify(normalized, null, " ")}\n`, "utf-8");
111
+ renameSync(tmp, p);
112
+ } catch {
113
+ if (existsSync(tmp)) unlinkSync(tmp);
114
+ }
115
+ }
116
+ function configToEnv(config) {
117
+ const env = {};
118
+ env["PI_PROXY_HOST"] = config.host;
119
+ env["PI_PROXY_PORT"] = String(config.port);
120
+ if (config.authToken.length > 0) env["PI_PROXY_AUTH_TOKEN"] = config.authToken;
121
+ env["PI_PROXY_REMOTE_IMAGES"] = String(config.remoteImages);
122
+ env["PI_PROXY_MAX_BODY_SIZE"] = String(config.maxBodySizeMb * 1024 * 1024);
123
+ env["PI_PROXY_UPSTREAM_TIMEOUT_MS"] = String(config.upstreamTimeoutSec * 1e3);
124
+ return env;
125
+ }
126
+ //#endregion
127
+ export { isPublicModelIdMode as a, saveConfigToFile as c, isModelExposureMode as i, isRecord as l, configToEnv as n, loadConfigFromFile as o, getConfigPath as r, normalizeConfig as s, DEFAULT_CONFIG as t };
@@ -76,18 +76,19 @@ export default function proxyExtension(pi: ExtensionAPI): void {
76
76
  const extensionDir = dirname(fileURLToPath(import.meta.url));
77
77
  const packageRoot = resolve(extensionDir, "..");
78
78
 
79
- // --- Model registry access (for verify/show/selectors) ---
79
+ // --- Model registry access (cached, refreshed per call) ---
80
+
81
+ const cachedAuth = AuthStorage.create();
82
+ const cachedRegistry = new ModelRegistry(cachedAuth);
80
83
 
81
84
  function getAvailableModels(): Model<Api>[] {
82
- const auth = AuthStorage.create();
83
- const registry = new ModelRegistry(auth);
84
- return registry.getAvailable();
85
+ cachedRegistry.refresh();
86
+ return cachedRegistry.getAvailable();
85
87
  }
86
88
 
87
89
  function getAllRegisteredModels(): Model<Api>[] {
88
- const auth = AuthStorage.create();
89
- const registry = new ModelRegistry(auth);
90
- return registry.getAll();
90
+ cachedRegistry.refresh();
91
+ return cachedRegistry.getAll();
91
92
  }
92
93
 
93
94
  function buildExposureConfig(): ModelExposureConfig {
@@ -256,8 +257,13 @@ export default function proxyExtension(pi: ExtensionAPI): void {
256
257
 
257
258
  async function probe(): Promise<RuntimeStatus> {
258
259
  try {
260
+ const headers: Record<string, string> = {};
261
+ if (config.authToken.length > 0) {
262
+ headers["authorization"] = `Bearer ${config.authToken}`;
263
+ }
259
264
  const res = await fetch(`${proxyUrl()}/v1/models`, {
260
265
  signal: AbortSignal.timeout(2000),
266
+ headers,
261
267
  });
262
268
  if (res.ok) {
263
269
  const body = (await res.json()) as { data?: unknown[] };
@@ -639,8 +645,10 @@ export default function proxyExtension(pi: ExtensionAPI): void {
639
645
  { enableSearch: true },
640
646
  );
641
647
 
642
- // SettingsList has no public selectedIndex setter.
643
- // Access internals via bracket notation for provider jumping.
648
+ // HACK: SettingsList has no public API for jumping to an index.
649
+ // Accesses private fields via bracket notation for provider jumping.
650
+ // Pinned to pi-tui behavior as of @mariozechner/pi-coding-agent ^0.62.0.
651
+ // Remove when SettingsList exposes a jumpTo/setSelectedIndex method.
644
652
  function jumpProvider(direction: "prev" | "next"): void {
645
653
  const sl = list as unknown as Record<string, unknown>;
646
654
  const idx = sl["selectedIndex"] as number;
@@ -837,16 +845,14 @@ export default function proxyExtension(pi: ExtensionAPI): void {
837
845
  }
838
846
  }
839
847
 
840
- const VALID_ID_MODES = new Set<string>(["collision-prefixed", "universal", "always-prefixed"]);
841
-
848
+ // Local type guards the extension resolves against the built dist, so it
849
+ // cannot import these from the source config module during development.
842
850
  function isPublicModelIdMode(v: string): v is PublicModelIdMode {
843
- return VALID_ID_MODES.has(v);
851
+ return v === "collision-prefixed" || v === "universal" || v === "always-prefixed";
844
852
  }
845
853
 
846
- const VALID_EXPOSURE_MODES = new Set<string>(["all", "scoped", "custom"]);
847
-
848
854
  function isModelExposureMode(v: string): v is ModelExposureMode {
849
- return VALID_EXPOSURE_MODES.has(v);
855
+ return v === "all" || v === "scoped" || v === "custom";
850
856
  }
851
857
 
852
858
  function applySetting(id: string, value: string): void {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@victor-software-house/pi-openai-proxy",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "OpenAI-compatible HTTP proxy for pi's multi-provider model registry",
5
5
  "license": "MIT",
6
6
  "author": "Victor Software House",
@@ -63,6 +63,7 @@
63
63
  "format": "biome format --write .",
64
64
  "test": "bun test",
65
65
  "test:ci": "bun test test/unit/ test/integration/",
66
+ "prepare": "tsdown",
66
67
  "prepack": "bun run build",
67
68
  "prepublishOnly": "bun test && bun run build"
68
69
  },