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/bin/omni.js CHANGED
@@ -188,6 +188,23 @@ class GatewayError extends Error {
188
188
  }
189
189
  // packages/ir/src/logger.ts
190
190
  var LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
191
+ var ANSI2 = /\u001B\[[0-9;]*m/g;
192
+ var HEAD = /^(\S+) (DEBUG|INFO|WARN|ERROR) {1,2}(.*)$/;
193
+ var FIELDS = / {2}(?=[a-zA-Z]+=)/;
194
+ function parseLine(raw) {
195
+ const absent = { raw, at: null, level: null, msg: null };
196
+ const match = HEAD.exec(raw.replace(ANSI2, ""));
197
+ if (match === null)
198
+ return absent;
199
+ const [, instant, label, tail] = match;
200
+ if (instant === undefined || label === undefined || tail === undefined)
201
+ return absent;
202
+ const at = Date.parse(instant);
203
+ if (Number.isNaN(at))
204
+ return absent;
205
+ const msg = tail.split(FIELDS, 1)[0] ?? tail;
206
+ return { raw, at, level: label.toLowerCase(), msg };
207
+ }
191
208
  var noopLogger = {
192
209
  debug: () => {},
193
210
  info: () => {},
@@ -234,6 +251,7 @@ function loadConfig(env) {
234
251
  const derivedBaseUrl = `http://${host}:${port}`;
235
252
  const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
236
253
  const staticDir = env.OMNI_STATIC_DIR?.trim();
254
+ const logFile = env.OMNI_LOG_FILE?.trim();
237
255
  const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
238
256
  const logLevel = parseLogLevel(rawLogLevel);
239
257
  return {
@@ -244,7 +262,8 @@ function loadConfig(env) {
244
262
  databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
245
263
  encryptionKey,
246
264
  baseUrl,
247
- staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
265
+ staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
266
+ logFile: logFile === undefined || logFile.length === 0 ? null : logFile
248
267
  };
249
268
  }
250
269
  // packages/providers/src/body.ts
@@ -669,7 +688,7 @@ async function* decodeAnthropic(messages) {
669
688
  if (cb.type === "text")
670
689
  yield { type: "blockStart", index, block: { type: "text" } };
671
690
  else if (cb.type === "thinking")
672
- yield { type: "blockStart", index, block: { type: "thinking" } };
691
+ yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
673
692
  else if (cb.type === "tool_use")
674
693
  yield {
675
694
  type: "blockStart",
@@ -689,7 +708,7 @@ async function* decodeAnthropic(messages) {
689
708
  index,
690
709
  delta: { type: "thinking", text: delta.thinking ?? "" }
691
710
  };
692
- else if (delta.type === "signature_delta")
711
+ else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
693
712
  yield {
694
713
  type: "blockDelta",
695
714
  index,
@@ -761,6 +780,9 @@ function wireCacheControl(c) {
761
780
  return {};
762
781
  return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
763
782
  }
783
+ function isReplayableThinking(b) {
784
+ return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
785
+ }
764
786
  function encodeBlock(b) {
765
787
  const cache = wireCacheControl(cacheControlOf(b));
766
788
  switch (b.type) {
@@ -790,6 +812,16 @@ function encodeSystemTurn(content) {
790
812
  return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
791
813
  `);
792
814
  }
815
+ function systemCacheControl(req) {
816
+ const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
817
+ const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
818
+ const final = markedSystemBlocks.at(-1);
819
+ const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
820
+ return {
821
+ ...promoted === undefined ? {} : { promoted },
822
+ lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
823
+ };
824
+ }
793
825
  function encodeToolChoice(c) {
794
826
  switch (c.type) {
795
827
  case "auto":
@@ -813,18 +845,30 @@ function toWire(req, model, opts) {
813
845
  system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
814
846
  note2("anthropic:oauth-system-prefix");
815
847
  }
848
+ const systemCache = systemCacheControl(req);
849
+ if (systemCache.lost)
850
+ note2("anthropic:system-turn-cache-control-dropped");
816
851
  const body = {
817
852
  model,
818
- messages: req.messages.map((m) => {
819
- if (m.role !== "system")
820
- return { role: m.role, content: m.content.map(encodeBlock) };
821
- if (m.content.some((b) => cacheControlOf(b) !== undefined)) {
822
- note2("anthropic:system-turn-cache-control-dropped");
823
- }
824
- return { role: m.role, content: encodeSystemTurn(m.content) };
853
+ messages: req.messages.flatMap((m) => {
854
+ if (m.role !== "system") {
855
+ const replayable = m.content.filter(isReplayableThinking);
856
+ if (replayable.length !== m.content.length)
857
+ note2("anthropic:unsigned-thinking-dropped");
858
+ if (replayable.length === 0)
859
+ return [];
860
+ return [{ role: m.role, content: replayable.map(encodeBlock) }];
861
+ }
862
+ return [{ role: m.role, content: encodeSystemTurn(m.content) }];
825
863
  }),
826
864
  max_tokens: req.maxTokens ?? 4096,
827
- stream: req.stream
865
+ stream: req.stream,
866
+ ...systemCache.promoted === undefined ? {} : {
867
+ cache_control: {
868
+ type: systemCache.promoted.type,
869
+ ...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
870
+ }
871
+ }
828
872
  };
829
873
  if (system !== undefined && system.length > 0)
830
874
  body.system = system;
@@ -2181,6 +2225,62 @@ function createConnectFlows(deps) {
2181
2225
  }
2182
2226
  };
2183
2227
  }
2228
+ // packages/control/src/console.ts
2229
+ var UNIT_NAME = "omnigateway.service";
2230
+ var MAX_CONSOLE_LINES = 500;
2231
+ var DEFAULT_CONSOLE_LINES = 200;
2232
+ function resolveConsoleSource(input) {
2233
+ const path = input.logFile?.trim();
2234
+ if (path !== undefined && path.length > 0)
2235
+ return { kind: "file", path };
2236
+ if (input.unitInstalled)
2237
+ return { kind: "journal", unit: UNIT_NAME, scope: input.scope };
2238
+ return { kind: "none" };
2239
+ }
2240
+ function consoleLimit(requested) {
2241
+ const value = requested === undefined ? DEFAULT_CONSOLE_LINES : Number(requested);
2242
+ if (!Number.isFinite(value))
2243
+ return DEFAULT_CONSOLE_LINES;
2244
+ return Math.floor(Math.min(Math.max(1, value), MAX_CONSOLE_LINES));
2245
+ }
2246
+ function keep(line, query) {
2247
+ if (query.since !== undefined && (line.at === null || line.at <= query.since))
2248
+ return false;
2249
+ if (query.level === undefined || line.level === null)
2250
+ return true;
2251
+ return LEVELS[line.level] >= LEVELS[query.level];
2252
+ }
2253
+ var SCAN_FACTOR = 20;
2254
+ var SCAN_LIMIT = 5000;
2255
+ function scanWidth(query) {
2256
+ const filtered = query.level !== undefined || query.since !== undefined;
2257
+ return filtered ? Math.min(query.lines * SCAN_FACTOR, SCAN_LIMIT) : query.lines;
2258
+ }
2259
+ async function readSource(deps, source, lines) {
2260
+ if (source.kind === "none")
2261
+ return "";
2262
+ if (source.kind === "file")
2263
+ return deps.readFile(source.path, lines) ?? "";
2264
+ const scope = source.scope === "system" ? [] : ["--user"];
2265
+ const result = await deps.run([
2266
+ "journalctl",
2267
+ ...scope,
2268
+ "-u",
2269
+ source.unit,
2270
+ "-n",
2271
+ String(lines),
2272
+ "--no-pager",
2273
+ "--output=cat"
2274
+ ]);
2275
+ return result.code === 0 ? result.stdout : "";
2276
+ }
2277
+ async function readConsole(deps, source, query) {
2278
+ const limited = { ...query, lines: consoleLimit(query.lines) };
2279
+ const text = await readSource(deps, source, scanWidth(limited));
2280
+ const lines = text.split(`
2281
+ `).filter((raw) => raw.trim().length > 0).map(parseLine).filter((line) => keep(line, limited)).slice(-limited.lines);
2282
+ return source.kind === "file" ? { source: "file", path: source.path, lines } : { source: source.kind, lines };
2283
+ }
2184
2284
  // node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
2185
2285
  var exports_external = {};
2186
2286
  __export(exports_external, {
@@ -18241,6 +18341,50 @@ async function putSettings(store, input) {
18241
18341
  await store.config.putSettings(settings);
18242
18342
  return settings;
18243
18343
  }
18344
+ // packages/control/src/tail.ts
18345
+ import { closeSync, fstatSync, openSync, readSync, statSync } from "fs";
18346
+ var CHUNK = 64 * 1024;
18347
+ var MAX_BYTES = 8 * 1024 * 1024;
18348
+ function tailFile(path, lines) {
18349
+ let fd;
18350
+ try {
18351
+ fd = openSync(path, "r");
18352
+ } catch {
18353
+ return null;
18354
+ }
18355
+ try {
18356
+ const size = fstatSync(fd).size;
18357
+ if (size === 0)
18358
+ return "";
18359
+ const budget = Math.min(size, MAX_BYTES);
18360
+ let read = 0;
18361
+ let newlines = 0;
18362
+ const chunks = [];
18363
+ while (read < budget && newlines <= lines) {
18364
+ const span = Math.min(CHUNK, budget - read);
18365
+ const buffer = Buffer.allocUnsafe(span);
18366
+ const position = size - read - span;
18367
+ readSync(fd, buffer, 0, span, position);
18368
+ chunks.unshift(buffer);
18369
+ read += span;
18370
+ for (const byte of buffer)
18371
+ if (byte === 10)
18372
+ newlines++;
18373
+ }
18374
+ return Buffer.concat(chunks).toString("utf8");
18375
+ } catch {
18376
+ return null;
18377
+ } finally {
18378
+ closeSync(fd);
18379
+ }
18380
+ }
18381
+ function fileExists(path) {
18382
+ try {
18383
+ return statSync(path).isFile();
18384
+ } catch {
18385
+ return false;
18386
+ }
18387
+ }
18244
18388
  // packages/control/src/usage.ts
18245
18389
  var MAX_LOG_LIMIT = 500;
18246
18390
  var DEFAULT_LOG_LIMIT = 100;
@@ -18475,6 +18619,278 @@ async function pollUntilAuthorized(flows, start, deps) {
18475
18619
  }
18476
18620
  }
18477
18621
 
18622
+ // apps/cli/src/service.ts
18623
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
18624
+ import { homedir as homedir2 } from "os";
18625
+ import { dirname, join as join2 } from "path";
18626
+ var UNIT_NAME2 = "omnigateway.service";
18627
+ function unitPath(scope) {
18628
+ return scope === "system" ? join2("/etc/systemd/system", UNIT_NAME2) : join2(homedir2(), ".config", "systemd", "user", UNIT_NAME2);
18629
+ }
18630
+ function systemctlArgs(scope, args) {
18631
+ return scope === "system" ? ["systemctl", ...args] : ["systemctl", "--user", ...args];
18632
+ }
18633
+ function pidFile(stateDir) {
18634
+ return join2(stateDir, "gateway.pid");
18635
+ }
18636
+ function logFile(stateDir) {
18637
+ return join2(stateDir, "gateway.log");
18638
+ }
18639
+ function supervisedLogFile(deps) {
18640
+ const configured = deps.logFile?.trim();
18641
+ return configured !== undefined && configured.length > 0 ? configured : logFile(deps.stateDir);
18642
+ }
18643
+ function defaultStateDir(env2) {
18644
+ const xdg = env2.XDG_STATE_HOME;
18645
+ const base = typeof xdg === "string" && xdg.length > 0 ? xdg : join2(homedir2(), ".local", "state");
18646
+ return join2(base, "omnigateway");
18647
+ }
18648
+ function unitFile(input) {
18649
+ return `[Unit]
18650
+ Description=${input.description ?? "OmniGateway"}
18651
+ After=network-online.target
18652
+ Wants=network-online.target
18653
+
18654
+ [Service]
18655
+ Type=simple
18656
+ WorkingDirectory=${input.root}
18657
+ EnvironmentFile=${join2(input.root, ".env")}
18658
+ ExecStart=${input.bun} ${input.entrypoint}
18659
+ Restart=on-failure
18660
+ RestartSec=2
18661
+
18662
+ [Install]
18663
+ WantedBy=default.target
18664
+ `;
18665
+ }
18666
+ function unitInstalled(deps) {
18667
+ return existsSync2(deps.unitPath);
18668
+ }
18669
+ function readPid(stateDir) {
18670
+ const file2 = pidFile(stateDir);
18671
+ if (!existsSync2(file2))
18672
+ return null;
18673
+ const pid = Number(readFileSync2(file2, "utf8").trim());
18674
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
18675
+ }
18676
+ function livePid(deps) {
18677
+ const pid = readPid(deps.stateDir);
18678
+ if (pid === null)
18679
+ return null;
18680
+ if (deps.alive(pid))
18681
+ return pid;
18682
+ rmSync(pidFile(deps.stateDir), { force: true });
18683
+ return null;
18684
+ }
18685
+ async function status(deps) {
18686
+ if (unitInstalled(deps)) {
18687
+ const active = await deps.run(systemctlArgs(deps.scope, ["is-active", UNIT_NAME2]));
18688
+ const state = active.stdout.trim() || active.stderr.trim() || "unknown";
18689
+ const shown = await deps.run(systemctlArgs(deps.scope, ["show", UNIT_NAME2, "--property=MainPID", "--value"]));
18690
+ const pid2 = Number(shown.stdout.trim());
18691
+ return {
18692
+ supervisor: "systemd",
18693
+ running: state === "active",
18694
+ pid: Number.isInteger(pid2) && pid2 > 0 ? pid2 : null,
18695
+ state,
18696
+ unitPath: deps.unitPath,
18697
+ logFile: null
18698
+ };
18699
+ }
18700
+ const pid = livePid(deps);
18701
+ return {
18702
+ supervisor: pid === null ? "none" : "pidfile",
18703
+ running: pid !== null,
18704
+ pid,
18705
+ state: null,
18706
+ unitPath: null,
18707
+ logFile: supervisedLogFile(deps)
18708
+ };
18709
+ }
18710
+ async function start(deps, input) {
18711
+ if (unitInstalled(deps)) {
18712
+ const result = await deps.run(systemctlArgs(deps.scope, ["start", UNIT_NAME2]));
18713
+ if (result.code !== 0) {
18714
+ throw new Error(result.stderr.trim() || `systemctl start exited ${result.code}`);
18715
+ }
18716
+ return {
18717
+ supervisor: "systemd",
18718
+ pid: null,
18719
+ healthy: await waitForHealth(deps, input.baseUrl)
18720
+ };
18721
+ }
18722
+ const running = livePid(deps);
18723
+ if (running !== null) {
18724
+ return {
18725
+ supervisor: "pidfile",
18726
+ pid: running,
18727
+ healthy: await waitForHealth(deps, input.baseUrl)
18728
+ };
18729
+ }
18730
+ mkdirSync(deps.stateDir, { recursive: true });
18731
+ const file2 = supervisedLogFile(deps);
18732
+ mkdirSync(dirname(file2), { recursive: true });
18733
+ const pid = deps.spawn({
18734
+ argv: input.argv,
18735
+ cwd: deps.root,
18736
+ env: { ...input.env, OMNI_LOG_FILE: file2 },
18737
+ logFile: file2
18738
+ });
18739
+ writeFileSync(pidFile(deps.stateDir), `${pid}
18740
+ `);
18741
+ return { supervisor: "pidfile", pid, healthy: await waitForHealth(deps, input.baseUrl) };
18742
+ }
18743
+ var HEALTH_TIMEOUT_MS = 1e4;
18744
+ var HEALTH_INTERVAL_MS = 200;
18745
+ async function waitForHealth(deps, baseUrl) {
18746
+ const attempts = Math.ceil(HEALTH_TIMEOUT_MS / HEALTH_INTERVAL_MS);
18747
+ for (let attempt = 0;attempt < attempts; attempt++) {
18748
+ if (await deps.probe(baseUrl))
18749
+ return true;
18750
+ await deps.sleep(HEALTH_INTERVAL_MS);
18751
+ }
18752
+ return false;
18753
+ }
18754
+ var STOP_GRACE_MS = 5000;
18755
+ var STOP_INTERVAL_MS = 100;
18756
+ async function stop(deps) {
18757
+ if (unitInstalled(deps)) {
18758
+ const result = await deps.run(systemctlArgs(deps.scope, ["stop", UNIT_NAME2]));
18759
+ if (result.code !== 0) {
18760
+ throw new Error(result.stderr.trim() || `systemctl stop exited ${result.code}`);
18761
+ }
18762
+ return { supervisor: "systemd", stopped: true };
18763
+ }
18764
+ const pid = livePid(deps);
18765
+ if (pid === null)
18766
+ return { supervisor: "none", stopped: false };
18767
+ deps.kill(pid, "SIGTERM");
18768
+ const attempts = Math.ceil(STOP_GRACE_MS / STOP_INTERVAL_MS);
18769
+ for (let attempt = 0;deps.alive(pid); attempt++) {
18770
+ if (attempt >= attempts) {
18771
+ deps.kill(pid, "SIGKILL");
18772
+ break;
18773
+ }
18774
+ await deps.sleep(STOP_INTERVAL_MS);
18775
+ }
18776
+ rmSync(pidFile(deps.stateDir), { force: true });
18777
+ return { supervisor: "pidfile", stopped: true };
18778
+ }
18779
+ async function install(deps, input) {
18780
+ const path = deps.unitPath;
18781
+ if (existsSync2(path) && !input.force) {
18782
+ throw new Error(`${path} already exists; pass --force to replace it`);
18783
+ }
18784
+ mkdirSync(dirname(path), { recursive: true });
18785
+ writeFileSync(path, unitFile({ root: deps.root, bun: input.bun, entrypoint: input.entrypoint }));
18786
+ const reload = await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
18787
+ let enabled = false;
18788
+ if (input.enable) {
18789
+ const result = await deps.run(systemctlArgs(deps.scope, ["enable", UNIT_NAME2]));
18790
+ enabled = result.code === 0;
18791
+ }
18792
+ return { path, reloaded: reload.code === 0, enabled };
18793
+ }
18794
+ async function uninstall(deps) {
18795
+ const path = deps.unitPath;
18796
+ if (!existsSync2(path))
18797
+ return { path, removed: false };
18798
+ await deps.run(systemctlArgs(deps.scope, ["disable", "--now", UNIT_NAME2]));
18799
+ rmSync(path, { force: true });
18800
+ await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
18801
+ return { path, removed: true };
18802
+ }
18803
+ function consoleSource(deps) {
18804
+ const configured = deps.logFile?.trim();
18805
+ const chosen = configured !== undefined && configured.length > 0 && fileExists(configured) ? configured : unitInstalled(deps) ? undefined : fileExists(supervisedLogFile(deps)) ? supervisedLogFile(deps) : undefined;
18806
+ return {
18807
+ source: resolveConsoleSource({
18808
+ ...chosen === undefined ? {} : { logFile: chosen },
18809
+ unitInstalled: unitInstalled(deps),
18810
+ scope: deps.scope
18811
+ }),
18812
+ deps: {
18813
+ readFile: (path, lines) => tailFile(path, lines),
18814
+ run: deps.run
18815
+ }
18816
+ };
18817
+ }
18818
+ async function serviceLogs(deps, lines) {
18819
+ const { source, deps: readDeps } = consoleSource(deps);
18820
+ const read = await readConsole(readDeps, source, { lines });
18821
+ return read.lines.map((line) => line.raw).join(`
18822
+ `);
18823
+ }
18824
+
18825
+ // apps/cli/src/commands/console.ts
18826
+ var FOLLOW_INTERVAL_MS = 2000;
18827
+ var LEVEL_TONE = {
18828
+ debug: "dim",
18829
+ warn: "yellow",
18830
+ error: "red"
18831
+ };
18832
+ function render(ctx, lines) {
18833
+ return lines.map((line) => {
18834
+ const tone = line.level === null ? undefined : LEVEL_TONE[line.level];
18835
+ return tone === undefined ? line.raw : paint(ctx, tone, line.raw);
18836
+ }).join(`
18837
+ `);
18838
+ }
18839
+ function sourceHint(source) {
18840
+ if (source.kind === "file")
18841
+ return `${source.path} (named by OMNI_LOG_FILE)`;
18842
+ if (source.kind === "journal")
18843
+ return `systemd journal for ${source.unit}`;
18844
+ return "not captured \u2014 run under systemd, or start it with `omni start`";
18845
+ }
18846
+ function empty(source) {
18847
+ if (source.kind !== "none")
18848
+ return `no output yet in ${sourceHint(source)}`;
18849
+ return [
18850
+ "this gateway's output is not being captured, so there is nothing to show.",
18851
+ "run it in the foreground and its output goes to your terminal instead.",
18852
+ "to capture it: `omni service install`, or `omni start`, which redirects",
18853
+ "output to a file and points OMNI_LOG_FILE at it."
18854
+ ].join(`
18855
+ `);
18856
+ }
18857
+ var console_ = {
18858
+ usage: "console [-n N] [--follow] [--level debug|info|warn|error]",
18859
+ summary: "Show the gateway process's own output",
18860
+ options: {
18861
+ number: { type: "string", short: "n" },
18862
+ follow: { type: "boolean" },
18863
+ level: { type: "string" },
18864
+ system: { type: "boolean" }
18865
+ },
18866
+ async run(args, { ctx, writer, service }) {
18867
+ const lines = numberFlag(args.values, "number") ?? 50;
18868
+ const rawLevel = stringFlag(args.values, "level");
18869
+ const level = parseLogLevel(rawLevel);
18870
+ if (rawLevel !== undefined && level === null) {
18871
+ throw new UsageError(`unknown level "${rawLevel}"; use debug, info, warn or error`);
18872
+ }
18873
+ const { source, deps } = consoleSource(service());
18874
+ const query = { lines, ...level === null ? {} : { level } };
18875
+ const first = await readConsole(deps, source, query);
18876
+ emit(ctx, writer, first, () => first.lines.length === 0 ? empty(source) : render(ctx, first.lines));
18877
+ if (!ctx.json && first.lines.length > 0) {
18878
+ note(ctx, writer, paint(ctx, "dim", `reading ${sourceHint(source)}`));
18879
+ }
18880
+ if (boolFlag(args.values, "follow") !== true || ctx.json)
18881
+ return;
18882
+ let seen = first.lines.at(-1)?.at ?? 0;
18883
+ for (;; ) {
18884
+ await Bun.sleep(FOLLOW_INTERVAL_MS);
18885
+ const next = await readConsole(deps, source, { ...query, since: seen });
18886
+ if (next.lines.length === 0)
18887
+ continue;
18888
+ seen = next.lines.at(-1)?.at ?? seen;
18889
+ writer.out(render(ctx, next.lines));
18890
+ }
18891
+ }
18892
+ };
18893
+
18478
18894
  // apps/cli/src/command.ts
18479
18895
  var PROVIDER_TONE = {
18480
18896
  anthropic: "magenta",
@@ -18515,7 +18931,7 @@ var credentialsList = {
18515
18931
  { header: "STATE" },
18516
18932
  { header: "EXPIRES" }
18517
18933
  ], credentials.map((c) => {
18518
- const status = condition(c);
18934
+ const status2 = condition(c);
18519
18935
  return [
18520
18936
  c.id,
18521
18937
  provider(ctx, c.provider),
@@ -18523,7 +18939,7 @@ var credentialsList = {
18523
18939
  c.authType,
18524
18940
  String(c.tier),
18525
18941
  String(c.weight),
18526
- state(ctx, status.ok, status.text),
18942
+ state(ctx, status2.ok, status2.text),
18527
18943
  formatTime(c.expiresAt)
18528
18944
  ];
18529
18945
  }));
@@ -18539,7 +18955,7 @@ var credentialsShow = {
18539
18955
  async run(args, { ctx, writer }) {
18540
18956
  const id = requirePositional(args, 0, "credential id");
18541
18957
  const credential = await findCredential(ctx, id);
18542
- const status = condition(credential);
18958
+ const status2 = condition(credential);
18543
18959
  const data = {
18544
18960
  id: credential.id,
18545
18961
  provider: credential.provider,
@@ -18562,7 +18978,7 @@ var credentialsShow = {
18562
18978
  ["provider", provider(ctx, credential.provider)],
18563
18979
  ["label", credential.label],
18564
18980
  ["auth", credential.authType],
18565
- ["state", state(ctx, status.ok, status.text)],
18981
+ ["state", state(ctx, status2.ok, status2.text)],
18566
18982
  ["tier", String(credential.tier)],
18567
18983
  ["weight", String(credential.weight)],
18568
18984
  ["account", credential.accountEmail ?? "\u2014"],
@@ -18697,12 +19113,12 @@ var credentialsHealth = {
18697
19113
  };
18698
19114
 
18699
19115
  // apps/cli/src/commands/db.ts
18700
- import { existsSync as existsSync2 } from "fs";
19116
+ import { existsSync as existsSync3 } from "fs";
18701
19117
  var dbMigrate = {
18702
19118
  usage: "db migrate",
18703
19119
  summary: "Create or upgrade the database schema",
18704
19120
  async run(_args, { ctx, writer }) {
18705
- const existed = existsSync2(ctx.databasePath);
19121
+ const existed = existsSync3(ctx.databasePath);
18706
19122
  const store = await ctx.store();
18707
19123
  await store.config.getSettings();
18708
19124
  emit(ctx, writer, { path: ctx.databasePath, created: !existed }, () => fields([
@@ -19030,207 +19446,8 @@ ${candidates}${excluded}`;
19030
19446
  import { existsSync as existsSync5 } from "fs";
19031
19447
 
19032
19448
  // apps/cli/src/runtime.ts
19033
- import { closeSync, existsSync as existsSync4, openSync } from "fs";
19449
+ import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2 } from "fs";
19034
19450
  import { join as join3 } from "path";
19035
-
19036
- // apps/cli/src/service.ts
19037
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
19038
- import { homedir as homedir2 } from "os";
19039
- import { dirname, join as join2 } from "path";
19040
- var UNIT_NAME = "omnigateway.service";
19041
- function unitPath(scope) {
19042
- return scope === "system" ? join2("/etc/systemd/system", UNIT_NAME) : join2(homedir2(), ".config", "systemd", "user", UNIT_NAME);
19043
- }
19044
- function systemctlArgs(scope, args) {
19045
- return scope === "system" ? ["systemctl", ...args] : ["systemctl", "--user", ...args];
19046
- }
19047
- function pidFile(stateDir) {
19048
- return join2(stateDir, "gateway.pid");
19049
- }
19050
- function logFile(stateDir) {
19051
- return join2(stateDir, "gateway.log");
19052
- }
19053
- function defaultStateDir(env2) {
19054
- const xdg = env2.XDG_STATE_HOME;
19055
- const base = typeof xdg === "string" && xdg.length > 0 ? xdg : join2(homedir2(), ".local", "state");
19056
- return join2(base, "omnigateway");
19057
- }
19058
- function unitFile(input) {
19059
- return `[Unit]
19060
- Description=${input.description ?? "OmniGateway"}
19061
- After=network-online.target
19062
- Wants=network-online.target
19063
-
19064
- [Service]
19065
- Type=simple
19066
- WorkingDirectory=${input.root}
19067
- EnvironmentFile=${join2(input.root, ".env")}
19068
- ExecStart=${input.bun} ${input.entrypoint}
19069
- Restart=on-failure
19070
- RestartSec=2
19071
-
19072
- [Install]
19073
- WantedBy=default.target
19074
- `;
19075
- }
19076
- function unitInstalled(deps) {
19077
- return existsSync3(deps.unitPath);
19078
- }
19079
- function readPid(stateDir) {
19080
- const file2 = pidFile(stateDir);
19081
- if (!existsSync3(file2))
19082
- return null;
19083
- const pid = Number(readFileSync2(file2, "utf8").trim());
19084
- return Number.isInteger(pid) && pid > 0 ? pid : null;
19085
- }
19086
- function livePid(deps) {
19087
- const pid = readPid(deps.stateDir);
19088
- if (pid === null)
19089
- return null;
19090
- if (deps.alive(pid))
19091
- return pid;
19092
- rmSync(pidFile(deps.stateDir), { force: true });
19093
- return null;
19094
- }
19095
- async function status(deps) {
19096
- if (unitInstalled(deps)) {
19097
- const active = await deps.run(systemctlArgs(deps.scope, ["is-active", UNIT_NAME]));
19098
- const state2 = active.stdout.trim() || active.stderr.trim() || "unknown";
19099
- const shown = await deps.run(systemctlArgs(deps.scope, ["show", UNIT_NAME, "--property=MainPID", "--value"]));
19100
- const pid2 = Number(shown.stdout.trim());
19101
- return {
19102
- supervisor: "systemd",
19103
- running: state2 === "active",
19104
- pid: Number.isInteger(pid2) && pid2 > 0 ? pid2 : null,
19105
- state: state2,
19106
- unitPath: deps.unitPath,
19107
- logFile: null
19108
- };
19109
- }
19110
- const pid = livePid(deps);
19111
- return {
19112
- supervisor: pid === null ? "none" : "pidfile",
19113
- running: pid !== null,
19114
- pid,
19115
- state: null,
19116
- unitPath: null,
19117
- logFile: logFile(deps.stateDir)
19118
- };
19119
- }
19120
- async function start(deps, input) {
19121
- if (unitInstalled(deps)) {
19122
- const result = await deps.run(systemctlArgs(deps.scope, ["start", UNIT_NAME]));
19123
- if (result.code !== 0) {
19124
- throw new Error(result.stderr.trim() || `systemctl start exited ${result.code}`);
19125
- }
19126
- return {
19127
- supervisor: "systemd",
19128
- pid: null,
19129
- healthy: await waitForHealth(deps, input.baseUrl)
19130
- };
19131
- }
19132
- const running = livePid(deps);
19133
- if (running !== null) {
19134
- return {
19135
- supervisor: "pidfile",
19136
- pid: running,
19137
- healthy: await waitForHealth(deps, input.baseUrl)
19138
- };
19139
- }
19140
- mkdirSync(deps.stateDir, { recursive: true });
19141
- const pid = deps.spawn({
19142
- argv: input.argv,
19143
- cwd: deps.root,
19144
- env: input.env,
19145
- logFile: logFile(deps.stateDir)
19146
- });
19147
- writeFileSync(pidFile(deps.stateDir), `${pid}
19148
- `);
19149
- return { supervisor: "pidfile", pid, healthy: await waitForHealth(deps, input.baseUrl) };
19150
- }
19151
- var HEALTH_TIMEOUT_MS = 1e4;
19152
- var HEALTH_INTERVAL_MS = 200;
19153
- async function waitForHealth(deps, baseUrl) {
19154
- const attempts = Math.ceil(HEALTH_TIMEOUT_MS / HEALTH_INTERVAL_MS);
19155
- for (let attempt = 0;attempt < attempts; attempt++) {
19156
- if (await deps.probe(baseUrl))
19157
- return true;
19158
- await deps.sleep(HEALTH_INTERVAL_MS);
19159
- }
19160
- return false;
19161
- }
19162
- var STOP_GRACE_MS = 5000;
19163
- var STOP_INTERVAL_MS = 100;
19164
- async function stop(deps) {
19165
- if (unitInstalled(deps)) {
19166
- const result = await deps.run(systemctlArgs(deps.scope, ["stop", UNIT_NAME]));
19167
- if (result.code !== 0) {
19168
- throw new Error(result.stderr.trim() || `systemctl stop exited ${result.code}`);
19169
- }
19170
- return { supervisor: "systemd", stopped: true };
19171
- }
19172
- const pid = livePid(deps);
19173
- if (pid === null)
19174
- return { supervisor: "none", stopped: false };
19175
- deps.kill(pid, "SIGTERM");
19176
- const attempts = Math.ceil(STOP_GRACE_MS / STOP_INTERVAL_MS);
19177
- for (let attempt = 0;deps.alive(pid); attempt++) {
19178
- if (attempt >= attempts) {
19179
- deps.kill(pid, "SIGKILL");
19180
- break;
19181
- }
19182
- await deps.sleep(STOP_INTERVAL_MS);
19183
- }
19184
- rmSync(pidFile(deps.stateDir), { force: true });
19185
- return { supervisor: "pidfile", stopped: true };
19186
- }
19187
- async function install(deps, input) {
19188
- const path = deps.unitPath;
19189
- if (existsSync3(path) && !input.force) {
19190
- throw new Error(`${path} already exists; pass --force to replace it`);
19191
- }
19192
- mkdirSync(dirname(path), { recursive: true });
19193
- writeFileSync(path, unitFile({ root: deps.root, bun: input.bun, entrypoint: input.entrypoint }));
19194
- const reload = await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
19195
- let enabled = false;
19196
- if (input.enable) {
19197
- const result = await deps.run(systemctlArgs(deps.scope, ["enable", UNIT_NAME]));
19198
- enabled = result.code === 0;
19199
- }
19200
- return { path, reloaded: reload.code === 0, enabled };
19201
- }
19202
- async function uninstall(deps) {
19203
- const path = deps.unitPath;
19204
- if (!existsSync3(path))
19205
- return { path, removed: false };
19206
- await deps.run(systemctlArgs(deps.scope, ["disable", "--now", UNIT_NAME]));
19207
- rmSync(path, { force: true });
19208
- await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
19209
- return { path, removed: true };
19210
- }
19211
- async function serviceLogs(deps, lines) {
19212
- if (unitInstalled(deps)) {
19213
- const args = deps.scope === "system" ? [] : ["--user"];
19214
- const result = await deps.run([
19215
- "journalctl",
19216
- ...args,
19217
- "-u",
19218
- UNIT_NAME,
19219
- "-n",
19220
- String(lines),
19221
- "--no-pager"
19222
- ]);
19223
- return result.stdout.trimEnd();
19224
- }
19225
- const file2 = logFile(deps.stateDir);
19226
- if (!existsSync3(file2))
19227
- return "";
19228
- return readFileSync2(file2, "utf8").split(`
19229
- `).slice(-lines).join(`
19230
- `).trimEnd();
19231
- }
19232
-
19233
- // apps/cli/src/runtime.ts
19234
19451
  var runCommand = async (argv) => {
19235
19452
  const proc = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe" });
19236
19453
  const [stdout, stderr, code] = await Promise.all([
@@ -19241,7 +19458,7 @@ var runCommand = async (argv) => {
19241
19458
  return { code, stdout, stderr };
19242
19459
  };
19243
19460
  var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
19244
- const fd = openSync(logFile2, "a");
19461
+ const fd = openSync2(logFile2, "a");
19245
19462
  try {
19246
19463
  const proc = Bun.spawn([...argv], {
19247
19464
  cwd,
@@ -19253,7 +19470,7 @@ var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
19253
19470
  proc.unref();
19254
19471
  return proc.pid;
19255
19472
  } finally {
19256
- closeSync(fd);
19473
+ closeSync2(fd);
19257
19474
  }
19258
19475
  };
19259
19476
  async function runForeground(input) {
@@ -19294,11 +19511,13 @@ function gatewayEntrypoint(root, cliDir = import.meta.dir) {
19294
19511
  return candidates.find((path) => existsSync4(path)) ?? null;
19295
19512
  }
19296
19513
  function createServiceDeps(input) {
19514
+ const logFile2 = input.env.OMNI_LOG_FILE?.trim();
19297
19515
  return {
19298
19516
  root: input.root,
19299
19517
  stateDir: defaultStateDir(input.env),
19300
19518
  scope: input.scope,
19301
19519
  unitPath: unitPath(input.scope),
19520
+ ...logFile2 === undefined || logFile2.length === 0 ? {} : { logFile: logFile2 },
19302
19521
  run: runCommand,
19303
19522
  spawn: spawnDetached,
19304
19523
  probe: probeHealth,
@@ -19341,7 +19560,7 @@ var start2 = {
19341
19560
  emit(ctx, writer, result, () => {
19342
19561
  const how = result.supervisor === "systemd" ? "systemd" : `pid ${result.pid}`;
19343
19562
  if (!result.healthy) {
19344
- const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" : logFile(deps.stateDir);
19563
+ const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" : supervisedLogFile(deps);
19345
19564
  return `${state(ctx, false, "started but /health never answered")} (${how}); see ${where}`;
19346
19565
  }
19347
19566
  return `${state(ctx, true, "running")} (${how}) at ${baseUrl}`;
@@ -19434,7 +19653,8 @@ var doctor = {
19434
19653
  gatewayEntrypoint: gatewayEntrypoint(deps.root),
19435
19654
  unitInstalled: unit,
19436
19655
  supervisor: status2.supervisor,
19437
- running: status2.running
19656
+ running: status2.running,
19657
+ consoleSource: consoleSource(deps).source
19438
19658
  };
19439
19659
  emit(ctx, writer, checks3, () => {
19440
19660
  const ok = (value, text) => state(ctx, value, text);
@@ -19449,7 +19669,8 @@ var doctor = {
19449
19669
  ["config", checks3.configError === null ? ok(true, "ok") : ok(false, checks3.configError)],
19450
19670
  ["entrypoint", checks3.gatewayEntrypoint ?? ok(false, "not found")],
19451
19671
  ["systemd unit", unit ? deps.scope : paint(ctx, "dim", "none")],
19452
- ["gateway", ok(status2.running, status2.running ? "running" : "stopped")]
19672
+ ["gateway", ok(status2.running, status2.running ? "running" : "stopped")],
19673
+ ["console log", sourceHint(checks3.consoleSource)]
19453
19674
  ]);
19454
19675
  });
19455
19676
  }
@@ -19653,11 +19874,12 @@ var usage = {
19653
19874
  };
19654
19875
  var logs = {
19655
19876
  usage: "logs [-n N] [--follow] [--service]",
19656
- summary: "Show recent requests, or the gateway process's own output",
19877
+ summary: "Show recent requests (--service is an alias for `omni console`)",
19657
19878
  options: {
19658
19879
  number: { type: "string", short: "n" },
19659
19880
  follow: { type: "boolean" },
19660
- service: { type: "boolean" }
19881
+ service: { type: "boolean" },
19882
+ system: { type: "boolean" }
19661
19883
  },
19662
19884
  async run(args, { ctx, writer, service }) {
19663
19885
  const limit = numberFlag(args.values, "number") ?? 20;
@@ -19667,7 +19889,7 @@ var logs = {
19667
19889
  return;
19668
19890
  }
19669
19891
  const store = await ctx.store();
19670
- const render = (rows) => table([
19892
+ const render2 = (rows) => table([
19671
19893
  { header: "AT" },
19672
19894
  { header: "MODEL" },
19673
19895
  { header: "RESOLVED" },
@@ -19685,7 +19907,7 @@ var logs = {
19685
19907
  String(row.durationMs)
19686
19908
  ]));
19687
19909
  const first = await recentLogs(store, limit);
19688
- emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" : render(first));
19910
+ emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" : render2(first));
19689
19911
  if (args.values.follow !== true || ctx.json)
19690
19912
  return;
19691
19913
  let seen = first[0]?.at ?? 0;
@@ -19695,7 +19917,7 @@ var logs = {
19695
19917
  if (next.length === 0)
19696
19918
  continue;
19697
19919
  seen = next[0]?.at ?? seen;
19698
- writer.out(render(next.reverse()));
19920
+ writer.out(render2(next.reverse()));
19699
19921
  }
19700
19922
  }
19701
19923
  };
@@ -19708,6 +19930,7 @@ var COMMANDS = {
19708
19930
  restart,
19709
19931
  doctor,
19710
19932
  logs,
19933
+ console: console_,
19711
19934
  usage,
19712
19935
  connect,
19713
19936
  "service install": serviceInstall,
@@ -19770,7 +19993,10 @@ function resolveCommand(argv) {
19770
19993
 
19771
19994
  // apps/cli/src/help.ts
19772
19995
  var GROUPS = [
19773
- { title: "Gateway", prefixes: ["status", "start", "stop", "restart", "doctor", "logs"] },
19996
+ {
19997
+ title: "Gateway",
19998
+ prefixes: ["status", "start", "stop", "restart", "doctor", "logs", "console"]
19999
+ },
19774
20000
  { title: "Service", prefixes: ["service "] },
19775
20001
  { title: "Accounts", prefixes: ["connect", "credentials "] },
19776
20002
  { title: "Models", prefixes: ["models "] },