@sanity/cli 7.17.0 → 7.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/commands/new.js +52 -1
- package/dist/commands/new.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +23 -14
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/services/newInstructions.js +48 -0
- package/dist/services/newInstructions.js.map +1 -0
- package/oclif.manifest.json +12 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -2972,13 +2972,14 @@ Create a Sanity project without an account, and claim it within 72 hours to keep
|
|
|
2972
2972
|
|
|
2973
2973
|
```
|
|
2974
2974
|
USAGE
|
|
2975
|
-
$ sanity new [PROJECTNAME] [--json] [--scaffold] [-y]
|
|
2975
|
+
$ sanity new [PROJECTNAME] [--json] [--instructions] [--scaffold] [-y]
|
|
2976
2976
|
|
|
2977
2977
|
ARGUMENTS
|
|
2978
2978
|
[PROJECTNAME] Display name for the new project
|
|
2979
2979
|
|
|
2980
2980
|
FLAGS
|
|
2981
2981
|
-y, --yes Skip prompts and use defaults (project: "My Sanity project")
|
|
2982
|
+
--instructions Print the full setup guide from https://sanity.new and exit, creating nothing
|
|
2982
2983
|
--[no-]scaffold Set up a Studio in ./sanity and a Next.js website in ./web (on by default)
|
|
2983
2984
|
|
|
2984
2985
|
GLOBAL FLAGS
|
|
@@ -3003,7 +3004,7 @@ DESCRIPTION
|
|
|
3003
3004
|
Keep both env files out of git, and never put the token in code that runs in
|
|
3004
3005
|
the browser.
|
|
3005
3006
|
|
|
3006
|
-
|
|
3007
|
+
Run this command with --instructions for the full agent setup guide.
|
|
3007
3008
|
|
|
3008
3009
|
EXAMPLES
|
|
3009
3010
|
Create a project with a Studio and a website
|
|
@@ -3025,6 +3026,10 @@ EXAMPLES
|
|
|
3025
3026
|
Create a project and print its details as JSON
|
|
3026
3027
|
|
|
3027
3028
|
$ sanity new --json
|
|
3029
|
+
|
|
3030
|
+
Print the full setup guide for an AI agent, without creating anything
|
|
3031
|
+
|
|
3032
|
+
$ sanity new --instructions
|
|
3028
3033
|
```
|
|
3029
3034
|
|
|
3030
3035
|
## `sanity openapi get SLUG`
|
package/dist/commands/new.js
CHANGED
|
@@ -6,12 +6,17 @@ import { SanityCommand } from '@sanity/cli-core/SanityCommand';
|
|
|
6
6
|
import { createFlow, input } from '@sanity/cli-core/ux';
|
|
7
7
|
import { FRONTEND_DIR, FRONTEND_ENV_FILE, getUnavailableScaffoldTarget, scaffoldProject, STUDIO_DIR, STUDIO_ENV_FILE } from '../actions/scaffold/scaffoldProject.js';
|
|
8
8
|
import { mintUnclaimedProject } from '../services/mintProject.js';
|
|
9
|
+
import { fetchNewInstructions, INSTRUCTIONS_URL, InstructionsUnavailableError } from '../services/newInstructions.js';
|
|
9
10
|
import { formatClaimDeadline } from '../util/formatClaimDeadline.js';
|
|
10
11
|
import { CLAIM_WINDOW_HOURS, SANITY_NEW_URL } from '../util/mintProjectConstants.js';
|
|
11
12
|
import { renderNewCommandSplash } from '../util/newCommandSplash.js';
|
|
12
13
|
import { recordUnclaimedProject } from '../util/unclaimedProjects.js';
|
|
13
14
|
const DEFAULT_PROJECT_NAME = 'My Sanity project';
|
|
14
15
|
const HELP_DESCRIPTION_WIDTH = 78;
|
|
16
|
+
/**
|
|
17
|
+
* Prepended to the fetched instructions so an agent cannot read this command's success as the
|
|
18
|
+
* project already existing. The only addition to an otherwise verbatim document.
|
|
19
|
+
*/ const INSTRUCTIONS_HEADER = '# Nothing has been created yet — follow these steps.';
|
|
15
20
|
function formatHelpDescription(...paragraphs) {
|
|
16
21
|
return paragraphs.map((paragraph)=>{
|
|
17
22
|
const lines = [];
|
|
@@ -81,7 +86,7 @@ export class NewCommand extends SanityCommand {
|
|
|
81
86
|
required: false
|
|
82
87
|
})
|
|
83
88
|
};
|
|
84
|
-
static description = formatHelpDescription(`Sets up two folders here: ./${STUDIO_DIR}, a Studio where you write and edit your content, ` + `and ./${FRONTEND_DIR}, a Next.js website that reads it. Both are already connected to your ` + 'new project, so you can start them straight away. Use --no-scaffold if you just want the ' + 'project and nothing else.', `The project is real and works immediately, but it is only yours for ${CLAIM_WINDOW_HOURS} hours. Claim it with ` + 'a Sanity account before the deadline and everything you have built stays exactly as it is. ' + 'Claiming is free and takes about a minute. Miss the deadline and the project and its ' + 'content are deleted.', 'Two things to keep private: the claim link, because anyone who opens it becomes the owner, ' + `and the access token saved in ./${STUDIO_ENV_FILE} and ./${FRONTEND_ENV_FILE}, because it ` + 'can read and change everything in the project. Keep both env files out of git, and never ' + 'put the token in code that runs in the browser.',
|
|
89
|
+
static description = formatHelpDescription(`Sets up two folders here: ./${STUDIO_DIR}, a Studio where you write and edit your content, ` + `and ./${FRONTEND_DIR}, a Next.js website that reads it. Both are already connected to your ` + 'new project, so you can start them straight away. Use --no-scaffold if you just want the ' + 'project and nothing else.', `The project is real and works immediately, but it is only yours for ${CLAIM_WINDOW_HOURS} hours. Claim it with ` + 'a Sanity account before the deadline and everything you have built stays exactly as it is. ' + 'Claiming is free and takes about a minute. Miss the deadline and the project and its ' + 'content are deleted.', 'Two things to keep private: the claim link, because anyone who opens it becomes the owner, ' + `and the access token saved in ./${STUDIO_ENV_FILE} and ./${FRONTEND_ENV_FILE}, because it ` + 'can read and change everything in the project. Keep both env files out of git, and never ' + 'put the token in code that runs in the browser.', 'Run this command with --instructions for the full agent setup guide.');
|
|
85
90
|
static enableJsonFlag = true;
|
|
86
91
|
static examples = [
|
|
87
92
|
{
|
|
@@ -103,9 +108,17 @@ export class NewCommand extends SanityCommand {
|
|
|
103
108
|
{
|
|
104
109
|
command: '<%= config.bin %> <%= command.id %> --json',
|
|
105
110
|
description: 'Create a project and print its details as JSON'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
command: '<%= config.bin %> <%= command.id %> --instructions',
|
|
114
|
+
description: 'Print the full setup guide for an AI agent, without creating anything'
|
|
106
115
|
}
|
|
107
116
|
];
|
|
108
117
|
static flags = {
|
|
118
|
+
instructions: Flags.boolean({
|
|
119
|
+
default: false,
|
|
120
|
+
description: `Print the full setup guide from ${SANITY_NEW_URL} and exit, creating nothing`
|
|
121
|
+
}),
|
|
109
122
|
scaffold: Flags.boolean({
|
|
110
123
|
allowNo: true,
|
|
111
124
|
default: true,
|
|
@@ -127,6 +140,22 @@ export class NewCommand extends SanityCommand {
|
|
|
127
140
|
...this.id?.split(':') ?? []
|
|
128
141
|
].join(' ');
|
|
129
142
|
const cwd = process.cwd();
|
|
143
|
+
// Runs before every precondition and side effect: this flag creates nothing.
|
|
144
|
+
if (this.flags.instructions) {
|
|
145
|
+
// oclif's `exclusive` does not cover the json flag added by enableJsonFlag, so guard it here.
|
|
146
|
+
if (json) {
|
|
147
|
+
throw new CLIError('--instructions cannot be combined with --json.', {
|
|
148
|
+
code: 'INSTRUCTIONS_JSON_CONFLICT',
|
|
149
|
+
exit: exitCodes.USAGE_ERROR,
|
|
150
|
+
suggestions: [
|
|
151
|
+
`Run \`${invoked} --instructions\` for the setup guide as markdown`,
|
|
152
|
+
`Or run \`${invoked} --json\` to create a project and print its details as JSON`
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
await this.printInstructions();
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
130
159
|
const unavailableScaffoldTarget = !json && this.flags.scaffold ? await getUnavailableScaffoldTarget(cwd) : undefined;
|
|
131
160
|
if (unavailableScaffoldTarget) {
|
|
132
161
|
throw new CLIError(`./${unavailableScaffoldTarget} is not an empty directory. No project was created.`, {
|
|
@@ -304,6 +333,28 @@ export class NewCommand extends SanityCommand {
|
|
|
304
333
|
}
|
|
305
334
|
return toResult(project);
|
|
306
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Dumps the guide from sanity.new verbatim, so an agent gets the whole document rather than a
|
|
338
|
+
* summary produced by whatever fetched it.
|
|
339
|
+
*/ async printInstructions() {
|
|
340
|
+
let instructions;
|
|
341
|
+
try {
|
|
342
|
+
instructions = await fetchNewInstructions();
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (err instanceof InstructionsUnavailableError) {
|
|
345
|
+
throw new CLIError(err.message, {
|
|
346
|
+
code: 'INSTRUCTIONS_UNAVAILABLE',
|
|
347
|
+
exit: exitCodes.RUNTIME_ERROR,
|
|
348
|
+
suggestions: [
|
|
349
|
+
`Fetch them directly: curl -sSL ${INSTRUCTIONS_URL}`,
|
|
350
|
+
`Or open ${SANITY_NEW_URL} in a browser`
|
|
351
|
+
]
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
throw err;
|
|
355
|
+
}
|
|
356
|
+
this.output.log(`${INSTRUCTIONS_HEADER}\n\n${instructions}`);
|
|
357
|
+
}
|
|
307
358
|
}
|
|
308
359
|
|
|
309
360
|
//# sourceMappingURL=new.js.map
|
package/dist/commands/new.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/new.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {createFlow, input} from '@sanity/cli-core/ux'\n\nimport {\n FRONTEND_DIR,\n FRONTEND_ENV_FILE,\n getUnavailableScaffoldTarget,\n scaffoldProject,\n type ScaffoldResult,\n STUDIO_DIR,\n STUDIO_ENV_FILE,\n} from '../actions/scaffold/scaffoldProject.js'\nimport {type MintedProject, mintUnclaimedProject} from '../services/mintProject.js'\nimport {formatClaimDeadline} from '../util/formatClaimDeadline.js'\nimport {CLAIM_WINDOW_HOURS, SANITY_NEW_URL} from '../util/mintProjectConstants.js'\nimport {renderNewCommandSplash} from '../util/newCommandSplash.js'\nimport {recordUnclaimedProject} from '../util/unclaimedProjects.js'\n\nconst DEFAULT_PROJECT_NAME = 'My Sanity project'\nconst HELP_DESCRIPTION_WIDTH = 78\n\nfunction formatHelpDescription(...paragraphs: string[]): string {\n return paragraphs\n .map((paragraph) => {\n const lines: string[] = []\n let line = ''\n\n for (const word of paragraph.split(/\\s+/u)) {\n if (!line || line.length + word.length + 1 <= HELP_DESCRIPTION_WIDTH) {\n line = line ? `${line} ${word}` : word\n } else {\n lines.push(line)\n line = word\n }\n }\n\n if (line) lines.push(line)\n return lines.join('\\n')\n })\n .join('\\n\\n')\n}\n\nconst FRONTEND_DEV_COMMANDS: Record<\n NonNullable<ScaffoldResult['frontendPackageManager']>,\n string\n> = {\n bun: 'bun dev',\n manual: 'npm run dev',\n npm: 'npm run dev',\n pnpm: 'pnpm dev',\n yarn: 'yarn dev',\n}\n\nfunction frontendDevCommand(packageManager: ScaffoldResult['frontendPackageManager']): string {\n return packageManager ? FRONTEND_DEV_COMMANDS[packageManager] : 'npm run dev'\n}\n\nfunction frontendInstallCommand(packageManager: ScaffoldResult['frontendPackageManager']): string {\n switch (packageManager) {\n case 'bun': {\n return 'bun add next-sanity'\n }\n case 'pnpm': {\n return 'pnpm add --save-prod next-sanity'\n }\n case 'yarn': {\n return 'yarn add next-sanity'\n }\n default: {\n return 'npm install next-sanity'\n }\n }\n}\n\nexport interface NewProjectResult {\n apiHost: string\n claimApiUrl: string\n claimToken: string\n claimUrl: string\n dataset: string\n expiresAt: string\n projectId: string\n terms: {\n notice: string\n url: string\n }\n token: string\n}\n\nfunction toResult(project: MintedProject): NewProjectResult {\n return {\n apiHost: project.apiHost,\n claimApiUrl: project.claimApiUrl,\n claimToken: project.claimToken,\n claimUrl: project.claimUrl,\n dataset: project.datasetName,\n expiresAt: project.expiresAt,\n projectId: project.resourceId,\n terms: {notice: project.termsNotice, url: project.termsUrl},\n token: project.token,\n }\n}\n\nexport class NewCommand extends SanityCommand<typeof NewCommand> {\n static override args = {\n projectName: Args.string({\n description: 'Display name for the new project',\n required: false,\n }),\n }\n\n static override description = formatHelpDescription(\n `Sets up two folders here: ./${STUDIO_DIR}, a Studio where you write and edit your content, ` +\n `and ./${FRONTEND_DIR}, a Next.js website that reads it. Both are already connected to your ` +\n 'new project, so you can start them straight away. Use --no-scaffold if you just want the ' +\n 'project and nothing else.',\n `The project is real and works immediately, but it is only yours for ${CLAIM_WINDOW_HOURS} hours. Claim it with ` +\n 'a Sanity account before the deadline and everything you have built stays exactly as it is. ' +\n 'Claiming is free and takes about a minute. Miss the deadline and the project and its ' +\n 'content are deleted.',\n 'Two things to keep private: the claim link, because anyone who opens it becomes the owner, ' +\n `and the access token saved in ./${STUDIO_ENV_FILE} and ./${FRONTEND_ENV_FILE}, because it ` +\n 'can read and change everything in the project. Keep both env files out of git, and never ' +\n 'put the token in code that runs in the browser.',\n `Fetch ${SANITY_NEW_URL} for full instructions, or point your AI agent at it.`,\n )\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Create a project with a Studio and a website',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My New Project\"',\n description: 'Create a project called \"My New Project\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --yes',\n description: 'Create a project without being asked anything',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --no-scaffold',\n description: 'Create the project only, with no Studio or website',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Create a project and print its details as JSON',\n },\n ]\n\n static override flags = {\n scaffold: Flags.boolean({\n allowNo: true,\n default: true,\n description: `Set up a Studio in ./${STUDIO_DIR} and a Next.js website in ./${FRONTEND_DIR} (on by default)`,\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: `Skip prompts and use defaults (project: \"${DEFAULT_PROJECT_NAME}\")`,\n }),\n }\n\n static override summary = `Create a Sanity project without an account, and claim it within ${CLAIM_WINDOW_HOURS} hours to keep it.`\n\n public async run(): Promise<NewProjectResult> {\n const {output} = this\n const json = this.jsonEnabled()\n const flow = createFlow(output.log)\n const invoked = [this.config.bin, ...(this.id?.split(':') ?? [])].join(' ')\n const cwd = process.cwd()\n\n const unavailableScaffoldTarget =\n !json && this.flags.scaffold ? await getUnavailableScaffoldTarget(cwd) : undefined\n if (unavailableScaffoldTarget) {\n throw new CLIError(\n `./${unavailableScaffoldTarget} is not an empty directory. No project was created.`,\n {\n code:\n unavailableScaffoldTarget === STUDIO_DIR\n ? 'EXISTING_STUDIO_DIRECTORY'\n : 'EXISTING_FRONTEND_DIRECTORY',\n exit: exitCodes.RUNTIME_ERROR,\n suggestions: [\n `Run this command where ./${unavailableScaffoldTarget} does not exist or is an empty directory`,\n `Or run \\`${invoked} --no-scaffold\\` to create the project without changing ./${unavailableScaffoldTarget}`,\n ],\n },\n )\n }\n\n if (!json) {\n renderNewCommandSplash(output.log)\n flow.intro('Setting up your Sanity project.')\n flow.gap()\n\n if (!this.isUnattended()) {\n flow.note(\n `${styleText('cyan', `${invoked} --yes`)} for a non-interactive flow with defaults.`,\n )\n flow.gap()\n }\n }\n\n let displayName = this.args.projectName?.trim()\n if (!displayName && this.isUnattended()) {\n displayName = DEFAULT_PROJECT_NAME\n } else if (!displayName) {\n displayName =\n (await input({default: DEFAULT_PROJECT_NAME, message: 'Project name'})).trim() ||\n DEFAULT_PROJECT_NAME\n }\n\n const spin = json ? undefined : flow.spin('Creating your project...')\n let project: MintedProject\n try {\n project = await mintUnclaimedProject({displayName})\n } catch (err) {\n spin?.fail('Creating your project failed.')\n throw err\n }\n\n const recorded = recordUnclaimedProject(project)\n spin?.succeed(`Created \"${displayName}\"`)\n\n if (!json) {\n flow.result(`Project ID: ${styleText('cyan', project.resourceId)}`)\n flow.result(`Dataset: ${styleText('cyan', project.datasetName)} (where your content lives)`)\n flow.result(`Token: ${styleText('cyan', project.token)}`)\n flow.link(project.claimUrl, {label: 'Claim link:'})\n flow.gap()\n flow.highlight(\n `Claim your project by ${styleText('yellow', formatClaimDeadline(project.expiresAt))} to avoid losing it.`,\n )\n flow.gap()\n flow.line('To use the Sanity CLI before claiming use your token by setting')\n flow.command(`SANITY_AUTH_TOKEN=\"${project.token}\" sanity \"command\"`)\n flow.gap()\n flow.note(\n styleText(\n 'dim',\n 'If you are an agent: give this claim URL to the person you are working for. They have ' +\n 'to open it themselves before the deadline.',\n ),\n )\n flow.gap()\n }\n\n const printStudioInstructions = () => {\n flow.highlight('Start your Studio:')\n flow.gap()\n flow.command(`cd ${STUDIO_DIR} && npx sanity dev`)\n flow.gap()\n flow.link(`http://localhost:3333/#token=${encodeURIComponent(project.token)}`, {\n label: 'Then open this link:',\n })\n flow.gap()\n flow.line('The token signs you in: there is no account yet.')\n }\n const printWebsiteInstructions = (packageManager: ScaffoldResult['frontendPackageManager']) => {\n flow.highlight('In a separate terminal, start your website:')\n flow.gap()\n flow.command(`cd ${FRONTEND_DIR} && ${frontendDevCommand(packageManager)}`)\n flow.gap()\n flow.link('http://localhost:3000', {label: 'Then open this link:'})\n }\n\n let scaffold: ScaffoldResult | undefined\n if (!json && this.flags.scaffold) {\n const scaffoldController = new AbortController()\n const abortScaffold = () => scaffoldController.abort(new Error('SIGINT'))\n process.once('SIGINT', abortScaffold)\n try {\n scaffold = await scaffoldProject({\n cancelSignal: scaffoldController.signal,\n dataset: project.datasetName,\n displayName,\n output,\n projectId: project.resourceId,\n telemetry: this.telemetry,\n token: project.token,\n workDir: cwd,\n })\n scaffoldController.signal.throwIfAborted()\n } catch (err) {\n if (\n scaffoldController.signal.aborted ||\n (err instanceof Error && err.message === 'SIGINT')\n ) {\n throw err\n }\n // Scaffolding is best-effort and may leave files or directories behind when it fails.\n // Partial filesystem state does not prove the scaffold is runnable, so report the failure,\n // continue only with the project/token/claim handoff below, and do not attempt cleanup.\n flow.note(`Automatic setup did not finish: ${err instanceof Error ? err.message : err}`)\n flow.line('The project was created. You do not need to create another one.')\n flow.gap()\n } finally {\n process.off('SIGINT', abortScaffold)\n }\n }\n\n if (scaffold) {\n if (scaffold.frontendPath) {\n flow.result('Created two folders')\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(`./${FRONTEND_DIR}: your website, a Next.js app that reads it`)\n flow.gap()\n printStudioInstructions()\n if (scaffold.frontendDependenciesInstalled === false) {\n flow.gap()\n if (scaffold.frontendDependencyError) {\n flow.line(`Installing website dependencies failed: ${scaffold.frontendDependencyError}`)\n flow.gap()\n }\n flow.highlight('Finish installing your website dependencies')\n flow.gap()\n flow.command(\n `cd ${FRONTEND_DIR} && ${frontendInstallCommand(scaffold.frontendPackageManager)}`,\n )\n }\n flow.gap()\n printWebsiteInstructions(scaffold.frontendPackageManager)\n } else if (scaffold.detectedFramework) {\n flow.result(`Created ./${STUDIO_DIR} for your existing ${scaffold.detectedFramework} app`)\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(`Your existing ${scaffold.detectedFramework} frontend was left unchanged.`)\n flow.gap()\n printStudioInstructions()\n } else {\n flow.result(`Created ./${STUDIO_DIR}`)\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(\n `The website was not created: ${scaffold.frontendCreationError ?? 'automatic setup did not finish'}`,\n )\n flow.gap()\n printStudioInstructions()\n }\n flow.gap()\n }\n\n if (recorded === false && !json) {\n flow.note('The local recovery record was not saved.')\n flow.line('Keep the claim URL and access token from this output.')\n flow.gap()\n }\n\n if (!json) {\n if (!scaffold && !this.flags.scaffold) {\n flow.result('Project created without scaffolding')\n flow.line(\n 'No folders or env files were created. Use the project ID and dataset above in your own setup.',\n )\n flow.gap()\n flow.link(`http://localhost:3333/#token=${encodeURIComponent(project.token)}`, {\n label: 'Once a Studio is running on http://localhost:3333, sign in with:',\n })\n flow.gap()\n flow.line('The token signs you in: there is no account yet.')\n flow.gap()\n }\n\n flow.line('Your content is private until you claim, to read it, you need the token.')\n flow.line(\"Treat your token as a password and don't expose it publicly in your app.\")\n flow.gap()\n flow.line(\n 'The claim link and token are printed above but you can recover them ' +\n `with ${styleText('cyan', 'sanity projects unclaimed')} before the timer runs out on ${formatClaimDeadline(project.expiresAt)}.`,\n )\n flow.gap()\n flow.line(project.termsNotice)\n flow.link(project.termsUrl, {label: 'Terms of Service:'})\n flow.gap()\n flow.line('For more information on how claiming works or how to build your app')\n flow.link(SANITY_NEW_URL, {label: 'visit', outro: true})\n }\n\n return toResult(project)\n }\n}\n"],"names":["styleText","Args","Flags","CLIError","exitCodes","SanityCommand","createFlow","input","FRONTEND_DIR","FRONTEND_ENV_FILE","getUnavailableScaffoldTarget","scaffoldProject","STUDIO_DIR","STUDIO_ENV_FILE","mintUnclaimedProject","formatClaimDeadline","CLAIM_WINDOW_HOURS","SANITY_NEW_URL","renderNewCommandSplash","recordUnclaimedProject","DEFAULT_PROJECT_NAME","HELP_DESCRIPTION_WIDTH","formatHelpDescription","paragraphs","map","paragraph","lines","line","word","split","length","push","join","FRONTEND_DEV_COMMANDS","bun","manual","npm","pnpm","yarn","frontendDevCommand","packageManager","frontendInstallCommand","toResult","project","apiHost","claimApiUrl","claimToken","claimUrl","dataset","datasetName","expiresAt","projectId","resourceId","terms","notice","termsNotice","url","termsUrl","token","NewCommand","args","projectName","string","description","required","enableJsonFlag","examples","command","flags","scaffold","boolean","allowNo","default","yes","char","summary","run","output","json","jsonEnabled","flow","log","invoked","config","bin","id","cwd","process","unavailableScaffoldTarget","undefined","code","exit","RUNTIME_ERROR","suggestions","intro","gap","isUnattended","note","displayName","trim","message","spin","err","fail","recorded","succeed","result","link","label","highlight","printStudioInstructions","encodeURIComponent","printWebsiteInstructions","scaffoldController","AbortController","abortScaffold","abort","Error","once","cancelSignal","signal","telemetry","workDir","throwIfAborted","aborted","off","frontendPath","frontendDependenciesInstalled","frontendDependencyError","frontendPackageManager","detectedFramework","frontendCreationError","outro"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/commands/new.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {createFlow, input} from '@sanity/cli-core/ux'\n\nimport {\n FRONTEND_DIR,\n FRONTEND_ENV_FILE,\n getUnavailableScaffoldTarget,\n scaffoldProject,\n type ScaffoldResult,\n STUDIO_DIR,\n STUDIO_ENV_FILE,\n} from '../actions/scaffold/scaffoldProject.js'\nimport {type MintedProject, mintUnclaimedProject} from '../services/mintProject.js'\nimport {\n fetchNewInstructions,\n INSTRUCTIONS_URL,\n InstructionsUnavailableError,\n} from '../services/newInstructions.js'\nimport {formatClaimDeadline} from '../util/formatClaimDeadline.js'\nimport {CLAIM_WINDOW_HOURS, SANITY_NEW_URL} from '../util/mintProjectConstants.js'\nimport {renderNewCommandSplash} from '../util/newCommandSplash.js'\nimport {recordUnclaimedProject} from '../util/unclaimedProjects.js'\n\nconst DEFAULT_PROJECT_NAME = 'My Sanity project'\nconst HELP_DESCRIPTION_WIDTH = 78\n\n/**\n * Prepended to the fetched instructions so an agent cannot read this command's success as the\n * project already existing. The only addition to an otherwise verbatim document.\n */\nconst INSTRUCTIONS_HEADER = '# Nothing has been created yet — follow these steps.'\n\nfunction formatHelpDescription(...paragraphs: string[]): string {\n return paragraphs\n .map((paragraph) => {\n const lines: string[] = []\n let line = ''\n\n for (const word of paragraph.split(/\\s+/u)) {\n if (!line || line.length + word.length + 1 <= HELP_DESCRIPTION_WIDTH) {\n line = line ? `${line} ${word}` : word\n } else {\n lines.push(line)\n line = word\n }\n }\n\n if (line) lines.push(line)\n return lines.join('\\n')\n })\n .join('\\n\\n')\n}\n\nconst FRONTEND_DEV_COMMANDS: Record<\n NonNullable<ScaffoldResult['frontendPackageManager']>,\n string\n> = {\n bun: 'bun dev',\n manual: 'npm run dev',\n npm: 'npm run dev',\n pnpm: 'pnpm dev',\n yarn: 'yarn dev',\n}\n\nfunction frontendDevCommand(packageManager: ScaffoldResult['frontendPackageManager']): string {\n return packageManager ? FRONTEND_DEV_COMMANDS[packageManager] : 'npm run dev'\n}\n\nfunction frontendInstallCommand(packageManager: ScaffoldResult['frontendPackageManager']): string {\n switch (packageManager) {\n case 'bun': {\n return 'bun add next-sanity'\n }\n case 'pnpm': {\n return 'pnpm add --save-prod next-sanity'\n }\n case 'yarn': {\n return 'yarn add next-sanity'\n }\n default: {\n return 'npm install next-sanity'\n }\n }\n}\n\nexport interface NewProjectResult {\n apiHost: string\n claimApiUrl: string\n claimToken: string\n claimUrl: string\n dataset: string\n expiresAt: string\n projectId: string\n terms: {\n notice: string\n url: string\n }\n token: string\n}\n\nfunction toResult(project: MintedProject): NewProjectResult {\n return {\n apiHost: project.apiHost,\n claimApiUrl: project.claimApiUrl,\n claimToken: project.claimToken,\n claimUrl: project.claimUrl,\n dataset: project.datasetName,\n expiresAt: project.expiresAt,\n projectId: project.resourceId,\n terms: {notice: project.termsNotice, url: project.termsUrl},\n token: project.token,\n }\n}\n\nexport class NewCommand extends SanityCommand<typeof NewCommand> {\n static override args = {\n projectName: Args.string({\n description: 'Display name for the new project',\n required: false,\n }),\n }\n\n static override description = formatHelpDescription(\n `Sets up two folders here: ./${STUDIO_DIR}, a Studio where you write and edit your content, ` +\n `and ./${FRONTEND_DIR}, a Next.js website that reads it. Both are already connected to your ` +\n 'new project, so you can start them straight away. Use --no-scaffold if you just want the ' +\n 'project and nothing else.',\n `The project is real and works immediately, but it is only yours for ${CLAIM_WINDOW_HOURS} hours. Claim it with ` +\n 'a Sanity account before the deadline and everything you have built stays exactly as it is. ' +\n 'Claiming is free and takes about a minute. Miss the deadline and the project and its ' +\n 'content are deleted.',\n 'Two things to keep private: the claim link, because anyone who opens it becomes the owner, ' +\n `and the access token saved in ./${STUDIO_ENV_FILE} and ./${FRONTEND_ENV_FILE}, because it ` +\n 'can read and change everything in the project. Keep both env files out of git, and never ' +\n 'put the token in code that runs in the browser.',\n 'Run this command with --instructions for the full agent setup guide.',\n )\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Create a project with a Studio and a website',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My New Project\"',\n description: 'Create a project called \"My New Project\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --yes',\n description: 'Create a project without being asked anything',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --no-scaffold',\n description: 'Create the project only, with no Studio or website',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Create a project and print its details as JSON',\n },\n\n {\n command: '<%= config.bin %> <%= command.id %> --instructions',\n description: 'Print the full setup guide for an AI agent, without creating anything',\n },\n ]\n\n static override flags = {\n instructions: Flags.boolean({\n default: false,\n description: `Print the full setup guide from ${SANITY_NEW_URL} and exit, creating nothing`,\n }),\n scaffold: Flags.boolean({\n allowNo: true,\n default: true,\n description: `Set up a Studio in ./${STUDIO_DIR} and a Next.js website in ./${FRONTEND_DIR} (on by default)`,\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: `Skip prompts and use defaults (project: \"${DEFAULT_PROJECT_NAME}\")`,\n }),\n }\n\n static override summary = `Create a Sanity project without an account, and claim it within ${CLAIM_WINDOW_HOURS} hours to keep it.`\n\n public async run(): Promise<NewProjectResult | undefined> {\n const {output} = this\n const json = this.jsonEnabled()\n const flow = createFlow(output.log)\n const invoked = [this.config.bin, ...(this.id?.split(':') ?? [])].join(' ')\n const cwd = process.cwd()\n\n // Runs before every precondition and side effect: this flag creates nothing.\n if (this.flags.instructions) {\n // oclif's `exclusive` does not cover the json flag added by enableJsonFlag, so guard it here.\n if (json) {\n throw new CLIError('--instructions cannot be combined with --json.', {\n code: 'INSTRUCTIONS_JSON_CONFLICT',\n exit: exitCodes.USAGE_ERROR,\n suggestions: [\n `Run \\`${invoked} --instructions\\` for the setup guide as markdown`,\n `Or run \\`${invoked} --json\\` to create a project and print its details as JSON`,\n ],\n })\n }\n\n await this.printInstructions()\n return undefined\n }\n\n const unavailableScaffoldTarget =\n !json && this.flags.scaffold ? await getUnavailableScaffoldTarget(cwd) : undefined\n if (unavailableScaffoldTarget) {\n throw new CLIError(\n `./${unavailableScaffoldTarget} is not an empty directory. No project was created.`,\n {\n code:\n unavailableScaffoldTarget === STUDIO_DIR\n ? 'EXISTING_STUDIO_DIRECTORY'\n : 'EXISTING_FRONTEND_DIRECTORY',\n exit: exitCodes.RUNTIME_ERROR,\n suggestions: [\n `Run this command where ./${unavailableScaffoldTarget} does not exist or is an empty directory`,\n `Or run \\`${invoked} --no-scaffold\\` to create the project without changing ./${unavailableScaffoldTarget}`,\n ],\n },\n )\n }\n\n if (!json) {\n renderNewCommandSplash(output.log)\n flow.intro('Setting up your Sanity project.')\n flow.gap()\n\n if (!this.isUnattended()) {\n flow.note(\n `${styleText('cyan', `${invoked} --yes`)} for a non-interactive flow with defaults.`,\n )\n flow.gap()\n }\n }\n\n let displayName = this.args.projectName?.trim()\n if (!displayName && this.isUnattended()) {\n displayName = DEFAULT_PROJECT_NAME\n } else if (!displayName) {\n displayName =\n (\n await input({\n default: DEFAULT_PROJECT_NAME,\n message: 'Project name',\n })\n ).trim() || DEFAULT_PROJECT_NAME\n }\n\n const spin = json ? undefined : flow.spin('Creating your project...')\n let project: MintedProject\n try {\n project = await mintUnclaimedProject({displayName})\n } catch (err) {\n spin?.fail('Creating your project failed.')\n throw err\n }\n\n const recorded = recordUnclaimedProject(project)\n spin?.succeed(`Created \"${displayName}\"`)\n\n if (!json) {\n flow.result(`Project ID: ${styleText('cyan', project.resourceId)}`)\n flow.result(`Dataset: ${styleText('cyan', project.datasetName)} (where your content lives)`)\n flow.result(`Token: ${styleText('cyan', project.token)}`)\n flow.link(project.claimUrl, {label: 'Claim link:'})\n flow.gap()\n flow.highlight(\n `Claim your project by ${styleText('yellow', formatClaimDeadline(project.expiresAt))} to avoid losing it.`,\n )\n flow.gap()\n flow.line('To use the Sanity CLI before claiming use your token by setting')\n flow.command(`SANITY_AUTH_TOKEN=\"${project.token}\" sanity \"command\"`)\n flow.gap()\n flow.note(\n styleText(\n 'dim',\n 'If you are an agent: give this claim URL to the person you are working for. They have ' +\n 'to open it themselves before the deadline.',\n ),\n )\n flow.gap()\n }\n\n const printStudioInstructions = () => {\n flow.highlight('Start your Studio:')\n flow.gap()\n flow.command(`cd ${STUDIO_DIR} && npx sanity dev`)\n flow.gap()\n flow.link(`http://localhost:3333/#token=${encodeURIComponent(project.token)}`, {\n label: 'Then open this link:',\n })\n flow.gap()\n flow.line('The token signs you in: there is no account yet.')\n }\n const printWebsiteInstructions = (packageManager: ScaffoldResult['frontendPackageManager']) => {\n flow.highlight('In a separate terminal, start your website:')\n flow.gap()\n flow.command(`cd ${FRONTEND_DIR} && ${frontendDevCommand(packageManager)}`)\n flow.gap()\n flow.link('http://localhost:3000', {label: 'Then open this link:'})\n }\n\n let scaffold: ScaffoldResult | undefined\n if (!json && this.flags.scaffold) {\n const scaffoldController = new AbortController()\n const abortScaffold = () => scaffoldController.abort(new Error('SIGINT'))\n process.once('SIGINT', abortScaffold)\n try {\n scaffold = await scaffoldProject({\n cancelSignal: scaffoldController.signal,\n dataset: project.datasetName,\n displayName,\n output,\n projectId: project.resourceId,\n telemetry: this.telemetry,\n token: project.token,\n workDir: cwd,\n })\n scaffoldController.signal.throwIfAborted()\n } catch (err) {\n if (\n scaffoldController.signal.aborted ||\n (err instanceof Error && err.message === 'SIGINT')\n ) {\n throw err\n }\n // Scaffolding is best-effort and may leave files or directories behind when it fails.\n // Partial filesystem state does not prove the scaffold is runnable, so report the failure,\n // continue only with the project/token/claim handoff below, and do not attempt cleanup.\n flow.note(`Automatic setup did not finish: ${err instanceof Error ? err.message : err}`)\n flow.line('The project was created. You do not need to create another one.')\n flow.gap()\n } finally {\n process.off('SIGINT', abortScaffold)\n }\n }\n\n if (scaffold) {\n if (scaffold.frontendPath) {\n flow.result('Created two folders')\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(`./${FRONTEND_DIR}: your website, a Next.js app that reads it`)\n flow.gap()\n printStudioInstructions()\n if (scaffold.frontendDependenciesInstalled === false) {\n flow.gap()\n if (scaffold.frontendDependencyError) {\n flow.line(`Installing website dependencies failed: ${scaffold.frontendDependencyError}`)\n flow.gap()\n }\n flow.highlight('Finish installing your website dependencies')\n flow.gap()\n flow.command(\n `cd ${FRONTEND_DIR} && ${frontendInstallCommand(scaffold.frontendPackageManager)}`,\n )\n }\n flow.gap()\n printWebsiteInstructions(scaffold.frontendPackageManager)\n } else if (scaffold.detectedFramework) {\n flow.result(`Created ./${STUDIO_DIR} for your existing ${scaffold.detectedFramework} app`)\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(`Your existing ${scaffold.detectedFramework} frontend was left unchanged.`)\n flow.gap()\n printStudioInstructions()\n } else {\n flow.result(`Created ./${STUDIO_DIR}`)\n flow.line(`./${STUDIO_DIR}: your Studio, where you write and edit content`)\n flow.line(\n `The website was not created: ${scaffold.frontendCreationError ?? 'automatic setup did not finish'}`,\n )\n flow.gap()\n printStudioInstructions()\n }\n flow.gap()\n }\n\n if (recorded === false && !json) {\n flow.note('The local recovery record was not saved.')\n flow.line('Keep the claim URL and access token from this output.')\n flow.gap()\n }\n\n if (!json) {\n if (!scaffold && !this.flags.scaffold) {\n flow.result('Project created without scaffolding')\n flow.line(\n 'No folders or env files were created. Use the project ID and dataset above in your own setup.',\n )\n flow.gap()\n flow.link(`http://localhost:3333/#token=${encodeURIComponent(project.token)}`, {\n label: 'Once a Studio is running on http://localhost:3333, sign in with:',\n })\n flow.gap()\n flow.line('The token signs you in: there is no account yet.')\n flow.gap()\n }\n\n flow.line('Your content is private until you claim, to read it, you need the token.')\n flow.line(\"Treat your token as a password and don't expose it publicly in your app.\")\n flow.gap()\n flow.line(\n 'The claim link and token are printed above but you can recover them ' +\n `with ${styleText('cyan', 'sanity projects unclaimed')} before the timer runs out on ${formatClaimDeadline(project.expiresAt)}.`,\n )\n flow.gap()\n flow.line(project.termsNotice)\n flow.link(project.termsUrl, {label: 'Terms of Service:'})\n flow.gap()\n flow.line('For more information on how claiming works or how to build your app')\n flow.link(SANITY_NEW_URL, {label: 'visit', outro: true})\n }\n\n return toResult(project)\n }\n\n /**\n * Dumps the guide from sanity.new verbatim, so an agent gets the whole document rather than a\n * summary produced by whatever fetched it.\n */\n private async printInstructions(): Promise<void> {\n let instructions: string\n try {\n instructions = await fetchNewInstructions()\n } catch (err) {\n if (err instanceof InstructionsUnavailableError) {\n throw new CLIError(err.message, {\n code: 'INSTRUCTIONS_UNAVAILABLE',\n exit: exitCodes.RUNTIME_ERROR,\n suggestions: [\n `Fetch them directly: curl -sSL ${INSTRUCTIONS_URL}`,\n `Or open ${SANITY_NEW_URL} in a browser`,\n ],\n })\n }\n throw err\n }\n\n this.output.log(`${INSTRUCTIONS_HEADER}\\n\\n${instructions}`)\n }\n}\n"],"names":["styleText","Args","Flags","CLIError","exitCodes","SanityCommand","createFlow","input","FRONTEND_DIR","FRONTEND_ENV_FILE","getUnavailableScaffoldTarget","scaffoldProject","STUDIO_DIR","STUDIO_ENV_FILE","mintUnclaimedProject","fetchNewInstructions","INSTRUCTIONS_URL","InstructionsUnavailableError","formatClaimDeadline","CLAIM_WINDOW_HOURS","SANITY_NEW_URL","renderNewCommandSplash","recordUnclaimedProject","DEFAULT_PROJECT_NAME","HELP_DESCRIPTION_WIDTH","INSTRUCTIONS_HEADER","formatHelpDescription","paragraphs","map","paragraph","lines","line","word","split","length","push","join","FRONTEND_DEV_COMMANDS","bun","manual","npm","pnpm","yarn","frontendDevCommand","packageManager","frontendInstallCommand","toResult","project","apiHost","claimApiUrl","claimToken","claimUrl","dataset","datasetName","expiresAt","projectId","resourceId","terms","notice","termsNotice","url","termsUrl","token","NewCommand","args","projectName","string","description","required","enableJsonFlag","examples","command","flags","instructions","boolean","default","scaffold","allowNo","yes","char","summary","run","output","json","jsonEnabled","flow","log","invoked","config","bin","id","cwd","process","code","exit","USAGE_ERROR","suggestions","printInstructions","undefined","unavailableScaffoldTarget","RUNTIME_ERROR","intro","gap","isUnattended","note","displayName","trim","message","spin","err","fail","recorded","succeed","result","link","label","highlight","printStudioInstructions","encodeURIComponent","printWebsiteInstructions","scaffoldController","AbortController","abortScaffold","abort","Error","once","cancelSignal","signal","telemetry","workDir","throwIfAborted","aborted","off","frontendPath","frontendDependenciesInstalled","frontendDependencyError","frontendPackageManager","detectedFramework","frontendCreationError","outro"],"mappings":"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"}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
function apiValidator({ args, flags }) {
|
|
3
|
+
const fields = Array.isArray(flags.field) ? flags.field : [];
|
|
4
|
+
const fieldReadsFromHost = fields.some((field)=>{
|
|
5
|
+
if (typeof field !== 'string') return false;
|
|
6
|
+
const separatorIndex = field.indexOf('=');
|
|
7
|
+
return separatorIndex > 0 && field[separatorIndex + 1] === '@';
|
|
8
|
+
});
|
|
9
|
+
if (fieldReadsFromHost) return false;
|
|
10
|
+
const headers = Array.isArray(flags.header) ? flags.header : [];
|
|
11
|
+
const headerOverridesAuthentication = headers.some((header)=>{
|
|
12
|
+
if (typeof header !== 'string') return false;
|
|
13
|
+
const separatorIndex = header.indexOf(':');
|
|
14
|
+
if (separatorIndex <= 0) return false;
|
|
15
|
+
const name = header.slice(0, separatorIndex).trim().toLowerCase();
|
|
16
|
+
return name === 'authorization' || name === 'cookie';
|
|
17
|
+
});
|
|
18
|
+
if (headerOverridesAuthentication) return false;
|
|
19
|
+
const endpoint = typeof args.endpoint === 'string' ? URL.parse(args.endpoint) : null;
|
|
20
|
+
const urlEmbedsCredentials = endpoint !== null && (endpoint.username !== '' || endpoint.password !== '');
|
|
21
|
+
return !urlEmbedsCredentials;
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
16
24
|
* MCP programmatic mode disables local project/config discovery (see the CLI
|
|
@@ -25,7 +33,8 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
|
|
|
25
33
|
*/ export const mcpPolicy = {
|
|
26
34
|
// Special exception, this can be very dangerous but is also super useful
|
|
27
35
|
// to expose. Refuse authentication overrides and host input channels:
|
|
28
|
-
// `--token
|
|
36
|
+
// `--token`, Authorization, and Cookie headers replace the MCP user's authentication,
|
|
37
|
+
// URL-embedded credentials replace it with Basic authentication,
|
|
29
38
|
// `--input` reads the request body from the host's filesystem or stdin, and
|
|
30
39
|
// `-F key=@<file>` / `-F key=@-` field values do the same.
|
|
31
40
|
api: conditionalPolicy({
|
|
@@ -33,7 +42,7 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
|
|
|
33
42
|
'input',
|
|
34
43
|
'token'
|
|
35
44
|
],
|
|
36
|
-
validate:
|
|
45
|
+
validate: apiValidator
|
|
37
46
|
}),
|
|
38
47
|
'backups:disable': allow,
|
|
39
48
|
// Writes a downloaded backup to the local filesystem.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/exports/invokeSanityCli/commandPolicies/mcpPolicy.ts"],"sourcesContent":["import {\n allow,\n type CommandPolicySet,\n conditionalDenyFlags,\n conditionalPolicy,\n deny,\n} from './policy.js'\n\n/**\n * A typed `-F/--field` value of `@<file>` or `@-` makes the `api` command\n * read the host's filesystem or stdin (raw `-f` fields are always verbatim).\n */\nfunction fieldReadsFromHost(field: unknown): boolean {\n if (typeof field !== 'string') return false\n const separatorIndex = field.indexOf('=')\n return separatorIndex > 0 && field[separatorIndex + 1] === '@'\n}\n\n/** A `-H/--header` value that replaces the invocation's authentication. */\nfunction headerOverridesAuthentication(header: unknown): boolean {\n if (typeof header !== 'string') return false\n const separatorIndex = header.indexOf(':')\n return (\n separatorIndex > 0 && header.slice(0, separatorIndex).trim().toLowerCase() === 'authorization'\n )\n}\n\n/**\n * MCP programmatic mode disables local project/config discovery (see the CLI\n * execution context). Missing project or dataset values may therefore produce\n * a usage error, but cannot cause local filesystem access. Destructive remote\n * operations are allowed and do not by themselves make a command unsafe.\n *\n * Every manifest command must have exactly one policy here:\n * - allow: every valid invocation is safe\n * - conditional: safety depends on parsed arguments or flags\n * - deny: no invocation is safe\n */\nexport const mcpPolicy: CommandPolicySet = {\n // Special exception, this can be very dangerous but is also super useful\n // to expose. Refuse authentication overrides and host input channels:\n // `--token` and an Authorization header replace the MCP user's token,\n // `--input` reads the request body from the host's filesystem or stdin, and\n // `-F key=@<file>` / `-F key=@-` field values do the same.\n api: conditionalPolicy({\n deniedFlags: ['input', 'token'],\n validate: ({flags}) =>\n (!Array.isArray(flags.field) || !flags.field.some((field) => fieldReadsFromHost(field))) &&\n (!Array.isArray(flags.header) ||\n !flags.header.some((header) => headerOverridesAuthentication(header))),\n }),\n\n 'backups:disable': allow,\n // Writes a downloaded backup to the local filesystem.\n 'backups:download': deny,\n 'backups:enable': allow,\n 'backups:list': allow,\n\n // Requires a local Studio project and writes build output to disk.\n build: deny,\n\n // Reads and rewrites local source code.\n codemod: deny,\n\n 'cors:add': allow,\n 'cors:delete': allow,\n 'cors:list': allow,\n\n 'datasets:alias:create': allow,\n 'datasets:alias:delete': allow,\n 'datasets:alias:link': allow,\n 'datasets:alias:unlink': allow,\n 'datasets:copy': allow,\n 'datasets:create': allow,\n 'datasets:delete': allow,\n 'datasets:embeddings:disable': allow,\n 'datasets:embeddings:enable': allow,\n 'datasets:embeddings:status': allow,\n // Writes dataset contents and assets to the local filesystem.\n 'datasets:export': deny,\n // Reads import data from the local filesystem and may replace documents.\n 'datasets:import': deny,\n 'datasets:list': allow,\n 'datasets:visibility:get': allow,\n 'datasets:visibility:set': allow,\n\n // Inspects local project files and can print authentication secrets.\n debug: deny,\n\n // Reads, builds, and deploys a local Studio project.\n deploy: deny,\n\n // Loads a local Studio project and starts a development server.\n dev: deny,\n\n // Opens a browser on the machine running the MCP server.\n 'docs:browse': deny,\n // --web opens a browser on the machine running the MCP server.\n 'docs:read': conditionalDenyFlags('web'),\n 'docs:search': allow,\n\n // Reads and executes local project configuration for diagnostics.\n doctor: deny,\n\n // Reads document input from disk or launches a local editor.\n 'documents:create': deny,\n 'documents:delete': allow,\n 'documents:get': allow,\n 'documents:query': allow,\n // Loads a local Studio schema to validate documents.\n 'documents:validate': deny,\n\n // Executes arbitrary code in the local Studio context.\n exec: deny,\n\n // Loads a local schema and deploys a GraphQL API.\n 'graphql:deploy': deny,\n 'graphql:list': allow,\n // --api loads GraphQL definitions from the local project; explicit project/dataset flags are safe.\n 'graphql:undeploy': conditionalDenyFlags('api'),\n\n 'hooks:attempt': allow,\n 'hooks:create': allow,\n 'hooks:delete': allow,\n 'hooks:list': allow,\n 'hooks:logs': allow,\n\n // Creates or modifies a local project and may install dependencies.\n init: deny,\n\n // Installs packages into the local project.\n install: deny,\n\n // Opens a browser on the machine running the MCP server.\n learn: deny,\n\n // Performs an authentication flow.\n login: deny,\n\n // Performs an authentication operation and clears local credentials.\n logout: deny,\n\n // Reads local project configuration and opens a browser.\n manage: deny,\n\n // Loads local Studio configuration and writes manifest files.\n 'manifest:extract': deny,\n\n // Reads and writes local MCP client configuration.\n 'mcp:configure': deny,\n\n // Writes an aspect definition to the local filesystem.\n 'media:create-aspect': deny,\n 'media:delete-aspect': allow,\n // Reads a local aspect definition before deploying it.\n 'media:deploy-aspect': deny,\n // Writes media assets to the local filesystem.\n 'media:export': deny,\n // Reads media assets from the local filesystem.\n 'media:import': deny,\n\n // Creates migration source files in the local project.\n 'migrations:create': deny,\n // Reads and loads migration definitions from the local project.\n 'migrations:list': deny,\n // Executes local migration code that may perform arbitrary document mutations.\n 'migrations:run': deny,\n\n // Writes project setup values and scaffolds local applications.\n new: deny,\n\n // --web opens a browser on the machine running the MCP server.\n 'openapi:get': conditionalDenyFlags('web'),\n 'openapi:list': conditionalDenyFlags('web'),\n\n 'organizations:create': allow,\n 'organizations:delete': allow,\n 'organizations:get': allow,\n 'organizations:list': allow,\n 'organizations:update': allow,\n\n // Serves a local production build.\n preview: deny,\n\n 'projects:create': allow,\n 'projects:list': allow,\n // Reads locally stored claim URLs and robot tokens.\n 'projects:unclaimed': deny,\n\n // Loads the local Studio configuration to resolve the datasets containing each schema.\n 'schemas:delete': deny,\n // Loads local schema files before deploying them.\n 'schemas:deploy': deny,\n // Loads local Studio configuration and writes an extracted schema to disk.\n 'schemas:extract': deny,\n // Requires a local project and loads its schema configuration.\n 'schemas:list': deny,\n // Loads and executes a local Studio schema.\n 'schemas:validate': deny,\n\n // Installs skills into local editor configuration directories.\n 'skills:install': deny,\n\n // Changes account telemetry preferences and mutates local cached configuration.\n 'telemetry:disable': deny,\n // Changes account telemetry preferences and mutates local cached configuration.\n 'telemetry:enable': deny,\n 'telemetry:status': allow,\n\n // Creates authentication credentials.\n 'tokens:create': deny,\n // Deletes authentication credentials.\n 'tokens:delete': deny,\n // Exposes authentication credential metadata.\n 'tokens:list': deny,\n\n // Loads local CLI and workbench configuration to identify the deployed Studio or application.\n undeploy: deny,\n\n // Grants a user access to a project.\n 'users:invite': deny,\n 'users:list': allow,\n\n // Reads the local project and installed package tree.\n versions: deny,\n}\n"],"names":["allow","conditionalDenyFlags","conditionalPolicy","deny","fieldReadsFromHost","field","separatorIndex","indexOf","headerOverridesAuthentication","header","slice","trim","toLowerCase","mcpPolicy","api","deniedFlags","validate","flags","Array","isArray","some","build","codemod","debug","deploy","dev","doctor","exec","init","install","learn","login","logout","manage","new","preview","undeploy","versions"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/exports/invokeSanityCli/commandPolicies/mcpPolicy.ts"],"sourcesContent":["import {\n allow,\n type CommandPolicySet,\n conditionalDenyFlags,\n conditionalPolicy,\n deny,\n} from './policy.js'\n\nfunction apiValidator({\n args,\n flags,\n}: {\n args: Readonly<Record<string, unknown>>\n flags: Readonly<Record<string, unknown>>\n}): boolean {\n const fields: unknown[] = Array.isArray(flags.field) ? flags.field : []\n\n const fieldReadsFromHost = fields.some((field) => {\n if (typeof field !== 'string') return false\n const separatorIndex = field.indexOf('=')\n return separatorIndex > 0 && field[separatorIndex + 1] === '@'\n })\n if (fieldReadsFromHost) return false\n\n const headers: unknown[] = Array.isArray(flags.header) ? flags.header : []\n\n const headerOverridesAuthentication = headers.some((header) => {\n if (typeof header !== 'string') return false\n const separatorIndex = header.indexOf(':')\n if (separatorIndex <= 0) return false\n\n const name = header.slice(0, separatorIndex).trim().toLowerCase()\n return name === 'authorization' || name === 'cookie'\n })\n\n if (headerOverridesAuthentication) return false\n\n const endpoint = typeof args.endpoint === 'string' ? URL.parse(args.endpoint) : null\n const urlEmbedsCredentials =\n endpoint !== null && (endpoint.username !== '' || endpoint.password !== '')\n\n return !urlEmbedsCredentials\n}\n\n/**\n * MCP programmatic mode disables local project/config discovery (see the CLI\n * execution context). Missing project or dataset values may therefore produce\n * a usage error, but cannot cause local filesystem access. Destructive remote\n * operations are allowed and do not by themselves make a command unsafe.\n *\n * Every manifest command must have exactly one policy here:\n * - allow: every valid invocation is safe\n * - conditional: safety depends on parsed arguments or flags\n * - deny: no invocation is safe\n */\nexport const mcpPolicy: CommandPolicySet = {\n // Special exception, this can be very dangerous but is also super useful\n // to expose. Refuse authentication overrides and host input channels:\n // `--token`, Authorization, and Cookie headers replace the MCP user's authentication,\n // URL-embedded credentials replace it with Basic authentication,\n // `--input` reads the request body from the host's filesystem or stdin, and\n // `-F key=@<file>` / `-F key=@-` field values do the same.\n api: conditionalPolicy({\n deniedFlags: ['input', 'token'],\n validate: apiValidator,\n }),\n\n 'backups:disable': allow,\n // Writes a downloaded backup to the local filesystem.\n 'backups:download': deny,\n 'backups:enable': allow,\n 'backups:list': allow,\n\n // Requires a local Studio project and writes build output to disk.\n build: deny,\n\n // Reads and rewrites local source code.\n codemod: deny,\n\n 'cors:add': allow,\n 'cors:delete': allow,\n 'cors:list': allow,\n\n 'datasets:alias:create': allow,\n 'datasets:alias:delete': allow,\n 'datasets:alias:link': allow,\n 'datasets:alias:unlink': allow,\n 'datasets:copy': allow,\n 'datasets:create': allow,\n 'datasets:delete': allow,\n 'datasets:embeddings:disable': allow,\n 'datasets:embeddings:enable': allow,\n 'datasets:embeddings:status': allow,\n // Writes dataset contents and assets to the local filesystem.\n 'datasets:export': deny,\n // Reads import data from the local filesystem and may replace documents.\n 'datasets:import': deny,\n 'datasets:list': allow,\n 'datasets:visibility:get': allow,\n 'datasets:visibility:set': allow,\n\n // Inspects local project files and can print authentication secrets.\n debug: deny,\n\n // Reads, builds, and deploys a local Studio project.\n deploy: deny,\n\n // Loads a local Studio project and starts a development server.\n dev: deny,\n\n // Opens a browser on the machine running the MCP server.\n 'docs:browse': deny,\n // --web opens a browser on the machine running the MCP server.\n 'docs:read': conditionalDenyFlags('web'),\n 'docs:search': allow,\n\n // Reads and executes local project configuration for diagnostics.\n doctor: deny,\n\n // Reads document input from disk or launches a local editor.\n 'documents:create': deny,\n 'documents:delete': allow,\n 'documents:get': allow,\n 'documents:query': allow,\n // Loads a local Studio schema to validate documents.\n 'documents:validate': deny,\n\n // Executes arbitrary code in the local Studio context.\n exec: deny,\n\n // Loads a local schema and deploys a GraphQL API.\n 'graphql:deploy': deny,\n 'graphql:list': allow,\n // --api loads GraphQL definitions from the local project; explicit project/dataset flags are safe.\n 'graphql:undeploy': conditionalDenyFlags('api'),\n\n 'hooks:attempt': allow,\n 'hooks:create': allow,\n 'hooks:delete': allow,\n 'hooks:list': allow,\n 'hooks:logs': allow,\n\n // Creates or modifies a local project and may install dependencies.\n init: deny,\n\n // Installs packages into the local project.\n install: deny,\n\n // Opens a browser on the machine running the MCP server.\n learn: deny,\n\n // Performs an authentication flow.\n login: deny,\n\n // Performs an authentication operation and clears local credentials.\n logout: deny,\n\n // Reads local project configuration and opens a browser.\n manage: deny,\n\n // Loads local Studio configuration and writes manifest files.\n 'manifest:extract': deny,\n\n // Reads and writes local MCP client configuration.\n 'mcp:configure': deny,\n\n // Writes an aspect definition to the local filesystem.\n 'media:create-aspect': deny,\n 'media:delete-aspect': allow,\n // Reads a local aspect definition before deploying it.\n 'media:deploy-aspect': deny,\n // Writes media assets to the local filesystem.\n 'media:export': deny,\n // Reads media assets from the local filesystem.\n 'media:import': deny,\n\n // Creates migration source files in the local project.\n 'migrations:create': deny,\n // Reads and loads migration definitions from the local project.\n 'migrations:list': deny,\n // Executes local migration code that may perform arbitrary document mutations.\n 'migrations:run': deny,\n\n // Writes project setup values and scaffolds local applications.\n new: deny,\n\n // --web opens a browser on the machine running the MCP server.\n 'openapi:get': conditionalDenyFlags('web'),\n 'openapi:list': conditionalDenyFlags('web'),\n\n 'organizations:create': allow,\n 'organizations:delete': allow,\n 'organizations:get': allow,\n 'organizations:list': allow,\n 'organizations:update': allow,\n\n // Serves a local production build.\n preview: deny,\n\n 'projects:create': allow,\n 'projects:list': allow,\n // Reads locally stored claim URLs and robot tokens.\n 'projects:unclaimed': deny,\n\n // Loads the local Studio configuration to resolve the datasets containing each schema.\n 'schemas:delete': deny,\n // Loads local schema files before deploying them.\n 'schemas:deploy': deny,\n // Loads local Studio configuration and writes an extracted schema to disk.\n 'schemas:extract': deny,\n // Requires a local project and loads its schema configuration.\n 'schemas:list': deny,\n // Loads and executes a local Studio schema.\n 'schemas:validate': deny,\n\n // Installs skills into local editor configuration directories.\n 'skills:install': deny,\n\n // Changes account telemetry preferences and mutates local cached configuration.\n 'telemetry:disable': deny,\n // Changes account telemetry preferences and mutates local cached configuration.\n 'telemetry:enable': deny,\n 'telemetry:status': allow,\n\n // Creates authentication credentials.\n 'tokens:create': deny,\n // Deletes authentication credentials.\n 'tokens:delete': deny,\n // Exposes authentication credential metadata.\n 'tokens:list': deny,\n\n // Loads local CLI and workbench configuration to identify the deployed Studio or application.\n undeploy: deny,\n\n // Grants a user access to a project.\n 'users:invite': deny,\n 'users:list': allow,\n\n // Reads the local project and installed package tree.\n versions: deny,\n}\n"],"names":["allow","conditionalDenyFlags","conditionalPolicy","deny","apiValidator","args","flags","fields","Array","isArray","field","fieldReadsFromHost","some","separatorIndex","indexOf","headers","header","headerOverridesAuthentication","name","slice","trim","toLowerCase","endpoint","URL","parse","urlEmbedsCredentials","username","password","mcpPolicy","api","deniedFlags","validate","build","codemod","debug","deploy","dev","doctor","exec","init","install","learn","login","logout","manage","new","preview","undeploy","versions"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { subdebug } from '@sanity/cli-core/debug';
|
|
2
|
+
import { SANITY_NEW_URL } from '../util/mintProjectConstants.js';
|
|
3
|
+
const debug = subdebug('new:instructions');
|
|
4
|
+
const INSTRUCTIONS_TIMEOUT = 10_000;
|
|
5
|
+
/**
|
|
6
|
+
* Prerendered markdown route on sanity.new. Preferred over content negotiation on `/`, which
|
|
7
|
+
* depends on request headers and can fall through to the HTML page.
|
|
8
|
+
*/ export const INSTRUCTIONS_URL = `${SANITY_NEW_URL}/llms.txt`;
|
|
9
|
+
const HTML_PATTERN = /^\s*(?:<!doctype html|<html[\s>])/iu;
|
|
10
|
+
export class InstructionsUnavailableError extends Error {
|
|
11
|
+
constructor(reason){
|
|
12
|
+
super(`Could not fetch the setup instructions from ${INSTRUCTIONS_URL}: ${reason}`);
|
|
13
|
+
this.name = 'InstructionsUnavailableError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Fetches the agent setup instructions from sanity.new, which is the single source of truth for
|
|
18
|
+
* them. Deliberately not bundled with the CLI: a stale local copy is worse than a clear failure.
|
|
19
|
+
*/ export async function fetchNewInstructions() {
|
|
20
|
+
debug('fetching instructions from %s', INSTRUCTIONS_URL);
|
|
21
|
+
let response;
|
|
22
|
+
try {
|
|
23
|
+
response = await fetch(INSTRUCTIONS_URL, {
|
|
24
|
+
headers: {
|
|
25
|
+
Accept: 'text/markdown'
|
|
26
|
+
},
|
|
27
|
+
method: 'GET',
|
|
28
|
+
signal: AbortSignal.timeout(INSTRUCTIONS_TIMEOUT)
|
|
29
|
+
});
|
|
30
|
+
} catch (err) {
|
|
31
|
+
const reason = err instanceof Error && err.name === 'TimeoutError' ? `the request timed out after ${INSTRUCTIONS_TIMEOUT / 1000} seconds` : `${err instanceof Error ? err.message : err}`;
|
|
32
|
+
throw new InstructionsUnavailableError(reason);
|
|
33
|
+
}
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new InstructionsUnavailableError(`the server responded with HTTP ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
const body = (await response.text()).trim();
|
|
38
|
+
if (!body) {
|
|
39
|
+
throw new InstructionsUnavailableError('the response was empty');
|
|
40
|
+
}
|
|
41
|
+
if (HTML_PATTERN.test(body)) {
|
|
42
|
+
throw new InstructionsUnavailableError('the response was HTML rather than markdown');
|
|
43
|
+
}
|
|
44
|
+
debug('fetched %d characters', body.length);
|
|
45
|
+
return body;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=newInstructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/newInstructions.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core/debug'\n\nimport {SANITY_NEW_URL} from '../util/mintProjectConstants.js'\n\nconst debug = subdebug('new:instructions')\n\nconst INSTRUCTIONS_TIMEOUT = 10_000\n\n/**\n * Prerendered markdown route on sanity.new. Preferred over content negotiation on `/`, which\n * depends on request headers and can fall through to the HTML page.\n */\nexport const INSTRUCTIONS_URL = `${SANITY_NEW_URL}/llms.txt`\n\nconst HTML_PATTERN = /^\\s*(?:<!doctype html|<html[\\s>])/iu\n\nexport class InstructionsUnavailableError extends Error {\n constructor(reason: string) {\n super(`Could not fetch the setup instructions from ${INSTRUCTIONS_URL}: ${reason}`)\n this.name = 'InstructionsUnavailableError'\n }\n}\n\n/**\n * Fetches the agent setup instructions from sanity.new, which is the single source of truth for\n * them. Deliberately not bundled with the CLI: a stale local copy is worse than a clear failure.\n */\nexport async function fetchNewInstructions(): Promise<string> {\n debug('fetching instructions from %s', INSTRUCTIONS_URL)\n\n let response: Response\n try {\n response = await fetch(INSTRUCTIONS_URL, {\n headers: {Accept: 'text/markdown'},\n method: 'GET',\n signal: AbortSignal.timeout(INSTRUCTIONS_TIMEOUT),\n })\n } catch (err) {\n const reason =\n err instanceof Error && err.name === 'TimeoutError'\n ? `the request timed out after ${INSTRUCTIONS_TIMEOUT / 1000} seconds`\n : `${err instanceof Error ? err.message : err}`\n throw new InstructionsUnavailableError(reason)\n }\n\n if (!response.ok) {\n throw new InstructionsUnavailableError(`the server responded with HTTP ${response.status}`)\n }\n\n const body = (await response.text()).trim()\n\n if (!body) {\n throw new InstructionsUnavailableError('the response was empty')\n }\n\n if (HTML_PATTERN.test(body)) {\n throw new InstructionsUnavailableError('the response was HTML rather than markdown')\n }\n\n debug('fetched %d characters', body.length)\n return body\n}\n"],"names":["subdebug","SANITY_NEW_URL","debug","INSTRUCTIONS_TIMEOUT","INSTRUCTIONS_URL","HTML_PATTERN","InstructionsUnavailableError","Error","reason","name","fetchNewInstructions","response","fetch","headers","Accept","method","signal","AbortSignal","timeout","err","message","ok","status","body","text","trim","test","length"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,yBAAwB;AAE/C,SAAQC,cAAc,QAAO,kCAAiC;AAE9D,MAAMC,QAAQF,SAAS;AAEvB,MAAMG,uBAAuB;AAE7B;;;CAGC,GACD,OAAO,MAAMC,mBAAmB,GAAGH,eAAe,SAAS,CAAC,CAAA;AAE5D,MAAMI,eAAe;AAErB,OAAO,MAAMC,qCAAqCC;IAChD,YAAYC,MAAc,CAAE;QAC1B,KAAK,CAAC,CAAC,4CAA4C,EAAEJ,iBAAiB,EAAE,EAAEI,QAAQ;QAClF,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeC;IACpBR,MAAM,iCAAiCE;IAEvC,IAAIO;IACJ,IAAI;QACFA,WAAW,MAAMC,MAAMR,kBAAkB;YACvCS,SAAS;gBAACC,QAAQ;YAAe;YACjCC,QAAQ;YACRC,QAAQC,YAAYC,OAAO,CAACf;QAC9B;IACF,EAAE,OAAOgB,KAAK;QACZ,MAAMX,SACJW,eAAeZ,SAASY,IAAIV,IAAI,KAAK,iBACjC,CAAC,4BAA4B,EAAEN,uBAAuB,KAAK,QAAQ,CAAC,GACpE,GAAGgB,eAAeZ,QAAQY,IAAIC,OAAO,GAAGD,KAAK;QACnD,MAAM,IAAIb,6BAA6BE;IACzC;IAEA,IAAI,CAACG,SAASU,EAAE,EAAE;QAChB,MAAM,IAAIf,6BAA6B,CAAC,+BAA+B,EAAEK,SAASW,MAAM,EAAE;IAC5F;IAEA,MAAMC,OAAO,AAAC,CAAA,MAAMZ,SAASa,IAAI,EAAC,EAAGC,IAAI;IAEzC,IAAI,CAACF,MAAM;QACT,MAAM,IAAIjB,6BAA6B;IACzC;IAEA,IAAID,aAAaqB,IAAI,CAACH,OAAO;QAC3B,MAAM,IAAIjB,6BAA6B;IACzC;IAEAJ,MAAM,yBAAyBqB,KAAKI,MAAM;IAC1C,OAAOJ;AACT"}
|
package/oclif.manifest.json
CHANGED
|
@@ -1150,7 +1150,7 @@
|
|
|
1150
1150
|
"required": false
|
|
1151
1151
|
}
|
|
1152
1152
|
},
|
|
1153
|
-
"description": "Sets up two folders here: ./sanity, a Studio where you write and edit your\ncontent, and ./web, a Next.js website that reads it. Both are already\nconnected to your new project, so you can start them straight away. Use\n--no-scaffold if you just want the project and nothing else.\n\nThe project is real and works immediately, but it is only yours for 72 hours.\nClaim it with a Sanity account before the deadline and everything you have\nbuilt stays exactly as it is. Claiming is free and takes about a minute. Miss\nthe deadline and the project and its content are deleted.\n\nTwo things to keep private: the claim link, because anyone who opens it\nbecomes the owner, and the access token saved in ./sanity/.env.local and\n./web/.env.local, because it can read and change everything in the project.\nKeep both env files out of git, and never put the token in code that runs in\nthe browser.\n\
|
|
1153
|
+
"description": "Sets up two folders here: ./sanity, a Studio where you write and edit your\ncontent, and ./web, a Next.js website that reads it. Both are already\nconnected to your new project, so you can start them straight away. Use\n--no-scaffold if you just want the project and nothing else.\n\nThe project is real and works immediately, but it is only yours for 72 hours.\nClaim it with a Sanity account before the deadline and everything you have\nbuilt stays exactly as it is. Claiming is free and takes about a minute. Miss\nthe deadline and the project and its content are deleted.\n\nTwo things to keep private: the claim link, because anyone who opens it\nbecomes the owner, and the access token saved in ./sanity/.env.local and\n./web/.env.local, because it can read and change everything in the project.\nKeep both env files out of git, and never put the token in code that runs in\nthe browser.\n\nRun this command with --instructions for the full agent setup guide.",
|
|
1154
1154
|
"examples": [
|
|
1155
1155
|
{
|
|
1156
1156
|
"command": "<%= config.bin %> <%= command.id %>",
|
|
@@ -1171,6 +1171,10 @@
|
|
|
1171
1171
|
{
|
|
1172
1172
|
"command": "<%= config.bin %> <%= command.id %> --json",
|
|
1173
1173
|
"description": "Create a project and print its details as JSON"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
"command": "<%= config.bin %> <%= command.id %> --instructions",
|
|
1177
|
+
"description": "Print the full setup guide for an AI agent, without creating anything"
|
|
1174
1178
|
}
|
|
1175
1179
|
],
|
|
1176
1180
|
"flags": {
|
|
@@ -1181,6 +1185,12 @@
|
|
|
1181
1185
|
"allowNo": false,
|
|
1182
1186
|
"type": "boolean"
|
|
1183
1187
|
},
|
|
1188
|
+
"instructions": {
|
|
1189
|
+
"description": "Print the full setup guide from https://sanity.new and exit, creating nothing",
|
|
1190
|
+
"name": "instructions",
|
|
1191
|
+
"allowNo": false,
|
|
1192
|
+
"type": "boolean"
|
|
1193
|
+
},
|
|
1184
1194
|
"scaffold": {
|
|
1185
1195
|
"description": "Set up a Studio in ./sanity and a Next.js website in ./web (on by default)",
|
|
1186
1196
|
"name": "scaffold",
|
|
@@ -5939,5 +5949,5 @@
|
|
|
5939
5949
|
]
|
|
5940
5950
|
}
|
|
5941
5951
|
},
|
|
5942
|
-
"version": "7.
|
|
5952
|
+
"version": "7.18.0"
|
|
5943
5953
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.18.0",
|
|
4
4
|
"description": "Sanity CLI tool for managing Sanity projects and organizations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"skills": "^1.5.9",
|
|
114
114
|
"smol-toml": "^1.6.1",
|
|
115
115
|
"string-argv": "^0.3.2",
|
|
116
|
-
"tar": "^7.5.
|
|
116
|
+
"tar": "^7.5.18",
|
|
117
117
|
"tar-fs": "^3.1.2",
|
|
118
118
|
"tar-stream": "^3.2.0",
|
|
119
119
|
"tinyglobby": "^0.2.17",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"yaml": "^2.9.0",
|
|
125
125
|
"zod": "^4.4.3",
|
|
126
126
|
"@sanity/cli-build": "^5.1.0",
|
|
127
|
-
"@sanity/cli-core": "^2.8.
|
|
127
|
+
"@sanity/cli-core": "^2.8.1",
|
|
128
128
|
"@sanity/workbench-cli": "^1.10.0"
|
|
129
129
|
},
|
|
130
130
|
"devDependencies": {
|
|
@@ -158,9 +158,9 @@
|
|
|
158
158
|
"sanity": "^6.7.0",
|
|
159
159
|
"typescript": "^6.0.3",
|
|
160
160
|
"vitest": "^4.1.10",
|
|
161
|
-
"@repo/package.config": "0.0.1",
|
|
162
161
|
"@repo/tsconfig": "3.70.0",
|
|
163
|
-
"@
|
|
162
|
+
"@repo/package.config": "0.0.1",
|
|
163
|
+
"@sanity/cli-test": "9.0.2",
|
|
164
164
|
"@sanity/eslint-config-cli": "1.1.3"
|
|
165
165
|
},
|
|
166
166
|
"peerDependencies": {
|