Skip to content

v0.8.12 — OAuth compute & test-isolation fixes

Choose a tag to compare

@yuens1002 yuens1002 released this 07 Aug 01:15
cff9e2c

Added

Changed

  • manage_receipts: tool description now explicitly directs Claude.ai (mobile/web) clients to never pass fileContent, explains why no client-side upload workaround exists (raw file bytes are unreachable; a signed-URL upload path was tried and removed because Claude.ai's sandboxed runtime blocks outbound HTTP requests — see PR #28), and clarifies that hasFile:false from a structured-only attach is the correct, complete outcome, not a fallback. docs/TOOLS.md regenerated (also picked up pre-existing drift from PR #30's add_transaction/import_transactions describe clarifications, which had never been regenerated).

Fixed

  • test/setup.ts: integration tests no longer run against whatever BOOKIE_DB_URL happens to point at. They now require BOOKIE_TEST_DB_URL (a separate Neon branch) and refuse to start if it is unset or resolves to the same host+database as BOOKIE_DB_URL. Cleanup only runs in afterAll, so an interrupted run against a real ledger left rows behind — and the comments in test/setup.ts, test/oauth.test.ts, test/receipts.test.ts and test/reconcile.test.ts all claimed the suite ran against a dev branch when it followed .env. The same-database guard normalizes Neon's pooled/direct hostname pair, which address one database — comparing raw hosts would have let the live ledger through as a "test" target (caught by Copilot review on PR #54). test/db-target.test.ts asserts the substitution actually took effect and covers the comparison directly, so a refactor that drops either fails loudly instead of silently writing to the live ledger. .env.example and CONTRIBUTING.md document the new variable.
  • src/lib/oauth.ts: removed the 60-second setInterval that purged expired oauth_tokens rows. Querying the database every minute meant the serverless compute never accumulated the 5 idle minutes it needs to scale to zero, so it ran continuously and consumed roughly 94% of the monthly compute allowance while completely idle. Expired refresh tokens are now purged opportunistically when a new one is issued (the only event that adds rows), and expired in-memory auth codes are swept in issueAuthCode instead of on a clock. The purge is awaited but error-swallowed so housekeeping can never fail an auth exchange. rotateRefreshToken now delegates token creation to issueRefreshToken rather than duplicating it inline.
  • src/transports/http.ts: /.well-known/oauth-authorization-server advertised token_endpoint_auth_methods_supported: ["none"] while /token rejects any exchange without a matching client_secret — a spec-compliant client that trusted the metadata would send no credentials and fail with invalid_client. Now correctly advertises ["client_secret_post"] (RFC 6749 §2.3.1). The contradiction was latent because the deployed secret sat under a pre-0.6.3 variable name (OAUTH_AUTH_SECRET), so the enforcement branch never executed.
  • manage_receipts: gracefully degrade (save structured data, return hasFile:false + a fileWarning) instead of hard-failing when fileContent is provided but Railway Bucket isn't configured — matches the mobile client flow (Claude.ai vision-extracts fields, can't base64-encode raw file bytes).
  • server.json: version drift — the /release skill's own version bump (0.8.4 → 0.8.5) never propagated to server.json's version/package-entry version, caught by test/server-json.test.ts failing on main immediately after the v0.8.5 release merged.