Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eb07834
Add X API provider (xapi) - cookie-based prepaid credits
Finesssee Oct 12, 2026
80a75d2
Fix code formatting
Finesssee Oct 12, 2026
a7815cb
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Finesssee Oct 12, 2026
5faa1d3
Add X API provider wiring and tests
Finesssee Oct 12, 2026
a452ab5
Add X API provider to documentation, settings, and tests
Finesssee Oct 12, 2026
d63e056
Fix test_provider_id_all: update expected count from 96 to 97 for X API
Finesssee Oct 12, 2026
80becb9
Add X API mock fixtures for real-window testing
Finesssee Oct 12, 2026
f2c321e
Move X API fixtures to proof report directory
Finesssee Oct 12, 2026
d254355
Add X API cookie-source tests to session_cookie_scope and settings
Finesssee Oct 12, 2026
b279a51
Add Xapi to cookie_source defaults and fix test
Finesssee Oct 12, 2026
81c4ed0
Add X API to cookie_source_options_for function
Finesssee Oct 12, 2026
7c1a196
Add xapi provider assert to test
Finesssee Oct 12, 2026
13c35aa
Remove xapi usage-source policy and unused locale keys
Finesssee Oct 12, 2026
3527e33
Fix xapi balance display - remove duplicate Balance label
Finesssee Oct 12, 2026
dbd04bf
fmt: xapi formatting
Finesssee Oct 12, 2026
4ecb0e2
Fix X API display structure and parsing validation
Oct 12, 2026
77e7325
Match upstream X API amounts, account IDs and card layout
Oct 12, 2026
1e84c29
Skip card-hidden windows in the provider settings pane
Oct 12, 2026
d7c858a
Format X API account ids without a lossy cast
Oct 12, 2026
3f834e0
Hide the X API cost block on the card like upstream
Oct 12, 2026
9e21529
Add X API fetch fixtures and qualify Automatic mode
Oct 13, 2026
2d29e6a
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Oct 13, 2026
2b674b0
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Oct 13, 2026
8ce6168
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Oct 13, 2026
a640930
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Oct 13, 2026
67421b6
Merge remote-tracking branch 'origin/main' into feat/provider-xapi
Oct 13, 2026
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ See the full history in [CHANGELOG.md](CHANGELOG.md).
| LithosAI | Browser cookies / manual Cookie header | Prepaid balance, UTC spend today and this month |
| Muse (muse.ai) | Browser cookies / manual Cookie header | Weekly usage, tokens left, top-up tokens |
| WorkBuddy | Browser cookies / manual Cookie header | Monthly credits left, plan, cycle reset |
| X API | Browser cookies / manual Cookie header | Prepaid balance, purchased and free credits |

</details>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ fn cookie_source_provider(provider_id: &str) -> Option<codexbar::core::ProviderI
"lithosai" => ProviderId::LithosAI,
"langdock" => ProviderId::Langdock,
"tinyapi" => ProviderId::TinyApi,
"xapi" => ProviderId::Xapi,
_ => return None,
})
}
Expand Down Expand Up @@ -900,6 +901,13 @@ pub fn cookie_source_options_for(provider_id: &str, lang: Language) -> Vec<Cooki
cookie_option(lang, "manual", None),
cookie_option(lang, "off", None),
],
// X API cookie source: automatic browser import,
// a pasted header, or off.
"xapi" => vec![
cookie_option(lang, "auto", None),
cookie_option(lang, "manual", None),
cookie_option(lang, "off", None),
],
_ => Vec::new(),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,44 @@ fn tinyapi_exposes_a_cookie_source_picker_and_routes_each_choice() {
assert_eq!(ctx.source_mode, SourceMode::Cli);
assert!(ctx.manual_cookie_header.is_none());
}

/// X API is web-only: the picker chooses the session, a stored header
/// always wins, an empty manual source fails closed without reading a browser,
/// and off reaches the provider as Cli (its "cookies are disabled" error).
#[test]
fn xapi_exposes_a_cookie_source_picker_and_routes_each_choice() {
let settings = Settings::default();
assert_eq!(
super::provider_cookie_source_lookup(&settings, "xapi").as_deref(),
Some("auto")
);
let values: Vec<_> = super::cookie_source_options_for("xapi", Language::English)
.into_iter()
.map(|option| option.value)
.collect();
assert_eq!(values, vec!["auto", "manual", "off"]);

let cookie = "auth_token=fixture; ct0=fixture";
for source in ["auto", "manual"] {
let ctx = session_context(ProviderId::Xapi, Some(source), "auto", Some(cookie));
assert!(
matches!(ctx.source_mode, SourceMode::Auto | SourceMode::Web),
"{source}"
);
assert_eq!(
ctx.manual_cookie_header.as_deref(),
Some(cookie),
"{source}"
);
assert!(!ctx.manual_cookie_missing, "{source}");
}

let ctx = session_context(ProviderId::Xapi, Some("manual"), "auto", None);
assert_eq!(ctx.source_mode, SourceMode::Web);
assert!(ctx.manual_cookie_header.is_none());
assert!(ctx.manual_cookie_missing);

let ctx = session_context(ProviderId::Xapi, Some("off"), "auto", Some(cookie));
assert_eq!(ctx.source_mode, SourceMode::Cli);
assert!(ctx.manual_cookie_header.is_none());
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/desktop-tauri/src/components/providers/providerIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import notion from "./icons/ProviderIcon-notion.svg?raw";
import nous from "./icons/ProviderIcon-nous.svg?raw";
import workbuddy from "./icons/ProviderIcon-workbuddy.svg?raw";
import xai from "./icons/ProviderIcon-xai.svg?raw";
import xapi from "./icons/ProviderIcon-xapi.svg?raw";
import xkiro from "./icons/ProviderIcon-xkiro.svg?raw";
import ollama from "./icons/ProviderIcon-ollama.svg?raw";
import opencode from "./icons/ProviderIcon-opencode.svg?raw";
Expand Down Expand Up @@ -159,6 +160,7 @@ const RAW: Record<string, string> = {
notion: tint(notion),
nous: tint(nous),
xai: tint(xai),
xapi: tint(xapi),
xkiro: tint(xkiro),
mistral: tint(mistral),
muse: tint(muse),
Expand Down Expand Up @@ -294,6 +296,7 @@ export const PROVIDER_ICON_REGISTRY: Record<string, ProviderIcon> = {
notion: { id: "notion", brandColor: "#337EA9", fallbackLetter: "N", svgPath: RAW.notion },
nous: { id: "nous", brandColor: "#D6A55C", fallbackLetter: "N", svgPath: RAW.nous },
xai: { id: "xai", brandColor: "#8e8e93", fallbackLetter: "X", svgPath: RAW.xai },
xapi: { id: "xapi", brandColor: "#71767B", fallbackLetter: "X", svgPath: RAW.xapi },
xkiro: { id: "xkiro", brandColor: "#52c99b", fallbackLetter: "X", svgPath: RAW.xkiro },
meta: { id: "meta", brandColor: "#0467DF", fallbackLetter: "M", svgPath: RAW.meta },
};
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/test/providerCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ export const TEST_PROVIDER_CATALOG: Array<[string, string]> = [
["lithosai", "LithosAI"],
["museai", "Muse (muse.ai)"],
["workbuddy", "WorkBuddy"],
["xapi", "X API"],
];
26 changes: 26 additions & 0 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,32 @@ portal in Settings → Providers → IBM Bob, as token accounts, or with
- 401 and 403 report that the key was rejected; response bodies are never
echoed in errors.

### X API prepaid credits

X API is disabled by default and reads prepaid credit balances from the X
(Twitter) API console. Sign in to console.x.com in your browser, and CodexBar
will read the `auth_token` and `ct0` cookies from that session.

- **Automatic:** A previously imported Cookie header is used on its own.
Otherwise CodexBar tries each detected browser (Chrome, Edge, Brave,
Firefox) holding both cookies. If a browser session expires (401), it moves
to the next browser. No session means sign in to console.x.com.
- **Manual:** Paste the Cookie header from a console.x.com request (with
`auth_token` and `ct0`). Manual mode does not fall back to browser sessions.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **Off:** Cookies are disabled.

The primary lane shows the balance (paid + free credits). Details rows show:
- Balance (total: paid + free)
- Purchased credits
- Free credits

Balance is a prepaid meter (no quota percentage); costs are always zero (no
usage tracking available). The provider reports identity as "Browser session".
Cost resets are never inferred. Session rotation on 401 is implemented: sessions
are tried in browser order, stale sessions are skipped, and a missing session
after all attempts triggers "sign in to console.x.com". Errors never echo
response bodies.

## Listing what is enabled

```powershell
Expand Down
10 changes: 9 additions & 1 deletion rust/src/core/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub enum ProviderId {
QwenCloud,
Notion,
Xai,
Xapi,
Fireworks,
AtlasCloud,
#[serde(alias = "metaspark")]
Expand Down Expand Up @@ -196,6 +197,7 @@ impl ProviderId {
ProviderId::QwenCloud,
ProviderId::Notion,
ProviderId::Xai,
ProviderId::Xapi,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ProviderId::Fireworks,
ProviderId::AtlasCloud,
ProviderId::Meta,
Expand Down Expand Up @@ -324,6 +326,7 @@ impl ProviderId {
ProviderId::QwenCloud => "qwen-cloud",
ProviderId::Notion => "notion",
ProviderId::Xai => "xai",
ProviderId::Xapi => "xapi",
ProviderId::Replicate => "replicate",
ProviderId::AtlasCloud => "atlascloud",
}
Expand Down Expand Up @@ -432,6 +435,7 @@ impl ProviderId {
ProviderId::QwenCloud => "Qwen Cloud",
ProviderId::Notion => "Notion AI",
ProviderId::Xai => "xAI",
ProviderId::Xapi => "X API",
ProviderId::Replicate => "Replicate",
ProviderId::AtlasCloud => "Atlas Cloud",
}
Expand Down Expand Up @@ -546,6 +550,7 @@ impl ProviderId {
ProviderId::ZoomMate => Some("zoommate.zoom.us"),
ProviderId::QwenCloud => Some("qwencloud.com"),
ProviderId::Notion => Some("app.notion.com"),
ProviderId::Xapi => Some("x.com"),
ProviderId::Xai => None,
}
}
Expand Down Expand Up @@ -641,6 +646,7 @@ impl ProviderId {
}
"grok" | "supergrok" | "super-grok" => Some(ProviderId::Grok),
"xai" | "x.ai" | "x-ai" => Some(ProviderId::Xai),
"xapi" | "x-api" | "x api" => Some(ProviderId::Xapi),
"elevenlabs" | "eleven-labs" | "11labs" => Some(ProviderId::ElevenLabs),
"deepgram" | "dg" => Some(ProviderId::Deepgram),
"groq" | "groqcloud" | "groq-cloud" | "groq cloud" => Some(ProviderId::Groq),
Expand Down Expand Up @@ -1382,6 +1388,7 @@ pub fn brand_color(id: ProviderId) -> &'static str {
ProviderId::QwenCloud => "#615CED",
ProviderId::Notion => "#337EA9",
ProviderId::Xai => "#8E8E93",
ProviderId::Xapi => "#71767B",
ProviderId::Fireworks => "#F25B1C",
ProviderId::Meta => "#0467DF",
ProviderId::Muse => "#0668E1",
Expand Down Expand Up @@ -1423,7 +1430,7 @@ mod tests {
#[test]
fn test_provider_id_all() {
let all = ProviderId::all();
assert_eq!(all.len(), 100);
assert_eq!(all.len(), 101);
assert!(all.contains(&ProviderId::Claude));
assert!(all.contains(&ProviderId::Codex));
assert!(all.contains(&ProviderId::Pi));
Expand Down Expand Up @@ -1480,6 +1487,7 @@ mod tests {
assert!(all.contains(&ProviderId::QwenCloud));
assert!(all.contains(&ProviderId::Notion));
assert!(all.contains(&ProviderId::Xai));
assert!(all.contains(&ProviderId::Xapi));
assert!(all.contains(&ProviderId::Meta));
assert!(all.contains(&ProviderId::Replicate));
assert!(all.contains(&ProviderId::Muse));
Expand Down
6 changes: 4 additions & 2 deletions rust/src/core/provider_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use crate::providers::{
QwenCloudProvider, RaycastProvider, ReplicateProvider, SailResearchProvider, SakanaProvider,
SofyaProvider, StepFunProvider, Sub2ApiProvider, T3ChatProvider, TinyApiProvider,
TypeSafeProvider, V0Provider, VeniceProvider, VercelProvider, VertexAIProvider, WarpProvider,
WayfinderProvider, WindsurfProvider, XKiroProvider, XaiProvider, ZaiProvider, ZedProvider,
ZenMuxProvider, ZoomMateProvider,
WayfinderProvider, WindsurfProvider, XKiroProvider, XaiProvider, XapiProvider, ZaiProvider,
ZedProvider, ZenMuxProvider, ZoomMateProvider,
};

/// Instantiate the concrete [`Provider`] implementation for a given [`ProviderId`].
Expand Down Expand Up @@ -129,6 +129,7 @@ pub fn instantiate(id: ProviderId) -> Box<dyn Provider> {
ProviderId::SailResearch => Box::new(SailResearchProvider::new()),
ProviderId::Notion => Box::new(NotionProvider::new()),
ProviderId::Xai => Box::new(XaiProvider::new()),
ProviderId::Xapi => Box::new(XapiProvider::new()),
ProviderId::Fireworks => Box::new(FireworksProvider::new()),
ProviderId::Meta => Box::new(MetaProvider::new()),
ProviderId::Muse => Box::new(MuseProvider::new()),
Expand Down Expand Up @@ -239,6 +240,7 @@ pub fn pace_capability(id: ProviderId) -> ProviderPaceCapability {
| ProviderId::Replicate
| ProviderId::SailResearch
| ProviderId::Xai
| ProviderId::Xapi
| ProviderId::Fireworks
| ProviderId::Meta
| ProviderId::Muse
Expand Down
3 changes: 2 additions & 1 deletion rust/src/core/token_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@ impl TokenAccountSupport {
| ProviderId::WorkBuddy
| ProviderId::SailResearch
| ProviderId::Sofya
| ProviderId::TinyApi => None,
| ProviderId::TinyApi
| ProviderId::Xapi => None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions rust/src/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub mod wayfinder;
pub mod windsurf;
pub mod workbuddy;
pub mod xai;
pub mod xapi;
pub mod xkiro;
pub mod zai;
pub mod zed;
Expand Down Expand Up @@ -207,6 +208,7 @@ pub use wayfinder::WayfinderProvider;
pub use windsurf::WindsurfProvider;
pub use workbuddy::WorkBuddyProvider;
pub use xai::XaiProvider;
pub use xapi::XapiProvider;
pub use xkiro::XKiroProvider;
pub use zai::ZaiProvider;
pub use zed::ZedProvider;
Expand Down
Loading