@byfriends/oauth 0.1.0 → 0.1.2

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.d.mts CHANGED
@@ -40,6 +40,15 @@ declare class ProviderApiError extends Error {
40
40
  constructor(message: string, status: number);
41
41
  }
42
42
  declare function fetchModels(baseUrl: string, apiKey: string, fetchImpl?: typeof fetch, signal?: AbortSignal): Promise<ModelInfo[]>;
43
+ /**
44
+ * Lists models from a provider using its native wire-type endpoint. Dispatches
45
+ * per `type` so each protocol gets its correct auth header and response shape.
46
+ * `openai-completions` and `openai_responses` share the OpenAI-compatible
47
+ * `/models` endpoint; `anthropic` uses its native `x-api-key` endpoint with
48
+ * pagination. Other types have dedicated native fetchers (added in
49
+ * later slices).
50
+ */
51
+ declare function fetchModelsByType(type: string, baseUrl: string, apiKey: string, fetchImpl?: typeof fetch, signal?: AbortSignal): Promise<ModelInfo[]>;
43
52
  declare function filterModelsByPrefix(models: ModelInfo[], prefixes?: readonly string[] | undefined): ModelInfo[];
44
53
  declare function capabilitiesForModel(model: ModelInfo): string[] | undefined;
45
54
  interface ApplyProviderResult {
@@ -48,6 +57,7 @@ interface ApplyProviderResult {
48
57
  }
49
58
  declare function applyProviderConfig(config: ConfigShape, options: {
50
59
  readonly name: string;
60
+ readonly type?: string | undefined;
51
61
  readonly baseUrl: string;
52
62
  readonly apiKey: string;
53
63
  readonly models: readonly ModelInfo[];
@@ -56,4 +66,4 @@ declare function applyProviderConfig(config: ConfigShape, options: {
56
66
  }): ApplyProviderResult;
57
67
  declare function removeProviderConfig(config: ConfigShape, providerName: string): void;
58
68
  //#endregion
59
- export { type ApplyProviderResult, type ConfigShape, type ModelAlias, type ModelInfo, ProviderApiError, type ProviderConfig, type ServicesConfig, applyProviderConfig, capabilitiesForModel, fetchModels, filterModelsByPrefix, removeProviderConfig };
69
+ export { type ApplyProviderResult, type ConfigShape, type ModelAlias, type ModelInfo, ProviderApiError, type ProviderConfig, type ServicesConfig, applyProviderConfig, capabilitiesForModel, fetchModels, fetchModelsByType, filterModelsByPrefix, removeProviderConfig };
package/dist/index.mjs CHANGED
@@ -103,6 +103,9 @@ function firstNonEmptyString(source, keys) {
103
103
  }
104
104
  }
105
105
  async function fetchModels(baseUrl, apiKey, fetchImpl = fetch, signal) {
106
+ return fetchOpenAICompatModels(baseUrl, apiKey, fetchImpl, signal);
107
+ }
108
+ async function fetchOpenAICompatModels(baseUrl, apiKey, fetchImpl = fetch, signal) {
106
109
  const res = await fetchImpl(`${baseUrl.replace(/\/+$/, "")}/models`, {
107
110
  headers: {
108
111
  Authorization: `Bearer ${apiKey}`,
@@ -115,6 +118,70 @@ async function fetchModels(baseUrl, apiKey, fetchImpl = fetch, signal) {
115
118
  if (!isRecord(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
116
119
  return payload["data"].map((item) => toModelInfo(item)).filter((item) => item !== void 0);
117
120
  }
121
+ /**
122
+ * Lists models from a provider using its native wire-type endpoint. Dispatches
123
+ * per `type` so each protocol gets its correct auth header and response shape.
124
+ * `openai-completions` and `openai_responses` share the OpenAI-compatible
125
+ * `/models` endpoint; `anthropic` uses its native `x-api-key` endpoint with
126
+ * pagination. Other types have dedicated native fetchers (added in
127
+ * later slices).
128
+ */
129
+ async function fetchModelsByType(type, baseUrl, apiKey, fetchImpl = fetch, signal) {
130
+ switch (type) {
131
+ case "openai-completions":
132
+ case "openai_responses": return fetchOpenAICompatModels(baseUrl, apiKey, fetchImpl, signal);
133
+ case "anthropic": return fetchAnthropicModels(baseUrl, apiKey, fetchImpl, signal);
134
+ default: throw new Error(`fetchModelsByType: unsupported provider type "${type}".`);
135
+ }
136
+ }
137
+ /**
138
+ * Anthropic native `/v1/models` listing. Uses `x-api-key` + `anthropic-version`
139
+ * headers (not Bearer). Response is paginated via `has_more` + `last_id`;
140
+ * follow pages by passing `?after_id=<last_id>`. Defensive guards: a page that
141
+ * claims `has_more` but omits `last_id` stops pagination (returns what we have)
142
+ * rather than looping forever; a hard cap (10 pages) also bounds the loop.
143
+ */
144
+ async function fetchAnthropicModels(baseUrl, apiKey, fetchImpl = fetch, signal) {
145
+ const base = baseUrl.replace(/\/+$/, "");
146
+ const ANTHROPIC_VERSION = "2023-06-01";
147
+ const MAX_PAGES = 10;
148
+ const collected = [];
149
+ let afterId;
150
+ for (let page = 0; page < MAX_PAGES; page += 1) {
151
+ const res = await fetchImpl(afterId === void 0 ? `${base}/models` : `${base}/models?after_id=${encodeURIComponent(afterId)}`, {
152
+ headers: {
153
+ "x-api-key": apiKey,
154
+ "anthropic-version": ANTHROPIC_VERSION,
155
+ Accept: "application/json"
156
+ },
157
+ signal
158
+ });
159
+ if (!res.ok) throw new ProviderApiError(await readApiErrorMessage(res, `Failed to list models (HTTP ${res.status}).`), res.status);
160
+ const payload = await res.json();
161
+ if (!isRecord(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
162
+ for (const item of payload["data"]) {
163
+ const info = anthropicModelToInfo(item);
164
+ if (info !== void 0) collected.push(info);
165
+ }
166
+ const hasMore = payload["has_more"] === true;
167
+ const lastId = typeof payload["last_id"] === "string" ? payload["last_id"] : void 0;
168
+ if (!hasMore || lastId === void 0 || lastId.length === 0) break;
169
+ afterId = lastId;
170
+ }
171
+ return collected;
172
+ }
173
+ function anthropicModelToInfo(item) {
174
+ if (!isRecord(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
175
+ const displayName = item["display_name"];
176
+ return {
177
+ id: item["id"],
178
+ contextLength: 2e5,
179
+ supportsReasoning: true,
180
+ supportsImageIn: true,
181
+ supportsVideoIn: false,
182
+ displayName: typeof displayName === "string" && displayName.length > 0 ? displayName : void 0
183
+ };
184
+ }
118
185
  function filterModelsByPrefix(models, prefixes) {
119
186
  if (!prefixes || prefixes.length === 0) return models;
120
187
  return models.filter((m) => prefixes.some((p) => m.id.startsWith(p)));
@@ -132,7 +199,7 @@ function applyProviderConfig(config, options) {
132
199
  const providerKey = options.name;
133
200
  const modelKey = `${providerKey}/${options.selectedModel.id}`;
134
201
  config.providers[providerKey] = {
135
- type: "openai-completions",
202
+ type: options.type ?? "openai-completions",
136
203
  baseUrl: options.baseUrl,
137
204
  apiKey: options.apiKey,
138
205
  thinkingEffortKey: options.selectedModel.reasoningEffortKey
@@ -171,4 +238,4 @@ function removeProviderConfig(config, providerName) {
171
238
  if (config["defaultProvider"] === providerName) config["defaultProvider"] = void 0;
172
239
  }
173
240
  //#endregion
174
- export { ProviderApiError, applyProviderConfig, capabilitiesForModel, fetchModels, filterModelsByPrefix, removeProviderConfig };
241
+ export { ProviderApiError, applyProviderConfig, capabilitiesForModel, fetchModels, fetchModelsByType, filterModelsByPrefix, removeProviderConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byfriends/oauth",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Custom OAuth provider configuration for BYF",
5
5
  "license": "Proprietary",
6
6
  "author": "ByronFinn",