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/gateway.js
CHANGED
|
@@ -535,7 +535,7 @@ function flushChunk(parts, chunk) {
|
|
|
535
535
|
}
|
|
536
536
|
function pushCodeUnit(parts, chunk, codeUnit) {
|
|
537
537
|
chunk.push(codeUnit);
|
|
538
|
-
if (chunk.length >=
|
|
538
|
+
if (chunk.length >= CHUNK2)
|
|
539
539
|
flushChunk(parts, chunk);
|
|
540
540
|
}
|
|
541
541
|
function pushCodePoint(parts, chunk, cp) {
|
|
@@ -660,8 +660,8 @@ function decodeUTF16LE(bytes) {
|
|
|
660
660
|
}
|
|
661
661
|
function decodeASCII(bytes) {
|
|
662
662
|
const parts = [];
|
|
663
|
-
for (let i = 0;i < bytes.length; i +=
|
|
664
|
-
const end = Math.min(bytes.length, i +
|
|
663
|
+
for (let i = 0;i < bytes.length; i += CHUNK2) {
|
|
664
|
+
const end = Math.min(bytes.length, i + CHUNK2);
|
|
665
665
|
const codes = new Array(end - i);
|
|
666
666
|
for (let j = i, k2 = 0;j < end; j++, k2++) {
|
|
667
667
|
codes[k2] = bytes[j] & 127;
|
|
@@ -672,8 +672,8 @@ function decodeASCII(bytes) {
|
|
|
672
672
|
}
|
|
673
673
|
function decodeLatin1(bytes) {
|
|
674
674
|
const parts = [];
|
|
675
|
-
for (let i = 0;i < bytes.length; i +=
|
|
676
|
-
const end = Math.min(bytes.length, i +
|
|
675
|
+
for (let i = 0;i < bytes.length; i += CHUNK2) {
|
|
676
|
+
const end = Math.min(bytes.length, i + CHUNK2);
|
|
677
677
|
const codes = new Array(end - i);
|
|
678
678
|
for (let j = i, k2 = 0;j < end; j++, k2++) {
|
|
679
679
|
codes[k2] = bytes[j];
|
|
@@ -689,7 +689,7 @@ function decodeWindows1252(bytes) {
|
|
|
689
689
|
const b = bytes[i];
|
|
690
690
|
const extra = b >= 128 && b <= 159 ? WINDOWS_1252_EXTRA[b] : undefined;
|
|
691
691
|
out += extra !== null && extra !== undefined ? extra : String.fromCharCode(b);
|
|
692
|
-
if (out.length >=
|
|
692
|
+
if (out.length >= CHUNK2) {
|
|
693
693
|
parts.push(out);
|
|
694
694
|
out = "";
|
|
695
695
|
}
|
|
@@ -698,7 +698,7 @@ function decodeWindows1252(bytes) {
|
|
|
698
698
|
parts.push(out);
|
|
699
699
|
return parts.join("");
|
|
700
700
|
}
|
|
701
|
-
var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder,
|
|
701
|
+
var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder, CHUNK2, REPLACEMENT = 65533;
|
|
702
702
|
var init_lib = __esm(() => {
|
|
703
703
|
WINDOWS_1252_EXTRA = {
|
|
704
704
|
128: "\u20AC",
|
|
@@ -733,7 +733,7 @@ var init_lib = __esm(() => {
|
|
|
733
733
|
for (const [code, char] of Object.entries(WINDOWS_1252_EXTRA)) {
|
|
734
734
|
WINDOWS_1252_REVERSE[char] = Number.parseInt(code, 10);
|
|
735
735
|
}
|
|
736
|
-
|
|
736
|
+
CHUNK2 = 32 * 1024;
|
|
737
737
|
});
|
|
738
738
|
|
|
739
739
|
// node_modules/.bun/token-types@6.1.2/node_modules/token-types/lib/index.js
|
|
@@ -5414,6 +5414,23 @@ function formatLine(level, at, msg, fields, color) {
|
|
|
5414
5414
|
}
|
|
5415
5415
|
return parts.length === 0 ? head : `${head} ${parts.join(" ")}`;
|
|
5416
5416
|
}
|
|
5417
|
+
var ANSI = /\u001B\[[0-9;]*m/g;
|
|
5418
|
+
var HEAD = /^(\S+) (DEBUG|INFO|WARN|ERROR) {1,2}(.*)$/;
|
|
5419
|
+
var FIELDS = / {2}(?=[a-zA-Z]+=)/;
|
|
5420
|
+
function parseLine(raw) {
|
|
5421
|
+
const absent = { raw, at: null, level: null, msg: null };
|
|
5422
|
+
const match = HEAD.exec(raw.replace(ANSI, ""));
|
|
5423
|
+
if (match === null)
|
|
5424
|
+
return absent;
|
|
5425
|
+
const [, instant, label, tail] = match;
|
|
5426
|
+
if (instant === undefined || label === undefined || tail === undefined)
|
|
5427
|
+
return absent;
|
|
5428
|
+
const at = Date.parse(instant);
|
|
5429
|
+
if (Number.isNaN(at))
|
|
5430
|
+
return absent;
|
|
5431
|
+
const msg = tail.split(FIELDS, 1)[0] ?? tail;
|
|
5432
|
+
return { raw, at, level: label.toLowerCase(), msg };
|
|
5433
|
+
}
|
|
5417
5434
|
function createLogger(opts) {
|
|
5418
5435
|
const threshold = LEVELS[opts.level];
|
|
5419
5436
|
const now = opts.now ?? (() => Date.now());
|
|
@@ -5491,7 +5508,7 @@ function collect(events) {
|
|
|
5491
5508
|
else if (ev.delta.type === "thinking" && acc.kind === "thinking")
|
|
5492
5509
|
acc.text += ev.delta.text;
|
|
5493
5510
|
else if (ev.delta.type === "thinkingSignature" && acc.kind === "thinking")
|
|
5494
|
-
acc.signature = ev.delta.signature;
|
|
5511
|
+
acc.signature = (acc.signature ?? "") + ev.delta.signature;
|
|
5495
5512
|
else if (ev.delta.type === "toolJson" && acc.kind === "toolUse")
|
|
5496
5513
|
acc.json += ev.delta.partial;
|
|
5497
5514
|
break;
|
|
@@ -5526,6 +5543,54 @@ function parseJson(raw) {
|
|
|
5526
5543
|
return {};
|
|
5527
5544
|
}
|
|
5528
5545
|
}
|
|
5546
|
+
// packages/ir/src/tokens.ts
|
|
5547
|
+
var CHARS_PER_TOKEN = 4;
|
|
5548
|
+
var IMAGE_TOKENS = 1600;
|
|
5549
|
+
var BLOCK_OVERHEAD = 4;
|
|
5550
|
+
var MESSAGE_OVERHEAD = 4;
|
|
5551
|
+
function fromText(text) {
|
|
5552
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
5553
|
+
}
|
|
5554
|
+
function blockTokens(block) {
|
|
5555
|
+
switch (block.type) {
|
|
5556
|
+
case "text":
|
|
5557
|
+
return BLOCK_OVERHEAD + fromText(block.text);
|
|
5558
|
+
case "image":
|
|
5559
|
+
return BLOCK_OVERHEAD + IMAGE_TOKENS;
|
|
5560
|
+
case "thinking":
|
|
5561
|
+
return BLOCK_OVERHEAD + fromText(block.text);
|
|
5562
|
+
case "toolUse":
|
|
5563
|
+
return BLOCK_OVERHEAD + fromText(block.name) + fromText(safeJson(block.input));
|
|
5564
|
+
case "toolResult":
|
|
5565
|
+
return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content);
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
function messageTokens(message) {
|
|
5569
|
+
let total = MESSAGE_OVERHEAD;
|
|
5570
|
+
for (const block of message.content)
|
|
5571
|
+
total += blockTokens(block);
|
|
5572
|
+
return total;
|
|
5573
|
+
}
|
|
5574
|
+
function toolTokens(tool) {
|
|
5575
|
+
return BLOCK_OVERHEAD + fromText(tool.name) + fromText(tool.description ?? "") + fromText(safeJson(tool.inputSchema));
|
|
5576
|
+
}
|
|
5577
|
+
function safeJson(value) {
|
|
5578
|
+
try {
|
|
5579
|
+
return JSON.stringify(value) ?? "";
|
|
5580
|
+
} catch {
|
|
5581
|
+
return "";
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
function estimateInputTokens(request) {
|
|
5585
|
+
let total = 0;
|
|
5586
|
+
for (const block of request.system ?? [])
|
|
5587
|
+
total += blockTokens(block);
|
|
5588
|
+
for (const message of request.messages)
|
|
5589
|
+
total += messageTokens(message);
|
|
5590
|
+
for (const tool of request.tools ?? [])
|
|
5591
|
+
total += toolTokens(tool);
|
|
5592
|
+
return total;
|
|
5593
|
+
}
|
|
5529
5594
|
// packages/ir/src/validate.ts
|
|
5530
5595
|
function validateRequest(req) {
|
|
5531
5596
|
const seenToolUseIds = new Set;
|
|
@@ -5556,6 +5621,7 @@ function validateRequest(req) {
|
|
|
5556
5621
|
}
|
|
5557
5622
|
// packages/control/src/config.ts
|
|
5558
5623
|
var MIN_KEY_LENGTH = 16;
|
|
5624
|
+
var TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
5559
5625
|
var DECIMAL_INTEGER = /^\d+$/;
|
|
5560
5626
|
function optionalText(value, fallback) {
|
|
5561
5627
|
return value?.trim() || fallback;
|
|
@@ -5574,6 +5640,8 @@ function loadConfig(env) {
|
|
|
5574
5640
|
const derivedBaseUrl = `http://${host}:${port}`;
|
|
5575
5641
|
const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
|
|
5576
5642
|
const staticDir = env.OMNI_STATIC_DIR?.trim();
|
|
5643
|
+
const logFile = env.OMNI_LOG_FILE?.trim();
|
|
5644
|
+
const exposeClaudeCodeAliases = TRUTHY.has((env.OMNI_EXPOSE_CLAUDE_CODE_ALIASES ?? "").trim().toLowerCase());
|
|
5577
5645
|
const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
|
|
5578
5646
|
const logLevel = parseLogLevel(rawLogLevel);
|
|
5579
5647
|
return {
|
|
@@ -5584,9 +5652,15 @@ function loadConfig(env) {
|
|
|
5584
5652
|
databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
|
|
5585
5653
|
encryptionKey,
|
|
5586
5654
|
baseUrl,
|
|
5587
|
-
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
|
|
5655
|
+
staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
|
|
5656
|
+
exposeClaudeCodeAliases,
|
|
5657
|
+
logFile: logFile === undefined || logFile.length === 0 ? null : logFile
|
|
5588
5658
|
};
|
|
5589
5659
|
}
|
|
5660
|
+
// packages/providers/src/betas.ts
|
|
5661
|
+
var CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
5662
|
+
var CONTEXT_1M_TOKENS = 1e6;
|
|
5663
|
+
|
|
5590
5664
|
// packages/providers/src/body.ts
|
|
5591
5665
|
var BODY_ORDER = {
|
|
5592
5666
|
anthropic: [
|
|
@@ -5694,6 +5768,119 @@ function signAnthropicBody(json) {
|
|
|
5694
5768
|
return json.replace(needle, `cch=${computeCch(json)};`);
|
|
5695
5769
|
}
|
|
5696
5770
|
|
|
5771
|
+
// packages/providers/src/anthropic/models.ts
|
|
5772
|
+
var ANTHROPIC_MODELS = {
|
|
5773
|
+
defaultModel: "claude-opus-5",
|
|
5774
|
+
models: [
|
|
5775
|
+
{
|
|
5776
|
+
id: "claude-fable-5",
|
|
5777
|
+
label: "Claude Fable 5",
|
|
5778
|
+
pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
|
|
5779
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
5780
|
+
},
|
|
5781
|
+
{
|
|
5782
|
+
id: "claude-opus-5",
|
|
5783
|
+
label: "Claude Opus 5",
|
|
5784
|
+
pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
|
|
5785
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
5786
|
+
},
|
|
5787
|
+
{
|
|
5788
|
+
id: "claude-sonnet-5",
|
|
5789
|
+
label: "Claude Sonnet 5",
|
|
5790
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
|
|
5791
|
+
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
5792
|
+
},
|
|
5793
|
+
{
|
|
5794
|
+
id: "claude-haiku-4-5",
|
|
5795
|
+
label: "Claude Haiku 4.5",
|
|
5796
|
+
pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
|
|
5797
|
+
limits: { contextWindow: 200000, maxOutputTokens: 64000 }
|
|
5798
|
+
}
|
|
5799
|
+
]
|
|
5800
|
+
};
|
|
5801
|
+
|
|
5802
|
+
// packages/providers/src/kimi/models.ts
|
|
5803
|
+
var KIMI_MODELS = {
|
|
5804
|
+
defaultModel: "k3-256k",
|
|
5805
|
+
models: [
|
|
5806
|
+
{
|
|
5807
|
+
id: "k3-256k",
|
|
5808
|
+
label: "Kimi K3 \u2014 256K",
|
|
5809
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5810
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
5811
|
+
},
|
|
5812
|
+
{
|
|
5813
|
+
id: "k3",
|
|
5814
|
+
label: "Kimi K3 \u2014 up to 1M",
|
|
5815
|
+
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5816
|
+
limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
|
|
5817
|
+
},
|
|
5818
|
+
{
|
|
5819
|
+
id: "kimi-for-coding",
|
|
5820
|
+
label: "Kimi K2.7 Code",
|
|
5821
|
+
pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5822
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
5823
|
+
},
|
|
5824
|
+
{
|
|
5825
|
+
id: "kimi-for-coding-highspeed",
|
|
5826
|
+
label: "Kimi K2.7 Code \u2014 High Speed",
|
|
5827
|
+
pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5828
|
+
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
5829
|
+
}
|
|
5830
|
+
]
|
|
5831
|
+
};
|
|
5832
|
+
|
|
5833
|
+
// packages/providers/src/openai/models.ts
|
|
5834
|
+
var OPENAI_MODELS = {
|
|
5835
|
+
defaultModel: "gpt-5.6",
|
|
5836
|
+
models: [
|
|
5837
|
+
{
|
|
5838
|
+
id: "gpt-5.6",
|
|
5839
|
+
label: "GPT-5.6 \u2014 routes to Sol",
|
|
5840
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5841
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
5842
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
5843
|
+
},
|
|
5844
|
+
{
|
|
5845
|
+
id: "gpt-5.6-sol",
|
|
5846
|
+
label: "GPT-5.6 Sol \u2014 deepest reasoning",
|
|
5847
|
+
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5848
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
5849
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
5850
|
+
},
|
|
5851
|
+
{
|
|
5852
|
+
id: "gpt-5.6-terra",
|
|
5853
|
+
label: "GPT-5.6 Terra \u2014 balanced",
|
|
5854
|
+
pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5855
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
5856
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
5857
|
+
},
|
|
5858
|
+
{
|
|
5859
|
+
id: "gpt-5.6-luna",
|
|
5860
|
+
label: "GPT-5.6 Luna \u2014 fastest",
|
|
5861
|
+
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
5862
|
+
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
5863
|
+
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
5864
|
+
}
|
|
5865
|
+
]
|
|
5866
|
+
};
|
|
5867
|
+
|
|
5868
|
+
// packages/providers/src/catalog.ts
|
|
5869
|
+
var PROVIDER_MODEL_CATALOG = {
|
|
5870
|
+
anthropic: ANTHROPIC_MODELS,
|
|
5871
|
+
openai: OPENAI_MODELS,
|
|
5872
|
+
kimi: KIMI_MODELS
|
|
5873
|
+
};
|
|
5874
|
+
function catalogPricing(provider, model) {
|
|
5875
|
+
return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
|
|
5876
|
+
}
|
|
5877
|
+
function catalogLimits(provider, model, auth = "apiKey") {
|
|
5878
|
+
const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
|
|
5879
|
+
if (entry === undefined)
|
|
5880
|
+
return null;
|
|
5881
|
+
return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5697
5884
|
// packages/providers/src/http.ts
|
|
5698
5885
|
function codeForStatus(status) {
|
|
5699
5886
|
if (status === 401 || status === 403)
|
|
@@ -6009,7 +6196,7 @@ async function* decodeAnthropic(messages) {
|
|
|
6009
6196
|
if (cb.type === "text")
|
|
6010
6197
|
yield { type: "blockStart", index, block: { type: "text" } };
|
|
6011
6198
|
else if (cb.type === "thinking")
|
|
6012
|
-
yield { type: "blockStart", index, block: { type: "thinking" } };
|
|
6199
|
+
yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
|
|
6013
6200
|
else if (cb.type === "tool_use")
|
|
6014
6201
|
yield {
|
|
6015
6202
|
type: "blockStart",
|
|
@@ -6029,7 +6216,7 @@ async function* decodeAnthropic(messages) {
|
|
|
6029
6216
|
index,
|
|
6030
6217
|
delta: { type: "thinking", text: delta.thinking ?? "" }
|
|
6031
6218
|
};
|
|
6032
|
-
else if (delta.type === "signature_delta")
|
|
6219
|
+
else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
|
|
6033
6220
|
yield {
|
|
6034
6221
|
type: "blockDelta",
|
|
6035
6222
|
index,
|
|
@@ -6101,6 +6288,9 @@ function wireCacheControl(c) {
|
|
|
6101
6288
|
return {};
|
|
6102
6289
|
return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
|
|
6103
6290
|
}
|
|
6291
|
+
function isReplayableThinking(b) {
|
|
6292
|
+
return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
|
|
6293
|
+
}
|
|
6104
6294
|
function encodeBlock(b) {
|
|
6105
6295
|
const cache = wireCacheControl(cacheControlOf(b));
|
|
6106
6296
|
switch (b.type) {
|
|
@@ -6130,6 +6320,16 @@ function encodeSystemTurn(content) {
|
|
|
6130
6320
|
return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
|
|
6131
6321
|
`);
|
|
6132
6322
|
}
|
|
6323
|
+
function systemCacheControl(req) {
|
|
6324
|
+
const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
|
|
6325
|
+
const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
|
|
6326
|
+
const final = markedSystemBlocks.at(-1);
|
|
6327
|
+
const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
|
|
6328
|
+
return {
|
|
6329
|
+
...promoted === undefined ? {} : { promoted },
|
|
6330
|
+
lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
|
|
6331
|
+
};
|
|
6332
|
+
}
|
|
6133
6333
|
function encodeToolChoice(c) {
|
|
6134
6334
|
switch (c.type) {
|
|
6135
6335
|
case "auto":
|
|
@@ -6153,18 +6353,30 @@ function toWire(req, model, opts) {
|
|
|
6153
6353
|
system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
|
|
6154
6354
|
note("anthropic:oauth-system-prefix");
|
|
6155
6355
|
}
|
|
6356
|
+
const systemCache = systemCacheControl(req);
|
|
6357
|
+
if (systemCache.lost)
|
|
6358
|
+
note("anthropic:system-turn-cache-control-dropped");
|
|
6156
6359
|
const body = {
|
|
6157
6360
|
model,
|
|
6158
|
-
messages: req.messages.
|
|
6159
|
-
if (m.role !== "system")
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6361
|
+
messages: req.messages.flatMap((m) => {
|
|
6362
|
+
if (m.role !== "system") {
|
|
6363
|
+
const replayable = m.content.filter(isReplayableThinking);
|
|
6364
|
+
if (replayable.length !== m.content.length)
|
|
6365
|
+
note("anthropic:unsigned-thinking-dropped");
|
|
6366
|
+
if (replayable.length === 0)
|
|
6367
|
+
return [];
|
|
6368
|
+
return [{ role: m.role, content: replayable.map(encodeBlock) }];
|
|
6369
|
+
}
|
|
6370
|
+
return [{ role: m.role, content: encodeSystemTurn(m.content) }];
|
|
6165
6371
|
}),
|
|
6166
6372
|
max_tokens: req.maxTokens ?? 4096,
|
|
6167
|
-
stream: req.stream
|
|
6373
|
+
stream: req.stream,
|
|
6374
|
+
...systemCache.promoted === undefined ? {} : {
|
|
6375
|
+
cache_control: {
|
|
6376
|
+
type: systemCache.promoted.type,
|
|
6377
|
+
...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6168
6380
|
};
|
|
6169
6381
|
if (system !== undefined && system.length > 0)
|
|
6170
6382
|
body.system = system;
|
|
@@ -6226,6 +6438,15 @@ var anthropicAdapter = {
|
|
|
6226
6438
|
["Accept", "text/event-stream"]
|
|
6227
6439
|
];
|
|
6228
6440
|
const betas = new Set(req.request.betas ?? []);
|
|
6441
|
+
const notes = [...degradations];
|
|
6442
|
+
if (betas.has(CONTEXT_1M_BETA)) {
|
|
6443
|
+
const limits = catalogLimits("anthropic", req.model, oauth ? "oauth" : "apiKey");
|
|
6444
|
+
const window2 = limits?.contextWindow;
|
|
6445
|
+
if (window2 !== undefined && window2 < CONTEXT_1M_TOKENS) {
|
|
6446
|
+
betas.delete(CONTEXT_1M_BETA);
|
|
6447
|
+
notes.push("anthropic:context-1m-dropped");
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6229
6450
|
if (oauth) {
|
|
6230
6451
|
protocol.push(["Authorization", `Bearer ${req.credentials.accessToken}`]);
|
|
6231
6452
|
betas.add(OAUTH_BETA);
|
|
@@ -6253,121 +6474,9 @@ var anthropicAdapter = {
|
|
|
6253
6474
|
throw await httpError(res, "anthropic");
|
|
6254
6475
|
if (res.body === null)
|
|
6255
6476
|
throw new GatewayError("UPSTREAM", "empty response body", { provider: "anthropic" });
|
|
6256
|
-
return { events: decodeAnthropic(parseSse(res.body)), degradations };
|
|
6477
|
+
return { events: decodeAnthropic(parseSse(res.body)), degradations: notes };
|
|
6257
6478
|
}
|
|
6258
6479
|
};
|
|
6259
|
-
// packages/providers/src/anthropic/models.ts
|
|
6260
|
-
var ANTHROPIC_MODELS = {
|
|
6261
|
-
defaultModel: "claude-opus-5",
|
|
6262
|
-
models: [
|
|
6263
|
-
{
|
|
6264
|
-
id: "claude-fable-5",
|
|
6265
|
-
label: "Claude Fable 5",
|
|
6266
|
-
pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
|
|
6267
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
6268
|
-
},
|
|
6269
|
-
{
|
|
6270
|
-
id: "claude-opus-5",
|
|
6271
|
-
label: "Claude Opus 5",
|
|
6272
|
-
pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
|
|
6273
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
6274
|
-
},
|
|
6275
|
-
{
|
|
6276
|
-
id: "claude-sonnet-5",
|
|
6277
|
-
label: "Claude Sonnet 5",
|
|
6278
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
|
|
6279
|
-
limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
|
|
6280
|
-
},
|
|
6281
|
-
{
|
|
6282
|
-
id: "claude-haiku-4-5",
|
|
6283
|
-
label: "Claude Haiku 4.5",
|
|
6284
|
-
pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
|
|
6285
|
-
limits: { contextWindow: 200000, maxOutputTokens: 64000 }
|
|
6286
|
-
}
|
|
6287
|
-
]
|
|
6288
|
-
};
|
|
6289
|
-
|
|
6290
|
-
// packages/providers/src/kimi/models.ts
|
|
6291
|
-
var KIMI_MODELS = {
|
|
6292
|
-
defaultModel: "k3-256k",
|
|
6293
|
-
models: [
|
|
6294
|
-
{
|
|
6295
|
-
id: "k3-256k",
|
|
6296
|
-
label: "Kimi K3 \u2014 256K",
|
|
6297
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6298
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
6299
|
-
},
|
|
6300
|
-
{
|
|
6301
|
-
id: "k3",
|
|
6302
|
-
label: "Kimi K3 \u2014 up to 1M",
|
|
6303
|
-
pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6304
|
-
limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
|
|
6305
|
-
},
|
|
6306
|
-
{
|
|
6307
|
-
id: "kimi-for-coding",
|
|
6308
|
-
label: "Kimi K2.7 Code",
|
|
6309
|
-
pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6310
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
6311
|
-
},
|
|
6312
|
-
{
|
|
6313
|
-
id: "kimi-for-coding-highspeed",
|
|
6314
|
-
label: "Kimi K2.7 Code \u2014 High Speed",
|
|
6315
|
-
pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6316
|
-
limits: { contextWindow: 262144, maxOutputTokens: 131072 }
|
|
6317
|
-
}
|
|
6318
|
-
]
|
|
6319
|
-
};
|
|
6320
|
-
|
|
6321
|
-
// packages/providers/src/openai/models.ts
|
|
6322
|
-
var OPENAI_MODELS = {
|
|
6323
|
-
defaultModel: "gpt-5.6",
|
|
6324
|
-
models: [
|
|
6325
|
-
{
|
|
6326
|
-
id: "gpt-5.6",
|
|
6327
|
-
label: "GPT-5.6 \u2014 routes to Sol",
|
|
6328
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6329
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
6330
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
6331
|
-
},
|
|
6332
|
-
{
|
|
6333
|
-
id: "gpt-5.6-sol",
|
|
6334
|
-
label: "GPT-5.6 Sol \u2014 deepest reasoning",
|
|
6335
|
-
pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6336
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
6337
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
6338
|
-
},
|
|
6339
|
-
{
|
|
6340
|
-
id: "gpt-5.6-terra",
|
|
6341
|
-
label: "GPT-5.6 Terra \u2014 balanced",
|
|
6342
|
-
pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6343
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
6344
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
6345
|
-
},
|
|
6346
|
-
{
|
|
6347
|
-
id: "gpt-5.6-luna",
|
|
6348
|
-
label: "GPT-5.6 Luna \u2014 fastest",
|
|
6349
|
-
pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
6350
|
-
limits: { contextWindow: 922000, maxOutputTokens: 128000 },
|
|
6351
|
-
oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
|
|
6352
|
-
}
|
|
6353
|
-
]
|
|
6354
|
-
};
|
|
6355
|
-
|
|
6356
|
-
// packages/providers/src/catalog.ts
|
|
6357
|
-
var PROVIDER_MODEL_CATALOG = {
|
|
6358
|
-
anthropic: ANTHROPIC_MODELS,
|
|
6359
|
-
openai: OPENAI_MODELS,
|
|
6360
|
-
kimi: KIMI_MODELS
|
|
6361
|
-
};
|
|
6362
|
-
function catalogPricing(provider, model) {
|
|
6363
|
-
return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
|
|
6364
|
-
}
|
|
6365
|
-
function catalogLimits(provider, model, auth = "apiKey") {
|
|
6366
|
-
const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
|
|
6367
|
-
if (entry === undefined)
|
|
6368
|
-
return null;
|
|
6369
|
-
return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
|
|
6370
|
-
}
|
|
6371
6480
|
// packages/providers/src/http-client.ts
|
|
6372
6481
|
import { request as httpRequest } from "http";
|
|
6373
6482
|
import { request as httpsRequest } from "https";
|
|
@@ -6593,6 +6702,8 @@ function toChatWire(req, model) {
|
|
|
6593
6702
|
if (!degradations.includes(d))
|
|
6594
6703
|
degradations.push(d);
|
|
6595
6704
|
};
|
|
6705
|
+
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
6706
|
+
note("kimi:context-1m-dropped");
|
|
6596
6707
|
const messages = [];
|
|
6597
6708
|
const system = req.system?.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
|
|
6598
6709
|
|
|
@@ -6869,6 +6980,8 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
6869
6980
|
if (!degradations.includes(d))
|
|
6870
6981
|
degradations.push(d);
|
|
6871
6982
|
};
|
|
6983
|
+
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
6984
|
+
note("openai:context-1m-dropped");
|
|
6872
6985
|
for (const message of req.messages) {
|
|
6873
6986
|
const parts = [];
|
|
6874
6987
|
const inlined = message.role === "system";
|
|
@@ -7527,6 +7640,60 @@ function createConnectFlows(deps) {
|
|
|
7527
7640
|
}
|
|
7528
7641
|
};
|
|
7529
7642
|
}
|
|
7643
|
+
// packages/control/src/console.ts
|
|
7644
|
+
var UNIT_NAME = "omnigateway.service";
|
|
7645
|
+
var MAX_CONSOLE_LINES = 500;
|
|
7646
|
+
var DEFAULT_CONSOLE_LINES = 200;
|
|
7647
|
+
function resolveConsoleSource(input) {
|
|
7648
|
+
const path = input.logFile?.trim();
|
|
7649
|
+
if (path !== undefined && path.length > 0)
|
|
7650
|
+
return { kind: "file", path };
|
|
7651
|
+
if (input.unitInstalled)
|
|
7652
|
+
return { kind: "journal", unit: UNIT_NAME, scope: input.scope };
|
|
7653
|
+
return { kind: "none" };
|
|
7654
|
+
}
|
|
7655
|
+
function consoleLimit(requested) {
|
|
7656
|
+
const value = requested === undefined ? DEFAULT_CONSOLE_LINES : Number(requested);
|
|
7657
|
+
if (!Number.isFinite(value))
|
|
7658
|
+
return DEFAULT_CONSOLE_LINES;
|
|
7659
|
+
return Math.floor(Math.min(Math.max(1, value), MAX_CONSOLE_LINES));
|
|
7660
|
+
}
|
|
7661
|
+
function keep(line, query) {
|
|
7662
|
+
if (query.since !== undefined && (line.at === null || line.at <= query.since))
|
|
7663
|
+
return false;
|
|
7664
|
+
if (query.level === undefined || line.level === null)
|
|
7665
|
+
return true;
|
|
7666
|
+
return LEVELS[line.level] >= LEVELS[query.level];
|
|
7667
|
+
}
|
|
7668
|
+
var SCAN_FACTOR = 20;
|
|
7669
|
+
var SCAN_LIMIT = 5000;
|
|
7670
|
+
function scanWidth(query) {
|
|
7671
|
+
const filtered = query.level !== undefined || query.since !== undefined;
|
|
7672
|
+
return filtered ? Math.min(query.lines * SCAN_FACTOR, SCAN_LIMIT) : query.lines;
|
|
7673
|
+
}
|
|
7674
|
+
async function readSource(deps, source, lines) {
|
|
7675
|
+
if (source.kind === "none")
|
|
7676
|
+
return "";
|
|
7677
|
+
if (source.kind === "file")
|
|
7678
|
+
return deps.readFile(source.path, lines) ?? "";
|
|
7679
|
+
const unit = source.scope === "system" ? ["-u", source.unit] : [`--user-unit=${source.unit}`];
|
|
7680
|
+
const result = await deps.run([
|
|
7681
|
+
"journalctl",
|
|
7682
|
+
...unit,
|
|
7683
|
+
"-n",
|
|
7684
|
+
String(lines),
|
|
7685
|
+
"--no-pager",
|
|
7686
|
+
"--output=cat"
|
|
7687
|
+
]);
|
|
7688
|
+
return result.code === 0 ? result.stdout : "";
|
|
7689
|
+
}
|
|
7690
|
+
async function readConsole(deps, source, query) {
|
|
7691
|
+
const limited = { ...query, lines: consoleLimit(query.lines) };
|
|
7692
|
+
const text = await readSource(deps, source, scanWidth(limited));
|
|
7693
|
+
const lines = text.split(`
|
|
7694
|
+
`).filter((raw) => raw.trim().length > 0).map(parseLine).filter((line) => keep(line, limited)).slice(-limited.lines);
|
|
7695
|
+
return source.kind === "file" ? { source: "file", path: source.path, lines } : { source: source.kind, lines };
|
|
7696
|
+
}
|
|
7530
7697
|
// node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
7531
7698
|
var exports_external = {};
|
|
7532
7699
|
__export(exports_external, {
|
|
@@ -21819,7 +21986,9 @@ var dryRunSchema = exports_external.object({
|
|
|
21819
21986
|
reasoning: exports_external.boolean().default(false)
|
|
21820
21987
|
}).strict();
|
|
21821
21988
|
var modelSchema = exports_external.object({
|
|
21822
|
-
id: exports_external.string().min(1),
|
|
21989
|
+
id: exports_external.string().min(1).refine((value) => !value.toLowerCase().startsWith("claude/"), {
|
|
21990
|
+
message: 'model id must not start with "claude/": that prefix is reserved for discovery mirrors'
|
|
21991
|
+
}),
|
|
21823
21992
|
strategy: exports_external.enum(["score", "priority", "roundRobin", "weighted"]),
|
|
21824
21993
|
isAlias: exports_external.boolean(),
|
|
21825
21994
|
targets: exports_external.array(exports_external.object({
|
|
@@ -23273,6 +23442,60 @@ async function createKey(store, input) {
|
|
|
23273
23442
|
async function revokeKey(store, id) {
|
|
23274
23443
|
await store.keys.revoke(id);
|
|
23275
23444
|
}
|
|
23445
|
+
// packages/control/src/modelLimits.ts
|
|
23446
|
+
function narrower(a, b) {
|
|
23447
|
+
const context = [a.contextWindow, b.contextWindow].filter((n) => n !== undefined);
|
|
23448
|
+
const output = [a.maxOutputTokens, b.maxOutputTokens].filter((n) => n !== undefined);
|
|
23449
|
+
return {
|
|
23450
|
+
...context.length === 0 ? {} : { contextWindow: Math.min(...context) },
|
|
23451
|
+
...output.length === 0 ? {} : { maxOutputTokens: Math.min(...output) }
|
|
23452
|
+
};
|
|
23453
|
+
}
|
|
23454
|
+
function targetLimits(target, auths) {
|
|
23455
|
+
const ways = auths.size === 0 ? ["apiKey"] : [...auths];
|
|
23456
|
+
let listed = {};
|
|
23457
|
+
for (const auth of ways) {
|
|
23458
|
+
const entry = catalogLimits(target.provider, target.model, auth);
|
|
23459
|
+
if (entry === null)
|
|
23460
|
+
continue;
|
|
23461
|
+
listed = narrower(listed, {
|
|
23462
|
+
contextWindow: entry.contextWindow,
|
|
23463
|
+
maxOutputTokens: entry.maxOutputTokens
|
|
23464
|
+
});
|
|
23465
|
+
}
|
|
23466
|
+
const contextWindow = target.contextWindow ?? listed.contextWindow;
|
|
23467
|
+
const maxOutputTokens = target.maxOutputTokens ?? listed.maxOutputTokens;
|
|
23468
|
+
return {
|
|
23469
|
+
...contextWindow === undefined ? {} : { contextWindow },
|
|
23470
|
+
...maxOutputTokens === undefined ? {} : { maxOutputTokens }
|
|
23471
|
+
};
|
|
23472
|
+
}
|
|
23473
|
+
function servingAuths(credentials) {
|
|
23474
|
+
const byProvider = new Map;
|
|
23475
|
+
for (const credential of credentials) {
|
|
23476
|
+
if (!credential.enabled)
|
|
23477
|
+
continue;
|
|
23478
|
+
const ways = byProvider.get(credential.provider) ?? new Set;
|
|
23479
|
+
ways.add(credential.authType);
|
|
23480
|
+
byProvider.set(credential.provider, ways);
|
|
23481
|
+
}
|
|
23482
|
+
return byProvider;
|
|
23483
|
+
}
|
|
23484
|
+
function resolveModelLimits(model, credentials) {
|
|
23485
|
+
const auths = servingAuths(credentials);
|
|
23486
|
+
let limits = {};
|
|
23487
|
+
for (const target of model.targets) {
|
|
23488
|
+
limits = narrower(limits, targetLimits(target, auths.get(target.provider) ?? new Set));
|
|
23489
|
+
}
|
|
23490
|
+
return limits;
|
|
23491
|
+
}
|
|
23492
|
+
function modelDisplayName(model) {
|
|
23493
|
+
const only = model.targets.length === 1 ? model.targets[0] : undefined;
|
|
23494
|
+
if (only === undefined)
|
|
23495
|
+
return model.id;
|
|
23496
|
+
const labelled = PROVIDER_MODEL_CATALOG[only.provider]?.models.find((choice) => choice.id === only.model);
|
|
23497
|
+
return labelled?.label ?? model.id;
|
|
23498
|
+
}
|
|
23276
23499
|
// packages/control/src/models.ts
|
|
23277
23500
|
async function listModels(store) {
|
|
23278
23501
|
return store.config.listModels();
|
|
@@ -23689,6 +23912,112 @@ async function putSettings(store, input) {
|
|
|
23689
23912
|
await store.config.putSettings(settings);
|
|
23690
23913
|
return settings;
|
|
23691
23914
|
}
|
|
23915
|
+
// packages/control/src/setup.ts
|
|
23916
|
+
var KEY_PLACEHOLDER = "<your OmniGateway key>";
|
|
23917
|
+
async function describeModelsForSetup(store) {
|
|
23918
|
+
const models = await listModels(store);
|
|
23919
|
+
const credentials = (await listCredentials(store)).map((credential) => ({
|
|
23920
|
+
provider: credential.provider,
|
|
23921
|
+
authType: credential.authType,
|
|
23922
|
+
enabled: credential.enabled
|
|
23923
|
+
}));
|
|
23924
|
+
return models.map((model) => ({
|
|
23925
|
+
model,
|
|
23926
|
+
limits: resolveModelLimits(model, credentials),
|
|
23927
|
+
label: modelDisplayName(model)
|
|
23928
|
+
}));
|
|
23929
|
+
}
|
|
23930
|
+
function slug(id) {
|
|
23931
|
+
return encodeURIComponent(id).replace(/\./g, "%2E");
|
|
23932
|
+
}
|
|
23933
|
+
function claudeProfiles(described, input) {
|
|
23934
|
+
const apiKey = input.apiKey ?? KEY_PLACEHOLDER;
|
|
23935
|
+
return described.map(({ model, limits }) => {
|
|
23936
|
+
const useMirror = input.discoveryMirrors === true && !/^(?:claude|anthropic)/i.test(model.id);
|
|
23937
|
+
const modelId = useMirror ? `claude/${model.id}` : model.id;
|
|
23938
|
+
const env2 = {
|
|
23939
|
+
ANTHROPIC_BASE_URL: input.baseUrl,
|
|
23940
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
23941
|
+
ANTHROPIC_MODEL: modelId,
|
|
23942
|
+
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1"
|
|
23943
|
+
};
|
|
23944
|
+
if (limits.contextWindow !== undefined && !/^claude-/i.test(modelId)) {
|
|
23945
|
+
env2.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(limits.contextWindow);
|
|
23946
|
+
}
|
|
23947
|
+
return {
|
|
23948
|
+
path: `${slug(model.id)}/settings.json`,
|
|
23949
|
+
contents: `${JSON.stringify({ env: env2 }, null, 2)}
|
|
23950
|
+
`
|
|
23951
|
+
};
|
|
23952
|
+
});
|
|
23953
|
+
}
|
|
23954
|
+
function opencodeConfig(described, input) {
|
|
23955
|
+
const models = {};
|
|
23956
|
+
for (const { model, limits, label: label2 } of described) {
|
|
23957
|
+
const limit = limits.contextWindow === undefined ? undefined : {
|
|
23958
|
+
context: limits.contextWindow,
|
|
23959
|
+
...limits.maxOutputTokens === undefined ? {} : { output: limits.maxOutputTokens }
|
|
23960
|
+
};
|
|
23961
|
+
models[model.id] = { name: label2, ...limit === undefined ? {} : { limit } };
|
|
23962
|
+
}
|
|
23963
|
+
const contents = `${JSON.stringify({
|
|
23964
|
+
$schema: "https://opencode.ai/config.json",
|
|
23965
|
+
provider: {
|
|
23966
|
+
omnigateway: {
|
|
23967
|
+
npm: "@ai-sdk/openai-compatible",
|
|
23968
|
+
name: "OmniGateway",
|
|
23969
|
+
options: {
|
|
23970
|
+
baseURL: `${input.baseUrl.replace(/\/+$/, "").replace(/(?:\/v1)+$/i, "")}/v1`,
|
|
23971
|
+
apiKey: input.apiKey ?? KEY_PLACEHOLDER
|
|
23972
|
+
},
|
|
23973
|
+
models
|
|
23974
|
+
}
|
|
23975
|
+
}
|
|
23976
|
+
}, null, 2)}
|
|
23977
|
+
`;
|
|
23978
|
+
return { path: "opencode.json", contents };
|
|
23979
|
+
}
|
|
23980
|
+
async function setupFiles(store, client, input) {
|
|
23981
|
+
const described = await describeModelsForSetup(store);
|
|
23982
|
+
return client === "claude" ? claudeProfiles(described, input) : [opencodeConfig(described, input)];
|
|
23983
|
+
}
|
|
23984
|
+
// packages/control/src/tail.ts
|
|
23985
|
+
import { closeSync, fstatSync, openSync, readSync, statSync } from "fs";
|
|
23986
|
+
var CHUNK = 64 * 1024;
|
|
23987
|
+
var MAX_BYTES = 8 * 1024 * 1024;
|
|
23988
|
+
function tailFile(path, lines) {
|
|
23989
|
+
let fd;
|
|
23990
|
+
try {
|
|
23991
|
+
fd = openSync(path, "r");
|
|
23992
|
+
} catch {
|
|
23993
|
+
return null;
|
|
23994
|
+
}
|
|
23995
|
+
try {
|
|
23996
|
+
const size = fstatSync(fd).size;
|
|
23997
|
+
if (size === 0)
|
|
23998
|
+
return "";
|
|
23999
|
+
const budget = Math.min(size, MAX_BYTES);
|
|
24000
|
+
let read = 0;
|
|
24001
|
+
let newlines = 0;
|
|
24002
|
+
const chunks = [];
|
|
24003
|
+
while (read < budget && newlines <= lines) {
|
|
24004
|
+
const span = Math.min(CHUNK, budget - read);
|
|
24005
|
+
const buffer = Buffer.allocUnsafe(span);
|
|
24006
|
+
const position = size - read - span;
|
|
24007
|
+
readSync(fd, buffer, 0, span, position);
|
|
24008
|
+
chunks.unshift(buffer);
|
|
24009
|
+
read += span;
|
|
24010
|
+
for (const byte of buffer)
|
|
24011
|
+
if (byte === 10)
|
|
24012
|
+
newlines++;
|
|
24013
|
+
}
|
|
24014
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
24015
|
+
} catch {
|
|
24016
|
+
return null;
|
|
24017
|
+
} finally {
|
|
24018
|
+
closeSync(fd);
|
|
24019
|
+
}
|
|
24020
|
+
}
|
|
23692
24021
|
// packages/control/src/usage.ts
|
|
23693
24022
|
var MAX_LOG_LIMIT = 500;
|
|
23694
24023
|
var DEFAULT_LOG_LIMIT = 100;
|
|
@@ -38299,7 +38628,9 @@ function adminRoutes(deps) {
|
|
|
38299
38628
|
}).post("/api/setup", async ({ request: request2, set: set2 }) => {
|
|
38300
38629
|
if (await deps.admin.isConfigured()) {
|
|
38301
38630
|
set2.status = 409;
|
|
38302
|
-
return {
|
|
38631
|
+
return {
|
|
38632
|
+
error: { code: "CONFLICT", message: "an admin password is already configured" }
|
|
38633
|
+
};
|
|
38303
38634
|
}
|
|
38304
38635
|
const body2 = await readJsonRecord(request2);
|
|
38305
38636
|
if (typeof body2?.password !== "string") {
|
|
@@ -38313,7 +38644,9 @@ function adminRoutes(deps) {
|
|
|
38313
38644
|
}
|
|
38314
38645
|
if (!created) {
|
|
38315
38646
|
set2.status = 409;
|
|
38316
|
-
return {
|
|
38647
|
+
return {
|
|
38648
|
+
error: { code: "CONFLICT", message: "an admin password is already configured" }
|
|
38649
|
+
};
|
|
38317
38650
|
}
|
|
38318
38651
|
const token = await deps.admin.login(body2.password);
|
|
38319
38652
|
if (token === null)
|
|
@@ -38362,6 +38695,16 @@ function adminRoutes(deps) {
|
|
|
38362
38695
|
await requireAdmin(request2, deps.admin);
|
|
38363
38696
|
await removeModel(deps.store, params.id);
|
|
38364
38697
|
return { ok: true };
|
|
38698
|
+
}).get("/api/agent-setup", async ({ request: request2, query }) => {
|
|
38699
|
+
await requireAdmin(request2, deps.admin);
|
|
38700
|
+
const client = query.client === "opencode" ? "opencode" : "claude";
|
|
38701
|
+
return {
|
|
38702
|
+
client,
|
|
38703
|
+
files: await setupFiles(deps.store, client, {
|
|
38704
|
+
baseUrl: deps.baseUrl,
|
|
38705
|
+
discoveryMirrors: deps.discoveryMirrors === true
|
|
38706
|
+
})
|
|
38707
|
+
};
|
|
38365
38708
|
}).get("/api/keys", async ({ request: request2 }) => {
|
|
38366
38709
|
await requireAdmin(request2, deps.admin);
|
|
38367
38710
|
return { keys: await listKeys(deps.store) };
|
|
@@ -38396,6 +38739,17 @@ function adminRoutes(deps) {
|
|
|
38396
38739
|
}).get("/api/logs", async ({ request: request2, query }) => {
|
|
38397
38740
|
await requireAdmin(request2, deps.admin);
|
|
38398
38741
|
return { logs: await recentLogs(deps.store, query.limit) };
|
|
38742
|
+
}).get("/api/console", async ({ request: request2, query }) => {
|
|
38743
|
+
await requireAdmin(request2, deps.admin);
|
|
38744
|
+
if (deps.console === undefined)
|
|
38745
|
+
return { source: "none", lines: [] };
|
|
38746
|
+
const level = parseLogLevel(query.level);
|
|
38747
|
+
const since = Number(query.since);
|
|
38748
|
+
return readConsole(deps.console.deps, deps.console.source, {
|
|
38749
|
+
lines: consoleLimit(query.lines),
|
|
38750
|
+
...level === null ? {} : { level },
|
|
38751
|
+
...Number.isFinite(since) && since > 0 ? { since } : {}
|
|
38752
|
+
});
|
|
38399
38753
|
});
|
|
38400
38754
|
}
|
|
38401
38755
|
|
|
@@ -38941,6 +39295,9 @@ var frame = (event, data) => ({
|
|
|
38941
39295
|
});
|
|
38942
39296
|
async function* anthropicStream(events, requestId) {
|
|
38943
39297
|
let model = "";
|
|
39298
|
+
const suppressed = new Set;
|
|
39299
|
+
const outIndex = new Map;
|
|
39300
|
+
let nextOutIndex = 0;
|
|
38944
39301
|
for await (const event of events) {
|
|
38945
39302
|
switch (event.type) {
|
|
38946
39303
|
case "start":
|
|
@@ -38966,26 +39323,39 @@ async function* anthropicStream(events, requestId) {
|
|
|
38966
39323
|
break;
|
|
38967
39324
|
case "blockStart": {
|
|
38968
39325
|
const b = event.block;
|
|
39326
|
+
if (b.type === "thinking" && b.signed !== true) {
|
|
39327
|
+
suppressed.add(event.index);
|
|
39328
|
+
break;
|
|
39329
|
+
}
|
|
38969
39330
|
const content_block = b.type === "text" ? { type: "text", text: "" } : b.type === "thinking" ? { type: "thinking", thinking: "" } : { type: "tool_use", id: b.id, name: b.name, input: {} };
|
|
39331
|
+
const index = nextOutIndex++;
|
|
39332
|
+
outIndex.set(event.index, index);
|
|
38970
39333
|
yield frame("content_block_start", {
|
|
38971
39334
|
type: "content_block_start",
|
|
38972
|
-
index
|
|
39335
|
+
index,
|
|
38973
39336
|
content_block
|
|
38974
39337
|
});
|
|
38975
39338
|
break;
|
|
38976
39339
|
}
|
|
38977
39340
|
case "blockDelta": {
|
|
39341
|
+
if (suppressed.has(event.index))
|
|
39342
|
+
break;
|
|
38978
39343
|
const d = event.delta;
|
|
38979
39344
|
const delta2 = d.type === "text" ? { type: "text_delta", text: d.text } : d.type === "thinking" ? { type: "thinking_delta", thinking: d.text } : d.type === "thinkingSignature" ? { type: "signature_delta", signature: d.signature } : { type: "input_json_delta", partial_json: d.partial };
|
|
38980
39345
|
yield frame("content_block_delta", {
|
|
38981
39346
|
type: "content_block_delta",
|
|
38982
|
-
index: event.index,
|
|
39347
|
+
index: outIndex.get(event.index) ?? event.index,
|
|
38983
39348
|
delta: delta2
|
|
38984
39349
|
});
|
|
38985
39350
|
break;
|
|
38986
39351
|
}
|
|
38987
39352
|
case "blockEnd":
|
|
38988
|
-
|
|
39353
|
+
if (suppressed.has(event.index))
|
|
39354
|
+
break;
|
|
39355
|
+
yield frame("content_block_stop", {
|
|
39356
|
+
type: "content_block_stop",
|
|
39357
|
+
index: outIndex.get(event.index) ?? event.index
|
|
39358
|
+
});
|
|
38989
39359
|
break;
|
|
38990
39360
|
case "end":
|
|
38991
39361
|
yield frame("message_delta", {
|
|
@@ -39015,7 +39385,7 @@ function anthropicResponse(collected, requestId) {
|
|
|
39015
39385
|
type: "message",
|
|
39016
39386
|
role: "assistant",
|
|
39017
39387
|
model: collected.model,
|
|
39018
|
-
content: collected.content.map((b) => {
|
|
39388
|
+
content: collected.content.filter((b) => b.type !== "thinking" || b.signature !== undefined && b.signature !== "").map((b) => {
|
|
39019
39389
|
switch (b.type) {
|
|
39020
39390
|
case "text":
|
|
39021
39391
|
return { type: "text", text: b.text };
|
|
@@ -39193,6 +39563,30 @@ function openaiErrorBody(code, message) {
|
|
|
39193
39563
|
return { error: { message, type: e.type, code: e.code } };
|
|
39194
39564
|
}
|
|
39195
39565
|
|
|
39566
|
+
// apps/gateway/src/ingress/model.ts
|
|
39567
|
+
var DISCOVERY_PREFIX = "claude/";
|
|
39568
|
+
var ONE_M_SUFFIX = "[1m]";
|
|
39569
|
+
function normalizeClientModel(raw, betas2 = []) {
|
|
39570
|
+
let model = raw.trim();
|
|
39571
|
+
let wantsOneM = false;
|
|
39572
|
+
if (model.toLowerCase().endsWith(ONE_M_SUFFIX)) {
|
|
39573
|
+
const stripped = model.slice(0, -ONE_M_SUFFIX.length).trim();
|
|
39574
|
+
if (stripped.length > 0) {
|
|
39575
|
+
model = stripped;
|
|
39576
|
+
wantsOneM = true;
|
|
39577
|
+
}
|
|
39578
|
+
}
|
|
39579
|
+
if (model.toLowerCase().startsWith(DISCOVERY_PREFIX)) {
|
|
39580
|
+
const stripped = model.slice(DISCOVERY_PREFIX.length);
|
|
39581
|
+
if (stripped.length > 0)
|
|
39582
|
+
model = stripped;
|
|
39583
|
+
}
|
|
39584
|
+
const merged = [...betas2];
|
|
39585
|
+
if (wantsOneM && !merged.includes(CONTEXT_1M_BETA))
|
|
39586
|
+
merged.push(CONTEXT_1M_BETA);
|
|
39587
|
+
return { model, betas: merged };
|
|
39588
|
+
}
|
|
39589
|
+
|
|
39196
39590
|
// apps/gateway/src/ingress/anthropic.ts
|
|
39197
39591
|
var textBlock = exports_external.object({
|
|
39198
39592
|
type: exports_external.literal("text"),
|
|
@@ -39374,8 +39768,9 @@ function parseAnthropicRequest(body2, headers) {
|
|
|
39374
39768
|
text: b.text,
|
|
39375
39769
|
...irCacheControl(b.cache_control)
|
|
39376
39770
|
}));
|
|
39771
|
+
const named = normalizeClientModel(parsed.model, readBetas(headers));
|
|
39377
39772
|
const request2 = {
|
|
39378
|
-
model:
|
|
39773
|
+
model: named.model,
|
|
39379
39774
|
messages,
|
|
39380
39775
|
stream: parsed.stream ?? false
|
|
39381
39776
|
};
|
|
@@ -39406,9 +39801,8 @@ function parseAnthropicRequest(body2, headers) {
|
|
|
39406
39801
|
const extras = extraFields(body2, KNOWN);
|
|
39407
39802
|
if (extras !== undefined)
|
|
39408
39803
|
request2.vendor = { anthropic: extras };
|
|
39409
|
-
|
|
39410
|
-
|
|
39411
|
-
request2.betas = betas;
|
|
39804
|
+
if (named.betas.length > 0)
|
|
39805
|
+
request2.betas = named.betas;
|
|
39412
39806
|
return validateRequest(request2);
|
|
39413
39807
|
}
|
|
39414
39808
|
|
|
@@ -39557,7 +39951,7 @@ function parseOpenAIRequest(body2) {
|
|
|
39557
39951
|
throw new GatewayError("BAD_REQUEST", "messages: at least one non-system message is required");
|
|
39558
39952
|
}
|
|
39559
39953
|
const request2 = {
|
|
39560
|
-
model: parsed.model,
|
|
39954
|
+
model: normalizeClientModel(parsed.model).model,
|
|
39561
39955
|
messages,
|
|
39562
39956
|
stream: parsed.stream ?? false
|
|
39563
39957
|
};
|
|
@@ -39591,65 +39985,13 @@ function parseOpenAIRequest(body2) {
|
|
|
39591
39985
|
|
|
39592
39986
|
// apps/gateway/src/routes/models.ts
|
|
39593
39987
|
var CREATED_AT = new Date(0).toISOString();
|
|
39594
|
-
function narrower(a, b) {
|
|
39595
|
-
const context = [a.contextWindow, b.contextWindow].filter((n) => n !== undefined);
|
|
39596
|
-
const output = [a.maxOutputTokens, b.maxOutputTokens].filter((n) => n !== undefined);
|
|
39597
|
-
return {
|
|
39598
|
-
...context.length === 0 ? {} : { contextWindow: Math.min(...context) },
|
|
39599
|
-
...output.length === 0 ? {} : { maxOutputTokens: Math.min(...output) }
|
|
39600
|
-
};
|
|
39601
|
-
}
|
|
39602
|
-
function targetLimits(target, auths) {
|
|
39603
|
-
const ways = auths.size === 0 ? ["apiKey"] : [...auths];
|
|
39604
|
-
let listed = {};
|
|
39605
|
-
for (const auth of ways) {
|
|
39606
|
-
const entry = catalogLimits(target.provider, target.model, auth);
|
|
39607
|
-
if (entry === null)
|
|
39608
|
-
continue;
|
|
39609
|
-
listed = narrower(listed, {
|
|
39610
|
-
contextWindow: entry.contextWindow,
|
|
39611
|
-
maxOutputTokens: entry.maxOutputTokens
|
|
39612
|
-
});
|
|
39613
|
-
}
|
|
39614
|
-
const contextWindow = target.contextWindow ?? listed.contextWindow;
|
|
39615
|
-
const maxOutputTokens = target.maxOutputTokens ?? listed.maxOutputTokens;
|
|
39616
|
-
return {
|
|
39617
|
-
...contextWindow === undefined ? {} : { contextWindow },
|
|
39618
|
-
...maxOutputTokens === undefined ? {} : { maxOutputTokens }
|
|
39619
|
-
};
|
|
39620
|
-
}
|
|
39621
|
-
function limitsOf(model, authsByProvider) {
|
|
39622
|
-
let limits = {};
|
|
39623
|
-
for (const target of model.targets) {
|
|
39624
|
-
limits = narrower(limits, targetLimits(target, authsByProvider.get(target.provider) ?? new Set));
|
|
39625
|
-
}
|
|
39626
|
-
return limits;
|
|
39627
|
-
}
|
|
39628
|
-
function displayName(model) {
|
|
39629
|
-
const only = model.targets.length === 1 ? model.targets[0] : undefined;
|
|
39630
|
-
if (only === undefined)
|
|
39631
|
-
return model.id;
|
|
39632
|
-
const labelled = PROVIDER_MODEL_CATALOG[only.provider]?.models.find((choice) => choice.id === only.model);
|
|
39633
|
-
return labelled?.label ?? model.id;
|
|
39634
|
-
}
|
|
39635
|
-
function servingAuths(credentials) {
|
|
39636
|
-
const byProvider = new Map;
|
|
39637
|
-
for (const credential of credentials) {
|
|
39638
|
-
if (!credential.enabled)
|
|
39639
|
-
continue;
|
|
39640
|
-
const ways = byProvider.get(credential.provider) ?? new Set;
|
|
39641
|
-
ways.add(credential.authType);
|
|
39642
|
-
byProvider.set(credential.provider, ways);
|
|
39643
|
-
}
|
|
39644
|
-
return byProvider;
|
|
39645
|
-
}
|
|
39646
39988
|
function describeModel(model, credentials) {
|
|
39647
|
-
const limits =
|
|
39989
|
+
const limits = resolveModelLimits(model, credentials);
|
|
39648
39990
|
return {
|
|
39649
39991
|
id: model.id,
|
|
39650
39992
|
object: "model",
|
|
39651
39993
|
type: "model",
|
|
39652
|
-
display_name:
|
|
39994
|
+
display_name: modelDisplayName(model),
|
|
39653
39995
|
created: 0,
|
|
39654
39996
|
created_at: CREATED_AT,
|
|
39655
39997
|
owned_by: "omnigateway",
|
|
@@ -39657,8 +39999,29 @@ function describeModel(model, credentials) {
|
|
|
39657
39999
|
...limits.maxOutputTokens === undefined ? {} : { max_tokens: limits.maxOutputTokens }
|
|
39658
40000
|
};
|
|
39659
40001
|
}
|
|
39660
|
-
|
|
39661
|
-
|
|
40002
|
+
var ALREADY_CLAUDE = /^(?:claude|anthropic)/i;
|
|
40003
|
+
var MIRROR_PREFIX = "claude/";
|
|
40004
|
+
function discoveryMirrors(data) {
|
|
40005
|
+
const taken = new Set(data.map((entry) => entry.id));
|
|
40006
|
+
const mirrors = [];
|
|
40007
|
+
for (const entry of data) {
|
|
40008
|
+
if (ALREADY_CLAUDE.test(entry.id))
|
|
40009
|
+
continue;
|
|
40010
|
+
const id = `${MIRROR_PREFIX}${entry.id}`;
|
|
40011
|
+
if (taken.has(id))
|
|
40012
|
+
continue;
|
|
40013
|
+
mirrors.push({
|
|
40014
|
+
...entry,
|
|
40015
|
+
id,
|
|
40016
|
+
root: entry.id,
|
|
40017
|
+
display_name: `${entry.display_name} (OmniGateway)`
|
|
40018
|
+
});
|
|
40019
|
+
}
|
|
40020
|
+
return mirrors;
|
|
40021
|
+
}
|
|
40022
|
+
function modelListBody(models, credentials, options = {}) {
|
|
40023
|
+
const described = models.map((model) => describeModel(model, credentials));
|
|
40024
|
+
const data = options.discoveryMirrors === true ? [...described, ...discoveryMirrors(described)] : described;
|
|
39662
40025
|
return {
|
|
39663
40026
|
object: "list",
|
|
39664
40027
|
data,
|
|
@@ -39808,32 +40171,6 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39808
40171
|
completed.durationMs = deps.now() - startedAt;
|
|
39809
40172
|
}
|
|
39810
40173
|
await finishLog(deps.store, completed, keyId, deps.logger);
|
|
39811
|
-
const fields = {
|
|
39812
|
-
requestId,
|
|
39813
|
-
surface,
|
|
39814
|
-
status: completed.status,
|
|
39815
|
-
provider: completed.resolvedProvider ?? undefined,
|
|
39816
|
-
model: completed.resolvedModel ?? undefined,
|
|
39817
|
-
requestedModel: completed.requestedModel,
|
|
39818
|
-
credentialId: completed.credentialId ?? undefined,
|
|
39819
|
-
apiKeyId: keyId ?? undefined,
|
|
39820
|
-
attempts: completed.attempts,
|
|
39821
|
-
code: completed.errorCode ?? undefined,
|
|
39822
|
-
stream: chatRequest.stream,
|
|
39823
|
-
inputTokens: completed.inputTokens,
|
|
39824
|
-
outputTokens: completed.outputTokens,
|
|
39825
|
-
cacheReadTokens: completed.cacheReadTokens,
|
|
39826
|
-
cacheWriteTokens: completed.cacheWriteTokens,
|
|
39827
|
-
costUsd: completed.costUsd,
|
|
39828
|
-
ttftMs: completed.ttftMs,
|
|
39829
|
-
durationMs: completed.durationMs
|
|
39830
|
-
};
|
|
39831
|
-
if (wasCancelled)
|
|
39832
|
-
deps.logger.debug("request cancelled", fields);
|
|
39833
|
-
else if (completed.status >= 400)
|
|
39834
|
-
deps.logger.error("request failed", fields);
|
|
39835
|
-
else
|
|
39836
|
-
deps.logger.info("request done", fields);
|
|
39837
40174
|
};
|
|
39838
40175
|
if (chatRequest.stream) {
|
|
39839
40176
|
const frames = surface === "anthropic" ? anthropicStream(outcome.events, requestId) : openaiStream(outcome.events, requestId, Math.floor(deps.now() / 1000));
|
|
@@ -39861,15 +40198,11 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39861
40198
|
durationMs: deps.now() - startedAt
|
|
39862
40199
|
});
|
|
39863
40200
|
await finishLog(deps.store, completed, keyId, deps.logger);
|
|
39864
|
-
deps.logger.
|
|
40201
|
+
deps.logger.warn("request rejected", {
|
|
39865
40202
|
requestId,
|
|
39866
40203
|
surface,
|
|
39867
40204
|
status: completed.status,
|
|
39868
|
-
requestedModel,
|
|
39869
|
-
apiKeyId: keyId ?? undefined,
|
|
39870
40205
|
code: gatewayError.code,
|
|
39871
|
-
attempts: completed.attempts,
|
|
39872
|
-
durationMs: completed.durationMs,
|
|
39873
40206
|
reason: gatewayError.message
|
|
39874
40207
|
});
|
|
39875
40208
|
return errorResponse(surface, gatewayError.code, gatewayError.message);
|
|
@@ -39890,7 +40223,9 @@ function proxyRoutes(deps) {
|
|
|
39890
40223
|
const snapshot = await dispatchDeps.snapshots.get(deps.now());
|
|
39891
40224
|
const models = [...snapshot.models.values()];
|
|
39892
40225
|
const visibleModels = key.modelAllowlist === null ? models : models.filter((model) => key.modelAllowlist?.includes(model.id));
|
|
39893
|
-
return Response.json(modelListBody(visibleModels, snapshot.credentials
|
|
40226
|
+
return Response.json(modelListBody(visibleModels, snapshot.credentials, {
|
|
40227
|
+
discoveryMirrors: deps.discoveryMirrors === true
|
|
40228
|
+
}));
|
|
39894
40229
|
} catch (error51) {
|
|
39895
40230
|
const gatewayError = asGatewayError(error51);
|
|
39896
40231
|
logger2.error("model listing failed", {
|
|
@@ -39900,6 +40235,25 @@ function proxyRoutes(deps) {
|
|
|
39900
40235
|
});
|
|
39901
40236
|
return errorResponse("anthropic", gatewayError.code, gatewayError.message);
|
|
39902
40237
|
}
|
|
40238
|
+
}).post("/v1/messages/count_tokens", async ({ request: request2 }) => {
|
|
40239
|
+
try {
|
|
40240
|
+
const key = await authenticateApiKey(deps.store, apiKeyHeader(request2.headers));
|
|
40241
|
+
rateLimiter.consume(key.id, key.rateLimitPerMin);
|
|
40242
|
+
const body2 = await request2.json();
|
|
40243
|
+
const chatRequest = parseAnthropicRequest(body2, request2.headers);
|
|
40244
|
+
if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
|
|
40245
|
+
throw new GatewayError("AUTH", `model "${chatRequest.model}" is not allowed for this API key`);
|
|
40246
|
+
}
|
|
40247
|
+
return Response.json({ input_tokens: estimateInputTokens(chatRequest) });
|
|
40248
|
+
} catch (error51) {
|
|
40249
|
+
const gatewayError = asGatewayError(error51);
|
|
40250
|
+
logger2.warn("token count failed", {
|
|
40251
|
+
status: HTTP_STATUS[gatewayError.code],
|
|
40252
|
+
code: gatewayError.code,
|
|
40253
|
+
reason: gatewayError.message
|
|
40254
|
+
});
|
|
40255
|
+
return errorResponse("anthropic", gatewayError.code, gatewayError.message);
|
|
40256
|
+
}
|
|
39903
40257
|
}).onError(({ error: error51 }) => {
|
|
39904
40258
|
const gatewayError = asGatewayError(error51);
|
|
39905
40259
|
logger2.error("unhandled proxy route error", {
|
|
@@ -39947,13 +40301,17 @@ function createApp(deps) {
|
|
|
39947
40301
|
refresh,
|
|
39948
40302
|
requestId,
|
|
39949
40303
|
rateLimiter,
|
|
39950
|
-
logger: logger2
|
|
40304
|
+
logger: logger2,
|
|
40305
|
+
discoveryMirrors: deps.discoveryMirrors === true
|
|
39951
40306
|
})).use(adminRoutes({
|
|
39952
40307
|
store: deps.store,
|
|
39953
40308
|
admin,
|
|
40309
|
+
baseUrl: deps.baseUrl,
|
|
40310
|
+
discoveryMirrors: deps.discoveryMirrors === true,
|
|
39954
40311
|
now,
|
|
39955
40312
|
sessionTtlMs: ADMIN_SESSION_TTL_MS,
|
|
39956
|
-
logger: logger2
|
|
40313
|
+
logger: logger2,
|
|
40314
|
+
...deps.console === undefined ? {} : { console: deps.console }
|
|
39957
40315
|
})).use(connectRoutes({
|
|
39958
40316
|
store: deps.store,
|
|
39959
40317
|
admin,
|
|
@@ -40126,6 +40484,30 @@ function stdoutLogger(level) {
|
|
|
40126
40484
|
});
|
|
40127
40485
|
}
|
|
40128
40486
|
var logger2 = stdoutLogger("info");
|
|
40487
|
+
function consoleSource(logFile) {
|
|
40488
|
+
const source = resolveConsoleSource({
|
|
40489
|
+
logFile,
|
|
40490
|
+
unitInstalled: process.env.JOURNAL_STREAM !== undefined,
|
|
40491
|
+
scope: process.env.MANAGERPID === undefined ? "system" : "user"
|
|
40492
|
+
});
|
|
40493
|
+
return {
|
|
40494
|
+
source,
|
|
40495
|
+
deps: {
|
|
40496
|
+
readFile: (path, lines) => tailFile(path, lines),
|
|
40497
|
+
run: async (argv) => {
|
|
40498
|
+
const [cmd, ...args] = argv;
|
|
40499
|
+
if (cmd === undefined)
|
|
40500
|
+
return { code: 1, stdout: "", stderr: "empty argv" };
|
|
40501
|
+
const proc = Bun.spawn([cmd, ...args], { stdout: "pipe", stderr: "pipe" });
|
|
40502
|
+
const [stdout, stderr] = await Promise.all([
|
|
40503
|
+
new Response(proc.stdout).text(),
|
|
40504
|
+
new Response(proc.stderr).text()
|
|
40505
|
+
]);
|
|
40506
|
+
return { code: await proc.exited, stdout, stderr };
|
|
40507
|
+
}
|
|
40508
|
+
}
|
|
40509
|
+
};
|
|
40510
|
+
}
|
|
40129
40511
|
async function main() {
|
|
40130
40512
|
const config2 = loadConfig(process.env);
|
|
40131
40513
|
logger2 = stdoutLogger(config2.logLevel);
|
|
@@ -40151,6 +40533,8 @@ async function main() {
|
|
|
40151
40533
|
const swept = await store.usage.sweepPending();
|
|
40152
40534
|
if (swept > 0)
|
|
40153
40535
|
logger2.info("retired interrupted requests", { count: swept });
|
|
40536
|
+
const console2 = consoleSource(config2.logFile);
|
|
40537
|
+
logger2.info("console log source resolved", { reason: console2.source.kind });
|
|
40154
40538
|
const now = () => Date.now();
|
|
40155
40539
|
const http = nodeHttpClient({ logger: logger2, now });
|
|
40156
40540
|
const refresh = createRefresher({ store, providers: OAUTH_PROVIDERS, http, now, logger: logger2 });
|
|
@@ -40165,7 +40549,9 @@ async function main() {
|
|
|
40165
40549
|
now,
|
|
40166
40550
|
refresh,
|
|
40167
40551
|
staticDir,
|
|
40168
|
-
logger: logger2
|
|
40552
|
+
logger: logger2,
|
|
40553
|
+
console: console2,
|
|
40554
|
+
discoveryMirrors: config2.exposeClaudeCodeAliases
|
|
40169
40555
|
});
|
|
40170
40556
|
const stopMaintenance = startMaintenance({ store, now, logger: logger2 });
|
|
40171
40557
|
const stopRefreshScheduler = startRefreshScheduler({ store, refresh, now, logger: logger2 });
|