omnigateway 0.8.0 → 0.8.1

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 (39) hide show
  1. package/README.md +18 -4
  2. package/bin/omni.js +90 -21
  3. package/gateway.js +93 -21
  4. package/package.json +1 -1
  5. package/public/assets/{Chip-DG-mqRx0.js → Chip-BZWa7onX.js} +1 -1
  6. package/public/assets/{CopyValue-CItB094V.js → CopyValue-Cs2JUZQD.js} +1 -1
  7. package/public/assets/{Lamp-Cxp-OxUq.js → Lamp-BHYJpauE.js} +1 -1
  8. package/public/assets/{Rack-xBT_6EtM.js → Rack-CRuJZY00.js} +1 -1
  9. package/public/assets/{RequestTable-CM9mPiMF.js → RequestTable-D9hFf4o5.js} +1 -1
  10. package/public/assets/{SummaryDeck-C3ZwZ3ng.js → SummaryDeck-D29h0rFx.js} +1 -1
  11. package/public/assets/{Toggle-CmXXn6F3.js → Toggle-BIHUR4pS.js} +1 -1
  12. package/public/assets/{TokenBreakdown-COJ643vg.js → TokenBreakdown-D5mQFSfo.js} +1 -1
  13. package/public/assets/{WindowChart-BrLf4KCu.js → WindowChart-DVl7kwqb.js} +1 -1
  14. package/public/assets/{_app.accounts-B-OYO2Yi.js → _app.accounts-C3Yv991L.js} +1 -1
  15. package/public/assets/_app.console-Bvo2fugd.js +44 -0
  16. package/public/assets/_app.database-CfWNOIl-.js +24 -0
  17. package/public/assets/{_app.index-CB3UFRwF.js → _app.index-CgI1OrZR.js} +1 -1
  18. package/public/assets/{_app.keys-B89CMo3x.js → _app.keys-CcfPhvCD.js} +1 -1
  19. package/public/assets/{_app.logs-CNAnRW07.js → _app.logs-C8ZiArCn.js} +1 -1
  20. package/public/assets/{_app.models-DpRcGhag.js → _app.models-nfMWzVb4.js} +1 -1
  21. package/public/assets/_app.plugins._pluginId-ByykviWs.js +2 -0
  22. package/public/assets/{_app.plugins._pluginId-sYDTZ9Px.js → _app.plugins._pluginId-CE8Jknhs.js} +1 -1
  23. package/public/assets/{_app.settings-BV085FpM.js → _app.settings-C45FJLeX.js} +1 -1
  24. package/public/assets/{_app.usage-CGuOdYun.js → _app.usage-C5s-DfBV.js} +1 -1
  25. package/public/assets/{chevron-right-BeVtzzum.js → chevron-right-B7JOx5ra.js} +1 -1
  26. package/public/assets/{client-Mf5f5R9Z.js → client-DvN2dbYV.js} +1 -1
  27. package/public/assets/{index-3lpol4cr.js → index-BW1O9p6N.js} +3 -3
  28. package/public/assets/{login-CwYbEKWH.js → login-9Ovj_4DV.js} +2 -2
  29. package/public/assets/{login-BpvnkW6i.js → login-DvRXHEGv.js} +1 -1
  30. package/public/assets/plus-64jUPIDJ.js +1 -0
  31. package/public/assets/{preload-helper-8jyVfd2a.js → preload-helper-D2sCuvIY.js} +1 -1
  32. package/public/assets/{shared-B2Lmnazz.js → shared-BKWNmsjH.js} +1 -1
  33. package/public/assets/{stream-VJMOV03S.js → stream-BUWwtwme.js} +1 -1
  34. package/public/assets/{trash-2-CbjLfrko.js → trash-2-rvGFhAn8.js} +1 -1
  35. package/public/index.html +7 -7
  36. package/public/assets/_app.console-BOZ1wncA.js +0 -44
  37. package/public/assets/_app.database-B5oHZPbu.js +0 -24
  38. package/public/assets/_app.plugins._pluginId-LzBq-Zjj.js +0 -2
  39. package/public/assets/plus-nHMFjQ2I.js +0 -1
package/README.md CHANGED
@@ -516,6 +516,15 @@ bun apps/gateway/src/index.ts >> /var/log/omni.log 2>&1
516
516
  `omni start` does both for the gateway it supervises, and under systemd the journal needs no
517
517
  setup.
518
518
 
519
+ In a fleet, capture is per process. Each replica can capture its own — tee its stdout to a file
520
+ inside the container and point `OMNI_LOG_FILE` at the same path — and the Console screen then
521
+ merges every process's tail and lets you pick one. That is worth having for an incident on a
522
+ running pod, and it is not a log stack: the file dies with the container, nothing rotates it, and
523
+ the screen reads one process at a time. Ship stdout to a collector for anything beyond that —
524
+ Elasticsearch and Kibana, Loki and Grafana, or whatever already reads your containers — where the
525
+ lines outlive the process that wrote them and can be searched across all of them at once. The
526
+ Console screen says so itself when it finds a fleet capturing nothing.
527
+
519
528
  Everything else lives in the database rather than the environment, so it can be
520
529
  changed without a restart: the six routing weights, `maxAttempts`,
521
530
  `requestDeadlineMs`, the circuit breaker's `breakerThreshold` and
@@ -641,9 +650,12 @@ format, structural bounds, and masking rules.
641
650
  ## Snapshots and restore
642
651
 
643
652
  The console's Database screen reports what this installation occupies — the
644
- database file, its write-ahead log, the captured-body tree, and the free pages a
645
- compaction would give back and takes snapshots. `omni db stats` prints the same
646
- figures.
653
+ database file, its write-ahead log, the captured-body tree, the free pages a
654
+ compaction would give back, and every table by size and takes snapshots.
655
+ `omni db stats` prints the same figures. On Postgres both show the server's own
656
+ size, the `request_bodies` table and the per-table listing instead; there is no
657
+ file, so nothing here compacts, snapshots or restores it — `pg_dump` is the
658
+ backup.
647
659
 
648
660
  **What a snapshot is.** One self-contained SQLite file, written into a
649
661
  `snapshots/` directory beside the database. The write-ahead log is folded in, so
@@ -737,7 +749,9 @@ and the workflow commits that version into `k8s/kustomization.yaml` on `main`, w
737
749
  CD syncs. Rolling back is editing `newTag` by hand.
738
750
 
739
751
  Plugins in a fleet are baked into the image so every replica holds the same
740
- set: `COPY plugins/ /data/plugins/` in a derived Dockerfile.
752
+ set: `COPY plugins/ /data/plugins/` in a derived Dockerfile. The image is
753
+ public, so no pull secret is configured; a private fork adds
754
+ `imagePullSecrets` to the Deployment.
741
755
 
742
756
  Give the container a restart policy — `--restart unless-stopped` — if you want a
743
757
  restart request to bring it back. A container cannot read its own policy, so
package/bin/omni.js CHANGED
@@ -16281,6 +16281,23 @@ function createMaintenanceRepo(sql, nodeId) {
16281
16281
  schemaVersion: row?.version ?? 0
16282
16282
  };
16283
16283
  },
16284
+ async tables() {
16285
+ const rows = await sql.unsafe(`SELECT c.relname AS name,
16286
+ pg_total_relation_size(c.oid) AS bytes,
16287
+ GREATEST(c.reltuples, 0)::bigint AS rows,
16288
+ COALESCE(s.n_dead_tup, 0) AS dead_rows
16289
+ FROM pg_class c
16290
+ JOIN pg_namespace n ON n.oid = c.relnamespace
16291
+ LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid
16292
+ WHERE c.relkind = 'r' AND n.nspname = current_schema()
16293
+ ORDER BY bytes DESC, name`);
16294
+ return rows.map((row) => ({
16295
+ name: row.name,
16296
+ bytes: num(row.bytes),
16297
+ rows: num(row.rows),
16298
+ deadRows: num(row.dead_rows)
16299
+ }));
16300
+ },
16284
16301
  vacuum: async () => unsupported(),
16285
16302
  snapshotTo: async () => unsupported(),
16286
16303
  inspect: async () => unsupported()
@@ -17219,6 +17236,7 @@ async function createPostgresStore(opts) {
17219
17236
  });
17220
17237
  };
17221
17238
  return {
17239
+ engine: "postgres",
17222
17240
  databasePath: redacted(opts.url),
17223
17241
  credentials: createCredentialRepo(sql, opts.encryptionKey, emit2),
17224
17242
  config: createConfigRepo(sql, emit2),
@@ -18401,6 +18419,15 @@ function createMaintenanceRepo2(db, nodeId) {
18401
18419
  schemaVersion: version2?.version ?? 0
18402
18420
  };
18403
18421
  },
18422
+ async tables() {
18423
+ return db.query(`SELECT m.tbl_name AS name,
18424
+ SUM(s.pgsize) AS bytes,
18425
+ SUM(CASE WHEN s.pagetype = 'leaf' AND s.name = m.tbl_name THEN s.ncell ELSE 0 END) AS rows
18426
+ FROM dbstat s JOIN sqlite_master m ON m.name = s.name
18427
+ WHERE m.tbl_name NOT LIKE 'sqlite_%'
18428
+ GROUP BY m.tbl_name
18429
+ ORDER BY bytes DESC, name`).all().map((row) => ({ ...row, deadRows: null }));
18430
+ },
18404
18431
  async vacuum() {
18405
18432
  db.run("VACUUM");
18406
18433
  db.run("PRAGMA wal_checkpoint(TRUNCATE)");
@@ -18872,6 +18899,7 @@ async function createStore(opts) {
18872
18899
  live = false;
18873
18900
  };
18874
18901
  return {
18902
+ engine: "sqlite",
18875
18903
  databasePath: opts.path,
18876
18904
  credentials: {
18877
18905
  list: () => handle.credentials.list(),
@@ -18936,6 +18964,7 @@ async function createStore(opts) {
18936
18964
  },
18937
18965
  maintenance: {
18938
18966
  stats: () => handle.maintenance.stats(),
18967
+ tables: () => handle.maintenance.tables(),
18939
18968
  heartbeat: (now) => handle.maintenance.heartbeat(now),
18940
18969
  nodes: (now) => handle.maintenance.nodes(now),
18941
18970
  vacuum: () => handle.maintenance.vacuum(),
@@ -20975,13 +21004,13 @@ function encodeSystemTurn(content, cloak) {
20975
21004
  return content.map((block) => encodeBlock(block, cloak));
20976
21005
  }
20977
21006
  function systemCacheControl(req) {
20978
- const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
20979
- const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
20980
- const final = markedSystemBlocks.at(-1);
20981
- const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
21007
+ const markedSystemBlocks = req.messages.flatMap((message) => message.role === "system" ? message.content.filter((block) => cacheControlOf(block) !== undefined) : []);
21008
+ const trailing = req.messages.at(-1);
21009
+ const marked = trailing?.role === "system" ? trailing.content.findLast((block) => cacheControlOf(block) !== undefined) : undefined;
21010
+ const retarget = marked === undefined ? undefined : cacheControlOf(marked);
20982
21011
  return {
20983
- ...promoted === undefined ? {} : { promoted },
20984
- lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
21012
+ ...retarget === undefined ? {} : { retarget },
21013
+ lost: markedSystemBlocks.length > (retarget === undefined ? 0 : 1)
20985
21014
  };
20986
21015
  }
20987
21016
  function encodeTool(t, cloak) {
@@ -21143,14 +21172,26 @@ function toWire(req, model, opts) {
21143
21172
  return [{ role: m.role, content: encodeSystemTurn(m.content, cloak) }];
21144
21173
  }),
21145
21174
  max_tokens: req.maxTokens ?? 4096,
21146
- stream: req.stream,
21147
- ...systemCache.promoted === undefined ? {} : {
21148
- cache_control: {
21149
- type: systemCache.promoted.type,
21150
- ...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
21175
+ stream: req.stream
21176
+ };
21177
+ if (systemCache.retarget !== undefined) {
21178
+ const trailing = body.messages.at(-1);
21179
+ if (isRecord(trailing) && Array.isArray(trailing.content)) {
21180
+ for (const block of trailing.content) {
21181
+ if (isRecord(block))
21182
+ delete block.cache_control;
21151
21183
  }
21152
21184
  }
21153
- };
21185
+ if (!hasVendorCacheControl(req)) {
21186
+ const target = lastCacheableHistoryBlock(body.messages.slice(0, -1));
21187
+ if (target === undefined) {
21188
+ note2("anthropic:system-turn-cache-control-dropped");
21189
+ } else if (target.cache_control === undefined) {
21190
+ Object.assign(target, wireCacheControl(systemCache.retarget));
21191
+ note2("anthropic:system-turn-cache-control-retargeted");
21192
+ }
21193
+ }
21194
+ }
21154
21195
  if (system !== undefined && system.length > 0)
21155
21196
  body.system = system;
21156
21197
  if (req.temperature !== undefined)
@@ -25412,18 +25453,37 @@ async function vacuum(deps) {
25412
25453
  }
25413
25454
  async function getDatabaseOverview(deps) {
25414
25455
  const live = deps.store.databasePath;
25415
- const stats = await deps.store.maintenance.stats();
25416
- const settings = await deps.store.config.getSettings();
25456
+ const [stats, tables, settings] = await Promise.all([
25457
+ deps.store.maintenance.stats(),
25458
+ deps.store.maintenance.tables(),
25459
+ deps.store.config.getSettings()
25460
+ ]);
25461
+ const shared = {
25462
+ engine: deps.store.engine,
25463
+ location: live,
25464
+ stats,
25465
+ logicalBytes: stats.pageSize * stats.pageCount,
25466
+ freePageBytes: stats.pageSize * stats.freelistCount,
25467
+ retention: retentionOf(settings),
25468
+ tables
25469
+ };
25470
+ if (deps.store.engine === "postgres") {
25471
+ return {
25472
+ ...shared,
25473
+ fileBytes: 0,
25474
+ walBytes: 0,
25475
+ bodiesBytes: tables.find((table2) => table2.name === "request_bodies")?.bytes ?? 0,
25476
+ freeDiskBytes: null,
25477
+ snapshots: { count: 0, totalBytes: 0, latestAt: null }
25478
+ };
25479
+ }
25417
25480
  const snapshots = listSnapshots(deps);
25418
25481
  return {
25419
- stats,
25482
+ ...shared,
25420
25483
  fileBytes: deps.fs.stat(live)?.size ?? 0,
25421
25484
  walBytes: deps.fs.stat(`${live}-wal`)?.size ?? 0,
25422
25485
  bodiesBytes: deps.fs.dirBytes(bodiesDirFor(live)),
25423
- logicalBytes: stats.pageSize * stats.pageCount,
25424
- freePageBytes: stats.pageSize * stats.freelistCount,
25425
25486
  freeDiskBytes: deps.fs.freeBytes(dirname2(live)),
25426
- retention: retentionOf(settings),
25427
25487
  snapshots: {
25428
25488
  count: snapshots.length,
25429
25489
  totalBytes: snapshots.reduce((sum2, snapshot) => sum2 + snapshot.sizeBytes, 0),
@@ -28447,7 +28507,13 @@ var dbStats = {
28447
28507
  async run(_args, env2) {
28448
28508
  const { ctx, writer } = env2;
28449
28509
  const overview = await getDatabaseOverview(await database(env2));
28450
- emit(ctx, writer, overview, () => fields([
28510
+ const tables = table([{ header: "TABLE" }, { header: "SIZE", align: "right" }, { header: "ROWS", align: "right" }], overview.tables.map((t) => [t.name, formatBytes(t.bytes), String(t.rows)]));
28511
+ const summary = overview.engine === "postgres" ? fields([
28512
+ ["database", overview.location],
28513
+ ["size", formatBytes(overview.logicalBytes)],
28514
+ ["captured bodies", `${formatBytes(overview.bodiesBytes)} (request_bodies table)`],
28515
+ ["schema version", String(overview.stats.schemaVersion)]
28516
+ ]) : fields([
28451
28517
  ["database", ctx.databasePath],
28452
28518
  ["size", formatBytes(overview.fileBytes)],
28453
28519
  ["write-ahead log", formatBytes(overview.walBytes)],
@@ -28466,7 +28532,10 @@ var dbStats = {
28466
28532
  "free disk",
28467
28533
  overview.freeDiskBytes === null ? "unknown" : formatBytes(overview.freeDiskBytes)
28468
28534
  ]
28469
- ]));
28535
+ ]);
28536
+ emit(ctx, writer, overview, () => `${summary}
28537
+
28538
+ ${tables}`);
28470
28539
  }
28471
28540
  };
28472
28541
  var dbSnapshots = {
@@ -29300,7 +29369,7 @@ function createServiceDeps(input) {
29300
29369
  }
29301
29370
 
29302
29371
  // apps/cli/src/version.ts
29303
- var VERSION = "0.8.0";
29372
+ var VERSION = "0.8.1";
29304
29373
 
29305
29374
  // apps/cli/src/commands/service.ts
29306
29375
  function gatewayArgv(root) {
package/gateway.js CHANGED
@@ -21710,6 +21710,23 @@ function createMaintenanceRepo(sql, nodeId) {
21710
21710
  schemaVersion: row?.version ?? 0
21711
21711
  };
21712
21712
  },
21713
+ async tables() {
21714
+ const rows = await sql.unsafe(`SELECT c.relname AS name,
21715
+ pg_total_relation_size(c.oid) AS bytes,
21716
+ GREATEST(c.reltuples, 0)::bigint AS rows,
21717
+ COALESCE(s.n_dead_tup, 0) AS dead_rows
21718
+ FROM pg_class c
21719
+ JOIN pg_namespace n ON n.oid = c.relnamespace
21720
+ LEFT JOIN pg_stat_user_tables s ON s.relid = c.oid
21721
+ WHERE c.relkind = 'r' AND n.nspname = current_schema()
21722
+ ORDER BY bytes DESC, name`);
21723
+ return rows.map((row) => ({
21724
+ name: row.name,
21725
+ bytes: num(row.bytes),
21726
+ rows: num(row.rows),
21727
+ deadRows: num(row.dead_rows)
21728
+ }));
21729
+ },
21713
21730
  vacuum: async () => unsupported(),
21714
21731
  snapshotTo: async () => unsupported(),
21715
21732
  inspect: async () => unsupported()
@@ -22648,6 +22665,7 @@ async function createPostgresStore(opts) {
22648
22665
  });
22649
22666
  };
22650
22667
  return {
22668
+ engine: "postgres",
22651
22669
  databasePath: redacted(opts.url),
22652
22670
  credentials: createCredentialRepo(sql, opts.encryptionKey, emit),
22653
22671
  config: createConfigRepo(sql, emit),
@@ -23830,6 +23848,15 @@ function createMaintenanceRepo2(db, nodeId) {
23830
23848
  schemaVersion: version2?.version ?? 0
23831
23849
  };
23832
23850
  },
23851
+ async tables() {
23852
+ return db.query(`SELECT m.tbl_name AS name,
23853
+ SUM(s.pgsize) AS bytes,
23854
+ SUM(CASE WHEN s.pagetype = 'leaf' AND s.name = m.tbl_name THEN s.ncell ELSE 0 END) AS rows
23855
+ FROM dbstat s JOIN sqlite_master m ON m.name = s.name
23856
+ WHERE m.tbl_name NOT LIKE 'sqlite_%'
23857
+ GROUP BY m.tbl_name
23858
+ ORDER BY bytes DESC, name`).all().map((row) => ({ ...row, deadRows: null }));
23859
+ },
23833
23860
  async vacuum() {
23834
23861
  db.run("VACUUM");
23835
23862
  db.run("PRAGMA wal_checkpoint(TRUNCATE)");
@@ -24301,6 +24328,7 @@ async function createStore(opts) {
24301
24328
  live = false;
24302
24329
  };
24303
24330
  return {
24331
+ engine: "sqlite",
24304
24332
  databasePath: opts.path,
24305
24333
  credentials: {
24306
24334
  list: () => handle.credentials.list(),
@@ -24365,6 +24393,7 @@ async function createStore(opts) {
24365
24393
  },
24366
24394
  maintenance: {
24367
24395
  stats: () => handle.maintenance.stats(),
24396
+ tables: () => handle.maintenance.tables(),
24368
24397
  heartbeat: (now) => handle.maintenance.heartbeat(now),
24369
24398
  nodes: (now) => handle.maintenance.nodes(now),
24370
24399
  vacuum: () => handle.maintenance.vacuum(),
@@ -26534,13 +26563,13 @@ function encodeSystemTurn(content, cloak) {
26534
26563
  return content.map((block) => encodeBlock(block, cloak));
26535
26564
  }
26536
26565
  function systemCacheControl(req) {
26537
- const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
26538
- const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
26539
- const final = markedSystemBlocks.at(-1);
26540
- const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
26566
+ const markedSystemBlocks = req.messages.flatMap((message) => message.role === "system" ? message.content.filter((block) => cacheControlOf(block) !== undefined) : []);
26567
+ const trailing = req.messages.at(-1);
26568
+ const marked = trailing?.role === "system" ? trailing.content.findLast((block) => cacheControlOf(block) !== undefined) : undefined;
26569
+ const retarget = marked === undefined ? undefined : cacheControlOf(marked);
26541
26570
  return {
26542
- ...promoted === undefined ? {} : { promoted },
26543
- lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
26571
+ ...retarget === undefined ? {} : { retarget },
26572
+ lost: markedSystemBlocks.length > (retarget === undefined ? 0 : 1)
26544
26573
  };
26545
26574
  }
26546
26575
  function encodeTool(t, cloak) {
@@ -26702,14 +26731,26 @@ function toWire(req, model, opts) {
26702
26731
  return [{ role: m.role, content: encodeSystemTurn(m.content, cloak) }];
26703
26732
  }),
26704
26733
  max_tokens: req.maxTokens ?? 4096,
26705
- stream: req.stream,
26706
- ...systemCache.promoted === undefined ? {} : {
26707
- cache_control: {
26708
- type: systemCache.promoted.type,
26709
- ...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
26734
+ stream: req.stream
26735
+ };
26736
+ if (systemCache.retarget !== undefined) {
26737
+ const trailing = body.messages.at(-1);
26738
+ if (isRecord(trailing) && Array.isArray(trailing.content)) {
26739
+ for (const block of trailing.content) {
26740
+ if (isRecord(block))
26741
+ delete block.cache_control;
26710
26742
  }
26711
26743
  }
26712
- };
26744
+ if (!hasVendorCacheControl(req)) {
26745
+ const target = lastCacheableHistoryBlock(body.messages.slice(0, -1));
26746
+ if (target === undefined) {
26747
+ note("anthropic:system-turn-cache-control-dropped");
26748
+ } else if (target.cache_control === undefined) {
26749
+ Object.assign(target, wireCacheControl(systemCache.retarget));
26750
+ note("anthropic:system-turn-cache-control-retargeted");
26751
+ }
26752
+ }
26753
+ }
26713
26754
  if (system !== undefined && system.length > 0)
26714
26755
  body.system = system;
26715
26756
  if (req.temperature !== undefined)
@@ -31024,18 +31065,37 @@ async function vacuum(deps) {
31024
31065
  }
31025
31066
  async function getDatabaseOverview(deps) {
31026
31067
  const live = deps.store.databasePath;
31027
- const stats = await deps.store.maintenance.stats();
31028
- const settings = await deps.store.config.getSettings();
31068
+ const [stats, tables, settings] = await Promise.all([
31069
+ deps.store.maintenance.stats(),
31070
+ deps.store.maintenance.tables(),
31071
+ deps.store.config.getSettings()
31072
+ ]);
31073
+ const shared = {
31074
+ engine: deps.store.engine,
31075
+ location: live,
31076
+ stats,
31077
+ logicalBytes: stats.pageSize * stats.pageCount,
31078
+ freePageBytes: stats.pageSize * stats.freelistCount,
31079
+ retention: retentionOf(settings),
31080
+ tables
31081
+ };
31082
+ if (deps.store.engine === "postgres") {
31083
+ return {
31084
+ ...shared,
31085
+ fileBytes: 0,
31086
+ walBytes: 0,
31087
+ bodiesBytes: tables.find((table) => table.name === "request_bodies")?.bytes ?? 0,
31088
+ freeDiskBytes: null,
31089
+ snapshots: { count: 0, totalBytes: 0, latestAt: null }
31090
+ };
31091
+ }
31029
31092
  const snapshots = listSnapshots(deps);
31030
31093
  return {
31031
- stats,
31094
+ ...shared,
31032
31095
  fileBytes: deps.fs.stat(live)?.size ?? 0,
31033
31096
  walBytes: deps.fs.stat(`${live}-wal`)?.size ?? 0,
31034
31097
  bodiesBytes: deps.fs.dirBytes(bodiesDirFor(live)),
31035
- logicalBytes: stats.pageSize * stats.pageCount,
31036
- freePageBytes: stats.pageSize * stats.freelistCount,
31037
31098
  freeDiskBytes: deps.fs.freeBytes(dirname2(live)),
31038
- retention: retentionOf(settings),
31039
31099
  snapshots: {
31040
31100
  count: snapshots.length,
31041
31101
  totalBytes: snapshots.reduce((sum2, snapshot) => sum2 + snapshot.sizeBytes, 0),
@@ -48520,15 +48580,27 @@ function adminRoutes(deps) {
48520
48580
  const live = await deps.store.maintenance.nodes(deps.now());
48521
48581
  const reads = await Promise.allSettled(live.map((entry) => fleet.read(entry.id, consoleQuery).then((read) => ({ nodeId: entry.id, read }))));
48522
48582
  const lines = [];
48523
- for (const outcome of reads) {
48524
- if (outcome.status !== "fulfilled")
48583
+ let captured = false;
48584
+ const unreachable2 = [];
48585
+ for (const [index, outcome] of reads.entries()) {
48586
+ if (outcome.status !== "fulfilled") {
48587
+ unreachable2.push(live[index]?.id ?? "");
48525
48588
  continue;
48589
+ }
48590
+ if (outcome.value.read.source !== "none")
48591
+ captured = true;
48526
48592
  for (const line of outcome.value.read.lines) {
48527
48593
  lines.push({ ...line, nodeId: outcome.value.nodeId });
48528
48594
  }
48529
48595
  }
48596
+ if (unreachable2.length === reads.length) {
48597
+ throw new GatewayError("TIMEOUT", "no process in the fleet answered a console read");
48598
+ }
48599
+ const reach = unreachable2.length === 0 ? {} : { unreachable: unreachable2 };
48600
+ if (!captured)
48601
+ return { source: "none", lines: [], ...reach };
48530
48602
  lines.sort((a, b) => (a.at ?? Number.POSITIVE_INFINITY) - (b.at ?? Number.POSITIVE_INFINITY));
48531
- return { source: "fleet", lines: lines.slice(-consoleQuery.lines) };
48603
+ return { source: "fleet", lines: lines.slice(-consoleQuery.lines), ...reach };
48532
48604
  }).get("/api/nodes", async ({ request: request2 }) => {
48533
48605
  await requireReader(request2, deps.admin);
48534
48606
  const nodes = await deps.store.maintenance.nodes(deps.now());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnigateway",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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{m as e}from"./Rack-xBT_6EtM.js";import{jsx as t}from"react/jsx-runtime";import n from"styled-components";var 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=n.span`
1
+ import{m as e}from"./Rack-CRuJZY00.js";import{jsx as t}from"react/jsx-runtime";import n from"styled-components";var 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=n.span`
2
2
  display: inline-flex;
3
3
  align-items: center;
4
4
  gap: 4px;
@@ -1,4 +1,4 @@
1
- import{C as e,O as t}from"./Panel-CYQvfI3L.js";import{b as n}from"./Rack-xBT_6EtM.js";import{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import l from"styled-components";var u=n(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),d=n(`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`}]]);function f(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 p(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=f(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var m=l.div`
1
+ import{C as e,O as t}from"./Panel-CYQvfI3L.js";import{b as n}from"./Rack-CRuJZY00.js";import{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import l from"styled-components";var u=n(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),d=n(`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`}]]);function f(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 p(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=f(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var m=l.div`
2
2
  display: flex;
3
3
  align-items: stretch;
4
4
  gap: 0;