Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ec253f1
feat: type distorted-text captchas and drag puzzle-piece sliders
JWriter20 Aug 3, 2026
68e4bf3
prompts: teach generation 2 grids what a replacing cell looks like
JWriter20 Aug 4, 2026
930dd01
solver: accept text_mode, and submit typed answers
JWriter20 Aug 5, 2026
81bbefc
solver: a sprite board is not a tile grid
JWriter20 Aug 6, 2026
8b09790
submit the answer on an inline widget, in both ports
JWriter20 Aug 6, 2026
4f4d2ad
free drag: carry the piece to the slot, not along the tray
JWriter20 Aug 6, 2026
464c6ed
submit a selection when it is made, not a round later
JWriter20 Aug 6, 2026
b6a23d3
report the solver's own reason, not a guess about it
JWriter20 Aug 6, 2026
d6355a7
vendor the mouse trajectory, and notice a solve that already landed
JWriter20 Aug 7, 2026
4adad55
docs: the logo, the vendors we actually solve, and numbers we stand b…
JWriter20 Aug 7, 2026
934ea91
send small captchas at the geometry the adapter was trained on
JWriter20 Aug 11, 2026
9df906e
_pick: take the cleanest trace's colour, not the longest one's
JWriter20 Aug 12, 2026
a2f72fb
2.5.0: auto-solve watcher, a verified Puppeteer adapter, and license …
JWriter20 Aug 18, 2026
1ec2d31
Runnable Playwright and Puppeteer examples, and a pixel-budget test t…
JWriter20 Aug 18, 2026
c1be80e
Examples: say how to run them from a checkout
JWriter20 Aug 18, 2026
6438014
Pin that one watcher covers every navigation, and drop the browser-wi…
JWriter20 Aug 18, 2026
0902e94
A board that cycles was solved as a still, forever
JWriter20 Aug 19, 2026
7eb2448
reCAPTCHA spent 40 seconds photographing an iframe that was already gone
JWriter20 Aug 19, 2026
ba8a315
A chipped tile is a finished answer, and a photo is not a badge
JWriter20 Aug 19, 2026
0cd9252
Point the client at v1.2, and document the hosted API
JWriter20 Aug 19, 2026
5208c06
Merge fix/cycling-board-solved-as-still: chipped boards, badge phanto…
JWriter20 Aug 19, 2026
58a51af
Two published releases, and say what each one actually solves
JWriter20 Aug 19, 2026
cb997de
Measured sizes for the v1.2 merges
JWriter20 Aug 19, 2026
cf227d9
Stop waiting for things that cannot happen, and press GeeTest's OK
JWriter20 Aug 19, 2026
66ebabd
2.6.0, and the MCP with it
JWriter20 Aug 19, 2026
5514dd1
Ask for the model models.json names, not a hardcoded generation-1 alias
JWriter20 Aug 20, 2026
28a0318
Ask the private repo for Tier 3, not Tier 2
JWriter20 Aug 20, 2026
6cb8f82
Dispatch through REST, so the token needs only the permission it uses
JWriter20 Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ VLLM_BASE_URL=http://localhost:8000/v1

# Bearer token.
# Self-hosted: whatever key you configured on your vLLM server.
# Cloud API (coming soon): your CaptchaKraken account key.
# Hosted API: your `ck_live_…` key from captchakraken.com/signin.
#
# You can skip BOTH of these on the hosted API: the MCP server's
# `create_api_key` tool writes them to ~/.captchakraken/credentials, which the
# solver reads on its own.
CAPTCHA_KRAKEN_API_KEY=your-key-here

# ── Advanced (optional) — change WHICH model is served. The solver is
# model-agnostic; these are the only knobs, and all have sensible defaults. ──
# CAPTCHA_BASE_MODEL=Qwen/Qwen3.5-9B
# CAPTCHA_LORA_ADAPTER=CaptchaKraken/CaptchaKraken_v1
# CAPTCHA_LORA_NAME=captcha
# CAPTCHA_BASE_MODEL=RedHatAI/Qwen3.5-9B-FP8-dynamic
# CAPTCHA_LORA_ADAPTER=CaptchaKraken/CaptchaKraken-Lora-v1.2
# CAPTCHA_LORA_NAME=captcha-v12
# CAPTCHA_KRAKEN_AUTOSTART=1 # set 0 to never auto-start a local server
44 changes: 0 additions & 44 deletions .github/workflows/tier2-request.yml

This file was deleted.

71 changes: 71 additions & 0 deletions .github/workflows/tier3-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Request Tier 3 driver gate

# Every PR here triggers a TIER 3 gate: both shipped solver ports driven
# end to end through a real browser against the fixture suite. The fixtures,
# generators and adapter live in the PRIVATE CaptchaKrakenFinetune repo, not
# here, so this workflow does not run the gate itself — it asks the private repo
# to run it and post a redacted result back (a commit status + a PR comment; see
# that repo's public-pr-gate.yml and scripts/post_public_check.py for exactly
# what does and does not leave it).
#
# TIER 3, NOT TIER 2. This repo ships the DRIVER. A pull request here cannot
# change the model, so a Tier 2 solve-rate measurement gated on the one thing
# the PR could not move. What a PR here CAN break is driving — a selector, a
# click landing off-widget, one port asking for a different adapter than the
# other — and Tier 3 is the only gate that exercises both ports end to end.
# On 2026-08-20 that last failure cost the JS port 22 of its 44 puzzle types
# while every check here stayed green.
#
# workflow_dispatch, not repository_dispatch: dispatching a workflow needs
# only `Actions: write` on the private repo. repository_dispatch would need a
# token with `Contents: write` there instead — a strictly bigger blast radius,
# and this (public) repo is the more exposed place for that token to live.
#
# Runs on ubuntu-latest — public repos get free Actions minutes, so this has
# no dependency on the private repo's runner/billing state.
#
# Fork PRs: `pull_request` (not `pull_request_target`) never exposes secrets to
# a fork's own workflow run, so PRIVATE_DISPATCH_TOKEN simply isn't available
# there — this step no-ops rather than leaking a token to untrusted code. The
# `if:` guard below makes that explicit instead of relying on the implicit
# secret absence alone. A fork PR shows the check as "Expected"/pending; the
# maintainer path is to push the branch into this repo directly.

on:
pull_request:
branches: [main]

jobs:
request:
name: Dispatch Tier 3 gate
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Dispatch public-pr-gate.yml on the private repo
env:
GH_TOKEN: ${{ secrets.PRIVATE_DISPATCH_TOKEN }}
run: |
set -euo pipefail
# The REST dispatch endpoint directly, NOT `gh workflow run`.
#
# `gh workflow run` looks the repository's default branch up over
# GraphQL first, purely to pick a ref for you. That lookup reads
# repository METADATA, so the convenience costs a permission the
# dispatch itself does not need, and the token failed on exactly that:
# Resource not accessible by personal access token
# (repository.defaultBranchRef)
# PRIVATE_DISPATCH_TOKEN is meant to carry `Actions: write` on the
# private repo and nothing more, so the smaller-scope call is the
# correct one rather than a workaround.
#
# Naming ref=main explicitly is also the more honest statement:
# workflow_dispatch can ONLY target a workflow file on the default
# branch, so this is a fact about the dispatch, not a default to be
# discovered at run time.
jq -n \
--arg pr "${{ github.event.pull_request.number }}" \
--arg sha "${{ github.event.pull_request.head.sha }}" \
'{ref: "main", inputs: {pr: $pr, sha: $sha}}' \
| gh api --input - -X POST \
repos/JWriter20/CaptchaKrakenFinetune/actions/workflows/public-pr-gate.yml/dispatches
echo "Dispatched. The private repo verifies this PR's head via the API before running anything — see public-pr-gate.yml. Status arrives as the 'tier3/driver-gate' check on this PR shortly after."
Loading
Loading