omnigateway 0.4.5 → 0.4.7

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 (3) hide show
  1. package/bin/omni.js +26 -18
  2. package/gateway.js +34 -31
  3. package/package.json +1 -1
package/bin/omni.js CHANGED
@@ -237,6 +237,12 @@ var RETRYABLE = {
237
237
  ALL_CANDIDATES_FAILED: false,
238
238
  INTERNAL: false
239
239
  };
240
+ function describeError(error, fallback) {
241
+ if (!(error instanceof Error))
242
+ return fallback;
243
+ return error.message.length > 0 ? error.message : error.name;
244
+ }
245
+
240
246
  class GatewayError extends Error {
241
247
  code;
242
248
  retryable;
@@ -2873,6 +2879,7 @@ var grokAdapter = {
2873
2879
  import { request as httpRequest } from "http";
2874
2880
  import { request as httpsRequest } from "https";
2875
2881
  import { Readable } from "stream";
2882
+ var CONNECT_ATTEMPT_TIMEOUT_MS = 3000;
2876
2883
  function nodeHttpClient(options = {}) {
2877
2884
  const logger2 = options.logger ?? noopLogger;
2878
2885
  const now = options.now ?? (() => Date.now());
@@ -2901,15 +2908,17 @@ function nodeHttpClient(options = {}) {
2901
2908
  if (req.body.length > 0 && !hasHeader(req, "content-length")) {
2902
2909
  headers["Content-Length"] = bodyBytes.byteLength;
2903
2910
  }
2904
- const outgoing = send({
2911
+ const requestOptions = {
2905
2912
  protocol: url.protocol,
2906
2913
  hostname: url.hostname,
2907
2914
  port: url.port || (url.protocol === "https:" ? 443 : 80),
2908
2915
  path: `${url.pathname}${url.search}`,
2909
2916
  method: req.method,
2910
2917
  headers,
2911
- setHost: !hasHeader(req, "host")
2912
- }, (incoming) => {
2918
+ setHost: !hasHeader(req, "host"),
2919
+ autoSelectFamilyAttemptTimeout: CONNECT_ATTEMPT_TIMEOUT_MS
2920
+ };
2921
+ const outgoing = send(requestOptions, (incoming) => {
2913
2922
  const chunks = [];
2914
2923
  let buffered = null;
2915
2924
  const responseHeaders = new Headers;
@@ -20131,7 +20140,7 @@ function createPluginRepo(db) {
20131
20140
  applied,
20132
20141
  failed: {
20133
20142
  version: migration.version,
20134
- reason: error51 instanceof Error ? error51.message : String(error51)
20143
+ reason: describeError(error51, String(error51))
20135
20144
  }
20136
20145
  };
20137
20146
  }
@@ -20975,14 +20984,14 @@ async function swapIn(deps, candidate) {
20975
20984
  await deps.store.usage.rebuildRollup();
20976
20985
  } catch (error51) {
20977
20986
  (deps.logger ?? noopLogger).warn("usage rollup not rebuilt after the swap; run omni doctor", {
20978
- reason: error51 instanceof Error ? error51.message : "unknown"
20987
+ reason: describeError(error51, "unknown")
20979
20988
  });
20980
20989
  }
20981
20990
  try {
20982
20991
  await deps.reapplyPluginSchema?.();
20983
20992
  } catch (error51) {
20984
20993
  (deps.logger ?? noopLogger).warn("plugin schema not reapplied after the swap; restart", {
20985
- reason: error51 instanceof Error ? error51.message : "unknown"
20994
+ reason: describeError(error51, "unknown")
20986
20995
  });
20987
20996
  }
20988
20997
  return {
@@ -21386,7 +21395,7 @@ function readManifest(fs, home) {
21386
21395
  try {
21387
21396
  document = JSON.parse(raw);
21388
21397
  } catch (error51) {
21389
- const detail = error51 instanceof Error ? error51.message : String(error51);
21398
+ const detail = describeError(error51, String(error51));
21390
21399
  return {
21391
21400
  check: "manifest",
21392
21401
  reason: `${MANIFEST_FILENAME} is not valid JSON: ${detail}`,
@@ -21592,7 +21601,7 @@ function gunzipIfNeeded(bytes) {
21592
21601
  try {
21593
21602
  return Bun.gunzipSync(new Uint8Array(bytes));
21594
21603
  } catch (error51) {
21595
- const detail = error51 instanceof Error ? error51.message : String(error51);
21604
+ const detail = describeError(error51, String(error51));
21596
21605
  throw new GatewayError("BAD_REQUEST", `could not decompress the archive: ${detail}`);
21597
21606
  }
21598
21607
  }
@@ -21774,7 +21783,7 @@ function parseJson(bytes, what) {
21774
21783
  try {
21775
21784
  return JSON.parse(decoder2.decode(bytes));
21776
21785
  } catch (error51) {
21777
- const detail = error51 instanceof Error ? error51.message : String(error51);
21786
+ const detail = describeError(error51, String(error51));
21778
21787
  throw new GatewayError("BAD_REQUEST", `${what} is not valid JSON: ${detail}`);
21779
21788
  }
21780
21789
  }
@@ -21812,7 +21821,7 @@ async function installPlugin(deps, root, spec) {
21812
21821
  try {
21813
21822
  document = JSON.parse(decoder2.decode(raw));
21814
21823
  } catch (error51) {
21815
- const detail = error51 instanceof Error ? error51.message : String(error51);
21824
+ const detail = describeError(error51, String(error51));
21816
21825
  throw new GatewayError("BAD_REQUEST", `${MANIFEST_FILENAME} is not valid JSON: ${detail}`);
21817
21826
  }
21818
21827
  const parsed = safeParseManifest(document);
@@ -23033,7 +23042,7 @@ function settingsObject(existing) {
23033
23042
  }
23034
23043
  return parsed;
23035
23044
  } catch (error51) {
23036
- const reason = error51 instanceof Error ? error51.message : "invalid JSON";
23045
+ const reason = describeError(error51, "invalid JSON");
23037
23046
  throw new Error(`cannot parse existing settings.json: ${reason}`);
23038
23047
  }
23039
23048
  }
@@ -23179,7 +23188,6 @@ async function recentLogs(store, requested) {
23179
23188
  }
23180
23189
  // apps/cli/src/args.ts
23181
23190
  import { parseArgs } from "util";
23182
-
23183
23191
  class UsageError extends Error {
23184
23192
  }
23185
23193
  var GLOBAL_OPTIONS = {
@@ -23201,7 +23209,7 @@ function parse5(argv, options = {}) {
23201
23209
  });
23202
23210
  return { positionals: result.positionals, values: result.values };
23203
23211
  } catch (error51) {
23204
- throw new UsageError(error51 instanceof Error ? error51.message : "could not parse arguments");
23212
+ throw new UsageError(describeError(error51, "could not parse arguments"));
23205
23213
  }
23206
23214
  }
23207
23215
  function stringFlag(values2, name) {
@@ -23303,7 +23311,7 @@ function createContext(parsed, options = {}) {
23303
23311
  try {
23304
23312
  config2 = loadConfig(env2);
23305
23313
  } catch (error51) {
23306
- configError = error51 instanceof Error ? error51.message : "invalid configuration";
23314
+ configError = describeError(error51, "invalid configuration");
23307
23315
  }
23308
23316
  const configuredPath = dbFlag ?? config2?.databasePath ?? "omnigateway.db";
23309
23317
  const databasePath = isAbsolute(configuredPath) ? configuredPath : resolve3(root.root, configuredPath);
@@ -25376,7 +25384,7 @@ var status2 = {
25376
25384
  try {
25377
25385
  store = await ctx.store();
25378
25386
  } catch (error51) {
25379
- storeError = error51 instanceof Error ? error51.message : "could not open the database";
25387
+ storeError = describeError(error51, "could not open the database");
25380
25388
  }
25381
25389
  const persistent = store === null ? { adminConfigured: false, credentials: [] } : await credentialStatus(store, { now: ctx.now });
25382
25390
  const { adminConfigured: configured, credentials } = persistent;
@@ -25432,7 +25440,7 @@ var adminSetPassword = {
25432
25440
  try {
25433
25441
  await admin.setPassword(password);
25434
25442
  } catch (error51) {
25435
- throw new CliError(error51 instanceof Error ? error51.message : "could not set the password");
25443
+ throw new CliError(describeError(error51, "could not set the password"));
25436
25444
  }
25437
25445
  note(ctx, writer, "restart the gateway to end sessions signed in with the old password");
25438
25446
  emit(ctx, writer, { ok: true }, () => "admin password set");
@@ -25799,7 +25807,7 @@ async function run(argv, writer, options = {}) {
25799
25807
  try {
25800
25808
  args = parse5(resolved.rest, resolved.command.options ?? {});
25801
25809
  } catch (error51) {
25802
- writer.err(error51 instanceof Error ? error51.message : "could not parse arguments");
25810
+ writer.err(describeError(error51, "could not parse arguments"));
25803
25811
  writer.err(`usage: omni ${resolved.command.usage}`);
25804
25812
  return 2;
25805
25813
  }
@@ -25849,7 +25857,7 @@ async function run(argv, writer, options = {}) {
25849
25857
  writer.err(`${error51.code}: ${error51.message}`);
25850
25858
  return 1;
25851
25859
  }
25852
- writer.err(error51 instanceof Error ? error51.message : "unknown error");
25860
+ writer.err(describeError(error51, "unknown error"));
25853
25861
  return 1;
25854
25862
  } finally {
25855
25863
  ctx.close();
package/gateway.js CHANGED
@@ -5375,6 +5375,11 @@ var HTTP_STATUS = {
5375
5375
  ALL_CANDIDATES_FAILED: 503,
5376
5376
  INTERNAL: 500
5377
5377
  };
5378
+ function describeError(error, fallback) {
5379
+ if (!(error instanceof Error))
5380
+ return fallback;
5381
+ return error.message.length > 0 ? error.message : error.name;
5382
+ }
5378
5383
 
5379
5384
  class GatewayError extends Error {
5380
5385
  code;
@@ -8234,6 +8239,7 @@ var grokAdapter = {
8234
8239
  import { request as httpRequest } from "http";
8235
8240
  import { request as httpsRequest } from "https";
8236
8241
  import { Readable } from "stream";
8242
+ var CONNECT_ATTEMPT_TIMEOUT_MS = 3000;
8237
8243
  function nodeHttpClient(options = {}) {
8238
8244
  const logger2 = options.logger ?? noopLogger;
8239
8245
  const now = options.now ?? (() => Date.now());
@@ -8262,15 +8268,17 @@ function nodeHttpClient(options = {}) {
8262
8268
  if (req.body.length > 0 && !hasHeader(req, "content-length")) {
8263
8269
  headers["Content-Length"] = bodyBytes.byteLength;
8264
8270
  }
8265
- const outgoing = send({
8271
+ const requestOptions = {
8266
8272
  protocol: url.protocol,
8267
8273
  hostname: url.hostname,
8268
8274
  port: url.port || (url.protocol === "https:" ? 443 : 80),
8269
8275
  path: `${url.pathname}${url.search}`,
8270
8276
  method: req.method,
8271
8277
  headers,
8272
- setHost: !hasHeader(req, "host")
8273
- }, (incoming) => {
8278
+ setHost: !hasHeader(req, "host"),
8279
+ autoSelectFamilyAttemptTimeout: CONNECT_ATTEMPT_TIMEOUT_MS
8280
+ };
8281
+ const outgoing = send(requestOptions, (incoming) => {
8274
8282
  const chunks = [];
8275
8283
  let buffered = null;
8276
8284
  const responseHeaders = new Headers;
@@ -25605,7 +25613,7 @@ function createPluginRepo(db) {
25605
25613
  applied,
25606
25614
  failed: {
25607
25615
  version: migration.version,
25608
- reason: error51 instanceof Error ? error51.message : String(error51)
25616
+ reason: describeError(error51, String(error51))
25609
25617
  }
25610
25618
  };
25611
25619
  }
@@ -26460,14 +26468,14 @@ async function swapIn(deps, candidate) {
26460
26468
  await deps.store.usage.rebuildRollup();
26461
26469
  } catch (error51) {
26462
26470
  (deps.logger ?? noopLogger).warn("usage rollup not rebuilt after the swap; run omni doctor", {
26463
- reason: error51 instanceof Error ? error51.message : "unknown"
26471
+ reason: describeError(error51, "unknown")
26464
26472
  });
26465
26473
  }
26466
26474
  try {
26467
26475
  await deps.reapplyPluginSchema?.();
26468
26476
  } catch (error51) {
26469
26477
  (deps.logger ?? noopLogger).warn("plugin schema not reapplied after the swap; restart", {
26470
- reason: error51 instanceof Error ? error51.message : "unknown"
26478
+ reason: describeError(error51, "unknown")
26471
26479
  });
26472
26480
  }
26473
26481
  return {
@@ -28009,7 +28017,7 @@ async function poll(deps) {
28009
28017
  credentialId: credential.id,
28010
28018
  code: error51 instanceof GatewayError ? error51.code : "INTERNAL",
28011
28019
  ...rateLimited ? { retryAfterMs: RATE_LIMIT_COOLDOWN_MS } : {},
28012
- reason: error51 instanceof Error ? error51.message : "unknown"
28020
+ reason: describeError(error51, "unknown")
28013
28021
  });
28014
28022
  }
28015
28023
  }
@@ -28062,7 +28070,7 @@ function settingsObject(existing) {
28062
28070
  }
28063
28071
  return parsed;
28064
28072
  } catch (error51) {
28065
- const reason = error51 instanceof Error ? error51.message : "invalid JSON";
28073
+ const reason = describeError(error51, "invalid JSON");
28066
28074
  throw new Error(`cannot parse existing settings.json: ${reason}`);
28067
28075
  }
28068
28076
  }
@@ -42885,7 +42893,7 @@ class ApiKeyRateLimiter {
42885
42893
  this.logger.warn("rate limit reset unavailable", {
42886
42894
  ...requestId === undefined ? {} : { requestId },
42887
42895
  apiKeyId: keyId,
42888
- reason: error51 instanceof Error ? error51.message : "unknown"
42896
+ reason: describeError(error51, "unknown")
42889
42897
  });
42890
42898
  return null;
42891
42899
  }
@@ -42951,7 +42959,7 @@ class ApiKeyRateLimiter {
42951
42959
  this.logger.warn("rate limit counters unavailable", {
42952
42960
  ...requestId === undefined ? {} : { requestId },
42953
42961
  apiKeyId: keyId,
42954
- reason: error51 instanceof Error ? error51.message : "unknown"
42962
+ reason: describeError(error51, "unknown")
42955
42963
  });
42956
42964
  state.debits = trimDebits(state.debits, now);
42957
42965
  return null;
@@ -43796,7 +43804,7 @@ function adminRoutes(deps) {
43796
43804
  try {
43797
43805
  created = await deps.admin.setInitialPassword(body2.password);
43798
43806
  } catch (error51) {
43799
- throw new GatewayError("BAD_REQUEST", error51 instanceof Error ? error51.message : "invalid password");
43807
+ throw new GatewayError("BAD_REQUEST", describeError(error51, "invalid password"));
43800
43808
  }
43801
43809
  if (!created) {
43802
43810
  set2.status = 409;
@@ -43888,7 +43896,7 @@ function adminRoutes(deps) {
43888
43896
  })
43889
43897
  };
43890
43898
  } catch (error51) {
43891
- throw new GatewayError("BAD_REQUEST", error51 instanceof Error ? error51.message : "invalid Claude model mapping");
43899
+ throw new GatewayError("BAD_REQUEST", describeError(error51, "invalid Claude model mapping"));
43892
43900
  }
43893
43901
  }).get("/api/keys", async ({ request: request2 }) => {
43894
43902
  await requireAdmin(request2, deps.admin);
@@ -46157,7 +46165,7 @@ function reportRejection(logger2, requestId, log, error51, surface) {
46157
46165
  function report(logger2, what, requestId, error51) {
46158
46166
  logger2.warn(what, {
46159
46167
  requestId,
46160
- reason: error51 instanceof Error ? error51.message : "unknown"
46168
+ reason: describeError(error51, "unknown")
46161
46169
  });
46162
46170
  }
46163
46171
  async function beginLog(store, log, keyId, logger2 = noopLogger) {
@@ -46261,11 +46269,6 @@ var NETWORK_HINTS = [
46261
46269
  "socket hang up",
46262
46270
  "unable to connect"
46263
46271
  ];
46264
- function describeError(error51) {
46265
- if (!(error51 instanceof Error))
46266
- return "attempt failed";
46267
- return error51.message.length > 0 ? error51.message : error51.name;
46268
- }
46269
46272
  function classify(error51) {
46270
46273
  if (error51 instanceof GatewayError) {
46271
46274
  return {
@@ -46393,7 +46396,7 @@ async function dispatch(request2, deps, signal, requestId) {
46393
46396
  if (isClientAbort(error51))
46394
46397
  return fail("TIMEOUT", "client disconnected", true);
46395
46398
  const { code } = classify(error51);
46396
- return fail(code, error51 instanceof Error ? error51.message : "unresolvable model");
46399
+ return fail(code, describeError(error51, "unresolvable model"));
46397
46400
  }
46398
46401
  const { candidates, excluded } = rank({
46399
46402
  request: dispatchRequest,
@@ -46558,7 +46561,7 @@ async function dispatch(request2, deps, signal, requestId) {
46558
46561
  requestId,
46559
46562
  provider: candidate.target.provider,
46560
46563
  credentialId: candidate.credential.id,
46561
- reason: healthError instanceof Error ? healthError.message : "unknown"
46564
+ reason: describeError(healthError, "unknown")
46562
46565
  });
46563
46566
  }
46564
46567
  return;
@@ -46593,7 +46596,7 @@ async function dispatch(request2, deps, signal, requestId) {
46593
46596
  throw signal.reason;
46594
46597
  const classifiedError = deadlineAt !== null && dispatchSignal.aborted ? { code: "TIMEOUT" } : classify(error51);
46595
46598
  const { code: code2 } = classifiedError;
46596
- const message2 = describeError(error51);
46599
+ const message2 = describeError(error51, "attempt failed");
46597
46600
  lastError = rewrap(classifiedError, message2);
46598
46601
  if (code2 === "AUTH" && !committed && !authRefreshRetried && !preemptiveRefreshRequired && candidate.credential.authType === "oauth" && candidate.credential.hasRefreshToken) {
46599
46602
  authRefreshRetried = true;
@@ -46612,7 +46615,7 @@ async function dispatch(request2, deps, signal, requestId) {
46612
46615
  if (signal.aborted)
46613
46616
  throw signal.reason;
46614
46617
  const classified = deadlineAt !== null && dispatchSignal.aborted ? { code: "TIMEOUT" } : classify(refreshError);
46615
- const refreshMessage = refreshError instanceof Error ? describeError(refreshError) : "credential refresh failed";
46618
+ const refreshMessage = describeError(refreshError, "credential refresh failed");
46616
46619
  lastError = rewrap(classified, refreshMessage);
46617
46620
  logger2.warn("credential refresh failed", {
46618
46621
  requestId,
@@ -47665,7 +47668,7 @@ function errorResponse(surface, code, message3, headers = {}) {
47665
47668
  function asGatewayError(error51) {
47666
47669
  if (error51 instanceof GatewayError)
47667
47670
  return error51;
47668
- return new GatewayError("INTERNAL", error51 instanceof Error ? error51.message : "internal error");
47671
+ return new GatewayError("INTERNAL", describeError(error51, "internal error"));
47669
47672
  }
47670
47673
  function sseResponse(frames, onDone, keepaliveMs, source, limitHeaders, onFrame) {
47671
47674
  const encoder4 = new TextEncoder;
@@ -48154,7 +48157,7 @@ function createShutdown(deps) {
48154
48157
  }, (error51) => {
48155
48158
  clearTimeout(timer);
48156
48159
  deps.logger.error("shutdown failed", {
48157
- reason: error51 instanceof Error ? error51.message : "unknown"
48160
+ reason: describeError(error51, "unknown")
48158
48161
  });
48159
48162
  exitAfterClosingStore(1);
48160
48163
  });
@@ -48198,7 +48201,7 @@ function startMaintenance(deps) {
48198
48201
  const timer = setInterval(() => {
48199
48202
  pruneFiles(files).then(({ snapshots, staging }) => logger2.debug("snapshots and staging files swept", { count: snapshots + staging })).catch((error51) => {
48200
48203
  logger2.error("snapshot sweeping failed", {
48201
- reason: error51 instanceof Error ? error51.message : "unknown"
48204
+ reason: describeError(error51, "unknown")
48202
48205
  });
48203
48206
  });
48204
48207
  pruneLogs(deps.store, deps.now()).then(({ raw, daily, quotaSamples, bodies, bodiesOverCap, bodyOrphans }) => logger2.debug("request logs pruned", {
@@ -48208,7 +48211,7 @@ function startMaintenance(deps) {
48208
48211
  count: bodies + bodiesOverCap + bodyOrphans
48209
48212
  })).catch((error51) => {
48210
48213
  logger2.error("log pruning failed", {
48211
- reason: error51 instanceof Error ? error51.message : "unknown"
48214
+ reason: describeError(error51, "unknown")
48212
48215
  });
48213
48216
  });
48214
48217
  }, SWEEP_INTERVAL_MS);
@@ -48245,7 +48248,7 @@ async function sweep(deps) {
48245
48248
  provider: credential.provider,
48246
48249
  credentialId: credential.id,
48247
48250
  code,
48248
- reason: error51 instanceof Error ? error51.message : "unknown"
48251
+ reason: describeError(error51, "unknown")
48249
48252
  });
48250
48253
  }
48251
48254
  }
@@ -48260,7 +48263,7 @@ function startRefreshScheduler(deps) {
48260
48263
  running = true;
48261
48264
  sweep(deps).catch((error51) => {
48262
48265
  logger2.error("token refresh sweep failed", {
48263
- reason: error51 instanceof Error ? error51.message : "unknown"
48266
+ reason: describeError(error51, "unknown")
48264
48267
  });
48265
48268
  }).finally(() => {
48266
48269
  running = false;
@@ -48491,7 +48494,7 @@ function buildContext(deps) {
48491
48494
  };
48492
48495
  }
48493
48496
  function reason(error51) {
48494
- return error51 instanceof Error ? error51.message : String(error51);
48497
+ return describeError(error51, String(error51));
48495
48498
  }
48496
48499
  async function loadPlugins(deps) {
48497
48500
  const logger2 = deps.logger ?? noopLogger;
@@ -48611,7 +48614,7 @@ async function startQuotaPoller(deps) {
48611
48614
  running = true;
48612
48615
  poll(deps).catch((error51) => {
48613
48616
  logger2.error("quota poll failed", {
48614
- reason: error51 instanceof Error ? error51.message : "unknown"
48617
+ reason: describeError(error51, "unknown")
48615
48618
  });
48616
48619
  }).finally(() => {
48617
48620
  running = false;
@@ -48773,7 +48776,7 @@ try {
48773
48776
  await main();
48774
48777
  } catch (error51) {
48775
48778
  logger2.error("gateway boot failed", {
48776
- reason: error51 instanceof Error ? error51.message : "unknown"
48779
+ reason: describeError(error51, "unknown")
48777
48780
  });
48778
48781
  process.exit(1);
48779
48782
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnigateway",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Self-hosted AI gateway with Anthropic- and OpenAI-compatible APIs, an admin console, and a CLI",
5
5
  "license": "MIT",
6
6
  "author": "Harismawan <mail@harismawan.com>",