@sanity/cli 7.14.0 → 7.15.1

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 CHANGED
@@ -528,13 +528,12 @@ Diagnose potential issues with local Blueprint and remote Stack configuration
528
528
 
529
529
  ```
530
530
  USAGE
531
- $ sanity blueprints doctor [--json] [-p <value>] [--verbose] [--fix]
531
+ $ sanity blueprints doctor [--json] [-p <value>] [--fix]
532
532
 
533
533
  FLAGS
534
534
  -p, --path=<value> [env: SANITY_BLUEPRINT_PATH] Path to a Blueprint file or directory containing one
535
535
  --fix Interactively fix configuration issues
536
536
  --json Format output as json
537
- --[no-]verbose Verbose output; defaults to true
538
537
 
539
538
  DESCRIPTION
540
539
  Diagnose potential issues with local Blueprint and remote Stack configuration
@@ -545,6 +544,8 @@ DESCRIPTION
545
544
  Run this command when encountering errors with other Blueprint commands. Use --fix to interactively resolve detected
546
545
  issues.
547
546
 
547
+ Supports --json for programmatic consumption of diagnostic results.
548
+
548
549
  EXAMPLES
549
550
  $ sanity blueprints doctor
550
551
 
@@ -1861,7 +1862,7 @@ ARGUMENTS
1861
1862
 
1862
1863
  FLAGS
1863
1864
  --anonymous Send the query without any authorization token
1864
- --api-version=<value> [env: SANITY_CLI_QUERY_API_VERSION] API version to use (defaults to 2025-08-15)
1865
+ --api-version=<value> API version to use (defaults to 2025-08-15)
1865
1866
  --pretty Colorize JSON output
1866
1867
 
1867
1868
  OVERRIDE FLAGS
@@ -2071,7 +2072,7 @@ Add or set an environment variable for a deployed function
2071
2072
 
2072
2073
  ```
2073
2074
  USAGE
2074
- $ sanity functions env add NAME KEY VALUE [--json]
2075
+ $ sanity functions env add NAME KEY VALUE [--json] [--stack <value>]
2075
2076
 
2076
2077
  ARGUMENTS
2077
2078
  NAME The name of the Sanity Function
@@ -2079,7 +2080,8 @@ ARGUMENTS
2079
2080
  VALUE The value of the environment variable
2080
2081
 
2081
2082
  FLAGS
2082
- --json Format output as json
2083
+ --json Format output as json
2084
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
2083
2085
 
2084
2086
  DESCRIPTION
2085
2087
  Add or set an environment variable for a deployed function
@@ -2091,6 +2093,8 @@ DESCRIPTION
2091
2093
 
2092
2094
  EXAMPLES
2093
2095
  $ sanity functions env add MyFunction API_URL https://api.example.com/
2096
+
2097
+ $ sanity functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
2094
2098
  ```
2095
2099
 
2096
2100
  ## `sanity functions env list NAME`
@@ -2099,13 +2103,14 @@ List environment variables for a deployed function
2099
2103
 
2100
2104
  ```
2101
2105
  USAGE
2102
- $ sanity functions env list NAME [--json]
2106
+ $ sanity functions env list NAME [--json] [--stack <value>]
2103
2107
 
2104
2108
  ARGUMENTS
2105
2109
  NAME The name of the Sanity Function
2106
2110
 
2107
2111
  FLAGS
2108
- --json Format output as json
2112
+ --json Format output as json
2113
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
2109
2114
 
2110
2115
  DESCRIPTION
2111
2116
  List environment variables for a deployed function
@@ -2116,6 +2121,8 @@ DESCRIPTION
2116
2121
 
2117
2122
  EXAMPLES
2118
2123
  $ sanity functions env list MyFunction
2124
+
2125
+ $ sanity functions env list --stack <name-or-id> MyFunction
2119
2126
  ```
2120
2127
 
2121
2128
  ## `sanity functions env remove NAME KEY`
@@ -2124,14 +2131,15 @@ Remove an environment variable from a deployed function
2124
2131
 
2125
2132
  ```
2126
2133
  USAGE
2127
- $ sanity functions env remove NAME KEY [--json]
2134
+ $ sanity functions env remove NAME KEY [--json] [--stack <value>]
2128
2135
 
2129
2136
  ARGUMENTS
2130
2137
  NAME The name of the Sanity Function
2131
2138
  KEY The name of the environment variable
2132
2139
 
2133
2140
  FLAGS
2134
- --json Format output as json
2141
+ --json Format output as json
2142
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
2135
2143
 
2136
2144
  DESCRIPTION
2137
2145
  Remove an environment variable from a deployed function
@@ -2143,6 +2151,8 @@ DESCRIPTION
2143
2151
 
2144
2152
  EXAMPLES
2145
2153
  $ sanity functions env remove MyFunction API_URL
2154
+
2155
+ $ sanity functions env remove --stack <name-or-id> MyFunction API_URL
2146
2156
  ```
2147
2157
 
2148
2158
  ## `sanity functions logs [NAME]`
@@ -2616,14 +2626,14 @@ Log in to your Sanity account
2616
2626
 
2617
2627
  ```
2618
2628
  USAGE
2619
- $ sanity login [--open] [--provider <providerId> | --sso <slug> | --with-token] [--sso-provider <name> ]
2629
+ $ sanity login [--with-token | --provider <providerId> | --sso <slug>] [--open] [--sso-provider <name> ]
2620
2630
 
2621
2631
  FLAGS
2632
+ --with-token Read token from standard input
2622
2633
  --[no-]open Open a browser window to log in (`--no-open` only prints URL)
2623
2634
  --provider=<providerId> Log in using a provider ID (google, github, sanity, vercel)
2624
2635
  --sso=<slug> Log in using Single Sign-On, using the given organization slug
2625
2636
  --sso-provider=<name> Select a specific SSO provider by name (use with --sso)
2626
- --with-token Read token from standard input
2627
2637
 
2628
2638
  DESCRIPTION
2629
2639
  Log in to your Sanity account
@@ -2633,6 +2643,10 @@ EXAMPLES
2633
2643
 
2634
2644
  $ sanity login
2635
2645
 
2646
+ Log in using a token from standard input
2647
+
2648
+ $ sanity login --with-token < token.txt
2649
+
2636
2650
  Login with GitHub provider, but do not open a browser window automatically
2637
2651
 
2638
2652
  $ sanity login --provider github --no-open
@@ -2644,10 +2658,6 @@ EXAMPLES
2644
2658
  Log in using a specific SSO provider within an organization
2645
2659
 
2646
2660
  $ sanity login --sso my-organization --sso-provider "Okta SSO"
2647
-
2648
- Log in using a token from standard input
2649
-
2650
- $ sanity login --with-token < token.txt
2651
2661
  ```
2652
2662
 
2653
2663
  ## `sanity logout`
@@ -38,6 +38,16 @@ const IS_README_GENERATION = (process.argv[process.argv.indexOf('readme') - 1] ?
38
38
  formatTopic(topic) {
39
39
  return prefixBinName(super.formatTopic(topic));
40
40
  }
41
+ getCommandHelpClass(command) {
42
+ const commandHelp = super.getCommandHelpClass(command);
43
+ if (command.id === 'login') {
44
+ commandHelp.opts = {
45
+ ...commandHelp.opts,
46
+ flagSortOrder: 'none'
47
+ };
48
+ }
49
+ return commandHelp;
50
+ }
41
51
  async showHelp(argv) {
42
52
  return super.showHelp(resolveTopicAliasInArgv(argv));
43
53
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SanityHelp.ts"],"sourcesContent":["import {Command, Help, Interfaces} from '@oclif/core'\nimport {getBinCommand, getRunningPackageManager} from '@sanity/cli-core/package-manager'\n\nimport {topicAliases} from './topicAliases.js'\n\n// Reverse map: alias name → canonical topic name\nconst aliasToCanonical = new Map<string, string>()\nfor (const [canonical, aliases] of Object.entries(topicAliases)) {\n for (const alias of aliases) {\n aliasToCanonical.set(alias, canonical)\n }\n}\n\n// Running `oclif readme`, we don't want to apply the `prefixBinName` transformation,\n// as it will include whatever pm was used to spawn the script in the generated readme.\n// argv will contain something like [nodeBinPath, oclifBinPath, 'readme', …] so check\n// for 'readme' with a preceeding argument that includes 'oclif' to be sure.\nconst IS_README_GENERATION = (process.argv[process.argv.indexOf('readme') - 1] ?? '').includes(\n 'oclif',\n)\n\n/**\n * Custom Help class for Sanity CLI that overrides the default help formatting to\n * prefix the bin name (e.g., `npx sanity`, `yarn sanity`, etc.) in the help text,\n * and to replace `sanity init` references with the appropriate `create` command\n * for the detected package manager when needed.\n *\n * @internal\n */\nexport default class SanityHelp extends Help {\n protected formatCommand(command: Command.Loadable): string {\n let help = super.formatCommand(command)\n\n // When `sanity init` is called, but originates from the `create-sanity`\n // package/binary (eg the one used by `npm create sanity@latest` etc), we want to\n // customize the help text to show that command instead of `sanity init`.\n const isFromCreate = process.argv.includes('--from-create') && command.id === 'init'\n if (isFromCreate) {\n help = replaceInitWithCreateCommand(help)\n }\n\n return prefixBinName(help)\n }\n\n protected formatRoot(): string {\n return prefixBinName(super.formatRoot())\n }\n\n protected formatTopic(topic: Interfaces.Topic): string {\n return prefixBinName(super.formatTopic(topic))\n }\n\n async showHelp(argv: string[]): Promise<void> {\n return super.showHelp(resolveTopicAliasInArgv(argv))\n }\n}\n\n/**\n * @internal\n */\nexport function prefixBinName(help: string): string {\n if (IS_README_GENERATION) return help\n const binCommand = getBinCommand()\n if (binCommand === 'sanity') return help\n return help.replaceAll('$ sanity', `$ ${binCommand}`)\n}\n\n/**\n * Replace `sanity init` references in help text with the equivalent `create` command\n * for the detected package manager. Lines ending in just `sanity init\\n` (no flags)\n * are replaced without a flag separator, while lines with flags get the separator\n * (eg `--` for npm) so the flags are forwarded correctly.\n *\n * @internal\n */\nexport function replaceInitWithCreateCommand(help: string): string {\n const createCmd = guessCreateCommand()\n const flagSeparator = needsFlagSeparator() ? ' --' : ''\n\n // First replace all `sanity init` references that ends with a newline with the\n // create variant that does not include any flag separator (eg `--`). Then replace\n // the other references that do. Most package managers do not require the `--`\n // separator, but npm does. Only include it if we need to, as the commands look\n // cleaner without it.\n return help\n .replaceAll(/(\\s+)sanity\\s+init\\s*\\n/g, `$1${createCmd}\\n`)\n .replaceAll(/(\\s+)sanity(\\s+)init/g, `$1${createCmd}${flagSeparator}`)\n}\n\nfunction guessCreateCommand() {\n const pm = getRunningPackageManager()\n if (pm === 'yarn') return `yarn create sanity`\n if (pm === 'bun') return `bun create sanity@latest`\n if (pm === 'pnpm') return `pnpm create sanity@latest`\n return `npm create sanity@latest`\n}\n\nfunction needsFlagSeparator() {\n const pm = getRunningPackageManager()\n return pm === 'npm' || !pm\n}\n\n/**\n * Replace the first positional argument in argv with the canonical topic name\n * if it is a known topic alias. This ensures that `sanity dataset --help`\n * resolves to the same help output as `sanity datasets --help`.\n *\n * Without this, `--help` bypasses the command_not_found hook (which normally\n * handles alias resolution) and the help system fails to find the topic.\n *\n * @internal\n */\nexport function resolveTopicAliasInArgv(argv: string[]): string[] {\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') break\n if (arg.startsWith('-')) continue\n\n // First positional argument is the topic/command name\n const canonical = aliasToCanonical.get(arg)\n if (canonical) {\n const resolved = [...argv]\n resolved[i] = canonical\n return resolved\n }\n break\n }\n return argv\n}\n"],"names":["Help","getBinCommand","getRunningPackageManager","topicAliases","aliasToCanonical","Map","canonical","aliases","Object","entries","alias","set","IS_README_GENERATION","process","argv","indexOf","includes","SanityHelp","formatCommand","command","help","isFromCreate","id","replaceInitWithCreateCommand","prefixBinName","formatRoot","formatTopic","topic","showHelp","resolveTopicAliasInArgv","binCommand","replaceAll","createCmd","guessCreateCommand","flagSeparator","needsFlagSeparator","pm","i","length","arg","startsWith","get","resolved"],"mappings":"AAAA,SAAiBA,IAAI,QAAmB,cAAa;AACrD,SAAQC,aAAa,EAAEC,wBAAwB,QAAO,mCAAkC;AAExF,SAAQC,YAAY,QAAO,oBAAmB;AAE9C,iDAAiD;AACjD,MAAMC,mBAAmB,IAAIC;AAC7B,KAAK,MAAM,CAACC,WAAWC,QAAQ,IAAIC,OAAOC,OAAO,CAACN,cAAe;IAC/D,KAAK,MAAMO,SAASH,QAAS;QAC3BH,iBAAiBO,GAAG,CAACD,OAAOJ;IAC9B;AACF;AAEA,qFAAqF;AACrF,uFAAuF;AACvF,qFAAqF;AACrF,4EAA4E;AAC5E,MAAMM,uBAAuB,AAACC,CAAAA,QAAQC,IAAI,CAACD,QAAQC,IAAI,CAACC,OAAO,CAAC,YAAY,EAAE,IAAI,EAAC,EAAGC,QAAQ,CAC5F;AAGF;;;;;;;CAOC,GACD,eAAe,MAAMC,mBAAmBjB;IAC5BkB,cAAcC,OAAyB,EAAU;QACzD,IAAIC,OAAO,KAAK,CAACF,cAAcC;QAE/B,wEAAwE;QACxE,iFAAiF;QACjF,yEAAyE;QACzE,MAAME,eAAeR,QAAQC,IAAI,CAACE,QAAQ,CAAC,oBAAoBG,QAAQG,EAAE,KAAK;QAC9E,IAAID,cAAc;YAChBD,OAAOG,6BAA6BH;QACtC;QAEA,OAAOI,cAAcJ;IACvB;IAEUK,aAAqB;QAC7B,OAAOD,cAAc,KAAK,CAACC;IAC7B;IAEUC,YAAYC,KAAuB,EAAU;QACrD,OAAOH,cAAc,KAAK,CAACE,YAAYC;IACzC;IAEA,MAAMC,SAASd,IAAc,EAAiB;QAC5C,OAAO,KAAK,CAACc,SAASC,wBAAwBf;IAChD;AACF;AAEA;;CAEC,GACD,OAAO,SAASU,cAAcJ,IAAY;IACxC,IAAIR,sBAAsB,OAAOQ;IACjC,MAAMU,aAAa7B;IACnB,IAAI6B,eAAe,UAAU,OAAOV;IACpC,OAAOA,KAAKW,UAAU,CAAC,YAAY,CAAC,EAAE,EAAED,YAAY;AACtD;AAEA;;;;;;;CAOC,GACD,OAAO,SAASP,6BAA6BH,IAAY;IACvD,MAAMY,YAAYC;IAClB,MAAMC,gBAAgBC,uBAAuB,QAAQ;IAErD,+EAA+E;IAC/E,kFAAkF;IAClF,8EAA8E;IAC9E,+EAA+E;IAC/E,sBAAsB;IACtB,OAAOf,KACJW,UAAU,CAAC,4BAA4B,CAAC,EAAE,EAAEC,UAAU,EAAE,CAAC,EACzDD,UAAU,CAAC,yBAAyB,CAAC,EAAE,EAAEC,YAAYE,eAAe;AACzE;AAEA,SAASD;IACP,MAAMG,KAAKlC;IACX,IAAIkC,OAAO,QAAQ,OAAO,CAAC,kBAAkB,CAAC;IAC9C,IAAIA,OAAO,OAAO,OAAO,CAAC,wBAAwB,CAAC;IACnD,IAAIA,OAAO,QAAQ,OAAO,CAAC,yBAAyB,CAAC;IACrD,OAAO,CAAC,wBAAwB,CAAC;AACnC;AAEA,SAASD;IACP,MAAMC,KAAKlC;IACX,OAAOkC,OAAO,SAAS,CAACA;AAC1B;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASP,wBAAwBf,IAAc;IACpD,IAAK,IAAIuB,IAAI,GAAGA,IAAIvB,KAAKwB,MAAM,EAAED,IAAK;QACpC,MAAME,MAAMzB,IAAI,CAACuB,EAAE;QACnB,IAAIE,QAAQ,MAAM;QAClB,IAAIA,IAAIC,UAAU,CAAC,MAAM;QAEzB,sDAAsD;QACtD,MAAMlC,YAAYF,iBAAiBqC,GAAG,CAACF;QACvC,IAAIjC,WAAW;YACb,MAAMoC,WAAW;mBAAI5B;aAAK;YAC1B4B,QAAQ,CAACL,EAAE,GAAG/B;YACd,OAAOoC;QACT;QACA;IACF;IACA,OAAO5B;AACT"}
1
+ {"version":3,"sources":["../src/SanityHelp.ts"],"sourcesContent":["import {Command, CommandHelp, Help, Interfaces} from '@oclif/core'\nimport {getBinCommand, getRunningPackageManager} from '@sanity/cli-core/package-manager'\n\nimport {topicAliases} from './topicAliases.js'\n\n// Reverse map: alias name → canonical topic name\nconst aliasToCanonical = new Map<string, string>()\nfor (const [canonical, aliases] of Object.entries(topicAliases)) {\n for (const alias of aliases) {\n aliasToCanonical.set(alias, canonical)\n }\n}\n\n// Running `oclif readme`, we don't want to apply the `prefixBinName` transformation,\n// as it will include whatever pm was used to spawn the script in the generated readme.\n// argv will contain something like [nodeBinPath, oclifBinPath, 'readme', …] so check\n// for 'readme' with a preceeding argument that includes 'oclif' to be sure.\nconst IS_README_GENERATION = (process.argv[process.argv.indexOf('readme') - 1] ?? '').includes(\n 'oclif',\n)\n\n/**\n * Custom Help class for Sanity CLI that overrides the default help formatting to\n * prefix the bin name (e.g., `npx sanity`, `yarn sanity`, etc.) in the help text,\n * and to replace `sanity init` references with the appropriate `create` command\n * for the detected package manager when needed.\n *\n * @internal\n */\nexport default class SanityHelp extends Help {\n protected formatCommand(command: Command.Loadable): string {\n let help = super.formatCommand(command)\n\n // When `sanity init` is called, but originates from the `create-sanity`\n // package/binary (eg the one used by `npm create sanity@latest` etc), we want to\n // customize the help text to show that command instead of `sanity init`.\n const isFromCreate = process.argv.includes('--from-create') && command.id === 'init'\n if (isFromCreate) {\n help = replaceInitWithCreateCommand(help)\n }\n\n return prefixBinName(help)\n }\n\n protected formatRoot(): string {\n return prefixBinName(super.formatRoot())\n }\n\n protected formatTopic(topic: Interfaces.Topic): string {\n return prefixBinName(super.formatTopic(topic))\n }\n\n protected override getCommandHelpClass(command: Command.Loadable): CommandHelp {\n const commandHelp = super.getCommandHelpClass(command)\n if (command.id === 'login') {\n commandHelp.opts = {...commandHelp.opts, flagSortOrder: 'none'}\n }\n return commandHelp\n }\n\n async showHelp(argv: string[]): Promise<void> {\n return super.showHelp(resolveTopicAliasInArgv(argv))\n }\n}\n\n/**\n * @internal\n */\nexport function prefixBinName(help: string): string {\n if (IS_README_GENERATION) return help\n const binCommand = getBinCommand()\n if (binCommand === 'sanity') return help\n return help.replaceAll('$ sanity', `$ ${binCommand}`)\n}\n\n/**\n * Replace `sanity init` references in help text with the equivalent `create` command\n * for the detected package manager. Lines ending in just `sanity init\\n` (no flags)\n * are replaced without a flag separator, while lines with flags get the separator\n * (eg `--` for npm) so the flags are forwarded correctly.\n *\n * @internal\n */\nexport function replaceInitWithCreateCommand(help: string): string {\n const createCmd = guessCreateCommand()\n const flagSeparator = needsFlagSeparator() ? ' --' : ''\n\n // First replace all `sanity init` references that ends with a newline with the\n // create variant that does not include any flag separator (eg `--`). Then replace\n // the other references that do. Most package managers do not require the `--`\n // separator, but npm does. Only include it if we need to, as the commands look\n // cleaner without it.\n return help\n .replaceAll(/(\\s+)sanity\\s+init\\s*\\n/g, `$1${createCmd}\\n`)\n .replaceAll(/(\\s+)sanity(\\s+)init/g, `$1${createCmd}${flagSeparator}`)\n}\n\nfunction guessCreateCommand() {\n const pm = getRunningPackageManager()\n if (pm === 'yarn') return `yarn create sanity`\n if (pm === 'bun') return `bun create sanity@latest`\n if (pm === 'pnpm') return `pnpm create sanity@latest`\n return `npm create sanity@latest`\n}\n\nfunction needsFlagSeparator() {\n const pm = getRunningPackageManager()\n return pm === 'npm' || !pm\n}\n\n/**\n * Replace the first positional argument in argv with the canonical topic name\n * if it is a known topic alias. This ensures that `sanity dataset --help`\n * resolves to the same help output as `sanity datasets --help`.\n *\n * Without this, `--help` bypasses the command_not_found hook (which normally\n * handles alias resolution) and the help system fails to find the topic.\n *\n * @internal\n */\nexport function resolveTopicAliasInArgv(argv: string[]): string[] {\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') break\n if (arg.startsWith('-')) continue\n\n // First positional argument is the topic/command name\n const canonical = aliasToCanonical.get(arg)\n if (canonical) {\n const resolved = [...argv]\n resolved[i] = canonical\n return resolved\n }\n break\n }\n return argv\n}\n"],"names":["Help","getBinCommand","getRunningPackageManager","topicAliases","aliasToCanonical","Map","canonical","aliases","Object","entries","alias","set","IS_README_GENERATION","process","argv","indexOf","includes","SanityHelp","formatCommand","command","help","isFromCreate","id","replaceInitWithCreateCommand","prefixBinName","formatRoot","formatTopic","topic","getCommandHelpClass","commandHelp","opts","flagSortOrder","showHelp","resolveTopicAliasInArgv","binCommand","replaceAll","createCmd","guessCreateCommand","flagSeparator","needsFlagSeparator","pm","i","length","arg","startsWith","get","resolved"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { getCliToken, isCi } from '@sanity/cli-core';
2
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
2
3
  import { fetchTelemetryConsent, isValidApiConsentStatus, VALID_API_STATUSES } from '../../services/telemetry.js';
3
4
  import { isTrueish } from './isTrueish.js';
4
5
  import { telemetryDebug } from './telemetryDebug.js';
@@ -10,13 +11,14 @@ function parseApiConsentStatus(value) {
10
11
  }
11
12
  export async function resolveConsent() {
12
13
  telemetryDebug('Resolving consent…');
13
- if (isCi()) {
14
+ const hasCliExecutionContext = Boolean(getCliExecutionContext());
15
+ if (!hasCliExecutionContext && isCi()) {
14
16
  telemetryDebug('CI environment detected, treating telemetry consent as denied');
15
17
  return {
16
18
  status: 'denied'
17
19
  };
18
20
  }
19
- if (isTrueish(process.env.DO_NOT_TRACK)) {
21
+ if (!hasCliExecutionContext && isTrueish(process.env.DO_NOT_TRACK)) {
20
22
  telemetryDebug('DO_NOT_TRACK is set, consent is denied');
21
23
  return {
22
24
  reason: 'localOverride',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/telemetry/resolveConsent.ts"],"sourcesContent":["import {type ConsentInformation, getCliToken, isCi} from '@sanity/cli-core'\n\nimport {\n fetchTelemetryConsent,\n isValidApiConsentStatus,\n VALID_API_STATUSES,\n type ValidApiConsentStatus,\n} from '../../services/telemetry.js'\nimport {isTrueish} from './isTrueish.js'\nimport {telemetryDebug} from './telemetryDebug.js'\n\nfunction parseApiConsentStatus(value: unknown): ValidApiConsentStatus {\n if (typeof value === 'string' && isValidApiConsentStatus(value)) {\n return value\n }\n throw new Error(`Invalid consent status. Must be one of: ${VALID_API_STATUSES.join(', ')}`)\n}\n\nexport async function resolveConsent(): Promise<ConsentInformation> {\n telemetryDebug('Resolving consent…')\n if (isCi()) {\n telemetryDebug('CI environment detected, treating telemetry consent as denied')\n return {status: 'denied'}\n }\n\n if (isTrueish(process.env.DO_NOT_TRACK)) {\n telemetryDebug('DO_NOT_TRACK is set, consent is denied')\n return {\n reason: 'localOverride',\n status: 'denied',\n }\n }\n\n const token = await getCliToken()\n if (!token) {\n telemetryDebug('User is not logged in, consent is undetermined')\n return {\n reason: 'unauthenticated',\n status: 'undetermined',\n }\n }\n\n try {\n const response = await fetchTelemetryConsent()\n\n telemetryDebug('User consent status is %s', response.status)\n return {status: parseApiConsentStatus(response.status)}\n } catch (err) {\n telemetryDebug('Failed to fetch user consent status, treating it as \"undetermined\": %s', err)\n return {\n reason: 'fetchError',\n status: 'undetermined',\n }\n }\n}\n"],"names":["getCliToken","isCi","fetchTelemetryConsent","isValidApiConsentStatus","VALID_API_STATUSES","isTrueish","telemetryDebug","parseApiConsentStatus","value","Error","join","resolveConsent","status","process","env","DO_NOT_TRACK","reason","token","response","err"],"mappings":"AAAA,SAAiCA,WAAW,EAAEC,IAAI,QAAO,mBAAkB;AAE3E,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,kBAAkB,QAEb,8BAA6B;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,cAAc,QAAO,sBAAqB;AAElD,SAASC,sBAAsBC,KAAc;IAC3C,IAAI,OAAOA,UAAU,YAAYL,wBAAwBK,QAAQ;QAC/D,OAAOA;IACT;IACA,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEL,mBAAmBM,IAAI,CAAC,OAAO;AAC5F;AAEA,OAAO,eAAeC;IACpBL,eAAe;IACf,IAAIL,QAAQ;QACVK,eAAe;QACf,OAAO;YAACM,QAAQ;QAAQ;IAC1B;IAEA,IAAIP,UAAUQ,QAAQC,GAAG,CAACC,YAAY,GAAG;QACvCT,eAAe;QACf,OAAO;YACLU,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,MAAMK,QAAQ,MAAMjB;IACpB,IAAI,CAACiB,OAAO;QACVX,eAAe;QACf,OAAO;YACLU,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,IAAI;QACF,MAAMM,WAAW,MAAMhB;QAEvBI,eAAe,6BAA6BY,SAASN,MAAM;QAC3D,OAAO;YAACA,QAAQL,sBAAsBW,SAASN,MAAM;QAAC;IACxD,EAAE,OAAOO,KAAK;QACZb,eAAe,0EAA0Ea;QACzF,OAAO;YACLH,QAAQ;YACRJ,QAAQ;QACV;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/telemetry/resolveConsent.ts"],"sourcesContent":["import {type ConsentInformation, getCliToken, isCi} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\n\nimport {\n fetchTelemetryConsent,\n isValidApiConsentStatus,\n VALID_API_STATUSES,\n type ValidApiConsentStatus,\n} from '../../services/telemetry.js'\nimport {isTrueish} from './isTrueish.js'\nimport {telemetryDebug} from './telemetryDebug.js'\n\nfunction parseApiConsentStatus(value: unknown): ValidApiConsentStatus {\n if (typeof value === 'string' && isValidApiConsentStatus(value)) {\n return value\n }\n throw new Error(`Invalid consent status. Must be one of: ${VALID_API_STATUSES.join(', ')}`)\n}\n\nexport async function resolveConsent(): Promise<ConsentInformation> {\n telemetryDebug('Resolving consent…')\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n if (!hasCliExecutionContext && isCi()) {\n telemetryDebug('CI environment detected, treating telemetry consent as denied')\n return {status: 'denied'}\n }\n\n if (!hasCliExecutionContext && isTrueish(process.env.DO_NOT_TRACK)) {\n telemetryDebug('DO_NOT_TRACK is set, consent is denied')\n return {\n reason: 'localOverride',\n status: 'denied',\n }\n }\n\n const token = await getCliToken()\n if (!token) {\n telemetryDebug('User is not logged in, consent is undetermined')\n return {\n reason: 'unauthenticated',\n status: 'undetermined',\n }\n }\n\n try {\n const response = await fetchTelemetryConsent()\n\n telemetryDebug('User consent status is %s', response.status)\n return {status: parseApiConsentStatus(response.status)}\n } catch (err) {\n telemetryDebug('Failed to fetch user consent status, treating it as \"undetermined\": %s', err)\n return {\n reason: 'fetchError',\n status: 'undetermined',\n }\n }\n}\n"],"names":["getCliToken","isCi","getCliExecutionContext","fetchTelemetryConsent","isValidApiConsentStatus","VALID_API_STATUSES","isTrueish","telemetryDebug","parseApiConsentStatus","value","Error","join","resolveConsent","hasCliExecutionContext","Boolean","status","process","env","DO_NOT_TRACK","reason","token","response","err"],"mappings":"AAAA,SAAiCA,WAAW,EAAEC,IAAI,QAAO,mBAAkB;AAC3E,SAAQC,sBAAsB,QAAO,oCAAmC;AAExE,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,kBAAkB,QAEb,8BAA6B;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,cAAc,QAAO,sBAAqB;AAElD,SAASC,sBAAsBC,KAAc;IAC3C,IAAI,OAAOA,UAAU,YAAYL,wBAAwBK,QAAQ;QAC/D,OAAOA;IACT;IACA,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEL,mBAAmBM,IAAI,CAAC,OAAO;AAC5F;AAEA,OAAO,eAAeC;IACpBL,eAAe;IACf,MAAMM,yBAAyBC,QAAQZ;IAEvC,IAAI,CAACW,0BAA0BZ,QAAQ;QACrCM,eAAe;QACf,OAAO;YAACQ,QAAQ;QAAQ;IAC1B;IAEA,IAAI,CAACF,0BAA0BP,UAAUU,QAAQC,GAAG,CAACC,YAAY,GAAG;QAClEX,eAAe;QACf,OAAO;YACLY,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,MAAMK,QAAQ,MAAMpB;IACpB,IAAI,CAACoB,OAAO;QACVb,eAAe;QACf,OAAO;YACLY,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,IAAI;QACF,MAAMM,WAAW,MAAMlB;QAEvBI,eAAe,6BAA6Bc,SAASN,MAAM;QAC3D,OAAO;YAACA,QAAQP,sBAAsBa,SAASN,MAAM;QAAC;IACxD,EAAE,OAAOO,KAAK;QACZf,eAAe,0EAA0Ee;QACzF,OAAO;YACLH,QAAQ;YACRJ,QAAQ;QACV;IACF;AACF"}
@@ -3,6 +3,7 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { exit } from '@oclif/core/errors';
4
4
  import { exitCodes } from '@sanity/cli-core';
5
5
  import { subdebug } from '@sanity/cli-core/debug';
6
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
6
7
  import { SanityCommand } from '@sanity/cli-core/SanityCommand';
7
8
  import { spinner } from '@sanity/cli-core/ux';
8
9
  import { Table } from 'console-table-printer';
@@ -385,6 +386,7 @@ export class CopyDatasetCommand extends SanityCommand {
385
386
  }
386
387
  async subscribeToProgress(projectId, jobId) {
387
388
  const spin = spinner('').start();
389
+ const hasCliExecutionContext = Boolean(getCliExecutionContext());
388
390
  return new Promise((resolve, reject)=>{
389
391
  const sigintHandler = ()=>{
390
392
  subscription.unsubscribe();
@@ -396,12 +398,12 @@ export class CopyDatasetCommand extends SanityCommand {
396
398
  projectId
397
399
  }).subscribe({
398
400
  complete: ()=>{
399
- process.off('SIGINT', sigintHandler);
401
+ if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler);
400
402
  spin.succeed('Copy finished.');
401
403
  resolve();
402
404
  },
403
405
  error: (err)=>{
404
- process.off('SIGINT', sigintHandler);
406
+ if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler);
405
407
  spin.fail('Copy failed.');
406
408
  reject(err);
407
409
  },
@@ -411,7 +413,7 @@ export class CopyDatasetCommand extends SanityCommand {
411
413
  }
412
414
  }
413
415
  });
414
- process.once('SIGINT', sigintHandler);
416
+ if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler);
415
417
  });
416
418
  }
417
419
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","subdebug","SanityCommand","spinner","Table","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatCliErrorMessages","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,IAAI,QAAO,qBAAoB;AACvC,SAAQC,SAAS,QAAO,mBAAkB;AAC1C,SAAQC,QAAQ,QAAO,yBAAwB;AAC/C,SAAQC,aAAa,QAAO,iCAAgC;AAC5D,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,KAAK,QAAO,wBAAuB;AAC3C,SAAQC,cAAc,QAAO,0BAAyB;AACtD,SAAQC,mBAAmB,QAAO,+BAA8B;AAChE,SAAQC,QAAQ,QAAO,oBAAmB;AAE1C,SAAQC,mBAAmB,QAAO,+CAA8C;AAChF,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,oBAAoB,QAAO,wCAAuC;AAC1E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SACEC,WAAW,EAGXC,qBAAqB,EACrBC,mBAAmB,EACnBC,YAAY,QACP,6BAA4B;AACnC,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,mBAAmBjB,SAAS;AAElC,OAAO,MAAMkB,2BAA2BjB;IACtC,OAAgBkB,OAAO;QACrBC,QAAQxB,KAAKyB,MAAM,CAAC;YAClBC,aAAa;YACbC,UAAU;QACZ;QACAC,QAAQ5B,KAAKyB,MAAM,CAAC;YAClBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,qCAAoC;IAElE,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,GAAGX,iBAAiB;YAClBM,aAAa;YACbM,WAAW;QACb,EAAE;QACFC,QAAQhC,MAAMwB,MAAM,CAAC;YACnBC,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;gBAAU;aAAe;YAC7CP,UAAU;QACZ;QACAQ,QAAQlC,MAAMmC,OAAO,CAAC;YACpBV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;QACAU,OAAOpC,MAAMqC,OAAO,CAAC;YACnBC,WAAW;gBAAC;aAAO;YACnBb,aAAa;YACbc,KAAK;YACLb,UAAU;QACZ;QACAc,MAAMxC,MAAMmC,OAAO,CAAC;YAClBV,aAAa;YACbQ,WAAW;gBAAC;gBAAU;gBAAU;aAAe;YAC/CP,UAAU;QACZ;QACAe,QAAQzC,MAAMqC,OAAO,CAAC;YACpBC,WAAW;gBAAC;aAAO;YACnBb,aAAa;YACbC,UAAU;QACZ;QACA,yBAAyB1B,MAAMmC,OAAO,CAAC;YACrCV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;QACA,gBAAgB1B,MAAMmC,OAAO,CAAC;YAC5BV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;IACF,EAAC;IAED,OAAgBgB,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAqB;QAChC,MAAM,EAACrB,IAAI,EAAEQ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACc,KAAK,CAACvB;QAEvC,IAAI,CAACS,MAAMU,IAAI,IAAI,CAACV,MAAME,MAAM,IAAI,IAAI,CAACa,YAAY,IAAI;YACvD,MAAMC,SAAmB,EAAE;YAE3B,IAAI,CAACxB,KAAKC,MAAM,EAAE;gBAChBuB,OAAOC,IAAI,CAAC;YACd;YACA,IAAI,CAACzB,KAAKK,MAAM,EAAE;gBAChBmB,OAAOC,IAAI,CAAC;YACd;YAEA,IAAID,OAAOE,MAAM,GAAG,GAAG;gBACrB,IAAI,CAACC,MAAM,CAACC,KAAK,CAAChC,uBAAuB4B,SAAS;oBAAC7C,MAAMC,UAAUiD,WAAW;gBAAA;YAChF;QACF;QAEA,MAAMC,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRzC,iBAAiB;oBACf0C,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;wBACrD;4BAACD,OAAO;4BAAUC,YAAY;wBAAyB;qBACxD;gBACH;QACJ;QAEA,4BAA4B;QAC5B,IAAI3B,MAAMU,IAAI,EAAE;YACd,OAAO,IAAI,CAACkB,cAAc,CAACN,WAAWtB;QACxC;QAEA,IAAIA,MAAME,MAAM,EAAE;YAChB,OAAO,IAAI,CAAC2B,gBAAgB,CAACP,WAAWtB,MAAME,MAAM;QACtD;QAEA,OAAO,IAAI,CAAC4B,cAAc,CAACR,WAAW9B,MAAMQ;IAC9C;IAEQ+B,qBAAqBC,IAAsB,EAAQ;QACzD,MAAMC,QAAQ,IAAIzD,MAAM;YACtB0D,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAAMC,OAAO;gBAAQ;gBAC/C;oBAACF,WAAW;oBAAQC,MAAM;oBAAiBC,OAAO;gBAAgB;gBAClE;oBAACF,WAAW;oBAAQC,MAAM;oBAAiBC,OAAO;gBAAgB;gBAClE;oBAACF,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAeC,OAAO;gBAAc;gBAC9D;oBAACF,WAAW;oBAAQC,MAAM;oBAAeC,OAAO;gBAAc;gBAC9D;oBAACF,WAAW;oBAAQC,MAAM;oBAAaC,OAAO;gBAAY;aAC3D;YACDA,OAAO;QACT;QAEA,KAAK,MAAMC,OAAON,KAAM;YACtB,MAAM,EAACO,SAAS,EAAEC,EAAE,EAAEC,aAAa,EAAEC,KAAK,EAAEC,aAAa,EAAEC,SAAS,EAAEC,WAAW,EAAC,GAAGP;YAErF,IAAIQ,cAAc;YAClB,IAAIP,cAAc,IAAI;gBACpBO,cAAcpE,oBAAoBC,SAAS4D;YAC7C;YAEA,IAAIQ,YAAY;YAChB,IAAIH,cAAc,IAAI;gBACpBG,YAAYtE,eAAeE,SAASiE,YAAYjE,SAAS4D;YAC3D;YAEA,IAAIS;YACJ,OAAQN;gBACN,KAAK;oBAAa;wBAChBM,QAAQ;wBACR;oBACF;gBACA,KAAK;oBAAU;wBACbA,QAAQ;wBACR;oBACF;gBACA,KAAK;oBAAW;wBACdA,QAAQ;wBACR;oBACF;gBACA;oBAAS;wBACPA,QAAQ;oBACV;YACF;YAEAf,MAAMgB,MAAM,CACV;gBACET;gBACAC;gBACAC;gBACAC;gBACAG,aAAa,GAAGA,YAAY,IAAI,CAAC;gBACjCC;gBACAF;YACF,GACA;gBAACG;YAAK;QAEV;QAEA,IAAI,CAAC7B,MAAM,CAAC+B,GAAG,CAACjB,MAAMkB,MAAM;IAC9B;IAEA,MAActB,iBAAiBP,SAAiB,EAAE8B,KAAa,EAAiB;QAC9E9D,iBAAiB,4BAA4B8D;QAE7C,IAAIA,MAAMC,IAAI,OAAO,IAAI;YACvB,OAAO,IAAI,CAAClC,MAAM,CAACC,KAAK,CAAC,+BAA+B;gBAACjD,MAAMC,UAAUkF,aAAa;YAAA;QACxF;QAEA,IAAI;YACF,MAAM,IAAI,CAACC,mBAAmB,CAACjC,WAAW8B;YAC1C,IAAI,CAACjC,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAElF,UAAU,SAASoF,OAAO,UAAU,CAAC;QAC9D,EAAE,OAAOhC,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,oCAAoCkE,SAASpC;YAC9D,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,8BAA8B,EAAEoC,SAAS,EAAE;gBACnErF,MAAMC,UAAUkF,aAAa;YAC/B;QACF;IACF;IAEA,MAAcxB,eACZR,SAAiB,EACjB9B,IAAwC,EACxCQ,KAAsF,EACvE;QACfV,iBAAiB;QAEjB,MAAMqE,cAAcC,QAAQ5D,KAAK,CAAC,eAAe;QACjD,MAAM6D,sBAAsBD,QAAQ5D,KAAK,CAAC,wBAAwB;QAElE,4EAA4E;QAC5E,2EAA2E;QAC3E,4BAA4B;QAC5B,IAAI,CAAC2D,aAAa;YAChB,IAAI,CAACxC,MAAM,CAAC+B,GAAG,CACb,CAAC,6GAA6G,CAAC;QAEnH;QAEA,kCAAkC;QAClC,IAAIT,gBAAgBjD,KAAKC,MAAM;QAC/B,IAAIgD,eAAe;YACjB,MAAMqB,YAAYlF,oBAAoB6D;YACtC,IAAIqB,WAAW;gBACb,OAAO,IAAI,CAAC3C,MAAM,CAACC,KAAK,CAAC0C,WAAW;oBAAC3F,MAAMC,UAAUiD,WAAW;gBAAA;YAClE;QACF;QAEA,IAAI0C;QACJ,IAAI;YACFA,mBAAmB,MAAM5E,aAAamC;QACxC,EAAE,OAAOF,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,gCAAgCkE,SAASpC;YAC1D,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,0BAA0B,EAAEoC,SAAS,EAAE;gBAC/DrF,MAAMC,UAAUkF,aAAa;YAC/B;QACF;QAEA,MAAMU,eAAe,IAAIC,IAAIF,iBAAiBG,GAAG,CAAC,CAACC,KAAOA,GAAG/B,IAAI;QAEjE,oCAAoC;QACpC,IAAI,CAACK,eAAe;YAClBA,gBAAgB,MAAM5D,iBAAiB;gBACrCuF,UAAUL;YACZ;QACF;QAEA,IAAI,CAACC,aAAaK,GAAG,CAAC5B,gBAAgB;YACpC,OAAO,IAAI,CAACtB,MAAM,CAACC,KAAK,CAAC,CAAC,gBAAgB,EAAEqB,cAAc,eAAe,CAAC,EAAE;gBAC1EtE,MAAMC,UAAUkF,aAAa;YAC/B;QACF;QAEA,kCAAkC;QAClC,IAAIX,gBAAgBnD,KAAKK,MAAM;QAC/B,IAAI8C,eAAe;YACjB,MAAMmB,YAAYlF,oBAAoB+D;YACtC,IAAImB,WAAW;gBACb,OAAO,IAAI,CAAC3C,MAAM,CAACC,KAAK,CAAC0C,WAAW;oBAAC3F,MAAMC,UAAUiD,WAAW;gBAAA;YAClE;QACF,OAAO;YACLsB,gBAAgB,MAAM7D,qBAAqB;gBACzC0E,SAAS;YACX;QACF;QAEA,IAAIQ,aAAaK,GAAG,CAAC1B,gBAAgB;YACnC,OAAO,IAAI,CAACxB,MAAM,CAACC,KAAK,CAAC,CAAC,gBAAgB,EAAEuB,cAAc,gBAAgB,CAAC,EAAE;gBAC3ExE,MAAMC,UAAUkF,aAAa;YAC/B;QACF;QAEA,qBAAqB;QACrB,IAAI;YACF,IAAI,CAACnC,MAAM,CAAC+B,GAAG,CACb,CAAC,gBAAgB,EAAElF,UAAU,SAASyE,eAAe,IAAI,EAAEzE,UAAU,SAAS2E,eAAe,GAAG,CAAC;YAGnG,MAAM2B,WAAW,MAAMtF,YAAY;gBACjCsC;gBACAuC;gBACAF;gBACAlB;gBACAE;YACF;YAEA,IAAI,CAACxB,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAElF,UAAU,SAASsG,SAASlB,KAAK,EAAE,QAAQ,CAAC;YAEnE,IAAIpD,MAAMI,MAAM,EAAE;gBAChB;YACF;YAEA,MAAM,IAAI,CAACmD,mBAAmB,CAACjC,WAAWgD,SAASlB,KAAK;YACxD,IAAI,CAACjC,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAElF,UAAU,SAASsG,SAASlB,KAAK,EAAE,UAAU,CAAC;QACvE,EAAE,OAAOhC,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,8BAA8BkE,SAASpC;YACxD,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,wBAAwB,EAAEoC,SAAS,EAAE;gBAC7DrF,MAAMC,UAAUkF,aAAa;YAC/B;QACF;IACF;IAEA,MAAc1B,eACZN,SAAiB,EACjBtB,KAAwC,EACzB;QACfV,iBAAiB;QAEjB,IAAI;YACF,MAAM0C,OAAO,MAAM9C,oBAAoB;gBACrCoB,OAAON,MAAMM,KAAK;gBAClBK,QAAQX,MAAMW,MAAM;gBACpBW;YACF;YAEA,IAAIU,KAAKd,MAAM,KAAK,GAAG;gBACrB,IAAI,CAACC,MAAM,CAAC+B,GAAG,CAAC;gBAChB;YACF;YAEA,IAAI,CAACnB,oBAAoB,CAACC;QAC5B,EAAE,OAAOZ,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,wCAAwCkE,SAASpC;YAClE,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,kCAAkC,EAAEoC,SAAS,EAAE;gBACvErF,MAAMC,UAAUkF,aAAa;YAC/B;QACF;IACF;IAEA,MAAcC,oBAAoBjC,SAAiB,EAAE8B,KAAa,EAAiB;QACjF,MAAMmB,OAAOhG,QAAQ,IAAIiG,KAAK;QAE9B,OAAO,IAAIC,QAAc,CAACC,SAASC;YACjC,MAAMC,gBAAgB;gBACpBC,aAAaC,WAAW;gBACxBP,KAAKQ,IAAI,CAAC;gBACV5G,KAAK;YACP;YAEA,MAAM0G,eAAe5F,sBAAsB;gBAACmE;gBAAO9B;YAAS,GAAG0D,SAAS,CAAC;gBACvEC,UAAU;oBACRC,QAAQC,GAAG,CAAC,UAAUP;oBACtBL,KAAKa,OAAO,CAAC;oBACbV;gBACF;gBACAtD,OAAO,CAACiE;oBACNH,QAAQC,GAAG,CAAC,UAAUP;oBACtBL,KAAKQ,IAAI,CAAC;oBACVJ,OAAOU;gBACT;gBACAC,MAAM,CAACC;oBACL,IAAI,OAAOA,MAAMC,QAAQ,KAAK,UAAU;wBACtCjB,KAAKkB,IAAI,GAAG,CAAC,kBAAkB,EAAEF,MAAMC,QAAQ,CAAC,CAAC,CAAC;oBACpD;gBACF;YACF;YAEAN,QAAQQ,IAAI,CAAC,UAAUd;QACzB;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","subdebug","getCliExecutionContext","SanityCommand","spinner","Table","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatCliErrorMessages","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","hasCliExecutionContext","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { colorizeJson, exitCodes, getProjectCliClient, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
3
4
  import { DOCUMENTS_API_VERSION } from '../../actions/documents/constants.js';
4
5
  import { promptForProject } from '../../prompts/promptForProject.js';
5
6
  import { getDatasetFlag, getProjectIdFlag } from '../../util/sharedFlags.js';
@@ -44,8 +45,7 @@ export class QueryDocumentCommand extends SanityCommand {
44
45
  description: 'Send the query without any authorization token'
45
46
  }),
46
47
  'api-version': Flags.string({
47
- description: `API version to use (defaults to ${DOCUMENTS_API_VERSION})`,
48
- env: 'SANITY_CLI_QUERY_API_VERSION'
48
+ description: `API version to use (defaults to ${DOCUMENTS_API_VERSION})`
49
49
  }),
50
50
  pretty: Flags.boolean({
51
51
  default: false,
@@ -78,8 +78,9 @@ export class QueryDocumentCommand extends SanityCommand {
78
78
  });
79
79
  }
80
80
  const targetDataset = dataset || cliConfig.api?.dataset;
81
- const targetApiVersion = apiVersion || DOCUMENTS_API_VERSION;
82
- if (!apiVersion) {
81
+ const configuredApiVersion = apiVersion ?? (getCliExecutionContext() ? undefined : process.env.SANITY_CLI_QUERY_API_VERSION);
82
+ const targetApiVersion = configuredApiVersion || DOCUMENTS_API_VERSION;
83
+ if (!configuredApiVersion) {
83
84
  this.warn(`--api-version not specified, using \`${DOCUMENTS_API_VERSION}\``);
84
85
  }
85
86
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/documents/query.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {\n colorizeJson,\n exitCodes,\n getProjectCliClient,\n SanityCommand,\n subdebug,\n} from '@sanity/cli-core'\n\nimport {DOCUMENTS_API_VERSION} from '../../actions/documents/constants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst queryDocumentDebug = subdebug('documents:query')\n\nexport class QueryDocumentCommand extends SanityCommand<typeof QueryDocumentCommand> {\n static override args = {\n query: Args.string({\n description: 'GROQ query to run against the dataset',\n required: true,\n }),\n }\n\n static override description = 'Query for documents'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \\'*[_type == \"movie\"][0..4]\\'',\n description: 'Fetch 5 documents of type \"movie\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_type == \"movie\"]|order(releaseDate asc)[0]{title}\\' --dataset staging',\n description: 'Fetch title of the oldest movie in the dataset named \"staging\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_id == \"header\"] { \"headerText\": pt::text(body) }\\' --api-version v2021-06-07',\n description: 'Use API version v2021-06-07 and do a query',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_type == \"post\"]\\' --project-id abc123 --dataset production',\n description: 'Query documents in a specific project and dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({description: 'Project ID to query', semantics: 'override'}),\n ...getDatasetFlag({description: 'Dataset to query', semantics: 'override'}),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the query without any authorization token',\n }),\n 'api-version': Flags.string({\n description: `API version to use (defaults to ${DOCUMENTS_API_VERSION})`,\n env: 'SANITY_CLI_QUERY_API_VERSION',\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n project: Flags.string({\n deprecated: {to: 'project-id'},\n description: 'Project ID to query',\n hidden: true,\n }),\n }\n\n static override hiddenAliases: string[] = ['document:query']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(QueryDocumentCommand)\n const {query} = args\n const {anonymous, 'api-version': apiVersion, dataset, pretty} = flags\n\n const cliConfig = await this.tryGetCliConfig()\n\n const projectId = await this.getProjectId({\n deprecatedFlagName: 'project',\n fallback: () => promptForProject({}),\n })\n\n const requireUser = !anonymous\n\n if (!cliConfig.api?.dataset && !dataset) {\n this.error(\n 'Dataset is required. Pass it with `--dataset <name>` or configure it in `sanity.cli.ts`.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const targetDataset = dataset || cliConfig.api?.dataset\n const targetApiVersion = apiVersion || DOCUMENTS_API_VERSION\n\n if (!apiVersion) {\n this.warn(`--api-version not specified, using \\`${DOCUMENTS_API_VERSION}\\``)\n }\n\n try {\n const projectClient = await getProjectCliClient({\n apiVersion: targetApiVersion,\n dataset: targetDataset,\n projectId,\n requireUser,\n })\n\n const docs = await projectClient.fetch(query)\n\n if (!docs) {\n this.error('Query returned no results', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Output the query results\n if (pretty) {\n this.log(colorizeJson(docs))\n } else {\n this.log(JSON.stringify(docs, null, 2))\n }\n } catch (error) {\n const err = error as Error\n\n queryDocumentDebug(`Error running query: ${query}`, err)\n\n // Provide more context in error messages\n const errorMsg = err.message.toLowerCase().includes('syntax')\n ? `Invalid GROQ query syntax: ${err.message}`\n : `Failed to run query: ${err.message}`\n\n this.error(`${errorMsg}\\nQuery: ${query}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["Args","Flags","colorizeJson","exitCodes","getProjectCliClient","SanityCommand","subdebug","DOCUMENTS_API_VERSION","promptForProject","getDatasetFlag","getProjectIdFlag","queryDocumentDebug","QueryDocumentCommand","args","query","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","env","pretty","project","deprecated","to","hidden","hiddenAliases","run","parse","apiVersion","dataset","cliConfig","tryGetCliConfig","projectId","getProjectId","deprecatedFlagName","fallback","requireUser","api","error","exit","USAGE_ERROR","targetDataset","targetApiVersion","warn","projectClient","docs","fetch","RUNTIME_ERROR","log","JSON","stringify","err","errorMsg","message","toLowerCase","includes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/documents/query.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {\n colorizeJson,\n exitCodes,\n getProjectCliClient,\n SanityCommand,\n subdebug,\n} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\n\nimport {DOCUMENTS_API_VERSION} from '../../actions/documents/constants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst queryDocumentDebug = subdebug('documents:query')\n\nexport class QueryDocumentCommand extends SanityCommand<typeof QueryDocumentCommand> {\n static override args = {\n query: Args.string({\n description: 'GROQ query to run against the dataset',\n required: true,\n }),\n }\n\n static override description = 'Query for documents'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \\'*[_type == \"movie\"][0..4]\\'',\n description: 'Fetch 5 documents of type \"movie\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_type == \"movie\"]|order(releaseDate asc)[0]{title}\\' --dataset staging',\n description: 'Fetch title of the oldest movie in the dataset named \"staging\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_id == \"header\"] { \"headerText\": pt::text(body) }\\' --api-version v2021-06-07',\n description: 'Use API version v2021-06-07 and do a query',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \\'*[_type == \"post\"]\\' --project-id abc123 --dataset production',\n description: 'Query documents in a specific project and dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({description: 'Project ID to query', semantics: 'override'}),\n ...getDatasetFlag({description: 'Dataset to query', semantics: 'override'}),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the query without any authorization token',\n }),\n 'api-version': Flags.string({\n description: `API version to use (defaults to ${DOCUMENTS_API_VERSION})`,\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n project: Flags.string({\n deprecated: {to: 'project-id'},\n description: 'Project ID to query',\n hidden: true,\n }),\n }\n\n static override hiddenAliases: string[] = ['document:query']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(QueryDocumentCommand)\n const {query} = args\n const {anonymous, 'api-version': apiVersion, dataset, pretty} = flags\n\n const cliConfig = await this.tryGetCliConfig()\n\n const projectId = await this.getProjectId({\n deprecatedFlagName: 'project',\n fallback: () => promptForProject({}),\n })\n\n const requireUser = !anonymous\n\n if (!cliConfig.api?.dataset && !dataset) {\n this.error(\n 'Dataset is required. Pass it with `--dataset <name>` or configure it in `sanity.cli.ts`.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const targetDataset = dataset || cliConfig.api?.dataset\n const configuredApiVersion =\n apiVersion ??\n (getCliExecutionContext() ? undefined : process.env.SANITY_CLI_QUERY_API_VERSION)\n const targetApiVersion = configuredApiVersion || DOCUMENTS_API_VERSION\n\n if (!configuredApiVersion) {\n this.warn(`--api-version not specified, using \\`${DOCUMENTS_API_VERSION}\\``)\n }\n\n try {\n const projectClient = await getProjectCliClient({\n apiVersion: targetApiVersion,\n dataset: targetDataset,\n projectId,\n requireUser,\n })\n\n const docs = await projectClient.fetch(query)\n\n if (!docs) {\n this.error('Query returned no results', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Output the query results\n if (pretty) {\n this.log(colorizeJson(docs))\n } else {\n this.log(JSON.stringify(docs, null, 2))\n }\n } catch (error) {\n const err = error as Error\n\n queryDocumentDebug(`Error running query: ${query}`, err)\n\n // Provide more context in error messages\n const errorMsg = err.message.toLowerCase().includes('syntax')\n ? `Invalid GROQ query syntax: ${err.message}`\n : `Failed to run query: ${err.message}`\n\n this.error(`${errorMsg}\\nQuery: ${query}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["Args","Flags","colorizeJson","exitCodes","getProjectCliClient","SanityCommand","subdebug","getCliExecutionContext","DOCUMENTS_API_VERSION","promptForProject","getDatasetFlag","getProjectIdFlag","queryDocumentDebug","QueryDocumentCommand","args","query","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","pretty","project","deprecated","to","hidden","hiddenAliases","run","parse","apiVersion","dataset","cliConfig","tryGetCliConfig","projectId","getProjectId","deprecatedFlagName","fallback","requireUser","api","error","exit","USAGE_ERROR","targetDataset","configuredApiVersion","undefined","process","env","SANITY_CLI_QUERY_API_VERSION","targetApiVersion","warn","projectClient","docs","fetch","RUNTIME_ERROR","log","JSON","stringify","err","errorMsg","message","toLowerCase","includes"],"mappings":"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"}
@@ -10,6 +10,10 @@ export class LoginCommand extends SanityCommand {
10
10
  command: '<%= config.bin %> <%= command.id %>',
11
11
  description: 'Log in using default settings'
12
12
  },
13
+ {
14
+ command: '<%= config.bin %> <%= command.id %> --with-token < token.txt',
15
+ description: 'Log in using a token from standard input'
16
+ },
13
17
  {
14
18
  command: '<%= config.bin %> <%= command.id %> --provider github --no-open',
15
19
  description: 'Login with GitHub provider, but do not open a browser window automatically'
@@ -21,13 +25,17 @@ export class LoginCommand extends SanityCommand {
21
25
  {
22
26
  command: '<%= config.bin %> <%= command.id %> --sso my-organization --sso-provider "Okta SSO"',
23
27
  description: 'Log in using a specific SSO provider within an organization'
24
- },
25
- {
26
- command: '<%= config.bin %> <%= command.id %> --with-token < token.txt',
27
- description: 'Log in using a token from standard input'
28
28
  }
29
29
  ];
30
30
  static flags = {
31
+ 'with-token': Flags.boolean({
32
+ description: 'Read token from standard input',
33
+ exclusive: [
34
+ 'provider',
35
+ 'sso'
36
+ ]
37
+ }),
38
+ // eslint-disable-next-line perfectionist/sort-objects -- Keep token login first in generated usage.
31
39
  experimental: Flags.boolean({
32
40
  default: false,
33
41
  hidden: true
@@ -59,13 +67,6 @@ export class LoginCommand extends SanityCommand {
59
67
  ],
60
68
  description: 'Select a specific SSO provider by name (use with --sso)',
61
69
  helpValue: '<name>'
62
- }),
63
- 'with-token': Flags.boolean({
64
- description: 'Read token from standard input',
65
- exclusive: [
66
- 'provider',
67
- 'sso'
68
- ]
69
70
  })
70
71
  };
71
72
  async run() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/login.ts"],"sourcesContent":["import {text} from 'node:stream/consumers'\n\nimport {Command, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {login} from '../actions/auth/login/login.js'\nimport {LOGIN_PROVIDER_IDS} from '../actions/auth/login/loginInstructions.js'\n\nexport class LoginCommand extends SanityCommand<typeof LoginCommand> {\n static override description = 'Log in to your Sanity account'\n static override examples: Array<Command.Example> = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Log in using default settings',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --provider github --no-open',\n description: 'Login with GitHub provider, but do not open a browser window automatically',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sso my-organization',\n description: 'Log in using Single Sign-On with the \"my-organization\" slug',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --sso my-organization --sso-provider \"Okta SSO\"',\n description: 'Log in using a specific SSO provider within an organization',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --with-token < token.txt',\n description: 'Log in using a token from standard input',\n },\n ]\n static override flags = {\n experimental: Flags.boolean({\n default: false,\n hidden: true,\n }),\n open: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Open a browser window to log in (`--no-open` only prints URL)',\n }),\n provider: Flags.string({\n description: `Log in using a provider ID (${LOGIN_PROVIDER_IDS.join(', ')})`,\n exclusive: ['sso', 'with-token'],\n helpValue: '<providerId>',\n }),\n sso: Flags.string({\n description: 'Log in using Single Sign-On, using the given organization slug',\n exclusive: ['provider', 'with-token'],\n helpValue: '<slug>',\n }),\n 'sso-provider': Flags.string({\n dependsOn: ['sso'],\n description: 'Select a specific SSO provider by name (use with --sso)',\n helpValue: '<name>',\n }),\n 'with-token': Flags.boolean({\n description: 'Read token from standard input',\n exclusive: ['provider', 'sso'],\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(LoginCommand)\n const {'sso-provider': ssoProvider, 'with-token': withToken, ...loginFlags} = flags\n\n try {\n const token = withToken ? await readTokenFromStdin() : undefined\n\n await login({\n ...loginFlags,\n output: this.output,\n ssoProvider,\n telemetry: this.telemetry,\n token,\n })\n this.log('Login successful')\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.error(`Login failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n\nasync function readTokenFromStdin(): Promise<string> {\n if (process.stdin.isTTY) {\n throw new Error(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n }\n\n return text(process.stdin)\n}\n"],"names":["text","Flags","exitCodes","SanityCommand","login","LOGIN_PROVIDER_IDS","LoginCommand","description","examples","command","flags","experimental","boolean","default","hidden","open","allowNo","provider","string","join","exclusive","helpValue","sso","dependsOn","run","parse","ssoProvider","withToken","loginFlags","token","readTokenFromStdin","undefined","output","telemetry","log","error","message","Error","String","exit","RUNTIME_ERROR","process","stdin","isTTY"],"mappings":"AAAA,SAAQA,IAAI,QAAO,wBAAuB;AAE1C,SAAiBC,KAAK,QAAO,cAAa;AAE1C,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,KAAK,QAAO,iCAAgC;AACpD,SAAQC,kBAAkB,QAAO,6CAA4C;AAE7E,OAAO,MAAMC,qBAAqBH;IAChC,OAAgBI,cAAc,gCAA+B;IAC7D,OAAgBC,WAAmC;QACjD;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SACE;YACFF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IACD,OAAgBG,QAAQ;QACtBC,cAAcV,MAAMW,OAAO,CAAC;YAC1BC,SAAS;YACTC,QAAQ;QACV;QACAC,MAAMd,MAAMW,OAAO,CAAC;YAClBI,SAAS;YACTH,SAAS;YACTN,aAAa;QACf;QACAU,UAAUhB,MAAMiB,MAAM,CAAC;YACrBX,aAAa,CAAC,4BAA4B,EAAEF,mBAAmBc,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5EC,WAAW;gBAAC;gBAAO;aAAa;YAChCC,WAAW;QACb;QACAC,KAAKrB,MAAMiB,MAAM,CAAC;YAChBX,aAAa;YACba,WAAW;gBAAC;gBAAY;aAAa;YACrCC,WAAW;QACb;QACA,gBAAgBpB,MAAMiB,MAAM,CAAC;YAC3BK,WAAW;gBAAC;aAAM;YAClBhB,aAAa;YACbc,WAAW;QACb;QACA,cAAcpB,MAAMW,OAAO,CAAC;YAC1BL,aAAa;YACba,WAAW;gBAAC;gBAAY;aAAM;QAChC;IACF,EAAqB;IAErB,MAAaI,MAAqB;QAChC,MAAM,EAACd,KAAK,EAAC,GAAG,MAAM,IAAI,CAACe,KAAK,CAACnB;QACjC,MAAM,EAAC,gBAAgBoB,WAAW,EAAE,cAAcC,SAAS,EAAE,GAAGC,YAAW,GAAGlB;QAE9E,IAAI;YACF,MAAMmB,QAAQF,YAAY,MAAMG,uBAAuBC;YAEvD,MAAM3B,MAAM;gBACV,GAAGwB,UAAU;gBACbI,QAAQ,IAAI,CAACA,MAAM;gBACnBN;gBACAO,WAAW,IAAI,CAACA,SAAS;gBACzBJ;YACF;YACA,IAAI,CAACK,GAAG,CAAC;QACX,EAAE,OAAOC,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE,IAAI,CAACA,KAAK,CAAC,CAAC,cAAc,EAAEC,SAAS,EAAE;gBAACG,MAAMrC,UAAUsC,aAAa;YAAA;QACvE;IACF;AACF;AAEA,eAAeV;IACb,IAAIW,QAAQC,KAAK,CAACC,KAAK,EAAE;QACvB,MAAM,IAAIN,MACR;IAEJ;IAEA,OAAOrC,KAAKyC,QAAQC,KAAK;AAC3B"}
1
+ {"version":3,"sources":["../../src/commands/login.ts"],"sourcesContent":["import {text} from 'node:stream/consumers'\n\nimport {Command, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {login} from '../actions/auth/login/login.js'\nimport {LOGIN_PROVIDER_IDS} from '../actions/auth/login/loginInstructions.js'\n\nexport class LoginCommand extends SanityCommand<typeof LoginCommand> {\n static override description = 'Log in to your Sanity account'\n static override examples: Array<Command.Example> = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Log in using default settings',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --with-token < token.txt',\n description: 'Log in using a token from standard input',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --provider github --no-open',\n description: 'Login with GitHub provider, but do not open a browser window automatically',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sso my-organization',\n description: 'Log in using Single Sign-On with the \"my-organization\" slug',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --sso my-organization --sso-provider \"Okta SSO\"',\n description: 'Log in using a specific SSO provider within an organization',\n },\n ]\n static override flags = {\n 'with-token': Flags.boolean({\n description: 'Read token from standard input',\n exclusive: ['provider', 'sso'],\n }),\n // eslint-disable-next-line perfectionist/sort-objects -- Keep token login first in generated usage.\n experimental: Flags.boolean({\n default: false,\n hidden: true,\n }),\n open: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Open a browser window to log in (`--no-open` only prints URL)',\n }),\n provider: Flags.string({\n description: `Log in using a provider ID (${LOGIN_PROVIDER_IDS.join(', ')})`,\n exclusive: ['sso', 'with-token'],\n helpValue: '<providerId>',\n }),\n sso: Flags.string({\n description: 'Log in using Single Sign-On, using the given organization slug',\n exclusive: ['provider', 'with-token'],\n helpValue: '<slug>',\n }),\n 'sso-provider': Flags.string({\n dependsOn: ['sso'],\n description: 'Select a specific SSO provider by name (use with --sso)',\n helpValue: '<name>',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(LoginCommand)\n const {'sso-provider': ssoProvider, 'with-token': withToken, ...loginFlags} = flags\n\n try {\n const token = withToken ? await readTokenFromStdin() : undefined\n\n await login({\n ...loginFlags,\n output: this.output,\n ssoProvider,\n telemetry: this.telemetry,\n token,\n })\n this.log('Login successful')\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.error(`Login failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n\nasync function readTokenFromStdin(): Promise<string> {\n if (process.stdin.isTTY) {\n throw new Error(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n }\n\n return text(process.stdin)\n}\n"],"names":["text","Flags","exitCodes","SanityCommand","login","LOGIN_PROVIDER_IDS","LoginCommand","description","examples","command","flags","boolean","exclusive","experimental","default","hidden","open","allowNo","provider","string","join","helpValue","sso","dependsOn","run","parse","ssoProvider","withToken","loginFlags","token","readTokenFromStdin","undefined","output","telemetry","log","error","message","Error","String","exit","RUNTIME_ERROR","process","stdin","isTTY"],"mappings":"AAAA,SAAQA,IAAI,QAAO,wBAAuB;AAE1C,SAAiBC,KAAK,QAAO,cAAa;AAE1C,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,KAAK,QAAO,iCAAgC;AACpD,SAAQC,kBAAkB,QAAO,6CAA4C;AAE7E,OAAO,MAAMC,qBAAqBH;IAChC,OAAgBI,cAAc,gCAA+B;IAC7D,OAAgBC,WAAmC;QACjD;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SACE;YACFF,aAAa;QACf;KACD,CAAA;IACD,OAAgBG,QAAQ;QACtB,cAAcT,MAAMU,OAAO,CAAC;YAC1BJ,aAAa;YACbK,WAAW;gBAAC;gBAAY;aAAM;QAChC;QACA,oGAAoG;QACpGC,cAAcZ,MAAMU,OAAO,CAAC;YAC1BG,SAAS;YACTC,QAAQ;QACV;QACAC,MAAMf,MAAMU,OAAO,CAAC;YAClBM,SAAS;YACTH,SAAS;YACTP,aAAa;QACf;QACAW,UAAUjB,MAAMkB,MAAM,CAAC;YACrBZ,aAAa,CAAC,4BAA4B,EAAEF,mBAAmBe,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5ER,WAAW;gBAAC;gBAAO;aAAa;YAChCS,WAAW;QACb;QACAC,KAAKrB,MAAMkB,MAAM,CAAC;YAChBZ,aAAa;YACbK,WAAW;gBAAC;gBAAY;aAAa;YACrCS,WAAW;QACb;QACA,gBAAgBpB,MAAMkB,MAAM,CAAC;YAC3BI,WAAW;gBAAC;aAAM;YAClBhB,aAAa;YACbc,WAAW;QACb;IACF,EAAqB;IAErB,MAAaG,MAAqB;QAChC,MAAM,EAACd,KAAK,EAAC,GAAG,MAAM,IAAI,CAACe,KAAK,CAACnB;QACjC,MAAM,EAAC,gBAAgBoB,WAAW,EAAE,cAAcC,SAAS,EAAE,GAAGC,YAAW,GAAGlB;QAE9E,IAAI;YACF,MAAMmB,QAAQF,YAAY,MAAMG,uBAAuBC;YAEvD,MAAM3B,MAAM;gBACV,GAAGwB,UAAU;gBACbI,QAAQ,IAAI,CAACA,MAAM;gBACnBN;gBACAO,WAAW,IAAI,CAACA,SAAS;gBACzBJ;YACF;YACA,IAAI,CAACK,GAAG,CAAC;QACX,EAAE,OAAOC,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE,IAAI,CAACA,KAAK,CAAC,CAAC,cAAc,EAAEC,SAAS,EAAE;gBAACG,MAAMrC,UAAUsC,aAAa;YAAA;QACvE;IACF;AACF;AAEA,eAAeV;IACb,IAAIW,QAAQC,KAAK,CAACC,KAAK,EAAE;QACvB,MAAM,IAAIN,MACR;IAEJ;IAEA,OAAOrC,KAAKyC,QAAQC,KAAK;AAC3B"}
@@ -7,6 +7,11 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
7
7
  const separatorIndex = field.indexOf('=');
8
8
  return separatorIndex > 0 && field[separatorIndex + 1] === '@';
9
9
  }
10
+ /** A `-H/--header` value that replaces the invocation's authentication. */ function headerOverridesAuthentication(header) {
11
+ if (typeof header !== 'string') return false;
12
+ const separatorIndex = header.indexOf(':');
13
+ return separatorIndex > 0 && header.slice(0, separatorIndex).trim().toLowerCase() === 'authorization';
14
+ }
10
15
  /**
11
16
  * MCP programmatic mode disables local project/config discovery (see the CLI
12
17
  * execution context). Missing project or dataset values may therefore produce
@@ -19,14 +24,16 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
19
24
  * - deny: no invocation is safe
20
25
  */ export const mcpPolicy = {
21
26
  // Special exception, this can be very dangerous but is also super useful
22
- // to expose. Only the host input channels are refused: `--input` reads the
23
- // request body from the host's filesystem or stdin, and `-F key=@<file>` /
24
- // `-F key=@-` field values do the same.
27
+ // to expose. Refuse authentication overrides and host input channels:
28
+ // `--token` and an Authorization header replace the MCP user's token,
29
+ // `--input` reads the request body from the host's filesystem or stdin, and
30
+ // `-F key=@<file>` / `-F key=@-` field values do the same.
25
31
  api: conditionalPolicy({
26
32
  deniedFlags: [
27
- 'input'
33
+ 'input',
34
+ 'token'
28
35
  ],
29
- validate: ({ flags })=>!Array.isArray(flags.field) || !flags.field.some((field)=>fieldReadsFromHost(field))
36
+ validate: ({ flags })=>(!Array.isArray(flags.field) || !flags.field.some((field)=>fieldReadsFromHost(field))) && (!Array.isArray(flags.header) || !flags.header.some((header)=>headerOverridesAuthentication(header)))
30
37
  }),
31
38
  'backups:disable': allow,
32
39
  // Writes a downloaded backup to the local filesystem.