open-agents-ai 0.184.54 → 0.184.56

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 +151 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -62035,6 +62035,110 @@ 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 [, route] of modelRouteMap) {
62126
+ if (route.originalId === modelId)
62127
+ return route;
62128
+ }
62129
+ const slashIdx = modelId.indexOf("/");
62130
+ if (slashIdx > 0) {
62131
+ const prefix = modelId.slice(0, slashIdx);
62132
+ const rest = modelId.slice(slashIdx + 1);
62133
+ const ep = endpointRegistry.find((e) => e.label === prefix);
62134
+ if (ep)
62135
+ return { endpoint: ep, originalId: rest };
62136
+ }
62137
+ if (endpointRegistry.length > 0) {
62138
+ return { endpoint: endpointRegistry[0], originalId: modelId };
62139
+ }
62140
+ return null;
62141
+ }
62038
62142
  function recordMetric(method, path, status) {
62039
62143
  const key = `${method}|${path}|${status}`;
62040
62144
  const existing = metrics.requests.get(key);
@@ -62104,10 +62208,10 @@ async function parseJsonBody(req) {
62104
62208
  return null;
62105
62209
  }
62106
62210
  }
62107
- function backendAuthHeaders() {
62108
- const config = loadConfig();
62109
- if (config.apiKey)
62110
- return { Authorization: `Bearer ${config.apiKey}` };
62211
+ function backendAuthHeaders(endpoint) {
62212
+ const key = endpoint?.authKey ?? loadConfig().apiKey;
62213
+ if (key)
62214
+ return { Authorization: `Bearer ${key}` };
62111
62215
  return {};
62112
62216
  }
62113
62217
  function ollamaRequest(ollamaUrl, path, method, body) {
@@ -62285,33 +62389,15 @@ function handleMetrics(res) {
62285
62389
  }
62286
62390
  async function handleV1Models(res, ollamaUrl) {
62287
62391
  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 });
62392
+ const models = await fetchAggregatedModels();
62393
+ jsonResponse(res, 200, {
62394
+ object: "list",
62395
+ data: models,
62396
+ _endpoints: endpointRegistry.map((e) => ({ label: e.label, url: e.url, type: e.type }))
62397
+ });
62312
62398
  } catch (err) {
62313
62399
  jsonResponse(res, 502, {
62314
- error: "Failed to proxy to backend",
62400
+ error: "Failed to fetch models",
62315
62401
  message: err instanceof Error ? err.message : String(err)
62316
62402
  });
62317
62403
  }
@@ -62325,10 +62411,13 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62325
62411
  const requestBody = body;
62326
62412
  const stream = requestBody["stream"] === true;
62327
62413
  const model = requestBody["model"] || "unknown";
62328
- const config = loadConfig();
62329
- const isVllm = config.backendType === "vllm";
62330
- if (isVllm) {
62331
- const payload = JSON.stringify(requestBody);
62414
+ const route = resolveModelEndpoint(model);
62415
+ const targetUrl = route?.endpoint.url ?? ollamaUrl;
62416
+ const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
62417
+ const originalModel = route?.originalId ?? model;
62418
+ const routedBody = { ...requestBody, model: originalModel };
62419
+ if (targetType === "vllm" || targetType === "openai") {
62420
+ const payload = JSON.stringify(routedBody);
62332
62421
  if (stream) {
62333
62422
  res.writeHead(200, {
62334
62423
  "Content-Type": "text/event-stream",
@@ -62338,7 +62427,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62338
62427
  "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
62339
62428
  "Access-Control-Allow-Headers": "Content-Type, Authorization"
62340
62429
  });
62341
- ollamaStream(ollamaUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62430
+ ollamaStream(targetUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62342
62431
  res.write(chunk);
62343
62432
  }, () => {
62344
62433
  res.end();
@@ -62353,7 +62442,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62353
62442
  });
62354
62443
  } else {
62355
62444
  try {
62356
- const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", payload);
62445
+ const result = await ollamaRequest(targetUrl, "/v1/chat/completions", "POST", payload);
62357
62446
  if (result.status !== 200) {
62358
62447
  jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
62359
62448
  return;
@@ -62371,7 +62460,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62371
62460
  return;
62372
62461
  }
62373
62462
  const ollamaPayload = JSON.stringify({
62374
- ...requestBody,
62463
+ ...routedBody,
62375
62464
  stream,
62376
62465
  think: false
62377
62466
  });
@@ -62386,7 +62475,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62386
62475
  });
62387
62476
  const chatId = `chatcmpl-${randomBytes16(12).toString("hex")}`;
62388
62477
  let buffer = "";
62389
- ollamaStream(ollamaUrl, "/api/chat", "POST", ollamaPayload, (chunk) => {
62478
+ ollamaStream(targetUrl, "/api/chat", "POST", ollamaPayload, (chunk) => {
62390
62479
  buffer += chunk;
62391
62480
  const lines = buffer.split("\n");
62392
62481
  buffer = lines.pop() ?? "";
@@ -62476,7 +62565,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62476
62565
  });
62477
62566
  } else {
62478
62567
  try {
62479
- const result = await ollamaRequest(ollamaUrl, "/api/chat", "POST", ollamaPayload);
62568
+ const result = await ollamaRequest(targetUrl, "/api/chat", "POST", ollamaPayload);
62480
62569
  if (result.status !== 200) {
62481
62570
  jsonResponse(res, result.status, {
62482
62571
  error: "Ollama request failed",
@@ -63149,29 +63238,35 @@ function startApiServer(options = {}) {
63149
63238
  `);
63150
63239
  }
63151
63240
  });
63152
- server.listen(port, host, () => {
63153
- const version = getVersion3();
63154
- process.stderr.write(`
63241
+ refreshEndpointRegistry().catch(() => {
63242
+ }).then(() => {
63243
+ server.listen(port, host, () => {
63244
+ const version = getVersion3();
63245
+ process.stderr.write(`
63155
63246
  open-agents API server v${version}
63156
63247
  `);
63157
- process.stderr.write(` Listening on http://${host}:${port}
63248
+ process.stderr.write(` Listening on http://${host}:${port}
63158
63249
  `);
63159
- const beType = config.backendType || "ollama";
63160
- process.stderr.write(` Backend (${beType}): ${ollamaUrl}
63250
+ process.stderr.write(` Endpoints: ${endpointRegistry.length} registered
63161
63251
  `);
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)
63252
+ for (const ep of endpointRegistry) {
63253
+ process.stderr.write(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
63165
63254
  `);
63166
- } else if (process.env["OA_API_KEY"]) {
63167
- process.stderr.write(` Auth: single Bearer token (admin scope)
63255
+ }
63256
+ if (process.env["OA_API_KEYS"]) {
63257
+ const keyCount = process.env["OA_API_KEYS"].split(",").length;
63258
+ process.stderr.write(` Auth: ${keyCount} scoped key(s) (read/run/admin)
63168
63259
  `);
63169
- } else {
63170
- process.stderr.write(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
63260
+ } else if (process.env["OA_API_KEY"]) {
63261
+ process.stderr.write(` Auth: single Bearer token (admin scope)
63171
63262
  `);
63172
- }
63173
- process.stderr.write(`
63263
+ } else {
63264
+ process.stderr.write(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
63265
+ `);
63266
+ }
63267
+ process.stderr.write(`
63174
63268
  `);
63269
+ });
63175
63270
  });
63176
63271
  const shutdown = () => {
63177
63272
  process.stderr.write("\n Shutting down API server...\n");
@@ -63201,7 +63296,7 @@ async function apiServeCommand(opts, config) {
63201
63296
  server.on("close", resolve36);
63202
63297
  });
63203
63298
  }
63204
- var metrics, startedAt, runningProcesses;
63299
+ var endpointRegistry, modelRouteMap, metrics, startedAt, runningProcesses;
63205
63300
  var init_serve = __esm({
63206
63301
  "packages/cli/dist/api/serve.js"() {
63207
63302
  "use strict";
@@ -63209,6 +63304,8 @@ var init_serve = __esm({
63209
63304
  init_oa_directory();
63210
63305
  init_render();
63211
63306
  init_profiles();
63307
+ endpointRegistry = [];
63308
+ modelRouteMap = /* @__PURE__ */ new Map();
63212
63309
  metrics = {
63213
63310
  requests: /* @__PURE__ */ new Map(),
63214
63311
  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.56",
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",