Add shopify store create preview command#7558
Draft
alfonso-noriega wants to merge 1 commit into
Draft
Conversation
Mints a Preview Store via Core's `/services/preview-stores` orchestrator and
persists the returned admin API token as a `kind: 'preview'` stored session
under the existing shopify-cli-store LocalStorage namespace. The new store can
be used immediately as a target for `shopify store execute --store
<permanent-domain>` with no PKCE flow and no browser interaction.
- commands/store/create/preview.ts: oclif command (`shopify store create
preview`) with --shop-name, --email, --country, --core-url, --cli-username,
--cli-secret, --json flags.
- services/store/create/preview/client.ts: Core HTTP client with snake_case
contract, basic-auth, response narrowing.
- services/store/create/preview/index.ts: orchestrator that calls the client,
persists the kind: 'preview' session via setStoredStoreAppSession, derives a
placeholder: <uuid> userId, computes the magic-link expiry locally, calls
setLastSeenUserId.
- services/store/create/preview/result.ts: text and JSON presenters; text
output omits the admin token to avoid copy-paste leakage, JSON output
surfaces it for agent harnesses.
- Re-runs refresh-manifests so README and oclif.manifest.json reflect the
new command surface.
- Defaults: app.shop.dev, basic-auth preview-store-cli/preview-store-cli-dev
matching the M1 prototype. These need productionization (real Core URL
resolution, real service auth) before the command ships to a non-developer
release channel \u2014 covered in a follow-up PR.
Contributor
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

WHY are these changes introduced?
M1 of the Preview Store for AI Agent Surfaces initiative ships a CLI-first flow where an AI agent can ask the CLI to create a Shopify store, get back a storefront URL, and then keep iterating on the store via existing CLI commands — all without an account, an admin UI, or a browser hop.
Growth's prototype on
shop/world#708792adds the Core orchestrator (POST /services/preview-stores) that creates a placeholder identity, mints a realshpat_…Admin API token against theshopify-cli-connector-app, and returns a one-time-use magic link to admin. River put up a parallel CLI prototype (branchShopify/cli@preview-store/prototype) that called this endpoint via a new top-levelpreviewcommand group and required users to either pipe--jsonoutput into a file or pass--domain/--tokenflags into a custompreview executeclone ofstore execute.This PR ports the prototype onto the production CLI's
storenamespace and wires it through the existing stored-session machinery so we can delete the parallel transport entirely:shopify preview create→shopify store create preview(matches the language used in the DevTools kickoff and slack threads).shopify preview execute→ simply useshopify store execute --store <permanent-domain> …because the new command writes akind: 'preview'session into the same LocalStorage namespace thatstore executereads from.shopify preview claimis M2 work and is intentionally not included.The schema work that lets
kind: 'preview'round-trip safely is in #7557; this PR introduces the only producer of those sessions.WHAT is this pull request doing?
New command —
shopify store create preview(packages/store/src/cli/commands/store/create/preview.ts)Flags:
-n, --shop-nameSHOPIFY_FLAG_PREVIEW_STORE_SHOP_NAME--emailSHOPIFY_FLAG_PREVIEW_STORE_EMAIL@previewstore.invalidaddress.--countrySHOPIFY_FLAG_PREVIEW_STORE_COUNTRY"US".--core-urlSHOPIFY_FLAG_PREVIEW_STORE_CORE_URLhttps://app.shop.dev).--cli-usernameSHOPIFY_FLAG_PREVIEW_STORE_CLI_USERNAME--cli-secretSHOPIFY_FLAG_PREVIEW_STORE_CLI_SECRET-j, --jsonSHOPIFY_FLAG_JSONCore HTTP client (
services/store/create/preview/client.ts)Uses
shopifyFetchtoPOST /services/preview-storeswith the snake_case body Core expects (shop_name,email,country). Translates the response into a camelCase type and rejects non-2xx responses, non-JSON bodies, and responses missing any of the five required identifiers. Defaults —https://app.shop.devand the basic-auth pairpreview-store-cli/preview-store-cli-dev— match the dev-only secret hardcoded inServices::PreviewStoresControllerand are explicitly flagged in code comments as "needs productionization before non-developer release".Orchestrator (
services/store/create/preview/index.ts)After the client returns, the orchestrator persists the response as a stored store-auth session via
setStoredStoreAppSessionwith:kind: 'preview',preview: { placeholderAccountUuid, coreUrl, magicLinkUrl, magicLinkExpiresAt }— surfacing the metadata Add preview-store discriminator to stored store auth sessions #7557 introduced.userId: \placeholder:`` — non-numeric and prefixed so analytics filters can isolate placeholder identities and there's no collision with PKCE-issued sessions (which use numeric Shopify user ids).scopes: []— Core does not surface the granted scope list; the array is a sentinel and is not consulted bystore execute(which only validates against the live Admin API). Preview sessions never go through the recovery path that suggests--scopes.magicLinkExpiresAtderived locally asacquiredAt + 30 minutesto matchPreviewStores::Create::MAGIC_LINK_TTL.recordStoreFqdnMetadatais called twice (unvalidated → validated) to mirror the analytics shape PKCE auth emits, andsetLastSeenUserIdis updated so the nextstore executeagainst the new store finds the right session.Result presenter (
services/store/create/preview/result.ts)Text output renders the shop id, permanent domain, placeholder UUID, and magic link, plus a next-step suggestion to run
shopify store execute --store <permanent-domain> --query '{ shop { name } }'. The admin token is intentionally not rendered in text mode to avoid accidental copy-paste leakage. JSON output (used by AI agents) does include it because the agent needs the full session shape.Wiring
packages/store/src/index.tsregisters'store:create:preview'.packages/cli/README.mdandpackages/cli/oclif.manifest.jsonregenerated viapnpm refresh-manifests.StoredStoreSessionKindandStoredPreviewStoreSessionfrom Add preview-store discriminator to stored store auth sessions #7557 stay module-internal;PreviewStoreCreateRequestandCreatePreviewStoreInputare also kept module-internal. (Knip's check is clean.)Out of scope
shopify store create preview --reuse <fqdn>and the corresponding "re-mint expired token" code path that Add preview-store discriminator to stored store auth sessions #7557's preview-recovery message hints at. The token's lifetime is whatever Core assigns — the M1 prototype does not surface an expiry to the CLI, so we treat preview sessions as non-expiring until Growth wires that through.shopify store listintegration (raised in Daniel's thread). The session is stored wherestore listwill read it from once that command lands; no extra work needed here.How to test your changes?
Unit tests:
End-to-end on a local Core rig (per River's demo and the M1 prototype setup in
shop/world#708792):Bring up Core with the preview-store rig (
dev rig preview-store-minagainstshop/worldPR 708792 checked out locally).From this branch:
pnpm shopify store create preview --shop-name my-preview --json | tee /tmp/preview.jsonThe new store can immediately be queried — no
shopify store authneeded:Open
magicLinkUrlfrom the JSON output in a fresh browser session to land in admin without an Identity login (one-time use, ~30 minutes).The pre-existing 2 callback test failures in
packages/store/src/cli/services/store/auth/callback.test.ts("doesn't"vs"does not"string mismatch) are unchanged onmainand unrelated to this PR.Post-release steps
None.
Checklist
shopifyFetchandLocalStoragefrom cli-kit, no platform-specific surface.packages/cli/README.mdregenerated; user-facing docs on shopify.dev forstore create previewto follow once the command moves out of M1 prototype defaults.recordStoreFqdnMetadataandsetLastSeenUserIdmirror the PKCE-auth shape; theplaceholder:<uuid>userId prefix is intentional to make placeholder-issued sessions filterable in analytics.patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add—minorbump on@shopify/storeand@shopify/cli(new top-level command).