omnigateway 0.1.6 → 0.1.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 (37) hide show
  1. package/README.md +24 -5
  2. package/bin/omni.js +454 -228
  3. package/gateway.js +225 -59
  4. package/package.json +1 -1
  5. package/public/assets/{Chip-Ccqsb_Mq.js → Chip-C69u2U_b.js} +2 -2
  6. package/public/assets/{CopyValue-cAwgC1sU.js → CopyValue-CJy2T0H6.js} +1 -1
  7. package/public/assets/{Field-Devm4K46.js → Field-Sc8mDqJv.js} +1 -1
  8. package/public/assets/{Lamp-BXSUIAtG.js → Lamp-7jD_9929.js} +2 -2
  9. package/public/assets/{Meter-Fn8X7t60.js → Meter-C0nUftAa.js} +2 -2
  10. package/public/assets/{Modal-Dqir9wBz.js → Modal-CiVHSJmm.js} +2 -2
  11. package/public/assets/Rack-CMqGBP4I.js +147 -0
  12. package/public/assets/{Readout-BKpOZIGX.js → Readout-Dv2dZWtI.js} +4 -4
  13. package/public/assets/{States-DBzsiHjQ.js → States-D55MhqMx.js} +10 -10
  14. package/public/assets/{Table-DPDJeeVc.js → Table-DBB3jdgi.js} +1 -1
  15. package/public/assets/{Toggle-BhViDMm9.js → Toggle-BoGjb5oi.js} +3 -3
  16. package/public/assets/_app-p5Mz7sXD.js +1 -0
  17. package/public/assets/_app.accounts-B5wMO9TH.js +51 -0
  18. package/public/assets/_app.console-Bka7AkiL.js +25 -0
  19. package/public/assets/_app.index-MnQuadXb.js +61 -0
  20. package/public/assets/_app.keys-BX8k8COP.js +39 -0
  21. package/public/assets/_app.logs-DD-sd4fn.js +33 -0
  22. package/public/assets/_app.models-CezFNL6y.js +144 -0
  23. package/public/assets/{_app.settings-DA1aYcR4.js → _app.settings-D1taAt8B.js} +3 -3
  24. package/public/assets/{_app.usage-DAyYUfIi.js → _app.usage-BRhEvwVr.js} +28 -28
  25. package/public/assets/index-vCuK1fRQ.js +170 -0
  26. package/public/assets/{login-DXHM-Noj.js → login-DaC-wVpI.js} +8 -8
  27. package/public/assets/{queries-BMwc6EvM.js → queries-2E6-CboE.js} +12 -12
  28. package/public/assets/{trash-2-JF_uX2gE.js → trash-2--aH2iKg-.js} +1 -1
  29. package/public/index.html +2 -2
  30. package/public/assets/Rack-XtfiZ6GC.js +0 -147
  31. package/public/assets/_app-B8lqq_ks.js +0 -1
  32. package/public/assets/_app.accounts-DSMHSYBF.js +0 -51
  33. package/public/assets/_app.index-DXH5sH41.js +0 -61
  34. package/public/assets/_app.keys-DdEyVy_b.js +0 -39
  35. package/public/assets/_app.logs-Camxditb.js +0 -33
  36. package/public/assets/_app.models-CuD-JtGS.js +0 -144
  37. package/public/assets/index-BtjyVE1i.js +0 -170
package/gateway.js CHANGED
@@ -535,7 +535,7 @@ function flushChunk(parts, chunk) {
535
535
  }
536
536
  function pushCodeUnit(parts, chunk, codeUnit) {
537
537
  chunk.push(codeUnit);
538
- if (chunk.length >= CHUNK)
538
+ if (chunk.length >= CHUNK2)
539
539
  flushChunk(parts, chunk);
540
540
  }
541
541
  function pushCodePoint(parts, chunk, cp) {
@@ -660,8 +660,8 @@ function decodeUTF16LE(bytes) {
660
660
  }
661
661
  function decodeASCII(bytes) {
662
662
  const parts = [];
663
- for (let i = 0;i < bytes.length; i += CHUNK) {
664
- const end = Math.min(bytes.length, i + CHUNK);
663
+ for (let i = 0;i < bytes.length; i += CHUNK2) {
664
+ const end = Math.min(bytes.length, i + CHUNK2);
665
665
  const codes = new Array(end - i);
666
666
  for (let j = i, k2 = 0;j < end; j++, k2++) {
667
667
  codes[k2] = bytes[j] & 127;
@@ -672,8 +672,8 @@ function decodeASCII(bytes) {
672
672
  }
673
673
  function decodeLatin1(bytes) {
674
674
  const parts = [];
675
- for (let i = 0;i < bytes.length; i += CHUNK) {
676
- const end = Math.min(bytes.length, i + CHUNK);
675
+ for (let i = 0;i < bytes.length; i += CHUNK2) {
676
+ const end = Math.min(bytes.length, i + CHUNK2);
677
677
  const codes = new Array(end - i);
678
678
  for (let j = i, k2 = 0;j < end; j++, k2++) {
679
679
  codes[k2] = bytes[j];
@@ -689,7 +689,7 @@ function decodeWindows1252(bytes) {
689
689
  const b = bytes[i];
690
690
  const extra = b >= 128 && b <= 159 ? WINDOWS_1252_EXTRA[b] : undefined;
691
691
  out += extra !== null && extra !== undefined ? extra : String.fromCharCode(b);
692
- if (out.length >= CHUNK) {
692
+ if (out.length >= CHUNK2) {
693
693
  parts.push(out);
694
694
  out = "";
695
695
  }
@@ -698,7 +698,7 @@ function decodeWindows1252(bytes) {
698
698
  parts.push(out);
699
699
  return parts.join("");
700
700
  }
701
- var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder, CHUNK, REPLACEMENT = 65533;
701
+ var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder, CHUNK2, REPLACEMENT = 65533;
702
702
  var init_lib = __esm(() => {
703
703
  WINDOWS_1252_EXTRA = {
704
704
  128: "\u20AC",
@@ -733,7 +733,7 @@ var init_lib = __esm(() => {
733
733
  for (const [code, char] of Object.entries(WINDOWS_1252_EXTRA)) {
734
734
  WINDOWS_1252_REVERSE[char] = Number.parseInt(code, 10);
735
735
  }
736
- CHUNK = 32 * 1024;
736
+ CHUNK2 = 32 * 1024;
737
737
  });
738
738
 
739
739
  // node_modules/.bun/token-types@6.1.2/node_modules/token-types/lib/index.js
@@ -5414,6 +5414,23 @@ function formatLine(level, at, msg, fields, color) {
5414
5414
  }
5415
5415
  return parts.length === 0 ? head : `${head} ${parts.join(" ")}`;
5416
5416
  }
5417
+ var ANSI = /\u001B\[[0-9;]*m/g;
5418
+ var HEAD = /^(\S+) (DEBUG|INFO|WARN|ERROR) {1,2}(.*)$/;
5419
+ var FIELDS = / {2}(?=[a-zA-Z]+=)/;
5420
+ function parseLine(raw) {
5421
+ const absent = { raw, at: null, level: null, msg: null };
5422
+ const match = HEAD.exec(raw.replace(ANSI, ""));
5423
+ if (match === null)
5424
+ return absent;
5425
+ const [, instant, label, tail] = match;
5426
+ if (instant === undefined || label === undefined || tail === undefined)
5427
+ return absent;
5428
+ const at = Date.parse(instant);
5429
+ if (Number.isNaN(at))
5430
+ return absent;
5431
+ const msg = tail.split(FIELDS, 1)[0] ?? tail;
5432
+ return { raw, at, level: label.toLowerCase(), msg };
5433
+ }
5417
5434
  function createLogger(opts) {
5418
5435
  const threshold = LEVELS[opts.level];
5419
5436
  const now = opts.now ?? (() => Date.now());
@@ -5491,7 +5508,7 @@ function collect(events) {
5491
5508
  else if (ev.delta.type === "thinking" && acc.kind === "thinking")
5492
5509
  acc.text += ev.delta.text;
5493
5510
  else if (ev.delta.type === "thinkingSignature" && acc.kind === "thinking")
5494
- acc.signature = ev.delta.signature;
5511
+ acc.signature = (acc.signature ?? "") + ev.delta.signature;
5495
5512
  else if (ev.delta.type === "toolJson" && acc.kind === "toolUse")
5496
5513
  acc.json += ev.delta.partial;
5497
5514
  break;
@@ -5574,6 +5591,7 @@ function loadConfig(env) {
5574
5591
  const derivedBaseUrl = `http://${host}:${port}`;
5575
5592
  const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
5576
5593
  const staticDir = env.OMNI_STATIC_DIR?.trim();
5594
+ const logFile = env.OMNI_LOG_FILE?.trim();
5577
5595
  const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
5578
5596
  const logLevel = parseLogLevel(rawLogLevel);
5579
5597
  return {
@@ -5584,7 +5602,8 @@ function loadConfig(env) {
5584
5602
  databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
5585
5603
  encryptionKey,
5586
5604
  baseUrl,
5587
- staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
5605
+ staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
5606
+ logFile: logFile === undefined || logFile.length === 0 ? null : logFile
5588
5607
  };
5589
5608
  }
5590
5609
  // packages/providers/src/body.ts
@@ -6009,7 +6028,7 @@ async function* decodeAnthropic(messages) {
6009
6028
  if (cb.type === "text")
6010
6029
  yield { type: "blockStart", index, block: { type: "text" } };
6011
6030
  else if (cb.type === "thinking")
6012
- yield { type: "blockStart", index, block: { type: "thinking" } };
6031
+ yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
6013
6032
  else if (cb.type === "tool_use")
6014
6033
  yield {
6015
6034
  type: "blockStart",
@@ -6029,7 +6048,7 @@ async function* decodeAnthropic(messages) {
6029
6048
  index,
6030
6049
  delta: { type: "thinking", text: delta.thinking ?? "" }
6031
6050
  };
6032
- else if (delta.type === "signature_delta")
6051
+ else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
6033
6052
  yield {
6034
6053
  type: "blockDelta",
6035
6054
  index,
@@ -6101,6 +6120,9 @@ function wireCacheControl(c) {
6101
6120
  return {};
6102
6121
  return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
6103
6122
  }
6123
+ function isReplayableThinking(b) {
6124
+ return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
6125
+ }
6104
6126
  function encodeBlock(b) {
6105
6127
  const cache = wireCacheControl(cacheControlOf(b));
6106
6128
  switch (b.type) {
@@ -6130,6 +6152,16 @@ function encodeSystemTurn(content) {
6130
6152
  return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
6131
6153
  `);
6132
6154
  }
6155
+ function systemCacheControl(req) {
6156
+ const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
6157
+ const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
6158
+ const final = markedSystemBlocks.at(-1);
6159
+ const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
6160
+ return {
6161
+ ...promoted === undefined ? {} : { promoted },
6162
+ lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
6163
+ };
6164
+ }
6133
6165
  function encodeToolChoice(c) {
6134
6166
  switch (c.type) {
6135
6167
  case "auto":
@@ -6153,18 +6185,30 @@ function toWire(req, model, opts) {
6153
6185
  system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
6154
6186
  note("anthropic:oauth-system-prefix");
6155
6187
  }
6188
+ const systemCache = systemCacheControl(req);
6189
+ if (systemCache.lost)
6190
+ note("anthropic:system-turn-cache-control-dropped");
6156
6191
  const body = {
6157
6192
  model,
6158
- messages: req.messages.map((m) => {
6159
- if (m.role !== "system")
6160
- return { role: m.role, content: m.content.map(encodeBlock) };
6161
- if (m.content.some((b) => cacheControlOf(b) !== undefined)) {
6162
- note("anthropic:system-turn-cache-control-dropped");
6163
- }
6164
- return { role: m.role, content: encodeSystemTurn(m.content) };
6193
+ messages: req.messages.flatMap((m) => {
6194
+ if (m.role !== "system") {
6195
+ const replayable = m.content.filter(isReplayableThinking);
6196
+ if (replayable.length !== m.content.length)
6197
+ note("anthropic:unsigned-thinking-dropped");
6198
+ if (replayable.length === 0)
6199
+ return [];
6200
+ return [{ role: m.role, content: replayable.map(encodeBlock) }];
6201
+ }
6202
+ return [{ role: m.role, content: encodeSystemTurn(m.content) }];
6165
6203
  }),
6166
6204
  max_tokens: req.maxTokens ?? 4096,
6167
- stream: req.stream
6205
+ stream: req.stream,
6206
+ ...systemCache.promoted === undefined ? {} : {
6207
+ cache_control: {
6208
+ type: systemCache.promoted.type,
6209
+ ...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
6210
+ }
6211
+ }
6168
6212
  };
6169
6213
  if (system !== undefined && system.length > 0)
6170
6214
  body.system = system;
@@ -7527,6 +7571,62 @@ function createConnectFlows(deps) {
7527
7571
  }
7528
7572
  };
7529
7573
  }
7574
+ // packages/control/src/console.ts
7575
+ var UNIT_NAME = "omnigateway.service";
7576
+ var MAX_CONSOLE_LINES = 500;
7577
+ var DEFAULT_CONSOLE_LINES = 200;
7578
+ function resolveConsoleSource(input) {
7579
+ const path = input.logFile?.trim();
7580
+ if (path !== undefined && path.length > 0)
7581
+ return { kind: "file", path };
7582
+ if (input.unitInstalled)
7583
+ return { kind: "journal", unit: UNIT_NAME, scope: input.scope };
7584
+ return { kind: "none" };
7585
+ }
7586
+ function consoleLimit(requested) {
7587
+ const value = requested === undefined ? DEFAULT_CONSOLE_LINES : Number(requested);
7588
+ if (!Number.isFinite(value))
7589
+ return DEFAULT_CONSOLE_LINES;
7590
+ return Math.floor(Math.min(Math.max(1, value), MAX_CONSOLE_LINES));
7591
+ }
7592
+ function keep(line, query) {
7593
+ if (query.since !== undefined && (line.at === null || line.at <= query.since))
7594
+ return false;
7595
+ if (query.level === undefined || line.level === null)
7596
+ return true;
7597
+ return LEVELS[line.level] >= LEVELS[query.level];
7598
+ }
7599
+ var SCAN_FACTOR = 20;
7600
+ var SCAN_LIMIT = 5000;
7601
+ function scanWidth(query) {
7602
+ const filtered = query.level !== undefined || query.since !== undefined;
7603
+ return filtered ? Math.min(query.lines * SCAN_FACTOR, SCAN_LIMIT) : query.lines;
7604
+ }
7605
+ async function readSource(deps, source, lines) {
7606
+ if (source.kind === "none")
7607
+ return "";
7608
+ if (source.kind === "file")
7609
+ return deps.readFile(source.path, lines) ?? "";
7610
+ const scope = source.scope === "system" ? [] : ["--user"];
7611
+ const result = await deps.run([
7612
+ "journalctl",
7613
+ ...scope,
7614
+ "-u",
7615
+ source.unit,
7616
+ "-n",
7617
+ String(lines),
7618
+ "--no-pager",
7619
+ "--output=cat"
7620
+ ]);
7621
+ return result.code === 0 ? result.stdout : "";
7622
+ }
7623
+ async function readConsole(deps, source, query) {
7624
+ const limited = { ...query, lines: consoleLimit(query.lines) };
7625
+ const text = await readSource(deps, source, scanWidth(limited));
7626
+ const lines = text.split(`
7627
+ `).filter((raw) => raw.trim().length > 0).map(parseLine).filter((line) => keep(line, limited)).slice(-limited.lines);
7628
+ return source.kind === "file" ? { source: "file", path: source.path, lines } : { source: source.kind, lines };
7629
+ }
7530
7630
  // node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
7531
7631
  var exports_external = {};
7532
7632
  __export(exports_external, {
@@ -23689,6 +23789,43 @@ async function putSettings(store, input) {
23689
23789
  await store.config.putSettings(settings);
23690
23790
  return settings;
23691
23791
  }
23792
+ // packages/control/src/tail.ts
23793
+ import { closeSync, fstatSync, openSync, readSync, statSync } from "fs";
23794
+ var CHUNK = 64 * 1024;
23795
+ var MAX_BYTES = 8 * 1024 * 1024;
23796
+ function tailFile(path, lines) {
23797
+ let fd;
23798
+ try {
23799
+ fd = openSync(path, "r");
23800
+ } catch {
23801
+ return null;
23802
+ }
23803
+ try {
23804
+ const size = fstatSync(fd).size;
23805
+ if (size === 0)
23806
+ return "";
23807
+ const budget = Math.min(size, MAX_BYTES);
23808
+ let read = 0;
23809
+ let newlines = 0;
23810
+ const chunks = [];
23811
+ while (read < budget && newlines <= lines) {
23812
+ const span = Math.min(CHUNK, budget - read);
23813
+ const buffer = Buffer.allocUnsafe(span);
23814
+ const position = size - read - span;
23815
+ readSync(fd, buffer, 0, span, position);
23816
+ chunks.unshift(buffer);
23817
+ read += span;
23818
+ for (const byte of buffer)
23819
+ if (byte === 10)
23820
+ newlines++;
23821
+ }
23822
+ return Buffer.concat(chunks).toString("utf8");
23823
+ } catch {
23824
+ return null;
23825
+ } finally {
23826
+ closeSync(fd);
23827
+ }
23828
+ }
23692
23829
  // packages/control/src/usage.ts
23693
23830
  var MAX_LOG_LIMIT = 500;
23694
23831
  var DEFAULT_LOG_LIMIT = 100;
@@ -38299,7 +38436,9 @@ function adminRoutes(deps) {
38299
38436
  }).post("/api/setup", async ({ request: request2, set: set2 }) => {
38300
38437
  if (await deps.admin.isConfigured()) {
38301
38438
  set2.status = 409;
38302
- return { error: { code: "CONFLICT", message: "an admin password is already configured" } };
38439
+ return {
38440
+ error: { code: "CONFLICT", message: "an admin password is already configured" }
38441
+ };
38303
38442
  }
38304
38443
  const body2 = await readJsonRecord(request2);
38305
38444
  if (typeof body2?.password !== "string") {
@@ -38313,7 +38452,9 @@ function adminRoutes(deps) {
38313
38452
  }
38314
38453
  if (!created) {
38315
38454
  set2.status = 409;
38316
- return { error: { code: "CONFLICT", message: "an admin password is already configured" } };
38455
+ return {
38456
+ error: { code: "CONFLICT", message: "an admin password is already configured" }
38457
+ };
38317
38458
  }
38318
38459
  const token = await deps.admin.login(body2.password);
38319
38460
  if (token === null)
@@ -38396,6 +38537,17 @@ function adminRoutes(deps) {
38396
38537
  }).get("/api/logs", async ({ request: request2, query }) => {
38397
38538
  await requireAdmin(request2, deps.admin);
38398
38539
  return { logs: await recentLogs(deps.store, query.limit) };
38540
+ }).get("/api/console", async ({ request: request2, query }) => {
38541
+ await requireAdmin(request2, deps.admin);
38542
+ if (deps.console === undefined)
38543
+ return { source: "none", lines: [] };
38544
+ const level = parseLogLevel(query.level);
38545
+ const since = Number(query.since);
38546
+ return readConsole(deps.console.deps, deps.console.source, {
38547
+ lines: consoleLimit(query.lines),
38548
+ ...level === null ? {} : { level },
38549
+ ...Number.isFinite(since) && since > 0 ? { since } : {}
38550
+ });
38399
38551
  });
38400
38552
  }
38401
38553
 
@@ -38941,6 +39093,9 @@ var frame = (event, data) => ({
38941
39093
  });
38942
39094
  async function* anthropicStream(events, requestId) {
38943
39095
  let model = "";
39096
+ const suppressed = new Set;
39097
+ const outIndex = new Map;
39098
+ let nextOutIndex = 0;
38944
39099
  for await (const event of events) {
38945
39100
  switch (event.type) {
38946
39101
  case "start":
@@ -38966,26 +39121,39 @@ async function* anthropicStream(events, requestId) {
38966
39121
  break;
38967
39122
  case "blockStart": {
38968
39123
  const b = event.block;
39124
+ if (b.type === "thinking" && b.signed !== true) {
39125
+ suppressed.add(event.index);
39126
+ break;
39127
+ }
38969
39128
  const content_block = b.type === "text" ? { type: "text", text: "" } : b.type === "thinking" ? { type: "thinking", thinking: "" } : { type: "tool_use", id: b.id, name: b.name, input: {} };
39129
+ const index = nextOutIndex++;
39130
+ outIndex.set(event.index, index);
38970
39131
  yield frame("content_block_start", {
38971
39132
  type: "content_block_start",
38972
- index: event.index,
39133
+ index,
38973
39134
  content_block
38974
39135
  });
38975
39136
  break;
38976
39137
  }
38977
39138
  case "blockDelta": {
39139
+ if (suppressed.has(event.index))
39140
+ break;
38978
39141
  const d = event.delta;
38979
39142
  const delta2 = d.type === "text" ? { type: "text_delta", text: d.text } : d.type === "thinking" ? { type: "thinking_delta", thinking: d.text } : d.type === "thinkingSignature" ? { type: "signature_delta", signature: d.signature } : { type: "input_json_delta", partial_json: d.partial };
38980
39143
  yield frame("content_block_delta", {
38981
39144
  type: "content_block_delta",
38982
- index: event.index,
39145
+ index: outIndex.get(event.index) ?? event.index,
38983
39146
  delta: delta2
38984
39147
  });
38985
39148
  break;
38986
39149
  }
38987
39150
  case "blockEnd":
38988
- yield frame("content_block_stop", { type: "content_block_stop", index: event.index });
39151
+ if (suppressed.has(event.index))
39152
+ break;
39153
+ yield frame("content_block_stop", {
39154
+ type: "content_block_stop",
39155
+ index: outIndex.get(event.index) ?? event.index
39156
+ });
38989
39157
  break;
38990
39158
  case "end":
38991
39159
  yield frame("message_delta", {
@@ -39015,7 +39183,7 @@ function anthropicResponse(collected, requestId) {
39015
39183
  type: "message",
39016
39184
  role: "assistant",
39017
39185
  model: collected.model,
39018
- content: collected.content.map((b) => {
39186
+ content: collected.content.filter((b) => b.type !== "thinking" || b.signature !== undefined && b.signature !== "").map((b) => {
39019
39187
  switch (b.type) {
39020
39188
  case "text":
39021
39189
  return { type: "text", text: b.text };
@@ -39808,32 +39976,6 @@ async function handle(deps, rateLimiter, surface, request2) {
39808
39976
  completed.durationMs = deps.now() - startedAt;
39809
39977
  }
39810
39978
  await finishLog(deps.store, completed, keyId, deps.logger);
39811
- const fields = {
39812
- requestId,
39813
- surface,
39814
- status: completed.status,
39815
- provider: completed.resolvedProvider ?? undefined,
39816
- model: completed.resolvedModel ?? undefined,
39817
- requestedModel: completed.requestedModel,
39818
- credentialId: completed.credentialId ?? undefined,
39819
- apiKeyId: keyId ?? undefined,
39820
- attempts: completed.attempts,
39821
- code: completed.errorCode ?? undefined,
39822
- stream: chatRequest.stream,
39823
- inputTokens: completed.inputTokens,
39824
- outputTokens: completed.outputTokens,
39825
- cacheReadTokens: completed.cacheReadTokens,
39826
- cacheWriteTokens: completed.cacheWriteTokens,
39827
- costUsd: completed.costUsd,
39828
- ttftMs: completed.ttftMs,
39829
- durationMs: completed.durationMs
39830
- };
39831
- if (wasCancelled)
39832
- deps.logger.debug("request cancelled", fields);
39833
- else if (completed.status >= 400)
39834
- deps.logger.error("request failed", fields);
39835
- else
39836
- deps.logger.info("request done", fields);
39837
39979
  };
39838
39980
  if (chatRequest.stream) {
39839
39981
  const frames = surface === "anthropic" ? anthropicStream(outcome.events, requestId) : openaiStream(outcome.events, requestId, Math.floor(deps.now() / 1000));
@@ -39861,15 +40003,11 @@ async function handle(deps, rateLimiter, surface, request2) {
39861
40003
  durationMs: deps.now() - startedAt
39862
40004
  });
39863
40005
  await finishLog(deps.store, completed, keyId, deps.logger);
39864
- deps.logger.error("request failed", {
40006
+ deps.logger.warn("request rejected", {
39865
40007
  requestId,
39866
40008
  surface,
39867
40009
  status: completed.status,
39868
- requestedModel,
39869
- apiKeyId: keyId ?? undefined,
39870
40010
  code: gatewayError.code,
39871
- attempts: completed.attempts,
39872
- durationMs: completed.durationMs,
39873
40011
  reason: gatewayError.message
39874
40012
  });
39875
40013
  return errorResponse(surface, gatewayError.code, gatewayError.message);
@@ -39953,7 +40091,8 @@ function createApp(deps) {
39953
40091
  admin,
39954
40092
  now,
39955
40093
  sessionTtlMs: ADMIN_SESSION_TTL_MS,
39956
- logger: logger2
40094
+ logger: logger2,
40095
+ ...deps.console === undefined ? {} : { console: deps.console }
39957
40096
  })).use(connectRoutes({
39958
40097
  store: deps.store,
39959
40098
  admin,
@@ -40126,6 +40265,30 @@ function stdoutLogger(level) {
40126
40265
  });
40127
40266
  }
40128
40267
  var logger2 = stdoutLogger("info");
40268
+ function consoleSource(logFile) {
40269
+ const source = resolveConsoleSource({
40270
+ logFile,
40271
+ unitInstalled: process.env.JOURNAL_STREAM !== undefined,
40272
+ scope: process.env.MANAGERPID === undefined ? "system" : "user"
40273
+ });
40274
+ return {
40275
+ source,
40276
+ deps: {
40277
+ readFile: (path, lines) => tailFile(path, lines),
40278
+ run: async (argv) => {
40279
+ const [cmd, ...args] = argv;
40280
+ if (cmd === undefined)
40281
+ return { code: 1, stdout: "", stderr: "empty argv" };
40282
+ const proc = Bun.spawn([cmd, ...args], { stdout: "pipe", stderr: "pipe" });
40283
+ const [stdout, stderr] = await Promise.all([
40284
+ new Response(proc.stdout).text(),
40285
+ new Response(proc.stderr).text()
40286
+ ]);
40287
+ return { code: await proc.exited, stdout, stderr };
40288
+ }
40289
+ }
40290
+ };
40291
+ }
40129
40292
  async function main() {
40130
40293
  const config2 = loadConfig(process.env);
40131
40294
  logger2 = stdoutLogger(config2.logLevel);
@@ -40151,6 +40314,8 @@ async function main() {
40151
40314
  const swept = await store.usage.sweepPending();
40152
40315
  if (swept > 0)
40153
40316
  logger2.info("retired interrupted requests", { count: swept });
40317
+ const console2 = consoleSource(config2.logFile);
40318
+ logger2.info("console log source resolved", { reason: console2.source.kind });
40154
40319
  const now = () => Date.now();
40155
40320
  const http = nodeHttpClient({ logger: logger2, now });
40156
40321
  const refresh = createRefresher({ store, providers: OAUTH_PROVIDERS, http, now, logger: logger2 });
@@ -40165,7 +40330,8 @@ async function main() {
40165
40330
  now,
40166
40331
  refresh,
40167
40332
  staticDir,
40168
- logger: logger2
40333
+ logger: logger2,
40334
+ console: console2
40169
40335
  });
40170
40336
  const stopMaintenance = startMaintenance({ store, now, logger: logger2 });
40171
40337
  const stopRefreshScheduler = startRefreshScheduler({ store, refresh, now, logger: logger2 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnigateway",
3
- "version": "0.1.6",
3
+ "version": "0.1.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>",
@@ -1,4 +1,4 @@
1
- import{Ot as e,V as t}from"./queries-BMwc6EvM.js";var n=e(),r={neutral:{fg:`var(--ink-dim)`,bg:`transparent`},ok:{fg:`var(--ok)`,bg:`var(--ok-wash)`},warn:{fg:`var(--warn)`,bg:`var(--warn-wash)`},down:{fg:`var(--down)`,bg:`var(--down-wash)`},accent:{fg:`var(--accent)`,bg:`var(--accent-wash)`}},i=t.span`
1
+ import{W as e,jt as t}from"./queries-2E6-CboE.js";var n=t(),r={neutral:{fg:`var(--ink-dim)`,bg:`transparent`},ok:{fg:`var(--ok)`,bg:`var(--ok-wash)`},warn:{fg:`var(--warn)`,bg:`var(--warn-wash)`},down:{fg:`var(--down)`,bg:`var(--down-wash)`},accent:{fg:`var(--accent)`,bg:`var(--accent-wash)`}},i=e.span`
2
2
  display: inline-flex;
3
3
  align-items: center;
4
4
  gap: 4px;
@@ -12,7 +12,7 @@ import{Ot as e,V as t}from"./queries-BMwc6EvM.js";var n=e(),r={neutral:{fg:`var(
12
12
  font-size: 10.5px;
13
13
  letter-spacing: 0.02em;
14
14
  white-space: nowrap;
15
- `,a=t.span`
15
+ `,a=e.span`
16
16
  display: inline-flex;
17
17
  align-items: center;
18
18
  gap: 5px;
@@ -1,4 +1,4 @@
1
- import{L as e,N as t,Ot as n,V as r,kt as i,qt as a}from"./queries-BMwc6EvM.js";import{m as o}from"./Rack-XtfiZ6GC.js";var s=o(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),c=o(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),l=a(i(),1);function u(e){if(e===null||typeof getSelection!=`function`)return!1;let t=getSelection();if(t===null)return!1;try{let n=document.createRange();return n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),!0}catch{return!1}}async function d(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=u(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var f=n(),p=r.div`
1
+ import{B as e,I as t,Mt as n,W as r,Xt as i,jt as a}from"./queries-2E6-CboE.js";import{m as o}from"./Rack-CMqGBP4I.js";var s=o(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),c=o(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),l=i(n(),1);function u(e){if(e===null||typeof getSelection!=`function`)return!1;let t=getSelection();if(t===null)return!1;try{let n=document.createRange();return n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),!0}catch{return!1}}async function d(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=u(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var f=a(),p=r.div`
2
2
  display: flex;
3
3
  align-items: stretch;
4
4
  gap: 0;
@@ -1,4 +1,4 @@
1
- import{O as e,Ot as t,V as n,kt as r,qt as i}from"./queries-BMwc6EvM.js";var a=i(r(),1),o=t(),s=`
1
+ import{A as e,Mt as t,W as n,Xt as r,jt as i}from"./queries-2E6-CboE.js";var a=r(t(),1),o=i(),s=`
2
2
  width: 100%;
3
3
  border-radius: 3px;
4
4
  transition: border-color 120ms ease;
@@ -1,4 +1,4 @@
1
- import{B as e,Ot as t,R as n,V as r}from"./queries-BMwc6EvM.js";function i(e,t){if(e.length===0)return null;let n=[...e].sort((e,t)=>e-t);return n[Math.min(n.length-1,Math.max(0,Math.ceil(t*n.length)-1))]??null}function a(e){return e.state===`pending`}function o(e){return e.filter(e=>!a(e))}function s(e){return a(e)?!1:e.status>=400||e.errorCode!==null}var c={ok:`●`,warn:`◐`,down:`○`,idle:`·`,live:`●`};function l(e){return a(e)?`live`:s(e)?`down`:`ok`}function u(e){return a(e)?`in flight`:s(e)?`failed with ${e.status}`:`succeeded`}function d(e,t){let n=o(e),r=[],a=0,c=0,l=0,u=0,d=0;for(let e of n)s(e)&&(a+=1),e.ttftMs!==null&&Number.isFinite(e.ttftMs)&&r.push(e.ttftMs),c+=e.costUsd,l+=e.inputTokens,u+=e.outputTokens,d+=e.cacheReadTokens;let f=t/6e4;return{requests:n.length,errors:a,errorRate:n.length===0?0:a/n.length,ratePerMin:f<=0?0:n.length/f,ttftP50:i(r,.5),ttftP95:i(r,.95),costUsd:c,inputTokens:l,outputTokens:u,cacheReadTokens:d}}function f(e,t){let n=o(e),{now:r,spanMs:a,count:c}=t,l=a/c,u=r-a,d=Array.from({length:c},(e,t)=>({at:u+t*l,total:0,errors:0,costUsd:0,ttftMs:null})),f=Array.from({length:c},()=>[]);for(let e of n){if(e.at<u||e.at>r)continue;let t=Math.min(c-1,Math.floor((e.at-u)/l)),n=d[t];n!==void 0&&(n.total+=1,n.costUsd+=e.costUsd,s(e)&&(n.errors+=1),e.ttftMs!==null&&Number.isFinite(e.ttftMs)&&f[t]?.push(e.ttftMs))}for(let[e,t]of d.entries())t.ttftMs=i(f[e]??[],.5);return d}var p={tokenRejected:`reconnect needed — provider rejected the refresh token`,expiredNoRefresh:`reconnect needed — token expired with nothing to refresh from`};function m(e,t,n,r=null){if(!n){let e=r===`tokenRejected`||r===`expiredNoRefresh`;return{state:e?`down`:`idle`,note:e?p[r]:`disabled`,ttftMs:null,lastUsedAt:null,consecutiveFailures:0}}if(e.length===0)return{state:`idle`,note:`unused`,ttftMs:null,lastUsedAt:null,consecutiveFailures:0};let i=`ok`,a=``,o=null,s=null,c=0;for(let n of e){if(n.ewmaTtftMs!==null&&(o=o===null?n.ewmaTtftMs:Math.max(o,n.ewmaTtftMs)),n.lastUsedAt!==null&&(s=s===null?n.lastUsedAt:Math.max(s,n.lastUsedAt)),c=Math.max(c,n.consecutiveFailures),n.breakerState===`open`){i=`down`,a=`breaker open`;continue}if(n.rateLimitedUntil!==null&&n.rateLimitedUntil>t&&i!==`down`){i=`warn`,a=`rate limited`;continue}n.breakerState===`halfOpen`&&i===`ok`&&(i=`warn`,a=`probing`)}return{state:i,note:a,ttftMs:o,lastUsedAt:s,consecutiveFailures:c}}var h={fiveHour:`5h`,daily:`24h`,weekly:`7d`};function g(e){return Math.max(e,6e4)*3}function _(e,t,n){return e.observedAt<=0||t-e.observedAt>g(n)}function v(e,t,n,r){let i=h[e.windowType];return e.observedAt<=0?`${i} · never observed`:_(e,t,n)?`${i} · stale, read ${r(e.observedAt,t)}`:e.resetsAt===null?i:`${i} · resets ${r(e.resetsAt,t)}`}var y={fiveHour:0,daily:1,weekly:2};function b(e){return e.filter(e=>e.limit!==null&&e.limit>0).map(e=>({window:e,fraction:Math.min(1,e.used/e.limit)})).sort((e,t)=>y[e.window.windowType]-y[t.window.windowType])}function x(e,t){let n=new Map;for(let r of e){let e=t(r),i=n.get(e);i===void 0?n.set(e,[r]):i.push(r)}return n}var S=t(),C=n`
1
+ import{U as e,V as t,W as n,jt as r}from"./queries-2E6-CboE.js";function i(e,t){if(e.length===0)return null;let n=[...e].sort((e,t)=>e-t);return n[Math.min(n.length-1,Math.max(0,Math.ceil(t*n.length)-1))]??null}function a(e){return e.state===`pending`}function o(e){return e.filter(e=>!a(e))}function s(e){return a(e)?!1:e.status>=400||e.errorCode!==null}var c={ok:`●`,warn:`◐`,down:`○`,idle:`·`,live:`●`};function l(e){return a(e)?`live`:s(e)?`down`:`ok`}function u(e){return a(e)?`in flight`:s(e)?`failed with ${e.status}`:`succeeded`}function d(e,t){let n=o(e),r=[],a=0,c=0,l=0,u=0,d=0;for(let e of n)s(e)&&(a+=1),e.ttftMs!==null&&Number.isFinite(e.ttftMs)&&r.push(e.ttftMs),c+=e.costUsd,l+=e.inputTokens,u+=e.outputTokens,d+=e.cacheReadTokens;let f=t/6e4;return{requests:n.length,errors:a,errorRate:n.length===0?0:a/n.length,ratePerMin:f<=0?0:n.length/f,ttftP50:i(r,.5),ttftP95:i(r,.95),costUsd:c,inputTokens:l,outputTokens:u,cacheReadTokens:d}}function f(e,t){let n=o(e),{now:r,spanMs:a,count:c}=t,l=a/c,u=r-a,d=Array.from({length:c},(e,t)=>({at:u+t*l,total:0,errors:0,costUsd:0,ttftMs:null})),f=Array.from({length:c},()=>[]);for(let e of n){if(e.at<u||e.at>r)continue;let t=Math.min(c-1,Math.floor((e.at-u)/l)),n=d[t];n!==void 0&&(n.total+=1,n.costUsd+=e.costUsd,s(e)&&(n.errors+=1),e.ttftMs!==null&&Number.isFinite(e.ttftMs)&&f[t]?.push(e.ttftMs))}for(let[e,t]of d.entries())t.ttftMs=i(f[e]??[],.5);return d}var p={tokenRejected:`reconnect needed — provider rejected the refresh token`,expiredNoRefresh:`reconnect needed — token expired with nothing to refresh from`};function m(e,t,n,r=null){if(!n){let e=r===`tokenRejected`||r===`expiredNoRefresh`;return{state:e?`down`:`idle`,note:e?p[r]:`disabled`,ttftMs:null,lastUsedAt:null,consecutiveFailures:0}}if(e.length===0)return{state:`idle`,note:`unused`,ttftMs:null,lastUsedAt:null,consecutiveFailures:0};let i=`ok`,a=``,o=null,s=null,c=0;for(let n of e){if(n.ewmaTtftMs!==null&&(o=o===null?n.ewmaTtftMs:Math.max(o,n.ewmaTtftMs)),n.lastUsedAt!==null&&(s=s===null?n.lastUsedAt:Math.max(s,n.lastUsedAt)),c=Math.max(c,n.consecutiveFailures),n.breakerState===`open`){i=`down`,a=`breaker open`;continue}if(n.rateLimitedUntil!==null&&n.rateLimitedUntil>t&&i!==`down`){i=`warn`,a=`rate limited`;continue}n.breakerState===`halfOpen`&&i===`ok`&&(i=`warn`,a=`probing`)}return{state:i,note:a,ttftMs:o,lastUsedAt:s,consecutiveFailures:c}}var h={fiveHour:`5h`,daily:`24h`,weekly:`7d`};function g(e){return Math.max(e,6e4)*3}function _(e,t,n){return e.observedAt<=0||t-e.observedAt>g(n)}function v(e,t,n,r){let i=h[e.windowType];return e.observedAt<=0?`${i} · never observed`:_(e,t,n)?`${i} · stale, read ${r(e.observedAt,t)}`:e.resetsAt===null?i:`${i} · resets ${r(e.resetsAt,t)}`}var y={fiveHour:0,daily:1,weekly:2};function b(e){return e.filter(e=>e.limit!==null&&e.limit>0).map(e=>({window:e,fraction:Math.min(1,e.used/e.limit)})).sort((e,t)=>y[e.window.windowType]-y[t.window.windowType])}function x(e,t){let n=new Map;for(let r of e){let e=t(r),i=n.get(e);i===void 0?n.set(e,[r]):i.push(r)}return n}var S=r(),C=t`
2
2
  0%, 100% { opacity: 1; }
3
3
  50% { opacity: 0.28; }
4
4
  `,w=e`
@@ -16,7 +16,7 @@ import{B as e,Ot as t,R as n,V as r}from"./queries-BMwc6EvM.js";function i(e,t){
16
16
  color: ${({theme:e})=>e.color.down};
17
17
  `,idle:e`
18
18
  color: ${({theme:e})=>e.color.inkFaint};
19
- `,live:w},E=r.span`
19
+ `,live:w},E=n.span`
20
20
  font-family: ${({theme:e})=>e.font.mono};
21
21
  font-size: 12px;
22
22
  line-height: 1;
@@ -1,4 +1,4 @@
1
- import{Ot as e,V as t}from"./queries-BMwc6EvM.js";var n=e(),r=t.div`
1
+ import{W as e,jt as t}from"./queries-2E6-CboE.js";var n=t(),r=e.div`
2
2
  position: relative;
3
3
  width: 100%;
4
4
  height: ${({$height:e})=>e}px;
@@ -6,7 +6,7 @@ import{Ot as e,V as t}from"./queries-BMwc6EvM.js";var n=e(),r=t.div`
6
6
  border: 1px solid ${({theme:e})=>e.color.rule};
7
7
  border-radius: 2px;
8
8
  overflow: hidden;
9
- `,i=t.div`
9
+ `,i=e.div`
10
10
  height: 100%;
11
11
  width: ${({$fraction:e})=>`${Math.max(0,Math.min(1,e))*100}%`};
12
12
  background: ${({$tone:e})=>e};