Replace Windsurf provider with Devin#551
Conversation
📝 WalkthroughWalkthroughAdds a Devin provider plugin (auth discovery, cloud probe, quota formatting), updates plugin manifest and tests, migrates stored Windsurf settings to Devin and integrates it into bootstrap, updates README/provider docs, and extends Rust redaction to hide Devin session tokens and extra JSON fields. ChangesDevin Provider Implementation and Migration
Documentation and Infrastructure Support
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
d33c00f to
592e33b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
plugins/devin/plugin.js (1)
145-152: ⚡ Quick winUse explicit result objects for expected quota/login outcomes.
QUOTA_HINTis part of the normal probe flow here, but it is propagated via thrown strings and caught by equality checks. That makes the control-flow contract brittle inside the plugin. Return an explicit status frombuildOutput()/tryAuth()and only throw once at theprobe()boundary.As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Use explicit result types for expected issues instead of throw/try/catch; fail LOUD with throw/console.error + toast.error for unexpected issues.Also applies to: 218-248
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/devin/plugin.js` around lines 145 - 152, The plugin currently uses a thrown string (QUOTA_HINT) to signal normal quota/login flow which is brittle; update tryAuth() and buildOutput() to return explicit result objects (e.g., { status: 'ok', userStatus }, { status: 'quota' } or { status: 'error', error }) instead of throwing QUOTA_HINT, and modify probe() to switch on those result.status values and only throw for truly unexpected errors; remove the catch(e === QUOTA_HINT) equality check and any thrown QUOTA_HINT occurrences, and ensure unexpected exceptions are still rethrown/logged (use ctx.host.log.error) in probe(); apply the same refactor for the similar logic around lines 218-248.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/devin/plugin.js`:
- Around line 238-243: The current dedupe logic only compares API keys
(appAuth.apiKey vs credentials.apiKey) and skips the app-auth fallback even when
the effective server URL differs; update the conditional that sets sawApiKey to
also compare the effective server URL (e.g., appAuth.apiServerUrl or the
resolved server URL used by tryAuth) so we only suppress the retry when both
apiKey and server URL match, and ensure tryAuth(ctx, appAuth) is still invoked
when the token is the same but the server URL differs; additionally add a
regression test case that uses the same token with a different apiServerUrl to
verify the plugin retries the app auth fallback instead of skipping it.
---
Nitpick comments:
In `@plugins/devin/plugin.js`:
- Around line 145-152: The plugin currently uses a thrown string (QUOTA_HINT) to
signal normal quota/login flow which is brittle; update tryAuth() and
buildOutput() to return explicit result objects (e.g., { status: 'ok',
userStatus }, { status: 'quota' } or { status: 'error', error }) instead of
throwing QUOTA_HINT, and modify probe() to switch on those result.status values
and only throw for truly unexpected errors; remove the catch(e === QUOTA_HINT)
equality check and any thrown QUOTA_HINT occurrences, and ensure unexpected
exceptions are still rethrown/logged (use ctx.host.log.error) in probe(); apply
the same refactor for the similar logic around lines 218-248.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1f6f666f-8c4a-48c9-a72c-631a3808a5ca
⛔ Files ignored due to path filters (2)
plugins/devin/icon.svgis excluded by!**/*.svgplugins/windsurf/icon.svgis excluded by!**/*.svg
📒 Files selected for processing (14)
README.mddocs/providers/antigravity.mddocs/providers/devin.mddocs/providers/windsurf.mdplugins/devin/plugin.jsplugins/devin/plugin.jsonplugins/devin/plugin.test.jsplugins/windsurf/plugin.jsplugins/windsurf/plugin.test.jssrc-tauri/src/plugin_engine/host_api.rssrc/hooks/app/use-settings-bootstrap.test.tssrc/hooks/app/use-settings-bootstrap.tssrc/lib/settings.test.tssrc/lib/settings.ts
💤 Files with no reviewable changes (3)
- docs/providers/windsurf.md
- plugins/windsurf/plugin.js
- plugins/windsurf/plugin.test.js
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
plugins/devin/plugin.js (1)
286-292:⚠️ Potential issue | 🟠 MajorApp-auth fallback is still skipped when only the server URL differs.
appAuth.apiServerUrlis alwaysnull(Line 139) and resolves to the default server, whilecredentialsmay carry a customapi_server_url. The dedupe at Line 287 compares onlyapiKey, so a same-token/different-server case never retries against the default server. Compare the effective server URL too and add a regression test.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/devin/plugin.js` around lines 286 - 292, The dedupe logic only compares apiKey (in the block using loadAppAuth and tryAuth) so when a token is the same but the server differs the app-auth fallback is skipped; modify the condition that sets sawApiKey to also compare the effective server URLs (normalize appAuth.apiServerUrl and credentials.api_server_url to the same default when null/undefined) before skipping retry, i.e. change the check around appAuth.apiKey !== credentials.apiKey to also check that the resolved server URLs match, update tryAuth/appAttempt behavior if needed, and add a regression test covering same-apiKey/different-server to ensure the fallback runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/devin/plugin.js`:
- Around line 158-167: Add a regression test that posts Devin’s exact request
payload shape and asserts redaction of the API key: craft a test that
sends/serializes {"metadata":{"apiKey":"SOME_SECRET"}} (matching the bodyText
construction in plugins/devin/plugin.js) through the same redaction path
exercised by host_api.rs (the sensitive_keys logic and the devin-session-token$
pattern) and verify the resulting string has the apiKey value redacted; update
or add the test near existing redaction tests to cover the nested
metadata.apiKey case so future changes to host_api.rs will be caught.
- Around line 5-6: Replace the hardcoded POSIX/macOS paths in
plugins/devin/plugin.js (CREDENTIALS_PATH, STATE_DB) with platform-aware
discovery: call the host API (expand_path or appDataDir/xdg dirs provided by the
host) to compute credentials and state DB locations, fall back to XDG on Linux
and the macOS Library path when process.platform === "darwin", and normalize the
expanded path; update any code that references CREDENTIALS_PATH or STATE_DB to
use the new resolver function; also extend plugins/devin/plugin.test.js to
assert behavior for non-darwin platforms by mocking the host-provided
appDataDir/expand_path values and verifying the resolved paths.
---
Duplicate comments:
In `@plugins/devin/plugin.js`:
- Around line 286-292: The dedupe logic only compares apiKey (in the block using
loadAppAuth and tryAuth) so when a token is the same but the server differs the
app-auth fallback is skipped; modify the condition that sets sawApiKey to also
compare the effective server URLs (normalize appAuth.apiServerUrl and
credentials.api_server_url to the same default when null/undefined) before
skipping retry, i.e. change the check around appAuth.apiKey !==
credentials.apiKey to also check that the resolved server URLs match, update
tryAuth/appAttempt behavior if needed, and add a regression test covering
same-apiKey/different-server to ensure the fallback runs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 57098843-b3d1-4048-8be5-bb8c1180eb0d
⛔ Files ignored due to path filters (2)
plugins/devin/icon.svgis excluded by!**/*.svgplugins/windsurf/icon.svgis excluded by!**/*.svg
📒 Files selected for processing (14)
README.mddocs/providers/antigravity.mddocs/providers/devin.mddocs/providers/windsurf.mdplugins/devin/plugin.jsplugins/devin/plugin.jsonplugins/devin/plugin.test.jsplugins/windsurf/plugin.jsplugins/windsurf/plugin.test.jssrc-tauri/src/plugin_engine/host_api.rssrc/hooks/app/use-settings-bootstrap.test.tssrc/hooks/app/use-settings-bootstrap.tssrc/lib/settings.test.tssrc/lib/settings.ts
💤 Files with no reviewable changes (3)
- plugins/windsurf/plugin.test.js
- docs/providers/windsurf.md
- plugins/windsurf/plugin.js
✅ Files skipped from review due to trivial changes (2)
- README.md
- docs/providers/antigravity.md
🚧 Files skipped from review as they are similar to previous changes (6)
- src/hooks/app/use-settings-bootstrap.ts
- src/lib/settings.ts
- src/hooks/app/use-settings-bootstrap.test.ts
- plugins/devin/plugin.json
- docs/providers/devin.md
- plugins/devin/plugin.test.js
| var CREDENTIALS_PATH = "~/.local/share/devin/credentials.toml" | ||
| var STATE_DB = "~/Library/Application Support/Devin/User/globalStorage/state.vscdb" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how the removed Windsurf plugin (or other plugins) resolved per-OS paths
rg -n -C3 'Application Support|\.local/share|process\.platform|os\.platform|darwin|win32' --glob 'plugins/**/*.js'Repository: robinebers/openusage
Length of output: 10125
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect Devin plugin implementation around the constants
echo "==== plugins/devin/plugin.js (lines ~1-120) ===="
nl -ba plugins/devin/plugin.js | sed -n '1,120p'
echo "==== plugins/devin/plugin.js (search usage) ===="
rg -n 'CREDENTIALS_PATH|STATE_DB' plugins/devin/plugin.js
echo "==== plugins/devin/plugin.test.js (if present) ===="
if [ -f plugins/devin/plugin.test.js ]; then
nl -ba plugins/devin/plugin.test.js | sed -n '1,200p'
rg -n 'CREDENTIALS_PATH|STATE_DB' plugins/devin/plugin.test.js || true
else
echo "plugins/devin/plugin.test.js not found"
fi
# 2) Find any shared helpers that resolve ~ or platform dirs
echo "==== Search for tilde/home or per-platform path helpers ===="
rg -n 'resolve.*~|homedir|home dir|os\.hom|expandHome|expandTilde|appDataDir|platform.*dir|darwin|macos|linux|win32' plugins/test-helpers.js plugins -S || true
# 3) Search host API / plugin engine for path normalization (best-effort)
echo "==== Search in repo for host-side path resolution ===="
rg -n 'CREDENTIALS_PATH|STATE_DB|globalStorage|state\.vscdb|Application Support|\.local/share|expand.*tilde|homedir|appDataDir' . -S || trueRepository: robinebers/openusage
Length of output: 174
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== plugins/devin/plugin.js (lines ~1-120) ===="
awk '{printf "%6d %s\n", NR, $0}' plugins/devin/plugin.js | sed -n '1,160p'
echo "==== plugins/devin/plugin.js (search usage) ===="
rg -n 'CREDENTIALS_PATH|STATE_DB' plugins/devin/plugin.js || true
echo "==== plugins/devin/plugin.test.js (if present) ===="
if [ -f plugins/devin/plugin.test.js ]; then
awk '{printf "%6d %s\n", NR, $0}' plugins/devin/plugin.test.js | sed -n '1,200p'
rg -n 'CREDENTIALS_PATH|STATE_DB' plugins/devin/plugin.test.js || true
else
echo "plugins/devin/plugin.test.js not found"
fi
echo "==== Search for tilde/home or per-platform path helpers ===="
rg -n 'resolve.*~|homedir|home dir|os\.hom|expandHome|expandTilde|appDataDir|platform.*dir|darwin|macos|linux|win32' plugins/test-helpers.js plugins || true
echo "==== Search in repo for host-side path resolution (best-effort) ===="
rg -n 'expand.*tilde|expandHome|homedir|appDataDir|globalStorage|state\.vscdb|Application Support|\.local/share' . -S || trueRepository: robinebers/openusage
Length of output: 23417
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src-tauri/src/plugin_engine/host_api.rs"
echo "==== host_api.rs: show expand_path_expands_tilde_prefix region ===="
awk 'NR>=3260 && NR<=3360 {printf "%6d %s\n", NR, $0}' "$FILE"
echo "==== host_api.rs: search for path expansion usage in fs/sqlite ===="
rg -n 'expand_path|expand.*path|tilde|homedir|~' "$FILE"
echo "==== host_api.rs: show path expansion call sites (around matches) ===="
# show a few lines around each match location
rg -n 'expand_path|expand.*path|tilde|homedir|~' "$FILE" | head -n 30 | while IFS=: read -r ln _; do
start=$((ln-10)); end=$((ln+10))
echo "---- $FILE:$ln ----"
awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%6d %s\n", NR, $0}' "$FILE"
doneRepository: robinebers/openusage
Length of output: 17223
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== plugins/devin/plugin.js: search for platform branching ===="
rg -n 'ctx\.app\.platform|platform|darwin|macos|linux|win32' plugins/devin/plugin.js || true
echo "==== docs/providers/devin.md ===="
if [ -f docs/providers/devin.md ]; then
awk 'NR<=220{print}' docs/providers/devin.md
else
echo "docs/providers/devin.md not found"
fi
echo "==== plugins/test-helpers.js: makeCtx platform/appDataDir info ===="
if [ -f plugins/test-helpers.js ]; then
awk 'NR<=120{print}' plugins/test-helpers.js
fiRepository: robinebers/openusage
Length of output: 8406
Fix cross-platform path handling in Devin plugin (credentials vs app state DB).
plugins/devin/plugin.js hardcodes CLI credentials at ~/.local/share/devin/credentials.toml and app SQLite auth at ~/Library/Application Support/Devin/User/globalStorage/state.vscdb with no platform branching; the host path expander only expands ~/... and does not map XDG↔macOS layouts (expand_path in src-tauri/src/plugin_engine/host_api.rs). This will break auth on platforms where one of those locations doesn’t exist. Add platform-specific paths (or discovery based on host-provided dirs like appDataDir) and extend plugins/devin/plugin.test.js to cover non-darwin platforms.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/devin/plugin.js` around lines 5 - 6, Replace the hardcoded
POSIX/macOS paths in plugins/devin/plugin.js (CREDENTIALS_PATH, STATE_DB) with
platform-aware discovery: call the host API (expand_path or appDataDir/xdg dirs
provided by the host) to compute credentials and state DB locations, fall back
to XDG on Linux and the macOS Library path when process.platform === "darwin",
and normalize the expanded path; update any code that references
CREDENTIALS_PATH or STATE_DB to use the new resolver function; also extend
plugins/devin/plugin.test.js to assert behavior for non-darwin platforms by
mocking the host-provided appDataDir/expand_path values and verifying the
resolved paths.
| bodyText: JSON.stringify({ | ||
| metadata: { | ||
| apiKey: auth.apiKey, | ||
| ideName: "devin", | ||
| ideVersion: CLOUD_COMPAT_VERSION, | ||
| extensionName: "devin", | ||
| extensionVersion: CLOUD_COMPAT_VERSION, | ||
| locale: "en", | ||
| }, | ||
| }), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect redaction rules in host_api.rs for apiKey / token coverage
fd -t f 'host_api.rs' --exec rg -n -C3 -i 'apikey|api_key|redact|session-token|sensitive'Repository: robinebers/openusage
Length of output: 27405
🏁 Script executed:
#!/bin/bash
# Search for explicit test coverage for JSON field name "apiKey" in host redaction tests
fd -t f 'host_api.rs' --exec rg -n '"apiKey"|devin-session-token\$|redact_body_redacts_'Repository: robinebers/openusage
Length of output: 1080
Audit metadata.apiKey redaction for the Devin cloud request (plugins/devin/plugin.js @ 158-167)
src-tauri/src/plugin_engine/host_api.rs already redacts JSON string values for the "apiKey" key (via sensitive_keys), and it also redacts devin-session-token$... patterns. Add a regression test that exercises Devin’s exact payload shape ({"metadata":{"apiKey":"..."} }) to ensure "apiKey" is redacted when nested under metadata—current tests cover generic "key"/API-key patterns and devin-session-token$..., but not the specific "apiKey" JSON field name used here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/devin/plugin.js` around lines 158 - 167, Add a regression test that
posts Devin’s exact request payload shape and asserts redaction of the API key:
craft a test that sends/serializes {"metadata":{"apiKey":"SOME_SECRET"}}
(matching the bodyText construction in plugins/devin/plugin.js) through the same
redaction path exercised by host_api.rs (the sensitive_keys logic and the
devin-session-token$ pattern) and verify the resulting string has the apiKey
value redacted; update or add the test near existing redaction tests to cover
the nested metadata.apiKey case so future changes to host_api.rs will be caught.
There was a problem hiding this comment.
2 issues found across 16 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
592e33b to
321292e
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 321292e. Configure here.
321292e to
4848fce
Compare

Summary
/usageoutput.Verification
bun run test -- plugins/devin/plugin.test.js src/lib/settings.test.ts src/hooks/app/use-settings-bootstrap.test.tscargo test -qbun run buildWeekly quota usagewithused: 100, reset2026-06-07T08:00:00.000Z, and extra usage balance fromoverageBalanceMicros.Visuals
Before Windsurf icon:
/tmp/openusage-pr-visuals/windsurf-before.svg.pngAfter Devin icon:
/tmp/openusage-pr-visuals/devin-after.svg.pngNote
Medium Risk
Touches local session tokens and an external quota API with non-obvious field semantics; settings migration could surprise users who had Windsurf enabled/disabled, though behavior is covered by tests.
Overview
Windsurf is removed and replaced by a Devin provider: local auth from Devin CLI
credentials.tomland/or app SQLite (windsurfAuthStatus), then HTTPS-onlyGetUserStatuson Codeium’s seat-management service (no localhost language-server probing).The Devin plugin surfaces weekly quota usage, optional daily quota, and extra usage balance, including the case where Devin hides daily quota but still sends
dailyQuotaRemainingPercent—that value is shown as weekly usage % (aligned with Devin’s UI, not “% remaining”). Docs/README list Devin instead of Windsurf; Antigravity’s doc wording is softened.One-time settings migration rewrites saved plugin order/disabled state from
windsurf→devinon bootstrap (with tests). Log/body redaction now masksdevin-session-token$…plus org/account display fields.Reviewed by Cursor Bugbot for commit 4848fce. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Replaced the Windsurf provider with a new
devinprovider that uses local Devin CLI/app auth and Codeium SeatManagement over HTTPS. Adds weekly quota usage and extra usage, with a one‑time settings migration and stronger token/org redaction.New Features
devinplugin callingSeatManagementService/GetUserStatusviaapi_server_url(defaults tohttps://server.codeium.com); HTTPS only and no localhost probing; ignores plaintexthttpURLs.~/.local/share/devin/credentials.tomlor Devin app SQLite; shows “Weekly quota usage” (mapsdailyQuotaRemainingPercentwhen weekly percent is absent and daily quota is hidden), optional daily quota, and “Extra usage balance”.plugins/devin/plugin.jsonmakes weekly usage primary; expanded Tauri host redaction fordevin-session-token$…,orgId/org_id, andaccountDisplayName/account_display_name.Migration
windsurf→devin, preserving order and disabled state; runs during bootstrap and saves on first launch.Written for commit 4848fce. Summary will update on new commits.
Summary by CodeRabbit
New Features
Documentation
Tests
Chores