Problem: credential and cookie files were world-readable (0644),
passwords transited via `CP_CREDENTIALS` env var (visible in
`/proc/PID/environ`), and Kattis/USACO echoed passwords back
through stdout unnecessarily.
Solution: set 0600 permissions on `cp-nvim.json` and `cookies.json`
after every write, pass credentials via stdin pipe instead of env
var, and stop emitting passwords in ndjson from Kattis/USACO
`LoginResult` (CSES token emission unchanged).
## Problem
Loading a problem whose source file already exists silently overwrites
user code. Submitting an empty file sends a blank submission to the
platform. Two ruff lint violations existed in the scrapers.
## Solution
- `setup.lua`: when the target source file exists on the filesystem
(`vim.uv.fs_stat`), show an inline `Overwrite? [y/N]:` prompt. Declining
keeps the existing file open and registers state normally. Skipped when
the file is already loaded in a buffer.
- `submit.lua`: resolve path to absolute, use `vim.uv.fs_stat` to verify
existence, abort with WARN if `stat.size == 0` ("Submit aborted: source
file has no content").
- `scrapers/atcoder.py`: remove unused `pathlib.Path` import (F401).
- `scrapers/base.py`: move local imports to top of file (E402).
Closes#364, #365.
## Problem
Scraper cookie handling was fragmented across per-platform files with no
shared access, httpx scrapers lacked `checking_login` fast paths on
login, and several re-auth edge cases (CodeChef submit, CF cookie guard,
AtCoder cookie persistence) caused unnecessary full re-logins or silent
failures.
## Solution
Centralize all cookie storage into a single `cookies.json` via helpers
in `base.py`. Add `checking_login` fast paths to `kattis.py` (using the
`x-username` response header as a session probe), `usaco.py`, and
`cses.py` login flows. Fix `kattis.py` submit to emit `checking_login`
only after loading cookies. Remove AtCoder cookie persistence from login
entirely — always do a fresh session. Harden CodeChef and CF reauth
with consistent status logging and cookie guard checks.
## Problem
`:CP <platform> login` blindly caches username/password without
server-side
validation. Bad credentials are only discovered at submit time, which is
confusing and wastes a browser session.
## Solution
Wire `:CP <platform> login` through the scraper pipeline so each
platform
actually authenticates before persisting credentials. On failure, the
user
sees an error and nothing is cached.
- CSES: reuses `_check_token` (fast path) and `_web_login`; returns API
token
in `LoginResult.credentials` so subsequent submits skip re-auth.
- AtCoder/Codeforces: new `_login_headless` functions open a
StealthySession,
solve Turnstile/Cloudflare, fill the login form, and validate success by
checking for the logout link. Cookies only persist on confirmed login.
- CodeChef/Kattis/USACO: return "not yet implemented" errors.
- `scraper.lua`: generalizes submit-only guards (`needs_browser` flag)
to
cover both `submit` and `login` subcommands.
- `credentials.lua`: prompts for username/password, passes cached token
for
CSES fast path, shows ndjson status notifications, only caches on
success.
## Problem
After the initial submit hardening, two issues remained: source code was
read in Lua and piped as stdin to the scraper (unnecessary roundtrip
since
the file exists on disk), and CF's `page.fill()` timed out on the hidden
`textarea[name="source"]` because CodeMirror owns the editor state.
## Solution
Pass the source file path as a CLI arg instead — AtCoder calls
`page.set_input_files(file_path)` directly, CF reads it with
`Path(file_path).read_text()`. Fix CF source injection via
`page.evaluate()`
into the CodeMirror instance. Extract `BROWSER_SUBMIT_NAV_TIMEOUT` as a
per-platform `defaultdict` (CF defaults to 2× nav timeout). Save the
buffer
with `vim.cmd.update()` before submitting.
Problem: problem pages contain floating-point precision requirements and
contest start timestamps that were not being extracted or stored. The
submit workflow also needed a foundation in the scraper layer.
Solution: add extract_precision() to base.py and propagate through all
scrapers into cache. Add start_time to ContestSummary and extract it
from AtCoder and Codeforces. Add SubmitResult model, abstract submit()
method, submit CLI case with get_language_id() resolution, stdin/env_extra
support in run_scraper, and a full AtCoder submit implementation; stub
the remaining platforms.