Skip to content

feat: local runner + stealth scraping backends (Camoufox, Scweet) - #90

Merged
5uck1ess merged 3 commits into
mainfrom
feat/local-runner-and-stealth-scrape
Apr 15, 2026
Merged

feat: local runner + stealth scraping backends (Camoufox, Scweet)#90
5uck1ess merged 3 commits into
mainfrom
feat/local-runner-and-stealth-scrape

Conversation

@5uck1ess

Copy link
Copy Markdown
Owner

Summary

  • Adds OpenAI-compatible local runner (src/runners/local.go) for Ollama/llama-server/vLLM. Opt-in via DEVKIT_LOCAL_ENABLED=1; defaults to qwen3:32b at http://localhost:11434/v1. Env overrides: DEVKIT_LOCAL_ENDPOINT, DEVKIT_LOCAL_MODEL, DEVKIT_LOCAL_API_KEY. Intended for fast-tier dispatch — cloud runners remain default for smart/general.
  • Expands scrape skill with Camoufox (patched Firefox, leak-fixed JA3/TLS fingerprints) between Playwright and Firecrawl with auto-fallback on Cloudflare/DataDome/Incapsula challenge-page signatures. Free and local.
  • Adds Scweet as host-routed backend for x.com / twitter.com URLs, gated behind SCWEET_AUTH_TOKEN + SCWEET_CT0.
  • Docs synced: CLAUDE.md, README.md, ROADMAP.md, skills/scrape/SKILL.md.

Context

Motivated by research into local inference stacks (RTX 5090 32GB + Apple Silicon) and anti-bot scraping. Local runner is deliberately opt-in and limited-scope: local 32B models have 15-30% tool-call failure rates on 4+ step chains vs <5% for Sonnet, which would corrupt enforce: hard workflow state if wired into smart/general tiers.

Test plan

  • make check — go vet + gofmt clean
  • go test ./... — 310 tests pass across 6 packages
  • Manual: set DEVKIT_LOCAL_ENABLED=1 with Ollama running and qwen3:32b pulled; confirm DetectRunners() returns the local runner and a Run() call completes
  • Manual: run /devkit:scrape against a Cloudflare-protected URL and verify Playwright → Camoufox fallback triggers on challenge signature
  • Manual: run /devkit:scrape https://x.com/some/status with SCWEET_AUTH_TOKEN/SCWEET_CT0 set and verify host routing skips the main chain

Security

  • No hardcoded secrets; all credentials from env
  • HTTP body built via json.Marshal, no string interpolation
  • Context-scoped requests with timeouts (3s probe, 10min run)
  • Scrape drivers pass URLs via env vars, never interpolated into shell
  • Existing URL validation (http/https only, no private IPs, no @) applies to new backends

Commits

  • 7ae1cf2 feat: add local runner + stealth scraping backends
  • 17032de docs: update CLAUDE.md, README.md, ROADMAP.md for local runner + stealth scrape feature

- src/runners/local.go: OpenAI-compatible runner for Ollama/llama-server/vLLM,
  opt-in via DEVKIT_LOCAL_ENABLED=1, defaults to qwen3:32b at localhost:11434.
  Intended for fast-tier dispatch only — local models have higher tool-call
  error rates that corrupt enforce:hard workflow state.
- skills/scrape: add Camoufox (TLS-spoofing Firefox) between Playwright and
  Firecrawl with challenge-page auto-fallback (Cloudflare/DataDome/Incapsula
  signatures), and Scweet as a host-routed backend for x.com/twitter.com.
local.go:
- Available(): treat HTTP >=400 as unavailable (was <500); log probe failures
  via DEVKIT_LOCAL_DEBUG=1 so auth/endpoint misconfig is diagnosable
- Run(): HTTP error path now sets ExitCode=1 (was leaking raw HTTP status);
  JSON unmarshal error wraps the underlying decode error with %w
- Add localRole string type + const roleSystem/roleUser/roleAssistant — makes
  illegal roles unrepresentable at compile time
- Add DEVKIT_LOCAL_TIMEOUT env knob (default 600s)
- Extract envDefault helper; drop dead Stream:false field

local_test.go (new):
- Table-driven coverage via httptest.NewServer for: Available() gating across
  env + HTTP status matrix, Run() HTTP statuses (200/400/401/500/503 + non-JSON
  + empty choices), context cancellation, conditional auth header, network
  unreachable, system-prompt-file both branches, endpoint/model defaults,
  DEVKIT_LOCAL_TIMEOUT parsing edge cases (9 test groups, 41 subtests)

scrape SKILL.md:
- Replace hardcoded /tmp/devkit-*.* paths with mktemp + trap cleanup
  (Windows portability + predictable-path symlink attack surface, per
  common.md temp-file rule)
- Camoufox + Scweet drivers now wrap execution in try/except emitting
  structured JSON error with stage + truncated traceback — bash caller can
  distinguish blocked page from crashed process
- Narrow challenge-page detection: require positive anti-bot signature OR
  body-text<200chars+no-structural-tag (was "under 2KB no article/main",
  which false-positived on valid minimal pages)
- Fix challenge signatures: drop incorrect DDG_ (DuckDuckGo), add
  dd_cookie_test_/datadome (actual DataDome), add px-captcha/perimeterx
- Scweet driver: route by URL path (status → get_tweet, profile →
  get_user_information) matching real Scweet 2.x API; swap ad-hoc
  SCWEET_AUTH_TOKEN+SCWEET_CT0 for SCWEET_COOKIES_PATH pointing at a
  JSON cookies file (standard Scweet input, no creds in env)
- Playwright driver: networkidle → domcontentloaded (avoids hangs on
  persistent websockets); browser.close() error now logged to stderr
  instead of fully suppressed

README.md:
- Architecture diagram: split subprocess runners (Codex/Gemini) from
  in-process HTTP runner (local) — they're categorically different
@5uck1ess
5uck1ess merged commit 832a693 into main Apr 15, 2026
6 checks passed
@5uck1ess
5uck1ess deleted the feat/local-runner-and-stealth-scrape branch April 15, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant