Commit Graph
182 Commits
Author SHA1 Message Date
Amruth Pillai d0d20ce0fd chore(deps): upgrade dependencies
Bump workspace dependencies to their latest versions and dedupe the lockfile.

The upgrade left stale duplicates in pnpm-lock.yaml that broke the build and tests:

- @deepseek-ai/schemastery resolved to both 3.18.2 and 3.18.4. Both copies declare
  the global Schemastery namespace, so dsh-plugin's declaration emit failed with
  TS2883 on `Config`. `pnpm dedupe` collapses it to 3.18.4.
- vite's optional tsx peer resolved to 4.23.13 for importers without a direct tsx
  dependency and 4.23.15 elsewhere, producing two vitest 5.0.2 instances. Loading
  both in one run broke `expect(...).rejects`. Re-resolving tsx unifies the graph.
2026-09-28 00:23:21 +02:00
Amruth Pillai 8c40313980 feat(deploy): support Vercel Hobby alongside Docker (#3541)
* feat(deploy): support Vercel Hobby alongside Docker

* fix(deploy): include PDFKit runtime font assets

* docs(deploy): document Vercel and Docker setup

* docs(deploy): record storage persistence checks

* refactor(deploy): drop scheduled staging cleanup

Staging uploads are deleted after finalization and expired ones are swept
on each new upload, so the Vercel cron job, its route, and CRON_SECRET are
no longer needed. The Deploy with Vercel wizard now asks for two secrets.

* docs(deploy): restructure Vercel guides

Split the Vercel page into a how-to with its environment reference, move the
large RPC staging protocol to an API reference page, and move CI deployment
checks to the contributing section. Point Deploy with Vercel buttons at main.

* chore: remove agent planning records and fix web app description

Delete superpowers plans/specs, ADRs, issue plans, execution briefs, domain
context maps, and Europass research. Describe apps/web as a TanStack Router
SPA served by apps/server.

* refactor(deploy): simplify Vercel support code

- Share one Redis client and key namespace through @reactive-resume/db/redis
  for API and auth instead of a second auth-only client.
- Drop the auth seeding retry; the provider already treats concurrent inserts
  as no-ops and deployment preparation seeds before runtime.
- Detect staging support from POST /api/storage/stage (404 on Docker) instead
  of a separate GET probe.
- Read staged bodies directly; the signed upload already caps their size.
- Close per-subscription Redis connections with disconnect() alone.
- Check Blob health with one list call instead of write/read/delete.
- Remove redundant tsdown onlyBundle list, dead namespace fallbacks, and the
  conditional spread in the health status.

* fix(deploy): heal stopped runs with dead owners and keep auth up without Redis

- Run owners refresh a Redis heartbeat until they release their claim. Stop
  requests reap the run immediately when the owner has stopped heartbeating,
  instead of leaving the thread blocked until the 15-minute TTL reaper.
- Auth and oRPC rate limiters fall back to per-instance memory limits when
  Redis errors, instead of rejecting every login or failing requests.

* ci: allow esbuild build for Vercel CLI and register deployment deps with knip

pnpm 12 fails dlx installs with ignored build scripts, so allow esbuild
explicitly. The server bundle keeps @vercel/blob, ioredis, and jose external,
and api/index.mjs is the Vercel Function entry.

* fix(web): send buffered RPC bodies instead of teed streams

Reading a request clone turned the original body into a stream, which
browsers send without inspectable request data and which needs duplex
mode. Send the already buffered Blob for direct requests.

* fix(web): send direct RPC bodies as bytes

Blob request bodies are sent as data pipes, so browser tooling cannot
inspect them. Buffer the original request as an ArrayBuffer and send those
bytes; this restores the e2e save assertions that match on request data.
2026-09-26 02:37:22 +02:00
Santhi PrakashandAmruth Pillai ac69dd3f1a fix(server): verify migrated schema at startup (#3513)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-09-21 19:46:36 +02:00
Amruth Pillai 4fde62df6d chore: update dependencies 2026-09-19 22:05:55 +02:00
Amruth Pillai b953435f2c fix: audit code for reduction 2026-09-17 22:16:44 +02:00
Emanuele TonelloandAmruth Pillai fbf1f8fbac docs(api): describe cover letter endpoints (#3509)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-09-17 00:13:21 +02:00
Amruth Pillai 96c7142fbc chore: update dependencies 2026-09-16 18:36:50 +02:00
Amruth Pillai f89acb4368 chore: migrate repository links to reactive-resume/reactive-resume 2026-09-12 11:18:32 +02:00
Amruth Pillai d77cb93494 fix: complete repository links and container publishing migration 2026-09-11 11:09:55 +02:00
Amruth Pillai 9550910f17 revert: remove repository migration changes from main 2026-09-11 03:23:27 +02:00
Amruth Pillai 31d6ee6251 chore: prepare repository migration and Docker Build Cloud publishing 2026-09-11 02:55:52 +02:00
Amruth Pillai 3fc0896a34 fix(auth): honor client-requested token_endpoint_auth_method during DCR
Dynamic client registration unconditionally rewrote token_endpoint_auth_method
to "none" for every unauthenticated request, downgrading clients that asked for
client_secret_basic or client_secret_post to public clients. Those clients were
issued no client_secret but still authenticated at the token endpoint with the
method they registered, so the exchange failed with 401 invalid_client.
Connecting Composio to the MCP server hit this on every attempt.

Default to "none" only when the client omits the field, which keeps PKCE-only
MCP clients working while confidential clients receive a usable secret.
2026-09-10 12:47:52 +02:00
Amruth Pillai dc8f9787a4 chore: update dependencies 2026-09-09 23:57:37 +02:00
Amruth Pillai 607eafd3e8 chore(deps): bump ai-sdk, aws-sdk, react-email and tooling dependencies
Patch and minor bumps across the AI provider SDKs (@ai-sdk/*, ai),
@aws-sdk/client-s3, react-email/@react-email/ui, knip and jszip, with
pnpm-lock.yaml regenerated to match.

Also records the audit overrides and patched dependencies in the root
package.json alongside the existing pnpm-workspace.yaml entries.
2026-09-09 12:16:20 +02:00
Amruth Pillai 6416da28a4 feat(homepage): rebuild landing page and fix untranslated homepage strings in 16 locales (#3496) 2026-09-08 18:01:59 +02:00
Amruth Pillai 7fef84078d chore: update dependencies 2026-09-06 08:46:54 +02:00
Amruth Pillai 744eaa902e feat(sharing): serve a configured public resume at root (#3470) 2026-09-05 19:42:44 -07:00
Amruth Pillai ab67831e4b feat: add cover and contain picture fitting (#3461)
* feat: add picture fit options

* fix: harden picture fit regressions

* fix: address picture fit review findings
2026-09-05 18:33:35 -07:00
Amruth Pillai 1d4194a207 feat: manage cover letters in a shared library (#3423)
* feat: add shared cover-letter library with resume styling

* fix: retain required sanitizer dependency in CI

* fix(cover-letters): prevent concurrent AI draft requests

* chore(codacy): exclude generated migrations

* fix(applications): keep Lingui macro out of callback dependencies
2026-09-05 10:01:22 -07:00
Amruth Pillai fe9b59e111 fix: restore MCP OAuth registration and authorization (#3421)
* fix: align MCP OAuth provider schema and authorization flow

* test: isolate OpenAPI generation from OAuth initialization

* fix: accept auth routes without a callback query

* fix: require explicit OAuth consent and preserve signed requests

* test: verify OAuth audiences through real MCP initialization

* test(e2e): isolate OAuth token audience validation
2026-09-05 09:33:15 -07:00
Amruth Pillai 779ea5cb4a fix(resume): reject invalid submitted write values (#3413) 2026-09-05 08:51:55 -07:00
Amruth Pillai 0878b256a9 fix(sharing): use neutral social preview image (#3410)
* fix(sharing): use neutral social preview image

* fix(sharing): use neutral server social preview
2026-09-05 08:51:19 -07:00
Amruth Pillai cd1c597ff0 fix(server): expose build version in health endpoint (#3404)
* fix(server): expose build version in health endpoint

* fix(server): redact public health failure details
2026-09-05 08:50:17 -07:00
Amruth Pillai 7d809da6f8 feat(pdf): add opt-in German hyphenation (#3435) 2026-09-05 08:29:22 -07:00
Amruth Pillai 57fee67d2d fix(pdf): render picture borders and soft shadows (#3427)
* fix(pdf): render picture borders and soft shadows

* fix(pdf): preserve picture padding and bound shadow rendering
2026-09-05 08:29:17 -07:00
Amruth Pillai 35cecf9c91 fix(storage): support S3 buckets with object ACLs disabled (#3432) 2026-09-05 07:29:42 -07:00
Amruth Pillai 84645f122b chore: update dependencies 2026-09-04 11:05:22 +02:00
Amruth Pillai f29b92e2fb chore(copy): rewrite marketing, app, and docs copy to read less AI-generated
Rewrites the landing page, in-app microcopy, and public docs, then fixes what the rewrite exposed: stale template counts, a broken quickstart anchor, out-of-sync FAQ structured data, dead error-hint branches in the MCP tools, and wrong-sense translations across all 53 locales. Adds GLOSSARY.md so translators get the right sense of the ambiguous UI terms.
2026-08-28 22:18:29 +02:00
Amruth Pillai c288675b16 Release v5.2.9 (#3382)
* feat(ats): add ATS checker and replace resume analysis

Adds a public, browser-only ATS checker at /ats-checker and an ATS Check
section in the builder's right sidebar. PDFs are parsed locally: text
extraction, reading order, contact and date recovery, section detection,
and file-level readability are scored deterministically, with evidence
cited per finding and skipped checks reported rather than counted as
passes.

Removes the AI-scored resume analysis it supersedes: the resume_analysis
table (dropped via migration), the get_resume_analysis MCP tool, and
POST /ai/analyze-resume. The replacement, POST /ai/ats-review, reviews
extracted resume text and returns qualitative feedback with no score.

Also bumps the version to 5.2.9 and adds the changelog entry.

* chore(deps): bump workspace dependencies

* fix(ats-checker): keep negation inside each 'what this does not do' bullet

The three bullets were bare fragments whose negation came from the
section heading, which translators never see. A dozen locales rendered
them as affirmative assertions or imperatives, so the page claimed the
checker enforces a one-page rule and predicts rejection -- the opposite
of the source, and directly contradicted by the sentence beside it.

Each bullet now carries its own negation, so the polarity cannot be
lost in translation. Re-extracted and refilled across all 53 target
locales.
2026-08-27 03:37:01 +02:00
Amruth Pillai 3c195dc3f8 Release v5.2.8 (#3375)
Upgrades to Better Auth 1.7, expands Custom Styles coverage of item headers, and adds a human-approval step to the AI agent's resume edits.

Breaking for self-hosters using a custom OAuth provider: the callback path changes from /api/auth/oauth2/callback/custom to /api/auth/callback/custom, and installs using OAUTH_DISCOVERY_URL need one additional UPDATE after upgrading. Both are documented in docs/self-hosting/sso.mdx.

- Better Auth 1.7, with the account issuer migration and the jwks alg/crv columns the 1.7 jwt plugin requires
- Agent edits gated behind an approval step, with crash-safe runs and context pruning
- item-header now covers every section header row on every template; adds the item-header-row part
- Fixes provider unlinking, auth error messages, and version conflicts on freshly created resumes
- New /auth/error page, translated across all 53 target locales
- DeepSeek Harness plugin moved into packages/dsh-plugin
- Dependency bumps across the workspace
2026-08-24 21:44:16 +02:00
Amruth Pillai c8081ac2fe feat(agent): adopt AI SDK v7 — crash safety, context pruning, HITL approvals (#3362)
* docs(adr): propose agent AI SDK v7 adoption plan

* fix(ai): bind analyzeResume through aiService in service test

The test destructured analyzeResume as a named export that does not exist; main was red.

* test(agent): keep pure ai helpers real via spread-actual mock factory

* feat(agent): add run guards, patch version guard, run wall-clock timeout

* feat(agent): validate UI messages at the send boundary

* feat(agent): crash-safe draft-row persistence and server-side cancellation

* feat(agent): reap stale run claims at boot, on send, and on thread open

* feat(agent): fresh-document patch output and tiered context pruning

* feat(ai): shared agent tool contracts and message metadata schema

* feat(agent): add per-thread review-patches setting with update endpoint

* feat(agent): gate resume patches behind hmac-signed tool approval

* feat(agent): merge question answers and approval decisions before run claim

* feat(agent): approval ui with composed auto-send and fixture-driven tests

* feat(agent): usage metadata, tool activity cards, smoother streaming

* feat(agent): tool-call repair, input examples, structured step logging

* chore(i18n): translate new agent workspace strings across all locales

* fix(agent): gate stale-run draft cancellation on winning the claim clear

Snapshot streaming drafts before the conditional clear and skip the flip entirely when another reaper or a replacement run already cleared the claim. Also address review nits in eleven locale catalogs.

* fix(agent): flip reaped drafts only when their snapshotted state is unchanged

* fix(agent): address review findings across run lifecycle, context budget, and approval flow

- bind patches to the revision the model read via signed baseUpdatedAt
- claim the run before consuming a continuation; recorded-but-unexecuted approvals retry as pending continuations
- keep run ownership on stop() until cancellation persists; preserve the claim for the reaper when final persistence fails
- estimate tokens without serializing binary attachments (tokenx) and enforce the budget by dropping oldest whole turns
- mark crash-recovered patch results as snapshot boundaries; strip /data prefixes at execution time
- retry failed continuations without regenerate; mount a single AgentChat; disable response controls on read-only threads; freeze review toggle during runs (client+server)
- accumulate usage across continuations and match the SDK's nested usage shape; label-form token strings; reorderable source label; accessible note field; state-neutral web-search label

* chore(i18n): translate revised agent strings across all locales

* fix(agent): harden baseUpdatedAt validation and address review follow-ups

- bundle tokenx in the server runtime dependencies (e2e boot failure)
- strict ISO schema for baseUpdatedAt plus loud executor rejection of unparseable values
- it-IT source label consistency (Fonte)
- prove penultimate-turn retention in the context pruning test

* chore(deps): exempt tokenx from knip for the externalized server bundle
2026-08-20 08:06:53 +02:00
Amruth Pillai 9dc2aade46 chore(deps): update dependencies
Routine version bumps across the workspace. The @react-pdf/textkit patch is
renamed to drop the pinned version so it survives the next bump.
2026-08-17 22:32:33 +02:00
Amruth Pillai bfdd29f941 test(server): generate the OpenAPI spec once per suite
Each case rebuilt the whole spec, which walks every router and resume JSON
schema. The first case already carried a raised 15s timeout and still timed out
on a loaded machine. The spec is deterministic and only read here, so build it
once: the file drops from over 15s to 1.86s.
2026-08-17 22:19:52 +02:00
Amruth Pillai 9d0dc36706 feat(seo): render social card metadata for public resumes
Public resume pages only produced their OpenGraph and Twitter tags client side,
so a shared link had no card at all. The server now injects them into the shell
and swaps in the resume's own title and description.

The lookup is scoped to public, password-free resumes and deliberately avoids
resumeService.getBySlug: that counts a view and would expose a protected
resume's summary to an unauthenticated crawler. User-authored values are escaped
before they reach the HTML, and any lookup failure falls back to the plain shell.

getResumeSocialMeta is shared with the client route head so the two cannot drift.
2026-08-17 22:19:52 +02:00
Amruth Pillai d0fa9ae8da fix(seo): shorten the meta description for mobile search results
The 131 character description overflowed the three line snippet Google renders
on mobile. The replacement is 114 characters and keeps the same claims.
2026-08-17 22:19:52 +02:00
Amruth Pillai 36c35c9bd5 fix(seo): serve the root request through the web app handler
The static middleware was mounted ahead of the web app fallback, and Hono's
serveStatic resolves "/" to the directory and returns dist/index.html verbatim.
handleWebApp never ran for the root route, so the OpenGraph, Twitter, canonical
and JSON-LD markup it injects was missing in production - fetching
https://rxresu.me/ as Twitterbot returned zero og: tags.

Route "/" explicitly before the static middleware so the injection runs.
2026-08-17 22:19:52 +02:00
Amruth Pillai 9509b5bc2e refactor(stylesheet): move Semantic CSS to the browser (#3329) 2026-08-16 16:50:27 +02:00
ignaciocarreandAmruth Pillai 6d9ebccc63 feat(mcp): add cover-letter PDF downloads (#3304)
* feat(mcp): add cover-letter PDF downloads

* fix(mcp): bind signed PDF targets

* test(mcp): cover unavailable cover letters

* fix(api): accept legacy PDF download targets

* fix(server): limit legacy PDF tokens to resumes

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 23:09:56 +02:00
Santhi PrakashandAmruth Pillai 7eb6d3bdbf fix(auth): use loopback URL for MCP OAuth JWKS verification (#3297)
* fix(auth): use loopback URL for MCP OAuth JWKS verification

Fetch the JWKS endpoint over the internal loopback address instead of the public APP_URL, so token verification works under Docker port-mapping, reverse proxies, and other deployments where the public URL does not loop back to the Node process.

Also log the specific MCP OAuth verification error instead of swallowing it with a bare catch.

Fixes #3077

* fix(auth): normalize internal JWKS URL and throttle MCP OAuth warnings

 - Problem: default loopback JWKS URL used PORT in dev where the server
   listens on SERVER_PORT (3001), and trailing-slash overrides produced
   //api/auth/jwks; unthrottled warn logs could flood on bad bearer tokens.
 - Fix: resolveInternalBaseUrl trims/normalizes BETTER_AUTH_INTERNAL_URL,
   mirrors apps/server listen-port selection, and MCP OAuth warnings are
   throttled to once per minute.
 - Verification: pnpm exec biome check on changed files; pnpm typecheck.

* fix(auth): declare BETTER_AUTH_INTERNAL_URL in turbo globalEnv

- Problem: Turborepo strict env mode strips undeclared BETTER_AUTH_INTERNAL_URL under pnpm dev, so the JWKS override silently falls back to loopback.
- Fix: add BETTER_AUTH_INTERNAL_URL to turbo.json globalEnv (required for any new env var per CLAUDE.md).
- Verification: python3 JSON parse of turbo.json; confirmed var was absent from globalEnv before this change.

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 22:51:02 +02:00
Amruth Pillai 035d94183b fix(web): show static template previews in gallery (#3302) 2026-08-10 12:18:47 +02:00
Amruth Pillai efd950bd93 fix(server): log unhandled rejections instead of crashing the process
Node 24 terminates the whole process on an unhandled promise rejection, so a
single request's stray rejection could take the server down for every user
(as the USER_STOPPED agent-abort bug did). Add a process-level unhandledRejection
handler that logs and keeps serving. Uncaught exceptions are intentionally left
on Node's default crash-and-restart, since process state is unsafe afterward.

Claude-Session: https://claude.ai/code/session_01ULhhLQ24DvnYwzP4afDuye
2026-08-09 16:01:23 +02:00
Amruth Pillai e6a31aab97 fix(stylesheet): warm and reuse the server PDF preflight worker
The server PDF preflight spawned a fresh worker per semantic-CSS edit, each
racing a 15s startup deadline to cold-load the ~721kB+5MB PDF runtime. That
load is super-linear in CPU (~3s at 1 vCPU, >15s on a throttled/shared vCPU),
so on a constrained box every edit hit the startup-timeout path and returned
STYLESHEET_PREFLIGHT_WORKER_FAILED. Since the service only advances the applied
stylesheet when preflight passes, custom styles never applied and the editor
stuck on Checking.

Warm one worker at boot and reuse it (message-based input, respawn on
crash/timeout), so the cold load is paid once instead of per edit. Raise the
render deadline 5s->30s (a rich resume renders ~5-18s on a slow box) and the
readiness ceiling to 120s so the one-time warm completes even when throttled.
Surface worker load failures instead of an unhandled-rejection crash, and log
runner-side failure paths so the previously opaque failure is diagnosable.
Verified in node:24-slim under --cpus=0.25/0.35/0.5: all reused requests pass.

Claude-Session: https://claude.ai/code/session_01ULhhLQ24DvnYwzP4afDuye
2026-08-09 14:30:55 +02:00
Amruth Pillai 88a19619da chore: update dependencies 2026-08-09 12:17:09 +02:00
Amruth Pillai 9eec1520a1 Merge branch 'main' of github.com:amruthpillai/reactive-resume 2026-07-31 17:24:40 +02:00
Amruth Pillai 131c1492cd chore: update dependencies 2026-07-31 17:24:32 +02:00
Amruth PillaiandCursor Agent ba8e1be2ab fix(stylesheet): harden PDF preflight and surface worker failures (#3284)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-31 07:57:54 +02:00
Amruth Pillai b071a118a3 test: remove slow OpenAPI spec synchronization test 2026-07-30 16:37:03 +02:00
Amruth Pillai 164a279306 chore: update dependencies 2026-07-30 12:45:49 +02:00
Amruth PillaiandCursor Agent d2ffbf9618 feat: add semantic CSS stylesheets (#3274)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-30 12:39:15 +02:00
Amruth Pillai 50f50b2672 fix(server): use public URL for homepage metadata 2026-07-28 08:56:43 +02:00