[object Object]

← back to Pattern Vault

spoonflower: default to Browserbase cloud (no local Chrome); local fallback uses isolated bundled Chromium not real Chrome; drop real-Chrome manual helper per Steve

42412e96a8fa561ea594c7c87b24e64e6cff5d58 · 2026-07-16 10:06:57 -0700 · Steve Abrams

Files touched

Diff

commit 42412e96a8fa561ea594c7c87b24e64e6cff5d58
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 16 10:06:57 2026 -0700

    spoonflower: default to Browserbase cloud (no local Chrome); local fallback uses isolated bundled Chromium not real Chrome; drop real-Chrome manual helper per Steve
---
 .../daily/scripts/capture_sf_session.js            | 23 +++-------
 whimsical-compare/daily/scripts/sf_login_manual.sh | 52 ----------------------
 whimsical-compare/daily/scripts/upload_sf.js       | 23 +++++-----
 3 files changed, 18 insertions(+), 80 deletions(-)

diff --git a/whimsical-compare/daily/scripts/capture_sf_session.js b/whimsical-compare/daily/scripts/capture_sf_session.js
index 0eaaa1e..813bb2f 100644
--- a/whimsical-compare/daily/scripts/capture_sf_session.js
+++ b/whimsical-compare/daily/scripts/capture_sf_session.js
@@ -34,31 +34,20 @@ const EMAIL = process.env.SPOONFLOWER_EMAIL || envFile.SPOONFLOWER_EMAIL;
 const PASSWORD = process.env.SPOONFLOWER_PASSWORD || envFile.SPOONFLOWER_PASSWORD;
 const STATE = process.env.SF_STATE || path.join(os.homedir(), '.config/pattern-vault/sf-state.json');
 const HEADFUL = process.argv.includes('--headful');
-const MANUAL = process.argv.includes('--manual');
-
-// --manual: escalation fallback when Cloudflare bounces even the Playwright real-Chrome path.
-// Hands off to sf_login_manual.sh, which opens the SHARED profile in plain real Chrome (zero
-// automation flags) so you log in + clear the human-check by hand. No env creds needed.
-if (MANUAL) {
-  const { spawnSync } = require('child_process');
-  const r = spawnSync('bash', [path.join(__dirname, 'sf_login_manual.sh')], { stdio: 'inherit' });
-  process.exit(r.status || 0);
-}
 
 if (!EMAIL || !PASSWORD) { console.error('❌ missing SPOONFLOWER_EMAIL/PASSWORD (env or resize-it/.env)'); process.exit(2); }
 console.log(`🔐 capturing session for ${EMAIL} → ${STATE}`);
 
 (async () => {
   fs.mkdirSync(path.dirname(STATE), { recursive: true });
-  // REAL Google Chrome (channel:'chrome'), NOT Playwright's bundled Chromium. Cloudflare
-  // fingerprints bundled Chromium and loops the "verify you are human" check forever even after
-  // you solve it. A persistent profile keeps the cf_clearance cookie + login together so the
-  // clearance you earn once survives into upload_sf.js. NO userAgent override — a spoofed UA that
-  // mismatches the real Chrome binary is itself a CF red flag and voids cf_clearance.
-  const PROFILE = process.env.SF_PROFILE || path.join(os.homedir(), '.config/pattern-vault/sf-chrome-profile');
+  // Isolated bundled Chromium (a separate Chromium app), NOT Steve's real Google Chrome — no
+  // channel:'chrome', so this never hijacks his browser or profile. NOTE: bundled Chromium is more
+  // CF-detectable, so the primary/default path is now Browserbase (upload_sf.js, cloud) which logs
+  // in fresh and needs no local capture at all. This local capture stays for the SF_LOCAL fallback.
+  const PROFILE = process.env.SF_PROFILE || path.join(os.homedir(), '.config/pattern-vault/sf-chromium-profile');
   fs.mkdirSync(PROFILE, { recursive: true });
   const ctx = await chromium.launchPersistentContext(PROFILE, {
-    headless: !HEADFUL, channel: 'chrome', viewport: null,
+    headless: !HEADFUL, viewport: null,
     ignoreDefaultArgs: ['--enable-automation'],           // drop the automation banner
     args: ['--disable-blink-features=AutomationControlled'], // hide navigator.webdriver from CF
   });
diff --git a/whimsical-compare/daily/scripts/sf_login_manual.sh b/whimsical-compare/daily/scripts/sf_login_manual.sh
deleted file mode 100755
index a3f1576..0000000
--- a/whimsical-compare/daily/scripts/sf_login_manual.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env bash
-# ---------------------------------------------------------------------------
-# Spoonflower login FALLBACK — the most-human, least-detectable path.
-#
-# Opens the SHARED persistent profile (the same --user-data-dir upload_sf.js and
-# capture_sf_session.js use) in REAL Google Chrome with ZERO automation flags —
-# no Playwright, no CDP, no --enable-automation. This is the openclaw principle:
-# a genuine human Chrome that Cloudflare waves straight through.
-#
-# You log in + solve any "verify you are human" / Cloudflare check BY HAND, then
-# CLOSE the window. The login (and the valid cf_clearance cookie) persist inside
-# the profile on disk, so upload_sf.js reuses it with no challenge.
-#
-# Rule-safe: nothing is extracted. The session stays inside the browser profile —
-# no cookie/token dump (which the safety classifier blocks, correctly).
-#
-# Use this when the Playwright `channel:'chrome'` capture still lands on /login
-# (Cloudflare still bounced the automated fingerprint). Escalation ladder:
-#   1. node capture_sf_session.js --headful   (Playwright real-Chrome)
-#   2. THIS script                            (plain real-Chrome, no automation)
-#   3. browserbase (proxies + solveCaptchas)  (cloud, clears CF natively)
-# ---------------------------------------------------------------------------
-set -euo pipefail
-
-PROFILE="${SF_PROFILE:-$HOME/.config/pattern-vault/sf-chrome-profile}"
-CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
-
-[ -x "$CHROME" ] || { echo "❌ Google Chrome not found at: $CHROME"; exit 1; }
-mkdir -p "$PROFILE"
-
-cat <<EOF
-🔐 Opening Spoonflower in REAL Chrome (no automation) — profile: $PROFILE
-
-   Account: info@designerwallcoverings.com
-   1) Log in.
-   2) Solve any "verify you are human" / Cloudflare check.
-   3) When you're signed in, CLOSE this Chrome window.
-
-   Waiting until you close it…
-EOF
-
-# Foreground the binary against the DEDICATED profile dir so this blocks until the
-# window is closed. A dedicated data-dir means closing its only window quits this
-# instance (and won't disturb your everyday Chrome).
-"$CHROME" \
-  --user-data-dir="$PROFILE" \
-  --no-first-run \
-  --no-default-browser-check \
-  "https://www.spoonflower.com/login" >/dev/null 2>&1 || true
-
-echo "✅ Chrome closed — profile primed."
-echo "   Verify + upload:  node upload_sf.js <manifest.json> --armed"
diff --git a/whimsical-compare/daily/scripts/upload_sf.js b/whimsical-compare/daily/scripts/upload_sf.js
index 7304780..64b506f 100755
--- a/whimsical-compare/daily/scripts/upload_sf.js
+++ b/whimsical-compare/daily/scripts/upload_sf.js
@@ -26,9 +26,11 @@ const os = require('os');
 
 const MANIFEST = process.argv[2];
 const ARMED = process.argv.includes('--armed');
-// RUNG 3: cloud Chrome (residential proxy + native Cloudflare/captcha solving) when the local
-// browser is fingerprint-blocked. SF_BROWSERBASE=1 selects it. Logs in FRESH (no cloud profile).
-const BROWSERBASE = process.env.SF_BROWSERBASE === '1';
+// DEFAULT TRANSPORT = Browserbase cloud Chrome (residential proxy + native Cloudflare/captcha
+// solving). It runs entirely in the cloud — NO local browser window ever opens, and it never
+// touches Steve's real Chrome. Logs in FRESH (no cloud profile). Opt into the local isolated
+// Chromium fallback with SF_LOCAL=1 (only if you specifically want a local run).
+const BROWSERBASE = process.env.SF_LOCAL !== '1';
 // minimal KEY=value .env parser (no dotenv dep) — same shape capture_sf_session.js uses
 function loadEnvFile(p) { const o = {}; try { for (const l of fs.readFileSync(p, 'utf8').split('\n')) { const m = l.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/); if (m) o[m[1]] = m[2].replace(/^"(.*)"$/, '$1'); } } catch {} return o; }
 const rzEnv = loadEnvFile(path.join(os.homedir(), '.claude/skills/resize-it/.env'));
@@ -98,19 +100,18 @@ async function messyType(el, text) {
     browser = await chromium.connectOverCDP(bbSession.connectUrl);
     ctx = browser.contexts()[0];
   } else {
-    // ---- RUNGS 1-2: local REAL Google Chrome + the SAME persistent profile the capture step
-    // cleared Cloudflare in. Bundled Chromium (the old chromium.launch) was what CF blocked;
-    // the spoofed Chrome/126 UA made it worse. channel:'chrome' + no UA override = one honest
-    // fingerprint, and the on-disk profile carries a valid cf_clearance.
-    if (!fs.existsSync(STATE)) { result.sessionExpired = true; result.error = 'no session file (run capture_sf_session.js --headful, or use SF_BROWSERBASE=1)'; console.log(JSON.stringify(result)); return; }
-    const PROFILE = process.env.SF_PROFILE || path.join(os.homedir(), '.config/pattern-vault/sf-chrome-profile');
+    // ---- LOCAL FALLBACK (SF_LOCAL=1 only): isolated bundled Chromium — a separate Chromium app,
+    // NOT Steve's real Google Chrome (no channel:'chrome'), so it can't hijack his browser/profile.
+    // Uses its own persistent profile for the login. Note: bundled Chromium is more CF-detectable
+    // than the cloud path, so Browserbase (the default) remains the reliable route past Cloudflare.
+    if (!fs.existsSync(STATE)) { result.sessionExpired = true; result.error = 'no session file for local fallback (default path is Browserbase — just run without SF_LOCAL)'; console.log(JSON.stringify(result)); return; }
+    const PROFILE = process.env.SF_PROFILE || path.join(os.homedir(), '.config/pattern-vault/sf-chromium-profile');
     fs.mkdirSync(PROFILE, { recursive: true });
     ctx = await chromium.launchPersistentContext(PROFILE, {
       headless: (process.env.SF_HEADLESS === '1') ? true : !ARMED,
-      channel: 'chrome',
       viewport: ARMED ? null : { width: 1400, height: 900 },
       ignoreDefaultArgs: ['--enable-automation'],              // drop the automation banner
-      args: ['--start-maximized', '--disable-blink-features=AutomationControlled'], // hide navigator.webdriver
+      args: ['--disable-blink-features=AutomationControlled'], // hide navigator.webdriver
     });
     // Bridge the existing Spoonflower login from the legacy storageState file (portable auth cookies)
     // in case this profile is fresh. cf_clearance is fingerprint-bound and intentionally NOT imported.

← c528dae spoonflower: add rung-3 Browserbase upload path (SF_BROWSERB  ·  back to Pattern Vault  ·  chore: lint, refactor, v0.2.3 (session close) a337213 →