open-agents-ai 0.184.54 → 0.184.55

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.
Files changed (2) hide show
  1. package/dist/index.js +143 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -62035,6 +62035,102 @@ function getVersion3() {
62035
62035
  }
62036
62036
  return "0.0.0";
62037
62037
  }
62038
+ async function refreshEndpointRegistry() {
62039
+ endpointRegistry.length = 0;
62040
+ modelRouteMap.clear();
62041
+ const config = loadConfig();
62042
+ const primaryLabel = (() => {
62043
+ if (config.backendUrl.includes("127.0.0.1") || config.backendUrl.includes("localhost"))
62044
+ return "local";
62045
+ try {
62046
+ return new URL(config.backendUrl).hostname.split(".")[0];
62047
+ } catch {
62048
+ return "primary";
62049
+ }
62050
+ })();
62051
+ endpointRegistry.push({
62052
+ label: primaryLabel,
62053
+ url: config.backendUrl,
62054
+ type: config.backendType || "ollama",
62055
+ authKey: config.apiKey
62056
+ });
62057
+ try {
62058
+ const resp = await fetch("https://openagents.nexus/api/v1/sponsors", {
62059
+ signal: AbortSignal.timeout(5e3)
62060
+ });
62061
+ if (resp.ok) {
62062
+ const data = await resp.json();
62063
+ for (const sp of (data.sponsors || []).filter((s) => s.status === "active" && s.tunnelUrl)) {
62064
+ if (sp.tunnelUrl === config.backendUrl)
62065
+ continue;
62066
+ const label = sp.name.replace(/[^a-zA-Z0-9]/g, "").slice(0, 12).toLowerCase() || "sponsor";
62067
+ endpointRegistry.push({
62068
+ label,
62069
+ url: sp.tunnelUrl,
62070
+ type: "vllm",
62071
+ authKey: sp.authKey
62072
+ });
62073
+ }
62074
+ }
62075
+ } catch {
62076
+ }
62077
+ }
62078
+ async function fetchAggregatedModels() {
62079
+ const allModels = [];
62080
+ modelRouteMap.clear();
62081
+ const fetches = endpointRegistry.map(async (ep) => {
62082
+ try {
62083
+ const isOllama = ep.type === "ollama";
62084
+ const modelsPath = isOllama ? "/api/tags" : "/v1/models";
62085
+ const result = await ollamaRequest(ep.url, modelsPath, "GET");
62086
+ if (result.status !== 200)
62087
+ return;
62088
+ const body = JSON.parse(result.body);
62089
+ if (isOllama) {
62090
+ const models = body.models ?? [];
62091
+ for (const m of models) {
62092
+ const prefixed = `${ep.label}/${m.name}`;
62093
+ modelRouteMap.set(prefixed, { endpoint: ep, originalId: m.name });
62094
+ allModels.push({
62095
+ id: prefixed,
62096
+ object: "model",
62097
+ created: m.modified_at ? Math.floor(new Date(m.modified_at).getTime() / 1e3) : 0,
62098
+ owned_by: ep.label
62099
+ });
62100
+ }
62101
+ } else {
62102
+ const models = body.data ?? [];
62103
+ for (const m of models) {
62104
+ const originalId = m.id || m.name;
62105
+ const prefixed = `${ep.label}/${originalId}`;
62106
+ modelRouteMap.set(prefixed, { endpoint: ep, originalId });
62107
+ allModels.push({
62108
+ id: prefixed,
62109
+ object: "model",
62110
+ created: m.created || 0,
62111
+ owned_by: ep.label
62112
+ });
62113
+ }
62114
+ }
62115
+ } catch {
62116
+ }
62117
+ });
62118
+ await Promise.allSettled(fetches);
62119
+ return allModels.sort((a, b) => a.id.localeCompare(b.id));
62120
+ }
62121
+ function resolveModelEndpoint(modelId) {
62122
+ const exact = modelRouteMap.get(modelId);
62123
+ if (exact)
62124
+ return exact;
62125
+ for (const [prefixed, route] of modelRouteMap) {
62126
+ if (route.originalId === modelId)
62127
+ return route;
62128
+ }
62129
+ if (endpointRegistry.length > 0) {
62130
+ return { endpoint: endpointRegistry[0], originalId: modelId };
62131
+ }
62132
+ return null;
62133
+ }
62038
62134
  function recordMetric(method, path, status) {
62039
62135
  const key = `${method}|${path}|${status}`;
62040
62136
  const existing = metrics.requests.get(key);
@@ -62104,10 +62200,10 @@ async function parseJsonBody(req) {
62104
62200
  return null;
62105
62201
  }
62106
62202
  }
62107
- function backendAuthHeaders() {
62108
- const config = loadConfig();
62109
- if (config.apiKey)
62110
- return { Authorization: `Bearer ${config.apiKey}` };
62203
+ function backendAuthHeaders(endpoint) {
62204
+ const key = endpoint?.authKey ?? loadConfig().apiKey;
62205
+ if (key)
62206
+ return { Authorization: `Bearer ${key}` };
62111
62207
  return {};
62112
62208
  }
62113
62209
  function ollamaRequest(ollamaUrl, path, method, body) {
@@ -62285,33 +62381,15 @@ function handleMetrics(res) {
62285
62381
  }
62286
62382
  async function handleV1Models(res, ollamaUrl) {
62287
62383
  try {
62288
- const config = loadConfig();
62289
- const isVllm = config.backendType === "vllm";
62290
- if (isVllm) {
62291
- const result2 = await ollamaRequest(ollamaUrl, "/v1/models", "GET");
62292
- if (result2.status !== 200) {
62293
- jsonResponse(res, result2.status, { error: "Failed to fetch models from backend" });
62294
- return;
62295
- }
62296
- jsonResponse(res, 200, JSON.parse(result2.body));
62297
- return;
62298
- }
62299
- const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
62300
- if (result.status !== 200) {
62301
- jsonResponse(res, result.status, { error: "Failed to fetch models from backend" });
62302
- return;
62303
- }
62304
- const ollamaBody = JSON.parse(result.body);
62305
- const models = (ollamaBody.models ?? []).map((m) => ({
62306
- id: m.name,
62307
- object: "model",
62308
- created: m.modified_at ? Math.floor(new Date(m.modified_at).getTime() / 1e3) : 0,
62309
- owned_by: "local"
62310
- }));
62311
- jsonResponse(res, 200, { object: "list", data: models });
62384
+ const models = await fetchAggregatedModels();
62385
+ jsonResponse(res, 200, {
62386
+ object: "list",
62387
+ data: models,
62388
+ _endpoints: endpointRegistry.map((e) => ({ label: e.label, url: e.url, type: e.type }))
62389
+ });
62312
62390
  } catch (err) {
62313
62391
  jsonResponse(res, 502, {
62314
- error: "Failed to proxy to backend",
62392
+ error: "Failed to fetch models",
62315
62393
  message: err instanceof Error ? err.message : String(err)
62316
62394
  });
62317
62395
  }
@@ -62325,10 +62403,13 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62325
62403
  const requestBody = body;
62326
62404
  const stream = requestBody["stream"] === true;
62327
62405
  const model = requestBody["model"] || "unknown";
62328
- const config = loadConfig();
62329
- const isVllm = config.backendType === "vllm";
62330
- if (isVllm) {
62331
- const payload = JSON.stringify(requestBody);
62406
+ const route = resolveModelEndpoint(model);
62407
+ const targetUrl = route?.endpoint.url ?? ollamaUrl;
62408
+ const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
62409
+ const originalModel = route?.originalId ?? model;
62410
+ const routedBody = { ...requestBody, model: originalModel };
62411
+ if (targetType === "vllm" || targetType === "openai") {
62412
+ const payload = JSON.stringify(routedBody);
62332
62413
  if (stream) {
62333
62414
  res.writeHead(200, {
62334
62415
  "Content-Type": "text/event-stream",
@@ -62338,7 +62419,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62338
62419
  "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
62339
62420
  "Access-Control-Allow-Headers": "Content-Type, Authorization"
62340
62421
  });
62341
- ollamaStream(ollamaUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62422
+ ollamaStream(targetUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62342
62423
  res.write(chunk);
62343
62424
  }, () => {
62344
62425
  res.end();
@@ -62353,7 +62434,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62353
62434
  });
62354
62435
  } else {
62355
62436
  try {
62356
- const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", payload);
62437
+ const result = await ollamaRequest(targetUrl, "/v1/chat/completions", "POST", payload);
62357
62438
  if (result.status !== 200) {
62358
62439
  jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
62359
62440
  return;
@@ -62371,7 +62452,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62371
62452
  return;
62372
62453
  }
62373
62454
  const ollamaPayload = JSON.stringify({
62374
- ...requestBody,
62455
+ ...routedBody,
62375
62456
  stream,
62376
62457
  think: false
62377
62458
  });
@@ -62386,7 +62467,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62386
62467
  });
62387
62468
  const chatId = `chatcmpl-${randomBytes16(12).toString("hex")}`;
62388
62469
  let buffer = "";
62389
- ollamaStream(ollamaUrl, "/api/chat", "POST", ollamaPayload, (chunk) => {
62470
+ ollamaStream(targetUrl, "/api/chat", "POST", ollamaPayload, (chunk) => {
62390
62471
  buffer += chunk;
62391
62472
  const lines = buffer.split("\n");
62392
62473
  buffer = lines.pop() ?? "";
@@ -62476,7 +62557,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62476
62557
  });
62477
62558
  } else {
62478
62559
  try {
62479
- const result = await ollamaRequest(ollamaUrl, "/api/chat", "POST", ollamaPayload);
62560
+ const result = await ollamaRequest(targetUrl, "/api/chat", "POST", ollamaPayload);
62480
62561
  if (result.status !== 200) {
62481
62562
  jsonResponse(res, result.status, {
62482
62563
  error: "Ollama request failed",
@@ -63149,29 +63230,35 @@ function startApiServer(options = {}) {
63149
63230
  `);
63150
63231
  }
63151
63232
  });
63152
- server.listen(port, host, () => {
63153
- const version = getVersion3();
63154
- process.stderr.write(`
63233
+ refreshEndpointRegistry().catch(() => {
63234
+ }).then(() => {
63235
+ server.listen(port, host, () => {
63236
+ const version = getVersion3();
63237
+ process.stderr.write(`
63155
63238
  open-agents API server v${version}
63156
63239
  `);
63157
- process.stderr.write(` Listening on http://${host}:${port}
63240
+ process.stderr.write(` Listening on http://${host}:${port}
63158
63241
  `);
63159
- const beType = config.backendType || "ollama";
63160
- process.stderr.write(` Backend (${beType}): ${ollamaUrl}
63242
+ process.stderr.write(` Endpoints: ${endpointRegistry.length} registered
63161
63243
  `);
63162
- if (process.env["OA_API_KEYS"]) {
63163
- const keyCount = process.env["OA_API_KEYS"].split(",").length;
63164
- process.stderr.write(` Auth: ${keyCount} scoped key(s) (read/run/admin)
63244
+ for (const ep of endpointRegistry) {
63245
+ process.stderr.write(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
63165
63246
  `);
63166
- } else if (process.env["OA_API_KEY"]) {
63167
- process.stderr.write(` Auth: single Bearer token (admin scope)
63247
+ }
63248
+ if (process.env["OA_API_KEYS"]) {
63249
+ const keyCount = process.env["OA_API_KEYS"].split(",").length;
63250
+ process.stderr.write(` Auth: ${keyCount} scoped key(s) (read/run/admin)
63168
63251
  `);
63169
- } else {
63170
- process.stderr.write(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
63252
+ } else if (process.env["OA_API_KEY"]) {
63253
+ process.stderr.write(` Auth: single Bearer token (admin scope)
63171
63254
  `);
63172
- }
63173
- process.stderr.write(`
63255
+ } else {
63256
+ process.stderr.write(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
63174
63257
  `);
63258
+ }
63259
+ process.stderr.write(`
63260
+ `);
63261
+ });
63175
63262
  });
63176
63263
  const shutdown = () => {
63177
63264
  process.stderr.write("\n Shutting down API server...\n");
@@ -63201,7 +63288,7 @@ async function apiServeCommand(opts, config) {
63201
63288
  server.on("close", resolve36);
63202
63289
  });
63203
63290
  }
63204
- var metrics, startedAt, runningProcesses;
63291
+ var endpointRegistry, modelRouteMap, metrics, startedAt, runningProcesses;
63205
63292
  var init_serve = __esm({
63206
63293
  "packages/cli/dist/api/serve.js"() {
63207
63294
  "use strict";
@@ -63209,6 +63296,8 @@ var init_serve = __esm({
63209
63296
  init_oa_directory();
63210
63297
  init_render();
63211
63298
  init_profiles();
63299
+ endpointRegistry = [];
63300
+ modelRouteMap = /* @__PURE__ */ new Map();
63212
63301
  metrics = {
63213
63302
  requests: /* @__PURE__ */ new Map(),
63214
63303
  totalTokensIn: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.184.54",
3
+ "version": "0.184.55",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",