Skip to content

feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.23.0) - #63

Open
thedogwiththedataonit wants to merge 44 commits into
claude/migrate-resendfrom
claude/init-providers
Open

thedogwiththedataonit wants to merge 44 commits into
claude/migrate-resendfrom
claude/init-providers

Conversation

@thedogwiththedataonit

@thedogwiththedataonit thedogwiththedataonit commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

11-01 status (current). Head 1baeb78 (11-01 (c): restacked on #61 52f18fc, plus Greptile fixes on e65b7d6). Spec: openapi/public-api-v1.yaml is the brew-v2 #2169 head 6e4accea4c byte for byte (sha256 b86fb228250266ea57637a3d5c1e722e05e289a9d59b0c9b349bcc1243178bd3), the same file at the final #2169 head aedf27704d (later commits: a main merge and docs only; no spec change), vendored by #54: main's spec (brew-v2 373e900a9a, unchanged on main since) plus the DX delta; types regenerated. The 6e4accea4c delta over 02a3946b18 is description text only (the AppEmailPutRequest.publish field, PUT /v1/app-emails/{triggerKey}, POST /v1/app-emails/kit and PUT /v1/integrations/{provider}/connection): an app email unpublished in Brew turns back on when it is published in Brew or when its setup is repeated with the same starter or source plus publish: true; publish: true alone rebuilds a starter-kit key from its starter and is 400 INVALID_REQUEST on any other key. Version 0.23.0 (not released). Local checks on this head: tsc, lint, test 101 files / 1567 tests, docs:commands:check, generate:skills:check, sync:launchers:check; SDK from the local typescript-sdk#74 build (build, bin, signal and regression smokes and smoke:launchers last run on 854f4f8). 854f4f8 fixes Greptile's earlier P1 (a brew.json save that fails puts the earlier hook secret back in .env.local) and P2 (provider setup reads every app email page until the keys it needs are found). Greptile's P2 on e65b7d6 is fixed in c72830b (the --app-url brand lookup reads every page until a match or the list's end, a repeated cursor ending it, so a matching brand past the first 1,000 is found instead of re-created into BRAND_DOMAIN_CONFLICT). Its follow-ups are fixed in ba40e0d and 1baeb78: .env.local's app key holds the domain, kit and provider setup unless it is on this run's server and bound to the brand the run sets up, whether --app-url switches brands or the run's key already has the app's brand (a staging copy with the same brand ids no longer passes). Each fix has a test that failed first. Greptile on 1baeb78: 5/5, no open threads. Older notes below that name 6f8ef50544, 5084a5202f, b884e78157 or 02a3946b18 as the spec, or other version numbers, are history.

11-01 Greptile fixes: 94159d9: a printed Supabase re-run (rerun-supabase-connect, configure-supabase-hook) keeps the project --supabase-project picked, so it never detects and changes another project's hook. 956fbcb: with an organization key, a failed brand list is no longer read as a website mismatch on the key's brand; the brand step fails (exit 1) and the domain, kit and provider steps wait. This CLI needs SDK 13.7.0 (^13.7.0); generated app code still asks for ^13.6.0.

Merge/publish order (nothing here is merged, published or tagged):

  1. brew-v2 #2169 merged and deployed, and the live spec (https://brew.new/openapi/public-api-v1.yaml) matches the vendored file.
  2. typescript-sdk publishes in order: init + the brew.new / create-brew launchers (C4, 0.21.0) #55 as 13.5.0, Release CLI 0.17.0 with registry SDK 13 #64 as 13.6.0, Spec drift: live API differs from the vendored spec #74 as 13.7.0 (each retargets to main once the one below merges).
  3. brew-cli bumps the SDK: bun add @brew.new/sdk@^13.6.0 for 0.20.0 to 0.22.0 (send by key, app-emails, trigger keys (C2, 0.20.0) #54, init + the brew.new / create-brew launchers (C4, 0.21.0) #55, migrate resend (C5, 0.22.0): move a Resend account and codebase to Brew #61; Project foundations + mcp install (C1) #44 and Login through the browser (session flow) (C3) #53 ship inside 0.20.0) and ^13.7.0 for 0.23.0 (feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.23.0) #63), each regenerating bun.lock from the registry, then a frozen install and the full checks.
  4. brew-cli publishes 0.20.0, 0.21.0, 0.22.0 and 0.23.0, in that order.

Renumbered 10-31: main published 0.19.0 (--input refuses unknown keys, SDK ^13.3.0) and SDK main published 13.4.0 (email lang), so this chain moves up one again: #54 0.20.0, #55 0.21.0, #61 0.22.0, #63 0.23.0. The SDK DX releases stay send-by-key 13.5.0, adapters/appEmails 13.6.0 (SDK_RANGE ^13.6.0), integrations.connect 13.7.0. Rebased on main 1f12ba4; the vendored spec was then the brew-v2 #2169 @ 6f8ef50544 DX delta merged structurally into main's spec (brew-v2 373e900a9a). This PR is 0.23.0; older numbers below are pre-rebase.

Update 2026-10-25: restacked on #61. Conflicts: CHANGELOG.md, generated docs. The hosted auth hook SDK gate is 13.7.0 (after 13.6.0); generated app code asks for ^13.6.0; doctor --project tests use ^13.3.0 as the too-old range. Checks against a local SDK build of typescript-sdk#74 merged with SDK main: tsc, lint, test 1135, generated types fresh, docs:commands:check, generate:skills:check, sync:launchers:check, build, bin smoke, launcher pack smoke, signal smoke ✓.

Renumbered 10-25: main published 0.18.0 (--lang/--clear-lang, contact imports, SDK ^13.3.0), and SDK main published 13.1.0, 13.2.0 and 13.3.0 without the DX features (13.4.0 is reserved for its pending email lang release). So this chain moves up one: #54 0.19.0, #55 0.20.0, #61 0.21.0, #63 0.22.0, and the DX SDK releases are send-by-key 13.5.0, adapters/appEmails 13.6.0 (SDK_RANGE ^13.6.0, so installs can no longer resolve to the published 13.3.0), integrations.connect 13.7.0. This PR is 0.22.0; older numbers below are pre-rebase.

Spec sync 2026-10-21: restacked on #61; the #2068/#2146 spec commit folded into #54's vendored brew-v2 #2169 @ 6f8ef50544 spec, and integrations connect|connection empty the parity pending list. Adapted to #2169's idempotent PUT /v1/integrations/{provider}/connection (README, skill, CHANGELOG: a re-run keeps connectionId and webhookUrl, replaces the secret in place, finishes failed auth emails); init --domain with DKIM_SELECTOR_IN_USE offers neither the DNS records step nor Connect automatically. RELEASING: brew-v2 #2169 deployed. Checks: tsc, lint, test 1102, generated types fresh, docs/skills/launchers freshness, build, launcher + signal smokes ✓.

Renumbered 10-21: main published 0.17.0 (canvas commands, SDK ^13.0.0), so this chain moved up one: #54 0.18.0, #55 0.19.0, #61 0.20.0, #63 0.21.0. This PR is 0.21.0; older numbers below are pre-rebase.

Stacked on #61 (head d3eef9f; restacked 11-01); needs brew-v2 #2169 deployed and SDK 13.7.0, after #61's gate.

Chain: #44 → #53 → #54 → #55 (init) → #61 (migrate resend) → this PR (C6, 0.23.0). Base: claude/migrate-resend.

Summary

The CLI pieces init was still missing (plan C4 "provider config", brand from --app-url, the domain link, doctor --project, integrations connect).

  • integrations connect <supabase_auth|clerk> binds the SDK's integrations.connect (typescript-sdk#74, 13.7.0, not released). Destructive class. The signing secret comes from --signing-secret-env <VAR> or --signing-secret-stdin, never from argv. It is never echoed, including when it is pasted where the variable name goes. Prints {connectionId, webhookUrl, status, authEmails}. integrations connection <provider> binds integrations.getConnection.

  • init --configure-supabase (needs SUPABASE_ACCESS_TOKEN locally; the token goes to api.supabase.com only):

    1. saves the current Send Email hook settings to brew.json#providerConfig before changing anything (a previous hook secret goes to .env.local, never brew.json);
    2. connects Brew with a freshly generated v1,whsec_ secret;
    3. PATCHes /v1/projects/{ref}/config/auth with hook_send_email_enabled, hook_send_email_uri and hook_send_email_secrets.

    --dry-run --json shows the exact PATCH with the secret replaced. A re-run makes no change.

  • init --configure-clerk (needs CLERK_SECRET_KEY in the shell or .env.local) runs only once integrations connection clerk is active. It then turns off delivery through POST /templates/email/{slug}/toggle_delivery for exactly the CLERK_EMAIL_SLUG_ROUTES slugs whose key has a published app email. Before the connection is active it refuses and makes no Clerk call. A bare --yes never touches Clerk or Supabase.

  • init --rollback-provider restores what either flag changed, using what brew.json saved.

  • Without those flags, init prints the manual dashboard steps. The self-hosted Edge Function and its local config.toml patch become the optional alternative.

  • --app-url: with an organization key, init reuses the brand whose website matches, or creates one through POST /v1/brands (idempotent) and waits for it to be ready. A 403 (ORG_SCOPE_REQUIRED) gets a clear next step. A key bound to one brand is never moved; a mismatch is only reported.

  • --domain: also returns the domain's page in Brew (domain.connectUrl). The report says plainly that Connect automatically (Entri) runs in the app, because v1 has no API for it, and that the sandbox applies until the domain verifies.

  • doctor --project [--json] checks:

    • managed file hashes;
    • BREW_API_KEY is present, git-ignored, and never under NEXT_PUBLIC_, VITE_, PUBLIC_ or EXPO_PUBLIC_;
    • the SDK is declared and installed at a version that satisfies SDK_RANGE (^13.6.0);
    • the key's brand, and every brew.json key exists and is published (GET /v1/app-emails);
    • the domain status and the hook connection status.

    Exit codes: 0, 1 if any check fails, 2 if there is no brew.json.

  • Spec: the final brew-v2 #2169 head 6e4accea4c whole, from send by key, app-emails, trigger keys (C2, 0.20.0) #54 (sha256 b86fb228250266ea57637a3d5c1e722e05e289a9d59b0c9b349bcc1243178bd3). History (superseded 10-21 by brew-v2 #2169 @ 6f8ef50544): migrate resend (C5, 0.22.0): move a Resend account and codebase to Brew #61's spec (brew-v2 #2145 b2912ad9f7: #2066 0fed90fbf3 merged with #2064 9f97aea3f8, plus #2145's source modes) plus exactly the deltas from #2068 (12a02af5e3 over 0fed90fbf3) and #2146 (c3da555e60 over 12a02af5e3). Re-checked on the restack: byte-identical to a three-way merge of #2145's and #2146's files over #2066's. Types are regenerated.

  • The init report stays schema v1 with additive fields: providers[], a provider step, brand.domain / matchesAppUrl / created, and domain.connectUrl.

  • vendorFetch now also redacts secrets carried in the request body. The init test helpers move to tests/helpers/init-api.ts, which keeps the test files from growing past 1,000 lines.

External APIs (verified against the official references; cited in code comments):

SDK and publish gate

This uses the SDK methods, not raw HTTP. Local validation ran against the typescript-sdk#74 build (b4508d8, on #64 3c3827f). package.json keeps main's published SDK range, so CI typecheck and parity stay red until SDK 13.7.0 is on npm. RELEASING.md has a gate for this: publish SDK 13.7.0, run bun add @brew.new/sdk@^13.7.0, then a frozen install and the full checks. brew-v2 #2169 must also be deployed first, and the live openapi.yaml compared with the bundled one. The restack kept both RELEASING.md sections (#61's API gate, then this one). Generated app code still asks for ^13.6.0.

Test plan

  • bun run tsc, bun run lint
  • bun run test: 100 files, 1495 tests (head 956fbcb, 11-01). New suites: integrations-connect, init-providers, init-app-url, doctor-project, lib/init/providers. Both parity sentinels pass.
  • docs:commands:check, generate:skills:check, sync:launchers:check, generate:types freshness
  • Secret canaries: the Supabase token, the Clerk secret key, the generated and previous hook secrets and the signing secrets never appear in stdout, stderr or brew.json, and no Brew request carries a provider token.
  • Dry-run output (exact requests, nothing written). Rollback for Supabase (with and without a previous secret) and for Clerk. Refusal under a bare --yes. Clerk refusal while the connection is pending. 403 from POST /v1/brands. 404 for a provider that is not connected. Supabase vendor errors are redacted.
  • Live: a Supabase test project and a Clerk test instance against brew-v2 #2169 (after deploy)
  • CI is green once SDK 13.7.0 is published

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

RetriggerView in GreptileConfidence Score: 5/5

The reviewed changes appear safe to merge under the documented SDK and deployment gates.

Summary

Adds hosted Supabase and Clerk connections, provider setup and rollback, brand selection from --app-url, and doctor --project.

  • The CLI connects Supabase and Clerk auth emails to Brew.
  • Init matches app URLs to brands and sets up auth-provider email routing.
  • The doctor command checks a project against its Brew setup.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Resolve brand from app URL] --> B{Saved app key differs from run key?}
  B -->|No| E[Continue when brand is ready]
  B -->|Yes| C{Server and brand match?}
  C -->|Yes| E
  C -->|No| D[Ask user to replace app key]
  D --> F[Hold domain, kit, patches and provider setup]
Loading

Reviews (45) · Last reviewed commit: "fix(init): check the app key's server an..." · Reviewed by Greptile

Comment thread src/commands/integrations/connect.ts Resolved
Comment thread src/lib/init/pipeline.ts Resolved
Comment thread src/lib/init/providers/step.ts Resolved
Comment thread src/lib/init/providers/step.ts Resolved
Comment thread src/lib/init/brand.ts Resolved
Comment thread src/lib/init/pipeline.ts Resolved
Comment thread src/lib/init/providers/step.ts Resolved
Comment thread src/lib/init/providers/step.ts Resolved
@thedogwiththedataonit thedogwiththedataonit changed the title feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.20.0) feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.21.0) Oct 20, 2026
Comment thread src/lib/init/providers/rollback.ts Resolved
Comment thread src/lib/init/providers/step.ts Resolved
thedogwiththedataonit added a commit that referenced this pull request Oct 21, 2026
The DX stack is one platform PR now, so the bundled spec is its whole
openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route)
instead of main plus per-PR deltas. Types regenerated.

It also carries the hosted auth hook connection routes, which
`integrations connect` / `integrations connection` bind in #63: they sit
in the spec parity test's pending-build list until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/lib/init/providers/rollback.ts Resolved
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
The DX stack is one platform PR now, so the bundled spec is its whole
openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route)
instead of main plus per-PR deltas. Types regenerated.

It also carries the hosted auth hook connection routes, which
`integrations connect` / `integrations connection` bind in #63: they sit
in the spec parity test's pending-build list until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedogwiththedataonit thedogwiththedataonit changed the title feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.21.0) feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.22.0) Oct 25, 2026
Comment thread src/lib/init/report.ts Resolved
thedogwiththedataonit added a commit that referenced this pull request Oct 31, 2026
The DX stack is one platform PR now, so the bundled spec is its whole
openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route)
instead of main plus per-PR deltas. Types regenerated.

It also carries the hosted auth hook connection routes, which
`integrations connect` / `integrations connection` bind in #63: they sit
in the spec parity test's pending-build list until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedogwiththedataonit thedogwiththedataonit changed the title feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.22.0) feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.23.0) Oct 31, 2026
Comment thread src/lib/init/providers/step.ts Resolved
Comment thread src/lib/doctor/project.ts Resolved
Comment thread src/lib/integrations/hosted-auth.ts Resolved
thedogwiththedataonit added a commit that referenced this pull request Nov 1, 2026
The DX stack is one platform PR now, so the bundled spec is its whole
openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route)
instead of main plus per-PR deltas. Types regenerated.

It also carries the hosted auth hook connection routes, which
`integrations connect` / `integrations connection` bind in #63: they sit
in the spec parity test's pending-build list until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit and others added 30 commits November 1, 2026 06:49
#55 now sets up nothing that routes the auth library's emails without the
auth set. The provider step does the same: with --sets lifecycle, Supabase
and Clerk keep their auth emails (no hook or delivery steps), and
--configure-supabase or --configure-clerk exits 2 before any request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#55's walk over init's value flags now covers the provider flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When --app-url picks a brand the app's key in .env.local is not bound to,
the client still acts as the old brand: --domain no longer lists or adds
a domain there. The step is skipped until a re-run with the right key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… checks readiness

- --app-url asks to replace the app key in .env.local only when that key
  is bound to another brand than the one picked (GET /v1/brand with the
  app key), not whenever the organization key had to switch.
- doctor --project reads each key's readiness: a published key that
  cannot send (DOMAIN_REQUIRED, AUTOMATION_MISSING) fails with its
  blockers, and a draft over a live version no longer reads as unpublished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xposure

doctor --project now checks the process environment as well as the .env
files: a NEXT_PUBLIC_/VITE_/PUBLIC_/EXPO_PUBLIC_ name holding a Brew key
there ships with the build too. Values are never printed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like app-emails kit: the full response still prints, and the exit code
tells a script the provider's auth emails are not all ready.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When `init --app-url` cannot read the key's brand (a 503 after retries),
the brand step failed but brandReady stayed true, so the domain, kit and
provider steps ran on the unswitched client: an organization key acting
as brand A could add --domain and set up the kit there for an app that
belongs to brand B. A failed lookup now sets brandReady to false, so
those steps are skipped until a re-run (exit 1 for the failed step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
patchesHeld held a reviewed patch only when the files or kit step
failed. A kit skipped because the brand is not ready (a failed lookup,
a brand still extracting, an app key bound to another brand) let a
pinned auth patch apply, wiring auth emails to kit keys that do not
exist yet. Patches are now held while brandReady is false too; they stay
pending with their apply next step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--rollback-provider reported the state step done and exited 0 even when
writer.writeFile refused brew.json (a symbolic link out of the project),
so the restored provider's record stayed and a later run could repeat
the rollback. A refused write is now a failed state step naming why
(exit 1), as the .env.local step already reports one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A connect re-run leaves an auth email someone unpublished in Brew off,
and a sandbox sender reaches the team only, yet the human output called
every key that set up "ready". It now says "set up" and points at
app-emails list for which ones send; the response is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the key worked, a 503 or connection error listing app emails,
domains or the hook connection became skipped, so doctor --project could
exit 0 without checking them. Those reads now fail the check; skipped is
left for checks that do not apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Configuring another project saves its hook settings to brew.json and its
secret to .env.local before connecting. When an auth email was not ready,
or the readiness read failed, the step returned the earlier project's
settings, so brew.json and the backed-up secret described different hooks
and a rollback would restore one hook with the other's secret. Those
results now keep the settings that were saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
init installs no @brew.new/sdk in a browser-only Vite app or Supabase
Edge Functions (they send from the server or import npm:@brew.new/sdk),
yet doctor --project failed those apps for not declaring it. The SDK check
is now skipped for them, from the framework brew.json records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ject picked

The rerun-supabase-connect command (an auth email not ready yet) and the
configure-supabase-hook command dropped --supabase-project, so a re-run
detected the project again from the app's files and could change another
project's Send Email hook, or find none. Both now carry the picked ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd step

init --app-url with an organization key acting as brand A answered a
failed GET /v1/brands as a website mismatch on A: the brand step was done
and the domain, kit and provider steps ran in A, though the app may be
another brand's. A failed list now fails the brand step (exit 1), holds
those steps like a failed brand lookup, and asks for a re-run or --brand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The printed integrations connect / connection commands resolve their own
key and brand (the CLI's saved key, BREW_BRAND_ID, BREW_API_URL), not the
app key in .env.local, so after an init for brand B they could connect
the CLI's brand A, or another server. They and the init follow-ups
(--configure-clerk, --configure-supabase, --rollback-provider) now carry
--brand and --api-url when a plain run would resolve another brand or
server; never a credential.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#55's failed-install test types its spawn as SpawnFn; this PR moved the
init test helpers out of init.test.ts and its SpawnFn import with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e verified

The domain check read the first 100 domains only, so a brand whose
verified sending domain sat on a later page was told it had none and
sent to the DNS setup. It now pages through them like init does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doctor --project paired the app key from .env.local with the server from
--api-url, BREW_API_URL or the CLI config, so for an app set up against
another server it sent that key elsewhere and reported that account. It
now uses the BREW_API_URL saved beside the key (none = the default), as
init does, and refuses another server named explicitly (exit 2) before
any request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The printed init follow-ups (--configure-clerk, --configure-supabase,
--rollback-provider) kept --app and the brand and server but dropped
--stack, so a run that needed --stack because detection was wrong
printed a command that detected the old stack and exited 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ure check

looksLikeKey flagged every browser-exposed name containing BREW, so
VITE_BREW_API_URL=https://brew.new/api or an empty VITE_BREW_API_KEY
placeholder failed doctor --project with advice to revoke a key. It now
needs a value, and either a Brew key-like name (KEY, SECRET, TOKEN), a
brew_ value, or the app key itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When --app-url picks a brand with an organization key, init asks which
brand the app key in .env.local is bound to. That client used the run's
server, so with --api-url (or BREW_API_URL) for another server the app
key went there. It now asks only the server saved beside the key
(BREW_API_URL in .env.local, none = the default).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…found

The app-emails check stopped after 20 pages even when more were left, so
a brand with over 2,000 app emails could see working keys reported as
missing. It now reads until every key in brew.json is found or the pages
end (a repeated cursor ends it too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restacked on #61, which sits on #54's spec from brew-v2 6e4accea4c
(sha256 b86fb228...178bd3). This PR adds no spec change of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mail page

Two Greptile findings on #63, each with a test that was red first.

P1: a --configure-supabase run for another project backed up that
project's hook secret in .env.local before brew.json saved the new
settings. When brew.json refused the write (a link outside the project),
it kept the first project's settings but .env.local now held the other
project's secret, so --rollback-provider would restore the first hook
with the wrong secret. The step now remembers the backup it replaces and
puts it back (or removes the new one) when the save fails.
backupHookSecret takes undefined to remove the backup; savedHookSecret
reads it.

P2: publicKeys stopped after 20 pages of GET /v1/app-emails, so on a
brand with more than 2,000 app emails a ready auth key on a later page
counted as not ready, and Supabase setup refused every run (or Clerk kept
its templates). It now reads until every key the provider needs has its
row or the list ends, and stops on a repeated cursor instead of a page
cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rand

resolveAppBrand stopped after 10 pages of 100 brands, so a matching brand
past the first 1,000 was missed and init tried to create another, which
Brew refuses as BRAND_DOMAIN_CONFLICT with a generic step. The lookup now
reads pages until a match or the list's end; a cursor seen before ends it,
so a server repeating one cannot loop it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With an organization key on one server and .env.local's app key (with
its BREW_API_URL) on another, the --app-url brand check asked the app
key's server which brand the key is bound to and compared only the id.
A staging copy with production's brand ids passed, so the domain, kit and
provider setup ran on the organization key's server while the app kept
sending to the other. The app key now holds the setup unless it is on
this run's server and bound to the brand picked; the replace-app-key step
names both servers when they differ, and the key is not sent anywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is needed

The app-key check ran only when --app-url switched the run to another
brand. When the run's own key (--api-key or BREW_API_KEY) already had the
app's brand, an app key in .env.local on another server, or bound to
another brand, still let the domain, kit and provider setup run where the
app does not send. The check now covers the brand the run sets up either
way, whenever .env.local's key is not the key the run acts as.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant