@hilbras/omnihilbras 1.71.1 → 1.75.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.
@@ -0,0 +1,619 @@
1
+ import { ProviderError } from '../errors.js';
2
+ /**
3
+ * Token Harbor Web — the chat application at tokenharbor.ai, driven by the Supabase session
4
+ * cookie a signed-in browser already holds.
5
+ *
6
+ * ## Read this before building on it
7
+ *
8
+ * Token Harbor's terms **prohibit** what this adapter does. Their product is itself a gateway,
9
+ * and the metered API key is its architectural guardrail; driving the web chat with a harvested
10
+ * session cookie routes around that guardrail. The two clauses that name it:
11
+ *
12
+ * > **Proxy Reselling:** Utilize the Service to construct a direct, white-labeled proxy
13
+ * > alternative to Token Harbor without adding substantive unique value or without prior written
14
+ * > enterprise authorization.
15
+ * >
16
+ * > ...designed to compromise the security, stability, or **architectural guardrails of the
17
+ * > Token Harbor gateway**.
18
+ *
19
+ * There is a legitimate path — `https://tokenharbor.ai/v1` with a Bearer key, OpenAI-compatible,
20
+ * documented, and free on the `:free` models — and it is the card beside this one. This adapter
21
+ * exists because the operator asked for the session-cookie route with that trade stated, and it
22
+ * carries the same warning in the dashboard catalog (`riskSeverity: 'high'`) so a user meets it
23
+ * before pasting a credential. It is the same shape as the Kiro notice: the vendor's terms and
24
+ * the gateway's behaviour are both on the card, and using it is the user's decision to make.
25
+ *
26
+ * ## What the credential is
27
+ *
28
+ * Auth is Supabase (`auth.tokenharbor.ai`), so the browser cookie is `sb-<ref>-auth-token`,
29
+ * where `<ref>` is the first label of the Supabase host — `auth`, giving `sb-auth-auth-token`.
30
+ * Above Supabase's size limit the value is split into `sb-...-auth-token.0`, `.1`, ... and the
31
+ * chunks are reassembled in order. It is written with `document.cookie`, so unlike ChatGPT Web's
32
+ * `HttpOnly` session it can be read from the Application panel or the Network tab's Cookie
33
+ * header — which is why the guide offers both.
34
+ *
35
+ * ## The wire format is not OpenAI's, and that is the whole adapter
36
+ *
37
+ * A turn is two requests and a decode:
38
+ *
39
+ * 1. `POST /api/direct-chat/sessions` with `{ model, temporary: true }` → `{ session: { id } }`.
40
+ * `temporary` is sent so a stateless gateway does not litter the user's sidebar with a
41
+ * conversation per request.
42
+ * 2. `POST /api/direct-chat/stream` with `{ sessionId, content, model, webSearch, tz }` → an SSE
43
+ * body whose events are **named**, not OpenAI's `choices[].delta`:
44
+ * `chunk` (the answer text in `delta`), `thinking` (reasoning in `delta`), `tool_use`,
45
+ * `citation`, `image`, `file`, `miniapp`, and `done`. `error` carries `{ code, message }`.
46
+ *
47
+ * Only `chunk`, `thinking`, `done` and `error` are consumed. The others are attachments and
48
+ * citations for a UI, and inventing an OpenAI shape for them here would be guessing at a
49
+ * contract this adapter cannot test without the session.
50
+ *
51
+ * ## What is not claimed
52
+ *
53
+ * `capabilities.streaming` is false and no `streamChat` is implemented: the transport is SSE,
54
+ * but the gateway consumes the whole body and returns one completion. A streaming surface would
55
+ * need the chunk boundary semantics verified against a live session first.
56
+ *
57
+ * `healthCheck` reports `verified: 'credential'`. It reads `/api/me/profile`, which proves the
58
+ * session cookie is accepted and nothing more — the same distinction the type exists to force.
59
+ */
60
+ export const tokenHarborWebProviderId = 'tokenharbor-web';
61
+ const WEB_ORIGIN = 'https://tokenharbor.ai';
62
+ const API_BASE = `${WEB_ORIGIN}/api`;
63
+ const PROFILE_URL = `${API_BASE}/me/profile`;
64
+ const SESSIONS_URL = `${API_BASE}/direct-chat/sessions`;
65
+ const STREAM_URL = `${API_BASE}/direct-chat/stream`;
66
+ const DEFAULT_CHAT_MODEL = 'th-rudder:free';
67
+ /** The cookie prefix Supabase mints on the `auth` host. Chunks append `.0`, `.1`, ... */
68
+ const SESSION_COOKIE_PREFIX = 'sb-auth-auth-token';
69
+ /**
70
+ * Headers the web client's own fetch carries without thinking about it.
71
+ *
72
+ * Same-origin `fetch` attaches the cookies and sends an `Origin` for free; from Node both have to
73
+ * be stated. They are not a bot-defeating bluff — Cloudflare sits in front of this host, and a
74
+ * request with no `Origin`, no `Referer` and Node's default `User-Agent` is the shape a challenge
75
+ * is meant to stop. What is sent here is what a browser at this origin sends.
76
+ */
77
+ const FINGERPRINT_HEADERS = {
78
+ Accept: 'text/event-stream, application/json',
79
+ 'Accept-Language': 'en-US,en;q=0.9',
80
+ Origin: WEB_ORIGIN,
81
+ Referer: `${WEB_ORIGIN}/chat`,
82
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
83
+ };
84
+ export const TOKENHARBOR_WEB = {
85
+ origin: WEB_ORIGIN,
86
+ apiBase: API_BASE,
87
+ profileUrl: PROFILE_URL,
88
+ sessionsUrl: SESSIONS_URL,
89
+ streamUrl: STREAM_URL,
90
+ cookiePrefix: SESSION_COOKIE_PREFIX,
91
+ defaultModel: DEFAULT_CHAT_MODEL,
92
+ /** The page a user signs in on and copies the cookie from. */
93
+ signInUrl: `${WEB_ORIGIN}/login`,
94
+ };
95
+ /**
96
+ * The catalog, read from `https://tokenharbor.ai/models` on 2026-10-07.
97
+ *
98
+ * A dated snapshot rather than a promise: the page is the live list and this is what it showed.
99
+ * Prices are per 1M tokens, which is the unit the SDK normalises to and the unit the page itself
100
+ * prints. The two `:free` ids are the ones that bill nothing.
101
+ *
102
+ * No context window is claimed because the page publishes none.
103
+ */
104
+ export const TOKENHARBOR_WEB_MODELS = [
105
+ { id: 'th-rudder:free', label: 'TH-Rudder', family: 'tokenharbor', inputPer1M: 0, outputPer1M: 0 },
106
+ { id: 'claude-fable-5.1', label: 'Claude Fable 5.1', family: 'anthropic', inputPer1M: 10, outputPer1M: 50 },
107
+ { id: 'claude-opus-5.5', label: 'Claude Opus 5.5', family: 'anthropic', inputPer1M: 4, outputPer1M: 20 },
108
+ { id: 'claude-sonnet-5.5', label: 'Claude Sonnet 5.5', family: 'anthropic', inputPer1M: 2, outputPer1M: 10 },
109
+ { id: 'deepseek-v4.1-flash', label: 'DeepSeek V4.1 Flash', family: 'deepseek', inputPer1M: 0.3, outputPer1M: 1.2 },
110
+ { id: 'deepseek-v4.1-flash:free', label: 'DeepSeek V4.1 Flash', family: 'deepseek', inputPer1M: 0, outputPer1M: 0 },
111
+ { id: 'gemini-3.8-flash', label: 'Gemini 3.8 Flash', family: 'google', inputPer1M: 0.75, outputPer1M: 3.75 },
112
+ { id: 'glm-5.3', label: 'GLM-5.3', family: 'z-ai', inputPer1M: 1.4, outputPer1M: 4.4 },
113
+ { id: 'glm-5.3-flash', label: 'GLM 5.3 Flash', family: 'z-ai', inputPer1M: 0.15, outputPer1M: 0.5 },
114
+ { id: 'gpt-6-astra', label: 'GPT-6 Astra', family: 'openai', inputPer1M: 10, outputPer1M: 50 },
115
+ { id: 'gpt-6-luna', label: 'GPT-6 Luna', family: 'openai', inputPer1M: 0.1, outputPer1M: 0.5 },
116
+ { id: 'gpt-6.1-sol', label: 'GPT-6.1 Sol', family: 'openai', inputPer1M: 2, outputPer1M: 10 },
117
+ { id: 'grok-4.7', label: 'Grok 4.7', family: 'xai', inputPer1M: 2, outputPer1M: 6 },
118
+ { id: 'kimi-k3', label: 'Kimi K3', family: 'kimi', inputPer1M: 3, outputPer1M: 15 },
119
+ { id: 'mimo-v2.6-flash', label: 'MiMo V2.6 Flash', family: 'xiaomi', inputPer1M: 0.14, outputPer1M: 1.28 },
120
+ { id: 'mimo-v2.6-flash:free', label: 'MiMo V2.6 Flash', family: 'xiaomi', inputPer1M: 0, outputPer1M: 0 },
121
+ { id: 'mimo-v2.6-pro', label: 'MiMo V2.6 Pro', family: 'xiaomi', inputPer1M: 0.435, outputPer1M: 0.87 },
122
+ { id: 'mistral-large-2512', label: 'Mistral Large 2512', family: 'mistral', inputPer1M: 0.68, outputPer1M: 2.09 },
123
+ { id: 'muse-spark-1-3', label: 'Muse Spark 1.3', family: 'meta', inputPer1M: 1.25, outputPer1M: 4.25 },
124
+ { id: 'qwen3.8-27b', label: 'Qwen3.8 27B', family: 'qwen', inputPer1M: 0.35, outputPer1M: 2.1 },
125
+ { id: 'qwen3.8-flash', label: 'Qwen3.8 Flash', family: 'qwen', inputPer1M: 0.15, outputPer1M: 0.47 },
126
+ { id: 'qwen3.8-max', label: 'Qwen3.8 Max', family: 'qwen', inputPer1M: 2, outputPer1M: 6 },
127
+ ];
128
+ export function allTokenHarborWebModels() {
129
+ return TOKENHARBOR_WEB_MODELS.map((model) => model.id);
130
+ }
131
+ function lookupModel(id) {
132
+ const wanted = id.trim().toLowerCase();
133
+ return TOKENHARBOR_WEB_MODELS.find((model) => model.id.toLowerCase() === wanted);
134
+ }
135
+ /** One place to build a provider error, so every failure carries the id and a safe public message. */
136
+ function fail(code, message, options = {}) {
137
+ return new ProviderError(code, message, {
138
+ providerId: tokenHarborWebProviderId,
139
+ retryable: options.retryable ?? false,
140
+ publicMessage: options.publicMessage ?? message,
141
+ });
142
+ }
143
+ /**
144
+ * Reads the Supabase session cookie out of whatever the user pasted.
145
+ *
146
+ * Two pastes are accepted, because both are things the guide tells people to copy:
147
+ *
148
+ * - the whole `Cookie:` request header (space for the Cloudflare clearance and the other cookies
149
+ * the SSR layer reads), and
150
+ * - just the `sb-auth-auth-token` value or the `sb-auth-auth-token=...` pair, from the
151
+ * Application panel.
152
+ *
153
+ * The chunks are **joined in numeric order**, which is not the order they appear in a header:
154
+ * a large token is stored as `.0`, `.1`, ... and a header that happens to list `.1` before `.0`
155
+ * would otherwise reassemble to a garbage session that fails later as an unexplained refusal.
156
+ */
157
+ export function parseTokenHarborCookieHeader(raw) {
158
+ const trimmed = raw.trim();
159
+ if (!trimmed) {
160
+ throw fail('INVALID_REQUEST', 'Paste the Cookie header or the sb-auth-auth-token value from tokenharbor.ai.', {
161
+ publicMessage: 'Paste the Cookie header or the sb-auth-auth-token value from tokenharbor.ai.',
162
+ });
163
+ }
164
+ // A bare value (no `;`, no `name=`) is the Application panel's single-field copy. Supabase v2
165
+ // stores the session as `base64-<...>`, which is the shape that identifies it as the session
166
+ // rather than one of the sibling cookies; a bare value that is neither that nor the name=value
167
+ // pair is refused rather than guessed at.
168
+ const looksLikeHeader = trimmed.includes(';') || /(?:^|[\s;])[A-Za-z0-9_.-]+=/.test(trimmed);
169
+ if (!looksLikeHeader) {
170
+ if (trimmed.startsWith(`${SESSION_COOKIE_PREFIX}=`))
171
+ return trimmed;
172
+ if (trimmed.startsWith(SESSION_COOKIE_PREFIX))
173
+ return `${SESSION_COOKIE_PREFIX}=${trimmed.slice(SESSION_COOKIE_PREFIX.length).replace(/^=/, '')}`;
174
+ if (trimmed.startsWith('base64-'))
175
+ return `${SESSION_COOKIE_PREFIX}=${trimmed}`;
176
+ throw fail('INVALID_REQUEST', `That does not look like a Token Harbor session cookie. It should start with "${SESSION_COOKIE_PREFIX}" or "base64-".`);
177
+ }
178
+ const pairs = new Map();
179
+ const order = [];
180
+ for (const part of trimmed.split(';')) {
181
+ const piece = part.trim();
182
+ if (!piece)
183
+ continue;
184
+ const eq = piece.indexOf('=');
185
+ if (eq < 0)
186
+ continue;
187
+ const name = piece.slice(0, eq).trim();
188
+ const value = piece.slice(eq + 1).trim();
189
+ if (!name)
190
+ continue;
191
+ if (!pairs.has(name))
192
+ order.push(name);
193
+ pairs.set(name, value);
194
+ }
195
+ const chunkNames = order
196
+ .filter((name) => name === SESSION_COOKIE_PREFIX || name.startsWith(`${SESSION_COOKIE_PREFIX}.`))
197
+ .sort((a, b) => chunkIndex(a, SESSION_COOKIE_PREFIX) - chunkIndex(b, SESSION_COOKIE_PREFIX));
198
+ if (chunkNames.length === 0) {
199
+ throw fail('INVALID_REQUEST', `That Cookie header carries no "${SESSION_COOKIE_PREFIX}" cookie. Sign in at tokenharbor.ai and copy it again.`);
200
+ }
201
+ const session = chunkNames.map((name) => pairs.get(name) ?? '').join('');
202
+ if (!session) {
203
+ throw fail('AUTHENTICATION_FAILED', `The "${SESSION_COOKIE_PREFIX}" cookie is empty, which is what the browser stores when you are signed out. Sign in at tokenharbor.ai and copy it again.`);
204
+ }
205
+ /**
206
+ * **A truncated paste is refused here, by name, rather than forwarded.**
207
+ *
208
+ * Supabase stores `base64-<base64url(JSON session)>`. A session is long, and the two ways people
209
+ * paste one — selecting a single Application-panel row, or copying a wrapped header line —
210
+ * routinely cut it short. A cut-short value is forwarded as a cookie, Token Harbor answers 401,
211
+ * and the user is told to sign in again for a paste that never finished. That is exactly the
212
+ * fault the DeepSeek parser was fixed for (`{"value":null}` treated as a token), one provider
213
+ * over: **the most likely bad paste is answered as the wrong problem.**
214
+ *
215
+ * Decoding is pure Web APIs (`atob`), which the SDK is allowed to reach for, so this adds no
216
+ * Node builtin to a module the dashboard imports.
217
+ */
218
+ const problem = sessionValueProblem(session);
219
+ if (problem)
220
+ throw fail('INVALID_REQUEST', problem);
221
+ // Keep every non-chunk cookie the paste carried — the SSR layer reads the clearance and the
222
+ // rest — and replace the chunks with the single reassembled value.
223
+ const kept = order
224
+ .filter((name) => name !== SESSION_COOKIE_PREFIX && !name.startsWith(`${SESSION_COOKIE_PREFIX}.`))
225
+ .map((name) => `${name}=${pairs.get(name) ?? ''}`);
226
+ return [...kept, `${SESSION_COOKIE_PREFIX}=${session}`].join('; ');
227
+ }
228
+ function chunkIndex(name, prefix) {
229
+ if (name === prefix)
230
+ return 0;
231
+ const suffix = Number(name.slice(prefix.length + 1));
232
+ return Number.isFinite(suffix) ? suffix + 1 : Number.MAX_SAFE_INTEGER;
233
+ }
234
+ /** `base64-` is Supabase's `base64url` cookie encoding, and the prefix their own decoder strips. */
235
+ const SESSION_VALUE_PREFIX = 'base64-';
236
+ function base64UrlDecodeToString(value) {
237
+ try {
238
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
239
+ const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
240
+ const binary = atob(padded);
241
+ // `atob` yields latin1; the JSON is UTF-8, so the bytes are re-decoded rather than mangled.
242
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
243
+ return new TextDecoder().decode(bytes);
244
+ }
245
+ catch {
246
+ return undefined;
247
+ }
248
+ }
249
+ /**
250
+ * Decodes the session cookie and reports what is wrong with it, or `undefined` when it is intact.
251
+ *
252
+ * Returns a sentence rather than a boolean because the two failures need different instructions: a
253
+ * value that does not decode is a **truncated paste**, and a value that decodes but carries no
254
+ * access token is a **shape this adapter does not recognise**. "Sign in again" is wrong for the
255
+ * first — the session is fine, the paste was short.
256
+ */
257
+ export function sessionValueProblem(session) {
258
+ if (!session.startsWith(SESSION_VALUE_PREFIX)) {
259
+ // Not base64url. It may still be a session this service accepts, so it is not refused — but a
260
+ // value that is neither `base64-` nor obviously a JWT is worth naming, because the paste
261
+ // instructions describe the `base64-` form.
262
+ return session.split('.').length === 3
263
+ ? undefined
264
+ : `That session value is not in the shape tokenharbor.ai stores it in — it should start with "${SESSION_VALUE_PREFIX}". Copy the whole value of the cookie, or the whole Cookie header.`;
265
+ }
266
+ const decoded = base64UrlDecodeToString(session.slice(SESSION_VALUE_PREFIX.length));
267
+ if (decoded === undefined) {
268
+ return `That session cookie is not valid base64, which almost always means the paste was cut short. Select the entire cookie value — it is long, and a partially copied one is refused here rather than sent and reported as a dead session.`;
269
+ }
270
+ let parsed;
271
+ try {
272
+ parsed = JSON.parse(decoded);
273
+ }
274
+ catch {
275
+ return `That session cookie was cut short: it decodes to something that is not complete JSON. Copy the whole cookie value (or the whole Cookie header) and paste it again — the session itself is probably fine.`;
276
+ }
277
+ if (!parsed || typeof parsed !== 'object' || typeof parsed.access_token !== 'string') {
278
+ return `That cookie decodes, but it carries no access token, so this adapter does not recognise it as a Token Harbor session. Confirm you copied the cookie named "${SESSION_COOKIE_PREFIX}".`;
279
+ }
280
+ return undefined;
281
+ }
282
+ /** The `expires_at` epoch seconds inside the session, when it is one this adapter can read.
283
+ *
284
+ * Accepts either the bare value or a whole Cookie header, because the two callers have different
285
+ * shapes in hand: the parser has the value, the refusal path has the assembled header.
286
+ */
287
+ export function sessionExpiresAt(sessionOrCookie) {
288
+ const session = sessionValueFrom(sessionOrCookie);
289
+ if (session === undefined)
290
+ return undefined;
291
+ const decoded = base64UrlDecodeToString(session.slice(SESSION_VALUE_PREFIX.length));
292
+ if (decoded === undefined)
293
+ return undefined;
294
+ try {
295
+ const parsed = JSON.parse(decoded);
296
+ return typeof parsed.expires_at === 'number' ? parsed.expires_at : undefined;
297
+ }
298
+ catch {
299
+ return undefined;
300
+ }
301
+ }
302
+ /** The `sb-auth-auth-token` value, from a bare value or a whole Cookie header. */
303
+ function sessionValueFrom(sessionOrCookie) {
304
+ const trimmed = sessionOrCookie.trim();
305
+ if (trimmed.startsWith(SESSION_VALUE_PREFIX))
306
+ return trimmed;
307
+ const match = trimmed.match(new RegExp(`(?:^|;\\s*)${SESSION_COOKIE_PREFIX}=([^;]+)`));
308
+ return match?.[1]?.trim() ?? undefined;
309
+ }
310
+ export function tokenHarborWebCredential(raw) {
311
+ return { type: 'api-key', value: parseTokenHarborCookieHeader(raw) };
312
+ }
313
+ export function tokenHarborWebCookieFromCredential(credential) {
314
+ if (!credential || credential.type !== 'api-key' || !credential.value.trim()) {
315
+ throw fail('AUTHENTICATION_FAILED', 'This Token Harbor Web connection has no session cookie. Sign in again and paste a fresh one.', {
316
+ publicMessage: 'This Token Harbor Web connection has no session cookie. Sign in again and paste a fresh one.',
317
+ });
318
+ }
319
+ return credential.value.trim();
320
+ }
321
+ /** Turns a non-ok status into the refusal it actually is, rather than one blanket code. */
322
+ function refusalFor(status, what, session) {
323
+ if (status === 401 || status === 403) {
324
+ // Read the expiry out of the pasted session so the sentence distinguishes the two cases a
325
+ // 401 covers. Telling somebody to sign in again when nothing is wrong with the session sends
326
+ // them round a loop; telling them the session is fine when it expired wastes the same trip.
327
+ const expiresAt = session ? sessionExpiresAt(session) : undefined;
328
+ const expired = expiresAt !== undefined && expiresAt * 1000 <= Date.now();
329
+ const who = expired
330
+ ? `The pasted session expired at ${new Date(expiresAt * 1000).toISOString()}. Sign in at tokenharbor.ai and copy a fresh cookie.`
331
+ : 'Sign in at tokenharbor.ai again and paste a fresh cookie. A 403 can also be Cloudflare refusing the request rather than the session being dead.';
332
+ return fail('AUTHENTICATION_FAILED', `Token Harbor refused this session while ${what} (${status}). ${who}`);
333
+ }
334
+ if (status === 429) {
335
+ return fail('RATE_LIMITED', `Token Harbor is rate limiting this session (${status}) while ${what}.`, { retryable: true });
336
+ }
337
+ if (status === 402) {
338
+ return fail('PROVIDER_REQUEST_FAILED', `Token Harbor refused ${what}: the account has no balance for this model (402). The ":free" models do not bill.`);
339
+ }
340
+ return fail('PROVIDER_UNAVAILABLE', `Token Harbor answered ${status} while ${what}.`, { retryable: status >= 500 });
341
+ }
342
+ export class TokenHarborWebAdapter {
343
+ id = tokenHarborWebProviderId;
344
+ name = 'Token Harbor Web';
345
+ capabilities = { chat: true, models: true };
346
+ now;
347
+ fetchImpl;
348
+ constructor(options = {}) {
349
+ this.now = options.now ?? (() => Date.now());
350
+ this.fetchImpl = options.fetch ?? fetch;
351
+ }
352
+ /**
353
+ * The dated catalog above, not a request.
354
+ *
355
+ * Token Harbor publishes no public models JSON — the live list is rendered into the `/models`
356
+ * page — so there is nothing to read without a session, and an authenticated read per call
357
+ * would be a cost for a list that changes weekly. The snapshot is what the page showed on the
358
+ * date beside it, and the free-only policy narrows it the way the dialog's checkbox promises.
359
+ */
360
+ listModels(context = {}) {
361
+ const freeOnly = context.importPolicy === 'free';
362
+ return Promise.resolve(TOKENHARBOR_WEB_MODELS.filter((model) => !freeOnly || model.id.endsWith(':free')).map((model) => ({
363
+ id: model.id,
364
+ providerId: this.id,
365
+ displayName: model.label,
366
+ ownedBy: model.family,
367
+ pricing: { inputPer1M: model.inputPer1M, outputPer1M: model.outputPer1M },
368
+ })));
369
+ }
370
+ /**
371
+ * A real round trip to `/api/me/profile`, which answers `{ ok: false }` with a 401 to a session
372
+ * it does not recognise. That is the cheapest endpoint that proves the cookie is accepted, and
373
+ * it is the whole of what this establishes — see `healthCheck`.
374
+ */
375
+ async validateCredential(credential, context = {}) {
376
+ const cookie = tokenHarborWebCookieFromCredential(credential);
377
+ const startedAt = this.now();
378
+ const response = await this.request(PROFILE_URL, { method: 'GET', cookie, signal: context.signal });
379
+ if (!response.ok)
380
+ throw refusalFor(response.status, 'checking the session', cookie);
381
+ return { status: 'valid', checkedAt: new Date(this.now()).toISOString(), latencyMs: this.now() - startedAt };
382
+ }
383
+ /**
384
+ * `verified: 'credential'` — deliberately.
385
+ *
386
+ * Reading the profile proves the cookie is live; it says nothing about whether a model can
387
+ * answer. Claiming `inference` would need a real turn on every 60-second poll, which is a real
388
+ * request against someone else's service on a timer, and the type exists precisely so this
389
+ * distinction is stated rather than implied.
390
+ */
391
+ async healthCheck(context = {}) {
392
+ try {
393
+ await this.validateCredential(context.credential, context);
394
+ return { status: 'healthy', verified: 'credential', checkedAt: new Date(this.now()).toISOString() };
395
+ }
396
+ catch (error) {
397
+ return {
398
+ status: 'unavailable',
399
+ verified: 'credential',
400
+ checkedAt: new Date(this.now()).toISOString(),
401
+ message: error instanceof Error ? error.message : 'The Token Harbor Web session could not be checked.',
402
+ };
403
+ }
404
+ }
405
+ async chat(request, context = {}) {
406
+ const wanted = request.model?.trim() || DEFAULT_CHAT_MODEL;
407
+ const model = lookupModel(wanted);
408
+ if (!model) {
409
+ throw fail('NOT_SUPPORTED', `Token Harbor Web does not offer a model called ${wanted}.`, {
410
+ publicMessage: `Token Harbor Web does not offer a model called ${wanted}. The models it serves are the ones on this page.`,
411
+ });
412
+ }
413
+ const cookie = tokenHarborWebCookieFromCredential(context.credential);
414
+ const sessionId = await this.createSession(model.id, cookie, context.signal);
415
+ // The stream endpoint takes one `content` string, so the conversation is flattened into it.
416
+ // A fresh temporary session per request holds no history server-side, so nothing is doubled.
417
+ const content = flattenMessages(request.messages);
418
+ if (!content)
419
+ throw fail('INVALID_REQUEST', 'There is no message to send to Token Harbor Web.');
420
+ const response = await this.request(STREAM_URL, {
421
+ method: 'POST',
422
+ cookie,
423
+ signal: context.signal,
424
+ body: {
425
+ sessionId,
426
+ content,
427
+ model: model.id,
428
+ webSearch: 'auto',
429
+ tz: timeZone(),
430
+ },
431
+ });
432
+ if (!response.ok)
433
+ throw refusalFor(response.status, 'answering the completion', cookie);
434
+ const answer = decodeTokenHarborStream(await readBodyCapped(response));
435
+ if (answer.error) {
436
+ throw fail(answer.error.code === 'unauthorized' ? 'AUTHENTICATION_FAILED' : 'PROVIDER_REQUEST_FAILED', `Token Harbor refused the turn: ${answer.error.message}`);
437
+ }
438
+ if (!answer.content && !answer.reasoning) {
439
+ throw fail('PROVIDER_REQUEST_FAILED', 'Token Harbor answered, but the stream carried no text. Its response format may have changed.');
440
+ }
441
+ if (!answer.finished) {
442
+ // A body that closes without `done` was cut off mid-generation. Reporting the fragment as a
443
+ // clean `stop` is the worst version: it looks like a model that finished and the ending is
444
+ // silently missing.
445
+ throw fail('PROVIDER_REQUEST_FAILED', `Token Harbor's stream ended before it finished — the answer was cut off after ${answer.content.length} characters. This is usually a dropped connection or an expired session. Try again.`);
446
+ }
447
+ const message = {
448
+ role: 'assistant',
449
+ content: answer.content,
450
+ ...(answer.reasoning ? { reasoningContent: answer.reasoning } : {}),
451
+ };
452
+ return {
453
+ id: `chatcmpl-${Math.floor(this.now() / 1000)}-${Math.random().toString(36).slice(2, 8)}`,
454
+ providerId: this.id,
455
+ model: model.id,
456
+ createdAt: new Date(this.now()).toISOString(),
457
+ message,
458
+ finishReason: 'stop',
459
+ };
460
+ }
461
+ async createSession(model, cookie, signal) {
462
+ const response = await this.request(SESSIONS_URL, {
463
+ method: 'POST',
464
+ cookie,
465
+ signal,
466
+ // `temporary` keeps the conversation out of the user's sidebar: a gateway request is not a
467
+ // chat they opened, and one row per API call is a side effect nobody asked for.
468
+ body: { model, temporary: true },
469
+ });
470
+ if (!response.ok)
471
+ throw refusalFor(response.status, 'starting a chat session', cookie);
472
+ const json = (await response.json().catch(() => ({})));
473
+ const id = json.session?.id;
474
+ if (typeof id !== 'string' || !id)
475
+ throw fail('PROVIDER_REQUEST_FAILED', 'Token Harbor started no chat session.');
476
+ return id;
477
+ }
478
+ request(url, options) {
479
+ return this.fetchImpl(url, {
480
+ method: options.method,
481
+ headers: {
482
+ ...FINGERPRINT_HEADERS,
483
+ Cookie: options.cookie,
484
+ ...(options.body === undefined ? {} : { 'Content-Type': 'application/json' }),
485
+ },
486
+ ...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }),
487
+ ...(options.signal ? { signal: options.signal } : {}),
488
+ });
489
+ }
490
+ }
491
+ function timeZone() {
492
+ try {
493
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;
494
+ }
495
+ catch {
496
+ return undefined;
497
+ }
498
+ }
499
+ function messageText(content) {
500
+ if (typeof content === 'string')
501
+ return content;
502
+ if (!Array.isArray(content))
503
+ return '';
504
+ return content
505
+ .map((part) => {
506
+ if (typeof part === 'string')
507
+ return part;
508
+ if (part && typeof part === 'object' && 'text' in part)
509
+ return String(part.text ?? '');
510
+ return '';
511
+ })
512
+ .join('');
513
+ }
514
+ /**
515
+ * Flattens the message list into the single `content` string the endpoint takes.
516
+ *
517
+ * Labelled rather than concatenated: a bare join of `["You are terse.", "2+2?"]` loses which was
518
+ * which, and the model then answers as if the instruction were part of the question.
519
+ */
520
+ export function flattenMessages(messages) {
521
+ const kept = messages
522
+ .map((message) => ({ role: message.role, text: messageText(message.content).trim() }))
523
+ .filter((message) => message.text);
524
+ const system = kept.filter((message) => message.role === 'system');
525
+ const rest = kept.filter((message) => message.role !== 'system');
526
+ const lines = [];
527
+ if (system.length > 0)
528
+ lines.push('Follow these instructions:', ...system.map((message) => `- ${message.text}`), '');
529
+ for (const message of rest) {
530
+ lines.push(`${message.role === 'assistant' ? 'Assistant' : 'User'}: ${message.text}`);
531
+ }
532
+ return lines.join('\n').trim();
533
+ }
534
+ /**
535
+ * Reads a completed answer out of Token Harbor's named-event SSE body.
536
+ *
537
+ * The client's own parser is the source of truth for the shapes, and it is not OpenAI's:
538
+ *
539
+ * ```text
540
+ * event: chunk
541
+ * data: {"delta":"working"}
542
+ *
543
+ * event: thinking
544
+ * data: {"delta":"..."}
545
+ *
546
+ * event: done
547
+ * data: {}
548
+ * ```
549
+ *
550
+ * `chunk` is the answer text and `thinking` is the reasoning, split into their own events — so a
551
+ * model that thinks before answering does not have its reasoning read as the reply. `error`
552
+ * carries `{ code, message }` and is returned rather than thrown so the caller can name the code.
553
+ *
554
+ * **`done` is what makes a truncated body detectable.** A stream that closes without it was cut
555
+ * off, and the caller reports that instead of handing back half an answer labelled complete.
556
+ */
557
+ export function decodeTokenHarborStream(body) {
558
+ let content = '';
559
+ let reasoning = '';
560
+ let finished = false;
561
+ let error;
562
+ for (const block of body.split('\n\n')) {
563
+ let event = 'message';
564
+ let data = '';
565
+ for (const line of block.split('\n')) {
566
+ if (line.startsWith('event:'))
567
+ event = line.slice(6).trim();
568
+ else if (line.startsWith('data:'))
569
+ data += line.slice(5).trim();
570
+ }
571
+ if (!data) {
572
+ if (event === 'done')
573
+ finished = true;
574
+ continue;
575
+ }
576
+ let parsed;
577
+ try {
578
+ parsed = JSON.parse(data);
579
+ }
580
+ catch {
581
+ continue;
582
+ }
583
+ if (event === 'error') {
584
+ error = {
585
+ code: typeof parsed.code === 'string' ? parsed.code : 'error',
586
+ message: typeof parsed.message === 'string' ? parsed.message : 'Token Harbor refused the turn.',
587
+ };
588
+ continue;
589
+ }
590
+ const delta = typeof parsed.delta === 'string' ? parsed.delta : '';
591
+ if (!delta) {
592
+ if (event === 'done')
593
+ finished = true;
594
+ continue;
595
+ }
596
+ if (event === 'thinking')
597
+ reasoning += delta;
598
+ else if (event === 'chunk')
599
+ content += delta;
600
+ if (event === 'done')
601
+ finished = true;
602
+ }
603
+ return { content, reasoning, finished, ...(error ? { error } : {}) };
604
+ }
605
+ /**
606
+ * Reads the body with a hard cap.
607
+ *
608
+ * An SSE body from a proxy that never closes is an unbounded read, and a cap that silently
609
+ * truncates is worse than one that refuses — so the cap throws and the caller reports it.
610
+ */
611
+ const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
612
+ async function readBodyCapped(response) {
613
+ const buffer = await response.arrayBuffer();
614
+ if (buffer.byteLength > MAX_RESPONSE_BYTES) {
615
+ throw fail('PROVIDER_REQUEST_FAILED', `Token Harbor's reply exceeded ${MAX_RESPONSE_BYTES} bytes and was refused rather than truncated.`);
616
+ }
617
+ return new TextDecoder().decode(buffer);
618
+ }
619
+ //# sourceMappingURL=tokenharbor-web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenharbor-web.js","sourceRoot":"","sources":["../../src/adapters/tokenharbor-web.ts"],"names":[],"mappings":"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"}