Skip to content

Commit f11fa0c

Browse files
committed
Fix CapSolver key enablement migration
1 parent 764b7a2 commit f11fa0c

59 files changed

Lines changed: 381 additions & 247 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/chrome/src/agent/agent.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import * as trace from '../trace/recorder.js';
3131
import { tracesToMarkdown } from './trace-export.js';
3232
import { solveCaptcha, detectCaptcha, injectToken, captchaParamError, captchaTypesMatch, captchaWebsiteUrl } from './captcha-solver.js';
33+
import { isCapsolverEnabled, normalizeCapsolverApiKey } from './capsolver-config.js';
3334
import { captchaChallengeKey, detectChallengeDialog, detectChallengeDialogInPage } from './captcha-gate.js';
3435
import { applyCaptchaFrameVisibility } from './captcha-frame-runtime.js';
3536
import { getRecordingStateFresh as recorderStateFresh } from '../recorder/host.js';
@@ -245,8 +246,8 @@ export class Agent extends LoopDetector {
245246
this.userMemoryRecords = [];
246247
this.userMemoryMaxPromptChars = USER_MEMORY_DEFAULT_MAX_PROMPT_CHARS;
247248

248-
// CapSolver integration. Off by default. When enabled AND an API key
249-
// is set, the system prompt grows a "[CAPTCHA SOLVER]" note that
249+
// CapSolver integration. A valid saved API key enables the
250+
// "[CAPTCHA SOLVER]" system-prompt note, which
250251
// tells the model to try `solve_captcha` once before falling back to
251252
// asking the user. The agent reads the key from chrome.storage.local
252253
// at call time so rotating the key doesn't require a restart.
@@ -14658,9 +14659,8 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
1465814659
}
1465914660

1466014661
// ─── CAPTCHA solver ──────────────────────────────────────────────
14661-
// Only meaningfully wired when the user has enabled CapSolver in
14662-
// Settings. We re-check on every call so flipping the toggle or
14663-
// rotating the key takes effect without a restart.
14662+
// A valid saved key is the enable control. Re-check it on every call so
14663+
// rotating or clearing the key takes effect without a restart.
1466414664
if (name === 'solve_captcha') {
1466514665
let dispatched = false;
1466614666
const noDispatchFailure = (error) => ({
@@ -14670,13 +14670,10 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
1467014670
error,
1467114671
});
1467214672
try {
14673-
const stored = await chrome.storage.local.get(['captchaSolverEnabled', 'capsolverApiKey']);
14674-
if (!stored.captchaSolverEnabled) {
14675-
return noDispatchFailure('CapSolver is not enabled. Ask the user to enable it in Settings → General → Advanced, or fall back to asking them to solve the captcha manually.');
14676-
}
14677-
const apiKey = (stored.capsolverApiKey || '').trim();
14678-
if (!apiKey) {
14679-
return noDispatchFailure('CapSolver is enabled but no API key is configured. Ask the user to set one in Settings → General → Advanced, or fall back to asking them to solve the captcha manually.');
14673+
const stored = await chrome.storage.local.get(['capsolverApiKey', 'captchaSolverEnabled']);
14674+
const apiKey = normalizeCapsolverApiKey(stored.capsolverApiKey);
14675+
if (!isCapsolverEnabled(apiKey, stored.captchaSolverEnabled)) {
14676+
return noDispatchFailure('CapSolver is not enabled with a valid API key. Ask the user to save their key in Settings → General → Advanced, or fall back to asking them to solve the captcha manually.');
1468014677
}
1468114678

1468214679
// Use the top-level URL as a fallback. Frame-aware detection below
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// CapSolver account keys currently use the CAP- prefix shown in Settings.
2+
// Keep this check intentionally structural: account validity and balance are
3+
// verified by the CapSolver API, while this prevents empty or obviously
4+
// malformed values from enabling paid CAPTCHA solving.
5+
export const CAPSOLVER_API_KEY_PATTERN = /^CAP-[A-Za-z0-9_-]{20,}$/;
6+
7+
export function normalizeCapsolverApiKey(value) {
8+
return typeof value === 'string' ? value.trim() : '';
9+
}
10+
11+
export function isValidCapsolverApiKey(value) {
12+
return CAPSOLVER_API_KEY_PATTERN.test(normalizeCapsolverApiKey(value));
13+
}
14+
15+
export function isCapsolverEnabled(apiKey, enabled) {
16+
return enabled === true && isValidCapsolverApiKey(apiKey);
17+
}

src/chrome/src/agent/tools.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ export const AGENT_TOOLS = [
10541054
type: 'function',
10551055
function: {
10561056
name: 'solve_captcha',
1057-
description: 'Solve a CAPTCHA on the current page using the CapSolver API (only available when the user has enabled CapSolver and provided an API key in Settings → General → Advanced). The tool scans every frame, ranks the active visible widget above hidden/background integrations, and fills missing type/site-key parameters from the selected frame. Returns the token, selected frame/type, and targeted injection/callback status; verify page progress afterward because token injection alone does not prove the challenge cleared. On failure (no CapSolver key configured, ambiguous candidates, unknown type, API error, timeout) the tool returns `{ success: false, error: "..." }` — use only the exact frameUrl, websiteKey, frameId, or framePath discriminator offered by the ambiguity result; otherwise ask the user to solve it manually.',
1057+
description: 'Solve a CAPTCHA on the current page using the CapSolver API (available when the user has saved a valid API key in Settings → General → Advanced). The tool scans every frame, ranks the active visible widget above hidden/background integrations, and fills missing type/site-key parameters from the selected frame. Returns the token, selected frame/type, and targeted injection/callback status; verify page progress afterward because token injection alone does not prove the challenge cleared. On failure (no CapSolver key configured, ambiguous candidates, unknown type, API error, timeout) the tool returns `{ success: false, error: "..." }` — use only the exact frameUrl, websiteKey, frameId, or framePath discriminator offered by the ambiguity result; otherwise ask the user to solve it manually.',
10581058
parameters: {
10591059
type: 'object',
10601060
properties: {

src/chrome/src/background.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
getClaudeOAuthStatus,
2828
} from './providers/oauth-claude.js';
2929
import { getBalance as capsolverGetBalance } from './agent/captcha-solver.js';
30+
import { isCapsolverEnabled } from './agent/capsolver-config.js';
3031
import { createCloudRunController } from './cloud-runs.js';
3132
import { ensureOffscreen } from './offscreen/ensure.js';
3233
import {
@@ -735,14 +736,15 @@ async function loadCustomSkills() {
735736
}
736737
const customSkillsReady = loadCustomSkills();
737738

738-
// CapSolver opt-in. We only need the toggle here — the API key is read at
739-
// call time inside the agent's solve_captcha handler so rotating it via
740-
// the settings page is picked up without a restart.
739+
// A valid key plus explicit consent enables CapSolver. Requiring the existing
740+
// boolean preserves legacy profiles that saved a key while the old switch was
741+
// off; pressing Save Key in the new UI sets consent to true.
741742
async function loadCaptchaSolver() {
742-
const stored = await chrome.storage.local.get('captchaSolverEnabled');
743-
if (stored.captchaSolverEnabled != null) {
744-
agent.captchaSolverEnabled = !!stored.captchaSolverEnabled;
745-
}
743+
const stored = await chrome.storage.local.get(['capsolverApiKey', 'captchaSolverEnabled']);
744+
agent.captchaSolverEnabled = isCapsolverEnabled(
745+
stored.capsolverApiKey,
746+
stored.captchaSolverEnabled,
747+
);
746748
}
747749
loadCaptchaSolver();
748750

@@ -931,9 +933,10 @@ chrome.storage.onChanged.addListener((changes) => {
931933
}
932934
refreshPrompts = true;
933935
}
934-
if (changes.captchaSolverEnabled) {
935-
agent.captchaSolverEnabled = !!changes.captchaSolverEnabled.newValue;
936-
refreshPrompts = true;
936+
if (changes.capsolverApiKey || changes.captchaSolverEnabled) {
937+
loadCaptchaSolver()
938+
.then(() => agent._refreshSystemPrompts())
939+
.catch((error) => console.warn('[WebBrain] CapSolver setting could not be refreshed', error));
937940
}
938941
if (changes.planBeforeActMode || changes.planBeforeAct) {
939942
applyPlanBeforeActMode(normalizePlanBeforeActMode({

src/chrome/src/ui/locales/ar.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,17 +458,17 @@ export default {
458458
"st.transcription.connected": "تم الاتصال! النموذج: {model}",
459459
"st.transcription.failed": "فشل: {error}",
460460
"st.transcription.fill_required": "املأ عنوان API الأساسي والنموذج أولًا.",
461-
"st.captcha.desc_html": "دع الوكيل يحلّ اختبارات CAPTCHA تلقائيًا عبر واجهة <a href=\"https://capsolver.com\" target=\"_blank\" style=\"color:var(--accent);\">CapSolver</a>. يدعم reCAPTCHA v2/v3 و hCaptcha و Cloudflare Turnstile. مُعطّل افتراضيًا — عند التعطيل، يتوقّف الوكيل ويطلب منك حلّ الاختبار بنفسك. تتقاضى CapSolver رسومًا لكل عملية حلّ (~$0.001–$0.003)؛ تستخدم حسابك ومفتاح API الخاص بك.",
461+
"st.captcha.desc_html": "دع الوكيل يحلّ اختبارات CAPTCHA تلقائيًا عبر واجهة <a href=\"https://capsolver.com\" target=\"_blank\" style=\"color:var(--accent);\">CapSolver</a>. يدعم reCAPTCHA v2/v3 وhCaptcha وCloudflare Turnstile. يؤدي حفظ مفتاح API صالح إلى تمكين CapSolver تلقائيًا؛ وبدونه، يتوقّف الوكيل ويطلب منك حلّ الاختبار بنفسك. تتقاضى CapSolver رسومًا لكل عملية حلّ (~$0.001–$0.003)؛ تستخدم حسابك ومفتاح API الخاص بك.",
462462
"st.captcha.enabled.label": "تفعيل CapSolver",
463463
"st.captcha.enabled.desc": "عندما يصادف الوكيل اختبار CAPTCHA فإنه يستدعي CapSolver مرّة واحدة قبل اللجوء إلى سؤالك. يتطلّب مفتاح API أدناه.",
464464
"st.captcha.api_key.label": "مفتاح CapSolver API",
465465
"st.captcha.save": "حفظ المفتاح",
466466
"st.captcha.check_balance": "التحقّق من الرصيد",
467467
"st.captcha.clear": "مسح",
468-
"st.captcha.saved": "تم الحفظ.",
468+
"st.captcha.saved": "تم الحفظ. تم تمكين CapSolver.",
469469
"st.captcha.cleared": "تم المسح.",
470470
"st.captcha.checking": "جارٍ التحقّق من الرصيد…",
471-
"st.captcha.need_key": "أدخل مفتاح API أولًا.",
471+
"st.captcha.need_key": "أدخل مفتاح CapSolver API صالحًا يبدأ بـ CAP-.",
472472
"st.captcha.balance_ok": "موافق — الرصيد: {balance}",
473473
"st.captcha.balance_fail": "فشل: {error}",
474474
"st.captcha.security_html": "<strong>تنبيه:</strong> يُخزَّن مفتاح API <strong>بصيغة نصية واضحة</strong> في التخزين المحلي للمتصفح. تتقاضى CapSolver رسومًا من حسابك عن كل عملية حلّ؛ ولن يستدعيها الوكيل إلا عندما يحجب اختبار CAPTCHA خطوةً فعلًا (مرّة واحدة كحدّ أقصى لكل مواجهة — لن يعيد المحاولة عند الفشل). تحظر شروط خدمة بعض المواقع حلّ اختبارات CAPTCHA آليًا؛ استخدم حسن تقديرك.",

src/chrome/src/ui/locales/bn.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -796,17 +796,17 @@ export default {
796796
'st.memory.reason.not_found': "কোনো সংরক্ষিত মেমরি সেই আইডি নেই।",
797797
'st.memory.security_html': "<strong>গোপনীয়তা:</strong> ব্যবহারকারীর মেমরি এই ব্রাউজার প্রোফাইলে প্লেইনটেক্সটে সংরক্ষণ করা হয়। সক্রিয় করা হলে, সিস্টেম প্রম্পটের অংশ হিসাবে আপনি যে LLM প্রদানকারীকে কনফিগার করেন সক্রিয় মেমরি রেকর্ডগুলি পাঠানো হয়৷ এখানে পাসওয়ার্ড, API কী, টোকেন, রিকভারি কোড বা সংবেদনশীল গোপনীয়তা সংরক্ষণ করবেন না।",
798798

799-
'st.captcha.desc_html': "এজেন্টকে স্বয়ংক্রিয়ভাবে এর মাধ্যমে ক্যাপচা সমাধান করতে দিন <a href=\"https://capsolver.com\" target=\"_blank\" style=\"color:var(--accent);\">CapSolver</a> API reCAPTCHA v2/v3, hCaptcha এবং Cloudflare টার্নস্টাইল সমর্থন করে। ডিফল্টরূপে বন্ধ — বন্ধ হয়ে গেলে, এজেন্ট থামে এবং আপনাকে নিজেই ক্যাপচা সমাধান করতে বলে। সমাধান প্রতি CapSolver চার্জ (~$0.001–$0.003); আপনি আপনার নিজের অ্যাকাউন্ট এবং API কী আনুন।",
799+
'st.captcha.desc_html': "এজেন্টকে <a href=\"https://capsolver.com\" target=\"_blank\" style=\"color:var(--accent);\">CapSolver</a> API-এর মাধ্যমে স্বয়ংক্রিয়ভাবে CAPTCHA সমাধান করতে দিন। এটি reCAPTCHA v2/v3, hCaptcha এবং Cloudflare Turnstile সমর্থন করে। একটি বৈধ API কী সংরক্ষণ করলে CapSolver স্বয়ংক্রিয়ভাবে সক্রিয় হয়; কী না থাকলে এজেন্ট থামে এবং আপনাকে নিজেই CAPTCHA সমাধান করতে বলে। প্রতিটি সমাধানের জন্য CapSolver চার্জ করে (~$0.001–$0.003); আপনি নিজের অ্যাকাউন্ট API কী ব্যবহার করেন।",
800800
'st.captcha.enabled.label': "CapSolver সক্ষম করুন",
801801
'st.captcha.enabled.desc': "যখন এজেন্ট একটি ক্যাপচা হিট করে তখন এটি আপনাকে জিজ্ঞাসা করার আগে একবার ক্যাপসোলভারকে কল করবে। নীচে একটি API কী প্রয়োজন৷",
802802
'st.captcha.api_key.label': "CapSolver API কী",
803803
'st.captcha.save': "কী সংরক্ষণ করুন",
804804
'st.captcha.check_balance': "ব্যালেন্স চেক করুন",
805805
'st.captcha.clear': "পরিষ্কার",
806-
'st.captcha.saved': "সংরক্ষিত",
806+
'st.captcha.saved': "সংরক্ষিত। CapSolver সক্রিয় হয়েছে।",
807807
'st.captcha.cleared': "সাফ করা হয়েছে।",
808808
'st.captcha.checking': "ব্যালেন্স চেক করা হচ্ছে...",
809-
'st.captcha.need_key': "প্রথমে একটি API কী লিখুন।",
809+
'st.captcha.need_key': "CAP- দিয়ে শুরু হওয়া একটি বৈধ CapSolver API কী লিখুন।",
810810
'st.captcha.balance_ok': "ঠিক আছে — ব্যালেন্স: {balance}",
811811
'st.captcha.balance_fail': "ব্যর্থ হয়েছে: {error}",
812812
'st.captcha.security_html': "<strong>হেড-আপ:</strong> API কী সংরক্ষণ করা হয় <strong>প্লেইনটেক্সট</strong> ব্রাউজার স্থানীয় স্টোরেজে। CapSolver প্রতিটি সমাধানের জন্য আপনার অ্যাকাউন্ট চার্জ করে; এজেন্ট কেবল তখনই এটিকে কল করবে যখন একটি ক্যাপচা প্রকৃতপক্ষে একটি পদক্ষেপকে ব্লক করে (প্রতি এনকাউন্টারে সর্বোচ্চ একবার - এটি ব্যর্থ হলে পুনরায় চেষ্টা করবে না)। কিছু সাইটের পরিষেবার শর্তাবলী স্বয়ংক্রিয় ক্যাপচা সমাধান নিষিদ্ধ করে; আপনার রায় ব্যবহার করুন।",

src/chrome/src/ui/locales/de.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -738,17 +738,17 @@ export default {
738738
'st.memory.security_html': '<strong>Datenschutz:</strong> Benutzermemorie wird als Klartext in diesem Browser-Profil gespeichert...',
739739

740740
// --- CAPTCHA settings ---
741-
'st.captcha.desc_html': 'Lassen Sie den Agenten CAPTCHAs automatisch über die <a href="https://capsolver.com" target="_blank" style="color:var(--accent);">CapSolver</a>-API lösen...',
741+
'st.captcha.desc_html': 'Lassen Sie den Agenten CAPTCHAs automatisch über die <a href="https://capsolver.com" target="_blank" style="color:var(--accent);">CapSolver</a>-API lösen. Unterstützt reCAPTCHA v2/v3, hCaptcha und Cloudflare Turnstile. Das Speichern eines gültigen API-Schlüssels aktiviert CapSolver automatisch; ohne Schlüssel hält der Agent an und bittet Sie, das CAPTCHA selbst zu lösen. CapSolver berechnet jede Lösung (~$0.001–$0.003); Sie verwenden Ihr eigenes Konto und Ihren eigenen API-Schlüssel.',
742742
'st.captcha.enabled.label': 'CapSolver aktivieren',
743743
'st.captcha.enabled.desc': 'Wenn der Agent auf ein CAPTCHA trifft, wird CapSolver einmal aufgerufen, bevor er Sie zur Lösung auffordert.',
744744
'st.captcha.api_key.label': 'CapSolver API-Schlüssel',
745745
'st.captcha.save': 'Schlüssel speichern',
746746
'st.captcha.check_balance': 'Guthaben prüfen',
747747
'st.captcha.clear': 'Löschen',
748-
'st.captcha.saved': 'Gespeichert.',
748+
'st.captcha.saved': 'Gespeichert. CapSolver ist aktiviert.',
749749
'st.captcha.cleared': 'Gelöscht.',
750750
'st.captcha.checking': 'Guthaben wird überprüft…',
751-
'st.captcha.need_key': 'Geben Sie zuerst einen API-Schlüssel ein.',
751+
'st.captcha.need_key': 'Geben Sie einen gültigen CapSolver-API-Schlüssel ein, der mit CAP- beginnt.',
752752
'st.captcha.balance_ok': 'OK — Guthaben: {balance}',
753753
'st.captcha.balance_fail': 'Fehlgeschlagen: {error}',
754754
'st.captcha.security_html': '<strong>Hinweis:</strong> Der API-Schlüssel wird als Klartext im Browser-Speicher gespeichert...',

0 commit comments

Comments
 (0)