Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/store-create-preview-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@shopify/store': minor
'@shopify/cli': minor
---

Add `shopify store create preview`, which mints a Preview Store via Core's preview-stores orchestrator (M1 of the [Preview Store for AI Agent Surfaces](https://vault.shopify.io/gsd/proposals/60T12R) initiative) and persists the returned admin API token as a `kind: 'preview'` stored session. After running it, `shopify store execute --store <permanent-domain>` works against the new store immediately, with no PKCE flow and no browser interaction.

The Core endpoint defaults to the local development rig (`https://app.shop.dev`) and the prototype basic-auth credentials. Override with `--core-url`, `--cli-username`, `--cli-secret` or the equivalent `SHOPIFY_FLAG_PREVIEW_STORE_*` environment variables when targeting other environments.
46 changes: 46 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
* [`shopify plugins update`](#shopify-plugins-update)
* [`shopify search [query]`](#shopify-search-query)
* [`shopify store auth`](#shopify-store-auth)
* [`shopify store create preview`](#shopify-store-create-preview)
* [`shopify store execute`](#shopify-store-execute)
* [`shopify theme check`](#shopify-theme-check)
* [`shopify theme console`](#shopify-theme-console)
Expand Down Expand Up @@ -2130,6 +2131,51 @@ EXAMPLES
$ shopify store auth --store shop.myshopify.com --scopes read_products,write_products --json
```

## `shopify store create preview`

Create a Preview Store backed by a placeholder identity.

```
USAGE
$ shopify store create preview [--cli-secret <value>] [--cli-username <value>] [--core-url <value>] [--country <value>]
[--email <value>] [-j] [--no-color] [-n <value>] [--verbose]

FLAGS
-j, --json [env: SHOPIFY_FLAG_JSON] Output the result as JSON. Automatically disables color output.
-n, --shop-name=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_SHOP_NAME] Subdomain prefix for the new preview store.
Auto-generated if omitted.
--cli-secret=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_CLI_SECRET] Basic-auth secret for the Core endpoint.
Defaults to the development rig value.
--cli-username=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_CLI_USERNAME] Basic-auth username for the Core endpoint.
Defaults to the development rig value.
--core-url=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_CORE_URL] Base URL of the Core preview-stores
orchestrator. Defaults to the local development rig.
--country=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_COUNTRY] ISO country code for the new store. Defaults to
"US".
--email=<value> [env: SHOPIFY_FLAG_PREVIEW_STORE_EMAIL] Email to associate with the placeholder identity.
Defaults to a generated `@previewstore.invalid` address chosen by Core.
--no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output.
--verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output.

DESCRIPTION
Create a Preview Store backed by a placeholder identity.

Creates a Preview Store via the Core preview-stores orchestrator. The returned admin API token is persisted locally as
a stored store-auth session, so the new store can be used immediately as a target for `shopify store execute --store
<permanent-domain>` without any further login.

The orchestrator endpoint, basic-auth username, and basic-auth secret default to the local development rig values used
by the M1 prototype. Override them with `--core-url`, `--cli-username`, and `--cli-secret` (or the corresponding
`SHOPIFY_FLAG_*` environment variables) when targeting a non-default environment.

EXAMPLES
$ shopify store create preview --shop-name my-preview

$ shopify store create preview --shop-name my-preview --email demo@previewstore.invalid

$ shopify store create preview --shop-name my-preview --json
```

## `shopify store execute`

Execute GraphQL queries and mutations on a store.
Expand Down
105 changes: 105 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5737,6 +5737,111 @@
"strict": true,
"summary": "Authenticate an app against a store for store commands."
},
"store:create:preview": {
"aliases": [
],
"args": {
},
"customPluginName": "@shopify/store",
"description": "Creates a Preview Store via the Core preview-stores orchestrator. The returned admin API token is persisted locally as a stored store-auth session, so the new store can be used immediately as a target for `shopify store execute --store <permanent-domain>` without any further login.\n\nThe orchestrator endpoint, basic-auth username, and basic-auth secret default to the local development rig values used by the M1 prototype. Override them with `--core-url`, `--cli-username`, and `--cli-secret` (or the corresponding `SHOPIFY_FLAG_*` environment variables) when targeting a non-default environment.",
"descriptionWithMarkdown": "Creates a Preview Store via the Core preview-stores orchestrator. The returned admin API token is persisted locally as a stored store-auth session, so the new store can be used immediately as a target for `shopify store execute --store <permanent-domain>` without any further login.\n\nThe orchestrator endpoint, basic-auth username, and basic-auth secret default to the local development rig values used by the M1 prototype. Override them with `--core-url`, `--cli-username`, and `--cli-secret` (or the corresponding `SHOPIFY_FLAG_*` environment variables) when targeting a non-default environment.",
"examples": [
"<%= config.bin %> <%= command.id %> --shop-name my-preview",
"<%= config.bin %> <%= command.id %> --shop-name my-preview --email demo@previewstore.invalid",
"<%= config.bin %> <%= command.id %> --shop-name my-preview --json"
],
"flags": {
"cli-secret": {
"description": "Basic-auth secret for the Core endpoint. Defaults to the development rig value.",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_CLI_SECRET",
"hasDynamicHelp": false,
"multiple": false,
"name": "cli-secret",
"required": false,
"type": "option"
},
"cli-username": {
"description": "Basic-auth username for the Core endpoint. Defaults to the development rig value.",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_CLI_USERNAME",
"hasDynamicHelp": false,
"multiple": false,
"name": "cli-username",
"required": false,
"type": "option"
},
"core-url": {
"description": "Base URL of the Core preview-stores orchestrator. Defaults to the local development rig.",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_CORE_URL",
"hasDynamicHelp": false,
"multiple": false,
"name": "core-url",
"required": false,
"type": "option"
},
"country": {
"description": "ISO country code for the new store. Defaults to \"US\".",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_COUNTRY",
"hasDynamicHelp": false,
"multiple": false,
"name": "country",
"required": false,
"type": "option"
},
"email": {
"description": "Email to associate with the placeholder identity. Defaults to a generated `@previewstore.invalid` address chosen by Core.",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_EMAIL",
"hasDynamicHelp": false,
"multiple": false,
"name": "email",
"required": false,
"type": "option"
},
"json": {
"allowNo": false,
"char": "j",
"description": "Output the result as JSON. Automatically disables color output.",
"env": "SHOPIFY_FLAG_JSON",
"hidden": false,
"name": "json",
"type": "boolean"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
"env": "SHOPIFY_FLAG_NO_COLOR",
"hidden": false,
"name": "no-color",
"type": "boolean"
},
"shop-name": {
"char": "n",
"description": "Subdomain prefix for the new preview store. Auto-generated if omitted.",
"env": "SHOPIFY_FLAG_PREVIEW_STORE_SHOP_NAME",
"hasDynamicHelp": false,
"multiple": false,
"name": "shop-name",
"required": false,
"type": "option"
},
"verbose": {
"allowNo": false,
"description": "Increase the verbosity of the output.",
"env": "SHOPIFY_FLAG_VERBOSE",
"hidden": false,
"name": "verbose",
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [
],
"id": "store:create:preview",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Create a Preview Store backed by a placeholder identity."
},
"store:execute": {
"aliases": [
],
Expand Down
81 changes: 81 additions & 0 deletions packages/store/src/cli/commands/store/create/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {createPreviewStoreCommand} from '../../../services/store/create/preview/index.js'
import {writeCreatePreviewStoreResult} from '../../../services/store/create/preview/result.js'
import StoreCommand from '../../../utilities/store-command.js'
import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli'
import {Flags} from '@oclif/core'

export default class StoreCreatePreview extends StoreCommand {
static summary = 'Create a Preview Store backed by a placeholder identity.'

static descriptionWithMarkdown = `Creates a Preview Store via the Core preview-stores orchestrator. The returned admin API token is persisted locally as a stored store-auth session, so the new store can be used immediately as a target for \`shopify store execute --store <permanent-domain>\` without any further login.

The orchestrator endpoint, basic-auth username, and basic-auth secret default to the local development rig values used by the M1 prototype. Override them with \`--core-url\`, \`--cli-username\`, and \`--cli-secret\` (or the corresponding \`SHOPIFY_FLAG_*\` environment variables) when targeting a non-default environment.`

static description = this.descriptionWithoutMarkdown()

static examples = [
'<%= config.bin %> <%= command.id %> --shop-name my-preview',
'<%= config.bin %> <%= command.id %> --shop-name my-preview --email demo@previewstore.invalid',
'<%= config.bin %> <%= command.id %> --shop-name my-preview --json',
]

static flags = {
...globalFlags,
...jsonFlag,
'shop-name': Flags.string({
char: 'n',
description: 'Subdomain prefix for the new preview store. Auto-generated if omitted.',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_SHOP_NAME',
required: false,
}),
email: Flags.string({
description:
'Email to associate with the placeholder identity. Defaults to a generated `@previewstore.invalid` address chosen by Core.',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_EMAIL',
required: false,
}),
country: Flags.string({
description: 'ISO country code for the new store. Defaults to "US".',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_COUNTRY',
required: false,
}),
'core-url': Flags.string({
description: 'Base URL of the Core preview-stores orchestrator. Defaults to the local development rig.',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_CORE_URL',
required: false,
}),
'cli-username': Flags.string({
description: 'Basic-auth username for the Core endpoint. Defaults to the development rig value.',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_CLI_USERNAME',
required: false,
}),
'cli-secret': Flags.string({
description: 'Basic-auth secret for the Core endpoint. Defaults to the development rig value.',
env: 'SHOPIFY_FLAG_PREVIEW_STORE_CLI_SECRET',
required: false,
}),
}

public async run(): Promise<void> {
const {flags} = await this.parse(StoreCreatePreview)

const shopName = flags['shop-name'] ?? generateDefaultShopName()

const result = await createPreviewStoreCommand({
shopName,
email: flags.email,
country: flags.country,
client: {
coreUrl: flags['core-url'],
cliUsername: flags['cli-username'],
cliSecret: flags['cli-secret'],
},
})

writeCreatePreviewStoreResult(result, flags.json ? 'json' : 'text')
}
}

function generateDefaultShopName(): string {
return `preview-${Math.floor(Date.now() / 1000)}`
}
4 changes: 0 additions & 4 deletions packages/store/src/cli/services/store/auth/session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {LocalStorage} from '@shopify/cli-kit/node/local-storage'
* Stored sessions written before this discriminator existed have no `kind` field and are
* read back as 'standard'.
*/
// Kept internal for now; re-exported when the `shopify store create preview` command lands.
type StoredStoreSessionKind = 'standard' | 'preview'

/**
Expand Down Expand Up @@ -58,9 +57,6 @@ export interface StoredStoreAppSession {
/**
* A stored session that has been narrowed to a preview-store session. The `preview`
* metadata is guaranteed to be present.
*
* Kept internal for now; re-exported when the `shopify store create preview` command
* lands and external callers need to construct or pass them around.
*/
type StoredPreviewStoreSession = StoredStoreAppSession & {
kind: 'preview'
Expand Down
Loading
Loading