Files
OmniRoute/open-sse/services/sessionPool/webExecutorWrapper.ts
Diego Rodrigues de Sa e Souza f1eb0b8357 refactor(providers): remove the Puter provider at its owner's request (#10210)
Remove the Puter provider (id `puter`, alias `pu`) entirely, at the
request of Puter's owner, Nariman Jelveh:

- registry entry (open-sse/config/providers/registry/puter/) and
  PuterExecutor (open-sse/executors/puter.ts), with their registrations
- API-key preset card (gateways.ts), provider icon and public SVG asset
- 33 free-model catalog entries (pool `puter`)
- authHint i18n key across all 43 UI locales
- credential-requirement frozen-list entry and related comments
- docs: ARCHITECTURE, CODEBASE_DOCUMENTATION, FREE_TIERS (removal note),
  PROVIDER_REFERENCE regenerated (337 providers), translated doc mirrors,
  llm.txt + its 42 i18n mirrors, README/AGENTS/package.json counts
  (338→337 providers, 144→145 migrations) and the 5 canonical SVGs
- migration 152 cleans up stored puter connections/keys/custom models;
  historical usage records are preserved (same principle as migration 151)
- regression guard: tests/unit/puter-provider-removed.test.ts; puter
  fixtures in shared tests swapped for neutral providers; translate-path
  golden snapshot regenerated

Historical CHANGELOG mentions are intentionally preserved; the removal
carries its own CHANGELOG entry.

Co-authored-by: backryun <bakryun0718@proton.me>
2026-08-12 16:54:50 -03:00

133 lines
3.6 KiB
TypeScript

/**
* WebExecutorWrapper — Wraps any web executor with session pool support
*
* This is the integration bridge between the session pool and OmniRoute's
* executor system. It intercepts the fetch() call to add session-pool
* headers (fingerprint-based User-Agent, Sec-CH-UA, etc.) and handles
* 429/5xx responses with pool-level cooldown management.
*
* Future: For cookie-based providers (ChatGPT Web, DeepSeek Web, etc.)
* the wrapper will also inject cookies from the Playwright-authenticated
* session.
*/
import { Session } from "./session.ts";
import { SessionPool } from "./sessionPool.ts";
export interface WebExecutorRequest {
url: string;
method?: string;
headers?: Record<string, string>;
body?: string;
signal?: AbortSignal;
[key: string]: unknown;
}
export interface WebExecutorResponse {
status: number;
statusText?: string;
headers?: Record<string, string>;
body: string;
ok: boolean;
[key: string]: unknown;
}
export interface WebExecutorFn {
(req: WebExecutorRequest): Promise<WebExecutorResponse>;
}
/**
* Decorate a web executor function with session pool support.
*
* Before the underlying executor fires:
* 1. Acquires a session from the pool (reusable, fingerprint-isolated)
* 2. Merges session headers (UA + Sec-CH-UA) into the request
* 3. Handles 429 → pool cooldown, 5xx → session death
*
* For zero-auth providers like Pollinations this is all
* that's needed for "truly unlimited" — the fingerprint rotation alone
* defeats burst-based rate limiting.
*/
export function withSessionPool(
executor: WebExecutorFn,
pool: SessionPool,
options?: {
/** When true, wraps the response body for error handling */
wrapResponse?: boolean;
}
): WebExecutorFn {
const wrapResponse = options?.wrapResponse ?? true;
return async (req: WebExecutorRequest): Promise<WebExecutorResponse> => {
// Acquire session from pool (blocking with backoff)
let session: Session | null = null;
try {
session = await pool.acquireBlocking();
} catch (err) {
return {
status: 503,
statusText: "Service Unavailable",
body: JSON.stringify({
error: "session_pool_exhausted",
message: `[SessionPool:${pool.provider}] ${(err as Error).message}`,
}),
ok: false,
headers: {},
};
}
try {
// Build request with session fingerprint headers
const sessionHeaders = session.buildHeaders(req.headers);
const poolReq: WebExecutorRequest = {
...req,
headers: sessionHeaders,
};
// Execute the underlying web request
const res = await executor(poolReq);
// Handle response status
if (res.status === 429) {
pool.reportCooldown(session);
if (wrapResponse) {
return {
...res,
body: JSON.stringify({
error: "pool_rate_limited",
message: `[SessionPool:${pool.provider}] Rate limited, session ${session.id} in cooldown`,
}),
};
}
return res;
}
if (res.status >= 500) {
pool.reportDead(session);
return res;
}
// Success
pool.reportSuccess(session);
pool.totalRequests++;
return res;
} catch (err) {
// Network error — cooldown, not dead (may be transient)
pool.reportCooldown(session);
return {
status: 502,
statusText: "Bad Gateway",
body: JSON.stringify({
error: "pool_network_error",
message: (err as Error).message,
}),
ok: false,
headers: {},
};
} finally {
session.release();
}
};
}