Skip to content

feat: headless provision subcommand + --ci --signup for agents#415

Open
MattBro wants to merge 5 commits intomainfrom
matt/wizard-headless-signup-install
Open

feat: headless provision subcommand + --ci --signup for agents#415
MattBro wants to merge 5 commits intomainfrom
matt/wizard-headless-signup-install

Conversation

@MattBro
Copy link
Copy Markdown
Contributor

@MattBro MattBro commented Apr 23, 2026

Problem

The wizard has no headless signup path. The interactive $0 flow requires a TTY + browser, and --ci requires a personal API key the caller already has. Neither works for a cold-start agent that needs to bootstrap PostHog into a project in one shot.

Changes

Two layered entry points:

1. wizard provision subcommand — a primitive that calls provisionNewAccount() directly and emits the structured ProvisioningResult on stdout. No TUI, no framework detection, no browser. For tests, pre-flight checks, or callers that want credentials without the install.

# Human-readable when stdout is a TTY
npx @posthog/wizard provision --email user@example.com --region us

# Machine-readable JSON when piped, or force with --json
npx @posthog/wizard provision --email user@example.com --region us --json
# → {"projectApiKey":"phc_...","host":"https://us.posthog.com","projectId":"...","accountId":"...","accessToken":"...","refreshToken":"...","personalApiKey":"phx_..."}

Failure exits 1; in --json mode errors surface on stderr as {"error":"...","code":"..."} with a stable email_exists code for the already-registered case.

2. wizard --ci --signup --email you@example.com --install-dir . — the end-to-end flow. The $0 CI branch provisions a new account via provisionNewAccount, sets options.apiKey = result.personalApiKey, then falls through to the existing runWizardCI install. --api-key takes precedence if both are supplied.

Also adds --email and --name options (scoped to $0, not global — global placement broke the existing cli.test.ts mocking setup via a yargs parse-order quirk).

Test plan

  • New: src/__tests__/provision-cli.test.ts — 8 tests covering the subcommand (required --email, region uppercasing, --name forwarding, JSON shape, auto-JSON for non-TTY, human-readable mode, email_exists error code, provisioning_failed error code).
  • Extended: src/__tests__/cli.test.ts — new --ci --signup flow describe block with 7 tests (missing --email, missing key + no signup, success path sets apiKey, --name forwarding, region uppercasing, provisioning rejection, --api-key precedence).
  • Full suite: 81/81 passing, lint clean (0 errors).
  • Manual against us.posthog.com: both provision --json and --ci --signup produce valid credentials and the latter reaches the install flow.

Dependency note: --ci --signup needs the server-side provisioned PAT to have working scopes. Tracked in PostHog/posthog#56029 — the wizard side works as soon as that PR merges (without it, the install reaches detectRegionFromToken and fails because the PAT has no scopes).

Authored with Claude Opus 4.7 (1M context). Tests passed locally and I verified the real-API flow against us.posthog.com; I'm an agent and did not run the full CI locally.

MattBro and others added 4 commits April 23, 2026 13:18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the headless provision subcommand:

- --json flag (auto-enabled when stdout is not a TTY) emits the full
  ProvisioningResult as a single JSON object to stdout; human log lines
  go to stderr so they don't corrupt piped output.
- --name flag forwards to provisionNewAccount (previously hardcoded to "").
- Output now includes accountId, accessToken, refreshToken alongside
  projectApiKey, host, projectId, personalApiKey.
- Errors in --json mode are emitted to stderr as {error, code}, with
  code="email_exists" when the address is already registered so scripts
  can branch without string-matching.
- README gains a "Headless Provisioning" section with usage examples and
  a warning that output contains live credentials.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers the behaviors guaranteed by the spec:

- --email is required (exit 1, API not called)
- --region is uppercased before provisionNewAccount is invoked
- --name forwards through as the name argument
- --json emits one JSON object of the full ProvisioningResult on stdout
- JSON mode auto-enables when stdout is not a TTY
- human-readable mode routes through LoggingUI on console.log
- email-already-associated rejections surface code: "email_exists"
- other errors surface code: "provisioning_failed"

Mocks provisionNewAccount and follows the existing cli.test.ts pattern
for bin.ts loading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the headless signup path into the existing --ci install flow so a
cold-start agent can bootstrap an account AND install PostHog into a
project with a single command, no TTY, no browser, no prior API key.

- Adds --email and --name to the $0 command (scoped, not global — global
  placement broke cli.test.ts mocking via a yargs parse-order quirk).
- Relaxes the CI api-key gate: if --signup is set, provision first via
  provisionNewAccount(email, name, region), then set options.apiKey to
  the returned personalApiKey and options.projectId before falling
  through to runWizardCI.
- --api-key takes precedence over --signup when both are supplied.
- Surfaces clear errors for --ci without key-or-signup, and for
  --ci --signup without --email.
- README: new "Headless signup + install" section for the combined flow;
  existing provision section preserved as a primitive.
- Tests: new --ci --signup flow describe block in cli.test.ts covering
  success, --name forwarding, region uppercasing, rejection handling,
  missing personal API key, and --api-key precedence. The block uses
  a silent process.exit mock because the exits happen inside the async
  provisioning IIFE and the throwing mock would escape as an unhandled
  rejection. Mock variables renamed to per-file suffixes since .test.ts
  files share TS project scope when they have no imports/exports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MattBro MattBro marked this pull request as ready for review April 23, 2026 17:19
@github-actions
Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci android
  • /wizard-ci angular
  • /wizard-ci astro
  • /wizard-ci django
  • /wizard-ci fastapi
  • /wizard-ci flask
  • /wizard-ci javascript-node
  • /wizard-ci javascript-web
  • /wizard-ci laravel
  • /wizard-ci next-js
  • /wizard-ci nuxt
  • /wizard-ci python
  • /wizard-ci rails
  • /wizard-ci react-native
  • /wizard-ci react-router
  • /wizard-ci stripe
  • /wizard-ci sveltekit
  • /wizard-ci swift
  • /wizard-ci tanstack-router
  • /wizard-ci tanstack-start
  • /wizard-ci vue

Test an individual app:

  • /wizard-ci android/Jetchat
  • /wizard-ci angular/angular-saas
  • /wizard-ci astro/astro-hybrid-marketing
Show more apps
  • /wizard-ci astro/astro-ssr-docs
  • /wizard-ci astro/astro-static-marketing
  • /wizard-ci astro/astro-view-transitions-marketing
  • /wizard-ci django/django3-saas
  • /wizard-ci fastapi/fastapi3-ai-saas
  • /wizard-ci flask/flask3-social-media
  • /wizard-ci javascript-node/express-todo
  • /wizard-ci javascript-node/fastify-blog
  • /wizard-ci javascript-node/hono-links
  • /wizard-ci javascript-node/koa-notes
  • /wizard-ci javascript-node/native-http-contacts
  • /wizard-ci javascript-web/saas-dashboard
  • /wizard-ci laravel/laravel12-saas
  • /wizard-ci next-js/15-app-router-saas
  • /wizard-ci next-js/15-app-router-todo
  • /wizard-ci next-js/15-pages-router-saas
  • /wizard-ci next-js/15-pages-router-todo
  • /wizard-ci nuxt/movies-nuxt-3-6
  • /wizard-ci nuxt/movies-nuxt-4
  • /wizard-ci python/meeting-summarizer
  • /wizard-ci rails/fizzy
  • /wizard-ci react-native/expo-react-native-hacker-news
  • /wizard-ci react-native/react-native-saas
  • /wizard-ci react-router/react-router-v7-project
  • /wizard-ci react-router/rrv7-starter
  • /wizard-ci react-router/saas-template
  • /wizard-ci react-router/shopper
  • /wizard-ci stripe/stripe-next-js-saas-starter
  • /wizard-ci stripe/stripe-saas-demo
  • /wizard-ci sveltekit/CMSaasStarter
  • /wizard-ci swift/hackers-ios
  • /wizard-ci tanstack-router/tanstack-router-code-based-saas
  • /wizard-ci tanstack-router/tanstack-router-file-based-saas
  • /wizard-ci tanstack-start/tanstack-start-saas
  • /wizard-ci vue/movies

Results will be posted here when complete.

@MattBro MattBro requested a review from a team April 23, 2026 20:38
Copy link
Copy Markdown
Member

@edwinyjlim edwinyjlim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread bin.ts
Comment on lines +188 to +192
email: {
describe:
'Email for account creation with --ci --signup\nenv: POSTHOG_WIZARD_EMAIL',
type: 'string',
},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove since email is defined in the global options

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.

2 participants