Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,15 @@ See the full history in [CHANGELOG.md](CHANGELOG.md).
| Raycast | Cookies (Chrome auto or manual) | Monthly AI credits, plan, renewal |
| Replicate | Cookies / token accounts | Monthly spend, credit balance |
| Aixy | API Key / token accounts | Applicable budget balances, 7-day key usage |
| ClawRouter | API Key | Monthly budget, requests, tokens, routed-provider spend |
| ElevenLabs | API Key | Subscription Credits, Voice Slots |
| Deepgram | API Key | Project Usage |
| Groq | API Key | Enterprise Metrics |
| LLM Proxy | API Key | Quota Stats |
| llmman | Local daemon / optional API Key | Memory in use, loaded and stored models |
| DevPass | API Key | Plan credits, Premium weekly, API-key spend |
| xKiro | API Key | Daily free tokens |
| Synthetic | API Key | Five-hour quota, Weekly tokens, Hourly search, Plan |

</details>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ mod tests {
(ProviderId::Wayfinder, "http://localhost:8787"),
(ProviderId::Bifrost, "https://bifrost.example.com"),
(ProviderId::Aixy, "https://aixy.example.com/prefix"),
(ProviderId::ClawRouter, "https://router.example.com/v1"),
] {
settings.set_gateway_url(id, url);
let ctx = super::super::providers::build_fetch_context(
Expand Down Expand Up @@ -485,6 +486,7 @@ mod tests {
assert_eq!(gateway_provider("wayfinder"), Some(ProviderId::Wayfinder));
assert_eq!(gateway_provider("bifrost"), Some(ProviderId::Bifrost));
assert_eq!(gateway_provider("aixy"), Some(ProviderId::Aixy));
assert_eq!(gateway_provider("clawrouter"), Some(ProviderId::ClawRouter));
assert_eq!(gateway_provider("codex"), None);
}

Expand Down Expand Up @@ -548,6 +550,7 @@ fn gateway_provider(provider_id: &str) -> Option<codexbar::core::ProviderId> {
"wayfinder" => Some(codexbar::core::ProviderId::Wayfinder),
"bifrost" => Some(codexbar::core::ProviderId::Bifrost),
"aixy" => Some(codexbar::core::ProviderId::Aixy),
"clawrouter" => Some(codexbar::core::ProviderId::ClawRouter),
_ => None,
}
}
Expand Down Expand Up @@ -577,6 +580,10 @@ pub fn set_provider_gateway_url(provider_id: String, gateway_url: String) -> Res
codexbar::providers::aixy::validate_gateway_url(gateway_url)
.map_err(|error| error.to_string())?;
}
codexbar::core::ProviderId::ClawRouter => {
codexbar::providers::clawrouter::validate_gateway_url(gateway_url)
.map_err(|error| error.to_string())?;
}
_ => unreachable!("gateway_provider only returns gateway providers"),
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ describe("provider icon registry", () => {
"elevenlabs",
"manus",
"replicate",
"synthetic",
"typesafe",
"v0",
]);
Expand Down
6 changes: 6 additions & 0 deletions apps/desktop-tauri/src/components/providers/providerIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ import replicate from "./icons/ProviderIcon-replicate.svg?raw";
import sakana from "./icons/ProviderIcon-sakana.svg?raw";
import stepfun from "./icons/ProviderIcon-stepfun.svg?raw";
import sub2api from "./icons/ProviderIcon-sub2api.svg?raw";
import synthetic from "./icons/ProviderIcon-synthetic.svg?raw";
import clawrouter from "./icons/ProviderIcon-clawrouter.svg?raw";
import t3chat from "./icons/ProviderIcon-t3chat.svg?raw";
import venice from "./icons/ProviderIcon-venice.svg?raw";
import vercel from "./icons/ProviderIcon-vercel.svg?raw";
Expand Down Expand Up @@ -158,6 +160,8 @@ const RAW: Record<string, string> = {
sakana: tint(sakana),
stepfun: tint(stepfun),
sub2api: tint(sub2api),
synthetic: tint(synthetic),
clawrouter: tint(clawrouter),
t3chat: tint(t3chat),
venice: tint(venice),
vercel: tint(vercel),
Expand Down Expand Up @@ -251,6 +255,8 @@ export const PROVIDER_ICON_REGISTRY: Record<string, ProviderIcon> = {
sub2api: { id: "sub2api", brandColor: "#14b8a6", fallbackLetter: "S", svgPath: RAW.sub2api },
venice: { id: "venice", brandColor: "#3c8fdd", fallbackLetter: "V", svgPath: RAW.venice },
vercel: { id: "vercel", brandColor: "#737373", fallbackLetter: "V", svgPath: RAW.vercel },
synthetic: { id: "synthetic", brandColor: "#141414", fallbackLetter: "S", svgPath: RAW.synthetic },
clawrouter: { id: "clawrouter", brandColor: "#596EF6", fallbackLetter: "C", svgPath: RAW.clawrouter },
openaiapi: { id: "openaiapi", brandColor: "#0f826e", fallbackLetter: "O" },
chutes: { id: "chutes", brandColor: "#3184ff", fallbackLetter: "C" },
litellm: { id: "litellm", brandColor: "#4c89f0", fallbackLetter: "L" },
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src/i18n/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ export const ALL_LOCALE_KEYS = [
"AixyGatewayTitle",
"AixyGatewayLabel",
"AixyGatewayHelp",
"ClawRouterGatewayTitle",
"ClawRouterGatewayHelp",
"WayfinderModels",
"WayfinderRequests",
"WayfinderTokens",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe("WayfinderGatewaySection", () => {
["wayfinder", "WayfinderGatewayTitle", "WayfinderGatewayLabel", "WayfinderGatewayHelp"],
["bifrost", "BifrostGatewayTitle", "WayfinderGatewayLabel", "BifrostGatewayHelp"],
["aixy", "AixyGatewayTitle", "AixyGatewayLabel", "AixyGatewayHelp"],
["clawrouter", "ClawRouterGatewayTitle", "AixyGatewayLabel", "ClawRouterGatewayHelp"],
] as const)("uses localized %s copy", (providerId, title, label, help) => {
renderSection(providerId);

Expand All @@ -45,6 +46,7 @@ describe("WayfinderGatewaySection", () => {

it("recognizes only gateway providers", () => {
expect(isGatewayProviderId("aixy")).toBe(true);
expect(isGatewayProviderId("clawrouter")).toBe(true);
expect(isGatewayProviderId("bifrost")).toBe(true);
expect(isGatewayProviderId("wayfinder")).toBe(true);
expect(isGatewayProviderId("codex")).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { LocaleKey } from "../../../../i18n/keys";

export type GatewayProviderId = "wayfinder" | "bifrost" | "aixy";
export type GatewayProviderId = "wayfinder" | "bifrost" | "aixy" | "clawrouter";

interface GatewayCopy {
title: LocaleKey;
Expand All @@ -24,6 +24,11 @@ const GATEWAY_COPY: Record<GatewayProviderId, GatewayCopy> = {
label: "AixyGatewayLabel",
help: "AixyGatewayHelp",
},
clawrouter: {
title: "ClawRouterGatewayTitle",
label: "AixyGatewayLabel",
help: "ClawRouterGatewayHelp",
},
};

export function isGatewayProviderId(id: string): id is GatewayProviderId {
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src/test/providerCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,6 @@ export const TEST_PROVIDER_CATALOG: Array<[string, string]> = [
["devpass", "DevPass"],
["xkiro", "xKiro"],
["raycast", "Raycast"],
["synthetic", "Synthetic"],
["clawrouter", "ClawRouter"],
];
39 changes: 39 additions & 0 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,45 @@ are never followed and response bodies are never echoed in errors.
budget, inconsistent coverage, malformed amounts) is a parse error, not a
partial balance.

### Synthetic

Synthetic reads `GET https://api.synthetic.new/v2/quotas` with
`Authorization: Bearer <key>`. Configure the key in Settings → Providers →
Synthetic (or `SYNTHETIC_API_KEY`); the CLI also accepts `synthetic.new`.

- `rollingFiveHourLimit` is the "Five-hour quota" lane, `weeklyTokenLimit` the
"Weekly tokens" lane and `search.hourly` a "Search hourly" window. Other
payload shapes fall back to the first quota-like objects found under
`quotas`, `quota`, `limits`, `usage`, `entries`, `subscription` or `data`.
- When the five-hour quota reports `tickPercent`, the lane resets at the next
regen tick and its detail line reads, for example,
"76% after next regen · Full in ~3 regens". The weekly lane does the same
when it carries `maxCredits` and `nextRegenCredits`. Like the Mac card, no
weekly USD cost is shown (on the card or in CLI JSON).
- The plan name (`plan`, `planName`, `tier`, ...) is shown as the plan.
- 401 and 403 report "Invalid Synthetic API credentials."; response bodies are
never echoed in errors.

### ClawRouter

ClawRouter reports the monthly budget, spend and routed-provider usage of one
policy key (`GET {base}/v1/usage`, `Authorization: Bearer <key>`). Configure
the key in Settings → Providers → ClawRouter (or `CLAWROUTER_API_KEY`). Leave
the Base URL empty for `https://clawrouter.openclaw.ai`; set it (or
`CLAWROUTER_BASE_URL`) for another deployment. The service root and its `/v1`
URL both work, a bare host is treated as HTTPS, and plain HTTP, embedded
credentials, a query or a fragment are rejected before the key is read.
Redirects are never followed and response bodies are never echoed in errors.

- The primary lane is the monthly budget (spent / limit), resetting on the
first day of the next month (UTC). The cost line shows this month's spend
against the limit.
- Details show requests, tokens, actual cost, the budget ledger and the
monthly budget, then up to 20 routed providers ordered by spend and
requests.
- A policy without a monthly limit shows "Unmetered" and this month's actual
cost.

## Listing what is enabled

```powershell
Expand Down
Loading