omnigateway 0.1.5 → 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.
- package/README.md +55 -2
- package/bin/omni.js +709 -325
- package/gateway.js +1028 -264
- package/package.json +1 -1
- package/public/assets/{Chip-Ccqsb_Mq.js → Chip-C69u2U_b.js} +2 -2
- package/public/assets/{CopyValue-CvEMy9Zo.js → CopyValue-CJy2T0H6.js} +1 -1
- package/public/assets/{Field-Devm4K46.js → Field-Sc8mDqJv.js} +1 -1
- package/public/assets/{Lamp-BXSUIAtG.js → Lamp-7jD_9929.js} +2 -2
- package/public/assets/{Meter-Fn8X7t60.js → Meter-C0nUftAa.js} +2 -2
- package/public/assets/{Modal-C4y9zALa.js → Modal-CiVHSJmm.js} +2 -2
- package/public/assets/Rack-CMqGBP4I.js +147 -0
- package/public/assets/{Readout-BKpOZIGX.js → Readout-Dv2dZWtI.js} +4 -4
- package/public/assets/{States-DBzsiHjQ.js → States-D55MhqMx.js} +10 -10
- package/public/assets/{Table-DPDJeeVc.js → Table-DBB3jdgi.js} +1 -1
- package/public/assets/{Toggle-CBq5XAMU.js → Toggle-BoGjb5oi.js} +3 -3
- package/public/assets/_app-p5Mz7sXD.js +1 -0
- package/public/assets/_app.accounts-B5wMO9TH.js +51 -0
- package/public/assets/_app.console-Bka7AkiL.js +25 -0
- package/public/assets/_app.index-MnQuadXb.js +61 -0
- package/public/assets/_app.keys-BX8k8COP.js +39 -0
- package/public/assets/_app.logs-DD-sd4fn.js +33 -0
- package/public/assets/_app.models-CezFNL6y.js +144 -0
- package/public/assets/{_app.settings-Du5Nsp9f.js → _app.settings-D1taAt8B.js} +3 -3
- package/public/assets/{_app.usage-DKq4Hsia.js → _app.usage-BRhEvwVr.js} +28 -28
- package/public/assets/catalog-C_OQ0icG.js +1 -0
- package/public/assets/index-vCuK1fRQ.js +170 -0
- package/public/assets/{login-Gyf-1fSa.js → login-DaC-wVpI.js} +8 -8
- package/public/assets/{queries-BMwc6EvM.js → queries-2E6-CboE.js} +12 -12
- package/public/assets/{trash-2-DFVxurNH.js → trash-2--aH2iKg-.js} +1 -1
- package/public/index.html +2 -2
- package/public/assets/Rack-CRQSwSeB.js +0 -147
- package/public/assets/_app-VCodWes4.js +0 -1
- package/public/assets/_app.accounts-BP3L84us.js +0 -51
- package/public/assets/_app.index-C5Vd-zlp.js +0 -61
- package/public/assets/_app.keys-0e1BHrIZ.js +0 -39
- package/public/assets/_app.logs-DNSND96R.js +0 -33
- package/public/assets/_app.models-DzFIbnem.js +0 -144
- package/public/assets/catalog-DVRPlBJJ.js +0 -1
- package/public/assets/index-BYyeRTQJ.js +0 -170
package/bin/omni.js
CHANGED
|
@@ -147,35 +147,6 @@ function createAdminAuth(store, opts) {
|
|
|
147
147
|
}
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
|
-
// packages/control/src/config.ts
|
|
151
|
-
var MIN_KEY_LENGTH = 16;
|
|
152
|
-
var DECIMAL_INTEGER = /^\d+$/;
|
|
153
|
-
function optionalText(value, fallback) {
|
|
154
|
-
return value?.trim() || fallback;
|
|
155
|
-
}
|
|
156
|
-
function loadConfig(env) {
|
|
157
|
-
const encryptionKey = env.OMNI_ENCRYPTION_KEY;
|
|
158
|
-
if (typeof encryptionKey !== "string" || encryptionKey.length < MIN_KEY_LENGTH) {
|
|
159
|
-
throw new Error(`OMNI_ENCRYPTION_KEY must be set to at least ${MIN_KEY_LENGTH} characters. ` + "Generate one with: openssl rand -base64 32");
|
|
160
|
-
}
|
|
161
|
-
const host = optionalText(env.OMNI_HOST, "127.0.0.1");
|
|
162
|
-
const rawPort = env.OMNI_PORT ?? "9000";
|
|
163
|
-
const port = Number(rawPort);
|
|
164
|
-
if (!DECIMAL_INTEGER.test(rawPort) || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
165
|
-
throw new Error(`OMNI_PORT must be an integer between 1 and 65535, got "${rawPort}"`);
|
|
166
|
-
}
|
|
167
|
-
const derivedBaseUrl = `http://${host}:${port}`;
|
|
168
|
-
const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
|
|
169
|
-
const staticDir = env.OMNI_STATIC_DIR?.trim();
|
|
170
|
-
return {
|
|
171
|
-
port,
|
|
172
|
-
host,
|
|
173
|
-
databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
|
|
174
|
-
encryptionKey,
|
|
175
|
-
baseUrl,
|
|
176
|
-
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
150
|
// packages/ir/src/capabilities.ts
|
|
180
151
|
var PROVIDER_CAPABILITIES = {
|
|
181
152
|
anthropic: { tools: true, images: true, reasoning: true },
|
|
@@ -215,6 +186,86 @@ class GatewayError extends Error {
|
|
|
215
186
|
this.retryAfterMs = opts?.retryAfterMs;
|
|
216
187
|
}
|
|
217
188
|
}
|
|
189
|
+
// packages/ir/src/logger.ts
|
|
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
|
+
}
|
|
208
|
+
var noopLogger = {
|
|
209
|
+
debug: () => {},
|
|
210
|
+
info: () => {},
|
|
211
|
+
warn: () => {},
|
|
212
|
+
error: () => {},
|
|
213
|
+
enabled: () => false
|
|
214
|
+
};
|
|
215
|
+
function parseLogLevel(value) {
|
|
216
|
+
const normalized = value?.trim().toLowerCase();
|
|
217
|
+
if (normalized === undefined || normalized.length === 0)
|
|
218
|
+
return null;
|
|
219
|
+
return Object.hasOwn(LEVELS, normalized) ? normalized : null;
|
|
220
|
+
}
|
|
221
|
+
// packages/ir/src/request.ts
|
|
222
|
+
function cacheControlOf(block) {
|
|
223
|
+
return block.type === "thinking" ? undefined : block.cacheControl;
|
|
224
|
+
}
|
|
225
|
+
// packages/ir/src/stream.ts
|
|
226
|
+
function usageFromPromptTotal(promptTokens, outputTokens, cacheReadTokens, cacheWriteTokens = 0) {
|
|
227
|
+
return {
|
|
228
|
+
inputTokens: Math.max(0, promptTokens - cacheReadTokens - cacheWriteTokens),
|
|
229
|
+
outputTokens,
|
|
230
|
+
cacheReadTokens,
|
|
231
|
+
cacheWriteTokens
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// packages/control/src/config.ts
|
|
235
|
+
var MIN_KEY_LENGTH = 16;
|
|
236
|
+
var DECIMAL_INTEGER = /^\d+$/;
|
|
237
|
+
function optionalText(value, fallback) {
|
|
238
|
+
return value?.trim() || fallback;
|
|
239
|
+
}
|
|
240
|
+
function loadConfig(env) {
|
|
241
|
+
const encryptionKey = env.OMNI_ENCRYPTION_KEY;
|
|
242
|
+
if (typeof encryptionKey !== "string" || encryptionKey.length < MIN_KEY_LENGTH) {
|
|
243
|
+
throw new Error(`OMNI_ENCRYPTION_KEY must be set to at least ${MIN_KEY_LENGTH} characters. ` + "Generate one with: openssl rand -base64 32");
|
|
244
|
+
}
|
|
245
|
+
const host = optionalText(env.OMNI_HOST, "127.0.0.1");
|
|
246
|
+
const rawPort = env.OMNI_PORT ?? "9000";
|
|
247
|
+
const port = Number(rawPort);
|
|
248
|
+
if (!DECIMAL_INTEGER.test(rawPort) || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
249
|
+
throw new Error(`OMNI_PORT must be an integer between 1 and 65535, got "${rawPort}"`);
|
|
250
|
+
}
|
|
251
|
+
const derivedBaseUrl = `http://${host}:${port}`;
|
|
252
|
+
const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
|
|
253
|
+
const staticDir = env.OMNI_STATIC_DIR?.trim();
|
|
254
|
+
const logFile = env.OMNI_LOG_FILE?.trim();
|
|
255
|
+
const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
|
|
256
|
+
const logLevel = parseLogLevel(rawLogLevel);
|
|
257
|
+
return {
|
|
258
|
+
logLevel: logLevel ?? "info",
|
|
259
|
+
logLevelFallbackFrom: logLevel === null && rawLogLevel ? rawLogLevel : null,
|
|
260
|
+
port,
|
|
261
|
+
host,
|
|
262
|
+
databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
|
|
263
|
+
encryptionKey,
|
|
264
|
+
baseUrl,
|
|
265
|
+
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
|
|
266
|
+
logFile: logFile === undefined || logFile.length === 0 ? null : logFile
|
|
267
|
+
};
|
|
268
|
+
}
|
|
218
269
|
// packages/providers/src/body.ts
|
|
219
270
|
var BODY_ORDER = {
|
|
220
271
|
anthropic: [
|
|
@@ -301,8 +352,13 @@ function applyAnthropicSystem(system) {
|
|
|
301
352
|
let rewritten = text;
|
|
302
353
|
for (const [from, to] of REWRITES)
|
|
303
354
|
rewritten = rewritten.replaceAll(from, to);
|
|
304
|
-
if (rewritten.trim().length > 0)
|
|
305
|
-
kept.push({
|
|
355
|
+
if (rewritten.trim().length > 0) {
|
|
356
|
+
kept.push({
|
|
357
|
+
type: "text",
|
|
358
|
+
text: rewritten,
|
|
359
|
+
...block.cache_control === undefined ? {} : { cache_control: block.cache_control }
|
|
360
|
+
});
|
|
361
|
+
}
|
|
306
362
|
}
|
|
307
363
|
return [{ type: "text", text: billingBlock() }, { type: "text", text: AGENT_PREAMBLE }, ...kept];
|
|
308
364
|
}
|
|
@@ -606,6 +662,8 @@ async function* decodeAnthropic(messages) {
|
|
|
606
662
|
let inputTokens = 0;
|
|
607
663
|
let cacheReadTokens = 0;
|
|
608
664
|
let cacheWriteTokens = 0;
|
|
665
|
+
let cacheWrite5mTokens;
|
|
666
|
+
let cacheWrite1hTokens;
|
|
609
667
|
let outputTokens = 0;
|
|
610
668
|
let stopReason = "endTurn";
|
|
611
669
|
let terminal = false;
|
|
@@ -619,6 +677,8 @@ async function* decodeAnthropic(messages) {
|
|
|
619
677
|
inputTokens = m.usage?.input_tokens ?? 0;
|
|
620
678
|
cacheReadTokens = m.usage?.cache_read_input_tokens ?? 0;
|
|
621
679
|
cacheWriteTokens = m.usage?.cache_creation_input_tokens ?? 0;
|
|
680
|
+
cacheWrite5mTokens = m.usage?.cache_creation?.ephemeral_5m_input_tokens;
|
|
681
|
+
cacheWrite1hTokens = m.usage?.cache_creation?.ephemeral_1h_input_tokens;
|
|
622
682
|
yield { type: "start", id: String(m.id ?? ""), model: String(m.model ?? "") };
|
|
623
683
|
break;
|
|
624
684
|
}
|
|
@@ -628,7 +688,7 @@ async function* decodeAnthropic(messages) {
|
|
|
628
688
|
if (cb.type === "text")
|
|
629
689
|
yield { type: "blockStart", index, block: { type: "text" } };
|
|
630
690
|
else if (cb.type === "thinking")
|
|
631
|
-
yield { type: "blockStart", index, block: { type: "thinking" } };
|
|
691
|
+
yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
|
|
632
692
|
else if (cb.type === "tool_use")
|
|
633
693
|
yield {
|
|
634
694
|
type: "blockStart",
|
|
@@ -648,7 +708,7 @@ async function* decodeAnthropic(messages) {
|
|
|
648
708
|
index,
|
|
649
709
|
delta: { type: "thinking", text: delta.thinking ?? "" }
|
|
650
710
|
};
|
|
651
|
-
else if (delta.type === "signature_delta")
|
|
711
|
+
else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
|
|
652
712
|
yield {
|
|
653
713
|
type: "blockDelta",
|
|
654
714
|
index,
|
|
@@ -678,7 +738,14 @@ async function* decodeAnthropic(messages) {
|
|
|
678
738
|
yield {
|
|
679
739
|
type: "end",
|
|
680
740
|
stopReason,
|
|
681
|
-
usage: {
|
|
741
|
+
usage: {
|
|
742
|
+
inputTokens,
|
|
743
|
+
outputTokens,
|
|
744
|
+
cacheReadTokens,
|
|
745
|
+
cacheWriteTokens,
|
|
746
|
+
...cacheWrite5mTokens === undefined ? {} : { cacheWrite5mTokens },
|
|
747
|
+
...cacheWrite1hTokens === undefined ? {} : { cacheWrite1hTokens }
|
|
748
|
+
}
|
|
682
749
|
};
|
|
683
750
|
break;
|
|
684
751
|
case "error": {
|
|
@@ -708,25 +775,36 @@ async function* decodeAnthropic(messages) {
|
|
|
708
775
|
|
|
709
776
|
// packages/providers/src/anthropic/wire.ts
|
|
710
777
|
var OAUTH_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
778
|
+
function wireCacheControl(c) {
|
|
779
|
+
if (c === undefined)
|
|
780
|
+
return {};
|
|
781
|
+
return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
|
|
782
|
+
}
|
|
783
|
+
function isReplayableThinking(b) {
|
|
784
|
+
return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
|
|
785
|
+
}
|
|
711
786
|
function encodeBlock(b) {
|
|
787
|
+
const cache = wireCacheControl(cacheControlOf(b));
|
|
712
788
|
switch (b.type) {
|
|
713
789
|
case "text":
|
|
714
|
-
return { type: "text", text: b.text };
|
|
790
|
+
return { type: "text", text: b.text, ...cache };
|
|
715
791
|
case "image":
|
|
716
792
|
return {
|
|
717
793
|
type: "image",
|
|
718
|
-
source: { type: "base64", media_type: b.mediaType, data: b.data }
|
|
794
|
+
source: { type: "base64", media_type: b.mediaType, data: b.data },
|
|
795
|
+
...cache
|
|
719
796
|
};
|
|
720
797
|
case "thinking":
|
|
721
798
|
return { type: "thinking", thinking: b.text, signature: b.signature };
|
|
722
799
|
case "toolUse":
|
|
723
|
-
return { type: "tool_use", id: b.id, name: b.name, input: b.input };
|
|
800
|
+
return { type: "tool_use", id: b.id, name: b.name, input: b.input, ...cache };
|
|
724
801
|
case "toolResult":
|
|
725
802
|
return {
|
|
726
803
|
type: "tool_result",
|
|
727
804
|
tool_use_id: b.toolUseId,
|
|
728
805
|
content: b.content,
|
|
729
|
-
is_error: b.isError
|
|
806
|
+
is_error: b.isError,
|
|
807
|
+
...cache
|
|
730
808
|
};
|
|
731
809
|
}
|
|
732
810
|
}
|
|
@@ -734,6 +812,16 @@ function encodeSystemTurn(content) {
|
|
|
734
812
|
return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
|
|
735
813
|
`);
|
|
736
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
|
+
}
|
|
737
825
|
function encodeToolChoice(c) {
|
|
738
826
|
switch (c.type) {
|
|
739
827
|
case "auto":
|
|
@@ -748,16 +836,39 @@ function encodeToolChoice(c) {
|
|
|
748
836
|
}
|
|
749
837
|
function toWire(req, model, opts) {
|
|
750
838
|
const degradations = [];
|
|
751
|
-
|
|
839
|
+
const note2 = (d) => {
|
|
840
|
+
if (!degradations.includes(d))
|
|
841
|
+
degradations.push(d);
|
|
842
|
+
};
|
|
843
|
+
let system = req.system?.flatMap((b) => b.type === "text" ? [{ type: "text", text: b.text, ...wireCacheControl(b.cacheControl) }] : []);
|
|
752
844
|
if (opts.oauth && system?.[0]?.text !== OAUTH_IDENTITY) {
|
|
753
845
|
system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
|
|
754
|
-
|
|
846
|
+
note2("anthropic:oauth-system-prefix");
|
|
755
847
|
}
|
|
848
|
+
const systemCache = systemCacheControl(req);
|
|
849
|
+
if (systemCache.lost)
|
|
850
|
+
note2("anthropic:system-turn-cache-control-dropped");
|
|
756
851
|
const body = {
|
|
757
852
|
model,
|
|
758
|
-
messages: req.messages.
|
|
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) }];
|
|
863
|
+
}),
|
|
759
864
|
max_tokens: req.maxTokens ?? 4096,
|
|
760
|
-
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
|
+
}
|
|
761
872
|
};
|
|
762
873
|
if (system !== undefined && system.length > 0)
|
|
763
874
|
body.system = system;
|
|
@@ -769,7 +880,8 @@ function toWire(req, model, opts) {
|
|
|
769
880
|
body.tools = req.tools.map((t) => ({
|
|
770
881
|
name: t.name,
|
|
771
882
|
...t.description === undefined ? {} : { description: t.description },
|
|
772
|
-
input_schema: t.inputSchema
|
|
883
|
+
input_schema: t.inputSchema,
|
|
884
|
+
...wireCacheControl(t.cacheControl)
|
|
773
885
|
}));
|
|
774
886
|
}
|
|
775
887
|
if (req.toolChoice !== undefined)
|
|
@@ -809,12 +921,13 @@ var anthropicAdapter = {
|
|
|
809
921
|
const { body, degradations } = toWire(req.request, req.model, { oauth });
|
|
810
922
|
const withSystem = {
|
|
811
923
|
...body,
|
|
812
|
-
system: applyAnthropicSystem(body.system ?? [])
|
|
924
|
+
system: applyAnthropicSystem(body.system ?? []),
|
|
925
|
+
stream: true
|
|
813
926
|
};
|
|
814
927
|
const protocol = [
|
|
815
928
|
["Content-Type", "application/json"],
|
|
816
929
|
["anthropic-version", API_VERSION],
|
|
817
|
-
["Accept",
|
|
930
|
+
["Accept", "text/event-stream"]
|
|
818
931
|
];
|
|
819
932
|
const betas = new Set(req.request.betas ?? []);
|
|
820
933
|
if (oauth) {
|
|
@@ -833,6 +946,7 @@ var anthropicAdapter = {
|
|
|
833
946
|
const headers = orderHeaders(mergeHeaders(profile.headers, protocol), profile.order);
|
|
834
947
|
const bodyString = signAnthropicBody(JSON.stringify(orderFields(withSystem, BODY_ORDER.anthropic)));
|
|
835
948
|
const res = await req.http({
|
|
949
|
+
provider: "anthropic",
|
|
836
950
|
url: BASE_URL,
|
|
837
951
|
method: "POST",
|
|
838
952
|
headers,
|
|
@@ -853,22 +967,26 @@ var ANTHROPIC_MODELS = {
|
|
|
853
967
|
{
|
|
854
968
|
id: "claude-fable-5",
|
|
855
969
|
label: "Claude Fable 5",
|
|
856
|
-
pricing: { input: 10, output: 50, cacheRead: 1 }
|
|
970
|
+
pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
|
|
971
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
857
972
|
},
|
|
858
973
|
{
|
|
859
974
|
id: "claude-opus-5",
|
|
860
975
|
label: "Claude Opus 5",
|
|
861
|
-
pricing: { input: 5, output: 25, cacheRead: 0.5 }
|
|
976
|
+
pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
|
|
977
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
862
978
|
},
|
|
863
979
|
{
|
|
864
980
|
id: "claude-sonnet-5",
|
|
865
981
|
label: "Claude Sonnet 5",
|
|
866
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3 }
|
|
982
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
|
|
983
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
867
984
|
},
|
|
868
985
|
{
|
|
869
986
|
id: "claude-haiku-4-5",
|
|
870
987
|
label: "Claude Haiku 4.5",
|
|
871
|
-
pricing: { input: 1, output: 5, cacheRead: 0.1 }
|
|
988
|
+
pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
|
|
989
|
+
limits: { contextWindow: 200000, maxOutputTokens: 64000 }
|
|
872
990
|
}
|
|
873
991
|
]
|
|
874
992
|
};
|
|
@@ -880,22 +998,26 @@ var KIMI_MODELS = {
|
|
|
880
998
|
{
|
|
881
999
|
id: "k3-256k",
|
|
882
1000
|
label: "Kimi K3 \u2014 256K",
|
|
883
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3 }
|
|
1001
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1002
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
884
1003
|
},
|
|
885
1004
|
{
|
|
886
1005
|
id: "k3",
|
|
887
1006
|
label: "Kimi K3 \u2014 up to 1M",
|
|
888
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3 }
|
|
1007
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1008
|
+
limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
|
|
889
1009
|
},
|
|
890
1010
|
{
|
|
891
1011
|
id: "kimi-for-coding",
|
|
892
1012
|
label: "Kimi K2.7 Code",
|
|
893
|
-
pricing: { input: 0.95, output: 4, cacheRead: 0.19 }
|
|
1013
|
+
pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1014
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
894
1015
|
},
|
|
895
1016
|
{
|
|
896
1017
|
id: "kimi-for-coding-highspeed",
|
|
897
1018
|
label: "Kimi K2.7 Code \u2014 High Speed",
|
|
898
|
-
pricing: { input: 0.95, output: 8, cacheRead: 0.19 }
|
|
1019
|
+
pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1020
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
899
1021
|
}
|
|
900
1022
|
]
|
|
901
1023
|
};
|
|
@@ -907,22 +1029,30 @@ var OPENAI_MODELS = {
|
|
|
907
1029
|
{
|
|
908
1030
|
id: "gpt-5.6",
|
|
909
1031
|
label: "GPT-5.6 \u2014 routes to Sol",
|
|
910
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5 }
|
|
1032
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1033
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1034
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
911
1035
|
},
|
|
912
1036
|
{
|
|
913
1037
|
id: "gpt-5.6-sol",
|
|
914
1038
|
label: "GPT-5.6 Sol \u2014 deepest reasoning",
|
|
915
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5 }
|
|
1039
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1040
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1041
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
916
1042
|
},
|
|
917
1043
|
{
|
|
918
1044
|
id: "gpt-5.6-terra",
|
|
919
1045
|
label: "GPT-5.6 Terra \u2014 balanced",
|
|
920
|
-
pricing: { input: 2, output: 12, cacheRead: 0.2 }
|
|
1046
|
+
pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1047
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1048
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
921
1049
|
},
|
|
922
1050
|
{
|
|
923
1051
|
id: "gpt-5.6-luna",
|
|
924
1052
|
label: "GPT-5.6 Luna \u2014 fastest",
|
|
925
|
-
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02 }
|
|
1053
|
+
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
1054
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
1055
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
926
1056
|
}
|
|
927
1057
|
]
|
|
928
1058
|
};
|
|
@@ -934,15 +1064,38 @@ var PROVIDER_MODEL_CATALOG = {
|
|
|
934
1064
|
kimi: KIMI_MODELS
|
|
935
1065
|
};
|
|
936
1066
|
function catalogPricing(provider, model) {
|
|
937
|
-
return PROVIDER_MODEL_CATALOG[provider]
|
|
1067
|
+
return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
|
|
1068
|
+
}
|
|
1069
|
+
function catalogLimits(provider, model, auth = "apiKey") {
|
|
1070
|
+
const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
|
|
1071
|
+
if (entry === undefined)
|
|
1072
|
+
return null;
|
|
1073
|
+
return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
|
|
938
1074
|
}
|
|
939
1075
|
// packages/providers/src/http-client.ts
|
|
940
1076
|
import { request as httpRequest } from "http";
|
|
941
1077
|
import { request as httpsRequest } from "https";
|
|
942
1078
|
import { Readable } from "stream";
|
|
943
|
-
function nodeHttpClient() {
|
|
1079
|
+
function nodeHttpClient(options = {}) {
|
|
1080
|
+
const logger2 = options.logger ?? noopLogger;
|
|
1081
|
+
const now = options.now ?? (() => Date.now());
|
|
944
1082
|
return (req) => new Promise((resolve, reject) => {
|
|
945
1083
|
const url = new URL(req.url);
|
|
1084
|
+
const startedAt = now();
|
|
1085
|
+
let traced = false;
|
|
1086
|
+
const trace = (status, failed = false) => {
|
|
1087
|
+
if (traced || !logger2.enabled("debug"))
|
|
1088
|
+
return;
|
|
1089
|
+
traced = true;
|
|
1090
|
+
logger2.debug("upstream http", {
|
|
1091
|
+
provider: req.provider,
|
|
1092
|
+
status,
|
|
1093
|
+
host: url.host,
|
|
1094
|
+
path: url.pathname,
|
|
1095
|
+
durationMs: now() - startedAt,
|
|
1096
|
+
reason: failed ? "transport error" : undefined
|
|
1097
|
+
});
|
|
1098
|
+
};
|
|
946
1099
|
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
947
1100
|
const bodyBytes = Buffer.from(req.body, "utf8");
|
|
948
1101
|
const headers = {};
|
|
@@ -970,6 +1123,7 @@ function nodeHttpClient() {
|
|
|
970
1123
|
else if (typeof v === "string")
|
|
971
1124
|
responseHeaders.set(k, v);
|
|
972
1125
|
}
|
|
1126
|
+
trace(incoming.statusCode);
|
|
973
1127
|
resolve({
|
|
974
1128
|
status: incoming.statusCode ?? 0,
|
|
975
1129
|
headers: responseHeaders,
|
|
@@ -985,17 +1139,17 @@ function nodeHttpClient() {
|
|
|
985
1139
|
});
|
|
986
1140
|
});
|
|
987
1141
|
const onAbort = () => outgoing.destroy(new Error("aborted"));
|
|
988
|
-
if (req.signal.aborted) {
|
|
989
|
-
outgoing.destroy(new Error("aborted"));
|
|
990
|
-
reject(new Error("aborted"));
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
994
1142
|
outgoing.on("error", (err) => {
|
|
995
1143
|
req.signal.removeEventListener("abort", onAbort);
|
|
1144
|
+
trace(undefined, true);
|
|
996
1145
|
reject(err);
|
|
997
1146
|
});
|
|
998
1147
|
outgoing.on("close", () => req.signal.removeEventListener("abort", onAbort));
|
|
1148
|
+
if (req.signal.aborted) {
|
|
1149
|
+
outgoing.destroy(new Error("aborted"));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
999
1153
|
if (bodyBytes.byteLength > 0)
|
|
1000
1154
|
outgoing.write(bodyBytes);
|
|
1001
1155
|
outgoing.end();
|
|
@@ -1064,12 +1218,8 @@ async function* decodeChat(messages) {
|
|
|
1064
1218
|
yield { type: "start", id: String(d.id ?? ""), model: String(d.model ?? "") };
|
|
1065
1219
|
}
|
|
1066
1220
|
if (d.usage) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
outputTokens: d.usage.completion_tokens ?? 0,
|
|
1070
|
-
cacheReadTokens: d.usage.prompt_cache_hit_tokens ?? 0,
|
|
1071
|
-
cacheWriteTokens: 0
|
|
1072
|
-
};
|
|
1221
|
+
const details = d.usage.prompt_tokens_details;
|
|
1222
|
+
usage = usageFromPromptTotal(d.usage.prompt_tokens ?? 0, d.usage.completion_tokens ?? 0, details?.cached_tokens ?? d.usage.prompt_cache_hit_tokens ?? 0, details?.cache_creation_tokens ?? details?.cache_write_tokens ?? 0);
|
|
1073
1223
|
}
|
|
1074
1224
|
const choice = d.choices?.[0];
|
|
1075
1225
|
if (!choice)
|
|
@@ -1195,7 +1345,12 @@ function toChatWire(req, model) {
|
|
|
1195
1345
|
`) });
|
|
1196
1346
|
}
|
|
1197
1347
|
}
|
|
1198
|
-
const body = {
|
|
1348
|
+
const body = {
|
|
1349
|
+
model,
|
|
1350
|
+
messages,
|
|
1351
|
+
stream: req.stream,
|
|
1352
|
+
stream_options: { include_usage: true }
|
|
1353
|
+
};
|
|
1199
1354
|
if (req.maxTokens !== undefined)
|
|
1200
1355
|
body.max_tokens = req.maxTokens;
|
|
1201
1356
|
if (req.temperature !== undefined)
|
|
@@ -1236,6 +1391,7 @@ var kimiAdapter = {
|
|
|
1236
1391
|
const profile = PROFILES.kimi;
|
|
1237
1392
|
const headers = orderHeaders(mergeHeaders(profile.headers, protocol), profile.order);
|
|
1238
1393
|
const res = await req.http({
|
|
1394
|
+
provider: "kimi",
|
|
1239
1395
|
url: BASE_URL2,
|
|
1240
1396
|
method: "POST",
|
|
1241
1397
|
headers,
|
|
@@ -1366,12 +1522,7 @@ async function* decodeResponses(messages) {
|
|
|
1366
1522
|
yield {
|
|
1367
1523
|
type: "end",
|
|
1368
1524
|
stopReason,
|
|
1369
|
-
usage:
|
|
1370
|
-
inputTokens: r.usage?.input_tokens ?? 0,
|
|
1371
|
-
outputTokens: r.usage?.output_tokens ?? 0,
|
|
1372
|
-
cacheReadTokens: r.usage?.input_tokens_details?.cached_tokens ?? 0,
|
|
1373
|
-
cacheWriteTokens: 0
|
|
1374
|
-
}
|
|
1525
|
+
usage: usageFromPromptTotal(r.usage?.input_tokens ?? 0, r.usage?.output_tokens ?? 0, r.usage?.input_tokens_details?.cached_tokens ?? r.usage?.prompt_tokens_details?.cached_tokens ?? 0)
|
|
1375
1526
|
};
|
|
1376
1527
|
break;
|
|
1377
1528
|
}
|
|
@@ -1544,6 +1695,7 @@ var openaiAdapter = {
|
|
|
1544
1695
|
const profile = PROFILES.openai;
|
|
1545
1696
|
const headers = orderHeaders(mergeHeaders(profile.headers, protocol), profile.order);
|
|
1546
1697
|
const res = await req.http({
|
|
1698
|
+
provider: "openai",
|
|
1547
1699
|
url: oauth ? OAUTH_URL : API_URL,
|
|
1548
1700
|
method: "POST",
|
|
1549
1701
|
headers,
|
|
@@ -1558,13 +1710,14 @@ var openaiAdapter = {
|
|
|
1558
1710
|
}
|
|
1559
1711
|
};
|
|
1560
1712
|
// packages/control/src/oauth/types.ts
|
|
1561
|
-
async function postJson(deps, url, profile2, opts) {
|
|
1713
|
+
async function postJson(deps, provider, url, profile2, opts) {
|
|
1562
1714
|
const headers = orderHeaders(mergeHeaders(profile2.headers, [
|
|
1563
1715
|
["Content-Type", opts.contentType],
|
|
1564
1716
|
["Accept", "application/json"],
|
|
1565
1717
|
...opts.extraHeaders ?? []
|
|
1566
1718
|
]), profile2.order);
|
|
1567
1719
|
const res = await deps.http({
|
|
1720
|
+
provider,
|
|
1568
1721
|
url,
|
|
1569
1722
|
method: "POST",
|
|
1570
1723
|
headers,
|
|
@@ -1578,13 +1731,14 @@ async function postJson(deps, url, profile2, opts) {
|
|
|
1578
1731
|
} catch {}
|
|
1579
1732
|
return { status: res.status, parsed };
|
|
1580
1733
|
}
|
|
1581
|
-
async function getJson(deps, url, profile2, opts) {
|
|
1734
|
+
async function getJson(deps, provider, url, profile2, opts) {
|
|
1582
1735
|
const headers = orderHeaders(mergeHeaders(profile2.headers, [
|
|
1583
1736
|
["Authorization", `Bearer ${opts.accessToken}`],
|
|
1584
1737
|
["Accept", "application/json"],
|
|
1585
1738
|
...opts.extraHeaders ?? []
|
|
1586
1739
|
]), profile2.order);
|
|
1587
1740
|
const res = await deps.http({
|
|
1741
|
+
provider,
|
|
1588
1742
|
url,
|
|
1589
1743
|
method: "GET",
|
|
1590
1744
|
headers,
|
|
@@ -1778,7 +1932,7 @@ function deviceCodeFrom(value) {
|
|
|
1778
1932
|
};
|
|
1779
1933
|
}
|
|
1780
1934
|
async function post(url, body2, device, deps) {
|
|
1781
|
-
const { status, parsed } = await postJson(deps, url, PROFILES.kimi, {
|
|
1935
|
+
const { status, parsed } = await postJson(deps, "kimi", url, PROFILES.kimi, {
|
|
1782
1936
|
contentType: "application/x-www-form-urlencoded",
|
|
1783
1937
|
body: new URLSearchParams({ ...body2, client_id: CLIENT_ID }).toString(),
|
|
1784
1938
|
extraHeaders: kimiDeviceHeaders(device)
|
|
@@ -1881,7 +2035,7 @@ var kimiOAuth = {
|
|
|
1881
2035
|
if (secrets.accessToken === null)
|
|
1882
2036
|
return null;
|
|
1883
2037
|
const device = deviceFrom(providerData);
|
|
1884
|
-
const { status, parsed } = await getJson(deps, USAGE_URL, PROFILES.kimi, {
|
|
2038
|
+
const { status, parsed } = await getJson(deps, "kimi", USAGE_URL, PROFILES.kimi, {
|
|
1885
2039
|
accessToken: secrets.accessToken,
|
|
1886
2040
|
extraHeaders: kimiDeviceHeaders(device)
|
|
1887
2041
|
});
|
|
@@ -1950,6 +2104,7 @@ function deviceIdFrom(start) {
|
|
|
1950
2104
|
return deviceId;
|
|
1951
2105
|
}
|
|
1952
2106
|
function createConnectFlows(deps) {
|
|
2107
|
+
const logger2 = deps.logger ?? noopLogger;
|
|
1953
2108
|
const flows = createPendingFlows({ now: deps.now, ttlMs: FLOW_TTL_MS });
|
|
1954
2109
|
const pollsInFlight = new Map;
|
|
1955
2110
|
const callbackUri = (provider) => provider === "openai" ? "http://localhost:1455/auth/callback" : "";
|
|
@@ -1994,6 +2149,10 @@ function createConnectFlows(deps) {
|
|
|
1994
2149
|
disabledAt: null,
|
|
1995
2150
|
...result.secrets
|
|
1996
2151
|
});
|
|
2152
|
+
logger2.info("oauth connect completed", {
|
|
2153
|
+
provider: flow.provider,
|
|
2154
|
+
credentialId: id
|
|
2155
|
+
});
|
|
1997
2156
|
return { id };
|
|
1998
2157
|
}
|
|
1999
2158
|
async function pollOutcome(flowId, poll) {
|
|
@@ -2066,6 +2225,62 @@ function createConnectFlows(deps) {
|
|
|
2066
2225
|
}
|
|
2067
2226
|
};
|
|
2068
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
|
+
}
|
|
2069
2284
|
// node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
2070
2285
|
var exports_external = {};
|
|
2071
2286
|
__export(exports_external, {
|
|
@@ -16369,28 +16584,18 @@ var modelSchema = exports_external.object({
|
|
|
16369
16584
|
costPerMTok: exports_external.object({
|
|
16370
16585
|
input: exports_external.number().min(0),
|
|
16371
16586
|
output: exports_external.number().min(0),
|
|
16372
|
-
cacheRead: exports_external.number().min(0)
|
|
16373
|
-
|
|
16374
|
-
|
|
16375
|
-
tools: exports_external.boolean(),
|
|
16376
|
-
images: exports_external.boolean(),
|
|
16377
|
-
reasoning: exports_external.boolean()
|
|
16378
|
-
})
|
|
16379
|
-
}).or(exports_external.object({
|
|
16380
|
-
provider: providerIdSchema,
|
|
16381
|
-
model: exports_external.string().min(1),
|
|
16382
|
-
tier: exports_external.number().int().min(1),
|
|
16383
|
-
weight: exports_external.number().positive(),
|
|
16384
|
-
costPerMTok: exports_external.object({
|
|
16385
|
-
input: exports_external.number().min(0),
|
|
16386
|
-
output: exports_external.number().min(0)
|
|
16587
|
+
cacheRead: exports_external.number().min(0).optional(),
|
|
16588
|
+
cacheWrite5m: exports_external.number().min(0).optional(),
|
|
16589
|
+
cacheWrite1h: exports_external.number().min(0).optional()
|
|
16387
16590
|
}),
|
|
16591
|
+
contextWindow: exports_external.number().int().positive().optional(),
|
|
16592
|
+
maxOutputTokens: exports_external.number().int().positive().optional(),
|
|
16388
16593
|
capabilities: exports_external.object({
|
|
16389
16594
|
tools: exports_external.boolean(),
|
|
16390
16595
|
images: exports_external.boolean(),
|
|
16391
16596
|
reasoning: exports_external.boolean()
|
|
16392
16597
|
})
|
|
16393
|
-
}))
|
|
16598
|
+
})).min(1, "a virtual model needs at least one target")
|
|
16394
16599
|
});
|
|
16395
16600
|
var keyCreateSchema = exports_external.object({
|
|
16396
16601
|
label: exports_external.string().min(1).default("api key"),
|
|
@@ -16468,7 +16673,7 @@ async function getCredential(store, id) {
|
|
|
16468
16673
|
throw new GatewayError("BAD_REQUEST", "no such credential");
|
|
16469
16674
|
return summarizeCredential(credential);
|
|
16470
16675
|
}
|
|
16471
|
-
async function createApiKeyCredential(store, input) {
|
|
16676
|
+
async function createApiKeyCredential(store, input, logger2 = noopLogger) {
|
|
16472
16677
|
const provider = parseOrThrow(providerIdSchema, input.provider);
|
|
16473
16678
|
if (typeof input.apiKey !== "string" || input.apiKey.trim().length === 0) {
|
|
16474
16679
|
throw new GatewayError("BAD_REQUEST", "apiKey: must not be empty");
|
|
@@ -16495,6 +16700,7 @@ async function createApiKeyCredential(store, input) {
|
|
|
16495
16700
|
apiKey: input.apiKey,
|
|
16496
16701
|
idToken: null
|
|
16497
16702
|
});
|
|
16703
|
+
logger2.info("credential added", { credentialId: created.id, provider: created.provider });
|
|
16498
16704
|
return summarizeCredential(created);
|
|
16499
16705
|
}
|
|
16500
16706
|
async function refreshCredential(deps, id) {
|
|
@@ -16554,6 +16760,12 @@ async function patchCredential(deps, id, input) {
|
|
|
16554
16760
|
...patch.tier === undefined ? {} : { tier: patch.tier },
|
|
16555
16761
|
...patch.weight === undefined ? {} : { weight: patch.weight }
|
|
16556
16762
|
});
|
|
16763
|
+
if (patch.enabled !== undefined && patch.enabled !== existing.enabled) {
|
|
16764
|
+
(deps.logger ?? noopLogger).info(patch.enabled ? "credential enabled" : "credential disabled", {
|
|
16765
|
+
credentialId: id,
|
|
16766
|
+
provider: existing.provider
|
|
16767
|
+
});
|
|
16768
|
+
}
|
|
16557
16769
|
}
|
|
16558
16770
|
async function removeCredential(store, id) {
|
|
16559
16771
|
await store.credentials.remove(id);
|
|
@@ -17718,7 +17930,9 @@ function createUsageRepo(db) {
|
|
|
17718
17930
|
|
|
17719
17931
|
// packages/store/src/sqlite/store.ts
|
|
17720
17932
|
async function createStore(opts) {
|
|
17933
|
+
const logger2 = opts.logger ?? noopLogger;
|
|
17721
17934
|
const db = openDb(opts.path);
|
|
17935
|
+
logger2.debug("store opened", { path: opts.path });
|
|
17722
17936
|
const listeners = new Set;
|
|
17723
17937
|
const emit2 = (change) => {
|
|
17724
17938
|
for (const listener of listeners) {
|
|
@@ -17811,7 +18025,7 @@ var REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
|
|
|
17811
18025
|
var SCOPES = "org:create_api_key user:profile user:inference";
|
|
17812
18026
|
var USAGE_URL2 = "https://api.anthropic.com/api/oauth/usage";
|
|
17813
18027
|
async function postToken(body2, deps) {
|
|
17814
|
-
const { status, parsed } = await postJson(deps, TOKEN_URL2, PROFILES.anthropic, {
|
|
18028
|
+
const { status, parsed } = await postJson(deps, "anthropic", TOKEN_URL2, PROFILES.anthropic, {
|
|
17815
18029
|
contentType: "application/json",
|
|
17816
18030
|
body: JSON.stringify({ ...body2, client_id: CLIENT_ID2 })
|
|
17817
18031
|
});
|
|
@@ -17889,7 +18103,7 @@ var anthropicOAuth = {
|
|
|
17889
18103
|
async usage(secrets, deps) {
|
|
17890
18104
|
if (secrets.accessToken === null)
|
|
17891
18105
|
return null;
|
|
17892
|
-
const { status, parsed } = await getJson(deps, USAGE_URL2, PROFILES.anthropic, {
|
|
18106
|
+
const { status, parsed } = await getJson(deps, "anthropic", USAGE_URL2, PROFILES.anthropic, {
|
|
17893
18107
|
accessToken: secrets.accessToken,
|
|
17894
18108
|
extraHeaders: [
|
|
17895
18109
|
["anthropic-beta", "oauth-2025-04-20"],
|
|
@@ -17947,7 +18161,7 @@ function parseTokenResponse(value) {
|
|
|
17947
18161
|
};
|
|
17948
18162
|
}
|
|
17949
18163
|
async function postToken2(body2, deps) {
|
|
17950
|
-
const { status, parsed } = await postJson(deps, TOKEN_URL3, PROFILES.openai, {
|
|
18164
|
+
const { status, parsed } = await postJson(deps, "openai", TOKEN_URL3, PROFILES.openai, {
|
|
17951
18165
|
contentType: "application/x-www-form-urlencoded",
|
|
17952
18166
|
body: new URLSearchParams({ ...body2, client_id: CLIENT_ID3 }).toString()
|
|
17953
18167
|
});
|
|
@@ -18046,7 +18260,7 @@ var openaiOAuth = {
|
|
|
18046
18260
|
if (secrets.accessToken === null)
|
|
18047
18261
|
return null;
|
|
18048
18262
|
const accountId = accountIdFromProviderData(providerData);
|
|
18049
|
-
const { status, parsed } = await getJson(deps, USAGE_URL3, PROFILES.openai, {
|
|
18263
|
+
const { status, parsed } = await getJson(deps, "openai", USAGE_URL3, PROFILES.openai, {
|
|
18050
18264
|
accessToken: secrets.accessToken,
|
|
18051
18265
|
...accountId === null ? {} : { extraHeaders: [["chatgpt-account-id", accountId]] }
|
|
18052
18266
|
});
|
|
@@ -18064,6 +18278,7 @@ var OAUTH_PROVIDERS = {
|
|
|
18064
18278
|
};
|
|
18065
18279
|
// packages/control/src/oauth/refresh.ts
|
|
18066
18280
|
function createRefresher(deps) {
|
|
18281
|
+
const logger2 = deps.logger ?? noopLogger;
|
|
18067
18282
|
const inFlight = new Map;
|
|
18068
18283
|
async function run(credential) {
|
|
18069
18284
|
const secrets = await credential.openForRefresh();
|
|
@@ -18071,11 +18286,20 @@ function createRefresher(deps) {
|
|
|
18071
18286
|
throw new GatewayError("AUTH", `credential ${credential.id} has no refresh token`);
|
|
18072
18287
|
}
|
|
18073
18288
|
const provider = deps.providers[credential.provider];
|
|
18289
|
+
logger2.debug("refreshing credential", {
|
|
18290
|
+
provider: credential.provider,
|
|
18291
|
+
credentialId: credential.id
|
|
18292
|
+
});
|
|
18074
18293
|
let result;
|
|
18075
18294
|
try {
|
|
18076
18295
|
result = await provider.refresh(secrets.refreshToken, { http: deps.http, now: deps.now }, credential.providerData);
|
|
18077
18296
|
} catch (error51) {
|
|
18078
18297
|
if (error51 instanceof GatewayError && error51.code === "AUTH") {
|
|
18298
|
+
logger2.warn("credential disabled: refresh token rejected", {
|
|
18299
|
+
provider: credential.provider,
|
|
18300
|
+
credentialId: credential.id,
|
|
18301
|
+
code: "AUTH"
|
|
18302
|
+
});
|
|
18079
18303
|
await deps.store.credentials.update(credential.id, {
|
|
18080
18304
|
enabled: false,
|
|
18081
18305
|
disabledReason: "tokenRejected",
|
|
@@ -18089,6 +18313,10 @@ function createRefresher(deps) {
|
|
|
18089
18313
|
accountEmail: result.accountEmail ?? credential.accountEmail,
|
|
18090
18314
|
providerData: { ...credential.providerData, ...result.providerData }
|
|
18091
18315
|
});
|
|
18316
|
+
logger2.debug("credential refreshed", {
|
|
18317
|
+
provider: credential.provider,
|
|
18318
|
+
credentialId: credential.id
|
|
18319
|
+
});
|
|
18092
18320
|
return result.secrets;
|
|
18093
18321
|
}
|
|
18094
18322
|
return function refresh(credential) {
|
|
@@ -18113,6 +18341,50 @@ async function putSettings(store, input) {
|
|
|
18113
18341
|
await store.config.putSettings(settings);
|
|
18114
18342
|
return settings;
|
|
18115
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
|
+
}
|
|
18116
18388
|
// packages/control/src/usage.ts
|
|
18117
18389
|
var MAX_LOG_LIMIT = 500;
|
|
18118
18390
|
var DEFAULT_LOG_LIMIT = 100;
|
|
@@ -18347,6 +18619,278 @@ async function pollUntilAuthorized(flows, start, deps) {
|
|
|
18347
18619
|
}
|
|
18348
18620
|
}
|
|
18349
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
|
+
|
|
18350
18894
|
// apps/cli/src/command.ts
|
|
18351
18895
|
var PROVIDER_TONE = {
|
|
18352
18896
|
anthropic: "magenta",
|
|
@@ -18387,7 +18931,7 @@ var credentialsList = {
|
|
|
18387
18931
|
{ header: "STATE" },
|
|
18388
18932
|
{ header: "EXPIRES" }
|
|
18389
18933
|
], credentials.map((c) => {
|
|
18390
|
-
const
|
|
18934
|
+
const status2 = condition(c);
|
|
18391
18935
|
return [
|
|
18392
18936
|
c.id,
|
|
18393
18937
|
provider(ctx, c.provider),
|
|
@@ -18395,7 +18939,7 @@ var credentialsList = {
|
|
|
18395
18939
|
c.authType,
|
|
18396
18940
|
String(c.tier),
|
|
18397
18941
|
String(c.weight),
|
|
18398
|
-
state(ctx,
|
|
18942
|
+
state(ctx, status2.ok, status2.text),
|
|
18399
18943
|
formatTime(c.expiresAt)
|
|
18400
18944
|
];
|
|
18401
18945
|
}));
|
|
@@ -18411,7 +18955,7 @@ var credentialsShow = {
|
|
|
18411
18955
|
async run(args, { ctx, writer }) {
|
|
18412
18956
|
const id = requirePositional(args, 0, "credential id");
|
|
18413
18957
|
const credential = await findCredential(ctx, id);
|
|
18414
|
-
const
|
|
18958
|
+
const status2 = condition(credential);
|
|
18415
18959
|
const data = {
|
|
18416
18960
|
id: credential.id,
|
|
18417
18961
|
provider: credential.provider,
|
|
@@ -18434,7 +18978,7 @@ var credentialsShow = {
|
|
|
18434
18978
|
["provider", provider(ctx, credential.provider)],
|
|
18435
18979
|
["label", credential.label],
|
|
18436
18980
|
["auth", credential.authType],
|
|
18437
|
-
["state", state(ctx,
|
|
18981
|
+
["state", state(ctx, status2.ok, status2.text)],
|
|
18438
18982
|
["tier", String(credential.tier)],
|
|
18439
18983
|
["weight", String(credential.weight)],
|
|
18440
18984
|
["account", credential.accountEmail ?? "\u2014"],
|
|
@@ -18569,12 +19113,12 @@ var credentialsHealth = {
|
|
|
18569
19113
|
};
|
|
18570
19114
|
|
|
18571
19115
|
// apps/cli/src/commands/db.ts
|
|
18572
|
-
import { existsSync as
|
|
19116
|
+
import { existsSync as existsSync3 } from "fs";
|
|
18573
19117
|
var dbMigrate = {
|
|
18574
19118
|
usage: "db migrate",
|
|
18575
19119
|
summary: "Create or upgrade the database schema",
|
|
18576
19120
|
async run(_args, { ctx, writer }) {
|
|
18577
|
-
const existed =
|
|
19121
|
+
const existed = existsSync3(ctx.databasePath);
|
|
18578
19122
|
const store = await ctx.store();
|
|
18579
19123
|
await store.config.getSettings();
|
|
18580
19124
|
emit(ctx, writer, { path: ctx.databasePath, created: !existed }, () => fields([
|
|
@@ -18696,6 +19240,11 @@ var modelsShow = {
|
|
|
18696
19240
|
{ header: "WEIGHT", align: "right" },
|
|
18697
19241
|
{ header: "IN $/MTOK", align: "right" },
|
|
18698
19242
|
{ header: "OUT $/MTOK", align: "right" },
|
|
19243
|
+
{ header: "CACHE R", align: "right" },
|
|
19244
|
+
{ header: "CACHE W 5M", align: "right" },
|
|
19245
|
+
{ header: "CACHE W 1H", align: "right" },
|
|
19246
|
+
{ header: "CONTEXT", align: "right" },
|
|
19247
|
+
{ header: "MAX OUT", align: "right" },
|
|
18699
19248
|
{ header: "CAPABILITIES" }
|
|
18700
19249
|
], model.targets.map((target) => [
|
|
18701
19250
|
provider(ctx, target.provider),
|
|
@@ -18704,12 +19253,23 @@ var modelsShow = {
|
|
|
18704
19253
|
String(target.weight),
|
|
18705
19254
|
target.costPerMTok.input.toFixed(2),
|
|
18706
19255
|
target.costPerMTok.output.toFixed(2),
|
|
19256
|
+
price(target.costPerMTok.cacheRead),
|
|
19257
|
+
price(target.costPerMTok.cacheWrite5m),
|
|
19258
|
+
price(target.costPerMTok.cacheWrite1h),
|
|
19259
|
+
tokens(target.contextWindow),
|
|
19260
|
+
tokens(target.maxOutputTokens),
|
|
18707
19261
|
capabilityList(target)
|
|
18708
19262
|
]))
|
|
18709
19263
|
].join(`
|
|
18710
19264
|
`));
|
|
18711
19265
|
}
|
|
18712
19266
|
};
|
|
19267
|
+
function price(value) {
|
|
19268
|
+
return value === undefined ? "\u2014" : value.toFixed(2);
|
|
19269
|
+
}
|
|
19270
|
+
function tokens(value) {
|
|
19271
|
+
return value === undefined ? "\u2014" : value.toLocaleString("en-US");
|
|
19272
|
+
}
|
|
18713
19273
|
function capabilityList(target) {
|
|
18714
19274
|
const on = Object.entries(target.capabilities).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
18715
19275
|
return on.length === 0 ? "\u2014" : on.join(",");
|
|
@@ -18741,7 +19301,7 @@ function isCatalogProvider(value) {
|
|
|
18741
19301
|
return value in PROVIDER_MODEL_CATALOG;
|
|
18742
19302
|
}
|
|
18743
19303
|
function catalogRows() {
|
|
18744
|
-
return Object.entries(PROVIDER_MODEL_CATALOG).flatMap(([id, entry]) => entry.models.map((model) => ({
|
|
19304
|
+
return Object.entries(PROVIDER_MODEL_CATALOG).flatMap(([id, entry]) => entry.models.map((model) => ({ ...model, provider: id })));
|
|
18745
19305
|
}
|
|
18746
19306
|
var modelsCatalog = {
|
|
18747
19307
|
usage: "models catalog [--provider P]",
|
|
@@ -18755,15 +19315,29 @@ var modelsCatalog = {
|
|
|
18755
19315
|
{ header: "MODEL" },
|
|
18756
19316
|
{ header: "LABEL" },
|
|
18757
19317
|
{ header: "IN $/MTOK", align: "right" },
|
|
18758
|
-
{ header: "OUT $/MTOK", align: "right" }
|
|
19318
|
+
{ header: "OUT $/MTOK", align: "right" },
|
|
19319
|
+
{ header: "CACHE R", align: "right" },
|
|
19320
|
+
{ header: "CACHE W 5M", align: "right" },
|
|
19321
|
+
{ header: "CACHE W 1H", align: "right" },
|
|
19322
|
+
{ header: "CONTEXT", align: "right" },
|
|
19323
|
+
{ header: "CONTEXT OAUTH", align: "right" },
|
|
19324
|
+
{ header: "MAX OUT", align: "right" }
|
|
18759
19325
|
], entries.map((entry) => {
|
|
18760
|
-
const
|
|
19326
|
+
const listed = catalogPricing(entry.provider, entry.id);
|
|
19327
|
+
const limits = catalogLimits(entry.provider, entry.id);
|
|
19328
|
+
const oauth = catalogLimits(entry.provider, entry.id, "oauth");
|
|
18761
19329
|
return [
|
|
18762
19330
|
provider(ctx, entry.provider),
|
|
18763
19331
|
entry.id,
|
|
18764
19332
|
entry.label,
|
|
18765
|
-
price
|
|
18766
|
-
price
|
|
19333
|
+
price(listed?.input),
|
|
19334
|
+
price(listed?.output),
|
|
19335
|
+
price(listed?.cacheRead),
|
|
19336
|
+
price(listed?.cacheWrite5m),
|
|
19337
|
+
price(listed?.cacheWrite1h),
|
|
19338
|
+
tokens(limits?.contextWindow),
|
|
19339
|
+
oauth?.contextWindow === limits?.contextWindow ? "\u2014" : tokens(oauth?.contextWindow),
|
|
19340
|
+
tokens(limits?.maxOutputTokens)
|
|
18767
19341
|
];
|
|
18768
19342
|
})));
|
|
18769
19343
|
}
|
|
@@ -18872,207 +19446,8 @@ ${candidates}${excluded}`;
|
|
|
18872
19446
|
import { existsSync as existsSync5 } from "fs";
|
|
18873
19447
|
|
|
18874
19448
|
// apps/cli/src/runtime.ts
|
|
18875
|
-
import { closeSync, existsSync as existsSync4, openSync } from "fs";
|
|
19449
|
+
import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2 } from "fs";
|
|
18876
19450
|
import { join as join3 } from "path";
|
|
18877
|
-
|
|
18878
|
-
// apps/cli/src/service.ts
|
|
18879
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
|
|
18880
|
-
import { homedir as homedir2 } from "os";
|
|
18881
|
-
import { dirname, join as join2 } from "path";
|
|
18882
|
-
var UNIT_NAME = "omnigateway.service";
|
|
18883
|
-
function unitPath(scope) {
|
|
18884
|
-
return scope === "system" ? join2("/etc/systemd/system", UNIT_NAME) : join2(homedir2(), ".config", "systemd", "user", UNIT_NAME);
|
|
18885
|
-
}
|
|
18886
|
-
function systemctlArgs(scope, args) {
|
|
18887
|
-
return scope === "system" ? ["systemctl", ...args] : ["systemctl", "--user", ...args];
|
|
18888
|
-
}
|
|
18889
|
-
function pidFile(stateDir) {
|
|
18890
|
-
return join2(stateDir, "gateway.pid");
|
|
18891
|
-
}
|
|
18892
|
-
function logFile(stateDir) {
|
|
18893
|
-
return join2(stateDir, "gateway.log");
|
|
18894
|
-
}
|
|
18895
|
-
function defaultStateDir(env2) {
|
|
18896
|
-
const xdg = env2.XDG_STATE_HOME;
|
|
18897
|
-
const base = typeof xdg === "string" && xdg.length > 0 ? xdg : join2(homedir2(), ".local", "state");
|
|
18898
|
-
return join2(base, "omnigateway");
|
|
18899
|
-
}
|
|
18900
|
-
function unitFile(input) {
|
|
18901
|
-
return `[Unit]
|
|
18902
|
-
Description=${input.description ?? "OmniGateway"}
|
|
18903
|
-
After=network-online.target
|
|
18904
|
-
Wants=network-online.target
|
|
18905
|
-
|
|
18906
|
-
[Service]
|
|
18907
|
-
Type=simple
|
|
18908
|
-
WorkingDirectory=${input.root}
|
|
18909
|
-
EnvironmentFile=${join2(input.root, ".env")}
|
|
18910
|
-
ExecStart=${input.bun} ${input.entrypoint}
|
|
18911
|
-
Restart=on-failure
|
|
18912
|
-
RestartSec=2
|
|
18913
|
-
|
|
18914
|
-
[Install]
|
|
18915
|
-
WantedBy=default.target
|
|
18916
|
-
`;
|
|
18917
|
-
}
|
|
18918
|
-
function unitInstalled(deps) {
|
|
18919
|
-
return existsSync3(deps.unitPath);
|
|
18920
|
-
}
|
|
18921
|
-
function readPid(stateDir) {
|
|
18922
|
-
const file2 = pidFile(stateDir);
|
|
18923
|
-
if (!existsSync3(file2))
|
|
18924
|
-
return null;
|
|
18925
|
-
const pid = Number(readFileSync2(file2, "utf8").trim());
|
|
18926
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
18927
|
-
}
|
|
18928
|
-
function livePid(deps) {
|
|
18929
|
-
const pid = readPid(deps.stateDir);
|
|
18930
|
-
if (pid === null)
|
|
18931
|
-
return null;
|
|
18932
|
-
if (deps.alive(pid))
|
|
18933
|
-
return pid;
|
|
18934
|
-
rmSync(pidFile(deps.stateDir), { force: true });
|
|
18935
|
-
return null;
|
|
18936
|
-
}
|
|
18937
|
-
async function status(deps) {
|
|
18938
|
-
if (unitInstalled(deps)) {
|
|
18939
|
-
const active = await deps.run(systemctlArgs(deps.scope, ["is-active", UNIT_NAME]));
|
|
18940
|
-
const state2 = active.stdout.trim() || active.stderr.trim() || "unknown";
|
|
18941
|
-
const shown = await deps.run(systemctlArgs(deps.scope, ["show", UNIT_NAME, "--property=MainPID", "--value"]));
|
|
18942
|
-
const pid2 = Number(shown.stdout.trim());
|
|
18943
|
-
return {
|
|
18944
|
-
supervisor: "systemd",
|
|
18945
|
-
running: state2 === "active",
|
|
18946
|
-
pid: Number.isInteger(pid2) && pid2 > 0 ? pid2 : null,
|
|
18947
|
-
state: state2,
|
|
18948
|
-
unitPath: deps.unitPath,
|
|
18949
|
-
logFile: null
|
|
18950
|
-
};
|
|
18951
|
-
}
|
|
18952
|
-
const pid = livePid(deps);
|
|
18953
|
-
return {
|
|
18954
|
-
supervisor: pid === null ? "none" : "pidfile",
|
|
18955
|
-
running: pid !== null,
|
|
18956
|
-
pid,
|
|
18957
|
-
state: null,
|
|
18958
|
-
unitPath: null,
|
|
18959
|
-
logFile: logFile(deps.stateDir)
|
|
18960
|
-
};
|
|
18961
|
-
}
|
|
18962
|
-
async function start(deps, input) {
|
|
18963
|
-
if (unitInstalled(deps)) {
|
|
18964
|
-
const result = await deps.run(systemctlArgs(deps.scope, ["start", UNIT_NAME]));
|
|
18965
|
-
if (result.code !== 0) {
|
|
18966
|
-
throw new Error(result.stderr.trim() || `systemctl start exited ${result.code}`);
|
|
18967
|
-
}
|
|
18968
|
-
return {
|
|
18969
|
-
supervisor: "systemd",
|
|
18970
|
-
pid: null,
|
|
18971
|
-
healthy: await waitForHealth(deps, input.baseUrl)
|
|
18972
|
-
};
|
|
18973
|
-
}
|
|
18974
|
-
const running = livePid(deps);
|
|
18975
|
-
if (running !== null) {
|
|
18976
|
-
return {
|
|
18977
|
-
supervisor: "pidfile",
|
|
18978
|
-
pid: running,
|
|
18979
|
-
healthy: await waitForHealth(deps, input.baseUrl)
|
|
18980
|
-
};
|
|
18981
|
-
}
|
|
18982
|
-
mkdirSync(deps.stateDir, { recursive: true });
|
|
18983
|
-
const pid = deps.spawn({
|
|
18984
|
-
argv: input.argv,
|
|
18985
|
-
cwd: deps.root,
|
|
18986
|
-
env: input.env,
|
|
18987
|
-
logFile: logFile(deps.stateDir)
|
|
18988
|
-
});
|
|
18989
|
-
writeFileSync(pidFile(deps.stateDir), `${pid}
|
|
18990
|
-
`);
|
|
18991
|
-
return { supervisor: "pidfile", pid, healthy: await waitForHealth(deps, input.baseUrl) };
|
|
18992
|
-
}
|
|
18993
|
-
var HEALTH_TIMEOUT_MS = 1e4;
|
|
18994
|
-
var HEALTH_INTERVAL_MS = 200;
|
|
18995
|
-
async function waitForHealth(deps, baseUrl) {
|
|
18996
|
-
const attempts = Math.ceil(HEALTH_TIMEOUT_MS / HEALTH_INTERVAL_MS);
|
|
18997
|
-
for (let attempt = 0;attempt < attempts; attempt++) {
|
|
18998
|
-
if (await deps.probe(baseUrl))
|
|
18999
|
-
return true;
|
|
19000
|
-
await deps.sleep(HEALTH_INTERVAL_MS);
|
|
19001
|
-
}
|
|
19002
|
-
return false;
|
|
19003
|
-
}
|
|
19004
|
-
var STOP_GRACE_MS = 5000;
|
|
19005
|
-
var STOP_INTERVAL_MS = 100;
|
|
19006
|
-
async function stop(deps) {
|
|
19007
|
-
if (unitInstalled(deps)) {
|
|
19008
|
-
const result = await deps.run(systemctlArgs(deps.scope, ["stop", UNIT_NAME]));
|
|
19009
|
-
if (result.code !== 0) {
|
|
19010
|
-
throw new Error(result.stderr.trim() || `systemctl stop exited ${result.code}`);
|
|
19011
|
-
}
|
|
19012
|
-
return { supervisor: "systemd", stopped: true };
|
|
19013
|
-
}
|
|
19014
|
-
const pid = livePid(deps);
|
|
19015
|
-
if (pid === null)
|
|
19016
|
-
return { supervisor: "none", stopped: false };
|
|
19017
|
-
deps.kill(pid, "SIGTERM");
|
|
19018
|
-
const attempts = Math.ceil(STOP_GRACE_MS / STOP_INTERVAL_MS);
|
|
19019
|
-
for (let attempt = 0;deps.alive(pid); attempt++) {
|
|
19020
|
-
if (attempt >= attempts) {
|
|
19021
|
-
deps.kill(pid, "SIGKILL");
|
|
19022
|
-
break;
|
|
19023
|
-
}
|
|
19024
|
-
await deps.sleep(STOP_INTERVAL_MS);
|
|
19025
|
-
}
|
|
19026
|
-
rmSync(pidFile(deps.stateDir), { force: true });
|
|
19027
|
-
return { supervisor: "pidfile", stopped: true };
|
|
19028
|
-
}
|
|
19029
|
-
async function install(deps, input) {
|
|
19030
|
-
const path = deps.unitPath;
|
|
19031
|
-
if (existsSync3(path) && !input.force) {
|
|
19032
|
-
throw new Error(`${path} already exists; pass --force to replace it`);
|
|
19033
|
-
}
|
|
19034
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
19035
|
-
writeFileSync(path, unitFile({ root: deps.root, bun: input.bun, entrypoint: input.entrypoint }));
|
|
19036
|
-
const reload = await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
|
|
19037
|
-
let enabled = false;
|
|
19038
|
-
if (input.enable) {
|
|
19039
|
-
const result = await deps.run(systemctlArgs(deps.scope, ["enable", UNIT_NAME]));
|
|
19040
|
-
enabled = result.code === 0;
|
|
19041
|
-
}
|
|
19042
|
-
return { path, reloaded: reload.code === 0, enabled };
|
|
19043
|
-
}
|
|
19044
|
-
async function uninstall(deps) {
|
|
19045
|
-
const path = deps.unitPath;
|
|
19046
|
-
if (!existsSync3(path))
|
|
19047
|
-
return { path, removed: false };
|
|
19048
|
-
await deps.run(systemctlArgs(deps.scope, ["disable", "--now", UNIT_NAME]));
|
|
19049
|
-
rmSync(path, { force: true });
|
|
19050
|
-
await deps.run(systemctlArgs(deps.scope, ["daemon-reload"]));
|
|
19051
|
-
return { path, removed: true };
|
|
19052
|
-
}
|
|
19053
|
-
async function serviceLogs(deps, lines) {
|
|
19054
|
-
if (unitInstalled(deps)) {
|
|
19055
|
-
const args = deps.scope === "system" ? [] : ["--user"];
|
|
19056
|
-
const result = await deps.run([
|
|
19057
|
-
"journalctl",
|
|
19058
|
-
...args,
|
|
19059
|
-
"-u",
|
|
19060
|
-
UNIT_NAME,
|
|
19061
|
-
"-n",
|
|
19062
|
-
String(lines),
|
|
19063
|
-
"--no-pager"
|
|
19064
|
-
]);
|
|
19065
|
-
return result.stdout.trimEnd();
|
|
19066
|
-
}
|
|
19067
|
-
const file2 = logFile(deps.stateDir);
|
|
19068
|
-
if (!existsSync3(file2))
|
|
19069
|
-
return "";
|
|
19070
|
-
return readFileSync2(file2, "utf8").split(`
|
|
19071
|
-
`).slice(-lines).join(`
|
|
19072
|
-
`).trimEnd();
|
|
19073
|
-
}
|
|
19074
|
-
|
|
19075
|
-
// apps/cli/src/runtime.ts
|
|
19076
19451
|
var runCommand = async (argv) => {
|
|
19077
19452
|
const proc = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe" });
|
|
19078
19453
|
const [stdout, stderr, code] = await Promise.all([
|
|
@@ -19083,7 +19458,7 @@ var runCommand = async (argv) => {
|
|
|
19083
19458
|
return { code, stdout, stderr };
|
|
19084
19459
|
};
|
|
19085
19460
|
var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
|
|
19086
|
-
const fd =
|
|
19461
|
+
const fd = openSync2(logFile2, "a");
|
|
19087
19462
|
try {
|
|
19088
19463
|
const proc = Bun.spawn([...argv], {
|
|
19089
19464
|
cwd,
|
|
@@ -19095,7 +19470,7 @@ var spawnDetached = ({ argv, cwd, env: env2, logFile: logFile2 }) => {
|
|
|
19095
19470
|
proc.unref();
|
|
19096
19471
|
return proc.pid;
|
|
19097
19472
|
} finally {
|
|
19098
|
-
|
|
19473
|
+
closeSync2(fd);
|
|
19099
19474
|
}
|
|
19100
19475
|
};
|
|
19101
19476
|
async function runForeground(input) {
|
|
@@ -19136,11 +19511,13 @@ function gatewayEntrypoint(root, cliDir = import.meta.dir) {
|
|
|
19136
19511
|
return candidates.find((path) => existsSync4(path)) ?? null;
|
|
19137
19512
|
}
|
|
19138
19513
|
function createServiceDeps(input) {
|
|
19514
|
+
const logFile2 = input.env.OMNI_LOG_FILE?.trim();
|
|
19139
19515
|
return {
|
|
19140
19516
|
root: input.root,
|
|
19141
19517
|
stateDir: defaultStateDir(input.env),
|
|
19142
19518
|
scope: input.scope,
|
|
19143
19519
|
unitPath: unitPath(input.scope),
|
|
19520
|
+
...logFile2 === undefined || logFile2.length === 0 ? {} : { logFile: logFile2 },
|
|
19144
19521
|
run: runCommand,
|
|
19145
19522
|
spawn: spawnDetached,
|
|
19146
19523
|
probe: probeHealth,
|
|
@@ -19183,7 +19560,7 @@ var start2 = {
|
|
|
19183
19560
|
emit(ctx, writer, result, () => {
|
|
19184
19561
|
const how = result.supervisor === "systemd" ? "systemd" : `pid ${result.pid}`;
|
|
19185
19562
|
if (!result.healthy) {
|
|
19186
|
-
const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" :
|
|
19563
|
+
const where = result.supervisor === "systemd" ? "journalctl -u omnigateway.service" : supervisedLogFile(deps);
|
|
19187
19564
|
return `${state(ctx, false, "started but /health never answered")} (${how}); see ${where}`;
|
|
19188
19565
|
}
|
|
19189
19566
|
return `${state(ctx, true, "running")} (${how}) at ${baseUrl}`;
|
|
@@ -19276,7 +19653,8 @@ var doctor = {
|
|
|
19276
19653
|
gatewayEntrypoint: gatewayEntrypoint(deps.root),
|
|
19277
19654
|
unitInstalled: unit,
|
|
19278
19655
|
supervisor: status2.supervisor,
|
|
19279
|
-
running: status2.running
|
|
19656
|
+
running: status2.running,
|
|
19657
|
+
consoleSource: consoleSource(deps).source
|
|
19280
19658
|
};
|
|
19281
19659
|
emit(ctx, writer, checks3, () => {
|
|
19282
19660
|
const ok = (value, text) => state(ctx, value, text);
|
|
@@ -19291,7 +19669,8 @@ var doctor = {
|
|
|
19291
19669
|
["config", checks3.configError === null ? ok(true, "ok") : ok(false, checks3.configError)],
|
|
19292
19670
|
["entrypoint", checks3.gatewayEntrypoint ?? ok(false, "not found")],
|
|
19293
19671
|
["systemd unit", unit ? deps.scope : paint(ctx, "dim", "none")],
|
|
19294
|
-
["gateway", ok(status2.running, status2.running ? "running" : "stopped")]
|
|
19672
|
+
["gateway", ok(status2.running, status2.running ? "running" : "stopped")],
|
|
19673
|
+
["console log", sourceHint(checks3.consoleSource)]
|
|
19295
19674
|
]);
|
|
19296
19675
|
});
|
|
19297
19676
|
}
|
|
@@ -19495,11 +19874,12 @@ var usage = {
|
|
|
19495
19874
|
};
|
|
19496
19875
|
var logs = {
|
|
19497
19876
|
usage: "logs [-n N] [--follow] [--service]",
|
|
19498
|
-
summary: "Show recent requests
|
|
19877
|
+
summary: "Show recent requests (--service is an alias for `omni console`)",
|
|
19499
19878
|
options: {
|
|
19500
19879
|
number: { type: "string", short: "n" },
|
|
19501
19880
|
follow: { type: "boolean" },
|
|
19502
|
-
service: { type: "boolean" }
|
|
19881
|
+
service: { type: "boolean" },
|
|
19882
|
+
system: { type: "boolean" }
|
|
19503
19883
|
},
|
|
19504
19884
|
async run(args, { ctx, writer, service }) {
|
|
19505
19885
|
const limit = numberFlag(args.values, "number") ?? 20;
|
|
@@ -19509,7 +19889,7 @@ var logs = {
|
|
|
19509
19889
|
return;
|
|
19510
19890
|
}
|
|
19511
19891
|
const store = await ctx.store();
|
|
19512
|
-
const
|
|
19892
|
+
const render2 = (rows) => table([
|
|
19513
19893
|
{ header: "AT" },
|
|
19514
19894
|
{ header: "MODEL" },
|
|
19515
19895
|
{ header: "RESOLVED" },
|
|
@@ -19527,7 +19907,7 @@ var logs = {
|
|
|
19527
19907
|
String(row.durationMs)
|
|
19528
19908
|
]));
|
|
19529
19909
|
const first = await recentLogs(store, limit);
|
|
19530
|
-
emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" :
|
|
19910
|
+
emit(ctx, writer, { logs: first }, () => first.length === 0 ? "no requests logged yet" : render2(first));
|
|
19531
19911
|
if (args.values.follow !== true || ctx.json)
|
|
19532
19912
|
return;
|
|
19533
19913
|
let seen = first[0]?.at ?? 0;
|
|
@@ -19537,7 +19917,7 @@ var logs = {
|
|
|
19537
19917
|
if (next.length === 0)
|
|
19538
19918
|
continue;
|
|
19539
19919
|
seen = next[0]?.at ?? seen;
|
|
19540
|
-
writer.out(
|
|
19920
|
+
writer.out(render2(next.reverse()));
|
|
19541
19921
|
}
|
|
19542
19922
|
}
|
|
19543
19923
|
};
|
|
@@ -19550,6 +19930,7 @@ var COMMANDS = {
|
|
|
19550
19930
|
restart,
|
|
19551
19931
|
doctor,
|
|
19552
19932
|
logs,
|
|
19933
|
+
console: console_,
|
|
19553
19934
|
usage,
|
|
19554
19935
|
connect,
|
|
19555
19936
|
"service install": serviceInstall,
|
|
@@ -19612,7 +19993,10 @@ function resolveCommand(argv) {
|
|
|
19612
19993
|
|
|
19613
19994
|
// apps/cli/src/help.ts
|
|
19614
19995
|
var GROUPS = [
|
|
19615
|
-
{
|
|
19996
|
+
{
|
|
19997
|
+
title: "Gateway",
|
|
19998
|
+
prefixes: ["status", "start", "stop", "restart", "doctor", "logs", "console"]
|
|
19999
|
+
},
|
|
19616
20000
|
{ title: "Service", prefixes: ["service "] },
|
|
19617
20001
|
{ title: "Accounts", prefixes: ["connect", "credentials "] },
|
|
19618
20002
|
{ title: "Models", prefixes: ["models "] },
|