feat: add CrofAI provider#412
Conversation
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src-tauri/src/plugin_engine/host_api.rs">
<violation number="1" location="src-tauri/src/plugin_engine/host_api.rs:18">
P1: Adding `CROFAI_API_KEY` to the global plugin env allowlist exposes a sensitive credential to any plugin via `host.env.get`, because env access is not scoped per plugin.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| "CODEX_HOME", | ||
| "CLAUDE_CONFIG_DIR", | ||
| "CLAUDE_CODE_OAUTH_TOKEN", | ||
| "CROFAI_API_KEY", |
There was a problem hiding this comment.
P1: Adding CROFAI_API_KEY to the global plugin env allowlist exposes a sensitive credential to any plugin via host.env.get, because env access is not scoped per plugin.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src-tauri/src/plugin_engine/host_api.rs, line 18:
<comment>Adding `CROFAI_API_KEY` to the global plugin env allowlist exposes a sensitive credential to any plugin via `host.env.get`, because env access is not scoped per plugin.</comment>
<file context>
@@ -11,10 +11,11 @@ use std::path::{Path, PathBuf};
"CODEX_HOME",
"CLAUDE_CONFIG_DIR",
"CLAUDE_CODE_OAUTH_TOKEN",
+ "CROFAI_API_KEY",
"USER_TYPE",
"USE_STAGING_OAUTH",
</file context>
robinebers
left a comment
There was a problem hiding this comment.
Hey! 👋 This is Rob's AI reviewer. Thanks for the contribution!
Nice clean provider plugin overall. The API call is scoped, tests are solid, docs are included, and the env var whitelist was updated. Three things to fix before merge:
-
Icon theming —
plugins/crofai/icon.svguses a fixed gradient fill. OpenUsage icons should usecurrentColorso they work in light/dark themes. -
Show zero credits — if CrofAI returns
credits: 0, the plugin hides the Credits line. A valid zero balance should show$0.00, not disappear. -
Fail clearly on unexpected data — if
creditsorusable_requestshas the wrong type, the plugin currently treats it like0. Better to show a clear “usage response invalid” error so bad API data does not look like real usage.
Everything else looks good: manifest shape, README/docs, tests, env whitelist, and request URL all match the repo’s patterns.
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/providers/crofai.md">
<violation number="1" location="docs/providers/crofai.md:55">
P3: Malformed Markdown table in Errors section: separator row declares 3 columns while header/data rows have 2.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
addressed the requested changes:
|
|
/usage_api endpoint now also returns requests_plan which is total number of requests offered per day in the active plan. Example response: So now we can show a progress bar instead of just text. |
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/crofai/plugin.js">
<violation number="1" location="plugins/crofai/plugin.js:59">
P2: `used` can go negative when `usable_requests` is greater than `requests_plan`, violating the documented progress-line contract.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
45c09d9 to
5d0696b
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new CrofAI provider plugin to the OpenUsage plugin ecosystem, enabling display of CrofAI credits and daily request usage via CrofAI’s Usage API.
Changes:
- Adds the
crofaiplugin (manifest, implementation, icon) and a comprehensive vitest suite. - Documents the provider and links it from the main README.
- Whitelists
CROFAI_API_KEYfor plugin environment access in the Tauri host.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src-tauri/src/plugin_engine/host_api.rs | Adds CROFAI_API_KEY to the environment variable allowlist for plugins. |
| plugins/crofai/plugin.js | Implements CrofAI usage probing, response validation, and line rendering. |
| plugins/crofai/plugin.json | Defines the CrofAI plugin manifest (lines, icon, links, branding). |
| plugins/crofai/plugin.test.js | Adds unit tests for auth handling, HTTP/error paths, parsing, and line output. |
| plugins/crofai/icon.svg | Adds the CrofAI provider icon. |
| docs/providers/crofai.md | Adds provider documentation for setup, API, output, and errors. |
| README.md | Adds CrofAI to the list of supported providers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var requestsPlan = data.requests_plan | ||
| if (typeof requestsPlan !== "number" || !Number.isFinite(requestsPlan) || requestsPlan <= 0) { | ||
| throw "Usage response invalid. Try again later." | ||
| } | ||
|
|
||
| var usableRequests = data.usable_requests | ||
| if (usableRequests !== null && usableRequests !== undefined) { | ||
| if (typeof usableRequests !== "number" || !Number.isFinite(usableRequests)) { | ||
| throw "Usage response invalid. Try again later." | ||
| } |
There was a problem hiding this comment.
requests_plan is validated unconditionally, so the probe will throw even when usable_requests is null/absent (i.e., when you intend to omit the Requests line). Consider only requiring/validating requests_plan inside the block that builds the Requests progress line, so Credits can still render when request fields are missing.
| var requestsPlan = data.requests_plan | |
| if (typeof requestsPlan !== "number" || !Number.isFinite(requestsPlan) || requestsPlan <= 0) { | |
| throw "Usage response invalid. Try again later." | |
| } | |
| var usableRequests = data.usable_requests | |
| if (usableRequests !== null && usableRequests !== undefined) { | |
| if (typeof usableRequests !== "number" || !Number.isFinite(usableRequests)) { | |
| throw "Usage response invalid. Try again later." | |
| } | |
| var usableRequests = data.usable_requests | |
| if (usableRequests !== null && usableRequests !== undefined) { | |
| if (typeof usableRequests !== "number" || !Number.isFinite(usableRequests)) { | |
| throw "Usage response invalid. Try again later." | |
| } | |
| var requestsPlan = data.requests_plan | |
| if (typeof requestsPlan !== "number" || !Number.isFinite(requestsPlan) || requestsPlan <= 0) { | |
| throw "Usage response invalid. Try again later." | |
| } |
| it("throws when credits is Infinity", async () => { | ||
| const ctx = makeCtx() | ||
| ctx.host.env.get.mockReturnValue("test-api-key") | ||
| ctx.host.http.request.mockReturnValue({ | ||
| status: 200, | ||
| bodyText: JSON.stringify({ credits: Infinity, usable_requests: 10, requests_plan: 500 }), | ||
| }) | ||
| const plugin = await loadPlugin() | ||
| expect(() => plugin.probe(ctx)).toThrow( | ||
| "Usage response invalid. Try again later." | ||
| ) |
There was a problem hiding this comment.
This test doesn't actually exercise credits: Infinity: JSON.stringify(Infinity) becomes null, so the plugin is really being tested against credits: null. Either adjust the test name/intent (e.g., credits is null) or use a non-JSON body (like the literal string "Infinity") to cover the invalid-response path you want.
| it("throws when credits is NaN", async () => { | ||
| const ctx = makeCtx() | ||
| ctx.host.env.get.mockReturnValue("test-api-key") | ||
| ctx.host.http.request.mockReturnValue({ | ||
| status: 200, | ||
| bodyText: JSON.stringify({ credits: NaN, usable_requests: 10, requests_plan: 500 }), | ||
| }) | ||
| const plugin = await loadPlugin() | ||
| expect(() => plugin.probe(ctx)).toThrow( | ||
| "Usage response invalid. Try again later." | ||
| ) | ||
| }) |
There was a problem hiding this comment.
Same issue as above: JSON.stringify(NaN) becomes null, so this test isn't validating a NaN value coming from the API. Rename the test to match what it covers, or change the mocked bodyText to a non-JSON value to verify the intended failure mode.
af9fd0e to
8c4f944
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Description
Adds CrofAI provider as plugin.
Type of Change
Testing
bun run buildand it succeededbun run testand all tests passbun tauri devChecklist
mainbranchSummary by cubic
Adds the
crofaiprovider to show CrofAI credits and daily request usage. Usesrequests_planto render a requests progress bar; hides lines when data is missing or invalid.New Features
GET https://crof.ai/usage_api/withCROFAI_API_KEY(Bearer).requests_plan(count); hidden whenusable_requestsis null/absent; clamps used to 0 if remaining exceeds plan.docs/providers/crofai.md; README updated;CROFAI_API_KEYwhitelisted; tests included.Migration
CROFAI_API_KEYin your environment and restart the app.Written for commit 5d0696b. Summary will update on new commits. Review in cubic