Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ OpenUsage lives in your menu bar and shows you how much of your AI coding subscr
- [**Claude**](docs/providers/claude.md) / session, weekly, peak/off-peak, extra usage, local token usage (ccusage)
- [**Codex**](docs/providers/codex.md) / session, weekly, reviews, credits
- [**Copilot**](docs/providers/copilot.md) / premium, chat, completions
- [**CrofAI**](docs/providers/crofai.md) / credits, daily requests
- [**Cursor**](docs/providers/cursor.md) / credits, total usage, auto usage, API usage, on-demand, CLI auth
- [**Factory / Droid**](docs/providers/factory.md) / standard, premium tokens
- [**Gemini**](docs/providers/gemini.md) / pro, flash, workspace/free/paid tier
Expand Down
60 changes: 60 additions & 0 deletions docs/providers/crofai.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# CrofAI

> Uses the CrofAI Usage API with a user-provided API key.

## Overview

- **Protocol:** HTTPS (JSON)
- **Endpoint:** `GET https://crof.ai/usage_api/`
- **Auth:** `Authorization: Bearer <api_key>`
- **Data:** credit balance, daily requests remaining

## Authentication

Set the `CROFAI_API_KEY` environment variable:

```bash
export CROFAI_API_KEY="your-api-key-here"
```

The key is read from the environment at probe time. Restart OpenUsage after setting it.

## Data Source

Request:

```http
GET /usage_api/ HTTP/1.1
Host: crof.ai
Authorization: Bearer <api_key>
Accept: application/json
```

Response:

```json
{
"credits": 12.3456,
"usable_requests": 450
}
```

| Field | Type | Description |
|---|---|---|
| `credits` | number | Available credit balance (USD) |
| `usable_requests` | number \| null | Requests remaining today (`null` if not on a subscription plan) |

## Output

- **Requests** (overview text line): requests remaining today (e.g., `450 remaining`); hidden when `usable_requests` is null
- **Credits** (overview text line): formatted dollar balance (e.g., `$12.35`)

## Errors

| Condition | Message |
|---|---|---|
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
| Missing `CROFAI_API_KEY` env var | `No CROFAI_API_KEY found. Set up environment variable first.` |
| HTTP 401/403 | `API key invalid. Check your CrofAI API key.` |
| Non-2xx | `Usage request failed (HTTP {status}). Try again later.` |
| Network failure | `Usage request failed. Check your connection.` |
| Unparseable or invalid response shape/type | `Usage response invalid. Try again later.` |
16 changes: 16 additions & 0 deletions plugins/crofai/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions plugins/crofai/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
(function () {
var API_URL = "https://crof.ai/usage_api/"

function probe(ctx) {
var apiKey = ctx.host.env.get("CROFAI_API_KEY")
if (!apiKey || !String(apiKey).trim()) {
throw "No CROFAI_API_KEY found. Set up environment variable first."
}

var resp
try {
resp = ctx.util.request({
method: "GET",
url: API_URL,
headers: {
Authorization: "Bearer " + String(apiKey).trim(),
Accept: "application/json",
},
timeoutMs: 10000,
})
} catch (e) {
throw "Usage request failed. Check your connection."
}

if (ctx.util.isAuthStatus(resp.status)) {
throw "API key invalid. Check your CrofAI API key."
}

if (resp.status < 200 || resp.status >= 300) {
throw "Usage request failed (HTTP " + String(resp.status) + "). Try again later."
}

var data = ctx.util.tryParseJson(resp.bodyText)
if (!data || typeof data !== "object" || Array.isArray(data)) {
throw "Usage response invalid. Try again later."
}

var hasCredits = typeof data.credits === "number" && Number.isFinite(data.credits)
if ("credits" in data && !hasCredits) {
throw "Usage response invalid. Try again later."
}
var credits = hasCredits ? data.credits : 0

var lines = []

var usableRequests = data.usable_requests
if (usableRequests !== null && usableRequests !== undefined) {
if (typeof usableRequests !== "number" || !Number.isFinite(usableRequests)) {
throw "Usage response invalid. Try again later."
}
lines.push(
ctx.line.text({
label: "Requests",
value: String(usableRequests) + " remaining",
})
)
}

if (hasCredits && credits >= 0) {
lines.push(
ctx.line.text({
label: "Credits",
value: "$" + credits.toFixed(2),
})
)
}

return { lines: lines }
}

globalThis.__openusage_plugin = { id: "crofai", probe }
})()
16 changes: 16 additions & 0 deletions plugins/crofai/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"id": "crofai",
"name": "CrofAI",
"version": "0.0.1",
"entry": "plugin.js",
"icon": "icon.svg",
"brandColor": "#000000",
"links": [
{ "label": "Status", "url": "https://status.nahcrof.com" }
],
"lines": [
{ "type": "text", "label": "Requests", "scope": "overview" },
{ "type": "text", "label": "Credits", "scope": "overview" }
]
}
Loading
Loading