fix(submit): harden atcoder and codeforces submit flow (#304)

## Problem

AtCoder file upload always wrote a `.cpp` temp file regardless of
language. CF submit used `solve_cloudflare=True` on the submit page,
causing a spurious "No Cloudflare challenge found" error;
`_wait_for_gate_reload` in `login_action` was dead code. Stale cookies
caused silent auth failures with no recovery path. The `uv.spawn` ndjson
path for submit had no overall timeout.

## Solution

Replace AtCoder's temp file with `page.set_input_files` using an
in-memory buffer and correct extension via `_LANGUAGE_ID_EXTENSION`.
Replace CF's temp-file/fallback dance with a direct
`textarea[name="source"]` fill and set `solve_cloudflare=False` on the
submit fetch. Add a login fast-path that skips the homepage check when
cookies exist, with automatic stale-cookie recovery via `_retried` flag
on redirect-to-login detection. Remove `_wait_for_gate_reload`. Fix
`_ensure_browser` to propagate install errors. Add a 120s kill timer to
the ndjson `uv.spawn` submit path in `scraper.lua`.
This commit is contained in:
Barrett Ruth 2026-03-05 11:18:34 -05:00 committed by GitHub
parent 6fcb5d1bbc
commit 127089c57f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 122 additions and 96 deletions

View file

@ -85,6 +85,7 @@ local function run_scraper(platform, subcommand, args, opts)
local stderr = uv.new_pipe(false)
local buf = ''
local timer = nil
local handle
handle = uv.spawn(cmd[1], {
args = vim.list_slice(cmd, 2),
@ -92,6 +93,10 @@ local function run_scraper(platform, subcommand, args, opts)
env = spawn_env_list(env),
cwd = plugin_path,
}, function(code, signal)
if timer and not timer:is_closing() then
timer:stop()
timer:close()
end
if buf ~= '' and opts.on_event then
local ok_tail, ev_tail = pcall(vim.json.decode, buf)
if ok_tail then
@ -124,6 +129,30 @@ local function run_scraper(platform, subcommand, args, opts)
return { success = false, error = 'spawn failed' }
end
if subcommand == 'submit' then
timer = uv.new_timer()
timer:start(120000, 0, function()
timer:stop()
timer:close()
if stdin_pipe and not stdin_pipe:is_closing() then
stdin_pipe:close()
end
if not stdout:is_closing() then
stdout:close()
end
if not stderr:is_closing() then
stderr:close()
end
if handle and not handle:is_closing() then
handle:kill(15)
handle:close()
end
if opts.on_exit then
opts.on_exit({ success = false, error = 'submit timed out' })
end
end)
end
if stdin_pipe then
uv.write(stdin_pipe, opts.stdin, function()
uv.shutdown(stdin_pipe, function()