@sentropic/h2a-runtime 0.81.0
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/dist/account-pool.d.ts +168 -0
- package/dist/account-pool.d.ts.map +1 -0
- package/dist/account-pool.js +385 -0
- package/dist/account-pool.js.map +1 -0
- package/dist/agents-projection.d.ts +56 -0
- package/dist/agents-projection.d.ts.map +1 -0
- package/dist/agents-projection.js +82 -0
- package/dist/agents-projection.js.map +1 -0
- package/dist/attach.d.ts +75 -0
- package/dist/attach.d.ts.map +1 -0
- package/dist/attach.js +432 -0
- package/dist/attach.js.map +1 -0
- package/dist/auth-bundle.d.ts +21 -0
- package/dist/auth-bundle.d.ts.map +1 -0
- package/dist/auth-bundle.js +112 -0
- package/dist/auth-bundle.js.map +1 -0
- package/dist/auth-diagnostics.d.ts +18 -0
- package/dist/auth-diagnostics.d.ts.map +1 -0
- package/dist/auth-diagnostics.js +23 -0
- package/dist/auth-diagnostics.js.map +1 -0
- package/dist/auth-login.d.ts +9 -0
- package/dist/auth-login.d.ts.map +1 -0
- package/dist/auth-login.js +21 -0
- package/dist/auth-login.js.map +1 -0
- package/dist/auth-refresh.d.ts +32 -0
- package/dist/auth-refresh.d.ts.map +1 -0
- package/dist/auth-refresh.js +83 -0
- package/dist/auth-refresh.js.map +1 -0
- package/dist/auth-tools.d.ts +55 -0
- package/dist/auth-tools.d.ts.map +1 -0
- package/dist/auth-tools.js +163 -0
- package/dist/auth-tools.js.map +1 -0
- package/dist/browser-bridge-local.d.ts +27 -0
- package/dist/browser-bridge-local.d.ts.map +1 -0
- package/dist/browser-bridge-local.js +51 -0
- package/dist/browser-bridge-local.js.map +1 -0
- package/dist/browser.d.ts +53 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +61 -0
- package/dist/browser.js.map +1 -0
- package/dist/conductor-launch.d.ts +179 -0
- package/dist/conductor-launch.d.ts.map +1 -0
- package/dist/conductor-launch.js +414 -0
- package/dist/conductor-launch.js.map +1 -0
- package/dist/config.d.ts +182 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +334 -0
- package/dist/config.js.map +1 -0
- package/dist/conv-guard.d.ts +76 -0
- package/dist/conv-guard.d.ts.map +1 -0
- package/dist/conv-guard.js +131 -0
- package/dist/conv-guard.js.map +1 -0
- package/dist/convsync.d.ts +24 -0
- package/dist/convsync.d.ts.map +1 -0
- package/dist/convsync.js +108 -0
- package/dist/convsync.js.map +1 -0
- package/dist/cred-health.d.ts +96 -0
- package/dist/cred-health.d.ts.map +1 -0
- package/dist/cred-health.js +170 -0
- package/dist/cred-health.js.map +1 -0
- package/dist/delegate.d.ts +406 -0
- package/dist/delegate.d.ts.map +1 -0
- package/dist/delegate.js +739 -0
- package/dist/delegate.js.map +1 -0
- package/dist/enroll.d.ts +91 -0
- package/dist/enroll.d.ts.map +1 -0
- package/dist/enroll.js +248 -0
- package/dist/enroll.js.map +1 -0
- package/dist/fanout.d.ts +63 -0
- package/dist/fanout.d.ts.map +1 -0
- package/dist/fanout.js +115 -0
- package/dist/fanout.js.map +1 -0
- package/dist/forward.d.ts +66 -0
- package/dist/forward.d.ts.map +1 -0
- package/dist/forward.js +141 -0
- package/dist/forward.js.map +1 -0
- package/dist/gitdiff.d.ts +31 -0
- package/dist/gitdiff.d.ts.map +1 -0
- package/dist/gitdiff.js +133 -0
- package/dist/gitdiff.js.map +1 -0
- package/dist/h2a-bridge.d.ts +102 -0
- package/dist/h2a-bridge.d.ts.map +1 -0
- package/dist/h2a-bridge.js +333 -0
- package/dist/h2a-bridge.js.map +1 -0
- package/dist/h2a-jobs.d.ts +235 -0
- package/dist/h2a-jobs.d.ts.map +1 -0
- package/dist/h2a-jobs.js +388 -0
- package/dist/h2a-jobs.js.map +1 -0
- package/dist/h2a-ping.d.ts +38 -0
- package/dist/h2a-ping.d.ts.map +1 -0
- package/dist/h2a-ping.js +58 -0
- package/dist/h2a-ping.js.map +1 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5887 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive-throttle.d.ts +164 -0
- package/dist/interactive-throttle.d.ts.map +1 -0
- package/dist/interactive-throttle.js +194 -0
- package/dist/interactive-throttle.js.map +1 -0
- package/dist/lineage-client.d.ts +77 -0
- package/dist/lineage-client.d.ts.map +1 -0
- package/dist/lineage-client.js +117 -0
- package/dist/lineage-client.js.map +1 -0
- package/dist/lineage-lease.d.ts +152 -0
- package/dist/lineage-lease.d.ts.map +1 -0
- package/dist/lineage-lease.js +303 -0
- package/dist/lineage-lease.js.map +1 -0
- package/dist/llm-gateway-runtime/accounts.d.ts +48 -0
- package/dist/llm-gateway-runtime/accounts.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/accounts.js +226 -0
- package/dist/llm-gateway-runtime/accounts.js.map +1 -0
- package/dist/llm-gateway-runtime/index.d.ts +4 -0
- package/dist/llm-gateway-runtime/index.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/index.js +58 -0
- package/dist/llm-gateway-runtime/index.js.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts +36 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.js +146 -0
- package/dist/llm-gateway-runtime/model-catalog.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts +3 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js +139 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts +117 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.js +1132 -0
- package/dist/llm-gateway-runtime/proxy-openai.js.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts +32 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.js +62 -0
- package/dist/llm-gateway-runtime/session-ledger.js.map +1 -0
- package/dist/llm-gateway-runtime/sticky.d.ts +40 -0
- package/dist/llm-gateway-runtime/sticky.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/sticky.js +254 -0
- package/dist/llm-gateway-runtime/sticky.js.map +1 -0
- package/dist/llm-mesh.d.ts +102 -0
- package/dist/llm-mesh.d.ts.map +1 -0
- package/dist/llm-mesh.js +433 -0
- package/dist/llm-mesh.js.map +1 -0
- package/dist/migrate-candidates.d.ts +35 -0
- package/dist/migrate-candidates.d.ts.map +1 -0
- package/dist/migrate-candidates.js +128 -0
- package/dist/migrate-candidates.js.map +1 -0
- package/dist/migrate.d.ts +125 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +561 -0
- package/dist/migrate.js.map +1 -0
- package/dist/plugin-manifest.d.ts +143 -0
- package/dist/plugin-manifest.d.ts.map +1 -0
- package/dist/plugin-manifest.js +233 -0
- package/dist/plugin-manifest.js.map +1 -0
- package/dist/plugin.d.ts +274 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +919 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pod-liveness.d.ts +51 -0
- package/dist/pod-liveness.d.ts.map +1 -0
- package/dist/pod-liveness.js +80 -0
- package/dist/pod-liveness.js.map +1 -0
- package/dist/profile-menu.d.ts +7 -0
- package/dist/profile-menu.d.ts.map +1 -0
- package/dist/profile-menu.js +52 -0
- package/dist/profile-menu.js.map +1 -0
- package/dist/profiles.d.ts +23 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +66 -0
- package/dist/profiles.js.map +1 -0
- package/dist/protocol-local.d.ts +27 -0
- package/dist/protocol-local.d.ts.map +1 -0
- package/dist/protocol-local.js +12 -0
- package/dist/protocol-local.js.map +1 -0
- package/dist/pty.d.ts +26 -0
- package/dist/pty.d.ts.map +1 -0
- package/dist/pty.js +45 -0
- package/dist/pty.js.map +1 -0
- package/dist/readiness.d.ts +38 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +169 -0
- package/dist/readiness.js.map +1 -0
- package/dist/registry.d.ts +258 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +570 -0
- package/dist/registry.js.map +1 -0
- package/dist/relaunch.d.ts +52 -0
- package/dist/relaunch.d.ts.map +1 -0
- package/dist/relaunch.js +76 -0
- package/dist/relaunch.js.map +1 -0
- package/dist/restore.d.ts +165 -0
- package/dist/restore.d.ts.map +1 -0
- package/dist/restore.js +530 -0
- package/dist/restore.js.map +1 -0
- package/dist/run.d.ts +29 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +206 -0
- package/dist/run.js.map +1 -0
- package/dist/secrets.d.ts +25 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +98 -0
- package/dist/secrets.js.map +1 -0
- package/dist/session-restore.d.ts +26 -0
- package/dist/session-restore.d.ts.map +1 -0
- package/dist/session-restore.js +122 -0
- package/dist/session-restore.js.map +1 -0
- package/dist/skills-sync.d.ts +126 -0
- package/dist/skills-sync.d.ts.map +1 -0
- package/dist/skills-sync.js +255 -0
- package/dist/skills-sync.js.map +1 -0
- package/dist/smoke.d.ts +23 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +138 -0
- package/dist/smoke.js.map +1 -0
- package/dist/soft-refresh.d.ts +107 -0
- package/dist/soft-refresh.d.ts.map +1 -0
- package/dist/soft-refresh.js +426 -0
- package/dist/soft-refresh.js.map +1 -0
- package/dist/sync-status.d.ts +22 -0
- package/dist/sync-status.d.ts.map +1 -0
- package/dist/sync-status.js +41 -0
- package/dist/sync-status.js.map +1 -0
- package/dist/sync.d.ts +77 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +302 -0
- package/dist/sync.js.map +1 -0
- package/dist/throttle-signatures.d.ts +43 -0
- package/dist/throttle-signatures.d.ts.map +1 -0
- package/dist/throttle-signatures.js +116 -0
- package/dist/throttle-signatures.js.map +1 -0
- package/dist/tmux.d.ts +286 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +970 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tunnel.d.ts +25 -0
- package/dist/tunnel.d.ts.map +1 -0
- package/dist/tunnel.js +127 -0
- package/dist/tunnel.js.map +1 -0
- package/dist/workspace-merge.d.ts +17 -0
- package/dist/workspace-merge.d.ts.map +1 -0
- package/dist/workspace-merge.js +153 -0
- package/dist/workspace-merge.js.map +1 -0
- package/dist/workspace-sync-incremental.d.ts +64 -0
- package/dist/workspace-sync-incremental.d.ts.map +1 -0
- package/dist/workspace-sync-incremental.js +199 -0
- package/dist/workspace-sync-incremental.js.map +1 -0
- package/dist/workspace-sync.d.ts +3 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace-sync.js +187 -0
- package/dist/workspace-sync.js.map +1 -0
- package/dist/workspace.d.ts +70 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +173 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { accountPoolForProvider, listModelCatalog, resolveModelRoute, } from "./model-catalog.js";
|
|
2
|
+
function isUnsupportedClaudeOAuthAccount(account) {
|
|
3
|
+
return (account.provider === "anthropic" &&
|
|
4
|
+
(account.authType === "bearer" ||
|
|
5
|
+
account.id === "claude-oauth" ||
|
|
6
|
+
account.token.startsWith("sk-ant-oat")));
|
|
7
|
+
}
|
|
8
|
+
function parse() {
|
|
9
|
+
const raw = process.env.GATEWAY_ACCOUNTS;
|
|
10
|
+
if (!raw)
|
|
11
|
+
throw new Error("GATEWAY_ACCOUNTS env var is required");
|
|
12
|
+
const list = JSON.parse(raw);
|
|
13
|
+
if (!Array.isArray(list) || list.length === 0)
|
|
14
|
+
throw new Error("GATEWAY_ACCOUNTS must be a non-empty JSON array");
|
|
15
|
+
const accounts = list.filter((account) => !isUnsupportedClaudeOAuthAccount(account));
|
|
16
|
+
if (accounts.length === 0) {
|
|
17
|
+
throw new Error("GATEWAY_ACCOUNTS has no supported accounts: Claude Code OAuth is not a supported upstream transport");
|
|
18
|
+
}
|
|
19
|
+
return accounts;
|
|
20
|
+
}
|
|
21
|
+
let _accounts = null;
|
|
22
|
+
export function getAccounts() {
|
|
23
|
+
if (!_accounts)
|
|
24
|
+
_accounts = parse();
|
|
25
|
+
return _accounts;
|
|
26
|
+
}
|
|
27
|
+
function accountStatus(account) {
|
|
28
|
+
return account.status ?? "active";
|
|
29
|
+
}
|
|
30
|
+
function explicitModelIds(account) {
|
|
31
|
+
return [...new Set([...(account.modelIds ?? []), ...(account.models ?? [])])];
|
|
32
|
+
}
|
|
33
|
+
export function publicAccountDescriptor(account) {
|
|
34
|
+
return {
|
|
35
|
+
id: account.id,
|
|
36
|
+
provider: account.provider,
|
|
37
|
+
label: account.label,
|
|
38
|
+
...(account.authType ? { authType: account.authType } : {}),
|
|
39
|
+
...(account.expiresAt ? { expiresAt: account.expiresAt } : {}),
|
|
40
|
+
modelIds: explicitModelIds(account),
|
|
41
|
+
status: accountStatus(account),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function listAccountDescriptors() {
|
|
45
|
+
return getAccounts().map(publicAccountDescriptor);
|
|
46
|
+
}
|
|
47
|
+
export function providerFamily(provider) {
|
|
48
|
+
if (provider === "openai" || provider === "codex")
|
|
49
|
+
return "openai";
|
|
50
|
+
if (provider === "anthropic" || provider === "claude-code")
|
|
51
|
+
return "anthropic";
|
|
52
|
+
return "other";
|
|
53
|
+
}
|
|
54
|
+
export const DEFAULT_QUOTA_EXHAUSTION_MS = 5 * 60 * 60 * 1000;
|
|
55
|
+
const _exhausted = new Map();
|
|
56
|
+
function quotaExhaustionWindowMs() {
|
|
57
|
+
const configured = Number.parseInt(process.env.LLM_GATEWAY_QUOTA_EXHAUSTION_MS ?? "", 10);
|
|
58
|
+
return Number.isFinite(configured) && configured > 0
|
|
59
|
+
? configured
|
|
60
|
+
: DEFAULT_QUOTA_EXHAUSTION_MS;
|
|
61
|
+
}
|
|
62
|
+
export function markAccountExhausted(accountId, reason, windowMs = quotaExhaustionWindowMs(), nowMs = Date.now()) {
|
|
63
|
+
_exhausted.set(accountId, {
|
|
64
|
+
exhaustedAtMs: nowMs,
|
|
65
|
+
windowMs,
|
|
66
|
+
...(reason !== undefined ? { reason } : {}),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function isAccountExhausted(accountId, nowMs = Date.now()) {
|
|
70
|
+
const rec = _exhausted.get(accountId);
|
|
71
|
+
if (!rec)
|
|
72
|
+
return false;
|
|
73
|
+
if (nowMs >= rec.exhaustedAtMs + rec.windowMs) {
|
|
74
|
+
_exhausted.delete(accountId);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
export function accountExhaustionReason(accountId) {
|
|
80
|
+
return _exhausted.get(accountId)?.reason;
|
|
81
|
+
}
|
|
82
|
+
// Visible for testing
|
|
83
|
+
export function resetAccountsCache() {
|
|
84
|
+
_accounts = null;
|
|
85
|
+
_rrIdx = 0;
|
|
86
|
+
_routeRrIdx.clear();
|
|
87
|
+
_exhausted.clear();
|
|
88
|
+
}
|
|
89
|
+
let _rrIdx = 0;
|
|
90
|
+
const _routeRrIdx = new Map();
|
|
91
|
+
export function accountSupportsRoute(account, route) {
|
|
92
|
+
if (accountStatus(account) !== "active")
|
|
93
|
+
return false;
|
|
94
|
+
if (isAccountExhausted(account.id))
|
|
95
|
+
return false;
|
|
96
|
+
if (accountPoolForProvider(account.provider) !== route.accountPool)
|
|
97
|
+
return false;
|
|
98
|
+
const modelIds = explicitModelIds(account);
|
|
99
|
+
if (modelIds.length === 0)
|
|
100
|
+
return true;
|
|
101
|
+
const accepted = [
|
|
102
|
+
route.requestedModel,
|
|
103
|
+
route.catalogModelId,
|
|
104
|
+
route.upstreamModel,
|
|
105
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
106
|
+
return accepted.some((modelId) => modelIds.includes(modelId));
|
|
107
|
+
}
|
|
108
|
+
export function eligibleAccountsForRoute(route) {
|
|
109
|
+
const accounts = getAccounts().filter((account) => accountStatus(account) === "active" && !isAccountExhausted(account.id));
|
|
110
|
+
if (!route)
|
|
111
|
+
return accounts;
|
|
112
|
+
return accounts.filter((account) => accountSupportsRoute(account, route));
|
|
113
|
+
}
|
|
114
|
+
export function selectAccount() {
|
|
115
|
+
const accounts = getAccounts();
|
|
116
|
+
const usable = eligibleAccountsForRoute();
|
|
117
|
+
const pool = usable.length > 0 ? usable : accounts;
|
|
118
|
+
const acct = pool[_rrIdx % pool.length];
|
|
119
|
+
_rrIdx++;
|
|
120
|
+
return acct;
|
|
121
|
+
}
|
|
122
|
+
export function selectAccountForRoute(route) {
|
|
123
|
+
const accounts = eligibleAccountsForRoute(route);
|
|
124
|
+
if (accounts.length === 0) {
|
|
125
|
+
throw new Error(`no eligible ${route.accountPool} account for ${route.requestedModel ?? route.upstreamModel ?? "route"}`);
|
|
126
|
+
}
|
|
127
|
+
const key = `${route.accountPool}:${route.upstreamModel ?? route.catalogModelId ?? ""}`;
|
|
128
|
+
const idx = _routeRrIdx.get(key) ?? 0;
|
|
129
|
+
const acct = accounts[idx % accounts.length];
|
|
130
|
+
_routeRrIdx.set(key, idx + 1);
|
|
131
|
+
return acct;
|
|
132
|
+
}
|
|
133
|
+
export function findAccount(accountId) {
|
|
134
|
+
return getAccounts().find((a) => a.id === accountId);
|
|
135
|
+
}
|
|
136
|
+
export function listRoutableModels() {
|
|
137
|
+
const accounts = getAccounts();
|
|
138
|
+
return listModelCatalog().filter((entry) => {
|
|
139
|
+
const route = resolveModelRoute(entry.id);
|
|
140
|
+
return (route !== undefined &&
|
|
141
|
+
accounts.some((account) => accountSupportsRoute(account, route)));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
export function selectFallbackAccount(exhaustedAccountId, nowMs = Date.now()) {
|
|
145
|
+
let accounts;
|
|
146
|
+
try {
|
|
147
|
+
accounts = getAccounts();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const exhausted = accounts.find((a) => a.id === exhaustedAccountId);
|
|
153
|
+
const candidates = accounts.filter((a) => a.id !== exhaustedAccountId &&
|
|
154
|
+
accountStatus(a) === "active" &&
|
|
155
|
+
!isAccountExhausted(a.id, nowMs));
|
|
156
|
+
if (candidates.length === 0)
|
|
157
|
+
return undefined;
|
|
158
|
+
const family = exhausted ? providerFamily(exhausted.provider) : undefined;
|
|
159
|
+
const sameFamily = family
|
|
160
|
+
? candidates.filter((a) => providerFamily(a.provider) === family)
|
|
161
|
+
: [];
|
|
162
|
+
return sameFamily[0] ?? candidates[0];
|
|
163
|
+
}
|
|
164
|
+
/** Update the in-memory token for an account (after OAuth refresh). */
|
|
165
|
+
export function updateAccountToken(accountId, newToken, expiresAt) {
|
|
166
|
+
const accounts = getAccounts();
|
|
167
|
+
const acc = accounts.find((a) => a.id === accountId);
|
|
168
|
+
if (!acc)
|
|
169
|
+
return;
|
|
170
|
+
acc.token = newToken;
|
|
171
|
+
if (expiresAt !== undefined)
|
|
172
|
+
acc.expiresAt = expiresAt;
|
|
173
|
+
}
|
|
174
|
+
function jwtExpiry(token) {
|
|
175
|
+
try {
|
|
176
|
+
const parts = token.split(".");
|
|
177
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
178
|
+
if (typeof payload.exp === "number")
|
|
179
|
+
return new Date(payload.exp * 1000).toISOString();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
/* ignore */
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Refresh the OAuth access token for an account using its refresh_token.
|
|
188
|
+
* Updates the in-memory account on success. No-op if no refresh_token.
|
|
189
|
+
* Returns the new token, or null if refresh is not applicable.
|
|
190
|
+
*/
|
|
191
|
+
export async function refreshOAuthToken(accountId) {
|
|
192
|
+
const acc = findAccount(accountId);
|
|
193
|
+
if (!acc?.refreshToken)
|
|
194
|
+
return null;
|
|
195
|
+
let resp;
|
|
196
|
+
try {
|
|
197
|
+
resp = await fetch("https://auth.openai.com/oauth/token", {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
200
|
+
body: new URLSearchParams({
|
|
201
|
+
grant_type: "refresh_token",
|
|
202
|
+
refresh_token: acc.refreshToken,
|
|
203
|
+
client_id: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
console.error(`[llm-gateway] OAuth refresh network error for ${accountId}:`, err);
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
if (!resp.ok) {
|
|
212
|
+
const body = await resp.text().catch(() => "");
|
|
213
|
+
console.error(`[llm-gateway] OAuth refresh failed (${resp.status}) for ${accountId}: ${body}`);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const data = (await resp.json());
|
|
217
|
+
if (!data.access_token) {
|
|
218
|
+
console.error(`[llm-gateway] OAuth refresh: no access_token in response for ${accountId}`);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const expiresAt = jwtExpiry(data.access_token);
|
|
222
|
+
updateAccountToken(accountId, data.access_token, expiresAt);
|
|
223
|
+
console.log(`[llm-gateway] OAuth token refreshed for ${accountId}${expiresAt ? `, expires ${expiresAt}` : ""}`);
|
|
224
|
+
return data.access_token;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/llm-gateway-runtime/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm-gateway-runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,eAAO,MAAM,GAAG,4EAAa,CAAC;AAmD9B,wBAAgB,WAAW,IAAI,IAAI,CAOlC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { acquireSession, sessionCount } from "./sticky.js";
|
|
4
|
+
import { handleMessages } from "./proxy-anthropic.js";
|
|
5
|
+
import { listAccountDescriptors, listRoutableModels } from "./accounts.js";
|
|
6
|
+
import { modelCatalogResponse } from "./model-catalog.js";
|
|
7
|
+
import { getSessionLedgerEntry, listSessionLedger } from "./session-ledger.js";
|
|
8
|
+
export const app = new Hono();
|
|
9
|
+
// Health — no internal state exposed
|
|
10
|
+
app.get("/health", (c) => c.json({ ok: true }));
|
|
11
|
+
app.get("/healthz", (c) => c.json({ ok: true }));
|
|
12
|
+
// Session creation — called by control-plane before pod start.
|
|
13
|
+
// Network policy enforces that pods cannot reach this endpoint.
|
|
14
|
+
app.post("/v1/session", async (c) => {
|
|
15
|
+
let body;
|
|
16
|
+
try {
|
|
17
|
+
body = await c.req.json();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return c.json({ error: "invalid JSON" }, 400);
|
|
21
|
+
}
|
|
22
|
+
if (typeof body.sessionId !== "string" || !body.sessionId) {
|
|
23
|
+
return c.json({ error: "sessionId (string) required" }, 400);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const result = await acquireSession(body.sessionId, {
|
|
27
|
+
...(typeof body.provider === "string" ? { provider: body.provider } : {}),
|
|
28
|
+
...(typeof body.model === "string" ? { model: body.model } : {}),
|
|
29
|
+
...(typeof body.workspaceId === "string" ? { workspaceId: body.workspaceId } : {}),
|
|
30
|
+
...(typeof body.profile === "string" ? { profile: body.profile } : {}),
|
|
31
|
+
...(typeof body.clientSessionId === "string" ? { clientSessionId: body.clientSessionId } : {}),
|
|
32
|
+
});
|
|
33
|
+
return c.json(result, 201);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 400);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
app.get("/v1/accounts", (c) => c.json({ data: listAccountDescriptors() }));
|
|
40
|
+
app.get("/v1/models", (c) => c.json(modelCatalogResponse(listRoutableModels())));
|
|
41
|
+
app.get("/v1/sessions", (c) => c.json({ data: listSessionLedger() }));
|
|
42
|
+
app.get("/v1/sessions/:id", (c) => {
|
|
43
|
+
const entry = getSessionLedgerEntry(c.req.param("id"));
|
|
44
|
+
if (!entry)
|
|
45
|
+
return c.json({ error: "session not found" }, 404);
|
|
46
|
+
return c.json(entry);
|
|
47
|
+
});
|
|
48
|
+
// Anthropic Messages proxy — pods call this with Bearer gw-<hex>
|
|
49
|
+
app.post("/v1/messages", handleMessages);
|
|
50
|
+
export function startServer() {
|
|
51
|
+
const port = parseInt(process.env.PORT ?? "3001", 10);
|
|
52
|
+
serve({ fetch: app.fetch, port }, () => {
|
|
53
|
+
process.stdout.write(`[llm-gateway] listening on :${port} — ${sessionCount()} sessions in memory\n`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (process.env.NODE_ENV !== "test")
|
|
57
|
+
startServer();
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/llm-gateway-runtime/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type AccountPool = "anthropic" | "codex";
|
|
2
|
+
export type GatewayProtocol = "anthropic.messages";
|
|
3
|
+
export type RoutingPolicy = "round-robin";
|
|
4
|
+
export interface ModelCatalogEntry {
|
|
5
|
+
id: string;
|
|
6
|
+
provider: "anthropic" | "codex";
|
|
7
|
+
upstreamModel: string;
|
|
8
|
+
accountPool: AccountPool;
|
|
9
|
+
inputProtocol: GatewayProtocol;
|
|
10
|
+
outputProtocol: GatewayProtocol;
|
|
11
|
+
capabilities: string[];
|
|
12
|
+
defaultPolicy: RoutingPolicy;
|
|
13
|
+
aliases?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface RoutingTarget {
|
|
16
|
+
requestedModel?: string;
|
|
17
|
+
catalogModelId?: string;
|
|
18
|
+
upstreamModel?: string;
|
|
19
|
+
accountPool: AccountPool;
|
|
20
|
+
routingPolicy: RoutingPolicy;
|
|
21
|
+
routeReason: "catalog-id" | "catalog-alias" | "env-model-map" | "provider-request" | "passthrough-gpt";
|
|
22
|
+
}
|
|
23
|
+
export declare function listModelCatalog(): ModelCatalogEntry[];
|
|
24
|
+
export declare function resetModelCatalogCache(): void;
|
|
25
|
+
export declare function accountPoolForProvider(provider: string): AccountPool | undefined;
|
|
26
|
+
export declare function routeForProvider(provider: string): RoutingTarget | undefined;
|
|
27
|
+
export declare function resolveModelRoute(model: string): RoutingTarget | undefined;
|
|
28
|
+
export declare function routeModelOrThrow(model: string): RoutingTarget;
|
|
29
|
+
export declare function modelCatalogResponse(entries?: ModelCatalogEntry[]): {
|
|
30
|
+
object: "list";
|
|
31
|
+
data: Array<ModelCatalogEntry & {
|
|
32
|
+
object: "model";
|
|
33
|
+
owned_by: string;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=model-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AACnD,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,eAAe,CAAC;IAC/B,cAAc,EAAE,eAAe,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EACP,YAAY,GACZ,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;CACvB;AAgED,wBAAgB,gBAAgB,IAAI,iBAAiB,EAAE,CAGtD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GACf,WAAW,GAAG,SAAS,CAMzB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAQ5E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAiD1E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAI9D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,sBAAqB,GAAG;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE,CASA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const CODEX_CAPABILITIES = ["streaming", "tools", "reasoning_effort"];
|
|
2
|
+
const DEFAULT_MODEL_CATALOG = [
|
|
3
|
+
{
|
|
4
|
+
id: "gpt-5.5",
|
|
5
|
+
provider: "codex",
|
|
6
|
+
upstreamModel: "gpt-5.5",
|
|
7
|
+
accountPool: "codex",
|
|
8
|
+
inputProtocol: "anthropic.messages",
|
|
9
|
+
outputProtocol: "anthropic.messages",
|
|
10
|
+
capabilities: [...CODEX_CAPABILITIES],
|
|
11
|
+
defaultPolicy: "round-robin",
|
|
12
|
+
aliases: [
|
|
13
|
+
"claude-opus-4-8",
|
|
14
|
+
"claude-opus-4-7",
|
|
15
|
+
"claude-opus-4-6",
|
|
16
|
+
"claude-sonnet-4-6",
|
|
17
|
+
"claude-sonnet-4-5",
|
|
18
|
+
"claude-haiku-4-5-20251001",
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "gpt-5.3-codex-spark",
|
|
23
|
+
provider: "codex",
|
|
24
|
+
upstreamModel: "gpt-5.3-codex-spark",
|
|
25
|
+
accountPool: "codex",
|
|
26
|
+
inputProtocol: "anthropic.messages",
|
|
27
|
+
outputProtocol: "anthropic.messages",
|
|
28
|
+
capabilities: [...CODEX_CAPABILITIES],
|
|
29
|
+
defaultPolicy: "round-robin",
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
let _catalog = null;
|
|
33
|
+
let _envModelMap = null;
|
|
34
|
+
function parseEnvModelMap() {
|
|
35
|
+
if (_envModelMap)
|
|
36
|
+
return _envModelMap;
|
|
37
|
+
if (!process.env.OPENAI_MODEL_MAP) {
|
|
38
|
+
_envModelMap = {};
|
|
39
|
+
return _envModelMap;
|
|
40
|
+
}
|
|
41
|
+
_envModelMap = JSON.parse(process.env.OPENAI_MODEL_MAP);
|
|
42
|
+
return _envModelMap;
|
|
43
|
+
}
|
|
44
|
+
function envCatalogEntries() {
|
|
45
|
+
return Object.entries(parseEnvModelMap()).map(([id, upstreamModel]) => ({
|
|
46
|
+
id,
|
|
47
|
+
provider: "codex",
|
|
48
|
+
upstreamModel,
|
|
49
|
+
accountPool: "codex",
|
|
50
|
+
inputProtocol: "anthropic.messages",
|
|
51
|
+
outputProtocol: "anthropic.messages",
|
|
52
|
+
capabilities: [...CODEX_CAPABILITIES],
|
|
53
|
+
defaultPolicy: "round-robin",
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
export function listModelCatalog() {
|
|
57
|
+
if (!_catalog)
|
|
58
|
+
_catalog = [...DEFAULT_MODEL_CATALOG, ...envCatalogEntries()];
|
|
59
|
+
return _catalog;
|
|
60
|
+
}
|
|
61
|
+
export function resetModelCatalogCache() {
|
|
62
|
+
_catalog = null;
|
|
63
|
+
_envModelMap = null;
|
|
64
|
+
}
|
|
65
|
+
export function accountPoolForProvider(provider) {
|
|
66
|
+
const normalized = provider.toLowerCase();
|
|
67
|
+
if (normalized === "openai" || normalized === "codex")
|
|
68
|
+
return "codex";
|
|
69
|
+
if (normalized === "anthropic" || normalized === "claude-code")
|
|
70
|
+
return "anthropic";
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
export function routeForProvider(provider) {
|
|
74
|
+
const accountPool = accountPoolForProvider(provider);
|
|
75
|
+
if (!accountPool)
|
|
76
|
+
return undefined;
|
|
77
|
+
return {
|
|
78
|
+
accountPool,
|
|
79
|
+
routingPolicy: "round-robin",
|
|
80
|
+
routeReason: "provider-request",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function resolveModelRoute(model) {
|
|
84
|
+
const envMap = parseEnvModelMap();
|
|
85
|
+
const envUpstream = envMap[model];
|
|
86
|
+
if (envUpstream) {
|
|
87
|
+
return {
|
|
88
|
+
requestedModel: model,
|
|
89
|
+
catalogModelId: model,
|
|
90
|
+
upstreamModel: envUpstream,
|
|
91
|
+
accountPool: "codex",
|
|
92
|
+
routingPolicy: "round-robin",
|
|
93
|
+
routeReason: "env-model-map",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
for (const entry of listModelCatalog()) {
|
|
97
|
+
if (entry.id === model) {
|
|
98
|
+
return {
|
|
99
|
+
requestedModel: model,
|
|
100
|
+
catalogModelId: entry.id,
|
|
101
|
+
upstreamModel: entry.upstreamModel,
|
|
102
|
+
accountPool: entry.accountPool,
|
|
103
|
+
routingPolicy: entry.defaultPolicy,
|
|
104
|
+
routeReason: "catalog-id",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (entry.aliases?.includes(model)) {
|
|
108
|
+
return {
|
|
109
|
+
requestedModel: model,
|
|
110
|
+
catalogModelId: entry.id,
|
|
111
|
+
upstreamModel: entry.upstreamModel,
|
|
112
|
+
accountPool: entry.accountPool,
|
|
113
|
+
routingPolicy: entry.defaultPolicy,
|
|
114
|
+
routeReason: "catalog-alias",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (model.startsWith("gpt-")) {
|
|
119
|
+
return {
|
|
120
|
+
requestedModel: model,
|
|
121
|
+
catalogModelId: model,
|
|
122
|
+
upstreamModel: model,
|
|
123
|
+
accountPool: "codex",
|
|
124
|
+
routingPolicy: "round-robin",
|
|
125
|
+
routeReason: "passthrough-gpt",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
export function routeModelOrThrow(model) {
|
|
131
|
+
const route = resolveModelRoute(model);
|
|
132
|
+
if (!route)
|
|
133
|
+
throw new Error(`unsupported model: ${model}`);
|
|
134
|
+
return route;
|
|
135
|
+
}
|
|
136
|
+
export function modelCatalogResponse(entries = listModelCatalog()) {
|
|
137
|
+
return {
|
|
138
|
+
object: "list",
|
|
139
|
+
data: entries.map((entry) => ({
|
|
140
|
+
...entry,
|
|
141
|
+
object: "model",
|
|
142
|
+
owned_by: entry.provider,
|
|
143
|
+
})),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=model-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-catalog.js","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-anthropic.d.ts","sourceRoot":"","sources":["../../src/llm-gateway-runtime/proxy-anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AA4JpC,wBAAsB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0BlE"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { markAccountExhausted, selectFallbackAccount } from "./accounts.js";
|
|
2
|
+
import { lookupToken, rebindGatewaySession, } from "./sticky.js";
|
|
3
|
+
import { handleMessagesViaOpenAI } from "./proxy-openai.js";
|
|
4
|
+
import { resolveModelRoute } from "./model-catalog.js";
|
|
5
|
+
import { recordSessionRequest } from "./session-ledger.js";
|
|
6
|
+
const ANTHROPIC_BASE = process.env.ANTHROPIC_UPSTREAM_URL ?? "https://api.anthropic.com";
|
|
7
|
+
const PASSTHROUGH_REQUEST_HEADERS = [
|
|
8
|
+
"anthropic-version",
|
|
9
|
+
"anthropic-beta",
|
|
10
|
+
"content-type",
|
|
11
|
+
];
|
|
12
|
+
const PASSTHROUGH_RESPONSE_HEADERS = [
|
|
13
|
+
"content-type",
|
|
14
|
+
"transfer-encoding",
|
|
15
|
+
"retry-after",
|
|
16
|
+
];
|
|
17
|
+
function gatewayTokenFromRequest(c) {
|
|
18
|
+
const auth = c.req.header("authorization") ?? "";
|
|
19
|
+
if (auth.startsWith("Bearer gw-"))
|
|
20
|
+
return auth.slice("Bearer ".length);
|
|
21
|
+
const apiKey = c.req.header("x-api-key") ?? "";
|
|
22
|
+
if (apiKey.startsWith("gw-"))
|
|
23
|
+
return apiKey;
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function usesOpenAIProvider(provider) {
|
|
27
|
+
return provider === "openai" || provider === "codex";
|
|
28
|
+
}
|
|
29
|
+
function isQuotaFallbackResponse(response) {
|
|
30
|
+
return response.status === 429;
|
|
31
|
+
}
|
|
32
|
+
function quotaReason(response) {
|
|
33
|
+
return `upstream ${response.status}`;
|
|
34
|
+
}
|
|
35
|
+
async function rebindAfterQuotaResponse(gatewayToken, session, response, route) {
|
|
36
|
+
markAccountExhausted(session.accountId, quotaReason(response));
|
|
37
|
+
const fallback = selectFallbackAccount(session.accountId);
|
|
38
|
+
if (!fallback)
|
|
39
|
+
return undefined;
|
|
40
|
+
let rebound;
|
|
41
|
+
try {
|
|
42
|
+
rebound = await rebindGatewaySession(gatewayToken, fallback, route);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
console.warn(`[llm-gateway] quota fallback rebind failed for ${session.accountId}: ${String(err)}`);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!rebound)
|
|
49
|
+
return undefined;
|
|
50
|
+
await response.body?.cancel().catch(() => { });
|
|
51
|
+
console.warn(`[llm-gateway] account ${session.accountId} returned ${response.status}; ` +
|
|
52
|
+
`rebinding session to ${fallback.id} (${fallback.provider})`);
|
|
53
|
+
return rebound;
|
|
54
|
+
}
|
|
55
|
+
async function handleMessagesViaAnthropic(c, session, body) {
|
|
56
|
+
const upstreamUrl = `${ANTHROPIC_BASE}/v1/messages`;
|
|
57
|
+
const requestHeaders = {
|
|
58
|
+
"anthropic-version": c.req.header("anthropic-version") ?? "2023-06-01",
|
|
59
|
+
};
|
|
60
|
+
for (const h of PASSTHROUGH_REQUEST_HEADERS) {
|
|
61
|
+
const v = c.req.header(h);
|
|
62
|
+
if (v !== undefined)
|
|
63
|
+
requestHeaders[h] = v;
|
|
64
|
+
}
|
|
65
|
+
requestHeaders["x-api-key"] = session.token;
|
|
66
|
+
let upstream;
|
|
67
|
+
try {
|
|
68
|
+
upstream = await fetch(upstreamUrl, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: requestHeaders,
|
|
71
|
+
body,
|
|
72
|
+
// @ts-expect-error Node 18+ fetch supports duplex for streaming
|
|
73
|
+
duplex: "half",
|
|
74
|
+
signal: c.req.raw.signal,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (err.name === "AbortError") {
|
|
79
|
+
return new Response(null, { status: 499 });
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
const responseHeaders = {};
|
|
84
|
+
for (const h of PASSTHROUGH_RESPONSE_HEADERS) {
|
|
85
|
+
const v = upstream.headers.get(h);
|
|
86
|
+
if (v !== null)
|
|
87
|
+
responseHeaders[h] = v;
|
|
88
|
+
}
|
|
89
|
+
// Pipe stream directly — never buffer
|
|
90
|
+
return new Response(upstream.body, {
|
|
91
|
+
status: upstream.status,
|
|
92
|
+
headers: responseHeaders,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function dispatchToSessionAccount(c, session, body) {
|
|
96
|
+
if (usesOpenAIProvider(session.provider)) {
|
|
97
|
+
return handleMessagesViaOpenAI(c, {
|
|
98
|
+
token: session.token,
|
|
99
|
+
gatewayToken: session.gatewayToken,
|
|
100
|
+
accountId: session.accountId,
|
|
101
|
+
sessionId: session.sessionId,
|
|
102
|
+
}, body);
|
|
103
|
+
}
|
|
104
|
+
return handleMessagesViaAnthropic(c, session, body);
|
|
105
|
+
}
|
|
106
|
+
function routeFromRequestBody(body) {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(new TextDecoder().decode(body));
|
|
109
|
+
return typeof parsed.model === "string"
|
|
110
|
+
? resolveModelRoute(parsed.model)
|
|
111
|
+
: undefined;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export async function handleMessages(c) {
|
|
118
|
+
const gatewayToken = gatewayTokenFromRequest(c);
|
|
119
|
+
if (!gatewayToken)
|
|
120
|
+
return c.json({ error: "unauthorized" }, 403);
|
|
121
|
+
let session = await lookupToken(gatewayToken);
|
|
122
|
+
if (!session)
|
|
123
|
+
return c.json({ error: "unauthorized" }, 403);
|
|
124
|
+
const body = await c.req.raw.arrayBuffer();
|
|
125
|
+
const route = routeFromRequestBody(body);
|
|
126
|
+
recordSessionRequest(session.sessionId, route);
|
|
127
|
+
const attempted = new Set();
|
|
128
|
+
for (;;) {
|
|
129
|
+
attempted.add(session.accountId);
|
|
130
|
+
const response = await dispatchToSessionAccount(c, session, body);
|
|
131
|
+
if (!isQuotaFallbackResponse(response))
|
|
132
|
+
return response;
|
|
133
|
+
const rebound = await rebindAfterQuotaResponse(gatewayToken, session, response, route);
|
|
134
|
+
if (!rebound || attempted.has(rebound.accountId))
|
|
135
|
+
return response;
|
|
136
|
+
session = rebound;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=proxy-anthropic.js.map
|