Windows implements fsync as FlushFileBuffers, which behaves differently
from POSIX in two ways the raw trace spool relied on.
Flushing a directory handle is rejected outright with EPERM. syncDirectory
only tolerated EINVAL, ENOTSUP and EISDIR, so the durability barrier that
follows every rename threw instead of being skipped.
Flushing a file handle requires that handle to carry write access, and both
file flush sites opened their target read-only, so they also failed with
EPERM.
Together these made every durable write fail on Windows: with
observability.requestLogs enabled the sync handler answered 503
spool_unavailable for every bundle and nothing was ever persisted.
Tolerate EPERM alongside the other directory-flush rejections, and reopen
files as "r+" so their flush stays a real barrier rather than a tolerated
error. The tolerated set is extracted into a predicate so a test can pin
down which codes are skipped and, just as importantly, which ones still
propagate.
Fixes#1635
Address review on #1634:
- keep GrokRefreshAuthError module-private, consistent with the updated
Kimi implementation, avoiding new public API surface;
- dedupe concurrent resolveGrokAuth calls with a per-credential in-flight
refresh map keyed by sourceFile + authRecordKey, mirroring
kimiRefreshInFlight, so parallel requests share one refresh (and its
peer-rotation adoption outcome) instead of racing the same stale
refresh token;
- add a test proving concurrent refreshes coalesce into a single token
request.
Address review feedback: keep KimiRefreshAuthError internal to the
module, and require an access token on the adopted credential so a
partially written file cannot mask the real refresh failure.
Address review feedback: in a multi-account auth.json, re-reading all
records could adopt a different account just because its refresh token
differs. Restrict adoption to the same sourceFile + authRecordKey that
was being refreshed, and require an access token on the adopted record
so a partially written file cannot mask the real failure.
The Grok refresh token rotates on every refresh. CCR reads and writes
the same ~/.grok/auth.json as the Grok CLI, so when the CLI (or any
other consumer of the file) refreshes first, the refresh token CCR
holds is invalidated and the refresh fails with 401/403. Previously the
error propagated to the caller even though the file on disk already
held a valid, newer credential.
On 401/403 from the refresh endpoint, re-read the credential records
once and, when the stored refresh token differs from the rejected one,
adopt the on-disk credential instead of failing the request. Other
errors propagate unchanged, and a 401 without a peer rotation still
surfaces as before. Same treatment as the Kimi provider fix.
The Kimi refresh token rotates on every refresh. CCR reads and writes
the same ~/.kimi/credentials file as the Kimi CLI, so when the CLI (or
any other consumer of the file) refreshes first, the refresh token CCR
holds is invalidated and the refresh fails with 401/403. Previously the
error propagated to the caller even though the file on disk already
held a valid, newer credential.
On 401/403 from the refresh endpoint, re-read the credential file once
and, when the stored refresh token differs from the rejected one, adopt
the on-disk credential instead of failing the request. Other errors
propagate unchanged, and a 401 without a peer rotation still surfaces
as before.