omnigateway 0.1.6 → 0.1.8
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.
- package/README.md +24 -5
- package/bin/omni.js +805 -352
- package/gateway.js +622 -236
- package/package.json +1 -1
- package/public/assets/{Chip-Ccqsb_Mq.js → Chip-CM9ZMdr9.js} +2 -2
- package/public/assets/{CopyValue-cAwgC1sU.js → CopyValue-WrOTcHwq.js} +4 -4
- package/public/assets/{Field-Devm4K46.js → Field-Br6jKPz8.js} +8 -8
- package/public/assets/{Lamp-BXSUIAtG.js → Lamp-DO-sRDg2.js} +7 -7
- package/public/assets/{Meter-Fn8X7t60.js → Meter-BzMaum0C.js} +2 -2
- package/public/assets/{Modal-Dqir9wBz.js → Modal-lhjr949H.js} +8 -8
- package/public/assets/Rack-BFWC50ex.js +147 -0
- package/public/assets/{Readout-BKpOZIGX.js → Readout-DMJs4gYD.js} +5 -5
- package/public/assets/{States-DBzsiHjQ.js → States-BNeCLhZn.js} +10 -10
- package/public/assets/{Table-DPDJeeVc.js → Table-nOg_lgEJ.js} +1 -1
- package/public/assets/{Toggle-BhViDMm9.js → Toggle-DYoCVD37.js} +3 -3
- package/public/assets/_app-lXr06Xli.js +1 -0
- package/public/assets/_app.accounts-BTb5vHI_.js +51 -0
- package/public/assets/_app.console-DHUClMsI.js +25 -0
- package/public/assets/_app.index-DZ--9mKU.js +61 -0
- package/public/assets/_app.keys-B-cy-2DS.js +39 -0
- package/public/assets/_app.logs-BQjAIT2P.js +33 -0
- package/public/assets/{_app.models-CuD-JtGS.js → _app.models-BAmXHlf2.js} +27 -27
- package/public/assets/_app.settings-CG8bv_rm.js +34 -0
- package/public/assets/{_app.usage-DAyYUfIi.js → _app.usage-BLrQ2hB8.js} +28 -28
- package/public/assets/index-UAZ0O5y0.js +170 -0
- package/public/assets/{login-DXHM-Noj.js → login-BpUl6C5b.js} +8 -8
- package/public/assets/{queries-BMwc6EvM.js → queries-D_-Jj9vt.js} +12 -12
- package/public/assets/{trash-2-JF_uX2gE.js → trash-2-DjWP2u19.js} +1 -1
- package/public/index.html +2 -2
- package/public/assets/Rack-XtfiZ6GC.js +0 -147
- package/public/assets/_app-B8lqq_ks.js +0 -1
- package/public/assets/_app.accounts-DSMHSYBF.js +0 -51
- package/public/assets/_app.index-DXH5sH41.js +0 -61
- package/public/assets/_app.keys-DdEyVy_b.js +0 -39
- package/public/assets/_app.logs-Camxditb.js +0 -33
- package/public/assets/_app.settings-DA1aYcR4.js +0 -16
- package/public/assets/index-BtjyVE1i.js +0 -170
package/bin/omni.js
CHANGED
|
@@ -90,6 +90,11 @@ function formatUsd(value) {
|
|
|
90
90
|
return `$${value.toFixed(value < 1 ? 4 : 2)}`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// apps/cli/src/run.ts
|
|
94
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
95
|
+
import { homedir as homedir3 } from "os";
|
|
96
|
+
import { dirname as dirname3 } from "path";
|
|
97
|
+
|
|
93
98
|
// packages/control/src/adminAuth.ts
|
|
94
99
|
import { hash, verify } from "@node-rs/argon2";
|
|
95
100
|
var MIN_PASSWORD_LENGTH = 12;
|
|
@@ -188,6 +193,23 @@ class GatewayError extends Error {
|
|
|
188
193
|
}
|
|
189
194
|
// packages/ir/src/logger.ts
|
|
190
195
|
var LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
196
|
+
var ANSI2 = /\u001B\[[0-9;]*m/g;
|
|
197
|
+
var HEAD = /^(\S+) (DEBUG|INFO|WARN|ERROR) {1,2}(.*)$/;
|
|
198
|
+
var FIELDS = / {2}(?=[a-zA-Z]+=)/;
|
|
199
|
+
function parseLine(raw) {
|
|
200
|
+
const absent = { raw, at: null, level: null, msg: null };
|
|
201
|
+
const match = HEAD.exec(raw.replace(ANSI2, ""));
|
|
202
|
+
if (match === null)
|
|
203
|
+
return absent;
|
|
204
|
+
const [, instant, label, tail] = match;
|
|
205
|
+
if (instant === undefined || label === undefined || tail === undefined)
|
|
206
|
+
return absent;
|
|
207
|
+
const at = Date.parse(instant);
|
|
208
|
+
if (Number.isNaN(at))
|
|
209
|
+
return absent;
|
|
210
|
+
const msg = tail.split(FIELDS, 1)[0] ?? tail;
|
|
211
|
+
return { raw, at, level: label.toLowerCase(), msg };
|
|
212
|
+
}
|
|
191
213
|
var noopLogger = {
|
|
192
214
|
debug: () => {},
|
|
193
215
|
info: () => {},
|
|
@@ -216,6 +238,7 @@ function usageFromPromptTotal(promptTokens, outputTokens, cacheReadTokens, cache
|
|
|
216
238
|
}
|
|
217
239
|
// packages/control/src/config.ts
|
|
218
240
|
var MIN_KEY_LENGTH = 16;
|
|
241
|
+
var TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
219
242
|
var DECIMAL_INTEGER = /^\d+$/;
|
|
220
243
|
function optionalText(value, fallback) {
|
|
221
244
|
return value?.trim() || fallback;
|
|
@@ -234,6 +257,8 @@ function loadConfig(env) {
|
|
|
234
257
|
const derivedBaseUrl = `http://${host}:${port}`;
|
|
235
258
|
const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
|
|
236
259
|
const staticDir = env.OMNI_STATIC_DIR?.trim();
|
|
260
|
+
const logFile = env.OMNI_LOG_FILE?.trim();
|
|
261
|
+
const exposeClaudeCodeAliases = TRUTHY.has((env.OMNI_EXPOSE_CLAUDE_CODE_ALIASES ?? "").trim().toLowerCase());
|
|
237
262
|
const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
|
|
238
263
|
const logLevel = parseLogLevel(rawLogLevel);
|
|
239
264
|
return {
|
|
@@ -244,9 +269,15 @@ function loadConfig(env) {
|
|
|
244
269
|
databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
|
|
245
270
|
encryptionKey,
|
|
246
271
|
baseUrl,
|
|
247
|
-
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
|
|
272
|
+
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
|
|
273
|
+
exposeClaudeCodeAliases,
|
|
274
|
+
logFile: logFile === undefined || logFile.length === 0 ? null : logFile
|
|
248
275
|
};
|
|
249
276
|
}
|
|
277
|
+
// packages/providers/src/betas.ts
|
|
278
|
+
var CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
279
|
+
var CONTEXT_1M_TOKENS = 1e6;
|
|
280
|
+
|
|
250
281
|
// packages/providers/src/body.ts
|
|
251
282
|
var BODY_ORDER = {
|
|
252
283
|
anthropic: [
|
|
@@ -354,6 +385,119 @@ function signAnthropicBody(json) {
|
|
|
354
385
|
return json.replace(needle, `cch=${computeCch(json)};`);
|
|
355
386
|
}
|
|
356
387
|
|
|
388
|
+
// packages/providers/src/anthropic/models.ts
|
|
389
|
+
var ANTHROPIC_MODELS = {
|
|
390
|
+
defaultModel: "claude-opus-5",
|
|
391
|
+
models: [
|
|
392
|
+
{
|
|
393
|
+
id: "claude-fable-5",
|
|
394
|
+
label: "Claude Fable 5",
|
|
395
|
+
pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
|
|
396
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "claude-opus-5",
|
|
400
|
+
label: "Claude Opus 5",
|
|
401
|
+
pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
|
|
402
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: "claude-sonnet-5",
|
|
406
|
+
label: "Claude Sonnet 5",
|
|
407
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
|
|
408
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
id: "claude-haiku-4-5",
|
|
412
|
+
label: "Claude Haiku 4.5",
|
|
413
|
+
pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
|
|
414
|
+
limits: { contextWindow: 200000, maxOutputTokens: 64000 }
|
|
415
|
+
}
|
|
416
|
+
]
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// packages/providers/src/kimi/models.ts
|
|
420
|
+
var KIMI_MODELS = {
|
|
421
|
+
defaultModel: "k3-256k",
|
|
422
|
+
models: [
|
|
423
|
+
{
|
|
424
|
+
id: "k3-256k",
|
|
425
|
+
label: "Kimi K3 \u2014 256K",
|
|
426
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
427
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "k3",
|
|
431
|
+
label: "Kimi K3 \u2014 up to 1M",
|
|
432
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
433
|
+
limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: "kimi-for-coding",
|
|
437
|
+
label: "Kimi K2.7 Code",
|
|
438
|
+
pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
439
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: "kimi-for-coding-highspeed",
|
|
443
|
+
label: "Kimi K2.7 Code \u2014 High Speed",
|
|
444
|
+
pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
445
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
// packages/providers/src/openai/models.ts
|
|
451
|
+
var OPENAI_MODELS = {
|
|
452
|
+
defaultModel: "gpt-5.6",
|
|
453
|
+
models: [
|
|
454
|
+
{
|
|
455
|
+
id: "gpt-5.6",
|
|
456
|
+
label: "GPT-5.6 \u2014 routes to Sol",
|
|
457
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
458
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
459
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
id: "gpt-5.6-sol",
|
|
463
|
+
label: "GPT-5.6 Sol \u2014 deepest reasoning",
|
|
464
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
465
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
466
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
id: "gpt-5.6-terra",
|
|
470
|
+
label: "GPT-5.6 Terra \u2014 balanced",
|
|
471
|
+
pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
472
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
473
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: "gpt-5.6-luna",
|
|
477
|
+
label: "GPT-5.6 Luna \u2014 fastest",
|
|
478
|
+
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
479
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
480
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// packages/providers/src/catalog.ts
|
|
486
|
+
var PROVIDER_MODEL_CATALOG = {
|
|
487
|
+
anthropic: ANTHROPIC_MODELS,
|
|
488
|
+
openai: OPENAI_MODELS,
|
|
489
|
+
kimi: KIMI_MODELS
|
|
490
|
+
};
|
|
491
|
+
function catalogPricing(provider, model) {
|
|
492
|
+
return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
|
|
493
|
+
}
|
|
494
|
+
function catalogLimits(provider, model, auth = "apiKey") {
|
|
495
|
+
const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
|
|
496
|
+
if (entry === undefined)
|
|
497
|
+
return null;
|
|
498
|
+
return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
|
|
499
|
+
}
|
|
500
|
+
|
|
357
501
|
// packages/providers/src/http.ts
|
|
358
502
|
function codeForStatus(status) {
|
|
359
503
|
if (status === 401 || status === 403)
|
|
@@ -669,7 +813,7 @@ async function* decodeAnthropic(messages) {
|
|
|
669
813
|
if (cb.type === "text")
|
|
670
814
|
yield { type: "blockStart", index, block: { type: "text" } };
|
|
671
815
|
else if (cb.type === "thinking")
|
|
672
|
-
yield { type: "blockStart", index, block: { type: "thinking" } };
|
|
816
|
+
yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
|
|
673
817
|
else if (cb.type === "tool_use")
|
|
674
818
|
yield {
|
|
675
819
|
type: "blockStart",
|
|
@@ -689,7 +833,7 @@ async function* decodeAnthropic(messages) {
|
|
|
689
833
|
index,
|
|
690
834
|
delta: { type: "thinking", text: delta.thinking ?? "" }
|
|
691
835
|
};
|
|
692
|
-
else if (delta.type === "signature_delta")
|
|
836
|
+
else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
|
|
693
837
|
yield {
|
|
694
838
|
type: "blockDelta",
|
|
695
839
|
index,
|
|
@@ -761,6 +905,9 @@ function wireCacheControl(c) {
|
|
|
761
905
|
return {};
|
|
762
906
|
return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
|
|
763
907
|
}
|
|
908
|
+
function isReplayableThinking(b) {
|
|
909
|
+
return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
|
|
910
|
+
}
|
|
764
911
|
function encodeBlock(b) {
|
|
765
912
|
const cache = wireCacheControl(cacheControlOf(b));
|
|
766
913
|
switch (b.type) {
|
|
@@ -790,6 +937,16 @@ function encodeSystemTurn(content) {
|
|
|
790
937
|
return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
|
|
791
938
|
`);
|
|
792
939
|
}
|
|
940
|
+
function systemCacheControl(req) {
|
|
941
|
+
const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
|
|
942
|
+
const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
|
|
943
|
+
const final = markedSystemBlocks.at(-1);
|
|
944
|
+
const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
|
|
945
|
+
return {
|
|
946
|
+
...promoted === undefined ? {} : { promoted },
|
|
947
|
+
lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
|
|
948
|
+
};
|
|
949
|
+
}
|
|
793
950
|
function encodeToolChoice(c) {
|
|
794
951
|
switch (c.type) {
|
|
795
952
|
case "auto":
|
|
@@ -813,18 +970,30 @@ function toWire(req, model, opts) {
|
|
|
813
970
|
system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
|
|
814
971
|
note2("anthropic:oauth-system-prefix");
|
|
815
972
|
}
|
|
973
|
+
const systemCache = systemCacheControl(req);
|
|
974
|
+
if (systemCache.lost)
|
|
975
|
+
note2("anthropic:system-turn-cache-control-dropped");
|
|
816
976
|
const body = {
|
|
817
977
|
model,
|
|
818
|
-
messages: req.messages.
|
|
819
|
-
if (m.role !== "system")
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
978
|
+
messages: req.messages.flatMap((m) => {
|
|
979
|
+
if (m.role !== "system") {
|
|
980
|
+
const replayable = m.content.filter(isReplayableThinking);
|
|
981
|
+
if (replayable.length !== m.content.length)
|
|
982
|
+
note2("anthropic:unsigned-thinking-dropped");
|
|
983
|
+
if (replayable.length === 0)
|
|
984
|
+
return [];
|
|
985
|
+
return [{ role: m.role, content: replayable.map(encodeBlock) }];
|
|
986
|
+
}
|
|
987
|
+
return [{ role: m.role, content: encodeSystemTurn(m.content) }];
|
|
825
988
|
}),
|
|
826
989
|
max_tokens: req.maxTokens ?? 4096,
|
|
827
|
-
stream: req.stream
|
|
990
|
+
stream: req.stream,
|
|
991
|
+
...systemCache.promoted === undefined ? {} : {
|
|
992
|
+
cache_control: {
|
|
993
|
+
type: systemCache.promoted.type,
|
|
994
|
+
...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
|
|
995
|
+
}
|
|
996
|
+
}
|
|
828
997
|
};
|
|
829
998
|
if (system !== undefined && system.length > 0)
|
|
830
999
|
body.system = system;
|
|
@@ -886,6 +1055,15 @@ var anthropicAdapter = {
|
|
|
886
1055
|
["Accept", "text/event-stream"]
|
|
887
1056
|
];
|
|
888
1057
|
const betas = new Set(req.request.betas ?? []);
|
|
1058
|
+
const notes = [...degradations];
|
|
1059
|
+
if (betas.has(CONTEXT_1M_BETA)) {
|
|
1060
|
+
const limits = catalogLimits("anthropic", req.model, oauth ? "oauth" : "apiKey");
|
|
1061
|
+
const window = limits?.contextWindow;
|
|
1062
|
+
if (window !== undefined && window < CONTEXT_1M_TOKENS) {
|
|
1063
|
+
betas.delete(CONTEXT_1M_BETA);
|
|
1064
|
+
notes.push("anthropic:context-1m-dropped");
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
889
1067
|
if (oauth) {
|
|
890
1068
|
protocol.push(["Authorization", `Bearer ${req.credentials.accessToken}`]);
|
|
891
1069
|
betas.add(OAUTH_BETA);
|
|
@@ -913,121 +1091,9 @@ var anthropicAdapter = {
|
|
|
913
1091
|
throw await httpError(res, "anthropic");
|
|
914
1092
|
if (res.body === null)
|
|
915
1093
|
throw new GatewayError("UPSTREAM", "empty response body", { provider: "anthropic" });
|
|
916
|
-
return { events: decodeAnthropic(parseSse(res.body)), degradations };
|
|
1094
|
+
return { events: decodeAnthropic(parseSse(res.body)), degradations: notes };
|
|
917
1095
|
}
|
|
918
1096
|
};
|
|
919
|
-
// packages/providers/src/anthropic/models.ts
|
|
920
|
-
var ANTHROPIC_MODELS = {
|
|
921
|
-
defaultModel: "claude-opus-5",
|
|
922
|
-
models: [
|
|
923
|
-
{
|
|
924
|
-
id: "claude-fable-5",
|
|
925
|
-
label: "Claude Fable 5",
|
|
926
|
-
pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
|
|
927
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
928
|
-
},
|
|
929
|
-
{
|
|
930
|
-
id: "claude-opus-5",
|
|
931
|
-
label: "Claude Opus 5",
|
|
932
|
-
pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
|
|
933
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
id: "claude-sonnet-5",
|
|
937
|
-
label: "Claude Sonnet 5",
|
|
938
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
|
|
939
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
940
|
-
},
|
|
941
|
-
{
|
|
942
|
-
id: "claude-haiku-4-5",
|
|
943
|
-
label: "Claude Haiku 4.5",
|
|
944
|
-
pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
|
|
945
|
-
limits: { contextWindow: 200000, maxOutputTokens: 64000 }
|
|
946
|
-
}
|
|
947
|
-
]
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
// packages/providers/src/kimi/models.ts
|
|
951
|
-
var KIMI_MODELS = {
|
|
952
|
-
defaultModel: "k3-256k",
|
|
953
|
-
models: [
|
|
954
|
-
{
|
|
955
|
-
id: "k3-256k",
|
|
956
|
-
label: "Kimi K3 \u2014 256K",
|
|
957
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
958
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
959
|
-
},
|
|
960
|
-
{
|
|
961
|
-
id: "k3",
|
|
962
|
-
label: "Kimi K3 \u2014 up to 1M",
|
|
963
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
964
|
-
limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
|
|
965
|
-
},
|
|
966
|
-
{
|
|
967
|
-
id: "kimi-for-coding",
|
|
968
|
-
label: "Kimi K2.7 Code",
|
|
969
|
-
pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
970
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
971
|
-
},
|
|
972
|
-
{
|
|
973
|
-
id: "kimi-for-coding-highspeed",
|
|
974
|
-
label: "Kimi K2.7 Code \u2014 High Speed",
|
|
975
|
-
pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
976
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
977
|
-
}
|
|
978
|
-
]
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
// packages/providers/src/openai/models.ts
|
|
982
|
-
var OPENAI_MODELS = {
|
|
983
|
-
defaultModel: "gpt-5.6",
|
|
984
|
-
models: [
|
|
985
|
-
{
|
|
986
|
-
id: "gpt-5.6",
|
|
987
|
-
label: "GPT-5.6 \u2014 routes to Sol",
|
|
988
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
989
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
990
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
id: "gpt-5.6-sol",
|
|
994
|
-
label: "GPT-5.6 Sol \u2014 deepest reasoning",
|
|
995
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
996
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
997
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
998
|
-
},
|
|
999
|
-
{
|
|
1000
|
-
id: "gpt-5.6-terra",
|
|
1001
|
-
label: "GPT-5.6 Terra \u2014 balanced",
|
|
1002
|
-
pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1003
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1004
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
1005
|
-
},
|
|
1006
|
-
{
|
|
1007
|
-
id: "gpt-5.6-luna",
|
|
1008
|
-
label: "GPT-5.6 Luna \u2014 fastest",
|
|
1009
|
-
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1010
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1011
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
1012
|
-
}
|
|
1013
|
-
]
|
|
1014
|
-
};
|
|
1015
|
-
|
|
1016
|
-
// packages/providers/src/catalog.ts
|
|
1017
|
-
var PROVIDER_MODEL_CATALOG = {
|
|
1018
|
-
anthropic: ANTHROPIC_MODELS,
|
|
1019
|
-
openai: OPENAI_MODELS,
|
|
1020
|
-
kimi: KIMI_MODELS
|
|
1021
|
-
};
|
|
1022
|
-
function catalogPricing(provider, model) {
|
|
1023
|
-
return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
|
|
1024
|
-
}
|
|
1025
|
-
function catalogLimits(provider, model, auth = "apiKey") {
|
|
1026
|
-
const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
|
|
1027
|
-
if (entry === undefined)
|
|
1028
|
-
return null;
|
|
1029
|
-
return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
|
|
1030
|
-
}
|
|
1031
1097
|
// packages/providers/src/http-client.ts
|
|
1032
1098
|
import { request as httpRequest } from "http";
|
|
1033
1099
|
import { request as httpsRequest } from "https";
|
|
@@ -1253,6 +1319,8 @@ function toChatWire(req, model) {
|
|
|
1253
1319
|
if (!degradations.includes(d))
|
|
1254
1320
|
degradations.push(d);
|
|
1255
1321
|
};
|
|
1322
|
+
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
1323
|
+
note2("kimi:context-1m-dropped");
|
|
1256
1324
|
const messages = [];
|
|
1257
1325
|
const system = req.system?.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
|
|
1258
1326
|
|
|
@@ -1529,6 +1597,8 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
1529
1597
|
if (!degradations.includes(d))
|
|
1530
1598
|
degradations.push(d);
|
|
1531
1599
|
};
|
|
1600
|
+
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
1601
|
+
note2("openai:context-1m-dropped");
|
|
1532
1602
|
for (const message of req.messages) {
|
|
1533
1603
|
const parts = [];
|
|
1534
1604
|
const inlined = message.role === "system";
|
|
@@ -2181,6 +2251,60 @@ function createConnectFlows(deps) {
|
|
|
2181
2251
|
}
|
|
2182
2252
|
};
|
|
2183
2253
|
}
|
|
2254
|
+
// packages/control/src/console.ts
|
|
2255
|
+
var UNIT_NAME = "omnigateway.service";
|
|
2256
|
+
var MAX_CONSOLE_LINES = 500;
|
|
2257
|
+
var DEFAULT_CONSOLE_LINES = 200;
|
|
2258
|
+
function resolveConsoleSource(input) {
|
|
2259
|
+
const path = input.logFile?.trim();
|
|
2260
|
+
if (path !== undefined && path.length > 0)
|
|
2261
|
+
return { kind: "file", path };
|
|
2262
|
+
if (input.unitInstalled)
|
|
2263
|
+
return { kind: "journal", unit: UNIT_NAME, scope: input.scope };
|
|
2264
|
+
return { kind: "none" };
|
|
2265
|
+
}
|
|
2266
|
+
function consoleLimit(requested) {
|
|
2267
|
+
const value = requested === undefined ? DEFAULT_CONSOLE_LINES : Number(requested);
|
|
2268
|
+
if (!Number.isFinite(value))
|
|
2269
|
+
return DEFAULT_CONSOLE_LINES;
|
|
2270
|
+
return Math.floor(Math.min(Math.max(1, value), MAX_CONSOLE_LINES));
|
|
2271
|
+
}
|
|
2272
|
+
function keep(line, query) {
|
|
2273
|
+
if (query.since !== undefined && (line.at === null || line.at <= query.since))
|
|
2274
|
+
return false;
|
|
2275
|
+
if (query.level === undefined || line.level === null)
|
|
2276
|
+
return true;
|
|
2277
|
+
return LEVELS[line.level] >= LEVELS[query.level];
|
|
2278
|
+
}
|
|
2279
|
+
var SCAN_FACTOR = 20;
|
|
2280
|
+
var SCAN_LIMIT = 5000;
|
|
2281
|
+
function scanWidth(query) {
|
|
2282
|
+
const filtered = query.level !== undefined || query.since !== undefined;
|
|
2283
|
+
return filtered ? Math.min(query.lines * SCAN_FACTOR, SCAN_LIMIT) : query.lines;
|
|
2284
|
+
}
|
|
2285
|
+
async function readSource(deps, source, lines) {
|
|
2286
|
+
if (source.kind === "none")
|
|
2287
|
+
return "";
|
|
2288
|
+
if (source.kind === "file")
|
|
2289
|
+
return deps.readFile(source.path, lines) ?? "";
|
|
2290
|
+
const unit = source.scope === "system" ? ["-u", source.unit] : [`--user-unit=${source.unit}`];
|
|
2291
|
+
const result = await deps.run([
|
|
2292
|
+
"journalctl",
|
|
2293
|
+
...unit,
|
|
2294
|
+
"-n",
|
|
2295
|
+
String(lines),
|
|
2296
|
+
"--no-pager",
|
|
2297
|
+
"--output=cat"
|
|
2298
|
+
]);
|
|
2299
|
+
return result.code === 0 ? result.stdout : "";
|
|
2300
|
+
}
|
|
2301
|
+
async function readConsole(deps, source, query) {
|
|
2302
|
+
const limited = { ...query, lines: consoleLimit(query.lines) };
|
|
2303
|
+
const text = await readSource(deps, source, scanWidth(limited));
|
|
2304
|
+
const lines = text.split(`
|
|
2305
|
+
`).filter((raw) => raw.trim().length > 0).map(parseLine).filter((line) => keep(line, limited)).slice(-limited.lines);
|
|
2306
|
+
return source.kind === "file" ? { source: "file", path: source.path, lines } : { source: source.kind, lines };
|
|
2307
|
+
}
|
|
2184
2308
|
// node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
2185
2309
|
var exports_external = {};
|
|
2186
2310
|
__export(exports_external, {
|
|
@@ -16473,7 +16597,9 @@ var dryRunSchema = exports_external.object({
|
|
|
16473
16597
|
reasoning: exports_external.boolean().default(false)
|
|
16474
16598
|
}).strict();
|
|
16475
16599
|
var modelSchema = exports_external.object({
|
|
16476
|
-
id: exports_external.string().min(1),
|
|
16600
|
+
id: exports_external.string().min(1).refine((value) => !value.toLowerCase().startsWith("claude/"), {
|
|
16601
|
+
message: 'model id must not start with "claude/": that prefix is reserved for discovery mirrors'
|
|
16602
|
+
}),
|
|
16477
16603
|
strategy: exports_external.enum(["score", "priority", "roundRobin", "weighted"]),
|
|
16478
16604
|
isAlias: exports_external.boolean(),
|
|
16479
16605
|
targets: exports_external.array(exports_external.object({
|
|
@@ -17885,11 +18011,65 @@ async function createKey(store, input) {
|
|
|
17885
18011
|
async function revokeKey(store, id) {
|
|
17886
18012
|
await store.keys.revoke(id);
|
|
17887
18013
|
}
|
|
17888
|
-
// packages/control/src/
|
|
17889
|
-
|
|
17890
|
-
|
|
18014
|
+
// packages/control/src/modelLimits.ts
|
|
18015
|
+
function narrower(a, b) {
|
|
18016
|
+
const context = [a.contextWindow, b.contextWindow].filter((n) => n !== undefined);
|
|
18017
|
+
const output = [a.maxOutputTokens, b.maxOutputTokens].filter((n) => n !== undefined);
|
|
18018
|
+
return {
|
|
18019
|
+
...context.length === 0 ? {} : { contextWindow: Math.min(...context) },
|
|
18020
|
+
...output.length === 0 ? {} : { maxOutputTokens: Math.min(...output) }
|
|
18021
|
+
};
|
|
17891
18022
|
}
|
|
17892
|
-
|
|
18023
|
+
function targetLimits(target, auths) {
|
|
18024
|
+
const ways = auths.size === 0 ? ["apiKey"] : [...auths];
|
|
18025
|
+
let listed = {};
|
|
18026
|
+
for (const auth of ways) {
|
|
18027
|
+
const entry = catalogLimits(target.provider, target.model, auth);
|
|
18028
|
+
if (entry === null)
|
|
18029
|
+
continue;
|
|
18030
|
+
listed = narrower(listed, {
|
|
18031
|
+
contextWindow: entry.contextWindow,
|
|
18032
|
+
maxOutputTokens: entry.maxOutputTokens
|
|
18033
|
+
});
|
|
18034
|
+
}
|
|
18035
|
+
const contextWindow = target.contextWindow ?? listed.contextWindow;
|
|
18036
|
+
const maxOutputTokens = target.maxOutputTokens ?? listed.maxOutputTokens;
|
|
18037
|
+
return {
|
|
18038
|
+
...contextWindow === undefined ? {} : { contextWindow },
|
|
18039
|
+
...maxOutputTokens === undefined ? {} : { maxOutputTokens }
|
|
18040
|
+
};
|
|
18041
|
+
}
|
|
18042
|
+
function servingAuths(credentials) {
|
|
18043
|
+
const byProvider = new Map;
|
|
18044
|
+
for (const credential of credentials) {
|
|
18045
|
+
if (!credential.enabled)
|
|
18046
|
+
continue;
|
|
18047
|
+
const ways = byProvider.get(credential.provider) ?? new Set;
|
|
18048
|
+
ways.add(credential.authType);
|
|
18049
|
+
byProvider.set(credential.provider, ways);
|
|
18050
|
+
}
|
|
18051
|
+
return byProvider;
|
|
18052
|
+
}
|
|
18053
|
+
function resolveModelLimits(model, credentials) {
|
|
18054
|
+
const auths = servingAuths(credentials);
|
|
18055
|
+
let limits = {};
|
|
18056
|
+
for (const target of model.targets) {
|
|
18057
|
+
limits = narrower(limits, targetLimits(target, auths.get(target.provider) ?? new Set));
|
|
18058
|
+
}
|
|
18059
|
+
return limits;
|
|
18060
|
+
}
|
|
18061
|
+
function modelDisplayName(model) {
|
|
18062
|
+
const only = model.targets.length === 1 ? model.targets[0] : undefined;
|
|
18063
|
+
if (only === undefined)
|
|
18064
|
+
return model.id;
|
|
18065
|
+
const labelled = PROVIDER_MODEL_CATALOG[only.provider]?.models.find((choice) => choice.id === only.model);
|
|
18066
|
+
return labelled?.label ?? model.id;
|
|
18067
|
+
}
|
|
18068
|
+
// packages/control/src/models.ts
|
|
18069
|
+
async function listModels(store) {
|
|
18070
|
+
return store.config.listModels();
|
|
18071
|
+
}
|
|
18072
|
+
async function getModel(store, id) {
|
|
17893
18073
|
const model = (await store.config.listModels()).find((m) => m.id === id);
|
|
17894
18074
|
if (model === undefined)
|
|
17895
18075
|
throw new GatewayError("MODEL_UNAVAILABLE", `no virtual model "${id}"`);
|
|
@@ -18241,6 +18421,115 @@ async function putSettings(store, input) {
|
|
|
18241
18421
|
await store.config.putSettings(settings);
|
|
18242
18422
|
return settings;
|
|
18243
18423
|
}
|
|
18424
|
+
// packages/control/src/setup.ts
|
|
18425
|
+
var KEY_PLACEHOLDER = "<your OmniGateway key>";
|
|
18426
|
+
async function describeModelsForSetup(store) {
|
|
18427
|
+
const models = await listModels(store);
|
|
18428
|
+
const credentials = (await listCredentials(store)).map((credential) => ({
|
|
18429
|
+
provider: credential.provider,
|
|
18430
|
+
authType: credential.authType,
|
|
18431
|
+
enabled: credential.enabled
|
|
18432
|
+
}));
|
|
18433
|
+
return models.map((model) => ({
|
|
18434
|
+
model,
|
|
18435
|
+
limits: resolveModelLimits(model, credentials),
|
|
18436
|
+
label: modelDisplayName(model)
|
|
18437
|
+
}));
|
|
18438
|
+
}
|
|
18439
|
+
function slug(id) {
|
|
18440
|
+
return encodeURIComponent(id).replace(/\./g, "%2E");
|
|
18441
|
+
}
|
|
18442
|
+
function claudeProfiles(described, input) {
|
|
18443
|
+
const apiKey = input.apiKey ?? KEY_PLACEHOLDER;
|
|
18444
|
+
return described.map(({ model, limits }) => {
|
|
18445
|
+
const useMirror = input.discoveryMirrors === true && !/^(?:claude|anthropic)/i.test(model.id);
|
|
18446
|
+
const modelId = useMirror ? `claude/${model.id}` : model.id;
|
|
18447
|
+
const env2 = {
|
|
18448
|
+
ANTHROPIC_BASE_URL: input.baseUrl,
|
|
18449
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
18450
|
+
ANTHROPIC_MODEL: modelId,
|
|
18451
|
+
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1"
|
|
18452
|
+
};
|
|
18453
|
+
if (limits.contextWindow !== undefined && !/^claude-/i.test(modelId)) {
|
|
18454
|
+
env2.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(limits.contextWindow);
|
|
18455
|
+
}
|
|
18456
|
+
return {
|
|
18457
|
+
path: `${slug(model.id)}/settings.json`,
|
|
18458
|
+
contents: `${JSON.stringify({ env: env2 }, null, 2)}
|
|
18459
|
+
`
|
|
18460
|
+
};
|
|
18461
|
+
});
|
|
18462
|
+
}
|
|
18463
|
+
function opencodeConfig(described, input) {
|
|
18464
|
+
const models = {};
|
|
18465
|
+
for (const { model, limits, label: label2 } of described) {
|
|
18466
|
+
const limit = limits.contextWindow === undefined ? undefined : {
|
|
18467
|
+
context: limits.contextWindow,
|
|
18468
|
+
...limits.maxOutputTokens === undefined ? {} : { output: limits.maxOutputTokens }
|
|
18469
|
+
};
|
|
18470
|
+
models[model.id] = { name: label2, ...limit === undefined ? {} : { limit } };
|
|
18471
|
+
}
|
|
18472
|
+
const contents = `${JSON.stringify({
|
|
18473
|
+
$schema: "https://opencode.ai/config.json",
|
|
18474
|
+
provider: {
|
|
18475
|
+
omnigateway: {
|
|
18476
|
+
npm: "@ai-sdk/openai-compatible",
|
|
18477
|
+
name: "OmniGateway",
|
|
18478
|
+
options: {
|
|
18479
|
+
baseURL: `${input.baseUrl.replace(/\/+$/, "").replace(/(?:\/v1)+$/i, "")}/v1`,
|
|
18480
|
+
apiKey: input.apiKey ?? KEY_PLACEHOLDER
|
|
18481
|
+
},
|
|
18482
|
+
models
|
|
18483
|
+
}
|
|
18484
|
+
}
|
|
18485
|
+
}, null, 2)}
|
|
18486
|
+
`;
|
|
18487
|
+
return { path: "opencode.json", contents };
|
|
18488
|
+
}
|
|
18489
|
+
// packages/control/src/tail.ts
|
|
18490
|
+
import { closeSync, fstatSync, openSync, readSync, statSync } from "fs";
|
|
18491
|
+
var CHUNK = 64 * 1024;
|
|
18492
|
+
var MAX_BYTES = 8 * 1024 * 1024;
|
|
18493
|
+
function tailFile(path, lines) {
|
|
18494
|
+
let fd;
|
|
18495
|
+
try {
|
|
18496
|
+
fd = openSync(path, "r");
|
|
18497
|
+
} catch {
|
|
18498
|
+
return null;
|
|
18499
|
+
}
|
|
18500
|
+
try {
|
|
18501
|
+
const size = fstatSync(fd).size;
|
|
18502
|
+
if (size === 0)
|
|
18503
|
+
return "";
|
|
18504
|
+
const budget = Math.min(size, MAX_BYTES);
|
|
18505
|
+
let read = 0;
|
|
18506
|
+
let newlines = 0;
|
|
18507
|
+
const chunks = [];
|
|
18508
|
+
while (read < budget && newlines <= lines) {
|
|
18509
|
+
const span = Math.min(CHUNK, budget - read);
|
|
18510
|
+
const buffer = Buffer.allocUnsafe(span);
|
|
18511
|
+
const position = size - read - span;
|
|
18512
|
+
readSync(fd, buffer, 0, span, position);
|
|
18513
|
+
chunks.unshift(buffer);
|
|
18514
|
+
read += span;
|
|
18515
|
+
for (const byte of buffer)
|
|
18516
|
+
if (byte === 10)
|
|
18517
|
+
newlines++;
|
|
18518
|
+
}
|
|
18519
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
18520
|
+
} catch {
|
|
18521
|
+
return null;
|
|
18522
|
+
} finally {
|
|
18523
|
+
closeSync(fd);
|
|
18524
|
+
}
|
|
18525
|
+
}
|
|
18526
|
+
function fileExists(path) {
|
|
18527
|
+
try {
|
|
18528
|
+
return statSync(path).isFile();
|
|
18529
|
+
} catch {
|
|
18530
|
+
return false;
|
|
18531
|
+
}
|
|
18532
|
+
}
|
|
18244
18533
|
// packages/control/src/usage.ts
|
|
18245
18534
|
var MAX_LOG_LIMIT = 500;
|
|
18246
18535
|
var DEFAULT_LOG_LIMIT = 100;
|
|
@@ -18475,6 +18764,278 @@ async function pollUntilAuthorized(flows, start, deps) {
|
|
|
18475
18764
|
}
|
|
18476
18765
|
}
|
|
18477
18766
|
|
|
18767
|
+
// apps/cli/src/service.ts
|
|
18768
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
|
|
18769
|
+
import { homedir as homedir2 } from "os";
|
|
18770
|
+
import { dirname, join as join2 } from "path";
|
|
18771
|
+
var UNIT_NAME2 = "omnigateway.service";
|
|
18772
|
+
function unitPath(scope) {
|
|
18773
|
+
return scope === "system" ? join2("/etc/systemd/system", UNIT_NAME2) : join2(homedir2(), ".config", "systemd", "user", UNIT_NAME2);
|
|
18774
|
+
}
|
|
18775
|
+
function systemctlArgs(scope, args) {
|
|
18776
|
+
return scope === "system" ? ["systemctl", ...args] : ["systemctl", "--user", ...args];
|
|
18777
|
+
}
|
|
18778
|
+
function pidFile(stateDir) {
|
|
18779
|
+
return join2(stateDir, "gateway.pid");
|
|
18780
|
+
}
|
|
18781
|
+
function logFile(stateDir) {
|
|
18782
|
+
return join2(stateDir, "gateway.log");
|
|
18783
|
+
}
|
|
18784
|
+
function supervisedLogFile(deps) {
|
|
18785
|
+
const configured = deps.logFile?.trim();
|
|
18786
|
+
return configured !== undefined && configured.length > 0 ? configured : logFile(deps.stateDir);
|
|
18787
|
+
}
|
|
18788
|
+
function defaultStateDir(env2) {
|
|
18789
|
+
const xdg = env2.XDG_STATE_HOME;
|
|
18790
|
+
const base = typeof xdg === "string" && xdg.length > 0 ? xdg : join2(homedir2(), ".local", "state");
|
|
18791
|
+
return join2(base, "omnigateway");
|
|
18792
|
+
}
|
|
18793
|
+
function unitFile(input) {
|
|
18794
|
+
return `[Unit]
|
|
18795
|
+
Description=${input.description ?? "OmniGateway"}
|
|
18796
|
+
After=network-online.target
|
|
18797
|
+
Wants=network-online.target
|
|
18798
|
+
|
|
18799
|
+
[Service]
|
|
18800
|
+
Type=simple
|
|
18801
|
+
WorkingDirectory=${input.root}
|
|
18802
|
+
EnvironmentFile=${join2(input.root, ".env")}
|
|
18803
|
+
ExecStart=${input.bun} ${input.entrypoint}
|
|
18804
|
+
Restart=on-failure
|
|
18805
|
+
RestartSec=2
|
|
18806
|
+
|
|
18807
|
+
[Install]
|
|
18808
|
+
WantedBy=default.target
|
|
18809
|
+
`;
|
|
18810
|
+
}
|
|
18811
|
+
function unitInstalled(deps) {
|
|
18812
|
+
return existsSync2(deps.unitPath);
|
|
18813
|
+
}
|
|
18814
|
+
function readPid(stateDir) {
|
|
18815
|
+
const file2 = pidFile(stateDir);
|
|
18816
|
+
if (!existsSync2(file2))
|
|
18817
|
+
return null;
|
|
18818
|
+
const pid = Number(readFileSync2(file2, "utf8").trim());
|
|
18819
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
18820
|
+
}
|
|
18821
|
+
function livePid(deps) {
|
|
18822
|
+
const pid = readPid(deps.stateDir);
|
|
18823
|
+
if (pid === null)
|
|
18824
|
+
return null;
|
|
18825
|
+
if (deps.alive(pid))
|
|
18826
|
+
return pid;
|
|
18827
|
+
rmSync(pidFile(deps.stateDir), { force: true });
|
|
18828
|
+
return null;
|
|
18829
|
+
}
|
|
18830
|
+
async function status(deps) {
|
|
18831
|
+
if (unitInstalled(deps)) {
|
|
18832
|
+
const active = await deps.run(systemctlArgs(deps.scope, ["is-active", UNIT_NAME2]));
|
|
18833
|
+
const state = active.stdout.trim() || active.stderr.trim() || "unknown";
|
|
18834
|
+
const shown = await deps.run(systemctlArgs(deps.scope, ["show", UNIT_NAME2, "--property=MainPID", "--value"]));
|
|
18835
|
+
const pid2 = Number(shown.stdout.trim());
|
|
18836
|
+
return {
|
|
18837
|
+
supervisor: "systemd",
|
|
18838
|
+
running: state === "active",
|
|
18839
|
+
pid: Number.isInteger(pid2) && pid2 > 0 ? pid2 : null,
|
|
18840
|
+
state,
|
|
18841
|
+
unitPath: deps.unitPath,
|
|
18842
|
+
logFile: null
|
|
18843
|
+
};
|
|
18844
|
+
}
|
|
18845
|
+
const pid = livePid(deps);
|
|
18846
|
+
return {
|
|
18847
|
+
supervisor: pid === null ? "none" : "pidfile",
|
|
18848
|
+
running: pid !== null,
|
|
18849
|
+
pid,
|
|
18850
|
+
state: null,
|
|
18851
|
+
unitPath: null,
|
|
18852
|
+
logFile: supervisedLogFile(deps)
|
|
18853
|
+
};
|
|
18854
|
+
}
|
|
18855
|
+
async function start(deps, input) {
|
|
18856
|
+
if (unitInstalled(deps)) {
|
|
18857
|
+
const result = await deps.run(systemctlArgs(deps.scope, ["start", UNIT_NAME2]));
|
|
18858
|
+
if (result.code !== 0) {
|
|
18859
|
+
throw new Error(result.stderr.trim() || `systemctl start exited ${result.code}`);
|
|
18860
|
+
}
|
|
18861
|
+
return {
|
|
18862
|
+
supervisor: "systemd",
|
|
18863
|
+
pid: null,
|
|
18864
|
+
healthy: await waitForHealth(deps, input.baseUrl)
|
|
18865
|
+
};
|
|
18866
|
+
}
|
|
18867
|
+
const running = livePid(deps);
|
|
18868
|
+
if (running !== null) {
|
|
18869
|
+
return {
|
|
18870
|
+
supervisor: "pidfile",
|
|
18871
|
+
pid: running,
|
|
18872
|
+
healthy: await waitForHealth(deps, input.baseUrl)
|
|
18873
|
+
};
|
|
18874
|
+
}
|
|
18875
|
+
mkdirSync(deps.stateDir, { recursive: true });
|
|
18876
|
+
const file2 = supervisedLogFile(deps);
|
|
18877
|
+
mkdirSync(dirname(file2), { recursive: true });
|
|
18878
|
+
const pid = deps.spawn({
|
|
18879
|
+
argv: input.argv,
|
|
18880
|
+
cwd: deps.root,
|
|
18881
|
+
env: { ...input.env, OMNI_LOG_FILE: file2 },
|
|
18882
|
+
logFile: file2
|
|
18883
|
+
});
|
|
18884
|
+
writeFileSync(pidFile(deps.stateDir), `${pid}
|
|
18885
|
+
`);
|
|
18886
|
+
return { supervisor: "pidfile", pid, healthy: await waitForHealth(deps, input.baseUrl) };
|
|
18887
|
+
}
|
|
18888
|
+
var HEALTH_TIMEOUT_MS = 1e4;
|
|
18889
|
+
var HEALTH_INTERVAL_MS = 200;
|
|
18890
|
+
async function waitForHealth(deps, baseUrl) {
|
|
18891
|
+
const attempts = Math.ceil(HEALTH_TIMEOUT_MS / HEALTH_INTERVAL_MS);
|
|
18892
|
+
for (let attempt = 0;attempt < attempts; attempt++) {
|
|
18893
|
+
if (await deps.probe(baseUrl))
|
|
18894
|
+
return true;
|
|
18895
|
+
await deps.sleep(HEALTH_INTERVAL_MS);
|
|
18896
|
+
}
|
|
18897
|
+
return false;
|
|
18898
|
+
}
|
|
18899
|
+
var STOP_GRACE_MS = 5000;
|
|
18900
|
+
var STOP_INTERVAL_MS = 100;
|
|
18901
|
+
async function stop(deps) {
|
|
18902
|
+
if (unitInstalled(deps)) {
|
|
18903
|
+
const result = await deps.run(systemctlArgs(deps.scope, ["stop", UNIT_NAME2]));
|
|
18904
|
+
if (result.code !== 0) {
|
|
18905
|
+
throw new Error(result.stderr.trim() || `systemctl stop exited ${result.code}`);
|
|
18906
|
+
}
|
|
18907
|
+
return { supervisor: "systemd", stopped: true };
|
|
18908
|
+
}
|
|
18909
|
+
const pid = livePid(deps);
|
|
18910
|
+
if (pid === null)
|
|
18911
|
+
return { supervisor: "none", stopped: false };
|
|
18912
|
+
deps.kill(pid, "SIGTERM");
|
|
18913
|
+
const attempts = Math.ceil(STOP_GRACE_MS / STOP_INTERVAL_MS);
|
|
18914
|
+
for (let attempt = 0;deps.alive(pid); attempt++) {
|
|
18915
|
+
if (attempt >= attempts) {
|
|
18916
|
+
deps.kill(pid, "SIGKILL");
|
|
18917
|
+
break;
|
|
18918
|
+
}
|
|
18919
|
+
await deps.sleep(STOP_INTERVAL_MS);
|
|
18920
|
+
}
|
|
18921
|
+
rmSync(pidFile(deps.stateDir), { force: true });
|
|
18922
|
+
return { supervisor: "pidfile", stopped: true };
|
|
18923
|
+
}
|
|
18924
|
+
async function install(deps, input) {
|
|
18925
|
+
const path = deps.unitPath;
|
|
18926
|
+
if (existsSync2(path) && !input.force) {
|
|
18927
|
+
throw new Error(`${path} already exists; pass --force to replace it`);
|
|
18928
|
+
}
|
|
18929
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
18930
|
+
writeFileSync(path, unitFile({ root: deps.root, bun: input.bun, entrypoint: input.entrypoint }));
|
|
18931
|
+
const reload = await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
|
|
18932
|
+
let enabled = false;
|
|
18933
|
+
if (input.enable) {
|
|
18934
|
+
const result = await deps.run(systemctlArgs(deps.scope, ["enable", UNIT_NAME2]));
|
|
18935
|
+
enabled = result.code === 0;
|
|
18936
|
+
}
|
|
18937
|
+
return { path, reloaded: reload.code === 0, enabled };
|
|
18938
|
+
}
|
|
18939
|
+
async function uninstall(deps) {
|
|
18940
|
+
const path = deps.unitPath;
|
|
18941
|
+
if (!existsSync2(path))
|
|
18942
|
+
return { path, removed: false };
|
|
18943
|
+
await deps.run(systemctlArgs(deps.scope, ["disable", "--now", UNIT_NAME2]));
|
|
18944
|
+
rmSync(path, { force: true });
|
|
18945
|
+
await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
|
|
18946
|
+
return { path, removed: true };
|
|
18947
|
+
}
|
|
18948
|
+
function consoleSource(deps) {
|
|
18949
|
+
const configured = deps.logFile?.trim();
|
|
18950
|
+
const chosen = configured !== undefined && configured.length > 0 && fileExists(configured) ? configured : unitInstalled(deps) ? undefined : fileExists(supervisedLogFile(deps)) ? supervisedLogFile(deps) : undefined;
|
|
18951
|
+
return {
|
|
18952
|
+
source: resolveConsoleSource({
|
|
18953
|
+
...chosen === undefined ? {} : { logFile: chosen },
|
|
18954
|
+
unitInstalled: unitInstalled(deps),
|
|
18955
|
+
scope: deps.scope
|
|
18956
|
+
}),
|
|
18957
|
+
deps: {
|
|
18958
|
+
readFile: (path, lines) => tailFile(path, lines),
|
|
18959
|
+
run: deps.run
|
|
18960
|
+
}
|
|
18961
|
+
};
|
|
18962
|
+
}
|
|
18963
|
+
async function serviceLogs(deps, lines) {
|
|
18964
|
+
const { source, deps: readDeps } = consoleSource(deps);
|
|
18965
|
+
const read = await readConsole(readDeps, source, { lines });
|
|
18966
|
+
return read.lines.map((line) => line.raw).join(`
|
|
18967
|
+
`);
|
|
18968
|
+
}
|
|
18969
|
+
|
|
18970
|
+
// apps/cli/src/commands/console.ts
|
|
18971
|
+
var FOLLOW_INTERVAL_MS = 2000;
|
|
18972
|
+
var LEVEL_TONE = {
|
|
18973
|
+
debug: "dim",
|
|
18974
|
+
warn: "yellow",
|
|
18975
|
+
error: "red"
|
|
18976
|
+
};
|
|
18977
|
+
function render(ctx, lines) {
|
|
18978
|
+
return lines.map((line) => {
|
|
18979
|
+
const tone = line.level === null ? undefined : LEVEL_TONE[line.level];
|
|
18980
|
+
return tone === undefined ? line.raw : paint(ctx, tone, line.raw);
|
|
18981
|
+
}).join(`
|
|
18982
|
+
`);
|
|
18983
|
+
}
|
|
18984
|
+
function sourceHint(source) {
|
|
18985
|
+
if (source.kind === "file")
|
|
18986
|
+
return `${source.path} (named by OMNI_LOG_FILE)`;
|
|
18987
|
+
if (source.kind === "journal")
|
|
18988
|
+
return `systemd journal for ${source.unit}`;
|
|
18989
|
+
return "not captured \u2014 run under systemd, or start it with `omni start`";
|
|
18990
|
+
}
|
|
18991
|
+
function empty(source) {
|
|
18992
|
+
if (source.kind !== "none")
|
|
18993
|
+
return `no output yet in ${sourceHint(source)}`;
|
|
18994
|
+
return [
|
|
18995
|
+
"this gateway's output is not being captured, so there is nothing to show.",
|
|
18996
|
+
"run it in the foreground and its output goes to your terminal instead.",
|
|
18997
|
+
"to capture it: `omni service install`, or `omni start`, which redirects",
|
|
18998
|
+
"output to a file and points OMNI_LOG_FILE at it."
|
|
18999
|
+
].join(`
|
|
19000
|
+
`);
|
|
19001
|
+
}
|
|
19002
|
+
var console_ = {
|
|
19003
|
+
usage: "console [-n N] [--follow] [--level debug|info|warn|error]",
|
|
19004
|
+
summary: "Show the gateway process's own output",
|
|
19005
|
+
options: {
|
|
19006
|
+
number: { type: "string", short: "n" },
|
|
19007
|
+
follow: { type: "boolean" },
|
|
19008
|
+
level: { type: "string" },
|
|
19009
|
+
system: { type: "boolean" }
|
|
19010
|
+
},
|
|
19011
|
+
async run(args, { ctx, writer, service }) {
|
|
19012
|
+
const lines = numberFlag(args.values, "number") ?? 50;
|
|
19013
|
+
const rawLevel = stringFlag(args.values, "level");
|
|
19014
|
+
const level = parseLogLevel(rawLevel);
|
|
19015
|
+
if (rawLevel !== undefined && level === null) {
|
|
19016
|
+
throw new UsageError(`unknown level "${rawLevel}"; use debug, info, warn or error`);
|
|
19017
|
+
}
|
|
19018
|
+
const { source, deps } = consoleSource(service());
|
|
19019
|
+
const query = { lines, ...level === null ? {} : { level } };
|
|
19020
|
+
const first = await readConsole(deps, source, query);
|
|
19021
|
+
emit(ctx, writer, first, () => first.lines.length === 0 ? empty(source) : render(ctx, first.lines));
|
|
19022
|
+
if (!ctx.json && first.lines.length > 0) {
|
|
19023
|
+
note(ctx, writer, paint(ctx, "dim", `reading ${sourceHint(source)}`));
|
|
19024
|
+
}
|
|
19025
|
+
if (boolFlag(args.values, "follow") !== true || ctx.json)
|
|
19026
|
+
return;
|
|
19027
|
+
let seen = first.lines.at(-1)?.at ?? 0;
|
|
19028
|
+
for (;; ) {
|
|
19029
|
+
await Bun.sleep(FOLLOW_INTERVAL_MS);
|
|
19030
|
+
const next = await readConsole(deps, source, { ...query, since: seen });
|
|
19031
|
+
if (next.lines.length === 0)
|
|
19032
|
+
continue;
|
|
19033
|
+
seen = next.lines.at(-1)?.at ?? seen;
|
|
19034
|
+
writer.out(render(ctx, next.lines));
|
|
19035
|
+
}
|
|
19036
|
+
}
|
|
19037
|
+
};
|
|
19038
|
+
|
|
18478
19039
|
// apps/cli/src/command.ts
|
|
18479
19040
|
var PROVIDER_TONE = {
|
|
18480
19041
|
anthropic: "magenta",
|
|
@@ -18515,7 +19076,7 @@ var credentialsList = {
|
|
|
18515
19076
|
{ header: "STATE" },
|
|
18516
19077
|
{ header: "EXPIRES" }
|
|
18517
19078
|
], credentials.map((c) => {
|
|
18518
|
-
const
|
|
19079
|
+
const status2 = condition(c);
|
|
18519
19080
|
return [
|
|
18520
19081
|
c.id,
|
|
18521
19082
|
provider(ctx, c.provider),
|
|
@@ -18523,7 +19084,7 @@ var credentialsList = {
|
|
|
18523
19084
|
c.authType,
|
|
18524
19085
|
String(c.tier),
|
|
18525
19086
|
String(c.weight),
|
|
18526
|
-
state(ctx,
|
|
19087
|
+
state(ctx, status2.ok, status2.text),
|
|
18527
19088
|
formatTime(c.expiresAt)
|
|
18528
19089
|
];
|
|
18529
19090
|
}));
|
|
@@ -18539,7 +19100,7 @@ var credentialsShow = {
|
|
|
18539
19100
|
async run(args, { ctx, writer }) {
|
|
18540
19101
|
const id = requirePositional(args, 0, "credential id");
|
|
18541
19102
|
const credential = await findCredential(ctx, id);
|
|
18542
|
-
const
|
|
19103
|
+
const status2 = condition(credential);
|
|
18543
19104
|
const data = {
|
|
18544
19105
|
id: credential.id,
|
|
18545
19106
|
provider: credential.provider,
|
|
@@ -18562,7 +19123,7 @@ var credentialsShow = {
|
|
|
18562
19123
|
["provider", provider(ctx, credential.provider)],
|
|
18563
19124
|
["label", credential.label],
|
|
18564
19125
|
["auth", credential.authType],
|
|
18565
|
-
["state", state(ctx,
|
|
19126
|
+
["state", state(ctx, status2.ok, status2.text)],
|
|
18566
19127
|
["tier", String(credential.tier)],
|
|
18567
19128
|
["weight", String(credential.weight)],
|
|
18568
19129
|
["account", credential.accountEmail ?? "\u2014"],
|
|
@@ -18697,12 +19258,12 @@ var credentialsHealth = {
|
|
|
18697
19258
|
};
|
|
18698
19259
|
|
|
18699
19260
|
// apps/cli/src/commands/db.ts
|
|
18700
|
-
import { existsSync as
|
|
19261
|
+
import { existsSync as existsSync3 } from "fs";
|
|
18701
19262
|
var dbMigrate = {
|
|
18702
19263
|
usage: "db migrate",
|
|
18703
19264
|
summary: "Create or upgrade the database schema",
|
|
18704
19265
|
async run(_args, { ctx, writer }) {
|
|
18705
|
-
const existed =
|
|
19266
|
+
const existed = existsSync3(ctx.databasePath);
|
|
18706
19267
|
const store = await ctx.store();
|
|
18707
19268
|
await store.config.getSettings();
|
|
18708
19269
|
emit(ctx, writer, { path: ctx.databasePath, created: !existed }, () => fields([
|
|
@@ -18840,8 +19401,8 @@ var modelsShow = {
|
|
|
18840
19401
|
price(target.costPerMTok.cacheRead),
|
|
18841
19402
|
price(target.costPerMTok.cacheWrite5m),
|
|
18842
19403
|
price(target.costPerMTok.cacheWrite1h),
|
|
18843
|
-
|
|
18844
|
-
|
|
19404
|
+
tokens2(target.contextWindow),
|
|
19405
|
+
tokens2(target.maxOutputTokens),
|
|
18845
19406
|
capabilityList(target)
|
|
18846
19407
|
]))
|
|
18847
19408
|
].join(`
|
|
@@ -18851,7 +19412,7 @@ var modelsShow = {
|
|
|
18851
19412
|
function price(value) {
|
|
18852
19413
|
return value === undefined ? "\u2014" : value.toFixed(2);
|
|
18853
19414
|
}
|
|
18854
|
-
function
|
|
19415
|
+
function tokens2(value) {
|
|
18855
19416
|
return value === undefined ? "\u2014" : value.toLocaleString("en-US");
|
|
18856
19417
|
}
|
|
18857
19418
|
function capabilityList(target) {
|
|
@@ -18919,9 +19480,9 @@ var modelsCatalog = {
|
|
|
18919
19480
|
price(listed?.cacheRead),
|
|
18920
19481
|
price(listed?.cacheWrite5m),
|
|
18921
19482
|
price(listed?.cacheWrite1h),
|
|
18922
|
-
|
|
18923
|
-
oauth?.contextWindow === limits?.contextWindow ? "\u2014" :
|
|
18924
|
-
|
|
19483
|
+
tokens2(limits?.contextWindow),
|
|
19484
|
+
oauth?.contextWindow === limits?.contextWindow ? "\u2014" : tokens2(oauth?.contextWindow),
|
|
19485
|
+
tokens2(limits?.maxOutputTokens)
|
|
18925
19486
|
];
|
|
18926
19487
|
})));
|
|
18927
19488
|
}
|
|
@@ -19030,207 +19591,8 @@ ${candidates}${excluded}`;
|
|
|
19030
19591
|
import { existsSync as existsSync5 } from "fs";
|
|
19031
19592
|
|
|
19032
19593
|
// apps/cli/src/runtime.ts
|
|
19033
|
-
import { closeSync, existsSync as existsSync4, openSync } from "fs";
|
|
19594
|
+
import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2 } from "fs";
|
|
19034
19595
|
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
19596
|
var runCommand = async (argv) => {
|
|
19235
19597
|
const proc = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe" });
|
|
19236
19598
|
const [stdout, stderr, code] = await Promise.all([
|
|
@@ -19241,7 +19603,7 @@ var runCommand = async (argv) => {
|
|
|
19241
19603
|
return { code, stdout, stderr };
|
|
19242
19604
|
};
|
|
19243
19605
|
var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
|
|
19244
|
-
const fd =
|
|
19606
|
+
const fd = openSync2(logFile2, "a");
|
|
19245
19607
|
try {
|
|
19246
19608
|
const proc = Bun.spawn([...argv], {
|
|
19247
19609
|
cwd,
|
|
@@ -19253,7 +19615,7 @@ var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
|
|
|
19253
19615
|
proc.unref();
|
|
19254
19616
|
return proc.pid;
|
|
19255
19617
|
} finally {
|
|
19256
|
-
|
|
19618
|
+
closeSync2(fd);
|
|
19257
19619
|
}
|
|
19258
19620
|
};
|
|
19259
19621
|
async function runForeground(input) {
|
|
@@ -19294,11 +19656,13 @@ function gatewayEntrypoint(root, cliDir = import.meta.dir) {
|
|
|
19294
19656
|
return candidates.find((path) => existsSync4(path)) ?? null;
|
|
19295
19657
|
}
|
|
19296
19658
|
function createServiceDeps(input) {
|
|
19659
|
+
const logFile2 = input.env.OMNI_LOG_FILE?.trim();
|
|
19297
19660
|
return {
|
|
19298
19661
|
root: input.root,
|
|
19299
19662
|
stateDir: defaultStateDir(input.env),
|
|
19300
19663
|
scope: input.scope,
|
|
19301
19664
|
unitPath: unitPath(input.scope),
|
|
19665
|
+
...logFile2 === undefined || logFile2.length === 0 ? {} : { logFile: logFile2 },
|
|
19302
19666
|
run: runCommand,
|
|
19303
19667
|
spawn: spawnDetached,
|
|
19304
19668
|
probe: probeHealth,
|
|
@@ -19341,7 +19705,7 @@ var start2 = {
|
|
|
19341
19705
|
emit(ctx, writer, result, () => {
|
|
19342
19706
|
const how = result.supervisor === "systemd" ? "systemd" : `pid ${result.pid}`;
|
|
19343
19707
|
if (!result.healthy) {
|
|
19344
|
-
const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" :
|
|
19708
|
+
const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" : supervisedLogFile(deps);
|
|
19345
19709
|
return `${state(ctx, false, "started but /health never answered")} (${how}); see ${where}`;
|
|
19346
19710
|
}
|
|
19347
19711
|
return `${state(ctx, true, "running")} (${how}) at ${baseUrl}`;
|
|
@@ -19434,7 +19798,8 @@ var doctor = {
|
|
|
19434
19798
|
gatewayEntrypoint: gatewayEntrypoint(deps.root),
|
|
19435
19799
|
unitInstalled: unit,
|
|
19436
19800
|
supervisor: status2.supervisor,
|
|
19437
|
-
running: status2.running
|
|
19801
|
+
running: status2.running,
|
|
19802
|
+
consoleSource: consoleSource(deps).source
|
|
19438
19803
|
};
|
|
19439
19804
|
emit(ctx, writer, checks3, () => {
|
|
19440
19805
|
const ok = (value, text) => state(ctx, value, text);
|
|
@@ -19449,7 +19814,8 @@ var doctor = {
|
|
|
19449
19814
|
["config", checks3.configError === null ? ok(true, "ok") : ok(false, checks3.configError)],
|
|
19450
19815
|
["entrypoint", checks3.gatewayEntrypoint ?? ok(false, "not found")],
|
|
19451
19816
|
["systemd unit", unit ? deps.scope : paint(ctx, "dim", "none")],
|
|
19452
|
-
["gateway", ok(status2.running, status2.running ? "running" : "stopped")]
|
|
19817
|
+
["gateway", ok(status2.running, status2.running ? "running" : "stopped")],
|
|
19818
|
+
["console log", sourceHint(checks3.consoleSource)]
|
|
19453
19819
|
]);
|
|
19454
19820
|
});
|
|
19455
19821
|
}
|
|
@@ -19500,6 +19866,78 @@ var settingsSet = {
|
|
|
19500
19866
|
}
|
|
19501
19867
|
};
|
|
19502
19868
|
|
|
19869
|
+
// apps/cli/src/commands/setup.ts
|
|
19870
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
19871
|
+
var OPTIONS = {
|
|
19872
|
+
dir: { type: "string" },
|
|
19873
|
+
key: { type: "string" },
|
|
19874
|
+
"dry-run": { type: "boolean" }
|
|
19875
|
+
};
|
|
19876
|
+
async function described(ctx) {
|
|
19877
|
+
const models = await describeModelsForSetup(await ctx.store());
|
|
19878
|
+
if (models.length === 0) {
|
|
19879
|
+
throw new CliError("no virtual models configured; add one with `omni models put`");
|
|
19880
|
+
}
|
|
19881
|
+
return models;
|
|
19882
|
+
}
|
|
19883
|
+
function baseUrl(ctx) {
|
|
19884
|
+
if (ctx.configError !== null)
|
|
19885
|
+
throw new CliError(ctx.configError);
|
|
19886
|
+
return ctx.config().baseUrl;
|
|
19887
|
+
}
|
|
19888
|
+
function finish(ctx, writer, files, dryRun2, key, write) {
|
|
19889
|
+
if (!dryRun2) {
|
|
19890
|
+
for (const file2 of files)
|
|
19891
|
+
write(file2.path, file2.contents);
|
|
19892
|
+
}
|
|
19893
|
+
emit(ctx, writer, { files }, () => dryRun2 ? files.map((f) => `--- ${f.path}
|
|
19894
|
+
${f.contents}`).join(`
|
|
19895
|
+
`) : files.map((f) => f.path).join(`
|
|
19896
|
+
`));
|
|
19897
|
+
if (key === undefined) {
|
|
19898
|
+
note(ctx, writer, "no --key given: the files carry a placeholder, so paste your key over it");
|
|
19899
|
+
}
|
|
19900
|
+
}
|
|
19901
|
+
function at(dir, file2) {
|
|
19902
|
+
return { path: join4(dir, file2.path), contents: file2.contents };
|
|
19903
|
+
}
|
|
19904
|
+
var setupClaude = {
|
|
19905
|
+
usage: "setup claude [--dir <path>] [--key <key>] [--dry-run]",
|
|
19906
|
+
summary: "Write a Claude Code profile per virtual model",
|
|
19907
|
+
options: OPTIONS,
|
|
19908
|
+
async run(args, { ctx, writer, setupFs }) {
|
|
19909
|
+
const models = await described(ctx);
|
|
19910
|
+
const dir = stringFlag(args.values, "dir") ?? join4(setupFs.homeDir, ".claude", "profiles");
|
|
19911
|
+
const key = stringFlag(args.values, "key");
|
|
19912
|
+
const dryRun2 = boolFlag(args.values, "dry-run");
|
|
19913
|
+
const files = claudeProfiles(models, {
|
|
19914
|
+
baseUrl: baseUrl(ctx),
|
|
19915
|
+
discoveryMirrors: ctx.config().exposeClaudeCodeAliases,
|
|
19916
|
+
...key === undefined ? {} : { apiKey: key }
|
|
19917
|
+
}).map((file2) => at(dir, file2));
|
|
19918
|
+
finish(ctx, writer, files, dryRun2, key, setupFs.write);
|
|
19919
|
+
if (!dryRun2) {
|
|
19920
|
+
note(ctx, writer, `run one with: CLAUDE_CONFIG_DIR=${dirname2(files[0]?.path ?? dir)} claude`);
|
|
19921
|
+
}
|
|
19922
|
+
}
|
|
19923
|
+
};
|
|
19924
|
+
var setupOpencode = {
|
|
19925
|
+
usage: "setup opencode [--dir <path>] [--key <key>] [--dry-run]",
|
|
19926
|
+
summary: "Write an opencode.json provider entry for this gateway",
|
|
19927
|
+
options: OPTIONS,
|
|
19928
|
+
async run(args, { ctx, writer, setupFs }) {
|
|
19929
|
+
const models = await described(ctx);
|
|
19930
|
+
const dir = stringFlag(args.values, "dir") ?? setupFs.cwd;
|
|
19931
|
+
const key = stringFlag(args.values, "key");
|
|
19932
|
+
const dryRun2 = boolFlag(args.values, "dry-run");
|
|
19933
|
+
const file2 = opencodeConfig(models, {
|
|
19934
|
+
baseUrl: baseUrl(ctx),
|
|
19935
|
+
...key === undefined ? {} : { apiKey: key }
|
|
19936
|
+
});
|
|
19937
|
+
finish(ctx, writer, [at(dir, file2)], dryRun2, key, setupFs.write);
|
|
19938
|
+
}
|
|
19939
|
+
};
|
|
19940
|
+
|
|
19503
19941
|
// apps/cli/src/commands/status.ts
|
|
19504
19942
|
var WINDOW_ORDER = {
|
|
19505
19943
|
fiveHour: 0,
|
|
@@ -19653,11 +20091,12 @@ var usage = {
|
|
|
19653
20091
|
};
|
|
19654
20092
|
var logs = {
|
|
19655
20093
|
usage: "logs [-n N] [--follow] [--service]",
|
|
19656
|
-
summary: "Show recent requests
|
|
20094
|
+
summary: "Show recent requests (--service is an alias for `omni console`)",
|
|
19657
20095
|
options: {
|
|
19658
20096
|
number: { type: "string", short: "n" },
|
|
19659
20097
|
follow: { type: "boolean" },
|
|
19660
|
-
service: { type: "boolean" }
|
|
20098
|
+
service: { type: "boolean" },
|
|
20099
|
+
system: { type: "boolean" }
|
|
19661
20100
|
},
|
|
19662
20101
|
async run(args, { ctx, writer, service }) {
|
|
19663
20102
|
const limit = numberFlag(args.values, "number") ?? 20;
|
|
@@ -19667,7 +20106,7 @@ var logs = {
|
|
|
19667
20106
|
return;
|
|
19668
20107
|
}
|
|
19669
20108
|
const store = await ctx.store();
|
|
19670
|
-
const
|
|
20109
|
+
const render2 = (rows) => table([
|
|
19671
20110
|
{ header: "AT" },
|
|
19672
20111
|
{ header: "MODEL" },
|
|
19673
20112
|
{ header: "RESOLVED" },
|
|
@@ -19685,7 +20124,7 @@ var logs = {
|
|
|
19685
20124
|
String(row.durationMs)
|
|
19686
20125
|
]));
|
|
19687
20126
|
const first = await recentLogs(store, limit);
|
|
19688
|
-
emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" :
|
|
20127
|
+
emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" : render2(first));
|
|
19689
20128
|
if (args.values.follow !== true || ctx.json)
|
|
19690
20129
|
return;
|
|
19691
20130
|
let seen = first[0]?.at ?? 0;
|
|
@@ -19695,7 +20134,7 @@ var logs = {
|
|
|
19695
20134
|
if (next.length === 0)
|
|
19696
20135
|
continue;
|
|
19697
20136
|
seen = next[0]?.at ?? seen;
|
|
19698
|
-
writer.out(
|
|
20137
|
+
writer.out(render2(next.reverse()));
|
|
19699
20138
|
}
|
|
19700
20139
|
}
|
|
19701
20140
|
};
|
|
@@ -19708,6 +20147,7 @@ var COMMANDS = {
|
|
|
19708
20147
|
restart,
|
|
19709
20148
|
doctor,
|
|
19710
20149
|
logs,
|
|
20150
|
+
console: console_,
|
|
19711
20151
|
usage,
|
|
19712
20152
|
connect,
|
|
19713
20153
|
"service install": serviceInstall,
|
|
@@ -19732,6 +20172,8 @@ var COMMANDS = {
|
|
|
19732
20172
|
"keys revoke": keysRevoke,
|
|
19733
20173
|
"settings get": settingsGet,
|
|
19734
20174
|
"settings set": settingsSet,
|
|
20175
|
+
"setup claude": setupClaude,
|
|
20176
|
+
"setup opencode": setupOpencode,
|
|
19735
20177
|
"admin set-password": adminSetPassword,
|
|
19736
20178
|
"db migrate": dbMigrate
|
|
19737
20179
|
};
|
|
@@ -19770,7 +20212,10 @@ function resolveCommand(argv) {
|
|
|
19770
20212
|
|
|
19771
20213
|
// apps/cli/src/help.ts
|
|
19772
20214
|
var GROUPS = [
|
|
19773
|
-
{
|
|
20215
|
+
{
|
|
20216
|
+
title: "Gateway",
|
|
20217
|
+
prefixes: ["status", "start", "stop", "restart", "doctor", "logs", "console"]
|
|
20218
|
+
},
|
|
19774
20219
|
{ title: "Service", prefixes: ["service "] },
|
|
19775
20220
|
{ title: "Accounts", prefixes: ["connect", "credentials "] },
|
|
19776
20221
|
{ title: "Models", prefixes: ["models "] },
|
|
@@ -19929,6 +20374,14 @@ async function run(argv, writer, options = {}) {
|
|
|
19929
20374
|
ctx,
|
|
19930
20375
|
writer,
|
|
19931
20376
|
prompt,
|
|
20377
|
+
setupFs: options.setupFs ?? {
|
|
20378
|
+
homeDir: homedir3(),
|
|
20379
|
+
cwd: process.cwd(),
|
|
20380
|
+
write: (path, contents) => {
|
|
20381
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
20382
|
+
writeFileSync2(path, contents);
|
|
20383
|
+
}
|
|
20384
|
+
},
|
|
19932
20385
|
service: () => options.service?.({ root: ctx.root.root, env: ctx.env }) ?? createServiceDeps({ root: ctx.root.root, env: ctx.env, scope, now: ctx.now }),
|
|
19933
20386
|
foreground: options.foreground ?? runForeground,
|
|
19934
20387
|
connect: (store) => options.connect?.(store) ?? createConnectFlows({
|