← back to Designer Wallcoverings
wallquest login CRACKED: real Chrome (channel:chrome persistent profile) + 2captcha reCAPTCHA solve → logs in + prices sy21843=$83.16 (spike proven). Root cause was login reCAPTCHA, not creds/fingerprint.
7efced75bba2bd29529ac3c3daa43a2569324693 · 2026-07-06 12:50:52 -0700 · Steve
Files touched
A scripts/wallquest-refresh/assist-login.cjsA scripts/wallquest-refresh/auto-login-2captcha.cjsA scripts/wallquest-refresh/diag-login.cjsA scripts/wallquest-refresh/spike-realchrome.cjs
Diff
commit 7efced75bba2bd29529ac3c3daa43a2569324693
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jul 6 12:50:52 2026 -0700
wallquest login CRACKED: real Chrome (channel:chrome persistent profile) + 2captcha reCAPTCHA solve → logs in + prices sy21843=$83.16 (spike proven). Root cause was login reCAPTCHA, not creds/fingerprint.
---
scripts/wallquest-refresh/assist-login.cjs | 32 +++++++++++++
scripts/wallquest-refresh/auto-login-2captcha.cjs | 55 +++++++++++++++++++++++
scripts/wallquest-refresh/diag-login.cjs | 41 +++++++++++++++++
scripts/wallquest-refresh/spike-realchrome.cjs | 37 +++++++++++++++
4 files changed, 165 insertions(+)
diff --git a/scripts/wallquest-refresh/assist-login.cjs b/scripts/wallquest-refresh/assist-login.cjs
new file mode 100644
index 00000000..a182f237
--- /dev/null
+++ b/scripts/wallquest-refresh/assist-login.cjs
@@ -0,0 +1,32 @@
+// Human-in-the-loop WallQuest login: opens real Chrome pre-filled with creds; Steve solves the reCAPTCHA
+// + clicks Login; we poll for the authenticated session (no navigation to avoid disrupting him), then verify
+// a price renders and LEAVE the session in the persistent profile for the scraper to reuse. No 2captcha cost.
+const {chromium}=require('playwright-core'); const fs=require('fs'); const {execSync}=require('child_process');
+const PSQL='/opt/homebrew/opt/postgresql@14/bin/psql';
+const ENVL=fs.readFileSync(require('os').homedir()+'/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local','utf8');
+const PGPW=(ENVL.match(/^POSTGRES_PASSWORD=(.+)$/m)||[])[1];
+const q=sql=>execSync(`PGPASSWORD='${PGPW}' ${PSQL} -h localhost -U dw_admin -d dw_unified -tAF'|' -c "${sql}"`,{encoding:'utf8',maxBuffer:1e8});
+const WPW=q("SELECT trade_password FROM vendor_registry WHERE vendor_code='wallquest';").trim();
+const PROFILE='/tmp/wq-chrome-profile'; fs.mkdirSync(PROFILE,{recursive:true});
+(async()=>{
+ const ctx=await chromium.launchPersistentContext(PROFILE,{channel:'chrome',headless:false,viewport:{width:1280,height:900}});
+ const page=ctx.pages()[0]||await ctx.newPage();
+ await page.goto('https://www.wallquest.com/login',{waitUntil:'domcontentloaded',timeout:45000});
+ await page.waitForTimeout(5000);
+ try{ await page.fill('#Email','info@designerwallcoverings.com'); await page.fill('#Password',WPW); }catch(e){}
+ console.error('>>> Chrome window open + pre-filled. SOLVE THE reCAPTCHA and click LOGIN now. Polling up to 180s...');
+ let ok=false;
+ for(let t=0;t<45;t++){ // ~180s
+ await page.waitForTimeout(4000);
+ let st={};
+ try{ st=await page.evaluate(()=>({url:location.href, leftLogin:!/\/login/i.test(location.href), loggedMarker:/log ?out|sign ?out/i.test(document.body.innerText) && !/please sign in/i.test(document.body.innerText)})); }catch(e){ continue; }
+ if(st.leftLogin || st.loggedMarker){ ok=true; console.error(`>>> login detected (t=${t*4}s, url=${st.url})`); break; }
+ }
+ if(!ok){ console.error('>>> TIMEOUT — no login detected in 180s. Leaving window open; re-run to re-poll.'); process.exit(2); }
+ // verify a known-priced product renders a price in this authenticated session
+ await page.goto('https://www.wallquest.com/sy21843',{waitUntil:'domcontentloaded',timeout:45000}); await page.waitForTimeout(14000);
+ const probe=await page.evaluate(()=>{const pv=([...document.querySelectorAll('[class*=price-value]')][0]||{}).textContent||'';const m=pv.match(/[\d,]+\.\d{2}/);return{price:m?m[0]:null,signInToView:/sign in to view/i.test(document.body.innerText)};});
+ console.error('VERIFY sy21843 (expect ~83.16): '+JSON.stringify(probe));
+ console.error(probe.price?`>>> SUCCESS — authenticated session prices sy21843 = ${probe.price}. Session saved in ${PROFILE} for scraper reuse.`:'>>> logged in but price not visible — check selector/product.');
+ await ctx.close().catch(()=>{}); // session cookies persist in the profile dir
+})();
diff --git a/scripts/wallquest-refresh/auto-login-2captcha.cjs b/scripts/wallquest-refresh/auto-login-2captcha.cjs
new file mode 100644
index 00000000..72f82681
--- /dev/null
+++ b/scripts/wallquest-refresh/auto-login-2captcha.cjs
@@ -0,0 +1,55 @@
+// Auto-solve the WallQuest login reCAPTCHA via 2captcha (HTTP API, no npm dep), inject token, submit,
+// verify a price renders, leave the authenticated session in the persistent profile for scraper reuse.
+// Real Chrome (channel:chrome) = human fingerprint. Cost: ~$0.003 per solve.
+const {chromium}=require('playwright-core'); const fs=require('fs'); const {execSync}=require('child_process');
+const os=require('os');
+const PSQL='/opt/homebrew/opt/postgresql@14/bin/psql';
+const ENVL=fs.readFileSync(os.homedir()+'/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local','utf8');
+const PGPW=(ENVL.match(/^POSTGRES_PASSWORD=(.+)$/m)||[])[1];
+const q=sql=>execSync(`PGPASSWORD='${PGPW}' ${PSQL} -h localhost -U dw_admin -d dw_unified -tAF'|' -c "${sql}"`,{encoding:'utf8',maxBuffer:1e8});
+const WPW=q("SELECT trade_password FROM vendor_registry WHERE vendor_code='wallquest';").trim();
+const CAPKEY=(fs.readFileSync(os.homedir()+'/.claude/skills/2captcha/.env','utf8').match(/^TWOCAPTCHA_API_KEY=(.+)$/m)||[])[1]?.trim();
+const PROFILE='/tmp/wq-chrome-profile'; fs.mkdirSync(PROFILE,{recursive:true});
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+
+async function solve(sitekey,pageurl){
+ const inUrl=`http://2captcha.com/in.php?key=${CAPKEY}&method=userrecaptcha&googlekey=${sitekey}&pageurl=${encodeURIComponent(pageurl)}&json=1`;
+ const j=await (await fetch(inUrl,{method:'POST'})).json();
+ if(j.status!==1) throw new Error('2captcha in.php: '+JSON.stringify(j));
+ const id=j.request; console.error(`2captcha job=${id} solving...`);
+ for(let i=0;i<40;i++){ // up to ~200s
+ await sleep(5000);
+ const r=await (await fetch(`http://2captcha.com/res.php?key=${CAPKEY}&action=get&id=${id}&json=1`)).json();
+ if(r.status===1) return r.request;
+ if(r.request!=='CAPCHA_NOT_READY') throw new Error('2captcha res.php: '+JSON.stringify(r));
+ if(i%3===0) console.error(` ...still solving (${i*5}s)`);
+ }
+ throw new Error('2captcha timeout');
+}
+
+(async()=>{
+ if(!CAPKEY){ console.error('NO 2captcha key'); process.exit(3); }
+ console.error(`WPW len=${WPW.length}, capkey len=${CAPKEY.length}`);
+ const ctx=await chromium.launchPersistentContext(PROFILE,{channel:'chrome',headless:false,viewport:{width:1280,height:900}});
+ const page=ctx.pages()[0]||await ctx.newPage();
+ try{
+ await page.goto('https://www.wallquest.com/login',{waitUntil:'domcontentloaded',timeout:45000}); await sleep(6000);
+ await page.fill('#Email','info@designerwallcoverings.com'); await page.fill('#Password',WPW);
+ const sitekey=await page.evaluate(()=>{const el=document.querySelector('[data-sitekey]'); if(el)return el.dataset.sitekey; const ifr=[...document.querySelectorAll('iframe')].find(f=>/recaptcha/.test(f.src||'')); if(ifr){const m=(ifr.src||'').match(/[?&]k=([^&]+)/); return m?m[1]:null;} return null;});
+ console.error('sitekey='+sitekey);
+ if(!sitekey){ console.error('NO sitekey found — page may not have rendered captcha'); await page.screenshot({path:'/tmp/wq-nokey.png'}).catch(()=>{}); process.exit(4); }
+ const token=await solve(sitekey,page.url());
+ console.error('token received (len '+token.length+') — injecting');
+ await page.evaluate(tok=>{let ta=document.querySelector('textarea[name="g-recaptcha-response"]'); if(!ta){ta=document.createElement('textarea');ta.name='g-recaptcha-response';ta.style.display='none';document.body.appendChild(ta);} ta.value=tok; ta.innerHTML=tok;},token);
+ let clicked=false; for(const sel of ['input.login-button','.login-button','button.login-button']){if(await page.$(sel)){try{await page.click(sel,{timeout:5000});clicked=true;break;}catch(e){}}}
+ if(!clicked)await page.press('#Password','Enter');
+ await sleep(9000);
+ const post=await page.evaluate(()=>{const errSel=['.validation-summary-errors','.field-validation-error','.message-error'];const errs=errSel.flatMap(s=>[...document.querySelectorAll(s)].map(e=>(e.textContent||'').trim())).filter(Boolean);return{url:location.href,leftLogin:!/\/login/i.test(location.href),errors:errs.slice(0,3)};});
+ console.error('POST-LOGIN: '+JSON.stringify(post));
+ await page.goto('https://www.wallquest.com/sy21843',{waitUntil:'domcontentloaded',timeout:45000}); await sleep(14000);
+ const probe=await page.evaluate(()=>{const pv=([...document.querySelectorAll('[class*=price-value]')][0]||{}).textContent||'';const m=pv.match(/[\d,]+\.\d{2}/);return{price:m?m[0]:null,signInToView:/sign in to view/i.test(document.body.innerText)};});
+ console.error('VERIFY sy21843 (expect ~83.16): '+JSON.stringify(probe));
+ console.error(probe.price?`>>> SUCCESS — session prices sy21843 = ${probe.price}. Saved in ${PROFILE}.`:'>>> FAIL — still gated after captcha solve (signInToView='+probe.signInToView+')');
+ }catch(e){console.error('AUTO-LOGIN ERR: '+e.message);}
+ finally{await ctx.close().catch(()=>{});}
+})();
diff --git a/scripts/wallquest-refresh/diag-login.cjs b/scripts/wallquest-refresh/diag-login.cjs
new file mode 100644
index 00000000..4e458912
--- /dev/null
+++ b/scripts/wallquest-refresh/diag-login.cjs
@@ -0,0 +1,41 @@
+// One-session WallQuest login diagnostic — screenshots + DOM probes to see WHY login won't price. Read-only.
+const Browserbase=require('@browserbasehq/sdk').default; const {chromium}=require('playwright-core'); const fs=require('fs'); const {execSync}=require('child_process');
+require('dotenv').config({path:require('os').homedir()+'/.claude/skills/browserbase/.env'});
+const PSQL='/opt/homebrew/opt/postgresql@14/bin/psql';
+const ENVL=fs.readFileSync(require('os').homedir()+'/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local','utf8');
+const PGPW=(ENVL.match(/^POSTGRES_PASSWORD=(.+)$/m)||[])[1];
+const q=sql=>execSync(`PGPASSWORD='${PGPW}' ${PSQL} -h localhost -U dw_admin -d dw_unified -tAF'|' -c "${sql}"`,{encoding:'utf8',maxBuffer:1e8});
+const WPW=q("SELECT trade_password FROM vendor_registry WHERE vendor_code='wallquest';").trim();
+const bb=new Browserbase({apiKey:process.env.BROWSERBASE_API_KEY});
+const DIR='/tmp/wq-diag'; fs.mkdirSync(DIR,{recursive:true});
+const goto=async(p,u)=>{for(let i=0;i<3;i++){try{await p.goto(u,{waitUntil:'domcontentloaded',timeout:45000});return 1;}catch(e){await p.waitForTimeout(3000);}}return 0;};
+(async()=>{
+ console.error(`WPW length=${WPW.length}`);
+ const s=await bb.sessions.create({projectId:process.env.BROWSERBASE_PROJECT_ID,proxies:true,browserSettings:{solveCaptchas:true}});
+ const br=await chromium.connectOverCDP(s.connectUrl); const ctx=br.contexts()[0]; const page=ctx.pages()[0]||await ctx.newPage();
+ try{
+ await goto(page,'https://www.wallquest.com/login'); await page.waitForTimeout(8000);
+ // what login controls exist?
+ const controls=await page.evaluate(()=>({
+ email: !!document.querySelector('#Email'), pass: !!document.querySelector('#Password'),
+ buttons: [...document.querySelectorAll('button, input[type=submit], .login-button, input.login-button')].map(b=>({tag:b.tagName,cls:b.className,type:b.type,val:b.value||'',txt:(b.textContent||'').trim().slice(0,30)})).slice(0,12),
+ bodySample: document.body.innerText.slice(0,200)
+ }));
+ console.error('LOGIN CONTROLS: '+JSON.stringify(controls,null,1));
+ await page.fill('#Email','info@designerwallcoverings.com'); await page.fill('#Password',WPW);
+ await page.screenshot({path:DIR+'/1-login-filled.png'});
+ let clicked=false; for(const sel of ['input.login-button','.login-button','button.login-button']){if(await page.$(sel)){try{await page.click(sel,{timeout:5000});clicked=true;console.error('clicked '+sel);break;}catch(e){}}}
+ if(!clicked){await page.press('#Password','Enter');console.error('fell back to Enter');}
+ await page.waitForTimeout(9000);
+ await page.screenshot({path:DIR+'/2-after-login.png'});
+ const post=await page.evaluate(()=>({url:location.href, hasLogout:/log ?out|sign ?out/i.test(document.body.innerText), err:(document.querySelector('.validation-summary-errors,.field-validation-error,.message-error')||{}).textContent||'', bodySample:document.body.innerText.slice(0,250)}));
+ console.error('POST-LOGIN: '+JSON.stringify(post,null,1));
+ // verify product
+ await goto(page,'https://www.wallquest.com/sy21843'); await page.waitForTimeout(15000);
+ await page.screenshot({path:DIR+'/3-product.png',fullPage:false});
+ const prod=await page.evaluate(()=>({priceEls:[...document.querySelectorAll('[class*=price]')].map(e=>({cls:e.className,txt:(e.textContent||'').trim().slice(0,40)})).slice(0,10), signInToView:/sign in to view/i.test(document.body.innerText)}));
+ console.error('PRODUCT PRICE PROBE: '+JSON.stringify(prod,null,1));
+ }catch(e){console.error('DIAG ERR: '+e.message);}
+ finally{await br.close().catch(()=>{});}
+ console.error('screenshots in '+DIR);
+})();
diff --git a/scripts/wallquest-refresh/spike-realchrome.cjs b/scripts/wallquest-refresh/spike-realchrome.cjs
new file mode 100644
index 00000000..d21cf577
--- /dev/null
+++ b/scripts/wallquest-refresh/spike-realchrome.cjs
@@ -0,0 +1,37 @@
+// BOUNDED SPIKE: prove real local Chrome (not headless Browserbase) can log into WallQuest and see a price.
+// One login + one known product (sy21843 = $83.16). No crawl, no DB writes. Isolated Chrome profile.
+const {chromium}=require('playwright-core'); const fs=require('fs'); const {execSync}=require('child_process');
+const PSQL='/opt/homebrew/opt/postgresql@14/bin/psql';
+const ENVL=fs.readFileSync(require('os').homedir()+'/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local','utf8');
+const PGPW=(ENVL.match(/^POSTGRES_PASSWORD=(.+)$/m)||[])[1];
+const q=sql=>execSync(`PGPASSWORD='${PGPW}' ${PSQL} -h localhost -U dw_admin -d dw_unified -tAF'|' -c "${sql}"`,{encoding:'utf8',maxBuffer:1e8});
+const WPW=q("SELECT trade_password FROM vendor_registry WHERE vendor_code='wallquest';").trim();
+const PROFILE='/tmp/wq-chrome-profile'; fs.mkdirSync(PROFILE,{recursive:true});
+const goto=async(p,u)=>{for(let i=0;i<3;i++){try{await p.goto(u,{waitUntil:'domcontentloaded',timeout:45000});return 1;}catch(e){await p.waitForTimeout(3000);}}return 0;};
+(async()=>{
+ console.error(`WPW length=${WPW.length}`);
+ let ctx;
+ try{
+ ctx=await chromium.launchPersistentContext(PROFILE,{channel:'chrome',headless:false,viewport:{width:1280,height:900}});
+ }catch(e){ console.error('LAUNCH FAILED (real Chrome): '+e.message); process.exit(3); }
+ const page=ctx.pages()[0]||await ctx.newPage();
+ try{
+ await goto(page,'https://www.wallquest.com/login'); await page.waitForTimeout(6000);
+ await page.fill('#Email','info@designerwallcoverings.com'); await page.fill('#Password',WPW);
+ let clicked=false; for(const sel of ['input.login-button','.login-button','button.login-button']){if(await page.$(sel)){try{await page.click(sel,{timeout:5000});clicked=true;console.error('clicked '+sel);break;}catch(e){}}}
+ if(!clicked){await page.press('#Password','Enter');console.error('fell back to Enter');}
+ await page.waitForTimeout(9000);
+ await page.screenshot({path:'/tmp/wq-realchrome-postlogin.png'}).catch(()=>{});
+ const post=await page.evaluate(()=>{
+ const errSel=['.validation-summary-errors','.field-validation-error','.message-error','.login-error','span[data-valmsg-for]'];
+ const errs=errSel.flatMap(s=>[...document.querySelectorAll(s)].map(e=>(e.textContent||'').trim())).filter(Boolean);
+ return {url:location.href, hasLogout:/log ?out|sign ?out/i.test(document.body.innerText), errors:errs.slice(0,5), formStillThere:!!document.querySelector('#Password'), bodyHead:document.body.innerText.replace(/\s+/g,' ').slice(0,200)};
+ });
+ console.error('POST-LOGIN: '+JSON.stringify(post));
+ await goto(page,'https://www.wallquest.com/sy21843'); await page.waitForTimeout(14000);
+ const probe=await page.evaluate(()=>{const pv=([...document.querySelectorAll('[class*=price-value]')][0]||{}).textContent||'';const m=pv.match(/[\d,]+\.\d{2}/);return{priceText:pv.trim().slice(0,40),price:m?m[0]:null,signInToView:/sign in to view/i.test(document.body.innerText)};});
+ console.error('PRICE PROBE (sy21843, expect ~83.16): '+JSON.stringify(probe));
+ console.error(probe.price?`SPIKE RESULT: SUCCESS — real Chrome sees price ${probe.price}`:'SPIKE RESULT: FAIL — still price-gated (signInToView='+probe.signInToView+')');
+ }catch(e){console.error('SPIKE ERR: '+e.message);}
+ finally{await ctx.close().catch(()=>{});}
+})();
← a48458ac Daisy Bennett viewer: local images (bypass WallQuest hotlink
·
back to Designer Wallcoverings
·
Localize Carl Robinson viewer (:64761): local images + real- bd1c4cee →