Compare commits

..

5 Commits

Author SHA1 Message Date
diegosouzapw
286a5ce283 Merge remote-tracking branch 'origin/release/v3.8.50' into feat/radar-key-input 2026-08-08 06:36:38 -03:00
diegosouzapw
ffa5105a74 Merge remote-tracking branch 'origin/release/v3.8.50' into feat/radar-key-input 2026-08-08 01:37:17 -03:00
diegosouzapw
aa701e14b4 docs(radar): close the paste-key-input known gap
RADAR.md documented a known gap: the activation screen had no
dedicated key-paste input, only the two claim/plans buttons. That gap
is closed — describe the new input, the combined opt-in+supporterKey
submission, and the masked-key "already activated" state instead.
2026-08-08 00:18:53 -03:00
diegosouzapw
0baf08e002 feat(dashboard): paste-key input on the Radar activation screen
The Radar activation screen had opt-in and the two "get a key" claim
buttons, but nowhere to paste a key someone already has — the last
piece of the supporter flow. Add the field to the activation screen
itself, as the primary path: pasting a key and submitting sends
POST /api/radar/settings with { optIn: true, supporterKey } together,
so pasting a valid key both sets it and unlocks the screen in one step.

Client-side format validation (via the shared isValidSupporterKeyFormat
helper) is a UX nicety only; the server's Zod schema already validates
authoritatively. When a key is already set (hasSupporterKey from
GET /api/radar/settings — e.g. set out of band before this UI existed),
the screen shows the masked form instead of an empty input, with a
"change key" control to paste a new one; the raw key is never
displayed. The existing plain "Activate" button (no key, community
tier) and the two claim/plans buttons are unchanged and still present
below, so all three paths to this screen coexist.

Adds 4 new i18n keys (keySectionTitle, keyInvalidFormatError,
activateWithKeyButton, changeKeyButton) with an English fallback across
all 43 locale files (172 entries) — no __MISSING__ sentinel, no price.
2026-08-08 00:18:47 -03:00
diegosouzapw
5a54a733c1 feat(radar): shared supporter-key format validator
Extract the "omr_" + 40 hex supporter-key regex out of the
POST /api/radar/settings Zod schema into a pure, client-safe helper
(src/lib/radar/supporterKey.ts) so the format rule lives in exactly one
place and the upcoming activation-screen input can reuse it for a
UX-only pre-check. Server-side Zod validation stays authoritative.

Adds regression coverage: both directions of the format check, a
combined opt-in+supporterKey POST persisting both fields with the key
always masked (never raw) in either the POST or GET response body, and
a flag-off inertia case for the same combined payload shape.
2026-08-08 00:18:35 -03:00
36 changed files with 199 additions and 2144 deletions

View File

@@ -1 +0,0 @@
- feat(providers): add Muse Code CLI provider preset (#9544)

View File

@@ -81,7 +81,7 @@
},
"open-sse/handlers/search.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 33
"count": 34
}
},
"open-sse/handlers/sseParser.ts": {

View File

@@ -299,84 +299,36 @@ auth state — only the masked form and a `hasSupporterKey` boolean.
## Referral links (free credits)
Referral links are served from a **standalone, always-current** feed —
`GET /v1/referrals/latest` — separate from the catalog feed. This is deliberate: the
catalog feed on the community tier is a snapshot that can be up to 30 days old, so a
referral link extracted from it used to lag the server's real link list by the same
amount (a newly-added referral wouldn't reach a free/community user for up to a month).
The referrals feed removes that delay by syncing on its own, much shorter cadence.
The server-published feed carries a `referrals` section (server-side D28 work, already
in production — this section documents the **client** consumption only):
```ts
// GET /v1/referrals/latest response body (Ed25519-signed, same pinned key as
// the catalog feed):
{
feed: "omniroute-radar-referrals",
schemaVersion: 1,
generatedAt: string, // ISO — deterministic: max(updatedAt) across referral
// links, so two identical requests produce the exact
// same signed bytes/signature
referrals: {
fixed: RadarReferral[], // present in EVERY tier, including no-auth/community
campaigns: RadarReferral[], // only populated for a valid live (supporter) Bearer
// key; no-auth/expired-key requests get []
},
referrals: {
fixed: RadarReferral[], // present in EVERY tier, including community
campaigns: RadarReferral[], // only populated on the live (supporter) tier;
// the community artifact always publishes []
}
// RadarReferral = { provider, url, kind: "fixo" | "campanha", validUntil,
// requiredAction, isDefault }
```
Unlike the catalog feed, this body carries no `tier` field at all — the server decides
what to include per-request based on the `Authorization` key, so the
`x-omniroute-feed-tier` response header is the ONLY source for the served tier
(`referralsSync.ts::syncRadarReferrals`); an absent/unrecognized header degrades to
`"community"`, the least-privileged assumption. `RadarReferralsFeedSchema`
(`src/lib/radar/referralsFeedSchema.ts`) validates the whole body, reusing the same
per-referral `RadarReferralSchema` exported from `feedSchema.ts` so both feeds validate
individual referrals identically. Every `RadarReferral.url` must be `https://` — a
`http://` url fails schema validation.
The OLD catalog-embedded `referrals` field on `RadarFeedSchema` (`feedSchema.ts`) is
kept for backward-compat with already-cached catalog feeds, but `getRadarReferrals()`
no longer reads it — see [Accessor](#accessor) below.
### Sync
`syncRadarReferrals()` (`src/lib/radar/referralsSync.ts`) is the ONLY module that
touches the network for referrals, mirroring `syncRadar()`'s contract exactly: flag off
→ `disabled`; opt-in false → `opt_out`; downloads `${RADAR_FEED_URL}/v1/referrals/latest`
(same `RADAR_FEED_URL`/`RADAR_FEED_PUBKEY` fork overrides as the catalog), verifies the
Ed25519 signature over the exact response bytes (`verifyFeedBytes`), validates against
`RadarReferralsFeedSchema`, and caches into the `radar_referrals_cache` table
(migration `142_radar_referrals_cache.sql`) — a table entirely separate from the
catalog's `radar_feed_cache`. A 10 MB response cap and a `generatedAt` floor (an
incoming feed with a `generatedAt` no newer than the cached one is treated as `stale`
and never overwrites the cache — guards against a replay of an older signed artifact)
mirror the catalog sync's own `MAX_FEED_BYTES`/version-floor guards. Never throws —
always returns a status object; errors never carry a stack trace in `reason`.
Two triggers keep the referrals cache warm, both independent of the catalog's own
24h cadence:
- **Sync-on-read** — `GET /api/radar/referrals` itself calls `syncRadarReferrals()`
inline whenever the cache is missing or older than `REFERRALS_STALE_MS` (1h,
`shouldSyncReferralsOnRead()`), before serving the response. This is what makes fixed
links "always current" for the very next dashboard load, without waiting on any
background timer.
- **Scheduler side-sync** — `radarSchedulerTick()` (`scheduler.ts`) independently
evaluates referrals staleness on the same hourly tick used for the catalog, calling
`syncRadarReferrals()` when due. This runs regardless of whether the catalog itself
was due that tick, and never affects `RadarTickResult`'s shape (best-effort side
effect only, swallowed on error).
The client never decides which tier it received or which referrals belong in which
tier — the server already publishes two artifacts (`live`/`community`) with
`campaigns` gated server-side, same principle as the [tiers](#tiers-community-and-live)
section above. `RadarFeedSchema` (`src/lib/radar/feedSchema.ts`) validates `referrals`
as a whole-object `.default({fixed:[],campaigns:[]})`, and `campaigns` defaults
independently inside it — so a feed cached before this section existed on the server
still parses cleanly, and `campaigns` alone can also be absent without failing
validation. Every `RadarReferral.url` must be `https://` — a `http://` url fails
schema validation.
### Accessor
`src/lib/radar/index.ts` exports two read-only accessors, both never throwing (same
defensive contract as `getRadarCatalog()` — flag off, no cache, or a corrupt cached
defensive contract as `getRadarCatalog()` — flag off, no cache, or a corrupt/old cached
payload all resolve to the empty shape instead of an error):
- `getRadarReferrals()` → `{ fixed: RadarReferral[], campaigns: RadarReferral[] }`,
reading from `radar_referrals_cache` (via `getRadarReferralsCache()`) and validating
through `RadarReferralsFeedSchema` — **not** the catalog cache.
- `getRadarReferrals()` → `{ fixed: RadarReferral[], campaigns: RadarReferral[] }`.
- `getDefaultReferralFor(provider)` → the `fixed` referral with `isDefault: true` for
that provider, or `null`. Only looks at `fixed` — a campaign is never used as a
provider's "default" link.
@@ -391,13 +343,11 @@ server-only; the providers dashboard imports `referrals.ts` directly instead of
### `GET /api/radar/referrals`
Follows the exact same gate order as every other Radar route: `RADAR_ENABLED` off →
`404` (checked first, byte-identical inertia); unauthenticated → `401`; otherwise
triggers a sync-on-read (see above) when stale, then `200` with
`{ fixed, campaigns, tier }` — `tier` comes straight from the (possibly just-refreshed)
cache row and is purely informative (drives the UI's soft upsell copy below). Never
proxies the feed server directly — the route's own source contains no `fetch(` call;
the network only ever happens inside `syncRadarReferrals()`, same local-cache-only
principle as `/api/radar/catalog`.
`404` (checked first, byte-identical inertia); unauthenticated → `401`; otherwise `200`
with `{ fixed, campaigns, tier }` — `tier` comes straight from the cache row and is
purely informative (drives the UI's soft upsell copy below), the route does no
gating of its own. Never proxies the feed server — same local-cache-only contract as
`/api/radar/catalog`.
### Dashboard UI — "Free credits" tab on `/dashboard/radar`
@@ -465,16 +415,6 @@ automatically (`getFeedPublicKeys()` in `src/lib/radar/pinnedKeys.ts`), and vers
comparison, schema validation, and the merge rules apply identically to a self-hosted
feed.
Referral links (see [Referral links (free credits)](#referral-links-free-credits)
above) are a separate, optional artifact: a fork that only serves `/v1/catalog/latest`
still works fully — `syncRadarReferrals()` degrades to `{ status: "error" }` on a `404`
from `/v1/referrals/latest` and the cache simply stays empty, so
`GET /api/radar/referrals` keeps returning `{ fixed: [], campaigns: [], tier: null }`
instead of failing the rest of the page. To also offer referral links, serve
`GET /v1/referrals/latest` satisfying `RadarReferralsFeedSchema`
(`src/lib/radar/referralsFeedSchema.ts`) and sign it with the same Ed25519 key pair as
the catalog feed.
---
## Related docs

View File

@@ -225,7 +225,6 @@ import { digitaloceanProvider } from "./registry/digitalocean/index.ts";
import { hcnsecProvider } from "./registry/hcnsec/index.ts";
import { promptqlProvider } from "./registry/promptql/index.ts";
import { hyperagentProvider } from "./registry/hyperagent/index.ts";
import { muse_codeProvider } from "./registry/muse-code/index.ts";
export const REGISTRY: Record<string, RegistryEntry> = {
aimlapi: aimlapiProvider,
@@ -452,6 +451,5 @@ export const REGISTRY: Record<string, RegistryEntry> = {
hcnsec: hcnsecProvider,
promptql: promptqlProvider,
hyperagent: hyperagentProvider,
"muse-code": muse_codeProvider,
unorouter: unorouterProvider,
};

View File

@@ -1,106 +0,0 @@
import type { RegistryEntry } from "../../shared.ts";
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
/**
* Muse Code CLI — Meta's agentic coding tool.
*
* Wire format: OpenAI Responses API (POST /responses).
* Auth: Bearer token from META_API_KEY env var.
* Reasoning efforts: xhigh/ultra -> high (handled generically).
*
* @see https://github.com/joymadhu49/muse-openrouter-shim
*/
export const muse_codeProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
id: "muse-code",
alias: "mc",
passthroughModels: true,
defaultContextLength: 200000,
models: [
{
id: "llama-4-maverick",
name: "Llama 4 Maverick",
contextLength: 1048576,
maxOutputTokens: 131072,
supportsReasoning: true,
supportsXHighEffort: true,
toolCalling: true,
supportsVision: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs", "logitBias"],
},
{
id: "llama-4-scout",
name: "Llama 4 Scout",
contextLength: 1048576,
maxOutputTokens: 131072,
supportsReasoning: true,
supportsXHighEffort: true,
toolCalling: true,
supportsVision: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs", "logitBias"],
},
{
id: "llama-3.3-70b",
name: "Llama 3.3 70B",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
{
id: "llama-3.1-405b",
name: "Llama 3.1 405B",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
{
id: "llama-3.1-70b",
name: "Llama 3.1 70B",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
{
id: "llama-3.1-8b",
name: "Llama 3.1 8B",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
{
id: "llama-3.2-90b-vision",
name: "Llama 3.2 90B Vision",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
supportsVision: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
{
id: "llama-3.2-11b-vision",
name: "Llama 3.2 11B Vision",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: false,
toolCalling: true,
supportsVision: true,
targetFormat: "openai-responses",
unsupportedParams: ["logprobs", "topLogprobs"],
},
],
});

View File

@@ -309,7 +309,6 @@ export function stripStoredItemReferences(body: Record<string, unknown>): void {
function stripOrphanedCodexFunctionCallOutputs(body: Record<string, unknown>): void {
if (!Array.isArray(body.input)) return;
const input = body.input;
// A previous_response_id delegates history resolution to the upstream
// Responses service, so a matching function_call may legitimately live in
// that remote response rather than in the local input array.
@@ -318,7 +317,7 @@ function stripOrphanedCodexFunctionCallOutputs(body: Record<string, unknown>): v
const callIds = new Set<string>();
let outputCount = 0;
for (const item of input) {
for (const item of body.input) {
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
const record = item as Record<string, unknown>;
@@ -342,7 +341,9 @@ function stripOrphanedCodexFunctionCallOutputs(body: Record<string, unknown>): v
}
if (outputCount === 0) return;
const filteredInput = input.filter((item) => {
const before = body.input.length;
body.input = body.input.filter((item) => {
if (!item || typeof item !== "object" || Array.isArray(item)) return true;
const record = item as Record<string, unknown>;
if (record.type === "function_call_output" && typeof record.call_id === "string") {
@@ -351,8 +352,7 @@ function stripOrphanedCodexFunctionCallOutputs(body: Record<string, unknown>): v
return true;
});
const removedCount = input.length - filteredInput.length;
body.input = filteredInput;
const removedCount = before - body.input.length;
if (removedCount > 0) {
console.debug(
`[Codex] stripOrphanedCodexFunctionCallOutputs: removed ${removedCount} orphaned function_call_output item(s)`

View File

@@ -61,7 +61,7 @@ type KiroStreamState = {
contextUsagePercentage?: number;
hasContextUsage?: boolean;
hasMeteringEvent?: boolean;
usage?: Partial<UsageSummary>;
usage?: UsageSummary;
hasReasoningContent?: boolean;
reasoningChunkCount?: number;
// Inline-thinking splitter state (populated only when thinkingExpected=true).
@@ -185,7 +185,8 @@ function resolveKiroMaxInputTokens(model: string): number {
* inflate `total_tokens`.
*/
function ensureKiroUsage(state: KiroStreamState, model: string) {
if (state.usage?.total_tokens !== undefined) return;
if (state.usage) return;
const estimatedOutputTokens =
state.totalContentLength && state.totalContentLength > 0
? Math.max(1, Math.floor(state.totalContentLength / 4))
@@ -197,11 +198,11 @@ function ensureKiroUsage(state: KiroStreamState, model: string) {
: 0;
if (estimatedTotalTokens <= 0 && estimatedOutputTokens <= 0) return;
// Without a percentage there is no total to split, so the output estimate is
// all that is known and stands on its own.
if (estimatedTotalTokens <= 0) {
state.usage = {
...state.usage,
prompt_tokens: 0,
completion_tokens: estimatedOutputTokens,
total_tokens: estimatedOutputTokens,
@@ -212,7 +213,6 @@ function ensureKiroUsage(state: KiroStreamState, model: string) {
const promptTokens = Math.max(0, estimatedTotalTokens - estimatedOutputTokens);
state.usage = {
...state.usage,
prompt_tokens: promptTokens,
completion_tokens: estimatedOutputTokens,
total_tokens: promptTokens + estimatedOutputTokens,

View File

@@ -199,10 +199,10 @@ describe("TierResolver", () => {
]);
// Observable effect of the cache: the duplicate resolves to the same tier and only
// ONE entry is memoized (getTierStats counts cache entries, not classify calls).
expect(results).toHaveLength(2);
expect(results[0].tier).toBe(results[1].tier);
assert.equal(results.length, 2);
assert.equal(results[0].tier, results[1].tier);
const stats = getTierStats();
expect(stats.free + stats.cheap + stats.premium).toBe(1);
assert.equal(stats.free + stats.cheap + stats.premium, 1);
});
});

View File

@@ -372,9 +372,7 @@ export function translateRequest(
preserveReasoningContent: isReasoner,
// Per-provider/model preserveVideoUrl flag from compat overrides.
// Falls back to true for moonshot/kimi when unset (legacy behavior).
preserveVideoUrl:
getModelPreserveVideoUrl(normalizedProvider, normalizedModel) ??
(normalizedProvider === "moonshot" || normalizedProvider === "kimi"),
preserveVideoUrl: getModelPreserveVideoUrl(normalizedProvider, options?.model ?? "") ?? (normalizedProvider === "moonshot" || normalizedProvider === "kimi"),
});
}

60
package-lock.json generated
View File

@@ -27,11 +27,12 @@
"@xyflow/react": "^12.11.1",
"axios": "^1.16.1",
"bcryptjs": "^3.0.3",
"better-sqlite3": "^13.0.2",
"bottleneck": "^2.19.5",
"clsx": "^2.1.1",
"commander": "^15.0.0",
"csv-stringify": "^6.7.0",
"dompurify": "^3.4.13",
"dompurify": "^3.4.12",
"express": "^5.2.1",
"fetch-socks": "^1.3.3",
"fflate": "^0.8.3",
@@ -103,7 +104,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/better-sqlite3": "^7.6.13",
"@types/bun": "latest",
"@types/bun": "*",
"@types/node": "^26.1.0",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
@@ -462,9 +463,9 @@
}
},
"node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
@@ -3075,9 +3076,9 @@
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
@@ -12639,9 +12640,9 @@
}
},
"node_modules/@yarnpkg/parsers/node_modules/js-yaml": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
@@ -16979,9 +16980,9 @@
"license": "MIT"
},
"node_modules/dompurify": {
"version": "3.4.13",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
"integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
"version": "3.4.12",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
"integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -25196,9 +25197,9 @@
}
},
"node_modules/lockfile-lint/node_modules/js-yaml": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
@@ -26005,9 +26006,9 @@
}
},
"node_modules/mermaid": {
"version": "11.16.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.1.tgz",
"integrity": "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==",
"version": "11.16.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.0.tgz",
"integrity": "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==",
"license": "MIT",
"dependencies": {
"@braintree/sanitize-url": "^7.1.2",
@@ -27528,9 +27529,9 @@
"optional": true
},
"node_modules/nanoid": {
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"funding": [
{
"type": "github",
@@ -36608,9 +36609,9 @@
}
},
"node_modules/xmlbuilder2/node_modules/js-yaml": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
@@ -36950,7 +36951,12 @@
},
"open-sse": {
"name": "@omniroute/open-sse",
"version": "3.8.50"
"version": "3.8.50",
"dependencies": {
"@toon-format/toon": "^4.1.0",
"safe-regex": "^2.1.1",
"smol-toml": "1.7.1"
}
}
}
}

View File

@@ -266,7 +266,7 @@
"clsx": "^2.1.1",
"commander": "^15.0.0",
"csv-stringify": "^6.7.0",
"dompurify": "^3.4.13",
"dompurify": "^3.4.12",
"express": "^5.2.1",
"fetch-socks": "^1.3.3",
"fflate": "^0.8.3",
@@ -415,6 +415,7 @@
"unrs-resolver": true
},
"overrides": {
"dompurify": "^3.4.12",
"fast-xml-parser": "^5.10.1",
"sharp": "^0.35.0",
"postcss": "^8.5.18",
@@ -430,7 +431,7 @@
"fast-uri": "^3.1.5",
"body-parser": "^2.3.0",
"@yarnpkg/parsers": {
"js-yaml": "^4.3.1"
"js-yaml": "^4.2.0"
},
"jsdom": {
"undici": "^7.29.0"
@@ -445,27 +446,11 @@
"promptfoo": {
"js-yaml": "^5.2.2",
"@apidevtools/json-schema-ref-parser": {
"js-yaml": "^4.3.1"
"js-yaml": "^4.2.0"
},
"undici": "^7.29.0"
},
"socket.io-parser": "^4.2.7",
"tar": "^7.5.21",
"nanoid": "^3.3.17",
"@eslint/eslintrc": {
"js-yaml": "^4.3.1"
},
"lockfile-lint": {
"js-yaml": "^4.3.1"
},
"xmlbuilder2": {
"js-yaml": "^4.3.1"
},
"monaco-editor": {
"dompurify": "^3.4.13"
},
"@apidevtools/json-schema-ref-parser": {
"js-yaml": "^4.3.1"
}
"tar": "^7.5.21"
}
}

View File

@@ -1,19 +1,11 @@
/**
* GET /api/radar/referrals — return the referral links section ("Pegue seus
* créditos grátis", D28) from the locally cached Radar REFERRALS feed
* (`GET /v1/referrals/latest` — a separate, always-current artifact from the
* catalog feed; see `src/lib/radar/referralsSync.ts`).
* créditos grátis", D28) of the locally cached Radar feed.
*
* NEVER proxies the private feed server directly — the browser only ever
* talks to this local endpoint. Unlike the catalog (whose sync is entirely
* client-triggered via `POST /api/radar/sync`), THIS route also triggers a
* sync itself, inline, whenever the cached referrals are stale or missing
* (`shouldSyncReferralsOnRead`, 1h window) — the whole point of the
* standalone referrals feed is that fixed links show up promptly instead of
* inheriting the catalog's up-to-30-day community-tier snapshot delay. The
* network call still only ever happens inside `syncRadarReferrals()`
* (`referralsSync.ts`) — this route itself never talks to the upstream feed
* server directly.
* NEVER proxies the private feed server. Like GET /api/radar/catalog, the
* browser talks only to this local endpoint; sync happens server-side via
* POST /api/radar/sync, and this route only reads the cache that sync
* already wrote.
*
* `fixed` referrals are present in every tier (community included, gated
* server-side); `campaigns` only comes populated on the `live` (supporter)
@@ -32,8 +24,7 @@ import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags";
import { isAuthenticated } from "@/shared/utils/apiAuth";
import { getRadarReferrals } from "@/lib/radar";
import { getRadarReferralsCache } from "@/lib/db/radar";
import { syncRadarReferrals, shouldSyncReferralsOnRead } from "@/lib/radar/referralsSync";
import { getRadarCache } from "@/lib/db/radar";
import { buildErrorBody } from "@omniroute/open-sse/utils/error";
export const dynamic = "force-dynamic";
@@ -60,18 +51,8 @@ export async function GET(request: Request) {
}
try {
// Sync-on-read: refresh the cache inline when it is stale or missing.
// `syncRadarReferrals()` self-gates on flag/opt-in and never throws, so
// this is safe to await unconditionally — a disabled/opted-out operator
// just gets an instant no-op here and falls through to serving whatever
// (possibly empty) cache already exists.
const existingCache = getRadarReferralsCache();
if (shouldSyncReferralsOnRead(existingCache?.fetchedAt ?? null, Date.now())) {
await syncRadarReferrals();
}
const { fixed, campaigns } = getRadarReferrals();
const cache = getRadarReferralsCache();
const cache = getRadarCache();
return NextResponse.json(
{ fixed, campaigns, tier: cache?.tier ?? null },
{ headers: { ...CORS_HEADERS, "Cache-Control": "no-store" } },

View File

@@ -1,87 +0,0 @@
/**
* Muse Code CLI proprietary model catalog endpoint.
*
* Muse CLI calls GET /muse-code/models (or --base-url/muse-code/models)
* to discover available models. Returns the proprietary Muse format:
*
* { object: "list", data: [{ id, object, created, owned_by, metadata }] }
*
* Each model's metadata includes: name, family, reasoning, tool_call,
* modalities, limit, cost.
*/
import { muse_codeProvider } from "@omniroute/open-sse/config/providers/registry/muse-code/index.ts";
const MUSECODE_TIMESTAMP = Math.floor(Date.now() / 1000);
interface MuseCodeModel {
id: string;
object: "model";
created: number;
owned_by: string;
metadata: {
name: string;
family: string;
reasoning: boolean;
tool_call: boolean;
modalities: string[];
limit: number;
cost: number;
};
}
function buildModelCatalog(): MuseCodeModel[] {
const data: MuseCodeModel[] = [];
for (const model of muse_codeProvider.models) {
let family = "llama";
if (model.id.includes("llama-4")) family = "llama-4";
else if (model.id.includes("llama-3.3")) family = "llama-3.3";
else if (model.id.includes("llama-3.2")) family = "llama-3.2";
else if (model.id.includes("llama-3.1")) family = "llama-3.1";
const modalities: string[] = ["text"];
if (model.supportsVision) modalities.push("image");
data.push({
id: model.id,
object: "model",
created: MUSECODE_TIMESTAMP,
owned_by: "meta",
metadata: {
name: model.name,
family,
reasoning: !!model.supportsReasoning,
tool_call: !!model.toolCalling,
modalities,
limit: model.contextLength ?? 200_000,
cost: model.id.includes("maverick") || model.id.includes("405b") ? 3 : 1,
},
});
}
return data;
}
// Cache the catalog for the lifetime of the process — model list is static.
const CATALOG = buildModelCatalog();
const CATALOG_PAYLOAD = JSON.stringify({ object: "list", data: CATALOG }, null, 2);
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
export async function GET() {
return new Response(CATALOG_PAYLOAD, {
status: 200,
headers: {
"content-type": "application/json",
"cache-control": "public, max-age=3600",
},
});
}

View File

@@ -1,20 +0,0 @@
-- 142_radar_referrals_cache.sql
-- Radar referrals client local cache table.
--
-- radar_referrals_cache: single-row table holding the last verified
-- referrals feed JSON, fetched from GET /v1/referrals/latest — a separate,
-- always-current artifact from the catalog feed cached in
-- radar_feed_cache (migration 136). Introduced so referral links no
-- longer inherit the catalog's up-to-30-day community-tier snapshot
-- delay. The payload is the exact byte-identical feed returned by the
-- Radar server after Ed25519 signature verification (same pinned key as
-- the catalog feed).
CREATE TABLE IF NOT EXISTS radar_referrals_cache (
id INTEGER PRIMARY KEY CHECK (id = 1),
generated_at TEXT,
tier TEXT,
payload TEXT,
signature TEXT,
fetched_at TEXT
);

View File

@@ -17,7 +17,6 @@ export { MODEL_COMPAT_PROTOCOL_KEYS, type ModelCompatProtocolKey };
export type ModelCompatPerProtocol = {
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean;
preserveVideoUrl?: boolean;
/** Merged into upstream HTTP requests for this model (after default auth headers). */
upstreamHeaders?: Record<string, string>;
};
@@ -77,7 +76,6 @@ export function deepMergeCompatByProtocol(
const hasDelta =
Object.prototype.hasOwnProperty.call(deltas, "normalizeToolCallId") ||
Object.prototype.hasOwnProperty.call(deltas, "preserveOpenAIDeveloperRole") ||
Object.prototype.hasOwnProperty.call(deltas, "preserveVideoUrl") ||
Object.prototype.hasOwnProperty.call(deltas, "upstreamHeaders");
if (!hasDelta) continue;
const cur: ModelCompatPerProtocol = { ...(out[key] || {}) };
@@ -87,9 +85,6 @@ export function deepMergeCompatByProtocol(
if ("preserveOpenAIDeveloperRole" in deltas) {
cur.preserveOpenAIDeveloperRole = Boolean(deltas.preserveOpenAIDeveloperRole);
}
if ("preserveVideoUrl" in deltas) {
cur.preserveVideoUrl = Boolean(deltas.preserveVideoUrl);
}
if ("upstreamHeaders" in deltas) {
const uh = deltas.upstreamHeaders;
if (uh === undefined) {
@@ -110,7 +105,6 @@ export type ModelCompatOverride = {
id: string;
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean;
preserveVideoUrl?: boolean;
compatByProtocol?: CompatByProtocolMap;
upstreamHeaders?: Record<string, string>;
isHidden?: boolean;
@@ -165,7 +159,6 @@ export function getModelCompatOverrides(providerId: string): ModelCompatOverride
export type ModelCompatPatch = {
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean | null;
preserveVideoUrl?: boolean | null;
compatByProtocol?: CompatByProtocolMap;
/** Replace top-level extra headers for override-only rows; omit to leave unchanged. */
upstreamHeaders?: Record<string, string> | null;
@@ -202,13 +195,6 @@ export function mergeModelCompatOverride(
next.preserveOpenAIDeveloperRole = Boolean(patch.preserveOpenAIDeveloperRole);
}
}
if ("preserveVideoUrl" in patch) {
if (patch.preserveVideoUrl === null) {
delete next.preserveVideoUrl;
} else {
next.preserveVideoUrl = Boolean(patch.preserveVideoUrl);
}
}
if (patch.compatByProtocol && Object.keys(patch.compatByProtocol).length > 0) {
const merged = deepMergeCompatByProtocol(next.compatByProtocol, patch.compatByProtocol);
if (compatByProtocolHasEntries(merged)) next.compatByProtocol = merged;
@@ -225,7 +211,6 @@ export function mergeModelCompatOverride(
}
const filtered = list.filter((e) => e.id !== modelId);
const hasPreserveFlag = Object.prototype.hasOwnProperty.call(next, "preserveOpenAIDeveloperRole");
const hasVideoUrlFlag = Object.prototype.hasOwnProperty.call(next, "preserveVideoUrl");
const hasTopUpstream = next.upstreamHeaders && Object.keys(next.upstreamHeaders).length > 0;
if ("isHidden" in patch) {
if (patch.isHidden === null) {
@@ -246,7 +231,6 @@ export function mergeModelCompatOverride(
if (
next.normalizeToolCallId ||
hasPreserveFlag ||
hasVideoUrlFlag ||
hasHiddenFlag ||
hasDeletedFlag ||
compatByProtocolHasEntries(next.compatByProtocol) ||

View File

@@ -4,15 +4,10 @@
* Provides local cache + settings storage for the OmniRoute Radar client.
* Nothing here talks to the network (that's the sync layer).
*
* Tables (migration 136):
* Tables (migration 134):
* - radar_feed_cache: single-row signed feed cache
* - radar_settings: opt-in + encrypted supporter key
*
* Tables (migration 142):
* - radar_referrals_cache: single-row signed referrals feed cache
* (`GET /v1/referrals/latest` — a separate, always-current artifact from
* the catalog feed, see `src/lib/radar/referralsSync.ts`).
*
* The supporter key is encrypted at rest with AES-256-GCM using the same
* `encrypt()`/`decrypt()` helpers from `./encryption.ts` that protect
* provider connection credentials.
@@ -39,14 +34,6 @@ export interface RadarSettings {
updatedAt: string;
}
export interface RadarReferralsCache {
generatedAt: string;
tier: string;
payload: string;
signature: string;
fetchedAt: string;
}
// ---------------------------------------------------------------------------
// radar_feed_cache
// ---------------------------------------------------------------------------
@@ -137,50 +124,3 @@ export function setRadarKey(key: string | null): void {
"UPDATE radar_settings SET supporter_key_encrypted = ?, updated_at = datetime('now') WHERE id = 1"
).run(encrypted);
}
// ---------------------------------------------------------------------------
// radar_referrals_cache
// ---------------------------------------------------------------------------
/**
* Read the cached Radar referrals feed (`GET /v1/referrals/latest`).
* Returns null when no referrals feed has been cached yet — separate from,
* and never falling back to, the catalog's `radar_feed_cache`.
*/
export function getRadarReferralsCache(): RadarReferralsCache | null {
const db = getDbInstance();
const row = db
.prepare(
"SELECT generated_at AS generatedAt, tier, payload, signature, fetched_at AS fetchedAt " +
"FROM radar_referrals_cache WHERE id = 1"
)
.get() as RadarReferralsCache | undefined;
return row ?? null;
}
/**
* Upsert the Radar referrals feed cache (single row). Replaces any existing
* entry. If `fetchedAt` is omitted, the current ISO timestamp is used.
*/
export function setRadarReferralsCache(entry: {
generatedAt: string;
tier: string;
payload: string;
signature: string;
fetchedAt?: string;
}): void {
const db = getDbInstance();
const fetchedAt = entry.fetchedAt ?? new Date().toISOString();
db.prepare(
`INSERT INTO radar_referrals_cache (id, generated_at, tier, payload, signature, fetched_at)
VALUES (1, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
generated_at = excluded.generated_at,
tier = excluded.tier,
payload = excluded.payload,
signature = excluded.signature,
fetched_at = excluded.fetched_at`
).run(entry.generatedAt, entry.tier, entry.payload, entry.signature, fetchedAt);
}

View File

@@ -818,7 +818,5 @@ export {
getRadarSettings,
setRadarOptIn,
setRadarKey,
getRadarReferralsCache,
setRadarReferralsCache,
} from "./db/radar";
export type { RadarCache, RadarSettings, RadarReferralsCache } from "./db/radar";
export type { RadarCache, RadarSettings } from "./db/radar";

View File

@@ -96,14 +96,7 @@ const HttpsUrlSchema = z
.url()
.refine((v) => v.startsWith("https://"), { message: "Referral url must use https://" });
/**
* Exported so `referralsFeedSchema.ts` (the standalone `/v1/referrals/latest`
* feed schema) can reuse the exact same per-referral shape instead of
* duplicating it — one definition, two feeds (the catalog's legacy embedded
* `referrals` section below, kept for backward-compat with old cached
* catalog feeds, and the live referrals-only feed).
*/
export const RadarReferralSchema = z.object({
const RadarReferralSchema = z.object({
provider: z.string(),
url: HttpsUrlSchema,
kind: ReferralKindEnum,

View File

@@ -11,11 +11,10 @@
import { FREE_MODEL_BUDGETS } from "@omniroute/open-sse/config/freeModelCatalog";
import { RadarFeedSchema, type RadarFeed, type RadarReferral } from "./feedSchema";
import { RadarReferralsFeedSchema, type RadarReferralsFeed } from "./referralsFeedSchema";
import { applyFeed, type MergedEntry, type FeedModel } from "./applyFeed";
import { findDefaultReferral } from "./referrals";
import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags";
import { getRadarCache, getRadarReferralsCache } from "@/lib/db/radar";
import { getRadarCache } from "@/lib/db/radar";
// ---------------------------------------------------------------------------
// Types
@@ -143,30 +142,23 @@ export interface RadarReferralsResult {
const EMPTY_REFERRALS: RadarReferralsResult = { fixed: [], campaigns: [] };
/**
* Injectable deps for testing. `getCache` now reads the STANDALONE referrals
* feed cache (`radar_referrals_cache`, populated by
* `syncRadarReferrals()`/`GET /v1/referrals/latest`) — no longer the
* catalog's `radar_feed_cache`. This is what removes the up-to-30-day
* community-tier delay referral links used to inherit from the catalog
* feed: referrals now sync on their own, much shorter cadence
* (`REFERRALS_STALE_MS`, see `referralsSync.ts`).
*/
/** Injectable deps for testing — mirrors GetRadarCatalogDeps. */
export interface GetRadarReferralsDeps {
getFlag?: (key: string) => boolean;
getCache?: () => { generatedAt: string; tier: string; payload: string; fetchedAt: string } | null;
getCache?: () => { version: string; tier: string; payload: string; fetchedAt: string } | null;
}
/**
* Return the referral links section of the cached Radar REFERRALS feed
* (`GET /v1/referrals/latest` — see `referralsSync.ts`).
* Return the referral links section of the cached Radar feed.
*
* Never throws — returns `{fixed:[],campaigns:[]}` when: the flag is off,
* there is no cache yet, or the cached payload fails defensive
* re-validation (corrupt/garbage payload).
* there is no cache yet, the cached payload fails defensive re-validation,
* or the cached feed predates the `referrals` section (old-feed compat —
* the schema's `.default()` already covers this, this is a second line of
* defense for a payload that fails to parse at all).
*/
export function getRadarReferrals(deps: GetRadarReferralsDeps = {}): RadarReferralsResult {
const { getFlag = isFeatureFlagEnabled, getCache: getCacheFn = getRadarReferralsCache } = deps;
const { getFlag = isFeatureFlagEnabled, getCache: getCacheFn = getRadarCache } = deps;
if (!getFlag("RADAR_ENABLED")) {
return EMPTY_REFERRALS;
@@ -177,10 +169,10 @@ export function getRadarReferrals(deps: GetRadarReferralsDeps = {}): RadarReferr
return EMPTY_REFERRALS;
}
let feed: RadarReferralsFeed;
let feed: RadarFeed;
try {
const parsed = JSON.parse(cache.payload);
feed = RadarReferralsFeedSchema.parse(parsed);
feed = RadarFeedSchema.parse(parsed);
} catch {
return EMPTY_REFERRALS;
}

View File

@@ -1,35 +0,0 @@
/**
* referralsFeedSchema.ts — Zod schema for the standalone Radar referrals feed
* (`GET /v1/referrals/latest`).
*
* This is the CLIENT-SIDE mirror of the server's referrals feed schema — a
* separate, always-current artifact from the catalog feed (`feedSchema.ts`),
* introduced so referral links no longer inherit the catalog's up-to-30-day
* community-tier snapshot delay. Reuses `RadarReferralSchema` (the per-link
* shape) from `feedSchema.ts` so both feeds validate referrals identically.
*
* Schema version: 1
*/
import { z } from "zod";
import { RadarReferralSchema } from "./feedSchema";
// ---------------------------------------------------------------------------
// Top-level feed schema
// ---------------------------------------------------------------------------
export const RadarReferralsFeedSchema = z.object({
feed: z.literal("omniroute-radar-referrals"),
schemaVersion: z.literal(1),
generatedAt: z.string().datetime(),
referrals: z.object({
fixed: z.array(RadarReferralSchema),
campaigns: z.array(RadarReferralSchema),
}),
});
// ---------------------------------------------------------------------------
// Inferred types
// ---------------------------------------------------------------------------
export type RadarReferralsFeed = z.infer<typeof RadarReferralsFeedSchema>;

View File

@@ -1,308 +0,0 @@
/**
* referralsSync.ts — Radar referrals feed sync: download, verify, validate, cache.
*
* Mirrors `sync.ts` (the catalog feed sync) but targets the standalone,
* always-current `GET /v1/referrals/latest` endpoint instead of the
* catalog's `/v1/catalog/latest` — the catalog feed is a up-to-30-day-old
* snapshot on the community tier, so referral links extracted from it lag
* behind the server by up to 30 days. This module removes that delay by
* consuming the dedicated referrals endpoint directly.
*
* This is the ONLY module that touches the network for Radar referrals.
* Every step is gated: flag off / opt-out / bad sig / bad schema / oversized
* body all bail early without touching the cache.
*
* Errors never escape `syncRadarReferrals()` — always return a status object.
* Stack traces are never included in the `reason` field.
*
* Deps are injectable for testing.
*/
import {
RadarReferralsFeedSchema,
type RadarReferralsFeed,
} from "./referralsFeedSchema";
import { RadarTierSchema, type RadarTier } from "./feedSchema";
import { verifyFeedBytes } from "./verify";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags";
import type { RadarSettingsSnapshot } from "./sync";
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/**
* Default feed base URL — same default/override convention as `sync.ts`
* (`RADAR_FEED_URL` env var points forks/self-hosters at their own server).
*/
const DEFAULT_FEED_BASE_URL = "https://radar.omniroute.online";
const SYNC_TIMEOUT_MS = 30_000;
/**
* Hard cap on the referrals feed response body. Same rationale as
* `sync.ts::MAX_FEED_BYTES` — this is a small, KB-scale signed JSON document;
* anything past this is either a misconfigured `RADAR_FEED_URL` or an
* upstream serving garbage. Enforced both via a `Content-Length` preflight
* and a running-total check while reading the body, so an absent/lying
* `Content-Length` cannot bypass the cap.
*/
const MAX_FEED_BYTES = 10 * 1024 * 1024; // 10 MB
/**
* How stale the cached referrals must be before a new sync is worth doing
* (used by the route's "sync if stale" trigger, see `shouldSyncReferralsOnRead`).
* Deliberately much shorter than the catalog's 24h cadence — referrals are
* meant to feel "always current", and the fixed links in particular should
* surface quickly for a free/community user.
*/
export const REFERRALS_STALE_MS = 60 * 60 * 1000; // 1h
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export type ReferralsSyncStatus =
| { status: "disabled" }
| { status: "opt_out" }
| { status: "invalid_signature" }
| { status: "invalid_schema" }
| { status: "stale" }
| { status: "too_large" }
| { status: "updated"; generatedAt: string; tier: string }
| { status: "error"; reason: string };
export interface RadarReferralsCacheEntry {
generatedAt: string;
tier: string;
payload: string;
signature: string;
fetchedAt?: string;
}
export interface ReferralsSyncDeps {
fetch?: typeof globalThis.fetch;
now?: () => Date;
getFlag?: (key: string) => boolean;
getSettings?: () => RadarSettingsSnapshot;
getCache?: () => RadarReferralsCacheEntry | null;
setCache?: (entry: RadarReferralsCacheEntry) => void;
}
// ---------------------------------------------------------------------------
// Served-tier header
// ---------------------------------------------------------------------------
/**
* Parse & validate the `x-omniroute-feed-tier` response header.
*
* Unlike the catalog feed, the referrals feed body carries no `tier` field
* at all (there is only ever one signed artifact per `generatedAt`, and the
* server decides which referrals to include per-request based on the
* `Authorization` key) — so the header is the ONLY source for the served
* tier. An absent or unrecognized header degrades to `"community"`, the
* least-privileged assumption (matches the server's own no-auth default:
* fixed links only, `campaigns: []`).
*/
function parseServedTierHeader(value: string | null): RadarTier | null {
const result = RadarTierSchema.safeParse(value);
return result.success ? result.data : null;
}
// ---------------------------------------------------------------------------
// Staleness helper (exported for the route's "sync if stale" trigger)
// ---------------------------------------------------------------------------
/**
* Whether the cached referrals feed (fetched at `fetchedAt`) is stale enough
* to warrant a fresh sync. Missing/unparseable timestamps count as stale —
* mirrors `autoSync.ts::shouldAutoSyncOnOpen`'s conservative default.
*/
export function shouldSyncReferralsOnRead(
fetchedAt: string | null | undefined,
nowMs: number,
staleMs: number = REFERRALS_STALE_MS
): boolean {
if (!fetchedAt) return true;
const fetchedMs = Date.parse(fetchedAt);
if (!Number.isFinite(fetchedMs)) return true;
return nowMs - fetchedMs >= staleMs;
}
// ---------------------------------------------------------------------------
// syncRadarReferrals
// ---------------------------------------------------------------------------
/**
* Download, verify, validate, and cache the Radar referrals feed.
*
* Steps:
* 1. Feature flag off => `{status:"disabled"}`, no network.
* 2. Opt-in false => `{status:"opt_out"}`, no network.
* 3. GET `${base}/v1/referrals/latest` with timeout.
* 4. Verify Ed25519 signature over exact bytes (same pinned key as the
* catalog feed — one key pins both artifacts).
* 5. Parse+validate with RadarReferralsFeedSchema.
* 6. Replay guard: incoming `generatedAt` must be strictly newer than the
* cache (a same/older `generatedAt` is a no-op — nothing changed, or a
* stale replay — either way the cache is left untouched).
* 7. Cache the result.
*
* @param deps - Injectable dependencies for testing.
*/
export async function syncRadarReferrals(
deps: ReferralsSyncDeps = {}
): Promise<ReferralsSyncStatus> {
const {
fetch: fetchFn = globalThis.fetch,
now = () => new Date(),
getFlag = isFeatureFlagEnabled,
getSettings: getSettingsFn,
getCache: getCacheFn,
setCache: setCacheFn,
} = deps;
try {
// Step 1: Feature flag gate
const flagOn = getFlag("RADAR_ENABLED");
if (!flagOn) {
return { status: "disabled" };
}
// Step 2: Opt-in gate
let settings: RadarSettingsSnapshot;
if (getSettingsFn) {
settings = getSettingsFn();
} else {
const mod = await import("@/lib/db/radar");
settings = mod.getRadarSettings();
}
if (!settings.optIn) {
return { status: "opt_out" };
}
// Step 3: Download feed
const baseUrl = (process.env.RADAR_FEED_URL || DEFAULT_FEED_BASE_URL).replace(/\/+$/, "");
const url = `${baseUrl}/v1/referrals/latest`;
const headers: Record<string, string> = {};
if (settings.supporterKey) {
headers["Authorization"] = `Bearer ${settings.supporterKey}`;
}
const res = await fetchFn(url, {
headers,
signal: AbortSignal.timeout(SYNC_TIMEOUT_MS),
});
if (!res.ok) {
return { status: "error", reason: `Referrals feed request failed with status ${res.status}` };
}
// Step 3b: Content-Length preflight — skip reading an already-oversized
// body entirely (untrusted header, fast-path only; the real enforcement
// is the running-total check below).
const contentLengthHeader = res.headers.get("content-length");
if (contentLengthHeader !== null) {
const declaredLength = Number(contentLengthHeader);
if (Number.isFinite(declaredLength) && declaredLength > MAX_FEED_BYTES) {
return { status: "too_large" };
}
}
// Step 4: Read exact bytes + signature header, enforcing MAX_FEED_BYTES
// while reading so an absent/lying Content-Length cannot bypass the cap.
let rawBytes: Buffer;
const body = res.body as ReadableStream<Uint8Array> | null | undefined;
if (body && typeof body.getReader === "function") {
const reader = body.getReader();
const chunks: Uint8Array[] = [];
let total = 0;
let tooLarge = false;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (value) {
total += value.byteLength;
if (total > MAX_FEED_BYTES) {
tooLarge = true;
await reader.cancel().catch(() => {});
break;
}
chunks.push(value);
}
}
if (tooLarge) {
return { status: "too_large" };
}
rawBytes = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));
} else {
const buffered = Buffer.from(await res.arrayBuffer());
if (buffered.byteLength > MAX_FEED_BYTES) {
return { status: "too_large" };
}
rawBytes = buffered;
}
const signature = res.headers.get("x-omniroute-feed-signature") ?? "";
// Step 5: Verify signature — same pinned Ed25519 key(s) as the catalog feed.
const sigValid = verifyFeedBytes(rawBytes, signature);
if (!sigValid) {
return { status: "invalid_signature" };
}
// Step 6: Parse + validate
let feed: RadarReferralsFeed;
try {
const parsed = JSON.parse(rawBytes.toString("utf-8"));
feed = RadarReferralsFeedSchema.parse(parsed);
} catch {
return { status: "invalid_schema" };
}
// Step 7: generatedAt floor — replay/no-op guard.
let existingCache: RadarReferralsCacheEntry | null = null;
if (getCacheFn) {
existingCache = getCacheFn();
} else {
const mod = await import("@/lib/db/radar");
existingCache = mod.getRadarReferralsCache();
}
if (existingCache) {
const existingMs = Date.parse(existingCache.generatedAt);
const incomingMs = Date.parse(feed.generatedAt);
if (Number.isFinite(existingMs) && Number.isFinite(incomingMs) && incomingMs <= existingMs) {
return { status: "stale" };
}
}
// Step 8: Resolve served tier — the body carries no `tier` field at all
// for this feed, so the header is the only source; absent/garbage header
// degrades to the least-privileged "community" default.
const servedTier = parseServedTierHeader(res.headers.get("x-omniroute-feed-tier")) ?? "community";
// Step 9: Cache the result
const cacheEntry: RadarReferralsCacheEntry = {
generatedAt: feed.generatedAt,
tier: servedTier,
payload: rawBytes.toString("utf-8"),
signature,
fetchedAt: now().toISOString(),
};
if (setCacheFn) {
setCacheFn(cacheEntry);
} else {
const mod = await import("@/lib/db/radar");
mod.setRadarReferralsCache(cacheEntry);
}
return { status: "updated", generatedAt: feed.generatedAt, tier: servedTier };
} catch (err: unknown) {
const reason = sanitizeErrorMessage(err) || "Radar referrals sync failed";
return { status: "error", reason };
}
}

View File

@@ -12,25 +12,11 @@
* network sync when the cache is older than the daily window computed by
* `nextSyncTime()`. `syncRadar()` re-checks flag/opt-in internally, so a
* mid-flight settings change degrades to a no-op instead of an errant fetch.
*
* Referrals (`GET /v1/referrals/latest`) piggyback on the SAME hourly tick,
* but on their own much shorter staleness window (`REFERRALS_STALE_MS`, 1h —
* see `referralsSync.ts`) so they stay close to real-time instead of
* inheriting the catalog's daily cadence. This is independent of, and never
* gates on, the catalog's own due-ness — the two feeds sync on separate
* schedules within the same tick. It is deliberately NOT reflected in
* `RadarTickResult` (best-effort, fire-and-await side effect only) so the
* existing catalog-sync result shape/assertions stay unchanged.
*/
import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags";
import { getRadarCache, getRadarSettings, getRadarReferralsCache } from "@/lib/db/radar";
import { getRadarCache, getRadarSettings } from "@/lib/db/radar";
import { nextSyncTime, syncRadar, type SyncStatus } from "./sync";
import {
syncRadarReferrals,
shouldSyncReferralsOnRead,
type ReferralsSyncStatus,
} from "./referralsSync";
/** How often the scheduler re-evaluates staleness (NOT the sync cadence). */
export const RADAR_SCHEDULER_TICK_MS = 60 * 60 * 1000; // hourly
@@ -45,10 +31,6 @@ export interface RadarSchedulerDeps {
getSettings?: () => { optIn: boolean };
getCache?: () => { fetchedAt: string } | null;
sync?: () => Promise<SyncStatus>;
/** Referrals cache reader — separate from `getCache` (the catalog cache). */
getReferralsCache?: () => { fetchedAt: string } | null;
/** Referrals sync — separate from `sync` (the catalog sync). */
syncReferrals?: () => Promise<ReferralsSyncStatus>;
now?: () => number;
setIntervalFn?: typeof setInterval;
clearIntervalFn?: typeof clearInterval;
@@ -56,23 +38,6 @@ export interface RadarSchedulerDeps {
let timer: ReturnType<typeof setInterval> | null = null;
/**
* Best-effort referrals sync, gated on its own (shorter) staleness window.
* Never throws — `syncRadarReferrals()` already never throws by contract,
* this is defense in depth so a scheduler tick can never fail because of
* the referrals side-sync.
*/
async function maybeSyncReferrals(deps: RadarSchedulerDeps, nowMs: number): Promise<void> {
try {
const referralsCache = (deps.getReferralsCache ?? getRadarReferralsCache)();
if (!shouldSyncReferralsOnRead(referralsCache?.fetchedAt ?? null, nowMs)) return;
await (deps.syncReferrals ?? syncRadarReferrals)();
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.warn("[RADAR_SYNC] Referrals side-sync failed (non-fatal):", msg);
}
}
/**
* One scheduler evaluation. Exported for tests and for the immediate
* post-start tick.
@@ -87,13 +52,8 @@ export async function radarSchedulerTick(deps: RadarSchedulerDeps = {}): Promise
const settings = (deps.getSettings ?? getRadarSettings)();
if (!settings.optIn) return { action: "skipped", reason: "opt_out" };
const nowMs = (deps.now ?? Date.now)();
// Referrals sync on their own staleness window — independent of the
// catalog's due-ness below, same tick.
await maybeSyncReferrals(deps, nowMs);
const cache = (deps.getCache ?? getRadarCache)();
const nowMs = (deps.now ?? Date.now)();
if (nowMs < nextSyncTime(cache?.fetchedAt ?? null).getTime()) {
return { action: "skipped", reason: "not_due" };
}

View File

@@ -51,13 +51,6 @@ const GEMINI_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
"User-Agent": "GeminiCLI/0.1.0 (linux; x64)",
}),
});
const MUSE_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
id: "muse-cli",
label: "Muse Code CLI",
headers: Object.freeze({
"User-Agent": "MuseCodeCLI/0.1.0 (linux; x64)",
}),
});
/** Ordered so `CLIENT_IDENTITY_PROFILE_OPTIONS` renders "Default" first. */
export const CLIENT_IDENTITY_PROFILES: Readonly<Record<string, ClientIdentityProfile>> =
@@ -66,7 +59,6 @@ export const CLIENT_IDENTITY_PROFILES: Readonly<Record<string, ClientIdentityPro
"claude-cli": CLAUDE_CLI_PROFILE,
"codex-cli": CODEX_CLI_PROFILE,
"gemini-cli": GEMINI_CLI_PROFILE,
"muse-cli": MUSE_CLI_PROFILE,
});
export const CLIENT_IDENTITY_PROFILE_IDS: readonly string[] = Object.keys(CLIENT_IDENTITY_PROFILES);

View File

@@ -275,19 +275,4 @@ export const APIKEY_PROVIDERS_FRONTIER = {
"Writer Palmyra is OpenAI-compatible at https://api.writer.com/v1. palmyra-x5 offers a 1M-token context window.",
hasFree: false,
},
"muse-code": {
id: "muse-code",
alias: "mc",
name: "Muse Code (Meta)",
icon: "auto_awesome",
color: "#0866FF",
textIcon: "MC",
website: "https://github.com/meta-llama/llama-stack",
authHint:
"Use your META_API_KEY env var as a Bearer token. Muse Code CLI uses the OpenAI Responses API wire format (POST /responses).",
apiHint:
"Muse Code is OpenAI-compatible. OmniRoute routes chat traffic through the Responses API and exposes the proprietary model catalog at /v1/muse-code/models.",
passthroughModels: true,
hasFree: false,
},
};

View File

@@ -3405,26 +3405,6 @@
"stream": "https://api.morphllm.com/v1/chat/completions"
}
},
"muse-code": {
"format": "openai",
"headers": {
"apiKey": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"nonStream": {
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"oauth": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
}
},
"url": {}
},
"muse-spark-web": {
"format": "openai",
"headers": {

View File

@@ -76,7 +76,6 @@ test("Codex keeps matched outputs and removes orphaned outputs from mixed input"
{ type: "function_call_output", call_id: "call_orphan", output: "orphaned" },
]);
assert.equal(result.length, 2);
assert.deepEqual(toolOutputs(result), [
{ type: "function_call_output", call_id: "call_keep", output: "ok" },
]);

View File

@@ -2318,7 +2318,7 @@ test("handleComboChat returns a 503 when every model is unavailable before execu
const payload = (await result.json()) as any;
assert.equal(result.status, 503);
assert.equal(payload.error.code, "ALL_TARGETS_SKIPPED");
assert.equal(payload.error.code, "ALL_ACCOUNTS_INACTIVE");
});
test("handleComboChat treats provider circuit breaker responses as ordinary target failures", async () => {
@@ -2847,7 +2847,7 @@ test("handleComboChat round-robin resolves nested combos and returns inactive wh
const payload = (await result.json()) as any;
assert.equal(result.status, 503);
assert.equal(payload.error.code, "ALL_TARGETS_SKIPPED");
assert.equal(payload.error.code, "ALL_ACCOUNTS_INACTIVE");
});
test("handleComboChat round-robin treats provider circuit breaker responses as ordinary target failures", async () => {

View File

@@ -339,12 +339,8 @@ test("KiroExecutor keeps cache tokens that arrive without input/output totals",
const chunks = parseSSEJsonChunks(await transformed.text());
const finish = chunks.find((chunk) => chunk.choices?.[0]?.finish_reason);
assert.deepEqual(finish.usage, {
prompt_tokens: 19999,
completion_tokens: 1,
total_tokens: 20000,
cache_read_input_tokens: 900,
});
assert.equal(finish.usage.cache_read_input_tokens, 900);
assert.equal(finish.usage.cache_creation_input_tokens, undefined);
});
// snake_case spellings appear on some Kiro frames; a cache count must not be

View File

@@ -1,81 +0,0 @@
/**
* Tests for Muse Code CLI model catalog endpoint.
*
* Verifies GET /v1/muse-code/models returns the proprietary Muse format.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { muse_codeProvider } from "../../open-sse/config/providers/registry/muse-code/index.ts";
// ── Model catalog shape ─────────────────────────────────────────────────────
test("muse-code provider has at least one model", () => {
assert.ok(muse_codeProvider.models.length >= 1);
});
test("muse-code models have unique ids", () => {
const ids = muse_codeProvider.models.map((m) => m.id);
const unique = new Set(ids);
assert.equal(unique.size, ids.length, "model IDs must be unique");
});
test("muse-code models include llama-4-maverick", () => {
const ids = muse_codeProvider.models.map((m) => m.id);
assert.ok(ids.includes("llama-4-maverick"), "must include llama-4-maverick");
});
test("muse-code models include llama-4-scout", () => {
const ids = muse_codeProvider.models.map((m) => m.id);
assert.ok(ids.includes("llama-4-scout"), "must include llama-4-scout");
});
test("muse-code models include llama-3.3-70b", () => {
const ids = muse_codeProvider.models.map((m) => m.id);
assert.ok(ids.includes("llama-3.3-70b"), "must include llama-3.3-70b");
});
test("llama-4 models have supportsXHighEffort", () => {
const maverick = muse_codeProvider.models.find((m) => m.id === "llama-4-maverick");
assert.ok(maverick, "llama-4-maverick must exist");
assert.equal(maverick.supportsXHighEffort, true);
const scout = muse_codeProvider.models.find((m) => m.id === "llama-4-scout");
assert.ok(scout, "llama-4-scout must exist");
assert.equal(scout.supportsXHighEffort, true);
});
test("llama-3.3-70b does not support reasoning", () => {
const model = muse_codeProvider.models.find((m) => m.id === "llama-3.3-70b");
assert.ok(model, "llama-3.3-70b must exist");
assert.equal(model.supportsReasoning, false);
});
test("non-reasoning models do not declare supportsXHighEffort", () => {
for (const model of muse_codeProvider.models) {
if (!model.supportsReasoning) {
assert.equal(
model.supportsXHighEffort,
undefined,
`${model.id} is not a reasoning model but has supportsXHighEffort`
);
}
}
});
// ── Vision models ───────────────────────────────────────────────────────────
test("vision models have supportsVision: true", () => {
const expectedVision = [
"llama-4-maverick",
"llama-4-scout",
"llama-3.2-90b-vision",
"llama-3.2-11b-vision",
];
for (const model of muse_codeProvider.models) {
if (expectedVision.includes(model.id)) {
assert.equal(model.supportsVision, true, `${model.id} should have supportsVision`);
}
}
});

View File

@@ -1,91 +0,0 @@
/**
* Tests for Muse Code CLI provider registry entry.
*
* Verifies the provider entry loads correctly with expected config.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { muse_codeProvider } from "../../open-sse/config/providers/registry/muse-code/index.ts";
import { getRegistryEntry } from "../../open-sse/config/providerRegistry.ts";
// ── Registry entry structure ────────────────────────────────────────────────
test("muse-code provider entry has id", () => {
assert.equal(muse_codeProvider.id, "muse-code");
});
test("muse-code provider entry has alias", () => {
assert.equal(muse_codeProvider.alias, "mc");
});
test("muse-code provider uses openai format", () => {
assert.equal(muse_codeProvider.format, "openai");
});
test("muse-code provider uses apikey auth", () => {
assert.equal(muse_codeProvider.authType, "apikey");
assert.equal(muse_codeProvider.authHeader, "bearer");
});
test("muse-code provider has passthroughModels enabled", () => {
assert.equal(muse_codeProvider.passthroughModels, true);
});
// ── Model entries ───────────────────────────────────────────────────────────
test("muse-code provider has curated models", () => {
assert.ok(muse_codeProvider.models.length > 0);
});
test("all muse-code models have contextLength", () => {
for (const model of muse_codeProvider.models) {
assert.ok(
typeof model.contextLength === "number" && model.contextLength > 0,
`${model.id} must have positive contextLength`
);
}
});
test("all muse-code models have toolCalling: true", () => {
for (const model of muse_codeProvider.models) {
assert.equal(model.toolCalling, true, `${model.id} must have toolCalling enabled`);
}
});
test("all muse-code models have targetFormat: openai-responses", () => {
for (const model of muse_codeProvider.models) {
assert.equal(
model.targetFormat,
"openai-responses",
`${model.id} must use openai-responses target format`
);
}
});
test("reasoning models have supportsXHighEffort", () => {
for (const model of muse_codeProvider.models) {
if (model.supportsReasoning) {
assert.equal(
model.supportsXHighEffort,
true,
`${model.id} is a reasoning model but missing supportsXHighEffort`
);
}
}
});
// ── Registry discovery ──────────────────────────────────────────────────────
test("muse-code is discoverable via getRegistryEntry", () => {
const entry = getRegistryEntry("muse-code");
assert.ok(entry, "getRegistryEntry must return muse-code entry");
assert.equal(entry.id, "muse-code");
});
test("muse-code is discoverable via alias", () => {
const entry = getRegistryEntry("mc");
assert.ok(entry, "getRegistryEntry must find muse-code by alias mc");
assert.equal(entry.id, "muse-code");
});

View File

@@ -1,5 +1,5 @@
import { describe, it } from "node:test";
import { deepEqual, equal, ok } from "node:assert/strict";
import { ok, equal } from "node:assert/strict";
describe("getModelPreserveVideoUrl", () => {
it("exports getModelPreserveVideoUrl as a function", async () => {
@@ -8,7 +8,8 @@ describe("getModelPreserveVideoUrl", () => {
});
it("fallback preserves moonshot and kimi legacy behavior", () => {
const fallback = (provider: string) => provider === "moonshot" || provider === "kimi";
const fallback = (provider: string) =>
provider === "moonshot" || provider === "kimi";
ok(fallback("moonshot"));
ok(fallback("kimi"));
equal(fallback("dashscope"), false);
@@ -26,67 +27,19 @@ describe("getModelPreserveVideoUrl", () => {
});
it("mergeModelCompatOverride accepts preserveVideoUrl", async () => {
const { mergeModelCompatOverride, removeModelCompatOverride } =
await import("@/lib/db/models/compat");
const { getModelPreserveVideoUrl } = await import("@/lib/db/models/modelPreserveVideoUrl");
const { mergeModelCompatOverride, removeModelCompatOverride } = await import("@/lib/db/models/compat");
const PROVIDER = "test_provider_9248v3";
const MODEL = "test_model_qwen_vl";
try {
mergeModelCompatOverride(PROVIDER, MODEL, { preserveVideoUrl: true });
equal(getModelPreserveVideoUrl(PROVIDER, MODEL), true);
} finally {
removeModelCompatOverride(PROVIDER, MODEL);
}
});
it("translateRequest resolves preserveVideoUrl with the routed model", async () => {
const { mergeModelCompatOverride, removeModelCompatOverride } =
await import("@/lib/db/models/compat");
const { translateRequest } = await import("@omniroute/open-sse/translator/index.ts");
const provider = "test-provider-video-override";
const model = "test-model-video-override";
mergeModelCompatOverride(provider, model, { preserveVideoUrl: true });
try {
const translated = translateRequest(
"openai",
"openai",
model,
{
messages: [
{
role: "user",
content: [
{
type: "video_url",
video_url: { url: "https://cdn.example.com/input.mp4" },
},
],
},
],
},
false,
null,
provider
) as { messages: Array<{ content: Array<{ type: string }> }> };
deepEqual(
translated.messages[0].content.map((part) => part.type),
["video_url"]
);
} finally {
removeModelCompatOverride(provider, model);
}
mergeModelCompatOverride(PROVIDER, MODEL, { preserveVideoUrl: true });
removeModelCompatOverride(PROVIDER, MODEL);
ok(true, "should accept preserveVideoUrl in ModelCompatPatch");
});
it("deepMergeCompatByProtocol accepts preserveVideoUrl under openai protocol", async () => {
const { deepMergeCompatByProtocol } = await import("@/lib/db/models/compat");
const result = deepMergeCompatByProtocol(
{},
{
openai: { preserveVideoUrl: true },
}
);
const result = deepMergeCompatByProtocol({}, {
openai: { preserveVideoUrl: true },
});
// Valid protocol keys are 'openai', 'openai-responses', 'claude'
equal(result.openai?.preserveVideoUrl, true);
});

View File

@@ -218,101 +218,3 @@ test("setRadarKey uses existing AES-256-GCM encryption from encryption.ts", () =
const parts = body.split(":");
assert.equal(parts.length, 3, "must have 3 parts (iv:ciphertext:authTag)");
});
// ---------------------------------------------------------------------------
// radar_referrals_cache (migration 142) -- standalone `GET /v1/referrals/latest`
// cache, separate from radar_feed_cache (the catalog feed).
// ---------------------------------------------------------------------------
test("getRadarReferralsCache returns null when no cache exists", () => {
const result = radar.getRadarReferralsCache();
assert.equal(result, null, "empty referrals cache must return null");
});
test("setRadarReferralsCache then getRadarReferralsCache round-trips exactly", () => {
const entry = {
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "live",
payload: JSON.stringify({ referrals: { fixed: [], campaigns: [] } }),
signature: "ed25519:referrals-sig-abc",
};
radar.setRadarReferralsCache(entry);
const result = radar.getRadarReferralsCache();
assert.ok(result, "referrals cache must not be null after set");
assert.equal(result.generatedAt, entry.generatedAt, "generatedAt must round-trip");
assert.equal(result.tier, entry.tier, "tier must round-trip");
assert.equal(result.payload, entry.payload, "payload must round-trip byte-identically");
assert.equal(result.signature, entry.signature, "signature must round-trip");
assert.ok(result.fetchedAt, "fetchedAt must be set");
});
test("second setRadarReferralsCache REPLACES the row (still single row)", () => {
const db = core.getDbInstance();
radar.setRadarReferralsCache({
generatedAt: "2026-08-07T10:00:00.000Z",
tier: "community",
payload: '{"old":true}',
signature: "sig-old",
});
radar.setRadarReferralsCache({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "live",
payload: '{"new":true}',
signature: "sig-new",
});
const result = radar.getRadarReferralsCache();
assert.ok(result);
assert.equal(result.generatedAt, "2026-08-07T12:00:00.000Z", "must have the second generatedAt");
assert.equal(result.tier, "live", "must have the second tier");
assert.equal(result.payload, '{"new":true}', "must have the second payload");
const count = db.prepare("SELECT COUNT(*) AS c FROM radar_referrals_cache").get() as { c: number };
assert.equal(count.c, 1, "must have exactly one row");
});
test("setRadarReferralsCache uses fetchedAt when provided", () => {
const fixed = "2026-08-07T12:00:00.000Z";
radar.setRadarReferralsCache({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "community",
payload: "{}",
signature: "sig",
fetchedAt: fixed,
});
const result = radar.getRadarReferralsCache();
assert.ok(result);
assert.equal(result.fetchedAt, fixed, "must use the provided fetchedAt");
});
test("radar_referrals_cache is independent of radar_feed_cache (separate tables)", () => {
radar.setRadarCache({
version: "2026.08.01.1",
tier: "community",
payload: '{"catalog":true}',
signature: "catalog-sig",
});
radar.setRadarReferralsCache({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "live",
payload: '{"referrals":true}',
signature: "referrals-sig",
});
const catalogCache = radar.getRadarCache();
const referralsCache = radar.getRadarReferralsCache();
assert.equal(catalogCache?.payload, '{"catalog":true}');
assert.equal(referralsCache?.payload, '{"referrals":true}');
assert.notEqual(
catalogCache?.payload,
referralsCache?.payload,
"the two caches must never share storage"
);
});

View File

@@ -1,26 +1,19 @@
/**
* tests/unit/radar-referrals-route.test.ts
*
* TDD regression guard for GET /api/radar/referrals (D28 -- referral links /
* TDD regression guard for GET /api/radar/referrals (D28 referral links /
* free credits, client side). Mirrors tests/unit/radar-api-routes.test.ts:
*
* - Flag off => 404, checked BEFORE auth (byte-identical inertia).
* - Flag on, no auth => 401.
* - Flag on, authenticated, no cache => 200 with { fixed: [], campaigns:
* [], tier: null }.
* - Flag on, authenticated, cached referrals feed => 200 with the cached
* fixed/campaigns + tier.
* - Sync-on-read: the route triggers `syncRadarReferrals()` inline when the
* cache is stale/missing (opt-in false in every test here, so the
* triggered sync always self-gates to a safe `opt_out` no-op -- this
* proves the trigger never touches the network in these tests while
* still exercising the code path).
* - Flag on, authenticated, cached feed with referrals => 200 with the
* cached fixed/campaigns + tier.
* - Error responses never leak stack traces (Hard Rule #12).
*
* NEVER proxies the private feed server directly -- this route's own source
* contains no `fetch(` call; the network only happens inside
* `syncRadarReferrals()` (`src/lib/radar/referralsSync.ts`), which this
* route calls but never inlines.
* NEVER proxies the private feed server — this route only reads the local
* cache written by POST /api/radar/sync.
*/
import test from "node:test";
@@ -76,12 +69,18 @@ function resetStorage() {
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
function baseReferralsFeed(): Record<string, unknown> {
function baseFeed(): Record<string, unknown> {
return {
feed: "omniroute-radar-referrals",
feed: "omniroute-radar",
schemaVersion: 1,
version: "2026-08-07.1",
generatedAt: new Date().toISOString(),
referrals: { fixed: [], campaigns: [] },
tier: "live",
counts: { providers: 0, models: 0 },
providers: [],
models: [],
quirks: [],
totals: { dedupedTokensPerMonth: 0, modelCount: 0, poolCount: 0 },
};
}
@@ -115,7 +114,7 @@ test("GET /api/radar/referrals: flag on, no auth => 401", async () => {
assert.ok(!JSON.stringify(body).includes("at /"), "Response must not leak stack traces");
});
test("GET /api/radar/referrals: flag on, authenticated, no cache => 200 empty shape (sync-on-read no-ops: opt-in false)", async () => {
test("GET /api/radar/referrals: flag on, authenticated, no cache => 200 empty shape", async () => {
resetStorage();
process.env.RADAR_ENABLED = "true";
@@ -129,12 +128,12 @@ test("GET /api/radar/referrals: flag on, authenticated, no cache => 200 empty sh
assert.equal(body.tier, null);
});
test("GET /api/radar/referrals: flag on, authenticated, cached referrals feed => returns fixed/campaigns/tier", async () => {
test("GET /api/radar/referrals: flag on, authenticated, cached feed => returns fixed/campaigns/tier", async () => {
resetStorage();
process.env.RADAR_ENABLED = "true";
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: {
fixed: [
{
@@ -149,15 +148,11 @@ test("GET /api/radar/referrals: flag on, authenticated, cached referrals feed =>
campaigns: [],
},
};
radarDb.setRadarReferralsCache({
generatedAt: feed.generatedAt as string,
radarDb.setRadarCache({
version: "2026-08-07.1",
tier: "live",
payload: JSON.stringify(feed),
signature: "test-signature",
// Fresh timestamp -- inside the 1h staleness window, so sync-on-read
// does NOT overwrite this row (opt-in is false anyway, but this also
// proves the "not stale" branch is exercised, not just "opt_out").
fetchedAt: new Date().toISOString(),
});
const { GET } = await import("../../src/app/api/radar/referrals/route.ts");
@@ -171,49 +166,6 @@ test("GET /api/radar/referrals: flag on, authenticated, cached referrals feed =>
assert.equal(body.tier, "live");
});
test("GET /api/radar/referrals: stale cached referrals feed still served (sync-on-read triggers but opt-in false => no-op, cache untouched)", async () => {
resetStorage();
process.env.RADAR_ENABLED = "true";
const feed = {
...baseReferralsFeed(),
referrals: {
fixed: [
{
provider: "cerebras",
url: "https://cerebras.ai/?ref=omniroute",
kind: "fixo",
validUntil: null,
requiredAction: null,
isDefault: true,
},
],
campaigns: [],
},
};
const staleFetchedAt = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(); // 2h ago
radarDb.setRadarReferralsCache({
generatedAt: feed.generatedAt as string,
tier: "community",
payload: JSON.stringify(feed),
signature: "test-signature",
fetchedAt: staleFetchedAt,
});
const { GET } = await import("../../src/app/api/radar/referrals/route.ts");
const response = await GET(mockGetRequest(undefined, await authHeaders()));
const body = await response.json();
assert.equal(response.status, 200);
assert.equal(body.fixed.length, 1, "stale cache is still served while the sync-on-read no-ops");
assert.equal(body.fixed[0].provider, "cerebras");
assert.equal(body.tier, "community");
// The no-op sync must never have overwritten fetchedAt/cache contents.
const cacheAfter = radarDb.getRadarReferralsCache();
assert.equal(cacheAfter?.fetchedAt, staleFetchedAt);
});
test("GET /api/radar/referrals: never proxies the private feed server (route source has no upstream fetch)", async () => {
const routeSrc = fs.readFileSync(
path.resolve(process.cwd(), "src/app/api/radar/referrals/route.ts"),

View File

@@ -1,672 +0,0 @@
/**
* tests/unit/radar-referrals-sync.test.ts
*
* TDD regression guard for the standalone Radar referrals feed sync layer
* (`GET /v1/referrals/latest`) — the fix that removes the up-to-30-day
* community-tier delay referral links used to inherit from the catalog
* feed:
* - referralsFeedSchema.ts: Zod schema validation
* - referralsSync.ts: download/verify/validate/cache pipeline +
* `shouldSyncReferralsOnRead` staleness helper
*
* Mirrors tests/unit/radar-sync.test.ts's structure and conventions (same
* ephemeral Ed25519 keypair + `RADAR_FEED_PUBKEY` override, same
* mockResponse() shape) — the referrals feed reuses the exact same pinned
* key as the catalog feed.
*/
import test from "node:test";
import assert from "node:assert/strict";
import crypto from "node:crypto";
// ---------------------------------------------------------------------------
// Generate ephemeral Ed25519 keypair for testing
// ---------------------------------------------------------------------------
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const PUB_KEY_DER = publicKey.export({ type: "spki", format: "der" });
const PUB_KEY_B64 = PUB_KEY_DER.toString("base64");
// Inject as env override so pinnedKeys.ts picks it up (fork path) — same
// pinned key backs both the catalog and the referrals feed.
process.env.RADAR_FEED_PUBKEY = PUB_KEY_B64;
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function signBytes(bytes: Buffer): string {
const sig = crypto.sign(null, bytes, privateKey);
return sig.toString("base64");
}
function tamperByte(buf: Buffer): Buffer {
const copy = Buffer.from(buf);
copy[0] = copy[0] ^ 0xff;
return copy;
}
/** Build a minimal Response-like object for fetch mock (matches radar-sync.test.ts). */
function mockResponse(body: Buffer, headers: Record<string, string> = {}, status = 200): Response {
return {
ok: status >= 200 && status < 300,
status,
headers: new Map(Object.entries(headers)),
arrayBuffer: () =>
Promise.resolve(body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength)),
} as unknown as Response;
}
function baseReferralsFeed(generatedAt = "2026-08-07T12:00:00.000Z"): Record<string, unknown> {
return {
feed: "omniroute-radar-referrals",
schemaVersion: 1,
generatedAt,
referrals: {
fixed: [
{
provider: "groq",
url: "https://groq.com/?ref=omniroute",
kind: "fixo",
validUntil: null,
requiredAction: null,
isDefault: true,
},
],
campaigns: [],
},
};
}
function feedBytes(feed: Record<string, unknown>): Buffer {
return Buffer.from(JSON.stringify(feed));
}
// ---------------------------------------------------------------------------
// Import modules under test (after env override)
// ---------------------------------------------------------------------------
const referralsFeedSchema = await import("../../src/lib/radar/referralsFeedSchema.ts");
const referralsSync = await import("../../src/lib/radar/referralsSync.ts");
// ===========================================================================
// referralsFeedSchema.ts
// ===========================================================================
test("RadarReferralsFeedSchema: valid feed parses successfully", () => {
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(baseReferralsFeed());
assert.equal(
result.success,
true,
"valid feed must parse: " + (result.success ? "" : JSON.stringify(result.error?.issues))
);
});
test("RadarReferralsFeedSchema: rejects wrong feed literal", () => {
const feed = { ...baseReferralsFeed(), feed: "omniroute-radar" };
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(feed);
assert.equal(result.success, false, "must reject a catalog-feed literal");
});
test("RadarReferralsFeedSchema: rejects wrong schemaVersion", () => {
const feed = { ...baseReferralsFeed(), schemaVersion: 2 };
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(feed);
assert.equal(result.success, false);
});
test("RadarReferralsFeedSchema: rejects missing referrals section", () => {
const feed = baseReferralsFeed();
delete (feed as Record<string, unknown>).referrals;
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(feed);
assert.equal(result.success, false, "referrals section is required (no old-feed compat needed here)");
});
test("RadarReferralsFeedSchema: rejects a non-https referral url", () => {
const feed = baseReferralsFeed();
(feed.referrals as { fixed: Array<Record<string, unknown>> }).fixed[0]!.url =
"http://groq.com/?ref=omniroute";
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(feed);
assert.equal(result.success, false);
});
test("RadarReferralsFeedSchema: rejects an invalid generatedAt", () => {
const feed = { ...baseReferralsFeed(), generatedAt: "not-a-date" };
const result = referralsFeedSchema.RadarReferralsFeedSchema.safeParse(feed);
assert.equal(result.success, false);
});
// ===========================================================================
// shouldSyncReferralsOnRead
// ===========================================================================
test("shouldSyncReferralsOnRead: null fetchedAt => stale (sync now)", () => {
assert.equal(referralsSync.shouldSyncReferralsOnRead(null, Date.now()), true);
});
test("shouldSyncReferralsOnRead: unparseable fetchedAt => stale", () => {
assert.equal(referralsSync.shouldSyncReferralsOnRead("garbage", Date.now()), true);
});
test("shouldSyncReferralsOnRead: fresh (< 1h) => not stale", () => {
const now = Date.parse("2026-08-07T12:00:00.000Z");
const fetchedAt = new Date(now - 30 * 60 * 1000).toISOString(); // 30m ago
assert.equal(referralsSync.shouldSyncReferralsOnRead(fetchedAt, now), false);
});
test("shouldSyncReferralsOnRead: exactly at the boundary => stale", () => {
const now = Date.parse("2026-08-07T12:00:00.000Z");
const fetchedAt = new Date(now - 60 * 60 * 1000).toISOString(); // exactly 1h ago
assert.equal(referralsSync.shouldSyncReferralsOnRead(fetchedAt, now), true);
});
test("shouldSyncReferralsOnRead: old (> 1h) => stale", () => {
const now = Date.parse("2026-08-07T12:00:00.000Z");
const fetchedAt = new Date(now - 2 * 60 * 60 * 1000).toISOString(); // 2h ago
assert.equal(referralsSync.shouldSyncReferralsOnRead(fetchedAt, now), true);
});
// ===========================================================================
// syncRadarReferrals — gating (flag/opt-in), never touching the network
// ===========================================================================
test("syncRadarReferrals: flag off => disabled, no fetch call", async () => {
let fetchCalled = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => false,
fetch: (() => {
fetchCalled = true;
return Promise.resolve(mockResponse(Buffer.from("{}")));
}) as unknown as typeof globalThis.fetch,
});
assert.deepEqual(result, { status: "disabled" });
assert.equal(fetchCalled, false);
});
test("syncRadarReferrals: opt-in false => opt_out, no fetch call", async () => {
let fetchCalled = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: false, supporterKey: null }),
fetch: (() => {
fetchCalled = true;
return Promise.resolve(mockResponse(Buffer.from("{}")));
}) as unknown as typeof globalThis.fetch,
});
assert.deepEqual(result, { status: "opt_out" });
assert.equal(fetchCalled, false);
});
// ===========================================================================
// syncRadarReferrals — signature verification over exact bytes
// ===========================================================================
test("syncRadarReferrals: valid signature => cache updated, payload byte-identical", async () => {
const feed = baseReferralsFeed();
const bytes = feedBytes(feed);
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig, "x-omniroute-feed-tier": "community" })
)) as unknown as typeof globalThis.fetch,
now: () => new Date("2026-08-07T12:05:00.000Z"),
});
assert.equal(result.status, "updated");
assert.equal(cacheStore.length, 1);
assert.equal(cacheStore[0]!.payload, bytes.toString("utf-8"));
assert.equal(cacheStore[0]!.generatedAt, feed.generatedAt);
assert.equal(cacheStore[0]!.tier, "community");
assert.equal(cacheStore[0]!.signature, sig);
assert.equal(cacheStore[0]!.fetchedAt, "2026-08-07T12:05:00.000Z");
});
test("syncRadarReferrals: tampered bytes => invalid_signature, cache untouched", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const tampered = tamperByte(bytes);
let cacheWritten = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {
cacheWritten = true;
},
fetch: (() =>
Promise.resolve(
mockResponse(tampered, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "invalid_signature");
assert.equal(cacheWritten, false);
});
test("syncRadarReferrals: missing signature header => invalid_signature", async () => {
const bytes = feedBytes(baseReferralsFeed());
let cacheWritten = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {
cacheWritten = true;
},
fetch: (() => Promise.resolve(mockResponse(bytes, {}))) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "invalid_signature");
assert.equal(cacheWritten, false);
});
test("syncRadarReferrals: valid sig over garbage JSON => invalid_schema, cache untouched", async () => {
const garbageBytes = Buffer.from('{"not":"a-valid-referrals-feed"}');
const sig = signBytes(garbageBytes);
let cacheWritten = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {
cacheWritten = true;
},
fetch: (() =>
Promise.resolve(
mockResponse(garbageBytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "invalid_schema");
assert.equal(cacheWritten, false);
});
// ===========================================================================
// syncRadarReferrals — generatedAt floor (replay/no-op guard)
// ===========================================================================
test("syncRadarReferrals: same generatedAt as cache => stale, cache untouched", async () => {
const feed = baseReferralsFeed("2026-08-07T12:00:00.000Z");
const bytes = feedBytes(feed);
const sig = signBytes(bytes);
let cacheWritten = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => ({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "community",
payload: "{}",
signature: "old-sig",
}),
setCache: () => {
cacheWritten = true;
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "stale");
assert.equal(cacheWritten, false);
});
test("syncRadarReferrals: older generatedAt than cache => stale (replay rejected)", async () => {
const feed = baseReferralsFeed("2026-08-07T10:00:00.000Z"); // older
const bytes = feedBytes(feed);
const sig = signBytes(bytes);
let cacheWritten = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => ({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "community",
payload: "{}",
signature: "old-sig",
}),
setCache: () => {
cacheWritten = true;
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "stale");
assert.equal(cacheWritten, false);
});
test("syncRadarReferrals: newer generatedAt than cache => updated", async () => {
const feed = baseReferralsFeed("2026-08-07T13:00:00.000Z"); // newer
const bytes = feedBytes(feed);
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => ({
generatedAt: "2026-08-07T12:00:00.000Z",
tier: "community",
payload: "{}",
signature: "old-sig",
}),
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
now: () => new Date("2026-08-07T13:05:00.000Z"),
});
assert.equal(result.status, "updated");
assert.equal(cacheStore.length, 1);
assert.equal(cacheStore[0]!.generatedAt, "2026-08-07T13:00:00.000Z");
});
// ===========================================================================
// syncRadarReferrals — 2 identical requests => same signature => same cache
// (determinism contract from the server: generatedAt is the max updatedAt
// across referral links, so unchanged data re-signs identically)
// ===========================================================================
test("syncRadarReferrals: two identical fetches (no cache between) produce identical cache entries modulo fetchedAt", async () => {
const feed = baseReferralsFeed();
const bytes = feedBytes(feed);
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const run = () =>
referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null, // simulate two independent "first sync" calls
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
now: () => new Date("2026-08-07T12:05:00.000Z"),
});
await run();
await run();
assert.equal(cacheStore.length, 2);
assert.equal(cacheStore[0]!.signature, cacheStore[1]!.signature);
assert.equal(cacheStore[0]!.payload, cacheStore[1]!.payload);
assert.equal(cacheStore[0]!.generatedAt, cacheStore[1]!.generatedAt);
});
// ===========================================================================
// syncRadarReferrals — served-tier header
// ===========================================================================
test("syncRadarReferrals: header 'community' => cache + result use community", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, {
"x-omniroute-feed-signature": sig,
"x-omniroute-feed-tier": "community",
})
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "updated");
if (result.status === "updated") assert.equal(result.tier, "community");
assert.equal(cacheStore[0]!.tier, "community");
});
test("syncRadarReferrals: header 'live' (supporter key) => cache + result use live", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: "omr_supporter-key" }),
getCache: () => null,
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, {
"x-omniroute-feed-signature": sig,
"x-omniroute-feed-tier": "live",
})
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "updated");
if (result.status === "updated") assert.equal(result.tier, "live");
assert.equal(cacheStore[0]!.tier, "live");
});
test("syncRadarReferrals: header absent => falls back to 'community' (no body tier field to fall back to)", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(mockResponse(bytes, { "x-omniroute-feed-signature": sig }))) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "updated");
if (result.status === "updated") assert.equal(result.tier, "community");
assert.equal(cacheStore[0]!.tier, "community");
});
test("syncRadarReferrals: garbage tier header => never trusted, falls back to 'community'", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const cacheStore: referralsSync.RadarReferralsCacheEntry[] = [];
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: (entry) => {
cacheStore.push(entry);
},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, {
"x-omniroute-feed-signature": sig,
"x-omniroute-feed-tier": "premium",
})
)) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "updated");
if (result.status === "updated") {
assert.equal(result.tier, "community");
assert.notEqual(result.tier as string, "premium");
}
});
// ===========================================================================
// syncRadarReferrals — Authorization header (supporter key)
// ===========================================================================
test("syncRadarReferrals: sends Authorization header when supporter key exists", async () => {
let capturedHeaders: Record<string, string> = {};
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: "omr_test-key-123" }),
getCache: () => null,
setCache: () => {},
fetch: ((url: string, init: RequestInit) => {
capturedHeaders = Object.fromEntries(
(init.headers as Record<string, string> | undefined)
? Object.entries(init.headers as Record<string, string>)
: []
);
return Promise.resolve(mockResponse(bytes, { "x-omniroute-feed-signature": sig }));
}) as unknown as typeof globalThis.fetch,
});
assert.equal(capturedHeaders["Authorization"], "Bearer omr_test-key-123");
});
test("syncRadarReferrals: no Authorization header when no supporter key", async () => {
let capturedHeaders: Record<string, string> = {};
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {},
fetch: ((url: string, init: RequestInit) => {
capturedHeaders = Object.fromEntries(
(init.headers as Record<string, string> | undefined)
? Object.entries(init.headers as Record<string, string>)
: []
);
return Promise.resolve(mockResponse(bytes, { "x-omniroute-feed-signature": sig }));
}) as unknown as typeof globalThis.fetch,
});
assert.equal(capturedHeaders["Authorization"], undefined);
});
// ===========================================================================
// syncRadarReferrals — errors, never leaking a stack
// ===========================================================================
test("syncRadarReferrals: network error => error with no stack in reason", async () => {
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
fetch: (() => Promise.reject(new Error("ECONNREFUSED"))) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "error");
if (result.status === "error") {
assert.ok(result.reason.length > 0);
assert.ok(!result.reason.includes("at ") && !result.reason.includes(".ts:"));
}
});
test("syncRadarReferrals: HTTP non-200 => error mentioning the status code", async () => {
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
fetch: (() =>
Promise.resolve(mockResponse(Buffer.from("Internal Server Error"), {}, 500))) as unknown as typeof globalThis.fetch,
});
assert.equal(result.status, "error");
if (result.status === "error") assert.ok(result.reason.includes("500"));
});
// ===========================================================================
// syncRadarReferrals — 10 MB response cap
// ===========================================================================
test("FIX: Content-Length exceeding the 10MB cap => too_large, cache untouched, body never read", async () => {
let arrayBufferCalled = false;
const oversizedContentLength = String(10 * 1024 * 1024 + 1);
const response = mockResponse(Buffer.from("irrelevant"), {
"content-length": oversizedContentLength,
});
const originalArrayBuffer = response.arrayBuffer.bind(response);
(response as unknown as { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer = () => {
arrayBufferCalled = true;
return originalArrayBuffer();
};
let setCacheCalled = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {
setCacheCalled = true;
},
fetch: (() => Promise.resolve(response)) as unknown as typeof globalThis.fetch,
});
assert.deepEqual(result, { status: "too_large" });
assert.equal(setCacheCalled, false);
assert.equal(arrayBufferCalled, false);
});
test("FIX: oversized body without a trustworthy Content-Length header => too_large, cache untouched", async () => {
const oversized = Buffer.alloc(10 * 1024 * 1024 + 1, 0x41);
let setCacheCalled = false;
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {
setCacheCalled = true;
},
fetch: (() => Promise.resolve(mockResponse(oversized, {}))) as unknown as typeof globalThis.fetch,
});
assert.deepEqual(result, { status: "too_large" });
assert.equal(setCacheCalled, false);
});
test("FIX: body within the 10MB cap proceeds normally (never returns too_large)", async () => {
const bytes = feedBytes(baseReferralsFeed());
const sig = signBytes(bytes);
const result = await referralsSync.syncRadarReferrals({
getFlag: () => true,
getSettings: () => ({ optIn: true, supporterKey: null }),
getCache: () => null,
setCache: () => {},
fetch: (() =>
Promise.resolve(
mockResponse(bytes, { "x-omniroute-feed-signature": sig })
)) as unknown as typeof globalThis.fetch,
});
assert.notEqual(result.status, "too_large");
});

View File

@@ -2,33 +2,29 @@
* tests/unit/radar-referrals.test.ts
*
* TDD regression guard for the client-side "referral links / free credits"
* feature (D28). Referral links now come from the STANDALONE, always-current
* `GET /v1/referrals/latest` feed (`radar_referrals_cache` table /
* `referralsSync.ts`) instead of being extracted from the catalog feed's
* cached snapshot -- the catalog feed on the community tier can be up to 30
* days stale, so referral links extracted from it used to lag the server by
* the same amount. This suite covers the CLIENT side only:
* feature (D28). The server already publishes a `referrals` section on the
* signed feed (`{ fixed: RadarReferral[], campaigns: RadarReferral[] }`) —
* this suite covers the CLIENT side only:
*
* - RadarReferralsFeedSchema (`referralsFeedSchema.ts`): valid feed parses;
* an invalid referral (non-https url) is rejected. (Schema-level
* coverage for the referrals feed's error/replay/tier paths lives in
* `tests/unit/radar-referrals-sync.test.ts`.)
* - RadarFeedSchema: a feed WITHOUT `referrals` stays valid (compat with
* old cached feeds); a feed WITH an invalid referral (non-https url) is
* rejected.
* - getRadarReferrals(): flag off => {fixed:[],campaigns:[]}; no cache =>
* same; corrupt cache => same (never throws).
* same; corrupt cache => same; feed without the section => same
* (never throws).
* - getDefaultReferralFor(): returns the fixed+isDefault referral for a
* provider, ignores campaigns, returns null when none.
* - findDefaultReferral() (pure helper, DB-free -- must be importable from
* a client bundle without pulling in @/lib/db/*) -- same contract as
* above, operating directly on a `fixed` array.
* - findDefaultReferral() (pure helper, DB-free must be importable from a
* client bundle without pulling in @/lib/db/*): same contract as above,
* operating directly on a `fixed` array.
*
* No DB is touched here -- all DB access is injected via `deps`, matching
* No DB is touched here all DB access is injected via `deps`, matching
* the existing tests/unit/radar-apply-feed.test.ts convention.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { RadarReferralsFeedSchema } from "../../src/lib/radar/referralsFeedSchema.ts";
import type { RadarReferral } from "../../src/lib/radar/feedSchema.ts";
import { RadarFeedSchema, type RadarFeed, type RadarReferral } from "../../src/lib/radar/feedSchema.ts";
import { findDefaultReferral } from "../../src/lib/radar/referrals.ts";
import { getRadarReferrals, getDefaultReferralFor } from "../../src/lib/radar/index.ts";
@@ -36,13 +32,18 @@ import { getRadarReferrals, getDefaultReferralFor } from "../../src/lib/radar/in
// Fixtures
// ---------------------------------------------------------------------------
/** A standalone referrals feed (`GET /v1/referrals/latest` shape). */
function baseReferralsFeed(): Record<string, unknown> {
function baseFeed(): Record<string, unknown> {
return {
feed: "omniroute-radar-referrals",
feed: "omniroute-radar",
schemaVersion: 1,
version: "2026-08-07.1",
generatedAt: new Date().toISOString(),
referrals: { fixed: [], campaigns: [] },
tier: "live",
counts: { providers: 0, models: 0 },
providers: [],
models: [],
quirks: [],
totals: { dedupedTokensPerMonth: 0, modelCount: 0, poolCount: 0 },
};
}
@@ -58,29 +59,25 @@ function makeReferral(overrides: Partial<RadarReferral> = {}): RadarReferral {
};
}
/** Build a `getRadarReferralsCache()`-shaped row from a referrals feed object. */
function cacheRowFor(feed: Record<string, unknown>, overrides: Record<string, unknown> = {}) {
return {
generatedAt: feed.generatedAt as string,
tier: "live",
payload: JSON.stringify(feed),
fetchedAt: new Date().toISOString(),
...overrides,
};
}
// ---------------------------------------------------------------------------
// RadarReferralsFeedSchema -- validation
// RadarFeedSchema — compat + validation
// ---------------------------------------------------------------------------
test("RadarReferralsFeedSchema: minimal empty-referrals feed parses successfully", () => {
const parsed = RadarReferralsFeedSchema.parse(baseReferralsFeed());
test("RadarFeedSchema: feed without `referrals` stays valid (old-feed compat)", () => {
const parsed = RadarFeedSchema.parse(baseFeed());
assert.deepEqual(parsed.referrals, { fixed: [], campaigns: [] });
});
test("RadarReferralsFeedSchema: full referrals section round-trips", () => {
test("RadarFeedSchema: feed with `referrals.fixed` but no `campaigns` defaults campaigns to []", () => {
const feed = { ...baseFeed(), referrals: { fixed: [makeReferral()] } };
const parsed = RadarFeedSchema.parse(feed);
assert.equal(parsed.referrals.fixed.length, 1);
assert.deepEqual(parsed.referrals.campaigns, []);
});
test("RadarFeedSchema: full referrals section round-trips", () => {
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: {
fixed: [makeReferral()],
campaigns: [
@@ -94,30 +91,30 @@ test("RadarReferralsFeedSchema: full referrals section round-trips", () => {
],
},
};
const parsed = RadarReferralsFeedSchema.parse(feed);
const parsed = RadarFeedSchema.parse(feed);
assert.equal(parsed.referrals.fixed.length, 1);
assert.equal(parsed.referrals.campaigns.length, 1);
assert.equal(parsed.referrals.campaigns[0]!.kind, "campanha");
});
test("RadarReferralsFeedSchema: rejects a referral with a non-https url", () => {
test("RadarFeedSchema: rejects a referral with a non-https url", () => {
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: { fixed: [makeReferral({ url: "http://groq.com/?ref=omniroute" })], campaigns: [] },
};
assert.throws(() => RadarReferralsFeedSchema.parse(feed));
assert.throws(() => RadarFeedSchema.parse(feed));
});
test("RadarReferralsFeedSchema: rejects an invalid `kind`", () => {
test("RadarFeedSchema: rejects an invalid `kind`", () => {
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: { fixed: [{ ...makeReferral(), kind: "bogus" }], campaigns: [] },
};
assert.throws(() => RadarReferralsFeedSchema.parse(feed));
assert.throws(() => RadarFeedSchema.parse(feed));
});
// ---------------------------------------------------------------------------
// findDefaultReferral -- pure, DB-free helper (client-safe)
// findDefaultReferral pure, DB-free helper (client-safe)
// ---------------------------------------------------------------------------
test("findDefaultReferral: returns the fixed+isDefault referral for the provider", () => {
@@ -144,7 +141,7 @@ test("findDefaultReferral: empty array => null", () => {
});
// ---------------------------------------------------------------------------
// getRadarReferrals() -- flag/cache gating, never throws
// getRadarReferrals() flag/cache gating, never throws
// ---------------------------------------------------------------------------
test("getRadarReferrals: flag off => empty, cache never read", () => {
@@ -169,7 +166,7 @@ test("getRadarReferrals: flag on, corrupt cache payload => empty (defensive, nev
const result = getRadarReferrals({
getFlag: () => true,
getCache: () => ({
generatedAt: "x",
version: "x",
tier: "live",
payload: "{not-json",
fetchedAt: new Date().toISOString(),
@@ -178,41 +175,37 @@ test("getRadarReferrals: flag on, corrupt cache payload => empty (defensive, nev
assert.deepEqual(result, { fixed: [], campaigns: [] });
});
test("getRadarReferrals: flag on, cached payload fails schema validation => empty (defensive)", () => {
test("getRadarReferrals: flag on, cached feed has no `referrals` section => empty", () => {
const result = getRadarReferrals({
getFlag: () => true,
getCache: () => ({
generatedAt: "x",
version: "x",
tier: "live",
// Wrong `feed` literal -- fails RadarReferralsFeedSchema.
payload: JSON.stringify({ ...baseReferralsFeed(), feed: "omniroute-radar" }),
payload: JSON.stringify(baseFeed()),
fetchedAt: new Date().toISOString(),
}),
});
assert.deepEqual(result, { fixed: [], campaigns: [] });
});
test("getRadarReferrals: flag on, cached referrals feed => returns them", () => {
test("getRadarReferrals: flag on, cached feed has referrals => returns them", () => {
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: { fixed: [makeReferral()], campaigns: [] },
};
const result = getRadarReferrals({
getFlag: () => true,
getCache: () => cacheRowFor(feed),
getCache: () => ({
version: "x",
tier: "live",
payload: JSON.stringify(feed),
fetchedAt: new Date().toISOString(),
}),
});
assert.equal(result.fixed.length, 1);
assert.equal(result.fixed[0]!.provider, "groq");
});
test("getRadarReferrals: default getCache reads from getRadarReferralsCache (module wiring)", async () => {
// Confirms the accessor's default dep is the NEW referrals cache reader,
// not the old catalog cache -- exercised via the flag-off short-circuit
// (no DB touch needed) so this stays a pure unit test.
const result = getRadarReferrals({ getFlag: () => false });
assert.deepEqual(result, { fixed: [], campaigns: [] });
});
// ---------------------------------------------------------------------------
// getDefaultReferralFor()
// ---------------------------------------------------------------------------
@@ -224,7 +217,7 @@ test("getDefaultReferralFor: flag off => null", () => {
test("getDefaultReferralFor: returns the fixed default referral, ignoring campaigns", () => {
const feed = {
...baseReferralsFeed(),
...baseFeed(),
referrals: {
fixed: [makeReferral({ provider: "groq", isDefault: true })],
campaigns: [makeReferral({ provider: "groq", kind: "campanha", isDefault: true })],
@@ -232,16 +225,26 @@ test("getDefaultReferralFor: returns the fixed default referral, ignoring campai
};
const result = getDefaultReferralFor("groq", {
getFlag: () => true,
getCache: () => cacheRowFor(feed),
getCache: () => ({
version: "x",
tier: "live",
payload: JSON.stringify(feed),
fetchedAt: new Date().toISOString(),
}),
});
assert.equal(result?.kind, "fixo");
});
test("getDefaultReferralFor: provider with no default referral => null", () => {
const feed = { ...baseReferralsFeed(), referrals: { fixed: [], campaigns: [] } };
const feed = { ...baseFeed(), referrals: { fixed: [], campaigns: [] } };
const result = getDefaultReferralFor("groq", {
getFlag: () => true,
getCache: () => cacheRowFor(feed),
getCache: () => ({
version: "x",
tier: "live",
payload: JSON.stringify(feed),
fetchedAt: new Date().toISOString(),
}),
});
assert.equal(result, null);
});

View File

@@ -20,11 +20,6 @@ const {
const NOW = Date.parse("2026-08-06T12:00:00.000Z");
const FRESH = new Date(NOW - 60 * 60 * 1000).toISOString(); // 1h ago — inside the daily window
const STALE = new Date(NOW - 25 * 60 * 60 * 1000).toISOString(); // 25h ago — due
// Referrals staleness window is much shorter (1h, see REFERRALS_STALE_MS) —
// this default must sit well inside it so existing catalog-only subtests
// never trigger a referrals sync as an unasserted side effect.
const REFERRALS_FRESH = new Date(NOW - 5 * 60 * 1000).toISOString(); // 5m ago
const REFERRALS_STALE = new Date(NOW - 2 * 60 * 60 * 1000).toISOString(); // 2h ago — due
/** Fake interval registry so no real timer ever exists in these tests. */
function fakeTimers() {
@@ -45,11 +40,9 @@ function fakeTimers() {
function deps(overrides: Record<string, unknown> = {}) {
const syncCalls: number[] = [];
const referralsSyncCalls: number[] = [];
const timers = fakeTimers();
return {
syncCalls,
referralsSyncCalls,
timers,
d: {
getFlag: () => true,
@@ -59,15 +52,6 @@ function deps(overrides: Record<string, unknown> = {}) {
syncCalls.push(1);
return { status: "updated", version: "2026.08.06.1", tier: "live" } as const;
},
// Referrals side-sync — separate cache/sync from the catalog above.
// Defaults to a FRESH referrals cache so existing subtests (which
// don't care about referrals at all) never trigger a referrals sync
// as an unasserted side effect.
getReferralsCache: () => ({ fetchedAt: REFERRALS_FRESH }),
syncReferrals: async () => {
referralsSyncCalls.push(1);
return { status: "updated", generatedAt: "2026-08-06T12:00:00.000Z", tier: "live" } as const;
},
now: () => NOW,
setIntervalFn: timers.setIntervalFn,
clearIntervalFn: timers.clearIntervalFn,
@@ -166,69 +150,4 @@ test("radar sync scheduler", async (t) => {
assert.equal(initRadarSyncScheduler(d), false);
assert.equal(timers.registered.length, 0);
});
// -------------------------------------------------------------------------
// Referrals side-sync — piggybacks on the same hourly tick but its own
// (much shorter, 1h) staleness window, independent of the catalog's
// due-ness. Never surfaces in RadarTickResult (fire-and-await side effect
// only) so the catalog-sync result shape/assertions above stay unchanged.
// -------------------------------------------------------------------------
await t.test("tick: referrals cache fresh => referrals sync NOT called (catalog path unaffected)", async () => {
const { d, syncCalls, referralsSyncCalls } = deps();
const result = await radarSchedulerTick(d);
assert.equal(result.action, "synced", "catalog was due and must still sync as before");
assert.equal(syncCalls.length, 1);
assert.equal(referralsSyncCalls.length, 0, "referrals cache was fresh — must not sync");
});
await t.test("tick: referrals cache stale => referrals sync called, independent of catalog due-ness", async () => {
const { d, syncCalls, referralsSyncCalls } = deps({
getCache: () => ({ fetchedAt: FRESH }), // catalog NOT due
getReferralsCache: () => ({ fetchedAt: REFERRALS_STALE }), // referrals due
});
const result = await radarSchedulerTick(d);
assert.deepEqual(result, { action: "skipped", reason: "not_due" }, "catalog result shape must stay unchanged");
assert.equal(syncCalls.length, 0, "catalog must not sync — it was not due");
assert.equal(referralsSyncCalls.length, 1, "referrals were due and must sync independently");
});
await t.test("tick: referrals cache missing => referrals sync called (missing counts as stale)", async () => {
const { d, referralsSyncCalls } = deps({
getCache: () => ({ fetchedAt: FRESH }),
getReferralsCache: () => null,
});
await radarSchedulerTick(d);
assert.equal(referralsSyncCalls.length, 1);
});
await t.test("tick: flag off => referrals sync NOT called (stopped before any sync check)", async () => {
const { d, referralsSyncCalls } = deps({
getFlag: () => false,
getReferralsCache: () => null, // would be due if ever reached
});
await radarSchedulerTick(d);
assert.equal(referralsSyncCalls.length, 0);
});
await t.test("tick: opt-in off => referrals sync NOT called (skipped before any sync check)", async () => {
const { d, referralsSyncCalls } = deps({
getSettings: () => ({ optIn: false }),
getReferralsCache: () => null, // would be due if ever reached
});
await radarSchedulerTick(d);
assert.equal(referralsSyncCalls.length, 0);
});
await t.test("tick: referrals sync throwing => swallowed, catalog tick still completes normally", async () => {
const { d, syncCalls } = deps({
getReferralsCache: () => ({ fetchedAt: REFERRALS_STALE }),
syncReferrals: async () => {
throw new Error("referrals upstream exploded");
},
});
const result = await radarSchedulerTick(d);
assert.equal(result.action, "synced", "a throwing referrals sync must never break the catalog tick");
assert.equal(syncCalls.length, 1);
});
});